From f6f9de12af5fe37fecddb79feba832087719b929 Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Wed, 13 Feb 2019 14:57:11 -0800 Subject: [PATCH 01/10] branch with experimental PyBullet support for PhysX see otherPhysicsEngine in examples/pybullet/examples folder for example usage --- setup.py | 1160 +-- src/PhysX/physx/include/PxActor.h | 330 + src/PhysX/physx/include/PxAggregate.h | 213 + src/PhysX/physx/include/PxArticulation.h | 281 + src/PhysX/physx/include/PxArticulationBase.h | 337 + src/PhysX/physx/include/PxArticulationJoint.h | 572 ++ .../PxArticulationJointReducedCoordinate.h | 95 + src/PhysX/physx/include/PxArticulationLink.h | 142 + .../include/PxArticulationReducedCoordinate.h | 409 + src/PhysX/physx/include/PxBatchQuery.h | 224 + src/PhysX/physx/include/PxBatchQueryDesc.h | 303 + src/PhysX/physx/include/PxBroadPhase.h | 174 + src/PhysX/physx/include/PxClient.h | 66 + src/PhysX/physx/include/PxConstraint.h | 280 + src/PhysX/physx/include/PxConstraintDesc.h | 443 + src/PhysX/physx/include/PxContact.h | 583 ++ .../physx/include/PxContactModifyCallback.h | 486 ++ src/PhysX/physx/include/PxDeletionListener.h | 106 + src/PhysX/physx/include/PxFiltering.h | 733 ++ src/PhysX/physx/include/PxForceMode.h | 66 + src/PhysX/physx/include/PxFoundation.h | 158 + src/PhysX/physx/include/PxImmediateMode.h | 228 + src/PhysX/physx/include/PxLockedData.h | 93 + src/PhysX/physx/include/PxMaterial.h | 324 + src/PhysX/physx/include/PxPhysXConfig.h | 78 + src/PhysX/physx/include/PxPhysics.h | 729 ++ src/PhysX/physx/include/PxPhysicsAPI.h | 218 + .../physx/include/PxPhysicsSerialization.h | 74 + src/PhysX/physx/include/PxPhysicsVersion.h | 70 + src/PhysX/physx/include/PxPruningStructure.h | 109 + src/PhysX/physx/include/PxQueryFiltering.h | 276 + src/PhysX/physx/include/PxQueryReport.h | 390 + src/PhysX/physx/include/PxRigidActor.h | 237 + src/PhysX/physx/include/PxRigidBody.h | 701 ++ src/PhysX/physx/include/PxRigidDynamic.h | 388 + src/PhysX/physx/include/PxRigidStatic.h | 77 + src/PhysX/physx/include/PxScene.h | 1649 ++++ src/PhysX/physx/include/PxSceneDesc.h | 1059 +++ src/PhysX/physx/include/PxSceneLock.h | 129 + src/PhysX/physx/include/PxShape.h | 639 ++ .../physx/include/PxSimulationEventCallback.h | 915 ++ .../physx/include/PxSimulationStatistics.h | 330 + .../physx/include/PxVisualizationParameter.h | 254 + .../characterkinematic/PxBoxController.h | 230 + .../characterkinematic/PxCapsuleController.h | 251 + .../include/characterkinematic/PxCharacter.h | 57 + .../include/characterkinematic/PxController.h | 911 ++ .../characterkinematic/PxControllerBehavior.h | 136 + .../characterkinematic/PxControllerManager.h | 301 + .../PxControllerObstacles.h | 192 + .../include/characterkinematic/PxExtended.h | 275 + .../physx/include/collision/PxCollisionDefs.h | 85 + src/PhysX/physx/include/common/PxBase.h | 201 + src/PhysX/physx/include/common/PxCollection.h | 279 + .../physx/include/common/PxCoreUtilityTypes.h | 211 + src/PhysX/physx/include/common/PxMetaData.h | 228 + .../physx/include/common/PxMetaDataFlags.h | 74 + .../include/common/PxPhysXCommonConfig.h | 124 + .../common/PxPhysicsInsertionCallback.h | 84 + .../physx/include/common/PxProfileZone.h | 51 + .../physx/include/common/PxRenderBuffer.h | 157 + .../physx/include/common/PxSerialFramework.h | 406 + src/PhysX/physx/include/common/PxSerializer.h | 257 + .../physx/include/common/PxStringTable.h | 73 + .../physx/include/common/PxTolerancesScale.h | 108 + src/PhysX/physx/include/common/PxTypeInfo.h | 124 + .../common/windows/PxWindowsDelayLoadHook.h | 102 + .../include/cooking/PxBVH33MidphaseDesc.h | 110 + .../include/cooking/PxBVH34MidphaseDesc.h | 90 + .../include/cooking/PxBVHStructureDesc.h | 109 + .../physx/include/cooking/PxConvexMeshDesc.h | 307 + src/PhysX/physx/include/cooking/PxCooking.h | 563 ++ .../physx/include/cooking/PxMidphaseDesc.h | 119 + .../include/cooking/PxTriangleMeshDesc.h | 120 + src/PhysX/physx/include/cooking/Pxc.h | 57 + .../cudamanager/PxCudaContextManager.h | 425 + .../include/cudamanager/PxCudaMemoryManager.h | 281 + .../physx/include/cudamanager/PxGpuCopyDesc.h | 86 + .../include/cudamanager/PxGpuCopyDescQueue.h | 149 + .../include/extensions/PxBinaryConverter.h | 132 + .../include/extensions/PxBroadPhaseExt.h | 75 + .../include/extensions/PxCollectionExt.h | 119 + .../include/extensions/PxConstraintExt.h | 70 + .../physx/include/extensions/PxContactJoint.h | 168 + .../include/extensions/PxConvexMeshExt.h | 73 + .../physx/include/extensions/PxD6Joint.h | 561 ++ .../include/extensions/PxD6JointCreate.h | 255 + .../include/extensions/PxDefaultAllocator.h | 110 + .../extensions/PxDefaultCpuDispatcher.h | 98 + .../extensions/PxDefaultErrorCallback.h | 62 + .../PxDefaultSimulationFilterShader.h | 263 + .../include/extensions/PxDefaultStreams.h | 149 + .../include/extensions/PxDistanceJoint.h | 269 + .../include/extensions/PxExtensionsAPI.h | 88 + .../physx/include/extensions/PxFixedJoint.h | 159 + src/PhysX/physx/include/extensions/PxJoint.h | 419 + .../physx/include/extensions/PxJointLimit.h | 566 ++ .../include/extensions/PxMassProperties.h | 334 + .../include/extensions/PxPrismaticJoint.h | 236 + .../physx/include/extensions/PxRaycastCCD.h | 100 + .../include/extensions/PxRepXSerializer.h | 148 + .../include/extensions/PxRepXSimpleType.h | 106 + .../include/extensions/PxRevoluteJoint.h | 332 + .../include/extensions/PxRigidActorExt.h | 155 + .../physx/include/extensions/PxRigidBodyExt.h | 460 + .../include/extensions/PxSceneQueryExt.h | 309 + .../include/extensions/PxSerialization.h | 284 + .../physx/include/extensions/PxShapeExt.h | 158 + .../include/extensions/PxSimpleFactory.h | 337 + .../include/extensions/PxSmoothNormals.h | 61 + .../include/extensions/PxSphericalJoint.h | 215 + .../include/extensions/PxStringTableExt.h | 57 + .../include/extensions/PxTriangleMeshExt.h | 188 + src/PhysX/physx/include/filebuf/PxFileBuf.h | 339 + .../physx/include/geometry/PxBVHStructure.h | 138 + .../physx/include/geometry/PxBoxGeometry.h | 109 + .../include/geometry/PxCapsuleGeometry.h | 121 + .../physx/include/geometry/PxConvexMesh.h | 197 + .../include/geometry/PxConvexMeshGeometry.h | 151 + src/PhysX/physx/include/geometry/PxGeometry.h | 94 + .../include/geometry/PxGeometryHelpers.h | 214 + .../physx/include/geometry/PxGeometryQuery.h | 225 + .../physx/include/geometry/PxHeightField.h | 262 + .../include/geometry/PxHeightFieldDesc.h | 187 + .../include/geometry/PxHeightFieldFlag.h | 162 + .../include/geometry/PxHeightFieldGeometry.h | 143 + .../include/geometry/PxHeightFieldSample.h | 124 + .../physx/include/geometry/PxMeshQuery.h | 201 + .../physx/include/geometry/PxMeshScale.h | 175 + .../physx/include/geometry/PxPlaneGeometry.h | 107 + .../include/geometry/PxSimpleTriangleMesh.h | 166 + .../physx/include/geometry/PxSphereGeometry.h | 93 + src/PhysX/physx/include/geometry/PxTriangle.h | 149 + .../physx/include/geometry/PxTriangleMesh.h | 317 + .../include/geometry/PxTriangleMeshGeometry.h | 150 + .../physx/include/geomutils/GuContactBuffer.h | 131 + .../physx/include/geomutils/GuContactPoint.h | 109 + src/PhysX/physx/include/gpu/PxGpu.h | 92 + src/PhysX/physx/include/pvd/PxPvd.h | 178 + .../physx/include/pvd/PxPvdSceneClient.h | 142 + src/PhysX/physx/include/pvd/PxPvdTransport.h | 129 + src/PhysX/physx/include/solver/PxSolverDefs.h | 256 + .../physx/include/task/PxCpuDispatcher.h | 79 + .../physx/include/task/PxGpuDispatcher.h | 248 + src/PhysX/physx/include/task/PxGpuTask.h | 118 + src/PhysX/physx/include/task/PxTask.h | 334 + src/PhysX/physx/include/task/PxTaskDefine.h | 37 + src/PhysX/physx/include/task/PxTaskManager.h | 228 + .../include/vehicle/PxVehicleComponents.h | 1359 +++ .../physx/include/vehicle/PxVehicleDrive.h | 566 ++ .../physx/include/vehicle/PxVehicleDrive4W.h | 279 + .../physx/include/vehicle/PxVehicleDriveNW.h | 237 + .../include/vehicle/PxVehicleDriveTank.h | 281 + .../physx/include/vehicle/PxVehicleNoDrive.h | 217 + .../physx/include/vehicle/PxVehicleSDK.h | 237 + .../physx/include/vehicle/PxVehicleShaders.h | 79 + .../include/vehicle/PxVehicleTireFriction.h | 223 + .../physx/include/vehicle/PxVehicleUpdate.h | 579 ++ .../physx/include/vehicle/PxVehicleUtil.h | 64 + .../include/vehicle/PxVehicleUtilControl.h | 651 ++ .../include/vehicle/PxVehicleUtilSetup.h | 160 + .../include/vehicle/PxVehicleUtilTelemetry.h | 410 + .../physx/include/vehicle/PxVehicleWheels.h | 811 ++ src/PhysX/physx/platform_readme.html | 16 + src/PhysX/physx/release_notes.html | 3680 ++++++++ .../include/windows/CmWindowsLoadLibrary.h | 87 + .../windows/CmWindowsModuleUpdateLoader.h | 70 + src/PhysX/physx/source/common/src/CmBitMap.h | 504 ++ .../physx/source/common/src/CmBlockArray.h | 153 + .../physx/source/common/src/CmBoxPruning.cpp | 197 + .../physx/source/common/src/CmBoxPruning.h | 49 + .../physx/source/common/src/CmCollection.cpp | 217 + .../physx/source/common/src/CmCollection.h | 103 + .../source/common/src/CmConeLimitHelper.h | 207 + .../physx/source/common/src/CmFlushPool.h | 157 + src/PhysX/physx/source/common/src/CmIDPool.h | 207 + src/PhysX/physx/source/common/src/CmIO.h | 136 + .../physx/source/common/src/CmMathUtils.cpp | 70 + .../physx/source/common/src/CmMatrix34.h | 286 + .../physx/source/common/src/CmPhysXCommon.h | 87 + src/PhysX/physx/source/common/src/CmPool.h | 292 + .../source/common/src/CmPreallocatingPool.h | 435 + .../physx/source/common/src/CmPriorityQueue.h | 237 + .../physx/source/common/src/CmPtrTable.cpp | 207 + .../physx/source/common/src/CmPtrTable.h | 142 + src/PhysX/physx/source/common/src/CmQueue.h | 152 + .../physx/source/common/src/CmRadixSort.cpp | 460 + .../physx/source/common/src/CmRadixSort.h | 102 + .../source/common/src/CmRadixSortBuffered.cpp | 149 + .../source/common/src/CmRadixSortBuffered.h | 63 + .../physx/source/common/src/CmRefCountable.h | 102 + .../physx/source/common/src/CmRenderBuffer.h | 132 + .../source/common/src/CmRenderOutput.cpp | 301 + .../physx/source/common/src/CmRenderOutput.h | 182 + src/PhysX/physx/source/common/src/CmScaling.h | 227 + .../physx/source/common/src/CmSpatialVector.h | 513 ++ src/PhysX/physx/source/common/src/CmTask.h | 267 + .../physx/source/common/src/CmTaskPool.h | 143 + src/PhysX/physx/source/common/src/CmTmpMem.h | 94 + .../source/common/src/CmTransformUtils.h | 145 + src/PhysX/physx/source/common/src/CmUtils.h | 311 + .../source/common/src/CmVisualization.cpp | 137 + .../physx/source/common/src/CmVisualization.h | 125 + .../src/windows/CmWindowsDelayLoadHook.cpp | 82 + .../windows/CmWindowsModuleUpdateLoader.cpp | 130 + .../source/compiler/cmake/CMakeLists.txt | 142 + .../physx/source/compiler/cmake/FastXml.cmake | 92 + .../source/compiler/cmake/IfTemplate.txt | 32 + .../source/compiler/cmake/LowLevel.cmake | 235 + .../source/compiler/cmake/LowLevelAABB.cmake | 131 + .../compiler/cmake/LowLevelDynamics.cmake | 202 + .../physx/source/compiler/cmake/PhysX.cmake | 382 + .../cmake/PhysXCharacterKinematic.cmake | 152 + .../source/compiler/cmake/PhysXCommon.cmake | 591 ++ .../source/compiler/cmake/PhysXCooking.cmake | 206 + .../compiler/cmake/PhysXExtensions.cmake | 320 + .../compiler/cmake/PhysXFoundation.cmake | 198 + .../source/compiler/cmake/PhysXPvdSDK.cmake | 186 + .../source/compiler/cmake/PhysXTask.cmake | 103 + .../source/compiler/cmake/PhysXVehicle.cmake | 176 + .../source/compiler/cmake/SceneQuery.cmake | 159 + .../compiler/cmake/SimulationController.cmake | 207 + .../compiler/cmake/android/CMakeLists.txt | 94 + .../compiler/cmake/android/FastXml.cmake | 42 + .../compiler/cmake/android/LowLevel.cmake | 53 + .../compiler/cmake/android/LowLevelAABB.cmake | 52 + .../cmake/android/LowLevelDynamics.cmake | 49 + .../source/compiler/cmake/android/PhysX.cmake | 64 + .../android/PhysXCharacterKinematic.cmake | 44 + .../compiler/cmake/android/PhysXCommon.cmake | 51 + .../compiler/cmake/android/PhysXCooking.cmake | 47 + .../cmake/android/PhysXExtensions.cmake | 54 + .../cmake/android/PhysXFoundation.cmake | 87 + .../compiler/cmake/android/PhysXPvdSDK.cmake | 42 + .../compiler/cmake/android/PhysXTask.cmake | 42 + .../compiler/cmake/android/PhysXVehicle.cmake | 43 + .../compiler/cmake/android/SceneQuery.cmake | 47 + .../cmake/android/SimulationController.cmake | 48 + .../source/compiler/cmake/ios/CMakeLists.txt | 76 + .../source/compiler/cmake/ios/FastXml.cmake | 44 + .../source/compiler/cmake/ios/LowLevel.cmake | 51 + .../compiler/cmake/ios/LowLevelAABB.cmake | 53 + .../compiler/cmake/ios/LowLevelDynamics.cmake | 52 + .../source/compiler/cmake/ios/PhysX.cmake | 68 + .../cmake/ios/PhysXCharacterKinematic.cmake | 43 + .../compiler/cmake/ios/PhysXCommon.cmake | 52 + .../compiler/cmake/ios/PhysXCooking.cmake | 52 + .../compiler/cmake/ios/PhysXExtensions.cmake | 55 + .../compiler/cmake/ios/PhysXFoundation.cmake | 91 + .../compiler/cmake/ios/PhysXPvdSDK.cmake | 45 + .../source/compiler/cmake/ios/PhysXTask.cmake | 41 + .../compiler/cmake/ios/PhysXVehicle.cmake | 42 + .../compiler/cmake/ios/SceneQuery.cmake | 48 + .../cmake/ios/SimulationController.cmake | 49 + .../compiler/cmake/linux/CMakeLists.txt | 123 + .../source/compiler/cmake/linux/FastXml.cmake | 43 + .../compiler/cmake/linux/LowLevel.cmake | 67 + .../compiler/cmake/linux/LowLevelAABB.cmake | 53 + .../cmake/linux/LowLevelDynamics.cmake | 53 + .../source/compiler/cmake/linux/PhysX.cmake | 111 + .../cmake/linux/PhysXCharacterKinematic.cmake | 49 + .../compiler/cmake/linux/PhysXCommon.cmake | 65 + .../compiler/cmake/linux/PhysXCooking.cmake | 64 + .../cmake/linux/PhysXExtensions.cmake | 56 + .../cmake/linux/PhysXFoundation.cmake | 110 + .../compiler/cmake/linux/PhysXPvdSDK.cmake | 44 + .../compiler/cmake/linux/PhysXTask.cmake | 45 + .../compiler/cmake/linux/PhysXVehicle.cmake | 47 + .../compiler/cmake/linux/SceneQuery.cmake | 50 + .../cmake/linux/SimulationController.cmake | 51 + .../source/compiler/cmake/mac/CMakeLists.txt | 92 + .../source/compiler/cmake/mac/FastXml.cmake | 45 + .../source/compiler/cmake/mac/LowLevel.cmake | 51 + .../compiler/cmake/mac/LowLevelAABB.cmake | 53 + .../compiler/cmake/mac/LowLevelDynamics.cmake | 50 + .../source/compiler/cmake/mac/PhysX.cmake | 72 + .../cmake/mac/PhysXCharacterKinematic.cmake | 41 + .../compiler/cmake/mac/PhysXCommon.cmake | 70 + .../compiler/cmake/mac/PhysXCooking.cmake | 57 + .../compiler/cmake/mac/PhysXExtensions.cmake | 55 + .../compiler/cmake/mac/PhysXFoundation.cmake | 102 + .../compiler/cmake/mac/PhysXPvdSDK.cmake | 45 + .../source/compiler/cmake/mac/PhysXTask.cmake | 41 + .../compiler/cmake/mac/PhysXVehicle.cmake | 45 + .../compiler/cmake/mac/SceneQuery.cmake | 48 + .../cmake/mac/SimulationController.cmake | 49 + .../compiler/cmake/windows/CMakeLists.txt | 216 + .../compiler/cmake/windows/FastXml.cmake | 55 + .../compiler/cmake/windows/LowLevel.cmake | 82 + .../compiler/cmake/windows/LowLevelAABB.cmake | 84 + .../cmake/windows/LowLevelDynamics.cmake | 83 + .../source/compiler/cmake/windows/PhysX.cmake | 169 + .../windows/PhysXCharacterKinematic.cmake | 65 + .../compiler/cmake/windows/PhysXCommon.cmake | 107 + .../compiler/cmake/windows/PhysXCooking.cmake | 94 + .../cmake/windows/PhysXExtensions.cmake | 77 + .../cmake/windows/PhysXFoundation.cmake | 126 + .../compiler/cmake/windows/PhysXPvdSDK.cmake | 89 + .../compiler/cmake/windows/PhysXTask.cmake | 73 + .../compiler/cmake/windows/PhysXVehicle.cmake | 64 + .../compiler/cmake/windows/SceneQuery.cmake | 81 + .../cmake/windows/SimulationController.cmake | 81 + .../source/compiler/resource_x64/PhysX.rc | 88 + .../compiler/resource_x64/PhysXCommon.rc | 88 + .../compiler/resource_x64/PhysXCooking.rc | 91 + .../compiler/resource_x64/PhysXFoundation.rc | Bin 0 -> 4644 bytes .../source/compiler/resource_x64/resource.h | 44 + .../source/compiler/resource_x86/PhysX.rc | 88 + .../compiler/resource_x86/PhysXCommon.rc | 92 + .../compiler/resource_x86/PhysXCooking.rc | 91 + .../compiler/resource_x86/PhysXFoundation.rc | Bin 0 -> 4642 bytes .../source/compiler/resource_x86/resource.h | 44 + .../physx/source/fastxml/include/PsFastXml.h | 167 + .../physx/source/fastxml/src/PsFastXml.cpp | 833 ++ .../filebuf/include/PsAsciiConversion.h | 99 + .../filebuf/include/PsAsciiConversion.inl | 566 ++ .../source/filebuf/include/PsFileBuffer.h | 250 + .../physx/source/filebuf/include/PsIOStream.h | 137 + .../source/filebuf/include/PsIOStream.inl | 451 + .../source/filebuf/include/PsMemoryBuffer.h | 449 + .../physx/source/foundation/include/Ps.h | 70 + .../foundation/include/PsAlignedMalloc.h | 88 + .../source/foundation/include/PsAlloca.h | 76 + .../source/foundation/include/PsAllocator.h | 367 + .../physx/source/foundation/include/PsAoS.h | 45 + .../physx/source/foundation/include/PsArray.h | 721 ++ .../source/foundation/include/PsAtomic.h | 63 + .../foundation/include/PsBasicTemplates.h | 146 + .../source/foundation/include/PsBitUtils.h | 109 + .../source/foundation/include/PsBroadcast.h | 277 + .../physx/source/foundation/include/PsCpu.h | 47 + .../physx/source/foundation/include/PsFPU.h | 103 + .../source/foundation/include/PsFoundation.h | 223 + .../physx/source/foundation/include/PsHash.h | 162 + .../foundation/include/PsHashInternals.h | 795 ++ .../source/foundation/include/PsHashMap.h | 118 + .../source/foundation/include/PsHashSet.h | 127 + .../foundation/include/PsInlineAllocator.h | 91 + .../source/foundation/include/PsInlineAoS.h | 48 + .../source/foundation/include/PsInlineArray.h | 68 + .../source/foundation/include/PsIntrinsics.h | 47 + .../source/foundation/include/PsMathUtils.h | 701 ++ .../physx/source/foundation/include/PsMutex.h | 182 + .../physx/source/foundation/include/PsPool.h | 298 + .../physx/source/foundation/include/PsSList.h | 140 + .../source/foundation/include/PsSocket.h | 186 + .../physx/source/foundation/include/PsSort.h | 130 + .../foundation/include/PsSortInternals.h | 188 + .../source/foundation/include/PsString.h | 90 + .../physx/source/foundation/include/PsSync.h | 138 + .../foundation/include/PsTempAllocator.h | 62 + .../source/foundation/include/PsThread.h | 384 + .../physx/source/foundation/include/PsTime.h | 95 + .../foundation/include/PsUserAllocated.h | 92 + .../source/foundation/include/PsUtilities.h | 165 + .../source/foundation/include/PsVecMath.h | 1337 +++ .../foundation/include/PsVecMathAoSScalar.h | 250 + .../include/PsVecMathAoSScalarInline.h | 2275 +++++ .../source/foundation/include/PsVecMathSSE.h | 68 + .../foundation/include/PsVecMathUtilities.h | 57 + .../source/foundation/include/PsVecQuat.h | 466 + .../foundation/include/PsVecTransform.h | 283 + .../foundation/include/unix/PsUnixAoS.h | 47 + .../foundation/include/unix/PsUnixFPU.h | 69 + .../foundation/include/unix/PsUnixInlineAoS.h | 45 + .../include/unix/PsUnixIntrinsics.h | 153 + .../include/unix/PsUnixTrigConstants.h | 93 + .../include/unix/neon/PsUnixNeonAoS.h | 140 + .../include/unix/neon/PsUnixNeonInlineAoS.h | 3597 ++++++++ .../include/unix/sse2/PsUnixSse2AoS.h | 191 + .../include/unix/sse2/PsUnixSse2InlineAoS.h | 3239 +++++++ .../foundation/include/windows/PsWindowsAoS.h | 142 + .../foundation/include/windows/PsWindowsFPU.h | 51 + .../include/windows/PsWindowsInclude.h | 104 + .../include/windows/PsWindowsInlineAoS.h | 3132 +++++++ .../include/windows/PsWindowsIntrinsics.h | 193 + .../include/windows/PsWindowsTrigConstants.h | 98 + .../source/foundation/src/PsAllocator.cpp | 124 + .../physx/source/foundation/src/PsAssert.cpp | 90 + .../source/foundation/src/PsFoundation.cpp | 284 + .../source/foundation/src/PsMathUtils.cpp | 212 + .../physx/source/foundation/src/PsString.cpp | 185 + .../source/foundation/src/PsTempAllocator.cpp | 129 + .../source/foundation/src/PsUtilities.cpp | 73 + .../foundation/src/unix/PsUnixAtomic.cpp | 102 + .../source/foundation/src/unix/PsUnixCpu.cpp | 58 + .../source/foundation/src/unix/PsUnixFPU.cpp | 117 + .../foundation/src/unix/PsUnixMutex.cpp | 172 + .../foundation/src/unix/PsUnixPrintString.cpp | 52 + .../foundation/src/unix/PsUnixSList.cpp | 156 + .../foundation/src/unix/PsUnixSocket.cpp | 483 ++ .../source/foundation/src/unix/PsUnixSync.cpp | 164 + .../foundation/src/unix/PsUnixThread.cpp | 504 ++ .../source/foundation/src/unix/PsUnixTime.cpp | 120 + .../src/windows/PsWindowsAtomic.cpp | 96 + .../foundation/src/windows/PsWindowsCpu.cpp | 64 + .../foundation/src/windows/PsWindowsFPU.cpp | 88 + .../foundation/src/windows/PsWindowsMutex.cpp | 162 + .../src/windows/PsWindowsPrintString.cpp | 54 + .../foundation/src/windows/PsWindowsSList.cpp | 77 + .../src/windows/PsWindowsSocket.cpp | 450 + .../foundation/src/windows/PsWindowsSync.cpp | 81 + .../src/windows/PsWindowsThread.cpp | 425 + .../foundation/src/windows/PsWindowsTime.cpp | 101 + .../physx/source/geomutils/include/GuAxes.h | 81 + .../physx/source/geomutils/include/GuBox.h | 246 + .../geomutils/include/GuDistanceSegmentBox.h | 57 + .../include/GuDistanceSegmentSegment.h | 65 + .../geomutils/include/GuIntersectionBoxBox.h | 54 + .../include/GuIntersectionTriangleBox.h | 91 + .../include/GuIntersectionTriangleBoxRef.h | 251 + .../source/geomutils/include/GuRaycastTests.h | 72 + .../source/geomutils/include/GuSIMDHelpers.h | 98 + .../source/geomutils/include/GuSegment.h | 182 + .../source/geomutils/src/GuAABBTreeBuild.cpp | 307 + .../source/geomutils/src/GuAABBTreeBuild.h | 185 + .../source/geomutils/src/GuAABBTreeQuery.h | 239 + .../source/geomutils/src/GuBVHStructure.cpp | 186 + .../source/geomutils/src/GuBVHStructure.h | 200 + .../source/geomutils/src/GuBVHTestsSIMD.h | 259 + .../physx/source/geomutils/src/GuBounds.cpp | 611 ++ .../physx/source/geomutils/src/GuBounds.h | 157 + .../physx/source/geomutils/src/GuBox.cpp | 132 + .../source/geomutils/src/GuCCTSweepTests.cpp | 440 + .../physx/source/geomutils/src/GuCapsule.cpp | 64 + .../physx/source/geomutils/src/GuCapsule.h | 92 + .../source/geomutils/src/GuCenterExtents.h | 131 + .../source/geomutils/src/GuGeometryQuery.cpp | 351 + .../source/geomutils/src/GuGeometryUnion.cpp | 120 + .../source/geomutils/src/GuGeometryUnion.h | 246 + .../physx/source/geomutils/src/GuInternal.cpp | 159 + .../physx/source/geomutils/src/GuInternal.h | 151 + .../physx/source/geomutils/src/GuMTD.cpp | 1457 ++++ src/PhysX/physx/source/geomutils/src/GuMTD.h | 60 + .../source/geomutils/src/GuMeshFactory.cpp | 702 ++ .../source/geomutils/src/GuMeshFactory.h | 142 + .../physx/source/geomutils/src/GuMetaData.cpp | 598 ++ .../source/geomutils/src/GuOverlapTests.cpp | 695 ++ .../source/geomutils/src/GuOverlapTests.h | 117 + .../source/geomutils/src/GuRaycastTests.cpp | 564 ++ .../source/geomutils/src/GuSerialize.cpp | 381 + .../physx/source/geomutils/src/GuSerialize.h | 196 + .../physx/source/geomutils/src/GuSphere.h | 108 + .../physx/source/geomutils/src/GuSweepMTD.cpp | 1218 +++ .../physx/source/geomutils/src/GuSweepMTD.h | 95 + .../geomutils/src/GuSweepSharedTests.cpp | 725 ++ .../source/geomutils/src/GuSweepSharedTests.h | 56 + .../source/geomutils/src/GuSweepTests.cpp | 604 ++ .../physx/source/geomutils/src/GuSweepTests.h | 136 + .../src/ccd/GuCCDSweepConvexMesh.cpp | 728 ++ .../geomutils/src/ccd/GuCCDSweepConvexMesh.h | 178 + .../src/ccd/GuCCDSweepPrimitives.cpp | 286 + .../src/common/GuBarycentricCoordinates.cpp | 89 + .../src/common/GuBarycentricCoordinates.h | 93 + .../geomutils/src/common/GuBoxConversion.h | 121 + .../source/geomutils/src/common/GuEdgeCache.h | 85 + .../geomutils/src/common/GuEdgeListData.h | 153 + .../geomutils/src/common/GuSeparatingAxes.cpp | 64 + .../geomutils/src/common/GuSeparatingAxes.h | 91 + .../geomutils/src/contact/GuContactBoxBox.cpp | 703 ++ .../src/contact/GuContactCapsuleBox.cpp | 457 + .../src/contact/GuContactCapsuleCapsule.cpp | 155 + .../src/contact/GuContactCapsuleConvex.cpp | 587 ++ .../src/contact/GuContactCapsuleMesh.cpp | 635 ++ .../src/contact/GuContactConvexConvex.cpp | 1033 +++ .../src/contact/GuContactConvexMesh.cpp | 1447 ++++ .../src/contact/GuContactMethodImpl.h | 170 + .../src/contact/GuContactPlaneBox.cpp | 128 + .../src/contact/GuContactPlaneCapsule.cpp | 80 + .../src/contact/GuContactPlaneConvex.cpp | 101 + .../src/contact/GuContactPolygonPolygon.cpp | 861 ++ .../src/contact/GuContactPolygonPolygon.h | 69 + .../src/contact/GuContactSphereBox.cpp | 181 + .../src/contact/GuContactSphereCapsule.cpp | 82 + .../src/contact/GuContactSphereMesh.cpp | 614 ++ .../src/contact/GuContactSpherePlane.cpp | 68 + .../src/contact/GuContactSphereSphere.cpp | 68 + .../geomutils/src/contact/GuFeatureCode.cpp | 128 + .../geomutils/src/contact/GuFeatureCode.h | 56 + .../geomutils/src/convex/GuBigConvexData.cpp | 203 + .../geomutils/src/convex/GuBigConvexData.h | 98 + .../geomutils/src/convex/GuBigConvexData2.h | 95 + .../geomutils/src/convex/GuConvexEdgeFlags.h | 59 + .../geomutils/src/convex/GuConvexHelper.cpp | 137 + .../geomutils/src/convex/GuConvexHelper.h | 66 + .../geomutils/src/convex/GuConvexMesh.cpp | 415 + .../geomutils/src/convex/GuConvexMesh.h | 178 + .../geomutils/src/convex/GuConvexMeshData.h | 215 + .../src/convex/GuConvexSupportTable.cpp | 44 + .../src/convex/GuConvexSupportTable.h | 117 + .../src/convex/GuConvexUtilsInternal.cpp | 78 + .../src/convex/GuConvexUtilsInternal.h | 67 + .../source/geomutils/src/convex/GuCubeIndex.h | 155 + .../geomutils/src/convex/GuHillClimbing.cpp | 96 + .../geomutils/src/convex/GuHillClimbing.h | 46 + .../geomutils/src/convex/GuShapeConvex.cpp | 516 ++ .../geomutils/src/convex/GuShapeConvex.h | 100 + .../src/distance/GuDistancePointBox.cpp | 66 + .../src/distance/GuDistancePointBox.h | 70 + .../src/distance/GuDistancePointSegment.h | 90 + .../src/distance/GuDistancePointTriangle.cpp | 353 + .../src/distance/GuDistancePointTriangle.h | 125 + .../distance/GuDistancePointTriangleSIMD.h | 54 + .../src/distance/GuDistanceSegmentBox.cpp | 549 ++ .../src/distance/GuDistanceSegmentSegment.cpp | 576 ++ .../distance/GuDistanceSegmentSegmentSIMD.h | 57 + .../distance/GuDistanceSegmentTriangle.cpp | 541 ++ .../src/distance/GuDistanceSegmentTriangle.h | 63 + .../distance/GuDistanceSegmentTriangleSIMD.h | 54 + .../physx/source/geomutils/src/gjk/GuEPA.cpp | 615 ++ .../physx/source/geomutils/src/gjk/GuEPA.h | 60 + .../source/geomutils/src/gjk/GuEPAFacet.h | 306 + .../physx/source/geomutils/src/gjk/GuGJK.h | 219 + .../geomutils/src/gjk/GuGJKPenetration.h | 320 + .../source/geomutils/src/gjk/GuGJKRaycast.h | 295 + .../source/geomutils/src/gjk/GuGJKSimplex.cpp | 216 + .../source/geomutils/src/gjk/GuGJKSimplex.h | 473 + .../source/geomutils/src/gjk/GuGJKTest.cpp | 70 + .../source/geomutils/src/gjk/GuGJKTest.h | 58 + .../source/geomutils/src/gjk/GuGJKType.h | 177 + .../source/geomutils/src/gjk/GuGJKUtil.h | 76 + .../physx/source/geomutils/src/gjk/GuVecBox.h | 226 + .../source/geomutils/src/gjk/GuVecCapsule.h | 238 + .../source/geomutils/src/gjk/GuVecConvex.h | 179 + .../geomutils/src/gjk/GuVecConvexHull.h | 501 ++ .../src/gjk/GuVecConvexHullNoScale.h | 176 + .../source/geomutils/src/gjk/GuVecPlane.h | 224 + .../source/geomutils/src/gjk/GuVecSphere.h | 243 + .../source/geomutils/src/gjk/GuVecTriangle.h | 268 + .../source/geomutils/src/hf/GuEntityReport.h | 56 + .../source/geomutils/src/hf/GuHeightField.cpp | 711 ++ .../source/geomutils/src/hf/GuHeightField.h | 1266 +++ .../geomutils/src/hf/GuHeightFieldData.h | 87 + .../geomutils/src/hf/GuHeightFieldUtil.cpp | 832 ++ .../geomutils/src/hf/GuHeightFieldUtil.h | 904 ++ .../geomutils/src/hf/GuOverlapTestsHF.cpp | 751 ++ .../source/geomutils/src/hf/GuSweepsHF.cpp | 606 ++ .../src/intersection/GuIntersectionBoxBox.cpp | 139 + .../GuIntersectionCapsuleTriangle.cpp | 61 + .../GuIntersectionCapsuleTriangle.h | 137 + .../intersection/GuIntersectionEdgeEdge.cpp | 84 + .../src/intersection/GuIntersectionEdgeEdge.h | 52 + .../src/intersection/GuIntersectionRay.h | 38 + .../src/intersection/GuIntersectionRayBox.cpp | 449 + .../src/intersection/GuIntersectionRayBox.h | 91 + .../intersection/GuIntersectionRayBoxSIMD.h | 50 + .../intersection/GuIntersectionRayCapsule.cpp | 245 + .../intersection/GuIntersectionRayCapsule.h | 93 + .../src/intersection/GuIntersectionRayPlane.h | 58 + .../intersection/GuIntersectionRaySphere.cpp | 105 + .../intersection/GuIntersectionRaySphere.h | 50 + .../intersection/GuIntersectionRayTriangle.h | 179 + .../intersection/GuIntersectionSphereBox.cpp | 88 + .../intersection/GuIntersectionSphereBox.h | 54 + .../GuIntersectionTriangleBox.cpp | 198 + .../source/geomutils/src/mesh/GuBV32.cpp | 276 + .../physx/source/geomutils/src/mesh/GuBV32.h | 146 + .../source/geomutils/src/mesh/GuBV32Build.cpp | 530 ++ .../source/geomutils/src/mesh/GuBV32Build.h | 50 + .../physx/source/geomutils/src/mesh/GuBV4.cpp | 390 + .../physx/source/geomutils/src/mesh/GuBV4.h | 283 + .../source/geomutils/src/mesh/GuBV4Build.cpp | 1503 ++++ .../source/geomutils/src/mesh/GuBV4Build.h | 125 + .../source/geomutils/src/mesh/GuBV4Settings.h | 40 + .../src/mesh/GuBV4_AABBAABBSweepTest.h | 114 + .../geomutils/src/mesh/GuBV4_AABBSweep.cpp | 39 + .../src/mesh/GuBV4_BoxBoxOverlapTest.h | 201 + .../geomutils/src/mesh/GuBV4_BoxOverlap.cpp | 463 + .../src/mesh/GuBV4_BoxOverlap_Internal.h | 103 + .../src/mesh/GuBV4_BoxSweep_Internal.h | 518 ++ .../src/mesh/GuBV4_BoxSweep_Params.h | 211 + .../geomutils/src/mesh/GuBV4_CapsuleSweep.cpp | 173 + .../src/mesh/GuBV4_CapsuleSweepAA.cpp | 110 + .../src/mesh/GuBV4_CapsuleSweep_Internal.h | 434 + .../source/geomutils/src/mesh/GuBV4_Common.h | 452 + .../geomutils/src/mesh/GuBV4_Internal.h | 318 + .../geomutils/src/mesh/GuBV4_OBBSweep.cpp | 170 + .../mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h | 111 + .../GuBV4_ProcessStreamNoOrder_SegmentAABB.h | 55 + ...rocessStreamNoOrder_SegmentAABB_Inflated.h | 55 + .../GuBV4_ProcessStreamNoOrder_SphereAABB.h | 113 + .../mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h | 111 + .../GuBV4_ProcessStreamOrdered_SegmentAABB.h | 67 + ...rocessStreamOrdered_SegmentAABB_Inflated.h | 67 + .../geomutils/src/mesh/GuBV4_Raycast.cpp | 622 ++ .../source/geomutils/src/mesh/GuBV4_Slabs.h | 193 + .../src/mesh/GuBV4_Slabs_KajiyaNoOrder.h | 309 + .../src/mesh/GuBV4_Slabs_KajiyaOrdered.h | 552 ++ .../src/mesh/GuBV4_Slabs_SwizzledNoOrder.h | 133 + .../src/mesh/GuBV4_Slabs_SwizzledOrdered.h | 158 + .../src/mesh/GuBV4_SphereOverlap.cpp | 326 + .../geomutils/src/mesh/GuBV4_SphereSweep.cpp | 386 + .../source/geomutils/src/mesh/GuBVConstants.h | 44 + .../source/geomutils/src/mesh/GuMeshData.h | 300 + .../source/geomutils/src/mesh/GuMeshQuery.cpp | 304 + .../geomutils/src/mesh/GuMidphaseBV4.cpp | 999 +++ .../geomutils/src/mesh/GuMidphaseInterface.h | 417 + .../geomutils/src/mesh/GuMidphaseRTree.cpp | 887 ++ .../geomutils/src/mesh/GuOverlapTestsMesh.cpp | 241 + .../source/geomutils/src/mesh/GuRTree.cpp | 430 + .../physx/source/geomutils/src/mesh/GuRTree.h | 297 + .../geomutils/src/mesh/GuRTreeQueries.cpp | 573 ++ .../geomutils/src/mesh/GuSweepConvexTri.h | 105 + .../source/geomutils/src/mesh/GuSweepMesh.h | 169 + .../geomutils/src/mesh/GuSweepsMesh.cpp | 603 ++ .../source/geomutils/src/mesh/GuTriangle32.h | 132 + .../geomutils/src/mesh/GuTriangleCache.h | 207 + .../geomutils/src/mesh/GuTriangleMesh.cpp | 232 + .../geomutils/src/mesh/GuTriangleMesh.h | 284 + .../geomutils/src/mesh/GuTriangleMeshBV4.cpp | 76 + .../geomutils/src/mesh/GuTriangleMeshBV4.h | 76 + .../src/mesh/GuTriangleMeshRTree.cpp | 151 + .../geomutils/src/mesh/GuTriangleMeshRTree.h | 81 + .../src/mesh/GuTriangleVertexPointers.h | 65 + .../geomutils/src/pcm/GuPCMContactBoxBox.cpp | 1030 +++ .../src/pcm/GuPCMContactBoxConvex.cpp | 274 + .../src/pcm/GuPCMContactCapsuleBox.cpp | 224 + .../src/pcm/GuPCMContactCapsuleCapsule.cpp | 297 + .../src/pcm/GuPCMContactCapsuleConvex.cpp | 266 + .../pcm/GuPCMContactCapsuleHeightField.cpp | 158 + .../src/pcm/GuPCMContactCapsuleMesh.cpp | 185 + .../src/pcm/GuPCMContactConvexCommon.cpp | 1354 +++ .../src/pcm/GuPCMContactConvexCommon.h | 437 + .../src/pcm/GuPCMContactConvexConvex.cpp | 294 + .../src/pcm/GuPCMContactConvexHeightField.cpp | 267 + .../src/pcm/GuPCMContactConvexMesh.cpp | 259 + .../geomutils/src/pcm/GuPCMContactGen.h | 82 + .../src/pcm/GuPCMContactGenBoxConvex.cpp | 811 ++ .../src/pcm/GuPCMContactGenSphereCapsule.cpp | 444 + .../geomutils/src/pcm/GuPCMContactGenUtil.h | 464 + .../src/pcm/GuPCMContactMeshCallback.h | 208 + .../src/pcm/GuPCMContactPlaneBox.cpp | 219 + .../src/pcm/GuPCMContactPlaneCapsule.cpp | 133 + .../src/pcm/GuPCMContactPlaneConvex.cpp | 159 + .../src/pcm/GuPCMContactSphereBox.cpp | 156 + .../src/pcm/GuPCMContactSphereCapsule.cpp | 121 + .../src/pcm/GuPCMContactSphereConvex.cpp | 284 + .../src/pcm/GuPCMContactSphereHeightField.cpp | 159 + .../src/pcm/GuPCMContactSphereMesh.cpp | 178 + .../src/pcm/GuPCMContactSpherePlane.cpp | 86 + .../src/pcm/GuPCMContactSphereSphere.cpp | 84 + .../geomutils/src/pcm/GuPCMShapeConvex.cpp | 210 + .../geomutils/src/pcm/GuPCMShapeConvex.h | 68 + .../src/pcm/GuPCMTriangleContactGen.cpp | 1254 +++ .../src/pcm/GuPCMTriangleContactGen.h | 69 + .../src/pcm/GuPersistentContactManifold.cpp | 2432 ++++++ .../src/pcm/GuPersistentContactManifold.h | 855 ++ .../geomutils/src/sweep/GuSweepBoxBox.cpp | 272 + .../geomutils/src/sweep/GuSweepBoxBox.h | 49 + .../geomutils/src/sweep/GuSweepBoxSphere.cpp | 158 + .../geomutils/src/sweep/GuSweepBoxSphere.h | 49 + .../sweep/GuSweepBoxTriangle_FeatureBased.cpp | 622 ++ .../sweep/GuSweepBoxTriangle_FeatureBased.h | 67 + .../src/sweep/GuSweepBoxTriangle_SAT.cpp | 39 + .../src/sweep/GuSweepBoxTriangle_SAT.h | 235 + .../geomutils/src/sweep/GuSweepCapsuleBox.cpp | 215 + .../geomutils/src/sweep/GuSweepCapsuleBox.h | 49 + .../src/sweep/GuSweepCapsuleCapsule.cpp | 344 + .../src/sweep/GuSweepCapsuleCapsule.h | 48 + .../src/sweep/GuSweepCapsuleTriangle.cpp | 334 + .../src/sweep/GuSweepCapsuleTriangle.h | 75 + .../src/sweep/GuSweepSphereCapsule.cpp | 103 + .../src/sweep/GuSweepSphereCapsule.h | 50 + .../src/sweep/GuSweepSphereSphere.cpp | 116 + .../geomutils/src/sweep/GuSweepSphereSphere.h | 46 + .../src/sweep/GuSweepSphereTriangle.cpp | 524 ++ .../src/sweep/GuSweepSphereTriangle.h | 156 + .../src/sweep/GuSweepTriangleUtils.cpp | 223 + .../src/sweep/GuSweepTriangleUtils.h | 338 + .../immediatemode/src/NpImmediateMode.cpp | 769 ++ .../lowlevel/api/include/PxsMaterialCore.h | 167 + .../lowlevel/api/include/PxsMaterialManager.h | 149 + .../source/lowlevel/api/include/PxvConfig.h | 47 + .../source/lowlevel/api/include/PxvDynamics.h | 157 + .../source/lowlevel/api/include/PxvGeometry.h | 93 + .../source/lowlevel/api/include/PxvGlobals.h | 116 + .../source/lowlevel/api/include/PxvManager.h | 239 + .../source/lowlevel/api/include/PxvSimStats.h | 114 + .../source/lowlevel/api/src/px_globals.cpp | 117 + .../include/collision/PxcContactMethodImpl.h | 95 + .../include/pipeline/PxcCCDStateStreamPair.h | 29 + .../pipeline/PxcConstraintBlockStream.h | 166 + .../common/include/pipeline/PxcContactCache.h | 64 + .../include/pipeline/PxcMaterialMethodImpl.h | 69 + .../common/include/pipeline/PxcNpBatch.h | 65 + .../common/include/pipeline/PxcNpCache.h | 154 + .../include/pipeline/PxcNpCacheStreamPair.h | 63 + .../include/pipeline/PxcNpContactPrepShared.h | 57 + .../include/pipeline/PxcNpMemBlockPool.h | 119 + .../include/pipeline/PxcNpThreadContext.h | 211 + .../common/include/pipeline/PxcNpWorkUnit.h | 165 + .../common/include/pipeline/PxcRigidBody.h | 118 + .../include/utils/PxcScratchAllocator.h | 138 + .../include/utils/PxcThreadCoherentCache.h | 150 + .../common/src/collision/PxcContact.cpp | 267 + .../common/src/pipeline/PxcContactCache.cpp | 393 + .../src/pipeline/PxcContactMethodImpl.cpp | 279 + .../src/pipeline/PxcMaterialHeightField.cpp | 116 + .../common/src/pipeline/PxcMaterialMesh.cpp | 107 + .../src/pipeline/PxcMaterialMethodImpl.cpp | 140 + .../common/src/pipeline/PxcMaterialShape.cpp | 62 + .../common/src/pipeline/PxcNpBatch.cpp | 445 + .../src/pipeline/PxcNpCacheStreamPair.cpp | 75 + .../src/pipeline/PxcNpContactPrepShared.cpp | 554 ++ .../common/src/pipeline/PxcNpMemBlockPool.cpp | 351 + .../src/pipeline/PxcNpThreadContext.cpp | 93 + .../lowlevel/software/include/PxsBodySim.h | 51 + .../source/lowlevel/software/include/PxsCCD.h | 622 ++ .../software/include/PxsContactManager.h | 178 + .../software/include/PxsContactManagerState.h | 95 + .../lowlevel/software/include/PxsContext.h | 333 + .../include/PxsDefaultMemoryManager.h | 98 + .../software/include/PxsHeapMemoryAllocator.h | 66 + .../PxsIncrementalConstraintPartitioning.h | 40 + .../software/include/PxsIslandManagerTypes.h | 363 + .../software/include/PxsIslandNodeIndex.h | 78 + .../lowlevel/software/include/PxsIslandSim.h | 935 ++ .../software/include/PxsKernelWrangler.h | 49 + .../software/include/PxsMaterialCombiner.h | 143 + .../software/include/PxsMemoryManager.h | 61 + .../include/PxsNphaseImplementationContext.h | 143 + .../lowlevel/software/include/PxsRigidBody.h | 169 + .../lowlevel/software/include/PxsShapeSim.h | 52 + .../software/include/PxsSimpleIslandManager.h | 202 + .../include/PxsSimulationController.h | 139 + .../software/include/PxsTransformCache.h | 144 + .../include/PxvNphaseImplementationContext.h | 217 + .../source/lowlevel/software/src/PxsCCD.cpp | 2060 +++++ .../software/src/PxsContactManager.cpp | 87 + .../lowlevel/software/src/PxsContext.cpp | 623 ++ .../software/src/PxsDefaultMemoryManager.cpp | 74 + .../lowlevel/software/src/PxsIslandSim.cpp | 2323 +++++ .../software/src/PxsMaterialCombiner.cpp | 102 + .../src/PxsNphaseImplementationContext.cpp | 999 +++ .../software/src/PxsSimpleIslandManager.cpp | 377 + .../lowlevelaabb/include/BpAABBManager.h | 610 ++ .../lowlevelaabb/include/BpAABBManagerTasks.h | 109 + .../lowlevelaabb/include/BpBroadPhase.h | 326 + .../lowlevelaabb/include/BpBroadPhaseUpdate.h | 523 ++ .../source/lowlevelaabb/src/BpAABBManager.cpp | 2532 ++++++ .../source/lowlevelaabb/src/BpBroadPhase.cpp | 175 + .../lowlevelaabb/src/BpBroadPhaseABP.cpp | 3402 ++++++++ .../source/lowlevelaabb/src/BpBroadPhaseABP.h | 101 + .../lowlevelaabb/src/BpBroadPhaseMBP.cpp | 3422 ++++++++ .../source/lowlevelaabb/src/BpBroadPhaseMBP.h | 114 + .../lowlevelaabb/src/BpBroadPhaseMBPCommon.h | 199 + .../lowlevelaabb/src/BpBroadPhaseSap.cpp | 1970 +++++ .../source/lowlevelaabb/src/BpBroadPhaseSap.h | 227 + .../lowlevelaabb/src/BpBroadPhaseSapAux.cpp | 964 +++ .../lowlevelaabb/src/BpBroadPhaseSapAux.h | 285 + .../lowlevelaabb/src/BpBroadPhaseShared.cpp | 246 + .../lowlevelaabb/src/BpBroadPhaseShared.h | 243 + .../source/lowlevelaabb/src/BpMBPTasks.cpp | 29 + .../source/lowlevelaabb/src/BpMBPTasks.h | 105 + .../source/lowlevelaabb/src/BpSAPTasks.cpp | 69 + .../source/lowlevelaabb/src/BpSAPTasks.h | 103 + .../lowleveldynamics/include/DyArticulation.h | 284 + .../include/DyArticulationCore.h | 78 + .../include/DyArticulationJointCore.h | 144 + .../lowleveldynamics/include/DyConstraint.h | 89 + .../include/DyConstraintWriteBack.h | 65 + .../lowleveldynamics/include/DyContext.h | 400 + .../include/DyFeatherstoneArticulation.h | 857 ++ .../DyFeatherstoneArticulationJointData.h | 261 + .../include/DyFeatherstoneArticulationUtils.h | 881 ++ .../include/DySleepingConfigulation.h | 42 + .../include/DyThresholdTable.h | 280 + .../include/DyVArticulation.h | 492 ++ .../lowleveldynamics/src/DyArticulation.cpp | 1665 ++++ .../src/DyArticulationContactPrep.cpp | 453 + .../src/DyArticulationContactPrep.h | 97 + .../src/DyArticulationContactPrepPF.cpp | 316 + .../src/DyArticulationFnsDebug.h | 262 + .../src/DyArticulationFnsScalar.h | 397 + .../src/DyArticulationFnsSimd.h | 438 + .../src/DyArticulationHelper.cpp | 524 ++ .../src/DyArticulationHelper.h | 214 + .../src/DyArticulationPImpl.h | 203 + .../src/DyArticulationReference.h | 92 + .../src/DyArticulationSIMD.cpp | 317 + .../src/DyArticulationScalar.cpp | 575 ++ .../src/DyArticulationScalar.h | 101 + .../src/DyArticulationUtils.h | 336 + .../src/DyBodyCoreIntegrator.h | 409 + .../src/DyConstraintPartition.cpp | 917 ++ .../src/DyConstraintPartition.h | 79 + .../lowleveldynamics/src/DyConstraintPrep.h | 101 + .../src/DyConstraintSetup.cpp | 605 ++ .../src/DyConstraintSetupBlock.cpp | 541 ++ .../lowleveldynamics/src/DyContactPrep.cpp | 820 ++ .../lowleveldynamics/src/DyContactPrep.h | 183 + .../lowleveldynamics/src/DyContactPrep4.cpp | 1535 ++++ .../lowleveldynamics/src/DyContactPrep4PF.cpp | 1030 +++ .../lowleveldynamics/src/DyContactPrepPF.cpp | 664 ++ .../src/DyContactPrepShared.h | 308 + .../lowleveldynamics/src/DyContactReduction.h | 409 + .../src/DyCorrelationBuffer.h | 107 + .../lowleveldynamics/src/DyDynamics.cpp | 3145 +++++++ .../source/lowleveldynamics/src/DyDynamics.h | 490 ++ .../src/DyFeatherstoneArticulation.cpp | 4218 +++++++++ .../src/DyFeatherstoneArticulationLink.h | 79 + .../src/DyFeatherstoneForwardDynamic.cpp | 1677 ++++ .../src/DyFeatherstoneInverseDynamic.cpp | 2120 +++++ .../src/DyFrictionCorrelation.cpp | 298 + .../lowleveldynamics/src/DyFrictionPatch.h | 83 + .../src/DyFrictionPatchStreamPair.h | 128 + .../src/DyRigidBodyToSolverBody.cpp | 93 + .../lowleveldynamics/src/DySolverBody.h | 72 + .../src/DySolverConstraint1D.h | 204 + .../src/DySolverConstraint1D4.h | 106 + .../src/DySolverConstraint1DStep.h | 222 + .../src/DySolverConstraintDesc.h | 142 + .../src/DySolverConstraintExtShared.h | 57 + .../src/DySolverConstraintTypes.h | 72 + .../src/DySolverConstraints.cpp | 1261 +++ .../src/DySolverConstraintsBlock.cpp | 1227 +++ .../src/DySolverConstraintsShared.h | 171 + .../lowleveldynamics/src/DySolverContact.h | 228 + .../lowleveldynamics/src/DySolverContact4.h | 179 + .../lowleveldynamics/src/DySolverContactPF.h | 123 + .../lowleveldynamics/src/DySolverContactPF4.h | 155 + .../lowleveldynamics/src/DySolverContext.h | 66 + .../lowleveldynamics/src/DySolverControl.cpp | 770 ++ .../lowleveldynamics/src/DySolverControl.h | 167 + .../src/DySolverControlPF.cpp | 760 ++ .../lowleveldynamics/src/DySolverControlPF.h | 71 + .../lowleveldynamics/src/DySolverCore.h | 251 + .../source/lowleveldynamics/src/DySolverExt.h | 85 + .../src/DySolverPFConstraints.cpp | 879 ++ .../src/DySolverPFConstraintsBlock.cpp | 985 +++ .../source/lowleveldynamics/src/DySpatial.h | 142 + .../lowleveldynamics/src/DyTGSContactPrep.cpp | 3381 ++++++++ .../src/DyTGSContactPrepBlock.cpp | 3658 ++++++++ .../lowleveldynamics/src/DyTGSDynamics.cpp | 3307 +++++++ .../lowleveldynamics/src/DyTGSDynamics.h | 637 ++ .../lowleveldynamics/src/DyTGSPartition.cpp | 961 +++ .../lowleveldynamics/src/DyTGSPartition.h | 79 + .../lowleveldynamics/src/DyThreadContext.cpp | 110 + .../lowleveldynamics/src/DyThreadContext.h | 255 + .../lowleveldynamics/src/DyThresholdTable.cpp | 68 + src/PhysX/physx/source/physx/src/NpActor.cpp | 486 ++ src/PhysX/physx/source/physx/src/NpActor.h | 163 + .../physx/source/physx/src/NpActorTemplate.h | 254 + .../physx/source/physx/src/NpAggregate.cpp | 421 + .../physx/source/physx/src/NpAggregate.h | 100 + .../physx/source/physx/src/NpArticulation.cpp | 247 + .../physx/source/physx/src/NpArticulation.h | 143 + .../source/physx/src/NpArticulationJoint.cpp | 381 + .../source/physx/src/NpArticulationJoint.h | 357 + .../NpArticulationJointReducedCoordinate.cpp | 242 + .../NpArticulationJointReducedCoordinate.h | 108 + .../source/physx/src/NpArticulationLink.cpp | 620 ++ .../source/physx/src/NpArticulationLink.h | 177 + .../src/NpArticulationReducedCoordinate.cpp | 424 + .../src/NpArticulationReducedCoordinate.h | 169 + .../source/physx/src/NpArticulationTemplate.h | 568 ++ .../physx/source/physx/src/NpBatchQuery.cpp | 603 ++ .../physx/source/physx/src/NpBatchQuery.h | 166 + src/PhysX/physx/source/physx/src/NpCast.h | 103 + .../physx/source/physx/src/NpConnector.h | 136 + .../physx/source/physx/src/NpConstraint.cpp | 407 + .../physx/source/physx/src/NpConstraint.h | 131 + .../physx/source/physx/src/NpFactory.cpp | 850 ++ src/PhysX/physx/source/physx/src/NpFactory.h | 266 + .../physx/source/physx/src/NpMaterial.cpp | 205 + src/PhysX/physx/source/physx/src/NpMaterial.h | 122 + .../source/physx/src/NpMaterialManager.h | 164 + .../physx/source/physx/src/NpMetaData.cpp | 546 ++ .../physx/source/physx/src/NpPhysics.cpp | 747 ++ src/PhysX/physx/source/physx/src/NpPhysics.h | 247 + .../physx/src/NpPhysicsInsertionCallback.h | 72 + .../physx/src/NpPtrTableStorageManager.h | 105 + .../physx/src/NpPvdSceneQueryCollector.cpp | 182 + .../physx/src/NpPvdSceneQueryCollector.h | 231 + .../physx/source/physx/src/NpQueryShared.h | 93 + .../physx/source/physx/src/NpReadCheck.cpp | 83 + .../physx/source/physx/src/NpReadCheck.h | 69 + .../source/physx/src/NpRigidActorTemplate.h | 452 + .../physx/src/NpRigidActorTemplateInternal.h | 69 + .../source/physx/src/NpRigidBodyTemplate.h | 631 ++ .../physx/source/physx/src/NpRigidDynamic.cpp | 566 ++ .../physx/source/physx/src/NpRigidDynamic.h | 176 + .../physx/source/physx/src/NpRigidStatic.cpp | 163 + .../physx/source/physx/src/NpRigidStatic.h | 115 + src/PhysX/physx/source/physx/src/NpScene.cpp | 3022 +++++++ src/PhysX/physx/source/physx/src/NpScene.h | 496 ++ .../physx/source/physx/src/NpSceneAccessor.h | 60 + .../physx/source/physx/src/NpSceneQueries.cpp | 850 ++ .../physx/source/physx/src/NpSceneQueries.h | 236 + .../source/physx/src/NpSerializerAdapter.cpp | 182 + src/PhysX/physx/source/physx/src/NpShape.cpp | 823 ++ src/PhysX/physx/source/physx/src/NpShape.h | 219 + .../physx/source/physx/src/NpShapeManager.cpp | 799 ++ .../physx/source/physx/src/NpShapeManager.h | 134 + .../physx/source/physx/src/NpWriteCheck.cpp | 92 + .../physx/source/physx/src/NpWriteCheck.h | 79 + .../source/physx/src/PvdMetaDataBindingData.h | 82 + .../physx/src/PvdMetaDataPvdBinding.cpp | 1635 ++++ .../source/physx/src/PvdMetaDataPvdBinding.h | 151 + .../source/physx/src/PvdPhysicsClient.cpp | 216 + .../physx/source/physx/src/PvdPhysicsClient.h | 95 + .../physx/source/physx/src/PvdTypeNames.h | 174 + .../source/physx/src/buffering/ScbActor.cpp | 53 + .../source/physx/src/buffering/ScbActor.h | 207 + .../physx/src/buffering/ScbAggregate.cpp | 139 + .../source/physx/src/buffering/ScbAggregate.h | 238 + .../physx/src/buffering/ScbArticulation.h | 414 + .../src/buffering/ScbArticulationJoint.h | 578 ++ .../source/physx/src/buffering/ScbBase.cpp | 47 + .../source/physx/src/buffering/ScbBase.h | 318 + .../source/physx/src/buffering/ScbBody.h | 1134 +++ .../physx/src/buffering/ScbConstraint.h | 313 + .../source/physx/src/buffering/ScbDefs.h | 210 + .../physx/src/buffering/ScbMetaData.cpp | 169 + .../source/physx/src/buffering/ScbNpDeps.h | 77 + .../physx/src/buffering/ScbRigidObject.h | 511 ++ .../physx/src/buffering/ScbRigidStatic.h | 163 + .../source/physx/src/buffering/ScbScene.cpp | 1334 +++ .../source/physx/src/buffering/ScbScene.h | 753 ++ .../physx/src/buffering/ScbSceneBuffer.h | 153 + .../physx/src/buffering/ScbScenePvdClient.cpp | 909 ++ .../physx/src/buffering/ScbScenePvdClient.h | 202 + .../source/physx/src/buffering/ScbShape.cpp | 144 + .../source/physx/src/buffering/ScbShape.h | 481 ++ .../source/physx/src/buffering/ScbType.h | 54 + .../source/physx/src/device/PhysXIndicator.h | 56 + .../src/device/linux/PhysXIndicatorLinux.cpp | 51 + .../source/physx/src/device/nvPhysXtoDrv.h | 93 + .../device/windows/PhysXIndicatorWindows.cpp | 131 + .../physx/source/physx/src/gpu/PxGpu.cpp | 93 + .../physx/src/gpu/PxPhysXGpuModuleLoader.cpp | 194 + .../src/windows/NpWindowsDelayLoadHook.cpp | 90 + .../src/CctBoxController.cpp | 197 + .../src/CctBoxController.h | 112 + .../src/CctCapsuleController.cpp | 192 + .../src/CctCapsuleController.h | 111 + .../src/CctCharacterController.cpp | 2552 ++++++ .../src/CctCharacterController.h | 481 ++ .../src/CctCharacterControllerCallbacks.cpp | 1106 +++ .../src/CctCharacterControllerManager.cpp | 661 ++ .../src/CctCharacterControllerManager.h | 147 + .../src/CctController.cpp | 235 + .../src/CctController.h | 130 + .../src/CctInternalStructs.h | 86 + .../src/CctObstacleContext.cpp | 538 ++ .../src/CctObstacleContext.h | 136 + .../src/CctSweptBox.cpp | 50 + .../physxcharacterkinematic/src/CctSweptBox.h | 55 + .../src/CctSweptCapsule.cpp | 48 + .../src/CctSweptCapsule.h | 56 + .../src/CctSweptVolume.cpp | 74 + .../src/CctSweptVolume.h | 76 + .../physxcharacterkinematic/src/CctUtils.h | 214 + .../source/physxcooking/src/Adjacencies.cpp | 712 ++ .../source/physxcooking/src/Adjacencies.h | 234 + .../physxcooking/src/BVHStructureBuilder.cpp | 215 + .../physxcooking/src/BVHStructureBuilder.h | 68 + .../physx/source/physxcooking/src/Cooking.cpp | 554 ++ .../physx/source/physxcooking/src/Cooking.h | 90 + .../source/physxcooking/src/CookingUtils.cpp | 120 + .../source/physxcooking/src/CookingUtils.h | 79 + .../source/physxcooking/src/EdgeList.cpp | 753 ++ .../physx/source/physxcooking/src/EdgeList.h | 110 + .../source/physxcooking/src/MeshCleaner.cpp | 234 + .../source/physxcooking/src/MeshCleaner.h | 55 + .../source/physxcooking/src/Quantizer.cpp | 338 + .../physx/source/physxcooking/src/Quantizer.h | 76 + .../src/convex/BigConvexDataBuilder.cpp | 358 + .../src/convex/BigConvexDataBuilder.h | 100 + .../src/convex/ConvexHullBuilder.cpp | 755 ++ .../src/convex/ConvexHullBuilder.h | 95 + .../physxcooking/src/convex/ConvexHullLib.cpp | 352 + .../physxcooking/src/convex/ConvexHullLib.h | 99 + .../src/convex/ConvexHullUtils.cpp | 925 ++ .../physxcooking/src/convex/ConvexHullUtils.h | 177 + .../src/convex/ConvexMeshBuilder.cpp | 502 ++ .../src/convex/ConvexMeshBuilder.h | 100 + .../src/convex/ConvexPolygonsBuilder.cpp | 1328 +++ .../src/convex/ConvexPolygonsBuilder.h | 64 + .../src/convex/QuickHullConvexHullLib.cpp | 2581 ++++++ .../src/convex/QuickHullConvexHullLib.h | 99 + .../src/convex/VolumeIntegration.cpp | 797 ++ .../src/convex/VolumeIntegration.h | 102 + .../src/mesh/GrbTriangleMeshCooking.cpp | 29 + .../src/mesh/GrbTriangleMeshCooking.h | 337 + .../src/mesh/HeightFieldCooking.cpp | 84 + .../src/mesh/HeightFieldCooking.h | 35 + .../physxcooking/src/mesh/MeshBuilder.cpp | 78 + .../physxcooking/src/mesh/MeshBuilder.h | 45 + .../physxcooking/src/mesh/QuickSelect.h | 114 + .../physxcooking/src/mesh/RTreeCooking.cpp | 894 ++ .../physxcooking/src/mesh/RTreeCooking.h | 51 + .../src/mesh/TriangleMeshBuilder.cpp | 1385 +++ .../src/mesh/TriangleMeshBuilder.h | 123 + .../windows/WindowsCookingDelayLoadHook.cpp | 82 + .../physxextensions/src/ExtBroadPhase.cpp | 74 + .../physxextensions/src/ExtCollection.cpp | 232 + .../physxextensions/src/ExtConstraintHelper.h | 387 + .../physxextensions/src/ExtContactJoint.cpp | 250 + .../physxextensions/src/ExtContactJoint.h | 122 + .../physxextensions/src/ExtConvexMeshExt.cpp | 91 + .../src/ExtCpuWorkerThread.cpp | 106 + .../physxextensions/src/ExtCpuWorkerThread.h | 79 + .../source/physxextensions/src/ExtD6Joint.cpp | 1089 +++ .../source/physxextensions/src/ExtD6Joint.h | 200 + .../physxextensions/src/ExtD6JointCreate.cpp | 289 + .../src/ExtDefaultCpuDispatcher.cpp | 205 + .../src/ExtDefaultCpuDispatcher.h | 122 + .../src/ExtDefaultErrorCallback.cpp | 104 + .../src/ExtDefaultSimulationFilterShader.cpp | 336 + .../physxextensions/src/ExtDefaultStreams.cpp | 194 + .../physxextensions/src/ExtDistanceJoint.cpp | 334 + .../physxextensions/src/ExtDistanceJoint.h | 139 + .../physxextensions/src/ExtExtensions.cpp | 193 + .../physxextensions/src/ExtFixedJoint.cpp | 178 + .../physxextensions/src/ExtFixedJoint.h | 117 + .../physxextensions/src/ExtInertiaTensor.h | 404 + .../source/physxextensions/src/ExtJoint.cpp | 134 + .../source/physxextensions/src/ExtJoint.h | 640 ++ .../src/ExtJointMetaDataExtensions.h | 65 + .../physxextensions/src/ExtMetaData.cpp | 445 + .../source/physxextensions/src/ExtPlatform.h | 39 + .../physxextensions/src/ExtPrismaticJoint.cpp | 236 + .../physxextensions/src/ExtPrismaticJoint.h | 132 + .../source/physxextensions/src/ExtPvd.cpp | 165 + .../physx/source/physxextensions/src/ExtPvd.h | 191 + .../physxextensions/src/ExtPxStringTable.cpp | 99 + .../physxextensions/src/ExtRaycastCCD.cpp | 305 + .../physxextensions/src/ExtRevoluteJoint.cpp | 345 + .../physxextensions/src/ExtRevoluteJoint.h | 153 + .../physxextensions/src/ExtRigidActorExt.cpp | 71 + .../physxextensions/src/ExtRigidBodyExt.cpp | 648 ++ .../physxextensions/src/ExtSceneQueryExt.cpp | 188 + .../physxextensions/src/ExtSerialization.h | 44 + .../src/ExtSharedQueueEntryPool.h | 156 + .../physxextensions/src/ExtSimpleFactory.cpp | 384 + .../physxextensions/src/ExtSmoothNormals.cpp | 145 + .../physxextensions/src/ExtSphericalJoint.cpp | 241 + .../physxextensions/src/ExtSphericalJoint.h | 129 + .../physxextensions/src/ExtTaskQueueHelper.h | 66 + .../src/ExtTriangleMeshExt.cpp | 164 + .../Binary/SnBinaryDeserialization.cpp | 305 + .../Binary/SnBinarySerialization.cpp | 402 + .../src/serialization/Binary/SnConvX.cpp | 166 + .../src/serialization/Binary/SnConvX.h | 183 + .../serialization/Binary/SnConvX_Align.cpp | 63 + .../src/serialization/Binary/SnConvX_Align.h | 44 + .../src/serialization/Binary/SnConvX_Common.h | 43 + .../serialization/Binary/SnConvX_Convert.cpp | 1434 +++ .../serialization/Binary/SnConvX_Error.cpp | 92 + .../serialization/Binary/SnConvX_MetaData.cpp | 960 +++ .../serialization/Binary/SnConvX_MetaData.h | 188 + .../serialization/Binary/SnConvX_Output.cpp | 451 + .../src/serialization/Binary/SnConvX_Output.h | 112 + .../serialization/Binary/SnConvX_Union.cpp | 90 + .../src/serialization/Binary/SnConvX_Union.h | 45 + .../Binary/SnSerializationContext.cpp | 94 + .../Binary/SnSerializationContext.h | 303 + .../src/serialization/File/SnFile.h | 85 + .../src/serialization/SnSerialUtils.cpp | 159 + .../src/serialization/SnSerialUtils.h | 50 + .../src/serialization/SnSerialization.cpp | 425 + .../serialization/SnSerializationRegistry.cpp | 287 + .../serialization/SnSerializationRegistry.h | 91 + .../Xml/SnJointRepXSerializer.cpp | 145 + .../serialization/Xml/SnJointRepXSerializer.h | 73 + .../serialization/Xml/SnPxStreamOperators.h | 134 + .../src/serialization/Xml/SnRepX1_0Defaults.h | 245 + .../src/serialization/Xml/SnRepX3_1Defaults.h | 274 + .../src/serialization/Xml/SnRepX3_2Defaults.h | 313 + .../src/serialization/Xml/SnRepXCollection.h | 173 + .../Xml/SnRepXCoreSerializer.cpp | 553 ++ .../serialization/Xml/SnRepXCoreSerializer.h | 125 + .../serialization/Xml/SnRepXSerializerImpl.h | 90 + .../src/serialization/Xml/SnRepXUpgrader.cpp | 465 + .../src/serialization/Xml/SnRepXUpgrader.h | 54 + .../src/serialization/Xml/SnSimpleXmlWriter.h | 257 + .../src/serialization/Xml/SnXmlDeserializer.h | 197 + .../src/serialization/Xml/SnXmlImpl.h | 243 + .../serialization/Xml/SnXmlMemoryAllocator.h | 129 + .../src/serialization/Xml/SnXmlMemoryPool.h | 373 + .../Xml/SnXmlMemoryPoolStreams.h | 173 + .../src/serialization/Xml/SnXmlReader.h | 130 + .../serialization/Xml/SnXmlSerialization.cpp | 834 ++ .../src/serialization/Xml/SnXmlSerializer.h | 117 + .../serialization/Xml/SnXmlSimpleXmlWriter.h | 215 + .../src/serialization/Xml/SnXmlStringToType.h | 275 + .../serialization/Xml/SnXmlVisitorReader.h | 907 ++ .../serialization/Xml/SnXmlVisitorWriter.h | 801 ++ .../src/serialization/Xml/SnXmlWriter.h | 57 + .../source/physxgpu/include/PxPhysXGpu.h | 189 + .../include/PvdMetaDataDefineProperties.h | 352 + .../core/include/PvdMetaDataExtensions.h | 318 + .../core/include/PvdMetaDataPropertyVisitor.h | 539 ++ .../PxAutoGeneratedMetaDataObjectNames.h | 373 + .../include/PxAutoGeneratedMetaDataObjects.h | 2888 +++++++ .../core/include/PxMetaDataCompare.h | 388 + .../core/include/PxMetaDataCppPrefix.h | 37 + .../core/include/PxMetaDataObjects.h | 662 ++ .../include/RepXMetaDataPropertyVisitor.h | 213 + .../src/PxAutoGeneratedMetaDataObjects.cpp | 1236 +++ .../core/src/PxMetaDataObjects.cpp | 148 + ...xtensionAutoGeneratedMetaDataObjectNames.h | 162 + .../PxExtensionAutoGeneratedMetaDataObjects.h | 1229 +++ .../include/PxExtensionMetaDataObjects.h | 72 + ...xExtensionAutoGeneratedMetaDataObjects.cpp | 488 ++ .../physxvehicle/src/PxVehicleComponents.cpp | 222 + .../physxvehicle/src/PxVehicleDefaults.h | 46 + .../physxvehicle/src/PxVehicleDrive.cpp | 213 + .../physxvehicle/src/PxVehicleDrive4W.cpp | 152 + .../physxvehicle/src/PxVehicleDriveNW.cpp | 149 + .../physxvehicle/src/PxVehicleDriveTank.cpp | 120 + .../physxvehicle/src/PxVehicleLinearMath.h | 433 + .../physxvehicle/src/PxVehicleMetaData.cpp | 541 ++ .../physxvehicle/src/PxVehicleNoDrive.cpp | 157 + .../source/physxvehicle/src/PxVehicleSDK.cpp | 115 + .../src/PxVehicleSerialization.cpp | 203 + .../physxvehicle/src/PxVehicleSerialization.h | 70 + .../src/PxVehicleSuspLimitConstraintShader.h | 317 + .../src/PxVehicleSuspWheelTire4.cpp | 191 + .../src/PxVehicleSuspWheelTire4.h | 393 + .../src/PxVehicleTireFriction.cpp | 132 + .../physxvehicle/src/PxVehicleUpdate.cpp | 7652 +++++++++++++++++ .../physxvehicle/src/PxVehicleWheels.cpp | 857 ++ .../physxvehicle/src/VehicleUtilControl.cpp | 474 + .../physxvehicle/src/VehicleUtilSetup.cpp | 246 + .../physxvehicle/src/VehicleUtilTelemetry.cpp | 577 ++ ...xVehicleAutoGeneratedMetaDataObjectNames.h | 231 + .../PxVehicleAutoGeneratedMetaDataObjects.h | 1797 ++++ .../include/PxVehicleMetaDataObjects.h | 97 + .../PxVehicleAutoGeneratedMetaDataObjects.cpp | 727 ++ .../src/PxVehicleMetaDataObjects.cpp | 86 + src/PhysX/physx/source/pvd/include/PsPvd.h | 83 + .../pvd/include/PxProfileAllocatorWrapper.h | 231 + .../physx/source/pvd/include/PxPvdClient.h | 77 + .../source/pvd/include/PxPvdDataStream.h | 272 + .../pvd/include/PxPvdDataStreamHelpers.h | 120 + .../source/pvd/include/PxPvdErrorCodes.h | 62 + .../pvd/include/PxPvdObjectModelBaseTypes.h | 428 + .../source/pvd/include/PxPvdRenderBuffer.h | 140 + .../source/pvd/include/PxPvdUserRenderer.h | 107 + .../source/pvd/src/PxProfileContextProvider.h | 58 + .../pvd/src/PxProfileContextProviderImpl.h | 52 + .../source/pvd/src/PxProfileDataBuffer.h | 167 + .../source/pvd/src/PxProfileDataParsing.h | 218 + .../source/pvd/src/PxProfileEventBuffer.h | 267 + .../pvd/src/PxProfileEventBufferAtomic.h | 318 + .../pvd/src/PxProfileEventBufferClient.h | 80 + .../src/PxProfileEventBufferClientManager.h | 94 + .../physx/source/pvd/src/PxProfileEventId.h | 64 + .../source/pvd/src/PxProfileEventImpl.cpp | 63 + .../source/pvd/src/PxProfileEventMutex.h | 63 + .../source/pvd/src/PxProfileEventNames.h | 89 + .../source/pvd/src/PxProfileEventSender.h | 111 + .../pvd/src/PxProfileEventSerialization.h | 257 + .../physx/source/pvd/src/PxProfileEvents.h | 705 ++ .../physx/source/pvd/src/PxProfileMemory.h | 92 + .../source/pvd/src/PxProfileMemoryBuffer.h | 192 + .../pvd/src/PxProfileMemoryEventBuffer.h | 157 + .../source/pvd/src/PxProfileMemoryEvents.h | 411 + .../source/pvd/src/PxProfileScopedEvent.h | 107 + .../source/pvd/src/PxProfileScopedMutexLock.h | 64 + .../physx/source/pvd/src/PxProfileZoneImpl.h | 315 + .../source/pvd/src/PxProfileZoneManager.h | 155 + .../source/pvd/src/PxProfileZoneManagerImpl.h | 173 + src/PhysX/physx/source/pvd/src/PxPvd.cpp | 46 + src/PhysX/physx/source/pvd/src/PxPvdBits.h | 132 + .../physx/source/pvd/src/PxPvdByteStreams.h | 126 + .../source/pvd/src/PxPvdCommStreamEventSink.h | 55 + .../source/pvd/src/PxPvdCommStreamEvents.h | 987 +++ .../source/pvd/src/PxPvdCommStreamTypes.h | 229 + .../physx/source/pvd/src/PxPvdDataStream.cpp | 864 ++ .../pvd/src/PxPvdDefaultFileTransport.cpp | 218 + .../pvd/src/PxPvdDefaultFileTransport.h | 77 + .../pvd/src/PxPvdDefaultSocketTransport.cpp | 134 + .../pvd/src/PxPvdDefaultSocketTransport.h | 79 + .../physx/source/pvd/src/PxPvdFoundation.h | 318 + src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp | 399 + src/PhysX/physx/source/pvd/src/PxPvdImpl.h | 221 + .../source/pvd/src/PxPvdInternalByteStreams.h | 99 + .../physx/source/pvd/src/PxPvdMarshalling.h | 220 + .../physx/source/pvd/src/PxPvdMemClient.cpp | 130 + .../physx/source/pvd/src/PxPvdMemClient.h | 85 + .../src/PxPvdObjectModelInternalTypeDefs.h | 32 + .../pvd/src/PxPvdObjectModelInternalTypes.h | 154 + .../pvd/src/PxPvdObjectModelMetaData.cpp | 1503 ++++ .../source/pvd/src/PxPvdObjectModelMetaData.h | 481 ++ .../source/pvd/src/PxPvdObjectRegistrar.cpp | 80 + .../source/pvd/src/PxPvdObjectRegistrar.h | 71 + .../physx/source/pvd/src/PxPvdProfileZone.h | 142 + .../source/pvd/src/PxPvdProfileZoneClient.cpp | 171 + .../source/pvd/src/PxPvdProfileZoneClient.h | 77 + .../source/pvd/src/PxPvdUserRenderImpl.h | 385 + .../source/pvd/src/PxPvdUserRenderTypes.h | 46 + .../source/pvd/src/PxPvdUserRenderer.cpp | 405 + .../source/scenequery/include/SqPruner.h | 397 + .../scenequery/include/SqPrunerMergeData.h | 62 + .../scenequery/include/SqPruningStructure.h | 110 + .../scenequery/include/SqSceneQueryManager.h | 221 + .../source/scenequery/src/SqAABBPruner.cpp | 848 ++ .../source/scenequery/src/SqAABBPruner.h | 269 + .../source/scenequery/src/SqAABBTree.cpp | 920 ++ .../physx/source/scenequery/src/SqAABBTree.h | 259 + .../scenequery/src/SqAABBTreeUpdateMap.cpp | 197 + .../scenequery/src/SqAABBTreeUpdateMap.h | 82 + .../physx/source/scenequery/src/SqBounds.cpp | 75 + .../physx/source/scenequery/src/SqBounds.h | 78 + .../source/scenequery/src/SqBucketPruner.cpp | 2599 ++++++ .../source/scenequery/src/SqBucketPruner.h | 279 + .../scenequery/src/SqCompoundPruner.cpp | 598 ++ .../source/scenequery/src/SqCompoundPruner.h | 98 + .../scenequery/src/SqCompoundPruningPool.cpp | 279 + .../scenequery/src/SqCompoundPruningPool.h | 108 + .../scenequery/src/SqExtendedBucketPruner.cpp | 920 ++ .../scenequery/src/SqExtendedBucketPruner.h | 198 + .../src/SqIncrementalAABBPruner.cpp | 473 + .../scenequery/src/SqIncrementalAABBPruner.h | 99 + .../src/SqIncrementalAABBPrunerCore.cpp | 435 + .../src/SqIncrementalAABBPrunerCore.h | 115 + .../scenequery/src/SqIncrementalAABBTree.cpp | 1077 +++ .../scenequery/src/SqIncrementalAABBTree.h | 215 + .../source/scenequery/src/SqMetaData.cpp | 57 + .../source/scenequery/src/SqPruningPool.cpp | 182 + .../source/scenequery/src/SqPruningPool.h | 120 + .../scenequery/src/SqPruningStructure.cpp | 429 + .../scenequery/src/SqSceneQueryManager.cpp | 604 ++ .../physx/source/scenequery/src/SqTypedef.h | 46 + .../include/ScActorCore.h | 131 + .../include/ScArticulationCore.h | 227 + .../include/ScArticulationJointCore.h | 226 + .../simulationcontroller/include/ScBodyCore.h | 213 + .../include/ScConstraintCore.h | 134 + .../include/ScIterators.h | 104 + .../include/ScMaterialCore.h | 70 + .../simulationcontroller/include/ScPhysics.h | 115 + .../include/ScRigidCore.h | 94 + .../simulationcontroller/include/ScScene.h | 993 +++ .../include/ScShapeCore.h | 137 + .../include/ScStaticCore.h | 78 + .../simulationcontroller/src/ScActorCore.cpp | 87 + .../simulationcontroller/src/ScActorPair.h | 217 + .../simulationcontroller/src/ScActorSim.cpp | 144 + .../simulationcontroller/src/ScActorSim.h | 124 + .../src/ScArticulationCore.cpp | 384 + .../src/ScArticulationJointCore.cpp | 300 + .../src/ScArticulationJointSim.cpp | 92 + .../src/ScArticulationJointSim.h | 71 + .../src/ScArticulationSim.cpp | 813 ++ .../src/ScArticulationSim.h | 213 + .../simulationcontroller/src/ScBodyCore.cpp | 717 ++ .../simulationcontroller/src/ScBodySim.cpp | 973 +++ .../simulationcontroller/src/ScBodySim.h | 333 + .../simulationcontroller/src/ScClient.h | 53 + .../src/ScConstraintCore.cpp | 149 + .../src/ScConstraintGroupNode.cpp | 138 + .../src/ScConstraintGroupNode.h | 178 + .../src/ScConstraintInteraction.cpp | 169 + .../src/ScConstraintInteraction.h | 66 + .../src/ScConstraintProjectionManager.cpp | 505 ++ .../src/ScConstraintProjectionManager.h | 84 + .../src/ScConstraintProjectionTree.cpp | 567 ++ .../src/ScConstraintProjectionTree.h | 75 + .../src/ScConstraintSim.cpp | 490 ++ .../src/ScConstraintSim.h | 156 + .../src/ScContactReportBuffer.h | 174 + .../src/ScContactStream.h | 413 + .../src/ScElementInteractionMarker.cpp | 45 + .../src/ScElementInteractionMarker.h | 68 + .../simulationcontroller/src/ScElementSim.cpp | 133 + .../simulationcontroller/src/ScElementSim.h | 130 + .../src/ScElementSimInteraction.h | 77 + .../src/ScInteraction.cpp | 71 + .../simulationcontroller/src/ScInteraction.h | 210 + .../src/ScInteractionFlags.h | 74 + .../simulationcontroller/src/ScIterators.cpp | 105 + .../simulationcontroller/src/ScMetaData.cpp | 424 + .../simulationcontroller/src/ScNPhaseCore.cpp | 2054 +++++ .../simulationcontroller/src/ScNPhaseCore.h | 390 + .../src/ScObjectIDTracker.h | 99 + .../simulationcontroller/src/ScPhysics.cpp | 61 + .../simulationcontroller/src/ScRigidCore.cpp | 125 + .../simulationcontroller/src/ScRigidSim.cpp | 88 + .../simulationcontroller/src/ScRigidSim.h | 63 + .../simulationcontroller/src/ScScene.cpp | 6223 ++++++++++++++ .../simulationcontroller/src/ScShapeCore.cpp | 372 + .../src/ScShapeInteraction.cpp | 1208 +++ .../src/ScShapeInteraction.h | 412 + .../simulationcontroller/src/ScShapeSim.cpp | 494 ++ .../simulationcontroller/src/ScShapeSim.h | 151 + .../simulationcontroller/src/ScSimStateData.h | 139 + .../simulationcontroller/src/ScSimStats.cpp | 132 + .../simulationcontroller/src/ScSimStats.h | 89 + .../src/ScSimulationController.cpp | 43 + .../src/ScSimulationController.h | 92 + .../src/ScSqBoundsManager.cpp | 122 + .../src/ScSqBoundsManager.h | 73 + .../simulationcontroller/src/ScStaticCore.cpp | 49 + .../simulationcontroller/src/ScStaticSim.h | 57 + .../src/ScTriggerInteraction.cpp | 135 + .../src/ScTriggerInteraction.h | 122 + .../simulationcontroller/src/ScTriggerPairs.h | 95 + .../physx/source/task/src/TaskManager.cpp | 538 ++ .../binarymetadata/android/android.metaData | Bin 0 -> 44148 bytes .../tools/binarymetadata/ios/ios64.metaData | Bin 0 -> 52514 bytes .../binarymetadata/linux/linux64.metaData | Bin 0 -> 52514 bytes .../tools/binarymetadata/mac/mac64.metaData | Bin 0 -> 52514 bytes .../binarymetadata/windows/win32.metaData | Bin 0 -> 44148 bytes .../binarymetadata/windows/win64.metaData | Bin 0 -> 52514 bytes .../physxmetadatagenerator/PxBoilerPlate.h | 26 + .../PxExtensionsCommon.h | 80 + .../PxPhysicsWithExtensionsAPI.h | 186 + .../PxVehicleExtensionAPI.h | 91 + .../generateMetaData.bat | 132 + .../generateMetaData.py | 251 + .../generateMetaData.sh | 47 + .../tools/physxmetadatagenerator/helper.sh | 37 + .../physxmetadatagenerator/lib/__init__.py | 0 .../physxmetadatagenerator/lib/compare.py | 120 + .../tools/physxmetadatagenerator/lib/utils.py | 104 + .../tools/physxmetadatagenerator/readme.txt | 54 + src/PhysX/premake4.lua | 128 + src/PhysX/pxshared/include/foundation/Px.h | 92 + .../include/foundation/PxAllocatorCallback.h | 95 + .../pxshared/include/foundation/PxAssert.h | 95 + .../include/foundation/PxBitAndData.h | 87 + .../pxshared/include/foundation/PxBounds3.h | 480 ++ .../include/foundation/PxErrorCallback.h | 73 + .../pxshared/include/foundation/PxErrors.h | 93 + .../pxshared/include/foundation/PxFlags.h | 376 + src/PhysX/pxshared/include/foundation/PxIO.h | 138 + .../include/foundation/PxIntrinsics.h | 47 + .../pxshared/include/foundation/PxMat33.h | 396 + .../pxshared/include/foundation/PxMat44.h | 376 + .../pxshared/include/foundation/PxMath.h | 338 + .../pxshared/include/foundation/PxMathUtils.h | 73 + .../pxshared/include/foundation/PxMemory.h | 110 + .../pxshared/include/foundation/PxPlane.h | 145 + .../include/foundation/PxPreprocessor.h | 553 ++ .../pxshared/include/foundation/PxProfiler.h | 99 + .../pxshared/include/foundation/PxQuat.h | 403 + .../include/foundation/PxSimpleTypes.h | 112 + .../include/foundation/PxStrideIterator.h | 353 + .../pxshared/include/foundation/PxTransform.h | 215 + .../pxshared/include/foundation/PxUnionCast.h | 69 + .../pxshared/include/foundation/PxVec2.h | 347 + .../pxshared/include/foundation/PxVec3.h | 394 + .../pxshared/include/foundation/PxVec4.h | 376 + .../foundation/unix/PxUnixIntrinsics.h | 185 + .../foundation/windows/PxWindowsIntrinsics.h | 188 + src/PhysXCharacterAll.cpp | 10 + src/PhysXCommonAll.cpp | 8 + src/PhysXCookingAll.cpp | 18 + src/PhysXExtensionAll.cpp | 34 + src/PhysXFoundationAll.cpp | 7 + src/PhysXFoundationUnix.cpp | 10 + src/PhysXFoundationWindows.cpp | 11 + src/PhysXGeomUtilsAll.cpp | 68 + src/PhysXLowLevelAll.cpp | 48 + src/PhysXNpSrcAll.cpp | 34 + src/PhysXPvdAll.cpp | 0 src/PhysXSceneQueryAll.cpp | 15 + src/PhysXSimulationControllerAll.cpp | 31 + src/PhysXVehicleAll.cpp | 18 + 1363 files changed, 413562 insertions(+), 484 deletions(-) create mode 100644 src/PhysX/physx/include/PxActor.h create mode 100644 src/PhysX/physx/include/PxAggregate.h create mode 100644 src/PhysX/physx/include/PxArticulation.h create mode 100644 src/PhysX/physx/include/PxArticulationBase.h create mode 100644 src/PhysX/physx/include/PxArticulationJoint.h create mode 100644 src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h create mode 100644 src/PhysX/physx/include/PxArticulationLink.h create mode 100644 src/PhysX/physx/include/PxArticulationReducedCoordinate.h create mode 100644 src/PhysX/physx/include/PxBatchQuery.h create mode 100644 src/PhysX/physx/include/PxBatchQueryDesc.h create mode 100644 src/PhysX/physx/include/PxBroadPhase.h create mode 100644 src/PhysX/physx/include/PxClient.h create mode 100644 src/PhysX/physx/include/PxConstraint.h create mode 100644 src/PhysX/physx/include/PxConstraintDesc.h create mode 100644 src/PhysX/physx/include/PxContact.h create mode 100644 src/PhysX/physx/include/PxContactModifyCallback.h create mode 100644 src/PhysX/physx/include/PxDeletionListener.h create mode 100644 src/PhysX/physx/include/PxFiltering.h create mode 100644 src/PhysX/physx/include/PxForceMode.h create mode 100644 src/PhysX/physx/include/PxFoundation.h create mode 100644 src/PhysX/physx/include/PxImmediateMode.h create mode 100644 src/PhysX/physx/include/PxLockedData.h create mode 100644 src/PhysX/physx/include/PxMaterial.h create mode 100644 src/PhysX/physx/include/PxPhysXConfig.h create mode 100644 src/PhysX/physx/include/PxPhysics.h create mode 100644 src/PhysX/physx/include/PxPhysicsAPI.h create mode 100644 src/PhysX/physx/include/PxPhysicsSerialization.h create mode 100644 src/PhysX/physx/include/PxPhysicsVersion.h create mode 100644 src/PhysX/physx/include/PxPruningStructure.h create mode 100644 src/PhysX/physx/include/PxQueryFiltering.h create mode 100644 src/PhysX/physx/include/PxQueryReport.h create mode 100644 src/PhysX/physx/include/PxRigidActor.h create mode 100644 src/PhysX/physx/include/PxRigidBody.h create mode 100644 src/PhysX/physx/include/PxRigidDynamic.h create mode 100644 src/PhysX/physx/include/PxRigidStatic.h create mode 100644 src/PhysX/physx/include/PxScene.h create mode 100644 src/PhysX/physx/include/PxSceneDesc.h create mode 100644 src/PhysX/physx/include/PxSceneLock.h create mode 100644 src/PhysX/physx/include/PxShape.h create mode 100644 src/PhysX/physx/include/PxSimulationEventCallback.h create mode 100644 src/PhysX/physx/include/PxSimulationStatistics.h create mode 100644 src/PhysX/physx/include/PxVisualizationParameter.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxBoxController.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxCapsuleController.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxCharacter.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxController.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxControllerManager.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxExtended.h create mode 100644 src/PhysX/physx/include/collision/PxCollisionDefs.h create mode 100644 src/PhysX/physx/include/common/PxBase.h create mode 100644 src/PhysX/physx/include/common/PxCollection.h create mode 100644 src/PhysX/physx/include/common/PxCoreUtilityTypes.h create mode 100644 src/PhysX/physx/include/common/PxMetaData.h create mode 100644 src/PhysX/physx/include/common/PxMetaDataFlags.h create mode 100644 src/PhysX/physx/include/common/PxPhysXCommonConfig.h create mode 100644 src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h create mode 100644 src/PhysX/physx/include/common/PxProfileZone.h create mode 100644 src/PhysX/physx/include/common/PxRenderBuffer.h create mode 100644 src/PhysX/physx/include/common/PxSerialFramework.h create mode 100644 src/PhysX/physx/include/common/PxSerializer.h create mode 100644 src/PhysX/physx/include/common/PxStringTable.h create mode 100644 src/PhysX/physx/include/common/PxTolerancesScale.h create mode 100644 src/PhysX/physx/include/common/PxTypeInfo.h create mode 100644 src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h create mode 100644 src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h create mode 100644 src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h create mode 100644 src/PhysX/physx/include/cooking/PxBVHStructureDesc.h create mode 100644 src/PhysX/physx/include/cooking/PxConvexMeshDesc.h create mode 100644 src/PhysX/physx/include/cooking/PxCooking.h create mode 100644 src/PhysX/physx/include/cooking/PxMidphaseDesc.h create mode 100644 src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h create mode 100644 src/PhysX/physx/include/cooking/Pxc.h create mode 100644 src/PhysX/physx/include/cudamanager/PxCudaContextManager.h create mode 100644 src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h create mode 100644 src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h create mode 100644 src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h create mode 100644 src/PhysX/physx/include/extensions/PxBinaryConverter.h create mode 100644 src/PhysX/physx/include/extensions/PxBroadPhaseExt.h create mode 100644 src/PhysX/physx/include/extensions/PxCollectionExt.h create mode 100644 src/PhysX/physx/include/extensions/PxConstraintExt.h create mode 100644 src/PhysX/physx/include/extensions/PxContactJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxConvexMeshExt.h create mode 100644 src/PhysX/physx/include/extensions/PxD6Joint.h create mode 100644 src/PhysX/physx/include/extensions/PxD6JointCreate.h create mode 100644 src/PhysX/physx/include/extensions/PxDefaultAllocator.h create mode 100644 src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h create mode 100644 src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h create mode 100644 src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h create mode 100644 src/PhysX/physx/include/extensions/PxDefaultStreams.h create mode 100644 src/PhysX/physx/include/extensions/PxDistanceJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxExtensionsAPI.h create mode 100644 src/PhysX/physx/include/extensions/PxFixedJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxJointLimit.h create mode 100644 src/PhysX/physx/include/extensions/PxMassProperties.h create mode 100644 src/PhysX/physx/include/extensions/PxPrismaticJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxRaycastCCD.h create mode 100644 src/PhysX/physx/include/extensions/PxRepXSerializer.h create mode 100644 src/PhysX/physx/include/extensions/PxRepXSimpleType.h create mode 100644 src/PhysX/physx/include/extensions/PxRevoluteJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxRigidActorExt.h create mode 100644 src/PhysX/physx/include/extensions/PxRigidBodyExt.h create mode 100644 src/PhysX/physx/include/extensions/PxSceneQueryExt.h create mode 100644 src/PhysX/physx/include/extensions/PxSerialization.h create mode 100644 src/PhysX/physx/include/extensions/PxShapeExt.h create mode 100644 src/PhysX/physx/include/extensions/PxSimpleFactory.h create mode 100644 src/PhysX/physx/include/extensions/PxSmoothNormals.h create mode 100644 src/PhysX/physx/include/extensions/PxSphericalJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxStringTableExt.h create mode 100644 src/PhysX/physx/include/extensions/PxTriangleMeshExt.h create mode 100644 src/PhysX/physx/include/filebuf/PxFileBuf.h create mode 100644 src/PhysX/physx/include/geometry/PxBVHStructure.h create mode 100644 src/PhysX/physx/include/geometry/PxBoxGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxCapsuleGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxConvexMesh.h create mode 100644 src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxGeometryHelpers.h create mode 100644 src/PhysX/physx/include/geometry/PxGeometryQuery.h create mode 100644 src/PhysX/physx/include/geometry/PxHeightField.h create mode 100644 src/PhysX/physx/include/geometry/PxHeightFieldDesc.h create mode 100644 src/PhysX/physx/include/geometry/PxHeightFieldFlag.h create mode 100644 src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxHeightFieldSample.h create mode 100644 src/PhysX/physx/include/geometry/PxMeshQuery.h create mode 100644 src/PhysX/physx/include/geometry/PxMeshScale.h create mode 100644 src/PhysX/physx/include/geometry/PxPlaneGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h create mode 100644 src/PhysX/physx/include/geometry/PxSphereGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxTriangle.h create mode 100644 src/PhysX/physx/include/geometry/PxTriangleMesh.h create mode 100644 src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h create mode 100644 src/PhysX/physx/include/geomutils/GuContactBuffer.h create mode 100644 src/PhysX/physx/include/geomutils/GuContactPoint.h create mode 100644 src/PhysX/physx/include/gpu/PxGpu.h create mode 100644 src/PhysX/physx/include/pvd/PxPvd.h create mode 100644 src/PhysX/physx/include/pvd/PxPvdSceneClient.h create mode 100644 src/PhysX/physx/include/pvd/PxPvdTransport.h create mode 100644 src/PhysX/physx/include/solver/PxSolverDefs.h create mode 100644 src/PhysX/physx/include/task/PxCpuDispatcher.h create mode 100644 src/PhysX/physx/include/task/PxGpuDispatcher.h create mode 100644 src/PhysX/physx/include/task/PxGpuTask.h create mode 100644 src/PhysX/physx/include/task/PxTask.h create mode 100644 src/PhysX/physx/include/task/PxTaskDefine.h create mode 100644 src/PhysX/physx/include/task/PxTaskManager.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleComponents.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleDrive.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleSDK.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleShaders.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleUpdate.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleUtil.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleUtilSetup.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleUtilTelemetry.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleWheels.h create mode 100644 src/PhysX/physx/platform_readme.html create mode 100644 src/PhysX/physx/release_notes.html create mode 100644 src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h create mode 100644 src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h create mode 100644 src/PhysX/physx/source/common/src/CmBitMap.h create mode 100644 src/PhysX/physx/source/common/src/CmBlockArray.h create mode 100644 src/PhysX/physx/source/common/src/CmBoxPruning.cpp create mode 100644 src/PhysX/physx/source/common/src/CmBoxPruning.h create mode 100644 src/PhysX/physx/source/common/src/CmCollection.cpp create mode 100644 src/PhysX/physx/source/common/src/CmCollection.h create mode 100644 src/PhysX/physx/source/common/src/CmConeLimitHelper.h create mode 100644 src/PhysX/physx/source/common/src/CmFlushPool.h create mode 100644 src/PhysX/physx/source/common/src/CmIDPool.h create mode 100644 src/PhysX/physx/source/common/src/CmIO.h create mode 100644 src/PhysX/physx/source/common/src/CmMathUtils.cpp create mode 100644 src/PhysX/physx/source/common/src/CmMatrix34.h create mode 100644 src/PhysX/physx/source/common/src/CmPhysXCommon.h create mode 100644 src/PhysX/physx/source/common/src/CmPool.h create mode 100644 src/PhysX/physx/source/common/src/CmPreallocatingPool.h create mode 100644 src/PhysX/physx/source/common/src/CmPriorityQueue.h create mode 100644 src/PhysX/physx/source/common/src/CmPtrTable.cpp create mode 100644 src/PhysX/physx/source/common/src/CmPtrTable.h create mode 100644 src/PhysX/physx/source/common/src/CmQueue.h create mode 100644 src/PhysX/physx/source/common/src/CmRadixSort.cpp create mode 100644 src/PhysX/physx/source/common/src/CmRadixSort.h create mode 100644 src/PhysX/physx/source/common/src/CmRadixSortBuffered.cpp create mode 100644 src/PhysX/physx/source/common/src/CmRadixSortBuffered.h create mode 100644 src/PhysX/physx/source/common/src/CmRefCountable.h create mode 100644 src/PhysX/physx/source/common/src/CmRenderBuffer.h create mode 100644 src/PhysX/physx/source/common/src/CmRenderOutput.cpp create mode 100644 src/PhysX/physx/source/common/src/CmRenderOutput.h create mode 100644 src/PhysX/physx/source/common/src/CmScaling.h create mode 100644 src/PhysX/physx/source/common/src/CmSpatialVector.h create mode 100644 src/PhysX/physx/source/common/src/CmTask.h create mode 100644 src/PhysX/physx/source/common/src/CmTaskPool.h create mode 100644 src/PhysX/physx/source/common/src/CmTmpMem.h create mode 100644 src/PhysX/physx/source/common/src/CmTransformUtils.h create mode 100644 src/PhysX/physx/source/common/src/CmUtils.h create mode 100644 src/PhysX/physx/source/common/src/CmVisualization.cpp create mode 100644 src/PhysX/physx/source/common/src/CmVisualization.h create mode 100644 src/PhysX/physx/source/common/src/windows/CmWindowsDelayLoadHook.cpp create mode 100644 src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp create mode 100644 src/PhysX/physx/source/compiler/cmake/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/IfTemplate.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/resource_x64/PhysX.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x64/PhysXFoundation.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x64/resource.h create mode 100644 src/PhysX/physx/source/compiler/resource_x86/PhysX.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x86/PhysXCommon.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x86/PhysXCooking.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x86/PhysXFoundation.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x86/resource.h create mode 100644 src/PhysX/physx/source/fastxml/include/PsFastXml.h create mode 100644 src/PhysX/physx/source/fastxml/src/PsFastXml.cpp create mode 100644 src/PhysX/physx/source/filebuf/include/PsAsciiConversion.h create mode 100644 src/PhysX/physx/source/filebuf/include/PsAsciiConversion.inl create mode 100644 src/PhysX/physx/source/filebuf/include/PsFileBuffer.h create mode 100644 src/PhysX/physx/source/filebuf/include/PsIOStream.h create mode 100644 src/PhysX/physx/source/filebuf/include/PsIOStream.inl create mode 100644 src/PhysX/physx/source/filebuf/include/PsMemoryBuffer.h create mode 100644 src/PhysX/physx/source/foundation/include/Ps.h create mode 100644 src/PhysX/physx/source/foundation/include/PsAlignedMalloc.h create mode 100644 src/PhysX/physx/source/foundation/include/PsAlloca.h create mode 100644 src/PhysX/physx/source/foundation/include/PsAllocator.h create mode 100644 src/PhysX/physx/source/foundation/include/PsAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/PsArray.h create mode 100644 src/PhysX/physx/source/foundation/include/PsAtomic.h create mode 100644 src/PhysX/physx/source/foundation/include/PsBasicTemplates.h create mode 100644 src/PhysX/physx/source/foundation/include/PsBitUtils.h create mode 100644 src/PhysX/physx/source/foundation/include/PsBroadcast.h create mode 100644 src/PhysX/physx/source/foundation/include/PsCpu.h create mode 100644 src/PhysX/physx/source/foundation/include/PsFPU.h create mode 100644 src/PhysX/physx/source/foundation/include/PsFoundation.h create mode 100644 src/PhysX/physx/source/foundation/include/PsHash.h create mode 100644 src/PhysX/physx/source/foundation/include/PsHashInternals.h create mode 100644 src/PhysX/physx/source/foundation/include/PsHashMap.h create mode 100644 src/PhysX/physx/source/foundation/include/PsHashSet.h create mode 100644 src/PhysX/physx/source/foundation/include/PsInlineAllocator.h create mode 100644 src/PhysX/physx/source/foundation/include/PsInlineAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/PsInlineArray.h create mode 100644 src/PhysX/physx/source/foundation/include/PsIntrinsics.h create mode 100644 src/PhysX/physx/source/foundation/include/PsMathUtils.h create mode 100644 src/PhysX/physx/source/foundation/include/PsMutex.h create mode 100644 src/PhysX/physx/source/foundation/include/PsPool.h create mode 100644 src/PhysX/physx/source/foundation/include/PsSList.h create mode 100644 src/PhysX/physx/source/foundation/include/PsSocket.h create mode 100644 src/PhysX/physx/source/foundation/include/PsSort.h create mode 100644 src/PhysX/physx/source/foundation/include/PsSortInternals.h create mode 100644 src/PhysX/physx/source/foundation/include/PsString.h create mode 100644 src/PhysX/physx/source/foundation/include/PsSync.h create mode 100644 src/PhysX/physx/source/foundation/include/PsTempAllocator.h create mode 100644 src/PhysX/physx/source/foundation/include/PsThread.h create mode 100644 src/PhysX/physx/source/foundation/include/PsTime.h create mode 100644 src/PhysX/physx/source/foundation/include/PsUserAllocated.h create mode 100644 src/PhysX/physx/source/foundation/include/PsUtilities.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecMath.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecMathSSE.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecQuat.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecTransform.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h create mode 100644 src/PhysX/physx/source/foundation/src/PsAllocator.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsAssert.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsFoundation.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsMathUtils.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsString.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsUtilities.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp create mode 100644 src/PhysX/physx/source/geomutils/include/GuAxes.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuBox.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuRaycastTests.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuSegment.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuBVHStructure.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuBounds.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuBounds.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuCapsule.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuCenterExtents.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuGeometryQuery.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuInternal.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuInternal.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuMTD.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuMTD.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuMeshFactory.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuMetaData.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuOverlapTests.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuSerialize.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuSerialize.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuSphere.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepMTD.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepTests.h create mode 100644 src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h create mode 100644 src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.h create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactBoxBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecCapsule.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHullNoScale.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuEntityReport.h create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBoxSIMD.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Raycast.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h create mode 100644 src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvManager.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h create mode 100644 src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcCCDStateStreamPair.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h create mode 100644 src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactMethodImpl.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialHeightField.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMesh.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsContext.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsCCD.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/include/BpAABBManagerTasks.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhase.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyContext.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetupBlock.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverContact.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverContact4.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF4.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverContext.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpActor.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpActor.h create mode 100644 src/PhysX/physx/source/physx/src/NpActorTemplate.h create mode 100644 src/PhysX/physx/source/physx/src/NpAggregate.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpAggregate.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulation.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpArticulation.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationJoint.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationLink.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationLink.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationTemplate.h create mode 100644 src/PhysX/physx/source/physx/src/NpBatchQuery.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpBatchQuery.h create mode 100644 src/PhysX/physx/source/physx/src/NpCast.h create mode 100644 src/PhysX/physx/source/physx/src/NpConnector.h create mode 100644 src/PhysX/physx/source/physx/src/NpConstraint.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpConstraint.h create mode 100644 src/PhysX/physx/source/physx/src/NpFactory.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpFactory.h create mode 100644 src/PhysX/physx/source/physx/src/NpMaterial.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpMaterial.h create mode 100644 src/PhysX/physx/source/physx/src/NpMaterialManager.h create mode 100644 src/PhysX/physx/source/physx/src/NpMetaData.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpPhysics.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpPhysics.h create mode 100644 src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h create mode 100644 src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h create mode 100644 src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h create mode 100644 src/PhysX/physx/source/physx/src/NpQueryShared.h create mode 100644 src/PhysX/physx/source/physx/src/NpReadCheck.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpReadCheck.h create mode 100644 src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h create mode 100644 src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h create mode 100644 src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h create mode 100644 src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpRigidDynamic.h create mode 100644 src/PhysX/physx/source/physx/src/NpRigidStatic.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpRigidStatic.h create mode 100644 src/PhysX/physx/source/physx/src/NpScene.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpScene.h create mode 100644 src/PhysX/physx/source/physx/src/NpSceneAccessor.h create mode 100644 src/PhysX/physx/source/physx/src/NpSceneQueries.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpSceneQueries.h create mode 100644 src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpShape.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpShape.h create mode 100644 src/PhysX/physx/source/physx/src/NpShapeManager.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpShapeManager.h create mode 100644 src/PhysX/physx/source/physx/src/NpWriteCheck.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpWriteCheck.h create mode 100644 src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h create mode 100644 src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp create mode 100644 src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h create mode 100644 src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp create mode 100644 src/PhysX/physx/source/physx/src/PvdPhysicsClient.h create mode 100644 src/PhysX/physx/source/physx/src/PvdTypeNames.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbActor.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbBase.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbBody.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbDefs.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbScene.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbShape.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbType.h create mode 100644 src/PhysX/physx/source/physx/src/device/PhysXIndicator.h create mode 100644 src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp create mode 100644 src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h create mode 100644 src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp create mode 100644 src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp create mode 100644 src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp create mode 100644 src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h create mode 100644 src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/Adjacencies.h create mode 100644 src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/Cooking.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/Cooking.h create mode 100644 src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/CookingUtils.h create mode 100644 src/PhysX/physx/source/physxcooking/src/EdgeList.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/EdgeList.h create mode 100644 src/PhysX/physx/source/physxcooking/src/MeshCleaner.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/MeshCleaner.h create mode 100644 src/PhysX/physx/source/physxcooking/src/Quantizer.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/Quantizer.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtCollection.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPlatform.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPvd.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSerialization.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Common.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Convert.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/SnSerialization.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnSimpleXmlWriter.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h create mode 100644 src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp create mode 100644 src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp create mode 100644 src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h create mode 100644 src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive4W.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleUpdate.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjectNames.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp create mode 100644 src/PhysX/physx/source/pvd/include/PsPvd.h create mode 100644 src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdClient.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdDataStream.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventId.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventNames.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventSender.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEvents.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileMemory.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvd.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdBits.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdFoundation.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdImpl.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdMemClient.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp create mode 100644 src/PhysX/physx/source/scenequery/include/SqPruner.h create mode 100644 src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h create mode 100644 src/PhysX/physx/source/scenequery/include/SqPruningStructure.h create mode 100644 src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBPruner.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBTree.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqBounds.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqBounds.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqBucketPruner.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqMetaData.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqPruningPool.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqPruningStructure.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqTypedef.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScIterators.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScScene.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScClient.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h create mode 100644 src/PhysX/physx/source/task/src/TaskManager.cpp create mode 100644 src/PhysX/physx/tools/binarymetadata/android/android.metaData create mode 100644 src/PhysX/physx/tools/binarymetadata/ios/ios64.metaData create mode 100644 src/PhysX/physx/tools/binarymetadata/linux/linux64.metaData create mode 100644 src/PhysX/physx/tools/binarymetadata/mac/mac64.metaData create mode 100644 src/PhysX/physx/tools/binarymetadata/windows/win32.metaData create mode 100644 src/PhysX/physx/tools/binarymetadata/windows/win64.metaData create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/PxBoilerPlate.h create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/PxExtensionsCommon.h create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/helper.sh create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/lib/__init__.py create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/readme.txt create mode 100644 src/PhysX/premake4.lua create mode 100644 src/PhysX/pxshared/include/foundation/Px.h create mode 100644 src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h create mode 100644 src/PhysX/pxshared/include/foundation/PxAssert.h create mode 100644 src/PhysX/pxshared/include/foundation/PxBitAndData.h create mode 100644 src/PhysX/pxshared/include/foundation/PxBounds3.h create mode 100644 src/PhysX/pxshared/include/foundation/PxErrorCallback.h create mode 100644 src/PhysX/pxshared/include/foundation/PxErrors.h create mode 100644 src/PhysX/pxshared/include/foundation/PxFlags.h create mode 100644 src/PhysX/pxshared/include/foundation/PxIO.h create mode 100644 src/PhysX/pxshared/include/foundation/PxIntrinsics.h create mode 100644 src/PhysX/pxshared/include/foundation/PxMat33.h create mode 100644 src/PhysX/pxshared/include/foundation/PxMat44.h create mode 100644 src/PhysX/pxshared/include/foundation/PxMath.h create mode 100644 src/PhysX/pxshared/include/foundation/PxMathUtils.h create mode 100644 src/PhysX/pxshared/include/foundation/PxMemory.h create mode 100644 src/PhysX/pxshared/include/foundation/PxPlane.h create mode 100644 src/PhysX/pxshared/include/foundation/PxPreprocessor.h create mode 100644 src/PhysX/pxshared/include/foundation/PxProfiler.h create mode 100644 src/PhysX/pxshared/include/foundation/PxQuat.h create mode 100644 src/PhysX/pxshared/include/foundation/PxSimpleTypes.h create mode 100644 src/PhysX/pxshared/include/foundation/PxStrideIterator.h create mode 100644 src/PhysX/pxshared/include/foundation/PxTransform.h create mode 100644 src/PhysX/pxshared/include/foundation/PxUnionCast.h create mode 100644 src/PhysX/pxshared/include/foundation/PxVec2.h create mode 100644 src/PhysX/pxshared/include/foundation/PxVec3.h create mode 100644 src/PhysX/pxshared/include/foundation/PxVec4.h create mode 100644 src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h create mode 100644 src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h create mode 100644 src/PhysXCharacterAll.cpp create mode 100644 src/PhysXCommonAll.cpp create mode 100644 src/PhysXCookingAll.cpp create mode 100644 src/PhysXExtensionAll.cpp create mode 100644 src/PhysXFoundationAll.cpp create mode 100644 src/PhysXFoundationUnix.cpp create mode 100644 src/PhysXFoundationWindows.cpp create mode 100644 src/PhysXGeomUtilsAll.cpp create mode 100644 src/PhysXLowLevelAll.cpp create mode 100644 src/PhysXNpSrcAll.cpp create mode 100644 src/PhysXPvdAll.cpp create mode 100644 src/PhysXSceneQueryAll.cpp create mode 100644 src/PhysXSimulationControllerAll.cpp create mode 100644 src/PhysXVehicleAll.cpp diff --git a/setup.py b/setup.py index 760d3c452..fe39a57a3 100644 --- a/setup.py +++ b/setup.py @@ -1,484 +1,676 @@ - -from setuptools import find_packages -from sys import platform as _platform -import sys -import glob -import os - - -from distutils.core import setup -from distutils.extension import Extension -from distutils.util import get_platform -from glob import glob - -# monkey-patch for parallel compilation -import multiprocessing -import multiprocessing.pool - - -def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): - # those lines are copied from distutils.ccompiler.CCompiler directly - macros, objects, extra_postargs, pp_opts, build = self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) - cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - # parallel code - N = 2*multiprocessing.cpu_count()# number of parallel compilations - def _single_compile(obj): - try: src, ext = build[obj] - except KeyError: return - newcc_args = cc_args - if _platform == "darwin": - if src.endswith('.cpp'): - newcc_args = cc_args + ["-stdlib=libc++"] - self._compile(obj, src, ext, newcc_args, extra_postargs, pp_opts) - # convert to list, imap is evaluated on-demand - pool = multiprocessing.pool.ThreadPool(N) - list(pool.imap(_single_compile,objects)) - return objects -import distutils.ccompiler -distutils.ccompiler.CCompiler.compile=parallelCCompile - -#see http://stackoverflow.com/a/8719066/295157 -import os - - -platform = get_platform() -print(platform) - -CXX_FLAGS = '' -CXX_FLAGS += '-DGWEN_COMPILE_STATIC ' -CXX_FLAGS += '-DBT_USE_DOUBLE_PRECISION ' -CXX_FLAGS += '-DBT_ENABLE_ENET ' -CXX_FLAGS += '-DBT_ENABLE_CLSOCKET ' -CXX_FLAGS += '-DB3_DUMP_PYTHON_VERSION ' -CXX_FLAGS += '-DEGL_ADD_PYTHON_INIT ' -CXX_FLAGS += '-DB3_ENABLE_FILEIO_PLUGIN ' -CXX_FLAGS += '-DB3_USE_ZIPFILE_FILEIO ' -CXX_FLAGS += '-DBT_THREADSAFE=1 ' -CXX_FLAGS += '-DSTATIC_LINK_SPD_PLUGIN ' - - -EGL_CXX_FLAGS = '' - - - -# libraries += [current_python] - -libraries = [] -include_dirs = [] - -try: - import numpy - NP_DIRS = [numpy.get_include()] -except: - print("numpy is disabled. getCameraImage maybe slower.") -else: - print("numpy is enabled.") - CXX_FLAGS += '-DPYBULLET_USE_NUMPY ' - for d in NP_DIRS: - print("numpy_include_dirs = %s" % d) - include_dirs += NP_DIRS - -sources = ["examples/pybullet/pybullet.c"]\ -+["src/btLinearMathAll.cpp"]\ -+["src/btBulletCollisionAll.cpp"]\ -+["src/btBulletDynamicsAll.cpp"]\ -+["examples/ExampleBrowser/InProcessExampleBrowser.cpp"]\ -+["examples/TinyRenderer/geometry.cpp"]\ -+["examples/TinyRenderer/model.cpp"]\ -+["examples/TinyRenderer/tgaimage.cpp"]\ -+["examples/TinyRenderer/our_gl.cpp"]\ -+["examples/TinyRenderer/TinyRenderer.cpp"]\ -+["examples/SharedMemory/plugins/pdControlPlugin/pdControlPlugin.cpp"]\ -+["examples/SharedMemory/plugins/collisionFilterPlugin/collisionFilterPlugin.cpp"]\ -+["examples/SharedMemory/plugins/fileIOPlugin/fileIOPlugin.cpp"]\ -+["examples/SharedMemory/b3RobotSimulatorClientAPI_NoDirect.cpp"]\ -+["examples/SharedMemory/IKTrajectoryHelper.cpp"]\ -+["examples/SharedMemory/InProcessMemory.cpp"]\ -+["examples/SharedMemory/PhysicsClient.cpp"]\ -+["examples/SharedMemory/PhysicsServer.cpp"]\ -+["examples/SharedMemory/PhysicsServerExample.cpp"]\ -+["examples/SharedMemory/PhysicsServerExampleBullet2.cpp"]\ -+["examples/SharedMemory/SharedMemoryInProcessPhysicsC_API.cpp"]\ -+["examples/SharedMemory/PhysicsServerSharedMemory.cpp"]\ -+["examples/SharedMemory/PhysicsDirect.cpp"]\ -+["examples/SharedMemory/PhysicsDirectC_API.cpp"]\ -+["examples/SharedMemory/PhysicsServerCommandProcessor.cpp"]\ -+["examples/SharedMemory/PhysicsClientSharedMemory.cpp"]\ -+["examples/SharedMemory/PhysicsClientSharedMemory_C_API.cpp"]\ -+["examples/SharedMemory/PhysicsClientC_API.cpp"]\ -+["examples/SharedMemory/Win32SharedMemory.cpp"]\ -+["examples/SharedMemory/PosixSharedMemory.cpp"]\ -+["examples/SharedMemory/plugins/tinyRendererPlugin/TinyRendererVisualShapeConverter.cpp"]\ -+["examples/SharedMemory/plugins/tinyRendererPlugin/tinyRendererPlugin.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/BulletConversion.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/KinTree.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/MathUtil.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/RBDModel.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/RBDUtil.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/Shape.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/SpAlg.cpp"]\ -+["examples/SharedMemory/PhysicsClientUDP.cpp"]\ -+["examples/SharedMemory/PhysicsClientUDP_C_API.cpp"]\ -+["examples/SharedMemory/PhysicsClientTCP.cpp"]\ -+["examples/SharedMemory/PhysicsClientTCP_C_API.cpp"]\ -+["examples/SharedMemory/b3PluginManager.cpp"]\ -+["examples/Utils/b3ResourcePath.cpp"]\ -+["examples/Utils/RobotLoggingUtil.cpp"]\ -+["examples/Utils/ChromeTraceUtil.cpp"]\ -+["examples/Utils/b3Clock.cpp"]\ -+["examples/Utils/b3Quickprof.cpp"]\ -+["examples/ThirdPartyLibs/tinyxml2/tinyxml2.cpp"]\ -+["examples/ThirdPartyLibs/Wavefront/tiny_obj_loader.cpp"]\ -+["examples/ThirdPartyLibs/stb_image/stb_image.cpp"]\ -+["examples/ThirdPartyLibs/stb_image/stb_image_write.cpp"]\ -+["examples/ThirdPartyLibs/minizip/ioapi.c"]\ -+["examples/ThirdPartyLibs/minizip/unzip.c"]\ -+["examples/ThirdPartyLibs/minizip/zip.c"]\ -+["examples/ThirdPartyLibs/zlib/adler32.c"]\ -+["examples/ThirdPartyLibs/zlib/compress.c"]\ -+["examples/ThirdPartyLibs/zlib/crc32.c"]\ -+["examples/ThirdPartyLibs/zlib/deflate.c"]\ -+["examples/ThirdPartyLibs/zlib/gzclose.c"]\ -+["examples/ThirdPartyLibs/zlib/gzlib.c"]\ -+["examples/ThirdPartyLibs/zlib/gzread.c"]\ -+["examples/ThirdPartyLibs/zlib/gzwrite.c"]\ -+["examples/ThirdPartyLibs/zlib/infback.c"]\ -+["examples/ThirdPartyLibs/zlib/inffast.c"]\ -+["examples/ThirdPartyLibs/zlib/inflate.c"]\ -+["examples/ThirdPartyLibs/zlib/inftrees.c"]\ -+["examples/ThirdPartyLibs/zlib/trees.c"]\ -+["examples/ThirdPartyLibs/zlib/uncompr.c"]\ -+["examples/ThirdPartyLibs/zlib/zutil.c"]\ -+["examples/Importers/ImportColladaDemo/LoadMeshFromCollada.cpp"]\ -+["examples/Importers/ImportObjDemo/LoadMeshFromObj.cpp"]\ -+["examples/Importers/ImportObjDemo/Wavefront2GLInstanceGraphicsShape.cpp"]\ -+["examples/Importers/ImportMJCFDemo/BulletMJCFImporter.cpp"]\ -+["examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp"]\ -+["examples/Importers/ImportURDFDemo/MyMultiBodyCreator.cpp"]\ -+["examples/Importers/ImportURDFDemo/URDF2Bullet.cpp"]\ -+["examples/Importers/ImportURDFDemo/UrdfParser.cpp"]\ -+["examples/Importers/ImportURDFDemo/urdfStringSplit.cpp"]\ -+["examples/Importers/ImportMeshUtility/b3ImportMeshUtility.cpp"]\ -+["examples/MultiThreading/b3PosixThreadSupport.cpp"]\ -+["examples/MultiThreading/b3Win32ThreadSupport.cpp"]\ -+["examples/MultiThreading/b3ThreadSupportInterface.cpp"]\ -+["examples/ThirdPartyLibs/enet/callbacks.c"]\ -+["examples/ThirdPartyLibs/enet/compress.c"]\ -+["examples/ThirdPartyLibs/enet/host.c"]\ -+["examples/ThirdPartyLibs/enet/list.c"]\ -+["examples/ThirdPartyLibs/enet/packet.c"]\ -+["examples/ThirdPartyLibs/enet/peer.c"]\ -+["examples/ThirdPartyLibs/enet/protocol.c"]\ -+["examples/ExampleBrowser/OpenGLGuiHelper.cpp"]\ -+["examples/ExampleBrowser/OpenGLExampleBrowser.cpp"]\ -+["examples/ExampleBrowser/CollisionShape2TriangleMesh.cpp"]\ -+["examples/ExampleBrowser/GL_ShapeDrawer.cpp"]\ -+["examples/OpenGLWindow/SimpleOpenGL2Renderer.cpp"]\ -+["examples/OpenGLWindow/GLInstancingRenderer.cpp"]\ -+["examples/OpenGLWindow/SimpleOpenGL3App.cpp"]\ -+["examples/OpenGLWindow/GLPrimitiveRenderer.cpp"]\ -+["examples/OpenGLWindow/TwFonts.cpp"]\ -+["examples/OpenGLWindow/GLRenderToTexture.cpp"]\ -+["examples/OpenGLWindow/LoadShader.cpp"]\ -+["examples/OpenGLWindow/OpenSans.cpp"]\ -+["examples/OpenGLWindow/SimpleCamera.cpp"]\ -+["examples/OpenGLWindow/fontstash.cpp"]\ -+["examples/OpenGLWindow/SimpleOpenGL2App.cpp"]\ -+["examples/OpenGLWindow/opengl_fontstashcallbacks.cpp"]\ -+["examples/ExampleBrowser/GwenGUISupport/GraphingTexture.cpp"]\ -+["examples/ExampleBrowser/GwenGUISupport/GwenProfileWindow.cpp"]\ -+["examples/ExampleBrowser/GwenGUISupport/gwenUserInterface.cpp"]\ -+["examples/ExampleBrowser/GwenGUISupport/GwenParameterInterface.cpp"]\ -+["examples/ExampleBrowser/GwenGUISupport/GwenTextureWindow.cpp"]\ -+["src/Bullet3Common/b3AlignedAllocator.cpp"]\ -+["src/Bullet3Common/b3Logging.cpp"]\ -+["src/Bullet3Common/b3Vector3.cpp"]\ -+["examples/ThirdPartyLibs/clsocket/src/ActiveSocket.cpp"]\ -+["examples/ThirdPartyLibs/clsocket/src/PassiveSocket.cpp"]\ -+["examples/ThirdPartyLibs/clsocket/src/SimpleSocket.cpp"]\ -+["Extras/Serialize/BulletFileLoader/bChunk.cpp"]\ -+["Extras/Serialize/BulletFileLoader/bDNA.cpp"]\ -+["Extras/Serialize/BulletFileLoader/bFile.cpp"]\ -+["Extras/Serialize/BulletFileLoader/btBulletFile.cpp"]\ -+["Extras/Serialize/BulletWorldImporter/btMultiBodyWorldImporter.cpp"]\ -+["Extras/Serialize/BulletWorldImporter/btBulletWorldImporter.cpp"]\ -+["Extras/Serialize/BulletWorldImporter/btWorldImporter.cpp"]\ -+["Extras/InverseDynamics/CloneTreeCreator.cpp"]\ -+["Extras/InverseDynamics/IDRandomUtil.cpp"]\ -+["Extras/InverseDynamics/MultiBodyTreeDebugGraph.cpp"]\ -+["Extras/InverseDynamics/User2InternalIndex.cpp"]\ -+["Extras/InverseDynamics/CoilCreator.cpp"]\ -+["Extras/InverseDynamics/MultiBodyNameMap.cpp"]\ -+["Extras/InverseDynamics/RandomTreeCreator.cpp"]\ -+["Extras/InverseDynamics/btMultiBodyTreeCreator.cpp"]\ -+["Extras/InverseDynamics/DillCreator.cpp"]\ -+["Extras/InverseDynamics/MultiBodyTreeCreator.cpp"]\ -+["Extras/InverseDynamics/SimpleTreeCreator.cpp"]\ -+["Extras/InverseDynamics/invdyn_bullet_comparison.cpp"]\ -+["src/BulletSoftBody/btDefaultSoftBodySolver.cpp"]\ -+["src/BulletSoftBody/btSoftBodyHelpers.cpp"]\ -+["src/BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp"]\ -+["src/BulletSoftBody/btSoftBody.cpp"]\ -+["src/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp"]\ -+["src/BulletSoftBody/btSoftRigidDynamicsWorld.cpp"]\ -+["src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp"]\ -+["src/BulletSoftBody/btSoftMultiBodyDynamicsWorld.cpp"]\ -+["src/BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp"]\ -+["src/BulletInverseDynamics/IDMath.cpp"]\ -+["src/BulletInverseDynamics/MultiBodyTree.cpp"]\ -+["src/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp"]\ -+["src/BulletInverseDynamics/details/MultiBodyTreeInitCache.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/Jacobian.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/LinearR2.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/LinearR3.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/LinearR4.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/MatrixRmn.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/Misc.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/Node.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/Tree.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/VectorRn.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Anim.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/DragAndDrop.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Hook.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/ToolTip.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/events.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/BaseRender.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Gwen.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Skin.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Utility.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/inputhandler.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Base.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Button.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Canvas.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/CheckBox.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ColorControls.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ColorPicker.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ComboBox.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/CrossSplitter.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/DockBase.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/DockedTabControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Dragger.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/GroupBox.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/HSVColorPicker.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/HorizontalScrollBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ImagePanel.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/HorizontalSlider.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Label.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/LabelClickable.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ListBox.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/MenuItem.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Menu.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/MenuStrip.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/NumericUpDown.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/PanelListPanel.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ProgressBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Properties.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/RadioButton.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/RadioButtonController.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ResizableControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Resizer.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/RichLabel.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ScrollBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ScrollBarBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ScrollBarButton.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ScrollControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Slider.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/SplitterBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TabButton.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TabControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TabStrip.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Text.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TextBox.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TextBoxNumeric.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TreeControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TreeNode.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/VerticalScrollBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/VerticalSlider.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/WindowControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Dialog/FileOpen.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Dialog/FileSave.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Dialog/Query.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Platforms/Null.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Platforms/Windows.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Renderers/OpenGL_DebugFont.cpp"]\ - - - -egl_renderer_sources = \ -["examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp"]\ -+["examples/SharedMemory/plugins/eglPlugin/eglRendererPlugin.cpp"]\ -+["examples/Importers/ImportColladaDemo/LoadMeshFromCollada.cpp"]\ -+["examples/Importers/ImportObjDemo/LoadMeshFromObj.cpp"]\ -+["examples/Importers/ImportMeshUtility/b3ImportMeshUtility.cpp"]\ -+["examples/Importers/ImportObjDemo/Wavefront2GLInstanceGraphicsShape.cpp"]\ -+["examples/TinyRenderer/geometry.cpp"]\ -+["examples/TinyRenderer/model.cpp"]\ -+["examples/TinyRenderer/tgaimage.cpp"]\ -+["examples/TinyRenderer/our_gl.cpp"]\ -+["examples/TinyRenderer/TinyRenderer.cpp"]\ -+["examples/ThirdPartyLibs/Wavefront/tiny_obj_loader.cpp"]\ -+["examples/ThirdPartyLibs/stb_image/stb_image.cpp"]\ -+["examples/ThirdPartyLibs/stb_image/stb_image_write.cpp"]\ -+["examples/ThirdPartyLibs/tinyxml2/tinyxml2.cpp"]\ -+["examples/OpenGLWindow/SimpleCamera.cpp"]\ -+["examples/Utils/b3Clock.cpp"]\ -+["examples/Utils/b3ResourcePath.cpp"]\ -+["src/BulletCollision/CollisionShapes/btShapeHull.cpp"]\ -+["src/BulletCollision/CollisionShapes/btConvexHullShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btBoxShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btSphereShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btConvexShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btCollisionShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp"]\ -+["src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp"]\ -+["src/Bullet3Common/b3Logging.cpp"]\ -+["src/LinearMath/btAlignedAllocator.cpp"]\ -+["src/LinearMath/btConvexHull.cpp"]\ -+["src/LinearMath/btConvexHullComputer.cpp"] \ -+["src/LinearMath/btGeometryUtil.cpp"]\ -+["src/LinearMath/btQuickprof.cpp"] \ -+["src/LinearMath/btThreads.cpp"] \ -+["src/Bullet3Common/b3AlignedAllocator.cpp"] \ -+["examples/ThirdPartyLibs/glad/gl.c"]\ -+["examples/OpenGLWindow/GLInstancingRenderer.cpp"]\ -+["examples/OpenGLWindow/GLRenderToTexture.cpp"] \ -+["examples/OpenGLWindow/LoadShader.cpp"] - -if 'BT_USE_EGL' in CXX_FLAGS: - sources += ['examples/ThirdPartyLibs/glad/egl.c'] - sources += ['examples/OpenGLWindow/EGLOpenGLWindow.cpp'] - -if _platform == "linux" or _platform == "linux2": - libraries = ['dl','pthread'] - CXX_FLAGS += '-D_LINUX ' - CXX_FLAGS += '-DGLEW_STATIC ' - CXX_FLAGS += '-DGLEW_INIT_OPENGL11_FUNCTIONS=1 ' - CXX_FLAGS += '-DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 ' - CXX_FLAGS += '-DDYNAMIC_LOAD_X11_FUNCTIONS ' - CXX_FLAGS += '-DHAS_SOCKLEN_T ' - CXX_FLAGS += '-fno-inline-functions-called-once ' - EGL_CXX_FLAGS += '-DBT_USE_EGL ' - EGL_CXX_FLAGS += '-fPIC ' # for plugins - - sources = sources + ["examples/ThirdPartyLibs/enet/unix.c"]\ - +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ - +["examples/ThirdPartyLibs/glad/gl.c"]\ - +["examples/ThirdPartyLibs/glad/glx.c"] - include_dirs += ["examples/ThirdPartyLibs/optionalX11"] - - if 'BT_USE_EGL' in EGL_CXX_FLAGS: - egl_renderer_sources = egl_renderer_sources\ - +["examples/OpenGLWindow/EGLOpenGLWindow.cpp"]\ - +['examples/ThirdPartyLibs/glad/egl.c'] - else: - egl_renderer_sources = egl_renderer_sources\ - +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ - +["examples/ThirdPartyLibs/glad/glx.c"] - - -elif _platform == "win32": - print("win32!") - libraries = ['Ws2_32','Winmm','User32','Opengl32','kernel32','glu32','Gdi32','Comdlg32'] - CXX_FLAGS += '-DWIN32 ' - CXX_FLAGS += '-DGLEW_STATIC ' - sources = sources + ["examples/ThirdPartyLibs/enet/win32.c"]\ - +["examples/OpenGLWindow/Win32Window.cpp"]\ - +["examples/OpenGLWindow/Win32OpenGLWindow.cpp"]\ - +["examples/ThirdPartyLibs/glad/gl.c"] -elif _platform == "darwin": - print("darwin!") - os.environ['LDFLAGS'] = '-framework Cocoa -stdlib=libc++ -framework OpenGL' - CXX_FLAGS += '-DB3_NO_PYTHON_FRAMEWORK ' - CXX_FLAGS += '-DHAS_SOCKLEN_T ' - CXX_FLAGS += '-D_DARWIN ' -# CXX_FLAGS += '-framework Cocoa ' - sources = sources + ["examples/ThirdPartyLibs/enet/unix.c"]\ - +["examples/OpenGLWindow/MacOpenGLWindow.cpp"]\ - +["examples/ThirdPartyLibs/glad/gl.c"]\ - +["examples/OpenGLWindow/MacOpenGLWindowObjC.m"] -else: - print("bsd!") - libraries = ['GL','GLEW','pthread'] - os.environ['LDFLAGS'] = '-L/usr/X11R6/lib' - CXX_FLAGS += '-D_BSD ' - CXX_FLAGS += '-I/usr/X11R6/include ' - CXX_FLAGS += '-DHAS_SOCKLEN_T ' - CXX_FLAGS += '-fno-inline-functions-called-once' - sources = ["examples/ThirdPartyLibs/enet/unix.c"]\ - +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ - +["examples/ThirdPartyLibs/glad/gl.c"]\ - + sources - -setup_py_dir = os.path.dirname(os.path.realpath(__file__)) - -need_files = [] -datadir = "examples/pybullet/gym/pybullet_data" - -hh = setup_py_dir + "/" + datadir - -for root, dirs, files in os.walk(hh): - for fn in files: - ext = os.path.splitext(fn)[1][1:] - if ext and ext in 'yaml index meta data-00000-of-00001 png gif jpg urdf sdf obj txt mtl dae off stl STL xml '.split(): - fn = root + "/" + fn - need_files.append(fn[1+len(hh):]) - -print("found resource files: %i" % len(need_files)) -for n in need_files: print("-- %s" % n) -print("packages") -print(find_packages('examples/pybullet/gym')) -print("-----") - -extensions = [] - -pybullet_ext = Extension("pybullet", - sources = sources, - libraries = libraries, - extra_compile_args=CXX_FLAGS.split(), - include_dirs = include_dirs + ["src","examples/ThirdPartyLibs","examples/ThirdPartyLibs/glad", "examples/ThirdPartyLibs/enet/include","examples/ThirdPartyLibs/clsocket/src"] - ) -extensions.append(pybullet_ext) - - -if 'BT_USE_EGL' in EGL_CXX_FLAGS: - - eglRender = Extension("eglRenderer", - sources = egl_renderer_sources, - libraries = libraries, - extra_compile_args=(CXX_FLAGS+EGL_CXX_FLAGS ).split(), - include_dirs = include_dirs + ["src","examples", "examples/ThirdPartyLibs","examples/ThirdPartyLibs/glad", "examples/ThirdPartyLibs/enet/include","examples/ThirdPartyLibs/clsocket/src"]) - - extensions.append(eglRender) - - -setup( - name = 'pybullet', - version='2.4.3', - description='Official Python Interface for the Bullet Physics SDK specialized for Robotics Simulation and Reinforcement Learning', - long_description='pybullet is an easy to use Python module for physics simulation, robotics and deep reinforcement learning based on the Bullet Physics SDK. With pybullet you can load articulated bodies from URDF, SDF and other file formats. pybullet provides forward dynamics simulation, inverse dynamics computation, forward and inverse kinematics and collision detection and ray intersection queries. Aside from physics simulation, pybullet supports to rendering, with a CPU renderer and OpenGL visualization and support for virtual reality headsets.', - url='https://github.com/bulletphysics/bullet3', - author='Erwin Coumans, Yunfei Bai, Jasmine Hsu', - author_email='erwincoumans@google.com', - license='zlib', - platforms='any', - keywords=['game development', 'virtual reality', 'physics simulation', 'robotics', 'collision detection', 'opengl'], - ext_modules = extensions, - classifiers=['Development Status :: 5 - Production/Stable', - 'License :: OSI Approved :: zlib/libpng License', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX :: Linux', - 'Operating System :: MacOS', - 'Intended Audience :: Science/Research', - "Programming Language :: Python", - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Topic :: Games/Entertainment :: Simulation', - 'Topic :: Scientific/Engineering :: Artificial Intelligence', - 'Framework :: Robot Framework'], - package_dir = { '': 'examples/pybullet/gym'}, - packages=[x for x in find_packages('examples/pybullet/gym')], - package_data = { 'pybullet_data': need_files } -) + +from setuptools import find_packages +from sys import platform as _platform +import sys +import glob +import os + + +from distutils.core import setup +from distutils.extension import Extension +from distutils.util import get_platform +from glob import glob + +# monkey-patch for parallel compilation +import multiprocessing +import multiprocessing.pool + + +def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): + # those lines are copied from distutils.ccompiler.CCompiler directly + macros, objects, extra_postargs, pp_opts, build = self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) + cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) + # parallel code + N = 2*multiprocessing.cpu_count()# number of parallel compilations + def _single_compile(obj): + try: src, ext = build[obj] + except KeyError: return + newcc_args = cc_args + if _platform == "darwin": + if src.endswith('.cpp'): + newcc_args = cc_args + ["-stdlib=libc++"] + self._compile(obj, src, ext, newcc_args, extra_postargs, pp_opts) + # convert to list, imap is evaluated on-demand + pool = multiprocessing.pool.ThreadPool(N) + list(pool.imap(_single_compile,objects)) + return objects +import distutils.ccompiler +distutils.ccompiler.CCompiler.compile=parallelCCompile + +#see http://stackoverflow.com/a/8719066/295157 +import os + + +platform = get_platform() +print(platform) + +CXX_FLAGS = '' +CXX_FLAGS += '-DGWEN_COMPILE_STATIC ' +CXX_FLAGS += '-DBT_USE_DOUBLE_PRECISION ' +CXX_FLAGS += '-DBT_ENABLE_ENET ' +CXX_FLAGS += '-DBT_ENABLE_CLSOCKET ' +CXX_FLAGS += '-DB3_DUMP_PYTHON_VERSION ' +CXX_FLAGS += '-DEGL_ADD_PYTHON_INIT ' +CXX_FLAGS += '-DB3_ENABLE_FILEIO_PLUGIN ' +CXX_FLAGS += '-DB3_USE_ZIPFILE_FILEIO ' +CXX_FLAGS += '-DBT_THREADSAFE=1 ' +CXX_FLAGS += '-DSTATIC_LINK_SPD_PLUGIN ' +CXX_FLAGS += '-DPX_PHYSX_STATIC_LIB ' +CXX_FLAGS += '-DBT_ENABLE_PHYSX ' +CXX_FLAGS += '-DPX_FOUNDATION_DLL=0 ' +CXX_FLAGS += '-DPX_COOKING ' +CXX_FLAGS += '-DNDEBUG ' +CXX_FLAGS += '-DDISABLE_CUDA_PHYSX ' + + + +EGL_CXX_FLAGS = '' + + + +# libraries += [current_python] + +libraries = [] +include_dirs = ["examples", + "src/PhysX/physx/source/common/include", + "src/PhysX/physx/source/common/src", + "src/PhysX/physx/source/fastxml/include", + "src/PhysX/physx/source/filebuf/include", + "src/PhysX/physx/source/foundation/include", + "src/PhysX/physx/source/geomutils/include", + "src/PhysX/physx/source/geomutils/src", + "src/PhysX/physx/source/geomutils/src/ccd", + "src/PhysX/physx/source/geomutils/src/common", + "src/PhysX/physx/source/geomutils/src/contact", + "src/PhysX/physx/source/geomutils/src/convex", + "src/PhysX/physx/source/geomutils/src/distance", + "src/PhysX/physx/source/geomutils/src/gjk", + "src/PhysX/physx/source/geomutils/src/hf", + "src/PhysX/physx/source/geomutils/src/intersection", + "src/PhysX/physx/source/geomutils/src/mesh", + "src/PhysX/physx/source/geomutils/src/pcm", + "src/PhysX/physx/source/geomutils/src/sweep", + "src/PhysX/physx/source/lowlevel/api/include", + "src/PhysX/physx/source/lowlevel/common/include", + "src/PhysX/physx/source/lowlevel/common/include/collision", + "src/PhysX/physx/source/lowlevel/common/include/pipeline", + "src/PhysX/physx/source/lowlevel/common/include/utils", + "src/PhysX/physx/source/lowlevel/software/include", + "src/PhysX/physx/source/lowlevelaabb/include", + "src/PhysX/physx/source/lowleveldynamics/include", + "src/PhysX/physx/source/physx/src", + "src/PhysX/physx/source/physx/src/buffering", + "src/PhysX/physx/source/physx/src/device", + "src/PhysX/physx/source/physxcooking/src", + "src/PhysX/physx/source/physxcooking/src/convex", + "src/PhysX/physx/source/physxcooking/src/mesh", + "src/PhysX/physx/source/physxextensions/src", + "src/PhysX/physx/source/physxextensions/src/serialization/Binary", + "src/PhysX/physx/source/physxextensions/src/serialization/File", + "src/PhysX/physx/source/physxextensions/src/serialization/Xml", + "src/PhysX/physx/source/physxmetadata/core/include", + "src/PhysX/physx/source/physxmetadata/extensions/include", + "src/PhysX/physx/source/physxvehicle/src", + "src/PhysX/physx/source/physxvehicle/src/physxmetadata/include", + "src/PhysX/physx/source/pvd/include", + "src/PhysX/physx/source/scenequery/include", + "src/PhysX/physx/source/simulationcontroller/include", + "src/PhysX/physx/source/simulationcontroller/src", + "src/PhysX/physx/include", + "src/PhysX/physx/include/characterkinematic", + "src/PhysX/physx/include/common", + "src/PhysX/physx/include/cooking", + "src/PhysX/physx/include/extensions", + "src/PhysX/physx/include/geometry", + "src/PhysX/physx/include/geomutils", + "src/PhysX/physx/include/vehicle", + "src/PhysX/pxshared/include", + ] + + +try: + import numpy + NP_DIRS = [numpy.get_include()] +except: + print("numpy is disabled. getCameraImage maybe slower.") +else: + print("numpy is enabled.") + CXX_FLAGS += '-DPYBULLET_USE_NUMPY ' + for d in NP_DIRS: + print("numpy_include_dirs = %s" % d) + include_dirs += NP_DIRS + +sources = ["examples/pybullet/pybullet.c"]\ ++["examples/SharedMemory/physx/PhysXC_API.cpp"]\ ++["examples/SharedMemory/physx/PhysXClient.cpp"]\ ++["examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp"]\ ++["examples/SharedMemory/physx/PhysXUrdfImporter.cpp"]\ ++["examples/SharedMemory/physx/URDF2PhysX.cpp"]\ ++["src/btLinearMathAll.cpp"]\ ++["src/PhysXGeomUtilsAll.cpp"]\ ++["examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp"]\ ++["examples/SharedMemory/plugins/eglPlugin/eglRendererPlugin.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactBoxBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Raycast.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp"]\ ++["src/PhysX/physx/source/fastxml/src/PsFastXml.cpp"]\ ++["src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp"]\ ++["src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp"]\ ++["src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp"]\ ++["src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp"]\ ++["src/PhysX/physx/source/task/src/TaskManager.cpp"]\ ++["src/PhysXVehicleAll.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvd.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp"]\ ++["src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp"]\ ++["src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp"]\ ++["src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp"]\ ++["src/PhysXCharacterAll.cpp"]\ ++["src/PhysXCommonAll.cpp"]\ ++["src/PhysXCookingAll.cpp"]\ ++["src/PhysXExtensionAll.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/SnSerialization.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Convert.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp"]\ ++["src/PhysXFoundationAll.cpp"]\ ++["src/PhysXLowLevelAll.cpp"]\ ++["src/PhysXNpSrcAll.cpp"]\ ++["src/PhysXSceneQueryAll.cpp"]\ ++["src/PhysXSimulationControllerAll.cpp"]\ ++["src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp"]\ ++["src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp"]\ ++["src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp"]\ ++["src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp"]\ ++["src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp"]\ ++["src/btBulletCollisionAll.cpp"]\ ++["src/btBulletDynamicsAll.cpp"]\ ++["examples/ExampleBrowser/InProcessExampleBrowser.cpp"]\ ++["examples/TinyRenderer/geometry.cpp"]\ ++["examples/TinyRenderer/model.cpp"]\ ++["examples/TinyRenderer/tgaimage.cpp"]\ ++["examples/TinyRenderer/our_gl.cpp"]\ ++["examples/TinyRenderer/TinyRenderer.cpp"]\ ++["examples/SharedMemory/plugins/pdControlPlugin/pdControlPlugin.cpp"]\ ++["examples/SharedMemory/plugins/collisionFilterPlugin/collisionFilterPlugin.cpp"]\ ++["examples/SharedMemory/plugins/fileIOPlugin/fileIOPlugin.cpp"]\ ++["examples/SharedMemory/b3RobotSimulatorClientAPI_NoDirect.cpp"]\ ++["examples/SharedMemory/IKTrajectoryHelper.cpp"]\ ++["examples/SharedMemory/InProcessMemory.cpp"]\ ++["examples/SharedMemory/PhysicsClient.cpp"]\ ++["examples/SharedMemory/PhysicsServer.cpp"]\ ++["examples/SharedMemory/PhysicsServerExample.cpp"]\ ++["examples/SharedMemory/PhysicsServerExampleBullet2.cpp"]\ ++["examples/SharedMemory/SharedMemoryInProcessPhysicsC_API.cpp"]\ ++["examples/SharedMemory/PhysicsServerSharedMemory.cpp"]\ ++["examples/SharedMemory/PhysicsDirect.cpp"]\ ++["examples/SharedMemory/PhysicsDirectC_API.cpp"]\ ++["examples/SharedMemory/PhysicsServerCommandProcessor.cpp"]\ ++["examples/SharedMemory/PhysicsClientSharedMemory.cpp"]\ ++["examples/SharedMemory/PhysicsClientSharedMemory_C_API.cpp"]\ ++["examples/SharedMemory/PhysicsClientC_API.cpp"]\ ++["examples/SharedMemory/Win32SharedMemory.cpp"]\ ++["examples/SharedMemory/PosixSharedMemory.cpp"]\ ++["examples/SharedMemory/plugins/tinyRendererPlugin/TinyRendererVisualShapeConverter.cpp"]\ ++["examples/SharedMemory/plugins/tinyRendererPlugin/tinyRendererPlugin.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/BulletConversion.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/KinTree.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/MathUtil.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/RBDModel.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/RBDUtil.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/Shape.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/SpAlg.cpp"]\ ++["examples/SharedMemory/PhysicsClientUDP.cpp"]\ ++["examples/SharedMemory/PhysicsClientUDP_C_API.cpp"]\ ++["examples/SharedMemory/PhysicsClientTCP.cpp"]\ ++["examples/SharedMemory/PhysicsClientTCP_C_API.cpp"]\ ++["examples/SharedMemory/b3PluginManager.cpp"]\ ++["examples/Utils/b3ResourcePath.cpp"]\ ++["examples/Utils/RobotLoggingUtil.cpp"]\ ++["examples/Utils/ChromeTraceUtil.cpp"]\ ++["examples/Utils/b3Clock.cpp"]\ ++["examples/Utils/b3Quickprof.cpp"]\ ++["examples/ThirdPartyLibs/tinyxml2/tinyxml2.cpp"]\ ++["examples/ThirdPartyLibs/Wavefront/tiny_obj_loader.cpp"]\ ++["examples/ThirdPartyLibs/stb_image/stb_image.cpp"]\ ++["examples/ThirdPartyLibs/stb_image/stb_image_write.cpp"]\ ++["examples/ThirdPartyLibs/minizip/ioapi.c"]\ ++["examples/ThirdPartyLibs/minizip/unzip.c"]\ ++["examples/ThirdPartyLibs/minizip/zip.c"]\ ++["examples/ThirdPartyLibs/zlib/adler32.c"]\ ++["examples/ThirdPartyLibs/zlib/compress.c"]\ ++["examples/ThirdPartyLibs/zlib/crc32.c"]\ ++["examples/ThirdPartyLibs/zlib/deflate.c"]\ ++["examples/ThirdPartyLibs/zlib/gzclose.c"]\ ++["examples/ThirdPartyLibs/zlib/gzlib.c"]\ ++["examples/ThirdPartyLibs/zlib/gzread.c"]\ ++["examples/ThirdPartyLibs/zlib/gzwrite.c"]\ ++["examples/ThirdPartyLibs/zlib/infback.c"]\ ++["examples/ThirdPartyLibs/zlib/inffast.c"]\ ++["examples/ThirdPartyLibs/zlib/inflate.c"]\ ++["examples/ThirdPartyLibs/zlib/inftrees.c"]\ ++["examples/ThirdPartyLibs/zlib/trees.c"]\ ++["examples/ThirdPartyLibs/zlib/uncompr.c"]\ ++["examples/ThirdPartyLibs/zlib/zutil.c"]\ ++["examples/Importers/ImportColladaDemo/LoadMeshFromCollada.cpp"]\ ++["examples/Importers/ImportObjDemo/LoadMeshFromObj.cpp"]\ ++["examples/Importers/ImportObjDemo/Wavefront2GLInstanceGraphicsShape.cpp"]\ ++["examples/Importers/ImportMJCFDemo/BulletMJCFImporter.cpp"]\ ++["examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp"]\ ++["examples/Importers/ImportURDFDemo/MyMultiBodyCreator.cpp"]\ ++["examples/Importers/ImportURDFDemo/URDF2Bullet.cpp"]\ ++["examples/Importers/ImportURDFDemo/UrdfParser.cpp"]\ ++["examples/Importers/ImportURDFDemo/urdfStringSplit.cpp"]\ ++["examples/Importers/ImportMeshUtility/b3ImportMeshUtility.cpp"]\ ++["examples/MultiThreading/b3PosixThreadSupport.cpp"]\ ++["examples/MultiThreading/b3Win32ThreadSupport.cpp"]\ ++["examples/MultiThreading/b3ThreadSupportInterface.cpp"]\ ++["examples/ThirdPartyLibs/enet/callbacks.c"]\ ++["examples/ThirdPartyLibs/enet/compress.c"]\ ++["examples/ThirdPartyLibs/enet/host.c"]\ ++["examples/ThirdPartyLibs/enet/list.c"]\ ++["examples/ThirdPartyLibs/enet/packet.c"]\ ++["examples/ThirdPartyLibs/enet/peer.c"]\ ++["examples/ThirdPartyLibs/enet/protocol.c"]\ ++["examples/ExampleBrowser/OpenGLGuiHelper.cpp"]\ ++["examples/ExampleBrowser/OpenGLExampleBrowser.cpp"]\ ++["examples/ExampleBrowser/CollisionShape2TriangleMesh.cpp"]\ ++["examples/ExampleBrowser/GL_ShapeDrawer.cpp"]\ ++["examples/OpenGLWindow/SimpleOpenGL2Renderer.cpp"]\ ++["examples/OpenGLWindow/GLInstancingRenderer.cpp"]\ ++["examples/OpenGLWindow/SimpleOpenGL3App.cpp"]\ ++["examples/OpenGLWindow/GLPrimitiveRenderer.cpp"]\ ++["examples/OpenGLWindow/TwFonts.cpp"]\ ++["examples/OpenGLWindow/GLRenderToTexture.cpp"]\ ++["examples/OpenGLWindow/LoadShader.cpp"]\ ++["examples/OpenGLWindow/OpenSans.cpp"]\ ++["examples/OpenGLWindow/SimpleCamera.cpp"]\ ++["examples/OpenGLWindow/fontstash.cpp"]\ ++["examples/OpenGLWindow/SimpleOpenGL2App.cpp"]\ ++["examples/OpenGLWindow/opengl_fontstashcallbacks.cpp"]\ ++["examples/ExampleBrowser/GwenGUISupport/GraphingTexture.cpp"]\ ++["examples/ExampleBrowser/GwenGUISupport/GwenProfileWindow.cpp"]\ ++["examples/ExampleBrowser/GwenGUISupport/gwenUserInterface.cpp"]\ ++["examples/ExampleBrowser/GwenGUISupport/GwenParameterInterface.cpp"]\ ++["examples/ExampleBrowser/GwenGUISupport/GwenTextureWindow.cpp"]\ ++["src/Bullet3Common/b3AlignedAllocator.cpp"]\ ++["src/Bullet3Common/b3Logging.cpp"]\ ++["src/Bullet3Common/b3Vector3.cpp"]\ ++["examples/ThirdPartyLibs/clsocket/src/ActiveSocket.cpp"]\ ++["examples/ThirdPartyLibs/clsocket/src/PassiveSocket.cpp"]\ ++["examples/ThirdPartyLibs/clsocket/src/SimpleSocket.cpp"]\ ++["Extras/Serialize/BulletFileLoader/bChunk.cpp"]\ ++["Extras/Serialize/BulletFileLoader/bDNA.cpp"]\ ++["Extras/Serialize/BulletFileLoader/bFile.cpp"]\ ++["Extras/Serialize/BulletFileLoader/btBulletFile.cpp"]\ ++["Extras/Serialize/BulletWorldImporter/btMultiBodyWorldImporter.cpp"]\ ++["Extras/Serialize/BulletWorldImporter/btBulletWorldImporter.cpp"]\ ++["Extras/Serialize/BulletWorldImporter/btWorldImporter.cpp"]\ ++["Extras/InverseDynamics/CloneTreeCreator.cpp"]\ ++["Extras/InverseDynamics/IDRandomUtil.cpp"]\ ++["Extras/InverseDynamics/MultiBodyTreeDebugGraph.cpp"]\ ++["Extras/InverseDynamics/User2InternalIndex.cpp"]\ ++["Extras/InverseDynamics/CoilCreator.cpp"]\ ++["Extras/InverseDynamics/MultiBodyNameMap.cpp"]\ ++["Extras/InverseDynamics/RandomTreeCreator.cpp"]\ ++["Extras/InverseDynamics/btMultiBodyTreeCreator.cpp"]\ ++["Extras/InverseDynamics/DillCreator.cpp"]\ ++["Extras/InverseDynamics/MultiBodyTreeCreator.cpp"]\ ++["Extras/InverseDynamics/SimpleTreeCreator.cpp"]\ ++["Extras/InverseDynamics/invdyn_bullet_comparison.cpp"]\ ++["src/BulletSoftBody/btDefaultSoftBodySolver.cpp"]\ ++["src/BulletSoftBody/btSoftBodyHelpers.cpp"]\ ++["src/BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp"]\ ++["src/BulletSoftBody/btSoftBody.cpp"]\ ++["src/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp"]\ ++["src/BulletSoftBody/btSoftRigidDynamicsWorld.cpp"]\ ++["src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp"]\ ++["src/BulletSoftBody/btSoftMultiBodyDynamicsWorld.cpp"]\ ++["src/BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp"]\ ++["src/BulletInverseDynamics/IDMath.cpp"]\ ++["src/BulletInverseDynamics/MultiBodyTree.cpp"]\ ++["src/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp"]\ ++["src/BulletInverseDynamics/details/MultiBodyTreeInitCache.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/Jacobian.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/LinearR2.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/LinearR3.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/LinearR4.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/MatrixRmn.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/Misc.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/Node.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/Tree.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/VectorRn.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Anim.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/DragAndDrop.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Hook.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/ToolTip.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/events.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/BaseRender.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Gwen.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Skin.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Utility.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/inputhandler.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Base.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Button.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Canvas.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/CheckBox.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ColorControls.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ColorPicker.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ComboBox.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/CrossSplitter.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/DockBase.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/DockedTabControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Dragger.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/GroupBox.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/HSVColorPicker.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/HorizontalScrollBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ImagePanel.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/HorizontalSlider.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Label.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/LabelClickable.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ListBox.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/MenuItem.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Menu.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/MenuStrip.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/NumericUpDown.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/PanelListPanel.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ProgressBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Properties.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/RadioButton.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/RadioButtonController.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ResizableControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Resizer.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/RichLabel.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ScrollBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ScrollBarBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ScrollBarButton.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ScrollControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Slider.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/SplitterBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TabButton.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TabControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TabStrip.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Text.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TextBox.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TextBoxNumeric.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TreeControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TreeNode.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/VerticalScrollBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/VerticalSlider.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/WindowControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Dialog/FileOpen.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Dialog/FileSave.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Dialog/Query.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Platforms/Null.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Platforms/Windows.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Renderers/OpenGL_DebugFont.cpp"] + + + +egl_renderer_sources = ["examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp"]\ ++["examples/SharedMemory/plugins/eglPlugin/eglRendererPlugin.cpp"]\ ++["examples/Importers/ImportColladaDemo/LoadMeshFromCollada.cpp"]\ ++["examples/Importers/ImportObjDemo/LoadMeshFromObj.cpp"]\ ++["examples/Importers/ImportMeshUtility/b3ImportMeshUtility.cpp"]\ ++["examples/Importers/ImportObjDemo/Wavefront2GLInstanceGraphicsShape.cpp"]\ ++["examples/TinyRenderer/geometry.cpp"]\ ++["examples/TinyRenderer/model.cpp"]\ ++["examples/TinyRenderer/tgaimage.cpp"]\ ++["examples/TinyRenderer/our_gl.cpp"]\ ++["examples/TinyRenderer/TinyRenderer.cpp"]\ ++["examples/ThirdPartyLibs/Wavefront/tiny_obj_loader.cpp"]\ ++["examples/ThirdPartyLibs/stb_image/stb_image.cpp"]\ ++["examples/ThirdPartyLibs/stb_image/stb_image_write.cpp"]\ ++["examples/ThirdPartyLibs/tinyxml2/tinyxml2.cpp"]\ ++["examples/OpenGLWindow/SimpleCamera.cpp"]\ ++["examples/Utils/b3Clock.cpp"]\ ++["examples/Utils/b3ResourcePath.cpp"]\ ++["src/BulletCollision/CollisionShapes/btShapeHull.cpp"]\ ++["src/BulletCollision/CollisionShapes/btConvexHullShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btBoxShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btSphereShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btConvexShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btCollisionShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp"]\ ++["src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp"]\ ++["src/Bullet3Common/b3Logging.cpp"]\ ++["src/LinearMath/btAlignedAllocator.cpp"]\ ++["src/LinearMath/btConvexHull.cpp"]\ ++["src/LinearMath/btConvexHullComputer.cpp"] \ ++["src/LinearMath/btGeometryUtil.cpp"]\ ++["src/LinearMath/btQuickprof.cpp"] \ ++["src/LinearMath/btThreads.cpp"] \ ++["src/Bullet3Common/b3AlignedAllocator.cpp"] \ ++["examples/ThirdPartyLibs/glad/gl.c"]\ ++["examples/OpenGLWindow/GLInstancingRenderer.cpp"]\ ++["examples/OpenGLWindow/GLRenderToTexture.cpp"] \ ++["examples/OpenGLWindow/LoadShader.cpp"] + +if 'BT_USE_EGL' in CXX_FLAGS: + sources += ['examples/ThirdPartyLibs/glad/egl.c'] + sources += ['examples/OpenGLWindow/EGLOpenGLWindow.cpp'] + +if _platform == "linux" or _platform == "linux2": + libraries = ['dl','pthread'] + CXX_FLAGS += '-D_LINUX ' + CXX_FLAGS += '-DGLEW_STATIC ' + CXX_FLAGS += '-DGLEW_INIT_OPENGL11_FUNCTIONS=1 ' + CXX_FLAGS += '-DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 ' + CXX_FLAGS += '-DDYNAMIC_LOAD_X11_FUNCTIONS ' + CXX_FLAGS += '-DHAS_SOCKLEN_T ' + CXX_FLAGS += '-fno-inline-functions-called-once ' + EGL_CXX_FLAGS += '-DBT_USE_EGL ' + EGL_CXX_FLAGS += '-fPIC ' # for plugins + + sources = sources + ["examples/ThirdPartyLibs/enet/unix.c"]\ + +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ + +["examples/ThirdPartyLibs/glad/gl.c"]\ + +["src/PhysXFoundationUnix.cpp"]\ + +["examples/ThirdPartyLibs/glad/glx.c"] + include_dirs += ["examples/ThirdPartyLibs/optionalX11"] + + if 'BT_USE_EGL' in EGL_CXX_FLAGS: + egl_renderer_sources = egl_renderer_sources\ + +["examples/OpenGLWindow/EGLOpenGLWindow.cpp"]\ + +['examples/ThirdPartyLibs/glad/egl.c'] + else: + egl_renderer_sources = egl_renderer_sources\ + +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ + +["examples/ThirdPartyLibs/glad/glx.c"] + + +elif _platform == "win32": + print("win32!") + libraries = ['Ws2_32','Winmm','User32','Opengl32','kernel32','glu32','Gdi32','Comdlg32'] + CXX_FLAGS += '-DWIN32 ' + CXX_FLAGS += '-DGLEW_STATIC ' + sources = sources + ["examples/ThirdPartyLibs/enet/win32.c"]\ + +["examples/OpenGLWindow/Win32Window.cpp"]\ + +["examples/OpenGLWindow/Win32OpenGLWindow.cpp"]\ + +["examples/ThirdPartyLibs/glad/gl.c"] + sources = sources + +["src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp"]\ + +["src/PhysX/physx/source/common/src/windows/CmWindowsDelayLoadHook.cpp"]\ + +["PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp"]\ + +["src/PhysXFoundationWindows.cpp"]\ + +["src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp"] +elif _platform == "darwin": + print("darwin!") + os.environ['LDFLAGS'] = '-framework Cocoa -stdlib=libc++ -framework OpenGL' + CXX_FLAGS += '-DB3_NO_PYTHON_FRAMEWORK ' + CXX_FLAGS += '-DHAS_SOCKLEN_T ' + CXX_FLAGS += '-D_DARWIN ' +# CXX_FLAGS += '-framework Cocoa ' + sources = sources + ["examples/ThirdPartyLibs/enet/unix.c"]\ + +["src/PhysXFoundationUnix.cpp"]\ + +["examples/OpenGLWindow/MacOpenGLWindow.cpp"]\ + +["examples/ThirdPartyLibs/glad/gl.c"]\ + +["examples/OpenGLWindow/MacOpenGLWindowObjC.m"] +else: + print("bsd!") + libraries = ['GL','GLEW','pthread'] + os.environ['LDFLAGS'] = '-L/usr/X11R6/lib' + CXX_FLAGS += '-D_BSD ' + CXX_FLAGS += '-I/usr/X11R6/include ' + CXX_FLAGS += '-DHAS_SOCKLEN_T ' + CXX_FLAGS += '-fno-inline-functions-called-once' + sources = ["examples/ThirdPartyLibs/enet/unix.c"]\ + +["src/PhysXFoundationUnix.cpp"]\ + +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ + +["examples/ThirdPartyLibs/glad/gl.c"]\ + + sources + +setup_py_dir = os.path.dirname(os.path.realpath(__file__)) + +need_files = [] +datadir = "examples/pybullet/gym/pybullet_data" + +hh = setup_py_dir + "/" + datadir + +for root, dirs, files in os.walk(hh): + for fn in files: + ext = os.path.splitext(fn)[1][1:] + if ext and ext in 'yaml index meta data-00000-of-00001 png gif jpg urdf sdf obj txt mtl dae off stl STL xml '.split(): + fn = root + "/" + fn + need_files.append(fn[1+len(hh):]) + +print("found resource files: %i" % len(need_files)) +for n in need_files: print("-- %s" % n) +print("packages") +print(find_packages('examples/pybullet/gym')) +print("-----") + +extensions = [] + +pybullet_ext = Extension("pybullet", + sources = sources, + libraries = libraries, + extra_compile_args=CXX_FLAGS.split(), + include_dirs = include_dirs + ["src","examples/ThirdPartyLibs","examples/ThirdPartyLibs/glad", "examples/ThirdPartyLibs/enet/include","examples/ThirdPartyLibs/clsocket/src"] + ) +extensions.append(pybullet_ext) + + +if 'BT_USE_EGL' in EGL_CXX_FLAGS: + + eglRender = Extension("eglRenderer", + sources = egl_renderer_sources, + libraries = libraries, + extra_compile_args=(CXX_FLAGS+EGL_CXX_FLAGS ).split(), + include_dirs = include_dirs + ["src","examples", "examples/ThirdPartyLibs","examples/ThirdPartyLibs/glad", "examples/ThirdPartyLibs/enet/include","examples/ThirdPartyLibs/clsocket/src"]) + + extensions.append(eglRender) + + +setup( + name = 'pybullet', + version='2.4.3', + description='Official Python Interface for the Bullet Physics SDK specialized for Robotics Simulation and Reinforcement Learning', + long_description='pybullet is an easy to use Python module for physics simulation, robotics and deep reinforcement learning based on the Bullet Physics SDK. With pybullet you can load articulated bodies from URDF, SDF and other file formats. pybullet provides forward dynamics simulation, inverse dynamics computation, forward and inverse kinematics and collision detection and ray intersection queries. Aside from physics simulation, pybullet supports to rendering, with a CPU renderer and OpenGL visualization and support for virtual reality headsets.', + url='https://github.com/bulletphysics/bullet3', + author='Erwin Coumans, Yunfei Bai, Jasmine Hsu', + author_email='erwincoumans@google.com', + license='zlib', + platforms='any', + keywords=['game development', 'virtual reality', 'physics simulation', 'robotics', 'collision detection', 'opengl'], + ext_modules = extensions, + classifiers=['Development Status :: 5 - Production/Stable', + 'License :: OSI Approved :: zlib/libpng License', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX :: Linux', + 'Operating System :: MacOS', + 'Intended Audience :: Science/Research', + "Programming Language :: Python", + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Topic :: Games/Entertainment :: Simulation', + 'Topic :: Scientific/Engineering :: Artificial Intelligence', + 'Framework :: Robot Framework'], + package_dir = { '': 'examples/pybullet/gym'}, + packages=[x for x in find_packages('examples/pybullet/gym')], + package_data = { 'pybullet_data': need_files } +) diff --git a/src/PhysX/physx/include/PxActor.h b/src/PhysX/physx/include/PxActor.h new file mode 100644 index 000000000..6e3306ca9 --- /dev/null +++ b/src/PhysX/physx/include/PxActor.h @@ -0,0 +1,330 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ACTOR +#define PX_PHYSICS_NX_ACTOR + +/** \addtogroup physics + @{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxBounds3.h" +#include "PxClient.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxRigidActor; +class PxRigidBody; +class PxRigidStatic; +class PxRigidDynamic; +class PxArticulation; +class PxArticulationLink; + + +/** Group index which allows to specify 1- or 2-way interaction */ +typedef PxU8 PxDominanceGroup; // Must be < 32, PxU8. + +/** +\brief Flags which control the behavior of an actor. + +@see PxActorFlags PxActor PxActor.setActorFlag() PxActor.getActorFlags() +*/ +struct PxActorFlag +{ + enum Enum + { + /** + \brief Enable debug renderer for this actor + + @see PxScene.getRenderBuffer() PxRenderBuffer PxVisualizationParameter + */ + eVISUALIZATION = (1<<0), + + /** + \brief Disables scene gravity for this actor + */ + eDISABLE_GRAVITY = (1<<1), + + /** + \brief Enables the sending of PxSimulationEventCallback::onWake() and PxSimulationEventCallback::onSleep() notify events + + @see PxSimulationEventCallback::onWake() PxSimulationEventCallback::onSleep() + */ + eSEND_SLEEP_NOTIFIES = (1<<2), + + /** + \brief Disables simulation for the actor. + + \note This is only supported by PxRigidStatic and PxRigidDynamic actors and can be used to reduce the memory footprint when rigid actors are + used for scene queries only. + + \note Setting this flag will remove all constraints attached to the actor from the scene. + + \note If this flag is set, the following calls are forbidden: + \li PxRigidBody: setLinearVelocity(), setAngularVelocity(), addForce(), addTorque(), clearForce(), clearTorque() + \li PxRigidDynamic: setKinematicTarget(), setWakeCounter(), wakeUp(), putToSleep() + + \par Sleeping: + Raising this flag will set all velocities and the wake counter to 0, clear all forces, clear the kinematic target, put the actor + to sleep and wake up all touching actors from the previous frame. + */ + eDISABLE_SIMULATION = (1<<3) + }; +}; + +/** +\brief collection of set bits defined in PxActorFlag. + +@see PxActorFlag +*/ +typedef PxFlags PxActorFlags; +PX_FLAGS_OPERATORS(PxActorFlag::Enum,PxU8) + +/** +\brief Identifies each type of actor. +@see PxActor +*/ +struct PxActorType +{ + enum Enum + { + /** + \brief A static rigid body + @see PxRigidStatic + */ + eRIGID_STATIC, + + /** + \brief A dynamic rigid body + @see PxRigidDynamic + */ + eRIGID_DYNAMIC, + + /** + \brief An articulation link + @see PxArticulationLink + */ + eARTICULATION_LINK, + + //brief internal use only! + eACTOR_COUNT, + + eACTOR_FORCE_DWORD = 0x7fffffff + }; +}; + +/** +\brief PxActor is the base class for the main simulation objects in the physics SDK. + +The actor is owned by and contained in a PxScene. + +*/ +class PxActor : public PxBase +{ +public: + /** + \brief Deletes the actor. + + Do not keep a reference to the deleted instance. + + If the actor belongs to a #PxAggregate object, it is automatically removed from the aggregate. + + @see PxBase.release(), PxAggregate + */ + virtual void release() = 0; + + /** + \brief Retrieves the type of actor. + + \return The actor type of the actor. + + @see PxActorType + */ + virtual PxActorType::Enum getType() const = 0; + + /** + \brief Retrieves the scene which this actor belongs to. + + \return Owner Scene. NULL if not part of a scene. + + @see PxScene + */ + virtual PxScene* getScene() const = 0; + + // Runtime modifications + + /** + \brief Sets a name string for the object that can be retrieved with getName(). + + This is for debugging and is not used by the SDK. The string is not copied by the SDK, + only the pointer is stored. + + \param[in] name String to set the objects name to. + + Default: NULL + + @see getName() + */ + virtual void setName(const char* name) = 0; + + /** + \brief Retrieves the name string set with setName(). + + \return Name string associated with object. + + @see setName() + */ + virtual const char* getName() const = 0; + + /** + \brief Retrieves the axis aligned bounding box enclosing the actor. + + \param[in] inflation Scale factor for computed world bounds. Box extents are multiplied by this value. + + \return The actor's bounding box. + + @see PxBounds3 + */ + virtual PxBounds3 getWorldBounds(float inflation=1.01f) const = 0; + + /** + \brief Raises or clears a particular actor flag. + + See the list of flags #PxActorFlag + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] flag The PxActor flag to raise(set) or clear. See #PxActorFlag. + \param[in] value The boolean value to assign to the flag. + + Default: PxActorFlag::eVISUALIZATION + + @see PxActorFlag getActorFlags() + */ + virtual void setActorFlag(PxActorFlag::Enum flag, bool value) = 0; + /** + \brief sets the actor flags + + See the list of flags #PxActorFlag + @see PxActorFlag setActorFlag() + */ + virtual void setActorFlags( PxActorFlags inFlags ) = 0; + + /** + \brief Reads the PxActor flags. + + See the list of flags #PxActorFlag + + \return The values of the PxActor flags. + + @see PxActorFlag setActorFlag() + */ + virtual PxActorFlags getActorFlags() const = 0; + + /** + \brief Assigns dynamic actors a dominance group identifier. + + PxDominanceGroup is a 5 bit group identifier (legal range from 0 to 31). + + The PxScene::setDominanceGroupPair() lets you set certain behaviors for pairs of dominance groups. + By default every dynamic actor is created in group 0. + + Default: 0 + + Sleeping: Changing the dominance group does NOT wake the actor up automatically. + + \param[in] dominanceGroup The dominance group identifier. Range: [0..31] + + @see getDominanceGroup() PxDominanceGroup PxScene::setDominanceGroupPair() + */ + virtual void setDominanceGroup(PxDominanceGroup dominanceGroup) = 0; + + /** + \brief Retrieves the value set with setDominanceGroup(). + + \return The dominance group of this actor. + + @see setDominanceGroup() PxDominanceGroup PxScene::setDominanceGroupPair() + */ + virtual PxDominanceGroup getDominanceGroup() const = 0; + + + /** + \brief Sets the owner client of an actor. + + This cannot be done once the actor has been placed into a scene. + + Default: PX_DEFAULT_CLIENT + + @see PxClientID PxScene::createClient() + */ + virtual void setOwnerClient( PxClientID inClient ) = 0; + + /** + \brief Returns the owner client that was specified with at creation time. + + This value cannot be changed once the object is placed into the scene. + + @see PxClientID PxScene::createClient() + */ + virtual PxClientID getOwnerClient() const = 0; + + /** + \brief Retrieves the aggregate the actor might be a part of. + + \return The aggregate the actor is a part of, or NULL if the actor does not belong to an aggregate. + + @see PxAggregate + */ + virtual PxAggregate* getAggregate() const = 0; + + //public variables: + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. + +protected: + PX_INLINE PxActor(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags), userData(NULL) {} + PX_INLINE PxActor(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxActor() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxActor", name) || PxBase::isKindOf(name); } + + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxAggregate.h b/src/PhysX/physx/include/PxAggregate.h new file mode 100644 index 000000000..3c0b97700 --- /dev/null +++ b/src/PhysX/physx/include/PxAggregate.h @@ -0,0 +1,213 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_AGGREGATE +#define PX_PHYSICS_NX_AGGREGATE + +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxActor; +class PxBVHStructure; + +/** +\brief Class to aggregate actors into a single broad-phase entry. + +A PxAggregate object is a collection of PxActors, which will exist as a single entry in the +broad-phase structures. This has 3 main benefits: + +1) it reduces "broad phase pollution" by allowing a collection of spatially coherent broad-phase +entries to be replaced by a single aggregated entry (e.g. a ragdoll or a single actor with a +large number of attached shapes). + +2) it reduces broad-phase memory usage + +3) filtering can be optimized a lot if self-collisions within an aggregate are not needed. For + example if you don't need collisions between ragdoll bones, it's faster to simply disable + filtering once and for all, for the aggregate containing the ragdoll, rather than filtering + out each bone-bone collision in the filter shader. + +@see PxActor, PxPhysics.createAggregate +*/ + +class PxAggregate : public PxBase +{ +public: + + /** + \brief Deletes the aggregate object. + + Deleting the PxAggregate object does not delete the aggregated actors. If the PxAggregate object + belongs to a scene, the aggregated actors are automatically re-inserted in that scene. If you intend + to delete both the PxAggregate and its actors, it is best to release the actors first, then release + the PxAggregate when it is empty. + */ + virtual void release() = 0; + + /** + \brief Adds an actor to the aggregate object. + + A warning is output if the total number of actors is reached, or if the incoming actor already belongs + to an aggregate. + + If the aggregate belongs to a scene, adding an actor to the aggregate also adds the actor to that scene. + + If the actor already belongs to a scene, a warning is output and the call is ignored. You need to remove + the actor from the scene first, before adding it to the aggregate. + + \note When BVHStructure is provided the actor shapes are grouped together. + The scene query pruning structure inside PhysX SDK will store/update one + bound per actor. The scene queries against such an actor will query actor + bounds and then make a local space query against the provided BVH structure, which is in + actor's local space. + + \param [in] actor The actor that should be added to the aggregate + \param [in] bvhStructure BVHStructure for actor shapes. + return true if success + */ + virtual bool addActor(PxActor& actor, const PxBVHStructure* bvhStructure = NULL) = 0; + + /** + \brief Removes an actor from the aggregate object. + + A warning is output if the incoming actor does not belong to the aggregate. Otherwise the actor is + removed from the aggregate. If the aggregate belongs to a scene, the actor is reinserted in that + scene. If you intend to delete the actor, it is best to call #PxActor::release() directly. That way + the actor will be automatically removed from its aggregate (if any) and not reinserted in a scene. + + \param [in] actor The actor that should be removed from the aggregate + return true if success + */ + virtual bool removeActor(PxActor& actor) = 0; + + /** + \brief Adds an articulation to the aggregate object. + + A warning is output if the total number of actors is reached (every articulation link counts as an actor), + or if the incoming articulation already belongs to an aggregate. + + If the aggregate belongs to a scene, adding an articulation to the aggregate also adds the articulation to that scene. + + If the articulation already belongs to a scene, a warning is output and the call is ignored. You need to remove + the articulation from the scene first, before adding it to the aggregate. + + \param [in] articulation The articulation that should be added to the aggregate + return true if success + */ + virtual bool addArticulation(PxArticulationBase& articulation) = 0; + + /** + \brief Removes an articulation from the aggregate object. + + A warning is output if the incoming articulation does not belong to the aggregate. Otherwise the articulation is + removed from the aggregate. If the aggregate belongs to a scene, the articulation is reinserted in that + scene. If you intend to delete the articulation, it is best to call #PxArticulation::release() directly. That way + the articulation will be automatically removed from its aggregate (if any) and not reinserted in a scene. + + \param [in] articulation The articulation that should be removed from the aggregate + return true if success + */ + virtual bool removeArticulation(PxArticulationBase& articulation) = 0; + + /** + \brief Returns the number of actors contained in the aggregate. + + You can use #getActors() to retrieve the actor pointers. + + \return Number of actors contained in the aggregate. + + @see PxActor getActors() + */ + virtual PxU32 getNbActors() const = 0; + + /** + \brief Retrieves max amount of actors that can be contained in the aggregate. + + \return Max aggregate size. + + @see PxPhysics::createAggregate() + */ + virtual PxU32 getMaxNbActors() const = 0; + + /** + \brief Retrieve all actors contained in the aggregate. + + You can retrieve the number of actor pointers by calling #getNbActors() + + \param[out] userBuffer The buffer to store the actor pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first actor pointer to be retrieved + \return Number of actor pointers written to the buffer. + + @see PxShape getNbShapes() + */ + virtual PxU32 getActors(PxActor** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Retrieves the scene which this aggregate belongs to. + + \return Owner Scene. NULL if not part of a scene. + + @see PxScene + */ + virtual PxScene* getScene() = 0; + + /** + \brief Retrieves aggregate's self-collision flag. + + \return self-collision flag + */ + virtual bool getSelfCollision() const = 0; + + virtual const char* getConcreteTypeName() const { return "PxAggregate"; } + +protected: + PX_INLINE PxAggregate(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxAggregate(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxAggregate() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxAggregate", name) || PxBase::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulation.h b/src/PhysX/physx/include/PxArticulation.h new file mode 100644 index 000000000..fd4bc3e82 --- /dev/null +++ b/src/PhysX/physx/include/PxArticulation.h @@ -0,0 +1,281 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION +#define PX_PHYSICS_NX_ARTICULATION +/** \addtogroup physics +@{ */ + +#include "PxArticulationBase.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxArticulationLink; + +/** +\brief Articulation drive cache + +This cache is used for making one or more impulse applications to the articulation. + +@see PxArticulation PxArticulation.createDriveCache +*/ +class PxArticulationDriveCache +{ +protected: + PxArticulationDriveCache(); +}; + + +/** +\brief a tree structure of bodies connected by joints that is treated as a unit by the dynamics solver + +Articulations are more expensive to simulate than the equivalent collection of +PxRigidDynamic and PxJoint structures, but because the dynamics solver treats +each articulation as a single object, they are much less prone to separation and +have better support for actuation. An articulation may have at most 64 links. + +@see PxArticulationJoint PxArticulationLink PxPhysics.createArticulation +*/ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4435) +#endif + +class PxArticulation : public PxArticulationBase +{ +public: + + virtual void release() = 0; + + /** + \brief sets maxProjectionIterations. + + This is the maximum number of iterations to run projection on the articulation to bring + the links back together if the separation tolerance is exceeded. + + + \param[in] iterations the maximum number of projection iterations + Default: 4 + + @see getMaxProjectionIterations() + */ + virtual void setMaxProjectionIterations(PxU32 iterations) = 0; + + /** + \brief gets maxProjectionIterations. + + \return the maximum number of projection iterations + + @see setMaxProjectionIterations() + */ + + virtual PxU32 getMaxProjectionIterations() const = 0; + + /** + \brief sets separationTolerance. + + This is the maximum allowed separation of any joint in the articulation before projection is used + + Default: 0.1f, scaled by the tolerance scale + + \param[in] tolerance the separation tolerance for the articulation + + @see getSeparationTolerance() + */ + virtual void setSeparationTolerance(PxReal tolerance) = 0; + + /** + \brief gets separationTolerance. + + \return the separation tolerance + + @see setSeparationTolerance() + */ + + virtual PxReal getSeparationTolerance() const = 0; + + + /** + \brief sets the number of iterations used to compute the drive response to internal forces + + The drive model uses an iterative algorithm to determine the load on each joint of the articulation. + This is the number of iterations to use when computing response of the drive to internal forces. + + \param[in] iterations the number of iterations used to compute the drive response to internal forces. + + Default: 4 + + @see getInternalDriveIterations() + */ + virtual void setInternalDriveIterations(PxU32 iterations) = 0; + + /** + \brief gets internal driveIterations. + + \return the number of iterations used to compute the drive response to internal forces + + @see setInternalDriveIterations() + */ + + virtual PxU32 getInternalDriveIterations() const = 0; + + + /** + \brief sets the number of iterations for drive response to external forces. + + The drive model uses an iterative algorithm to determine the load on each joint of the articulation. + This is the number of iterations to use when computing response of the drive to external forces. + + \param[in] iterations the number of iterations used to compute the drive response to external forces. + + Default: 4 + + @see getExternalDriveIterations() + */ + + virtual void setExternalDriveIterations(PxU32 iterations) = 0; + + /** + \brief gets externalDriveIterations. + + \return the number of iterations used to compute the drive response to external forces + + @see setExternalDriveIterations() + */ + + virtual PxU32 getExternalDriveIterations() const = 0; + + /** + \brief create a drive cache for applying impulses which are propagated to the entire articulation + + \param[in] compliance the compliance value to use at all joints of the articulation. This is equivalent to the external compliance + parameter for articulation joints, as the impulse is treated as an external force + \param[in] driveIterations the number of iterations to use to evaluate the drive strengths + + \return a drive cache + + @see PxArticulationDriveCache updateDriveCache releaseDriveCache applyImpulse computeImpulseResponse + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + + */ + virtual PxArticulationDriveCache* + createDriveCache(PxReal compliance, PxU32 driveIterations) const = 0; + + + /** + \brief update a drive cache + + \param[in] driveCache the drive cache to update + \param[in] compliance the compliance value to use at all joints of the articulation. + \param[in] driveIterations the number of iterations to use to evaluate the drive strengths + + @see releaseDriveCache createDriveCache applyImpulse computeImpulseResponse + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + + */ + virtual void updateDriveCache(PxArticulationDriveCache& driveCache, + PxReal compliance, + PxU32 driveIterations) const = 0; + + /** + \brief release a drive cache + + \param[in] driveCache the drive cache to release + + @see createDriveCache updateDriveCache + */ + virtual void releaseDriveCache(PxArticulationDriveCache& driveCache) const = 0; + + /** + \brief apply an impulse to an entire articulation + + \param[in] link the link to which to apply the impulse + \param[in] driveCache the drive cache + \param[in] linearImpulse the linear impulse to apply + \param[in] angularImpulse the angular impulse to apply + + @see computeImpulseResponse + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + + */ + virtual void applyImpulse(PxArticulationLink* link, + const PxArticulationDriveCache& driveCache, + const PxVec3& linearImpulse, + const PxVec3& angularImpulse) = 0; + + /** + \brief determine the effect of applying an impulse to an entire articulation, without applying the impulse + + \param[in] link the link to which to apply the impulse + \param[out] linearResponse the change in linear velocity of the articulation link + \param[out] angularResponse the change in angular velocity of the articulation link + \param[in] driveCache the drive cache + \param[in] linearImpulse the linear impulse to apply + \param[in] angularImpulse the angular impulse to apply + + @see applyImpulse + + This call will wake up the articulation if it is asleep. + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + */ + + virtual void computeImpulseResponse(PxArticulationLink*link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const PxArticulationDriveCache& driveCache, + const PxVec3& linearImpulse, + const PxVec3& angularImpulse) const = 0; + +protected: + PX_INLINE PxArticulation(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationBase(concreteType, baseFlags){} + PX_INLINE PxArticulation(PxBaseFlags baseFlags) : PxArticulationBase(baseFlags){} + virtual ~PxArticulation() {} + +}; + +#if PX_VC +#pragma warning(pop) +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulationBase.h b/src/PhysX/physx/include/PxArticulationBase.h new file mode 100644 index 000000000..596ee4c5a --- /dev/null +++ b/src/PhysX/physx/include/PxArticulationBase.h @@ -0,0 +1,337 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION_BASE +#define PX_PHYSICS_NX_ARTICULATION_BASE +/** \addtogroup physics +@{ */ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + + class PxArticulationImpl; + + /** + \brief a tree structure of bodies connected by joints that is treated as a unit by the dynamics solver + + Articulations are more expensive to simulate than the equivalent collection of + PxRigidDynamic and PxJoint structures, but because the dynamics solver treats + each articulation as a single object, they are much less prone to separation and + have better support for actuation. An articulation may have at most 64 links. + + @see PxArticulationJoint PxArticulationLink PxPhysics.createArticulation + */ + class PxArticulationBase : public PxBase + { + public: + + enum Enum + { + eReducedCoordinate = 0, + eMaximumCoordinate = 1 + }; + + /** + \brief Retrieves the scene which this articulation belongs to. + + \return Owner Scene. NULL if not part of a scene. + + @see PxScene + */ + virtual PxScene* getScene() const = 0; + + /** + \brief Sets the solver iteration counts for the articulation. + + The solver iteration count determines how accurately joints and contacts are resolved. + If you are having trouble with jointed bodies oscillating and behaving erratically, then + setting a higher position iteration count may improve their stability. + + If intersecting bodies are being depenetrated too violently, increase the number of velocity + iterations. More velocity iterations will drive the relative exit velocity of the intersecting + objects closer to the correct value given the restitution. + + \param[in] minPositionIters Number of position iterations the solver should perform for this articulation. Range: [1,255] + \param[in] minVelocityIters Number of velocity iterations the solver should perform for this articulation. Range: [1,255] + + @see getSolverIterationCounts() + */ + virtual void setSolverIterationCounts(PxU32 minPositionIters, PxU32 minVelocityIters = 1) = 0; + + /** + \brief Retrieves the solver iteration counts. + + @see setSolverIterationCounts() + */ + virtual void getSolverIterationCounts(PxU32 & minPositionIters, PxU32 & minVelocityIters) const = 0; + + /** + \brief Returns true if this articulation is sleeping. + + When an actor does not move for a period of time, it is no longer simulated in order to save time. This state + is called sleeping. However, because the object automatically wakes up when it is either touched by an awake object, + or a sleep-affecting property is changed by the user, the entire sleep mechanism should be transparent to the user. + + An articulation can only go to sleep if all links are ready for sleeping. An articulation is guaranteed to be awake + if at least one of the following holds: + + \li The wake counter is positive (see #setWakeCounter()). + \li The linear or angular velocity of any link is non-zero. + \li A non-zero force or torque has been applied to the articulation or any of its links. + + If an articulation is sleeping, the following state is guaranteed: + + \li The wake counter is zero. + \li The linear and angular velocity of all links is zero. + \li There is no force update pending. + + When an articulation gets inserted into a scene, it will be considered asleep if all the points above hold, else it will + be treated as awake. + + If an articulation is asleep after the call to PxScene::fetchResults() returns, it is guaranteed that the poses of the + links were not changed. You can use this information to avoid updating the transforms of associated of dependent objects. + + \note It is invalid to use this method if the articulation has not been added to a scene already. + + \return True if the articulation is sleeping. + + @see isSleeping() wakeUp() putToSleep() getSleepThreshold() + */ + virtual bool isSleeping() const = 0; + + /** + \brief Sets the mass-normalized energy threshold below which an articulation may go to sleep. + + The articulation will sleep if the energy of each body is below this threshold. + + \param[in] threshold Energy below which an actor may go to sleep. Range: [0, PX_MAX_F32) + + @see isSleeping() getSleepThreshold() wakeUp() putToSleep() + */ + virtual void setSleepThreshold(PxReal threshold) = 0; + + /** + \brief Returns the mass-normalized energy below which an articulation may go to sleep. + + \return The energy threshold for sleeping. + + @see isSleeping() wakeUp() putToSleep() setSleepThreshold() + */ + virtual PxReal getSleepThreshold() const = 0; + + /** + \brief Sets the mass-normalized kinetic energy threshold below which an articulation may participate in stabilization. + + Articulation whose kinetic energy divided by their mass is above this threshold will not participate in stabilization. + + This value has no effect if PxSceneFlag::eENABLE_STABILIZATION was not enabled on the PxSceneDesc. + + Default: 0.01 * PxTolerancesScale::speed * PxTolerancesScale::speed + + \param[in] threshold Energy below which an actor may participate in stabilization. Range: [0,inf) + + @see getStabilizationThreshold() PxSceneFlag::eENABLE_STABILIZATION + */ + virtual void setStabilizationThreshold(PxReal threshold) = 0; + + /** + \brief Returns the mass-normalized kinetic energy below which an articulation may participate in stabilization. + + Articulations whose kinetic energy divided by their mass is above this threshold will not participate in stabilization. + + \return The energy threshold for participating in stabilization. + + @see setStabilizationThreshold() PxSceneFlag::eENABLE_STABILIZATION + */ + virtual PxReal getStabilizationThreshold() const = 0; + + /** + \brief Sets the wake counter for the articulation. + + The wake counter value determines the minimum amount of time until the articulation can be put to sleep. Please note + that an articulation will not be put to sleep if the energy is above the specified threshold (see #setSleepThreshold()) + or if other awake objects are touching it. + + \note Passing in a positive value will wake the articulation up automatically. + + Default: 0.4 (which corresponds to 20 frames for a time step of 0.02) + + \param[in] wakeCounterValue Wake counter value. Range: [0, PX_MAX_F32) + + @see isSleeping() getWakeCounter() + */ + virtual void setWakeCounter(PxReal wakeCounterValue) = 0; + + /** + \brief Returns the wake counter of the articulation. + + \return The wake counter of the articulation. + + @see isSleeping() setWakeCounter() + */ + virtual PxReal getWakeCounter() const = 0; + + /** + \brief Wakes up the articulation if it is sleeping. + + The articulation will get woken up and might cause other touching objects to wake up as well during the next simulation step. + + \note This will set the wake counter of the articulation to the value specified in #PxSceneDesc::wakeCounterResetValue. + + \note It is invalid to use this method if the articulation has not been added to a scene already. + + @see isSleeping() putToSleep() + */ + virtual void wakeUp() = 0; + + /** + \brief Forces the articulation to sleep. + + The articulation will stay asleep during the next simulation step if not touched by another non-sleeping actor. + + \note This will set any applied force, the velocity and the wake counter of all bodies in the articulation to zero. + + \note It is invalid to use this method if the articulation has not been added to a scene already. + + @see isSleeping() wakeUp() + */ + virtual void putToSleep() = 0; + + /** + \brief adds a link to the articulation with default attribute values. + + \param[in] parent the parent link of the articulation. Should be NULL if (and only if) this is the root link + \param[in] pose the initial pose of the new link. Must be a valid transform + + \return the new link, or NULL if the link cannot be created because the articulation has reached + its maximum link count (currently 64). + + @see PxArticulationLink + */ + + virtual PxArticulationLink* createLink(PxArticulationLink* parent, const PxTransform& pose) = 0; + + + /** + \brief returns the number of links in the articulation + */ + + virtual PxU32 getNbLinks() const = 0; + + /** + \brief returns the set of links in the articulation + + \param[in] userBuffer buffer into which to write an array of articulation link pointers + \param[in] bufferSize the size of the buffer. If this is not large enough to contain all the pointers to links, + only as many as will fit are written. + \param[in] startIndex Index of first link pointer to be retrieved + + \return the number of links written into the buffer. + + @see ArticulationLink + */ + + virtual PxU32 getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex = 0) const = 0; + + + /** + \brief Sets a name string for the object that can be retrieved with getName(). + + This is for debugging and is not used by the SDK. The string is not copied by the SDK, + only the pointer is stored. + + \param[in] name String to set the objects name to. + + @see getName() + */ + virtual void setName(const char* name) = 0; + + /** + \brief Retrieves the name string set with setName(). + + \return Name string associated with object. + + @see setName() + */ + virtual const char* getName() const = 0; + + /** + \brief Retrieves the axis aligned bounding box enclosing the articulation. + + \param[in] inflation Scale factor for computed world bounds. Box extents are multiplied by this value. + + \return The articulation's bounding box. + + @see PxBounds3 + */ + virtual PxBounds3 getWorldBounds(float inflation = 1.01f) const = 0; + + /** + \brief Retrieves the aggregate the articulation might be a part of. + + \return The aggregate the articulation is a part of, or NULL if the articulation does not belong to an aggregate. + + @see PxAggregate + */ + virtual PxAggregate* getAggregate() const = 0; + + virtual PxArticulationImpl* getImpl() = 0; + + virtual const PxArticulationImpl* getImpl() const = 0; + + virtual PxArticulationBase::Enum getType() const = 0; + + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. + + virtual ~PxArticulationBase() {} + + protected: + PX_INLINE PxArticulationBase(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationBase(PxBaseFlags baseFlags) : PxBase(baseFlags) {} +public: + virtual PxArticulationJointBase* createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame) = 0; + virtual void releaseArticulationJoint(PxArticulationJointBase* joint) = 0; + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + + /** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulationJoint.h b/src/PhysX/physx/include/PxArticulationJoint.h new file mode 100644 index 000000000..295c19fbe --- /dev/null +++ b/src/PhysX/physx/include/PxArticulationJoint.h @@ -0,0 +1,572 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION_JOINT +#define PX_PHYSICS_NX_ARTICULATION_JOINT +/** \addtogroup physics +@{ */ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxArticulationJointImpl; + +/** +\brief The type of joint drive to use for the articulation joint. + +Two drive models are currently supported. in the TARGET model, the drive spring displacement will be determined +as the rotation vector from the relative quaternion beetween child and parent, and the target quaternion. + +In the ERROR model, the drive spring displacement will be taken directly from the imaginary part of the relative +quaternion. This drive model requires more computation on the part of the application, but allows driving the joint +with a spring displacement that is more than a complete rotation. + +@see PxArticulationJoint +*/ + +struct PxArticulationJointDriveType +{ + enum Enum + { + eTARGET = 0, // use the quaternion as the drive target + eERROR = 1 // use the vector part of the quaternion as the drive error. + }; +}; + +struct PxArticulationAxis +{ + enum Enum + { + eTWIST = 0, + eSWING1 = 1, + eSWING2 = 2, + eX = 3, + eY = 4, + eZ = 5, + eCOUNT = 6 + }; +}; + +PX_FLAGS_OPERATORS(PxArticulationAxis::Enum, PxU8) + +struct PxArticulationMotion +{ + enum Enum + { + eLOCKED = 0, + eLIMITED = 1, + eFREE = 2 + }; +}; + +typedef PxFlags PxArticulationMotions; +PX_FLAGS_OPERATORS(PxArticulationMotion::Enum, PxU8) + +struct PxArticulationJointType +{ + enum Enum + { + ePRISMATIC = 0, + eREVOLUTE = 1, + eSPHERICAL = 2, + eFIX = 3, + eUNDEFINED = 4 + }; +}; + + +class PxArticulationJointBase : public PxBase +{ +public: + /** + \brief get the parent articulation link to which this articulation joint belongs + + \return the articulation link to which this joint belongs + */ + virtual PxArticulationLink& getParentArticulationLink() const = 0; + + /** + \brief set the joint pose in the parent frame + + \param[in] pose the joint pose in the parent frame + Default: the identity matrix + + @see getParentPose() + */ + + virtual void setParentPose(const PxTransform& pose) = 0; + + /** + \brief get the joint pose in the parent frame + + \return the joint pose in the parent frame + + @see setParentPose() + */ + + virtual PxTransform getParentPose() const = 0; + + /** + \brief get the child articulation link to which this articulation joint belongs + + \return the articulation link to which this joint belongs + */ + virtual PxArticulationLink& getChildArticulationLink() const = 0; + + + /** + \brief set the joint pose in the child frame + + \param[in] pose the joint pose in the child frame + Default: the identity matrix + + @see getChildPose() + */ + + virtual void setChildPose(const PxTransform& pose) = 0; + + /** + \brief get the joint pose in the child frame + + \return the joint pose in the child frame + + @see setChildPose() + */ + virtual PxTransform getChildPose() const = 0; + + virtual PxArticulationJointImpl* getImpl() = 0; + virtual const PxArticulationJointImpl* getImpl() const = 0; + + virtual ~PxArticulationJointBase() {} + +private: +protected: + PX_INLINE PxArticulationJointBase(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationJointBase(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationJointBase", name) || PxBase::isKindOf(name); } +}; + + + +/** +\brief a joint between two links in an articulation. + +The joint model is very similar to a PxSphericalJoint with swing and twist limits, +and an implicit drive model. + +@see PxArticulation PxArticulationLink +*/ + +class PxArticulationJoint : public PxArticulationJointBase +{ +public: + + /** + \brief set the target drive + + This is the target position for the joint drive, measured in the parent constraint frame. + + \param[in] orientation the target orientation for the joint + Range: a unit quaternion + Default: the identity quaternion + + @see getTargetOrientation() + */ + + virtual void setTargetOrientation(const PxQuat& orientation) = 0; + + /** + \brief get the target drive position + + \return the joint drive target position + + @see setTargetOrientation() + */ + virtual PxQuat getTargetOrientation() const = 0; + + /** + \brief set the target drive velocity + + This is the target velocity for the joint drive, measured in the parent constraint frame + + \param[in] velocity the target velocity for the joint + Default: the zero vector + + @see getTargetVelocity() + */ + virtual void setTargetVelocity(const PxVec3& velocity) = 0; + + /** + \brief get the target drive velocity + + \return the target velocity for the joint + + @see setTargetVelocity() + */ + virtual PxVec3 getTargetVelocity() const = 0; + + + /** + \brief set the drive type + + \param[in] driveType the drive type for the joint + Default: PxArticulationJointDriveType::eTARGET + + @see getDriveType() + */ + virtual void setDriveType(PxArticulationJointDriveType::Enum driveType) = 0; + + /** + \brief get the drive type + + \return the drive type + + @see setDriveType() + */ + virtual PxArticulationJointDriveType::Enum + getDriveType() const = 0; + + + + /** + \brief set the drive strength of the joint acceleration spring. + + The acceleration generated by the spring drive is proportional to + this value and the angle between the drive target position and the + current position. + + \param[in] spring the spring strength of the joint + Range: [0, PX_MAX_F32)
+ Default: 0.0 + + @see getStiffness() + */ + virtual void setStiffness(PxReal spring) = 0; + + /** + \brief get the drive strength of the joint acceleration spring + + \return the spring strength of the joint + + @see setStiffness() + */ + virtual PxReal getStiffness() const = 0; + + + /** + \brief set the damping of the joint acceleration spring + + The acceleration generated by the spring drive is proportional to + this value and the difference between the angular velocity of the + joint and the target drive velocity. + + \param[in] damping the damping of the joint drive + Range: [0, PX_MAX_F32)
+ Default: 0.0 + + @see getDamping() + */ + virtual void setDamping(PxReal damping) = 0; + + /** + \brief get the damping of the joint acceleration spring + + @see setDamping() + */ + + virtual PxReal getDamping() const = 0; + + /** + \brief set the internal compliance + + Compliance determines the extent to which the joint resists acceleration. + + There are separate values for resistance to accelerations caused by external + forces such as gravity and contact forces, and internal forces generated from + other joints. + + A low compliance means that forces have little effect, a compliance of 1 means + the joint does not resist such forces at all. + + \param[in] compliance the compliance to internal forces + Range: (0, 1] + Default: 0.0 + + @see getInternalCompliance() + */ + + virtual void setInternalCompliance(PxReal compliance) = 0; + + + /** + \brief get the internal compliance + + \return the compliance to internal forces + + @see setInternalCompliance() + */ + virtual PxReal getInternalCompliance() const = 0; + + /** + \brief get the drive external compliance + + Compliance determines the extent to which the joint resists acceleration. + + There are separate values for resistance to accelerations caused by external + forces such as gravity and contact forces, and internal forces generated from + other joints. + + A low compliance means that forces have little effect, a compliance of 1 means + the joint does not resist such forces at all. + + \param[in] compliance the compliance to external forces + Range: (0, 1] + Default: 0.0 + + @see getExternalCompliance() + */ + + virtual void setExternalCompliance(PxReal compliance) = 0; + + /** + \brief get the drive external compliance + + \return the compliance to external forces + + @see setExternalCompliance() + */ + virtual PxReal getExternalCompliance() const = 0; + + + + /** + \brief set the extents of the cone limit. The extents are measured in the frame + of the parent. + + Note that very small or highly elliptical limit cones may result in jitter. + + \param[in] zLimit the allowed extent of rotation around the z-axis + \param[in] yLimit the allowed extent of rotation around the y-axis + Range: ( (0, Pi), (0, Pi) ) + Default: (Pi/4, Pi/4) + + \note Please note the order of zLimit and yLimit. + */ + virtual void setSwingLimit(PxReal zLimit, PxReal yLimit) = 0; + + + /** + \brief get the extents for the swing limit cone + + \param[out] zLimit the allowed extent of rotation around the z-axis + \param[out] yLimit the allowed extent of rotation around the y-axis + + \note Please note the order of zLimit and yLimit. + + @see setSwingLimit() + */ + virtual void getSwingLimit(PxReal& zLimit, PxReal& yLimit) const = 0; + + + + /** + \brief set the tangential spring for the limit cone + Range: ([0, PX_MAX_F32), [0, PX_MAX_F32)) + Default: (0.0, 0.0) + */ + + virtual void setTangentialStiffness(PxReal spring) = 0; + + + /** + \brief get the tangential spring for the swing limit cone + + \return the tangential spring + + @see setTangentialStiffness() + */ + virtual PxReal getTangentialStiffness() const = 0; + + + /** + \brief set the tangential damping for the limit cone + Range: ([0, PX_MAX_F32), [0, PX_MAX_F32)) + Default: (0.0, 0.0) + */ + + virtual void setTangentialDamping(PxReal damping) = 0; + + + /** + \brief get the tangential damping for the swing limit cone + + \return the tangential damping + + @see setTangentialDamping() + */ + virtual PxReal getTangentialDamping() const = 0; + + + /** + \brief set the contact distance for the swing limit + + The contact distance should be less than either limit angle. + + Range: [0, Pi] + Default: 0.05 radians + + @see getSwingLimitContactDistance() + */ + + virtual void setSwingLimitContactDistance(PxReal contactDistance) = 0; + + + /** + \brief get the contact distance for the swing limit + + \return the contact distance for the swing limit cone + + @see setSwingLimitContactDistance() + */ + virtual PxReal getSwingLimitContactDistance() const = 0; + + + + /** + \brief set the flag which enables the swing limit + + \param[in] enabled whether the limit is enabled + Default: false + + @see getSwingLimitEnabled() + */ + virtual void setSwingLimitEnabled(bool enabled) = 0; + + /** + \brief get the flag which enables the swing limit + + \return whether the swing limit is enabled + + @see setSwingLimitEnabled() + */ + + virtual bool getSwingLimitEnabled() const = 0; + + + /** + \brief set the bounds of the twistLimit + + \param[in] lower the lower extent of the twist limit + \param[in] upper the upper extent of the twist limit + Range: (-Pi, Pi) + Default: (-Pi/4, Pi/4) + + The lower limit value must be less than the upper limit if the limit is enabled + + @see getTwistLimit() + */ + virtual void setTwistLimit(PxReal lower, PxReal upper) = 0; + + /** + \brief get the bounds of the twistLimit + + \param[out] lower the lower extent of the twist limit + \param[out] upper the upper extent of the twist limit + + @see setTwistLimit() + */ + + virtual void getTwistLimit(PxReal &lower, PxReal &upper) const = 0; + + /** + \brief set the flag which enables the twist limit + + \param[in] enabled whether the twist limit is enabled + Default: false + + @see getTwistLimitEnabled() + */ + virtual void setTwistLimitEnabled(bool enabled) = 0; + + /** + \brief get the twistLimitEnabled flag + + \return whether the twist limit is enabled + + @see setTwistLimitEnabled() + */ + + virtual bool getTwistLimitEnabled() const = 0; + + + /** + \brief set the contact distance for the swing limit + + The contact distance should be less than half the distance between the upper and lower limits. + + Range: [0, Pi) + Default: 0.05 radians + + @see getTwistLimitContactDistance() + */ + + virtual void setTwistLimitContactDistance(PxReal contactDistance) = 0; + + + /** + \brief get the contact distance for the swing limit + + \return the contact distance for the twist limit + + @see setTwistLimitContactDistance() + */ + virtual PxReal getTwistLimitContactDistance() const = 0; + + virtual const char* getConcreteTypeName() const { return "PxArticulationJoint"; } + +protected: + PX_INLINE PxArticulationJoint(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationJointBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationJoint(PxBaseFlags baseFlags) : PxArticulationJointBase(baseFlags) {} + virtual ~PxArticulationJoint() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationJoint", name) || PxArticulationJointBase::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h b/src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h new file mode 100644 index 000000000..2397da8d5 --- /dev/null +++ b/src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION_JOINT_RC +#define PX_PHYSICS_NX_ARTICULATION_JOINT_RC +/** \addtogroup physics +@{ */ + +#if 1 +#include "PxPhysXConfig.h" +#include "common/PxBase.h" +#include "PxArticulationJoint.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief a joint between two links in an articulation. + + The joint model is very similar to a PxSphericalJoint with swing and twist limits, + and an implicit drive model. + + @see PxArticulation PxArticulationLink + */ + + class PxArticulationJointReducedCoordinate : public PxArticulationJointBase + { + public: + + virtual void setJointType(PxArticulationJointType::Enum jointType) = 0; + virtual PxArticulationJointType::Enum getJointType() const = 0; + + virtual void setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) = 0; + virtual PxArticulationMotion::Enum getMotion(PxArticulationAxis::Enum axis) const = 0; + + virtual void setLimit(PxArticulationAxis::Enum axis, const PxReal lowLimit, const PxReal highLimit) = 0; + virtual void getLimit(PxArticulationAxis::Enum axis, PxReal& lowLimit, PxReal& highLimit) = 0; + virtual void setDrive(PxArticulationAxis::Enum axis, const PxReal stiffness, const PxReal damping, const PxReal maxForce, bool isAccelerationDrive = false) = 0; + virtual void getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration) = 0; + virtual void setDriveTarget(PxArticulationAxis::Enum axis, const PxReal target) = 0; + virtual void setDriveVelocity(PxArticulationAxis::Enum axis, const PxReal targetVel) = 0; + virtual PxReal getDriveTarget(PxArticulationAxis::Enum axis) = 0; + virtual PxReal getDriveVelocity(PxArticulationAxis::Enum axis) = 0; + + virtual void setFrictionCoefficient(const PxReal coefficient) = 0; + virtual PxReal getFrictionCoefficient() const = 0; + virtual const char* getConcreteTypeName() const { return "PxArticulationJointReducedCoordinate"; } + + virtual void setMaxJointVelocity(const PxReal maxJointV) = 0; + virtual PxReal getMaxJointVelocity() const = 0; + + protected: + PX_INLINE PxArticulationJointReducedCoordinate(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationJointBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationJointReducedCoordinate(PxBaseFlags baseFlags) : PxArticulationJointBase(baseFlags) {} + virtual ~PxArticulationJointReducedCoordinate() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationJointReducedCoordinate", name) || PxBase::isKindOf(name); } + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif + + /** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulationLink.h b/src/PhysX/physx/include/PxArticulationLink.h new file mode 100644 index 000000000..9b3faae68 --- /dev/null +++ b/src/PhysX/physx/include/PxArticulationLink.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION_LINK +#define PX_PHYSICS_NX_ARTICULATION_LINK +/** \addtogroup physics +@{ */ + +#include "PxPhysXConfig.h" +#include "PxArticulationJoint.h" +#include "PxRigidBody.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxArticulationBase; + +/** +\brief a component of an articulation that represents a rigid body + +A limited subset of the properties of PxRigidDynamic are supported. In particular, sleep properties +are attributes of the articulation rather than each individual body, damping and velocity limits +are not supported, and links may not be kinematic. + +@see PxArticulation PxArticulation.createLink PxArticulationJoint PxRigidBody +*/ + +class PxArticulationLink : public PxRigidBody +{ +public: + /** + \brief Deletes the articulation link. + + \note Only a leaf articulation link can be released + + Do not keep a reference to the deleted instance. + + @see PxArticulation::createLink() + */ + virtual void release() = 0; + + + /** + \brief get the articulation to which this articulation link belongs. This returns the base class. The application should + establish which articulation implementation this actually is and upcast to that type to access non-common functionality + + \return the articulation to which this link belongs + */ + virtual PxArticulationBase& getArticulation() const = 0; + + + /** + \brief Get the joint which connects this link to its parent. + + \return The joint connecting the link to the parent. NULL for the root link. + + @see PxArticulationJoint + */ + virtual PxArticulationJointBase* getInboundJoint() const = 0; + + /** + \brief Get the degree of freedom of the joint which connects this link to its parent. + + \return The degree of freeedom of the joint connecting the link to the parent. 0xffffffff for the root link. + + @see PxArticulationJoint + */ + virtual PxU32 getInboundJointDof() const = 0; + + /** + \brief Get number of child links. + + \return the number of child links + + @see getChildren() + */ + virtual PxU32 getNbChildren() const = 0; + + + /** + \brief Get low-level link index + + \return low-level index + */ + virtual PxU32 getLinkIndex() const = 0; + + /** + \brief Retrieve all the child links. + + \param[out] userBuffer The buffer to receive articulation link pointers. + \param[in] bufferSize Size of provided user buffer. + \return Number of articulation links written to the buffer. + \param[in] startIndex Index of first child pointer to be retrieved + + @see getNbChildren() + */ + virtual PxU32 getChildren(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + virtual const char* getConcreteTypeName() const { return "PxArticulationLink"; } + +protected: + PX_INLINE PxArticulationLink(PxType concreteType, PxBaseFlags baseFlags) : PxRigidBody(concreteType, baseFlags) {} + PX_INLINE PxArticulationLink(PxBaseFlags baseFlags) : PxRigidBody(baseFlags) {} + virtual ~PxArticulationLink() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationLink", name) || PxRigidBody::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulationReducedCoordinate.h b/src/PhysX/physx/include/PxArticulationReducedCoordinate.h new file mode 100644 index 000000000..6314c0953 --- /dev/null +++ b/src/PhysX/physx/include/PxArticulationReducedCoordinate.h @@ -0,0 +1,409 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION_RC +#define PX_PHYSICS_NX_ARTICULATION_RC +/** \addtogroup physics +@{ */ + +#include "PxArticulationBase.h" +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + namespace Cm + { + class SpatialVector; + } + + class PxContactJoint; + + struct PxArticulationFlag + { + enum Enum + { + eFIX_BASE = (1 << 1) + }; + }; + + + class PxJoint; + + typedef PxFlags PxArticulationFlags; + PX_FLAGS_OPERATORS(PxArticulationFlag::Enum, PxU8) + + //PxKinematicJacobian is in world space 6x6 matrix + class PxKinematicJacobian + { + public: + //in each single column, top part is angular term and bottom is linear term + PxReal j[6][6];//[column][row] + + PxU32 nbColumns; + }; + + struct PxArticulationRootLinkData + { + + PxTransform transform; + PxVec3 linVel; + PxVec3 angVel; + PxVec3 linAcel; + PxVec3 angAcel; + }; + + class PxArticulationCache + { + public: + enum Enum + { + eVELOCITY = (1 << 0), //!< The joint velocities this frame. Note, this is the accumulated joint velocities, not change in joint velocity. + eACCELERATION = (1 << 1), //!< The joint accelerations this frame. Delta velocity can be computed from acceleration * dt. + ePOSITION = (1 << 2), //!< The joint positions this frame. Note, this is the accumulated joint positions over frames, not change in joint position. + eFORCE = (1 << 3), //!< The joint forces this frame. Note, the application should provide these values for the forward dynamic. If the application is using inverse dynamic, this is the joint force returned. + eROOT = (1 << 4), //!< Root link transform, velocity and acceleration. Note, when the application call applyCache with eROOT flag, it won't apply root link's acceleration to the simulation + eALL = (eVELOCITY | eACCELERATION | ePOSITION| eROOT) + }; + PxArticulationCache() : coefficentMatrix(NULL), lambda(NULL) + {} + + Cm::SpatialVector* externalForces; // N total number of links + PxKinematicJacobian* jacobian; //this store jacobian matrix + PxReal* massMatrix; //N X N (dof X dof) + PxReal* jointVelocity; //N total Dofs + PxReal* jointAcceleration; //N total Dofs + PxReal* jointPosition; //N total Dofs + PxReal* jointForce; //N total Dofs + + //application need to allocate those memory and assign them to the cache + PxReal* coefficentMatrix; + PxReal* lambda; + + //root link data + PxArticulationRootLinkData rootLinkData; + + //These three members won't be set to zero when zeroCache get called + void* scratchMemory; //this is used for internal calculation + void* scratchAllocator; + PxU32 version; //cache version. If the articulation configulation change, the cache is invalid + + + }; + + typedef PxFlags PxArticulationCacheFlags; + PX_FLAGS_OPERATORS(PxArticulationCache::Enum, PxU8) + + + /** + \brief a tree structure of bodies connected by joints that is treated as a unit by the dynamics solver + + Articulations are more expensive to simulate than the equivalent collection of + PxRigidDynamic and PxJoint structures, but because the dynamics solver treats + each articulation as a single object, they are much less prone to separation and + have better support for actuation. An articulation may have at most 64 links. + + @see PxArticulationJoint PxArticulationLink PxPhysics.createArticulation + */ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4435) +#endif + + class PxArticulationReducedCoordinate : public PxArticulationBase + { + public: + + virtual void release() = 0; + + + /** + \brief Sets flags on the articulation + + \param[in] flags Articulation flags + + */ + virtual void setArticulationFlags(PxArticulationFlags flags) = 0; + + /** + \brief Raises or clears a flag on the articulation + + \param[in] flag The articulation flag + \param[in] value true/false indicating whether to raise or clear the flag + + */ + virtual void setArticulationFlag(PxArticulationFlag::Enum flag, bool value) = 0; + + /** + \brief return PxArticulationFlags + */ + virtual PxArticulationFlags getArticulationFlags() const = 0; + + /** + \brief returns the total Dofs of the articulation + */ + virtual PxU32 getDofs() const = 0; + + /** + \brief create an articulation cache + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + */ + virtual PxArticulationCache* createCache() const = 0; + + /** + \brief Get the size of the articulation cache + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + */ + virtual PxU32 getCacheDataSize() const = 0; + + /** + \brief zero all data in the articulation cache beside the cache version + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + */ + virtual void zeroCache(PxArticulationCache& cache) = 0; + + + /** + \brief apply the user defined data in the cache to the articulation system + + \param[in] cache articulation data. + \param[in] flag The mode to use when determine which value in the cache will be applied to the articulation + \param[in] autowake Specify if the call should wake up the articulation if it is currently asleep. If true and the current wake counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see createCache copyInternalStateToCache + */ + virtual void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake = true) = 0; + + /** + \brief copy the internal data of the articulation to the cache + + \param[in] cache articulation data + \param[in] flag this indicates what kind of data the articulation system need to copy to the cache + + @see createCache applyCache + */ + virtual void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const = 0; + + + /** + \brief release an articulation cache + + \param[in] cache the cache to release + + @see createCache applyCache copyInternalStateToCache + */ + virtual void releaseCache(PxArticulationCache& cache) const = 0; + + + /** + \brief reduce the maximum data format to the reduced internal data + \param[in] maximum joint data format + \param[out] reduced joint data format + */ + virtual void packJointData(const PxReal* maximum, PxReal* reduced) const = 0; + + /** + \brief turn the reduced internal data to maximum joint data format + \param[in] reduced joint data format + \param[out] maximum joint data format + */ + virtual void unpackJointData(const PxReal* reduced, PxReal* maximum) const = 0; + + /** + \brief initialize all the common data for inverse dynamic + */ + virtual void commonInit() const = 0; + + + /** + \brief determine the statically balance of the joint force of gravity for entire articulation. External force, joint velocity and joint acceleration + are set to zero, the joint force returned will be purely determined by gravity. + + \param[out] cache return joint forces which can counteract gravity force + + @see commonInit + */ + virtual void computeGeneralizedGravityForce(PxArticulationCache& cache) const = 0; + + /** + \brief determine coriolise and centrifugal force. External force, gravity and joint acceleration + are set to zero, the joint force return will be coriolise and centrifugal force for each joint. + + \param[in] cache data + + @see commonInit + */ + virtual void computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const = 0; + + /** + \brief determine joint force change caused by external force. Gravity, joint acceleration and joint velocity + are all set to zero. + + \param[in] cache data + + @see commonInit + */ + virtual void computeGeneralizedExternalForce(PxArticulationCache& cache) const = 0; + /** + \brief determine the joint acceleration for each joint + This is purely calculates the change in joint acceleration due to change in the joint force + + \param[in] cache articulation data + + @see commonInit + */ + virtual void computeJointAcceleration(PxArticulationCache& cache) const = 0; + + + /** + \brief determine the joint force + This is purely calculates the change in joint force due to change in the joint acceleration + This means gravity and joint velocity will be zero + + \param[in] cache return joint force + + @see commonInit + */ + virtual void computeJointForce(PxArticulationCache& cache) const = 0; + + /** + \brief compute the kinematic jacobian for each joint from end effector to the root in world space + \param[in] linkID is the end effector id + \param[in] cache return jacobian matrix + + @see commonInit + */ + virtual void computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const = 0; + + + /** + \brief compute the coefficent matrix for contact force. PxContactJoint is the contact point + \param[out] cache returs the coefficent matrix. Each column is the joint force effected by a contact based on impulse strength 1 + @see commonInit + */ + virtual void computeCoefficentMatrix(PxArticulationCache& cache) const = 0; + + + /** + \brief compute the lambda value when the test impulse is 1 + \param[in] initialState the initial state of the articulation system + \param[in] jointTorque M(q)*qddot + C(q,qdot) + g(q) + \param[in] maxIter maximum number of solver iterations to run. If the system converges, fewer iterations may be used. + \param[out] cache returns the coefficent matrix. Each column is the joint force effected by a contact based on impulse strength 1 + @see commonInit + */ + virtual bool computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxU32 maxIter) const = 0; + + /** + \brief compute the joint-space inertia matrix + \param[in] cache articulation data + + @see commonInit + */ + virtual void computeGeneralizedMassMatrix(PxArticulationCache& cache) const = 0; + + /** + \brief add loop joint to the articulation system for inverse dynamic + \param[in] joint required to add loop joint + + @see commonInit + */ + virtual void addLoopJoint(PxJoint* joint) = 0; + + /** + \brief remove loop joint from the articulation system + \param[in] joint required to remove loop joint + + @see commonInit + */ + virtual void removeLoopJoint(PxJoint* joint) = 0; + + /** + \brief returns the number of loop joints in the articulation + \return number of loop joints + */ + + virtual PxU32 getNbLoopJoints() const = 0; + + /** + \brief returns the set of loop constraints in the articulation + + \param[in] userBuffer buffer into which to write an array of constraints pointers + \param[in] bufferSize the size of the buffer. If this is not large enough to contain all the pointers to links, + only as many as will fit are written. + \param[in] startIndex Index of first link pointer to be retrieved + + \return the number of links written into the buffer. + + @see ArticulationLink + */ + + virtual PxU32 getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex = 0) const = 0; + + /** + \brief returns the required size of coeffient matrix in the articulation. The coefficient matrix is number of constraint(loop joints) by total dofs. Constraint Torque = transpose(K) * lambda(). Lambda is a vector of number of constraints + \return bite size of the coefficient matrix(nc * n) + */ + + virtual PxU32 getCoefficentMatrixSize() const = 0; + + /** + \brief teleport root link to a new location + \param[in] pose the new location of the root link + \param[in] autowake wake up the articulation system + + @see commonInit + */ + virtual void teleportRootLink(const PxTransform& pose, bool autowake) = 0; + + protected: + PX_INLINE PxArticulationReducedCoordinate(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationReducedCoordinate(PxBaseFlags baseFlags) : PxArticulationBase(baseFlags) {} + virtual ~PxArticulationReducedCoordinate() {} + }; + +#if PX_VC +#pragma warning(pop) +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + + + /** @} */ +#endif diff --git a/src/PhysX/physx/include/PxBatchQuery.h b/src/PhysX/physx/include/PxBatchQuery.h new file mode 100644 index 000000000..63f1e7209 --- /dev/null +++ b/src/PhysX/physx/include/PxBatchQuery.h @@ -0,0 +1,224 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENEQUERY +#define PX_PHYSICS_NX_SCENEQUERY +/** \addtogroup scenequery +@{ */ + +#include "PxPhysXConfig.h" +#include "PxShape.h" +#include "PxBatchQueryDesc.h" +#include "PxQueryFiltering.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxBoxGeometry; +class PxSphereGeometry; +struct PxQueryCache; + +/** +\brief Batched queries object. This is used to perform several queries at the same time. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 + +@see PxScene, PxScene.createBatchQuery +*/ +class PX_DEPRECATED PxBatchQuery +{ + public: + + /** + \brief Executes batched queries. + */ + virtual void execute() = 0; + + /** + \brief Gets the prefilter shader in use for this scene query. + + \return Prefilter shader. + + @see PxBatchQueryDesc.preFilterShade PxBatchQueryPreFilterShader + */ + virtual PxBatchQueryPreFilterShader getPreFilterShader() const = 0; + + /** + \brief Gets the postfilter shader in use for this scene query. + + \return Postfilter shader. + + @see PxBatchQueryDesc.preFilterShade PxBatchQueryPostFilterShader + */ + virtual PxBatchQueryPostFilterShader getPostFilterShader() const = 0; + + + /** + \brief Gets the shared global filter data in use for this scene query. + + \return Shared filter data for filter shader. + + @see getFilterShaderDataSize() PxBatchQueryDesc.filterShaderData PxBatchQueryPreFilterShader, PxBatchQueryPostFilterShader + */ + virtual const void* getFilterShaderData() const = 0; + + /** + \brief Gets the size of the shared global filter data (#PxSceneDesc.filterShaderData) + + \return Size of shared filter data [bytes]. + + @see getFilterShaderData() PxBatchQueryDesc.filterShaderDataSize PxBatchQueryPreFilterShader, PxBatchQueryPostFilterShader + */ + virtual PxU32 getFilterShaderDataSize() const = 0; + + /** + \brief Sets new user memory pointers. + + It is not possible to change the memory during query execute. + + @see PxBatchQueryDesc + */ + virtual void setUserMemory(const PxBatchQueryMemory&) = 0; + + /** + \brief Gets the user memory pointers. + + @see PxBatchQueryDesc + */ + virtual const PxBatchQueryMemory& getUserMemory() = 0; + + /** + \brief Releases PxBatchQuery from PxScene + + @see PxScene, PxScene.createBatchQuery + */ + virtual void release() = 0; + + /** + \brief Performs a raycast against objects in the scene, returns results in PxBatchQueryMemory::userRaycastResultBuffer + specified at PxBatchQuery creation time or via PxBatchQuery::setUserMemory call. + + \note Touching hits are not ordered. + \note Shooting a ray from within an object leads to different results depending on the shape type. Please check the details in article SceneQuery. User can ignore such objects by using one of the provided filter mechanisms. + + \param[in] origin Origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] distance Length of the ray. Needs to be larger than 0. + \param[in] maxTouchHits Maximum number of hits to record in the touch buffer for this query. Default=0 reports a single blocking hit. If maxTouchHits is set to 0 all hits are treated as blocking by default. + \param[in] hitFlags Specifies which properties per hit should be computed and returned in hit array and blocking hit. + \param[in] filterData Filtering data passed to the filer shader. See #PxQueryFilterData #PxBatchQueryPreFilterShader, #PxBatchQueryPostFilterShader + \param[in] userData User can pass any value in this argument, usually to identify this particular query + \param[in] cache Cached hit shape (optional). Query is tested against cached shape first. If no hit is found the ray gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + + \note This query call writes to a list associated with the query object and is NOT thread safe (for performance reasons there is no lock + and overlapping writes from different threads may result in undefined behavior). + + @see PxQueryFilterData PxBatchQueryPreFilterShader PxBatchQueryPostFilterShader PxRaycastHit PxScene::raycast + */ + virtual void raycast( + const PxVec3& origin, const PxVec3& unitDir, PxReal distance = PX_MAX_F32, PxU16 maxTouchHits = 0, + PxHitFlags hitFlags = PxHitFlag::eDEFAULT, + const PxQueryFilterData& filterData = PxQueryFilterData(), + void* userData = NULL, const PxQueryCache* cache = NULL) = 0; + + + /** + \brief Performs an overlap test of a given geometry against objects in the scene, returns results in PxBatchQueryMemory::userOverlapResultBuffer + specified at PxBatchQuery creation time or via PxBatchQuery::setUserMemory call. + + \note Filtering: returning eBLOCK from user filter for overlap queries will cause a warning (see #PxQueryHitType). + + \param[in] geometry Geometry of object to check for overlap (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the object. + \param[in] maxTouchHits Maximum number of hits to record in the touch buffer for this query. Default=0 reports a single blocking hit. If maxTouchHits is set to 0 all hits are treated as blocking by default. + \param[in] filterData Filtering data and simple logic. See #PxQueryFilterData #PxBatchQueryPreFilterShader, #PxBatchQueryPostFilterShader + \param[in] userData User can pass any value in this argument, usually to identify this particular query + \param[in] cache Cached hit shape (optional). Query is tested against cached shape first. If no hit is found the ray gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + + \note eBLOCK should not be returned from user filters for overlap(). Doing so will result in undefined behavior, and a warning will be issued. + \note If the PxQueryFlag::eNO_BLOCK flag is set, the eBLOCK will instead be automatically converted to an eTOUCH and the warning suppressed. + \note This query call writes to a list associated with the query object and is NOT thread safe (for performance reasons there is no lock + and overlapping writes from different threads may result in undefined behavior). + + @see PxQueryFilterData PxBatchQueryPreFilterShader PxBatchQueryPostFilterShader + */ + virtual void overlap( + const PxGeometry& geometry, const PxTransform& pose, PxU16 maxTouchHits = 0, + const PxQueryFilterData& filterData = PxQueryFilterData(), void* userData=NULL, const PxQueryCache* cache = NULL) = 0; + + /** + \brief Performs a sweep test against objects in the scene, returns results in PxBatchQueryMemory::userSweepResultBuffer + specified at PxBatchQuery creation time or via PxBatchQuery::setUserMemory call. + + \note Touching hits are not ordered. + \note If a shape from the scene is already overlapping with the query shape in its starting position, + the hit is returned unless eASSUME_NO_INITIAL_OVERLAP was specified. + + \param[in] geometry Geometry of object to sweep (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the sweep object. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. Will be clamped to PX_MAX_SWEEP_DISTANCE. + \param[in] maxTouchHits Maximum number of hits to record in the touch buffer for this query. Default=0 reports a single blocking hit. If maxTouchHits is set to 0 all hits are treated as blocking by default. + \param[in] hitFlags Specifies which properties per hit should be computed and returned in hit array and blocking hit. + \param[in] filterData Filtering data and simple logic. See #PxQueryFilterData #PxBatchQueryPreFilterShader, #PxBatchQueryPostFilterShader + \param[in] userData User can pass any value in this argument, usually to identify this particular query + \param[in] cache Cached hit shape (optional). Query is tested against cached shape first. If no hit is found the ray gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + Note: ePRECISE_SWEEP doesn't support inflation. Therefore the sweep will be performed with zero inflation. + + \note This query call writes to a list associated with the query object and is NOT thread safe (for performance reasons there is no lock + and overlapping writes from different threads may result in undefined behavior). + + @see PxHitFlags PxQueryFilterData PxBatchQueryPreFilterShader PxBatchQueryPostFilterShader PxSweepHit + */ + virtual void sweep( + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxU16 maxTouchHits = 0, PxHitFlags hitFlags = PxHitFlag::eDEFAULT, + const PxQueryFilterData& filterData = PxQueryFilterData(), void* userData=NULL, const PxQueryCache* cache = NULL, + const PxReal inflation = 0.f) = 0; + +protected: + virtual ~PxBatchQuery() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxBatchQueryDesc.h b/src/PhysX/physx/include/PxBatchQueryDesc.h new file mode 100644 index 000000000..185ca89a7 --- /dev/null +++ b/src/PhysX/physx/include/PxBatchQueryDesc.h @@ -0,0 +1,303 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENEQUERYDESC +#define PX_PHYSICS_NX_SCENEQUERYDESC +/** \addtogroup physics +@{ */ + +#include "PxPhysXConfig.h" +#include "PxClient.h" +#include "PxFiltering.h" +#include "PxQueryFiltering.h" +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxSweepHit; +struct PxRaycastHit; + +/** +\brief Batched query status. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 +*/ +struct PX_DEPRECATED PxBatchQueryStatus +{ + enum Enum + { + /** + \brief This is the initial state before a query starts. + */ + ePENDING = 0, + + /** + \brief The query is finished; results have been written into the result and hit buffers. + */ + eSUCCESS, + + /** + \brief The query results were incomplete due to touch hit buffer overflow. Blocking hit is still correct. + */ + eOVERFLOW + }; +}; + +/** +\brief Generic struct for receiving results of single query in a batch. Gets templated on hit type PxRaycastHit, PxSweepHit or PxOverlapHit. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 +*/ +template +struct PX_DEPRECATED PxBatchQueryResult +{ + HitType block; //!< Holds the closest blocking hit for a single query in a batch. Only valid if hasBlock is true. + HitType* touches; //!< This pointer will either be set to NULL for 0 nbTouches or will point + //!< into the user provided batch query results buffer specified in PxBatchQueryDesc. + PxU32 nbTouches; //!< Number of touching hits returned by this query, works in tandem with touches pointer. + void* userData; //!< Copy of the userData pointer specified in the corresponding query. + PxU8 queryStatus; //!< Takes on values from PxBatchQueryStatus::Enum. + bool hasBlock; //!< True if there was a blocking hit. + PxU16 pad; //!< pads the struct to 16 bytes. + + /** \brief Computes the number of any hits in this result, blocking or touching. */ + PX_INLINE PxU32 getNbAnyHits() const { return nbTouches + (hasBlock ? 1 : 0); } + + /** \brief Convenience iterator used to access any hits in this result, blocking or touching. */ + PX_INLINE const HitType& getAnyHit(const PxU32 index) const { PX_ASSERT(index < nbTouches + (hasBlock ? 1 : 0)); + return index < nbTouches ? touches[index] : block; } +}; + +/** \brief Convenience typedef for the result of a batched raycast query. */ +typedef PX_DEPRECATED PxBatchQueryResult PxRaycastQueryResult; + +/** \brief Convenience typedef for the result of a batched sweep query. */ +typedef PX_DEPRECATED PxBatchQueryResult PxSweepQueryResult; + +/** \brief Convenience typedef for the result of a batched overlap query. */ +typedef PX_DEPRECATED PxBatchQueryResult PxOverlapQueryResult; + +/** +\brief Struct for #PxBatchQuery memory pointers. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 + +@see PxBatchQuery PxBatchQueryDesc +*/ +struct PX_DEPRECATED PxBatchQueryMemory + { + /** + \brief The pointer to the user-allocated buffer for results of raycast queries in corresponding order of issue + + \note The size should be large enough to fit the number of expected raycast queries. + + @see PxRaycastQueryResult + */ + PxRaycastQueryResult* userRaycastResultBuffer; + + /** + \brief The pointer to the user-allocated buffer for raycast touch hits. + \note The size of this buffer should be large enough to store PxRaycastHit. + If the buffer is too small to store hits, the related PxRaycastQueryResult.queryStatus will be set to eOVERFLOW + + */ + PxRaycastHit* userRaycastTouchBuffer; + + /** + \brief The pointer to the user-allocated buffer for results of sweep queries in corresponding order of issue + + \note The size should be large enough to fit the number of expected sweep queries. + + @see PxRaycastQueryResult + */ + PxSweepQueryResult* userSweepResultBuffer; + + /** + \brief The pointer to the user-allocated buffer for sweep hits. + \note The size of this buffer should be large enough to store PxSweepHit. + If the buffer is too small to store hits, the related PxSweepQueryResult.queryStatus will be set to eOVERFLOW + + */ + PxSweepHit* userSweepTouchBuffer; + + /** + \brief The pointer to the user-allocated buffer for results of overlap queries in corresponding order of issue + + \note The size should be large enough to fit the number of expected overlap queries. + + @see PxRaycastQueryResult + */ + PxOverlapQueryResult* userOverlapResultBuffer; + + /** + \brief The pointer to the user-allocated buffer for overlap hits. + \note The size of this buffer should be large enough to store the hits returned. + If the buffer is too small to store hits, the related PxOverlapQueryResult.queryStatus will be set to eABORTED + + */ + PxOverlapHit* userOverlapTouchBuffer; + + /** \brief Capacity of the user-allocated userRaycastTouchBuffer in elements */ + PxU32 raycastTouchBufferSize; + + /** \brief Capacity of the user-allocated userSweepTouchBuffer in elements */ + PxU32 sweepTouchBufferSize; + + /** \brief Capacity of the user-allocated userOverlapTouchBuffer in elements */ + PxU32 overlapTouchBufferSize; + + /** \return Capacity of the user-allocated userRaycastResultBuffer in elements (max number of raycast() calls before execute() call) */ + PX_FORCE_INLINE PxU32 getMaxRaycastsPerExecute() const { return raycastResultBufferSize; } + + /** \return Capacity of the user-allocated userSweepResultBuffer in elements (max number of sweep() calls before execute() call) */ + PX_FORCE_INLINE PxU32 getMaxSweepsPerExecute() const { return sweepResultBufferSize; } + + /** \return Capacity of the user-allocated userOverlapResultBuffer in elements (max number of overlap() calls before execute() call) */ + PX_FORCE_INLINE PxU32 getMaxOverlapsPerExecute() const { return overlapResultBufferSize; } + + PxBatchQueryMemory(PxU32 raycastResultBufferSize_, PxU32 sweepResultBufferSize_, PxU32 overlapResultBufferSize_) : + userRaycastResultBuffer (NULL), + userRaycastTouchBuffer (NULL), + userSweepResultBuffer (NULL), + userSweepTouchBuffer (NULL), + userOverlapResultBuffer (NULL), + userOverlapTouchBuffer (NULL), + raycastTouchBufferSize (0), + sweepTouchBufferSize (0), + overlapTouchBufferSize (0), + raycastResultBufferSize (raycastResultBufferSize_), + sweepResultBufferSize (sweepResultBufferSize_), + overlapResultBufferSize (overlapResultBufferSize_) + { + } + +protected: + PxU32 raycastResultBufferSize; + PxU32 sweepResultBufferSize; + PxU32 overlapResultBufferSize; +}; + +/** +\brief Descriptor class for #PxBatchQuery. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 + +@see PxBatchQuery PxSceneQueryExecuteMode +*/ +class PX_DEPRECATED PxBatchQueryDesc +{ +public: + + /** + \brief Shared global filter data which will get passed into the filter shader. + + \note The provided data will get copied to internal buffers and this copy will be used for filtering calls. + + Default: NULL + + @see PxSimulationFilterShader + */ + void* filterShaderData; + + /** + \brief Size (in bytes) of the shared global filter data #filterShaderData. + + Default: 0 + + @see PxSimulationFilterShader filterShaderData + */ + PxU32 filterShaderDataSize; + + /** + \brief The custom preFilter shader to use for filtering. + + @see PxBatchQueryPreFilterShader PxDefaultPreFilterShader + */ + PxBatchQueryPreFilterShader preFilterShader; + + /** + \brief The custom postFilter shader to use for filtering. + + @see PxBatchQueryPostFilterShader PxDefaultPostFilterShader + */ + PxBatchQueryPostFilterShader postFilterShader; + + /** + \brief User memory buffers for the query. + + @see PxBatchQueryMemory + */ + PxBatchQueryMemory queryMemory; + + /** + \brief Construct a batch query with specified maximum number of queries per batch. + + If the number of raycasts/sweeps/overlaps per execute exceeds the limit, the query will be discarded with a warning. + + \param maxRaycastsPerExecute Maximum number of raycast() calls allowed before execute() call. + This has to match the amount of memory allocated for PxBatchQueryMemory::userRaycastResultBuffer. + \param maxSweepsPerExecute Maximum number of sweep() calls allowed before execute() call. + This has to match the amount of memory allocated for PxBatchQueryMemory::userSweepResultBuffer. + \param maxOverlapsPerExecute Maximum number of overlap() calls allowed before execute() call. + This has to match the amount of memory allocated for PxBatchQueryMemory::userOverlapResultBuffer. + */ + PX_INLINE PxBatchQueryDesc(PxU32 maxRaycastsPerExecute, PxU32 maxSweepsPerExecute, PxU32 maxOverlapsPerExecute); + PX_INLINE bool isValid() const; +}; + + +PX_INLINE PxBatchQueryDesc::PxBatchQueryDesc(PxU32 maxRaycastsPerExecute, PxU32 maxSweepsPerExecute, PxU32 maxOverlapsPerExecute) : + filterShaderData (NULL), + filterShaderDataSize (0), + preFilterShader (NULL), + postFilterShader (NULL), + queryMemory (maxRaycastsPerExecute, maxSweepsPerExecute, maxOverlapsPerExecute) +{ +} + + +PX_INLINE bool PxBatchQueryDesc::isValid() const +{ + if ( ((filterShaderDataSize == 0) && (filterShaderData != NULL)) || + ((filterShaderDataSize > 0) && (filterShaderData == NULL)) ) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxBroadPhase.h b/src/PhysX/physx/include/PxBroadPhase.h new file mode 100644 index 000000000..39d3c4305 --- /dev/null +++ b/src/PhysX/physx/include/PxBroadPhase.h @@ -0,0 +1,174 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_BROAD_PHASE_H +#define PX_PHYSICS_BROAD_PHASE_H +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxBounds3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxActor; + + /** + \brief Broad phase algorithm used in the simulation + + eSAP is a good generic choice with great performance when many objects are sleeping. Performance + can degrade significantly though, when all objects are moving, or when large numbers of objects + are added to or removed from the broad phase. This algorithm does not need world bounds to be + defined in order to work. + + eMBP is an alternative broad phase algorithm that does not suffer from the same performance + issues as eSAP when all objects are moving or when inserting large numbers of objects. However + its generic performance when many objects are sleeping might be inferior to eSAP, and it requires + users to define world bounds in order to work. + + eABP is a revisited implementation of MBP, which automatically manages broad-phase regions. + It offers the convenience of eSAP (no need to define world bounds or regions) and the performance + of eMBP when a lot of objects are moving. While eSAP can remain faster when most objects are + sleeping and eMBP can remain faster when it uses a large number of properly-defined regions, + eABP often gives the best performance on average and the best memory usage. + */ + struct PxBroadPhaseType + { + enum Enum + { + eSAP, //!< 3-axes sweep-and-prune + eMBP, //!< Multi box pruning + eABP, //!< Automatic box pruning + eGPU, + + eLAST + }; + }; + + /** + \brief Broad-phase callback to receive broad-phase related events. + + Each broadphase callback object is associated with a PxClientID. It is possible to register different + callbacks for different clients. The callback functions are called this way: + - for shapes/actors, the callback assigned to the actors' clients are used + - for aggregates, the callbacks assigned to clients from aggregated actors are used + + \note SDK state should not be modified from within the callbacks. In particular objects should not + be created or destroyed. If state modification is needed then the changes should be stored to a buffer + and performed after the simulation step. + + Threading: It is not necessary to make this class thread safe as it will only be called in the context of the + user thread. + + @see PxSceneDesc PxScene.setBroadPhaseCallback() PxScene.getBroadPhaseCallback() + */ + class PxBroadPhaseCallback + { + public: + virtual ~PxBroadPhaseCallback() {} + + /** + \brief Out-of-bounds notification. + + This function is called when an object leaves the broad-phase. + + \param[in] shape Shape that left the broad-phase bounds + \param[in] actor Owner actor + */ + virtual void onObjectOutOfBounds(PxShape& shape, PxActor& actor) = 0; + + /** + \brief Out-of-bounds notification. + + This function is called when an aggregate leaves the broad-phase. + + \param[in] aggregate Aggregate that left the broad-phase bounds + */ + virtual void onObjectOutOfBounds(PxAggregate& aggregate) = 0; + }; + + /** + \brief "Region of interest" for the broad-phase. + + This is currently only used for the PxBroadPhaseType::eMBP broad-phase, which requires zones or regions to be defined + when the simulation starts in order to work. Regions can overlap and be added or removed at runtime, but at least one + region needs to be defined when the scene is created. + + If objects that do no overlap any region are inserted into the scene, they will not be added to the broad-phase and + thus collisions will be disabled for them. A PxBroadPhaseCallback out-of-bounds notification will be sent for each one + of those objects. + + The total number of regions is limited by PxBroadPhaseCaps::maxNbRegions. + + The number of regions has a direct impact on performance and memory usage, so it is recommended to experiment with + various settings to find the best combination for your game. A good default setup is to start with global bounds + around the whole world, and subdivide these bounds into 4*4 regions. The PxBroadPhaseExt::createRegionsFromWorldBounds + function can do that for you. + + @see PxBroadPhaseCallback PxBroadPhaseExt.createRegionsFromWorldBounds + */ + struct PxBroadPhaseRegion + { + PxBounds3 bounds; //!< Region's bounds + void* userData; //!< Region's user-provided data + }; + + /** + \brief Information & stats structure for a region + */ + struct PxBroadPhaseRegionInfo + { + PxBroadPhaseRegion region; //!< User-provided region data + PxU32 nbStaticObjects; //!< Number of static objects in the region + PxU32 nbDynamicObjects; //!< Number of dynamic objects in the region + bool active; //!< True if region is currently used, i.e. it has not been removed + bool overlap; //!< True if region overlaps other regions (regions that are just touching are not considering overlapping) + }; + + /** + \brief Caps class for broad phase. + */ + struct PxBroadPhaseCaps + { + PxU32 maxNbRegions; //!< Max number of regions supported by the broad-phase + PxU32 maxNbObjects; //!< Max number of objects supported by the broad-phase + bool needsPredefinedBounds; //!< If true, broad-phase needs 'regions' to work + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxClient.h b/src/PhysX/physx/include/PxClient.h new file mode 100644 index 000000000..4124add51 --- /dev/null +++ b/src/PhysX/physx/include/PxClient.h @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_CLIENT +#define PX_PHYSICS_NX_CLIENT + +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief An ID to identify different clients for multiclient support. + +@see PxScene::createClient() +*/ +typedef PxU8 PxClientID; + +/** +\brief The predefined default PxClientID value. + +@see PxClientID PxScene::createClient() +*/ +static const PxClientID PX_DEFAULT_CLIENT = 0; + +/** +\brief The maximum number of clients we support. + +@see PxClientID PxScene::createClient() +*/ +static const PxClientID PX_MAX_CLIENTS = 128; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif diff --git a/src/PhysX/physx/include/PxConstraint.h b/src/PhysX/physx/include/PxConstraint.h new file mode 100644 index 000000000..f0fb733c0 --- /dev/null +++ b/src/PhysX/physx/include/PxConstraint.h @@ -0,0 +1,280 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NX_CONSTRAINT +#define PX_PHYSICS_NX_CONSTRAINT + +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxConstraintDesc.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxRigidActor; +class PxScene; +class PxConstraintConnector; + +/** +\brief a table of function pointers for a constraint + +@see PxConstraint +*/ + +/** +\brief constraint flags + +\note eBROKEN is a read only flag +*/ + +struct PxConstraintFlag +{ + enum Enum + { + eBROKEN = 1<<0, //!< whether the constraint is broken + ePROJECT_TO_ACTOR0 = 1<<1, //!< whether actor1 should get projected to actor0 for this constraint (note: projection of a static/kinematic actor to a dynamic actor will be ignored) + ePROJECT_TO_ACTOR1 = 1<<2, //!< whether actor0 should get projected to actor1 for this constraint (note: projection of a static/kinematic actor to a dynamic actor will be ignored) + ePROJECTION = ePROJECT_TO_ACTOR0 | ePROJECT_TO_ACTOR1, //!< whether the actors should get projected for this constraint (the direction will be chosen by PhysX) + eCOLLISION_ENABLED = 1<<3, //!< whether contacts should be generated between the objects this constraint constrains + eVISUALIZATION = 1<<4, //!< whether this constraint should be visualized, if constraint visualization is turned on + eDRIVE_LIMITS_ARE_FORCES = 1<<5, //!< limits for drive strength are forces rather than impulses + eIMPROVED_SLERP = 1<<7, //!< perform preprocessing for improved accuracy on D6 Slerp Drive (this flag will be removed in a future release when preprocessing is no longer required) + eDISABLE_PREPROCESSING = 1<<8, //!< suppress constraint preprocessing, intended for use with rowResponseThreshold. May result in worse solver accuracy for ill-conditioned constraints. + eENABLE_EXTENDED_LIMITS = 1<<9, //!< enables extended limit ranges for angular limits (e.g. limit values > PxPi or < -PxPi) + eGPU_COMPATIBLE = 1<<10 //!< the constraint type is supported by gpu dynamic + }; +}; + +/** +\brief constraint flags +@see PxConstraintFlag +*/ + +typedef PxFlags PxConstraintFlags; +PX_FLAGS_OPERATORS(PxConstraintFlag::Enum, PxU16) + +struct PxConstraintShaderTable +{ + enum + { + eMAX_SOLVERPRPEP_DATASIZE=400 + }; + + PxConstraintSolverPrep solverPrep; //!< solver constraint generation function + PxConstraintProject project; //!< constraint projection function + PxConstraintVisualize visualize; //!< constraint visualization function + PxConstraintFlag::Enum flag; //!< gpu constraint +}; + + +/** +\brief A plugin class for implementing constraints + +@see PxPhysics.createConstraint +*/ + +class PxConstraint : public PxBase +{ +public: + + /** + \brief Releases a PxConstraint instance. + + \note This call does not wake up the connected rigid bodies. + + @see PxPhysics.createConstraint, PxBase.release() + */ + virtual void release() = 0; + + /** + \brief Retrieves the scene which this constraint belongs to. + + \return Owner Scene. NULL if not part of a scene. + + @see PxScene + */ + virtual PxScene* getScene() const = 0; + + /** + \brief Retrieves the actors for this constraint. + + \param[out] actor0 a reference to the pointer for the first actor + \param[out] actor1 a reference to the pointer for the second actor + + @see PxActor + */ + virtual void getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const = 0; + + /** + \brief Sets the actors for this constraint. + + \param[in] actor0 a reference to the pointer for the first actor + \param[in] actor1 a reference to the pointer for the second actor + + @see PxActor + */ + virtual void setActors(PxRigidActor* actor0, PxRigidActor* actor1) = 0; + + /** + \brief Notify the scene that the constraint shader data has been updated by the application + */ + virtual void markDirty() = 0; + + /** + \brief Set the flags for this constraint + + \param[in] flags the new constraint flags + + default: PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES + + @see PxConstraintFlags + */ + virtual void setFlags(PxConstraintFlags flags) = 0; + + /** + \brief Retrieve the flags for this constraint + + \return the constraint flags + @see PxConstraintFlags + */ + virtual PxConstraintFlags getFlags() const = 0; + + /** + \brief Set a flag for this constraint + + \param[in] flag the constraint flag + \param[in] value the new value of the flag + + @see PxConstraintFlags + */ + virtual void setFlag(PxConstraintFlag::Enum flag, bool value) = 0; + + /** + \brief Retrieve the constraint force most recently applied to maintain this constraint. + + \param[out] linear the constraint force + \param[out] angular the constraint torque + */ + virtual void getForce(PxVec3& linear, PxVec3& angular) const = 0; + + /** + \brief whether the constraint is valid. + + A constraint is valid if it has at least one dynamic rigid body or articulation link. A constraint that + is not valid may not be inserted into a scene, and therefore a static actor to which an invalid constraint + is attached may not be inserted into a scene. + + Invalid constraints arise only when an actor to which the constraint is attached has been deleted. + */ + virtual bool isValid() const = 0; + + /** + \brief Set the break force and torque thresholds for this constraint. + + If either the force or torque measured at the constraint exceed these thresholds the constraint will break. + + \param[in] linear the linear break threshold + \param[in] angular the angular break threshold + */ + virtual void setBreakForce(PxReal linear, PxReal angular) = 0; + + /** + \brief Retrieve the constraint break force and torque thresholds + + \param[out] linear the linear break threshold + \param[out] angular the angular break threshold + */ + virtual void getBreakForce(PxReal& linear, PxReal& angular) const = 0; + + /** + \brief Set the minimum response threshold for a constraint row + + When using mass modification for a joint or infinite inertia for a jointed body, very stiff solver constraints can be generated which + can destabilize simulation. Setting this value to a small positive value (e.g. 1e-8) will cause constraint rows to be ignored if very + large changes in impulses will generate only small changes in velocity. When setting this value, also set + PxConstraintFlag::eDISABLE_PREPROCESSING. The solver accuracy for this joint may be reduced. + + \param[in] threshold the minimum response threshold + + @see PxConstraintFlag::eDISABLE_PREPROCESSING + */ + virtual void setMinResponseThreshold(PxReal threshold) = 0; + + /** + \brief Retrieve the constraint break force and torque thresholds + + \return the minimum response threshold for a constraint row + */ + virtual PxReal getMinResponseThreshold() const = 0; + + /** + \brief Fetch external owner of the constraint. + + Provides a reference to the external owner of a constraint and a unique owner type ID. + + \param[out] typeID Unique type identifier of the external object. + \return Reference to the external object which owns the constraint. + + @see PxConstraintConnector.getExternalReference() + */ + virtual void* getExternalReference(PxU32& typeID) = 0; + + /** + \brief Set the constraint functions for this constraint + + \param[in] connector the constraint connector object by which the SDK communicates with the constraint. + \param[in] shaders the shader table for the constraint + + @see PxConstraintConnector PxConstraintSolverPrep PxConstraintProject PxConstraintVisualize + */ + virtual void setConstraintFunctions(PxConstraintConnector& connector, + const PxConstraintShaderTable& shaders) = 0; + + virtual const char* getConcreteTypeName() const { return "PxConstraint"; } + +protected: + PX_INLINE PxConstraint(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxConstraint(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxConstraint() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxConstraint", name) || PxBase::isKindOf(name); } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxConstraintDesc.h b/src/PhysX/physx/include/PxConstraintDesc.h new file mode 100644 index 000000000..b469b55b5 --- /dev/null +++ b/src/PhysX/physx/include/PxConstraintDesc.h @@ -0,0 +1,443 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NX_CONSTRAINTDESC +#define PX_PHYSICS_NX_CONSTRAINTDESC + +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxFlags.h" +#include "foundation/PxVec3.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx { namespace pvdsdk { +#endif + class PvdDataStream; +#if !PX_DOXYGEN +}} +#endif + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxConstraintConnector; +class PxRigidActor; +class PxScene; +class PxConstraintConnector; +class PxRenderBuffer; +class PxDeletionListener; + +/** + \brief constraint row flags + + These flags configure the post-processing of constraint rows and the behavior of the solver while solving constraints +*/ +struct Px1DConstraintFlag +{ + PX_CUDA_CALLABLE Px1DConstraintFlag(){} + + enum Type + { + eSPRING = 1<<0, //!< whether the constraint is a spring. Mutually exclusive with eRESTITUTION. If set, eKEEPBIAS is ignored. + eACCELERATION_SPRING = 1<<1, //!< whether the constraint is a force or acceleration spring. Only valid if eSPRING is set. + eRESTITUTION = 1<<2, //!< whether the restitution model should be applied to generate the target velocity. Mutually exclusive with eSPRING. If restitution causes a bounces, eKEEPBIAS is ignored + eKEEPBIAS = 1<<3, //!< whether to keep the error term when solving for velocity. Ignored if restitution generates bounce, or eSPRING is set. + eOUTPUT_FORCE = 1<<4, //!< whether to accumulate the force value from this constraint in the force total that is reported for the constraint and tested for breakage + eHAS_DRIVE_LIMIT = 1<<5, //!< whether the constraint has a drive force limit (which will be scaled by dt unless PxConstraintFlag::eLIMITS_ARE_FORCES is set) + eANGULAR_CONSTRAINT = 1 << 6,//!< Whether this is an angular or linear constraint + eDRIVE_ROW = 1 << 7 + }; +}; + +typedef PxFlags Px1DConstraintFlags; +PX_FLAGS_OPERATORS(Px1DConstraintFlag::Type, PxU16) + +/** +\brief constraint type hints which the solver uses to optimize constraint handling +*/ +struct PxConstraintSolveHint +{ + enum Enum + { + eNONE = 0, //!< no special properties + eACCELERATION1 = 256, //!< a group of acceleration drive constraints with the same stiffness and drive parameters + eSLERP_SPRING = 258, //!< temporary special value to identify SLERP drive rows + eACCELERATION2 = 512, //!< a group of acceleration drive constraints with the same stiffness and drive parameters + eACCELERATION3 = 768, //!< a group of acceleration drive constraints with the same stiffness and drive parameters + eROTATIONAL_EQUALITY = 1024, //!< rotational equality constraints with no force limit and no velocity target + eROTATIONAL_INEQUALITY = 1025, //!< rotational inequality constraints with (0, PX_MAX_FLT) force limits + eEQUALITY = 2048, //!< equality constraints with no force limit and no velocity target + eINEQUALITY = 2049 //!< inequality constraints with (0, PX_MAX_FLT) force limits + }; +}; + +/** +\brief A constraint + +A constraint is expressed as a set of 1-dimensional constraint rows which define the required constraint +on the objects' velocities. + +Each constraint is either a hard constraint or a spring. We define the velocity at the constraint to be +the quantity + + v = body0vel.dot(lin0,ang0) - body1vel.dot(lin1, ang1) + +For a hard constraint, the solver attempts to generate + +1. a set of velocities for the objects which, when integrated, respect the constraint errors: + + v + (geometricError / timestep) = velocityTarget + +2. a set of velocities for the objects which respect the constraints: + + v = velocityTarget + +Hard constraints support restitution: if the impact velocity exceeds the bounce threshold, then the target velocity +of the constraint will be set to restitution * -v + +Alternatively, the solver can attempt to resolve the velocity constraint as an implicit spring: + + F = stiffness * -geometricError + damping * (velocityTarget - v) + +where F is the constraint force or acceleration. Springs are fully implicit: that is, the force or acceleration +is a function of the position and velocity after the solve. + +All constraints support limits on the minimum or maximum impulse applied. +*/ + +PX_ALIGN_PREFIX(16) +struct Px1DConstraint +{ + PxVec3 linear0; //!< linear component of velocity jacobian in world space + PxReal geometricError; //!< geometric error of the constraint along this axis + PxVec3 angular0; //!< angular component of velocity jacobian in world space + PxReal velocityTarget; //!< velocity target for the constraint along this axis + + PxVec3 linear1; //!< linear component of velocity jacobian in world space + PxReal minImpulse; //!< minimum impulse the solver may apply to enforce this constraint + PxVec3 angular1; //!< angular component of velocity jacobian in world space + PxReal maxImpulse; //!< maximum impulse the solver may apply to enforce this constraint + + union + { + struct SpringModifiers + { + PxReal stiffness; //!< spring parameter, for spring constraints + PxReal damping; //!< damping parameter, for spring constraints + } spring; + struct RestitutionModifiers + { + PxReal restitution; //!< restitution parameter for determining additional "bounce" + PxReal velocityThreshold; //!< minimum impact velocity for bounce + } bounce; + } mods; + + PxReal forInternalUse; //!< for internal use only + PxU16 flags; //!< a set of Px1DConstraintFlags + PxU16 solveHint; //!< constraint optimization hint, should be an element of PxConstraintSolveHint +} +PX_ALIGN_SUFFIX(16); + + +/** +\brief Flags for determining which components of the constraint should be visualized. + +@see PxConstraintVisualize +*/ +struct PxConstraintVisualizationFlag +{ + enum Enum + { + eLOCAL_FRAMES = 1, //!< visualize constraint frames + eLIMITS = 2 //!< visualize constraint limits + }; +}; + +PX_ALIGN_PREFIX(16) +struct PxConstraintInvMassScale +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PxReal linear0; //!< multiplier for inverse mass of body0 + PxReal angular0; //!< multiplier for inverse MoI of body0 + PxReal linear1; //!< multiplier for inverse mass of body1 + PxReal angular1; //!< multiplier for inverse MoI of body1 + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxConstraintInvMassScale(){} + PX_CUDA_CALLABLE PX_FORCE_INLINE PxConstraintInvMassScale(PxReal lin0, PxReal ang0, PxReal lin1, PxReal ang1) : linear0(lin0), angular0(ang0), linear1(lin1), angular1(ang1){} +} +PX_ALIGN_SUFFIX(16); + +/** solver constraint generation shader + +This function is called by the constraint solver framework. The function must be reentrant, since it may be called simultaneously +from multiple threads, and should access only the arguments passed into it. + +Developers writing custom constraints are encouraged to read the documentation in the user guide and the implementation code in PhysXExtensions. + +\param[out] constraints An array of solver constraint rows to be filled in +\param[out] bodyAWorldOffset The origin point (offset from the position vector of bodyA's center of mass) at which the constraint is resolved. This value does not affect how constraints are solved, only the constraint force reported. +\param[in] maxConstraints The size of the constraint buffer. At most this many constraints rows may be written +\param[out] invMassScale The inverse mass and inertia scales for the constraint +\param[in] constantBlock The constant data block +\param[in] bodyAToWorld The center of mass frame of the first constrained body (the identity transform if the first actor is static, or if a NULL actor pointer was provided for it) +\param[in] bodyBToWorld The center of mass frame of the second constrained body (the identity transform if the second actor is static, or if a NULL actor pointer was provided for it) +\param[in] useExtendedLimits Enables limit ranges outside of (-PI, PI) +\param[out] cAtW The world space location of body A's joint frame (position only) +\param[out] cBtW The world space location of body B's joint frame (position only) + +\return the number of constraint rows written. +*/ +typedef PxU32 (*PxConstraintSolverPrep)(Px1DConstraint* constraints, + PxVec3& bodyAWorldOffset, + PxU32 maxConstraints, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bodyAToWorld, + const PxTransform& bodyBToWorld, + bool useExtendedLimits, + PxVec3& cAtW, + PxVec3& cBtW); + +/** solver constraint projection shader + +This function is called by the constraint post-solver framework. The function must be reentrant, since it may be called simultaneously +from multiple threads and should access only the arguments passed into it. + +\param[in] constantBlock The constant data block +\param[out] bodyAToWorld The center of mass frame of the first constrained body (the identity if the actor is static or a NULL pointer was provided for it) +\param[out] bodyBToWorld The center of mass frame of the second constrained body (the identity if the actor is static or a NULL pointer was provided for it) +\param[in] projectToA True if the constraint should be projected by moving the second body towards the first, false if the converse +*/ +typedef void (*PxConstraintProject)(const void* constantBlock, + PxTransform& bodyAToWorld, + PxTransform& bodyBToWorld, + bool projectToA); + +/** + API used to visualize details about a constraint. +*/ +class PxConstraintVisualizer +{ +protected: + virtual ~PxConstraintVisualizer(){} +public: + /** Visualize joint frames + + \param[in] parent Parent transformation + \param[in] child Child transformation + */ + virtual void visualizeJointFrames(const PxTransform& parent, const PxTransform& child) = 0; + + /** Visualize joint linear limit + + \param[in] t0 Base transformation + \param[in] t1 End transformation + \param[in] value Distance + \param[in] active State of the joint - active/inactive + */ + virtual void visualizeLinearLimit(const PxTransform& t0, const PxTransform& t1, PxReal value, bool active) = 0; + + /** Visualize joint angular limit + + \param[in] t0 Transformation for the visualization + \param[in] lower Lower limit angle + \param[in] upper Upper limit angle + \param[in] active State of the joint - active/inactive + */ + virtual void visualizeAngularLimit(const PxTransform& t0, PxReal lower, PxReal upper, bool active) = 0; + + /** Visualize limit cone + + \param[in] t Transformation for the visualization + \param[in] tanQSwingY Tangent of the quarter Y angle + \param[in] tanQSwingZ Tangent of the quarter Z angle + \param[in] active State of the joint - active/inactive + */ + virtual void visualizeLimitCone(const PxTransform& t, PxReal tanQSwingY, PxReal tanQSwingZ, bool active) = 0; + + /** Visualize joint double cone + + \param[in] t Transformation for the visualization + \param[in] angle Limit angle + \param[in] active State of the joint - active/inactive + */ + virtual void visualizeDoubleCone(const PxTransform& t, PxReal angle, bool active) = 0; + + /** Visualize line + + \param[in] p0 Start position + \param[in] p1 End postion + \param[in] color Color + */ + virtual void visualizeLine(const PxVec3& p0, const PxVec3& p1, PxU32 color) = 0; +}; + +/** solver constraint visualization function + +This function is called by the constraint post-solver framework to visualize the constraint + +\param[out] visualizer The render buffer to render to +\param[in] constantBlock The constant data block +\param[in] body0Transform The center of mass frame of the first constrained body (the identity if the actor is static, or a NULL pointer was provided for it) +\param[in] body1Transform The center of mass frame of the second constrained body (the identity if the actor is static, or a NULL pointer was provided for it) +\param[in] flags The visualization flags (PxConstraintVisualizationFlag) + +@see PxRenderBuffer +*/ +typedef void (*PxConstraintVisualize)(PxConstraintVisualizer& visualizer, + const void* constantBlock, + const PxTransform& body0Transform, + const PxTransform& body1Transform, + PxU32 flags); + + +struct PxPvdUpdateType +{ + enum Enum + { + CREATE_INSTANCE, + RELEASE_INSTANCE, + UPDATE_ALL_PROPERTIES, + UPDATE_SIM_PROPERTIES + }; +}; + +/** + +\brief This class connects a custom constraint to the SDK + +This class connects a custom constraint to the SDK, and functions are called by the SDK +to query the custom implementation for specific information to pass on to the application +or inform the constraint when the application makes calls into the SDK which will update +the custom constraint's internal implementation +*/ +class PxConstraintConnector +{ +public: + /** + when the constraint is marked dirty, this function is called at the start of the simulation + step for the SDK to copy the constraint data block. + */ + virtual void* prepareData() = 0; + + /** + this function is called by the SDK to update PVD's view of it + */ + virtual bool updatePvdProperties(physx::pvdsdk::PvdDataStream& pvdConnection, + const PxConstraint* c, + PxPvdUpdateType::Enum updateType) const = 0; + + /** + When the SDK deletes a PxConstraint object this function is called by the SDK. In general + custom constraints should not be deleted directly by applications: rather, the constraint + should respond to a release() request by calling PxConstraint::release(), then wait for + this call to release its own resources, so that even if the release() call occurs during + a simulation step, the deletion of the constraint is buffered until that step completes. + + This function is also called when a PxConstraint object is deleted on cleanup due to + destruction of the PxPhysics object. + */ + virtual void onConstraintRelease() = 0; + + /** + This function is called by the SDK when the CoM of one of the actors is moved. Since the + API specifies constraint positions relative to actors, and the constraint shader functions + are supplied with coordinates relative to bodies, some synchronization is usually required + when the application moves an object's center of mass. + */ + virtual void onComShift(PxU32 actor) = 0; + + /** + This function is called by the SDK when the scene origin gets shifted and allows to adjust + custom data which contains world space transforms. + + \note If the adjustments affect constraint shader data, it is necessary to call PxConstraint::markDirty() + to make sure that the data gets synced at the beginning of the next simulation step. + + \param[in] shift Translation vector the origin is shifted by. + + @see PxScene.shiftOrigin() + */ + virtual void onOriginShift(const PxVec3& shift) = 0; + + /** + \brief Fetches external data for a constraint. + + This function is used by the SDK to acquire a reference to the owner of a constraint and a unique + owner type ID. This information will be passed on when a breakable constraint breaks or when + #PxConstraint::getExternalReference() is called. + + \param[out] typeID Unique type identifier of the external object. The value 0xffffffff is reserved and should not be used. Furthermore, if the PhysX extensions library is used, some other IDs are reserved already (see PxConstraintExtIDs) + \return Reference to the external object which owns the constraint. + + @see PxConstraintInfo PxSimulationEventCallback.onConstraintBreak() + */ + virtual void* getExternalReference(PxU32& typeID) = 0; + + /** + \brief Obtain a reference to a PxBase interface if the constraint has one. + + If the constraint does not implement the PxBase interface, it should return NULL. + */ + virtual PxBase* getSerializable() = 0; + + /** + \brief Obtain the shader function pointer used to prep rows for this constraint + */ + virtual PxConstraintSolverPrep getPrep() const = 0; + + /** + \brief Obtain the pointer to the constraint's constant data + */ + virtual const void* getConstantBlock() const = 0; + + /** + \brief virtual destructor + */ + virtual ~PxConstraintConnector() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxContact.h b/src/PhysX/physx/include/PxContact.h new file mode 100644 index 000000000..0fab1333d --- /dev/null +++ b/src/PhysX/physx/include/PxContact.h @@ -0,0 +1,583 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONTACT_H +#define PX_CONTACT_H + +#include "foundation/PxVec3.h" +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +#define PXC_CONTACT_NO_FACE_INDEX 0xffffffff + +PX_ALIGN_PREFIX(16) +struct PxMassModificationProps +{ + PxReal mInvMassScale0; + PxReal mInvInertiaScale0; + PxReal mInvMassScale1; + PxReal mInvInertiaScale1; +} +PX_ALIGN_SUFFIX(16); + +/** +\brief Header for contact patch where all points share same material and normal +*/ + +PX_ALIGN_PREFIX(16) +struct PxContactPatch +{ + enum PxContactPatchFlags + { + eHAS_FACE_INDICES = 1, //!< Indicates this contact stream has face indices. + eMODIFIABLE = 2, //!< Indicates this contact stream is modifiable. + eFORCE_NO_RESPONSE = 4, //!< Indicates this contact stream is notify-only (no contact response). + eHAS_MODIFIED_MASS_RATIOS = 8, //!< Indicates this contact stream has modified mass ratios + eHAS_TARGET_VELOCITY = 16, //!< Indicates this contact stream has target velocities set + eHAS_MAX_IMPULSE = 32, //!< Indicates this contact stream has max impulses set + eREGENERATE_PATCHES = 64, //!< Indicates this contact stream needs patches re-generated. + //!< This is required if the application modified either the contact normal or the material properties + eCOMPRESSED_MODIFIED_CONTACT = 128 + }; + + PX_ALIGN(16, PxMassModificationProps mMassModification); //16 + /** + \brief Contact normal + */ + PX_ALIGN(16, PxVec3 normal); //28 + /** + \brief Restitution coefficient + */ + PxReal restitution; //32 + + PxReal dynamicFriction; //36 + PxReal staticFriction; //40 + PxU8 startContactIndex; //41 + PxU8 nbContacts; //42 //Can be a U8 + + PxU8 materialFlags; //43 //Can be a U16 + PxU8 internalFlags; //44 //Can be a U16 + PxU16 materialIndex0; //46 //Can be a U16 + PxU16 materialIndex1; //48 //Can be a U16 + + +} +PX_ALIGN_SUFFIX(16); + +/** +\brief Contact point data including face (feature) indices +*/ + +PX_ALIGN_PREFIX(16) +struct PxContact +{ + /** + \brief Contact point in world space + */ + PxVec3 contact; //12 + /** + \brief Separation value (negative implies penetration). + */ + PxReal separation; //16 +} +PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct PxExtendedContact : public PxContact +{ + /** + \brief Target velocity + */ + PX_ALIGN(16, PxVec3 targetVelocity); //28 + /** + \brief Maximum impulse + */ + PxReal maxImpulse; //32 +} +PX_ALIGN_SUFFIX(16); + +/** +\brief A modifiable contact point. This has additional fields per-contact to permit modification by user. +\note Not all fields are currently exposed to the user. +*/ +PX_ALIGN_PREFIX(16) +struct PxModifiableContact : public PxExtendedContact +{ + /** + \brief Contact normal + */ + PX_ALIGN(16, PxVec3 normal); //44 + /** + \brief Restitution coefficient + */ + PxReal restitution; //48 + + /** + \brief Material Flags + */ + PxU32 materialFlags; //52 + + /** + \brief Shape A's material index + */ + PxU16 materialIndex0; //54 + /** + \brief Shape B's material index + */ + PxU16 materialIndex1; //56 + /** + \brief static friction coefficient + */ + PxReal staticFriction; //60 + /** + \brief dynamic friction coefficient + */ + PxReal dynamicFriction; //64 +} +PX_ALIGN_SUFFIX(16); + +/** +\brief A class to iterate over a compressed contact stream. This supports read-only access to the various contact formats. +*/ +struct PxContactStreamIterator +{ + enum StreamFormat + { + eSIMPLE_STREAM, + eMODIFIABLE_STREAM, + eCOMPRESSED_MODIFIABLE_STREAM + }; + /** + \brief Utility zero vector to optimize functions returning zero vectors when a certain flag isn't set. + \note This allows us to return by reference instead of having to return by value. Returning by value will go via memory (registers -> stack -> registers), which can + cause performance issues on certain platforms. + */ + PxVec3 zero; + /** + \brief The patch headers. + */ + const PxContactPatch* patch; + + /** + \brief The contacts + */ + const PxContact* contact; + + /** + \brief The contact triangle face index + */ + const PxU32* faceIndice; + + + /** + \brief The total number of patches in this contact stream + */ + PxU32 totalPatches; + + /** + \brief The total number of contact points in this stream + */ + PxU32 totalContacts; + + /** + \brief The current contact index + */ + PxU32 nextContactIndex; + + /** + \brief The current patch Index + */ + PxU32 nextPatchIndex; + + /* + \brief Size of contact patch header + \note This varies whether the patch is modifiable or not. + */ + PxU32 contactPatchHeaderSize; + /** + \brief Contact point size + \note This varies whether the patch has feature indices or is modifiable. + */ + PxU32 contactPointSize; + /** + \brief The stream format + */ + StreamFormat mStreamFormat; + /** + \brief Indicates whether this stream is notify-only or not. + */ + PxU32 forceNoResponse; + + bool pointStepped; + + PxU32 hasFaceIndices; + + /** + \brief Constructor + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxContactStreamIterator(const PxU8* contactPatches, const PxU8* contactPoints, const PxU32* contactFaceIndices, PxU32 nbPatches, PxU32 nbContacts) + : zero(0.f) + { + bool modify = false; + bool compressedModify = false; + bool response = false; + bool indices = false; + + PxU32 pointSize = 0; + PxU32 patchHeaderSize = sizeof(PxContactPatch); + + const PxContactPatch* patches = reinterpret_cast(contactPatches); + + if(patches) + { + modify = (patches->internalFlags & PxContactPatch::eMODIFIABLE) != 0; + compressedModify = (patches->internalFlags & PxContactPatch::eCOMPRESSED_MODIFIED_CONTACT) != 0; + indices = (patches->internalFlags & PxContactPatch::eHAS_FACE_INDICES) != 0; + + patch = patches; + + contact = reinterpret_cast(contactPoints); + + faceIndice = contactFaceIndices; + + pointSize = compressedModify ? sizeof(PxExtendedContact) : modify ? sizeof(PxModifiableContact) : sizeof(PxContact); + + response = (patch->internalFlags & PxContactPatch::eFORCE_NO_RESPONSE) == 0; + } + + + mStreamFormat = compressedModify ? eCOMPRESSED_MODIFIABLE_STREAM : modify ? eMODIFIABLE_STREAM : eSIMPLE_STREAM; + hasFaceIndices = PxU32(indices); + forceNoResponse = PxU32(!response); + + contactPatchHeaderSize = patchHeaderSize; + contactPointSize = pointSize; + nextPatchIndex = 0; + nextContactIndex = 0; + totalContacts = nbContacts; + totalPatches = nbPatches; + + pointStepped = false; + } + + /** + \brief Returns whether there are more patches in this stream. + \return Whether there are more patches in this stream. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool hasNextPatch() const + { + return nextPatchIndex < totalPatches; + } + + /** + \brief Returns the total contact count. + \return Total contact count. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getTotalContactCount() const + { + return totalContacts; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getTotalPatchCount() const + { + return totalPatches; + } + + /** + \brief Advances iterator to next contact patch. + */ + PX_CUDA_CALLABLE PX_INLINE void nextPatch() + { + PX_ASSERT(nextPatchIndex < totalPatches); + if(nextPatchIndex) + { + if(nextContactIndex < patch->nbContacts) + { + PxU32 nbToStep = patch->nbContacts - this->nextContactIndex; + contact = reinterpret_cast(reinterpret_cast(contact) + contactPointSize * nbToStep); + } + patch = reinterpret_cast(reinterpret_cast(patch) + contactPatchHeaderSize); + } + nextPatchIndex++; + nextContactIndex = 0; + } + + /** + \brief Returns if the current patch has more contacts. + \return If there are more contacts in the current patch. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool hasNextContact() const + { + return nextContactIndex < (patch->nbContacts); + } + + /** + \brief Advances to the next contact in the patch. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void nextContact() + { + PX_ASSERT(nextContactIndex < patch->nbContacts); + if(pointStepped) + { + contact = reinterpret_cast(reinterpret_cast(contact) + contactPointSize); + faceIndice++; + } + nextContactIndex++; + pointStepped = true; + } + + + /** + \brief Gets the current contact's normal + \return The current contact's normal. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& getContactNormal() const + { + return getContactPatch().normal; + } + + /** + \brief Gets the inverse mass scale for body 0. + \return The inverse mass scale for body 0. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getInvMassScale0() const + { + return patch->mMassModification.mInvMassScale0; + } + + /** + \brief Gets the inverse mass scale for body 1. + \return The inverse mass scale for body 1. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getInvMassScale1() const + { + return patch->mMassModification.mInvMassScale1; + } + + /** + \brief Gets the inverse inertia scale for body 0. + \return The inverse inertia scale for body 0. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getInvInertiaScale0() const + { + return patch->mMassModification.mInvInertiaScale0; + } + + /** + \brief Gets the inverse inertia scale for body 1. + \return The inverse inertia scale for body 1. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getInvInertiaScale1() const + { + return patch->mMassModification.mInvInertiaScale1; + } + + /** + \brief Gets the contact's max impulse. + \return The contact's max impulse. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getMaxImpulse() const + { + return mStreamFormat != eSIMPLE_STREAM ? getExtendedContact().maxImpulse : PX_MAX_REAL; + } + + /** + \brief Gets the contact's target velocity. + \return The contact's target velocity. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& getTargetVel() const + { + return mStreamFormat != eSIMPLE_STREAM ? getExtendedContact().targetVelocity : zero; + } + + /** + \brief Gets the contact's contact point. + \return The contact's contact point. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& getContactPoint() const + { + return contact->contact; + } + + /** + \brief Gets the contact's separation. + \return The contact's separation. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getSeparation() const + { + return contact->separation; + } + + /** + \brief Gets the contact's face index for shape 0. + \return The contact's face index for shape 0. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getFaceIndex0() const + { + return PXC_CONTACT_NO_FACE_INDEX; + } + + /** + \brief Gets the contact's face index for shape 1. + \return The contact's face index for shape 1. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getFaceIndex1() const + { + return hasFaceIndices ? *faceIndice : PXC_CONTACT_NO_FACE_INDEX; + } + + /** + \brief Gets the contact's static friction coefficient. + \return The contact's static friction coefficient. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getStaticFriction() const + { + return getContactPatch().staticFriction; + } + + /** + \brief Gets the contact's static dynamic coefficient. + \return The contact's static dynamic coefficient. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getDynamicFriction() const + { + return getContactPatch().dynamicFriction; + } + + /** + \brief Gets the contact's restitution coefficient. + \return The contact's restitution coefficient. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getRestitution() const + { + return getContactPatch().restitution; + } + + /** + \brief Gets the contact's material flags. + \return The contact's material flags. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaterialFlags() const + { + return getContactPatch().materialFlags; + } + + /** + \brief Gets the contact's material index for shape 0. + \return The contact's material index for shape 0. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex0() const + { + return PxU16(getContactPatch().materialIndex0); + } + + /** + \brief Gets the contact's material index for shape 1. + \return The contact's material index for shape 1. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex1() const + { + return PxU16(getContactPatch().materialIndex1); + } + + /** + \brief Advances the contact stream iterator to a specific contact index. + */ + bool advanceToIndex(const PxU32 initialIndex) + { + PX_ASSERT(this->nextPatchIndex == 0 && this->nextContactIndex == 0); + + PxU32 numToAdvance = initialIndex; + + if(numToAdvance == 0) + { + PX_ASSERT(hasNextPatch()); + nextPatch(); + return true; + } + + while(numToAdvance) + { + while(hasNextPatch()) + { + nextPatch(); + PxU32 patchSize = patch->nbContacts; + if(numToAdvance <= patchSize) + { + contact = reinterpret_cast(reinterpret_cast(contact) + contactPointSize * numToAdvance); + nextContactIndex += numToAdvance; + return true; + } + else + { + numToAdvance -= patchSize; + } + } + } + return false; + } + +private: + + /** + \brief Internal helper + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxContactPatch& getContactPatch() const + { + return *static_cast(patch); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxExtendedContact& getExtendedContact() const + { + PX_ASSERT(mStreamFormat == eMODIFIABLE_STREAM || mStreamFormat == eCOMPRESSED_MODIFIABLE_STREAM); + return *static_cast(contact); + } + +}; + + +#if PX_VC +#pragma warning(pop) +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif diff --git a/src/PhysX/physx/include/PxContactModifyCallback.h b/src/PhysX/physx/include/PxContactModifyCallback.h new file mode 100644 index 000000000..adca7a60a --- /dev/null +++ b/src/PhysX/physx/include/PxContactModifyCallback.h @@ -0,0 +1,486 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONTACT_MODIFY_CALLBACK +#define PX_CONTACT_MODIFY_CALLBACK +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxShape.h" +#include "PxContact.h" +#include "foundation/PxTransform.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxShape; + +/** +\brief An array of contact points, as passed to contact modification. + +The word 'set' in the name does not imply that duplicates are filtered in any +way. This initial set of contacts does potentially get reduced to a smaller +set before being passed to the solver. + +You can use the accessors to read and write contact properties. The number of +contacts is immutable, other than being able to disable contacts using ignore(). + +@see PxContactModifyCallback, PxModifiableContact +*/ +class PxContactSet +{ +public: + /** + \brief Get the position of a specific contact point in the set. + + @see PxModifiableContact.point + */ + PX_FORCE_INLINE const PxVec3& getPoint(PxU32 i) const { return mContacts[i].contact; } + + /** + \brief Alter the position of a specific contact point in the set. + + @see PxModifiableContact.point + */ + PX_FORCE_INLINE void setPoint(PxU32 i, const PxVec3& p) { mContacts[i].contact = p; } + + /** + \brief Get the contact normal of a specific contact point in the set. + + @see PxModifiableContact.normal + */ + PX_FORCE_INLINE const PxVec3& getNormal(PxU32 i) const { return mContacts[i].normal; } + + /** + \brief Alter the contact normal of a specific contact point in the set. + + \note Changing the normal can cause contact points to be ignored. + + @see PxModifiableContact.normal + */ + PX_FORCE_INLINE void setNormal(PxU32 i, const PxVec3& n) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eREGENERATE_PATCHES; + mContacts[i].normal = n; + } + + /** + \brief Get the separation of a specific contact point in the set. + + @see PxModifiableContact.separation + */ + PX_FORCE_INLINE PxReal getSeparation(PxU32 i) const { return mContacts[i].separation; } + + /** + \brief Alter the separation of a specific contact point in the set. + + @see PxModifiableContact.separation + */ + PX_FORCE_INLINE void setSeparation(PxU32 i, PxReal s) { mContacts[i].separation = s; } + + /** + \brief Get the target velocity of a specific contact point in the set. + + @see PxModifiableContact.targetVelocity + + */ + PX_FORCE_INLINE const PxVec3& getTargetVelocity(PxU32 i) const { return mContacts[i].targetVelocity; } + + /** + \brief Alter the target velocity of a specific contact point in the set. + + @see PxModifiableContact.targetVelocity + */ + PX_FORCE_INLINE void setTargetVelocity(PxU32 i, const PxVec3& v) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eHAS_TARGET_VELOCITY; + mContacts[i].targetVelocity = v; + } + + /** + \brief Get the face index with respect to the first shape of the pair for a specific contact point in the set. + + @see PxModifiableContact.internalFaceIndex0 + */ + PX_FORCE_INLINE PxU32 getInternalFaceIndex0(PxU32 i) { PX_UNUSED(i); return PXC_CONTACT_NO_FACE_INDEX; } + + /** + \brief Get the face index with respect to the second shape of the pair for a specific contact point in the set. + + @see PxModifiableContact.internalFaceIndex1 + */ + PX_FORCE_INLINE PxU32 getInternalFaceIndex1(PxU32 i) + { + PxContactPatch* patch = getPatch(); + if (patch->internalFlags & PxContactPatch::eHAS_FACE_INDICES) + { + return reinterpret_cast(mContacts + mCount)[mCount + i]; + } + return PXC_CONTACT_NO_FACE_INDEX; + } + + /** + \brief Get the maximum impulse for a specific contact point in the set. + + @see PxModifiableContact.maxImpulse + */ + PX_FORCE_INLINE PxReal getMaxImpulse(PxU32 i) const { return mContacts[i].maxImpulse; } + + /** + \brief Alter the maximum impulse for a specific contact point in the set. + + \note Must be nonnegative. If set to zero, the contact point will be ignored + + @see PxModifiableContact.maxImpulse + */ + PX_FORCE_INLINE void setMaxImpulse(PxU32 i, PxReal s) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eHAS_MAX_IMPULSE; + mContacts[i].maxImpulse = s; + } + + /** + \brief Get the restitution coefficient for a specific contact point in the set. + + @see PxModifiableContact.restitution + */ + PX_FORCE_INLINE PxReal getRestitution(PxU32 i) const { return mContacts[i].restitution; } + + /** + \brief Alter the restitution coefficient for a specific contact point in the set. + + \note Valid ranges [0,1] + + @see PxModifiableContact.restitution + */ + PX_FORCE_INLINE void setRestitution(PxU32 i, PxReal r) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eREGENERATE_PATCHES; + mContacts[i].restitution = r; + } + + /** + \brief Get the static friction coefficient for a specific contact point in the set. + + @see PxModifiableContact.staticFriction + */ + PX_FORCE_INLINE PxReal getStaticFriction(PxU32 i) const { return mContacts[i].staticFriction; } + + /** + \brief Alter the static friction coefficient for a specific contact point in the set. + + @see PxModifiableContact.staticFriction + */ + PX_FORCE_INLINE void setStaticFriction(PxU32 i, PxReal f) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eREGENERATE_PATCHES; + mContacts[i].staticFriction = f; + } + + /** + \brief Get the static friction coefficient for a specific contact point in the set. + + @see PxModifiableContact.dynamicFriction + */ + PX_FORCE_INLINE PxReal getDynamicFriction(PxU32 i) const { return mContacts[i].dynamicFriction; } + + /** + \brief Alter the static dynamic coefficient for a specific contact point in the set. + + @see PxModifiableContact.dynamic + */ + PX_FORCE_INLINE void setDynamicFriction(PxU32 i, PxReal f) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eREGENERATE_PATCHES; + mContacts[i].dynamicFriction = f; + } + + /** + \brief Ignore the contact point. + + If a contact point is ignored then no force will get applied at this point. This can be used to disable collision in certain areas of a shape, for example. + */ + PX_FORCE_INLINE void ignore(PxU32 i) { mContacts[i].maxImpulse = 0.f; } + + /** + \brief The number of contact points in the set. + */ + PX_FORCE_INLINE PxU32 size() const { return mCount; } + + /** + \brief Returns the invMassScale of body 0 + + A value < 1.0 makes this contact treat the body as if it had larger mass. A value of 0.f makes this contact + treat the body as if it had infinite mass. Any value > 1.f makes this contact treat the body as if it had smaller mass. + */ + PX_FORCE_INLINE PxReal getInvMassScale0() const + { + PxContactPatch* patch = getPatch(); + return patch->mMassModification.mInvMassScale0; + } + + /** + \brief Returns the invMassScale of body 1 + + A value < 1.0 makes this contact treat the body as if it had larger mass. A value of 0.f makes this contact + treat the body as if it had infinite mass. Any value > 1.f makes this contact treat the body as if it had smaller mass. + */ + PX_FORCE_INLINE PxReal getInvMassScale1() const + { + PxContactPatch* patch = getPatch(); + return patch->mMassModification.mInvMassScale1; + } + + /** + \brief Returns the invInertiaScale of body 0 + + A value < 1.0 makes this contact treat the body as if it had larger inertia. A value of 0.f makes this contact + treat the body as if it had infinite inertia. Any value > 1.f makes this contact treat the body as if it had smaller inertia. + */ + PX_FORCE_INLINE PxReal getInvInertiaScale0() const + { + PxContactPatch* patch = getPatch(); + return patch->mMassModification.mInvInertiaScale0; + } + + /** + \brief Returns the invInertiaScale of body 1 + + A value < 1.0 makes this contact treat the body as if it had larger inertia. A value of 0.f makes this contact + treat the body as if it had infinite inertia. Any value > 1.f makes this contact treat the body as if it had smaller inertia. + */ + PX_FORCE_INLINE PxReal getInvInertiaScale1() const + { + PxContactPatch* patch = getPatch(); + return patch->mMassModification.mInvInertiaScale1; + } + + /** + \brief Sets the invMassScale of body 0 + + This can be set to any value in the range [0, PX_MAX_F32). A value < 1.0 makes this contact treat the body as if it had larger mass. A value of 0.f makes this contact + treat the body as if it had infinite mass. Any value > 1.f makes this contact treat the body as if it had smaller mass. + */ + PX_FORCE_INLINE void setInvMassScale0(const PxReal scale) + { + PxContactPatch* patch = getPatch(); + patch->mMassModification.mInvMassScale0 = scale; + patch->internalFlags |= PxContactPatch::eHAS_MODIFIED_MASS_RATIOS; + } + + /** + \brief Sets the invMassScale of body 1 + + This can be set to any value in the range [0, PX_MAX_F32). A value < 1.0 makes this contact treat the body as if it had larger mass. A value of 0.f makes this contact + treat the body as if it had infinite mass. Any value > 1.f makes this contact treat the body as if it had smaller mass. + */ + PX_FORCE_INLINE void setInvMassScale1(const PxReal scale) + { + PxContactPatch* patch = getPatch(); + patch->mMassModification.mInvMassScale1 = scale; + patch->internalFlags |= PxContactPatch::eHAS_MODIFIED_MASS_RATIOS; + } + + /** + \brief Sets the invInertiaScale of body 0 + + This can be set to any value in the range [0, PX_MAX_F32). A value < 1.0 makes this contact treat the body as if it had larger inertia. A value of 0.f makes this contact + treat the body as if it had infinite inertia. Any value > 1.f makes this contact treat the body as if it had smaller inertia. + */ + PX_FORCE_INLINE void setInvInertiaScale0(const PxReal scale) + { + PxContactPatch* patch = getPatch(); + patch->mMassModification.mInvInertiaScale0 = scale; + patch->internalFlags |= PxContactPatch::eHAS_MODIFIED_MASS_RATIOS; + } + + /** + \brief Sets the invInertiaScale of body 1 + + This can be set to any value in the range [0, PX_MAX_F32). A value < 1.0 makes this contact treat the body as if it had larger inertia. A value of 0.f makes this contact + treat the body as if it had infinite inertia. Any value > 1.f makes this contact treat the body as if it had smaller inertia. + */ + PX_FORCE_INLINE void setInvInertiaScale1(const PxReal scale) + { + PxContactPatch* patch = getPatch(); + patch->mMassModification.mInvInertiaScale1 = scale; + patch->internalFlags |= PxContactPatch::eHAS_MODIFIED_MASS_RATIOS; + } + +protected: + + PX_FORCE_INLINE PxContactPatch* getPatch() const + { + const size_t headerOffset = sizeof(PxContactPatch)*mCount; + return reinterpret_cast(reinterpret_cast(mContacts) - headerOffset); + } + + PxU32 mCount; //!< Number of contact points in the set + PxModifiableContact* mContacts; //!< The contact points of the set +}; + + + +/** +\brief An array of instances of this class is passed to PxContactModifyCallback::onContactModify(). + +@see PxContactModifyCallback +*/ + +class PxContactModifyPair +{ +public: + + /** + \brief The actors which make up the pair in contact. + + Note that these are the actors as seen by the simulation, and may have been deleted since the simulation step started. + */ + + const PxRigidActor* actor[2]; + /** + \brief The shapes which make up the pair in contact. + + Note that these are the shapes as seen by the simulation, and may have been deleted since the simulation step started. + */ + + const PxShape* shape[2]; + + /** + \brief The shape to world transforms of the two shapes. + + These are the transforms as the simulation engine sees them, and may have been modified by the application + since the simulation step started. + + */ + + PxTransform transform[2]; + + /** + \brief An array of contact points between these two shapes. + */ + + PxContactSet contacts; +}; + + +/** +\brief An interface class that the user can implement in order to modify contact constraints. + +Threading: It is necessary to make this class thread safe as it will be called in the context of the +simulation thread. It might also be necessary to make it reentrant, since some calls can be made by multi-threaded +parts of the physics engine. + +You can enable the use of this contact modification callback by raising the flag PxPairFlag::eMODIFY_CONTACTS in +the filter shader/callback (see #PxSimulationFilterShader) for a pair of rigid body objects. + +Please note: ++ Raising the contact modification flag will not wake the actors up automatically. ++ It is not possible to turn off the performance degradation by simply removing the callback from the scene, the + filter shader/callback has to be used to clear the contact modification flag. ++ The contacts will only be reported as long as the actors are awake. There will be no callbacks while the actors are sleeping. + +@see PxScene.setContactModifyCallback() PxScene.getContactModifyCallback() +*/ +class PxContactModifyCallback +{ +public: + + /** + \brief Passes modifiable arrays of contacts to the application. + + The initial contacts are as determined fresh each frame by collision detection. + + The number of contacts can not be changed, so you cannot add your own contacts. You may however + disable contacts using PxContactSet::ignore(). + + @see PxContactModifyPair + */ + virtual void onContactModify(PxContactModifyPair* const pairs, PxU32 count) = 0; + +protected: + virtual ~PxContactModifyCallback(){} +}; + +/** +\brief An interface class that the user can implement in order to modify CCD contact constraints. + +Threading: It is necessary to make this class thread safe as it will be called in the context of the +simulation thread. It might also be necessary to make it reentrant, since some calls can be made by multi-threaded +parts of the physics engine. + +You can enable the use of this contact modification callback by raising the flag PxPairFlag::eMODIFY_CONTACTS in +the filter shader/callback (see #PxSimulationFilterShader) for a pair of rigid body objects. + +Please note: ++ Raising the contact modification flag will not wake the actors up automatically. ++ It is not possible to turn off the performance degradation by simply removing the callback from the scene, the + filter shader/callback has to be used to clear the contact modification flag. ++ The contacts will only be reported as long as the actors are awake. There will be no callbacks while the actors are sleeping. + +@see PxScene.setContactModifyCallback() PxScene.getContactModifyCallback() +*/ +class PxCCDContactModifyCallback +{ +public: + + /** + \brief Passes modifiable arrays of contacts to the application. + + The initial contacts are as determined fresh each frame by collision detection. + + The number of contacts can not be changed, so you cannot add your own contacts. You may however + disable contacts using PxContactSet::ignore(). + + @see PxContactModifyPair + */ + virtual void onCCDContactModify(PxContactModifyPair* const pairs, PxU32 count) = 0; + +protected: + virtual ~PxCCDContactModifyCallback(){} +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxDeletionListener.h b/src/PhysX/physx/include/PxDeletionListener.h new file mode 100644 index 000000000..0ca180360 --- /dev/null +++ b/src/PhysX/physx/include/PxDeletionListener.h @@ -0,0 +1,106 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_DELETIONLISTENER +#define PX_PHYSICS_NX_DELETIONLISTENER +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + +/** +\brief Flags specifying deletion event types. + +@see PxDeletionListener::onRelease PxPhysics.registerDeletionListener() +*/ +struct PxDeletionEventFlag +{ + enum Enum + { + eUSER_RELEASE = (1<<0), //!< The user has called release on an object. + eMEMORY_RELEASE = (1<<1) //!< The destructor of an object has been called and the memory has been released. + }; +}; + +/** +\brief Collection of set bits defined in PxDeletionEventFlag. + +@see PxDeletionEventFlag +*/ +typedef PxFlags PxDeletionEventFlags; +PX_FLAGS_OPERATORS(PxDeletionEventFlag::Enum,PxU8) + + +/** +\brief interface to get notification on object deletion + +*/ +class PxDeletionListener +{ +public: + /** + \brief Notification if an object or its memory gets released + + If release() gets called on a PxBase object, an eUSER_RELEASE event will get fired immediately. The object state can be queried in the callback but + it is not allowed to change the state. Furthermore, when reading from the object it is the user's responsibility to make sure that no other thread + is writing at the same time to the object (this includes the simulation itself, i.e., #PxScene::fetchResults() must not get called at the same time). + + Calling release() on a PxBase object does not necessarily trigger its destructor immediately. For example, the object can be shared and might still + be referenced by other objects or the simulation might still be running and accessing the object state. In such cases the destructor will be called + as soon as it is safe to do so. After the destruction of the object and its memory, an eMEMORY_RELEASE event will get fired. In this case it is not + allowed to dereference the object pointer in the callback. + + \param[in] observed The object for which the deletion event gets fired. + \param[in] userData The user data pointer of the object for which the deletion event gets fired. Not available for all object types in which case it will be set to 0. + \param[in] deletionEvent The type of deletion event. Do not dereference the object pointer argument if the event is eMEMORY_RELEASE. + + */ + virtual void onRelease(const PxBase* observed, void* userData, PxDeletionEventFlag::Enum deletionEvent) = 0; + +protected: + PxDeletionListener() {} + virtual ~PxDeletionListener() {} +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxFiltering.h b/src/PhysX/physx/include/PxFiltering.h new file mode 100644 index 000000000..b3a7f5289 --- /dev/null +++ b/src/PhysX/physx/include/PxFiltering.h @@ -0,0 +1,733 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_FILTERING +#define PX_PHYSICS_NX_FILTERING +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxActor; +class PxShape; + +static const PxU32 INVALID_FILTER_PAIR_INDEX = 0xffffffff; + +/** +\brief Collection of flags describing the actions to take for a collision pair. + +@see PxPairFlags PxSimulationFilterShader.filter() PxSimulationFilterCallback +*/ +struct PxPairFlag +{ + enum Enum + { + /** + \brief Process the contacts of this collision pair in the dynamics solver. + + \note Only takes effect if the colliding actors are rigid bodies. + */ + eSOLVE_CONTACT = (1<<0), + + /** + \brief Call contact modification callback for this collision pair + + \note Only takes effect if the colliding actors are rigid bodies. + + @see PxContactModifyCallback + */ + eMODIFY_CONTACTS = (1<<1), + + /** + \brief Call contact report callback or trigger callback when this collision pair starts to be in contact. + + If one of the two collision objects is a trigger shape (see #PxShapeFlag::eTRIGGER_SHAPE) + then the trigger callback will get called as soon as the other object enters the trigger volume. + If none of the two collision objects is a trigger shape then the contact report callback will get + called when the actors of this collision pair start to be in contact. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() PxSimulationEventCallback.onTrigger() + */ + eNOTIFY_TOUCH_FOUND = (1<<2), + + /** + \brief Call contact report callback while this collision pair is in contact + + If none of the two collision objects is a trigger shape then the contact report callback will get + called while the actors of this collision pair are in contact. + + \note Triggers do not support this event. Persistent trigger contacts need to be tracked separately by observing eNOTIFY_TOUCH_FOUND/eNOTIFY_TOUCH_LOST events. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note No report will get sent if the objects in contact are sleeping. + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + \note If this flag gets enabled while a pair is in touch already, there will be no eNOTIFY_TOUCH_PERSISTS events until the pair loses and regains touch. + + @see PxSimulationEventCallback.onContact() PxSimulationEventCallback.onTrigger() + */ + eNOTIFY_TOUCH_PERSISTS = (1<<3), + + /** + \brief Call contact report callback or trigger callback when this collision pair stops to be in contact + + If one of the two collision objects is a trigger shape (see #PxShapeFlag::eTRIGGER_SHAPE) + then the trigger callback will get called as soon as the other object leaves the trigger volume. + If none of the two collision objects is a trigger shape then the contact report callback will get + called when the actors of this collision pair stop to be in contact. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note This event will also get triggered if one of the colliding objects gets deleted. + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() PxSimulationEventCallback.onTrigger() + */ + eNOTIFY_TOUCH_LOST = (1<<4), + + /** + \brief Call contact report callback when this collision pair is in contact during CCD passes. + + If CCD with multiple passes is enabled, then a fast moving object might bounce on and off the same + object multiple times. Hence, the same pair might be in contact multiple times during a simulation step. + This flag will make sure that all the detected collision during CCD will get reported. For performance + reasons, the system can not always tell whether the contact pair lost touch in one of the previous CCD + passes and thus can also not always tell whether the contact is new or has persisted. eNOTIFY_TOUCH_CCD + just reports when the two collision objects were detected as being in contact during a CCD pass. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note Trigger shapes are not supported. + + \note Only takes effect if eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() PxSimulationEventCallback.onTrigger() + */ + eNOTIFY_TOUCH_CCD = (1<<5), + + /** + \brief Call contact report callback when the contact force between the actors of this collision pair exceeds one of the actor-defined force thresholds. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() + */ + eNOTIFY_THRESHOLD_FORCE_FOUND = (1<<6), + + /** + \brief Call contact report callback when the contact force between the actors of this collision pair continues to exceed one of the actor-defined force thresholds. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note If a pair gets re-filtered and this flag has previously been disabled, then the report will not get fired in the same frame even if the force threshold has been reached in the + previous one (unless #eNOTIFY_THRESHOLD_FORCE_FOUND has been set in the previous frame). + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() + */ + eNOTIFY_THRESHOLD_FORCE_PERSISTS = (1<<7), + + /** + \brief Call contact report callback when the contact force between the actors of this collision pair falls below one of the actor-defined force thresholds (includes the case where this collision pair stops being in contact). + + \note Only takes effect if the colliding actors are rigid bodies. + + \note If a pair gets re-filtered and this flag has previously been disabled, then the report will not get fired in the same frame even if the force threshold has been reached in the + previous one (unless #eNOTIFY_THRESHOLD_FORCE_FOUND or #eNOTIFY_THRESHOLD_FORCE_PERSISTS has been set in the previous frame). + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() + */ + eNOTIFY_THRESHOLD_FORCE_LOST = (1<<8), + + /** + \brief Provide contact points in contact reports for this collision pair. + + \note Only takes effect if the colliding actors are rigid bodies and if used in combination with the flags eNOTIFY_TOUCH_... or eNOTIFY_THRESHOLD_FORCE_... + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() PxContactPair PxContactPair.extractContacts() + */ + eNOTIFY_CONTACT_POINTS = (1<<9), + + /** + \brief This flag is used to indicate whether this pair generates discrete collision detection contacts. + + \note Contacts are only responded to if eSOLVE_CONTACT is enabled. + */ + eDETECT_DISCRETE_CONTACT = (1<<10), + + /** + \brief This flag is used to indicate whether this pair generates CCD contacts. + + \note The contacts will only be responded to if eSOLVE_CONTACT is enabled on this pair. + \note The scene must have PxSceneFlag::eENABLE_CCD enabled to use this feature. + \note Non-static bodies of the pair should have PxRigidBodyFlag::eENABLE_CCD specified for this feature to work correctly. + \note This flag is not supported with trigger shapes. However, CCD trigger events can be emulated using non-trigger shapes + and requesting eNOTIFY_TOUCH_FOUND and eNOTIFY_TOUCH_LOST and not raising eSOLVE_CONTACT on the pair. + + @see PxRigidBodyFlag::eENABLE_CCD + @see PxSceneFlag::eENABLE_CCD + */ + eDETECT_CCD_CONTACT = (1<<11), + + /** + \brief Provide pre solver velocities in contact reports for this collision pair. + + If the collision pair has contact reports enabled, the velocities of the rigid bodies before contacts have been solved + will be provided in the contact report callback unless the pair lost touch in which case no data will be provided. + + \note Usually it is not necessary to request these velocities as they will be available by querying the velocity from the provided + PxRigidActor object directly. However, it might be the case that the velocity of a rigid body gets set while the simulation is running + in which case the PxRigidActor would return this new velocity in the contact report callback and not the velocity the simulation used. + + @see PxSimulationEventCallback.onContact(), PxContactPairVelocity, PxContactPairHeader.extraDataStream + */ + ePRE_SOLVER_VELOCITY = (1<<12), + + /** + \brief Provide post solver velocities in contact reports for this collision pair. + + If the collision pair has contact reports enabled, the velocities of the rigid bodies after contacts have been solved + will be provided in the contact report callback unless the pair lost touch in which case no data will be provided. + + @see PxSimulationEventCallback.onContact(), PxContactPairVelocity, PxContactPairHeader.extraDataStream + */ + ePOST_SOLVER_VELOCITY = (1<<13), + + /** + \brief Provide rigid body poses in contact reports for this collision pair. + + If the collision pair has contact reports enabled, the rigid body poses at the contact event will be provided + in the contact report callback unless the pair lost touch in which case no data will be provided. + + \note Usually it is not necessary to request these poses as they will be available by querying the pose from the provided + PxRigidActor object directly. However, it might be the case that the pose of a rigid body gets set while the simulation is running + in which case the PxRigidActor would return this new pose in the contact report callback and not the pose the simulation used. + Another use case is related to CCD with multiple passes enabled, A fast moving object might bounce on and off the same + object multiple times. This flag can be used to request the rigid body poses at the time of impact for each such collision event. + + @see PxSimulationEventCallback.onContact(), PxContactPairPose, PxContactPairHeader.extraDataStream + */ + eCONTACT_EVENT_POSE = (1<<14), + + eNEXT_FREE = (1<<15), //!< For internal use only. + + /** + \brief Provided default flag to do simple contact processing for this collision pair. + */ + eCONTACT_DEFAULT = eSOLVE_CONTACT | eDETECT_DISCRETE_CONTACT, + + /** + \brief Provided default flag to get commonly used trigger behavior for this collision pair. + */ + eTRIGGER_DEFAULT = eNOTIFY_TOUCH_FOUND | eNOTIFY_TOUCH_LOST | eDETECT_DISCRETE_CONTACT + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxPairFlag. + +@see PxPairFlag +*/ +typedef PxFlags PxPairFlags; +PX_FLAGS_OPERATORS(PxPairFlag::Enum, PxU16) + + + +/** +\brief Collection of flags describing the filter actions to take for a collision pair. + +@see PxFilterFlags PxSimulationFilterShader PxSimulationFilterCallback +*/ +struct PxFilterFlag +{ + enum Enum + { + /** + \brief Ignore the collision pair as long as the bounding volumes of the pair objects overlap. + + Killed pairs will be ignored by the simulation and won't run through the filter again until one + of the following occurs: + + \li The bounding volumes of the two objects overlap again (after being separated) + \li The user enforces a re-filtering (see #PxScene::resetFiltering()) + + @see PxScene::resetFiltering() + */ + eKILL = (1<<0), + + /** + \brief Ignore the collision pair as long as the bounding volumes of the pair objects overlap or until filtering relevant data changes for one of the collision objects. + + Suppressed pairs will be ignored by the simulation and won't make another filter request until one + of the following occurs: + + \li Same conditions as for killed pairs (see #eKILL) + \li The filter data or the filter object attributes change for one of the collision objects + + @see PxFilterData PxFilterObjectAttributes + */ + eSUPPRESS = (1<<1), + + /** + \brief Invoke the filter callback (#PxSimulationFilterCallback::pairFound()) for this collision pair. + + @see PxSimulationFilterCallback + */ + eCALLBACK = (1<<2), + + /** + \brief Track this collision pair with the filter callback mechanism. + + When the bounding volumes of the collision pair lose contact, the filter callback #PxSimulationFilterCallback::pairLost() + will be invoked. Furthermore, the filter status of the collision pair can be adjusted through #PxSimulationFilterCallback::statusChange() + once per frame (until a pairLost() notification occurs). + + @see PxSimulationFilterCallback + */ + eNOTIFY = (1<<3) | eCALLBACK, + + /** + \brief Provided default to get standard behavior: + + The application configure the pair's collision properties once when bounding volume overlap is found and + doesn't get asked again about that pair until overlap status or filter properties changes, or re-filtering is requested. + + No notification is provided when bounding volume overlap is lost + + The pair will not be killed or suppressed, so collision detection will be processed + */ + + eDEFAULT = 0 + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxFilterFlag. + +@see PxFilterFlag +*/ +typedef PxFlags PxFilterFlags; +PX_FLAGS_OPERATORS(PxFilterFlag::Enum, PxU16) + + +/** +\brief PxFilterData is user-definable data which gets passed into the collision filtering shader and/or callback. + +@see PxShape.setSimulationFilterData() PxShape.getSimulationFilterData() PxSimulationFilterShader PxSimulationFilterCallback +*/ +struct PxFilterData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PX_INLINE PxFilterData(const PxEMPTY) + { + } + + /** + \brief Default constructor. + */ + PX_INLINE PxFilterData() + { + word0 = word1 = word2 = word3 = 0; + } + + /** + \brief Copy constructor. + */ + PX_INLINE PxFilterData(const PxFilterData& fd) : word0(fd.word0), word1(fd.word1), word2(fd.word2), word3(fd.word3) {} + + /** + \brief Constructor to set filter data initially. + */ + PX_INLINE PxFilterData(PxU32 w0, PxU32 w1, PxU32 w2, PxU32 w3) : word0(w0), word1(w1), word2(w2), word3(w3) {} + + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE void setToDefault() + { + *this = PxFilterData(); + } + + /** + \brief Assignment operator + */ + PX_INLINE void operator = (const PxFilterData& fd) + { + word0 = fd.word0; + word1 = fd.word1; + word2 = fd.word2; + word3 = fd.word3; + } + + /** + \brief Comparison operator to allow use in Array. + */ + PX_INLINE bool operator == (const PxFilterData& a) const + { + return a.word0 == word0 && a.word1 == word1 && a.word2 == word2 && a.word3 == word3; + } + + /** + \brief Comparison operator to allow use in Array. + */ + PX_INLINE bool operator != (const PxFilterData& a) const + { + return !(a == *this); + } + + PxU32 word0; + PxU32 word1; + PxU32 word2; + PxU32 word3; +}; + + +/** +\brief Identifies each type of filter object. + +@see PxGetFilterObjectType() +*/ +struct PxFilterObjectType +{ + enum Enum + { + /** + \brief A static rigid body + @see PxRigidStatic + */ + eRIGID_STATIC, + + /** + \brief A dynamic rigid body + @see PxRigidDynamic + */ + eRIGID_DYNAMIC, + + /** + \brief An articulation + @see PxArticulation + */ + eARTICULATION, + + //brief internal use only! + eMAX_TYPE_COUNT = 16, + + //brief internal use only! + eUNDEFINED = eMAX_TYPE_COUNT-1 + }; +}; + + +// For internal use only +struct PxFilterObjectFlag +{ + enum Enum + { + eKINEMATIC = (1<<4), + eTRIGGER = (1<<5) + }; +}; + + +/** +\brief Structure which gets passed into the collision filtering shader and/or callback providing additional information on objects of a collision pair + +@see PxSimulationFilterShader PxSimulationFilterCallback getActorType() PxFilterObjectIsKinematic() PxFilterObjectIsTrigger() +*/ +typedef PxU32 PxFilterObjectAttributes; + + +/** +\brief Extract filter object type from the filter attributes of a collision pair object + +\param[in] attr The filter attribute of a collision pair object +\return The type of the collision pair object. + +@see PxFilterObjectType +*/ +PX_INLINE PxFilterObjectType::Enum PxGetFilterObjectType(PxFilterObjectAttributes attr) +{ + return PxFilterObjectType::Enum(attr & (PxFilterObjectType::eMAX_TYPE_COUNT-1)); +} + + +/** +\brief Specifies whether the collision object belongs to a kinematic rigid body + +\param[in] attr The filter attribute of a collision pair object +\return True if the object belongs to a kinematic rigid body, else false + +@see PxRigidBodyFlag::eKINEMATIC +*/ +PX_INLINE bool PxFilterObjectIsKinematic(PxFilterObjectAttributes attr) +{ + return (attr & PxFilterObjectFlag::eKINEMATIC) != 0; +} + + +/** +\brief Specifies whether the collision object is a trigger shape + +\param[in] attr The filter attribute of a collision pair object +\return True if the object is a trigger shape, else false + +@see PxShapeFlag::eTRIGGER_SHAPE +*/ +PX_INLINE bool PxFilterObjectIsTrigger(PxFilterObjectAttributes attr) +{ + return (attr & PxFilterObjectFlag::eTRIGGER) != 0; +} + + +/** +\brief Filter shader to specify handling of collision pairs. + +Collision filtering is a mechanism to specify how a pair of potentially colliding objects should be processed by the +simulation. A pair of objects is potentially colliding if the bounding volumes of the two objects overlap. +In short, a collision filter decides whether a collision pair should get processed, temporarily ignored or discarded. +If a collision pair should get processed, the filter can additionally specify how it should get processed, for instance, +whether contacts should get resolved, which callbacks should get invoked or which reports should be sent etc. + +\note A default implementation of a filter shader is provided in the PhysX extensions library, see #PxDefaultSimulationFilterShader. + +@see PxSceneDesc.filterShader PxSimulationFilterCallback +*/ + +/** +\brief Filter method to specify how a pair of potentially colliding objects should be processed. + +Return the PxFilterFlag flags and set the PxPairFlag flags to define what the simulation should do with the given collision pair. + +This methods gets called when: +\li The bounding volumes of two objects start to overlap. +\li The bounding volumes of two objects overlap and the filter data or filter attributes of one of the objects changed +\li A re-filtering was forced through resetFiltering() (see #PxScene::resetFiltering()) +\li Filtering is requested in scene queries + +\note Certain pairs of objects are always ignored and this method does not get called. This is the case for the +following pairs: + +\li Pair of static rigid actors +\li A static rigid actor and a kinematic actor (unless one is a trigger or if explicitly enabled through PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS) +\li Two kinematic actors (unless one is a trigger or if explicitly enabled through PxSceneFlag::eENABLE_KINEMATIC_PAIRS) +\li Two jointed rigid bodies and the joint was defined to disable collision +\li Two articulation links if connected through an articulation joint + +\note This is a performance critical method and should be stateless. You should neither access external objects +from within this method nor should you call external methods that are not inlined. If you need a more complex +logic to filter a collision pair then use the filter callback mechanism for this pair (see #PxSimulationFilterCallback, +#PxFilterFlag::eCALLBACK, #PxFilterFlag::eNOTIFY). + +\param[in] attributes0 The filter attribute of the first object +\param[in] filterData0 The custom filter data of the first object +\param[in] attributes1 The filter attribute of the second object +\param[in] filterData1 The custom filter data of the second object +\param[out] pairFlags Flags giving additional information on how an accepted pair should get processed +\param[in] constantBlock The constant global filter data (see #PxSceneDesc.filterShaderData) +\param[in] constantBlockSize Size of the global filter data (see #PxSceneDesc.filterShaderDataSize) +\return Filter flags defining whether the pair should be discarded, temporarily ignored, processed and whether the +filter callback should get invoked for this pair. + +@see PxSimulationFilterCallback PxFilterData PxFilterObjectAttributes PxFilterFlag PxFilterFlags PxPairFlag PxPairFlags +*/ + +typedef PxFilterFlags (*PxSimulationFilterShader) + (PxFilterObjectAttributes attributes0, PxFilterData filterData0, + PxFilterObjectAttributes attributes1, PxFilterData filterData1, + PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize); + + + +/** +\brief Filter callback to specify handling of collision pairs. + +This class is provided to implement more complex and flexible collision pair filtering logic, for instance, taking +the state of the user application into account. Filter callbacks also give the user the opportunity to track collision +pairs and update their filter state. + +You might want to check the documentation on #PxSimulationFilterShader as well since it includes more general information +on filtering. + +\note SDK state should not be modified from within the callbacks. In particular objects should not +be created or destroyed. If state modification is needed then the changes should be stored to a buffer +and performed after the simulation step. + +\note The callbacks may execute in user threads or simulation threads, possibly simultaneously. The corresponding objects +may have been deleted by the application earlier in the frame. It is the application's responsibility to prevent race conditions +arising from using the SDK API in the callback while an application thread is making write calls to the scene, and to ensure that +the callbacks are thread-safe. Return values which depend on when the callback is called during the frame will introduce nondeterminism +into the simulation. + +@see PxSceneDesc.filterCallback PxSimulationFilterShader +*/ +class PxSimulationFilterCallback +{ +public: + + /** + \brief Filter method to specify how a pair of potentially colliding objects should be processed. + + This method gets called when the filter flags returned by the filter shader (see #PxSimulationFilterShader) + indicate that the filter callback should be invoked (#PxFilterFlag::eCALLBACK or #PxFilterFlag::eNOTIFY set). + Return the PxFilterFlag flags and set the PxPairFlag flags to define what the simulation should do with the given + collision pair. + + \param[in] pairID Unique ID of the collision pair used to issue filter status changes for the pair (see #statusChange()) + \param[in] attributes0 The filter attribute of the first object + \param[in] filterData0 The custom filter data of the first object + \param[in] a0 Actor pointer of the first object + \param[in] s0 Shape pointer of the first object (NULL if the object has no shapes) + \param[in] attributes1 The filter attribute of the second object + \param[in] filterData1 The custom filter data of the second object + \param[in] a1 Actor pointer of the second object + \param[in] s1 Shape pointer of the second object (NULL if the object has no shapes) + \param[in,out] pairFlags In: Pair flags returned by the filter shader. Out: Additional information on how an accepted pair should get processed + \return Filter flags defining whether the pair should be discarded, temporarily ignored or processed and whether the pair + should be tracked and send a report on pair deletion through the filter callback + + @see PxSimulationFilterShader PxFilterData PxFilterObjectAttributes PxFilterFlag PxPairFlag + */ + virtual PxFilterFlags pairFound( PxU32 pairID, + PxFilterObjectAttributes attributes0, PxFilterData filterData0, const PxActor* a0, const PxShape* s0, + PxFilterObjectAttributes attributes1, PxFilterData filterData1, const PxActor* a1, const PxShape* s1, + PxPairFlags& pairFlags) = 0; + + /** + \brief Callback to inform that a tracked collision pair is gone. + + This method gets called when a collision pair disappears or gets re-filtered. Only applies to + collision pairs which have been marked as filter callback pairs (#PxFilterFlag::eNOTIFY set in #pairFound()). + + \param[in] pairID Unique ID of the collision pair that disappeared + \param[in] attributes0 The filter attribute of the first object + \param[in] filterData0 The custom filter data of the first object + \param[in] attributes1 The filter attribute of the second object + \param[in] filterData1 The custom filter data of the second object + \param[in] objectRemoved True if the pair was lost because one of the objects got removed from the scene + + @see pairFound() PxSimulationFilterShader PxFilterData PxFilterObjectAttributes + */ + virtual void pairLost( PxU32 pairID, + PxFilterObjectAttributes attributes0, + PxFilterData filterData0, + PxFilterObjectAttributes attributes1, + PxFilterData filterData1, + bool objectRemoved) = 0; + + /** + \brief Callback to give the opportunity to change the filter state of a tracked collision pair. + + This method gets called once per simulation step to let the application change the filter and pair + flags of a collision pair that has been reported in #pairFound() and requested callbacks by + setting #PxFilterFlag::eNOTIFY. To request a change of filter status, the target pair has to be + specified by its ID, the new filter and pair flags have to be provided and the method should return true. + + \note If this method changes the filter status of a collision pair and the pair should keep being tracked + by the filter callbacks then #PxFilterFlag::eNOTIFY has to be set. + + \note The application is responsible to ensure that this method does not get called for pairs that have been + reported as lost, see #pairLost(). + + \param[out] pairID ID of the collision pair for which the filter status should be changed + \param[out] pairFlags The new pairFlags to apply to the collision pair + \param[out] filterFlags The new filterFlags to apply to the collision pair + \return True if the changes should be applied. In this case the method will get called again. False if + no more status changes should be done in the current simulation step. In that case the provided flags will be discarded. + + @see pairFound() pairLost() PxFilterFlag PxPairFlag + */ + virtual bool statusChange(PxU32& pairID, PxPairFlags& pairFlags, PxFilterFlags& filterFlags) = 0; + +protected: + virtual ~PxSimulationFilterCallback() {} +}; + +struct PxPairFilteringMode +{ + enum Enum + { + /** + Output pair from BP, potentially send to user callbacks, create regular interaction object. + Similar to enabling PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS / PxSceneFlag::eENABLE_KINEMATIC_PAIRS. + */ + eKEEP, + + /** + Output pair from BP, create interaction marker. Can be later switched to regular interaction. + Similar to disabling PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS / PxSceneFlag::eENABLE_KINEMATIC_PAIRS. + */ + eSUPPRESS, + + /** + Don't output pair from BP. Cannot be later switched to regular interaction, needs "resetFiltering" call. + */ + eKILL, + + /** + Default is to ignore the mode and use PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS and PxSceneFlag::eENABLE_KINEMATIC_PAIRS instead (compatibility). + */ + eDEFAULT + }; +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxForceMode.h b/src/PhysX/physx/include/PxForceMode.h new file mode 100644 index 000000000..bac6253ec --- /dev/null +++ b/src/PhysX/physx/include/PxForceMode.h @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_FORCE_MODE +#define PX_PHYSICS_NX_FORCE_MODE + +#include "foundation/PxPreprocessor.h" + +/** \addtogroup physics +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Parameter to addForce() and addTorque() calls, determines the exact operation that is carried out. + +@see PxRigidBody.addForce() PxRigidBody.addTorque() +*/ +struct PxForceMode +{ + enum Enum + { + eFORCE, //!< parameter has unit of mass * distance/ time^2, i.e. a force + eIMPULSE, //!< parameter has unit of mass * distance /time + eVELOCITY_CHANGE, //!< parameter has unit of distance / time, i.e. the effect is mass independent: a velocity change. + eACCELERATION //!< parameter has unit of distance/ time^2, i.e. an acceleration. It gets treated just like a force except the mass is not divided out before integration. + }; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxFoundation.h b/src/PhysX/physx/include/PxFoundation.h new file mode 100644 index 000000000..8a1104c5b --- /dev/null +++ b/src/PhysX/physx/include/PxFoundation.h @@ -0,0 +1,158 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_FOUNDATION_PX_FOUNDATION_H +#define PX_FOUNDATION_PX_FOUNDATION_H + +/** \addtogroup foundation + @{ +*/ + +#include "foundation/Px.h" +#include "foundation/PxErrors.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Foundation SDK singleton class. + +You need to have an instance of this class to instance the higher level SDKs. +*/ +class PX_FOUNDATION_API PxFoundation +{ + public: + /** + \brief Destroys the instance it is called on. + + The operation will fail, if there are still modules referencing the foundation object. Release all dependent modules + prior + to calling this method. + + @see PxCreateFoundation() + */ + virtual void release() = 0; + + /** + retrieves error callback + */ + virtual PxErrorCallback& getErrorCallback() = 0; + + /** + Sets mask of errors to report. + */ + virtual void setErrorLevel(PxErrorCode::Enum mask = PxErrorCode::eMASK_ALL) = 0; + + /** + Retrieves mask of errors to be reported. + */ + virtual PxErrorCode::Enum getErrorLevel() const = 0; + + /** + Retrieves the allocator this object was created with. + */ + virtual PxAllocatorCallback& getAllocatorCallback() = 0; + + /** + Retrieves if allocation names are being passed to allocator callback. + */ + virtual bool getReportAllocationNames() const = 0; + + /** + Set if allocation names are being passed to allocator callback. + \details Enabled by default in debug and checked build, disabled by default in profile and release build. + */ + virtual void setReportAllocationNames(bool value) = 0; + + protected: + virtual ~PxFoundation() + { + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** +\brief Creates an instance of the foundation class + +The foundation class is needed to initialize higher level SDKs. There may be only one instance per process. +Calling this method after an instance has been created already will result in an error message and NULL will be +returned. + +\param version Version number we are expecting (should be #PX_PHYSICS_VERSION) +\param allocator User supplied interface for allocating memory(see #PxAllocatorCallback) +\param errorCallback User supplied interface for reporting errors and displaying messages(see #PxErrorCallback) +\return Foundation instance on success, NULL if operation failed + +@see PxFoundation +*/ + +PX_C_EXPORT PX_FOUNDATION_API physx::PxFoundation* PX_CALL_CONV +PxCreateFoundation(physx::PxU32 version, physx::PxAllocatorCallback& allocator, physx::PxErrorCallback& errorCallback); +/** +\brief Retrieves the Foundation SDK after it has been created. + +\note The behavior of this method is undefined if the foundation instance has not been created already. + +@see PxCreateFoundation() +*/ +#if PX_CLANG +#if PX_LINUX +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#endif // PX_LINUX +#endif // PX_CLANG +PX_C_EXPORT PX_FOUNDATION_API physx::PxFoundation& PX_CALL_CONV PxGetFoundation(); +#if PX_CLANG +#if PX_LINUX +#pragma clang diagnostic pop +#endif // PX_LINUX +#endif // PX_CLANG + +namespace physx +{ +class PxProfilerCallback; +} + +/** +\brief Get the callback that will be used for all profiling. +*/ +PX_C_EXPORT PX_FOUNDATION_API physx::PxProfilerCallback* PX_CALL_CONV PxGetProfilerCallback(); + +/** +\brief Set the callback that will be used for all profiling. +*/ +PX_C_EXPORT PX_FOUNDATION_API void PX_CALL_CONV PxSetProfilerCallback(physx::PxProfilerCallback* profiler); + +/** @} */ +#endif // PX_FOUNDATION_PX_FOUNDATION_H diff --git a/src/PhysX/physx/include/PxImmediateMode.h b/src/PhysX/physx/include/PxImmediateMode.h new file mode 100644 index 000000000..827d2a3ec --- /dev/null +++ b/src/PhysX/physx/include/PxImmediateMode.h @@ -0,0 +1,228 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_IMMEDIATE_MODE +#define PX_PHYSICS_IMMEDIATE_MODE +/** \addtogroup immediatemode +@{ */ + +#include "PxPhysXConfig.h" +#include "solver/PxSolverDefs.h" +#include "collision/PxCollisionDefs.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#if !PX_DOXYGEN +namespace immediate +{ +#endif + + /** + \brief Structure to store rigid body properties + */ + struct PxRigidBodyData + { + PX_ALIGN(16, PxVec3 linearVelocity); //!< 12 Linear velocity + PxReal invMass; //!< 16 Inverse mass + PxVec3 angularVelocity; //!< 28 Angular velocity + PxReal maxDepenetrationVelocity; //!< 32 Maximum de-penetration velocity + PxVec3 invInertia; //!< 44 Mass-space inverse interia diagonal vector + PxReal maxContactImpulse; //!< 48 Maximum permissable contact impulse + PxTransform body2World; //!< 76 World space transform + PxReal linearDamping; //!< 80 Linear damping coefficient + PxReal angularDamping; //!< 84 Angular damping coefficient + PxReal maxLinearVelocitySq; //!< 88 Squared maximum linear velocity + PxReal maxAngularVelocitySq; //!< 92 Squared maximum angular velocity + PxU32 pad; //!< 96 Padding for 16-byte alignment + }; + + /** + \brief Callback class to record contact points produced by immediate::PxGenerateContacts + */ + class PxContactRecorder + { + public: + /** + \brief Method to record new contacts + \param[in] contactPoints The contact points produced + \param[in] nbContacts The number of contact points produced + \param[in] index The index of this pair. This is an index from 0-N-1 identifying which pair this relates to from within the array of pairs passed to PxGenerateContacts + \return a boolean to indicate if this callback successfully stored the contacts or not. + */ + virtual bool recordContacts(const Gu::ContactPoint* contactPoints, const PxU32 nbContacts, const PxU32 index) = 0; + + virtual ~PxContactRecorder(){} + }; + + /** + \brief Constructs a PxSolverBodyData structure based on rigid body properties. Applies gravity, damping and clamps maximum velocity. + \param[in] inRigidData The array rigid body properties + \param[out] outSolverBodyData The array of solverBodyData produced to repreent these bodies + \param[in] nbBodies The total number of solver bodies to create + \param[in] gravity The gravity vector + \param[in] dt The timestep + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PxConstructSolverBodies(const PxRigidBodyData* inRigidData, PxSolverBodyData* outSolverBodyData, const PxU32 nbBodies, const PxVec3& gravity, const PxReal dt); + + /** + \brief Constructs a PxSolverBodyData structure for a static body at a given pose. + \param[in] globalPose The pose of this static actor + \param[out] solverBodyData The solver body representation of this static actor + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PxConstructStaticSolverBody(const PxTransform& globalPose,PxSolverBodyData& solverBodyData); + + /** + \brief Groups together sets of independent PxSolverConstraintDesc objects to be solved using SIMD SOA approach. + \param[in] solverConstraintDescs the set of solver constraint descs to batch + \param[in] nbConstraints The number of constraints to batch + \param[in,out] solverBodies The array of solver bodies that the constraints reference. Some fields in these structures are written to as scratch memory for the batching. + \param[in] nbBodies The number of bodies + \param[out] outBatchHeaders The batch headers produced by this batching process. This array must have at least 1 entry per input constraint + \param[out] outOrderedConstraintDescs A reordered copy of the constraint descs. This array is referenced by the constraint batches. This array must have at least 1 entry per input constraint. + \return The total number of batches produced. This should be less than or equal to nbConstraints. + + \note This method considers all bodies within the range [0, nbBodies-1] to be valid dynamic bodies. A given dynamic body can only be referenced in a batch once. Static or kinematic bodies can be + referenced multiple times within a batch safely because constraints do not affect their velocities. The batching will implicitly consider any bodies outside of the range [0, nbBodies-1] to be + infinite mass (static or kinematic). This means that either appending static/kinematic to the end of the array of bodies or placing static/kinematic bodies at before the start body pointer + will ensure that the minimum number of batches are produced. + */ + PX_C_EXPORT PX_PHYSX_CORE_API PxU32 PxBatchConstraints(PxSolverConstraintDesc* solverConstraintDescs, const PxU32 nbConstraints, PxSolverBody* solverBodies, PxU32 nbBodies, PxConstraintBatchHeader* outBatchHeaders, + PxSolverConstraintDesc* outOrderedConstraintDescs); + + /** + + \brief Creates a set of contact constraint blocks. Note that, depending the results of PxBatchConstraints, each batchHeader may refer to up to 4 solverConstraintDescs. + This function will allocate both constraint and friction patch data via the PxConstraintAllocator provided. Constraint data is only valid until PxSolveConstraints has completed. + Friction data is to be retained and provided by the application for friction correlation. + + \param[in] batchHeader Array of batch headers to process + \param[in] nbHeaders The total number of headers + \param[in] contactDescs An array of contact descs defining the pair and contact properties of each respective contacting pair + \param[in] allocator An allocator callback to allocate constraint and friction memory + \param[in] invDt The inverse timestep + \param[in] bounceThreshold The bounce threshold. Relative velocities below this will be solved by bias only. Relative velocities above this will be solved by restitution. If restitution is zero + then these pairs will always be solved by bias. + \param[in] frictionOffsetThreshold The friction offset threshold. Contacts whose separations are below this threshold can generate friction constraints. + \param[in] correlationDistance The correlation distance used by friction correlation to identify whether a friction patch is broken on the grounds of relation separation. + + \return a boolean to define if this method was successful or not. + */ + PX_C_EXPORT PX_PHYSX_CORE_API bool PxCreateContactConstraints(PxConstraintBatchHeader* batchHeader, const PxU32 nbHeaders, PxSolverContactDesc* contactDescs, + PxConstraintAllocator& allocator, PxReal invDt, PxReal bounceThreshold, PxReal frictionOffsetThreshold, PxReal correlationDistance); + + /** + \brief Creates a set of joint constraint blocks. Note that, depending the results of PxBatchConstraints, the batchHeader may refer to up to 4 solverConstraintDescs + \param[in] batchHeader The array of batch headers to be processed + \param[in] nbHeaders The total number of batch headers to process + \param[in] jointDescs An array of constraint prep descs defining the properties of the constraints being created + \param[in] allocator An allocator callback to allocate constraint data + \param[in] dt The timestep + \param[in] invDt The inverse timestep + \return a boolean indicating if this method was successful or not. + */ + PX_C_EXPORT PX_PHYSX_CORE_API bool PxCreateJointConstraints(PxConstraintBatchHeader* batchHeader, const PxU32 nbHeaders, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, PxReal dt, PxReal invDt); + + /** + \brief Creates a set of joint constraint blocks. This function runs joint shaders defined inside PxConstraint** param, fills in joint row information in jointDescs and then calls PxCreateJointConstraints. + \param[in] batchHeader The set of batchHeaders to be processed + \param[in] nbBatchHeaders The number of batch headers to process. + \param[in] constraints The set of constraints to be used to produce constraint rows + \param[in,out] jointDescs An array of constraint prep descs defining the properties of the constraints being created + \param[in] allocator An allocator callback to allocate constraint data + \param[in] dt The timestep + \param[in] invDt The inverse timestep + \return a boolean indicating if this method was successful or not. + */ + PX_C_EXPORT PX_PHYSX_CORE_API bool PxCreateJointConstraintsWithShaders(PxConstraintBatchHeader* batchHeader, const PxU32 nbBatchHeaders, PxConstraint** constraints, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, PxReal dt, PxReal invDt); + + /** + \brief Iteratively solves the set of constraints defined by the provided PxConstraintBatchHeader and PxSolverConstraintDesc structures. Updates deltaVelocities inside the PxSolverBody structures. Produces resulting linear and angular motion velocities. + \param[in] batchHeaders The set of batch headers to be solved + \param[in] nbBatchHeaders The total number of batch headers to be solved + \param[in] solverConstraintDescs The reordererd set of solver constraint descs referenced by the batch headers + \param[in,out] solverBodies The set of solver bodies the bodies reference + \param[out] linearMotionVelocity The resulting linear motion velocity + \param[out] angularMotionVelocity The resulting angular motion velocity. + \param[in] nbSolverBodies The total number of solver bodies + \param[in] nbPositionIterations The number of position iterations to run + \param[in] nbVelocityIterations The number of velocity iterations to run + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PxSolveConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbBatchHeaders, PxSolverConstraintDesc* solverConstraintDescs, PxSolverBody* solverBodies, + PxVec3* linearMotionVelocity, PxVec3* angularMotionVelocity, const PxU32 nbSolverBodies, const PxU32 nbPositionIterations, const PxU32 nbVelocityIterations); + + /** + \brief Integrates a rigid body, returning the new velocities and transforms. After this function has been called, solverBodyData stores all the body's velocity data. + + \param[in,out] solverBodyData The array of solver body data to be integrated + \param[in] solverBody The bodies' linear and angular velocities + \param[in] linearMotionVelocity The bodies' linear motion velocity array + \param[in] angularMotionState The bodies' angular motion velocity array + \param[in] nbBodiesToIntegrate The total number of bodies to integrate + \param[in] dt The timestep + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PxIntegrateSolverBodies(PxSolverBodyData* solverBodyData, PxSolverBody* solverBody, const PxVec3* linearMotionVelocity, const PxVec3* angularMotionState, const PxU32 nbBodiesToIntegrate, PxReal dt); + + /** + abrief Performs contact generation for a given pair of geometries at the specified poses. Produced contacts are stored in the provided Gu::ContactBuffer. Information is cached in PxCache structure + to accelerate future contact generation between pairs. This cache data is valid only as long as the memory provided by PxCacheAllocator has not been released/re-used. Recommendation is to + retain that data for a single simulation frame, discarding cached data after 2 frames. If the cached memory has been released/re-used prior to the corresponding pair having contact generation + performed again, it is the application's responsibility to reset the PxCache. + + \param[in] geom0 Array of geometries to perform collision detection on. + \param[in] geom1 Array of geometries to perform collision detection on + \param[in] pose0 Array of poses associated with the corresponding entry in the geom0 array + \param[in] pose1 Array of poses associated with the corresponding entry in the geom1 array + \param[in,out] contactCache Array of contact caches associated with each pair geom0[i] + geom1[i] + \param[in] nbPairs The total number of pairs to process + \param[in] contactRecorder A callback that is called to record contacts for each pair that detects contacts + \param[in] contactDistance The distance at which contacts begin to be generated between the pairs + \param[in] meshContactMargin The mesh contact margin. + \param[in] toleranceLength The toleranceLength. Used for scaling distance-based thresholds internally to produce appropriate results given simulations in different units + \param[in] allocator A callback to allocate memory for the contact cache + + \return a boolean indicating if the function was successful or not. + */ + PX_C_EXPORT PX_PHYSX_CORE_API bool PxGenerateContacts(const PxGeometry* const * geom0, const PxGeometry* const * geom1, const PxTransform* pose0, const PxTransform* pose1, PxCache* contactCache, const PxU32 nbPairs, PxContactRecorder& contactRecorder, + const PxReal contactDistance, const PxReal meshContactMargin, const PxReal toleranceLength, PxCacheAllocator& allocator); + +#if !PX_DOXYGEN +} +#endif + + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif + diff --git a/src/PhysX/physx/include/PxLockedData.h b/src/PhysX/physx/include/PxLockedData.h new file mode 100644 index 000000000..11b0b829e --- /dev/null +++ b/src/PhysX/physx/include/PxLockedData.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_LOCKED_DATA +#define PX_PHYSICS_NX_LOCKED_DATA +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxDataAccessFlag +{ + enum Enum + { + eREADABLE = (1 << 0), + eWRITABLE = (1 << 1), + eDEVICE = (1 << 2) + }; +}; + +/** +\brief collection of set bits defined in PxDataAccessFlag. + +@see PxDataAccessFlag +*/ +typedef PxFlags PxDataAccessFlags; +PX_FLAGS_OPERATORS(PxDataAccessFlag::Enum,PxU8) + + +/** +\brief Parent class for bulk data that is shared between the SDK and the application. +*/ +class PxLockedData +{ +public: + + /** + \brief Any combination of PxDataAccessFlag::eREADABLE and PxDataAccessFlag::eWRITABLE + @see PxDataAccessFlag + */ + virtual PxDataAccessFlags getDataAccessFlags() = 0; + + /** + \brief Unlocks the bulk data. + */ + virtual void unlock() = 0; + + /** + \brief virtual destructor + */ + virtual ~PxLockedData() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxMaterial.h b/src/PhysX/physx/include/PxMaterial.h new file mode 100644 index 000000000..24dc74a3d --- /dev/null +++ b/src/PhysX/physx/include/PxMaterial.h @@ -0,0 +1,324 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NXMATERIAL +#define PX_PHYSICS_NXMATERIAL +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxScene; + +/** +\brief Flags which control the behavior of a material. + +@see PxMaterial +*/ +struct PxMaterialFlag +{ + enum Enum + { + + /** + If this flag is set, friction computations are always skipped between shapes with this material and any other shape. + */ + eDISABLE_FRICTION = 1 << 0, + + /** + The difference between "normal" and "strong" friction is that the strong friction feature + remembers the "friction error" between simulation steps. The friction is a force trying to + hold objects in place (or slow them down) and this is handled in the solver. But since the + solver is only an approximation, the result of the friction calculation can include a small + "error" - e.g. a box resting on a slope should not move at all if the static friction is in + action, but could slowly glide down the slope because of a small friction error in each + simulation step. The strong friction counter-acts this by remembering the small error and + taking it to account during the next simulation step. + + However, in some cases the strong friction could cause problems, and this is why it is + possible to disable the strong friction feature by setting this flag. One example is + raycast vehicles, that are sliding fast across the surface, but still need a precise + steering behavior. It may be a good idea to reenable the strong friction when objects + are coming to a rest, to prevent them from slowly creeping down inclines. + + Note: This flag only has an effect if the PxMaterialFlag::eDISABLE_FRICTION bit is 0. + */ + eDISABLE_STRONG_FRICTION = 1 << 1 + }; +}; + +/** +\brief collection of set bits defined in PxMaterialFlag. + +@see PxMaterialFlag +*/ +typedef PxFlags PxMaterialFlags; +PX_FLAGS_OPERATORS(PxMaterialFlag::Enum,PxU16) + + +/** +\brief enumeration that determines the way in which two material properties will be combined to yield a friction or restitution coefficient for a collision. + +When two actors come in contact with each other, they each have materials with various coefficients, but we only need a single set of coefficients for the pair. + +Physics doesn't have any inherent combinations because the coefficients are determined empirically on a case by case +basis. However, simulating this with a pairwise lookup table is often impractical. + +For this reason the following combine behaviors are available: + +eAVERAGE +eMIN +eMULTIPLY +eMAX + +The effective combine mode for the pair is maximum(material0.combineMode, material1.combineMode). + +@see PxMaterial.setFrictionCombineMode() PxMaterial.getFrictionCombineMode() PxMaterial.setRestitutionCombineMode() PxMaterial.getFrictionCombineMode() +*/ +struct PxCombineMode +{ + enum Enum + { + eAVERAGE = 0, //!< Average: (a + b)/2 + eMIN = 1, //!< Minimum: minimum(a,b) + eMULTIPLY = 2, //!< Multiply: a*b + eMAX = 3, //!< Maximum: maximum(a,b) + eN_VALUES = 4, //!< This is not a valid combine mode, it is a sentinel to denote the number of possible values. We assert that the variable's value is smaller than this. + ePAD_32 = 0x7fffffff //!< This is not a valid combine mode, it is to assure that the size of the enum type is big enough. + }; +}; + +/** +\brief Material class to represent a set of surface properties. + +@see PxPhysics.createMaterial +*/ +class PxMaterial : public PxBase +{ +public: + + /** + \brief Decrements the reference count of a material and releases it if the new reference count is zero. + + @see PxPhysics.createMaterial() + */ + virtual void release() = 0; + + /** + \brief Returns the reference count of the material. + + At creation, the reference count of the material is 1. Every shape referencing this material increments the + count by 1. When the reference count reaches 0, and only then, the material gets destroyed automatically. + + \return the current reference count. + */ + virtual PxU32 getReferenceCount() const = 0; + + /** + \brief Acquires a counted reference to a material. + + This method increases the reference count of the material by 1. Decrement the reference count by calling release() + */ + virtual void acquireReference() = 0; + + /** + \brief Sets the coefficient of dynamic friction. + + The coefficient of dynamic friction should be in [0, PX_MAX_F32). If set to greater than staticFriction, the effective value of staticFriction will be increased to match. + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] coef Coefficient of dynamic friction. Range: [0, PX_MAX_F32) + + @see getDynamicFriction() + */ + virtual void setDynamicFriction(PxReal coef) = 0; + + /** + \brief Retrieves the DynamicFriction value. + + \return The coefficient of dynamic friction. + + @see setDynamicFriction + */ + virtual PxReal getDynamicFriction() const = 0; + + /** + \brief Sets the coefficient of static friction + + The coefficient of static friction should be in the range [0, PX_MAX_F32) + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] coef Coefficient of static friction. Range: [0, PX_MAX_F32) + + @see getStaticFriction() + */ + virtual void setStaticFriction(PxReal coef) = 0; + + /** + \brief Retrieves the coefficient of static friction. + \return The coefficient of static friction. + + @see setStaticFriction + */ + virtual PxReal getStaticFriction() const = 0; + + /** + \brief Sets the coefficient of restitution + + A coefficient of 0 makes the object bounce as little as possible, higher values up to 1.0 result in more bounce. + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] rest Coefficient of restitution. Range: [0,1] + + @see getRestitution() + */ + virtual void setRestitution(PxReal rest) = 0; + + /** + \brief Retrieves the coefficient of restitution. + + See #setRestitution. + + \return The coefficient of restitution. + + @see setRestitution() + */ + virtual PxReal getRestitution() const = 0; + + /** + \brief Raises or clears a particular material flag. + + See the list of flags #PxMaterialFlag + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] flag The PxMaterial flag to raise(set) or clear. + + @see getFlags() PxMaterialFlag + */ + virtual void setFlag(PxMaterialFlag::Enum flag, bool) = 0; + + + /** + \brief sets all the material flags. + + See the list of flags #PxMaterialFlag + + Sleeping: Does NOT wake any actors which may be affected. + + */ + virtual void setFlags( PxMaterialFlags inFlags ) = 0; + + /** + \brief Retrieves the flags. See #PxMaterialFlag. + + \return The material flags. + + @see PxMaterialFlag setFlags() + */ + virtual PxMaterialFlags getFlags() const = 0; + + /** + \brief Sets the friction combine mode. + + See the enum ::PxCombineMode . + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] combMode Friction combine mode to set for this material. See #PxCombineMode. + + @see PxCombineMode getFrictionCombineMode setStaticFriction() setDynamicFriction() + */ + virtual void setFrictionCombineMode(PxCombineMode::Enum combMode) = 0; + + /** + \brief Retrieves the friction combine mode. + + See #setFrictionCombineMode. + + \return The friction combine mode for this material. + + @see PxCombineMode setFrictionCombineMode() + */ + virtual PxCombineMode::Enum getFrictionCombineMode() const = 0; + + /** + \brief Sets the restitution combine mode. + + See the enum ::PxCombineMode . + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] combMode Restitution combine mode for this material. See #PxCombineMode. + + @see PxCombineMode getRestitutionCombineMode() setRestitution() + */ + virtual void setRestitutionCombineMode(PxCombineMode::Enum combMode) = 0; + + /** + \brief Retrieves the restitution combine mode. + + See #setRestitutionCombineMode. + + \return The coefficient of restitution combine mode for this material. + + @see PxCombineMode setRestitutionCombineMode getRestitution() + */ + virtual PxCombineMode::Enum getRestitutionCombineMode() const = 0; + + //public variables: + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. + + virtual const char* getConcreteTypeName() const { return "PxMaterial"; } + +protected: + PX_INLINE PxMaterial(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags), userData(NULL) {} + PX_INLINE PxMaterial(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxMaterial() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxMaterial", name) || PxBase::isKindOf(name); } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxPhysXConfig.h b/src/PhysX/physx/include/PxPhysXConfig.h new file mode 100644 index 000000000..9f532199b --- /dev/null +++ b/src/PhysX/physx/include/PxPhysXConfig.h @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX +#define PX_PHYSICS_NX + +/** Configuration include file for PhysX SDK */ + +/** \addtogroup physics +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysics; +class PxShape; + +class PxActor; +class PxRigidActor; +class PxRigidStatic; +class PxRigidDynamic; +class PxConstraint; +class PxConstraintDesc; + +class PxArticulation; +class PxArticulationReducedCoordinate; +class PxArticulationBase; +class PxArticulationLink; +class PxArticulationJoint; +class PxArticulationJointReducedCoordinate; +class PxArticulationJointBase; + +class PxMaterial; + +class PxScene; +class PxSceneDesc; +class PxTolerancesScale; + +class PxAggregate; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxPhysics.h b/src/PhysX/physx/include/PxPhysics.h new file mode 100644 index 000000000..e05dbc0c6 --- /dev/null +++ b/src/PhysX/physx/include/PxPhysics.h @@ -0,0 +1,729 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NX_PHYSICS +#define PX_PHYSICS_NX_PHYSICS + +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxDeletionListener.h" +#include "foundation/PxTransform.h" +#include "PxShape.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPvd; +class PxPhysicsInsertionCallback; + +class PxRigidActor; +class PxConstraintConnector; +struct PxConstraintShaderTable; + +class PxGeometry; +class PxFoundation; +class PxSerializationRegistry; + +class PxPruningStructure; +class PxBVHStructure; + +/** +\brief Abstract singleton factory class used for instancing objects in the Physics SDK. + +In addition you can use PxPhysics to set global parameters which will effect all scenes and create +objects that can be shared across multiple scenes. + +You can get an instance of this class by calling PxCreateBasePhysics() or PxCreatePhysics() with pre-registered modules. + +@see PxCreatePhysics() PxCreateBasePhysics() PxScene PxVisualizationParameter +*/ +class PxPhysics +{ +public: + + /** @name Basics + */ + //@{ + + virtual ~PxPhysics() {} + + /** + \brief Destroys the instance it is called on. + + Use this release method to destroy an instance of this class. Be sure + to not keep a reference to this object after calling release. + Avoid release calls while a scene is simulating (in between simulate() and fetchResults() calls). + + Note that this must be called once for each prior call to PxCreatePhysics, as + there is a reference counter. Also note that you mustn't destroy the allocator or the error callback (if available) until after the + reference count reaches 0 and the SDK is actually removed. + + Releasing an SDK will also release any scenes, triangle meshes, convex meshes, heightfields and shapes + created through it, provided the user hasn't already done so. + + \note This function is required to be called to release foundation usage. + + @see PxCreatePhysics() + */ + virtual void release() = 0; + + /** + \brief Retrieves the Foundation instance. + \return A reference to the Foundation object. + */ + virtual PxFoundation& getFoundation() = 0; + + /** + \brief Creates an aggregate with the specified maximum size and selfCollision property. + + \param [in] maxSize The maximum number of actors that may be placed in the aggregate. + \param [in] enableSelfCollision Whether the aggregate supports self-collision + \return The new aggregate. + + @see PxAggregate + */ + virtual PxAggregate* createAggregate(PxU32 maxSize, bool enableSelfCollision) = 0; + + /** + \brief Returns the simulation tolerance parameters. + \return The current simulation tolerance parameters. + */ + virtual const PxTolerancesScale& getTolerancesScale() const = 0; + + //@} + /** @name Meshes + */ + //@{ + + /** + \brief Creates a triangle mesh object. + + This can then be instanced into #PxShape objects. + + \param [in] stream The triangle mesh stream. + \return The new triangle mesh. + + @see PxTriangleMesh PxMeshPreprocessingFlag PxTriangleMesh.release() PxInputStream PxTriangleMeshFlag + */ + virtual PxTriangleMesh* createTriangleMesh(PxInputStream& stream) = 0; + + /** + \brief Return the number of triangle meshes that currently exist. + + \return Number of triangle meshes. + + @see getTriangleMeshes() + */ + virtual PxU32 getNbTriangleMeshes() const = 0; + + /** + \brief Writes the array of triangle mesh pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the triangle meshes in the array is not specified. + + \param [out] userBuffer The buffer to receive triangle mesh pointers. + \param [in] bufferSize The number of triangle mesh pointers which can be stored in the buffer. + \param [in] startIndex Index of first mesh pointer to be retrieved + \return The number of triangle mesh pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbTriangleMeshes() PxTriangleMesh + */ + virtual PxU32 getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Creates a heightfield object from previously cooked stream. + + This can then be instanced into #PxShape objects. + + \param [in] stream The heightfield mesh stream. + \return The new heightfield. + + @see PxHeightField PxHeightField.release() PxInputStream PxRegisterHeightFields + */ + virtual PxHeightField* createHeightField(PxInputStream& stream) = 0; + + /** + \brief Return the number of heightfields that currently exist. + + \return Number of heightfields. + + @see getHeightFields() + */ + virtual PxU32 getNbHeightFields() const = 0; + + /** + \brief Writes the array of heightfield pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the heightfields in the array is not specified. + + \param [out] userBuffer The buffer to receive heightfield pointers. + \param [in] bufferSize The number of heightfield pointers which can be stored in the buffer. + \param [in] startIndex Index of first heightfield pointer to be retrieved + \return The number of heightfield pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbHeightFields() PxHeightField + */ + virtual PxU32 getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Creates a convex mesh object. + + This can then be instanced into #PxShape objects. + + \param [in] stream The stream to load the convex mesh from. + \return The new convex mesh. + + @see PxConvexMesh PxConvexMesh.release() PxInputStream createTriangleMesh() PxConvexMeshGeometry PxShape + */ + virtual PxConvexMesh* createConvexMesh(PxInputStream &stream) = 0; + + /** + \brief Return the number of convex meshes that currently exist. + + \return Number of convex meshes. + + @see getConvexMeshes() + */ + virtual PxU32 getNbConvexMeshes() const = 0; + + /** + \brief Writes the array of convex mesh pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the convex meshes in the array is not specified. + + \param [out] userBuffer The buffer to receive convex mesh pointers. + \param [in] bufferSize The number of convex mesh pointers which can be stored in the buffer. + \param [in] startIndex Index of first convex mesh pointer to be retrieved + \return The number of convex mesh pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbConvexMeshes() PxConvexMesh + */ + virtual PxU32 getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Creates a bounding volume hierarchy structure. + + \param [in] stream The stream to load the BVH structure from. + \return The new BVH structure. + + @see PxBVHStructure PxInputStream + */ + virtual PxBVHStructure* createBVHStructure(PxInputStream &stream) = 0; + + /** + \brief Return the number of bounding volume hierarchy structures that currently exist. + + \return Number of bounding volume hierarchy structures. + + @see getBVHStructures() + */ + virtual PxU32 getNbBVHStructures() const = 0; + + /** + \brief Writes the array of bounding volume hierarchy structure pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the BVH structures in the array is not specified. + + \param [out] userBuffer The buffer to receive BVH structure pointers. + \param [in] bufferSize The number of BVH structure pointers which can be stored in the buffer. + \param [in] startIndex Index of first BVH structure pointer to be retrieved + \return The number of BVH structure pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbBVHStructures() PxBVHStructure + */ + virtual PxU32 getBVHStructures(PxBVHStructure** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + //@} + /** @name Scenes + */ + //@{ + + /** + \brief Creates a scene. + + \note Every scene uses a Thread Local Storage slot. This imposes a platform specific limit on the + number of scenes that can be created. + + \param [in] sceneDesc Scene descriptor. See #PxSceneDesc + \return The new scene object. + + @see PxScene PxScene.release() PxSceneDesc + */ + virtual PxScene* createScene(const PxSceneDesc& sceneDesc) = 0; + + /** + \brief Gets number of created scenes. + + \return The number of scenes created. + + @see getScene() + */ + virtual PxU32 getNbScenes() const = 0; + + /** + \brief Writes the array of scene pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the scene pointers in the array is not specified. + + \param [out] userBuffer The buffer to receive scene pointers. + \param [in] bufferSize The number of scene pointers which can be stored in the buffer. + \param [in] startIndex Index of first scene pointer to be retrieved + \return The number of scene pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbScenes() PxScene + */ + virtual PxU32 getScenes(PxScene** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + //@} + /** @name Actors + */ + //@{ + + /** + \brief Creates a static rigid actor with the specified pose and all other fields initialized + to their default values. + + \param [in] pose The initial pose of the actor. Must be a valid transform + + @see PxRigidStatic + */ + virtual PxRigidStatic* createRigidStatic(const PxTransform& pose) = 0; + + /** + \brief Creates a dynamic rigid actor with the specified pose and all other fields initialized + to their default values. + + \param [in] pose The initial pose of the actor. Must be a valid transform + + @see PxRigidDynamic + */ + virtual PxRigidDynamic* createRigidDynamic(const PxTransform& pose) = 0; + + /** + \brief Creates a pruning structure from actors. + + \note Every provided actor needs at least one shape with the eSCENE_QUERY_SHAPE flag set. + \note Both static and dynamic actors can be provided. + \note It is not allowed to pass in actors which are already part of a scene. + \note Articulation links cannot be provided. + + \param [in] actors Array of actors to add to the pruning structure. Must be non NULL. + \param [in] nbActors Number of actors in the array. Must be >0. + \return Pruning structure created from given actors, or NULL if any of the actors did not comply with the above requirements. + @see PxActor PxPruningStructure + */ + virtual PxPruningStructure* createPruningStructure(PxRigidActor*const* actors, PxU32 nbActors) = 0; + + //@} + /** @name Shapes + */ + //@{ + + /** + \brief Creates a shape which may be attached to multiple actors + + The shape will be created with a reference count of 1. + + \param [in] geometry The geometry for the shape + \param [in] material The material for the shape + \param [in] isExclusive Whether this shape is exclusive to a single actor or maybe be shared + \param [in] shapeFlags The PxShapeFlags to be set + + Shared shapes are not mutable when they are attached to an actor + + @see PxShape + */ + PX_FORCE_INLINE PxShape* createShape( const PxGeometry& geometry, + const PxMaterial& material, + bool isExclusive = false, + PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) + { + PxMaterial* materialPtr = const_cast(&material); + return createShape(geometry, &materialPtr, 1, isExclusive, shapeFlags); + } + + /** + \brief Creates a shape which may be attached to multiple actors + + The shape will be created with a reference count of 1. + + \param [in] geometry The geometry for the shape + \param [in] materials The materials for the shape + \param [in] materialCount The number of materials + \param [in] isExclusive Whether this shape is exclusive to a single actor or may be shared + \param [in] shapeFlags The PxShapeFlags to be set + + Shared shapes are not mutable when they are attached to an actor + + @see PxShape + */ + virtual PxShape* createShape(const PxGeometry& geometry, + PxMaterial*const * materials, + PxU16 materialCount, + bool isExclusive = false, + PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) = 0; + + /** + \brief Return the number of shapes that currently exist. + + \return Number of shapes. + + @see getShapes() + */ + virtual PxU32 getNbShapes() const = 0; + + /** + \brief Writes the array of shape pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the shapes in the array is not specified. + + \param [out] userBuffer The buffer to receive shape pointers. + \param [in] bufferSize The number of shape pointers which can be stored in the buffer. + \param [in] startIndex Index of first shape pointer to be retrieved + \return The number of shape pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbShapes() PxShape + */ + virtual PxU32 getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + //@} + /** @name Constraints and Articulations + */ + //@{ + + /** + \brief Creates a constraint shader. + + \note A constraint shader will get added automatically to the scene the two linked actors belong to. Either, but not both, of actor0 and actor1 may + be NULL to denote attachment to the world. + + \param [in] actor0 The first actor + \param [in] actor1 The second actor + \param [in] connector The connector object, which the SDK uses to communicate with the infrastructure for the constraint + \param [in] shaders The shader functions for the constraint + \param [in] dataSize The size of the data block for the shader + + \return The new shader. + + @see PxConstraint + */ + virtual PxConstraint* createConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) = 0; + + /** + \brief Creates an articulation with all fields initialized to their default values. + + \return the new articulation + + @see PxArticulation, PxRegisterArticulations + */ + virtual PxArticulation* createArticulation() = 0; + + /** + \brief Creates a reduced coordinate articulation with all fields initialized to their default values. + + \return the new articulation + + @see PxArticulationReducedCoordinate, PxRegisterArticulationsReducedCoordinate + */ + virtual PxArticulationReducedCoordinate* createArticulationReducedCoordinate() = 0; + + + //@} + /** @name Materials + */ + //@{ + + /** + \brief Creates a new material with default properties. + + \return The new material. + + \param [in] staticFriction The coefficient of static friction + \param [in] dynamicFriction The coefficient of dynamic friction + \param [in] restitution The coefficient of restitution + + @see PxMaterial + */ + virtual PxMaterial* createMaterial(PxReal staticFriction, PxReal dynamicFriction, PxReal restitution) = 0; + + + /** + \brief Return the number of materials that currently exist. + + \return Number of materials. + + @see getMaterials() + */ + virtual PxU32 getNbMaterials() const = 0; + + /** + \brief Writes the array of material pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the materials in the array is not specified. + + \param [out] userBuffer The buffer to receive material pointers. + \param [in] bufferSize The number of material pointers which can be stored in the buffer. + \param [in] startIndex Index of first material pointer to be retrieved + \return The number of material pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbMaterials() PxMaterial + */ + virtual PxU32 getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + //@} + /** @name Deletion Listeners + */ + //@{ + + /** + \brief Register a deletion listener. Listeners will be called whenever an object is deleted. + + It is illegal to register or unregister a deletion listener while deletions are being processed. + + \note By default a registered listener will receive events from all objects. Set the restrictedObjectSet parameter to true on registration and use #registerDeletionListenerObjects to restrict the received events to specific objects. + + \note The deletion events are only supported on core PhysX objects. In general, objects in extension modules do not provide this functionality, however, in the case of PxJoint objects, the underlying PxConstraint will send the events. + + \param [in] observer Observer object to send notifications to. + \param [in] deletionEvents The deletion event types to get notified of. + \param [in] restrictedObjectSet If false, the deletion listener will get events from all objects, else the objects to receive events from have to be specified explicitly through #registerDeletionListenerObjects. + + @see PxDeletionListener unregisterDeletionListener + */ + virtual void registerDeletionListener(PxDeletionListener& observer, const PxDeletionEventFlags& deletionEvents, bool restrictedObjectSet = false) = 0; + + /** + \brief Unregister a deletion listener. + + It is illegal to register or unregister a deletion listener while deletions are being processed. + + \param [in] observer Observer object to send notifications to + + @see PxDeletionListener registerDeletionListener + */ + virtual void unregisterDeletionListener(PxDeletionListener& observer) = 0; + + /** + \brief Register specific objects for deletion events. + + This method allows for a deletion listener to limit deletion events to specific objects only. + + \note It is illegal to register or unregister objects while deletions are being processed. + + \note The deletion listener has to be registered through #registerDeletionListener() and configured to support restricted objects sets prior to this method being used. + + \param [in] observer Observer object to send notifications to. + \param [in] observables List of objects for which to receive deletion events. Only PhysX core objects are supported. In the case of PxJoint objects, the underlying PxConstraint can be used to get the events. + \param [in] observableCount Size of the observables list. + + @see PxDeletionListener unregisterDeletionListenerObjects + */ + virtual void registerDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount) = 0; + + /** + \brief Unregister specific objects for deletion events. + + This method allows to clear previously registered objects for a deletion listener (see #registerDeletionListenerObjects()). + + \note It is illegal to register or unregister objects while deletions are being processed. + + \note The deletion listener has to be registered through #registerDeletionListener() and configured to support restricted objects sets prior to this method being used. + + \param [in] observer Observer object to stop sending notifications to. + \param [in] observables List of objects for which to not receive deletion events anymore. + \param [in] observableCount Size of the observables list. + + @see PxDeletionListener registerDeletionListenerObjects + */ + virtual void unregisterDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount) = 0; + + /** + \brief Gets PxPhysics object insertion interface. + + The insertion interface is needed ie. for PxCooking::createTriangleMesh, this allows runtime mesh creation. This is not advised to do, please + use offline cooking if possible. + + @see PxCooking::createTriangleMesh PxCooking::createHeightfield + */ + virtual PxPhysicsInsertionCallback& getPhysicsInsertionCallback() = 0; + + //@} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** +\brief Enables the usage of the articulations feature. This function is called automatically inside PxCreatePhysics(). +On resource constrained platforms, it is possible to call PxCreateBasePhysics() and then NOT call this function +to save on code memory if your application does not use articulations. In this case the linker should strip out +the relevant implementation code from the library. If you need to use articulations but not some other optional +component, you shoud call PxCreateBasePhysics() followed by this call. +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxRegisterArticulations(physx::PxPhysics& physics); + + +/** +\brief Enables the usage of the reduced coordinate articulations feature. This function is called automatically inside PxCreatePhysics(). +On resource constrained platforms, it is possible to call PxCreateBasePhysics() and then NOT call this function +to save on code memory if your application does not use articulations. In this case the linker should strip out +the relevant implementation code from the library. If you need to use articulations but not some other optional +component, you shoud call PxCreateBasePhysics() followed by this call. +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxRegisterArticulationsReducedCoordinate(physx::PxPhysics& physics); + +/** +\brief Enables the usage of the heightfield feature. + +This call will link the default 'unified' implementation of heightfields which is identical to the narrow phase of triangle meshes. +This function is called automatically inside PxCreatePhysics(). + +On resource constrained platforms, it is possible to call PxCreateBasePhysics() and then NOT call this function +to save on code memory if your application does not use heightfields. In this case the linker should strip out +the relevant implementation code from the library. If you need to use heightfield but not some other optional +component, you shoud call PxCreateBasePhysics() followed by this call. + +You must call this function at a time where no ::PxScene instance exists, typically before calling PxPhysics::createScene(). +This is to prevent a change to the heightfield implementation code at runtime which would have undefined results. + +Calling PxCreateBasePhysics() and then attempting to create a heightfield shape without first calling +::PxRegisterHeightFields(), will result in an error. +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxRegisterHeightFields(physx::PxPhysics& physics); + +/** +\brief Creates an instance of the physics SDK with minimal additional components registered + +Creates an instance of this class. May not be a class member to avoid name mangling. +Pass the constant #PX_PHYSICS_VERSION as the argument. +There may be only one instance of this class per process. Calling this method after an instance +has been created already will result in an error message and NULL will be returned. + +\param version Version number we are expecting(should be #PX_PHYSICS_VERSION) +\param foundation Foundation instance (see PxFoundation) +\param scale values used to determine default tolerances for objects at creation time +\param trackOutstandingAllocations true if you want to track memory allocations + so a debugger connection partway through your physics simulation will get + an accurate map of everything that has been allocated so far. This could have a memory + and performance impact on your simulation hence it defaults to off. +\param pvd When pvd points to a valid PxPvd instance (PhysX Visual Debugger), a connection to the specified PxPvd instance is created. + If pvd is NULL no connection will be attempted. +\return PxPhysics instance on success, NULL if operation failed + +@see PxPhysics, PxFoundation, PxTolerancesScale, PxPvd +*/ +PX_C_EXPORT PX_PHYSX_CORE_API physx::PxPhysics* PX_CALL_CONV PxCreateBasePhysics(physx::PxU32 version, + physx::PxFoundation& foundation, + const physx::PxTolerancesScale& scale, + bool trackOutstandingAllocations = false, + physx::PxPvd* pvd = NULL); + +/** +\brief Creates an instance of the physics SDK. + +Creates an instance of this class. May not be a class member to avoid name mangling. +Pass the constant #PX_PHYSICS_VERSION as the argument. +There may be only one instance of this class per process. Calling this method after an instance +has been created already will result in an error message and NULL will be returned. + +Calling this will register all optional code modules (Articulations and HeightFields), preparing them for use. +If you do not need some of these modules, consider calling PxCreateBasePhysics() instead and registering needed +modules manually. + +\param version Version number we are expecting(should be #PX_PHYSICS_VERSION) +\param foundation Foundation instance (see PxFoundation) +\param scale values used to determine default tolerances for objects at creation time +\param trackOutstandingAllocations true if you want to track memory allocations + so a debugger connection partway through your physics simulation will get + an accurate map of everything that has been allocated so far. This could have a memory + and performance impact on your simulation hence it defaults to off. +\param pvd When pvd points to a valid PxPvd instance (PhysX Visual Debugger), a connection to the specified PxPvd instance is created. + If pvd is NULL no connection will be attempted. +\return PxPhysics instance on success, NULL if operation failed + +@see PxPhysics, PxCreateBasePhysics, PxRegisterArticulations, PxRegisterArticulationsReducedCoordinate, PxRegisterHeightFields +*/ +PX_INLINE physx::PxPhysics* PxCreatePhysics(physx::PxU32 version, + physx::PxFoundation& foundation, + const physx::PxTolerancesScale& scale, + bool trackOutstandingAllocations = false, + physx::PxPvd* pvd = NULL ) +{ + physx::PxPhysics* physics = PxCreateBasePhysics(version, foundation, scale, trackOutstandingAllocations, pvd); + if(!physics) + return NULL; + + PxRegisterArticulations(*physics); + PxRegisterArticulationsReducedCoordinate(*physics); + PxRegisterHeightFields(*physics); + + return physics; +} + + +/** +\brief Retrieves the Physics SDK after it has been created. + +Before using this function the user must call #PxCreatePhysics(). + +\note The behavior of this method is undefined if the Physics SDK instance has not been created already. +*/ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#endif + +PX_C_EXPORT PX_PHYSX_CORE_API physx::PxPhysics& PX_CALL_CONV PxGetPhysics(); + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxPhysicsAPI.h b/src/PhysX/physx/include/PxPhysicsAPI.h new file mode 100644 index 000000000..727774540 --- /dev/null +++ b/src/PhysX/physx/include/PxPhysicsAPI.h @@ -0,0 +1,218 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NXPHYSICS_API +#define PX_PHYSICS_NXPHYSICS_API +/** \addtogroup physics +@{ +*/ + +/** +This is the main include header for the Physics SDK, for users who +want to use a single #include file. + +Alternatively, one can instead directly #include a subset of the below files. +*/ + +// Foundation SDK +#include "foundation/Px.h" +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxAssert.h" +#include "foundation/PxBitAndData.h" +#include "foundation/PxBounds3.h" +#include "foundation/PxErrorCallback.h" +#include "foundation/PxErrors.h" +#include "foundation/PxFlags.h" +#include "foundation/PxIntrinsics.h" +#include "foundation/PxIO.h" +#include "foundation/PxMat33.h" +#include "foundation/PxMat44.h" +#include "foundation/PxMath.h" +#include "foundation/PxMathUtils.h" +#include "foundation/PxPlane.h" +#include "foundation/PxPreprocessor.h" +#include "foundation/PxQuat.h" +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxStrideIterator.h" +#include "foundation/PxTransform.h" +#include "foundation/PxUnionCast.h" +#include "foundation/PxVec2.h" +#include "foundation/PxVec3.h" +#include "foundation/PxVec4.h" + +//Not physics specific utilities and common code +#include "common/PxCoreUtilityTypes.h" +#include "common/PxPhysXCommonConfig.h" +#include "common/PxRenderBuffer.h" +#include "common/PxBase.h" +#include "common/PxTolerancesScale.h" +#include "common/PxTypeInfo.h" +#include "common/PxStringTable.h" +#include "common/PxSerializer.h" +#include "common/PxMetaData.h" +#include "common/PxMetaDataFlags.h" +#include "common/PxSerialFramework.h" +#include "common/PxPhysicsInsertionCallback.h" + +//Task Manager +#include "task/PxTask.h" + +// Cuda Mananger +#if PX_SUPPORT_GPU_PHYSX +#include "gpu/PxGpu.h" +#endif + +//Geometry Library +#include "geometry/PxBoxGeometry.h" +#include "geometry/PxBVHStructure.h" +#include "geometry/PxCapsuleGeometry.h" +#include "geometry/PxConvexMesh.h" +#include "geometry/PxConvexMeshGeometry.h" +#include "geometry/PxGeometry.h" +#include "geometry/PxGeometryHelpers.h" +#include "geometry/PxGeometryQuery.h" +#include "geometry/PxHeightField.h" +#include "geometry/PxHeightFieldDesc.h" +#include "geometry/PxHeightFieldFlag.h" +#include "geometry/PxHeightFieldGeometry.h" +#include "geometry/PxHeightFieldSample.h" +#include "geometry/PxMeshQuery.h" +#include "geometry/PxMeshScale.h" +#include "geometry/PxPlaneGeometry.h" +#include "geometry/PxSimpleTriangleMesh.h" +#include "geometry/PxSphereGeometry.h" +#include "geometry/PxTriangle.h" +#include "geometry/PxTriangleMesh.h" +#include "geometry/PxTriangleMeshGeometry.h" + + +// PhysX Core SDK +#include "PxActor.h" +#include "PxAggregate.h" +#include "PxArticulation.h" +#include "PxArticulationReducedCoordinate.h" +#include "PxArticulationJoint.h" +#include "PxArticulationJointReducedCoordinate.h" +#include "PxArticulationLink.h" +#include "PxBatchQuery.h" +#include "PxBatchQueryDesc.h" +#include "PxClient.h" +#include "PxConstraint.h" +#include "PxConstraintDesc.h" +#include "PxContact.h" +#include "PxContactModifyCallback.h" +#include "PxDeletionListener.h" +#include "PxFiltering.h" +#include "PxForceMode.h" +#include "PxFoundation.h" +#include "PxLockedData.h" +#include "PxMaterial.h" +#include "PxPhysics.h" +#include "PxPhysicsVersion.h" +#include "PxPhysXConfig.h" +#include "PxQueryFiltering.h" +#include "PxQueryReport.h" +#include "PxRigidActor.h" +#include "PxRigidBody.h" +#include "PxRigidDynamic.h" +#include "PxRigidStatic.h" +#include "PxScene.h" +#include "PxSceneDesc.h" +#include "PxSceneLock.h" +#include "PxShape.h" +#include "PxSimulationEventCallback.h" +#include "PxSimulationStatistics.h" +#include "PxVisualizationParameter.h" +#include "PxPruningStructure.h" + +//Character Controller +#include "characterkinematic/PxBoxController.h" +#include "characterkinematic/PxCapsuleController.h" +#include "characterkinematic/PxCharacter.h" +#include "characterkinematic/PxController.h" +#include "characterkinematic/PxControllerBehavior.h" +#include "characterkinematic/PxControllerManager.h" +#include "characterkinematic/PxControllerObstacles.h" +#include "characterkinematic/PxExtended.h" + +//Cooking (data preprocessing) +#include "cooking/Pxc.h" +#include "cooking/PxConvexMeshDesc.h" +#include "cooking/PxCooking.h" +#include "cooking/PxTriangleMeshDesc.h" +#include "cooking/PxBVH33MidphaseDesc.h" +#include "cooking/PxBVH34MidphaseDesc.h" +#include "cooking/PxMidphaseDesc.h" + +//Extensions to the SDK +#include "extensions/PxDefaultStreams.h" +#include "extensions/PxDistanceJoint.h" +#include "extensions/PxExtensionsAPI.h" +#include "extensions/PxFixedJoint.h" +#include "extensions/PxJoint.h" +#include "extensions/PxJointLimit.h" +#include "extensions/PxPrismaticJoint.h" +#include "extensions/PxRevoluteJoint.h" +#include "extensions/PxRigidBodyExt.h" +#include "extensions/PxShapeExt.h" +#include "extensions/PxSimpleFactory.h" +#include "extensions/PxSmoothNormals.h" +#include "extensions/PxSphericalJoint.h" +#include "extensions/PxStringTableExt.h" +#include "extensions/PxTriangleMeshExt.h" +#include "extensions/PxConvexMeshExt.h" + +//Serialization +#include "extensions/PxSerialization.h" +#include "extensions/PxBinaryConverter.h" +#include "extensions/PxRepXSerializer.h" + +//Vehicle Simulation +#include "vehicle/PxVehicleComponents.h" +#include "vehicle/PxVehicleDrive.h" +#include "vehicle/PxVehicleDrive4W.h" +#include "vehicle/PxVehicleDriveTank.h" +#include "vehicle/PxVehicleSDK.h" +#include "vehicle/PxVehicleShaders.h" +#include "vehicle/PxVehicleTireFriction.h" +#include "vehicle/PxVehicleUpdate.h" +#include "vehicle/PxVehicleUtilControl.h" +#include "vehicle/PxVehicleUtilSetup.h" +#include "vehicle/PxVehicleUtilTelemetry.h" +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleNoDrive.h" +#include "vehicle/PxVehicleDriveNW.h" + +//Connecting the SDK to Visual Debugger +#include "pvd/PxPvdSceneClient.h" +#include "pvd/PxPvd.h" +#include "pvd/PxPvdTransport.h" +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxPhysicsSerialization.h b/src/PhysX/physx/include/PxPhysicsSerialization.h new file mode 100644 index 000000000..c4cad9d29 --- /dev/null +++ b/src/PhysX/physx/include/PxPhysicsSerialization.h @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_PX_PHYSICS_SERIALIZATION +#define PX_PHYSICS_PX_PHYSICS_SERIALIZATION + +#include "PxPhysXConfig.h" +#include "common/PxSerialFramework.h" + +#if !PX_DOXYGEN +/** +\brief Retrieves the PhysX SDK metadata. +This function is used to implement PxSerialization.dumpBinaryMetaData() and is not intended to be needed otherwise. +@see PxSerialization.dumpBinaryMetaData() +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxGetPhysicsBinaryMetaData(physx::PxOutputStream& stream); + +/** +\brief Registers physics classes for serialization. +This function is used to implement PxSerialization.createSerializationRegistry() and is not intended to be needed otherwise. +@see PxSerializationRegistry +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxRegisterPhysicsSerializers(physx::PxSerializationRegistry& sr); + +/** +\brief Unregisters physics classes for serialization. +This function is used in the release implementation of PxSerializationRegistry and in not intended to be used otherwise. +@see PxSerializationRegistry +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxUnregisterPhysicsSerializers(physx::PxSerializationRegistry& sr); + + +/** +\brief Adds collected objects to PxPhysics. + +This function adds all objects contained in the input collection to the PxPhysics instance. This is used after deserializing +the collection, to populate the physics with inplace deserialized objects. This function is used in the implementation of +PxSerialization.createCollectionFromBinary and is not intended to be needed otherwise. +\param[in] collection Objects to add to the PxPhysics instance. + +@see PxCollection, PxSerialization.createCollectionFromBinary +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxAddCollectionToPhysics(const physx::PxCollection& collection); + +#endif // !PX_DOXYGEN + +#endif // PX_PHYSICS_PX_PHYSICS_SERIALIZATION diff --git a/src/PhysX/physx/include/PxPhysicsVersion.h b/src/PhysX/physx/include/PxPhysicsVersion.h new file mode 100644 index 000000000..9dd9de053 --- /dev/null +++ b/src/PhysX/physx/include/PxPhysicsVersion.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_VERSION_NUMBER_H +#define PX_PHYSICS_VERSION_NUMBER_H + +/* +VersionNumbers: The combination of these +numbers uniquely identifies the API, and should +be incremented when the SDK API changes. This may +include changes to file formats. + +This header is included in the main SDK header files +so that the entire SDK and everything that builds on it +is completely rebuilt when this file changes. Thus, +this file is not to include a frequently changing +build number. See BuildNumber.h for that. + +Each of these three values should stay below 255 because +sometimes they are stored in a byte. +*/ +/** \addtogroup foundation + @{ +*/ + +// +// Important: if you adjust the versions below, don't forget to adjust the compatibility list in +// sBinaryCompatibleVersions as well. +// + +#define PX_PHYSICS_VERSION_MAJOR 4 +#define PX_PHYSICS_VERSION_MINOR 0 +#define PX_PHYSICS_VERSION_BUGFIX 0 + +/** +The constant PX_PHYSICS_VERSION is used when creating certain PhysX module objects. +This is to ensure that the application is using the same header version as the library was built with. +*/ +#define PX_PHYSICS_VERSION ((PX_PHYSICS_VERSION_MAJOR<<24) + (PX_PHYSICS_VERSION_MINOR<<16) + (PX_PHYSICS_VERSION_BUGFIX<<8) + 0) + + +#endif // PX_PHYSICS_VERSION_NUMBER_H + + /** @} */ diff --git a/src/PhysX/physx/include/PxPruningStructure.h b/src/PhysX/physx/include/PxPruningStructure.h new file mode 100644 index 000000000..beeb1d7ac --- /dev/null +++ b/src/PhysX/physx/include/PxPruningStructure.h @@ -0,0 +1,109 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_PRUNING_STRUCTURE +#define PX_PHYSICS_NX_PRUNING_STRUCTURE +/** \addtogroup physics +@{ */ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + +/** +\brief A precomputed pruning structure to accelerate scene queries against newly added actors. + +The pruning structure can be provided to #PxScene:: addActors() in which case it will get merged +directly into the scene query optimization AABB tree, thus leading to improved performance when +doing queries against the newly added actors. This applies to both static and dynamic actors. + +\note PxPruningStructure objects can be added to a collection and get serialized. +\note Adding a PxPruningStructure object to a collection will also add the actors that were used to build the pruning structure. + +\note PxPruningStructure must be released before its rigid actors. +\note PxRigidBody objects can be in one PxPruningStructure only. +\note Changing the bounds of PxRigidBody objects assigned to a pruning structure that has not been added to a scene yet will +invalidate the pruning structure. Same happens if shape scene query flags change or shape gets removed from an actor. + +@see PxScene::addActors PxCollection +*/ +class PxPruningStructure : public PxBase +{ +public: + /** + \brief Release this object. + */ + virtual void release() = 0; + + /** + \brief Retrieve rigid actors in the pruning structure. + + You can retrieve the number of rigid actor pointers by calling #getNbRigidActors() + + \param[out] userBuffer The buffer to store the actor pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first actor pointer to be retrieved + \return Number of rigid actor pointers written to the buffer. + + @see PxRigidActor + */ + virtual PxU32 getRigidActors(PxRigidActor** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Returns the number of rigid actors in the pruning structure. + + You can use #getRigidActors() to retrieve the rigid actor pointers. + + \return Number of rigid actors in the pruning structure. + + @see PxRigidActor + */ + virtual PxU32 getNbRigidActors() const = 0; + + virtual const char* getConcreteTypeName() const { return "PxPruningStructure"; } +protected: + PX_INLINE PxPruningStructure(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxPruningStructure(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxPruningStructure() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxPruningStructure", name) || PxBase::isKindOf(name); } +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PX_PHYSICS_NX_PRUNING_STRUCTURE diff --git a/src/PhysX/physx/include/PxQueryFiltering.h b/src/PhysX/physx/include/PxQueryFiltering.h new file mode 100644 index 000000000..7991246f6 --- /dev/null +++ b/src/PhysX/physx/include/PxQueryFiltering.h @@ -0,0 +1,276 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENE_QUERY_FILTERING +#define PX_PHYSICS_NX_SCENE_QUERY_FILTERING +/** \addtogroup scenequery +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxFiltering.h" +#include "PxQueryReport.h" +#include "PxClient.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxShape; +class PxRigidActor; +struct PxQueryHit; + + +/** +\brief Filtering flags for scene queries. + +@see PxQueryFilterData.flags +*/ +struct PxQueryFlag +{ + enum Enum + { + eSTATIC = (1<<0), //!< Traverse static shapes + + eDYNAMIC = (1<<1), //!< Traverse dynamic shapes + + ePREFILTER = (1<<2), //!< Run the pre-intersection-test filter (see #PxQueryFilterCallback::preFilter()) + + ePOSTFILTER = (1<<3), //!< Run the post-intersection-test filter (see #PxQueryFilterCallback::postFilter()) + + eANY_HIT = (1<<4), //!< Abort traversal as soon as any hit is found and return it via callback.block. + //!< Helps query performance. Both eTOUCH and eBLOCK hitTypes are considered hits with this flag. + + eNO_BLOCK = (1<<5), //!< All hits are reported as touching. Overrides eBLOCK returned from user filters with eTOUCH. + //!< This is also an optimization hint that may improve query performance. + + eRESERVED = (1<<15) //!< Reserved for internal use + }; +}; +PX_COMPILE_TIME_ASSERT(PxQueryFlag::eSTATIC==(1<<0)); +PX_COMPILE_TIME_ASSERT(PxQueryFlag::eDYNAMIC==(1<<1)); + +/** +\brief Flags typedef for the set of bits defined in PxQueryFlag. + +*/ +typedef PxFlags PxQueryFlags; +PX_FLAGS_OPERATORS(PxQueryFlag::Enum,PxU16) + +/** +\brief Classification of scene query hits (intersections). + + - eNONE: Returning this hit type means that the hit should not be reported. + - eBLOCK: For all raycast, sweep and overlap queries the nearest eBLOCK type hit will always be returned in PxHitCallback::block member. + - eTOUCH: Whenever a raycast, sweep or overlap query was called with non-zero PxHitCallback::nbTouches and PxHitCallback::touches + parameters, eTOUCH type hits that are closer or same distance (touchDistance <= blockDistance condition) + as the globally nearest eBLOCK type hit, will be reported. + - For example, to record all hits from a raycast query, always return eTOUCH. + +All hits in overlap() queries are treated as if the intersection distance were zero. +This means the hits are unsorted and all eTOUCH hits are recorded by the callback even if an eBLOCK overlap hit was encountered. +Even though all overlap() blocking hits have zero length, only one (arbitrary) eBLOCK overlap hit is recorded in PxHitCallback::block. +All overlap() eTOUCH type hits are reported (zero touchDistance <= zero blockDistance condition). + +For raycast/sweep/overlap calls with zero touch buffer or PxHitCallback::nbTouches member, +only the closest hit of type eBLOCK is returned. All eTOUCH hits are discarded. + +@see PxQueryFilterCallback.preFilter PxQueryFilterCallback.postFilter PxScene.raycast PxScene.sweep PxScene.overlap +*/ +struct PxQueryHitType +{ + enum Enum + { + eNONE = 0, //!< the query should ignore this shape + eTOUCH = 1, //!< a hit on the shape touches the intersection geometry of the query but does not block it + eBLOCK = 2 //!< a hit on the shape blocks the query (does not block overlap queries) + }; +}; + +/** +\brief Scene query filtering data. + +Whenever the scene query intersects a shape, filtering is performed in the following order: + +\li For non-batched queries only:
If the data field is non-zero, and the bitwise-AND value of data AND the shape's +queryFilterData is zero, the shape is skipped +\li If filter callbacks are enabled in flags field (see #PxQueryFlags) they will get invoked accordingly. +\li If neither #PxQueryFlag::ePREFILTER or #PxQueryFlag::ePOSTFILTER is set, the hit defaults +to type #PxQueryHitType::eBLOCK when the value of PxHitCallback::nbTouches provided with the query is zero and to type +#PxQueryHitType::eTOUCH when PxHitCallback::nbTouches is positive. + +@see PxScene.raycast PxScene.sweep PxScene.overlap PxBatchQuery.raycast PxBatchQuery.sweep PxBatchQuery.overlap PxQueryFlag::eANY_HIT +*/ +struct PxQueryFilterData +{ + /** \brief default constructor */ + explicit PX_INLINE PxQueryFilterData() : flags(PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC) {} + + /** \brief constructor to set both filter data and filter flags */ + explicit PX_INLINE PxQueryFilterData(const PxFilterData& fd, PxQueryFlags f) : data(fd), flags(f) {} + + /** \brief constructor to set filter flags only */ + explicit PX_INLINE PxQueryFilterData(PxQueryFlags f) : flags(f) {} + + PxFilterData data; //!< Filter data associated with the scene query + PxQueryFlags flags; //!< Filter flags (see #PxQueryFlags) +}; + +/** +\brief Scene query filtering callbacks. + +Custom filtering logic for scene query intersection candidates. If an intersection candidate object passes the data based filter +(see #PxQueryFilterData), filtering callbacks are executed if requested (see #PxQueryFilterData.flags) + +\li If #PxQueryFlag::ePREFILTER is set, the preFilter function runs before exact intersection tests. +If this function returns #PxQueryHitType::eTOUCH or #PxQueryHitType::eBLOCK, exact testing is performed to +determine the intersection location. + +The preFilter function may overwrite the copy of queryFlags it receives as an argument to specify any of #PxHitFlag::eMODIFIABLE_FLAGS +on a per-shape basis. Changes apply only to the shape being filtered, and changes to other flags are ignored. + +\li If #PxQueryFlag::ePREFILTER is not set, precise intersection testing is performed using the original query's filterData.flags. + +\li If #PxQueryFlag::ePOSTFILTER is set, the postFilter function is called for each intersection to determine the touch/block status. +This overrides any touch/block status previously returned from the preFilter function for this shape. + +Filtering calls are not guaranteed to be sorted along the ray or sweep direction. + +@see PxScene.raycast PxScene.sweep PxScene.overlap PxQueryFlags PxHitFlags +*/ +class PxQueryFilterCallback +{ +public: + + /** + \brief This filter callback is executed before the exact intersection test if PxQueryFlag::ePREFILTER flag was set. + + \param[in] filterData custom filter data specified as the query's filterData.data parameter. + \param[in] shape A shape that has not yet passed the exact intersection test. + \param[in] actor The shape's actor. + \param[in,out] queryFlags scene query flags from the query's function call (only flags from PxHitFlag::eMODIFIABLE_FLAGS bitmask can be modified) + \return the updated type for this hit (see #PxQueryHitType) + */ + virtual PxQueryHitType::Enum preFilter( + const PxFilterData& filterData, const PxShape* shape, const PxRigidActor* actor, PxHitFlags& queryFlags) = 0; + + /** + \brief This filter callback is executed if the exact intersection test returned true and PxQueryFlag::ePOSTFILTER flag was set. + + \param[in] filterData custom filter data of the query + \param[in] hit Scene query hit information. faceIndex member is not valid for overlap queries. For sweep and raycast queries the hit information can be cast to #PxSweepHit and #PxRaycastHit respectively. + \return the updated hit type for this hit (see #PxQueryHitType) + */ + virtual PxQueryHitType::Enum postFilter(const PxFilterData& filterData, const PxQueryHit& hit) = 0; + + /** + \brief virtual destructor + */ + virtual ~PxQueryFilterCallback() {} +}; + +/** +\brief Batched query pre-filter shader. + +Custom filtering logic for batched query intersection candidates. If an intersection candidate object passes the data based filter (see #PxQueryFilterData), +filtering shader runs if specified in filtering flags (see #PxQueryFilterData.flags) + +\li If #PxQueryFlag::ePREFILTER is set, the preFilter shader runs before exact intersection tests. +If the shader returns #PxQueryHitType::eTOUCH or #PxQueryHitType::eBLOCK, exact testing is performed to +determine the intersection location. + +The preFilter shader may overwrite the copy of queryFlags it receives as an argument to specify any of #PxHitFlag::eMODIFIABLE_FLAGS +on a per-shape basis. Changes apply only to the shape being filtered, and changes to other flags are ignored. + +\li If #PxQueryFlag::ePREFILTER is not set, precise intersection testing is performed using the original query's filterData.flags. + +Filtering calls are not guaranteed to be sorted along the ray or sweep direction. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 + +@see PxBatchQueryDesc.preFilterShader PxQueryFilterCallback.preFilter PxBatchQueryPostFilterShader + +*/ + +/** +\param[in] queryFilterData Query filter data +\param[in] objectFilterData Object filter data +\param[in] constantBlock Global constant filter data (see #PxBatchQuery) +\param[in] constantBlockSize Size of global filter data (see #PxBatchQuery) +\param[in,out] hitFlags Per-object modifiable hit flags (only flags from PxHitFlag::eMODIFIABLE_FLAGS mask can be modified) +\return the updated hit type for this hit (see #PxQueryHitType) + +@see PxBatchQueryPostFilterShader +*/ +typedef PX_DEPRECATED PxQueryHitType::Enum (*PxBatchQueryPreFilterShader)( + PxFilterData queryFilterData, PxFilterData objectFilterData, + const void* constantBlock, PxU32 constantBlockSize, + PxHitFlags& hitFlags); + +/** +\brief Batched query post-filter shader. + +Custom filtering logic for batched query intersection candidates. If an intersection candidate object passes the data based filter (see #PxQueryFilterData), +the filtering shader run on request (see #PxQueryFilterData.flags) + +\li If #PxQueryFlag::ePOSTFILTER is set, the postFilter shader is called for each intersection to determine the touch/block status. +This overrides any touch/block status previously returned from the preFilter function for this shape. + +Filtering shaders are not in order along the query direction, rather they are processed in the order in which +candidate shapes for testing are found by PhysX' scene traversal algorithms. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 + +@see PxBatchQueryDesc.postFilterShader PxQueryFilterCallback.postFilter PxBatchQueryPreFilterShader +*/ + +/** +\param[in] queryFilterData Query filter data +\param[in] objectFilterData Object filter data +\param[in] constantBlock Global constant filter data (see #PxBatchQuery) +\param[in] constantBlockSize Size of global filter data (see #PxBatchQuery) +\param[in] hit Hit data from the prior exact intersection test. +\return the new hit type for this hit (see #PxQueryHitType) + +@see PxBatchQueryPreFilterShader +*/ + +typedef PX_DEPRECATED PxQueryHitType::Enum (*PxBatchQueryPostFilterShader)( + PxFilterData queryFilterData, PxFilterData objectFilterData, + const void* constantBlock, PxU32 constantBlockSize, + const PxQueryHit& hit); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxQueryReport.h b/src/PhysX/physx/include/PxQueryReport.h new file mode 100644 index 000000000..f50322906 --- /dev/null +++ b/src/PhysX/physx/include/PxQueryReport.h @@ -0,0 +1,390 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENEQUERYREPORT +#define PX_PHYSICS_NX_SCENEQUERYREPORT +/** \addtogroup scenequery +@{ +*/ +#include "PxPhysXConfig.h" +#include "foundation/PxVec3.h" +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxShape; +class PxRigidActor; + +/** +\brief Scene query and geometry query behavior flags. + +PxHitFlags are used for 3 different purposes: + +1) To request hit fields to be filled in by scene queries (such as hit position, normal, face index or UVs). +2) Once query is completed, to indicate which fields are valid (note that a query may produce more valid fields than requested). +3) To specify additional options for the narrow phase and mid-phase intersection routines. + +All these flags apply to both scene queries and geometry queries (PxGeometryQuery). + +@see PxRaycastHit PxSweepHit PxOverlapHit PxScene.raycast PxScene.sweep PxScene.overlap PxGeometryQuery PxFindFaceIndex +*/ +struct PxHitFlag +{ + enum Enum + { + ePOSITION = (1<<0), //!< "position" member of #PxQueryHit is valid + eNORMAL = (1<<1), //!< "normal" member of #PxQueryHit is valid + eUV = (1<<3), //!< "u" and "v" barycentric coordinates of #PxQueryHit are valid. Not applicable to sweep queries. + eASSUME_NO_INITIAL_OVERLAP = (1<<4), //!< Performance hint flag for sweeps when it is known upfront there's no initial overlap. + //!< NOTE: using this flag may cause undefined results if shapes are initially overlapping. + eMESH_MULTIPLE = (1<<5), //!< Report all hits for meshes rather than just the first. Not applicable to sweep queries. + eMESH_ANY = (1<<6), //!< Report any first hit for meshes. If neither eMESH_MULTIPLE nor eMESH_ANY is specified, + //!< a single closest hit will be reported for meshes. + eMESH_BOTH_SIDES = (1<<7), //!< Report hits with back faces of mesh triangles. Also report hits for raycast + //!< originating on mesh surface and facing away from the surface normal. Not applicable to sweep queries. + //!< Please refer to the user guide for heightfield-specific differences. + ePRECISE_SWEEP = (1<<8), //!< Use more accurate but slower narrow phase sweep tests. + //!< May provide better compatibility with PhysX 3.2 sweep behavior. + eMTD = (1<<9), //!< Report the minimum translation depth, normal and contact point. + eFACE_INDEX = (1<<10), //!< "face index" member of #PxQueryHit is valid + + eDEFAULT = ePOSITION|eNORMAL|eFACE_INDEX, + + /** \brief Only this subset of flags can be modified by pre-filter. Other modifications will be discarded. */ + eMODIFIABLE_FLAGS = eMESH_MULTIPLE|eMESH_BOTH_SIDES|eASSUME_NO_INITIAL_OVERLAP|ePRECISE_SWEEP + }; +}; + + +/** +\brief collection of set bits defined in PxHitFlag. + +@see PxHitFlag +*/ +PX_FLAGS_TYPEDEF(PxHitFlag, PxU16) + +/** +\brief Combines a shape pointer and the actor the shape belongs to into one memory location. + +Serves as a base class for PxQueryHit. + +@see PxQueryHit +*/ +struct PxActorShape +{ + PX_INLINE PxActorShape() : actor(NULL), shape(NULL) {} + PX_INLINE PxActorShape(PxRigidActor* a, PxShape* s) : actor(a), shape(s) {} + + PxRigidActor* actor; + PxShape* shape; +}; + + +/** +\brief Scene query hit information. +*/ +struct PxQueryHit : public PxActorShape +{ + PX_INLINE PxQueryHit() : faceIndex(0xFFFFffff) {} + + /** + Face index of touched triangle, for triangle meshes, convex meshes and height fields. + + \note This index will default to 0xFFFFffff value for overlap queries. + \note Please refer to the user guide for more details for sweep queries. + \note This index is remapped by mesh cooking. Use #PxTriangleMesh::getTrianglesRemap() to convert to original mesh index. + \note For convex meshes use #PxConvexMesh::getPolygonData() to retrieve touched polygon data. + */ + PxU32 faceIndex; +}; + +/** +\brief Scene query hit information for raycasts and sweeps returning hit position and normal information. + +::PxHitFlag flags can be passed to scene query functions, as an optimization, to cause the SDK to +only generate specific members of this structure. +*/ +struct PxLocationHit : public PxQueryHit +{ + PX_INLINE PxLocationHit() : flags(0), position(PxVec3(0)), normal(PxVec3(0)), distance(PX_MAX_REAL) {} + + /** + \note For raycast hits: true for shapes overlapping with raycast origin. + \note For sweep hits: true for shapes overlapping at zero sweep distance. + + @see PxRaycastHit PxSweepHit + */ + PX_INLINE bool hadInitialOverlap() const { return (distance <= 0.0f); } + + // the following fields are set in accordance with the #PxHitFlags + PxHitFlags flags; //!< Hit flags specifying which members contain valid values. + PxVec3 position; //!< World-space hit position (flag: #PxHitFlag::ePOSITION) + PxVec3 normal; //!< World-space hit normal (flag: #PxHitFlag::eNORMAL) + + /** + \brief Distance to hit. + \note If the eMTD flag is used, distance will be a negative value if shapes are overlapping indicating the penetration depth. + \note Otherwise, this value will be >= 0 */ + PxF32 distance; +}; + + +/** +\brief Stores results of raycast queries. + +::PxHitFlag flags can be passed to raycast function, as an optimization, to cause the SDK to only compute specified members of this +structure. + +Some members like barycentric coordinates are currently only computed for triangle meshes and height fields, but next versions +might provide them in other cases. The client code should check #flags to make sure returned values are valid. + +@see PxScene.raycast PxBatchQuery.raycast +*/ +struct PxRaycastHit : public PxLocationHit +{ + PX_INLINE PxRaycastHit() : u(0.0f), v(0.0f) {} + + // the following fields are set in accordance with the #PxHitFlags + + PxReal u, v; //!< barycentric coordinates of hit point, for triangle mesh and height field (flag: #PxHitFlag::eUV) +#if !PX_P64_FAMILY + PxU32 padTo16Bytes[3]; +#endif +}; + + +/** +\brief Stores results of overlap queries. + +@see PxScene.overlap PxBatchQuery.overlap +*/ +struct PxOverlapHit: public PxQueryHit { PxU32 padTo16Bytes; }; + + +/** +\brief Stores results of sweep queries. + +@see PxScene.sweep PxBatchQuery.sweep +*/ +struct PxSweepHit : public PxLocationHit +{ + PX_INLINE PxSweepHit() {} + + PxU32 padTo16Bytes; +}; + + +/** +\brief Describes query behavior after returning a partial query result via a callback. + +If callback returns true, traversal will continue and callback can be issued again. +If callback returns false, traversal will stop, callback will not be issued again. + +@see PxHitCallback +*/ +typedef bool PxAgain; + + +/** +\brief This callback class facilitates reporting scene query hits (intersections) to the user. + +User overrides the virtual processTouches function to receive hits in (possibly multiple) fixed size blocks. + +\note PxHitBuffer derives from this class and is used to receive touching hits in a fixed size buffer. +\note Since the compiler doesn't look in template dependent base classes when looking for non-dependent names +\note with some compilers it will be necessary to use "this->hasBlock" notation to access a parent variable +\note in a child callback class. +\note Pre-made typedef shorthands, such as ::PxRaycastCallback can be used for raycast, overlap and sweep queries. + +@see PxHitBuffer PxRaycastHit PxSweepHit PxOverlapHit PxRaycastCallback PxOverlapCallback PxSweepCallback +*/ +template +struct PxHitCallback +{ + HitType block; //!< Holds the closest blocking hit result for the query. Invalid if hasBlock is false. + bool hasBlock; //!< Set to true if there was a blocking hit during query. + + HitType* touches; //!< User specified buffer for touching hits. + + /** + \brief Size of the user specified touching hits buffer. + \note If set to 0 all hits will default to PxQueryHitType::eBLOCK, otherwise to PxQueryHitType::eTOUCH + \note Hit type returned from pre-filter overrides this default */ + PxU32 maxNbTouches; + + /** + \brief Number of touching hits returned by the query. Used with PxHitBuffer. + \note If true (PxAgain) is returned from the callback, nbTouches will be reset to 0. */ + PxU32 nbTouches; + + /** + \brief Initializes the class with user provided buffer. + + \param[in] aTouches Optional buffer for recording PxQueryHitType::eTOUCH type hits. + \param[in] aMaxNbTouches Size of touch buffer. + + \note if aTouches is NULL and aMaxNbTouches is 0, only the closest blocking hit will be recorded by the query. + \note If PxQueryFlag::eANY_HIT flag is used as a query parameter, hasBlock will be set to true and blockingHit will be used to receive the result. + \note Both eTOUCH and eBLOCK hits will be registered as hasBlock=true and stored in PxHitCallback.block when eANY_HIT flag is used. + + @see PxHitCallback.hasBlock PxHitCallback.block */ + PxHitCallback(HitType* aTouches, PxU32 aMaxNbTouches) + : hasBlock(false), touches(aTouches), maxNbTouches(aMaxNbTouches), nbTouches(0) + {} + + /** + \brief virtual callback function used to communicate query results to the user. + + This callback will always be invoked with #touches as a buffer if #touches was specified as non-NULL. + All reported touch hits are guaranteed to be closer than the closest blocking hit. + + \param[in] buffer Callback will report touch hits to the user in this buffer. This pointer will be the same as #touches. + \param[in] nbHits Number of touch hits reported in buffer. This number will not exceed #maxNbTouches. + + \note There is a significant performance penalty in case multiple touch callbacks are issued (up to 2x) + \note to avoid the penalty use a bigger buffer so that all touching hits can be reported in a single buffer. + \note If true (again) is returned from the callback, nbTouches will be reset to 0, + \note If false is returned, nbTouched will remain unchanged. + \note By the time processTouches is first called, the globally closest blocking hit is already determined, + \note values of hasBlock and block are final and all touch hits are guaranteed to be closer than the blocking hit. + \note touches and maxNbTouches can be modified inside of processTouches callback. + + \return true to continue receiving callbacks in case there are more hits or false to stop. + + @see PxAgain PxRaycastHit PxSweepHit PxOverlapHit */ + virtual PxAgain processTouches(const HitType* buffer, PxU32 nbHits) = 0; + + virtual void finalizeQuery() {} //!< Query finalization callback, called after the last processTouches callback. + + virtual ~PxHitCallback() {} + + /** \brief Returns true if any blocking or touching hits were encountered during a query. */ + PX_FORCE_INLINE bool hasAnyHits() { return (hasBlock || (nbTouches > 0)); } +}; + + +/** +\brief Returns scene query hits (intersections) to the user in a preallocated buffer. + +Will clip touch hits to maximum buffer capacity. When clipped, an arbitrary subset of touching hits will be discarded. +Overflow does not trigger warnings or errors. block and hasBlock will be valid in finalizeQuery callback and after query completion. +Touching hits are guaranteed to have closer or same distance ( <= condition) as the globally nearest blocking hit at the time any processTouches() +callback is issued. + +\note Pre-made typedef shorthands, such as ::PxRaycastBuffer can be used for raycast, overlap and sweep queries. + +@see PxHitCallback +@see PxRaycastBuffer PxOverlapBuffer PxSweepBuffer PxRaycastBufferN PxOverlapBufferN PxSweepBufferN +*/ +template +struct PxHitBuffer : public PxHitCallback +{ + /** + \brief Initializes the buffer with user memory. + + The buffer is initialized with 0 touch hits by default => query will only report a single closest blocking hit. + Use PxQueryFlag::eANY_HIT to tell the query to abort and return any first hit encoutered as blocking. + + \param[in] aTouches Optional buffer for recording PxQueryHitType::eTOUCH type hits. + \param[in] aMaxNbTouches Size of touch buffer. + + @see PxHitCallback */ + PxHitBuffer(HitType* aTouches = NULL, PxU32 aMaxNbTouches = 0) : PxHitCallback(aTouches, aMaxNbTouches) {} + + /** \brief Computes the number of any hits in this result, blocking or touching. */ + PX_INLINE PxU32 getNbAnyHits() const { return getNbTouches() + PxU32(this->hasBlock); } + /** \brief Convenience iterator used to access any hits in this result, blocking or touching. */ + PX_INLINE const HitType& getAnyHit(const PxU32 index) const { PX_ASSERT(index < getNbTouches() + PxU32(this->hasBlock)); + return index < getNbTouches() ? getTouches()[index] : this->block; } + + PX_INLINE PxU32 getNbTouches() const { return this->nbTouches; } + PX_INLINE const HitType* getTouches() const { return this->touches; } + PX_INLINE const HitType& getTouch(const PxU32 index) const { PX_ASSERT(index < getNbTouches()); return getTouches()[index]; } + PX_INLINE PxU32 getMaxNbTouches() const { return this->maxNbTouches; } + + virtual ~PxHitBuffer() {} + +protected: + // stops after the first callback + virtual PxAgain processTouches(const HitType* buffer, PxU32 nbHits) { PX_UNUSED(buffer); PX_UNUSED(nbHits); return false; } +}; + + +/** \brief Raycast query callback. */ +typedef PxHitCallback PxRaycastCallback; + +/** \brief Overlap query callback. */ +typedef PxHitCallback PxOverlapCallback; + +/** \brief Sweep query callback. */ +typedef PxHitCallback PxSweepCallback; + +/** \brief Raycast query buffer. */ +typedef PxHitBuffer PxRaycastBuffer; + +/** \brief Overlap query buffer. */ +typedef PxHitBuffer PxOverlapBuffer; + +/** \brief Sweep query buffer. */ +typedef PxHitBuffer PxSweepBuffer; + +/** \brief Returns touching raycast hits to the user in a fixed size array embedded in the buffer class. **/ +template +struct PxRaycastBufferN : public PxHitBuffer +{ + PxRaycastHit hits[N]; + PxRaycastBufferN() : PxHitBuffer(hits, N) {} +}; + +/** \brief Returns touching overlap hits to the user in a fixed size array embedded in the buffer class. **/ +template +struct PxOverlapBufferN : public PxHitBuffer +{ + PxOverlapHit hits[N]; + PxOverlapBufferN() : PxHitBuffer(hits, N) {} +}; + +/** \brief Returns touching sweep hits to the user in a fixed size array embedded in the buffer class. **/ +template +struct PxSweepBufferN : public PxHitBuffer +{ + PxSweepHit hits[N]; + PxSweepBufferN() : PxHitBuffer(hits, N) {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxRigidActor.h b/src/PhysX/physx/include/PxRigidActor.h new file mode 100644 index 000000000..950f35e59 --- /dev/null +++ b/src/PhysX/physx/include/PxRigidActor.h @@ -0,0 +1,237 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_RIGIDACTOR +#define PX_PHYSICS_NX_RIGIDACTOR +/** \addtogroup physics +@{ +*/ + +#include "PxActor.h" +#include "PxShape.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxConstraint; +class PxMaterial; +class PxGeometry; +class PxBVHStructure; + +/** +\brief PxRigidActor represents a base class shared between dynamic and static rigid bodies in the physics SDK. + +PxRigidActor objects specify the geometry of the object by defining a set of attached shapes (see #PxShape). + +@see PxActor +*/ + +class PxRigidActor : public PxActor +{ +public: + /** + \brief Deletes the rigid actor object. + + Also releases any shapes associated with the actor. + + Releasing an actor will affect any objects that are connected to the actor (constraint shaders like joints etc.). + Such connected objects will be deleted upon scene deletion, or explicitly by the user by calling release() + on these objects. It is recommended to always remove all objects that reference actors before the actors + themselves are removed. It is not possible to retrieve list of dead connected objects. + + Sleeping: This call will awaken any sleeping actors contacting the deleted actor (directly or indirectly). + + Calls #PxActor::release() so you might want to check the documentation of that method as well. + + @see PxActor::release() + */ + virtual void release() = 0; + + +/************************************************************************************************/ +/** @name Global Pose Manipulation +*/ + + /** + \brief Retrieves the actors world space transform. + + The getGlobalPose() method retrieves the actor's current actor space to world space transformation. + + \return Global pose of object. + + @see PxRigidDynamic.setGlobalPose() PxRigidStatic.setGlobalPose() + */ + virtual PxTransform getGlobalPose() const = 0; + + /** + \brief Method for setting an actor's pose in the world. + + This method instantaneously changes the actor space to world space transformation. + + This method is mainly for dynamic rigid bodies (see #PxRigidDynamic). Calling this method on static actors is + likely to result in a performance penalty, since internal optimization structures for static actors may need to be + recomputed. In addition, moving static actors will not interact correctly with dynamic actors or joints. + + To directly control an actor's position and have it correctly interact with dynamic bodies and joints, create a dynamic + body with the PxRigidBodyFlag::eKINEMATIC flag, then use the setKinematicTarget() commands to define its path. + + Even when moving dynamic actors, exercise restraint in making use of this method. Where possible, avoid: + + \li moving actors into other actors, thus causing overlap (an invalid physical state) + + \li moving an actor that is connected by a joint to another away from the other (thus causing joint error) + + Sleeping: This call wakes dynamic actors if they are sleeping and the autowake parameter is true (default). + + \param[in] pose Transformation from the actors local frame to the global frame. Range: rigid body transform. + \param[in] autowake whether to wake the object if it is dynamic. This parameter has no effect for static or kinematic actors. If true and the current wake counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see getGlobalPose() + */ + virtual void setGlobalPose(const PxTransform& pose, bool autowake = true) = 0; + + +/************************************************************************************************/ +/** @name Shapes +*/ + + /** attach a shared shape to an actor + + This call will increment the reference count of the shape. + + \note Mass properties of dynamic rigid actors will not automatically be recomputed + to reflect the new mass distribution implied by the shape. Follow this call with a call to + the PhysX extensions method #PxRigidBodyExt::updateMassAndInertia() to do that. + + Attaching a triangle mesh, heightfield or plane geometry shape configured as eSIMULATION_SHAPE is not supported for + non-kinematic PxRigidDynamic instances. + + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] shape the shape to attach. + + \return True if success. + */ + virtual bool attachShape(PxShape& shape) = 0; + + + /** detach a shape from an actor. + + This will also decrement the reference count of the PxShape, and if the reference count is zero, will cause it to be deleted. + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] shape the shape to detach. + \param[in] wakeOnLostTouch Specifies whether touching objects from the previous frame should get woken up in the next frame. Only applies to PxArticulation and PxRigidActor types. + + */ + virtual void detachShape(PxShape& shape, bool wakeOnLostTouch = true) = 0; + + + /** + \brief Returns the number of shapes assigned to the actor. + + You can use #getShapes() to retrieve the shape pointers. + + \return Number of shapes associated with this actor. + + @see PxShape getShapes() + */ + virtual PxU32 getNbShapes() const = 0; + + + /** + \brief Retrieve all the shape pointers belonging to the actor. + + These are the shapes used by the actor for collision detection. + + You can retrieve the number of shape pointers by calling #getNbShapes() + + Note: Removing shapes with #PxShape::release() will invalidate the pointer of the released shape. + + \param[out] userBuffer The buffer to store the shape pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first shape pointer to be retrieved + \return Number of shape pointers written to the buffer. + + @see PxShape getNbShapes() PxShape::release() + */ + virtual PxU32 getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + +/************************************************************************************************/ +/** @name Constraints +*/ + + /** + \brief Returns the number of constraint shaders attached to the actor. + + You can use #getConstraints() to retrieve the constraint shader pointers. + + \return Number of constraint shaders attached to this actor. + + @see PxConstraint getConstraints() + */ + virtual PxU32 getNbConstraints() const = 0; + + + /** + \brief Retrieve all the constraint shader pointers belonging to the actor. + + You can retrieve the number of constraint shader pointers by calling #getNbConstraints() + + Note: Removing constraint shaders with #PxConstraint::release() will invalidate the pointer of the released constraint. + + \param[out] userBuffer The buffer to store the constraint shader pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first constraint pointer to be retrieved + \return Number of constraint shader pointers written to the buffer. + + @see PxConstraint getNbConstraints() PxConstraint::release() + */ + virtual PxU32 getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + +protected: + PX_INLINE PxRigidActor(PxType concreteType, PxBaseFlags baseFlags) : PxActor(concreteType, baseFlags) {} + PX_INLINE PxRigidActor(PxBaseFlags baseFlags) : PxActor(baseFlags) {} + virtual ~PxRigidActor() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxRigidActor", name) || PxActor::isKindOf(name); } +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxRigidBody.h b/src/PhysX/physx/include/PxRigidBody.h new file mode 100644 index 000000000..f25628f3c --- /dev/null +++ b/src/PhysX/physx/include/PxRigidBody.h @@ -0,0 +1,701 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_RIGIDBODY +#define PX_PHYSICS_NX_RIGIDBODY +/** \addtogroup physics +@{ +*/ + +#include "PxRigidActor.h" +#include "PxForceMode.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + +/** +\brief Collection of flags describing the behavior of a rigid body. + +@see PxRigidBody.setRigidBodyFlag(), PxRigidBody.getRigidBodyFlags() +*/ + +struct PxRigidBodyFlag +{ + enum Enum + { + + /** + \brief Enables kinematic mode for the actor. + + Kinematic actors are special dynamic actors that are not + influenced by forces (such as gravity), and have no momentum. They are considered to have infinite + mass and can be moved around the world using the setKinematicTarget() method. They will push + regular dynamic actors out of the way. Kinematics will not collide with static or other kinematic objects. + + Kinematic actors are great for moving platforms or characters, where direct motion control is desired. + + You can not connect Reduced joints to kinematic actors. Lagrange joints work ok if the platform + is moving with a relatively low, uniform velocity. + + Sleeping: + \li Setting this flag on a dynamic actor will put the actor to sleep and set the velocities to 0. + \li If this flag gets cleared, the current sleep state of the actor will be kept. + + \note kinematic actors are incompatible with CCD so raising this flag will automatically clear eENABLE_CCD + + @see PxRigidDynamic.setKinematicTarget() + */ + eKINEMATIC = (1<<0), //!< Enable kinematic mode for the body. + + /** + \brief Use the kinematic target transform for scene queries. + + If this flag is raised, then scene queries will treat the kinematic target transform as the current pose + of the body (instead of using the actual pose). Without this flag, the kinematic target will only take + effect with respect to scene queries after a simulation step. + + @see PxRigidDynamic.setKinematicTarget() + */ + eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES = (1<<1), + + /** + \brief Enables swept integration for the actor. + + If this flag is raised and CCD is enabled on the scene, then this body will be simulated by the CCD system to ensure that collisions are not missed due to + high-speed motion. Note individual shape pairs still need to enable PxPairFlag::eDETECT_CCD_CONTACT in the collision filtering to enable the CCD to respond to + individual interactions. + + \note kinematic actors are incompatible with CCD so this flag will be cleared automatically when raised on a kinematic actor + + */ + eENABLE_CCD = (1<<2), //!< Enable CCD for the body. + + /** + \brief Enabled CCD in swept integration for the actor. + + If this flag is raised and CCD is enabled, CCD interactions will simulate friction. By default, friction is disabled in CCD interactions because + CCD friction has been observed to introduce some simulation artifacts. CCD friction was enabled in previous versions of the SDK. Raising this flag will result in behavior + that is a closer match for previous versions of the SDK. + + \note This flag requires PxRigidBodyFlag::eENABLE_CCD to be raised to have any effect. + */ + eENABLE_CCD_FRICTION = (1<<3), + + /** + \brief Register a rigid body for reporting pose changes by the simulation at an early stage. + + Sometimes it might be advantageous to get access to the new pose of a rigid body as early as possible and + not wait until the call to fetchResults() returns. Setting this flag will schedule the rigid body to get reported + in #PxSimulationEventCallback::onAdvance(). Please refer to the documentation of that callback to understand + the behavior and limitations of this functionality. + + @see PxSimulationEventCallback::onAdvance() + */ + eENABLE_POSE_INTEGRATION_PREVIEW = (1 << 4), + + /** + \brief Register a rigid body to dynamicly adjust contact offset based on velocity. This can be used to achieve a CCD effect. + */ + eENABLE_SPECULATIVE_CCD = (1 << 5), + + /** + \brief Permit CCD to limit maxContactImpulse. This is useful for use-cases like a destruction system but can cause visual artefacts so is not enabled by default. + */ + eENABLE_CCD_MAX_CONTACT_IMPULSE = (1 << 6), + + /** + \brief Carries over forces/accelerations between frames, rather than clearning them + */ + eRETAIN_ACCELERATIONS = (1<<7) + }; +}; + +/** +\brief collection of set bits defined in PxRigidBodyFlag. + +@see PxRigidBodyFlag +*/ +typedef PxFlags PxRigidBodyFlags; +PX_FLAGS_OPERATORS(PxRigidBodyFlag::Enum,PxU8) + +/** +\brief PxRigidBody is a base class shared between dynamic rigid body objects. + +@see PxRigidActor +*/ + +class PxRigidBody : public PxRigidActor +{ +public: + // Runtime modifications + +/************************************************************************************************/ +/** @name Mass Manipulation +*/ + + /** + \brief Sets the pose of the center of mass relative to the actor. + + \note Changing this transform will not move the actor in the world! + + \note Setting an unrealistic center of mass which is a long way from the body can make it difficult for + the SDK to solve constraints. Perhaps leading to instability and jittering bodies. + + Default: the identity transform + + \param[in] pose Mass frame offset transform relative to the actor frame. Range: rigid body transform. + + @see getCMassLocalPose() PxRigidBodyDesc.massLocalPose + */ + virtual void setCMassLocalPose(const PxTransform& pose) = 0; + + + /** + \brief Retrieves the center of mass pose relative to the actor frame. + + \return The center of mass pose relative to the actor frame. + + @see setCMassLocalPose() PxRigidBodyDesc.massLocalPose + */ + virtual PxTransform getCMassLocalPose() const = 0; + + + /** + \brief Sets the mass of a dynamic actor. + + The mass must be non-negative. + + setMass() does not update the inertial properties of the body, to change the inertia tensor + use setMassSpaceInertiaTensor() or the PhysX extensions method #PxRigidBodyExt::updateMassAndInertia(). + + \note A value of 0 is interpreted as infinite mass. + \note Values of 0 are not permitted for instances of PxArticulationLink but are permitted for instances of PxRigidDynamic. + + Default: 1.0 + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] mass New mass value for the actor. Range: [0, PX_MAX_F32) + + @see getMass() PxRigidBodyDesc.mass setMassSpaceInertiaTensor() + */ + virtual void setMass(PxReal mass) = 0; + + /** + \brief Retrieves the mass of the actor. + + \note A value of 0 is interpreted as infinite mass. + + \return The mass of this actor. + + @see setMass() PxRigidBodyDesc.mass setMassSpaceInertiaTensor() + */ + virtual PxReal getMass() const = 0; + + /** + \brief Retrieves the inverse mass of the actor. + + \return The inverse mass of this actor. + + @see setMass() PxRigidBodyDesc.mass setMassSpaceInertiaTensor() + */ + virtual PxReal getInvMass() const = 0; + + /** + \brief Sets the inertia tensor, using a parameter specified in mass space coordinates. + + Note that such matrices are diagonal -- the passed vector is the diagonal. + + If you have a non diagonal world/actor space inertia tensor(3x3 matrix). Then you need to + diagonalize it and set an appropriate mass space transform. See #setCMassLocalPose(). + + The inertia tensor elements must be non-negative. + + \note A value of 0 in an element is interpreted as infinite inertia along that axis. + \note Values of 0 are not permitted for instances of PxArticulationLink but are permitted for instances of PxRigidDynamic. + + Default: (1.0, 1.0, 1.0) + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] m New mass space inertia tensor for the actor. + + @see PxRigidBodyDesc.massSpaceInertia getMassSpaceInertia() setMass() setCMassLocalPose() + */ + virtual void setMassSpaceInertiaTensor(const PxVec3& m) = 0; + + /** + \brief Retrieves the diagonal inertia tensor of the actor relative to the mass coordinate frame. + + This method retrieves a mass frame inertia vector. + + \return The mass space inertia tensor of this actor. + + \note A value of 0 in an element is interpreted as infinite inertia along that axis. + + @see PxRigidBodyDesc.massSpaceInertia setMassSpaceInertiaTensor() setMass() setCMassLocalPose() + */ + virtual PxVec3 getMassSpaceInertiaTensor() const = 0; + + /** + \brief Retrieves the diagonal inverse inertia tensor of the actor relative to the mass coordinate frame. + + This method retrieves a mass frame inverse inertia vector. + + \note A value of 0 in an element is interpreted as infinite inertia along that axis. + + \return The mass space inverse inertia tensor of this actor. + + @see PxRigidBodyDesc.massSpaceInertia setMassSpaceInertiaTensor() setMass() setCMassLocalPose() + */ + virtual PxVec3 getMassSpaceInvInertiaTensor() const = 0; + + /************************************************************************************************/ + /** @name Damping + */ + + /** + \brief Sets the linear damping coefficient. + + Zero represents no damping. The damping coefficient must be nonnegative. + + Default: 0.0 + + \param[in] linDamp Linear damping coefficient. Range: [0, PX_MAX_F32) + + @see getLinearDamping() setAngularDamping() + */ + virtual void setLinearDamping(PxReal linDamp) = 0; + + /** + \brief Retrieves the linear damping coefficient. + + \return The linear damping coefficient associated with this actor. + + @see setLinearDamping() getAngularDamping() + */ + virtual PxReal getLinearDamping() const = 0; + + /** + \brief Sets the angular damping coefficient. + + Zero represents no damping. + + The angular damping coefficient must be nonnegative. + + Default: 0.05 + + \param[in] angDamp Angular damping coefficient. Range: [0, PX_MAX_F32) + + @see getAngularDamping() setLinearDamping() + */ + virtual void setAngularDamping(PxReal angDamp) = 0; + + /** + \brief Retrieves the angular damping coefficient. + + \return The angular damping coefficient associated with this actor. + + @see setAngularDamping() getLinearDamping() + */ + virtual PxReal getAngularDamping() const = 0; + + +/************************************************************************************************/ +/** @name Velocity +*/ + + + /** + \brief Retrieves the linear velocity of an actor. + + \return The linear velocity of the actor. + + @see PxRigidDynamic.setLinearVelocity() getAngularVelocity() + */ + virtual PxVec3 getLinearVelocity() const = 0; + + /** + \brief Sets the linear velocity of the actor. + + Note that if you continuously set the velocity of an actor yourself, + forces such as gravity or friction will not be able to manifest themselves, because forces directly + influence only the velocity/momentum of an actor. + + Default: (0.0, 0.0, 0.0) + + Sleeping: This call wakes the actor if it is sleeping, the autowake parameter is true (default) or the + new velocity is non-zero + + \note It is invalid to use this method if PxActorFlag::eDISABLE_SIMULATION is set. + + \param[in] linVel New linear velocity of actor. Range: velocity vector + \param[in] autowake Whether to wake the object up if it is asleep and the velocity is non-zero. If true and the current wake counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see getLinearVelocity() setAngularVelocity() + */ + virtual void setLinearVelocity(const PxVec3& linVel, bool autowake = true ) = 0; + + + + /** + \brief Retrieves the angular velocity of the actor. + + \return The angular velocity of the actor. + + @see PxRigidDynamic.setAngularVelocity() getLinearVelocity() + */ + virtual PxVec3 getAngularVelocity() const = 0; + + + /** + \brief Sets the angular velocity of the actor. + + Note that if you continuously set the angular velocity of an actor yourself, + forces such as friction will not be able to rotate the actor, because forces directly influence only the velocity/momentum. + + Default: (0.0, 0.0, 0.0) + + Sleeping: This call wakes the actor if it is sleeping, the autowake parameter is true (default) or the + new velocity is non-zero + + \note It is invalid to use this method if PxActorFlag::eDISABLE_SIMULATION is set. + + \param[in] angVel New angular velocity of actor. Range: angular velocity vector + \param[in] autowake Whether to wake the object up if it is asleep and the velocity is non-zero. If true and the current wake + counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see getAngularVelocity() setLinearVelocity() + */ + virtual void setAngularVelocity(const PxVec3& angVel, bool autowake = true ) = 0; + + /** + \brief Lets you set the maximum angular velocity permitted for this actor. + + For various internal computations, very quickly rotating actors introduce error + into the simulation, which leads to undesired results. + + With this function, you can set the maximum angular velocity permitted for this rigid body. + Higher angular velocities are clamped to this value. + + Note: The angular velocity is clamped to the set value before the solver, which means that + the limit may still be momentarily exceeded. + + Default: 100.0 + + \param[in] maxAngVel Max allowable angular velocity for actor. Range: [0, PX_MAX_F32) + + @see getMaxAngularVelocity() + */ + virtual void setMaxAngularVelocity(PxReal maxAngVel) = 0; + + /** + \brief Retrieves the maximum angular velocity permitted for this actor. + + \return The maximum allowed angular velocity for this actor. + + @see setMaxAngularVelocity + */ + virtual PxReal getMaxAngularVelocity() const = 0; + + + /** + \brief Lets you set the maximum linear velocity permitted for this actor. + + With this function, you can set the maximum linear velocity permitted for this rigid body. + Higher angular velocities are clamped to this value. + + Note: The angular velocity is clamped to the set value before the solver, which means that + the limit may still be momentarily exceeded. + + Default: PX_MAX_F32 + + \param[in] maxLinVel Max allowable linear velocity for actor. Range: [0, PX_MAX_F32) + + @see getMaxAngularVelocity() + */ + virtual void setMaxLinearVelocity(PxReal maxLinVel) = 0; + + /** + \brief Retrieves the maximum angular velocity permitted for this actor. + + \return The maximum allowed angular velocity for this actor. + + @see setMaxLinearVelocity + */ + virtual PxReal getMaxLinearVelocity() const = 0; + + +/************************************************************************************************/ +/** @name Forces +*/ + + /** + \brief Applies a force (or impulse) defined in the global coordinate frame to the actor at its center of mass. + + This will not induce a torque. + + ::PxForceMode determines if the force is to be conventional or impulsive. + + Each actor has an acceleration and a velocity change accumulator which are directly modified using the modes PxForceMode::eACCELERATION + and PxForceMode::eVELOCITY_CHANGE respectively. The modes PxForceMode::eFORCE and PxForceMode::eIMPULSE also modify these same + accumulators and are just short hand for multiplying the vector parameter by inverse mass and then using PxForceMode::eACCELERATION and + PxForceMode::eVELOCITY_CHANGE respectively. + + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \note The force modes PxForceMode::eIMPULSE and PxForceMode::eVELOCITY_CHANGE can not be applied to articulation links. + + \note if this is called on an articulation link, only the link is updated, not the entire articulation. + + \note see #PxRigidBodyExt::computeVelocityDeltaFromImpulse for details of how to compute the change in linear velocity that + will arise from the application of an impulsive force, where an impulsive force is applied force multiplied by a timestep. + + Sleeping: This call wakes the actor if it is sleeping and the autowake parameter is true (default) or the force is non-zero. + + \param[in] force Force/Impulse to apply defined in the global frame. + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode) + \param[in] autowake Specify if the call should wake up the actor if it is currently asleep. If true and the current wake counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see PxForceMode addTorque + */ + virtual void addForce(const PxVec3& force, PxForceMode::Enum mode = PxForceMode::eFORCE, bool autowake = true) = 0; + + /** + \brief Applies an impulsive torque defined in the global coordinate frame to the actor. + + ::PxForceMode determines if the torque is to be conventional or impulsive. + + Each actor has an angular acceleration and an angular velocity change accumulator which are directly modified using the modes + PxForceMode::eACCELERATION and PxForceMode::eVELOCITY_CHANGE respectively. The modes PxForceMode::eFORCE and PxForceMode::eIMPULSE + also modify these same accumulators and are just short hand for multiplying the vector parameter by inverse inertia and then + using PxForceMode::eACCELERATION and PxForceMode::eVELOCITY_CHANGE respectively. + + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \note The force modes PxForceMode::eIMPULSE and PxForceMode::eVELOCITY_CHANGE can not be applied to articulation links. + + \note if this called on an articulation link, only the link is updated, not the entire articulation. + + \note see #PxRigidBodyExt::computeVelocityDeltaFromImpulse for details of how to compute the change in angular velocity that + will arise from the application of an impulsive torque, where an impulsive torque is an applied torque multiplied by a timestep. + + Sleeping: This call wakes the actor if it is sleeping and the autowake parameter is true (default) or the torque is non-zero. + + \param[in] torque Torque to apply defined in the global frame. Range: torque vector + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode). + \param[in] autowake whether to wake up the object if it is asleep. If true and the current wake counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see PxForceMode addForce() + */ + virtual void addTorque(const PxVec3& torque, PxForceMode::Enum mode = PxForceMode::eFORCE, bool autowake = true) = 0; + + /** + \brief Clears the accumulated forces (sets the accumulated force back to zero). + + Each actor has an acceleration and a velocity change accumulator which are directly modified using the modes PxForceMode::eACCELERATION + and PxForceMode::eVELOCITY_CHANGE respectively. The modes PxForceMode::eFORCE and PxForceMode::eIMPULSE also modify these same + accumulators (see PxRigidBody::addForce() for details); therefore the effect of calling clearForce(PxForceMode::eFORCE) is equivalent to calling + clearForce(PxForceMode::eACCELERATION), and the effect of calling clearForce(PxForceMode::eIMPULSE) is equivalent to calling + clearForce(PxForceMode::eVELOCITY_CHANGE). + + ::PxForceMode determines if the cleared force is to be conventional or impulsive. + + \note The force modes PxForceMode::eIMPULSE and PxForceMode::eVELOCITY_CHANGE can not be applied to articulation links. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \param[in] mode The mode to use when clearing the force/impulse(see #PxForceMode) + + @see PxForceMode addForce + */ + virtual void clearForce(PxForceMode::Enum mode = PxForceMode::eFORCE) = 0; + + /** + \brief Clears the impulsive torque defined in the global coordinate frame to the actor. + + ::PxForceMode determines if the cleared torque is to be conventional or impulsive. + + Each actor has an angular acceleration and a velocity change accumulator which are directly modified using the modes PxForceMode::eACCELERATION + and PxForceMode::eVELOCITY_CHANGE respectively. The modes PxForceMode::eFORCE and PxForceMode::eIMPULSE also modify these same + accumulators (see PxRigidBody::addTorque() for details); therefore the effect of calling clearTorque(PxForceMode::eFORCE) is equivalent to calling + clearTorque(PxForceMode::eACCELERATION), and the effect of calling clearTorque(PxForceMode::eIMPULSE) is equivalent to calling + clearTorque(PxForceMode::eVELOCITY_CHANGE). + + \note The force modes PxForceMode::eIMPULSE and PxForceMode::eVELOCITY_CHANGE can not be applied to articulation links. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \param[in] mode The mode to use when clearing the force/impulse(see #PxForceMode). + + @see PxForceMode addTorque + */ + virtual void clearTorque(PxForceMode::Enum mode = PxForceMode::eFORCE) = 0; + + + /** + \brief Sets the impulsive force and torque defined in the global coordinate frame to the actor. + + ::PxForceMode determines if the cleared torque is to be conventional or impulsive. + + \note The force modes PxForceMode::eIMPULSE and PxForceMode::eVELOCITY_CHANGE can not be applied to articulation links. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + @see PxForceMode addTorque + */ + virtual void setForceAndTorque(const PxVec3& force, const PxVec3& torque, PxForceMode::Enum mode = PxForceMode::eFORCE) = 0; + + /** + \brief Raises or clears a particular rigid body flag. + + See the list of flags #PxRigidBodyFlag + + Default: no flags are set + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] flag The PxRigidBody flag to raise(set) or clear. See #PxRigidBodyFlag. + \param[in] value The new boolean value for the flag. + + @see PxRigidBodyFlag getRigidBodyFlags() + */ + + virtual void setRigidBodyFlag(PxRigidBodyFlag::Enum flag, bool value) = 0; + virtual void setRigidBodyFlags(PxRigidBodyFlags inFlags) = 0; + + /** + \brief Reads the PxRigidBody flags. + + See the list of flags #PxRigidBodyFlag + + \return The values of the PxRigidBody flags. + + @see PxRigidBodyFlag setRigidBodyFlag() + */ + virtual PxRigidBodyFlags getRigidBodyFlags() const = 0; + + /** + \brief Sets the CCD minimum advance coefficient. + + The CCD minimum advance coefficient is a value in the range [0, 1] that is used to control the minimum amount of time a body is integrated when + it has a CCD contact. The actual minimum amount of time that is integrated depends on various properties, including the relative speed and collision shapes + of the bodies involved in the contact. From these properties, a numeric value is calculated that determines the maximum distance (and therefore maximum time) + which these bodies could be integrated forwards that would ensure that these bodies did not pass through each-other. This value is then scaled by CCD minimum advance + coefficient to determine the amount of time that will be consumed in the CCD pass. + + Things to consider: + A large value (approaching 1) ensures that the objects will always advance some time. However, larger values increase the chances of objects gently drifting through each-other in + scenes which the constraint solver can't converge, e.g. scenes where an object is being dragged through a wall with a constraint. + A value of 0 ensures that the pair of objects stop at the exact time-of-impact and will not gently drift through each-other. However, with very small/thin objects initially in + contact, this can lead to a large amount of time being dropped and increases the chances of jamming. Jamming occurs when the an object is persistently in contact with an object + such that the time-of-impact is 0, which results in no time being advanced for those objects in that CCD pass. + + The chances of jamming can be reduced by increasing the number of CCD mass @see PxSceneDesc.ccdMaxPasses. However, increasing this number increases the CCD overhead. + + \param[in] advanceCoefficient The CCD min advance coefficient. Range: [0, 1] Default: 0.15 + */ + + virtual void setMinCCDAdvanceCoefficient(PxReal advanceCoefficient) = 0; + + /** + \brief Gets the CCD minimum advance coefficient. + + \return The value of the CCD min advance coefficient. + + @see setMinCCDAdvanceCoefficient + + */ + + virtual PxReal getMinCCDAdvanceCoefficient() const = 0; + + + /** + \brief Sets the maximum depenetration velocity permitted to be introduced by the solver. + This value controls how much velocity the solver can introduce to correct for penetrations in contacts. + \param[in] biasClamp The maximum velocity to de-penetrate by Range: (0, PX_MAX_F32]. + */ + virtual void setMaxDepenetrationVelocity(PxReal biasClamp) = 0; + + /** + \brief Returns the maximum depenetration velocity the solver is permitted to introduced. + This value controls how much velocity the solver can introduce to correct for penetrations in contacts. + \return The maximum penetration bias applied by the solver. + */ + virtual PxReal getMaxDepenetrationVelocity() const = 0; + + + /** + \brief Sets a limit on the impulse that may be applied at a contact. The maximum impulse at a contact between two dynamic or kinematic + bodies will be the minimum of the two limit values. For a collision between a static and a dynamic body, the impulse is limited + by the value for the dynamic body. + + \param[in] maxImpulse the maximum contact impulse. Range: [0, PX_MAX_F32] Default: PX_MAX_F32 + + @see getMaxContactImpulse + */ + virtual void setMaxContactImpulse(PxReal maxImpulse) = 0; + + /** + \brief Returns the maximum impulse that may be applied at a contact. + + \return The maximum impulse that may be applied at a contact + + @see setMaxContactImpulse + */ + virtual PxReal getMaxContactImpulse() const = 0; + + /** + \brief Returns the island node index that only for internal use only + + \return The island node index that only for internal use only + */ + virtual PxU32 getInternalIslandNodeIndex() const = 0; + + +protected: + PX_INLINE PxRigidBody(PxType concreteType, PxBaseFlags baseFlags) : PxRigidActor(concreteType, baseFlags) {} + PX_INLINE PxRigidBody(PxBaseFlags baseFlags) : PxRigidActor(baseFlags) {} + virtual ~PxRigidBody() {} + virtual bool isKindOf(const char* name)const { return !::strcmp("PxRigidBody", name) || PxRigidActor::isKindOf(name); } +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxRigidDynamic.h b/src/PhysX/physx/include/PxRigidDynamic.h new file mode 100644 index 000000000..071e9a9d3 --- /dev/null +++ b/src/PhysX/physx/include/PxRigidDynamic.h @@ -0,0 +1,388 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_RIGIDDYNAMIC +#define PX_PHYSICS_NX_RIGIDDYNAMIC +/** \addtogroup physics +@{ +*/ + +#include "PxRigidBody.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + +/** +\brief Collection of flags providing a mechanism to lock motion along/around a specific axis. + +@see PxRigidDynamic.setRigidDynamicLockFlag(), PxRigidBody.getRigidDynamicLockFlags() +*/ +struct PxRigidDynamicLockFlag +{ + enum Enum + { + eLOCK_LINEAR_X = (1 << 0), + eLOCK_LINEAR_Y = (1 << 1), + eLOCK_LINEAR_Z = (1 << 2), + eLOCK_ANGULAR_X = (1 << 3), + eLOCK_ANGULAR_Y = (1 << 4), + eLOCK_ANGULAR_Z = (1 << 5) + }; +}; + +typedef PxFlags PxRigidDynamicLockFlags; +PX_FLAGS_OPERATORS(PxRigidDynamicLockFlag::Enum, PxU16) + +/** +\brief PxRigidDynamic represents a dynamic rigid simulation object in the physics SDK. + +

Creation

+Instances of this class are created by calling #PxPhysics::createRigidDynamic() and deleted with #release(). + + +

Visualizations

+\li #PxVisualizationParameter::eACTOR_AXES +\li #PxVisualizationParameter::eBODY_AXES +\li #PxVisualizationParameter::eBODY_MASS_AXES +\li #PxVisualizationParameter::eBODY_LIN_VELOCITY +\li #PxVisualizationParameter::eBODY_ANG_VELOCITY + +@see PxRigidBody PxPhysics.createRigidDynamic() release() +*/ + +class PxRigidDynamic : public PxRigidBody +{ +public: + // Runtime modifications + + +/************************************************************************************************/ +/** @name Kinematic Actors +*/ + + /** + \brief Moves kinematically controlled dynamic actors through the game world. + + You set a dynamic actor to be kinematic using the PxRigidBodyFlag::eKINEMATIC flag + with setRigidBodyFlag(). + + The move command will result in a velocity that will move the body into + the desired pose. After the move is carried out during a single time step, + the velocity is returned to zero. Thus, you must continuously call + this in every time step for kinematic actors so that they move along a path. + + This function simply stores the move destination until the next simulation + step is processed, so consecutive calls will simply overwrite the stored target variable. + + The motion is always fully carried out. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + Sleeping: This call wakes the actor if it is sleeping and will set the wake counter to #PxSceneDesc::wakeCounterResetValue. + + \param[in] destination The desired pose for the kinematic actor, in the global frame. Range: rigid body transform. + + @see getKinematicTarget() PxRigidBodyFlag setRigidBodyFlag() + */ + virtual void setKinematicTarget(const PxTransform& destination) = 0; + + /** + \brief Get target pose of a kinematically controlled dynamic actor. + + \param[out] target Transform to write the target pose to. Only valid if the method returns true. + \return True if the actor is a kinematically controlled dynamic and the target has been set, else False. + + @see setKinematicTarget() PxRigidBodyFlag setRigidBodyFlag() + */ + virtual bool getKinematicTarget(PxTransform& target) const = 0; + + +/************************************************************************************************/ +/** @name Sleeping +*/ + + /** + \brief Returns true if this body is sleeping. + + When an actor does not move for a period of time, it is no longer simulated in order to save time. This state + is called sleeping. However, because the object automatically wakes up when it is either touched by an awake object, + or one of its properties is changed by the user, the entire sleep mechanism should be transparent to the user. + + In general, a dynamic rigid actor is guaranteed to be awake if at least one of the following holds: + + \li The wake counter is positive (see #setWakeCounter()). + \li The linear or angular velocity is non-zero. + \li A non-zero force or torque has been applied. + + If a dynamic rigid actor is sleeping, the following state is guaranteed: + + \li The wake counter is zero. + \li The linear and angular velocity is zero. + \li There is no force update pending. + + When an actor gets inserted into a scene, it will be considered asleep if all the points above hold, else it will be treated as awake. + + If an actor is asleep after the call to PxScene::fetchResults() returns, it is guaranteed that the pose of the actor + was not changed. You can use this information to avoid updating the transforms of associated objects. + + \note A kinematic actor is asleep unless a target pose has been set (in which case it will stay awake until the end of the next + simulation step where no target pose has been set anymore). The wake counter will get set to zero or to the reset value + #PxSceneDesc::wakeCounterResetValue in the case where a target pose has been set to be consistent with the definitions above. + + \note It is invalid to use this method if the actor has not been added to a scene already. + + \return True if the actor is sleeping. + + @see isSleeping() wakeUp() putToSleep() getSleepThreshold() + */ + virtual bool isSleeping() const = 0; + + + /** + \brief Sets the mass-normalized kinetic energy threshold below which an actor may go to sleep. + + Actors whose kinetic energy divided by their mass is below this threshold will be candidates for sleeping. + + Default: 5e-5f * PxTolerancesScale::speed * PxTolerancesScale::speed + + \param[in] threshold Energy below which an actor may go to sleep. Range: [0, PX_MAX_F32) + + @see isSleeping() getSleepThreshold() wakeUp() putToSleep() PxTolerancesScale + */ + virtual void setSleepThreshold(PxReal threshold) = 0; + + /** + \brief Returns the mass-normalized kinetic energy below which an actor may go to sleep. + + \return The energy threshold for sleeping. + + @see isSleeping() wakeUp() putToSleep() setSleepThreshold() + */ + virtual PxReal getSleepThreshold() const = 0; + + /** + \brief Sets the mass-normalized kinetic energy threshold below which an actor may participate in stabilization. + + Actors whose kinetic energy divided by their mass is above this threshold will not participate in stabilization. + + This value has no effect if PxSceneFlag::eENABLE_STABILIZATION was not enabled on the PxSceneDesc. + + Default: 1e-5f * PxTolerancesScale::speed * PxTolerancesScale::speed + + \param[in] threshold Energy below which an actor may participate in stabilization. Range: [0,inf) + + @see getStabilizationThreshold() PxSceneFlag::eENABLE_STABILIZATION + */ + virtual void setStabilizationThreshold(PxReal threshold) = 0; + + /** + \brief Returns the mass-normalized kinetic energy below which an actor may participate in stabilization. + + Actors whose kinetic energy divided by their mass is above this threshold will not participate in stabilization. + + \return The energy threshold for participating in stabilization. + + @see setStabilizationThreshold() PxSceneFlag::eENABLE_STABILIZATION + */ + virtual PxReal getStabilizationThreshold() const = 0; + + + /** + \brief Reads the PxRigidDynamic lock flags. + + See the list of flags #PxRigidDynamicLockFlag + + \return The values of the PxRigidDynamicLock flags. + + @see PxRigidDynamicLockFlag setRigidDynamicLockFlag() + */ + virtual PxRigidDynamicLockFlags getRigidDynamicLockFlags() const = 0; + + /** + \brief Raises or clears a particular rigid dynamic lock flag. + + See the list of flags #PxRigidDynamicLockFlag + + Default: no flags are set + + + \param[in] flag The PxRigidDynamicLockBody flag to raise(set) or clear. See #PxRigidBodyFlag. + \param[in] value The new boolean value for the flag. + + @see PxRigidDynamicLockFlag getRigidDynamicLockFlags() + */ + virtual void setRigidDynamicLockFlag(PxRigidDynamicLockFlag::Enum flag, bool value) = 0; + virtual void setRigidDynamicLockFlags(PxRigidDynamicLockFlags flags) = 0; + + + + /** + \brief Sets the wake counter for the actor. + + The wake counter value determines the minimum amount of time until the body can be put to sleep. Please note + that a body will not be put to sleep if the energy is above the specified threshold (see #setSleepThreshold()) + or if other awake bodies are touching it. + + \note Passing in a positive value will wake the actor up automatically. + + \note It is invalid to use this method for kinematic actors since the wake counter for kinematics is defined + based on whether a target pose has been set (see the comment in #isSleeping()). + + \note It is invalid to use this method if PxActorFlag::eDISABLE_SIMULATION is set. + + Default: 0.4 (which corresponds to 20 frames for a time step of 0.02) + + \param[in] wakeCounterValue Wake counter value. Range: [0, PX_MAX_F32) + + @see isSleeping() getWakeCounter() + */ + virtual void setWakeCounter(PxReal wakeCounterValue) = 0; + + /** + \brief Returns the wake counter of the actor. + + \return The wake counter of the actor. + + @see isSleeping() setWakeCounter() + */ + virtual PxReal getWakeCounter() const = 0; + + /** + \brief Wakes up the actor if it is sleeping. + + The actor will get woken up and might cause other touching actors to wake up as well during the next simulation step. + + \note This will set the wake counter of the actor to the value specified in #PxSceneDesc::wakeCounterResetValue. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \note It is invalid to use this method for kinematic actors since the sleep state for kinematics is defined + based on whether a target pose has been set (see the comment in #isSleeping()). + + @see isSleeping() putToSleep() + */ + virtual void wakeUp() = 0; + + /** + \brief Forces the actor to sleep. + + The actor will stay asleep during the next simulation step if not touched by another non-sleeping actor. + + \note Any applied force will be cleared and the velocity and the wake counter of the actor will be set to 0. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \note It is invalid to use this method for kinematic actors since the sleep state for kinematics is defined + based on whether a target pose has been set (see the comment in #isSleeping()). + + @see isSleeping() wakeUp() + */ + virtual void putToSleep() = 0; + +/************************************************************************************************/ + + /** + \brief Sets the solver iteration counts for the body. + + The solver iteration count determines how accurately joints and contacts are resolved. + If you are having trouble with jointed bodies oscillating and behaving erratically, then + setting a higher position iteration count may improve their stability. + + If intersecting bodies are being depenetrated too violently, increase the number of velocity + iterations. More velocity iterations will drive the relative exit velocity of the intersecting + objects closer to the correct value given the restitution. + + Default: 4 position iterations, 1 velocity iteration + + \param[in] minPositionIters Number of position iterations the solver should perform for this body. Range: [1,255] + \param[in] minVelocityIters Number of velocity iterations the solver should perform for this body. Range: [1,255] + + @see getSolverIterationCounts() + */ + virtual void setSolverIterationCounts(PxU32 minPositionIters, PxU32 minVelocityIters = 1) = 0; + + /** + \brief Retrieves the solver iteration counts. + + @see setSolverIterationCounts() + */ + virtual void getSolverIterationCounts(PxU32& minPositionIters, PxU32& minVelocityIters) const = 0; + + /** + \brief Retrieves the force threshold for contact reports. + + The contact report threshold is a force threshold. If the force between + two actors exceeds this threshold for either of the two actors, a contact report + will be generated according to the contact report threshold flags provided by + the filter shader/callback. + See #PxPairFlag. + + The threshold used for a collision between a dynamic actor and the static environment is + the threshold of the dynamic actor, and all contacts with static actors are summed to find + the total normal force. + + Default: PX_MAX_F32 + + \return Force threshold for contact reports. + + @see setContactReportThreshold PxPairFlag PxSimulationFilterShader PxSimulationFilterCallback + */ + virtual PxReal getContactReportThreshold() const = 0; + + /** + \brief Sets the force threshold for contact reports. + + See #getContactReportThreshold(). + + \param[in] threshold Force threshold for contact reports. Range: [0, PX_MAX_F32) + + @see getContactReportThreshold PxPairFlag + */ + virtual void setContactReportThreshold(PxReal threshold) = 0; + + virtual const char* getConcreteTypeName() const { return "PxRigidDynamic"; } + +protected: + PX_INLINE PxRigidDynamic(PxType concreteType, PxBaseFlags baseFlags) : PxRigidBody(concreteType, baseFlags) {} + PX_INLINE PxRigidDynamic(PxBaseFlags baseFlags) : PxRigidBody(baseFlags) {} + virtual ~PxRigidDynamic() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxRigidDynamic", name) || PxRigidBody::isKindOf(name); } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxRigidStatic.h b/src/PhysX/physx/include/PxRigidStatic.h new file mode 100644 index 000000000..9d9281455 --- /dev/null +++ b/src/PhysX/physx/include/PxRigidStatic.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_RIGIDSTATIC +#define PX_PHYSICS_NX_RIGIDSTATIC +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxRigidActor.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief PxRigidStatic represents a static rigid body simulation object in the physics SDK. + +PxRigidStatic objects are static rigid physics entities. They shall be used to define solid objects which are fixed in the world. + +

Creation

+Instances of this class are created by calling #PxPhysics::createRigidStatic() and deleted with #release(). + +

Visualizations

+\li #PxVisualizationParameter::eACTOR_AXES + +@see PxRigidActor PxPhysics.createRigidStatic() release() +*/ + +class PxRigidStatic : public PxRigidActor +{ +public: + virtual const char* getConcreteTypeName() const { return "PxRigidStatic"; } + +protected: + PX_INLINE PxRigidStatic(PxType concreteType, PxBaseFlags baseFlags) : PxRigidActor(concreteType, baseFlags) {} + PX_INLINE PxRigidStatic(PxBaseFlags baseFlags) : PxRigidActor(baseFlags) {} + virtual ~PxRigidStatic() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxRigidStatic", name) || PxRigidActor::isKindOf(name); } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxScene.h b/src/PhysX/physx/include/PxScene.h new file mode 100644 index 000000000..49ea87f34 --- /dev/null +++ b/src/PhysX/physx/include/PxScene.h @@ -0,0 +1,1649 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENE +#define PX_PHYSICS_NX_SCENE +/** \addtogroup physics +@{ +*/ + +#include "PxVisualizationParameter.h" +#include "PxSceneDesc.h" +#include "PxSimulationStatistics.h" +#include "PxQueryReport.h" +#include "PxQueryFiltering.h" +#include "PxClient.h" +#include "task/PxTask.h" + +#include "pvd/PxPvdSceneClient.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxRigidStatic; +class PxRigidDynamic; +class PxConstraint; +class PxMaterial; +class PxSimulationEventCallback; +class PxPhysics; +class PxBatchQueryDesc; +class PxBatchQuery; +class PxAggregate; +class PxRenderBuffer; + +class PxSphereGeometry; +class PxBoxGeometry; +class PxCapsuleGeometry; + +class PxPruningStructure; +class PxBVHStructure; +struct PxContactPairHeader; + +typedef PxU8 PxDominanceGroup; + +class PxPvdSceneClient; + +/** +\brief Expresses the dominance relationship of a contact. +For the time being only three settings are permitted: + +(1, 1), (0, 1), and (1, 0). + +@see getDominanceGroup() PxDominanceGroup PxScene::setDominanceGroupPair() +*/ +struct PxDominanceGroupPair +{ + PxDominanceGroupPair(PxU8 a, PxU8 b) + : dominance0(a), dominance1(b) {} + PxU8 dominance0; + PxU8 dominance1; +}; + + +/** +\brief Identifies each type of actor for retrieving actors from a scene. + +\note #PxArticulationLink objects are not supported. Use the #PxArticulation object to retrieve all its links. + +@see PxScene::getActors(), PxScene::getNbActors() +*/ +struct PxActorTypeFlag +{ + enum Enum + { + /** + \brief A static rigid body + @see PxRigidStatic + */ + eRIGID_STATIC = (1 << 0), + + /** + \brief A dynamic rigid body + @see PxRigidDynamic + */ + eRIGID_DYNAMIC = (1 << 1) + }; +}; + +/** +\brief Collection of set bits defined in PxActorTypeFlag. + +@see PxActorTypeFlag +*/ +typedef PxFlags PxActorTypeFlags; +PX_FLAGS_OPERATORS(PxActorTypeFlag::Enum,PxU16) + +/** +\brief single hit cache for scene queries. + +If a cache object is supplied to a scene query, the cached actor/shape pair is checked for intersection first. +\note Filters are not executed for the cached shape. +\note If intersection is found, the hit is treated as blocking. +\note Typically actor and shape from the last PxHitCallback.block query result is used as a cached actor/shape pair. +\note Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. +\note Cache is only used if no touch buffer was provided, for single nearest blocking hit queries and queries using eANY_HIT flag. +\note if non-zero touch buffer was provided, cache will be ignored + +\note It is the user's responsibility to ensure that the shape and actor are valid, so care must be taken +when deleting shapes to invalidate cached references. + +The faceIndex field is an additional hint for a mesh or height field which is not currently used. + +@see PxScene.raycast +*/ +struct PxQueryCache +{ + /** + \brief constructor sets to default + */ + PX_INLINE PxQueryCache() : shape(NULL), actor(NULL), faceIndex(0xffffffff) {} + + /** + \brief constructor to set properties + */ + PX_INLINE PxQueryCache(PxShape* s, PxU32 findex) : shape(s), actor(NULL), faceIndex(findex) {} + + PxShape* shape; //!< Shape to test for intersection first + PxRigidActor* actor; //!< Actor to which the shape belongs + PxU32 faceIndex; //!< Triangle index to test first - NOT CURRENTLY SUPPORTED +}; + +/** + \brief A scene is a collection of bodies and constraints which can interact. + + The scene simulates the behavior of these objects over time. Several scenes may exist + at the same time, but each body or constraint is specific to a scene + -- they may not be shared. + + @see PxSceneDesc PxPhysics.createScene() release() +*/ +class PxScene +{ + protected: + + /************************************************************************************************/ + + /** @name Basics + */ + //@{ + + PxScene(): userData(0) {} + virtual ~PxScene() {} + + public: + + /** + \brief Deletes the scene. + + Removes any actors and constraint shaders from this scene + (if the user hasn't already done so). + + Be sure to not keep a reference to this object after calling release. + Avoid release calls while the scene is simulating (in between simulate() and fetchResults() calls). + + @see PxPhysics.createScene() + */ + virtual void release() = 0; + + /** + \brief Sets a scene flag. You can only set one flag at a time. + + \note Not all flags are mutable and changing some will result in an error. Please check #PxSceneFlag to see which flags can be changed. + + @see PxSceneFlag + */ + virtual void setFlag(PxSceneFlag::Enum flag, bool value) = 0; + + /** + \brief Get the scene flags. + + \return The scene flags. See #PxSceneFlag + + @see PxSceneFlag + */ + virtual PxSceneFlags getFlags() const = 0; + + + /** + \brief Set new scene limits. + + \note Increase the maximum capacity of various data structures in the scene. The new capacities will be + at least as large as required to deal with the objects currently in the scene. Further, these values + are for preallocation and do not represent hard limits. + + \param[in] limits Scene limits. + @see PxSceneLimits + */ + virtual void setLimits(const PxSceneLimits& limits) = 0; + + /** + \brief Get current scene limits. + \return Current scene limits. + @see PxSceneLimits + */ + virtual PxSceneLimits getLimits() const = 0; + + + /** + \brief Call this method to retrieve the Physics SDK. + + \return The physics SDK this scene is associated with. + + @see PxPhysics + */ + virtual PxPhysics& getPhysics() = 0; + + /** + \brief Retrieves the scene's internal timestamp, increased each time a simulation step is completed. + + \return scene timestamp + */ + virtual PxU32 getTimestamp() const = 0; + + + //@} + /************************************************************************************************/ + + /** @name Add/Remove Contained Objects + */ + //@{ + /** + \brief Adds an articulation to this scene. + + \note If the articulation is already assigned to a scene (see #PxArticulation::getScene), the call is ignored and an error is issued. + + \param[in] articulation Articulation to add to scene. See #PxArticulation + + @see PxArticulation + */ + virtual void addArticulation(PxArticulationBase& articulation) = 0; + + /** + \brief Removes an articulation from this scene. + + \note If the articulation is not part of this scene (see #PxArticulation::getScene), the call is ignored and an error is issued. + + \note If the articulation is in an aggregate it will be removed from the aggregate. + + \param[in] articulation Articulation to remove from scene. See #PxArticulation + \param[in] wakeOnLostTouch Specifies whether touching objects from the previous frame should get woken up in the next frame. Only applies to PxArticulation and PxRigidActor types. + + @see PxArticulation, PxAggregate + */ + virtual void removeArticulation(PxArticulationBase& articulation, bool wakeOnLostTouch = true) = 0; + + + //@} + /************************************************************************************************/ + + + /** + \brief Adds an actor to this scene. + + \note If the actor is already assigned to a scene (see #PxActor::getScene), the call is ignored and an error is issued. + \note If the actor has an invalid constraint, in checked builds the call is ignored and an error is issued. + + \note You can not add individual articulation links (see #PxArticulationLink) to the scene. Use #addArticulation() instead. + + \note If the actor is a PxRigidActor then each assigned PxConstraint object will get added to the scene automatically if + it connects to another actor that is part of the scene already. + + \note When BVHStructure is provided the actor shapes are grouped together. + The scene query pruning structure inside PhysX SDK will store/update one + bound per actor. The scene queries against such an actor will query actor + bounds and then make a local space query against the provided BVH structure, which is in + actor's local space. + + \param[in] actor Actor to add to scene. + \param[in] bvhStructure BVHStructure for actor shapes. + + @see PxActor, PxConstraint::isValid(), PxBVHStructure + */ + virtual void addActor(PxActor& actor, const PxBVHStructure* bvhStructure = NULL) = 0; + + /** + \brief Adds actors to this scene. + + \note If one of the actors is already assigned to a scene (see #PxActor::getScene), the call is ignored and an error is issued. + + \note You can not add individual articulation links (see #PxArticulationLink) to the scene. Use #addArticulation() instead. + + \note If an actor in the array contains an invalid constraint, in checked builds the call is ignored and an error is issued. + \note If an actor in the array is a PxRigidActor then each assigned PxConstraint object will get added to the scene automatically if + it connects to another actor that is part of the scene already. + + \note this method is optimized for high performance, and does not support buffering. It may not be called during simulation. + + \param[in] actors Array of actors to add to scene. + \param[in] nbActors Number of actors in the array. + + @see PxActor, PxConstraint::isValid() + */ + virtual void addActors(PxActor*const* actors, PxU32 nbActors) = 0; + + /** + \brief Adds a pruning structure together with its actors to this scene. + + \note If an actor in the pruning structure contains an invalid constraint, in checked builds the call is ignored and an error is issued. + \note For all actors in the pruning structure each assigned PxConstraint object will get added to the scene automatically if + it connects to another actor that is part of the scene already. + + \note This method is optimized for high performance, and does not support buffering. It may not be called during simulation. + + \note Merging a PxPruningStructure into an active scene query optimization AABB tree might unbalance the tree. A typical use case for + PxPruningStructure is a large world scenario where blocks of closely positioned actors get streamed in. The merge process finds the + best node in the active scene query optimization AABB tree and inserts the PxPruningStructure. Therefore using PxPruningStructure + for actors scattered throughout the world will result in an unbalanced tree. + + \param[in] pruningStructure Pruning structure for a set of actors. + + @see PxPhysics::createPruningStructure, PxPruningStructure + */ + virtual void addActors(const PxPruningStructure& pruningStructure) = 0; + + /** + \brief Removes an actor from this scene. + + \note If the actor is not part of this scene (see #PxActor::getScene), the call is ignored and an error is issued. + + \note You can not remove individual articulation links (see #PxArticulationLink) from the scene. Use #removeArticulation() instead. + + \note If the actor is a PxRigidActor then all assigned PxConstraint objects will get removed from the scene automatically. + + \note If the actor is in an aggregate it will be removed from the aggregate. + + \param[in] actor Actor to remove from scene. + \param[in] wakeOnLostTouch Specifies whether touching objects from the previous frame should get woken up in the next frame. Only applies to PxArticulation and PxRigidActor types. + + @see PxActor, PxAggregate + */ + virtual void removeActor(PxActor& actor, bool wakeOnLostTouch = true) = 0; + + /** + \brief Removes actors from this scene. + + \note If some actor is not part of this scene (see #PxActor::getScene), the actor remove is ignored and an error is issued. + + \note You can not remove individual articulation links (see #PxArticulationLink) from the scene. Use #removeArticulation() instead. + + \note If the actor is a PxRigidActor then all assigned PxConstraint objects will get removed from the scene automatically. + + \param[in] actors Array of actors to add to scene. + \param[in] nbActors Number of actors in the array. + \param[in] wakeOnLostTouch Specifies whether touching objects from the previous frame should get woken up in the next frame. Only applies to PxArticulation and PxRigidActor types. + + @see PxActor + */ + virtual void removeActors(PxActor*const* actors, PxU32 nbActors, bool wakeOnLostTouch = true) = 0; + + /** + \brief Adds an aggregate to this scene. + + \note If the aggregate is already assigned to a scene (see #PxAggregate::getScene), the call is ignored and an error is issued. + \note If the aggregate contains an actor with an invalid constraint, in checked builds the call is ignored and an error is issued. + + \note If the aggregate already contains actors, those actors are added to the scene as well. + + \param[in] aggregate Aggregate to add to scene. + + @see PxAggregate, PxConstraint::isValid() + */ + virtual void addAggregate(PxAggregate& aggregate) = 0; + + /** + \brief Removes an aggregate from this scene. + + \note If the aggregate is not part of this scene (see #PxAggregate::getScene), the call is ignored and an error is issued. + + \note If the aggregate contains actors, those actors are removed from the scene as well. + + \param[in] aggregate Aggregate to remove from scene. + \param[in] wakeOnLostTouch Specifies whether touching objects from the previous frame should get woken up in the next frame. Only applies to PxArticulation and PxRigidActor types. + + @see PxAggregate + */ + virtual void removeAggregate(PxAggregate& aggregate, bool wakeOnLostTouch = true) = 0; + + /** + \brief Adds objects in the collection to this scene. + + This function adds the following types of objects to this scene: PxActor, PxAggregate, PxArticulation. + This method is typically used after deserializing the collection in order to populate the scene with deserialized objects. + + \note If the collection contains an actor with an invalid constraint, in checked builds the call is ignored and an error is issued. + + \param[in] collection Objects to add to this scene. See #PxCollection + + @see PxCollection, PxConstraint::isValid() + */ + virtual void addCollection(const PxCollection& collection) = 0; + //@} + /************************************************************************************************/ + + /** @name Contained Object Retrieval + */ + //@{ + + /** + \brief Retrieve the number of actors of certain types in the scene. + + \param[in] types Combination of actor types. + \return the number of actors. + + @see getActors() + */ + virtual PxU32 getNbActors(PxActorTypeFlags types) const = 0; + + /** + \brief Retrieve an array of all the actors of certain types in the scene. + + \param[in] types Combination of actor types to retrieve. + \param[out] userBuffer The buffer to receive actor pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first actor pointer to be retrieved + \return Number of actors written to the buffer. + + @see getNbActors() + */ + virtual PxU32 getActors(PxActorTypeFlags types, PxActor** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Queries the PxScene for a list of the PxActors whose transforms have been + updated during the previous simulation step + + \note PxSceneFlag::eENABLE_ACTIVE_ACTORS must be set. + + \note Do not use this method while the simulation is running. Calls to this method while the simulation is running will be ignored and NULL will be returned. + + \param[out] nbActorsOut The number of actors returned. + + \return A pointer to the list of active PxActors generated during the last call to fetchResults(). + + @see PxActor + */ + virtual PxActor** getActiveActors(PxU32& nbActorsOut) = 0; + + /** + \brief Returns the number of articulations in the scene. + + \return the number of articulations in this scene. + + @see getArticulations() + */ + virtual PxU32 getNbArticulations() const = 0; + + /** + \brief Retrieve all the articulations in the scene. + + \param[out] userBuffer The buffer to receive articulations pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first articulations pointer to be retrieved + \return Number of articulations written to the buffer. + + @see getNbArticulations() + */ + virtual PxU32 getArticulations(PxArticulationBase** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Returns the number of constraint shaders in the scene. + + \return the number of constraint shaders in this scene. + + @see getConstraints() + */ + virtual PxU32 getNbConstraints() const = 0; + + /** + \brief Retrieve all the constraint shaders in the scene. + + \param[out] userBuffer The buffer to receive constraint shader pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first constraint pointer to be retrieved + \return Number of constraint shaders written to the buffer. + + @see getNbConstraints() + */ + virtual PxU32 getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + + /** + \brief Returns the number of aggregates in the scene. + + \return the number of aggregates in this scene. + + @see getAggregates() + */ + virtual PxU32 getNbAggregates() const = 0; + + /** + \brief Retrieve all the aggregates in the scene. + + \param[out] userBuffer The buffer to receive aggregates pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first aggregate pointer to be retrieved + \return Number of aggregates written to the buffer. + + @see getNbAggregates() + */ + virtual PxU32 getAggregates(PxAggregate** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + //@} + /************************************************************************************************/ + + /** @name Dominance + */ + //@{ + + /** + \brief Specifies the dominance behavior of contacts between two actors with two certain dominance groups. + + It is possible to assign each actor to a dominance groups using #PxActor::setDominanceGroup(). + + With dominance groups one can have all contacts created between actors act in one direction only. This is useful, for example, if you + want an object to push debris out of its way and be unaffected,while still responding physically to forces and collisions + with non-debris objects. + + Whenever a contact between two actors (a0, a1) needs to be solved, the groups (g0, g1) of both + actors are retrieved. Then the PxDominanceGroupPair setting for this group pair is retrieved with getDominanceGroupPair(g0, g1). + + In the contact, PxDominanceGroupPair::dominance0 becomes the dominance setting for a0, and + PxDominanceGroupPair::dominance1 becomes the dominance setting for a1. A dominanceN setting of 1.0f, the default, + will permit aN to be pushed or pulled by a(1-N) through the contact. A dominanceN setting of 0.0f, will however + prevent aN to be pushed by a(1-N) via the contact. Thus, a PxDominanceGroupPair of (1.0f, 0.0f) makes + the interaction one-way. + + + The matrix sampled by getDominanceGroupPair(g1, g2) is initialised by default such that: + + if g1 == g2, then (1.0f, 1.0f) is returned + if g1 < g2, then (0.0f, 1.0f) is returned + if g1 > g2, then (1.0f, 0.0f) is returned + + In other words, we permit actors in higher groups to be pushed around by actors in lower groups by default. + + These settings should cover most applications, and in fact not overriding these settings may likely result in higher performance. + + It is not possible to make the matrix asymetric, or to change the diagonal. In other words: + + * it is not possible to change (g1, g2) if (g1==g2) + * if you set + + (g1, g2) to X, then (g2, g1) will implicitly and automatically be set to ~X, where: + + ~(1.0f, 1.0f) is (1.0f, 1.0f) + ~(0.0f, 1.0f) is (1.0f, 0.0f) + ~(1.0f, 0.0f) is (0.0f, 1.0f) + + These two restrictions are to make sure that contacts between two actors will always evaluate to the same dominance + setting, regardless of the order of the actors. + + Dominance settings are currently specified as floats 0.0f or 1.0f because in the future we may permit arbitrary + fractional settings to express 'partly-one-way' interactions. + + Sleeping: Does NOT wake actors up automatically. + + @see getDominanceGroupPair() PxDominanceGroup PxDominanceGroupPair PxActor::setDominanceGroup() PxActor::getDominanceGroup() + */ + virtual void setDominanceGroupPair( + PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance) = 0; + + /** + \brief Samples the dominance matrix. + + @see setDominanceGroupPair() PxDominanceGroup PxDominanceGroupPair PxActor::setDominanceGroup() PxActor::getDominanceGroup() + */ + virtual PxDominanceGroupPair getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const = 0; + + //@} + /************************************************************************************************/ + + /** @name Dispatcher + */ + //@{ + + /** + \brief Return the cpu dispatcher that was set in PxSceneDesc::cpuDispatcher when creating the scene with PxPhysics::createScene + + @see PxSceneDesc::cpuDispatcher, PxPhysics::createScene + */ + virtual PxCpuDispatcher* getCpuDispatcher() const = 0; + + /** + \brief Return the gpu dispatcher that was set in PxSceneDesc::gpuDispatcher when creating the scene with PxPhysics::createScene + + Platform specific: Applies to PC GPU only. + + @see PxSceneDesc::gpuDispatcher, PxPhysics::createScene + */ + virtual PxGpuDispatcher* getGpuDispatcher() const = 0; + + //@} + /************************************************************************************************/ + /** @name Multiclient + */ + //@{ + /** + \brief Reserves a new client ID. + + PX_DEFAULT_CLIENT is always available as the default clientID. + Additional clients are returned by this function. Clients cannot be released once created. + An error is reported when more than a supported number of clients (currently 128) are created. + + @see PxClientID + */ + virtual PxClientID createClient() = 0; + + //@} + + /************************************************************************************************/ + + /** @name Callbacks + */ + //@{ + + /** + \brief Sets a user notify object which receives special simulation events when they occur. + + \note Do not set the callback while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[in] callback User notification callback. See #PxSimulationEventCallback. + + @see PxSimulationEventCallback getSimulationEventCallback + */ + virtual void setSimulationEventCallback(PxSimulationEventCallback* callback) = 0; + + /** + \brief Retrieves the simulationEventCallback pointer set with setSimulationEventCallback(). + + \return The current user notify pointer. See #PxSimulationEventCallback. + + @see PxSimulationEventCallback setSimulationEventCallback() + */ + virtual PxSimulationEventCallback* getSimulationEventCallback() const = 0; + + /** + \brief Sets a user callback object, which receives callbacks on all contacts generated for specified actors. + + \note Do not set the callback while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[in] callback Asynchronous user contact modification callback. See #PxContactModifyCallback. + */ + virtual void setContactModifyCallback(PxContactModifyCallback* callback) = 0; + + /** + \brief Sets a user callback object, which receives callbacks on all CCD contacts generated for specified actors. + + \note Do not set the callback while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[in] callback Asynchronous user contact modification callback. See #PxCCDContactModifyCallback. + */ + virtual void setCCDContactModifyCallback(PxCCDContactModifyCallback* callback) = 0; + + /** + \brief Retrieves the PxContactModifyCallback pointer set with setContactModifyCallback(). + + \return The current user contact modify callback pointer. See #PxContactModifyCallback. + + @see PxContactModifyCallback setContactModifyCallback() + */ + virtual PxContactModifyCallback* getContactModifyCallback() const = 0; + + /** + \brief Retrieves the PxCCDContactModifyCallback pointer set with setContactModifyCallback(). + + \return The current user contact modify callback pointer. See #PxContactModifyCallback. + + @see PxContactModifyCallback setContactModifyCallback() + */ + virtual PxCCDContactModifyCallback* getCCDContactModifyCallback() const = 0; + + /** + \brief Sets a broad-phase user callback object. + + \note Do not set the callback while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[in] callback Asynchronous broad-phase callback. See #PxBroadPhaseCallback. + */ + virtual void setBroadPhaseCallback(PxBroadPhaseCallback* callback) = 0; + + /** + \brief Retrieves the PxBroadPhaseCallback pointer set with setBroadPhaseCallback(). + + \return The current broad-phase callback pointer. See #PxBroadPhaseCallback. + + @see PxBroadPhaseCallback setBroadPhaseCallback() + */ + virtual PxBroadPhaseCallback* getBroadPhaseCallback() const = 0; + + //@} + /************************************************************************************************/ + + /** @name Collision Filtering + */ + //@{ + + /** + \brief Sets the shared global filter data which will get passed into the filter shader. + + \note It is the user's responsibility to ensure that changing the shared global filter data does not change the filter output value for existing pairs. + If the filter output for existing pairs does change nonetheless then such a change will not take effect until the pair gets refiltered. + resetFiltering() can be used to explicitly refilter the pairs of specific objects. + + \note The provided data will get copied to internal buffers and this copy will be used for filtering calls. + + \note Do not use this method while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[in] data The shared global filter shader data. + \param[in] dataSize Size of the shared global filter shader data (in bytes). + + @see getFilterShaderData() PxSceneDesc.filterShaderData PxSimulationFilterShader + */ + virtual void setFilterShaderData(const void* data, PxU32 dataSize) = 0; + + /** + \brief Gets the shared global filter data in use for this scene. + + \note The reference points to a copy of the original filter data specified in #PxSceneDesc.filterShaderData or provided by #setFilterShaderData(). + + \return Shared filter data for filter shader. + + @see getFilterShaderDataSize() setFilterShaderData() PxSceneDesc.filterShaderData PxSimulationFilterShader + */ + virtual const void* getFilterShaderData() const = 0; + + /** + \brief Gets the size of the shared global filter data (#PxSceneDesc.filterShaderData) + + \return Size of shared filter data [bytes]. + + @see getFilterShaderData() PxSceneDesc.filterShaderDataSize PxSimulationFilterShader + */ + virtual PxU32 getFilterShaderDataSize() const = 0; + + /** + \brief Gets the custom collision filter shader in use for this scene. + + \return Filter shader class that defines the collision pair filtering. + + @see PxSceneDesc.filterShader PxSimulationFilterShader + */ + virtual PxSimulationFilterShader + getFilterShader() const = 0; + + /** + \brief Gets the custom collision filter callback in use for this scene. + + \return Filter callback class that defines the collision pair filtering. + + @see PxSceneDesc.filterCallback PxSimulationFilterCallback + */ + virtual PxSimulationFilterCallback* + getFilterCallback() const = 0; + + /** + \brief Marks the object to reset interactions and re-run collision filters in the next simulation step. + + This call forces the object to remove all existing collision interactions, to search anew for existing contact + pairs and to run the collision filters again for found collision pairs. + + \note The operation is supported for PxRigidActor objects only. + + \note All persistent state of existing interactions will be lost and can not be retrieved even if the same collison pair + is found again in the next step. This will mean, for example, that you will not get notified about persistent contact + for such an interaction (see #PxPairFlag::eNOTIFY_TOUCH_PERSISTS), the contact pair will be interpreted as newly found instead. + + \note Lost touch contact reports will be sent for every collision pair which includes this shape, if they have + been requested through #PxPairFlag::eNOTIFY_TOUCH_LOST or #PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST. + + \note This is an expensive operation, don't use it if you don't have to. + + \note Can be used to retrieve collision pairs that were killed by the collision filters (see #PxFilterFlag::eKILL) + + \note It is invalid to use this method if the actor has not been added to a scene already. + + \note It is invalid to use this method if PxActorFlag::eDISABLE_SIMULATION is set. + + Sleeping: Does wake up the actor. + + \param[in] actor The actor for which to re-evaluate interactions. + + @see PxSimulationFilterShader PxSimulationFilterCallback + */ + virtual void resetFiltering(PxActor& actor) = 0; + + /** + \brief Marks the object to reset interactions and re-run collision filters for specified shapes in the next simulation step. + + This is a specialization of the resetFiltering(PxActor& actor) method and allows to reset interactions for specific shapes of + a PxRigidActor. + + Sleeping: Does wake up the actor. + + \param[in] actor The actor for which to re-evaluate interactions. + \param[in] shapes The shapes for which to re-evaluate interactions. + \param[in] shapeCount Number of shapes in the list. + + @see PxSimulationFilterShader PxSimulationFilterCallback + */ + virtual void resetFiltering(PxRigidActor& actor, PxShape*const* shapes, PxU32 shapeCount) = 0; + + //@} + /************************************************************************************************/ + + /** @name Simulation + */ + //@{ + /** + \brief Advances the simulation by an elapsedTime time. + + \note Large elapsedTime values can lead to instabilities. In such cases elapsedTime + should be subdivided into smaller time intervals and simulate() should be called + multiple times for each interval. + + Calls to simulate() should pair with calls to fetchResults(): + Each fetchResults() invocation corresponds to exactly one simulate() + invocation; calling simulate() twice without an intervening fetchResults() + or fetchResults() twice without an intervening simulate() causes an error + condition. + + scene->simulate(); + ...do some processing until physics is computed... + scene->fetchResults(); + ...now results of run may be retrieved. + + + \param[in] elapsedTime Amount of time to advance simulation by. The parameter has to be larger than 0, else the resulting behavior will be undefined. Range: (0, PX_MAX_F32) + \param[in] completionTask if non-NULL, this task will have its refcount incremented in simulate(), then + decremented when the scene is ready to have fetchResults called. So the task will not run until the + application also calls removeReference(). + \param[in] scratchMemBlock a memory region for physx to use for temporary data during simulation. This block may be reused by the application + after fetchResults returns. Must be aligned on a 16-byte boundary + \param[in] scratchMemBlockSize the size of the scratch memory block. Must be a multiple of 16K. + \param[in] controlSimulation if true, the scene controls its PxTaskManager simulation state. Leave + true unless the application is calling the PxTaskManager start/stopSimulation() methods itself. + + @see fetchResults() checkResults() + */ + virtual void simulate(PxReal elapsedTime, physx::PxBaseTask* completionTask = NULL, + void* scratchMemBlock = 0, PxU32 scratchMemBlockSize = 0, bool controlSimulation = true) = 0; + + + /** + \brief Performs dynamics phase of the simulation pipeline. + + \note Calls to advance() should follow calls to fetchCollision(). An error message will be issued if this sequence is not followed. + + \param[in] completionTask if non-NULL, this task will have its refcount incremented in advance(), then + decremented when the scene is ready to have fetchResults called. So the task will not run until the + application also calls removeReference(). + + */ + virtual void advance(physx::PxBaseTask* completionTask = 0) = 0; + + /** + \brief Performs collision detection for the scene over elapsedTime + + \note Calls to collide() should be the first method called to simulate a frame. + + + \param[in] elapsedTime Amount of time to advance simulation by. The parameter has to be larger than 0, else the resulting behavior will be undefined. Range: (0, PX_MAX_F32) + \param[in] completionTask if non-NULL, this task will have its refcount incremented in collide(), then + decremented when the scene is ready to have fetchResults called. So the task will not run until the + application also calls removeReference(). + \param[in] scratchMemBlock a memory region for physx to use for temporary data during simulation. This block may be reused by the application + after fetchResults returns. Must be aligned on a 16-byte boundary + \param[in] scratchMemBlockSize the size of the scratch memory block. Must be a multiple of 16K. + \param[in] controlSimulation if true, the scene controls its PxTaskManager simulation state. Leave + true unless the application is calling the PxTaskManager start/stopSimulation() methods itself. + + */ + virtual void collide(PxReal elapsedTime, physx::PxBaseTask* completionTask = 0, void* scratchMemBlock = 0, + PxU32 scratchMemBlockSize = 0, bool controlSimulation = true) = 0; + + /** + \brief This checks to see if the simulation run has completed. + + This does not cause the data available for reading to be updated with the results of the simulation, it is simply a status check. + The bool will allow it to either return immediately or block waiting for the condition to be met so that it can return true + + \param[in] block When set to true will block until the condition is met. + \return True if the results are available. + + @see simulate() fetchResults() + */ + virtual bool checkResults(bool block = false) = 0; + + /** + This method must be called after collide() and before advance(). It will wait for the collision phase to finish. If the user makes an illegal simulation call, the SDK will issue an error + message. + + \param[in] block When set to true will block until the condition is met, which is collision must finish running. + */ + + virtual bool fetchCollision(bool block = false) = 0; + + /** + This is the big brother to checkResults() it basically does the following: + + \code + if ( checkResults(block) ) + { + fire appropriate callbacks + swap buffers + return true + } + else + return false + + \endcode + + \param[in] block When set to true will block until results are available. + \param[out] errorState Used to retrieve hardware error codes. A non zero value indicates an error. + \return True if the results have been fetched. + + @see simulate() checkResults() + */ + virtual bool fetchResults(bool block = false, PxU32* errorState = 0) = 0; + + + /** + This call performs the first section of fetchResults (callbacks fired before swapBuffers), and returns a pointer to a + to the contact streams output by the simulation. It can be used to process contact pairs in parallel, which is often a limiting factor + for fetchResults() performance. + + After calling this function and processing the contact streams, call fetchResultsFinish(). Note that writes to the simulation are not + permitted between the start of fetchResultsStart() and the end of fetchResultsFinish(). + + \param[in] block When set to true will block until results are available. + \param[out] contactPairs an array of pointers to contact pair headers + \param[out] nbContactPairs the number of contact pairs + \return True if the results have been fetched. + + @see simulate() checkResults() fetchResults() fetchResultsFinish() + */ + virtual bool fetchResultsStart(const PxContactPairHeader*& contactPairs, PxU32& nbContactPairs, bool block = false) = 0; + + + /** + This call processes all event callbacks in parallel. It takes a continuation task, which will be executed once all callbacks have been processed. + + This is a utility function to make it easier to process callbacks in parallel using the PhysX task system. It can only be used in conjunction with + fetchResultsStart(...) and fetchResultsFinish(...) + + \param[in] continuation The task that will be executed once all callbacks have been processed. + */ + virtual void processCallbacks(physx::PxBaseTask* continuation) = 0; + + + /** + This call performs the second section of fetchResults: the buffer swap and subsequent callbacks. + + It must be called after fetchResultsStart() returns and contact reports have been processed. + + Note that once fetchResultsFinish() has been called, the contact streams returned in fetchResultsStart() will be invalid. + + \param[out] errorState Used to retrieve hardware error codes. A non zero value indicates an error. + + @see simulate() checkResults() fetchResults() fetchResultsStart() + */ + virtual void fetchResultsFinish(PxU32* errorState = 0) = 0; + + + /** + \brief Clear internal buffers and free memory. + + This method can be used to clear buffers and free internal memory without having to destroy the scene. Can be useful if + the physics data gets streamed in and a checkpoint with a clean state should be created. + + \note It is not allowed to call this method while the simulation is running. The call will fail. + + \param[in] sendPendingReports When set to true pending reports will be sent out before the buffers get cleaned up (for instance lost touch contact/trigger reports due to deleted objects). + */ + virtual void flushSimulation(bool sendPendingReports = false) = 0; + + /** + \brief Sets a constant gravity for the entire scene. + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] vec A new gravity vector(e.g. PxVec3(0.0f,-9.8f,0.0f) ) Range: force vector + + @see PxSceneDesc.gravity getGravity() + */ + virtual void setGravity(const PxVec3& vec) = 0; + + /** + \brief Retrieves the current gravity setting. + + \return The current gravity for the scene. + + @see setGravity() PxSceneDesc.gravity + */ + virtual PxVec3 getGravity() const = 0; + + /** + \brief Set the bounce threshold velocity. Collision speeds below this threshold will not cause a bounce. + + @see PxSceneDesc::bounceThresholdVelocity, getBounceThresholdVelocity + */ + virtual void setBounceThresholdVelocity(const PxReal t) = 0; + + /** + \brief Return the bounce threshold velocity. + + @see PxSceneDesc.bounceThresholdVelocity, setBounceThresholdVelocity + */ + virtual PxReal getBounceThresholdVelocity() const = 0; + + + /** + \brief Sets the maximum number of CCD passes + + \param[in] ccdMaxPasses Maximum number of CCD passes + + @see PxSceneDesc.ccdMaxPasses getCCDMaxPasses() + + */ + virtual void setCCDMaxPasses(PxU32 ccdMaxPasses) = 0; + + /** + \brief Gets the maximum number of CCD passes. + + \return The maximum number of CCD passes. + + @see PxSceneDesc::ccdMaxPasses setCCDMaxPasses() + + */ + virtual PxU32 getCCDMaxPasses() const = 0; + + /** + \brief Return the value of frictionOffsetThreshold that was set in PxSceneDesc when creating the scene with PxPhysics::createScene + + @see PxSceneDesc::frictionOffsetThreshold, PxPhysics::createScene + */ + virtual PxReal getFrictionOffsetThreshold() const = 0; + + /** + \brief Set the friction model. + @see PxFrictionType, PxSceneDesc::frictionType + */ + virtual void setFrictionType(PxFrictionType::Enum frictionType) = 0; + + /** + \brief Return the friction model. + @see PxFrictionType, PxSceneDesc::frictionType + */ + virtual PxFrictionType::Enum getFrictionType() const = 0; + + //@} + /************************************************************************************************/ + + /** @name Visualization and Statistics + */ + //@{ + /** + \brief Function that lets you set debug visualization parameters. + + Returns false if the value passed is out of range for usage specified by the enum. + + \param[in] param Parameter to set. See #PxVisualizationParameter + \param[in] value The value to set, see #PxVisualizationParameter for allowable values. Setting to zero disables visualization for the specified property, setting to a positive value usually enables visualization and defines the scale factor. + \return False if the parameter is out of range. + + @see getVisualizationParameter PxVisualizationParameter getRenderBuffer() + */ + virtual bool setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) = 0; + + /** + \brief Function that lets you query debug visualization parameters. + + \param[in] paramEnum The Parameter to retrieve. + \return The value of the parameter. + + @see setVisualizationParameter PxVisualizationParameter + */ + virtual PxReal getVisualizationParameter(PxVisualizationParameter::Enum paramEnum) const = 0; + + + /** + \brief Defines a box in world space to which visualization geometry will be (conservatively) culled. Use a non-empty culling box to enable the feature, and an empty culling box to disable it. + + \param[in] box the box to which the geometry will be culled. Empty box to disable the feature. + @see setVisualizationParameter getVisualizationCullingBox getRenderBuffer() + */ + virtual void setVisualizationCullingBox(const PxBounds3& box) = 0; + + /** + \brief Retrieves the visualization culling box. + + \return the box to which the geometry will be culled. + @see setVisualizationParameter setVisualizationCullingBox + */ + virtual PxBounds3 getVisualizationCullingBox() const = 0; + + /** + \brief Retrieves the render buffer. + + This will contain the results of any active visualization for this scene. + + \note Do not use this method while the simulation is running. Calls to this method while result in undefined behaviour. + + \return The render buffer. + + @see PxRenderBuffer + */ + virtual const PxRenderBuffer& getRenderBuffer() = 0; + + /** + \brief Call this method to retrieve statistics for the current simulation step. + + \note Do not use this method while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[out] stats Used to retrieve statistics for the current simulation step. + + @see PxSimulationStatistics + */ + virtual void getSimulationStatistics(PxSimulationStatistics& stats) const = 0; + + + //@} + /************************************************************************************************/ + + /** @name Scene Query + */ + //@{ + + /** + \brief Return the value of PxSceneDesc::staticStructure that was set when creating the scene with PxPhysics::createScene + + @see PxSceneDesc::staticStructure, PxPhysics::createScene + */ + virtual PxPruningStructureType::Enum getStaticStructure() const = 0; + + /** + \brief Return the value of PxSceneDesc::dynamicStructure that was set when creating the scene with PxPhysics::createScene + + @see PxSceneDesc::dynamicStructure, PxPhysics::createScene + */ + virtual PxPruningStructureType::Enum getDynamicStructure() const = 0; + + /** + \brief Flushes any changes in the simulation to the scene query representation. + + This method updates the state of the scene query representation to match changes in the scene state. + + By default, these changes are buffered until the next query is submitted. Calling this function will not change + the results from scene queries, but can be used to ensure that a query will not perform update work in the course of + its execution. + + A thread performing updates will hold a write lock on the query structure, and thus stall other querying threads. In multithread + scenarios it can be useful to explicitly schedule the period where this lock may be held for a significant period, so that + subsequent queries issued from multiple threads will not block. + + Note that while queries will block during the execution of this method, other read operations on the scene will not. + */ + virtual void flushQueryUpdates() = 0; + + /** + \brief Creates a BatchQuery object. + + Scene queries like raycasts, overlap tests and sweeps are batched in this object and are then executed at once. See #PxBatchQuery. + + \deprecated The batched query feature has been deprecated in PhysX version 3.4 + + \param[in] desc The descriptor of scene query. Scene Queries need to register a callback. See #PxBatchQueryDesc. + + @see PxBatchQuery PxBatchQueryDesc + */ + PX_DEPRECATED virtual PxBatchQuery* createBatchQuery(const PxBatchQueryDesc& desc) = 0; + + /** + \brief Sets the rebuild rate of the dynamic tree pruning structures. + + \param[in] dynamicTreeRebuildRateHint Rebuild rate of the dynamic tree pruning structures. + + @see PxSceneDesc.dynamicTreeRebuildRateHint getDynamicTreeRebuildRateHint() forceDynamicTreeRebuild() + */ + virtual void setDynamicTreeRebuildRateHint(PxU32 dynamicTreeRebuildRateHint) = 0; + + /** + \brief Retrieves the rebuild rate of the dynamic tree pruning structures. + + \return The rebuild rate of the dynamic tree pruning structures. + + @see PxSceneDesc.dynamicTreeRebuildRateHint setDynamicTreeRebuildRateHint() forceDynamicTreeRebuild() + */ + virtual PxU32 getDynamicTreeRebuildRateHint() const = 0; + + /** + \brief Forces dynamic trees to be immediately rebuilt. + + \param[in] rebuildStaticStructure True to rebuild the dynamic tree containing static objects + \param[in] rebuildDynamicStructure True to rebuild the dynamic tree containing dynamic objects + + @see PxSceneDesc.dynamicTreeRebuildRateHint setDynamicTreeRebuildRateHint() getDynamicTreeRebuildRateHint() + */ + virtual void forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure) = 0; + + /** + \brief Sets scene query update mode + + \param[in] updateMode Scene query update mode. + + @see PxSceneQueryUpdateMode::Enum + */ + virtual void setSceneQueryUpdateMode(PxSceneQueryUpdateMode::Enum updateMode) = 0; + + /** + \brief Gets scene query update mode + + \return Current scene query update mode. + + @see PxSceneQueryUpdateMode::Enum + */ + virtual PxSceneQueryUpdateMode::Enum getSceneQueryUpdateMode() const = 0; + + /** + \brief Executes scene queries update tasks. + This function will refit dirty shapes within the pruner and will execute a task to build a new AABB tree, which is + build on a different thread. The new AABB tree is built based on the dynamic tree rebuild hint rate. Once + the new tree is ready it will be commited in next fetchQueries call, which must be called after. + + \note If PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED is used, it is required to update the scene queries + using this function. + + \param[in] completionTask if non-NULL, this task will have its refcount incremented in sceneQueryUpdate(), then + decremented when the scene is ready to have fetchQueries called. So the task will not run until the + application also calls removeReference(). + \param[in] controlSimulation if true, the scene controls its PxTaskManager simulation state. Leave + true unless the application is calling the PxTaskManager start/stopSimulation() methods itself. + + @see PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED + */ + virtual void sceneQueriesUpdate(physx::PxBaseTask* completionTask = NULL, bool controlSimulation = true) = 0; + + /** + \brief This checks to see if the scene queries update has completed. + + This does not cause the data available for reading to be updated with the results of the scene queries update, it is simply a status check. + The bool will allow it to either return immediately or block waiting for the condition to be met so that it can return true + + \param[in] block When set to true will block until the condition is met. + \return True if the results are available. + + @see sceneQueriesUpdate() fetchResults() + */ + virtual bool checkQueries(bool block = false) = 0; + + /** + This method must be called after sceneQueriesUpdate. It will wait for the scene queries update to finish. If the user makes an illegal scene queries update call, + the SDK will issue an error message. + + If a new AABB tree build finished, then during fetchQueries the current tree within the pruning structure is swapped with the new tree. + + \param[in] block When set to true will block until the condition is met, which is tree built task must finish running. + */ + + virtual bool fetchQueries(bool block = false) = 0; + + /** + \brief Performs a raycast against objects in the scene, returns results in a PxRaycastBuffer object + or via a custom user callback implementation inheriting from PxRaycastCallback. + + \note Touching hits are not ordered. + \note Shooting a ray from within an object leads to different results depending on the shape type. Please check the details in user guide article SceneQuery. User can ignore such objects by employing one of the provided filter mechanisms. + + \param[in] origin Origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] distance Length of the ray. Has to be in the [0, inf) range. + \param[out] hitCall Raycast hit buffer or callback object used to report raycast hits. + \param[in] hitFlags Specifies which properties per hit should be computed and returned via the hit callback. + \param[in] filterData Filtering data passed to the filer shader. See #PxQueryFilterData #PxBatchQueryPreFilterShader, #PxBatchQueryPostFilterShader + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxQueryFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first. If no hit is found the ray gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + + \return True if any touching or blocking hits were found or any hit was found in case PxQueryFlag::eANY_HIT was specified. + + @see PxRaycastCallback PxRaycastBuffer PxQueryFilterData PxQueryFilterCallback PxQueryCache PxRaycastHit PxQueryFlag PxQueryFlag::eANY_HIT + */ + virtual bool raycast( + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxRaycastCallback& hitCall, PxHitFlags hitFlags = PxHitFlags(PxHitFlag::eDEFAULT), + const PxQueryFilterData& filterData = PxQueryFilterData(), PxQueryFilterCallback* filterCall = NULL, + const PxQueryCache* cache = NULL) const = 0; + + /** + \brief Performs a sweep test against objects in the scene, returns results in a PxSweepBuffer object + or via a custom user callback implementation inheriting from PxSweepCallback. + + \note Touching hits are not ordered. + \note If a shape from the scene is already overlapping with the query shape in its starting position, + the hit is returned unless eASSUME_NO_INITIAL_OVERLAP was specified. + + \param[in] geometry Geometry of object to sweep (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the sweep object. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be in [0, inf) range and >0 if eASSUME_NO_INITIAL_OVERLAP was specified. Will be clamped to PX_MAX_SWEEP_DISTANCE. + \param[out] hitCall Sweep hit buffer or callback object used to report sweep hits. + \param[in] hitFlags Specifies which properties per hit should be computed and returned via the hit callback. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxQueryFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Sweep is performed against cached shape first. If no hit is found the sweep gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + Note: ePRECISE_SWEEP doesn't support inflation. Therefore the sweep will be performed with zero inflation. + + \return True if any touching or blocking hits were found or any hit was found in case PxQueryFlag::eANY_HIT was specified. + + + @see PxSweepCallback PxSweepBuffer PxQueryFilterData PxQueryFilterCallback PxSweepHit PxQueryCache + */ + virtual bool sweep(const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSweepCallback& hitCall, PxHitFlags hitFlags = PxHitFlags(PxHitFlag::eDEFAULT), + const PxQueryFilterData& filterData = PxQueryFilterData(), PxQueryFilterCallback* filterCall = NULL, + const PxQueryCache* cache = NULL, const PxReal inflation = 0.f) const = 0; + + + /** + \brief Performs an overlap test of a given geometry against objects in the scene, returns results in a PxOverlapBuffer object + or via a custom user callback implementation inheriting from PxOverlapCallback. + + \note Filtering: returning eBLOCK from user filter for overlap queries will cause a warning (see #PxQueryHitType). + + \param[in] geometry Geometry of object to check for overlap (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the object. + \param[out] hitCall Overlap hit buffer or callback object used to report overlap hits. + \param[in] filterData Filtering data and simple logic. See #PxQueryFilterData #PxQueryFilterCallback + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxQueryFlag flags are set. If NULL, all hits are assumed to overlap. + + \return True if any touching or blocking hits were found or any hit was found in case PxQueryFlag::eANY_HIT was specified. + + \note eBLOCK should not be returned from user filters for overlap(). Doing so will result in undefined behavior, and a warning will be issued. + \note If the PxQueryFlag::eNO_BLOCK flag is set, the eBLOCK will instead be automatically converted to an eTOUCH and the warning suppressed. + + @see PxOverlapCallback PxOverlapBuffer PxHitFlags PxQueryFilterData PxQueryFilterCallback + */ + virtual bool overlap(const PxGeometry& geometry, const PxTransform& pose, PxOverlapCallback& hitCall, + const PxQueryFilterData& filterData = PxQueryFilterData(), PxQueryFilterCallback* filterCall = NULL + ) const = 0; + + + /** + \brief Retrieves the scene's internal scene query timestamp, increased each time a change to the + static scene query structure is performed. + + \return scene query static timestamp + */ + virtual PxU32 getSceneQueryStaticTimestamp() const = 0; + //@} + + /************************************************************************************************/ + /** @name Broad-phase + */ + //@{ + + /** + \brief Returns broad-phase type. + + \return Broad-phase type + */ + virtual PxBroadPhaseType::Enum getBroadPhaseType() const = 0; + + /** + \brief Gets broad-phase caps. + + \param[out] caps Broad-phase caps + \return True if success + */ + virtual bool getBroadPhaseCaps(PxBroadPhaseCaps& caps) const = 0; + + /** + \brief Returns number of regions currently registered in the broad-phase. + + \return Number of regions + */ + virtual PxU32 getNbBroadPhaseRegions() const = 0; + + /** + \brief Gets broad-phase regions. + + \param[out] userBuffer Returned broad-phase regions + \param[in] bufferSize Size of userBuffer + \param[in] startIndex Index of first desired region, in [0 ; getNbRegions()[ + \return Number of written out regions + */ + virtual PxU32 getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Adds a new broad-phase region. + + Note that by default, objects already existing in the SDK that might touch this region will not be automatically + added to the region. In other words the newly created region will be empty, and will only be populated with new + objects when they are added to the simulation, or with already existing objects when they are updated. + + It is nonetheless possible to override this default behavior and let the SDK populate the new region automatically + with already existing objects overlapping the incoming region. This has a cost though, and it should only be used + when the game can not guarantee that all objects within the new region will be added to the simulation after the + region itself. + + \param[in] region User-provided region data + \param[in] populateRegion Automatically populate new region with already existing objects overlapping it + \return Handle for newly created region, or 0xffffffff in case of failure. + */ + virtual PxU32 addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion=false) = 0; + + /** + \brief Removes a new broad-phase region. + + If the region still contains objects, and if those objects do not overlap any region any more, they are not + automatically removed from the simulation. Instead, the PxBroadPhaseCallback::onObjectOutOfBounds notification + is used for each object. Users are responsible for removing the objects from the simulation if this is the + desired behavior. + + If the handle is invalid, or if a valid handle is removed twice, an error message is sent to the error stream. + + \param[in] handle Region's handle, as returned by PxScene::addBroadPhaseRegion. + \return True if success + */ + virtual bool removeBroadPhaseRegion(PxU32 handle) = 0; + + //@} + + /************************************************************************************************/ + + /** @name Threads and Memory + */ + //@{ + + /** + \brief Get the task manager associated with this scene + + \return the task manager associated with the scene + */ + virtual PxTaskManager* getTaskManager() const = 0; + + + /** + \brief Lock the scene for reading from the calling thread. + + When the PxSceneFlag::eREQUIRE_RW_LOCK flag is enabled lockRead() must be + called before any read calls are made on the scene. + + Multiple threads may read at the same time, no threads may read while a thread is writing. + If a call to lockRead() is made while another thread is holding a write lock + then the calling thread will be blocked until the writing thread calls unlockWrite(). + + \note Lock upgrading is *not* supported, that means it is an error to + call lockRead() followed by lockWrite(). + + \note Recursive locking is supported but each lockRead() call must be paired with an unlockRead(). + + \param file String representing the calling file, for debug purposes + \param line The source file line number, for debug purposes + */ + virtual void lockRead(const char* file=NULL, PxU32 line=0) = 0; + + /** + \brief Unlock the scene from reading. + + \note Each unlockRead() must be paired with a lockRead() from the same thread. + */ + virtual void unlockRead() = 0; + + /** + \brief Lock the scene for writing from this thread. + + When the PxSceneFlag::eREQUIRE_RW_LOCK flag is enabled lockWrite() must be + called before any write calls are made on the scene. + + Only one thread may write at a time and no threads may read while a thread is writing. + If a call to lockWrite() is made and there are other threads reading then the + calling thread will be blocked until the readers complete. + + Writers have priority. If a thread is blocked waiting to write then subsequent calls to + lockRead() from other threads will be blocked until the writer completes. + + \note If multiple threads are waiting to write then the thread that is first + granted access depends on OS scheduling. + + \note Recursive locking is supported but each lockWrite() call must be paired + with an unlockWrite(). + + \note If a thread has already locked the scene for writing then it may call + lockRead(). + + \param file String representing the calling file, for debug purposes + \param line The source file line number, for debug purposes + */ + virtual void lockWrite(const char* file=NULL, PxU32 line=0) = 0; + + /** + \brief Unlock the scene from writing. + + \note Each unlockWrite() must be paired with a lockWrite() from the same thread. + */ + virtual void unlockWrite() = 0; + + + /** + \brief set the cache blocks that can be used during simulate(). + + Each frame the simulation requires memory to store contact, friction, and contact cache data. This memory is used in blocks of 16K. + Each frame the blocks used by the previous frame are freed, and may be retrieved by the application using PxScene::flushSimulation() + + This call will force allocation of cache blocks if the numBlocks parameter is greater than the currently allocated number + of blocks, and less than the max16KContactDataBlocks parameter specified at scene creation time. + + \param[in] numBlocks The number of blocks to allocate. + + @see PxSceneDesc.nbContactDataBlocks PxSceneDesc.maxNbContactDataBlocks flushSimulation() getNbContactDataBlocksUsed getMaxNbContactDataBlocksUsed + */ + virtual void setNbContactDataBlocks(PxU32 numBlocks) = 0; + + + /** + \brief get the number of cache blocks currently used by the scene + + This function may not be called while the scene is simulating + + \return the number of cache blocks currently used by the scene + + @see PxSceneDesc.nbContactDataBlocks PxSceneDesc.maxNbContactDataBlocks flushSimulation() setNbContactDataBlocks() getMaxNbContactDataBlocksUsed() + */ + virtual PxU32 getNbContactDataBlocksUsed() const = 0; + + /** + \brief get the maximum number of cache blocks used by the scene + + This function may not be called while the scene is simulating + + \return the maximum number of cache blocks everused by the scene + + @see PxSceneDesc.nbContactDataBlocks PxSceneDesc.maxNbContactDataBlocks flushSimulation() setNbContactDataBlocks() getNbContactDataBlocksUsed() + */ + virtual PxU32 getMaxNbContactDataBlocksUsed() const = 0; + + + /** + \brief Return the value of PxSceneDesc::contactReportStreamBufferSize that was set when creating the scene with PxPhysics::createScene + + @see PxSceneDesc::contactReportStreamBufferSize, PxPhysics::createScene + */ + virtual PxU32 getContactReportStreamBufferSize() const = 0; + + + /** + \brief Sets the number of actors required to spawn a separate rigid body solver thread. + + \param[in] solverBatchSize Number of actors required to spawn a separate rigid body solver thread. + + @see PxSceneDesc.solverBatchSize getSolverBatchSize() + */ + virtual void setSolverBatchSize(PxU32 solverBatchSize) = 0; + + /** + \brief Retrieves the number of actors required to spawn a separate rigid body solver thread. + + \return Current number of actors required to spawn a separate rigid body solver thread. + + @see PxSceneDesc.solverBatchSize setSolverBatchSize() + */ + virtual PxU32 getSolverBatchSize() const = 0; + + + //@} + + /** + \brief Returns the wake counter reset value. + + \return Wake counter reset value + + @see PxSceneDesc.wakeCounterResetValue + */ + virtual PxReal getWakeCounterResetValue() const = 0; + + /** + \brief Shift the scene origin by the specified vector. + + The poses of all objects in the scene and the corresponding data structures will get adjusted to reflect the new origin location + (the shift vector will get subtracted from all object positions). + + \note It is the user's responsibility to keep track of the summed total origin shift and adjust all input/output to/from PhysX accordingly. + + \note Do not use this method while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \note Make sure to propagate the origin shift to other dependent modules (for example, the character controller module etc.). + + \note This is an expensive operation and we recommend to use it only in the case where distance related precision issues may arise in areas far from the origin. + + \param[in] shift Translation vector to shift the origin by. + */ + virtual void shiftOrigin(const PxVec3& shift) = 0; + + /** + \brief Returns the Pvd client associated with the scene. + \return the client, NULL if no PVD supported. + */ + virtual PxPvdSceneClient* getScenePvdClient() = 0; + + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxSceneDesc.h b/src/PhysX/physx/include/PxSceneDesc.h new file mode 100644 index 000000000..67c107690 --- /dev/null +++ b/src/PhysX/physx/include/PxSceneDesc.h @@ -0,0 +1,1059 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENEDESC +#define PX_PHYSICS_NX_SCENEDESC +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxFlags.h" +#include "foundation/PxBounds3.h" +#include "PxFiltering.h" +#include "PxBroadPhase.h" +#include "common/PxTolerancesScale.h" +#include "task/PxTask.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Pruning structure used to accelerate scene queries. + +eNONE uses a simple data structure that consumes less memory than the alternatives, +but generally has slower query performance. + +eDYNAMIC_AABB_TREE usually provides the fastest queries. However there is a +constant per-frame management cost associated with this structure. How much work should +be done per frame can be tuned via the #PxSceneDesc::dynamicTreeRebuildRateHint +parameter. + +eSTATIC_AABB_TREE is typically used for static objects. It is the same as the +dynamic AABB tree, without the per-frame overhead. This can be a good choice for static +objects, if no static objects are added, moved or removed after the scene has been +created. If there is no such guarantee (e.g. when streaming parts of the world in and out), +then the dynamic version is a better choice even for static objects. + +*/ +struct PxPruningStructureType +{ + enum Enum + { + eNONE, //!< Using a simple data structure + eDYNAMIC_AABB_TREE, //!< Using a dynamic AABB tree + eSTATIC_AABB_TREE, //!< Using a static AABB tree + + eLAST + }; +}; + +/** +\brief Scene query update mode + +When PxScene::fetchResults is called it does scene query related work, with this enum it is possible to +set what work is done during the fetchResults. + +FetchResults will sync changed bounds during simulation and update the scene query bounds in pruners, this work is mandatory. + +eCOMMIT_ENABLED_BUILD_ENABLED does allow to execute the new AABB tree build step during fetchResults, additionally the pruner commit is +called where any changes are applied. During commit PhysX refits the dynamic scene query tree and if a new tree was built and +the build finished the tree is swapped with current AABB tree. + +eCOMMIT_DISABLED_BUILD_ENABLED does allow to execute the new AABB tree build step during fetchResults. Pruner commit is not called, +this means that refit will then occur during the first scene query following fetchResults, or may be forced by the method PxScene::flushSceneQueryUpdates(). + +eCOMMIT_DISABLED_BUILD_DISABLED no further scene query work is executed. The scene queries update needs to be called manually, see PxScene::sceneQueriesUpdate. +It is recommended to call PxScene::sceneQueriesUpdate right after fetchResults as the pruning structures are not updated. + +*/ +struct PxSceneQueryUpdateMode +{ + enum Enum + { + eBUILD_ENABLED_COMMIT_ENABLED, //!< Both scene query build and commit are executed. + eBUILD_ENABLED_COMMIT_DISABLED, //!< Scene query build only is executed. + eBUILD_DISABLED_COMMIT_DISABLED //!< No work is done, no update of scene queries + }; +}; + + +/** +\brief Enum for selecting the friction algorithm used for simulation. + +#PxFrictionType::ePATCH selects the patch friction model which typically leads to the most stable results at low solver iteration counts and is also quite inexpensive, as it uses only +up to four scalar solver constraints per pair of touching objects. The patch friction model is the same basic strong friction algorithm as PhysX 3.2 and before. + +#PxFrictionType::eONE_DIRECTIONAL is a simplification of the Coulomb friction model, in which the friction for a given point of contact is applied in the alternating tangent directions of +the contact's normal. This simplification allows us to reduce the number of iterations required for convergence but is not as accurate as the two directional model. + +#PxFrictionType::eTWO_DIRECTIONAL is identical to the one directional model, but it applies friction in both tangent directions simultaneously. This hurts convergence a bit so it +requires more solver iterations, but is more accurate. Like the one directional model, it is applied at every contact point, which makes it potentially more expensive +than patch friction for scenarios with many contact points. + +#PxFrictionType::eFRICTION_COUNT is the total numer of friction models supported by the SDK. +*/ +struct PxFrictionType +{ + enum Enum + { + ePATCH, //!< Select default patch-friction model. + eONE_DIRECTIONAL, //!< Select one directional per-contact friction model. + eTWO_DIRECTIONAL, //!< Select two directional per-contact friction model. + eFRICTION_COUNT //!< The total number of friction models supported by the SDK. + }; +}; + + +/** +\brief Enum for selecting the type of solver used for the simulation. + +#PxSolverType::ePGS selects the default iterative sequential impulse solver. This is the same kind of solver used in PhysX 3.4 and earlier releases. + +#PxSolverType::eTGS selects a non linear iterative solver. This kind of solver can lead to improved convergence and handle large mass ratios, long chains and jointed systems better. It is slightly more expensive than the default solver and can introduce more energy to correct joint and contact errors. +*/ +struct PxSolverType +{ + enum Enum + { + ePGS, //!< Default Projected Gauss-Seidel iterative solver + eTGS //!< Temporal Gauss-Seidel solver + }; +}; + + +/** +\brief flags for configuring properties of the scene + +@see PxScene +*/ + +struct PxSceneFlag +{ + enum Enum + { + /** + \brief Enable Active Actors Notification. + + This flag enables the Active Actor Notification feature for a scene. This + feature defaults to disabled. When disabled, the function + PxScene::getActiveActors() will always return a NULL list. + + \note There may be a performance penalty for enabling the Active Actor Notification, hence this flag should + only be enabled if the application intends to use the feature. + + Default: False + */ + eENABLE_ACTIVE_ACTORS = (1<<0), + + /** + \brief Enables a second broad phase check after integration that makes it possible to prevent objects from tunneling through eachother. + + PxPairFlag::eDETECT_CCD_CONTACT requires this flag to be specified. + + \note For this feature to be effective for bodies that can move at a significant velocity, the user should raise the flag PxRigidBodyFlag::eENABLE_CCD for them. + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: False + + @see PxRigidBodyFlag::eENABLE_CCD, PxPairFlag::eDETECT_CCD_CONTACT, eDISABLE_CCD_RESWEEP + */ + eENABLE_CCD = (1<<1), + + /** + \brief Enables a simplified swept integration strategy, which sacrifices some accuracy for improved performance. + + This simplified swept integration approach makes certain assumptions about the motion of objects that are not made when using a full swept integration. + These assumptions usually hold but there are cases where they could result in incorrect behavior between a set of fast-moving rigid bodies. A key issue is that + fast-moving dynamic objects may tunnel through each-other after a rebound. This will not happen if this mode is disabled. However, this approach will be potentially + faster than a full swept integration because it will perform significantly fewer sweeps in non-trivial scenes involving many fast-moving objects. This approach + should successfully resist objects passing through the static environment. + + PxPairFlag::eDETECT_CCD_CONTACT requires this flag to be specified. + + \note This scene flag requires eENABLE_CCD to be enabled as well. If it is not, this scene flag will do nothing. + \note For this feature to be effective for bodies that can move at a significant velocity, the user should raise the flag PxRigidBodyFlag::eENABLE_CCD for them. + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: False + + @see PxRigidBodyFlag::eENABLE_CCD, PxPairFlag::eDETECT_CCD_CONTACT, eENABLE_CCD + */ + eDISABLE_CCD_RESWEEP = (1<<2), + + /** + \brief Enable adaptive forces to accelerate convergence of the solver. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: false + */ + eADAPTIVE_FORCE = (1<<3), + + /** + \brief Enable contact pair filtering between kinematic and static rigid bodies. + + By default contacts between kinematic and static rigid bodies are suppressed (see #PxFilterFlag::eSUPPRESS) and don't get reported to the filter mechanism. + Raise this flag if these pairs should go through the filtering pipeline nonetheless. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: false + */ + eENABLE_KINEMATIC_STATIC_PAIRS PX_DEPRECATED = (1<<4), + + /** + \brief Enable contact pair filtering between kinematic rigid bodies. + + By default contacts between kinematic bodies are suppressed (see #PxFilterFlag::eSUPPRESS) and don't get reported to the filter mechanism. + Raise this flag if these pairs should go through the filtering pipeline nonetheless. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: false + */ + eENABLE_KINEMATIC_PAIRS PX_DEPRECATED = (1<<5), + + /** + \brief Enable GJK-based distance collision detection system. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: true + */ + eENABLE_PCM = (1 << 6), + + /** + \brief Disable contact report buffer resize. Once the contact buffer is full, the rest of the contact reports will + not be buffered and sent. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: false + */ + eDISABLE_CONTACT_REPORT_BUFFER_RESIZE = (1 << 7), + + /** + \brief Disable contact cache. + + Contact caches are used internally to provide faster contact generation. You can disable all contact caches + if memory usage for this feature becomes too high. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: false + */ + eDISABLE_CONTACT_CACHE = (1 << 8), + + /** + \brief Require scene-level locking + + When set to true this requires that threads accessing the PxScene use the + multi-threaded lock methods. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + @see PxScene::lockRead + @see PxScene::unlockRead + @see PxScene::lockWrite + @see PxScene::unlockWrite + + Default: false + */ + eREQUIRE_RW_LOCK = (1 << 9), + + /** + \brief Enables additional stabilization pass in solver + + When set to true, this enables additional stabilization processing to improve that stability of complex interactions between large numbers of bodies. + + Note that this flag is not mutable and must be set in PxSceneDesc at scene creation. Also, this is an experimental feature which does result in some loss of momentum. + */ + eENABLE_STABILIZATION = (1 << 10), + + /** + \brief Enables average points in contact manifolds + + When set to true, this enables additional contacts to be generated per manifold to represent the average point in a manifold. This can stabilize stacking when only a small + number of solver iterations is used. + + Note that this flag is not mutable and must be set in PxSceneDesc at scene creation. + */ + eENABLE_AVERAGE_POINT = (1 << 11), + + /** + \brief Do not report kinematics in list of active actors. + + Since the target pose for kinematics is set by the user, an application can track the activity state directly and use + this flag to avoid that kinematics get added to the list of active actors. + + \note This flag has only an effect in combination with eENABLE_ACTIVE_ACTORS. + + @see eENABLE_ACTIVE_ACTORS + + Default: false + */ + eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS = (1 << 12), + + /*\brief Enables the GPU dynamics pipeline + + When set to true, a CUDA ARCH 3.0 or above-enabled NVIDIA GPU is present and the GPU dispatcher has been configured, this will run the GPU dynamics pipelin instead of the CPU dynamics pipeline. + + Note that this flag is not mutable and must be set in PxSceneDesc at scene creation. + */ + eENABLE_GPU_DYNAMICS = (1 << 13), + + /** + \brief Provides improved determinism at the expense of performance. + + By default, PhysX provides limited determinism guarantees. Specifically, PhysX guarantees that the exact scene (same actors created in the same order) and simulated using the same + time-stepping scheme should provide the exact same behaviour. + + However, if additional actors are added to the simulation, this can affect the behaviour of the existing actors in the simulation, even if the set of new actors do not interact with + the existing actors. + + This flag provides an additional level of determinism that guarantees that the simulation will not change if additional actors are added to the simulation, provided those actors do not interfere + with the existing actors in the scene. Determinism is only guaranteed if the actors are inserted in a consistent order each run in a newly-created scene and simulated using a consistent time-stepping + scheme. + + Note that this flag is not mutable and must be set at scene creation. + + Note that enabling this flag can have a negative impact on performance. + + Note that this feature is not currently supported on GPU. + + Default false + */ + eENABLE_ENHANCED_DETERMINISM = (1<<14), + + /** + \brief Controls processing friction in all solver iterations + + By default, PhysX processes friction only in the final 3 position iterations, and all velocity + iterations. This flag enables friction processing in all position and velocity iterations. + + The default behaviour provides a good trade-off between performance and stability and is aimed + primarily at game development. + + When simulating more complex frictional behaviour, such as grasping of complex geometries with + a robotic manipulator, better results can be achieved by enabling friction in all solver iterations. + + \note This flag only has effect with the default solver. The TGS solver always performs friction per-iteration. + */ + eENABLE_FRICTION_EVERY_ITERATION = (1 << 15), + + eMUTABLE_FLAGS = eENABLE_ACTIVE_ACTORS|eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS + }; +}; + + +/** +\brief collection of set bits defined in PxSceneFlag. + +@see PxSceneFlag +*/ +typedef PxFlags PxSceneFlags; +PX_FLAGS_OPERATORS(PxSceneFlag::Enum,PxU32) + + +class PxSimulationEventCallback; +class PxContactModifyCallback; +class PxCCDContactModifyCallback; +class PxSimulationFilterCallback; + +/** +\brief Class used to retrieve limits(e.g. maximum number of bodies) for a scene. The limits +are used as a hint to the size of the scene, not as a hard limit (i.e. it will be possible +to create more objects than specified in the scene limits). + +0 indicates no limit. Using limits allows the SDK to preallocate various arrays, leading to +less re-allocations and faster code at runtime. +*/ +class PxSceneLimits +{ +public: + PxU32 maxNbActors; //!< Expected maximum number of actors + PxU32 maxNbBodies; //!< Expected maximum number of dynamic rigid bodies + PxU32 maxNbStaticShapes; //!< Expected maximum number of static shapes + PxU32 maxNbDynamicShapes; //!< Expected maximum number of dynamic shapes + PxU32 maxNbAggregates; //!< Expected maximum number of aggregates + PxU32 maxNbConstraints; //!< Expected maximum number of constraint shaders + PxU32 maxNbRegions; //!< Expected maximum number of broad-phase regions + PxU32 maxNbBroadPhaseOverlaps; //!< Expected maximum number of broad-phase overlaps + + /** + \brief constructor sets to default + */ + PX_INLINE PxSceneLimits(); + + /** + \brief (re)sets the structure to the default + */ + PX_INLINE void setToDefault(); + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + PX_INLINE bool isValid() const; +}; + +PX_INLINE PxSceneLimits::PxSceneLimits() : //constructor sets to default + maxNbActors (0), + maxNbBodies (0), + maxNbStaticShapes (0), + maxNbDynamicShapes (0), + maxNbAggregates (0), + maxNbConstraints (0), + maxNbRegions (0), + maxNbBroadPhaseOverlaps (0) +{ +} + +PX_INLINE void PxSceneLimits::setToDefault() +{ + *this = PxSceneLimits(); +} + +PX_INLINE bool PxSceneLimits::isValid() const +{ + if(maxNbRegions>256) // max number of regions is currently limited + return false; + + return true; +} + +//#if PX_SUPPORT_GPU_PHYSX +/** +\brief Sizes of pre-allocated buffers use for GPU dynamics +*/ + +struct PxgDynamicsMemoryConfig +{ + PxU32 constraintBufferCapacity; //!< Capacity of constraint buffer allocated in GPU global memory + PxU32 contactBufferCapacity; //!< Capacity of contact buffer allocated in GPU global memory + PxU32 tempBufferCapacity; //!< Capacity of temp buffer allocated in pinned host memory. + PxU32 contactStreamSize; //!< Size of contact stream buffer allocated in pinned host memory. This is double-buffered so total allocation size = 2* contactStreamCapacity * sizeof(PxContact). + PxU32 patchStreamSize; //!< Size of the contact patch stream buffer allocated in pinned host memory. This is double-buffered so total allocation size = 2 * patchStreamCapacity * sizeof(PxContactPatch). + PxU32 forceStreamCapacity; //!< Capacity of force buffer allocated in pinned host memory. + PxU32 heapCapacity; //!< Initial capacity of the GPU and pinned host memory heaps. Additional memory will be allocated if more memory is required. + PxU32 foundLostPairsCapacity; //!< Capacity of found and lost buffers allocated in GPU global memory. This is used for the found/lost pair reports in the BP. + + PxgDynamicsMemoryConfig() : + constraintBufferCapacity(32 * 1024 * 1024), + contactBufferCapacity(24 * 1024 * 1024), + tempBufferCapacity(16 * 1024 * 1024), + contactStreamSize(1024 * 512), + patchStreamSize(1024 * 80), + forceStreamCapacity(1 * 1024 * 1024), + heapCapacity(64 * 1024 * 1024), + foundLostPairsCapacity(256 * 1024) + { + } +}; + +//#endif + +/** +\brief Descriptor class for scenes. See #PxScene. + +This struct must be initialized with the same PxTolerancesScale values used to initialize PxPhysics. + +@see PxScene PxPhysics.createScene PxTolerancesScale +*/ +class PxSceneDesc +{ +public: + + /** + \brief Gravity vector. + + Range: force vector
+ Default: Zero + + @see PxScene.setGravity() + + When setting gravity, you should probably also set bounce threshold. + */ + PxVec3 gravity; + + /** + \brief Possible notification callback. + + Default: NULL + + @see PxSimulationEventCallback PxScene.setSimulationEventCallback() PxScene.getSimulationEventCallback() + */ + PxSimulationEventCallback* simulationEventCallback; + + /** + \brief Possible asynchronous callback for contact modification. + + Default: NULL + + @see PxContactModifyCallback PxScene.setContactModifyCallback() PxScene.getContactModifyCallback() + */ + PxContactModifyCallback* contactModifyCallback; + + /** + \brief Possible asynchronous callback for contact modification. + + Default: NULL + + @see PxContactModifyCallback PxScene.setContactModifyCallback() PxScene.getContactModifyCallback() + */ + PxCCDContactModifyCallback* ccdContactModifyCallback; + + /** + \brief Shared global filter data which will get passed into the filter shader. + + \note The provided data will get copied to internal buffers and this copy will be used for filtering calls. + + Default: NULL + + @see PxSimulationFilterShader PxScene::setFilterShaderData() + */ + const void* filterShaderData; + + /** + \brief Size (in bytes) of the shared global filter data #filterShaderData. + + Default: 0 + + @see PxSimulationFilterShader filterShaderData + */ + PxU32 filterShaderDataSize; + + /** + \brief The custom filter shader to use for collision filtering. + + \note This parameter is compulsory. If you don't want to define your own filter shader you can + use the default shader #PxDefaultSimulationFilterShader which can be found in the PhysX extensions + library. + + @see PxSimulationFilterShader + */ + PxSimulationFilterShader filterShader; + + /** + \brief A custom collision filter callback which can be used to implement more complex filtering operations which need + access to the simulation state, for example. + + Default: NULL + + @see PxSimulationFilterCallback + */ + PxSimulationFilterCallback* filterCallback; + + /** + \brief Filtering mode for kinematic-kinematic pairs in the broadphase. + + Default: PxPairFilteringMode::eDEFAULT + + @see PxPairFilteringMode + */ + PxPairFilteringMode::Enum kineKineFilteringMode; + + /** + \brief Filtering mode for static-kinematic pairs in the broadphase. + + Default: PxPairFilteringMode::eDEFAULT + + @see PxPairFilteringMode + */ + PxPairFilteringMode::Enum staticKineFilteringMode; + + /** + \brief Selects the broad-phase algorithm to use. + + Default: PxBroadPhaseType::eABP + + @see PxBroadPhaseType + */ + PxBroadPhaseType::Enum broadPhaseType; + + /** + \brief Broad-phase callback + + Default: NULL + + @see PxBroadPhaseCallback + */ + PxBroadPhaseCallback* broadPhaseCallback; + + /** + \brief Expected scene limits. + + @see PxSceneLimits + */ + PxSceneLimits limits; + + /** + \brief Selects the friction algorithm to use for simulation. + + \note frictionType cannot be modified after the first call to any of PxScene::simulate, PxScene::solve and PxScene::collide + + @see PxFrictionType + Default: PxFrictionType::ePATCH + + @see PxScene::setFrictionType, PxScene::getFrictionType + */ + PxFrictionType::Enum frictionType; + + /** + \brief Selects the solver algorithm to use. + + Default: PxSolverType::eDEFAULT + + @see PxSolverType + */ + PxSolverType::Enum solverType; + + /** + \brief A contact with a relative velocity below this will not bounce. A typical value for simulation. + stability is about 0.2 * gravity. + + Range: [0, PX_MAX_F32)
+ Default: 0.2 * PxTolerancesScale::speed + + @see PxMaterial + */ + PxReal bounceThresholdVelocity; + + /** + \brief A threshold of contact separation distance used to decide if a contact point will experience friction forces. + + \note If the separation distance of a contact point is greater than the threshold then the contact point will not experience friction forces. + + \note If the aggregated contact offset of a pair of shapes is large it might be desirable to neglect friction + for contact points whose separation distance is sufficiently large that the shape surfaces are clearly separated. + + \note This parameter can be used to tune the separation distance of contact points at which friction starts to have an effect. + + Range: [0, PX_MAX_F32)
+ Default: 0.04 * PxTolerancesScale::length + */ + PxReal frictionOffsetThreshold; + + /** + \brief A threshold for speculative CCD. Used to control whether bias, restitution or a combination of the two are used to resolve the contacts. + + \note This only has any effect on contacting pairs where one of the bodies has PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD raised. + + Range: [0, PX_MAX_F32)
+ Default: 0.04 * PxTolerancesScale::length + */ + + PxReal ccdMaxSeparation; + + /** + \brief A slop value used to zero contact offsets from the body's COM on an axis if the offset along that axis is smaller than this threshold. Can be used to compensate + for small numerical errors in contact generation. + + Range: [0, PX_MAX_F32)
+ Default: 0.0 + */ + + PxReal solverOffsetSlop; + + /** + \brief Flags used to select scene options. + + @see PxSceneFlag PxSceneFlags + */ + PxSceneFlags flags; + + /** + \brief The CPU task dispatcher for the scene. + + See PxCpuDispatcher, PxScene::getCpuDispatcher + */ + PxCpuDispatcher* cpuDispatcher; + + /** + \brief The GPU task dispatcher for the scene. + + Platform specific: Applies to PC GPU only. + + See PxGpuDispatcher, PxScene::getGpuDispatcher + */ + PxGpuDispatcher* gpuDispatcher; + + /** + \brief Defines the structure used to store static objects. + + \note Only PxPruningStructureType::eSTATIC_AABB_TREE and PxPruningStructureType::eDYNAMIC_AABB_TREE are allowed here. + */ + PxPruningStructureType::Enum staticStructure; + + /** + \brief Defines the structure used to store dynamic objects. + */ + PxPruningStructureType::Enum dynamicStructure; + + /** + \brief Hint for how much work should be done per simulation frame to rebuild the pruning structure. + + This parameter gives a hint on the distribution of the workload for rebuilding the dynamic AABB tree + pruning structure #PxPruningStructureType::eDYNAMIC_AABB_TREE. It specifies the desired number of simulation frames + the rebuild process should take. Higher values will decrease the workload per frame but the pruning + structure will get more and more outdated the longer the rebuild takes (which can make + scene queries less efficient). + + \note Only used for #PxPruningStructureType::eDYNAMIC_AABB_TREE pruning structure. + + \note This parameter gives only a hint. The rebuild process might still take more or less time depending on the + number of objects involved. + + Range: [4, PX_MAX_U32)
+ Default: 100 + */ + PxU32 dynamicTreeRebuildRateHint; + + /** + \brief Defines the scene query update mode. + Default: PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED + */ + PxSceneQueryUpdateMode::Enum sceneQueryUpdateMode; + + /** + \brief Will be copied to PxScene::userData. + + Default: NULL + */ + void* userData; + + /** + \brief Defines the number of actors required to spawn a separate rigid body solver island task chain. + + This parameter defines the minimum number of actors required to spawn a separate rigid body solver task chain. Setting a low value + will potentially cause more task chains to be generated. This may result in the overhead of spawning tasks can become a limiting performance factor. + Setting a high value will potentially cause fewer islands to be generated. This may reduce thread scaling (fewer task chains spawned) and may + detrimentally affect performance if some bodies in the scene have large solver iteration counts because all constraints in a given island are solved by the + maximum number of solver iterations requested by any body in the island. + + Default: 128 + + @see PxScene.setSolverBatchSize() PxScene.getSolverBatchSize() + */ + PxU32 solverBatchSize; + + /** + \brief Setting to define the number of 16K blocks that will be initially reserved to store contact, friction, and contact cache data. + This is the number of 16K memory blocks that will be automatically allocated from the user allocator when the scene is instantiated. Further 16k + memory blocks may be allocated during the simulation up to maxNbContactDataBlocks. + + \note This value cannot be larger than maxNbContactDataBlocks because that defines the maximum number of 16k blocks that can be allocated by the SDK. + + Default: 0 + + Range: [0, PX_MAX_U32]
+ + @see PxPhysics::createScene PxScene::setNbContactDataBlocks + */ + PxU32 nbContactDataBlocks; + + /** + \brief Setting to define the maximum number of 16K blocks that can be allocated to store contact, friction, and contact cache data. + As the complexity of a scene increases, the SDK may require to allocate new 16k blocks in addition to the blocks it has already + allocated. This variable controls the maximum number of blocks that the SDK can allocate. + + In the case that the scene is sufficiently complex that all the permitted 16K blocks are used, contacts will be dropped and + a warning passed to the error stream. + + If a warning is reported to the error stream to indicate the number of 16K blocks is insufficient for the scene complexity + then the choices are either (i) re-tune the number of 16K data blocks until a number is found that is sufficient for the scene complexity, + (ii) to simplify the scene or (iii) to opt to not increase the memory requirements of physx and accept some dropped contacts. + + Default: 65536 + + Range: [0, PX_MAX_U32]
+ + @see nbContactDataBlocks PxScene::setNbContactDataBlocks + */ + PxU32 maxNbContactDataBlocks; + + /** + \brief The maximum bias coefficient used in the constraint solver + + When geometric errors are found in the constraint solver, either as a result of shapes penetrating + or joints becoming separated or violating limits, a bias is introduced in the solver position iterations + to correct these errors. This bias is proportional to 1/dt, meaning that the bias becomes increasingly + strong as the time-step passed to PxScene::simulate(...) becomes smaller. This coefficient allows the + application to restrict how large the bias coefficient is, to reduce how violent error corrections are. + This can improve simulation quality in cases where either variable time-steps or extremely small time-steps + are used. + + Default: PX_MAX_F32 + + Range [0, PX_MAX_F32]
+ + */ + PxReal maxBiasCoefficient; + + /** + \brief Size of the contact report stream (in bytes). + + The contact report stream buffer is used during the simulation to store all the contact reports. + If the size is not sufficient, the buffer will grow by a factor of two. + It is possible to disable the buffer growth by setting the flag PxSceneFlag::eDISABLE_CONTACT_REPORT_BUFFER_RESIZE. + In that case the buffer will not grow but contact reports not stored in the buffer will not get sent in the contact report callbacks. + + Default: 8192 + + Range: (0, PX_MAX_U32]
+ + */ + PxU32 contactReportStreamBufferSize; + + /** + \brief Maximum number of CCD passes + + The CCD performs multiple passes, where each pass every object advances to its time of first impact. This value defines how many passes the CCD system should perform. + + \note The CCD system is a multi-pass best-effort conservative advancement approach. After the defined number of passes has been completed, any remaining time is dropped. + \note This defines the maximum number of passes the CCD can perform. It may perform fewer if additional passes are not necessary. + + Default: 1 + Range: [1, PX_MAX_U32]
+ */ + PxU32 ccdMaxPasses; + + /** + \brief The wake counter reset value + + Calling wakeUp() on objects which support sleeping will set their wake counter value to the specified reset value. + + Range: (0, PX_MAX_F32)
+ Default: 0.4 (which corresponds to 20 frames for a time step of 0.02) + + @see PxRigidDynamic::wakeUp() PxArticulationBase::wakeUp() + */ + PxReal wakeCounterResetValue; + + /** + \brief The bounds used to sanity check user-set positions of actors and articulation links + + These bounds are used to check the position values of rigid actors inserted into the scene, and positions set for rigid actors + already within the scene. + + Range: any valid PxBounds3
+ Default: (-PX_MAX_BOUNDS_EXTENTS, PX_MAX_BOUNDS_EXTENTS) on each axis + */ + PxBounds3 sanityBounds; + + /** + \brief The pre-allocations performed in the GPU dynamics pipeline. + */ + PxgDynamicsMemoryConfig gpuDynamicsConfig; + + /** + \brief Limitation for the partitions in the GPU dynamics pipeline. + This variable must be power of 2. + A value greater than 32 is currently not supported. + Range: (1, 32)
+ */ + PxU32 gpuMaxNumPartitions; + + /** + \brief Defines which compute version the GPU dynamics should target. DO NOT MODIFY + */ + PxU32 gpuComputeVersion; + +private: + /** + \cond + */ + // For internal use only + PxTolerancesScale tolerancesScale; + /** + \endcond + */ + + +public: + /** + \brief constructor sets to default. + + \param[in] scale scale values for the tolerances in the scene, these must be the same values passed into + PxCreatePhysics(). The affected tolerances are bounceThresholdVelocity and frictionOffsetThreshold. + + @see PxCreatePhysics() PxTolerancesScale bounceThresholdVelocity frictionOffsetThreshold + */ + PX_INLINE PxSceneDesc(const PxTolerancesScale& scale); + + /** + \brief (re)sets the structure to the default. + + \param[in] scale scale values for the tolerances in the scene, these must be the same values passed into + PxCreatePhysics(). The affected tolerances are bounceThresholdVelocity and frictionOffsetThreshold. + + @see PxCreatePhysics() PxTolerancesScale bounceThresholdVelocity frictionOffsetThreshold + */ + PX_INLINE void setToDefault(const PxTolerancesScale& scale); + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + PX_INLINE bool isValid() const; + + /** + \cond + */ + // For internal use only + PX_INLINE const PxTolerancesScale& getTolerancesScale() const { return tolerancesScale; } + /** + \endcond + */ +}; + +PX_INLINE PxSceneDesc::PxSceneDesc(const PxTolerancesScale& scale): + gravity (PxVec3(0.0f)), + simulationEventCallback (NULL), + contactModifyCallback (NULL), + ccdContactModifyCallback (NULL), + + filterShaderData (NULL), + filterShaderDataSize (0), + filterShader (NULL), + filterCallback (NULL), + + kineKineFilteringMode (PxPairFilteringMode::eDEFAULT), + staticKineFilteringMode (PxPairFilteringMode::eDEFAULT), + + broadPhaseType (PxBroadPhaseType::eABP), + broadPhaseCallback (NULL), + + frictionType (PxFrictionType::ePATCH), + solverType (PxSolverType::ePGS), + bounceThresholdVelocity (0.2f * scale.speed), + frictionOffsetThreshold (0.04f * scale.length), + ccdMaxSeparation (0.04f * scale.length), + solverOffsetSlop (0.0f), + + flags (PxSceneFlag::eENABLE_PCM), + + cpuDispatcher (NULL), + gpuDispatcher (NULL), + + staticStructure (PxPruningStructureType::eDYNAMIC_AABB_TREE), + dynamicStructure (PxPruningStructureType::eDYNAMIC_AABB_TREE), + dynamicTreeRebuildRateHint (100), + sceneQueryUpdateMode (PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED), + + userData (NULL), + + solverBatchSize (128), + + nbContactDataBlocks (0), + maxNbContactDataBlocks (1<<16), + maxBiasCoefficient (PX_MAX_F32), + contactReportStreamBufferSize (8192), + ccdMaxPasses (1), + wakeCounterResetValue (20.0f*0.02f), + sanityBounds (PxBounds3(PxVec3(-PX_MAX_BOUNDS_EXTENTS), PxVec3(PX_MAX_BOUNDS_EXTENTS))), + gpuMaxNumPartitions (8), + gpuComputeVersion (0), + tolerancesScale (scale) +{ +} + +PX_INLINE void PxSceneDesc::setToDefault(const PxTolerancesScale& scale) +{ + *this = PxSceneDesc(scale); +} + +PX_INLINE bool PxSceneDesc::isValid() const +{ + if(!filterShader) + return false; + + if( ((filterShaderDataSize == 0) && (filterShaderData != NULL)) || + ((filterShaderDataSize > 0) && (filterShaderData == NULL)) ) + return false; + + if(!limits.isValid()) + return false; + + if(staticStructure!=PxPruningStructureType::eSTATIC_AABB_TREE && staticStructure!=PxPruningStructureType::eDYNAMIC_AABB_TREE) + return false; + + if(dynamicTreeRebuildRateHint < 4) + return false; + + if(bounceThresholdVelocity < 0.0f) + return false; + if(frictionOffsetThreshold < 0.0f) + return false; + if(ccdMaxSeparation < 0.0f) + return false; + if (solverOffsetSlop < 0.f) + return false; + + if(!cpuDispatcher) + return false; + + if(!contactReportStreamBufferSize) + return false; + + if(maxNbContactDataBlocks < nbContactDataBlocks) + return false; + + if(wakeCounterResetValue <= 0.0f) + return false; + + //Adaptive force and stabilization are incompatible. You can only have one or the other + if((flags & (PxSceneFlag::eADAPTIVE_FORCE | PxSceneFlag::eENABLE_STABILIZATION)) == (PxSceneFlag::eADAPTIVE_FORCE | PxSceneFlag::eENABLE_STABILIZATION)) + return false; + + if(!sanityBounds.isValid()) + return false; + +#if PX_SUPPORT_GPU_PHYSX + //gpuMaxNumPartitions must be power of 2 + if((gpuMaxNumPartitions&(gpuMaxNumPartitions - 1)) != 0) + return false; + if (gpuMaxNumPartitions > 32) + return false; +#endif + + return true; +} + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxSceneLock.h b/src/PhysX/physx/include/PxSceneLock.h new file mode 100644 index 000000000..97ffad0c8 --- /dev/null +++ b/src/PhysX/physx/include/PxSceneLock.h @@ -0,0 +1,129 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENELOCK +#define PX_PHYSICS_NX_SCENELOCK +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxScene.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief RAII wrapper for the PxScene read lock. + +Use this class as follows to lock the scene for reading by the current thread +for the duration of the enclosing scope: + + PxSceneReadLock lock(sceneRef); + +\see PxScene::lockRead(), PxScene::unlockRead(), PxSceneFlag::eREQUIRE_RW_LOCK +*/ +class PxSceneReadLock +{ + PxSceneReadLock(const PxSceneReadLock&); + PxSceneReadLock& operator=(const PxSceneReadLock&); + +public: + + /** + \brief Constructor + \param scene The scene to lock for reading + \param file Optional string for debugging purposes + \param line Optional line number for debugging purposes + */ + PxSceneReadLock(PxScene& scene, const char* file=NULL, PxU32 line=0) + : mScene(scene) + { + mScene.lockRead(file, line); + } + + ~PxSceneReadLock() + { + mScene.unlockRead(); + } + +private: + + PxScene& mScene; +}; + +/** +\brief RAII wrapper for the PxScene write lock. + +Use this class as follows to lock the scene for writing by the current thread +for the duration of the enclosing scope: + + PxSceneWriteLock lock(sceneRef); + +\see PxScene::lockWrite(), PxScene::unlockWrite(), PxSceneFlag::eREQUIRE_RW_LOCK +*/ +class PxSceneWriteLock +{ + PxSceneWriteLock(const PxSceneWriteLock&); + PxSceneWriteLock& operator=(const PxSceneWriteLock&); + +public: + + /** + \brief Constructor + \param scene The scene to lock for writing + \param file Optional string for debugging purposes + \param line Optional line number for debugging purposes + */ + PxSceneWriteLock(PxScene& scene, const char* file=NULL, PxU32 line=0) + : mScene(scene) + { + mScene.lockWrite(file, line); + } + + ~PxSceneWriteLock() + { + mScene.unlockWrite(); + } + +private: + + PxScene& mScene; +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxShape.h b/src/PhysX/physx/include/PxShape.h new file mode 100644 index 000000000..392006428 --- /dev/null +++ b/src/PhysX/physx/include/PxShape.h @@ -0,0 +1,639 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NX_SHAPE +#define PX_PHYSICS_NX_SHAPE +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" +#include "geometry/PxGeometry.h" +#include "geometry/PxGeometryHelpers.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxBoxGeometry; +class PxSphereGeometry; +class PxCapsuleGeometry; +class PxPlaneGeometry; +class PxConvexMeshGeometry; +class PxTriangleMeshGeometry; +class PxHeightFieldGeometry; +class PxRigidActor; +struct PxFilterData; +struct PxRaycastHit; +struct PxSweepHit; + +/** +\brief Flags which affect the behavior of PxShapes. + +@see PxShape PxShape.setFlag() +*/ +struct PxShapeFlag +{ + enum Enum + { + /** + \brief The shape will partake in collision in the physical simulation. + + \note It is illegal to raise the eSIMULATION_SHAPE and eTRIGGER_SHAPE flags. + In the event that one of these flags is already raised the sdk will reject any + attempt to raise the other. To raise the eSIMULATION_SHAPE first ensure that + eTRIGGER_SHAPE is already lowered. + + \note This flag has no effect if simulation is disabled for the corresponding actor (see #PxActorFlag::eDISABLE_SIMULATION). + + @see PxSimulationEventCallback.onContact() PxScene.setSimulationEventCallback() PxShape.setFlag(), PxShape.setFlags() + */ + eSIMULATION_SHAPE = (1<<0), + + /** + \brief The shape will partake in scene queries (ray casts, overlap tests, sweeps, ...). + */ + eSCENE_QUERY_SHAPE = (1<<1), + + /** + \brief The shape is a trigger which can send reports whenever other shapes enter/leave its volume. + + \note Triangle meshes and heightfields can not be triggers. Shape creation will fail in these cases. + + \note Shapes marked as triggers do not collide with other objects. If an object should act both + as a trigger shape and a collision shape then create a rigid body with two shapes, one being a + trigger shape and the other a collision shape. It is illegal to raise the eTRIGGER_SHAPE and + eSIMULATION_SHAPE flags on a single PxShape instance. In the event that one of these flags is already + raised the sdk will reject any attempt to raise the other. To raise the eTRIGGER_SHAPE flag first + ensure that eSIMULATION_SHAPE flag is already lowered. + + \note Trigger shapes will no longer send notification events for interactions with other trigger shapes. + + \note Shapes marked as triggers are allowed to participate in scene queries, provided the eSCENE_QUERY_SHAPE flag is set. + + \note This flag has no effect if simulation is disabled for the corresponding actor (see #PxActorFlag::eDISABLE_SIMULATION). + + @see PxSimulationEventCallback.onTrigger() PxScene.setSimulationEventCallback() PxShape.setFlag(), PxShape.setFlags() + */ + eTRIGGER_SHAPE = (1<<2), + + /** + \brief Enable debug renderer for this shape + + @see PxScene.getRenderBuffer() PxRenderBuffer PxVisualizationParameter + */ + eVISUALIZATION = (1<<3) + }; +}; + +/** +\brief collection of set bits defined in PxShapeFlag. + +@see PxShapeFlag +*/ +typedef PxFlags PxShapeFlags; +PX_FLAGS_OPERATORS(PxShapeFlag::Enum,PxU8) + + +/** +\brief Abstract class for collision shapes. + +Shapes are shared, reference counted objects. + +An instance can be created by calling the createShape() method of the PxRigidActor class, or +the createShape() method of the PxPhysics class. + +

Visualizations

+\li PxVisualizationParameter::eCOLLISION_AABBS +\li PxVisualizationParameter::eCOLLISION_SHAPES +\li PxVisualizationParameter::eCOLLISION_AXES + +@see PxPhysics.createShape() PxRigidActor.createShape() PxBoxGeometry PxSphereGeometry PxCapsuleGeometry PxPlaneGeometry PxConvexMeshGeometry +PxTriangleMeshGeometry PxHeightFieldGeometry +*/ +class PxShape : public PxBase +{ +public: + + /** + \brief Decrements the reference count of a shape and releases it if the new reference count is zero. + + Note that in releases prior to PhysX 3.3 this method did not have reference counting semantics and was used to destroy a shape + created with PxActor::createShape(). In PhysX 3.3 and above, this usage is deprecated, instead, use PxRigidActor::detachShape() to detach + a shape from an actor. If the shape to be detached was created with PxActor::createShape(), the actor holds the only counted reference, + and so when the shape is detached it will also be destroyed. + + @see PxRigidActor::createShape() PxPhysics::createShape() PxRigidActor::attachShape() PxRigidActor::detachShape() + */ + virtual void release() = 0; + + /** + \brief Returns the reference count of the shape. + + At creation, the reference count of the shape is 1. Every actor referencing this shape increments the + count by 1. When the reference count reaches 0, and only then, the shape gets destroyed automatically. + + \return the current reference count. + */ + virtual PxU32 getReferenceCount() const = 0; + + /** + \brief Acquires a counted reference to a shape. + + This method increases the reference count of the shape by 1. Decrement the reference count by calling release() + */ + virtual void acquireReference() = 0; + + /** + \brief Get the geometry type of the shape. + + \return Type of shape geometry. + + @see PxGeometryType + */ + virtual PxGeometryType::Enum getGeometryType() const = 0; + + /** + \brief Adjust the geometry of the shape. + + \note The type of the passed in geometry must match the geometry type of the shape. + \note It is not allowed to change the geometry type of a shape. + \note This function does not guarantee correct/continuous behavior when objects are resting on top of old or new geometry. + + \param[in] geometry New geometry of the shape. + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual void setGeometry(const PxGeometry& geometry) = 0; + + + /** + \brief Retrieve the geometry from the shape in a PxGeometryHolder wrapper class. + + \return a PxGeometryHolder object containing the geometry; + + @see PxGeometry PxGeometryType getGeometryType() setGeometry() + */ + + virtual PxGeometryHolder getGeometry() const = 0; + + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getBoxGeometry(PxBoxGeometry& geometry) const = 0; + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getSphereGeometry(PxSphereGeometry& geometry) const = 0; + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getCapsuleGeometry(PxCapsuleGeometry& geometry) const = 0; + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getPlaneGeometry(PxPlaneGeometry& geometry) const = 0; + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getConvexMeshGeometry(PxConvexMeshGeometry& geometry) const = 0; + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getTriangleMeshGeometry(PxTriangleMeshGeometry& geometry) const = 0; + + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getHeightFieldGeometry(PxHeightFieldGeometry& geometry) const = 0; + + /** + \brief Retrieves the actor which this shape is associated with. + + \return The actor this shape is associated with, if it is an exclusive shape, else NULL + + @see PxRigidStatic, PxRigidDynamic, PxArticulationLink + */ + virtual PxRigidActor* getActor() const = 0; + + +/************************************************************************************************/ + +/** @name Pose Manipulation +*/ +//@{ + + /** + \brief Sets the pose of the shape in actor space, i.e. relative to the actors to which they are attached. + + This transformation is identity by default. + + The local pose is an attribute of the shape, and so will apply to all actors to which the shape is attached. + + Sleeping: Does NOT wake the associated actor up automatically. + + Note: Does not automatically update the inertia properties of the owning actor (if applicable); use the + PhysX extensions method #PxRigidBodyExt::updateMassAndInertia() to do this. + + Default: the identity transform + + \param[in] pose The new transform from the actor frame to the shape frame. Range: rigid body transform + + @see getLocalPose() + */ + virtual void setLocalPose(const PxTransform& pose) = 0; + + /** + \brief Retrieves the pose of the shape in actor space, i.e. relative to the actor they are owned by. + + This transformation is identity by default. + + \return Pose of shape relative to the actor's frame. + + @see setLocalPose() + */ + virtual PxTransform getLocalPose() const = 0; + +//@} +/************************************************************************************************/ + +/** @name Collision Filtering +*/ +//@{ + + /** + \brief Sets the user definable collision filter data. + + Sleeping: Does wake up the actor if the filter data change causes a formerly suppressed + collision pair to be enabled. + + Default: (0,0,0,0) + + @see getSimulationFilterData() + */ + virtual void setSimulationFilterData(const PxFilterData& data) = 0; + + /** + \brief Retrieves the shape's collision filter data. + + @see setSimulationFilterData() + */ + virtual PxFilterData getSimulationFilterData() const = 0; + + /** + \brief Sets the user definable query filter data. + + Default: (0,0,0,0) + + @see getQueryFilterData() + */ + virtual void setQueryFilterData(const PxFilterData& data) = 0; + + /** + \brief Retrieves the shape's Query filter data. + + @see setQueryFilterData() + */ + virtual PxFilterData getQueryFilterData() const = 0; + +//@} +/************************************************************************************************/ + + /** + \brief Assigns material(s) to the shape. + + Sleeping: Does NOT wake the associated actor up automatically. + + \param[in] materials List of material pointers to assign to the shape. See #PxMaterial + \param[in] materialCount The number of materials provided. + + @see PxPhysics.createMaterial() getMaterials() + */ + virtual void setMaterials(PxMaterial*const* materials, PxU16 materialCount) = 0; + + /** + \brief Returns the number of materials assigned to the shape. + + You can use #getMaterials() to retrieve the material pointers. + + \return Number of materials associated with this shape. + + @see PxMaterial getMaterials() + */ + virtual PxU16 getNbMaterials() const = 0; + + /** + \brief Retrieve all the material pointers associated with the shape. + + You can retrieve the number of material pointers by calling #getNbMaterials() + + Note: Removing materials with #PxMaterial::release() will invalidate the pointer of the released material. + + \param[out] userBuffer The buffer to store the material pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first material pointer to be retrieved + \return Number of material pointers written to the buffer. + + @see PxMaterial getNbMaterials() PxMaterial::release() + */ + virtual PxU32 getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Retrieve material from given triangle index. + + The input index is the internal triangle index as used inside the SDK. This is the index + returned to users by various SDK functions such as raycasts. + + This function is only useful for triangle meshes or heightfields, which have per-triangle + materials. For other shapes the function returns the single material associated with the + shape, regardless of the index. + + \param[in] faceIndex The internal triangle index whose material you want to retrieve. + \return Material from input triangle + + \note If faceIndex value of 0xFFFFffff is passed as an input for mesh and heightfield shapes, this function will issue a warning and return NULL. + \note Scene queries set the value of PxQueryHit::faceIndex to 0xFFFFffff whenever it is undefined or does not apply. + + @see PxMaterial getNbMaterials() PxMaterial::release() + */ + virtual PxMaterial* getMaterialFromInternalFaceIndex(PxU32 faceIndex) const = 0; + + /** + \brief Sets the contact offset. + + Shapes whose distance is less than the sum of their contactOffset values will generate contacts. The contact offset must be positive and + greater than the rest offset. Having a contactOffset greater than than the restOffset allows the collision detection system to + predictively enforce the contact constraint even when the objects are slightly separated. This prevents jitter that would occur + if the constraint were enforced only when shapes were within the rest distance. + + Default: 0.02f * PxTolerancesScale::length + + Sleeping: Does NOT wake the associated actor up automatically. + + \param[in] contactOffset Range: [maximum(0,restOffset), PX_MAX_F32) + + @see getContactOffset PxTolerancesScale setRestOffset + */ + virtual void setContactOffset(PxReal contactOffset) = 0; + + /** + \brief Retrieves the contact offset. + + \return The contact offset of the shape. + + @see setContactOffset() + */ + virtual PxReal getContactOffset() const = 0; + + /** + \brief Sets the rest offset. + + Two shapes will come to rest at a distance equal to the sum of their restOffset values. If the restOffset is 0, they should converge to touching + exactly. Having a restOffset greater than zero is useful to have objects slide smoothly, so that they do not get hung up on irregularities of + each others' surfaces. + + Default: 0.0f + + Sleeping: Does NOT wake the associated actor up automatically. + + \param[in] restOffset Range: (-PX_MAX_F32, contactOffset) + + @see getRestOffset setContactOffset + */ + virtual void setRestOffset(PxReal restOffset) = 0; + + /** + \brief Retrieves the rest offset. + + \return The rest offset of the shape. + + @see setRestOffset() + */ + virtual PxReal getRestOffset() const = 0; + + + /** + \brief Sets torsional patch radius. + + This defines the radius of the contact patch used to apply torsional friction. If the radius is 0, no torsional friction + will be applied. If the radius is > 0, some torsional friction will be applied. This is proportional to the penetration depth + so, if the shapes are separated or penetration is zero, no torsional friction will be applied. It is used to approximate + rotational friction introduced by the compression of contacting surfaces. + + \param[in] radius Range: (0, PX_MAX_F32) + + */ + virtual void setTorsionalPatchRadius(PxReal radius) = 0; + + /** + \brief Gets torsional patch radius. + + This defines the radius of the contact patch used to apply torsional friction. If the radius is 0, no torsional friction + will be applied. If the radius is > 0, some torsional friction will be applied. This is proportional to the penetration depth + so, if the shapes are separated or penetration is zero, no torsional friction will be applied. It is used to approximate + rotational friction introduced by the compression of contacting surfaces. + + \return The torsional patch radius of the shape. + */ + virtual PxReal getTorsionalPatchRadius() const = 0; + + /** + \brief Sets minimum torsional patch radius. + + This defines the minimum radius of the contact patch used to apply torsional friction. If the radius is 0, the amount of torsional friction + that will be applied will be entirely dependent on the value of torsionalPatchRadius. + + If the radius is > 0, some torsional friction will be applied regardless of the value of torsionalPatchRadius or the amount of penetration. + + \param[in] radius Range: (0, PX_MAX_F32) + + */ + virtual void setMinTorsionalPatchRadius(PxReal radius) = 0; + + /** + \brief Gets minimum torsional patch radius. + + This defines the minimum radius of the contact patch used to apply torsional friction. If the radius is 0, the amount of torsional friction + that will be applied will be entirely dependent on the value of torsionalPatchRadius. + + If the radius is > 0, some torsional friction will be applied regardless of the value of torsionalPatchRadius or the amount of penetration. + + \return The minimum torsional patch radius of the shape. + */ + virtual PxReal getMinTorsionalPatchRadius() const = 0; + + +/************************************************************************************************/ + + /** + \brief Sets shape flags + + Sleeping: Does NOT wake the associated actor up automatically. + + \param[in] flag The shape flag to enable/disable. See #PxShapeFlag. + \param[in] value True to set the flag. False to clear the flag specified in flag. + + Default: PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eSCENE_QUERY_SHAPE + + @see PxShapeFlag getFlags() + */ + virtual void setFlag(PxShapeFlag::Enum flag, bool value) = 0; + + /** + \brief Sets shape flags + + @see PxShapeFlag getFlags() + */ + virtual void setFlags(PxShapeFlags inFlags) = 0; + + /** + \brief Retrieves shape flags. + + \return The values of the shape flags. + + @see PxShapeFlag setFlag() + */ + virtual PxShapeFlags getFlags() const = 0; + + /** + \brief Returns true if the shape is exclusive to an actor. + + @see PxPhysics::createShape() + */ + virtual bool isExclusive() const = 0; + + /** + \brief Sets a name string for the object that can be retrieved with #getName(). + + This is for debugging and is not used by the SDK. + The string is not copied by the SDK, only the pointer is stored. + + Default: NULL + + \param[in] name The name string to set the objects name to. + + @see getName() + */ + virtual void setName(const char* name) = 0; + + + /** + \brief retrieves the name string set with setName(). + \return The name associated with the shape. + + @see setName() + */ + virtual const char* getName() const = 0; + + + virtual const char* getConcreteTypeName() const { return "PxShape"; } + +/************************************************************************************************/ + + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. + +protected: + PX_INLINE PxShape(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + PX_INLINE PxShape(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags), userData(NULL) {} + virtual ~PxShape() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxShape", name) || PxBase::isKindOf(name); } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxSimulationEventCallback.h b/src/PhysX/physx/include/PxSimulationEventCallback.h new file mode 100644 index 000000000..52186d374 --- /dev/null +++ b/src/PhysX/physx/include/PxSimulationEventCallback.h @@ -0,0 +1,915 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_SIMULATION_EVENT_CALLBACK +#define PX_SIMULATION_EVENT_CALLBACK +/** \addtogroup physics +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMemory.h" +#include "PxPhysXConfig.h" +#include "PxFiltering.h" +#include "PxContact.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxShape; +class PxActor; +class PxRigidActor; +class PxRigidBody; +class PxConstraint; + + +/** +\brief Extra data item types for contact pairs. + +@see PxContactPairExtraDataItem.type +*/ +struct PxContactPairExtraDataType +{ + enum Enum + { + ePRE_SOLVER_VELOCITY, //!< see #PxContactPairVelocity + ePOST_SOLVER_VELOCITY, //!< see #PxContactPairVelocity + eCONTACT_EVENT_POSE, //!< see #PxContactPairPose + eCONTACT_PAIR_INDEX //!< see #PxContactPairIndex + }; +}; + + +/** +\brief Base class for items in the extra data stream of contact pairs + +@see PxContactPairHeader.extraDataStream +*/ +struct PxContactPairExtraDataItem +{ +public: + PX_FORCE_INLINE PxContactPairExtraDataItem() {} + + /** + \brief The type of the extra data stream item + */ + PxU8 type; +}; + + +/** +\brief Velocities of the contact pair rigid bodies + +This struct is shared by multiple types of extra data items. The #type field allows to distinguish between them: +\li PxContactPairExtraDataType::ePRE_SOLVER_VELOCITY: see #PxPairFlag::ePRE_SOLVER_VELOCITY +\li PxContactPairExtraDataType::ePOST_SOLVER_VELOCITY: see #PxPairFlag::ePOST_SOLVER_VELOCITY + +\note For static rigid bodies, the velocities will be set to zero. + +@see PxContactPairHeader.extraDataStream +*/ +struct PxContactPairVelocity : public PxContactPairExtraDataItem +{ +public: + PX_FORCE_INLINE PxContactPairVelocity() {} + + /** + \brief The linear velocity of the rigid bodies + */ + PxVec3 linearVelocity[2]; + + /** + \brief The angular velocity of the rigid bodies + */ + PxVec3 angularVelocity[2]; +}; + + +/** +\brief World space actor poses of the contact pair rigid bodies + +@see PxContactPairHeader.extraDataStream PxPairFlag::eCONTACT_EVENT_POSE +*/ +struct PxContactPairPose : public PxContactPairExtraDataItem +{ +public: + PX_FORCE_INLINE PxContactPairPose() {} + + /** + \brief The world space pose of the rigid bodies + */ + PxTransform globalPose[2]; +}; + + +/** +\brief Marker for the beginning of a new item set in the extra data stream. + +If CCD with multiple passes is enabled, then a fast moving object might bounce on and off the same +object multiple times. Also, different shapes of the same actor might gain and lose contact with an other +object over multiple passes. This marker allows to seperate the extra data items for each collision case, as well as +distinguish the shape pair reports of different CCD passes. + +Example: +Let us assume that an actor a0 with shapes s0_0 and s0_1 hits another actor a1 with shape s1. +First s0_0 will hit s1, then a0 will slightly rotate and s0_1 will hit s1 while s0_0 will lose contact with s1. +Furthermore, let us say that contact event pose information is requested as extra data. +The extra data stream will look like this: + +PxContactPairIndexA | PxContactPairPoseA | PxContactPairIndexB | PxContactPairPoseB + +The corresponding array of PxContactPair events (see #PxSimulationEventCallback.onContact()) will look like this: + +PxContactPair(touch_found: s0_0, s1) | PxContactPair(touch_lost: s0_0, s1) | PxContactPair(touch_found: s0_1, s1) + +The #index of PxContactPairIndexA will point to the first entry in the PxContactPair array, for PxContactPairIndexB, +#index will point to the third entry. + +@see PxContactPairHeader.extraDataStream +*/ +struct PxContactPairIndex : public PxContactPairExtraDataItem +{ +public: + PX_FORCE_INLINE PxContactPairIndex() {} + + /** + \brief The next item set in the extra data stream refers to the contact pairs starting at #index in the reported PxContactPair array. + */ + PxU16 index; +}; + + +/** +\brief A class to iterate over a contact pair extra data stream. + +@see PxContactPairHeader.extraDataStream +*/ +struct PxContactPairExtraDataIterator +{ + /** + \brief Constructor + \param[in] stream Pointer to the start of the stream. + \param[in] size Size of the stream in bytes. + */ + PX_FORCE_INLINE PxContactPairExtraDataIterator(const PxU8* stream, PxU32 size) + : currPtr(stream), endPtr(stream + size), contactPairIndex(0) + { + clearDataPtrs(); + } + + /** + \brief Advances the iterator to next set of extra data items. + + The contact pair extra data stream contains sets of items as requested by the corresponding #PxPairFlag flags + #PxPairFlag::ePRE_SOLVER_VELOCITY, #PxPairFlag::ePOST_SOLVER_VELOCITY, #PxPairFlag::eCONTACT_EVENT_POSE. A set can contain one + item of each plus the PxContactPairIndex item. This method parses the stream and points the iterator + member variables to the corresponding items of the current set, if they are available. If CCD is not enabled, + you should only get one set of items. If CCD with multiple passes is enabled, you might get more than one item + set. + + \note Even though contact pair extra data is requested per shape pair, you will not get an item set per shape pair + but one per actor pair. If, for example, an actor has two shapes and both collide with another actor, then + there will only be one item set (since it applies to both shape pairs). + + \return True if there was another set of extra data items in the stream, else false. + + @see PxContactPairVelocity PxContactPairPose PxContactPairIndex + */ + PX_INLINE bool nextItemSet() + { + clearDataPtrs(); + + bool foundEntry = false; + bool endOfItemSet = false; + while ((currPtr < endPtr) && (!endOfItemSet)) + { + const PxContactPairExtraDataItem* edItem = reinterpret_cast(currPtr); + PxU8 type = edItem->type; + + switch(type) + { + case PxContactPairExtraDataType::ePRE_SOLVER_VELOCITY: + { + PX_ASSERT(!preSolverVelocity); + preSolverVelocity = static_cast(edItem); + currPtr += sizeof(PxContactPairVelocity); + foundEntry = true; + } + break; + + case PxContactPairExtraDataType::ePOST_SOLVER_VELOCITY: + { + postSolverVelocity = static_cast(edItem); + currPtr += sizeof(PxContactPairVelocity); + foundEntry = true; + } + break; + + case PxContactPairExtraDataType::eCONTACT_EVENT_POSE: + { + eventPose = static_cast(edItem); + currPtr += sizeof(PxContactPairPose); + foundEntry = true; + } + break; + + case PxContactPairExtraDataType::eCONTACT_PAIR_INDEX: + { + if (!foundEntry) + { + contactPairIndex = static_cast(edItem)->index; + currPtr += sizeof(PxContactPairIndex); + foundEntry = true; + } + else + endOfItemSet = true; + } + break; + + default: + return foundEntry; + } + } + + return foundEntry; + } + +private: + /** + \brief Internal helper + */ + PX_FORCE_INLINE void clearDataPtrs() + { + preSolverVelocity = NULL; + postSolverVelocity = NULL; + eventPose = NULL; + } + +public: + /** + \brief Current pointer in the stream. + */ + const PxU8* currPtr; + + /** + \brief Pointer to the end of the stream. + */ + const PxU8* endPtr; + + /** + \brief Pointer to the current pre solver velocity item in the stream. NULL if there is none. + + @see PxContactPairVelocity + */ + const PxContactPairVelocity* preSolverVelocity; + + /** + \brief Pointer to the current post solver velocity item in the stream. NULL if there is none. + + @see PxContactPairVelocity + */ + const PxContactPairVelocity* postSolverVelocity; + + /** + \brief Pointer to the current contact event pose item in the stream. NULL if there is none. + + @see PxContactPairPose + */ + const PxContactPairPose* eventPose; + + /** + \brief The contact pair index of the current item set in the stream. + + @see PxContactPairIndex + */ + PxU32 contactPairIndex; +}; + + +/** +\brief Collection of flags providing information on contact report pairs. + +@see PxContactPairHeader +*/ +struct PxContactPairHeaderFlag +{ + enum Enum + { + eREMOVED_ACTOR_0 = (1<<0), //!< The actor with index 0 has been removed from the scene. + eREMOVED_ACTOR_1 = (1<<1) //!< The actor with index 1 has been removed from the scene. + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxContactPairHeaderFlag. + +@see PxContactPairHeaderFlag +*/ +typedef PxFlags PxContactPairHeaderFlags; +PX_FLAGS_OPERATORS(PxContactPairHeaderFlag::Enum, PxU16) + + +/** +\brief An Instance of this class is passed to PxSimulationEventCallback.onContact(). + +@see PxSimulationEventCallback.onContact() +*/ +struct PxContactPairHeader +{ + public: + PX_INLINE PxContactPairHeader() {} + + /** + \brief The two actors of the notification shape pairs. + + \note The actor pointers might reference deleted actors. This will be the case if PxPairFlag::eNOTIFY_TOUCH_LOST + or PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST events were requested for the pair and one of the involved actors + gets deleted or removed from the scene. Check the #flags member to see whether that is the case. + Do not dereference a pointer to a deleted actor. The pointer to a deleted actor is only provided + such that user data structures which might depend on the pointer value can be updated. + + @see PxActor + */ + PxRigidActor* actors[2]; + + /** + \brief Stream containing extra data as requested in the PxPairFlag flags of the simulation filter. + + This pointer is only valid if any kind of extra data information has been requested for the contact report pair (see #PxPairFlag::ePOST_SOLVER_VELOCITY etc.), + else it will be NULL. + + @see PxPairFlag + */ + const PxU8* extraDataStream; + + /** + \brief Size of the extra data stream [bytes] + */ + PxU16 extraDataStreamSize; + + /** + \brief Additional information on the contact report pair. + + @see PxContactPairHeaderFlag + */ + PxContactPairHeaderFlags flags; + + /** + \brief pointer to the contact pairs + */ + const struct PxContactPair* pairs; + + /** + \brief number of contact pairs + */ + PxU32 nbPairs; +}; + + +/** +\brief Collection of flags providing information on contact report pairs. + +@see PxContactPair +*/ +struct PxContactPairFlag +{ + enum Enum + { + /** + \brief The shape with index 0 has been removed from the actor/scene. + */ + eREMOVED_SHAPE_0 = (1<<0), + + /** + \brief The shape with index 1 has been removed from the actor/scene. + */ + eREMOVED_SHAPE_1 = (1<<1), + + /** + \brief First actor pair contact. + + The provided shape pair marks the first contact between the two actors, no other shape pair has been touching prior to the current simulation frame. + + \note: This info is only available if #PxPairFlag::eNOTIFY_TOUCH_FOUND has been declared for the pair. + */ + eACTOR_PAIR_HAS_FIRST_TOUCH = (1<<2), + + /** + \brief All contact between the actor pair was lost. + + All contact between the two actors has been lost, no shape pairs remain touching after the current simulation frame. + */ + eACTOR_PAIR_LOST_TOUCH = (1<<3), + + /** + \brief Internal flag, used by #PxContactPair.extractContacts() + + The applied contact impulses are provided for every contact point. + This is the case if #PxPairFlag::eSOLVE_CONTACT has been set for the pair. + */ + eINTERNAL_HAS_IMPULSES = (1<<4), + + /** + \brief Internal flag, used by #PxContactPair.extractContacts() + + The provided contact point information is flipped with regards to the shapes of the contact pair. This mainly concerns the order of the internal triangle indices. + */ + eINTERNAL_CONTACTS_ARE_FLIPPED = (1<<5) + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxContactPairFlag. + +@see PxContactPairFlag +*/ +typedef PxFlags PxContactPairFlags; +PX_FLAGS_OPERATORS(PxContactPairFlag::Enum, PxU16) + + +/** +\brief A contact point as used by contact notification +*/ +struct PxContactPairPoint +{ + /** + \brief The position of the contact point between the shapes, in world space. + */ + PxVec3 position; + + /** + \brief The separation of the shapes at the contact point. A negative separation denotes a penetration. + */ + PxReal separation; + + /** + \brief The normal of the contacting surfaces at the contact point. The normal direction points from the second shape to the first shape. + */ + PxVec3 normal; + + /** + \brief The surface index of shape 0 at the contact point. This is used to identify the surface material. + */ + PxU32 internalFaceIndex0; + + /** + \brief The impulse applied at the contact point, in world space. Divide by the simulation time step to get a force value. + */ + PxVec3 impulse; + + /** + \brief The surface index of shape 1 at the contact point. This is used to identify the surface material. + */ + PxU32 internalFaceIndex1; +}; + + +/** +\brief Contact report pair information. + +Instances of this class are passed to PxSimulationEventCallback.onContact(). If contact reports have been requested for a pair of shapes (see #PxPairFlag), +then the corresponding contact information will be provided through this structure. + +@see PxSimulationEventCallback.onContact() +*/ +struct PxContactPair +{ + public: + PX_INLINE PxContactPair() {} + + /** + \brief The two shapes that make up the pair. + + \note The shape pointers might reference deleted shapes. This will be the case if #PxPairFlag::eNOTIFY_TOUCH_LOST + or #PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST events were requested for the pair and one of the involved shapes + gets deleted. Check the #flags member to see whether that is the case. Do not dereference a pointer to a + deleted shape. The pointer to a deleted shape is only provided such that user data structures which might + depend on the pointer value can be updated. + + @see PxShape + */ + PxShape* shapes[2]; + + /** + \brief Pointer to first patch header in contact stream containing contact patch data + + This pointer is only valid if contact point information has been requested for the contact report pair (see #PxPairFlag::eNOTIFY_CONTACT_POINTS). + Use #extractContacts() as a reference for the data layout of the stream. + */ + const PxU8* contactPatches; + + /** + \brief Pointer to first contact point in contact stream containing contact data + + This pointer is only valid if contact point information has been requested for the contact report pair (see #PxPairFlag::eNOTIFY_CONTACT_POINTS). + Use #extractContacts() as a reference for the data layout of the stream. + */ + const PxU8* contactPoints; + + /** + \brief Buffer containing applied impulse data. + + This pointer is only valid if contact point information has been requested for the contact report pair (see #PxPairFlag::eNOTIFY_CONTACT_POINTS). + Use #extractContacts() as a reference for the data layout of the stream. + */ + const PxReal* contactImpulses; + + /** + \brief Size of the contact stream [bytes] including force buffer + */ + PxU32 requiredBufferSize; + + /** + \brief Number of contact points stored in the contact stream + */ + PxU8 contactCount; + + /** + \brief Number of contact patches stored in the contact stream + */ + + PxU8 patchCount; + + /** + \brief Size of the contact stream [bytes] not including force buffer + */ + + PxU16 contactStreamSize; + + /** + \brief Additional information on the contact report pair. + + @see PxContactPairFlag + */ + PxContactPairFlags flags; + + /** + \brief Flags raised due to the contact. + + The events field is a combination of: + +
    +
  • PxPairFlag::eNOTIFY_TOUCH_FOUND,
  • +
  • PxPairFlag::eNOTIFY_TOUCH_PERSISTS,
  • +
  • PxPairFlag::eNOTIFY_TOUCH_LOST,
  • +
  • PxPairFlag::eNOTIFY_TOUCH_CCD,
  • +
  • PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND,
  • +
  • PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS,
  • +
  • PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST
  • +
+ + See the documentation of #PxPairFlag for an explanation of each. + + \note eNOTIFY_TOUCH_CCD can get raised even if the pair did not request this event. However, in such a case it will only get + raised in combination with one of the other flags to point out that the other event occured during a CCD pass. + + @see PxPairFlag + */ + PxPairFlags events; + + PxU32 internalData[2]; // For internal use only + + /** + \brief Extracts the contact points from the stream and stores them in a convenient format. + + \param[out] userBuffer Array of PxContactPairPoint structures to extract the contact points to. The number of contacts for a pair is defined by #contactCount + \param[in] bufferSize Number of PxContactPairPoint structures the provided buffer can store. + \return Number of contact points written to the buffer. + + @see PxContactPairPoint + */ + PX_INLINE PxU32 extractContacts(PxContactPairPoint* userBuffer, PxU32 bufferSize) const; + + /** + \brief Helper method to clone the contact pair and copy the contact data stream into a user buffer. + + The contact data stream is only accessible during the contact report callback. This helper function provides copy functionality + to buffer the contact stream information such that it can get accessed at a later stage. + + \param[out] newPair The contact pair info will get copied to this instance. The contact data stream pointer of the copy will be redirected to the provided user buffer. Use NULL to skip the contact pair copy operation. + \param[out] bufferMemory Memory block to store the contact data stream to. At most #requiredBufferSize bytes will get written to the buffer. + */ + PX_INLINE void bufferContacts(PxContactPair* newPair, PxU8* bufferMemory) const; + + PX_INLINE const PxU32* getInternalFaceIndices() const; +}; + + +PX_INLINE PxU32 PxContactPair::extractContacts(PxContactPairPoint* userBuffer, PxU32 bufferSize) const +{ + PxU32 nbContacts = 0; + + if(contactCount && bufferSize) + { + PxContactStreamIterator iter(contactPatches, contactPoints, getInternalFaceIndices(), patchCount, contactCount); + + const PxReal* impulses = contactImpulses; + + const PxU32 flippedContacts = (flags & PxContactPairFlag::eINTERNAL_CONTACTS_ARE_FLIPPED); + const PxU32 hasImpulses = (flags & PxContactPairFlag::eINTERNAL_HAS_IMPULSES); + + while(iter.hasNextPatch()) + { + iter.nextPatch(); + while(iter.hasNextContact()) + { + iter.nextContact(); + PxContactPairPoint& dst = userBuffer[nbContacts]; + dst.position = iter.getContactPoint(); + dst.separation = iter.getSeparation(); + dst.normal = iter.getContactNormal(); + if(!flippedContacts) + { + dst.internalFaceIndex0 = iter.getFaceIndex0(); + dst.internalFaceIndex1 = iter.getFaceIndex1(); + } + else + { + dst.internalFaceIndex0 = iter.getFaceIndex1(); + dst.internalFaceIndex1 = iter.getFaceIndex0(); + } + + if(hasImpulses) + { + const PxReal impulse = impulses[nbContacts]; + dst.impulse = dst.normal * impulse; + } + else + dst.impulse = PxVec3(0.0f); + ++nbContacts; + if(nbContacts == bufferSize) + return nbContacts; + } + } + } + + return nbContacts; +} + + +PX_INLINE void PxContactPair::bufferContacts(PxContactPair* newPair, PxU8* bufferMemory) const +{ + PxU8* patches = bufferMemory; + PxU8* contacts = NULL; + if(patches) + { + contacts = bufferMemory + patchCount * sizeof(PxContactPatch); + PxMemCopy(patches, contactPatches, sizeof(PxContactPatch)*patchCount); + PxMemCopy(contacts, contactPoints, contactStreamSize - (sizeof(PxContactPatch)*patchCount)); + } + + if(contactImpulses) + { + PxMemCopy(bufferMemory + ((contactStreamSize + 15) & (~15)), contactImpulses, sizeof(PxReal) * contactCount); + } + + if (newPair) + { + *newPair = *this; + newPair->contactPatches = patches; + newPair->contactPoints = contacts; + } +} + + +PX_INLINE const PxU32* PxContactPair::getInternalFaceIndices() const +{ + return reinterpret_cast(contactImpulses + contactCount); +} + +/** +\brief Collection of flags providing information on trigger report pairs. + +@see PxTriggerPair +*/ +struct PxTriggerPairFlag +{ + enum Enum + { + eREMOVED_SHAPE_TRIGGER = (1<<0), //!< The trigger shape has been removed from the actor/scene. + eREMOVED_SHAPE_OTHER = (1<<1), //!< The shape causing the trigger event has been removed from the actor/scene. + eNEXT_FREE = (1<<2) //!< For internal use only. + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxTriggerPairFlag. + +@see PxTriggerPairFlag +*/ +typedef PxFlags PxTriggerPairFlags; +PX_FLAGS_OPERATORS(PxTriggerPairFlag::Enum, PxU8) + + +/** +\brief Descriptor for a trigger pair. + +An array of these structs gets passed to the PxSimulationEventCallback::onTrigger() report. + +\note The shape pointers might reference deleted shapes. This will be the case if #PxPairFlag::eNOTIFY_TOUCH_LOST + events were requested for the pair and one of the involved shapes gets deleted. Check the #flags member to see + whether that is the case. Do not dereference a pointer to a deleted shape. The pointer to a deleted shape is + only provided such that user data structures which might depend on the pointer value can be updated. + +@see PxSimulationEventCallback.onTrigger() +*/ +struct PxTriggerPair +{ + PX_INLINE PxTriggerPair() {} + + PxShape* triggerShape; //!< The shape that has been marked as a trigger. + PxRigidActor* triggerActor; //!< The actor to which triggerShape is attached + PxShape* otherShape; //!< The shape causing the trigger event. \deprecated (see #PxSimulationEventCallback::onTrigger()) If collision between trigger shapes is enabled, then this member might point to a trigger shape as well. + PxRigidActor* otherActor; //!< The actor to which otherShape is attached + PxPairFlag::Enum status; //!< Type of trigger event (eNOTIFY_TOUCH_FOUND or eNOTIFY_TOUCH_LOST). eNOTIFY_TOUCH_PERSISTS events are not supported. + PxTriggerPairFlags flags; //!< Additional information on the pair (see #PxTriggerPairFlag) +}; + + +/** +\brief Descriptor for a broken constraint. + +An array of these structs gets passed to the PxSimulationEventCallback::onConstraintBreak() report. + +@see PxConstraint PxSimulationEventCallback.onConstraintBreak() +*/ +struct PxConstraintInfo +{ + PX_INLINE PxConstraintInfo() {} + PX_INLINE PxConstraintInfo(PxConstraint* c, void* extRef, PxU32 t) : constraint(c), externalReference(extRef), type(t) {} + + PxConstraint* constraint; //!< The broken constraint. + void* externalReference; //!< The external object which owns the constraint (see #PxConstraintConnector::getExternalReference()) + PxU32 type; //!< Unique type ID of the external object. Allows to cast the provided external reference to the appropriate type +}; + + +/** +\brief An interface class that the user can implement in order to receive simulation events. + +With the exception of onAdvance(), the events get sent during the call to either #PxScene::fetchResults() or +#PxScene::flushSimulation() with sendPendingReports=true. onAdvance() gets called while the simulation +is running (that is between PxScene::simulate(), onAdvance() and PxScene::fetchResults()). + +\note SDK state should not be modified from within the callbacks. In particular objects should not +be created or destroyed. If state modification is needed then the changes should be stored to a buffer +and performed after the simulation step. + +Threading: With the exception of onAdvance(), it is not necessary to make these callbacks thread safe as +they will only be called in the context of the user thread. + +@see PxScene.setSimulationEventCallback() PxScene.getSimulationEventCallback() +*/ +class PxSimulationEventCallback + { + public: + /** + \brief This is called when a breakable constraint breaks. + + \note The user should not release the constraint shader inside this call! + + \note No event will get reported if the constraint breaks but gets deleted while the time step is still being simulated. + + \param[in] constraints - The constraints which have been broken. + \param[in] count - The number of constraints + + @see PxConstraint PxConstraintDesc.linearBreakForce PxConstraintDesc.angularBreakForce + */ + virtual void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count) = 0; + + /** + \brief This is called with the actors which have just been woken up. + + \note Only supported by rigid bodies yet. + \note Only called on actors for which the PxActorFlag eSEND_SLEEP_NOTIFIES has been set. + \note Only the latest sleep state transition happening between fetchResults() of the previous frame and fetchResults() of the current frame + will get reported. For example, let us assume actor A is awake, then A->putToSleep() gets called, then later A->wakeUp() gets called. + At the next simulate/fetchResults() step only an onWake() event will get triggered because that was the last transition. + \note If an actor gets newly added to a scene with properties such that it is awake and the sleep state does not get changed by + the user or simulation, then an onWake() event will get sent at the next simulate/fetchResults() step. + + \param[in] actors - The actors which just woke up. + \param[in] count - The number of actors + + @see PxScene.setSimulationEventCallback() PxSceneDesc.simulationEventCallback PxActorFlag PxActor.setActorFlag() + */ + virtual void onWake(PxActor** actors, PxU32 count) = 0; + + /** + \brief This is called with the actors which have just been put to sleep. + + \note Only supported by rigid bodies yet. + \note Only called on actors for which the PxActorFlag eSEND_SLEEP_NOTIFIES has been set. + \note Only the latest sleep state transition happening between fetchResults() of the previous frame and fetchResults() of the current frame + will get reported. For example, let us assume actor A is asleep, then A->wakeUp() gets called, then later A->putToSleep() gets called. + At the next simulate/fetchResults() step only an onSleep() event will get triggered because that was the last transition (assuming the simulation + does not wake the actor up). + \note If an actor gets newly added to a scene with properties such that it is asleep and the sleep state does not get changed by + the user or simulation, then an onSleep() event will get sent at the next simulate/fetchResults() step. + + \param[in] actors - The actors which have just been put to sleep. + \param[in] count - The number of actors + + @see PxScene.setSimulationEventCallback() PxSceneDesc.simulationEventCallback PxActorFlag PxActor.setActorFlag() + */ + virtual void onSleep(PxActor** actors, PxU32 count) = 0; + + /** + \brief This is called when certain contact events occur. + + The method will be called for a pair of actors if one of the colliding shape pairs requested contact notification. + You request which events are reported using the filter shader/callback mechanism (see #PxSimulationFilterShader, + #PxSimulationFilterCallback, #PxPairFlag). + + Do not keep references to the passed objects, as they will be + invalid after this function returns. + + \param[in] pairHeader Information on the two actors whose shapes triggered a contact report. + \param[in] pairs The contact pairs of two actors for which contact reports have been requested. See #PxContactPair. + \param[in] nbPairs The number of provided contact pairs. + + @see PxScene.setSimulationEventCallback() PxSceneDesc.simulationEventCallback PxContactPair PxPairFlag PxSimulationFilterShader PxSimulationFilterCallback + */ + virtual void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 nbPairs) = 0; + + /** + \brief This is called with the current trigger pair events. + + Shapes which have been marked as triggers using PxShapeFlag::eTRIGGER_SHAPE will send events + according to the pair flag specification in the filter shader (see #PxPairFlag, #PxSimulationFilterShader). + + \note Trigger shapes will no longer send notification events for interactions with other trigger shapes. + + \param[in] pairs - The trigger pair events. + \param[in] count - The number of trigger pair events. + + @see PxScene.setSimulationEventCallback() PxSceneDesc.simulationEventCallback PxPairFlag PxSimulationFilterShader PxShapeFlag PxShape.setFlag() + */ + virtual void onTrigger(PxTriggerPair* pairs, PxU32 count) = 0; + + /** + \brief Provides early access to the new pose of moving rigid bodies. + + When this call occurs, rigid bodies having the #PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW + flag set, were moved by the simulation and their new poses can be accessed through the provided buffers. + + \note The provided buffers are valid and can be read until the next call to #PxScene::simulate() or #PxScene::collide(). + + \note Buffered user changes to the rigid body pose will not yet be reflected in the provided data. More important, + the provided data might contain bodies that have been deleted while the simulation was running. It is the user's + responsibility to detect and avoid dereferencing such bodies. + + \note This callback gets triggered while the simulation is running. If the provided rigid body references are used to + read properties of the object, then the callback has to guarantee no other thread is writing to the same body at the same + time. + + \note The code in this callback should be lightweight as it can block the simulation, that is, the + #PxScene::fetchResults() call. + + \param[in] bodyBuffer The rigid bodies that moved and requested early pose reporting. + \param[in] poseBuffer The integrated rigid body poses of the bodies listed in bodyBuffer. + \param[in] count The number of entries in the provided buffers. + + @see PxScene.setSimulationEventCallback() PxSceneDesc.simulationEventCallback PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW + */ + virtual void onAdvance(const PxRigidBody*const* bodyBuffer, const PxTransform* poseBuffer, const PxU32 count) = 0; + + virtual ~PxSimulationEventCallback() {} + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxSimulationStatistics.h b/src/PhysX/physx/include/PxSimulationStatistics.h new file mode 100644 index 000000000..6aa3e74a7 --- /dev/null +++ b/src/PhysX/physx/include/PxSimulationStatistics.h @@ -0,0 +1,330 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_SIMULATION_STATISTICS +#define PX_SIMULATION_STATISTICS +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxAssert.h" +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Class used to retrieve statistics for a simulation step. + +@see PxScene::getSimulationStatistics() +*/ +class PxSimulationStatistics +{ +public: + + /** + \brief Different types of rigid body collision pair statistics. + @see getRbPairStats + */ + enum RbPairStatsType + { + /** + \brief Shape pairs processed as discrete contact pairs for the current simulation step. + */ + eDISCRETE_CONTACT_PAIRS, + + /** + \brief Shape pairs processed as swept integration pairs for the current simulation step. + + \note Counts the pairs for which special CCD (continuous collision detection) work was actually done and NOT the number of pairs which were configured for CCD. + Furthermore, there can be multiple CCD passes and all processed pairs of all passes are summed up, hence the number can be larger than the amount of pairs which have been configured for CCD. + + @see PxPairFlag::eDETECT_CCD_CONTACT, + */ + eCCD_PAIRS, + + /** + \brief Shape pairs processed with user contact modification enabled for the current simulation step. + + @see PxContactModifyCallback + */ + eMODIFIED_CONTACT_PAIRS, + + /** + \brief Trigger shape pairs processed for the current simulation step. + + @see PxShapeFlag::eTRIGGER_SHAPE + */ + eTRIGGER_PAIRS + }; + + +//objects: + /** + \brief Number of active PxConstraint objects (joints etc.) for the current simulation step. + */ + PxU32 nbActiveConstraints; + + /** + \brief Number of active dynamic bodies for the current simulation step. + + \note Does not include active kinematic bodies + */ + PxU32 nbActiveDynamicBodies; + + /** + \brief Number of active kinematic bodies for the current simulation step. + + \note Kinematic deactivation occurs at the end of the frame after the last call to PxRigidDynamic::setKinematicTarget() was called so kinematics that are + deactivated in a given frame will be included by this counter. + */ + PxU32 nbActiveKinematicBodies; + + /** + \brief Number of static bodies for the current simulation step. + */ + PxU32 nbStaticBodies; + + /** + \brief Number of dynamic bodies for the current simulation step. + + \note Includes inactive and kinematic bodies, and articulation links + */ + PxU32 nbDynamicBodies; + + /** + \brief Number of shapes of each geometry type. + */ + + PxU32 nbShapes[PxGeometryType::eGEOMETRY_COUNT]; + + /** + \brief Number of aggregates in the scene. + */ + PxU32 nbAggregates; + + /** + \brief Number of articulations in the scene. + */ + PxU32 nbArticulations; + +//solver: + /** + \brief The number of 1D axis constraints(joints+contact) present in the current simulation step. + */ + PxU32 nbAxisSolverConstraints; + + /** + \brief The size (in bytes) of the compressed contact stream in the current simulation step + */ + PxU32 compressedContactSize; + + /** + \brief The total required size (in bytes) of the contact constraints in the current simulation step + */ + PxU32 requiredContactConstraintMemory; + + /** + \brief The peak amount of memory (in bytes) that was allocated for constraints (this includes joints) in the current simulation step + */ + PxU32 peakConstraintMemory; + +//broadphase: + /** + \brief Get number of broadphase volumes added for the current simulation step. + + \return Number of broadphase volumes added. + */ + PX_FORCE_INLINE PxU32 getNbBroadPhaseAdds() const + { + return nbBroadPhaseAdds; + } + + /** + \brief Get number of broadphase volumes removed for the current simulation step. + + \return Number of broadphase volumes removed. + */ + PX_FORCE_INLINE PxU32 getNbBroadPhaseRemoves() const + { + return nbBroadPhaseRemoves; + } + +//collisions: + /** + \brief Get number of shape collision pairs of a certain type processed for the current simulation step. + + There is an entry for each geometry pair type. + + \note entry[i][j] = entry[j][i], hence, if you want the sum of all pair + types, you need to discard the symmetric entries + + \param[in] pairType The type of pair for which to get information + \param[in] g0 The geometry type of one pair object + \param[in] g1 The geometry type of the other pair object + \return Number of processed pairs of the specified geometry types. + */ + PxU32 getRbPairStats(RbPairStatsType pairType, PxGeometryType::Enum g0, PxGeometryType::Enum g1) const + { + PX_ASSERT_WITH_MESSAGE( (pairType >= eDISCRETE_CONTACT_PAIRS) && + (pairType <= eTRIGGER_PAIRS), + "Invalid pairType in PxSimulationStatistics::getRbPairStats"); + + if (g0 >= PxGeometryType::eGEOMETRY_COUNT || g1 >= PxGeometryType::eGEOMETRY_COUNT) + { + PX_ASSERT(false); + return 0; + } + + PxU32 nbPairs = 0; + switch(pairType) + { + case eDISCRETE_CONTACT_PAIRS: + nbPairs = nbDiscreteContactPairs[g0][g1]; + break; + case eCCD_PAIRS: + nbPairs = nbCCDPairs[g0][g1]; + break; + case eMODIFIED_CONTACT_PAIRS: + nbPairs = nbModifiedContactPairs[g0][g1]; + break; + case eTRIGGER_PAIRS: + nbPairs = nbTriggerPairs[g0][g1]; + break; + } + return nbPairs; + } + + /** + \brief Total number of (non CCD) pairs reaching narrow phase + */ + PxU32 nbDiscreteContactPairsTotal; + + /** + \brief Total number of (non CCD) pairs for which contacts are successfully cached (<=nbDiscreteContactPairsTotal) + \note This includes pairs for which no contacts are generated, it still counts as a cache hit. + */ + PxU32 nbDiscreteContactPairsWithCacheHits; + + /** + \brief Total number of (non CCD) pairs for which at least 1 contact was generated (<=nbDiscreteContactPairsTotal) + */ + PxU32 nbDiscreteContactPairsWithContacts; + + /** + \brief Number of new pairs found by BP this frame + */ + PxU32 nbNewPairs; + + /** + \brief Number of lost pairs from BP this frame + */ + PxU32 nbLostPairs; + + /** + \brief Number of new touches found by NP this frame + */ + PxU32 nbNewTouches; + + /** + \brief Number of lost touches from NP this frame + */ + PxU32 nbLostTouches; + + /** + \brief Number of partitions used by the solver this frame + */ + PxU32 nbPartitions; + + PxSimulationStatistics() : + nbActiveConstraints (0), + nbActiveDynamicBodies (0), + nbActiveKinematicBodies (0), + nbStaticBodies (0), + nbDynamicBodies (0), + nbAggregates (0), + nbArticulations (0), + nbAxisSolverConstraints (0), + compressedContactSize (0), + requiredContactConstraintMemory (0), + peakConstraintMemory (0), + nbDiscreteContactPairsTotal (0), + nbDiscreteContactPairsWithCacheHits (0), + nbDiscreteContactPairsWithContacts (0), + nbNewPairs (0), + nbLostPairs (0), + nbNewTouches (0), + nbLostTouches (0), + nbPartitions (0) + { + nbBroadPhaseAdds = 0; + nbBroadPhaseRemoves = 0; + + for(PxU32 i=0; i < PxGeometryType::eGEOMETRY_COUNT; i++) + { + for(PxU32 j=0; j < PxGeometryType::eGEOMETRY_COUNT; j++) + { + nbDiscreteContactPairs[i][j] = 0; + nbModifiedContactPairs[i][j] = 0; + nbCCDPairs[i][j] = 0; + nbTriggerPairs[i][j] = 0; + } + } + + for(PxU32 i=0; i < PxGeometryType::eGEOMETRY_COUNT; i++) + { + nbShapes[i] = 0; + } + } + + + // + // We advise to not access these members directly. Use the provided accessor methods instead. + // +//broadphase: + PxU32 nbBroadPhaseAdds; + PxU32 nbBroadPhaseRemoves; + +//collisions: + PxU32 nbDiscreteContactPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 nbCCDPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 nbModifiedContactPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 nbTriggerPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxVisualizationParameter.h b/src/PhysX/physx/include/PxVisualizationParameter.h new file mode 100644 index 000000000..eac8a3f00 --- /dev/null +++ b/src/PhysX/physx/include/PxVisualizationParameter.h @@ -0,0 +1,254 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_DEBUG_VISUALIZATION_PARAMETER +#define PX_PHYSICS_NX_DEBUG_VISUALIZATION_PARAMETER + +#include "foundation/PxPreprocessor.h" + +/** \addtogroup physics +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/* +NOTE: Parameters should NOT be conditionally compiled out. Even if a particular feature is not available. +Otherwise the parameter values get shifted about and the numeric values change per platform. This causes problems +when trying to serialize parameters. + +New parameters should also be added to the end of the list for this reason. Also make sure to update +eNUM_VALUES, which should be one higher than the maximum value in the enum. +*/ + +/** +\brief Debug visualization parameters. + +#PxVisualizationParameter::eSCALE is the master switch for enabling visualization, please read the corresponding documentation +for further details. + +@see PxScene.setVisualizationParameter() PxScene.getVisualizationParameter() PxScene.getRenderBuffer() +*/ +struct PxVisualizationParameter +{ + enum Enum + { + /* RigidBody-related parameters */ + + /** + \brief This overall visualization scale gets multiplied with the individual scales. Setting to zero ignores all visualizations. Default is 0. + + The below settings permit the debug visualization of various simulation properties. + The setting is either zero, in which case the property is not drawn. Otherwise it is a scaling factor + that determines the size of the visualization widgets. + + Only objects for which visualization is turned on using setFlag(eVISUALIZATION) are visualized (see #PxActorFlag::eVISUALIZATION, #PxShapeFlag::eVISUALIZATION, ...). + Contacts are visualized if they involve a body which is being visualized. + Default is 0. + + Notes: + - to see any visualization, you have to set PxVisualizationParameter::eSCALE to nonzero first. + - the scale factor has been introduced because it's difficult (if not impossible) to come up with a + good scale for 3D vectors. Normals are normalized and their length is always 1. But it doesn't mean + we should render a line of length 1. Depending on your objects/scene, this might be completely invisible + or extremely huge. That's why the scale factor is here, to let you tune the length until it's ok in + your scene. + - however, things like collision shapes aren't ambiguous. They are clearly defined for example by the + triangles & polygons themselves, and there's no point in scaling that. So the visualization widgets + are only scaled when it makes sense. + + Range: [0, PX_MAX_F32)
+ Default: 0 + */ + eSCALE, + + + /** + \brief Visualize the world axes. + */ + eWORLD_AXES, + + /* Body visualizations */ + + /** + \brief Visualize a bodies axes. + + @see PxActor.globalPose PxActor + */ + eBODY_AXES, + + /** + \brief Visualize a body's mass axes. + + This visualization is also useful for visualizing the sleep state of bodies. Sleeping bodies are drawn in + black, while awake bodies are drawn in white. If the body is sleeping and part of a sleeping group, it is + drawn in red. + + @see PxBodyDesc.massLocalPose PxActor + */ + eBODY_MASS_AXES, + + /** + \brief Visualize the bodies linear velocity. + + @see PxBodyDesc.linearVelocity PxActor + */ + eBODY_LIN_VELOCITY, + + /** + \brief Visualize the bodies angular velocity. + + @see PxBodyDesc.angularVelocity PxActor + */ + eBODY_ANG_VELOCITY, + + + /* Contact visualisations */ + + /** + \brief Visualize contact points. Will enable contact information. + */ + eCONTACT_POINT, + + /** + \brief Visualize contact normals. Will enable contact information. + */ + eCONTACT_NORMAL, + + /** + \brief Visualize contact errors. Will enable contact information. + */ + eCONTACT_ERROR, + + /** + \brief Visualize Contact forces. Will enable contact information. + */ + eCONTACT_FORCE, + + + /** + \brief Visualize actor axes. + + @see PxRigidStatic PxRigidDynamic PxArticulationLink + */ + eACTOR_AXES, + + + /** + \brief Visualize bounds (AABBs in world space) + */ + eCOLLISION_AABBS, + + /** + \brief Shape visualization + + @see PxShape + */ + eCOLLISION_SHAPES, + + /** + \brief Shape axis visualization + + @see PxShape + */ + eCOLLISION_AXES, + + /** + \brief Compound visualization (compound AABBs in world space) + */ + eCOLLISION_COMPOUNDS, + + /** + \brief Mesh & convex face normals + + @see PxTriangleMesh PxConvexMesh + */ + eCOLLISION_FNORMALS, + + /** + \brief Active edges for meshes + + @see PxTriangleMesh + */ + eCOLLISION_EDGES, + + /** + \brief Static pruning structures + */ + eCOLLISION_STATIC, + + /** + \brief Dynamic pruning structures + */ + eCOLLISION_DYNAMIC, + + /** + \brief Visualizes pairwise state. + */ + eDEPRECATED_COLLISION_PAIRS, + + /** + \brief Joint local axes + */ + eJOINT_LOCAL_FRAMES, + + /** + \brief Joint limits + */ + eJOINT_LIMITS, + + /** + \brief Visualize culling box + */ + eCULL_BOX, + + /** + \brief MBP regions + */ + eMBP_REGIONS, + + /** + \brief This is not a parameter, it just records the current number of parameters (as maximum(PxVisualizationParameter)+1) for use in loops. + */ + eNUM_VALUES, + + eFORCE_DWORD = 0x7fffffff + }; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxBoxController.h b/src/PhysX/physx/include/characterkinematic/PxBoxController.h new file mode 100644 index 000000000..ecb7d3baa --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxBoxController.h @@ -0,0 +1,230 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CCT_BOX_CONTROLLER +#define PX_PHYSICS_CCT_BOX_CONTROLLER +/** \addtogroup character + @{ +*/ + +#include "characterkinematic/PxCharacter.h" +#include "characterkinematic/PxController.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Descriptor for a box character controller. + +@see PxBoxController PxControllerDesc +*/ +class PxBoxControllerDesc : public PxControllerDesc +{ +public: + /** + \brief constructor sets to default. + */ + PX_INLINE PxBoxControllerDesc(); + PX_INLINE virtual ~PxBoxControllerDesc() {} + + /** + \brief copy constructor. + */ + PX_INLINE PxBoxControllerDesc(const PxBoxControllerDesc&); + + /** + \brief assignment operator. + */ + PX_INLINE PxBoxControllerDesc& operator=(const PxBoxControllerDesc&); + + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE virtual void setToDefault(); + + /** + \brief returns true if the current settings are valid + + \return True if the descriptor is valid. + */ + PX_INLINE virtual bool isValid() const; + + /** + \brief Half height + + Default: 1.0 + */ + PxF32 halfHeight; // Half-height in the "up" direction + + /** + \brief Half side extent + + Default: 0.5 + */ + PxF32 halfSideExtent; // Half-extent in the "side" direction + + /** + \brief Half forward extent + + Default: 0.5 + */ + PxF32 halfForwardExtent; // Half-extent in the "forward" direction + +protected: + PX_INLINE void copy(const PxBoxControllerDesc&); +}; + +PX_INLINE PxBoxControllerDesc::PxBoxControllerDesc() : + PxControllerDesc (PxControllerShapeType::eBOX), + halfHeight (1.0f), + halfSideExtent (0.5f), + halfForwardExtent (0.5f) +{ +} + +PX_INLINE PxBoxControllerDesc::PxBoxControllerDesc(const PxBoxControllerDesc& other) : PxControllerDesc(other) +{ + copy(other); +} + +PX_INLINE PxBoxControllerDesc& PxBoxControllerDesc::operator=(const PxBoxControllerDesc& other) +{ + PxControllerDesc::operator=(other); + copy(other); + return *this; +} + +PX_INLINE void PxBoxControllerDesc::copy(const PxBoxControllerDesc& other) +{ + halfHeight = other.halfHeight; + halfSideExtent = other.halfSideExtent; + halfForwardExtent = other.halfForwardExtent; +} + +PX_INLINE void PxBoxControllerDesc::setToDefault() +{ + *this = PxBoxControllerDesc(); +} + +PX_INLINE bool PxBoxControllerDesc::isValid() const +{ + if(!PxControllerDesc::isValid()) return false; + if(halfHeight<=0.0f) return false; + if(halfSideExtent<=0.0f) return false; + if(halfForwardExtent<=0.0f) return false; + if(stepOffset>2.0f*halfHeight) return false; // Prevents obvious mistakes + return true; +} + +/** +\brief Box character controller. + +@see PxBoxControllerDesc PxController +*/ +class PxBoxController : public PxController +{ +public: + + /** + \brief Gets controller's half height. + + \return The half height of the controller. + + @see PxBoxControllerDesc.halfHeight setHalfHeight() + */ + virtual PxF32 getHalfHeight() const = 0; + + /** + \brief Gets controller's half side extent. + + \return The half side extent of the controller. + + @see PxBoxControllerDesc.halfSideExtent setHalfSideExtent() + */ + virtual PxF32 getHalfSideExtent() const = 0; + + /** + \brief Gets controller's half forward extent. + + \return The half forward extent of the controller. + + @see PxBoxControllerDesc.halfForwardExtent setHalfForwardExtent() + */ + virtual PxF32 getHalfForwardExtent() const = 0; + + /** + \brief Sets controller's half height. + + \warning this doesn't check for collisions. + + \param[in] halfHeight The new half height for the controller. + \return Currently always true. + + @see PxBoxControllerDesc.halfHeight getHalfHeight() + */ + virtual bool setHalfHeight(PxF32 halfHeight) = 0; + + /** + \brief Sets controller's half side extent. + + \warning this doesn't check for collisions. + + \param[in] halfSideExtent The new half side extent for the controller. + \return Currently always true. + + @see PxBoxControllerDesc.halfSideExtent getHalfSideExtent() + */ + virtual bool setHalfSideExtent(PxF32 halfSideExtent) = 0; + + /** + \brief Sets controller's half forward extent. + + \warning this doesn't check for collisions. + + \param[in] halfForwardExtent The new half forward extent for the controller. + \return Currently always true. + + @see PxBoxControllerDesc.halfForwardExtent getHalfForwardExtent() + */ + virtual bool setHalfForwardExtent(PxF32 halfForwardExtent) = 0; + +protected: + PX_INLINE PxBoxController() {} + virtual ~PxBoxController() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxCapsuleController.h b/src/PhysX/physx/include/characterkinematic/PxCapsuleController.h new file mode 100644 index 000000000..c8cf8a67f --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxCapsuleController.h @@ -0,0 +1,251 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CCT_CAPSULE_CONTROLLER +#define PX_PHYSICS_CCT_CAPSULE_CONTROLLER +/** \addtogroup character + @{ +*/ + +#include "characterkinematic/PxCharacter.h" +#include "characterkinematic/PxController.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxCapsuleClimbingMode +{ + enum Enum + { + eEASY, //!< Standard mode, let the capsule climb over surfaces according to impact normal + eCONSTRAINED, //!< Constrained mode, try to limit climbing according to the step offset + + eLAST + }; +}; + +/** +\brief A descriptor for a capsule character controller. + +@see PxCapsuleController PxControllerDesc +*/ +class PxCapsuleControllerDesc : public PxControllerDesc +{ +public: + /** + \brief constructor sets to default. + */ + PX_INLINE PxCapsuleControllerDesc (); + PX_INLINE virtual ~PxCapsuleControllerDesc () {} + + /** + \brief copy constructor. + */ + PX_INLINE PxCapsuleControllerDesc(const PxCapsuleControllerDesc&); + + /** + \brief assignment operator. + */ + PX_INLINE PxCapsuleControllerDesc& operator=(const PxCapsuleControllerDesc&); + + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE virtual void setToDefault(); + /** + \brief returns true if the current settings are valid + + \return True if the descriptor is valid. + */ + PX_INLINE virtual bool isValid() const; + + /** + \brief The radius of the capsule + + Default: 0.0 + + @see PxCapsuleController + */ + PxF32 radius; + + /** + \brief The height of the controller + + Default: 0.0 + + @see PxCapsuleController + */ + PxF32 height; + + /** + \brief The climbing mode + + Default: PxCapsuleClimbingMode::eEASY + + @see PxCapsuleController + */ + PxCapsuleClimbingMode::Enum climbingMode; + +protected: + PX_INLINE void copy(const PxCapsuleControllerDesc&); +}; + +PX_INLINE PxCapsuleControllerDesc::PxCapsuleControllerDesc () : PxControllerDesc(PxControllerShapeType::eCAPSULE) +{ + radius = height = 0.0f; + climbingMode = PxCapsuleClimbingMode::eEASY; +} + +PX_INLINE PxCapsuleControllerDesc::PxCapsuleControllerDesc(const PxCapsuleControllerDesc& other) : PxControllerDesc(other) +{ + copy(other); +} + +PX_INLINE PxCapsuleControllerDesc& PxCapsuleControllerDesc::operator=(const PxCapsuleControllerDesc& other) +{ + PxControllerDesc::operator=(other); + copy(other); + return *this; +} + +PX_INLINE void PxCapsuleControllerDesc::copy(const PxCapsuleControllerDesc& other) +{ + radius = other.radius; + height = other.height; + climbingMode = other.climbingMode; +} + +PX_INLINE void PxCapsuleControllerDesc::setToDefault() +{ + *this = PxCapsuleControllerDesc(); +} + +PX_INLINE bool PxCapsuleControllerDesc::isValid() const +{ + if(!PxControllerDesc::isValid()) return false; + if(radius<=0.0f) return false; + if(height<=0.0f) return false; + if(stepOffset>height+radius*2.0f) return false; // Prevents obvious mistakes + return true; +} +/** +\brief A capsule character controller. + +The capsule is defined as a position, a vertical height, and a radius. +The height is the distance between the two sphere centers at the end of the capsule. +In other words: + +p = pos (returned by controller)
+h = height
+r = radius
+ +p = center of capsule
+top sphere center = p.y + h*0.5
+bottom sphere center = p.y - h*0.5
+top capsule point = p.y + h*0.5 + r
+bottom capsule point = p.y - h*0.5 - r
+*/ +class PxCapsuleController : public PxController +{ +public: + + /** + \brief Gets controller's radius. + + \return The radius of the controller. + + @see PxCapsuleControllerDesc.radius setRadius() + */ + virtual PxF32 getRadius() const = 0; + + /** + \brief Sets controller's radius. + + \warning this doesn't check for collisions. + + \param[in] radius The new radius for the controller. + \return Currently always true. + + @see PxCapsuleControllerDesc.radius getRadius() + */ + virtual bool setRadius(PxF32 radius) = 0; + + /** + \brief Gets controller's height. + + \return The height of the capsule controller. + + @see PxCapsuleControllerDesc.height setHeight() + */ + virtual PxF32 getHeight() const = 0; + + /** + \brief Resets controller's height. + + \warning this doesn't check for collisions. + + \param[in] height The new height for the controller. + \return Currently always true. + + @see PxCapsuleControllerDesc.height getHeight() + */ + virtual bool setHeight(PxF32 height) = 0; + + /** + \brief Gets controller's climbing mode. + + \return The capsule controller's climbing mode. + + @see PxCapsuleControllerDesc.climbingMode setClimbingMode() + */ + virtual PxCapsuleClimbingMode::Enum getClimbingMode() const = 0; + + /** + \brief Sets controller's climbing mode. + + \param[in] mode The capsule controller's climbing mode. + + @see PxCapsuleControllerDesc.climbingMode getClimbingMode() + */ + virtual bool setClimbingMode(PxCapsuleClimbingMode::Enum mode) = 0; + +protected: + PX_INLINE PxCapsuleController() {} + virtual ~PxCapsuleController() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxCharacter.h b/src/PhysX/physx/include/characterkinematic/PxCharacter.h new file mode 100644 index 000000000..a0f34fea7 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxCharacter.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CHARACTER_H +#define PX_CHARACTER_H +/** \addtogroup character + @{ +*/ + +#include "foundation/Px.h" + +// define API function declaration +#if defined PX_PHYSX_STATIC_LIB || defined PX_PHYSX_CHARACTER_STATIC_LIB + #define PX_PHYSX_CHARACTER_API +#else + #if PX_WINDOWS + #if defined PX_PHYSX_CHARACTER_EXPORTS + #define PX_PHYSX_CHARACTER_API __declspec(dllexport) + #else + #define PX_PHYSX_CHARACTER_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_PHYSX_CHARACTER_API PX_UNIX_EXPORT + #else + #define PX_PHYSX_CHARACTER_API + #endif +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxController.h b/src/PhysX/physx/include/characterkinematic/PxController.h new file mode 100644 index 000000000..a0be05740 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxController.h @@ -0,0 +1,911 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PX_PHYSICS_CCT_CONTROLLER +#define PX_PHYSICS_CCT_CONTROLLER +/** \addtogroup character + @{ +*/ + +#include "characterkinematic/PxCharacter.h" +#include "characterkinematic/PxExtended.h" +#include "characterkinematic/PxControllerObstacles.h" +#include "PxQueryFiltering.h" +#include "foundation/PxErrorCallback.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief The type of controller, eg box, sphere or capsule. +*/ +struct PxControllerShapeType +{ + enum Enum + { + /** + \brief A box controller. + + @see PxBoxController PxBoxControllerDesc + */ + eBOX, + + /** + \brief A capsule controller + + @see PxCapsuleController PxCapsuleControllerDesc + */ + eCAPSULE, + + eFORCE_DWORD = 0x7fffffff + }; +}; + +class PxShape; +class PxScene; +class PxController; +class PxRigidDynamic; +class PxMaterial; +struct PxFilterData; +class PxQueryFilterCallback; +class PxControllerBehaviorCallback; +class PxObstacleContext; +class PxObstacle; + +/** +\brief specifies how a CCT interacts with non-walkable parts. + +This is only used when slopeLimit is non zero. It is currently enabled for static actors only, and not supported for spheres or capsules. +*/ +struct PxControllerNonWalkableMode +{ + enum Enum + { + ePREVENT_CLIMBING, //!< Stops character from climbing up non-walkable slopes, but doesn't move it otherwise + ePREVENT_CLIMBING_AND_FORCE_SLIDING //!< Stops character from climbing up non-walkable slopes, and forces it to slide down those slopes + }; +}; + +/** +\brief specifies which sides a character is colliding with. +*/ +struct PxControllerCollisionFlag +{ + enum Enum + { + eCOLLISION_SIDES = (1<<0), //!< Character is colliding to the sides. + eCOLLISION_UP = (1<<1), //!< Character has collision above. + eCOLLISION_DOWN = (1<<2) //!< Character has collision below. + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxControllerCollisionFlag. + +@see PxControllerCollisionFlag +*/ +typedef PxFlags PxControllerCollisionFlags; +PX_FLAGS_OPERATORS(PxControllerCollisionFlag::Enum, PxU8) + + +/** +\brief Describes a controller's internal state. +*/ +struct PxControllerState +{ + PxVec3 deltaXP; //!< delta position vector for the object the CCT is standing/riding on. Not always match the CCT delta when variable timesteps are used. + PxShape* touchedShape; //!< Shape on which the CCT is standing + PxRigidActor* touchedActor; //!< Actor owning 'touchedShape' + ObstacleHandle touchedObstacleHandle; // Obstacle on which the CCT is standing + PxU32 collisionFlags; //!< Last known collision flags (PxControllerCollisionFlag) + bool standOnAnotherCCT; //!< Are we standing on another CCT? + bool standOnObstacle; //!< Are we standing on a user-defined obstacle? + bool isMovingUp; //!< is CCT moving up or not? (i.e. explicit jumping) +}; + +/** +\brief Describes a controller's internal statistics. +*/ +struct PxControllerStats +{ + PxU16 nbIterations; + PxU16 nbFullUpdates; + PxU16 nbPartialUpdates; + PxU16 nbTessellation; +}; + +/** +\brief Describes a generic CCT hit. +*/ +struct PxControllerHit +{ + PxController* controller; //!< Current controller + PxExtendedVec3 worldPos; //!< Contact position in world space + PxVec3 worldNormal; //!< Contact normal in world space + PxVec3 dir; //!< Motion direction + PxF32 length; //!< Motion length +}; + +/** +\brief Describes a hit between a CCT and a shape. Passed to onShapeHit() + +@see PxUserControllerHitReport.onShapeHit() +*/ +struct PxControllerShapeHit : public PxControllerHit +{ + PxShape* shape; //!< Touched shape + PxRigidActor* actor; //!< Touched actor + PxU32 triangleIndex; //!< touched triangle index (only for meshes/heightfields) +}; + +/** +\brief Describes a hit between a CCT and another CCT. Passed to onControllerHit(). + +@see PxUserControllerHitReport.onControllerHit() +*/ +struct PxControllersHit : public PxControllerHit +{ + PxController* other; //!< Touched controller +}; + +/** +\brief Describes a hit between a CCT and a user-defined obstacle. Passed to onObstacleHit(). + +@see PxUserControllerHitReport.onObstacleHit() PxObstacleContext +*/ +struct PxControllerObstacleHit : public PxControllerHit +{ + const void* userData; +}; + +/** +\brief User callback class for character controller events. + +\note Character controller hit reports are only generated when move is called. + +@see PxControllerDesc.callback +*/ +class PxUserControllerHitReport +{ +public: + + /** + \brief Called when current controller hits a shape. + + This is called when the CCT moves and hits a shape. This will not be called when a moving shape hits a non-moving CCT. + + \param[in] hit Provides information about the hit. + + @see PxControllerShapeHit + */ + virtual void onShapeHit(const PxControllerShapeHit& hit) = 0; + + /** + \brief Called when current controller hits another controller. + + \param[in] hit Provides information about the hit. + + @see PxControllersHit + */ + virtual void onControllerHit(const PxControllersHit& hit) = 0; + + /** + \brief Called when current controller hits a user-defined obstacle. + + \param[in] hit Provides information about the hit. + + @see PxControllerObstacleHit PxObstacleContext + */ + virtual void onObstacleHit(const PxControllerObstacleHit& hit) = 0; + +protected: + virtual ~PxUserControllerHitReport(){} +}; + + +/** +\brief Dedicated filtering callback for CCT vs CCT. + +This controls collisions between CCTs (one CCT vs anoter CCT). + +To make each CCT collide against all other CCTs, just return true - or simply avoid defining a callback. +To make each CCT freely go through all other CCTs, just return false. +Otherwise create a custom filtering logic in this callback. + +@see PxControllerFilters +*/ +class PxControllerFilterCallback +{ +public: + virtual ~PxControllerFilterCallback(){} + + /** + \brief Filtering method for CCT-vs-CCT. + + \param[in] a First CCT + \param[in] b Second CCT + \return true to keep the pair, false to filter it out + */ + virtual bool filter(const PxController& a, const PxController& b) = 0; +}; + +/** +\brief Filtering data for "move" call. + +This class contains all filtering-related parameters for the PxController::move() call. + +Collisions between a CCT and the world are filtered using the mFilterData, mFilterCallback and mFilterFlags +members. These parameters are internally passed to PxScene::overlap() to find objects touched by the CCT. +Please refer to the PxScene::overlap() documentation for details. + +Collisions between a CCT and another CCT are filtered using the mCCTFilterCallback member. If this filter +callback is not defined, none of the CCT-vs-CCT collisions are filtered, and each CCT will collide against +all other CCTs. + +\note PxQueryFlag::eANY_HIT and PxQueryFlag::eNO_BLOCK are ignored in mFilterFlags. + +@see PxController.move() PxControllerFilterCallback +*/ +class PxControllerFilters +{ + public: + + PX_INLINE PxControllerFilters(const PxFilterData* filterData=NULL, PxQueryFilterCallback* cb=NULL, PxControllerFilterCallback* cctFilterCb=NULL) : + mFilterData (filterData), + mFilterCallback (cb), + mFilterFlags (PxQueryFlag::eSTATIC|PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER), + mCCTFilterCallback (cctFilterCb) + {} + + // CCT-vs-shapes: + const PxFilterData* mFilterData; //!< Data for internal PxQueryFilterData structure. Passed to PxScene::overlap() call. + //!< This can be NULL, in which case a default PxFilterData is used. + PxQueryFilterCallback* mFilterCallback; //!< Custom filter logic (can be NULL). Passed to PxScene::overlap() call. + PxQueryFlags mFilterFlags; //!< Flags for internal PxQueryFilterData structure. Passed to PxScene::overlap() call. + // CCT-vs-CCT: + PxControllerFilterCallback* mCCTFilterCallback; //!< CCT-vs-CCT filter callback. If NULL, all CCT-vs-CCT collisions are kept. +}; + +/** +\brief Descriptor class for a character controller. + +@see PxBoxController PxCapsuleController +*/ +class PxControllerDesc +{ +public: + + /** + \brief returns true if the current settings are valid + + \return True if the descriptor is valid. + */ + PX_INLINE virtual bool isValid() const; + + /** + \brief Returns the character controller type + + \return The controllers type. + + @see PxControllerType PxCapsuleControllerDesc PxBoxControllerDesc + */ + PX_INLINE PxControllerShapeType::Enum getType() const { return mType; } + + /** + \brief The position of the character + + \note The character's initial position must be such that it does not overlap the static geometry. + + Default: Zero + */ + PxExtendedVec3 position; + + /** + \brief Specifies the 'up' direction + + In order to provide stepping functionality the SDK must be informed about the up direction. + + Default: (0, 1, 0) + + */ + PxVec3 upDirection; + + /** + \brief The maximum slope which the character can walk up. + + In general it is desirable to limit where the character can walk, in particular it is unrealistic + for the character to be able to climb arbitary slopes. + + The limit is expressed as the cosine of desired limit angle. A value of 0 disables this feature. + + \warning It is currently enabled for static actors only (not for dynamic/kinematic actors), and not supported for spheres or capsules. + + Default: 0.707 + + @see upDirection invisibleWallHeight maxJumpHeight + */ + PxF32 slopeLimit; + + /** + \brief Height of invisible walls created around non-walkable triangles + + The library can automatically create invisible walls around non-walkable triangles defined + by the 'slopeLimit' parameter. This defines the height of those walls. If it is 0.0, then + no extra triangles are created. + + Default: 0.0 + + @see upDirection slopeLimit maxJumpHeight + */ + PxF32 invisibleWallHeight; + + /** + \brief Maximum height a jumping character can reach + + This is only used if invisible walls are created ('invisibleWallHeight' is non zero). + + When a character jumps, the non-walkable triangles he might fly over are not found + by the collision queries (since the character's bounding volume does not touch them). + Thus those non-walkable triangles do not create invisible walls, and it is possible + for a jumping character to land on a non-walkable triangle, while he wouldn't have + reached that place by just walking. + + The 'maxJumpHeight' variable is used to extend the size of the collision volume + downward. This way, all the non-walkable triangles are properly found by the collision + queries and it becomes impossible to 'jump over' invisible walls. + + If the character in your game can not jump, it is safe to use 0.0 here. Otherwise it + is best to keep this value as small as possible, since a larger collision volume + means more triangles to process. + + Default: 0.0 + + @see upDirection slopeLimit invisibleWallHeight + */ + PxF32 maxJumpHeight; + + /** + \brief The contact offset used by the controller. + + Specifies a skin around the object within which contacts will be generated. + Use it to avoid numerical precision issues. + + This is dependant on the scale of the users world, but should be a small, positive + non zero value. + + Default: 0.1 + */ + PxF32 contactOffset; + + /** + \brief Defines the maximum height of an obstacle which the character can climb. + + A small value will mean that the character gets stuck and cannot walk up stairs etc, + a value which is too large will mean that the character can climb over unrealistically + high obstacles. + + Default: 0.5 + + @see upDirection + */ + PxF32 stepOffset; + + /** + \brief Density of underlying kinematic actor + + The CCT creates a PhysX's kinematic actor under the hood. This controls its density. + + Default: 10.0 + */ + PxF32 density; + + /** + \brief Scale coefficient for underlying kinematic actor + + The CCT creates a PhysX's kinematic actor under the hood. This controls its scale factor. + This should be a number a bit smaller than 1.0. + + Default: 0.8 + */ + PxF32 scaleCoeff; + + /** + \brief Cached volume growth + + Amount of space around the controller we cache to improve performance. This is a scale factor + that should be higher than 1.0f but not too big, ideally lower than 2.0f. + + Default: 1.5 + */ + PxF32 volumeGrowth; + + /** + \brief Specifies a user report callback. + + This report callback is called when the character collides with shapes and other characters. + + Setting this to NULL disables the callback. + + Default: NULL + + @see PxUserControllerHitReport + */ + PxUserControllerHitReport* reportCallback; + + /** + \brief Specifies a user behavior callback. + + This behavior callback is called to customize the controller's behavior w.r.t. touched shapes. + + Setting this to NULL disables the callback. + + Default: NULL + + @see PxControllerBehaviorCallback + */ + PxControllerBehaviorCallback* behaviorCallback; + + /** + \brief The non-walkable mode controls if a character controller slides or not on a non-walkable part. + + This is only used when slopeLimit is non zero. + + Default: PxControllerNonWalkableMode::ePREVENT_CLIMBING + + @see PxControllerNonWalkableMode + */ + PxControllerNonWalkableMode::Enum nonWalkableMode; + + /** + \brief The material for the actor associated with the controller. + + The controller internally creates a rigid body actor. This parameter specifies the material of the actor. + + Default: NULL + + @see PxMaterial + */ + PxMaterial* material; + + /** + \brief Use a deletion listener to get informed about released objects and clear internal caches if needed. + + If a character controller registers a deletion listener, it will get informed about released objects. That allows the + controller to invalidate cached data that connects to a released object. If a deletion listener is not + registered, PxController::invalidateCache has to be called manually after objects have been released. + + @see PxController::invalidateCache + + Default: true + */ + bool registerDeletionListener; + + /** + \brief User specified data associated with the controller. + + Default: NULL + */ + void* userData; + +protected: + const PxControllerShapeType::Enum mType; //!< The type of the controller. This gets set by the derived class' ctor, the user should not have to change it. + + /** + \brief constructor sets to default. + */ + PX_INLINE PxControllerDesc(PxControllerShapeType::Enum); + PX_INLINE virtual ~PxControllerDesc(); + + /** + \brief copy constructor. + */ + PX_INLINE PxControllerDesc(const PxControllerDesc&); + + /** + \brief assignment operator. + */ + PX_INLINE PxControllerDesc& operator=(const PxControllerDesc&); + + PX_INLINE void copy(const PxControllerDesc&); +}; + +PX_INLINE PxControllerDesc::PxControllerDesc(PxControllerShapeType::Enum t) : mType(t) +{ + upDirection = PxVec3(0.0f, 1.0f, 0.0f); + slopeLimit = 0.707f; + contactOffset = 0.1f; + stepOffset = 0.5f; + density = 10.0f; + scaleCoeff = 0.8f; + volumeGrowth = 1.5f; + reportCallback = NULL; + behaviorCallback = NULL; + userData = NULL; + nonWalkableMode = PxControllerNonWalkableMode::ePREVENT_CLIMBING; + position.x = PxExtended(0.0); + position.y = PxExtended(0.0); + position.z = PxExtended(0.0); + material = NULL; + invisibleWallHeight = 0.0f; + maxJumpHeight = 0.0f; + registerDeletionListener = true; +} + +PX_INLINE PxControllerDesc::PxControllerDesc(const PxControllerDesc& other) : mType(other.mType) +{ + copy(other); +} + +PX_INLINE PxControllerDesc& PxControllerDesc::operator=(const PxControllerDesc& other) +{ + copy(other); + return *this; +} + +PX_INLINE void PxControllerDesc::copy(const PxControllerDesc& other) +{ + upDirection = other.upDirection; + slopeLimit = other.slopeLimit; + contactOffset = other.contactOffset; + stepOffset = other.stepOffset; + density = other.density; + scaleCoeff = other.scaleCoeff; + volumeGrowth = other.volumeGrowth; + reportCallback = other.reportCallback; + behaviorCallback = other.behaviorCallback; + userData = other.userData; + nonWalkableMode = other.nonWalkableMode; + position.x = other.position.x; + position.y = other.position.y; + position.z = other.position.z; + material = other.material; + invisibleWallHeight = other.invisibleWallHeight; + maxJumpHeight = other.maxJumpHeight; + registerDeletionListener = other.registerDeletionListener; +} + +PX_INLINE PxControllerDesc::~PxControllerDesc() +{ +} + +PX_INLINE bool PxControllerDesc::isValid() const +{ + if( mType!=PxControllerShapeType::eBOX + && mType!=PxControllerShapeType::eCAPSULE) + return false; + if(scaleCoeff<0.0f) return false; + if(volumeGrowth<1.0f) return false; + if(density<0.0f) return false; + if(slopeLimit<0.0f) return false; + if(stepOffset<0.0f) return false; + if(contactOffset<=0.0f) return false; + if(!material) return false; + + return true; +} + + +/** +\brief Base class for character controllers. + +@see PxCapsuleController PxBoxController +*/ +class PxController +{ +public: + //********************************************************************* + // DEPRECATED FUNCTIONS: + // + // PX_DEPRECATED virtual void setInteraction(PxCCTInteractionMode::Enum flag) = 0; + // PX_DEPRECATED virtual PxCCTInteractionMode::Enum getInteraction() const = 0; + // PX_DEPRECATED virtual void setGroupsBitmask(PxU32 bitmask) = 0; + // PX_DEPRECATED virtual PxU32 getGroupsBitmask() const = 0; + // + // => replaced with: + // + // PxControllerFilters::mCCTFilterCallback. Please define a PxControllerFilterCallback object and emulate the old interaction mode there. + // + //********************************************************************* + + /** + \brief Return the type of controller + + @see PxControllerType + */ + virtual PxControllerShapeType::Enum getType() const = 0; + + /** + \brief Releases the controller. + */ + virtual void release() = 0; + + /** + \brief Moves the character using a "collide-and-slide" algorithm. + + \param[in] disp Displacement vector + \param[in] minDist The minimum travelled distance to consider. If travelled distance is smaller, the character doesn't move. + This is used to stop the recursive motion algorithm when remaining distance to travel is small. + \param[in] elapsedTime Time elapsed since last call + \param[in] filters User-defined filters for this move + \param[in] obstacles Potential additional obstacles the CCT should collide with. + \return Collision flags, collection of ::PxControllerCollisionFlags + */ + virtual PxControllerCollisionFlags move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles=NULL) = 0; + + /** + \brief Sets controller's position. + + The position controlled by this function is the center of the collision shape. + + \warning This is a 'teleport' function, it doesn't check for collisions. + \warning The character's position must be such that it does not overlap the static geometry. + + To move the character under normal conditions use the #move() function. + + \param[in] position The new (center) positon for the controller. + \return Currently always returns true. + + @see PxControllerDesc.position getPosition() getFootPosition() setFootPosition() move() + */ + virtual bool setPosition(const PxExtendedVec3& position) = 0; + + /** + \brief Retrieve the raw position of the controller. + + The position retrieved by this function is the center of the collision shape. To retrieve the bottom position of the shape, + a.k.a. the foot position, use the getFootPosition() function. + + The position is updated by calls to move(). Calling this method without calling + move() will return the last position or the initial position of the controller. + + \return The controller's center position + + @see PxControllerDesc.position setPosition() getFootPosition() setFootPosition() move() + */ + virtual const PxExtendedVec3& getPosition() const = 0; + + /** + \brief Set controller's foot position. + + The position controlled by this function is the bottom of the collision shape, a.k.a. the foot position. + + \note The foot position takes the contact offset into account + + \warning This is a 'teleport' function, it doesn't check for collisions. + + To move the character under normal conditions use the #move() function. + + \param[in] position The new (bottom) positon for the controller. + \return Currently always returns true. + + @see PxControllerDesc.position setPosition() getPosition() getFootPosition() move() + */ + virtual bool setFootPosition(const PxExtendedVec3& position) = 0; + + /** + \brief Retrieve the "foot" position of the controller, i.e. the position of the bottom of the CCT's shape. + + \note The foot position takes the contact offset into account + + \return The controller's foot position + + @see PxControllerDesc.position setPosition() getPosition() setFootPosition() move() + */ + virtual PxExtendedVec3 getFootPosition() const = 0; + + /** + \brief Get the rigid body actor associated with this controller (see PhysX documentation). + The behavior upon manually altering this actor is undefined, you should primarily + use it for reading const properties. + + \return the actor associated with the controller. + */ + virtual PxRigidDynamic* getActor() const = 0; + + /** + \brief The step height. + + \param[in] offset The new step offset for the controller. + + @see PxControllerDesc.stepOffset + */ + virtual void setStepOffset(const PxF32 offset) =0; + + /** + \brief Retrieve the step height. + + \return The step offset for the controller. + + @see setStepOffset() + */ + virtual PxF32 getStepOffset() const =0; + + /** + \brief Sets the non-walkable mode for the CCT. + + \param[in] flag The new value of the non-walkable mode. + + \see PxControllerNonWalkableMode + */ + virtual void setNonWalkableMode(PxControllerNonWalkableMode::Enum flag) = 0; + + /** + \brief Retrieves the non-walkable mode for the CCT. + + \return The current non-walkable mode. + + \see PxControllerNonWalkableMode + */ + virtual PxControllerNonWalkableMode::Enum getNonWalkableMode() const = 0; + + /** + \brief Retrieve the contact offset. + + \return The contact offset for the controller. + + @see PxControllerDesc.contactOffset + */ + virtual PxF32 getContactOffset() const =0; + + /** + \brief Sets the contact offset. + + \param[in] offset The contact offset for the controller. + + @see PxControllerDesc.contactOffset + */ + virtual void setContactOffset(PxF32 offset) =0; + + /** + \brief Retrieve the 'up' direction. + + \return The up direction for the controller. + + @see PxControllerDesc.upDirection + */ + virtual PxVec3 getUpDirection() const =0; + + /** + \brief Sets the 'up' direction. + + \param[in] up The up direction for the controller. + + @see PxControllerDesc.upDirection + */ + virtual void setUpDirection(const PxVec3& up) =0; + + /** + \brief Retrieve the slope limit. + + \return The slope limit for the controller. + + @see PxControllerDesc.slopeLimit + */ + virtual PxF32 getSlopeLimit() const =0; + + /** + \brief Sets the slope limit. + + \note This feature can not be enabled at runtime, i.e. if the slope limit is zero when creating the CCT + (which disables the feature) then changing the slope limit at runtime will not have any effect, and the call + will be ignored. + + \param[in] slopeLimit The slope limit for the controller. + + @see PxControllerDesc.slopeLimit + */ + virtual void setSlopeLimit(PxF32 slopeLimit) =0; + + /** + \brief Flushes internal geometry cache. + + The character controller uses caching in order to speed up collision testing. The cache is + automatically flushed when a change to static objects is detected in the scene. For example when a + static shape is added, updated, or removed from the scene, the cache is automatically invalidated. + + However there may be situations that cannot be automatically detected, and those require manual + invalidation of the cache. Currently the user must call this when the filtering behavior changes (the + PxControllerFilters parameter of the PxController::move call). While the controller in principle + could detect a change in these parameters, it cannot detect a change in the behavior of the filtering + function. + + @see PxController.move + */ + virtual void invalidateCache() = 0; + + /** + \brief Retrieve the scene associated with the controller. + + \return The physics scene + */ + virtual PxScene* getScene() = 0; + + /** + \brief Returns the user data associated with this controller. + + \return The user pointer associated with the controller. + + @see PxControllerDesc.userData + */ + virtual void* getUserData() const = 0; + + /** + \brief Sets the user data associated with this controller. + + \param[in] userData The user pointer associated with the controller. + + @see PxControllerDesc.userData + */ + virtual void setUserData(void* userData) = 0; + + /** + \brief Returns information about the controller's internal state. + + \param[out] state The controller's internal state + + @see PxControllerState + */ + virtual void getState(PxControllerState& state) const = 0; + + /** + \brief Returns the controller's internal statistics. + + \param[out] stats The controller's internal statistics + + @see PxControllerStats + */ + virtual void getStats(PxControllerStats& stats) const = 0; + + /** + \brief Resizes the controller. + + This function attempts to resize the controller to a given size, while making sure the bottom + position of the controller remains constant. In other words the function modifies both the + height and the (center) position of the controller. This is a helper function that can be used + to implement a 'crouch' functionality for example. + + \param[in] height Desired controller's height + */ + virtual void resize(PxReal height) = 0; + +protected: + PX_INLINE PxController() {} + virtual ~PxController() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h b/src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h new file mode 100644 index 000000000..e3b3f74c9 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h @@ -0,0 +1,136 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_CCT_BEHAVIOR +#define PX_PHYSICS_CCT_BEHAVIOR +/** \addtogroup character + @{ +*/ + +#include "PxFiltering.h" +#include "characterkinematic/PxCharacter.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxShape; + class PxObstacle; + class PxController; + + /** + \brief specifies controller behavior + */ + struct PxControllerBehaviorFlag + { + enum Enum + { + eCCT_CAN_RIDE_ON_OBJECT = (1<<0), //!< Controller can ride on touched object (i.e. when this touched object is moving horizontally). \note The CCT vs. CCT case is not supported. + eCCT_SLIDE = (1<<1), //!< Controller should slide on touched object + eCCT_USER_DEFINED_RIDE = (1<<2) //!< Disable all code dealing with controllers riding on objects, let users define it outside of the SDK. + }; + }; + + /** + \brief Bitfield that contains a set of raised flags defined in PxControllerBehaviorFlag. + + @see PxControllerBehaviorFlag + */ + typedef PxFlags PxControllerBehaviorFlags; + PX_FLAGS_OPERATORS(PxControllerBehaviorFlag::Enum, PxU8) + + /** + \brief User behavior callback. + + This behavior callback is called to customize the controller's behavior w.r.t. touched shapes. + */ + class PxControllerBehaviorCallback + { + public: + /** + \brief Retrieve behavior flags for a shape. + + When the CCT touches a shape, the CCT's behavior w.r.t. this shape can be customized by users. + This function retrieves the desired PxControllerBehaviorFlag flags capturing the desired behavior. + + \note See comments about deprecated functions at the start of this class + + \param[in] shape The shape the CCT is currently touching + \param[in] actor The actor owning the shape + + \return Desired behavior flags for the given shape + + @see PxControllerBehaviorFlag + */ + virtual PxControllerBehaviorFlags getBehaviorFlags(const PxShape& shape, const PxActor& actor) = 0; + + /** + \brief Retrieve behavior flags for a controller. + + When the CCT touches a controller, the CCT's behavior w.r.t. this controller can be customized by users. + This function retrieves the desired PxControllerBehaviorFlag flags capturing the desired behavior. + + \note The flag PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT is not supported. + \note See comments about deprecated functions at the start of this class + + \param[in] controller The controller the CCT is currently touching + + \return Desired behavior flags for the given controller + + @see PxControllerBehaviorFlag + */ + virtual PxControllerBehaviorFlags getBehaviorFlags(const PxController& controller) = 0; + + /** + \brief Retrieve behavior flags for an obstacle. + + When the CCT touches an obstacle, the CCT's behavior w.r.t. this obstacle can be customized by users. + This function retrieves the desired PxControllerBehaviorFlag flags capturing the desired behavior. + + \note See comments about deprecated functions at the start of this class + + \param[in] obstacle The obstacle the CCT is currently touching + + \return Desired behavior flags for the given obstacle + + @see PxControllerBehaviorFlag + */ + virtual PxControllerBehaviorFlags getBehaviorFlags(const PxObstacle& obstacle) = 0; + + protected: + virtual ~PxControllerBehaviorCallback(){} + }; + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxControllerManager.h b/src/PhysX/physx/include/characterkinematic/PxControllerManager.h new file mode 100644 index 000000000..924133ca3 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxControllerManager.h @@ -0,0 +1,301 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CCT_MANAGER +#define PX_PHYSICS_CCT_MANAGER +/** \addtogroup character + @{ +*/ + +#include "characterkinematic/PxCharacter.h" + +#include "PxPhysXConfig.h" +#include "foundation/PxFlags.h" +#include "foundation/PxErrorCallback.h" +#include "common/PxRenderBuffer.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysics; +class PxScene; +class PxController; +class PxControllerDesc; +class PxObstacleContext; +class PxControllerFilterCallback; + +/** +\brief specifies debug-rendering flags +*/ +struct PxControllerDebugRenderFlag +{ + enum Enum + { + eTEMPORAL_BV = (1<<0), //!< Temporal bounding volume around controllers + eCACHED_BV = (1<<1), //!< Cached bounding volume around controllers + eOBSTACLES = (1<<2), //!< User-defined obstacles + + eNONE = 0, + eALL = 0xffffffff + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxControllerDebugRenderFlag. + +@see PxControllerDebugRenderFlag +*/ +typedef PxFlags PxControllerDebugRenderFlags; +PX_FLAGS_OPERATORS(PxControllerDebugRenderFlag::Enum, PxU32) + + +/** +\brief Manages an array of character controllers. + +@see PxController PxBoxController PxCapsuleController +*/ +class PX_PHYSX_CHARACTER_API PxControllerManager +{ +public: + /** + \brief Releases the controller manager. + + \note This will release all associated controllers and obstacle contexts. + + \note This function is required to be called to release foundation usage. + + */ + virtual void release() = 0; + + /** + \brief Returns the scene the manager is adding the controllers to. + + \return The associated physics scene. + */ + virtual PxScene& getScene() const = 0; + + /** + \brief Returns the number of controllers that are being managed. + + \return The number of controllers. + */ + virtual PxU32 getNbControllers() const = 0; + + /** + \brief Retrieve one of the controllers in the manager. + + \param index the index of the controller to return + \return The controller with the specified index. + */ + virtual PxController* getController(PxU32 index) = 0; + + /** + \brief Creates a new character controller. + + \param[in] desc The controllers descriptor + \return The new controller + + @see PxController PxController.release() PxControllerDesc + */ + virtual PxController* createController(const PxControllerDesc& desc) = 0; + + /** + \brief Releases all the controllers that are being managed. + */ + virtual void purgeControllers() = 0; + + /** + \brief Retrieves debug data. + + \return The render buffer filled with debug-render data + + @see PxControllerManager.setDebugRenderingFlags() + */ + virtual PxRenderBuffer& getRenderBuffer() = 0; + + /** + \brief Sets debug rendering flags + + \param[in] flags The debug rendering flags (combination of PxControllerDebugRenderFlags) + + @see PxControllerManager.getRenderBuffer() PxControllerDebugRenderFlags + */ + virtual void setDebugRenderingFlags(PxControllerDebugRenderFlags flags) = 0; + + /** + \brief Returns the number of obstacle contexts that are being managed. + + \return The number of obstacle contexts. + */ + virtual PxU32 getNbObstacleContexts() const = 0; + + /** + \brief Retrieve one of the obstacle contexts in the manager. + + \param index The index of the obstacle context to retrieve. + \return The obstacle context with the specified index. + */ + virtual PxObstacleContext* getObstacleContext(PxU32 index) = 0; + + /** + \brief Creates an obstacle context. + + \return New obstacle context + + @see PxObstacleContext + */ + virtual PxObstacleContext* createObstacleContext() = 0; + + /** + \brief Computes character-character interactions. + + This function is an optional helper to properly resolve interactions between characters, in case they overlap (which can happen for gameplay reasons, etc). + + You should call this once per frame, before your PxController::move() calls. The function will not move the characters directly, but it will + compute overlap information for each character that will be used in the next move() call. + + You need to provide a proper time value here so that interactions are resolved in a way that do not depend on the framerate. + + If you only have one character in the scene, or if you can guarantee your characters will never overlap, then you do not need to call this function. + + \note Releasing the manager will automatically release all the associated obstacle contexts. + + \param[in] elapsedTime Elapsed time since last call + \param[in] cctFilterCb Filtering callback for CCT-vs-CCT interactions + */ + virtual void computeInteractions(PxF32 elapsedTime, PxControllerFilterCallback* cctFilterCb=NULL) = 0; + + /** + \brief Enables or disables runtime tessellation. + + Large triangles can create accuracy issues in the sweep code, which in turn can lead to characters not sliding smoothly + against geometries, or even penetrating them. This feature allows one to reduce those issues by tessellating large + triangles at runtime, before performing sweeps against them. The amount of tessellation is controlled by the 'maxEdgeLength' parameter. + Any triangle with at least one edge length greater than the maxEdgeLength will get recursively tessellated, until resulting triangles are small enough. + + This features only applies to triangle meshes, convex meshes, heightfields and boxes. + + \param[in] flag True/false to enable/disable runtime tessellation. + \param[in] maxEdgeLength Max edge length allowed before tessellation kicks in. + */ + virtual void setTessellation(bool flag, float maxEdgeLength) = 0; + + /** + \brief Enables or disables the overlap recovery module. + + The overlap recovery module can be used to depenetrate CCTs from static objects when an overlap is detected. This can happen + in three main cases: + - when the CCT is directly spawned or teleported in another object + - when the CCT algorithm fails due to limited FPU accuracy + - when the "up vector" is modified, making the rotated CCT shape overlap surrounding objects + + When activated, the CCT module will automatically try to resolve the penetration, and move the CCT to a safe place where it does + not overlap other objects anymore. This only concerns static objects, dynamic objects are ignored by the recovery module. + + When the recovery module is not activated, it is possible for the CCTs to go through static objects. By default, the recovery + module is enabled. + + The recovery module currently works with all geometries except heightfields. + + \param[in] flag True/false to enable/disable overlap recovery module. + */ + virtual void setOverlapRecoveryModule(bool flag) = 0; + + /** + \brief Enables or disables the precise sweeps. + + Precise sweeps are more accurate, but also potentially slower than regular sweeps. + + By default, precise sweeps are enabled. + + \param[in] flag True/false to enable/disable precise sweeps. + */ + virtual void setPreciseSweeps(bool flag) = 0; + + /** + \brief Enables or disables vertical sliding against ceilings. + + Geometry is seen as "ceilings" when the following condition is met: + + dot product(contact normal, up direction)<0.0f + + This flag controls whether characters should slide vertically along the geometry in that case. + + By default, sliding is allowed. + + \param[in] flag True/false to enable/disable sliding. + */ + virtual void setPreventVerticalSlidingAgainstCeiling(bool flag) = 0; + + /** + \brief Shift the origin of the character controllers and obstacle objects by the specified vector. + + The positions of all character controllers, obstacle objects and the corresponding data structures will get adjusted to reflect the shifted origin location + (the shift vector will get subtracted from all character controller and obstacle object positions). + + \note It is the user's responsibility to keep track of the summed total origin shift and adjust all input/output to/from PhysXCharacterKinematic accordingly. + + \note This call will not automatically shift the PhysX scene and its objects. You need to call PxScene::shiftOrigin() seperately to keep the systems in sync. + + \param[in] shift Translation vector to shift the origin by. + */ + virtual void shiftOrigin(const PxVec3& shift) = 0; + +protected: + PxControllerManager() {} + virtual ~PxControllerManager() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + + /** + \brief Creates the controller manager. + + \param[in] scene PhysX scene. + \param[in] lockingEnabled Enables/disables internal locking. + + The character controller is informed by #PxDeletionListener::onRelease() when actors or shapes are released, and updates its internal + caches accordingly. If character controller movement or a call to #PxControllerManager::shiftOrigin() may overlap with actor/shape releases, + internal data structures must be guarded against concurrent access. + + Locking guarantees thread safety in such scenarios. + + \note locking may result in significant slowdown for release of actors or shapes. + + By default, locking is disabled. + */ +PX_C_EXPORT PX_PHYSX_CHARACTER_API physx::PxControllerManager* PX_CALL_CONV PxCreateControllerManager(physx::PxScene& scene, bool lockingEnabled = false); + +/** @} */ +#endif //PX_PHYSICS_CCT_MANAGER diff --git a/src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h b/src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h new file mode 100644 index 000000000..ae1ef5231 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h @@ -0,0 +1,192 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_CCT_OBSTACLES +#define PX_PHYSICS_CCT_OBSTACLES +/** \addtogroup character + @{ +*/ + +#include "characterkinematic/PxCharacter.h" +#include "characterkinematic/PxExtended.h" +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxControllerManager; + + #define INVALID_OBSTACLE_HANDLE 0xffffffff + + /** + \brief Base class for obstacles. + + @see PxBoxObstacle PxCapsuleObstacle PxObstacleContext + */ + class PxObstacle + { + protected: + PxObstacle() : + mType (PxGeometryType::eINVALID), + mUserData (NULL), + mPos (0.0, 0.0, 0.0), + mRot (PxQuat(PxIdentity)) + {} + + PxGeometryType::Enum mType; + public: + + PX_FORCE_INLINE PxGeometryType::Enum getType() const { return mType; } + + void* mUserData; + PxExtendedVec3 mPos; + PxQuat mRot; + }; + + /** + \brief A box obstacle. + + @see PxObstacle PxCapsuleObstacle PxObstacleContext + */ + class PxBoxObstacle : public PxObstacle + { + public: + PxBoxObstacle() : + mHalfExtents(0.0f) + { mType = PxGeometryType::eBOX; } + + PxVec3 mHalfExtents; + }; + + /** + \brief A capsule obstacle. + + @see PxBoxObstacle PxObstacle PxObstacleContext + */ + class PxCapsuleObstacle : public PxObstacle + { + public: + PxCapsuleObstacle() : + mHalfHeight (0.0f), + mRadius (0.0f) + { mType = PxGeometryType::eCAPSULE; } + + PxReal mHalfHeight; + PxReal mRadius; + }; + + typedef PxU32 ObstacleHandle; + + /** + \brief Context class for obstacles. + + An obstacle context class contains and manages a set of user-defined obstacles. + + @see PxBoxObstacle PxCapsuleObstacle PxObstacle + */ + class PxObstacleContext + { + public: + PxObstacleContext() {} + virtual ~PxObstacleContext() {} + + /** + \brief Releases the context. + */ + virtual void release() = 0; + + /** + \brief Retrieves the controller manager associated with this context. + + \return The associated controller manager + */ + virtual PxControllerManager& getControllerManager() const = 0; + + /** + \brief Adds an obstacle to the context. + + \param [in] obstacle Obstacle data for the new obstacle. The data gets copied. + + \return Handle for newly-added obstacle + */ + virtual ObstacleHandle addObstacle(const PxObstacle& obstacle) = 0; + + /** + \brief Removes an obstacle from the context. + + \param [in] handle Handle for the obstacle object that needs to be removed. + + \return True if success + */ + virtual bool removeObstacle(ObstacleHandle handle) = 0; + + /** + \brief Updates data for an existing obstacle. + + \param [in] handle Handle for the obstacle object that needs to be updated. + \param [in] obstacle New obstacle data + + \return True if success + */ + virtual bool updateObstacle(ObstacleHandle handle, const PxObstacle& obstacle) = 0; + + /** + \brief Retrieves number of obstacles in the context. + + \return Number of obstacles in the context + */ + virtual PxU32 getNbObstacles() const = 0; + + /** + \brief Retrieves desired obstacle. + + \param [in] i Obstacle index + + \return Desired obstacle + */ + virtual const PxObstacle* getObstacle(PxU32 i) const = 0; + + /** + \brief Retrieves desired obstacle by given handle. + + \param [in] handle Obstacle handle + + \return Desired obstacle + */ + virtual const PxObstacle* getObstacleByHandle(ObstacleHandle handle) const = 0; + }; + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxExtended.h b/src/PhysX/physx/include/characterkinematic/PxExtended.h new file mode 100644 index 000000000..1adb4b9f4 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxExtended.h @@ -0,0 +1,275 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CCT_EXTENDED +#define PX_PHYSICS_CCT_EXTENDED +/** \addtogroup character + @{ +*/ + +// This needs to be included in Foundation just for the debug renderer + +#include "PxPhysXConfig.h" +#include "foundation/PxTransform.h" +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +// This has to be done here since it also changes the top-level "Px" and "Np" APIs +#define PX_BIG_WORLDS + +#ifdef PX_BIG_WORLDS +typedef double PxExtended; +#define PX_MAX_EXTENDED PX_MAX_F64 +#define PxExtendedAbs(x) fabs(x) + +struct PxExtendedVec3 +{ + PX_INLINE PxExtendedVec3() {} + PX_INLINE PxExtendedVec3(PxExtended _x, PxExtended _y, PxExtended _z) : x(_x), y(_y), z(_z) {} + + PX_INLINE bool isZero() const + { + if(x!=0.0 || y!=0.0 || z!=0.0) return false; + return true; + } + + PX_INLINE PxExtended dot(const PxVec3& v) const + { + return x * PxExtended(v.x) + y * PxExtended(v.y) + z * PxExtended(v.z); + } + + PX_INLINE PxExtended distanceSquared(const PxExtendedVec3& v) const + { + PxExtended dx = x - v.x; + PxExtended dy = y - v.y; + PxExtended dz = z - v.z; + return dx * dx + dy * dy + dz * dz; + } + + PX_INLINE PxExtended magnitudeSquared() const + { + return x * x + y * y + z * z; + } + + PX_INLINE PxExtended magnitude() const + { + return PxSqrt(x * x + y * y + z * z); + } + + PX_INLINE PxExtended normalize() + { + PxExtended m = magnitude(); + if (m != 0.0) + { + const PxExtended il = PxExtended(1.0) / m; + x *= il; + y *= il; + z *= il; + } + return m; + } + + PX_INLINE bool isFinite() const + { + return PxIsFinite(x) && PxIsFinite(y) && PxIsFinite(z); + } + + PX_INLINE void maximum(const PxExtendedVec3& v) + { + if (x < v.x) x = v.x; + if (y < v.y) y = v.y; + if (z < v.z) z = v.z; + } + + + PX_INLINE void minimum(const PxExtendedVec3& v) + { + if (x > v.x) x = v.x; + if (y > v.y) y = v.y; + if (z > v.z) z = v.z; + } + + PX_INLINE void set(PxExtended x_, PxExtended y_, PxExtended z_) + { + this->x = x_; + this->y = y_; + this->z = z_; + } + + PX_INLINE void setPlusInfinity() + { + x = y = z = PX_MAX_EXTENDED; + } + + PX_INLINE void setMinusInfinity() + { + x = y = z = -PX_MAX_EXTENDED; + } + + PX_INLINE void cross(const PxExtendedVec3& left, const PxVec3& right) + { + // temps needed in case left or right is this. + PxExtended a = (left.y * PxExtended(right.z)) - (left.z * PxExtended(right.y)); + PxExtended b = (left.z * PxExtended(right.x)) - (left.x * PxExtended(right.z)); + PxExtended c = (left.x * PxExtended(right.y)) - (left.y * PxExtended(right.x)); + + x = a; + y = b; + z = c; + } + + PX_INLINE void cross(const PxExtendedVec3& left, const PxExtendedVec3& right) + { + // temps needed in case left or right is this. + PxExtended a = (left.y * right.z) - (left.z * right.y); + PxExtended b = (left.z * right.x) - (left.x * right.z); + PxExtended c = (left.x * right.y) - (left.y * right.x); + + x = a; + y = b; + z = c; + } + + PX_INLINE PxExtendedVec3 cross(const PxExtendedVec3& v) const + { + PxExtendedVec3 temp; + temp.cross(*this,v); + return temp; + } + + PX_INLINE void cross(const PxVec3& left, const PxExtendedVec3& right) + { + // temps needed in case left or right is this. + PxExtended a = (PxExtended(left.y) * right.z) - (PxExtended(left.z) * right.y); + PxExtended b = (PxExtended(left.z) * right.x) - (PxExtended(left.x) * right.z); + PxExtended c = (PxExtended(left.x) * right.y) - (PxExtended(left.y) * right.x); + + x = a; + y = b; + z = c; + } + + PX_INLINE PxExtendedVec3 operator-() const + { + return PxExtendedVec3(-x, -y, -z); + } + + PX_INLINE PxExtendedVec3& operator+=(const PxExtendedVec3& v) + { + x += v.x; + y += v.y; + z += v.z; + return *this; + } + + PX_INLINE PxExtendedVec3& operator-=(const PxExtendedVec3& v) + { + x -= v.x; + y -= v.y; + z -= v.z; + return *this; + } + + PX_INLINE PxExtendedVec3& operator+=(const PxVec3& v) + { + x += PxExtended(v.x); + y += PxExtended(v.y); + z += PxExtended(v.z); + return *this; + } + + PX_INLINE PxExtendedVec3& operator-=(const PxVec3& v) + { + x -= PxExtended(v.x); + y -= PxExtended(v.y); + z -= PxExtended(v.z); + return *this; + } + + PX_INLINE PxExtendedVec3& operator*=(const PxReal& s) + { + x *= PxExtended(s); + y *= PxExtended(s); + z *= PxExtended(s); + return *this; + } + + PX_INLINE PxExtendedVec3 operator+(const PxExtendedVec3& v) const + { + return PxExtendedVec3(x + v.x, y + v.y, z + v.z); + } + + PX_INLINE PxVec3 operator-(const PxExtendedVec3& v) const + { + return PxVec3(PxReal(x - v.x), PxReal(y - v.y), PxReal(z - v.z)); + } + + PX_INLINE PxExtended& operator[](int index) + { + PX_ASSERT(index>=0 && index<=2); + + return reinterpret_cast(this)[index]; + } + + + PX_INLINE PxExtended operator[](int index) const + { + PX_ASSERT(index>=0 && index<=2); + + return reinterpret_cast(this)[index]; + } + + PxExtended x,y,z; +}; + + PX_FORCE_INLINE PxVec3 toVec3(const PxExtendedVec3& v) + { + return PxVec3(float(v.x), float(v.y), float(v.z)); + } + +#else +// Big worlds not defined + +typedef PxVec3 PxExtendedVec3; +typedef PxReal PxExtended; +#define PX_MAX_EXTENDED PX_MAX_F32 +#define PxExtendedAbs(x) fabsf(x) +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/collision/PxCollisionDefs.h b/src/PhysX/physx/include/collision/PxCollisionDefs.h new file mode 100644 index 000000000..276954912 --- /dev/null +++ b/src/PhysX/physx/include/collision/PxCollisionDefs.h @@ -0,0 +1,85 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_DEFS_H +#define PX_COLLISION_DEFS_H + +#include "PxPhysXConfig.h" +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "geomutils/GuContactPoint.h" +#include "geomutils/GuContactBuffer.h" +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief A structure to cache contact information produced by LL contact gen functions. + */ + struct PxCache + { + PxU8* mCachedData; //!< Cached data pointer. Allocated via PxCacheAllocator + PxU16 mCachedSize; //!< The total size of the cached data + PxU8 mPairData; //!< Pair data information used and cached internally by some contact gen functions to accelerate performance. + PxU8 mManifoldFlags; //!< Manifold flags used to identify the format the cached data is stored in. + + PxCache() : mCachedData(NULL), mCachedSize(0), mPairData(0), mManifoldFlags(0) + { + } + }; + + + /** + A callback class to allocate memory to cache information used in contact generation. + */ + class PxCacheAllocator + { + public: + /** + \brief Allocates cache data for contact generation. This data is stored inside PxCache objects. The application can retain and provide this information for future contact generation passes + for a given pair to improve contact generation performance. It is the application's responsibility to release this memory appropriately. If the memory is released, the application must ensure that + this memory is no longer referenced by any PxCache objects passed to PxGenerateContacts. + \param byteSize The size of the allocation in bytes + \return the newly-allocated memory. The returned address must be 16-byte aligned. + */ + virtual PxU8* allocateCacheData(const PxU32 byteSize) = 0; + + virtual ~PxCacheAllocator() {} + }; + +#if !PX_DOXYGEN +} +#endif + +#endif + diff --git a/src/PhysX/physx/include/common/PxBase.h b/src/PhysX/physx/include/common/PxBase.h new file mode 100644 index 000000000..848fe91cb --- /dev/null +++ b/src/PhysX/physx/include/common/PxBase.h @@ -0,0 +1,201 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_PX_BASE +#define PX_PHYSICS_PX_BASE + +/** \addtogroup common +@{ +*/ + +#include "PxSerialFramework.h" +#include "PxCollection.h" +#include "common/PxTypeInfo.h" +#include "foundation/PxFlags.h" +#include // For strcmp + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +typedef PxU16 PxType; + +/** +\brief Flags for PxBase. +*/ +struct PxBaseFlag +{ + enum Enum + { + eOWNS_MEMORY = (1<<0), + eIS_RELEASABLE = (1<<1) + }; +}; + +typedef PxFlags PxBaseFlags; +PX_FLAGS_OPERATORS(PxBaseFlag::Enum, PxU16) + +/** +\brief Base class for objects that can be members of a PxCollection. + +All PxBase sub-classes can be serialized. + +@see PxCollection +*/ +class PxBase +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief Releases the PxBase instance, please check documentation of release in derived class. + */ + virtual void release() = 0; + + /** + \brief Returns string name of dynamic type. + \return Class name of most derived type of this object. + */ + virtual const char* getConcreteTypeName() const = 0; + + /* brief Implements dynamic cast functionality. + + Example use: + + if(actor->is()) {...} + + \return A pointer to the specified type if object matches, otherwise NULL + */ + template T* is() { return typeMatch() ? static_cast(this) : NULL; } + + /* brief Implements dynamic cast functionality for const objects. + + Example use: + + if(actor->is()) {...} + + \return A pointer to the specified type if object matches, otherwise NULL + */ + template const T* is() const { return typeMatch() ? static_cast(this) : NULL; } + + /** + \brief Returns concrete type of object. + \return PxConcreteType::Enum of serialized object + + @see PxConcreteType + */ + PX_FORCE_INLINE PxType getConcreteType() const { return mConcreteType; } + + /** + \brief Set PxBaseFlag + + \param[in] flag The flag to be set + \param[in] value The flags new value + */ + PX_FORCE_INLINE void setBaseFlag(PxBaseFlag::Enum flag, bool value) { mBaseFlags = value ? mBaseFlags|flag : mBaseFlags&~flag; } + + /** + \brief Set PxBaseFlags + + \param[in] inFlags The flags to be set + + @see PxBaseFlags + */ + PX_FORCE_INLINE void setBaseFlags(PxBaseFlags inFlags) { mBaseFlags = inFlags; } + + /** + \brief Returns PxBaseFlags + + \return PxBaseFlags + + @see PxBaseFlags + */ + PX_FORCE_INLINE PxBaseFlags getBaseFlags() const { return mBaseFlags; } + + /** + \brief Whether the object is subordinate. + + A class is subordinate, if it can only be instantiated in the context of another class. + + \return Whether the class is subordinate + + @see PxSerialization::isSerializable + */ + virtual bool isReleasable() const { return mBaseFlags & PxBaseFlag::eIS_RELEASABLE; } + +protected: + /** + \brief Constructor setting concrete type and base flags. + */ + PX_INLINE PxBase(PxType concreteType, PxBaseFlags baseFlags) + : mConcreteType(concreteType), mBaseFlags(baseFlags) {} + + /** + \brief Deserialization constructor setting base flags. + */ + PX_INLINE PxBase(PxBaseFlags baseFlags) : mBaseFlags(baseFlags) {} + + /** + \brief Destructor. + */ + virtual ~PxBase() {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* superClass) const { return !::strcmp(superClass, "PxBase"); } + + template bool typeMatch() const + { + return PxU32(PxTypeInfo::eFastTypeId)!=PxU32(PxConcreteType::eUNDEFINED) ? + PxU32(getConcreteType()) == PxU32(PxTypeInfo::eFastTypeId) : isKindOf(PxTypeInfo::name()); + } + + +private: + friend void getBinaryMetaData_PxBase(PxOutputStream& stream); + +protected: + PxType mConcreteType; // concrete type identifier - see PxConcreteType. + PxBaseFlags mBaseFlags; // internal flags + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxCollection.h b/src/PhysX/physx/include/common/PxCollection.h new file mode 100644 index 000000000..904055445 --- /dev/null +++ b/src/PhysX/physx/include/common/PxCollection.h @@ -0,0 +1,279 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_PX_COLLECTION +#define PX_PHYSICS_PX_COLLECTION + +#include "PxSerialFramework.h" + +/** \addtogroup common +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxBase; + +/** +\brief Collection class for serialization. + +A collection is a set of PxBase objects. PxBase objects can be added to the collection +regardless of other objects they depend on. Objects may be named using PxSerialObjectId values in order +to resolve dependencies between objects of different collections. + +Serialization and deserialization only work through collections. + +A scene is typically serialized using the following steps: + + -# create a serialization registry + -# create a collection for scene objects + -# complete the scene objects (adds all dependent objects, e.g. meshes) + -# serialize collection + -# release collection + -# release serialization registry + +For example the code may look like this: + +\code + PxPhysics* physics; // The physics + PxScene* scene; // The physics scene + SerialStream s; // The user-defined stream doing the actual write to disk + + PxSerializationRegistry* registry = PxSerialization::createSerializationRegistry(*physics); // step 1) + PxCollection* collection = PxSerialization::createCollection(*scene); // step 2) + PxSerialization::complete(*collection, *registry); // step 3) + PxSerialization::serializeCollectionToBinary(s, *collection, *registry); // step 4) + collection->release(); // step 5) + registry->release(); // step 6) +\endcode + +A scene is typically deserialized using the following steps: + + -# load a serialized collection into memory + -# create a serialization registry + -# create a collection by passing the serialized memory block + -# add collected objects to scene + -# release collection + -# release serialization registry + +For example the code may look like this: + +\code + PxPhysics* physics; // The physics + PxScene* scene; // The physics scene + void* memory128; // a 128-byte aligned buffer previously loaded from disk by the user - step 1) + + PxSerializationRegistry* registry = PxSerialization::createSerializationRegistry(*physics); // step 2) + PxCollection* collection = PxSerialization::createCollectionFromBinary(memory128, *registry); // step 3) + scene->addCollection(*collection); // step 4) + collection->release(); // step 5) + registry->release(); // step 6) +\endcode + +@see PxBase, PxCreateCollection() +*/ +class PxCollection +{ +public: + + /** + \brief Adds a PxBase object to the collection. + + Adds a PxBase object to the collection. Optionally a PxSerialObjectId can be provided + in order to resolve dependencies between collections. A PxSerialObjectId value of PX_SERIAL_OBJECT_ID_INVALID + means the object remains without id. Objects can be added regardless of other objects they require. If the object + is already in the collection, the ID will be set if it was PX_SERIAL_OBJECT_ID_INVALID previously, otherwise the + operation fails. + + + \param[in] object Object to be added to the collection + \param[in] id Optional PxSerialObjectId id + */ + virtual void add(PxBase& object, PxSerialObjectId id = PX_SERIAL_OBJECT_ID_INVALID) = 0; + + /** + \brief Removes a PxBase member object from the collection. + + Object needs to be contained by the collection. + + \param[in] object PxBase object to be removed + */ + virtual void remove(PxBase& object) = 0; + + /** + \brief Returns whether the collection contains a certain PxBase object. + + \param[in] object PxBase object + \return Whether object is contained. + */ + virtual bool contains(PxBase& object) const = 0; + + /** + \brief Adds an id to a member PxBase object. + + If the object is already associated with an id within the collection, the id is replaced. + May only be called for objects that are members of the collection. The id needs to be unique + within the collection. + + \param[in] object Member PxBase object + \param[in] id PxSerialObjectId id to be given to the object + */ + virtual void addId(PxBase& object, PxSerialObjectId id) = 0; + + /** + \brief Removes id from a contained PxBase object. + + May only be called for ids that are associated with an object in the collection. + + \param[in] id PxSerialObjectId value + */ + virtual void removeId(PxSerialObjectId id) = 0; + + /** + \brief Adds all PxBase objects and their ids of collection to this collection. + + PxBase objects already in this collection are ignored. Object ids need to be conflict + free, i.e. the same object may not have two different ids within the two collections. + + \param[in] collection Collection to be added + */ + virtual void add(PxCollection& collection) = 0; + + /** + \brief Removes all PxBase objects of collection from this collection. + + PxBase objects not present in this collection are ignored. Ids of objects + which are removed are also removed. + + \param[in] collection Collection to be removed + */ + virtual void remove(PxCollection& collection) = 0; + + /** + \brief Gets number of PxBase objects in this collection. + + \return Number of objects in this collection + */ + virtual PxU32 getNbObjects() const = 0; + + /** + \brief Gets the PxBase object of this collection given its index. + + \param[in] index PxBase index in [0, getNbObjects()) + \return PxBase object at index index + */ + virtual PxBase& getObject(PxU32 index) const = 0; + + /** + \brief Copies member PxBase pointers to a user specified buffer. + + \param[out] userBuffer Array of PxBase pointers + \param[in] bufferSize Capacity of userBuffer + \param[in] startIndex Offset into list of member PxBase objects + \return number of members PxBase objects that have been written to the userBuffer + */ + virtual PxU32 getObjects(PxBase** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Looks for a PxBase object given a PxSerialObjectId value. + + If there is no PxBase object in the collection with the given id, NULL is returned. + + \param[in] id PxSerialObjectId value to look for + \return PxBase object with the given id value or NULL + */ + virtual PxBase* find(PxSerialObjectId id) const = 0; + + /** + \brief Gets number of PxSerialObjectId names in this collection. + + \return Number of PxSerialObjectId names in this collection + */ + virtual PxU32 getNbIds() const = 0; + + /** + \brief Copies member PxSerialObjectId values to a user specified buffer. + + \param[out] userBuffer Array of PxSerialObjectId values + \param[in] bufferSize Capacity of userBuffer + \param[in] startIndex Offset into list of member PxSerialObjectId values + \return number of members PxSerialObjectId values that have been written to the userBuffer + */ + virtual PxU32 getIds(PxSerialObjectId* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Gets the PxSerialObjectId name of a PxBase object within the collection. + + The PxBase object needs to be a member of the collection. + + \param[in] object PxBase object to get id for + \return PxSerialObjectId name of the object or PX_SERIAL_OBJECT_ID_INVALID if the object is unnamed + */ + virtual PxSerialObjectId getId(const PxBase& object) const = 0; + + /** + \brief Deletes a collection object. + + This function only deletes the collection object, i.e. the container class. It doesn't delete objects + that are part of the collection. + + @see PxCreateCollection() + */ + + virtual void release() = 0; + +protected: + PxCollection() {} + virtual ~PxCollection() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** +\brief Creates a collection object. + +Objects can only be serialized or deserialized through a collection. +For serialization, users must add objects to the collection and serialize the collection as a whole. +For deserialization, the system gives back a collection of deserialized objects to users. + +\return The new collection object. + +@see PxCollection, PxCollection::release() +*/ +PX_PHYSX_COMMON_API physx::PxCollection* PX_CALL_CONV PxCreateCollection(); + + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxCoreUtilityTypes.h b/src/PhysX/physx/include/common/PxCoreUtilityTypes.h new file mode 100644 index 000000000..57a475aa9 --- /dev/null +++ b/src/PhysX/physx/include/common/PxCoreUtilityTypes.h @@ -0,0 +1,211 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CORE_UTILTY_TYPES_H +#define PX_CORE_UTILTY_TYPES_H +/** \addtogroup common +@{ +*/ + +#include "foundation/PxAssert.h" +#include "foundation/PxMemory.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + +struct PxStridedData +{ + /** + \brief The offset in bytes between consecutive samples in the data. + + Default: 0 + */ + PxU32 stride; + const void* data; + + PxStridedData() : stride( 0 ), data( NULL ) {} + + template + PX_INLINE const TDataType& at( PxU32 idx ) const + { + PxU32 theStride( stride ); + if ( theStride == 0 ) + theStride = sizeof( TDataType ); + PxU32 offset( theStride * idx ); + return *(reinterpret_cast( reinterpret_cast< const PxU8* >( data ) + offset )); + } +}; + +template +struct PxTypedStridedData +{ + PxU32 stride; + const TDataType* data; + + PxTypedStridedData() + : stride( 0 ) + , data( NULL ) + { + } + +}; + +struct PxBoundedData : public PxStridedData +{ + PxU32 count; + PxBoundedData() : count( 0 ) {} +}; + +template +struct PxPadding +{ + PxU8 mPadding[TNumBytes]; + PxPadding() + { + for ( PxU8 idx =0; idx < TNumBytes; ++idx ) + mPadding[idx] = 0; + } +}; + +template class PxFixedSizeLookupTable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + PxFixedSizeLookupTable() + : mNbDataPairs(0) + { + } + + PxFixedSizeLookupTable(const PxEMPTY) {} + + PxFixedSizeLookupTable(const PxReal* dataPairs, const PxU32 numDataPairs) + { + PxMemCopy(mDataPairs,dataPairs,sizeof(PxReal)*2*numDataPairs); + mNbDataPairs=numDataPairs; + } + + PxFixedSizeLookupTable(const PxFixedSizeLookupTable& src) + { + PxMemCopy(mDataPairs,src.mDataPairs,sizeof(PxReal)*2*src.mNbDataPairs); + mNbDataPairs=src.mNbDataPairs; + } + + ~PxFixedSizeLookupTable() + { + } + + PxFixedSizeLookupTable& operator=(const PxFixedSizeLookupTable& src) + { + PxMemCopy(mDataPairs,src.mDataPairs,sizeof(PxReal)*2*src.mNbDataPairs); + mNbDataPairs=src.mNbDataPairs; + return *this; + } + + PX_FORCE_INLINE void addPair(const PxReal x, const PxReal y) + { + PX_ASSERT(mNbDataPairs=x0)&&(x=getX(mNbDataPairs-1)); + return getY(mNbDataPairs-1); + } + + PxU32 getNbDataPairs() const {return mNbDataPairs;} + + void clear() + { + memset(mDataPairs, 0, NB_ELEMENTS*2*sizeof(PxReal)); + mNbDataPairs = 0; + } + + PX_FORCE_INLINE PxReal getX(const PxU32 i) const + { + return mDataPairs[2*i]; + } + PX_FORCE_INLINE PxReal getY(const PxU32 i) const + { + return mDataPairs[2*i+1]; + } + + PxReal mDataPairs[2*NB_ELEMENTS]; + PxU32 mNbDataPairs; + PxU32 mPad[3]; + + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxMetaData.h b/src/PhysX/physx/include/common/PxMetaData.h new file mode 100644 index 000000000..c1a050fc4 --- /dev/null +++ b/src/PhysX/physx/include/common/PxMetaData.h @@ -0,0 +1,228 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_METADATA_H +#define PX_PHYSICS_METADATA_H +/** \addtogroup physics +@{ +*/ + +#include "foundation/Px.h" +#include "foundation/PxIO.h" +#include "PxMetaDataFlags.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief Struct to store meta data definitions. + + Note: The individual fields have different meaning depending on the meta data entry configuration. + */ + struct PxMetaDataEntry + { + const char* type; //!< Field type (bool, byte, quaternion, etc) + const char* name; //!< Field name (appears exactly as in the source file) + PxU32 offset; //!< Offset from the start of the class (ie from "this", field is located at "this"+Offset) + PxU32 size; //!< sizeof(Type) + PxU32 count; //!< Number of items of type Type (0 for dynamic sizes) + PxU32 offsetSize; //!< Offset of dynamic size param, for dynamic arrays + PxU32 flags; //!< Field parameters + PxU32 alignment; //!< Explicit alignment + }; + + #define PX_STORE_METADATA(stream, metaData) stream.write(&metaData, sizeof(PxMetaDataEntry)) + + #define PX_SIZE_OF(Class, Member) sizeof((reinterpret_cast(0))->Member) + + /** + \brief specifies a binary metadata entry for a member variable of a class + */ + #define PX_DEF_BIN_METADATA_ITEM(stream, Class, type, name, flags) \ + { \ + PxMetaDataEntry tmp = { #type, #name, PxU32(PX_OFFSET_OF_RT(Class, name)), PX_SIZE_OF(Class, name), \ + 1, 0, flags, 0}; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a member array variable of a class + \details similar to PX_DEF_BIN_METADATA_ITEMS_AUTO but for cases with mismatch between specified type and array type + */ + #define PX_DEF_BIN_METADATA_ITEMS(stream, Class, type, name, flags, count) \ + { \ + PxMetaDataEntry tmp = { #type, #name, PxU32(PX_OFFSET_OF_RT(Class, name)), PX_SIZE_OF(Class, name), \ + count, 0, flags, 0}; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a member array variable of a class + \details similar to PX_DEF_BIN_METADATA_ITEMS but automatically detects the array length, which only works when the specified + type matches the type of the array - does not support PxMetaDataFlag::ePTR + */ + #define PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Class, type, name, flags) \ + { \ + PxMetaDataEntry tmp = { #type, #name, PxU32(PX_OFFSET_OF_RT(Class, name)), PX_SIZE_OF(Class, name), \ + sizeof((reinterpret_cast(0))->name)/sizeof(type), 0, flags, 0}; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a class + */ + #define PX_DEF_BIN_METADATA_CLASS(stream, Class) \ + { \ + PxMetaDataEntry tmp = { #Class, 0, 0, sizeof(Class), 0, 0, PxMetaDataFlag::eCLASS, 0 }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a virtual class + */ + #define PX_DEF_BIN_METADATA_VCLASS(stream, Class) \ + { \ + PxMetaDataEntry tmp = { #Class, 0, 0, sizeof(Class), 0, 0, PxMetaDataFlag::eCLASS|PxMetaDataFlag::eVIRTUAL, 0}; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a typedef + */ + #define PX_DEF_BIN_METADATA_TYPEDEF(stream, newType, oldType) \ + { \ + PxMetaDataEntry tmp = { #newType, #oldType, 0, 0, 0, 0, PxMetaDataFlag::eTYPEDEF, 0 }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for declaring a base class + */ + #define PX_DEF_BIN_METADATA_BASE_CLASS(stream, Class, BaseClass) \ + { \ + Class* myClass = reinterpret_cast(42); \ + BaseClass* s = static_cast(myClass); \ + const PxU32 offset = PxU32(size_t(s) - size_t(myClass)); \ + PxMetaDataEntry tmp = { #Class, #BaseClass, offset, sizeof(Class), 0, 0, PxMetaDataFlag::eCLASS, 0 }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a union + */ + #define PX_DEF_BIN_METADATA_UNION(stream, Class, name) \ + { \ + PxMetaDataEntry tmp = { #Class, 0, PxU32(PX_OFFSET_OF_RT(Class, name)), PX_SIZE_OF(Class, name), \ + 1, 0, PxMetaDataFlag::eUNION, 0 }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a particular member type of a union + */ + #define PX_DEF_BIN_METADATA_UNION_TYPE(stream, Class, type, enumValue) \ + { \ + PxMetaDataEntry tmp = { #Class, #type, enumValue, 0, 0, 0, PxMetaDataFlag::eUNION, 0 }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for extra data + */ + #define PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, Class, type, control, align) \ + { \ + PxMetaDataEntry tmp = { #type, 0, PxU32(PX_OFFSET_OF_RT(Class, control)), sizeof(type), 0, PxU32(PX_SIZE_OF(Class, control)), \ + PxMetaDataFlag::eEXTRA_DATA|PxMetaDataFlag::eEXTRA_ITEM, align }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for an array of extra data + */ + #define PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, Class, type, control, count, flags, align) \ + { \ + PxMetaDataEntry tmp = { #type, 0, PxU32(PX_OFFSET_OF_RT(Class, control)), PxU32(PX_SIZE_OF(Class, control)), \ + PxU32(PX_OFFSET_OF_RT(Class, count)), PxU32(PX_SIZE_OF(Class, count)), \ + PxMetaDataFlag::eEXTRA_DATA|PxMetaDataFlag::eEXTRA_ITEMS|flags, align }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for an array of extra data + additional to PX_DEF_BIN_METADATA_EXTRA_ITEMS a mask can be specified to interpret the control value + @see PxMetaDataFlag::eCONTROL_MASK + */ + #define PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, Class, type, control, controlMask ,count, flags, align) \ + { \ + PxMetaDataEntry tmp = { #type, 0, PxU32(PX_OFFSET_OF_RT(Class, control)), PxU32(PX_SIZE_OF(Class, control)), \ + PxU32(PX_OFFSET_OF_RT(Class, count)), PxU32(PX_SIZE_OF(Class, count)), \ + PxMetaDataFlag::eCONTROL_MASK|PxMetaDataFlag::eEXTRA_DATA|PxMetaDataFlag::eEXTRA_ITEMS|flags|(controlMask & PxMetaDataFlag::eCONTROL_MASK_RANGE) << 16, \ + align}; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for an array of extra data + \details similar to PX_DEF_BIN_METADATA_EXTRA_ITEMS, but supporting no control - PxMetaDataFlag::ePTR is also not supported + */ + #define PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Class, type, dyn_count, align, flags) \ + { \ + PxMetaDataEntry tmp = { #type, 0, PxU32(PX_OFFSET_OF_RT(Class, dyn_count)), PX_SIZE_OF(Class, dyn_count), align, 0, \ + PxMetaDataFlag::eEXTRA_DATA|flags, align }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for an string of extra data + */ + #define PX_DEF_BIN_METADATA_EXTRA_NAME(stream, Class, control, align) \ + { \ + PxMetaDataEntry tmp = { "char", "string", 0, 0, 0, 0, PxMetaDataFlag::eEXTRA_DATA|PxMetaDataFlag::eEXTRA_NAME, align }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry declaring an extra data alignment for a class + */ + #define PX_DEF_BIN_METADATA_EXTRA_ALIGN(stream, Class, align) \ + { \ + PxMetaDataEntry tmp = { "PxU8", "Alignment", 0, 0, 0, 0, PxMetaDataFlag::eEXTRA_DATA|PxMetaDataFlag::eALIGNMENT, align}; \ + PX_STORE_METADATA(stream, tmp); \ + } + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxMetaDataFlags.h b/src/PhysX/physx/include/common/PxMetaDataFlags.h new file mode 100644 index 000000000..7c890f9e0 --- /dev/null +++ b/src/PhysX/physx/include/common/PxMetaDataFlags.h @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_METADATA_FLAGS +#define PX_PHYSICS_METADATA_FLAGS + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief Flags used to configure binary meta data entries, typically set through PX_DEF_BIN_METADATA defines. + + @see PxMetaDataEntry + */ + struct PxMetaDataFlag + { + enum Enum + { + eCLASS = (1<<0), //!< declares a class + eVIRTUAL = (1<<1), //!< declares class to be virtual + eTYPEDEF = (1<<2), //!< declares a typedef + ePTR = (1<<3), //!< declares a pointer + eEXTRA_DATA = (1<<4), //!< declares extra data exported with PxSerializer::exportExtraData + eEXTRA_ITEM = (1<<5), //!< specifies one element of extra data + eEXTRA_ITEMS = (1<<6), //!< specifies an array of extra data + eEXTRA_NAME = (1<<7), //!< specifies a name of extra data + eUNION = (1<<8), //!< declares a union + ePADDING = (1<<9), //!< declares explicit padding data + eALIGNMENT = (1<<10), //!< declares aligned data + eCOUNT_MASK_MSB = (1<<11), //!< specifies that the count value's most significant bit needs to be masked out + eCOUNT_SKIP_IF_ONE = (1<<12), //!< specifies that the count value is treated as zero for a variable value of one - special case for single triangle meshes + eCONTROL_FLIP = (1<<13), //!< specifies that the control value is the negate of the variable value + eCONTROL_MASK = (1<<14), //!< specifies that the control value is masked - mask bits are assumed to be within eCONTROL_MASK_RANGE + eCONTROL_MASK_RANGE = 0x000000FF, //!< mask range allowed for eCONTROL_MASK + eFORCE_DWORD = 0x7fffffff + }; + }; + +#if !PX_DOXYGEN +} +#endif + +#endif diff --git a/src/PhysX/physx/include/common/PxPhysXCommonConfig.h b/src/PhysX/physx/include/common/PxPhysXCommonConfig.h new file mode 100644 index 000000000..61e2d8754 --- /dev/null +++ b/src/PhysX/physx/include/common/PxPhysXCommonConfig.h @@ -0,0 +1,124 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_NX +#define PX_PHYSICS_COMMON_NX + +/** \addtogroup common +@{ */ + +#include "foundation/Px.h" + +/*Disable support for VS2017 prior version 15.5.1 for windows platform, because of a compiler bug: +https://developercommunity.visualstudio.com/content/problem/66047/possible-compiler-bug.html +*/ +#if (PX_VC == 15) && PX_WINDOWS && (_MSC_FULL_VER < 191225830) +#error Visual studio 2017 prior to 15.5.1 is not supported because of a compiler bug. +#endif + +// define API function declaration (public API only needed because of extensions) +#if defined PX_PHYSX_STATIC_LIB || defined PX_PHYSX_CORE_STATIC_LIB + #define PX_PHYSX_CORE_API +#else + #if PX_WINDOWS + #if defined PX_PHYSX_CORE_EXPORTS + #define PX_PHYSX_CORE_API __declspec(dllexport) + #else + #define PX_PHYSX_CORE_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_PHYSX_CORE_API PX_UNIX_EXPORT + #else + #define PX_PHYSX_CORE_API + #endif +#endif + +#if PX_SUPPORT_GPU_PHYSX +// define API function declaration +#if defined PX_PHYSX_GPU_STATIC + #define PX_PHYSX_GPU_API +#else + #if PX_WINDOWS + #if defined PX_PHYSX_GPU_EXPORTS + #define PX_PHYSX_GPU_API __declspec(dllexport) + #else + #define PX_PHYSX_GPU_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_PHYSX_GPU_API PX_UNIX_EXPORT + #else + #define PX_PHYSX_GPU_API + #endif +#endif + +#else // PX_SUPPORT_GPU_PHYSX +#define PX_PHYSX_GPU_API +#endif // PX_SUPPORT_GPU_PHYSX + +#if defined PX_PHYSX_STATIC_LIB || defined PX_PHYSX_CORE_STATIC_LIB + #define PX_PHYSX_COMMON_API +#else + #if PX_WINDOWS && !defined(__CUDACC__) + #if defined PX_PHYSX_COMMON_EXPORTS + #define PX_PHYSX_COMMON_API __declspec(dllexport) + #else + #define PX_PHYSX_COMMON_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_PHYSX_COMMON_API PX_UNIX_EXPORT + #else + #define PX_PHYSX_COMMON_API + #endif +#endif + +// Changing these parameters requires recompilation of the SDK + +#if !PX_DOXYGEN +namespace physx +{ +#endif + class PxCollection; + class PxBase; + + class PxHeightField; + class PxHeightFieldDesc; + + class PxTriangleMesh; + class PxConvexMesh; + + typedef PxU32 PxTriangleID; + typedef PxU16 PxMaterialTableIndex; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h b/src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h new file mode 100644 index 000000000..414695259 --- /dev/null +++ b/src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h @@ -0,0 +1,84 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_PX_PHYSICS_INSERTION_CALLBACK +#define PX_PHYSICS_PX_PHYSICS_INSERTION_CALLBACK + +#include "PxBase.h" + +/** \addtogroup common +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + + \brief Callback interface that permits PxCooking to insert a + TriangleMesh, HeightfieldMesh or ConvexMesh directly into PxPhysics without the need to store + the cooking results into a stream. + + + Using this is advised only if real-time cooking is required; using "offline" cooking and + streams is otherwise preferred. + + The default PxPhysicsInsertionCallback implementation must be used. The PxPhysics + default callback can be obtained using the PxPhysics::getPhysicsInsertionCallback(). + + @see PxCooking PxPhysics + */ + class PxPhysicsInsertionCallback + { + public: + PxPhysicsInsertionCallback() {} + + /** + \brief Builds object (TriangleMesh, HeightfieldMesh or ConvexMesh) from given data in PxPhysics. + + \param type Object type to build. + \param data Object data + \return PxBase Created object in PxPhysics. + */ + virtual PxBase* buildObjectFromData(PxConcreteType::Enum type, void* data) = 0; + + protected: + virtual ~PxPhysicsInsertionCallback() {} + }; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxProfileZone.h b/src/PhysX/physx/include/common/PxProfileZone.h new file mode 100644 index 000000000..0fda159f1 --- /dev/null +++ b/src/PhysX/physx/include/common/PxProfileZone.h @@ -0,0 +1,51 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXFOUNDATION_PXPROFILEZONE_H +#define PXFOUNDATION_PXPROFILEZONE_H + +#include "foundation/PxProfiler.h" +#include "PxFoundation.h" + +#if PX_DEBUG || PX_CHECKED || PX_PROFILE + #define PX_PROFILE_ZONE(x, y) \ + physx::PxProfileScoped PX_CONCAT(_scoped, __LINE__)(PxGetProfilerCallback(), x, false, y) + #define PX_PROFILE_START_CROSSTHREAD(x, y) \ + if(PxGetProfilerCallback()) \ + PxGetProfilerCallback()->zoneStart(x, true, y) + #define PX_PROFILE_STOP_CROSSTHREAD(x, y) \ + if(PxGetProfilerCallback()) \ + PxGetProfilerCallback()->zoneEnd(NULL, x, true, y) +#else + #define PX_PROFILE_ZONE(x, y) + #define PX_PROFILE_START_CROSSTHREAD(x, y) + #define PX_PROFILE_STOP_CROSSTHREAD(x, y) +#endif + +#define PX_PROFILE_POINTER_TO_U64(pointer) static_cast(reinterpret_cast(pointer)) + +#endif // PXFOUNDATION_PXPROFILEZONE_H diff --git a/src/PhysX/physx/include/common/PxRenderBuffer.h b/src/PhysX/physx/include/common/PxRenderBuffer.h new file mode 100644 index 000000000..8fe0b0fa1 --- /dev/null +++ b/src/PhysX/physx/include/common/PxRenderBuffer.h @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_FOUNDATION_PXRENDERBUFFER_H +#define PX_FOUNDATION_PXRENDERBUFFER_H + +/** \addtogroup common +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "foundation/PxBounds3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Default color values used for debug rendering. +*/ +struct PxDebugColor +{ + enum Enum + { + eARGB_BLACK = 0xff000000, + eARGB_RED = 0xffff0000, + eARGB_GREEN = 0xff00ff00, + eARGB_BLUE = 0xff0000ff, + eARGB_YELLOW = 0xffffff00, + eARGB_MAGENTA = 0xffff00ff, + eARGB_CYAN = 0xff00ffff, + eARGB_WHITE = 0xffffffff, + eARGB_GREY = 0xff808080, + eARGB_DARKRED = 0x88880000, + eARGB_DARKGREEN = 0x88008800, + eARGB_DARKBLUE = 0x88000088 + }; +}; + +/** +\brief Used to store a single point and colour for debug rendering. +*/ +struct PxDebugPoint +{ + PxDebugPoint(const PxVec3& p, const PxU32& c) + : pos(p), color(c) {} + + PxVec3 pos; + PxU32 color; +}; + +/** +\brief Used to store a single line and colour for debug rendering. +*/ +struct PxDebugLine +{ + PxDebugLine(const PxVec3& p0, const PxVec3& p1, const PxU32& c) + : pos0(p0), color0(c), pos1(p1), color1(c) {} + + PxVec3 pos0; + PxU32 color0; + PxVec3 pos1; + PxU32 color1; +}; + +/** +\brief Used to store a single triangle and colour for debug rendering. +*/ +struct PxDebugTriangle +{ + PxDebugTriangle(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxU32& c) + : pos0(p0), color0(c), pos1(p1), color1(c), pos2(p2), color2(c) {} + + PxVec3 pos0; + PxU32 color0; + PxVec3 pos1; + PxU32 color1; + PxVec3 pos2; + PxU32 color2; +}; + +/** +\brief Used to store a text for debug rendering. Doesn't own 'string' array. +*/ +struct PxDebugText +{ + PxDebugText() : string(0) {} + + PxDebugText(const PxVec3& p, const PxReal& s, const PxU32& c, const char* str) + : position(p), size(s), color(c), string(str) {} + + PxVec3 position; + PxReal size; + PxU32 color; + const char* string; +}; + +/** +\brief Interface for points, lines, triangles, and text buffer. +*/ +class PxRenderBuffer +{ +public: + virtual ~PxRenderBuffer() {} + + virtual PxU32 getNbPoints() const = 0; + virtual const PxDebugPoint* getPoints() const = 0; + + virtual PxU32 getNbLines() const = 0; + virtual const PxDebugLine* getLines() const = 0; + + virtual PxU32 getNbTriangles() const = 0; + virtual const PxDebugTriangle* getTriangles() const = 0; + + virtual PxU32 getNbTexts() const = 0; + virtual const PxDebugText* getTexts() const = 0; + + virtual void append(const PxRenderBuffer& other) = 0; + virtual void clear() = 0; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxSerialFramework.h b/src/PhysX/physx/include/common/PxSerialFramework.h new file mode 100644 index 000000000..ac2565a7b --- /dev/null +++ b/src/PhysX/physx/include/common/PxSerialFramework.h @@ -0,0 +1,406 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_NX_SERIAL_FRAMEWORK +#define PX_PHYSICS_COMMON_NX_SERIAL_FRAMEWORK + +/** \addtogroup common +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +typedef PxU16 PxType; +class PxBase; +class PxSerializationContext; +class PxRepXSerializer; +class PxSerializer; +class PxPhysics; + +//! Default serialization alignment +#define PX_SERIAL_ALIGN 16 + +//! Serialized input data must be aligned to this value +#define PX_SERIAL_FILE_ALIGN 128 + +//! PxSerialObjectId value for objects that do not have an ID +#define PX_SERIAL_OBJECT_ID_INVALID 0 + +//! ID type for PxBase objects in a PxCollection +typedef PxU64 PxSerialObjectId; + +//! Bit to mark pointer type references, @see PxDeserializationContext +#define PX_SERIAL_REF_KIND_PTR_TYPE_BIT (1u<<31) + +//! Reference kind value for PxBase objects +#define PX_SERIAL_REF_KIND_PXBASE (0 | PX_SERIAL_REF_KIND_PTR_TYPE_BIT) + +//! Reference kind value for material indices +#define PX_SERIAL_REF_KIND_MATERIAL_IDX (1) + +//! Used to fix multi-byte characters warning from gcc for situations like: PxU32 foo = 'CCTS'; +#define PX_MAKE_FOURCC(a, b, c, d) ( (a) | ((b)<<8) | ((c)<<16) | ((d)<<24) ) + +/** +\brief Callback class used to process PxBase objects. + +@see PxSerializer::requires +*/ +class PxProcessPxBaseCallback +{ +public: + virtual ~PxProcessPxBaseCallback() {} + virtual void process(PxBase&) = 0; +}; + + +/** +\brief Binary serialization context class. + +This class is used to register reference values and write object +and object extra data during serialization. +It is mainly used by the serialization framework. Except for custom +serializable types, users should not have to worry about it. + +@see PxDeserializationContext +*/ +class PxSerializationContext +{ +public: + + /** + \brief Registers a reference value corresponding to a PxBase object. + + This method is assumed to be called in the implementation of PxSerializer::registerReferences for serialized + references that need to be resolved on deserialization. + + A reference needs to be associated with exactly one PxBase object in either the collection or the + external references collection. + + Different kinds of references are supported and need to be specified. In the most common case + (PX_SERIAL_REF_KIND_PXBASE) the PxBase object matches the reference value (which is the pointer + to the PxBase object). Integer references maybe registered as well (used for internal material + indices with PX_SERIAL_REF_KIND_MATERIAL_IDX). Other kinds could be added with the restriction that + for pointer types the kind value needs to be marked with the PX_SERIAL_REF_KIND_PTR_TYPE_BIT. + + \param[in] base PxBase object associated with the reference + \param[in] kind What kind of reference this is (PX_SERIAL_REF_KIND_PXBASE, PX_SERIAL_REF_KIND_MATERIAL_IDX or custom kind) + \param[in] reference Value of reference + + @see PxDeserializationContext::resolveReference, PX_SERIAL_REF_KIND_PXBASE, PX_SERIAL_REF_KIND_MATERIAL_IDX, PxSerializer::registerReferences + */ + virtual void registerReference(PxBase& base, PxU32 kind, size_t reference) = 0; + + /** + \brief Returns the collection that is being serialized. + */ + virtual const PxCollection& getCollection() const = 0; + + /** + \brief Serializes object data and object extra data. + + This function is assumed to be called within the implementation of PxSerializer::exportData and PxSerializer::exportExtraData. + + @see PxSerializer::exportData, PxSerializer::exportExtraData, PxSerializer::createObject, PxDeserializationContext::readExtraData + */ + virtual void writeData(const void* data, PxU32 size) = 0; + + /** + \brief Aligns the serialized data. + + This function is assumed to be called within the implementation of PxSerializer::exportData and PxSerializer::exportExtraData. + + @see PxSerializer::exportData, PxSerializer::exportExtraData, PxDeserializationContext::alignExtraData + */ + virtual void alignData(PxU32 alignment = PX_SERIAL_ALIGN) = 0; + + /** + \brief Helper function to write a name to the extraData if serialization is configured to save names. + + This function is assumed to be called within the implementation of PxSerializer::exportExtraData. + + @see PxSerialization::serializeCollectionToBinary, PxDeserializationContext::readName + */ + virtual void writeName(const char* name) = 0; + +protected: + + PxSerializationContext() {} + virtual ~PxSerializationContext() {} +}; + + +/** +\brief Binary deserialization context class. + +This class is used to resolve references and access extra data during deserialization. +It is mainly used by the serialization framework. Except for custom +serializable types, users should not have to worry about it. + +@see PxSerializationContext +*/ +class PxDeserializationContext +{ +public: + + /** + \brief Retrieves a pointer to a deserialized PxBase object given a corresponding deserialized reference value + + This method is assumed to be called in the implementation of PxSerializer::createObject in order + to update reference values on deserialization. + + To update a PxBase reference the corresponding deserialized pointer value needs to be provided in order to retrieve + the location of the corresponding deserialized PxBase object. (PxDeserializationContext::translatePxBase simplifies + this common case). + + For other kinds of references the reverence values need to be updated by deduction given the corresponding PxBase instance. + + \param[in] kind What kind of reference this is (PX_SERIAL_REF_KIND_PXBASE, PX_SERIAL_REF_KIND_MATERIAL_IDX or custom kind) + \param[in] reference Deserialized reference value + \return PxBase object associated with the reference value + + @see PxSerializationContext::registerReference, PX_SERIAL_REF_KIND_PXBASE, PX_SERIAL_REF_KIND_MATERIAL_IDX, translatePxBase + */ + virtual PxBase* resolveReference(PxU32 kind, size_t reference) const = 0; + + /** + \brief Helper function to update PxBase pointer on deserialization + + @see resolveReference, PX_SERIAL_REF_KIND_PXBASE + */ + template + void translatePxBase(T*& base) { if (base) { base = static_cast(resolveReference(PX_SERIAL_REF_KIND_PXBASE, size_t(base))); } } + + /** + \brief Helper function to read a name from the extra data during deserialization. + + This function is assumed to be called within the implementation of PxSerializer::createObject. + + @see PxSerializationContext::writeName + */ + PX_INLINE void readName(const char*& name) + { + PxU32 len = *reinterpret_cast(mExtraDataAddress); + mExtraDataAddress += sizeof(len); + name = len ? reinterpret_cast(mExtraDataAddress) : NULL; + mExtraDataAddress += len; + } + + /** + \brief Function to read extra data during deserialization. + + This function is assumed to be called within the implementation of PxSerializer::createObject. + + @see PxSerializationContext::writeData, PxSerializer::createObject + */ + template + PX_INLINE T* readExtraData(PxU32 count=1) + { + T* data = reinterpret_cast(mExtraDataAddress); + mExtraDataAddress += sizeof(T)*count; + return data; + } + + /** + \brief Function to read extra data during deserialization optionally aligning the extra data stream before reading. + + This function is assumed to be called within the implementation of PxSerializer::createObject. + + @see PxSerializationContext::writeData, PxDeserializationContext::alignExtraData, PxSerializer::createObject + */ + template + PX_INLINE T* readExtraData(PxU32 count=1) + { + alignExtraData(alignment); + return readExtraData(count); + } + + /** + \brief Function to align the extra data stream to a power of 2 alignment + + This function is assumed to be called within the implementation of PxSerializer::createObject. + + @see PxSerializationContext::alignData, PxSerializer::createObject + */ + PX_INLINE void alignExtraData(PxU32 alignment = PX_SERIAL_ALIGN) + { + size_t addr = reinterpret_cast(mExtraDataAddress); + addr = (addr+alignment-1)&~size_t(alignment-1); + mExtraDataAddress = reinterpret_cast(addr); + } + + + /** + \brief Function to return the PX_PHYSX_VERSION value with which the data was originally serialized + */ + + virtual PxU32 getPhysXVersion() const = 0; + +protected: + + PxDeserializationContext() {} + virtual ~PxDeserializationContext() {} + + PxU8* mExtraDataAddress; +}; + +/** +\brief Callback type for exporting binary meta data for a serializable type. +@see PxSerializationRegistry::registerBinaryMetaDataCallback + +\param stream Stream to store binary meta data. +*/ +typedef void (*PxBinaryMetaDataCallback)(PxOutputStream& stream); + +/** +\brief Class serving as a registry for XML (RepX) and binary serializable types. + +In order to serialize and deserialize objects the application needs +to maintain an instance of this class. It can be created with +PxSerialization::createSerializationRegistry() and released with +PxSerializationRegistry::release(). + +@see PxSerialization::createSerializationRegistry +*/ +class PxSerializationRegistry +{ +public: + /************************************************************************************************/ + + /** @name Binary Serialization Functionality + */ + //@{ + + /** + \brief Register a serializer for a concrete type + + \param type PxConcreteType corresponding to the serializer + \param serializer The PxSerializer to be registered + + @see PxConcreteType, PxSerializer, PxSerializationRegistry::unregisterSerializer + */ + virtual void registerSerializer(PxType type, PxSerializer& serializer) = 0; + + /** + \brief Unregister a serializer for a concrete type, and retrieves the corresponding serializer object. + + \param type PxConcreteType for which the serializer should be unregistered + \return Unregistered serializer corresponding to type, NULL for types for which no serializer has been registered. + + @see PxConcreteType, PxSerializationRegistry::registerSerializer, PxSerializationRegistry::release + */ + virtual PxSerializer* unregisterSerializer(PxType type) = 0; + + /** + \brief Register binary meta data callback + + The callback is executed when calling PxSerialization::dumpBinaryMetaData. + + \param callback PxBinaryMetaDataCallback to be registered. + + @see PxBinaryMetaDataCallback, PxSerialization::dumpBinaryMetaData + */ + virtual void registerBinaryMetaDataCallback(PxBinaryMetaDataCallback callback) = 0; + + /** + \brief Returns PxSerializer corresponding to type + + \param type PxConcreteType of the serializer requested. + \return Registered PxSerializer object corresponding to type + + @see PxConcreteType + */ + virtual const PxSerializer* getSerializer(PxType type) const = 0; + + //@} + /************************************************************************************************/ + + /** @name RepX (XML) Serialization Functionality + */ + //@{ + + /** + \brief Register a RepX serializer for a concrete type + + \param type PxConcreteType corresponding to the RepX serializer + \param serializer The PxRepXSerializer to be registered + + @see PxConcreteType, PxRepXSerializer + */ + virtual void registerRepXSerializer(PxType type, PxRepXSerializer& serializer) = 0; + + /** + \brief Unregister a RepX serializer for a concrete type, and retrieves the corresponding serializer object. + + \param type PxConcreteType for which the RepX serializer should be unregistered + \return Unregistered PxRepxSerializer corresponding to type, NULL for types for which no RepX serializer has been registered. + + @see PxConcreteType, PxSerializationRegistry::registerRepXSerializer, PxSerializationRegistry::release + */ + virtual PxRepXSerializer* unregisterRepXSerializer(PxType type) = 0; + + /** + \brief Returns RepX serializer given the corresponding type name + + \param typeName Name of the type + \return Registered PxRepXSerializer object corresponding to type name + + @see PxRepXSerializer, PxTypeInfo, PX_DEFINE_TYPEINFO + */ + virtual PxRepXSerializer* getRepXSerializer(const char* typeName) const = 0; + + //@} + /************************************************************************************************/ + + /** + \brief Releases PxSerializationRegistry instance. + + This unregisters all PhysX and PhysXExtension serializers. Make sure to unregister all custom type + serializers before releasing the PxSerializationRegistry. + + @see PxSerializationRegistry::unregisterSerializer, PxSerializationRegistry::unregisterRepXSerializer + */ + virtual void release() = 0; + +protected: + virtual ~PxSerializationRegistry(){} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxSerializer.h b/src/PhysX/physx/include/common/PxSerializer.h new file mode 100644 index 000000000..d3c1327fc --- /dev/null +++ b/src/PhysX/physx/include/common/PxSerializer.h @@ -0,0 +1,257 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_SERIALIZER_H +#define PX_SERIALIZER_H +/** \addtogroup extensions +@{ +*/ + +#include "PxSerialFramework.h" +#include "PxCollection.h" +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** + \brief Serialization interface class. + + PxSerializer is used to extend serializable PxBase classes with serialization functionality. The + interface is structured such that per-class adapter instances can be used as opposed to per-object + adapter instances, avoiding per object allocations. Hence the methods take a reference to PxBase as a parameter. + + The PxSerializer interface needs to be implemented for binary or RepX serialization to work on custom + types. If only RepX serialization is needed, some methods can be left empty, as they are only needed + for binary serialization. + + A default implementation is available as a template adapter (PxSerializerDefaultAdapter). + + @see PxSerializerDefaultAdapter, PX_NEW_SERIALIZER_ADAPTER, PxSerializationRegistry::registerSerializer +*/ +class PxSerializer +{ +public: + + /**********************************************************************************************************************/ + + /** @name Basics needed for Binary- and RepX-Serialization + */ + //@{ + + /** + \brief Returns string name of dynamic type. + + \return Class name of most derived type of this object. + */ + virtual const char* getConcreteTypeName() const = 0; + + /** + \brief Adds required objects to the collection. + + This method does not add the required objects recursively, e.g. objects required by required objects. + + @see PxCollection, PxSerialization::complete + */ + virtual void requiresObjects(PxBase&, PxProcessPxBaseCallback&) const = 0; + + /** + \brief Whether the object is subordinate. + + A class is subordinate, if it can only be instantiated in the context of another class. + + \return Whether the class is subordinate + + @see PxSerialization::isSerializable + */ + virtual bool isSubordinate() const = 0; + + //@} + /**********************************************************************************************************************/ + + /**********************************************************************************************************************/ + + /** @name Functionality needed for Binary Serialization only + */ + //@{ + + /** + \brief Exports object's extra data to stream. + */ + virtual void exportExtraData(PxBase&, PxSerializationContext&) const = 0; + + /** + \brief Exports object's data to stream. + */ + virtual void exportData(PxBase&, PxSerializationContext&) const = 0; + + /** + \brief Register references that the object maintains to other objects. + */ + virtual void registerReferences(PxBase& obj, PxSerializationContext& s) const = 0; + + /** + \brief Returns size needed to create the class instance. + + \return sizeof class instance. + */ + virtual size_t getClassSize() const = 0; + + /** + \brief Create object at a given address, resolve references and import extra data. + + \param address Location at which object is created. Address is increased by the size of the created object. + \param context Context for reading external data and resolving references. + \return Created PxBase pointer (needs to be identical to address before increment). + */ + virtual PxBase* createObject(PxU8*& address, PxDeserializationContext& context) const = 0; + + //@} + /**********************************************************************************************************************/ + virtual ~PxSerializer() {} +}; + + +/** + \brief Default PxSerializer implementation. +*/ +template +class PxSerializerDefaultAdapter : public PxSerializer +{ +public: + + /************************************************************************************************/ + + /** @name Basics needed for Binary- and RepX-Serialization + */ + //@{ + + PxSerializerDefaultAdapter(const char* name) : mTypeName(name){} + + virtual const char* getConcreteTypeName() const + { + return mTypeName; + } + + virtual void requiresObjects(PxBase& obj, PxProcessPxBaseCallback& c) const + { + T& t = static_cast(obj); + t.requiresObjects(c); + } + + virtual bool isSubordinate() const + { + return false; + } + + //@} + /************************************************************************************************/ + + /** @name Functionality needed for Binary Serialization only + */ + //@{ + + // object methods + + virtual void exportExtraData(PxBase& obj, PxSerializationContext& s) const + { + T& t = static_cast(obj); + t.exportExtraData(s); + } + + virtual void exportData(PxBase& obj, PxSerializationContext& s) const + { + s.writeData(&obj, sizeof(T)); + } + + virtual void registerReferences(PxBase& obj, PxSerializationContext& s) const + { + T& t = static_cast(obj); + + s.registerReference(obj, PX_SERIAL_REF_KIND_PXBASE, size_t(&obj)); + + struct RequiresCallback : public PxProcessPxBaseCallback + { + RequiresCallback(PxSerializationContext& c) : context(c) {} + RequiresCallback& operator=(RequiresCallback&) { PX_ASSERT(0); return *this; } + void process(physx::PxBase& base) + { + context.registerReference(base, PX_SERIAL_REF_KIND_PXBASE, size_t(&base)); + } + PxSerializationContext& context; + }; + + RequiresCallback callback(s); + t.requiresObjects(callback); + } + + // class methods + + virtual size_t getClassSize() const + { + return sizeof(T); + } + + virtual PxBase* createObject(PxU8*& address, PxDeserializationContext& context) const + { + return T::createObject(address, context); + } + + + //@} + /************************************************************************************************/ + +private: + const char* mTypeName; +}; + +/** + \brief Preprocessor Macro to simplify adapter creation. + + Note: that the allocator used for creation needs to match with the one used in PX_DELETE_SERIALIZER_ADAPTER. +*/ +#define PX_NEW_SERIALIZER_ADAPTER(x) \ + *new( PxGetFoundation().getAllocatorCallback().allocate(sizeof(PxSerializerDefaultAdapter), \ + "PxSerializerDefaultAdapter", __FILE__, __LINE__ )) PxSerializerDefaultAdapter(#x) + +/** + \brief Preprocessor Macro to simplify adapter deletion. +*/ +#define PX_DELETE_SERIALIZER_ADAPTER(x) \ + { PxSerializer* s = x; if (s) { s->~PxSerializer(); PxGetFoundation().getAllocatorCallback().deallocate(s); } } + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxStringTable.h b/src/PhysX/physx/include/common/PxStringTable.h new file mode 100644 index 000000000..5564045eb --- /dev/null +++ b/src/PhysX/physx/include/common/PxStringTable.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_STRING_TABLE_H +#define PX_STRING_TABLE_H + +#include "foundation/PxPreprocessor.h" + +/** \addtogroup physics +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** + * \brief a table to manage strings. Strings allocated through this object are expected to be owned by this object. + */ +class PxStringTable +{ +protected: + virtual ~PxStringTable(){} +public: + /** + * \brief Allocate a new string. + * + * \param[in] inSrc Source string, null terminated or null. + * + * \return *Always* a valid null terminated string. "" is returned if "" or null is passed in. + */ + virtual const char* allocateStr( const char* inSrc ) = 0; + + /** + * Release the string table and all the strings associated with it. + */ + virtual void release() = 0; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxTolerancesScale.h b/src/PhysX/physx/include/common/PxTolerancesScale.h new file mode 100644 index 000000000..f966de3a4 --- /dev/null +++ b/src/PhysX/physx/include/common/PxTolerancesScale.h @@ -0,0 +1,108 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_SCALE_H +#define PX_SCALE_H + +/** \addtogroup common + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysics; + +/** +\brief Class to define the scale at which simulation runs. Most simulation tolerances are +calculated in terms of the values here. + +\note if you change the simulation scale, you will probablly also wish to change the scene's +default value of gravity, and stable simulation will probably require changes to the scene's +bounceThreshold also. +*/ + +class PxTolerancesScale +{ +public: + + /** brief + The approximate size of objects in the simulation. + + For simulating roughly human-sized in metric units, 1 is a good choice. + If simulation is done in centimetres, use 100 instead. This is used to + estimate certain length-related tolerances. + */ + PxReal length; + + /** brief + The typical magnitude of velocities of objects in simulation. This is used to estimate + whether a contact should be treated as bouncing or resting based on its impact velocity, + and a kinetic energy threshold below which the simulation may put objects to sleep. + + For normal physical environments, a good choice is the approximate speed of an object falling + under gravity for one second. + */ + PxReal speed; + + /** + \brief constructor sets to default + */ + PX_INLINE PxTolerancesScale(); + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid (returns always true). + */ + PX_INLINE bool isValid() const; + +}; + +PX_INLINE PxTolerancesScale::PxTolerancesScale(): + length(1.0f), + speed(10.0f) + { + } + +PX_INLINE bool PxTolerancesScale::isValid() const +{ + return length>0.0f; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxTypeInfo.h b/src/PhysX/physx/include/common/PxTypeInfo.h new file mode 100644 index 000000000..5e74ea331 --- /dev/null +++ b/src/PhysX/physx/include/common/PxTypeInfo.h @@ -0,0 +1,124 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_COMMON_PX_TYPEINFO +#define PX_PHYSICS_COMMON_PX_TYPEINFO + +/** \addtogroup common +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief an enumeration of concrete classes inheriting from PxBase + +Enumeration space is reserved for future PhysX core types, PhysXExtensions, +PhysXVehicle and Custom application types. + +@see PxBase, PxTypeInfo +*/ + +struct PxConcreteType +{ + enum Enum + { + eUNDEFINED, + + eHEIGHTFIELD, + eCONVEX_MESH, + eTRIANGLE_MESH_BVH33, + eTRIANGLE_MESH_BVH34, + + eRIGID_DYNAMIC, + eRIGID_STATIC, + eSHAPE, + eMATERIAL, + eCONSTRAINT, + eAGGREGATE, + eARTICULATION, + eARTICULATION_LINK, + eARTICULATION_JOINT, + ePRUNING_STRUCTURE, + eBVH_STRUCTURE, + + ePHYSX_CORE_COUNT, + eFIRST_PHYSX_EXTENSION = 256, + eFIRST_VEHICLE_EXTENSION = 512, + eFIRST_USER_EXTENSION = 1024 + }; +}; + +/** +\brief a structure containing per-type information for types inheriting from PxBase + +@see PxBase, PxConcreteType +*/ + +template struct PxTypeInfo {}; + +#define PX_DEFINE_TYPEINFO(_name, _fastType) \ + class _name; \ + template <> struct PxTypeInfo<_name> { static const char* name() { return #_name; } enum { eFastTypeId = _fastType }; }; + +/* the semantics of the fastType are as follows: an object A can be cast to a type B if B's fastType is defined, and A has the same fastType. + * This implies that B has no concrete subclasses or superclasses. + */ + +PX_DEFINE_TYPEINFO(PxBase, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxMaterial, PxConcreteType::eMATERIAL) +PX_DEFINE_TYPEINFO(PxConvexMesh, PxConcreteType::eCONVEX_MESH) +PX_DEFINE_TYPEINFO(PxTriangleMesh, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxBVH33TriangleMesh, PxConcreteType::eTRIANGLE_MESH_BVH33) +PX_DEFINE_TYPEINFO(PxBVH34TriangleMesh, PxConcreteType::eTRIANGLE_MESH_BVH34) +PX_DEFINE_TYPEINFO(PxHeightField, PxConcreteType::eHEIGHTFIELD) +PX_DEFINE_TYPEINFO(PxActor, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxRigidActor, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxRigidBody, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxRigidDynamic, PxConcreteType::eRIGID_DYNAMIC) +PX_DEFINE_TYPEINFO(PxRigidStatic, PxConcreteType::eRIGID_STATIC) +PX_DEFINE_TYPEINFO(PxArticulationLink, PxConcreteType::eARTICULATION_LINK) +PX_DEFINE_TYPEINFO(PxArticulationJoint, PxConcreteType::eARTICULATION_JOINT) +PX_DEFINE_TYPEINFO(PxArticulation, PxConcreteType::eARTICULATION) +PX_DEFINE_TYPEINFO(PxAggregate, PxConcreteType::eAGGREGATE) +PX_DEFINE_TYPEINFO(PxConstraint, PxConcreteType::eCONSTRAINT) +PX_DEFINE_TYPEINFO(PxShape, PxConcreteType::eSHAPE) +PX_DEFINE_TYPEINFO(PxPruningStructure, PxConcreteType::ePRUNING_STRUCTURE) + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h b/src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h new file mode 100644 index 000000000..7f3e45bae --- /dev/null +++ b/src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h @@ -0,0 +1,102 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_DELAY_LOAD_HOOK +#define PX_PHYSICS_DELAY_LOAD_HOOK + +#include "foundation/PxPreprocessor.h" +#include "common/PxPhysXCommonConfig.h" + +/** \addtogroup foundation +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + /** + \brief PxDelayLoadHook + + This is a helper class for delay loading the PhysXCommon dll and PhysXFoundation dll. + If a PhysXCommon dll or PhysXFoundation dll with a non-default file name needs to be loaded, + PxDelayLoadHook can be sub-classed to provide the custom filenames. + + Once the names are set, the instance must be set for use by PhysX.dll using PxSetPhysXDelayLoadHook(), + PhysXCooking.dll using PxSetPhysXCookingDelayLoadHook() or by PhysXCommon.dll using PxSetPhysXCommonDelayLoadHook(). + + @see PxSetPhysXDelayLoadHook(), PxSetPhysXCookingDelayLoadHook(), PxSetPhysXCommonDelayLoadHook() + */ + class PxDelayLoadHook + { + public: + PxDelayLoadHook() {} + virtual ~PxDelayLoadHook() {} + + virtual const char* getPhysXFoundationDllName() const = 0; + + virtual const char* getPhysXCommonDllName() const = 0; + + protected: + private: + }; + + /** + \brief Sets delay load hook instance for PhysX dll. + + \param[in] hook Delay load hook. + + @see PxDelayLoadHook + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxSetPhysXDelayLoadHook(const physx::PxDelayLoadHook* hook); + + /** + \brief Sets delay load hook instance for PhysXCooking dll. + + \param[in] hook Delay load hook. + + @see PxDelayLoadHook + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxSetPhysXCookingDelayLoadHook(const physx::PxDelayLoadHook* hook); + + /** + \brief Sets delay load hook instance for PhysXCommon dll. + + \param[in] hook Delay load hook. + + @see PxDelayLoadHook + */ + PX_C_EXPORT PX_PHYSX_COMMON_API void PX_CALL_CONV PxSetPhysXCommonDelayLoadHook(const physx::PxDelayLoadHook* hook); + +#if !PX_DOXYGEN +} // namespace physx +#endif +/** @} */ +#endif diff --git a/src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h b/src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h new file mode 100644 index 000000000..dd0168922 --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_BVH_33_MIDPHASE_DESC_H +#define PX_BVH_33_MIDPHASE_DESC_H +/** \addtogroup cooking +@{ +*/ + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** \brief Enumeration for mesh cooking hints. */ +struct PxMeshCookingHint +{ + enum Enum + { + eSIM_PERFORMANCE = 0, //!< Default value. Favors higher quality hierarchy with higher runtime performance over cooking speed. + eCOOKING_PERFORMANCE = 1 //!< Enables fast cooking path at the expense of somewhat lower quality hierarchy construction. + }; +}; + +/** + +\brief Structure describing parameters affecting BVH33 midphase mesh structure. + +@see PxCookingParams, PxMidphaseDesc +*/ +struct PxBVH33MidphaseDesc +{ + /** + \brief Controls the trade-off between mesh size and runtime performance. + + Using a value of 1.0 will produce a larger cooked mesh with generally higher runtime performance, + using 0.0 will produce a smaller cooked mesh, with generally lower runtime performance. + + Values outside of [0,1] range will be clamped and cause a warning when any mesh gets cooked. + + Default value: 0.55 + Range: [0.0f, 1.0f] + */ + PxF32 meshSizePerformanceTradeOff; + + /** + \brief Mesh cooking hint. Used to specify mesh hierarchy construction preference. + + Default value: PxMeshCookingHint::eSIM_PERFORMANCE + */ + PxMeshCookingHint::Enum meshCookingHint; + + /** + \brief Desc initialization to default value. + */ + void setToDefault() + { + meshSizePerformanceTradeOff = 0.55f; + meshCookingHint = PxMeshCookingHint::eSIM_PERFORMANCE; + } + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + bool isValid() const + { + if(meshSizePerformanceTradeOff < 0.0f || meshSizePerformanceTradeOff > 1.0f) + return false; + return true; + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + + + /** @} */ +#endif // PX_BVH_33_MIDPHASE_DESC_H diff --git a/src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h b/src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h new file mode 100644 index 000000000..aec2fe218 --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_BVH_34_MIDPHASE_DESC_H +#define PX_BVH_34_MIDPHASE_DESC_H +/** \addtogroup cooking +@{ +*/ + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** + +\brief Structure describing parameters affecting BVH34 midphase mesh structure. + +@see PxCookingParams, PxMidphaseDesc +*/ +struct PxBVH34MidphaseDesc +{ + /** + \brief Mesh cooking hint for max primitives per leaf limit. + Less primitives per leaf produces larger meshes with better runtime performance + and worse cooking performance. More triangles per leaf results in faster cooking speed and + smaller mesh sizes, but with worse runtime performance. + + Default value: 4 + Range: <4, 15> + */ + PxU32 numPrimsPerLeaf; + + /** + \brief Desc initialization to default value. + */ + void setToDefault() + { + numPrimsPerLeaf = 4; + } + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + bool isValid() const + { + if(numPrimsPerLeaf < 4 || numPrimsPerLeaf > 15) + return false; + return true; + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + + + /** @} */ +#endif // PX_BVH_34_MIDPHASE_DESC_H diff --git a/src/PhysX/physx/include/cooking/PxBVHStructureDesc.h b/src/PhysX/physx/include/cooking/PxBVHStructureDesc.h new file mode 100644 index 000000000..6c9a8544a --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxBVHStructureDesc.h @@ -0,0 +1,109 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_BVH_STRUCTURE_DESC_H +#define PX_BVH_STRUCTURE_DESC_H +/** \addtogroup cooking +@{ +*/ + +#include "common/PxCoreUtilityTypes.h" +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxTransform.h" +#include "foundation/PxBounds3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** + +\brief Descriptor class for #PxBVHStructure. + +@see PxBVHStructure +*/ +class PxBVHStructureDesc +{ +public: + PX_INLINE PxBVHStructureDesc(); + + /** + \brief Pointer to first bounding box. + */ + PxBoundedData bounds; + + /** + \brief Initialize the BVH structure descriptor + */ + PX_INLINE void setToDefault(); + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + PX_INLINE bool isValid() const; + + +protected: +}; + + + +PX_INLINE PxBVHStructureDesc::PxBVHStructureDesc() +{ +} + +PX_INLINE void PxBVHStructureDesc::setToDefault() +{ + *this = PxBVHStructureDesc(); +} + +PX_INLINE bool PxBVHStructureDesc::isValid() const +{ + // Check BVH desc data + if(!bounds.data) + return false; + if(bounds.stride < sizeof(PxBounds3)) //should be at least one point's worth of data + return false; + + if(bounds.count == 0) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + + + /** @} */ +#endif // PX_BVH_STRUCTURE_DESC_H diff --git a/src/PhysX/physx/include/cooking/PxConvexMeshDesc.h b/src/PhysX/physx/include/cooking/PxConvexMeshDesc.h new file mode 100644 index 000000000..20d0ee1e6 --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxConvexMeshDesc.h @@ -0,0 +1,307 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_NXCONVEXMESHDESC +#define PX_COLLISION_NXCONVEXMESHDESC +/** \addtogroup cooking +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxFlags.h" +#include "common/PxCoreUtilityTypes.h" +#include "geometry/PxConvexMesh.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Flags which describe the format and behavior of a convex mesh. +*/ +struct PxConvexFlag +{ + enum Enum + { + /** + Denotes the use of 16-bit vertex indices in PxConvexMeshDesc::triangles or PxConvexMeshDesc::polygons. + (otherwise, 32-bit indices are assumed) + @see #PxConvexMeshDesc.indices + */ + e16_BIT_INDICES = (1<<0), + + /** + Automatically recomputes the hull from the vertices. If this flag is not set, you must provide the entire geometry manually. + + \note There are two different algorithms for hull computation, please see PxConvexMeshCookingType. + + @see PxConvexMeshCookingType + */ + eCOMPUTE_CONVEX = (1<<1), + + /** + \brief Checks and removes almost zero-area triangles during convex hull computation. + The rejected area size is specified in PxCookingParams::areaTestEpsilon + + \note This flag is only used in combination with eCOMPUTE_CONVEX. + + @see PxCookingParams PxCookingParams::areaTestEpsilon + */ + eCHECK_ZERO_AREA_TRIANGLES = (1<<2), + + /** + \brief Quantizes the input vertices using the k-means clustering + + \note The input vertices are quantized to PxConvexMeshDesc::quantizedCount + see http://en.wikipedia.org/wiki/K-means_clustering + + */ + eQUANTIZE_INPUT = (1 << 3), + + /** + \brief Disables the convex mesh validation to speed-up hull creation. Please use separate validation + function in checked/debug builds. Creating a convex mesh with invalid input data without prior validation + may result in undefined behavior. + + @see PxCooking::validateConvexMesh + */ + eDISABLE_MESH_VALIDATION = (1 << 4), + + /** + \brief Enables plane shifting vertex limit algorithm. + + Plane shifting is an alternative algorithm for the case when the computed hull has more vertices + than the specified vertex limit. + + The default algorithm computes the full hull, and an OBB around the input vertices. This OBB is then sliced + with the hull planes until the vertex limit is reached.The default algorithm requires the vertex limit + to be set to at least 8, and typically produces results that are much better quality than are produced + by plane shifting. + + When plane shifting is enabled, the hull computation stops when vertex limit is reached. The hull planes + are then shifted to contain all input vertices, and the new plane intersection points are then used to + generate the final hull with the given vertex limit.Plane shifting may produce sharp edges to vertices + very far away from the input cloud, and does not guarantee that all input vertices are inside the resulting + hull.However, it can be used with a vertex limit as low as 4. + */ + ePLANE_SHIFTING = (1 << 5), + + /** + \brief Inertia tensor computation is faster using SIMD code, but the precision is lower, which may result + in incorrect inertia for very thin hulls. + */ + eFAST_INERTIA_COMPUTATION = (1 << 6), + + /** + \brief Convex hulls are created with respect to GPU simulation limitations. Vertex limit is set to 64 and + vertex limit per face is internally set to 32. + \note Can be used only with eCOMPUTE_CONVEX flag. + */ + eGPU_COMPATIBLE = (1 << 7), + + /** + \brief Convex hull input vertices are shifted to be around origin to provide better computation stability. + It is recommended to provide input vertices around the origin, otherwise use this flag to improve + numerical stability. + \note Is used only with eCOMPUTE_CONVEX flag. + */ + eSHIFT_VERTICES = (1 << 8) + }; +}; + +/** +\brief collection of set bits defined in PxConvexFlag. + +@see PxConvexFlag +*/ +typedef PxFlags PxConvexFlags; +PX_FLAGS_OPERATORS(PxConvexFlag::Enum,PxU16) + +/** +\brief Descriptor class for #PxConvexMesh. +\note The number of vertices and the number of convex polygons in a cooked convex mesh is limited to 256. + +@see PxConvexMesh PxConvexMeshGeometry PxShape PxPhysics.createConvexMesh() + +*/ +class PxConvexMeshDesc +{ +public: + + /** + \brief Vertex positions data in PxBoundedData format. + + Default: NULL + */ + PxBoundedData points; + + /** + \brief Polygons data in PxBoundedData format. +

Pointer to first polygon.

+ + Default: NULL + + @see PxHullPolygon + */ + PxBoundedData polygons; + + /** + \brief Polygon indices data in PxBoundedData format. +

Pointer to first index.

+ + Default: NULL + +

This is declared as a void pointer because it is actually either an PxU16 or a PxU32 pointer.

+ + @see PxHullPolygon PxConvexFlag::e16_BIT_INDICES + */ + PxBoundedData indices; + + /** + \brief Flags bits, combined from values of the enum ::PxConvexFlag + + Default: 0 + */ + PxConvexFlags flags; + + /** + \brief Limits the number of vertices of the result convex mesh. Hard maximum limit is 256 + and minimum limit is 4 if PxConvexFlag::ePLANE_SHIFTING is used, otherwise the minimum + limit is 8. + + \note Vertex limit is only used when PxConvexFlag::eCOMPUTE_CONVEX is specified. + \note The please see PxConvexFlag::ePLANE_SHIFTING for algorithm explanation + + @see PxConvexFlag::ePLANE_SHIFTING + + Range: [4, 255]
+ Default: 255 + */ + PxU16 vertexLimit; + + /** + \brief Maximum number of vertices after quantization. The quantization is done during the vertex cleaning phase. + The quantization is applied when PxConvexFlag::eQUANTIZE_INPUT is specified. + + @see PxConvexFlag::eQUANTIZE_INPUT + + Range: [4, 65535]
+ Default: 255 + */ + PxU16 quantizedCount; + + /** + \brief constructor sets to default. + */ + PX_INLINE PxConvexMeshDesc(); + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE void setToDefault(); + /** + \brief Returns true if the descriptor is valid. + + \return True if the current settings are valid + */ + PX_INLINE bool isValid() const; +}; + +PX_INLINE PxConvexMeshDesc::PxConvexMeshDesc() //constructor sets to default +: vertexLimit(255), quantizedCount(255) +{ +} + +PX_INLINE void PxConvexMeshDesc::setToDefault() +{ + *this = PxConvexMeshDesc(); +} + +PX_INLINE bool PxConvexMeshDesc::isValid() const +{ + // Check geometry + if(points.count < 3 || //at least 1 trig's worth of points + (points.count > 0xffff && flags & PxConvexFlag::e16_BIT_INDICES)) + return false; + if(!points.data) + return false; + if(points.stride < sizeof(PxVec3)) //should be at least one point's worth of data + return false; + if (quantizedCount < 4) + return false; + + // Check topology + if(polygons.data) + { + if(polygons.count < 4) // we require 2 neighbors for each vertex - 4 polygons at least + return false; + + if(!indices.data) // indices must be provided together with polygons + return false; + + PxU32 limit = (flags & PxConvexFlag::e16_BIT_INDICES) ? sizeof(PxU16) : sizeof(PxU32); + if(indices.stride < limit) + return false; + + limit = sizeof(PxHullPolygon); + if(polygons.stride < limit) + return false; + } + else + { + // We can compute the hull from the vertices + if(!(flags & PxConvexFlag::eCOMPUTE_CONVEX)) + return false; // If the mesh is convex and we're not allowed to compute the hull, + // you have to provide it completely (geometry & topology). + } + + if((flags & PxConvexFlag::ePLANE_SHIFTING) && vertexLimit < 4) + { + return false; + } + + if (!(flags & PxConvexFlag::ePLANE_SHIFTING) && vertexLimit < 8) + { + return false; + } + + if(vertexLimit > 256) + { + return false; + } + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/cooking/PxCooking.h b/src/PhysX/physx/include/cooking/PxCooking.h new file mode 100644 index 000000000..f1a8a257e --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxCooking.h @@ -0,0 +1,563 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COOKING_H +#define PX_COOKING_H +/** \addtogroup cooking +@{ +*/ +#include "common/PxPhysXCommonConfig.h" +#include "common/PxTolerancesScale.h" +#include "cooking/Pxc.h" + +#include "cooking/PxConvexMeshDesc.h" +#include "cooking/PxTriangleMeshDesc.h" +#include "cooking/PxMidphaseDesc.h" +#include "cooking/PxBVHStructureDesc.h" +#include "geometry/PxTriangleMesh.h" +#include "geometry/PxBVHStructure.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysicsInsertionCallback; +class PxFoundation; + +/** +\brief Result from convex cooking. +*/ +struct PxConvexMeshCookingResult +{ + enum Enum + { + /** + \brief Convex mesh cooking succeeded. + */ + eSUCCESS, + + /** + \brief Convex mesh cooking failed, algorithm couldn't find 4 initial vertices without a small triangle. + + @see PxCookingParams::areaTestEpsilon PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES + */ + eZERO_AREA_TEST_FAILED, + + /** + \brief Convex mesh cooking succeeded, but the algorithm has reached the 255 polygons limit. + The produced hull does not contain all input vertices. Try to simplify the input vertices + or try to use the eINFLATE_CONVEX or the eQUANTIZE_INPUT flags. + + @see PxConvexFlag::eINFLATE_CONVEX PxConvexFlag::eQUANTIZE_INPUT + */ + ePOLYGONS_LIMIT_REACHED, + + /** + \brief Something unrecoverable happened. Check the error stream to find out what. + */ + eFAILURE + }; +}; + +/** \brief Enumeration for convex mesh cooking algorithms. */ +struct PxConvexMeshCookingType +{ + enum Enum + { + /** + \brief The Quickhull algorithm constructs the hull from the given input points. The resulting hull + will only contain a subset of the input points. + + */ + eQUICKHULL + }; +}; + +/** +\brief Result from triangle mesh cooking +*/ +struct PxTriangleMeshCookingResult +{ + enum Enum + { + /** + \brief Everything is A-OK. + */ + eSUCCESS = 0, + + /** + \brief a triangle is too large for well-conditioned results. Tessellate the mesh for better behavior, see the user guide section on cooking for more details. + */ + eLARGE_TRIANGLE, + + /** + \brief Something unrecoverable happened. Check the error stream to find out what. + */ + eFAILURE + }; +}; + +/** + +\brief Enum for the set of mesh pre-processing parameters. + +*/ + +struct PxMeshPreprocessingFlag +{ + enum Enum + { + /** + \brief When set, mesh welding is performed. See PxCookingParams::meshWeldTolerance. Clean mesh must be enabled. + */ + eWELD_VERTICES = 1 << 0, + + /** + \brief When set, mesh cleaning is disabled. This makes cooking faster. + + When clean mesh is not performed, mesh welding is also not performed. + + It is recommended to use only meshes that passed during validateTriangleMesh. + + */ + eDISABLE_CLEAN_MESH = 1 << 1, + + /** + \brief When set, active edges are set for each triangle edge. This makes cooking faster but slow up contact generation. + */ + eDISABLE_ACTIVE_EDGES_PRECOMPUTE = 1 << 2, + + /** + \brief When set, 32-bit indices will always be created regardless of triangle count. + + \note By default mesh will be created with 16-bit indices for triangle count <= 0xFFFF and 32-bit otherwise. + */ + eFORCE_32BIT_INDICES = 1 << 3 + }; +}; + +typedef PxFlags PxMeshPreprocessingFlags; + +/** + +\brief Structure describing parameters affecting mesh cooking. + +@see PxSetCookingParams() PxGetCookingParams() +*/ +struct PxCookingParams +{ + /** + \brief Zero-size area epsilon used in convex hull computation. + + If the area of a triangle of the hull is below this value, the triangle will be rejected. This test + is done only if PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES is used. + + @see PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES + + Default value: 0.06f*PxTolerancesScale.length*PxTolerancesScale.length + + Range: (0.0f, PX_MAX_F32) + */ + float areaTestEpsilon; + + /** + \brief Plane tolerance used in convex hull computation. + + The value is used during hull construction. When a new point is about to be added to the hull it + gets dropped when the point is closer to the hull than the planeTolerance. The planeTolerance + is increased according to the hull size. + + If 0.0f is set all points are accepted when the convex hull is created. This may lead to edge cases + where the new points may be merged into an existing polygon and the polygons plane equation might + slightly change therefore. This might lead to failures during polygon merging phase in the hull computation. + + It is recommended to use the default value, however if it is required that all points needs to be + accepted or huge thin convexes are created, it might be required to lower the default value. + + \note The plane tolerance is used only within PxConvexMeshCookingType::eQUICKHULL algorithm. + + Default value: 0.0007f + + Range: <0.0f, PX_MAX_F32) + */ + float planeTolerance; + + /** + \brief Convex hull creation algorithm. + + Default value: PxConvexMeshCookingType::eQUICKHULL + + @see PxConvexMeshCookingType + */ + PxConvexMeshCookingType::Enum convexMeshCookingType; + + /** + \brief When true, the face remap table is not created. This saves a significant amount of memory, but the SDK will + not be able to provide the remap information for internal mesh triangles returned by collisions, + sweeps or raycasts hits. + + Default value: false + */ + bool suppressTriangleMeshRemapTable; + + /** + \brief When true, the triangle adjacency information is created. You can get the adjacency triangles + for a given triangle from getTriangle. + + Default value: false + */ + bool buildTriangleAdjacencies; + + /** + \brief When true, addigional information required for GPU-accelerated rigid body simulation is created. This can increase memory usage and cooking times for convex meshes and triangle meshes. + + Default value: false + */ + bool buildGPUData; + + /** + \brief Tolerance scale is used to check if cooked triangles are not too huge. This check will help with simulation stability. + + \note The PxTolerancesScale values have to match the values used when creating a PxPhysics or PxScene instance. + + @see PxTolerancesScale + */ + PxTolerancesScale scale; + + /** + \brief Mesh pre-processing parameters. Used to control options like whether the mesh cooking performs vertex welding before cooking. + + Default value: 0 + */ + PxMeshPreprocessingFlags meshPreprocessParams; + + /** + \brief Mesh weld tolerance. If mesh welding is enabled, this controls the distance at which vertices are welded. + If mesh welding is not enabled, this value defines the acceptance distance for mesh validation. Provided no two vertices are within this distance, the mesh is considered to be + clean. If not, a warning will be emitted. Having a clean, welded mesh is required to achieve the best possible performance. + + The default vertex welding uses a snap-to-grid approach. This approach effectively truncates each vertex to integer values using meshWeldTolerance. + Once these snapped vertices are produced, all vertices that snap to a given vertex on the grid are remapped to reference a single vertex. Following this, + all triangles' indices are remapped to reference this subset of clean vertices. It should be noted that the vertices that we do not alter the + position of the vertices; the snap-to-grid is only performed to identify nearby vertices. + + The mesh validation approach also uses the same snap-to-grid approach to identify nearby vertices. If more than one vertex snaps to a given grid coordinate, + we ensure that the distance between the vertices is at least meshWeldTolerance. If this is not the case, a warning is emitted. + + Default value: 0.0 + */ + PxReal meshWeldTolerance; + + /** + \brief Controls the desired midphase desc structure for triangle meshes. + + @see PxBVH33MidphaseDesc, PxBVH34MidphaseDesc, PxMidphaseDesc + + Default value: PxMeshMidPhase::eBVH33 + */ + PxMidphaseDesc midphaseDesc; + + /** + \brief Vertex limit beyond which additional acceleration structures are computed for each convex mesh. Increase that limit to reduce memory usage. + Computing the extra structures all the time does not guarantee optimal performance. There is a per-platform break-even point below which the + extra structures actually hurt performance. + + Default value: 32 + */ + PxU32 gaussMapLimit; + + PxCookingParams(const PxTolerancesScale& sc): + areaTestEpsilon (0.06f*sc.length*sc.length), + planeTolerance (0.0007f), + convexMeshCookingType (PxConvexMeshCookingType::eQUICKHULL), + suppressTriangleMeshRemapTable (false), + buildTriangleAdjacencies (false), + buildGPUData (false), + scale (sc), + meshPreprocessParams (0), + meshWeldTolerance (0.f), + gaussMapLimit (32) + { + } +}; + +class PxCooking +{ +public: + /** + \brief Closes this instance of the interface. + + This function should be called to cleanly shut down the Cooking library before application exit. + + \note This function is required to be called to release foundation usage. + + */ + virtual void release() = 0; + + /** + \brief Sets cooking parameters + + \param[in] params Cooking parameters + + @see getParams() + */ + virtual void setParams(const PxCookingParams& params) = 0; + + /** + \brief Gets cooking parameters + + \return Current cooking parameters. + + @see PxCookingParams setParams() + */ + virtual const PxCookingParams& getParams() const = 0; + + /** + \brief Checks endianness is the same between cooking & target platforms + + \return True if there is and endian mismatch. + */ + virtual bool platformMismatch() const = 0; + + /** + \brief Cooks a triangle mesh. The results are written to the stream. + + To create a triangle mesh object it is necessary to first 'cook' the mesh data into + a form which allows the SDK to perform efficient collision detection. + + cookTriangleMesh() allows a mesh description to be cooked into a binary stream + suitable for loading and performing collision detection at runtime. + + \param[in] desc The triangle mesh descriptor to read the mesh from. + \param[in] stream User stream to output the cooked data. + \param[out] condition Result from triangle mesh cooking. + \return true on success + + @see cookConvexMesh() setParams() PxPhysics.createTriangleMesh() PxTriangleMeshCookingResult::Enum + */ + virtual bool cookTriangleMesh(const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition = NULL) const = 0; + + + /** + \brief Cooks and creates a triangle mesh and inserts it into PxPhysics. + + \note PxPhysicsInsertionCallback can be obtained through PxPhysics::getPhysicsInsertionCallback(). + + \param[in] desc The triangle mesh descriptor to read the mesh from. + \param[in] insertionCallback The insertion interface from PxPhysics. + \param[out] condition Result from triangle mesh cooking. + \return PxTriangleMesh pointer on success. + + @see cookTriangleMesh() setParams() PxPhysics.createTriangleMesh() PxPhysicsInsertionCallback + */ + virtual PxTriangleMesh* createTriangleMesh(const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition = NULL) const = 0; + + /** + \brief Verifies if the triangle mesh is valid. Prints an error message for each inconsistency found. + + The following conditions are true for a valid triangle mesh: + 1. There are no duplicate vertices (within specified vertexWeldTolerance. See PxCookingParams::meshWeldTolerance) + 2. There are no large triangles (within specified PxTolerancesScale.) + + \param[in] desc The triangle mesh descriptor to read the mesh from. + + \return true if all the validity conditions hold, false otherwise. + + @see cookTriangleMesh() + */ + virtual bool validateTriangleMesh(const PxTriangleMeshDesc& desc) const = 0; + + + /** + \brief Cooks a convex mesh. The results are written to the stream. + + To create a triangle mesh object it is necessary to first 'cook' the mesh data into + a form which allows the SDK to perform efficient collision detection. + + cookConvexMesh() allows a mesh description to be cooked into a binary stream + suitable for loading and performing collision detection at runtime. + + \note The number of vertices and the number of convex polygons in a cooked convex mesh is limited to 255. + \note If those limits are exceeded in either the user-provided data or the final cooked mesh, an error is reported. + + \param[in] desc The convex mesh descriptor to read the mesh from. + \param[in] stream User stream to output the cooked data. + \param[out] condition Result from convex mesh cooking. + \return true on success. + + @see cookTriangleMesh() setParams() PxConvexMeshCookingResult::Enum + */ + virtual bool cookConvexMesh(const PxConvexMeshDesc& desc, PxOutputStream& stream, PxConvexMeshCookingResult::Enum* condition = NULL) const = 0; + + /** + \brief Cooks and creates a convex mesh and inserts it into PxPhysics. + + \note This method does the same as cookConvexMesh, but the produced convex mesh is not stored + into a stream but is directly inserted in PxPhysics. Use this method if you are unable to cook offline. + + \note PxPhysicsInsertionCallback can be obtained through PxPhysics::getPhysicsInsertionCallback(). + + \param[in] desc The convex mesh descriptor to read the mesh from. + \param[in] insertionCallback The insertion interface from PxPhysics. + \param[out] condition Result from convex mesh cooking. + \return PxConvexMesh pointer on success + + @see cookConvexMesh() setParams() PxPhysicsInsertionCallback + */ + virtual PxConvexMesh* createConvexMesh(const PxConvexMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxConvexMeshCookingResult::Enum* condition = NULL) const = 0; + + /** + \brief Verifies if the convex mesh is valid. Prints an error message for each inconsistency found. + + The convex mesh descriptor must contain an already created convex mesh - the vertices, indices and polygons must be provided. + + \note This function should be used if PxConvexFlag::eDISABLE_MESH_VALIDATION is planned to be used in release builds. + + \param[in] desc The convex mesh descriptor to read the mesh from. + + \return true if all the validity conditions hold, false otherwise. + + @see cookConvexMesh() + */ + virtual bool validateConvexMesh(const PxConvexMeshDesc& desc) const = 0; + + + /** + \brief Computed hull polygons from given vertices and triangles. Polygons are needed for PxConvexMeshDesc rather than triangles. + + Please note that the resulting polygons may have different number of vertices. Some vertices may be removed. + The output vertices, indices and polygons must be used to construct a hull. + + The provided PxAllocatorCallback does allocate the out array's. It is the user responsibility to deallocated those + array's. + + \param[in] mesh Simple triangle mesh containing vertices and triangles used to compute polygons. + \param[in] inCallback Memory allocator for out array allocations. + \param[out] nbVerts Number of vertices used by polygons. + \param[out] vertices Vertices array used by polygons. + \param[out] nbIndices Number of indices used by polygons. + \param[out] indices Indices array used by polygons. + \param[out] nbPolygons Number of created polygons. + \param[out] hullPolygons Polygons array. + \return true on success + + @see cookConvexMesh() PxConvexFlags PxConvexMeshDesc PxSimpleTriangleMesh + */ + virtual bool computeHullPolygons(const PxSimpleTriangleMesh& mesh, PxAllocatorCallback& inCallback, PxU32& nbVerts, PxVec3*& vertices, + PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& hullPolygons) const = 0; + + /** + \brief Cooks a heightfield. The results are written to the stream. + + To create a heightfield object there is an option to precompute some of calculations done while loading the heightfield data. + + cookHeightField() allows a heightfield description to be cooked into a binary stream + suitable for loading and performing collision detection at runtime. + + \param[in] desc The heightfield descriptor to read the HF from. + \param[in] stream User stream to output the cooked data. + \return true on success + + @see PxPhysics.createHeightField() + */ + virtual bool cookHeightField(const PxHeightFieldDesc& desc, PxOutputStream& stream) const = 0; + + /** + \brief Cooks and creates a heightfield mesh and inserts it into PxPhysics. + + \param[in] desc The heightfield descriptor to read the HF from. + \param[in] insertionCallback The insertion interface from PxPhysics. + \return PxHeightField pointer on success + + @see cookConvexMesh() setParams() PxPhysics.createTriangleMesh() PxPhysicsInsertionCallback + */ + virtual PxHeightField* createHeightField(const PxHeightFieldDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const = 0; + + /** + \brief Cooks a bounding volume hierarchy structure. The results are written to the stream. + + cookBVHStructure() allows a BVH structure description to be cooked into a binary stream + suitable for loading and performing BVH detection at runtime. + + \param[in] desc The BVH structure descriptor. + \param[in] stream User stream to output the cooked data. + \return true on success. + + @see PxBVHStructure PxRigidActorExt::getRigidActorShapeLocalBoundsList + */ + virtual bool cookBVHStructure(const PxBVHStructureDesc& desc, PxOutputStream& stream) const = 0; + + /** + \brief Cooks and creates a bounding volume hierarchy structure and inserts it into PxPhysics. + + \note This method does the same as cookBVHStructure, but the produced BVH structure is not stored + into a stream but is directly inserted in PxPhysics. Use this method if you are unable to cook offline. + + \note PxPhysicsInsertionCallback can be obtained through PxPhysics::getPhysicsInsertionCallback(). + + \param[in] desc The BVH structure descriptor. + \param[in] insertionCallback The insertion interface from PxPhysics. + \return PxBVHStructure pointer on success + + @see cookBVHStructure() PxPhysicsInsertionCallback + */ + virtual PxBVHStructure* createBVHStructure(const PxBVHStructureDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const = 0; +protected: + virtual ~PxCooking(){} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** +\brief Create an instance of the cooking interface. + +Note that the foundation object is handled as an application-wide singleton in statically linked executables +and a DLL-wide singleton in dynamically linked executables. Therefore, if you are using the runtime SDK in the +same executable as cooking, you should pass the Physics's copy of foundation (acquired with +PxPhysics::getFoundation()) to the cooker. This will also ensure correct handling of memory for objects +passed from the cooker to the SDK. + +To use cooking in standalone mode, create an instance of the Foundation object with PxCreateCookingFoundation. +You should pass the same foundation object to all instances of the cooking interface. + +\param[in] version the SDK version number +\param[in] foundation the foundation object associated with this instance of the cooking interface. +\param[in] params the parameters for this instance of the cooking interface +\return true on success. +*/ +PX_C_EXPORT PX_PHYSX_COOKING_API physx::PxCooking* PX_CALL_CONV PxCreateCooking(physx::PxU32 version, + physx::PxFoundation& foundation, + const physx::PxCookingParams& params); + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/cooking/PxMidphaseDesc.h b/src/PhysX/physx/include/cooking/PxMidphaseDesc.h new file mode 100644 index 000000000..731926451 --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxMidphaseDesc.h @@ -0,0 +1,119 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_MIDPHASE_DESC_H +#define PX_MIDPHASE_DESC_H +/** \addtogroup cooking +@{ +*/ + +#include "geometry/PxTriangleMesh.h" +#include "cooking/PxBVH33MidphaseDesc.h" +#include "cooking/PxBVH34MidphaseDesc.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** + +\brief Structure describing parameters affecting midphase mesh structure. + +@see PxCookingParams, PxBVH33MidphaseDesc, PxBVH34MidphaseDesc +*/ +class PxMidphaseDesc +{ +public: + PX_FORCE_INLINE PxMidphaseDesc() { setToDefault(PxMeshMidPhase::eBVH33); } + + /** + \brief Returns type of midphase mesh structure. + \return PxMeshMidPhase::Enum + + @see PxMeshMidPhase::Enum + */ + PX_FORCE_INLINE PxMeshMidPhase::Enum getType() const { return mType; } + + /** + \brief Midphase descriptors union + + @see PxBV33MidphaseDesc, PxBV34MidphaseDesc + */ + union { + PxBVH33MidphaseDesc mBVH33Desc; + PxBVH34MidphaseDesc mBVH34Desc; + }; + + /** + \brief Initialize the midphase mesh structure descriptor + \param[in] type Midphase mesh structure descriptor + + @see PxBV33MidphaseDesc, PxBV34MidphaseDesc + */ + void setToDefault(PxMeshMidPhase::Enum type) + { + mType = type; + if(type==PxMeshMidPhase::eBVH33) + mBVH33Desc.setToDefault(); + else if(type==PxMeshMidPhase::eBVH34) + mBVH34Desc.setToDefault(); + } + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + bool isValid() const + { + if(mType==PxMeshMidPhase::eBVH33) + return mBVH33Desc.isValid(); + else if(mType==PxMeshMidPhase::eBVH34) + return mBVH34Desc.isValid(); + return false; + } + + PX_FORCE_INLINE PxMidphaseDesc& operator=(PxMeshMidPhase::Enum descType) + { + setToDefault(descType); + return *this; + } + +protected: + PxMeshMidPhase::Enum mType; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + + + /** @} */ +#endif // PX_MIDPHASE_DESC_UNION_H diff --git a/src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h b/src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h new file mode 100644 index 000000000..9aa422fc3 --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_NXTRIANGLEMESHDESC +#define PX_COLLISION_NXTRIANGLEMESHDESC +/** \addtogroup cooking +@{ +*/ + +#include "PxPhysXConfig.h" +#include "geometry/PxSimpleTriangleMesh.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Descriptor class for #PxTriangleMesh. + +Note that this class is derived from PxSimpleTriangleMesh which contains the members that describe the basic mesh. +The mesh data is *copied* when an PxTriangleMesh object is created from this descriptor. After the call the +user may discard the triangle data. + +@see PxTriangleMesh PxTriangleMeshGeometry PxShape +*/ +class PxTriangleMeshDesc : public PxSimpleTriangleMesh +{ +public: + + /** + Optional pointer to first material index, or NULL. There are PxSimpleTriangleMesh::numTriangles indices in total. + Caller may add materialIndexStride bytes to the pointer to access the next triangle. + + When a triangle mesh collides with another object, a material is required at the collision point. + If materialIndices is NULL, then the material of the PxShape instance is used. + Otherwise, if the point of contact is on a triangle with index i, then the material index is determined as: + PxMaterialTableIndex index = *(PxMaterialTableIndex *)(((PxU8*)materialIndices) + materialIndexStride * i); + + If the contact point falls on a vertex or an edge, a triangle adjacent to the vertex or edge is selected, and its index + used to look up a material. The selection is arbitrary but consistent over time. + + Default: NULL + + @see materialIndexStride + */ + PxTypedStridedData materialIndices; + + /** + \brief Constructor sets to default. + */ + PX_INLINE PxTriangleMeshDesc(); + + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE void setToDefault(); + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const; +}; + +PX_INLINE PxTriangleMeshDesc::PxTriangleMeshDesc() //constructor sets to default +{ + PxSimpleTriangleMesh::setToDefault(); +} + +PX_INLINE void PxTriangleMeshDesc::setToDefault() +{ + *this = PxTriangleMeshDesc(); +} + +PX_INLINE bool PxTriangleMeshDesc::isValid() const +{ + if(points.count < 3) //at least 1 trig's worth of points + return false; + if ((!triangles.data) && (points.count%3)) // Non-indexed mesh => we must ensure the geometry defines an implicit number of triangles // i.e. numVertices can't be divided by 3 + return false; + //add more validity checks here + if (materialIndices.data && materialIndices.stride < sizeof(PxMaterialTableIndex)) + return false; + return PxSimpleTriangleMesh::isValid(); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/cooking/Pxc.h b/src/PhysX/physx/include/cooking/Pxc.h new file mode 100644 index 000000000..a279db6cd --- /dev/null +++ b/src/PhysX/physx/include/cooking/Pxc.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COOKING_NX +#define PX_COOKING_NX + +#include "foundation/Px.h" + +// define API function declaration +#if !defined PX_PHYSX_STATIC_LIB + #if PX_WINDOWS + #if defined PX_PHYSX_COOKING_EXPORTS + #define PX_PHYSX_COOKING_API __declspec(dllexport) + #else + #define PX_PHYSX_COOKING_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_PHYSX_COOKING_API PX_UNIX_EXPORT + #endif +#endif + +#if !defined(PX_PHYSX_COOKING_API) + #define PX_PHYSX_COOKING_API +#endif + +#ifndef PX_C_EXPORT + #define PX_C_EXPORT extern "C" +#endif + +#endif diff --git a/src/PhysX/physx/include/cudamanager/PxCudaContextManager.h b/src/PhysX/physx/include/cudamanager/PxCudaContextManager.h new file mode 100644 index 000000000..1bcaf2409 --- /dev/null +++ b/src/PhysX/physx/include/cudamanager/PxCudaContextManager.h @@ -0,0 +1,425 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef PXCUDACONTEXTMANAGER_PXCUDACONTEXTMANAGER_H +#define PXCUDACONTEXTMANAGER_PXCUDACONTEXTMANAGER_H + +#include "foundation/PxPreprocessor.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxErrorCallback.h" +#include "foundation/PxFlags.h" +#include "task/PxTaskDefine.h" +#include "cudamanager/PxCudaMemoryManager.h" + +/* Forward decl to avoid inclusion of cuda.h */ +typedef struct CUctx_st *CUcontext; +typedef struct CUgraphicsResource_st *CUgraphicsResource; +typedef int CUdevice; + +namespace physx +{ + +class PxGpuDispatcher; + + +/** \brief Possible graphic/CUDA interoperability modes for context */ +struct PxCudaInteropMode +{ + /** + * \brief Possible graphic/CUDA interoperability modes for context + */ + enum Enum + { + NO_INTEROP = 0, + D3D10_INTEROP, + D3D11_INTEROP, + OGL_INTEROP, + + COUNT + }; +}; + +struct PxCudaInteropRegisterFlag +{ + enum Enum + { + eNONE = 0x00, + eREAD_ONLY = 0x01, + eWRITE_DISCARD = 0x02, + eSURFACE_LDST = 0x04, + eTEXTURE_GATHER = 0x08 + }; +}; + +/** +\brief collection of set bits defined in NxCudaInteropRegisterFlag. + +@see NxCudaInteropRegisterFlag +*/ +typedef PxFlags PxCudaInteropRegisterFlags; +PX_FLAGS_OPERATORS(PxCudaInteropRegisterFlag::Enum, uint32_t) + +//! \brief Descriptor used to create a PxCudaContextManager +class PxCudaContextManagerDesc +{ +public: + /** + * \brief The CUDA context to manage + * + * If left NULL, the PxCudaContextManager will create a new context. If + * graphicsDevice is also not NULL, this new CUDA context will be bound to + * that graphics device, enabling the use of CUDA/Graphics interop features. + * + * If ctx is not NULL, the specified context must be applied to the thread + * that is allocating the PxCudaContextManager at creation time (aka, it + * cannot be popped). The PxCudaContextManager will take ownership of the + * context until the manager is released. All access to the context must be + * gated by lock acquisition. + * + * If the user provides a context for the PxCudaContextManager, the context + * _must_ have either been created on the GPU ordinal returned by + * PxGetSuggestedCudaDeviceOrdinal() or on your graphics device. + * + * It is perfectly acceptable to allocate device or host pinned memory from + * the context outside the scope of the PxCudaMemoryManager, so long as you + * manage its eventual cleanup. + */ + CUcontext *ctx; + + /** + * \brief D3D device pointer or OpenGl context handle + * + * Only applicable when ctx is NULL, thus forcing a new context to be + * created. In that case, the created context will be bound to this + * graphics device. + */ + void *graphicsDevice; + +#if PX_SUPPORT_GPU_PHYSX + /** + * \brief Application-specific GUID + * + * If your application employs PhysX modules that use CUDA you need to use a GUID + * so that patches for new architectures can be released for your game.You can obtain a GUID for your + * application from Nvidia. + */ + const char* appGUID; +#endif + /** + * \brief The CUDA/Graphics interop mode of this context + * + * If ctx is NULL, this value describes the nature of the graphicsDevice + * pointer provided by the user. Else it describes the nature of the + * context provided by the user. + */ + PxCudaInteropMode::Enum interopMode; + + + /** + * \brief Size of persistent memory + * + * This memory is allocated up front and stays allocated until the + * PxCudaContextManager is released. Size is in bytes, has to be power of two + * and bigger than the page size. Set to 0 to only use dynamic pages. + * + * Note: On Vista O/S and above, there is a per-memory allocation overhead + * to every CUDA work submission, so we recommend that you carefully tune + * this initial base memory size to closely approximate the amount of + * memory your application will consume. + + Note: This is currently not used by PxSceneFlag::eENABLE_GPU_DYNAMICS. Memory allocation properties are configured + for GPU rigid bodies using PxSceneDesc::gpuDynamicsConfig. + */ + uint32_t memoryBaseSize[PxCudaBufferMemorySpace::COUNT]; + + /** + * \brief Size of memory pages + * + * The memory manager will dynamically grow and shrink in blocks multiple of + * this page size. Size has to be power of two and bigger than 0. + + Note: This is currently not used by PxSceneFlag::eENABLE_GPU_DYNAMICS. Memory allocation properties are configured + for GPU rigid bodies using PxSceneDesc::gpuDynamicsConfig. + */ + uint32_t memoryPageSize[PxCudaBufferMemorySpace::COUNT]; + + /** + * \brief Maximum size of memory that the memory manager will allocate + + Note: This is currently not used by PxSceneFlag::eENABLE_GPU_DYNAMICS. Memory allocation properties are configured + for GPU rigid bodies using PxSceneDesc::gpuDynamicsConfig. + */ + uint32_t maxMemorySize[PxCudaBufferMemorySpace::COUNT]; + + PX_INLINE PxCudaContextManagerDesc() + { + ctx = NULL; + interopMode = PxCudaInteropMode::NO_INTEROP; + graphicsDevice = 0; +#if PX_SUPPORT_GPU_PHYSX + appGUID = NULL; +#endif + for(uint32_t i = 0; i < PxCudaBufferMemorySpace::COUNT; i++) + { + memoryBaseSize[i] = 0; + memoryPageSize[i] = 2 * 1024*1024; + maxMemorySize[i] = UINT32_MAX; + } + } +}; + + +/** + * \brief Manages memory, thread locks, and task scheduling for a CUDA context + * + * A PxCudaContextManager manages access to a single CUDA context, allowing it to + * be shared between multiple scenes. Memory allocations are dynamic: starting + * with an initial heap size and growing on demand by a configurable page size. + * The context must be acquired from the manager before using any CUDA APIs. + * + * The PxCudaContextManager is based on the CUDA driver API and explictly does not + * support the CUDA runtime API (aka, CUDART). + * + * To enable CUDA use by an APEX scene, a PxCudaContextManager must be created + * (supplying your own CUDA context, or allowing a new context to be allocated + * for you), the PxGpuDispatcher for that context is retrieved via the + * getGpuDispatcher() method, and this is assigned to the TaskManager that is + * given to the scene via its NxApexSceneDesc. + */ +class PxCudaContextManager +{ +public: + /** + * \brief Acquire the CUDA context for the current thread + * + * Acquisitions are allowed to be recursive within a single thread. + * You can acquire the context multiple times so long as you release + * it the same count. + * + * The context must be acquired before using most CUDA functions. + * + * It is not necessary to acquire the CUDA context inside GpuTask + * launch functions, because the PxGpuDispatcher will have already + * acquired the context for its worker thread. However it is not + * harmfull to (re)acquire the context in code that is shared between + * GpuTasks and non-task functions. + */ + virtual void acquireContext() = 0; + + /** + * \brief Release the CUDA context from the current thread + * + * The CUDA context should be released as soon as practically + * possible, to allow other CPU threads (including the + * PxGpuDispatcher) to work efficiently. + */ + virtual void releaseContext() = 0; + + /** + * \brief Return the CUcontext + */ + virtual CUcontext getContext() = 0; + + /** + * \brief Return the PxCudaMemoryManager instance associated with this + * CUDA context + * Note: This is currently not used by PxSceneFlag::eENABLE_GPU_DYNAMICS. Memory allocation properties are configured + * for GPU rigid bodies using PxSceneDesc::gpuDynamicsConfig. + */ + virtual PxCudaMemoryManager *getMemoryManager() = 0; + + /** + * \brief Return the PxGpuDispatcher instance associated with this + * CUDA context + */ + virtual class physx::PxGpuDispatcher *getGpuDispatcher() = 0; + + /** + * \brief Context manager has a valid CUDA context + * + * This method should be called after creating a PxCudaContextManager, + * especially if the manager was responsible for allocating its own + * CUDA context (desc.ctx == NULL). If it returns false, there is + * no point in assigning this manager's PxGpuDispatcher to a + * TaskManager as it will be unable to execute GpuTasks. + */ + virtual bool contextIsValid() const = 0; + + /* Query CUDA context and device properties, without acquiring context */ + + virtual bool supportsArchSM10() const = 0; //!< G80 + virtual bool supportsArchSM11() const = 0; //!< G92 + virtual bool supportsArchSM12() const = 0; //!< GT200 + virtual bool supportsArchSM13() const = 0; //!< GT260 + virtual bool supportsArchSM20() const = 0; //!< GF100 + virtual bool supportsArchSM30() const = 0; //!< GK100 + virtual bool supportsArchSM35() const = 0; //!< GK110 + virtual bool supportsArchSM50() const = 0; //!< GM100 + virtual bool supportsArchSM52() const = 0; //!< GM200 + virtual bool supportsArchSM60() const = 0; //!< GP100 + virtual bool isIntegrated() const = 0; //!< true if GPU is an integrated (MCP) part + virtual bool canMapHostMemory() const = 0; //!< true if GPU map host memory to GPU (0-copy) + virtual int getDriverVersion() const = 0; //!< returns cached value of cuGetDriverVersion() + virtual size_t getDeviceTotalMemBytes() const = 0; //!< returns cached value of device memory size + virtual int getMultiprocessorCount() const = 0; //!< returns cache value of SM unit count + virtual unsigned int getClockRate() const = 0; //!< returns cached value of SM clock frequency + virtual int getSharedMemPerBlock() const = 0; //!< returns total amount of shared memory available per block in bytes + virtual int getSharedMemPerMultiprocessor() const = 0; //!< returns total amount of shared memory available per multiprocessor in bytes + virtual unsigned int getMaxThreadsPerBlock() const = 0; //!< returns the maximum number of threads per block + virtual const char *getDeviceName() const = 0; //!< returns device name retrieved from driver + virtual CUdevice getDevice() const = 0; //!< returns device handle retrieved from driver + virtual PxCudaInteropMode::Enum getInteropMode() const = 0; //!< interop mode the context was created with + + virtual void setUsingConcurrentStreams(bool) = 0; //!< turn on/off using concurrent streams for GPU work + virtual bool getUsingConcurrentStreams() const = 0; //!< true if GPU work can run in concurrent streams + /* End query methods that don't require context to be acquired */ + + /** + * \brief Register a rendering resource with CUDA + * + * This function is called to register render resources (allocated + * from OpenGL) with CUDA so that the memory may be shared + * between the two systems. This is only required for render + * resources that are designed for interop use. In APEX, each + * render resource descriptor that could support interop has a + * 'registerInCUDA' boolean variable. + * + * The function must be called again any time your graphics device + * is reset, to re-register the resource. + * + * Returns true if the registration succeeded. A registered + * resource must be unregistered before it can be released. + * + * \param resource [OUT] the handle to the resource that can be used with CUDA + * \param buffer [IN] GLuint buffer index to be mapped to cuda + * \param flags [IN] cuda interop registration flags + */ + virtual bool registerResourceInCudaGL(CUgraphicsResource &resource, uint32_t buffer, PxCudaInteropRegisterFlags flags = PxCudaInteropRegisterFlags()) = 0; + + /** + * \brief Register a rendering resource with CUDA + * + * This function is called to register render resources (allocated + * from Direct3D) with CUDA so that the memory may be shared + * between the two systems. This is only required for render + * resources that are designed for interop use. In APEX, each + * render resource descriptor that could support interop has a + * 'registerInCUDA' boolean variable. + * + * The function must be called again any time your graphics device + * is reset, to re-register the resource. + * + * Returns true if the registration succeeded. A registered + * resource must be unregistered before it can be released. + * + * \param resource [OUT] the handle to the resource that can be used with CUDA + * \param resourcePointer [IN] A pointer to either IDirect3DResource9, or ID3D10Device, or ID3D11Resource to be registered. + * \param flags [IN] cuda interop registration flags + */ + virtual bool registerResourceInCudaD3D(CUgraphicsResource &resource, void *resourcePointer, PxCudaInteropRegisterFlags flags = PxCudaInteropRegisterFlags()) = 0; + + /** + * \brief Unregister a rendering resource with CUDA + * + * If a render resource was successfully registered with CUDA using + * the registerResourceInCuda***() methods, this function must be called + * to unregister the resource before the it can be released. + */ + virtual bool unregisterResourceInCuda(CUgraphicsResource resource) = 0; + + /** + * \brief Determine if the user has configured a dedicated PhysX GPU in the NV Control Panel + * \note If using CUDA Interop, this will always return false + * \returns 1 if there is a dedicated GPU + * 0 if there is NOT a dedicated GPU + * -1 if the routine is not implemented + */ + virtual int usingDedicatedGPU() const = 0; + + /** + * \brief Release the PxCudaContextManager + * + * When the manager instance is released, it also releases its + * PxGpuDispatcher instance and PxCudaMemoryManager. Before the memory + * manager is released, it frees all allocated memory pages. If the + * PxCudaContextManager created the CUDA context it was responsible + * for, it also frees that context. + * + * Do not release the PxCudaContextManager if there are any scenes + * using its PxGpuDispatcher. Those scenes must be released first + * since there is no safe way to remove a PxGpuDispatcher from a + * TaskManager once the TaskManager has been given to a scene. + * + */ + virtual void release() = 0; + +protected: + + /** + * \brief protected destructor, use release() method + */ + virtual ~PxCudaContextManager() {} +}; + +/** + * \brief Convenience class for holding CUDA lock within a scope + */ +class PxScopedCudaLock +{ +public: + /** + * \brief ScopedCudaLock constructor + */ + PxScopedCudaLock(PxCudaContextManager& ctx) : mCtx(&ctx) + { + mCtx->acquireContext(); + } + + /** + * \brief ScopedCudaLock destructor + */ + ~PxScopedCudaLock() + { + mCtx->releaseContext(); + } + +protected: + + /** + * \brief CUDA context manager pointer (initialized in the constructor) + */ + PxCudaContextManager* mCtx; +}; + +} // end physx namespace + +#endif // PX_SUPPORT_GPU_PHYSX +#endif // PXCUDACONTEXTMANAGER_PXCUDACONTEXTMANAGER_H diff --git a/src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h b/src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h new file mode 100644 index 000000000..9d20feb61 --- /dev/null +++ b/src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h @@ -0,0 +1,281 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXCUDACONTEXTMANAGER_PXCUDAMEMORYMANAGER_H +#define PXCUDACONTEXTMANAGER_PXCUDAMEMORYMANAGER_H + +#include "foundation/PxPreprocessor.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "task/PxTaskDefine.h" + +// some macros to keep the source code more readable +#define PX_ALLOC_INFO(name, ID) __FILE__, __LINE__, name, physx::PxAllocId::ID +#define PX_ALLOC_INFO_PARAMS_DECL(p0, p1, p2, p3) const char* file = p0, int line = p1, const char* allocName = p2, physx::PxAllocId::Enum allocId = physx::PxAllocId::p3 +#define PX_ALLOC_INFO_PARAMS_DEF() const char* file, int line, const char* allocName, physx::PxAllocId::Enum allocId +#define PX_ALLOC_INFO_PARAMS_INPUT() file, line, allocName, allocId +#define PX_ALLOC_INFO_PARAMS_INPUT_INFO(info) info.getFileName(), info.getLine(), info.getAllocName(), info.getAllocId() + +#ifndef NULL // don't want to include +#define NULL 0 +#endif + +namespace physx +{ + +PX_PUSH_PACK_DEFAULT + +/** \brief ID of the Feature which owns/allocated memory from the heap + * + * Maximum of 64k IDs allowed. + */ +struct PxAllocId +{ + /** + * \brief ID of the Feature which owns/allocated memory from the heap + */ + enum Enum + { + UNASSIGNED, //!< default + APEX, //!< APEX stuff not further classified + PARTICLES, //!< all particle related + GPU_UTIL, //!< e.g. RadixSort (used in SPH and deformable self collision) + CLOTH, //!< all cloth related + NUM_IDS //!< number of IDs, be aware that ApexHeapStats contains PxAllocIdStats[NUM_IDS] + }; +}; + +/// \brief memory type managed by a heap +struct PxCudaBufferMemorySpace +{ + /** + * \brief memory type managed by a heap + */ + enum Enum + { + T_GPU, + T_PINNED_HOST, + T_WRITE_COMBINED, + T_HOST, + COUNT + }; +}; + +/// \brief class to track allocation statistics, see PxgMirrored +class PxAllocInfo +{ +public: + /** + * \brief AllocInfo default constructor + */ + PxAllocInfo() {} + + /** + * \brief AllocInfo constructor that initializes all of the members + */ + PxAllocInfo(const char* file, int line, const char* allocName, PxAllocId::Enum allocId) + : mFileName(file) + , mLine(line) + , mAllocName(allocName) + , mAllocId(allocId) + {} + + /// \brief get the allocation file name + inline const char* getFileName() const + { + return mFileName; + } + + /// \brief get the allocation line + inline int getLine() const + { + return mLine; + } + + /// \brief get the allocation name + inline const char* getAllocName() const + { + return mAllocName; + } + + /// \brief get the allocation ID + inline PxAllocId::Enum getAllocId() const + { + return mAllocId; + } + +private: + const char* mFileName; + int mLine; + const char* mAllocName; + PxAllocId::Enum mAllocId; +}; + +/// \brief statistics collected per AllocationId by HeapManager. +struct PxAllocIdStats +{ + size_t size; //!< currently allocated memory by this ID + size_t maxSize; //!< max allocated memory by this ID + size_t elements; //!< number of current allocations by this ID + size_t maxElements; //!< max number of allocations by this ID +}; + +class PxCudaMemoryManager; +typedef size_t PxCudaBufferPtr; + +/// \brief Hint flag to tell how the buffer will be used +struct PxCudaBufferFlags +{ +/// \brief Enumerations for the hint flag to tell how the buffer will be used + enum Enum + { + F_READ = (1 << 0), + F_WRITE = (1 << 1), + F_READ_WRITE = F_READ | F_WRITE + }; +}; + + +/// \brief Memory statistics struct returned by CudaMemMgr::getStats() +struct PxCudaMemoryManagerStats +{ + + size_t heapSize; //!< Size of all pages allocated for this memory type (allocated + free). + size_t totalAllocated; //!< Size occupied by the current allocations. + size_t maxAllocated; //!< High water mark of allocations since the SDK was created. + PxAllocIdStats allocIdStats[PxAllocId::NUM_IDS]; //!< Stats for each allocation ID, see PxAllocIdStats +}; + + +/// \brief Buffer type: made of hint flags and the memory space (Device Memory, Pinned Host Memory, ...) +struct PxCudaBufferType +{ + /// \brief PxCudaBufferType copy constructor + PX_INLINE PxCudaBufferType(const PxCudaBufferType& t) + : memorySpace(t.memorySpace) + , flags(t.flags) + {} + + /// \brief PxCudaBufferType constructor to explicitely assign members + PX_INLINE PxCudaBufferType(PxCudaBufferMemorySpace::Enum _memSpace, PxCudaBufferFlags::Enum _flags) + : memorySpace(_memSpace) + , flags(_flags) + {} + + PxCudaBufferMemorySpace::Enum memorySpace; //!< specifies which memory space for the buffer + PxCudaBufferFlags::Enum flags; //!< specifies the usage flags for the buffer +}; + + +/// \brief Buffer which keeps informations about allocated piece of memory. +class PxCudaBuffer +{ +public: + /// Retrieves the manager over which the buffer was allocated. + virtual PxCudaMemoryManager* getCudaMemoryManager() const = 0; + + /// Releases the buffer and the memory it used, returns true if successful. + virtual bool free() = 0; + + /// Realloc memory. Use to shrink or resize the allocated chunk of memory of this buffer. + /// Returns true if successful. Fails if the operation would change the address and need a memcopy. + /// In that case the user has to allocate, copy and free the memory with separate steps. + /// Realloc to size 0 always returns false and doesn't change the state. + virtual bool realloc(size_t size, PX_ALLOC_INFO_PARAMS_DECL(NULL, 0, NULL, UNASSIGNED)) = 0; + + /// Returns the type of the allocated memory. + virtual const PxCudaBufferType& getType() const = 0; + + /// Returns the pointer to the allocated memory. + virtual PxCudaBufferPtr getPtr() const = 0; + + /// Returns the size of the allocated memory. + virtual size_t getSize() const = 0; + +protected: + /// \brief protected destructor + virtual ~PxCudaBuffer() {} +}; + + +/// \brief Allocator class for different kinds of CUDA related memory. +class PxCudaMemoryManager +{ +public: + /// Allocate memory of given type and size. Returns a CudaBuffer if successful. Returns NULL if failed. + virtual PxCudaBuffer* alloc(const PxCudaBufferType& type, size_t size, PX_ALLOC_INFO_PARAMS_DECL(NULL, 0, NULL, UNASSIGNED)) = 0; + + /// Basic heap allocator without PxCudaBuffer + virtual PxCudaBufferPtr alloc(PxCudaBufferMemorySpace::Enum memorySpace, size_t size, PX_ALLOC_INFO_PARAMS_DECL(NULL, 0, NULL, UNASSIGNED)) = 0; + + /// Basic heap deallocator without PxCudaBuffer + virtual bool free(PxCudaBufferMemorySpace::Enum memorySpace, PxCudaBufferPtr addr) = 0; + + /// Basic heap realloc without PxCudaBuffer + virtual bool realloc(PxCudaBufferMemorySpace::Enum memorySpace, PxCudaBufferPtr addr, size_t size, PX_ALLOC_INFO_PARAMS_DECL(NULL, 0, NULL, UNASSIGNED)) = 0; + + /// Retrieve stats for the memory of given type. See PxCudaMemoryManagerStats. + virtual void getStats(const PxCudaBufferType& type, PxCudaMemoryManagerStats& outStats) = 0; + + /// Ensure that a given amount of free memory is available. Triggers CUDA allocations in size of (2^n * pageSize) if necessary. + /// Returns false if page allocations failed. + virtual bool reserve(const PxCudaBufferType& type, size_t size) = 0; + + /// Set the page size. The managed memory grows by blocks 2^n * pageSize. Page allocations trigger CUDA driver allocations, + /// so the page size should be reasonably big. Returns false if input size was invalid, i.e. not power of two. + /// Default is 2 MB. + virtual bool setPageSize(const PxCudaBufferType& type, size_t size) = 0; + + /// Set the upper limit until which pages of a given memory type can be allocated. + /// Reducing the max when it is already hit does not shrink the memory until it is deallocated by releasing the buffers which own the memory. + virtual bool setMaxMemorySize(const PxCudaBufferType& type, size_t size) = 0; + + /// Returns the base size. The base memory block stays persistently allocated over the SDKs life time. + virtual size_t getBaseSize(const PxCudaBufferType& type) = 0; + + /// Returns the currently set page size. The memory grows and shrinks in blocks of size (2^n pageSize) + virtual size_t getPageSize(const PxCudaBufferType& type) = 0; + + /// Returns the upper limit until which the manager is allowed to allocate additional pages from the CUDA driver. + virtual size_t getMaxMemorySize(const PxCudaBufferType& type) = 0; + + /// Get device mapped pinned host mem ptr. Operation only valid for memory space PxCudaBufferMemorySpace::T_PINNED_HOST. + virtual PxCudaBufferPtr getMappedPinnedPtr(PxCudaBufferPtr hostPtr) = 0; + +protected: + /// \brief protected destructor + virtual ~PxCudaMemoryManager() {} +}; + +PX_POP_PACK + + +} // end physx namespace + +#endif // PX_SUPPORT_GPU_PHYSX +#endif // PXCUDACONTEXTMANAGER_PXCUDAMEMORYMANAGER_H diff --git a/src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h b/src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h new file mode 100644 index 000000000..6429a7b1b --- /dev/null +++ b/src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h @@ -0,0 +1,86 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXCUDACONTEXTMANAGER_PXGPUCOPYDESC_H +#define PXCUDACONTEXTMANAGER_PXGPUCOPYDESC_H + +#include "foundation/PxPreprocessor.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "task/PxTaskDefine.h" + +namespace physx +{ + +PX_PUSH_PACK_DEFAULT + +/** + * \brief Input descriptor for the GpuDispatcher's built-in copy kernel + * + * All host memory involved in copy transactions must be page-locked. + * If more than one descriptor is passed to the copy kernel in one launch, + * the descriptors themselves must be in page-locked memory. + */ +struct PxGpuCopyDesc +{ + /** + * \brief Input descriptor for the GpuDispatcher's built-in copy kernel + */ + enum CopyType + { + HostToDevice, + DeviceToHost, + DeviceToDevice, + DeviceMemset32 + }; + + size_t dest; //!< the destination + size_t source; //!< the source (32bit value when type == DeviceMemset) + size_t bytes; //!< the size in bytes + CopyType type; //!< the memory transaction type + + /** + * \brief Copy is optimally performed as 64bit words, requires 64bit alignment. But it can + * gracefully degrade to 32bit copies if necessary + */ + PX_INLINE bool isValid() + { + bool ok = true; + ok &= ((dest & 0x3) == 0); + ok &= ((type == DeviceMemset32) || (source & 0x3) == 0); + ok &= ((bytes & 0x3) == 0); + return ok; + } +}; + +PX_POP_PACK + +} // end physx namespace + +#endif // PX_SUPPORT_GPU_PHYSX +#endif // PXCUDACONTEXTMANAGER_PXGPUCOPYDESC_H diff --git a/src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h b/src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h new file mode 100644 index 000000000..64d190b03 --- /dev/null +++ b/src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXCUDACONTEXTMANAGER_PXGPUCOPYDESCQUEUE_H +#define PXCUDACONTEXTMANAGER_PXGPUCOPYDESCQUEUE_H + +#include "foundation/PxPreprocessor.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "foundation/PxAssert.h" +#include "task/PxTaskDefine.h" +#include "task/PxGpuDispatcher.h" +#include "cudamanager/PxGpuCopyDesc.h" +#include "cudamanager/PxCudaContextManager.h" + +/* forward decl to avoid including */ +typedef struct CUstream_st* CUstream; + +namespace physx +{ + +PX_PUSH_PACK_DEFAULT + +/// \brief Container class for queueing PxGpuCopyDesc instances in pinned (non-pageable) CPU memory +class PxGpuCopyDescQueue +{ +public: + /// \brief PxGpuCopyDescQueue constructor + PxGpuCopyDescQueue(PxGpuDispatcher& d) + : mDispatcher(d) + , mBuffer(0) + , mStream(0) + , mReserved(0) + , mOccupancy(0) + , mFlushed(0) + { + } + + /// \brief PxGpuCopyDescQueue destructor + ~PxGpuCopyDescQueue() + { + if (mBuffer) + { + mDispatcher.getCudaContextManager()->getMemoryManager()->free(PxCudaBufferMemorySpace::T_PINNED_HOST, (size_t) mBuffer); + } + } + + /// \brief Reset the enqueued copy descriptor list + /// + /// Must be called at least once before any copies are enqueued, and each time the launched + /// copies are known to have been completed. The recommended use case is to call this at the + /// start of each simulation step. + void reset(CUstream stream, uint32_t reserveSize) + { + if (reserveSize > mReserved) + { + if (mBuffer) + { + mDispatcher.getCudaContextManager()->getMemoryManager()->free( + PxCudaBufferMemorySpace::T_PINNED_HOST, + (size_t) mBuffer); + mReserved = 0; + } + mBuffer = (PxGpuCopyDesc*) mDispatcher.getCudaContextManager()->getMemoryManager()->alloc( + PxCudaBufferMemorySpace::T_PINNED_HOST, + reserveSize * sizeof(PxGpuCopyDesc), + PX_ALLOC_INFO("PxGpuCopyDescQueue", GPU_UTIL)); + if (mBuffer) + { + mReserved = reserveSize; + } + } + + mOccupancy = 0; + mFlushed = 0; + mStream = stream; + } + + /// \brief Enqueue the specified copy descriptor, or launch immediately if no room is available + void enqueue(PxGpuCopyDesc& desc) + { + PX_ASSERT(desc.isValid()); + if (desc.bytes == 0) + { + return; + } + + if (mOccupancy < mReserved) + { + mBuffer[ mOccupancy++ ] = desc; + } + else + { + mDispatcher.launchCopyKernel(&desc, 1, mStream); + } + } + + /// \brief Launch all copies queued since the last flush or reset + void flushEnqueued() + { + if (mOccupancy > mFlushed) + { + mDispatcher.launchCopyKernel(mBuffer + mFlushed, mOccupancy - mFlushed, mStream); + mFlushed = mOccupancy; + } + } + +private: + PxGpuDispatcher& mDispatcher; + PxGpuCopyDesc* mBuffer; + CUstream mStream; + uint32_t mReserved; + uint32_t mOccupancy; + uint32_t mFlushed; + + void operator=(const PxGpuCopyDescQueue&); // prevent a warning... +}; + +PX_POP_PACK + +} // end physx namespace + +#endif // PX_SUPPORT_GPU_PHYSX +#endif // PXCUDACONTEXTMANAGER_PXGPUCOPYDESCQUEUE_H diff --git a/src/PhysX/physx/include/extensions/PxBinaryConverter.h b/src/PhysX/physx/include/extensions/PxBinaryConverter.h new file mode 100644 index 000000000..1a985fda7 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxBinaryConverter.h @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_BINARY_CONVERTER_H +#define PX_BINARY_CONVERTER_H +/** \addtogroup extensions +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxConverterReportMode +{ + enum Enum + { + eNONE, //!< Silent mode. If enabled, no information is sent to the error stream. + eNORMAL, //!< Normal mode. If enabled, only important information is sent to the error stream. + eVERBOSE //!< Verbose mode. If enabled, detailed information is sent to the error stream. + }; +}; + + +/** +\brief Binary converter for serialized streams. + +The binary converter class is targeted at converting binary streams from authoring platforms, +such as windows, osx or linux to any game runtime platform supported by PhysX. Particularly +it is currently not supported to run the converter on a platforms that has an endian mismatch +with the platform corresponding to the source binary file and source meta data. + +If you want to use multiple threads for batch conversions, please create one instance +of this class for each thread. + +@see PxSerialization.createBinaryConverter +*/ +class PxBinaryConverter +{ +public: + + /** + \brief Releases binary converter + */ + virtual void release() = 0; + + /** + \brief Sets desired report mode. + + \param[in] mode Report mode + */ + virtual void setReportMode(PxConverterReportMode::Enum mode) = 0; + + /** + \brief Setups source and target meta-data streams + + The source meta data provided needs to have the same endianness as the platform the converter is run on. + The meta data needs to be set before calling the conversion method. + + \param[in] srcMetaData Source platform's meta-data stream + \param[in] dstMetaData Target platform's meta-data stream + + \return True if success + */ + virtual bool setMetaData(PxInputStream& srcMetaData, PxInputStream& dstMetaData) = 0; + + /** + \brief Test utility function to compare two sets of meta data. + + The meta data needs to be set before calling the compareMetaData method. + This method will issue PxErrorCode::eDEBUG_INFO messages if mismatches are encountered. + + \return True if meta data is equivalend + */ + virtual bool compareMetaData() const = 0; + + /** + \brief Converts binary stream from source platform to target platform + + The converter needs to be configured with source and destination meta data before calling the conversion method. + The source meta data needs to correspond to the same platform as the source binary data. + + \param[in] srcStream Source stream + \param[in] srcSize Number of bytes to convert + \param[in] targetStream Target stream + + \return True if success + */ + virtual bool convert(PxInputStream& srcStream, PxU32 srcSize, PxOutputStream& targetStream) = 0; + + +protected: + PxBinaryConverter() {} + virtual ~PxBinaryConverter() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxBroadPhaseExt.h b/src/PhysX/physx/include/extensions/PxBroadPhaseExt.h new file mode 100644 index 000000000..8347bcc10 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxBroadPhaseExt.h @@ -0,0 +1,75 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_BROAD_PHASE_H +#define PX_PHYSICS_EXTENSIONS_BROAD_PHASE_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxBroadPhaseExt +{ +public: + + /** + \brief Creates regions for PxSceneDesc, from a global box. + + This helper simply subdivides the given global box into a 2D grid of smaller boxes. Each one of those smaller boxes + is a region of interest for the broadphase. There are nbSubdiv*nbSubdiv regions in the 2D grid. The function does not + subdivide along the given up axis. + + This is the simplest setup one can use with PxBroadPhaseType::eMBP. A more sophisticated setup would try to cover + the game world with a non-uniform set of regions (i.e. not just a grid). + + \param[out] regions Regions computed from the input global box + \param[in] globalBounds World-space box covering the game world + \param[in] nbSubdiv Grid subdivision level. The function will create nbSubdiv*nbSubdiv regions. + \param[in] upAxis Up axis (0 for X, 1 for Y, 2 for Z). + \return number of regions written out to the 'regions' array + + @see PxSceneDesc PxBroadPhaseType + */ + static PxU32 createRegionsFromWorldBounds(PxBounds3* regions, const PxBounds3& globalBounds, PxU32 nbSubdiv, PxU32 upAxis=1); +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxCollectionExt.h b/src/PhysX/physx/include/extensions/PxCollectionExt.h new file mode 100644 index 000000000..157c28108 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxCollectionExt.h @@ -0,0 +1,119 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLECTION_EXT_H +#define PX_COLLECTION_EXT_H +/** \addtogroup extensions +@{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxCollection.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxCollectionExt + { + public: + /** + \brief Removes and releases all object from a collection. + + The Collection itself is not released. + + If the releaseExclusiveShapes flag is not set to true, release() will not be called on exclusive shapes. + + It is assumed that the application holds a reference to each of the objects in the collection, with the exception of objects that are not releasable + (PxBase::isReleasable()). In general, objects that violate this assumption need to be removed from the collection prior to calling releaseObjects. + + \note when a shape is created with PxRigidActor::createShape() or PxRigidActorExt::createExclusiveShape(), the only counted reference is held by the actor. + If such a shape and its actor are present in the collection, the reference count will be decremented once when the actor is released, and once when the + shape is released, resulting in undefined behavior. Shape reference counts can be incremented with PxShape::acquireReference(). + + \param[in] collection to remove and release all object from. + \param[in] releaseExclusiveShapes if this parameter is set to false, release() will not be called on exclusive shapes. + */ + static void releaseObjects(PxCollection& collection, bool releaseExclusiveShapes = true); + + /** + \brief Removes objects of a given type from a collection, potentially adding them to another collection. + + \param[in,out] collection Collection from which objects are removed + \param[in] concreteType PxConcreteType of sdk objects that should be removed + \param[in,out] to Optional collection to which the removed objects are added + + @see PxCollection, PxConcreteType + */ + static void remove(PxCollection& collection, PxType concreteType, PxCollection* to = NULL); + + + /** + \brief Collects all objects in PxPhysics that are shareable across multiple scenes. + + This function creates a new collection from all objects that are shareable across multiple + scenes. Instances of the following types are included: PxConvexMesh, PxTriangleMesh, + PxHeightField, PxShape and PxMaterial. + + This is a helper function to ease the creation of collections for serialization. + + \param[in] physics The physics SDK instance from which objects are collected. See #PxPhysics + \return Collection to which objects are added. See #PxCollection + + @see PxCollection, PxPhysics + */ + static PxCollection* createCollection(PxPhysics& physics); + + /** + \brief Collects all objects from a PxScene. + + This function creates a new collection from all objects that where added to the specified + PxScene. Instances of the following types are included: PxActor, PxAggregate, + PxArticulation and PxJoint (other PxConstraint types are not included). + + This is a helper function to ease the creation of collections for serialization. + The function PxSerialization.complete() can be used to complete the collection with required objects prior to + serialization. + + \param[in] scene The PxScene instance from which objects are collected. See #PxScene + \return Collection to which objects are added. See #PxCollection + + @see PxCollection, PxScene, PxSerialization.complete() + */ + static PxCollection* createCollection(PxScene& scene); + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxConstraintExt.h b/src/PhysX/physx/include/extensions/PxConstraintExt.h new file mode 100644 index 000000000..62ca07814 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxConstraintExt.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_CONSTRAINT_H +#define PX_PHYSICS_EXTENSIONS_CONSTRAINT_H + +#include "foundation/PxPreprocessor.h" + +/** \addtogroup extensions + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Unique identifiers for extensions classes which implement a constraint based on PxConstraint. + +\note Users which want to create their own custom constraint types should choose an ID larger or equal to eNEXT_FREE_ID +and not eINVALID_ID. + +@see PxConstraint PxSimulationEventCallback.onConstraintBreak() +*/ +struct PxConstraintExtIDs +{ + enum Enum + { + eJOINT, + eVEHICLE_SUSP_LIMIT, + eVEHICLE_STICKY_TYRE, + eNEXT_FREE_ID, + eINVALID_ID = 0x7fffffff + }; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxContactJoint.h b/src/PhysX/physx/include/extensions/PxContactJoint.h new file mode 100644 index 000000000..00f84a5e3 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxContactJoint.h @@ -0,0 +1,168 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_CONTACTJOINT_H +#define PX_CONTACTJOINT_H + +#include "extensions/PxJoint.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxContactJoint; + + /** + \brief Create a distance Joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + + @see PxContactJoint + */ + PxContactJoint* PxContactJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + + /** + \brief a joint that maintains an upper or lower bound (or both) on the distance between two points on different objects + + @see PxContactJointCreate PxJoint + */ + + struct PxJacobianRow + { + PxVec3 linear0; + PxVec3 linear1; + PxVec3 angular0; + PxVec3 angular1; + + PxJacobianRow(){} + + PxJacobianRow(const PxVec3& lin0, const PxVec3& lin1, const PxVec3& ang0, const PxVec3& ang1) : + linear0(lin0), linear1(lin1), angular0(ang0), angular1(ang1) + { + + } + + void operator *= (const PxReal scale) + { + linear0 *= scale; + linear1 *= scale; + angular0 *= scale; + angular1 *= scale; + } + + PxJacobianRow operator * (const PxReal scale) const + { + return PxJacobianRow(linear0*scale, linear1*scale, angular0*scale, angular1*scale); + } + }; + + /** + \brief a joint that maintains an upper or lower bound (or both) on the distance between two points on different objects + + @see PxContactJointCreate PxJoint + */ + class PxContactJoint : public PxJoint + { + public: + + /** + \brief Set the current contact of the joint + */ + virtual void setContact(const PxVec3& contact) = 0; + + /** + \brief Set the current contact normal of the joint + */ + virtual void setContactNormal(const PxVec3& contactNormal) = 0; + + /** + \brief Set the current penetration of the joint + */ + virtual void setPenetration(const PxReal penetration) = 0; + + /** + \brief Return the current contact of the joint + */ + virtual PxVec3 getContact() const = 0; + + /** + \brief Return the current contact normal of the joint + */ + virtual PxVec3 getContactNormal() const = 0; + + /** + \brief Return the current penetration value of the joint + */ + virtual PxReal getPenetration() const = 0; + + virtual PxReal getResititution() const = 0; + virtual void setResititution(const PxReal resititution) = 0; + virtual PxReal getBounceThreshold() const = 0; + virtual void setBounceThreshold(const PxReal bounceThreshold) = 0; + + /** + \brief Returns string name of PxContactJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxContactJoint"; } + + virtual void computeJacobians(PxJacobianRow* jacobian) const = 0; + virtual PxU32 getNbJacobianRows() const = 0; + + protected: + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxContactJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxContactJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxContactJoint", name) || PxJoint::isKindOf(name); } + + //~serialization + }; + +#if !PX_DOXYGEN +} +#endif + + +#endif diff --git a/src/PhysX/physx/include/extensions/PxConvexMeshExt.h b/src/PhysX/physx/include/extensions/PxConvexMeshExt.h new file mode 100644 index 000000000..39b150f42 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxConvexMeshExt.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_CONVEX_MESH_H +#define PX_PHYSICS_EXTENSIONS_CONVEX_MESH_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxConvexMeshGeometry; + + /** + \brief Computes closest polygon of the convex hull geometry for a given impact point + and impact direction. When doing sweeps against a scene, one might want to delay + the rather expensive computation of the hit face index for convexes until it is clear + the information is really needed and then use this method to get the corresponding + face index. + + \param[in] convexGeom The convex mesh geometry. + \param[in] geomPose Pose for the geometry object. + \param[in] impactPos Impact position. + \param[in] unitDir Normalized impact direction. + + \return Closest face index of the convex geometry. + + @see PxTransform PxConvexMeshGeometry + */ + PxU32 PxFindFaceIndex(const PxConvexMeshGeometry& convexGeom, + const PxTransform& geomPose, + const PxVec3& impactPos, + const PxVec3& unitDir); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxD6Joint.h b/src/PhysX/physx/include/extensions/PxD6Joint.h new file mode 100644 index 000000000..7fe722d8d --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxD6Joint.h @@ -0,0 +1,561 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_D6JOINT_H +#define PX_D6JOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" +#include "extensions/PxJointLimit.h" +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxD6Joint; + +/** +\brief Create a D6 joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxD6Joint +*/ +PxD6Joint* PxD6JointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + +/** +\brief Used to specify one of the degrees of freedom of a D6 joint. + +@see PxD6Joint +*/ +struct PxD6Axis +{ + enum Enum + { + eX = 0, //!< motion along the X axis + eY = 1, //!< motion along the Y axis + eZ = 2, //!< motion along the Z axis + eTWIST = 3, //!< motion around the X axis + eSWING1 = 4, //!< motion around the Y axis + eSWING2 = 5, //!< motion around the Z axis + eCOUNT = 6 + }; +}; + + +/** +\brief Used to specify the range of motions allowed for a degree of freedom in a D6 joint. + +@see PxD6Joint +*/ +struct PxD6Motion +{ + enum Enum + { + eLOCKED, //!< The DOF is locked, it does not allow relative motion. + eLIMITED, //!< The DOF is limited, it only allows motion within a specific range. + eFREE //!< The DOF is free and has its full range of motion. + }; +}; + + +/** +\brief Used to specify which axes of a D6 joint are driven. + +Each drive is an implicit force-limited damped spring: + +force = spring * (target position - position) + damping * (targetVelocity - velocity) + +Alternatively, the spring may be configured to generate a specified acceleration instead of a force. + +A linear axis is affected by drive only if the corresponding drive flag is set. There are two possible models +for angular drive: swing/twist, which may be used to drive one or more angular degrees of freedom, or slerp, +which may only be used to drive all three angular degrees simultaneously. + +@see PxD6Joint +*/ +struct PxD6Drive +{ + enum Enum + { + eX = 0, //!< drive along the X-axis + eY = 1, //!< drive along the Y-axis + eZ = 2, //!< drive along the Z-axis + eSWING = 3, //!< drive of displacement from the X-axis + eTWIST = 4, //!< drive of the displacement around the X-axis + eSLERP = 5, //!< drive of all three angular degrees along a SLERP-path + eCOUNT = 6 + }; +}; + +/** +\brief flags for configuring the drive model of a PxD6Joint + +@see PxD6JointDrive PxD6Joint +*/ +struct PxD6JointDriveFlag +{ + enum Enum + { + eACCELERATION = 1 //!< drive spring is for the acceleration at the joint (rather than the force) + }; +}; +typedef PxFlags PxD6JointDriveFlags; +PX_FLAGS_OPERATORS(PxD6JointDriveFlag::Enum, PxU32) + +/** +\brief parameters for configuring the drive model of a PxD6Joint + +@see PxD6Joint +*/ +class PxD6JointDrive : public PxSpring +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + +public: + PxReal forceLimit; //!< the force limit of the drive - may be an impulse or a force depending on PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES + PxD6JointDriveFlags flags; //!< the joint drive flags + + /** + \brief default constructor for PxD6JointDrive. + */ + PxD6JointDrive(): PxSpring(0,0), forceLimit(PX_MAX_F32), flags(0) {} + + /** + \brief constructor a PxD6JointDrive. + + \param[in] driveStiffness The stiffness of the drive spring. + \param[in] driveDamping The damping of the drive spring + \param[in] driveForceLimit The maximum impulse or force that can be exerted by the drive + \param[in] isAcceleration Whether the drive is an acceleration drive or a force drive + */ + PxD6JointDrive(PxReal driveStiffness, PxReal driveDamping, PxReal driveForceLimit, bool isAcceleration = false) + : PxSpring(driveStiffness, driveDamping) + , forceLimit(driveForceLimit) + , flags(isAcceleration?PxU32(PxD6JointDriveFlag::eACCELERATION) : 0) + {} + + /** + \brief returns true if the drive is valid + */ + bool isValid() const + { + return PxIsFinite(stiffness) && stiffness>=0 && + PxIsFinite(damping) && damping >=0 && + PxIsFinite(forceLimit) && forceLimit >=0; + } +}; + + +/** + \brief A D6 joint is a general constraint between two actors. + + It allows the application to individually define the linear and rotational degrees of freedom, + and also to configure a variety of limits and driven degrees of freedom. + + By default all degrees of freedom are locked. So to create a prismatic joint with free motion + along the x-axis: + + \code + ... + joint->setMotion(PxD6Axis::eX, PxD6JointMotion::eFREE); + ... + \endcode + + Or a Revolute joint with motion free allowed around the x-axis: + + \code + ... + joint->setMotion(PxD6Axis::eTWIST, PxD6JointMotion::eFREE); + ... + \endcode + + Degrees of freedom may also be set to limited instead of locked. + + There are two different kinds of linear limits available. The first kind is a single limit value + for all linear degrees of freedom, which may act as a linear, circular, or spherical limit depending + on which degrees of freedom are limited. This is similar to a distance limit. Then, the second kind + supports a pair of limit values for each linear axis, which can be used to implement a traditional + prismatic joint for example. + + If the twist degree of freedom is limited, is supports upper and lower limits. The two swing degrees + of freedom are limited with a cone limit. +@see PxD6JointCreate() PxJoint +*/ +class PxD6Joint : public PxJoint +{ +public: + + /** + \brief Set the motion type around the specified axis. + + Each axis may independently specify that the degree of freedom is locked (blocking relative movement + along or around this axis), limited by the corresponding limit, or free. + + \param[in] axis the axis around which motion is specified + \param[in] type the motion type around the specified axis + + Default: all degrees of freedom are locked + + @see getMotion() PxD6Axis PxD6Motion + */ + virtual void setMotion(PxD6Axis::Enum axis, PxD6Motion::Enum type) = 0; + + /** + \brief Get the motion type around the specified axis. + + @see setMotion() PxD6Axis PxD6Motion + + \param[in] axis the degree of freedom around which the motion type is specified + \return the motion type around the specified axis + */ + virtual PxD6Motion::Enum getMotion(PxD6Axis::Enum axis) const = 0; + + /** + \brief get the twist angle of the joint, in the range (-2*Pi, 2*Pi] + */ + virtual PxReal getTwistAngle() const = 0; + + /** + \brief get the twist angle of the joint + + \deprecated Use getTwistAngle instead. Deprecated since PhysX version 4.0 + */ + PX_DEPRECATED PX_FORCE_INLINE PxReal getTwist() const { return getTwistAngle(); } + + /** + \brief get the swing angle of the joint from the Y axis + */ + virtual PxReal getSwingYAngle() const = 0; + + /** + \brief get the swing angle of the joint from the Z axis + */ + virtual PxReal getSwingZAngle() const = 0; + + /** + \brief Set the distance limit for the joint. + + A single limit constraints all linear limited degrees of freedom, forming a linear, circular + or spherical constraint on motion depending on the number of limited degrees. This is similar + to a distance limit. + + \param[in] limit the distance limit structure + + @see getDistanceLimit() PxJointLinearLimit + */ + virtual void setDistanceLimit(const PxJointLinearLimit& limit) = 0; + + /** + \brief Get the distance limit for the joint. + + \return the distance limit structure + + @see setDistanceLimit() PxJointLinearLimit + */ + virtual PxJointLinearLimit getDistanceLimit() const = 0; + + /** + \deprecated Use setDistanceLimit instead. Deprecated since PhysX version 4.0 + */ + PX_DEPRECATED PX_FORCE_INLINE void setLinearLimit(const PxJointLinearLimit& limit) { setDistanceLimit(limit); } + + /** + \deprecated Use getDistanceLimit instead. Deprecated since PhysX version 4.0 + */ + PX_DEPRECATED PX_FORCE_INLINE PxJointLinearLimit getLinearLimit() const { return getDistanceLimit(); } + + /** + \brief Set the linear limit for a given linear axis. + + This function extends the previous setDistanceLimit call with the following features: + - there can be a different limit for each linear axis + - each limit is defined by two values, i.e. it can now be asymmetric + + This can be used to create prismatic joints similar to PxPrismaticJoint, or point-in-quad joints, + or point-in-box joints. + + \param[in] axis The limited linear axis (must be PxD6Axis::eX, PxD6Axis::eY or PxD6Axis::eZ) + \param[in] limit The linear limit pair structure + + @see getLinearLimit() + */ + virtual void setLinearLimit(PxD6Axis::Enum axis, const PxJointLinearLimitPair& limit) = 0; + + /** + \brief Get the linear limit for a given linear axis. + + \param[in] axis The limited linear axis (must be PxD6Axis::eX, PxD6Axis::eY or PxD6Axis::eZ) + + \return the linear limit pair structure from desired axis + + @see setLinearLimit() PxJointLinearLimit + */ + virtual PxJointLinearLimitPair getLinearLimit(PxD6Axis::Enum axis) const = 0; + + /** + \brief Set the twist limit for the joint. + + The twist limit controls the range of motion around the twist axis. + + The limit angle range is (-2*Pi, 2*Pi). + + \param[in] limit the twist limit structure + + @see getTwistLimit() PxJointAngularLimitPair + */ + virtual void setTwistLimit(const PxJointAngularLimitPair& limit) = 0; + + /** + \brief Get the twist limit for the joint. + + \return the twist limit structure + + @see setTwistLimit() PxJointAngularLimitPair + */ + virtual PxJointAngularLimitPair getTwistLimit() const = 0; + + /** + \brief Set the swing cone limit for the joint. + + The cone limit is used if either or both swing axes are limited. The extents are + symmetrical and measured in the frame of the parent. If only one swing degree of freedom + is limited, the corresponding value from the cone limit defines the limit range. + + \param[in] limit the cone limit structure + + @see getLimitCone() PxJointLimitCone + */ + virtual void setSwingLimit(const PxJointLimitCone& limit) = 0; + + /** + \brief Get the cone limit for the joint. + + \return the swing limit structure + + @see setLimitCone() PxJointLimitCone + */ + virtual PxJointLimitCone getSwingLimit() const = 0; + + /** + \brief Set a pyramidal swing limit for the joint. + + The pyramid limits will only be used in the following cases: + - both swing Y and Z are limited. The limit shape is then a pyramid. + - Y is limited and Z is locked, or vice versa. The limit shape is an asymmetric angular section, similar to + what is supported for the twist axis. + The remaining cases (Y limited and Z is free, or vice versa) are not supported. + + \param[in] limit the cone limit structure + + @see getLimitCone() PxJointLimitPyramid + */ + virtual void setPyramidSwingLimit(const PxJointLimitPyramid& limit) = 0; + + /** + \brief Get the pyramidal swing limit for the joint. + + \return the swing limit structure + + @see setLimitCone() PxJointLimitPyramid + */ + virtual PxJointLimitPyramid getPyramidSwingLimit() const = 0; + + /** + \brief Set the drive parameters for the specified drive type. + + \param[in] index the type of drive being specified + \param[in] drive the drive parameters + + @see getDrive() PxD6JointDrive + + Default The default drive spring and damping values are zero, the force limit is zero, and no flags are set. + */ + virtual void setDrive(PxD6Drive::Enum index, const PxD6JointDrive& drive) = 0; + + /** + \brief Get the drive parameters for the specified drive type. + + \param[in] index the specified drive type + + @see setDrive() PxD6JointDrive + */ + virtual PxD6JointDrive getDrive(PxD6Drive::Enum index) const = 0; + + /** + \brief Set the drive goal pose + + The goal is relative to the constraint frame of actor[0] + + Default the identity transform + + \param[in] pose The goal drive pose if positional drive is in use. + \param[in] autowake Whether to wake the joint rigids up if it is asleep. + + @see setDrivePosition() + */ + virtual void setDrivePosition(const PxTransform& pose, bool autowake = true) = 0; + + /** + \brief Get the drive goal pose. + + @see getDrivePosition() + */ + virtual PxTransform getDrivePosition() const = 0; + + /** + \brief Set the target goal velocity for drive. + + The velocity is measured in the constraint frame of actor[0] + + \param[in] linear The goal velocity for linear drive + \param[in] angular The goal velocity for angular drive + \param[in] autowake Whether to wake the joint rigids up if it is asleep. + + @see getDriveVelocity() + */ + virtual void setDriveVelocity(const PxVec3& linear, const PxVec3& angular, bool autowake = true) = 0; + + /** + \brief Get the target goal velocity for joint drive. + + \param[in] linear The goal velocity for linear drive + \param[in] angular The goal velocity for angular drive + + @see setDriveVelocity() + */ + virtual void getDriveVelocity(PxVec3& linear, PxVec3& angular) const = 0; + + /** + \brief Set the linear tolerance threshold for projection. Projection is enabled if PxConstraintFlag::ePROJECTION + is set for the joint. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0, PX_MAX_F32)
+ Default: 1e10f + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() PxJoint::setConstraintFlags() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionLinearTolerance(PxReal tolerance) = 0; + + /** + \brief Get the linear tolerance threshold for projection. + + \return the linear tolerance threshold + + @see setProjectionLinearTolerance() + */ + virtual PxReal getProjectionLinearTolerance() const = 0; + + /** + \brief Set the angular tolerance threshold for projection. Projection is enabled if + PxConstraintFlag::ePROJECTION is set for the joint. + + If the joint deviates by more than this angle around its locked angular degrees of freedom, + the solver will move the bodies to close the angle. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0,Pi]
+ Default: Pi + + \param[in] tolerance the angular tolerance threshold in radians + + \note + Angular projection is implemented only for the case of two or three locked angular degrees of freedom. + + @see getProjectionAngularTolerance() PxJoint::setConstraintFlag() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionAngularTolerance(PxReal tolerance) = 0; + + /** + \brief Get the angular tolerance threshold for projection. + + \return tolerance the angular tolerance threshold in radians + + @see setProjectionAngularTolerance() + */ + virtual PxReal getProjectionAngularTolerance() const = 0; + + /** + \brief Returns string name of PxD6Joint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxD6Joint"; } + +protected: + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxD6Joint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxD6Joint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxD6Joint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxD6JointCreate.h b/src/PhysX/physx/include/extensions/PxD6JointCreate.h new file mode 100644 index 000000000..0e2d1964a --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxD6JointCreate.h @@ -0,0 +1,255 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_D6JOINT_CREATE_H +#define PX_D6JOINT_CREATE_H + +#include "common/PxPhysXCommonConfig.h" + +/** \addtogroup extensions + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysics; +class PxRigidActor; +class PxJoint; + +/** + \brief Helper function to create a fixed joint, using either a PxD6Joint or PxFixedJoint. + + For fixed joints it is important that the joint frames have the same orientation. This helper function uses an identity rotation for both. + It is also important that the joint frames have an equivalent position in world space. The function does not check this, so it is up to users + to ensure that this is the case. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] useD6 True to use a PxD6Joint, false to use a PxFixedJoint; + + \return The created joint. + + @see PxD6Joint PxFixedJoint +*/ +PxJoint* PxD6JointCreate_Fixed(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, bool useD6); + +/** + \brief Helper function to create a distance joint, using either a PxD6Joint or PxDistanceJoint. + + This helper function only supports a maximum distance constraint, because PxD6Joint does not support a minimum distance constraint (contrary + to PxDistanceJoint). + + The distance is computed between the joint frames' world-space positions. The joint frames' orientations are irrelevant here so the function + sets them to identity. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] maxDist The maximum allowed distance + \param[in] useD6 True to use a PxD6Joint, false to use a PxDistanceJoint; + + \return The created joint. + + @see PxD6Joint PxDistanceJoint +*/ +PxJoint* PxD6JointCreate_Distance(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, float maxDist, bool useD6); + +/** + \brief Helper function to create a prismatic joint, using either a PxD6Joint or PxPrismaticJoint. + + This function enforces that the joint frames have the same orientation, which is a local frame whose X is the desired translation axis. + This orientation is computed by the function, so users only have to define the desired translation axis (typically 1;0;0 or 0;1;0 or 0;0;1). + + The translation can be limited. Limits are enforced if minLimitmaxLimit the + limits are not enforced and the axis is free. The limit values are computed relative to the position of actor0's joint frame. + + The function creates hard limits, and uses PhysX's default contact distance parameter. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] axis The axis along which objects are allowed to move, expressed in the actors' local space + \param[in] minLimit The minimum allowed position along the axis + \param[in] maxLimit The maximum allowed position along the axis + \param[in] useD6 True to use a PxD6Joint, false to use a PxPrismaticJoint; + + \return The created joint. + + @see PxD6Joint PxPrismaticJoint +*/ +PxJoint* PxD6JointCreate_Prismatic(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float minLimit, float maxLimit, bool useD6); + +/** + \brief Helper function to create a revolute joint, using either a PxD6Joint or PxRevoluteJoint. + + This function enforces that the joint frames have the same orientation, which is a local frame whose X is the desired rotation axis. + This orientation is computed by the function, so users only have to define the desired rotation axis (typically 1;0;0 or 0;1;0 or 0;0;1). + + The rotation can be limited. Limits are enforced if minLimitmaxLimit the + limits are not enforced and the axis is free. The limit values are computed relative to the rotation of actor0's joint frame. + + The function creates hard limits, and uses PhysX's default contact distance parameter. + + Limits are expressed in radians. Allowed range is ]-2*PI;+2*PI[ + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] axis The axis around which objects are allowed to move, expressed in the actors' local space + \param[in] minLimit The minimum allowed rotation along the axis + \param[in] maxLimit The maximum allowed rotation along the axis + \param[in] useD6 True to use a PxD6Joint, false to use a PxRevoluteJoint; + + \return The created joint. + + @see PxD6Joint PxRevoluteJoint +*/ +PxJoint* PxD6JointCreate_Revolute(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float minLimit, float maxLimit, bool useD6); + +/** + \brief Helper function to create a spherical joint, using either a PxD6Joint or PxSphericalJoint. + + This function supports a cone limit shape, defined by a cone axis and two angular limit values. + + This function enforces that the joint frames have the same orientation, which is a local frame whose X is the desired cone axis. + This orientation is computed by the function, so users only have to define the desired cone axis (typically 1;0;0 or 0;1;0 or 0;0;1). + + The rotations can be limited. Limits are enforced if limit1>0 and limit2>0. Otherwise the motion is free. The limit values define an ellipse, + which is the cross-section of the cone limit shape. + + The function creates hard limits, and uses PhysX's default contact distance parameter. + + Limits are expressed in radians. Allowed range is ]0;PI[. Limits are symmetric around the cone axis. + + The cone axis is equivalent to the twist axis for the D6 joint. The twist motion is not limited. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] axis The cone axis, expressed in the actors' local space + \param[in] limit1 Max angular limit for the ellipse along the joint frame's second axis (first axis = cone axis) + \param[in] limit2 Max angular limit for the ellipse along the joint frame's third axis (first axis = cone axis) + \param[in] useD6 True to use a PxD6Joint, false to use a PxSphericalJoint; + + \return The created joint. + + @see PxD6Joint PxSphericalJoint +*/ +PxJoint* PxD6JointCreate_Spherical(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float limit1, float limit2, bool useD6); + +/** + \brief Helper function to create a spherical joint, using either a PxD6Joint or PxSphericalJoint. + + This function supports a cone limit shape, defined by two pairs of angular limit values. This can be used to create an asymmetric cone. If the + angular limit values are symmetric (i.e. minLimit1=-maxLimit1 and minLimit2=-maxLimit2) then the cone axis is the X axis in actor0's space. + If the limits are not symmetric, the function rotates the cone axis accordingly so that limits remain symmetric for PhysX. If this happens, + the initial joint frames will be different for both actors. By default minLimit1/maxLimit1 are limits around the joint's Y axis, and + minLimit2/maxLimit2 are limits around the joint's Z axis. + + The function creates hard limits, and uses PhysX's default contact distance parameter. + + Limits are expressed in radians. Allowed range is ]-PI;PI[. + + The cone axis is equivalent to the twist axis for the D6 joint. The twist motion is not limited. + + The returned apiroty and apirotz values can later be added to retrieved Y and Z swing angle values (from the joint), to remap + angle values to the given input range. + + \param[out] apiroty Amount of rotation around Y used to setup actor0's joint frame + \param[out] apirotz Amount of rotation around Z used to setup actor0's joint frame + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] minLimit1 Min angular limit along the joint frame's second axis (first axis = cone axis) + \param[in] maxLimit1 Max angular limit along the joint frame's second axis (first axis = cone axis) + \param[in] minLimit2 Min angular limit along the joint frame's third axis (first axis = cone axis) + \param[in] maxLimit2 Max angular limit along the joint frame's third axis (first axis = cone axis) + \param[in] useD6 True to use a PxD6Joint, false to use a PxSphericalJoint; + + \return The created joint. + + @see PxD6Joint PxSphericalJoint +*/ +PxJoint* PxD6JointCreate_GenericCone(float& apiroty, float& apirotz, PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, float minLimit1, float maxLimit1, float minLimit2, float maxLimit2, bool useD6); + + +/** + \brief Helper function to create a D6 joint with pyramidal swing limits. + + This function supports a pyramid limit shape, defined by two pairs of angular limit values. This can be used to create an asymmetric pyramid. If the + angular limit values are symmetric (i.e. minLimit1=-maxLimit1 and minLimit2=-maxLimit2) then the pyramid axis is the X axis in actor0's space. + By default minLimit1/maxLimit1 are limits around the joint's Y axis, and minLimit2/maxLimit2 are limits around the joint's Z axis. + + The function creates hard limits, and uses PhysX's default contact distance parameter. + + Limits are expressed in radians. Allowed range is ]-PI;PI[. + + The pyramid axis is equivalent to the twist axis for the D6 joint. The twist motion is not limited. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] axis The pyramid axis, expressed in the actors' local space + \param[in] minLimit1 Min angular limit along the joint frame's second axis (first axis = pyramid axis) + \param[in] maxLimit1 Max angular limit along the joint frame's second axis (first axis = pyramid axis) + \param[in] minLimit2 Min angular limit along the joint frame's third axis (first axis = pyramid axis) + \param[in] maxLimit2 Max angular limit along the joint frame's third axis (first axis = pyramid axis) + + \return The created joint. + + @see PxD6Joint +*/ +PxJoint* PxD6JointCreate_Pyramid(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, + float minLimit1, float maxLimit1, float minLimit2, float maxLimit2); + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxDefaultAllocator.h b/src/PhysX/physx/include/extensions/PxDefaultAllocator.h new file mode 100644 index 000000000..86405a44b --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDefaultAllocator.h @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_DEFAULT_ALLOCATOR_H +#define PX_DEFAULT_ALLOCATOR_H +/** \addtogroup extensions + @{ +*/ + +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxAssert.h" +#include "common/PxPhysXCommonConfig.h" + +#include + +#if PX_WINDOWS || PX_LINUX_FAMILY || PX_SWITCH +#include +#endif + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#if PX_WINDOWS +// on win32 we only have 8-byte alignment guaranteed, but the CRT provides special aligned allocation fns +PX_FORCE_INLINE void* platformAlignedAlloc(size_t size) +{ + return _aligned_malloc(size, 16); +} + +PX_FORCE_INLINE void platformAlignedFree(void* ptr) +{ + _aligned_free(ptr); +} +#elif PX_LINUX_FAMILY || PX_SWITCH +PX_FORCE_INLINE void* platformAlignedAlloc(size_t size) +{ + return ::memalign(16, size); +} + +PX_FORCE_INLINE void platformAlignedFree(void* ptr) +{ + ::free(ptr); +} +#else +// on all other platforms we get 16-byte alignment by default +PX_FORCE_INLINE void* platformAlignedAlloc(size_t size) +{ + return ::malloc(size); +} + +PX_FORCE_INLINE void platformAlignedFree(void* ptr) +{ + ::free(ptr); +} +#endif + +/** +\brief default implementation of the allocator interface required by the SDK +*/ +class PxDefaultAllocator : public PxAllocatorCallback +{ +public: + void* allocate(size_t size, const char*, const char*, int) + { + void* ptr = platformAlignedAlloc(size); + PX_ASSERT((reinterpret_cast(ptr) & 15)==0); + return ptr; + } + + void deallocate(void* ptr) + { + platformAlignedFree(ptr); + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h b/src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h new file mode 100644 index 000000000..458676b32 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_DEFAULT_CPU_DISPATCHER_H +#define PX_PHYSICS_EXTENSIONS_DEFAULT_CPU_DISPATCHER_H +/** \addtogroup extensions + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "task/PxCpuDispatcher.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief A default implementation for a CPU task dispatcher. + +@see PxDefaultCpuDispatcherCreate() PxCpuDispatcher +*/ +class PxDefaultCpuDispatcher: public PxCpuDispatcher +{ +public: + /** + \brief Deletes the dispatcher. + + Do not keep a reference to the deleted instance. + + @see PxDefaultCpuDispatcherCreate() + */ + virtual void release() = 0; + + /** + \brief Enables profiling at task level. + + \note By default enabled only in profiling builds. + + \param[in] runProfiled True if tasks should be profiled. + */ + virtual void setRunProfiled(bool runProfiled) = 0; + + /** + \brief Checks if profiling is enabled at task level. + + \return True if tasks should be profiled. + */ + virtual bool getRunProfiled() const = 0; +}; + + +/** +\brief Create default dispatcher, extensions SDK needs to be initialized first. + +\param[in] numThreads Number of worker threads the dispatcher should use. +\param[in] affinityMasks Array with affinity mask for each thread. If not defined, default masks will be used. + +\note numThreads may be zero in which case no worker thread are initialized and +simulation tasks will be executed on the thread that calls PxScene::simulate() + +@see PxDefaultCpuDispatcher +*/ +PxDefaultCpuDispatcher* PxDefaultCpuDispatcherCreate(PxU32 numThreads, PxU32* affinityMasks = NULL); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h b/src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h new file mode 100644 index 000000000..7880c12ed --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h @@ -0,0 +1,62 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_EXTENSIONS_DEFAULT_ERROR_CALLBACK_H +#define PX_PHYSICS_EXTENSIONS_DEFAULT_ERROR_CALLBACK_H + +#include "foundation/PxErrorCallback.h" +#include "PxPhysXConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief default implementation of the error callback + + This class is provided in order to enable the SDK to be started with the minimum of user code. Typically an application + will use its own error callback, and log the error to file or otherwise make it visible. Warnings and error messages from + the SDK are usually indicative that changes are required in order for PhysX to function correctly, and should not be ignored. + */ + + class PxDefaultErrorCallback : public PxErrorCallback + { + public: + PxDefaultErrorCallback(); + ~PxDefaultErrorCallback(); + + virtual void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line); + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif diff --git a/src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h b/src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h new file mode 100644 index 000000000..c4edb5e11 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h @@ -0,0 +1,263 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_DEFAULTSIMULATIONFILTERSHADER_H +#define PX_PHYSICS_EXTENSIONS_DEFAULTSIMULATIONFILTERSHADER_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" + +#include "PxFiltering.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxActor; + +/** +\brief 64-bit mask used for collision filtering. + +The collision filtering equation for 2 objects o0 and o1 is: + +
 (G0 op0 K0) op2 (G1 op1 K1) == b 
+ +with + +
    +
  • G0 = PxGroupsMask for object o0. See PxSetGroupsMask
  • +
  • G1 = PxGroupsMask for object o1. See PxSetGroupsMask
  • +
  • K0 = filtering constant 0. See PxSetFilterConstants
  • +
  • K1 = filtering constant 1. See PxSetFilterConstants
  • +
  • b = filtering boolean. See PxSetFilterBool
  • +
  • op0, op1, op2 = filtering operations. See PxSetFilterOps
  • +
+ +If the filtering equation is true, collision detection is enabled. + +@see PxSetFilterOps() +*/ +class PxGroupsMask +{ +public: + PX_INLINE PxGroupsMask():bits0(0),bits1(0),bits2(0),bits3(0) {} + PX_INLINE ~PxGroupsMask() {} + + PxU16 bits0, bits1, bits2, bits3; +}; + +/** +\brief Collision filtering operations. + +@see PxGroupsMask +*/ +struct PxFilterOp +{ + enum Enum + { + PX_FILTEROP_AND, + PX_FILTEROP_OR, + PX_FILTEROP_XOR, + PX_FILTEROP_NAND, + PX_FILTEROP_NOR, + PX_FILTEROP_NXOR, + PX_FILTEROP_SWAP_AND + }; +}; + +/** +\brief Implementation of a simple filter shader that emulates PhysX 2.8.x filtering + +This shader provides the following logic: +\li If one of the two filter objects is a trigger, the pair is acccepted and #PxPairFlag::eTRIGGER_DEFAULT will be used for trigger reports +\li Else, if the filter mask logic (see further below) discards the pair it will be suppressed (#PxFilterFlag::eSUPPRESS) +\li Else, the pair gets accepted and collision response gets enabled (#PxPairFlag::eCONTACT_DEFAULT) + +Filter mask logic: +Given the two #PxFilterData structures fd0 and fd1 of two collision objects, the pair passes the filter if the following +conditions are met: + + 1) Collision groups of the pair are enabled + 2) Collision filtering equation is satisfied + +@see PxSimulationFilterShader +*/ + +PxFilterFlags PxDefaultSimulationFilterShader( + PxFilterObjectAttributes attributes0, + PxFilterData filterData0, + PxFilterObjectAttributes attributes1, + PxFilterData filterData1, + PxPairFlags& pairFlags, + const void* constantBlock, + PxU32 constantBlockSize); + +/** + \brief Determines if collision detection is performed between a pair of groups + + \note Collision group is an integer between 0 and 31. + + \param[in] group1 First Group + \param[in] group2 Second Group + + \return True if the groups could collide + + @see PxSetGroupCollisionFlag +*/ +bool PxGetGroupCollisionFlag(const PxU16 group1, const PxU16 group2); + +/** + \brief Specifies if collision should be performed by a pair of groups + + \note Collision group is an integer between 0 and 31. + + \param[in] group1 First Group + \param[in] group2 Second Group + \param[in] enable True to enable collision between the groups + + @see PxGetGroupCollisionFlag +*/ +void PxSetGroupCollisionFlag(const PxU16 group1, const PxU16 group2, const bool enable); + +/** + \brief Retrieves the value set with PxSetGroup() + + \note Collision group is an integer between 0 and 31. + + \param[in] actor The actor + + \return The collision group this actor belongs to + + @see PxSetGroup +*/ +PxU16 PxGetGroup(const PxActor& actor); + +/** + \brief Sets which collision group this actor is part of + + \note Collision group is an integer between 0 and 31. + + \param[in] actor The actor + \param[in] collisionGroup Collision group this actor belongs to + + @see PxGetGroup +*/ +void PxSetGroup(PxActor& actor, const PxU16 collisionGroup); + +/** +\brief Retrieves filtering operation. See comments for PxGroupsMask + +\param[out] op0 First filter operator. +\param[out] op1 Second filter operator. +\param[out] op2 Third filter operator. + +@see PxSetFilterOps PxSetFilterBool PxSetFilterConstants +*/ +void PxGetFilterOps(PxFilterOp::Enum& op0, PxFilterOp::Enum& op1, PxFilterOp::Enum& op2); + +/** +\brief Setups filtering operations. See comments for PxGroupsMask + +\param[in] op0 Filter op 0. +\param[in] op1 Filter op 1. +\param[in] op2 Filter op 2. + +@see PxSetFilterBool PxSetFilterConstants +*/ +void PxSetFilterOps(const PxFilterOp::Enum& op0, const PxFilterOp::Enum& op1, const PxFilterOp::Enum& op2); + +/** +\brief Retrieves filtering's boolean value. See comments for PxGroupsMask + +\return flag Boolean value for filter. + +@see PxSetFilterBool PxSetFilterConstants +*/ +bool PxGetFilterBool(); + +/** +\brief Setups filtering's boolean value. See comments for PxGroupsMask + +\param[in] enable Boolean value for filter. + +@see PxSetFilterOps PxSsetFilterConstants +*/ +void PxSetFilterBool(const bool enable); + +/** +\brief Gets filtering constant K0 and K1. See comments for PxGroupsMask + +\param[out] c0 the filtering constants, as a mask. See #PxGroupsMask. +\param[out] c1 the filtering constants, as a mask. See #PxGroupsMask. + +@see PxSetFilterOps PxSetFilterBool PxSetFilterConstants +*/ +void PxGetFilterConstants(PxGroupsMask& c0, PxGroupsMask& c1); + +/** +\brief Setups filtering's K0 and K1 value. See comments for PxGroupsMask + +\param[in] c0 The new group mask. See #PxGroupsMask. +\param[in] c1 The new group mask. See #PxGroupsMask. + +@see PxSetFilterOps PxSetFilterBool PxGetFilterConstants +*/ +void PxSetFilterConstants(const PxGroupsMask& c0, const PxGroupsMask& c1); + +/** +\brief Gets 64-bit mask used for collision filtering. See comments for PxGroupsMask + +\param[in] actor The actor + +\return The group mask for the actor. + +@see PxSetGroupsMask() +*/ +PxGroupsMask PxGetGroupsMask(const PxActor& actor); + +/** +\brief Sets 64-bit mask used for collision filtering. See comments for PxGroupsMask + +\param[in] actor The actor +\param[in] mask The group mask to set for the actor. + +@see PxGetGroupsMask() +*/ +void PxSetGroupsMask(PxActor& actor, const PxGroupsMask& mask); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxDefaultStreams.h b/src/PhysX/physx/include/extensions/PxDefaultStreams.h new file mode 100644 index 000000000..9eddeda5e --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDefaultStreams.h @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_DEFAULT_STREAMS_H +#define PX_PHYSICS_EXTENSIONS_DEFAULT_STREAMS_H +/** \addtogroup extensions + @{ +*/ + +#include +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxIO.h" +#include "PxFoundation.h" + +typedef FILE* PxFileHandle; + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief default implementation of a memory write stream + +@see PxOutputStream +*/ + +class PxDefaultMemoryOutputStream: public PxOutputStream +{ +public: + PxDefaultMemoryOutputStream(PxAllocatorCallback &allocator = PxGetFoundation().getAllocatorCallback()); + virtual ~PxDefaultMemoryOutputStream(); + + virtual PxU32 write(const void* src, PxU32 count); + + virtual PxU32 getSize() const { return mSize; } + virtual PxU8* getData() const { return mData; } + +private: + PxDefaultMemoryOutputStream(const PxDefaultMemoryOutputStream&); + PxDefaultMemoryOutputStream& operator=(const PxDefaultMemoryOutputStream&); + + PxAllocatorCallback& mAllocator; + PxU8* mData; + PxU32 mSize; + PxU32 mCapacity; +}; + +/** +\brief default implementation of a memory read stream + +@see PxInputData +*/ + +class PxDefaultMemoryInputData: public PxInputData +{ +public: + PxDefaultMemoryInputData(PxU8* data, PxU32 length); + + virtual PxU32 read(void* dest, PxU32 count); + virtual PxU32 getLength() const; + virtual void seek(PxU32 pos); + virtual PxU32 tell() const; + +private: + PxU32 mSize; + const PxU8* mData; + PxU32 mPos; +}; + + + +/** +\brief default implementation of a file write stream + +@see PxOutputStream +*/ + +class PxDefaultFileOutputStream: public PxOutputStream +{ +public: + PxDefaultFileOutputStream(const char* name); + virtual ~PxDefaultFileOutputStream(); + + virtual PxU32 write(const void* src, PxU32 count); + virtual bool isValid(); +private: + PxFileHandle mFile; +}; + + +/** +\brief default implementation of a file read stream + +@see PxInputData +*/ + +class PxDefaultFileInputData: public PxInputData +{ +public: + PxDefaultFileInputData(const char* name); + virtual ~PxDefaultFileInputData(); + + virtual PxU32 read(void* dest, PxU32 count); + virtual void seek(PxU32 pos); + virtual PxU32 tell() const; + virtual PxU32 getLength() const; + + bool isValid() const; +private: + PxFileHandle mFile; + PxU32 mLength; +}; + +#if !PX_DOXYGEN +} +#endif + +/** @} */ + +#endif + diff --git a/src/PhysX/physx/include/extensions/PxDistanceJoint.h b/src/PhysX/physx/include/extensions/PxDistanceJoint.h new file mode 100644 index 000000000..bd752d25d --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDistanceJoint.h @@ -0,0 +1,269 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_DISTANCEJOINT_H +#define PX_DISTANCEJOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxDistanceJoint; + +/** +\brief Create a distance Joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxDistanceJoint +*/ +PxDistanceJoint* PxDistanceJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + + +/** +\brief flags for configuring the drive of a PxDistanceJoint + +@see PxDistanceJoint +*/ +struct PxDistanceJointFlag +{ + enum Enum + { + eMAX_DISTANCE_ENABLED = 1<<1, + eMIN_DISTANCE_ENABLED = 1<<2, + eSPRING_ENABLED = 1<<3 + }; +}; + +typedef PxFlags PxDistanceJointFlags; +PX_FLAGS_OPERATORS(PxDistanceJointFlag::Enum, PxU16) + +/** +\brief a joint that maintains an upper or lower bound (or both) on the distance between two points on different objects + +@see PxDistanceJointCreate PxJoint +*/ +class PxDistanceJoint : public PxJoint +{ +public: + + /** + \brief Return the current distance of the joint + */ + virtual PxReal getDistance() const = 0; + + /** + \brief Set the allowed minimum distance for the joint. + + The minimum distance must be no more than the maximum distance + + Default 0.0f + Range [0, PX_MAX_F32) + + \param[in] distance the minimum distance + + @see PxDistanceJoint::minDistance, PxDistanceJointFlag::eMIN_DISTANCE_ENABLED getMinDistance() + */ + virtual void setMinDistance(PxReal distance) = 0; + + /** + \brief Get the allowed minimum distance for the joint. + + \return the allowed minimum distance + + @see PxDistanceJoint::minDistance, PxDistanceJointFlag::eMIN_DISTANCE_ENABLED setMinDistance() + */ + virtual PxReal getMinDistance() const = 0; + + /** + \brief Set the allowed maximum distance for the joint. + + The maximum distance must be no less than the minimum distance. + + Default 0.0f + Range [0, PX_MAX_F32) + + \param[in] distance the maximum distance + + @see PxDistanceJoint::maxDistance, PxDistanceJointFlag::eMAX_DISTANCE_ENABLED getMinDistance() + */ + virtual void setMaxDistance(PxReal distance) = 0; + + /** + \brief Get the allowed maximum distance for the joint. + + \return the allowed maximum distance + + @see PxDistanceJoint::maxDistance, PxDistanceJointFlag::eMAX_DISTANCE_ENABLED setMaxDistance() + */ + virtual PxReal getMaxDistance() const = 0; + + /** + \brief Set the error tolerance of the joint. + + \param[in] tolerance the distance beyond the allowed range at which the joint becomes active + + @see PxDistanceJoint::tolerance, getTolerance() + */ + virtual void setTolerance(PxReal tolerance) = 0; + + /** + \brief Get the error tolerance of the joint. + + the distance beyond the joint's [min, max] range before the joint becomes active. + + Default 0.25f * PxTolerancesScale::length + Range (0, PX_MAX_F32) + + This value should be used to ensure that if the minimum distance is zero and the + spring function is in use, the rest length of the spring is non-zero. + + @see PxDistanceJoint::tolerance, setTolerance() + */ + virtual PxReal getTolerance() const = 0; + + /** + \brief Set the strength of the joint spring. + + The spring is used if enabled, and the distance exceeds the range [min-error, max+error]. + + Default 0.0f + Range [0, PX_MAX_F32) + + \param[in] stiffness the spring strength of the joint + + @see PxDistanceJointFlag::eSPRING_ENABLED getStiffness() + */ + virtual void setStiffness(PxReal stiffness) = 0; + + /** + \brief Get the strength of the joint spring. + + \return stiffness the spring strength of the joint + + @see PxDistanceJointFlag::eSPRING_ENABLED setStiffness() + */ + virtual PxReal getStiffness() const = 0; + + /** + \brief Set the damping of the joint spring. + + The spring is used if enabled, and the distance exceeds the range [min-error, max+error]. + + Default 0.0f + Range [0, PX_MAX_F32) + + \param[in] damping the degree of damping of the joint spring of the joint + + @see PxDistanceJointFlag::eSPRING_ENABLED setDamping() + */ + virtual void setDamping(PxReal damping) = 0; + + /** + \brief Get the damping of the joint spring. + + \return the degree of damping of the joint spring of the joint + + @see PxDistanceJointFlag::eSPRING_ENABLED setDamping() + */ + virtual PxReal getDamping() const = 0; + + /** + \brief Set the flags specific to the Distance Joint. + + Default PxDistanceJointFlag::eMAX_DISTANCE_ENABLED + + \param[in] flags The joint flags. + + @see PxDistanceJointFlag setFlag() getFlags() + */ + virtual void setDistanceJointFlags(PxDistanceJointFlags flags) = 0; + + /** + \brief Set a single flag specific to a Distance Joint to true or false. + + \param[in] flag The flag to set or clear. + \param[in] value the value to which to set the flag + + @see PxDistanceJointFlag, getFlags() setFlags() + */ + virtual void setDistanceJointFlag(PxDistanceJointFlag::Enum flag, bool value) = 0; + + /** + \brief Get the flags specific to the Distance Joint. + + \return the joint flags + + @see PxDistanceJoint::flags, PxDistanceJointFlag setFlag() setFlags() + */ + virtual PxDistanceJointFlags getDistanceJointFlags() const = 0; + + /** + \brief Returns string name of PxDistanceJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxDistanceJoint"; } + +protected: + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxDistanceJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxDistanceJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxDistanceJoint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxExtensionsAPI.h b/src/PhysX/physx/include/extensions/PxExtensionsAPI.h new file mode 100644 index 000000000..6647b3cc2 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxExtensionsAPI.h @@ -0,0 +1,88 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_EXTENSIONS_API_H +#define PX_EXTENSIONS_API_H +/** \addtogroup extensions + @{ +*/ + +#include "foundation/PxErrorCallback.h" +#include "extensions/PxDefaultAllocator.h" +#include "extensions/PxConstraintExt.h" +#include "extensions/PxDistanceJoint.h" +#include "extensions/PxContactJoint.h" +#include "extensions/PxFixedJoint.h" +#include "extensions/PxPrismaticJoint.h" +#include "extensions/PxRevoluteJoint.h" +#include "extensions/PxSphericalJoint.h" +#include "extensions/PxD6Joint.h" +#include "extensions/PxDefaultSimulationFilterShader.h" +#include "extensions/PxDefaultErrorCallback.h" +#include "extensions/PxDefaultStreams.h" +#include "extensions/PxRigidActorExt.h" +#include "extensions/PxRigidBodyExt.h" +#include "extensions/PxShapeExt.h" +#include "extensions/PxTriangleMeshExt.h" +#include "extensions/PxSerialization.h" +#include "extensions/PxDefaultCpuDispatcher.h" +#include "extensions/PxSmoothNormals.h" +#include "extensions/PxSimpleFactory.h" +#include "extensions/PxStringTableExt.h" +#include "extensions/PxBroadPhaseExt.h" +#include "extensions/PxMassProperties.h" +#include "extensions/PxSceneQueryExt.h" + +/** \brief Initialize the PhysXExtensions library. + +This should be called before calling any functions or methods in extensions which may require allocation. +\note This function does not need to be called before creating a PxDefaultAllocator object. + +\param physics a PxPhysics object +\param pvd an PxPvd (PhysX Visual Debugger) object + +@see PxCloseExtensions PxFoundation PxPhysics +*/ + +PX_C_EXPORT bool PX_CALL_CONV PxInitExtensions(physx::PxPhysics& physics, physx::PxPvd* pvd); + +/** \brief Shut down the PhysXExtensions library. + +This function should be called to cleanly shut down the PhysXExtensions library before application exit. + +\note This function is required to be called to release foundation usage. + +@see PxInitExtensions +*/ + +PX_C_EXPORT void PX_CALL_CONV PxCloseExtensions(); + +/** @} */ +#endif // PX_EXTENSIONS_API_H diff --git a/src/PhysX/physx/include/extensions/PxFixedJoint.h b/src/PhysX/physx/include/extensions/PxFixedJoint.h new file mode 100644 index 000000000..fff4b84e9 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxFixedJoint.h @@ -0,0 +1,159 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_FIXEDJOINT_H +#define PX_FIXEDJOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxFixedJoint; + +/** +\brief Create a fixed joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxFixedJoint +*/ +PxFixedJoint* PxFixedJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + +/** + \brief A fixed joint permits no relative movement between two bodies. ie the bodies are glued together. + + \image html fixedJoint.png + + @see PxFixedJointCreate() PxJoint +*/ +class PxFixedJoint : public PxJoint +{ +public: + + /** + \brief Set the linear tolerance threshold for projection. Projection is enabled if PxConstraintFlag::ePROJECTION + is set for the joint. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0, PX_MAX_F32)
+ Default: 1e10f + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() PxJoint::setConstraintFlags() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionLinearTolerance(PxReal tolerance) = 0; + + /** + \brief Get the linear tolerance threshold for projection. + + \return the linear tolerance threshold + + @see setProjectionLinearTolerance() PxJoint::setConstraintFlag() + */ + virtual PxReal getProjectionLinearTolerance() const = 0; + + /** + \brief Set the angular tolerance threshold for projection. Projection is enabled if + PxConstraintFlag::ePROJECTION is set for the joint. + + If the joint deviates by more than this angle around its locked angular degrees of freedom, + the solver will move the bodies to close the angle. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0,Pi]
+ Default: Pi + + \param[in] tolerance the angular tolerance threshold in radians + + @see getProjectionAngularTolerance() PxJoint::setConstraintFlag() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionAngularTolerance(PxReal tolerance) = 0; + + /** + \brief Get the angular tolerance threshold for projection. + + \return the angular tolerance threshold in radians + + @see setProjectionAngularTolerance() + */ + virtual PxReal getProjectionAngularTolerance() const = 0; + + /** + \brief Returns string name of PxFixedJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxFixedJoint"; } + +protected: + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxFixedJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxFixedJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxFixedJoint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxJoint.h b/src/PhysX/physx/include/extensions/PxJoint.h new file mode 100644 index 000000000..14898d6be --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxJoint.h @@ -0,0 +1,419 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_JOINTCONSTRAINT_H +#define PX_JOINTCONSTRAINT_H +/** \addtogroup extensions + @{ +*/ + +#include "foundation/PxTransform.h" +#include "PxRigidActor.h" +#include "PxConstraint.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxRigidActor; +class PxScene; +class PxPhysics; +class PxConstraint; + +/** +\brief an enumeration of PhysX' built-in joint types + +@see PxJoint +*/ +struct PxJointConcreteType +{ + enum Enum + { + eSPHERICAL = PxConcreteType::eFIRST_PHYSX_EXTENSION, + eREVOLUTE, + ePRISMATIC, + eFIXED, + eDISTANCE, + eD6, + eCONTACT, + eLast + }; +}; + +PX_DEFINE_TYPEINFO(PxJoint, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxD6Joint, PxJointConcreteType::eD6) +PX_DEFINE_TYPEINFO(PxDistanceJoint, PxJointConcreteType::eDISTANCE) +PX_DEFINE_TYPEINFO(PxContactJoint, PxJointConcreteType::eCONTACT) +PX_DEFINE_TYPEINFO(PxFixedJoint, PxJointConcreteType::eFIXED) +PX_DEFINE_TYPEINFO(PxPrismaticJoint, PxJointConcreteType::ePRISMATIC) +PX_DEFINE_TYPEINFO(PxRevoluteJoint, PxJointConcreteType::eREVOLUTE) +PX_DEFINE_TYPEINFO(PxSphericalJoint, PxJointConcreteType::eSPHERICAL) + + +/** +\brief an enumeration for specifying one or other of the actors referenced by a joint + +@see PxJoint +*/ + +struct PxJointActorIndex +{ + enum Enum + { + eACTOR0, + eACTOR1, + COUNT + }; +}; + +/** +\brief a base interface providing common functionality for PhysX joints +*/ + +class PxJoint : public PxBase +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + /** + \brief Set the actors for this joint. + + An actor may be NULL to indicate the world frame. At most one of the actors may be NULL. + + \param[in] actor0 the first actor. + \param[in] actor1 the second actor + + @see getActors() + */ + virtual void setActors(PxRigidActor* actor0, PxRigidActor* actor1) = 0; + + /** + \brief Get the actors for this joint. + + \param[out] actor0 the first actor. + \param[out] actor1 the second actor + + @see setActors() + */ + virtual void getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const = 0; + + /** + \brief Set the joint local pose for an actor. + + This is the relative pose which locates the joint frame relative to the actor. + + \param[in] actor 0 for the first actor, 1 for the second actor. + \param[in] localPose the local pose for the actor this joint + + @see getLocalPose() + */ + virtual void setLocalPose(PxJointActorIndex::Enum actor, const PxTransform& localPose) = 0; + + /** + \brief get the joint local pose for an actor. + + \param[in] actor 0 for the first actor, 1 for the second actor. + + return the local pose for this joint + + @see setLocalPose() + */ + virtual PxTransform getLocalPose(PxJointActorIndex::Enum actor) const = 0; + + /** + \brief get the relative pose for this joint + + This function returns the pose of the joint frame of actor1 relative to actor0 + + */ + virtual PxTransform getRelativeTransform() const = 0; + + /** + \brief get the relative linear velocity of the joint + + This function returns the linear velocity of the origin of the constraint frame of actor1, relative to the origin of the constraint + frame of actor0. The value is returned in the constraint frame of actor0 + */ + virtual PxVec3 getRelativeLinearVelocity() const = 0; + + /** + \brief get the relative angular velocity of the joint + + This function returns the angular velocity of actor1 relative to actor0. The value is returned in the constraint frame of actor0 + */ + virtual PxVec3 getRelativeAngularVelocity() const = 0; + + /** + \brief set the break force for this joint. + + if the constraint force or torque on the joint exceeds the specified values, the joint will break, + at which point it will not constrain the two actors and the flag PxConstraintFlag::eBROKEN will be set. The + force and torque are measured in the joint frame of the first actor + + \param[in] force the maximum force the joint can apply before breaking + \param[in] torque the maximum torque the joint can apply before breaking + */ + virtual void setBreakForce(PxReal force, PxReal torque) = 0; + + /** + \brief get the break force for this joint. + + \param[out] force the maximum force the joint can apply before breaking + \param[out] torque the maximum torque the joint can apply before breaking + + @see setBreakForce() + */ + virtual void getBreakForce(PxReal& force, PxReal& torque) const = 0; + + /** + \brief set the constraint flags for this joint. + + \param[in] flags the constraint flags + + @see PxConstraintFlag + */ + virtual void setConstraintFlags(PxConstraintFlags flags) = 0; + + /** + \brief set a constraint flags for this joint to a specified value. + + \param[in] flag the constraint flag + \param[in] value the value to which to set the flag + + @see PxConstraintFlag + */ + virtual void setConstraintFlag(PxConstraintFlag::Enum flag, bool value) = 0; + + /** + \brief get the constraint flags for this joint. + + \return the constraint flags + + @see PxConstraintFlag + */ + virtual PxConstraintFlags getConstraintFlags() const = 0; + + /** + \brief set the inverse mass scale for actor0. + + \param[in] invMassScale the scale to apply to the inverse mass of actor 0 for resolving this constraint + + @see getInvMassScale0 + */ + virtual void setInvMassScale0(PxReal invMassScale) = 0; + + /** + \brief get the inverse mass scale for actor0. + + \return inverse mass scale for actor0 + + @see setInvMassScale0 + */ + virtual PxReal getInvMassScale0() const = 0; + + /** + \brief set the inverse inertia scale for actor0. + + \param[in] invInertiaScale the scale to apply to the inverse inertia of actor0 for resolving this constraint + + @see getInvMassScale0 + */ + virtual void setInvInertiaScale0(PxReal invInertiaScale) = 0; + + /** + \brief get the inverse inertia scale for actor0. + + \return inverse inertia scale for actor0 + + @see setInvInertiaScale0 + */ + virtual PxReal getInvInertiaScale0() const = 0; + + /** + \brief set the inverse mass scale for actor1. + + \param[in] invMassScale the scale to apply to the inverse mass of actor 1 for resolving this constraint + + @see getInvMassScale1 + */ + virtual void setInvMassScale1(PxReal invMassScale) = 0; + + /** + \brief get the inverse mass scale for actor1. + + \return inverse mass scale for actor1 + + @see setInvMassScale1 + */ + virtual PxReal getInvMassScale1() const = 0; + + /** + \brief set the inverse inertia scale for actor1. + + \param[in] invInertiaScale the scale to apply to the inverse inertia of actor1 for resolving this constraint + + @see getInvInertiaScale1 + */ + virtual void setInvInertiaScale1(PxReal invInertiaScale) = 0; + + /** + \brief get the inverse inertia scale for actor1. + + \return inverse inertia scale for actor1 + + @see setInvInertiaScale1 + */ + virtual PxReal getInvInertiaScale1() const = 0; + + /** + \brief Retrieves the PxConstraint corresponding to this joint. + + This can be used to determine, among other things, the force applied at the joint. + + \return the constraint + */ + virtual PxConstraint* getConstraint() const = 0; + + /** + \brief Sets a name string for the object that can be retrieved with getName(). + + This is for debugging and is not used by the SDK. The string is not copied by the SDK, + only the pointer is stored. + + \param[in] name String to set the objects name to. + + @see getName() + */ + virtual void setName(const char* name) = 0; + + /** + \brief Retrieves the name string set with setName(). + + \return Name string associated with object. + + @see setName() + */ + virtual const char* getName() const = 0; + + /** + \brief Deletes the joint. + + \note This call does not wake up the connected rigid bodies. + */ + virtual void release() = 0; + + /** + \brief Retrieves the scene which this joint belongs to. + + \return Owner Scene. NULL if not part of a scene. + + @see PxScene + */ + virtual PxScene* getScene() const = 0; + + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. + + //serialization + + /** + \brief Put class meta data in stream, used for serialization + */ + static void getBinaryMetaData(PxOutputStream& stream); + + //~serialization + +protected: + virtual ~PxJoint() {} + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxJoint(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags), userData(NULL) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxJoint(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxJoint", name) || PxBase::isKindOf(name); } + + //~serialization +}; + +class PxSpring +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + PxReal stiffness; //!< the spring strength of the drive: that is, the force proportional to the position error + PxReal damping; //!< the damping strength of the drive: that is, the force proportional to the velocity error + + PxSpring(PxReal stiffness_, PxReal damping_): stiffness(stiffness_), damping(damping_) {} +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** \brief Helper function to setup a joint's global frame + + This replaces the following functions from previous SDK versions: + + void NxJointDesc::setGlobalAnchor(const NxVec3& wsAnchor); + void NxJointDesc::setGlobalAxis(const NxVec3& wsAxis); + + The function sets the joint's localPose using world-space input parameters. + + \param[in] wsAnchor Global frame anchor point. Range: position vector + \param[in] wsAxis Global frame axis. Range: direction vector + \param[in,out] joint Joint having its global frame set. +*/ + +PX_C_EXPORT void PX_CALL_CONV PxSetJointGlobalFrame(physx::PxJoint& joint, const physx::PxVec3* wsAnchor, const physx::PxVec3* wsAxis); + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxJointLimit.h b/src/PhysX/physx/include/extensions/PxJointLimit.h new file mode 100644 index 000000000..85a4ec82f --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxJointLimit.h @@ -0,0 +1,566 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_EXTENSIONS_JOINT_LIMIT +#define PX_EXTENSIONS_JOINT_LIMIT +/** \addtogroup extensions + @{ +*/ + +#include "foundation/PxMath.h" +#include "PxPhysXConfig.h" +#include "common/PxTolerancesScale.h" +#include "PxJoint.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Describes the parameters for a joint limit. + +Limits are enabled or disabled by setting flags or other configuration parameters joints, see the +documentation for specific joint types for details. +*/ +class PxJointLimitParameters +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief Controls the amount of bounce when the joint hits a limit. + + A restitution value of 1.0 causes the joint to bounce back with the velocity which it hit the limit. + A value of zero causes the joint to stop dead. + + In situations where the joint has many locked DOFs (e.g. 5) the restitution may not be applied + correctly. This is due to a limitation in the solver which causes the restitution velocity to become zero + as the solver enforces constraints on the other DOFs. + + This limitation applies to both angular and linear limits, however it is generally most apparent with limited + angular DOFs. Disabling joint projection and increasing the solver iteration count may improve this behavior + to some extent. + + Also, combining soft joint limits with joint drives driving against those limits may affect stability. + + Range: [0,1]
+ Default: 0.0 + */ + PxReal restitution; + + /** + determines the minimum impact velocity which will cause the joint to bounce + */ + PxReal bounceThreshold; + + /** + \brief if greater than zero, the limit is soft, i.e. a spring pulls the joint back to the limit + + Range: [0, PX_MAX_F32)
+ Default: 0.0 + */ + PxReal stiffness; + + /** + \brief if spring is greater than zero, this is the damping of the limit spring + + Range: [0, PX_MAX_F32)
+ Default: 0.0 + */ + PxReal damping; + + /** + \brief the distance inside the limit value at which the limit will be considered to be active by the + solver. As this value is made larger, the limit becomes active more quickly. It thus becomes less + likely to violate the extents of the limit, but more expensive. + + The contact distance should be less than the limit angle or distance, and in the case of a pair limit, + less than half the distance between the upper and lower bounds. Exceeding this value will result in + the limit being active all the time. + + Making this value too small can result in jitter around the limit. + + Default: depends on the joint + + @see PxPhysics::getTolerancesScale() + */ + PxReal contactDistance; + + PxJointLimitParameters() : + restitution (0.0f), + bounceThreshold (0.0f), + stiffness (0.0f), + damping (0.0f), + contactDistance (0.0f) + { + } + + PxJointLimitParameters(const PxJointLimitParameters& p) : + restitution (p.restitution), + bounceThreshold (p.bounceThreshold), + stiffness (p.stiffness), + damping (p.damping), + contactDistance (p.contactDistance) + { + } + + /** + \brief Returns true if the current settings are valid. + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxIsFinite(restitution) && restitution >= 0 && restitution <= 1 && + PxIsFinite(stiffness) && stiffness >= 0 && + PxIsFinite(damping) && damping >= 0 && + PxIsFinite(bounceThreshold) && bounceThreshold >= 0 && + PxIsFinite(contactDistance) && contactDistance >= 0; + } + + PX_INLINE bool isSoft() const + { + return damping>0 || stiffness>0; + } + +protected: + ~PxJointLimitParameters() {} +}; + + +/** +\brief Describes a one-sided linear limit. +*/ +class PxJointLinearLimit : public PxJointLimitParameters +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief the extent of the limit. + + Range: (0, PX_MAX_F32)
+ Default: PX_MAX_F32 + */ + PxReal value; + + /** + \brief construct a linear hard limit + + \param[in] scale A PxTolerancesScale struct. Should be the same as used when creating the PxPhysics object. + \param[in] extent The extent of the limit + \param[in] contactDist The distance from the limit at which it becomes active. Default is 0.01f scaled by the tolerance length scale + + @see PxJointLimitParameters PxTolerancesScale + */ + PxJointLinearLimit(const PxTolerancesScale& scale, PxReal extent, PxReal contactDist = -1.0f) + : value(extent) + { + PxJointLimitParameters::contactDistance = contactDist == -1.0f ? 0.01f*scale.length : contactDist; + } + + /** + \brief construct a linear soft limit + + \param[in] extent the extent of the limit + \param[in] spring the stiffness and damping parameters for the limit spring + + @see PxJointLimitParameters PxTolerancesScale + */ + PxJointLinearLimit(PxReal extent, const PxSpring& spring) : value(extent) + { + stiffness = spring.stiffness; + damping = spring.damping; + } + + /** + \brief Returns true if the limit is valid + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxJointLimitParameters::isValid() && + PxIsFinite(value) && + value > 0.0f; + } +}; + + +/** +\brief Describes a two-sided limit. +*/ +class PxJointLinearLimitPair : public PxJointLimitParameters +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief the range of the limit. The upper limit must be no lower than the + lower limit, and if they are equal the limited degree of freedom will be treated as locked. + + Range: See the joint on which the limit is used for details
+ Default: lower = -PX_MAX_F32/3, upper = PX_MAX_F32/3 + */ + PxReal upper, lower; + + /** + \brief Construct a linear hard limit pair. The lower distance value must be less than the upper distance value. + + \param[in] scale A PxTolerancesScale struct. Should be the same as used when creating the PxPhysics object. + \param[in] lowerLimit The lower distance of the limit + \param[in] upperLimit The upper distance of the limit + \param[in] contactDist The distance from the limit at which it becomes active. Default is the lesser of 0.01f scaled by the tolerance length scale, and 0.49 * (upperLimit - lowerLimit) + + @see PxJointLimitParameters PxTolerancesScale + */ + PxJointLinearLimitPair(const PxTolerancesScale& scale, PxReal lowerLimit = -PX_MAX_F32/3.0f, PxReal upperLimit = PX_MAX_F32/3.0f, PxReal contactDist = -1.0f) : + upper(upperLimit), + lower(lowerLimit) + { + PxJointLimitParameters::contactDistance = contactDist == -1.0f ? PxMin(scale.length * 0.01f, (upperLimit*0.49f-lowerLimit*0.49f)) : contactDist; + bounceThreshold = 2.0f*scale.length; + } + + /** + \brief construct a linear soft limit pair + + \param[in] lowerLimit The lower distance of the limit + \param[in] upperLimit The upper distance of the limit + \param[in] spring The stiffness and damping parameters of the limit spring + + @see PxJointLimitParameters PxTolerancesScale + */ + PxJointLinearLimitPair(PxReal lowerLimit, PxReal upperLimit, const PxSpring& spring) : + upper(upperLimit), + lower(lowerLimit) + { + stiffness = spring.stiffness; + damping = spring.damping; + } + + /** + \brief Returns true if the limit is valid. + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxJointLimitParameters::isValid() && + PxIsFinite(upper) && PxIsFinite(lower) && upper >= lower && + PxIsFinite(upper - lower); + } +}; + + +class PxJointAngularLimitPair : public PxJointLimitParameters +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief the range of the limit. The upper limit must be no lower than the lower limit. + + Unit: Angular: Radians + Range: See the joint on which the limit is used for details
+ Default: lower = -PI/2, upper = PI/2 + */ + PxReal upper, lower; + + /** + \brief construct an angular hard limit pair. + + The lower value must be less than the upper value. + + \param[in] lowerLimit The lower angle of the limit + \param[in] upperLimit The upper angle of the limit + \param[in] contactDist The distance from the limit at which it becomes active. Default is the lesser of 0.1 radians, and 0.49 * (upperLimit - lowerLimit) + + @see PxJointLimitParameters + */ + PxJointAngularLimitPair(PxReal lowerLimit, PxReal upperLimit, PxReal contactDist = -1.0f) : + upper(upperLimit), + lower(lowerLimit) + { + PxJointLimitParameters::contactDistance = contactDist ==-1.0f ? PxMin(0.1f, 0.49f*(upperLimit-lowerLimit)) : contactDist; + bounceThreshold = 0.5f; + } + + /** + \brief construct an angular soft limit pair. + + The lower value must be less than the upper value. + + \param[in] lowerLimit The lower angle of the limit + \param[in] upperLimit The upper angle of the limit + \param[in] spring The stiffness and damping of the limit spring + + @see PxJointLimitParameters + */ + PxJointAngularLimitPair(PxReal lowerLimit, PxReal upperLimit, const PxSpring& spring) : + upper(upperLimit), + lower(lowerLimit) + { + stiffness = spring.stiffness; + damping = spring.damping; + } + + /** + \brief Returns true if the limit is valid. + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxJointLimitParameters::isValid() && + PxIsFinite(upper) && PxIsFinite(lower) && upper >= lower; + } +}; + +/** +\brief Describes an elliptical conical joint limit. Note that very small or highly elliptical limit cones may +result in jitter. + +@see PxD6Joint PxSphericalJoint +*/ +class PxJointLimitCone : public PxJointLimitParameters +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief the maximum angle from the Y axis of the constraint frame. + + Unit: Angular: Radians + Range: Angular: (0,PI)
+ Default: PI/2 + */ + PxReal yAngle; + + /** + \brief the maximum angle from the Z-axis of the constraint frame. + + Unit: Angular: Radians + Range: Angular: (0,PI)
+ Default: PI/2 + */ + PxReal zAngle; + + /** + \brief Construct a cone hard limit. + + \param[in] yLimitAngle The limit angle from the Y-axis of the constraint frame + \param[in] zLimitAngle The limit angle from the Z-axis of the constraint frame + \param[in] contactDist The distance from the limit at which it becomes active. Default is the lesser of 0.1 radians, and 0.49 * the lower of the limit angles + + @see PxJointLimitParameters + */ + PxJointLimitCone(PxReal yLimitAngle, PxReal zLimitAngle, PxReal contactDist = -1.0f) : + yAngle(yLimitAngle), + zAngle(zLimitAngle) + { + PxJointLimitParameters::contactDistance = contactDist == -1.0f ? PxMin(0.1f, PxMin(yLimitAngle, zLimitAngle)*0.49f) : contactDist; + bounceThreshold = 0.5f; + } + + /** + \brief Construct a cone soft limit. + + \param[in] yLimitAngle The limit angle from the Y-axis of the constraint frame + \param[in] zLimitAngle The limit angle from the Z-axis of the constraint frame + \param[in] spring The stiffness and damping of the limit spring + + @see PxJointLimitParameters + */ + PxJointLimitCone(PxReal yLimitAngle, PxReal zLimitAngle, const PxSpring& spring) : + yAngle(yLimitAngle), + zAngle(zLimitAngle) + { + stiffness = spring.stiffness; + damping = spring.damping; + } + + /** + \brief Returns true if the limit is valid. + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxJointLimitParameters::isValid() && + PxIsFinite(yAngle) && yAngle>0 && yAngle0 && zAngleUnit: Angular: Radians + Range: Angular: (-PI,PI)
+ Default: -PI/2 + */ + PxReal yAngleMin; + + /** + \brief the maximum angle from the Y axis of the constraint frame. + + Unit: Angular: Radians + Range: Angular: (-PI,PI)
+ Default: PI/2 + */ + PxReal yAngleMax; + + /** + \brief the minimum angle from the Z-axis of the constraint frame. + + Unit: Angular: Radians + Range: Angular: (-PI,PI)
+ Default: -PI/2 + */ + PxReal zAngleMin; + + /** + \brief the maximum angle from the Z-axis of the constraint frame. + + Unit: Angular: Radians + Range: Angular: (-PI,PI)
+ Default: PI/2 + */ + PxReal zAngleMax; + + /** + \brief Construct a pyramid hard limit. + + \param[in] yLimitAngleMin The minimum limit angle from the Y-axis of the constraint frame + \param[in] yLimitAngleMax The maximum limit angle from the Y-axis of the constraint frame + \param[in] zLimitAngleMin The minimum limit angle from the Z-axis of the constraint frame + \param[in] zLimitAngleMax The maximum limit angle from the Z-axis of the constraint frame + \param[in] contactDist The distance from the limit at which it becomes active. Default is the lesser of 0.1 radians, and 0.49 * the lower of the limit angles + + @see PxJointLimitParameters + */ + PxJointLimitPyramid(PxReal yLimitAngleMin, PxReal yLimitAngleMax, PxReal zLimitAngleMin, PxReal zLimitAngleMax, PxReal contactDist = -1.0f) : + yAngleMin(yLimitAngleMin), + yAngleMax(yLimitAngleMax), + zAngleMin(zLimitAngleMin), + zAngleMax(zLimitAngleMax) + { + if(contactDist == -1.0f) + { + const PxReal contactDistY = PxMin(0.1f, 0.49f*(yLimitAngleMax - yLimitAngleMin)); + const PxReal contactDistZ = PxMin(0.1f, 0.49f*(zLimitAngleMax - zLimitAngleMin)); + PxJointLimitParameters::contactDistance = contactDist == PxMin(contactDistY, contactDistZ); + } + else + { + PxJointLimitParameters::contactDistance = contactDist; + } + + bounceThreshold = 0.5f; + } + + /** + \brief Construct a pyramid soft limit. + + \param[in] yLimitAngleMin The minimum limit angle from the Y-axis of the constraint frame + \param[in] yLimitAngleMax The maximum limit angle from the Y-axis of the constraint frame + \param[in] zLimitAngleMin The minimum limit angle from the Z-axis of the constraint frame + \param[in] zLimitAngleMax The maximum limit angle from the Z-axis of the constraint frame + \param[in] spring The stiffness and damping of the limit spring + + @see PxJointLimitParameters + */ + PxJointLimitPyramid(PxReal yLimitAngleMin, PxReal yLimitAngleMax, PxReal zLimitAngleMin, PxReal zLimitAngleMax, const PxSpring& spring) : + yAngleMin(yLimitAngleMin), + yAngleMax(yLimitAngleMax), + zAngleMin(zLimitAngleMin), + zAngleMax(zLimitAngleMax) + { + stiffness = spring.stiffness; + damping = spring.damping; + } + + /** + \brief Returns true if the limit is valid. + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxJointLimitParameters::isValid() && + PxIsFinite(yAngleMin) && yAngleMin>-PxPi && yAngleMin-PxPi && yAngleMax-PxPi && zAngleMin-PxPi && zAngleMax=yAngleMin && zAngleMax>=zAngleMin; + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxMassProperties.h b/src/PhysX/physx/include/extensions/PxMassProperties.h new file mode 100644 index 000000000..a5bc057a5 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxMassProperties.h @@ -0,0 +1,334 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_MASS_PROPERTIES_H +#define PX_PHYSICS_EXTENSIONS_MASS_PROPERTIES_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxMath.h" +#include "foundation/PxMathUtils.h" +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "geometry/PxGeometry.h" +#include "geometry/PxBoxGeometry.h" +#include "geometry/PxSphereGeometry.h" +#include "geometry/PxCapsuleGeometry.h" +#include "geometry/PxConvexMeshGeometry.h" +#include "geometry/PxConvexMesh.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Utility class to compute and manipulate mass and inertia tensor properties. + +In most cases #PxRigidBodyExt::updateMassAndInertia(), #PxRigidBodyExt::setMassAndUpdateInertia() should be enough to +setup the mass properties of a rigid body. This utility class targets users that need to customize the mass properties +computation. +*/ +class PxMassProperties +{ +public: + /** + \brief Default constructor. + */ + PX_FORCE_INLINE PxMassProperties() : inertiaTensor(PxIdentity), centerOfMass(0.0f), mass(1.0f) {} + + /** + \brief Construct from individual elements. + */ + PX_FORCE_INLINE PxMassProperties(const PxReal m, const PxMat33& inertiaT, const PxVec3& com) : inertiaTensor(inertiaT), centerOfMass(com), mass(m) {} + + /** + \brief Compute mass properties based on a provided geometry structure. + + This constructor assumes the geometry has a density of 1. Mass and inertia tensor scale linearly with density. + + \param[in] geometry The geometry to compute the mass properties for. Supported geometry types are: sphere, box, capsule and convex mesh. + */ + PxMassProperties(const PxGeometry& geometry) + { + switch (geometry.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& s = static_cast(geometry); + mass = (4.0f / 3.0f) * PxPi * s.radius * s.radius * s.radius; + inertiaTensor = PxMat33::createDiagonal(PxVec3(2.0f / 5.0f * mass * s.radius * s.radius)); + centerOfMass = PxVec3(0.0f); + } + break; + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& b = static_cast(geometry); + mass = b.halfExtents.x * b.halfExtents.y * b.halfExtents.z * 8.0f; + PxVec3 d2 = b.halfExtents.multiply(b.halfExtents); + inertiaTensor = PxMat33::createDiagonal(PxVec3(d2.y + d2.z, d2.x + d2.z, d2.x + d2.y)) * (mass * 1.0f / 3.0f); + centerOfMass = PxVec3(0.0f); + } + break; + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& c = static_cast(geometry); + PxReal r = c.radius, h = c.halfHeight; + mass = ((4.0f / 3.0f) * r + 2 * c.halfHeight) * PxPi * r * r; + + PxReal a = r*r*r * (8.0f / 15.0f) + h*r*r * (3.0f / 2.0f) + h*h*r * (4.0f / 3.0f) + h*h*h * (2.0f / 3.0f); + PxReal b = r*r*r * (8.0f / 15.0f) + h*r*r; + inertiaTensor = PxMat33::createDiagonal(PxVec3(b, a, a) * PxPi * r * r); + centerOfMass = PxVec3(0.0f); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& c = static_cast(geometry); + PxVec3 unscaledCoM; + PxMat33 unscaledInertiaTensorNonCOM; // inertia tensor of convex mesh in mesh local space + PxMat33 unscaledInertiaTensorCOM; + PxReal unscaledMass; + c.convexMesh->getMassInformation(unscaledMass, unscaledInertiaTensorNonCOM, unscaledCoM); + + // inertia tensor relative to center of mass + unscaledInertiaTensorCOM[0][0] = unscaledInertiaTensorNonCOM[0][0] - unscaledMass*PxReal((unscaledCoM.y*unscaledCoM.y+unscaledCoM.z*unscaledCoM.z)); + unscaledInertiaTensorCOM[1][1] = unscaledInertiaTensorNonCOM[1][1] - unscaledMass*PxReal((unscaledCoM.z*unscaledCoM.z+unscaledCoM.x*unscaledCoM.x)); + unscaledInertiaTensorCOM[2][2] = unscaledInertiaTensorNonCOM[2][2] - unscaledMass*PxReal((unscaledCoM.x*unscaledCoM.x+unscaledCoM.y*unscaledCoM.y)); + unscaledInertiaTensorCOM[0][1] = unscaledInertiaTensorCOM[1][0] = (unscaledInertiaTensorNonCOM[0][1] + unscaledMass*PxReal(unscaledCoM.x*unscaledCoM.y)); + unscaledInertiaTensorCOM[1][2] = unscaledInertiaTensorCOM[2][1] = (unscaledInertiaTensorNonCOM[1][2] + unscaledMass*PxReal(unscaledCoM.y*unscaledCoM.z)); + unscaledInertiaTensorCOM[0][2] = unscaledInertiaTensorCOM[2][0] = (unscaledInertiaTensorNonCOM[0][2] + unscaledMass*PxReal(unscaledCoM.z*unscaledCoM.x)); + + const PxMeshScale& s = c.scale; + mass = unscaledMass * s.scale.x * s.scale.y * s.scale.z; + centerOfMass = s.rotation.rotate(s.scale.multiply(s.rotation.rotateInv(unscaledCoM))); + inertiaTensor = scaleInertia(unscaledInertiaTensorCOM, s.rotation, s.scale); + } + break; + + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eINVALID: + case PxGeometryType::eGEOMETRY_COUNT: + { + *this = PxMassProperties(); + } + break; + } + + PX_ASSERT(inertiaTensor.column0.isFinite() && inertiaTensor.column1.isFinite() && inertiaTensor.column2.isFinite()); + PX_ASSERT(centerOfMass.isFinite()); + PX_ASSERT(PxIsFinite(mass)); + } + + /** + \brief Scale mass properties. + + \param[in] scale The linear scaling factor to apply to the mass properties. + \return The scaled mass properties. + */ + PX_FORCE_INLINE PxMassProperties operator*(const PxReal scale) const + { + PX_ASSERT(PxIsFinite(scale)); + + return PxMassProperties(mass * scale, inertiaTensor * scale, centerOfMass); + } + + /** + \brief Translate the center of mass by a given vector and adjust the inertia tensor accordingly. + + \param[in] t The translation vector for the center of mass. + */ + PX_FORCE_INLINE void translate(const PxVec3& t) + { + PX_ASSERT(t.isFinite()); + + inertiaTensor = translateInertia(inertiaTensor, mass, t); + centerOfMass += t; + + PX_ASSERT(inertiaTensor.column0.isFinite() && inertiaTensor.column1.isFinite() && inertiaTensor.column2.isFinite()); + PX_ASSERT(centerOfMass.isFinite()); + } + + /** + \brief Get the entries of the diagonalized inertia tensor and the corresponding reference rotation. + + \param[in] inertia The inertia tensor to diagonalize. + \param[out] massFrame The frame the diagonalized tensor refers to. + \return The entries of the diagonalized inertia tensor. + */ + PX_FORCE_INLINE static PxVec3 getMassSpaceInertia(const PxMat33& inertia, PxQuat& massFrame) + { + PX_ASSERT(inertia.column0.isFinite() && inertia.column1.isFinite() && inertia.column2.isFinite()); + + PxVec3 diagT = PxDiagonalize(inertia, massFrame); + PX_ASSERT(diagT.isFinite()); + PX_ASSERT(massFrame.isFinite()); + return diagT; + } + + /** + \brief Translate an inertia tensor using the parallel axis theorem + + \param[in] inertia The inertia tensor to translate. + \param[in] mass The mass of the object. + \param[in] t The relative frame to translate the inertia tensor to. + \return The translated inertia tensor. + */ + PX_FORCE_INLINE static PxMat33 translateInertia(const PxMat33& inertia, const PxReal mass, const PxVec3& t) + { + PX_ASSERT(inertia.column0.isFinite() && inertia.column1.isFinite() && inertia.column2.isFinite()); + PX_ASSERT(PxIsFinite(mass)); + PX_ASSERT(t.isFinite()); + + PxMat33 s( PxVec3(0,t.z,-t.y), + PxVec3(-t.z,0,t.x), + PxVec3(t.y,-t.x,0) ); + + PxMat33 translatedIT = s.getTranspose() * s * mass + inertia; + PX_ASSERT(translatedIT.column0.isFinite() && translatedIT.column1.isFinite() && translatedIT.column2.isFinite()); + return translatedIT; + } + + /** + \brief Rotate an inertia tensor around the center of mass + + \param[in] inertia The inertia tensor to rotate. + \param[in] q The rotation to apply to the inertia tensor. + \return The rotated inertia tensor. + */ + PX_FORCE_INLINE static PxMat33 rotateInertia(const PxMat33& inertia, const PxQuat& q) + { + PX_ASSERT(inertia.column0.isFinite() && inertia.column1.isFinite() && inertia.column2.isFinite()); + PX_ASSERT(q.isUnit()); + + PxMat33 m(q); + PxMat33 rotatedIT = m * inertia * m.getTranspose(); + PX_ASSERT(rotatedIT.column0.isFinite() && rotatedIT.column1.isFinite() && rotatedIT.column2.isFinite()); + return rotatedIT; + } + + /** + \brief Non-uniform scaling of the inertia tensor + + \param[in] inertia The inertia tensor to scale. + \param[in] scaleRotation The frame of the provided scaling factors. + \param[in] scale The scaling factor for each axis (relative to the frame specified in scaleRotation). + \return The scaled inertia tensor. + */ + static PxMat33 scaleInertia(const PxMat33& inertia, const PxQuat& scaleRotation, const PxVec3& scale) + { + PX_ASSERT(inertia.column0.isFinite() && inertia.column1.isFinite() && inertia.column2.isFinite()); + PX_ASSERT(scaleRotation.isUnit()); + PX_ASSERT(scale.isFinite()); + + PxMat33 localInertiaT = rotateInertia(inertia, scaleRotation); // rotate inertia into scaling frame + PxVec3 diagonal(localInertiaT[0][0], localInertiaT[1][1], localInertiaT[2][2]); + + PxVec3 xyz2 = PxVec3(diagonal.dot(PxVec3(0.5f))) - diagonal; // original x^2, y^2, z^2 + PxVec3 scaledxyz2 = xyz2.multiply(scale).multiply(scale); + + PxReal xx = scaledxyz2.y + scaledxyz2.z, + yy = scaledxyz2.z + scaledxyz2.x, + zz = scaledxyz2.x + scaledxyz2.y; + + PxReal xy = localInertiaT[0][1] * scale.x * scale.y, + xz = localInertiaT[0][2] * scale.x * scale.z, + yz = localInertiaT[1][2] * scale.y * scale.z; + + PxMat33 scaledInertia( PxVec3(xx, xy, xz), + PxVec3(xy, yy, yz), + PxVec3(xz, yz, zz)); + + PxMat33 scaledIT = rotateInertia(scaledInertia * (scale.x * scale.y * scale.z), scaleRotation.getConjugate()); + PX_ASSERT(scaledIT.column0.isFinite() && scaledIT.column1.isFinite() && scaledIT.column2.isFinite()); + return scaledIT; + } + + /** + \brief Sum up individual mass properties. + + \param[in] props Array of mass properties to sum up. + \param[in] transforms Reference transforms for each mass properties entry. + \param[in] count The number of mass properties to sum up. + \return The summed up mass properties. + */ + static PxMassProperties sum(const PxMassProperties* props, const PxTransform* transforms, const PxU32 count) + { + PxReal combinedMass = 0.0f; + PxVec3 combinedCoM(0.0f); + PxMat33 combinedInertiaT = PxMat33(PxZero); + + for(PxU32 i = 0; i < count; i++) + { + PX_ASSERT(props[i].inertiaTensor.column0.isFinite() && props[i].inertiaTensor.column1.isFinite() && props[i].inertiaTensor.column2.isFinite()); + PX_ASSERT(props[i].centerOfMass.isFinite()); + PX_ASSERT(PxIsFinite(props[i].mass)); + + combinedMass += props[i].mass; + const PxVec3 comTm = transforms[i].transform(props[i].centerOfMass); + combinedCoM += comTm * props[i].mass; + } + + combinedCoM /= combinedMass; + + for(PxU32 i = 0; i < count; i++) + { + const PxVec3 comTm = transforms[i].transform(props[i].centerOfMass); + combinedInertiaT += translateInertia(rotateInertia(props[i].inertiaTensor, transforms[i].q), props[i].mass, combinedCoM - comTm); + } + + PX_ASSERT(combinedInertiaT.column0.isFinite() && combinedInertiaT.column1.isFinite() && combinedInertiaT.column2.isFinite()); + PX_ASSERT(combinedCoM.isFinite()); + PX_ASSERT(PxIsFinite(combinedMass)); + + return PxMassProperties(combinedMass, combinedInertiaT, combinedCoM); + } + + + PxMat33 inertiaTensor; //!< The inertia tensor of the object. + PxVec3 centerOfMass; //!< The center of mass of the object. + PxReal mass; //!< The mass of the object. +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxPrismaticJoint.h b/src/PhysX/physx/include/extensions/PxPrismaticJoint.h new file mode 100644 index 000000000..b82ac2c8a --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxPrismaticJoint.h @@ -0,0 +1,236 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PRISMATICJOINT_H +#define PX_PRISMATICJOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" +#include "extensions/PxJointLimit.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPrismaticJoint; + +/** +\brief Create a prismatic joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxPrismaticJoint +*/ +PxPrismaticJoint* PxPrismaticJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + + +/** +\brief Flags specific to the prismatic joint. + +@see PxPrismaticJoint +*/ +struct PxPrismaticJointFlag +{ + enum Enum + { + eLIMIT_ENABLED = 1<<1 + }; +}; + +typedef PxFlags PxPrismaticJointFlags; +PX_FLAGS_OPERATORS(PxPrismaticJointFlag::Enum, PxU16) + +/** + \brief A prismatic joint permits relative translational movement between two bodies along + an axis, but no relative rotational movement. + + the axis on each body is defined as the line containing the origin of the joint frame and + extending along the x-axis of that frame + + \image html prismJoint.png + + @see PxPrismaticJointCreate() PxJoint +*/ +class PxPrismaticJoint : public PxJoint +{ +public: + + /** + \brief returns the displacement of the joint along its axis. + */ + virtual PxReal getPosition() const = 0; + + /** + \brief returns the velocity of the joint along its axis + */ + virtual PxReal getVelocity() const = 0; + + /** + \brief sets the joint limit parameters. + + The limit range is [-PX_MAX_F32, PX_MAX_F32], but note that the width of the limit (upper-lower) must also be + a valid float. + + @see PxJointLinearLimitPair getLimit() + */ + virtual void setLimit(const PxJointLinearLimitPair&) = 0; + + /** + \brief gets the joint limit parameters. + + @see PxJointLinearLimit getLimit() + */ + virtual PxJointLinearLimitPair getLimit() const = 0; + + /** + \brief Set the flags specific to the Prismatic Joint. + + Default PxPrismaticJointFlags(0) + + \param[in] flags The joint flags. + + @see PxPrismaticJointFlag setFlag() getFlags() + */ + virtual void setPrismaticJointFlags(PxPrismaticJointFlags flags) = 0; + + /** + \brief Set a single flag specific to a Prismatic Joint to true or false. + + \param[in] flag The flag to set or clear. + \param[in] value The value to which to set the flag + + @see PxPrismaticJointFlag, getFlags() setFlags() + */ + virtual void setPrismaticJointFlag(PxPrismaticJointFlag::Enum flag, bool value) = 0; + + /** + \brief Get the flags specific to the Prismatic Joint. + + \return the joint flags + + @see PxPrismaticJoint::flags, PxPrismaticJointFlag setFlag() setFlags() + */ + virtual PxPrismaticJointFlags getPrismaticJointFlags() const = 0; + + /** + \brief Set the linear tolerance threshold for projection. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + This value must be nonnegative. + + Range: [0, PX_MAX_F32)
+ Default: 1e10f + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() + */ + virtual void setProjectionLinearTolerance(PxReal tolerance) = 0; + + /** + \brief Get the linear tolerance threshold for projection. + + \return the linear tolerance threshold in radians + + @see setProjectionLinearTolerance() + */ + virtual PxReal getProjectionLinearTolerance() const = 0; + + /** + \brief Set the angular tolerance threshold for projection. Projection is enabled if PxConstraintFlag::ePROJECTION + is set for the joint. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0, PX_MAX_F32)
+ Default: Pi + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() PxJoint::setConstraintFlags() + */ + virtual void setProjectionAngularTolerance(PxReal tolerance) = 0; + + /** + \brief Get the angular tolerance threshold for projection. + + @see getProjectionAngularTolerance() + */ + virtual PxReal getProjectionAngularTolerance() const = 0; + + /** + \brief Returns string name of PxPrismaticJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxPrismaticJoint"; } + +protected: + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxPrismaticJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxPrismaticJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxPrismaticJoint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxRaycastCCD.h b/src/PhysX/physx/include/extensions/PxRaycastCCD.h new file mode 100644 index 000000000..ed8f84ba0 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRaycastCCD.h @@ -0,0 +1,100 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_RAYCAST_CCD_H +#define PX_RAYCAST_CCD_H +/** \addtogroup extensions +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxScene; + class PxShape; + class PxRigidDynamic; + class RaycastCCDManagerInternal; + + /** + \brief Raycast-CCD manager. + + Raycast-CCD is a simple and potentially cheaper alternative to the SDK's built-in continuous collision detection algorithm. + + This implementation has some limitations: + - it is only implemented for PxRigidDynamic objects (not for PxArticulationLink) + - it is only implemented for simple actors with 1 shape (not for "compounds") + + Also, since it is raycast-based, the solution is not perfect. In particular: + - small dynamic objects can still go through the static world if the ray goes through a crack between edges, or a small + hole in the world (like the keyhole from a door). + - dynamic-vs-dynamic CCD is very approximate. It only works well for fast-moving dynamic objects colliding against + slow-moving dynamic objects. + + Finally, since it is using the SDK's scene queries under the hood, it only works provided the simulation shapes also have + scene-query shapes associated with them. That is, if the objects in the scene only use PxShapeFlag::eSIMULATION_SHAPE + (and no PxShapeFlag::eSCENE_QUERY_SHAPE), then the raycast-CCD system will not work. + */ + class RaycastCCDManager + { + public: + RaycastCCDManager(PxScene* scene); + ~RaycastCCDManager(); + + /** + \brief Register dynamic object for raycast CCD. + + \param[in] actor object's actor + \param[in] shape object's shape + + \return True if success + */ + bool registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape); + + /** + \brief Perform raycast CCD. Call this after your simulate/fetchResults calls. + + \param[in] doDynamicDynamicCCD True to enable dynamic-vs-dynamic CCD (more expensive, not always needed) + */ + void doRaycastCCD(bool doDynamicDynamicCCD); + + private: + RaycastCCDManagerInternal* mImpl; + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxRepXSerializer.h b/src/PhysX/physx/include/extensions/PxRepXSerializer.h new file mode 100644 index 000000000..dae0ef604 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRepXSerializer.h @@ -0,0 +1,148 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_REPX_SERIALIZER_H +#define PX_REPX_SERIALIZER_H +/** \addtogroup Serializers + @{ +*/ + +#include "common/PxBase.h" +#include "extensions/PxRepXSimpleType.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class XmlMemoryAllocator; + class XmlWriter; + class XmlReader; + class MemoryBuffer; + + /** + \brief Serializer interface for RepX (Xml) serialization. + + In order to serialize a class to RepX both a PxSerializer and + a PxRepXSerializer implementation are needed. + + A repx Serializer provides the ability to capture a live + object to a descriptor or static state and the ability to + write that state out to a file. Objects allocated + by the Serializer using the allocator are freed when the + collection itself is freed. + SnRepXCoreSerializers.cpp implements a set of Serializers + for the core PhysX types. + + \note Implementing a PxRepXSerializer is currently not practical without including the internal PhysXExtension header "SnRepXSerializerImpl.h". + + @see PxSerializer, PX_NEW_REPX_SERIALIZER, PxSerializationRegistry::registerRepXSerializer + */ + class PxRepXSerializer + { + protected: + virtual ~PxRepXSerializer(){} + public: + + /** + \brief The type this Serializer is meant to operate on. + @see PxRepXObject::typeName + */ + virtual const char* getTypeName() = 0; + + /** + \brief Convert from a RepX object to a key-value pair hierarchy + + \param[in] inLiveObject The object to convert to the passed in descriptor. + \param[in] inCollection The collection to use to find ids of references of this object. + \param[in] inWriter Interface to write data to. + \param[in] inTempBuffer used to for temporary allocations. + \param[in] inArgs The arguments used in create resources and objects. + */ + virtual void objectToFile( const PxRepXObject& inLiveObject, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs ) = 0; + + /** + \brief Convert from a descriptor to a live object. Must be an object of this Serializer type. + + \param[in] inReader The inverse of the writer, a key-value pair database. + \param[in] inAllocator An allocator to use for temporary allocations. These will be freed after instantiation completes. + \param[in] inArgs The arguments used in create resources and objects. + \param[in] inCollection The collection used to find references. + + \return The new live object. It can be an invalid object if the instantiation cannot take place. + */ + virtual PxRepXObject fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) = 0; + + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** +\brief Inline helper template function to create PxRepXObject from TDataType type supporting PxTypeInfo::name. +*/ +template +PX_INLINE physx::PxRepXObject PxCreateRepXObject(const TDataType* inType, const physx::PxSerialObjectId inId) +{ + return physx::PxRepXObject(physx::PxTypeInfo::name(), inType, inId); +} + +/** +\brief Inline helper function to create PxRepXObject from a PxBase instance. +*/ +PX_INLINE physx::PxRepXObject PxCreateRepXObject(const physx::PxBase* inType, const physx::PxSerialObjectId inId) +{ + PX_ASSERT(inType); + return physx::PxRepXObject(inType->getConcreteTypeName(), inType, inId); +} + +/** +\brief Inline helper template function to create PxRepXObject form TDataType type using inType pointer as a PxSerialObjectId id. +*/ +template +PX_INLINE physx::PxRepXObject PxCreateRepXObject(const TDataType* inType) +{ + return PxCreateRepXObject(inType, static_cast(reinterpret_cast(inType))); +} + +/** +\brief Preprocessor macro for RepX serializer creation. +*/ +#define PX_NEW_REPX_SERIALIZER(T) \ + *PX_PLACEMENT_NEW(PxGetFoundation().getAllocatorCallback().allocate(sizeof(T), "PxRepXSerializer", __FILE__, __LINE__ ), T)(PxGetFoundation().getAllocatorCallback()) + +/** +\brief Preprocessor Macro to simplify RepX serializer delete. +*/ +#define PX_DELETE_REPX_SERIALIZER(x) \ + { PxRepXSerializer* s = x; if (s) { PxGetFoundation().getAllocatorCallback().deallocate(s); } } + + +/** @} */ +#endif // PX_REPX_SERIALIZER_H diff --git a/src/PhysX/physx/include/extensions/PxRepXSimpleType.h b/src/PhysX/physx/include/extensions/PxRepXSimpleType.h new file mode 100644 index 000000000..bd6023e92 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRepXSimpleType.h @@ -0,0 +1,106 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PX_REPX_SIMPLE_TYPE_H +#define PX_REPX_SIMPLE_TYPE_H + +/** \addtogroup extensions + @{ +*/ + +#include "foundation/PxSimpleTypes.h" +#include "cooking/PxCooking.h" +#include "common/PxStringTable.h" +#include "common/PxSerialFramework.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief Helper class containing the mapping of id to object, and type name. + */ + struct PxRepXObject + { + /** + \brief Identifies the extension meant to handle this object. + @see PxTypeInfo, PX_DEFINE_TYPEINFO, PxRepXSerializer + */ + const char* typeName; + + /** + \brief Pointer to the serializable this was created from + */ + const void* serializable; + + /** + \brief Id given to this object at some point + */ + PxSerialObjectId id; + PxRepXObject( const char* inTypeName = "", const void* inSerializable = NULL, const PxSerialObjectId inId = 0 ) + : typeName( inTypeName ) + , serializable( inSerializable ) + , id( inId ) + { + } + bool isValid() const { return serializable != NULL; } + }; + + /** + \brief Arguments required to instantiate a serializable object from RepX. + + Extra arguments can be added to the object map under special ids. + + @see PxRepXSerializer::objectToFile, PxRepXSerializer::fileToObject + */ + struct PxRepXInstantiationArgs + { + PxPhysics& physics; + PxCooking* cooker; + PxStringTable* stringTable; + PxRepXInstantiationArgs( PxPhysics& inPhysics, PxCooking* inCooking = NULL , PxStringTable* inStringTable = NULL ) + : physics( inPhysics ) + , cooker( inCooking ) + , stringTable( inStringTable ) + { + } + + PxRepXInstantiationArgs& operator=(const PxRepXInstantiationArgs&); + }; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxRevoluteJoint.h b/src/PhysX/physx/include/extensions/PxRevoluteJoint.h new file mode 100644 index 000000000..d7d1c685c --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRevoluteJoint.h @@ -0,0 +1,332 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_REVOLUTEJOINT_H +#define PX_REVOLUTEJOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" +#include "extensions/PxJointLimit.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxRevoluteJoint; + +/** +\brief Create a revolute joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxRevoluteJoint +*/ +PxRevoluteJoint* PxRevoluteJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + +/** +\brief Flags specific to the Revolute Joint. + +@see PxRevoluteJoint +*/ +struct PxRevoluteJointFlag +{ + enum Enum + { + eLIMIT_ENABLED = 1<<0, //!< enable the limit + eDRIVE_ENABLED = 1<<1, //!< enable the drive + eDRIVE_FREESPIN = 1<<2 //!< if the existing velocity is beyond the drive velocity, do not add force + }; +}; + +typedef PxFlags PxRevoluteJointFlags; +PX_FLAGS_OPERATORS(PxRevoluteJointFlag::Enum, PxU16) + +/** + +\brief A joint which behaves in a similar way to a hinge or axle. + + A hinge joint removes all but a single rotational degree of freedom from two objects. + The axis along which the two bodies may rotate is specified with a point and a direction + vector. + + The position of the hinge on each body is specified by the origin of the body's joint frame. + The axis of the hinge is specified as the direction of the x-axis in the body's joint frame. + + \image html revoluteJoint.png + + A revolute joint can be given a motor, so that it can apply a force to rotate the attached actors. + It may also be given a limit, to restrict the revolute motion to within a certain range. In + addition, the bodies may be projected together if the distance or angle between them exceeds + a given threshold. + + Projection, drive and limits are activated by setting the appropriate flags on the joint. + + @see PxRevoluteJointCreate() PxJoint +*/ +class PxRevoluteJoint : public PxJoint +{ +public: + + /** + \brief return the angle of the joint, in the range (-2*Pi, 2*Pi] + */ + virtual PxReal getAngle() const = 0; + + /** + \brief return the velocity of the joint + */ + virtual PxReal getVelocity() const = 0; + + /** + \brief set the joint limit parameters. + + The limit is activated using the flag PxRevoluteJointFlag::eLIMIT_ENABLED + + The limit angle range is (-2*Pi, 2*Pi). + + \param[in] limits The joint limit parameters. + + @see PxJointAngularLimitPair getLimit() + */ + virtual void setLimit(const PxJointAngularLimitPair& limits) = 0; + + /** + \brief get the joint limit parameters. + + \return the joint limit parameters + + @see PxJointAngularLimitPair setLimit() + */ + virtual PxJointAngularLimitPair getLimit() const = 0; + + /** + \brief set the target velocity for the drive model. + + The motor will only be able to reach this velocity if the maxForce is sufficiently large. + If the joint is spinning faster than this velocity, the motor will actually try to brake + (see PxRevoluteJointFlag::eDRIVE_FREESPIN.) + + If you set this to infinity then the motor will keep speeding up, unless there is some sort + of resistance on the attached bodies. The sign of this variable determines the rotation direction, + with positive values going the same way as positive joint angles. + + \param[in] velocity the drive target velocity + \param[in] autowake Whether to wake the joint rigids up if it is asleep. + + Range: [0, PX_MAX_F32)
+ Default: 0.0 + + @see PxRevoluteFlags::eDRIVE_FREESPIN + */ + virtual void setDriveVelocity(PxReal velocity, bool autowake = true) = 0; + + /** + \brief gets the target velocity for the drive model. + + \return the drive target velocity + + @see setDriveVelocity() + */ + virtual PxReal getDriveVelocity() const = 0; + + /** + \brief sets the maximum torque the drive can exert. + + Setting this to a very large value if velTarget is also very large may cause unexpected results. + + The value set here may be used either as an impulse limit or a force limit, depending on the flag PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES + + Range: [0, PX_MAX_F32)
+ Default: PX_MAX_F32 + + @see setDriveVelocity() + */ + virtual void setDriveForceLimit(PxReal limit) = 0; + + /** + \brief gets the maximum torque the drive can exert. + + \return the torque limit + + @see setDriveVelocity() + */ + virtual PxReal getDriveForceLimit() const = 0; + + /** + \brief sets the gear ratio for the drive. + + When setting up the drive constraint, the velocity of the first actor is scaled by this value, and its response to drive torque is scaled down. + So if the drive target velocity is zero, the second actor will be driven to the velocity of the first scaled by the gear ratio + + Range: [0, PX_MAX_F32)
+ Default: 1.0 + + \param[in] ratio the drive gear ratio + + @see getDriveGearRatio() + */ + virtual void setDriveGearRatio(PxReal ratio) = 0; + + /** + \brief gets the gear ratio. + + \return the drive gear ratio + + @see setDriveGearRatio() + */ + virtual PxReal getDriveGearRatio() const = 0; + + /** + \brief sets the flags specific to the Revolute Joint. + + Default PxRevoluteJointFlags(0) + + \param[in] flags The joint flags. + + @see PxRevoluteJointFlag setFlag() getFlags() + */ + virtual void setRevoluteJointFlags(PxRevoluteJointFlags flags) = 0; + + /** + \brief sets a single flag specific to a Revolute Joint. + + \param[in] flag The flag to set or clear. + \param[in] value the value to which to set the flag + + @see PxRevoluteJointFlag, getFlags() setFlags() + */ + virtual void setRevoluteJointFlag(PxRevoluteJointFlag::Enum flag, bool value) = 0; + + /** + \brief gets the flags specific to the Revolute Joint. + + \return the joint flags + + @see PxRevoluteJoint::flags, PxRevoluteJointFlag setFlag() setFlags() + */ + virtual PxRevoluteJointFlags getRevoluteJointFlags() const = 0; + + /** + \brief Set the linear tolerance threshold for projection. Projection is enabled if PxConstraintFlag::ePROJECTION + is set for the joint. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0, PX_MAX_F32)
+ Default: 1e10f + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() PxJoint::setConstraintFlags() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionLinearTolerance(PxReal tolerance) = 0; + + /** + \brief Get the linear tolerance threshold for projection. + + \return the linear tolerance threshold + + @see setProjectionLinearTolerance() + */ + virtual PxReal getProjectionLinearTolerance() const = 0; + + /** + \brief Set the angular tolerance threshold for projection. Projection is enabled if + PxConstraintFlag::ePROJECTION is set for the joint. + + If the joint deviates by more than this angle around its locked angular degrees of freedom, + the solver will move the bodies to close the angle. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0,Pi]
+ Default: Pi + + \param[in] tolerance the angular tolerance threshold in radians + + @see getProjectionAngularTolerance() PxJoint::setConstraintFlag() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionAngularTolerance(PxReal tolerance) = 0; + + /** + \brief gets the angular tolerance threshold for projection. + + \return the angular tolerance threshold in radians + + @see setProjectionAngularTolerance() + */ + virtual PxReal getProjectionAngularTolerance() const = 0; + + /** + \brief Returns string name of PxRevoluteJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxRevoluteJoint"; } + +protected: + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxRevoluteJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxRevoluteJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxRevoluteJoint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxRigidActorExt.h b/src/PhysX/physx/include/extensions/PxRigidActorExt.h new file mode 100644 index 000000000..2a437be7f --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRigidActorExt.h @@ -0,0 +1,155 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_RIGIDACTOR_H +#define PX_PHYSICS_EXTENSIONS_RIGIDACTOR_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "PxPhysics.h" +#include "PxRigidActor.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief utility functions for use with PxRigidActor and subclasses + +@see PxRigidActor PxRigidStatic PxRigidBody PxRigidDynamic PxArticulationLink +*/ + +class PxRigidActorExt +{ +public: + + /** + \brief Creates a new shape with default properties and a list of materials and adds it to the list of shapes of this actor. + + This is equivalent to the following + + PxShape* shape(...) = PxGetPhysics().createShape(...); // reference count is 1 + actor->attachShape(shape); // increments reference count + shape->release(); // releases user reference, leaving reference count at 1 + + As a consequence, detachShape() will result in the release of the last reference, and the shape will be deleted. + + \note The default shape flags to be set are: eVISUALIZATION, eSIMULATION_SHAPE, eSCENE_QUERY_SHAPE (see #PxShapeFlag). + Triangle mesh, heightfield or plane geometry shapes configured as eSIMULATION_SHAPE are not supported for + non-kinematic PxRigidDynamic instances. + + \note Creating compounds with a very large number of shapes may adversely affect performance and stability. + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] actor the actor to which to attach the shape + \param[in] geometry the geometry of the shape + \param[in] materials a pointer to an array of material pointers + \param[in] materialCount the count of materials + \param[in] shapeFlags optional PxShapeFlags + + \return The newly created shape. + + @see PxShape PxShape::release(), PxPhysics::createShape(), PxRigidActor::attachShape() + */ + + static PxShape* createExclusiveShape(PxRigidActor& actor, const PxGeometry& geometry, PxMaterial*const* materials, PxU16 materialCount, + PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) + { + PxShape* shape = PxGetPhysics().createShape(geometry, materials, materialCount, true, shapeFlags); + if(shape) + { + bool status = actor.attachShape(*shape); // attach can fail, if e.g. we try and attach a trimesh simulation shape to a dynamic actor + shape->release(); // if attach fails, we hold the only counted reference, and so this cleans up properly + if(!status) + shape = NULL; + } + return shape; + } + + /** + \brief Creates a new shape with default properties and a single material adds it to the list of shapes of this actor. + + This is equivalent to the following + + PxShape* shape(...) = PxGetPhysics().createShape(...); // reference count is 1 + actor->attachShape(shape); // increments reference count + shape->release(); // releases user reference, leaving reference count at 1 + + As a consequence, detachShape() will result in the release of the last reference, and the shape will be deleted. + + \note The default shape flags to be set are: eVISUALIZATION, eSIMULATION_SHAPE, eSCENE_QUERY_SHAPE (see #PxShapeFlag). + Triangle mesh, heightfield or plane geometry shapes configured as eSIMULATION_SHAPE are not supported for + non-kinematic PxRigidDynamic instances. + + \note Creating compounds with a very large number of shapes may adversely affect performance and stability. + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] actor the actor to which to attach the shape + \param[in] geometry the geometry of the shape + \param[in] material the material for the shape + \param[in] shapeFlags optional PxShapeFlags + + \return The newly created shape. + + @see PxShape PxShape::release(), PxPhysics::createShape(), PxRigidActor::attachShape() + */ + + static PX_FORCE_INLINE PxShape* createExclusiveShape(PxRigidActor& actor, const PxGeometry& geometry, const PxMaterial& material, + PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) + { + PxMaterial* materialPtr = const_cast(&material); + return createExclusiveShape(actor, geometry, &materialPtr, 1, shapeFlags); + } + + + /** + \brief Gets a list of bounds based on shapes in rigid actor. This list can be used to cook/create + bounding volume hierarchy though PxCooking API. + + \param[in] actor The actor from which the bounds list is retrieved. + \param[out] numBounds Number of bounds in returned list. + + @see PxShape PxBVHStructure PxCooking::createBVHStructure PxCooking::cookBVHStructure + */ + static PxBounds3* getRigidActorShapeLocalBoundsList(const PxRigidActor& actor, PxU32& numBounds); + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxRigidBodyExt.h b/src/PhysX/physx/include/extensions/PxRigidBodyExt.h new file mode 100644 index 000000000..f7c8f53ed --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRigidBodyExt.h @@ -0,0 +1,460 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_RIGIDBODY_H +#define PX_PHYSICS_EXTENSIONS_RIGIDBODY_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "PxRigidBody.h" +#include "PxQueryReport.h" +#include "PxQueryFiltering.h" +#include "extensions/PxMassProperties.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxScene; +struct PxQueryCache; +class PxShape; + +/** +\brief utility functions for use with PxRigidBody and subclasses + +@see PxRigidBody PxRigidDynamic PxArticulationLink +*/ + +class PxRigidBodyExt +{ +public: + /** + \brief Computation of mass properties for a rigid body actor + + To simulate a dynamic rigid actor, the SDK needs a mass and an inertia tensor. + + This method offers functionality to compute the necessary mass and inertia properties based on the shapes declared in + the PxRigidBody descriptor and some additionally specified parameters. For each shape, the shape geometry, + the shape positioning within the actor and the specified shape density are used to compute the body's mass and + inertia properties. + +
    +
  • Shapes without PxShapeFlag::eSIMULATION_SHAPE set are ignored unless includeNonSimShapes is true.
  • +
  • Shapes with plane, triangle mesh or heightfield geometry and PxShapeFlag::eSIMULATION_SHAPE set are not allowed for PxRigidBody collision.
  • +
+ + This method will set the mass, center of mass, and inertia tensor + + if no collision shapes are found, the inertia tensor is set to (1,1,1) and the mass to 1 + + if massLocalPose is non-NULL, the rigid body's center of mass parameter will be set + to the user provided value (massLocalPose) and the inertia tensor will be resolved at that point. + + \note If all shapes of the actor have the same density then the overloaded method updateMassAndInertia() with a single density parameter can be used instead. + + \param[in,out] body The rigid body. + \param[in] shapeDensities The per shape densities. There must be one entry for each shape which has the PxShapeFlag::eSIMULATION_SHAPE set (or for all shapes if includeNonSimShapes is set to true). Other shapes are ignored. The density values must be greater than 0. + \param[in] shapeDensityCount The number of provided density values. + \param[in] massLocalPose The center of mass relative to the actor frame. If set to null then (0,0,0) is assumed. + \param[in] includeNonSimShapes True if all kind of shapes (PxShapeFlag::eSCENE_QUERY_SHAPE, PxShapeFlag::eTRIGGER_SHAPE) should be taken into account. + \return Boolean. True on success else false. + + @see PxRigidBody::setMassLocalPose PxRigidBody::setMassSpaceInertiaTensor PxRigidBody::setMass + */ + static bool updateMassAndInertia(PxRigidBody& body, const PxReal* shapeDensities, PxU32 shapeDensityCount, const PxVec3* massLocalPose = NULL, bool includeNonSimShapes = false); + + + /** + \brief Computation of mass properties for a rigid body actor + + See previous method for details. + + \param[in,out] body The rigid body. + \param[in] density The density of the body. Used to compute the mass of the body. The density must be greater than 0. + \param[in] massLocalPose The center of mass relative to the actor frame. If set to null then (0,0,0) is assumed. + \param[in] includeNonSimShapes True if all kind of shapes (PxShapeFlag::eSCENE_QUERY_SHAPE, PxShapeFlag::eTRIGGER_SHAPE) should be taken into account. + \return Boolean. True on success else false. + + @see PxRigidBody::setMassLocalPose PxRigidBody::setMassSpaceInertiaTensor PxRigidBody::setMass + */ + static bool updateMassAndInertia(PxRigidBody& body, PxReal density, const PxVec3* massLocalPose = NULL, bool includeNonSimShapes = false); + + + /** + \brief Computation of mass properties for a rigid body actor + + This method sets the mass, inertia and center of mass of a rigid body. The mass is set to the sum of all user-supplied + shape mass values, and the inertia and center of mass are computed according to the rigid body's shapes and the per shape mass input values. + + If no collision shapes are found, the inertia tensor is set to (1,1,1) + + \note If a single mass value should be used for the actor as a whole then the overloaded method setMassAndUpdateInertia() with a single mass parameter can be used instead. + + @see updateMassAndInertia for more details. + + \param[in,out] body The rigid body for which to set the mass and centre of mass local pose properties. + \param[in] shapeMasses The per shape mass values. There must be one entry for each shape which has the PxShapeFlag::eSIMULATION_SHAPE set. Other shapes are ignored. The mass values must be greater than 0. + \param[in] shapeMassCount The number of provided mass values. + \param[in] massLocalPose The center of mass relative to the actor frame. If set to null then (0,0,0) is assumed. + \param[in] includeNonSimShapes True if all kind of shapes (PxShapeFlag::eSCENE_QUERY_SHAPE, PxShapeFlag::eTRIGGER_SHAPE) should be taken into account. + \return Boolean. True on success else false. + + @see PxRigidBody::setCMassLocalPose PxRigidBody::setMassSpaceInertiaTensor PxRigidBody::setMass + */ + static bool setMassAndUpdateInertia(PxRigidBody& body, const PxReal* shapeMasses, PxU32 shapeMassCount, const PxVec3* massLocalPose = NULL, bool includeNonSimShapes = false); + + + /** + \brief Computation of mass properties for a rigid body actor + + This method sets the mass, inertia and center of mass of a rigid body. The mass is set to the user-supplied + value, and the inertia and center of mass are computed according to the rigid body's shapes and the input mass. + + If no collision shapes are found, the inertia tensor is set to (1,1,1) + + @see updateMassAndInertia for more details. + + \param[in,out] body The rigid body for which to set the mass and centre of mass local pose properties. + \param[in] mass The mass of the body. Must be greater than 0. + \param[in] massLocalPose The center of mass relative to the actor frame. If set to null then (0,0,0) is assumed. + \param[in] includeNonSimShapes True if all kind of shapes (PxShapeFlag::eSCENE_QUERY_SHAPE, PxShapeFlag::eTRIGGER_SHAPE) should be taken into account. + \return Boolean. True on success else false. + + @see PxRigidBody::setCMassLocalPose PxRigidBody::setMassSpaceInertiaTensor PxRigidBody::setMass + */ + static bool setMassAndUpdateInertia(PxRigidBody& body, PxReal mass, const PxVec3* massLocalPose = NULL, bool includeNonSimShapes = false); + + + /** + \brief Compute the mass, inertia tensor and center of mass from a list of shapes. + + \param[in] shapes The shapes to compute the mass properties from. + \param[in] shapeCount The number of provided shapes. + \return The mass properties from the combined shapes. + + @see PxRigidBody::setCMassLocalPose PxRigidBody::setMassSpaceInertiaTensor PxRigidBody::setMass + */ + static PxMassProperties computeMassPropertiesFromShapes(const PxShape* const* shapes, PxU32 shapeCount); + + + /** + \brief Applies a force (or impulse) defined in the global coordinate frame, acting at a particular + point in global coordinates, to the actor. + + Note that if the force does not act along the center of mass of the actor, this + will also add the corresponding torque. Because forces are reset at the end of every timestep, + you can maintain a total external force on an object by calling this once every frame. + + \note if this call is used to apply a force or impulse to an articulation link, only the link is updated, not the entire + articulation + + ::PxForceMode determines if the force is to be conventional or impulsive. Only eFORCE and eIMPULSE are supported, as the + force required to produce a given velocity change or acceleration is underdetermined given only the desired change at a + given point. + + Sleeping: This call wakes the actor if it is sleeping and the wakeup parameter is true (default). + + \param[in] body The rigid body to apply the force to. + \param[in] force Force/impulse to add, defined in the global frame. Range: force vector + \param[in] pos Position in the global frame to add the force at. Range: position vector + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode). + \param[in] wakeup Specify if the call should wake up the actor. + + @see PxForceMode + @see addForceAtLocalPos() addLocalForceAtPos() addLocalForceAtLocalPos() + */ + static void addForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode = PxForceMode::eFORCE, bool wakeup = true); + + /** + \brief Applies a force (or impulse) defined in the global coordinate frame, acting at a particular + point in local coordinates, to the actor. + + Note that if the force does not act along the center of mass of the actor, this + will also add the corresponding torque. Because forces are reset at the end of every timestep, you can maintain a + total external force on an object by calling this once every frame. + + \note if this call is used to apply a force or impulse to an articulation link, only the link is updated, not the entire + articulation + + ::PxForceMode determines if the force is to be conventional or impulsive. Only eFORCE and eIMPULSE are supported, as the + force required to produce a given velocity change or acceleration is underdetermined given only the desired change at a + given point. + + Sleeping: This call wakes the actor if it is sleeping and the wakeup parameter is true (default). + + \param[in] body The rigid body to apply the force to. + \param[in] force Force/impulse to add, defined in the global frame. Range: force vector + \param[in] pos Position in the local frame to add the force at. Range: position vector + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode). + \param[in] wakeup Specify if the call should wake up the actor. + + @see PxForceMode + @see addForceAtPos() addLocalForceAtPos() addLocalForceAtLocalPos() + */ + static void addForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode = PxForceMode::eFORCE, bool wakeup = true); + + /** + \brief Applies a force (or impulse) defined in the actor local coordinate frame, acting at a + particular point in global coordinates, to the actor. + + Note that if the force does not act along the center of mass of the actor, this + will also add the corresponding torque. Because forces are reset at the end of every timestep, you can maintain a + total external force on an object by calling this once every frame. + + \note if this call is used to apply a force or impulse to an articulation link, only the link is updated, not the entire + articulation + + ::PxForceMode determines if the force is to be conventional or impulsive. Only eFORCE and eIMPULSE are supported, as the + force required to produce a given velocity change or acceleration is underdetermined given only the desired change at a + given point. + + Sleeping: This call wakes the actor if it is sleeping and the wakeup parameter is true (default). + + \param[in] body The rigid body to apply the force to. + \param[in] force Force/impulse to add, defined in the local frame. Range: force vector + \param[in] pos Position in the global frame to add the force at. Range: position vector + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode). + \param[in] wakeup Specify if the call should wake up the actor. + + @see PxForceMode + @see addForceAtPos() addForceAtLocalPos() addLocalForceAtLocalPos() + */ + static void addLocalForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode = PxForceMode::eFORCE, bool wakeup = true); + + /** + \brief Applies a force (or impulse) defined in the actor local coordinate frame, acting at a + particular point in local coordinates, to the actor. + + Note that if the force does not act along the center of mass of the actor, this + will also add the corresponding torque. Because forces are reset at the end of every timestep, you can maintain a + total external force on an object by calling this once every frame. + + \note if this call is used to apply a force or impulse to an articulation link, only the link is updated, not the entire + articulation + + ::PxForceMode determines if the force is to be conventional or impulsive. Only eFORCE and eIMPULSE are supported, as the + force required to produce a given velocity change or acceleration is underdetermined given only the desired change at a + given point. + + Sleeping: This call wakes the actor if it is sleeping and the wakeup parameter is true (default). + + \param[in] body The rigid body to apply the force to. + \param[in] force Force/impulse to add, defined in the local frame. Range: force vector + \param[in] pos Position in the local frame to add the force at. Range: position vector + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode). + \param[in] wakeup Specify if the call should wake up the actor. + + @see PxForceMode + @see addForceAtPos() addForceAtLocalPos() addLocalForceAtPos() + */ + static void addLocalForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode = PxForceMode::eFORCE, bool wakeup = true); + + /** + \brief Computes the velocity of a point given in world coordinates if it were attached to the + specified body and moving with it. + + \param[in] body The rigid body the point is attached to. + \param[in] pos Position we wish to determine the velocity for, defined in the global frame. Range: position vector + \return The velocity of point in the global frame. + + @see getLocalPointVelocity() + */ + static PxVec3 getVelocityAtPos(const PxRigidBody& body, const PxVec3& pos); + + /** + \brief Computes the velocity of a point given in local coordinates if it were attached to the + specified body and moving with it. + + \param[in] body The rigid body the point is attached to. + \param[in] pos Position we wish to determine the velocity for, defined in the local frame. Range: position vector + \return The velocity of point in the local frame. + + @see getLocalPointVelocity() + */ + static PxVec3 getLocalVelocityAtLocalPos(const PxRigidBody& body, const PxVec3& pos); + + /** + \brief Computes the velocity of a point (offset from the origin of the body) given in world coordinates if it were attached to the + specified body and moving with it. + + \param[in] body The rigid body the point is attached to. + \param[in] pos Position (offset from the origin of the body) we wish to determine the velocity for, defined in the global frame. Range: position vector + \return The velocity of point (offset from the origin of the body) in the global frame. + + @see getLocalPointVelocity() + */ + static PxVec3 getVelocityAtOffset(const PxRigidBody& body, const PxVec3& pos); + + + /** + \brief Performs a linear sweep through space with the body's geometry objects. + + \note Supported geometries are: box, sphere, capsule, convex. Other geometry types will be ignored. + \note If eTOUCH is returned from the filter callback, it will trigger an error and the hit will be discarded. + + The function sweeps all shapes attached to a given rigid body through space and reports the nearest + object in the scene which intersects any of of the shapes swept paths. + Information about the closest intersection is written to a #PxSweepHit structure. + + \param[in] body The rigid body to sweep. + \param[in] scene The scene object to process the query. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. + \param[in] outputFlags Specifies which properties should be written to the hit information. + \param[out] closestHit Closest hit result. + \param[out] shapeIndex Index of the body shape that caused the closest hit. + \param[in] filterData If any word in filterData.data is non-zero then filterData.data will be used for filtering, + otherwise shape->getQueryFilterData() will be used instead. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxQueryFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + + \return True if a blocking hit was found. + + @see PxScene PxQueryFlags PxFilterData PxBatchQueryPreFilterShader PxBatchQueryPostFilterShader PxSweepHit + */ + static bool linearSweepSingle( + PxRigidBody& body, PxScene& scene, const PxVec3& unitDir, const PxReal distance, + PxHitFlags outputFlags, + PxSweepHit& closestHit, PxU32& shapeIndex, + const PxQueryFilterData& filterData = PxQueryFilterData(), + PxQueryFilterCallback* filterCall = NULL, + const PxQueryCache* cache = NULL, + const PxReal inflation=0.0f); + + /** + \brief Performs a linear sweep through space with the body's geometry objects, returning all overlaps. + + \note Supported geometries are: box, sphere, capsule, convex. Other geometry types will be ignored. + + This function sweeps all shapes attached to a given rigid body through space and reports all + objects in the scene that intersect any of the shapes' swept paths until there are no more objects to report + or a blocking hit is encountered. + + \param[in] body The rigid body to sweep. + \param[in] scene The scene object to process the query. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. + \param[in] outputFlags Specifies which properties should be written to the hit information. + \param[out] touchHitBuffer Raycast hit information buffer. If the buffer overflows, an arbitrary subset of touch hits + is returned (typically the query should be restarted with a larger buffer). + \param[out] touchHitShapeIndices After the query is completed, touchHitShapeIndices[i] will contain the body index that caused the hit stored in hitBuffer[i] + \param[in] touchHitBufferSize Size of both touch hit buffers in elements. + \param[out] block Closest blocking hit is returned via this reference. + \param[out] blockingShapeIndex Set to -1 if if a blocking hit was not found, otherwise set to closest blocking hit shape index. The touching hits are reported separately in hitBuffer. + \param[out] overflow Set to true if touchHitBuffer didn't have enough space for all results. Touch hits will be incomplete if overflow occurred. Possible solution is to restart the query with a larger buffer. + \param[in] filterData If any word in filterData.data is non-zero then filterData.data will be used for filtering, + otherwise shape->getQueryFilterData() will be used instead. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxQueryFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + + \return the number of touching hits. If overflow is set to true, the results are incomplete. In case of overflow there are also no guarantees that all touching hits returned are closer than the blocking hit. + + @see PxScene PxQueryFlags PxFilterData PxBatchQueryPreFilterShader PxBatchQueryPostFilterShader PxSweepHit + */ + static PxU32 linearSweepMultiple( + PxRigidBody& body, PxScene& scene, const PxVec3& unitDir, const PxReal distance, + PxHitFlags outputFlags, + PxSweepHit* touchHitBuffer, PxU32* touchHitShapeIndices, PxU32 touchHitBufferSize, + PxSweepHit& block, PxI32& blockingShapeIndex, bool& overflow, + const PxQueryFilterData& filterData = PxQueryFilterData(), + PxQueryFilterCallback* filterCall = NULL, + const PxQueryCache* cache = NULL, const PxReal inflation = 0.0f); + + + /** + \brief Compute the change to linear and angular velocity that would occur if an impulsive force and torque were to be applied to a specified rigid body. + + The rigid body is left unaffected unless a subsequent independent call is executed that actually applies the computed changes to velocity and angular velocity. + + \note if this call is used to determine the velocity delta for an articulation link, only the mass properties of the link are taken into account. + + @see PxRigidBody::getLinearVelocity, PxRigidBody::setLinearVelocity, PxRigidBody::getAngularVelocity, PxRigidBody::setAngularVelocity + + \param[in] body The body under consideration. + \param[in] impulsiveForce The impulsive force that would be applied to the specified rigid body. + \param[in] impulsiveTorque The impulsive torque that would be applied to the specified rigid body. + \param[out] deltaLinearVelocity The change in linear velocity that would arise if impulsiveForce was to be applied to the specified rigid body. + \param[out] deltaAngularVelocity The change in angular velocity that would arise if impulsiveTorque was to be applied to the specified rigid body. + */ + static void computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxVec3& impulsiveForce, const PxVec3& impulsiveTorque, PxVec3& deltaLinearVelocity, PxVec3& deltaAngularVelocity); + + /** + \brief Computes the linear and angular velocity change vectors for a given impulse at a world space position taking a mass and inertia scale into account + + This function is useful for extracting the respective linear and angular velocity changes from a contact or joint when the mass/inertia ratios have been adjusted. + + \note if this call is used to determine the velocity delta for an articulation link, only the mass properties of the link are taken into account. + + \param[in] body The rigid body + \param[in] globalPose The body's world space transform + \param[in] point The point in world space where the impulse is applied + \param[in] impulse The impulse vector in world space + \param[in] invMassScale The inverse mass scale + \param[in] invInertiaScale The inverse inertia scale + \param[out] deltaLinearVelocity The linear velocity change + \param[out] deltaAngularVelocity The angular velocity change + */ + + static void computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale, + const PxReal invInertiaScale, PxVec3& deltaLinearVelocity, PxVec3& deltaAngularVelocity); + + /** + \brief Computes the linear and angular impulse vectors for a given impulse at a world space position taking a mass and inertia scale into account + + This function is useful for extracting the respective linear and angular impulses from a contact or joint when the mass/inertia ratios have been adjusted. + + \param[in] body The rigid body + \param[in] globalPose The body's world space transform + \param[in] point The point in world space where the impulse is applied + \param[in] impulse The impulse vector in world space + \param[in] invMassScale The inverse mass scale + \param[in] invInertiaScale The inverse inertia scale + \param[out] linearImpulse The linear impulse + \param[out] angularImpulse The angular impulse + */ + static void computeLinearAngularImpulse(const PxRigidBody& body, const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale, + const PxReal invInertiaScale, PxVec3& linearImpulse, PxVec3& angularImpulse); + + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxSceneQueryExt.h b/src/PhysX/physx/include/extensions/PxSceneQueryExt.h new file mode 100644 index 000000000..1d4b86c01 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxSceneQueryExt.h @@ -0,0 +1,309 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_SCENE_QUERY_H +#define PX_PHYSICS_EXTENSIONS_SCENE_QUERY_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" + +#include "PxScene.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +// These types have been deprecated (removed) in PhysX 3.4. We typedef them to the new types here for easy migration from 3.3 to 3.4. +typedef PxQueryHit PxSceneQueryHit; +typedef PxQueryFilterData PxSceneQueryFilterData; +typedef PxQueryFilterCallback PxSceneQueryFilterCallback; +typedef PxQueryCache PxSceneQueryCache; +typedef PxHitFlag PxSceneQueryFlag; +typedef PxHitFlags PxSceneQueryFlags; + +/** +\brief utility functions for use with PxScene, related to scene queries. + +Some of these functions have been deprecated (removed) in PhysX 3.4. We re-implement them here for easy migration from 3.3 to 3.4. + +@see PxShape +*/ + +class PxSceneQueryExt +{ +public: + + /** + \brief Raycast returning any blocking hit, not necessarily the closest. + + Returns whether any rigid actor is hit along the ray. + + \note Shooting a ray from within an object leads to different results depending on the shape type. Please check the details in article SceneQuery. User can ignore such objects by using one of the provided filter mechanisms. + + \param[in] scene The scene + \param[in] origin Origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] distance Length of the ray. Needs to be larger than 0. + \param[out] hit Raycast hit information. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first. If no hit is found the ray gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \return True if a blocking hit was found. + + @see PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryCache PxSceneQueryHit + */ + static bool raycastAny( const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryHit& hit, const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, const PxSceneQueryCache* cache = NULL); + + /** + \brief Raycast returning a single result. + + Returns the first rigid actor that is hit along the ray. Data for a blocking hit will be returned as specified by the outputFlags field. Touching hits will be ignored. + + \note Shooting a ray from within an object leads to different results depending on the shape type. Please check the details in article SceneQuery. User can ignore such objects by using one of the provided filter mechanisms. + + \param[in] scene The scene + \param[in] origin Origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] distance Length of the ray. Needs to be larger than 0. + \param[in] outputFlags Specifies which properties should be written to the hit information + \param[out] hit Raycast hit information. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \return True if a blocking hit was found. + + @see PxSceneQueryFlags PxRaycastHit PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryCache + */ + static bool raycastSingle( const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, PxRaycastHit& hit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, const PxSceneQueryCache* cache = NULL); + + /** + \brief Raycast returning multiple results. + + Find all rigid actors that get hit along the ray. Each result contains data as specified by the outputFlags field. + + \note Touching hits are not ordered. + + \note Shooting a ray from within an object leads to different results depending on the shape type. Please check the details in article SceneQuery. User can ignore such objects by using one of the provided filter mechanisms. + + \param[in] scene The scene + \param[in] origin Origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] distance Length of the ray. Needs to be larger than 0. + \param[in] outputFlags Specifies which properties should be written to the hit information + \param[out] hitBuffer Raycast hit information buffer. If the buffer overflows, the blocking hit is returned as the last entry together with an arbitrary subset + of the nearer touching hits (typically the query should be restarted with a larger buffer). + \param[in] hitBufferSize Size of the hit buffer. + \param[out] blockingHit True if a blocking hit was found. If found, it is the last in the buffer, preceded by any touching hits which are closer. Otherwise the touching hits are listed. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be touching. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \return Number of hits in the buffer, or -1 if the buffer overflowed. + + @see PxSceneQueryFlags PxRaycastHit PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryCache + */ + static PxI32 raycastMultiple( const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, + PxRaycastHit* hitBuffer, PxU32 hitBufferSize, bool& blockingHit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, const PxSceneQueryCache* cache = NULL); + + /** + \brief Sweep returning any blocking hit, not necessarily the closest. + + Returns whether any rigid actor is hit along the sweep path. + + \note If a shape from the scene is already overlapping with the query shape in its starting position, behavior is controlled by the PxSceneQueryFlag::eINITIAL_OVERLAP flag. + + \param[in] scene The scene + \param[in] geometry Geometry of object to sweep (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the sweep object. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. Will be clamped to PX_MAX_SWEEP_DISTANCE. + \param[in] queryFlags Combination of PxSceneQueryFlag defining the query behavior + \param[out] hit Sweep hit information. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Sweep is performed against cached shape first. If no hit is found the sweep gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + \return True if a blocking hit was found. + + @see PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryHit PxSceneQueryCache + */ + static bool sweepAny( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags queryFlags, + PxSceneQueryHit& hit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, + const PxSceneQueryCache* cache = NULL, + PxReal inflation = 0.0f); + + /** + \brief Sweep returning a single result. + + Returns the first rigid actor that is hit along the ray. Data for a blocking hit will be returned as specified by the outputFlags field. Touching hits will be ignored. + + \note If a shape from the scene is already overlapping with the query shape in its starting position, behavior is controlled by the PxSceneQueryFlag::eINITIAL_OVERLAP flag. + + \param[in] scene The scene + \param[in] geometry Geometry of object to sweep (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the sweep object. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. Will be clamped to PX_MAX_SWEEP_DISTANCE. + \param[in] outputFlags Specifies which properties should be written to the hit information. + \param[out] hit Sweep hit information. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Sweep is performed against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + \return True if a blocking hit was found. + + @see PxSceneQueryFlags PxSweepHit PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryCache + */ + static bool sweepSingle(const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, + PxSweepHit& hit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, + const PxSceneQueryCache* cache = NULL, + PxReal inflation=0.0f); + + /** + \brief Sweep returning multiple results. + + Find all rigid actors that get hit along the sweep. Each result contains data as specified by the outputFlags field. + + \note Touching hits are not ordered. + + \note If a shape from the scene is already overlapping with the query shape in its starting position, behavior is controlled by the PxSceneQueryFlag::eINITIAL_OVERLAP flag. + + \param[in] scene The scene + \param[in] geometry Geometry of object to sweep (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the sweep object. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. Will be clamped to PX_MAX_SWEEP_DISTANCE. + \param[in] outputFlags Specifies which properties should be written to the hit information. + \param[out] hitBuffer Sweep hit information buffer. If the buffer overflows, the blocking hit is returned as the last entry together with an arbitrary subset + of the nearer touching hits (typically the query should be restarted with a larger buffer). + \param[in] hitBufferSize Size of the hit buffer. + \param[out] blockingHit True if a blocking hit was found. If found, it is the last in the buffer, preceded by any touching hits which are closer. Otherwise the touching hits are listed. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be touching. + \param[in] cache Cached hit shape (optional). Sweep is performed against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + \return Number of hits in the buffer, or -1 if the buffer overflowed. + + @see PxSceneQueryFlags PxSweepHit PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryCache + */ + static PxI32 sweepMultiple( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, PxSweepHit* hitBuffer, PxU32 hitBufferSize, bool& blockingHit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, const PxSceneQueryCache* cache = NULL, + PxReal inflation = 0.0f); + + /** + \brief Test overlap between a geometry and objects in the scene. + + \note Filtering: Overlap tests do not distinguish between touching and blocking hit types. Both get written to the hit buffer. + + \note PxHitFlag::eMESH_MULTIPLE and PxHitFlag::eMESH_BOTH_SIDES have no effect in this case + + \param[in] scene The scene + \param[in] geometry Geometry of object to check for overlap (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the object. + \param[out] hitBuffer Buffer to store the overlapping objects to. If the buffer overflows, an arbitrary subset of overlapping objects is stored (typically the query should be restarted with a larger buffer). + \param[in] hitBufferSize Size of the hit buffer. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to overlap. + \return Number of hits in the buffer, or -1 if the buffer overflowed. + + @see PxSceneQueryFlags PxSceneQueryFilterData PxSceneQueryFilterCallback + */ + static PxI32 overlapMultiple( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, + PxOverlapHit* hitBuffer, PxU32 hitBufferSize, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL); + + /** + \brief Test returning, for a given geometry, any overlapping object in the scene. + + \note Filtering: Overlap tests do not distinguish between touching and blocking hit types. Both trigger a hit. + + \note PxHitFlag::eMESH_MULTIPLE and PxHitFlag::eMESH_BOTH_SIDES have no effect in this case + + \param[in] scene The scene + \param[in] geometry Geometry of object to check for overlap (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the object. + \param[out] hit Pointer to store the overlapping object to. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to overlap. + \return True if an overlap was found. + + @see PxSceneQueryFlags PxSceneQueryFilterData PxSceneQueryFilterCallback + */ + static bool overlapAny( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, + PxOverlapHit& hit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL); +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxSerialization.h b/src/PhysX/physx/include/extensions/PxSerialization.h new file mode 100644 index 000000000..2d111ca78 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxSerialization.h @@ -0,0 +1,284 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_SERIALIZATION_H +#define PX_SERIALIZATION_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" +#include "cooking/PxCooking.h" +#include "foundation/PxIO.h" +#include "common/PxTolerancesScale.h" +#include "common/PxTypeInfo.h" +#include "common/PxStringTable.h" + +// +// Important: if you adjust the comment about binary compatible versions below, don't forget to adjust the compatibility list in +// sBinaryCompatibleVersionsbinary as well +// +/** +PX_BINARY_SERIAL_VERSION is used to specify the binary data format compatibility additionally to the physics sdk version. +The binary format version is defined as "PX_PHYSICS_VERSION_MAJOR.PX_PHYSICS_VERSION_MINOR.PX_PHYSICS_VERSION_BUGFIX-PX_BINARY_SERIAL_VERSION". +The following binary format versions are compatible with the current physics version: + (no compatible versions) + +The PX_BINARY_SERIAL_VERSION for a given PhysX release is typically 0. If incompatible modifications are made to a customer specific branch the +number should be increased. +*/ +#define PX_BINARY_SERIAL_VERSION 0 + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxBinaryConverter; + +/** +\brief Utility functions for serialization + +@see PxCollection, PxSerializationRegistry +*/ +class PxSerialization +{ +public: + /** + \brief Additional PxScene and PxPhysics options stored in XML serialized data. + + The PxXmlMiscParameter parameter can be serialized and deserialized along with PxCollection instances (XML only). + This is for application use only and has no impact on how objects are serialized or deserialized. + @see PxSerialization::createCollectionFromXml, PxSerialization::serializeCollectionToXml + */ + struct PxXmlMiscParameter + { + /** + \brief Up vector for the scene reference coordinate system. + */ + PxVec3 upVector; + + /** + \brief Tolerances scale to be used for the scene. + */ + PxTolerancesScale scale; + + PxXmlMiscParameter() : upVector(0) {} + PxXmlMiscParameter(PxVec3& inUpVector, PxTolerancesScale inScale) : upVector(inUpVector), scale(inScale) {} + }; + + /** + \brief Returns whether the collection is serializable with the externalReferences collection. + + Some definitions to explain whether a collection can be serialized or not: + + For definitions of requires and complete see #PxSerialization::complete + + A serializable object is subordinate if it cannot be serialized on its own + The following objects are subordinate: + - articulation links + - articulation joints + - joints + + A collection C can be serialized with external references collection D iff + - C is complete relative to D (no dangling references) + - Every object in D required by an object in C has a valid ID (no unnamed references) + - Every subordinate object in C is required by another object in C (no orphans) + + \param[in] collection Collection to be checked + \param[in] sr PxSerializationRegistry instance with information about registered classes. + \param[in] externalReferences the external References collection + \return Whether the collection is serializable + @see PxSerialization::complete, PxSerialization::serializeCollectionToBinary, PxSerialization::serializeCollectionToXml, PxSerializationRegistry + */ + static bool isSerializable(PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* externalReferences = NULL); + + /** + \brief Adds to a collection all objects such that it can be successfully serialized. + + A collection C is complete relative to an other collection D if every object required by C is either in C or D. + This function adds objects to a collection, such that it becomes complete with respect to the exceptFor collection. + Completeness is needed for serialization. See #PxSerialization::serializeCollectionToBinary, + #PxSerialization::serializeCollectionToXml. + + Sdk objects require other sdk object according to the following rules: + - joints require their actors and constraint + - rigid actors require their shapes + - shapes require their material(s) and mesh (triangle mesh, convex mesh or height field), if any + - articulations require their links and joints + - aggregates require their actors + + If followJoints is specified another rule is added: + - actors require their joints + + Specifying followJoints will make whole jointed actor chains being added to the collection. Following chains + is interrupted whenever a object in exceptFor is encountered. + + \param[in,out] collection Collection which is completed + \param[in] sr PxSerializationRegistry instance with information about registered classes. + \param[in] exceptFor Optional exemption collection + \param[in] followJoints Specifies whether joints should be added for jointed actors + @see PxCollection, PxSerialization::serializeCollectionToBinary, PxSerialization::serializeCollectionToXml, PxSerializationRegistry + */ + static void complete(PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* exceptFor = NULL, bool followJoints = false); + + /** + \brief Creates PxSerialObjectId values for unnamed objects in a collection. + + Creates PxSerialObjectId names for unnamed objects in a collection starting at a base value and incrementing, + skipping values that are already assigned to objects in the collection. + + \param[in,out] collection Collection for which names are created + \param[in] base Start address for PxSerialObjectId names + @see PxCollection + */ + static void createSerialObjectIds(PxCollection& collection, const PxSerialObjectId base); + + /** + \brief Creates a PxCollection from XML data. + + \param inputData The input data containing the XML collection. + \param cooking PxCooking instance used for sdk object instantiation. + \param sr PxSerializationRegistry instance with information about registered classes. + \param externalRefs PxCollection used to resolve external references. + \param stringTable PxStringTable instance used for storing object names. + \param outArgs Optional parameters of physics and scene deserialized from XML. See #PxSerialization::PxXmlMiscParameter + \return a pointer to a PxCollection if successful or NULL if it failed. + + @see PxCollection, PxSerializationRegistry, PxInputData, PxStringTable, PxCooking, PxSerialization::PxXmlMiscParameter + */ + static PxCollection* createCollectionFromXml(PxInputData& inputData, PxCooking& cooking, PxSerializationRegistry& sr, const PxCollection* externalRefs = NULL, PxStringTable* stringTable = NULL, PxXmlMiscParameter* outArgs = NULL); + + /** + \brief Deserializes a PxCollection from memory. + + Creates a collection from memory. If the collection has external dependencies another collection + can be provided to resolve these. + + The memory block provided has to be 128 bytes aligned and contain a contiguous serialized collection as written + by PxSerialization::serializeCollectionToBinary. The contained binary data needs to be compatible with the current binary format version + which is defined by "PX_PHYSICS_VERSION_MAJOR.PX_PHYSICS_VERSION_MINOR.PX_PHYSICS_VERSION_BUGFIX-PX_BINARY_SERIAL_VERSION". + For a list of compatible sdk releases refer to the documentation of PX_BINARY_SERIAL_VERSION. + + \param[in] memBlock Pointer to memory block containing the serialized collection + \param[in] sr PxSerializationRegistry instance with information about registered classes. + \param[in] externalRefs Collection to resolve external dependencies + + @see PxCollection, PxSerialization::complete, PxSerialization::serializeCollectionToBinary, PxSerializationRegistry, PX_BINARY_SERIAL_VERSION + */ + static PxCollection* createCollectionFromBinary(void* memBlock, PxSerializationRegistry& sr, const PxCollection* externalRefs = NULL); + + /** + \brief Serializes a physics collection to an XML output stream. + + The collection to be serialized needs to be complete @see PxSerialization.complete. + Optionally the XML may contain meshes in binary cooked format for fast loading. It does this when providing a valid non-null PxCooking pointer. + + \note Serialization of objects in a scene that is simultaneously being simulated is not supported and leads to undefined behavior. + + \param outputStream Stream to save collection to. + \param collection PxCollection instance which is serialized. The collection needs to be complete with respect to the externalRefs collection. + \param sr PxSerializationRegistry instance with information about registered classes. + \param cooking Optional pointer to cooking instance. If provided, cooked mesh data is cached for fast loading. + \param externalRefs Collection containing external references. + \param inArgs Optional parameters of physics and scene serialized to XML along with the collection. See #PxSerialization::PxXmlMiscParameter + \return true if the collection is successfully serialized. + + @see PxCollection, PxOutputStream, PxSerializationRegistry, PxCooking, PxSerialization::PxXmlMiscParameter + */ + static bool serializeCollectionToXml(PxOutputStream& outputStream, PxCollection& collection, PxSerializationRegistry& sr, PxCooking* cooking = NULL, const PxCollection* externalRefs = NULL, PxXmlMiscParameter* inArgs = NULL); + + /** + \brief Serializes a collection to a binary stream. + + Serializes a collection to a stream. In order to resolve external dependencies the externalReferences collection has to be provided. + Optionally names of objects that where set for example with #PxActor::setName are serialized along with the objects. + + The collection can be successfully serialized if isSerializable(collection) returns true. See #isSerializable. + + The implementation of the output stream needs to fulfill the requirements on the memory block input taken by + PxSerialization::createCollectionFromBinary. + + \note Serialization of objects in a scene that is simultaneously being simulated is not supported and leads to undefined behavior. + + \param[out] outputStream into which the collection is serialized + \param[in] collection Collection to be serialized + \param[in] sr PxSerializationRegistry instance with information about registered classes. + \param[in] externalRefs Collection used to resolve external dependencies + \param[in] exportNames Specifies whether object names are serialized + \return Whether serialization was successful + + @see PxCollection, PxOutputStream, PxSerialization::complete, PxSerialization::createCollectionFromBinary, PxSerializationRegistry + */ + static bool serializeCollectionToBinary(PxOutputStream& outputStream, PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* externalRefs = NULL, bool exportNames = false ); + + /** + \brief Dumps the binary meta-data to a stream. + + A meta-data file contains information about the SDK's internal classes and about custom user types ready + for serialization. Such a file is needed to convert binary-serialized data from one platform to another (re-targeting). + The converter needs meta-data files for the source and target platforms to perform conversions. + + Custom user types can be supported with PxSerializationRegistry::registerBinaryMetaDataCallback (see the guide for more information). + + \param[out] outputStream Stream to write meta data to + \param[in] sr PxSerializationRegistry instance with information about registered classes used for conversion. + + @see PxOutputStream, PxSerializationRegistry + */ + static void dumpBinaryMetaData(PxOutputStream& outputStream, PxSerializationRegistry& sr); + + /** + \brief Creates binary converter for re-targeting binary-serialized data. + + \return Binary converter instance. + */ + static PxBinaryConverter* createBinaryConverter(); + + /** + \brief Creates an application managed registry for serialization. + + \param[in] physics Physics SDK to generate create serialization registry + + \return PxSerializationRegistry instance. + + @see PxSerializationRegistry + */ + static PxSerializationRegistry* createSerializationRegistry(PxPhysics& physics); +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxShapeExt.h b/src/PhysX/physx/include/extensions/PxShapeExt.h new file mode 100644 index 000000000..02228d5e4 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxShapeExt.h @@ -0,0 +1,158 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_SHAPE_H +#define PX_PHYSICS_EXTENSIONS_SHAPE_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" + +#include "PxShape.h" +#include "PxRigidActor.h" +#include "geometry/PxGeometryQuery.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief utility functions for use with PxShape + +@see PxShape +*/ + +class PxShapeExt +{ +public: + /** + \brief Retrieves the world space pose of the shape. + + \param[in] shape The shape for which to get the global pose. + \param[in] actor The actor to which the shape is attached + + \return Global pose of shape. + */ + static PX_INLINE PxTransform getGlobalPose(const PxShape& shape, const PxRigidActor& actor) + { + return actor.getGlobalPose() * shape.getLocalPose(); + } + + /** + \brief Raycast test against the shape. + + \param[in] shape the shape + \param[in] actor the actor to which the shape is attached + \param[in] rayOrigin The origin of the ray to test the geometry object against + \param[in] rayDir The direction of the ray to test the geometry object against + \param[in] maxDist Maximum ray length + \param[in] hitFlags Specify which properties per hit should be computed and written to result hit array. Combination of #PxHitFlag flags + \param[in] maxHits max number of returned hits = size of 'rayHits' buffer + \param[out] rayHits Raycast hits information + \return Number of hits between the ray and the shape + + @see PxRaycastHit PxTransform + */ + static PX_INLINE PxU32 raycast(const PxShape& shape, const PxRigidActor& actor, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, PxHitFlags hitFlags, + PxU32 maxHits, PxRaycastHit* rayHits) + { + return PxGeometryQuery::raycast( + rayOrigin, rayDir, shape.getGeometry().any(), getGlobalPose(shape, actor), maxDist, hitFlags, maxHits, rayHits); + } + + /** + \brief Test overlap between the shape and a geometry object + + \param[in] shape the shape + \param[in] actor the actor to which the shape is attached + \param[in] otherGeom The other geometry object to test overlap with + \param[in] otherGeomPose Pose of the other geometry object + \return True if the shape overlaps the geometry object + + @see PxGeometry PxTransform + */ + static PX_INLINE bool overlap(const PxShape& shape, const PxRigidActor& actor, + const PxGeometry& otherGeom, const PxTransform& otherGeomPose) + { + return PxGeometryQuery::overlap(shape.getGeometry().any(), getGlobalPose(shape, actor), otherGeom, otherGeomPose); + } + + /** + \brief Sweep a geometry object against the shape. + + Currently only box, sphere, capsule and convex mesh shapes are supported, i.e. the swept geometry object must be one of those types. + + \param[in] shape the shape + \param[in] actor the actor to which the shape is attached + \param[in] unitDir Normalized direction along which the geometry object should be swept. + \param[in] distance Sweep distance. Needs to be larger than 0. + \param[in] otherGeom The geometry object to sweep against the shape + \param[in] otherGeomPose Pose of the geometry object + \param[out] sweepHit The sweep hit information. Only valid if this method returns true. + \param[in] hitFlags Specify which properties per hit should be computed and written to result hit array. Combination of #PxHitFlag flags + \return True if the swept geometry object hits the shape + + @see PxGeometry PxTransform PxSweepHit + */ + static PX_INLINE bool sweep(const PxShape& shape, const PxRigidActor& actor, + const PxVec3& unitDir, const PxReal distance, const PxGeometry& otherGeom, const PxTransform& otherGeomPose, + PxSweepHit& sweepHit, PxHitFlags hitFlags) + { + return PxGeometryQuery::sweep(unitDir, distance, otherGeom, otherGeomPose, shape.getGeometry().any(), getGlobalPose(shape, actor), sweepHit, hitFlags); + } + + + /** + \brief Retrieves the axis aligned bounding box enclosing the shape. + + \return The shape's bounding box. + + \param[in] shape the shape + \param[in] actor the actor to which the shape is attached + \param[in] inflation Scale factor for computed world bounds. Box extents are multiplied by this value. + + @see PxBounds3 + */ + static PX_INLINE PxBounds3 getWorldBounds(const PxShape& shape, const PxRigidActor& actor, float inflation=1.01f) + { + return PxGeometryQuery::getWorldBounds(shape.getGeometry().any(), getGlobalPose(shape, actor), inflation); + } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxSimpleFactory.h b/src/PhysX/physx/include/extensions/PxSimpleFactory.h new file mode 100644 index 000000000..354fe2080 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxSimpleFactory.h @@ -0,0 +1,337 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_SIMPLE_FACTORY_H +#define PX_PHYSICS_EXTENSIONS_SIMPLE_FACTORY_H +/** \addtogroup extensions + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxTransform.h" +#include "foundation/PxPlane.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxPhysics; + class PxMaterial; + class PxRigidActor; + class PxRigidDynamic; + class PxRigidStatic; + class PxGeometry; + class PxShape; + + +/** \brief simple method to create a PxRigidDynamic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] geometry the geometry of the new object's shape, which must be a sphere, capsule, box or convex + \param[in] material the material for the new object's shape + \param[in] density the density of the new object. Must be greater than zero. + \param[in] shapeOffset an optional offset for the new shape, defaults to identity + + \return a new dynamic actor with the PxRigidBodyFlag, or NULL if it could + not be constructed + + @see PxRigidDynamic PxShapeFlag +*/ + +PxRigidDynamic* PxCreateDynamic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + PxReal density, + const PxTransform& shapeOffset = PxTransform(PxIdentity)); + + +/** \brief simple method to create a PxRigidDynamic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the transform of the new object + \param[in] shape the shape of the new object + \param[in] density the density of the new object. Must be greater than zero. + + \return a new dynamic actor with the PxRigidBodyFlag, or NULL if it could + not be constructed + + @see PxRigidDynamic PxShapeFlag +*/ + +PxRigidDynamic* PxCreateDynamic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape, + PxReal density); + + +/** \brief simple method to create a kinematic PxRigidDynamic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] geometry the geometry of the new object's shape + \param[in] material the material for the new object's shape + \param[in] density the density of the new object. Must be greater than zero if the object is to participate in simulation. + \param[in] shapeOffset an optional offset for the new shape, defaults to identity + + \note unlike PxCreateDynamic, the geometry is not restricted to box, capsule, sphere or convex. However, + kinematics of other geometry types may not participate in simulation collision and may be used only for + triggers or scene queries of moving objects under animation control. In this case the density parameter + will be ignored and the created shape will be set up as a scene query only shape (see #PxShapeFlag::eSCENE_QUERY_SHAPE) + + \return a new dynamic actor with the PxRigidBodyFlag::eKINEMATIC set, or NULL if it could + not be constructed + + @see PxRigidDynamic PxShapeFlag +*/ + +PxRigidDynamic* PxCreateKinematic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + PxReal density, + const PxTransform& shapeOffset = PxTransform(PxIdentity)); + + +/** \brief simple method to create a kinematic PxRigidDynamic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] density the density of the new object. Must be greater than zero if the object is to participate in simulation. + \param[in] shape the shape of the new object + + \note unlike PxCreateDynamic, the geometry is not restricted to box, capsule, sphere or convex. However, + kinematics of other geometry types may not participate in simulation collision and may be used only for + triggers or scene queries of moving objects under animation control. In this case the density parameter + will be ignored and the created shape will be set up as a scene query only shape (see #PxShapeFlag::eSCENE_QUERY_SHAPE) + + \return a new dynamic actor with the PxRigidBodyFlag::eKINEMATIC set, or NULL if it could + not be constructed + + @see PxRigidDynamic PxShapeFlag +*/ + +PxRigidDynamic* PxCreateKinematic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape, + PxReal density); + + +/** \brief simple method to create a PxRigidStatic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] geometry the geometry of the new object's shape + \param[in] material the material for the new object's shape + \param[in] shapeOffset an optional offset for the new shape, defaults to identity + + \return a new static actor, or NULL if it could not be constructed + + @see PxRigidStatic +*/ + +PxRigidStatic* PxCreateStatic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + const PxTransform& shapeOffset = PxTransform(PxIdentity)); + + +/** \brief simple method to create a PxRigidStatic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] shape the new object's shape + + \return a new static actor, or NULL if it could not be constructed + + @see PxRigidStatic +*/ + +PxRigidStatic* PxCreateStatic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape); + + +/** \brief simple method to create a PxRigidStatic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] shape the new object's shape + + \return a new static actor, or NULL if it could not be constructed + + @see PxRigidStatic +*/ + +PxRigidStatic* PxCreateStatic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape); + + +/** +\brief create a shape by copying attributes from another shape + +The function clones a PxShape. The following properties are copied: +- geometry +- flags +- materials +- actor-local pose +- contact offset +- rest offset +- simulation filter data +- query filter data + +The following are not copied and retain their default values: +- name +- user data + +\param[in] physicsSDK - the physics SDK used to allocate the shape +\param[in] shape the shape from which to take the attributes. +\param[in] isExclusive whether the new shape should be an exclusive or shared shape. + +\return the newly-created rigid static + +*/ + +PxShape* PxCloneShape(PxPhysics& physicsSDK, + const PxShape& shape, + bool isExclusive); + + + +/** +\brief create a static body by copying attributes from another rigid actor + +The function clones a PxRigidDynamic or PxRigidStatic as a PxRigidStatic. A uniform scale is applied. The following properties are copied: +- shapes +- actor flags +- owner client and client behavior bits + +The following are not copied and retain their default values: +- name +- joints or observers +- aggregate or scene membership +- user data + +\note Transforms are not copied with bit-exact accuracy. + +\param[in] physicsSDK - the physics SDK used to allocate the rigid static +\param[in] actor the rigid actor from which to take the attributes. +\param[in] transform the transform of the new static. + +\return the newly-created rigid static + +*/ + +PxRigidStatic* PxCloneStatic(PxPhysics& physicsSDK, + const PxTransform& transform, + const PxRigidActor& actor); + + +/** +\brief create a dynamic body by copying attributes from an existing body + +The following properties are copied: +- shapes +- actor flags and rigidDynamic flags +- mass, moment of inertia, and center of mass frame +- linear and angular velocity +- linear and angular damping +- maximum angular velocity +- position and velocity solver iterations +- maximum depenetration velocity +- sleep threshold +- contact report threshold +- dominance group +- owner client and client behavior bits +- name pointer + +The following are not copied and retain their default values: +- name +- joints or observers +- aggregate or scene membership +- sleep timer +- user data + +\note Transforms are not copied with bit-exact accuracy. + +\param[in] physicsSDK PxPhysics - the physics SDK used to allocate the rigid static +\param[in] body the rigid dynamic to clone. +\param[in] transform the transform of the new dynamic + +\return the newly-created rigid static + +*/ + +PxRigidDynamic* PxCloneDynamic(PxPhysics& physicsSDK, + const PxTransform& transform, + const PxRigidDynamic& body); + + +/** \brief create a plane actor. The plane equation is n.x + d = 0 + + \param[in] sdk the PxPhysics object + \param[in] plane a plane of the form n.x + d = 0 + \param[in] material the material for the new object's shape + + \return a new static actor, or NULL if it could not be constructed + + @see PxRigidStatic +*/ + +PxRigidStatic* PxCreatePlane(PxPhysics& sdk, + const PxPlane& plane, + PxMaterial& material); + + +/** +\brief scale a rigid actor by a uniform scale + +The geometry and relative positions of the actor are multiplied by the given scale value. If the actor is a rigid body or an +articulation link and the scaleMassProps value is true, the mass properties are scaled assuming the density is constant: the +center of mass is linearly scaled, the mass is multiplied by the cube of the scale, and the inertia tensor by the fifth power of the scale. + +\param[in] actor a rigid actor +\param[in] scale the scale by which to multiply the actor. Must be >0. +\param[in] scaleMassProps whether to scale the mass properties +*/ + +void PxScaleRigidActor(PxRigidActor& actor, PxReal scale, bool scaleMassProps = true); + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxSmoothNormals.h b/src/PhysX/physx/include/extensions/PxSmoothNormals.h new file mode 100644 index 000000000..0aa373b44 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxSmoothNormals.h @@ -0,0 +1,61 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_SMOOTH_NORMALS_H +#define PX_PHYSICS_EXTENSIONS_SMOOTH_NORMALS_H +/** \addtogroup extensions + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +/** +\brief Builds smooth vertex normals over a mesh. + +- "smooth" because smoothing groups are not supported here +- takes angles into account for correct cube normals computation + +To use 32bit indices pass a pointer in dFaces and set wFaces to zero. Alternatively pass a pointer to +wFaces and set dFaces to zero. + +\param[in] nbTris Number of triangles +\param[in] nbVerts Number of vertices +\param[in] verts Array of vertices +\param[in] dFaces Array of dword triangle indices, or null +\param[in] wFaces Array of word triangle indices, or null +\param[out] normals Array of computed normals (assumes nbVerts vectors) +\param[in] flip Flips the normals or not +\return True on success. +*/ +PX_C_EXPORT bool PX_CALL_CONV PxBuildSmoothNormals(physx::PxU32 nbTris, physx::PxU32 nbVerts, const physx::PxVec3* verts, + const physx::PxU32* dFaces, const physx::PxU16* wFaces, physx::PxVec3* normals, bool flip); + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxSphericalJoint.h b/src/PhysX/physx/include/extensions/PxSphericalJoint.h new file mode 100644 index 000000000..80e513193 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxSphericalJoint.h @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_SPHERICALJOINT_H +#define PX_SPHERICALJOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" +#include "extensions/PxJointLimit.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxSphericalJoint; + +/** +\brief Create a spherical joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxSphericalJoint +*/ +PxSphericalJoint* PxSphericalJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + + +/** +\brief Flags specific to the spherical joint. + +@see PxSphericalJoint +*/ +struct PxSphericalJointFlag +{ + enum Enum + { + eLIMIT_ENABLED = 1<<1 //!< the cone limit for the spherical joint is enabled + }; +}; +typedef PxFlags PxSphericalJointFlags; +PX_FLAGS_OPERATORS(PxSphericalJointFlag::Enum, PxU16) + +/** +\brief A joint which behaves in a similar way to a ball and socket. + + A spherical joint removes all linear degrees of freedom from two objects. + + The position of the joint on each actor is specified by the origin of the body's joint frame. + + A spherical joint may have a cone limit, to restrict the motion to within a certain range. In + addition, the bodies may be projected together if the distance between them exceeds a given threshold. + + Projection, drive and limits are activated by setting the appropriate flags on the joint. + + @see PxRevoluteJointCreate() PxJoint +*/ +class PxSphericalJoint : public PxJoint +{ +public: + + /** + \brief Set the limit cone. + + If enabled, the limit cone will constrain the angular movement of the joint to lie + within an elliptical cone. + + \return the limit cone + + @see PxJointLimitCone setLimit() + */ + virtual PxJointLimitCone getLimitCone() const = 0; + + /** + \brief Get the limit cone. + + \param[in] limit the limit cone + + @see PxJointLimitCone getLimit() + */ + virtual void setLimitCone(const PxJointLimitCone& limit) = 0; + + /** + \brief get the swing angle of the joint from the Y axis + */ + virtual PxReal getSwingYAngle() const = 0; + + /** + \brief get the swing angle of the joint from the Z axis + */ + virtual PxReal getSwingZAngle() const = 0; + + /** + \brief Set the flags specific to the Spherical Joint. + + Default PxSphericalJointFlags(0) + + \param[in] flags The joint flags. + + @see PxSphericalJointFlag setFlag() getFlags() + */ + virtual void setSphericalJointFlags(PxSphericalJointFlags flags) = 0; + + /** + \brief Set a single flag specific to a Spherical Joint to true or false. + + \param[in] flag The flag to set or clear. + \param[in] value the value to which to set the flag + + @see PxSphericalJointFlag, getFlags() setFlags() + */ + virtual void setSphericalJointFlag(PxSphericalJointFlag::Enum flag, bool value) = 0; + + /** + \brief Get the flags specific to the Spherical Joint. + + \return the joint flags + + @see PxSphericalJoint::flags, PxSphericalJointFlag setFlag() setFlags() + */ + virtual PxSphericalJointFlags getSphericalJointFlags() const = 0; + + /** + \brief Set the linear tolerance threshold for projection. Projection is enabled if PxConstraintFlag::ePROJECTION + is set for the joint. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0, PX_MAX_F32)
+ Default: 1e10f + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() PxJoint::setConstraintFlags() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionLinearTolerance(PxReal tolerance) = 0; + + /** + \brief Get the linear tolerance threshold for projection. + + \return the linear tolerance threshold + + @see setProjectionLinearTolerance() + */ + virtual PxReal getProjectionLinearTolerance() const = 0; + + /** + \brief Returns string name of PxSphericalJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxSphericalJoint"; } + +protected: + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxSphericalJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxSphericalJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxSphericalJoint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxStringTableExt.h b/src/PhysX/physx/include/extensions/PxStringTableExt.h new file mode 100644 index 000000000..3585983ea --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxStringTableExt.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_STRING_TABLE_EXT_H +#define PX_STRING_TABLE_EXT_H +#include "foundation/Px.h" +#include "common/PxStringTable.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief a factory class for creating PxStringTable with a specific allocator. + +@see PxStringTable +*/ + +class PxStringTableExt +{ +public: + static PxStringTable& createStringTable( physx::PxAllocatorCallback& inAllocator ); +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif diff --git a/src/PhysX/physx/include/extensions/PxTriangleMeshExt.h b/src/PhysX/physx/include/extensions/PxTriangleMeshExt.h new file mode 100644 index 000000000..43dc29e2d --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxTriangleMeshExt.h @@ -0,0 +1,188 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_TRIANGLE_MESH_H +#define PX_PHYSICS_EXTENSIONS_TRIANGLE_MESH_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxGeometry; +class PxTriangleMeshGeometry; +class PxHeightFieldGeometry; + + /** + \brief Utility class to find mesh triangles touched by a specified geometry object. + + This class is a helper calling PxMeshQuery::findOverlapTriangleMesh or PxMeshQuery::findOverlapHeightField under the hood, + while taking care of necessary memory management issues. + + PxMeshQuery::findOverlapTriangleMesh and PxMeshQuery::findOverlapHeightField are the "raw" functions operating on user-provided fixed-size + buffers. These functions abort with an error code in case of buffer overflow. PxMeshOverlapUtil is a convenient helper function checking + this error code, and resizing buffers appropriately, until the desired call succeeds. + + Returned triangle indices are stored within the class, and can be used with PxMeshQuery::getTriangle() to retrieve the triangle properties. + */ + class PxMeshOverlapUtil + { + public: + PxMeshOverlapUtil(); + ~PxMeshOverlapUtil(); + /** + \brief Find the mesh triangles which touch the specified geometry object. + + \param[in] geom The geometry object to test for mesh triangle overlaps. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry and #PxBoxGeometry + \param[in] geomPose Pose of the geometry object + \param[in] meshGeom The triangle mesh geometry to check overlap against + \param[in] meshPose Pose of the triangle mesh + \return Number of overlaps found. Triangle indices can then be accessed through the #getResults() function. + + @see PxGeometry PxTransform PxTriangleMeshGeometry PxMeshQuery::findOverlapTriangleMesh + */ + PxU32 findOverlap(const PxGeometry& geom, const PxTransform& geomPose, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose); + + /** + \brief Find the height field triangles which touch the specified geometry object. + + \param[in] geom The geometry object to test for height field overlaps. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry and #PxBoxGeometry. The sphere and capsule queries are currently conservative estimates. + \param[in] geomPose Pose of the geometry object + \param[in] hfGeom The height field geometry to check overlap against + \param[in] hfPose Pose of the height field + \return Number of overlaps found. Triangle indices can then be accessed through the #getResults() function. + + @see PxGeometry PxTransform PxHeightFieldGeometry PxMeshQuery::findOverlapHeightField + */ + PxU32 findOverlap(const PxGeometry& geom, const PxTransform& geomPose, const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose); + + /** + \brief Retrieves array of triangle indices after a findOverlap call. + \return Indices of touched triangles + */ + PX_FORCE_INLINE const PxU32* getResults() const { return mResultsMemory; } + + /** + \brief Retrieves number of triangle indices after a findOverlap call. + \return Number of touched triangles + */ + PX_FORCE_INLINE PxU32 getNbResults() const { return mNbResults; } + + private: + PxU32* mResultsMemory; + PxU32 mResults[256]; + PxU32 mNbResults; + PxU32 mMaxNbResults; + }; + + /** + \brief Computes an approximate minimum translational distance (MTD) between a geometry object and a mesh. + + This iterative function computes an approximate vector that can be used to depenetrate a geom object + from a triangle mesh. Returned depenetration vector should be applied to 'geom', to get out of the mesh. + + The function works best when the amount of overlap between the geom object and the mesh is small. If the + geom object's center goes inside the mesh, backface culling usually kicks in, no overlap is detected, + and the function does not compute an MTD vector. + + The function early exits if no overlap is detected after a depenetration attempt. This means that if + maxIter = N, the code will attempt at most N iterations but it might exit earlier if depenetration has + been successful. Usually N = 4 gives good results. + + \param[out] direction Computed MTD unit direction + \param[out] depth Penetration depth. Always positive or zero. + \param[in] geom The geometry object + \param[in] geomPose Pose for the geometry object + \param[in] meshGeom The mesh geometry + \param[in] meshPose Pose for the mesh + \param[in] maxIter Max number of iterations before returning. + \param[out] usedIter Number of depenetrations attempts performed during the call. Will not be returned if the pointer is NULL. + + \return True if the MTD has successfully been computed, i.e. if objects do overlap. + + @see PxGeometry PxTransform PxTriangleMeshGeometry + */ + bool PxComputeTriangleMeshPenetration(PxVec3& direction, + PxReal& depth, + const PxGeometry& geom, + const PxTransform& geomPose, + const PxTriangleMeshGeometry& meshGeom, + const PxTransform& meshPose, + PxU32 maxIter, + PxU32* usedIter = NULL); + + /** + \brief Computes an approximate minimum translational distance (MTD) between a geometry object and a heightfield. + + This iterative function computes an approximate vector that can be used to depenetrate a geom object + from a heightfield. Returned depenetration vector should be applied to 'geom', to get out of the heightfield. + + The function works best when the amount of overlap between the geom object and the mesh is small. If the + geom object's center goes inside the heightfield, backface culling usually kicks in, no overlap is detected, + and the function does not compute an MTD vector. + + The function early exits if no overlap is detected after a depenetration attempt. This means that if + maxIter = N, the code will attempt at most N iterations but it might exit earlier if depenetration has + been successful. Usually N = 4 gives good results. + + \param[out] direction Computed MTD unit direction + \param[out] depth Penetration depth. Always positive or zero. + \param[in] geom The geometry object + \param[in] geomPose Pose for the geometry object + \param[in] heightFieldGeom The heightfield geometry + \param[in] heightFieldPose Pose for the heightfield + \param[in] maxIter Max number of iterations before returning. + \param[out] usedIter Number of depenetrations attempts performed during the call. Will not be returned if the pointer is NULL. + + \return True if the MTD has successfully been computed, i.e. if objects do overlap. + + @see PxGeometry PxTransform PxHeightFieldGeometry + */ + bool PxComputeHeightFieldPenetration(PxVec3& direction, + PxReal& depth, + const PxGeometry& geom, + const PxTransform& geomPose, + const PxHeightFieldGeometry& heightFieldGeom, + const PxTransform& heightFieldPose, + PxU32 maxIter, + PxU32* usedIter = NULL); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/filebuf/PxFileBuf.h b/src/PhysX/physx/include/filebuf/PxFileBuf.h new file mode 100644 index 000000000..b54f09b2a --- /dev/null +++ b/src/PhysX/physx/include/filebuf/PxFileBuf.h @@ -0,0 +1,339 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PSFILEBUFFER_PXFILEBUF_H +#define PSFILEBUFFER_PXFILEBUF_H + +#include "foundation/PxSimpleTypes.h" + +/** \addtogroup foundation + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ + +namespace general_PxIOStream2 +{ +#endif + +PX_PUSH_PACK_DEFAULT + +/** +\brief Callback class for data serialization. + +The user needs to supply an PxFileBuf implementation to a number of methods to allow the SDK to read or write +chunks of binary data. This allows flexibility for the source/destination of the data. For example the PxFileBuf +could store data in a file, memory buffer or custom file format. + +\note It is the users responsibility to ensure that the data is written to the appropriate offset. + +*/ +class PxFileBuf +{ +public: + + enum EndianMode + { + ENDIAN_NONE = 0, // do no conversion for endian mode + ENDIAN_BIG = 1, // always read/write data as natively big endian (Power PC, etc.) + ENDIAN_LITTLE = 2 // always read/write data as natively little endian (Intel, etc.) Default Behavior! + }; + + PxFileBuf(EndianMode mode=ENDIAN_LITTLE) + { + setEndianMode(mode); + } + + virtual ~PxFileBuf(void) + { + + } + + /** + \brief Declares a constant to seek to the end of the stream. + * + * Does not support streams longer than 32 bits + */ + static const uint32_t STREAM_SEEK_END=0xFFFFFFFF; + + enum OpenMode + { + OPEN_FILE_NOT_FOUND, + OPEN_READ_ONLY, // open file buffer stream for read only access + OPEN_WRITE_ONLY, // open file buffer stream for write only access + OPEN_READ_WRITE_NEW, // open a new file for both read/write access + OPEN_READ_WRITE_EXISTING // open an existing file for both read/write access + }; + + virtual OpenMode getOpenMode(void) const = 0; + + bool isOpen(void) const + { + return getOpenMode()!=OPEN_FILE_NOT_FOUND; + } + + enum SeekType + { + SEEKABLE_NO = 0, + SEEKABLE_READ = 0x1, + SEEKABLE_WRITE = 0x2, + SEEKABLE_READWRITE = 0x3 + }; + + virtual SeekType isSeekable(void) const = 0; + + void setEndianMode(EndianMode e) + { + mEndianMode = e; + if ( (e==ENDIAN_BIG && !isBigEndian() ) || + (e==ENDIAN_LITTLE && isBigEndian() ) ) + { + mEndianSwap = true; + } + else + { + mEndianSwap = false; + } + } + + EndianMode getEndianMode(void) const + { + return mEndianMode; + } + + virtual uint32_t getFileLength(void) const = 0; + + /** + \brief Seeks the stream to a particular location for reading + * + * If the location passed exceeds the length of the stream, then it will seek to the end. + * Returns the location it ended up at (useful if you seek to the end) to get the file position + */ + virtual uint32_t seekRead(uint32_t loc) = 0; + + /** + \brief Seeks the stream to a particular location for writing + * + * If the location passed exceeds the length of the stream, then it will seek to the end. + * Returns the location it ended up at (useful if you seek to the end) to get the file position + */ + virtual uint32_t seekWrite(uint32_t loc) = 0; + + /** + \brief Reads from the stream into a buffer. + + \param[out] mem The buffer to read the stream into. + \param[in] len The number of bytes to stream into the buffer + + \return Returns the actual number of bytes read. If not equal to the length requested, then reached end of stream. + */ + virtual uint32_t read(void *mem,uint32_t len) = 0; + + + /** + \brief Reads from the stream into a buffer but does not advance the read location. + + \param[out] mem The buffer to read the stream into. + \param[in] len The number of bytes to stream into the buffer + + \return Returns the actual number of bytes read. If not equal to the length requested, then reached end of stream. + */ + virtual uint32_t peek(void *mem,uint32_t len) = 0; + + /** + \brief Writes a buffer of memory to the stream + + \param[in] mem The address of a buffer of memory to send to the stream. + \param[in] len The number of bytes to send to the stream. + + \return Returns the actual number of bytes sent to the stream. If not equal to the length specific, then the stream is full or unable to write for some reason. + */ + virtual uint32_t write(const void *mem,uint32_t len) = 0; + + /** + \brief Reports the current stream location read aqccess. + + \return Returns the current stream read location. + */ + virtual uint32_t tellRead(void) const = 0; + + /** + \brief Reports the current stream location for write access. + + \return Returns the current stream write location. + */ + virtual uint32_t tellWrite(void) const = 0; + + /** + \brief Causes any temporarily cached data to be flushed to the stream. + */ + virtual void flush(void) = 0; + + /** + \brief Close the stream. + */ + virtual void close(void) {} + + void release(void) + { + delete this; + } + + static PX_INLINE bool isBigEndian() + { + int32_t i = 1; + return *(reinterpret_cast(&i))==0; + } + + PX_INLINE void swap2Bytes(void* _data) const + { + char *data = static_cast(_data); + char one_byte; + one_byte = data[0]; data[0] = data[1]; data[1] = one_byte; + } + + PX_INLINE void swap4Bytes(void* _data) const + { + char *data = static_cast(_data); + char one_byte; + one_byte = data[0]; data[0] = data[3]; data[3] = one_byte; + one_byte = data[1]; data[1] = data[2]; data[2] = one_byte; + } + + PX_INLINE void swap8Bytes(void *_data) const + { + char *data = static_cast(_data); + char one_byte; + one_byte = data[0]; data[0] = data[7]; data[7] = one_byte; + one_byte = data[1]; data[1] = data[6]; data[6] = one_byte; + one_byte = data[2]; data[2] = data[5]; data[5] = one_byte; + one_byte = data[3]; data[3] = data[4]; data[4] = one_byte; + } + + + PX_INLINE void storeDword(uint32_t v) + { + if ( mEndianSwap ) + swap4Bytes(&v); + + write(&v,sizeof(v)); + } + + PX_INLINE void storeFloat(float v) + { + if ( mEndianSwap ) + swap4Bytes(&v); + write(&v,sizeof(v)); + } + + PX_INLINE void storeDouble(double v) + { + if ( mEndianSwap ) + swap8Bytes(&v); + write(&v,sizeof(v)); + } + + PX_INLINE void storeByte(uint8_t b) + { + write(&b,sizeof(b)); + } + + PX_INLINE void storeWord(uint16_t w) + { + if ( mEndianSwap ) + swap2Bytes(&w); + write(&w,sizeof(w)); + } + + uint8_t readByte(void) + { + uint8_t v=0; + read(&v,sizeof(v)); + return v; + } + + uint16_t readWord(void) + { + uint16_t v=0; + read(&v,sizeof(v)); + if ( mEndianSwap ) + swap2Bytes(&v); + return v; + } + + uint32_t readDword(void) + { + uint32_t v=0; + read(&v,sizeof(v)); + if ( mEndianSwap ) + swap4Bytes(&v); + return v; + } + + float readFloat(void) + { + float v=0; + read(&v,sizeof(v)); + if ( mEndianSwap ) + swap4Bytes(&v); + return v; + } + + double readDouble(void) + { + double v=0; + read(&v,sizeof(v)); + if ( mEndianSwap ) + swap8Bytes(&v); + return v; + } + +private: + bool mEndianSwap; // whether or not the endian should be swapped on the current platform + EndianMode mEndianMode; // the current endian mode behavior for the stream +}; + +PX_POP_PACK + +#if !PX_DOXYGEN +} // end of namespace + +using namespace general_PxIOStream2; + +namespace general_PxIOStream = general_PxIOStream2; + +} // end of namespace +#endif + +/** @} */ + +#endif // PSFILEBUFFER_PXFILEBUF_H diff --git a/src/PhysX/physx/include/geometry/PxBVHStructure.h b/src/PhysX/physx/include/geometry/PxBVHStructure.h new file mode 100644 index 000000000..86050b13e --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxBVHStructure.h @@ -0,0 +1,138 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_BVH_STRUCTURE +#define PX_PHYSICS_BVH_STRUCTURE +/** \addtogroup geomutils +@{ +*/ + +#include "common/PxBase.h" +#include "foundation/PxTransform.h" +#include "foundation/PxBounds3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Class representing the bounding volume hierarchy structure. + +PxBVHStructure can be provided to PxScene::addActor. In this case the scene query +pruning structure inside PhysX SDK will store/update one bound per actor. +The scene queries against such an actor will query actor bounds and then +make a local space query against the provided BVH structure, which is in +actor's local space. + +@see PxScene::addActor +*/ +class PxBVHStructure: public PxBase +{ +public: + + /** + \brief Raycast test against a BVH structure. + + \param[in] origin The origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] maxDist Maximum ray length, has to be in the [0, inf) range + \param[in] maxHits Max number of returned hits = size of 'rayHits' buffer + \param[out] rayHits Raycast hits information, bounds indices + \return Number of hits + */ + virtual PxU32 raycast(const PxVec3& origin, + const PxVec3& unitDir, + PxReal maxDist, + PxU32 maxHits, + PxU32* PX_RESTRICT rayHits) const = 0; + + /** + \brief Sweep test against a BVH structure. + + \param[in] aabb The axis aligned bounding box to sweep + \param[in] unitDir Normalized direction of the sweep. + \param[in] maxDist Maximum sweep length, has to be in the [0, inf) range + \param[in] maxHits Max number of returned hits = size of 'sweepHits' buffer + \param[out] sweepHits Sweep hits information, bounds indices + \return Number of hits + */ + virtual PxU32 sweep(const PxBounds3& aabb, + const PxVec3& unitDir, + PxReal maxDist, + PxU32 maxHits, + PxU32* PX_RESTRICT sweepHits) const = 0; + + /** + \brief AABB overlap test against a BVH structure. + + \param[in] aabb The axis aligned bounding box + \param[in] maxHits Max number of returned hits = size of 'overlapHits' buffer + \param[out] overlapHits Overlap hits information, bounds indices + \return Number of hits + */ + virtual PxU32 overlap(const PxBounds3& aabb, + PxU32 maxHits, + PxU32* PX_RESTRICT overlapHits) const = 0; + + /** + \brief Retrieve the bounds in the BVH. + + @see PxBounds3 + */ + virtual const PxBounds3* getBounds() const = 0; + + /** + \brief Returns the number of bounds in the BVH. + + You can use #getBounds() to retrieve the bounds. + + \return Number of bounds in the BVH. + + */ + virtual PxU32 getNbBounds() const = 0; + + virtual const char* getConcreteTypeName() const { return "PxBVHStructure"; } +protected: + PX_INLINE PxBVHStructure(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxBVHStructure(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxBVHStructure() {} + + virtual bool isKindOf(const char* name) const { return !::strcmp("PxBVHStructure", name) || PxBase::isKindOf(name); } + +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxBoxGeometry.h b/src/PhysX/physx/include/geometry/PxBoxGeometry.h new file mode 100644 index 000000000..a0fa7d0c6 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxBoxGeometry.h @@ -0,0 +1,109 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_BOX_GEOMETRY +#define PX_PHYSICS_NX_BOX_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxGeometry.h" +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Class representing the geometry of a box. + +The geometry of a box can be fully specified by its half extents. This is the half of its width, height, and depth. +\note The scaling of the box is expected to be baked into these values, there is no additional scaling parameter. +*/ +class PxBoxGeometry : public PxGeometry +{ +public: + /** + \brief Default constructor, initializes to a box with zero dimensions. + */ + PX_INLINE PxBoxGeometry() : PxGeometry(PxGeometryType::eBOX), halfExtents(0,0,0) {} + + /** + \brief Constructor to initialize half extents from scalar parameters. + \param hx Initial half extents' x component. + \param hy Initial half extents' y component. + \param hz Initial half extents' z component. + */ + PX_INLINE PxBoxGeometry(PxReal hx, PxReal hy, PxReal hz) : PxGeometry(PxGeometryType::eBOX), halfExtents(hx, hy, hz) {} + + /** + \brief Constructor to initialize half extents from vector parameter. + \param halfExtents_ Initial half extents. + */ + PX_INLINE PxBoxGeometry(PxVec3 halfExtents_) : PxGeometry(PxGeometryType::eBOX), halfExtents(halfExtents_) {} + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid + + \note A valid box has a positive extent in each direction (halfExtents.x > 0, halfExtents.y > 0, halfExtents.z > 0). + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a box that has zero extent in any direction. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + /** + \brief Half of the width, height, and depth of the box. + */ + PxVec3 halfExtents; +}; + + +PX_INLINE bool PxBoxGeometry::isValid() const +{ + if (mType != PxGeometryType::eBOX) + return false; + if (!halfExtents.isFinite()) + return false; + if (halfExtents.x <= 0.0f || halfExtents.y <= 0.0f || halfExtents.z <= 0.0f) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxCapsuleGeometry.h b/src/PhysX/physx/include/geometry/PxCapsuleGeometry.h new file mode 100644 index 000000000..4600f4da4 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxCapsuleGeometry.h @@ -0,0 +1,121 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_CAPSULE_GEOMETRY +#define PX_PHYSICS_NX_CAPSULE_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Class representing the geometry of a capsule. + +Capsules are shaped as the union of a cylinder of length 2 * halfHeight and with the +given radius centered at the origin and extending along the x axis, and two hemispherical ends. +\note The scaling of the capsule is expected to be baked into these values, there is no additional scaling parameter. + +The function PxTransformFromSegment is a helper for generating an appropriate transform for the capsule from the capsule's interior line segment. + +@see PxTransformFromSegment +*/ +class PxCapsuleGeometry : public PxGeometry +{ +public: + /** + \brief Default constructor, initializes to a capsule with zero height and radius. + */ + PX_INLINE PxCapsuleGeometry() : PxGeometry(PxGeometryType::eCAPSULE), radius(0), halfHeight(0) {} + + /** + \brief Constructor, initializes to a capsule with passed radius and half height. + */ + PX_INLINE PxCapsuleGeometry(PxReal radius_, PxReal halfHeight_) : PxGeometry(PxGeometryType::eCAPSULE), radius(radius_), halfHeight(halfHeight_) {} + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid. + + \note A valid capsule has radius > 0, halfHeight > 0. + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a capsule that has zero radius or height. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + /** + \brief The radius of the capsule. + */ + PxReal radius; + + /** + \brief half of the capsule's height, measured between the centers of the hemispherical ends. + */ + PxReal halfHeight; +}; + + +PX_INLINE bool PxCapsuleGeometry::isValid() const +{ + if (mType != PxGeometryType::eCAPSULE) + return false; + if (!PxIsFinite(radius) || !PxIsFinite(halfHeight)) + return false; + if (radius <= 0.0f || halfHeight <= 0.0f) + return false; + + return true; +} + + +/** \brief creates a transform from the endpoints of a segment, suitable for an actor transform for a PxCapsuleGeometry + +\param[in] p0 one end of major axis of the capsule +\param[in] p1 the other end of the axis of the capsule +\param[out] halfHeight the halfHeight of the capsule. This parameter is optional. +\return A PxTransform which will transform the vector (1,0,0) to the capsule axis shrunk by the halfHeight +*/ + +PX_FOUNDATION_API PxTransform PxTransformFromSegment(const PxVec3& p0, const PxVec3& p1, PxReal* halfHeight = NULL); + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxConvexMesh.h b/src/PhysX/physx/include/geometry/PxConvexMesh.h new file mode 100644 index 000000000..b331a4223 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxConvexMesh.h @@ -0,0 +1,197 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_NX_CONVEXMESH +#define PX_PHYSICS_GEOMUTILS_NX_CONVEXMESH +/** \addtogroup geomutils + @{ +*/ + +#include "foundation/Px.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Polygon data + +Plane format: (mPlane[0],mPlane[1],mPlane[2]).dot(x) + mPlane[3] = 0 +With the normal outward-facing from the hull. +*/ +struct PxHullPolygon +{ + PxReal mPlane[4]; //!< Plane equation for this polygon + PxU16 mNbVerts; //!< Number of vertices/edges in the polygon + PxU16 mIndexBase; //!< Offset in index buffer +}; + +/** +\brief A convex mesh. + +Internally represented as a list of convex polygons. The number +of polygons is limited to 256. + +To avoid duplicating data when you have several instances of a particular +mesh positioned differently, you do not use this class to represent a +convex object directly. Instead, you create an instance of this mesh via +the PxConvexMeshGeometry and PxShape classes. + +

Creation

+ +To create an instance of this class call PxPhysics::createConvexMesh(), +and PxConvexMesh::release() to delete it. This is only possible +once you have released all of its #PxShape instances. + +

Visualizations:

+\li #PxVisualizationParameter::eCOLLISION_AABBS +\li #PxVisualizationParameter::eCOLLISION_SHAPES +\li #PxVisualizationParameter::eCOLLISION_AXES +\li #PxVisualizationParameter::eCOLLISION_FNORMALS +\li #PxVisualizationParameter::eCOLLISION_EDGES + +@see PxConvexMeshDesc PxPhysics.createConvexMesh() +*/ +class PxConvexMesh : public PxBase +{ +public: + + /** + \brief Returns the number of vertices. + \return Number of vertices. + @see getVertices() + */ + PX_PHYSX_COMMON_API virtual PxU32 getNbVertices() const = 0; + + /** + \brief Returns the vertices. + \return Array of vertices. + @see getNbVertices() + */ + PX_PHYSX_COMMON_API virtual const PxVec3* getVertices() const = 0; + + /** + \brief Returns the index buffer. + \return Index buffer. + @see getNbPolygons() getPolygonData() + */ + PX_PHYSX_COMMON_API virtual const PxU8* getIndexBuffer() const = 0; + + /** + \brief Returns the number of polygons. + \return Number of polygons. + @see getIndexBuffer() getPolygonData() + */ + PX_PHYSX_COMMON_API virtual PxU32 getNbPolygons() const = 0; + + /** + \brief Returns the polygon data. + \param[in] index Polygon index in [0 ; getNbPolygons()[. + \param[out] data Polygon data. + \return True if success. + @see getIndexBuffer() getNbPolygons() + */ + PX_PHYSX_COMMON_API virtual bool getPolygonData(PxU32 index, PxHullPolygon& data) const = 0; + + /** + \brief Decrements the reference count of a convex mesh and releases it if the new reference count is zero. + + @see PxPhysics.createConvexMesh() PxConvexMeshGeometry PxShape + */ + PX_PHYSX_COMMON_API virtual void release() = 0; + + /** + \brief Returns the reference count of a convex mesh. + + At creation, the reference count of the convex mesh is 1. Every shape referencing this convex mesh increments the + count by 1. When the reference count reaches 0, and only then, the convex mesh gets destroyed automatically. + + \return the current reference count. + */ + PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const = 0; + + /** + \brief Acquires a counted reference to a convex mesh. + + This method increases the reference count of the convex mesh by 1. Decrement the reference count by calling release() + */ + PX_PHYSX_COMMON_API virtual void acquireReference() = 0; + + /** + \brief Returns the mass properties of the mesh assuming unit density. + + The following relationship holds between mass and volume: + + mass = volume * density + + The mass of a unit density mesh is equal to its volume, so this function returns the volume of the mesh. + + Similarly, to obtain the localInertia of an identically shaped object with a uniform density of d, simply multiply the + localInertia of the unit density mesh by d. + + \param[out] mass The mass of the mesh assuming unit density. + \param[out] localInertia The inertia tensor in mesh local space assuming unit density. + \param[out] localCenterOfMass Position of center of mass (or centroid) in mesh local space. + */ + PX_PHYSX_COMMON_API virtual void getMassInformation(PxReal& mass, PxMat33& localInertia, PxVec3& localCenterOfMass) const = 0; + + /** + \brief Returns the local-space (vertex space) AABB from the convex mesh. + + \return local-space bounds + */ + PX_PHYSX_COMMON_API virtual PxBounds3 getLocalBounds() const = 0; + + PX_PHYSX_COMMON_API virtual const char* getConcreteTypeName() const { return "PxConvexMesh"; } + + /** + \brief This method decides whether a convex mesh is gpu compatible. If the total number of vertices are more than 64 or any number of vertices in a polygon is more than 32, or + convex hull data was not cooked with GPU data enabled during cooking or was loaded from a serialized collection, the convex hull is incompatible with GPU collision detection. Otherwise + it is compatible. + + \return True if the convex hull is gpu compatible + */ + PX_PHYSX_COMMON_API virtual bool isGpuCompatible() const = 0; + +protected: + PX_INLINE PxConvexMesh(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxConvexMesh(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + PX_PHYSX_COMMON_API virtual ~PxConvexMesh() {} + PX_PHYSX_COMMON_API virtual bool isKindOf(const char* name) const { return !::strcmp("PxConvexMesh", name) || PxBase::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h b/src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h new file mode 100644 index 000000000..dad042c8c --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h @@ -0,0 +1,151 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_CONVEXMESH_GEOMETRY +#define PX_PHYSICS_NX_CONVEXMESH_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxGeometry.h" +#include "geometry/PxMeshScale.h" +#include "common/PxCoreUtilityTypes.h" +#include "geometry/PxConvexMesh.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxConvexMesh; + +/** +\brief Flags controlling the simulated behavior of the convex mesh geometry. + +Used in ::PxConvexMeshGeometryFlags. +*/ +struct PxConvexMeshGeometryFlag +{ + enum Enum + { + eTIGHT_BOUNDS = (1<<0) //!< Use tighter (but more expensive to compute) bounds around the convex geometry. + }; +}; + +/** +\brief collection of set bits defined in PxConvexMeshGeometryFlag. + +@see PxConvexMeshGeometryFlag +*/ +typedef PxFlags PxConvexMeshGeometryFlags; +PX_FLAGS_OPERATORS(PxConvexMeshGeometryFlag::Enum,PxU8) + +/** +\brief Convex mesh geometry class. + +This class unifies a convex mesh object with a scaling transform, and +lets the combined object be used anywhere a PxGeometry is needed. + +The scaling is a transform along arbitrary axes contained in the scale object. +The vertices of the mesh in geometry (or shape) space is the +PxMeshScale::toMat33() transform, multiplied by the vertex space vertices +in the PxConvexMesh object. +*/ +class PxConvexMeshGeometry : public PxGeometry +{ +public: + /** + \brief Default constructor. + + Creates an empty object with a NULL mesh and identity scale. + */ + PX_INLINE PxConvexMeshGeometry() : + PxGeometry (PxGeometryType::eCONVEXMESH), + scale (PxMeshScale(1.0f)), + convexMesh (NULL), + meshFlags (PxConvexMeshGeometryFlag::eTIGHT_BOUNDS) + {} + + /** + \brief Constructor. + \param[in] mesh Mesh pointer. May be NULL, though this will not make the object valid for shape construction. + \param[in] scaling Scale factor. + \param[in] flags Mesh flags. + \ + */ + PX_INLINE PxConvexMeshGeometry( PxConvexMesh* mesh, + const PxMeshScale& scaling = PxMeshScale(), + PxConvexMeshGeometryFlags flags = PxConvexMeshGeometryFlag::eTIGHT_BOUNDS) : + PxGeometry (PxGeometryType::eCONVEXMESH), + scale (scaling), + convexMesh (mesh), + meshFlags (flags) + { + } + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid for shape creation. + + \note A valid convex mesh has a positive scale value in each direction (scale.x > 0, scale.y > 0, scale.z > 0). + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a convex that has zero extent in any direction. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + PxMeshScale scale; //!< The scaling transformation (from vertex space to shape space). + PxConvexMesh* convexMesh; //!< A reference to the convex mesh object. + PxConvexMeshGeometryFlags meshFlags; //!< Mesh flags. + PxPadding<3> paddingFromFlags; //!< padding for mesh flags +}; + + +PX_INLINE bool PxConvexMeshGeometry::isValid() const +{ + if(mType != PxGeometryType::eCONVEXMESH) + return false; + if(!scale.scale.isFinite() || !scale.rotation.isUnit()) + return false; + if(!scale.isValidForConvexMesh()) + return false; + if(!convexMesh) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxGeometry.h b/src/PhysX/physx/include/geometry/PxGeometry.h new file mode 100644 index 000000000..5c8133918 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxGeometry.h @@ -0,0 +1,94 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_GEOMETRY +#define PX_PHYSICS_NX_GEOMETRY +/** \addtogroup geomutils +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxFlags.h" +#include "foundation/PxMath.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief A geometry type. + +Used to distinguish the type of a ::PxGeometry object. +*/ +struct PxGeometryType +{ + enum Enum + { + eSPHERE, + ePLANE, + eCAPSULE, + eBOX, + eCONVEXMESH, + eTRIANGLEMESH, + eHEIGHTFIELD, + eGEOMETRY_COUNT, //!< internal use only! + eINVALID = -1 //!< internal use only! + }; +}; + +/** +\brief A geometry object. + +A geometry object defines the characteristics of a spatial object, but without any information +about its placement in the world. + +\note This is an abstract class. You cannot create instances directly. Create an instance of one of the derived classes instead. +*/ +class PxGeometry +{ +public: + /** + \brief Returns the type of the geometry. + \return The type of the object. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxGeometryType::Enum getType() const { return mType; } + +protected: + PX_CUDA_CALLABLE PX_FORCE_INLINE PxGeometry(PxGeometryType::Enum type) : mType(type) {} + PxGeometryType::Enum mType; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxGeometryHelpers.h b/src/PhysX/physx/include/geometry/PxGeometryHelpers.h new file mode 100644 index 000000000..e4637df70 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxGeometryHelpers.h @@ -0,0 +1,214 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMETRYHELPERS +#define PX_PHYSICS_GEOMETRYHELPERS +/** \addtogroup geomutils +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "PxGeometry.h" +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "foundation/PxPlane.h" +#include "foundation/PxTransform.h" +#include "foundation/PxUnionCast.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Geometry holder class + +This class contains enough space to hold a value of any PxGeometry subtype. + +Its principal use is as a convenience class to allow geometries to be returned polymorphically +from functions. See PxShape::getGeometry(); +*/ + +PX_ALIGN_PREFIX(4) +class PxGeometryHolder +{ +public: + PX_FORCE_INLINE PxGeometryType::Enum getType() const + { + return any().getType(); + } + + PX_FORCE_INLINE PxGeometry& any() + { + return *PxUnionCast(&bytes.geometry); + } + + PX_FORCE_INLINE const PxGeometry& any() const + { + return *PxUnionCast(&bytes.geometry); + } + + PX_FORCE_INLINE PxSphereGeometry& sphere() + { + return get(); + } + + PX_FORCE_INLINE const PxSphereGeometry& sphere() const + { + return get(); + } + + PX_FORCE_INLINE PxPlaneGeometry& plane() + { + return get(); + } + + PX_FORCE_INLINE const PxPlaneGeometry& plane() const + { + return get(); + } + + PX_FORCE_INLINE PxCapsuleGeometry& capsule() + { + return get(); + } + + PX_FORCE_INLINE const PxCapsuleGeometry& capsule() const + { + return get(); + } + + PX_FORCE_INLINE PxBoxGeometry& box() + { + return get(); + } + + PX_FORCE_INLINE const PxBoxGeometry& box() const + { + return get(); + } + + PX_FORCE_INLINE PxConvexMeshGeometry& convexMesh() + { + return get(); + } + + PX_FORCE_INLINE const PxConvexMeshGeometry& convexMesh() const + { + return get(); + } + + PX_FORCE_INLINE PxTriangleMeshGeometry& triangleMesh() + { + return get(); + } + + PX_FORCE_INLINE const PxTriangleMeshGeometry& triangleMesh() const + { + return get(); + } + + PX_FORCE_INLINE PxHeightFieldGeometry& heightField() + { + return get(); + } + + PX_FORCE_INLINE const PxHeightFieldGeometry& heightField() const + { + return get(); + } + + PX_FORCE_INLINE void storeAny(const PxGeometry& geometry) + { + PX_ASSERT_WITH_MESSAGE( (geometry.getType() >= PxGeometryType::eSPHERE) && + (geometry.getType() < PxGeometryType::eGEOMETRY_COUNT), + "Unexpected GeometryType in PxGeometryHolder::storeAny"); + + switch(geometry.getType()) + { + case PxGeometryType::eSPHERE: put(geometry); break; + case PxGeometryType::ePLANE: put(geometry); break; + case PxGeometryType::eCAPSULE: put(geometry); break; + case PxGeometryType::eBOX: put(geometry); break; + case PxGeometryType::eCONVEXMESH: put(geometry); break; + case PxGeometryType::eTRIANGLEMESH: put(geometry); break; + case PxGeometryType::eHEIGHTFIELD: put(geometry); break; + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: break; + } + } + + PX_FORCE_INLINE PxGeometryHolder() {} + PX_FORCE_INLINE PxGeometryHolder(const PxGeometry& geometry){ storeAny(geometry); } + + private: + template void put(const PxGeometry& geometry) + { + static_cast(any()) = static_cast(geometry); + } + + template T& get() + { + PX_ASSERT(getType() == type); + return static_cast(any()); + } + + template T& get() const + { + PX_ASSERT(getType() == type); + return static_cast(any()); + } + + union { + PxU8 geometry[sizeof(PxGeometry)]; + PxU8 box[sizeof(PxBoxGeometry)]; + PxU8 sphere[sizeof(PxSphereGeometry)]; + PxU8 capsule[sizeof(PxCapsuleGeometry)]; + PxU8 plane[sizeof(PxPlaneGeometry)]; + PxU8 convex[sizeof(PxConvexMeshGeometry)]; + PxU8 mesh[sizeof(PxTriangleMeshGeometry)]; + PxU8 heightfield[sizeof(PxHeightFieldGeometry)]; + } bytes; +} +PX_ALIGN_SUFFIX(4); + + + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxGeometryQuery.h b/src/PhysX/physx/include/geometry/PxGeometryQuery.h new file mode 100644 index 000000000..95274b34f --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxGeometryQuery.h @@ -0,0 +1,225 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_PX_GEOMETRY_QUERY +#define PX_PHYSICS_GEOMUTILS_PX_GEOMETRY_QUERY + +/** +\brief Maximum sweep distance for scene sweeps. The distance parameter for sweep functions will be clamped to this value. +The reason for this is GJK support cannot be evaluated near infinity. A viable alternative can be a sweep followed by an infinite raycast. + +@see PxScene +*/ +#define PX_MAX_SWEEP_DISTANCE 1e8f + +/** \addtogroup geomutils + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "PxQueryReport.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxGeometry; +struct PxSweepHit; +struct PxRaycastHit; + +class PxTriangle; + +/** +\brief Collection of geometry object queries (sweeps, raycasts, overlaps, ...). +*/ +class PxGeometryQuery +{ +public: + + /** + \brief Sweep a specified geometry object in space and test for collision with a given object. + + The following combinations are supported. + + \li PxSphereGeometry vs. {PxSphereGeometry, PxPlaneGeometry, PxCapsuleGeometry, PxBoxGeometry, PxConvexMeshGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry} + \li PxCapsuleGeometry vs. {PxSphereGeometry, PxPlaneGeometry, PxCapsuleGeometry, PxBoxGeometry, PxConvexMeshGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry} + \li PxBoxGeometry vs. {PxSphereGeometry, PxPlaneGeometry, PxCapsuleGeometry, PxBoxGeometry, PxConvexMeshGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry} + \li PxConvexMeshGeometry vs. {PxSphereGeometry, PxPlaneGeometry, PxCapsuleGeometry, PxBoxGeometry, PxConvexMeshGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry} + + \param[in] unitDir Normalized direction along which object geom0 should be swept + \param[in] maxDist Maximum sweep distance, has to be in the [0, inf) range + \param[in] geom0 The geometry object to sweep. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry, #PxBoxGeometry and #PxConvexMeshGeometry + \param[in] pose0 Pose of the geometry object to sweep + \param[in] geom1 The geometry object to test the sweep against + \param[in] pose1 Pose of the geometry object to sweep against + \param[out] sweepHit The sweep hit information. Only valid if this method returns true. + \param[in] hitFlags Specify which properties per hit should be computed and written to result hit array. Combination of #PxHitFlag flags + \param[in] inflation Surface of the swept shape is additively extruded in the normal direction, rounding corners and edges. + + \return True if the swept geometry object geom0 hits the object geom1 + + @see PxSweepHit PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static bool sweep(const PxVec3& unitDir, + const PxReal maxDist, + const PxGeometry& geom0, + const PxTransform& pose0, + const PxGeometry& geom1, + const PxTransform& pose1, + PxSweepHit& sweepHit, + PxHitFlags hitFlags = PxHitFlag::eDEFAULT, + const PxReal inflation = 0.f); + + + /** + \brief Overlap test for two geometry objects. + + All combinations are supported except: + \li PxPlaneGeometry vs. {PxPlaneGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry} + \li PxTriangleMeshGeometry vs. {PxTriangleMeshGeometry, PxHeightFieldGeometry} + \li PxHeightFieldGeometry vs. {PxHeightFieldGeometry} + + \param[in] geom0 The first geometry object + \param[in] pose0 Pose of the first geometry object + \param[in] geom1 The second geometry object + \param[in] pose1 Pose of the second geometry object + \return True if the two geometry objects overlap + + @see PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static bool overlap(const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1); + + + /** + \brief Raycast test against a geometry object. + + \param[in] origin The origin of the ray to test the geometry object against + \param[in] unitDir Normalized direction of the ray to test the geometry object against + \param[in] geom The geometry object to test the ray against + \param[in] pose Pose of the geometry object + \param[in] maxDist Maximum ray length, has to be in the [0, inf) range + \param[in] hitFlags Specification of the kind of information to retrieve on hit. Combination of #PxHitFlag flags + \param[in] maxHits max number of returned hits = size of 'rayHits' buffer + \param[out] rayHits Raycast hits information + \return Number of hits between the ray and the geometry object + + @see PxRaycastHit PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static PxU32 raycast(const PxVec3& origin, + const PxVec3& unitDir, + const PxGeometry& geom, + const PxTransform& pose, + PxReal maxDist, + PxHitFlags hitFlags, + PxU32 maxHits, + PxRaycastHit* PX_RESTRICT rayHits); + + /** + \brief Compute minimum translational distance (MTD) between two geometry objects. + + All combinations of geom objects are supported except: + - plane/plane + - plane/mesh + - plane/heightfield + - mesh/mesh + - mesh/heightfield + - heightfield/heightfield + + The function returns a unit vector ('direction') and a penetration depth ('depth'). + + The depenetration vector D = direction * depth should be applied to the first object, to + get out of the second object. + + Returned depth should always be positive or null. + + If objects do not overlap, the function can not compute the MTD and returns false. + + \param[out] direction Computed MTD unit direction + \param[out] depth Penetration depth. Always positive or null. + \param[in] geom0 The first geometry object + \param[in] pose0 Pose of the first geometry object + \param[in] geom1 The second geometry object + \param[in] pose1 Pose of the second geometry object + \return True if the MTD has successfully been computed, i.e. if objects do overlap. + + @see PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static bool computePenetration(PxVec3& direction, PxF32& depth, + const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1); + + /** + \brief Computes distance between a point and a geometry object. + + Currently supported geometry objects: box, sphere, capsule, convex. + + \param[in] point The point P + \param[in] geom The geometry object + \param[in] pose Pose of the geometry object + \param[out] closestPoint Optionally returned closest point to P on the geom object. Only valid when returned distance is strictly positive. + \return Square distance between the point and the geom object, or 0.0 if the point is inside the object, or -1.0 if the geometry type is not supported. + + @see PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static PxReal pointDistance(const PxVec3& point, const PxGeometry& geom, const PxTransform& pose, PxVec3* closestPoint=NULL); + + + /** + \brief get the bounds for a geometry object + + \param[in] geom The geometry object + \param[in] pose Pose of the geometry object + \param[in] inflation Scale factor for computed world bounds. Box extents are multiplied by this value. + \return The bounds of the object + + @see PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static PxBounds3 getWorldBounds(const PxGeometry& geom, const PxTransform& pose, float inflation=1.01f); + + /** + \brief Checks if provided geometry is valid. + + \param[in] geom The geometry object. + \return True if geometry is valid. + + @see PxGeometry PxSphereGeometry, PxCapsuleGeometry, PxBoxGeometry, PxConvexGeometry + */ + PX_PHYSX_COMMON_API static bool isValid(const PxGeometry& geom); +}; + + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxHeightField.h b/src/PhysX/physx/include/geometry/PxHeightField.h new file mode 100644 index 000000000..5cc07ae71 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxHeightField.h @@ -0,0 +1,262 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_NX_HEIGHTFIELD +#define PX_PHYSICS_GEOMUTILS_NX_HEIGHTFIELD +/** \addtogroup geomutils + @{ +*/ + +#include "geometry/PxHeightFieldFlag.h" +#include "geometry/PxHeightFieldSample.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxHeightFieldDesc; + +/** +\brief A height field class. + +Height fields work in a similar way as triangle meshes specified to act as +height fields, with some important differences: + +Triangle meshes can be made of nonuniform geometry, while height fields are +regular, rectangular grids. This means that with PxHeightField, you sacrifice +flexibility in return for improved performance and decreased memory consumption. + +In local space rows extend in X direction, columns in Z direction and height in Y direction. + +Like Convexes and TriangleMeshes, HeightFields are referenced by shape instances +(see #PxHeightFieldGeometry, #PxShape). + +To avoid duplicating data when you have several instances of a particular +height field differently, you do not use this class to represent a +height field object directly. Instead, you create an instance of this height field +via the PxHeightFieldGeometry and PxShape classes. + +

Creation

+ +To create an instance of this class call PxPhysics::createHeightField() or +PxCooking::createHeightField(const PxHeightFieldDesc&, PxPhysicsInsertionCallback&). +To delete it call release(). This is only possible +once you have released all of its PxHeightFiedShape instances. + +

Visualizations:

+\li #PxVisualizationParameter::eCOLLISION_AABBS +\li #PxVisualizationParameter::eCOLLISION_SHAPES +\li #PxVisualizationParameter::eCOLLISION_AXES +\li #PxVisualizationParameter::eCOLLISION_FNORMALS +\li #PxVisualizationParameter::eCOLLISION_EDGES + +@see PxHeightFieldDesc PxHeightFieldGeometry PxShape PxPhysics.createHeightField() PxCooking.createHeightField() +*/ + +class PxHeightField : public PxBase +{ + public: + /** + \brief Decrements the reference count of a height field and releases it if the new reference count is zero. + + @see PxPhysics.createHeightField() PxHeightFieldDesc PxHeightFieldGeometry PxShape + */ + PX_PHYSX_COMMON_API virtual void release() = 0; + + /** + \brief Writes out the sample data array. + + The user provides destBufferSize bytes storage at destBuffer. + The data is formatted and arranged as PxHeightFieldDesc.samples. + + \param[out] destBuffer The destination buffer for the sample data. + \param[in] destBufferSize The size of the destination buffer. + \return The number of bytes written. + + @see PxHeightFieldDesc.samples + */ + PX_PHYSX_COMMON_API virtual PxU32 saveCells(void* destBuffer, PxU32 destBufferSize) const = 0; + + /** + \brief Replaces a rectangular subfield in the sample data array. + + The user provides the description of a rectangular subfield in subfieldDesc. + The data is formatted and arranged as PxHeightFieldDesc.samples. + + \param[in] startCol First cell in the destination heightfield to be modified. Can be negative. + \param[in] startRow First row in the destination heightfield to be modified. Can be negative. + \param[in] subfieldDesc Description of the source subfield to read the samples from. + \param[in] shrinkBounds If left as false, the bounds will never shrink but only grow. If set to true the bounds will be recomputed from all HF samples at O(nbColums*nbRows) perf cost. + \return True on success, false on failure. Failure can occur due to format mismatch. + + \note Modified samples are constrained to the same height quantization range as the original heightfield. + Source samples that are out of range of target heightfield will be clipped with no error. + PhysX does not keep a mapping from the heightfield to heightfield shapes that reference it. + Call PxShape::setGeometry on each shape which references the height field, to ensure that internal data structures are updated to reflect the new geometry. + Please note that PxShape::setGeometry does not guarantee correct/continuous behavior when objects are resting on top of old or new geometry. + + @see PxHeightFieldDesc.samples PxShape.setGeometry + */ + PX_PHYSX_COMMON_API virtual bool modifySamples(PxI32 startCol, PxI32 startRow, const PxHeightFieldDesc& subfieldDesc, bool shrinkBounds = false) = 0; + + /** + \brief Retrieves the number of sample rows in the samples array. + + \return The number of sample rows in the samples array. + + @see PxHeightFieldDesc.nbRows + */ + PX_PHYSX_COMMON_API virtual PxU32 getNbRows() const = 0; + + /** + \brief Retrieves the number of sample columns in the samples array. + + \return The number of sample columns in the samples array. + + @see PxHeightFieldDesc.nbColumns + */ + PX_PHYSX_COMMON_API virtual PxU32 getNbColumns() const = 0; + + /** + \brief Retrieves the format of the sample data. + + \return The format of the sample data. + + @see PxHeightFieldDesc.format PxHeightFieldFormat + */ + PX_PHYSX_COMMON_API virtual PxHeightFieldFormat::Enum getFormat() const = 0; + + /** + \brief Retrieves the offset in bytes between consecutive samples in the array. + + \return The offset in bytes between consecutive samples in the array. + + @see PxHeightFieldDesc.sampleStride + */ + PX_PHYSX_COMMON_API virtual PxU32 getSampleStride() const = 0; + + /** + \brief Retrieves the convex edge threshold. + + \return The convex edge threshold. + + @see PxHeightFieldDesc.convexEdgeThreshold + */ + PX_PHYSX_COMMON_API virtual PxReal getConvexEdgeThreshold() const = 0; + + /** + \brief Retrieves the flags bits, combined from values of the enum ::PxHeightFieldFlag. + + \return The flags bits, combined from values of the enum ::PxHeightFieldFlag. + + @see PxHeightFieldDesc.flags PxHeightFieldFlag + */ + PX_PHYSX_COMMON_API virtual PxHeightFieldFlags getFlags() const = 0; + + /** + \brief Retrieves the height at the given coordinates in grid space. + + \return The height at the given coordinates or 0 if the coordinates are out of range. + */ + PX_PHYSX_COMMON_API virtual PxReal getHeight(PxReal x, PxReal z) const = 0; + + /** + \brief Returns the reference count for shared heightfields. + + At creation, the reference count of the heightfield is 1. Every shape referencing this heightfield increments the + count by 1. When the reference count reaches 0, and only then, the heightfield gets destroyed automatically. + + \return the current reference count. + */ + PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const = 0; + + /** + \brief Acquires a counted reference to a heightfield. + + This method increases the reference count of the heightfield by 1. Decrement the reference count by calling release() + */ + PX_PHYSX_COMMON_API virtual void acquireReference() = 0; + + /** + \brief Returns material table index of given triangle + + \note This function takes a post cooking triangle index. + + \param[in] triangleIndex (internal) index of desired triangle + \return Material table index, or 0xffff if no per-triangle materials are used + */ + PX_PHYSX_COMMON_API virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const = 0; + + /** + \brief Returns a triangle face normal for a given triangle index + + \note This function takes a post cooking triangle index. + + \param[in] triangleIndex (internal) index of desired triangle + \return Triangle normal for a given triangle index + */ + PX_PHYSX_COMMON_API virtual PxVec3 getTriangleNormal(PxTriangleID triangleIndex) const = 0; + + /** + \brief Returns heightfield sample of given row and column + + \param[in] row Given heightfield row + \param[in] column Given heightfield column + \return Heightfield sample + */ + PX_PHYSX_COMMON_API virtual const PxHeightFieldSample& getSample(PxU32 row, PxU32 column) const = 0; + + /** + \brief Returns the number of times the heightfield data has been modified + + This method returns the number of times modifySamples has been called on this heightfield, so that code that has + retained state that depends on the heightfield can efficiently determine whether it has been modified. + + \return the number of times the heightfield sample data has been modified. + */ + PX_PHYSX_COMMON_API virtual PxU32 getTimestamp() const = 0; + + PX_PHYSX_COMMON_API virtual const char* getConcreteTypeName() const { return "PxHeightField"; } + +protected: + PX_INLINE PxHeightField(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxHeightField(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + PX_PHYSX_COMMON_API virtual ~PxHeightField() {} + PX_PHYSX_COMMON_API virtual bool isKindOf(const char* name) const { return !::strcmp("PxHeightField", name) || PxBase::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldDesc.h b/src/PhysX/physx/include/geometry/PxHeightFieldDesc.h new file mode 100644 index 000000000..eac748ccc --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxHeightFieldDesc.h @@ -0,0 +1,187 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_NXHEIGHTFIELDDESC +#define PX_COLLISION_NXHEIGHTFIELDDESC +/** \addtogroup geomutils +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "geometry/PxHeightFieldFlag.h" +#include "common/PxCoreUtilityTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Descriptor class for #PxHeightField. + +\note The heightfield data is *copied* when a PxHeightField object is created from this descriptor. After the call the +user may discard the height data. + +@see PxHeightField PxHeightFieldGeometry PxShape PxPhysics.createHeightField() PxCooking.createHeightField() +*/ +class PxHeightFieldDesc +{ +public: + + /** + \brief Number of sample rows in the height field samples array. + + \note Local space X-axis corresponds to rows. + + Range: >1
+ Default: 0 + */ + PxU32 nbRows; + + /** + \brief Number of sample columns in the height field samples array. + + \note Local space Z-axis corresponds to columns. + + Range: >1
+ Default: 0 + */ + PxU32 nbColumns; + + /** + \brief Format of the sample data. + + Currently the only supported format is PxHeightFieldFormat::eS16_TM: + + Default: PxHeightFieldFormat::eS16_TM + + @see PxHeightFormat PxHeightFieldDesc.samples + */ + PxHeightFieldFormat::Enum format; + + /** + \brief The samples array. + + It is copied to the SDK's storage at creation time. + + There are nbRows * nbColumn samples in the array, + which define nbRows * nbColumn vertices and cells, + of which (nbRows - 1) * (nbColumns - 1) cells are actually used. + + The array index of sample(row, column) = row * nbColumns + column. + The byte offset of sample(row, column) = sampleStride * (row * nbColumns + column). + The sample data follows at the offset and spans the number of bytes defined by the format. + Then there are zero or more unused bytes depending on sampleStride before the next sample. + + Default: NULL + + @see PxHeightFormat + */ + PxStridedData samples; + + /** + This threshold is used by the collision detection to determine if a height field edge is convex + and can generate contact points. + Usually the convexity of an edge is determined from the angle (or cosine of the angle) between + the normals of the faces sharing that edge. + The height field allows a more efficient approach by comparing height values of neighboring vertices. + This parameter offsets the comparison. Smaller changes than 0.5 will not alter the set of convex edges. + The rule of thumb is that larger values will result in fewer edge contacts. + + This parameter is ignored in contact generation with sphere and capsule primitives. + + Range: [0, PX_MAX_F32)
+ Default: 0 + */ + PxReal convexEdgeThreshold; + + /** + \brief Flags bits, combined from values of the enum ::PxHeightFieldFlag. + + Default: 0 + + @see PxHeightFieldFlag PxHeightFieldFlags + */ + PxHeightFieldFlags flags; + + /** + \brief Constructor sets to default. + */ + PX_INLINE PxHeightFieldDesc(); + + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE void setToDefault(); + + /** + \brief Returns true if the descriptor is valid. + \return True if the current settings are valid. + */ + PX_INLINE bool isValid() const; +}; + +PX_INLINE PxHeightFieldDesc::PxHeightFieldDesc() //constructor sets to default +{ + nbColumns = 0; + nbRows = 0; + format = PxHeightFieldFormat::eS16_TM; + convexEdgeThreshold = 0.0f; + flags = PxHeightFieldFlags(); +} + +PX_INLINE void PxHeightFieldDesc::setToDefault() +{ + *this = PxHeightFieldDesc(); +} + +PX_INLINE bool PxHeightFieldDesc::isValid() const +{ + if (nbColumns < 2) + return false; + if (nbRows < 2) + return false; + if(format != PxHeightFieldFormat::eS16_TM) + return false; + if (samples.stride < 4) + return false; + if (convexEdgeThreshold < 0) + return false; + if ((flags & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) != flags) + return false; + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldFlag.h b/src/PhysX/physx/include/geometry/PxHeightFieldFlag.h new file mode 100644 index 000000000..0f0e407df --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxHeightFieldFlag.h @@ -0,0 +1,162 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_NXHEIGHTFIELDFLAG +#define PX_COLLISION_NXHEIGHTFIELDFLAG +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Describes the format of height field samples. +@see PxHeightFieldDesc.format PxHeightFieldDesc.samples +*/ +struct PxHeightFieldFormat +{ + enum Enum + { + /** + \brief Height field height data is 16 bit signed integers, followed by triangle materials. + + Each sample is 32 bits wide arranged as follows: + + \image html heightFieldFormat_S16_TM.png + + 1) First there is a 16 bit height value. + 2) Next, two one byte material indices, with the high bit of each byte reserved for special use. + (so the material index is only 7 bits). + The high bit of material0 is the tess-flag. + The high bit of material1 is reserved for future use. + + There are zero or more unused bytes before the next sample depending on PxHeightFieldDesc.sampleStride, + where the application may eventually keep its own data. + + This is the only format supported at the moment. + + @see PxHeightFieldDesc.format PxHeightFieldDesc.samples + */ + eS16_TM = (1 << 0) + }; +}; + +/** +\brief Determines the tessellation of height field cells. +@see PxHeightFieldDesc.format PxHeightFieldDesc.samples +*/ +struct PxHeightFieldTessFlag +{ + enum Enum + { + /** + \brief This flag determines which way each quad cell is subdivided. + + The flag lowered indicates subdivision like this: (the 0th vertex is referenced by only one triangle) + + \image html heightfieldTriMat2.PNG + +
+		+--+--+--+---> column
+		| /| /| /|
+		|/ |/ |/ |
+		+--+--+--+
+		| /| /| /|
+		|/ |/ |/ |
+		+--+--+--+
+		|
+		|
+		V row
+		
+ + The flag raised indicates subdivision like this: (the 0th vertex is shared by two triangles) + + \image html heightfieldTriMat1.PNG + +
+		+--+--+--+---> column
+		|\ |\ |\ |
+		| \| \| \|
+		+--+--+--+
+		|\ |\ |\ |
+		| \| \| \|
+		+--+--+--+
+		|
+		|
+		V row
+		
+ + @see PxHeightFieldDesc.format PxHeightFieldDesc.samples + */ + e0TH_VERTEX_SHARED = (1 << 0) + }; +}; + + +/** +\brief Enum with flag values to be used in PxHeightFieldDesc.flags. +*/ +struct PxHeightFieldFlag +{ + enum Enum + { + /** + \brief Disable collisions with height field with boundary edges. + + Raise this flag if several terrain patches are going to be placed adjacent to each other, + to avoid a bump when sliding across. + + This flag is ignored in contact generation with sphere and capsule shapes. + + @see PxHeightFieldDesc.flags + */ + eNO_BOUNDARY_EDGES = (1 << 0) + }; +}; + +/** +\brief collection of set bits defined in PxHeightFieldFlag. + +@see PxHeightFieldFlag +*/ +typedef PxFlags PxHeightFieldFlags; +PX_FLAGS_OPERATORS(PxHeightFieldFlag::Enum,PxU16) + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h b/src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h new file mode 100644 index 000000000..b0dc61102 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h @@ -0,0 +1,143 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_HEIGHTFIELD_GEOMETRY +#define PX_PHYSICS_NX_HEIGHTFIELD_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxTriangleMeshGeometry.h" +#include "common/PxCoreUtilityTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#define PX_MIN_HEIGHTFIELD_XZ_SCALE 1e-8f +#define PX_MIN_HEIGHTFIELD_Y_SCALE (0.0001f / PxReal(0xFFFF)) + +class PxHeightField; + +/** +\brief Height field geometry class. + +This class allows to create a scaled height field geometry instance. + +There is a minimum allowed value for Y and XZ scaling - PX_MIN_HEIGHTFIELD_XZ_SCALE, heightfield creation will fail if XZ value is below this value. +*/ +class PxHeightFieldGeometry : public PxGeometry +{ +public: + PX_INLINE PxHeightFieldGeometry() : + PxGeometry (PxGeometryType::eHEIGHTFIELD), + heightField (NULL), + heightScale (1.0f), + rowScale (1.0f), + columnScale (1.0f), + heightFieldFlags(0) + {} + + PX_INLINE PxHeightFieldGeometry(PxHeightField* hf, + PxMeshGeometryFlags flags, + PxReal heightScale_, + PxReal rowScale_, + PxReal columnScale_) : + PxGeometry (PxGeometryType::eHEIGHTFIELD), + heightField (hf) , + heightScale (heightScale_), + rowScale (rowScale_), + columnScale (columnScale_), + heightFieldFlags (flags) + { + } + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid + + \note A valid height field has a positive scale value in each direction (heightScale > 0, rowScale > 0, columnScale > 0). + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a height field that has zero extents in any direction. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + /** + \brief The height field data. + */ + PxHeightField* heightField; + + /** + \brief The scaling factor for the height field in vertical direction (y direction in local space). + */ + PxReal heightScale; + + /** + \brief The scaling factor for the height field in the row direction (x direction in local space). + */ + PxReal rowScale; + + /** + \brief The scaling factor for the height field in the column direction (z direction in local space). + */ + PxReal columnScale; + + /** + \brief Flags to specify some collision properties for the height field. + */ + PxMeshGeometryFlags heightFieldFlags; + + PxPadding<3> paddingFromFlags; //!< padding for mesh flags. +}; + + +PX_INLINE bool PxHeightFieldGeometry::isValid() const +{ + if (mType != PxGeometryType::eHEIGHTFIELD) + return false; + if (!PxIsFinite(heightScale) || !PxIsFinite(rowScale) || !PxIsFinite(columnScale)) + return false; + if (rowScale < PX_MIN_HEIGHTFIELD_XZ_SCALE || columnScale < PX_MIN_HEIGHTFIELD_XZ_SCALE || heightScale < PX_MIN_HEIGHTFIELD_Y_SCALE) + return false; + if (!heightField) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldSample.h b/src/PhysX/physx/include/geometry/PxHeightFieldSample.h new file mode 100644 index 000000000..4f2170556 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxHeightFieldSample.h @@ -0,0 +1,124 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NXHEIGHTFIELDSAMPLE +#define PX_PHYSICS_NXHEIGHTFIELDSAMPLE +/** \addtogroup geomutils +@{ */ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxBitAndData.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Special material index values for height field samples. + +@see PxHeightFieldSample.materialIndex0 PxHeightFieldSample.materialIndex1 +*/ +struct PxHeightFieldMaterial +{ + enum Enum + { + eHOLE = 127 //!< A material indicating that the triangle should be treated as a hole in the mesh. + }; +}; + +/** +\brief Heightfield sample format. + +This format corresponds to the #PxHeightFieldFormat member PxHeightFieldFormat::eS16_TM. + +An array of heightfield samples are used when creating a PxHeightField to specify +the elevation of the heightfield points. In addition the material and tessellation of the adjacent +triangles are specified. + +@see PxHeightField PxHeightFieldDesc PxHeightFieldDesc.samples +*/ +struct PxHeightFieldSample +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + /** + \brief The height of the heightfield sample + + This value is scaled by PxHeightFieldGeometry::heightScale. + + @see PxHeightFieldGeometry + */ + PxI16 height; + + /** + \brief The triangle material index of the quad's lower triangle + tesselation flag + + An index pointing into the material table of the shape which instantiates the heightfield. + This index determines the material of the lower of the quad's two triangles (i.e. the quad whose + upper-left corner is this sample, see the Guide for illustrations). + + Special values of the 7 data bits are defined by PxHeightFieldMaterial + + The tesselation flag specifies which way the quad is split whose upper left corner is this sample. + If the flag is set, the diagonal of the quad will run from this sample to the opposite vertex; if not, + it will run between the other two vertices (see the Guide for illustrations). + + @see PxHeightFieldGeometry materialIndex1 PxShape.setmaterials() PxShape.getMaterials() + */ + PxBitAndByte materialIndex0; + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU8 tessFlag() const { return PxU8(materialIndex0.isBitSet() ? 1 : 0); } // PT: explicit conversion to make sure we don't break the code + PX_CUDA_CALLABLE PX_FORCE_INLINE void setTessFlag() { materialIndex0.setBit(); } + PX_CUDA_CALLABLE PX_FORCE_INLINE void clearTessFlag() { materialIndex0.clearBit(); } + + /** + \brief The triangle material index of the quad's upper triangle + reserved flag + + An index pointing into the material table of the shape which instantiates the heightfield. + This index determines the material of the upper of the quad's two triangles (i.e. the quad whose + upper-left corner is this sample, see the Guide for illustrations). + + @see PxHeightFieldGeometry materialIndex0 PxShape.setmaterials() PxShape.getMaterials() + */ + PxBitAndByte materialIndex1; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxMeshQuery.h b/src/PhysX/physx/include/geometry/PxMeshQuery.h new file mode 100644 index 000000000..0c381ea7e --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxMeshQuery.h @@ -0,0 +1,201 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_PX_MESH_QUERY +#define PX_PHYSICS_GEOMUTILS_PX_MESH_QUERY + +/** \addtogroup geomutils + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "PxQueryReport.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxGeometry; +class PxConvexMeshGeometry; +class PxTriangleMeshGeometry; +class PxHeightFieldGeometry; + +class PxTriangle; + +class PxMeshQuery +{ +public: + + /** + \brief Retrieves triangle data from a triangle ID. + + This function can be used together with #findOverlapTriangleMesh() to retrieve triangle properties. + + \param[in] triGeom Geometry of the triangle mesh to extract the triangle from. + \param[in] transform Transform for the triangle mesh + \param[in] triangleIndex The index of the triangle to retrieve. + \param[out] triangle Triangle points in world space. + \param[out] vertexIndices Returned vertex indices for given triangle + \param[out] adjacencyIndices Returned 3 triangle adjacency internal face indices (0xFFFFFFFF if no adjacency). The mesh must be cooked with cooking param buildTriangleAdjacencies enabled. + + \note This function will flip the triangle normal whenever triGeom.scale.hasNegativeDeterminant() is true. + + @see PxTriangle PxTriangleFlags PxTriangleID findOverlapTriangleMesh() + */ + PX_PHYSX_COMMON_API static void getTriangle(const PxTriangleMeshGeometry& triGeom, const PxTransform& transform, PxTriangleID triangleIndex, PxTriangle& triangle, PxU32* vertexIndices=NULL, PxU32* adjacencyIndices=NULL); + + + /** + \brief Retrieves triangle data from a triangle ID. + + This function can be used together with #findOverlapHeightField() to retrieve triangle properties. + + \param[in] hfGeom Geometry of the height field to extract the triangle from. + \param[in] transform Transform for the height field. + \param[in] triangleIndex The index of the triangle to retrieve. + \param[out] triangle Triangle points in world space. + \param[out] vertexIndices Returned vertex indices for given triangle + \param[out] adjacencyIndices Returned 3 triangle adjacency triangle indices (0xFFFFFFFF if no adjacency). + + \note This function will flip the triangle normal whenever triGeom.scale.hasNegativeDeterminant() is true. + \note TriangleIndex is an index used in internal format, which does have an index out of the bounds in last row. + To traverse all tri indices in the HF, the following code can be applied: + for (PxU32 row = 0; row < (nbRows - 1); row++) + { + for (PxU32 col = 0; col < (nbCols - 1); col++) + { + for (PxU32 k = 0; k < 2; k++) + { + const PxU32 triIndex = 2 * (row*nbCols + col) + k; + .... + } + } + } + @see PxTriangle PxTriangleFlags PxTriangleID findOverlapHeightField() + */ + PX_PHYSX_COMMON_API static void getTriangle(const PxHeightFieldGeometry& hfGeom, const PxTransform& transform, PxTriangleID triangleIndex, PxTriangle& triangle, PxU32* vertexIndices=NULL, PxU32* adjacencyIndices=NULL); + + + /** + \brief Find the mesh triangles which touch the specified geometry object. + + Returned triangle indices can be used with #getTriangle() to retrieve the triangle properties. + + \param[in] geom The geometry object to test for mesh triangle overlaps. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry and #PxBoxGeometry + \param[in] geomPose Pose of the geometry object + \param[in] meshGeom The triangle mesh geometry to check overlap against + \param[in] meshPose Pose of the triangle mesh + \param[out] results Indices of overlapping triangles + \param[in] maxResults Size of 'results' buffer + \param[in] startIndex Index of first result to be retrieved. Previous indices are skipped. + \param[out] overflow True if a buffer overflow occurred + \return Number of overlaps found, i.e. number of elements written to the results buffer + + @see PxTriangleMeshGeometry getTriangle() + */ + PX_PHYSX_COMMON_API static PxU32 findOverlapTriangleMesh( const PxGeometry& geom, const PxTransform& geomPose, + const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose, + PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow); + + /** + \brief Find the height field triangles which touch the specified geometry object. + + Returned triangle indices can be used with #getTriangle() to retrieve the triangle properties. + + \param[in] geom The geometry object to test for height field overlaps. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry and #PxBoxGeometry. The sphere and capsule queries are currently conservative estimates. + \param[in] geomPose Pose of the geometry object + \param[in] hfGeom The height field geometry to check overlap against + \param[in] hfPose Pose of the height field + \param[out] results Indices of overlapping triangles + \param[in] maxResults Size of 'results' buffer + \param[in] startIndex Index of first result to be retrieved. Previous indices are skipped. + \param[out] overflow True if a buffer overflow occurred + \return Number of overlaps found, i.e. number of elements written to the results buffer + + @see PxHeightFieldGeometry getTriangle() + */ + PX_PHYSX_COMMON_API static PxU32 findOverlapHeightField(const PxGeometry& geom, const PxTransform& geomPose, + const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose, + PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow); + + + /** + \brief Sweep a specified geometry object in space and test for collision with a set of given triangles. + + This function simply sweeps input geometry against each input triangle, in the order they are given. + This is an O(N) operation with N = number of input triangles. It does not use any particular acceleration structure. + + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. Clamped to PX_MAX_SWEEP_DISTANCE. + \param[in] geom The geometry object to sweep. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry and #PxBoxGeometry + \param[in] pose Pose of the geometry object to sweep. + \param[in] triangleCount Number of specified triangles + \param[in] triangles Array of triangles to sweep against + \param[out] sweepHit The sweep hit information. See the notes below for limitations about returned results. + \param[in] hitFlags Specification of the kind of information to retrieve on hit. Combination of #PxHitFlag flags. See the notes below for limitations about supported flags. + \param[in] cachedIndex Cached triangle index for subsequent calls. Cached triangle is tested first. Optional parameter. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + \param[in] doubleSided Counterpart of PxMeshGeometryFlag::eDOUBLE_SIDED for input triangles. + \return True if the swept geometry object hits the specified triangles + + \note Only the following geometry types are currently supported: PxSphereGeometry, PxCapsuleGeometry, PxBoxGeometry + \note If a shape from the scene is already overlapping with the query shape in its starting position, the hit is returned unless eASSUME_NO_INITIAL_OVERLAP was specified. + \note This function returns a single closest hit across all the input triangles. Multiple hits are not supported. + \note Supported hitFlags are PxHitFlag::eDEFAULT, PxHitFlag::eASSUME_NO_INITIAL_OVERLAP, PxHitFlag::ePRECISE_SWEEP, PxHitFlag::eMESH_BOTH_SIDES, PxHitFlag::eMESH_ANY. + \note ePOSITION is only defined when there is no initial overlap (sweepHit.hadInitialOverlap() == false) + \note The returned normal for initially overlapping sweeps is set to -unitDir. + \note Otherwise the returned normal is the front normal of the triangle even if PxHitFlag::eMESH_BOTH_SIDES is set. + \note The returned PxSweepHit::faceIndex parameter will hold the index of the hit triangle in input array, i.e. the range is [0; triangleCount). For initially overlapping sweeps, this is the index of overlapping triangle. + \note The returned PxSweepHit::actor and PxSweepHit::shape pointers are not filled. + \note The inflation parameter is not compatible with PxHitFlag::ePRECISE_SWEEP. + + @see PxTriangle PxSweepHit PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static bool sweep(const PxVec3& unitDir, + const PxReal distance, + const PxGeometry& geom, + const PxTransform& pose, + PxU32 triangleCount, + const PxTriangle* triangles, + PxSweepHit& sweepHit, + PxHitFlags hitFlags = PxHitFlag::eDEFAULT, + const PxU32* cachedIndex = NULL, + const PxReal inflation = 0.0f, + bool doubleSided = false); +}; + + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxMeshScale.h b/src/PhysX/physx/include/geometry/PxMeshScale.h new file mode 100644 index 000000000..7771a7e0e --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxMeshScale.h @@ -0,0 +1,175 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_MESHSCALE +#define PX_PHYSICS_NX_MESHSCALE +/** \addtogroup geomutils +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxMat33.h" +#include "foundation/PxAssert.h" + +/** \brief Minimum allowed absolute magnitude for each of mesh scale's components (x,y,z). + \note Only positive scale values are allowed for convex meshes. */ +#define PX_MESH_SCALE_MIN 1e-6f + +/** \brief Maximum allowed absolute magnitude for each of mesh scale's components (x,y,z). + \note Only positive scale values are allowed for convex meshes. */ +#define PX_MESH_SCALE_MAX 1e6f + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief A class expressing a nonuniform scaling transformation. + +The scaling is along arbitrary axes that are specified by PxMeshScale::rotation. + +\note Negative scale values are supported for PxTriangleMeshGeometry + with absolute values for each component within [PX_MIN_ABS_MESH_SCALE, PX_MAX_ABS_MESH_SCALE] range. + Negative scale causes a reflection around the specified axis, in addition PhysX will flip the normals + for mesh triangles when scale.x*scale.y*scale.z < 0. +\note Only positive scale values are supported for PxConvexMeshGeometry + with values for each component within [PX_MIN_ABS_MESH_SCALE, PX_MAX_ABS_MESH_SCALE] range). + +@see PxConvexMeshGeometry PxTriangleMeshGeometry +*/ +class PxMeshScale +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief Constructor initializes to identity scale. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMeshScale(): scale(1.0f), rotation(PxIdentity) + { + } + + /** + \brief Constructor from scalar. + */ + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE PxMeshScale(PxReal r): scale(r), rotation(PxIdentity) + { + } + + /** + \brief Constructor to initialize to arbitrary scale and identity scale rotation. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMeshScale(const PxVec3& s) + { + scale = s; + rotation = PxQuat(PxIdentity); + } + + /** + \brief Constructor to initialize to arbitrary scaling. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMeshScale(const PxVec3& s, const PxQuat& r) + { + PX_ASSERT(r.isUnit()); + scale = s; + rotation = r; + } + + + /** + \brief Returns true if the scaling is an identity transformation. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isIdentity() const + { + return (scale.x == 1.0f && scale.y == 1.0f && scale.z == 1.0f); + } + + /** + \brief Returns the inverse of this scaling transformation. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMeshScale getInverse() const + { + return PxMeshScale(PxVec3(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z), rotation); + } + + /** + \brief Converts this transformation to a 3x3 matrix representation. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33 toMat33() const + { + PxMat33 rot(rotation); + PxMat33 trans = rot.getTranspose(); + trans.column0 *= scale[0]; + trans.column1 *= scale[1]; + trans.column2 *= scale[2]; + return trans * rot; + } + + /** + \brief Returns true if combination of negative scale components will cause the triangle normal to flip. The SDK will flip the normals internally. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool hasNegativeDeterminant() const + { + return (scale.x * scale.y * scale.z < 0.0f); + } + + PxVec3 transform(const PxVec3& v) const + { + return rotation.rotateInv(scale.multiply(rotation.rotate(v))); + } + + bool isValidForTriangleMesh() const + { + PxVec3 absXYZ = scale.abs(); + return (absXYZ.maxElement() <= PX_MESH_SCALE_MAX) && (absXYZ.minElement() >= PX_MESH_SCALE_MIN); + } + + bool isValidForConvexMesh() const + { + return (scale.maxElement() <= PX_MESH_SCALE_MAX) && (scale.minElement() >= PX_MESH_SCALE_MIN); + } + + PxVec3 scale; //!< A nonuniform scaling + PxQuat rotation; //!< The orientation of the scaling axes + + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxPlaneGeometry.h b/src/PhysX/physx/include/geometry/PxPlaneGeometry.h new file mode 100644 index 000000000..002c72715 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxPlaneGeometry.h @@ -0,0 +1,107 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_PLANE_GEOMETRY +#define PX_PHYSICS_NX_PLANE_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "foundation/PxPlane.h" +#include "foundation/PxTransform.h" +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Class describing a plane geometry. + +The plane geometry specifies the half-space volume x<=0. As with other geometry types, +when used in a PxShape the collision volume is obtained by transforming the halfspace +by the shape local pose and the actor global pose. + +To generate a PxPlane from a PxTransform, transform PxPlane(1,0,0,0). + +To generate a PxTransform from a PxPlane, use PxTransformFromPlaneEquation. + +@see PxShape.setGeometry() PxShape.getPlaneGeometry() PxTransformFromPlaneEquation +*/ +class PxPlaneGeometry : public PxGeometry +{ +public: + PX_INLINE PxPlaneGeometry() : PxGeometry(PxGeometryType::ePLANE) {} + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid + */ + PX_INLINE bool isValid() const; +}; + + +PX_INLINE bool PxPlaneGeometry::isValid() const +{ + if (mType != PxGeometryType::ePLANE) + return false; + + return true; +} + + +/** \brief creates a transform from a plane equation, suitable for an actor transform for a PxPlaneGeometry + +\param[in] plane the desired plane equation +\return a PxTransform which will transform the plane PxPlane(1,0,0,0) to the specified plane +*/ + +PX_FOUNDATION_API PxTransform PxTransformFromPlaneEquation(const PxPlane& plane); + +/** \brief creates a plane equation from a transform, such as the actor transform for a PxPlaneGeometry + +\param[in] transform the transform +\return the plane +*/ + + +PX_INLINE PxPlane PxPlaneEquationFromTransform(const PxTransform& transform) +{ + return transform.transform(PxPlane(1.f,0.f,0.f,0.f)); +} + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h b/src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h new file mode 100644 index 000000000..eeaea9858 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h @@ -0,0 +1,166 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_NX_SIMPLETRIANGLEMESH +#define PX_PHYSICS_GEOMUTILS_NX_SIMPLETRIANGLEMESH +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxFlags.h" +#include "common/PxCoreUtilityTypes.h" +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Enum with flag values to be used in PxSimpleTriangleMesh::flags. +*/ +struct PxMeshFlag +{ + enum Enum + { + /** + \brief Specifies if the SDK should flip normals. + + The PhysX libraries assume that the face normal of a triangle with vertices [a,b,c] can be computed as: + edge1 = b-a + edge2 = c-a + face_normal = edge1 x edge2. + + Note: This is the same as a counterclockwise winding in a right handed coordinate system or + alternatively a clockwise winding order in a left handed coordinate system. + + If this does not match the winding order for your triangles, raise the below flag. + */ + eFLIPNORMALS = (1<<0), + e16_BIT_INDICES = (1<<1) //!< Denotes the use of 16-bit vertex indices + }; +}; + +/** +\brief collection of set bits defined in PxMeshFlag. + +@see PxMeshFlag +*/ +typedef PxFlags PxMeshFlags; +PX_FLAGS_OPERATORS(PxMeshFlag::Enum,PxU16) + + +/** +\brief A structure describing a triangle mesh. +*/ +class PxSimpleTriangleMesh +{ +public: + + /** + \brief Pointer to first vertex point. + */ + PxBoundedData points; + + /** + \brief Pointer to first triangle. + + Caller may add triangleStrideBytes bytes to the pointer to access the next triangle. + + These are triplets of 0 based indices: + vert0 vert1 vert2 + vert0 vert1 vert2 + vert0 vert1 vert2 + ... + + where vertex is either a 32 or 16 bit unsigned integer. There are numTriangles*3 indices. + + This is declared as a void pointer because it is actually either an PxU16 or a PxU32 pointer. + */ + PxBoundedData triangles; + + /** + \brief Flags bits, combined from values of the enum ::PxMeshFlag + */ + PxMeshFlags flags; + + /** + \brief constructor sets to default. + */ + PX_INLINE PxSimpleTriangleMesh(); + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE void setToDefault(); + /** + \brief returns true if the current settings are valid + */ + PX_INLINE bool isValid() const; +}; + + +PX_INLINE PxSimpleTriangleMesh::PxSimpleTriangleMesh() +{ +} + +PX_INLINE void PxSimpleTriangleMesh::setToDefault() +{ + *this = PxSimpleTriangleMesh(); +} + +PX_INLINE bool PxSimpleTriangleMesh::isValid() const +{ + // Check geometry + if(points.count > 0xffff && flags & PxMeshFlag::e16_BIT_INDICES) + return false; + if(!points.data) + return false; + if(points.stride < sizeof(PxVec3)) //should be at least one point's worth of data + return false; + + // Check topology + // The triangles pointer is not mandatory + if(triangles.data) + { + // Indexed mesh + PxU32 limit = (flags & PxMeshFlag::e16_BIT_INDICES) ? sizeof(PxU16)*3 : sizeof(PxU32)*3; + if(triangles.stride < limit) + return false; + } + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxSphereGeometry.h b/src/PhysX/physx/include/geometry/PxSphereGeometry.h new file mode 100644 index 000000000..05ea69c76 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxSphereGeometry.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SPHERE_GEOMETRY +#define PX_PHYSICS_NX_SPHERE_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief A class representing the geometry of a sphere. + +Spheres are defined by their radius. +\note The scaling of the sphere is expected to be baked into this value, there is no additional scaling parameter. +*/ +class PxSphereGeometry : public PxGeometry +{ +public: + PX_INLINE PxSphereGeometry() : PxGeometry(PxGeometryType::eSPHERE), radius(0) {} + PX_INLINE PxSphereGeometry(PxReal ir) : PxGeometry(PxGeometryType::eSPHERE), radius(ir) {} + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid + + \note A valid sphere has radius > 0. + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a sphere that has zero radius. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + + /** + \brief The radius of the sphere. + */ + PxReal radius; +}; + + +PX_INLINE bool PxSphereGeometry::isValid() const +{ + if (mType != PxGeometryType::eSPHERE) + return false; + if (!PxIsFinite(radius)) + return false; + if (radius <= 0.0f) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxTriangle.h b/src/PhysX/physx/include/geometry/PxTriangle.h new file mode 100644 index 000000000..68d227464 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxTriangle.h @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_PX_TRIANGLE +#define PX_PHYSICS_GEOMUTILS_PX_TRIANGLE +/** \addtogroup geomutils + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Triangle class. +*/ +class PxTriangle +{ + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE PxTriangle() {} + + /** + \brief Constructor + + \param[in] p0 Point 0 + \param[in] p1 Point 1 + \param[in] p2 Point 2 + */ + PX_FORCE_INLINE PxTriangle(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) + { + verts[0] = p0; + verts[1] = p1; + verts[2] = p2; + } + + /** + \brief Copy constructor + + \param[in] triangle Tri to copy + */ + PX_FORCE_INLINE PxTriangle(const PxTriangle& triangle) + { + verts[0] = triangle.verts[0]; + verts[1] = triangle.verts[1]; + verts[2] = triangle.verts[2]; + } + + /** + \brief Destructor + */ + PX_FORCE_INLINE ~PxTriangle() {} + + /** + \brief Assignment operator + */ + PX_FORCE_INLINE void operator=(const PxTriangle& triangle) + { + verts[0] = triangle.verts[0]; + verts[1] = triangle.verts[1]; + verts[2] = triangle.verts[2]; + } + + /** + \brief Compute the normal of the Triangle. + + \param[out] _normal Triangle normal. + */ + PX_FORCE_INLINE void normal(PxVec3& _normal) const + { + _normal = (verts[1]-verts[0]).cross(verts[2]-verts[0]); + _normal.normalize(); + } + + /** + \brief Compute the unnormalized normal of the triangle. + + \param[out] _normal Triangle normal (not normalized). + */ + PX_FORCE_INLINE void denormalizedNormal(PxVec3& _normal) const + { + _normal = (verts[1]-verts[0]).cross(verts[2]-verts[0]); + } + + /** + \brief Compute the area of the triangle. + + \return Area of the triangle. + */ + PX_FORCE_INLINE PxReal area() const + { + const PxVec3& p0 = verts[0]; + const PxVec3& p1 = verts[1]; + const PxVec3& p2 = verts[2]; + return ((p0 - p1).cross(p0 - p2)).magnitude() * 0.5f; + } + + /** + \return Computes a point on the triangle from u and v barycentric coordinates. + */ + PxVec3 pointFromUV(PxReal u, PxReal v) const { return (1.0f-u-v)*verts[0] + u*verts[1] + v*verts[2]; } + + /** + \brief Array of Vertices. + */ + PxVec3 verts[3]; + +}; + + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxTriangleMesh.h b/src/PhysX/physx/include/geometry/PxTriangleMesh.h new file mode 100644 index 000000000..e6fc1582e --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxTriangleMesh.h @@ -0,0 +1,317 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_NX_TRIANGLEMESH +#define PX_PHYSICS_GEOMUTILS_NX_TRIANGLEMESH +/** \addtogroup geomutils +@{ */ + +#include "foundation/PxVec3.h" +#include "foundation/PxBounds3.h" +#include "common/PxPhysXCommonConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Enables the dynamic rtree mesh feature. It is recommended to use this feature for scene queries only. +@see PxTriangleMesh::getVerticesForModification +@see PxTriangleMesh::refitBVH +*/ +#define PX_ENABLE_DYNAMIC_MESH_RTREE 1 + +/** +\brief Mesh midphase structure. This enum is used to select the desired acceleration structure for midphase queries + (i.e. raycasts, overlaps, sweeps vs triangle meshes). + + The PxMeshMidPhase::eBVH33 structure is the one used in recent PhysX versions (up to PhysX 3.3). It has great performance and is + supported on all platforms. + + The PxMeshMidPhase::eBVH34 structure is a revisited implementation introduced in PhysX 3.4. It can be significantly faster both + in terms of cooking performance and runtime performance, but it is currently only available on platforms supporting the + SSE2 instuction set. +*/ +struct PxMeshMidPhase +{ + enum Enum + { + eBVH33 = 0, //!< Default midphase mesh structure, as used up to PhysX 3.3 + eBVH34 = 1, //!< New midphase mesh structure, introduced in PhysX 3.4 + + eLAST + }; +}; + +/** +\brief Flags for the mesh geometry properties. + +Used in ::PxTriangleMeshFlags. +*/ +struct PxTriangleMeshFlag +{ + enum Enum + { + e16_BIT_INDICES = (1<<1), //!< The triangle mesh has 16bits vertex indices. + eADJACENCY_INFO = (1<<2) //!< The triangle mesh has adjacency information build. + }; +}; + +/** +\brief collection of set bits defined in PxTriangleMeshFlag. + +@see PxTriangleMeshFlag +*/ +typedef PxFlags PxTriangleMeshFlags; +PX_FLAGS_OPERATORS(PxTriangleMeshFlag::Enum,PxU8) + +/** + +\brief A triangle mesh, also called a 'polygon soup'. + +It is represented as an indexed triangle list. There are no restrictions on the +triangle data. + +To avoid duplicating data when you have several instances of a particular +mesh positioned differently, you do not use this class to represent a +mesh object directly. Instead, you create an instance of this mesh via +the PxTriangleMeshGeometry and PxShape classes. + +

Creation

+ +To create an instance of this class call PxPhysics::createTriangleMesh(), +and release() to delete it. This is only possible +once you have released all of its PxShape instances. + + +

Visualizations:

+\li #PxVisualizationParameter::eCOLLISION_AABBS +\li #PxVisualizationParameter::eCOLLISION_SHAPES +\li #PxVisualizationParameter::eCOLLISION_AXES +\li #PxVisualizationParameter::eCOLLISION_FNORMALS +\li #PxVisualizationParameter::eCOLLISION_EDGES + +@see PxTriangleMeshDesc PxTriangleMeshGeometry PxShape PxPhysics.createTriangleMesh() +*/ + +class PxTriangleMesh : public PxBase +{ + public: + /** + \brief Returns the number of vertices. + \return number of vertices + @see getVertices() + */ + virtual PxU32 getNbVertices() const = 0; + + /** + \brief Returns the vertices. + \return array of vertices + @see getNbVertices() + */ + virtual const PxVec3* getVertices() const = 0; + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + /** + \brief Returns all mesh vertices for modification. + + This function will return the vertices of the mesh so that their positions can be changed in place. + After modifying the vertices you must call refitBVH for the refitting to actually take place. + This function maintains the old mesh topology (triangle indices). + + \return inplace vertex coordinates for each existing mesh vertex. + + \note works only for PxMeshMidPhase::eBVH33 + \note Size of array returned is equal to the number returned by getNbVertices(). + \note This function operates on cooked vertex indices. + \note This means the index mapping and vertex count can be different from what was provided as an input to the cooking routine. + \note To achieve unchanged 1-to-1 index mapping with orignal mesh data (before cooking) please use the following cooking flags: + \note eWELD_VERTICES = 0, eDISABLE_CLEAN_MESH = 1. + \note It is also recommended to make sure that a call to validateTriangleMesh returns true if mesh cleaning is disabled. + @see getNbVertices() + @see refitBVH() + */ + virtual PxVec3* getVerticesForModification() = 0; + + /** + \brief Refits BVH for mesh vertices. + + This function will refit the mesh BVH to correctly enclose the new positions updated by getVerticesForModification. + Mesh BVH will not be reoptimized by this function so significantly different new positions will cause significantly reduced performance. + + \return New bounds for the entire mesh. + + \note works only for PxMeshMidPhase::eBVH33 + \note PhysX does not keep a mapping from the mesh to mesh shapes that reference it. + \note Call PxShape::setGeometry on each shape which references the mesh, to ensure that internal data structures are updated to reflect the new geometry. + \note PxShape::setGeometry does not guarantee correct/continuous behavior when objects are resting on top of old or new geometry. + \note It is also recommended to make sure that a call to validateTriangleMesh returns true if mesh cleaning is disabled. + \note Active edges information will be lost during refit, the rigid body mesh contact generation might not perform as expected. + @see getNbVertices() + @see getVerticesForModification() + */ + virtual PxBounds3 refitBVH() = 0; +#endif // PX_ENABLE_DYNAMIC_MESH_RTREE + + /** + \brief Returns the number of triangles. + \return number of triangles + @see getTriangles() getTrianglesRemap() + */ + virtual PxU32 getNbTriangles() const = 0; + + /** + \brief Returns the triangle indices. + + The indices can be 16 or 32bit depending on the number of triangles in the mesh. + Call getTriangleMeshFlags() to know if the indices are 16 or 32 bits. + + The number of indices is the number of triangles * 3. + + \return array of triangles + @see getNbTriangles() getTriangleMeshFlags() getTrianglesRemap() + */ + virtual const void* getTriangles() const = 0; + + /** + \brief Reads the PxTriangleMesh flags. + + See the list of flags #PxTriangleMeshFlag + + \return The values of the PxTriangleMesh flags. + + @see PxTriangleMesh + */ + virtual PxTriangleMeshFlags getTriangleMeshFlags() const = 0; + + /** + \brief Returns the triangle remapping table. + + The triangles are internally sorted according to various criteria. Hence the internal triangle order + does not always match the original (user-defined) order. The remapping table helps finding the old + indices knowing the new ones: + + remapTable[ internalTriangleIndex ] = originalTriangleIndex + + \return the remapping table (or NULL if 'PxCookingParams::suppressTriangleMeshRemapTable' has been used) + @see getNbTriangles() getTriangles() PxCookingParams::suppressTriangleMeshRemapTable + */ + virtual const PxU32* getTrianglesRemap() const = 0; + + + /** + \brief Decrements the reference count of a triangle mesh and releases it if the new reference count is zero. + + @see PxPhysics.createTriangleMesh() + */ + virtual void release() = 0; + + /** + \brief Returns material table index of given triangle + + This function takes a post cooking triangle index. + + \param[in] triangleIndex (internal) index of desired triangle + \return Material table index, or 0xffff if no per-triangle materials are used + */ + virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const = 0; + + /** + \brief Returns the local-space (vertex space) AABB from the triangle mesh. + + \return local-space bounds + */ + virtual PxBounds3 getLocalBounds() const = 0; + + /** + \brief Returns the reference count for shared meshes. + + At creation, the reference count of the mesh is 1. Every shape referencing this mesh increments the + count by 1. When the reference count reaches 0, and only then, the mesh gets destroyed automatically. + + \return the current reference count. + */ + virtual PxU32 getReferenceCount() const = 0; + + /** + \brief Acquires a counted reference to a triangle mesh. + + This method increases the reference count of the triangle mesh by 1. Decrement the reference count by calling release() + */ + virtual void acquireReference() = 0; + +protected: + PX_INLINE PxTriangleMesh(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxTriangleMesh(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxTriangleMesh() {} + + virtual bool isKindOf(const char* name) const { return !::strcmp("PxTriangleMesh", name) || PxBase::isKindOf(name); } +}; + +/** + +\brief A triangle mesh containing the PxMeshMidPhase::eBVH33 structure. + +@see PxMeshMidPhase +*/ +class PxBVH33TriangleMesh : public PxTriangleMesh +{ + public: +protected: + PX_INLINE PxBVH33TriangleMesh(PxType concreteType, PxBaseFlags baseFlags) : PxTriangleMesh(concreteType, baseFlags) {} + PX_INLINE PxBVH33TriangleMesh(PxBaseFlags baseFlags) : PxTriangleMesh(baseFlags) {} + virtual ~PxBVH33TriangleMesh() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxBVH33TriangleMesh", name) || PxTriangleMesh::isKindOf(name); } +}; + +/** + +\brief A triangle mesh containing the PxMeshMidPhase::eBVH34 structure. + +@see PxMeshMidPhase +*/ +class PxBVH34TriangleMesh : public PxTriangleMesh +{ + public: +protected: + PX_INLINE PxBVH34TriangleMesh(PxType concreteType, PxBaseFlags baseFlags) : PxTriangleMesh(concreteType, baseFlags) {} + PX_INLINE PxBVH34TriangleMesh(PxBaseFlags baseFlags) : PxTriangleMesh(baseFlags) {} + virtual ~PxBVH34TriangleMesh() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxBVH34TriangleMesh", name) || PxTriangleMesh::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h b/src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h new file mode 100644 index 000000000..f2a8bdbd8 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h @@ -0,0 +1,150 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_TRIANGLEMESH_GEOMETRY +#define PX_PHYSICS_NX_TRIANGLEMESH_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxGeometry.h" +#include "geometry/PxMeshScale.h" +#include "common/PxCoreUtilityTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxTriangleMesh; + + +/** +\brief Flags controlling the simulated behavior of the triangle mesh geometry. + +Used in ::PxMeshGeometryFlags. +*/ +struct PxMeshGeometryFlag +{ + enum Enum + { + eDOUBLE_SIDED = (1<<1) //!< Meshes with this flag set are treated as double-sided. + //!< This flag is currently only used for raycasts and sweeps (it is ignored for overlap queries). + //!< For detailed specifications of this flag for meshes and heightfields please refer to the Geometry Query section of the user guide. + }; +}; + +/** +\brief collection of set bits defined in PxMeshGeometryFlag. + +@see PxMeshGeometryFlag +*/ +typedef PxFlags PxMeshGeometryFlags; +PX_FLAGS_OPERATORS(PxMeshGeometryFlag::Enum,PxU8) + +/** +\brief Triangle mesh geometry class. + +This class unifies a mesh object with a scaling transform, and +lets the combined object be used anywhere a PxGeometry is needed. + +The scaling is a transform along arbitrary axes contained in the scale object. +The vertices of the mesh in geometry (or shape) space is the +PxMeshScale::toMat33() transform, multiplied by the vertex space vertices +in the PxConvexMesh object. +*/ +class PxTriangleMeshGeometry : public PxGeometry +{ +public: + /** + \brief Default constructor. + + Creates an empty object with a NULL mesh and identity scale. + */ + PX_INLINE PxTriangleMeshGeometry() : + PxGeometry (PxGeometryType::eTRIANGLEMESH), + triangleMesh(NULL) + {} + + /** + \brief Constructor. + \param[in] mesh Mesh pointer. May be NULL, though this will not make the object valid for shape construction. + \param[in] scaling Scale factor. + \param[in] flags Mesh flags. + \ + */ + PX_INLINE PxTriangleMeshGeometry( PxTriangleMesh* mesh, + const PxMeshScale& scaling = PxMeshScale(), + PxMeshGeometryFlags flags = PxMeshGeometryFlags()) : + PxGeometry (PxGeometryType::eTRIANGLEMESH), + scale (scaling), + meshFlags (flags), + triangleMesh(mesh) + {} + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid for shape creation. + + \note A valid triangle mesh has a positive scale value in each direction (scale.scale.x > 0, scale.scale.y > 0, scale.scale.z > 0). + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a triangle mesh that has zero extents in any direction. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + PxMeshScale scale; //!< The scaling transformation. + PxMeshGeometryFlags meshFlags; //!< Mesh flags. + PxPadding<3> paddingFromFlags; //!< padding for mesh flags + PxTriangleMesh* triangleMesh; //!< A reference to the mesh object. +}; + + +PX_INLINE bool PxTriangleMeshGeometry::isValid() const +{ + if(mType != PxGeometryType::eTRIANGLEMESH) + return false; + if(!scale.scale.isFinite() || !scale.rotation.isUnit()) + return false; + if(!scale.isValidForTriangleMesh()) + return false; + if(!triangleMesh) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geomutils/GuContactBuffer.h b/src/PhysX/physx/include/geomutils/GuContactBuffer.h new file mode 100644 index 000000000..06568fe62 --- /dev/null +++ b/src/PhysX/physx/include/geomutils/GuContactBuffer.h @@ -0,0 +1,131 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONTACTBUFFER_H +#define GU_CONTACTBUFFER_H + +#include "PxPhysXConfig.h" +#include "PxContact.h" +#include "GuContactPoint.h" + +namespace physx +{ +namespace Gu +{ + + struct NarrowPhaseParams + { + PX_FORCE_INLINE NarrowPhaseParams(PxReal contactDistance, PxReal meshContactMargin, PxReal toleranceLength) : + mContactDistance(contactDistance), + mMeshContactMargin(meshContactMargin), + mToleranceLength(toleranceLength) {} + + PxReal mContactDistance; + PxReal mMeshContactMargin; // PT: Margin used to generate mesh contacts. Temp & unclear, should be removed once GJK is default path. + PxReal mToleranceLength; // PT: copy of PxTolerancesScale::length + }; + +//sizeof(SavedContactData)/sizeof(PxU32) = 17, 1088/17 = 64 triangles in the local array +#define LOCAL_CONTACTS_SIZE 1088 + +class ContactBuffer +{ +public: + + static const PxU32 MAX_CONTACTS = 64; + + Gu::ContactPoint contacts[MAX_CONTACTS]; + PxU32 count; + PxU32 pad; + + PX_FORCE_INLINE void reset() + { + count = 0; + } + + PX_FORCE_INLINE bool contact(const PxVec3& worldPoint, + const PxVec3& worldNormalIn, + PxReal separation, + PxU32 faceIndex1 = PXC_CONTACT_NO_FACE_INDEX + ) + { + PX_ASSERT(PxAbs(worldNormalIn.magnitude()-1)<1e-3f); + + if(count>=MAX_CONTACTS) + return false; + + Gu::ContactPoint& p = contacts[count++]; + p.normal = worldNormalIn; + p.point = worldPoint; + p.separation = separation; + p.internalFaceIndex1= faceIndex1; + return true; + } + + PX_FORCE_INLINE bool contact(const PxVec3& worldPoint, + const PxVec3& worldNormalIn, + PxReal separation, + PxU16 internalUsage, + PxU32 faceIndex1 = PXC_CONTACT_NO_FACE_INDEX + ) + { + PX_ASSERT(PxAbs(worldNormalIn.magnitude() - 1)<1e-3f); + + if (count >= MAX_CONTACTS) + return false; + + Gu::ContactPoint& p = contacts[count++]; + p.normal = worldNormalIn; + p.point = worldPoint; + p.separation = separation; + p.internalFaceIndex1 = faceIndex1; + p.forInternalUse = internalUsage; + return true; + } + + PX_FORCE_INLINE bool contact(const Gu::ContactPoint & pt) + { + if(count>=MAX_CONTACTS) + return false; + contacts[count++] = pt; + return true; + } + + PX_FORCE_INLINE Gu::ContactPoint* contact() + { + if(count>=MAX_CONTACTS) + return NULL; + return &contacts[count++]; + } +}; + +} +} + +#endif diff --git a/src/PhysX/physx/include/geomutils/GuContactPoint.h b/src/PhysX/physx/include/geomutils/GuContactPoint.h new file mode 100644 index 000000000..5f2bcb623 --- /dev/null +++ b/src/PhysX/physx/include/geomutils/GuContactPoint.h @@ -0,0 +1,109 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONTACT_POINT_H +#define GU_CONTACT_POINT_H + +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" + +namespace physx +{ +namespace Gu +{ + +struct ContactPoint +{ + /** + \brief The normal of the contacting surfaces at the contact point. + + For two shapes s0 and s1, the normal points in the direction that s0 needs to move in to resolve the contact with s1. + */ + PX_ALIGN(16, PxVec3 normal); + /** + \brief The separation of the shapes at the contact point. A negative separation denotes a penetration. + */ + PxReal separation; + + /** + \brief The point of contact between the shapes, in world space. + */ + PX_ALIGN(16, PxVec3 point); + + /** + \brief The max impulse permitted at this point + */ + PxReal maxImpulse; + + PX_ALIGN(16, PxVec3 targetVel); + + /** + \brief The static friction coefficient + */ + PxReal staticFriction; + + /** + \brief Material flags for this contact (eDISABLE_FRICTION, eDISABLE_STRONG_FRICTION). @see PxMaterialFlag + */ + PxU8 materialFlags; + + /** + \brief internal structure used for internal use only + */ + PxU16 forInternalUse; + + /** + \brief The surface index of shape 1 at the contact point. This is used to identify the surface material. + + \note This field is only supported by triangle meshes and heightfields, else it will be set to PXC_CONTACT_NO_FACE_INDEX. + \note This value must be directly after internalFaceIndex0 in memory + */ + + PxU32 internalFaceIndex1; + + /** + \brief The dynamic friction coefficient + */ + PxReal dynamicFriction; + /** + \brief The restitution coefficient + */ + PxReal restitution; + +}; + +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/gpu/PxGpu.h b/src/PhysX/physx/include/gpu/PxGpu.h new file mode 100644 index 000000000..5f495bcaa --- /dev/null +++ b/src/PhysX/physx/include/gpu/PxGpu.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PX_GPU_H +#define PX_GPU_H + +#include "PxPhysXConfig.h" + + +#if PX_SUPPORT_GPU_PHYSX + +#include "cudamanager/PxCudaContextManager.h" +#include "cudamanager/PxCudaMemoryManager.h" +#include "cudamanager/PxGpuCopyDesc.h" +#include "foundation/Px.h" +#include "foundation/PxPreprocessor.h" +#include "common/PxPhysXCommonConfig.h" +#include "PxFoundation.h" + +/** +\brief PxGpuLoadHook + +This is a helper class for loading the PhysXGpu dll. +If a PhysXGpu dll with a non-default file name needs to be loaded, +PxGpuLoadHook can be sub-classed to provide the custom filenames. + +Once the names are set, the instance must be set for use by PhysX.dll using PxSetPhysXGpuLoadHook(), + +@see PxSetPhysXGpuLoadHook() +*/ +class PxGpuLoadHook +{ +public: + PxGpuLoadHook() {} + virtual ~PxGpuLoadHook() {} + + virtual const char* getPhysXGpuDllName() const = 0; + +protected: +private: +}; + +/** +\brief Sets GPU load hook instance for PhysX dll. + +\param[in] hook GPU load hook. + +@see PxGpuLoadHook +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxSetPhysXGpuLoadHook(const PxGpuLoadHook* hook); + +/** + * \brief Ask the NVIDIA control panel which GPU has been selected for use by + * PhysX. Returns -1 if no PhysX capable GPU is found or GPU PhysX has + * been disabled. + */ +PX_C_EXPORT PX_PHYSX_CORE_API int PX_CALL_CONV PxGetSuggestedCudaDeviceOrdinal(physx::PxErrorCallback& errc); + +/** + * \brief Allocate a CUDA Context manager, complete with heaps and task dispatcher. + * You only need one CUDA context manager per GPU device you intend to use for + * CUDA tasks. + */ +PX_C_EXPORT PX_PHYSX_CORE_API physx::PxCudaContextManager* PX_CALL_CONV PxCreateCudaContextManager(physx::PxFoundation& foundation, const physx::PxCudaContextManagerDesc& desc); + +#endif // PX_SUPPORT_GPU_PHYSX + +#endif // PX_GPU_H diff --git a/src/PhysX/physx/include/pvd/PxPvd.h b/src/PhysX/physx/include/pvd/PxPvd.h new file mode 100644 index 000000000..df4cbc63d --- /dev/null +++ b/src/PhysX/physx/include/pvd/PxPvd.h @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVD_H +#define PXPVDSDK_PXPVD_H + +/** \addtogroup pvd +@{ +*/ +#include "foundation/PxFlags.h" +#include "foundation/PxProfiler.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxFoundation; +class PxPvdTransport; + +/** +\brief types of instrumentation that PVD can do. +*/ +struct PxPvdInstrumentationFlag +{ + enum Enum + { + /** + \brief Send debugging information to PVD. + + This information is the actual object data of the rigid statics, shapes, + articulations, etc. Sending this information has a noticeable impact on + performance and thus this flag should not be set if you want an accurate + performance profile. + */ + eDEBUG = 1 << 0, + + /** + \brief Send profile information to PVD. + + This information populates PVD's profile view. It has (at this time) negligible + cost compared to Debug information and makes PVD *much* more useful so it is quite + highly recommended. + + This flag works together with a PxCreatePhysics parameter. + Using it allows the SDK to send profile events to PVD. + */ + ePROFILE = 1 << 1, + + /** + \brief Send memory information to PVD. + + The PVD sdk side hooks into the Foundation memory controller and listens to + allocation/deallocation events. This has a noticable hit on the first frame, + however, this data is somewhat compressed and the PhysX SDK doesn't allocate much + once it hits a steady state. This information also has a fairly negligible + impact and thus is also highly recommended. + + This flag works together with a PxCreatePhysics parameter, + trackOutstandingAllocations. Using both of them together allows users to have + an accurate view of the overall memory usage of the simulation at the cost of + a hashtable lookup per allocation/deallocation. Again, PhysX makes a best effort + attempt not to allocate or deallocate during simulation so this hashtable lookup + tends to have no effect past the first frame. + + Sending memory information without tracking outstanding allocations means that + PVD will accurate information about the state of the memory system before the + actual connection happened. + */ + eMEMORY = 1 << 2, + + eALL = (eDEBUG | ePROFILE | eMEMORY) + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxPvdInstrumentationFlag. + +@see PxPvdInstrumentationFlag +*/ +typedef PxFlags PxPvdInstrumentationFlags; +PX_FLAGS_OPERATORS(PxPvdInstrumentationFlag::Enum, uint8_t) + +/** +\brief PxPvd is the top-level class for the PVD framework, and the main customer interface for PVD +configuration.It is a singleton class, instantiated and owned by the application. +*/ +class PxPvd : public physx::PxProfilerCallback +{ + public: + /** + Connects the SDK to the PhysX Visual Debugger application. + \param transport transport for pvd captured data. + \param flags Flags to set. + return True if success + */ + virtual bool connect(PxPvdTransport& transport, PxPvdInstrumentationFlags flags) = 0; + + /** + Disconnects the SDK from the PhysX Visual Debugger application. + If we are still connected, this will kill the entire debugger connection. + */ + virtual void disconnect() = 0; + + /** + * Return if connection to PVD is created. + \param useCachedStatus + 1> When useCachedStaus is false, isConnected() checks the lowlevel network status. + This can be slow because it needs to lock the lowlevel network stream. If isConnected() is + called frequently, the expense of locking can be significant. + 2> When useCachedStatus is true, isConnected() checks the highlevel cached status with atomic access. + It is faster than locking, but the status may be different from the lowlevel network with latency of up to + one frame. + The reason for this is that the cached status is changed inside socket listener, which is not + called immediately when the lowlevel connection status changes. + */ + virtual bool isConnected(bool useCachedStatus = true) = 0; + + /** + returns the PVD data transport + returns NULL if no transport is present. + */ + virtual PxPvdTransport* getTransport() = 0; + + /** + Retrieves the PVD flags. See PxPvdInstrumentationFlags. + */ + virtual PxPvdInstrumentationFlags getInstrumentationFlags() = 0; + + /** + \brief Releases the pvd instance. + */ + virtual void release() = 0; + + protected: + virtual ~PxPvd() + { + } +}; + +/** + \brief Create a pvd instance. + \param foundation is the foundation instance that stores the allocator and error callbacks. +*/ +PX_C_EXPORT PxPvd* PX_CALL_CONV PxCreatePvd(PxFoundation& foundation); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVD_H diff --git a/src/PhysX/physx/include/pvd/PxPvdSceneClient.h b/src/PhysX/physx/include/pvd/PxPvdSceneClient.h new file mode 100644 index 000000000..86c5eb040 --- /dev/null +++ b/src/PhysX/physx/include/pvd/PxPvdSceneClient.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PVD_SCENE_CLIENT_H +#define PX_PVD_SCENE_CLIENT_H + +/** \addtogroup pvd +@{ +*/ + +#include "foundation/PxFlags.h" + +namespace physx +{ + namespace pvdsdk + { + class PvdClient; + struct PvdDebugPoint; + struct PvdDebugLine; + struct PvdDebugTriangle; + struct PvdDebugText; + } +} + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief PVD scene Flags. They are disabled by default, and only works if PxPvdInstrumentationFlag::eDEBUG is set. +*/ +struct PxPvdSceneFlag +{ + enum Enum + { + eTRANSMIT_CONTACTS = (1 << 0), //! Transmits contact stream to PVD. + eTRANSMIT_SCENEQUERIES = (1 << 1), //! Transmits scene query stream to PVD. + eTRANSMIT_CONSTRAINTS = (1 << 2) //! Transmits constraints visualize stream to PVD. + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxPvdSceneFlag. + +@see PxPvdSceneFlag +*/ +typedef PxFlags PxPvdSceneFlags; +PX_FLAGS_OPERATORS(PxPvdSceneFlag::Enum, PxU8) + +/** +\brief Special client for PxScene. +It provides access to the PxPvdSceneFlag. +It also provides simple user debug services that associated scene position such as immediate rendering and camera updates. +*/ +class PxPvdSceneClient +{ + public: + /** + Sets the PVD flag. See PxPvdSceneFlag. + \param flag Flag to set. + \param value value the flag gets set to. + */ + virtual void setScenePvdFlag(PxPvdSceneFlag::Enum flag, bool value) = 0; + + /** + Sets the PVD flags. See PxPvdSceneFlags. + \param flags Flags to set. + */ + virtual void setScenePvdFlags(PxPvdSceneFlags flags) = 0; + + /** + Retrieves the PVD flags. See PxPvdSceneFlags. + */ + virtual PxPvdSceneFlags getScenePvdFlags() const = 0; + + /** + update camera on PVD application's render window + */ + virtual void updateCamera(const char* name, const PxVec3& origin, const PxVec3& up, const PxVec3& target) = 0; + + /** + draw points on PVD application's render window + */ + virtual void drawPoints(const physx::pvdsdk::PvdDebugPoint* points, PxU32 count) = 0; + + /** + draw lines on PVD application's render window + */ + virtual void drawLines(const physx::pvdsdk::PvdDebugLine* lines, PxU32 count) = 0; + + /** + draw triangles on PVD application's render window + */ + virtual void drawTriangles(const physx::pvdsdk::PvdDebugTriangle* triangles, PxU32 count) = 0; + + /** + draw text on PVD application's render window + */ + virtual void drawText(const physx::pvdsdk::PvdDebugText& text) = 0; + + /** + get the underlying client, for advanced users + */ + virtual physx::pvdsdk::PvdClient* getClientInternal() = 0; + +protected: + virtual ~PxPvdSceneClient(){} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PX_PVD_SCENE_CLIENT_H diff --git a/src/PhysX/physx/include/pvd/PxPvdTransport.h b/src/PhysX/physx/include/pvd/PxPvdTransport.h new file mode 100644 index 000000000..098fbb5c1 --- /dev/null +++ b/src/PhysX/physx/include/pvd/PxPvdTransport.h @@ -0,0 +1,129 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDTRANSPORT_H +#define PXPVDSDK_PXPVDTRANSPORT_H + +/** \addtogroup pvd +@{ +*/ +#include "foundation/PxErrors.h" +#include "foundation/PxFlags.h" +#include "pvd/PxPvd.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief PxPvdTransport is an interface representing the data transport mechanism. +This class defines all services associated with the transport: configuration, connection, reading, writing etc. +It is owned by the application, and can be realized as a file or a socket (using one-line PxDefault<...> methods in +PhysXExtensions) or in a custom implementation. This is a class that is intended for use by PVD, not by the +application, the application entry points are PxPvd and PvdClient. +*/ + +class PxPvdTransport +{ + public: + // connect, isConnected, disconnect, read, write, flush + + /** + Connects to the Visual Debugger application. + return True if success + */ + virtual bool connect() = 0; + + /** + Disconnects from the Visual Debugger application. + If we are still connected, this will kill the entire debugger connection. + */ + virtual void disconnect() = 0; + + /** + * Return if connection to PVD is created. + */ + virtual bool isConnected() = 0; + + /** + * write bytes to the other endpoint of the connection. should lock before witre. If an error occurs + * this connection will assume to be dead. + */ + virtual bool write(const uint8_t* inBytes, uint32_t inLength) = 0; + + /* + lock this transport and return it + */ + virtual PxPvdTransport& lock() = 0; + + /* + unlock this transport + */ + virtual void unlock() = 0; + + /** + * send any data and block until we know it is at least on the wire. + */ + virtual void flush() = 0; + + /** + * Return size of written data. + */ + virtual uint64_t getWrittenDataSize() = 0; + + virtual void release() = 0; + + protected: + virtual ~PxPvdTransport() + { + } +}; + +/** + \brief Create a default socket transport. + \param host host address of the pvd application. + \param port ip port used for pvd, should same as the port setting in pvd application. + \param timeoutInMilliseconds timeout when connect to pvd host. +*/ +PX_C_EXPORT PxPvdTransport* PX_CALL_CONV +PxDefaultPvdSocketTransportCreate(const char* host, int port, unsigned int timeoutInMilliseconds); + +/** + \brief Create a default file transport. + \param name full path filename used save captured pvd data, or NULL for a fake/test file transport. +*/ +PX_C_EXPORT PxPvdTransport* PX_CALL_CONV PxDefaultPvdFileTransportCreate(const char* name); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDTRANSPORT_H diff --git a/src/PhysX/physx/include/solver/PxSolverDefs.h b/src/PhysX/physx/include/solver/PxSolverDefs.h new file mode 100644 index 000000000..6fad51007 --- /dev/null +++ b/src/PhysX/physx/include/solver/PxSolverDefs.h @@ -0,0 +1,256 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_SOLVER_DEFS_H +#define PX_SOLVER_DEFS_H + + +#include "PxPhysXConfig.h" +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "foundation/PxTransform.h" +#include "PxConstraintDesc.h" +#include "geomutils/GuContactPoint.h" + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4324) // structure was padded due to alignment +#endif + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +namespace Dy +{ + //struct FsData; + class ArticulationV; +} + +namespace Sc +{ + class ShapeInteraction; +} + +struct PxSolverBody +{ + PX_ALIGN(16, PxVec3) linearVelocity; //!< Delta linear velocity computed by the solver + PxU16 maxSolverNormalProgress; //!< Progress counter used by constraint batching and parallel island solver. + PxU16 maxSolverFrictionProgress; //!< Progress counter used by constraint batching and parallel island solver. + + PxVec3 angularState; //!< Delta angular velocity state computed by the solver. + + PxU32 solverProgress; //!< Progress counter used by constraint batching and parallel island solver + + PxSolverBody() : linearVelocity(0.f), maxSolverNormalProgress(0), maxSolverFrictionProgress(0), angularState(0), solverProgress(0) + { + } +}; + +PX_COMPILE_TIME_ASSERT(sizeof(PxSolverBody) == 32); + +struct PxSolverBodyData +{ + PX_ALIGN(16, PxVec3 linearVelocity); //!< 12 Pre-solver linear velocity + PxReal invMass; //!< 16 inverse mass + PxVec3 angularVelocity; //!< 28 Pre-solver angular velocity + PxReal reportThreshold; //!< 32 contact force threshold + PxMat33 sqrtInvInertia; //!< 68 inverse inertia in world space + PxReal penBiasClamp; //!< 72 the penetration bias clamp + PxU32 nodeIndex; //!< 76 the node idx of this solverBodyData. Used by solver to reference between solver bodies and island bodies. Not required by immediate mode + PxReal maxContactImpulse; //!< 80 the max contact impulse + PxTransform body2World; //!< 108 the body's transform + PxU16 lockFlags; //!< 110 lock flags + PxU16 pad; //!< 112 pad + + PX_FORCE_INLINE PxReal projectVelocity(const PxVec3& lin, const PxVec3& ang) const + { + return linearVelocity.dot(lin) + angularVelocity.dot(ang); + } +}; + +//---------------------------------- +/* +* A header that defines the size of a specific batch of constraints (of same type and without dependencies) +*/ +struct PxConstraintBatchHeader +{ + PxU32 mStartIndex; //!< Start index for this batch + PxU16 mStride; //!< Number of constraints in this batch (range: 1-4) + PxU16 mConstraintType; //!< The type of constraint this batch references +}; + + +struct PxSolverConstraintDesc +{ + static const PxU16 NO_LINK = 0xffff; + + enum ConstraintType + { + eCONTACT_CONSTRAINT, //!< Defines this pair is a contact constraint + eJOINT_CONSTRAINT //!< Defines this pair is a joint constraint + }; + + union + { + PxSolverBody* bodyA; //!< bodyA pointer + Dy::ArticulationV* articulationA; //!< Articulation pointer for body A + }; + + union + { + PxSolverBody* bodyB; //!< BodyB pointer + Dy::ArticulationV* articulationB; //!< Articulation pointer for body B + }; + PxU16 linkIndexA; //!< Link index defining which link in Articulation A this constraint affects. If not an articulation, must be NO_LINK + PxU16 linkIndexB; //!< Link index defining which link in Articulation B this constraint affects. If not an articulation, must be NO_LINK. + union + { + PxU16 articulationALength; //!< The total length of articulation A in multiples of 16 bytes + PxU32 bodyADataIndex; //!< Body A's index into the SolverBodyData array + }; + + union + { + PxU16 articulationBLength; //!< The total lengh of articulation B in multiples of 16 bytes. + PxU32 bodyBDataIndex; //!< Body B's index into the SolverBodyData array + }; + + PxU16 writeBackLengthOver4; //!< writeBackLength/4, max writeback length is 256K, allows PxSolverConstraintDesc to fit in 32 bytes + PxU16 constraintLengthOver16; //!< constraintLength/16, max constraint length is 1MB, allows PxSolverConstraintDesc to fit in 32 bytes + + PxU8* constraint; //!< Pointer to the constraint rows to be solved + void* writeBack; //!< Pointer to the writeback structure results for this given constraint are to be written to +}; + +struct PxSolverConstraintPrepDescBase +{ + enum BodyState + { + eDYNAMIC_BODY = 1 << 0, + eSTATIC_BODY = 1 << 1, + eKINEMATIC_BODY = 1 << 2, + eARTICULATION = 1 << 3 + }; + + PxConstraintInvMassScale mInvMassScales; //!< In: The local mass scaling for this pair. + + PxSolverConstraintDesc* desc; //!< Output: The PxSolverConstraintDesc filled in by contact prep + + const PxSolverBody* body0; //!< In: The first body. Stores velocity information. Unused unless contact involves articulations. + const PxSolverBody* body1; //!< In: The second body. Stores velocity information. Unused unless contact involves articulations. + + const PxSolverBodyData* data0; //!< In: The first PxSolverBodyData. Stores mass and miscellaneous information for the first body. + const PxSolverBodyData* data1; //!< In: The second PxSolverBodyData. Stores mass and miscellaneous information for the second body + + PxTransform bodyFrame0; //!< In: The world-space transform of the first body. + PxTransform bodyFrame1; //!< In: The world-space transform of the second body. + + BodyState bodyState0; //!< In: Defines what kind of actor the first body is + BodyState bodyState1; //!< In: Defines what kind of actor the second body is + +}; + +struct PxSolverConstraintPrepDesc : public PxSolverConstraintPrepDescBase +{ + PX_ALIGN(16, Px1DConstraint* rows); //!< The start of the constraint rows + PxU32 numRows; //!< The number of rows + + PxReal linBreakForce, angBreakForce; //!< Break forces + PxReal minResponseThreshold; //!< The minimum response threshold + void* writeback; //!< Pointer to constraint writeback structure. Reports back joint breaking. If not required, set to NULL. + bool disablePreprocessing; //!< Disable joint pre-processing. Pre-processing can improve stability but under certain circumstances, e.g. when some invInertia rows are zero/almost zero, can cause instabilities. + bool improvedSlerp; //!< Use improved slerp model + bool driveLimitsAreForces; //!< Indicates whether drive limits are forces + bool extendedLimits; //!< Indicates whether we want to use extended limits + + PxVec3 body0WorldOffset; //!< Body0 world offset +}; + + +struct PxSolverContactDesc : public PxSolverConstraintPrepDescBase +{ + + PX_ALIGN(16, Sc::ShapeInteraction* shapeInteraction); //!< Pointer to share interaction. Used for force threshold reports in solver. Set to NULL if using immediate mode. + Gu::ContactPoint* contacts; //!< The start of the contacts for this pair + PxU32 numContacts; //!< The total number of contacs this pair references. + + bool hasMaxImpulse; //!< Defines whether this pairs has maxImpulses clamping enabled + bool disableStrongFriction; //!< Defines whether this pair disables strong friction (sticky friction correlation) + bool hasForceThresholds; //!< Defines whether this pair requires force thresholds + + PxReal restDistance; //!< A distance at which the solver should aim to hold the bodies separated. Default is 0 + PxReal maxCCDSeparation; //!< A distance used to configure speculative CCD behavior. Default is PX_MAX_F32. Set internally in PhysX for bodies with eENABLE_SPECULATIVE_CCD on. Do not set directly! + + PxU8* frictionPtr; //!< InOut: Friction patch correlation data. Set each frame by solver. Can be retained for improved behaviour or discarded each frame. + PxU8 frictionCount; //!< The total number of friction patches in this pair + + PxReal* contactForces; //!< Out: A buffer for the solver to write applied contact forces to. + + PxU32 startFrictionPatchIndex; //!< Start index of friction patch in the correlation buffer. Set by friction correlation + PxU32 numFrictionPatches; //!< Total number of friction patches in this pair. Set by friction correlation + + PxU32 startContactPatchIndex; //!< The start index of this pair's contact patches in the correlation buffer. For internal use only + PxU16 numContactPatches; //!< Total number of contact patches. + PxU16 axisConstraintCount; //!< Axis constraint count. Defines how many constraint rows this pair has produced. Useful for statistical purposes. + + PxU8 pad[16 - sizeof(void*)]; +}; + +class PxConstraintAllocator +{ +public: + /** + \brief Allocates constraint data. It is the application's responsibility to release this memory after PxSolveConstraints has completed. + \param[in] byteSize Allocation size in bytes + \return the allocated memory. This address must be 16-byte aligned. + */ + virtual PxU8* reserveConstraintData(const PxU32 byteSize) = 0; + /** + \brief Allocates friction data. Friction data can be retained by the application for a given pair and provided as an input to PxSolverContactDesc to improve simulation stability. + It is the application's responsibility to release this memory. If this memory is released, the application should ensure it does not pass pointers to this memory to PxSolverContactDesc. + \param[in] byteSize Allocation size in bytes + \return the allocated memory. This address must be 4-byte aligned. + */ + virtual PxU8* reserveFrictionData(const PxU32 byteSize) = 0; + + virtual ~PxConstraintAllocator() {} +}; + +#if !PX_DOXYGEN +} +#endif + +#if PX_VC +#pragma warning(pop) +#endif + +#endif + diff --git a/src/PhysX/physx/include/task/PxCpuDispatcher.h b/src/PhysX/physx/include/task/PxCpuDispatcher.h new file mode 100644 index 000000000..523c69af4 --- /dev/null +++ b/src/PhysX/physx/include/task/PxCpuDispatcher.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXCPUDISPATCHER_H +#define PXTASK_PXCPUDISPATCHER_H + +#include "task/PxTaskDefine.h" +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + +class PxBaseTask; + +/** + \brief A CpuDispatcher is responsible for scheduling the execution of tasks passed to it by the SDK. + + A typical implementation would for example use a thread pool with the dispatcher + pushing tasks onto worker thread queues or a global queue. + + @see PxBaseTask + @see PxTask + @see PxTaskManager +*/ +class PxCpuDispatcher +{ +public: + /** + \brief Called by the TaskManager when a task is to be queued for execution. + + Upon receiving a task, the dispatcher should schedule the task + to run when resource is available. After the task has been run, + it should call the release() method and discard it's pointer. + + \param[in] task The task to be run. + + @see PxBaseTask + */ + virtual void submitTask( PxBaseTask& task ) = 0; + + /** + \brief Returns the number of available worker threads for this dispatcher. + + The SDK will use this count to control how many tasks are submitted. By + matching the number of tasks with the number of execution units task + overhead can be reduced. + */ + virtual uint32_t getWorkerCount() const = 0; + + virtual ~PxCpuDispatcher() {} +}; + +} // end physx namespace + +#endif // PXTASK_PXCPUDISPATCHER_H diff --git a/src/PhysX/physx/include/task/PxGpuDispatcher.h b/src/PhysX/physx/include/task/PxGpuDispatcher.h new file mode 100644 index 000000000..ae53ab1a1 --- /dev/null +++ b/src/PhysX/physx/include/task/PxGpuDispatcher.h @@ -0,0 +1,248 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXGPUDISPATCHER_H +#define PXTASK_PXGPUDISPATCHER_H + +#include "task/PxTaskDefine.h" +#include "task/PxTask.h" + +/* forward decl to avoid including */ +typedef struct CUstream_st* CUstream; + +namespace physx +{ + +struct PxGpuCopyDesc; +class PxCudaContextManager; + +PX_PUSH_PACK_DEFAULT + +class PxTaskManager; + +/** \brief A GpuTask dispatcher + * + * A PxGpuDispatcher executes GpuTasks submitted by one or more TaskManagers (one + * or more scenes). It maintains a CPU worker thread which waits on GpuTask + * "groups" to be submitted. The submission API is explicitly sessioned so that + * GpuTasks are dispatched together as a group whenever possible to improve + * parallelism on the GPU. + * + * A PxGpuDispatcher cannot be allocated ad-hoc, they are created as a result of + * creating a PxCudaContextManager. Every PxCudaContextManager has a PxGpuDispatcher + * instance that can be queried. In this way, each PxGpuDispatcher is tied to + * exactly one CUDA context. + * + * A scene will use CPU fallback Tasks for GpuTasks if the PxTaskManager provided + * to it does not have a PxGpuDispatcher. For this reason, the PxGpuDispatcher must + * be assigned to the PxTaskManager before the PxTaskManager is given to a scene. + * + * Multiple TaskManagers may safely share a single PxGpuDispatcher instance, thus + * enabling scenes to share a CUDA context. + * + * Only failureDetected() is intended for use by the user. The rest of the + * nvGpuDispatcher public methods are reserved for internal use by only both + * TaskManagers and GpuTasks. + */ +class PxGpuDispatcher +{ +public: + /** \brief Record the start of a simulation step + * + * A PxTaskManager calls this function to record the beginning of a simulation + * step. The PxGpuDispatcher uses this notification to initialize the + * profiler state. + */ + virtual void startSimulation() = 0; + + /** \brief Record the start of a GpuTask batch submission + * + * A PxTaskManager calls this function to notify the PxGpuDispatcher that one or + * more GpuTasks are about to be submitted for execution. The PxGpuDispatcher + * will not read the incoming task queue until it receives one finishGroup() + * call for each startGroup() call. This is to ensure as many GpuTasks as + * possible are executed together as a group, generating optimal parallelism + * on the GPU. + */ + virtual void startGroup() = 0; + + /** \brief Submit a GpuTask for execution + * + * Submitted tasks are pushed onto an incoming queue. The PxGpuDispatcher + * will take the contents of this queue every time the pending group count + * reaches 0 and run the group of submitted GpuTasks as an interleaved + * group. + */ + virtual void submitTask(PxTask& task) = 0; + + /** \brief Record the end of a GpuTask batch submission + * + * A PxTaskManager calls this function to notify the PxGpuDispatcher that it is + * done submitting a group of GpuTasks (GpuTasks which were all make ready + * to run by the same prerequisite dependency becoming resolved). If no + * other group submissions are in progress, the PxGpuDispatcher will execute + * the set of ready tasks. + */ + virtual void finishGroup() = 0; + + /** \brief Add a CUDA completion prerequisite dependency to a task + * + * A GpuTask calls this function to add a prerequisite dependency on another + * task (usually a CpuTask) preventing that task from starting until all of + * the CUDA kernels and copies already launched have been completed. The + * PxGpuDispatcher will increment that task's reference count, blocking its + * execution, until the CUDA work is complete. + * + * This is generally only required when a CPU task is expecting the results + * of the CUDA kernels to have been copied into host memory. + * + * This mechanism is not at all not required to ensure CUDA kernels and + * copies are issued in the correct order. Kernel issue order is determined + * by normal task dependencies. The rule of thumb is to only use a blocking + * completion prerequisite if the task in question depends on a completed + * GPU->Host DMA. + * + * The PxGpuDispatcher issues a blocking event record to CUDA for the purposes + * of tracking the already submitted CUDA work. When this event is + * resolved, the PxGpuDispatcher manually decrements the reference count of + * the specified task, allowing it to execute (assuming it does not have + * other pending prerequisites). + */ + virtual void addCompletionPrereq(PxBaseTask& task) = 0; + + /** \brief Retrieve the PxCudaContextManager associated with this + * PxGpuDispatcher + * + * Every PxCudaContextManager has one PxGpuDispatcher, and every PxGpuDispatcher + * has one PxCudaContextManager. + */ + virtual PxCudaContextManager* getCudaContextManager() = 0; + + /** \brief Record the end of a simulation frame + * + * A PxTaskManager calls this function to record the completion of its + * dependency graph. If profiling is enabled, the PxGpuDispatcher will + * trigger the retrieval of profiling data from the GPU at this point. + */ + virtual void stopSimulation() = 0; + + /** \brief Returns true if a CUDA call has returned a non-recoverable error + * + * A return value of true indicates a fatal error has occurred. To protect + * itself, the PxGpuDispatcher enters a fall through mode that allows GpuTasks + * to complete without being executed. This allows simulations to continue + * but leaves GPU content static or corrupted. + * + * The user may try to recover from these failures by deleting GPU content + * so the visual artifacts are minimized. But there is no way to recover + * the state of the GPU actors before the failure. Once a CUDA context is + * in this state, the only recourse is to create a new CUDA context, a new + * scene, and start over. + * + * This is our "Best Effort" attempt to not turn a soft failure into a hard + * failure because continued use of a CUDA context after it has returned an + * error will usually result in a driver reset. However if the initial + * failure was serious enough, a reset may have already occurred by the time + * we learn of it. + */ + virtual bool failureDetected() const = 0; + + /** \brief Force the PxGpuDispatcher into failure mode + * + * This API should be used if user code detects a non-recoverable CUDA + * error. This ensures the PxGpuDispatcher does not launch any further + * CUDA work. Subsequent calls to failureDetected() will return true. + */ + virtual void forceFailureMode() = 0; + + /** \brief Launch a copy kernel with arbitrary number of copy commands + * + * This method is intended to be called from Kernel GpuTasks, but it can + * function outside of that context as well. + * + * If count is 1, the descriptor is passed to the kernel as arguments, so it + * may be declared on the stack. + * + * If count is greater than 1, the kernel will read the descriptors out of + * host memory. Because of this, the descriptor array must be located in + * page locked (pinned) memory. The provided descriptors may be modified by + * this method (converting host pointers to their GPU mapped equivalents) + * and should be considered *owned* by CUDA until the current batch of work + * has completed, so descriptor arrays should not be freed or modified until + * you have received a completion notification. + * + * If your GPU does not support mapping of page locked memory (SM>=1.1), + * this function degrades to calling CUDA copy methods. + */ + virtual void launchCopyKernel(PxGpuCopyDesc* desc, uint32_t count, CUstream stream) = 0; + + /** \brief Query pre launch task that runs before launching gpu kernels. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Do *not* set the continuation on the returned task, but use addPreLaunchDependent(). + */ + virtual PxBaseTask& getPreLaunchTask() = 0; + + /** \brief Adds a gpu launch task that gets executed after the pre launch task. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Each call adds a reference to the pre-launch task. + */ + virtual void addPreLaunchDependent(PxBaseTask& dependent) = 0; + + /** \brief Query post launch task that runs after the gpu is done. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Do *not* set the continuation on the returned task, but use addPostLaunchDependent(). + */ + virtual PxBaseTask& getPostLaunchTask() = 0; + + /** \brief Adds a task that gets executed after the post launch task. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Each call adds a reference to the pre-launch task. + */ + virtual void addPostLaunchDependent(PxBaseTask& dependent) = 0; + +protected: + /** \brief protected destructor + * + * GpuDispatchers are allocated and freed by their PxCudaContextManager. + */ + virtual ~PxGpuDispatcher() {} +}; + +PX_POP_PACK + +} // end physx namespace + + +#endif // PXTASK_PXGPUDISPATCHER_H diff --git a/src/PhysX/physx/include/task/PxGpuTask.h b/src/PhysX/physx/include/task/PxGpuTask.h new file mode 100644 index 000000000..e9d88ac52 --- /dev/null +++ b/src/PhysX/physx/include/task/PxGpuTask.h @@ -0,0 +1,118 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef PXTASK_PXGPUTASK_H +#define PXTASK_PXGPUTASK_H + +#include "task/PxTaskDefine.h" +#include "task/PxTask.h" +#include "task/PxGpuDispatcher.h" + +namespace physx +{ + +PX_PUSH_PACK_DEFAULT + +/** \brief Define the 'flavor' of a PxGpuTask + * + * Each PxGpuTask should have a specific function; either copying data to the + * device, running kernels on that data, or copying data from the device. + * + * For optimal performance, the dispatcher should run all available HtoD tasks + * before running all Kernel tasks, and all Kernel tasks before running any DtoH + * tasks. This provides maximal kernel overlap and the least number of CUDA + * flushes. + */ +struct PxGpuTaskHint +{ + /// \brief Enums for the type of GPU task + enum Enum + { + HostToDevice, + Kernel, + DeviceToHost, + + NUM_GPU_TASK_HINTS + }; +}; + +/** + * \brief PxTask implementation for launching CUDA work + */ +class PxGpuTask : public PxTask +{ +public: + PxGpuTask() : mComp(NULL) {} + + /** + * \brief iterative "run" function for a PxGpuTask + * + * The GpuDispatcher acquires the CUDA context for the duration of this + * function call, and it is highly recommended that the PxGpuTask use the + * provided CUstream for all kernels. + * + * kernelIndex will be 0 for the initial call and incremented before each + * subsequent call. Once launchInstance() returns false, its PxGpuTask is + * considered completed and is released. + */ + virtual bool launchInstance(CUstream stream, int kernelIndex) = 0; + + /** + * \brief Returns a hint indicating the function of this task + */ + virtual PxGpuTaskHint::Enum getTaskHint() const = 0; + + /** + * \brief Specify a task that will have its reference count decremented + * when this task is released + */ + void setCompletionTask(PxBaseTask& task) + { + mComp = &task; + } + + void release() + { + if (mComp) + { + mComp->removeReference(); + mComp = NULL; + } + PxTask::release(); + } + +protected: + /// \brief A pointer to the completion task + PxBaseTask* mComp; +}; + +PX_POP_PACK + +} // end physx namespace + +#endif // PXTASK_PXGPUTASK_H diff --git a/src/PhysX/physx/include/task/PxTask.h b/src/PhysX/physx/include/task/PxTask.h new file mode 100644 index 000000000..b51e6bc58 --- /dev/null +++ b/src/PhysX/physx/include/task/PxTask.h @@ -0,0 +1,334 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXTASK_H +#define PXTASK_PXTASK_H + +#include "task/PxTaskDefine.h" +#include "task/PxTaskManager.h" +#include "task/PxCpuDispatcher.h" +#include "task/PxGpuDispatcher.h" +#include "foundation/PxAssert.h" + +namespace physx +{ + +/** + * \brief Base class of all task types + * + * PxBaseTask defines a runnable reference counted task with built-in profiling. + */ +class PxBaseTask +{ +public: + PxBaseTask() : mContextID(0), mTm(NULL) {} + virtual ~PxBaseTask() {} + + /** + * \brief The user-implemented run method where the task's work should be performed + * + * run() methods must be thread safe, stack friendly (no alloca, etc), and + * must never block. + */ + virtual void run() = 0; + + /** + * \brief Return a user-provided task name for profiling purposes. + * + * It does not have to be unique, but unique names are helpful. + * + * \return The name of this task + */ + virtual const char* getName() const = 0; + + //! \brief Implemented by derived implementation classes + virtual void addReference() = 0; + //! \brief Implemented by derived implementation classes + virtual void removeReference() = 0; + //! \brief Implemented by derived implementation classes + virtual int32_t getReference() const = 0; + + /** \brief Implemented by derived implementation classes + * + * A task may assume in its release() method that the task system no longer holds + * references to it - so it may safely run its destructor, recycle itself, etc. + * provided no additional user references to the task exist + */ + virtual void release() = 0; + + /** + * \brief Return PxTaskManager to which this task was submitted + * + * Note, can return NULL if task was not submitted, or has been + * completed. + */ + PX_FORCE_INLINE PxTaskManager* getTaskManager() const + { + return mTm; + } + + PX_FORCE_INLINE void setContextId(PxU64 id) { mContextID = id; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + +protected: + PxU64 mContextID; //!< Context ID for profiler interface + PxTaskManager* mTm; //!< Owning PxTaskManager instance + + friend class PxTaskMgr; +}; + + +/** + * \brief A PxBaseTask implementation with deferred execution and full dependencies + * + * A PxTask must be submitted to a PxTaskManager to to be executed, Tasks may + * optionally be named when they are submitted. + */ +class PxTask : public PxBaseTask +{ +public: + PxTask() : mTaskID(0) {} + virtual ~PxTask() {} + + //! \brief Release method implementation + virtual void release() + { + PX_ASSERT(mTm); + + // clear mTm before calling taskCompleted() for safety + PxTaskManager* save = mTm; + mTm = NULL; + save->taskCompleted( *this ); + } + + //! \brief Inform the PxTaskManager this task must finish before the given + // task is allowed to start. + PX_INLINE void finishBefore( PxTaskID taskID ) + { + PX_ASSERT(mTm); + mTm->finishBefore( *this, taskID); + } + + //! \brief Inform the PxTaskManager this task cannot start until the given + // task has completed. + PX_INLINE void startAfter( PxTaskID taskID ) + { + PX_ASSERT(mTm); + mTm->startAfter( *this, taskID ); + } + + /** + * \brief Manually increment this task's reference count. The task will + * not be allowed to run until removeReference() is called. + */ + PX_INLINE void addReference() + { + PX_ASSERT(mTm); + mTm->addReference( mTaskID ); + } + + /** + * \brief Manually decrement this task's reference count. If the reference + * count reaches zero, the task will be dispatched. + */ + PX_INLINE void removeReference() + { + PX_ASSERT(mTm); + mTm->decrReference( mTaskID ); + } + + /** + * \brief Return the ref-count for this task + */ + PX_INLINE int32_t getReference() const + { + return mTm->getReference( mTaskID ); + } + + /** + * \brief Return the unique ID for this task + */ + PX_INLINE PxTaskID getTaskID() const + { + return mTaskID; + } + + /** + * \brief Called by PxTaskManager at submission time for initialization + * + * Perform simulation step initialization here. + */ + virtual void submitted() + { + mStreamIndex = 0; + mPreSyncRequired = false; + } + + /** + * \brief Specify that the GpuTask sync flag be set + */ + PX_INLINE void requestSyncPoint() + { + mPreSyncRequired = true; + } + +protected: + PxTaskID mTaskID; //!< ID assigned at submission + uint32_t mStreamIndex; //!< GpuTask CUDA stream index + bool mPreSyncRequired; //!< GpuTask sync flag + + friend class PxTaskMgr; + friend class PxGpuWorkerThread; +}; + + +/** + * \brief A PxBaseTask implementation with immediate execution and simple dependencies + * + * A PxLightCpuTask bypasses the PxTaskManager launch dependencies and will be + * submitted directly to your scene's CpuDispatcher. When the run() function + * completes, it will decrement the reference count of the specified + * continuation task. + * + * You must use a full-blown PxTask if you want your task to be resolved + * by another PxTask, or you need more than a single dependency to be + * resolved when your task completes, or your task will not run on the + * CpuDispatcher. + */ +class PxLightCpuTask : public PxBaseTask +{ +public: + PxLightCpuTask() + : mCont( NULL ) + , mRefCount( 0 ) + { + } + virtual ~PxLightCpuTask() + { + mTm = NULL; + } + + /** + * \brief Initialize this task and specify the task that will have its ref count decremented on completion. + * + * Submission is deferred until the task's mRefCount is decremented to zero. + * Note that we only use the PxTaskManager to query the appropriate dispatcher. + * + * \param[in] tm The PxTaskManager this task is managed by + * \param[in] c The task to be executed when this task has finished running + */ + PX_INLINE void setContinuation(PxTaskManager& tm, PxBaseTask* c) + { + PX_ASSERT( mRefCount == 0 ); + mRefCount = 1; + mCont = c; + mTm = &tm; + if( mCont ) + { + mCont->addReference(); + } + } + + /** + * \brief Initialize this task and specify the task that will have its ref count decremented on completion. + * + * This overload of setContinuation() queries the PxTaskManager from the continuation + * task, which cannot be NULL. + * \param[in] c The task to be executed after this task has finished running + */ + PX_INLINE void setContinuation( PxBaseTask* c ) + { + PX_ASSERT( c ); + PX_ASSERT( mRefCount == 0 ); + mRefCount = 1; + mCont = c; + if( mCont ) + { + mCont->addReference(); + mTm = mCont->getTaskManager(); + PX_ASSERT( mTm ); + } + } + + /** + * \brief Retrieves continuation task + */ + PX_INLINE PxBaseTask* getContinuation() const + { + return mCont; + } + + /** + * \brief Manually decrement this task's reference count. If the reference + * count reaches zero, the task will be dispatched. + */ + PX_INLINE void removeReference() + { + mTm->decrReference(*this); + } + + /** \brief Return the ref-count for this task */ + PX_INLINE int32_t getReference() const + { + return mRefCount; + } + + /** + * \brief Manually increment this task's reference count. The task will + * not be allowed to run until removeReference() is called. + */ + PX_INLINE void addReference() + { + mTm->addReference(*this); + } + + /** + * \brief called by CpuDispatcher after run method has completed + * + * Decrements the continuation task's reference count, if specified. + */ + PX_INLINE void release() + { + if( mCont ) + { + mCont->removeReference(); + } + } + +protected: + + PxBaseTask* mCont; //!< Continuation task, can be NULL + volatile int32_t mRefCount; //!< PxTask is dispatched when reaches 0 + + friend class PxTaskMgr; +}; + + +}// end physx namespace + + +#endif // PXTASK_PXTASK_H diff --git a/src/PhysX/physx/include/task/PxTaskDefine.h b/src/PhysX/physx/include/task/PxTaskDefine.h new file mode 100644 index 000000000..935089046 --- /dev/null +++ b/src/PhysX/physx/include/task/PxTaskDefine.h @@ -0,0 +1,37 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXTASKDEFINE_H +#define PXTASK_PXTASKDEFINE_H + +#include "foundation/PxPreprocessor.h" + +#ifndef PX_SUPPORT_PXTASK_PROFILING +#define PX_SUPPORT_PXTASK_PROFILING 1 +#endif + +#endif // PXTASK_PXTASKDEFINE_H diff --git a/src/PhysX/physx/include/task/PxTaskManager.h b/src/PhysX/physx/include/task/PxTaskManager.h new file mode 100644 index 000000000..d1738ef0b --- /dev/null +++ b/src/PhysX/physx/include/task/PxTaskManager.h @@ -0,0 +1,228 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXTASKMANAGER_H +#define PXTASK_PXTASKMANAGER_H + +#include "task/PxTaskDefine.h" +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxErrorCallback.h" + +namespace physx +{ +PX_PUSH_PACK_DEFAULT + +class PxBaseTask; +class PxTask; +class PxLightCpuTask; +typedef unsigned int PxTaskID; + +/** +\brief Identifies the type of each heavyweight PxTask object + +\note This enum type is only used by PxTask and GpuTask objects, LightCpuTasks do not use this enum. + +@see PxTask +@see PxLightCpuTask +*/ +struct PxTaskType +{ + /** + * \brief Identifies the type of each heavyweight PxTask object + */ + enum Enum + { + TT_CPU, //!< PxTask will be run on the CPU + TT_GPU, //!< PxTask will be run on the GPU + TT_NOT_PRESENT, //!< Return code when attempting to find a task that does not exist + TT_COMPLETED //!< PxTask execution has been completed + }; +}; + +class PxCpuDispatcher; +class PxGpuDispatcher; + +/** + \brief The PxTaskManager interface + + A PxTaskManager instance holds references to user-provided dispatcher objects, when tasks are + submitted the PxTaskManager routes them to the appropriate dispatcher and handles task profiling if enabled. + Users should not implement the PxTaskManager interface, the SDK creates its own concrete PxTaskManager object + per-scene which users can configure by passing dispatcher objects into the PxSceneDesc. + + @see CpuDispatcher + @see PxGpuDispatcher + +*/ +class PxTaskManager +{ +public: + + /** + \brief Set the user-provided dispatcher object for CPU tasks + + \param[in] ref The dispatcher object. + + @see CpuDispatcher + */ + virtual void setCpuDispatcher(PxCpuDispatcher& ref) = 0; + + /** + \brief Set the user-provided dispatcher object for GPU tasks + + \param[in] ref The dispatcher object. + + @see PxGpuDispatcher + */ + virtual void setGpuDispatcher(PxGpuDispatcher& ref) = 0; + + /** + \brief Get the user-provided dispatcher object for CPU tasks + + \return The CPU dispatcher object. + + @see CpuDispatcher + */ + virtual PxCpuDispatcher* getCpuDispatcher() const = 0; + + /** + \brief Get the user-provided dispatcher object for GPU tasks + + \return The GPU dispatcher object. + + @see PxGpuDispatcher + */ + virtual PxGpuDispatcher* getGpuDispatcher() const = 0; + + /** + \brief Reset any dependencies between Tasks + + \note Will be called at the start of every frame before tasks are submitted. + + @see PxTask + */ + virtual void resetDependencies() = 0; + + /** + \brief Called by the owning scene to start the task graph. + + \note All tasks with with ref count of 1 will be dispatched. + + @see PxTask + */ + virtual void startSimulation() = 0; + + /** + \brief Called by the owning scene at the end of a simulation step to synchronize the PxGpuDispatcher + + @see PxGpuDispatcher + */ + virtual void stopSimulation() = 0; + + /** + \brief Called by the worker threads to inform the PxTaskManager that a task has completed processing + + \param[in] task The task which has been completed + */ + virtual void taskCompleted(PxTask& task) = 0; + + /** + \brief Retrieve a task by name + + \param[in] name The unique name of a task + \return The ID of the task with that name, or TT_NOT_PRESENT if not found + */ + virtual PxTaskID getNamedTask(const char* name) = 0; + + /** + \brief Submit a task with a unique name. + + \param[in] task The task to be executed + \param[in] name The unique name of a task + \param[in] type The type of the task (default TT_CPU) + \return The ID of the task with that name, or TT_NOT_PRESENT if not found + */ + virtual PxTaskID submitNamedTask(PxTask* task, const char* name, PxTaskType::Enum type = PxTaskType::TT_CPU) = 0; + + /** + \brief Submit an unnamed task. + + \param[in] task The task to be executed + \param[in] type The type of the task (default TT_CPU) + + \return The ID of the task with that name, or TT_NOT_PRESENT if not found + */ + virtual PxTaskID submitUnnamedTask(PxTask& task, PxTaskType::Enum type = PxTaskType::TT_CPU) = 0; + + /** + \brief Retrieve a task given a task ID + + \param[in] id The ID of the task to return, a valid ID must be passed or results are undefined + + \return The task associated with the ID + */ + virtual PxTask* getTaskFromID(PxTaskID id) = 0; + + /** + \brief Release the PxTaskManager object, referenced dispatchers will not be released + */ + virtual void release() = 0; + + /** + \brief Construct a new PxTaskManager instance with the given [optional] dispatchers + */ + static PxTaskManager* createTaskManager(PxErrorCallback& errorCallback, PxCpuDispatcher* = 0, PxGpuDispatcher* = 0); + +protected: + virtual ~PxTaskManager() {} + + /*! \cond PRIVATE */ + + virtual void finishBefore(PxTask& task, PxTaskID taskID) = 0; + virtual void startAfter(PxTask& task, PxTaskID taskID) = 0; + + virtual void addReference(PxTaskID taskID) = 0; + virtual void decrReference(PxTaskID taskID) = 0; + virtual int32_t getReference(PxTaskID taskID) const = 0; + + virtual void decrReference(PxLightCpuTask&) = 0; + virtual void addReference(PxLightCpuTask&) = 0; + + /*! \endcond */ + + friend class PxBaseTask; + friend class PxTask; + friend class PxLightCpuTask; + friend class PxGpuWorkerThread; +}; + +PX_POP_PACK + +} // end physx namespace + + +#endif // PXTASK_PXTASKMANAGER_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleComponents.h b/src/PhysX/physx/include/vehicle/PxVehicleComponents.h new file mode 100644 index 000000000..3a85fcbd5 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleComponents.h @@ -0,0 +1,1359 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_CORE_COMPONENTS_H +#define PX_VEHICLE_CORE_COMPONENTS_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxMemory.h" +#include "foundation/PxVec3.h" +#include "common/PxCoreUtilityTypes.h" +#include "PxVehicleSDK.h" +#include "common/PxTypeInfo.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxVehicleChassisData +{ +public: + + friend class PxVehicleDriveSimData4W; + + PxVehicleChassisData() + : mMOI(PxVec3(0,0,0)), + mMass(1500), + mCMOffset(PxVec3(0,0,0)) + { + } + + /** + \brief Moment of inertia of vehicle rigid body actor. + + \note Specified in kilograms metres-squared (kg m^2). + */ + PxVec3 mMOI; + + /** + \brief Mass of vehicle rigid body actor. + + \note Specified in kilograms (kg). + */ + PxReal mMass; + + /** + \brief Center of mass offset of vehicle rigid body actor. + + \note Specified in metres (m). + */ + PxVec3 mCMOffset; + +private: + + PxReal pad; + + bool isValid() const; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleChassisData)& 0x0f)); + +class PxVehicleEngineData +{ +public: + + friend class PxVehicleDriveSimData; + + enum + { + eMAX_NB_ENGINE_TORQUE_CURVE_ENTRIES = 8 + }; + + PxVehicleEngineData() + : mMOI(1.0f), + mPeakTorque(500.0f), + mMaxOmega(600.0f), + mDampingRateFullThrottle(0.15f), + mDampingRateZeroThrottleClutchEngaged(2.0f), + mDampingRateZeroThrottleClutchDisengaged(0.35f) + { + mTorqueCurve.addPair(0.0f, 0.8f); + mTorqueCurve.addPair(0.33f, 1.0f); + mTorqueCurve.addPair(1.0f, 0.8f); + + mRecipMOI=1.0f/mMOI; + mRecipMaxOmega=1.0f/mMaxOmega; + } + + /** + \brief Graph of normalized torque (torque/mPeakTorque) against normalized engine speed ( engineRotationSpeed / mMaxOmega ). + + \note The normalized engine speed is the x-axis of the graph, while the normalized torque is the y-axis of the graph. + */ + PxFixedSizeLookupTable mTorqueCurve; + + /** + \brief Moment of inertia of the engine around the axis of rotation. + + \note Specified in kilograms metres-squared (kg m^2) + */ + PxReal mMOI; + + /** + \brief Maximum torque available to apply to the engine when the accelerator pedal is at maximum. + + \note The torque available is the value of the accelerator pedal (in range [0, 1]) multiplied by the normalized torque as computed from mTorqueCurve multiplied by mPeakTorque. + + \note Specified in kilograms metres-squared per second-squared (kg m^2 s^-2). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mPeakTorque; + + /** + \brief Maximum rotation speed of the engine. + + \note Specified in radians per second (s^-1). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mMaxOmega; + + /** + \brief Damping rate of engine when full throttle is applied. + + \note If the clutch is engaged (any gear except neutral) then the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchEngaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchEngaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchEngaged)*acceleratorPedal; + + \note If the clutch is disengaged (in neutral gear) the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchDisengaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchDisengaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchDisengaged)*acceleratorPedal; + + \note Specified in kilograms metres-squared per second (kg m^2 s^-1). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mDampingRateFullThrottle; + + + /** + \brief Damping rate of engine when full throttle is applied. + + \note If the clutch is engaged (any gear except neutral) then the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchEngaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchEngaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchEngaged)*acceleratorPedal; + + \note If the clutch is disengaged (in neutral gear) the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchDisengaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchDisengaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchDisengaged)*acceleratorPedal; + + \note Specified in kilograms metres-squared per second (kg m^2 s^-1). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mDampingRateZeroThrottleClutchEngaged; + + /** + \brief Damping rate of engine when full throttle is applied. + + \note If the clutch is engaged (any gear except neutral) then the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchEngaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchEngaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchEngaged)*acceleratorPedal; + + \note If the clutch is disengaged (in neutral gear) the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchDisengaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchDisengaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchDisengaged)*acceleratorPedal; + + \note Specified in kilograms metres-squared per second (kg m^2 s^-1). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mDampingRateZeroThrottleClutchDisengaged; + + /** + \brief Return value of mRecipMOI(=1.0f/mMOI) that is automatically set by PxVehicleDriveSimData::setEngineData + */ + PX_FORCE_INLINE PxReal getRecipMOI() const {return mRecipMOI;} + + /** + \brief Return value of mRecipMaxOmega( = 1.0f / mMaxOmega ) that is automatically set by PxVehicleDriveSimData::setEngineData + */ + PX_FORCE_INLINE PxReal getRecipMaxOmega() const {return mRecipMaxOmega;} + +private: + + /** + \brief Reciprocal of the engine moment of inertia. + + \note Not necessary to set this value because it is set by PxVehicleDriveSimData::setEngineData + + Range: [0, PX_MAX_F32)
+ */ + PxReal mRecipMOI; + + /** + \brief Reciprocal of the maximum rotation speed of the engine. + + \note Not necessary to set this value because it is set by PxVehicleDriveSimData::setEngineData + + Range: [0, PX_MAX_F32)
+ */ + PxReal mRecipMaxOmega; + + bool isValid() const; + + +//serialization +public: + PxVehicleEngineData(const PxEMPTY) : mTorqueCurve(PxEmpty) {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleEngineData)& 0x0f)); + +class PxVehicleGearsData +{ +public: + + friend class PxVehicleDriveSimData; + + enum Enum + { + eREVERSE=0, + eNEUTRAL, + eFIRST, + eSECOND, + eTHIRD, + eFOURTH, + eFIFTH, + eSIXTH, + eSEVENTH, + eEIGHTH, + eNINTH, + eTENTH, + eELEVENTH, + eTWELFTH, + eTHIRTEENTH, + eFOURTEENTH, + eFIFTEENTH, + eSIXTEENTH, + eSEVENTEENTH, + eEIGHTEENTH, + eNINETEENTH, + eTWENTIETH, + eTWENTYFIRST, + eTWENTYSECOND, + eTWENTYTHIRD, + eTWENTYFOURTH, + eTWENTYFIFTH, + eTWENTYSIXTH, + eTWENTYSEVENTH, + eTWENTYEIGHTH, + eTWENTYNINTH, + eTHIRTIETH, + eGEARSRATIO_COUNT + }; + + PxVehicleGearsData() + : mFinalRatio(4.0f), + mNbRatios(7), + mSwitchTime(0.5f) + { + mRatios[PxVehicleGearsData::eREVERSE]=-4.0f; + mRatios[PxVehicleGearsData::eNEUTRAL]=0.0f; + mRatios[PxVehicleGearsData::eFIRST]=4.0f; + mRatios[PxVehicleGearsData::eSECOND]=2.0f; + mRatios[PxVehicleGearsData::eTHIRD]=1.5f; + mRatios[PxVehicleGearsData::eFOURTH]=1.1f; + mRatios[PxVehicleGearsData::eFIFTH]=1.0f; + + for(PxU32 i = PxVehicleGearsData::eSIXTH; i < PxVehicleGearsData::eGEARSRATIO_COUNT; ++i) + mRatios[i]=0.f; + } + + /** + \brief Gear ratios + + Range: [0, PX_MAX_F32)
+ */ + PxReal mRatios[PxVehicleGearsData::eGEARSRATIO_COUNT]; + + /** + \brief Gear ratio applied is mRatios[currentGear]*finalRatio + + Range: [0, PX_MAX_F32)
+ */ + PxReal mFinalRatio; + + /** + \brief Number of gears (including reverse and neutral). + + Range: (0, MAX_NB_GEAR_RATIOS)
+ */ + PxU32 mNbRatios; + + /** + \brief Time it takes to switch gear. + + \note Specified in seconds (s). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mSwitchTime; + +private: + + PxReal mPad; + + bool isValid() const; + +//serialization +public: + PxVehicleGearsData(const PxEMPTY) {} + PxReal getGearRatio(PxVehicleGearsData::Enum a) const {return mRatios[a];} + void setGearRatio(PxVehicleGearsData::Enum a, PxReal ratio) { mRatios[a] = ratio;} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleGearsData)& 0x0f)); + +class PxVehicleAutoBoxData +{ +public: + + friend class PxVehicleDriveSimData; + + PxVehicleAutoBoxData() + { + for(PxU32 i=0;i mUpRatios[currentGear] the autobox will begin + a transition to currentGear+1 unless currentGear is the highest possible gear or neutral or reverse. + + Range: [0, 1]
+ */ + PxReal mUpRatios[PxVehicleGearsData::eGEARSRATIO_COUNT]; + + /** + \brief Value of engineRevs/maxEngineRevs that is low enough to decrement gear. + + \note When ( engineRotationSpeed / PxVehicleEngineData::mMaxOmega ) < mDownRatios[currentGear] the autobox will begin + a transition to currentGear-1 unless currentGear is first gear or neutral or reverse. + + Range: [0, 1]
+ */ + PxReal mDownRatios[PxVehicleGearsData::eGEARSRATIO_COUNT]; + + /** + \brief Set the latency time of the autobox. + + \note Latency time is the minimum time that must pass between each gear change that is initiated by the autobox. + The auto-box will only attempt to initiate another gear change up or down if the simulation time that has passed since the most recent + automated gear change is greater than the specified latency. + + \note Specified in seconds (s). + + @see getLatency + */ + void setLatency(const PxReal latency) + { + mDownRatios[PxVehicleGearsData::eREVERSE]=latency; + } + + /** + \brief Get the latency time of the autobox. + + \note Specified in seconds (s). + + @see setLatency + */ + PxReal getLatency() const + { + return mDownRatios[PxVehicleGearsData::eREVERSE]; + } + +private: + bool isValid() const; + +//serialization +public: + PxVehicleAutoBoxData(const PxEMPTY) {} + + PxReal getUpRatios(PxVehicleGearsData::Enum a) const {return mUpRatios[a];} + void setUpRatios(PxVehicleGearsData::Enum a, PxReal ratio) { mUpRatios[a] = ratio;} + + PxReal getDownRatios(PxVehicleGearsData::Enum a) const {return mDownRatios[a];} + void setDownRatios(PxVehicleGearsData::Enum a, PxReal ratio) { mDownRatios[a] = ratio;} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleAutoBoxData)& 0x0f)); + +class PxVehicleDifferential4WData +{ +public: + + friend class PxVehicleDriveSimData4W; + + enum Enum + { + eDIFF_TYPE_LS_4WD, //limited slip differential for car with 4 driven wheels + eDIFF_TYPE_LS_FRONTWD, //limited slip differential for car with front-wheel drive + eDIFF_TYPE_LS_REARWD, //limited slip differential for car with rear-wheel drive + eDIFF_TYPE_OPEN_4WD, //open differential for car with 4 driven wheels + eDIFF_TYPE_OPEN_FRONTWD, //open differential for car with front-wheel drive + eDIFF_TYPE_OPEN_REARWD, //open differential for car with rear-wheel drive + eMAX_NB_DIFF_TYPES + }; + + PxVehicleDifferential4WData() + : mFrontRearSplit(0.45f), + mFrontLeftRightSplit(0.5f), + mRearLeftRightSplit(0.5f), + mCentreBias(1.3f), + mFrontBias(1.3f), + mRearBias(1.3f), + mType(PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD) + { + } + + /** + \brief Ratio of torque split between front and rear (>0.5 means more to front, <0.5 means more to rear). + + \note Only applied to DIFF_TYPE_LS_4WD and eDIFF_TYPE_OPEN_4WD + + Range: [0, 1]
+ */ + PxReal mFrontRearSplit; + + /** + \brief Ratio of torque split between front-left and front-right (>0.5 means more to front-left, <0.5 means more to front-right). + + \note Only applied to DIFF_TYPE_LS_4WD and eDIFF_TYPE_OPEN_4WD and eDIFF_TYPE_LS_FRONTWD + + Range: [0, 1]
+ */ + PxReal mFrontLeftRightSplit; + + /** + \brief Ratio of torque split between rear-left and rear-right (>0.5 means more to rear-left, <0.5 means more to rear-right). + + \note Only applied to DIFF_TYPE_LS_4WD and eDIFF_TYPE_OPEN_4WD and eDIFF_TYPE_LS_REARWD + + Range: [0, 1]
+ */ + PxReal mRearLeftRightSplit; + + /** + \brief Maximum allowed ratio of average front wheel rotation speed and rear wheel rotation speeds + The differential will divert more torque to the slower wheels when the bias is exceeded. + + \note Only applied to DIFF_TYPE_LS_4WD + + Range: [1, PX_MAX_F32)
+ */ + PxReal mCentreBias; + + /** + \brief Maximum allowed ratio of front-left and front-right wheel rotation speeds. + The differential will divert more torque to the slower wheel when the bias is exceeded. + + \note Only applied to DIFF_TYPE_LS_4WD and DIFF_TYPE_LS_FRONTWD + + Range: [1, PX_MAX_F32)
+ */ + PxReal mFrontBias; + + /** + \brief Maximum allowed ratio of rear-left and rear-right wheel rotation speeds. + The differential will divert more torque to the slower wheel when the bias is exceeded. + + \note Only applied to DIFF_TYPE_LS_4WD and DIFF_TYPE_LS_REARWD + + Range: [1, PX_MAX_F32)
+ */ + PxReal mRearBias; + + /** + \brief Type of differential. + + Range: [DIFF_TYPE_LS_4WD, DIFF_TYPE_OPEN_FRONTWD]
+ */ + PxVehicleDifferential4WData::Enum mType; + +private: + + PxReal mPad[1]; + + bool isValid() const; + +//serialization +public: + PxVehicleDifferential4WData(const PxEMPTY) {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDifferential4WData)& 0x0f)); + +class PxVehicleDifferentialNWData +{ +public: + + friend class PxVehicleDriveSimDataNW; + friend class PxVehicleUpdate; + + PxVehicleDifferentialNWData() + { + PxMemSet(mBitmapBuffer, 0, sizeof(PxU32) * (((PX_MAX_NB_WHEELS + 31) & ~31) >> 5)); + mNbDrivenWheels=0; + mInvNbDrivenWheels=0.0f; + } + + /** + \brief Set a specific wheel to be driven or non-driven by the differential. + + \note The available drive torque will be split equally between all driven wheels. + Zero torque will be applied to non-driven wheels. + The default state of each wheel is to be uncoupled to the differential. + */ + void setDrivenWheel(const PxU32 wheelId, const bool drivenState); + + /** + \brief Test if a specific wheel has been configured as a driven or non-driven wheel. + */ + bool getIsDrivenWheel(const PxU32 wheelId) const; + +private: + + PxU32 mBitmapBuffer[((PX_MAX_NB_WHEELS + 31) & ~31) >> 5]; + PxU32 mNbDrivenWheels; + PxReal mInvNbDrivenWheels; + PxU32 mPad; + + bool isValid() const; + +//serialization +public: + PxVehicleDifferentialNWData(const PxEMPTY) {} + PxU32 getDrivenWheelStatus() const; + void setDrivenWheelStatus(PxU32 status); +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDifferentialNWData)& 0x0f)); + + +class PxVehicleAckermannGeometryData +{ +public: + + friend class PxVehicleDriveSimData4W; + + PxVehicleAckermannGeometryData() + : mAccuracy(1.0f), + mFrontWidth(0.0f), //Must be filled out + mRearWidth(0.0f), //Must be filled out + mAxleSeparation(0.0f) //Must be filled out + { + } + + /** + \brief Accuracy of Ackermann steer calculation. + + \note Accuracy with value 0.0 results in no Ackermann steer-correction, while + accuracy with value 1.0 results in perfect Ackermann steer-correction. + + \note Perfect Ackermann steer correction modifies the steer angles applied to the front-left and + front-right wheels so that the perpendiculars to the wheels' longitudinal directions cross the + extended vector of the rear axle at the same point. It is also applied to any steer angle applied + to the rear wheels but instead using the extended vector of the front axle. + + \note In general, more steer correction produces better cornering behavior. + + Range: [0, 1]
+ */ + PxReal mAccuracy; + + /** + \brief Distance between center-point of the two front wheels. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mFrontWidth; + + /** + \brief Distance between center-point of the two rear wheels. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mRearWidth; + + /** + \brief Distance between center of front axle and center of rear axle. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mAxleSeparation; + +private: + + bool isValid() const; + +//serialization +public: + PxVehicleAckermannGeometryData(const PxEMPTY) {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleAckermannGeometryData)& 0x0f)); + +/** +\brief Choose between a potentially more expensive but more accurate solution to the clutch model or a potentially cheaper but less accurate solution. +@see PxVehicleClutchData +*/ +struct PxVehicleClutchAccuracyMode +{ + enum Enum + { + eESTIMATE = 0, + eBEST_POSSIBLE + }; +}; + +class PxVehicleClutchData +{ +public: + + friend class PxVehicleDriveSimData; + + PxVehicleClutchData() + : mStrength(10.0f), + mAccuracyMode(PxVehicleClutchAccuracyMode::eBEST_POSSIBLE), + mEstimateIterations(5) + { + } + + /** + \brief Strength of clutch. + + \note The clutch is the mechanism that couples the engine to the wheels. + A stronger clutch more strongly couples the engine to the wheels, while a + clutch of strength zero completely decouples the engine from the wheels. + Stronger clutches more quickly bring the wheels and engine into equilibrium, while weaker + clutches take longer, resulting in periods of clutch slip and delays in power transmission + from the engine to the wheels. + The torque generated by the clutch is proportional to the clutch strength and + the velocity difference between the engine's rotational speed and the rotational speed of the + driven wheels after accounting for the gear ratio. + The torque at the clutch is applied negatively to the engine and positively to the driven wheels. + + \note Specified in kilograms metres-squared per second (kg m^2 s^-1) + + Range: [0,PX_MAX_F32)
+ */ + PxReal mStrength; + + /** + \brief The engine and wheel rotation speeds that are coupled through the clutch can be updated by choosing + one of two modes: eESTIMATE and eBEST_POSSIBLE. + + \note If eESTIMATE is chosen the vehicle sdk will update the wheel and engine rotation speeds + with estimated values to the implemented clutch model. + + \note If eBEST_POSSIBLE is chosen the vehicle sdk will compute the best possible + solution (within floating point tolerance) to the implemented clutch model. + This is the recommended mode. + + \note The clutch model remains the same if either eESTIMATE or eBEST_POSSIBLE is chosen but the accuracy and + computational cost of the solution to the model can be tuned as required. + */ + PxVehicleClutchAccuracyMode::Enum mAccuracyMode; + + /** + \brief Tune the mathematical accuracy and computational cost of the computed estimate to the wheel and + engine rotation speeds if eESTIMATE is chosen. + + \note As mEstimateIterations increases the computational cost of the clutch also increases and the solution + approaches the solution that would be computed if eBEST_POSSIBLE was chosen instead. + + \note This has no effect if eBEST_POSSIBLE is chosen as the accuracy mode. + + \note A value of zero is not allowed if eESTIMATE is chosen as the accuracy mode. + */ + PxU32 mEstimateIterations; + +private: + + PxU8 mPad[4]; + + bool isValid() const; + +//serialization +public: + PxVehicleClutchData(const PxEMPTY) {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleClutchData)& 0x0f)); + + +/** +\brief Tire load variation can be strongly dependent on the time-step so it is a good idea to filter it +to give less jerky handling behavior. + +\note The x-axis of the graph is normalized tire load, while the y-axis is the filtered normalized tire load. + +\note The normalized load is the force acting downwards on the tire divided by the force experienced by the tire when the car is at rest on the ground. + +\note The rest load is approximately the product of the value of gravitational acceleration and PxVehicleSuspensionData::mSprungMass. + +\note The minimum possible normalized load is zero. + +\note There are two points on the graph: (mMinNormalisedLoad, mMinNormalisedFilteredLoad) and (mMaxNormalisedLoad, mMaxFilteredNormalisedLoad). + +\note Normalized loads less than mMinNormalisedLoad have filtered normalized load = mMinNormalisedFilteredLoad. + +\note Normalized loads greater than mMaxNormalisedLoad have filtered normalized load = mMaxFilteredNormalisedLoad. + +\note Normalized loads in-between are linearly interpolated between mMinNormalisedFilteredLoad and mMaxFilteredNormalisedLoad. + +\note The tire load applied as input to the tire force computation is the filtered normalized load multiplied by the rest load. +*/ +class PxVehicleTireLoadFilterData +{ +public: + + friend class PxVehicleWheelsSimData; + + PxVehicleTireLoadFilterData() + : mMinNormalisedLoad(0), + mMinFilteredNormalisedLoad(0.2308f), + mMaxNormalisedLoad(3.0f), + mMaxFilteredNormalisedLoad(3.0f) + { + mDenominator=1.0f/(mMaxNormalisedLoad - mMinNormalisedLoad); + } + + /** + \brief Graph point (mMinNormalisedLoad,mMinFilteredNormalisedLoad) + */ + PxReal mMinNormalisedLoad; + + /** + \brief Graph point (mMinNormalisedLoad,mMinFilteredNormalisedLoad) + */ + PxReal mMinFilteredNormalisedLoad; + + /** + \brief Graph point (mMaxNormalisedLoad,mMaxFilteredNormalisedLoad) + */ + PxReal mMaxNormalisedLoad; + + /** + \brief Graph point (mMaxNormalisedLoad,mMaxFilteredNormalisedLoad) + */ + PxReal mMaxFilteredNormalisedLoad; + + PX_FORCE_INLINE PxReal getDenominator() const {return mDenominator;} + +private: + + /** + \brief Not necessary to set this value. + */ + //1.0f/(mMaxNormalisedLoad-mMinNormalisedLoad) for quick calculations + PxReal mDenominator; + + PxU32 mPad[3]; + + bool isValid() const; + +//serialization +public: + PxVehicleTireLoadFilterData(const PxEMPTY) {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleTireLoadFilterData)& 0x0f)); + +class PxVehicleWheelData +{ +public: + + friend class PxVehicleWheels4SimData; + + PxVehicleWheelData() + : mRadius(0.0f), //Must be filled out + mWidth(0.0f), + mMass(20.0f), + mMOI(0.0f), //Must be filled out + mDampingRate(0.25f), + mMaxBrakeTorque(1500.0f), + mMaxHandBrakeTorque(0.0f), + mMaxSteer(0.0f), + mToeAngle(0.0f), + mRecipRadius(0.0f), //Must be filled out + mRecipMOI(0.0f) //Must be filled out + { + } + + /** + \brief Radius of unit that includes metal wheel plus rubber tire. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mRadius; + + /** + \brief Maximum width of unit that includes wheel plus tire. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mWidth; + + /** + \brief Mass of unit that includes wheel plus tire. + + \note Specified in kilograms (kg). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mMass; + + /** + \brief Moment of inertia of unit that includes wheel plus tire about the rolling axis. + + \note Specified in kilograms metres-squared (kg m^2). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mMOI; + + /** + \brief Damping rate applied to wheel. + + \note Specified in kilograms metres-squared per second (kg m^2 s^-1). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mDampingRate; + + /** + \brief Max brake torque that can be applied to wheel. + + \note Specified in kilograms metres-squared per second-squared (kg m^2 s^-2) + + Range: [0, PX_MAX_F32)
+ */ + PxReal mMaxBrakeTorque; + + /** + \brief Max handbrake torque that can be applied to wheel. + + \note Specified in kilograms metres-squared per second-squared (kg m^2 s^-2) + + Range: [0, PX_MAX_F32)
+ */ + PxReal mMaxHandBrakeTorque; + + /** + \brief Max steer angle that can be achieved by the wheel. + + \note Specified in radians. + + Range: [0, PX_MAX_F32)
+ */ + PxReal mMaxSteer; + + /** + \brief Wheel toe angle. This value is ignored by PxVehicleDriveTank and PxVehicleNoDrive. + + \note Specified in radians. + + Range: [0, Pi/2]
+ */ + PxReal mToeAngle;//in radians + + /** + \brief Return value equal to 1.0f/mRadius + + @see PxVehicleWheelsSimData::setWheelData + */ + PX_FORCE_INLINE PxReal getRecipRadius() const {return mRecipRadius;} + + /** + \brief Return value equal to 1.0f/mRecipMOI + + @see PxVehicleWheelsSimData::setWheelData + */ + PX_FORCE_INLINE PxReal getRecipMOI() const {return mRecipMOI;} + +private: + + /** + \brief Reciprocal of radius of unit that includes metal wheel plus rubber tire. + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setWheelData + + Range: [0, PX_MAX_F32)
+ */ + PxReal mRecipRadius; + + /** + \brief Reciprocal of moment of inertia of unit that includes wheel plus tire about single allowed axis of rotation. + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setWheelData + + Range: [0, PX_MAX_F32)
+ */ + PxReal mRecipMOI; + + PxReal mPad[1]; + + bool isValid() const; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleWheelData)& 0x0f)); + +class PxVehicleSuspensionData +{ +public: + + friend class PxVehicleWheels4SimData; + + PxVehicleSuspensionData() + : mSpringStrength(0.0f), + mSpringDamperRate(0.0f), + mMaxCompression(0.3f), + mMaxDroop(0.1f), + mSprungMass(0.0f), + mCamberAtRest(0.0f), + mCamberAtMaxCompression(0.0f), + mCamberAtMaxDroop(0.0f), + mRecipMaxCompression(1.0f), + mRecipMaxDroop(1.0f) + { + } + + /** + \brief Spring strength of suspension unit. + + \note Specified in kilograms per second-squared (kg s^-2). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mSpringStrength; + + /** + \brief Spring damper rate of suspension unit. + + \note Specified in kilograms per second (kg s^-1). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mSpringDamperRate; + + /** + \brief Maximum compression allowed by suspension spring. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mMaxCompression; + + /** + \brief Maximum elongation allowed by suspension spring. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
+ */ + PxReal mMaxDroop; + + /** + \brief Mass of vehicle that is supported by suspension spring. + + \note Specified in kilograms (kg). + + \note Each suspension is guaranteed to generate an upwards force of |gravity|*mSprungMass along the suspension direction when the wheel is perfectly + at rest and sitting at the rest pose defined by the wheel centre offset. + + \note The sum of the sprung masses of all suspensions of a vehicle should match the mass of the PxRigidDynamic associated with the vehicle. + When this condition is satisfied for a vehicle on a horizontal plane the wheels of the vehicle are guaranteed to sit at the rest pose + defined by the wheel centre offset. The mass matching condition is not enforced. + + \note As the wheel compresses or elongates along the suspension direction the force generated by the spring is + F = |gravity|*mSprungMass + deltaX*mSpringStrength + deltaXDot*mSpringDamperRate + where deltaX is the deviation from the defined rest pose and deltaXDot is the velocity of the sprung mass along the suspension direction. + In practice, deltaXDot is computed by comparing the current and previous deviation from the rest pose and dividing the difference + by the simulation timestep. + + \note If a single suspension spring is hanging in the air and generates zero force the remaining springs of the vehicle will necessarily + sit in a compressed configuration. In summary, the sum of the remaining suspension forces cannot balance the downwards gravitational force + acting on the vehicle without extra force arising from the deltaX*mSpringStrength force term. + + \note Theoretically, a suspension spring should generate zero force at maximum elongation and increase linearly as the suspension approaches the rest pose. + PxVehicleSuspensionData will only enforce this physical law if the spring is configured so that |gravity|*mSprungMass == mMaxDroop*mSpringStrength. + To help decouple vehicle handling from visual wheel positioning this condition is not enforced. + In practice, the value of |gravity|*mSprungMass + deltaX*mSpringStrength is clamped at zero to ensure it never falls negative. + + @see PxVehicleComputeSprungMasses, PxVehicleWheelsSimData::setWheelCentreOffset, PxVehicleSuspensionData::mSpringStrength, PxVehicleSuspensionData::mSpringDamperRate, PxVehicleSuspensionData::mMaxDroop + + Range: [0, PX_MAX_F32)
+ */ + PxReal mSprungMass; + + /** + \brief Camber angle (in radians) of wheel when the suspension is at its rest position. + + \note Specified in radians. + + Range: [-pi/2, pi/2]
+ + */ + PxReal mCamberAtRest; + + /** + \brief Camber angle (in radians) of wheel when the suspension is at maximum compression. + + \note For compressed suspensions the camber angle is a linear interpolation of + mCamberAngleAtRest and mCamberAtMaxCompression + + \note Specified in radians. + + Range: [-pi/2, pi/2]
+ */ + PxReal mCamberAtMaxCompression; + + /** + \brief Camber angle (in radians) of wheel when the suspension is at maximum droop. + + \note For extended suspensions the camber angle is linearly interpolation of + mCamberAngleAtRest and mCamberAtMaxDroop + + \note Specified in radians. + + Range: [-pi/2, pi/2]
+ */ + PxReal mCamberAtMaxDroop; + + /** + \brief Reciprocal of maximum compression. + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setSuspensionData + + Range: [0, PX_MAX_F32)
+ */ + PX_FORCE_INLINE PxReal getRecipMaxCompression() const {return mRecipMaxCompression;} + + /** + \brief Reciprocal of maximum droop. + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setSuspensionData + + Range: [0, PX_MAX_F32)
+ */ + PX_FORCE_INLINE PxReal getRecipMaxDroop() const {return mRecipMaxDroop;} + + /** + \brief Set a new sprung mass for the suspension and modify the spring strength so that the natural frequency + of the spring is preserved. + \param[in] newSprungMass is the new mass that the suspension spring will support. + */ + void setMassAndPreserveNaturalFrequency(const PxReal newSprungMass) + { + const PxF32 oldStrength = mSpringStrength; + const PxF32 oldSprungMass = mSprungMass; + const PxF32 newStrength = oldStrength * (newSprungMass / oldSprungMass); + mSpringStrength = newStrength; + mSprungMass = newSprungMass; + } + +private: + + /** + \brief Cached value of 1.0f/mMaxCompression + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setSuspensionData + */ + PxReal mRecipMaxCompression; + + /** + \brief Cached value of 1.0f/mMaxDroop + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setSuspensionData + */ + PxReal mRecipMaxDroop; + + //padding + PxReal mPad[2]; + + bool isValid() const; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleSuspensionData)& 0x0f)); + +class PxVehicleAntiRollBarData +{ +public: + + friend class PxVehicleWheelsSimData; + + PxVehicleAntiRollBarData() + : mWheel0(0xffffffff), + mWheel1(0xffffffff), + mStiffness(0.0f) + { + } + + /* + \brief The anti-roll bar connects two wheels with indices mWheel0 and mWheel1 + */ + PxU32 mWheel0; + + /* + \brief The anti-roll bar connects two wheels with indices mWheel0 and mWheel1 + */ + PxU32 mWheel1; + + /* + \brief The stiffness of the anti-roll bar. + + \note Specified in kilograms per second-squared (kg s^-2). + + Range: [0, PX_MAX_F32)
+ */ + PxF32 mStiffness; + +private: + + PxF32 mPad[1]; + + bool isValid() const; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleAntiRollBarData)& 0x0f)); + +class PxVehicleTireData +{ +public: + friend class PxVehicleWheels4SimData; + + PxVehicleTireData() + : mLatStiffX(2.0f), + mLatStiffY(0.3125f*(180.0f / PxPi)), + mLongitudinalStiffnessPerUnitGravity(1000.0f), + mCamberStiffnessPerUnitGravity(0.1f*(180.0f / PxPi)), + mType(0) + { + mFrictionVsSlipGraph[0][0]=0.0f; + mFrictionVsSlipGraph[0][1]=1.0f; + mFrictionVsSlipGraph[1][0]=0.1f; + mFrictionVsSlipGraph[1][1]=1.0f; + mFrictionVsSlipGraph[2][0]=1.0f; + mFrictionVsSlipGraph[2][1]=1.0f; + + mRecipLongitudinalStiffnessPerUnitGravity=1.0f/mLongitudinalStiffnessPerUnitGravity; + + mFrictionVsSlipGraphRecipx1Minusx0=1.0f/(mFrictionVsSlipGraph[1][0]-mFrictionVsSlipGraph[0][0]); + mFrictionVsSlipGraphRecipx2Minusx1=1.0f/(mFrictionVsSlipGraph[2][0]-mFrictionVsSlipGraph[1][0]); + } + + /** + \brief Tire lateral stiffness is a graph of tire load that has linear behavior near zero load and + flattens at large loads. mLatStiffX describes the minimum normalized load (load/restLoad) that gives a + flat lateral stiffness response to load. + + Range: [0, PX_MAX_F32)
+ */ + PxReal mLatStiffX; + + /** + \brief Tire lateral stiffness is a graph of tire load that has linear behavior near zero load and + flattens at large loads. mLatStiffY describes the maximum possible value of lateralStiffness/restLoad that occurs + when (load/restLoad)>= mLatStiffX. + + \note If load/restLoad is greater than mLatStiffX then the lateral stiffness is mLatStiffY*restLoad. + + \note If load/restLoad is less than mLatStiffX then the lateral stiffness is mLastStiffY*(load/mLatStiffX) + + \note Lateral force can be approximated as lateralStiffness * lateralSlip. + + \note Specified in per radian. + + Range: [0, PX_MAX_F32)
+ */ + PxReal mLatStiffY; + + /** + \brief Tire Longitudinal stiffness per unit gravitational acceleration. + + \note Longitudinal stiffness of the tire is calculated as gravitationalAcceleration*mLongitudinalStiffnessPerUnitGravity. + + \note Longitudinal force can be approximated as gravitationalAcceleration*mLongitudinalStiffnessPerUnitGravity*longitudinalSlip. + + \note Specified in kilograms per radian. + + Range: [0, PX_MAX_F32)
+ */ + PxReal mLongitudinalStiffnessPerUnitGravity; + + /** + \brief tire Tire camber stiffness per unity gravitational acceleration. + + \note Camber stiffness of the tire is calculated as gravitationalAcceleration*mCamberStiffnessPerUnitGravity + + \note Camber force can be approximated as gravitationalAcceleration*mCamberStiffnessPerUnitGravity*camberAngle. + + \note Specified in kilograms per radian. + + Range: [0, PX_MAX_F32)
+ */ + PxReal mCamberStiffnessPerUnitGravity; + + /** + \brief Graph of friction vs longitudinal slip with 3 points. + + \note mFrictionVsSlipGraph[0][0] is always zero. + + \note mFrictionVsSlipGraph[0][1] is the friction available at zero longitudinal slip. + + \note mFrictionVsSlipGraph[1][0] is the value of longitudinal slip with maximum friction. + + \note mFrictionVsSlipGraph[1][1] is the maximum friction. + + \note mFrictionVsSlipGraph[2][0] is the end point of the graph. + + \note mFrictionVsSlipGraph[2][1] is the value of friction for slips greater than mFrictionVsSlipGraph[2][0]. + + \note The friction value computed from the friction vs longitudinal slip graph is used to scale the friction + value for the combination of material and tire type (PxVehicleDrivableSurfaceToTireFrictionPairs). + + \note mFrictionVsSlipGraph[2][0] > mFrictionVsSlipGraph[1][0] > mFrictionVsSlipGraph[0][0] + + \note mFrictionVsSlipGraph[1][1] is typically greater than mFrictionVsSlipGraph[0][1] + + \note mFrictionVsSlipGraph[2][1] is typically smaller than mFrictionVsSlipGraph[1][1] + + \note longitudinal slips > mFrictionVsSlipGraph[2][0] use friction multiplier mFrictionVsSlipGraph[2][1] + + \note The final friction value used by the tire model is the value returned by PxVehicleDrivableSurfaceToTireFrictionPairs + multiplied by the value computed from mFrictionVsSlipGraph. + + @see PxVehicleDrivableSurfaceToTireFrictionPairs, PxVehicleComputeTireForce + + Range: [0, PX_MAX_F32)
+ */ + PxReal mFrictionVsSlipGraph[3][2]; + + /** + \brief Tire type denoting slicks, wets, snow, winter, summer, all-terrain, mud etc. + + @see PxVehicleDrivableSurfaceToTireFrictionPairs + + Range: [0, PX_MAX_F32)
+ */ + PxU32 mType; + + /** + \brief Return Cached value of 1.0/mLongitudinalStiffnessPerUnitGravity + + @see PxVehicleWheelsSimData::setTireData + */ + PX_FORCE_INLINE PxReal getRecipLongitudinalStiffnessPerUnitGravity() const {return mRecipLongitudinalStiffnessPerUnitGravity;} + + /** + \brief Return Cached value of 1.0f/(mFrictionVsSlipGraph[1][0]-mFrictionVsSlipGraph[0][0]) + + @see PxVehicleWheelsSimData::setTireData + */ + PX_FORCE_INLINE PxReal getFrictionVsSlipGraphRecipx1Minusx0() const {return mFrictionVsSlipGraphRecipx1Minusx0;} + + /** + \brief Return Cached value of 1.0f/(mFrictionVsSlipGraph[2][0]-mFrictionVsSlipGraph[1][0]) + + @see PxVehicleWheelsSimData::setTireData + */ + PX_FORCE_INLINE PxReal getFrictionVsSlipGraphRecipx2Minusx1() const {return mFrictionVsSlipGraphRecipx2Minusx1;} + +private: + + /** + \brief Cached value of 1.0/mLongitudinalStiffnessPerUnitGravity. + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setTireData + + @see PxVehicleWheelsSimData::setTireData + */ + PxReal mRecipLongitudinalStiffnessPerUnitGravity; + + /** + \brief Cached value of 1.0f/(mFrictionVsSlipGraph[1][0]-mFrictionVsSlipGraph[0][0]) + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setTireData + + @see PxVehicleWheelsSimData::setTireData + */ + PxReal mFrictionVsSlipGraphRecipx1Minusx0; + + /** + \brief Cached value of 1.0f/(mFrictionVsSlipGraph[2][0]-mFrictionVsSlipGraph[1][0]) + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setTireData + + @see PxVehicleWheelsSimData::setTireData + */ + PxReal mFrictionVsSlipGraphRecipx2Minusx1; + + PxReal mPad[2]; + + bool isValid() const; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleTireData)& 0x0f)); +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_CORE_COMPONENTS_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDrive.h b/src/PhysX/physx/include/vehicle/PxVehicleDrive.h new file mode 100644 index 000000000..6358c7f84 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleDrive.h @@ -0,0 +1,566 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_DRIVE_H +#define PX_VEHICLE_DRIVE_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleComponents.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxFilterData; +class PxGeometry; +class PxPhysics; +class PxBatchQuery; +class PxVehicleDrivableSurfaceToTireFrictionPairs; +class PxShape; +class PxMaterial; +class PxRigidDynamic; + +/** +\brief Data structure describing non-wheel configuration data of a vehicle that has engine, gears, clutch, and auto-box. +@see PxVehicleWheelsSimData for wheels configuration data. +*/ +class PxVehicleDriveSimData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleDriveTank; + + /** + \brief Return the engine data + */ + PX_FORCE_INLINE const PxVehicleEngineData& getEngineData() const + { + return mEngine; + } + + /** + \brief Set the engine data + \param[in] engine - the data stored in engine is copied to the vehicle's engine. + */ + void setEngineData(const PxVehicleEngineData& engine); + + /** + \brief Return the gears data + */ + PX_FORCE_INLINE const PxVehicleGearsData& getGearsData() const + { + return mGears; + } + + /** + \brief Set the gears data + \param[in] gears - the data stored in gears is copied to the vehicle's gears. + */ + void setGearsData(const PxVehicleGearsData& gears); + + /** + \brief Return the clutch data + */ + PX_FORCE_INLINE const PxVehicleClutchData& getClutchData() const + { + return mClutch; + } + + /** + \brief Set the clutch data + \param[in] clutch - the data stored in clutch is copied to the vehicle's clutch. + */ + void setClutchData(const PxVehicleClutchData& clutch); + + /** + \brief Return the autobox data + */ + PX_FORCE_INLINE const PxVehicleAutoBoxData& getAutoBoxData() const + { + return mAutoBox; + } + + /** + \brief Set the autobox data + \param[in] autobox - the data stored in autobox is copied to the vehicle's autobox. + */ + void setAutoBoxData(const PxVehicleAutoBoxData& autobox); + +protected: + /* + \brief Engine simulation data + @see setEngineData, getEngineData + */ + PxVehicleEngineData mEngine; + + /* + \brief Gear simulation data + @see setGearsData, getGearsData + */ + PxVehicleGearsData mGears; + + /* + \brief Clutch simulation data + @see setClutchData, getClutchData + */ + PxVehicleClutchData mClutch; + + /* + \brief Autobox simulation data + @see setAutoboxData, getAutoboxData + */ + PxVehicleAutoBoxData mAutoBox; + + /** + \brief Test that a PxVehicleDriveSimData instance has been configured with legal data. + Call only after setting all components with setEngineData,setGearsData,setClutchData,setAutoBoxData + @see PxVehicleDrive4W::setup, PxVehicleDriveTank::setup + */ + bool isValid() const; + + +//serialization +public: + PxVehicleDriveSimData() {} + PxVehicleDriveSimData(const PxEMPTY) : mEngine(PxEmpty), mGears(PxEmpty), mClutch(PxEmpty), mAutoBox(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveSimData) & 15)); + + +/** +\brief Data structure with instanced dynamics data for vehicle with engine, clutch, gears, autobox +@see PxVehicleWheelsDynData for wheels dynamics data. +*/ +class PxVehicleDriveDynData +{ +public: + + enum + { + eMAX_NB_ANALOG_INPUTS=16 + }; + + friend class PxVehicleDrive; + + /** + \brief Set all dynamics data to zero to bring the vehicle to rest. + */ + void setToRestState(); + + /** + \brief Set an analog control value to drive the vehicle. + \param[in] type describes the type of analog control being modified + \param[in] analogVal is the new value of the specific analog control. + @see PxVehicleDrive4WControl, PxVehicleDriveNWControl, PxVehicleDriveTankControl + */ + void setAnalogInput(const PxU32 type, const PxReal analogVal); + + /** + \brief Get the analog control value that has been applied to the vehicle. + \return The value of the specified analog control value. + @see PxVehicleDrive4WControl, PxVehicleDriveNWControl, PxVehicleDriveTankControl + */ + PxReal getAnalogInput(const PxU32 type) const; + + /** + \brief Inform the vehicle that the gear-up button has been pressed. + + \param[in] digitalVal is the state of the gear-up button. + + \note If digitalVal is true the vehicle will attempt to initiate a gear change at the next call to PxVehicleUpdates. + + \note The value of mGearUpPressed is not reset by PxVehicleUpdates + */ + void setGearUp(const bool digitalVal) + { + mGearUpPressed = digitalVal; + } + + /** + \brief Set that the gear-down button has been pressed. + + \param[in] digitalVal is the state of the gear-down button. + + \note If digitalVal is true the vehicle will attempt to initiate a gear change at the next call to PxVehicleUpdates. + + \note The value of mGearDownPressed is not reset by PxVehicleUpdates + */ + void setGearDown(const bool digitalVal) + { + mGearDownPressed = digitalVal; + } + + /** + \brief Check if the gear-up button has been pressed + \return The state of the gear-up button. + */ + bool getGearUp() const + { + return mGearUpPressed; + } + + /** + \brief Check if the gear-down button has been pressed + \return The state of the gear-down button. + */ + bool getGearDown() const + { + return mGearDownPressed; + } + + /** + \brief Set the flag that will be used to select auto-gears + If useAutoGears is true the auto-box will be active. + \param[in] useAutoGears is the active state of the auto-box. + */ + PX_FORCE_INLINE void setUseAutoGears(const bool useAutoGears) + { + mUseAutoGears=useAutoGears; + } + + /** + \brief Get the flag status that is used to select auto-gears + \return The active status of the auto-box. + */ + PX_FORCE_INLINE bool getUseAutoGears() const + { + return mUseAutoGears; + } + + /** + \brief Toggle the auto-gears flag + If useAutoGears is true the auto-box will be active. + */ + PX_FORCE_INLINE void toggleAutoGears() + { + mUseAutoGears = !mUseAutoGears; + } + + /** + \brief Set the current gear. + + \param[in] currentGear is the vehicle's gear. + + \note If the target gear is different from the current gear the vehicle will + attempt to start a gear change from the current gear that has just been set + towards the target gear at the next call to PxVehicleUpdates. + + @see setTargetGear, PxVehicleGearsData + */ + PX_FORCE_INLINE void setCurrentGear(PxU32 currentGear) + { + mCurrentGear = currentGear; + } + + /** + \brief Get the current gear. + + \return The vehicle's current gear. + + @see getTargetGear, PxVehicleGearsData + */ + PX_FORCE_INLINE PxU32 getCurrentGear() const + { + return mCurrentGear; + } + + /** + \brief Set the target gear. + + \param[in] targetGear is the vehicle's target gear. + + \note If the target gear is different from the current gear the vehicle will + attempt to start a gear change towards the target gear at the next call to + PxVehicleUpdates. + + @see PxVehicleGearsData + */ + PX_FORCE_INLINE void setTargetGear(PxU32 targetGear) + { + mTargetGear = targetGear; + } + + /** + \brief Get the target gear. + + \return The vehicle's target gear. + + @see setTargetGear, PxVehicleGearsData + */ + PX_FORCE_INLINE PxU32 getTargetGear() const + { + return mTargetGear; + } + + /** + \brief Start a gear change to a target gear. + + \param[in] targetGear is the gear the vehicle will begin a transition towards. + + \note The gear change will begin at the next call to PxVehicleUpadates. + + @see PxVehicleGearsData + */ + PX_FORCE_INLINE void startGearChange(const PxU32 targetGear) + { + mTargetGear=targetGear; + } + + /** + \brief Force an immediate gear change to a target gear + + \param[in] targetGear is the gear the vehicle will be given immediately. + + @see PxVehicleGearsData + */ + PX_FORCE_INLINE void forceGearChange(const PxU32 targetGear) + { + mTargetGear=targetGear; + mCurrentGear=targetGear; + } + + /** + \brief Set the rotation speed of the engine (radians per second) + + \param[in] speed is the rotational speed (radians per second) to apply to the engine. + */ + PX_FORCE_INLINE void setEngineRotationSpeed(const PxF32 speed) + { + mEnginespeed = speed; + } + + /** + \brief Return the rotation speed of the engine (radians per second) + + \return The rotational speed (radians per second) of the engine. + */ + PX_FORCE_INLINE PxReal getEngineRotationSpeed() const + { + return mEnginespeed; + } + + /** + \brief Return the time that has passed since the current gear change was initiated. + + \return The time that has passed since the current gear change was initiated. + + \note If no gear change is in process the gear switch time will be zero. + + @see PxVehicleGearsData.mSwitchTime + */ + PX_FORCE_INLINE PxReal getGearSwitchTime() const + { + return mGearSwitchTime; + } + + /** + \brief Return the time that has passed since the autobox last initiated a gear change. + + \return The time that has passed since the autobox last initiated a gear change. + + @see PxVehicleAutoBoxData::setLatency, PxVehicleAutoBoxData::getLatency + */ + PX_FORCE_INLINE PxReal getAutoBoxSwitchTime() const + { + return mAutoBoxSwitchTime; + } + + /** + \brief All dynamic data values are public for fast access. + */ + + + /** + \brief Analog control values used by vehicle simulation. + @see setAnalogInput, getAnalogInput, PxVehicleDrive4WControl, PxVehicleDriveNWControl, PxVehicleDriveTankControl + */ + PxReal mControlAnalogVals[eMAX_NB_ANALOG_INPUTS]; + + /** + \brief Auto-gear flag used by vehicle simulation. Set true to enable the autobox, false to disable the autobox. + @see setUseAutoGears, setUseAutoGears, toggleAutoGears, PxVehicleAutoBoxData + */ + bool mUseAutoGears; + + /** + \brief Gear-up digital control value used by vehicle simulation. + + \note If true a gear change will be initiated towards currentGear+1 (or to first gear if in reverse). + + @see setDigitalInput, getDigitalInput + */ + bool mGearUpPressed; + + /** + \brief Gear-down digital control value used by vehicle simulation. + + \note If true a gear change will be initiated towards currentGear-1 (or to reverse if in first). + + @see setDigitalInput, getDigitalInput + */ + bool mGearDownPressed; + + /** + \brief Current gear + @see startGearChange, forceGearChange, getCurrentGear, PxVehicleGearsData + */ + PxU32 mCurrentGear; + + /** + \brief Target gear (different from current gear if a gear change is underway) + @see startGearChange, forceGearChange, getTargetGear, PxVehicleGearsData + */ + PxU32 mTargetGear; + + /** + \brief Rotation speed of engine + @see setToRestState, getEngineRotationSpeed + */ + PxReal mEnginespeed; + + /** + \brief Reported time that has passed since gear change started. + @see setToRestState, startGearChange, PxVehicleGearsData::mSwitchTime + */ + PxReal mGearSwitchTime; + + /** + \brief Reported time that has passed since last autobox gearup/geardown decision. + @see setToRestState, PxVehicleAutoBoxData::setLatency + */ + PxReal mAutoBoxSwitchTime; + +private: + PxU32 mPad[2]; + + /** + \brief Test that a PxVehicleDriveDynData instance has legal values. + @see setToRestState + */ + bool isValid() const; + +//serialization +public: + PxVehicleDriveDynData(); + PxVehicleDriveDynData(const PxEMPTY) {} + PxU32 getNbAnalogInput() const { return eMAX_NB_ANALOG_INPUTS; } + PX_FORCE_INLINE void setGearChange(const PxU32 gearChange) { mTargetGear= gearChange; } + PX_FORCE_INLINE PxU32 getGearChange() const { return mTargetGear; } + PX_FORCE_INLINE void setGearSwitchTime(const PxReal switchTime) { mGearSwitchTime = switchTime; } + PX_FORCE_INLINE void setAutoBoxSwitchTime(const PxReal autoBoxSwitchTime) { mAutoBoxSwitchTime = autoBoxSwitchTime; } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveDynData) & 15)); + +/** +\brief A complete vehicle with instance dynamics data and configuration data for wheels and engine,clutch,gears,autobox. +@see PxVehicleDrive4W, PxVehicleDriveTank +*/ +class PxVehicleDrive : public PxVehicleWheels +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + /** + \brief Dynamics data of vehicle instance. + @see setup + */ + PxVehicleDriveDynData mDriveDynData; + +protected: + + /** + \brief Test that all instanced dynamics data and configuration data have legal values. + */ + bool isValid() const; + + /** + \brief Set vehicle to rest. + */ + void setToRestState(); + + /** + @see PxVehicleDrive4W::allocate, PxVehicleDriveTank::allocate + */ + static PxU32 computeByteSize(const PxU32 numWheels); + static PxU8* patchupPointers(const PxU32 nbWheels, PxVehicleDrive* vehDrive, PxU8* ptr); + virtual void init(const PxU32 numWheels); + + /** + \brief Deallocate a PxVehicle4WDrive instance. + @see PxVehicleDrive4W::free, PxVehicleDriveTank::free + */ + void free(); + + /** + @see PxVehicleDrive4W::setup, PxVehicleDriveTank::setup + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, + const PxU32 nbDrivenWheels, const PxU32 nbNonDrivenWheels); + +//serialization +public: + static void getBinaryMetaData(PxOutputStream& stream); + PxVehicleDrive(PxBaseFlags baseFlags) : PxVehicleWheels(baseFlags), mDriveDynData(PxEmpty) {} + virtual const char* getConcreteTypeName() const { return "PxVehicleDrive"; } +protected: + PxVehicleDrive(PxType concreteType, PxBaseFlags baseFlags) : PxVehicleWheels(concreteType, baseFlags) {} + ~PxVehicleDrive() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleDrive", name) || PxBase::isKindOf(name); } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDrive) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_DRIVE_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h b/src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h new file mode 100644 index 000000000..ec91e7b49 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h @@ -0,0 +1,279 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_4WDRIVE_H +#define PX_VEHICLE_4WDRIVE_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleDrive.h" +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleComponents.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxFilterData; +class PxGeometry; +class PxPhysics; +class PxBatchQuery; +class PxVehicleDrivableSurfaceToTireFrictionPairs; +class PxShape; +class PxMaterial; +class PxRigidDynamic; + +/** +\brief Data structure describing the drive model components of a vehicle with up to 4 driven wheels and up to 16 un-driven wheels. +The drive model incorporates engine, clutch, gears, autobox, differential, and Ackermann steer correction. +@see PxVehicleDriveSimData +*/ +class PxVehicleDriveSimData4W : public PxVehicleDriveSimData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleDrive4W; + + PxVehicleDriveSimData4W() + : PxVehicleDriveSimData() + { + } + + /** + \brief Return the data describing the differential. + @see PxVehicleDifferential4WData + */ + PX_FORCE_INLINE const PxVehicleDifferential4WData& getDiffData() const + { + return mDiff; + } + + /** + \brief Return the data describing the Ackermann steer-correction. + @see PxVehicleAckermannGeometryData + */ + PX_FORCE_INLINE const PxVehicleAckermannGeometryData& getAckermannGeometryData() const + { + return mAckermannGeometry; + } + + /** + \brief Set the data describing the differential. + @see PxVehicleDifferential4WData + */ + void setDiffData(const PxVehicleDifferential4WData& diff); + + /** + \brief Set the data describing the Ackermann steer-correction. + @see PxVehicleAckermannGeometryData + */ + void setAckermannGeometryData(const PxVehicleAckermannGeometryData& ackermannData); + +private: + + /** + \brief Differential simulation data + @see setDiffData, getDiffData + */ + PxVehicleDifferential4WData mDiff; + + /** + \brief Data for ackermann steer angle computation. + @see setAckermannGeometryData, getAckermannGeometryData + */ + PxVehicleAckermannGeometryData mAckermannGeometry; + + /** + \brief Test if the 4W-drive simulation data has been setup with legal data. + \note Call only after setting all components. + @see setEnginedata, setClutchData, setGearsData, setAutoboxData, setDiffData, setAckermannGeometryData + */ + bool isValid() const; + +//serialization +public: + PxVehicleDriveSimData4W(const PxEMPTY) : PxVehicleDriveSimData(PxEmpty), mDiff(PxEmpty), mAckermannGeometry(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveSimData4W) & 15)); + + + +/** +\brief The ordering of the driven and steered wheels of a PxVehicleDrive4W. + +@see PxVehicleWheelsSimData, PxVehicleWheelsDynData +*/ + +struct PxVehicleDrive4WWheelOrder +{ + enum Enum + { + eFRONT_LEFT=0, + eFRONT_RIGHT, + eREAR_LEFT, + eREAR_RIGHT + }; +}; + +/** +\brief The control inputs for a PxVehicleDrive4W. + +@see PxVehicleDriveDynData::setAnalogInput, PxVehicleDriveDynData::getAnalogInput +*/ + +struct PxVehicleDrive4WControl +{ + enum Enum + { + eANALOG_INPUT_ACCEL=0, + eANALOG_INPUT_BRAKE, + eANALOG_INPUT_HANDBRAKE, + eANALOG_INPUT_STEER_LEFT, + eANALOG_INPUT_STEER_RIGHT, + eMAX_NB_DRIVE4W_ANALOG_INPUTS + }; +}; + +/** +\brief Data structure with instanced dynamics data and configuration data of a vehicle with up to 4 driven wheels and up to 16 non-driven wheels. +*/ +class PxVehicleDrive4W : public PxVehicleDrive +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + friend class PxVehicleUpdate; + + /** + \brief Allocate a PxVehicleDrive4W instance for a 4WDrive vehicle with nbWheels (= 4 + number of un-driven wheels) + + \param[in] nbWheels is the number of vehicle wheels (= 4 + number of un-driven wheels) + + \return The instantiated vehicle. + + @see free, setup + */ + static PxVehicleDrive4W* allocate(const PxU32 nbWheels); + + /** + \brief Deallocate a PxVehicleDrive4W instance. + @see allocate + */ + void free(); + + /** + \brief Set up a vehicle using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \param[in] driveData describes the properties of the vehicle's drive model (gears/engine/clutch/differential/autobox). The vehicle instance takes a copy of this data. + \param[in] nbNonDrivenWheels is the number of wheels on the vehicle that cannot be connected to the differential (= numWheels - 4). + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + \note wheelsData must contain data for at least 4 wheels. Unwanted wheels can be disabled with PxVehicleWheelsSimData::disableWheel after calling setup. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, + const PxU32 nbNonDrivenWheels); + + /** + \brief Allocate and set up a vehicle using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \param[in] driveData describes the properties of the vehicle's drive model (gears/engine/clutch/differential/autobox). The vehicle instance takes a copy of this data. + \param[in] nbNonDrivenWheels is the number of wheels on the vehicle that cannot be connected to the differential (= numWheels - 4). + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + \note wheelsData must contain data for at least 4 wheels. Unwanted wheels can be disabled with PxVehicleWheelsSimData::disableWheel after calling setup. + \return The instantiated vehicle. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + */ + static PxVehicleDrive4W* create + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, + const PxU32 nbNonDrivenWheels); + + /** + \brief Set a vehicle to its rest state. Aside from the rigid body transform, this will set the vehicle and rigid body + to the state they were in immediately after setup or create. + \note Calling setToRestState invalidates the cached raycast hit planes under each wheel meaning that suspension line + raycasts need to be performed at least once with PxVehicleSuspensionRaycasts before calling PxVehicleUpdates. + @see setup, create, PxVehicleSuspensionRaycasts, PxVehicleUpdates + */ + void setToRestState(); + + /** + \brief Simulation data that describes the configuration of the vehicle's drive model. + @see setup, create + */ + PxVehicleDriveSimData4W mDriveSimData; + +private: + + /** + \brief Test if the instanced dynamics and configuration data has legal values. + */ + bool isValid() const; + +//serialization +protected: + PxVehicleDrive4W(); + ~PxVehicleDrive4W(){} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleDrive4W", name) || PxBase::isKindOf(name); } +public: + static PxVehicleDrive4W* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + PxVehicleDrive4W(PxBaseFlags baseFlags) : PxVehicleDrive(baseFlags), mDriveSimData(PxEmpty) {} + virtual const char* getConcreteTypeName() const { return "PxVehicleDrive4W"; } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDrive4W) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_4WDRIVE_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h b/src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h new file mode 100644 index 000000000..f4a0f84ea --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h @@ -0,0 +1,237 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_NWDRIVE_H +#define PX_VEHICLE_NWDRIVE_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleDrive.h" +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleComponents.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxFilterData; +class PxGeometry; +class PxPhysics; +class PxBatchQuery; +class PxVehicleDrivableSurfaceToTireFrictionPairs; +class PxShape; +class PxMaterial; +class PxRigidDynamic; + +/** +\brief Data structure describing configuration data of a vehicle with up to PX_MAX_NB_WHEELS driven equally through the differential. The vehicle has an +engine, clutch, gears, autobox, differential. +@see PxVehicleDriveSimData +*/ +class PxVehicleDriveSimDataNW : public PxVehicleDriveSimData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleDriveNW; + + PxVehicleDriveSimDataNW() + : PxVehicleDriveSimData() + { + } + + /** + \brief Return the data describing the differential of a vehicle with up to PX_MAX_NB_WHEELS driven wheels. + */ + const PxVehicleDifferentialNWData& getDiffData() const + { + return mDiff; + } + + /** + \brief Set the data describing the differential of a vehicle with up to PX_MAX_NB_WHEELS driven wheels. + The differential data describes the set of wheels that are driven by the differential. + */ + void setDiffData(const PxVehicleDifferentialNWData& diff); + +private: + + /** + \brief Differential simulation data + @see setDiffData, getDiffData + */ + PxVehicleDifferentialNWData mDiff; + + /** + \brief Test if the NW-drive simulation data has been setup with legal data. + Call only after setting all components. + @see setEngineData, setClutchData, setGearsData, setAutoboxData, setDiffData, setAckermannGeometryData + */ + bool isValid() const; + +//serialization +public: + PxVehicleDriveSimDataNW(const PxEMPTY) : PxVehicleDriveSimData(PxEmpty), mDiff(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveSimDataNW) & 15)); + + +/** +\brief The control inputs for a PxVehicleDriveNW. + +@see PxVehicleDriveDynData::setAnalogInput, PxVehicleDriveDynData::getAnalogInput +*/ +struct PxVehicleDriveNWControl +{ + enum Enum + { + eANALOG_INPUT_ACCEL=0, + eANALOG_INPUT_BRAKE, + eANALOG_INPUT_HANDBRAKE, + eANALOG_INPUT_STEER_LEFT, + eANALOG_INPUT_STEER_RIGHT, + eMAX_NB_DRIVENW_ANALOG_INPUTS + }; +}; + +/** +\brief Data structure with instanced dynamics data and configuration data of a vehicle with up to PX_MAX_NB_WHEELS driven wheels. +*/ +class PxVehicleDriveNW : public PxVehicleDrive +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + /** + \brief Allocate a PxVehicleDriveNW instance for a NWDrive vehicle with nbWheels + + \param[in] nbWheels is the number of wheels on the vehicle. + + \return The instantiated vehicle. + + @see free, setup + */ + static PxVehicleDriveNW* allocate(const PxU32 nbWheels); + + /** + \brief Deallocate a PxVehicleDriveNW instance. + @see allocate + */ + void free(); + + /** + \brief Set up a vehicle using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \param[in] driveData describes the properties of the vehicle's drive model (gears/engine/clutch/differential/autobox). The vehicle instance takes a copy of this data. + \param[in] nbWheels is the number of wheels on the vehicle. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimDataNW& driveData, + const PxU32 nbWheels); + + /** + \brief Allocate and set up a vehicle using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \param[in] driveData describes the properties of the vehicle's drive model (gears/engine/clutch/differential/autobox). The vehicle instance takes a copy of this data. + \param[in] nbWheels is the number of wheels on the vehicle. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + \return The instantiated vehicle. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + */ + static PxVehicleDriveNW* create + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimDataNW& driveData, + const PxU32 nbWheels); + + /** + \brief Set a vehicle to its rest state. Aside from the rigid body transform, this will set the vehicle and rigid body + to the state they were in immediately after setup or create. + \note Calling setToRestState invalidates the cached raycast hit planes under each wheel meaning that suspension line + raycasts need to be performed at least once with PxVehicleSuspensionRaycasts before calling PxVehicleUpdates. + @see setup, create, PxVehicleSuspensionRaycasts, PxVehicleUpdates + */ + void setToRestState(); + + /** + \brief Simulation data that describes the configuration of the vehicle's drive model. + @see setup, create + */ + PxVehicleDriveSimDataNW mDriveSimData; + +private: + + /** + \brief Test if the instanced dynamics and configuration data has legal values. + */ + bool isValid() const; + +//serialization +public: + PxVehicleDriveNW(PxBaseFlags baseFlags) : PxVehicleDrive(baseFlags), mDriveSimData(PxEmpty) {} + PxVehicleDriveNW(); + ~PxVehicleDriveNW(){} + static PxVehicleDriveNW* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + virtual const char* getConcreteTypeName() const { return "PxVehicleDriveNW"; } + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleDriveNW", name) || PxBase::isKindOf(name); } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveNW) & 15)); + + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_NWDRIVE_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h b/src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h new file mode 100644 index 000000000..3e176f124 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h @@ -0,0 +1,281 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_DRIVE_TANK_H +#define PX_VEHICLE_DRIVE_TANK_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleDrive.h" +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleComponents.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxFilterData; +class PxGeometry; +class PxPhysics; +class PxBatchQuery; +class PxVehicleDrivableSurfaceToTireFrictionPairs; +class PxShape; +class PxMaterial; +class PxRigidDynamic; + +/** +\brief The ordering of the wheels of a PxVehicleDriveTank. + +@see PxVehicleWheelsSimData, PxVehicleWheelsDynData +*/ +struct PxVehicleDriveTankWheelOrder +{ + enum Enum + { + eFRONT_LEFT=0, + eFRONT_RIGHT, + e1ST_FROM_FRONT_LEFT, + e1ST_FROM_FRONT_RIGHT, + e2ND_FROM_FRONT_LEFT, + e2ND_FROM_FRONT_RIGHT, + e3RD_FROM_FRONT_LEFT, + e3RD_FROM_FRONT_RIGHT, + e4TH_FROM_FRONT_LEFT, + e4TH_FROM_FRONT_RIGHT, + e5TH_FROM_FRONT_LEFT, + e5TH_FROM_FRONT_RIGHT, + e6TH_FROM_FRONT_LEFT, + e6TH_FROM_FRONT_RIGHT, + e7TH_FROM_FRONT_LEFT, + e7TH_FROM_FRONT_RIGHT, + e8TH_FROM_FRONT_LEFT, + e8TH_FROM_FRONT_RIGHT, + e9TH_FROM_FRONT_LEFT, + e9TH_FROM_FRONT_RIGHT + }; +}; + + +/** +\brief The control inputs for a PxVehicleDriveTank. + +\note The values of eANALOG_INPUT_THRUST_LEFT and eANALOG_INPUT_THRUST_RIGHT determine how much +of the total available drive torque is diverted to the left and right wheels. These entries in the +enumerated list represent the state of the left and right control sticks of a tank. The total available +drive torque available is controlled by eANALOG_INPUT_ACCEL, which represents the state of the acceleration +pedal and controls how much torque will be applied to the engine. + +\note To accelerate forwards eANALOG_INPUT_ACCEL must be greater than zero so that torque is applied to drive the +engine, while eANALOG_INPUT_THRUST_LEFT and eANALOG_INPUT_THRUST_RIGHT must also be greater than zero +to divert the available drive torque to the left and wheels. If eANALOG_INPUT_THRUST_LEFT > eANALOG_INPUT_THRUST_RIGHT +the tank will turn to the right. If eANALOG_INPUT_THRUST_RIGHT > eANALOG_INPUT_THRUST_LEFT +the tank will turn to the left. + +@see PxVehicleDriveDynData::setAnalogInput, PxVehicleDriveDynData::getAnalogInput +*/ + +struct PxVehicleDriveTankControl +{ + enum Enum + { + eANALOG_INPUT_ACCEL=0, + eANALOG_INPUT_BRAKE_LEFT, + eANALOG_INPUT_BRAKE_RIGHT, + eANALOG_INPUT_THRUST_LEFT, + eANALOG_INPUT_THRUST_RIGHT, + eMAX_NB_DRIVETANK_ANALOG_INPUTS + }; +}; + +/** +\brief Two driving models are supported. + +\note If eSTANDARD is chosen the left and right wheels are always driven in the same direction. If the tank is in +a forward gear the left and right wheels will all be driven forwards, while in reverse gear the left and right wheels +will all be driven backwards. With eSTANDARD the legal range of left and right thrust is (0,1). + +\note If eSPECIAL is chosen it is possible to drive the left and right wheels in different directions. +With eSPECIAL the legal range of left and right thrust is (-1,1). In forward(reverse) gear negative thrust values drive the wheels +backwards(forwards), while positive thrust values drives the wheels forwards(backwards). + +\note A sharp left turn can be achieved in eSTANDARD mode by braking with the left wheels and thrusting forward with the +right wheels. A smaller turning circle can theoretically be achieved in eSPECIAL mode by applying negative thrust to the left wheels and positive +thrust to the right wheels. + +\note In both modes the legal ranges of acceleration and left/right brake are all (0,1). + +@see PxVehicleDriveTank::setDriveModel +*/ +struct PxVehicleDriveTankControlModel +{ + enum Enum + { + eSTANDARD=0, + eSPECIAL + }; +}; + + +/** +\brief Data structure with instanced dynamics data and configuration data of a tank. +*/ +class PxVehicleDriveTank : public PxVehicleDrive +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + + /** + \brief Allocate a PxVehicleTankDrive instance for a tank with nbWheels + + \param[in] nbWheels is the number of wheels on the vehicle. + + \note It is assumed that all wheels are driven wheels. + + \return The instantiated vehicle. + + @see free, setup + */ + static PxVehicleDriveTank* allocate(const PxU32 nbWheels); + + /** + \brief Deallocate a PxVehicleDriveTank instance. + @see allocate + */ + void free(); + + /** + \brief Set up a tank using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the tank in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the tank. The tank instance takes a copy of this data. + \param[in] driveData describes the properties of the tank's drive model (gears/engine/clutch/autobox). The tank instance takes a copy of this data. + \param[in] nbDrivenWheels is the number of wheels on the tank. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + \note nbDrivenWheels must be an even number + \note The wheels must be arranged according to PxVehicleDriveTankWheelOrder; that is, + the even wheels are on the left side of the tank and the odd wheels are on the right side of the tank. + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData& driveData, + const PxU32 nbDrivenWheels); + + /** + \brief Allocate and set up a tank using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the tank. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the tank in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the tank. The tank instance takes a copy of this data. + \param[in] driveData describes the properties of the tank's drive model (gears/engine/clutch/differential/autobox). The tank instance takes a copy of this data. + \param[in] nbDrivenWheels is the number of wheels on the tank. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + \return The instantiated vehicle. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + */ + static PxVehicleDriveTank* create + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData& driveData, + const PxU32 nbDrivenWheels); + + /** + \brief Set the control model used by the tank. + \note eDRIVE_MODEL_STANDARD: turning achieved by braking on one side, accelerating on the other side. + \note eDRIVE_MODEL_SPECIAL: turning achieved by accelerating forwards on one side, accelerating backwards on the other side. + \note The default value is eDRIVE_MODEL_STANDARD + */ + void setDriveModel(const PxVehicleDriveTankControlModel::Enum driveModel) + { + mDriveModel=driveModel; + } + + /** + \brief Return the control model used by the tank. + */ + PxVehicleDriveTankControlModel::Enum getDriveModel() const {return mDriveModel;} + + /** + \brief Set a vehicle to its rest state. Aside from the rigid body transform, this will set the vehicle and rigid body + to the state they were in immediately after setup or create. + \note Calling setToRestState invalidates the cached raycast hit planes under each wheel meaning that suspension line + raycasts need to be performed at least once with PxVehicleSuspensionRaycasts before calling PxVehicleUpdates. + @see setup, create, PxVehicleSuspensionRaycasts, PxVehicleUpdates + */ + void setToRestState(); + + /** + \brief Simulation data that models vehicle components + @see setup, create + */ + PxVehicleDriveSimData mDriveSimData; + +private: + /** + \brief Test if the instanced dynamics and configuration data has legal values. + */ + bool isValid() const; + + /** + \brief Drive model + @see setDriveModel, getDriveModel, PxVehicleDriveTankControlModel + */ + PxVehicleDriveTankControlModel::Enum mDriveModel; + + PxU32 mPad[3]; + +//serialization +public: + PxVehicleDriveTank(PxBaseFlags baseFlags) : PxVehicleDrive(baseFlags) {} + static PxVehicleDriveTank* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + virtual const char* getConcreteTypeName() const { return "PxVehicleDriveTank"; } + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleDriveTank", name) || PxBase::isKindOf(name); } +protected: + PxVehicleDriveTank(); + ~PxVehicleDriveTank(){} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveTank) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_DRIVE_TANK_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h b/src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h new file mode 100644 index 000000000..a92a082e4 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h @@ -0,0 +1,217 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_NO_DRIVE_H +#define PX_VEHICLE_NO_DRIVE_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleComponents.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxFilterData; +class PxGeometry; +class PxPhysics; +class PxBatchQuery; +class PxVehicleDrivableSurfaceToTireFrictionPairs; +class PxShape; +class PxMaterial; +class PxRigidDynamic; + +/** +\brief Data structure with instanced dynamics data and configuration data of a vehicle with no drive model. +*/ +class PxVehicleNoDrive : public PxVehicleWheels +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + /** + \brief Allocate a PxVehicleNoDrive instance for a vehicle without drive model and with nbWheels + + \param[in] nbWheels is the number of wheels on the vehicle. + + \return The instantiated vehicle. + + @see free, setup + */ + static PxVehicleNoDrive* allocate(const PxU32 nbWheels); + + /** + \brief Deallocate a PxVehicleNoDrive instance. + @see allocate + */ + void free(); + + /** + \brief Set up a vehicle using simulation data for the wheels. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheels::setWheelShapeMapping. + @see allocate, free, setToRestState, PxVehicleWheels::setWheelShapeMapping + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, const PxVehicleWheelsSimData& wheelsData); + + /** + \brief Allocate and set up a vehicle using simulation data for the wheels. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheels::setWheelShapeMapping. + \return The instantiated vehicle. + @see allocate, free, setToRestState, PxVehicleWheels::setWheelShapeMapping + */ + static PxVehicleNoDrive* create + (PxPhysics* physics, PxRigidDynamic* vehActor, const PxVehicleWheelsSimData& wheelsData); + + /** + \brief Set a vehicle to its rest state. Aside from the rigid body transform, this will set the vehicle and rigid body + to the state they were in immediately after setup or create. + \note Calling setToRestState invalidates the cached raycast hit planes under each wheel meaning that suspension line + raycasts need to be performed at least once with PxVehicleSuspensionRaycasts before calling PxVehicleUpdates. + @see setup, create, PxVehicleSuspensionRaycasts, PxVehicleUpdates + */ + void setToRestState(); + + /** + \brief Set the brake torque to be applied to a specific wheel + + \note The applied brakeTorque persists until the next call to setBrakeTorque + + \note The brake torque is specified in Newton metres. + + \param[in] id is the wheel being given the brake torque + \param[in] brakeTorque is the value of the brake torque + */ + void setBrakeTorque(const PxU32 id, const PxReal brakeTorque); + + /** + \brief Set the drive torque to be applied to a specific wheel + + \note The applied driveTorque persists until the next call to setDriveTorque + + \note The brake torque is specified in Newton metres. + + \param[in] id is the wheel being given the brake torque + \param[in] driveTorque is the value of the brake torque + */ + void setDriveTorque(const PxU32 id, const PxReal driveTorque); + + /** + \brief Set the steer angle to be applied to a specific wheel + + \note The applied steerAngle persists until the next call to setSteerAngle + + \note The steer angle is specified in radians. + + \param[in] id is the wheel being given the steer angle + \param[in] steerAngle is the value of the steer angle in radians. + */ + void setSteerAngle(const PxU32 id, const PxReal steerAngle); + + /** + \brief Get the brake torque that has been applied to a specific wheel + \param[in] id is the wheel being queried for its brake torque + \return The brake torque applied to the queried wheel. + */ + PxReal getBrakeTorque(const PxU32 id) const; + + /** + \brief Get the drive torque that has been applied to a specific wheel + \param[in] id is the wheel being queried for its drive torque + \return The drive torque applied to the queried wheel. + */ + PxReal getDriveTorque(const PxU32 id) const; + + /** + \brief Get the steer angle that has been applied to a specific wheel + \param[in] id is the wheel being queried for its steer angle + \return The steer angle (in radians) applied to the queried wheel. + */ + PxReal getSteerAngle(const PxU32 id) const; + +private: + + PxReal* mSteerAngles; + PxReal* mDriveTorques; + PxReal* mBrakeTorques; + +#if PX_P64_FAMILY + PxU32 mPad[2]; +#else + PxU32 mPad[1]; +#endif + + /** + \brief Test if the instanced dynamics and configuration data has legal values. + */ + bool isValid() const; + + +//serialization +public: + PxVehicleNoDrive(PxBaseFlags baseFlags) : PxVehicleWheels(baseFlags) {} + virtual void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext&); + static PxVehicleNoDrive* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + virtual const char* getConcreteTypeName() const { return "PxVehicleNoDrive"; } + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleNoDrive", name) || PxBase::isKindOf(name); } + PxU32 getNbSteerAngle() const { return mWheelsSimData.getNbWheels(); } + PxU32 getNbDriveTorque() const { return mWheelsSimData.getNbWheels(); } + PxU32 getNbBrakeTorque() const { return mWheelsSimData.getNbWheels(); } +protected: + PxVehicleNoDrive(); + ~PxVehicleNoDrive() {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleNoDrive) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_NO_DRIVE_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleSDK.h b/src/PhysX/physx/include/vehicle/PxVehicleSDK.h new file mode 100644 index 000000000..01924b203 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleSDK.h @@ -0,0 +1,237 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_SDK_H +#define PX_VEHICLE_SDK_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/Px.h" +#include "common/PxTypeInfo.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysics; +class PxSerializationRegistry; + +/** +\brief Initialize the PhysXVehicle library. + +Call this before using any of the vehicle functions. + +\param physics The PxPhysics instance. +\param serializationRegistry PxSerializationRegistry instance, if NULL vehicle serialization is not supported. + +\note This function must be called after PxFoundation and PxPhysics instances have been created. +\note If a PxSerializationRegistry instance is specified then PhysXVehicle is also dependent on PhysXExtensions. + +@see PxCloseVehicleSDK +*/ +PX_C_EXPORT bool PX_CALL_CONV PxInitVehicleSDK(PxPhysics& physics, PxSerializationRegistry* serializationRegistry = NULL); + + +/** +\brief Shut down the PhysXVehicle library. + +Call this function as part of the physx shutdown process. + +\param serializationRegistry PxSerializationRegistry instance, if non-NULL must be the same as passed into PxInitVehicleSDK. + +\note This function must be called prior to shutdown of PxFoundation and PxPhysics. +\note If the PxSerializationRegistry instance is specified this function must additionally be called prior to shutdown of PhysXExtensions. + +@see PxInitVehicleSDK +*/ +PX_C_EXPORT void PX_CALL_CONV PxCloseVehicleSDK(PxSerializationRegistry* serializationRegistry = NULL); + + +/** +\brief This number is the maximum number of wheels allowed for a vehicle. +*/ +#define PX_MAX_NB_WHEELS (20) + + +/** +\brief Compiler setting to enable recording of telemetry data + +@see PxVehicleUpdateSingleVehicleAndStoreTelemetryData, PxVehicleTelemetryData +*/ +#define PX_DEBUG_VEHICLE_ON (1) + + +/** +@see PxVehicleDrive4W, PxVehicleDriveTank, PxVehicleDriveNW, PxVehicleNoDrive, PxVehicleWheels::getVehicleType +*/ +struct PxVehicleTypes +{ + enum Enum + { + eDRIVE4W=0, + eDRIVENW, + eDRIVETANK, + eNODRIVE, + eUSER1, + eUSER2, + eUSER3, + eMAX_NB_VEHICLE_TYPES + }; +}; + + +/** +\brief An enumeration of concrete vehicle classes inheriting from PxBase. +\note This enum can be used to identify a vehicle object stored in a PxCollection. +@see PxBase, PxTypeInfo, PxBase::getConcreteType +*/ +struct PxVehicleConcreteType +{ + enum Enum + { + eVehicleNoDrive = PxConcreteType::eFIRST_VEHICLE_EXTENSION, + eVehicleDrive4W, + eVehicleDriveNW, + eVehicleDriveTank + }; +}; + + +/** +\brief Set the basis vectors of the vehicle simulation + +Default values PxVec3(0,1,0), PxVec3(0,0,1) + +Call this function before using PxVehicleUpdates unless the default values are correct. +*/ +void PxVehicleSetBasisVectors(const PxVec3& up, const PxVec3& forward); + + +/** +@see PxVehicleSetUpdateMode +*/ +struct PxVehicleUpdateMode +{ + enum Enum + { + eVELOCITY_CHANGE, + eACCELERATION + }; +}; + + +/** +\brief Set the effect of PxVehicleUpdates to be either to modify each vehicle's rigid body actor + +with an acceleration to be applied in the next PhysX SDK update or as an immediate velocity modification. + +Default behavior is immediate velocity modification. + +Call this function before using PxVehicleUpdates for the first time if the default is not the desired behavior. + +@see PxVehicleUpdates +*/ +void PxVehicleSetUpdateMode(PxVehicleUpdateMode::Enum vehicleUpdateMode); + +/** + +\brief Set threshold angles that are used to determine if a wheel hit is to be resolved by vehicle suspension or by rigid body collision. + + +\note ^ + N ___ + |** + ** + ** + %%% %%% ** + %%% %%% ** / + / + %%% %%% / + / + %%% %%% / + C / + %%% | ** %%% / + | ** / + %%% | **%%%/ + | X** + %%% | %%% / **_| ^ + | / D + %%% | %%% / + | / + | / + | / + | + ^ | + S \|/ + +The diagram above depicts a wheel centered at "C" that has hit an inclined plane at point "X". +The inclined plane has unit normal "N", while the suspension direction has unit vector "S". +The unit vector from the wheel center to the hit point is "D". +Hit points are analyzed by comparing the unit vectors D and N with the suspension direction S. +This analysis is performed in the contact modification callback PxVehicleModifyWheelContacts (when enabled) and in +PxVehicleUpdates (when non-blocking sweeps are enabled). +If the angle between D and S is less than pointRejectAngle the hit is accepted by the suspension in PxVehicleUpdates and rejected +by the contact modification callback PxVehicleModifyWheelContacts. +If the angle between -N and S is less than normalRejectAngle the hit is accepted by the suspension in PxVehicleUpdates and rejected +by the contact modification callback PxVehicleModifyWheelContacts. + +\param pointRejectAngle is the threshold angle used when comparing the angle between D and S. + +\param normalRejectAngle is the threshold angle used when comparing the angle between -N and S. + +\note PxVehicleUpdates ignores the rejection angles for raycasts and for sweeps that return blocking hits. + +\note Both angles have default values of Pi/4. + +@see PxVehicleSuspensionSweeps, PxVehicleModifyWheelContacts +*/ +void PxVehicleSetSweepHitRejectionAngles(const PxF32 pointRejectAngle, const PxF32 normalRejectAngle); + + +/** +\brief Determine the maximum acceleration experienced by PxRigidDynamic instances that are found to be in contact +with a wheel. + +\note Newton's Third Law states that every force has an equal and opposite force. As a consequence, forces applied to +the suspension must be applied to dynamic objects that lie under the wheel. This can lead to instabilities, particularly +when a heavy wheel is driving on a light object. The value of maxHitActorAcceleration clamps the applied force so that it never +generates an acceleration greater than the specified value. + +\note Default value of maxHitActorAcceleration is PX_MAX_REAL +*/ +void PxVehicleSetMaxHitActorAcceleration(const PxF32 maxHitActorAcceleration); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_SDK_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleShaders.h b/src/PhysX/physx/include/vehicle/PxVehicleShaders.h new file mode 100644 index 000000000..915ab0af5 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleShaders.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_SHADERS_H +#define PX_VEHICLE_SHADERS_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Prototype of shader function that is used to compute wheel torque and tire forces. +\param[in] shaderData is the shader data for the tire being processed. The shader data describes the tire data in the format required by the tire model that is implemented by the shader function. +\param[in] tireFriction is the value of friction for the contact between the tire and the ground. +\param[in] longSlip is the value of longitudinal slip experienced by the tire. +\param[in] latSlip is the value of lateral slip experienced by the tire. +\param[in] camber is the camber angle of the tire in radians. +\param[in] wheelOmega is the rotational speed of the wheel. +\param[in] wheelRadius is the distance from the tire surface to the center of the wheel. +\param[in] recipWheelRadius is the reciprocal of wheelRadius. +\param[in] restTireLoad is the load force experienced by the tire when the vehicle is at rest. +\param[in] normalisedTireLoad is a pre-computed value equal to the load force on the tire divided by restTireLoad. +\param[in] tireLoad is the load force currently experienced by the tire (= restTireLoad*normalisedTireLoad) +\param[in] gravity is the magnitude of gravitational acceleration. +\param[in] recipGravity is the reciprocal of the magnitude of gravitational acceleration. +\param[out] wheelTorque is the torque that is to be applied to the wheel around the wheel's axle. +\param[out] tireLongForceMag is the magnitude of the longitudinal tire force to be applied to the vehicle's rigid body. +\param[out] tireLatForceMag is the magnitude of the lateral tire force to be applied to the vehicle's rigid body. +\param[out] tireAlignMoment is the aligning moment of the tire that is to be applied to the vehicle's rigid body (not currently used). +@see PxVehicleWheelsDynData::setTireForceShaderFunction, PxVehicleWheelsDynData::setTireForceShaderData +*/ +typedef void (*PxVehicleComputeTireForce) +(const void* shaderData, + const PxF32 tireFriction, + const PxF32 longSlip, const PxF32 latSlip, const PxF32 camber, + const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 recipWheelRadius, + const PxF32 restTireLoad, const PxF32 normalisedTireLoad, const PxF32 tireLoad, + const PxF32 gravity, const PxF32 recipGravity, + PxF32& wheelTorque, PxF32& tireLongForceMag, PxF32& tireLatForceMag, PxF32& tireAlignMoment); + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_SHADERS_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h b/src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h new file mode 100644 index 000000000..dd4f05c30 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h @@ -0,0 +1,223 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_TIREFRICTION_H +#define PX_VEHICLE_TIREFRICTION_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxMaterial; + +/** +\brief Driving surface type. Each PxMaterial is associated with a corresponding PxVehicleDrivableSurfaceType. +@see PxMaterial, PxVehicleDrivableSurfaceToTireFrictionPairs +*/ +struct PxVehicleDrivableSurfaceType +{ + enum + { + eSURFACE_TYPE_UNKNOWN=0xffffffff + }; + PxU32 mType; +}; + +/** +\brief Friction for each combination of driving surface type and tire type. +@see PxVehicleDrivableSurfaceType, PxVehicleTireData::mType +*/ +class PxVehicleDrivableSurfaceToTireFrictionPairs +{ +public: + + friend class VehicleSurfaceTypeHashTable; + + enum + { + eMAX_NB_SURFACE_TYPES=256 + }; + + /** + \brief Allocate the memory for a PxVehicleDrivableSurfaceToTireFrictionPairs instance + that can hold data for combinations of tire type and surface type with up to maxNbTireTypes types of tire and maxNbSurfaceTypes types of surface. + + \param[in] maxNbTireTypes is the maximum number of allowed tire types. + \param[in] maxNbSurfaceTypes is the maximum number of allowed surface types. Must be less than or equal to eMAX_NB_SURFACE_TYPES + + \return a PxVehicleDrivableSurfaceToTireFrictionPairs instance that can be reused later with new type and friction data. + + @see setup + */ + static PxVehicleDrivableSurfaceToTireFrictionPairs* allocate + (const PxU32 maxNbTireTypes, const PxU32 maxNbSurfaceTypes); + + /** + \brief Set up a PxVehicleDrivableSurfaceToTireFrictionPairs instance for combinations of nbTireTypes tire types and nbSurfaceTypes surface types. + + \param[in] nbTireTypes is the number of different types of tire. This value must be less than or equal to maxNbTireTypes specified in allocate(). + \param[in] nbSurfaceTypes is the number of different types of surface. This value must be less than or equal to maxNbSurfaceTypes specified in allocate(). + \param[in] drivableSurfaceMaterials is an array of PxMaterial pointers of length nbSurfaceTypes. + \param[in] drivableSurfaceTypes is an array of PxVehicleDrivableSurfaceType instances of length nbSurfaceTypes. + + \note If the pointer to the PxMaterial that touches the tire is found in drivableSurfaceMaterials[x] then the surface type is drivableSurfaceTypes[x].mType + and the friction is the value that is set with setTypePairFriction(drivableSurfaceTypes[x].mType, PxVehicleTireData::mType, frictionValue). + + \note A friction value of 1.0 will be assigned as default to each combination of tire and surface type. To override this use setTypePairFriction. + @see release, setTypePairFriction, getTypePairFriction, PxVehicleTireData.mType + */ + void setup + (const PxU32 nbTireTypes, const PxU32 nbSurfaceTypes, + const PxMaterial** drivableSurfaceMaterials, const PxVehicleDrivableSurfaceType* drivableSurfaceTypes); + + /** + \brief Deallocate a PxVehicleDrivableSurfaceToTireFrictionPairs instance + */ + void release(); + + /** + \brief Set the friction for a specified pair of tire type and drivable surface type. + + \param[in] surfaceType describes the surface type + \param[in] tireType describes the tire type. + \param[in] value describes the friction coefficient for the combination of surface type and tire type. + */ + void setTypePairFriction(const PxU32 surfaceType, const PxU32 tireType, const PxReal value); + + /** + \brief Return the friction for a specified combination of surface type and tire type. + \return The friction for a specified combination of surface type and tire type. + \note The final friction value used by the tire model is the value returned by getTypePairFriction + multiplied by the value computed from PxVehicleTireData::mFrictionVsSlipGraph + @see PxVehicleTireData::mFrictionVsSlipGraph + */ + PxReal getTypePairFriction(const PxU32 surfaceType, const PxU32 tireType) const; + + /** + \brief Return the maximum number of surface types + \return The maximum number of surface types + @see allocate + */ + PxU32 getMaxNbSurfaceTypes() const {return mMaxNbSurfaceTypes;} + + /** + \brief Return the maximum number of tire types + \return The maximum number of tire types + @see allocate + */ + PxU32 getMaxNbTireTypes() const {return mMaxNbTireTypes;} + +private: + + /** + \brief Ptr to base address of a 2d PxReal array with dimensions [mNbSurfaceTypes][mNbTireTypes] + + \note Each element of the array describes the maximum friction provided by a surface type-tire type combination. + eg the friction corresponding to a combination of surface type x and tire type y is mPairs[x][y] + */ + PxReal* mPairs; + + /** + \brief Ptr to 1d array of material ptrs that is of length mNbSurfaceTypes. + + \note If the PxMaterial that touches the tire corresponds to mDrivableSurfaceMaterials[x] then the drivable surface + type is mDrivableSurfaceTypes[x].mType and the friction for that contact is mPairs[mDrivableSurfaceTypes[x].mType][y], + assuming a tire type y. + + \note If the PxMaterial that touches the tire is not found in mDrivableSurfaceMaterials then the friction is + mPairs[0][y], assuming a tire type y. + */ + const PxMaterial** mDrivableSurfaceMaterials; + + /** + \brief Ptr to 1d array of PxVehicleDrivableSurfaceType that is of length mNbSurfaceTypes. + + \note If the PxMaterial that touches the tire is found in mDrivableSurfaceMaterials[x] then the drivable surface + type is mDrivableSurfaceTypes[x].mType and the friction for that contact is mPairs[mDrivableSurfaceTypes[x].mType][y], + assuming a tire type y. + + \note If the PxMaterial that touches the tire is not found in mDrivableSurfaceMaterials then the friction is + mPairs[0][y], assuming a tire type y. + */ + PxVehicleDrivableSurfaceType* mDrivableSurfaceTypes; + + /** + \brief Number of different driving surface types. + + \note mDrivableSurfaceMaterials and mDrivableSurfaceTypes are both 1d arrays of length mMaxNbSurfaceTypes. + + \note mNbSurfaceTypes must be less than or equal to mMaxNbSurfaceTypes. + */ + PxU32 mNbSurfaceTypes; + + /** + \brief Maximum number of different driving surface types. + + \note mMaxNbSurfaceTypes must be less than or equal to eMAX_NB_SURFACE_TYPES. + */ + PxU32 mMaxNbSurfaceTypes; + + /** + \brief Number of different tire types. + + \note Tire types stored in PxVehicleTireData.mType + */ + PxU32 mNbTireTypes; + + /** + \brief Maximum number of different tire types. + + \note Tire types stored in PxVehicleTireData.mType + */ + PxU32 mMaxNbTireTypes; + + +#if !PX_P64_FAMILY + PxU32 mPad[1]; +#else + PxU32 mPad[2]; +#endif + + PxVehicleDrivableSurfaceToTireFrictionPairs(){} + ~PxVehicleDrivableSurfaceToTireFrictionPairs(){} +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDrivableSurfaceToTireFrictionPairs) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_TIREFRICTION_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUpdate.h b/src/PhysX/physx/include/vehicle/PxVehicleUpdate.h new file mode 100644 index 000000000..381a01ead --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleUpdate.h @@ -0,0 +1,579 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_UPDATE_H +#define PX_VEHICLE_UPDATE_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleSDK.h" +#include "vehicle/PxVehicleTireFriction.h" +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxMemory.h" +#include "foundation/PxTransform.h" +#include "PxBatchQueryDesc.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxBatchQuery; + class PxContactModifyPair; + class PxVehicleWheels; + class PxVehicleDrivableSurfaceToTireFrictionPairs; + class PxVehicleTelemetryData; + + /** + \brief Structure containing data describing the non-persistent state of each suspension/wheel/tire unit. + This structure is filled out in PxVehicleUpdates and PxVehicleUpdateSingleVehicleAndStoreTelemetryData + @see PxVehicleUpdates, PxVehicleUpdateSingleVehicleAndStoreTelemetryData + */ + struct PxWheelQueryResult + { + PxWheelQueryResult() + { + PxMemZero(this, sizeof(PxWheelQueryResult)); + isInAir=true; + tireSurfaceType = PxU32(PxVehicleDrivableSurfaceType::eSURFACE_TYPE_UNKNOWN); + localPose = PxTransform(PxIdentity); + } + + /** + \brief Start point of suspension line raycast/sweep used in the raycast/sweep completed immediately before PxVehicleUpdates. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then (0,0,0) is stored. + @see PxVehicleSuspensionRaycasts, PxVehicleSuspensionRaycasts + */ + PxVec3 suspLineStart; + + /** + \brief Directions of suspension line raycast/sweep used in the raycast/sweep completed immediately before PxVehicleUpdates. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then (0,0,0) is stored. + @see PxVehicleSuspensionRaycasts, PxVehicleSuspensionRaycasts + */ + PxVec3 suspLineDir; + + /** + \brief Lengths of suspension line raycast/sweep used in raycast/sweep completed immediately before PxVehicleUpdates. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then 0 is stored. + @see PxVehicleSuspensionRaycasts, PxVehicleSuspensionRaycasts + */ + PxReal suspLineLength; + + /** + \brief If suspension travel limits forbid the wheel from touching the drivable surface then isInAir is true. + \note If the wheel can be placed on the contact plane of the most recent suspension line raycast/sweep then isInAir is false. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then isInAir + is computed using the contact plane that was hit by the most recent suspension line raycast/sweep. + */ + bool isInAir; + + /** + \brief PxActor instance of the driving surface under the corresponding vehicle wheel. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireContactActor is NULL. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then NULL is stored. + */ + PxActor* tireContactActor; + + /** + \brief PxShape instance of the driving surface under the corresponding vehicle wheel. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireContactShape is NULL. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then NULL is stored. + */ + PxShape* tireContactShape; + + /** + \brief PxMaterial instance of the driving surface under the corresponding vehicle wheel. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireSurfaceMaterial is NULL. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then NULL is stored. + */ + const PxMaterial* tireSurfaceMaterial; + + /** + \brief Surface type integer that corresponds to the mapping between tireSurfaceMaterial and integer as + described in PxVehicleDrivableSurfaceToTireFrictionPairs. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireSurfaceType is + PxVehicleDrivableSurfaceType::eSURFACE_TYPE_UNKNOWN. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then + PxVehicleDrivableSurfaceType::eSURFACE_TYPE_UNKNOWN is stored. + @see PxVehicleDrivableSurfaceToTireFrictionPairs + */ + PxU32 tireSurfaceType; + + /** + \brief Point on the drivable surface hit by the most recent suspension raycast or sweep. + \note If suspension travel limits forbid the wheel from touching the drivable surface then the contact point is (0,0,0). + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then (0,0,0) is stored. + */ + PxVec3 tireContactPoint; + + /** + \brief Normal on the drivable surface at the hit point of the most recent suspension raycast or sweep. + \note If suspension travel limits forbid the wheel from touching the drivable surface then the contact normal is (0,0,0). + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then (0,0,0) is stored. + */ + PxVec3 tireContactNormal; + + /** + \brief Friction experienced by the tire for the combination of tire type and surface type after accounting + for the friction vs slip graph. + \note If suspension travel limits forbid the wheel from touching the drivable surface then the tire friction is 0. + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + stored tire friction is the value computed in PxVehicleUpdates that immediately followed the last raycast or sweep. + @see PxVehicleDrivableSurfaceToTireFrictionPairs, PxVehicleTireData + */ + PxReal tireFriction; + + /** + \brief Compression of the suspension spring. + \note If suspension travel limits forbid the wheel from touching the drivable surface then the jounce is -PxVehicleSuspensionData.mMaxDroop + The jounce can never exceed PxVehicleSuspensionData.mMaxCompression. Positive values result when the suspension is compressed from + the rest position, while negative values mean the suspension is elongated from the rest position. + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + suspension compression is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + */ + PxReal suspJounce; + + /** + \brief Magnitude of force applied by the suspension spring along the direction of suspension travel. + \note If suspension travel limits forbid the wheel from touching the drivable surface then the force is 0 + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + suspension spring force is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + @see PxVehicleWheelsSimData::getSuspTravelDirection + */ + PxReal suspSpringForce; + + /** + \brief Forward direction of the wheel/tire accounting for steer/toe/camber angle projected on to the contact plane of the drivable surface. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireLongitudinalDir is (0,0,0) + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + tire longitudinal direction is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + */ + PxVec3 tireLongitudinalDir; + + /** + \brief Lateral direction of the wheel/tire accounting for steer/toe/camber angle projected on to the contact plan of the drivable surface. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireLateralDir is (0,0,0) + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + tire lateral direction is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + */ + PxVec3 tireLateralDir; + + /** + \brief Longitudinal slip of the tire. + \note If suspension travel limits forbid the wheel from touching the drivable surface then longitudinalSlip is 0.0 + \note The longitudinal slip is approximately (w*r - vz) / PxAbs(vz) where w is the angular speed of the wheel, r is the radius of the wheel, and + vz component of rigid body velocity computed at the wheel base along the longitudinal direction of the tire. + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + tire longitudinal slip is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + */ + PxReal longitudinalSlip; + + /** + \brief Lateral slip of the tire. + \note If suspension travel limits forbid the wheel from touching the drivable surface then lateralSlip is 0.0 + \note The lateral slip angle is approximately PxAtan(vx / PxAbs(vz)) where vx and vz are the components of rigid body velocity at the wheel base + along the wheel's lateral and longitudinal directions, respectively. + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + tire lateral slip is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + */ + PxReal lateralSlip; + + /** + \brief Steer angle of the wheel about the "up" vector accounting for input steer and toe and, if applicable, Ackermann steer correction. + @see PxVehicleWheelData::mToeAngle + */ + PxReal steerAngle; + + /** + \brief Local pose of the wheel. + */ + PxTransform localPose; + }; + + struct PxVehicleWheelQueryResult + { + /** + \brief Pointer to an PxWheelQueryResult buffer of length nbWheelQueryResults + The wheelQueryResults buffer must persist until the end of PxVehicleUpdates + A NULL pointer is permitted. + The wheelQueryResults buffer is left unmodified in PxVehicleUpdates for vehicles with sleeping rigid bodies + whose control inputs indicate they should remain inert. + @see PxVehicleUpdates + */ + PxWheelQueryResult* wheelQueryResults; + + /** + \brief The length of the wheelQueryResults buffer. This value corresponds to the + number of wheels in the associated vehicle in PxVehicleUpdates. + */ + PxU32 nbWheelQueryResults; + }; + + /** + \brief Structure containing data that is computed for a wheel during concurrent calls to PxVehicleUpdates + but which cannot be safely concurrently applied. + + @see PxVehicleUpdates, PxVehiclePostUpdates, PxVehicleConcurrentUpdate + */ + struct PxVehicleWheelConcurrentUpdateData + { + friend class PxVehicleUpdate; + + PxVehicleWheelConcurrentUpdateData() + : localPose(PxTransform(PxIdentity)), + hitActor(NULL), + hitActorForce(PxVec3(0,0,0)), + hitActorForcePosition(PxVec3(0,0,0)) + { + } + + private: + + PxTransform localPose; + PxRigidDynamic* hitActor; + PxVec3 hitActorForce; + PxVec3 hitActorForcePosition; + }; + + /** + \brief Structure containing data that is computed for a vehicle and its wheels during concurrent calls to PxVehicleUpdates + but which cannot be safely concurrently applied. + + @see PxVehicleUpdates, PxVehiclePostUpdates, PxVehicleWheelConcurrentUpdateData + */ + + struct PxVehicleConcurrentUpdateData + { + friend class PxVehicleUpdate; + + PxVehicleConcurrentUpdateData() + : concurrentWheelUpdates(NULL), + nbConcurrentWheelUpdates(0), + linearMomentumChange(PxVec3(0,0,0)), + angularMomentumChange(PxVec3(0,0,0)), + staySleeping(false), + wakeup(false) + { + } + + /** + \brief Pointer to an PxVehicleWheelConcurrentUpdate buffer of length nbConcurrentWheelUpdates + The concurrentWheelUpdates buffer must persist until the end of PxVehiclePostUpdates + A NULL pointer is not permitted. + @see PxVehicleUpdates, PxVehiclePostUpdates + */ + PxVehicleWheelConcurrentUpdateData* concurrentWheelUpdates; + + /** + \brief The length of the concurrentWheelUpdates buffer. This value corresponds to the + number of wheels in the associated vehicle passed to PxVehicleUpdates. + */ + PxU32 nbConcurrentWheelUpdates; + + private: + + PxVec3 linearMomentumChange; + PxVec3 angularMomentumChange; + bool staySleeping; + bool wakeup; + }; + + /** + \brief Perform raycasts for all suspension lines for all vehicles. + + \param[in] batchQuery is a PxBatchQuery instance used to specify shader data and functions for the raycast scene queries. + + \param[in] nbVehicles is the number of vehicles in the vehicles array. + + \param[in] vehicles is an array of all vehicles that are to have a raycast issued from each wheel. + + \param[in] nbSceneQueryResults must be greater than or equal to the total number of wheels of all the vehicles in the vehicles array; that is, + sceneQueryResults must have dimensions large enough for one raycast hit result per wheel for all the vehicles in the vehicles array. + + \param[in] sceneQueryResults must persist without being overwritten until the end of the next PxVehicleUpdates call. + + \param[in] vehiclesToRaycast is an array of bools of length nbVehicles that is used to decide if raycasts will be performed for the corresponding vehicle + in the vehicles array. If vehiclesToRaycast[i] is true then suspension line raycasts will be performed for vehicles[i]. If vehiclesToRaycast[i] is + false then suspension line raycasts will not be performed for vehicles[i]. + + \note If vehiclesToRaycast is NULL then raycasts are performed for all vehicles in the vehicles array. + + \note If vehiclesToRaycast[i] is false then the vehicle stored in vehicles[i] will automatically use the raycast or sweep hit planes recorded by the most recent + suspension sweeps or raycasts for that vehicle. For vehicles far from the camera or not visible on the screen it can be + optimal to only perform suspension line raycasts every Nth update rather than every single update. The accuracy of the cached contact plane + naturally diminishes as N increase, meaning that wheels might start to hover or intersect the ground for large values of N or even with values close to 1 in + conjunction with large vehicle speeds and/or geometry that has low spatial coherence. + + \note Calling setToRestState invalidates any cached hit planes. Prior to calling PxVehicleUpdates each vehicle needs to perform suspension line raycasts + or sweeps at least once after instantiation and at least once after calling setToRestState. + + \note Each raycast casts along the suspension travel direction from the position of the top of the wheel at maximum suspension compression + to the position of the base of the wheel at maximum droop. Raycasts that start inside a PxShape are subsequently ignored by the + corresponding vehicle. + + \note Only blocking hits are supported (PxQueryHitType::eBLOCK). + + @see PxVehicleDrive4W::setToRestState, PxVehicleDriveNW::setToRestState, PxVehicleDriveTank::setToRestState, PxVehicleNoDrive::setToRestState + */ + void PxVehicleSuspensionRaycasts + (PxBatchQuery* batchQuery, + const PxU32 nbVehicles, PxVehicleWheels** vehicles, + const PxU32 nbSceneQueryResults, PxRaycastQueryResult* sceneQueryResults, + const bool* vehiclesToRaycast = NULL); + + + /** + \brief Perform sweeps for all suspension lines for all vehicles. + + \param[in] batchQuery is a PxBatchQuery instance used to specify shader data and functions for the sweep scene queries. + + \param[in] nbVehicles is the number of vehicles in the vehicles array. + + \param[in] vehicles is an array of all vehicles that are to have a sweep issued from each wheel. + + \param[in] nbSceneQueryResults must be greater than or equal to the total number of wheels of all the vehicles in the vehicles array; that is, + sceneQueryResults must have dimensions large enough for one sweep hit result per wheel for all the vehicles in the vehicles array. + + \param[in] sceneQueryResults must persist without being overwritten until the end of the next PxVehicleUpdates call. + + \param[in] nbHitsPerQuery is the maximum numbers of hits that will be returned for each query. + + \param[in] vehiclesToSweep is an array of bools of length nbVehicles that is used to decide if sweeps will be performed for the corresponding vehicle + in the vehicles array. If vehiclesToSweep[i] is true then suspension sweeps will be performed for vehicles[i]. If vehiclesToSweep[i] is + false then suspension sweeps will not be performed for vehicles[i]. + + \param[in] sweepWidthScale scales the geometry of the wheel used in the sweep. Values < 1 result in a thinner swept wheel, while values > 1 result in a fatter swept wheel. + + \param[in] sweepRadiusScale scales the geometry of the wheel used in the sweep. Values < 1 result in a larger swept wheel, while values > 1 result in a smaller swept wheel. + + \note If vehiclesToSweep is NULL then sweeps are performed for all vehicles in the vehicles array. + + \note If vehiclesToSweep[i] is false then the vehicle stored in vehicles[i] will automatically use the most recent sweep or raycast hit planes + recorded by the most recent suspension sweeps or raycasts for that vehicle. For vehicles far from the camera or not visible on the screen it can be + optimal to only perform suspension queries every Nth update rather than every single update. The accuracy of the cached contact plane + naturally diminishes as N increase, meaning that wheels might start to hover or intersect the ground for large values of N or even with values close to 1 in + conjunction with large vehicle speeds and/or geometry that has low spatial coherence. + + \note Calling setToRestState invalidates any cached hit planes. Prior to calling PxVehicleUpdates each vehicle needs to perform suspension raycasts + or sweeps at least once after instantiation and at least once after calling setToRestState. + + \note Each sweep casts the wheel's shape along the suspension travel direction from the position of the top of the wheel at maximum suspension compression + to the position of the base of the wheel at maximum droop. Sweeps that start inside a PxShape are subsequently ignored by the + corresponding vehicle. + + \note A scale can be applied to the shape so that a modified shape is swept through the scene. The parameters sweepWidthScale and sweepRadiusScale scale the + swept wheel shape in the width and radial directions. It is sometimes a good idea to sweep a thinner wheel to allow contact with other dynamic actors to be resolved + first before attempting to drive on them. + + \note Blocking hits (PxQueryHitType::eBLOCK) and non-blocking hits (PxQueryHitType::TOUCH) are supported. If the pre-and post-filter functions of the PxBatchQuery + instance are set up to return blocking hits it is recommended to set nbHitsPerQuery = 1. If the filter functions returns touch hits then it is recommended to + set nbHitsPerQuery > 1. The exact value depends on the expected complexity of the geometry that lies under the wheel. For complex geometry, especially with dynamic + objects, it is recommended to use non-blocking hits. The vehicle update function will analyze all returned hits and choose the most appropriate using the thresholds + set in PxVehicleSetSweepHitRejectionAngles. + + @see PxVehicleDrive4W::setToRestState, PxVehicleDriveNW::setToRestState, PxVehicleDriveTank::setToRestState, PxVehicleNoDrive::setToRestState + + @see PxBatchQuery::sweep + + @see PxVehicleSetSweepHitRejectionAngles + */ + void PxVehicleSuspensionSweeps + (PxBatchQuery* batchQuery, + const PxU32 nbVehicles, PxVehicleWheels** vehicles, + const PxU32 nbSceneQueryResults, PxSweepQueryResult* sceneQueryResults, const PxU16 nbHitsPerQuery, + const bool* vehiclesToSweep = NULL, + const PxF32 sweepWidthScale = 1.0f, const PxF32 sweepRadiusScale = 1.0f); + + /** + \brief A function called from PxContactModifyCallback::onContactModify. The function determines if rigid body contact points + recorded for the wheel's PxShape are likely to be duplicated and resolved by the wheel's suspension raycast. Contact points that will be + resolved by the suspension are ignored. Contact points that are accepted (rather than ignored) are modified to account for the effect of the + suspension geometry and the angular speed of the wheel. + + \param[in] vehicle is a reference to the PxVehicleWheels instance that owns the wheel + + \param[in] wheelId is the id of the wheel + + \param[in] wheelTangentVelocityMultiplier determines the amount of wheel angular velocity that is used to modify the target relative velocity of the contact. + The target relative velocity is modified by adding a vector equal to the tangent velocity of the rotating wheel at the contact point and scaled by + wheelTangentVelocityMultiplier. The value of wheelTangentVelocityMultiplier is limited to the range (0,1). Higher values mimic higher values of friction + and tire load, while lower values mimic lower values of friction and tire load. + + \param[in] maxImpulse determines the maximum impulse strength that the contacts can apply when a wheel is in contact with a PxRigidDynamic. This value is ignored for + contacts with PxRigidStatic instances. + + \param[in,out] contactModifyPair describes the set of contacts involving the PxShape of the specified wheel and one other shape. The contacts in the contact set are + ignored or modified as required. + + \note[in] Contact points are accepted or rejected using the threshold angles specified in the function PxVehicleSetSweepHitRejectionAngles. + + \note If a contact point is not rejected it is modified to account for the wheel rotation speed. + + \note Set maxImpulse to PX_MAX_F32 to allow any impulse value to be applied. + + \note Reduce maxImpulse if the wheels are frequently colliding with light objects with mass much less than the vehicle's mass. + Reducing this value encourages numerical stability. + + @see PxContactModifyCallback::onContactModify, PxVehicleSetSweepHitRejectionAngles + */ + PxU32 PxVehicleModifyWheelContacts + (const PxVehicleWheels& vehicle, const PxU32 wheelId, + const PxF32 wheelTangentVelocityMultiplier, const PxReal maxImpulse, + PxContactModifyPair& contactModifyPair); + + + /** + \brief Update an array of vehicles by either applying an acceleration to the rigid body actor associated with + each vehicle or by an immediate update of the velocity of the actor. + + \note The update mode (acceleration or velocity change) can be selected with PxVehicleSetUpdateMode. + + \param[in] timestep is the timestep of the update + + \param[in] gravity is the value of gravitational acceleration + + \param[in] vehicleDrivableSurfaceToTireFrictionPairs describes the mapping between each PxMaterial ptr and an integer representing a + surface type. It also stores the friction value for each combination of surface and tire type. + + \param[in] nbVehicles is the number of vehicles pointers in the vehicles array + + \param[in,out] vehicles is an array of length nbVehicles containing all vehicles to be updated by the specified timestep + + \param[out] vehicleWheelQueryResults is an array of length nbVehicles storing the wheel query results of each corresponding vehicle and wheel in the + vehicles array. A NULL pointer is permitted. + + \param[out] vehicleConcurrentUpdates is an array of length nbVehicles. It is only necessary to specify vehicleConcurrentUpdates if PxVehicleUpdates is + called concurrently. The element vehicleWheelQueryResults[i] of the array stores data that is computed for vehicle[i] during PxVehicleUpdates but which + cannot be safely written when concurrently called. The data computed and stored in vehicleConcurrentUpdates must be passed to PxVehiclePostUpdates, where + it is applied to all relevant actors in sequence. A NULL pointer is permitted. + + \note The vehicleWheelQueryResults buffer must persist until the end of PxVehicleUpdates. + + \note The vehicleWheelQueryResults buffer is left unmodified for vehicles with sleeping rigid bodies whose control inputs indicate they should remain inert. + + \note If PxVehicleUpdates is called concurrently then vehicleConcurrentUpdates must be specified. Do not specify vehicleConcurrentUpdates is PxVehicleUpdates + is not called concurrently. + + \note The vehicleConcurrentUpdates buffer must persist until the end of PxVehiclePostUpdate. + + \note If any vehicle has one or more disabled wheels (PxVehicleWheelsSimData::disableWheel) then the disabled wheels must not be associated + with a PxShape (PxVehicleWheelsSimData::setWheelShapeMapping); the differential of the vehicle must be configured so that no drive torque + is delivered to a disabled wheel; and the wheel must have zero rotation speed (PxVehicleWheelsDynData::setWheelRotationSpeed) + + \note PxVehicleUpdates may be called concurrently provided all concurrent calls to PxVehicleUpdates involve only vehicles in the scene specified by PxVehicleUpdateSetScene. + PxVehicleUpdates must never run concurrently with PxVehicleUpdateSingleVehicleAndStoreTelemetryData. + + @see PxVehicleSetUpdateMode, PxVehicleWheelsSimData::disableWheel, PxVehicleWheelsSimData::setWheelShapeMapping, PxVehicleWheelsDynData::setWheelRotationSpeed, + PxVehiclePostUpdates + */ + void PxVehicleUpdates( + const PxReal timestep, const PxVec3& gravity, + const PxVehicleDrivableSurfaceToTireFrictionPairs& vehicleDrivableSurfaceToTireFrictionPairs, + const PxU32 nbVehicles, PxVehicleWheels** vehicles, PxVehicleWheelQueryResult* vehicleWheelQueryResults, PxVehicleConcurrentUpdateData* vehicleConcurrentUpdates = NULL); + + + /** + \brief Apply actor changes that were computed in concurrent calls to PxVehicleUpdates but which could not be safely applied due to the concurrency. + + \param[in] vehicleConcurrentUpdates is an array of length nbVehicles where vehicleConcurrentUpdates[i] contains data describing actor changes that + were computed for vehicles[i] during concurrent calls to PxVehicleUpdates. + + \param[in] nbVehicles is the number of vehicles pointers in the vehicles array + + \param[in,out] vehicles is an array of length nbVehicles containing all vehicles that were partially updated in concurrent calls to PxVehicleUpdates. + + @see PxVehicleUpdates + */ + void PxVehiclePostUpdates( + const PxVehicleConcurrentUpdateData* vehicleConcurrentUpdates, const PxU32 nbVehicles, PxVehicleWheels** vehicles); + + + /** + \brief Shift the origin of vehicles by the specified vector. + + Call this method to adjust the internal data structures of vehicles to reflect the shifted origin location + (the shift vector will get subtracted from all world space spatial data). + + \note It is the user's responsibility to keep track of the summed total origin shift and adjust all input/output to/from PhysXVehicle accordingly. + + \note This call will not automatically shift the PhysX scene and its objects. You need to call PxScene::shiftOrigin() seperately to keep the systems in sync. + + \param[in] shift is the translation vector to shift the origin by. + + \param[in] nbVehicles is the number of vehicles in the vehicles array. + + \param[in,out] vehicles is an array of all vehicles that should be updated to map to the new scene origin. + */ + void PxVehicleShiftOrigin(const PxVec3& shift, const PxU32 nbVehicles, PxVehicleWheels** vehicles); + +#if PX_DEBUG_VEHICLE_ON + /** + \brief Update an single vehicle by either applying an acceleration to the rigid body actor associated with + each vehicle or by an immediate update of the velocity of the actor. Also record telemetry data from the + vehicle so that it may be visualized or queried. + + \note The update mode (acceleration or velocity change) can be selected with PxVehicleSetUpdateMode. + + \param[in] timestep is the timestep of the update + + \param[in] gravity is the value of gravitational acceleration + + \param[in] vehicleDrivableSurfaceToTireFrictionPairs describes the mapping between each PxMaterial ptr and an integer representing a + surface type. It also stores the friction value for each combination of surface and tire type. + + \param[in,out] focusVehicle is the vehicle to be updated and have its telemetry data recorded + + \param[out] vehicleWheelQueryResults is an array of length 1 storing the wheel query results of each wheel of the vehicle/ + A NULL pointer is permitted. + + \param[out] telemetryData is the data structure used to record telemetry data during the update for later query or visualization + + \note The vehicleWheelQueryResults buffer must persist until the end of PxVehicleUpdates + + \note The vehicleWheelQueryResults buffer is left unmodified for vehicles with sleeping rigid bodies whose control inputs indicate they should remain inert. + + \note PxVehicleUpdateSingleVehicleAndStoreTelemetryData is not thread-safe. As a consequence, it must run sequentially and never concurrently with PxVehicleUpdates + + @see PxVehicleSetUpdateMode, PxVehicleTelemetryData + */ + void PxVehicleUpdateSingleVehicleAndStoreTelemetryData + (const PxReal timestep, const PxVec3& gravity, + const PxVehicleDrivableSurfaceToTireFrictionPairs& vehicleDrivableSurfaceToTireFrictionPairs, + PxVehicleWheels* focusVehicle, PxVehicleWheelQueryResult* vehicleWheelQueryResults, + PxVehicleTelemetryData& telemetryData); +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_UPDATE_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUtil.h b/src/PhysX/physx/include/vehicle/PxVehicleUtil.h new file mode 100644 index 000000000..8c67751bc --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleUtil.h @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_UTILHELPER_H +#define PX_VEHICLE_UTILHELPER_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxVehicleWheelQueryResult; + +/** +\brief Test if all wheels of a vehicle are in the air by querying the wheel query data +stored in the last call to PxVehicleUpdates. If all wheels are in the air then true is returned. + +\note False is returned if any wheel can reach to the ground. + +\note If vehWheelQueryResults.wheelQueryResults is NULL or vehWheelQueryResults.nbWheelQueryResults is 0 then true is returned. +This function does not account for wheels that have been disabled since the last execution of PxVehicleUpdates so it is possible +that wheels disabled more recently than the last call to PxVehicleUpdates report are treated as touching the ground. + +\return True if the vehicle is in the air, false if any wheel is touching the ground. +*/ +bool PxVehicleIsInAir(const PxVehicleWheelQueryResult& vehWheelQueryResults); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_UTILHELPER_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h b/src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h new file mode 100644 index 000000000..3daf06453 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h @@ -0,0 +1,651 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_CONTROL_H +#define PX_VEHICLE_CONTROL_H +/** \addtogroup vehicle + @{ +*/ +#include "vehicle/PxVehicleSDK.h" +#include "vehicle/PxVehicleDrive4W.h" +#include "vehicle/PxVehicleDriveNW.h" +#include "vehicle/PxVehicleDriveTank.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#if PX_CHECKED + void testValidAnalogValue(const PxF32 actualValue, const PxF32 minVal, const PxF32 maxVal, const char* errorString); +#endif + +/** +\brief Used to produce smooth vehicle driving control values from key inputs. +@see PxVehicle4WSmoothDigitalRawInputsAndSetAnalogInputs, PxVehicle4WSmoothAnalogRawInputsAndSetAnalogInputs +*/ +struct PxVehicleKeySmoothingData +{ +public: + + /** + \brief Rise rate of each analog value if digital value is 1 + */ + PxReal mRiseRates[PxVehicleDriveDynData::eMAX_NB_ANALOG_INPUTS]; + + /** + \brief Fall rate of each analog value if digital value is 0 + */ + PxReal mFallRates[PxVehicleDriveDynData::eMAX_NB_ANALOG_INPUTS]; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleKeySmoothingData)& 0x0f)); + +/** +\brief Used to produce smooth analog vehicle control values from analog inputs. +@see PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs, PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs +*/ +struct PxVehiclePadSmoothingData +{ +public: + + /** + \brief Rise rate of each analog value from previous value towards target if target>previous + */ + PxReal mRiseRates[PxVehicleDriveDynData::eMAX_NB_ANALOG_INPUTS]; + + /** + \brief Rise rate of each analog value from previous value towards target if target& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDrive4W& focusVehicle); + +/** +\brief Used to smooth and set analog vehicle control values from analog inputs (gamepad). +Also used to set boolean gearup, geardown values. +\param[in] padSmoothing describes how quickly the control values applied to the vehicle blend from the current vehicle values towards the raw analog values from the gamepad. +\param[in] steerVsForwardSpeedTable is a table of maximum allowed steer versus forward vehicle speed. +\param[in] rawInputData is the state of all gamepad analog inputs that will be used control the vehicle. +\param[in] timestep is the time that has passed since the last call to PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs +\param[in] isVehicleInAir describes if the vehicle is in the air or on the ground and is used to decide whether or not to apply steerVsForwardSpeedTable. +\param[in] focusVehicle is the vehicle that will be given analog control values arising from the gamepad inputs. +*/ +void PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs + (const PxVehiclePadSmoothingData& padSmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDrive4W& focusVehicle); + + +/** +\brief Used to produce smooth vehicle driving control values from analog and digital inputs. +@see PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs, PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs +*/ +class PxVehicleDriveNWRawInputData : public PxVehicleDrive4WRawInputData +{ +public: + + PxVehicleDriveNWRawInputData() : PxVehicleDrive4WRawInputData(){} + ~PxVehicleDriveNWRawInputData(){} +}; + +/** +\brief Used to smooth and set analog vehicle control values (accel,brake,handbrake,steer) from digital inputs (keyboard). + Also used to set boolean gearup, geardown values. + \param[in] keySmoothing describes the rise and fall rates of the corresponding analog values when keys are pressed on and off. + \param[in] steerVsForwardSpeedTable is a table of maximum allowed steer versus forward vehicle speed. + \param[in] rawInputData is the state of all digital inputs that control the vehicle. + \param[in] timestep is the time that has passed since the last call to PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs + \param[in] isVehicleInAir describes if the vehicle is in the air or on the ground and is used to decide whether or not to apply steerVsForwardSpeedTable. + \param[in] focusVehicle is the vehicle that will be given analog and gearup/geardown control values arising from the digital inputs. +*/ +void PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs + (const PxVehicleKeySmoothingData& keySmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDriveNWRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDriveNW& focusVehicle); + +/** +\brief Used to smooth and set analog vehicle control values from analog inputs (gamepad). +Also used to set boolean gearup, geardown values. +\param[in] padSmoothing describes how quickly the control values applied to the vehicle blend from the current vehicle values towards the raw analog values from the gamepad. +\param[in] steerVsForwardSpeedTable is a table of maximum allowed steer versus forward vehicle speed. +\param[in] rawInputData is the state of all gamepad analog inputs that will be used control the vehicle. +\param[in] timestep is the time that has passed since the last call to PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs +\param[in] isVehicleInAir describes if the vehicle is in the air or on the ground and is used to decide whether or not to apply steerVsForwardSpeedTable. +\param[in] focusVehicle is the vehicle that will be given analog control values arising from the gamepad inputs. +*/ +void PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs + (const PxVehiclePadSmoothingData& padSmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDriveNWRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDriveNW& focusVehicle); + + +/** +\brief Used to produce smooth analog tank control values from analog and digital inputs. +@see PxVehicleDriveTankSmoothDigitalRawInputsAndSetAnalogInputs, PxVehicleDriveTankSmoothAnalogRawInputsAndSetAnalogInputs +*/ +class PxVehicleDriveTankRawInputData +{ +public: + + PxVehicleDriveTankRawInputData(const PxVehicleDriveTankControlModel::Enum mode) + : mMode(mode) + { + for(PxU32 i=0;iRange: (0,1)
+ */ + PxReal mPosX; + + /** + \brief y-coord of graph centre. + Range: (0,1)
+ */ + PxReal mPosY; + + /** + \brief x-extents of graph (from mPosX-0.5f*mSizeX to mPosX+0.5f*mSizeX). + Range: (0,1)
+ */ + PxReal mSizeX; + + /** + \brief y-extents of graph (from mPosY-0.5f*mSizeY to mPosY+0.5f*mSizeY). + Range: (0,1)
+ */ + PxReal mSizeY; + + /** + \brief Background color of graph. + */ + PxVec3 mBackgroundColor; + + /** + \brief Alpha value of background color. + */ + PxReal mAlpha; + +private: + + bool isValid() const; +}; + +struct PxVehicleGraphChannelDesc +{ +public: + + friend class PxVehicleGraph; + + PxVehicleGraphChannelDesc(); + + /** + \brief Data values less than mMinY will be clamped at mMinY. + */ + PxReal mMinY; + + /** + \brief Data values greater than mMaxY will be clamped at mMaxY. + */ + PxReal mMaxY; + + /** + \brief Data values greater than mMidY will be drawn with color mColorHigh. + Data values less than mMidY will be drawn with color mColorLow. + */ + PxReal mMidY; + + /** + \brief Color used to render data values lower than mMidY. + */ + PxVec3 mColorLow; + + /** + \brief Color used to render data values greater than mMidY. + */ + PxVec3 mColorHigh; + + /** + \brief String to describe data channel. + */ + char* mTitle; + +private: + + bool isValid() const; +}; + +struct PxVehicleWheelGraphChannel +{ + enum Enum + { + eJOUNCE=0, + eSUSPFORCE, + eTIRELOAD, + eNORMALIZED_TIRELOAD, + eWHEEL_OMEGA, + eTIRE_FRICTION, + eTIRE_LONG_SLIP, + eNORM_TIRE_LONG_FORCE, + eTIRE_LAT_SLIP, + eNORM_TIRE_LAT_FORCE, + eNORM_TIRE_ALIGNING_MOMENT, + eMAX_NB_WHEEL_CHANNELS + }; +}; + +struct PxVehicleDriveGraphChannel +{ + enum Enum + { + eENGINE_REVS=0, + eENGINE_DRIVE_TORQUE, + eCLUTCH_SLIP, + eACCEL_CONTROL, //TANK_ACCEL + eBRAKE_CONTROL, //TANK_BRAKE_LEFT + eHANDBRAKE_CONTROL, //TANK_BRAKE_RIGHT + eSTEER_LEFT_CONTROL, //TANK_THRUST_LEFT + eSTEER_RIGHT_CONTROL, //TANK_THRUST_RIGHT + eGEAR_RATIO, + eMAX_NB_DRIVE_CHANNELS + }; +}; + +struct PxVehicleGraphType +{ + enum Enum + { + eWHEEL=0, + eDRIVE + }; +}; + + +class PxVehicleGraph +{ +public: + + friend class PxVehicleTelemetryData; + friend class PxVehicleUpdate; + + enum + { + eMAX_NB_SAMPLES=256 + }; + + enum + { + eMAX_NB_TITLE_CHARS=256 + }; + + enum + { + eMAX_NB_CHANNELS=12 + }; + + /** + \brief Setup a graph from a descriptor. + */ + void setup(const PxVehicleGraphDesc& desc, const PxVehicleGraphType::Enum graphType); + + /** + \brief Clear all data recorded in a graph. + */ + void clearRecordedChannelData(); + + /** + \brief Get the color of the graph background. Used for rendering a graph. + */ + const PxVec3& getBackgroundColor() const {return mBackgroundColor;} + + /** + \brief Get the alpha transparency of the color of the graph background. Used for rendering a graph. + */ + PxReal getBackgroundAlpha() const {return mBackgroundAlpha;} + + /** + \brief Get the coordinates of the graph background. Used for rendering a graph + + \param[out] xMin is the x-coord of the lower-left corner + \param[out] yMin is the y-coord of the lower-left corner + \param[out] xMax is the x-coord of the upper-right corner + \param[out] yMax is the y-coord of the upper-right corner + */ + void getBackgroundCoords(PxReal& xMin, PxReal& yMin, PxReal& xMax, PxReal& yMax) const {xMin = mBackgroundMinX;xMax = mBackgroundMaxX;yMin = mBackgroundMinY;yMax = mBackgroundMaxY;} + + /** + \brief Compute the coordinates of the graph data of a specific graph channel. + + \param[out] xy is an array of graph sample coordinates stored in order x0,y0,x1,y1,x2,y2...xn,yn. + \param[out] colors stores the color of each point on the graph. + \param[out] title is the title of the graph. + */ + void computeGraphChannel(const PxU32 channel, PxReal* xy, PxVec3* colors, char* title) const; + + /** + \brief Return the latest value stored in the specified graph channel + */ + PxF32 getLatestValue(const PxU32 channel) const ; + +private: + + //Min and max of each sample. + PxReal mChannelMinY[eMAX_NB_CHANNELS]; + PxReal mChannelMaxY[eMAX_NB_CHANNELS]; + //Discriminate between high and low values with different colors. + PxReal mChannelMidY[eMAX_NB_CHANNELS]; + //Different colors for values than midY and less than midY. + PxVec3 mChannelColorLow[eMAX_NB_CHANNELS]; + PxVec3 mChannelColorHigh[eMAX_NB_CHANNELS]; + //Title of graph + char mChannelTitle[eMAX_NB_CHANNELS][eMAX_NB_TITLE_CHARS]; + //Graph data. + PxReal mChannelSamples[eMAX_NB_CHANNELS][eMAX_NB_SAMPLES]; + + //Background color,alpha,coords + PxVec3 mBackgroundColor; + PxReal mBackgroundAlpha; + PxReal mBackgroundMinX; + PxReal mBackgroundMaxX; + PxReal mBackgroundMinY; + PxReal mBackgroundMaxY; + + PxU32 mSampleTide; + + PxU32 mNbChannels; + + PxU32 mPad[2]; + + + void setup + (const PxF32 graphSizeX, const PxF32 graphSizeY, + const PxF32 engineGraphPosX, const PxF32 engineGraphPosY, + const PxF32* const wheelGraphPosX, const PxF32* const wheelGraphPosY, + const PxVec3& backgroundColor, const PxVec3& lineColorHigh, const PxVec3& lineColorLow); + + void updateTimeSlice(const PxReal* const samples); + + void setChannel(PxVehicleGraphChannelDesc& desc, const PxU32 channel); + + void setupEngineGraph + (const PxF32 sizeX, const PxF32 sizeY, const PxF32 posX, const PxF32 posY, + const PxVec3& backgoundColor, const PxVec3& lineColorHigh, const PxVec3& lineColorLow); + + void setupWheelGraph + (const PxF32 sizeX, const PxF32 sizeY, const PxF32 posX, const PxF32 posY, + const PxVec3& backgoundColor, const PxVec3& lineColorHigh, const PxVec3& lineColorLow); + + PxVehicleGraph(); + ~PxVehicleGraph(); +}; +PX_COMPILE_TIME_ASSERT(PxU32(PxVehicleGraph::eMAX_NB_CHANNELS) >= PxU32(PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS) && PxU32(PxVehicleGraph::eMAX_NB_CHANNELS) >= PxU32(PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS)); +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleGraph) & 15)); + +class PxVehicleTelemetryData +{ +public: + + friend class PxVehicleUpdate; + + /** + \brief Allocate a PxVehicleNWTelemetryData instance for a vehicle with nbWheels + @see PxVehicleNWTelemetryDataFree + */ + static PxVehicleTelemetryData* allocate(const PxU32 nbWheels); + + /** + \brief Free a PxVehicleNWTelemetryData instance for a vehicle. + @see PxVehicleNWTelemetryDataAllocate + */ + void free(); + + /** + \brief Set up all the graphs so that they are ready to record data. + */ + void setup + (const PxReal graphSizeX, const PxReal graphSizeY, + const PxReal engineGraphPosX, const PxReal engineGraphPosY, + const PxReal* const wheelGraphPosX, const PxReal* const wheelGraphPosY, + const PxVec3& backGroundColor, const PxVec3& lineColorHigh, const PxVec3& lineColorLow); + + /** + \brief Clear the graphs of recorded data. + */ + void clear(); + + /** + \brief Get the graph data for the engine + */ + const PxVehicleGraph& getEngineGraph() const {return *mEngineGraph;} + + /** + \brief Get the number of wheel graphs + */ + PxU32 getNbWheelGraphs() const {return mNbActiveWheels;} + + /** + \brief Get the graph data for the kth wheel + */ + const PxVehicleGraph& getWheelGraph(const PxU32 k) const {return mWheelGraphs[k];} + + /** + \brief Get the array of tire force application points so they can be rendered + */ + const PxVec3* getTireforceAppPoints() const {return mTireforceAppPoints;} + + /** + \brief Get the array of susp force application points so they can be rendered + */ + const PxVec3* getSuspforceAppPoints() const {return mSuspforceAppPoints;} + +private: + + /** + \brief Graph data for engine. + Used for storing single timeslices of debug data for engine graph. + @see PxVehicleGraph + */ + PxVehicleGraph* mEngineGraph; + + /** + \brief Graph data for each wheel. + Used for storing single timeslices of debug data for wheel graphs. + @see PxVehicleGraph + */ + PxVehicleGraph* mWheelGraphs; + + /** + \brief Application point of tire forces. + */ + PxVec3* mTireforceAppPoints; + + /** + \brief Application point of susp forces. + */ + PxVec3* mSuspforceAppPoints; + + /** + \brief Total number of active wheels + */ + PxU32 mNbActiveWheels; + + PxU32 mPad[3]; + +private: + + PxVehicleTelemetryData(){} + ~PxVehicleTelemetryData(){} +}; + +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleTelemetryData) & 15)); + +#endif //PX_DEBUG_VEHICLE_ON + +//#endif // PX_DEBUG_VEHICLE_ON + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_UTILSTELEMETRY_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleWheels.h b/src/PhysX/physx/include/vehicle/PxVehicleWheels.h new file mode 100644 index 000000000..cd36131b4 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleWheels.h @@ -0,0 +1,811 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_VEHICLE_WHEELS_H +#define PX_VEHICLE_WHEELS_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxSimpleTypes.h" +#include "vehicle/PxVehicleShaders.h" +#include "vehicle/PxVehicleComponents.h" +#include "common/PxBase.h" +#include "PxRigidDynamic.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxVehicleWheels4SimData; +class PxVehicleWheels4DynData; +class PxVehicleTireForceCalculator; +class PxShape; +class PxPhysics; +class PxMaterial; + +/** +\brief Data structure describing configuration data of a vehicle with up to 20 wheels. +*/ + +class PxVehicleWheelsSimData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleWheels; + friend class PxVehicleNoDrive; + friend class PxVehicleDrive4W; + friend class PxVehicleDriveTank; + friend class PxVehicleUpdate; + + /** + \brief Allocate a PxVehicleWheelsSimData instance for with nbWheels. + @see free + */ + static PxVehicleWheelsSimData* allocate(const PxU32 nbWheels); + + /** + \brief Setup with mass information that can be applied to the default values of the suspensions, wheels, and tires + set in their respective constructors. + + \param chassisMass is the mass of the chassis. + + \note This function assumes that the suspensions equally share the load of the chassis mass. It also + assumes that the suspension will have a particular natural frequency and damping ratio that is typical + of a standard car. If either of these assumptions is broken then each suspension will need to + be individually configured with custom strength, damping rate, and sprung mass. + + @see allocate + */ + void setChassisMass(const PxF32 chassisMass); + + /** + \brief Free a PxVehicleWheelsSimData instance + @see allocate + */ + void free(); + + /** + \brief Copy wheel simulation data. + \note The number of wheels on both instances of PxVehicleWheelsSimData must match. + */ + PxVehicleWheelsSimData& operator=(const PxVehicleWheelsSimData& src); + + /** + \brief Copy the data of a single wheel unit (wheel, suspension, tire) from srcWheel of src to trgWheel. + \param[in] src is the data to be copied. + \param[in] srcWheel is the wheel whose data will be copied from src. + \param[in] trgWheel is the wheel that will be assigned the copied data. + */ + void copy(const PxVehicleWheelsSimData& src, const PxU32 srcWheel, const PxU32 trgWheel); + + /** + \brief Return the number of wheels + @see allocate + */ + PxU32 getNbWheels() const {return mNbActiveWheels;} + + /** + \brief Return the suspension data of the idth wheel + */ + const PxVehicleSuspensionData& getSuspensionData(const PxU32 id) const; + + /** + \brief Return the wheel data of the idth wheel + */ + const PxVehicleWheelData& getWheelData(const PxU32 id) const; + + /** + \brief Return the tire data of the idth wheel + */ + const PxVehicleTireData& getTireData(const PxU32 id) const; + + /** + \brief Return the direction of travel of the suspension of the idth wheel + */ + const PxVec3& getSuspTravelDirection(const PxU32 id) const; + + /** + \brief Return the application point of the suspension force of the suspension of the idth wheel as an offset from the rigid body center of mass. + \note Specified relative to the center of mass of the rigid body + */ + const PxVec3& getSuspForceAppPointOffset(const PxU32 id) const; + + /** + \brief Return the application point of the tire force of the tire of the idth wheel as an offset from the rigid body center of mass. + \note Specified relative to the centre of mass of the rigid body + */ + const PxVec3& getTireForceAppPointOffset(const PxU32 id) const; + + /** + \brief Return the offset from the rigid body centre of mass to the centre of the idth wheel. + */ + const PxVec3& getWheelCentreOffset(const PxU32 id) const; + + /** + \brief Return the wheel mapping for the ith wheel. + + \note The return value is the element in the array of + shapes of the vehicle's PxRigidDynamic that corresponds to the ith wheel. A return value of -1 means + that the wheel is not mapped to a PxShape. + + @see PxRigidActor.getShapes + */ + PxI32 getWheelShapeMapping(const PxU32 wheelId) const; + + /** + \brief Return the scene query filter data used by the specified suspension line + */ + const PxFilterData& getSceneQueryFilterData(const PxU32 suspId) const; + + /** + \brief Return the number of unique anti-roll bars that have been added with addAntiRollBarData + @see PxVehicleWheelsSimData::addAntiRollBarData + */ + PxU32 getNbAntiRollBars() const + { + return mNbActiveAntiRollBars; + } + + /** + \brief Return a specific anti-roll bar. + \param antiRollId is the unique id of the anti-roll bar + \note The return value of addAntiRollBarData is a unique id for that specific anti-roll bar + and can be used as input parameter for getAntiRollBarData in order to query the same anti-roll bar. + Alternatively, it is possible to iterate over all anti-roll bars by choosing antiRollId + in range (0, getNbAntiRollBars()). + */ + const PxVehicleAntiRollBarData& getAntiRollBarData(const PxU32 antiRollId) const; + + /** + \brief Return the data that describes the filtering of the tire load to produce smoother handling at large time-steps. + */ + PX_FORCE_INLINE const PxVehicleTireLoadFilterData& getTireLoadFilterData() const + { + return mNormalisedLoadFilter; + } + + /** + \brief Set the suspension data of the idth wheel + \param[in] id is the wheel index. + \param[in] susp is the suspension data to be applied. + */ + void setSuspensionData(const PxU32 id, const PxVehicleSuspensionData& susp); + + /** + \brief Set the wheel data of the idth wheel + \param[in] id is the wheel index. + \param[in] wheel is the wheel data to be applied. + */ + void setWheelData(const PxU32 id, const PxVehicleWheelData& wheel); + + /** + \brief Set the tire data of the idth wheel + \param[in] id is the wheel index. + \param[in] tire is the tire data to be applied. + */ + void setTireData(const PxU32 id, const PxVehicleTireData& tire); + + /** + \brief Set the direction of travel of the suspension of the idth wheel + \param[in] id is the wheel index + \param[in] dir is the suspension travel direction to be applied. + */ + void setSuspTravelDirection(const PxU32 id, const PxVec3& dir); + + /** + \brief Set the application point of the suspension force of the suspension of the idth wheel. + \param[in] id is the wheel index + \param[in] offset is the offset from the rigid body center of mass to the application point of the suspension force. + \note Specified relative to the centre of mass of the rigid body + */ + void setSuspForceAppPointOffset(const PxU32 id, const PxVec3& offset); + + /** + \brief Set the application point of the tire force of the tire of the idth wheel. + \param[in] id is the wheel index + \param[in] offset is the offset from the rigid body center of mass to the application point of the tire force. + \note Specified relative to the centre of mass of the rigid body + */ + void setTireForceAppPointOffset(const PxU32 id, const PxVec3& offset); + + /** + \brief Set the offset from the rigid body centre of mass to the centre of the idth wheel. + \param[in] id is the wheel index + \param[in] offset is the offset from the rigid body center of mass to the center of the wheel at rest. + \note Specified relative to the centre of mass of the rigid body + */ + void setWheelCentreOffset(const PxU32 id, const PxVec3& offset); + + /** + \brief Set mapping between wheel id and position of corresponding wheel shape in the list of actor shapes. + + \note This mapping is used to pose the correct wheel shapes with the latest wheel rotation angle, steer angle, and suspension travel + while allowing arbitrary ordering of the wheel shapes in the actor's list of shapes. + + \note Use setWheelShapeMapping(i,-1) to register that there is no wheel shape corresponding to the ith wheel + + \note Set setWheelShapeMapping(i,k) to register that the ith wheel corresponds to the kth shape in the actor's list of shapes. + + \note The default values correspond to setWheelShapeMapping(i,i) for all wheels. + + \note Calling this function will also pose the relevant PxShape at the rest position of the wheel. + + \param wheelId is the wheel index + + \param shapeId is the shape index. + + @see PxVehicleUpdates, PxVehicleDrive4W::setup, PxVehicleDriveTank::setup, PxVehicleNoDrive::setup, setSceneQueryFilterData, PxRigidActor::getShapes + */ + void setWheelShapeMapping(const PxU32 wheelId, const PxI32 shapeId); + + /** + \brief Set the scene query filter data that will be used for raycasts along the travel + direction of the specified suspension. The default value is PxFilterData(0,0,0,0) + \param suspId is the wheel index + \param sqFilterData is the raycast filter data for the suspension raycast. + @see setWheelShapeMapping + */ + void setSceneQueryFilterData(const PxU32 suspId, const PxFilterData& sqFilterData); + + /** + \brief Set the data that describes the filtering of the tire load to produce smoother handling at large timesteps. + \param tireLoadFilter is the smoothing function data. + */ + void setTireLoadFilterData(const PxVehicleTireLoadFilterData& tireLoadFilter); + + /** + \brief Set the anti-roll suspension for a pair of wheels. + + \param antiRoll is the anti-roll suspension. + + \note If an anti-roll bar has already been set for the same logical wheel pair + (independent of wheel index order specified by PxVehicleAntiRollBar.mWheel0 and PxVehicleAntiRollBar.mWheel0) + then the existing anti-roll bar is updated with a new stiffness parameter antiRoll.mStiffness. + + \note If the wheel pair specified by antiRoll does not yet have an anti-roll bar then antiRoll is added to + a list of anti-roll bars for the vehicle. + + \return If antiRoll represents a new wheel pair then a unique id is assigned to the anti-roll bar and returned. + If antiRoll represents an existing wheel pair then the unique id of the existing anti-roll bar is returned. + The return value is always in range (0, getNbAntiRollBars()). + + \note The return value can be used to query the anti-roll bar with getAntiRollBarData(id). + + \note The number of possible anti-roll bars is limited to half the wheel count. + + \note An existing anti-roll bar can be disabled by calling antiRoll.mStiffness to zero. + + @see PxVehicleWheelsSimData::getAntiRollBarData, PxVehicleAntiRollBarData + */ + PxU32 addAntiRollBarData(const PxVehicleAntiRollBarData& antiRoll); + + /** + \brief Disable a wheel so that zero suspension forces and zero tire forces are applied to the rigid body from this wheel. + + \note If the vehicle has a differential (PxVehicleNW/PxVehicle4W) then the differential (PxVehicleDifferentialNWData/PxVehicleDifferential4WData) + needs to be configured so that no drive torque is delivered to the disabled wheel. + + \note If the vehicle is of type PxVehicleNoDrive then zero drive torque must be applied to the disabled wheel. + + \note For tanks (PxVehicleDriveTank) any drive torque that could be delivered to the wheel through the tank differential will be + re-directed to the remaining enabled wheels. + + @see enableWheel + @see PxVehicleDifferentialNWData::setDrivenWheel + @see PxVehicleDifferential4WData::mFrontLeftRightSplit, PxVehicleDifferential4WData::mRearLeftRightSplit, PxVehicleDifferential4WData::mType + @see PxVehicleNoDrive::setDriveTorque + @see PxVehicle4WEnable3WTadpoleMode, PxVehicle4WEnable3WDeltaMode + + \note If a PxShape is associated with the disabled wheel then the association must be broken by calling setWheelShapeMapping(wheelId, -1). + @see setWheelShapeMapping + + \note A wheel that is disabled must also simultaneously be given zero wheel rotation speed. + @see PxVehicleWheelsDynData::setWheelRotationSpeed + + \note Care must be taken with the sprung mass supported by the remaining enabled wheels. Depending on the desired effect, the mass of the rigid body + might need to be distributed among the remaining enabled wheels and suspensions. + + \param[in] wheel is the wheel index. + */ + void disableWheel(const PxU32 wheel); + + /** + \brief Enable a wheel so that suspension forces and tire forces are applied to the rigid body. + All wheels are enabled by default and remain enabled until they are disabled. + \param[in] wheel is the wheel index. + @see disableWheel + */ + void enableWheel(const PxU32 wheel); + + /** + \brief Test if a wheel has been disabled. + \param[in] wheel is the wheel index. + */ + bool getIsWheelDisabled(const PxU32 wheel) const; + + /** + \brief Set the number of vehicle sub-steps that will be performed when the vehicle's longitudinal + speed is below and above a threshold longitudinal speed. + + \note More sub-steps provides better stability but with greater computational cost. + + \note Typically, vehicles require more sub-steps at very low forward speeds. + + \note The threshold longitudinal speed has a default value that is the equivalent of 5 metres per second after accounting for + the length scale set in PxTolerancesScale. + + \note The sub-step count below the threshold longitudinal speed has a default of 3. + + \note The sub-step count above the threshold longitudinal speed has a default of 1. + + \note Each sub-step has time advancement equal to the time-step passed to PxVehicleUpdates divided by the number of required sub-steps. + + \note The contact planes of the most recent suspension line raycast are reused across all sub-steps. + + \note Each sub-step computes tire and suspension forces and then advances a velocity, angular velocity and transform. + + \note At the end of all sub-steps the vehicle actor is given the velocity and angular velocity that would move the actor from its start transform prior + to the first sub-step to the transform computed at the end of the last substep, assuming it doesn't collide with anything along the way in the next PhysX SDK update. + + \note The global pose of the actor is left unchanged throughout the sub-steps. + + \param[in] thresholdLongitudinalSpeed is a threshold speed that is used to categorize vehicle speed as low speed or high speed. + \param[in] lowForwardSpeedSubStepCount is the number of sub-steps performed in PxVehicleUpates for vehicles that have longitudinal speed lower than thresholdLongitudinalSpeed. + \param[in] highForwardSpeedSubStepCount is the number of sub-steps performed in PxVehicleUpdates for vehicles that have longitudinal speed graeter than thresholdLongitudinalSpeed. + */ + void setSubStepCount(const PxReal thresholdLongitudinalSpeed, const PxU32 lowForwardSpeedSubStepCount, const PxU32 highForwardSpeedSubStepCount); + + /** + \brief Set the minimum denominator used in the longitudinal slip calculation. + + \note The longitudinal slip has a theoretical value of (w*r - vz)/|vz|, where w is the angular speed of the wheel; r is the radius of the wheel; + and vz is the component of rigid body velocity (computed at the wheel base) that lies along the longitudinal wheel direction. The term |vz| + normalizes the slip, while preserving the sign of the longitudinal tire slip. The difficulty here is that when |vz| approaches zero the + longitudinal slip approaches infinity. A solution to this problem is to replace the denominator (|vz|) with a value that never falls below a chosen threshold. + The longitudinal slip is then calculated with (w*r - vz)/PxMax(|vz|, minLongSlipDenominator). + + \note The default value is the equivalent of 4 metres per second after accounting for the length scale set in PxTolerancesScale. + + \note Adjust this value upwards if a vehicle has difficulty coming to rest. + + \note Decreasing the timestep (or increasing the number of sub-steps at low longitudinal speed with setSubStepCount) should allow stable stable + behavior with smaller values of minLongSlipDenominator. + */ + void setMinLongSlipDenominator(const PxReal minLongSlipDenominator); + +private: + + /** + \brief Graph to filter normalised load + @see setTireLoadFilterData, getTireLoadFilterData + */ + PxVehicleTireLoadFilterData mNormalisedLoadFilter; + + /** + \brief Wheels data organised in blocks of 4 wheels. + */ + PxVehicleWheels4SimData* mWheels4SimData; + + /** + \brief Number of blocks of 4 wheels. + */ + PxU32 mNbWheels4; + + /** + \brief Number of actual wheels (<=(mNbWheels4*4)) + */ + PxU32 mNbActiveWheels; + + /** + \brief Anti-roll bars + */ + PxVehicleAntiRollBarData* mAntiRollBars; + + /** + \brief 2 anti-rollbars allocated for each block of 4 wheels. + */ + PxU32 mNbAntiRollBars4; + + /** + \brief Number of active anti-roll bars. + */ + PxU32 mNbActiveAntiRollBars; + + /** + \brief Which of the mNbActiveWheels are active or disabled? + The default is that all mNbActiveWheels wheels are active. + */ + PxU32 mActiveWheelsBitmapBuffer[((PX_MAX_NB_WHEELS + 31) & ~31) >> 5]; + + /** + \brief Threshold longitudinal speed used to decide whether to use + mLowForwardSpeedSubStepCount or mHighForwardSpeedSubStepCount as the + number of sub-steps that will be peformed. + */ + PxF32 mThresholdLongitudinalSpeed; + + /** + \brief Number of sub-steps that will be performed if the longitudinal speed + of the vehicle is smaller than mThresholdLongitudinalSpeed. + */ + PxU32 mLowForwardSpeedSubStepCount; + + /** + \brief Number of sub-steps that will be performed if the longitudinal speed + of the vehicle is greater than or equal to mThresholdLongitudinalSpeed. + */ + PxU32 mHighForwardSpeedSubStepCount; + + /** + \brief Minimum long slip denominator + */ + PxF32 mMinLongSlipDenominator; + +#if PX_P64_FAMILY + PxU32 mPad[2]; +#else + PxU32 mPad[1]; +#endif + + /** + \brief Test if wheel simulation data has been setup with legal values. + */ + bool isValid() const; + + /** + \brief see PxVehicleWheels::allocate + */ + static PxU32 computeByteSize(const PxU32 numWheels); + static PxU8* patchUpPointers(const PxU32 numWheels, PxVehicleWheelsSimData* simData, PxU8* ptrIn); + PxVehicleWheelsSimData(const PxU32 numWheels); + +//serialization +public: + PxVehicleWheelsSimData(const PxEMPTY) : mNormalisedLoadFilter(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); + PxU32 getNbWheels4() const { return mNbWheels4; } + PxU32 getNbSuspensionData() const { return mNbActiveWheels; } + PxU32 getNbWheelData() const { return mNbActiveWheels; } + PxU32 getNbSuspTravelDirection() const { return mNbActiveWheels; } + PxU32 getNbTireData() const { return mNbActiveWheels; } + PxU32 getNbSuspForceAppPointOffset() const { return mNbActiveWheels; } + PxU32 getNbTireForceAppPointOffset() const { return mNbActiveWheels; } + PxU32 getNbWheelCentreOffset() const { return mNbActiveWheels; } + PxU32 getNbWheelShapeMapping() const { return mNbActiveWheels; } + PxU32 getNbSceneQueryFilterData() const { return mNbActiveWheels; } + PxF32 getMinLongSlipDenominator() const {return mMinLongSlipDenominator;} + void setThresholdLongSpeed(const PxF32 f) {mThresholdLongitudinalSpeed = f;} + PxF32 getThresholdLongSpeed() const {return mThresholdLongitudinalSpeed;} + void setLowForwardSpeedSubStepCount(const PxU32 f) {mLowForwardSpeedSubStepCount = f;} + PxU32 getLowForwardSpeedSubStepCount() const {return mLowForwardSpeedSubStepCount;} + void setHighForwardSpeedSubStepCount(const PxU32 f) {mHighForwardSpeedSubStepCount = f;} + PxU32 getHighForwardSpeedSubStepCount() const {return mHighForwardSpeedSubStepCount;} + void setWheelEnabledState(const PxU32 wheel, const bool state) {if(state) {enableWheel(wheel);} else {disableWheel(wheel);}} + bool getWheelEnabledState(const PxU32 wheel) const {return !getIsWheelDisabled(wheel);} + PxU32 getNbWheelEnabledState() const {return mNbActiveWheels;} + PxU32 getNbAntiRollBars4() const { return mNbAntiRollBars4; } + PxU32 getNbAntiRollBarData() const {return mNbActiveAntiRollBars;} + void setAntiRollBarData(const PxU32 id, const PxVehicleAntiRollBarData& antiRoll); + PxVehicleWheelsSimData(){} + ~PxVehicleWheelsSimData(){} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleWheelsSimData) & 15)); + +/** +\brief Data structure with instanced dynamics data for wheels +*/ +class PxVehicleWheelsDynData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleWheels; + friend class PxVehicleDrive4W; + friend class PxVehicleDriveTank; + friend class PxVehicleUpdate; + + PxVehicleWheelsDynData(){} + ~PxVehicleWheelsDynData(){} + + /** + \brief Set all wheels to their rest state. + @see setup + */ + void setToRestState(); + + /** + \brief Set the tire force shader function + \param[in] tireForceShaderFn is the shader function that will be used to compute tire forces. + */ + void setTireForceShaderFunction(PxVehicleComputeTireForce tireForceShaderFn); + + /** + \brief Set the tire force shader data for a specific tire + \param[in] tireId is the wheel index + \param[in] tireForceShaderData is the data describing the tire. + */ + void setTireForceShaderData(const PxU32 tireId, const void* tireForceShaderData); + + /** + \brief Get the tire force shader data for a specific tire + */ + const void* getTireForceShaderData(const PxU32 tireId) const; + + /** + \brief Set the wheel rotation speed (radians per second) about the rolling axis for the specified wheel. + \param[in] wheelIdx is the wheel index + \param[in] speed is the rotation speed to be applied to the wheel. + */ + void setWheelRotationSpeed(const PxU32 wheelIdx, const PxReal speed); + + /** + \brief Return the rotation speed about the rolling axis of a specified wheel . + */ + PxReal getWheelRotationSpeed(const PxU32 wheelIdx) const; + + /** + \brief Set the wheel rotation angle (radians) about the rolling axis of the specified wheel. + \param[in] wheelIdx is the wheel index + \param[in] angle is the rotation angle to be applied to the wheel. + */ + void setWheelRotationAngle(const PxU32 wheelIdx, const PxReal angle); + + /** + \brief Return the rotation angle about the rolling axis for the specified wheel. + */ + PxReal getWheelRotationAngle(const PxU32 wheelIdx) const; + + /** + \brief Set the user data pointer for the specified wheel + It has a default value of NULL. + \param[in] tireIdx is the wheel index + \param[in] userData is the data to be associated with the wheel. + */ + void setUserData(const PxU32 tireIdx, void* userData); + + /** + \brief Get the user data pointer that was set for the specified wheel + */ + void* getUserData(const PxU32 tireIdx) const; + + /** + \brief Copy the dynamics data of a single wheel unit (wheel, suspension, tire) from srcWheel of src to trgWheel. + \param[in] src is the data to be copied. + \param[in] srcWheel is the wheel whose data will be copied from src. + \param[in] trgWheel is the wheel that will be assigned the copied data. + */ + void copy(const PxVehicleWheelsDynData& src, const PxU32 srcWheel, const PxU32 trgWheel); + +private: + + /** + \brief Dynamics data arranged in blocks of 4 wheels. + */ + PxVehicleWheels4DynData* mWheels4DynData; + + /** + \brief Test if wheel dynamics data have legal values. + */ + bool isValid() const; + + /** + \brief Shader data and function for tire force calculations. + */ + PxVehicleTireForceCalculator* mTireForceCalculators; + + /** + \brief A userData pointer can be stored for each wheel. + @see setUserData, getUserData + */ + void** mUserDatas; + + /** + \brief Number of blocks of 4 wheels. + */ + PxU32 mNbWheels4; + + /** + \brief Number of wheels (mNbActiveWheels <= (mNbWheels4*4)) + */ + PxU32 mNbActiveWheels; + + PxU32 mPad[3]; + + /** + \brief see PxVehicleWheels::allocate + */ + static PxU32 computeByteSize(const PxU32 numWheels); + static PxU8* patchUpPointers(const PxU32 numWheels, PxVehicleWheelsDynData* dynData, PxU8* ptr); + PxVehicleWheelsDynData(const PxU32 numWheels); + +//serialization +public: + static void getBinaryMetaData(PxOutputStream& stream); + PxU32 getNbWheelRotationSpeed() const { return mNbActiveWheels; } + PxU32 getNbWheelRotationAngle() const { return mNbActiveWheels; } + PxVehicleWheels4DynData* getWheel4DynData() const { return mWheels4DynData; } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleWheelsDynData) & 15)); + +/** +\brief Data structure with instanced dynamics data and configuration data of a vehicle with just wheels +@see PxVehicleDrive, PxVehicleDrive4W, PxVehicleDriveTank +*/ +class PxVehicleWheels : public PxBase +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + friend class PxVehicleConstraintShader; + + /** + \brief Return the type of vehicle + @see PxVehicleTypes + */ + PX_FORCE_INLINE PxU32 getVehicleType() const {return mType;} + + /** + \brief Get non-const ptr to PxRigidDynamic instance that is the vehicle's physx representation + */ + PX_FORCE_INLINE PxRigidDynamic* getRigidDynamicActor() {return mActor;} + + /** + \brief Get const ptr to PxRigidDynamic instance that is the vehicle's physx representation + */ + PX_FORCE_INLINE const PxRigidDynamic* getRigidDynamicActor() const {return mActor;} + + /** + \brief Compute the rigid body velocity component along the forward vector of the rigid body transform. + @see PxVehicleSetBasisVectors + */ + PxReal computeForwardSpeed() const; + + /** + \brief Compute the rigid body velocity component along the right vector of the rigid body transform. + @see PxVehicleSetBasisVectors + */ + PxReal computeSidewaysSpeed() const; + + /** + \brief Data describing the setup of all the wheels/suspensions/tires. + */ + PxVehicleWheelsSimData mWheelsSimData; + + /** + \brief Data describing the dynamic state of all wheels/suspension/tires. + */ + PxVehicleWheelsDynData mWheelsDynData; + +protected: + + /** + \brief Set all wheels to their rest state + */ + void setToRestState(); + + /** + \brief Test that all configuration and instanced dynamics data is valid. + */ + bool isValid() const; + + /** + @see PxVehicleDrive4W::allocate, PxVehicleDriveTank::allocate + */ + static PxU32 computeByteSize(const PxU32 nbWheels); + static PxU8* patchupPointers(const PxU32 nbWheels, PxVehicleWheels* vehWheels, PxU8* ptr); + virtual void init(const PxU32 numWheels); + + /** + \brief Deallocate a PxVehicleWheels instance. + @see PxVehicleDrive4W::free, PxVehicleDriveTank::free + */ + void free(); + + /** + @see PxVehicleDrive4W::setup, PxVehicleDriveTank::setup + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, + const PxU32 nbDrivenWheels, const PxU32 nbNonDrivenWheels); + + /** + \brief The rigid body actor that represents the vehicle in the PhysX SDK. + */ + PxRigidDynamic* mActor; + +private: + + /** + \brief Count the number of constraint connectors that have hit their callback when deleting a vehicle. + Can only delete the vehicle's memory when all constraint connectors have hit their callback. + */ + PxU32 mNbNonDrivenWheels; + + PxU8 mOnConstraintReleaseCounter; + +protected: + + /** + \brief Vehicle type (eVehicleDriveTypes) + */ + PxU8 mType; + +#if PX_P64_FAMILY + PxU8 mPad0[14]; +#else + PxU8 mPad0[14]; +#endif + +//serialization +public: + virtual void requiresObjects(PxProcessPxBaseCallback& c); + virtual const char* getConcreteTypeName() const { return "PxVehicleWheels"; } + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleWheels", name) || PxBase::isKindOf(name); } + virtual void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext&); + void resolveReferences(PxDeserializationContext&); + static void getBinaryMetaData(PxOutputStream& stream); + PX_FORCE_INLINE PxU32 getNbNonDrivenWheels() const { return mNbNonDrivenWheels; } + PxVehicleWheels(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PxVehicleWheels(PxBaseFlags baseFlags) : PxBase(baseFlags), mWheelsSimData(PxEmpty) {} + virtual ~PxVehicleWheels() {} + virtual void release() { free(); } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleWheels) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_WHEELS_H diff --git a/src/PhysX/physx/platform_readme.html b/src/PhysX/physx/platform_readme.html new file mode 100644 index 000000000..8d9c05553 --- /dev/null +++ b/src/PhysX/physx/platform_readme.html @@ -0,0 +1,16 @@ + + + + +Platform Specific Readme Files + + + + + + +


+Copyright (C) 2008-2018 NVIDIA Corporation, 2701 San Thomas Expressway, Santa Clara, CA 95050 U.S.A. All rights reserved. www.nvidia.com +

+ + diff --git a/src/PhysX/physx/release_notes.html b/src/PhysX/physx/release_notes.html new file mode 100644 index 000000000..ea4e7b40f --- /dev/null +++ b/src/PhysX/physx/release_notes.html @@ -0,0 +1,3680 @@ + + + + + Release Notes - NVIDIA PhysX SDK 4.0 + + + + + + + +

Release Notes - NVIDIA® PhysX® SDK 4.0

+

December 2018

+ +

Supported Platforms

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Runtime + Development
+ Apple iOS (tested on 12.1) + Xcode (tested with 10.1)
+ Apple macOS (tested on 10.13) + Xcode (tested with 10.1)
+ Google Android ARM (tested with API Level 19 - KITKAT) + NDK r13b
+ Linux (tested on Ubuntu 16.04), GPU acceleration: display driver and GPU supporting CUDA 10 / CUDA ARCH 3.0 + Clang (tested with 3.8)
+ Microsoft Windows, GPU acceleration: display driver and GPU supporting CUDA 10 / CUDA ARCH 3.0 + Microsoft Visual Studio 2013, 2015, 2017
+ Microsoft XBox One* +
+ Nintendo Switch* +
+ Sony Playstation 4* +
+ + * Console code subject to platform NDA not available on GitHub. Developers licensed by respective platform owners please contact NVIDIA for access. + +

Changes and Resolved Issues

+
+ + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
+ +

General

+
    +
  • Added:
  • +
      +
    • New Temporal Gauss Seidel (TGS) solver offering a new level of simulation accuracy.
    • +
    • New Reduced Coordinate Articulation feature with no relative positional error and realistic actuation.
    • +
    • New automatic multi-broadphase (ABP) providing better out of the box performance for many use cases.
    • +
    • New BVH structure supporting better performance for actors with many shapes.
    • +
    +
  • Removed:
  • +
      +
    • PhysX Particle feature.
    • +
    • PhysX Cloth feature.
    • +
    • The deprecated active transforms feature has been removed. Please use active actors instead.
    • +
    • The deprecated multi client behavior feature has been removed.
    • +
    • The deprecated legacy heightfields have been removed.
    • +
    +
  • Changed:
  • +
      +
    • The PhysX SDK build system is now based on CMake generated build configuration files. For more details, please refer to the PhysX SDK 4.0 Migration Guide.
    • +
    • The Linux build has been changed to produce static as opposed to shared libraries. The compiler was switched from GCC to Clang.
    • +
    • The PxShared library contains functionality shared beyond the PhysX SDK. It has been streamlined to a minimal set of headers. The PxFoundation singleton has been moved back to the PhysX SDK, as well as the task manager, CUDA context manager and PhysX Visual Debugger (PVD) functionality.
    • +
    • PhysXDelayLoadHook and PhysXGpuLoadHook have been simplified, and PxFoundationDelayLoadHook has been removed.
    • +
    +
+ +

Rigid Bodies

+
    +
  • Added:
  • +
      +
    • A new broadphase implementation has been added. See PxBroadPhaseType::eABP for details. This is now the default broadphase implementation.
    • +
    • TGS: A new rigid body solver, which can produce improved convergence compared to the default rigid body solver.
    • +
    • Optional torsional friction model to simulate rotational friction when there is just a single point of contact.
    • +
    • A flag to enable friction constraints to be processed every frame has been added
    • +
    • PxContactJoint added to represent contacts in inverse dynamics. Not intended for use in simulation.
    • +
    • A missing PxShape::getReferenceCount() function has been added.
    • +
    +
  • Removed:
  • +
      +
    • PxVisualizationParameter::eDEPRECATED_BODY_JOINT_GROUPS has been removed.
    • +
    • PxSceneDesc::maxNbObjectsPerRegion has been removed.
    • +
    • PxRigidActor::createShape() has been removed. Please use PxPhysics::createShape() or PxRigidActorExt::createExclusiveShape() instead
    • +
    • The deprecated mass parameter in PxTolerancesScale has been removed.
    • +
    • PxSceneFlag::eDEPRECATED_TRIGGER_TRIGGER_REPORTS has been removed.
    • +
    +
  • Changed:
  • +
      +
    • Aggregates can now contain more than 128 actors.
    • +
    • Switching a kinematic object to dynamic does not automatically wake up the object anymore. Explicit calls to PxRigidDynamic::wakeUp() are now needed.
    • +
    • Switching a kinematic object to dynamic re-inserts the object into the broadphase, producing PxPairFlag::eNOTIFY_TOUCH_FOUND events instead of PxPairFlag::eNOTIFY_TOUCH_PERSISTS events.
    • +
    • PxConvexMeshGeometryFlag::eTIGHT_BOUNDS is now enabled by default for PxConvexMeshGeometry.
    • +
    • The default max angular velocity for rigid bodies has been changed, from 7 to 100.
    • + +
    +
+ + +

Extensions

+
    +
  • Added:
  • +
      +
    • PxD6Joint now supports per-axis linear limit pairs.
    • +
    • Added PxSphericalJoint::getSwingYAngle and PxSphericalJoint::getSwingZAngle.
    • +
    • Added PxD6Joint distance limit debug visualization.
    • +
    • Added PxD6JointCreate.h file with helper functions to setup the D6 joint in various common configurations.
    • +
    • Added pyramidal swing limits to the D6 joint.
    • +
    + +
  • Removed:
  • +
      +
    • PxComputeHeightFieldPenetration has a new signature.
    • +
    • PxComputeMeshPenetration has been removed. Use PxComputeTriangleMeshPenetration instead.
    • +
    + +
  • Changed:
  • +
      +
    • PxRevoluteJoint now properly supports a -PI*2 to +PI*2 range for its limits, and the accuracy of limits has been improved. In order to use extended limit ranges, PxConstraintFlag::eENABLE_EXTENDED_LIMITS must be raised on the constraint.
    • +
    • PxD6Joint now properly supports a -PI*2 to +PI*2 range for its twist limit, and the accuracy of the twist limit has been improved. In order to use extended limit ranges, PxConstraintFlag::eENABLE_EXTENDED_LIMITS must be raised on the constraint.
    • +
    • The accuracy of the D6 joint swing limits has been improved.
    • +
    • PxDistanceJoint does now always insert constraint row, this change does increase the limit precision.
    • +
    • PxDistanceJoint::getDistance does not anymore return squared distance.
    • +
    • PxD6Joint::setDriveVelocity, PxD6Joint::setDrivePosition and PxRevoluteJoint::setDriveVelocity have now additional parameter autowake, which will wake the joint rigids up if true (default behavior).
    • +
    • Joint shaders now take a bool to define whether to use extended joint limits or not.
    • +
    • Joint shaders must now provide the cA2w and cB2w vectors, defining the world-space location of the joint anchors for both bodies.
    • +
    + +
  • Deprecated:
  • +
      +
    • PxD6Joint::getTwist() has been deprecated. Please use PxD6Joint::getTwistAngle() now.
    • +
    • The previous PxD6Joint::setLinearLimit() and PxD6Joint::getLinearLimit() functions (supporting a single linear limit value) have been deprecated. Please use PxD6Joint::setDistanceLimit() and PxD6Joint::getDistanceLimit() instead. Or you can also use the new PxD6Joint::setLinearLimit() and PxD6Joint::getLinearLimit() functions, which now support pairs of linear limit values.
    • +
    +
+ +

Articulations

+
    +
  • Added:
  • +
      +
    • New reduced coordinate articulation implementation, supporting a wider range of joint types, more accurate drive model, inverse dynamics and joint torque control.
    • +
    • PxArticulationJoint::getParentArticulationLink and PxArticulationJoint::getChildArticulationLink has been added.
    • +
    +
+ +

Scene queries

+
    +
  • Removed:
  • +
      +
    • PxHitFlag::eDISTANCE has been removed.
    • +
    • The PxVolumeCache feature has been removed.
    • +
    • The PxSpatialIndex feature has been removed.
    • +
    • The deprecated PxSceneFlag::eSUPPRESS_EAGER_SCENE_QUERY_REFIT has been removed.
    • +
    +
+ +

Cooking

+
    +
  • Added:
  • +
      +
    • PxBVHStructure added, it computes and stores BVH structure for given bounds. The structure can be used for actors with large amount of shapes to perform scene queries actor centric rather than shape centric. For more information please see guide or snippets.
    • +
    +
  • Removed:
  • +
      +
    • PxPlatform enum has been removed. PhysX supported platforms all share the same endianness
    • +
    • The deprecated PxCookingParams::meshCookingHint and PxCookingParams::meshSizePerformanceTradeOff parameters have been removed.
    • +
    • The deprecated PxGetGaussMapVertexLimitForPlatform has been removed, use PxCookingParams::gaussMapLimit instead.
    • +
    • The deprecated PxConvexMeshCookingType::eINFLATION_INCREMENTAL_HULL and PxCookingParams::skinWidth have been removed.
    • +
    • PxBVH34MidphaseDesc::numTrisPerLeaf has been renamed to PxBVH34MidphaseDesc::numPrimsPerLeaf
    • +
    +
+ + +
+ + + +

Release Notes - NVIDIA® PhysX® SDK 3.4.2.25354359

+

December 2018

+ +

General

+
    +
  • Changed:
  • +
      +
    • Changed GitHub distribution to BSD license.
    • +
    +
+ +

Supported Platforms

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Runtime + Development
+ Apple iOS (tested on 12.1) + Xcode (tested with 10.1)
+ Apple macOS (tested on 10.13) + Xcode (tested with 10.1)
+ Google Android ARM (tested with API Level 16, Android 4.1 - JELLY_BEAN) + NDK r13b-win32
+ Linux (tested on Ubuntu 16.04, GPU acceleration: NVIDIA Driver version R361+ and CUDA ARCH 3.0) + GCC (tested with 4.8)
+ Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration) + Microsoft Visual Studio 2012, 2013, 2015
+ Microsoft XBox One* +
+ Nintendo Switch* +
+ Sony Playstation 4* +
+ + * Console code subject to platform NDA not available on GitHub. Developers licensed by respective platform owners please contact NVIDIA for access. + + + +

Release Notes - NVIDIA® PhysX® SDK 3.4.2.25256367

+

November 2018

+ +

General

+
    +
  • Changed:
  • +
      +
    • A couple of serialization write functions have been optimized.
    • +
    • Rtree cooking has been slightly optimized.
    • +
    • Renamed PxSerializer::requires to PxSerializer::requiresObjects due to an erroneous clash with C++20 keyword with apple-clang.
    • +
    +
  • Fixed:
  • +
  • Moved external vector math includes out of PhysX namespaces.
  • +
+ + +

Rigid Bodies

+
    +
  • Fixed:
  • +
      +
    • Fixed an incorrect trigger behavior when a trigger was removed and inserted within the same frame.
    • +
    +
+ +

Scene query

+
    +
  • Fixed:
  • +
      +
    • Fixed a bug in BVH34. Raycasts could fail on binary deserialized BVH34 triangle meshes.
    • +
    +
+ + + +

Release Notes - NVIDIA® PhysX® SDK 3.4.2.24990349

+

September 2018

+ +

General

+
    +
  • Fixed:
  • +
      +
    • PxMeshQuery::getTriangle adjacency information for heightfield geometry fixed.
    • +
    • Removed PxSetPhysXGpuDelayLoadHook from API. Delay loaded dynamically linked library names are now provided through PxSetPhysXDelayLoadHook.
    • +
    • Fixed a source of non-determinism with GPU rigid bodies.
    • +
    +
+ +

Rigid Bodies

+
    +
  • Fixed:
  • +
      +
    • Fixed a divide by zero bug when gjk is trying to calculate the barycentric coordinate for two identical/nearly identical points.
    • +
    • Fixed an incorrect mesh index reported in contact buffer when stabilization flag was used.
    • +
    +
+ + + +

Release Notes - NVIDIA® PhysX® SDK 3.4.2.24698370

+

August 2018

+ +

Rigid Bodies

+
    +
  • Fixed:
  • +
      +
    • Fixed a crash bug when EPA's edge buffer overflow.
    • +
    • GPU rigid bodies fixed for Volta GPUs.
    • +
    +
  • Added:
  • +
      +
    • Aggregate broad phase now runs in parallel
    • +
    +
+ +

Cooking

+
    +
  • Fixed:
  • +
      +
    • Added:
    • +
        +
      • PxHeightField::getSample has been added.
      • +
      +
    + +

    Character controller

    +
      +
    • Fixed:
    • +
        +
      • Capsule controller with a very small height could degenerate to spheres (with a height of exactly zero) far away from the origin, which would then triggers errors in Debug/Checked builds. This has been fixed.
      • +
      • The triangle array growing strategy has been changed again to prevent performance issues when tessellation is used. Memory usage may increase in these cases.
      • +
      • Some internal debug visualization code has been disabled and a crash in it has been fixed.
      • +
      +
    + +

    Scene query

    +
      +
    • Fixed:
    • +
        +
      • Fixed possible buffer overrun when PxPruningStructure was used.
      • +
      • Raycasts against a heightfield may have missed if a large distance was used.
      • +
      • Sq raycasts against heightfield or triangle mesh could return a mildly negative values, this has been fixed.
      • +
      +
    + + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.2.24214033

    +

    May 2018

    + +

    General

    +
      +
    • Fixed:
    • +
        +
      • Fixed clang 7 unionCast issues.
      • +
      • Fixed the binary meta data in PhysX_3.4/Tools/BinaryMetaData for conversion of vehicles.
      • +
      +
    + +

    Rigid Bodies

    +
      +
    • Deprecated:
    • +
        +
      • PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS and PxSceneDesc::eENABLE_KINEMATIC_PAIRS have been deprecated. Use the new PxPairFilteringMode parameters in PxSceneDesc instead.
      • +
      +
    • Fixed:
    • +
        +
      • A sequence of shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, false) / shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, true) without simulation calls inbetween could produce errors in the broadphase. This has been fixed.
      • +
      • Fixed a bug in the broadphase (SAP) that got recently introduced in SDK 3.4.1.23933511. It should only have generated a few false positives (more overlaps than stricly necessary).
      • +
      • Fixed a bug in PxShape::checkMaterialSetup.
      • +
      • Fixed intermittent crash with GPU rigid bodies when materials were destroyed.
      • +
      • Fixed bug where setting maxImpulse to 0 on CCD contact modification meant contact was not reported in the frame's contact reports.
      • +
      +
    + +

    Cooking

    +
      +
    • Fixed:
    • +
        +
      • Big convex mesh serialization used together with insertion callback stored incorrect memory for big convex data. This has been fixed.
      • +
      +
    + +

    Scene query

    +
      +
    • Fixed:
    • +
        +
      • Fixed a bug in extended bucket pruner, when a pruning structure was added and immediatelly released.
      • +
      +
    + + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.1.23933511

    +

    April 2018

    + +

    General

    +
      +
    • Added:
    • +
        +
      • Added snippet for deformable meshes, added section in the guide for them.
      • +
      +
    + +

    Rigid Bodies

    +
      +
    • Fixed:
    • +
        +
      • PxTriangleMesh::refitBVH() was crashing with PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE. This has been fixed.
      • +
      • SQ-only shapes contained in aggregates could result in crashes or memory corruption when removed from the aggregate. This has been fixed.
      • +
      • Assert no longer fired if a dynamic body contacting a static is converted to kinematic with kinematic-static pairs enabled.
      • +
      • Assert reporting broad phase as being "inconsistent" could fire when using speculative CCD when sleeping objects were activated.
      • +
      +
    + +

    Cooking

    +
      +
    • Fixed:
    • +
        +
      • Convex hull cooking could have produced hulls with vertices too far from convex hull planes, this has been fixed.
      • +
      +
    • Changed:
    • +
        +
      • PxCooking::createTriangleMesh does now take additional output parameter PxTriangleMeshCookingResult::Enum.
      • +
      • PxCooking::createConvexMesh does now take additional output parameter PxConvexMeshCookingResult::Enum.
      • +
      +
    + +

    Scene query

    +
      +
    • Changed:
    • +
        +
      • PxSceneFlag::eSUPPRESS_EAGER_SCENE_QUERY_REFIT has been marked as deprecated. Was replaced with PxSceneQueryUpdateMode enum, if this new enum is set the flag gets ignored.
      • +
      +
    • Added:
    • +
        +
      • PxSceneQueryUpdateMode was added to control work done during fetchResults.
      • +
      • PxScene::sceneQueriesUpdate, PxScene::checkQueries and PxScene::fetchQueries were added to run separate scene query update, see manual for more details.
      • +
      +
    + + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.1.23584284

    +

    February 2018

    + +

    General

    +
      +
    • Fixed:
    • +
        +
      • PhysX sometimes froze in a spinlock after certain sequences of read & write locks. This has been fixed.
      • +
      +
    + +

    Scene queries

    +
      +
    • Fixed:
    • +
        +
      • Raycasts against heightfields were sometimes missing hits for vertical rays were located exactly at the heightfield's boundaries. This has been fixed.
      • +
      +
    + +

    Rigid Bodies

    +
      +
    • Fixed:
    • +
        +
      • Avoid edge-face collisions on boundary edges when eNO_BOUNDARY_EDGES flag is raised on heightfield when using PCM and unified HF collision.
      • +
      +
    + + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.1.23472123

    +

    January 2018

    + +

    General

    +
      +
    • Added:
    • +
        +
      • Visual Studio 2017 15.5.1 and newer is now supported. Samples are currently not supported with Visual Studio 2017.
      • +
      + +
    • Removed:
    • +
        +
      • Visual Studio 2012 support is discontinued.
      • +
      +
    + +

    Cooking

    +
      +
    • Fixed:
    • +
        +
      • Cooked mesh structures contained a mix of little-endian and big-endian data (the midphase structures were always saved as big-endian). This made loading of cooked files slower than necessary. This has been fixed.
      • +
      +
    + +

    Scene queries

    +
      +
    • Fixed:
    • +
        +
      • Buffered moves were sometimes not properly taken into account by scene queries, leading to invalid results for one frame. This has been fixed.
      • +
      • Pruning structure failed to build when actor had more shapes. This has been fixed.
      • +
      +
    + + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.1.23173160

    +

    November 2017

    + +

    Extensions

    +
      +
    • Fixed:
    • +
        +
      • An issue with CCD sweeps against meshes that could potentially lead to the earliest impact not being detected has been fixed.
      • +
      +
    + + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.1.23131702

    +

    November 2017

    + +

    General

    +
      +
    • Fixed:
    • +
        +
      • A bug in the management of internal interaction objects has been fixed.
      • +
      +
    + +

    Extensions

    +
      +
    • Fixed:
    • +
        +
      • A regression in prismatic constraint stability introduced in PhysX 3.4.1 has been fixed.
      • +
      • PxRevoluteJoint::getAngle(), PxD6Joint::getTwist(), PxD6Joint::getSwingYAngle() and PxD6Joint::getSwingZAngle() did not always return the correct angle. This problem has been fixed.
      • +
      • The "double cone" case of the D6 joint had errors both in the debug visualization and the code dealing with limits. This has been fixed.
      • +
      • The debug visualization of the D6 joint in the twist case did not properly color-code the angular limits. This has been fixed.
      • +
      • The debug visualization of distance joints has been fixed.
      • +
      • The debug visualization of prismatic joints has been fixed.
      • +
      • The debug visualization of revolute joints has been fixed. It now renders active limits properly (in red when the limit is active, grey otherwise).
      • +
      • Proper visualization flags are now passed to the PxConstraintVisualize function. Previously all available flags were active, even if PxVisualizationParameter::eJOINT_LOCAL_FRAMES and/or PxVisualizationParameter::eJOINT_LIMITS were set to zero.
      • +
      • PxRevoluteJoint::getVelocity has been fixed.
      • +
      +
    + +

    Scene queries

    +
      +
    • Fixed:
    • +
        +
      • Sweep queries using the eMTD flag could generate incorrect normals in sphere/sphere, sphere/capsule or capsule/capsule cases, when the objects were exactly overlapping each-other. This has been fixed.
      • +
      • Sweep convex mesh vs heightfield queries using the eMTD flag did not fill correctly returned faceIndex. This has been fixed.
      • +
      +
    + + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.1

    +

    September 2017

    + +

    Changes and Resolved Issues

    +
    + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
    + +

    General

    +
      +
    • Deprecated:
    • +
        +
      • The PhysX cloth feature has been deprecated.
      • +
      +
    • Changed:
    • +
        +
      • The meaning of PxVisualizationParameter::eCULL_BOX has changed. It is now used to visualize the current culling box, while it was previously used to enable or disable the feature. Please simply use PxScene::setVisualizationCullingBox() to enable the feature from now on.
      • +
      • PxVisualizationParameter::eBODY_JOINT_GROUPS has been deprecated.
      • +
      • Performance of setCMassLocalPose function has been improved.
      • +
      +
    • Added:
    • +
        +
      • PhysXGpu: Added warnings for the case when the dynamic gpu library fails to load.
      • +
      +
    + +

    Rigid Bodies

    +
      +
    • Fixed:
    • +
        +
      • A potential crash when calling detachShape has been fixed.
      • +
      • Fixed assert that fired if application switched a body from dynamic to kinematic, then queried kinematic target without setting one. This assert only fired if the modifications overlapped simulation so were buffered.
      • +
      • PxArticulation::getStabilizationThreshold() and PxArticulation::setStabilizationThreshold() were accessing the sleep threshold instead of the stabilization threshold. This has been fixed.
      • +
      • Fixed an internal edge bug in PCM sphere vs mesh code
      • +
      • Make sure sphere vs sphere and sphere vs box in PCM contact gen generate contacts consistently on the second body when the sphere center is contained in the other shape
      • +
      +
    • Changed:
    • +
        +
      • Improved convex vs mesh contact generation when using GPU rigid bodies. Requires the mesh to be recooked.
      • +
      • Improved convex vs convex contact generation when using GPU rigid bodies.
      • +
      • Reduced memory footprint of GPU triangle meshes significantly. Requires the mesh to be recooked.
      • +
      +
    • Added:
    • +
        +
      • Support for modifying friction and restitution coefficients has been added to contact modification.
      • +
      • Added PxRigidBodyFlag::eENABLE_CCD_MAX_CONTACT_IMPULSE to enable maxContactImpulse member of PxRigidBody to be used in CCD. This is disabled by default. It is useful in some circumstances, e.g. shooting a small ball through a plate glass window and triggering it to break, but it can also result in behavioral artefacts so it is disabled by default.
      • +
      • Added PxSceneDesc::solverOffsetSlop. This is defaulted to a value of 0. A positive, non-zero value defines a tolerance used in the solver below which a contacts' offset from the COM of the body is negligible and therefore snapped to zero. This clamping occurs in a space tangential to the contact or friction direction. This is aimed at pool or golf simulations, where small numerical imprecision in either contact points or normals can lead to balls curving slightly when there are relatively high angular velocities involved.
      • +
      • Added PxConvexMeshGeometry::maxMargin. This allows the application to tune the maximum amount by which PCM collision detection will shrink convex shapes in contact generation. This shrinking approach leads to some noticeable clipping around edges and vertices, but should not lead to clipping with face collisions. By default, the mesh is shrunk by an amount that is automatically computed based on the shape's properties. This allows you to limit by how much the shape will be shrunk. If the maxMargin is set to 0, then the original shape will be used for collision detection.
      • +
      + +
    + +

    Scene queries

    +
      +
    • Fixed:
    • +
        +
      • A rare invalid memory read that could lead to incorrect sweep results in case of an initial overlap has been fixed.
      • +
      +
    + +

    Serialization

    +
      +
    • Fixed:
    • +
        +
      • Binary serialization didn't preserve PxConstraintFlags, e.g. projection flags.
      • +
      • Xml serialization failed if a shape referencing a PxTriangleMesh was added to another (dependent) collection.
      • +
      +
    + + +
    + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.0.22387197

    +

    June 2017

    + +

    Changes and Resolved Issues

    +
    + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
    + +

    Cooking

    +
      +
    • Fixed:
    • +
        +
      • Fixed issue when PxConvexFlag::e16_BIT_INDICES was used together with PxConvexFlag::eCOMPUTE_CONVEX.
      • +
      • Fixed issue in convex hull cooking when postHullMerge was not executed.
      • +
      • Fixed crash in CCD when using bodies with 0 mass.
      • +
      +
    + +

    Rigid Bodies

    +
      +
    • Fixed:
    • +
        +
      • Fixed behavioral differences when comparing the results of a given scene to the results when simulating a subset of the islands in that scene. In order for this case to be deterministic, it is necessary to raise PxSceneFlag::eENABLE_ENHANCED_DETERMINISM.
      • +
      + +
    • Added:
    • +
        +
      • Introduced maxBiasCoefficient in PxSceneDesc to be able to limit the coefficient used to scale error to produce the bias used in the constraint solver to correct geometric error. The default value is PX_MAX_F32 and, therefore, a value of 1/dt will be used. This value can be useful to reduce/remove jitter in scenes with variable or very small time-steps. +
      +
    + + +
    + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.0.22121272

    +

    May

    + +

    Changes and Resolved Issues

    +
    + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
    + +

    Rigid Bodies

    +
      +
    • Fixed:
    • +
        +
      • Fixed a bug in convex vs convex PCM contact gen code. There were cases in which full contact gen should have been triggered but was not.
      • +
      • Fixed a jittering bug on both GPU/CPU codepath in PCM contact gen code. This is due to the contact recycling condition not considering the toleranceLength.
      • +
      • Fixed a bug causing simulation to behave incorrectly when greater than 65536 bodies were simulated.
      • +
      +
    + +

    Scene queries

    +
      +
    • Fixed:
    • +
        +
      • A rare crash that could happen with sphere overlap calls has been fixed.
      • +
      • Fixed a stack corruption in CCD contact modification callback.
      • +
      • Fixed a bug where external forces were not cleared correctly with PxArticulations.
      • +
      +
    + +

    Cooking

    +
      +
    • Changed:
    • +
        +
      • Convex hull cooking now reuses edge information, perf optimization.
      • +
      • PxCooking API is now const if possible.
      • +
      +
    + + +
    + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.0.22017166

    +

    April 2017

    + +

    Changes and Resolved Issues

    +
    + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
    + +

    General

    +
      +
    • Added:
    • +
        +
      • PxConvexMeshGeometry::maxMargin has been added. This will let the application limit how much the shape is shrunk in GJK when using by PCM contact gen
      • +
      +
    + +

    Rigid Bodies

    +
      +
    • Fixed:
    • +
        +
      • Fixed a bug with joint breaking where sometimes joints would not break as expected.
      • +
      • Fix a race condition between cloth/particle/trigger interactions and the parallel filtering of rigid body interaction.
      • +
      • GPU rigid body feature now issues a performance warning in checked build if feature is enabled but PCM contact gen is not.
      • +
      • Fixed a bug with applying external force/torque to a body in buffered insertion stage.
      • +
      • Fixed a bug with CCD involving rotated static mesh actors.
      • +
      • Fixed a memory leak in CCD.
      • +
      +
    • Changed:
    • +
        +
      • Optimizations for GPU rigid body feature, including reduced memory footprint and improvements to performance spikes when many objects are woken in a single frame.
      • +
      +
    + +

    Midphase

    +
      +
    • Fixed:
    • +
        +
      • Fix Crash in BV4 code (serialization bug).
      • +
      +
    + +

    Cooking

    +
      +
    • Fixed:
    • +
        +
      • Fix endless loop in convex hull cooking.
      • +
      +
    + + +
    + + +

    Release Notes - NVIDIA® PhysX® SDK 3.4.0.21821222

    +

    March 2017

    + +

    Supported Platforms

    +
    +

    Runtime

    +
      +
    • Apple iOS
    • +
    • Apple Mac OS X
    • +
    • Google Android ARM (version 2.2 or later required for SDK, 2.3 or later required for snippets)
    • +
    • Linux (tested on Ubuntu)
    • +
    • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
    • +
    • Microsoft XBox One
    • +
    • Nintendo Switch
    • +
    • Sony Playstation 4
    • +
    +

    Development

    +
      +
    • Microsoft Windows XP or later
    • +
    • Microsoft Visual Studio 2012, 2013, 2015
    • +
    • Xcode 8.2
    • +
    +
    +

    Known Issues

    +
    +
      +
      +

      Changes and Resolved Issues

      +
      + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
      + +

      General

      +
        +
      • Fixed:
      • +
          +
        • A rare crash happening in the debug visualization code has been fixed.
        • +
        +
      + +

      Rigid Bodies

      +
        +
      • Fixed:
      • +
          +
        • Fixed a bug in PCM capsule vs plane contact gen.
        • +
        • A crash happening with more than 64K interactions has been fixed.
        • +
        • Fixed an island management issue in CCD when using multiple CCD passes.
        • +
        • Fixed a bug with GPU rigid bodies with non-simulation scene query-only shapes.
        • +
        • Fixed a bug in convex vs convex PCM contact gen code. There were cases in which full contact gen should have been triggered but was not.
        • +
        • Fixed a jittering bug on both GPU/CPU codepath in PCM contact gen code. This is due to the contact recycling condition not considering the toleranceLength.
        • +
        +
      • Added:
      • +
          +
        • getFrozenActors has been added to allow application queries the frozen actors
        • +
        • PxRigidDynamic::setKinematicSurfaceVelocity has been added, permitting the user to set a persistent velocity on a kinematic actor which behaves like a conveyor belt
        • +
        • PxSceneDesc::solverOffsetSlop added. This defines a threshold distance from a body's COM under which a contact will be snapped to the COM of the body inside the solver along any principal component axis
        • +
        +
      + +

      Scene queries

      +
        +
      • Fixed:
      • +
          +
        • A bug in the BVH34 overlap code sometimes made PxMeshOverlapUtil::findOverlap assert (reporting an incorrect buffer overflow). This has been fixed.
        • +
        • Fix a bug in the case of two primitives just touching in sweep with eMTD flag on.
        • +
        +
      + + +
      + + +

      Release Notes - NVIDIA® PhysX® SDK 3.4

      +

      February 2017

      + +

      Supported Platforms

      +
      +

      Runtime

      +
        +
      • Apple iOS
      • +
      • Apple Mac OS X
      • +
      • Google Android ARM (version 2.2 or later required for SDK, 2.3 or later required for snippets)
      • +
      • Linux (tested on Ubuntu)
      • +
      • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
      • +
      • Microsoft XBox One
      • +
      • Nintendo Switch
      • +
      • Sony Playstation 4
      • +
      +

      Development

      +
        +
      • Microsoft Windows XP or later
      • +
      • Microsoft Visual Studio 2012, 2013, 2015
      • +
      • Xcode 8.2
      • +
      +
      +

      Known Issues

      +
      +
        +
        +

        Changes and Resolved Issues

        +
        + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

        General

        +
          +
        • Added:
        • +
            +
          • nbAggregates, nbArticulations, nbDiscreteContactPairsTotal, nbDiscreteContactPairsWithCacheHits and nbDiscreteContactPairsWithContacts have been added to PxSimulationStatistics.
          • +
          • PxSceneLimits::maxNbBroadPhaseOverlaps has been added.
          • +
          • A new midphase has been added. See PxMeshMidPhase enum for details.
          • +
          • Midphase descriptor has been added. See PxMidphaseDesc for details.
          • +
          • PxHeightField::getTimestamp() has been added
          • +
          • PxCloneShape has been added to enable cloning of shapes.
          • +
          • acquireReference() has been added to increment the reference count of shapes, materials, triangle meshes, convex meshes heightfields and cloth fabrics.
          • +
          • The global filter shader data block can now be set through PxScene::setFilterShaderData().
          • +
          • PxGpuLoadHook and PxSetPhysXGpuLoadHook has been added to support loading of GPU dll with a different name than the default.
          • +
          • A new profile zone has been added for debug visualization.
          • +
          +
        • Changed:
        • +
            +
          • PxMathUtils.h has moved from include/common to include/foundation
          • +
          • GPU support requires SM2.x (Fermi architecture, GeForce 400 series) or later hardware. SM1.x is no longer supported.
          • +
          • Windows XP 64-bit and Windows Vista no longer support GPU acceleration. Windows XP 32-bit still supports GPU acceleration.
          • +
          • PxTaskManager::createTaskManager requires error callback and does not accept SPU task manager.
          • +
          • PxCreatePhysics and PxCreateBasePhysics now take an optional pointer to an physx::PxPvd instance.
          • +
          • PxConstraintConnector::updatePvdProperties now takes an optional pointer to an physx::pvdsdk::PvdDataStream instance.
          • +
          • PxInitExtensions now takes an optional pointer to an physx::PxPvd instance.
          • +
          • Shared objects (triangle mesh, convex mesh, heightfield, material, shape, cloth fabric) no longer issue an eUSER_RELEASE event when their release() method is called
          • +
          • + PxBase::isReleasable() is now a property only of the type of an object, not the object state. In particular, + isReleasable() returns true for PxShape objects whose only counted reference belongs to their owning actor. +
          • +
          • PxCollection::releaseObjects() now calls release() even on shapes whose only counted reference belongs to their owning actor. An optional parameter releaseExclusiveShapes, which defaults to true, has been added to this method to assist with common scenarios in which all shapes are created with the deprecated method PxRigidActor::createShape() or its replacement PxRigidActorExt::createExclusiveShape()
          • +
          • Negative mesh scale is now supported for PxTriangleMeshGeometry. Negative scale corresponds to reflection and scale along the corresponding axis. In addition to reflection PhysX will flip the triangle normals.
          • +
          • PxDelayLoadHook is now inherited from PxFoundationDelayLoadHook. PxFoundation dll and PxPvdSDK dll are now delay loaded inside the SDK, their names can be provided through the delay load hook.
          • +
          +
        • Removed:
        • +
            +
          • Sony Playstation 3 is not supported any longer. Any related APIs have been removed.
          • +
          • Microsoft XBox 360 is not supported any longer. Any related APIs have been removed.
          • +
          • Nintendo Wii U is not supported any longer. Any related APIs have been removed.
          • +
          • Sony Playstation Vita is not supported any longer. Any related APIs have been removed.
          • +
          • Visual Studio 2010 is not supported any longer.
          • +
          • Microsoft Windows RT is not supported any longer.
          • +
          • Google Android X86 is not supported any longer.
          • +
          • PhysX Samples are not supported anymore except on Microsoft Windows.
          • +
          • Linux 32-bit no longer support GPU acceleration. Linux 64-bit still supports GPU acceleration.
          • +
          +
        • Fixed:
        • +
            +
          • PxScene::setFlag() does now properly send error messages in CHECKED builds if a non-mutable scene flag gets passed in.
          • +
          • Fixed a bug in force threshold based contact reports, which caused events to be lost.
          • +
          • Fixed a bug in aggregates that led to a crash when rigid bodies are added to an aggregate after removing all rigid bodies from an aggregate. This only occurred with aggregates that were added to the scene and with rigid bodies that had shapes attached.
          • +
          • Fixed a bug where non-breakable joints could break, leading to a crash.
          • +
          • Fixed RepX load of kinematic rigid bodies with mesh shapes.
          • +
          • Fixed a divide by zero bug in SIMD distanceSegmentTriangle function.
          • +
          • Debug visualization for compound bounds (PxVisualizationParameter::eCOLLISION_COMPOUNDS) now works even when all the compounds' shapes have their debug viz flag (PxShapeFlag::eVISUALIZATION) disabled.
          • +
          • Double-buffering now works properly for the debug visualization culling box.
          • +
          • The default debug visualization culling box now works correctly with heightfields.
          • +
          • Rare crashes in PxShape::setGeometry() and PxShape::getMaterials() have been fixed.
          • +
          • A crash happening when doing an origin shift on a scene containing an empty aggregate has been fixed.
          • +
          +
        • Deprecated:
        • +
            +
          • PxSceneLimits::maxNbObjectsPerRegion has been deprecated. It is currently not used.
          • +
          • PxComputeHeightFieldPenetration has a new signature, and the old one has been deprecated
          • +
          • PxComputeMeshPenetration has been deprecated. Use PxComputeTriangleMeshPenetration.
          • +
          • The PhysX particle feature has been deprecated.
          • +
          • PxTolerancesScale::mass has been deprecated. It is currently not used.
          • +
          • PxActorClientBehaviorFlag has been marked as deprecated and will be removed in future releases.
          • +
          +
        • Removed deprecated API:
        • +
            +
          • PxPairFlag::eCCD_LINEAR removed. Use PxPairFlag::eDETECT_CCD_CONTACT | PxPairFlag::eSOLVE_CONTACT instead.
          • +
          • PxPairFlag::eRESOLVE_CONTACTS removed. Use PxPairFlag::eDETECT_DISCRETE_CONTACT | PxPairFlag::eSOLVE_CONTACT instead.
          • +
          • PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICE removed. Use PxTriangleMeshFlag::e16_BIT_INDICES instead.
          • +
          • PxTriangleMeshFlag::eHAS_ADJACENCY_INFO removed. Use PxTriangleMeshFlag::eADJACENCY_INFO instead.
          • +
          • PxTriangleMeshDesc::convexEdgeThreshold removed.
          • +
          • PxSceneQueryFlag renamed to PxHitFlag.
          • +
          • PxHitFlag::eDIRECT_SWEEP renamed to PxHitFlag::ePRECISE_SWEEP.
          • +
          • PxHitFlag::eIMPACT renamed to PxHitFlag::ePOSITION.
          • +
          • PxSceneQueryHitType renamed to PxQueryHitType.
          • +
          • PxSceneQueryCache renamed to PxQueryCache.
          • +
          • PxSceneQueryFilterFlag renamed to PxQueryFlag.
          • +
          • PxSceneQueryFilterFlags renamed to PxQueryFlags.
          • +
          • PxSceneQueryFilterData renamed to PxQueryFilterData.
          • +
          • PxSceneQueryFilterCallback renamed to PxQueryFilterCallback.
          • +
          • PxScene::raycastAll,PxScene::raycastSingle,PxScene::raycastAny replaced by PxScene::raycast.
          • +
          • PxScene::overlapAll,PxScene::overlapAny replaced by PxScene::overlap.
          • +
          • PxScene::sweepAll,PxScene::sweepSingle,PxScene::sweepAny replaced by PxScene::sweep.
          • +
          • PxQuat, PxTranform, PxMat33, PxMat44 createIdentity and createZero removed. Use PxIdentity, PxZero in constructor.
          • +
          • PxJointType::Enum, PxJoint::getType() removed. Use PxJointConcreteType instead.
          • +
          • PxVisualDebugger removed. Use PxPvd instead.
          • +
          • PxControllerFlag renamed to PxControllerCollisionFlag.
          • +
          • PxCCTHit renamed to PxControllerHit.
          • +
          • PxCCTNonWalkableMode renamed to PxControllerNonWalkableMode.
          • +
          • PxControllerNonWalkableMode::eFORCE_SLIDING changed to PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING.
          • +
          • PxControllerDesc::interactionMode, groupsBitmask, callback removed.
          • +
          • PxController::setInteraction, getInteraction, setGroupsBitmask, getGroupsBitmask removed.
          • +
          • PxControllerManager::createController no longer needs PxPhysics and PxScene.
          • +
          • PxControllerFilters::mActiveGroups replaced with PxControllerFilters::mCCTFilterCallback.
          • +
          • PxSerialization::createBinaryConverter(PxSerializationRegistry&) changed to PxSerialization::createBinaryConverter().
          • +
          • PxConstraintDominance renamed to PxDominanceGroupPair.
          • +
          • PxScene::flush renamed to PxScene::flushSimulation.
          • +
          • PxClothFabric::getPhaseType removed.
          • +
          • PxCollection::addRequired removed.
          • +
          • PxRigidActor::createShape discontinued support for initial transform.
          • +
          • PxShape::resetFiltering removed.
          • +
          • PxParticleBase::resetFiltering removed.
          • +
          • PxSceneDesc::meshContactMargin removed.
          • +
          • PxSceneDesc::contactCorrelationDistance removed.
          • +
          • Indexing operators taking signed integers in PxVec3, PxVec4, PxMat33, PxMat44, PxStrideIterator have been removed.
          • +
          • PxHitFlag::ePOSITION, PxHitFlag::eDISTANCE and PxHitFlag::eNORMAL are now supported in PxMeshQuery::sweep function.
          • +
          • PxClothFlag::eGPU renamed to PxClothFlag::eCUDA.
          • +
          • PxActorTypeSelectionFlag/PxActorTypeSelectionFlags. Use PxActorTypeFlag/PxActorTypeFlags instead.
          • +
          • PxConstraintFlag::eDEPRECATED_32_COMPATIBILITY flag removed.
          • +
          +
        + +

        PxShared

        +
          + APEX 1.4 can now be used independently of PhysX. In order to achieve that a new shared code base was created called "PxShared". PhysX functionality such as common types, PxFoundation, the task infrastructure are now part of PxShared. +
        + +

        Rigid Bodies

        +
          +
        • Added:
        • +
            +
          • An alternative simulation API has been introduced. This makes use of the following new functions: PsScene:collide(), PxScene::fetchCollision() and PxScene::advance(). Expected usage of these functions is illustrated in a new snippet SnippetSplitSim. This feature is also described in the manual in Section Simulation->SplitSim.
          • +
          • PxSceneFlag::eDEPRECATED_TRIGGER_TRIGGER_REPORTS has been introduced to re-enable the legacy behavior of trigger shape pairs sending reports. This flag and the corresponding legacy behavior will be removed in version 4.
          • +
          • The active actors feature has been added. See PxSceneFlag::eENABLE_ACTIVE_ACTORS.
          • +
          • Functionality to compute and manipulate mass, inertia tensor and center of mass of objects has been exposed in the new class PxMassProperties.
          • +
          • The option to exclude kinematics from the active actors/transforms list has been added. See PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS for details.
          • +
          • The integrated pose of dynamic rigid bodies can be accessed earlier in the pipeline through a new callback (see the API documentation for PxSimulationEventCallback::onAdvance() and PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW for details).
          • +
          • PxConvexMeshGeometryFlag::eTIGHT_BOUNDS has been added. See the user manual for details.
          • +
          • New split fetchResults methods introduced, see PxScene::fetchResultsBegin(), PxScene::fetchResultsFinish() and PxScene::processCallbacks(). This is intended to permit the application to parallelize the event notification callbacks.
          • +
          • New flag introduced to suppress updating scene query pruner trees inside fetchResults, see PxSceneFlag::eSUPPRESS_EAGER_SCENE_QUERY_REFIT. Instead, pruners will be updated during the next query.
          • +
          • Introduced GPU rigid body simulation support, see PxSceneFlag::eENABLE_GPU_DYNAMICS. GPU rigid body support requires SM3.0 or later.
          • +
          • Introduced a new GPU-accelerated broad phase. See PxBroadPhaseType::eGPU. GPU broad phase support requires SM3.0 or later.
          • +
          • Introduced a new enhanced determinism mode. See PxSceneFlag::eENABLE_ENHANCED_DETERMINISM. This provides additional levels of rigid body simulation determinism at the cost of some performance.
          • +
          • Introduced a new speculative contacts CCD approach. See PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD. This is a slightly cheaper, less robust solution to PxRigidBodyFlag::eENABLE_CCD. There is no need to turn CCD on the scene using PxSceneFlag::eENABLE_CCD or enable PxPairFlag::eDETECT_CCD_CONTACT with this CCD mode as it functions as an extension to the discrete time-stepping scheme. This form of CCD can be enabled on kinematic actors.
          • +
          • New "immediate mode" API has been added. This exposes access to the low-level contact generation and constraint solver, which allows the application to use these PhysX low-level components to perform its own simulations without needing to populate and simulate a PxScene.
          • +
          • RigidDynamic lock flags added which permit the application to disallow rotation/translation of PxRigidDynamics around specific axes.
          • +
          +
        • Changed:
        • +
            +
          • PxRigidStatic objects can now have zero shapes while being part of a scene.
          • +
          • PxContactPairFlag::eINTERNAL_HAS_FACE_INDICES is obsolete and has been removed.
          • +
          • PxConstraintFlag::eDEPRECATED_32_COMPATIBILITY was previously only implemented for spring constraints. It is now correctly implemented for equality constraints.
          • +
          • PxSceneFlag::eENABLE_PCM is enabled by default. This means PhysX uses PCM distance-based collision detection by default.
          • +
          • Calls to PxRigidDynamic::setWakeCounter() following PxScene::collide() do now explicitly get taken into account in the subsequent call to PxScene::advance().
          • +
          • Calls to contact modification callbacks can be made from multiple threads simultaneously. Therefore, modification callbacks should must be thread-safe.
          • +
          • Unified heightfield contact generation is now the default heightfield contact generation approach. This approach offers similar performance and behavior to contact generation with triangle meshes. Unified heightfields have no thickness because contact genreation operates on triangles so objects may tunnel if CCD is not enabled.
          • +
          • When unified heightfield contact generation is in use, the bounds of heightfield shapes are no longer extruded by "thickness".
          • +
          • PxArticulationJoint::setTwistLimit and PxArticulationJoint::getTwistLimit were incorrectly documented with zLimit and yLimit in the wrong order. The behavior of both functions remains unchanged but now they are correctly documented with zLimit and yLimit in the correct order. This is simply a clarification of the existing function behavior.
          • +
          +
        • Removed:
        • +
            +
          • The deprecated class PxFindOverlapTriangleMeshUtil has been removed. Please use PxMeshOverlapUtil instead.
          • +
          • The deprecated flag PxConstraintFlag::eREPORTING has been removed. Force reports are now always generated.
          • +
          • The following deprecated simulation event flags have been removed: PxContactPairHeaderFlag::eDELETED_ACTOR_0, ::eDELETED_ACTOR_1, PxContactPairFlag::eDELETED_SHAPE_0, ::eDELETED_SHAPE_1, PxTriggerPairFlag::eDELETED_SHAPE_TRIGGER, ::eDELETED_SHAPE_OTHER. Please use the following flags instead: PxContactPairHeaderFlag::eREMOVED_ACTOR_0, ::eREMOVED_ACTOR_1, PxContactPairFlag::eREMOVED_SHAPE_0, ::eREMOVED_SHAPE_1, PxTriggerPairFlag::eREMOVED_SHAPE_TRIGGER, ::REMOVED_SHAPE_OTHER.
          • +
          • The deprecated method PxPhysics::createHeightField(const PxHeightFieldDesc&) has been removed. Please use PxCooking::createHeightField(const PxHeightFieldDesc&, PxPhysicsInsertionCallback&) instead. The insertion callback can be obtained through PxPhysics::getPhysicsInsertionCallback().
          • +
          +
        • Deprecated:
        • +
            +
          • PxRigidActor::createShape() has been deprecated in favor of PxRigidActorExt::createExclusiveShape()
          • +
          • Trigger notification events for trigger-trigger pairs have been deprecated and will be omitted by default. See the 3.4 migration guide for more information.
          • +
          • The active transforms feature (PxSceneFlag::eENABLE_ACTIVETRANSFORMS) has been deprecated. Please use PxSceneFlag::eENABLE_ACTIVE_ACTORS instead.
          • +
          • PxRegisterHeightFields has been modified to register unified heightfields, which are now the default implementation. PxRegisterLegacyHeightFields() has been added to register the legacy (deprecated) heightfield contact gen approach.
          • +
          • PxHeightFieldDesc::thickness has been deprecated, as the new unified height field (see PxRegisterUnifiedHeightFields()) does not support thickness any longer.
          • +
          +
        • Fixed:
        • +
            +
          • The capsule-vs-heightfield contact generation had a rare bug where a vertical capsule standing exactly on a shared edge could fall through a mesh. This has been fixed.
          • +
          • The bounding box of a shape was not always properly updated when the contact offset changed.
          • +
          • Fixed a bug in the GJK sweep caused by lost precision in the math
          • +
          • Calls to PxScene::shiftOrigin() can crash when PxRigidDynamic actors with PxActorFlag::eDISABLE_SIMULATION are present.
          • +
          • Fixed a bug when PxShape::setMaterials was called with less materials than shape had before.
          • +
          • Fixed a bug in CCD that could lead to a hang in the simulation.
          • +
          • Fixed a bug in PCM mesh edge-edge check for the parallel case.
          • +
          • Fixed a bug in CCD where contact modify callbacks could be called when the CCD did not detect a contact.
          • +
          • Fixed a bug with applying external force/torque to a body in buffered insertion stage.
          • +
          • A rare capsule-vs-mesh contact generation bug has been fixed.
          • +
          • A rare crash due to an invalid assert in the MBP broad-phase has been fixed. This error only affected debug & checked builds; release & profile builds were unaffected.
          • +
          +
        + +

        Particles

        +
          +
        • Gpu: Maxwell Optimizations
        • +
        • The PhysX particle feature has been deprecated.
        • +
        • Fixed particle collision issue with PxParticleBaseFlag::ePER_PARTICLE_COLLISION_CACHE_HINT (on by default). When particles collided against very dense triangle mesh areas an assert would be triggered or particles would leak through the triangle mesh. A workaround was to disable PxParticleBaseFlag::ePER_PARTICLE_COLLISION_CACHE_HINT.
        • +
        + +

        Cloth

        +
          +
        • Continuous collision (PxClothFlag::eSWEPT_CONTACT) behavior has been optimized to reduce cloth sticking to collision shape.
        • +
        • Added air resistance feature (see PxCloth::setWindVelocity(PxVec3), PxCloth::setWindDrag(PxReal), PxCloth::setWindLift(PxReal), PxClothFabricDesc::nbTriangles, PxClothFabricDesc::triangles.
        • +
        + +

        Serialization

        +
          +
        • Fixed:
        • +
            +
          • PxTriangleMesh instances with adjacency information were not correctly initialized when created with cooking.createTriangleMesh. This caused a crash when converting the binary serialized triangle mesh data.
          • +
          +
        + +

        Character controller

        +
          +
        • Added:
        • +
            +
          • Profile zones have been added for the character controller.
          • +
          • Added PxControllerDesc::registerDeletionListener boolean defining if deletion listener for CCT should be registered.
          • +
          +
        • Fixed:
        • +
            +
          • Character controllers cannot stand on dynamic triggers anymore.
          • +
          • Fixed: the capsule-vs-sphere sweep now returns a normal in the correct direction.
          • +
          • Fixed a bug where CCT shapes initially overlapping static geometry would be moved down by an incorrect amount (the length of the step offset).
          • +
          • The overlap recovery module now works against kinematic objects.
          • +
          +
        + +

        Vehicles

        +
          +
        • Added:
        • +
            +
          • + Anti-roll suspension has been added. The class PxVehicleAntiRollBar and the functions PxVehicleWheelsSimData::setAntiRollBar, + PxVehicleWheelsSimData::getAntiRollBar, PxVehicleWheelsSimData::getNbAntiRollBars allow anti-roll bars to be configured and queried. +
          • +
          • + A new function PxVehicleSuspensionSweeps has been introduced. This sweeps the PxShape that represents the wheel along the suspension direction. The hit planes resulting + from the sweep are used as driving surfaces similar to those found by PxVehicleSuspensionRaycasts. +
          • +
          • + A new snippet SnippetVehicleContactMod has been added. This snippet demonstrates how to use sweeps and contact modification to allow the wheel's volume to fully interact + with the environment. +
          • +
          • A new function PxVehicleModifyWheelContacts has been introduced. This function analyses contact in the contact modification callback and rejects contact points that represent drivable surfaces.
          • +
          • A new function PxVehicleSetMaxHitActorAcceleration has been introduced. This function sets the maximum acceleration experienced by a PxRigidDynamic that finds itself under the wheel of a vehicle.
          • +
          +
        • Changed:
        • +
            +
          • + In checked build the functions PxVehicleDrive4W::allocate, PxVehicleDriveNW::allocate, PxVehicleDriveTank::allocate, PxVehicleNoDrive::allocate all return NULL and issue a warning if called before + PxInitVehicleSDK. +
          • +
          • Tire width is no longer accounted for when computing the suspension compression from raycasts (PxVehicleSuspensionRaycasts). Instead, tire width is incorporated into the suspension compression arising from swept wheels (PxVehicleSuspensionSweeps). It is recommended to use PxVehicleSuspensionSweeps if there is a strict requirement that the inside and outside of the wheel don't visibly penetrate geometry.
          • +
          +
        • Fixed:
        • +
            +
          • Suspension force calculation now applies an extra force perpendicular to the spring travel direction. This force is calculated to satisfy the constraint that the sprung mass only has motion along the spring travel direction. This change mostly affects vehicles with suspension travel directions that are not vertical.
          • +
          • PxVehicleWheelsSimData::mThresholdLongitudinalSpeed and PxVehicleWheelsSimData::mMinLongSlipDenominator are now given default values that reflect the length scale set in PxTolerancesScale.
          • +
          • Unphysically large constraint forces were generated to resolve the suspension compression beyond its limit when the suspension direction and the hit normal under the wheel approach perpendicularity. This has been fixed so that the constraint force approaches zero as the angle between the hit normal and suspension direction approaches a right angle.
          • +
          +
        + +

        Scene queries

        +
          +
        • Added:
        • +
            +
          • PxPruningStructure was introduced as an optimization structure to accelerate scene queries against large sets of newly added actors.
          • +
          • PxScene::addActors(PxPruningStructure& ) has been added.
          • +
          • PxMeshQuery::sweep now supports PxHitFlag::eMESH_ANY.
          • +
          • PxHitFlag::eFACE_INDEX was introduced to reduce the perf cost for convex hull face index computation. In order to receive face index for sweeps against a convex hull, the flag PxHitFlag::eFACE_INDEX has to be set. Note that the face index can also be computed externally using the newly introduced method PxFindFaceIndex from the extensions library.
          • +
          • PxGeometryQuery::isValid was added to check provided geometry validity.
          • +
          +
        • Changed:
        • +
            +
          • Raycasts against triangle meshes with PxHitFlag::eMESH_MULTIPLE flag now return all hits, code for discarding hits close to each other has been removed.
          • +
          • PxPruningStructure enum has been renamed to PxPruningStructureType
          • +
          +
        • Deprecated:
        • +
            +
          • PxHitFlag::eDISTANCE has been deprecated.
          • +
          • The batched query feature has been deprecated.
          • +
          • Volume cache feature has been deprecated.
          • +
          • Spatial index feature has been deprecated.
          • +
          +
        • Fixed:
        • +
            +
          • PxScene::sweep now properly implements PxHitFlag::eMESH_BOTH_SIDES (returned normal follows the same convention as for raycasts).
          • +
          • Raycasts against heightfields now correctly return multiple hits when PxHitFlag::eMESH_MULTIPLE flag is used.
          • +
          • PxSweepHit.faceIndex was computed incorrectly for sweep tests initially overlapping convex objects. The face index is now set to 0xffffffff in these cases.
          • +
          • Convex vs convex sweeps in PxGeometryQuery::sweep() do now correctly return the face index of the convex mesh that gets passed in as parameter geom1 (and not the one from geom0).
          • +
          • PxMeshQuery::sweep now supports PxHitFlag::eMESH_ANY.
          • +
          • Deprecated definition PxSceneQueryHit has been removed. Please use PxQueryHit instead.
          • +
          • PxGeometryQuery::computePenetration with convex geometries.
          • +
          • On Android platforms, the eDYNAMIC_AABB_TREE pruning structure could pass already released objects into the scene query filter callback.
          • +
          +
        + + +

        Cooking

        +
          +
        • Added:
        • +
            +
          • PxTriangleMeshCookingResult added, cookTriangleMesh now does return additional PxTriangleMeshCookingResult. Please see the manual for more information.
          • +
          • New convex hull generator added. It is now possible to switch between a new quickhull implementation and the legacy inflation based hull. Quickhull is the default algorithm.
          • +
          • Convex hulls can now be directly inserted in PxPhysics as triangle meshes and height fields.
          • +
          • A separate convex hull validation function has been added, it is now possible to create hulls without validation.
          • +
          • Convex hull generator vertex limit has two different algorithms - plane shifting and OBB slicing.
          • +
          • PxConvexFlag::eFAST_INERTIA_COMPUTATION added. When enabled, the inertia tensor is computed faster but with less precision.
          • +
          • PxConvexFlag::eGPU_COMPATIBLE added. When enabled convex hulls are created with vertex limit set to 64 and vertex limit per face is 32.
          • +
          • PxConvexFlag::eSHIFT_VERTICES added. When enabled input points are shifted to be around origin to improve computation stability.
          • +
          • PxCookingParams::gaussMapLimit has been added. The limit can now be fully user-defined. Please refer to the migration guide and best practices sections of the manual.
          • +
          +
        • Changed:
        • +
            +
          • The performance of convex creation from polygons has been improved.
          • +
          +
        • Deprecated:
        • +
            +
          • The PxPlatform enum and the PxGetGaussMapVertexLimitForPlatform function have been deprecated.
          • +
          +
        • Removed:
        • +
            +
          • The deprecated flags PxMeshPreprocessingFlag::eREMOVE_UNREFERENCED_VERTICES and ::eREMOVE_DUPLICATED_TRIANGLES have been removed. Meshes get cleaned up by default unless PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH is set.
          • +
          +
        • Fixed:
        • +
            +
          • Mesh cooking was sometimes crashing for meshes with less than 4 triangles. This has been fixed.
          • +
          • Cooking convex mesh from a flat input mesh produced incorrect large mesh.
          • +
          +
        + +

        Extensions

        +
          +
        • Added:
        • +
            +
          • PxRaycastCCD has been added, to improve the visibility of the raycast-based CCD solution, which was previously only available in the Sample framework. This is a simpler and potentially cheaper alternative to the SDK's built-in continuous collision detection algorithm.
          • +
          • PxFindFaceIndex has been added. The function computes the closest polygon on a convex mesh from a given hit point and direction.
          • +
          +
        • Changed:
        • +
            +
          • Memory churn of PxDefaultMemoryOutputStream has been reduced.
          • +
          • The signatures for the PxComputeMeshPenetration and PxComputeHeightFieldPenetration functions have changed.
          • +
          +
        + +

        Profiling

        +
          +
        • Changed:
        • +
            +
          • Profiling information is now available only in debug, checked and profile configuration.
          • +
          • PxProfileZoneManager::createProfileZoneManager now takes PxAllocatorCallback as input parameter instead of PxFoundation.
          • +
          +
        + +

        Physx Visual Debugger

        +
          +
        • PhysXVisualDebuggerSDK, PvdRuntime projects replaced with PxPvdSDK.
        • +
        • PxPvdSceneClient::drawPoints now takes physx::pvdsdk::PvdDebugPoint as input parameter instead of PxDebugPoint. drawLines, drawTriangles, drawText and so on.
        • +
        • The SDK's Debug visualization data is not sent to PVD anymore in ePROFILE mode.
        • +
        • PxPvdSceneFlag::eTRANSMIT_CONTACTS (instead of PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS) was sometimes incorrectly used to control the transmission of constraint-related data. This has been fixed. In addition, the PxPvdSceneFlag flags are now consistently ignored when PxPvdInstrumentationFlag::eDEBUG is not set.
        • +
        + +

        Aggregates

        +
          + +

          Snippets

          +
            +
          • Snippet profile zone has been removed.
          • +
          +
          + + +
          + + +

          Release Notes - NVIDIA® PhysX® SDK 3.3.4

          +

          October 2015

          + +

          Supported Platforms

          +
          +

          Runtime

          +
            +
          • Apple iOS
          • +
          • Apple Mac OS X
          • +
          • Google Android ARM & x86 (version 2.2 or later required for SDK, 2.3 or later required for samples)
          • +
          • Linux (tested on Ubuntu)
          • +
          • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
          • +
          • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
          • +
          • Microsoft XBox One (SDK only, no samples)
          • +
          • Microsoft XBox 360
          • +
          • Nintendo Wii U
          • +
          • Sony Playstation 3
          • +
          • Sony Playstation 4 (SDK only, no samples)
          • +
          • Sony Playstation Vita
          • +
          +

          Development

          +
            +
          • Microsoft Windows XP or later
          • +
          • Microsoft Visual Studio 2012, 2013 and 2015
          • +
          • Xcode 6.3
          • +
          +
          +

          Changes and Resolved Issues

          +
          + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

          General

          +
            +
          • Added support for Microsoft Visual Studio 2015 for Windows builds. Note that the GPU features are not currently supported with Visual Studio 2015.
          • +
          • Removed support for Microsoft Visual Studio 2010
          • +
          • Added support for Android platform API Level 16 and removed support for platform API Level 8 and 9.
          • +
          • Fixed:
          • +
              +
            • Fixed a bug in aggregates that led to a crash when rigid bodies are added to an aggregate after removing all rigid bodies from an aggregate. This only occurred with aggregates that were added to the scene and with rigid bodies that had shapes attached.
            • +
            +
          + +

          Rigid Bodies

          +
            +
          • Fixed:
          • +
              +
            • Creating a PxConstraint or using PxConstraint::setActors() could cause a crash in situations where one of the two newly connected actors was part of a previously simulated graph of connected constraints (with some of them having projection enabled, i.e., PxConstraintFlag::ePROJECT_TO_ACTOR0 or ::ePROJECT_TO_ACTOR1 set).
            • +
            • PxD6Joint::getTwist(), getSwingY(), getSwingZ() returned incorrect angle values when the corresponding components of the quaternion were negative
            • +
            • The thickness of a heightfield was incorrectly applied when the heightfield transform had a non-identity quaternion.
            • +
            • PxD6Joint angular projection now functions correctly when there is one free axis and it is not the twist axis.
            • +
            • The bounding box of a shape was not always properly updated when the contact offset changed.
            • +
            • Fixed an edge case bug in PCM contact gen that could result in a QNAN reported as the contact point.
            • +
            • Fixed an uninitialized variable bug in the GJK algorithm resulting in unintialized closest points reported.
            • +
            • Fixed an edge case in which the constraint solver could access invalid memory in constraint partitioning.
            • +
            • Fixed a bug in capsule vs heightfield contact generation that could produce incorrect penetration depths.
            • +
            • Fixed a bug in Unified MTD code path which transformed the normal twice for the polygon index calculation.
            • +
            • Fixed a crash in height fields when a capsule collided with an edge whose shared triangles had a hole material.
            • +
            +
          + +

          Serialization

          +
            +
          • Fixed:
          • +
              +
            • PxTriangleMesh instances with adjacency information were not correctly initialized when created with cooking.createTriangleMesh. This caused a crash when converting the binary serialized triangle mesh data.
            • +
            +
          + +

          Scene Queries

          +
            +
          • Fixed:
          • +
              +
            • Sweeps against scaled meshes.
            • +
            +
          + +

          Cooking

          +
            +
          • Fixed:
          • +
              +
            • Mesh cooking was sometimes crashing for meshes with less than 4 triangles. This has been fixed.
            • +
            +
          + +
          + + +
          + + +

          Release Notes - NVIDIA® PhysX® SDK 3.3.3

          +

          January 2015

          + +

          Supported Platforms

          +
          +

          Runtime

          +
            +
          • Apple iOS
          • +
          • Apple Mac OS X
          • +
          • Google Android ARM & x86 (version 2.2 or later required for SDK, 2.3 or later required for samples)
          • +
          • Linux (tested on Ubuntu)
          • +
          • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
          • +
          • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
          • +
          • Microsoft XBox One (SDK only, no samples)
          • +
          • Microsoft XBox 360
          • +
          • Nintendo Wii U
          • +
          • Sony Playstation 3
          • +
          • Sony Playstation 4 (SDK only, no samples)
          • +
          • Sony Playstation Vita
          • +
          +

          Development

          +
            +
          • Microsoft Windows XP or later
          • +
          • Microsoft Visual Studio 2010, 2012 and 2013
          • +
          • Xcode 6.2
          • +
          +
          +

          Known Issues

          +
          +
            +
          • The combination of releasing an actor and reassigning the actors of any affected joint so that the joint no longer references the released actor will lead to a crash if these operations are performed as buffered calls ie after simulate but before fetchResults.
          • +
          +
          +

          Changes and Resolved Issues

          +
          + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

          General

          +
            +
          • Added support for Microsoft Visual Studio 2013 and removed support for Microsoft Visual Studio 2008.
          • +
          • Where applicable, mutexes on Unix flavored platforms now inherit priority to avoid priority inversion. This is a default behavior of Windows mutexes.
          • +
          • Added arm64 support for the iOS version of the PhysX SDK.
          • +
          • Removed samples from the iOS version of the PhysX SDK.
          • +
          • Fixed:
          • +
              +
            • x64 samples running on Windows 8.
            • +
            • Concurrent calls to PxPhysics::(un)registerDeletionListener() and PxPhysics::(un)registerDeletionListenerObjects() were not safe.
            • +
            • PxGeometryQuery::pointDistance() could sometimes read uninitialized memory. This has been fixed.
            • +
            • The SDK will not crash anymore if an object is involved in more than 65535 interactions. Instead, it will emit an error message and the additional interactions will be ignored.
            • +
            • The PxPhysics::getMaterials() function did not work with a non-zero 'startIndex' parameter. This has been fixed.
            • +
            • The following static Android libs are now packed into a single library called PhysX3: LowLevel, LowLevelCloth, PhysX3, PhysXProfileSDK, PhysXVisualDebuggerSDK, PvdRuntime, PxTask, SceneQuery and SimulationController. This fixes cyclic dependencies between these libraries.
            • +
            • FSqrt(0), V4Sqrt(0), V4Length(0) and V3Length(0) will return 0 instead of QNan in Android and iOS.
            • +
            +
          + +

          Rigid Bodies

          +
            +
          • Fixed:
          • +
              +
            • The Prismatic joint limit now acts correctly when its frame is not the identity.
            • +
            • Calling PxRigidDynamic::setGlobalPose() with the autowake parameter set to true could result in an assert when the rigid body got released and had the PxActorFlag::eDISABLE_SIMULATION flag set.
            • +
            • Added errors on misuse of PxRegister[Unified]Heightfields() function, and documented it.
            • +
            • PxConstraint has a eDISABLE_PREPROCESSING flag and minResponseThreshold attribute to assist in stabilizing stiffness caused by infinite inertias or mass modification.
            • +
            • Island manager performance in the presence of large numbers of kinematic actors has been improved.
            • +
            • Using PxConstraint::setActors() or PxJoint::setActors() could cause a crash if the new actors resulted in the constraint/joint being removed from the scene.
            • +
            • The functions PxRigidActor::setGlobalPose and PxShape::setLocalPose failed to update cached contact data internal to PhysX. This led to contacts being generated with transforms that were no longer valid. Similarly, contacts could be missed due to transforms being invalid. This defect affected the classes PxRigidStatic and PxRigidDynamic, though it was more immediately noticeable with the PxRigidStatic class.
            • +
            • The sphere-vs-mesh contact generation code has been improved. It could previously generate wrong contacts. This has been fixed.
            • +
            • The capsule-vs-convex contact generation had a bug that could lead to rare invalid contacts. This has been fixed.
            • +
            • The mesh contact generation had a bug on PS3 that could lead to invalid contacts. This has been fixed.
            • +
            • The PxRigidBody::clearForce() and PxRigidBody::clearTorque() were not properly decoupled - they both cleared the force and the torque. This has been fixed.
            • +
            • Repeatedly calling PxRigidActor::attachShape and PxRigidActor::detachShape in between calls to simulate resulted in a memory leak. This has been fixed.
            • +
            +
          • Added:
          • +
              +
            • Enabling CCD on kinematic actors is now disallowed. When the kinematic flags are raised on a CCD-enabled actor, CCD is automatically disabled.
            • +
            +
          + +

          Particles

          +
            +
          • Fixed:
          • +
              +
            • + Consistency between GPU and CPU particles has been improved in the case of a spatial date structure overflow. The positions and velocities of particles that have the PxParticleFlag::eSPATIAL_DATA_STRUCTURE_OVERFLOW set + are now updated also for CPU particles. +
            • +
            • Fixed potential deadlocks from occurring in the GPU particle kernels running on GM204 and above GPUs.
            • +
            • Fixed fluid simulation crash on Titan X.
            • +
            +
          + +

          Cloth

          +
            +
          • Fixed:
          • +
              +
            • A bug related to hitting the sphere or plane limit while world collision is enabled has been fixed.
            • +
            • PxCloth::getParticleAccelerations() implementation was fixed for GPU cloth.
            • +
            +
          + +

          Character controller

          +
            +
          • Fixed:
          • +
              +
            • Character controllers cannot stand on dynamic triggers anymore.
            • +
            +
          • Added:
          • +
              +
            • added lockingEnabled parameter to PxCreateControllerManager(), to support thread-safe release of objects while the character controller's move() routine is executing.
            • +
            +
          + +

          Scene Queries

          +
            +
          • Fixed:
          • +
              +
            • Raycasts against triangle meshes with large scales potentionaly failed to register a hit.
            • +
            • Overlaps against height field meshes with the flag eNO_BOUNDARY_EDGES potentionaly failed to register a hit.
            • +
            • Sweeps using shapes modelled around a space significantly offset from their geometric center could fail to register a hit.
            • +
            +
          + +

          Vehicles

          +
            +
          • Fixed:
          • +
              +
            • Sticky tire friction was unreliable with more than one substep but is now fixed. This defect led to vehicles sliding down slopes where the sticky friction should have held firm.
            • +
            • An error in the jounce speed calculation that led to lift force at high forward speeds has been fixed. This defect led to instabilities at high speed.
            • +
            • Improved documentation for PxVehicleSuspsensionData::mSprungMass.
            • +
            +
          + +

          Cooking

          +
            +
          • Fixed:
          • +
              +
            • Convex meshes generated from PhysX 3.2 were not able to load inside PhysX 3.3.
            • +
            +
          + +
          + + +
          + + +

          Release Notes - NVIDIA® PhysX® SDK 3.3.2

          +

          September 2014

          + +

          Supported Platforms

          +
          +

          Runtime

          +
            +
          • Apple iOS
          • +
          • Apple Mac OS X
          • +
          • Google Android ARM & x86 (version 2.2 or later required for SDK, 2.3 or later required for samples)
          • +
          • Linux (tested on Ubuntu)
          • +
          • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
          • +
          • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
          • +
          • Microsoft XBox One (SDK only, no samples)
          • +
          • Microsoft XBox 360
          • +
          • Nintendo Wii U
          • +
          • Sony Playstation 3
          • +
          • Sony Playstation 4 (SDK only, no samples)
          • +
          • Sony Playstation Vita
          • +
          +

          Development

          +
            +
          • Microsoft Windows XP or later
          • +
          • Microsoft Visual Studio 2008, 2010, 2012 (Windows RT only)
          • +
          • Xcode 4.6|5.0|5.0.1
          • +
          +
          +

          Known Issues

          +
          +
            +
          • The combination of releasing an actor and reassigning the actors of any affected joint so that the joint no longer references the released actor will lead to a crash if these operations are performed as buffered calls ie after simulate but before fetchResults.
          • +
          +
          +

          Changes and Resolved Issues

          +
          + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

          General

          +
            +
          • Added:
          • +
              +
            • The PhysXCommon/64.dll, nvcuda.dll and PhysXUpdateLoader/64.dll are loaded and checked for the NVIDIA Corporation digital signature. The signature is expected on all NVIDIA Corporation provided dlls. The application will exit if the signature check fails.
            • +
            • Added the PxDefaultBufferedProfiler extension for simplified SDK profile events extraction.
            • +
            • PxSceneDesc::sanityBounds allows a bounding box to be set for validating the position coordinates of inserted or updated rigid actors and articulations.
            • +
            • Linux: Now supports GPU PhysX.
            • +
            • Added set/getRunProfiled() for PxDefaultCpuDispatcher to control profiling at task level.
            • +
            • Android: Support for x86 based devices was added.
            • +
            • PxProfileEventHandler::durationToNanoseconds() added. Translates event duration in timestamp (cycles) into nanoseconds.
            • +
            • Added SnippetProfileZone to show how to retrieve profiling information.
            • +
            • Added SnippetCustomJoint to better illustrate custom joint implementation, and removed SnippetExtension.
            • +
            • Added SnippetStepper to demonstrate kinematic updates while substepping with tasks.
            • +
            +
          • Fixed:
          • +
              +
            • PxTask::runProfiled() now takes threadId as a parameter.
            • +
            • The static pruner now issues a performance warning in debug and checked configurations when a tree rebuild occurs and the tree is not empty.
            • +
            • PxSceneDesc::staticStructure now defaults to PxPruningStructure::eDYNAMIC_AABB_TREE.
            • +
            • Linux: Switched to shared libraries.
            • +
            • Profile zone event names changed to match function calls.
            • +
            • Overlapping read/write errors will now issue a PxErrorCode::eINVALID_OPERATION rather than PxErrorCode::eDEBUG_INFO.
            • +
            • Improved SnippetToleranceScale to better demonstrate the intended use case.
            • +
            • Increased 126 characters limit for warnings on unix platforms, 1k limit on all platforms.
            • +
            • PhysXCommon dll load within PhysX dll now respects dll name. Please see the manual's PhysXCommon DLL load section.
            • +
            • Significant revision of the user's guide. Both structure and most content have been modified.
            • +
            • Fixed search function of user's guide.
            • +
            • Foundation math classes now have in-place arithmetic operators (+= etc).
            • +
            • PxScene::checkResults() no longer counts as a read API method. Hence it is now possible to call this method in blocking mode without causing all writes to block until it returns.
            • +
            +
          • Deprecated:
          • +
              +
            • Indexing operators taking signed integers in PxVec3, PxVec4, PxMat33, PxMat44, PxStrideIterator have been deprecated.
            • +
            +
          + +

          Rigid Bodies

          +
            +
          • Fixed:
          • +
              +
            • A minor bug in contact generation between capsules and triangle meshes has been fixed, reducing the amount of tunneling cases when CCD is not used.
            • +
            • Discrete contact reports are no longer produced for pairs without PxPairFlag::eDETECT_DISCRETE_CONTACT raised in the filter shader. Previously, discrete contact generation would always have been performed regardless of the presence of the PxPairFlag::eDETECT_DISCRETE_CONTACT flag. This change potentially improves performance when using specific shapes for CCD-only collision, which would have previously generated discrtete contacts and then ignored them in the solver.
            • +
            • Trigger reports are no longer produced for pairs without PxPairFlag::eDETECT_DISCRETE_CONTACT raised in the filter shader. PxPairFlag::eTRIGGER_DEFAULT has been modified to include the PxPairFlag::eDETECT_DISCRETE_CONTACT flag.
            • +
            • An incorrect PX_DEPRECATED annotation on the default constructor for PxD6JointDrive has been removed.
            • +
            • PxRigidDynamic::getKinematicTarget() returned a wrong transform if the actor center of mass pose was different from the actor global pose.
            • +
            • Switching a PxRigidDynamic from dynamic to kinematic did not properly suppress existing pairs which turned into kinematic-kinematic or kinematic-static pairs.
            • +
            • PxRigidDynamic::isSleeping() did not return the correct value on the frame the object got inserted if PxScene::addActors() was used and if the object was awake.
            • +
            • PxSceneFlag::eDISABLE_CONTACT_CACHE now correctly works on PS3/SPU.
            • +
            • If an object was added to the scene, put asleep and had overlap with another sleeping object then contact points for that pair might not have been reported once the object woke up.
            • +
            • Potential crash when calling PxScene::resetFiltering() mulitple times for the same actor while the simulation was running has been fixed.
            • +
            • Potential crash when using PxScene::resetFiltering() with shapes that were just added while the simulation was running has been fixed.
            • +
            • A crash in MBP when deleting an object that just went out of broad-phase bounds has been fixed.
            • +
            • A new drive mode has been added to drive articulation joints using rotation vectors.
            • +
            • In contact and trigger reports, the shape references in PxTriggerPair and PxContactPair might not have been properly marked as removed shapes if the removal took place while the simulation was running.
            • +
            • PxPairFlag::eSOLVE_CONTACT is now properly observed if the flag is not set on a contacting pair. A consequence of this fix is that sleeping actors will no longer be woken up due to contact or lost contact with awake actors if PxPairFlag::eSOLVE_CONTACT is not set for the pair. This also affects kinematic-kinematic pairs if one kinematic of the pair moves out of contact with the other. Behaviour is unaffected for any pair that has PxPairFlag::eSOLVE_CONTACT set.
            • +
            • A memory leak with buffered shape and actor removal has been fixed. The memory leak occurred when the release of an actor's shapes was followed by the release of the actor, all in-between simulate() and fetchResults().
            • +
            • A bug was fixed which caused incorrect force reports to sometimes be reported.
            • +
            • Fixed a bug where incorrect normals were reported when using PCM contact gen.
            • +
            • Fixed some issues with scaled convex hulls in the PCM contact gen code path.
            • +
            • The accuracy of capsule collision code has been improved.
            • +
            • An isValid() method has been added to constraints, that is satisfied if and only if at least one actor is a dynamic body or articulation link
            • +
            • A check has been added to prevent constraint construction or modification that would leave the constraint invalid.
            • +
            • In checked builds the PxScene methods addActor(), addActors(), addAggregate() and addCollection() will warn and return if an invalid constraint would be added to the scene
            • +
            +
          • Deprecated:
          • +
              +
            • The following flags have been renamed and deprecated because the name did not properly reflect the root cause.
            • +
                +
              • PxContactPairHeaderFlag
              • +
                  +
                • eDELETED_ACTOR_0 (use eREMOVED_ACTOR_0 instead)
                • +
                • eDELETED_ACTOR_1 (use eREMOVED_ACTOR_1 instead)
                • +
                +
              • PxContactPairFlag
              • +
                  +
                • eDELETED_SHAPE_0 (use eREMOVED_SHAPE_0 instead)
                • +
                • eDELETED_SHAPE_1 (use eREMOVED_SHAPE_1 instead)
                • +
                +
              • PxTriggerPairFlag
              • +
                  +
                • eDELETED_SHAPE_TRIGGER (use eREMOVED_SHAPE_TRIGGER instead)
                • +
                • eDELETED_SHAPE_OTHER (use eREMOVED_SHAPE_OTHER instead)
                • +
                +
              +
            +
          + +

          Vehicles

          +
            +
          • Added:
          • +
              +
            • In profile config the functions PxVehicleUpdates, PxVehiclePostUpdates and PxVehicleSuspensionRaycasts are now tracked with profile events (provided that a PxProfileZoneManager instance was passed to PxCreatePhysics). These profile events can be viewed in profile view in pvd, where the names of the profile events match the names of the tracked vehicle functions.
            • +
            +
          • Fixed:
          • +
              +
            • In checked config PxVehicleDriveTank::allocate enforces the rule that only tanks with even numbers of wheels are legal and warns when this rule is broken.
            • +
            • In checked config PxVehicleDrive4W::allocate enforces the rule that only vehicles with 4 wheels or more are legal and warns when this rule is broken.
            • +
            • PxWheelQueryResult::localPose was incorrectly only set when the vehicle had a corresponding PxShape, as described by PxVehicleWheelsSimData::setWheelShapeMapping. The localPose is now properly set independent of the mapping between wheel and shape.
            • +
            • Wheels resting on moving actors now properly observe the relative speed of the two actors when their relative speed is small. This fixes a bug where at small relative speeds the velocity of the other actor was assumed to be zero.
            • +
            • Repx serialization failed to serialize PxVehicleWheelsSimData::setMinLongSlipDenominator, PxVehicleWheelsSimData::setSubStepCount, PxVehicleWheelsSimData::disableWheel, PxVehicleWheelsSimData::enableWheel and the number of entries in the engine torque curve. These have now been fixed.
            • +
            • PxVehicleConcreteType used for vehicle serialization is now in the public API and has been moved to PxVehicleSDK.h.
            • +
            • Very small longitudinal and lateral slip angles could lead to numerical instabilities in some circumstances. A threshold has been introduced to reject very small slip angles by setting them to zero when they are below the threshold.
            • +
            • Vehicles now account for rigid bodies that have been given a zero inertia component in order to lock rotation around the corresponding axis.
            • +
            • Fixed a bug where the sticky wheel constraints sometimes didn't function correctly.
            • +
            +
          + +

          Cloth

          +
            +
          • Fixed:
          • +
              +
            • The version number written to the fabric stream changed from PX_PHYSICS_VERSION to 1. A fabric can be created from streams written with version 3.3.0 and later until the stream format changes. Previously, the version of the producer and the consumer of the stream needed to match.
            • +
            • GPU cloth friction against convexes has been fixed.
            • +
            • A crash resulting from deleting a shape in proximity of a cloth with scene collision enabled has been fixed.
            • +
            +
          + +

          Scene Queries

          +
            +
          • Fixed:
          • +
              +
            • PxMeshQuery::sweep now respects PxHitFlag::eMESH_BOTH_SIDES, and supports double-sided input triangles.
            • +
            • PxRigidBodyExt::linearSweepSingle and PxRigidBodyExt::linearSweepMultiple now correctly use query filter data instead of simulation filter data if filter data is not provided.
            • +
            • The spec for raycasts whose origin is located inside a solid object (sphere, box, capsule, convex) has changed back to what it was in 3.3.0. It was accidentally changed in 3.3.1. See the manual for details.
            • +
            • Convex sweeps against heightfields worked only when the heightfield had the identity transform. This has now been fixed to support arbitrary transforms again.
            • +
            +
          + +

          Cooking

          +
            +
          • Added:
          • +
              +
            • Using PxMeshPreprocessingFlag::eFORCE_32BIT_INDICES will always cook meshes with 32-bit triangle indices.
            • +
            +
          • Fixed:
          • +
              +
            • Convex hull cooking fix. Some input points could be ignored during cooking, fixed.
            • +
            • Inserted triangle meshes now respect 16 bit indices flag.
            • + + +
            +
          + +

          Geometry

          +
            +
          • Fixed:
          • +
              +
            • PxHeightFieldDesc::thickness is now limited to [-PX_MAX_BOUNDS_EXTENTS, PX_MAX_BOUNDS_EXTENTS range]. (Previously unbounded).
            • +
            +
          + +

          Particles

          +
            +
          • Fixed:
          • +
              +
            • Setting PxParticleReadDataFlag::eREST_OFFSET_BUFFER on a PxParticleBase instance that was not created with the per particle rest offset option (see PxPhysics::createParticleSystem, PxPhysics::createParticleFluid and PxParticleBaseFlag::ePER_PARTICLE_REST_OFFSET) is not supported. The unsupported configuration may have resulted in crashes. The SDK now rejects this configuration on calling PxParticleBase::setParticleBaseFlag and issues an appropriate warning to the error stream.
            • +
            • Performance improvements on Kepler and above GPUs running SPH.
            • +
            • In rare cases particle systems could access released memory when all interactions with a rigid body shape were lost.
            • +
            +
          + +

          Serialization

          +
            +
          • Fixed:
          • +
              +
            • PxBinaryConverter::convert potentially failed in checked mode with allocators that don't set 0xcd pattern. This has been fixed now.
            • +
            +
          + +
          + + + +
          + + +

          Release Notes - NVIDIA® PhysX® SDK 3.3.1

          +

          December 2013

          + +

          Supported Platforms

          +
          +

          Runtime

          +
            +
          • Apple iOS
          • +
          • Apple Mac OS X
          • +
          • Google Android (version 2.2 or later for SDK, 2.3 or later required for samples)
          • +
          • Linux (tested on Ubuntu)
          • +
          • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
          • +
          • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
          • +
          • Microsoft XBox One
          • +
          • Microsoft XBox 360
          • +
          • Nintendo Wii U
          • +
          • Sony Playstation 3
          • +
          • Sony Playstation 4 (SDK only, no samples)
          • +
          • Sony Playstation Vita
          • +
          +

          Development

          +
            +
          • Microsoft Windows XP or later
          • +
          • Microsoft Visual Studio 2008, 2010, 2012 (Windows RT only)
          • +
          • Xcode 4.6|5.0|5.0.1
          • +
          +
          +

          Known Issues

          +
          +
            +
            +

            Changes and Resolved Issues

            +
            + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

            General

            +
              +
            • Added:
            • +
                +
              • The friction model can now be changed after scene instantiation with PxScene::setFrictionType. The friction model can also be queried with PxScene::getFrictionType.
              • +
              +
            • Changed:
            • +
                +
              • PxDefaultSimulationFilterShader now supports particles and cloth as well.
              • +
              • PxSimulationFilterCallback: the provided actor and shape pointers are now defined as const. Note: this is no behavior change, it was never allowed to write to those objects from within the callback.
              • +
              • The PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICES and PxTriangleMeshFlag::eHAS_ADJACENCY_INFO enums have been deprecated. Please use PxTriangleMeshFlag::e16_BIT_INDICES and PxTriangleMeshFlag::eADJACENCY_INFO instead.
              • +
              • Removed following functions from the API for platforms which do not support CUDA: PxGetSuggestedCudaDeviceOrdinal, PxCreateCudaContextManager, PxLoadPhysxGPUModule.
              • +
              +
            • Fixed:
            • +
                +
              • Fixed concurrency issue on windows. Calling PxScene::simulate on multiple scenes concurrently may have caused a deadlock. This only happened if the scenes shared a single PxCpuDispatcher and the dispatcher was configured to use one worker thread only.
              • +
              +
            + +

            Rigid Bodies

            +
              +
            • Added:
            • +
                +
              • The projection direction for constraints can now be specified through the flags PxConstraintFlag::ePROJECT_TO_ACTOR0, ::ePROJECT_TO_ACTOR1.
              • +
              • A parameter has been added to PxRigidBodyExt::updateMassAndInertia() and ::setMassAndUpdateInertia() to optionally take non-simulation shapes into account for computing the mass and the inertia tensor of a rigid body.
              • +
              • It is now possible to retrieve additional information in contact reports. See the API documentation of PxContactPairHeader.extraDataStream, PxPairFlag::ePRE_SOLVER_VELOCITY, ::ePOST_SOLVER_VELOCITY, ::eCONTACT_EVENT_POSE for details.
              • +
              • The contact report system has been extended to support multiple notification events if the same two objects collide multiple times in multipass CCD scenarios. See the API documentation of PxPairFlag::eNOTIFY_TOUCH_CCD for details.
              • +
              + +
            • Changed:
            • +
                +
              • If touching objects were added to the scene asleep and one of them got woken up, then all contact pairs of the touching objects which contained a static rigid body were resolved with a delay of one simulation step. Now these pairs get resolved without delay in the next simulation step.
              • +
              • If touching objects were added to the scene asleep, eNOTIFY_TOUCH_FOUND contact reports were sent out for pairs of dynamic rigid bodies if requested. These reports will not be sent at the end of the simulation step after insertion anymore but rather at the end of the simulation step after the touching objects have been woken up.
              • +
              • Rigid bodies now permit zeroes in passed to setMass and setMassSpaceInertiaTensor. Zeroes are interpreted to indicate infinite mass or infinite moment of inertia around a given principal axis of inertia. Previously, zeroes were illegal values to these methods. Note that zeroes are still illegal for instances of PxArticulationLink.
              • +
              + +
            • Fixed:
            • +
                +
              • Reading back the kinematic target in the PxSimulationEventCallback::onContact() callback through PxRigidDynamic::getKinematicTarget() will now work.
              • +
              • Contact reports are no longer generated for contact pairs involving two sleeping kinematic actors or for pairs involving a sleeping kinematic actor in contact with a static actor. This fixes a bug that was introduced in 3.3.0.
              • +
              • No PxPairFlag::eNOTIFY_TOUCH_LOST event was sent in contact reports if a pair of sleeping rigid bodies got woken up after setting the pose on one of them (with autowake parameter set to false) and if the bounding boxes of the two objects still overlapped.
              • +
              • No PxPairFlag::eNOTIFY_TOUCH_PERSISTS event was sent in contact reports during the first simulation step after a pair of sleeping rigid bodies got woken up.
              • +
              • The inertia tensor computation for convex meshes has been adjusted to be more stable in certain cases where floating point precision issues arise. Furthermore, a fallback routine has been added to use an approximation if the diagonalized inertia tensor still ends up with invalid entries.
              • +
              • PxRigidBody::clearForce() and ::clearTorque() did not properly clear the respective properties if used with force mode PxForceMode::eIMPULES or PxForceMode::eVELOCITY_CHANGE.
              • +
              • Setting PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS also enabled PxSceneFlag::eENABLE_KINEMATIC_PAIRS internally and vice versa.
              • +
              • Missing validation checks for some joint set() methods have been added. Similarly to other API calls, when validation fails in the checked build PhysX will report an error and return without updating the joint.
              • +
              • Switching a kinematic rigid body to dynamic could lead to a crash in a subsequent simulation step, if the kinematic was moved and connected to another kinematic through a breakable PxConstraint/PxJoint.
              • +
              • Deleting a breakable PxConstraint/PxJoint while the simulation is running could lead to a crash if the PxConstraint/PxJoint broke in the same simulation step.
              • +
              • A bug in the PxScene::addBroadPhaseRegion() function, that could lead to a crash when using 'populateRegion=true', has been fixed.
              • +
              +
            + +

            Particles

            +
              +
            • Added:
            • +
                +
              • Added triangle mesh cache statistics for GPU particles. Triangle mesh cache statistics are also captured by PVD as part of simulation statistics.
              • +
              • Added new option to query approximate particle velocities relative to colliding rigid actors. This can be used for debris rotation on moving objects. Enable with PxParticleReadDataFlag::eCOLLISION_VELOCITY_BUFFER and read from PxParticleReadData::collisionVelocityBuffer.
              • +
              +
            • Fixed:
            • +
                +
              • Fixed a bug which might lead to GPU particle pipeline failures on low end GPUs.
              • +
              • Enabled warning when a spatial data structure overflow occured for GPU particles (see the guide for more information).
              • +
              +
            + +

            Cloth

            +
              +
            • Fixed:
            • +
                +
              • PxFilterFlag::eSUPPRESS was ignored for collision pairs that involved a PxCloth object. This does work now, however, please note that PxFilterFlag::eSUPPRESS is treated as PxFilterFlag::eKILL for pairs with a PxCloth object.
              • +
              +
            + +

            Serialization

            +
              +
            • Added:
            • +
                +
              • Support for binary compatibility between different sdk releases and patches has been added (PX_BINARY_SERIAL_VERSION). The current sdk version can load binary data of the older sdk versions listed in the documentation of PX_BINARY_SERIAL_VERSION.
              • +
              • SnippetLoadCollection has been added. It illustrates loading repx or binary serialized collections and instatiating the objects in a scene. It only compiles and runs on authoring platforms (windows, osx and linux).
              • +
              • SnippetConvert has been added. It illustrates how to convert PhysX 3 serialized binary files from one platform to another. It only compiles and runs on authoring platforms (windows, osx and linux).
              • +
              +
            • Deprecated:
            • +
                +
              • Method PxCollection::addRequire is deprecated, use PxCollection::add and PxCollection::contains instead.
              • +
              • Method PxCollection::createBinaryConverter(PxSerializationRegistry&) is deprecated, use PxCollection::createBinaryConverter() instead.
              • +
              +
            + +

            Character controller

            +
              +
            • Added:
            • +
                +
              • PxControllerManager::setPreventVerticalSlidingAgainstCeiling() has been added, to control the behaviour of characters against ceilings.
              • +
              +
            + + +

            Vehicles

            +
              +
            • Added:
            • +
                +
              • Vehicles may now be updated concurrently through the addition of a new function PxVehiclePostUpdates and passing a PxVehicleConcurrentUpdateData array to PxVehicleupdates.
              • +
              • A new snippet SnippetVehicleMultiThreading has been added to show the operation of concurrent vehicle updates.
              • +
              • PxVehicleDriveTankControl and PxVehicleDriveTankControlModel now have improved doxy comments.
              • +
              • A new function PxVehicleUpdateCMassLocalPose has been added to help update a vehicle after the center of mass pose of the vehicle's actor has been modified.
              • +
              • PxWheelQueryResult now records the local pose of the wheel.
              • +
              +
            • Changed:
            • +
                +
              • PxVehcicleDrive4W::setup now tests that at least 4 wheels are specified and returns wtih an error message if numWheels < 4. It is only possible to create a PxVehicleDrive4W instance with less than 4 active wheels by disabling wheels after instantiating a 4-wheeled car.
              • +
              • In debug and checked build configurations PxVehicleComputeSprungMasses now reports whether the sprung masses were successfully computed. Warnings are passed to the error stream in checked configuration if the function does not complete successfully. Apart from error checking the operation of the function is unchanged.
              • +
              +
            • Fixed:
            • +
                +
              • The doxy comment describing the default setting for PxVehicleWheelsSimData::setWheelShapeMapping was incorrect. This now correctly documents the default mapping as PxVehicleWheelsSimData::setWheelShapeMapping(i,i).
              • +
              • Suspensions raycasts that start inside geometry are ignored for all geometry types. Prior to this release this was true for all geometry types except for heightfields and triangle meshes. This inconsistency has now been fixed so that all geometry types obey the rule that suspension raycasts starting inside geometry are neglected.
              • +
              +
            + +

            Scene queries

            +
              +
            • Added:
            • +
                +
              • Added eMTD flag. If an initial overlap is detected, this flag triggers the sweep to compute the MTD (Minimum Translation Direction), which can be used to de-penetrate the query shape from the shape with which an initial overlap was found. In this case, the distance reported will be negative. This negative distance can be used to scale the reported normal to generate the translation vector required to de-penetrate the query shape.
              • +
              • Added PxTriangle::pointFromUV.
              • +
              +
            • Fixed:
            • +
                +
              • A rare ray-capsule intersection bug has been fixed, when the capsule's height is close to zero.
              • +
              • A capsule-capsule distance bug has been fixed, when the tested capsules are large and parallel.
              • +
              • Raycasts against heightfields now correctly return triangle UVs.
              • +
              +
            • Changed:
            • +
                +
              • + PxBatchQuery::raycast, overlap and sweep previously had an incorrect const modifier indicating that these methods were safe to call from multiple threads simultaneously. This has been removed. + Multiple batch queries can still be executed (via PxBatchQuery::execute()) in parallel. +
              • +
              +
            + +

            Cooking

            +
              +
            • Added:
            • +
                +
              • PxCookingParams::meshSizePerformanceTradeOff parameter can be used to make the mesh smaller at the expense of reduced simulation and scene query performance (or the other way around).
              • +
              • PxCookingParams::meshCookingHint parameter can be used to specify mesh hierarchy construction preference (cooking speed or simulation speed).
              • +
              • PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH disables mesh clean proces. Vertices duplicities are not searched, huge triangles test is not done. Vertices welding is not done. Does speed up the cooking.
              • +
              • PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE disables vertex edge precomputation. Makes cooking faster but slow up contact generation.
              • +
              • PxPhysicsInsertionCallback adds the support for inserting cooked triangle mesh or height field directly into PxPhysics without the stream serialization.
              • +
              • PxCooking::createTriangleMesh creates triangle mesh and inserts it to PxPhysics without using the stream serialization.
              • +
              • PxCooking::createHeightField creates height field and inserts it to PxPhysics without using the stream serialization.
              • +
              • PxCooking::validateTriangleMesh validates mesh in separate function before it can be cooked without the mesh cleaning.
              • +
              • PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES checks and removes almost zero-area triangles during the computation of the convex hull.
              • +
              • PxCookingParams::areaTestEpsilon triangle area size was added. This epsilon is used for the zero-area test in the computation of the convex hull.
              • +
              +
            • Changed:
            • +
                +
              • PxCooking::computeHullPolygons does now return also vertices used by the polygons. Redundant vertices are removed.
              • +
              • PxCooking::cookConvexMesh now returns a PxConvexMeshCookingResult::Enum with additional error information.
              • +
              +
            + +

            Aggregates

            +
              +
            • Added:
            • +
                +
              • PxSceneLimits has a new member variable maxNbAggregates. Setting this value to a good approximation of the peak number of aggregates will avoid the need for internal run-time allocations that can occur when aggregates are added to the scene.
              • +
              +
            • Fixed:
            • +
                +
              • PxScene::removeActor will auotmatically remove that actor from all PxAggregate instances that contain the removed actor. Likewise, PxScene::removeArticulation will automatically remove all articulation links from all relevant aggregates. This fix upholds the rule that all actors of an aggregate must be in same scene.
              • +
              • The condition of an internal assert that triggered after calling PxScene::addAggregate has been corrected. This assert triggered when an aggregate was added to the scene after removal of all aggregates from the scene. The operation of the function PxScene::addAggregate is unchanged apart from the asserted condtition.
              • +
              +
            + +

            Samples

            +
              +
            • Changed:
            • +
                +
              • Starting with Release 302 drivers, application developers can direct the Optimus driver at runtime to use the High Performance Graphics to render any application - even those applications for which there is no existing application profile. The samples now make use of this feature to enable High Performance Graphics by default.
              • +
              +
            +
            + + +
            + + +

            Release Notes - NVIDIA® PhysX® SDK 3.3

            +

            September 2013

            + +

            Release Highlights

            +
            +
              +
            • Added PhysXDevice/64.dll to the PC packages. See the Windows readme for details.
            • +
            • Added support for the NVIDIA Kepler GPU architecture.
            • +
            • Added support for the Nintendo Wii U console.
            • +
            • Added support for Windows 8 Modern UI applications (ARM and x86).
            • +
            • Ported our SIMD library to the ARM NEON architecture.
            • +
            • Multi Box Pruning (MBP) is offered as an alternative broad phase algorithm to Sweep And Prune (SAP). MBP shows improved performance when most objects are moving or when inserting large numbers of objects. SAP can be faster in scenarios with few moving (many sleeping) objects.
            • +
            • Significant performance and stability optimizations for rigid body solver.
            • +
            • New function to compute the minimum translational distance and direction to separate two overlapping geometry objects.
            • +
            • New 'PCM' contact generation mode which is often faster and more robust than the still available legacy path.
            • +
            • Improved performance of scene queries and contact reports.
            • +
            • Improved behavior and performance of Continuous Collision Detection (CCD).
            • +
            • Reduced memory footprint of rigid body classes.
            • +
            • Added support for sharing shapes among rigid bodies.
            • +
            • Significantly improved cloth behavior and GPU performance.
            • +
            • Added support for cloth colliding against itself, other cloth instances, and scene geometry.
            • +
            • Improved useability of binary and xml serialization.
            • +
            • Memory can be saved for objects that do not participate in the simulation and are used for scene queries only. For details see the new flag PxActorFlag::eDISABLE_SIMULATION.
            • +
            +
            + +

            Supported Platforms

            +
            +

            Runtime

            +
              +
            • Apple iOS
            • +
            • Apple Mac OS X
            • +
            • Google Android (version 2.2 or later for SDK, 2.3 or later required for samples)
            • +
            • Linux (tested on Ubuntu)
            • +
            • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
            • +
            • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
            • +
            • Microsoft XBox One
            • +
            • Microsoft XBox 360
            • +
            • Nintendo Wii U
            • +
            • Sony Playstation 3
            • +
            • Sony Playstation 4 (SDK only, no samples yet)
            • +
            • Sony Playstation Vita
            • +
            +

            Development

            +
              +
            • Microsoft Windows XP or later
            • +
            • Microsoft Visual Studio 2008, 2010, 2012 (Windows RT/PS4/XboxOne only)
            • +
            • Xcode 4.6
            • +
            +
            + +

            Known Issues

            +
            +
              +
            • PxSimulationOrder::eSOLVE_COLLIDE feature is not implemented in this release. Calls to PxScene::solve() and PxScene::collide() will be ignored with a warning added to the error stream
            • +
            • Reading back the kinematic target through PxRigidDynamic::getKinematicTarget() in the PxSimulationEventCallback::onContact() callback will fail.
            • +
            • Cloth self-collision without rest position is not supported on GPUs with SM capability lower than 2.0.
            • +
            +
            +

            + Changes and Resolved Issues

            +
            + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

            General

            +
              +
            • Added:
            • +
                +
              • PxScene::setLimits: Hint to preallocate capacities of various data structures. See PxSceneLimits.
              • +
              • Scalar constructors PxQuat(r), PxMat33(r), PxMat33(r).
              • +
              • identity constructors for PxMat33, PxMat44, PxQuat, PxTransform - e.g. PxTransform(PxIdentity).
              • +
              • zero constructors for PxMat33, PxMat44, PxVec3 - e.g. PxMat33(PxZero).
              • +
              • PxTransform(x, y, z) constructor was added as a shortcut for PxTransform(PxVec3(x,y,z)).
              • +
              • The GPU code uses CUDA version 5.0 and supports Kepler GPUs (SM version 3.0 and 3.5). +
              • Helper method PxContactPair::bufferContacts() has been added to copy the contact pair data stream into a user buffer.
              • +
              • PxGeometryQuery::computePenetration() has been added, to compute the Minimum Translational Distance between geometry objects.
              • +
              • Ability for APEX and other PhysX extensions to change the PhysX Visual Indicator.
              • +
              • Reporting allocation names can now be enabled or disabled (see PxFoundation::setReportAllocationNames). When enabled, some platforms allocate memory through 'malloc'.
              • +
              • The scene origin can now be shifted to better support big world scenarios. See PxScene::shiftOrigin() for details.
              • +
              • PxAssertHandler got extended with a boolean parameter to support ignoring specific asserts. See windows implementation of DefaultAssertHandler.
              • +
              • Added new PxPairFlags - PxPairFlag::eSOLVE_CONTACT and PxPairFlag::eDETECT_DISCRETE_CONTACT.
              • +
              +
            • Removed:
            • +
                +
              • The obsolete PxConvexFlag::eUSE_UNCOMPRESSED_NORMALS flag has been removed.
              • +
              • The PxSceneFlag::eDISABLE_SSE was obsolete, and has now been removed.
              • +
              • The obsolte PxPtrArray has been removed
              • +
              +
            • Changed:
            • +
                +
              • Mesh BVH construction was significantly improved for meshes with a mix of large and small triangles. Mesh sizes are now slightly increased, but traversals are substantially faster. As a side effect, cooked mesh format has changed. This requires meshes to be recooked!
              • +
              • The specification for valid PxBounds3 representations has changed. See the API documentation for details (especially the newly introduced PxBounds3::isValid() method).
              • +
              • PxBounds3::transform(), ::scale() and ::fatten() have been split into a fast and a safe version to avoid unnecessary checks and improve stability for empty bounds respectively.
              • +
              • PxBounds3::setInfinite() has been renamed to PxBounds3::setMaximal() to better fit the actual behavior.
              • +
              • PxTask: PVD profile events for tasks are now only emitted in profile builds (all platforms).
              • +
              • Platform mutex implementations now verify that lock/unlock come from correct thread in debug builds.
              • +
              • PxWindowsDelayLoadHook.h has been moved from Include/foundation/windows to Include/common/windows.
              • +
              • API instances of 'Num' as in 'maxNum' and 'getNum' have changed uniformly to 'Nb'.
              • +
              • + The following classes have been renamed: +
                  +
                • PxSceneQueryHit to PxQueryHit
                • +
                • PxSceneQueryFlags to PxHitFlags
                • +
                • PxSceneQueryHitType to PxQueryHitType
                • +
                • PxSceneQueryFilterData to PxQueryFilterData
                • +
                • PxSceneQueryFilterCallback to PxQueryFilterCallback
                • +
                • PxSceneQueryFilterFlags to PxQueryFlags
                • +
                • PxSceneQueryCache to PxQueryCache
                • +
                • PxCCTNonWalkableMode to PxControllerNonWalkableMode
                • +
                • PxControllerFlags to PxControllerCollisionFlags
                • +
                • PxCCTHit to PxControllerHit
                • +
                • PxConstraintDominance to PxDominanceGroupPair
                • +
                • PxActorTypeSelectionFlags to PxActorTypeFlags
                • +
                • PxFindOverlapTriangleMeshUtil to PxMeshOverlapUtil
                • +
                + The previous names have been retained for compatibility but are deprecated. +
              • +
              • PX_SLEEP_INTERVAL has been replaced with the new parameter PxSceneDesc::wakeCounterResetValue to specify the wake counter value to set when wakeUp() gets called on dynamic objects.
              • +
              • PxClientBehaviorBit has been renamed PxClientBehaviorFlag, PxActorClientBehaviorBit has been renamed PxActorClientBehaviorFlag. Names of related functions have also changed.
              • +
              • queryClient parameter in raycast(), sweep(), overlap() functions was moved inside of PxQueryFilterData struct.
              • +
              • The PxObserver/PxObservable system has been replaced by the PxDeletionListener API. The supported object types have been extended from PxActor to all core objects inheriting from PxBase. Furthermore, two kinds of deletion events are now distinguished: user release and memory release. Please read the API documentation for details.
              • +
              • Deprecated PxPairFlag::eRESOLVE_CONTACT. Use PxPairFlag::eDETECT_DISCRETE_CONTACT and PxPairFlag::eSOLVE_CONTACT instead.
              • +
              • Number of materials per shape is now PxU16 instead of PxU32, contact material information also now returns PxU16.
              • +
              • Maximum number of touching hits in batched queries is now PxU16 instead of PxU32.
              • +
              • SweepEpsilonDistance has been replaced by meshContactMargin and marked as deprecated. Please read the API documentation for details.
              • +
              • PxShape::resetFiltering() and PxParticleBase::resetFiltering() have been deprecated. Please use one of the new overloaded methods PxScene::resetFiltering() instead.
              • +
              • The pxtask namespace has been removed and it's types have been added to the physx namespace with a Px* prefix
              • +
              • The delay load hook PxDelayLoadHook::setPhysXInstance has been renamed to PxSetPhysXDelayLoadHook and PxDelayLoadHook::setPhysXCookingInstance has been renamed to PxSetPhysXCookingDelayLoadHook
              • +
              +
            • Fixed:
            • +
                +
              • Calling Thread::setAffinityMask() before the thread has been started is now supported.
              • +
              • PxDefaultSimulationFilterShader ignored the value of the second filter constant in the collision filtering equation and used the value of the first filter constant instead.
              • +
              +
            • Deprecated:
            • +
                +
              • The PxScene::flush() method has been deprecated, please use PxScene::flushSimulation().
              • +
              • PxRigidDynamicFlag has been deprecated and replaced with PxRigidBodyFlag to allow flags to be shared between PxArticulationLink and PxRigidDynamic.
              • +
              • PxTransform::createIdentity(), PxQuat::createIdentity(), PxMat33::createIdentity(), PxMat44::createIdentity(), PxMat33::createZero(), PxMat44::createZero()
              • +
              +
            + +

            Character controller

            +
              +
            • Added:
            • +
                +
              • The PxControllerBehaviorFlag::eCCT_USER_DEFINED_RIDE flag has been added.
              • +
              • A new helper function PxController::resize() has been added to facilitate character controller resizing.
              • +
              • A new runtime tessellation feature has been added that can help reducing FPU accuracy issues in the sweep tests.
              • +
              • PxControllerFilterCallback has been added to make CCT-vs-CCT filtering more flexible.
              • +
              • An overlap recovery module has been added. See PxControllerManager::setOverlapRecoveryModule
              • +
              • PxObstacle notifications has been added to handle touched obstacles.
              • +
              • PxObstacleContext::getObstacleByHandle has been added.
              • +
              • The origin of character controllers and obstacles can now be shifted to stay in sync when the origin of the underlying PxScene is shifted. See PxControllerManager::shiftOrigin() for details.
              • +
              +
            • Changed:
            • +
                +
              • The PxControllerManager is now tightly coupled to a PxScene which has to be provided on creation. See PxCreateControllerManager().
              • +
              • The PxObstacleContext instances of a PxControllerManager will now get released automatically when the manager is released.
              • +
              • PxController::reportSceneChanged() has been renamed to PxController::invalidateCache().
              • +
              • PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT is not the default behavior anymore.
              • +
              • PxCCTNonWalkableMode::eFORCE_SLIDING has been renamed to PxCCTNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING.
              • +
              • PxCCTInteractionMode has been renamed PxControllerInteractionMode
              • +
              • PxCCTNonWalkableMode has been renamed PxControllerNonWalkableMode
              • +
              • PxCCTHit has been renamed PxControllerHit
              • +
              • Touched PxObstacle has been replaced by touched ObstacleHandle.
              • +
              • PxControllerInteractionMode and PxControllerFilters::mActiveGroups have been removed. Please use the new PxControllerFilterCallback instead.
              • +
              +
            • Fixed:
            • +
                +
              • Bugs with respect to deleting shapes from a touching actor have been fixed.
              • +
              • Touched actor's scene is checked in CCT::move before rideOnTouchedObject is called to ensure that the touched shape is valid.
              • +
              • Touched shape's scene query flag is checked in CCT::move before rideOnTouchedObject is called to ensure that the touched shape is valid.
              • +
              • Touched shape's user CCT filtering is triggered in CCT::move before rideOnTouchedObject is called to ensure that the touched shape is valid.
              • +
              • The PxControllerBehaviorCallback was not called when a PxUserControllerHitReport was not defined.
              • +
              • It is not possible anymore to create a CCT whose contact offset is zero. The doc has always said a non-zero value was expected, but corresponding check was missing.
              • +
              • Box & capsule controllers' resize function now properly take the up direction into account
              • +
              +
            + +

            CCD

            +
              +
            • Added:
            • +
                +
              • Introduced PxRigidBodyFlag::eENABLE_CCD_FRICTION to control whether friction is applied inside CCD on a given body. This is disabled by default. In general, disabling friction in CCD improves the behavior of high-speed collisions.
              • +
              • Introduced PxSceneDesc::ccdMaxPasses field, which controls the maximum number of CCD passes. Each CCD pass will advance each object to its next TOI. By default, we use 1 pass. Increasing the number of passes can reduced the likelihood of time being dropped inside the CCD system at the cost of extra CCD procesing overhead.
              • +
              • Introduced per-body value to control how far past the initial time of impact the CCD advances the simulation. Advancing further can improve fluidity at the increased risk of tunneling.
              • +
              • CCD now has limited support contact modification. It supports disabling response to contacts by setting max impulse to 0. It does not yet support target velocity, non-zero max impulse values or scaling mass or inertia.
              • +
              • Introduced new PxPairFlag eDETECT_CCD_CONTACT. This flags is used to control whether CCD performs sweep tests for a give pair. Decision over whether any collisions are responded to is made by the presence of the flag eSOLVE_CONTACT.
              • +
              +
            • Removed:
            • +
                +
              • CCD is now enabled per-body instead of per-shape. As a result, PxShapeFlag::eUSE_SWEPT_BOUNDS has been removed and replaced with PxRigidBodyFlag::eENABLE_CCD.
              • +
              +
            • Changed:
            • +
                +
              • API attributes previously named SWEPT_INTEGRATION have now been renamed 'ccd': specifically PxSceneFlag::eENABLE_SWEPT_INTEGRATION, PxSceneFlag::eSWEPT_INTEGRATION_LINEAR, PxSceneDesc::sweptIntegrationLinearSpeedFactor, PxSceneDesc::sweptIntegrationAngularSpeedFactor, PxPairFlag::eENABLE_SWEPT_INTEGRATION
              • +
              • PxPairFlag::eCCD_LINEAR has been deprecated. Use (PxPairFlag::eDETECT_CCD_CONTACT | PxPairFlag::eSOLVE_CONTACT) instead.
              • +
              +
            • Fixed:
            • +
                +
              • Updated CCD algorithm which improves the fluidity of motion of the CCD system while reducing the processing overhead significantly.
              • +
              • Contact notification is now reliable in CCD. CCD contacts are appended to the end of the contact list for a given pair so that both discrete and continuous contacts are reported
              • +
              • CCD contact notification now reports applied forces. These contacts will be correctly filtered by force thresholds.
              • +
              +
            + +

            Serialization

            +
              +
            • Added:
            • +
                +
              • Added extension PxSerialization::isSerializable method to query whether a collection is serializable.
              • +
              • Added extension PxSerialization::complete which allows to prepare a collection for serialization.
              • +
              • Added extension PxSerialization::createNames which adds reference names to serializables.
              • +
              • Added extension PxCollectionExt::remove which removes all serializables of a certain type and optionally adds them to another collection.
              • +
              • Added extension function PxCollectionExt::releaseObjects to remove and release objects from a collection
              • +
              • Added class PxSerializationRegistry for handling custom serializable types.
              • +
              • Added PxSerialization::serializeCollectionToXml and PxSerialization::createCollectionFromXml
              • +
              • Included pre-built binary meta data with SDK at [path to installed PhysX SDK]/Tools/BinaryMetaData
              • +
              • Added class PxSerializer to provide serialization specific functionality to serializable classes.
              • +
              • Added classes PxSerializationContext and PxDeserializationContext to provide functionality for serialization and deserialization operations.
              • +
              +
            • Removed:
            • +
                +
              • Removed PxUserReferences class and PxPhysics::createUserReferences.
              • +
              • Removed RepX.h and unified serialization interfaces for xml and binary serialization.
              • +
              +
            • Changed:
            • +
                +
              • PxCollection was reworked to improve reference management rendering PxUserReferences obsolete. PxCollection was also decoupled more from Serialization/Deserialization functionality. Serialization of incomplete collections fails now early at serialization as opposed to late at deserialization.
              • +
              • Replaced PxPhysics::createCollection with PxCreateCollection. +
              • +
              • Replaced RepXCollection with PxCollection, and unified corresponding interfaces.
              • +
              • Replaced PxCollection::serialize with PxSerialization::serializeCollectionToBinary.
              • +
              • Replaced PxCollection::deserialize with PxSerialization::createCollectionFromBinary.
              • +
              • Replaced PxSerializable::collectForExport(PxCollection& c) with PxSerializer::requires. The new method works in a non-recursive way.
              • +
              • Replaced PxDumpMetaData with PxSerialization::dumpMetaData.
              • +
              • Replaced PxCollectForExportSDK with PxCollectionExt::createCollection(PxPhysics& sdk).
              • +
              • Replaced PxCollectForExportScene with PxCollectionExt::createCollection(PxScene& scene).
              • +
              • Moved PxCooking::createBinaryConverter to PxSerialization
              • +
              • Changed PxShape release semantics for shapes. As a consequence deserialized shapes are never autmatically released, but need to be released by the application. Exception: PxPhysics::release.
              • +
              +
            + +

            Cloth

            +
              +
            • Added:
            • +
                +
              • Improved GPU cloth performance significantly with new parallel solver.
              • +
              • Added tether constraints, which allow minimum amount of cloth stretching even for large gravity scales. See PxClothFabric.
              • +
              • Added support for dynamic addition and deletion of collision primitives. See PxCloth::addCollision* and PxCloth::removeCollision*.
              • +
              • Added triangle mesh collider support. See PxCloth::setCollisionTriangles.
              • +
              • Added support for self collision and inter-cloth collision. See PxCloth::setSelfCollision*() and PxScene::setClothInterCollision*().
              • +
              • Added direct access to CUDA particle data for graphics interoperability, see PxCloth::lockParticleData()
              • +
              • Added PxRegisterCloth to reduce binary size by stripping unused code on platforms where static linking is used.
              • +
              • Added methods setWakeCounter/getWakeCounter() (see the corresponding API documentation for details).
              • +
                  +
                • It is illegal to call wakeUp/putToSleep/isSleeping() on a PxCloth that has not been added to a scene.
                • +
                +
              +
            • Changed:
            • +
                +
              • Cloth solver does not use fibers any more. See PxClothFabric for changes in fabric API.
              • +
              • Moved PxCooking.cookClothFabric() to extensions. See PxClothFabricCooker and PxClothFabricCreate.
              • +
              • PxClothMeshDesc has been moved to extensions and now supports both triangle and quad representations. See PxClothMeshDesc.
              • +
              • The scaling of damping and stiffness coefficients has been separated from the solver frequency and can now be set indepedently using PxCloth::setStiffnessFrequency().
              • +
              • PxCloth::setInertiaScale() has been split into linear, angular, and centrifugal components. See PxCloth::set*IntertiaScale.
              • +
              • Drag coefficient has been split into linear and angular coefficient. See PxCloth::setLinearDragCoefficient and PxCloth::setAngularDragCoefficient.
              • +
              • Renamed PxCloth::lockClothReadData() to lockParticleData(). Added support for update operations on the returned particle arrays (as an alternative to setParticles()).
              • +
              • PxCloth::wakeUp() does not have a parameter anymore. Use setWakeCounter() instead to set a specific value.
              • +
              • PxCloth::getNbCollisionSpherePairs() has been renamed to PxCloth::getNbCollisionCapsules()
              • +
              +
            • Fixed:
            • +
                +
              • Fixed a crash bug in the clothing collision code appearing on iOS.
              • +
              +
            + +

            Rigid Bodies

            +
              +
            • Added:
            • +
                +
              • The contact distance parameter for a limit is automatically estimated if not supplied in the constructor for the limit structure.
              • +
              • The new callback PxConstraintConnector::onOriginShift() has been introduced. It gets called for all constraints of a scene, when its origin is shifted.
              • +
              • New helper function PxRigidBodyExt::computeVelocityDeltaFromImpulse has been added.
              • +
              • Shapes may be declared as shared on creation, and then attached to multiple actors, see the user manual for restrictions.
              • +
              • + Since a shape is no longer necessarily associated with a unique actor, references to shapes in callbacks from the engine are accompanied by the + a reference to the associated actor +
              • +
              • Joints and contact modification now support tuning relative mass and inertia for the bodies on a per-contact basis. Inertia and mass can be tuned independently.
              • +
              +
            • Removed:
            • +
                +
              • PxShape::overlap(), PxShape::sweep() and PxShape::raycast() have been removed. Equivalent functionality is provided in PxGeometryQuery.
              • +
              +
            • Changed:
            • +
                +
              • It is illegal to call resetFiltering() on a PxShape or PxParticleBase if they have not been added to a scene.
              • +
              • It is illegal to call addForce/addTorque/clearForce/clearTorque() on a PxRigidBody that has not been added to a scene.
              • +
              • + The sleep behavior of dynamic rigid bodies has changed significantly (for details on the current behavior see the API documentation of isSleeping(), wakeUp(), putToSleep(), setKinematicTarget(), PxRigidBodyFlag::eKINEMATIC, ...). Among the changes are: +
                  +
                • The methods setWakeCounter/getWakeCounter() have been added for PxRigidDynamic and PxArticulation objects (see the corresponding API documentation for details).
                • +
                • The wakeUp() method of PxRigidDynamic and PxArticulation has lost the wake counter parameter. Use setWakeCounter() instead to set a specific value.
                • +
                • It is illegal to call wakeUp/putToSleep/isSleeping() on a PxRigidDynamic or PxArticulation that has not been added to a scene.
                • +
                • Putting a dynamic rigid actor to sleep will clear any pending force updates.
                • +
                • Switching a dynamic actor to kinematic will put the actor to sleep immediately.
                • +
                • Switching a kinematic actor back to dynamic will not affect the sleep state (previously the actor was woken up).
                • +
                • Calling wakeUp/putToSleep() on a kinematically controlled dynamic actor is not valid any longer. The sleep state of a kinematic actor is solely defined based on whether a target pose has been set (see API documentation of isSleeping() for details).
                • +
                • A call to PxRigidBody::setCMassLocalPose() does not wake up the actor anymore. Note: this also affects related methods in PhysXExtensions like PxRigidBodyExt::updateMassAndInertia() etc.
                • +
                • If a non-zero velocity or force is set through PxRigidBody::setLinearVelocity(), ::setAngularVelocity(), ::addForce() or ::addTorque(), the actor will get woken up automatically even if the autowake parameter is false.
                • +
                • PxRigidBody::clearForce() and ::clearTorque() do not have the autowake parameter, to optionally wake the actor up, anymore. These methods will not change the sleep state any longer. Call ::wakeUp() subsequently to get the old default behavior.
                • +
                • Adding or removing a PxConstraint/PxJoint to/from the scene does not wake the connected actors up automatically anymore.
                • +
                • It is now possible to avoid automatic wake up of previously touching objects on scene removal. See the additional parameter wakeOnLostTouch in PxScene::removeActor(), ::removeArticulation(), ::removeAggregte(), PxRigidActor::detachShape().
                • +
                +
              • +
              • PxJointLimit and PxJointLimitPair are now PxJointLinearLimit, PxJointLinearLimitPair, PxJointAngularLimitPair, depending on whether the limit is linear or angular.
              • +
              • Joints now solve for the entire position error rather than a ratio of 0.7 of it. The flag PxConstraintFlag::eDEPRECATED_32_COMPATIBILITY can be used to restore this behavior
              • +
              • PxConstraintFlag::Type has been renamed to PxConstraintFlag::Enum +
              • +
              • The spring constant parameter in joints and articulations that was previously 'spring' is now 'stiffness'.
              • +
              • The tangential spring constant parameter in articulations that was previously 'tangentialSpring' is now 'tangentialStiffness'.
              • +
              • Constraints do not respect PxDominanceGroup settings. Use PxJoint::setInvMassScale and setInvInertiaScale
              • +
              • Shapes are reference counted. PxShape::release() now decrements the reference count on a shape, and its use is deprecated for detaching a shape from its actor - use detachShape() instead.
              • +
              • Shape creation methods do not take a local transform parameter anymore. Instead PxShapeFlags can be specified. Triangle meshes, height fields and plane geometry shapes cannot be combined with non-kinematic PxRigidDynmic actors if PxShapeFlag::eSIMULATION_SHAPE is specified. Corresponding calls to PxRigidActor::createShape() or PxRigidActor::attachShape() are not supported.
              • +
              • PxShape::getActor() now returns a pointer, which is NULL if the shape is shareable.
              • +
              • PxShape::getWorldBounds() has been replaced with PxShapeExt::getWorldBounds().
              • +
              • PxContactPoint has been renamed PxFeatureContact.
              • +
              • The internal format for contact storage has been modified; applications directly accessing the internal contact representation rather than PxContactPair::extractContacts should be modified accordingly.
              • +
              • Friction mode flags eENABLE_ONE_DIRECTIONAL_FRICTION and eENABLE_TWO_DIRECTIONAL_FRICTION have been replaced by PxFrictionType::Enum PxSceneDesc::frictionType.
              • +
              • PxSceneDesc::contactCorrelationDistance has been deprecated.
              • +
              • PxSceneDesc::contactCorrelationDistance no longer has an influence on how many friction anchors are created in a single frame, only on when they are removed in later frames. This may cause a very minor change in friction behavior.
              • +
              +
            • Fixed:
            • +
                +
              • Rigid bodies now properly accumulate the forces/torques that are applied with addForce/addTorque while scene insertion is still pending. This affects bodies added to the scene while the scene is simulating and then given forces/torques with addForce/addTorque while the scene is simulating. These accumulated forces and torques are applied during the next simulate() call. Prior to this fix the forces/torques that accumulated while scene insertion was pending were lost and never applied.
              • +
              • Its now possible to serialize collections with jointed actors without including the corresponding joints in the collection. The deserialized actors will not be jointed anymore.
              • +
              • Joint drive force limits are actual force limits rather than impulse limits. Set the flag PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES to false to support legacy behavior
              • +
              • Angular drive constraints for revolute joints were wrongly computed, resulting in a negative angular velocity when a positive drive target was set.
              • +
              • Scene addActors will now correctly remove all actors that were passed to add, if some insert failed.
              • +
              • Contact modification now enforces max impulse field correctly. Previously, it only enforced it if max impulse was set to 0.
              • +
              • Contact modification now supports target velocity in all directions. Previously, it only enforced the target velocity components that were perpendicular to the contact normal.
              • +
              • Jittering of small spheres & capsules on very large triangles has been fixed.
              • +
              • Setting sleep threshold to 0 now guarantees that bodies won't fall asleep even if their kinetic energy reaches exactly 0.
              • +
              +
            • Deprecated:
            • +
                +
              • PxShape::release() now decrements the reference count on a shape, and its use is deprecated for detaching a shape from its actor - use detachShape() instead.
              • +
              • PxJoint::getType() is deprecated - use getConcreteType() instead.
              • +
              • PxConstraintFlag::eREPORTING is deprecated - constraints always generate force reports
              • +
              • PxConstraintDominance is deprecated - use PxDominanceGroupPair instead.
              • +
              +
            + +

            Scene queries

            +
              +
            • Added:
            • +
                +
              • PxVolumeCache, a volumetric cache for local collision geometry.
              • +
              • Parameter inflation was added to some PxGeometryQuery functions.
              • +
              • Flag eMESH_BOTH_SIDES can be now used to control triangle mesh culling for raycasts and sweeps.
              • +
              • Added PxBatchQueryMemory as a part of PxBatchQueryDesc, to allow memory buffers to be set before execution.
              • +
              • PxBatchQueryDesc() constructor now requires 3 parameters at initialization - see migration guide for more details.
              • +
              • PxBatchQuery::raycast, sweep, overlap calls will now issue a warning and discard the query when over maximum allowed queries.
              • +
              • There is a new flag to allow manually updating the scene query representation, see: PxSceneFlags::eENABLE_MANUAL_SQ_UPDATE and PxScene::flushQueryChanges().
              • +
              • Added PxScene::forceDynamicTreeRebuild() function to immediately rebuild the scene query structures.
              • +
              • Added bool PxSweepHit::hadInitialOverlap() returning true if a sweep hit occurred early due to initial overlap at distance=0.
              • +
              +
            • Removed:
            • +
                +
              • PxBatchQuery::linearCompoundGeometrySweepSingle and PxBatchQuery::linearCompoundGeometrySweepMultiple functions are no longer supported.
              • +
              • Globals PxPS3ConfigParam::eSPU_OVERLAP, eSPU_RAYCAST, eSPU_SWEEP that were previous set via setSceneParamInt call are replaced with PxBatchQueryDesc::runOnSpu. See migration guide for more details.
              • +
              +
            • Changed:
            • +
                +
              • Scene Query raycastAny/Single/Multiple APIs were merged into a single raycast() call (same for overlaps and sweeps). Please refer to user and migration guides for details.
              • +
              • Scene Query raycast() API now uses a PxRaycastBuffer or a PxRaycastCallback parameter for reporting hits. Blocking hits are now reported separately from toching and PxRaycastCallback class supports reporting an unbounded number of results (same for overlaps and sweeps).
              • +
              • A const templated PxRaycastBufferN object was added to allow convenient creation of fixed size scene query touch buffers. Same for overlaps and sweeps.
              • +
              • Support for compound sweeps was moved out from core SDK to extensions.
              • +
              • Support for compound sweeps was moved from PxScene to extensions (see PxRigidBodyExt::linearSweepSingle, PxRigidBodyExt::linearSweepMultiple).
              • +
              • PxQueryFilterCallback::preFilter now passes an actor pointer as well as a shape pointer.
              • +
              • PxSceneQueryFlag::eINITIAL_OVERLAP and PxSceneQueryFlag::eINITIAL_OVERLAP_KEEP have been replaced with PxHitFlag::eINITIAL_OVERLAP_DISABLE and PxLocationHit::hadInitialOverlap(). Note that checking for initial overlap is now the defaut for sweeps.
              • +
              • Sweeps in 3.3 execute using a new faster code path, in some cases with reduced precision. If you encounter precision issues not previously experienced in earlier versions of PhysX, use ePRECISE_SWEEP flag to enable the backwards compatible more accurate sweep code.
              • +
              • The default behavior for overlap queries with query filters returning eBLOCK has changed to only return one of eBLOCK hits. Please refer to the migration guide for details.
              • +
              +
            • Fixed:
            • +
                +
              • Scene Query performance was significantly improved in a variety of scenarios.
              • +
              • Fixed a bug in capsule/mesh overlap code that occasionally caused unreported and misreported faces.
              • +
              • Fixed a crash in raycastMultiple when a convex was hit by the ray and eMESH_MULTIPLE flag was specified as a query flag.
              • +
              • Fixed a rare crash in heightfield raycast code.
              • +
              • Internal limit of 65536 results has been removed.
              • +
              • Accuracy in sphere/capsule-vs-triangle overlap and sweep tests has been improved.
              • +
              +
            + +

            Cooking

            +
              +
            • Added:
            • +
                +
              • Added support for convex hull creation with limited output vertices count.
              • +
              • Added support for convex hull creation directly from polygons rather than triangles.
              • +
              • Added support function computeHullPolygons in cooking, that creates hull polygons from given vertices and triangles. The resulting polygons can be used to create the convex hull directly.
              • +
              +
            • Changed:
            • +
                +
              • Changed convex hull volume integrals.
              • +
              • PxCookingParams constructor requires now PxTolerancesScale as an additional parameter. This enables us to perform further checks on the triangles during cooking. A warning will be emitted to the error stream if too huge triangles were found. This will ensure better simulation stability.
              • +
              +
            • Fixed:
            • +
                +
              • Optimized heightfield load code for no-endian conversion case.
              • +
              +
            + +

            Triangle meshes

            +
              +
            • Added:
            • +
                +
              • Added PxTriangleMeshFlag::eHAS_ADJACENCY_INFO flag for adjacency information checks.
              • +
              +
            • Removed:
            • +
                +
              • Removed has16BitTriangleIndices(), replaced by triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICES.
              • +
              +
            + +

            Particles

            +
              +
            • Added:
            • +
                +
              • Direct read access to CUDA particle data has been added for graphics interoperability, see PxParticleBase::lockParticleReadData(PxDataAccessFlags flags) and PxParticleFluid::lockParticleFluidReadData(PxDataAccessFlags flags)
              • +
              • Added PxRegisterParticles to reduce binary size by stipping unused code on platforms where static linking is used.
              • +
              • Added setExplicitCudaFlushCountHint to allow early flushing of the cuda push buffer.
              • +
              • Added caching of triangle meshes. setTriangleMeshCacheSizeHint is supported on Kepler and above GPUs.
              • +
              +
            • Fixed:
            • +
                +
              • Creating and immediately setting the position of particles failed and possibly crashed with GPU acceleration enabled. This would only happen after one or more simulation updates.
              • +
              • Creating many spread out particles might have crashed the GPU pipeline.
              • +
              +
            + +

            Broad Phase

            +
              +
            • Added:
            • +
                +
              • The SDK now supports multiple broad-phase algorithms (SAP & MBP). See Release Highlights, PxBroadPhaseType and PxBroadPhaseDesc.
              • +
              • PxVisualizationParameter::eMBP_REGIONS has been added to visualize MBP regions
              • +
              +
            • Fixed:
            • +
                +
              • The sap broadphase now gracefully handles PxShape instances whose axis-aligned bounding boxes have min/max limits with value +/- PX_MAX_F32 or QNAN or INF. Such bounds values can occur if a PxShape is given a global transform that is either illegal or close to the upper limit of the floating point range. Prior to this release, the sap broadphase could crash when the axis-aligned bounds of shapes had values that weren't within the floating point range. This has now been fixed. The overlap pairs reported by the broadphase for bounds with such values is undefined.
              • +
              +
            + +

            Vehicles

            +
              +
            • Added:
            • +
                +
              • Vehicles now support serialization. A PxSerializationRegistry instance may be passed into PxInitVehicleSdk and PxCloseVehicleSdk in order to enable support.
              • +
              • Vehicle telemetry graphs can now be queried per channel for the most recent value with the function PxVehicleGraph::getLatestValue.
              • +
              • New vehicle PxVehicleDriveNW type has been introduced. This class makes use of a new differential type PxVehicleDifferentialNW, which allows specified wheels to be equally coupled to the differential, and allows all for some or all of the N wheels to be driven.
              • +
              • Support for camber angles has been added to the PxVehicleSuspensionData class.
              • +
              • Moment of inertia parameter has been added to the PxVehicleEngineData class. Prior to this a value of 1.0 was assumed internally. A default value of 1.0 has been used in the constructor for backwards compatability.
              • +
              • Vehicle manual contains new section describing the conversion of default vehicle parameter values from SI units to any system of units with particular reference to the use of centimetres instead of metres .
              • +
              • The SI units of each parameter in PxVehicleComponents.h has been documented.
              • +
              • Vehicle manual contains updated troubleshooting section.
              • +
              • The requirements for disabled wheels (PxVehicleWheelsSimData::disableWheel) have been documented to make it clear that disabled wheels must be no longer associated with a PxShape, must have zero rotation speed, and must be decoupled from the differential. This is also now discussed in the guide.
              • +
              • Wheel raycasts documentation has been improved to clarify the start and end points of each raycast.
              • +
              • Suspension line raycasts do not need to be performed for each vehicle prior to update with PxVehicleUpdates. This feature is implemented with a boolean array passed as an extra function argument to PxVehicleSuspensionRaycasts. This feature is useful for vehicles that require only low level of detail.
              • +
              • The clutch model now supports two accuracy modes (PxVehicleClutchAccuracyMode::eESTIMATE and PxVehicleClutchAccuracyMode::eBEST_POSSIBLE). If the estimate mode is chosen the computational cost and accuracy of the clutch can be tuned with PxVehicleClutchData::mEstimateIterations.
              • +
              • PxVehicleSuspensionData now has a function setMassAndPreserveNaturalFrequency. This modifies the mass and stiffness of the spring in a way that preserves the spring natural frequency.
              • +
              • A new helper function PxVehicleCopyDynamicsData has been added that allows dynamics data such as engine rotation speed, wheel rotation speed, gear etc to be copied from one vehicle to another of the same type. This is particularly useful if a vehicle has a number of different versions where each represents a different level of detail.
              • +
              • A new function PxVehicleWheelsSimData::copy has been added to allow per wheel dynamics data to be copied from one vehicle to another.
              • +
              • The vehicle manual contains a new section "Level of Detail" describing the available options for vehicles that require only a low level of detail.
              • +
              • PxVehicleTireLoadFilterData now requires that mMinNormalisedLoad is greater than or equal to zero.
              • +
              • PxVehicleTireLoadFilterData now has a new member variable mMinFilteredNormalisedLoad. This value describes the filtered normalised load that occurs when the normalised is less than or equal to mMinNormalisedLoad.
              • +
              • PxVehicleWheelsSimData now has a new function setMinLongSlipDenominator. This can be used to tune stability issues that can arise when the vehicle slows down in the absence of brake and drive torques.
              • +
              • A new section "PxVehicleAutoBoxData" has been added to the vehicle tuning guide to describe operation of the automatic gearbox.
              • +
              • A new section "The Vehicle Under-steers Then Over-steers" has been added to the vehicle troubleshooting guide to describe steps to avoid twitchy handling on bumpy surfaces.
              • + A new section "The Vehicle Never Goes Beyond First Gear" has been added to the vehicle troubleshooting guide to describe a common scenario that occurs when the automatic gearbox is given a latency time that is shorter than the time taken to complete a gear change. +
              • A new section "The Vehicle Slows Down Unnaturally" has been added to the vehicle troubleshooting guide to describe the steps that can be taken to help the vehicle slow down more smoothly.
              • +
              • A number of vehicle snippets have been added.
              • +
              +
            • Changed:
            • +
                +
              • Minor api change for consistency: PxVehicleDrive4WWheelOrder has been introduced to replace the enum beginning with PxVehicleDrive4W::eFRONT_LEFT_WHEEL.
              • +
              • Minor api change for consistency: PxVehicleDrive4WControl has been introduced to replace the enum beginning with PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL.
              • +
              • Minor api change for consistency: PxVehicleDriveTankWheelOrder has been introduced to replace the enum beginning with PxVehicleDriveTankWheelOrder::eFRONT_LEFT.
              • +
              • Minor api change for consistency: PxVehicleDriveTankControl has been introduced to replace the enum beginning with PxVehicleDriveTank::eANALOG_INPUT_ACCEL.
              • +
              • Minor api change for consistency: PxVehicleDriveTankControlModel has been introduced to replace the enum beginning with PxVehicleDriveTank::eDRIVE_MODEL_STANDARD.
              • +
              • Minor api change for consistency: PxVehicleTypes has been introduced to replace the enum beginning with eVEHICLE_TYPE_DRIVE4W.
              • +
              • Minor api change for consistency: PxVehicleWheelGraphChannel has been introduced to replace the enum beginning with PxVehicleGraph::eCHANNEL_JOUNCE.
              • +
              • Minor api change for consistency: PxVehicleDriveGraphChannel has been introduced to replace the enum beginning with PxVehicleGraph::eCHANNEL_ENGINE_REVS.
              • +
              • Minor api change for consistency: PxVehicleGraphType has been introduced to replace the enum beginning with PxVehicleGraph::eGRAPH_TYPE_WHEEL.
              • +
              • PxVehicleUpdates now checks in checked and debug config that the wheel positioning of the driven wheels in a PxVehicleDrive4W obey the wheel ordering specified in PxVehicleDrive4WWheelOrder. Vehicles that are back-to-front or right-to-left are also allowed. A warning is issued if the check fails.
              • +
              • PxVehicleUpdates now checks in checked and debug config that the odd wheels of a PxVehicleDriveTank are either all to the left or all to the right of their even-wheeled complement, as specified in PxVehicleDriveTankWheelOrder. A warning is issued if the check fails.
              • +
              • To improve api consistency the arguments of the function PxVehicleDriveDynData::setAnalogInput(const PxReal analogVal, const PxU32 type) have been swapped so that it is now of the form PxVehicleDriveDynData::setAnalogInput(const PxU32 type, const PxReal analogVal).
              • +
              • Non-persistent wheel dynamics data (slip, friction, suspension force, hit data etc) has been moved out of the PxVehicleWheelsDynData class and is now recorded in a PxWheelQueryResult buffer that is passed as a function argument to the PxVehicleUpdates function.
              • +
              • PxVehicleWheels::isInAir() has been replaced with PxVehicleIsInAir(const PxVehicleWheelQueryResult& vehWheelQueryResults) to reflect the change to non-persistent data storage.
              • +
              • The origin of vehicles can now be shifted to stay in sync when the origin of the underlying PxScene is shifted. See PxVehicleShiftOrigin() for details.
              • +
              • PxVehicleWheels::setWheelShapeMapping and PxVehicleWheels::getWheelShapeMapping have been moved to PxVehicleWheelsSimData::setWheelShapeMapping and PxVehicleWheelsSimData::getWheelShapeMapping
              • +
              • PxVehicleWheels::setSceneQueryFilterData and PxVehicleWheels::getSceneQueryFilterData have been moved to PxVehicleWheelsSimData::setSceneQueryFilterData and PxVehicleWheelsSimData::getSceneQueryFilterData
              • +
              • PxVehicle4WEnable3WTadpoleMode and PxVehicle4WEnable3WDeltaMode now take an extra function argument: a non-const reference to a PxVehicleWheelsDynData.
              • +
              • The section "SI Units" in the vehicle guide has been updated to include the new functon PxVehicleWheelsSimData::setMinLongSlipDenominator.
              • +
              • PxVehicleTireData::mCamberStiffness has been replaced with PxVehicleTireData::mCamberStiffnessPerUnitGravity. PxVehicleTireData::mCamberStiffnessPerUnitGravity should be set so that it is equivalent to the old value of PxVehicleTireData::mCamberStiffness divided by the magnitude of gravitational acceleration.
              • +
              • PxVehicleComputeTireForceDefault has been removed from the public vehicle api. Custom tire shaders that call PxVehicleComputeTireForceDefault are best implemented by taking a copy of PxVehicleComputeTireForceDefault and calling the copy instead.
              • +
              +
            • Fixed:
            • +
                +
              • Sticky tire friction is now activated in the tire's lateral direction at the tire force application point when the velocity at the base of the tire has low longitudinal and low lateral components. Longitudinal sticky tire friction is unaffected and is still activated when the vehicle has low forward speed. This fixes a minor bug where vehicles positioned on a slope perpendicular to the slope's downwards direction can slowly drift down the slope.
              • +
              • Bugs in the suspension force and tire load computation have been fixed that affected handling when the car was upside down.
              • +
              • The tire load passed to the tire force computation is now clamped so that it never falls below zero.
              • +
              • A bug in the tank damping forces has now been fixed. Tanks now slow down more aggressively from engine and wheel damping forces.
              • +
              +
            + + +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.2.4

            + +

            April 2013

            + +

            What's New In NVIDIA PhysX 3.2.4

            + +
            +

            General

            +
              +
            • Note: PS3 platform specific changes can be found in the PS3 readme file.
            • +
            • Fixed a bug which caused actors to return wrong world bounds if the bounds minimum was above 10000 on any axis.
            • +
            • Reporting allocation names can now be enabled or disabled (see PxFoundation::setReportAllocationNames). When enabled, some platforms allocate memory through 'malloc'.
            • +
            • eEXCEPTION_ON_STARTUP is removed from PxErrorCode and it is no longer needed.
            • +
            • Added boilerplate.txt to the Tools folder. SpuShaderDump.exe and clang.exe require it.
            • +
            • PxWindowsDelayLoadHook.h has been moved from Include/foundation/windows to Include/common/windows.
            • +
            • PxScene::saveToDesc now reports the bounceThresholdVelocity value.
            • +
            • Fixed a bug in PxDefaultSimulationFilterShader: the value of the second filter constant in the collision filtering equation was ignored and instead the value of the first filter constant was used.
            • +
            • Fixed a crash bug in PCM collision.
            • +
            + + +

            Rigid Bodies

            +
              +
            • Forces applied to bodies (with PxRigidBody::addForce) that go to sleep in the subsequent update now have their applied forces cleared when the body is set to sleep to avoid them being applied in a later update when the body is once more awake. This bug broke the rule that forces applied with PxRigidBody::addForce do not persist beyond the next scene update.
            • +
            • Jittering of small spheres & capsules on very large triangles has been fixed.
            • +
            + +

            Cooking

            +
              +
            • PxCookingParams constructor is now marked as deprecated. PxToleranceScale is needed for PxCookingParams in order to perform additional triangle check during cooking. This triangle check will trigger warning if too huge triangles are used. This check will ensure better simulation stability.
            • +
            + +

            Scene Queries

            +
              +
            • Added PxScene::forceDynamicTreeRebuild() function to immediately rebuild the scene query structures.
            • +
            • Accuracy in sphere/capsule-vs-triangle overlap and sweep tests has been improved.
            • +
            + +

            Broad Phase

            +
              +
            • Fixed assert in debug mode that wrongly asserted when an overlap pair was removed with perfect equality between the min of one bound and the max of the other along at least one axis.
            • +
            + +

            Character controller

            +
              +
            • PxControllerFilterCallback has been added, to make CCT-vs-CCT filtering more flexible.
            • +
            • Fixed a bug where PxControllerBehaviorCallback was not called when a PxUserControllerHitReport was not defined.
            • +
            • PxObstacle notifications has been added to handle touched obstacles.
            • +
            • Touched PxObstacle has been replaced by touched ObstacleHandle.
            • +
            • PxObstacleContext::getObstacleByHandle has been added.
            • +
            • Touched actors scene is checked before ride on shape, to ensure valid touched shape.
            • +
            • Touched shape scene query flag is checked before ride on shape, to ensure valid touched shape.
            • +
            • Touched shape user CCT filtering is triggered before ride on shape, to ensure valid touched shape.
            • +
            • Box & capsule controllers' resize function now properly take the up direction into account
            • +
            + +

            Vehicles

            +
              +
            • Documented potential problem with PxVehicleWheelsDynData::getTireDrivableSurfaceType() and PxVehicleWheelsDynData::getTireDrivableSurfaceShape() whereby the pointer returned may reference a PxShape or PxMaterial that has been released in-between storing the pointer in PxVehicleUpdates and any subsequent query.
            • +
            • PxVehicleWheelsSimData::disableWheel has been documented in more detail. PxVehicleWheelsSimData::enableWheel has been added.
            • +
            • Fixed a bug where the denominator of the longitudinal slip calculation didn't take into account the value of PxTolerancesScale::length. This will only have an impact if PxTolerancesScale::length != 1.0f.
            • +
            • Fixed a bug where the engine torque would be incorrectly applied if PxTolerancesScale::length != 1.0f. This will only have an impact if PxTolerancesScale::length != 1.0f.
            • +
            + +

            Particles

            +
              +
            • Creating and immediately setting the position of particles failed and possibly crashed with GPU acceleration enabled. This would only happen after one or more simulation updates.
            • +
            + +
            + +


            Supported Platforms

            + +
            + Unchanged from from 3.2.3 except: +

            Development

            +
              +
            • Upgraded to Xcode 4.6
            • +
            +
            + +


            Known Issues And Limitations

            + +
            + Unchanged from from 3.2.3. +
            +
            + + + +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.2.3

            + +

            November 2012

            + +

            What's New In NVIDIA PhysX 3.2.3

            + +
            +

            General

            +
              +
            • Note: PS3 platform specific changes can be found in the PS3 readme file.
            • +
            • Quaternions passed through the API are now considered valid if their magnitude is between 0.99 and 1.01.
            • +
            • Fixed crash when running out of memory on creation of a triangle mesh.
            • +
            • For applications using floating point exceptions, the SDK will now mask or avoid exceptions arising from invalid floating point operations (inexact and underflow exceptions may still be generated).
            • +
            • Fixed a bug with recursive use of the PxScene read/write lock.
            • +
            • Fixed a shutdown bug with very short lived threads on Linux platforms.
            • +
            • PhysX version number in error messages now printed in hex for easier reading.
            • +
            • Fixed memory barrier and prefetch implementation for all posix based platforms (android, ios, osx, linux).
            • +
            + +

            Broad Phase

            +
              +
            • Fixed a broad phase crash bug that occurred when deleting shapes with bounds very far from the origin.
            • +
            + + +

            Collision Detection

            +
              +
            • Documentation of limits of PxShape counts has been added for affected platforms.
            • +
            • Made kinematics interact better with CCD.
            • +
            • Adding support for disabled contact response in CCD by respecting the dominance setting. In this case, CCD will emit events but will not alter the motion of the bodies.
            • +
            • Fixed potential crash in eENABLE_PCM codepath.
            • +
            + + +

            Rigid Bodies

            +
              +
            • Fixed bug in force based contact reports. An assert could occur when PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS was set and PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND was not set.
            • +
            • Twist Limit range is documented for revolute and D6 joints, and validated.
            • +
            • Reduced the number of SDK allocations when using CCD.
            • +
            + + +

            Scene Queries

            +
              +
            • Raycasts against heighfields now return correct actual mesh index, which can be used for getTriangle().
            • +
            • Fixed bug that caused scene queries to miss hits for static rigid bodies that got moved around (after having been added to the scene).
            • +
            • Fixed rebuilding the dynamic structure properly when used for static rigid bodies.
            • +
            • Fixed a rare crash in heightfield raycast code.
            • +
            + +

            Character controller

            +
              +
            • A new runtime tessellation feature has been added, that can help reducing FPU accuracy issues in the sweep tests.
            • +
            + +

            Convex hulls

            +
              +
            • Zero-sized convex hull data double delete detection fix.
            • +
            + +

            Vehicles

            +
              +
            • Vehicles with rigid body actors that are asleep and also have no acceleration or steer inputs are treated as though they are asleep too; that is, they are bypassed completely in the PxVehicleUpdates function. Vehicles with sleeping rigid body actors but with non-zero acceleration or steer inputs are processed as normal in PxVehicleUpdates and will automatically have their rigid body actor woken up.
            • +
            • New function PxVehicleSetUpdateMode to allow PxVehicleUpdates to select between applying accelerations to vehicle rigid bodies or immediate updating of their velocity.
            • +
            + +

            Particles

            +
              +
            • Fixed a non-deterministic crash appearing with rigid bodies using CCD and gpu particles in the same scene.
            • +
            + +

            Physx Visual Debugger

            +
              +
            • Material release events are now correctly sent to PVD.
            • +
            + +

            RepX

            +
              +
            • Add more RepX class information in PhysXAPI document.
            • +
            + +
            + +


            Supported Platforms

            + +
            +

            Runtime

            +
              +
            • Apple iOS
            • +
            • Apple Mac OS X
            • +
            • Google Android (version 2.2 or later for SDK, 2.3 or later required for samples)
            • +
            • Linux (tested on Ubuntu)
            • +
            • Microsoft Windows XP or later (NVIDIA Driver ver 295.73 or later is required for GPU acceleration)
            • +
            • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
            • +
            • Microsoft XBox 360
            • +
            • Sony Playstation 3
            • +
            +

            Development

            +
              +
            • Microsoft Windows XP or later
            • +
            • Microsoft Visual Studio 2008, 2010
            • +
            • Xcode 4.5
            • +
            +
            + +


            Known Issues And Limitations

            + +
            + Unchanged from from 3.2.2. +
            +
            + + + +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.2.2

            + +

            October 2012

            + +

            What's New In NVIDIA PhysX 3.2.2

            + +
            +

            General

            +
              +
            • Note: PS3 platform specific changes can be found in the PS3 readme file.
            • + +
            • Added Microsoft Windows RT (formerly known as Windows on ARM) support.
            • +
            • Suspended Sony Playstation Vita support.
            • +
            • PxScene now exposes methods to make multithreaded sharing of scenes easier, see PxSceneFlags::eREQUIRE_RW_LOCK for details.
            • +
            • Enabled Win64 DEBUG build to use SIMD enabled code path.
            • +
            • Fixed bug in quaternion to axis/angle routine which failed on negative w values.
            • +
            • Fixed crash when using ConvX on a PxCollection with external references.
            • +
            • Fixed a spurious overlapping read/write error report when using simulation call backs in checked builds.
            • +
            • The bounce threshold velocity can be set at run-time with PxScene::setBounceThresholdVelocity. Likewise, it can be queried with PxScene::getBounceThresholdVelocity
            • +
            • Fixed return value of Ps::atomicExchange for POSIX based platforms: Linux, Android, IOS and OSX
            • +
            • PxGeometryQuery::computePenetration() has been added, to compute the Minimum Translational Distance between geometry objects.
            • +
            + +

            Broad Phase

            +
              +
            • Fixed a broad phase crash bug.
            • +
            + +

            Collision Detection

            +
              +
            • Collision detection now more robust when confronted with ill-conditioned scenarios.
            • +
            • Fixed crash when SDK is unable to create more contact pairs. Now a warning is emitted and the contacts are ignored.
            • +
            + +

            Rigid Bodies

            +
              +
            • Improved the numerical stability of articulations.
            • +
            • The target pose of a kinematically controlled dynamic actor can now get extracted through PxRigidDynamic::getKinematicTarget().
            • +
            • The new flag PxRigidDynamicFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES makes scene queries use the target pose of a kinematically controlled dynamic actor instead of the actual pose.
            • +
            • Fixed PxConstraintFlag::eVISUALIZATION having no effect.
            • +
            • Fixed bug that caused sleep/wake-up notification events to get lost.
            • +
            • Fixed a bug where sleeping objects were not waking up properly.
            • +
            • Fixed potential assert when switching a rigid body between kinematic and dynamic with contact reports enabled.
            • +
            • Fixed a bug where CCD didn't consider the scaling transform for triangle meshes.
            • +
            • Fixed a potential crash bug when PxConstraintFlag::ePROJECTION gets raised after a joint (that connects to other projecting joints) has been created.
            • +
            • Fixed a bug that resulted in joint breakage being reported every frame for any broken joint
            • +
            • Added PxArticulationDriveCache for applying an impulse to an entire articulation
            • +
            • fixed various crash bugs when sleeping articulations were removed from the scene and re-added +
            • +
            + +

            GPU Physics

            +
              +
            • Fixed a possible out of bounds array access in GPU particle collision code.
            • +
            • Updated the GPU PhysX Visual Indicator to allow APEX to hook into it.
            • +
            • Fixed sample particles crash when there is a cuda kernel error.
            • +
            • Upgraded GPU tech to CUDA 4.2.
            • +
            + +

            Scene Queries

            +
              +
            • PxSceneQueryHit::faceIndex is now filled in for sweeps against convex meshes.
            • +
            • Added support for sphere and capsule shaped heightfield overlap queries.
            • +
            • Added an inflation parameter to all sweep queries in PxGeometryQuery, PxBatchQuery, and PxScene. This can be used to maintain a minimum distance between objects when moving them using sweeps.
            • +
            • Made sure that raycast multiple will include the closest triangle in the results even if the output cannot hold all the triangles that are hit.
            • +
            • Fixed swept sphere against capsule not properly testing for initial overlap.
            • +
            • Fixed the normal vector returned from sweeps being sometimes negated.
            • +
            • Fixed a scenario where raycasting could miss an actor after the user has moved it using setGlobalPose().
            • +
            • Fixed swept convex against plane sometimes reporting false hits
            • +
            • Fixed swept/overlap/raycast with convexes that don't have identity scale rotation.
            • +
            • Fixed a culling bug for box-triangle mesh sweep.
            • +
            + +

            Convex hulls

            +
              +
            • Convex hull is rejected if it has less than 4 polygons.
            • +
            • Additional convex hull check has been added to detect open volumes.
            • +
            + +

            Triangle meshes

            +
              +
            • Added triangle mesh flags for 16bit indices and adjacency information.
            • +
            • Fixed adjacency information order for getTriangle with triangle meshes to respect the vertex order.
            • +
            + +

            HeightFields

            +
              +
            • Fixed bug where capsules would bounce as they roll across heightfield edges.
            • +
            • Fixed bug where spheres would bounce as they roll across heightfield vertices.
            • +
            • Added adjacency information for getTriangle with height fields.
            • +
            + +

            Particles

            +
              +
            • Fixed triangle mesh shapes with ePARTICLE_DRAIN colliding with particles.
            • +
            • Fixed crash with GPU particles when a lot of grid cells get occupied and vacated within one time step.
            • +
            + +

            Character Controller

            +
              +
            • The PxControllerBehaviorFlag::eCCT_USER_DEFINED_RIDE flag has been added.
            • +
            • Fixed character controller not walking up steps properly if they are not exactly 90 degrees vertical.
            • +
            • Fixed a bug where the character controller was not able to move up slopes when using a small step offset setting.
            • +
            • Fixed a bug where the character controller could rise off the ground when blocked by a step.
            • +
            • Fixed a bug where the character controller could rise off the ground when hitting another character controller.
            • +
            • Fixed a bug where releasing a shape of a touching actor and then releasing the character controller would crash. Releasing shapes of actors in touch may still lead to crashes in other situations. PxController::invalidateCache() can be used to work around these situations.
            • +
            +

            CCD

            +
              +
            • Fixed culling bug in CCD sweeps with meshes with transforms that caused contacts to be missed
            • +
            + +

            Vehicles

            +
              +
            • The vehicle sdk used to make the assumption that wheels 0 and 1 (the front wheels) of a PxVehicleDrive4W responded positively to the input steering wheel angle, while wheels 2 and 3 (the rear wheels) responded in the opposite direction to that of the input steering wheel angle. A consequence of this assumed behaviour was the restriction that PxVehicleWheelData::mMaxSteer was limited to positive values. This restriction has now been relaxed with the caveat that PxVehicleWheelData::mMaxSteer must be in range (-Pi/2, Pi/2). It is now possible to turn each wheel positively or negatively relative to the input steering wheel angle by choosing positive or negative values for PxVehicleWheelData::mMaxSteer. Ackermann steer correction might result in the front and rear wheels competing against each other if the rear and front all steer in the same direction relative to the input steering wheel angle. If this is the case it will be necessary to set the Ackermann accuracy to zero.
            • +
            • It is now possible to set the engine rotation speed (PxVehicleDriveDynData::setEngineRotationSpeed), the rotation speed of each wheel (PxVehicleWheelsDynData::setWheelRotationSpeed) and the rotation angle of each wheel (PxVehicleWheelsDynData::setWheelRotationAngle). The default values for each of these properties remains zero.
            • +
            • Wheel contact reporting has been improved with the addition of a number of query functions to the PxVehicleWheelsDynData class. These are getTireDrivableSurfaceContactPoint, getTireDrivableSurfaceContactNormal, getTireLongitudinalDir, getTireLateralDir, getSuspensionForce, getTireDrivableSurfaceShape.
            • +
            • It is now possible to store a userData pointer per wheel. This allows, for example, each wheel to be associated with a game object. The relevant functions are PxVehicleWheelsDynData::setUserData and PxVehicleWheelsDynData::getUserData.
            • +
            • The default behavior of PxVehicleWheels::setWheelShapeMapping has changed. Previously, default values were automatically assigned to each wheel at construction so that the ith wheel was mapped to the ith body shape. This, however, made the assumption that there was a wheel shape for each wheel, which is not always true. As a consequence, the default value is now -1, meaning any mapping between body shape and wheel has to be explictily made by calling setWheelShapeMapping.
            • +
            • It is now possible to query the mapping between wheel and body shape with PxVehicleWheels::getWheelShapeMapping.
            • +
            • It is now possible to query the tire shader data that has been applied to each wheel with PxVehicleWheelsDynData::getTireForceShaderData.
            • +
            • The helper function PxVehicleComputeSprungMasses has been added to aid the setup of the suspension sprung masses from the rigid body centre of mass and wheel center coordinates.
            • +
            • The scene query filter data applied to the suspension raycasts was previously taken from the filter data of the associated body shape. This makes the assumption of a body shape per wheel, which is not always true. As a consequence, the filter data must be explictly set per wheel by calling PxVehicleWheels::setSceneQueryFilterData. The filter data can be queried with PxVehicleWheels::getSceneQueryFilterData.
            • +
            • Sub-stepping of the vehicle update can now be applied per vehicle with PxVehicleWheelsSimData::setSubStepCount.
            • +
            • PxVehicleDrivableSurfaceToTireFrictionPairs has been modified so that the dictionary of material pointers can be updated without the necessity of further allocation. The create function has been replaced with separate allocate and setup functions.
            • +
            • A new vehicle type PxVehicleNoDrive has been added to provide a close approximation to backwards compatibility with the api of the 2.8.x NxWheelShape.
            • +
            + + +

            Visual Remote Debugger

            +
              +
            • Added PVD compatible profile zones for batched queries.
            • +
            • Added the ability to capture and inspect scene queries in PVD.
            • +
            • SDK will now flush the pvd connection stream immediately after cloth or cloth fabric is created or released.
            • +
            • Fixed the PVD support for articulations.
            • +
            • Fixed PVD rendering wrong constraint limits.
            • +
            + + +

            Documentation

            +
              +
            • Wrong statement in PxRigidStatic::release() has been corrected. Static rigid bodies do wake up touching dynamic rigid bodies on release.
            • +
            • Wrong statement in PxShape::setFlag() has been corrected. It is a valid operation to clear all flags.
            • +
            • Retroactively added more detail about changes to 3.2.1 release notes below.
            • +
            +
            + +


            Supported Platforms

            + +
            +

            Runtime

            +
              +
            • Apple iOS
            • +
            • Apple Mac OS X
            • +
            • Google Android (version 2.2 or later for SDK, 2.3 or later required for samples)
            • +
            • Linux (tested on Ubuntu)
            • +
            • Microsoft Windows XP or later (NVIDIA Driver ver 295.73 or later is required for GPU acceleration)
            • +
            • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
            • +
            • Microsoft XBox 360
            • +
            • Sony Playstation 3
            • +
            +

            Development

            +
              +
            • Microsoft Windows XP or later
            • +
            • Microsoft Visual Studio 2008, 2010
            • +
            • Xcode 4.2
            • +
            +
            + + + + +


            Known Issues And Limitations

            + +
            +

            General

            +
              +
            • Memory leaks might get reported when using the debug versions of malloc and free together with the debug version of PhysX on Microsoft Windows platforms with Visual Studio 2010. This is caused by a bug in the Visual Studio 2010 runtime libraries. If such a leak appears immediately after releasing PhysX and all its components, please contact us for information about workarounds.
            • +
            • Use of articulations may require an increase of 64K in the stack size for threads making API calls and engine worker threads
            • +
            +
            + +
            + Please also see the previous lists from 3.2.1 and earlier. +
            + + + +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.2.1

            + +

            June 2012

            + +

            What's New In NVIDIA PhysX 3.2.1

            + +
            +

            General

            +
              +
            • Note: PS3 platform specific changes can be found in the PS3 readme file.
            • +
            • Added GRB hooks for APEX 1.2.1.
            • +
            • Some incorrect usages of __restrict have been fixed.
            • +
            • A crash when the user's code had FPU exceptions enabled has been fixed.
            • +
            • A rare crash on Win64 has been fixed.
            • +
            • Removed no longer needed RapidXML library from distribution.
            • +
            • Binary serialization can now save the names of actors and shapes.
            • +
            • Removed a RepXUtility.h reinterpret_cast compile warning that happened on some platforms.
            • +
            • Fixed a spurious overlapping read/write error report when using simulation call backs in checked builds.
            • +
            • Fixed a bug in PxBinaryConverter when processing PxConvexMesh and PxMaterial assets.
            • +
            • Fixed bug in implementations of array accessors (PxPhysics::getScenes(), GuMeshFactory::getTriangleMeshes(), GuMeshFactory::getConvexMeshes(), GuMeshFactory::getHeightFields(), PxAggregate::getActors(), PxScene::getAggregates(), PxScene::getArticulations(), PxScene::getConstraints() ) when startIndex > bufferSize.
            • +
            • Reduced the number of libraries provided for game consoles. The following core libraries are included in the PhysX3 library and are not available separately anymore: LowLevel, LowLevelCloth, PhysX3Common, PhysXProfileSDK, PhysXVisualDebuggerSDK, PvdRuntime, PxTask, SceneQuery, SimulationController.
            • +
            + +

            Documentation

            +
              +
            • Clarified documentation regarding use of eSEND_SLEEP_NOTIFIES flag.
            • +
            • Clarified documentation regarding using different CRT libraries.
            • +
            • Removed some confusing statements about meta data from the documentation.
            • +
            • Updated PsPool, PsSort so they can use the user allocator.
            • +
            + +

            Mesh Cooking

            +
              +
            • A warning message about negative volumes in the convex mesh cooker could cause a crash.
            • +
            • Fixed failure to create valid convex hull in cooking when using PxConvexFlag::eINFLATE_CONVEX.
            • +
            • The convex mesh cooking has been made more robust and properly outputs some error messages that were previously missing.
            • +
            • Fixed crash bug in x64 version of ClothEdgeQuadifier.
            • +
            • Adjacency information option for TriangleMeshes. This is controlled using the PxCookingParams::buildTriangleAdjacencies flag, and returned if available by PxMeshQuery::getTriangle().
            • +
            + +

            Broad Phase

            +
              +
            • The sdk gracefully handles the case of more than 65536 broadphase pairs and reports a warning that some contacts will be dropped in the event that this limit is exceeded. This affects all platforms except win32/win64/linux/linux64, which support a maximum number of 4294967296 pairs.
            • +
            + +

            Collision Detection

            +
              +
            • Fixed a memory corruption bug in heightfield code.
            • +
            • Fixed a bug in sphere vs mesh contact generation that could result in bad normals.
            • +
            • Added a flag to enable or disable contact caching, to permit users to make a performance vs memory tradeoff.
            • +
            • Fixed a crash bug in ContactReport cleanup code.
            • +
            + +

            Rigid Bodies

            +
              +
            • The simultaneous raising of both the PxShapeFlag::eSIMULATION_SHAPE and PxShapeFlag::eTRIGGER_SHAPE flags is now explicitly forbidden by the sdk. If any of the two is raised then any attempt to raise the other is rejected by the sdk and an error is passed to the error stream.
            • +
            + +

            Articulations

            +
              +
            • The API stubbed in 3.2.0 for applying an impulse to an entire articulation is now implemented.
            • +
            + +

            GPU Physics

            +
              +
            • Much more specific error messages for CUDA compute capability related failures.
            • +
            • Added SM35 support for GK110 GPUs.
            • +
            • The CUDA contexts provided by the gpu dispatcher can now be shared across scenes.
            • +
            • Fixed a possible out of bounds array access in GPU particle collision code.
            • +
            + + +

            Scene Queries

            +
              +
            • Resolved poor GJK sweep convergence.
            • +
            • Batched queries accept sphere geometry for sweeps.
            • +
            • Optimized performance of raycasts.
            • +
            • An internal limit of 65536 objects in the scene-query subsytem has been lifted.
            • +
            • Fixed a bug where raycasts that sliced through two adjoining heightfields did not return correct results.
            • +
            • Fixed a crash bug in PxFindOverlapTriangleMeshUtil when doing a sphere overlap query against a heightfield.
            • +
            + + +

            Cloth

            +
              +
            • PxCloth::setMotionConstraints() now works with NULL parameter.
            • +
            + +

            Character Controller

            +
              +
            • PhysX CCT code no longer sets PxShape::userData.
            • +
            • Intersection of pairs of CCTs now uses the supplied filter data and the supplied callback prefilter. The callback postfilter is not yet hooked up.
            • +
            • A bug has been fixed whereby the filterData was ignored in one of the scene queries initiated by the PhysX CCT code.
            • +
            • Introduced a more automatic mechanism for invelidating the character controller's scene cache. As part of this, PxController::reportSceneChanged() was replaced with PxController::invalidateCache().
            • +
            • Added helper functions PxController::get/setFootPosition() to let user specify the bottom point of the character controller, rather than the center.
            • +
            • A new helper function, PxController::resize(), has been added to facilitate character controller resizing.
            • +
            • PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT is not the default behavior anymore.
            • +
            • The slope limit is only observed when walking on static convex meshes, static triangle meshes, static boxes and static heightfields. The slope limit is not observed when walking on dynamic or kinematic rigid bodies or static capsules or static spheres. This partially fixes a bug where the slope limit was inadvertently considered for shapes attached to dynamic rigid bodies and inadvertently ignored for boxes attached to static shapes.
            • +
            + +

            Vehicles

            +
              +
            • The vehicle sdk now reports an error to the error stream and exits from PxVehicleUpates if PxInitVehicleSdk has not been called in advance. This avoids a divide-by-zero error that can arise if the vehicle sdk has not been initialised correctly.
            • +
            + +

            Visual Debugger

            +
              +
            • Releasing of cloth fabrics is reported to the VRD.
            • +
            • Ext::Joint::setActors() call now reported to PVD.
            • +
            • Fixed crash bug when removing an aggregate containing a PxArticulation, while PVD is running.
            • +
            +
            + +


            Supported Platforms

            + +
            + Unchanged from from 3.2. +
            + +


            Known Issues And Limitations

            + +
            + Unchanged from from 3.2. +
            + + + +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.2

            + +

            December 2011

            + +

            What's New In NVIDIA PhysX 3.2

            + +
            +

            General

            +
              +
            • Three new sample applications: SampleCharacterCloth (character with cloth cape and cloth flags), SampleBridges (character controller walking on dynamic bridges and moving platforms), SampleCustomGravity (character controller with arbitrary up vector).
            • +
            • On Windows, the PxFoundation instance is now a process wide singleton and part of the new PhysX3Common.dll library
            • +
            • PxCreatePhysics() does not create a PxFoundation instance any longer. The PxFoundation instance has to be created in advance through PxCreateFoundation().
            • +
            • Calls to PxCreatePhysics() are not valid anymore if a PxPhysics instance already exists.
            • +
            • If profiling information should be sent to the PhysX Visual Debugger, a PxProfileZoneManager instance has to be provided when creating the PxPhysics instance.
            • +
            • The version number constant PX_PUBLIC_FOUNDATION_VERSION has been replaced with PX_PHYSICS_VERSION. Both PxFoundation and PxPhysics use the same version number now.
            • +
            • The API now distinguishes between input and output stream types.
            • +
            • Added mechanism to reduce code size by not linking optional components. See PxCreateBasePhysics() and the PxRegister*() functions.
            • +
            • Added getConcreteTypeName() to API classes to provide run time type information.
            • +
            • Added PxScene::getTimestamp() to retrieve the simulation counter.
            • +
            • PxGetFoundation has been moved to PxGetFoundation.h
            • +
            • Changed the functions PxPhysics::releaseUserReferences(), releaseCollection(), addCollection() and releaseCollected() to now take a reference rather than a pointer.
            • +
            • The signature of PxCreatePhysics has changed: The Foundation SDK instance must be passed in explicitly. One can also hook profiling information by passing a PxProfileZoneManager.
            • +
            • Platform conversion for serialized data has been moved from the ConvX command line tool to the PxBinaryConverter interface in the cooking library
            • +
            • contact data block allocation now provides statistics on usage and max usage
            • +
            • On all platforms except PS3, contact data blocks can be progressively allocated
            • +
            • PxExtensionVisualDebugger has been renamed to PxVisualDebuggerExt, PxExtensionsConnectionType renamed to PxVisualDebuggerConnectionFlag
            • +
            • Default implementations of memory and file streams added in PxDefaultStreams.h
            • +
            • Renamed PxPhysics::getMetaData() to ::PxGetSDKMetaData().
            • +
            • Scene::simulate() now takes a memory block which is used for allocation of temporary data during simulation
            • +
            • On Windows, CudaContextManagerDesc support appGUID now. It only works on release build. If your application employs PhysX modules that use CUDA you need to use a GUID so that patches for new architectures can be released for your game.You can obtain a GUID for your application from Nvidia.
            • +
            + +

            Rigid Bodies

            +
              +
            • Introduced a new contact generation mode, see eENABLE_PCM. Note that this is an experimental feature that still shows simulation artifacts in some scenarios.
            • +
            • Introduced two new friction simulation modes, see eENABLE_ONE_DIRECTIONAL_FRICTION and eENABLE_TWO_DIRECTIONAL_FRICTION.
            • +
            • Introduced a new scene query flag PxSceneQueryFlag::eINITIAL_OVERLAP_KEEP to control how initial overhaps are treated in scene queries.
            • +
            • Per-triangle materials have been implemented.
            • +
            • Changes to material properties are automatically reflected in contact resolution.
            • +
            • New helper methods to compute mass properties for a dynamic rigid body taking per shape density/mass values into account (see documentation on PxRigidBodyExt for details).
            • +
            • A new set of methods for overlap, sweep and raycast tests based on PxGeometry objects has been introduced. See documentation on PxMeshQuery and PxGeometryQuery for details).
            • +
            • + The contact report API has changed (for details see the documentation on PxSimulationEventCallback::onContact()). Among the changes are: +
                +
              • Reports only get sent for shape pairs which request them. Previously, reports were sent for an actor pair even if the requested shape pair event was not triggered (for example because other shapes of the same actors started colliding etc.)
              • +
              • The following PxPairFlags have been removed eNOTIFY_CONTACT_FORCES, eNOTIFY_CONTACT_FORCE_PER_POINT, eNOTIFY_CONTACT_FEATURE_INDICES_PER_POINT. Forces and feature indices are now always provided if applicable.
              • +
              • It is much easier now to skip shape pairs or contact point information when traversing the contact report data.
              • +
              • The memory footprint of contact reports has been reduced.
              • +
              +
            • +
            • The members featureIndex0/1 of PxContactPoint have been renamed to internalFaceIndex0/1 for consistency.
            • +
            • For trigger reports, the eNOTIFY_TOUCH_PERSISTS event has been deprecated and will be removed in the next release. For performance and flexibility reasons it is recommended to use eNOTIFY_TOUCH_FOUND and eNOTIFY_TOUCH_LOST only and manage the persistent state separately.
            • +
            • Added PxConstraintVisualizer interface and code to visualize joint frames and limits.
            • +
            • Improved PxBatchQuery API.
            • +
            • PxPhysics::getProfileZoneManager() now returns a pointer rather than a reference.
            • +
            • PxRigidDynamic::moveKinematic() has been renamed to setKinematicTarget() to underline its precise semantics.
            • +
            • Added new function PxShape::getGeometry and class PxGeometryHolder to improve Geometry APIs.
            • +
            • PxCreatePlane now takes a PxPlane equation as a parameter. Note that the interpretation of the distance value is negated relative to 3.1
            • +
            • Added new actor creation helpers PxCloneStatic, PxCloneDynamic, PxScaleActor.
            • +
            • Added new functions PxTransformFromSegment, PxTransformFromPlaneEquation to simplify creation of planes and capsules.
            • +
            • added PxJoint::getConstraint() to access the underlying constraint object, from which the constraint force can be read
            • +
            • + Some methods of PxAggregate have been renamed for consistency or replaced for extended functionality. +
                +
              • getMaxSize() is now called getMaxNbActors().
              • +
              • getCurrentSize() is now called getNbActors().
              • +
              • getActor() has been replaced by getActors() which copies the actor pointers to a user buffer.
              • +
              +
            • +
            • Added support for kinematic triangle meshes, planes and heighfields.
            • +
            + +

            Scene queries

            +
              +
            • Dynamic AABBTree has been set as the default dynamic pruning structure.
            • +
            + +

            Particles

            +
              +
            • Removed descriptors from particle API: The properties maxParticles and PxParticleBaseFlag::ePER_PARTICLE_REST_OFFSET need to be specified when calling PxPhysics::createParticleSystem() and createParticleFluid(). All other properties can be adjusted after creation through set methods.
            • +
            + +

            Cloth

            +
              +
            • Added convex collision shapes, see PxCloth::addCollisionConvex()
            • +
            • Added friction support, see PxCloth::setFrictionCoefficient()
            • +
            • Added angle based bending constraints, see PxClothPhaseSolverConfig::SolverType::eBENDING
            • +
            • Added separation constraints, a spherical volume that particles should stay outside of, see PxCloth::setSeparationConstraints()
            • +
            • Added drag, see PxCloth::setDragCoefficient()
            • +
            • Added inertia scaling, controls how much movement due to PxCloth::setTargetPose() will affect the cloth
            • +
            • Added support for setting particle previous positions, see PxCloth::setParticles()
            • +
            • Added controls for scaling particle mass during collision, this can help reduce edge stretching around joints on characters, see PxCloth::setCollisionMassScale()
            • +
            • Particle data is now copied asynchronously from the GPU after simulate (rather than on demand)
            • +
            • Improved fabric layout, you can now share fabric data across multiple phases to reduce memory usage, see PxClothFabric
            • +
            • Fixed bug in collision when capsules are tapered at a slope > 1
            • +
            + +

            Vehicles

            +
              +
            • Added PxVehicleDriveTank, a vehicle class that enables tank behaviors.
            • +
            • Support for vehicles with more than 4 wheels, see PxVehicleDrive4W, PxVehicleDriveTank.
            • +
            • Significant refactor of vehicle api to allow further types of vehicle to be added.
            • +
            • Americal English spelling used in vehicle api.
            • +
            • PxVehicle4W replaced with PxVehicleDrive4W, PxVehicle4WSimulationData replaced with PxVehicleDriveSimData4W.
            • +
            • Removal of scene query helper functions and structs: PxVehicle4WSceneQueryData, PxVehicle4WSetUpSceneQuery, PxWheelRaycastPreFilter, PxSetupDrivableShapeQueryFilterData, PxSetupNonDrivableShapeQueryFilterData, PxSetupVehicleShapeQueryFilterData. See SampleVehicle_SceneQuery.h for their implementation in SampleVehicle.
            • +
            • PxVehicle4WSimpleSetup and PxCreateVehicle4WSimulationData have been removed and replaced with default values in vehicle components, see PxVehicleComponents.h.
            • +
            • PxVehicle4WTelemetryData has been replaced with PxVehicleTelemetryData, a class that supports vehicles with any number of wheels, see PxVehicleTelemetryData
            • +
            • PxVehicleDrivableSurfaceType no longer stored in PxMaterial::userData. A hash table of PxMaterial pointers is instead used to associate each PxMaterial with a PxVehicleDrivableSurfaceType, see PxVehicleDrivableSurfaceToTireFrictionPairs.
            • +
            • PxVehicleTyreData::mLongitudinalStiffness has been replaced with PxVehicleTireData::mLongitudinalStiffnessPerUnitGravity, see PxVehicleTireData.
            • +
            • Tire forces now computed from a shader to allow user-specified tire force functions, see PxVehicleTireForceCalculator.
            • +
            • Added helper functions to quickly configure 3-wheeled cars, see PxVehicle4WEnable3WTadpoleMode, PxVehicle4WEnable3WDeltaMode.
            • +
            + +

            Serialization

            +
              +
            • Changed the functions PxPhysics::releaseUserReferences(), releaseCollection(), addCollection() and releaseCollected() to now take a reference rather than a pointer.
            • +
            • Platform conversion for serialized data has been moved from the ConvX command line tool to the PxBinaryConverter interface in the cooking library.
            • +
            • Changed some functions in RepXUtility.h and RepX.h to take a reference rather than a pointer.
            • +
            + + +

            What we removed:

            +
              +
            • Deformables have been removed. Use the optimized solution for clothing simulation instead (see documentation on PxCloth for details).
            • +
            • PxSweepCache was replaced with PxVolumeCache.
            • +
            • PVD is no longer enabled in the release build.
            • +
            • Removed anisotropic friction.
            • +
            • Removed the CCD mode eSWEPT_CONTACT_PAIRS.
            • +
            • PxActorDesc has been removed.
            • +
            • The ConvX tool has been removed.
            • +
            • Removed empty default implementations of functions in PxSimulationEventCallback for consistency and because it can create bugs in user code if function prototypes change between releases. Users must now supply (eventually blank) implementations for all functions.
            • +
            • Octree and quadtree pruning structures have been removed.
            • +
            + +

            Fixed Bugs

            +
              +
            • PxScene::getActors() might not work properly when the startIndex parameter is used.
            • +
            • Improved the doc-comment of PxConvexMesh::getMassInformation().
            • +
            • RepX instantiation can lose all internal references when addOriginalIdsToObjectMap is false.
            • +
            • PxSetGroup crashed when used on a compound.
            • +
            • PhysXCommon.dll can be delay loaded.
            • +
            • ContactReportStream can now handle huge report numbers and size (resize-able flag) can be set in PxSceneDesc.h.
            • +
            • Fixed assert in sweep tests.
            • +
            • Concurrent read/write operations during a PxScene::fetchResults() call were not detected properly and no warning message got sent in checked builds. Forbidden write operations during callbacks triggered by PxScene::fetchResults() (contact/trigger reports etc.) were not covered either.
            • +
            • Fixed crash bug that occurred during collision detection when more than 16K of contact data was generated. Contacts that generate more than 16K of contact data are now fully supported.
            • +
            • Fixed crash bug when PhysX Visual Debugger is connected and an object gets modified and then released while the simulation is running.
            • +
            + +
            + +


            Supported Platforms

            + +
            +

            Runtime

            +
              +
            • Apple iOS
            • +
            • Apple Mac OS X
            • +
            • Google Android (version 2.2 or later for SDK, 2.3 or later required for samples)
            • +
            • Linux (tested on Ubuntu)
            • +
            • Microsoft Windows XP or later (NVIDIA Driver ver 295.73 or later is required for GPU acceleration)
            • +
            • Microsoft XBox 360
            • +
            • Sony Playstation 3
            • +
            • Sony Playstation Vita
            • +
            +

            Development

            +
              +
            • Microsoft Windows XP or later
            • +
            • Microsoft Visual Studio 2008, 2010
            • +
            • Xcode 4.2
            • +
            +
            + +


            Known Issues And Limitations

            + +
            + +

            Binary Serialization

            +
              +
            • Meta data generation for PxParticleFluid and PxParticleSystem serializables currently fails.
            • +
            • For collections that contain jointed rigid bodies all corresponding joints need to be added as well, otherwise deserialization will fail. +
            +

            Rigid Body Simulation

            +
              +
            • Capsules and spheres can struggle to come to rest on even perfectly flat surfaces. To ensure these objects come to rest, it is necessary to increase angular damping on rigid bodies made of these shapes. In addition, flagging the capsule/sphere's material with physx::PxMaterialFlag::eDISABLE_STRONG_FRICTION can help bring these shapes to rest.
            • +
            +

            Character Cloth Sample

            +
              +
            • An NVIDIA GPU with Compute Capability 2.0 or higher is required for GPU accelerated simulation in the SampleCharacterCloth sample, if no such device is present then simulation will be performed on the CPU.
            • +
            • Note that this is not a general SDK requirement, the clothing SDK supports cards with Compute Capability < 2.0 but with limitations on mesh size.
            • +
            +

            Character Controller

            +
              +
            • Releasing shapes of actors that are in touch with a character controller may lead to crashes. Releasing whole actors doesn't lead to the same problems. PxController::invalidateCache() can be used to work around these issues.
            • +
            + +
            + +
            + Please also see the previous lists from 3.1.1 and earlier. +
            + + + +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.1.2

            + +

            December 2011

            + +

            What's New In NVIDIA PhysX 3.1.2

            + +
            +

            General

            +
              +
            • Fixed wrong write/read clash checks.
            • +
            • Removed some compiler warnings from public header files.
            • +
            • Fixed PxScene::getActors() returning wrong actors when a start index is specified.
            • +
            + + +

            Rigid Bodies

            +
              +
            • Fixed broken joint projection in connection with kinematics.
            • +
            • Fixed inaccurate normals returned from height field scene queries.
            • +
            • Fixed a crash when the geometry of a shape changes and then the actor gets removed from the scene while the simulation is running.
            • +
            • Fixed a crash when re-adding scene-query shape actors to scene.
            • +
            + + +

            Particles

            +
              +
            • Fixed crash bug in particle simulation code on GPU.
            • +
            + + +

            Cloth

            +
              +
            • Fixed a crash when GPU fabrics are shared between cloths.
            • +
            • Fixed a hang in cloth fiber cooker when handed non-manifold geometry.
            • +
            + +

            Samples

            +
              +
            • Fixed SampleVehicles doing an invalid write.
            • +
            • Fixed SampleVehicle jitter in profile build.
            • +
            + +
            + +


            Supported Platforms (available in separate packages)

            + +
            + Unchanged from from 3.1.1. +
            + +


            Known Issues And Limitations

            + +
            + Unchanged from from 3.1. +
            + + + +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.1.1

            + +

            November 2011

            + +

            What's New In NVIDIA PhysX 3.1.1

            + +
            +

            General

            +
              +
            • Ported samples to Linux.
            • +
            • Fixed crash bug in ConvX.
            • +
            • Fixed crash bug in the allocator code of PXC_NP_MEM_BLOCK_EXTENSIBLE.
            • +
            • Fixed crash bug when connected to PVD on various platforms.
            • +
            • Fixed bogus asserts due to overly strict validation of quaternions.
            • +
            • Fixed one frame lag in PVD scene statistics.
            • +
            • Fixed a number of OSX PVD sockets issues.
            • +
            • Fixed SampleSubmarine code that violated concurrent read/writes restriction.
            • +
            • Added warnings about read/write hazards to the checked build.
            • +
            • Fixed RepX not reading joint properties.
            • +
            • Fixed support for concurrent scene queries.
            • +
            • Fixed PhysX GPU Visual Indicator support.
            • +
            • Made it more clear in documentation that simulate(0) is not allowed.
            • +
            + +

            Rigid Bodies

            +
              +
            • eNOTIFY_TOUCH_LOST trigger events do now get reported if one of the objects in contact gets deleted (see documentation of PxTriggerPair for details).
            • +
            • Dynamic rigid bodies with trigger shapes only do not wake up other touching bodies anymore.
            • +
            • Added lost touch events for trigger reports when objects get deleted.
            • +
            • Fixed dynamic triggers waking up actors they are triggered by.
            • +
            • Removed an inapropriate assert from articulation code.
            • +
            • Fixed problem with the angular momentum conservation of articulations.
            • +
            • Fixed articulation sleep problems.
            • +
            • Fixed a linear velocity related bug in CCD.
            • +
            • Fixed crash bug CCD.
            • +
            • Optimized performance of joint information being sent to PVD.
            • +
            + +
            + +


            Supported Platforms (available in separate packages)

            + +
            +

            Runtime

            +
              +
            • Microsoft Windows XP or later
            • +
            • Microsoft XBox 360
            • +
            • Sony Playstation 3
            • +
            • Android 2.2 or later for SDK, 2.3 or later required for samples
            • +
            • Linux (tested on Ubuntu)
            • +
            • Mac OS X
            • +
            +

            Development

            +
              +
            • Microsoft Windows XP or later
            • +
            • Microsoft Visual Studio 2008, 2010
            • +
            • Xcode 3
            • +
            +
            + +


            Known Issues And Limitations

            + +
            + Unchanged from from 3.1. +
            + + + +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.1

            + +

            September 2011

            + +

            What's New In NVIDIA PhysX 3.1

            + +
            +

            General

            +
              +
            • VC10 support has been introduced.
            • +
            • VC8 support has been discontinued.
            • +
            • Namespaces cleaned up.
            • +
            • Extensions, Character Controller and Vehicle source code made available in binary distribution.
            • +
            • Added x86,x64 suffix to PxTaskCUDA.dll
            • +
            • Removed boolean return value from PxScene::addActor(...), and similar API calls.
            • +
            • Added MacOS, Android and Linux to the list of supported platforms. See Supported Platforms below for details.
            • +
            • Upgraded GPU tech to CUDA 4.
            • +
            • Cleaned up a large number of warnings at C++ warning level 4, and set SDK to compile with warnings as errors.
            • +
            • Removed individual sample executables in favor of one Samples executable from PC and console builds.
            • +
            • Fixed alpha blending in samples.
            • +
            • Simplified some code in samples.
            • +
            • Improved ambient lighting in samples.
            • +
            • Made samples work with older graphics cards.
            • +
            • Improved and added more content the user's guide.
            • +
            • No longer passing NULL pointers to user allocator to deallocate.
            • +
            • Various improvements to Foundation and classes shared with APEX.
            • +
            +

            Rigid Bodies

            +
              +
            • Rigid Body: High performance alternative convex narrow phase code available to source licensees. See PERSISTENT_CONTACT_MANIFOLD in the code.
            • +
            • Significant advancements in the continuous collision detection algorithm.
            • +
            • Optimizations and robustness improvements for articulations.
            • +
            • Added some helper code to the API.
            • +
            • Added sleep code for articulations.
            • +
            • Added support for vehicles with more than one chassis shape.
            • +
            • Solver iteration count for articulations.
            • +
            • Articulation limit padding configurable.
            • +
            • The reference count of meshes does now take the application's reference into acount as well and thus has increased by 1 (it used to count the number of objects referencing the mesh only). Note that a mesh does only get destroyed and removed from the list of meshes once the reference count reaches 0.
            • +
            • Fixed autowake parameter sometimes being ignored.
            • +
            • Constraint solver optimizations.
            • +
            • Improved behavior of character controller on steep slopes.
            • +
            • Binary serialization now saves names.
            • +
            • Removed some descriptors from API.
            • +
            • Removed the angular velocity term in the joint positional drive error formula.
            • +
            • Fixed bug in capsule sweep versus mesh.
            • +
            • Fixed a crash bug in the tire model.
            • +
            • Fixed crashing of single link articulations.
            • +
            • Fixed bug related to removing elements of an aggregate.
            • +
            • Fixed swapped wheel graphs in sample vehicle.
            • +
            • Fixed some slow moving bodies falling asleep in midair.
            • +
            • Fixed missing collisions after a call to resetFiltering.
            • +
            • Fixed broken autowake option in setAngularVelocity.
            • +
            • Fixed D6 joint linear limits being uninitialized.
            • +
            • A large number of misc. bug fixes and optimizations.
            • +
            • Improved documentation and error messages associated with running out of narrow phase buffer blocks.
            • +
            • Added articulation documentation.
            • +
            • Expanded manual sections on joints.
            • +
            • Improved reference doc for PxSceneQueryHitType.
            • +
            • Added reference doc for PxSerializable.
            • +
            + +

            Particles

            +
              +
            • Particle index allocation removed from SDK. Added index allocation pool to extensions.
            • +
            • Replaced GPU specific side band API PxPhysicsGpu and PxPhysics::getPhysicsGpu() with PxParticleGpu.
            • +
            • + Memory optimizations on all platforms and options to reduce memory usage according to use case with new per particle system flags: +
                +
              • PxParticleBaseFlag::eCOLLISION_WITH_DYNAMIC_ACTORS
              • +
              • PxParticleBaseFlag::ePER_PARTICLE_COLLISION_CACHE_HINT
              • +
              +
            • +
            • Fixed rare crash appearing with multi-threaded non-GPU particle systems and rigid bodies.
            • +
            • Fixed particles leaking through triangle mesh geometry on GPU.
            • +
            • Fixed fast particles tunneling through scene geometry in some cases.
            • +
            • Fixed erroneous collision of particles with teleporting rigid shapes (setGlobalPose).
            • +
            • Fixed particle sample behavior with some older GPU models.
            • +
            • Fixed a GPU particle crash bug.
            • +
            + + +

            Cloth

            +
              +
            • A new solution for simulating cloth and clothing.
            • +
            + + +

            Deformables

            +
              +
            • Deformables are deprecated and will be removed in the next release. There is a new optimized solution for clothing simulation (see documentation on PxCloth for details).
            • +
            +
            + +


            Supported Platforms (available in separate packages)

            + +
            +

            Runtime

            +
              +
            • Microsoft Windows XP or later
            • +
            • Microsoft XBox 360
            • +
            • Sony Playstation 3
            • +
            • Android 2.2 or later for SDK, 2.3 or later required for samples
            • +
            • Linux (SDK tested on Ubuntu, samples not yet ported)
            • +
            • Mac OS X
            • +
            +

            Development

            +
              +
            • Microsoft Windows XP or later
            • +
            • Microsoft Visual Studio 2008, 2010
            • +
            • Xcode 3
            • +
            +
            + +


            Known Issues And Limitations

            + +
            +

            General

            +
              +
            • Under VC10, you may get warnings due to conflicting build configuration flags. Workaround: Clear the "Inherit from parent or project defaults" flag for all projects in Project->Properties->C/C++->Command Line. We plan to fix this for the 3.1 general availability release.
            • +
            +

            Scene Query

            +
              +
            • Querying the scene (e.g. using raycastSingle()) from multiple threads simultaneously is not safe.
            • +
            +

            Cloth

            +
              +
            • Even simple parameters of a PxCloth can not be set or accessed while the simulation is running.
            • +
            +

            RepX

            +
              +
            • RepX fails to load elements of aggregate joint parameters (PxD6JointDrive etc.)
            • +
            +
            + +
            + Please also see the previous lists from 3.0. +
            + + + +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.0

            + +

            February 14th2011

            + +

            What's New In NVIDIA PhysX 3.0

            + +
            +

            General

            + + This third major version of the SDK is a significant rewrite of the entire technology. We did away with a large amount of legacy clutter and replaced them with a wealth of new features and improvements. + Because even the API changes are so significant, it is easier to see it as a whole new product rather than a long list of changes. + +

            What we removed:

            +
              +
            • The dedicated NVIDIA PhysX PPU hardware is not supported any more.
            • +
            • Scene compartments are not supported anymore. All created physical objects are now part of one and the same compartment.
            • +
            • Force fields are not part of the NVIDIA PhysX SDK anymore.
            • +
            • Splitting a simulation timestep into multiple substeps is not a functionality of the NVIDIA PhysX SDK any longer and has to be implemented above the SDK.
            • +
            + +

            Key new features:

            +
              +
            • Articulations: A way to create very stiff joint assemblies.
            • +
            • Serialization: Save objects in a binary format and load them back quickly!
            • +
            • Broad Phase Clustering: Put objects that belong together into a single broadphase volume.
            • +
            • Driverless Model: No need to worry about system software on PC anymore.
            • +
            • Dynamic Character Controller: A character controller that can robustly walk on dynamic objects.
            • +
            • Vehicle Library: A toolkit to make vehicles, including an all new tire model.
            • +
            • Non-Simulation Objects: A staging are outside of the simulation from where you can add things into the simulation at high speed.
            • +
            • Simulation Task Manager: Take control of the management of simulation tasks.
            • +
            • Stable Depenetration: Large penetrations can be gracefully recovered from.
            • +
            • Double Buffering: You can read from and write to the simulated objects while the simulation is running on another thread.
            • +
            • Mesh Scaling: Create different nonuniformly scaled instances of your meshes and convexes without duplicating the memory.
            • +
            • Distance Based Collision Detection: Have the simulation create contacts before objects touch, and do away with unsightly overlaps.
            • +
            • Fast Continuous Collision Detection: Have small and high speed objects collide correctly without sacrificing performance.
            • +
            • Significantly increased performance and memory footprint, especially on consoles.
            • +
            • Unified solver for deformables and rigid bodies for great interaction.
            • +
            • Triangle collision detection with deformables.
            • +
            • Support for our new Physics Visual Debugger, including integrated profiling.
            • +
            + + +

            Math classes

            +
              +
            • Matrix based transforms have been replaced by quaternions.
            • +
            • + All angles are now expressed in radians. IN PARTICULAR the PxQuat constructor from + axis and angle as well as the getAngleAxis and fromAngleAxis methods now use radians rather than degrees. +
            • +
            + +

            Rigid Bodies

            +
              +
            • Capsules are now defined to extend along the x rather than the y axis.
            • +
            • Triangle meshes do not support heightfield functionality anymore. Use the dedicated PxHeightField class instead.
            • +
            • Dynamic triangle mesh actors are not supported any longer. However, you can decompose your mesh into convex parts and create a dynamic actor consisting of these convex parts.
            • +
            • The deprecated heightfield property NxHeightFieldDesc::verticalExtent is not supported any longer. Please use the PxHeightFieldDesc::thickness parameter instead.
            • +
            • NxSpringAndDamperEffector is not supported anymore. Use PxDistanceJoint instead.
            • +
            • Joints are now part of the PhysX extensions library (PhysXExtensions).
            • +
            • Wheel shapes have been replaced by the more flexible entity PxWheel. A default wheel implementation, encapsulating most of the old wheel functionality, can be found in the PhysX extensions library (see PxDefaultWheel).
            • +
            • The NxUtilLib library has been removed. Sweep/overlap/raycast tests and other helper methods can be found in the new GeomUtils library.
            • +
            • Materials can no longer be accessed through indices. Per triangle material meshes need a material table which can be specified per shape (see PxShape::setMaterials() for details).
            • +
            • The default material is not available anymore.
            • +
            + +

            Particle Systems, Particle Fluids

            +
              +
            • + The NxFluid class has been replaced with two classes for separation of functionality and ease of use. +
                +
              • PxParticleSystem: Particles colliding against the scene.
              • +
              • PxParticleFluid: Particles simulating a fluid (sph).
              • +
              +
            • +
            • + Simplified parameterization for particle systems. +
                +
              • Using absolute distances instead of relative multipliers to rest spacing
              • +
              • Simplified sph parameters
              • +
              • Unified collision parameters with deformable and rigid body features
              • +
              +
            • +
            • + Creating and releasing particles is now fully controlled by the application. +
                +
              • Particle lifetime management isn't provided through the SDK anymore.
              • +
              • Emitters have been removed from the SDK.
              • +
              • Drain shapes don't cause particles to be deleted directly, but to be flagged instead.
              • +
              • Initial particle creation from the particle system descriptor isn't supported anymore.
              • +
              +
            • +
            • Particle data buffer handling has been moved to the SDK.
            • +
            • Per particle collision rest offset.
            • +
            • + GPU accelerated particle systems. +
                +
              • Application controllable mesh mirroring to device memory.
              • +
              • Runtime switching between software and GPU accelerated implementation.
              • +
              +
            • +
            +
            + +


            Supported Platforms (available in separate packages)

            + +
            +

            Runtime

            +
              +
            • Microsoft Windows XP or and later
            • +
            • Microsoft XBox360
            • +
            • Sony Playstation 3
            • +
            +

            Development

            +
              +
            • Microsoft Windows XP or and later
            • +
            • Microsoft Visual Studio 2008
            • +
            +
            + +


            Known Issues And Limitations

            + +
            +

            Rigid Bodies

            +
              +
            • Adding or removing a PxAggregate object to the scene is not possible while the simulation is running.
            • +
            +

            Particle Systems

            +
              +
            • + Releasing the Physics SDK may result in a crash when using GPU accelerated particle systems.
              + This can be avoided by doing the following before releasing the Physics SDK: +
                +
              • Releasing the PxScene objects that contain the GPU accelerated particle systems.
              • +
              • Releasing application mirrored meshes by calling PxPhysicsGpu::releaseTriangleMeshMirror(...), PxPhysicsGpu::releaseHeightFieldMirror(...) or PxPhysicsGpu::releaseConvexMeshMirror(...).
              • +
              +
            • +
            +
            + +

            +
            + Copyright (C) 2008-2018 NVIDIA Corporation, 2701 San Thomas Expressway, Santa Clara, CA 95050 U.S.A. All rights reserved. www.nvidia.com +

            + + diff --git a/src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h b/src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h new file mode 100644 index 000000000..6a99075be --- /dev/null +++ b/src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef CM_WINDOWS_LOADLIBRARY_H +#define CM_WINDOWS_LOADLIBRARY_H + +#include "foundation/PxPreprocessor.h" +#include "windows/PxWindowsDelayLoadHook.h" +#include "windows/PsWindowsInclude.h" + +#ifdef PX_SECURE_LOAD_LIBRARY +#include "nvSecureLoadLibrary.h" +#endif + + +namespace physx +{ +namespace Cm +{ + EXTERN_C IMAGE_DOS_HEADER __ImageBase; + + PX_INLINE HMODULE WINAPI loadLibrary(const char* name) + { +#ifdef PX_SECURE_LOAD_LIBRARY + HMODULE retVal = nvLoadSignedLibrary(name,true); + if(!retVal) + { + exit(1); + } + return retVal; +#else + return ::LoadLibraryA( name ); +#endif + }; + + PX_INLINE FARPROC WINAPI physXCommonDliNotePreLoadLibrary(const char* libraryName, const physx::PxDelayLoadHook* delayLoadHook) + { + if(!delayLoadHook) + { + return (FARPROC)loadLibrary(libraryName); + } + else + { + if(strstr(libraryName, "PhysXFoundation")) + { + return (FARPROC)Cm::loadLibrary(delayLoadHook->getPhysXFoundationDllName()); + } + + if(strstr(libraryName, "PhysXCommon")) + { + return (FARPROC)Cm::loadLibrary(delayLoadHook->getPhysXCommonDllName()); + } + } + return NULL; + } +} // namespace Cm +} // namespace physx + + +#endif // CM_WINDOWS_LOADLIBRARY_H diff --git a/src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h b/src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h new file mode 100644 index 000000000..3afd86db2 --- /dev/null +++ b/src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef CM_WINDOWS_MODULEUPDATELOADER_H +#define CM_WINDOWS_MODULEUPDATELOADER_H + +#include "foundation/PxPreprocessor.h" +#include "common/PxPhysXCommonConfig.h" + +#include "windows/PsWindowsInclude.h" + +namespace physx +{ +namespace Cm +{ + +#if PX_X64 +#define UPDATE_LOADER_DLL_NAME "PhysXUpdateLoader64.dll" +#else +#define UPDATE_LOADER_DLL_NAME "PhysXUpdateLoader.dll" +#endif + +class PX_PHYSX_COMMON_API CmModuleUpdateLoader +{ +public: + CmModuleUpdateLoader(const char* updateLoaderDllName); + + ~CmModuleUpdateLoader(); + + // Loads the given module through the update loader. Loads it from the path if + // the update loader doesn't find the requested module. Returns NULL if no + // module found. + HMODULE LoadModule(const char* moduleName, const char* appGUID); + +protected: + HMODULE mUpdateLoaderDllHandle; + FARPROC mGetUpdatedModuleFunc; +}; +} // namespace Cm +} // namespace physx + + +#endif // CM_WINDOWS_MODULEUPDATELOADER_H diff --git a/src/PhysX/physx/source/common/src/CmBitMap.h b/src/PhysX/physx/source/common/src/CmBitMap.h new file mode 100644 index 000000000..31c62853b --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmBitMap.h @@ -0,0 +1,504 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_BITMAP +#define PX_PHYSICS_COMMON_BITMAP + +#include "foundation/PxAssert.h" +#include "foundation/PxMath.h" +#include "foundation/PxMemory.h" +#include "PsAllocator.h" +#include "PsUserAllocated.h" +#include "PsIntrinsics.h" +#include "PsMathUtils.h" +#include "CmPhysXCommon.h" +#include "PsBitUtils.h" +// PX_SERIALIZATION +#include "PxSerialFramework.h" +//~PX_SERIALIZATION + +namespace physx +{ +namespace Cm +{ + + /*! + Hold a bitmap with operations to set,reset or test given bit. + + We inhibit copy to prevent unintentional copies. If a copy is desired copy() should be used or + alternatively a copy constructor implemented. + */ + template + class BitMapBase : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_NOCOPY(BitMapBase) + + public: + + // PX_SERIALIZATION + /* todo: explicit */ BitMapBase(const PxEMPTY) + { + if(mMap) + mWordCount |= PX_SIGN_BITMASK; + } + + void exportExtraData(PxSerializationContext& stream, void*) + { + if(mMap && getWordCount()) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mMap, getWordCount()*sizeof(PxU32)); + } + } + void importExtraData(PxDeserializationContext& context) + { + if(mMap && getWordCount()) + mMap = context.readExtraData(getWordCount()); + } + //~PX_SERIALIZATION + + //sschirm: function for placement new. Almost the same as importExtraData above, but lets you set word count and map after default construction + void importData(PxU32 worldCount, PxU32* words) + { + PX_ASSERT(mWordCount == 0 && !mMap); + mMap = words; + mWordCount = worldCount | PX_SIGN_BITMASK; + } + + PX_INLINE BitMapBase(Allocator& allocator) : mMap(0), mWordCount(0), mAllocator(allocator) {} + + PX_INLINE BitMapBase() : mMap(0), mWordCount(0) {} + + PX_INLINE ~BitMapBase() + { + if(mMap && !isInUserMemory()) + mAllocator.deallocate(mMap); + mMap = NULL; + } + + PX_INLINE Allocator& getAllocator() { return mAllocator; } + + PX_INLINE void growAndSet(PxU32 index) + { + extend(index+1); + mMap[index>>5] |= 1<<(index&31); + } + + PX_INLINE void growAndReset(PxU32 index) + { + extend(index+1); + mMap[index>>5] &= ~(1<<(index&31)); + } + + PX_INLINE Ps::IntBool boundedTest(PxU32 index) const + { + return Ps::IntBool(index>>5 >= getWordCount() ? Ps::IntFalse : (mMap[index>>5]&(1<<(index&31)))); + } + + // Special optimized versions, when you _know_ your index is in range + PX_INLINE void set(PxU32 index) + { + PX_ASSERT(index>5] |= 1<<(index&31); + } + + PX_INLINE void reset(PxU32 index) + { + PX_ASSERT(index>5] &= ~(1<<(index&31)); + } + + PX_INLINE Ps::IntBool test(PxU32 index) const + { + PX_ASSERT(index>5]&(1<<(index&31))); + } + + // nibble == 4 bits + PX_INLINE PxU32 getNibbleFast(PxU32 nibIndex) const + { + PxU32 bitIndex = nibIndex << 2; + PX_ASSERT(bitIndex < getWordCount()*32); + return (mMap[bitIndex >> 5] >> (bitIndex & 31)) & 0xf; + } + + PX_INLINE void andNibbleFast(PxU32 nibIndex, PxU32 mask) + { + //TODO: there has to be a faster way... + PxU32 bitIndex = nibIndex << 2; + PxU32 shift = (bitIndex & 31); + PxU32 nibMask = 0xf << shift; + + PX_ASSERT(bitIndex < getWordCount()*32); + + mMap[bitIndex >> 5] &= ((mask << shift) | ~nibMask); + } + + PX_INLINE void orNibbleFast(PxU32 nibIndex, PxU32 mask) + { + PX_ASSERT(!(mask & ~0xf)); //check extra bits are not set + + PxU32 bitIndex = nibIndex << 2; + PxU32 shift = bitIndex & 31; + + PX_ASSERT(bitIndex < getWordCount()*32); + + mMap[bitIndex >> 5] |= (mask << shift); + } + + void clear() + { + PxMemSet(mMap, 0, getWordCount()*sizeof(PxU32)); + } + + void resizeAndClear(PxU32 newBitCount) + { + extendUninitialized(newBitCount); + PxMemSet(mMap, 0, getWordCount()*sizeof(PxU32)); + } + + void setEmpty() + { + mMap=NULL; + mWordCount=0; + } + + void setWords(PxU32* map, PxU32 wordCount) + { + mMap=map; + mWordCount=wordCount; + mWordCount |= PX_SIGN_BITMASK; + } + + // !!! only sets /last/ bit to value + void resize(PxU32 newBitCount, bool value = false) + { + PX_ASSERT(!value); // only new class supports this + PX_UNUSED(value); + extend(newBitCount); + } + PxU32 size() const { return getWordCount()*32; } + + void copy(const BitMapBase& a) + { + extendUninitialized(a.getWordCount()<<5); + PxMemCopy(mMap, a.mMap, a.getWordCount() * sizeof(PxU32)); + if(getWordCount() > a.getWordCount()) + PxMemSet(mMap + a.getWordCount(), 0, (getWordCount() - a.getWordCount()) * sizeof(PxU32)); + } + + PX_INLINE PxU32 count() const + { + // NOTE: we can probably do this faster, since the last steps in PxcBitCount32 can be defered to + // the end of the seq. + 64/128bits at a time + native bit counting instructions(360 is fast non micro code). + PxU32 count = 0; + PxU32 wordCount = getWordCount(); + for(PxU32 i=0; i 0;) + { + if(mMap[i]) + return (i<<5)+Ps::highestSetBit(mMap[i]); + } + return PxU32(0); + } + + + + // the obvious combiners and some used in the SDK + + struct OR { PX_INLINE PxU32 operator()(PxU32 a, PxU32 b) { return a|b; } }; + struct AND { PX_INLINE PxU32 operator()(PxU32 a, PxU32 b) { return a&b; } }; + struct XOR { PX_INLINE PxU32 operator()(PxU32 a, PxU32 b) { return a^b; } }; + + // we use auxiliary functions here so as not to generate combiners for every combination + // of allocators + + template + PX_INLINE void combineInPlace(const BitMapBase<_>& b) + { + combine1(b.mMap,b.getWordCount()); + } + + template + PX_INLINE void combine(const BitMapBase<_1>& a, const BitMapBase<_2>& b) + { + combine2(a.mMap,a.getWordCount(),b.mMap,b.getWordCount()); + } + + PX_FORCE_INLINE const PxU32* getWords() const { return mMap; } + PX_FORCE_INLINE PxU32* getWords() { return mMap; } + + // PX_SERIALIZATION + PX_FORCE_INLINE PxU32 getWordCount() const { return mWordCount & ~PX_SIGN_BITMASK; } + + // We need one bit to mark arrays that have been deserialized from a user-provided memory block. + PX_FORCE_INLINE PxU32 isInUserMemory() const { return mWordCount & PX_SIGN_BITMASK; } + //~PX_SERIALIZATION + + /*! + Iterate over indices in a bitmap + + This iterator is good because it finds the set bit without looping over the cached bits upto 31 times. + However it does require a variable shift. + */ + + class Iterator + { + public: + static const PxU32 DONE = 0xffffffff; + + PX_INLINE Iterator(const BitMapBase &map) : mBitMap(map) + { + reset(); + } + + PX_INLINE Iterator& operator=(const Iterator& other) + { + PX_ASSERT(&mBitMap == &other.mBitMap); + mBlock = other.mBlock; + mIndex = other.mIndex; + return *this; + } + + PX_INLINE PxU32 getNext() + { + if(mBlock) + { + PxU32 bitIndex = mIndex<<5 | Ps::lowestSetBit(mBlock); + mBlock &= mBlock-1; + PxU32 wordCount = mBitMap.getWordCount(); + while(!mBlock && ++mIndex < wordCount) + mBlock = mBitMap.mMap[mIndex]; + return bitIndex; + } + return DONE; + } + + PX_INLINE void reset() + { + mIndex = mBlock = 0; + PxU32 wordCount = mBitMap.getWordCount(); + while(mIndex < wordCount && ((mBlock = mBitMap.mMap[mIndex]) == 0)) + ++mIndex; + } + private: + PxU32 mBlock, mIndex; + const BitMapBase& mBitMap; + }; + + // DS: faster but less general: hasBits() must be true or getNext() is illegal so it is the calling code's responsibility to ensure that getNext() is not called illegally. + class LoopIterator + { + PX_NOCOPY(LoopIterator) + + public: + PX_FORCE_INLINE LoopIterator(const BitMapBase &map) : mMap(map.getWords()), mBlock(0), mIndex(-1), mWordCount(PxI32(map.getWordCount())) {} + + PX_FORCE_INLINE bool hasBits() + { + PX_ASSERT(mIndex>5; + if(newWordCount > getWordCount()) + { + PxU32* newMap = reinterpret_cast(mAllocator.allocate(newWordCount*sizeof(PxU32), __FILE__, __LINE__)); + if(mMap) + { + PxMemCopy(newMap, mMap, getWordCount()*sizeof(PxU32)); + if (!isInUserMemory()) + mAllocator.deallocate(mMap); + } + PxMemSet(newMap+getWordCount(), 0, (newWordCount-getWordCount())*sizeof(PxU32)); + mMap = newMap; + // also resets the isInUserMemory bit + mWordCount = newWordCount; + } + } + + void extendUninitialized(PxU32 size) + { + PxU32 newWordCount = (size+31)>>5; + if(newWordCount > getWordCount()) + { + if(mMap && !isInUserMemory()) + mAllocator.deallocate(mMap); + // also resets the isInUserMemory bit + mWordCount = newWordCount; + mMap = reinterpret_cast(mAllocator.allocate(mWordCount*sizeof(PxU32), __FILE__, __LINE__)); + } + } + + template + void combine1(const PxU32* words, PxU32 length) + { + extend(length<<5); + PxU32 combineLength = PxMin(getWordCount(), length); + for(PxU32 i=0;i + void combine2(const PxU32* words1, PxU32 length1, + const PxU32* words2, PxU32 length2) + { + extendUninitialized(PxMax(length1,length2)<<5); + + PxU32 commonSize = PxMin(length1,length2); + + for(PxU32 i=0;i BitMap; + typedef BitMapBase BitMapPinned; + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmBlockArray.h b/src/PhysX/physx/source/common/src/CmBlockArray.h new file mode 100644 index 000000000..2e80a9b47 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmBlockArray.h @@ -0,0 +1,153 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CM_BLOCK_ARRAY_H +#define CM_BLOCK_ARRAY_H + +#include "foundation/PxAssert.h" +#include "foundation/PxMath.h" +#include "foundation/PxMemory.h" +#include "PsAllocator.h" +#include "PsUserAllocated.h" +#include "PsIntrinsics.h" +#include "PsMathUtils.h" +#include "CmPhysXCommon.h" +#include "PsArray.h" + +namespace physx +{ +namespace Cm +{ + +template +class BlockArray +{ + Ps::Array mBlocks; + PxU32 mSize; + PxU32 mCapacity; + PxU32 mSlabSize; + +public: + + BlockArray(PxU32 slabSize = 2048) : mSize(0), mCapacity(0), mSlabSize(slabSize) + { + PX_ASSERT(slabSize > 0); + } + + ~BlockArray() + { + for (PxU32 a = 0; a < mBlocks.size(); ++a) + { + PX_FREE(mBlocks[a]); + } + mBlocks.resize(0); + } + + void reserve(PxU32 capacity) + { + if (capacity > mCapacity) + { + PxU32 nbSlabsRequired = (capacity + mSlabSize - 1) / mSlabSize; + + PxU32 nbSlabsToAllocate = nbSlabsRequired - mBlocks.size(); + + mCapacity += nbSlabsToAllocate * mSlabSize; + + for (PxU32 a = 0; a < nbSlabsToAllocate; ++a) + { + mBlocks.pushBack(reinterpret_cast(PX_ALLOC(sizeof(T) * mSlabSize, PX_DEBUG_EXP("BlockArray")))); + } + } + } + + void resize(PxU32 size) + { + reserve(size); + for (PxU32 a = mSize; a < size; ++a) + { + mBlocks[a / mSlabSize][a%mSlabSize] = T(); + } + mSize = size; + } + + void forceSize_Unsafe(PxU32 size) + { + PX_ASSERT(size <= mCapacity); + mSize = size; + } + + void remove(PxU32 idx) + { + PX_ASSERT(idx < mSize); + for (PxU32 a = idx; a < mSize; ++a) + { + mBlocks[a / mSlabSize][a%mSlabSize] = mBlocks[(a + 1) / mSlabSize][(a + 1) % mSlabSize]; + } + + mSize--; + } + + void replaceWithLast(PxU32 idx) + { + PX_ASSERT(idx < mSize); + --mSize; + mBlocks[idx / mSlabSize][idx%mSlabSize] = mBlocks[mSize / mSlabSize][mSize%mSlabSize]; + } + + T& operator [] (const PxU32 idx) + { + PX_ASSERT(idx < mSize); + + return mBlocks[idx / mSlabSize][idx%mSlabSize]; + } + + const T& operator [] (const PxU32 idx) const + { + PX_ASSERT(idx < mSize); + + return mBlocks[idx / mSlabSize][idx%mSlabSize]; + } + + void pushBack(const T& item) + { + reserve(mSize + 1); + mBlocks[mSize / mSlabSize][mSize%mSlabSize] = item; + mSize++; + } + + PxU32 capacity() const { return mCapacity; } + + PxU32 size() const { return mSize; } +}; + +} +} + +#endif + diff --git a/src/PhysX/physx/source/common/src/CmBoxPruning.cpp b/src/PhysX/physx/source/common/src/CmBoxPruning.cpp new file mode 100644 index 000000000..9622ffdbc --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmBoxPruning.cpp @@ -0,0 +1,197 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CmBoxPruning.h" +#include "CmRadixSortBuffered.h" +#include "PsAllocator.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. + * \param nb0 [in] number of boxes in the first set + * \param bounds0 [in] list of boxes for the first set + * \param nb1 [in] number of boxes in the second set + * \param bounds1 [in] list of boxes for the second set + * \param pairs [out] list of overlapping pairs + * \param axes [in] projection order (0,2,1 is often best) + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Cm::BipartiteBoxPruning(const PxBounds3* bounds0, PxU32 nb0, const PxBounds3* bounds1, PxU32 nb1, Ps::Array& pairs, const Axes& axes) +{ + pairs.clear(); + // Checkings + if(nb0 == 0 || nb1 == 0) + return false; + + // Catch axes + PxU32 Axis0 = axes.mAxis0; + PxU32 Axis1 = axes.mAxis1; + PxU32 Axis2 = axes.mAxis2; + + PX_UNUSED(Axis1); + PX_UNUSED(Axis2); + + // Allocate some temporary data + float* MinPosBounds0 = reinterpret_cast(PX_ALLOC_TEMP(sizeof(float)*nb0, "Gu::BipartiteBoxPruning")); + float* MinPosBounds1 = reinterpret_cast(PX_ALLOC_TEMP(sizeof(float)*nb1, "Gu::BipartiteBoxPruning")); + + // 1) Build main lists using the primary axis + for(PxU32 i=0;i& pairs, const Axes& axes) +{ + pairs.clear(); + + // Checkings + if(!nb) + return false; + + // Catch axes + const PxU32 Axis0 = axes.mAxis0; + const PxU32 Axis1 = axes.mAxis1; + const PxU32 Axis2 = axes.mAxis2; + + PX_UNUSED(Axis1); + PX_UNUSED(Axis2); + + // Allocate some temporary data + float* PosList = reinterpret_cast(PX_ALLOC_TEMP(sizeof(float)*nb, "Cm::CompleteBoxPruning")); + + // 1) Build main list using the primary axis + for(PxU32 i=0;i& pairs, const Gu::Axes& axes); + PX_PHYSX_COMMON_API bool BipartiteBoxPruning(const PxBounds3* bounds0, PxU32 nb0, const PxBounds3* bounds1, PxU32 nb1, Ps::Array& pairs, const Gu::Axes& axes); +} +} + +#endif // CM_BOXPRUNING_H diff --git a/src/PhysX/physx/source/common/src/CmCollection.cpp b/src/PhysX/physx/source/common/src/CmCollection.cpp new file mode 100644 index 000000000..87e9e1ccb --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmCollection.cpp @@ -0,0 +1,217 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CmCollection.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Cm; + +void Collection::add(PxBase& object, PxSerialObjectId id) +{ + PxSerialObjectId originId = getId(object); + if( originId != PX_SERIAL_OBJECT_ID_INVALID) + { + if( originId != id) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxCollection::add called for an object that has an associated id already present in the collection!"); + } + return; + } + + if(id != PX_SERIAL_OBJECT_ID_INVALID) + { + if(!mIds.insert(id, &object)) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxCollection::add called with an id which is already used in the collection"); + return; + } + } + + mObjects[&object] = id; +} + +void Collection::remove(PxBase& object) +{ + PX_CHECK_AND_RETURN(contains(object), "PxCollection::remove called for an object not contained in the collection!"); + + const ObjectToIdMap::Entry* e = mObjects.find(&object); + if(e) + { + mIds.erase(e->second); + mObjects.erase(&object); + } +} + +bool Collection::contains(PxBase& object) const +{ + return mObjects.find(&object) != NULL; +} + +void Collection::addId(PxBase& object, PxSerialObjectId id) +{ + PX_CHECK_AND_RETURN(contains(object), "PxCollection::addId called for object that is not contained in the collection!"); + PX_CHECK_AND_RETURN(id != PX_SERIAL_OBJECT_ID_INVALID, "PxCollection::addId called with PxSerialObjectId being set to PX_SERIAL_OBJECT_ID_INVALID!"); + PX_CHECK_AND_RETURN(mIds.find(id) == NULL, "PxCollection::addId called with an id which is already used in the collection!"); + + const ObjectToIdMap::Entry* e = mObjects.find(&object); + if(e && e->second != PX_SERIAL_OBJECT_ID_INVALID) + mIds.erase(e->second); + + mIds.insert(id, &object); + mObjects[&object] = id; +} + +void Collection::removeId(PxSerialObjectId id) +{ + PX_CHECK_AND_RETURN(id != PX_SERIAL_OBJECT_ID_INVALID, "PxCollection::removeId called with PxSerialObjectId being set to PX_SERIAL_OBJECT_ID_INVALID!"); + PX_CHECK_AND_RETURN(mIds.find(id), "PxCollection::removeId called with PxSerialObjectId not contained in the collection!"); + const IdToObjectMap::Entry* e = mIds.find(id); + if(e) + { + mObjects[e->second] = PX_SERIAL_OBJECT_ID_INVALID; + mIds.erase(id); + } +} + +PxBase* Collection::find(PxSerialObjectId id) const +{ + PX_CHECK_AND_RETURN_NULL(id != PX_SERIAL_OBJECT_ID_INVALID, "PxCollection::find called with PxSerialObjectId being set to PX_SERIAL_OBJECT_ID_INVALID!"); + const IdToObjectMap::Entry* e = mIds.find(id); + return e ? static_cast(e->second) : NULL; +} + +void Collection::add(PxCollection& _collection) +{ + Collection& collection = static_cast(_collection); + PX_CHECK_AND_RETURN(this != &collection, "PxCollection::add(PxCollection&) called with itself!"); + + mObjects.reserve(mObjects.capacity() + collection.mObjects.size()); + const ObjectToIdMap::Entry* e = collection.mObjects.getEntries(); + for (PxU32 i = 0; i < collection.mObjects.size(); ++i) + { + PxSerialObjectId id = e[i].second; + if( id != PX_SERIAL_OBJECT_ID_INVALID) + { + if(!mIds.insert(id, e[i].first)) + { + if(mIds[id] != e[i].first) + { + PX_CHECK_MSG( false, "PxCollection::add(PxCollection&) called with conflicting id!"); + mObjects.insert(e[i].first, PX_SERIAL_OBJECT_ID_INVALID); + } + } + else + mObjects[ e[i].first ] = id; + } + else + mObjects.insert(e[i].first, PX_SERIAL_OBJECT_ID_INVALID); + } +} + +void Collection::remove(PxCollection& _collection) +{ + Collection& collection = static_cast(_collection); + PX_CHECK_AND_RETURN(this != &collection, "PxCollection::remove(PxCollection&) called with itself!"); + + const ObjectToIdMap::Entry* e = collection.mObjects.getEntries(); + for (PxU32 i = 0; i < collection.mObjects.size(); ++i) + { + const ObjectToIdMap::Entry* e1 = mObjects.find(e[i].first); + if(e1) + { + mIds.erase(e1->second); + mObjects.erase(e1->first); + } + } +} + +PxU32 Collection::getNbObjects() const +{ + return mObjects.size(); +} + +PxBase& Collection::getObject(PxU32 index) const +{ + PX_ASSERT(index < mObjects.size()); + return *mObjects.getEntries()[index].first; +} + +PxU32 Collection::getObjects(PxBase** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + PX_CHECK_AND_RETURN_NULL(userBuffer != NULL, "PxCollection::getObjects called with userBuffer NULL!"); + PX_CHECK_AND_RETURN_NULL(bufferSize != 0, "PxCollection::getObjects called with bufferSize 0!"); + PxU32 dstIndex = 0; + const ObjectToIdMap::Entry* e = mObjects.getEntries(); + for (PxU32 srcIndex = startIndex; srcIndex < mObjects.size() && dstIndex < bufferSize; ++srcIndex) + userBuffer[dstIndex++] = e[srcIndex].first; + + return dstIndex; +} + +PxU32 Collection::getNbIds() const +{ + return mIds.size(); +} + +PxSerialObjectId Collection::getId(const PxBase& object) const +{ + const ObjectToIdMap::Entry* e = mObjects.find(const_cast(&object)); + return e ? e->second : PX_SERIAL_OBJECT_ID_INVALID; +} + +PxU32 Collection::getIds(PxSerialObjectId* userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + PX_CHECK_AND_RETURN_NULL(userBuffer != NULL, "PxCollection::getIds called with userBuffer NULL!"); + PX_CHECK_AND_RETURN_NULL(bufferSize != 0, "PxCollection::getIds called with bufferSize 0!"); + PxU32 dstIndex = 0; + + IdToObjectMap::Iterator srcIt = (const_cast(mIds)).getIterator(); + + while (!srcIt.done() && dstIndex < bufferSize) + { + if(srcIt->first != PX_SERIAL_OBJECT_ID_INVALID) + { + if(startIndex > 0) + startIndex--; + else + userBuffer[dstIndex++] = srcIt->first; + } + srcIt++; + } + + return dstIndex; +} + +PxCollection* PxCreateCollection() +{ + return PX_NEW(Collection); +} diff --git a/src/PhysX/physx/source/common/src/CmCollection.h b/src/PhysX/physx/source/common/src/CmCollection.h new file mode 100644 index 000000000..b80ec256b --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmCollection.h @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CM_COLLECTION +#define PX_PHYSICS_CM_COLLECTION + +#include "CmPhysXCommon.h" +#include "PxCollection.h" +#include "PsHashMap.h" +#include "PsUserAllocated.h" +#include "PsAllocator.h" + +namespace physx +{ +namespace Cm +{ + template , + class Allocator = Ps::NonTrackingAllocator > + class CollectionHashMap : public Ps::CoalescedHashMap< Key, Value, HashFn, Allocator> + { + typedef physx::shdfnd::internal::HashMapBase< Key, Value, HashFn, Allocator> MapBase; + typedef Ps::Pair EntryData; + + public: + CollectionHashMap(PxU32 initialTableSize = 64, float loadFactor = 0.75f): + Ps::CoalescedHashMap< Key, Value, HashFn, Allocator>(initialTableSize,loadFactor) {} + + void insertUnique(const Key& k, const Value& v) + { + PX_PLACEMENT_NEW(MapBase::mBase.insertUnique(k), EntryData)(k,v); + } + }; + + + + class Collection : public PxCollection, public Ps::UserAllocated + { + public: + typedef CollectionHashMap ObjectToIdMap; + typedef CollectionHashMap IdToObjectMap; + + virtual void add(PxBase& object, PxSerialObjectId ref); + virtual void remove(PxBase& object); + virtual bool contains(PxBase& object) const; + virtual void addId(PxBase& object, PxSerialObjectId id); + virtual void removeId(PxSerialObjectId id); + virtual PxBase* find(PxSerialObjectId ref) const; + virtual void add(PxCollection& collection); + virtual void remove(PxCollection& collection); + virtual PxU32 getNbObjects() const; + virtual PxBase& getObject(PxU32 index) const; + virtual PxU32 getObjects(PxBase** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxU32 getNbIds() const; + virtual PxSerialObjectId getId(const PxBase& object) const; + virtual PxU32 getIds(PxSerialObjectId* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + void release() { PX_DELETE(this); } + + + // Only for internal use. Bypasses virtual calls, specialized behaviour. + PX_INLINE void internalAdd(PxBase* s, PxSerialObjectId id = PX_SERIAL_OBJECT_ID_INVALID) { mObjects.insertUnique(s, id); } + PX_INLINE PxU32 internalGetNbObjects() const { return mObjects.size(); } + PX_INLINE PxBase* internalGetObject(PxU32 i) const { PX_ASSERT(i1e-6f); + return (tan1+tan2)/(1-tan1*tan2); + } + + PX_FORCE_INLINE float computeAxisAndError(const PxVec3& r, const PxVec3& d, const PxVec3& twistAxis, PxVec3& axis) + { + // the point on the cone defined by the tanQ swing vector r + // this code is equal to quatFromTanQVector(r).rotate(PxVec3(1.0f, 0.0f, 0.0f); + PxVec3 p(1.f,0,0); + PxReal r2 = r.dot(r), a = 1-r2, b = 1/(1+r2), b2 = b*b; + PxReal v1 = 2*a*b2; + PxVec3 v2(a, 2*r.z, -2*r.y); // a*p + 2*r.cross(p); + PxVec3 coneLine = v1 * v2 - p; // already normalized + + // the derivative of coneLine in the direction d + PxReal rd = r.dot(d); + PxReal dv1 = -4*rd*(3-r2)*b2*b; + PxVec3 dv2(-2*rd, 2*d.z, -2*d.y); + + PxVec3 coneNormal = v1 * dv2 + dv1 * v2; + + axis = coneLine.cross(coneNormal)/coneNormal.magnitude(); + return coneLine.cross(axis).dot(twistAxis); + } + + // this is here because it's used in both LL and Extensions. However, it + // should STAY IN THE SDK CODE BASE because it's SDK-specific + + class ConeLimitHelper + { + public: + ConeLimitHelper(PxReal tanQSwingY, PxReal tanQSwingZ, PxReal tanQPadding) + : mTanQYMax(tanQSwingY), mTanQZMax(tanQSwingZ), mTanQPadding(tanQPadding) {} + + // whether the point is inside the (inwardly) padded cone - if it is, there's no limit + // constraint + + PX_FORCE_INLINE bool contains(const PxVec3& tanQSwing) const + { + PxReal tanQSwingYPadded = tanAdd(PxAbs(tanQSwing.y),mTanQPadding); + PxReal tanQSwingZPadded = tanAdd(PxAbs(tanQSwing.z),mTanQPadding); + return Ps::sqr(tanQSwingYPadded/mTanQYMax)+Ps::sqr(tanQSwingZPadded/mTanQZMax) <= 1; + } + + PX_FORCE_INLINE PxVec3 clamp(const PxVec3& tanQSwing, PxVec3& normal) const + { + PxVec3 p = Ps::ellipseClamp(tanQSwing, PxVec3(0,mTanQYMax,mTanQZMax)); + normal = PxVec3(0, p.y/Ps::sqr(mTanQYMax), p.z/Ps::sqr(mTanQZMax)); +#ifdef PX_PARANOIA_ELLIPSE_CHECK + PxReal err = PxAbs(Ps::sqr(p.y/mTanQYMax) + Ps::sqr(p.z/mTanQZMax) - 1); + PX_ASSERT(err<1e-3); +#endif + return p; + } + + // input is a swing quat, such that swing.x = twist.y = twist.z = 0, q = swing * twist + // The routine is agnostic to the sign of q.w (i.e. we don't need the minimal-rotation swing) + + // output is an axis such that positive rotation increases the angle outward from the + // limit (i.e. the image of the x axis), the error is the sine of the angular difference, + // positive if the twist axis is inside the cone + + bool getLimit(const PxQuat& swing, PxVec3& axis, PxReal& error) const + { + PX_ASSERT(swing.w>0); + PxVec3 twistAxis = swing.getBasisVector0(); + PxVec3 tanQSwing = PxVec3(0, Ps::tanHalf(swing.z,swing.w), -Ps::tanHalf(swing.y,swing.w)); + if(contains(tanQSwing)) + return false; + + PxVec3 normal, clamped = clamp(tanQSwing, normal); + + // rotation vector and ellipse normal + PxVec3 r(0,-clamped.z,clamped.y), d(0, -normal.z, normal.y); + + error = computeAxisAndError(r, d, twistAxis, axis); + + PX_ASSERT(PxAbs(axis.magnitude()-1)<1e-5f); + +#ifdef PX_PARANOIA_ELLIPSE_CHECK + bool inside = Ps::sqr(tanQSwing.y/mTanQYMax) + Ps::sqr(tanQSwing.z/mTanQZMax) <= 1; + PX_ASSERT(inside && error>-1e-4f || !inside && error<1e-4f); +#endif + return true; + } + + private: + + + PxReal mTanQYMax, mTanQZMax, mTanQPadding; + }; + + class ConeLimitHelperTanLess + { + public: + ConeLimitHelperTanLess(PxReal swingY, PxReal swingZ, PxReal padding) + : mYMax(swingY), mZMax(swingZ), mPadding(padding) {} + + // whether the point is inside the (inwardly) padded cone - if it is, there's no limit + // constraint + PX_FORCE_INLINE bool contains(const PxVec3& swing) const + { + // padded current swing angles + PxReal swingYPadded = PxAbs(swing.y) + mPadding; + PxReal swingZPadded = PxAbs(swing.z) + mPadding; + // if angle is within ellipse defined by mYMax/mZMax + return Ps::sqr(swingYPadded/mYMax)+Ps::sqr(swingZPadded/mZMax) <= 1; + } + + PX_FORCE_INLINE PxVec3 clamp(const PxVec3& swing, PxVec3& normal) const + { + // finds the closest point on the ellipse to a given point + PxVec3 p = Ps::ellipseClamp(swing, PxVec3(0,mYMax,mZMax)); + // normal to the point on ellipse + normal = PxVec3(0, p.y/Ps::sqr(mYMax), p.z/Ps::sqr(mZMax)); +#ifdef PX_PARANOIA_ELLIPSE_CHECK + PxReal err = PxAbs(Ps::sqr(p.y/mYMax) + Ps::sqr(p.z/mZMax) - 1); + PX_ASSERT(err<1e-3); +#endif + return p; + } + + // input is a swing quat, such that swing.x = twist.y = twist.z = 0, q = swing * twist + // The routine is agnostic to the sign of q.w (i.e. we don't need the minimal-rotation swing) + + // output is an axis such that positive rotation increases the angle outward from the + // limit (i.e. the image of the x axis), the error is the sine of the angular difference, + // positive if the twist axis is inside the cone + + bool getLimit(const PxQuat& swing, PxVec3& axis, PxReal& error) const + { + PX_ASSERT(swing.w>0); + PxVec3 twistAxis = swing.getBasisVector0(); + // get the angles from the swing quaternion + PxVec3 swingAngle(0.0f, 4 * PxAtan2(swing.y, 1 + swing.w), 4 * PxAtan2(swing.z, 1 + swing.w)); + if(contains(swingAngle)) + return false; + + PxVec3 normal, clamped = clamp(swingAngle, normal); + + // rotation vector and ellipse normal + PxVec3 r(0,PxTan(clamped.y/4),PxTan(clamped.z/4)), d(0, normal.y, normal.z); + + error = computeAxisAndError(r, d, twistAxis, axis); + + PX_ASSERT(PxAbs(axis.magnitude()-1)<1e-5f); + + return true; + } + + private: + PxReal mYMax, mZMax, mPadding; + }; + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmFlushPool.h b/src/PhysX/physx/source/common/src/CmFlushPool.h new file mode 100644 index 000000000..e9e9ebe47 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmFlushPool.h @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_FLUSHPOOL +#define PX_PHYSICS_COMMON_FLUSHPOOL + +#include "foundation/Px.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsMutex.h" +#include "PsArray.h" +#include "PsBitUtils.h" + +/* +Pool used to allocate variable sized tasks. It's intended to be cleared after a short period (time step). +*/ + +namespace physx +{ +namespace Cm +{ + static const PxU32 sSpareChunkCount = 2; + + class FlushPool + { + PX_NOCOPY(FlushPool) + public: + FlushPool(PxU32 chunkSize) : mChunks(PX_DEBUG_EXP("FlushPoolChunk")), mChunkIndex(0), mOffset(0), mChunkSize(chunkSize) + { + mChunks.pushBack(static_cast(PX_ALLOC(mChunkSize, "PxU8"))); + } + + ~FlushPool() + { + for (PxU32 i = 0; i < mChunks.size(); ++i) + PX_FREE(mChunks[i]); + } + + // alignment must be a power of two + void* allocate(PxU32 size, PxU32 alignment=16) + { + Ps::Mutex::ScopedLock lock(mMutex); + return allocateNotThreadSafe(size, alignment); + } + + // alignment must be a power of two + void* allocateNotThreadSafe(PxU32 size, PxU32 alignment=16) + { + PX_ASSERT(shdfnd::isPowerOfTwo(alignment)); + PX_ASSERT(size <= mChunkSize && !mChunks.empty()); + + // padding for alignment + size_t unalignedStart = reinterpret_cast(mChunks[mChunkIndex]+mOffset); + PxU32 pad = PxU32(((unalignedStart+alignment-1)&~(size_t(alignment)-1)) - unalignedStart); + + if (mOffset + size + pad > mChunkSize) + { + mChunkIndex++; + mOffset = 0; + if (mChunkIndex >= mChunks.size()) + mChunks.pushBack(static_cast(PX_ALLOC(mChunkSize, "PxU8"))); + + // update padding to ensure new alloc is aligned + unalignedStart = reinterpret_cast(mChunks[mChunkIndex]); + pad = PxU32(((unalignedStart+alignment-1)&~(size_t(alignment)-1)) - unalignedStart); + } + + void* ptr = mChunks[mChunkIndex] + mOffset + pad; + PX_ASSERT((reinterpret_cast(ptr)&(size_t(alignment)-1)) == 0); + mOffset += size + pad; + return ptr; + } + + void clear(PxU32 spareChunkCount = sSpareChunkCount) + { + Ps::Mutex::ScopedLock lock(mMutex); + + clearNotThreadSafe(spareChunkCount); + } + + void clearNotThreadSafe(PxU32 spareChunkCount = sSpareChunkCount) + { + PX_UNUSED(spareChunkCount); + + //release memory not used previously + PxU32 targetSize = mChunkIndex+sSpareChunkCount; + while (mChunks.size() > targetSize) + PX_FREE(mChunks.popBack()); + + mChunkIndex = 0; + mOffset = 0; + } + + void resetNotThreadSafe() + { + PxU8* firstChunk = mChunks[0]; + + for (PxU32 i = 1; i < mChunks.size(); ++i) + PX_FREE(mChunks[i]); + + mChunks.clear(); + mChunks.pushBack(firstChunk); + mChunkIndex = 0; + mOffset = 0; + } + + void lock() + { + mMutex.lock(); + } + + void unlock() + { + mMutex.unlock(); + } + + private: + Ps::Mutex mMutex; + Ps::Array mChunks; + PxU32 mChunkIndex; + PxU32 mOffset; + PxU32 mChunkSize; + }; + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmIDPool.h b/src/PhysX/physx/source/common/src/CmIDPool.h new file mode 100644 index 000000000..ea848face --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmIDPool.h @@ -0,0 +1,207 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_ID_POOL +#define PX_PHYSICS_COMMON_ID_POOL + +#include "foundation/Px.h" +#include "CmPhysXCommon.h" +#include "PsArray.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Cm +{ + template + class IDPoolBase : public Ps::UserAllocated + { + protected: + PxU32 mCurrentID; + FreeBuffer mFreeIDs; + public: + IDPoolBase() : mCurrentID(0) {} + + void freeID(PxU32 id) + { + // Allocate on first call + // Add released ID to the array of free IDs + if(id == (mCurrentID - 1)) + --mCurrentID; + else + mFreeIDs.pushBack(id); + } + + void freeAll() + { + mCurrentID = 0; + mFreeIDs.clear(); + } + + PxU32 getNewID() + { + // If recycled IDs are available, use them + const PxU32 size = mFreeIDs.size(); + if(size) + { + // Recycle last ID + return mFreeIDs.popBack(); + } + // Else create a new ID + return mCurrentID++; + } + + PxU32 getNumUsedID() const + { + return mCurrentID - mFreeIDs.size(); + } + + PxU32 getMaxID() const + { + return mCurrentID; + } + + }; + + //This class extends IDPoolBase. This is mainly used for when it is unsafe for the application to free the id immediately so that it can + //defer the free process until it is safe to do so + template + class DeferredIDPoolBase : public IDPoolBase + { + FreeBuffer mDeferredFreeIDs; + public: + //release an index into the deferred list + void deferredFreeID(PxU32 id) + { + mDeferredFreeIDs.pushBack(id); + } + + //release the deferred indices into the free list + void processDeferredIds() + { + const PxU32 deferredFreeIDCount = mDeferredFreeIDs.size(); + for(PxU32 a = 0; a < deferredFreeIDCount;++a) + { + IDPoolBase::freeID(mDeferredFreeIDs[a]); + } + mDeferredFreeIDs.clear(); + } + + //release all indices + void freeAll() + { + mDeferredFreeIDs.clear(); + IDPoolBase::freeAll(); + } + + PxU32 getNumUsedID() const + { + return IDPoolBase::getNumUsedID() - mDeferredFreeIDs.size(); + } + + FreeBuffer& getDeferredFreeIDs() { return mDeferredFreeIDs; } + }; + + //This is spu friendly fixed size array + template + class InlineFixedArray + { + T mArr[N]; + PxU32 mSize; + public: + + InlineFixedArray() : mSize(0) + { + } + + ~InlineFixedArray(){} + + void pushBack(const T& t) + { + PX_ASSERT(mSize < N); + mArr[mSize++] = t; + } + + T popBack() + { + PX_ASSERT(mSize > 0); + return mArr[--mSize]; + } + + void clear() { mSize = 0; } + + T& operator [] (PxU32 index) { PX_ASSERT(index < N); return mArr[index]; } + + const T& operator [] (PxU32 index) const { PX_ASSERT(index < N); return mArr[index]; } + + PxU32 size() const { return mSize; } + }; + + //Fix size IDPool + template + class InlineIDPool : public IDPoolBase > + { + public: + PxU32 getNumRemainingIDs() + { + return Capacity - this->getNumUsedID(); + } + }; + + //Dynamic resize IDPool + class IDPool : public IDPoolBase > + { + }; + + + //This class is used to recycle indices. It supports deferred release, so that until processDeferredIds is called, + //released indices will not be reallocated. This class will fail if the calling code request more id than the InlineDeferredIDPoll + //has. It is the calling code's responsibility to ensure that this does not happen. + template + class InlineDeferredIDPool : public DeferredIDPoolBase > + { + public: + PxU32 getNumRemainingIDs() + { + return Capacity - IDPoolBase< InlineFixedArray >::getNumUsedID(); + } + }; + + //Dynamic resize DeferredIDPool + class DeferredIDPool : public DeferredIDPoolBase > + { + + }; + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmIO.h b/src/PhysX/physx/source/common/src/CmIO.h new file mode 100644 index 000000000..5d290a0fb --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmIO.h @@ -0,0 +1,136 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_IO +#define PX_PHYSICS_COMMON_IO + +#include "foundation/PxIO.h" +#include "foundation/PxAssert.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + + // wrappers for IO classes so that we can add extra functionality (byte counting, buffering etc) + +namespace Cm +{ + +class InputStreamReader +{ +public: + + InputStreamReader(PxInputStream& stream) : mStream(stream) { } + PxU32 read(void* dest, PxU32 count) + { + PxU32 readLength = mStream.read(dest, count); + + // zero the buffer if we didn't get all the data + if(readLength(dest)+readLength, count-readLength); + + return readLength; + } + + template T get() + { + T val; + PxU32 length = mStream.read(&val, sizeof(T)); + PX_ASSERT(length == sizeof(T)); + PX_UNUSED(length); + return val; + } + + +protected: + PxInputStream &mStream; +private: + InputStreamReader& operator=(const InputStreamReader&); +}; + + +class InputDataReader : public InputStreamReader +{ +public: + InputDataReader(PxInputData& data) : InputStreamReader(data) {} + InputDataReader &operator=(const InputDataReader &); + + PxU32 length() const { return getData().getLength(); } + void seek(PxU32 offset) { getData().seek(offset); } + PxU32 tell() { return getData().tell(); } + +private: + PxInputData& getData() { return static_cast(mStream); } + const PxInputData& getData() const { return static_cast(mStream); } +}; + + +class OutputStreamWriter +{ +public: + + PX_INLINE OutputStreamWriter(PxOutputStream& stream) + : mStream(stream) + , mCount(0) + {} + + PX_INLINE PxU32 write(const void* src, PxU32 offset) + { + PxU32 count = mStream.write(src, offset); + mCount += count; + return count; + } + + PX_INLINE PxU32 getStoredSize() + { + return mCount; + } + + template void put(const T& val) + { + PxU32 length = write(&val, sizeof(T)); + PX_ASSERT(length == sizeof(T)); + PX_UNUSED(length); + } + +private: + + OutputStreamWriter& operator=(const OutputStreamWriter&); + PxOutputStream& mStream; + PxU32 mCount; +}; + + + +} +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmMathUtils.cpp b/src/PhysX/physx/source/common/src/CmMathUtils.cpp new file mode 100644 index 000000000..70c71326e --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmMathUtils.cpp @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMathUtils.h" +#include "common/PxPhysXCommonConfig.h" + +namespace physx +{ + + +PX_PHYSX_COMMON_API PxTransform PxTransformFromPlaneEquation(const PxPlane& plane) +{ + PxPlane p = plane; + p.normalize(); + + // special case handling for axis aligned planes + const PxReal halfsqrt2 = 0.707106781f; + PxQuat q; + if(2 == (p.n.x == 0.0f) + (p.n.y == 0.0f) + (p.n.z == 0.0f)) // special handling for axis aligned planes + { + if(p.n.x > 0) q = PxQuat(PxIdentity); + else if(p.n.x < 0) q = PxQuat(0, 0, 1.0f, 0); + else q = PxQuat(0.0f, -p.n.z, p.n.y, 1.0f) * halfsqrt2; + } + else q = PxShortestRotation(PxVec3(1.f,0,0), p.n); + + return PxTransform(-p.n * p.d, q); + +} + +PX_PHYSX_COMMON_API PxTransform PxTransformFromSegment(const PxVec3& p0, const PxVec3& p1, PxReal* halfHeight) +{ + const PxVec3 axis = p1-p0; + const PxReal height = axis.magnitude(); + if(halfHeight) + *halfHeight = height/2; + + return PxTransform((p1+p0) * 0.5f, + height<1e-6f ? PxQuat(PxIdentity) : PxShortestRotation(PxVec3(1.f,0,0), axis/height)); +} + +} diff --git a/src/PhysX/physx/source/common/src/CmMatrix34.h b/src/PhysX/physx/source/common/src/CmMatrix34.h new file mode 100644 index 000000000..01d8f34ea --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmMatrix34.h @@ -0,0 +1,286 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_MATRIX34 +#define PX_PHYSICS_COMMON_MATRIX34 + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Cm +{ + +/*! +Basic mathematical 3x4 matrix, implemented as a 3x3 rotation matrix and a translation + +See PxMat33 for the format of the rotation matrix. + +*/ + +class Matrix34 +{ +public: + //! Default constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34() + {} + + //! Construct from four base vectors + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const PxVec3& b0, const PxVec3& b1, const PxVec3& b2, const PxVec3& b3) + : m(b0, b1, b2), p(b3) + {} + + //! Construct from float[12] + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(PxReal values[]): + m(values), p(values[9], values[10], values[11]) + { + } + + //! Construct from a 3x3 matrix + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const PxMat33& other) + : m(other), p(PxZero) + { + } + + //! Construct from a 3x3 matrix and a translation vector + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const PxMat33& other, const PxVec3& t) + : m(other), p(t) + {} + + //! Construct from a PxTransform + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const PxTransform& other): + m(other.q), p(other.p) + { + } + + //! Construct from a quaternion + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const PxQuat& q): + m(q), p(PxZero) + { + } + + //! Copy constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const Matrix34& other): + m(other.m), p(other.p) + { + } + + //! Assignment operator + PX_CUDA_CALLABLE PX_FORCE_INLINE const Matrix34& operator=(const Matrix34& other) + { + m = other.m; + p = other.p; + return *this; + } + + //! Set to identity matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE void setIdentity() + { + m = PxMat33(PxIdentity); + p = PxVec3(0); + } + + // Simpler operators + //! Equality operator + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator==(const Matrix34& other) const + { + return m == other.m && p == other.p; + } + + //! Inequality operator + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator!=(const Matrix34& other) const + { + return !operator==(other); + } + + //! Unary minus + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator-() const + { + return Matrix34(-m, -p); + } + + //! Add + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator+(const Matrix34& other) const + { + return Matrix34(m + other.m, p + other.p); + } + + //! Subtract + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator-(const Matrix34& other) const + { + return Matrix34(m - other.m, p - other.p); + } + + //! Scalar multiplication + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator*(PxReal scalar) const + { + return Matrix34(m*scalar, p*scalar); + } + + friend Matrix34 operator*(PxReal, const Matrix34&); + + //! Matrix multiplication + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator*(const Matrix34& other) const + { + //Rows from this columns from other + //base0 = rotate(other.m.column0) etc + return Matrix34(m*other.m, m*other.p + p); + } + + //! Matrix multiplication, extend the second matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator*(const PxMat33& other) const + { + //Rows from this columns from other + //base0 = transform(other.m.column0) etc + return Matrix34(m*other, p); + } + + friend Matrix34 operator*(const PxMat33& a, const Matrix34& b); + + // a = b operators + + //! Equals-add + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34& operator+=(const Matrix34& other) + { + m += other.m; + p += other.p; + return *this; + } + + //! Equals-sub + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34& operator-=(const Matrix34& other) + { + m -= other.m; + p -= other.p; + return *this; + } + + //! Equals scalar multiplication + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34& operator*=(PxReal scalar) + { + m *= scalar; + p *= scalar; + + return *this; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal operator()(PxU32 row, PxU32 col) const + { + return (*this)[col][row]; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal& operator()(PxU32 row, PxU32 col) + { + return (*this)[col][row]; + } + + // Transform etc + + //! Transform vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 rotate(const PxVec3& other) const + { + return m*other; + } + + //! Transform vector by transpose of matrix, equal to v' = M^t*v + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 rotateTranspose(const PxVec3& other) const + { + return m.transformTranspose(other); + } + + //! Transform point by matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 transform(const PxVec3& other) const + { + return m*other + p; + } + + //! Transform point by transposed matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 transformTranspose(const PxVec3& other) const + { + return m.transformTranspose(other - p); + } + + //! Transform point by transposed matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::Matrix34 transformTranspose(const Cm::Matrix34& other) const + { + return Cm::Matrix34(m.transformTranspose(other.m.column0), + m.transformTranspose(other.m.column1), + m.transformTranspose(other.m.column2), + m.transformTranspose(other.p - p)); + } + + + //! Invert matrix treating it as a rotation+translation matrix only + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 getInverseRT() const + { + return Matrix34(m.getTranspose(), m.transformTranspose(-p)); + } + + + // Conversion + //! Set matrix from quaternion + PX_CUDA_CALLABLE PX_FORCE_INLINE void set(const PxQuat& q) + { + m = PxMat33(q); + p = PxVec3(PxZero); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator[](unsigned int num){return (&m.column0)[num];} + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator[](int num) { return (&m.column0)[num]; } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& operator[](unsigned int num) const { return (&m.column0)[num]; } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& operator[](int num) const { return (&m.column0)[num]; } + + //Data, see above for format! + + PxMat33 m; + PxVec3 p; + +}; + + +//! Multiply a*b, a is extended +PX_INLINE Matrix34 operator*(const PxMat33& a, const Matrix34& b) +{ + return Matrix34(a * b.m, a * b.p); +} + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmPhysXCommon.h b/src/PhysX/physx/source/common/src/CmPhysXCommon.h new file mode 100644 index 000000000..6ff0387c6 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPhysXCommon.h @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON +#define PX_PHYSICS_COMMON + +//! \file Top level internal include file for PhysX SDK + +#include "Ps.h" + +// Enable debug visualization +#define PX_ENABLE_DEBUG_VISUALIZATION 1 + +// Enable simulation statistics generation +#define PX_ENABLE_SIM_STATS 1 + +// PT: typical "invalid" value in various CD algorithms +#define PX_INVALID_U32 0xffffffff +#define PX_INVALID_U16 0xffff + +// PT: this used to be replicated everywhere in the code, causing bugs to sometimes reappear (e.g. TTP 3587). +// It is better to define it in a header and use the same constant everywhere. The original value (1e-05f) +// caused troubles (e.g. TTP 1705, TTP 306). +#define PX_PARALLEL_TOLERANCE 1e-02f + +namespace physx +{ + // alias shared foundation to something usable + namespace Ps = shdfnd; +} + +#if PX_CHECKED + #define PX_CHECK_MSG(exp, msg) (!!(exp) || (physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, msg), 0) ) + #define PX_CHECK(exp) PX_CHECK_MSG(exp, #exp) + #define PX_CHECK_AND_RETURN(exp,msg) { if(!(exp)) { PX_CHECK_MSG(exp, msg); return; } } + #define PX_CHECK_AND_RETURN_NULL(exp,msg) { if(!(exp)) { PX_CHECK_MSG(exp, msg); return 0; } } + #define PX_CHECK_AND_RETURN_VAL(exp,msg,r) { if(!(exp)) { PX_CHECK_MSG(exp, msg); return r; } } +#else + #define PX_CHECK_MSG(exp, msg) + #define PX_CHECK(exp) + #define PX_CHECK_AND_RETURN(exp,msg) + #define PX_CHECK_AND_RETURN_NULL(exp,msg) + #define PX_CHECK_AND_RETURN_VAL(exp,msg,r) +#endif + +#if PX_VC + // VC compiler defines __FUNCTION__ as a string literal so it is possible to concatenate it with another string + // Example: #define PX_CHECK_VALID(x) PX_CHECK_MSG(physx::shdfnd::checkValid(x), __FUNCTION__ ": parameter invalid!") + #define PX_CHECK_VALID(x) PX_CHECK_MSG(physx::shdfnd::checkValid(x), __FUNCTION__) +#elif PX_GCC_FAMILY + // GCC compiler defines __FUNCTION__ as a variable, hence, it is NOT possible concatenate an additional string to it + // In GCC, __FUNCTION__ only returns the function name, using __PRETTY_FUNCTION__ will return the full function definition + #define PX_CHECK_VALID(x) PX_CHECK_MSG(physx::shdfnd::checkValid(x), __PRETTY_FUNCTION__) +#else + // Generic macro for other compilers + #define PX_CHECK_VALID(x) PX_CHECK_MSG(physx::shdfnd::checkValid(x), __FUNCTION__) +#endif + + +#endif diff --git a/src/PhysX/physx/source/common/src/CmPool.h b/src/PhysX/physx/source/common/src/CmPool.h new file mode 100644 index 000000000..a28ea0307 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPool.h @@ -0,0 +1,292 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef CM_POOL_H +#define CM_POOL_H + +#include "PsSort.h" +#include "PsMutex.h" +#include "PsBasicTemplates.h" + +#include "CmBitMap.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Cm +{ + +/*! +Allocator for pools of data structures +Also decodes indices (which can be computed from handles) into objects. To make this +faster, the EltsPerSlab must be a power of two +*/ +template +class PoolList : public Ps::AllocatorTraits::Type +{ + typedef typename Ps::AllocatorTraits::Type Alloc; + PX_NOCOPY(PoolList) +public: + PX_INLINE PoolList(const Alloc& alloc, ArgumentType* argument, PxU32 eltsPerSlab, PxU32 maxSlabs) + : Alloc(alloc), + mEltsPerSlab(eltsPerSlab), + mMaxSlabs(maxSlabs), + mSlabCount(0), + mFreeList(0), + mFreeCount(0), + mSlabs(reinterpret_cast(Alloc::allocate(maxSlabs * sizeof(T*), __FILE__, __LINE__))), + mArgument(argument) + { + PX_ASSERT(mEltsPerSlab>0); + // either maxSlabs = 1 (non-resizable pool), or elts per slab must be a power of two + PX_ASSERT((maxSlabs==1) || ((maxSlabs < 8192) && (mEltsPerSlab & (mEltsPerSlab-1))) == 0); + mLog2EltsPerSlab = 0; + + if(mMaxSlabs>1) + { + for(mLog2EltsPerSlab=0; mEltsPerSlab!=PxU32(1< mFreeCount ? nbRequired - mFreeCount : 0; + + PxU32 nbElements = nbRequired - nbToAllocate; + + PxMemCopy(elements, mFreeList + (mFreeCount - nbElements), sizeof(T*) * nbElements); + //PxU32 originalFreeCount = mFreeCount; + mFreeCount -= nbElements; + + if (nbToAllocate) + { + PX_ASSERT(mFreeCount == 0); + + PxU32 nbSlabs = (nbToAllocate + mEltsPerSlab - 1) / mEltsPerSlab; //The number of slabs we need to allocate... + + if (mSlabCount + nbSlabs >= mMaxSlabs) + return nbElements; //Return only nbFromFree because we're not going to allocate any slabs. Seriously, we need to nuke this "maxSlabs" stuff ASAP! + + //allocate our slabs... + + PxU32 freeCount = mFreeCount; + + for (PxU32 i = 0; i < nbSlabs; ++i) + { + + //KS - would be great to allocate this using a single allocation but it will make releasing slabs fail later :( + T * mAddr = reinterpret_cast(Alloc::allocate(mEltsPerSlab * sizeof(T), __FILE__, __LINE__)); + if (!mAddr) + return nbElements; //Allocation failed so only return the set of elements we could allocate from the free list + + mSlabs[mSlabCount++] = mAddr; + + // Make sure the usage bitmap is up-to-size + if (mUseBitmap.size() < mSlabCount*mEltsPerSlab) + { + mUseBitmap.resize(2 * mSlabCount*mEltsPerSlab); //set last element as not used + if (mFreeList) + PX_FREE(mFreeList); + mFreeList = reinterpret_cast(Alloc::allocate(2 * mSlabCount * mEltsPerSlab * sizeof(T*), __FILE__, __LINE__)); + } + + PxU32 baseIndex = (mSlabCount-1) * mEltsPerSlab; + + //Now add all these to the mFreeList and elements... + PxI32 idx = PxI32(mEltsPerSlab - 1); + + for (; idx >= PxI32(nbToAllocate); --idx) + { + mFreeList[freeCount++] = new(mAddr + idx) T(mArgument, baseIndex + idx); + } + + PxU32 origElements = nbElements; + T** writeIdx = elements + nbElements; + for (; idx >= 0; --idx) + { + writeIdx[idx] = new(mAddr + idx) T(mArgument, baseIndex + idx); + nbElements++; + } + + nbToAllocate -= (nbElements - origElements); + } + + mFreeCount = freeCount; + } + + PX_ASSERT(nbElements == nbRequired); + + for (PxU32 a = 0; a < nbElements; ++a) + { + mUseBitmap.set(elements[a]->getIndex()); + } + + return nbRequired; + } + + // TODO: would be nice to add templated construct/destroy methods like ObjectPool + + PX_INLINE T* get() + { + if(mFreeCount == 0 && !extend()) + return 0; + T* element = mFreeList[--mFreeCount]; + mUseBitmap.set(element->getIndex()); + return element; + } + + PX_INLINE void put(T* element) + { + PxU32 i = element->getIndex(); + mUseBitmap.reset(i); + mFreeList[mFreeCount++] = element; + } + + /* + WARNING: Unlike findByIndexFast below, this method is NOT safe to use if another thread + is concurrently updating the pool (e.g. through put/get/extend/getIterator), since the + safety boundedTest uses mSlabCount and mUseBitmap. + */ + PX_FORCE_INLINE T* findByIndex(PxU32 index) const + { + if(index>=mSlabCount*mEltsPerSlab || !(mUseBitmap.boundedTest(index))) + return 0; + return mMaxSlabs==1 ? mSlabs[0]+index : mSlabs[index>>mLog2EltsPerSlab] + (index&(mEltsPerSlab-1)); + } + + /* + This call is safe to do while other threads update the pool. + */ + PX_FORCE_INLINE T* findByIndexFast(PxU32 index) const + { + PX_ASSERT(mMaxSlabs != 1); + return mSlabs[index>>mLog2EltsPerSlab] + (index&(mEltsPerSlab-1)); + } + + bool extend() + { + if(mSlabCount == mMaxSlabs) + return false; + T * mAddr = reinterpret_cast(Alloc::allocate(mEltsPerSlab * sizeof(T), __FILE__, __LINE__)); + if(!mAddr) + return false; + mSlabs[mSlabCount++] = mAddr; + + + + // Make sure the usage bitmap is up-to-size + if(mUseBitmap.size() < mSlabCount*mEltsPerSlab) + { + mUseBitmap.resize(2*mSlabCount*mEltsPerSlab); //set last element as not used + if(mFreeList) + PX_FREE(mFreeList); + mFreeList = reinterpret_cast(Alloc::allocate(2*mSlabCount * mEltsPerSlab * sizeof(T*), __FILE__, __LINE__)); + } + + // Add to free list in descending order so that lowest indices get allocated first - + // the FW context code currently *relies* on this behavior to grab the zero-index volume + // which can't be allocated to the user. TODO: fix this + + PxU32 baseIndex = (mSlabCount-1) * mEltsPerSlab; + PxU32 freeCount = mFreeCount; + for(PxI32 i=PxI32(mEltsPerSlab-1);i>=0;i--) + mFreeList[freeCount++] = new(mAddr+i) T(mArgument, baseIndex+ i); + + mFreeCount = freeCount; + + return true; + } + + PX_INLINE PxU32 getMaxUsedIndex() const + { + return mUseBitmap.findLast(); + } + + PX_INLINE BitMap::Iterator getIterator() const + { + return BitMap::Iterator(mUseBitmap); + } + +private: + const PxU32 mEltsPerSlab; + const PxU32 mMaxSlabs; + PxU32 mSlabCount; + PxU32 mLog2EltsPerSlab; + T** mFreeList; + PxU32 mFreeCount; + T** mSlabs; + ArgumentType* mArgument; + BitMap mUseBitmap; +}; + + +} +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmPreallocatingPool.h b/src/PhysX/physx/source/common/src/CmPreallocatingPool.h new file mode 100644 index 000000000..e3b9ece83 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPreallocatingPool.h @@ -0,0 +1,435 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_PREALLOCATINGPOOL +#define PX_PHYSICS_COMMON_PREALLOCATINGPOOL + +#include "foundation/Px.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsSort.h" +#include "PsArray.h" + +/* +Pool used to allocate variable sized tasks. It's intended to be cleared after a short period (time step). +*/ + +namespace physx +{ +namespace Cm +{ + +class PreallocatingRegion +{ +public: + PX_FORCE_INLINE PreallocatingRegion() : mMemory(NULL), mFirstFree(NULL), mNbElements(0) {} + + void init(PxU32 maxElements, PxU32 elementSize, const char* typeName) + { + mFirstFree = NULL; + mNbElements = 0; + PX_ASSERT(typeName); + PX_UNUSED(typeName); + mMemory = reinterpret_cast(PX_ALLOC(sizeof(PxU8)*elementSize*maxElements, typeName?typeName:"SceneSim Pool")); // ### addActor alloc + PX_ASSERT(elementSize*maxElements>=sizeof(void*)); + } + + void reset() + { + PX_FREE_AND_RESET(mMemory); + } + + PX_FORCE_INLINE PxU8* allocateMemory(PxU32 maxElements, PxU32 elementSize) + { + if(mFirstFree) + { + PxU8* recycled = reinterpret_cast(mFirstFree); + + void** recycled32 = reinterpret_cast(recycled); + mFirstFree = *recycled32; + + return recycled; + } + else + { + if(mNbElements==maxElements) + return NULL; // Out of memory + + const PxU32 freeIndex = mNbElements++; + return mMemory + freeIndex * elementSize; + } + } + + void deallocateMemory(PxU32 maxElements, PxU32 elementSize, PxU8* element) + { + PX_ASSERT(element); + PX_ASSERT(element>=mMemory && element(element); + *recycled32 = mFirstFree; + + mFirstFree = element; + } + + PX_FORCE_INLINE bool operator < (const PreallocatingRegion& p) const + { + return mMemory < p.mMemory; + } + + PX_FORCE_INLINE bool operator > (const PreallocatingRegion& p) const + { + return mMemory > p.mMemory; + } + + PxU8* mMemory; + void* mFirstFree; + PxU32 mNbElements; +}; + + + +class PreallocatingRegionManager +{ + public: + PreallocatingRegionManager(PxU32 maxElements, PxU32 elementSize, const char* typeName) + : mMaxElements (maxElements) + , mElementSize (elementSize) + , mActivePoolIndex (0) + , mPools(PX_DEBUG_EXP("MyPoolManagerPools")) + , mNeedsSorting (true) + , mTypeName (typeName) + { + PreallocatingRegion tmp; + tmp.init(maxElements, elementSize, mTypeName); + mPools.pushBack(tmp); + } + + ~PreallocatingRegionManager() + { + const PxU32 nbPools = mPools.size(); + for(PxU32 i=0;iavailableSpace) + { + PreallocatingRegion tmp; + tmp.init(maxElements, elementSize, mTypeName); + mPools.pushBack(tmp); + + availableSpace += maxElements; + } + } + + PX_FORCE_INLINE PxU8* allocateMemory() + { + PX_ASSERT(mActivePoolIndex>1; + + PreallocatingRegion& candidate = mPools[PxU32(mid)]; + if(contains(candidate.mMemory, slabSize, element)) + { + candidate.deallocateMemory(maxElements, elementSize, element); + + // when we sorted earlier we trashed the active index, but at least this region has a free element + if(mNeedsSorting) + mActivePoolIndex = PxU32(mid); + + mNeedsSorting = false; + return; + } + + if(candidate.mMemory=memory && element mPools; + bool mNeedsSorting; + const char* mTypeName; +}; + +template +class PreallocatingPool : public Ps::UserAllocated +{ + PreallocatingPool& operator=(const PreallocatingPool&); + +public: + PreallocatingPool(PxU32 maxElements, const char* typeName) : mPool(maxElements, sizeof(T), typeName) + { + } + + ~PreallocatingPool() + { + } + + PX_FORCE_INLINE void preAllocate(PxU32 n) + { + mPool.preAllocate(n); + } + + PX_INLINE T* allocate() + { + return reinterpret_cast(mPool.allocateMemory()); + } + + PX_FORCE_INLINE T* allocateAndPrefetch() + { + T* t = reinterpret_cast(mPool.allocateMemory()); + Ps::prefetch(t, sizeof(T)); + return t; + } + + PX_INLINE T* construct() + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T() : 0; + } + + template + PX_INLINE T* construct(A1& a) + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T(a) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b) + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T(a,b) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c) + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T(a,b,c) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c, A4& d) + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T(a,b,c,d) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c, A4& d, A5& e) + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T(a,b,c,d,e) : 0; + } + + //// + + PX_INLINE T* construct(T* t) + { + PX_ASSERT(t); + return new (t) T(); + } + + template + PX_INLINE T* construct(T* t, A1& a) + { + PX_ASSERT(t); + return new (t) T(a); + } + + template + PX_INLINE T* construct(T* t, A1& a, A2& b) + { + PX_ASSERT(t); + return new (t) T(a,b); + } + + template + PX_INLINE T* construct(T* t, A1& a, A2& b, A3& c) + { + PX_ASSERT(t); + return new (t) T(a,b,c); + } + + template + PX_INLINE T* construct(T* t, A1& a, A2& b, A3& c, A4& d) + { + PX_ASSERT(t); + return new (t) T(a,b,c,d); + } + + template + PX_INLINE T* construct(T* t, A1& a, A2& b, A3& c, A4& d, A5& e) + { + PX_ASSERT(t); + return new (t) T(a,b,c,d,e); + } + + PX_INLINE void destroy(T* const p) + { + if(p) + { + p->~T(); + mPool.deallocateMemory(reinterpret_cast(p)); + } + } + + PX_INLINE void releasePreallocated(T* const p) + { + if(p) + mPool.deallocateMemory(reinterpret_cast(p)); + } +protected: + PreallocatingRegionManager mPool; +}; + +template +class BufferedPreallocatingPool : public PreallocatingPool +{ + Ps::Array mDeletedElems; + PX_NOCOPY(BufferedPreallocatingPool) +public: + BufferedPreallocatingPool(PxU32 maxElements, const char* typeName) : PreallocatingPool(maxElements, typeName) + { + } + + PX_INLINE void destroy(T* const p) + { + if (p) + { + p->~T(); + mDeletedElems.pushBack(p); + } + } + + void processPendingDeletedElems() + { + for (PxU32 i = 0; i < mDeletedElems.size(); ++i) + this->mPool.deallocateMemory(reinterpret_cast(mDeletedElems[i])); + mDeletedElems.clear(); + } + + +}; + + + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmPriorityQueue.h b/src/PhysX/physx/source/common/src/CmPriorityQueue.h new file mode 100644 index 000000000..a1eeef523 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPriorityQueue.h @@ -0,0 +1,237 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_PRIORITYQUEUE +#define PX_PHYSICS_COMMON_PRIORITYQUEUE + +#include "PsBasicTemplates.h" +#include "CmPhysXCommon.h" +#include "PsAllocator.h" +#include "foundation/PxMemory.h" + +namespace physx +{ +namespace Cm +{ + template > + class PriorityQueueBase : protected Comparator // inherit so that stateless comparators take no space + { + public: + PriorityQueueBase(const Comparator& less, Element* elements) : Comparator(less), mHeapSize(0), mDataPtr(elements) + { + } + + ~PriorityQueueBase() + { + } + + //! Get the element with the highest priority + PX_FORCE_INLINE const Element top() const + { + return mDataPtr[0]; + } + + //! Get the element with the highest priority + PX_FORCE_INLINE Element top() + { + return mDataPtr[0]; + } + + //! Check to whether the priority queue is empty + PX_FORCE_INLINE bool empty() const + { + return (mHeapSize == 0); + } + + //! Empty the priority queue + PX_FORCE_INLINE void clear() + { + mHeapSize = 0; + } + + //! Insert a new element into the priority queue. Only valid when size() is less than Capacity + PX_FORCE_INLINE void push(const Element& value) + { + PxU32 newIndex; + PxU32 parentIndex = parent(mHeapSize); + + for (newIndex = mHeapSize; newIndex > 0 && compare(value, mDataPtr[parentIndex]); newIndex = parentIndex, parentIndex= parent(newIndex)) + { + mDataPtr[ newIndex ] = mDataPtr[parentIndex]; + } + mDataPtr[newIndex] = value; + mHeapSize++; + PX_ASSERT(valid()); + } + + //! Delete the highest priority element. Only valid when non-empty. + PX_FORCE_INLINE Element pop() + { + PX_ASSERT(mHeapSize > 0); + PxU32 i, child; + //try to avoid LHS + PxU32 tempHs = mHeapSize-1; + mHeapSize = tempHs; + Element min = mDataPtr[0]; + Element last = mDataPtr[tempHs]; + + for (i = 0; (child = left(i)) < tempHs; i = child) + { + /* Find highest priority child */ + const PxU32 rightChild = child + 1; + + child += ((rightChild < tempHs) & compare((mDataPtr[rightChild]), (mDataPtr[child]))) ? 1 : 0; + + if(compare(last, mDataPtr[child])) + break; + + mDataPtr[i] = mDataPtr[child]; + } + mDataPtr[ i ] = last; + + PX_ASSERT(valid()); + return min; + } + + //! Make sure the priority queue sort all elements correctly + bool valid() const + { + const Element& min = mDataPtr[0]; + for(PxU32 i=1; i> 1; + } + private: + PriorityQueueBase& operator = (const PriorityQueueBase); + }; + + template + class InlinePriorityQueue : public PriorityQueueBase + { + Element mData[Capacity]; + public: + InlinePriorityQueue(const Comparator& less = Comparator()) : PriorityQueueBase(less, mData) + { + } + + PX_FORCE_INLINE void push(Element& elem) + { + PX_ASSERT(this->mHeapSize < Capacity); + PriorityQueueBase::push(elem); + } + private: + InlinePriorityQueue& operator = (const InlinePriorityQueue); + }; + + template ::Type> + class PriorityQueue : public PriorityQueueBase, protected Alloc + { + PxU32 mCapacity; + public: + PriorityQueue(const Comparator& less = Comparator(), PxU32 initialCapacity = 0, Alloc alloc = Alloc()) + : PriorityQueueBase(less, NULL), Alloc(alloc), mCapacity(initialCapacity) + { + if(initialCapacity > 0) + this->mDataPtr = reinterpret_cast(Alloc::allocate(sizeof(Element)*initialCapacity, __FILE__, __LINE__)); + } + + ~PriorityQueue() + { + if(this->mDataPtr) + this->deallocate(this->mDataPtr); + } + + PX_FORCE_INLINE void push(Element& elem) + { + if(this->mHeapSize == mCapacity) + { + reserve((this->mHeapSize+1)*2); + } + PriorityQueueBase::push(elem); + } + + PX_FORCE_INLINE PxU32 capacity() + { + return mCapacity; + } + + PX_FORCE_INLINE void reserve(const PxU32 newCapacity) + { + if(newCapacity > mCapacity) + { + Element* newElems = reinterpret_cast(Alloc::allocate(sizeof(Element)*newCapacity, __FILE__, __LINE__)); + if(this->mDataPtr) + { + physx::PxMemCopy(newElems, this->mDataPtr, sizeof(Element) * this->mHeapSize); + Alloc::deallocate(this->mDataPtr); + } + this->mDataPtr = newElems; + mCapacity = newCapacity; + } + } + + private: + PriorityQueue& operator = (const PriorityQueue); + }; + +} +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmPtrTable.cpp b/src/PhysX/physx/source/common/src/CmPtrTable.cpp new file mode 100644 index 000000000..2341f0a3b --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPtrTable.cpp @@ -0,0 +1,207 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" +#include "foundation/PxMemory.h" +#include "CmPtrTable.h" +#include "CmUtils.h" +#include "PxMetaData.h" +#include "PsBitUtils.h" + +using namespace physx; +using namespace Cm; + +PtrTable::PtrTable() +: mList(NULL) +, mCount(0) +, mOwnsMemory(true) +, mBufferUsed(false) +{ +} + +PtrTable::~PtrTable() +{ + PX_ASSERT(mOwnsMemory); + PX_ASSERT(mCount == 0); + PX_ASSERT(mList == NULL); +} + +void PtrTable::clear(PtrTableStorageManager& sm) +{ + if(mOwnsMemory && mCount>1) + { + PxU32 implicitCapacity = Ps::nextPowerOfTwo(PxU32(mCount)-1); + sm.deallocate(mList, sizeof(void*)*implicitCapacity); + } + + mList = NULL; + mOwnsMemory = true; + mCount = 0; +} + +PxU32 PtrTable::find(const void* ptr) const +{ + const PxU32 nbPtrs = mCount; + void*const * PX_RESTRICT ptrs = getPtrs(); + + for(PxU32 i=0; i1) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mList, sizeof(void*)*mCount); + } +} + +void PtrTable::importExtraData(PxDeserializationContext& context) +{ + if(mCount>1) + mList = context.readExtraData(mCount); +} + +void PtrTable::realloc(PxU32 oldCapacity, PxU32 newCapacity, PtrTableStorageManager& sm) +{ + PX_ASSERT((mOwnsMemory && oldCapacity) || (!mOwnsMemory && oldCapacity == 0)); + PX_ASSERT(newCapacity); + + if(mOwnsMemory && sm.canReuse(oldCapacity, newCapacity)) + return; + + void** newMem = sm.allocate(newCapacity * sizeof(void*)); + PxMemCopy(newMem, mList, mCount * sizeof(void*)); + + if(mOwnsMemory) + sm.deallocate(mList, oldCapacity*sizeof(void*)); + + mList = newMem; + mOwnsMemory = true; +} + +void PtrTable::add(void* ptr, PtrTableStorageManager& sm) +{ + if(mCount == 0) // 0 -> 1, easy case + { + PX_ASSERT(mOwnsMemory); + PX_ASSERT(mList == NULL); + PX_ASSERT(!mBufferUsed); + mSingle = ptr; + mCount = 1; + mBufferUsed = true; + return; + } + + if(mCount == 1) // 1 -> 2, easy case + { + PX_ASSERT(mOwnsMemory); + PX_ASSERT(mBufferUsed); + + void* single = mSingle; + mList = sm.allocate(2*sizeof(void*)); + mList[0] = single; + mBufferUsed = false; + mOwnsMemory = true; + } + else + { + PX_ASSERT(!mBufferUsed); + + if(!mOwnsMemory) // don't own the memory, must always alloc + realloc(0, Ps::nextPowerOfTwo(mCount), sm); // we're guaranteed nextPowerOfTwo(x) > x + + else if(Ps::isPowerOfTwo(mCount)) // count is at implicit capacity, so realloc + realloc(mCount, PxU32(mCount)*2, sm); // ... to next higher power of 2 + + PX_ASSERT(mOwnsMemory); + } + + mList[mCount++] = ptr; +} + +void PtrTable::replaceWithLast(PxU32 index, PtrTableStorageManager& sm) +{ + PX_ASSERT(mCount!=0); + + if(mCount == 1) // 1 -> 0 easy case + { + PX_ASSERT(mOwnsMemory); + PX_ASSERT(mBufferUsed); + + mList = NULL; + mCount = 0; + mBufferUsed = false; + } + else if(mCount == 2) // 2 -> 1 easy case + { + PX_ASSERT(!mBufferUsed); + void* ptr = mList[1-index]; + if(mOwnsMemory) + sm.deallocate(mList, 2*sizeof(void*)); + mSingle = ptr; + mCount = 1; + mBufferUsed = true; + mOwnsMemory = true; + } + else + { + PX_ASSERT(!mBufferUsed); + + mList[index] = mList[--mCount]; // remove before adjusting memory + + if(!mOwnsMemory) // don't own the memory, must alloc + realloc(0, Ps::nextPowerOfTwo(PxU32(mCount)-1), sm); // if currently a power of 2, don't jump to the next one + + else if(Ps::isPowerOfTwo(mCount)) // own the memory, and implicit capacity requires that we downsize + realloc(PxU32(mCount)*2, PxU32(mCount), sm); // ... from the next power of 2, which was the old implicit capacity + + PX_ASSERT(mOwnsMemory); + } +} + +void Cm::PtrTable::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PtrTable) + + PX_DEF_BIN_METADATA_ITEM(stream, PtrTable, void, mSingle, PxMetaDataFlag::ePTR) // PT: this is actually a union, beware + PX_DEF_BIN_METADATA_ITEM(stream, PtrTable, PxU16, mCount, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PtrTable, bool, mOwnsMemory, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PtrTable, bool, mBufferUsed, 0) + + //------ Extra-data ------ + + // mList + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PtrTable, void, mBufferUsed, mCount, PxMetaDataFlag::eCONTROL_FLIP|PxMetaDataFlag::ePTR, PX_SERIAL_ALIGN) +} diff --git a/src/PhysX/physx/source/common/src/CmPtrTable.h b/src/PhysX/physx/source/common/src/CmPtrTable.h new file mode 100644 index 000000000..cc5041d4d --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPtrTable.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_PTR_TABLE +#define PX_PHYSICS_COMMON_PTR_TABLE + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +class PxSerializationContext; +class PxDeserializationContext; + + +namespace Cm +{ + +class PtrTableStorageManager +{ + // This will typically be backed by a MultiPool implementation with fallback to the user + // allocator. For MultiPool, when deallocating we want to know what the previously requested size was + // so we can release into the right pool + +public: + + // capacity is in bytes + + virtual void** allocate(PxU32 capacity) = 0; + virtual void deallocate(void** addr, PxU32 originalCapacity) = 0; + + // whether memory allocated at one capacity can (and should) be safely reused at a different capacity + // allows realloc-style reuse by clients. + + virtual bool canReuse(PxU32 originalCapacity, PxU32 newCapacity) = 0; +protected: + virtual ~PtrTableStorageManager() {} +}; + + + +// specialized class to hold an array of pointers with extrinsic storage management, +// serialization-compatible with 3.3.1 PtrTable +// +// note that extrinsic storage implies you *must* clear the table before the destructor runs +// +// capacity is implicit: +// if the memory is not owned (i.e. came from deserialization) then the capacity is exactly mCount +// else if mCount==0, capacity is 0 +// else the capacity is the power of 2 >= mCount +// +// one implication of this is that if we want to add or remove a pointer from unowned memory, we always realloc + +struct PX_PHYSX_COMMON_API PtrTable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PtrTable(); + ~PtrTable(); + + void add(void* ptr, PtrTableStorageManager& sm); + void replaceWithLast(PxU32 index, PtrTableStorageManager& sm); + void clear(PtrTableStorageManager& sm); + + PxU32 find(const void* ptr) const; + + PX_FORCE_INLINE PxU32 getCount() const { return mCount; } + PX_FORCE_INLINE void*const* getPtrs() const { return mCount == 1 ? &mSingle : mList; } + PX_FORCE_INLINE void** getPtrs() { return mCount == 1 ? &mSingle : mList; } + + + // SERIALIZATION + + // 3.3.1 compatibility fixup: this implementation ALWAYS sets 'ownsMemory' if the size is 0 or 1 + PtrTable(const PxEMPTY) + { + mOwnsMemory = mCount<2; + if(mCount == 0) + mList = NULL; + } + + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + + static void getBinaryMetaData(physx::PxOutputStream& stream); + +private: + void realloc(PxU32 oldCapacity, PxU32 newCapacity, PtrTableStorageManager& sm); + + union + { + void* mSingle; + void** mList; + }; + + PxU16 mCount; + bool mOwnsMemory; + bool mBufferUsed; // dark magic in serialization requires this, otherwise redundant because it's logically equivalent to mCount == 1. + +}; + +} // namespace Cm +#if !PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(sizeof(Cm::PtrTable)==8); +#endif + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmQueue.h b/src/PhysX/physx/source/common/src/CmQueue.h new file mode 100644 index 000000000..334a530b5 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmQueue.h @@ -0,0 +1,152 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_QUEUE +#define PX_PHYSICS_COMMON_QUEUE + +#include "foundation/PxAssert.h" +#include "PsAllocator.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Cm +{ + + template + class Queue: public Ps::UserAllocated + { + public: + Queue(PxU32 maxEntries); + ~Queue(); + + T popFront(); + T front(); + T popBack(); + T back(); + bool pushBack(const T& element); + bool empty() const; + PxU32 size() const; + + private: + T* mJobQueue; + PxU32 mNum; + PxU32 mHead; + PxU32 mTail; + PxU32 mMaxEntries; + AllocType mAllocator; + }; + + template + Queue::Queue(PxU32 maxEntries): + mNum(0), + mHead(0), + mTail(0), + mMaxEntries(maxEntries) + { + mJobQueue = reinterpret_cast(mAllocator.allocate(sizeof(T)*mMaxEntries, __FILE__, __LINE__)); + } + + template + Queue::~Queue() + { + if(mJobQueue) + mAllocator.deallocate(mJobQueue); + } + + template + T Queue::popFront() + { + PX_ASSERT(mNum>0); + + mNum--; + T& element = mJobQueue[mTail]; + mTail = (mTail+1) % (mMaxEntries); + return element; + } + + template + T Queue::front() + { + PX_ASSERT(mNum>0); + + return mJobQueue[mTail]; + } + + template + T Queue::popBack() + { + PX_ASSERT(mNum>0); + + mNum--; + mHead = (mHead-1) % (mMaxEntries); + return mJobQueue[mHead]; + } + + template + T Queue::back() + { + PX_ASSERT(mNum>0); + + PxU32 headAccess = (mHead-1) % (mMaxEntries); + return mJobQueue[headAccess]; + } + + template + bool Queue::pushBack(const T& element) + { + if (mNum == mMaxEntries) return false; + mJobQueue[mHead] = element; + + mNum++; + mHead = (mHead+1) % (mMaxEntries); + + return true; + } + + template + bool Queue::empty() const + { + return mNum == 0; + } + + template + PxU32 Queue::size() const + { + return mNum; + } + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmRadixSort.cpp b/src/PhysX/physx/source/common/src/CmRadixSort.cpp new file mode 100644 index 000000000..bb0adf40e --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmRadixSort.cpp @@ -0,0 +1,460 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "foundation/PxMemory.h" +#include "foundation/PxAssert.h" +#include "CmRadixSort.h" + +using namespace physx; +using namespace Cm; + +#if defined(__BIG_ENDIAN__) || defined(_XBOX) + #define H0_OFFSET 768 + #define H1_OFFSET 512 + #define H2_OFFSET 256 + #define H3_OFFSET 0 + #define BYTES_INC (3-j) +#else + #define H0_OFFSET 0 + #define H1_OFFSET 256 + #define H2_OFFSET 512 + #define H3_OFFSET 768 + #define BYTES_INC j +#endif + +#define CREATE_HISTOGRAMS(type, buffer) \ + /* Clear counters/histograms */ \ + PxMemZero(mHistogram1024, 256*4*sizeof(PxU32)); \ + \ + /* Prepare to count */ \ + const PxU8* PX_RESTRICT p = reinterpret_cast(input); \ + const PxU8* PX_RESTRICT pe = &p[nb*4]; \ + PxU32* PX_RESTRICT h0= &mHistogram1024[H0_OFFSET]; /* Histogram for first pass (LSB)*/ \ + PxU32* PX_RESTRICT h1= &mHistogram1024[H1_OFFSET]; /* Histogram for second pass */ \ + PxU32* PX_RESTRICT h2= &mHistogram1024[H2_OFFSET]; /* Histogram for third pass */ \ + PxU32* PX_RESTRICT h3= &mHistogram1024[H3_OFFSET]; /* Histogram for last pass (MSB)*/ \ + \ + bool AlreadySorted = true; /* Optimism... */ \ + \ + if(INVALID_RANKS) \ + { \ + /* Prepare for temporal coherence */ \ + const type* PX_RESTRICT Running = reinterpret_cast(buffer); \ + type PrevVal = *Running; \ + \ + while(p!=pe) \ + { \ + /* Read input buffer in previous sorted order */ \ + const type Val = *Running++; \ + /* Check whether already sorted or not */ \ + if(Val(input))+pass); + + // Check that byte's counter + if(CurCount[UniqueVal]==nb) + return NULL; + + return CurCount; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +RadixSort::RadixSort() : mCurrentSize(0), mRanks(NULL), mRanks2(NULL), mHistogram1024(0), mLinks256(0), mTotalCalls(0), mNbHits(0), mDeleteRanks(true) +{ + // Initialize indices + INVALIDATE_RANKS; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +RadixSort::~RadixSort() +{ +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main sort routine. + * This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data. + * \param input [in] a list of integer values to sort + * \param nb [in] number of values to sort, must be < 2^31 + * \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +RadixSort& RadixSort::Sort(const PxU32* input, PxU32 nb, RadixHint hint) +{ + PX_ASSERT(mHistogram1024); + PX_ASSERT(mLinks256); + PX_ASSERT(mRanks); + PX_ASSERT(mRanks2); + + // Checkings + if(!input || !nb || nb&0x80000000) return *this; + + // Stats + mTotalCalls++; + + // Create histograms (counters). Counters for all passes are created in one run. + // Pros: read input buffer once instead of four times + // Cons: mHistogram1024 is 4Kb instead of 1Kb + // We must take care of signed/unsigned values for temporal coherence.... I just + // have 2 code paths even if just a single opcode changes. Self-modifying code, someone? + if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(PxU32, input); } + else { CREATE_HISTOGRAMS(PxI32, input); } + + // Compute #negative values involved if needed + PxU32 NbNegativeValues = 0; + if(hint==RADIX_SIGNED) + { + // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128 + // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte, + // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers. + PxU32* PX_RESTRICT h3= &mHistogram1024[768]; + for(PxU32 i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part + } + + // Radix sort, j is the pass number (0=LSB, 3=MSB) + for(PxU32 j=0;j<4;j++) + { +// CHECK_PASS_VALIDITY(j); + PxU8 UniqueVal; + const PxU32* PX_RESTRICT CurCount = CheckPassValidity(j, mHistogram1024, nb, input, UniqueVal); + + // Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is + // not a problem, numbers are correctly sorted anyway. + if(CurCount) + { + PxU32** PX_RESTRICT Links256 = mLinks256; + + // Should we care about negative values? + if(j!=3 || hint==RADIX_UNSIGNED) + { + // Here we deal with positive values only + + // Create offsets + Links256[0] = mRanks2; + for(PxU32 i=1;i<256;i++) + Links256[i] = Links256[i-1] + CurCount[i-1]; + } + else + { + // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place. + + // Create biased offsets, in order for negative numbers to be sorted as well + Links256[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones + for(PxU32 i=1;i<128;i++) + Links256[i] = Links256[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + + // Fixing the wrong place for negative values + Links256[128] = mRanks2; + for(PxU32 i=129;i<256;i++) + Links256[i] = Links256[i-1] + CurCount[i-1]; + } + + // Perform Radix Sort + const PxU8* PX_RESTRICT InputBytes = reinterpret_cast(input); + InputBytes += BYTES_INC; + if(INVALID_RANKS) + { + for(PxU32 i=0;i(input2); + + // Allocate histograms & offsets on the stack + //PxU32 mHistogram1024[256*4]; + //PxU32* mLinks256[256]; + + // Create histograms (counters). Counters for all passes are created in one run. + // Pros: read input buffer once instead of four times + // Cons: mHistogram1024 is 4Kb instead of 1Kb + // Floating-point values are always supposed to be signed values, so there's only one code path there. + // Please note the floating point comparison needed for temporal coherence! Although the resulting asm code + // is dreadful, this is surprisingly not such a performance hit - well, I suppose that's a big one on first + // generation Pentiums....We can't make comparison on integer representations because, as Chris said, it just + // wouldn't work with mixed positive/negative values.... + { CREATE_HISTOGRAMS(float, input2); } + + // Compute #negative values involved if needed + PxU32 NbNegativeValues = 0; + // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128 + // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte, + // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers. + // ### is that ok on Apple ?! + PxU32* PX_RESTRICT h3= &mHistogram1024[768]; + for(PxU32 i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part + + // Radix sort, j is the pass number (0=LSB, 3=MSB) + for(PxU32 j=0;j<4;j++) + { + PxU8 UniqueVal; + const PxU32* PX_RESTRICT CurCount = CheckPassValidity(j, mHistogram1024, nb, input, UniqueVal); + // Should we care about negative values? + if(j!=3) + { + // Here we deal with positive values only +// CHECK_PASS_VALIDITY(j); +// const bool PerformPass = CheckPassValidity(j, mHistogram1024, nb, input); + + if(CurCount) + { + PxU32** PX_RESTRICT Links256 = mLinks256; + + // Create offsets + Links256[0] = mRanks2; + for(PxU32 i=1;i<256;i++) + Links256[i] = Links256[i-1] + CurCount[i-1]; + + // Perform Radix Sort + const PxU8* PX_RESTRICT InputBytes = reinterpret_cast(input); + InputBytes += BYTES_INC; + if(INVALID_RANKS) + { + for(PxU32 i=0;i>24; // Radix byte, same as above. AND is useless here (PxU32). + // ### cmp to be killed. Not good. Later. + if(Radix<128) *Links256[Radix]++ = i; // Number is positive, same as above + else *(--Links256[Radix]) = i; // Number is negative, flip the sorting order + } + VALIDATE_RANKS; + } + else + { + const PxU32* PX_RESTRICT Ranks = mRanks; + for(PxU32 i=0;i>24; // Radix byte, same as above. AND is useless here (PxU32). + // ### cmp to be killed. Not good. Later. + if(Radix<128) *Links256[Radix]++ = Ranks[i]; // Number is positive, same as above + else *(--Links256[Radix]) = Ranks[i]; // Number is negative, flip the sorting order + } + } + // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap. + PxU32* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp; + } + else + { + // The pass is useless, yet we still have to reverse the order of current list if all values are negative. + if(UniqueVal>=128) + { + if(INVALID_RANKS) + { + // ###Possible? + for(PxU32 i=0;i(PX_ALLOC(sizeof(PxU32)*nb, "RadixSortBuffered:mRanks")); + mRanks2 = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nb, "RadixSortBuffered:mRanks2")); + } + + return true; +} + +PX_INLINE void RadixSortBuffered::CheckResize(PxU32 nb) +{ + PxU32 CurSize = CURRENT_SIZE; + if(nb!=CurSize) + { + if(nb>CurSize) Resize(nb); + mCurrentSize = nb; + INVALIDATE_RANKS; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main sort routine. + * This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data. + * \param input [in] a list of integer values to sort + * \param nb [in] number of values to sort, must be < 2^31 + * \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RadixSortBuffered& RadixSortBuffered::Sort(const PxU32* input, PxU32 nb, RadixHint hint) +{ + // Checkings + if(!input || !nb || nb&0x80000000) return *this; + + // Resize lists if needed + CheckResize(nb); + + //Set histogram buffers. + PxU32 histogram[1024]; + PxU32* links[256]; + mHistogram1024=histogram; + mLinks256=links; + + RadixSort::Sort(input,nb,hint); + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main sort routine. + * This one is for floating-point values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data. + * \param input2 [in] a list of floating-point values to sort + * \param nb [in] number of values to sort, must be < 2^31 + * \return Self-Reference + * \warning only sorts IEEE floating-point values + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RadixSortBuffered& RadixSortBuffered::Sort(const float* input2, PxU32 nb) +{ + // Checkings + if(!input2 || !nb || nb&0x80000000) return *this; + + // Resize lists if needed + CheckResize(nb); + + //Set histogram buffers. + PxU32 histogram[1024]; + PxU32* links[256]; + mHistogram1024=histogram; + mLinks256=links; + + RadixSort::Sort(input2,nb); + return *this; +} + diff --git a/src/PhysX/physx/source/common/src/CmRadixSortBuffered.h b/src/PhysX/physx/source/common/src/CmRadixSortBuffered.h new file mode 100644 index 000000000..9e6bcc034 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmRadixSortBuffered.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CM_RADIX_SORT_BUFFERED_H +#define CM_RADIX_SORT_BUFFERED_H + +#include "CmPhysXCommon.h" +#include "CmRadixSort.h" + +namespace physx +{ +namespace Cm +{ + class PX_PHYSX_COMMON_API RadixSortBuffered : public RadixSort + { + public: + RadixSortBuffered(); + ~RadixSortBuffered(); + + void reset(); + + RadixSortBuffered& Sort(const PxU32* input, PxU32 nb, RadixHint hint=RADIX_SIGNED); + RadixSortBuffered& Sort(const float* input, PxU32 nb); + + private: + RadixSortBuffered(const RadixSortBuffered& object); + RadixSortBuffered& operator=(const RadixSortBuffered& object); + + // Internal methods + void CheckResize(PxU32 nb); + bool Resize(PxU32 nb); + }; +} + +} + +#endif // CM_RADIX_SORT_BUFFERED_H diff --git a/src/PhysX/physx/source/common/src/CmRefCountable.h b/src/PhysX/physx/source/common/src/CmRefCountable.h new file mode 100644 index 000000000..361437628 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmRefCountable.h @@ -0,0 +1,102 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_REFCOUNTABLE +#define PX_PHYSICS_COMMON_REFCOUNTABLE + +#include "foundation/PxAssert.h" +#include "PsAtomic.h" + +namespace physx +{ +namespace Cm +{ + + // simple thread-safe reference count + // when the ref count is zero, the object is in an undefined state (pending delete) + + class RefCountable + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + RefCountable(const PxEMPTY) : mRefCount(1) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + explicit RefCountable(PxU32 initialCount = 1) + : mRefCount(PxI32(initialCount)) + { + PX_ASSERT(mRefCount!=0); + } + + virtual ~RefCountable() {} + + /** + Calls 'delete this;'. It needs to be overloaded for classes also deriving from + PxBase and call 'Cm::deletePxBase(this);' instead. + */ + virtual void onRefCountZero() + { + delete this; + } + + void incRefCount() + { + physx::shdfnd::atomicIncrement(&mRefCount); + // value better be greater than 1, or we've created a ref to an undefined object + PX_ASSERT(mRefCount>1); + } + + void decRefCount() + { + PX_ASSERT(mRefCount>0); + if(physx::shdfnd::atomicDecrement(&mRefCount) == 0) + onRefCountZero(); + } + + PX_FORCE_INLINE PxU32 getRefCount() const + { + return PxU32(mRefCount); + } + private: + volatile PxI32 mRefCount; + }; + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmRenderBuffer.h b/src/PhysX/physx/source/common/src/CmRenderBuffer.h new file mode 100644 index 000000000..062bd0e34 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmRenderBuffer.h @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_FOUNDATION_PSRENDERBUFFER_H +#define PX_FOUNDATION_PSRENDERBUFFER_H + +#include "common/PxRenderBuffer.h" +#include "CmPhysXCommon.h" +#include "PsArray.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Cm +{ + /** + Implementation of PxRenderBuffer. + */ + class RenderBuffer : public PxRenderBuffer, public Ps::UserAllocated + { + + template + void append(Ps::Array& dst, const T* src, PxU32 count) + { + dst.reserve(dst.size() + count); + for(const T* end=src+count; src mPoints; + Ps::Array mLines; + Ps::Array mTriangles; + Ps::Array mTexts; + Ps::Array mCharBuf; + }; + +} // Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmRenderOutput.cpp b/src/PhysX/physx/source/common/src/CmRenderOutput.cpp new file mode 100644 index 000000000..bc5b78241 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmRenderOutput.cpp @@ -0,0 +1,301 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxMat44.h" +#include "CmRenderOutput.h" +#include "PsMathUtils.h" +#include "PsString.h" +#include + +#if PX_VC +#pragma warning(disable: 4342 ) // behavior change: 'function' called, but a member operator was called in previous versions +#pragma warning(disable: 4996 ) // intentionally suppressing this warning message +#endif + +using namespace physx; +using namespace Cm; + + +namespace physx +{ +namespace Cm +{ + RenderOutput& RenderOutput::operator<<(Primitive prim) + { + mPrim = prim; mVertexCount = 0; return *this; + } + + RenderOutput& RenderOutput::operator<<(PxU32 color) + { + mColor = color; return *this; + } + + RenderOutput& RenderOutput::operator<<(const PxMat44& transform) + { + mTransform = transform; return *this; + } + + RenderOutput& RenderOutput::operator<<(const PxTransform&t) + { + mTransform = PxMat44(t); + return *this; + } + + RenderOutput& RenderOutput::operator<<(PxVec3 vertex) + { + // apply transformation + vertex = mTransform.transform(vertex); + ++mVertexCount; + + // add primitive to render buffer + switch(mPrim) + { + case POINTS: + mBuffer.mPoints.pushBack(PxDebugPoint(vertex, mColor)); break; + case LINES: + if(mVertexCount == 2) + { + mBuffer.mLines.pushBack(PxDebugLine(mVertex0, vertex, mColor)); + mVertexCount = 0; + } + break; + case LINESTRIP: + if(mVertexCount >= 2) + mBuffer.mLines.pushBack(PxDebugLine(mVertex0, vertex, mColor)); + break; + case TRIANGLES: + if(mVertexCount == 3) + { + mBuffer.mTriangles.pushBack(PxDebugTriangle(mVertex1, mVertex0, vertex, mColor)); + mVertexCount = 0; + } + break; + case TRIANGLESTRIP: + if(mVertexCount >= 3) + mBuffer.mTriangles.pushBack(PxDebugTriangle( + (mVertexCount & 0x1) ? mVertex0 : mVertex1, + (mVertexCount & 0x1) ? mVertex1 : mVertex0, vertex, mColor)); + break; + case TEXT: + break; + } + + // cache the last 2 vertices (for strips) + if(1 < mVertexCount) + { + mVertex1 = mVertex0; + mVertex0 = vertex; + } else { + mVertex0 = vertex; + } + return *this; + } + + DebugText::DebugText(const PxVec3& position_, PxReal size_, const char* string, ...) + : position(position_), size(size_) + { + va_list argList; + va_start(argList, string); + if(0 >= Ps::vsnprintf(buffer, sBufferSize-1, string, argList)) + buffer[sBufferSize-1] = 0; // terminate string + va_end(argList); + } + + RenderOutput& RenderOutput::operator<<(const DebugText& text) + { + const PxU32 n = PxU32(strlen(text.buffer)); + const PxU32 newCharBufSize = mBuffer.mCharBuf.size()+n+1; + if(mBuffer.mCharBuf.capacity() < newCharBufSize) + { + char* oldBuf = mBuffer.mCharBuf.begin(); + mBuffer.mCharBuf.reserve(newCharBufSize); + intptr_t diff = mBuffer.mCharBuf.begin() - oldBuf; + for (PxU32 i = 0; i < mBuffer.mTexts.size(); ++i) + mBuffer.mTexts[i].string += diff; + } + mBuffer.mTexts.pushBack(PxDebugText(mTransform.transform(text.position), text.size, mColor, mBuffer.mCharBuf.end())); + for(size_t i=0; i<=n; ++i) + mBuffer.mCharBuf.pushBack(text.buffer[i]); + return *this; + } + + RenderOutput& operator<<(RenderOutput& out, const DebugBox& box) + { + if(box.wireframe) + { + out << RenderOutput::LINESTRIP; + out << PxVec3(box.minimum.x, box.minimum.y, box.minimum.z); + out << PxVec3(box.maximum.x, box.minimum.y, box.minimum.z); + out << PxVec3(box.maximum.x, box.maximum.y, box.minimum.z); + out << PxVec3(box.minimum.x, box.maximum.y, box.minimum.z); + out << PxVec3(box.minimum.x, box.minimum.y, box.minimum.z); + out << PxVec3(box.minimum.x, box.minimum.y, box.maximum.z); + out << PxVec3(box.maximum.x, box.minimum.y, box.maximum.z); + out << PxVec3(box.maximum.x, box.maximum.y, box.maximum.z); + out << PxVec3(box.minimum.x, box.maximum.y, box.maximum.z); + out << PxVec3(box.minimum.x, box.minimum.y, box.maximum.z); + out << RenderOutput::LINES; + out << PxVec3(box.maximum.x, box.minimum.y, box.minimum.z); + out << PxVec3(box.maximum.x, box.minimum.y, box.maximum.z); + out << PxVec3(box.maximum.x, box.maximum.y, box.minimum.z); + out << PxVec3(box.maximum.x, box.maximum.y, box.maximum.z); + out << PxVec3(box.minimum.x, box.maximum.y, box.minimum.z); + out << PxVec3(box.minimum.x, box.maximum.y, box.maximum.z); + } + else + { + out << RenderOutput::TRIANGLESTRIP; + out << PxVec3(box.minimum.x, box.minimum.y, box.minimum.z); // 0 + out << PxVec3(box.minimum.x, box.maximum.y, box.minimum.z); // 2 + out << PxVec3(box.maximum.x, box.minimum.y, box.minimum.z); // 1 + out << PxVec3(box.maximum.x, box.maximum.y, box.minimum.z); // 3 + out << PxVec3(box.maximum.x, box.maximum.y, box.maximum.z); // 7 + out << PxVec3(box.minimum.x, box.maximum.y, box.minimum.z); // 2 + out << PxVec3(box.minimum.x, box.maximum.y, box.maximum.z); // 6 + out << PxVec3(box.minimum.x, box.minimum.y, box.minimum.z); // 0 + out << PxVec3(box.minimum.x, box.minimum.y, box.maximum.z); // 4 + out << PxVec3(box.maximum.x, box.minimum.y, box.minimum.z); // 1 + out << PxVec3(box.maximum.x, box.minimum.y, box.maximum.z); // 5 + out << PxVec3(box.maximum.x, box.maximum.y, box.maximum.z); // 7 + out << PxVec3(box.minimum.x, box.minimum.y, box.maximum.z); // 4 + out << PxVec3(box.minimum.x, box.maximum.y, box.maximum.z); // 6 + } + return out; + } + + RenderOutput& operator<<(RenderOutput& out, const DebugArrow& arrow) + { + PxVec3 t0 = arrow.tip - arrow.base, t1, t2; + + t0.normalize(); + Ps::normalToTangents(t0, t1, t2); + + const PxReal tipAngle = 0.25f; + t1 *= arrow.headLength * tipAngle; + t2 *= arrow.headLength * tipAngle * PxSqrt(3.0f); + PxVec3 headBase = arrow.tip - t0 * arrow.headLength; + + out << RenderOutput::LINES; + out << arrow.base << arrow.tip; + + out << RenderOutput::TRIANGLESTRIP; + out << arrow.tip; + out << headBase + t1 + t1; + out << headBase - t1 - t2; + out << headBase - t1 + t2; + out << arrow.tip; + out << headBase + t1 + t1; + return out; + } + + RenderOutput& operator<<(RenderOutput& out, const DebugBasis& basis) + { + const PxReal headLength = basis.extends.magnitude() * 0.15f; + out << basis.colorX << DebugArrow(PxVec3(0.0f), PxVec3(basis.extends.x, 0, 0), headLength); + out << basis.colorY << DebugArrow(PxVec3(0.0f), PxVec3(0, basis.extends.y, 0), headLength); + out << basis.colorZ << DebugArrow(PxVec3(0.0f), PxVec3(0, 0, basis.extends.z), headLength); + return out; + } + + RenderOutput& operator<<(RenderOutput& out, const DebugCircle& circle) + { + const PxF32 step = PxTwoPi/circle.nSegments; + PxF32 angle = 0; + out << RenderOutput::LINESTRIP; + for(PxU32 i=0; ipos0 = v0; + segment->pos1 = v1; + segment->color0 = segment->color1 = mColor; + } + + RenderOutput& outputCapsule(PxF32 radius, PxF32 halfHeight, const PxMat44& absPose); + + private: + + RenderOutput& operator=(const RenderOutput&); + + Primitive mPrim; + PxU32 mColor; + PxVec3 mVertex0, mVertex1; + PxU32 mVertexCount; + PxMat44 mTransform; + RenderBuffer& mBuffer; + }; + + /** debug render helper types */ + struct PX_PHYSX_COMMON_API DebugText + { + DebugText(const PxVec3& position, PxReal size, const char* string, ...); + static const int sBufferSize = 1008; // sizeof(DebugText)==1kB + char buffer[sBufferSize]; + PxVec3 position; + PxReal size; + }; + + struct DebugBox + { + explicit DebugBox(const PxVec3& extents, bool wireframe_ = true) + : minimum(-extents), maximum(extents), wireframe(wireframe_) {} + + explicit DebugBox(const PxVec3& pos, const PxVec3& extents, bool wireframe_ = true) + : minimum(pos-extents), maximum(pos+extents), wireframe(wireframe_) {} + + explicit DebugBox(const PxBounds3& bounds, bool wireframe_ = true) + : minimum(bounds.minimum), maximum(bounds.maximum), wireframe(wireframe_) {} + + PxVec3 minimum, maximum; + bool wireframe; + }; + PX_PHYSX_COMMON_API RenderOutput& operator<<(RenderOutput& out, const DebugBox& box); + + struct DebugArrow + { + DebugArrow(const PxVec3& pos, const PxVec3& vec) + : base(pos), tip(pos+vec), headLength(vec.magnitude()*0.15f) {} + + DebugArrow(const PxVec3& pos, const PxVec3& vec, PxReal headLength_) + : base(pos), tip(pos+vec), headLength(headLength_) {} + + PxVec3 base, tip; + PxReal headLength; + }; + PX_PHYSX_COMMON_API RenderOutput& operator<<(RenderOutput& out, const DebugArrow& arrow); + + struct DebugBasis + { + DebugBasis(const PxVec3& ext, PxU32 cX = PxDebugColor::eARGB_RED, + PxU32 cY = PxDebugColor::eARGB_GREEN, PxU32 cZ = PxDebugColor::eARGB_BLUE) + : extends(ext), colorX(cX), colorY(cY), colorZ(cZ) {} + PxVec3 extends; + PxU32 colorX, colorY, colorZ; + }; + PX_PHYSX_COMMON_API RenderOutput& operator<<(RenderOutput& out, const DebugBasis& basis); + +#if PX_VC + #pragma warning(pop) +#endif + + struct DebugCircle + { + DebugCircle(PxU32 s, PxReal r) + : nSegments(s), radius(r) {} + PxU32 nSegments; + PxReal radius; + }; + PX_PHYSX_COMMON_API RenderOutput& operator<<(RenderOutput& out, const DebugCircle& circle); + + struct DebugArc + { + DebugArc(PxU32 s, PxReal r, PxReal minAng, PxReal maxAng) + : nSegments(s), radius(r), minAngle(minAng), maxAngle(maxAng) {} + PxU32 nSegments; + PxReal radius; + PxReal minAngle, maxAngle; + }; + PX_PHYSX_COMMON_API RenderOutput& operator<<(RenderOutput& out, const DebugArc& arc); + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmScaling.h b/src/PhysX/physx/source/common/src/CmScaling.h new file mode 100644 index 000000000..53ba22345 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmScaling.h @@ -0,0 +1,227 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_SCALING +#define PX_PHYSICS_COMMON_SCALING + +#include "foundation/PxBounds3.h" +#include "foundation/PxMat33.h" +#include "geometry/PxMeshScale.h" +#include "CmMatrix34.h" +#include "CmUtils.h" +#include "PsMathUtils.h" + +namespace physx +{ +namespace Cm +{ + // class that can perform scaling fast. Relatively large size, generated from PxMeshScale on demand. + // CS: I've removed most usages of this class, because most of the time only one-way transform is needed. + // If you only need a temporary FastVertex2ShapeScaling, setup your transform as PxMat34Legacy and use + // normal matrix multiplication or a transform() overload to convert points and bounds between spaces. + class FastVertex2ShapeScaling + { + public: + PX_INLINE FastVertex2ShapeScaling() + { + //no scaling by default: + vertex2ShapeSkew = PxMat33(PxIdentity); + shape2VertexSkew = PxMat33(PxIdentity); + mFlipNormal = false; + } + + PX_INLINE explicit FastVertex2ShapeScaling(const PxMeshScale& scale) + { + init(scale); + } + + PX_INLINE FastVertex2ShapeScaling(const PxVec3& scale, const PxQuat& rotation) + { + init(scale, rotation); + } + + PX_INLINE void init(const PxMeshScale& scale) + { + init(scale.scale, scale.rotation); + } + + PX_INLINE void setIdentity() + { + vertex2ShapeSkew = PxMat33(PxIdentity); + shape2VertexSkew = PxMat33(PxIdentity); + mFlipNormal = false; + } + + PX_INLINE void init(const PxVec3& scale, const PxQuat& rotation) + { + // TODO: may want to optimize this for cases where we have uniform or axis aligned scaling! + // That would introduce branches and it's unclear to me whether that's faster than just doing the math. + // Lazy computation would be another option, at the cost of introducing even more branches. + + const PxMat33 R(rotation); + vertex2ShapeSkew = R.getTranspose(); + const PxMat33 diagonal = PxMat33::createDiagonal(scale); + vertex2ShapeSkew = vertex2ShapeSkew * diagonal; + vertex2ShapeSkew = vertex2ShapeSkew * R; + + /* + The inverse, is, explicitly: + shape2VertexSkew.setTransposed(R); + shape2VertexSkew.multiplyDiagonal(PxVec3(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z)); + shape2VertexSkew *= R; + + It may be competitive to compute the inverse -- though this has a branch in it: + */ + + shape2VertexSkew = vertex2ShapeSkew.getInverse(); + + mFlipNormal = ((scale.x * scale.y * scale.z) < 0.0f); + } + + PX_FORCE_INLINE void flipNormal(PxVec3& v1, PxVec3& v2) const + { + if (mFlipNormal) + { + PxVec3 tmp = v1; v1 = v2; v2 = tmp; + } + } + + PX_FORCE_INLINE PxVec3 operator* (const PxVec3& src) const + { + return vertex2ShapeSkew * src; + } + PX_FORCE_INLINE PxVec3 operator% (const PxVec3& src) const + { + return shape2VertexSkew * src; + } + + PX_FORCE_INLINE const PxMat33& getVertex2ShapeSkew() const + { + return vertex2ShapeSkew; + } + + PX_FORCE_INLINE const PxMat33& getShape2VertexSkew() const + { + return shape2VertexSkew; + } + + PX_INLINE Cm::Matrix34 getVertex2WorldSkew(const Cm::Matrix34& shape2world) const + { + const Cm::Matrix34 vertex2worldSkew = shape2world * getVertex2ShapeSkew(); + //vertex2worldSkew = shape2world * [vertex2shapeSkew, 0] + //[aR at] * [bR bt] = [aR * bR aR * bt + at] NOTE: order of operations important so it works when this ?= left ?= right. + return vertex2worldSkew; + } + + PX_INLINE Cm::Matrix34 getWorld2VertexSkew(const Cm::Matrix34& shape2world) const + { + //world2vertexSkew = shape2vertex * invPQ(shape2world) + //[aR 0] * [bR' -bR'*bt] = [aR * bR' -aR * bR' * bt + 0] + + const PxMat33 rotate( shape2world[0], shape2world[1], shape2world[2] ); + const PxMat33 M = getShape2VertexSkew() * rotate.getTranspose(); + return Cm::Matrix34(M[0], M[1], M[2], -M * shape2world[3]); + } + + //! Transforms a shape space OBB to a vertex space OBB. All 3 params are in and out. + void transformQueryBounds(PxVec3& center, PxVec3& extents, PxMat33& basis) const + { + basis.column0 = shape2VertexSkew * (basis.column0 * extents.x); + basis.column1 = shape2VertexSkew * (basis.column1 * extents.y); + basis.column2 = shape2VertexSkew * (basis.column2 * extents.z); + + center = shape2VertexSkew * center; + extents = Ps::optimizeBoundingBox(basis); + } + + void transformPlaneToShapeSpace(const PxVec3& nIn, const PxReal dIn, PxVec3& nOut, PxReal& dOut) const + { + const PxVec3 tmp = shape2VertexSkew.transformTranspose(nIn); + const PxReal denom = 1.0f / tmp.magnitude(); + nOut = tmp * denom; + dOut = dIn * denom; + } + + PX_FORCE_INLINE bool flipsNormal() const { return mFlipNormal; } + + private: + PxMat33 vertex2ShapeSkew; + PxMat33 shape2VertexSkew; + bool mFlipNormal; + }; + + PX_FORCE_INLINE void getScaledVertices(PxVec3* v, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, bool idtMeshScale, const Cm::FastVertex2ShapeScaling& scaling) + { + if(idtMeshScale) + { + v[0] = v0; + v[1] = v1; + v[2] = v2; + } + else + { + const PxI32 winding = scaling.flipsNormal() ? 1 : 0; + v[0] = scaling * v0; + v[1+winding] = scaling * v1; + v[2-winding] = scaling * v2; + } + } + +} // namespace Cm + + +PX_INLINE Cm::Matrix34 operator*(const PxTransform& transform, const PxMeshScale& scale) +{ + return Cm::Matrix34(PxMat33(transform.q) * scale.toMat33(), transform.p); +} + +PX_INLINE Cm::Matrix34 operator*(const PxMeshScale& scale, const PxTransform& transform) +{ + const PxMat33 scaleMat = scale.toMat33(); + const PxMat33 t = PxMat33(transform.q); + const PxMat33 r = scaleMat * t; + const PxVec3 p = scaleMat * transform.p; + return Cm::Matrix34(r, p); +} + +PX_INLINE Cm::Matrix34 operator*(const Cm::Matrix34& transform, const PxMeshScale& scale) +{ + return Cm::Matrix34(transform.m * scale.toMat33(), transform.p); +} + +PX_INLINE Cm::Matrix34 operator*(const PxMeshScale& scale, const Cm::Matrix34& transform) +{ + const PxMat33 scaleMat = scale.toMat33(); + return Cm::Matrix34(scaleMat * transform.m, scaleMat * transform.p); +} + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmSpatialVector.h b/src/PhysX/physx/source/common/src/CmSpatialVector.h new file mode 100644 index 000000000..05e7ef57c --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmSpatialVector.h @@ -0,0 +1,513 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_VECTOR +#define PX_PHYSICS_COMMON_VECTOR + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" +#include "foundation/PxTransform.h" + +/*! +Combination of two R3 vectors. +*/ + +namespace physx +{ +namespace Cm +{ +PX_ALIGN_PREFIX(16) +class SpatialVector +{ +public: + //! Default constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector() + {} + + //! Construct from two PxcVectors + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector(const PxVec3& lin, const PxVec3& ang) + : linear(lin), pad0(0.0f), angular(ang), pad1(0.0f) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE ~SpatialVector() + {} + + + + // PT: this one is very important. Without it, the Xbox compiler generates weird "float-to-int" and "int-to-float" LHS + // each time we copy a SpatialVector (see for example PIX on "solveSimpleGroupA" without this operator). + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator = (const SpatialVector& v) + { + linear = v.linear; + pad0 = 0.0f; + angular = v.angular; + pad1 = 0.0f; + } + + + static PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector zero() { return SpatialVector(PxVec3(0),PxVec3(0)); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector operator+(const SpatialVector& v) const + { + return SpatialVector(linear+v.linear,angular+v.angular); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector operator-(const SpatialVector& v) const + { + return SpatialVector(linear-v.linear,angular-v.angular); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector operator-() const + { + return SpatialVector(-linear,-angular); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector operator *(PxReal s) const + { + return SpatialVector(linear*s,angular*s); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator+=(const SpatialVector& v) + { + linear+=v.linear; + angular+=v.angular; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator-=(const SpatialVector& v) + { + linear-=v.linear; + angular-=v.angular; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal magnitude() const + { + return angular.magnitude() + linear.magnitude(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal dot(const SpatialVector& v) const + { + return linear.dot(v.linear) + angular.dot(v.angular); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite() const + { + return linear.isFinite() && angular.isFinite(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVector scale(PxReal l, PxReal a) const + { + return Cm::SpatialVector(linear*l, angular*a); + } + + PxVec3 linear; + PxReal pad0; + PxVec3 angular; + PxReal pad1; +} +PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct SpatialVectorF +{ +public: + //! Default constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF() + {} + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF(const PxReal* v) + : pad0(0.0f), pad1(0.0f) + { + top.x = v[0]; top.y = v[1]; top.z = v[2]; + bottom.x = v[3]; bottom.y = v[4]; bottom.z = v[5]; + } + //! Construct from two PxcVectors + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF(const PxVec3& top_, const PxVec3& bottom_) + : top(top_), pad0(0.0f), bottom(bottom_), pad1(0.0f) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE ~SpatialVectorF() + {} + + + + // PT: this one is very important. Without it, the Xbox compiler generates weird "float-to-int" and "int-to-float" LHS + // each time we copy a SpatialVector (see for example PIX on "solveSimpleGroupA" without this operator). + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator = (const SpatialVectorF& v) + { + top = v.top; + pad0 = 0.0f; + bottom = v.bottom; + pad1 = 0.0f; + } + + + static PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF Zero() { return SpatialVectorF(PxVec3(0), PxVec3(0)); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF operator+(const SpatialVectorF& v) const + { + return SpatialVectorF(top + v.top, bottom + v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF operator-(const SpatialVectorF& v) const + { + return SpatialVectorF(top - v.top, bottom - v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF operator-() const + { + return SpatialVectorF(-top, -bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF operator *(PxReal s) const + { + return SpatialVectorF(top*s, bottom*s); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator *= (const PxReal s) + { + top *= s; + bottom *= s; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator += (const SpatialVectorF& v) + { + top += v.top; + bottom += v.bottom; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator -= (const SpatialVectorF& v) + { + top -= v.top; + bottom -= v.bottom; + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal magnitude() const + { + return top.magnitude() + bottom.magnitude(); + } + + PX_FORCE_INLINE PxReal magnitudeSquared() const + { + return top.magnitudeSquared() + bottom.magnitudeSquared(); + } + + //This is inner product + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal innerProduct(const SpatialVectorF& v) const + { + return bottom.dot(v.top) + top.dot(v.bottom); + /*PxVec3 p0 = bottom.multiply(v.top); + PxVec3 p1 = top.multiply(v.bottom); + + PxReal result = (((p1.y + p1.z) + (p0.z + p1.x)) + (p0.x + p0.y)); + return result;*/ + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal dot(const SpatialVectorF& v) const + { + return top.dot(v.top) + bottom.dot(v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal dot(const SpatialVector& v) const + { + return bottom.dot(v.angular) + top.dot(v.linear); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF cross(const SpatialVectorF& v) const + { + SpatialVectorF a; + a.top = top.cross(v.top); + a.bottom = top.cross(v.bottom) + bottom.cross(v.top); + return a; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF abs() const + { + return SpatialVectorF(top.abs(), bottom.abs()); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF rotate(const PxTransform& rot) const + { + return SpatialVectorF(rot.rotate(top), rot.rotate(bottom)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF rotateInv(const PxTransform& rot) const + { + return SpatialVectorF(rot.rotateInv(top), rot.rotateInv(bottom)); + } + + + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite() const + { + return top.isFinite() && bottom.isFinite(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isValid(const PxReal maxV) const + { + const bool tValid = ((PxAbs(top.x) <= maxV) && (PxAbs(top.y) <= maxV) && (PxAbs(top.z) <= maxV)); + const bool bValid = ((PxAbs(bottom.x) <= maxV) && (PxAbs(bottom.y) <= maxV) && (PxAbs(bottom.z) <= maxV)); + + return tValid && bValid; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF scale(PxReal l, PxReal a) const + { + return Cm::SpatialVectorF(top*l, bottom*a); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void assignTo(PxReal* val) const + { + val[0] = top.x; val[1] = top.y; val[2] = top.z; + val[3] = bottom.x; val[4] = bottom.y; val[5] = bottom.z; + } + + PxVec3 top; + PxReal pad0; + PxVec3 bottom; + PxReal pad1; +} PX_ALIGN_SUFFIX(16); + + +struct UnAlignedSpatialVector +{ +public: + //! Default constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector() + {} + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector(const PxReal* v) + { + top.x = v[0]; top.y = v[1]; top.z = v[2]; + bottom.x = v[3]; bottom.y = v[4]; bottom.z = v[5]; + } + //! Construct from two PxcVectors + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector(const PxVec3& top_, const PxVec3& bottom_) + : top(top_), bottom(bottom_) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE ~UnAlignedSpatialVector() + {} + + + + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator = (const SpatialVectorF& v) + { + top = v.top; + bottom = v.bottom; + } + + + static PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector Zero() { return UnAlignedSpatialVector(PxVec3(0), PxVec3(0)); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector operator+(const UnAlignedSpatialVector& v) const + { + return UnAlignedSpatialVector(top + v.top, bottom + v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector operator-(const UnAlignedSpatialVector& v) const + { + return UnAlignedSpatialVector(top - v.top, bottom - v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector operator-() const + { + return UnAlignedSpatialVector(-top, -bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector operator *(PxReal s) const + { + return UnAlignedSpatialVector(top*s, bottom*s); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator *= (const PxReal s) + { + top *= s; + bottom *= s; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator += (const UnAlignedSpatialVector& v) + { + top += v.top; + bottom += v.bottom; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator += (const SpatialVectorF& v) + { + top += v.top; + bottom += v.bottom; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator -= (const UnAlignedSpatialVector& v) + { + top -= v.top; + bottom -= v.bottom; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator -= (const SpatialVectorF& v) + { + top -= v.top; + bottom -= v.bottom; + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal magnitude() const + { + return top.magnitude() + bottom.magnitude(); + } + + PX_FORCE_INLINE PxReal magnitudeSquared() const + { + return top.magnitudeSquared() + bottom.magnitudeSquared(); + } + + //This is inner product + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal innerProduct(const UnAlignedSpatialVector& v) const + { + return bottom.dot(v.top) + top.dot(v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal innerProduct(const SpatialVectorF& v) const + { + return bottom.dot(v.top) + top.dot(v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal dot(const UnAlignedSpatialVector& v) const + { + return top.dot(v.top) + bottom.dot(v.bottom); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector cross(const UnAlignedSpatialVector& v) const + { + UnAlignedSpatialVector a; + a.top = top.cross(v.top); + a.bottom = top.cross(v.bottom) + bottom.cross(v.top); + return a; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector abs() const + { + return UnAlignedSpatialVector(top.abs(), bottom.abs()); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector rotate(const PxTransform& rot) const + { + return UnAlignedSpatialVector(rot.rotate(top), rot.rotate(bottom)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector rotateInv(const PxTransform& rot) const + { + return UnAlignedSpatialVector(rot.rotateInv(top), rot.rotateInv(bottom)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite() const + { + return top.isFinite() && bottom.isFinite(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isValid(const PxReal maxV) const + { + const bool tValid = ((top.x <= maxV) && (top.y <= maxV) && (top.z <= maxV)); + const bool bValid = ((bottom.x <= maxV) && (bottom.y <= maxV) && (bottom.z <= maxV)); + + return tValid && bValid; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector scale(PxReal l, PxReal a) const + { + return Cm::UnAlignedSpatialVector(top*l, bottom*a); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void assignTo(PxReal* val) const + { + val[0] = top.x; val[1] = top.y; val[2] = top.z; + val[3] = bottom.x; val[4] = bottom.y; val[5] = bottom.z; + } + + PxVec3 top; //12 12 + PxVec3 bottom; //12 24 +}; + + +PX_ALIGN_PREFIX(16) +struct SpatialVectorV +{ + Ps::aos::Vec3V linear; + Ps::aos::Vec3V angular; + + PX_FORCE_INLINE SpatialVectorV() {} + PX_FORCE_INLINE SpatialVectorV(PxZERO): linear(Ps::aos::V3Zero()), angular(Ps::aos::V3Zero()) {} + PX_FORCE_INLINE SpatialVectorV(const Cm::SpatialVector& v): linear(Ps::aos::V3LoadU(v.linear)), angular(Ps::aos::V3LoadU(v.angular)) {} + PX_FORCE_INLINE SpatialVectorV(const Ps::aos::Vec3VArg l, const Ps::aos::Vec3VArg a): linear(l), angular(a) {} + PX_FORCE_INLINE SpatialVectorV(const SpatialVectorV& other): linear(other.linear), angular(other.angular) {} + PX_FORCE_INLINE SpatialVectorV& operator=(const SpatialVectorV& other) { linear = other.linear; angular = other.angular; return *this; } + + PX_FORCE_INLINE SpatialVectorV operator+(const SpatialVectorV& other) const { return SpatialVectorV(Ps::aos::V3Add(linear,other.linear), + Ps::aos::V3Add(angular, other.angular)); } + + PX_FORCE_INLINE SpatialVectorV& operator+=(const SpatialVectorV& other) { linear = Ps::aos::V3Add(linear,other.linear); + angular = Ps::aos::V3Add(angular, other.angular); + return *this; + } + + PX_FORCE_INLINE SpatialVectorV operator-(const SpatialVectorV& other) const { return SpatialVectorV(Ps::aos::V3Sub(linear,other.linear), + Ps::aos::V3Sub(angular, other.angular)); } + + PX_FORCE_INLINE SpatialVectorV operator-() const { return SpatialVectorV(Ps::aos::V3Neg(linear), Ps::aos::V3Neg(angular)); } + + PX_FORCE_INLINE SpatialVectorV operator*(const Ps::aos::FloatVArg r) const { return SpatialVectorV(Ps::aos::V3Scale(linear,r), Ps::aos::V3Scale(angular,r)); } + + PX_FORCE_INLINE SpatialVectorV& operator-=(const SpatialVectorV& other) { linear = Ps::aos::V3Sub(linear,other.linear); + angular = Ps::aos::V3Sub(angular, other.angular); + return *this; + } + + PX_FORCE_INLINE Ps::aos::FloatV dot(const SpatialVectorV& other) const { return Ps::aos::FAdd(Ps::aos::V3Dot(linear, other.linear), Ps::aos::V3Dot(angular, other.angular)); } + + +}PX_ALIGN_SUFFIX(16); + +} // namespace Cm + +PX_COMPILE_TIME_ASSERT(sizeof(Cm::SpatialVector) == 32); +PX_COMPILE_TIME_ASSERT(sizeof(Cm::SpatialVectorV) == 32); + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmTask.h b/src/PhysX/physx/source/common/src/CmTask.h new file mode 100644 index 000000000..cf28c7da5 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmTask.h @@ -0,0 +1,267 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_TASK +#define PX_PHYSICS_COMMON_TASK + +#include "task/PxTask.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PsAtomic.h" +#include "PsMutex.h" +#include "PsInlineArray.h" +#include "PsFPU.h" + +namespace physx +{ +namespace Cm +{ + // wrapper around the public PxLightCpuTask + // internal SDK tasks should be inherited from + // this and override the runInternal() method + // to ensure that the correct floating point + // state is set / reset during execution + class Task : public physx::PxLightCpuTask + { + public: + Task(PxU64 contextId) + { + mContextID = contextId; + } + + virtual void run() + { +#if PX_SWITCH // special case because default rounding mode is not nearest + PX_FPU_GUARD; +#else + PX_SIMD_GUARD; +#endif + runInternal(); + } + + virtual void runInternal()=0; + }; + + // same as Cm::Task but inheriting from physx::PxBaseTask + // instead of PxLightCpuTask + class BaseTask : public physx::PxBaseTask + { + public: + + virtual void run() + { +#if PX_SWITCH // special case because default rounding mode is not nearest + PX_FPU_GUARD; +#else + PX_SIMD_GUARD; +#endif + runInternal(); + } + + virtual void runInternal()=0; + }; + + template + class DelegateTask : public Cm::Task, public shdfnd::UserAllocated + { + public: + + DelegateTask(PxU64 contextID, T* obj, const char* name) : Cm::Task(contextID), mObj(obj), mName(name) {} + + virtual void runInternal() + { + (mObj->*Fn)(mCont); + } + + virtual const char* getName() const + { + return mName; + } + + void setObject(T* obj) { mObj = obj; } + + private: + T* mObj; + const char* mName; + }; + + + /** + \brief A task that maintains a list of dependent tasks. + + This task maintains a list of dependent tasks that have their reference counts + reduced on completion of the task. + + The refcount is incremented every time a dependent task is added. + */ + class FanoutTask : public Cm::BaseTask + { + PX_NOCOPY(FanoutTask) + public: + FanoutTask(PxU64 contextID, const char* name) : Cm::BaseTask(), mRefCount(0), mName(name), mNotifySubmission(false) { mContextID = contextID; } + + virtual void runInternal() {} + + virtual const char* getName() const { return mName; } + + /** + Swap mDependents with mReferencesToRemove when refcount goes to 0. + */ + virtual void removeReference() + { + shdfnd::Mutex::ScopedLock lock(mMutex); + if (!physx::shdfnd::atomicDecrement(&mRefCount)) + { + // prevents access to mReferencesToRemove until release + physx::shdfnd::atomicIncrement(&mRefCount); + mNotifySubmission = false; + PX_ASSERT(mReferencesToRemove.empty()); + for (PxU32 i = 0; i < mDependents.size(); i++) + mReferencesToRemove.pushBack(mDependents[i]); + mDependents.clear(); + mTm->getCpuDispatcher()->submitTask(*this); + } + } + + /** + \brief Increases reference count + */ + virtual void addReference() + { + shdfnd::Mutex::ScopedLock lock(mMutex); + physx::shdfnd::atomicIncrement(&mRefCount); + mNotifySubmission = true; + } + + /** + \brief Return the ref-count for this task + */ + PX_INLINE PxI32 getReference() const + { + return mRefCount; + } + + /** + Sets the task manager. Doesn't increase the reference count. + */ + PX_INLINE void setTaskManager(physx::PxTaskManager& tm) + { + mTm = &tm; + } + + /** + Adds a dependent task. It also sets the task manager querying it from the dependent task. + The refcount is incremented every time a dependent task is added. + */ + PX_INLINE void addDependent(physx::PxBaseTask& dependent) + { + shdfnd::Mutex::ScopedLock lock(mMutex); + physx::shdfnd::atomicIncrement(&mRefCount); + mTm = dependent.getTaskManager(); + mDependents.pushBack(&dependent); + dependent.addReference(); + mNotifySubmission = true; + } + + /** + Reduces reference counts of the continuation task and the dependent tasks, also + clearing the copy of continuation and dependents task list. + */ + virtual void release() + { + Ps::InlineArray referencesToRemove; + + { + shdfnd::Mutex::ScopedLock lock(mMutex); + + const PxU32 contCount = mReferencesToRemove.size(); + referencesToRemove.reserve(contCount); + for (PxU32 i=0; i < contCount; ++i) + referencesToRemove.pushBack(mReferencesToRemove[i]); + + mReferencesToRemove.clear(); + // allow access to mReferencesToRemove again + if (mNotifySubmission) + { + removeReference(); + } + else + { + physx::shdfnd::atomicDecrement(&mRefCount); + } + + // the scoped lock needs to get freed before the continuation tasks get (potentially) submitted because + // those continuation tasks might trigger events that delete this task and corrupt the memory of the + // mutex (for example, assume this task is a member of the scene then the submitted tasks cause the simulation + // to finish and then the scene gets released which in turn will delete this task. When this task then finally + // continues the heap memory will be corrupted. + } + + for (PxU32 i=0; i < referencesToRemove.size(); ++i) + referencesToRemove[i]->removeReference(); + } + + protected: + volatile PxI32 mRefCount; + const char* mName; + Ps::InlineArray mDependents; + Ps::InlineArray mReferencesToRemove; + bool mNotifySubmission; + Ps::Mutex mMutex; // guarding mDependents and mNotifySubmission + }; + + + /** + \brief Specialization of FanoutTask class in order to provide the delegation mechanism. + */ + template + class DelegateFanoutTask : public FanoutTask, public shdfnd::UserAllocated + { + public: + DelegateFanoutTask(PxU64 contextID, T* obj, const char* name) : + FanoutTask(contextID, name), mObj(obj) { } + + virtual void runInternal() + { + physx::PxBaseTask* continuation = mReferencesToRemove.empty() ? NULL : mReferencesToRemove[0]; + (mObj->*Fn)(continuation); + } + + void setObject(T* obj) { mObj = obj; } + + private: + T* mObj; + }; + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmTaskPool.h b/src/PhysX/physx/source/common/src/CmTaskPool.h new file mode 100644 index 000000000..8bb36a71b --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmTaskPool.h @@ -0,0 +1,143 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_TASKPOOL +#define PX_PHYSICS_COMMON_TASKPOOL + +#include "foundation/Px.h" +#include "PsMutex.h" +#include "PsSList.h" +#include "PsAllocator.h" +#include "PsArray.h" + +class PxTask; + +/* +Implimentation of a thread safe task pool. (PxTask derived classes). + +T is the actual type of the task(currently NphaseTask or GroupSolveTask). +*/ + +namespace Cm +{ + template class TaskPool : public Ps::AlignedAllocator<16> + { + const static PxU32 TaskPoolSlabSize=64; + + public: + + typedef Ps::SListEntry TaskPoolItem; + + PX_INLINE TaskPool() : slabArray(PX_DEBUG_EXP("taskPoolSlabArray")) + { + //we have to ensure that the list header is 16byte aligned for win64. + freeTasks = (Ps::SList*)allocate(sizeof(Ps::SList), __FILE__, __LINE__); + PX_PLACEMENT_NEW(freeTasks, Ps::SList)(); + + slabArray.reserve(16); + } + + ~TaskPool() + { + Ps::Mutex::ScopedLock lock(slabAllocMutex); + + freeTasks->flush(); + + for(PxU32 i=0;i~SList(); + deallocate(freeTasks); + freeTasks = NULL; + } + } + + T *allocTask() + { + T *rv = static_cast(freeTasks->pop()); + if(rv == NULL) + return static_cast(allocateSlab()); + else + return rv; + } + void freeTask(T *task) + { + freeTasks->push(*task); + } + + private: + + T *allocateSlab() + { + //ack, convoluted memory macros. + + //T *newSlab=new T[TaskPoolSlabSize]; + + // we must align this memory. + T *newSlab=(T *)allocate(sizeof(T)*TaskPoolSlabSize, __FILE__, __LINE__); + + new (newSlab) T(); + + //we keep one for the caller + // and build a list of tasks and insert in the free list + for(PxU32 i=1;ipush(newSlab[i]); + } + + Ps::Mutex::ScopedLock lock(slabAllocMutex); + slabArray.pushBack(newSlab); + + return newSlab; + } + + Ps::Mutex slabAllocMutex; + Ps::Array slabArray; + + Ps::SList *freeTasks; + + }; + + +} // namespace Cm + + +#endif diff --git a/src/PhysX/physx/source/common/src/CmTmpMem.h b/src/PhysX/physx/source/common/src/CmTmpMem.h new file mode 100644 index 000000000..9f30304d8 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmTmpMem.h @@ -0,0 +1,94 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_TMPMEM +#define PX_PHYSICS_COMMON_TMPMEM + +#include "CmPhysXCommon.h" +#include "PsAllocator.h" + +namespace physx +{ +namespace Cm +{ + // dsequeira: we should be able to use PX_ALLOCA or Ps::InlineArray for this, but both seem slightly flawed: + // + // PX_ALLOCA has non-configurable fallback threshold and uses _alloca, which means the allocation is necessarily + // function-scope rather than block-scope (sometimes useful, mostly not.) + // + // Ps::InlineArray touches all memory on resize (a general flaw in the array class which badly needs fixing) + // + // Todo: fix both the above issues. + + template + class TmpMem + { + + public: + PX_FORCE_INLINE TmpMem(PxU32 size): + mPtr(size<=stackLimit?mStackBuf : reinterpret_cast(PX_ALLOC(size*sizeof(T), "char"))) + { + } + + PX_FORCE_INLINE ~TmpMem() + { + if(mPtr!=mStackBuf) + PX_FREE(mPtr); + } + + PX_FORCE_INLINE T& operator*() const + { + return *mPtr; + } + + PX_FORCE_INLINE T* operator->() const + { + return mPtr; + } + + PX_FORCE_INLINE T& operator[](PxU32 index) + { + return mPtr[index]; + } + + T* getBase() + { + return mPtr; + } + + private: + T mStackBuf[stackLimit]; + T* mPtr; + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmTransformUtils.h b/src/PhysX/physx/source/common/src/CmTransformUtils.h new file mode 100644 index 000000000..fe70469b6 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmTransformUtils.h @@ -0,0 +1,145 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_TRANSFORMUTILS +#define PX_PHYSICS_COMMON_TRANSFORMUTILS + +#include "PsVecMath.h" + +namespace +{ + +using namespace physx::shdfnd::aos; + +// V3PrepareCross would help here, but it's not on all platforms yet... + +PX_FORCE_INLINE void transformFast(const FloatVArg wa, const Vec3VArg va, const Vec3VArg pa, + const FloatVArg wb, const Vec3VArg vb, const Vec3VArg pb, + FloatV& wo, Vec3V& vo, Vec3V& po) +{ + wo = FSub(FMul(wa, wb), V3Dot(va, vb)); + vo = V3ScaleAdd(va, wb, V3ScaleAdd(vb, wa, V3Cross(va, vb))); + + const Vec3V t1 = V3Scale(pb, FScaleAdd(wa, wa, FLoad(-0.5f))); + const Vec3V t2 = V3ScaleAdd(V3Cross(va, pb), wa, t1); + const Vec3V t3 = V3ScaleAdd(va, V3Dot(va, pb), t2); + + po = V3ScaleAdd(t3, FLoad(2.f), pa); +} + +PX_FORCE_INLINE void transformInvFast(const FloatVArg wa, const Vec3VArg va, const Vec3VArg pa, + const FloatVArg wb, const Vec3VArg vb, const Vec3VArg pb, + FloatV& wo, Vec3V& vo, Vec3V& po) +{ + wo = FScaleAdd(wa, wb, V3Dot(va, vb)); + vo = V3NegScaleSub(va, wb, V3ScaleAdd(vb, wa, V3Cross(vb, va))); + + const Vec3V pt = V3Sub(pb, pa); + const Vec3V t1 = V3Scale(pt, FScaleAdd(wa, wa, FLoad(-0.5f))); + const Vec3V t2 = V3ScaleAdd(V3Cross(pt, va), wa, t1); + const Vec3V t3 = V3ScaleAdd(va, V3Dot(va, pt), t2); + po = V3Add(t3,t3); +} + +} + + + + + +namespace physx +{ +namespace Cm +{ + +PX_FORCE_INLINE void getStaticGlobalPoseAligned(const PxTransform& actor2World, const PxTransform& shape2Actor, PxTransform& outTransform) +{ + using namespace shdfnd::aos; + + PX_ASSERT((size_t(&actor2World)&15) == 0); + PX_ASSERT((size_t(&shape2Actor)&15) == 0); + PX_ASSERT((size_t(&outTransform)&15) == 0); + + const Vec3V actor2WorldPos = V3LoadA(actor2World.p); + const QuatV actor2WorldRot = QuatVLoadA(&actor2World.q.x); + + const Vec3V shape2ActorPos = V3LoadA(shape2Actor.p); + const QuatV shape2ActorRot = QuatVLoadA(&shape2Actor.q.x); + + Vec3V v,p; + FloatV w; + + transformFast(V4GetW(actor2WorldRot), Vec3V_From_Vec4V(actor2WorldRot), actor2WorldPos, + V4GetW(shape2ActorRot), Vec3V_From_Vec4V(shape2ActorRot), shape2ActorPos, + w, v, p); + + V3StoreA(p, outTransform.p); + V4StoreA(V4SetW(v,w), &outTransform.q.x); +} + + +PX_FORCE_INLINE void getDynamicGlobalPoseAligned(const PxTransform& body2World, const PxTransform& shape2Actor, const PxTransform& body2Actor, PxTransform& outTransform) +{ + PX_ASSERT((size_t(&body2World)&15) == 0); + PX_ASSERT((size_t(&shape2Actor)&15) == 0); + PX_ASSERT((size_t(&body2Actor)&15) == 0); + PX_ASSERT((size_t(&outTransform)&15) == 0); + + using namespace shdfnd::aos; + + const Vec3V shape2ActorPos = V3LoadA(shape2Actor.p); + const QuatV shape2ActorRot = QuatVLoadA(&shape2Actor.q.x); + + const Vec3V body2ActorPos = V3LoadA(body2Actor.p); + const QuatV body2ActorRot = QuatVLoadA(&body2Actor.q.x); + + const Vec3V body2WorldPos = V3LoadA(body2World.p); + const QuatV body2WorldRot = QuatVLoadA(&body2World.q.x); + + Vec3V v1, p1, v2, p2; + FloatV w1, w2; + + transformInvFast(V4GetW(body2ActorRot), Vec3V_From_Vec4V(body2ActorRot), body2ActorPos, + V4GetW(shape2ActorRot), Vec3V_From_Vec4V(shape2ActorRot), shape2ActorPos, + w1, v1, p1); + + transformFast(V4GetW(body2WorldRot), Vec3V_From_Vec4V(body2WorldRot), body2WorldPos, + w1, v1, p1, + w2, v2, p2); + + V3StoreA(p2, outTransform.p); + V4StoreA(V4SetW(v2, w2), &outTransform.q.x); +} + + +} +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmUtils.h b/src/PhysX/physx/source/common/src/CmUtils.h new file mode 100644 index 000000000..c9480bef7 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmUtils.h @@ -0,0 +1,311 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_UTILS +#define PX_PHYSICS_COMMON_UTILS + + +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "foundation/PxBounds3.h" +#include "CmPhysXCommon.h" +#include "PxBase.h" +#include "PsInlineArray.h" +#include "PsArray.h" +#include "PsAllocator.h" + +namespace physx +{ +namespace Cm +{ + +template +PX_FORCE_INLINE PxU32 getArrayOfPointers(DstType** PX_RESTRICT userBuffer, PxU32 bufferSize, PxU32 startIndex, SrcType*const* PX_RESTRICT src, PxU32 size) +{ + const PxU32 remainder = PxU32(PxMax(PxI32(size - startIndex), 0)); + const PxU32 writeCount = PxMin(remainder, bufferSize); + src += startIndex; + for(PxU32 i=0;i(src[i]); + return writeCount; +} + +PX_INLINE void transformInertiaTensor(const PxVec3& invD, const PxMat33& M, PxMat33& mIInv) +{ + const float axx = invD.x*M(0,0), axy = invD.x*M(1,0), axz = invD.x*M(2,0); + const float byx = invD.y*M(0,1), byy = invD.y*M(1,1), byz = invD.y*M(2,1); + const float czx = invD.z*M(0,2), czy = invD.z*M(1,2), czz = invD.z*M(2,2); + + mIInv(0,0) = axx*M(0,0) + byx*M(0,1) + czx*M(0,2); + mIInv(1,1) = axy*M(1,0) + byy*M(1,1) + czy*M(1,2); + mIInv(2,2) = axz*M(2,0) + byz*M(2,1) + czz*M(2,2); + + mIInv(0,1) = mIInv(1,0) = axx*M(1,0) + byx*M(1,1) + czx*M(1,2); + mIInv(0,2) = mIInv(2,0) = axx*M(2,0) + byx*M(2,1) + czx*M(2,2); + mIInv(1,2) = mIInv(2,1) = axy*M(2,0) + byy*M(2,1) + czy*M(2,2); +} + +// PT: TODO: refactor this with PxBounds3 header +PX_FORCE_INLINE PxVec3 basisExtent(const PxVec3& basis0, const PxVec3& basis1, const PxVec3& basis2, const PxVec3& extent) +{ + // extended basis vectors + const PxVec3 c0 = basis0 * extent.x; + const PxVec3 c1 = basis1 * extent.y; + const PxVec3 c2 = basis2 * extent.z; + + // find combination of base vectors that produces max. distance for each component = sum of abs() + return PxVec3 ( PxAbs(c0.x) + PxAbs(c1.x) + PxAbs(c2.x), + PxAbs(c0.y) + PxAbs(c1.y) + PxAbs(c2.y), + PxAbs(c0.z) + PxAbs(c1.z) + PxAbs(c2.z)); +} + +PX_FORCE_INLINE PxBounds3 basisExtent(const PxVec3& center, const PxVec3& basis0, const PxVec3& basis1, const PxVec3& basis2, const PxVec3& extent) +{ + const PxVec3 w = basisExtent(basis0, basis1, basis2, extent); + return PxBounds3(center - w, center + w); +} + +PX_FORCE_INLINE bool isValid(const PxVec3& c, const PxVec3& e) +{ + return (c.isFinite() && e.isFinite() && (((e.x >= 0.0f) && (e.y >= 0.0f) && (e.z >= 0.0f)) || + ((e.x == -PX_MAX_BOUNDS_EXTENTS) && + (e.y == -PX_MAX_BOUNDS_EXTENTS) && + (e.z == -PX_MAX_BOUNDS_EXTENTS)))); +} + +PX_FORCE_INLINE bool isEmpty(const PxVec3& c, const PxVec3& e) +{ + PX_UNUSED(c); + PX_ASSERT(isValid(c, e)); + return e.x<0.0f; +} + +// Array with externally managed storage. +// Allocation and resize policy are managed by the owner, +// Very minimal functionality right now, just POD types + +template +class OwnedArray +{ +public: + OwnedArray() + : mData(0) + , mCapacity(0) + , mSize(0) + {} + + ~OwnedArray() // owner must call releaseMem before destruction + { + PX_ASSERT(mCapacity==0); + } + + void pushBack(T& element, Owner& owner) + { + // there's a failure case if here if we push an existing element which causes a resize - + // a rare case not worth coding around; if you need it, copy the element then push it. + + PX_ASSERT(&element=mData+mSize); + if(mSize==mCapacity) + (owner.*realloc)(mData, mCapacity, mSize, IndexType(mSize+1)); + + PX_ASSERT(mData && mSize=mCapacity) + (owner.*realloc)(mData, mCapacity, mSize, capacity); + } + + void releaseMem(Owner &owner) + { + mSize = 0; + (owner.*realloc)(mData, mCapacity, 0, 0); + } + +private: + T* mData; + IndexType mCapacity; + IndexType mSize; + + // just in case someone tries to use a non-POD in here + union FailIfNonPod + { + T t; + int x; + }; +}; + +/** +Any object deriving from PxBase needs to call this function instead of 'delete object;'. + +We don't want implement 'operator delete' in PxBase because that would impose how +memory of derived classes is allocated. Even though most or all of the time derived classes will +be user allocated, we don't want to put UserAllocatable into the API and derive from that. +*/ +template +PX_INLINE void deletePxBase(T* object) +{ + if(object->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + PX_DELETE(object); + else + object->~T(); +} + +#if PX_CHECKED +/** +Mark a specified amount of memory with 0xcd pattern. This is used to check that the meta data +definition for serialized classes is complete in checked builds. +*/ +PX_INLINE void markSerializedMem(void* ptr, PxU32 byteSize) +{ + for (PxU32 i = 0; i < byteSize; ++i) + reinterpret_cast(ptr)[i] = 0xcd; +} + +/** +Macro to instantiate a type for serialization testing. +Note: Only use PX_NEW_SERIALIZED once in a scope. +*/ +#define PX_NEW_SERIALIZED(v,T) \ + void* _buf = physx::shdfnd::ReflectionAllocator().allocate(sizeof(T),__FILE__,__LINE__); \ + Cm::markSerializedMem(_buf, sizeof(T)); \ + v = PX_PLACEMENT_NEW(_buf, T) + +#else +PX_INLINE void markSerializedMem(void*, PxU32){} + +#define PX_NEW_SERIALIZED(v,T) v = PX_NEW(T) +#endif + +template +struct ArrayAccess: public Ps::Array +{ + void store(PxSerializationContext& context) const + { + if(this->mData && (this->mSize || this->capacity())) + context.writeData(this->mData, this->capacity()*sizeof(T)); + } + + void load(PxDeserializationContext& context) + { + if(this->mData && (this->mSize || this->capacity())) + this->mData = context.readExtraData(this->capacity()); + } +}; + +template +void exportArray(const Ps::Array& a, PxSerializationContext& context) +{ + static_cast&>(a).store(context); +} + +template +void importArray(Ps::Array& a, PxDeserializationContext& context) +{ + static_cast&>(a).load(context); +} + +template +void exportInlineArray(const Ps::InlineArray& a, PxSerializationContext& context) +{ + if(!a.isInlined()) + Cm::exportArray(a, context); +} + +template +void importInlineArray(Ps::InlineArray& a, PxDeserializationContext& context) +{ + if(!a.isInlined()) + Cm::importArray(a, context); +} + +template +static PX_INLINE T* reserveContainerMemory(Ps::Array& container, PxU32 nb) +{ + const PxU32 maxNbEntries = container.capacity(); + const PxU32 requiredSize = container.size() + nb; + + if(requiredSize>maxNbEntries) + { + const PxU32 naturalGrowthSize = maxNbEntries ? maxNbEntries*2 : 2; + const PxU32 newSize = PxMax(requiredSize, naturalGrowthSize); + container.reserve(newSize); + } + + T* buf = container.end(); + container.forceSize_Unsafe(requiredSize); + return buf; +} + +} // namespace Cm + + + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmVisualization.cpp b/src/PhysX/physx/source/common/src/CmVisualization.cpp new file mode 100644 index 000000000..70a54e905 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmVisualization.cpp @@ -0,0 +1,137 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxTransform.h" +#include "CmPhysXCommon.h" +#include "CmRenderOutput.h" +#include "CmVisualization.h" + +using namespace physx; +using namespace Cm; + +void Cm::visualizeJointFrames(RenderOutput& out, PxReal scale, const PxTransform& parent, const PxTransform& child) +{ + if(scale==0.0f) + return; + + out << parent << Cm::DebugBasis(PxVec3(scale, scale, scale) * 1.5f, + PxU32(PxDebugColor::eARGB_DARKRED), PxU32(PxDebugColor::eARGB_DARKGREEN), PxU32(PxDebugColor::eARGB_DARKBLUE)); + out << child << Cm::DebugBasis(PxVec3(scale, scale, scale)); +} + +void Cm::visualizeLinearLimit(RenderOutput& out, PxReal scale, const PxTransform& t0, const PxTransform& /*t1*/, PxReal value, bool active) +{ + if(scale==0.0f) + return; + + // debug circle is around z-axis, and we want it around x-axis + PxTransform r(t0.p+value*t0.q.getBasisVector0(), t0.q*PxQuat(PxPi/2,PxVec3(0,1.f,0))); + out << (active ? PxDebugColor::eARGB_RED : PxDebugColor::eARGB_GREY); + out << PxTransform(PxIdentity); + out << Cm::DebugArrow(t0.p,r.p-t0.p); + + out << r << Cm::DebugCircle(20, scale*0.3f); +} + +void Cm::visualizeAngularLimit(RenderOutput& out, PxReal scale, const PxTransform& t, PxReal lower, PxReal upper, bool active) +{ + if(scale==0.0f) + return; + + out << t << (active ? PxDebugColor::eARGB_RED : PxDebugColor::eARGB_GREY); + + out << Cm::RenderOutput::LINES + << PxVec3(0) << PxVec3(0, PxCos(lower), PxSin(lower)) * scale + << PxVec3(0) << PxVec3(0, PxCos(upper), PxSin(upper)) * scale; + + out << Cm::RenderOutput::LINESTRIP; + PxReal angle = lower, step = (upper-lower)/20; + + for(PxU32 i=0; i<=20; i++, angle += step) + out << PxVec3(0, PxCos(angle), PxSin(angle)) * scale; +} + +void Cm::visualizeLimitCone(RenderOutput& out, PxReal scale, const PxTransform& t, PxReal tanQSwingY, PxReal tanQSwingZ, bool active) +{ + if(scale==0.0f) + return; + + out << t << (active ? PxDebugColor::eARGB_RED : PxDebugColor::eARGB_GREY); + out << Cm::RenderOutput::LINES; + + PxVec3 prev(0,0,0); + + const PxU32 LINES = 32; + + for(PxU32 i=0;i<=LINES;i++) + { + PxReal angle = 2*PxPi/LINES*i; + PxReal c = PxCos(angle), s = PxSin(angle); + PxVec3 rv(0,-tanQSwingZ*s, tanQSwingY*c); + PxReal rv2 = rv.magnitudeSquared(); + PxQuat q = PxQuat(0,2*rv.y,2*rv.z,1-rv2) * (1/(1+rv2)); + PxVec3 a = q.rotate(PxVec3(1.0f,0,0)) * scale; + + out << prev << a << PxVec3(0) << a; + prev = a; + } +} + +void Cm::visualizeDoubleCone(Cm::RenderOutput& out, PxReal scale, const PxTransform& t, PxReal angle, bool active) +{ + if(scale==0.0f) + return; + + out << t << (active ? PxDebugColor::eARGB_RED : PxDebugColor::eARGB_GREY); + + const PxReal height = PxTan(angle); + + const PxU32 LINES = 32; + + out << Cm::RenderOutput::LINESTRIP; + + const PxReal step = PxPi*2/LINES; + + for(PxU32 i=0; i<=LINES; i++) + out << PxVec3(height, PxCos(step * i), PxSin(step * i)) * scale; + + angle = 0; + out << Cm::RenderOutput::LINESTRIP; + for(PxU32 i=0; i<=LINES; i++, angle += PxPi*2/LINES) + out << PxVec3(-height, PxCos(step * i), PxSin(step * i)) * scale; + + angle = 0; + out << Cm::RenderOutput::LINES; + for(PxU32 i=0;i + +static const physx::PxDelayLoadHook* gDelayLoadHook = NULL; + +void physx::PxSetPhysXCommonDelayLoadHook(const physx::PxDelayLoadHook* hook) +{ + gDelayLoadHook = hook; +} + +using namespace physx; + +#pragma comment(lib, "delayimp") + +FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli) +{ + switch (dliNotify) { + case dliStartProcessing : + break; + + case dliNotePreLoadLibrary : + { + return Cm::physXCommonDliNotePreLoadLibrary(pdli->szDll,gDelayLoadHook); + } + break; + + case dliNotePreGetProcAddress : + break; + + case dliFailLoadLib : + break; + + case dliFailGetProc : + break; + + case dliNoteEndProcessing : + break; + + default : + + return NULL; + } + + return NULL; +} + +PfnDliHook __pfnDliNotifyHook2 = delayHook; diff --git a/src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp b/src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp new file mode 100644 index 000000000..b10cba8d8 --- /dev/null +++ b/src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsFoundation.h" + +#ifdef SUPPORT_UPDATE_LOADER_LOGGING +#if PX_X86 +#define NX_USE_SDK_DLLS +#include "PhysXUpdateLoader.h" +#endif +#endif /* SUPPORT_UPDATE_LOADER_LOGGING */ + +#include "windows/CmWindowsModuleUpdateLoader.h" +#include "windows/CmWindowsLoadLibrary.h" + + +namespace physx { namespace Cm { + +#if PX_VC +#pragma warning(disable: 4191) //'operator/operation' : unsafe conversion from 'type of expression' to 'type required' +#endif + + +typedef HMODULE (*GetUpdatedModule_FUNC)(const char*, const char*); + +#ifdef SUPPORT_UPDATE_LOADER_LOGGING +#if PX_X86 +typedef void (*setLogging_FUNC)(PXUL_ErrorCode, pt2LogFunc); + +static void LogMessage(PXUL_ErrorCode messageType, char* message) +{ + switch(messageType) + { + case PXUL_ERROR_MESSAGES: + getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PhysX Update Loader Error: %s.", message); + break; + case PXUL_WARNING_MESSAGES: + getFoundation().error(PX_WARN, "PhysX Update Loader Warning: %s.", message); + break; + case PXUL_INFO_MESSAGES: + getFoundation().error(PX_INFO, "PhysX Update Loader Information: %s.", message); + break; + default: + getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Unknown message type from update loader."); + break; + } +} +#endif +#endif /* SUPPORT_UPDATE_LOADER_LOGGING */ + +CmModuleUpdateLoader::CmModuleUpdateLoader(const char* updateLoaderDllName) + : mGetUpdatedModuleFunc(NULL) +{ + mUpdateLoaderDllHandle = loadLibrary(updateLoaderDllName); + + if (mUpdateLoaderDllHandle != NULL) + { + mGetUpdatedModuleFunc = GetProcAddress(mUpdateLoaderDllHandle, "GetUpdatedModule"); + +#ifdef SUPPORT_UPDATE_LOADER_LOGGING +#if PX_X86 + setLogging_FUNC setLoggingFunc; + setLoggingFunc = (setLogging_FUNC)GetProcAddress(mUpdateLoaderDllHandle, "setLoggingFunction"); + if(setLoggingFunc != NULL) + { + setLoggingFunc(PXUL_ERROR_MESSAGES, LogMessage); + } +#endif +#endif /* SUPPORT_UPDATE_LOADER_LOGGING */ + } +} + +CmModuleUpdateLoader::~CmModuleUpdateLoader() +{ + if (mUpdateLoaderDllHandle != NULL) + { + FreeLibrary(mUpdateLoaderDllHandle); + mUpdateLoaderDllHandle = NULL; + } +} + +HMODULE CmModuleUpdateLoader::LoadModule(const char* moduleName, const char* appGUID) +{ + HMODULE result = NULL; + + if (mGetUpdatedModuleFunc != NULL) + { + // Try to get the module through PhysXUpdateLoader + GetUpdatedModule_FUNC getUpdatedModuleFunc = (GetUpdatedModule_FUNC)mGetUpdatedModuleFunc; + result = getUpdatedModuleFunc(moduleName, appGUID); + } + else + { + // If no PhysXUpdateLoader, just load the DLL directly + result = loadLibrary(moduleName); + } + + return result; +} + +}; // end of namespace +}; // end of namespace diff --git a/src/PhysX/physx/source/compiler/cmake/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/CMakeLists.txt new file mode 100644 index 000000000..f8747b862 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/CMakeLists.txt @@ -0,0 +1,142 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +cmake_minimum_required(VERSION 3.7) + +project(PhysX C CXX) + +CMAKE_POLICY(SET CMP0057 NEW) # Enable IN_LIST + +OPTION(PX_GENERATE_GPU_PROJECTS "Generate the GPU projects, if possible." OFF) +OPTION(PX_GENERATE_GPU_PROJECTS_ONLY "Generate ONLY the GPU projects, if possible." OFF) +OPTION(PX_GENERATE_GPU_STATIC_LIBRARIES "Generate PhysXGPU static libraries" OFF) +OPTION(PX_SCALAR_MATH "Disable SIMD math" OFF) +OPTION(PX_GENERATE_STATIC_LIBRARIES "Generate static libraries" OFF) +OPTION(PX_EXPORT_LOWLEVEL_PDB "Export low level pdb's" OFF) + +IF(NOT DEFINED PHYSX_ROOT_DIR) + + STRING(REPLACE "\\" "/" BRD_TEMP $ENV{PHYSX_ROOT_DIR}) + + # This env variable is set by GenerateProjects.bat, and is no longer available when CMake rebuilds, so this stores it in the cache + SET(PHYSX_ROOT_DIR ${BRD_TEMP} CACHE INTERNAL "Root of the PhysX source tree") + +ENDIF() + +MESSAGE("PHYSX ROOT ${PHYSX_ROOT_DIR}") + +IF(NOT EXISTS ${PHYSX_ROOT_DIR}) + MESSAGE(FATAL_ERROR "PHYSX_ROOT_DIR environment variable wasn't set or was invalid.") +ENDIF() + +IF(NOT DEFINED CMAKEMODULES_VERSION) + SET(CMAKEMODULES_PATH $ENV{PM_CMakeModules_PATH} CACHE INTERNAL "Path to CMakeModules") + SET(CMAKEMODULES_NAME $ENV{PM_CMakeModules_NAME} CACHE INTERNAL "CMakeModules name") + SET(CMAKEMODULES_VERSION $ENV{PM_CMakeModules_VERSION} CACHE INTERNAL "CMakeModules version from generation batch") +ENDIF() + +#TODO: More elegance +IF(NOT EXISTS ${CMAKEMODULES_PATH}) + MESSAGE(FATAL_ERROR "Could not find ${CMAKEMODULES_PATH}") +ENDIF() + +SET(CMAKE_MODULE_PATH ${CMAKEMODULES_PATH}) + + +MESSAGE("PhysX Build Platform: " ${TARGET_BUILD_PLATFORM}) +MESSAGE("Using CXX Compiler: " ${CMAKE_CXX_COMPILER}) + +INCLUDE(NvidiaBuildOptions) + +IF (NOT EXISTS ${PXSHARED_PATH}) + IF (NOT EXISTS $ENV{PM_PxShared_PATH}) + MESSAGE(FATAL_ERROR "Trying to set PxShared path, but PM_PxShared_PATH is not valid: '$ENV{PM_PxShared_PATH}'.") + ENDIF() + SET(PXSHARED_PATH_TMP $ENV{PM_PxShared_PATH}) + + STRING(REPLACE "\\" "/" PXSHARED_PATH_TMP ${PXSHARED_PATH_TMP}) + SET(PXSHARED_PATH ${PXSHARED_PATH_TMP} CACHE INTERNAL "Path to PxShared") + + STRING(REGEX REPLACE "/PhysX$" "/PxShared" PXSHARED_INSTALL_PREFIX_TMP ${CMAKE_INSTALL_PREFIX}) + SET(PXSHARED_INSTALL_PREFIX ${PXSHARED_INSTALL_PREFIX_TMP} CACHE INTERNAL "Path to PxShared") +ENDIF() + +IF(CMAKE_CONFIGURATION_TYPES) + SET(CMAKE_CONFIGURATION_TYPES debug checked profile release) + SET(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING + "Reset config to what we need" + FORCE) + + SET(CMAKE_SHARED_LINKER_FLAGS_CHECKED "") + SET(CMAKE_SHARED_LINKER_FLAGS_PROFILE "") + + # Build PDBs for all configurations + SET(CMAKE_SHARED_LINKER_FLAGS "/DEBUG") + +ENDIF() + +# Prevent failure due to command line limitations +IF(USE_RESPONSE_FILES) + SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) + SET(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1) + SET(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES 1) + SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1) + SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1) + SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_LIBRARIES 1) +ENDIF() + +IF($ENV{PHYSX_AUTOBUILD}) + IF($ENV{PHYSX_AUTOBUILD} STREQUAL "1") + SET(PHYSX_AUTOBUILD "PHYSX_AUTOBUILD") + ENDIF() +ENDIF() + +IF($ENV{AQUA_BUILD_CHANGELIST}) + SET(PHYSX_AUTOBUILDNUMBER PX_BUILD_NUMBER=$ENV{AQUA_BUILD_CHANGELIST}) +ELSE() + SET(PHYSX_AUTOBUILDNUMBER PX_BUILD_NUMBER=0) +ENDIF() + +SET(PROJECT_CMAKE_FILES_DIR source/compiler/cmake) +SET(PLATFORM_CMAKELISTS ${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/CMakeLists.txt) + +IF(NOT EXISTS ${PLATFORM_CMAKELISTS}) + MESSAGE(FATAL_ERROR "Unable to find platform CMakeLists.txt for ${TARGET_BUILD_PLATFORM} at ${PLATFORM_CMAKELISTS}") +ENDIF() + +SET(CMAKE_POSITION_INDEPENDENT_CODE ON) + +SET(SOURCE_DISTRO_FILE_LIST "") + +# Include the platform specific CMakeLists +INCLUDE(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/CMakeLists.txt) + +IF(PX_GENERATE_SOURCE_DISTRO) + FOREACH(FILE_NAME ${SOURCE_DISTRO_FILE_LIST}) + FILE(APPEND "${CMAKE_CURRENT_BINARY_DIR}/source_distro_list.txt" "${FILE_NAME}\n") + ENDFOREACH() +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/FastXml.cmake new file mode 100644 index 000000000..4ac700275 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/FastXml.cmake @@ -0,0 +1,92 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml common +# + + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/fastxml) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/FastXml.cmake) + + +SET(FASTXML_HEADERS + ${LL_SOURCE_DIR}/include/PsFastXml.h +) +SOURCE_GROUP(include FILES ${FASTXML_HEADERS}) + +SET(FASTXML_SOURCE + ${LL_SOURCE_DIR}/src/PsFastXml.cpp +) +SOURCE_GROUP(src FILES ${FASTXML_SOURCE}) + +ADD_LIBRARY(FastXml ${FASTXML_LIBTYPE} + ${FASTXML_HEADERS} + ${FASTXML_SOURCE} +) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(FastXml + PRIVATE ${PLATFORM_INCLUDES} + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + PRIVATE ${LL_SOURCE_DIR}/include +) + +TARGET_COMPILE_DEFINITIONS(FastXml + PRIVATE ${FASTXML_COMPILE_DEFS} +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND FASTXML_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(FastXml PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "FastXml_static" + ARCHIVE_OUTPUT_NAME_CHECKED "FastXml_static" + ARCHIVE_OUTPUT_NAME_PROFILE "FastXml_static" + ARCHIVE_OUTPUT_NAME_RELEASE "FastXml_static" + ) +ENDIF() + +IF(FASTXML_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(FastXml PROPERTIES + COMPILE_PDB_NAME_DEBUG "${FASTXML_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${FASTXML_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${FASTXML_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${FASTXML_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${FASTXML_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${FASTXML_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(FastXml PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + diff --git a/src/PhysX/physx/source/compiler/cmake/IfTemplate.txt b/src/PhysX/physx/source/compiler/cmake/IfTemplate.txt new file mode 100644 index 000000000..5561e9ae7 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/IfTemplate.txt @@ -0,0 +1,32 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +IF(TARGET_BUILD_PLATFORM STREQUAL "Windows") +ELSEIF(TARGET_BUILD_PLATFORM STREQUAL "PS4") +ELSEIF(TARGET_BUILD_PLATFORM STREQUAL "XboxOne") +ELSEIF(TARGET_BUILD_PLATFORM STREQUAL "Unix") +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/LowLevel.cmake new file mode 100644 index 000000000..8b97e6b98 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/LowLevel.cmake @@ -0,0 +1,235 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/lowlevel) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/LowLevel.cmake) + + +SET(LL_API_DIR ${LL_SOURCE_DIR}/api/) +SET(LL_API_HEADERS + ${LL_API_DIR}/include/PxsMaterialCore.h + ${LL_API_DIR}/include/PxsMaterialManager.h + ${LL_API_DIR}/include/PxvConfig.h + ${LL_API_DIR}/include/PxvDynamics.h + ${LL_API_DIR}/include/PxvGeometry.h + ${LL_API_DIR}/include/PxvGlobals.h + ${LL_API_DIR}/include/PxvManager.h + ${LL_API_DIR}/include/PxvSimStats.h +) +SOURCE_GROUP("API Includes" FILES ${LL_API_HEADERS}) + +SET(LL_API_SOURCE + ${LL_API_DIR}/src/px_globals.cpp +) +SOURCE_GROUP("API Source" FILES ${LL_API_SOURCE}) + +SET(LL_COMMON_DIR ${LL_SOURCE_DIR}/common/) +SET(LL_COMMON_COLLISION_HEADERS + ${LL_COMMON_DIR}/include/collision/PxcContactMethodImpl.h +) +SOURCE_GROUP("Common Includes\\collision" FILES ${LL_COMMON_COLLISION_HEADERS}) +SET(LL_COMMON_PIPELINE_HEADERS + ${LL_COMMON_DIR}/include/pipeline/PxcCCDStateStreamPair.h + ${LL_COMMON_DIR}/include/pipeline/PxcConstraintBlockStream.h + ${LL_COMMON_DIR}/include/pipeline/PxcContactCache.h + ${LL_COMMON_DIR}/include/pipeline/PxcMaterialMethodImpl.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpBatch.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpCache.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpCacheStreamPair.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpContactPrepShared.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpMemBlockPool.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpThreadContext.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpWorkUnit.h + ${LL_COMMON_DIR}/include/pipeline/PxcRigidBody.h +) +SOURCE_GROUP("Common Includes\\pipeline" FILES ${LL_COMMON_PIPELINE_HEADERS}) +SET(LL_COMMON_UTILS_HEADERS + ${LL_COMMON_DIR}/include/utils/PxcScratchAllocator.h + ${LL_COMMON_DIR}/include/utils/PxcThreadCoherentCache.h +) +SOURCE_GROUP("Common Includes\\utils" FILES ${LL_COMMON_UTILS_HEADERS}) + +SET(LL_COMMON_COLLISION_SOURCE + ${LL_COMMON_DIR}/src/collision/PxcContact.cpp +) +SOURCE_GROUP("Common Source\\collision" FILES ${LL_COMMON_COLLISION_SOURCE}) +SET(LL_COMMON_PIPELINE_SOURCE + ${LL_COMMON_DIR}/src/pipeline/PxcContactCache.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcContactMethodImpl.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcMaterialHeightField.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcMaterialMesh.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcMaterialMethodImpl.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcMaterialShape.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcNpBatch.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcNpCacheStreamPair.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcNpContactPrepShared.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcNpMemBlockPool.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcNpThreadContext.cpp +) +SOURCE_GROUP("Common Source\\pipeline" FILES ${LL_COMMON_PIPELINE_SOURCE}) + +SET(LL_SOFTWARE_DIR ${LL_SOURCE_DIR}/software/) +SET(LL_SOFTWARE_HEADERS + ${LL_SOFTWARE_DIR}/include/PxsBodySim.h + ${LL_SOFTWARE_DIR}/include/PxsCCD.h + ${LL_SOFTWARE_DIR}/include/PxsContactManager.h + ${LL_SOFTWARE_DIR}/include/PxsContactManagerState.h + ${LL_SOFTWARE_DIR}/include/PxsContext.h + ${LL_SOFTWARE_DIR}/include/PxsDefaultMemoryManager.h + ${LL_SOFTWARE_DIR}/include/PxsHeapMemoryAllocator.h + ${LL_SOFTWARE_DIR}/include/PxsIncrementalConstraintPartitioning.h + ${LL_SOFTWARE_DIR}/include/PxsIslandManagerTypes.h + ${LL_SOFTWARE_DIR}/include/PxsIslandSim.h + ${LL_SOFTWARE_DIR}/include/PxsIslandNodeIndex.h + ${LL_SOFTWARE_DIR}/include/PxsKernelWrangler.h + ${LL_SOFTWARE_DIR}/include/PxsMaterialCombiner.h + ${LL_SOFTWARE_DIR}/include/PxsMemoryManager.h + ${LL_SOFTWARE_DIR}/include/PxsNphaseImplementationContext.h + ${LL_SOFTWARE_DIR}/include/PxsRigidBody.h + ${LL_SOFTWARE_DIR}/include/PxsShapeSim.h + ${LL_SOFTWARE_DIR}/include/PxsSimpleIslandManager.h + ${LL_SOFTWARE_DIR}/include/PxsSimulationController.h + ${LL_SOFTWARE_DIR}/include/PxsTransformCache.h + ${LL_SOFTWARE_DIR}/include/PxvNphaseImplementationContext.h +) +SOURCE_GROUP("Software Includes" FILES ${LL_SOFTWARE_HEADERS}) +SET(LL_SOFTWARE_SOURCE + ${LL_SOFTWARE_DIR}/src/PxsCCD.cpp + ${LL_SOFTWARE_DIR}/src/PxsContactManager.cpp + ${LL_SOFTWARE_DIR}/src/PxsContext.cpp + ${LL_SOFTWARE_DIR}/src/PxsDefaultMemoryManager.cpp + ${LL_SOFTWARE_DIR}/src/PxsIslandSim.cpp + ${LL_SOFTWARE_DIR}/src/PxsMaterialCombiner.cpp + ${LL_SOFTWARE_DIR}/src/PxsNphaseImplementationContext.cpp + ${LL_SOFTWARE_DIR}/src/PxsSimpleIslandManager.cpp +) +SOURCE_GROUP("Software Source" FILES ${LL_SOFTWARE_SOURCE}) + +ADD_LIBRARY(LowLevel ${LOWLEVEL_LIBTYPE} + ${LL_API_HEADERS} + ${LL_API_SOURCE} + + ${LL_COMMON_COLLISION_HEADERS} + ${LL_COMMON_COLLISION_SOURCE} + + ${LL_COMMON_PIPELINE_HEADERS} + ${LL_COMMON_PIPELINE_SOURCE} + + ${LL_COMMON_UTILS_HEADERS} + + ${LL_SOFTWARE_HEADERS} + ${LL_SOFTWARE_SOURCE} +) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(LowLevel + PRIVATE ${LOWLEVEL_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/collision + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/utils + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/software/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowleveldynamics/include + +) + +TARGET_COMPILE_DEFINITIONS(LowLevel + PRIVATE ${LOWLEVEL_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(LowLevel PROPERTIES + LINK_FLAGS ${LOWLEVEL_PLATFORM_LINK_FLAGS} +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) + SET_TARGET_PROPERTIES(LowLevel PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "LowLevel_static" + ARCHIVE_OUTPUT_NAME_CHECKED "LowLevel_static" + ARCHIVE_OUTPUT_NAME_PROFILE "LowLevel_static" + ARCHIVE_OUTPUT_NAME_RELEASE "LowLevel_static" + ) +ENDIF() + +IF(LOWLEVEL_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(LowLevel PROPERTIES + COMPILE_PDB_NAME_DEBUG "${LOWLEVEL_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${LOWLEVEL_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${LOWLEVEL_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${LOWLEVEL_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_SOFTWARE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_SOFTWARE_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_API_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_API_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_COMMON_COLLISION_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_COMMON_COLLISION_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_COMMON_PIPELINE_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_COMMON_PIPELINE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_COMMON_UTILS_HEADERS}) +ENDIF() + +SET_TARGET_PROPERTIES(LowLevel PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake new file mode 100644 index 000000000..1a655d664 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake @@ -0,0 +1,131 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LLAABB_DIR ${PHYSX_SOURCE_DIR}/lowlevelaabb) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/LowLevelAABB.cmake) + + +SET(LLAABB_HEADERS + ${LLAABB_DIR}/include/BpAABBManager.h + ${LLAABB_DIR}/include/BpAABBManagerTasks.h + ${LLAABB_DIR}/include/BpBroadPhase.h + ${LLAABB_DIR}/include/BpBroadPhaseUpdate.h +) +SOURCE_GROUP("includes" FILES ${LLAABB_HEADERS}) + +SET(LLAABB_SOURCE + ${LLAABB_DIR}/src/BpAABBManager.cpp + ${LLAABB_DIR}/src/BpBroadPhase.cpp + ${LLAABB_DIR}/src/BpBroadPhaseABP.cpp + ${LLAABB_DIR}/src/BpBroadPhaseABP.h + ${LLAABB_DIR}/src/BpBroadPhaseMBP.cpp + ${LLAABB_DIR}/src/BpBroadPhaseMBP.h + ${LLAABB_DIR}/src/BpBroadPhaseMBPCommon.h + ${LLAABB_DIR}/src/BpBroadPhaseSap.cpp + ${LLAABB_DIR}/src/BpBroadPhaseSap.h + ${LLAABB_DIR}/src/BpBroadPhaseSapAux.cpp + ${LLAABB_DIR}/src/BpBroadPhaseSapAux.h + ${LLAABB_DIR}/src/BpBroadPhaseShared.cpp + ${LLAABB_DIR}/src/BpBroadPhaseShared.h + ${LLAABB_DIR}/src/BpMBPTasks.cpp + ${LLAABB_DIR}/src/BpMBPTasks.h + ${LLAABB_DIR}/src/BpSAPTasks.cpp + ${LLAABB_DIR}/src/BpSAPTasks.h +) +SOURCE_GROUP("src" FILES ${LLAABB_SOURCE}) + +ADD_LIBRARY(LowLevelAABB ${LOWLEVELAABB_LIBTYPE} + ${LLAABB_HEADERS} + ${LLAABB_SOURCE} +) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(LowLevelAABB + PRIVATE ${LOWLEVELAABB_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/utils + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevelaabb/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevelaabb/src +) + +TARGET_COMPILE_DEFINITIONS(LowLevelAABB + PRIVATE ${LOWLEVELAABB_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(LowLevelAABB PROPERTIES + LINK_FLAGS ${LOWLEVELAABB_PLATFORM_LINK_FLAGS} +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) + SET_TARGET_PROPERTIES(LowLevelAABB PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "LowLevelAABB_static" + ARCHIVE_OUTPUT_NAME_CHECKED "LowLevelAABB_static" + ARCHIVE_OUTPUT_NAME_PROFILE "LowLevelAABB_static" + ARCHIVE_OUTPUT_NAME_RELEASE "LowLevelAABB_static" + ) +ENDIF() + +IF(LOWLEVELAABB_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(LowLevelAABB PROPERTIES + COMPILE_PDB_NAME_DEBUG "${LOWLEVELAABB_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${LOWLEVELAABB_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${LOWLEVELAABB_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${LOWLEVELAABB_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LLAABB_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LLAABB_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(LowLevelAABB PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake new file mode 100644 index 000000000..0069c413f --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake @@ -0,0 +1,202 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/lowleveldynamics/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/LowLevelDynamics.cmake) + + +SET(LLDYNAMICS_BASE_DIR ${PHYSX_ROOT_DIR}/source/lowleveldynamics) +SET(LLDYNAMICS_INCLUDES + ${LLDYNAMICS_BASE_DIR}/include/DyArticulationCore.h + ${LLDYNAMICS_BASE_DIR}/include/DyVArticulation.h + ${LLDYNAMICS_BASE_DIR}/include/DyArticulation.h + ${LLDYNAMICS_BASE_DIR}/include/DyFeatherstoneArticulation.h + ${LLDYNAMICS_BASE_DIR}/include/DyFeatherstoneArticulationJointData.h + ${LLDYNAMICS_BASE_DIR}/include/DyFeatherstoneArticulationUtils.h + ${LLDYNAMICS_BASE_DIR}/include/DyConstraint.h + ${LLDYNAMICS_BASE_DIR}/include/DyConstraintWriteBack.h + ${LLDYNAMICS_BASE_DIR}/include/DyContext.h + ${LLDYNAMICS_BASE_DIR}/include/DySleepingConfigulation.h + ${LLDYNAMICS_BASE_DIR}/include/DyThresholdTable.h + ${LLDYNAMICS_BASE_DIR}/include/DyArticulationJointCore.h +) +SOURCE_GROUP("Dynamics Includes" FILES ${LLDYNAMICS_INCLUDES}) + +SET(LLDYNAMICS_SOURCE + ${LLDYNAMICS_BASE_DIR}/src/DyArticulation.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationContactPrep.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationContactPrepPF.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationHelper.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationScalar.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationSIMD.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyFeatherstoneArticulation.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyFeatherstoneForwardDynamic.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyFeatherstoneInverseDynamic.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyConstraintPartition.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyConstraintSetup.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyConstraintSetupBlock.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrep.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrep4.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrep4PF.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrepPF.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyDynamics.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyFrictionCorrelation.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyRigidBodyToSolverBody.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraints.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraintsBlock.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverControl.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverControlPF.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverPFConstraints.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverPFConstraintsBlock.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraint1DStep.h + ${LLDYNAMICS_BASE_DIR}/src/DyThreadContext.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyThresholdTable.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyTGSDynamics.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyTGSPartition.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyTGSContactPrep.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyTGSContactPrepBlock.cpp +) +SOURCE_GROUP("Dynamics Source" FILES ${LLDYNAMICS_SOURCE}) + +SET(LLDYNAMICS_INTERNAL_INCLUDES + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationContactPrep.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationFnsDebug.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationFnsScalar.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationFnsSimd.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationHelper.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationPImpl.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationReference.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationScalar.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationUtils.h + ${LLDYNAMICS_BASE_DIR}/src/DyFeatherstoneArticulationLink.h + ${LLDYNAMICS_BASE_DIR}/src/DyBodyCoreIntegrator.h + ${LLDYNAMICS_BASE_DIR}/src/DyConstraintPartition.h + ${LLDYNAMICS_BASE_DIR}/src/DyConstraintPrep.h + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrep.h + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrepShared.h + ${LLDYNAMICS_BASE_DIR}/src/DyContactReduction.h + ${LLDYNAMICS_BASE_DIR}/src/DyCorrelationBuffer.h + ${LLDYNAMICS_BASE_DIR}/src/DyDynamics.h + ${LLDYNAMICS_BASE_DIR}/src/DyFrictionPatch.h + ${LLDYNAMICS_BASE_DIR}/src/DyFrictionPatchStreamPair.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverBody.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraint1D.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraint1D4.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraintDesc.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraintExtShared.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraintsShared.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraintTypes.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverContact.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverContact4.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverContactPF.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverContactPF4.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverContext.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverControl.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverControlPF.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverCore.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverExt.h + ${LLDYNAMICS_BASE_DIR}/src/DySpatial.h + ${LLDYNAMICS_BASE_DIR}/src/DyThreadContext.h + ${LLDYNAMICS_BASE_DIR}/src/DyTGSPartition.h + ${LLDYNAMICS_BASE_DIR}/src/DyTGSDynamics.h +) +SOURCE_GROUP("Dynamics Internal Includes" FILES ${LLDYNAMICS_INTERNAL_INCLUDES}) + +ADD_LIBRARY(LowLevelDynamics ${LOWLEVELDYNAMICS_LIBTYPE} + ${LLDYNAMICS_INCLUDES} + ${LLDYNAMICS_SOURCE} + ${LLDYNAMICS_INTERNAL_INCLUDES} +) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(LowLevelDynamics + PRIVATE ${LOWLEVELDYNAMICS_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/utils + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/software/include + + PRIVATE ${PHYSX_SOURCE_DIR}/lowleveldynamics/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowleveldynamics/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include +) + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(LowLevelDynamics + + # Common to all configurations + PRIVATE ${LOWLEVELDYNAMICS_COMPILE_DEFS} +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) + SET_TARGET_PROPERTIES(LowLevelDynamics PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "LowLevelDynamics_static" + ARCHIVE_OUTPUT_NAME_CHECKED "LowLevelDynamics_static" + ARCHIVE_OUTPUT_NAME_PROFILE "LowLevelDynamics_static" + ARCHIVE_OUTPUT_NAME_RELEASE "LowLevelDynamics_static" + ) +ENDIF() + +IF(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(LowLevelDynamics PROPERTIES + COMPILE_PDB_NAME_DEBUG "${LOWLEVELDYNAMICS_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${LOWLEVELDYNAMICS_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${LOWLEVELDYNAMICS_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${LOWLEVELDYNAMICS_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LLDYNAMICS_INCLUDES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LLDYNAMICS_INTERNAL_INCLUDES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LLDYNAMICS_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(LowLevelDynamics PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/PhysX.cmake new file mode 100644 index 000000000..a4628160e --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysX.cmake @@ -0,0 +1,382 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(PX_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physx/src) +SET(MD_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physxmetadata) + +SET(PHYSX_PLATFORM_LINK_FLAGS " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_DEBUG " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_CHECKED " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_PROFILE " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_RELEASE " ") + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysX.cmake) + +SET(PHYSX_HEADERS + ${PHYSX_ROOT_DIR}/include/PxActor.h + ${PHYSX_ROOT_DIR}/include/PxAggregate.h + ${PHYSX_ROOT_DIR}/include/PxArticulationReducedCoordinate.h + ${PHYSX_ROOT_DIR}/include/PxArticulationBase.h + ${PHYSX_ROOT_DIR}/include/PxArticulation.h + ${PHYSX_ROOT_DIR}/include/PxArticulationJoint.h + ${PHYSX_ROOT_DIR}/include/PxArticulationJointReducedCoordinate.h + ${PHYSX_ROOT_DIR}/include/PxArticulationLink.h + ${PHYSX_ROOT_DIR}/include/PxBatchQuery.h + ${PHYSX_ROOT_DIR}/include/PxBatchQueryDesc.h + ${PHYSX_ROOT_DIR}/include/PxBroadPhase.h + ${PHYSX_ROOT_DIR}/include/PxClient.h + ${PHYSX_ROOT_DIR}/include/PxConstraint.h + ${PHYSX_ROOT_DIR}/include/PxConstraintDesc.h + ${PHYSX_ROOT_DIR}/include/PxContact.h + ${PHYSX_ROOT_DIR}/include/PxContactModifyCallback.h + ${PHYSX_ROOT_DIR}/include/PxDeletionListener.h + ${PHYSX_ROOT_DIR}/include/PxFiltering.h + ${PHYSX_ROOT_DIR}/include/PxForceMode.h + ${PHYSX_ROOT_DIR}/include/PxImmediateMode.h + ${PHYSX_ROOT_DIR}/include/PxLockedData.h + ${PHYSX_ROOT_DIR}/include/PxMaterial.h + ${PHYSX_ROOT_DIR}/include/PxPhysics.h + ${PHYSX_ROOT_DIR}/include/PxPhysicsAPI.h + ${PHYSX_ROOT_DIR}/include/PxPhysicsSerialization.h + ${PHYSX_ROOT_DIR}/include/PxPhysicsVersion.h + ${PHYSX_ROOT_DIR}/include/PxPhysXConfig.h + ${PHYSX_ROOT_DIR}/include/PxPruningStructure.h + ${PHYSX_ROOT_DIR}/include/PxQueryFiltering.h + ${PHYSX_ROOT_DIR}/include/PxQueryReport.h + ${PHYSX_ROOT_DIR}/include/PxRigidActor.h + ${PHYSX_ROOT_DIR}/include/PxRigidBody.h + ${PHYSX_ROOT_DIR}/include/PxRigidDynamic.h + ${PHYSX_ROOT_DIR}/include/PxRigidStatic.h + ${PHYSX_ROOT_DIR}/include/PxScene.h + ${PHYSX_ROOT_DIR}/include/PxSceneDesc.h + ${PHYSX_ROOT_DIR}/include/PxSceneLock.h + ${PHYSX_ROOT_DIR}/include/PxShape.h + ${PHYSX_ROOT_DIR}/include/PxSimulationEventCallback.h + ${PHYSX_ROOT_DIR}/include/PxSimulationStatistics.h + ${PHYSX_ROOT_DIR}/include/PxVisualizationParameter.h +) +SOURCE_GROUP(include FILES ${PHYSX_HEADERS}) + +SET(PHYSX_COMMON_HEADERS + ${PHYSX_ROOT_DIR}/include/common/PxBase.h + ${PHYSX_ROOT_DIR}/include/common/PxCollection.h + ${PHYSX_ROOT_DIR}/include/common/PxCoreUtilityTypes.h + ${PHYSX_ROOT_DIR}/include/common/PxMetaData.h + ${PHYSX_ROOT_DIR}/include/common/PxMetaDataFlags.h + ${PHYSX_ROOT_DIR}/include/common/PxPhysicsInsertionCallback.h + ${PHYSX_ROOT_DIR}/include/common/PxPhysXCommonConfig.h + ${PHYSX_ROOT_DIR}/include/common/PxRenderBuffer.h + ${PHYSX_ROOT_DIR}/include/common/PxSerialFramework.h + ${PHYSX_ROOT_DIR}/include/common/PxSerializer.h + ${PHYSX_ROOT_DIR}/include/common/PxStringTable.h + ${PHYSX_ROOT_DIR}/include/common/PxTolerancesScale.h + ${PHYSX_ROOT_DIR}/include/common/PxTypeInfo.h + ${PHYSX_ROOT_DIR}/include/common/PxProfileZone.h +) +SOURCE_GROUP(include\\common FILES ${PHYSX_COMMON_HEADERS}) + +SET(PHYSX_PVD_HEADERS + ${PHYSX_ROOT_DIR}/include/pvd/PxPvdSceneClient.h + ${PHYSX_ROOT_DIR}/include/pvd/PxPvd.h + ${PHYSX_ROOT_DIR}/include/pvd/PxPvdTransport.h +) +SOURCE_GROUP(include\\pvd FILES ${PHYSX_PVD_HEADERS}) + +SET(PHYSX_COLLISION_HEADERS + ${PHYSX_ROOT_DIR}/include/collision/PxCollisionDefs.h +) +SOURCE_GROUP(include\\collision FILES ${PHYSX_COLLISION_HEADERS}) + +SET(PHYSX_SOLVER_HEADERS + ${PHYSX_ROOT_DIR}/include/solver/PxSolverDefs.h +) +SOURCE_GROUP(include\\collision FILES ${PHYSX_SOLVER_HEADERS}) + +SET(PHYSX_METADATA_HEADERS + ${MD_SOURCE_DIR}/core/include/PvdMetaDataDefineProperties.h + ${MD_SOURCE_DIR}/core/include/PvdMetaDataExtensions.h + ${MD_SOURCE_DIR}/core/include/PvdMetaDataPropertyVisitor.h + ${MD_SOURCE_DIR}/core/include/PxAutoGeneratedMetaDataObjectNames.h + ${MD_SOURCE_DIR}/core/include/PxAutoGeneratedMetaDataObjects.h + ${MD_SOURCE_DIR}/core/include/PxMetaDataCompare.h + ${MD_SOURCE_DIR}/core/include/PxMetaDataCppPrefix.h + ${MD_SOURCE_DIR}/core/include/PxMetaDataObjects.h + ${MD_SOURCE_DIR}/core/include/RepXMetaDataPropertyVisitor.h +) +SOURCE_GROUP(metadata\\include FILES ${PHYSX_METADATA_HEADERS}) + +SET(PHYSX_METADATA_SOURCE + ${MD_SOURCE_DIR}/core/src/PxAutoGeneratedMetaDataObjects.cpp + ${MD_SOURCE_DIR}/core/src/PxMetaDataObjects.cpp +) +SOURCE_GROUP(metadata\\src FILES ${PHYSX_METADATA_SOURCE}) + +SET(PHYSX_IMMEDIATEMODE_SOURCE + ${PHYSX_ROOT_DIR}/source/immediatemode/src/NpImmediateMode.cpp +) +SOURCE_GROUP(src\\immediatemode FILES ${PHYSX_IMMEDIATEMODE_SOURCE}) + +SET(PHYSX_BUFFERING_SOURCE + ${PX_SOURCE_DIR}/buffering/ScbActor.cpp + ${PX_SOURCE_DIR}/buffering/ScbAggregate.cpp + ${PX_SOURCE_DIR}/buffering/ScbBase.cpp + ${PX_SOURCE_DIR}/buffering/ScbMetaData.cpp + ${PX_SOURCE_DIR}/buffering/ScbScene.cpp + ${PX_SOURCE_DIR}/buffering/ScbScenePvdClient.cpp + ${PX_SOURCE_DIR}/buffering/ScbShape.cpp + ${PX_SOURCE_DIR}/buffering/ScbActor.h + ${PX_SOURCE_DIR}/buffering/ScbAggregate.h + ${PX_SOURCE_DIR}/buffering/ScbArticulation.h + ${PX_SOURCE_DIR}/buffering/ScbArticulationJoint.h + ${PX_SOURCE_DIR}/buffering/ScbBase.h + ${PX_SOURCE_DIR}/buffering/ScbBody.h + ${PX_SOURCE_DIR}/buffering/ScbConstraint.h + ${PX_SOURCE_DIR}/buffering/ScbDefs.h + ${PX_SOURCE_DIR}/buffering/ScbNpDeps.h + ${PX_SOURCE_DIR}/buffering/ScbRigidObject.h + ${PX_SOURCE_DIR}/buffering/ScbRigidStatic.h + ${PX_SOURCE_DIR}/buffering/ScbScene.h + ${PX_SOURCE_DIR}/buffering/ScbSceneBuffer.h + ${PX_SOURCE_DIR}/buffering/ScbScenePvdClient.h + ${PX_SOURCE_DIR}/buffering/ScbShape.h + ${PX_SOURCE_DIR}/buffering/ScbType.h +) +SOURCE_GROUP(src\\buffering FILES ${PHYSX_BUFFERING_SOURCE}) + +SET(PHYSX_CORE_SOURCE + ${PX_SOURCE_DIR}/NpActor.cpp + ${PX_SOURCE_DIR}/NpAggregate.cpp + ${PX_SOURCE_DIR}/NpArticulationReducedCoordinate.cpp + ${PX_SOURCE_DIR}/NpArticulation.cpp + ${PX_SOURCE_DIR}/NpArticulationJoint.cpp + ${PX_SOURCE_DIR}/NpArticulationJointReducedCoordinate.cpp + ${PX_SOURCE_DIR}/NpArticulationLink.cpp + ${PX_SOURCE_DIR}/NpBatchQuery.cpp + ${PX_SOURCE_DIR}/NpConstraint.cpp + ${PX_SOURCE_DIR}/NpFactory.cpp + ${PX_SOURCE_DIR}/NpMaterial.cpp + ${PX_SOURCE_DIR}/NpMetaData.cpp + ${PX_SOURCE_DIR}/NpPhysics.cpp + ${PX_SOURCE_DIR}/NpPvdSceneQueryCollector.cpp + ${PX_SOURCE_DIR}/NpReadCheck.cpp + ${PX_SOURCE_DIR}/NpRigidDynamic.cpp + ${PX_SOURCE_DIR}/NpRigidStatic.cpp + ${PX_SOURCE_DIR}/NpScene.cpp + ${PX_SOURCE_DIR}/NpSceneQueries.cpp + ${PX_SOURCE_DIR}/NpSerializerAdapter.cpp + ${PX_SOURCE_DIR}/NpShape.cpp + ${PX_SOURCE_DIR}/NpShapeManager.cpp + ${PX_SOURCE_DIR}/NpWriteCheck.cpp + ${PX_SOURCE_DIR}/PvdMetaDataPvdBinding.cpp + ${PX_SOURCE_DIR}/PvdPhysicsClient.cpp + ${PX_SOURCE_DIR}/NpActor.h + ${PX_SOURCE_DIR}/NpActorTemplate.h + ${PX_SOURCE_DIR}/NpAggregate.h + ${PX_SOURCE_DIR}/NpArticulationReducedCoordinate.h + ${PX_SOURCE_DIR}/NpArticulation.h + ${PX_SOURCE_DIR}/NpArticulationJoint.h + ${PX_SOURCE_DIR}/NpArticulationJointReducedCoordinate.h + ${PX_SOURCE_DIR}/NpArticulationLink.h + ${PX_SOURCE_DIR}/NpArticulationTemplate.h + ${PX_SOURCE_DIR}/NpBatchQuery.h + ${PX_SOURCE_DIR}/NpCast.h + ${PX_SOURCE_DIR}/NpConnector.h + ${PX_SOURCE_DIR}/NpConstraint.h + ${PX_SOURCE_DIR}/NpFactory.h + ${PX_SOURCE_DIR}/NpMaterial.h + ${PX_SOURCE_DIR}/NpMaterialManager.h + ${PX_SOURCE_DIR}/NpPhysics.h + ${PX_SOURCE_DIR}/NpPhysicsInsertionCallback.h + ${PX_SOURCE_DIR}/NpPtrTableStorageManager.h + ${PX_SOURCE_DIR}/NpPvdSceneQueryCollector.h + ${PX_SOURCE_DIR}/NpQueryShared.h + ${PX_SOURCE_DIR}/NpReadCheck.h + ${PX_SOURCE_DIR}/NpRigidActorTemplate.h + ${PX_SOURCE_DIR}/NpRigidActorTemplateInternal.h + ${PX_SOURCE_DIR}/NpRigidBodyTemplate.h + ${PX_SOURCE_DIR}/NpRigidDynamic.h + ${PX_SOURCE_DIR}/NpRigidStatic.h + ${PX_SOURCE_DIR}/NpScene.h + ${PX_SOURCE_DIR}/NpSceneQueries.h + ${PX_SOURCE_DIR}/NpSceneAccessor.h + ${PX_SOURCE_DIR}/NpShape.h + ${PX_SOURCE_DIR}/NpShapeManager.h + ${PX_SOURCE_DIR}/NpWriteCheck.h + ${PX_SOURCE_DIR}/PvdMetaDataBindingData.h + ${PX_SOURCE_DIR}/PvdMetaDataPvdBinding.h + ${PX_SOURCE_DIR}/PvdPhysicsClient.h + ${PX_SOURCE_DIR}/PvdTypeNames.h +) +SOURCE_GROUP(src FILES ${PHYSX_CORE_SOURCE}) + +ADD_LIBRARY(PhysX ${PHYSX_LIBTYPE} + ${PHYSX_HEADERS} + ${PHYSX_COMMON_HEADERS} + ${PHYSX_PVD_HEADERS} + ${PHYSX_SOLVER_HEADERS} + + ${PHYSX_METADATA_HEADERS} + ${PHYSX_METADATA_SOURCE} + + ${PHYSX_CORE_SOURCE} + ${PHYSX_BUFFERING_SOURCE} + ${PHYSX_IMMEDIATEMODE_SOURCE} + + ${PHYSX_PLATFORM_SRC_FILES} +) + +# Add the headers to the install +INSTALL(FILES ${PHYSX_HEADERS} DESTINATION include) +INSTALL(FILES ${PHYSX_COMMON_HEADERS} DESTINATION include/common) +INSTALL(FILES ${PHYSX_PVD_HEADERS} DESTINATION include/pvd) +INSTALL(FILES ${PHYSX_COLLISION_HEADERS} DESTINATION include/collision) +INSTALL(FILES ${PHYSX_SOLVER_HEADERS} DESTINATION include/solver) + +TARGET_INCLUDE_DIRECTORIES(PhysX + PRIVATE ${PHYSX_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/gpu + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/pvd + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src/device + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src/buffering + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/software/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevelaabb/include + + PRIVATE ${PHYSX_SOURCE_DIR}/lowleveldynamics/include + + PRIVATE ${PHYSX_SOURCE_DIR}/simulationcontroller/include + PRIVATE ${PHYSX_SOURCE_DIR}/simulationcontroller/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src/convex + + PRIVATE ${PHYSX_SOURCE_DIR}/scenequery/include + + PRIVATE ${PHYSX_SOURCE_DIR}/physxmetadata/core/include + + PRIVATE ${PHYSX_SOURCE_DIR}/immediatemode/include + + PRIVATE ${PHYSX_SOURCE_DIR}/pvd/include +) + +TARGET_COMPILE_DEFINITIONS(PhysX + + # Common to all configurations + PRIVATE ${PHYSX_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysX PROPERTIES + OUTPUT_NAME PhysX + +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSX_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysX PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysX_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysX_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysX_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysX_static" + ) +ENDIF() + +IF(PHYSX_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysX PROPERTIES + COMPILE_PDB_NAME_DEBUG "${PHYSX_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${PHYSX_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${PHYSX_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${PHYSX_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +TARGET_LINK_LIBRARIES(PhysX + PRIVATE ${PHYSX_PRIVATE_PLATFORM_LINKED_LIBS} + PRIVATE PhysXPvdSDK + PUBLIC PhysXCommon + PUBLIC PhysXFoundation + PUBLIC ${PHYSX_PLATFORM_LINKED_LIBS} +) + +SET_TARGET_PROPERTIES(PhysX PROPERTIES + LINK_FLAGS "${PHYSX_PLATFORM_LINK_FLAGS}" + LINK_FLAGS_DEBUG "${PHYSX_PLATFORM_LINK_FLAGS_DEBUG}" + LINK_FLAGS_CHECKED "${PHYSX_PLATFORM_LINK_FLAGS_CHECKED}" + LINK_FLAGS_PROFILE "${PHYSX_PLATFORM_LINK_FLAGS_PROFILE}" + LINK_FLAGS_RELEASE "${PHYSX_PLATFORM_LINK_FLAGS_RELEASE}" +) + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COMMON_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_PVD_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_METADATA_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_METADATA_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_CORE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_BUFFERING_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_IMMEDIATEMODE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_PLATFORM_SRC_FILES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_SOLVER_HEADERS}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysX PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..cd9ee9dd6 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake @@ -0,0 +1,152 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physxcharacterkinematic/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXCharacterKinematic.cmake) + + +SET(PHYSXCCT_HEADERS + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxBoxController.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxCapsuleController.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxCharacter.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxController.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxControllerBehavior.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxControllerManager.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxControllerObstacles.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxExtended.h +) +SOURCE_GROUP(include FILES ${PHYSXCCT_HEADERS}) + +SET(PHYSXCCT_SOURCE + ${LL_SOURCE_DIR}/CctBoxController.cpp + ${LL_SOURCE_DIR}/CctCapsuleController.cpp + ${LL_SOURCE_DIR}/CctCharacterController.cpp + ${LL_SOURCE_DIR}/CctCharacterControllerCallbacks.cpp + ${LL_SOURCE_DIR}/CctCharacterControllerManager.cpp + ${LL_SOURCE_DIR}/CctController.cpp + ${LL_SOURCE_DIR}/CctObstacleContext.cpp + ${LL_SOURCE_DIR}/CctSweptBox.cpp + ${LL_SOURCE_DIR}/CctSweptCapsule.cpp + ${LL_SOURCE_DIR}/CctSweptVolume.cpp + ${LL_SOURCE_DIR}/CctBoxController.h + ${LL_SOURCE_DIR}/CctCapsuleController.h + ${LL_SOURCE_DIR}/CctCharacterController.h + ${LL_SOURCE_DIR}/CctCharacterControllerManager.h + ${LL_SOURCE_DIR}/CctController.h + ${LL_SOURCE_DIR}/CctInternalStructs.h + ${LL_SOURCE_DIR}/CctObstacleContext.h + ${LL_SOURCE_DIR}/CctSweptBox.h + ${LL_SOURCE_DIR}/CctSweptCapsule.h + ${LL_SOURCE_DIR}/CctSweptVolume.h + ${LL_SOURCE_DIR}/CctUtils.h +) +SOURCE_GROUP(src FILES ${PHYSXCCT_SOURCE}) + +ADD_LIBRARY(PhysXCharacterKinematic ${PHYSXCHARACTERKINEMATIC_LIBTYPE} + ${PHYSXCCT_HEADERS} + ${PHYSXCCT_SOURCE} +) + +INSTALL(FILES ${PHYSXCCT_HEADERS} DESTINATION include/characterkinematic) + +TARGET_INCLUDE_DIRECTORIES(PhysXCharacterKinematic + + PRIVATE ${PHYSXCHARACTERKINEMATICS_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/characterkinematic + PRIVATE ${PHYSX_ROOT_DIR}/include/extensions + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm +) + +TARGET_COMPILE_DEFINITIONS(PhysXCharacterKinematic + + # Common to all configurations + PRIVATE ${PHYSXCHARACTERKINEMATICS_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXCharacterKinematic PROPERTIES + OUTPUT_NAME PhysXCharacterKinematic +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCHARACTERKINEMATIC_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXCharacterKinematic PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXCharacterKinematic_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXCharacterKinematic_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXCharacterKinematic_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXCharacterKinematic_static" + ) +ENDIF() + +IF(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXCharacterKinematic PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +TARGET_LINK_LIBRARIES(PhysXCharacterKinematic + PUBLIC ${PHYSXCHARACTERKINEMATICS_PLATFORM_LINKED_LIBS} + PUBLIC PhysXFoundation +) + + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCCT_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCCT_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXCharacterKinematic PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake new file mode 100644 index 000000000..5bcbf8222 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake @@ -0,0 +1,591 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(COMMON_SRC_DIR ${PHYSX_SOURCE_DIR}/common/src) +SET(GU_SOURCE_DIR ${PHYSX_SOURCE_DIR}/geomutils) + +SET(PXCOMMON_PLATFORM_LINK_FLAGS_DEBUG " ") +SET(PXCOMMON_PLATFORM_LINK_FLAGS_CHECKED " ") +SET(PXCOMMON_PLATFORM_LINK_FLAGS_PROFILE " ") +SET(PXCOMMON_PLATFORM_LINK_FLAGS_RELEASE " ") + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXCommon.cmake) + + +SET(PHYSX_COMMON_SOURCE + ${COMMON_SRC_DIR}/CmBoxPruning.cpp + ${COMMON_SRC_DIR}/CmCollection.cpp + ${COMMON_SRC_DIR}/CmMathUtils.cpp + ${COMMON_SRC_DIR}/CmPtrTable.cpp + ${COMMON_SRC_DIR}/CmRadixSort.cpp + ${COMMON_SRC_DIR}/CmRadixSortBuffered.cpp + ${COMMON_SRC_DIR}/CmRenderOutput.cpp + ${COMMON_SRC_DIR}/CmVisualization.cpp + ${COMMON_SRC_DIR}/CmBitMap.h + ${COMMON_SRC_DIR}/CmBlockArray.h + ${COMMON_SRC_DIR}/CmBoxPruning.h + ${COMMON_SRC_DIR}/CmCollection.h + ${COMMON_SRC_DIR}/CmConeLimitHelper.h + ${COMMON_SRC_DIR}/CmFlushPool.h + ${COMMON_SRC_DIR}/CmIDPool.h + ${COMMON_SRC_DIR}/CmIO.h + ${COMMON_SRC_DIR}/CmMatrix34.h + ${COMMON_SRC_DIR}/CmPhysXCommon.h + ${COMMON_SRC_DIR}/CmPool.h + ${COMMON_SRC_DIR}/CmPreallocatingPool.h + ${COMMON_SRC_DIR}/CmPriorityQueue.h + ${COMMON_SRC_DIR}/CmPtrTable.h + ${COMMON_SRC_DIR}/CmQueue.h + ${COMMON_SRC_DIR}/CmRadixSort.h + ${COMMON_SRC_DIR}/CmRadixSortBuffered.h + ${COMMON_SRC_DIR}/CmRefCountable.h + ${COMMON_SRC_DIR}/CmRenderBuffer.h + ${COMMON_SRC_DIR}/CmRenderOutput.h + ${COMMON_SRC_DIR}/CmScaling.h + ${COMMON_SRC_DIR}/CmSpatialVector.h + ${COMMON_SRC_DIR}/CmTask.h + ${COMMON_SRC_DIR}/CmTaskPool.h + ${COMMON_SRC_DIR}/CmTmpMem.h + ${COMMON_SRC_DIR}/CmTransformUtils.h + ${COMMON_SRC_DIR}/CmUtils.h + ${COMMON_SRC_DIR}/CmVisualization.h +) +SOURCE_GROUP(common\\src FILES ${PHYSX_COMMON_SOURCE}) + +SET(PHYSXCOMMON_COMMON_HEADERS + ${PHYSX_ROOT_DIR}/include/common/PxBase.h + ${PHYSX_ROOT_DIR}/include/common/PxCollection.h + ${PHYSX_ROOT_DIR}/include/common/PxCoreUtilityTypes.h + ${PHYSX_ROOT_DIR}/include/common/PxMetaData.h + ${PHYSX_ROOT_DIR}/include/common/PxMetaDataFlags.h + ${PHYSX_ROOT_DIR}/include/common/PxPhysicsInsertionCallback.h + ${PHYSX_ROOT_DIR}/include/common/PxPhysXCommonConfig.h + ${PHYSX_ROOT_DIR}/include/common/PxRenderBuffer.h + ${PHYSX_ROOT_DIR}/include/common/PxSerialFramework.h + ${PHYSX_ROOT_DIR}/include/common/PxSerializer.h + ${PHYSX_ROOT_DIR}/include/common/PxStringTable.h + ${PHYSX_ROOT_DIR}/include/common/PxTolerancesScale.h + ${PHYSX_ROOT_DIR}/include/common/PxTypeInfo.h + ${PHYSX_ROOT_DIR}/include/common/PxProfileZone.h +) +SOURCE_GROUP(include\\common FILES ${PHYSXCOMMON_COMMON_HEADERS}) + +SET(PHYSXCOMMON_GEOMETRY_HEADERS + ${PHYSX_ROOT_DIR}/include/geometry/PxBoxGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxCapsuleGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxConvexMesh.h + ${PHYSX_ROOT_DIR}/include/geometry/PxConvexMeshGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxGeometryHelpers.h + ${PHYSX_ROOT_DIR}/include/geometry/PxGeometryQuery.h + ${PHYSX_ROOT_DIR}/include/geometry/PxHeightField.h + ${PHYSX_ROOT_DIR}/include/geometry/PxHeightFieldDesc.h + ${PHYSX_ROOT_DIR}/include/geometry/PxHeightFieldFlag.h + ${PHYSX_ROOT_DIR}/include/geometry/PxHeightFieldGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxHeightFieldSample.h + ${PHYSX_ROOT_DIR}/include/geometry/PxMeshQuery.h + ${PHYSX_ROOT_DIR}/include/geometry/PxMeshScale.h + ${PHYSX_ROOT_DIR}/include/geometry/PxPlaneGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxSimpleTriangleMesh.h + ${PHYSX_ROOT_DIR}/include/geometry/PxSphereGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxTriangle.h + ${PHYSX_ROOT_DIR}/include/geometry/PxTriangleMesh.h + ${PHYSX_ROOT_DIR}/include/geometry/PxTriangleMeshGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxBVHStructure.h +) +SOURCE_GROUP(include\\geometry FILES ${PHYSXCOMMON_GEOMETRY_HEADERS}) + +SET(PHYSXCOMMON_GEOMUTILS_HEADERS + ${PHYSX_ROOT_DIR}/include/geomutils/GuContactBuffer.h + ${PHYSX_ROOT_DIR}/include/geomutils/GuContactPoint.h +) +SOURCE_GROUP(include\\geomutils FILES ${PHYSXCOMMON_GEOMUTILS_HEADERS}) + +SET(PHYSXCOMMON_COLLISION_HEADERS + ${PHYSX_ROOT_DIR}/include/collision/PxCollisionDefs.h +) +SOURCE_GROUP(include\\collision FILES ${PHYSXCOMMON_COLLISION_HEADERS}) + +SET(PHYSXCOMMON_GU_HEADERS + ${GU_SOURCE_DIR}/include/GuAxes.h + ${GU_SOURCE_DIR}/include/GuBox.h + ${GU_SOURCE_DIR}/include/GuDistanceSegmentBox.h + ${GU_SOURCE_DIR}/include/GuDistanceSegmentSegment.h + ${GU_SOURCE_DIR}/include/GuIntersectionBoxBox.h + ${GU_SOURCE_DIR}/include/GuIntersectionTriangleBox.h + ${GU_SOURCE_DIR}/include/GuIntersectionTriangleBoxRef.h + ${GU_SOURCE_DIR}/include/GuRaycastTests.h + ${GU_SOURCE_DIR}/include/GuSegment.h + ${GU_SOURCE_DIR}/include/GuSIMDHelpers.h +) +SOURCE_GROUP(geomutils\\headers FILES ${PHYSXCOMMON_GU_HEADERS}) + +SET(PHYSXCOMMON_GU_PXHEADERS + ${PHYSX_ROOT_DIR}/include/geomutils/GuContactBuffer.h + ${PHYSX_ROOT_DIR}/include/geomutils/GuContactPoint.h +) +SOURCE_GROUP(geomutils\\include FILES ${PHYSXCOMMON_GU_PXHEADERS}) + +SET(PHYSXCOMMON_GU_SOURCE + ${GU_SOURCE_DIR}/src/GuBounds.cpp + ${GU_SOURCE_DIR}/src/GuBox.cpp + ${GU_SOURCE_DIR}/src/GuCapsule.cpp + ${GU_SOURCE_DIR}/src/GuCCTSweepTests.cpp + ${GU_SOURCE_DIR}/src/GuGeometryQuery.cpp + ${GU_SOURCE_DIR}/src/GuGeometryUnion.cpp + ${GU_SOURCE_DIR}/src/GuInternal.cpp + ${GU_SOURCE_DIR}/src/GuMeshFactory.cpp + ${GU_SOURCE_DIR}/src/GuMetaData.cpp + ${GU_SOURCE_DIR}/src/GuMTD.cpp + ${GU_SOURCE_DIR}/src/GuOverlapTests.cpp + ${GU_SOURCE_DIR}/src/GuRaycastTests.cpp + ${GU_SOURCE_DIR}/src/GuSerialize.cpp + ${GU_SOURCE_DIR}/src/GuSweepMTD.cpp + ${GU_SOURCE_DIR}/src/GuSweepSharedTests.cpp + ${GU_SOURCE_DIR}/src/GuSweepTests.cpp + ${GU_SOURCE_DIR}/src/GuBounds.h + ${GU_SOURCE_DIR}/src/GuCapsule.h + ${GU_SOURCE_DIR}/src/GuCenterExtents.h + ${GU_SOURCE_DIR}/src/GuGeometryUnion.h + ${GU_SOURCE_DIR}/src/GuInternal.h + ${GU_SOURCE_DIR}/src/GuMeshFactory.h + ${GU_SOURCE_DIR}/src/GuMTD.h + ${GU_SOURCE_DIR}/src/GuOverlapTests.h + ${GU_SOURCE_DIR}/src/GuSerialize.h + ${GU_SOURCE_DIR}/src/GuSphere.h + ${GU_SOURCE_DIR}/src/GuSweepMTD.h + ${GU_SOURCE_DIR}/src/GuSweepSharedTests.h + ${GU_SOURCE_DIR}/src/GuSweepTests.h + ${GU_SOURCE_DIR}/src/GuAABBTreeBuild.cpp + ${GU_SOURCE_DIR}/src/GuAABBTreeBuild.h + ${GU_SOURCE_DIR}/src/GuAABBTreeQuery.h + ${GU_SOURCE_DIR}/src/GuBVHStructure.cpp + ${GU_SOURCE_DIR}/src/GuBVHStructure.h + ${GU_SOURCE_DIR}/src/GuBVHTestsSIMD.h +) +SOURCE_GROUP(geomutils\\src FILES ${PHYSXCOMMON_GU_SOURCE}) + +SET(PHYSXCOMMON_GU_CCD_SOURCE + ${GU_SOURCE_DIR}/src/ccd/GuCCDSweepConvexMesh.cpp + ${GU_SOURCE_DIR}/src/ccd/GuCCDSweepPrimitives.cpp + ${GU_SOURCE_DIR}/src/ccd/GuCCDSweepConvexMesh.h +) +SOURCE_GROUP(geomutils\\src\\ccd FILES ${PHYSXCOMMON_GU_CCD_SOURCE}) + +SET(PHYSXCOMMON_GU_COMMON_SOURCE + ${GU_SOURCE_DIR}/src/common/GuBarycentricCoordinates.cpp + ${GU_SOURCE_DIR}/src/common/GuSeparatingAxes.cpp + ${GU_SOURCE_DIR}/src/common/GuBarycentricCoordinates.h + ${GU_SOURCE_DIR}/src/common/GuBoxConversion.h + ${GU_SOURCE_DIR}/src/common/GuEdgeCache.h + ${GU_SOURCE_DIR}/src/common/GuEdgeListData.h + ${GU_SOURCE_DIR}/src/common/GuSeparatingAxes.h +) +SOURCE_GROUP(geomutils\\src\\common FILES ${PHYSXCOMMON_GU_COMMON_SOURCE}) + +SET(PHYSXCOMMON_GU_CONTACT_SOURCE + ${GU_SOURCE_DIR}/src/contact/GuContactBoxBox.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactCapsuleBox.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactCapsuleCapsule.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactCapsuleConvex.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactCapsuleMesh.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactConvexConvex.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactConvexMesh.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactPlaneBox.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactPlaneCapsule.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactPlaneConvex.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactPolygonPolygon.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactSphereBox.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactSphereCapsule.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactSphereMesh.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactSpherePlane.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactSphereSphere.cpp + ${GU_SOURCE_DIR}/src/contact/GuFeatureCode.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactMethodImpl.h + ${GU_SOURCE_DIR}/src/contact/GuContactPolygonPolygon.h + ${GU_SOURCE_DIR}/src/contact/GuFeatureCode.h +) +SOURCE_GROUP(geomutils\\src\\contact FILES ${PHYSXCOMMON_GU_CONTACT_SOURCE}) + +SET(PHYSXCOMMON_GU_CONVEX_SOURCE + ${GU_SOURCE_DIR}/src/convex/GuBigConvexData.cpp + ${GU_SOURCE_DIR}/src/convex/GuConvexHelper.cpp + ${GU_SOURCE_DIR}/src/convex/GuConvexMesh.cpp + ${GU_SOURCE_DIR}/src/convex/GuConvexSupportTable.cpp + ${GU_SOURCE_DIR}/src/convex/GuConvexUtilsInternal.cpp + ${GU_SOURCE_DIR}/src/convex/GuHillClimbing.cpp + ${GU_SOURCE_DIR}/src/convex/GuShapeConvex.cpp + ${GU_SOURCE_DIR}/src/convex/GuBigConvexData.h + ${GU_SOURCE_DIR}/src/convex/GuBigConvexData2.h + ${GU_SOURCE_DIR}/src/convex/GuConvexEdgeFlags.h + ${GU_SOURCE_DIR}/src/convex/GuConvexHelper.h + ${GU_SOURCE_DIR}/src/convex/GuConvexMesh.h + ${GU_SOURCE_DIR}/src/convex/GuConvexMeshData.h + ${GU_SOURCE_DIR}/src/convex/GuConvexSupportTable.h + ${GU_SOURCE_DIR}/src/convex/GuConvexUtilsInternal.h + ${GU_SOURCE_DIR}/src/convex/GuCubeIndex.h + ${GU_SOURCE_DIR}/src/convex/GuHillClimbing.h + ${GU_SOURCE_DIR}/src/convex/GuShapeConvex.h +) +SOURCE_GROUP(geomutils\\src\\convex FILES ${PHYSXCOMMON_GU_CONVEX_SOURCE}) + +SET(PHYSXCOMMON_GU_DISTANCE_SOURCE + ${GU_SOURCE_DIR}/src/distance/GuDistancePointBox.cpp + ${GU_SOURCE_DIR}/src/distance/GuDistancePointTriangle.cpp + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentBox.cpp + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentSegment.cpp + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentTriangle.cpp + ${GU_SOURCE_DIR}/src/distance/GuDistancePointBox.h + ${GU_SOURCE_DIR}/src/distance/GuDistancePointSegment.h + ${GU_SOURCE_DIR}/src/distance/GuDistancePointTriangle.h + ${GU_SOURCE_DIR}/src/distance/GuDistancePointTriangleSIMD.h + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentSegmentSIMD.h + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentTriangle.h + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentTriangleSIMD.h +) +SOURCE_GROUP(geomutils\\src\\distance FILES ${PHYSXCOMMON_GU_DISTANCE_SOURCE}) + +SET(PHYSXCOMMON_GU_GJK_SOURCE + ${GU_SOURCE_DIR}/src/gjk/GuEPA.cpp + ${GU_SOURCE_DIR}/src/gjk/GuGJKSimplex.cpp + ${GU_SOURCE_DIR}/src/gjk/GuGJKTest.cpp + ${GU_SOURCE_DIR}/src/gjk/GuEPA.h + ${GU_SOURCE_DIR}/src/gjk/GuEPAFacet.h + ${GU_SOURCE_DIR}/src/gjk/GuGJK.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKPenetration.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKRaycast.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKSimplex.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKTest.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKType.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKUtil.h + ${GU_SOURCE_DIR}/src/gjk/GuVecBox.h + ${GU_SOURCE_DIR}/src/gjk/GuVecCapsule.h + ${GU_SOURCE_DIR}/src/gjk/GuVecConvex.h + ${GU_SOURCE_DIR}/src/gjk/GuVecConvexHull.h + ${GU_SOURCE_DIR}/src/gjk/GuVecConvexHullNoScale.h + ${GU_SOURCE_DIR}/src/gjk/GuVecPlane.h + ${GU_SOURCE_DIR}/src/gjk/GuVecSphere.h + ${GU_SOURCE_DIR}/src/gjk/GuVecTriangle.h +) +SOURCE_GROUP(geomutils\\src\\gjk FILES ${PHYSXCOMMON_GU_GJK_SOURCE}) + +SET(PHYSXCOMMON_GU_HF_SOURCE + ${GU_SOURCE_DIR}/src/hf/GuHeightField.cpp + ${GU_SOURCE_DIR}/src/hf/GuHeightFieldUtil.cpp + ${GU_SOURCE_DIR}/src/hf/GuOverlapTestsHF.cpp + ${GU_SOURCE_DIR}/src/hf/GuSweepsHF.cpp + ${GU_SOURCE_DIR}/src/hf/GuEntityReport.h + ${GU_SOURCE_DIR}/src/hf/GuHeightField.h + ${GU_SOURCE_DIR}/src/hf/GuHeightFieldData.h + ${GU_SOURCE_DIR}/src/hf/GuHeightFieldUtil.h +) +SOURCE_GROUP(geomutils\\src\\hf FILES ${PHYSXCOMMON_GU_HF_SOURCE}) + +SET(PHYSXCOMMON_GU_INTERSECTION_SOURCE + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionBoxBox.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionCapsuleTriangle.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionEdgeEdge.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayBox.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayCapsule.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRaySphere.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionSphereBox.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionTriangleBox.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionCapsuleTriangle.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionEdgeEdge.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRay.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayBox.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayBoxSIMD.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayCapsule.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayPlane.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRaySphere.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayTriangle.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionSphereBox.h +) +SOURCE_GROUP(geomutils\\src\\intersection FILES ${PHYSXCOMMON_GU_INTERSECTION_SOURCE}) + +SET(PXCOMMON_BVH4_FILES + ${GU_SOURCE_DIR}/src/mesh/GuBV4_AABBSweep.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_BoxOverlap.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_CapsuleSweep.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_CapsuleSweepAA.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_OBBSweep.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Raycast.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_SphereOverlap.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_SphereSweep.cpp + ${GU_SOURCE_DIR}/src/mesh/GuMidphaseBV4.cpp +) + +SET(PHYSXCOMMON_GU_MESH_SOURCE + ${GU_SOURCE_DIR}/src/mesh/GuBV4.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4Build.cpp + + ${PXCOMMON_BVH4_FILES} + + ${GU_SOURCE_DIR}/src/mesh/GuMeshQuery.cpp + ${GU_SOURCE_DIR}/src/mesh/GuMidphaseRTree.cpp + ${GU_SOURCE_DIR}/src/mesh/GuOverlapTestsMesh.cpp + ${GU_SOURCE_DIR}/src/mesh/GuRTree.cpp + ${GU_SOURCE_DIR}/src/mesh/GuRTreeQueries.cpp + ${GU_SOURCE_DIR}/src/mesh/GuSweepsMesh.cpp + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMesh.cpp + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMeshBV4.cpp + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMeshRTree.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV32.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV32Build.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV32.h + ${GU_SOURCE_DIR}/src/mesh/GuBV32Build.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4Build.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4Settings.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_AABBAABBSweepTest.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_BoxBoxOverlapTest.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_BoxOverlap_Internal.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_BoxSweep_Internal.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_BoxSweep_Params.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_CapsuleSweep_Internal.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Common.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Internal.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Slabs.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Slabs_KajiyaOrdered.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Slabs_SwizzledOrdered.h + ${GU_SOURCE_DIR}/src/mesh/GuBVConstants.h + ${GU_SOURCE_DIR}/src/mesh/GuMeshData.h + ${GU_SOURCE_DIR}/src/mesh/GuMidphaseInterface.h + ${GU_SOURCE_DIR}/src/mesh/GuRTree.h + ${GU_SOURCE_DIR}/src/mesh/GuSweepConvexTri.h + ${GU_SOURCE_DIR}/src/mesh/GuSweepMesh.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangle32.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangleCache.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMesh.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMeshBV4.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMeshRTree.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangleVertexPointers.h +) +SOURCE_GROUP(geomutils\\src\\mesh FILES ${PHYSXCOMMON_GU_MESH_SOURCE}) + +SET(PHYSXCOMMON_GU_PCM_SOURCE + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactBoxBox.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactBoxConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactCapsuleBox.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactCapsuleCapsule.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactCapsuleConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactCapsuleHeightField.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactCapsuleMesh.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactConvexCommon.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactConvexConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactConvexHeightField.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactConvexMesh.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactGenBoxConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactGenSphereCapsule.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactPlaneBox.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactPlaneCapsule.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactPlaneConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereBox.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereCapsule.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereHeightField.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereMesh.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSpherePlane.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereSphere.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMShapeConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMTriangleContactGen.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPersistentContactManifold.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactConvexCommon.h + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactGen.h + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactGenUtil.h + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactMeshCallback.h + ${GU_SOURCE_DIR}/src/pcm/GuPCMShapeConvex.h + ${GU_SOURCE_DIR}/src/pcm/GuPCMTriangleContactGen.h + ${GU_SOURCE_DIR}/src/pcm/GuPersistentContactManifold.h +) +SOURCE_GROUP(geomutils\\src\\pcm FILES ${PHYSXCOMMON_GU_PCM_SOURCE}) + +SET(PHYSXCOMMON_GU_SWEEP_SOURCE + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxBox.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxSphere.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxTriangle_SAT.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleBox.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleCapsule.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleTriangle.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereCapsule.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereSphere.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereTriangle.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepTriangleUtils.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxBox.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxSphere.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxTriangle_FeatureBased.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxTriangle_SAT.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleBox.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleCapsule.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleTriangle.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereCapsule.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereSphere.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereTriangle.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepTriangleUtils.h +) +SOURCE_GROUP(geomutils\\src\\sweep FILES ${PHYSXCOMMON_GU_SWEEP_SOURCE}) + +ADD_LIBRARY(PhysXCommon ${PHYSXCOMMON_LIBTYPE} + ${PHYSX_COMMON_SOURCE} + + ${PHYSXCOMMON_COMMON_HEADERS} + ${PHYSXCOMMON_GEOMETRY_HEADERS} + ${PHYSXCOMMON_GEOMUTILS_HEADERS} + ${PHYSXCOMMON_COLLISION_HEADERS} + + ${PXCOMMON_PLATFORM_SRC_FILES} + + ${PHYSXCOMMON_GU_HEADERS} + ${PHYSXCOMMON_GU_PXHEADERS} + + ${PHYSXCOMMON_GU_SOURCE} + ${PHYSXCOMMON_GU_CCD_SOURCE} + ${PHYSXCOMMON_GU_COMMON_SOURCE} + ${PHYSXCOMMON_GU_CONTACT_SOURCE} + ${PHYSXCOMMON_GU_CONVEX_SOURCE} + ${PHYSXCOMMON_GU_DISTANCE_SOURCE} + ${PHYSXCOMMON_GU_GJK_SOURCE} + ${PHYSXCOMMON_GU_HF_SOURCE} + ${PHYSXCOMMON_GU_INTERSECTION_SOURCE} + ${PHYSXCOMMON_GU_MESH_SOURCE} + ${PHYSXCOMMON_GU_PCM_SOURCE} + ${PHYSXCOMMON_GU_SWEEP_SOURCE} +) + +INSTALL(FILES ${PHYSXCOMMON_GEOMETRY_HEADERS} DESTINATION include/geometry) +INSTALL(FILES ${PHYSXCOMMON_GEOMUTILS_HEADERS} DESTINATION include/geomutils) + +TARGET_INCLUDE_DIRECTORIES(PhysXCommon + PRIVATE ${PXCOMMON_PLATFORM_INCLUDES} + + PUBLIC ${PHYSX_ROOT_DIR}/include + PUBLIC ${PHYSX_ROOT_DIR}/include/common + PUBLIC ${PHYSX_ROOT_DIR}/include/geometry + PUBLIC ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include +) + +TARGET_COMPILE_DEFINITIONS(PhysXCommon + PRIVATE ${PXCOMMON_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXCommon PROPERTIES + OUTPUT_NAME PhysXCommon +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCOMMON_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXCommon PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXCommon_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXCommon_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXCommon_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXCommon_static" + ) +ENDIF() + +IF(PHYSXCOMMON_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXCommon PROPERTIES + COMPILE_PDB_NAME_DEBUG "${PHYSXCOMMON_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${PHYSXCOMMON_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${PHYSXCOMMON_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${PHYSXCOMMON_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +SET_TARGET_PROPERTIES(PhysXCommon PROPERTIES + LINK_FLAGS "${PXCOMMON_PLATFORM_LINK_FLAGS}" + LINK_FLAGS_DEBUG "${PXCOMMON_PLATFORM_LINK_FLAGS_DEBUG}" + LINK_FLAGS_CHECKED "${PXCOMMON_PLATFORM_LINK_FLAGS_CHECKED}" + LINK_FLAGS_PROFILE "${PXCOMMON_PLATFORM_LINK_FLAGS_PROFILE}" + LINK_FLAGS_RELEASE "${PXCOMMON_PLATFORM_LINK_FLAGS_RELEASE}" +) + +TARGET_LINK_LIBRARIES(PhysXCommon + PUBLIC ${PXCOMMON_PLATFORM_LINKED_LIBS} + PUBLIC PhysXFoundation +) + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COMMON_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_COMMON_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GEOMETRY_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GEOMUTILS_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PXCOMMON_PLATFORM_SRC_FILES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_PXHEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_CCD_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_COMMON_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_CONTACT_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_CONVEX_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_DISTANCE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_GJK_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_HF_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_INTERSECTION_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_MESH_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_PCM_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_SWEEP_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_COLLISION_HEADERS}) +ENDIF() + + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXCommon PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake new file mode 100644 index 000000000..4a7719cc9 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake @@ -0,0 +1,206 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking common +# +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physxcooking/src) + +SET(PHYSXCOOKING_LINK_FLAGS_DEBUG " ") +SET(PHYSXCOOKING_LINK_FLAGS_CHECKED " ") +SET(PHYSXCOOKING_LINK_FLAGS_PROFILE " ") +SET(PHYSXCOOKING_LINK_FLAGS_RELEASE " ") + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXCooking.cmake) + + +SET(PHYSX_COOKING_HEADERS + ${PHYSX_ROOT_DIR}/include/cooking/PxBVH33MidphaseDesc.h + ${PHYSX_ROOT_DIR}/include/cooking/PxBVH34MidphaseDesc.h + ${PHYSX_ROOT_DIR}/include/cooking/Pxc.h + ${PHYSX_ROOT_DIR}/include/cooking/PxConvexMeshDesc.h + ${PHYSX_ROOT_DIR}/include/cooking/PxCooking.h + ${PHYSX_ROOT_DIR}/include/cooking/PxMidphaseDesc.h + ${PHYSX_ROOT_DIR}/include/cooking/PxTriangleMeshDesc.h + ${PHYSX_ROOT_DIR}/include/cooking/PxBVHStructureDesc.h +) +SOURCE_GROUP(include FILES ${PHYSX_COOKING_HEADERS}) + +SET(PHYSX_COOKING_SOURCE + ${LL_SOURCE_DIR}/Adjacencies.cpp + ${LL_SOURCE_DIR}/Cooking.cpp + ${LL_SOURCE_DIR}/CookingUtils.cpp + ${LL_SOURCE_DIR}/EdgeList.cpp + ${LL_SOURCE_DIR}/MeshCleaner.cpp + ${LL_SOURCE_DIR}/Quantizer.cpp + ${LL_SOURCE_DIR}/Adjacencies.h + ${LL_SOURCE_DIR}/Cooking.h + ${LL_SOURCE_DIR}/CookingUtils.h + ${LL_SOURCE_DIR}/EdgeList.h + ${LL_SOURCE_DIR}/MeshCleaner.h + ${LL_SOURCE_DIR}/Quantizer.h + ${LL_SOURCE_DIR}/BVHStructureBuilder.cpp + ${LL_SOURCE_DIR}/BVHStructureBuilder.h +) +SOURCE_GROUP(src FILES ${PHYSX_COOKING_SOURCE}) + +SET(PHYSX_COOKING_MESH_SOURCE + ${LL_SOURCE_DIR}/mesh/HeightFieldCooking.cpp + ${LL_SOURCE_DIR}/mesh/RTreeCooking.cpp + ${LL_SOURCE_DIR}/mesh/MeshBuilder.cpp + ${LL_SOURCE_DIR}/mesh/TriangleMeshBuilder.cpp + ${LL_SOURCE_DIR}/mesh/GrbTriangleMeshCooking.cpp + ${LL_SOURCE_DIR}/mesh/GrbTriangleMeshCooking.h + ${LL_SOURCE_DIR}/mesh/HeightFieldCooking.h + ${LL_SOURCE_DIR}/mesh/QuickSelect.h + ${LL_SOURCE_DIR}/mesh/RTreeCooking.h + ${LL_SOURCE_DIR}/mesh/MeshBuilder.h + ${LL_SOURCE_DIR}/mesh/TriangleMeshBuilder.h +) +SOURCE_GROUP(src\\mesh FILES ${PHYSX_COOKING_MESH_SOURCE}) + + +SET(PHYSX_COOKING_CONVEX_SOURCE + ${LL_SOURCE_DIR}/convex/BigConvexDataBuilder.cpp + ${LL_SOURCE_DIR}/convex/ConvexHullBuilder.cpp + ${LL_SOURCE_DIR}/convex/ConvexHullLib.cpp + ${LL_SOURCE_DIR}/convex/ConvexHullUtils.cpp + ${LL_SOURCE_DIR}/convex/ConvexMeshBuilder.cpp + ${LL_SOURCE_DIR}/convex/ConvexPolygonsBuilder.cpp + ${LL_SOURCE_DIR}/convex/QuickHullConvexHullLib.cpp + ${LL_SOURCE_DIR}/convex/VolumeIntegration.cpp + ${LL_SOURCE_DIR}/convex/BigConvexDataBuilder.h + ${LL_SOURCE_DIR}/convex/ConvexHullBuilder.h + ${LL_SOURCE_DIR}/convex/ConvexHullLib.h + ${LL_SOURCE_DIR}/convex/ConvexHullUtils.h + ${LL_SOURCE_DIR}/convex/ConvexMeshBuilder.h + ${LL_SOURCE_DIR}/convex/ConvexPolygonsBuilder.h + ${LL_SOURCE_DIR}/convex/QuickHullConvexHullLib.h + ${LL_SOURCE_DIR}/convex/VolumeIntegration.h +) +SOURCE_GROUP(src\\convex FILES ${PHYSX_COOKING_CONVEX_SOURCE}) + +ADD_LIBRARY(PhysXCooking ${PHYSXCOOKING_LIBTYPE} + ${PHYSX_COOKING_HEADERS} + + ${PHYSX_COOKING_SOURCE} + ${PHYSX_COOKING_MESH_SOURCE} + ${PHYSX_COOKING_CONVEX_SOURCE} + + ${PHYSXCOOKING_PLATFORM_SRC_FILES} +) + +INSTALL(FILES ${PHYSX_COOKING_HEADERS} DESTINATION include/cooking) + +# Target specific compile options + +TARGET_INCLUDE_DIRECTORIES(PhysXCooking + PRIVATE ${PHYSXCOOKING_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/cooking + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/include + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/common + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src/convex + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include + +) + +TARGET_LINK_LIBRARIES(PhysXCooking PUBLIC PhysXCommon PhysXFoundation) + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(PhysXCooking + PRIVATE ${PHYSXCOOKING_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXCooking PROPERTIES + OUTPUT_NAME PhysXCooking +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCOOKING_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXCooking PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXCooking_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXCooking_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXCooking_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXCooking_static" + ) +ENDIF() + +IF(PHYSXCOOKING_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXCooking PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXCOOKING_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXCOOKING_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXCOOKING_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXCOOKING_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +SET_TARGET_PROPERTIES(PhysXCooking PROPERTIES + LINK_FLAGS ${PHYSXCOOKING_LINK_FLAGS} + LINK_FLAGS_DEBUG ${PHYSXCOOKING_LINK_FLAGS_DEBUG} + LINK_FLAGS_CHECKED ${PHYSXCOOKING_LINK_FLAGS_CHECKED} + LINK_FLAGS_PROFILE ${PHYSXCOOKING_LINK_FLAGS_PROFILE} + LINK_FLAGS_RELEASE ${PHYSXCOOKING_LINK_FLAGS_RELEASE} +) + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COOKING_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COOKING_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COOKING_MESH_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COOKING_CONVEX_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOOKING_PLATFORM_SRC_FILES}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXCooking PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + + diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake new file mode 100644 index 000000000..aeb868943 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake @@ -0,0 +1,320 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physxextensions/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXExtensions.cmake) + + + +SET(PHYSX_EXTENSIONS_SOURCE + ${LL_SOURCE_DIR}/ExtBroadPhase.cpp + ${LL_SOURCE_DIR}/ExtCollection.cpp + ${LL_SOURCE_DIR}/ExtConvexMeshExt.cpp + ${LL_SOURCE_DIR}/ExtCpuWorkerThread.cpp + ${LL_SOURCE_DIR}/ExtD6Joint.cpp + ${LL_SOURCE_DIR}/ExtD6JointCreate.cpp + ${LL_SOURCE_DIR}/ExtDefaultCpuDispatcher.cpp + ${LL_SOURCE_DIR}/ExtDefaultErrorCallback.cpp + ${LL_SOURCE_DIR}/ExtDefaultSimulationFilterShader.cpp + ${LL_SOURCE_DIR}/ExtDefaultStreams.cpp + ${LL_SOURCE_DIR}/ExtDistanceJoint.cpp + ${LL_SOURCE_DIR}/ExtContactJoint.cpp + ${LL_SOURCE_DIR}/ExtExtensions.cpp + ${LL_SOURCE_DIR}/ExtFixedJoint.cpp + ${LL_SOURCE_DIR}/ExtJoint.cpp + ${LL_SOURCE_DIR}/ExtMetaData.cpp + ${LL_SOURCE_DIR}/ExtPrismaticJoint.cpp + ${LL_SOURCE_DIR}/ExtPvd.cpp + ${LL_SOURCE_DIR}/ExtPxStringTable.cpp + ${LL_SOURCE_DIR}/ExtRaycastCCD.cpp + ${LL_SOURCE_DIR}/ExtRevoluteJoint.cpp + ${LL_SOURCE_DIR}/ExtRigidBodyExt.cpp + ${LL_SOURCE_DIR}/ExtRigidActorExt.cpp + ${LL_SOURCE_DIR}/ExtSceneQueryExt.cpp + ${LL_SOURCE_DIR}/ExtSimpleFactory.cpp + ${LL_SOURCE_DIR}/ExtSmoothNormals.cpp + ${LL_SOURCE_DIR}/ExtSphericalJoint.cpp + ${LL_SOURCE_DIR}/ExtTriangleMeshExt.cpp + ${LL_SOURCE_DIR}/ExtConstraintHelper.h + ${LL_SOURCE_DIR}/ExtCpuWorkerThread.h + ${LL_SOURCE_DIR}/ExtD6Joint.h + ${LL_SOURCE_DIR}/ExtDefaultCpuDispatcher.h + ${LL_SOURCE_DIR}/ExtDistanceJoint.h + ${LL_SOURCE_DIR}/ExtContactJoint.h + ${LL_SOURCE_DIR}/ExtFixedJoint.h + ${LL_SOURCE_DIR}/ExtInertiaTensor.h + ${LL_SOURCE_DIR}/ExtJoint.h + ${LL_SOURCE_DIR}/ExtJointMetaDataExtensions.h + ${LL_SOURCE_DIR}/ExtPlatform.h + ${LL_SOURCE_DIR}/ExtPrismaticJoint.h + ${LL_SOURCE_DIR}/ExtPvd.h + ${LL_SOURCE_DIR}/ExtRevoluteJoint.h + ${LL_SOURCE_DIR}/ExtSerialization.h + ${LL_SOURCE_DIR}/ExtSharedQueueEntryPool.h + ${LL_SOURCE_DIR}/ExtSphericalJoint.h + ${LL_SOURCE_DIR}/ExtTaskQueueHelper.h +) +SOURCE_GROUP(src FILES ${PHYSX_EXTENSIONS_SOURCE}) + +SET(PHYSX_EXTENSIONS_METADATA_SOURCE + ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp + ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h + ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h + ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h +) +SOURCE_GROUP(src\\metadata FILES ${PHYSX_EXTENSIONS_METADATA_SOURCE}) + +SET(PHYSX_EXTENSIONS_HEADERS + ${PHYSX_ROOT_DIR}/include/extensions/PxBinaryConverter.h + ${PHYSX_ROOT_DIR}/include/extensions/PxBroadPhaseExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxCollectionExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxConstraintExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxContactJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxConvexMeshExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxD6Joint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxD6JointCreate.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDefaultAllocator.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDefaultCpuDispatcher.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDefaultErrorCallback.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDefaultSimulationFilterShader.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDefaultStreams.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDistanceJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxContactJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxExtensionsAPI.h + ${PHYSX_ROOT_DIR}/include/extensions/PxFixedJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxJointLimit.h +# ${PHYSX_ROOT_DIR}/include/extensions/PxJointRepXSerializer.h + ${PHYSX_ROOT_DIR}/include/extensions/PxMassProperties.h + ${PHYSX_ROOT_DIR}/include/extensions/PxPrismaticJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRaycastCCD.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRepXSerializer.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRepXSimpleType.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRevoluteJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRigidActorExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRigidBodyExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxSceneQueryExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxSerialization.h + ${PHYSX_ROOT_DIR}/include/extensions/PxShapeExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxSimpleFactory.h + ${PHYSX_ROOT_DIR}/include/extensions/PxSmoothNormals.h + ${PHYSX_ROOT_DIR}/include/extensions/PxSphericalJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxStringTableExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxTriangleMeshExt.h +) +SOURCE_GROUP(include FILES ${PHYSX_EXTENSIONS_HEADERS}) + +SET(PHYSX_FILEBUF_HEADERS + ${PHYSX_ROOT_DIR}/include/filebuf/PxFileBuf.h +) +SOURCE_GROUP(include FILES ${PHYSX_FILEBUF_HEADERS}) + +SET(PHYSX_EXTENSIONS_SERIALIZATION_SOURCE + ${LL_SOURCE_DIR}/serialization/SnSerialization.cpp + ${LL_SOURCE_DIR}/serialization/SnSerializationRegistry.cpp + ${LL_SOURCE_DIR}/serialization/SnSerializationRegistry.h + ${LL_SOURCE_DIR}/serialization/SnSerialUtils.cpp + ${LL_SOURCE_DIR}/serialization/SnSerialUtils.h +) +SOURCE_GROUP(serialization FILES ${PHYSX_EXTENSIONS_SERIALIZATION_SOURCE}) + +SET(PHYSX_EXTENSIONS_SERIALIZATION_XML_SOURCE + ${LL_SOURCE_DIR}/serialization/Xml/SnJointRepXSerializer.cpp + ${LL_SOURCE_DIR}/serialization/Xml/SnJointRepXSerializer.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXCoreSerializer.cpp + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXUpgrader.cpp + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlSerialization.cpp + ${LL_SOURCE_DIR}/serialization/Xml/SnPxStreamOperators.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepX1_0Defaults.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepX3_1Defaults.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepX3_2Defaults.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXCollection.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXCoreSerializer.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXSerializerImpl.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXUpgrader.h + ${LL_SOURCE_DIR}/serialization/Xml/SnSimpleXmlWriter.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlDeserializer.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlImpl.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlMemoryAllocator.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlMemoryPool.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlMemoryPoolStreams.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlReader.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlSerializer.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlSimpleXmlWriter.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlStringToType.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlVisitorReader.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlVisitorWriter.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlWriter.h +) +SOURCE_GROUP(serialization\\xml FILES ${PHYSX_EXTENSIONS_SERIALIZATION_XML_SOURCE}) + +SET(PHYSX_EXTENSIONS_SERIALIZATION_FILE_SOURCE + ${LL_SOURCE_DIR}/serialization/File/SnFile.h +) +SOURCE_GROUP(serialization\\file FILES ${PHYSX_EXTENSIONS_SERIALIZATION_FILE_SOURCE}) + +SET(PHYSX_EXTENSIONS_SERIALIZATION_BINARY_SOURCE + ${LL_SOURCE_DIR}/serialization/Binary/SnBinaryDeserialization.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnBinarySerialization.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Align.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Convert.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Error.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_MetaData.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Output.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Union.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnSerializationContext.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX.h + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Align.h + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Common.h + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_MetaData.h + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Output.h + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Union.h + ${LL_SOURCE_DIR}/serialization/Binary/SnSerializationContext.h +) +SOURCE_GROUP(serialization\\binary FILES ${PHYSX_EXTENSIONS_SERIALIZATION_BINARY_SOURCE}) + +ADD_LIBRARY(PhysXExtensions ${PHYSXEXTENSIONS_LIBTYPE} + ${PHYSXEXTENSIONS_PLATFORM_SRC_FILES} + + ${PHYSX_EXTENSIONS_SOURCE} + ${PHYSX_EXTENSIONS_METADATA_SOURCE} + + ${PHYSX_EXTENSIONS_HEADERS} + ${PHYSX_FILEBUF_HEADERS} + + ${PHYSX_EXTENSIONS_SERIALIZATION_SOURCE} + ${PHYSX_EXTENSIONS_SERIALIZATION_XML_SOURCE} + ${PHYSX_EXTENSIONS_SERIALIZATION_FILE_SOURCE} + ${PHYSX_EXTENSIONS_SERIALIZATION_BINARY_SOURCE} +) + +INSTALL(FILES ${PHYSX_EXTENSIONS_HEADERS} DESTINATION include/extensions) +INSTALL(FILES ${PHYSX_FILEBUF_HEADERS} DESTINATION include/filebuf) + +TARGET_INCLUDE_DIRECTORIES(PhysXExtensions + + PRIVATE ${PHYSXEXTENSIONS_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/pvd + PRIVATE ${PHYSX_ROOT_DIR}/include/cooking + PRIVATE ${PHYSX_ROOT_DIR}/include/extensions + PRIVATE ${PHYSX_ROOT_DIR}/include/vehicle + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + + PRIVATE ${PHYSX_SOURCE_DIR}/physxmetadata/core/include + PRIVATE ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/include + + PRIVATE ${PHYSX_SOURCE_DIR}/physxextensions/src + PRIVATE ${PHYSX_SOURCE_DIR}/physxextensions/src/serialization/Xml + PRIVATE ${PHYSX_SOURCE_DIR}/physxextensions/src/serialization/Binary + PRIVATE ${PHYSX_SOURCE_DIR}/physxextensions/src/serialization/File + + PRIVATE ${PHYSX_SOURCE_DIR}/pvdsdk/src + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src + + PRIVATE ${PHYSX_SOURCE_DIR}/pvd/include + + PRIVATE ${PHYSX_SOURCE_DIR}/fastxml/include +) + +TARGET_LINK_LIBRARIES(PhysXExtensions + PRIVATE ${PHYSXEXTENSIONS_PRIVATE_PLATFORM_LINKED_LIBS} + PUBLIC PhysXFoundation + PUBLIC PhysXPvdSDK + PUBLIC PhysX +) + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(PhysXExtensions + PRIVATE ${PHYSXEXTENSIONS_COMPILE_DEFS} +) + + +SET_TARGET_PROPERTIES(PhysXExtensions PROPERTIES + OUTPUT_NAME PhysXExtensions +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXEXTENSIONS_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXExtensions PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXExtensions_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXExtensions_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXExtensions_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXExtensions_static" + ) +ENDIF() + +IF(PHYSXEXTENSIONS_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXExtensions PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXEXTENSIONS_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXEXTENSIONS_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXEXTENSIONS_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXEXTENSIONS_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXEXTENSIONS_PLATFORM_SRC_FILES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_METADATA_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_FILEBUF_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_SERIALIZATION_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_SERIALIZATION_XML_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_SERIALIZATION_FILE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_SERIALIZATION_BINARY_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXExtensions PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake new file mode 100644 index 000000000..fb93058f3 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake @@ -0,0 +1,198 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/foundation) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXFoundation.cmake) + +SET(PHYSXFOUNDATION_HEADERS + ${PHYSX_ROOT_DIR}/include/PxFoundation.h +) +SOURCE_GROUP(include FILES ${PHYSXFOUNDATION_HEADERS}) + +SET(PXSHARED_HEADERS + ${PXSHARED_PATH}/include/foundation/Px.h + ${PXSHARED_PATH}/include/foundation/PxAllocatorCallback.h + ${PXSHARED_PATH}/include/foundation/PxProfiler.h + ${PXSHARED_PATH}/include/foundation/PxAssert.h + ${PXSHARED_PATH}/include/foundation/PxBitAndData.h + ${PXSHARED_PATH}/include/foundation/PxBounds3.h + ${PXSHARED_PATH}/include/foundation/PxErrorCallback.h + ${PXSHARED_PATH}/include/foundation/PxErrors.h + ${PXSHARED_PATH}/include/foundation/PxFlags.h + ${PXSHARED_PATH}/include/foundation/PxIntrinsics.h + ${PXSHARED_PATH}/include/foundation/PxIO.h + ${PXSHARED_PATH}/include/foundation/PxMat33.h + ${PXSHARED_PATH}/include/foundation/PxMat44.h + ${PXSHARED_PATH}/include/foundation/PxMath.h + ${PXSHARED_PATH}/include/foundation/PxMathUtils.h + ${PXSHARED_PATH}/include/foundation/PxMemory.h + ${PXSHARED_PATH}/include/foundation/PxPlane.h + ${PXSHARED_PATH}/include/foundation/PxPreprocessor.h + ${PXSHARED_PATH}/include/foundation/PxQuat.h + ${PXSHARED_PATH}/include/foundation/PxSimpleTypes.h + ${PXSHARED_PATH}/include/foundation/PxStrideIterator.h + ${PXSHARED_PATH}/include/foundation/PxTransform.h + ${PXSHARED_PATH}/include/foundation/PxUnionCast.h + ${PXSHARED_PATH}/include/foundation/PxVec2.h + ${PXSHARED_PATH}/include/foundation/PxVec3.h + ${PXSHARED_PATH}/include/foundation/PxVec4.h +) +SOURCE_GROUP(shared\\include FILES ${PXSHARED_HEADERS}) + +SET(PHYSXFOUNDATION_SOURCE + ${LL_SOURCE_DIR}/src/PsAllocator.cpp + ${LL_SOURCE_DIR}/src/PsAssert.cpp + ${LL_SOURCE_DIR}/src/PsFoundation.cpp + ${LL_SOURCE_DIR}/src/PsMathUtils.cpp + ${LL_SOURCE_DIR}/src/PsString.cpp + ${LL_SOURCE_DIR}/src/PsTempAllocator.cpp + ${LL_SOURCE_DIR}/src/PsUtilities.cpp +) +SOURCE_GROUP(src\\src FILES ${PHYSXFOUNDATION_SOURCE}) + +SET(PHYSXFOUNDATION_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/Ps.h + ${LL_SOURCE_DIR}/include/PsAlignedMalloc.h + ${LL_SOURCE_DIR}/include/PsAlloca.h + ${LL_SOURCE_DIR}/include/PsAllocator.h + ${LL_SOURCE_DIR}/include/PsAoS.h + ${LL_SOURCE_DIR}/include/PsArray.h + ${LL_SOURCE_DIR}/include/PsAtomic.h + ${LL_SOURCE_DIR}/include/PsBasicTemplates.h + ${LL_SOURCE_DIR}/include/PsBitUtils.h + ${LL_SOURCE_DIR}/include/PsBroadcast.h + ${LL_SOURCE_DIR}/include/PsCpu.h + ${LL_SOURCE_DIR}/include/PsFoundation.h + ${LL_SOURCE_DIR}/include/PsFPU.h + ${LL_SOURCE_DIR}/include/PsHash.h + ${LL_SOURCE_DIR}/include/PsHashInternals.h + ${LL_SOURCE_DIR}/include/PsHashMap.h + ${LL_SOURCE_DIR}/include/PsHashSet.h + ${LL_SOURCE_DIR}/include/PsInlineAllocator.h + ${LL_SOURCE_DIR}/include/PsInlineAoS.h + ${LL_SOURCE_DIR}/include/PsInlineArray.h + ${LL_SOURCE_DIR}/include/PsIntrinsics.h + ${LL_SOURCE_DIR}/include/PsMathUtils.h + ${LL_SOURCE_DIR}/include/PsMutex.h + ${LL_SOURCE_DIR}/include/PsPool.h + ${LL_SOURCE_DIR}/include/PsSList.h + ${LL_SOURCE_DIR}/include/PsSocket.h + ${LL_SOURCE_DIR}/include/PsSort.h + ${LL_SOURCE_DIR}/include/PsSortInternals.h + ${LL_SOURCE_DIR}/include/PsString.h + ${LL_SOURCE_DIR}/include/PsSync.h + ${LL_SOURCE_DIR}/include/PsTempAllocator.h + ${LL_SOURCE_DIR}/include/PsThread.h + ${LL_SOURCE_DIR}/include/PsTime.h + ${LL_SOURCE_DIR}/include/PsUserAllocated.h + ${LL_SOURCE_DIR}/include/PsUtilities.h + ${LL_SOURCE_DIR}/include/PsVecMath.h + ${LL_SOURCE_DIR}/include/PsVecMathAoSScalar.h + ${LL_SOURCE_DIR}/include/PsVecMathAoSScalarInline.h + ${LL_SOURCE_DIR}/include/PsVecMathSSE.h + ${LL_SOURCE_DIR}/include/PsVecMathUtilities.h + ${LL_SOURCE_DIR}/include/PsVecQuat.h + ${LL_SOURCE_DIR}/include/PsVecTransform.h +) +SOURCE_GROUP("src\\include" FILES ${PHYSXFOUNDATION_SOURCE_HEADERS}) + +ADD_LIBRARY(PhysXFoundation ${PHYSXFOUNDATION_LIBTYPE} + ${PHYSXFOUNDATION_SOURCE} + ${PHYSXFOUNDATION_SOURCE_HEADERS} + ${PHYSXFOUNDATION_HEADERS} + ${PHYSXFOUNDATION_PLATFORM_FILES} + ${PXSHARED_HEADERS} + ${PXSHARED_PLATFORM_HEADERS} +) + +# Add the headers to the install +INSTALL(FILES ${PHYSXFOUNDATION_HEADERS} DESTINATION include) +INSTALL(FILES ${PHYSXFOUNDATION_SOURCE_HEADERS} DESTINATION source/foundation/include) +INSTALL(FILES ${PXSHARED_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation) + +TARGET_INCLUDE_DIRECTORIES(PhysXFoundation + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PXSHARED_PATH}/include + PRIVATE ${LL_SOURCE_DIR}/include + PRIVATE ${PHYSXFOUNDATION_PLATFORM_INCLUDES} + + INTERFACE $$ + INTERFACE $$ + + # FIXME: This is really terrible! Don't export src directories + INTERFACE $$ +) + +TARGET_COMPILE_DEFINITIONS(PhysXFoundation + PRIVATE ${PHYSXFOUNDATION_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXFoundation PROPERTIES + OUTPUT_NAME PhysXFoundation +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXFOUNDATION_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXFoundation PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXFoundation_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXFoundation_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXFoundation_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXFoundation_static" + ) +ENDIF() + +IF(PHYSXFOUNDATION_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXFoundation PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXFOUNDATION_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXFOUNDATION_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXFOUNDATION_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXFOUNDATION_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +# Add linked libraries +TARGET_LINK_LIBRARIES(PhysXFoundation + PRIVATE ${PHYSXFOUNDATION_PLATFORM_LINKED_LIBS} +) + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_SOURCE_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_PLATFORM_FILES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PXSHARED_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PXSHARED_PLATFORM_HEADERS}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXFoundation PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake new file mode 100644 index 000000000..aad0e7143 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake @@ -0,0 +1,186 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/pvd) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXPvdSDK.cmake) + + +SET(PHYSXPVDSDK_HEADERS + ${PHYSX_ROOT_DIR}/include/pvd/PxPvd.h + ${PHYSX_ROOT_DIR}/include/pvd/PxPvdTransport.h +) +SOURCE_GROUP(include FILES ${PHYSXPVDSDK_HEADERS}) + +SET(PHYSXPVDSDK_SOURCE + ${LL_SOURCE_DIR}/src/PxProfileContextProvider.h + ${LL_SOURCE_DIR}/src/PxProfileContextProviderImpl.h + ${LL_SOURCE_DIR}/src/PxProfileDataBuffer.h + ${LL_SOURCE_DIR}/src/PxProfileDataParsing.h + ${LL_SOURCE_DIR}/src/PxProfileEventBuffer.h + ${LL_SOURCE_DIR}/src/PxProfileEventBufferAtomic.h + ${LL_SOURCE_DIR}/src/PxProfileEventBufferClient.h + ${LL_SOURCE_DIR}/src/PxProfileEventBufferClientManager.h + ${LL_SOURCE_DIR}/src/PxProfileEventId.h + ${LL_SOURCE_DIR}/src/PxProfileEventImpl.cpp + ${LL_SOURCE_DIR}/src/PxProfileEventMutex.h + ${LL_SOURCE_DIR}/src/PxProfileEventNames.h + ${LL_SOURCE_DIR}/src/PxProfileEvents.h + ${LL_SOURCE_DIR}/src/PxProfileEventSender.h + ${LL_SOURCE_DIR}/src/PxProfileEventSerialization.h + ${LL_SOURCE_DIR}/src/PxProfileMemory.h + ${LL_SOURCE_DIR}/src/PxProfileMemoryBuffer.h + ${LL_SOURCE_DIR}/src/PxProfileMemoryEventBuffer.h + ${LL_SOURCE_DIR}/src/PxProfileMemoryEvents.h + ${LL_SOURCE_DIR}/src/PxProfileScopedEvent.h + ${LL_SOURCE_DIR}/src/PxProfileScopedMutexLock.h + ${LL_SOURCE_DIR}/src/PxPvdProfileZone.h + ${LL_SOURCE_DIR}/src/PxProfileZoneImpl.h + ${LL_SOURCE_DIR}/src/PxProfileZoneManager.h + ${LL_SOURCE_DIR}/src/PxProfileZoneManagerImpl.h + ${LL_SOURCE_DIR}/src/PxPvd.cpp + ${LL_SOURCE_DIR}/src/PxPvdBits.h + ${LL_SOURCE_DIR}/src/PxPvdByteStreams.h + ${LL_SOURCE_DIR}/src/PxPvdCommStreamEvents.h + ${LL_SOURCE_DIR}/src/PxPvdCommStreamEventSink.h + ${LL_SOURCE_DIR}/src/PxPvdCommStreamTypes.h + ${LL_SOURCE_DIR}/src/PxPvdDataStream.cpp + ${LL_SOURCE_DIR}/src/PxPvdDefaultFileTransport.cpp + ${LL_SOURCE_DIR}/src/PxPvdDefaultFileTransport.h + ${LL_SOURCE_DIR}/src/PxPvdDefaultSocketTransport.cpp + ${LL_SOURCE_DIR}/src/PxPvdDefaultSocketTransport.h + ${LL_SOURCE_DIR}/src/PxPvdFoundation.h + ${LL_SOURCE_DIR}/src/PxPvdImpl.cpp + ${LL_SOURCE_DIR}/src/PxPvdImpl.h + ${LL_SOURCE_DIR}/src/PxPvdInternalByteStreams.h + ${LL_SOURCE_DIR}/src/PxPvdMarshalling.h + ${LL_SOURCE_DIR}/src/PxPvdMemClient.cpp + ${LL_SOURCE_DIR}/src/PxPvdMemClient.h + ${LL_SOURCE_DIR}/src/PxPvdObjectModelInternalTypeDefs.h + ${LL_SOURCE_DIR}/src/PxPvdObjectModelInternalTypes.h + ${LL_SOURCE_DIR}/src/PxPvdObjectModelMetaData.cpp + ${LL_SOURCE_DIR}/src/PxPvdObjectModelMetaData.h + ${LL_SOURCE_DIR}/src/PxPvdObjectRegistrar.cpp + ${LL_SOURCE_DIR}/src/PxPvdObjectRegistrar.h + ${LL_SOURCE_DIR}/src/PxPvdProfileZoneClient.cpp + ${LL_SOURCE_DIR}/src/PxPvdProfileZoneClient.h + ${LL_SOURCE_DIR}/src/PxPvdUserRenderer.cpp + ${LL_SOURCE_DIR}/src/PxPvdUserRenderImpl.h + ${LL_SOURCE_DIR}/src/PxPvdUserRenderTypes.h +) +SOURCE_GROUP(src\\src FILES ${PHYSXPVDSDK_SOURCE}) + +SET(PHYSXPVDSDK_INTERNAL_HEADERS + ${LL_SOURCE_DIR}/include/PsPvd.h + ${LL_SOURCE_DIR}/include/PxProfileAllocatorWrapper.h + ${LL_SOURCE_DIR}/include/PxPvdClient.h + ${LL_SOURCE_DIR}/include/PxPvdDataStream.h + ${LL_SOURCE_DIR}/include/PxPvdDataStreamHelpers.h + ${LL_SOURCE_DIR}/include/PxPvdErrorCodes.h + ${LL_SOURCE_DIR}/include/PxPvdObjectModelBaseTypes.h + ${LL_SOURCE_DIR}/include/PxPvdRenderBuffer.h + ${LL_SOURCE_DIR}/include/PxPvdUserRenderer.h +) +SOURCE_GROUP(src\\include FILES ${PHYSXPVDSDK_INTERNAL_HEADERS}) + +SET(PHYSXPVDSDK_FILEBUF_FILES + ${PHYSX_SOURCE_DIR}/filebuf/include/PsAsciiConversion.h + ${PHYSX_SOURCE_DIR}/filebuf/include/PsAsciiConversion.inl + ${PHYSX_SOURCE_DIR}/filebuf/include/PsFileBuffer.h + ${PHYSX_SOURCE_DIR}/filebuf/include/PsIOStream.h + ${PHYSX_SOURCE_DIR}/filebuf/include/PsIOStream.inl + ${PHYSX_SOURCE_DIR}/filebuf/include/PsMemoryBuffer.h +) +SOURCE_GROUP(filebuf\\include FILES ${PHYSXPVDSDK_FILEBUF_FILES}) + +ADD_LIBRARY(PhysXPvdSDK ${PHYSXPVDSDK_LIBTYPE} + ${PHYSXPVDSDK_HEADERS} + ${PHYSXPVDSDK_FILEBUF_FILES} + + ${PHYSXPVDSDK_INTERNAL_HEADERS} + ${PHYSXPVDSDK_SOURCE} + + ${PHYSXPVDSDK_PLATFORM_FILES} +) + +TARGET_INCLUDE_DIRECTORIES(PhysXPvdSDK + PRIVATE ${PHYSXPVDSDK_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${LL_SOURCE_DIR}/include + PRIVATE ${PHYSX_SOURCE_DIR}/filebuf/include + +) + +TARGET_COMPILE_DEFINITIONS(PhysXPvdSDK + PRIVATE ${PHYSXPVDSDK_COMPILE_DEFS} +) + +# Add linked libraries +TARGET_LINK_LIBRARIES(PhysXPvdSDK + PUBLIC ${PHYSXPVDSDK_PLATFORM_LINKED_LIBS} + PUBLIC PhysXFoundation +) + +SET_TARGET_PROPERTIES(PhysXPvdSDK PROPERTIES + OUTPUT_NAME PhysXPvdSDK +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXPVDSDK_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXPvdSDK PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXPvdSDK_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXPvdSDK_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXPvdSDK_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXPvdSDK_static" + ) +ENDIF() + +IF(PHYSXPVDSDK_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXPvdSDK PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXPVDSDK_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXPVDSDK_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXPVDSDK_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXPVDSDK_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXPVDSDK_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXPVDSDK_INTERNAL_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXPVDSDK_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXPVDSDK_PLATFORM_FILES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXPVDSDK_FILEBUF_FILES}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXPvdSDK PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake new file mode 100644 index 000000000..61596f3d9 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake @@ -0,0 +1,103 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/task) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXTask.cmake) + +SET(PHYSXTASK_HEADERS + ${PHYSX_ROOT_DIR}/include/task/PxCpuDispatcher.h + ${PHYSX_ROOT_DIR}/include/task/PxGpuDispatcher.h + ${PHYSX_ROOT_DIR}/include/task/PxGpuTask.h + ${PHYSX_ROOT_DIR}/include/task/PxTask.h + ${PHYSX_ROOT_DIR}/include/task/PxTaskDefine.h + ${PHYSX_ROOT_DIR}/include/task/PxTaskManager.h +) +SOURCE_GROUP(include FILES ${PHYSXTASK_HEADERS}) + +SET(PHYSXTASK_SOURCE + ${LL_SOURCE_DIR}/src/TaskManager.cpp +) +SOURCE_GROUP(src FILES ${PHYSXTASK_SOURCE}) + +ADD_LIBRARY(PhysXTask ${PHYSXTASK_LIBTYPE} + ${PHYSXTASK_HEADERS} + ${PHYSXTASK_SOURCE} +) + +INSTALL(FILES ${PHYSXTASK_HEADERS} DESTINATION include/task) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(PhysXTask + PRIVATE ${PHYSXTASK_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_SOURCE_DIR}/cudamanager/include +) + +TARGET_COMPILE_DEFINITIONS(PhysXTask + PRIVATE ${PHYSXTASK_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXTask PROPERTIES + OUTPUT_NAME PhysXTask +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXTASK_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXTask PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXTask_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXTask_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXTask_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXTask_static" + ) +ENDIF() + +IF(PHYSXTASK_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXTask PROPERTIES + COMPILE_PDB_NAME_DEBUG "${PHYSXTASK_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${PHYSXTASK_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${PHYSXTASK_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${PHYSXTASK_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXTASK_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXTASK_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXTask PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake new file mode 100644 index 000000000..64ac6ff85 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake @@ -0,0 +1,176 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physxvehicle/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXVehicle.cmake) + + + +SET(PHYSX_VEHICLE_HEADERS + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleComponents.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleDrive.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleDrive4W.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleDriveNW.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleDriveTank.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleNoDrive.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleSDK.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleShaders.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleTireFriction.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleUpdate.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleUtil.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleUtilControl.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleUtilSetup.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleUtilTelemetry.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleWheels.h +) +SOURCE_GROUP(include FILES ${PHYSX_VEHICLE_HEADERS}) + +SET(PHYSX_VEHICLE_SOURCE + ${LL_SOURCE_DIR}/PxVehicleComponents.cpp + ${LL_SOURCE_DIR}/PxVehicleDrive.cpp + ${LL_SOURCE_DIR}/PxVehicleDrive4W.cpp + ${LL_SOURCE_DIR}/PxVehicleDriveNW.cpp + ${LL_SOURCE_DIR}/PxVehicleDriveTank.cpp + ${LL_SOURCE_DIR}/PxVehicleMetaData.cpp + ${LL_SOURCE_DIR}/PxVehicleNoDrive.cpp + ${LL_SOURCE_DIR}/PxVehicleSDK.cpp + ${LL_SOURCE_DIR}/PxVehicleSerialization.cpp + ${LL_SOURCE_DIR}/PxVehicleSuspWheelTire4.cpp + ${LL_SOURCE_DIR}/PxVehicleTireFriction.cpp + ${LL_SOURCE_DIR}/PxVehicleUpdate.cpp + ${LL_SOURCE_DIR}/PxVehicleWheels.cpp + ${LL_SOURCE_DIR}/VehicleUtilControl.cpp + ${LL_SOURCE_DIR}/VehicleUtilSetup.cpp + ${LL_SOURCE_DIR}/VehicleUtilTelemetry.cpp + ${LL_SOURCE_DIR}/PxVehicleDefaults.h + ${LL_SOURCE_DIR}/PxVehicleLinearMath.h + ${LL_SOURCE_DIR}/PxVehicleSerialization.h + ${LL_SOURCE_DIR}/PxVehicleSuspLimitConstraintShader.h + ${LL_SOURCE_DIR}/PxVehicleSuspWheelTire4.h +) +SOURCE_GROUP(src FILES ${PHYSX_VEHICLE_SOURCE}) + +SET(PHYSX_VEHICLE_METADATA_HEADERS + ${LL_SOURCE_DIR}/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjectNames.h + ${LL_SOURCE_DIR}/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h + ${LL_SOURCE_DIR}/physxmetadata/include/PxVehicleMetaDataObjects.h +) +SOURCE_GROUP(metadata\\include FILES ${PHYSX_VEHICLE_METADATA_HEADERS}) + +SET(PHYSX_VEHICLE_METADATA_SOURCE + ${LL_SOURCE_DIR}/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp + ${LL_SOURCE_DIR}/physxmetadata/src/PxVehicleMetaDataObjects.cpp +) +SOURCE_GROUP(metadata\\src FILES ${PHYSX_VEHICLE_METADATA_SOURCE}) + +ADD_LIBRARY(PhysXVehicle ${PHYSXVEHICLE_LIBTYPE} + ${PHYSX_VEHICLE_SOURCE} + ${PHYSX_VEHICLE_HEADERS} + ${PHYSX_VEHICLE_METADATA_HEADERS} + ${PHYSX_VEHICLE_METADATA_SOURCE} +) + +INSTALL(FILES ${PHYSX_VEHICLE_HEADERS} DESTINATION include/vehicle) + +TARGET_INCLUDE_DIRECTORIES(PhysXVehicle + PRIVATE ${PHYSXVEHICLE_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/vehicle + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/extensions + PRIVATE ${PHYSX_ROOT_DIR}/include/pvd + PRIVATE ${PHYSX_ROOT_DIR}/include/physxprofilesdk + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physxvehicle/src + PRIVATE ${PHYSX_SOURCE_DIR}/physxvehicle/src/physxmetadata/include + + PRIVATE ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/include + PRIVATE ${PHYSX_SOURCE_DIR}/physxmetadata/core/include + + PRIVATE ${PHYSX_SOURCE_DIR}/physxextensions/src/serialization/Xml + + PRIVATE ${PHYSX_SOURCE_DIR}/pvdsdk/src + + PRIVATE ${PHYSX_SOURCE_DIR}/pvd/include +) + +# No linked libraries + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(PhysXVehicle + PRIVATE ${PHYSXVEHICLE_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXVehicle PROPERTIES + OUTPUT_NAME PhysXVehicle +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXVEHICLE_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXVehicle PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXVehicle_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXVehicle_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXVehicle_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXVehicle_static" + ) +ENDIF() + +IF(PHYSXVEHICLE_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXVehicle PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXVEHICLE_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXVEHICLE_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXVEHICLE_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXVEHICLE_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +TARGET_LINK_LIBRARIES(PhysXVehicle + PUBLIC ${PHYSXVEHICLE_PLATFORM_LINKED_LIBS} PhysXFoundation PhysXPvdSDK +) + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_VEHICLE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_VEHICLE_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_VEHICLE_METADATA_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_VEHICLE_METADATA_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXVehicle PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake new file mode 100644 index 000000000..c73b20b24 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake @@ -0,0 +1,159 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/scenequery/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/SceneQuery.cmake) + + +SET(SCENEQUERY_BASE_DIR ${PHYSX_ROOT_DIR}/source/scenequery) +SET(SCENEQUERY_HEADERS + ${SCENEQUERY_BASE_DIR}/include/SqPruner.h + ${SCENEQUERY_BASE_DIR}/include/SqPrunerMergeData.h + ${SCENEQUERY_BASE_DIR}/include/SqPruningStructure.h + ${SCENEQUERY_BASE_DIR}/include/SqSceneQueryManager.h +) +SOURCE_GROUP(include FILES ${SCENEQUERY_HEADERS}) + +SET(SCENEQUERY_SOURCE + ${SCENEQUERY_BASE_DIR}/src/SqAABBPruner.cpp + ${SCENEQUERY_BASE_DIR}/src/SqAABBPruner.h + ${SCENEQUERY_BASE_DIR}/src/SqAABBTree.cpp + ${SCENEQUERY_BASE_DIR}/src/SqAABBTree.h + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBPruner.cpp + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBPruner.h + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBPrunerCore.cpp + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBPrunerCore.h + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBTree.cpp + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBTree.h + ${SCENEQUERY_BASE_DIR}/src/SqAABBTreeUpdateMap.cpp + ${SCENEQUERY_BASE_DIR}/src/SqAABBTreeUpdateMap.h + ${SCENEQUERY_BASE_DIR}/src/SqBounds.cpp + ${SCENEQUERY_BASE_DIR}/src/SqBounds.h + ${SCENEQUERY_BASE_DIR}/src/SqCompoundPruner.cpp + ${SCENEQUERY_BASE_DIR}/src/SqCompoundPruner.h + ${SCENEQUERY_BASE_DIR}/src/SqCompoundPruningPool.cpp + ${SCENEQUERY_BASE_DIR}/src/SqCompoundPruningPool.h + ${SCENEQUERY_BASE_DIR}/src/SqBucketPruner.cpp + ${SCENEQUERY_BASE_DIR}/src/SqBucketPruner.h + ${SCENEQUERY_BASE_DIR}/src/SqExtendedBucketPruner.cpp + ${SCENEQUERY_BASE_DIR}/src/SqExtendedBucketPruner.h + ${SCENEQUERY_BASE_DIR}/src/SqMetaData.cpp + ${SCENEQUERY_BASE_DIR}/src/SqPruningPool.cpp + ${SCENEQUERY_BASE_DIR}/src/SqPruningPool.h + ${SCENEQUERY_BASE_DIR}/src/SqPruningStructure.cpp + ${SCENEQUERY_BASE_DIR}/src/SqSceneQueryManager.cpp + ${SCENEQUERY_BASE_DIR}/src/SqTypedef.h +) +SOURCE_GROUP(src FILES ${SCENEQUERY_SOURCE}) + +ADD_LIBRARY(SceneQuery ${SCENEQUERY_LIBTYPE} + ${SCENEQUERY_HEADERS} + ${SCENEQUERY_SOURCE} +) + +# Target specific compile options + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(SceneQuery + PRIVATE ${SCENEQUERY_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/pvd + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + + PRIVATE ${PHYSX_SOURCE_DIR}/scenequery/include + PRIVATE ${PHYSX_SOURCE_DIR}/simulationcontroller/include + + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src/buffering + + PRIVATE ${PHYSX_SOURCE_DIR}/pvd/include +) + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(SceneQuery + + # Common to all configurations + PRIVATE ${SCENEQUERY_COMPILE_DEFS} +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND SCENEQUERY_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(SceneQuery PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "SceneQuery_static" + ARCHIVE_OUTPUT_NAME_CHECKED "SceneQuery_static" + ARCHIVE_OUTPUT_NAME_PROFILE "SceneQuery_static" + ARCHIVE_OUTPUT_NAME_RELEASE "SceneQuery_static" + ) +ENDIF() + +IF(SCENEQUERY_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(SceneQuery PROPERTIES + COMPILE_PDB_NAME_DEBUG "${SCENEQUERY_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${SCENEQUERY_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${SCENEQUERY_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${SCENEQUERY_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${SCENEQUERY_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${SCENEQUERY_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(SceneQuery PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/SimulationController.cmake new file mode 100644 index 000000000..577a75d38 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/SimulationController.cmake @@ -0,0 +1,207 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/simulationcontroller/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/SimulationController.cmake) + + +SET(SIMULATIONCONTROLLER_BASE_DIR ${PHYSX_ROOT_DIR}/source/simulationcontroller) +SET(SIMULATIONCONTROLLER_HEADERS + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScActorCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScArticulationCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScArticulationJointCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScBodyCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScConstraintCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScIterators.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScMaterialCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScPhysics.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScRigidCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScScene.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScShapeCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScStaticCore.h +) +SOURCE_GROUP(include FILES ${SIMULATIONCONTROLLER_HEADERS}) + +SET(SIMULATIONCONTROLLER_SOURCE + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScActorCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScActorPair.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScActorSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScActorSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationJointCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationJointSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationJointSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScBodyCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScBodySim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScBodySim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScClient.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintGroupNode.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintGroupNode.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintInteraction.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintInteraction.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintProjectionManager.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintProjectionManager.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintProjectionTree.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintProjectionTree.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScContactReportBuffer.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScContactStream.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScElementInteractionMarker.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScElementInteractionMarker.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScElementSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScElementSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScElementSimInteraction.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScInteraction.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScInteraction.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScInteractionFlags.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScIterators.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScMetaData.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScNPhaseCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScNPhaseCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScObjectIDTracker.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScPhysics.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScRigidCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScRigidSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScRigidSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScScene.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScShapeCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScShapeInteraction.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScShapeInteraction.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScShapeSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScShapeSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSimStateData.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSimStats.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSimStats.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSimulationController.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSimulationController.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSqBoundsManager.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSqBoundsManager.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScStaticCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScStaticSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScTriggerInteraction.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScTriggerInteraction.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScTriggerPairs.h +) +SOURCE_GROUP(src FILES ${SIMULATIONCONTROLLER_SOURCE}) + +ADD_LIBRARY(SimulationController ${SIMULATIONCONTROLLER_LIBTYPE} + ${SIMULATIONCONTROLLER_HEADERS} + ${SIMULATIONCONTROLLER_SOURCE} +) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(SimulationController + PRIVATE ${SIMULATIONCONTROLLER_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/pvd + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/simulationcontroller/include + PRIVATE ${PHYSX_SOURCE_DIR}/simulationcontroller/src + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/collision + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/utils + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/software/include + + PRIVATE ${PHYSX_SOURCE_DIR}/lowleveldynamics/include + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevelaabb/include + +) + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(SimulationController + + # Common to all configurations + PRIVATE ${SIMULATIONCONTROLLER_COMPILE_DEFS} +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND SIMULATIONCONTROLLER_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(SimulationController PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "SimulationController_static" + ARCHIVE_OUTPUT_NAME_CHECKED "SimulationController_static" + ARCHIVE_OUTPUT_NAME_PROFILE "SimulationController_static" + ARCHIVE_OUTPUT_NAME_RELEASE "SimulationController_static" + ) +ENDIF() + +IF(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(SimulationController PROPERTIES + COMPILE_PDB_NAME_DEBUG "${SIMULATIONCONTROLLER_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${SIMULATIONCONTROLLER_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${SIMULATIONCONTROLLER_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${SIMULATIONCONTROLLER_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${SIMULATIONCONTROLLER_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${SIMULATIONCONTROLLER_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(SimulationController PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt new file mode 100644 index 000000000..b53ca716b --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt @@ -0,0 +1,94 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# We define the CXX flags for this CMakeLists and all others that are included afterwards. This is a GLOBAL setting. +# If/when the solutions go standalone (say, samples and visual tests) - those CMakeLists will need to be fixed. + +STRING(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWERCASE) + +SET(PHYSX_WARNING_DISABLES "-Wno-invalid-offsetof -Wno-maybe-uninitialized -Wno-unused-variable -Wno-variadic-macros -Wno-array-bounds") + +IF(${ANDROID_ABI} STREQUAL "armeabi-v7a") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wpedantic -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -funwind-tables -fomit-frame-pointer -funswitch-loops -finline-limit=300 -fno-strict-aliasing -fstack-protector ${PHYSX_WARNING_DISABLES}" CACHE INTERAL "PhysX CXX") +ELSEIF(${ANDROID_ABI} STREQUAL "armeabi-v7a with NEON") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wpedantic -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -funwind-tables -fomit-frame-pointer -funswitch-loops -finline-limit=300 -fno-strict-aliasing -fstack-protector ${PHYSX_WARNING_DISABLES}" CACHE INTERAL "PhysX CXX") +ELSEIF(${ANDROID_ABI} STREQUAL "arm64-v8a") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wpedantic -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections ${PHYSX_WARNING_DISABLES} " CACHE INTERAL "PhysX CXX") +ELSEIF(${ANDROID_ABI} STREQUAL "x86") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wpedantic -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections ${PHYSX_WARNING_DISABLES} -fpack-struct=8 -malign-double " CACHE INTERAL "PhysX CXX") +ELSEIF(${ANDROID_ABI} STREQUAL "x86_64") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wpedantic -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections ${PHYSX_WARNING_DISABLES} -mstackrealign -msse3 " CACHE INTERAL "PhysX CXX") +ENDIF() + +# Build debug info for all configurations +SET(PHYSX_CXX_FLAGS_DEBUG "-O0 -g" CACHE INTERAL "PhysX Debug CXX Flags") +SET(PHYSX_CXX_FLAGS_CHECKED "-O3 -g" CACHE INTERAL "PhysX Checked CXX Flags") +SET(PHYSX_CXX_FLAGS_PROFILE "-O3 -g" CACHE INTERAL "PhysX Profile CXX Flags") +SET(PHYSX_CXX_FLAGS_RELEASE "-O3 -g" CACHE INTERAL "PhysX Release CXX Flags") + + +# These flags are local to the directory the CMakeLists.txt is in +SET(CMAKE_CXX_FLAGS ${PHYSX_CXX_FLAGS}) + +SET(CMAKE_CXX_FLAGS_DEBUG ${PHYSX_CXX_FLAGS_DEBUG}) +SET(CMAKE_CXX_FLAGS_CHECKED ${PHYSX_CXX_FLAGS_CHECKED}) +SET(CMAKE_CXX_FLAGS_PROFILE ${PHYSX_CXX_FLAGS_PROFILE}) +SET(CMAKE_CXX_FLAGS_RELEASE ${PHYSX_CXX_FLAGS_RELEASE}) + +# Controls PX_NVTX for all projects +SET(NVTX_FLAG "PX_NVTX=0") + +# Disable cuda and dx for all projects on Android +SET(PHYSX_ANDROID_COMPILE_DEFS "__STDC_LIMIT_MACROS;${PHYSX_AUTOBUILD};${PHYSX_AUTOBUILDNUMBER}" CACHE INTERNAL "Base PhysX preprocessor definitions") +SET(PHYSX_ANDROID_DEBUG_COMPILE_DEFS "_DEBUG;PX_DEBUG=1;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Debug PhysX preprocessor definitions") +SET(PHYSX_ANDROID_CHECKED_COMPILE_DEFS "NDEBUG;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Checked PhysX preprocessor definitions") +SET(PHYSX_ANDROID_PROFILE_COMPILE_DEFS "NDEBUG;PX_PROFILE=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Profile PhysX preprocessor definitions") +SET(PHYSX_ANDROID_RELEASE_COMPILE_DEFS "NDEBUG;PX_SUPPORT_PVD=0" CACHE INTERNAL "Release PhysX preprocessor definitions") + +SET(CMAKE_DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}") +SET(CMAKE_PROFILE_POSTFIX "${CMAKE_PROFILE_POSTFIX}") +SET(CMAKE_CHECKED_POSTFIX "${CMAKE_CHECKED_POSTFIX}") +SET(CMAKE_RELEASE_POSTFIX "${CMAKE_RELEASE_POSTFIX}") + + +# Include all of the projects +INCLUDE(PhysXFoundation.cmake) +INCLUDE(LowLevel.cmake) +INCLUDE(LowLevelAABB.cmake) +INCLUDE(LowLevelDynamics.cmake) +INCLUDE(PhysX.cmake) +INCLUDE(PhysXCharacterKinematic.cmake) +INCLUDE(PhysXCommon.cmake) +INCLUDE(PhysXCooking.cmake) +INCLUDE(PhysXExtensions.cmake) +INCLUDE(PhysXVehicle.cmake) +INCLUDE(PhysXPvdSDK.cmake) +INCLUDE(PhysXTask.cmake) +INCLUDE(SceneQuery.cmake) +INCLUDE(SimulationController.cmake) +INCLUDE(FastXml.cmake) + diff --git a/src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake new file mode 100644 index 000000000..7ba5b42da --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake @@ -0,0 +1,42 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml +# + +SET(FASTXML_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(FASTXML_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake new file mode 100644 index 000000000..8190dcae3 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake @@ -0,0 +1,53 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel +# + +SET(LOWLEVEL_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/linux + ${PHYSX_SOURCE_DIR}/LowLevel/software/include/linux + ${PHYSX_SOURCE_DIR}/LowLevelDynamics/include/linux + ${PHYSX_SOURCE_DIR}/LowLevel/common/include/pipeline/linux +) + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVEL_LIBTYPE OBJECT) + +SET(LOWLEVEL_PLATFORM_LINK_FLAGS "/MAP") + + diff --git a/src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake new file mode 100644 index 000000000..8438d7a2a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake @@ -0,0 +1,52 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB +# + + +SET(LOWLEVELAABB_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/linux + ${PHYSX_SOURCE_DIR}/LowLevelAABB/linux/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/src +) + +SET(LOWLEVELAABB_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELAABB_LIBTYPE OBJECT) + +SET(LOWLEVELAABB_PLATFORM_LINK_FLAGS "/MAP") diff --git a/src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake new file mode 100644 index 000000000..3c0478908 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake @@ -0,0 +1,49 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics +# + +SET(LOWLEVELDYNAMICS_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/linux + ${PHYSX_SOURCE_DIR}/lowlevel/software/include/linux + ${PHYSX_SOURCE_DIR}/lowleveldynamics/include/linux + ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline/linux +) + +SET(LOWLEVELDYNAMICS_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELDYNAMICS_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake new file mode 100644 index 000000000..be1c03fa1 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake @@ -0,0 +1,64 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) +# + +SET(PHYSX_PLATFORM_INCLUDES + ${NVTOOLSEXT_INCLUDE_DIRS} +) + +SET(PHYSX_PLATFORM_OBJECT_FILES + $ + $ + $ + $ + $ + $ +) + +SET(PHYSX_PLATFORM_SRC_FILES + ${PX_SOURCE_DIR}/device/linux/PhysXIndicatorLinux.cpp + ${PX_SOURCE_DIR}/gpu/PxGpu.cpp + ${PX_SOURCE_DIR}/gpu/PxPhysXGpuModuleLoader.cpp + + ${PHYSX_PLATFORM_OBJECT_FILES} +) + +SET(PHYSX_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSX_LIBTYPE STATIC) + diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..083c4a81c --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake @@ -0,0 +1,44 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic +# + +SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + + +SET(PHYSXCHARACTERKINEMATIC_LIBTYPE STATIC) + diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake new file mode 100644 index 000000000..33846420f --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake @@ -0,0 +1,51 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon +# + +SET(PHYSXCOMMON_LIBTYPE STATIC) + +SET(PXCOMMON_PLATFORM_SRC_FILES +) + +SET(PXCOMMON_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/linux +) + +SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PXCOMMON_PLATFORM_LINK_FLAGS "/MAP") \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake new file mode 100644 index 000000000..ea13629c4 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake @@ -0,0 +1,47 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking +# + +SET(PHYSXCOOKING_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB;PX_COOKING; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXCOOKING_PLATFORM_SRC_FILES +) + +SET(PHYSXCOOKING_LIBTYPE STATIC) + +SET(PHYSXCOOKING_LINK_FLAGS "/MAP") \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake new file mode 100644 index 000000000..a75161f1b --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake @@ -0,0 +1,54 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions +# + +SET(PHYSXEXTENSIONS_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/linux +) + +SET(PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES + $ +) + +SET(PHYSXEXTENSIONS_PLATFORM_SRC_FILES + ${PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES} +) + +SET(PHYSXEXTENSIONS_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + + diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake new file mode 100644 index 000000000..3a403b252 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake @@ -0,0 +1,87 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation +# + +SET(PHYSXFOUNDATION_LIBTYPE STATIC) + +SET(PXSHARED_PLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/unix/PxUnixIntrinsics.h +) +SOURCE_GROUP(shared\\include\\unix FILES ${PXSHARED_PLATFORM_HEADERS}) + +SET(PHYSXFOUNDATION_PLATFORM_FILES + ${LL_SOURCE_DIR}/src/unix/PsUnixAtomic.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixCpu.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixFPU.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixMutex.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixPrintString.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSList.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSocket.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSync.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixThread.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixTime.cpp + ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c +) +SOURCE_GROUP("src\\src\\unix" FILES ${PHYSXFOUNDATION_PLATFORM_FILES}) + +SET(PHYSXFOUNDATION_PLATFORM_INCLUDES + ${LL_SOURCE_DIR}/include/linux + ${ANDROID_NDK}/sources/android/cpufeatures +) + +SET(PHYSXFOUNDATION_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS}; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/unix/PsUnixInlineAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixTrigConstants.h + ${LL_SOURCE_DIR}/include/unix/PsUnixAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixIntrinsics.h + ${LL_SOURCE_DIR}/include/unix/PsUnixFPU.h +) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS_3 + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonAoS.h + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonInlineAoS.h +) + +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} DESTINATION source/foundation/include/unix) +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS_3} DESTINATION source/foundation/include/unix/neon) +INSTALL(FILES ${PXSHARED_PLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/unix) + + +SET(PHYSXFOUNDATION_PLATFORM_LINKED_LIBS log) diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake new file mode 100644 index 000000000..a38d1580c --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake @@ -0,0 +1,42 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK +# + +SET(PHYSXPVDSDK_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS}; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXPVDSDK_LIBTYPE STATIC) diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake new file mode 100644 index 000000000..350cf7961 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake @@ -0,0 +1,42 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask +# + +SET(PHYSXTASK_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS}; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXTASK_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake new file mode 100644 index 000000000..c3e9bee77 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake @@ -0,0 +1,43 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle +# + +SET(PHYSXVEHICLE_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXVEHICLE_LIBTYPE STATIC) + diff --git a/src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake new file mode 100644 index 000000000..03e10a690 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake @@ -0,0 +1,47 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery +# + +SET(SCENEQUERY_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/linux +) + +SET(SCENEQUERY_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(SCENEQUERY_LIBTYPE OBJECT) + diff --git a/src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake new file mode 100644 index 000000000..36d435d58 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake @@ -0,0 +1,48 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController +# + +SET(SIMULATIONCONTROLLER_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/linux + ${PHYSX_SOURCE_DIR}/LowLevel/linux/include +) + +SET(SIMULATIONCONTROLLER_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(SIMULATIONCONTROLLER_LIBTYPE OBJECT) + diff --git a/src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt new file mode 100644 index 000000000..bcbaf2284 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt @@ -0,0 +1,76 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + + +SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -ferror-limit=0 -Wall -Wextra -Werror -fstrict-aliasing -Wstrict-aliasing=2 -Weverything -Wno-unknown-warning-option -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-undefined-reinterpret-cast -Wno-invalid-offsetof -Wno-zero-as-null-pointer-constant -gdwarf-2" CACHE INTERAL "PhysX CXX") + +SET(CMAKE_SHARED_LINKER_FLAGS_CHECKED "") +SET(CMAKE_SHARED_LINKER_FLAGS_PROFILE "") +SET(CMAKE_SHARED_LINKER_FLAGS "") + +# Build debug info for all configurations +SET(PHYSX_CXX_FLAGS_DEBUG "-O0 -g" CACHE INTERAL "PhysX Debug CXX Flags") +SET(PHYSX_CXX_FLAGS_CHECKED "-O3 -g" CACHE INTERAL "PhysX Checked CXX Flags") +SET(PHYSX_CXX_FLAGS_PROFILE "-O3 -g" CACHE INTERAL "PhysX Profile CXX Flags") +SET(PHYSX_CXX_FLAGS_RELEASE "-O3 -g" CACHE INTERAL "PhysX Release CXX Flags") + +# These flags are local to the directory the CMakeLists.txt is in +SET(CMAKE_CXX_FLAGS ${PHYSX_CXX_FLAGS}) + +SET(CMAKE_CXX_FLAGS_DEBUG ${PHYSX_CXX_FLAGS_DEBUG}) +SET(CMAKE_CXX_FLAGS_CHECKED ${PHYSX_CXX_FLAGS_CHECKED}) +SET(CMAKE_CXX_FLAGS_PROFILE ${PHYSX_CXX_FLAGS_PROFILE}) +SET(CMAKE_CXX_FLAGS_RELEASE ${PHYSX_CXX_FLAGS_RELEASE}) + +# Controls PX_NVTX for all projects +SET(NVTX_FLAG "PX_NVTX=0") + +# Disable cuda and dx for all projects on iOS +SET(PHYSX_IOS_COMPILE_DEFS "DISABLE_CUDA_PHYSX;${PHYSX_AUTOBUILD};${PHYSX_AUTOBUILDNUMBER}" CACHE INTERNAL "Base PhysX preprocessor definitions") +SET(PHYSX_IOS_DEBUG_COMPILE_DEFS "_DEBUG;PX_DEBUG=1;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Debug PhysX preprocessor definitions") +SET(PHYSX_IOS_CHECKED_COMPILE_DEFS "NDEBUG;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Checked PhysX preprocessor definitions") +SET(PHYSX_IOS_PROFILE_COMPILE_DEFS "NDEBUG;PX_PROFILE=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Profile PhysX preprocessor definitions") +SET(PHYSX_IOS_RELEASE_COMPILE_DEFS "NDEBUG;PX_SUPPORT_PVD=0" CACHE INTERNAL "Release PhysX preprocessor definitions") + + +# Include all of the projects +INCLUDE(PhysXFoundation.cmake) +INCLUDE(LowLevel.cmake) +INCLUDE(LowLevelAABB.cmake) +INCLUDE(LowLevelDynamics.cmake) +INCLUDE(PhysX.cmake) +INCLUDE(PhysXCharacterKinematic.cmake) +INCLUDE(PhysXCommon.cmake) +INCLUDE(PhysXCooking.cmake) +INCLUDE(PhysXExtensions.cmake) +INCLUDE(PhysXVehicle.cmake) +INCLUDE(PhysXPvdSDK.cmake) +INCLUDE(PhysXTask.cmake) +INCLUDE(SceneQuery.cmake) +INCLUDE(SimulationController.cmake) +INCLUDE(FastXml.cmake) + diff --git a/src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake new file mode 100644 index 000000000..299dec64b --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake @@ -0,0 +1,44 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml +# + +# Use generator expressions to set config specific preprocessor definitions +SET(FASTXML_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(FASTXML_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake new file mode 100644 index 000000000..9c0d07f0a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake @@ -0,0 +1,51 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel +# + +SET(LOWLEVEL_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/ios + ${PHYSX_SOURCE_DIR}/LowLevel/software/include/ios + ${PHYSX_SOURCE_DIR}/LowLevelDynamics/include/ios + ${PHYSX_SOURCE_DIR}/LowLevel/common/include/pipeline/ios +) + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVEL_PLATFORM_LINK_FLAGS " ") + +SET(LOWLEVEL_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake new file mode 100644 index 000000000..5ba12ba7a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake @@ -0,0 +1,53 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB +# + +SET(LOWLEVELAABB_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/ios + ${PHYSX_SOURCE_DIR}/LowLevelAABB/ios/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/src +) + + +SET(LOWLEVELAABB_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELAABB_PLATFORM_LINK_FLAGS " ") + +SET(LOWLEVELAABB_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake new file mode 100644 index 000000000..f2747d0e0 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake @@ -0,0 +1,52 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics +# + + +SET(LOWLEVELDYNAMICS_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/ios + ${PHYSX_SOURCE_DIR}/lowlevel/software/include/ios + ${PHYSX_SOURCE_DIR}/lowleveldynamics/include/ios + ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline/ios +) + +# Use generator expressions to set config specific preprocessor definitions +SET(LOWLEVELDYNAMICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELDYNAMICS_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake new file mode 100644 index 000000000..fe635d5ec --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake @@ -0,0 +1,68 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) +# + +SET(PHYSX_PLATFORM_INCLUDES +) + +SET(PHYSX_PLATFORM_OBJECT_FILES + $ + $ + $ + $ + $ + $ +) + +SET(PHYSX_PLATFORM_SRC_FILES + ${PX_SOURCE_DIR}/gpu/PxGpu.cpp + ${PX_SOURCE_DIR}/gpu/PxPhysXGpuModuleLoader.cpp + + ${PHYSX_PLATFORM_OBJECT_FILES} +) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSX_COMPILE_DEFS + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_CORE_EXPORTS + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSX_LIBTYPE STATIC) + +SET(PHYSX_PLATFORM_LINK_FLAGS " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_DEBUG " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_CHECKED " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_PROFILE " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_RELEASE " ") diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..af2d60b58 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake @@ -0,0 +1,43 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic +# + + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_CHARACTER_EXPORTS;PX_PHYSX_CORE_EXPORTS;PX_FOUNDATION_DLL=1; + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake new file mode 100644 index 000000000..b19f22570 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake @@ -0,0 +1,52 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon +# + +SET(PHYSXCOMMON_LIBTYPE STATIC) + +SET(PXCOMMON_PLATFORM_SRC_FILES +) + +SET(PXCOMMON_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/ios +) + +SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS; + + # Switch platforms here? + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS}> +) + +SET(PXCOMMON_PLATFORM_LINK_FLAGS " ") \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake new file mode 100644 index 000000000..73e6d26b2 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake @@ -0,0 +1,52 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking +# + +SET(PHYSXCOOKING_COMPILE_DEFS + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_CORE_EXPORTS;PX_COOKING; + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> + +) + +SET(PHYSXCOOKING_PLATFORM_SRC_FILES +) + +SET(PHYSXCOOKING_LIBTYPE STATIC) + +SET(PHYSXCOOKING_LINK_FLAGS " ") +SET(PHYSXCOOKING_LINK_FLAGS_DEBUG " ") +SET(PHYSXCOOKING_LINK_FLAGS_CHECKED " ") +SET(PHYSXCOOKING_LINK_FLAGS_PROFILE " ") +SET(PHYSXCOOKING_LINK_FLAGS_RELEASE " ") \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake new file mode 100644 index 000000000..773941eea --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake @@ -0,0 +1,55 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions +# + +SET(PHYSXEXTENSIONS_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/ios +) + +SET(PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES + $ +) + +SET(PHYSXEXTENSIONS_PLATFORM_SRC_FILES + ${PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES} +) + + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXEXTENSIONS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake new file mode 100644 index 000000000..d047086fa --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake @@ -0,0 +1,91 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation +# + +SET(PHYSXFOUNDATION_LIBTYPE STATIC) + +SET(PXSHARED_PLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/unix/PxUnixIntrinsics.h +) +SOURCE_GROUP(shared\\include\\unix FILES ${PXSHARED_PLATFORM_HEADERS}) + +SET(PHYSXFOUNDATION_PLATFORM_INCLUDES + ${LL_SOURCE_DIR}/include/ios +) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE + ${LL_SOURCE_DIR}/src/unix/PsUnixAtomic.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixCpu.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixFPU.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixMutex.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixPrintString.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSList.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSocket.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSync.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixThread.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixTime.cpp +) + +SET(PHYSXFOUNDATION_NEON_FILES + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonAoS.h + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonInlineAoS.h +) + + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/unix/PsUnixAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixFPU.h + ${LL_SOURCE_DIR}/include/unix/PsUnixInlineAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixIntrinsics.h + ${LL_SOURCE_DIR}/include/unix/PsUnixTrigConstants.h +) +SOURCE_GROUP("src\\include\\unix" FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS}) + +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} DESTINATION source/foundation/include/unix) +INSTALL(FILES ${PHYSXFOUNDATION_NEON_FILES} DESTINATION source/foundation/include/unix/neon) +INSTALL(FILES ${PXSHARED_PLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/unix) + +SET(PHYSXFOUNDATION_PLATFORM_FILES + ${PHYSXFOUNDATION_PLATFORM_SOURCE} + ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} + ${PHYSXFOUNDATION_NEON_FILES} + ${PHYSXFOUNDATION_RESOURCE_FILE} +) + +SET(PHYSXFOUNDATION_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS} + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS}> +) diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake new file mode 100644 index 000000000..ba98d234e --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake @@ -0,0 +1,45 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK +# + +SET(PHYSXPVDSDK_LIBTYPE STATIC) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXPVDSDK_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS} + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS}> +) + diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake new file mode 100644 index 000000000..687e798ac --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake @@ -0,0 +1,41 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask +# + + +SET(PHYSXTASK_COMPILE_DEFS + ${PHYSX_IOS_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXTASK_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake new file mode 100644 index 000000000..e5e5a477e --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake @@ -0,0 +1,42 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle +# + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXVEHICLE_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) diff --git a/src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake new file mode 100644 index 000000000..f00560aa4 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake @@ -0,0 +1,48 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery +# + +SET(SCENEQUERY_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/ios +) + +# Use generator expressions to set config specific preprocessor definitions +SET(SCENEQUERY_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(SCENEQUERY_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake new file mode 100644 index 000000000..bf3309e65 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake @@ -0,0 +1,49 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController +# + +SET(SIMULATIONCONTROLLER_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/ios + ${PHYSX_SOURCE_DIR}/LowLevel/mac/include +) + +# Use generator expressions to set config specific preprocessor definitions +SET(SIMULATIONCONTROLLER_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(SIMULATIONCONTROLLER_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt new file mode 100644 index 000000000..65ba87305 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt @@ -0,0 +1,123 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +STRING(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWERCASE) + +#TODO: Fix warnings +SET(CLANG_WARNINGS "-ferror-limit=0 -Wall -Wextra -Werror -Wstrict-aliasing=2 -Weverything -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-gnu-anonymous-struct -Wno-undef -Wno-unused-function -Wno-nested-anon-types -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-format-nonliteral -Wno-implicit-fallthrough -Wno-undefined-reinterpret-cast -Wno-disabled-macro-expansion -Wno-zero-as-null-pointer-constant -Wno-shadow -Wno-unknown-warning-option") +SET(GCC_WARNINGS "-Wall -Werror -Wno-invalid-offsetof -Wno-uninitialized") + +IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # using Clang + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -fstrict-aliasing ${CLANG_WARNINGS}" CACHE INTERAL "PhysX CXX") +ELSEIF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -fno-strict-aliasing ${GCC_WARNINGS}" CACHE INTERAL "PhysX CXX") +ENDIF() + +# Build debug info for all configurations +SET(PHYSX_CXX_FLAGS_DEBUG "-O0 -g3 -gdwarf-2" CACHE INTERNAL "PhysX Debug CXX Flags") +SET(PHYSX_CXX_FLAGS_CHECKED "-O3 -g3 -gdwarf-2" CACHE INTERNAL "PhysX Checked CXX Flags") +SET(PHYSX_CXX_FLAGS_PROFILE "-O3" CACHE INTERNAL "PhysX Profile CXX Flags") +SET(PHYSX_CXX_FLAGS_RELEASE "-O3" CACHE INTERNAL "PhysX Release CXX Flags") + +# These flags are local to the directory the CMakeLists.txt is in, so don't get carried over to OTHER CMakeLists.txt (thus the CACHE variables above) +SET(CMAKE_CXX_FLAGS ${PHYSX_CXX_FLAGS}) + +SET(CMAKE_CXX_FLAGS_DEBUG ${PHYSX_CXX_FLAGS_DEBUG}) +SET(CMAKE_CXX_FLAGS_CHECKED ${PHYSX_CXX_FLAGS_CHECKED}) +SET(CMAKE_CXX_FLAGS_PROFILE ${PHYSX_CXX_FLAGS_PROFILE}) +SET(CMAKE_CXX_FLAGS_RELEASE ${PHYSX_CXX_FLAGS_RELEASE}) + + +# Controls PX_NVTX for all projects +SET(NVTX_FLAG "PX_NVTX=0") + +IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + SET(CUDA_FLAG "DISABLE_CUDA_PHYSX") +ENDIF() + +SET(PHYSX_LINUX_COMPILE_DEFS "${CUDA_FLAG};${PHYSX_AUTOBUILD};${PHYSX_AUTOBUILDNUMBER}" CACHE INTERNAL "Base PhysX preprocessor definitions") + +SET(PHYSX_LINUX_DEBUG_COMPILE_DEFS "NDEBUG;PX_DEBUG=1;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Debug PhysX preprocessor definitions") +SET(PHYSX_LINUX_CHECKED_COMPILE_DEFS "NDEBUG;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Checked PhysX preprocessor definitions") +SET(PHYSX_LINUX_PROFILE_COMPILE_DEFS "NDEBUG;PX_PROFILE=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Profile PhysX preprocessor definitions") +SET(PHYSX_LINUX_RELEASE_COMPILE_DEFS "NDEBUG;PX_SUPPORT_PVD=0" CACHE INTERNAL "Release PhysX preprocessor definitions") + +SET(GENERATED_GPU_CUDA_FILES "") + +# Include all of the projects +IF(PX_GENERATE_GPU_PROJECTS_ONLY) + INCLUDE(PhysXCommon.cmake) + INCLUDE(PhysXBroadphaseGpu.cmake) + INCLUDE(PhysXCommonGpu.cmake) + INCLUDE(PhysXNarrowphaseGpu.cmake) + INCLUDE(PhysXSimulationControllerGpu.cmake) + INCLUDE(PhysXSolverGpu.cmake) + INCLUDE(CloudGpu.cmake) + INCLUDE(PhysXCudaContextManager.cmake) + INCLUDE(PhysXArticulationGpu.cmake) + INCLUDE(PhysXGpu.cmake) # must be last +ELSE() + INCLUDE(PhysXFoundation.cmake) + INCLUDE(LowLevel.cmake) + INCLUDE(LowLevelAABB.cmake) + INCLUDE(LowLevelDynamics.cmake) + INCLUDE(PhysX.cmake) + INCLUDE(PhysXCharacterKinematic.cmake) + INCLUDE(PhysXCommon.cmake) + INCLUDE(PhysXCooking.cmake) + INCLUDE(PhysXExtensions.cmake) + INCLUDE(PhysXVehicle.cmake) + INCLUDE(SceneQuery.cmake) + INCLUDE(SimulationController.cmake) + INCLUDE(FastXml.cmake) + INCLUDE(PhysXPvdSDK.cmake) + INCLUDE(PhysXTask.cmake) + + SET(PHYSXDISTRO_LIBS PhysXFoundation PhysX PhysXCharacterKinematic PhysXPvdSDK PhysXCommon PhysXCooking PhysXExtensions PhysXVehicle) + + IF(PX_GENERATE_GPU_PROJECTS) + INCLUDE(PhysXBroadphaseGpu.cmake) + INCLUDE(PhysXCommonGpu.cmake) + INCLUDE(PhysXNarrowphaseGpu.cmake) + INCLUDE(PhysXSimulationControllerGpu.cmake) + INCLUDE(PhysXSolverGpu.cmake) + INCLUDE(CloudGpu.cmake) + INCLUDE(PhysXCudaContextManager.cmake) + INCLUDE(PhysXArticulationGpu.cmake) + INCLUDE(PhysXGpu.cmake) # must be last + + LIST(APPEND PHYSXDISTRO_LIBS PhysXGpu) + ENDIF() + + INSTALL( + TARGETS ${PHYSXDISTRO_LIBS} + EXPORT PhysXSDK + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> + ) + +ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake new file mode 100644 index 000000000..e2377741e --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake @@ -0,0 +1,43 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml +# + +# Use generator expressions to set config specific preprocessor definitions +SET(FASTXML_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS}; + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(FASTXML_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake new file mode 100644 index 000000000..20b7eddf9 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake @@ -0,0 +1,67 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel +# + + +SET(LOWLEVEL_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/linux + ${PHYSX_SOURCE_DIR}/LowLevel/software/include/linux + ${PHYSX_SOURCE_DIR}/LowLevelDynamics/include/linux + ${PHYSX_SOURCE_DIR}/LowLevel/common/include/pipeline/linux +) + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB +) + +IF(PX_GENERATE_GPU_STATIC_LIBRARIES) + SET(LOWLEVEL_GPU_LIBTYPE_DEFS + PX_PHYSX_GPU_STATIC; + ) +ENDIF() + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB;${LOWLEVEL_GPU_LIBTYPE_DEFS} + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVEL_PLATFORM_LINK_FLAGS " ") + +SET(LOWLEVEL_LIBTYPE OBJECT) + +# NOTE: Is this a UE4 specific change? +# enable -fPIC so we can link static libs with the editor +#SET_TARGET_PROPERTIES(LowLevel PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake new file mode 100644 index 000000000..5ff3a9592 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake @@ -0,0 +1,53 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB +# + +SET(LOWLEVELAABB_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/linux + ${PHYSX_SOURCE_DIR}/LowLevelAABB/linux/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/src +) + + +SET(LOWLEVELAABB_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELAABB_LIBTYPE OBJECT) + +SET(LOWLEVELAABB_PLATFORM_LINK_FLAGS " ") diff --git a/src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake new file mode 100644 index 000000000..e7cdc1d63 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake @@ -0,0 +1,53 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics +# + + +SET(LOWLEVELDYNAMICS_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/linux + ${PHYSX_SOURCE_DIR}/lowlevel/software/include/linux + ${PHYSX_SOURCE_DIR}/lowleveldynamics/include/linux + ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline/linux +) + +# Use generator expressions to set config specific preprocessor definitions +SET(LOWLEVELDYNAMICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELDYNAMICS_LIBTYPE OBJECT) + diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake new file mode 100644 index 000000000..5ab23e0d2 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake @@ -0,0 +1,111 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) +# + +SET(PHYSX_GPU_HEADERS + ${PHYSX_ROOT_DIR}/include/gpu/PxGpu.h +) +SOURCE_GROUP(include\\gpu FILES ${PHYSX_GPU_HEADERS}) + + +SET(PHYSX_PLATFORM_INCLUDES + ${NVTOOLSEXT_INCLUDE_DIRS} +) + +SET(PHYSX_PLATFORM_OBJECT_FILES + $ + $ + $ + $ + $ + $ +) + +SET(PHYSX_PLATFORM_SRC_FILES + ${PX_SOURCE_DIR}/device/linux/PhysXIndicatorLinux.cpp + ${PX_SOURCE_DIR}/gpu/PxGpu.cpp + ${PX_SOURCE_DIR}/gpu/PxPhysXGpuModuleLoader.cpp + + ${PHYSX_PLATFORM_OBJECT_FILES} +) + +INSTALL(FILES ${PHYSX_GPU_HEADERS} DESTINATION include/gpu) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_LIBTYPE STATIC) + SET(PXPHYSX_LIBTYPE_DEFS + PX_PHYSX_STATIC_LIB; + ) +ELSE() + SET(PHYSX_LIBTYPE SHARED) + SET(PXPHYSX_LIBTYPE_DEFS + PX_PHYSX_CORE_EXPORTS; + ) +ENDIF() + +IF(PX_GENERATE_GPU_STATIC_LIBRARIES) + SET(PXPHYSX_GPU_DEFS PX_PHYSX_GPU_STATIC) +ENDIF() + +# Set default PhysXGpu shared library name +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(BITNESS_STRING "64") + ELSE() + SET(BITNESS_STRING "32") + ENDIF() + SET(PHYSX_GPU_SHARED_LIB_NAME_DEF PX_PHYSX_GPU_SHARED_LIB_NAME=libPhysXGpu_${BITNESS_STRING}.so) +ELSE() + SET(CONFIG_STRING $<$:DEBUG>$<$:CHECKED>$<$:PROFILE>) + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(BITNESS_STRING "x64") + ELSE() + SET(BITNESS_STRING "x86") + ENDIF() + SET(PHYSX_GPU_SHARED_LIB_NAME_DEF PX_PHYSX_GPU_SHARED_LIB_NAME=libPhysXGpu${CONFIG_STRING}_${BITNESS_STRING}.so) +ENDIF() + +SET(PHYSX_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};${PXPHYSX_LIBTYPE_DEFS};${PHYSX_GPU_SHARED_LIB_NAME_DEF};${PXPHYSX_GPU_DEFS} + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSX_PLATFORM_LINK_FLAGS " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_DEBUG " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_CHECKED " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_PROFILE " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_RELEASE " ") + +SET(PHYSX_PLATFORM_LINKED_LIBS dl) diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..daa63e5cd --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake @@ -0,0 +1,49 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic +# + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_CHARACTER_STATIC_LIB;PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXCHARACTERKINEMATIC_LIBTYPE STATIC) + + +# enable -fPIC so we can link static libs with the editor +#SET_TARGET_PROPERTIES(PhysXCharacterKinematic PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake new file mode 100644 index 000000000..6471c8b81 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake @@ -0,0 +1,65 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon +# + +SET(PXCOMMON_PLATFORM_SRC_FILES +) + +SET(PXCOMMON_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/linux +) + + + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PXCOMMON_LIBTYPE_DEFS + PX_PHYSX_STATIC_LIB; + ) + SET(PHYSXCOMMON_LIBTYPE STATIC) +ELSE() + SET(PXCOMMON_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS; + ) + SET(PHYSXCOMMON_LIBTYPE SHARED) +ENDIF() + + +SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};${PXCOMMON_LIBTYPE_DEFS} + + # Switch platforms here? + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS}> +) + +SET(PXCOMMON_PLATFORM_LINK_FLAGS " ") diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake new file mode 100644 index 000000000..5f11a3f5d --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake @@ -0,0 +1,64 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking +# + + +SET(PHYSXCOOKING_PLATFORM_SRC_FILES +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXCOOKING_LIBTYPE STATIC) + SET(PXCOOKING_LIBTYPE_DEFS + PX_PHYSX_STATIC_LIB; + ) +ELSE() + SET(PHYSXCOOKING_LIBTYPE SHARED) + SET(PXCOOKING_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_CORE_EXPORTS + ) +ENDIF() + +SET(PHYSXCOOKING_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_COOKING;${PXCOOKING_LIBTYPE_DEFS} + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> + +) + + +SET(PHYSXCOOKING_LINK_FLAGS " ") +SET(PHYSXCOOKING_LINK_FLAGS_DEBUG " ") +SET(PHYSXCOOKING_LINK_FLAGS_CHECKED " ") +SET(PHYSXCOOKING_LINK_FLAGS_PROFILE " ") +SET(PHYSXCOOKING_LINK_FLAGS_RELEASE " ") diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake new file mode 100644 index 000000000..8e5831592 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake @@ -0,0 +1,56 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions +# + +SET(PHYSXEXTENSIONS_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/linux +) + +SET(PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES + $ +) + +SET(PHYSXEXTENSIONS_PLATFORM_SRC_FILES + ${PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES} +) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXEXTENSIONS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXEXTENSIONS_LIBTYPE STATIC) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake new file mode 100644 index 000000000..b4520e7fd --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake @@ -0,0 +1,110 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation +# + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXFOUNDATION_LIBTYPE STATIC) +ELSE() + SET(PHYSXFOUNDATION_LIBTYPE SHARED) + SET(PHYSXFOUNDATION_PLATFORM_LINKED_LIBS rt) + SET(PXFOUNDATION_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1; + ) +ENDIF() + +SET(PXSHARED_PLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/unix/PxUnixIntrinsics.h +) +SOURCE_GROUP(shared\\include\\unix FILES ${PXSHARED_PLATFORM_HEADERS}) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE + ${LL_SOURCE_DIR}/src/unix/PsUnixAtomic.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixCpu.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixFPU.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixMutex.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixPrintString.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSList.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSocket.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSync.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixThread.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixTime.cpp +) +SOURCE_GROUP("src\\src\\unix" FILES ${PHYSXFOUNDATION_PLATFORM_FILES}) + +SET(PHYSXFOUNDATION_PLATFORM_INCLUDES + ${LL_SOURCE_DIR}/include/linux +) + +SET(PHYSXFOUNDATION_NEON_FILES + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonAoS.h + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonInlineAoS.h +) + +SET(PHYSXFOUNDATION_SSE2_FILES + ${LL_SOURCE_DIR}/include/unix/sse2/PsUnixSse2AoS.h + ${LL_SOURCE_DIR}/include/unix/sse2/PsUnixSse2InlineAoS.h +) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/unix/PsUnixAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixFPU.h + ${LL_SOURCE_DIR}/include/unix/PsUnixInlineAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixIntrinsics.h + ${LL_SOURCE_DIR}/include/unix/PsUnixTrigConstants.h +) +SOURCE_GROUP("src\\include\\unix" FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS}) + +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} DESTINATION source/foundation/include/unix) +INSTALL(FILES ${PHYSXFOUNDATION_NEON_FILES} DESTINATION source/foundation/include/unix/neon) +INSTALL(FILES ${PHYSXFOUNDATION_SSE2_FILES} DESTINATION source/foundation/include/unix/sse2) +INSTALL(FILES ${PXSHARED_PLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/unix) + +SET(PHYSXFOUNDATION_PLATFORM_FILES + ${PHYSXFOUNDATION_PLATFORM_SOURCE} + ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} + ${PHYSXFOUNDATION_NEON_FILES} + ${PHYSXFOUNDATION_SSE2_FILES} + ${PHYSXFOUNDATION_RESOURCE_FILE} +) + + + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXFOUNDATION_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};${PXFOUNDATION_LIBTYPE_DEFS} + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(PXFOUNDATION_PLATFORM_LINK_FLAGS "-m64") diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake new file mode 100644 index 000000000..164ca1f83 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake @@ -0,0 +1,44 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK +# + +SET(PHYSXPVDSDK_LIBTYPE STATIC) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXPVDSDK_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake new file mode 100644 index 000000000..a896acfb4 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake @@ -0,0 +1,45 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask +# + +SET(PHYSXTASK_COMPILE_DEFS + ${PHYSX_LINUX_COMPILE_DEFS}; +) + +SET(PHYSXTASK_COMPILE_DEFS + ${PHYSX_LINUX_COMPILE_DEFS};_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXTASK_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake new file mode 100644 index 000000000..048d2ea02 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake @@ -0,0 +1,47 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle +# + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXVEHICLE_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + + +SET(PHYSXVEHICLE_LIBTYPE STATIC) + + diff --git a/src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake new file mode 100644 index 000000000..6a9540b6a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake @@ -0,0 +1,50 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery +# + +SET(SCENEQUERY_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/linux +) + + +# Use generator expressions to set config specific preprocessor definitions +SET(SCENEQUERY_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(SCENEQUERY_LIBTYPE OBJECT) + diff --git a/src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake new file mode 100644 index 000000000..a15ea443a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake @@ -0,0 +1,51 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController +# + +SET(SIMULATIONCONTROLLER_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/linux + ${PHYSX_SOURCE_DIR}/lowlevel/linux/include +) + +# Use generator expressions to set config specific preprocessor definitions +SET(SIMULATIONCONTROLLER_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(SIMULATIONCONTROLLER_LIBTYPE OBJECT) + + diff --git a/src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt new file mode 100644 index 000000000..9e9618c62 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt @@ -0,0 +1,92 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +OPTION(PX_32BIT "Specifies 32bit compilation flags." OFF) +OPTION(PX_64BIT "Specifies 64bit compilation flags." OFF) + +IF (PX_32BIT) + SET(OSX_BITNESS "-arch i386") +ELSE() + SET(OSX_BITNESS "-arch x86_64") + SET(CMAKE_OSX_ARCHITECTURES "x86_64") +ENDIF() + + +SET(PHYSX_CXX_FLAGS "${OSX_BITNESS} -msse2 -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -Werror -ferror-limit=0 -Wall -Wextra -fstrict-aliasing -Wstrict-aliasing=2 -Weverything -Wno-unknown-warning-option -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-undefined-reinterpret-cast -Wno-invalid-offsetof -Wno-zero-as-null-pointer-constant -gdwarf-2" CACHE INTERAL "PhysX CXX") + +SET(CMAKE_SHARED_LINKER_FLAGS_CHECKED "") +SET(CMAKE_SHARED_LINKER_FLAGS_PROFILE "") +SET(CMAKE_SHARED_LINKER_FLAGS "") + +# Build debug info for all configurations +SET(PHYSX_CXX_FLAGS_DEBUG "-O0 -g" CACHE INTERAL "PhysX Debug CXX Flags") +SET(PHYSX_CXX_FLAGS_CHECKED "-O3 -g" CACHE INTERAL "PhysX Checked CXX Flags") +SET(PHYSX_CXX_FLAGS_PROFILE "-O3 -g" CACHE INTERAL "PhysX Profile CXX Flags") +SET(PHYSX_CXX_FLAGS_RELEASE "-O3 -g" CACHE INTERAL "PhysX Release CXX Flags") + +# These flags are local to the directory the CMakeLists.txt is in +SET(CMAKE_CXX_FLAGS ${PHYSX_CXX_FLAGS}) + +SET(CMAKE_CXX_FLAGS_DEBUG ${PHYSX_CXX_FLAGS_DEBUG}) +SET(CMAKE_CXX_FLAGS_CHECKED ${PHYSX_CXX_FLAGS_CHECKED}) +SET(CMAKE_CXX_FLAGS_PROFILE ${PHYSX_CXX_FLAGS_PROFILE}) +SET(CMAKE_CXX_FLAGS_RELEASE ${PHYSX_CXX_FLAGS_RELEASE}) + +# Build libs compatible with OS X 10.9 +SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.9") + +#set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym") + +# Controls PX_NVTX for all projects on Mac +SET(NVTX_FLAG "PX_NVTX=0") + +# Disable cuda for all projects +SET(PHYSX_MAC_COMPILE_DEFS "DISABLE_CUDA_PHYSX;${PHYSX_AUTOBUILD};${PHYSX_AUTOBUILDNUMBER}" CACHE INTERNAL "Base PhysX preprocessor definitions") +SET(PHYSX_MAC_DEBUG_COMPILE_DEFS "_DEBUG;PX_DEBUG=1;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Debug PhysX preprocessor definitions") +SET(PHYSX_MAC_CHECKED_COMPILE_DEFS "NDEBUG;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Checked PhysX preprocessor definitions") +SET(PHYSX_MAC_PROFILE_COMPILE_DEFS "NDEBUG;PX_PROFILE=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Profile PhysX preprocessor definitions") +SET(PHYSX_MAC_RELEASE_COMPILE_DEFS "NDEBUG;PX_SUPPORT_PVD=0" CACHE INTERNAL "Release PhysX preprocessor definitions") + + +# Include all of the projects +INCLUDE(PhysXFoundation.cmake) +INCLUDE(LowLevel.cmake) +INCLUDE(LowLevelAABB.cmake) +INCLUDE(LowLevelDynamics.cmake) +INCLUDE(PhysX.cmake) +INCLUDE(PhysXCharacterKinematic.cmake) +INCLUDE(PhysXCommon.cmake) +INCLUDE(PhysXCooking.cmake) +INCLUDE(PhysXExtensions.cmake) +INCLUDE(PhysXVehicle.cmake) +INCLUDE(PhysXPvdSDK.cmake) +INCLUDE(PhysXTask.cmake) +INCLUDE(SceneQuery.cmake) +INCLUDE(SimulationController.cmake) +INCLUDE(FastXml.cmake) + + diff --git a/src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake new file mode 100644 index 000000000..ac4c4d53e --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake @@ -0,0 +1,45 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml +# + + +# Use generator expressions to set config specific preprocessor definitions +SET(FASTXML_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(FASTXML_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake new file mode 100644 index 000000000..b314a2f0c --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake @@ -0,0 +1,51 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel +# +SET(LOWLEVEL_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/mac + ${PHYSX_SOURCE_DIR}/LowLevel/software/include/mac + ${PHYSX_SOURCE_DIR}/LowLevelDynamics/include/mac + ${PHYSX_SOURCE_DIR}/LowLevel/common/include/pipeline/mac +) + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVEL_PLATFORM_LINK_FLAGS " ") + +SET(LOWLEVEL_LIBTYPE OBJECT) + diff --git a/src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake new file mode 100644 index 000000000..fbec014b1 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake @@ -0,0 +1,53 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB +# + +SET(LOWLEVELAABB_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/mac + ${PHYSX_SOURCE_DIR}/LowLevelAABB/mac/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/src +) + + +SET(LOWLEVELAABB_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELAABB_PLATFORM_LINK_FLAGS " ") + +SET(LOWLEVELAABB_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake new file mode 100644 index 000000000..9069816b2 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake @@ -0,0 +1,50 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics +# +SET(LOWLEVELDYNAMICS_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/mac + ${PHYSX_SOURCE_DIR}/lowlevel/software/include/mac + ${PHYSX_SOURCE_DIR}/lowleveldynamics/include/mac + ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline/mac +) + +# Use generator expressions to set config specific preprocessor definitions +SET(LOWLEVELDYNAMICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELDYNAMICS_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake new file mode 100644 index 000000000..731c18224 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake @@ -0,0 +1,72 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) +# + +SET(PHYSX_PLATFORM_INCLUDES +) + +SET(PHYSX_PLATFORM_OBJECT_FILES + $ + $ + $ + $ + $ + $ +) + +SET(PHYSX_PLATFORM_SRC_FILES + ${PX_SOURCE_DIR}/gpu/PxGpu.cpp + ${PX_SOURCE_DIR}/gpu/PxPhysXGpuModuleLoader.cpp + + ${PHYSX_PLATFORM_OBJECT_FILES} +) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSX_COMPILE_DEFS + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_CORE_EXPORTS + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_LIBTYPE STATIC) +ELSE() + SET(PHYSX_LIBTYPE SHARED) +ENDIF() + +SET(PHYSX_PLATFORM_LINK_FLAGS " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_DEBUG " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_CHECKED " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_PROFILE " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_RELEASE " ") diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..055eb7911 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake @@ -0,0 +1,41 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic +# +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_CHARACTER_EXPORTS;PX_PHYSX_CORE_EXPORTS; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake new file mode 100644 index 000000000..739565130 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake @@ -0,0 +1,70 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon +# + + +SET(PXCOMMON_PLATFORM_SRC_FILES +) + +SET(PXCOMMON_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/mac +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS}> + ) +ELSE() + SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_COMMON_EXPORTS; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS}> + ) +ENDIF() + + + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXCOMMON_LIBTYPE STATIC) +ELSE() + SET(PHYSXCOMMON_LIBTYPE SHARED) +ENDIF() + +SET(PXCOMMON_PLATFORM_LINK_FLAGS " ") diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake new file mode 100644 index 000000000..9e882ef83 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake @@ -0,0 +1,57 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking +# + +SET(PHYSXCOOKING_COMPILE_DEFS + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_CORE_EXPORTS;PX_COOKING; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> + +) + +SET(PHYSXCOOKING_PLATFORM_SRC_FILES +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXCOOKING_LIBTYPE STATIC) +ELSE() + SET(PHYSXCOOKING_LIBTYPE SHARED) +ENDIF() + + +SET(PHYSXCOOKING_LINK_FLAGS " ") +SET(PHYSXCOOKING_LINK_FLAGS_DEBUG " ") +SET(PHYSXCOOKING_LINK_FLAGS_CHECKED " ") +SET(PHYSXCOOKING_LINK_FLAGS_PROFILE " ") +SET(PHYSXCOOKING_LINK_FLAGS_RELEASE " ") diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake new file mode 100644 index 000000000..b4c15ac27 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake @@ -0,0 +1,55 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions +# + +SET(PHYSXEXTENSIONS_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/mac +) + +SET(PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES + $ +) + +SET(PHYSXEXTENSIONS_PLATFORM_SRC_FILES + ${PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES} +) + + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXEXTENSIONS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake new file mode 100644 index 000000000..256293a02 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake @@ -0,0 +1,102 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation +# + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXFOUNDATION_LIBTYPE STATIC) +ELSE() + SET(PHYSXFOUNDATION_LIBTYPE SHARED) + SET(PHYSXFOUNDATION_SHARED_LIBRARY_DEFS PX_FOUNDATION_DLL=1;) +ENDIF() + +SET(PXSHARED_PLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/unix/PxUnixIntrinsics.h +) +SOURCE_GROUP(shared\\include\\unix FILES ${PXSHARED_PLATFORM_HEADERS}) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE + ${LL_SOURCE_DIR}/src/unix/PsUnixAtomic.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixCpu.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixFPU.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixMutex.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixPrintString.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSList.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSocket.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSync.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixThread.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixTime.cpp +) + +SET(PHYSXFOUNDATION_PLATFORM_INCLUDES + ${LL_SOURCE_DIR}/include/linux +) + +SET(PHYSXFOUNDATION_NEON_FILES + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonAoS.h + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonInlineAoS.h +) + +SET(PHYSXFOUNDATION_SSE2_FILES + ${LL_SOURCE_DIR}/include/unix/sse2/PsUnixSse2AoS.h + ${LL_SOURCE_DIR}/include/unix/sse2/PsUnixSse2InlineAoS.h +) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/unix/PsUnixAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixFPU.h + ${LL_SOURCE_DIR}/include/unix/PsUnixInlineAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixIntrinsics.h + ${LL_SOURCE_DIR}/include/unix/PsUnixTrigConstants.h +) +SOURCE_GROUP("src\\include\\unix" FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS}) + +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} DESTINATION source/foundation/include/unix) +INSTALL(FILES ${PHYSXFOUNDATION_NEON_FILES} DESTINATION source/foundation/include/unix/neon) +INSTALL(FILES ${PHYSXFOUNDATION_SSE2_FILES} DESTINATION source/foundation/include/unix/sse2) +INSTALL(FILES ${PXSHARED_PLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/unix) + +SET(PHYSXFOUNDATION_PLATFORM_FILES + ${PHYSXFOUNDATION_PLATFORM_SOURCE} + ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} + ${PHYSXFOUNDATION_NEON_FILES} + ${PHYSXFOUNDATION_SSE2_FILES} + ${PHYSXFOUNDATION_RESOURCE_FILE} +) + +SET(PHYSXFOUNDATION_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS} + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS}> +) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake new file mode 100644 index 000000000..1f0506c85 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake @@ -0,0 +1,45 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK +# + +SET(PHYSXPVDSDK_LIBTYPE STATIC) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXPVDSDK_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS} + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS}> +) + diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake new file mode 100644 index 000000000..ec71f0c54 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake @@ -0,0 +1,41 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask +# + + +SET(PHYSXTASK_COMPILE_DEFS + ${PHYSX_MAC_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXTASK_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake new file mode 100644 index 000000000..c81d171e3 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake @@ -0,0 +1,45 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle +# + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXVEHICLE_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + + +SET(PHYSXVEHICLE_LIBTYPE STATIC) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake new file mode 100644 index 000000000..6222fe3a0 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake @@ -0,0 +1,48 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery +# + +SET(SCENEQUERY_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/mac +) + +# Use generator expressions to set config specific preprocessor definitions +SET(SCENEQUERY_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(SCENEQUERY_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake new file mode 100644 index 000000000..6c811d569 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake @@ -0,0 +1,49 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController +# + +SET(SIMULATIONCONTROLLER_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/mac + ${PHYSX_SOURCE_DIR}/LowLevel/mac/include +) + +# Use generator expressions to set config specific preprocessor definitions +SET(SIMULATIONCONTROLLER_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(SIMULATIONCONTROLLER_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt new file mode 100644 index 000000000..db4d0457d --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt @@ -0,0 +1,216 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +OPTION(PX_COPY_EXTERNAL_DLL "Copy external dlls into SDK bin directory" OFF) +OPTION(PX_FLOAT_POINT_PRECISE_MATH "Float point precise math" OFF) +OPTION(PX_USE_NVTX "Enabled NVTX profiling" OFF) + +# We define the CXX flags for this CMakeLists and all others that are included afterwards. This is a GLOBAL setting. +# If/when the solutions go standalone (say, samples and visual tests) - those CMakeLists will need to be fixed. + +SET(PHYSX_WARNING_DISABLES "/wd4514 /wd4820 /wd4127 /wd4710 /wd4711 /wd4577 /wd4996") + +# Cache the CXX flags so the other CMakeLists.txt can use them if needed +IF(PX_FLOAT_POINT_PRECISE_MATH) + SET(PHYSX_FP_MODE "/fp:precise") +ELSE() + SET(PHYSX_FP_MODE "/fp:fast") +ENDIF() +IF(CMAKE_CL_64) + SET(PHYSX_CXX_FLAGS "/Wall /d2Zi+ /MP /WX /W4 /GF /GS- /GR- /Gd ${PHYSX_FP_MODE} /Oy ${PHYSX_WARNING_DISABLES}" CACHE INTERNAL "PhysX CXX") +ELSE() + SET(PHYSX_CXX_FLAGS "/Wall /arch:SSE2 /d2Zi+ /MP /WX /W4 /GF /GS- /GR- /Gd ${PHYSX_FP_MODE} /Oy ${PHYSX_WARNING_DISABLES}" CACHE INTERNAL "PhysX CXX") +ENDIF() + +SET(PHYSX_CXX_FLAGS_DEBUG "/Od ${WINCRT_DEBUG} /RTCu /Zi" CACHE INTERNAL "PhysX Debug CXX Flags") +SET(PHYSX_CXX_FLAGS_CHECKED "/Ox ${WINCRT_NDEBUG} /Zi" CACHE INTERNAL "PhysX Checked CXX Flags") +SET(PHYSX_CXX_FLAGS_PROFILE "/Ox ${WINCRT_NDEBUG} /Zi" CACHE INTERNAL "PhysX Profile CXX Flags") +SET(PHYSX_CXX_FLAGS_RELEASE "/Ox ${WINCRT_NDEBUG} /Zi" CACHE INTERNAL "PhysX Release CXX Flags") + +# cache lib type defs +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_LIBTYPE_DEFS "PX_PHYSX_STATIC_LIB;PX_FOUNDATION_DLL=0;" CACHE INTERNAL "PhysX lib type defs") +ENDIF() +IF(PX_GENERATE_GPU_STATIC_LIBRARIES) + SET(PHYSXGPU_LIBTYPE_DEFS "PX_PHYSX_GPU_STATIC;" CACHE INTERNAL "PhysX GPU lib type defs") +ENDIF() + +SET(CUDA_SUPPRESS_WARNINGS "-Xptxas -w -Wno-deprecated-declarations -ftemplate-backtrace-limit=2") + +# These flags are local to the directory the CMakeLists.txt is in, so don't get carried over to OTHER CMakeLists.txt (thus the CACHE variables above) +SET(CMAKE_CXX_FLAGS ${PHYSX_CXX_FLAGS}) + +SET(CMAKE_CXX_FLAGS_DEBUG ${PHYSX_CXX_FLAGS_DEBUG}) +SET(CMAKE_CXX_FLAGS_CHECKED ${PHYSX_CXX_FLAGS_CHECKED}) +SET(CMAKE_CXX_FLAGS_PROFILE ${PHYSX_CXX_FLAGS_PROFILE}) +SET(CMAKE_CXX_FLAGS_RELEASE ${PHYSX_CXX_FLAGS_RELEASE}) + +# Build PDBs for all configurations +SET(CMAKE_SHARED_LINKER_FLAGS "/DEBUG /INCREMENTAL:NO") +SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG "/DEBUG /INCREMENTAL:NO") +SET(CMAKE_SHARED_LINKER_FLAGS_CHECKED "/DEBUG /INCREMENTAL:NO") +SET(CMAKE_SHARED_LINKER_FLAGS_PROFILE "/DEBUG /INCREMENTAL:NO") +SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "/DEBUG /INCREMENTAL:NO") + +IF(CMAKE_CL_64) + SET(WIN64_FLAG "WIN64") +ENDIF(CMAKE_CL_64) + +IF(PX_SCALAR_MATH) + SET(SCALAR_MATH_FLAG "PX_SIMD_DISABLED") +ENDIF() + +# Controls PX_NVTX for all projects +IF(PX_USE_NVTX) + SET(NVTX_FLAG "PX_NVTX=1") +ELSE() + SET(NVTX_FLAG "PX_NVTX=0") +ENDIF() + +SET(PHYSX_WINDOWS_COMPILE_DEFS "WIN32;${WIN64_FLAG};${SCALAR_MATH_FLAG};${CUDA_FLAG};_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;${PHYSX_AUTOBUILD};${PHYSX_AUTOBUILDNUMBER}" CACHE INTERNAL "Base PhysX preprocessor definitions") + +SET(PHYSX_WINDOWS_DEBUG_COMPILE_DEFS "PX_DEBUG=1;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Debug PhysX preprocessor definitions") +SET(PHYSX_WINDOWS_CHECKED_COMPILE_DEFS "PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Checked PhysX preprocessor definitions") +SET(PHYSX_WINDOWS_PROFILE_COMPILE_DEFS "PX_PROFILE=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Profile PhysX preprocessor definitions") +SET(PHYSX_WINDOWS_RELEASE_COMPILE_DEFS "PX_SUPPORT_PVD=0" CACHE INTERNAL "Release PhysX preprocessor definitions") + +# copy the external dlls +IF(PX_COPY_EXTERNAL_DLL) + IF(NOT PHYSX_SLN_PHYSXDEVICE_PATH) + SET(PHYSX_SLN_PHYSXDEVICE_PATH "$ENV{PM_PhysXDevice_PATH}/bin/x86/" CACHE INTERNAL "PhysX device copy path") + ENDIF() + + IF(NOT PHYSX_SLN_GLUT32_PATH) + SET(PHYSX_SLN_GLUT32_PATH "$ENV{PM_glut32_PATH}/bin/" CACHE INTERNAL "PhysX glut32 copy path") + ENDIF() + + IF(CMAKE_CL_64) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x64/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x64/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x64/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x64/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + ELSE() + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x86/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x86/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x86/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x86/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + ENDIF() +ENDIF() +# for public release we copy the dlls for GPU other platforms so we dont keep duplicates around the repository +IF(PUBLIC_RELEASE) + IF(NOT GPU_DLL_COPIED) + SET(GPU_DLL_COPIED 1 CACHE INTERNAL "PhysX GPU dlls copied") + IF(CMAKE_CL_64) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_64.vc140.mt/debug/PhysXGpu_64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_64.vc140.mt/checked/PhysXGpu_64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_64.vc140.mt/profile/PhysXGpu_64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_64.vc140.mt/release/PhysXGpu_64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + ELSE() + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_32.vc120.mt/debug/PhysXGpu_32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_32.vc120.mt/checked/PhysXGpu_32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_32.vc120.mt/profile/PhysXGpu_32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_32.vc120.mt/release/PhysXGpu_32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + ENDIF() + ENDIF() +ENDIF() + +SET(GENERATED_GPU_CUDA_FILES "") + +# Include all of the projects +IF(PX_GENERATE_GPU_PROJECTS_ONLY) + INCLUDE(PhysXFoundation.cmake) + INCLUDE(PhysXCommon.cmake) + INCLUDE(PhysXBroadphaseGpu.cmake) + INCLUDE(PhysXCommonGpu.cmake) + INCLUDE(PhysXNarrowphaseGpu.cmake) + INCLUDE(PhysXSimulationControllerGpu.cmake) + INCLUDE(PhysXSolverGpu.cmake) + INCLUDE(CloudGpu.cmake) + INCLUDE(PhysXCudaContextManager.cmake) + INCLUDE(PhysXArticulationGpu.cmake) + INCLUDE(PhysXGpu.cmake) # must be the last PhysXGPU + + SET(PHYSXDISTRO_LIBS PhysXGpu) + INSTALL( + TARGETS ${PHYSXDISTRO_LIBS} + EXPORT PhysXSDK + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> + ) +ELSE() + INCLUDE(PhysXFoundation.cmake) + INCLUDE(LowLevel.cmake) + INCLUDE(LowLevelAABB.cmake) + INCLUDE(LowLevelDynamics.cmake) + INCLUDE(PhysX.cmake) + INCLUDE(PhysXCharacterKinematic.cmake) + INCLUDE(PhysXCommon.cmake) + INCLUDE(PhysXCooking.cmake) + INCLUDE(PhysXExtensions.cmake) + INCLUDE(PhysXVehicle.cmake) + INCLUDE(SceneQuery.cmake) + INCLUDE(SimulationController.cmake) + INCLUDE(FastXml.cmake) + INCLUDE(PhysXPvdSDK.cmake) + INCLUDE(PhysXTask.cmake) + + IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXDISTRO_LIBS PhysXFoundation PhysX PhysXCharacterKinematic PhysXPvdSDK PhysXCommon PhysXCooking PhysXExtensions PhysXVehicle) + ELSE() + SET(PHYSXDISTRO_LIBS PhysXFoundation PhysX PhysXCharacterKinematic PhysXPvdSDK PhysXCommon PhysXCooking PhysXExtensions PhysXVehicle PhysXTask) + ENDIF() + + IF(PX_GENERATE_GPU_PROJECTS) + INCLUDE(PhysXBroadphaseGpu.cmake) + INCLUDE(PhysXCommonGpu.cmake) + INCLUDE(PhysXNarrowphaseGpu.cmake) + INCLUDE(PhysXSimulationControllerGpu.cmake) + INCLUDE(PhysXSolverGpu.cmake) + INCLUDE(CloudGpu.cmake) + INCLUDE(PhysXCudaContextManager.cmake) + INCLUDE(PhysXArticulationGpu.cmake) + INCLUDE(PhysXGpu.cmake) # must be the last PhysXGPU + + LIST(APPEND PHYSXDISTRO_LIBS PhysXGpu) + ENDIF() + + INSTALL( + TARGETS ${PHYSXDISTRO_LIBS} + EXPORT PhysXSDK + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> + ) + +ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake new file mode 100644 index 000000000..da1153c3c --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake @@ -0,0 +1,55 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml +# + +# Use generator expressions to set config specific preprocessor definitions +SET(FASTXML_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +SET(FASTXML_LIBTYPE STATIC) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND FASTXML_LIBTYPE STREQUAL "STATIC") + SET(FASTXML_COMPILE_PDB_NAME_DEBUG "FastXml_static${CMAKE_DEBUG_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_CHECKED "FastXml_static${CMAKE_CHECKED_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_PROFILE "FastXml_static${CMAKE_PROFILE_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_RELEASE "FastXml_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(FASTXML_COMPILE_PDB_NAME_DEBUG "FastXml${CMAKE_DEBUG_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_CHECKED "FastXml${CMAKE_CHECKED_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_PROFILE "FastXml${CMAKE_PROFILE_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_RELEASE "FastXml${CMAKE_RELEASE_POSTFIX}") +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake new file mode 100644 index 000000000..7b037d3a6 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake @@ -0,0 +1,82 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel +# + +SET(LOWLEVEL_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/windows + ${PHYSX_SOURCE_DIR}/LowLevel/software/include/windows + ${PHYSX_SOURCE_DIR}/LowLevelDynamics/include/windows + ${PHYSX_SOURCE_DIR}/LowLevel/common/include/pipeline/windows +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(LOWLEVEL_LIBTYPE OBJECT) +ELSE() + SET(LOWLEVEL_LIBTYPE STATIC) +ENDIF() + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND LOWLEVEL_LIBTYPE STREQUAL "STATIC") + SET(LL_COMPILE_PDB_NAME_DEBUG "LowLevel_static${CMAKE_DEBUG_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_CHECKED "LowLevel_static${CMAKE_CHECKED_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_PROFILE "LowLevel_static${CMAKE_PROFILE_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_RELEASE "LowLevel_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(LL_COMPILE_PDB_NAME_DEBUG "LowLevel${CMAKE_DEBUG_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_CHECKED "LowLevel${CMAKE_CHECKED_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_PROFILE "LowLevel${CMAKE_PROFILE_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_RELEASE "LowLevel${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(LOWLEVEL_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${LL_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVEL_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${LL_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVEL_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${LL_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVEL_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${LL_COMPILE_PDB_NAME_RELEASE}") + + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${LL_COMPILE_PDB_NAME_DEBUG}>$<$:${LL_COMPILE_PDB_NAME_CHECKED}>$<$:${LL_COMPILE_PDB_NAME_PROFILE}>$<$:${LL_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + SET(LOWLEVEL_COMPILE_PDB_NAME_DEBUG "${LL_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVEL_COMPILE_PDB_NAME_CHECKED "${LL_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVEL_COMPILE_PDB_NAME_PROFILE "${LL_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVEL_COMPILE_PDB_NAME_RELEASE "${LL_COMPILE_PDB_NAME_RELEASE}") +ENDIF() + +SET(LOWLEVEL_PLATFORM_LINK_FLAGS "/MAP") diff --git a/src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake new file mode 100644 index 000000000..2efd43b8b --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake @@ -0,0 +1,84 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB +# + +SET(LOWLEVELAABB_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/windows + ${PHYSX_SOURCE_DIR}/LowLevelAABB/windows/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/src +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(LOWLEVELAABB_LIBTYPE OBJECT) +ELSE() + SET(LOWLEVELAABB_LIBTYPE STATIC) +ENDIF() + +SET(LOWLEVELAABB_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND LOWLEVELAABB_LIBTYPE STREQUAL "STATIC") + SET(LLAABB_COMPILE_PDB_NAME_DEBUG "LowLevelAABB_static${CMAKE_DEBUG_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_CHECKED "LowLevelAABB_static${CMAKE_CHECKED_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_PROFILE "LowLevelAABB_static${CMAKE_PROFILE_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_RELEASE "LowLevelAABB_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(LLAABB_COMPILE_PDB_NAME_DEBUG "LowLevelAABB${CMAKE_DEBUG_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_CHECKED "LowLevelAABB${CMAKE_CHECKED_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_PROFILE "LowLevelAABB${CMAKE_PROFILE_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_RELEASE "LowLevelAABB${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(LOWLEVELAABB_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${LLAABB_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${LLAABB_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${LLAABB_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${LLAABB_COMPILE_PDB_NAME_RELEASE}") + + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${LLAABB_COMPILE_PDB_NAME_DEBUG}>$<$:${LLAABB_COMPILE_PDB_NAME_CHECKED}>$<$:${LLAABB_COMPILE_PDB_NAME_PROFILE}>$<$:${LLAABB_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + SET(LOWLEVELAABB_COMPILE_PDB_NAME_DEBUG "${LLAABB_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_CHECKED "${LLAABB_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_PROFILE "${LLAABB_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_RELEASE "${LLAABB_COMPILE_PDB_NAME_RELEASE}") +ENDIF() + +SET(LOWLEVELAABB_PLATFORM_LINK_FLAGS "/MAP") + diff --git a/src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake new file mode 100644 index 000000000..facc9890d --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake @@ -0,0 +1,83 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics +# + +SET(LOWLEVELDYNAMICS_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/windows + ${PHYSX_SOURCE_DIR}/lowlevel/software/include/windows + ${PHYSX_SOURCE_DIR}/lowleveldynamics/include/windows + ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline/windows +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(LOWLEVELDYNAMICS_LIBTYPE OBJECT) +ELSE() + SET(LOWLEVELDYNAMICS_LIBTYPE STATIC) +ENDIF() + +# Use generator expressions to set config specific preprocessor definitions +SET(LOWLEVELDYNAMICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND LOWLEVELDYNAMICS_LIBTYPE STREQUAL "STATIC") + SET(LLDYNAMICS_COMPILE_PDB_NAME_DEBUG "LowLevelDynamics_static${CMAKE_DEBUG_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_CHECKED "LowLevelDynamics_static${CMAKE_CHECKED_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_PROFILE "LowLevelDynamics_static${CMAKE_PROFILE_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_RELEASE "LowLevelDynamics_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(LLDYNAMICS_COMPILE_PDB_NAME_DEBUG "LowLevelDynamics${CMAKE_DEBUG_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_CHECKED "LowLevelDynamics${CMAKE_CHECKED_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_PROFILE "LowLevelDynamics${CMAKE_PROFILE_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_RELEASE "LowLevelDynamics${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${LLDYNAMICS_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${LLDYNAMICS_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${LLDYNAMICS_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${LLDYNAMICS_COMPILE_PDB_NAME_RELEASE}") + + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${LLDYNAMICS_COMPILE_PDB_NAME_DEBUG}>$<$:${LLDYNAMICS_COMPILE_PDB_NAME_CHECKED}>$<$:${LLDYNAMICS_COMPILE_PDB_NAME_PROFILE}>$<$:${LLDYNAMICS_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_DEBUG "${LLDYNAMICS_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_CHECKED "${LLDYNAMICS_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_PROFILE "${LLDYNAMICS_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_RELEASE "${LLDYNAMICS_COMPILE_PDB_NAME_RELEASE}") +ENDIF() + diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake new file mode 100644 index 000000000..aada387ab --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake @@ -0,0 +1,169 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) +# + +IF(PX_USE_NVTX) + FIND_PACKAGE(nvToolsExt $ENV{PM_nvToolsExt_VERSION} REQUIRED) + MESSAGE("Using nvtx lib: ${NVTOOLSEXT_LIB} path: ${NVTOOLSEXTSDK_PATH}") + SET(NV_TOOLS_EXT_LIB ${NVTOOLSEXT_LIB}) +ENDIF() + +SET(PHYSX_PLATFORM_INCLUDES + ${NVTOOLSEXT_INCLUDE_DIRS} +) + +SET(PHYSX_GPU_HEADERS + ${PHYSX_ROOT_DIR}/include/gpu/PxGpu.h +) +SOURCE_GROUP(include\\gpu FILES ${PHYSX_GPU_HEADERS}) + +SET(PHYSX_COMMON_WINDOWS_HEADERS + ${PHYSX_ROOT_DIR}/include/common/windows/PxWindowsDelayLoadHook.h +) +SOURCE_GROUP(include\\gpu FILES ${PHYSX_GPU_HEADERS}) + +SET(PHYSX_RESOURCE + ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/PhysX.rc + ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/resource.h +) +SOURCE_GROUP(resource FILES ${PHYSX_RESOURCE}) + +SET(PHYSX_DEVICE_SOURCE + ${PX_SOURCE_DIR}/device/nvPhysXtoDrv.h + ${PX_SOURCE_DIR}/device/PhysXIndicator.h + ${PX_SOURCE_DIR}/device/windows/PhysXIndicatorWindows.cpp +) +SOURCE_GROUP(src\\device FILES ${PHYSX_DEVICE_SOURCE}) + +SET(PHYSX_GPU_SOURCE + ${PX_SOURCE_DIR}/gpu/PxGpu.cpp + ${PX_SOURCE_DIR}/gpu/PxPhysXGpuModuleLoader.cpp +) +SOURCE_GROUP(src\\gpu FILES ${PHYSX_GPU_SOURCE}) + +SET(PHYSX_WINDOWS_SOURCE + ${PX_SOURCE_DIR}/windows/NpWindowsDelayLoadHook.cpp +) +SOURCE_GROUP(src\\windows FILES ${PHYSX_WINDOWS_SOURCE}) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_PLATFORM_OBJECT_FILES + $ + $ + $ + $ + $ + $ + ) +ENDIF() + +SET(PHYSX_PLATFORM_SRC_FILES + ${PHYSX_GPU_HEADERS} + ${PHYSX_RESOURCE} + ${PHYSX_COMMON_WINDOWS_HEADERS} + + ${PHYSX_DEVICE_SOURCE} + ${PHYSX_GPU_SOURCE} + + ${PHYSX_WINDOWS_SOURCE} + + ${PHYSX_PLATFORM_OBJECT_FILES} +) + +INSTALL(FILES ${PHYSX_GPU_HEADERS} DESTINATION include/gpu) +INSTALL(FILES ${PHYSX_COMMON_WINDOWS_HEADERS} DESTINATION include/common/windows) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) + SET(BITNESS_STRING $<$:64>$<$:32>) + SET(PHYSX_GPU_SHARED_LIB_NAME_DEF PX_PHYSX_GPU_SHARED_LIB_NAME=PhysXGpu_${BITNESS_STRING}.dll) +ELSE() + SET(CONFIG_STRING $<$:DEBUG>$<$:CHECKED>$<$:PROFILE>) + SET(BITNESS_STRING $<$:x64>$<$:x86>) + SET(PHYSX_GPU_SHARED_LIB_NAME_DEF PX_PHYSX_GPU_SHARED_LIB_NAME=PhysXGpu${CONFIG_STRING}_${BITNESS_STRING}.dll) +ENDIF() + +IF(NOT PX_GENERATE_STATIC_LIBRARIES) + SET(PXPHYSX_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1;PX_PHYSX_CORE_EXPORTS; + ) +ENDIF() + +SET(PHYSX_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PXPHYSX_LIBTYPE_DEFS};${PHYSX_GPU_SHARED_LIB_NAME_DEF};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_LIBTYPE STATIC) +ELSE() + SET(PHYSX_LIBTYPE SHARED) +ENDIF() + +IF(PX_GENERATE_GPU_PROJECTS) + SET(PHYSX_PLATFORM_LINK_FLAGS "/DELAYLOAD:nvcuda.dll") +ENDIF() + +IF(NOT PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_PRIVATE_PLATFORM_LINKED_LIBS + LowLevel LowLevelAABB LowLevelDynamics PhysXTask SceneQuery SimulationController ${NV_TOOLS_EXT_LIB} + ) +ENDIF() + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSX_LIBTYPE STREQUAL "STATIC") + SET(PHYSX_COMPILE_PDB_NAME_DEBUG "PhysX_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_CHECKED "PhysX_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_PROFILE "PhysX_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_RELEASE "PhysX_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSX_COMPILE_PDB_NAME_DEBUG "PhysX${CMAKE_DEBUG_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_CHECKED "PhysX${CMAKE_CHECKED_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_PROFILE "PhysX${CMAKE_PROFILE_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_RELEASE "PhysX${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +SET(PHYSX_PLATFORM_LINK_FLAGS "/MAP") + +IF(PHYSX_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) + + SET(PHYSX_PLATFORM_LINK_FLAGS_DEBUG "/DELAYLOAD:PhysXFoundation${CMAKE_DEBUG_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_DEBUG_POSTFIX}.dll") + SET(PHYSX_PLATFORM_LINK_FLAGS_CHECKED "/DELAYLOAD:PhysXFoundation${CMAKE_CHECKED_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_CHECKED_POSTFIX}.dll") + SET(PHYSX_PLATFORM_LINK_FLAGS_PROFILE "/DELAYLOAD:PhysXFoundation${CMAKE_PROFILE_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_PROFILE_POSTFIX}.dll") + SET(PHYSX_PLATFORM_LINK_FLAGS_RELEASE "/DELAYLOAD:PhysXFoundation${CMAKE_RELEASE_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_RELEASE_POSTFIX}.dll") +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSX_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSX_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSX_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSX_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..0519de2d7 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake @@ -0,0 +1,65 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic +# + +SET(PHYSXCHARACTERKINEMATIC_LIBTYPE STATIC) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};PX_PHYSX_CHARACTER_STATIC_LIB;${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCHARACTERKINEMATIC_LIBTYPE STREQUAL "STATIC") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_DEBUG "PhysXCharacterKinematic_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_CHECKED "PhysXCharacterKinematic_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_PROFILE "PhysXCharacterKinematic_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_RELEASE "PhysXCharacterKinematic_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_DEBUG "PhysXCharacterKinematic${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_CHECKED "PhysXCharacterKinematic${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_PROFILE "PhysXCharacterKinematic${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_RELEASE "PhysXCharacterKinematic${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXCHARACTERKINEMATIC_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() + diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake new file mode 100644 index 000000000..572f0db76 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake @@ -0,0 +1,107 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon +# + +SET(PHYSXCOMMON_WINDOWS_HEADERS + ${PHYSX_SOURCE_DIR}/common/include/windows/CmWindowsLoadLibrary.h + ${PHYSX_SOURCE_DIR}/common/include/windows/CmWindowsModuleUpdateLoader.h +) +SOURCE_GROUP(common\\include\\windows FILES ${PHYSXCOMMON_WINDOWS_HEADERS}) + +SET(PHYSXCOMMON_WINDOWS_SOURCE + ${COMMON_SRC_DIR}/windows/CmWindowsModuleUpdateLoader.cpp + ${COMMON_SRC_DIR}/windows/CmWindowsDelayLoadHook.cpp +) +SOURCE_GROUP(common\\src\\windows FILES ${PHYSXCOMMON_WINDOWS_SOURCE}) + +SET(PHYSXCOMMON_RESOURCE + ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/PhysXCommon.rc) +SOURCE_GROUP(resource FILES ${PHYSXCOMMON_RESOURCE}) + +SET(PXCOMMON_PLATFORM_SRC_FILES + ${PHYSXCOMMON_WINDOWS_HEADERS} + ${PHYSXCOMMON_WINDOWS_SOURCE} + + ${PHYSXCOMMON_RESOURCE} +) + +SET(PXCOMMON_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/windows +) + +IF(NOT PX_GENERATE_STATIC_LIBRARIES) + SET(PXCOMMON_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS; + ) +ENDIF() + +SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PXCOMMON_LIBTYPE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + # Switch platforms here? + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS}> +) + +SET(PXCOMMON_PLATFORM_LINK_FLAGS "/MAP") + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXCOMMON_LIBTYPE STATIC) +ELSE() + SET(PHYSXCOMMON_LIBTYPE SHARED) +ENDIF() + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCOMMON_LIBTYPE STREQUAL "STATIC") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_DEBUG "PhysXCommon_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_CHECKED "PhysXCommon_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_PROFILE "PhysXCommon_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_RELEASE "PhysXCommon_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXCOMMON_COMPILE_PDB_NAME_DEBUG "PhysXCommon${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_CHECKED "PhysXCommon${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_PROFILE "PhysXCommon${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_RELEASE "PhysXCommon${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXCOMMON_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) + + SET(PXCOMMON_PLATFORM_LINK_FLAGS_DEBUG "/DELAYLOAD:PhysXFoundation${CMAKE_DEBUG_POSTFIX}.dll") + SET(PXCOMMON_PLATFORM_LINK_FLAGS_CHECKED "/DELAYLOAD:PhysXFoundation${CMAKE_CHECKED_POSTFIX}.dll") + SET(PXCOMMON_PLATFORM_LINK_FLAGS_PROFILE "/DELAYLOAD:PhysXFoundation${CMAKE_PROFILE_POSTFIX}.dll") + SET(PXCOMMON_PLATFORM_LINK_FLAGS_RELEASE "/DELAYLOAD:PhysXFoundation${CMAKE_RELEASE_POSTFIX}.dll") +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXCOMMON_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXCOMMON_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXCOMMON_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXCOMMON_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake new file mode 100644 index 000000000..13c254ac4 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake @@ -0,0 +1,94 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking +# + +IF(NOT PX_GENERATE_STATIC_LIBRARIES) + SET(PXCOOKING_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_CORE_EXPORTS + ) +ENDIF() + +SET(PHYSXCOOKING_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};PX_COOKING;${PXCOOKING_LIBTYPE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> + +) + +SET(PHYSX_COOKING_WINDOWS_SOURCE + ${LL_SOURCE_DIR}/windows/WindowsCookingDelayLoadHook.cpp +) +SOURCE_GROUP(src\\windows FILES ${PHYSX_COOKING_WINDOWS_SOURCE}) + +SET(PHYSX_COOKING_RESOURCE + ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/PhysXCooking.rc +) +SOURCE_GROUP(resource FILES ${PHYSX_COOKING_RESOURCE}) + +SET(PHYSXCOOKING_PLATFORM_SRC_FILES + ${PHYSX_COOKING_WINDOWS_SOURCE} + ${PHYSX_COOKING_RESOURCE} +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXCOOKING_LIBTYPE STATIC) +ELSE() + SET(PHYSXCOOKING_LIBTYPE SHARED) +ENDIF() + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCOOKING_LIBTYPE STREQUAL "STATIC") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_DEBUG "PhysXCooking_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_CHECKED "PhysXCooking_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_PROFILE "PhysXCooking_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_RELEASE "PhysXCooking_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXCOOKING_COMPILE_PDB_NAME_DEBUG "PhysXCooking${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_CHECKED "PhysXCooking${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_PROFILE "PhysXCooking${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_RELEASE "PhysXCooking${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXCOOKING_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXCOOKING_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXCOOKING_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXCOOKING_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXCOOKING_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() + +SET(PHYSXCOOKING_LINK_FLAGS "/MAP") +SET(PHYSXCOOKING_LINK_FLAGS_DEBUG "/DELAYLOAD:PhysXFoundation${CMAKE_DEBUG_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_DEBUG_POSTFIX}.dll") +SET(PHYSXCOOKING_LINK_FLAGS_CHECKED "/DELAYLOAD:PhysXFoundation${CMAKE_CHECKED_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_CHECKED_POSTFIX}.dll") +SET(PHYSXCOOKING_LINK_FLAGS_PROFILE "/DELAYLOAD:PhysXFoundation${CMAKE_PROFILE_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_PROFILE_POSTFIX}.dll") +SET(PHYSXCOOKING_LINK_FLAGS_RELEASE "/DELAYLOAD:PhysXFoundation${CMAKE_RELEASE_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_RELEASE_POSTFIX}.dll") diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake new file mode 100644 index 000000000..6fec964d7 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake @@ -0,0 +1,77 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions +# + + +SET(PHYSXEXTENSIONS_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/windows +) + +SET(PHYSXEXTENSIONS_PLATFORM_SRC_FILES + +) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXEXTENSIONS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXEXTENSIONS_PRIVATE_PLATFORM_LINKED_LIBS + FastXml +) + +SET(PHYSXEXTENSIONS_LIBTYPE STATIC) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXEXTENSIONS_LIBTYPE STREQUAL "STATIC") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_DEBUG "PhysXExtensions_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_CHECKED "PhysXExtensions_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_PROFILE "PhysXExtensions_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_RELEASE "PhysXExtensions_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_DEBUG "PhysXExtensions${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_CHECKED "PhysXExtensions${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_PROFILE "PhysXExtensions${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_RELEASE "PhysXExtensions${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXEXTENSIONS_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXEXTENSIONS_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXEXTENSIONS_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXEXTENSIONS_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXEXTENSIONS_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake new file mode 100644 index 000000000..a4ca7b522 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake @@ -0,0 +1,126 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation +# + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXFOUNDATION_LIBTYPE STATIC) +ELSE() + SET(PHYSXFOUNDATION_LIBTYPE SHARED) + SET(PHYSXFOUNDATION_SHARED_LIBRARY_DEFS PX_FOUNDATION_DLL=1;) +ENDIF() + +SET(PXSHARED_PLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/windows/PxWindowsIntrinsics.h +) +SOURCE_GROUP(shared\\include\\windows FILES ${PXSHARED_PLATFORM_HEADERS}) +SET(PXSHARED_UNIXPLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/unix/PxUnixIntrinsics.h +) +SOURCE_GROUP(shared\\include\\unix FILES ${PXSHARED_UNIXPLATFORM_HEADERS}) + +SET(PHYSXFOUNDATION_RESOURCE_FILE + ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/PhysXFoundation.rc +) +SOURCE_GROUP(resource FILES ${PHYSXFOUNDATION_RESOURCE_FILE}) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE + ${LL_SOURCE_DIR}/src/windows/PsWindowsAtomic.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsCpu.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsFPU.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsMutex.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsPrintString.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsSList.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsSocket.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsSync.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsThread.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsTime.cpp +) +SOURCE_GROUP("src\\src\\windows" FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE}) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/windows/PsWindowsAoS.h + ${LL_SOURCE_DIR}/include/windows/PsWindowsFPU.h + ${LL_SOURCE_DIR}/include/windows/PsWindowsInclude.h + ${LL_SOURCE_DIR}/include/windows/PsWindowsInlineAoS.h + ${LL_SOURCE_DIR}/include/windows/PsWindowsIntrinsics.h + ${LL_SOURCE_DIR}/include/windows/PsWindowsTrigConstants.h +) +SOURCE_GROUP("src\\include\\windows" FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS}) + +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} DESTINATION source/foundation/include/windows) +INSTALL(FILES ${PXSHARED_PLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/windows) +INSTALL(FILES ${PXSHARED_UNIXPLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/unix) + +SET(PHYSXFOUNDATION_PLATFORM_FILES + ${PHYSXFOUNDATION_PLATFORM_HEADERS} + ${PHYSXFOUNDATION_PLATFORM_SOURCE} + ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} + ${PHYSXFOUNDATION_RESOURCE_FILE} +) + +SET(PHYSXFOUNDATION_PLATFORM_INCLUDES + ${LL_SOURCE_DIR}/include/windows +) + +IF(NOT PX_GENERATE_STATIC_LIBRARIES) + SET(PXFOUNDATION_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1; + ) +ENDIF() + +SET(PHYSXFOUNDATION_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};_WINSOCK_DEPRECATED_NO_WARNINGS;${PXFOUNDATION_LIBTYPE_DEFS};${PHYSX_LIBTYPE_DEFS}; + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXFOUNDATION_LIBTYPE STREQUAL "STATIC") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_DEBUG "PhysXFoundation_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_CHECKED "PhysXFoundation_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_PROFILE "PhysXFoundation_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_RELEASE "PhysXFoundation_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_DEBUG "PhysXFoundation${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_CHECKED "PhysXFoundation${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_PROFILE "PhysXFoundation${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_RELEASE "PhysXFoundation${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXFOUNDATION_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXFOUNDATION_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXFOUNDATION_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXFOUNDATION_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXFOUNDATION_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake new file mode 100644 index 000000000..06cb95478 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake @@ -0,0 +1,89 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK +# + +IF(PX_USE_NVTX) + FIND_PACKAGE(nvToolsExt $ENV{PM_nvToolsExt_VERSION} REQUIRED) + MESSAGE("Using nvtx lib: ${NVTOOLSEXT_LIB} path: ${NVTOOLSEXTSDK_PATH}") + SET(NV_TOOLS_EXT_LIB ${NVTOOLSEXT_LIB}) +ENDIF() + +SOURCE_GROUP(resource FILES ${PHYSXPVDSDK_RESOURCE_FILE}) + +SOURCE_GROUP(include\\windows FILES ${PHYSXPVDSDK_PLATFORM_HEADERS}) + +SOURCE_GROUP(src\\src\\windows FILES ${PHYSXPVDSDK_PLATFORM_SOURCE}) + +SET(PHYSXPVDSDK_PLATFORM_FILES + ${PHYSXPVDSDK_RESOURCE_FILE} + ${PHYSXPVDSDK_PLATFORM_HEADERS} + ${PHYSXPVDSDK_PLATFORM_SOURCE} +) + +SET(PHYSXPVDSDK_PLATFORM_INCLUDES + ${NVTOOLSEXT_INCLUDE_DIRS} +) + +SET(PHYSXPVDSDK_LIBTYPE STATIC) + +SET(PHYSXPVDSDK_PLATFORM_LINKED_LIBS ${NV_TOOLS_EXT_LIB}) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXPVDSDK_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB;${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXPVDSDK_LIBTYPE STREQUAL "STATIC") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_DEBUG "PhysXPvdSDK_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_CHECKED "PhysXPvdSDK_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_PROFILE "PhysXPvdSDK_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_RELEASE "PhysXPvdSDK_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_DEBUG "PhysXPvdSDK${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_CHECKED "PhysXPvdSDK${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_PROFILE "PhysXPvdSDK${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_RELEASE "PhysXPvdSDK${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXPVDSDK_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXPVDSDK_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXPVDSDK_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXPVDSDK_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXPVDSDK_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() + + diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake new file mode 100644 index 000000000..dcd1013e5 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake @@ -0,0 +1,73 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask +# + + +SET(PHYSXTASK_COMPILE_DEFS + ${PHYSX_WINDOWS_COMPILE_DEFS};_LIB;${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXTASK_LIBTYPE OBJECT) +ELSE() + SET(PHYSXTASK_LIBTYPE STATIC) +ENDIF() + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXTASK_LIBTYPE STREQUAL "STATIC") + SET(PT_COMPILE_PDB_NAME_DEBUG "PhysXTask_static${CMAKE_DEBUG_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_CHECKED "PhysXTask_static${CMAKE_CHECKED_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_PROFILE "PhysXTask_static${CMAKE_PROFILE_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_RELEASE "PhysXTask_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PT_COMPILE_PDB_NAME_DEBUG "PhysXTask${CMAKE_DEBUG_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_CHECKED "PhysXTask${CMAKE_CHECKED_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_PROFILE "PhysXTask${CMAKE_PROFILE_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_RELEASE "PhysXTask${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(PHYSXTASK_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${PT_COMPILE_PDB_NAME_DEBUG}") + SET(PHYSXTASK_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${PT_COMPILE_PDB_NAME_CHECKED}") + SET(PHYSXTASK_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${PT_COMPILE_PDB_NAME_PROFILE}") + SET(PHYSXTASK_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${PT_COMPILE_PDB_NAME_RELEASE}") +ELSE() + SET(PHYSXTASK_COMPILE_PDB_NAME_DEBUG "${PT_COMPILE_PDB_NAME_DEBUG}") + SET(PHYSXTASK_COMPILE_PDB_NAME_CHECKED "${PT_COMPILE_PDB_NAME_CHECKED}") + SET(PHYSXTASK_COMPILE_PDB_NAME_PROFILE "${PT_COMPILE_PDB_NAME_PROFILE}") + SET(PHYSXTASK_COMPILE_PDB_NAME_RELEASE "${PT_COMPILE_PDB_NAME_RELEASE}") +ENDIF() + +INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PT_COMPILE_PDB_NAME_DEBUG}>$<$:${PT_COMPILE_PDB_NAME_CHECKED}>$<$:${PT_COMPILE_PDB_NAME_PROFILE}>$<$:${PT_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake new file mode 100644 index 000000000..3c8eec283 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake @@ -0,0 +1,64 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle +# + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXVEHICLE_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB;${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXVEHICLE_LIBTYPE STATIC) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXVEHICLE_LIBTYPE STREQUAL "STATIC") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_DEBUG "PhysXVehicle_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_CHECKED "PhysXVehicle_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_PROFILE "PhysXVehicle_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_RELEASE "PhysXVehicle_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_DEBUG "PhysXVehicle${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_CHECKED "PhysXVehicle${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_PROFILE "PhysXVehicle${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_RELEASE "PhysXVehicle${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXVEHICLE_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXVEHICLE_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXVEHICLE_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXVEHICLE_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXVEHICLE_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake new file mode 100644 index 000000000..76604a4dc --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake @@ -0,0 +1,81 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery +# + + +SET(SCENEQUERY_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/windows +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(SCENEQUERY_LIBTYPE OBJECT) +ELSE() + SET(SCENEQUERY_LIBTYPE STATIC) +ENDIF() + + +# Use generator expressions to set config specific preprocessor definitions +SET(SCENEQUERY_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND SCENEQUERY_LIBTYPE STREQUAL "STATIC") + SET(SQ_COMPILE_PDB_NAME_DEBUG "SceneQuery_static${CMAKE_DEBUG_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_CHECKED "SceneQuery_static${CMAKE_CHECKED_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_PROFILE "SceneQuery_static${CMAKE_PROFILE_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_RELEASE "SceneQuery_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(SQ_COMPILE_PDB_NAME_DEBUG "SceneQuery${CMAKE_DEBUG_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_CHECKED "SceneQuery${CMAKE_CHECKED_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_PROFILE "SceneQuery${CMAKE_PROFILE_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_RELEASE "SceneQuery${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(SCENEQUERY_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${SQ_COMPILE_PDB_NAME_DEBUG}") + SET(SCENEQUERY_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${SQ_COMPILE_PDB_NAME_CHECKED}") + SET(SCENEQUERY_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${SQ_COMPILE_PDB_NAME_PROFILE}") + SET(SCENEQUERY_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${SQ_COMPILE_PDB_NAME_RELEASE}") + + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${SQ_COMPILE_PDB_NAME_DEBUG}>$<$:${SQ_COMPILE_PDB_NAME_CHECKED}>$<$:${SQ_COMPILE_PDB_NAME_PROFILE}>$<$:${SQ_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + SET(SCENEQUERY_COMPILE_PDB_NAME_DEBUG "${SQ_COMPILE_PDB_NAME_DEBUG}") + SET(SCENEQUERY_COMPILE_PDB_NAME_CHECKED "${SQ_COMPILE_PDB_NAME_CHECKED}") + SET(SCENEQUERY_COMPILE_PDB_NAME_PROFILE "${SQ_COMPILE_PDB_NAME_PROFILE}") + SET(SCENEQUERY_COMPILE_PDB_NAME_RELEASE "${SQ_COMPILE_PDB_NAME_RELEASE}") +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake new file mode 100644 index 000000000..856d9a23a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake @@ -0,0 +1,81 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController +# + +SET(SIMULATIONCONTROLLER_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/windows + ${PHYSX_SOURCE_DIR}/LowLevel/windows/include +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(SIMULATIONCONTROLLER_LIBTYPE OBJECT) +ELSE() + SET(SIMULATIONCONTROLLER_LIBTYPE STATIC) +ENDIF() + + +# Use generator expressions to set config specific preprocessor definitions +SET(SIMULATIONCONTROLLER_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND SIMULATIONCONTROLLER_LIBTYPE STREQUAL "STATIC") + SET(SC_COMPILE_PDB_NAME_DEBUG "SimulationController_static${CMAKE_DEBUG_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_CHECKED "SimulationController_static${CMAKE_CHECKED_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_PROFILE "SimulationController_static${CMAKE_PROFILE_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_RELEASE "SimulationController_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(SC_COMPILE_PDB_NAME_DEBUG "SimulationController${CMAKE_DEBUG_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_CHECKED "SimulationController${CMAKE_CHECKED_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_PROFILE "SimulationController${CMAKE_PROFILE_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_RELEASE "SimulationController${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${SC_COMPILE_PDB_NAME_DEBUG}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${SC_COMPILE_PDB_NAME_CHECKED}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${SC_COMPILE_PDB_NAME_PROFILE}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${SC_COMPILE_PDB_NAME_RELEASE}") + + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${SC_COMPILE_PDB_NAME_DEBUG}>$<$:${SC_COMPILE_PDB_NAME_CHECKED}>$<$:${SC_COMPILE_PDB_NAME_PROFILE}>$<$:${SC_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_DEBUG "${SC_COMPILE_PDB_NAME_DEBUG}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_CHECKED "${SC_COMPILE_PDB_NAME_CHECKED}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_PROFILE "${SC_COMPILE_PDB_NAME_PROFILE}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_RELEASE "${SC_COMPILE_PDB_NAME_RELEASE}") +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysX.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysX.rc new file mode 100644 index 000000000..e93c4a969 --- /dev/null +++ b/src/PhysX/physx/source/compiler/resource_x64/PhysX.rc @@ -0,0 +1,88 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""windows.h""\r\r\r\0" +END + +3 TEXTINCLUDE +BEGIN + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,0,0,0 + PRODUCTVERSION 4,0,0,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "NVIDIA Corporation" + VALUE "FileDescription", "PhysX 64bit Dynamic Link Library" + VALUE "FileVersion", "4.0.0.0" + VALUE "InternalName", "PhysX_64" + VALUE "LegalCopyright", "Copyright (C) 2018 NVIDIA Corporation" + VALUE "OriginalFilename", "PhysX_64.dll" + VALUE "ProductName", "PhysX" + VALUE "ProductVersion", "4.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc new file mode 100644 index 000000000..5d8a51c48 --- /dev/null +++ b/src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc @@ -0,0 +1,88 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""windows.h""\r\r\r\0" +END + +3 TEXTINCLUDE +BEGIN + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,0,0,0 + PRODUCTVERSION 4,0,0,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "NVIDIA Corporation" + VALUE "FileDescription", "PhysXCommon 64bit Dynamic Link Library" + VALUE "FileVersion", "4.0.0.0" + VALUE "InternalName", "PhysXCommon_64" + VALUE "LegalCopyright", "Copyright (C) 2018 NVIDIA Corporation" + VALUE "OriginalFilename", "PhysXCommon_64.dll" + VALUE "ProductName", "PhysX" + VALUE "ProductVersion", "4.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc new file mode 100644 index 000000000..577b8060d --- /dev/null +++ b/src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc @@ -0,0 +1,91 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" + + + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""windows.h""\r\r\r\0" +END + +3 TEXTINCLUDE +BEGIN + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,0,0,0 + PRODUCTVERSION 4,0,0,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "NVIDIA Corporation" + VALUE "FileDescription", "PhysXCooking 64bit Dynamic Link Library" + VALUE "FileVersion", "4.0.0.0" + VALUE "InternalName", "PhysXCooking_64" + VALUE "LegalCopyright", "Copyright (C) 2018 NVIDIA Corporation" + VALUE "OriginalFilename", "PhysXCooking_64.dll" + VALUE "ProductName", "PhysX" + VALUE "ProductVersion", "4.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysXFoundation.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysXFoundation.rc new file mode 100644 index 0000000000000000000000000000000000000000..f2e1701b63d2ddabaf25bd782049b201c35e4eae GIT binary patch literal 4644 zcmdUyTW=CU6vxlAiQi$RFE*NX+Y+^p<ofs{*X(uB0-A`yXfAxh$BSAS=QVRzXD z1^a;6%+Bo0oSAd}=eoas9?P+KaxDXSlqVTWKDvs)<-%)XwX z)3Yz6CUt2@Qwma&s#_oRQVgDY-o$Ech0+&^cd!RIy zugE_I9mS!6)VI7#NS#Mc#iPjlDQ#6Ff7Zb_gRiBFj zox5MXb8DCe_Yc zaP$%C!hh$}>E7yetG%`p5Lv%7hvZkr-*vCIc+%tNo%o9ws#>skDV0Wt?Qckn+8Ig4 zjEvk7d+xAG8^1#y*YR_<6F=MDa$q8A2Vc=BVITGKw1|7WHyO0Tw@Q79cPVvr#my_F$Bq&)pt!QflPtdMFPq(4i&(1FPhA1_tLTYek*H07cbvVv}O>~#LG2Pd5oKXc&MI^(` zQ}nF$R3FU@kf@7(Mj$g@@xS!boF~Lj_0gs;=d`Rm@Q!Iv$mYhOZsgNkpg!_>rr=-y|qRr zowI%mf?C0n=d^?Bf)mx1^5^WrYBIFH5RtP~TdOgv_^jO9CIcZosqNYMZ>jS>M!hu&ztLNkGlP_}+l%kdX+-0~Z88;)$&2&1@@ z%vJ%a40GTgh6ejK6~45c3Es= zLj%nPm9^gW?#|B4KZhOv{&g(J;>ooP0-4Lb4Ef5-J04H^(v=Zy z%KVJJ!`zf!>E?_c8GC%`X#<(C=SW`5E4Un+{(#vLt3!5o%-&3b{z$$^#_U$g1GBGZ z==AIhsYzWL(v*Ufq$+K>ks`AN#uX{cXJ+cWuO-J!nX%)qGVI^uq;tI|SKzXWaSxQn z@(ulGprbf6(E65l39a+!sdyBbKc%f&! zCw`hlS02u7ysONk=l1Y&;|3pYLsfzP&~Pm?TVb|Ie`WmiT;6la$%;(tEvbIq zLZVMl7yi4LPLEb+TkW--fXMotIW)gA@veKl#gm>t@5Eo^P_=@^OQ|$FY|kMrYiA^x zGcs~V?zzJ&ZT=2rTqn=jPV#Ka<-lar4zZ$H!af@1X&EEwXNuc?<%Fm-PCD1?&8_!y zfVN{S;pVm1*5XEN^DYvlb#sGO_*Shii7us%uDE%n^w?1%2NYNKc#6h@(EK%nUzu^4|&c6I0IzF(Xr>j5qmRVnOQ<2-g2~Awi@FaJFqwkVO z18~~B-(T|68`=6#oT7?+_U!CpZ-`QZDx`*FcKtN5UPs~_*hF_Zj_I+UtUQS*|s1 z(kIq$K~O7LwQ|VMlQS~r1KhPDF0gZCq}FQLLo>zx>tghPy!W)HHHdANG9I%IlPuM< zziiuRuYRC)!PH1ROrX74@3zr<8-8VY_0fN5EH?oK^~^r#&XyKdo5okPhRK_n;uJkk zE&q~!P+dr(wo>_=Usy|q{`VY2j_{5l&o;=^;6H|Lj?c`tw8QRvcK4^SUT_|p zoQY?QmhpRs>n<56o+lb0ra2-2=`a>;Oz1se^OLO})o=Vo7Ps<-(uN~@XM{Q2X=ZDH zm#M3xck+IXRr_eOx96t6reqDYUm8WFQO)$*_5b?+tQG0z|7V9~REqr?tAy)o-Vymo M@3Z9Ja? +#include +#include +#include + +using namespace physx; + +namespace +{ +#define MIN_CLOSE_COUNT 2 +#define DEFAULT_READ_BUFFER_SIZE (16 * 1024) +#define NUM_ENTITY 5 + +struct Entity +{ + const char* str; + unsigned int strLength; + char chr; +}; + +static const Entity entity[NUM_ENTITY] = { + { "<", 4, '<' }, { "&", 5, '&' }, { ">", 4, '>' }, { """, 6, '\"' }, { "'", 6, '\'' } +}; + +class MyFastXml : public physx::shdfnd::FastXml +{ + public: + enum CharType + { + CT_DATA, + CT_EOF, + CT_SOFT, + CT_END_OF_ELEMENT, // either a forward slash or a greater than symbol + CT_END_OF_LINE + }; + + MyFastXml(Callback* c) + { + mStreamFromMemory = true; + mCallback = c; + memset(mTypes, CT_DATA, sizeof(mTypes)); + mTypes[0] = CT_EOF; + mTypes[uint8_t(' ')] = mTypes[uint8_t('\t')] = CT_SOFT; + mTypes[uint8_t('/')] = mTypes[uint8_t('>')] = mTypes[uint8_t('?')] = CT_END_OF_ELEMENT; + mTypes[uint8_t('\n')] = mTypes[uint8_t('\r')] = CT_END_OF_LINE; + mError = 0; + mStackIndex = 0; + mFileBuf = NULL; + mReadBufferEnd = NULL; + mReadBuffer = NULL; + mReadBufferSize = DEFAULT_READ_BUFFER_SIZE; + mOpenCount = 0; + mLastReadLoc = 0; + for(uint32_t i = 0; i < (MAX_STACK + 1); i++) + { + mStack[i] = NULL; + mStackAllocated[i] = false; + } + } + + char* processClose(char c, const char* element, char* scan, int32_t argc, const char** argv, + FastXml::Callback* iface, bool& isError) + { + AttributePairs attr(argc, argv); + isError = true; // by default, if we return null it's due to an error. + if(c == '/' || c == '?') + { + char* slash = const_cast(static_cast(strchr(element, c))); + if(slash) + *slash = 0; + + if(c == '?' && strcmp(element, "xml") == 0) + { + if(!iface->processXmlDeclaration(attr, 0, mLineNo)) + return NULL; + } + else + { + if(!iface->processElement(element, 0, attr, mLineNo)) + { + mError = "User aborted the parsing process"; + return NULL; + } + + pushElement(element); + + const char* close = popElement(); + + if(!iface->processClose(close, mStackIndex, isError)) + { + return NULL; + } + } + + if(!slash) + ++scan; + } + else + { + scan = skipNextData(scan); + char* data = scan; // this is the data portion of the element, only copies memory if we encounter line feeds + char* dest_data = 0; + while(*scan && *scan != '<') + { + if(getCharType(scan) == CT_END_OF_LINE) + { + if(*scan == '\r') + mLineNo++; + dest_data = scan; + *dest_data++ = ' '; // replace the linefeed with a space... + scan = skipNextData(scan); + while(*scan && *scan != '<') + { + if(getCharType(scan) == CT_END_OF_LINE) + { + if(*scan == '\r') + mLineNo++; + *dest_data++ = ' '; // replace the linefeed with a space... + scan = skipNextData(scan); + } + else + { + *dest_data++ = *scan++; + } + } + break; + } + else if('&' == *scan) + { + dest_data = scan; + while(*scan && *scan != '<') + { + if('&' == *scan) + { + if(*(scan + 1) && *(scan + 1) == '#' && *(scan + 2)) + { + if(*(scan + 2) == 'x') + { + // Hexadecimal. + if(!*(scan + 3)) + break; + + char* q = scan + 3; + q = strchr(q, ';'); + + if(!q || !*q) + PX_ASSERT(0); + + --q; + char ch = char(*q > '9' ? (tolower(*q) - 'a' + 10) : *q - '0'); + if(*(--q) != tolower('x')) + ch |= char(*q > '9' ? (tolower(*q) - 'a' + 10) : *q - '0') << 4; + + *dest_data++ = ch; + } + else + { + // Decimal. + if(!*(scan + 2)) + break; + + const char* q = scan + 2; + q = strchr(q, ';'); + + if(!q || !*q) + PX_ASSERT(0); + + --q; + char ch = *q - '0'; + if(*(--q) != '#') + ch |= (*q - '0') * 10; + + *dest_data++ = ch; + } + + char* start = scan; + char* end = strchr(start, ';'); + if(end) + { + *end = 0; + scan = end + 1; + } + + continue; + } + + for(int i = 0; i < NUM_ENTITY; ++i) + { + if(strncmp(entity[i].str, scan, entity[i].strLength) == 0) + { + *dest_data++ = entity[i].chr; + scan += entity[i].strLength; + break; + } + } + } + else + { + *dest_data++ = *scan++; + } + } + break; + } + else + ++scan; + } + + if(*scan == '<') + { + if(scan[1] != '/') + { + PX_ASSERT(mOpenCount > 0); + mOpenCount--; + } + if(dest_data) + { + *dest_data = 0; + } + else + { + *scan = 0; + } + + scan++; // skip it.. + + if(*data == 0) + data = 0; + + if(!iface->processElement(element, data, attr, mLineNo)) + { + mError = "User aborted the parsing process"; + return 0; + } + + pushElement(element); + + // check for the comment use case... + if(scan[0] == '!' && scan[1] == '-' && scan[2] == '-') + { + scan += 3; + while(*scan && *scan == ' ') + ++scan; + + char* comment = scan; + char* comment_end = strstr(scan, "-->"); + if(comment_end) + { + *comment_end = 0; + scan = comment_end + 3; + if(!iface->processComment(comment)) + { + mError = "User aborted the parsing process"; + return 0; + } + } + } + else if(*scan == '/') + { + scan = processClose(scan, iface, isError); + if(scan == NULL) + { + return NULL; + } + } + } + else + { + mError = "Data portion of an element wasn't terminated properly"; + return NULL; + } + } + + if(mOpenCount < MIN_CLOSE_COUNT) + { + scan = readData(scan); + } + + return scan; + } + + char* processClose(char* scan, FastXml::Callback* iface, bool& isError) + { + const char* start = popElement(), *close = start; + if(scan[1] != '>') + { + scan++; + close = scan; + while(*scan && *scan != '>') + scan++; + *scan = 0; + } + + if(0 != strcmp(start, close)) + { + mError = "Open and closing tags do not match"; + return 0; + } + + if(!iface->processClose(close, mStackIndex, isError)) + { + // we need to set the read pointer! + uint32_t offset = uint32_t(mReadBufferEnd - scan) - 1; + uint32_t readLoc = mLastReadLoc - offset; + mFileBuf->seek(readLoc); + return NULL; + } + ++scan; + + return scan; + } + + virtual bool processXml(physx::PxInputData& fileBuf, bool streamFromMemory) + { + releaseMemory(); + mFileBuf = &fileBuf; + mStreamFromMemory = streamFromMemory; + return processXml(mCallback); + } + + // if we have finished processing the data we had pending.. + char* readData(char* scan) + { + for(uint32_t i = 0; i < (mStackIndex + 1); i++) + { + if(!mStackAllocated[i]) + { + const char* text = mStack[i]; + if(text) + { + uint32_t tlen = uint32_t(strlen(text)); + mStack[i] = static_cast(mCallback->allocate(tlen + 1)); + PxMemCopy(const_cast(static_cast(mStack[i])), text, tlen + 1); + mStackAllocated[i] = true; + } + } + } + + if(!mStreamFromMemory) + { + if(scan == NULL) + { + uint32_t seekLoc = mFileBuf->tell(); + mReadBufferSize = (mFileBuf->getLength() - seekLoc); + } + else + { + return scan; + } + } + + if(mReadBuffer == NULL) + { + mReadBuffer = static_cast(mCallback->allocate(mReadBufferSize + 1)); + } + uint32_t offset = 0; + uint32_t readLen = mReadBufferSize; + + if(scan) + { + offset = uint32_t(scan - mReadBuffer); + uint32_t copyLen = mReadBufferSize - offset; + if(copyLen) + { + PX_ASSERT(scan >= mReadBuffer); + memmove(mReadBuffer, scan, copyLen); + mReadBuffer[copyLen] = 0; + readLen = mReadBufferSize - copyLen; + } + offset = copyLen; + } + + uint32_t readCount = mFileBuf->read(&mReadBuffer[offset], readLen); + + while(readCount > 0) + { + + mReadBuffer[readCount + offset] = 0; // end of string terminator... + mReadBufferEnd = &mReadBuffer[readCount + offset]; + + const char* scan_ = &mReadBuffer[offset]; + while(*scan_) + { + if(*scan_ == '<' && scan_[1] != '/') + { + mOpenCount++; + } + scan_++; + } + + if(mOpenCount < MIN_CLOSE_COUNT) + { + uint32_t oldSize = uint32_t(mReadBufferEnd - mReadBuffer); + mReadBufferSize = mReadBufferSize * 2; + char* oldReadBuffer = mReadBuffer; + mReadBuffer = static_cast(mCallback->allocate(mReadBufferSize + 1)); + PxMemCopy(mReadBuffer, oldReadBuffer, oldSize); + mCallback->deallocate(oldReadBuffer); + offset = oldSize; + uint32_t readSize = mReadBufferSize - oldSize; + readCount = mFileBuf->read(&mReadBuffer[offset], readSize); + if(readCount == 0) + break; + } + else + { + break; + } + } + mLastReadLoc = mFileBuf->tell(); + + return mReadBuffer; + } + + bool processXml(FastXml::Callback* iface) + { + bool ret = true; + + const int MAX_ATTRIBUTE = 2048; // can't imagine having more than 2,048 attributes in a single element right? + + mLineNo = 1; + + char* element, *scan = readData(0); + + while(*scan) + { + + scan = skipNextData(scan); + + if(*scan == 0) + break; + + if(*scan == '<') + { + + if(scan[1] != '/') + { + PX_ASSERT(mOpenCount > 0); + mOpenCount--; + } + scan++; + + if(*scan == '?') // Allow xml declarations + { + scan++; + } + else if(scan[0] == '!' && scan[1] == '-' && scan[2] == '-') + { + scan += 3; + while(*scan && *scan == ' ') + scan++; + char* comment = scan, *comment_end = strstr(scan, "-->"); + if(comment_end) + { + *comment_end = 0; + scan = comment_end + 3; + if(!iface->processComment(comment)) + { + mError = "User aborted the parsing process"; + return false; + } + } + continue; + } + else if(scan[0] == '!') // Allow doctype + { + scan++; + + // DOCTYPE syntax differs from usual XML so we parse it here + + // Read DOCTYPE + const char* tag = "DOCTYPE"; + if(!strstr(scan, tag)) + { + mError = "Invalid DOCTYPE"; + return false; + } + + scan += strlen(tag); + + // Skip whites + while(CT_SOFT == getCharType(scan)) + ++scan; + + // Read rootElement + const char* rootElement = scan; + while(CT_DATA == getCharType(scan)) + ++scan; + + char* endRootElement = scan; + + // TODO: read remaining fields (fpi, uri, etc.) + while(CT_END_OF_ELEMENT != getCharType(scan++)) + ; + + *endRootElement = 0; + + if(!iface->processDoctype(rootElement, 0, 0, 0)) + { + mError = "User aborted the parsing process"; + return false; + } + + continue; // Restart loop + } + } + + if(*scan == '/') + { + bool isError; + scan = processClose(scan, iface, isError); + if(!scan) + { + if(isError) + { + mError = "User aborted the parsing process"; + } + return !isError; + } + } + else + { + if(*scan == '?') + scan++; + element = scan; + int32_t argc = 0; + const char* argv[MAX_ATTRIBUTE]; + bool close; + scan = nextSoftOrClose(scan, close); + if(close) + { + char c = *(scan - 1); + if(c != '?' && c != '/') + { + c = '>'; + } + *scan++ = 0; + bool isError; + scan = processClose(c, element, scan, argc, argv, iface, isError); + if(!scan) + { + if(isError) + { + mError = "User aborted the parsing process"; + } + return !isError; + } + } + else + { + if(*scan == 0) + { + return ret; + } + + *scan = 0; // place a zero byte to indicate the end of the element name... + scan++; + + while(*scan) + { + scan = skipNextData(scan); // advance past any soft seperators (tab or space) + + if(getCharType(scan) == CT_END_OF_ELEMENT) + { + char c = *scan++; + if('?' == c) + { + if('>' != *scan) //?> + { + PX_ASSERT(0); + return false; + } + + scan++; + } + bool isError; + scan = processClose(c, element, scan, argc, argv, iface, isError); + if(!scan) + { + if(isError) + { + mError = "User aborted the parsing process"; + } + return !isError; + } + break; + } + else + { + if(argc >= MAX_ATTRIBUTE) + { + mError = "encountered too many attributes"; + return false; + } + argv[argc] = scan; + scan = nextSep(scan); // scan up to a space, or an equal + if(*scan) + { + if(*scan != '=') + { + *scan = 0; + scan++; + while(*scan && *scan != '=') + scan++; + if(*scan == '=') + scan++; + } + else + { + *scan = 0; + scan++; + } + + if(*scan) // if not eof... + { + scan = skipNextData(scan); + if(*scan == '"') + { + scan++; + argc++; + argv[argc] = scan; + argc++; + while(*scan && *scan != 34) + scan++; + if(*scan == '"') + { + *scan = 0; + scan++; + } + else + { + mError = "Failed to find closing quote for attribute"; + return false; + } + } + else + { + // mError = "Expected quote to begin attribute"; + // return false; + // PH: let's try to have a more graceful fallback + argc--; + while(*scan != '/' && *scan != '>' && *scan != 0) + scan++; + } + } + } // if( *scan ) + } // if ( mTypes[*scan] + } // if( close ) + } // if( *scan == '/' + } // while( *scan ) + } + + if(mStackIndex) + { + mError = "Invalid file format"; + return false; + } + + return ret; + } + + const char* getError(int32_t& lineno) + { + const char* ret = mError; + lineno = mLineNo; + mError = 0; + return ret; + } + + virtual void release(void) + { + Callback* c = mCallback; // get the user allocator interface + MyFastXml* f = this; // cast the this pointer + f->~MyFastXml(); // explicitely invoke the destructor for this class + c->deallocate(f); // now free up the memory associated with it. + } + + private: + virtual ~MyFastXml(void) + { + releaseMemory(); + } + + PX_INLINE void releaseMemory(void) + { + mFileBuf = NULL; + mCallback->deallocate(mReadBuffer); + mReadBuffer = NULL; + mStackIndex = 0; + mReadBufferEnd = NULL; + mOpenCount = 0; + mLastReadLoc = 0; + mError = NULL; + for(uint32_t i = 0; i < (mStackIndex + 1); i++) + { + if(mStackAllocated[i]) + { + mCallback->deallocate(const_cast(static_cast(mStack[i]))); + mStackAllocated[i] = false; + } + mStack[i] = NULL; + } + } + + PX_INLINE CharType getCharType(char* scan) const + { + return mTypes[uint8_t(*scan)]; + } + + PX_INLINE char* nextSoftOrClose(char* scan, bool& close) + { + while(*scan && getCharType(scan) != CT_SOFT && *scan != '>') + scan++; + close = *scan == '>'; + return scan; + } + + PX_INLINE char* nextSep(char* scan) + { + while(*scan && getCharType(scan) != CT_SOFT && *scan != '=') + scan++; + return scan; + } + + PX_INLINE char* skipNextData(char* scan) + { + // while we have data, and we encounter soft seperators or line feeds... + while(*scan && (getCharType(scan) == CT_SOFT || getCharType(scan) == CT_END_OF_LINE)) + { + if(*scan == '\n') + mLineNo++; + scan++; + } + return scan; + } + + void pushElement(const char* element) + { + PX_ASSERT(mStackIndex < uint32_t(MAX_STACK)); + if(mStackIndex < uint32_t(MAX_STACK)) + { + if(mStackAllocated[mStackIndex]) + { + mCallback->deallocate(const_cast(static_cast(mStack[mStackIndex]))); + mStackAllocated[mStackIndex] = false; + } + mStack[mStackIndex++] = element; + } + } + + const char* popElement(void) + { + PX_ASSERT(mStackIndex > 0); + if(mStackAllocated[mStackIndex]) + { + mCallback->deallocate(const_cast(static_cast(mStack[mStackIndex]))); + mStackAllocated[mStackIndex] = false; + } + mStack[mStackIndex] = NULL; + return mStackIndex ? mStack[--mStackIndex] : NULL; + } + + static const int MAX_STACK = 2048; + + CharType mTypes[256]; + + physx::PxInputData* mFileBuf; + + char* mReadBuffer; + char* mReadBufferEnd; + + uint32_t mOpenCount; + uint32_t mReadBufferSize; + uint32_t mLastReadLoc; + + int32_t mLineNo; + const char* mError; + uint32_t mStackIndex; + const char* mStack[MAX_STACK + 1]; + bool mStreamFromMemory; + bool mStackAllocated[MAX_STACK + 1]; + Callback* mCallback; +}; +} + +namespace physx +{ +namespace shdfnd +{ + +FastXml* createFastXml(FastXml::Callback* iface) +{ + MyFastXml* m = static_cast(iface->allocate(sizeof(MyFastXml))); + if(m) + { + new (m) MyFastXml(iface); + } + return static_cast(m); +} +} +} diff --git a/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.h b/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.h new file mode 100644 index 000000000..5f74c07f8 --- /dev/null +++ b/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PSFILEBUFFER_PSASCIICONVERSION_H +#define PSFILEBUFFER_PSASCIICONVERSION_H + +/*! +\file +\brief PxAsciiConversion namespace contains string/value helper functions +*/ + +#include "PxMath.h" +#include "PsString.h" +#include +#include +#include +#include +#include + +namespace physx +{ +namespace general_string_parsing2 +{ +namespace PxAsc +{ + +const uint32_t PxF32StrLen = 24; +const uint32_t PxF64StrLen = 32; +const uint32_t IntStrLen = 32; + +PX_INLINE bool isWhiteSpace(char c); +PX_INLINE const char * skipNonWhiteSpace(const char *scan); +PX_INLINE const char * skipWhiteSpace(const char *scan); + +////////////////////////// +// str to value functions +////////////////////////// +PX_INLINE bool strToBool(const char *str, const char **endptr); +PX_INLINE int8_t strToI8(const char *str, const char **endptr); +PX_INLINE int16_t strToI16(const char *str, const char **endptr); +PX_INLINE int32_t strToI32(const char *str, const char **endptr); +PX_INLINE int64_t strToI64(const char *str, const char **endptr); +PX_INLINE uint8_t strToU8(const char *str, const char **endptr); +PX_INLINE uint16_t strToU16(const char *str, const char **endptr); +PX_INLINE uint32_t strToU32(const char *str, const char **endptr); +PX_INLINE uint64_t strToU64(const char *str, const char **endptr); +PX_INLINE float strToF32(const char *str, const char **endptr); +PX_INLINE double strToF64(const char *str, const char **endptr); +PX_INLINE void strToF32s(float *v,uint32_t count,const char *str, const char**endptr); + + +////////////////////////// +// value to str functions +////////////////////////// +PX_INLINE const char * valueToStr( bool val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( int8_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( int16_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( int32_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( int64_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( uint8_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( uint16_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( uint32_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( uint64_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( float val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( double val, char *buf, uint32_t n ); + +#include "PsAsciiConversion.inl" + +} // end of namespace +} // end of namespace +using namespace general_string_parsing2; +} // end of namespace + + +#endif // PSFILEBUFFER_PSASCIICONVERSION_H diff --git a/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.inl b/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.inl new file mode 100644 index 000000000..aa84e84a7 --- /dev/null +++ b/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.inl @@ -0,0 +1,566 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +/*! +\file +\brief NvAsciiConversion namespace contains string/value helper functions +*/ + +#include + +PX_INLINE bool isWhiteSpace(char c) +{ + bool ret = false; + if ( c == 32 || c == 9 || c == 13 || c == 10 || c == ',' ) ret = true; + return ret; +} + +PX_INLINE const char * skipNonWhiteSpace(const char *scan) +{ + while ( !isWhiteSpace(*scan) && *scan) scan++; + if ( *scan == 0 ) scan = NULL; + return scan; +} +PX_INLINE const char * skipWhiteSpace(const char *scan) +{ + while ( isWhiteSpace(*scan) && *scan ) scan++; + if ( *scan == 0 ) scan = NULL; + return scan; +} + +static double strtod_fast(const char * pString) +{ + //--- + // Find the start of the string + const char* pNumberStart = skipWhiteSpace(pString); + + //--- + // Find the end of the string + const char* pNumberEnd = pNumberStart; + + // skip optional sign + if( *pNumberEnd == '-' || *pNumberEnd == '+' ) + ++pNumberEnd; + + // skip optional digits + while( isdigit(*pNumberEnd) ) + ++pNumberEnd; + + // skip optional decimal and digits + if( *pNumberEnd == '.' ) + { + ++pNumberEnd; + + while( isdigit(*pNumberEnd) ) + ++pNumberEnd; + } + + // skip optional exponent + if( *pNumberEnd == 'd' + || *pNumberEnd == 'D' + || *pNumberEnd == 'e' + || *pNumberEnd == 'E' ) + { + ++pNumberEnd; + + if( *pNumberEnd == '-' || *pNumberEnd == '+' ) + ++pNumberEnd; + + while( isdigit(*pNumberEnd) ) + ++pNumberEnd; + } + + //--- + // Process the string + const uint32_t numberLen = (const uint32_t)(pNumberEnd-pNumberStart); + char buffer[32]; + if( numberLen+1 < sizeof(buffer)/sizeof(buffer[0]) ) + { + // copy into buffer and terminate with NUL before calling the + // standard function + memcpy( buffer, pNumberStart, numberLen*sizeof(buffer[0]) ); + buffer[numberLen] = '\0'; + const double result = strtod( buffer, NULL ); + + return result; + } + else + { + // buffer was too small so just call the standard function on the + // source input to get a proper result + return strtod( pString, NULL ); + } +} + +static float strtof_fast(const char* pString) +{ + return (float)strtod_fast(pString); +} + + +////////////////////////// +// str to value functions +////////////////////////// +PX_INLINE bool strToBool(const char *str, const char **endptr) +{ + bool ret = false; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + size_t len = (size_t)(end - begin); + if ( physx::shdfnd::strnicmp(begin,"true", len) == 0 || physx::shdfnd::strnicmp(begin,"1", len) == 0 ) + ret = true; + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE int8_t strToI8(const char *str, const char **endptr) +{ + int8_t ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + if( strncmp(begin, "INT8_MIN", (size_t)(end-begin)) == 0) + ret = INT8_MIN; + else if( strncmp(begin, "INT8_MAX", (size_t)(end-begin)) == 0) + ret = INT8_MAX; + else if( strncmp(begin, "PX_MIN_I8", (size_t)(end-begin)) == 0) + ret = INT8_MIN; + else if( strncmp(begin, "PX_MAX_I8", (size_t)(end-begin)) == 0) + ret = INT8_MAX; + else + ret = (int8_t)strtol(begin, 0, 0); //FIXME + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE int16_t strToI16(const char *str, const char **endptr) +{ + int16_t ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + if( strncmp(begin, "INT16_MIN", (size_t)(end-begin)) == 0) + ret = INT16_MIN; + else if( strncmp(begin, "INT16_MAX", (size_t)(end-begin)) == 0) + ret = INT16_MAX; + else if( strncmp(begin, "PX_MIN_I16", (size_t)(end-begin)) == 0) + ret = INT16_MIN; + else if( strncmp(begin, "PX_MAX_I16", (size_t)(end-begin)) == 0) + ret = INT16_MAX; + else + ret = (int16_t)strtol(begin, 0, 0); //FIXME + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE int32_t strToI32(const char *str, const char **endptr) +{ + int32_t ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + if( strncmp(begin, "INT32_MIN", (size_t)(end-begin)) == 0) + ret = INT32_MIN; + else if( strncmp(begin, "INT32_MAX", (size_t)(end-begin)) == 0) + ret = INT32_MAX; + else if( strncmp(begin, "PX_MIN_I32", (size_t)(end-begin)) == 0) + ret = INT32_MIN; + else if( strncmp(begin, "PX_MAX_I32", (size_t)(end-begin)) == 0) + ret = INT32_MAX; + else + ret = (int32_t)strtol(begin, 0, 0); //FIXME + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE int64_t strToI64(const char *str, const char **endptr) +{ + int64_t ret; + const char *begin = skipWhiteSpace(str); + + //FIXME +#ifdef _WIN32 //NV_WINDOWS, NV_XBOX + ret = (int64_t)_strtoi64(begin,0,10); +#else + ret = (int64_t)strtoll(begin,0,10); +#endif + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE uint8_t strToU8(const char *str, const char **endptr) +{ + uint8_t ret; + const char *begin = skipWhiteSpace(str); + + ret = (uint8_t)strtoul(begin, 0, 0); + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE uint16_t strToU16(const char *str, const char **endptr) +{ + uint16_t ret; + const char *end; + const char *begin = skipWhiteSpace(str); + + end = skipNonWhiteSpace(begin); + if( !end ) + end = begin + strlen(str); + + if( strncmp(begin, "UINT16_MAX", (size_t)(end-begin)) == 0) + ret = UINT16_MAX; + else if( strncmp(begin, "PX_MAX_U16", (size_t)(end-begin)) == 0) + ret = UINT16_MAX; + else + ret = (uint16_t)strtoul(begin,0,0); + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE uint32_t strToU32(const char *str, const char **endptr) +{ + uint32_t ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + if( strncmp(begin, "UINT32_MAX", (size_t)(end-begin)) == 0) + ret = UINT32_MAX; + else if( strncmp(begin, "PX_U32_MAX", (size_t)(end-begin)) == 0) + ret = UINT32_MAX; + else + ret = (uint32_t)strtoul(begin,0,0); + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE uint64_t strToU64(const char *str, const char **endptr) +{ + uint64_t ret; + const char *begin; + begin = skipWhiteSpace(str); + + //FIXME +#ifdef _WIN32 //NV_WINDOWS, NV_XBOX + ret = (uint64_t)_strtoui64(begin,0,10); +#else + ret = (uint64_t)strtoull(begin,0,10); +#endif + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +#ifndef DEBUGGING_MISMATCHES +#define DEBUGGING_MISMATCHES 0 +#endif + +PX_INLINE float strToF32(const char *str, const char **endptr) +{ + float ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + const uint32_t len = (uint32_t)(end - begin); + + const char F32_MIN[] = "NV_MIN_F32"; + const char F32_MAX[] = "NV_MAX_F32"; + const char PX_F32_MIN[] = "PX_MIN_F32"; + const char PX_F32_MAX[] = "PX_MAX_F32"; + + if( strncmp(begin, PX_F32_MIN, physx::PxMin(len, (uint32_t)(sizeof(PX_F32_MIN) - 1))) == 0) + ret = -PX_MAX_F32; + else if( strncmp(begin, PX_F32_MAX, physx::PxMin(len, (uint32_t)(sizeof(PX_F32_MAX) - 1))) == 0) + ret = PX_MAX_F32; + else if( strncmp(begin, F32_MIN, physx::PxMin(len, (uint32_t)(sizeof(F32_MIN) - 1))) == 0) + ret = -PX_MAX_F32; + else if( strncmp(begin, F32_MAX, physx::PxMin(len, (uint32_t)(sizeof(F32_MAX) - 1))) == 0) + ret = PX_MAX_F32; + else + { + ret = (float)strtof_fast(begin); + } + +#if DEBUGGING_MISMATCHES + float testRet = (float)atof(begin); + if( ret != testRet ) + { + PX_ASSERT(0 && "Inaccurate float string"); + } +#endif + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + + +PX_INLINE double strToF64(const char *str, const char **endptr) +{ + double ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + const uint32_t len = (const uint32_t)(end - begin); + + const char F64_MIN[] = "PX_MIN_F364"; + const char F64_MAX[] = "PX_MAX_F64"; + const char PX_F64_MIN[] = "PX_MIN_F64"; + const char PX_F64_MAX[] = "PX_MAX_F64"; + + if( strncmp(begin, F64_MIN, physx::PxMin(len, (uint32_t)(sizeof(F64_MIN) - 1))) == 0) + ret = -PX_MAX_F64; + else if( strncmp(begin, F64_MAX, physx::PxMin(len, (uint32_t)(sizeof(F64_MAX) - 1))) == 0) + ret = PX_MAX_F64; + else if( strncmp(begin, PX_F64_MIN, physx::PxMin(len, (uint32_t)(sizeof(PX_F64_MIN) - 1))) == 0) + ret = -PX_MAX_F64; + else if( strncmp(begin, PX_F64_MAX, physx::PxMin(len, (uint32_t)(sizeof(PX_F64_MAX) - 1))) == 0) + ret = PX_MAX_F64; + else + ret = (double)strtod_fast(begin); + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE void strToF32s(float *v,uint32_t count,const char *str, const char**endptr) +{ + const char *begin = skipWhiteSpace(str); + + if ( *begin == '(' ) begin++; + for (uint32_t i=0; i + +namespace physx +{ +namespace general_PxIOStream2 +{ + using namespace shdfnd; + +//Use this class if you want to use your own allocator +class PxFileBufferBase : public PxFileBuf +{ +public: + PxFileBufferBase(const char *fileName,OpenMode mode) + { + mOpenMode = mode; + mFph = NULL; + mFileLength = 0; + mSeekRead = 0; + mSeekWrite = 0; + mSeekCurrent = 0; + switch ( mode ) + { + case OPEN_READ_ONLY: + mFph = fopen(fileName,"rb"); + break; + case OPEN_WRITE_ONLY: + mFph = fopen(fileName,"wb"); + break; + case OPEN_READ_WRITE_NEW: + mFph = fopen(fileName,"wb+"); + break; + case OPEN_READ_WRITE_EXISTING: + mFph = fopen(fileName,"rb+"); + break; + case OPEN_FILE_NOT_FOUND: + break; + } + if ( mFph ) + { + fseek(mFph,0L,SEEK_END); + mFileLength = static_cast(ftell(mFph)); + fseek(mFph,0L,SEEK_SET); + } + else + { + mOpenMode = OPEN_FILE_NOT_FOUND; + } + } + + virtual ~PxFileBufferBase() + { + close(); + } + + virtual void close() + { + if( mFph ) + { + fclose(mFph); + mFph = 0; + } + } + + virtual SeekType isSeekable(void) const + { + return mSeekType; + } + + virtual uint32_t read(void* buffer, uint32_t size) + { + uint32_t ret = 0; + if ( mFph ) + { + setSeekRead(); + ret = static_cast(::fread(buffer,1,size,mFph)); + mSeekRead+=ret; + mSeekCurrent+=ret; + } + return ret; + } + + virtual uint32_t peek(void* buffer, uint32_t size) + { + uint32_t ret = 0; + if ( mFph ) + { + uint32_t loc = tellRead(); + setSeekRead(); + ret = static_cast(::fread(buffer,1,size,mFph)); + mSeekCurrent+=ret; + seekRead(loc); + } + return ret; + } + + virtual uint32_t write(const void* buffer, uint32_t size) + { + uint32_t ret = 0; + if ( mFph ) + { + setSeekWrite(); + ret = static_cast(::fwrite(buffer,1,size,mFph)); + mSeekWrite+=ret; + mSeekCurrent+=ret; + if ( mSeekWrite > mFileLength ) + { + mFileLength = mSeekWrite; + } + } + return ret; + } + + virtual uint32_t tellRead(void) const + { + return mSeekRead; + } + + virtual uint32_t tellWrite(void) const + { + return mSeekWrite; + } + + virtual uint32_t seekRead(uint32_t loc) + { + mSeekRead = loc; + if ( mSeekRead > mFileLength ) + { + mSeekRead = mFileLength; + } + return mSeekRead; + } + + virtual uint32_t seekWrite(uint32_t loc) + { + mSeekWrite = loc; + if ( mSeekWrite > mFileLength ) + { + mSeekWrite = mFileLength; + } + return mSeekWrite; + } + + virtual void flush(void) + { + if ( mFph ) + { + ::fflush(mFph); + } + } + + virtual OpenMode getOpenMode(void) const + { + return mOpenMode; + } + + virtual uint32_t getFileLength(void) const + { + return mFileLength; + } + +private: + // Moves the actual file pointer to the current read location + void setSeekRead(void) + { + if ( mSeekRead != mSeekCurrent && mFph ) + { + if ( mSeekRead >= mFileLength ) + { + fseek(mFph,0L,SEEK_END); + } + else + { + fseek(mFph,static_cast(mSeekRead),SEEK_SET); + } + mSeekCurrent = mSeekRead = static_cast(ftell(mFph)); + } + } + // Moves the actual file pointer to the current write location + void setSeekWrite(void) + { + if ( mSeekWrite != mSeekCurrent && mFph ) + { + if ( mSeekWrite >= mFileLength ) + { + fseek(mFph,0L,SEEK_END); + } + else + { + fseek(mFph,static_cast(mSeekWrite),SEEK_SET); + } + mSeekCurrent = mSeekWrite = static_cast(ftell(mFph)); + } + } + + + FILE *mFph; + uint32_t mSeekRead; + uint32_t mSeekWrite; + uint32_t mSeekCurrent; + uint32_t mFileLength; + SeekType mSeekType; + OpenMode mOpenMode; +}; + +//Use this class if you want to use PhysX memory allocator +class PsFileBuffer: public PxFileBufferBase, public UserAllocated +{ +public: + PsFileBuffer(const char *fileName,OpenMode mode): PxFileBufferBase(fileName, mode) {} +}; + +} +using namespace general_PxIOStream2; +} + +#endif // PSFILEBUFFER_PSFILEBUFFER_H diff --git a/src/PhysX/physx/source/filebuf/include/PsIOStream.h b/src/PhysX/physx/source/filebuf/include/PsIOStream.h new file mode 100644 index 000000000..e32330c91 --- /dev/null +++ b/src/PhysX/physx/source/filebuf/include/PsIOStream.h @@ -0,0 +1,137 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PSFILEBUFFER_PSIOSTREAM_H +#define PSFILEBUFFER_PSIOSTREAM_H + +/*! +\file +\brief PsIOStream class +*/ +#include "filebuf/PxFileBuf.h" + +#include "Ps.h" +#include "PsString.h" +#include +#include +#include "PsAsciiConversion.h" + +#define safePrintf physx::shdfnd::snprintf + +PX_PUSH_PACK_DEFAULT + +namespace physx +{ + namespace general_PxIOStream2 + { + +/** +\brief A wrapper class for physx::PxFileBuf that provides both binary and ASCII streaming capabilities +*/ +class PsIOStream +{ + static const uint32_t MAX_STREAM_STRING = 1024; +public: + /** + \param [in] stream the physx::PxFileBuf through which all reads and writes will be performed + \param [in] streamLen the length of the input data stream when de-serializing + */ + PsIOStream(physx::PxFileBuf &stream,uint32_t streamLen) : mBinary(true), mStreamLen(streamLen), mStream(stream) { } + ~PsIOStream(void) { } + + /** + \brief Set the stream to binary or ASCII + + \param [in] state if true, stream is binary, if false, stream is ASCII + + If the stream is binary, stream access is passed straight through to the respecitve + physx::PxFileBuf methods. If the stream is ASCII, all stream reads and writes are converted to + human readable ASCII. + */ + PX_INLINE void setBinary(bool state) { mBinary = state; } + PX_INLINE bool getBinary() { return mBinary; } + + PX_INLINE PsIOStream& operator<<(bool v); + PX_INLINE PsIOStream& operator<<(char c); + PX_INLINE PsIOStream& operator<<(uint8_t v); + PX_INLINE PsIOStream& operator<<(int8_t v); + + PX_INLINE PsIOStream& operator<<(const char *c); + PX_INLINE PsIOStream& operator<<(int64_t v); + PX_INLINE PsIOStream& operator<<(uint64_t v); + PX_INLINE PsIOStream& operator<<(double v); + PX_INLINE PsIOStream& operator<<(float v); + PX_INLINE PsIOStream& operator<<(uint32_t v); + PX_INLINE PsIOStream& operator<<(int32_t v); + PX_INLINE PsIOStream& operator<<(uint16_t v); + PX_INLINE PsIOStream& operator<<(int16_t v); + PX_INLINE PsIOStream& operator<<(const physx::PxVec3 &v); + PX_INLINE PsIOStream& operator<<(const physx::PxQuat &v); + PX_INLINE PsIOStream& operator<<(const physx::PxBounds3 &v); + + PX_INLINE PsIOStream& operator>>(const char *&c); + PX_INLINE PsIOStream& operator>>(bool &v); + PX_INLINE PsIOStream& operator>>(char &c); + PX_INLINE PsIOStream& operator>>(uint8_t &v); + PX_INLINE PsIOStream& operator>>(int8_t &v); + PX_INLINE PsIOStream& operator>>(int64_t &v); + PX_INLINE PsIOStream& operator>>(uint64_t &v); + PX_INLINE PsIOStream& operator>>(double &v); + PX_INLINE PsIOStream& operator>>(float &v); + PX_INLINE PsIOStream& operator>>(uint32_t &v); + PX_INLINE PsIOStream& operator>>(int32_t &v); + PX_INLINE PsIOStream& operator>>(uint16_t &v); + PX_INLINE PsIOStream& operator>>(int16_t &v); + PX_INLINE PsIOStream& operator>>(physx::PxVec3 &v); + PX_INLINE PsIOStream& operator>>(physx::PxQuat &v); + PX_INLINE PsIOStream& operator>>(physx::PxBounds3 &v); + + uint32_t getStreamLen(void) const { return mStreamLen; } + + physx::PxFileBuf& getStream(void) { return mStream; } + + PX_INLINE void storeString(const char *c,bool zeroTerminate=false); + +private: + PsIOStream& operator=( const PsIOStream& ); + + + bool mBinary; // true if we are serializing binary data. Otherwise, everything is assumed converted to ASCII + uint32_t mStreamLen; // the length of the input data stream when de-serializing. + physx::PxFileBuf &mStream; + char mReadString[MAX_STREAM_STRING]; // a temp buffer for streaming strings on input. +}; + +#include "PsIOStream.inl" // inline methods... + + } // end of namespace + using namespace general_PxIOStream2; +} // end of physx namespace + +PX_POP_PACK + +#endif // PSFILEBUFFER_PSIOSTREAM_H diff --git a/src/PhysX/physx/source/filebuf/include/PsIOStream.inl b/src/PhysX/physx/source/filebuf/include/PsIOStream.inl new file mode 100644 index 000000000..74f7e2066 --- /dev/null +++ b/src/PhysX/physx/source/filebuf/include/PsIOStream.inl @@ -0,0 +1,451 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + + +/* + * Copyright 2009-2018 NVIDIA Corporation. All rights reserved. + * + * NOTICE TO USER: + * + * This source code is subject to NVIDIA ownership rights under U.S. and + * international Copyright laws. Users and possessors of this source code + * are hereby granted a nonexclusive, royalty-free license to use this code + * in individual and commercial software. + * + * NVIDIA MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS SOURCE + * CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR + * IMPLIED WARRANTY OF ANY KIND. NVIDIA DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOURCE CODE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. + * IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL, + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE + * OR PERFORMANCE OF THIS SOURCE CODE. + * + * U.S. Government End Users. This source code is a "commercial item" as + * that term is defined at 48 C.F.R. 2.101 (OCT 1995), consisting of + * "commercial computer software" and "commercial computer software + * documentation" as such terms are used in 48 C.F.R. 12.212 (SEPT 1995) + * and is provided to the U.S. Government only as a commercial end item. + * Consistent with 48 C.F.R.12.212 and 48 C.F.R. 227.7202-1 through + * 227.7202-4 (JUNE 1995), all U.S. Government End Users acquire the + * source code with only those rights set forth herein. + * + * Any use of this source code in individual and commercial software must + * include, in the user documentation and internal comments to the code, + * the above Disclaimer and U.S. Government End Users Notice. + */ + +/*! +\file +\brief PsIOStream inline implementation +*/ + +PX_INLINE PsIOStream& PsIOStream::operator<<(bool v) +{ + if ( mBinary ) + { + mStream.storeByte((uint8_t)v); + } + else + { + char scratch[6]; + storeString( physx::PxAsc::valueToStr(v, scratch, 6) ); + } + return *this; +} + + +PX_INLINE PsIOStream& PsIOStream::operator<<(char c) +{ + mStream.storeByte((uint8_t)c); + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(uint8_t c) +{ + if ( mBinary ) + { + mStream.storeByte((uint8_t)c); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(c, scratch, physx::PxAsc::IntStrLen) ); + } + + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(int8_t c) +{ + if ( mBinary ) + { + mStream.storeByte((uint8_t)c); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(c, scratch, physx::PxAsc::IntStrLen) ); + } + + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(const char *c) +{ + if ( mBinary ) + { + c = c ? c : ""; // it it is a null pointer, assign it to an empty string. + uint32_t len = (uint32_t)strlen(c); + PX_ASSERT( len < (MAX_STREAM_STRING-1)); + if ( len > (MAX_STREAM_STRING-1) ) + { + len = MAX_STREAM_STRING-1; + } + mStream.storeDword(len); + if ( len ) + mStream.write(c,len); + } + else + { + storeString(c); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(uint64_t v) +{ + if ( mBinary ) + { + mStream.storeDouble( (double) v ); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(int64_t v) +{ + if ( mBinary ) + { + mStream.storeDouble( (double) v ); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(double v) +{ + if ( mBinary ) + { + mStream.storeDouble( (double) v ); + } + else + { + char scratch[physx::PxAsc::PxF64StrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::PxF64StrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(float v) +{ + if ( mBinary ) + { + mStream.storeFloat(v); + } + else + { + char scratch[physx::PxAsc::PxF32StrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::PxF32StrLen) ); + + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(uint32_t v) +{ + if ( mBinary ) + { + mStream.storeDword(v); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(int32_t v) +{ + if ( mBinary ) + { + mStream.storeDword( (uint32_t) v ); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(uint16_t v) +{ + if ( mBinary ) + { + mStream.storeWord(v); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(int16_t v) +{ + if ( mBinary ) + { + mStream.storeWord( (uint16_t) v ); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + + +PX_INLINE PsIOStream& PsIOStream::operator>>(uint32_t &v) +{ + if ( mBinary ) + { + v = mStream.readDword(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(char &v) +{ + if ( mBinary ) + { + v = (char)mStream.readByte(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(uint8_t &v) +{ + if ( mBinary ) + { + v = mStream.readByte(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(int8_t &v) +{ + if ( mBinary ) + { + v = (int8_t)mStream.readByte(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(int64_t &v) +{ + if ( mBinary ) + { + v = mStream.readDword(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(uint64_t &v) +{ + if ( mBinary ) + { + v = (uint64_t)mStream.readDouble(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(double &v) +{ + if ( mBinary ) + { + v = mStream.readDouble(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(float &v) +{ + if ( mBinary ) + { + v = mStream.readFloat(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(int32_t &v) +{ + if ( mBinary ) + { + v = (int32_t)mStream.readDword(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(uint16_t &v) +{ + if ( mBinary ) + { + v = mStream.readWord(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(int16_t &v) +{ + if ( mBinary ) + { + v = (int16_t)mStream.readWord(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(bool &v) +{ + int8_t iv; + iv = (int8_t)mStream.readByte(); + v = iv ? true : false; + return *this; +} + +#define NX_IOSTREAM_COMMA_SEPARATOR if(!mBinary) *this << ' '; + +PX_INLINE PsIOStream& PsIOStream::operator<<(const physx::PxVec3 &v) +{ + *this << v.x; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.y; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.z; + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(const physx::PxQuat &v) +{ + *this << v.x; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.y; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.z; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.w; + return *this; +} + + +PX_INLINE PsIOStream& PsIOStream::operator<<(const physx::PxBounds3 &v) +{ + *this << v.minimum; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.maximum; + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(physx::PxVec3 &v) +{ + *this >> v.x; + *this >> v.y; + *this >> v.z; + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(physx::PxQuat &v) +{ + *this>>v.x; + *this>>v.y; + *this>>v.z; + *this>>v.w; + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(physx::PxBounds3 &v) +{ + *this >> v.minimum; + *this >> v.maximum; + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(const char *&str) +{ + str = NULL; // by default no string streamed... + if ( mBinary ) + { + uint32_t len=0; + *this >> len; + + PX_ASSERT( len < (MAX_STREAM_STRING-1) ); + if ( len < (MAX_STREAM_STRING-1) ) + { + mStream.read(mReadString,len); + mReadString[len] = 0; + str = mReadString; + } + } + return *this; +} + + +PX_INLINE void PsIOStream::storeString(const char *c,bool zeroTerminate) +{ + while ( *c ) + { + mStream.storeByte((uint8_t)*c); + c++; + } + if ( zeroTerminate ) + { + mStream.storeByte(0); + } +} diff --git a/src/PhysX/physx/source/filebuf/include/PsMemoryBuffer.h b/src/PhysX/physx/source/filebuf/include/PsMemoryBuffer.h new file mode 100644 index 000000000..ae0bc8da5 --- /dev/null +++ b/src/PhysX/physx/source/filebuf/include/PsMemoryBuffer.h @@ -0,0 +1,449 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PSFILEBUFFER_PSMEMORYBUFFER_H +#define PSFILEBUFFER_PSMEMORYBUFFER_H + +#include "Ps.h" +#include "PsUserAllocated.h" +#include "PsAlignedMalloc.h" +#include "filebuf/PxFileBuf.h" +#include "foundation/PxAssert.h" + +namespace physx +{ +namespace general_PxIOStream2 +{ + using namespace shdfnd; + + const uint32_t BUFFER_SIZE_DEFAULT = 4096; + +//Use this class if you want to use your own allocator +template +class PxMemoryBufferBase : public PxFileBuf, public Allocator +{ + PX_NOCOPY(PxMemoryBufferBase) + void init(const void *readMem, uint32_t readLen) + { + mAllocator = this; + + mReadBuffer = mReadLoc = static_cast(readMem); + mReadStop = &mReadLoc[readLen]; + + mWriteBuffer = mWriteLoc = mWriteStop = NULL; + mWriteBufferSize = 0; + mDefaultWriteBufferSize = BUFFER_SIZE_DEFAULT; + + mOpenMode = OPEN_READ_ONLY; + mSeekType = SEEKABLE_READ; + } + + void init(uint32_t defaultWriteBufferSize) + { + mAllocator = this; + + mReadBuffer = mReadLoc = mReadStop = NULL; + + mWriteBuffer = mWriteLoc = mWriteStop = NULL; + mWriteBufferSize = 0; + mDefaultWriteBufferSize = defaultWriteBufferSize; + + mOpenMode = OPEN_READ_WRITE_NEW; + mSeekType = SEEKABLE_READWRITE; + } + +public: + PxMemoryBufferBase(const void *readMem,uint32_t readLen) + { + init(readMem, readLen); + } + + PxMemoryBufferBase(const void *readMem,uint32_t readLen, const Allocator &alloc): Allocator(alloc) + { + init(readMem, readLen); + } + + PxMemoryBufferBase(uint32_t defaultWriteBufferSize = BUFFER_SIZE_DEFAULT) + { + init(defaultWriteBufferSize); + } + + PxMemoryBufferBase(uint32_t defaultWriteBufferSize, const Allocator &alloc): Allocator(alloc) + { + init(defaultWriteBufferSize); + } + + virtual ~PxMemoryBufferBase(void) + { + reset(); + } + + void setAllocator(Allocator *allocator) + { + mAllocator = allocator; + } + + void initWriteBuffer(uint32_t size) + { + if ( mWriteBuffer == NULL ) + { + if ( size < mDefaultWriteBufferSize ) size = mDefaultWriteBufferSize; + mWriteBuffer = static_cast(mAllocator->allocate(size)); + PX_ASSERT( mWriteBuffer ); + mWriteLoc = mWriteBuffer; + mWriteStop = &mWriteBuffer[size]; + mWriteBufferSize = size; + mReadBuffer = mWriteBuffer; + mReadStop = &mWriteBuffer[size]; + mReadLoc = mWriteBuffer; + } + } + + void reset(void) + { + mAllocator->deallocate(mWriteBuffer); + mWriteBuffer = NULL; + mWriteBufferSize = 0; + mWriteLoc = NULL; + mWriteStop = NULL; + mReadBuffer = NULL; + mReadStop = NULL; + mReadLoc = NULL; + } + + virtual OpenMode getOpenMode(void) const + { + return mOpenMode; + } + + + SeekType isSeekable(void) const + { + return mSeekType; + } + + virtual uint32_t read(void* buffer, uint32_t size) + { + if ( (mReadLoc+size) > mReadStop ) + { + size = uint32_t(mReadStop - mReadLoc); + } + if ( size != 0 ) + { + memmove(buffer,mReadLoc,size); + mReadLoc+=size; + } + return size; + } + + virtual uint32_t peek(void* buffer, uint32_t size) + { + if ( (mReadLoc+size) > mReadStop ) + { + size = uint32_t(mReadStop - mReadLoc); + } + if ( size != 0 ) + { + memmove(buffer,mReadLoc,size); + } + return size; + } + + virtual uint32_t write(const void* buffer, uint32_t size) + { + PX_ASSERT( mOpenMode == OPEN_READ_WRITE_NEW ); + if ( mOpenMode == OPEN_READ_WRITE_NEW ) + { + if ( (mWriteLoc+size) > mWriteStop ) + growWriteBuffer(size); + memmove(mWriteLoc,buffer,size); + mWriteLoc+=size; + mReadStop = mWriteLoc; + } + else + { + size = 0; + } + return size; + } + + PX_INLINE const uint8_t * getReadLoc(void) const { return mReadLoc; } + PX_INLINE void advanceReadLoc(uint32_t len) + { + PX_ASSERT(mReadBuffer); + if ( mReadBuffer ) + { + mReadLoc+=len; + if ( mReadLoc >= mReadStop ) + { + mReadLoc = mReadStop; + } + } + } + + virtual uint32_t tellRead(void) const + { + uint32_t ret=0; + + if ( mReadBuffer ) + { + ret = uint32_t(mReadLoc-mReadBuffer); + } + return ret; + } + + virtual uint32_t tellWrite(void) const + { + return uint32_t(mWriteLoc-mWriteBuffer); + } + + virtual uint32_t seekRead(uint32_t loc) + { + uint32_t ret = 0; + PX_ASSERT(mReadBuffer); + if ( mReadBuffer ) + { + mReadLoc = &mReadBuffer[loc]; + if ( mReadLoc >= mReadStop ) + { + mReadLoc = mReadStop; + } + ret = uint32_t(mReadLoc-mReadBuffer); + } + return ret; + } + + virtual uint32_t seekWrite(uint32_t loc) + { + uint32_t ret = 0; + PX_ASSERT( mOpenMode == OPEN_READ_WRITE_NEW ); + if ( mWriteBuffer ) + { + if ( loc > mWriteBufferSize ) + { + mWriteLoc = mWriteStop; + growWriteBuffer(loc - mWriteBufferSize); + } + mWriteLoc = &mWriteBuffer[loc]; + ret = uint32_t(mWriteLoc-mWriteBuffer); + } + return ret; + } + + virtual void flush(void) + { + + } + + virtual uint32_t getFileLength(void) const + { + uint32_t ret = 0; + if ( mReadBuffer ) + { + ret = uint32_t(mReadStop-mReadBuffer); + } + else if ( mWriteBuffer ) + { + ret = uint32_t(mWriteLoc-mWriteBuffer); + } + return ret; + } + + uint32_t getWriteBufferSize(void) const + { + return uint32_t(mWriteLoc-mWriteBuffer); + } + + void setWriteLoc(uint8_t *writeLoc) + { + PX_ASSERT(writeLoc >= mWriteBuffer && writeLoc < mWriteStop ); + mWriteLoc = writeLoc; + mReadStop = mWriteLoc; + } + + const uint8_t * getWriteBuffer(void) const + { + return mWriteBuffer; + } + + /** + * Attention: if you use aligned allocator you cannot free memory with PX_FREE macros instead use deallocate method from base + */ + uint8_t * getWriteBufferOwnership(uint32_t &dataLen) // return the write buffer, and zero it out, the caller is taking ownership of the memory + { + uint8_t *ret = mWriteBuffer; + dataLen = uint32_t(mWriteLoc-mWriteBuffer); + mWriteBuffer = NULL; + mWriteLoc = NULL; + mWriteStop = NULL; + mWriteBufferSize = 0; + return ret; + } + + + void alignRead(uint32_t a) + { + uint32_t loc = tellRead(); + uint32_t aloc = ((loc+(a-1))/a)*a; + if ( aloc != loc ) + { + seekRead(aloc); + } + } + + void alignWrite(uint32_t a) + { + uint32_t loc = tellWrite(); + uint32_t aloc = ((loc+(a-1))/a)*a; + if ( aloc != loc ) + { + seekWrite(aloc); + } + } + +private: + + + // double the size of the write buffer or at least as large as the 'size' value passed in. + void growWriteBuffer(uint32_t size) + { + if ( mWriteBuffer == NULL ) + { + if ( size < mDefaultWriteBufferSize ) size = mDefaultWriteBufferSize; + initWriteBuffer(size); + } + else + { + uint32_t oldWriteIndex = uint32_t(mWriteLoc - mWriteBuffer); + uint32_t newSize = mWriteBufferSize*2; + uint32_t avail = newSize-oldWriteIndex; + if ( size >= avail ) newSize = newSize+size; + uint8_t *writeBuffer = static_cast(mAllocator->allocate(newSize)); + PX_ASSERT( writeBuffer ); + memmove(writeBuffer,mWriteBuffer,mWriteBufferSize); + mAllocator->deallocate(mWriteBuffer); + mWriteBuffer = writeBuffer; + mWriteBufferSize = newSize; + mWriteLoc = &mWriteBuffer[oldWriteIndex]; + mWriteStop = &mWriteBuffer[mWriteBufferSize]; + uint32_t oldReadLoc = uint32_t(mReadLoc-mReadBuffer); + mReadBuffer = mWriteBuffer; + mReadStop = mWriteLoc; + mReadLoc = &mReadBuffer[oldReadLoc]; + } + } + + const uint8_t *mReadBuffer; + const uint8_t *mReadLoc; + const uint8_t *mReadStop; + + uint8_t *mWriteBuffer; + uint8_t *mWriteLoc; + uint8_t *mWriteStop; + + uint32_t mWriteBufferSize; + uint32_t mDefaultWriteBufferSize; + Allocator *mAllocator; + OpenMode mOpenMode; + SeekType mSeekType; + +}; + +class PxMemoryBufferAllocator +{ +public: + PxMemoryBufferAllocator(uint32_t a = 0) : alignment(a) {} + + virtual void * allocate(uint32_t size) + { + switch(alignment) + { + case 0: + return PX_ALLOC(size, PX_DEBUG_EXP("PxMemoryBufferAllocator")); + case 16 : + return physx::AlignedAllocator<16>().allocate(size, __FILE__, __LINE__); + case 32 : + return physx::AlignedAllocator<32>().allocate(size, __FILE__, __LINE__); + case 64 : + return physx::AlignedAllocator<64>().allocate(size, __FILE__, __LINE__); + case 128 : + return physx::AlignedAllocator<128>().allocate(size, __FILE__, __LINE__); + default : + PX_ASSERT(0); + } + return NULL; + } + virtual void deallocate(void *mem) + { + switch(alignment) + { + case 0: + PX_FREE(mem); + break; + case 16 : + physx::AlignedAllocator<16>().deallocate(mem); + break; + case 32 : + physx::AlignedAllocator<32>().deallocate(mem); + break; + case 64 : + physx::AlignedAllocator<64>().deallocate(mem); + break; + case 128 : + physx::AlignedAllocator<128>().deallocate(mem); + break; + default : + PX_ASSERT(0); + } + } + virtual ~PxMemoryBufferAllocator(void) {} +private: + PxMemoryBufferAllocator& operator=(const PxMemoryBufferAllocator&); + + const uint32_t alignment; +}; + +//Use this class if you want to use PhysX memory allocator +class PsMemoryBuffer: public PxMemoryBufferBase, public UserAllocated +{ + PX_NOCOPY(PsMemoryBuffer) + typedef PxMemoryBufferBase BaseClass; + +public: + PsMemoryBuffer(const void *readMem,uint32_t readLen): BaseClass(readMem, readLen) {} + PsMemoryBuffer(const void *readMem,uint32_t readLen, uint32_t alignment): BaseClass(readMem, readLen, PxMemoryBufferAllocator(alignment)) {} + + PsMemoryBuffer(uint32_t defaultWriteBufferSize=BUFFER_SIZE_DEFAULT): BaseClass(defaultWriteBufferSize) {} + PsMemoryBuffer(uint32_t defaultWriteBufferSize,uint32_t alignment): BaseClass(defaultWriteBufferSize, PxMemoryBufferAllocator(alignment)) {} +}; + +} +using namespace general_PxIOStream2; +} + +#endif // PSFILEBUFFER_PSMEMORYBUFFER_H + diff --git a/src/PhysX/physx/source/foundation/include/Ps.h b/src/PhysX/physx/source/foundation/include/Ps.h new file mode 100644 index 000000000..7827ca13d --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/Ps.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PS_H +#define PSFOUNDATION_PS_H + +/*! \file top level include file for shared foundation */ + +#include "foundation/Px.h" + +/** +Platform specific defines +*/ +#if PX_WINDOWS_FAMILY || PX_XBOXONE +#pragma intrinsic(memcmp) +#pragma intrinsic(memcpy) +#pragma intrinsic(memset) +#pragma intrinsic(abs) +#pragma intrinsic(labs) +#endif + +// An expression that should expand to nothing in non PX_CHECKED builds. +// We currently use this only for tagging the purpose of containers for memory use tracking. +#if PX_CHECKED +#define PX_DEBUG_EXP(x) (x) +#else +#define PX_DEBUG_EXP(x) +#endif + +#define PX_SIGN_BITMASK 0x80000000 + +namespace physx +{ +namespace shdfnd +{ +// Int-as-bool type - has some uses for efficiency and with SIMD +typedef int IntBool; +static const IntBool IntFalse = 0; +static const IntBool IntTrue = 1; +} + +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PS_H diff --git a/src/PhysX/physx/source/foundation/include/PsAlignedMalloc.h b/src/PhysX/physx/source/foundation/include/PsAlignedMalloc.h new file mode 100644 index 000000000..50fdbe954 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsAlignedMalloc.h @@ -0,0 +1,88 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSALIGNEDMALLOC_H +#define PSFOUNDATION_PSALIGNEDMALLOC_H + +#include "PsUserAllocated.h" + +/*! +Allocate aligned memory. +Alignment must be a power of 2! +-- should be templated by a base allocator +*/ + +namespace physx +{ +namespace shdfnd +{ +/** +Allocator, which is used to access the global PxAllocatorCallback instance +(used for dynamic data types template instantiation), which can align memory +*/ + +// SCS: AlignedMalloc with 3 params not found, seems not used on PC either +// disabled for now to avoid GCC error + +template +class AlignedAllocator : public BaseAllocator +{ + public: + AlignedAllocator(const BaseAllocator& base = BaseAllocator()) : BaseAllocator(base) + { + } + + void* allocate(size_t size, const char* file, int line) + { + size_t pad = N - 1 + sizeof(size_t); // store offset for delete. + uint8_t* base = reinterpret_cast(BaseAllocator::allocate(size + pad, file, line)); + if(!base) + return NULL; + + uint8_t* ptr = reinterpret_cast(size_t(base + pad) & ~(size_t(N) - 1)); // aligned pointer, ensuring N + // is a size_t + // wide mask + reinterpret_cast(ptr)[-1] = size_t(ptr - base); // store offset + + return ptr; + } + void deallocate(void* ptr) + { + if(ptr == NULL) + return; + + uint8_t* base = reinterpret_cast(ptr) - reinterpret_cast(ptr)[-1]; + BaseAllocator::deallocate(base); + } +}; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSALIGNEDMALLOC_H diff --git a/src/PhysX/physx/source/foundation/include/PsAlloca.h b/src/PhysX/physx/source/foundation/include/PsAlloca.h new file mode 100644 index 000000000..a264eb72a --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsAlloca.h @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSALLOCA_H +#define PSFOUNDATION_PSALLOCA_H + +#include "PsTempAllocator.h" + +namespace physx +{ +namespace shdfnd +{ +template +class ScopedPointer : private Alloc +{ + public: + ~ScopedPointer() + { + if(mOwned) + Alloc::deallocate(mPointer); + } + + operator T*() const + { + return mPointer; + } + + T* mPointer; + bool mOwned; +}; + +} // namespace shdfnd +} // namespace physx + +/*! Stack allocation for \c count instances of \c type. Falling back to temp allocator if using more than 1kB. */ +#ifdef __SPU__ +#define PX_ALLOCA(var, type, count) type* var = reinterpret_cast(PxAlloca(sizeof(type) * (count))) +#else +#define PX_ALLOCA(var, type, count) \ + physx::shdfnd::ScopedPointer var; \ + { \ + uint32_t size = sizeof(type) * (count); \ + var.mOwned = size > 1024; \ + if(var.mOwned) \ + var.mPointer = reinterpret_cast(physx::shdfnd::TempAllocator().allocate(size, __FILE__, __LINE__)); \ + else \ + var.mPointer = reinterpret_cast(PxAlloca(size)); \ + } +#endif +#endif // #ifndef PSFOUNDATION_PSALLOCA_H diff --git a/src/PhysX/physx/source/foundation/include/PsAllocator.h b/src/PhysX/physx/source/foundation/include/PsAllocator.h new file mode 100644 index 000000000..9de5d81c0 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsAllocator.h @@ -0,0 +1,367 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSALLOCATOR_H +#define PSFOUNDATION_PSALLOCATOR_H + +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxAssert.h" +#include "PxFoundation.h" +#include "Ps.h" + +#if(PX_WINDOWS_FAMILY || PX_XBOXONE) +#include +#include +#endif +#if(PX_APPLE_FAMILY) +#include +#endif + +#include + +// Allocation macros going through user allocator +#if PX_CHECKED +#define PX_ALLOC(n, name) physx::shdfnd::NamedAllocator(name).allocate(n, __FILE__, __LINE__) +#else +#define PX_ALLOC(n, name) physx::shdfnd::NonTrackingAllocator().allocate(n, __FILE__, __LINE__) +#endif +#define PX_ALLOC_TEMP(n, name) PX_ALLOC(n, name) +#define PX_FREE(x) physx::shdfnd::NonTrackingAllocator().deallocate(x) +#define PX_FREE_AND_RESET(x) \ + { \ + PX_FREE(x); \ + x = 0; \ + } + +// The following macros support plain-old-types and classes derived from UserAllocated. +#define PX_NEW(T) new (physx::shdfnd::ReflectionAllocator(), __FILE__, __LINE__) T +#define PX_NEW_TEMP(T) PX_NEW(T) +#define PX_DELETE(x) delete x +#define PX_DELETE_AND_RESET(x) \ + { \ + PX_DELETE(x); \ + x = 0; \ + } +#define PX_DELETE_POD(x) \ + { \ + PX_FREE(x); \ + x = 0; \ + } +#define PX_DELETE_ARRAY(x) \ + { \ + PX_DELETE([] x); \ + x = 0; \ + } + +// aligned allocation +#define PX_ALIGNED16_ALLOC(n) physx::shdfnd::AlignedAllocator<16>().allocate(n, __FILE__, __LINE__) +#define PX_ALIGNED16_FREE(x) physx::shdfnd::AlignedAllocator<16>().deallocate(x) + +//! placement new macro to make it easy to spot bad use of 'new' +#define PX_PLACEMENT_NEW(p, T) new (p) T + +#if PX_DEBUG || PX_CHECKED +#define PX_USE_NAMED_ALLOCATOR 1 +#else +#define PX_USE_NAMED_ALLOCATOR 0 +#endif + +// Don't use inline for alloca !!! +#if PX_WINDOWS_FAMILY +#include +#define PxAlloca(x) _alloca(x) +#elif PX_LINUX || PX_ANDROID +#include +#define PxAlloca(x) alloca(x) +#elif PX_APPLE_FAMILY +#include +#define PxAlloca(x) alloca(x) +#elif PX_PS4 +#include +#define PxAlloca(x) alloca(x) +#elif PX_XBOXONE +#include +#define PxAlloca(x) alloca(x) +#elif PX_SWITCH +#include +#define PxAlloca(x) alloca(x) +#endif + +#define PxAllocaAligned(x, alignment) ((size_t(PxAlloca(x + alignment)) + (alignment - 1)) & ~size_t(alignment - 1)) + +namespace physx +{ +namespace shdfnd +{ + +PX_FOUNDATION_API PxAllocatorCallback& getAllocator(); + +/** +Allocator used to access the global PxAllocatorCallback instance without providing additional information. +*/ + +class PX_FOUNDATION_API Allocator +{ + public: + Allocator(const char* = 0) + { + } + void* allocate(size_t size, const char* file, int line); + void deallocate(void* ptr); +}; + +/* + * Bootstrap allocator using malloc/free. + * Don't use unless your objects get allocated before foundation is initialized. + */ +class RawAllocator +{ + public: + RawAllocator(const char* = 0) + { + } + void* allocate(size_t size, const char*, int) + { + // malloc returns valid pointer for size==0, no need to check + return ::malloc(size); + } + void deallocate(void* ptr) + { + // free(0) is guaranteed to have no side effect, no need to check + ::free(ptr); + } +}; + +/* + * Allocator that simply calls straight back to the application without tracking. + * This is used by the heap (Foundation::mNamedAllocMap) that tracks allocations + * because it needs to be able to grow as a result of an allocation. + * Making the hash table re-entrant to deal with this may not make sense. + */ +class NonTrackingAllocator +{ + public: + PX_FORCE_INLINE NonTrackingAllocator(const char* = 0) + { + } + PX_FORCE_INLINE void* allocate(size_t size, const char* file, int line) + { + return !size ? 0 : getAllocator().allocate(size, "NonTrackedAlloc", file, line); + } + PX_FORCE_INLINE void deallocate(void* ptr) + { + if(ptr) + getAllocator().deallocate(ptr); + } +}; + +/* +\brief Virtual allocator callback used to provide run-time defined allocators to foundation types like Array or Bitmap. + This is used by VirtualAllocator +*/ +class VirtualAllocatorCallback +{ + public: + VirtualAllocatorCallback() + { + } + virtual ~VirtualAllocatorCallback() + { + } + virtual void* allocate(const size_t size, const char* file, const int line) = 0; + virtual void deallocate(void* ptr) = 0; +}; + +/* +\brief Virtual allocator to be used by foundation types to provide run-time defined allocators. +Due to the fact that Array extends its allocator, rather than contains a reference/pointer to it, the VirtualAllocator +must +be a concrete type containing a pointer to a virtual callback. The callback may not be available at instantiation time, +therefore +methods are provided to set the callback later. +*/ +class VirtualAllocator +{ + public: + VirtualAllocator(VirtualAllocatorCallback* callback = NULL) : mCallback(callback) + { + } + + void* allocate(const size_t size, const char* file, const int line) + { + PX_ASSERT(mCallback); + if(size) + return mCallback->allocate(size, file, line); + return NULL; + } + void deallocate(void* ptr) + { + PX_ASSERT(mCallback); + if(ptr) + mCallback->deallocate(ptr); + } + + void setCallback(VirtualAllocatorCallback* callback) + { + mCallback = callback; + } + VirtualAllocatorCallback* getCallback() + { + return mCallback; + } + + private: + VirtualAllocatorCallback* mCallback; + VirtualAllocator& operator=(const VirtualAllocator&); +}; + +#if PX_USE_NAMED_ALLOCATOR // can be slow, so only use in debug/checked +class PX_FOUNDATION_API NamedAllocator +{ + public: + NamedAllocator(const PxEMPTY); + NamedAllocator(const char* name = 0); // todo: should not have default argument! + NamedAllocator(const NamedAllocator&); + ~NamedAllocator(); + NamedAllocator& operator=(const NamedAllocator&); + void* allocate(size_t size, const char* filename, int line); + void deallocate(void* ptr); +}; +#else +class NamedAllocator; +#endif // PX_DEBUG + +/** +Allocator used to access the global PxAllocatorCallback instance using a static name derived from T. +*/ + +template +class ReflectionAllocator +{ + static const char* getName() + { + if(!PxGetFoundation().getReportAllocationNames()) + return ""; +#if PX_GCC_FAMILY + return __PRETTY_FUNCTION__; +#else + // name() calls malloc(), raw_name() wouldn't + return typeid(T).name(); +#endif + } + + public: + ReflectionAllocator(const PxEMPTY) + { + } + ReflectionAllocator(const char* = 0) + { + } + inline ReflectionAllocator(const ReflectionAllocator&) + { + } + void* allocate(size_t size, const char* filename, int line) + { + return size ? getAllocator().allocate(size, getName(), filename, line) : 0; + } + void deallocate(void* ptr) + { + if(ptr) + getAllocator().deallocate(ptr); + } +}; + +template +struct AllocatorTraits +{ +#if PX_USE_NAMED_ALLOCATOR + typedef NamedAllocator Type; +#else + typedef ReflectionAllocator Type; +#endif +}; + +// if you get a build error here, you are trying to PX_NEW a class +// that is neither plain-old-type nor derived from UserAllocated +template +union EnableIfPod +{ + int i; + T t; + typedef X Type; +}; + +} // namespace shdfnd +} // namespace physx + +// Global placement new for ReflectionAllocator templated by +// plain-old-type. Allows using PX_NEW for pointers and built-in-types. +// +// ATTENTION: You need to use PX_DELETE_POD or PX_FREE to deallocate +// memory, not PX_DELETE. PX_DELETE_POD redirects to PX_FREE. +// +// Rationale: PX_DELETE uses global operator delete(void*), which we dont' want to overload. +// Any other definition of PX_DELETE couldn't support array syntax 'PX_DELETE([]a);'. +// PX_DELETE_POD was preferred over PX_DELETE_ARRAY because it is used +// less often and applies to both single instances and arrays. +template +PX_INLINE void* operator new(size_t size, physx::shdfnd::ReflectionAllocator alloc, const char* fileName, + typename physx::shdfnd::EnableIfPod::Type line) +{ + return alloc.allocate(size, fileName, line); +} + +template +PX_INLINE void* operator new [](size_t size, physx::shdfnd::ReflectionAllocator alloc, const char* fileName, + typename physx::shdfnd::EnableIfPod::Type line) +{ return alloc.allocate(size, fileName, line); } + +// If construction after placement new throws, this placement delete is being called. +template +PX_INLINE void operator delete(void* ptr, physx::shdfnd::ReflectionAllocator alloc, const char* fileName, + typename physx::shdfnd::EnableIfPod::Type line) +{ + PX_UNUSED(fileName); + PX_UNUSED(line); + + alloc.deallocate(ptr); +} + +// If construction after placement new throws, this placement delete is being called. +template +PX_INLINE void operator delete [](void* ptr, physx::shdfnd::ReflectionAllocator alloc, const char* fileName, + typename physx::shdfnd::EnableIfPod::Type line) +{ + PX_UNUSED(fileName); + PX_UNUSED(line); + + alloc.deallocate(ptr); +} + +#endif // #ifndef PSFOUNDATION_PSALLOCATOR_H diff --git a/src/PhysX/physx/source/foundation/include/PsAoS.h b/src/PhysX/physx/source/foundation/include/PsAoS.h new file mode 100644 index 000000000..128e99fb6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsAoS.h @@ -0,0 +1,45 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSAOS_H +#define PSFOUNDATION_PSAOS_H + +#include "foundation/Px.h" + +#if PX_WINDOWS && !PX_NEON +#include "windows/PsWindowsAoS.h" +#elif(PX_UNIX_FAMILY || PX_PS4 || PX_SWITCH) +#include "unix/PsUnixAoS.h" +#elif PX_XBOXONE +#include "XboxOne/PsXboxOneAoS.h" +#else +#error "Platform not supported!" +#endif + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsArray.h b/src/PhysX/physx/source/foundation/include/PsArray.h new file mode 100644 index 000000000..0282899a1 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsArray.h @@ -0,0 +1,721 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSARRAY_H +#define PSFOUNDATION_PSARRAY_H + +#include "foundation/PxAssert.h" +#include "foundation/PxIntrinsics.h" +#include "PsAllocator.h" +#include "PsBasicTemplates.h" + +namespace physx +{ +namespace shdfnd +{ +template +void exportArray(Serializer& stream, const void* data, uint32_t size, uint32_t sizeOfElement, uint32_t capacity); +char* importArray(char* address, void** data, uint32_t size, uint32_t sizeOfElement, uint32_t capacity); + +/*! +An array is a sequential container. + +Implementation note +* entries between 0 and size are valid objects +* we use inheritance to build this because the array is included inline in a lot + of objects and we want the allocator to take no space if it's not stateful, which + aggregation doesn't allow. Also, we want the metadata at the front for the inline + case where the allocator contains some inline storage space +*/ +template ::Type> +class Array : protected Alloc +{ + public: + typedef T* Iterator; + typedef const T* ConstIterator; + + explicit Array(const PxEMPTY v) : Alloc(v) + { + if(mData) + mCapacity |= PX_SIGN_BITMASK; + } + + /*! + Default array constructor. Initialize an empty array + */ + PX_INLINE explicit Array(const Alloc& alloc = Alloc()) : Alloc(alloc), mData(0), mSize(0), mCapacity(0) + { + } + + /*! + Initialize array with given capacity + */ + PX_INLINE explicit Array(uint32_t size, const T& a = T(), const Alloc& alloc = Alloc()) + : Alloc(alloc), mData(0), mSize(0), mCapacity(0) + { + resize(size, a); + } + + /*! + Copy-constructor. Copy all entries from other array + */ + template + PX_INLINE explicit Array(const Array& other, const Alloc& alloc = Alloc()) + : Alloc(alloc) + { + copy(other); + } + + // This is necessary else the basic default copy constructor is used in the case of both arrays being of the same + // template instance + // The C++ standard clearly states that a template constructor is never a copy constructor [2]. In other words, + // the presence of a template constructor does not suppress the implicit declaration of the copy constructor. + // Also never make a copy constructor explicit, or copy-initialization* will no longer work. This is because + // 'binding an rvalue to a const reference requires an accessible copy constructor' (http://gcc.gnu.org/bugs/) + // *http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy-initialization-and-assignment-initializ + PX_INLINE Array(const Array& other, const Alloc& alloc = Alloc()) : Alloc(alloc) + { + copy(other); + } + + /*! + Initialize array with given length + */ + PX_INLINE explicit Array(const T* first, const T* last, const Alloc& alloc = Alloc()) + : Alloc(alloc), mSize(last < first ? 0 : uint32_t(last - first)), mCapacity(mSize) + { + mData = allocate(mSize); + copy(mData, mData + mSize, first); + } + + /*! + Destructor + */ + PX_INLINE ~Array() + { + destroy(mData, mData + mSize); + + if(capacity() && !isInUserMemory()) + deallocate(mData); + } + + /*! + Assignment operator. Copy content (deep-copy) + */ + template + PX_INLINE Array& operator=(const Array& rhs) + { + if(&rhs == this) + return *this; + + clear(); + reserve(rhs.mSize); + copy(mData, mData + rhs.mSize, rhs.mData); + + mSize = rhs.mSize; + return *this; + } + + PX_INLINE Array& operator=(const Array& t) // Needs to be declared, see comment at copy-constructor + { + return operator=(t); + } + + /*! + Array indexing operator. + \param i + The index of the element that will be returned. + \return + The element i in the array. + */ + PX_FORCE_INLINE const T& operator[](uint32_t i) const + { + PX_ASSERT(i < mSize); + return mData[i]; + } + + /*! + Array indexing operator. + \param i + The index of the element that will be returned. + \return + The element i in the array. + */ + PX_FORCE_INLINE T& operator[](uint32_t i) + { + PX_ASSERT(i < mSize); + return mData[i]; + } + + /*! + Returns a pointer to the initial element of the array. + \return + a pointer to the initial element of the array. + */ + PX_FORCE_INLINE ConstIterator begin() const + { + return mData; + } + + PX_FORCE_INLINE Iterator begin() + { + return mData; + } + + /*! + Returns an iterator beyond the last element of the array. Do not dereference. + \return + a pointer to the element beyond the last element of the array. + */ + + PX_FORCE_INLINE ConstIterator end() const + { + return mData + mSize; + } + + PX_FORCE_INLINE Iterator end() + { + return mData + mSize; + } + + /*! + Returns a reference to the first element of the array. Undefined if the array is empty. + \return a reference to the first element of the array + */ + + PX_FORCE_INLINE const T& front() const + { + PX_ASSERT(mSize); + return mData[0]; + } + + PX_FORCE_INLINE T& front() + { + PX_ASSERT(mSize); + return mData[0]; + } + + /*! + Returns a reference to the last element of the array. Undefined if the array is empty + \return a reference to the last element of the array + */ + + PX_FORCE_INLINE const T& back() const + { + PX_ASSERT(mSize); + return mData[mSize - 1]; + } + + PX_FORCE_INLINE T& back() + { + PX_ASSERT(mSize); + return mData[mSize - 1]; + } + + /*! + Returns the number of entries in the array. This can, and probably will, + differ from the array capacity. + \return + The number of of entries in the array. + */ + PX_FORCE_INLINE uint32_t size() const + { + return mSize; + } + + /*! + Clears the array. + */ + PX_INLINE void clear() + { + destroy(mData, mData + mSize); + mSize = 0; + } + + /*! + Returns whether the array is empty (i.e. whether its size is 0). + \return + true if the array is empty + */ + PX_FORCE_INLINE bool empty() const + { + return mSize == 0; + } + + /*! + Finds the first occurrence of an element in the array. + \param a + The element to find. + */ + + PX_INLINE Iterator find(const T& a) + { + uint32_t index; + for(index = 0; index < mSize && mData[index] != a; index++) + ; + return mData + index; + } + + PX_INLINE ConstIterator find(const T& a) const + { + uint32_t index; + for(index = 0; index < mSize && mData[index] != a; index++) + ; + return mData + index; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Adds one element to the end of the array. Operation is O(1). + \param a + The element that will be added to this array. + */ + ///////////////////////////////////////////////////////////////////////// + + PX_FORCE_INLINE T& pushBack(const T& a) + { + if(capacity() <= mSize) + return growAndPushBack(a); + + PX_PLACEMENT_NEW(reinterpret_cast(mData + mSize), T)(a); + + return mData[mSize++]; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Returns the element at the end of the array. Only legal if the array is non-empty. + */ + ///////////////////////////////////////////////////////////////////////// + PX_INLINE T popBack() + { + PX_ASSERT(mSize); + T t = mData[mSize - 1]; + + mData[--mSize].~T(); + + return t; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Construct one element at the end of the array. Operation is O(1). + */ + ///////////////////////////////////////////////////////////////////////// + PX_INLINE T& insert() + { + if(capacity() <= mSize) + grow(capacityIncrement()); + + T* ptr = mData + mSize++; + new (ptr) T; // not 'T()' because PODs should not get default-initialized. + return *ptr; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Subtracts the element on position i from the array and replace it with + the last element. + Operation is O(1) + \param i + The position of the element that will be subtracted from this array. + */ + ///////////////////////////////////////////////////////////////////////// + PX_INLINE void replaceWithLast(uint32_t i) + { + PX_ASSERT(i < mSize); + mData[i] = mData[--mSize]; + + mData[mSize].~T(); + } + + PX_INLINE void replaceWithLast(Iterator i) + { + replaceWithLast(static_cast(i - mData)); + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Replaces the first occurrence of the element a with the last element + Operation is O(n) + \param a + The position of the element that will be subtracted from this array. + \return true if the element has been removed. + */ + ///////////////////////////////////////////////////////////////////////// + + PX_INLINE bool findAndReplaceWithLast(const T& a) + { + uint32_t index = 0; + while(index < mSize && mData[index] != a) + ++index; + if(index == mSize) + return false; + replaceWithLast(index); + return true; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Subtracts the element on position i from the array. Shift the entire + array one step. + Operation is O(n) + \param i + The position of the element that will be subtracted from this array. + */ + ///////////////////////////////////////////////////////////////////////// + PX_INLINE void remove(uint32_t i) + { + PX_ASSERT(i < mSize); + + T* it = mData + i; + it->~T(); + while (++i < mSize) + { + new (it) T(mData[i]); + ++it; + it->~T(); + } + --mSize; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Removes a range from the array. Shifts the array so order is maintained. + Operation is O(n) + \param begin + The starting position of the element that will be subtracted from this array. + \param count + The number of elments that will be subtracted from this array. + */ + ///////////////////////////////////////////////////////////////////////// + PX_INLINE void removeRange(uint32_t begin, uint32_t count) + { + PX_ASSERT(begin < mSize); + PX_ASSERT((begin + count) <= mSize); + + for(uint32_t i = 0; i < count; i++) + mData[begin + i].~T(); // call the destructor on the ones being removed first. + + T* dest = &mData[begin]; // location we are copying the tail end objects to + T* src = &mData[begin + count]; // start of tail objects + uint32_t move_count = mSize - (begin + count); // compute remainder that needs to be copied down + + for(uint32_t i = 0; i < move_count; i++) + { + new (dest) T(*src); // copy the old one to the new location + src->~T(); // call the destructor on the old location + dest++; + src++; + } + mSize -= count; + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Resize array + */ + ////////////////////////////////////////////////////////////////////////// + PX_NOINLINE void resize(const uint32_t size, const T& a = T()); + + PX_NOINLINE void resizeUninitialized(const uint32_t size); + + ////////////////////////////////////////////////////////////////////////// + /*! + Resize array such that only as much memory is allocated to hold the + existing elements + */ + ////////////////////////////////////////////////////////////////////////// + PX_INLINE void shrink() + { + recreate(mSize); + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Deletes all array elements and frees memory. + */ + ////////////////////////////////////////////////////////////////////////// + PX_INLINE void reset() + { + resize(0); + shrink(); + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Ensure that the array has at least size capacity. + */ + ////////////////////////////////////////////////////////////////////////// + PX_INLINE void reserve(const uint32_t capacity) + { + if(capacity > this->capacity()) + grow(capacity); + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Query the capacity(allocated mem) for the array. + */ + ////////////////////////////////////////////////////////////////////////// + PX_FORCE_INLINE uint32_t capacity() const + { + return mCapacity & ~PX_SIGN_BITMASK; + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Unsafe function to force the size of the array + */ + ////////////////////////////////////////////////////////////////////////// + PX_FORCE_INLINE void forceSize_Unsafe(uint32_t size) + { + PX_ASSERT(size <= mCapacity); + mSize = size; + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Swap contents of an array without allocating temporary storage + */ + ////////////////////////////////////////////////////////////////////////// + PX_INLINE void swap(Array& other) + { + shdfnd::swap(mData, other.mData); + shdfnd::swap(mSize, other.mSize); + shdfnd::swap(mCapacity, other.mCapacity); + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Assign a range of values to this vector (resizes to length of range) + */ + ////////////////////////////////////////////////////////////////////////// + PX_INLINE void assign(const T* first, const T* last) + { + resizeUninitialized(uint32_t(last - first)); + copy(begin(), end(), first); + } + + // We need one bit to mark arrays that have been deserialized from a user-provided memory block. + // For alignment & memory saving purpose we store that bit in the rarely used capacity member. + PX_FORCE_INLINE uint32_t isInUserMemory() const + { + return mCapacity & PX_SIGN_BITMASK; + } + + /// return reference to allocator + PX_INLINE Alloc& getAllocator() + { + return *this; + } + + protected: + // constructor for where we don't own the memory + Array(T* memory, uint32_t size, uint32_t capacity, const Alloc& alloc = Alloc()) + : Alloc(alloc), mData(memory), mSize(size), mCapacity(capacity | PX_SIGN_BITMASK) + { + } + + template + PX_NOINLINE void copy(const Array& other); + + PX_INLINE T* allocate(uint32_t size) + { + if(size > 0) + { + T* p = reinterpret_cast(Alloc::allocate(sizeof(T) * size, __FILE__, __LINE__)); +/** +Mark a specified amount of memory with 0xcd pattern. This is used to check that the meta data +definition for serialized classes is complete in checked builds. +*/ +#if PX_CHECKED + if(p) + { + for(uint32_t i = 0; i < (sizeof(T) * size); ++i) + reinterpret_cast(p)[i] = 0xcd; + } +#endif + return p; + } + return 0; + } + + PX_INLINE void deallocate(void* mem) + { + Alloc::deallocate(mem); + } + + static PX_INLINE void create(T* first, T* last, const T& a) + { + for(; first < last; ++first) + ::new (first) T(a); + } + + static PX_INLINE void copy(T* first, T* last, const T* src) + { + if(last <= first) + return; + + for(; first < last; ++first, ++src) + ::new (first) T(*src); + } + + static PX_INLINE void destroy(T* first, T* last) + { + for(; first < last; ++first) + first->~T(); + } + + /*! + Called when pushBack() needs to grow the array. + \param a The element that will be added to this array. + */ + PX_NOINLINE T& growAndPushBack(const T& a); + + /*! + Resizes the available memory for the array. + + \param capacity + The number of entries that the set should be able to hold. + */ + PX_INLINE void grow(uint32_t capacity) + { + PX_ASSERT(this->capacity() < capacity); + recreate(capacity); + } + + /*! + Creates a new memory block, copies all entries to the new block and destroys old entries. + + \param capacity + The number of entries that the set should be able to hold. + */ + PX_NOINLINE void recreate(uint32_t capacity); + + // The idea here is to prevent accidental bugs with pushBack or insert. Unfortunately + // it interacts badly with InlineArrays with smaller inline allocations. + // TODO(dsequeira): policy template arg, this is exactly what they're for. + PX_INLINE uint32_t capacityIncrement() const + { + const uint32_t capacity = this->capacity(); + return capacity == 0 ? 1 : capacity * 2; + } + + T* mData; + uint32_t mSize; + uint32_t mCapacity; +}; + +template +PX_NOINLINE void Array::resize(const uint32_t size, const T& a) +{ + reserve(size); + create(mData + mSize, mData + size, a); + destroy(mData + size, mData + mSize); + mSize = size; +} + +template +template +PX_NOINLINE void Array::copy(const Array& other) +{ + if(!other.empty()) + { + mData = allocate(mSize = mCapacity = other.size()); + copy(mData, mData + mSize, other.begin()); + } + else + { + mData = NULL; + mSize = 0; + mCapacity = 0; + } + + // mData = allocate(other.mSize); + // mSize = other.mSize; + // mCapacity = other.mSize; + // copy(mData, mData + mSize, other.mData); +} + +template +PX_NOINLINE void Array::resizeUninitialized(const uint32_t size) +{ + reserve(size); + mSize = size; +} + +template +PX_NOINLINE T& Array::growAndPushBack(const T& a) +{ + uint32_t capacity = capacityIncrement(); + + T* newData = allocate(capacity); + PX_ASSERT((!capacity) || (newData && (newData != mData))); + copy(newData, newData + mSize, mData); + + // inserting element before destroying old array + // avoids referencing destroyed object when duplicating array element. + PX_PLACEMENT_NEW(reinterpret_cast(newData + mSize), T)(a); + + destroy(mData, mData + mSize); + if(!isInUserMemory()) + deallocate(mData); + + mData = newData; + mCapacity = capacity; + + return mData[mSize++]; +} + +template +PX_NOINLINE void Array::recreate(uint32_t capacity) +{ + T* newData = allocate(capacity); + PX_ASSERT((!capacity) || (newData && (newData != mData))); + + copy(newData, newData + mSize, mData); + destroy(mData, mData + mSize); + if(!isInUserMemory()) + deallocate(mData); + + mData = newData; + mCapacity = capacity; +} + +template +PX_INLINE void swap(Array& x, Array& y) +{ + x.swap(y); +} + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSARRAY_H diff --git a/src/PhysX/physx/source/foundation/include/PsAtomic.h b/src/PhysX/physx/source/foundation/include/PsAtomic.h new file mode 100644 index 000000000..58ca22bd1 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsAtomic.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSATOMIC_H +#define PSFOUNDATION_PSATOMIC_H + +#include "Ps.h" + +namespace physx +{ +namespace shdfnd +{ +/* set *dest equal to val. Return the old value of *dest */ +PX_FOUNDATION_API int32_t atomicExchange(volatile int32_t* dest, int32_t val); + +/* if *dest == comp, replace with exch. Return original value of *dest */ +PX_FOUNDATION_API int32_t atomicCompareExchange(volatile int32_t* dest, int32_t exch, int32_t comp); + +/* if *dest == comp, replace with exch. Return original value of *dest */ +PX_FOUNDATION_API void* atomicCompareExchangePointer(volatile void** dest, void* exch, void* comp); + +/* increment the specified location. Return the incremented value */ +PX_FOUNDATION_API int32_t atomicIncrement(volatile int32_t* val); + +/* decrement the specified location. Return the decremented value */ +PX_FOUNDATION_API int32_t atomicDecrement(volatile int32_t* val); + +/* add delta to *val. Return the new value */ +PX_FOUNDATION_API int32_t atomicAdd(volatile int32_t* val, int32_t delta); + +/* compute the maximum of dest and val. Return the new value */ +PX_FOUNDATION_API int32_t atomicMax(volatile int32_t* val, int32_t val2); + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSATOMIC_H diff --git a/src/PhysX/physx/source/foundation/include/PsBasicTemplates.h b/src/PhysX/physx/source/foundation/include/PsBasicTemplates.h new file mode 100644 index 000000000..bd4f82da9 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsBasicTemplates.h @@ -0,0 +1,146 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSBASICTEMPLATES_H +#define PSFOUNDATION_PSBASICTEMPLATES_H + +#include "Ps.h" + +namespace physx +{ +namespace shdfnd +{ +template +struct Equal +{ + bool operator()(const A& a, const A& b) const + { + return a == b; + } +}; + +template +struct Less +{ + bool operator()(const A& a, const A& b) const + { + return a < b; + } +}; + +template +struct Greater +{ + bool operator()(const A& a, const A& b) const + { + return a > b; + } +}; + +template +class Pair +{ + public: + F first; + S second; + Pair() : first(F()), second(S()) + { + } + Pair(const F& f, const S& s) : first(f), second(s) + { + } + Pair(const Pair& p) : first(p.first), second(p.second) + { + } + // CN - fix for /.../PsBasicTemplates.h(61) : warning C4512: 'physx::shdfnd::Pair' : assignment operator could + // not be generated + Pair& operator=(const Pair& p) + { + first = p.first; + second = p.second; + return *this; + } + bool operator==(const Pair& p) const + { + return first == p.first && second == p.second; + } + bool operator<(const Pair& p) const + { + if(first < p.first) + return true; + else + return !(p.first < first) && (second < p.second); + } +}; + +template +struct LogTwo +{ + static const unsigned int value = LogTwo<(A >> 1)>::value + 1; +}; +template <> +struct LogTwo<1> +{ + static const unsigned int value = 0; +}; + +template +struct UnConst +{ + typedef T Type; +}; +template +struct UnConst +{ + typedef T Type; +}; + +template +T pointerOffset(void* p, ptrdiff_t offset) +{ + return reinterpret_cast(reinterpret_cast(p) + offset); +} +template +T pointerOffset(const void* p, ptrdiff_t offset) +{ + return reinterpret_cast(reinterpret_cast(p) + offset); +} + +template +PX_CUDA_CALLABLE PX_INLINE void swap(T& x, T& y) +{ + const T tmp = x; + x = y; + y = tmp; +} + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSBASICTEMPLATES_H diff --git a/src/PhysX/physx/source/foundation/include/PsBitUtils.h b/src/PhysX/physx/source/foundation/include/PsBitUtils.h new file mode 100644 index 000000000..b01fb532b --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsBitUtils.h @@ -0,0 +1,109 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSBITUTILS_H +#define PSFOUNDATION_PSBITUTILS_H + +#include "foundation/PxIntrinsics.h" +#include "foundation/PxAssert.h" +#include "PsIntrinsics.h" +#include "Ps.h" + +namespace physx +{ +namespace shdfnd +{ +PX_INLINE uint32_t bitCount(uint32_t v) +{ + // from http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + uint32_t const w = v - ((v >> 1) & 0x55555555); + uint32_t const x = (w & 0x33333333) + ((w >> 2) & 0x33333333); + return (((x + (x >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +PX_INLINE bool isPowerOfTwo(uint32_t x) +{ + return x != 0 && (x & (x - 1)) == 0; +} + +// "Next Largest Power of 2 +// Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm +// that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with +// the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next +// largest power of 2. For a 32-bit value:" +PX_INLINE uint32_t nextPowerOfTwo(uint32_t x) +{ + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return x + 1; +} + +/*! +Return the index of the highest set bit. Not valid for zero arg. +*/ + +PX_INLINE uint32_t lowestSetBit(uint32_t x) +{ + PX_ASSERT(x); + return lowestSetBitUnsafe(x); +} + +/*! +Return the index of the highest set bit. Not valid for zero arg. +*/ + +PX_INLINE uint32_t highestSetBit(uint32_t x) +{ + PX_ASSERT(x); + return highestSetBitUnsafe(x); +} + +// Helper function to approximate log2 of an integer value +// assumes that the input is actually power of two. +// todo: replace 2 usages with 'highestSetBit' +PX_INLINE uint32_t ilog2(uint32_t num) +{ + for(uint32_t i = 0; i < 32; i++) + { + num >>= 1; + if(num == 0) + return i; + } + + PX_ASSERT(0); + return uint32_t(-1); +} + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSBITUTILS_H diff --git a/src/PhysX/physx/source/foundation/include/PsBroadcast.h b/src/PhysX/physx/source/foundation/include/PsBroadcast.h new file mode 100644 index 000000000..242bf81f6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsBroadcast.h @@ -0,0 +1,277 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSBROADCAST_H +#define PSFOUNDATION_PSBROADCAST_H + +#include "Ps.h" +#include "PsInlineArray.h" + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxErrorCallback.h" + +namespace physx +{ +namespace shdfnd +{ + +/** +\brief Abstract listener class that listens to allocation and deallocation events from the + foundation memory system. + +Threading: All methods of this class should be thread safe as it can be called from the user thread +or the physics processing thread(s). +*/ +class AllocationListener +{ + public: + /** + \brief callback when memory is allocated. + \param size Size of the allocation in bytes. + \param typeName Type this data is being allocated for. + \param filename File the allocation came from. + \param line the allocation came from. + \param allocatedMemory memory that will be returned from the allocation. + */ + virtual void onAllocation(size_t size, const char* typeName, const char* filename, int line, + void* allocatedMemory) = 0; + + /** + \brief callback when memory is deallocated. + \param allocatedMemory memory just before allocation. + */ + virtual void onDeallocation(void* allocatedMemory) = 0; + + protected: + virtual ~AllocationListener() + { + } +}; + +/** +\brief Broadcast class implementation, registering listeners. + +Threading: All methods of this class should be thread safe as it can be called from the user thread +or the physics processing thread(s). There is not internal locking +*/ +template +class Broadcast : public Base +{ + public: + static const uint32_t MAX_NB_LISTENERS = 16; + + /** + \brief The default constructor. + */ + Broadcast() + { + } + + /** + \brief Register new listener. + + \note It is NOT SAFE to register and deregister listeners while allocations may be taking place. + moreover, there is no thread safety to registration/deregistration. + + \param listener Listener to register. + */ + void registerListener(Listener& listener) + { + if(mListeners.size() < MAX_NB_LISTENERS) + mListeners.pushBack(&listener); + } + + /** + \brief Deregister an existing listener. + + \note It is NOT SAFE to register and deregister listeners while allocations may be taking place. + moreover, there is no thread safety to registration/deregistration. + + \param listener Listener to deregister. + */ + void deregisterListener(Listener& listener) + { + mListeners.findAndReplaceWithLast(&listener); + } + + /** + \brief Get number of registered listeners. + + \return Number of listeners. + */ + uint32_t getNbListeners() const + { + return mListeners.size(); + } + + /** + \brief Get an existing listener from given index. + + \param index Index of the listener. + \return Listener on given index. + */ + Listener& getListener(uint32_t index) + { + PX_ASSERT(index <= mListeners.size()); + return *mListeners[index]; + } + + protected: + virtual ~Broadcast() + { + } + + physx::shdfnd::InlineArray mListeners; +}; + +/** +\brief Abstract base class for an application defined memory allocator that allows an external listener +to audit the memory allocations. +*/ +class BroadcastingAllocator : public Broadcast +{ + PX_NOCOPY(BroadcastingAllocator) + + public: + /** + \brief The default constructor. + */ + BroadcastingAllocator(PxAllocatorCallback& allocator, PxErrorCallback& error) : mAllocator(allocator), mError(error) + { + mListeners.clear(); + } + + /** + \brief The default constructor. + */ + virtual ~BroadcastingAllocator() + { + mListeners.clear(); + } + + /** + \brief Allocates size bytes of memory, which must be 16-byte aligned. + + This method should never return NULL. If you run out of memory, then + you should terminate the app or take some other appropriate action. + + Threading: This function should be thread safe as it can be called in the context of the user thread + and physics processing thread(s). + + \param size Number of bytes to allocate. + \param typeName Name of the datatype that is being allocated + \param filename The source file which allocated the memory + \param line The source line which allocated the memory + \return The allocated block of memory. + */ + void* allocate(size_t size, const char* typeName, const char* filename, int line) + { + void* mem = mAllocator.allocate(size, typeName, filename, line); + + if(!mem) + { + mError.reportError(PxErrorCode::eABORT, "User allocator returned NULL.", __FILE__, __LINE__); + return NULL; + } + + if((reinterpret_cast(mem) & 15)) + { + mError.reportError(PxErrorCode::eABORT, "Allocations must be 16-byte aligned.", __FILE__, __LINE__); + return NULL; + } + + for(uint32_t i = 0; i < mListeners.size(); i++) + mListeners[i]->onAllocation(size, typeName, filename, line, mem); + + return mem; + } + + /** + \brief Frees memory previously allocated by allocate(). + + Threading: This function should be thread safe as it can be called in the context of the user thread + and physics processing thread(s). + + \param ptr Memory to free. + */ + void deallocate(void* ptr) + { + for(uint32_t i = 0; i < mListeners.size(); i++) + { + mListeners[i]->onDeallocation(ptr); + } + mAllocator.deallocate(ptr); + } + + private: + PxAllocatorCallback& mAllocator; + PxErrorCallback& mError; +}; + +/** +\brief Abstract base class for an application defined error callback that allows an external listener +to report errors. +*/ +class BroadcastingErrorCallback : public Broadcast +{ + PX_NOCOPY(BroadcastingErrorCallback) + public: + /** + \brief The default constructor. + */ + BroadcastingErrorCallback(PxErrorCallback& errorCallback) + { + registerListener(errorCallback); + } + + /** + \brief The default destructor. + */ + virtual ~BroadcastingErrorCallback() + { + mListeners.clear(); + } + + /** + \brief Reports an error code. + \param code Error code, see #PxErrorCode + \param message Message to display. + \param file File error occured in. + \param line Line number error occured on. + */ + void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) + { + for(uint32_t i = 0; i < mListeners.size(); i++) + mListeners[i]->reportError(code, message, file, line); + } +}; +} +} // namespace physx + +#endif // PSFOUNDATION_PXBROADCAST_H diff --git a/src/PhysX/physx/source/foundation/include/PsCpu.h b/src/PhysX/physx/source/foundation/include/PsCpu.h new file mode 100644 index 000000000..11df8cf7e --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsCpu.h @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSCPU_H +#define PSFOUNDATION_PSCPU_H + +#include "Ps.h" + +namespace physx +{ +namespace shdfnd +{ +class Cpu +{ + public: + static uint8_t getCpuId(); +}; +} +} + +#endif // #ifndef PSFOUNDATION_PSCPU_H diff --git a/src/PhysX/physx/source/foundation/include/PsFPU.h b/src/PhysX/physx/source/foundation/include/PsFPU.h new file mode 100644 index 000000000..f599d21fc --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsFPU.h @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSFPU_H +#define PSFOUNDATION_PSFPU_H + +#include "Ps.h" +#include "PsIntrinsics.h" + +#define PX_IR(x) ((uint32_t&)(x)) +#define PX_SIR(x) ((int32_t&)(x)) +#define PX_FR(x) ((float&)(x)) + +// signed integer representation of a floating-point value. + +// Floating-point representation of a integer value. + +#define PX_SIGN_BITMASK 0x80000000 + +#define PX_FPU_GUARD shdfnd::FPUGuard scopedFpGuard; +#define PX_SIMD_GUARD shdfnd::SIMDGuard scopedFpGuard; + +#define PX_SUPPORT_GUARDS (PX_WINDOWS_FAMILY || PX_XBOXONE || (PX_LINUX && (PX_X86 || PX_X64)) || PX_PS4 || PX_OSX) + +namespace physx +{ +namespace shdfnd +{ +// sets the default SDK state for scalar and SIMD units +class PX_FOUNDATION_API FPUGuard +{ + public: + FPUGuard(); // set fpu control word for PhysX + ~FPUGuard(); // restore fpu control word + private: + uint32_t mControlWords[8]; +}; + +// sets default SDK state for simd unit only, lighter weight than FPUGuard +class SIMDGuard +{ + public: + PX_INLINE SIMDGuard(); // set simd control word for PhysX + PX_INLINE ~SIMDGuard(); // restore simd control word + private: +#if PX_SUPPORT_GUARDS + uint32_t mControlWord; +#endif +}; + +/** +\brief Enables floating point exceptions for the scalar and SIMD unit +*/ +PX_FOUNDATION_API void enableFPExceptions(); + +/** +\brief Disables floating point exceptions for the scalar and SIMD unit +*/ +PX_FOUNDATION_API void disableFPExceptions(); + +} // namespace shdfnd +} // namespace physx + +#if PX_WINDOWS_FAMILY || PX_XBOXONE +#include "windows/PsWindowsFPU.h" +#elif (PX_LINUX && PX_SSE2) || PX_PS4 || PX_OSX +#include "unix/PsUnixFPU.h" +#else +PX_INLINE physx::shdfnd::SIMDGuard::SIMDGuard() +{ +} +PX_INLINE physx::shdfnd::SIMDGuard::~SIMDGuard() +{ +} +#endif + +#endif // #ifndef PSFOUNDATION_PSFPU_H diff --git a/src/PhysX/physx/source/foundation/include/PsFoundation.h b/src/PhysX/physx/source/foundation/include/PsFoundation.h new file mode 100644 index 000000000..e95753fbd --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsFoundation.h @@ -0,0 +1,223 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_FOUNDATION_PSFOUNDATION_H +#define PX_FOUNDATION_PSFOUNDATION_H + +#include "foundation/PxErrors.h" +#include "foundation/PxProfiler.h" + +#include "PxFoundation.h" + +#include "PsBroadcast.h" +#include "PsAllocator.h" +#include "PsTempAllocator.h" +#include "PsMutex.h" +#include "PsHashMap.h" +#include "PsUserAllocated.h" + +#include + +namespace physx +{ +namespace shdfnd +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4251) // class needs to have dll-interface to be used by clients of class +#endif + +class PX_FOUNDATION_API Foundation : public PxFoundation, public UserAllocated +{ + PX_NOCOPY(Foundation) + + public: + typedef MutexT Mutex; + + typedef HashMap, NonTrackingAllocator> AllocNameMap; + typedef Array AllocFreeTable; + + public: + // factory + // note, you MUST eventually call release if createInstance returned true! + static Foundation* createInstance(PxU32 version, PxErrorCallback& errc, PxAllocatorCallback& alloc); + static Foundation& getInstance(); + static void setInstance(Foundation& foundation); + void release(); + static void incRefCount(); // this call requires a foundation object to exist already + static void decRefCount(); // this call requires a foundation object to exist already + + // Begin Errors + virtual PxErrorCallback& getErrorCallback() + { + return mErrorCallback; + } // Return the user's error callback + PxErrorCallback& getInternalErrorCallback() + { + return mBroadcastingError; + } // Return the broadcasting error callback + + void registerErrorCallback(PxErrorCallback& listener); + void deregisterErrorCallback(PxErrorCallback& listener); + + virtual void setErrorLevel(PxErrorCode::Enum mask) + { + mErrorMask = mask; + } + virtual PxErrorCode::Enum getErrorLevel() const + { + return mErrorMask; + } + + void error(PxErrorCode::Enum, const char* file, int line, const char* messageFmt, ...); // Report errors with the + // broadcasting + void errorImpl(PxErrorCode::Enum, const char* file, int line, const char* messageFmt, va_list); // error callback + static PxU32 getWarnOnceTimestamp(); + + // End errors + + // Begin Allocations + virtual PxAllocatorCallback& getAllocatorCallback() + { + return mAllocatorCallback; + } // Return the user's allocator callback + PxAllocatorCallback& getAllocator() + { + return mBroadcastingAllocator; + } // Return the broadcasting allocator + + void registerAllocationListener(physx::shdfnd::AllocationListener& listener); + void deregisterAllocationListener(physx::shdfnd::AllocationListener& listener); + + virtual bool getReportAllocationNames() const + { + return mReportAllocationNames; + } + virtual void setReportAllocationNames(bool value) + { + mReportAllocationNames = value; + } + + PX_INLINE AllocNameMap& getNamedAllocMap() + { + return mNamedAllocMap; + } + PX_INLINE Mutex& getNamedAllocMutex() + { + return mNamedAllocMutex; + } + + PX_INLINE AllocFreeTable& getTempAllocFreeTable() + { + return mTempAllocFreeTable; + } + PX_INLINE Mutex& getTempAllocMutex() + { + return mTempAllocMutex; + } + // End allocations + + private: + static void destroyInstance(); + + Foundation(PxErrorCallback& errc, PxAllocatorCallback& alloc); + ~Foundation(); + + // init order is tricky here: the mutexes require the allocator, the allocator may require the error stream + PxAllocatorCallback& mAllocatorCallback; + PxErrorCallback& mErrorCallback; + + BroadcastingAllocator mBroadcastingAllocator; + BroadcastingErrorCallback mBroadcastingError; + + bool mReportAllocationNames; + + PxErrorCode::Enum mErrorMask; + Mutex mErrorMutex; + + AllocNameMap mNamedAllocMap; + Mutex mNamedAllocMutex; + + AllocFreeTable mTempAllocFreeTable; + Mutex mTempAllocMutex; + + Mutex mListenerMutex; + + static Foundation* mInstance; + static PxU32 mRefCount; + static PxU32 mWarnOnceTimestap; +}; +#if PX_VC +#pragma warning(pop) +#endif + +PX_INLINE Foundation& getFoundation() +{ + return Foundation::getInstance(); +} + +PX_INLINE void setFoundationInstance(Foundation& foundation) +{ + Foundation::setInstance(foundation); +} + +} // namespace shdfnd +} // namespace physx + +// shortcut macros: +// usage: Foundation::error(PX_WARN, "static friction %f is is lower than dynamic friction %d", sfr, dfr); +#define PX_WARN ::physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__ +#define PX_INFO ::physx::PxErrorCode::eDEBUG_INFO, __FILE__, __LINE__ + +#if PX_DEBUG || PX_CHECKED +#define PX_WARN_ONCE(string) \ + { \ + static PxU32 timestamp = 0; \ + if(timestamp != Ps::getFoundation().getWarnOnceTimestamp()) \ + { \ + timestamp = Ps::getFoundation().getWarnOnceTimestamp(); \ + Ps::getFoundation().error(PX_WARN, string); \ + } \ + \ +} +#define PX_WARN_ONCE_IF(condition, string) \ + { \ + if(condition) \ + { \ + PX_WARN_ONCE(string) \ + } \ + \ +} +#else +#define PX_WARN_ONCE(string) ((void)0) +#define PX_WARN_ONCE_IF(condition, string) ((void)0) +#endif + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsHash.h b/src/PhysX/physx/source/foundation/include/PsHash.h new file mode 100644 index 000000000..786907033 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsHash.h @@ -0,0 +1,162 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSHASH_H +#define PSFOUNDATION_PSHASH_H + +#include "Ps.h" +#include "PsBasicTemplates.h" + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4302) +#endif + +#if PX_LINUX +#include "foundation/PxSimpleTypes.h" +#endif + +/*! +Central definition of hash functions +*/ + +namespace physx +{ +namespace shdfnd +{ +// Hash functions + +// Thomas Wang's 32 bit mix +// http://www.cris.com/~Ttwang/tech/inthash.htm +PX_FORCE_INLINE uint32_t hash(const uint32_t key) +{ + uint32_t k = key; + k += ~(k << 15); + k ^= (k >> 10); + k += (k << 3); + k ^= (k >> 6); + k += ~(k << 11); + k ^= (k >> 16); + return uint32_t(k); +} + +PX_FORCE_INLINE uint32_t hash(const int32_t key) +{ + return hash(uint32_t(key)); +} + +// Thomas Wang's 64 bit mix +// http://www.cris.com/~Ttwang/tech/inthash.htm +PX_FORCE_INLINE uint32_t hash(const uint64_t key) +{ + uint64_t k = key; + k += ~(k << 32); + k ^= (k >> 22); + k += ~(k << 13); + k ^= (k >> 8); + k += (k << 3); + k ^= (k >> 15); + k += ~(k << 27); + k ^= (k >> 31); + return uint32_t(UINT32_MAX & k); +} + +#if PX_APPLE_FAMILY +// hash for size_t, to make gcc happy +PX_INLINE uint32_t hash(const size_t key) +{ +#if PX_P64_FAMILY + return hash(uint64_t(key)); +#else + return hash(uint32_t(key)); +#endif +} +#endif + +// Hash function for pointers +PX_INLINE uint32_t hash(const void* ptr) +{ +#if PX_P64_FAMILY + return hash(uint64_t(ptr)); +#else + return hash(uint32_t(UINT32_MAX & size_t(ptr))); +#endif +} + +// Hash function for pairs +template +PX_INLINE uint32_t hash(const Pair& p) +{ + uint32_t seed = 0x876543; + uint32_t m = 1000007; + return hash(p.second) ^ (m * (hash(p.first) ^ (m * seed))); +} + +// hash object for hash map template parameter +template +struct Hash +{ + uint32_t operator()(const Key& k) const + { + return hash(k); + } + bool equal(const Key& k0, const Key& k1) const + { + return k0 == k1; + } +}; + +// specialization for strings +template <> +struct Hash +{ + public: + uint32_t operator()(const char* _string) const + { + // "DJB" string hash + const uint8_t* string = reinterpret_cast(_string); + uint32_t h = 5381; + for(const uint8_t* ptr = string; *ptr; ptr++) + h = ((h << 5) + h) ^ uint32_t(*ptr); + return h; + } + bool equal(const char* string0, const char* string1) const + { + return !strcmp(string0, string1); + } +}; + +} // namespace shdfnd +} // namespace physx + +#if PX_VC +#pragma warning(pop) +#endif + +#endif // #ifndef PSFOUNDATION_PSHASH_H diff --git a/src/PhysX/physx/source/foundation/include/PsHashInternals.h b/src/PhysX/physx/source/foundation/include/PsHashInternals.h new file mode 100644 index 000000000..89d0265e6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsHashInternals.h @@ -0,0 +1,795 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSHASHINTERNALS_H +#define PSFOUNDATION_PSHASHINTERNALS_H + +#include "PsBasicTemplates.h" +#include "PsArray.h" +#include "PsBitUtils.h" +#include "PsHash.h" +#include "foundation/PxIntrinsics.h" + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant +#endif +namespace physx +{ +namespace shdfnd +{ +namespace internal +{ +template +class HashBase : private Allocator +{ + void init(uint32_t initialTableSize, float loadFactor) + { + mBuffer = NULL; + mEntries = NULL; + mEntriesNext = NULL; + mHash = NULL; + mEntriesCapacity = 0; + mHashSize = 0; + mLoadFactor = loadFactor; + mFreeList = uint32_t(EOL); + mTimestamp = 0; + mEntriesCount = 0; + + if(initialTableSize) + reserveInternal(initialTableSize); + } + + public: + typedef Entry EntryType; + + HashBase(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : Allocator(PX_DEBUG_EXP("hashBase")) + { + init(initialTableSize, loadFactor); + } + + HashBase(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) : Allocator(alloc) + { + init(initialTableSize, loadFactor); + } + + HashBase(const Allocator& alloc) : Allocator(alloc) + { + init(64, 0.75f); + } + + ~HashBase() + { + destroy(); // No need to clear() + + if(mBuffer) + Allocator::deallocate(mBuffer); + } + + static const uint32_t EOL = 0xffffffff; + + PX_INLINE Entry* create(const Key& k, bool& exists) + { + uint32_t h = 0; + if(mHashSize) + { + h = hash(k); + uint32_t index = mHash[h]; + while(index != EOL && !HashFn().equal(GetKey()(mEntries[index]), k)) + index = mEntriesNext[index]; + exists = index != EOL; + if(exists) + return mEntries + index; + } + else + exists = false; + + if(freeListEmpty()) + { + grow(); + h = hash(k); + } + + uint32_t entryIndex = freeListGetNext(); + + mEntriesNext[entryIndex] = mHash[h]; + mHash[h] = entryIndex; + + mEntriesCount++; + mTimestamp++; + + return mEntries + entryIndex; + } + + PX_INLINE const Entry* find(const Key& k) const + { + if(!mEntriesCount) + return NULL; + + const uint32_t h = hash(k); + uint32_t index = mHash[h]; + while(index != EOL && !HashFn().equal(GetKey()(mEntries[index]), k)) + index = mEntriesNext[index]; + return index != EOL ? mEntries + index : NULL; + } + + PX_INLINE bool erase(const Key& k, Entry& e) + { + if(!mEntriesCount) + return false; + + const uint32_t h = hash(k); + uint32_t* ptr = mHash + h; + while(*ptr != EOL && !HashFn().equal(GetKey()(mEntries[*ptr]), k)) + ptr = mEntriesNext + *ptr; + + if(*ptr == EOL) + return false; + + PX_PLACEMENT_NEW(&e, Entry)(mEntries[*ptr]); + + return eraseInternal(ptr); + } + + PX_INLINE bool erase(const Key& k) + { + if(!mEntriesCount) + return false; + + const uint32_t h = hash(k); + uint32_t* ptr = mHash + h; + while(*ptr != EOL && !HashFn().equal(GetKey()(mEntries[*ptr]), k)) + ptr = mEntriesNext + *ptr; + + if(*ptr == EOL) + return false; + + return eraseInternal(ptr); + } + + PX_INLINE uint32_t size() const + { + return mEntriesCount; + } + + PX_INLINE uint32_t capacity() const + { + return mHashSize; + } + + void clear() + { + if(!mHashSize || mEntriesCount == 0) + return; + + destroy(); + + intrinsics::memSet(mHash, EOL, mHashSize * sizeof(uint32_t)); + + const uint32_t sizeMinus1 = mEntriesCapacity - 1; + for(uint32_t i = 0; i < sizeMinus1; i++) + { + prefetchLine(mEntriesNext + i, 128); + mEntriesNext[i] = i + 1; + } + mEntriesNext[mEntriesCapacity - 1] = uint32_t(EOL); + mFreeList = 0; + mEntriesCount = 0; + } + + void reserve(uint32_t size) + { + if(size > mHashSize) + reserveInternal(size); + } + + PX_INLINE const Entry* getEntries() const + { + return mEntries; + } + + PX_INLINE Entry* insertUnique(const Key& k) + { + PX_ASSERT(find(k) == NULL); + uint32_t h = hash(k); + + uint32_t entryIndex = freeListGetNext(); + + mEntriesNext[entryIndex] = mHash[h]; + mHash[h] = entryIndex; + + mEntriesCount++; + mTimestamp++; + + return mEntries + entryIndex; + } + + private: + void destroy() + { + for(uint32_t i = 0; i < mHashSize; i++) + { + for(uint32_t j = mHash[i]; j != EOL; j = mEntriesNext[j]) + mEntries[j].~Entry(); + } + } + + template + PX_NOINLINE void copy(const HashBase& other); + + // free list management - if we're coalescing, then we use mFreeList to hold + // the top of the free list and it should always be equal to size(). Otherwise, + // we build a free list in the next() pointers. + + PX_INLINE void freeListAdd(uint32_t index) + { + if(compacting) + { + mFreeList--; + PX_ASSERT(mFreeList == mEntriesCount); + } + else + { + mEntriesNext[index] = mFreeList; + mFreeList = index; + } + } + + PX_INLINE void freeListAdd(uint32_t start, uint32_t end) + { + if(!compacting) + { + for(uint32_t i = start; i < end - 1; i++) // add the new entries to the free list + mEntriesNext[i] = i + 1; + + // link in old free list + mEntriesNext[end - 1] = mFreeList; + PX_ASSERT(mFreeList != end - 1); + mFreeList = start; + } + else if(mFreeList == EOL) // don't reset the free ptr for the compacting hash unless it's empty + mFreeList = start; + } + + PX_INLINE uint32_t freeListGetNext() + { + PX_ASSERT(!freeListEmpty()); + if(compacting) + { + PX_ASSERT(mFreeList == mEntriesCount); + return mFreeList++; + } + else + { + uint32_t entryIndex = mFreeList; + mFreeList = mEntriesNext[mFreeList]; + return entryIndex; + } + } + + PX_INLINE bool freeListEmpty() const + { + if(compacting) + return mEntriesCount == mEntriesCapacity; + else + return mFreeList == EOL; + } + + PX_INLINE void replaceWithLast(uint32_t index) + { + PX_PLACEMENT_NEW(mEntries + index, Entry)(mEntries[mEntriesCount]); + mEntries[mEntriesCount].~Entry(); + mEntriesNext[index] = mEntriesNext[mEntriesCount]; + + uint32_t h = hash(GetKey()(mEntries[index])); + uint32_t* ptr; + for(ptr = mHash + h; *ptr != mEntriesCount; ptr = mEntriesNext + *ptr) + PX_ASSERT(*ptr != EOL); + *ptr = index; + } + + PX_INLINE uint32_t hash(const Key& k, uint32_t hashSize) const + { + return HashFn()(k) & (hashSize - 1); + } + + PX_INLINE uint32_t hash(const Key& k) const + { + return hash(k, mHashSize); + } + + PX_INLINE bool eraseInternal(uint32_t* ptr) + { + const uint32_t index = *ptr; + + *ptr = mEntriesNext[index]; + + mEntries[index].~Entry(); + + mEntriesCount--; + mTimestamp++; + + if (compacting && index != mEntriesCount) + replaceWithLast(index); + + freeListAdd(index); + return true; + } + + void reserveInternal(uint32_t size) + { + if(!isPowerOfTwo(size)) + size = nextPowerOfTwo(size); + + PX_ASSERT(!(size & (size - 1))); + + // decide whether iteration can be done on the entries directly + bool resizeCompact = compacting || freeListEmpty(); + + // define new table sizes + uint32_t oldEntriesCapacity = mEntriesCapacity; + uint32_t newEntriesCapacity = uint32_t(float(size) * mLoadFactor); + uint32_t newHashSize = size; + + // allocate new common buffer and setup pointers to new tables + uint8_t* newBuffer; + uint32_t* newHash; + uint32_t* newEntriesNext; + Entry* newEntries; + { + uint32_t newHashByteOffset = 0; + uint32_t newEntriesNextBytesOffset = newHashByteOffset + newHashSize * sizeof(uint32_t); + uint32_t newEntriesByteOffset = newEntriesNextBytesOffset + newEntriesCapacity * sizeof(uint32_t); + newEntriesByteOffset += (16 - (newEntriesByteOffset & 15)) & 15; + uint32_t newBufferByteSize = newEntriesByteOffset + newEntriesCapacity * sizeof(Entry); + + newBuffer = reinterpret_cast(Allocator::allocate(newBufferByteSize, __FILE__, __LINE__)); + PX_ASSERT(newBuffer); + + newHash = reinterpret_cast(newBuffer + newHashByteOffset); + newEntriesNext = reinterpret_cast(newBuffer + newEntriesNextBytesOffset); + newEntries = reinterpret_cast(newBuffer + newEntriesByteOffset); + } + + // initialize new hash table + intrinsics::memSet(newHash, uint32_t(EOL), newHashSize * sizeof(uint32_t)); + + // iterate over old entries, re-hash and create new entries + if(resizeCompact) + { + // check that old free list is empty - we don't need to copy the next entries + PX_ASSERT(compacting || mFreeList == EOL); + + for(uint32_t index = 0; index < mEntriesCount; ++index) + { + uint32_t h = hash(GetKey()(mEntries[index]), newHashSize); + newEntriesNext[index] = newHash[h]; + newHash[h] = index; + + PX_PLACEMENT_NEW(newEntries + index, Entry)(mEntries[index]); + mEntries[index].~Entry(); + } + } + else + { + // copy old free list, only required for non compact resizing + intrinsics::memCopy(newEntriesNext, mEntriesNext, mEntriesCapacity * sizeof(uint32_t)); + + for(uint32_t bucket = 0; bucket < mHashSize; bucket++) + { + uint32_t index = mHash[bucket]; + while(index != EOL) + { + uint32_t h = hash(GetKey()(mEntries[index]), newHashSize); + newEntriesNext[index] = newHash[h]; + PX_ASSERT(index != newHash[h]); + + newHash[h] = index; + + PX_PLACEMENT_NEW(newEntries + index, Entry)(mEntries[index]); + mEntries[index].~Entry(); + + index = mEntriesNext[index]; + } + } + } + + // swap buffer and pointers + Allocator::deallocate(mBuffer); + mBuffer = newBuffer; + mHash = newHash; + mHashSize = newHashSize; + mEntriesNext = newEntriesNext; + mEntries = newEntries; + mEntriesCapacity = newEntriesCapacity; + + freeListAdd(oldEntriesCapacity, newEntriesCapacity); + } + + void grow() + { + PX_ASSERT((mFreeList == EOL) || (compacting && (mEntriesCount == mEntriesCapacity))); + + uint32_t size = mHashSize == 0 ? 16 : mHashSize * 2; + reserve(size); + } + + uint8_t* mBuffer; + Entry* mEntries; + uint32_t* mEntriesNext; // same size as mEntries + uint32_t* mHash; + uint32_t mEntriesCapacity; + uint32_t mHashSize; + float mLoadFactor; + uint32_t mFreeList; + uint32_t mTimestamp; + uint32_t mEntriesCount; // number of entries + + public: + class Iter + { + public: + PX_INLINE Iter(HashBase& b) : mBucket(0), mEntry(uint32_t(b.EOL)), mTimestamp(b.mTimestamp), mBase(b) + { + if(mBase.mEntriesCapacity > 0) + { + mEntry = mBase.mHash[0]; + skip(); + } + } + + PX_INLINE void check() const + { + PX_ASSERT(mTimestamp == mBase.mTimestamp); + } + PX_INLINE const Entry& operator*() const + { + check(); + return mBase.mEntries[mEntry]; + } + PX_INLINE Entry& operator*() + { + check(); + return mBase.mEntries[mEntry]; + } + PX_INLINE const Entry* operator->() const + { + check(); + return mBase.mEntries + mEntry; + } + PX_INLINE Entry* operator->() + { + check(); + return mBase.mEntries + mEntry; + } + PX_INLINE Iter operator++() + { + check(); + advance(); + return *this; + } + PX_INLINE Iter operator++(int) + { + check(); + Iter i = *this; + advance(); + return i; + } + PX_INLINE bool done() const + { + check(); + return mEntry == mBase.EOL; + } + + private: + PX_INLINE void advance() + { + mEntry = mBase.mEntriesNext[mEntry]; + skip(); + } + PX_INLINE void skip() + { + while(mEntry == mBase.EOL) + { + if(++mBucket == mBase.mHashSize) + break; + mEntry = mBase.mHash[mBucket]; + } + } + + Iter& operator=(const Iter&); + + uint32_t mBucket; + uint32_t mEntry; + uint32_t mTimestamp; + HashBase& mBase; + }; + + /*! + Iterate over entries in a hash base and allow entry erase while iterating + */ + class EraseIterator + { + public: + PX_INLINE EraseIterator(HashBase& b): mBase(b) + { + reset(); + } + + PX_INLINE Entry* eraseCurrentGetNext(bool eraseCurrent) + { + if(eraseCurrent && mCurrentEntryIndexPtr) + { + mBase.eraseInternal(mCurrentEntryIndexPtr); + // if next was valid return the same ptr, if next was EOL search new hash entry + if(*mCurrentEntryIndexPtr != mBase.EOL) + return mBase.mEntries + *mCurrentEntryIndexPtr; + else + return traverseHashEntries(); + } + + // traverse mHash to find next entry + if(mCurrentEntryIndexPtr == NULL) + return traverseHashEntries(); + + const uint32_t index = *mCurrentEntryIndexPtr; + if(mBase.mEntriesNext[index] == mBase.EOL) + { + return traverseHashEntries(); + } + else + { + mCurrentEntryIndexPtr = mBase.mEntriesNext + index; + return mBase.mEntries + *mCurrentEntryIndexPtr; + } + } + + PX_INLINE void reset() + { + mCurrentHashIndex = 0; + mCurrentEntryIndexPtr = NULL; + } + + private: + PX_INLINE Entry* traverseHashEntries() + { + mCurrentEntryIndexPtr = NULL; + while (mCurrentEntryIndexPtr == NULL && mCurrentHashIndex < mBase.mHashSize) + { + if (mBase.mHash[mCurrentHashIndex] != mBase.EOL) + { + mCurrentEntryIndexPtr = mBase.mHash + mCurrentHashIndex; + mCurrentHashIndex++; + return mBase.mEntries + *mCurrentEntryIndexPtr; + } + else + { + mCurrentHashIndex++; + } + } + return NULL; + } + + EraseIterator& operator=(const EraseIterator&); + private: + uint32_t* mCurrentEntryIndexPtr; + uint32_t mCurrentHashIndex; + HashBase& mBase; + }; +}; + +template +template +PX_NOINLINE void +HashBase::copy(const HashBase& other) +{ + reserve(other.mEntriesCount); + + for(uint32_t i = 0; i < other.mEntriesCount; i++) + { + for(uint32_t j = other.mHash[i]; j != EOL; j = other.mEntriesNext[j]) + { + const Entry& otherEntry = other.mEntries[j]; + + bool exists; + Entry* newEntry = create(GK()(otherEntry), exists); + PX_ASSERT(!exists); + + PX_PLACEMENT_NEW(newEntry, Entry)(otherEntry); + } + } +} + +template ::Type, bool Coalesced = false> +class HashSetBase +{ + PX_NOCOPY(HashSetBase) + public: + struct GetKey + { + PX_INLINE const Key& operator()(const Key& e) + { + return e; + } + }; + + typedef HashBase BaseMap; + typedef typename BaseMap::Iter Iterator; + + HashSetBase(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) + : mBase(initialTableSize, loadFactor, alloc) + { + } + + HashSetBase(const Allocator& alloc) : mBase(64, 0.75f, alloc) + { + } + + HashSetBase(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : mBase(initialTableSize, loadFactor) + { + } + + bool insert(const Key& k) + { + bool exists; + Key* e = mBase.create(k, exists); + if(!exists) + PX_PLACEMENT_NEW(e, Key)(k); + return !exists; + } + + PX_INLINE bool contains(const Key& k) const + { + return mBase.find(k) != 0; + } + PX_INLINE bool erase(const Key& k) + { + return mBase.erase(k); + } + PX_INLINE uint32_t size() const + { + return mBase.size(); + } + PX_INLINE uint32_t capacity() const + { + return mBase.capacity(); + } + PX_INLINE void reserve(uint32_t size) + { + mBase.reserve(size); + } + PX_INLINE void clear() + { + mBase.clear(); + } + + protected: + BaseMap mBase; +}; + +template >::Type> +class HashMapBase +{ + PX_NOCOPY(HashMapBase) + public: + typedef Pair Entry; + + struct GetKey + { + PX_INLINE const Key& operator()(const Entry& e) + { + return e.first; + } + }; + + typedef HashBase BaseMap; + typedef typename BaseMap::Iter Iterator; + typedef typename BaseMap::EraseIterator EraseIterator; + + HashMapBase(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) + : mBase(initialTableSize, loadFactor, alloc) + { + } + + HashMapBase(const Allocator& alloc) : mBase(64, 0.75f, alloc) + { + } + + HashMapBase(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : mBase(initialTableSize, loadFactor) + { + } + + bool insert(const Key /*&*/ k, const Value /*&*/ v) + { + bool exists; + Entry* e = mBase.create(k, exists); + if(!exists) + PX_PLACEMENT_NEW(e, Entry)(k, v); + return !exists; + } + + Value& operator[](const Key& k) + { + bool exists; + Entry* e = mBase.create(k, exists); + if(!exists) + PX_PLACEMENT_NEW(e, Entry)(k, Value()); + + return e->second; + } + + PX_INLINE const Entry* find(const Key& k) const + { + return mBase.find(k); + } + PX_INLINE bool erase(const Key& k) + { + return mBase.erase(k); + } + PX_INLINE bool erase(const Key& k, Entry& e) + { + return mBase.erase(k, e); + } + PX_INLINE uint32_t size() const + { + return mBase.size(); + } + PX_INLINE uint32_t capacity() const + { + return mBase.capacity(); + } + PX_INLINE Iterator getIterator() + { + return Iterator(mBase); + } + PX_INLINE EraseIterator getEraseIterator() + { + return EraseIterator(mBase); + } + PX_INLINE void reserve(uint32_t size) + { + mBase.reserve(size); + } + PX_INLINE void clear() + { + mBase.clear(); + } + + protected: + BaseMap mBase; +}; +} + +} // namespace shdfnd +} // namespace physx + +#if PX_VC +#pragma warning(pop) +#endif +#endif // #ifndef PSFOUNDATION_PSHASHINTERNALS_H diff --git a/src/PhysX/physx/source/foundation/include/PsHashMap.h b/src/PhysX/physx/source/foundation/include/PsHashMap.h new file mode 100644 index 000000000..f5913fb96 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsHashMap.h @@ -0,0 +1,118 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSHASHMAP_H +#define PSFOUNDATION_PSHASHMAP_H + +#include "PsHashInternals.h" + +// TODO: make this doxy-format +// +// This header defines two hash maps. Hash maps +// * support custom initial table sizes (rounded up internally to power-of-2) +// * support custom static allocator objects +// * auto-resize, based on a load factor (i.e. a 64-entry .75 load factor hash will resize +// when the 49th element is inserted) +// * are based on open hashing +// * have O(1) contains, erase +// +// Maps have STL-like copying semantics, and properly initialize and destruct copies of objects +// +// There are two forms of map: coalesced and uncoalesced. Coalesced maps keep the entries in the +// initial segment of an array, so are fast to iterate over; however deletion is approximately +// twice as expensive. +// +// HashMap: +// bool insert(const Key& k, const Value& v) O(1) amortized (exponential resize policy) +// Value & operator[](const Key& k) O(1) for existing objects, else O(1) amortized +// const Entry * find(const Key& k); O(1) +// bool erase(const T& k); O(1) +// uint32_t size(); constant +// void reserve(uint32_t size); O(MAX(currentOccupancy,size)) +// void clear(); O(currentOccupancy) (with zero constant for objects +// without +// destructors) +// Iterator getIterator(); +// +// operator[] creates an entry if one does not exist, initializing with the default constructor. +// CoalescedHashMap does not support getIterator, but instead supports +// const Key *getEntries(); +// +// Use of iterators: +// +// for(HashMap::Iterator iter = test.getIterator(); !iter.done(); ++iter) +// myFunction(iter->first, iter->second); + +namespace physx +{ +namespace shdfnd +{ +template , class Allocator = NonTrackingAllocator> +class HashMap : public internal::HashMapBase +{ + public: + typedef internal::HashMapBase HashMapBase; + typedef typename HashMapBase::Iterator Iterator; + + HashMap(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : HashMapBase(initialTableSize, loadFactor) + { + } + HashMap(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) + : HashMapBase(initialTableSize, loadFactor, alloc) + { + } + HashMap(const Allocator& alloc) : HashMapBase(64, 0.75f, alloc) + { + } + Iterator getIterator() + { + return Iterator(HashMapBase::mBase); + } +}; + +template , class Allocator = NonTrackingAllocator> +class CoalescedHashMap : public internal::HashMapBase +{ + public: + typedef internal::HashMapBase HashMapBase; + + CoalescedHashMap(uint32_t initialTableSize = 64, float loadFactor = 0.75f) + : HashMapBase(initialTableSize, loadFactor) + { + } + const Pair* getEntries() const + { + return HashMapBase::mBase.getEntries(); + } +}; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSHASHMAP_H diff --git a/src/PhysX/physx/source/foundation/include/PsHashSet.h b/src/PhysX/physx/source/foundation/include/PsHashSet.h new file mode 100644 index 000000000..95eae09a6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsHashSet.h @@ -0,0 +1,127 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSHASHSET_H +#define PSFOUNDATION_PSHASHSET_H + +#include "PsHashInternals.h" + +// TODO: make this doxy-format + +// This header defines two hash sets. Hash sets +// * support custom initial table sizes (rounded up internally to power-of-2) +// * support custom static allocator objects +// * auto-resize, based on a load factor (i.e. a 64-entry .75 load factor hash will resize +// when the 49th element is inserted) +// * are based on open hashing +// +// Sets have STL-like copying semantics, and properly initialize and destruct copies of objects +// +// There are two forms of set: coalesced and uncoalesced. Coalesced sets keep the entries in the +// initial segment of an array, so are fast to iterate over; however deletion is approximately +// twice as expensive. +// +// HashSet: +// bool insert(const T& k) amortized O(1) (exponential resize policy) +// bool contains(const T& k) const; O(1) +// bool erase(const T& k); O(1) +// uint32_t size() const; constant +// void reserve(uint32_t size); O(MAX(size, currentOccupancy)) +// void clear(); O(currentOccupancy) (with zero constant for objects without +// destructors) +// Iterator getIterator(); +// +// Use of iterators: +// +// for(HashSet::Iterator iter = test.getIterator(); !iter.done(); ++iter) +// myFunction(*iter); +// +// CoalescedHashSet does not support getIterator, but instead supports +// const Key *getEntries(); +// +// insertion into a set already containing the element fails returning false, as does +// erasure of an element not in the set +// + +namespace physx +{ +namespace shdfnd +{ +template , class Allocator = NonTrackingAllocator> +class HashSet : public internal::HashSetBase +{ + public: + typedef internal::HashSetBase HashSetBase; + typedef typename HashSetBase::Iterator Iterator; + + HashSet(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : HashSetBase(initialTableSize, loadFactor) + { + } + HashSet(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) + : HashSetBase(initialTableSize, loadFactor, alloc) + { + } + HashSet(const Allocator& alloc) : HashSetBase(64, 0.75f, alloc) + { + } + Iterator getIterator() + { + return Iterator(HashSetBase::mBase); + } +}; + +template , class Allocator = NonTrackingAllocator> +class CoalescedHashSet : public internal::HashSetBase +{ + public: + typedef typename internal::HashSetBase HashSetBase; + + CoalescedHashSet(uint32_t initialTableSize = 64, float loadFactor = 0.75f) + : HashSetBase(initialTableSize, loadFactor) + { + } + + CoalescedHashSet(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) + : HashSetBase(initialTableSize, loadFactor, alloc) + { + } + CoalescedHashSet(const Allocator& alloc) : HashSetBase(64, 0.75f, alloc) + { + } + + const Key* getEntries() const + { + return HashSetBase::mBase.getEntries(); + } +}; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSHASHSET_H diff --git a/src/PhysX/physx/source/foundation/include/PsInlineAllocator.h b/src/PhysX/physx/source/foundation/include/PsInlineAllocator.h new file mode 100644 index 000000000..9acdc9120 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsInlineAllocator.h @@ -0,0 +1,91 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSINLINEALLOCATOR_H +#define PSFOUNDATION_PSINLINEALLOCATOR_H + +#include "PsUserAllocated.h" + +namespace physx +{ +namespace shdfnd +{ +// this is used by the array class to allocate some space for a small number +// of objects along with the metadata +template +class InlineAllocator : private BaseAllocator +{ + public: + InlineAllocator(const PxEMPTY v) : BaseAllocator(v) + { + } + + InlineAllocator(const BaseAllocator& alloc = BaseAllocator()) : BaseAllocator(alloc), mBufferUsed(false) + { + } + + InlineAllocator(const InlineAllocator& aloc) : BaseAllocator(aloc), mBufferUsed(false) + { + } + + void* allocate(uint32_t size, const char* filename, int line) + { + if(!mBufferUsed && size <= N) + { + mBufferUsed = true; + return mBuffer; + } + return BaseAllocator::allocate(size, filename, line); + } + + void deallocate(void* ptr) + { + if(ptr == mBuffer) + mBufferUsed = false; + else + BaseAllocator::deallocate(ptr); + } + + PX_FORCE_INLINE uint8_t* getInlineBuffer() + { + return mBuffer; + } + PX_FORCE_INLINE bool isBufferUsed() const + { + return mBufferUsed; + } + + protected: + uint8_t mBuffer[N]; + bool mBufferUsed; +}; +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSINLINEALLOCATOR_H diff --git a/src/PhysX/physx/source/foundation/include/PsInlineAoS.h b/src/PhysX/physx/source/foundation/include/PsInlineAoS.h new file mode 100644 index 000000000..f08405758 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsInlineAoS.h @@ -0,0 +1,48 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSINLINEAOS_H +#define PSFOUNDATION_PSINLINEAOS_H + +#include "foundation/PxPreprocessor.h" + +#if PX_WINDOWS +#include "windows/PsWindowsTrigConstants.h" +#include "windows/PsWindowsInlineAoS.h" +#elif(PX_UNIX_FAMILY || PX_PS4 || PX_SWITCH || (PX_UWP && PX_NEON)) +#include "unix/PsUnixTrigConstants.h" +#include "unix/PsUnixInlineAoS.h" +#elif PX_XBOXONE +#include "XboxOne/PsXboxOneTrigConstants.h" +#include "XboxOne/PsXboxOneInlineAoS.h" +#else +#error "Platform not supported!" +#endif + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsInlineArray.h b/src/PhysX/physx/source/foundation/include/PsInlineArray.h new file mode 100644 index 000000000..c7fed5eb2 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsInlineArray.h @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSINLINEARRAY_H +#define PSFOUNDATION_PSINLINEARRAY_H + +#include "PsArray.h" +#include "PsInlineAllocator.h" + +namespace physx +{ +namespace shdfnd +{ + +// array that pre-allocates for N elements +template ::Type> +class InlineArray : public Array > +{ + typedef InlineAllocator Allocator; + + public: + InlineArray(const PxEMPTY v) : Array(v) + { + if(isInlined()) + this->mData = reinterpret_cast(Array::getInlineBuffer()); + } + + PX_INLINE bool isInlined() const + { + return Allocator::isBufferUsed(); + } + + PX_INLINE explicit InlineArray(const Alloc& alloc = Alloc()) : Array(alloc) + { + this->mData = this->allocate(N); + this->mCapacity = N; + } +}; +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSINLINEARRAY_H diff --git a/src/PhysX/physx/source/foundation/include/PsIntrinsics.h b/src/PhysX/physx/source/foundation/include/PsIntrinsics.h new file mode 100644 index 000000000..d6b6408c4 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsIntrinsics.h @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSINTRINSICS_H +#define PSFOUNDATION_PSINTRINSICS_H + +#include "foundation/PxPreprocessor.h" + +#if PX_WINDOWS_FAMILY +#include "windows/PsWindowsIntrinsics.h" +#elif(PX_LINUX || PX_ANDROID || PX_APPLE_FAMILY || PX_PS4) +#include "unix/PsUnixIntrinsics.h" +#elif PX_XBOXONE +#include "XboxOne/PsXboxOneIntrinsics.h" +#elif PX_SWITCH +#include "switch/PsSwitchIntrinsics.h" +#else +#error "Platform not supported!" +#endif + +#endif // #ifndef PSFOUNDATION_PSINTRINSICS_H diff --git a/src/PhysX/physx/source/foundation/include/PsMathUtils.h b/src/PhysX/physx/source/foundation/include/PsMathUtils.h new file mode 100644 index 000000000..33200a38f --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsMathUtils.h @@ -0,0 +1,701 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSMATHUTILS_H +#define PSFOUNDATION_PSMATHUTILS_H + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" +#include "Ps.h" +#include "PsIntrinsics.h" + +// General guideline is: if it's an abstract math function, it belongs here. +// If it's a math function where the inputs have specific semantics (e.g. +// separateSwingTwist) it doesn't. + +namespace physx +{ +namespace shdfnd +{ +/** +\brief sign returns the sign of its argument. The sign of zero is undefined. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 sign(const PxF32 a) +{ + return intrinsics::sign(a); +} + +/** +\brief sign returns the sign of its argument. The sign of zero is undefined. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 sign(const PxF64 a) +{ + return (a >= 0.0) ? 1.0 : -1.0; +} + +/** +\brief sign returns the sign of its argument. The sign of zero is undefined. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxI32 sign(const PxI32 a) +{ + return (a >= 0) ? 1 : -1; +} + +/** +\brief Returns true if the two numbers are within eps of each other. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE bool equals(const PxF32 a, const PxF32 b, const PxF32 eps) +{ + return (PxAbs(a - b) < eps); +} + +/** +\brief Returns true if the two numbers are within eps of each other. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE bool equals(const PxF64 a, const PxF64 b, const PxF64 eps) +{ + return (PxAbs(a - b) < eps); +} + +/** +\brief The floor function returns a floating-point value representing the largest integer that is less than or equal to +x. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 floor(const PxF32 a) +{ + return floatFloor(a); +} + +/** +\brief The floor function returns a floating-point value representing the largest integer that is less than or equal to +x. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 floor(const PxF64 a) +{ + return ::floor(a); +} + +/** +\brief The ceil function returns a single value representing the smallest integer that is greater than or equal to x. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 ceil(const PxF32 a) +{ + return ::ceilf(a); +} + +/** +\brief The ceil function returns a double value representing the smallest integer that is greater than or equal to x. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 ceil(const PxF64 a) +{ + return ::ceil(a); +} + +/** +\brief mod returns the floating-point remainder of x / y. + +If the value of y is 0.0, mod returns a quiet NaN. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 mod(const PxF32 x, const PxF32 y) +{ + return PxF32(::fmodf(x, y)); +} + +/** +\brief mod returns the floating-point remainder of x / y. + +If the value of y is 0.0, mod returns a quiet NaN. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 mod(const PxF64 x, const PxF64 y) +{ + return ::fmod(x, y); +} + +/** +\brief Square. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 sqr(const PxF32 a) +{ + return a * a; +} + +/** +\brief Square. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 sqr(const PxF64 a) +{ + return a * a; +} + +/** +\brief Calculates x raised to the power of y. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 pow(const PxF32 x, const PxF32 y) +{ + return ::powf(x, y); +} + +/** +\brief Calculates x raised to the power of y. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 pow(const PxF64 x, const PxF64 y) +{ + return ::pow(x, y); +} + +/** +\brief Calculates e^n +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 exp(const PxF32 a) +{ + return ::expf(a); +} +/** + +\brief Calculates e^n +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 exp(const PxF64 a) +{ + return ::exp(a); +} + +/** +\brief Calculates 2^n +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 exp2(const PxF32 a) +{ + return ::expf(a * 0.693147180559945309417f); +} +/** + +\brief Calculates 2^n +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 exp2(const PxF64 a) +{ + return ::exp(a * 0.693147180559945309417); +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 logE(const PxF32 a) +{ + return ::logf(a); +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 logE(const PxF64 a) +{ + return ::log(a); +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 log2(const PxF32 a) +{ + return ::logf(a) / 0.693147180559945309417f; +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 log2(const PxF64 a) +{ + return ::log(a) / 0.693147180559945309417; +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 log10(const PxF32 a) +{ + return ::log10f(a); +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 log10(const PxF64 a) +{ + return ::log10(a); +} + +/** +\brief Converts degrees to radians. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 degToRad(const PxF32 a) +{ + return 0.01745329251994329547f * a; +} + +/** +\brief Converts degrees to radians. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 degToRad(const PxF64 a) +{ + return 0.01745329251994329547 * a; +} + +/** +\brief Converts radians to degrees. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 radToDeg(const PxF32 a) +{ + return 57.29577951308232286465f * a; +} + +/** +\brief Converts radians to degrees. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 radToDeg(const PxF64 a) +{ + return 57.29577951308232286465 * a; +} + +//! \brief compute sine and cosine at the same time. There is a 'fsincos' on PC that we probably want to use here +PX_CUDA_CALLABLE PX_FORCE_INLINE void sincos(const PxF32 radians, PxF32& sin, PxF32& cos) +{ + /* something like: + _asm fld Local + _asm fsincos + _asm fstp LocalCos + _asm fstp LocalSin + */ + sin = PxSin(radians); + cos = PxCos(radians); +} + +/** +\brief uniform random number in [a,b] +*/ +PX_FORCE_INLINE PxI32 rand(const PxI32 a, const PxI32 b) +{ + return a + PxI32(::rand() % (b - a + 1)); +} + +/** +\brief uniform random number in [a,b] +*/ +PX_FORCE_INLINE PxF32 rand(const PxF32 a, const PxF32 b) +{ + return a + (b - a) * ::rand() / RAND_MAX; +} + +//! \brief return angle between two vectors in radians +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 angle(const PxVec3& v0, const PxVec3& v1) +{ + const PxF32 cos = v0.dot(v1); // |v0|*|v1|*Cos(Angle) + const PxF32 sin = (v0.cross(v1)).magnitude(); // |v0|*|v1|*Sin(Angle) + return PxAtan2(sin, cos); +} + +//! If possible use instead fsel on the dot product /*fsel(d.dot(p),onething,anotherthing);*/ +//! Compares orientations (more readable, user-friendly function) +PX_CUDA_CALLABLE PX_FORCE_INLINE bool sameDirection(const PxVec3& d, const PxVec3& p) +{ + return d.dot(p) >= 0.0f; +} + +//! Checks 2 values have different signs +PX_CUDA_CALLABLE PX_FORCE_INLINE IntBool differentSign(PxReal f0, PxReal f1) +{ +#if !PX_EMSCRIPTEN + union + { + PxU32 u; + PxReal f; + } u1, u2; + u1.f = f0; + u2.f = f1; + return IntBool((u1.u ^ u2.u) & PX_SIGN_BITMASK); +#else + // javascript floats are 64-bits... + return IntBool( (f0*f1) < 0.0f ); +#endif +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33 star(const PxVec3& v) +{ + return PxMat33(PxVec3(0, v.z, -v.y), PxVec3(-v.z, 0, v.x), PxVec3(v.y, -v.x, 0)); +} + +PX_CUDA_CALLABLE PX_INLINE PxVec3 log(const PxQuat& q) +{ + const PxReal s = q.getImaginaryPart().magnitude(); + if(s < 1e-12f) + return PxVec3(0.0f); + // force the half-angle to have magnitude <= pi/2 + PxReal halfAngle = q.w < 0 ? PxAtan2(-s, -q.w) : PxAtan2(s, q.w); + PX_ASSERT(halfAngle >= -PxPi / 2 && halfAngle <= PxPi / 2); + + return q.getImaginaryPart().getNormalized() * 2.f * halfAngle; +} + +PX_CUDA_CALLABLE PX_INLINE PxQuat exp(const PxVec3& v) +{ + const PxReal m = v.magnitudeSquared(); + return m < 1e-24f ? PxQuat(PxIdentity) : PxQuat(PxSqrt(m), v * PxRecipSqrt(m)); +} + +// quat to rotate v0 t0 v1 +PX_CUDA_CALLABLE PX_INLINE PxQuat rotationArc(const PxVec3& v0, const PxVec3& v1) +{ + const PxVec3 cross = v0.cross(v1); + const PxReal d = v0.dot(v1); + if(d <= -0.99999f) + return (PxAbs(v0.x) < 0.1f ? PxQuat(0.0f, v0.z, -v0.y, 0.0f) : PxQuat(v0.y, -v0.x, 0.0, 0.0)).getNormalized(); + + const PxReal s = PxSqrt((1 + d) * 2), r = 1 / s; + + return PxQuat(cross.x * r, cross.y * r, cross.z * r, s * 0.5f).getNormalized(); +} + +/** +\brief returns largest axis +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 largestAxis(const PxVec3& v) +{ + PxU32 m = PxU32(v.y > v.x ? 1 : 0); + return v.z > v[m] ? 2 : m; +} + +/** +\brief returns indices for the largest axis and 2 other axii +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 largestAxis(const PxVec3& v, PxU32& other1, PxU32& other2) +{ + if(v.x >= PxMax(v.y, v.z)) + { + other1 = 1; + other2 = 2; + return 0; + } + else if(v.y >= v.z) + { + other1 = 0; + other2 = 2; + return 1; + } + else + { + other1 = 0; + other2 = 1; + return 2; + } +} + +/** +\brief returns axis with smallest absolute value +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 closestAxis(const PxVec3& v) +{ + PxU32 m = PxU32(PxAbs(v.y) > PxAbs(v.x) ? 1 : 0); + return PxAbs(v.z) > PxAbs(v[m]) ? 2 : m; +} + +PX_CUDA_CALLABLE PX_INLINE PxU32 closestAxis(const PxVec3& v, PxU32& j, PxU32& k) +{ + // find largest 2D plane projection + const PxF32 absPx = PxAbs(v.x); + const PxF32 absNy = PxAbs(v.y); + const PxF32 absNz = PxAbs(v.z); + + PxU32 m = 0; // x biggest axis + j = 1; + k = 2; + if(absNy > absPx && absNy > absNz) + { + // y biggest + j = 2; + k = 0; + m = 1; + } + else if(absNz > absPx) + { + // z biggest + j = 0; + k = 1; + m = 2; + } + return m; +} + +/*! +Extend an edge along its length by a factor +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE void makeFatEdge(PxVec3& p0, PxVec3& p1, PxReal fatCoeff) +{ + PxVec3 delta = p1 - p0; + + const PxReal m = delta.magnitude(); + if(m > 0.0f) + { + delta *= fatCoeff / m; + p0 -= delta; + p1 += delta; + } +} + +//! Compute point as combination of barycentric coordinates +PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 +computeBarycentricPoint(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxReal u, PxReal v) +{ + // This seems to confuse the compiler... + // return (1.0f - u - v)*p0 + u*p1 + v*p2; + const PxF32 w = 1.0f - u - v; + return PxVec3(w * p0.x + u * p1.x + v * p2.x, w * p0.y + u * p1.y + v * p2.y, w * p0.z + u * p1.z + v * p2.z); +} + +// generates a pair of quaternions (swing, twist) such that in = swing * twist, with +// swing.x = 0 +// twist.y = twist.z = 0, and twist is a unit quat +PX_FORCE_INLINE void separateSwingTwist(const PxQuat& q, PxQuat& swing, PxQuat& twist) +{ + twist = q.x != 0.0f ? PxQuat(q.x, 0, 0, q.w).getNormalized() : PxQuat(PxIdentity); + swing = q * twist.getConjugate(); +} + +// generate two tangent vectors to a given normal +PX_FORCE_INLINE void normalToTangents(const PxVec3& normal, PxVec3& tangent0, PxVec3& tangent1) +{ + tangent0 = PxAbs(normal.x) < 0.70710678f ? PxVec3(0, -normal.z, normal.y) : PxVec3(-normal.y, normal.x, 0); + tangent0.normalize(); + tangent1 = normal.cross(tangent0); +} + +/** +\brief computes a oriented bounding box around the scaled basis. +\param basis Input = skewed basis, Output = (normalized) orthogonal basis. +\return Bounding box extent. +*/ +PX_FOUNDATION_API PxVec3 optimizeBoundingBox(PxMat33& basis); + +PX_FOUNDATION_API PxQuat slerp(const PxReal t, const PxQuat& left, const PxQuat& right); + +PX_CUDA_CALLABLE PX_INLINE PxVec3 ellipseClamp(const PxVec3& point, const PxVec3& radii) +{ + // This function need to be implemented in the header file because + // it is included in a spu shader program. + + // finds the closest point on the ellipse to a given point + + // (p.y, p.z) is the input point + // (e.y, e.z) are the radii of the ellipse + + // lagrange multiplier method with Newton/Halley hybrid root-finder. + // see http://www.geometrictools.com/Documentation/DistancePointToEllipse2.pdf + // for proof of Newton step robustness and initial estimate. + // Halley converges much faster but sometimes overshoots - when that happens we take + // a newton step instead + + // converges in 1-2 iterations where D&C works well, and it's good with 4 iterations + // with any ellipse that isn't completely crazy + + const PxU32 MAX_ITERATIONS = 20; + const PxReal convergenceThreshold = 1e-4f; + + // iteration requires first quadrant but we recover generality later + + PxVec3 q(0, PxAbs(point.y), PxAbs(point.z)); + const PxReal tinyEps = 1e-6f; // very close to minor axis is numerically problematic but trivial + if(radii.y >= radii.z) + { + if(q.z < tinyEps) + return PxVec3(0, point.y > 0 ? radii.y : -radii.y, 0); + } + else + { + if(q.y < tinyEps) + return PxVec3(0, 0, point.z > 0 ? radii.z : -radii.z); + } + + PxVec3 denom, e2 = radii.multiply(radii), eq = radii.multiply(q); + + // we can use any initial guess which is > maximum(-e.y^2,-e.z^2) and for which f(t) is > 0. + // this guess works well near the axes, but is weak along the diagonals. + + PxReal t = PxMax(eq.y - e2.y, eq.z - e2.z); + + for(PxU32 i = 0; i < MAX_ITERATIONS; i++) + { + denom = PxVec3(0, 1 / (t + e2.y), 1 / (t + e2.z)); + PxVec3 denom2 = eq.multiply(denom); + + PxVec3 fv = denom2.multiply(denom2); + PxReal f = fv.y + fv.z - 1; + + // although in exact arithmetic we are guaranteed f>0, we can get here + // on the first iteration via catastrophic cancellation if the point is + // very close to the origin. In that case we just behave as if f=0 + + if(f < convergenceThreshold) + return e2.multiply(point).multiply(denom); + + PxReal df = fv.dot(denom) * -2.0f; + t = t - f / df; + } + + // we didn't converge, so clamp what we have + PxVec3 r = e2.multiply(point).multiply(denom); + return r * PxRecipSqrt(sqr(r.y / radii.y) + sqr(r.z / radii.z)); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal tanHalf(PxReal sin, PxReal cos) +{ + // PT: avoids divide by zero for singularity. We return sqrt(FLT_MAX) instead of FLT_MAX + // to make sure the calling code doesn't generate INF values when manipulating the returned value + // (some joints multiply it by 4, etc). + if(cos==-1.0f) + return sin<0.0f ? -sqrtf(FLT_MAX) : sqrtf(FLT_MAX); + + // PT: half-angle formula: tan(a/2) = sin(a)/(1+cos(a)) + return sin / (1.0f + cos); +} + +PX_INLINE PxQuat quatFromTanQVector(const PxVec3& v) +{ + PxReal v2 = v.dot(v); + if(v2 < 1e-12f) + return PxQuat(PxIdentity); + PxReal d = 1 / (1 + v2); + return PxQuat(v.x * 2, v.y * 2, v.z * 2, 1 - v2) * d; +} + +PX_FORCE_INLINE PxVec3 cross100(const PxVec3& b) +{ + return PxVec3(0.0f, -b.z, b.y); +} +PX_FORCE_INLINE PxVec3 cross010(const PxVec3& b) +{ + return PxVec3(b.z, 0.0f, -b.x); +} +PX_FORCE_INLINE PxVec3 cross001(const PxVec3& b) +{ + return PxVec3(-b.y, b.x, 0.0f); +} + +PX_INLINE void decomposeVector(PxVec3& normalCompo, PxVec3& tangentCompo, const PxVec3& outwardDir, + const PxVec3& outwardNormal) +{ + normalCompo = outwardNormal * (outwardDir.dot(outwardNormal)); + tangentCompo = outwardDir - normalCompo; +} + +//! \brief Return (i+1)%3 +// Avoid variable shift for XBox: +// PX_INLINE PxU32 Ps::getNextIndex3(PxU32 i) { return (1<> 1)) & 3; +} + +PX_INLINE PxMat33 rotFrom2Vectors(const PxVec3& from, const PxVec3& to) +{ + // See bottom of http://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/index.htm + + // Early exit if to = from + if((from - to).magnitudeSquared() < 1e-4f) + return PxMat33(PxIdentity); + + // Early exit if to = -from + if((from + to).magnitudeSquared() < 1e-4f) + return PxMat33::createDiagonal(PxVec3(1.0f, -1.0f, -1.0f)); + + PxVec3 n = from.cross(to); + + PxReal C = from.dot(to), S = PxSqrt(1 - C * C), CC = 1 - C; + + PxReal xx = n.x * n.x, yy = n.y * n.y, zz = n.z * n.z, xy = n.x * n.y, yz = n.y * n.z, xz = n.x * n.z; + + PxMat33 R; + + R(0, 0) = 1 + CC * (xx - 1); + R(0, 1) = -n.z * S + CC * xy; + R(0, 2) = n.y * S + CC * xz; + + R(1, 0) = n.z * S + CC * xy; + R(1, 1) = 1 + CC * (yy - 1); + R(1, 2) = -n.x * S + CC * yz; + + R(2, 0) = -n.y * S + CC * xz; + R(2, 1) = n.x * S + CC * yz; + R(2, 2) = 1 + CC * (zz - 1); + + return R; +} + +PX_FOUNDATION_API void integrateTransform(const PxTransform& curTrans, const PxVec3& linvel, const PxVec3& angvel, + PxReal timeStep, PxTransform& result); + +PX_INLINE void computeBasis(const PxVec3& dir, PxVec3& right, PxVec3& up) +{ + // Derive two remaining vectors + if(PxAbs(dir.y) <= 0.9999f) + { + right = PxVec3(dir.z, 0.0f, -dir.x); + right.normalize(); + + // PT: normalize not needed for 'up' because dir & right are unit vectors, + // and by construction the angle between them is 90 degrees (i.e. sin(angle)=1) + up = PxVec3(dir.y * right.z, dir.z * right.x - dir.x * right.z, -dir.y * right.x); + } + else + { + right = PxVec3(1.0f, 0.0f, 0.0f); + + up = PxVec3(0.0f, dir.z, -dir.y); + up.normalize(); + } +} + +PX_INLINE void computeBasis(const PxVec3& p0, const PxVec3& p1, PxVec3& dir, PxVec3& right, PxVec3& up) +{ + // Compute the new direction vector + dir = p1 - p0; + dir.normalize(); + + // Derive two remaining vectors + computeBasis(dir, right, up); +} + +PX_FORCE_INLINE bool isAlmostZero(const PxVec3& v) +{ + if(PxAbs(v.x) > 1e-6f || PxAbs(v.y) > 1e-6f || PxAbs(v.z) > 1e-6f) + return false; + return true; +} + +} // namespace shdfnd +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsMutex.h b/src/PhysX/physx/source/foundation/include/PsMutex.h new file mode 100644 index 000000000..675f6bf04 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsMutex.h @@ -0,0 +1,182 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSMUTEX_H +#define PSFOUNDATION_PSMUTEX_H + +#include "PsAllocator.h" + +/* + * This inclusion is a best known fix for gcc 4.4.1 error: + * Creating object file for apex/src/PsAllocator.cpp ... + * In file included from apex/include/PsFoundation.h:30, + * from apex/src/PsAllocator.cpp:26: + * apex/include/PsMutex.h: In constructor 'physx::shdfnd::MutexT::MutexT(const Alloc&)': + * apex/include/PsMutex.h:92: error: no matching function for call to 'operator new(unsigned int, + * physx::shdfnd::MutexImpl*&)' + * :0: note: candidates are: void* operator new(unsigned int) + */ +#include + +namespace physx +{ +namespace shdfnd +{ +class PX_FOUNDATION_API MutexImpl +{ + public: + /** + The constructor for Mutex creates a mutex. It is initially unlocked. + */ + MutexImpl(); + + /** + The destructor for Mutex deletes the mutex. + */ + ~MutexImpl(); + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method blocks until the mutex is + unlocked. + */ + void lock(); + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method returns false without blocking. + */ + bool trylock(); + + /** + Release (unlock) the mutex. + */ + void unlock(); + + /** + Size of this class. + */ + static uint32_t getSize(); +}; + +template > +class MutexT : protected Alloc +{ + PX_NOCOPY(MutexT) + public: + class ScopedLock + { + MutexT& mMutex; + PX_NOCOPY(ScopedLock) + public: + PX_INLINE ScopedLock(MutexT& mutex) : mMutex(mutex) + { + mMutex.lock(); + } + PX_INLINE ~ScopedLock() + { + mMutex.unlock(); + } + }; + + /** + The constructor for Mutex creates a mutex. It is initially unlocked. + */ + MutexT(const Alloc& alloc = Alloc()) : Alloc(alloc) + { + mImpl = reinterpret_cast(Alloc::allocate(MutexImpl::getSize(), __FILE__, __LINE__)); + PX_PLACEMENT_NEW(mImpl, MutexImpl)(); + } + + /** + The destructor for Mutex deletes the mutex. + */ + ~MutexT() + { + mImpl->~MutexImpl(); + Alloc::deallocate(mImpl); + } + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method blocks until the mutex is + unlocked. + */ + void lock() const + { + mImpl->lock(); + } + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method returns false without blocking, + returns true if lock is successfully acquired + */ + bool trylock() const + { + return mImpl->trylock(); + } + + /** + Release (unlock) the mutex, the calling thread must have + previously called lock() or method will error + */ + void unlock() const + { + mImpl->unlock(); + } + + private: + MutexImpl* mImpl; +}; + +class PX_FOUNDATION_API ReadWriteLock +{ + PX_NOCOPY(ReadWriteLock) + public: + ReadWriteLock(); + ~ReadWriteLock(); + + // "takeLock" can only be false if the thread already holds the mutex, e.g. if it already acquired the write lock + void lockReader(bool takeLock); + void lockWriter(); + + void unlockReader(); + void unlockWriter(); + + private: + class ReadWriteLockImpl* mImpl; +}; + +typedef MutexT<> Mutex; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSMUTEX_H diff --git a/src/PhysX/physx/source/foundation/include/PsPool.h b/src/PhysX/physx/source/foundation/include/PsPool.h new file mode 100644 index 000000000..a6f3a10fb --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsPool.h @@ -0,0 +1,298 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSPOOL_H +#define PSFOUNDATION_PSPOOL_H + +#include "PsArray.h" +#include "PsSort.h" +#include "PsBasicTemplates.h" +#include "PsInlineArray.h" + +namespace physx +{ +namespace shdfnd +{ + +/*! +Simple allocation pool +*/ +template ::Type> +class PoolBase : public UserAllocated, public Alloc +{ + PX_NOCOPY(PoolBase) + protected: + PoolBase(const Alloc& alloc, uint32_t elementsPerSlab, uint32_t slabSize) + : Alloc(alloc), mSlabs(alloc), mElementsPerSlab(elementsPerSlab), mUsed(0), mSlabSize(slabSize), mFreeElement(0) + { + PX_COMPILE_TIME_ASSERT(sizeof(T) >= sizeof(size_t)); + } + + public: + ~PoolBase() + { + if(mUsed) + disposeElements(); + + for(void** slabIt = mSlabs.begin(), *slabEnd = mSlabs.end(); slabIt != slabEnd; ++slabIt) + Alloc::deallocate(*slabIt); + } + + // Allocate space for single object + PX_INLINE T* allocate() + { + if(mFreeElement == 0) + allocateSlab(); + T* p = reinterpret_cast(mFreeElement); + mFreeElement = mFreeElement->mNext; + mUsed++; +/** +Mark a specified amount of memory with 0xcd pattern. This is used to check that the meta data +definition for serialized classes is complete in checked builds. +*/ +#if PX_CHECKED + for(uint32_t i = 0; i < sizeof(T); ++i) + reinterpret_cast(p)[i] = 0xcd; +#endif + return p; + } + + // Put space for a single element back in the lists + PX_INLINE void deallocate(T* p) + { + if(p) + { + PX_ASSERT(mUsed); + mUsed--; + push(reinterpret_cast(p)); + } + } + + PX_INLINE T* construct() + { + T* t = allocate(); + return t ? new (t) T() : 0; + } + + template + PX_INLINE T* construct(A1& a) + { + T* t = allocate(); + return t ? new (t) T(a) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b) + { + T* t = allocate(); + return t ? new (t) T(a, b) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c) + { + T* t = allocate(); + return t ? new (t) T(a, b, c) : 0; + } + + template + PX_INLINE T* construct(A1* a, A2& b, A3& c) + { + T* t = allocate(); + return t ? new (t) T(a, b, c) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c, A4& d) + { + T* t = allocate(); + return t ? new (t) T(a, b, c, d) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c, A4& d, A5& e) + { + T* t = allocate(); + return t ? new (t) T(a, b, c, d, e) : 0; + } + + PX_INLINE void destroy(T* const p) + { + if(p) + { + p->~T(); + deallocate(p); + } + } + + protected: + struct FreeList + { + FreeList* mNext; + }; + + // All the allocated slabs, sorted by pointer + InlineArray mSlabs; + + uint32_t mElementsPerSlab; + uint32_t mUsed; + uint32_t mSlabSize; + + FreeList* mFreeElement; // Head of free-list + + // Helper function to get bitmap of allocated elements + + void push(FreeList* p) + { + p->mNext = mFreeElement; + mFreeElement = p; + } + + // Allocate a slab and segregate it into the freelist + void allocateSlab() + { + T* slab = reinterpret_cast(Alloc::allocate(mSlabSize, __FILE__, __LINE__)); + + mSlabs.pushBack(slab); + + // Build a chain of nodes for the freelist + T* it = slab + mElementsPerSlab; + while(--it >= slab) + push(reinterpret_cast(it)); + } + + /* + Cleanup method. Go through all active slabs and call destructor for live objects, + then free their memory + */ + void disposeElements() + { + Array freeNodes(*this); + while(mFreeElement) + { + freeNodes.pushBack(mFreeElement); + mFreeElement = mFreeElement->mNext; + } + Alloc& alloc(*this); + sort(freeNodes.begin(), freeNodes.size(), Less(), alloc); + sort(mSlabs.begin(), mSlabs.size(), Less(), alloc); + + typename Array::Iterator slabIt = mSlabs.begin(), slabEnd = mSlabs.end(); + for(typename Array::Iterator freeIt = freeNodes.begin(); slabIt != slabEnd; ++slabIt) + { + for(T* tIt = reinterpret_cast(*slabIt), *tEnd = tIt + mElementsPerSlab; tIt != tEnd; ++tIt) + { + if(freeIt != freeNodes.end() && *freeIt == tIt) + ++freeIt; + else + tIt->~T(); + } + } + } + + /* + Go through all slabs and call destructor if the slab is empty + */ + void releaseEmptySlabs() + { + Array freeNodes(*this); + Array slabNodes(mSlabs, *this); + while(mFreeElement) + { + freeNodes.pushBack(mFreeElement); + mFreeElement = mFreeElement->mNext; + } + + typename Array::Iterator freeIt = freeNodes.begin(), freeEnd = freeNodes.end(), + lastCheck = freeNodes.end() - mElementsPerSlab; + + if(freeNodes.size() > mElementsPerSlab) + { + Alloc& alloc(*this); + sort(freeNodes.begin(), freeNodes.size(), Less(), alloc); + sort(slabNodes.begin(), slabNodes.size(), Less(), alloc); + + mSlabs.clear(); + for(void** slabIt = slabNodes.begin(), *slabEnd = slabNodes.end(); slabIt != slabEnd; ++slabIt) + { + while((freeIt < lastCheck) && (*slabIt > (*freeIt))) + { + push(reinterpret_cast(*freeIt)); + freeIt++; + } + + if(*slabIt == (*freeIt)) // the slab's first element in freeList + { + const size_t endSlabAddress = size_t(*slabIt) + mSlabSize; + const size_t endFreeAddress = size_t(*(freeIt + mElementsPerSlab - 1)); + if(endFreeAddress + sizeof(T) == endSlabAddress) + { // all slab's element in freeList + Alloc::deallocate(*slabIt); + freeIt += mElementsPerSlab; + continue; + } + } + + mSlabs.pushBack(*slabIt); + } + } + + while(freeIt != freeEnd) + { + push(reinterpret_cast(*freeIt)); + ++freeIt; + } + } +}; + +// original pool implementation +template ::Type> +class Pool : public PoolBase +{ + public: + Pool(const Alloc& alloc = Alloc(), uint32_t elementsPerSlab = 32) + : PoolBase(alloc, elementsPerSlab, elementsPerSlab * sizeof(T)) + { + } +}; + +// allows specification of the slab size instead of the occupancy +template ::Type> +class Pool2 : public PoolBase +{ + public: + Pool2(const Alloc& alloc = Alloc()) : PoolBase(alloc, slabSize / sizeof(T), slabSize) + { + } +}; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSPOOL_H diff --git a/src/PhysX/physx/source/foundation/include/PsSList.h b/src/PhysX/physx/source/foundation/include/PsSList.h new file mode 100644 index 000000000..b03bc9b96 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsSList.h @@ -0,0 +1,140 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSLIST_H +#define PSFOUNDATION_PSSLIST_H + +#include "foundation/Px.h" +#include "foundation/PxAssert.h" +#include "PsAlignedMalloc.h" + +#if PX_P64_FAMILY +#define PX_SLIST_ALIGNMENT 16 +#else +#define PX_SLIST_ALIGNMENT 8 +#endif + +namespace physx +{ +namespace shdfnd +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +#if !PX_GCC_FAMILY +__declspec(align(PX_SLIST_ALIGNMENT)) +#endif + class SListEntry +{ + friend struct SListImpl; + + public: + SListEntry() : mNext(NULL) + { + PX_ASSERT((size_t(this) & (PX_SLIST_ALIGNMENT - 1)) == 0); + } + + // Only use on elements returned by SList::flush() + // because the operation is not atomic. + SListEntry* next() + { + return mNext; + } + + private: + SListEntry* mNext; +} +#if PX_GCC_FAMILY +__attribute__((aligned(PX_SLIST_ALIGNMENT))); +#else +; +#endif + +#if PX_VC +#pragma warning(pop) +#endif + +// template-less implementation +struct PX_FOUNDATION_API SListImpl +{ + SListImpl(); + ~SListImpl(); + void push(SListEntry* entry); + SListEntry* pop(); + SListEntry* flush(); + static uint32_t getSize(); +}; + +template > +class SListT : protected Alloc +{ + public: + SListT(const Alloc& alloc = Alloc()) : Alloc(alloc) + { + mImpl = reinterpret_cast(Alloc::allocate(SListImpl::getSize(), __FILE__, __LINE__)); + PX_ASSERT((size_t(mImpl) & (PX_SLIST_ALIGNMENT - 1)) == 0); + PX_PLACEMENT_NEW(mImpl, SListImpl)(); + } + ~SListT() + { + mImpl->~SListImpl(); + Alloc::deallocate(mImpl); + } + + // pushes a new element to the list + void push(SListEntry& entry) + { + mImpl->push(&entry); + } + + // pops an element from the list + SListEntry* pop() + { + return mImpl->pop(); + } + + // removes all items from list, returns pointer to first element + SListEntry* flush() + { + return mImpl->flush(); + } + + private: + SListImpl* mImpl; +}; + +typedef SListT<> SList; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSSLIST_H diff --git a/src/PhysX/physx/source/foundation/include/PsSocket.h b/src/PhysX/physx/source/foundation/include/PsSocket.h new file mode 100644 index 000000000..3b3d68f6b --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsSocket.h @@ -0,0 +1,186 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSOCKET_H +#define PSFOUNDATION_PSSOCKET_H + +#include "PsUserAllocated.h" + +namespace physx +{ +namespace shdfnd +{ +/** +Socket abstraction API +*/ + +class PX_FOUNDATION_API Socket : public UserAllocated +{ + public: + static const uint32_t DEFAULT_BUFFER_SIZE; + + Socket(bool inEnableBuffering = true, bool blocking = true); + + virtual ~Socket(); + + /*! + Opens a network socket for input and/or output + + \param host + Name of the host to connect to. This can be an IP, URL, etc + + \param port + The port to connect to on the remote host + + \param timeout + Timeout in ms until the connection must be established. + + \return + True if the connection was successful, false otherwise + */ + bool connect(const char* host, uint16_t port, uint32_t timeout = 1000); + + /*! + Opens a network socket for input and/or output as a server. Put the connection in listening mode + + \param port + The port on which the socket listens + */ + bool listen(uint16_t port); + + /*! + Accept a connection on a socket that is in listening mode + + \note + This method only supports a single connection client. Additional clients + that connect to the listening port will overwrite the existing socket handle. + + \param block + whether or not the call should block + + \return whether a connection was established + */ + bool accept(bool block); + + /*! + Disconnects an open socket + */ + void disconnect(); + + /*! + Returns whether the socket is currently open (connected) or not. + + \return + True if the socket is connected, false otherwise + */ + bool isConnected() const; + + /*! + Returns the name of the connected host. This is the same as the string + that was supplied to the connect call. + + \return + The name of the connected host + */ + const char* getHost() const; + + /*! + Returns the port of the connected host. This is the same as the port + that was supplied to the connect call. + + \return + The port of the connected host + */ + uint16_t getPort() const; + + /*! + Flushes the output stream. Until the stream is flushed, there is no + guarantee that the written data has actually reached the destination + storage. Flush forces all buffered data to be sent to the output. + + \note flush always blocks. If the socket is in non-blocking mode, this will result + the thread spinning. + + \return + True if the flush was successful, false otherwise + */ + bool flush(); + + /*! + Writes data to the output stream. + + \param data + Pointer to a block of data to write to the stream + + \param length + Amount of data to write, in bytes + + \return + Number of bytes actually written. This could be lower than length if the socket is non-blocking. + */ + + uint32_t write(const uint8_t* data, uint32_t length); + + /*! + Reads data from the output stream. + + \param data + Pointer to a buffer where the read data will be stored. + + \param length + Amount of data to read, in bytes. + + \return + Number of bytes actually read. This could be lower than length if the stream end is + encountered or the socket is non-blocking. + */ + uint32_t read(uint8_t* data, uint32_t length); + + /*! + Sets blocking mode of the socket. + Socket must be connected, otherwise calling this method won't take any effect. + */ + void setBlocking(bool blocking); + + /*! + Returns whether read/write/flush calls to the socket are blocking. + + \return + True if the socket is blocking. + */ + bool isBlocking() const; + + private: + class SocketImpl* mImpl; +}; + +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSSOCKET_H diff --git a/src/PhysX/physx/source/foundation/include/PsSort.h b/src/PhysX/physx/source/foundation/include/PsSort.h new file mode 100644 index 000000000..8beec55ec --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsSort.h @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSORT_H +#define PSFOUNDATION_PSSORT_H + +/** \addtogroup foundation +@{ +*/ + +#include "PsSortInternals.h" +#include "PsAlloca.h" + +#define PX_SORT_PARANOIA PX_DEBUG + +/** +\brief Sorts an array of objects in ascending order, assuming +that the predicate implements the < operator: + +\see Less, Greater +*/ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4706) // disable the warning that we did an assignment within a conditional expression, as +// this was intentional. +#endif + +namespace physx +{ +namespace shdfnd +{ +template +void sort(T* elements, uint32_t count, const Predicate& compare, const Allocator& inAllocator, + const uint32_t initialStackSize = 32) +{ + static const uint32_t SMALL_SORT_CUTOFF = 5; // must be >= 3 since we need 3 for median + + PX_ALLOCA(stackMem, int32_t, initialStackSize); + internal::Stack stack(stackMem, initialStackSize, inAllocator); + + int32_t first = 0, last = int32_t(count - 1); + if(last > first) + { + for(;;) + { + while(last > first) + { + PX_ASSERT(first >= 0 && last < int32_t(count)); + if(uint32_t(last - first) < SMALL_SORT_CUTOFF) + { + internal::smallSort(elements, first, last, compare); + break; + } + else + { + const int32_t partIndex = internal::partition(elements, first, last, compare); + + // push smaller sublist to minimize stack usage + if((partIndex - first) < (last - partIndex)) + { + stack.push(first, partIndex - 1); + first = partIndex + 1; + } + else + { + stack.push(partIndex + 1, last); + last = partIndex - 1; + } + } + } + + if(stack.empty()) + break; + + stack.pop(first, last); + } + } +#if PX_SORT_PARANOIA + for(uint32_t i = 1; i < count; i++) + PX_ASSERT(!compare(elements[i], elements[i - 1])); +#endif +} + +template +void sort(T* elements, uint32_t count, const Predicate& compare) +{ + sort(elements, count, compare, typename shdfnd::AllocatorTraits::Type()); +} + +template +void sort(T* elements, uint32_t count) +{ + sort(elements, count, shdfnd::Less(), typename shdfnd::AllocatorTraits::Type()); +} + +} // namespace shdfnd +} // namespace physx + +#if PX_VC +#pragma warning(pop) +#endif + +#endif // #ifndef PSFOUNDATION_PSSORT_H diff --git a/src/PhysX/physx/source/foundation/include/PsSortInternals.h b/src/PhysX/physx/source/foundation/include/PsSortInternals.h new file mode 100644 index 000000000..54d9b4480 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsSortInternals.h @@ -0,0 +1,188 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSORTINTERNALS_H +#define PSFOUNDATION_PSSORTINTERNALS_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxAssert.h" +#include "foundation/PxIntrinsics.h" +#include "PsBasicTemplates.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace shdfnd +{ +namespace internal +{ +template +PX_INLINE void median3(T* elements, int32_t first, int32_t last, Predicate& compare) +{ + /* + This creates sentinels because we know there is an element at the start minimum(or equal) + than the pivot and an element at the end greater(or equal) than the pivot. Plus the + median of 3 reduces the chance of degenerate behavour. + */ + + int32_t mid = (first + last) / 2; + + if(compare(elements[mid], elements[first])) + swap(elements[first], elements[mid]); + + if(compare(elements[last], elements[first])) + swap(elements[first], elements[last]); + + if(compare(elements[last], elements[mid])) + swap(elements[mid], elements[last]); + + // keep the pivot at last-1 + swap(elements[mid], elements[last - 1]); +} + +template +PX_INLINE int32_t partition(T* elements, int32_t first, int32_t last, Predicate& compare) +{ + median3(elements, first, last, compare); + + /* + WARNING: using the line: + + T partValue = elements[last-1]; + + and changing the scan loops to: + + while(comparator.greater(partValue, elements[++i])); + while(comparator.greater(elements[--j], partValue); + + triggers a compiler optimizer bug on xenon where it stores a double to the stack for partValue + then loads it as a single...:-( + */ + + int32_t i = first; // we know first is less than pivot(but i gets pre incremented) + int32_t j = last - 1; // pivot is in last-1 (but j gets pre decremented) + + for(;;) + { + while(compare(elements[++i], elements[last - 1])) + ; + while(compare(elements[last - 1], elements[--j])) + ; + + if(i >= j) + break; + + PX_ASSERT(i <= last && j >= first); + swap(elements[i], elements[j]); + } + // put the pivot in place + + PX_ASSERT(i <= last && first <= (last - 1)); + swap(elements[i], elements[last - 1]); + + return i; +} + +template +PX_INLINE void smallSort(T* elements, int32_t first, int32_t last, Predicate& compare) +{ + // selection sort - could reduce to fsel on 360 with floats. + + for(int32_t i = first; i < last; i++) + { + int32_t m = i; + for(int32_t j = i + 1; j <= last; j++) + if(compare(elements[j], elements[m])) + m = j; + + if(m != i) + swap(elements[m], elements[i]); + } +} + +template +class Stack +{ + Allocator mAllocator; + uint32_t mSize, mCapacity; + int32_t* mMemory; + bool mRealloc; + + public: + Stack(int32_t* memory, uint32_t capacity, const Allocator& inAllocator) + : mAllocator(inAllocator), mSize(0), mCapacity(capacity), mMemory(memory), mRealloc(false) + { + } + ~Stack() + { + if(mRealloc) + mAllocator.deallocate(mMemory); + } + + void grow() + { + mCapacity *= 2; + int32_t* newMem = + reinterpret_cast(mAllocator.allocate(sizeof(int32_t) * mCapacity, __FILE__, __LINE__)); + intrinsics::memCopy(newMem, mMemory, mSize * sizeof(int32_t)); + if(mRealloc) + mAllocator.deallocate(mMemory); + mRealloc = true; + mMemory = newMem; + } + + PX_INLINE void push(int32_t start, int32_t end) + { + if(mSize >= mCapacity - 1) + grow(); + mMemory[mSize++] = start; + mMemory[mSize++] = end; + } + + PX_INLINE void pop(int32_t& start, int32_t& end) + { + PX_ASSERT(!empty()); + end = mMemory[--mSize]; + start = mMemory[--mSize]; + } + + PX_INLINE bool empty() + { + return mSize == 0; + } +}; +} // namespace internal + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSSORTINTERNALS_H diff --git a/src/PhysX/physx/source/foundation/include/PsString.h b/src/PhysX/physx/source/foundation/include/PsString.h new file mode 100644 index 000000000..eeb1b2444 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsString.h @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSTRING_H +#define PSFOUNDATION_PSSTRING_H + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxSimpleTypes.h" +#include + +namespace physx +{ +namespace shdfnd +{ + +// the following functions have C99 semantics. Note that C99 requires for snprintf and vsnprintf: +// * the resulting string is always NULL-terminated regardless of truncation. +// * in the case of truncation the return value is the number of characters that would have been created. + +PX_FOUNDATION_API int32_t sscanf(const char* buffer, const char* format, ...); +PX_FOUNDATION_API int32_t strcmp(const char* str1, const char* str2); +PX_FOUNDATION_API int32_t strncmp(const char* str1, const char* str2, size_t count); +PX_FOUNDATION_API int32_t snprintf(char* dst, size_t dstSize, const char* format, ...); +PX_FOUNDATION_API int32_t vsnprintf(char* dst, size_t dstSize, const char* src, va_list arg); + +// strlcat and strlcpy have BSD semantics: +// * dstSize is always the size of the destination buffer +// * the resulting string is always NULL-terminated regardless of truncation +// * in the case of truncation the return value is the length of the string that would have been created + +PX_FOUNDATION_API size_t strlcat(char* dst, size_t dstSize, const char* src); +PX_FOUNDATION_API size_t strlcpy(char* dst, size_t dstSize, const char* src); + +// case-insensitive string comparison +PX_FOUNDATION_API int32_t stricmp(const char* str1, const char* str2); +PX_FOUNDATION_API int32_t strnicmp(const char* str1, const char* str2, size_t count); + +// in-place string case conversion +PX_FOUNDATION_API void strlwr(char* str); +PX_FOUNDATION_API void strupr(char* str); + +/** +\brief The maximum supported formatted output string length +(number of characters after replacement). + +@see printFormatted() +*/ +static const size_t MAX_PRINTFORMATTED_LENGTH = 1024; + +/** +\brief Prints the formatted data, trying to make sure it's visible to the app programmer + +@see NS_MAX_PRINTFORMATTED_LENGTH +*/ +PX_FOUNDATION_API void printFormatted(const char*, ...); + +/** +\brief Prints the string literally (does not consume % specifier), trying to make sure it's visible to the app +programmer +*/ +PX_FOUNDATION_API void printString(const char*); +} +} +#endif // #ifndef PSFOUNDATION_PSSTRING_H diff --git a/src/PhysX/physx/source/foundation/include/PsSync.h b/src/PhysX/physx/source/foundation/include/PsSync.h new file mode 100644 index 000000000..f8d73b36f --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsSync.h @@ -0,0 +1,138 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSYNC_H +#define PSFOUNDATION_PSSYNC_H + +#include "PsAllocator.h" + +namespace physx +{ +namespace shdfnd +{ +/*! +Implementation notes: +* - Calling set() on an already signaled Sync does not change its state. +* - Calling reset() on an already reset Sync does not change its state. +* - Calling set() on a reset Sync wakes all waiting threads (potential for thread contention). +* - Calling wait() on an already signaled Sync will return true immediately. +* - NOTE: be careful when pulsing an event with set() followed by reset(), because a +* thread that is not waiting on the event will miss the signal. +*/ +class PX_FOUNDATION_API SyncImpl +{ + public: + static const uint32_t waitForever = 0xffffffff; + + SyncImpl(); + + ~SyncImpl(); + + /** Wait on the object for at most the given number of ms. Returns + * true if the object is signaled. Sync::waitForever will block forever + * or until the object is signaled. + */ + + bool wait(uint32_t milliseconds = waitForever); + + /** Signal the synchronization object, waking all threads waiting on it */ + + void set(); + + /** Reset the synchronization object */ + + void reset(); + + /** + Size of this class. + */ + static uint32_t getSize(); +}; + +/*! +Implementation notes: +* - Calling set() on an already signaled Sync does not change its state. +* - Calling reset() on an already reset Sync does not change its state. +* - Calling set() on a reset Sync wakes all waiting threads (potential for thread contention). +* - Calling wait() on an already signaled Sync will return true immediately. +* - NOTE: be careful when pulsing an event with set() followed by reset(), because a +* thread that is not waiting on the event will miss the signal. +*/ +template > +class SyncT : protected Alloc +{ + public: + static const uint32_t waitForever = SyncImpl::waitForever; + + SyncT(const Alloc& alloc = Alloc()) : Alloc(alloc) + { + mImpl = reinterpret_cast(Alloc::allocate(SyncImpl::getSize(), __FILE__, __LINE__)); + PX_PLACEMENT_NEW(mImpl, SyncImpl)(); + } + + ~SyncT() + { + mImpl->~SyncImpl(); + Alloc::deallocate(mImpl); + } + + /** Wait on the object for at most the given number of ms. Returns + * true if the object is signaled. Sync::waitForever will block forever + * or until the object is signaled. + */ + + bool wait(uint32_t milliseconds = SyncImpl::waitForever) + { + return mImpl->wait(milliseconds); + } + + /** Signal the synchronization object, waking all threads waiting on it */ + + void set() + { + mImpl->set(); + } + + /** Reset the synchronization object */ + + void reset() + { + mImpl->reset(); + } + + private: + class SyncImpl* mImpl; +}; + +typedef SyncT<> Sync; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSSYNC_H diff --git a/src/PhysX/physx/source/foundation/include/PsTempAllocator.h b/src/PhysX/physx/source/foundation/include/PsTempAllocator.h new file mode 100644 index 000000000..6594bafad --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsTempAllocator.h @@ -0,0 +1,62 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSTEMPALLOCATOR_H +#define PSFOUNDATION_PSTEMPALLOCATOR_H + +#include "PsAllocator.h" + +namespace physx +{ +namespace shdfnd +{ +union TempAllocatorChunk +{ + TempAllocatorChunk() : mNext(0) + { + } + TempAllocatorChunk* mNext; // while chunk is free + uint32_t mIndex; // while chunk is allocated + uint8_t mPad[16]; // 16 byte aligned allocations +}; + +class TempAllocator +{ + public: + PX_FORCE_INLINE TempAllocator(const char* = 0) + { + } + PX_FOUNDATION_API void* allocate(size_t size, const char* file, int line); + PX_FOUNDATION_API void deallocate(void* ptr); +}; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSTEMPALLOCATOR_H diff --git a/src/PhysX/physx/source/foundation/include/PsThread.h b/src/PhysX/physx/source/foundation/include/PsThread.h new file mode 100644 index 000000000..c9304ca7a --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsThread.h @@ -0,0 +1,384 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSTHREAD_H +#define PSFOUNDATION_PSTHREAD_H + +#include "PsUserAllocated.h" + +// dsequeira: according to existing comment here (David Black would be my guess) +// "This is useful to reduce bus contention on tight spin locks. And it needs +// to be a macro as the xenon compiler often ignores even __forceinline." What's not +// clear is why a pause function needs inlining...? (TODO: check with XBox team) + +// todo: these need to go somewhere else + +#if PX_WINDOWS_FAMILY || PX_XBOXONE +#define PxSpinLockPause() __asm pause +#elif PX_LINUX || PX_ANDROID || PX_PS4 || PX_APPLE_FAMILY || PX_SWITCH +#define PxSpinLockPause() asm("nop") +#else +#error "Platform not supported!" +#endif + +namespace physx +{ +namespace shdfnd +{ +struct ThreadPriority // todo: put in some other header file +{ + enum Enum + { + /** + \brief High priority + */ + eHIGH = 0, + + /** + \brief Above Normal priority + */ + eABOVE_NORMAL = 1, + + /** + \brief Normal/default priority + */ + eNORMAL = 2, + + /** + \brief Below Normal priority + */ + eBELOW_NORMAL = 3, + + /** + \brief Low priority. + */ + eLOW = 4, + eFORCE_DWORD = 0xffFFffFF + }; +}; + +class Runnable +{ + public: + Runnable() + { + } + virtual ~Runnable() + { + } + virtual void execute(void) + { + } +}; + +class PX_FOUNDATION_API ThreadImpl +{ + public: + typedef size_t Id; // space for a pointer or an integer + typedef void* (*ExecuteFn)(void*); + + static uint32_t getDefaultStackSize(); + static Id getId(); + + /** + Construct (but do not start) the thread object. The OS thread object will not be created + until start() is called. Executes in the context + of the spawning thread. + */ + + ThreadImpl(); + + /** + Construct and start the the thread, passing the given arg to the given fn. (pthread style) + */ + + ThreadImpl(ExecuteFn fn, void* arg, const char* name); + + /** + Deallocate all resources associated with the thread. Should be called in the + context of the spawning thread. + */ + + ~ThreadImpl(); + + /** + Create the OS thread and start it running. Called in the context of the spawning thread. + If an affinity mask has previously been set then it will be applied after the + thread has been created. + */ + + void start(uint32_t stackSize, Runnable* r); + + /** + Violently kill the current thread. Blunt instrument, not recommended since + it can leave all kinds of things unreleased (stack, memory, mutexes...) Should + be called in the context of the spawning thread. + */ + + void kill(); + + /** + Stop the thread. Signals the spawned thread that it should stop, so the + thread should check regularly + */ + + void signalQuit(); + + /** + Wait for a thread to stop. Should be called in the context of the spawning + thread. Returns false if the thread has not been started. + */ + + bool waitForQuit(); + + /** + check whether the thread is signalled to quit. Called in the context of the + spawned thread. + */ + + bool quitIsSignalled(); + + /** + Cleanly shut down this thread. Called in the context of the spawned thread. + */ + void quit(); + + /** + Change the affinity mask for this thread. The mask is a platform + specific value. + + On Windows, Linux, PS4, XboxOne and Switch platforms, each set mask bit represents + the index of a logical processor that the OS may schedule thread execution on. + Bits outside the range of valid logical processors may be ignored or cause + the function to return an error. + + On Apple platforms, this function has no effect. + + If the thread has not yet been started then the mask is stored + and applied when the thread is started. + + If the thread has already been started then this method returns the + previous affinity mask on success, otherwise it returns zero. + */ + uint32_t setAffinityMask(uint32_t mask); + + static ThreadPriority::Enum getPriority(Id threadId); + + /** Set thread priority. */ + void setPriority(ThreadPriority::Enum prio); + + /** set the thread's name */ + void setName(const char* name); + + /** Put the current thread to sleep for the given number of milliseconds */ + static void sleep(uint32_t ms); + + /** Yield the current thread's slot on the CPU */ + static void yield(); + + /** Return the number of physical cores (does not include hyper-threaded cores), returns 0 on failure */ + static uint32_t getNbPhysicalCores(); + + /** + Size of this class. + */ + static uint32_t getSize(); +}; + +/** +Thread abstraction API +*/ +template > +class ThreadT : protected Alloc, public UserAllocated, public Runnable +{ + public: + typedef ThreadImpl::Id Id; // space for a pointer or an integer + + /** + Construct (but do not start) the thread object. Executes in the context + of the spawning thread + */ + ThreadT(const Alloc& alloc = Alloc()) : Alloc(alloc) + { + mImpl = reinterpret_cast(Alloc::allocate(ThreadImpl::getSize(), __FILE__, __LINE__)); + PX_PLACEMENT_NEW(mImpl, ThreadImpl)(); + } + + /** + Construct and start the the thread, passing the given arg to the given fn. (pthread style) + */ + ThreadT(ThreadImpl::ExecuteFn fn, void* arg, const char* name, const Alloc& alloc = Alloc()) : Alloc(alloc) + { + mImpl = reinterpret_cast(Alloc::allocate(ThreadImpl::getSize(), __FILE__, __LINE__)); + PX_PLACEMENT_NEW(mImpl, ThreadImpl)(fn, arg, name); + } + + /** + Deallocate all resources associated with the thread. Should be called in the + context of the spawning thread. + */ + virtual ~ThreadT() + { + mImpl->~ThreadImpl(); + Alloc::deallocate(mImpl); + } + + /** + start the thread running. Called in the context of the spawning thread. + */ + + void start(uint32_t stackSize = ThreadImpl::getDefaultStackSize()) + { + mImpl->start(stackSize, this); + } + + /** + Violently kill the current thread. Blunt instrument, not recommended since + it can leave all kinds of things unreleased (stack, memory, mutexes...) Should + be called in the context of the spawning thread. + */ + + void kill() + { + mImpl->kill(); + } + + /** + The virtual execute() method is the user defined function that will + run in the new thread. Called in the context of the spawned thread. + */ + + virtual void execute(void) + { + } + + /** + stop the thread. Signals the spawned thread that it should stop, so the + thread should check regularly + */ + + void signalQuit() + { + mImpl->signalQuit(); + } + + /** + Wait for a thread to stop. Should be called in the context of the spawning + thread. Returns false if the thread has not been started. + */ + + bool waitForQuit() + { + return mImpl->waitForQuit(); + } + + /** + check whether the thread is signalled to quit. Called in the context of the + spawned thread. + */ + + bool quitIsSignalled() + { + return mImpl->quitIsSignalled(); + } + + /** + Cleanly shut down this thread. Called in the context of the spawned thread. + */ + void quit() + { + mImpl->quit(); + } + + uint32_t setAffinityMask(uint32_t mask) + { + return mImpl->setAffinityMask(mask); + } + + static ThreadPriority::Enum getPriority(ThreadImpl::Id threadId) + { + return ThreadImpl::getPriority(threadId); + } + + /** Set thread priority. */ + void setPriority(ThreadPriority::Enum prio) + { + mImpl->setPriority(prio); + } + + /** set the thread's name */ + void setName(const char* name) + { + mImpl->setName(name); + } + + /** Put the current thread to sleep for the given number of milliseconds */ + static void sleep(uint32_t ms) + { + ThreadImpl::sleep(ms); + } + + /** Yield the current thread's slot on the CPU */ + static void yield() + { + ThreadImpl::yield(); + } + + static uint32_t getDefaultStackSize() + { + return ThreadImpl::getDefaultStackSize(); + } + + static ThreadImpl::Id getId() + { + return ThreadImpl::getId(); + } + + static uint32_t getNbPhysicalCores() + { + return ThreadImpl::getNbPhysicalCores(); + } + + private: + class ThreadImpl* mImpl; +}; + +typedef ThreadT<> Thread; + +PX_FOUNDATION_API uint32_t TlsAlloc(); +PX_FOUNDATION_API void TlsFree(uint32_t index); +PX_FOUNDATION_API void* TlsGet(uint32_t index); +PX_FOUNDATION_API size_t TlsGetValue(uint32_t index); +PX_FOUNDATION_API uint32_t TlsSet(uint32_t index, void* value); +PX_FOUNDATION_API uint32_t TlsSetValue(uint32_t index, size_t value); + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSTHREAD_H diff --git a/src/PhysX/physx/source/foundation/include/PsTime.h b/src/PhysX/physx/source/foundation/include/PsTime.h new file mode 100644 index 000000000..078e45365 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsTime.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSTIME_H +#define PSFOUNDATION_PSTIME_H + +#include "Ps.h" + +#if PX_LINUX || PX_ANDROID +#include +#endif + +namespace physx +{ +namespace shdfnd +{ + +struct CounterFrequencyToTensOfNanos +{ + uint64_t mNumerator; + uint64_t mDenominator; + CounterFrequencyToTensOfNanos(uint64_t inNum, uint64_t inDenom) : mNumerator(inNum), mDenominator(inDenom) + { + } + + // quite slow. + uint64_t toTensOfNanos(uint64_t inCounter) const + { + return (inCounter * mNumerator) / mDenominator; + } +}; + +class PX_FOUNDATION_API Time +{ + public: + typedef double Second; + static const uint64_t sNumTensOfNanoSecondsInASecond = 100000000; + // This is supposedly guaranteed to not change after system boot + // regardless of processors, speedstep, etc. + static const CounterFrequencyToTensOfNanos& getBootCounterFrequency(); + + static CounterFrequencyToTensOfNanos getCounterFrequency(); + + static uint64_t getCurrentCounterValue(); + + // SLOW!! + // Thar be a 64 bit divide in thar! + static uint64_t getCurrentTimeInTensOfNanoSeconds() + { + uint64_t ticks = getCurrentCounterValue(); + return getBootCounterFrequency().toTensOfNanos(ticks); + } + + Time(); + Second getElapsedSeconds(); + Second peekElapsedSeconds(); + Second getLastTime() const; + + private: +#if PX_LINUX || PX_ANDROID || PX_APPLE_FAMILY || PX_PS4 + Second mLastTime; +#else + int64_t mTickCount; +#endif +}; +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSTIME_H diff --git a/src/PhysX/physx/source/foundation/include/PsUserAllocated.h b/src/PhysX/physx/source/foundation/include/PsUserAllocated.h new file mode 100644 index 000000000..1ca80f193 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsUserAllocated.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUSERALLOCATED_H +#define PSFOUNDATION_PSUSERALLOCATED_H + +#include "PsAllocator.h" + +namespace physx +{ +namespace shdfnd +{ +/** +Provides new and delete using a UserAllocator. +Guarantees that 'delete x;' uses the UserAllocator too. +*/ +class UserAllocated +{ + public: + // PX_SERIALIZATION + PX_INLINE void* operator new(size_t, void* address) + { + return address; + } + //~PX_SERIALIZATION + // Matching operator delete to the above operator new. Don't ask me + // how this makes any sense - Nuernberger. + PX_INLINE void operator delete(void*, void*) + { + } + + template + PX_INLINE void* operator new(size_t size, Alloc alloc, const char* fileName, int line) + { + return alloc.allocate(size, fileName, line); + } + template + PX_INLINE void* operator new [](size_t size, Alloc alloc, const char* fileName, int line) + { return alloc.allocate(size, fileName, line); } + + // placement delete + template + PX_INLINE void operator delete(void* ptr, Alloc alloc, const char* fileName, int line) + { + PX_UNUSED(fileName); + PX_UNUSED(line); + alloc.deallocate(ptr); + } + template + PX_INLINE void operator delete [](void* ptr, Alloc alloc, const char* fileName, int line) + { + PX_UNUSED(fileName); + PX_UNUSED(line); + alloc.deallocate(ptr); + } PX_INLINE void + operator delete(void* ptr) + { + NonTrackingAllocator().deallocate(ptr); + } + PX_INLINE void operator delete [](void* ptr) + { NonTrackingAllocator().deallocate(ptr); } +}; +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSUSERALLOCATED_H diff --git a/src/PhysX/physx/source/foundation/include/PsUtilities.h b/src/PhysX/physx/source/foundation/include/PsUtilities.h new file mode 100644 index 000000000..d991be1fd --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsUtilities.h @@ -0,0 +1,165 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUTILITIES_H +#define PSFOUNDATION_PSUTILITIES_H + +#include "foundation/PxVec3.h" +#include "foundation/PxAssert.h" +#include "Ps.h" +#include "PsIntrinsics.h" +#include "PsBasicTemplates.h" + +namespace physx +{ +namespace shdfnd +{ +PX_INLINE char littleEndian() +{ + int i = 1; + return *(reinterpret_cast(&i)); +} + +// PT: checked casts +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 to32(PxU64 value) +{ + PX_ASSERT(value <= 0xffffffff); + return PxU32(value); +} +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 to16(PxU32 value) +{ + PX_ASSERT(value <= 0xffff); + return PxU16(value); +} +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU8 to8(PxU16 value) +{ + PX_ASSERT(value <= 0xff); + return PxU8(value); +} +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU8 to8(PxU32 value) +{ + PX_ASSERT(value <= 0xff); + return PxU8(value); +} +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU8 to8(PxI32 value) +{ + PX_ASSERT(value <= 0xff); + PX_ASSERT(value >= 0); + return PxU8(value); +} +PX_CUDA_CALLABLE PX_FORCE_INLINE PxI8 toI8(PxU32 value) +{ + PX_ASSERT(value <= 0x7f); + return PxI8(value); +} + +/*! +Get number of elements in array +*/ +template +char (&ArraySizeHelper(T (&array)[N]))[N]; +#define PX_ARRAY_SIZE(_array) (sizeof(physx::shdfnd::ArraySizeHelper(_array))) + +/*! +Sort two elements using operator< + +On return x will be the smaller of the two +*/ +template +PX_CUDA_CALLABLE PX_FORCE_INLINE void order(T& x, T& y) +{ + if(y < x) + swap(x, y); +} + +// most architectures can do predication on real comparisons, and on VMX, it matters + +PX_CUDA_CALLABLE PX_FORCE_INLINE void order(PxReal& x, PxReal& y) +{ + PxReal newX = PxMin(x, y); + PxReal newY = PxMax(x, y); + x = newX; + y = newY; +} + +/*! +Sort two elements using operator< and also keep order +of any extra data +*/ +template +PX_CUDA_CALLABLE PX_FORCE_INLINE void order(T& x, T& y, E1& xe1, E1& ye1) +{ + if(y < x) + { + swap(x, y); + swap(xe1, ye1); + } +} + +#if PX_GCC_FAMILY && !PX_EMSCRIPTEN && !PX_LINUX +__attribute__((noreturn)) +#endif + PX_INLINE void debugBreak() +{ +#if PX_WINDOWS || PX_XBOXONE + __debugbreak(); +#elif PX_ANDROID + raise(SIGTRAP); // works better than __builtin_trap. Proper call stack and can be continued. +#elif PX_LINUX + asm("int $3"); +#elif PX_GCC_FAMILY + __builtin_trap(); +#else + PX_ASSERT(false); +#endif +} + +bool checkValid(const float&); +bool checkValid(const PxVec3&); +bool checkValid(const PxQuat&); +bool checkValid(const PxMat33&); +bool checkValid(const PxTransform&); +bool checkValid(const char*); + +// equivalent to std::max_element +template +inline const T* maxElement(const T* first, const T* last) +{ + const T* m = first; + for(const T* it = first + 1; it < last; ++it) + if(*m < *it) + m = it; + + return m; +} + +} // namespace shdfnd +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsVecMath.h b/src/PhysX/physx/source/foundation/include/PsVecMath.h new file mode 100644 index 000000000..680c350c6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecMath.h @@ -0,0 +1,1337 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECMATH_H +#define PSFOUNDATION_PSVECMATH_H + +#include "Ps.h" +#include "PsIntrinsics.h" +#include "foundation/PxVec3.h" +#include "foundation/PxVec4.h" +#include "foundation/PxMat33.h" +#include "foundation/PxUnionCast.h" + +// We can opt to use the scalar version of vectorised functions. +// This can catch type safety issues and might even work out more optimal on pc. +// It will also be useful for benchmarking and testing. +// NEVER submit with vector intrinsics deactivated without good reason. +// AM: deactivating SIMD for debug win64 just so autobuild will also exercise +// non-SIMD path, until a dedicated non-SIMD platform sich as Arm comes online. +// TODO: dima: reference all platforms with SIMD support here, +// all unknown/experimental cases should better default to NO SIMD. + +// enable/disable SIMD +#if !defined(PX_SIMD_DISABLED) +#if PX_INTEL_FAMILY && (!defined(__EMSCRIPTEN__) || defined(__SSE2__)) +#define COMPILE_VECTOR_INTRINSICS 1 +#elif PX_ANDROID&& PX_NEON +#define COMPILE_VECTOR_INTRINSICS 1 +#elif PX_IOS&& PX_NEON +#define COMPILE_VECTOR_INTRINSICS 1 +#elif PX_SWITCH +#define COMPILE_VECTOR_INTRINSICS 1 +#else +#define COMPILE_VECTOR_INTRINSICS 0 +#endif +#else +#define COMPILE_VECTOR_INTRINSICS 0 +#endif + +#if COMPILE_VECTOR_INTRINSICS && PX_INTEL_FAMILY&&(PX_UNIX_FAMILY || PX_PS4) +// only SSE2 compatible platforms should reach this +#if PX_EMSCRIPTEN +#include +#endif +#include +#endif + +#if COMPILE_VECTOR_INTRINSICS +#include "PsAoS.h" +#else +#include "PsVecMathAoSScalar.h" +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +// Basic AoS types are +// FloatV - 16-byte aligned representation of float. +// Vec3V - 16-byte aligned representation of PxVec3 stored as (x y z 0). +// Vec4V - 16-byte aligned representation of vector of 4 floats stored as (x y z w). +// BoolV - 16-byte aligned representation of vector of 4 bools stored as (x y z w). +// VecU32V - 16-byte aligned representation of 4 unsigned ints stored as (x y z w). +// VecI32V - 16-byte aligned representation of 4 signed ints stored as (x y z w). +// Mat33V - 16-byte aligned representation of any 3x3 matrix. +// Mat34V - 16-byte aligned representation of transformation matrix (rotation in col1,col2,col3 and translation in +// col4). +// Mat44V - 16-byte aligned representation of any 4x4 matrix. + +////////////////////////////////////////// +// Construct a simd type from a scalar type +////////////////////////////////////////// + +// FloatV +//(f,f,f,f) +PX_FORCE_INLINE FloatV FLoad(const PxF32 f); + +// Vec3V +//(f,f,f,0) +PX_FORCE_INLINE Vec3V V3Load(const PxF32 f); +//(f.x,f.y,f.z,0) +PX_FORCE_INLINE Vec3V V3LoadU(const PxVec3& f); +//(f.x,f.y,f.z,0), f must be 16-byte aligned +PX_FORCE_INLINE Vec3V V3LoadA(const PxVec3& f); +//(f.x,f.y,f.z,w_undefined), f must be 16-byte aligned +PX_FORCE_INLINE Vec3V V3LoadUnsafeA(const PxVec3& f); +//(f.x,f.y,f.z,0) +PX_FORCE_INLINE Vec3V V3LoadU(const PxF32* f); +//(f.x,f.y,f.z,0), f must be 16-byte aligned +PX_FORCE_INLINE Vec3V V3LoadA(const PxF32* f); + +// Vec4V +//(f,f,f,f) +PX_FORCE_INLINE Vec4V V4Load(const PxF32 f); +//(f[0],f[1],f[2],f[3]) +PX_FORCE_INLINE Vec4V V4LoadU(const PxF32* const f); +//(f[0],f[1],f[2],f[3]), f must be 16-byte aligned +PX_FORCE_INLINE Vec4V V4LoadA(const PxF32* const f); +//(x,y,z,w) +PX_FORCE_INLINE Vec4V V4LoadXYZW(const PxF32& x, const PxF32& y, const PxF32& z, const PxF32& w); + +// BoolV +//(f,f,f,f) +PX_FORCE_INLINE BoolV BLoad(const bool f); +//(f[0],f[1],f[2],f[3]) +PX_FORCE_INLINE BoolV BLoad(const bool* const f); + +// VecU32V +//(f,f,f,f) +PX_FORCE_INLINE VecU32V U4Load(const PxU32 f); +//(f[0],f[1],f[2],f[3]) +PX_FORCE_INLINE VecU32V U4LoadU(const PxU32* f); +//(f[0],f[1],f[2],f[3]), f must be 16-byte aligned +PX_FORCE_INLINE VecU32V U4LoadA(const PxU32* f); +//((U32)x, (U32)y, (U32)z, (U32)w) +PX_FORCE_INLINE VecU32V U4LoadXYZW(PxU32 x, PxU32 y, PxU32 z, PxU32 w); + +// VecI32V +//(i,i,i,i) +PX_FORCE_INLINE VecI32V I4Load(const PxI32 i); +//(i,i,i,i) +PX_FORCE_INLINE VecI32V I4LoadU(const PxI32* i); +//(i,i,i,i) +PX_FORCE_INLINE VecI32V I4LoadA(const PxI32* i); + +// QuatV +//(x = v[0], y=v[1], z=v[2], w=v3[3]) and array don't need to aligned +PX_FORCE_INLINE QuatV QuatVLoadU(const PxF32* v); +//(x = v[0], y=v[1], z=v[2], w=v3[3]) and array need to aligned, fast load +PX_FORCE_INLINE QuatV QuatVLoadA(const PxF32* v); +//(x, y, z, w) +PX_FORCE_INLINE QuatV QuatVLoadXYZW(const PxF32 x, const PxF32 y, const PxF32 z, const PxF32 w); + +// not added to public api +Vec4V Vec4V_From_PxVec3_WUndefined(const PxVec3& v); + +/////////////////////////////////////////////////// +// Construct a simd type from a different simd type +/////////////////////////////////////////////////// + +// Vec3V +//(v.x,v.y,v.z,0) +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V v); +//(v.x,v.y,v.z,undefined) - be very careful with w!=0 because many functions require w==0 for correct operation eg V3Dot, V3Length, V3Cross etc etc. +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V_WUndefined(const Vec4V v); + +// Vec4V +//(f.x,f.y,f.z,f.w) +PX_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f); +//((PxF32)f.x, (PxF32)f.y, (PxF32)f.z, (PxF32)f.w) +PX_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a); +//((PxF32)f.x, (PxF32)f.y, (PxF32)f.z, (PxF32)f.w) +PX_FORCE_INLINE Vec4V Vec4V_From_VecI32V(VecI32V a); +//(*(reinterpret_cast(&f.x), (reinterpret_cast(&f.y), (reinterpret_cast(&f.z), +//(reinterpret_cast(&f.w)) +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecU32V(VecU32V a); +//(*(reinterpret_cast(&f.x), (reinterpret_cast(&f.y), (reinterpret_cast(&f.z), +//(reinterpret_cast(&f.w)) +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecI32V(VecI32V a); + +// VecU32V +//(*(reinterpret_cast(&f.x), (reinterpret_cast(&f.y), (reinterpret_cast(&f.z), +//(reinterpret_cast(&f.w)) +PX_FORCE_INLINE VecU32V VecU32V_ReinterpretFrom_Vec4V(Vec4V a); +//(b[0], b[1], b[2], b[3]) +PX_FORCE_INLINE VecU32V VecU32V_From_BoolV(const BoolVArg b); + +// VecI32V +//(*(reinterpret_cast(&f.x), (reinterpret_cast(&f.y), (reinterpret_cast(&f.z), +//(reinterpret_cast(&f.w)) +PX_FORCE_INLINE VecI32V VecI32V_ReinterpretFrom_Vec4V(Vec4V a); +//((I32)a.x, (I32)a.y, (I32)a.z, (I32)a.w) +PX_FORCE_INLINE VecI32V VecI32V_From_Vec4V(Vec4V a); +//((I32)b.x, (I32)b.y, (I32)b.z, (I32)b.w) +PX_FORCE_INLINE VecI32V VecI32V_From_BoolV(const BoolVArg b); + +/////////////////////////////////////////////////// +// Convert from a simd type back to a scalar type +/////////////////////////////////////////////////// + +// FloatV +// a.x +PX_FORCE_INLINE void FStore(const FloatV a, PxF32* PX_RESTRICT f); + +// Vec3V +//(a.x,a.y,a.z) +PX_FORCE_INLINE void V3StoreA(const Vec3V a, PxVec3& f); +//(a.x,a.y,a.z) +PX_FORCE_INLINE void V3StoreU(const Vec3V a, PxVec3& f); + +// Vec4V +PX_FORCE_INLINE void V4StoreA(const Vec4V a, PxF32* f); +PX_FORCE_INLINE void V4StoreU(const Vec4V a, PxF32* f); + +// BoolV +PX_FORCE_INLINE void BStoreA(const BoolV b, PxU32* f); + +// VecU32V +PX_FORCE_INLINE void U4StoreA(const VecU32V uv, PxU32* u); + +// VecI32V +PX_FORCE_INLINE void I4StoreA(const VecI32V iv, PxI32* i); + +////////////////////////////////////////////////////////////////// +// Test that simd types have elements in the floating point range +////////////////////////////////////////////////////////////////// + +// check for each component is valid ie in floating point range +PX_FORCE_INLINE bool isFiniteFloatV(const FloatV a); +// check for each component is valid ie in floating point range +PX_FORCE_INLINE bool isFiniteVec3V(const Vec3V a); +// check for each component is valid ie in floating point range +PX_FORCE_INLINE bool isFiniteVec4V(const Vec4V a); + +// Check that w-component is zero. +PX_FORCE_INLINE bool isValidVec3V(const Vec3V a); + +////////////////////////////////////////////////////////////////// +// Tests that all elements of two 16-byte types are completely equivalent. +// Use these tests for unit testing and asserts only. +////////////////////////////////////////////////////////////////// + +namespace _VecMathTests +{ +PX_FORCE_INLINE Vec3V getInvalidVec3V(); +PX_FORCE_INLINE bool allElementsEqualFloatV(const FloatV a, const FloatV b); +PX_FORCE_INLINE bool allElementsEqualVec3V(const Vec3V a, const Vec3V b); +PX_FORCE_INLINE bool allElementsEqualVec4V(const Vec4V a, const Vec4V b); +PX_FORCE_INLINE bool allElementsEqualBoolV(const BoolV a, const BoolV b); +PX_FORCE_INLINE bool allElementsEqualVecU32V(const VecU32V a, const VecU32V b); +PX_FORCE_INLINE bool allElementsEqualVecI32V(const VecI32V a, const VecI32V b); + +PX_FORCE_INLINE bool allElementsEqualMat33V(const Mat33V& a, const Mat33V& b) +{ + return (allElementsEqualVec3V(a.col0, b.col0) && allElementsEqualVec3V(a.col1, b.col1) && + allElementsEqualVec3V(a.col2, b.col2)); +} +PX_FORCE_INLINE bool allElementsEqualMat34V(const Mat34V& a, const Mat34V& b) +{ + return (allElementsEqualVec3V(a.col0, b.col0) && allElementsEqualVec3V(a.col1, b.col1) && + allElementsEqualVec3V(a.col2, b.col2) && allElementsEqualVec3V(a.col3, b.col3)); +} +PX_FORCE_INLINE bool allElementsEqualMat44V(const Mat44V& a, const Mat44V& b) +{ + return (allElementsEqualVec4V(a.col0, b.col0) && allElementsEqualVec4V(a.col1, b.col1) && + allElementsEqualVec4V(a.col2, b.col2) && allElementsEqualVec4V(a.col3, b.col3)); +} + +PX_FORCE_INLINE bool allElementsNearEqualFloatV(const FloatV a, const FloatV b); +PX_FORCE_INLINE bool allElementsNearEqualVec3V(const Vec3V a, const Vec3V b); +PX_FORCE_INLINE bool allElementsNearEqualVec4V(const Vec4V a, const Vec4V b); +PX_FORCE_INLINE bool allElementsNearEqualMat33V(const Mat33V& a, const Mat33V& b) +{ + return (allElementsNearEqualVec3V(a.col0, b.col0) && allElementsNearEqualVec3V(a.col1, b.col1) && + allElementsNearEqualVec3V(a.col2, b.col2)); +} +PX_FORCE_INLINE bool allElementsNearEqualMat34V(const Mat34V& a, const Mat34V& b) +{ + return (allElementsNearEqualVec3V(a.col0, b.col0) && allElementsNearEqualVec3V(a.col1, b.col1) && + allElementsNearEqualVec3V(a.col2, b.col2) && allElementsNearEqualVec3V(a.col3, b.col3)); +} +PX_FORCE_INLINE bool allElementsNearEqualMat44V(const Mat44V& a, const Mat44V& b) +{ + return (allElementsNearEqualVec4V(a.col0, b.col0) && allElementsNearEqualVec4V(a.col1, b.col1) && + allElementsNearEqualVec4V(a.col2, b.col2) && allElementsNearEqualVec4V(a.col3, b.col3)); +} +} + +////////////////////////////////////////////////////////////////// +// Math operations on FloatV +////////////////////////////////////////////////////////////////// + +//(0,0,0,0) +PX_FORCE_INLINE FloatV FZero(); +//(1,1,1,1) +PX_FORCE_INLINE FloatV FOne(); +//(0.5,0.5,0.5,0.5) +PX_FORCE_INLINE FloatV FHalf(); +//(PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL) +PX_FORCE_INLINE FloatV FEps(); +//(PX_MAX_REAL, PX_MAX_REAL, PX_MAX_REAL PX_MAX_REAL) +PX_FORCE_INLINE FloatV FMax(); +//(-PX_MAX_REAL, -PX_MAX_REAL, -PX_MAX_REAL -PX_MAX_REAL) +PX_FORCE_INLINE FloatV FNegMax(); +//(1e-6f, 1e-6f, 1e-6f, 1e-6f) +PX_FORCE_INLINE FloatV FEps6(); +//((PxF32*)&1, (PxF32*)&1, (PxF32*)&1, (PxF32*)&1) + +//-f (per component) +PX_FORCE_INLINE FloatV FNeg(const FloatV f); +// a+b (per component) +PX_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b); +// a-b (per component) +PX_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b); +// a*b (per component) +PX_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b); +// 1.0f/a +PX_FORCE_INLINE FloatV FRecip(const FloatV a); +// 1.0f/a +PX_FORCE_INLINE FloatV FRecipFast(const FloatV a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE FloatV FRsqrt(const FloatV a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE FloatV FRsqrtFast(const FloatV a); +// sqrt(a) +PX_FORCE_INLINE FloatV FSqrt(const FloatV a); +// a*b+c +PX_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c); +// c-a*b +PX_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c); +// fabs(a) +PX_FORCE_INLINE FloatV FAbs(const FloatV a); +// c ? a : b (per component) +PX_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b); +// a>b (per component) +PX_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b); +// a>=b (per component) +PX_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b); +// a==b (per component) +PX_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b); +// Max(a,b) (per component) +PX_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b); +// Min(a,b) (per component) +PX_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b); +// Clamp(a,b) (per component) +PX_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV); + +// a.x>b.x +PX_FORCE_INLINE PxU32 FAllGrtr(const FloatV a, const FloatV b); +// a.x>=b.x +PX_FORCE_INLINE PxU32 FAllGrtrOrEq(const FloatV a, const FloatV b); +// a.x==b.x +PX_FORCE_INLINE PxU32 FAllEq(const FloatV a, const FloatV b); +// amax +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max); +// a>=min && a<=max +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV min, const FloatV max); +// a<-bounds || a>bounds +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV bounds); +// a>=-bounds && a<=bounds +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV bounds); + +// round float a to the near int +PX_FORCE_INLINE FloatV FRound(const FloatV a); +// calculate the sin of float a +PX_FORCE_INLINE FloatV FSin(const FloatV a); +// calculate the cos of float b +PX_FORCE_INLINE FloatV FCos(const FloatV a); + +////////////////////////////////////////////////////////////////// +// Math operations on Vec3V +////////////////////////////////////////////////////////////////// + +//(f,f,f,f) +PX_FORCE_INLINE Vec3V V3Splat(const FloatV f); + +//(x,y,z) +PX_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z); + +//(1,0,0,0) +PX_FORCE_INLINE Vec3V V3UnitX(); +//(0,1,0,0) +PX_FORCE_INLINE Vec3V V3UnitY(); +//(0,0,1,0) +PX_FORCE_INLINE Vec3V V3UnitZ(); + +//(f.x,f.x,f.x,f.x) +PX_FORCE_INLINE FloatV V3GetX(const Vec3V f); +//(f.y,f.y,f.y,f.y) +PX_FORCE_INLINE FloatV V3GetY(const Vec3V f); +//(f.z,f.z,f.z,f.z) +PX_FORCE_INLINE FloatV V3GetZ(const Vec3V f); + +//(f,v.y,v.z,v.w) +PX_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f); +//(v.x,f,v.z,v.w) +PX_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f); +//(v.x,v.y,f,v.w) +PX_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f); + +// v.x=f +PX_FORCE_INLINE void V3WriteX(Vec3V& v, const PxF32 f); +// v.y=f +PX_FORCE_INLINE void V3WriteY(Vec3V& v, const PxF32 f); +// v.z=f +PX_FORCE_INLINE void V3WriteZ(Vec3V& v, const PxF32 f); +// v.x=f.x, v.y=f.y, v.z=f.z +PX_FORCE_INLINE void V3WriteXYZ(Vec3V& v, const PxVec3& f); +// return v.x +PX_FORCE_INLINE PxF32 V3ReadX(const Vec3V& v); +// return v.y +PX_FORCE_INLINE PxF32 V3ReadY(const Vec3V& v); +// return v.y +PX_FORCE_INLINE PxF32 V3ReadZ(const Vec3V& v); +// return (v.x,v.y,v.z) +PX_FORCE_INLINE const PxVec3& V3ReadXYZ(const Vec3V& v); + +//(a.x, b.x, c.x) +PX_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c); +//(a.y, b.y, c.y) +PX_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c); +//(a.z, b.z, c.z) +PX_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c); + +//(0,0,0,0) +PX_FORCE_INLINE Vec3V V3Zero(); +//(1,1,1,1) +PX_FORCE_INLINE Vec3V V3One(); +//(PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL) +PX_FORCE_INLINE Vec3V V3Eps(); +//-c (per component) +PX_FORCE_INLINE Vec3V V3Neg(const Vec3V c); +// a+b (per component) +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b); +// a-b (per component) +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b); +// a*b (per component) +PX_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b); +// a*b (per component) +PX_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b); +// a/b (per component) +PX_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b); +// a/b (per component) +PX_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b); +// 1.0f/a +PX_FORCE_INLINE Vec3V V3Recip(const Vec3V a); +// 1.0f/a +PX_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a); +// a*b+c +PX_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c); +// c-a*b +PX_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c); +// a*b+c +PX_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c); +// c-a*b +PX_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c); +// fabs(a) +PX_FORCE_INLINE Vec3V V3Abs(const Vec3V a); + +// a.b +// Note: a.w and b.w must have value zero +PX_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b); +// aXb +// Note: a.w and b.w must have value zero +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b); +// |a.a|^1/2 +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3Length(const Vec3V a); +// a.a +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3LengthSq(const Vec3V a); +// a*|a.a|^-1/2 +// Note: a.w must have value zero +PX_FORCE_INLINE Vec3V V3Normalize(const Vec3V a); +// a.a>0 ? a*|a.a|^-1/2 : (0,0,0,0) +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3Length(const Vec3V a); +// a.a>0 ? a*|a.a|^-1/2 : unsafeReturnValue +// Note: a.w must have value zero +PX_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a, const Vec3V unsafeReturnValue); +// a.x + a.y + a.z +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3SumElems(const Vec3V a); + +// c ? a : b (per component) +PX_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b); +// a>b (per component) +PX_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b); +// a>=b (per component) +PX_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b); +// a==b (per component) +PX_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b); +// Max(a,b) (per component) +PX_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b); +// Min(a,b) (per component) +PX_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b); + +// Extract the maximum value from a +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a); + +// Extract the minimum value from a +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a); + +// Clamp(a,b) (per component) +PX_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV); + +// Extract the sign for each component +PX_FORCE_INLINE Vec3V V3Sign(const Vec3V a); + +// Test all components. +// (a.x>b.x && a.y>b.y && a.z>b.z) +// Note: a.w and b.w must have value zero +PX_FORCE_INLINE PxU32 V3AllGrtr(const Vec3V a, const Vec3V b); +// (a.x>=b.x && a.y>=b.y && a.z>=b.z) +// Note: a.w and b.w must have value zero +PX_FORCE_INLINE PxU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b); +// (a.x==b.x && a.y==b.y && a.z==b.z) +// Note: a.w and b.w must have value zero +PX_FORCE_INLINE PxU32 V3AllEq(const Vec3V a, const Vec3V b); +// a.xmax.x || a.y>max.y || a.z>max.z +// Note: a.w and min.w and max.w must have value zero +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max); +// a.x>=min.x && a.y>=min.y && a.z>=min.z && a.x<=max.x && a.y<=max.y && a.z<=max.z +// Note: a.w and min.w and max.w must have value zero +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max); +// a.x<-bounds.x || a.y<=-bounds.y || a.zbounds.x || a.y>bounds.y || a.z>bounds.z +// Note: a.w and bounds.w must have value zero +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds); +// a.x>=-bounds.x && a.y>=-bounds.y && a.z>=-bounds.z && a.x<=bounds.x && a.y<=bounds.y && a.z<=bounds.z +// Note: a.w and bounds.w must have value zero +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V bounds); + +//(floor(a.x + 0.5f), floor(a.y + 0.5f), floor(a.z + 0.5f)) +PX_FORCE_INLINE Vec3V V3Round(const Vec3V a); + +//(sinf(a.x), sinf(a.y), sinf(a.z)) +PX_FORCE_INLINE Vec3V V3Sin(const Vec3V a); +//(cosf(a.x), cosf(a.y), cosf(a.z)) +PX_FORCE_INLINE Vec3V V3Cos(const Vec3V a); + +//(a.y,a.z,a.z) +PX_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a); +//(a.x,a.y,a.x) +PX_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a); +//(a.y,a.z,a.x) +PX_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a); +//(a.z, a.x, a.y) +PX_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a); +//(a.z,a.z,a.y) +PX_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a); +//(a.y,a.x,a.x) +PX_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a); +//(0, v1.z, v0.y) +PX_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1); +//(v0.z, 0, v1.x) +PX_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1); +//(v1.y, v0.x, 0) +PX_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1); + +// Transpose 3 Vec3Vs inplace. Sets the w component to zero +// [ x0, y0, z0, w0] [ x1, y1, z1, w1] [ x2, y2, z2, w2] -> [x0 x1 x2 0] [y0 y1 y2 0] [z0 z1 z2 0] +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2); + +////////////////////////////////////////////////////////////////// +// Math operations on Vec4V +////////////////////////////////////////////////////////////////// + +//(f,f,f,f) +PX_FORCE_INLINE Vec4V V4Splat(const FloatV f); + +//(f[0],f[1],f[2],f[3]) +PX_FORCE_INLINE Vec4V V4Merge(const FloatV* const f); +//(x,y,z,w) +PX_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w); +//(x.w, y.w, z.w, w.w) +PX_FORCE_INLINE Vec4V V4MergeW(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w); +//(x.z, y.z, z.z, w.z) +PX_FORCE_INLINE Vec4V V4MergeZ(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w); +//(x.y, y.y, z.y, w.y) +PX_FORCE_INLINE Vec4V V4MergeY(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w); +//(x.x, y.x, z.x, w.x) +PX_FORCE_INLINE Vec4V V4MergeX(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w); + +//(a.x, b.x, a.y, b.y) +PX_FORCE_INLINE Vec4V V4UnpackXY(const Vec4VArg a, const Vec4VArg b); +//(a.z, b.z, a.w, b.w) +PX_FORCE_INLINE Vec4V V4UnpackZW(const Vec4VArg a, const Vec4VArg b); + +//(1,0,0,0) +PX_FORCE_INLINE Vec4V V4UnitW(); +//(0,1,0,0) +PX_FORCE_INLINE Vec4V V4UnitY(); +//(0,0,1,0) +PX_FORCE_INLINE Vec4V V4UnitZ(); +//(0,0,0,1) +PX_FORCE_INLINE Vec4V V4UnitW(); + +//(f.x,f.x,f.x,f.x) +PX_FORCE_INLINE FloatV V4GetX(const Vec4V f); +//(f.y,f.y,f.y,f.y) +PX_FORCE_INLINE FloatV V4GetY(const Vec4V f); +//(f.z,f.z,f.z,f.z) +PX_FORCE_INLINE FloatV V4GetZ(const Vec4V f); +//(f.w,f.w,f.w,f.w) +PX_FORCE_INLINE FloatV V4GetW(const Vec4V f); + +//(f,v.y,v.z,v.w) +PX_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f); +//(v.x,f,v.z,v.w) +PX_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f); +//(v.x,v.y,f,v.w) +PX_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f); +//(v.x,v.y,v.z,f) +PX_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f); + +//(v.x,v.y,v.z,0) +PX_FORCE_INLINE Vec4V V4ClearW(const Vec4V v); + +//(a[elementIndex], a[elementIndex], a[elementIndex], a[elementIndex]) +template +PX_FORCE_INLINE Vec4V V4SplatElement(Vec4V a); + +// v.x=f +PX_FORCE_INLINE void V4WriteX(Vec4V& v, const PxF32 f); +// v.y=f +PX_FORCE_INLINE void V4WriteY(Vec4V& v, const PxF32 f); +// v.z=f +PX_FORCE_INLINE void V4WriteZ(Vec4V& v, const PxF32 f); +// v.w=f +PX_FORCE_INLINE void V4WriteW(Vec4V& v, const PxF32 f); +// v.x=f.x, v.y=f.y, v.z=f.z +PX_FORCE_INLINE void V4WriteXYZ(Vec4V& v, const PxVec3& f); +// return v.x +PX_FORCE_INLINE PxF32 V4ReadX(const Vec4V& v); +// return v.y +PX_FORCE_INLINE PxF32 V4ReadY(const Vec4V& v); +// return v.z +PX_FORCE_INLINE PxF32 V4ReadZ(const Vec4V& v); +// return v.w +PX_FORCE_INLINE PxF32 V4ReadW(const Vec4V& v); +// return (v.x,v.y,v.z) +PX_FORCE_INLINE const PxVec3& V4ReadXYZ(const Vec4V& v); + +//(0,0,0,0) +PX_FORCE_INLINE Vec4V V4Zero(); +//(1,1,1,1) +PX_FORCE_INLINE Vec4V V4One(); +//(PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL) +PX_FORCE_INLINE Vec4V V4Eps(); + +//-c (per component) +PX_FORCE_INLINE Vec4V V4Neg(const Vec4V c); +// a+b (per component) +PX_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b); +// a-b (per component) +PX_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b); +// a*b (per component) +PX_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b); +// a*b (per component) +PX_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b); +// a/b (per component) +PX_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b); +// a/b (per component) +PX_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b); +// 1.0f/a +PX_FORCE_INLINE Vec4V V4Recip(const Vec4V a); +// 1.0f/a +PX_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a); +// a*b+c +PX_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c); +// c-a*b +PX_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c); +// a*b+c +PX_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c); +// c-a*b +PX_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c); + +// fabs(a) +PX_FORCE_INLINE Vec4V V4Abs(const Vec4V a); +// bitwise a & ~b +PX_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b); + +// a.b (W is taken into account) +PX_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b); +// a.b (same computation as V3Dot. W is ignored in input) +PX_FORCE_INLINE FloatV V4Dot3(const Vec4V a, const Vec4V b); +// aXb (same computation as V3Cross. W is ignored in input and undefined in output) +PX_FORCE_INLINE Vec4V V4Cross(const Vec4V a, const Vec4V b); + +//|a.a|^1/2 +PX_FORCE_INLINE FloatV V4Length(const Vec4V a); +// a.a +PX_FORCE_INLINE FloatV V4LengthSq(const Vec4V a); + +// a*|a.a|^-1/2 +PX_FORCE_INLINE Vec4V V4Normalize(const Vec4V a); +// a.a>0 ? a*|a.a|^-1/2 : unsafeReturnValue +PX_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a, const Vec4V unsafeReturnValue); +// a*|a.a|^-1/2 +PX_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a); + +// c ? a : b (per component) +PX_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b); +// a>b (per component) +PX_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b); +// a>=b (per component) +PX_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b); +// a==b (per component) +PX_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b); +// Max(a,b) (per component) +PX_FORCE_INLINE Vec4V V4Max(const Vec4V a, const Vec4V b); +// Min(a,b) (per component) +PX_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b); +// Get the maximum component from a +PX_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a); +// Get the minimum component from a +PX_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a); + +// Clamp(a,b) (per component) +PX_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV); + +// return 1 if all components of a are greater than all components of b. +PX_FORCE_INLINE PxU32 V4AllGrtr(const Vec4V a, const Vec4V b); +// return 1 if all components of a are greater than or equal to all components of b +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b); +// return 1 if XYZ components of a are greater than or equal to XYZ components of b. W is ignored. +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq3(const Vec4V a, const Vec4V b); +// return 1 if all components of a are equal to all components of b +PX_FORCE_INLINE PxU32 V4AllEq(const Vec4V a, const Vec4V b); +// return 1 if any XYZ component of a is greater than the corresponding component of b. W is ignored. +PX_FORCE_INLINE PxU32 V4AnyGrtr3(const Vec4V a, const Vec4V b); + +// round(a)(per component) +PX_FORCE_INLINE Vec4V V4Round(const Vec4V a); +// sin(a) (per component) +PX_FORCE_INLINE Vec4V V4Sin(const Vec4V a); +// cos(a) (per component) +PX_FORCE_INLINE Vec4V V4Cos(const Vec4V a); + +// Permute v into a new vec4v with YXWZ format +PX_FORCE_INLINE Vec4V V4PermYXWZ(const Vec4V v); +// Permute v into a new vec4v with XZXZ format +PX_FORCE_INLINE Vec4V V4PermXZXZ(const Vec4V v); +// Permute v into a new vec4v with YWYW format +PX_FORCE_INLINE Vec4V V4PermYWYW(const Vec4V v); +// Permute v into a new vec4v with YZXW format +PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V v); +// Permute v into a new vec4v with ZWXY format - equivalent to a swap of the two 64bit parts of the vector +PX_FORCE_INLINE Vec4V V4PermZWXY(const Vec4V a); + +// Permute v into a new vec4v with format {a[x], a[y], a[z], a[w]} +// V4Perm<1,3,1,3> is equal to V4PermYWYW +// V4Perm<0,2,0,2> is equal to V4PermXZXZ +// V3Perm<1,0,3,2> is equal to V4PermYXWZ +template +PX_FORCE_INLINE Vec4V V4Perm(const Vec4V a); + +// Transpose 4 Vec4Vs inplace. +// [ x0, y0, z0, w0] [ x1, y1, z1, w1] [ x2, y2, z2, w2] [ x3, y3, z3, w3] -> +// [ x0, x1, x2, x3] [ y0, y1, y2, y3] [ z0, z1, z2, z3] [ w0, w1, w2, w3] +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2); + +// q = cos(a/2) + u*sin(a/2) +PX_FORCE_INLINE QuatV QuatV_From_RotationAxisAngle(const Vec3V u, const FloatV a); +// convert q to a unit quaternion +PX_FORCE_INLINE QuatV QuatNormalize(const QuatV q); +//|q.q|^1/2 +PX_FORCE_INLINE FloatV QuatLength(const QuatV q); +// q.q +PX_FORCE_INLINE FloatV QuatLengthSq(const QuatV q); +// a.b +PX_FORCE_INLINE FloatV QuatDot(const QuatV a, const QuatV b); +//(-q.x, -q.y, -q.z, q.w) +PX_FORCE_INLINE QuatV QuatConjugate(const QuatV q); +//(q.x, q.y, q.z) +PX_FORCE_INLINE Vec3V QuatGetImaginaryPart(const QuatV q); +// convert quaternion to matrix 33 +PX_FORCE_INLINE Mat33V QuatGetMat33V(const QuatVArg q); +// convert quaternion to matrix 33 +PX_FORCE_INLINE void QuatGetMat33V(const QuatVArg q, Vec3V& column0, Vec3V& column1, Vec3V& column2); +// convert matrix 33 to quaternion +PX_FORCE_INLINE QuatV Mat33GetQuatV(const Mat33V& a); +// brief computes rotation of x-axis +PX_FORCE_INLINE Vec3V QuatGetBasisVector0(const QuatV q); +// brief computes rotation of y-axis +PX_FORCE_INLINE Vec3V QuatGetBasisVector1(const QuatV q); +// brief computes rotation of z-axis +PX_FORCE_INLINE Vec3V QuatGetBasisVector2(const QuatV q); +// calculate the rotation vector from q and v +PX_FORCE_INLINE Vec3V QuatRotate(const QuatV q, const Vec3V v); +// calculate the rotation vector from the conjugate quaternion and v +PX_FORCE_INLINE Vec3V QuatRotateInv(const QuatV q, const Vec3V v); +// quaternion multiplication +PX_FORCE_INLINE QuatV QuatMul(const QuatV a, const QuatV b); +// quaternion add +PX_FORCE_INLINE QuatV QuatAdd(const QuatV a, const QuatV b); +// (-q.x, -q.y, -q.z, -q.w) +PX_FORCE_INLINE QuatV QuatNeg(const QuatV q); +// (a.x - b.x, a.y-b.y, a.z-b.z, a.w-b.w ) +PX_FORCE_INLINE QuatV QuatSub(const QuatV a, const QuatV b); +// (a.x*b, a.y*b, a.z*b, a.w*b) +PX_FORCE_INLINE QuatV QuatScale(const QuatV a, const FloatV b); +// (x = v[0], y = v[1], z = v[2], w =v[3]) +PX_FORCE_INLINE QuatV QuatMerge(const FloatV* const v); +// (x = v[0], y = v[1], z = v[2], w =v[3]) +PX_FORCE_INLINE QuatV QuatMerge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w); +// (x = 0.f, y = 0.f, z = 0.f, w = 1.f) +PX_FORCE_INLINE QuatV QuatIdentity(); +// check for each component is valid +PX_FORCE_INLINE bool isFiniteQuatV(const QuatV q); +// check for each component is valid +PX_FORCE_INLINE bool isValidQuatV(const QuatV q); +// check for each component is valid +PX_FORCE_INLINE bool isSaneQuatV(const QuatV q); + +// Math operations on 16-byte aligned booleans. +// x=false y=false z=false w=false +PX_FORCE_INLINE BoolV BFFFF(); +// x=false y=false z=false w=true +PX_FORCE_INLINE BoolV BFFFT(); +// x=false y=false z=true w=false +PX_FORCE_INLINE BoolV BFFTF(); +// x=false y=false z=true w=true +PX_FORCE_INLINE BoolV BFFTT(); +// x=false y=true z=false w=false +PX_FORCE_INLINE BoolV BFTFF(); +// x=false y=true z=false w=true +PX_FORCE_INLINE BoolV BFTFT(); +// x=false y=true z=true w=false +PX_FORCE_INLINE BoolV BFTTF(); +// x=false y=true z=true w=true +PX_FORCE_INLINE BoolV BFTTT(); +// x=true y=false z=false w=false +PX_FORCE_INLINE BoolV BTFFF(); +// x=true y=false z=false w=true +PX_FORCE_INLINE BoolV BTFFT(); +// x=true y=false z=true w=false +PX_FORCE_INLINE BoolV BTFTF(); +// x=true y=false z=true w=true +PX_FORCE_INLINE BoolV BTFTT(); +// x=true y=true z=false w=false +PX_FORCE_INLINE BoolV BTTFF(); +// x=true y=true z=false w=true +PX_FORCE_INLINE BoolV BTTFT(); +// x=true y=true z=true w=false +PX_FORCE_INLINE BoolV BTTTF(); +// x=true y=true z=true w=true +PX_FORCE_INLINE BoolV BTTTT(); + +// x=false y=false z=false w=true +PX_FORCE_INLINE BoolV BWMask(); +// x=true y=false z=false w=false +PX_FORCE_INLINE BoolV BXMask(); +// x=false y=true z=false w=false +PX_FORCE_INLINE BoolV BYMask(); +// x=false y=false z=true w=false +PX_FORCE_INLINE BoolV BZMask(); + +// get x component +PX_FORCE_INLINE BoolV BGetX(const BoolV f); +// get y component +PX_FORCE_INLINE BoolV BGetY(const BoolV f); +// get z component +PX_FORCE_INLINE BoolV BGetZ(const BoolV f); +// get w component +PX_FORCE_INLINE BoolV BGetW(const BoolV f); + +// Use elementIndex to splat xxxx or yyyy or zzzz or wwww +template +PX_FORCE_INLINE BoolV BSplatElement(Vec4V a); + +// component-wise && (AND) +PX_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b); +// component-wise || (OR) +PX_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b); +// component-wise not +PX_FORCE_INLINE BoolV BNot(const BoolV a); + +// if all four components are true, return true, otherwise return false +PX_FORCE_INLINE BoolV BAllTrue4(const BoolV a); + +// if any four components is true, return true, otherwise return false +PX_FORCE_INLINE BoolV BAnyTrue4(const BoolV a); + +// if all three(0, 1, 2) components are true, return true, otherwise return false +PX_FORCE_INLINE BoolV BAllTrue3(const BoolV a); + +// if any three (0, 1, 2) components is true, return true, otherwise return false +PX_FORCE_INLINE BoolV BAnyTrue3(const BoolV a); + +// Return 1 if all components equal, zero otherwise. +PX_FORCE_INLINE PxU32 BAllEq(const BoolV a, const BoolV b); + +// Specialized/faster BAllEq function for b==TTTT +PX_FORCE_INLINE PxU32 BAllEqTTTT(const BoolV a); +// Specialized/faster BAllEq function for b==FFFF +PX_FORCE_INLINE PxU32 BAllEqFFFF(const BoolV a); + +/// Get BoolV as bits set in an PxU32. A bit in the output is set if the element is 'true' in the input. +/// There is a bit for each element in a, with element 0s value held in bit0, element 1 in bit 1s and so forth. +/// If nothing is true in the input it will return 0, and if all are true if will return 0xf. +/// NOTE! That performance of the function varies considerably by platform, thus it is recommended to use +/// where your algorithm really needs a BoolV in an integer variable. +PX_FORCE_INLINE PxU32 BGetBitMask(const BoolV a); + +// VecI32V stuff + +PX_FORCE_INLINE VecI32V VecI32V_Zero(); + +PX_FORCE_INLINE VecI32V VecI32V_One(); + +PX_FORCE_INLINE VecI32V VecI32V_Two(); + +PX_FORCE_INLINE VecI32V VecI32V_MinusOne(); + +// Compute a shift parameter for VecI32V_LeftShift and VecI32V_RightShift +// Each element of shift must be identical ie the vector must have form {count, count, count, count} with count>=0 +PX_FORCE_INLINE VecShiftV VecI32V_PrepareShift(const VecI32VArg shift); + +// Shift each element of a leftwards by the same amount +// Compute shift with VecI32V_PrepareShift +//{a.x<>shift[0], a.y>>shift[0], a.z>>shift[0], a.w>>shift[0]} +PX_FORCE_INLINE VecI32V VecI32V_RightShift(const VecI32VArg a, const VecShiftVArg shift); + +PX_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b); + +PX_FORCE_INLINE VecI32V VecI32V_Or(const VecI32VArg a, const VecI32VArg b); + +PX_FORCE_INLINE VecI32V VecI32V_GetX(const VecI32VArg a); + +PX_FORCE_INLINE VecI32V VecI32V_GetY(const VecI32VArg a); + +PX_FORCE_INLINE VecI32V VecI32V_GetZ(const VecI32VArg a); + +PX_FORCE_INLINE VecI32V VecI32V_GetW(const VecI32VArg a); + +PX_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b); + +PX_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b); + +PX_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b); + +PX_FORCE_INLINE VecI32V V4I32Sel(const BoolV c, const VecI32V a, const VecI32V b); + +// VecU32V stuff + +PX_FORCE_INLINE VecU32V U4Zero(); + +PX_FORCE_INLINE VecU32V U4One(); + +PX_FORCE_INLINE VecU32V U4Two(); + +PX_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b); + +PX_FORCE_INLINE VecU32V V4U32Sel(const BoolV c, const VecU32V a, const VecU32V b); + +PX_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b); + +PX_FORCE_INLINE VecU32V V4U32xor(VecU32V a, VecU32V b); + +PX_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b); + +PX_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b); + +// VecU32 - why does this not return a bool? +PX_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b); + +// Math operations on 16-byte aligned Mat33s (represents any 3x3 matrix) +// a*b +PX_FORCE_INLINE Vec3V M33MulV3(const Mat33V& a, const Vec3V b); +// A*x + b +PX_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V& A, const Vec3V b, const Vec3V c); +// transpose(a) * b +PX_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V& a, const Vec3V b); +// a*b +PX_FORCE_INLINE Mat33V M33MulM33(const Mat33V& a, const Mat33V& b); +// a+b +PX_FORCE_INLINE Mat33V M33Add(const Mat33V& a, const Mat33V& b); +// a+b +PX_FORCE_INLINE Mat33V M33Sub(const Mat33V& a, const Mat33V& b); +//-a +PX_FORCE_INLINE Mat33V M33Neg(const Mat33V& a); +// absolute value of the matrix +PX_FORCE_INLINE Mat33V M33Abs(const Mat33V& a); +// inverse mat +PX_FORCE_INLINE Mat33V M33Inverse(const Mat33V& a); +// transpose(a) +PX_FORCE_INLINE Mat33V M33Trnsps(const Mat33V& a); +// create an identity matrix +PX_FORCE_INLINE Mat33V M33Identity(); + +// create a vec3 to store the diagonal element of the M33 +PX_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg); + +// Not implemented +// return 1 if all components of a are equal to all components of b +// PX_FORCE_INLINE PxU32 V4U32AllEq(const VecU32V a, const VecU32V b); +// v.w=f +// PX_FORCE_INLINE void V3WriteW(Vec3V& v, const PxF32 f); +// PX_FORCE_INLINE PxF32 V3ReadW(const Vec3V& v); + +// Not used +// PX_FORCE_INLINE Vec4V V4LoadAligned(Vec4V* addr); +// PX_FORCE_INLINE Vec4V V4LoadUnaligned(Vec4V* addr); +// floor(a)(per component) +// PX_FORCE_INLINE Vec4V V4Floor(Vec4V a); +// ceil(a) (per component) +// PX_FORCE_INLINE Vec4V V4Ceil(Vec4V a); +// PX_FORCE_INLINE VecU32V V4ConvertToU32VSaturate(const Vec4V a, PxU32 power); + +// Math operations on 16-byte aligned Mat34s (represents transformation matrix - rotation and translation). +// namespace _Mat34V +//{ +// //a*b +// PX_FORCE_INLINE Vec3V multiplyV(const Mat34V& a, const Vec3V b); +// //a_rotation * b +// PX_FORCE_INLINE Vec3V multiply3X3V(const Mat34V& a, const Vec3V b); +// //transpose(a_rotation)*b +// PX_FORCE_INLINE Vec3V multiplyTranspose3X3V(const Mat34V& a, const Vec3V b); +// //a*b +// PX_FORCE_INLINE Mat34V multiplyV(const Mat34V& a, const Mat34V& b); +// //a_rotation*b +// PX_FORCE_INLINE Mat33V multiply3X3V(const Mat34V& a, const Mat33V& b); +// //a_rotation*b_rotation +// PX_FORCE_INLINE Mat33V multiply3X3V(const Mat34V& a, const Mat34V& b); +// //a+b +// PX_FORCE_INLINE Mat34V addV(const Mat34V& a, const Mat34V& b); +// //a^-1 +// PX_FORCE_INLINE Mat34V getInverseV(const Mat34V& a); +// //transpose(a_rotation) +// PX_FORCE_INLINE Mat33V getTranspose3X3(const Mat34V& a); +//}; //namespace _Mat34V + +// a*b +//#define M34MulV3(a,b) (M34MulV3(a,b)) +////a_rotation * b +//#define M34Mul33V3(a,b) (M34Mul33V3(a,b)) +////transpose(a_rotation)*b +//#define M34TrnspsMul33V3(a,b) (M34TrnspsMul33V3(a,b)) +////a*b +//#define M34MulM34(a,b) (_Mat34V::multiplyV(a,b)) +// a_rotation*b +//#define M34MulM33(a,b) (M34MulM33(a,b)) +// a_rotation*b_rotation +//#define M34Mul33MM34(a,b) (M34MulM33(a,b)) +// a+b +//#define M34Add(a,b) (M34Add(a,b)) +////a^-1 +//#define M34Inverse(a,b) (M34Inverse(a)) +// transpose(a_rotation) +//#define M34Trnsps33(a) (M33Trnsps3X3(a)) + +// Math operations on 16-byte aligned Mat44s (represents any 4x4 matrix) +// namespace _Mat44V +//{ +// //a*b +// PX_FORCE_INLINE Vec4V multiplyV(const Mat44V& a, const Vec4V b); +// //transpose(a)*b +// PX_FORCE_INLINE Vec4V multiplyTransposeV(const Mat44V& a, const Vec4V b); +// //a*b +// PX_FORCE_INLINE Mat44V multiplyV(const Mat44V& a, const Mat44V& b); +// //a+b +// PX_FORCE_INLINE Mat44V addV(const Mat44V& a, const Mat44V& b); +// //a&-1 +// PX_FORCE_INLINE Mat44V getInverseV(const Mat44V& a); +// //transpose(a) +// PX_FORCE_INLINE Mat44V getTransposeV(const Mat44V& a); +//}; //namespace _Mat44V + +// namespace _VecU32V +//{ +// // pack 8 U32s to 8 U16s with saturation +// PX_FORCE_INLINE VecU16V pack2U32VToU16VSaturate(VecU32V a, VecU32V b); +// PX_FORCE_INLINE VecU32V orV(VecU32V a, VecU32V b); +// PX_FORCE_INLINE VecU32V andV(VecU32V a, VecU32V b); +// PX_FORCE_INLINE VecU32V andcV(VecU32V a, VecU32V b); +// // conversion from integer to float +// PX_FORCE_INLINE Vec4V convertToVec4V(VecU32V a); +// // splat a[elementIndex] into all fields of a +// template +// PX_FORCE_INLINE VecU32V splatElement(VecU32V a); +// PX_FORCE_INLINE void storeAligned(VecU32V a, VecU32V* address); +//}; + +// namespace _VecI32V +//{ +// template PX_FORCE_INLINE VecI32V splatI32(); +//}; +// +// namespace _VecU16V +//{ +// PX_FORCE_INLINE VecU16V orV(VecU16V a, VecU16V b); +// PX_FORCE_INLINE VecU16V andV(VecU16V a, VecU16V b); +// PX_FORCE_INLINE VecU16V andcV(VecU16V a, VecU16V b); +// PX_FORCE_INLINE void storeAligned(VecU16V val, VecU16V *address); +// PX_FORCE_INLINE VecU16V loadAligned(VecU16V* addr); +// PX_FORCE_INLINE VecU16V loadUnaligned(VecU16V* addr); +// PX_FORCE_INLINE VecU16V compareGt(VecU16V a, VecU16V b); +// template +// PX_FORCE_INLINE VecU16V splatElement(VecU16V a); +// PX_FORCE_INLINE VecU16V subtractModulo(VecU16V a, VecU16V b); +// PX_FORCE_INLINE VecU16V addModulo(VecU16V a, VecU16V b); +// PX_FORCE_INLINE VecU32V getLo16(VecU16V a); // [0,2,4,6] 16-bit values to [0,1,2,3] 32-bit vector +// PX_FORCE_INLINE VecU32V getHi16(VecU16V a); // [1,3,5,7] 16-bit values to [0,1,2,3] 32-bit vector +//}; +// +// namespace _VecI16V +//{ +// template PX_FORCE_INLINE VecI16V splatImmediate(); +//}; +// +// namespace _VecU8V +//{ +//}; + +// a*b +//#define M44MulV4(a,b) (M44MulV4(a,b)) +////transpose(a)*b +//#define M44TrnspsMulV4(a,b) (M44TrnspsMulV4(a,b)) +////a*b +//#define M44MulM44(a,b) (M44MulM44(a,b)) +////a+b +//#define M44Add(a,b) (M44Add(a,b)) +////a&-1 +//#define M44Inverse(a) (M44Inverse(a)) +////transpose(a) +//#define M44Trnsps(a) (M44Trnsps(a)) + +// dsequeira: these used to be assert'd out in SIMD builds, but they're necessary if +// we want to be able to write some scalar functions which run using SIMD data structures + +PX_FORCE_INLINE void V3WriteX(Vec3V& v, const PxF32 f) +{ + reinterpret_cast(v).x = f; +} + +PX_FORCE_INLINE void V3WriteY(Vec3V& v, const PxF32 f) +{ + reinterpret_cast(v).y = f; +} + +PX_FORCE_INLINE void V3WriteZ(Vec3V& v, const PxF32 f) +{ + reinterpret_cast(v).z = f; +} + +PX_FORCE_INLINE void V3WriteXYZ(Vec3V& v, const PxVec3& f) +{ + reinterpret_cast(v) = f; +} + +PX_FORCE_INLINE PxF32 V3ReadX(const Vec3V& v) +{ + return reinterpret_cast(v).x; +} + +PX_FORCE_INLINE PxF32 V3ReadY(const Vec3V& v) +{ + return reinterpret_cast(v).y; +} + +PX_FORCE_INLINE PxF32 V3ReadZ(const Vec3V& v) +{ + return reinterpret_cast(v).z; +} + +PX_FORCE_INLINE const PxVec3& V3ReadXYZ(const Vec3V& v) +{ + return reinterpret_cast(v); +} + +PX_FORCE_INLINE void V4WriteX(Vec4V& v, const PxF32 f) +{ + reinterpret_cast(v).x = f; +} + +PX_FORCE_INLINE void V4WriteY(Vec4V& v, const PxF32 f) +{ + reinterpret_cast(v).y = f; +} + +PX_FORCE_INLINE void V4WriteZ(Vec4V& v, const PxF32 f) +{ + reinterpret_cast(v).z = f; +} + +PX_FORCE_INLINE void V4WriteW(Vec4V& v, const PxF32 f) +{ + reinterpret_cast(v).w = f; +} + +PX_FORCE_INLINE void V4WriteXYZ(Vec4V& v, const PxVec3& f) +{ + reinterpret_cast(v) = f; +} + +PX_FORCE_INLINE PxF32 V4ReadX(const Vec4V& v) +{ + return reinterpret_cast(v).x; +} + +PX_FORCE_INLINE PxF32 V4ReadY(const Vec4V& v) +{ + return reinterpret_cast(v).y; +} + +PX_FORCE_INLINE PxF32 V4ReadZ(const Vec4V& v) +{ + return reinterpret_cast(v).z; +} + +PX_FORCE_INLINE PxF32 V4ReadW(const Vec4V& v) +{ + return reinterpret_cast(v).w; +} + +PX_FORCE_INLINE const PxVec3& V4ReadXYZ(const Vec4V& v) +{ + return reinterpret_cast(v); +} + +// this macro transposes 4 Vec4V into 3 Vec4V (assuming that the W component can be ignored +#define PX_TRANSPOSE_44_34(inA, inB, inC, inD, outA, outB, outC) \ + \ +outA = V4UnpackXY(inA, inC); \ + \ +inA = V4UnpackZW(inA, inC); \ + \ +inC = V4UnpackXY(inB, inD); \ + \ +inB = V4UnpackZW(inB, inD); \ + \ +outB = V4UnpackZW(outA, inC); \ + \ +outA = V4UnpackXY(outA, inC); \ + \ +outC = V4UnpackXY(inA, inB); + +// this macro transposes 3 Vec4V into 4 Vec4V (with W components as garbage!) +#define PX_TRANSPOSE_34_44(inA, inB, inC, outA, outB, outC, outD) \ + outA = V4UnpackXY(inA, inC); \ + inA = V4UnpackZW(inA, inC); \ + outC = V4UnpackXY(inB, inB); \ + inC = V4UnpackZW(inB, inB); \ + outB = V4UnpackZW(outA, outC); \ + outA = V4UnpackXY(outA, outC); \ + outC = V4UnpackXY(inA, inC); \ + outD = V4UnpackZW(inA, inC); + +#define PX_TRANSPOSE_44(inA, inB, inC, inD, outA, outB, outC, outD) \ + outA = V4UnpackXY(inA, inC); \ + inA = V4UnpackZW(inA, inC); \ + inC = V4UnpackXY(inB, inD); \ + inB = V4UnpackZW(inB, inD); \ + outB = V4UnpackZW(outA, inC); \ + outA = V4UnpackXY(outA, inC); \ + outC = V4UnpackXY(inA, inB); \ + outD = V4UnpackZW(inA, inB); + +// This function returns a Vec4V, where each element is the dot product of one pair of Vec3Vs. On PC, each element in +// the result should be identical to the results if V3Dot was performed +// for each pair of Vec3V. +// However, on other platforms, the result might diverge by some small margin due to differences in FP rounding, e.g. if +// _mm_dp_ps was used or some other approximate dot product or fused madd operations +// were used. +// Where there does not exist a hw-accelerated dot-product operation, this approach should be the fastest way to compute +// the dot product of 4 vectors. +PX_FORCE_INLINE Vec4V V3Dot4(const Vec3VArg a0, const Vec3VArg b0, const Vec3VArg a1, const Vec3VArg b1, + const Vec3VArg a2, const Vec3VArg b2, const Vec3VArg a3, const Vec3VArg b3) +{ + Vec4V a0b0 = Vec4V_From_Vec3V(V3Mul(a0, b0)); + Vec4V a1b1 = Vec4V_From_Vec3V(V3Mul(a1, b1)); + Vec4V a2b2 = Vec4V_From_Vec3V(V3Mul(a2, b2)); + Vec4V a3b3 = Vec4V_From_Vec3V(V3Mul(a3, b3)); + + Vec4V aTrnsps, bTrnsps, cTrnsps; + + PX_TRANSPOSE_44_34(a0b0, a1b1, a2b2, a3b3, aTrnsps, bTrnsps, cTrnsps); + + return V4Add(V4Add(aTrnsps, bTrnsps), cTrnsps); +} + +//(f.x,f.y,f.z,0) - Alternative/faster V3LoadU implementation when it is safe to read "W", i.e. the 32bits after the PxVec3. +PX_FORCE_INLINE Vec3V V3LoadU_SafeReadW(const PxVec3& f) +{ + return Vec3V_From_Vec4V(V4LoadU(&f.x)); +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +// Now for the cross-platform implementations of the 16-byte aligned maths functions (win32/360/ppu/spu etc). +#if COMPILE_VECTOR_INTRINSICS +#include "PsInlineAoS.h" +#else // #if COMPILE_VECTOR_INTRINSICS +#include "PsVecMathAoSScalarInline.h" +#endif // #if !COMPILE_VECTOR_INTRINSICS +#include "PsVecQuat.h" + +#endif // PSFOUNDATION_PSVECMATH_H diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h new file mode 100644 index 000000000..8607565c8 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h @@ -0,0 +1,250 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECMATHAOSSCALAR_H +#define PSFOUNDATION_PSVECMATHAOSSCALAR_H + +#if COMPILE_VECTOR_INTRINSICS +#error Scalar version should not be included when using vector intrinsics. +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +struct VecI16V; +struct VecU16V; +struct VecI32V; +struct VecU32V; +struct Vec4V; +typedef Vec4V QuatV; + +PX_ALIGN_PREFIX(16) +struct FloatV +{ + PxF32 x; + PxF32 pad[3]; + FloatV() + { + } + FloatV(const PxF32 _x) : x(_x) + { + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Vec4V +{ + PxF32 x, y, z, w; + Vec4V() + { + } + Vec4V(const PxF32 _x, const PxF32 _y, const PxF32 _z, const PxF32 _w) : x(_x), y(_y), z(_z), w(_w) + { + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Vec3V +{ + PxF32 x, y, z; + PxF32 pad; + Vec3V() + { + } + Vec3V(const PxF32 _x, const PxF32 _y, const PxF32 _z) : x(_x), y(_y), z(_z), pad(0.0f) + { + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct BoolV +{ + PxU32 ux, uy, uz, uw; + BoolV() + { + } + BoolV(const PxU32 _x, const PxU32 _y, const PxU32 _z, const PxU32 _w) : ux(_x), uy(_y), uz(_z), uw(_w) + { + } +} PX_ALIGN_SUFFIX(16); + +struct Mat33V +{ + Mat33V() + { + } + Mat33V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec3V col0; + Vec3V col1; + Vec3V col2; +}; + +struct Mat34V +{ + Mat34V() + { + } + Mat34V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2, const Vec3V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec3V col0; + Vec3V col1; + Vec3V col2; + Vec3V col3; +}; + +struct Mat43V +{ + Mat43V() + { + } + Mat43V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec4V col0; + Vec4V col1; + Vec4V col2; +}; + +struct Mat44V +{ + Mat44V() + { + } + Mat44V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2, const Vec4V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec4V col0; + Vec4V col1; + Vec4V col2; + Vec4V col3; +}; + +PX_ALIGN_PREFIX(16) +struct VecU32V +{ + PxU32 u32[4]; + PX_FORCE_INLINE VecU32V() + { + } + PX_FORCE_INLINE VecU32V(PxU32 a, PxU32 b, PxU32 c, PxU32 d) + { + u32[0] = a; + u32[1] = b; + u32[2] = c; + u32[3] = d; + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct VecI32V +{ + PxI32 i32[4]; + PX_FORCE_INLINE VecI32V() + { + } + PX_FORCE_INLINE VecI32V(PxI32 a, PxI32 b, PxI32 c, PxI32 d) + { + i32[0] = a; + i32[1] = b; + i32[2] = c; + i32[3] = d; + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct VecI16V +{ + PxI16 i16[8]; + PX_FORCE_INLINE VecI16V() + { + } + PX_FORCE_INLINE VecI16V(PxI16 a, PxI16 b, PxI16 c, PxI16 d, PxI16 e, PxI16 f, PxI16 g, PxI16 h) + { + i16[0] = a; + i16[1] = b; + i16[2] = c; + i16[3] = d; + i16[4] = e; + i16[5] = f; + i16[6] = g; + i16[7] = h; + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct VecU16V +{ + union + { + PxU16 u16[8]; + PxI16 i16[8]; + }; + PX_FORCE_INLINE VecU16V() + { + } + PX_FORCE_INLINE VecU16V(PxU16 a, PxU16 b, PxU16 c, PxU16 d, PxU16 e, PxU16 f, PxU16 g, PxU16 h) + { + u16[0] = a; + u16[1] = b; + u16[2] = c; + u16[3] = d; + u16[4] = e; + u16[5] = f; + u16[6] = g; + u16[7] = h; + } +} PX_ALIGN_SUFFIX(16); + +#define FloatVArg FloatV & +#define Vec3VArg Vec3V & +#define Vec4VArg Vec4V & +#define BoolVArg BoolV & +#define VecU32VArg VecU32V & +#define VecI32VArg VecI32V & +#define VecU16VArg VecU16V & +#define VecI16VArg VecI16V & +#define QuatVArg QuatV & + +#define VecCrossV Vec3V + +typedef VecI32V VecShiftV; +#define VecShiftVArg VecShiftV & + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PX_PHYSICS_COMMON_VECMATH_INLINE_SCALAR diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h new file mode 100644 index 000000000..e88f79c9b --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h @@ -0,0 +1,2275 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECMATHAOSSCALARINLINE_H +#define PSFOUNDATION_PSVECMATHAOSSCALARINLINE_H + +#if COMPILE_VECTOR_INTRINSICS +#error Scalar version should not be included when using vector intrinsics. +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +#define BOOL_TO_U32(b) (PxU32)(- PxI32(b)) +#define TRUE_TO_U32 (PxU32)(-1) +#define FALSE_TO_U32 (PxU32)(0) + +#define BOOL_TO_U16(b) (PxU16)(- PxI32(b)) + +#define PX_VECMATH_ASSERT_ENABLED 0 + +#if PX_VECMATH_ASSERT_ENABLED +#define VECMATHAOS_ASSERT(x) { PX_ASSERT(x); } +#else +#define VECMATHAOS_ASSERT(x) +#endif + +///////////////////////////////////////////////////////////////////// +////INTERNAL USE ONLY AND TESTS +///////////////////////////////////////////////////////////////////// + +namespace internalScalarSimd +{ +PX_FORCE_INLINE PxF32 FStore(const FloatV a) +{ + return a.x; +} + +PX_FORCE_INLINE bool hasZeroElementInFloatV(const FloatV a) +{ + return (0 == a.x); +} + +PX_FORCE_INLINE bool hasZeroElementInVec3V(const Vec3V a) +{ + return (0 == a.x || 0 == a.y || 0 == a.z); +} + +PX_FORCE_INLINE bool hasZeroElementInVec4V(const Vec4V a) +{ + return (0 == a.x || 0 == a.y || 0 == a.z || 0 == a.w); +} +} + +namespace _VecMathTests +{ +// PT: this function returns an invalid Vec3V (W!=0.0f) just for unit-testing 'isValidVec3V' +PX_FORCE_INLINE Vec3V getInvalidVec3V() +{ + Vec3V tmp; + tmp.x = tmp.y = tmp.z = 0.0f; + tmp.pad = 1.0f; + return tmp; +} + +PX_FORCE_INLINE bool allElementsEqualFloatV(const FloatV a, const FloatV b) +{ + return (a.x == b.x); +} + +PX_FORCE_INLINE bool allElementsEqualVec3V(const Vec3V a, const Vec3V b) +{ + return (a.x == b.x && a.y == b.y && a.z == b.z); +} + +PX_FORCE_INLINE bool allElementsEqualVec4V(const Vec4V a, const Vec4V b) +{ + return (a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w); +} + +PX_FORCE_INLINE bool allElementsEqualBoolV(const BoolV a, const BoolV b) +{ + return (a.ux == b.ux && a.uy == b.uy && a.uz == b.uz && a.uw == b.uw); +} + +PX_FORCE_INLINE bool allElementsEqualVecU32V(const VecU32V a, const VecU32V b) +{ + return (a.u32[0] == b.u32[0] && a.u32[1] == b.u32[1] && a.u32[2] == b.u32[2] && a.u32[3] == b.u32[3]); +} + +PX_FORCE_INLINE bool allElementsEqualVecI32V(const VecI32V a, const VecI32V b) +{ + return (a.i32[0] == b.i32[0] && a.i32[1] == b.i32[1] && a.i32[2] == b.i32[2] && a.i32[3] == b.i32[3]); +} + +#define VECMATH_AOS_EPSILON (1e-3f) + +PX_FORCE_INLINE bool allElementsNearEqualFloatV(const FloatV a, const FloatV b) +{ + const PxF32 cx = a.x - b.x; + return (cx > -VECMATH_AOS_EPSILON && cx < VECMATH_AOS_EPSILON); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec3V(const Vec3V a, const Vec3V b) +{ + const PxF32 cx = a.x - b.x; + const PxF32 cy = a.y - b.y; + const PxF32 cz = a.z - b.z; + return (cx > -VECMATH_AOS_EPSILON && cx < VECMATH_AOS_EPSILON && cy > -VECMATH_AOS_EPSILON && + cy < VECMATH_AOS_EPSILON && cz > -VECMATH_AOS_EPSILON && cz < VECMATH_AOS_EPSILON); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec4V(const Vec4V a, const Vec4V b) +{ + const PxF32 cx = a.x - b.x; + const PxF32 cy = a.y - b.y; + const PxF32 cz = a.z - b.z; + const PxF32 cw = a.w - b.w; + return (cx > -VECMATH_AOS_EPSILON && cx < VECMATH_AOS_EPSILON && cy > -VECMATH_AOS_EPSILON && + cy < VECMATH_AOS_EPSILON && cz > -VECMATH_AOS_EPSILON && cz < VECMATH_AOS_EPSILON && + cw > -VECMATH_AOS_EPSILON && cw < VECMATH_AOS_EPSILON); +} +} + +/////////////////////////////////////////////////////// + +PX_FORCE_INLINE bool isValidVec3V(const Vec3V a) +{ + return a.pad == 0.f; +} + +PX_FORCE_INLINE bool isFiniteFloatV(const FloatV a) +{ + return PxIsFinite(a.x); +} + +PX_FORCE_INLINE bool isFiniteVec3V(const Vec3V a) +{ + return PxIsFinite(a.x) && PxIsFinite(a.y) && PxIsFinite(a.z); +} + +PX_FORCE_INLINE bool isFiniteVec4V(const Vec4V a) +{ + return PxIsFinite(a.x) && PxIsFinite(a.y) && PxIsFinite(a.z) && PxIsFinite(a.w); +} + +///////////////////////////////////////////////////////////////////// +////VECTORISED FUNCTION IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE FloatV FLoad(const PxF32 f) +{ + return FloatV(f); +} + +PX_FORCE_INLINE Vec3V V3Load(const PxF32 f) +{ + return Vec3V(f, f, f); +} + +PX_FORCE_INLINE Vec4V V4Load(const PxF32 f) +{ + return Vec4V(f, f, f, f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool f) +{ +#if PX_ARM + // SD: Android ARM builds fail if this is done with a cast. + // Might also fail because of something else but the select + // operator here seems to fix everything that failed in release builds. + return f ? BTTTT() : BFFFF(); +#else + return BoolV(BOOL_TO_U32(f), BOOL_TO_U32(f), BOOL_TO_U32(f), BOOL_TO_U32(f)); +#endif +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxVec3& f) +{ + return Vec3V(f.x, f.y, f.z); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxVec3& f) +{ + return Vec3V(f.x, f.y, f.z); +} + +PX_FORCE_INLINE Vec3V V3LoadUnsafeA(const PxVec3& f) +{ + return Vec3V(f.x, f.y, f.z); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxF32* const f) +{ + return Vec3V(f[0], f[1], f[2]); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxF32* const f) +{ + return Vec3V(f[0], f[1], f[2]); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V f) +{ + return Vec3V(f.x, f.y, f.z); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V_WUndefined(const Vec4V v) +{ + return Vec3V(v.x, v.y, v.z); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f) +{ + return Vec4V(f.x, f.y, f.z, 0.0f); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_FloatV(FloatV f) +{ + return Vec4V(f.x, f.x, f.x, f.x); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV(FloatV f) +{ + return Vec3V(f.x, f.x, f.x); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV_WUndefined(FloatV f) +{ + return Vec3V(f.x, f.x, f.x); +} + +PX_FORCE_INLINE Vec4V V4LoadA(const PxF32* const f) +{ + return Vec4V(f[0], f[1], f[2], f[3]); +} + +PX_FORCE_INLINE void V4StoreA(const Vec4V a, PxF32* f) +{ + *reinterpret_cast(f) = a; +} + +PX_FORCE_INLINE void V4StoreU(const Vec4V a, PxF32* f) +{ + *reinterpret_cast(f) = *reinterpret_cast(&a.x); +} + +PX_FORCE_INLINE void BStoreA(const BoolV a, PxU32* f) +{ + *reinterpret_cast(f) = a; +} + +PX_FORCE_INLINE void U4StoreA(const VecU32V uv, PxU32* u) +{ + *reinterpret_cast(u) = uv; +} + +PX_FORCE_INLINE void I4StoreA(const VecI32V iv, PxI32* i) +{ + *reinterpret_cast(i) = iv; +} + +PX_FORCE_INLINE Vec4V V4LoadU(const PxF32* const f) +{ + return Vec4V(f[0], f[1], f[2], f[3]); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_PxVec3_WUndefined(const PxVec3& f) +{ + return Vec4V(f[0], f[1], f[2], 0.0f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool* const f) +{ + return BoolV(BOOL_TO_U32(f[0]), BOOL_TO_U32(f[1]), BOOL_TO_U32(f[2]), BOOL_TO_U32(f[3])); +} + +PX_FORCE_INLINE void FStore(const FloatV a, PxF32* PX_RESTRICT f) +{ + *f = a.x; +} + +PX_FORCE_INLINE void V3StoreA(const Vec3V a, PxVec3& f) +{ + f = PxVec3(a.x, a.y, a.z); +} + +PX_FORCE_INLINE void V3StoreU(const Vec3V a, PxVec3& f) +{ + f = PxVec3(a.x, a.y, a.z); +} + +PX_FORCE_INLINE void Store_From_BoolV(const BoolV b, PxU32* b2) +{ + *b2 = b.ux; +} + +////////////////////////// +// FLOATV +////////////////////////// + +PX_FORCE_INLINE FloatV FZero() +{ + return FLoad(0.0f); +} + +PX_FORCE_INLINE FloatV FOne() +{ + return FLoad(1.0f); +} + +PX_FORCE_INLINE FloatV FHalf() +{ + return FLoad(0.5f); +} + +PX_FORCE_INLINE FloatV FEps() +{ + return FLoad(PX_EPS_REAL); +} + +PX_FORCE_INLINE FloatV FEps6() +{ + return FLoad(1e-6f); +} + +PX_FORCE_INLINE FloatV FMax() +{ + return FLoad(PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV FNegMax() +{ + return FLoad(-PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV FNeg(const FloatV f) +{ + return FloatV(-f.x); +} + +PX_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b) +{ + return FloatV(a.x + b.x); +} + +PX_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b) +{ + return FloatV(a.x - b.x); +} + +PX_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b) +{ + return FloatV(a.x * b.x); +} + +PX_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(b.x != 0.0f); + return FloatV(a.x / b.x); +} + +PX_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(b.x != 0.0f); + return FloatV(a.x / b.x); +} + +PX_FORCE_INLINE FloatV FRecip(const FloatV a) +{ + VECMATHAOS_ASSERT(a.x != 0.0f); + return 1.0f / a.x; +} + +PX_FORCE_INLINE FloatV FRecipFast(const FloatV a) +{ + VECMATHAOS_ASSERT(a.x != 0.0f); + return 1.0f / a.x; +} + +PX_FORCE_INLINE FloatV FRsqrt(const FloatV a) +{ + VECMATHAOS_ASSERT(a.x != 0.0f); + return PxRecipSqrt(a.x); +} + +PX_FORCE_INLINE FloatV FSqrt(const FloatV a) +{ + return PxSqrt(a.x); +} + +PX_FORCE_INLINE FloatV FRsqrtFast(const FloatV a) +{ + VECMATHAOS_ASSERT(a.x != 0.0f); + return PxRecipSqrt(a.x); +} + +PX_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c) +{ + return FAdd(FMul(a, b), c); +} + +PX_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c) +{ + return FSub(c, FMul(a, b)); +} + +PX_FORCE_INLINE FloatV FAbs(const FloatV a) +{ + return FloatV(PxAbs(a.x)); +} + +PX_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b) +{ + return FloatV(c.ux ? a.x : b.x); +} + +PX_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b) +{ + return BLoad(a.x > b.x); +} + +PX_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b) +{ + return BLoad(a.x >= b.x); +} + +PX_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b) +{ + return BLoad(a.x == b.x); +} + +PX_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b) +{ + return (a.x > b.x ? FloatV(a.x) : FloatV(b.x)); +} + +PX_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b) +{ + return (a.x > b.x ? FloatV(b.x) : FloatV(a.x)); +} + +PX_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV) +{ + return FMax(FMin(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 FAllGrtr(const FloatV a, const FloatV b) +{ + return BOOL_TO_U32(a.x > b.x); +} + +PX_FORCE_INLINE PxU32 FAllGrtrOrEq(const FloatV a, const FloatV b) +{ + return BOOL_TO_U32(a.x >= b.x); +} +PX_FORCE_INLINE PxU32 FAllEq(const FloatV a, const FloatV b) +{ + return BOOL_TO_U32(a.x == b.x); +} + +PX_FORCE_INLINE FloatV FRound(const FloatV a) +{ + return floorf(a.x + 0.5f); +} + +PX_FORCE_INLINE FloatV FSin(const FloatV a) +{ + return sinf(a.x); +} + +PX_FORCE_INLINE FloatV FCos(const FloatV a) +{ + return cosf(a.x); +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max) +{ + return BOOL_TO_U32(a.x > max.x || a.x < min.x); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV min, const FloatV max) +{ + return BOOL_TO_U32(a.x >= min.x && a.x <= max.x); +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV bounds) +{ + return FOutOfBounds(a, FNeg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV bounds) +{ + return FInBounds(a, FNeg(bounds), bounds); +} + +///////////////////// +// VEC3V +///////////////////// + +PX_FORCE_INLINE Vec3V V3Splat(const FloatV f) +{ + return Vec3V(f.x, f.x, f.x); +} + +PX_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z) +{ + return Vec3V(x.x, y.x, z.x); +} + +PX_FORCE_INLINE Vec3V V3UnitX() +{ + return Vec3V(1.0f, 0.0f, 0.0f); +} + +PX_FORCE_INLINE Vec3V V3UnitY() +{ + return Vec3V(0.0f, 1.0f, 0.0f); +} + +PX_FORCE_INLINE Vec3V V3UnitZ() +{ + return Vec3V(0.0f, 0.0f, 1.0f); +} + +PX_FORCE_INLINE FloatV V3GetX(const Vec3V f) +{ + return FloatV(f.x); +} + +PX_FORCE_INLINE FloatV V3GetY(const Vec3V f) +{ + return FloatV(f.y); +} + +PX_FORCE_INLINE FloatV V3GetZ(const Vec3V f) +{ + return FloatV(f.z); +} + +PX_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f) +{ + return Vec3V(f.x, v.y, v.z); +} + +PX_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f) +{ + return Vec3V(v.x, f.x, v.z); +} + +PX_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f) +{ + return Vec3V(v.x, v.y, f.x); +} + +PX_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c) +{ + return Vec3V(a.x, b.x, c.x); +} + +PX_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c) +{ + return Vec3V(a.y, b.y, c.y); +} + +PX_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c) +{ + return Vec3V(a.z, b.z, c.z); +} + +PX_FORCE_INLINE Vec3V V3Zero() +{ + return V3Load(0.0f); +} + +PX_FORCE_INLINE Vec3V V3One() +{ + return V3Load(1.0f); +} + +PX_FORCE_INLINE Vec3V V3Eps() +{ + return V3Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec3V V3Neg(const Vec3V c) +{ + return Vec3V(-c.x, -c.y, -c.z); +} + +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x + b.x, a.y + b.y, a.z + b.z); +} + +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x - b.x, a.y - b.y, a.z - b.z); +} + +PX_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b) +{ + return Vec3V(a.x * b.x, a.y * b.x, a.z * b.x); +} + +PX_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x * b.x, a.y * b.y, a.z * b.z); +} + +PX_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b) +{ + const PxF32 bInv = 1.0f / b.x; + return Vec3V(a.x * bInv, a.y * bInv, a.z * bInv); +} + +PX_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x / b.x, a.y / b.y, a.z / b.z); +} + +PX_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b) +{ + const PxF32 bInv = 1.0f / b.x; + return Vec3V(a.x * bInv, a.y * bInv, a.z * bInv); +} + +PX_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x / b.x, a.y / b.y, a.z / b.z); +} + +PX_FORCE_INLINE Vec3V V3Recip(const Vec3V a) +{ + return Vec3V(1.0f / a.x, 1.0f / a.y, 1.0f / a.z); +} + +PX_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a) +{ + return Vec3V(1.0f / a.x, 1.0f / a.y, 1.0f / a.z); +} + +PX_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a) +{ + return Vec3V(PxRecipSqrt(a.x), PxRecipSqrt(a.y), PxRecipSqrt(a.z)); +} + +PX_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a) +{ + return Vec3V(PxRecipSqrt(a.x), PxRecipSqrt(a.y), PxRecipSqrt(a.z)); +} + +PX_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c) +{ + return V3Add(V3Scale(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c) +{ + return V3Sub(c, V3Scale(a, b)); +} + +PX_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c) +{ + return V3Add(V3Mul(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c) +{ + return V3Sub(c, V3Mul(a, b)); +} + +PX_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b) +{ + return FloatV(a.x * b.x + a.y * b.y + a.z * b.z); +} + +PX_FORCE_INLINE VecCrossV V3PrepareCross(const Vec3VArg normal) +{ + return normal; +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); +} + +PX_FORCE_INLINE FloatV V3Length(const Vec3V a) +{ + return FloatV(PxSqrt(a.x * a.x + a.y * a.y + a.z * a.z)); +} + +PX_FORCE_INLINE FloatV V3LengthSq(const Vec3V a) +{ + return FloatV(a.x * a.x + a.y * a.y + a.z * a.z); +} + +PX_FORCE_INLINE Vec3V V3Normalize(const Vec3V a) +{ + VECMATHAOS_ASSERT(a.x != 0 || a.y != 0 || a.z != 0); + const PxF32 lengthInv = 1.0f / PxSqrt(a.x * a.x + a.y * a.y + a.z * a.z); + return Vec3V(a.x * lengthInv, a.y * lengthInv, a.z * lengthInv); +} + +PX_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a, const Vec3V unsafeReturnValue) +{ + const PxF32 length = PxSqrt(a.x * a.x + a.y * a.y + a.z * a.z); + if(PX_EPS_REAL >= length) + { + return unsafeReturnValue; + } + else + { + const PxF32 lengthInv = 1.0f / length; + return Vec3V(a.x * lengthInv, a.y * lengthInv, a.z * lengthInv); + } +} + +PX_FORCE_INLINE Vec3V V3NormalizeFast(const Vec3V a) +{ + VECMATHAOS_ASSERT(a.x != 0 || a.y != 0 || a.z != 0); + const PxF32 lengthInv = 1.0f / PxSqrt(a.x * a.x + a.y * a.y + a.z * a.z); + return Vec3V(a.x * lengthInv, a.y * lengthInv, a.z * lengthInv); +} + +PX_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b) +{ + return Vec3V(c.ux ? a.x : b.x, c.uy ? a.y : b.y, c.uz ? a.z : b.z); +} + +PX_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b) +{ + return BoolV(BOOL_TO_U32(a.x > b.x), BOOL_TO_U32(a.y > b.y), BOOL_TO_U32(a.z > b.z), FALSE_TO_U32); +} + +PX_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b) +{ + return BoolV(BOOL_TO_U32(a.x >= b.x), BOOL_TO_U32(a.y >= b.y), BOOL_TO_U32(a.z >= b.z), TRUE_TO_U32); +} + +PX_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b) +{ + return BoolV(BOOL_TO_U32(a.x == b.x), BOOL_TO_U32(a.y == b.y), BOOL_TO_U32(a.z == b.z), TRUE_TO_U32); +} + +PX_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x > b.x ? a.x : b.x, a.y > b.y ? a.y : b.y, a.z > b.z ? a.z : b.z); +} + +PX_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x < b.x ? a.x : b.x, a.y < b.y ? a.y : b.y, a.z < b.z ? a.z : b.z); +} + +PX_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a) +{ + const PxF32 t0 = (a.x >= a.y) ? a.x : a.y; + return t0 >= a.z ? t0 : a.z; +} + +PX_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a) +{ + const PxF32 t0 = (a.x <= a.y) ? a.x : a.y; + return t0 <= a.z ? t0 : a.z; +} + +// return (a >= 0.0f) ? 1.0f : -1.0f; +PX_FORCE_INLINE Vec3V V3Sign(const Vec3V a) +{ + return Vec3V((a.x >= 0.f ? 1.f : -1.f), (a.y >= 0.f ? 1.f : -1.f), (a.z >= 0.f ? 1.f : -1.f)); +} + +PX_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV) +{ + return V3Max(V3Min(a, maxV), minV); +} + +PX_FORCE_INLINE Vec3V V3Abs(const Vec3V a) +{ + return V3Max(a, V3Neg(a)); +} + +PX_FORCE_INLINE PxU32 V3AllGrtr(const Vec3V a, const Vec3V b) +{ + return BOOL_TO_U32((a.x > b.x) & (a.y > b.y) & (a.z > b.z)); +} + +PX_FORCE_INLINE PxU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b) +{ + return BOOL_TO_U32((a.x >= b.x) & (a.y >= b.y) & (a.z >= b.z)); +} + +PX_FORCE_INLINE PxU32 V3AllEq(const Vec3V a, const Vec3V b) +{ + return BOOL_TO_U32((a.x == b.x) & (a.y == b.y) & (a.z == b.z)); +} + +PX_FORCE_INLINE Vec3V V3Round(const Vec3V a) +{ + return Vec3V(floorf(a.x + 0.5f), floorf(a.y + 0.5f), floorf(a.z + 0.5f)); +} + +PX_FORCE_INLINE Vec3V V3Sin(const Vec3V a) +{ + return Vec3V(sinf(a.x), sinf(a.y), sinf(a.z)); +} + +PX_FORCE_INLINE Vec3V V3Cos(const Vec3V a) +{ + return Vec3V(cosf(a.x), cosf(a.y), cosf(a.z)); +} + +PX_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a) +{ + return Vec3V(a.y, a.z, a.z); +} + +PX_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a) +{ + return Vec3V(a.x, a.y, a.x); +} + +PX_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a) +{ + return Vec3V(a.y, a.z, a.x); +} + +PX_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a) +{ + return Vec3V(a.z, a.x, a.y); +} + +PX_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a) +{ + return Vec3V(a.z, a.z, a.y); +} + +PX_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a) +{ + return Vec3V(a.y, a.x, a.x); +} + +PX_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1) +{ + return Vec3V(0.0f, v1.z, v0.y); +} + +PX_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1) +{ + return Vec3V(v0.z, 0.0f, v1.x); +} + +PX_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1) +{ + return Vec3V(v1.y, v0.x, 0.0f); +} + +PX_FORCE_INLINE FloatV V3SumElems(const Vec3V a) +{ + return FloatV(a.x + a.y + a.z); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + return BOOL_TO_U32(a.x > max.x || a.y > max.y || a.z > max.z || a.x < min.x || a.y < min.y || a.z < min.z); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + return BOOL_TO_U32(a.x <= max.x && a.y <= max.y && a.z <= max.z && a.x >= min.x && a.y >= min.y && a.z >= min.z); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds) +{ + return V3OutOfBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V bounds) +{ + return V3InBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2) +{ + const PxF32 t01 = col0.y, t02 = col0.z, t12 = col1.z; + col0.y = col1.x; + col0.z = col2.x; + col1.z = col2.y; + col1.x = t01; + col2.x = t02; + col2.y = t12; +} + +///////////////////////// +// VEC4V +///////////////////////// + +PX_FORCE_INLINE Vec4V V4Splat(const FloatV f) +{ + return Vec4V(f.x, f.x, f.x, f.x); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatV* const floatVArray) +{ + return Vec4V(floatVArray[0].x, floatVArray[1].x, floatVArray[2].x, floatVArray[3].x); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w) +{ + return Vec4V(x.x, y.x, z.x, w.x); +} + +PX_FORCE_INLINE Vec4V V4MergeW(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + return Vec4V(x.w, y.w, z.w, w.w); +} + +PX_FORCE_INLINE Vec4V V4MergeZ(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + return Vec4V(x.z, y.z, z.z, w.z); +} + +PX_FORCE_INLINE Vec4V V4MergeY(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + return Vec4V(x.y, y.y, z.y, w.y); +} + +PX_FORCE_INLINE Vec4V V4MergeX(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + return Vec4V(x.x, y.x, z.x, w.x); +} + +PX_FORCE_INLINE Vec4V V4UnpackXY(const Vec4VArg a, const Vec4VArg b) +{ + return Vec4V(a.x, b.x, a.y, b.y); +} + +PX_FORCE_INLINE Vec4V V4UnpackZW(const Vec4VArg a, const Vec4VArg b) +{ + return Vec4V(a.z, b.z, a.w, b.w); +} + +PX_FORCE_INLINE Vec4V V4UnitX() +{ + return Vec4V(1.0f, 0.0f, 0.0f, 0.0f); +} + +PX_FORCE_INLINE Vec4V V4UnitY() +{ + return Vec4V(0.0f, 1.0f, 0.0f, 0.0f); +} + +PX_FORCE_INLINE Vec4V V4UnitZ() +{ + return Vec4V(0.0f, 0.0f, 1.0f, 0.0f); +} + +PX_FORCE_INLINE Vec4V V4UnitW() +{ + return Vec4V(0.0f, 0.0f, 0.0f, 1.0f); +} + +PX_FORCE_INLINE FloatV V4GetX(const Vec4V f) +{ + return FloatV(f.x); +} + +PX_FORCE_INLINE FloatV V4GetY(const Vec4V f) +{ + return FloatV(f.y); +} + +PX_FORCE_INLINE FloatV V4GetZ(const Vec4V f) +{ + return FloatV(f.z); +} + +PX_FORCE_INLINE FloatV V4GetW(const Vec4V f) +{ + return FloatV(f.w); +} + +PX_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f) +{ + return Vec4V(f.x, v.y, v.z, v.w); +} + +PX_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f) +{ + return Vec4V(v.x, f.x, v.z, v.w); +} + +PX_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f) +{ + return Vec4V(v.x, v.y, f.x, v.w); +} + +PX_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f) +{ + return Vec4V(v.x, v.y, v.z, f.x); +} + +PX_FORCE_INLINE Vec4V V4SetW(const Vec3V v, const FloatV f) +{ + return Vec4V(v.x, v.y, v.z, f.x); +} + +PX_FORCE_INLINE Vec4V V4ClearW(const Vec4V v) +{ + return Vec4V(v.x, v.y, v.z, 0.0f); +} + +PX_FORCE_INLINE Vec4V V4PermYXWZ(const Vec4V v) +{ + return Vec4V(v.y, v.x, v.w, v.z); +} + +PX_FORCE_INLINE Vec4V V4PermXZXZ(const Vec4V v) +{ + return Vec4V(v.x, v.z, v.x, v.z); +} + +PX_FORCE_INLINE Vec4V V4PermYWYW(const Vec4V v) +{ + return Vec4V(v.y, v.w, v.y, v.w); +} + +PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V v) +{ + return Vec4V(v.y, v.z, v.x, v.w); +} + +PX_FORCE_INLINE Vec4V V4PermZWXY(const Vec4V v) +{ + return Vec4V(v.z, v.w, v.x, v.y); +} + +template +PX_FORCE_INLINE Vec4V V4Perm(const Vec4V v) +{ + const PxF32 f[4] = { v.x, v.y, v.z, v.w }; + return Vec4V(f[_x], f[_y], f[_z], f[_w]); +} + +PX_FORCE_INLINE Vec4V V4Zero() +{ + return V4Load(0.0f); +} + +PX_FORCE_INLINE Vec4V V4One() +{ + return V4Load(1.0f); +} + +PX_FORCE_INLINE Vec4V V4Eps() +{ + return V4Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec4V V4Neg(const Vec4V c) +{ + return Vec4V(-c.x, -c.y, -c.z, -c.w); +} + +PX_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); +} + +PX_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); +} + +PX_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b) +{ + return Vec4V(a.x * b.x, a.y * b.x, a.z * b.x, a.w * b.x); +} + +PX_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); +} + +PX_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b) +{ + const PxF32 bInv = 1.0f / b.x; + return Vec4V(a.x * bInv, a.y * bInv, a.z * bInv, a.w * bInv); +} + +PX_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b) +{ + VECMATHAOS_ASSERT(b.x != 0 && b.y != 0 && b.z != 0 && b.w != 0); + return Vec4V(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); +} + +PX_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b) +{ + const PxF32 bInv = 1.0f / b.x; + return Vec4V(a.x * bInv, a.y * bInv, a.z * bInv, a.w * bInv); +} + +PX_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); +} + +PX_FORCE_INLINE Vec4V V4Recip(const Vec4V a) +{ + return Vec4V(1.0f / a.x, 1.0f / a.y, 1.0f / a.z, 1.0f / a.w); +} + +PX_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a) +{ + return Vec4V(1.0f / a.x, 1.0f / a.y, 1.0f / a.z, 1.0f / a.w); +} + +PX_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a) +{ + return Vec4V(PxRecipSqrt(a.x), PxRecipSqrt(a.y), PxRecipSqrt(a.z), PxRecipSqrt(a.w)); +} + +PX_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a) +{ + return Vec4V(PxRecipSqrt(a.x), PxRecipSqrt(a.y), PxRecipSqrt(a.z), PxRecipSqrt(a.w)); +} + +PX_FORCE_INLINE Vec4V V4Sqrt(const Vec4V a) +{ + return Vec4V(PxSqrt(a.x), PxSqrt(a.y), PxSqrt(a.z), PxSqrt(a.w)); +} + +PX_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c) +{ + return V4Add(V4Scale(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c) +{ + return V4Sub(c, V4Scale(a, b)); +} + +PX_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Add(V4Mul(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Sub(c, V4Mul(a, b)); +} + +PX_FORCE_INLINE FloatV V4SumElements(const Vec4V a) +{ + return FloatV(a.x + a.y + a.z + a.w); +} + +PX_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b) +{ + return FloatV(a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w); +} + +PX_FORCE_INLINE FloatV V4Dot3(const Vec4V a, const Vec4V b) +{ + return FloatV(a.x * b.x + a.y * b.y + a.z * b.z); +} + +PX_FORCE_INLINE Vec4V V4Cross(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x, 0.0f); +} + +PX_FORCE_INLINE FloatV V4Length(const Vec4V a) +{ + return FloatV(PxSqrt(a.x * a.x + a.y * a.y + a.z * a.z + a.w * a.w)); +} + +PX_FORCE_INLINE FloatV V4LengthSq(const Vec4V a) +{ + return V4Dot(a, a); +} + +PX_FORCE_INLINE Vec4V V4Normalize(const Vec4V a) +{ + VECMATHAOS_ASSERT(0 != a.x || 0 != a.y || 0 != a.z || 0 != a.w); + const FloatV length = FloatV(V4Length(a)); + return V4ScaleInv(a, length); +} + +PX_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a, const Vec4V unsafeReturnValue) +{ + const FloatV length = FloatV(V4Length(a)); + if(PX_EPS_REAL >= length.x) + { + return unsafeReturnValue; + } + else + { + return V4ScaleInv(a, length); + } +} +PX_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a) +{ + VECMATHAOS_ASSERT(0 != a.x || 0 != a.y || 0 != a.z || 0 != a.w); + const FloatV length = FloatV(V4Length(a)); + return V4ScaleInv(a, length); +} + +PX_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b) +{ + return Vec4V(c.ux ? a.x : b.x, c.uy ? a.y : b.y, c.uz ? a.z : b.z, c.uw ? a.w : b.w); +} + +PX_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b) +{ + return BoolV(BOOL_TO_U32(a.x > b.x), BOOL_TO_U32(a.y > b.y), BOOL_TO_U32(a.z > b.z), BOOL_TO_U32(a.w > b.w)); +} + +PX_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return BoolV(BOOL_TO_U32(a.x >= b.x), BOOL_TO_U32(a.y >= b.y), BOOL_TO_U32(a.z >= b.z), BOOL_TO_U32(a.w >= b.w)); +} + +PX_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b) +{ + return BoolV(BOOL_TO_U32(a.x == b.x), BOOL_TO_U32(a.y == b.y), BOOL_TO_U32(a.z == b.z), BOOL_TO_U32(a.w == b.w)); +} + +PX_FORCE_INLINE Vec4V V4Max(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x > b.x ? a.x : b.x, a.y > b.y ? a.y : b.y, a.z > b.z ? a.z : b.z, a.w > b.w ? a.w : b.w); +} + +PX_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x < b.x ? a.x : b.x, a.y < b.y ? a.y : b.y, a.z < b.z ? a.z : b.z, a.w < b.w ? a.w : b.w); +} + +PX_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a) +{ + const PxF32 t0 = (a.x >= a.y) ? a.x : a.y; + const PxF32 t1 = (a.z >= a.w) ? a.x : a.w; + return t0 >= t1 ? t0 : t1; +} + +PX_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a) +{ + const PxF32 t0 = (a.x <= a.y) ? a.x : a.y; + const PxF32 t1 = (a.z <= a.w) ? a.x : a.w; + return t0 <= t1 ? t0 : t1; +} + +PX_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV) +{ + return V4Max(V4Min(a, maxV), minV); +} + +PX_FORCE_INLINE Vec4V V4Round(const Vec4V a) +{ + return Vec4V(floorf(a.x + 0.5f), floorf(a.y + 0.5f), floorf(a.z + 0.5f), floorf(a.w + 0.5f)); +} + +PX_FORCE_INLINE Vec4V V4Sin(const Vec4V a) +{ + return Vec4V(sinf(a.x), sinf(a.y), sinf(a.z), sinf(a.w)); +} + +PX_FORCE_INLINE Vec4V V4Cos(const Vec4V a) +{ + return Vec4V(cosf(a.x), cosf(a.y), cosf(a.z), cosf(a.w)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtr(const Vec4V a, const Vec4V b) +{ + return BOOL_TO_U32((a.x > b.x) & (a.y > b.y) & (a.z > b.z) & (a.w > b.w)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return BOOL_TO_U32((a.x >= b.x) & (a.y >= b.y) & (a.z >= b.z) & (a.w >= b.w)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq3(const Vec4V a, const Vec4V b) +{ + return BOOL_TO_U32((a.x >= b.x) & (a.y >= b.y) & (a.z >= b.z)); +} + +PX_FORCE_INLINE PxU32 V4AllEq(const Vec4V a, const Vec4V b) +{ + return BOOL_TO_U32((a.x == b.x) & (a.y == b.y) & (a.z == b.z) & (a.w == b.w)); +} + +PX_FORCE_INLINE PxU32 V4AnyGrtr3(const Vec4V a, const Vec4V b) +{ + return BOOL_TO_U32((a.x > b.x) | (a.y > b.y) | (a.z > b.z)); +} + +PX_FORCE_INLINE void V4Transpose(Vec4V& col0, Vec4V& col1, Vec4V& col2, Vec4V& col3) +{ + const PxF32 t01 = col0.y, t02 = col0.z, t03 = col0.w; + const PxF32 t12 = col1.z, t13 = col1.w; + const PxF32 t23 = col2.w; + col0.y = col1.x; + col0.z = col2.x; + col0.w = col3.x; + col1.z = col2.y; + col1.w = col3.y; + col2.w = col3.z; + col1.x = t01; + col2.x = t02; + col3.x = t03; + col2.y = t12; + col3.y = t13; + col3.z = t23; +} + +PX_FORCE_INLINE BoolV BFFFF() +{ + return BoolV(FALSE_TO_U32, FALSE_TO_U32, FALSE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BFFFT() +{ + return BoolV(FALSE_TO_U32, FALSE_TO_U32, FALSE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BFFTF() +{ + return BoolV(FALSE_TO_U32, FALSE_TO_U32, TRUE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BFFTT() +{ + return BoolV(FALSE_TO_U32, FALSE_TO_U32, TRUE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BFTFF() +{ + return BoolV(FALSE_TO_U32, TRUE_TO_U32, FALSE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BFTFT() +{ + return BoolV(FALSE_TO_U32, TRUE_TO_U32, FALSE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BFTTF() +{ + return BoolV(FALSE_TO_U32, TRUE_TO_U32, TRUE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BFTTT() +{ + return BoolV(FALSE_TO_U32, TRUE_TO_U32, TRUE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BTFFF() +{ + return BoolV(TRUE_TO_U32, FALSE_TO_U32, FALSE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BTFFT() +{ + return BoolV(TRUE_TO_U32, FALSE_TO_U32, FALSE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BTFTF() +{ + return BoolV(TRUE_TO_U32, FALSE_TO_U32, TRUE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BTFTT() +{ + return BoolV(TRUE_TO_U32, FALSE_TO_U32, TRUE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BTTFF() +{ + return BoolV(TRUE_TO_U32, TRUE_TO_U32, FALSE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BTTFT() +{ + return BoolV(TRUE_TO_U32, TRUE_TO_U32, FALSE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BTTTF() +{ + return BoolV(TRUE_TO_U32, TRUE_TO_U32, TRUE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BTTTT() +{ + return BoolV(TRUE_TO_U32, TRUE_TO_U32, TRUE_TO_U32, TRUE_TO_U32); +} + +PX_FORCE_INLINE BoolV BXMask() +{ + return BTFFF(); +} +PX_FORCE_INLINE BoolV BYMask() +{ + return BFTFF(); +} +PX_FORCE_INLINE BoolV BZMask() +{ + return BFFTF(); +} +PX_FORCE_INLINE BoolV BWMask() +{ + return BFFFT(); +} + +PX_FORCE_INLINE BoolV BGetX(const BoolV a) +{ + return BoolV(a.ux, a.ux, a.ux, a.ux); +} + +PX_FORCE_INLINE BoolV BGetY(const BoolV a) +{ + return BoolV(a.uy, a.uy, a.uy, a.uy); +} + +PX_FORCE_INLINE BoolV BGetZ(const BoolV a) +{ + return BoolV(a.uz, a.uz, a.uz, a.uz); +} + +PX_FORCE_INLINE BoolV BGetW(const BoolV a) +{ + return BoolV(a.uw, a.uw, a.uw, a.uw); +} + +PX_FORCE_INLINE BoolV BSetX(const BoolV v, const BoolV f) +{ + return BoolV(f.ux, v.uy, v.uz, v.uw); +} + +PX_FORCE_INLINE BoolV BSetY(const BoolV v, const BoolV f) +{ + return BoolV(v.ux, f.uy, v.uz, v.uw); +} + +PX_FORCE_INLINE BoolV BSetZ(const BoolV v, const BoolV f) +{ + return BoolV(v.ux, v.uy, f.uz, v.uw); +} + +PX_FORCE_INLINE BoolV BSetW(const BoolV v, const BoolV f) +{ + return BoolV(v.ux, v.uy, v.uz, f.uw); +} + +template +BoolV BSplatElement(BoolV a) +{ + PxU32* b = (PxU32*)&a; + return BoolV(b[index], b[index], b[index], b[index]); +} + +PX_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b) +{ + return BoolV(BOOL_TO_U32(a.ux && b.ux), BOOL_TO_U32(a.uy && b.uy), BOOL_TO_U32(a.uz && b.uz), BOOL_TO_U32(a.uw && b.uw)); +} + +PX_FORCE_INLINE BoolV BAndNot(const BoolV a, const BoolV b) +{ + return BoolV(a.ux & ~b.ux, a.uy & ~b.uy, a.uz & ~b.uz, a.uw & ~b.uw); +} + +PX_FORCE_INLINE BoolV BNot(const BoolV a) +{ + return BoolV(~a.ux, ~a.uy, ~a.uz, ~a.uw); +} + +PX_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b) +{ + return BoolV(BOOL_TO_U32(a.ux || b.ux), BOOL_TO_U32(a.uy || b.uy), BOOL_TO_U32(a.uz || b.uz), BOOL_TO_U32(a.uw || b.uw)); +} + +PX_FORCE_INLINE PxU32 BAllEq(const BoolV a, const BoolV b) +{ + return (a.ux == b.ux && a.uy == b.uy && a.uz == b.uz && a.uw == b.uw ? 1 : 0); +} + +PX_FORCE_INLINE PxU32 BAllEqTTTT(const BoolV a) +{ + return BAllEq(a, BTTTT()); +} + +PX_FORCE_INLINE PxU32 BAllEqFFFF(const BoolV a) +{ + return BAllEq(a, BFFFF()); +} + +PX_FORCE_INLINE BoolV BAllTrue4(const BoolV a) +{ + return (a.ux & a.uy & a.uz & a.uw) ? BTTTT() : BFFFF(); +} + +PX_FORCE_INLINE BoolV BAnyTrue4(const BoolV a) +{ + return (a.ux | a.uy | a.uz | a.uw) ? BTTTT() : BFFFF(); +} + +PX_FORCE_INLINE BoolV BAllTrue3(const BoolV a) +{ + return (a.ux & a.uy & a.uz) ? BTTTT() : BFFFF(); +} + +PX_FORCE_INLINE BoolV BAnyTrue3(const BoolV a) +{ + return (a.ux | a.uy | a.uz) ? BTTTT() : BFFFF(); +} + +PX_FORCE_INLINE PxU32 BGetBitMask(const BoolV a) +{ + return (a.ux & 1) | (a.uy & 2) | (a.uz & 4) | (a.uw & 8); +} + +////////////////////////////////// +// MAT33V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M33MulV3(const Mat33V& a, const Vec3V b) +{ + return Vec3V(a.col0.x * b.x + a.col1.x * b.y + a.col2.x * b.z, a.col0.y * b.x + a.col1.y * b.y + a.col2.y * b.z, + a.col0.z * b.x + a.col1.z * b.y + a.col2.z * b.z); +} + +PX_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V& a, const Vec3V b) +{ + return Vec3V(a.col0.x * b.x + a.col0.y * b.y + a.col0.z * b.z, a.col1.x * b.x + a.col1.y * b.y + a.col1.z * b.z, + a.col2.x * b.x + a.col2.y * b.y + a.col2.z * b.z); +} + +PX_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V& A, const Vec3V b, const Vec3V c) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + Vec3V result = V3ScaleAdd(A.col0, x, c); + result = V3ScaleAdd(A.col1, y, result); + return V3ScaleAdd(A.col2, z, result); +} + +PX_FORCE_INLINE Mat33V M33MulM33(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(M33MulV3(a, b.col0), M33MulV3(a, b.col1), M33MulV3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Add(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Scale(const Mat33V& a, const FloatV& b) +{ + return Mat33V(V3Scale(a.col0, b), V3Scale(a.col1, b), V3Scale(a.col2, b)); +} + +PX_FORCE_INLINE Mat33V M33Sub(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Sub(a.col0, b.col0), V3Sub(a.col1, b.col1), V3Sub(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Neg(const Mat33V& a) +{ + return Mat33V(V3Neg(a.col0), V3Neg(a.col1), V3Neg(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Abs(const Mat33V& a) +{ + return Mat33V(V3Abs(a.col0), V3Abs(a.col1), V3Abs(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg d) +{ + const Vec3V x = V3Mul(V3UnitX(), d); + const Vec3V y = V3Mul(V3UnitY(), d); + const Vec3V z = V3Mul(V3UnitZ(), d); + return Mat33V(x, y, z); +} + +PX_FORCE_INLINE Mat33V M33Inverse(const Mat33V& a) +{ + const PxF32 det = a.col0.x * (a.col1.y * a.col2.z - a.col1.z * a.col2.y) - + a.col1.x * (a.col0.y * a.col2.z - a.col2.y * a.col0.z) + + a.col2.x * (a.col0.y * a.col1.z - a.col1.y * a.col0.z); + + const PxF32 invDet = 1.0f / det; + + Mat33V ret; + ret.col0.x = invDet * (a.col1.y * a.col2.z - a.col2.y * a.col1.z); + ret.col0.y = invDet * (a.col2.y * a.col0.z - a.col0.y * a.col2.z); + ret.col0.z = invDet * (a.col0.y * a.col1.z - a.col1.y * a.col0.z); + + ret.col1.x = invDet * (a.col2.x * a.col1.z - a.col1.x * a.col2.z); + ret.col1.y = invDet * (a.col0.x * a.col2.z - a.col2.x * a.col0.z); + ret.col1.z = invDet * (a.col1.x * a.col0.z - a.col0.x * a.col1.z); + + ret.col2.x = invDet * (a.col1.x * a.col2.y - a.col2.x * a.col1.y); + ret.col2.y = invDet * (a.col2.x * a.col0.y - a.col0.x * a.col2.y); + ret.col2.z = invDet * (a.col0.x * a.col1.y - a.col1.x * a.col0.y); + + return ret; +} + +PX_FORCE_INLINE Mat33V Mat33V_From_PxMat33(const PxMat33& m) +{ + return Mat33V(V3LoadU(m.column0), V3LoadU(m.column1), V3LoadU(m.column2)); +} + +PX_FORCE_INLINE void PxMat33_From_Mat33V(const Mat33V& m, PxMat33& out) +{ + PX_ASSERT((size_t(&out) & 15) == 0); + V3StoreU(m.col0, out.column0); + V3StoreU(m.col1, out.column1); + V3StoreU(m.col2, out.column2); +} + +PX_FORCE_INLINE Mat33V M33Trnsps(const Mat33V& a) +{ + return Mat33V(Vec3V(a.col0.x, a.col1.x, a.col2.x), Vec3V(a.col0.y, a.col1.y, a.col2.y), + Vec3V(a.col0.z, a.col1.z, a.col2.z)); +} + +PX_FORCE_INLINE Mat33V M33Identity() +{ + return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ()); +} + +////////////////////////////////// +// MAT34V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M34MulV3(const Mat34V& a, const Vec3V b) +{ + return Vec3V(a.col0.x * b.x + a.col1.x * b.y + a.col2.x * b.z + a.col3.x, + a.col0.y * b.x + a.col1.y * b.y + a.col2.y * b.z + a.col3.y, + a.col0.z * b.x + a.col1.z * b.y + a.col2.z * b.z + a.col3.z); +} + +PX_FORCE_INLINE Vec3V M34Mul33V3(const Mat34V& a, const Vec3V b) +{ + return Vec3V(a.col0.x * b.x + a.col1.x * b.y + a.col2.x * b.z, a.col0.y * b.x + a.col1.y * b.y + a.col2.y * b.z, + a.col0.z * b.x + a.col1.z * b.y + a.col2.z * b.z); +} + +PX_FORCE_INLINE Vec3V M34TrnspsMul33V3(const Mat34V& a, const Vec3V b) +{ + return Vec3V(a.col0.x * b.x + a.col0.y * b.y + a.col0.z * b.z, a.col1.x * b.x + a.col1.y * b.y + a.col1.z * b.z, + a.col2.x * b.x + a.col2.y * b.y + a.col2.z * b.z); +} + +PX_FORCE_INLINE Mat34V M34MulM34(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2), M34MulV3(a, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34MulM33(const Mat34V& a, const Mat33V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M34Mul33V3(const Mat34V& a, const Mat33V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M34Mul33MM34(const Mat34V& a, const Mat34V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat34V M34Add(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2), V3Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34Trnsps33(const Mat34V& a) +{ + return Mat33V(Vec3V(a.col0.x, a.col1.x, a.col2.x), Vec3V(a.col0.y, a.col1.y, a.col2.y), + Vec3V(a.col0.z, a.col1.z, a.col2.z)); +} + +////////////////////////////////// +// MAT44V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V M44MulV4(const Mat44V& a, const Vec4V b) +{ + return Vec4V(a.col0.x * b.x + a.col1.x * b.y + a.col2.x * b.z + a.col3.x * b.w, + a.col0.y * b.x + a.col1.y * b.y + a.col2.y * b.z + a.col3.y * b.w, + a.col0.z * b.x + a.col1.z * b.y + a.col2.z * b.z + a.col3.z * b.w, + a.col0.w * b.x + a.col1.w * b.y + a.col2.w * b.z + a.col3.w * b.w); +} + +PX_FORCE_INLINE Vec4V M44TrnspsMulV4(const Mat44V& a, const Vec4V b) +{ + return Vec4V(a.col0.x * b.x + a.col0.y * b.y + a.col0.z * b.z + a.col0.w * b.w, + a.col1.x * b.x + a.col1.y * b.y + a.col1.z * b.z + a.col1.w * b.w, + a.col2.x * b.x + a.col2.y * b.y + a.col2.z * b.z + a.col2.w * b.w, + a.col3.x * b.x + a.col3.y * b.y + a.col3.z * b.z + a.col3.w * b.w); +} + +PX_FORCE_INLINE Mat44V M44MulM44(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(M44MulV4(a, b.col0), M44MulV4(a, b.col1), M44MulV4(a, b.col2), M44MulV4(a, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Add(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(V4Add(a.col0, b.col0), V4Add(a.col1, b.col1), V4Add(a.col2, b.col2), V4Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Inverse(const Mat44V& a) +{ + PxF32 tmp[12]; + PxF32 dst[16]; + PxF32 det; + + const PxF32 src[16] = { a.col0.x, a.col0.y, a.col0.z, a.col0.w, a.col1.x, a.col1.y, a.col1.z, a.col1.w, + a.col2.x, a.col2.y, a.col2.z, a.col2.w, a.col3.x, a.col3.y, a.col3.z, a.col3.w }; + + tmp[0] = src[10] * src[15]; + tmp[1] = src[11] * src[14]; + tmp[2] = src[9] * src[15]; + tmp[3] = src[11] * src[13]; + tmp[4] = src[9] * src[14]; + tmp[5] = src[10] * src[13]; + tmp[6] = src[8] * src[15]; + tmp[7] = src[11] * src[12]; + tmp[8] = src[8] * src[14]; + tmp[9] = src[10] * src[12]; + tmp[10] = src[8] * src[13]; + tmp[11] = src[9] * src[12]; + + dst[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7]; + dst[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7]; + dst[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7]; + dst[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7]; + dst[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7]; + dst[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7]; + dst[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6]; + dst[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6]; + dst[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3]; + dst[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3]; + dst[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3]; + dst[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3]; + dst[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3]; + dst[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3]; + dst[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2]; + dst[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2]; + + tmp[0] = src[2] * src[7]; + tmp[1] = src[3] * src[6]; + tmp[2] = src[1] * src[7]; + tmp[3] = src[3] * src[5]; + tmp[4] = src[1] * src[6]; + tmp[5] = src[2] * src[5]; + tmp[6] = src[0] * src[7]; + tmp[7] = src[3] * src[4]; + tmp[8] = src[0] * src[6]; + tmp[9] = src[2] * src[4]; + tmp[10] = src[0] * src[5]; + tmp[11] = src[1] * src[4]; + + dst[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15]; + dst[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15]; + dst[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15]; + dst[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15]; + dst[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15]; + dst[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15]; + dst[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14]; + dst[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14]; + dst[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9]; + dst[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10]; + dst[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10]; + dst[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8]; + dst[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8]; + dst[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9]; + dst[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9]; + dst[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8]; + + det = src[0] * dst[0] + src[1] * dst[1] + src[2] * dst[2] + src[3] * dst[3]; + + det = 1.0f / det; + for(PxU32 j = 0; j < 16; j++) + { + dst[j] *= det; + } + + return Mat44V(Vec4V(dst[0], dst[4], dst[8], dst[12]), Vec4V(dst[1], dst[5], dst[9], dst[13]), + Vec4V(dst[2], dst[6], dst[10], dst[14]), Vec4V(dst[3], dst[7], dst[11], dst[15])); +} + +PX_FORCE_INLINE Mat44V M44Trnsps(const Mat44V& a) +{ + return Mat44V(Vec4V(a.col0.x, a.col1.x, a.col2.x, a.col3.x), Vec4V(a.col0.y, a.col1.y, a.col2.y, a.col3.y), + Vec4V(a.col0.z, a.col1.z, a.col2.z, a.col3.z), Vec4V(a.col0.w, a.col1.w, a.col2.w, a.col3.w)); +} + +PX_FORCE_INLINE Vec4V V4LoadXYZW(const PxF32& x, const PxF32& y, const PxF32& z, const PxF32& w) +{ + return Vec4V(x, y, z, w); +} + +/* +PX_FORCE_INLINE VecU16V V4U32PK(VecU32V a, VecU32V b) +{ + return VecU16V( + PxU16(PxClamp((a).u32[0], 0, 0xFFFF)), + PxU16(PxClamp((a).u32[1], 0, 0xFFFF)), + PxU16(PxClamp((a).u32[2], 0, 0xFFFF)), + PxU16(PxClamp((a).u32[3], 0, 0xFFFF)), + PxU16(PxClamp((b).u32[0], 0, 0xFFFF)), + PxU16(PxClamp((b).u32[1], 0, 0xFFFF)), + PxU16(PxClamp((b).u32[2], 0, 0xFFFF)), + PxU16(PxClamp((b).u32[3], 0, 0xFFFF))); +} +*/ + +PX_FORCE_INLINE VecU32V V4U32Sel(const BoolV c, const VecU32V a, const VecU32V b) +{ + return VecU32V(c.ux ? a.u32[0] : b.u32[0], c.uy ? a.u32[1] : b.u32[1], c.uz ? a.u32[2] : b.u32[2], + c.uw ? a.u32[3] : b.u32[3]); +} + +PX_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b) +{ + return VecU32V((a).u32[0] | (b).u32[0], (a).u32[1] | (b).u32[1], (a).u32[2] | (b).u32[2], (a).u32[3] | (b).u32[3]); +} + +PX_FORCE_INLINE VecU32V V4U32xor(VecU32V a, VecU32V b) +{ + return VecU32V((a).u32[0] ^ (b).u32[0], (a).u32[1] ^ (b).u32[1], (a).u32[2] ^ (b).u32[2], (a).u32[3] ^ (b).u32[3]); +} + +PX_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b) +{ + return VecU32V((a).u32[0] & (b).u32[0], (a).u32[1] & (b).u32[1], (a).u32[2] & (b).u32[2], (a).u32[3] & (b).u32[3]); +} + +PX_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b) +{ + return VecU32V((a).u32[0] & ~(b).u32[0], (a).u32[1] & ~(b).u32[1], (a).u32[2] & ~(b).u32[2], + (a).u32[3] & ~(b).u32[3]); +} + +/* +PX_FORCE_INLINE VecU16V V4U16Or(VecU16V a, VecU16V b) +{ + return VecU16V( + (a).u16[0]|(b).u16[0], (a).u16[1]|(b).u16[1], (a).u16[2]|(b).u16[2], (a).u16[3]|(b).u16[3], + (a).u16[4]|(b).u16[4], (a).u16[5]|(b).u16[5], (a).u16[6]|(b).u16[6], (a).u16[7]|(b).u16[7]); +} +*/ + +/* +PX_FORCE_INLINE VecU16V V4U16And(VecU16V a, VecU16V b) +{ + return VecU16V( + (a).u16[0]&(b).u16[0], (a).u16[1]&(b).u16[1], (a).u16[2]&(b).u16[2], (a).u16[3]&(b).u16[3], + (a).u16[4]&(b).u16[4], (a).u16[5]&(b).u16[5], (a).u16[6]&(b).u16[6], (a).u16[7]&(b).u16[7]); +} +*/ + +/* +PX_FORCE_INLINE VecU16V V4U16Andc(VecU16V a, VecU16V b) +{ + return VecU16V( + (a).u16[0]&~(b).u16[0], (a).u16[1]&~(b).u16[1], (a).u16[2]&~(b).u16[2], (a).u16[3]&~(b).u16[3], + (a).u16[4]&~(b).u16[4], (a).u16[5]&~(b).u16[5], (a).u16[6]&~(b).u16[6], (a).u16[7]&~(b).u16[7]); +} +*/ + +/* +template PX_FORCE_INLINE VecI32V V4ISplat() +{ + return VecI32V(a, a, a, a); +} + +template PX_FORCE_INLINE VecU32V V4USplat() +{ + return VecU32V(a, a, a, a); +} +*/ + +/* +PX_FORCE_INLINE void V4U16StoreAligned(VecU16V val, VecU16V* address) +{ + *address = val; +} +*/ + +PX_FORCE_INLINE void V4U32StoreAligned(VecU32V val, VecU32V* address) +{ + *address = val; +} + +PX_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b) +{ + VecU32V r = V4U32Andc(*reinterpret_cast(&a), b); + return (*reinterpret_cast(&r)); +} + +PX_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b) +{ + return VecU32V(a.x > b.x ? 0xFFFFffff : 0, a.y > b.y ? 0xFFFFffff : 0, a.z > b.z ? 0xFFFFffff : 0, + a.w > b.w ? 0xFFFFffff : 0); +} + +PX_FORCE_INLINE VecU16V V4U16LoadAligned(VecU16V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE VecU16V V4U16LoadUnaligned(VecU16V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE VecU16V V4U16CompareGt(VecU16V a, VecU16V b) +{ + return VecU16V + ( + BOOL_TO_U16(a.u16[0] > b.u16[0]), BOOL_TO_U16(a.u16[1] > b.u16[1]), BOOL_TO_U16(a.u16[2] > b.u16[2]), BOOL_TO_U16(a.u16[3] > b.u16[3]), + BOOL_TO_U16(a.u16[4] > b.u16[4]), BOOL_TO_U16(a.u16[5] > b.u16[5]), BOOL_TO_U16(a.u16[6] > b.u16[6]), BOOL_TO_U16(a.u16[7] > b.u16[7]) + ); +} + +PX_FORCE_INLINE VecU16V V4I16CompareGt(VecU16V a, VecU16V b) +{ + return VecU16V + ( + BOOL_TO_U16(a.i16[0] > b.i16[0]), BOOL_TO_U16(a.i16[1] > b.i16[1]), BOOL_TO_U16(a.i16[2] > b.i16[2]), BOOL_TO_U16(a.i16[3] > b.i16[3]), + BOOL_TO_U16(a.i16[4] > b.i16[4]), BOOL_TO_U16(a.i16[5] > b.i16[5]), BOOL_TO_U16(a.i16[6] > b.i16[6]), BOOL_TO_U16(a.i16[7] > b.i16[7]) + ); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a) +{ + return Vec4V(PxF32((a).u32[0]), PxF32((a).u32[1]), PxF32((a).u32[2]), PxF32((a).u32[3])); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecI32V(VecI32V a) +{ + return Vec4V(PxF32((a).i32[0]), PxF32((a).i32[1]), PxF32((a).i32[2]), PxF32((a).i32[3])); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_Vec4V(Vec4V a) +{ + float* data = (float*)&a; + return VecI32V(PxI32(data[0]), PxI32(data[1]), PxI32(data[2]), PxI32(data[3])); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecU32V(VecU32V a) +{ + Vec4V b = *reinterpret_cast(&a); + return b; +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecI32V(VecI32V a) +{ + Vec4V b = *reinterpret_cast(&a); + return b; +} + +PX_FORCE_INLINE VecU32V VecU32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + VecU32V b = *reinterpret_cast(&a); + return b; +} + +PX_FORCE_INLINE VecI32V VecI32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + VecI32V b = *reinterpret_cast(&a); + return b; +} + +template +PX_FORCE_INLINE VecU32V V4U32SplatElement(VecU32V a) +{ + return VecU32V((a).u32[index], (a).u32[index], (a).u32[index], (a).u32[index]); +} + +template +PX_FORCE_INLINE VecU32V V4U32SplatElement(BoolV a) +{ + const PxU32 u = (&a.ux)[index]; + return VecU32V(u, u, u, u); +} + +template +PX_FORCE_INLINE Vec4V V4SplatElement(Vec4V a) +{ + float* data = (float*)&a; + return Vec4V(data[index], data[index], data[index], data[index]); +} + +PX_FORCE_INLINE VecU32V U4LoadXYZW(PxU32 x, PxU32 y, PxU32 z, PxU32 w) +{ + return VecU32V(x, y, z, w); +} + +PX_FORCE_INLINE Vec4V V4Abs(const Vec4V a) +{ + return V4Max(a, V4Neg(a)); +} + +PX_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b) +{ + return BoolV(BOOL_TO_U32(a.u32[0] == b.u32[0]), BOOL_TO_U32(a.u32[1] == b.u32[1]), BOOL_TO_U32(a.u32[2] == b.u32[2]), BOOL_TO_U32(a.u32[3] == b.u32[3])); +} + +PX_FORCE_INLINE VecU32V U4Load(const PxU32 i) +{ + return VecU32V(i, i, i, i); +} + +PX_FORCE_INLINE VecU32V U4LoadU(const PxU32* i) +{ + return VecU32V(i[0], i[1], i[2], i[3]); +} + +PX_FORCE_INLINE VecU32V U4LoadA(const PxU32* i) +{ + return VecU32V(i[0], i[1], i[2], i[3]); +} + +PX_FORCE_INLINE VecI32V I4Load(const PxI32 i) +{ + return VecI32V(i, i, i, i); +} + +PX_FORCE_INLINE VecI32V I4LoadU(const PxI32* i) +{ + return VecI32V(i[0], i[1], i[2], i[3]); +} + +PX_FORCE_INLINE VecI32V I4LoadA(const PxI32* i) +{ + return VecI32V(i[0], i[1], i[2], i[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b) +{ + return VecI32V(a.i32[0] + b.i32[0], a.i32[1] + b.i32[1], a.i32[2] + b.i32[2], a.i32[3] + b.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b) +{ + return VecI32V(a.i32[0] - b.i32[0], a.i32[1] - b.i32[1], a.i32[2] - b.i32[2], a.i32[3] - b.i32[3]); +} + +PX_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b) +{ + return BoolV(BOOL_TO_U32(a.i32[0] > b.i32[0]), BOOL_TO_U32(a.i32[1] > b.i32[1]), BOOL_TO_U32(a.i32[2] > b.i32[2]), BOOL_TO_U32(a.i32[3] > b.i32[3])); +} + +PX_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b) +{ + return BoolV(BOOL_TO_U32(a.i32[0] == b.i32[0]), BOOL_TO_U32(a.i32[1] == b.i32[1]), BOOL_TO_U32(a.i32[2] == b.i32[2]), BOOL_TO_U32(a.i32[3] == b.i32[3])); +} + +PX_FORCE_INLINE VecI32V V4I32Sel(const BoolV c, const VecI32V a, const VecI32V b) +{ + return VecI32V(c.ux ? a.i32[0] : b.i32[0], c.uy ? a.i32[1] : b.i32[1], c.uz ? a.i32[2] : b.i32[2], + c.uw ? a.i32[3] : b.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Zero() +{ + return VecI32V(0, 0, 0, 0); +} + +PX_FORCE_INLINE VecI32V VecI32V_One() +{ + return VecI32V(1, 1, 1, 1); +} + +PX_FORCE_INLINE VecI32V VecI32V_Two() +{ + return VecI32V(2, 2, 2, 2); +} + +PX_FORCE_INLINE VecI32V VecI32V_MinusOne() +{ + return VecI32V(-1, -1, -1, -1); +} + +PX_FORCE_INLINE VecU32V U4Zero() +{ + return VecU32V(0, 0, 0, 0); +} + +PX_FORCE_INLINE VecU32V U4One() +{ + return VecU32V(1, 1, 1, 1); +} + +PX_FORCE_INLINE VecU32V U4Two() +{ + return VecU32V(2, 2, 2, 2); +} + +PX_FORCE_INLINE VecShiftV VecI32V_PrepareShift(const VecI32VArg shift) +{ + return shift; +} + +PX_FORCE_INLINE VecI32V VecI32V_LeftShift(const VecI32VArg a, const VecShiftVArg count) +{ + return VecI32V(a.i32[0] << count.i32[0], a.i32[1] << count.i32[1], a.i32[2] << count.i32[2], a.i32[3] + << count.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_RightShift(const VecI32VArg a, const VecShiftVArg count) +{ + return VecI32V(a.i32[0] >> count.i32[0], a.i32[1] >> count.i32[1], a.i32[2] >> count.i32[2], + a.i32[3] >> count.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_And(const VecI32VArg a, const VecI32VArg b) +{ + return VecI32V(a.i32[0] & b.i32[0], a.i32[1] & b.i32[1], a.i32[2] & b.i32[2], a.i32[3] & b.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Or(const VecI32VArg a, const VecI32VArg b) +{ + return VecI32V(a.i32[0] | b.i32[0], a.i32[1] | b.i32[1], a.i32[2] | b.i32[2], a.i32[3] | b.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetX(const VecI32VArg a) +{ + return VecI32V(a.i32[0], a.i32[0], a.i32[0], a.i32[0]); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetY(const VecI32VArg a) +{ + return VecI32V(a.i32[1], a.i32[1], a.i32[1], a.i32[1]); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetZ(const VecI32VArg a) +{ + return VecI32V(a.i32[2], a.i32[2], a.i32[2], a.i32[2]); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetW(const VecI32VArg a) +{ + return VecI32V(a.i32[3], a.i32[3], a.i32[3], a.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sel(const BoolV c, const VecI32VArg a, const VecI32VArg b) +{ + return VecI32V(c.ux ? a.i32[0] : b.i32[0], c.uy ? a.i32[1] : b.i32[1], c.uz ? a.i32[2] : b.i32[2], + c.uw ? a.i32[3] : b.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Merge(const VecI32VArg a, const VecI32VArg b, const VecI32VArg c, const VecI32VArg d) +{ + return VecI32V(a.i32[0], b.i32[0], c.i32[0], d.i32[0]); +} + +PX_FORCE_INLINE void PxI32_From_VecI32V(const VecI32VArg a, PxI32* i) +{ + *i = a.i32[0]; +} + +PX_FORCE_INLINE VecI32V VecI32V_From_BoolV(const BoolVArg b) +{ + return VecI32V(PxI32(b.ux), PxI32(b.uy), PxI32(b.uz), PxI32(b.uw)); +} + +PX_FORCE_INLINE VecU32V VecU32V_From_BoolV(const BoolVArg b) +{ + return VecU32V(b.ux, b.uy, b.uz, b.uw); +} + +PX_FORCE_INLINE void QuatGetMat33V(const QuatVArg q, Vec3V& column0, Vec3V& column1, Vec3V& column2) +{ + const FloatV one = FOne(); + const FloatV x = V4GetX(q); + const FloatV y = V4GetY(q); + const FloatV z = V4GetZ(q); + const FloatV w = V4GetW(q); + + const FloatV x2 = FAdd(x, x); + const FloatV y2 = FAdd(y, y); + const FloatV z2 = FAdd(z, z); + + const FloatV xx = FMul(x2, x); + const FloatV yy = FMul(y2, y); + const FloatV zz = FMul(z2, z); + + const FloatV xy = FMul(x2, y); + const FloatV xz = FMul(x2, z); + const FloatV xw = FMul(x2, w); + + const FloatV yz = FMul(y2, z); + const FloatV yw = FMul(y2, w); + const FloatV zw = FMul(z2, w); + + const FloatV v = FSub(one, xx); + + column0 = V3Merge(FSub(FSub(one, yy), zz), FAdd(xy, zw), FSub(xz, yw)); + column1 = V3Merge(FSub(xy, zw), FSub(v, zz), FAdd(yz, xw)); + column2 = V3Merge(FAdd(xz, yw), FSub(yz, xw), FSub(v, yy)); +} + + +// not used + +/* +PX_FORCE_INLINE Vec4V V4LoadAligned(Vec4V* addr) +{ + return *addr; +} +*/ + +/* +PX_FORCE_INLINE Vec4V V4LoadUnaligned(Vec4V* addr) +{ + return *addr; +} +*/ + +/* +PX_FORCE_INLINE Vec4V V4Ceil(const Vec4V a) +{ + return Vec4V(PxCeil(a.x), PxCeil(a.y), PxCeil(a.z), PxCeil(a.w)); +} + +PX_FORCE_INLINE Vec4V V4Floor(const Vec4V a) +{ + return Vec4V(PxFloor(a.x), PxFloor(a.y), PxFloor(a.z), PxFloor(a.w)); +} +*/ + +/* +PX_FORCE_INLINE VecU32V V4ConvertToU32VSaturate(const Vec4V a, PxU32 power) +{ + PX_ASSERT(power == 0 && "Non-zero power not supported in convertToU32VSaturate"); + PX_UNUSED(power); // prevent warning in release builds + PxF32 ffffFFFFasFloat = PxF32(0xFFFF0000); + return VecU32V( + PxU32(PxClamp((a).x, 0.0f, ffffFFFFasFloat)), + PxU32(PxClamp((a).y, 0.0f, ffffFFFFasFloat)), + PxU32(PxClamp((a).z, 0.0f, ffffFFFFasFloat)), + PxU32(PxClamp((a).w, 0.0f, ffffFFFFasFloat))); +} +*/ + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSVECMATHAOSSCALARINLINE_H diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathSSE.h b/src/PhysX/physx/source/foundation/include/PsVecMathSSE.h new file mode 100644 index 000000000..cc292caf8 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecMathSSE.h @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECMATHSSE_H +#define PSFOUNDATION_PSVECMATHSSE_H + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +namespace +{ + const PX_ALIGN(16, PxF32) minus1w[4] = { 0.0f, 0.0f, 0.0f, -1.0f }; +} + +PX_FORCE_INLINE void QuatGetMat33V(const QuatVArg q, Vec3V& column0, Vec3V& column1, Vec3V& column2) +{ + const __m128 q2 = V4Add(q, q); + const __m128 qw2 = V4MulAdd(q2, V4GetW(q), _mm_load_ps(minus1w)); // (2wx, 2wy, 2wz, 2ww-1) + const __m128 nw2 = Vec3V_From_Vec4V(V4Neg(qw2)); // (-2wx, -2wy, -2wz, 0) + const __m128 v = Vec3V_From_Vec4V(q); + + const __m128 a0 = _mm_shuffle_ps(qw2, nw2, _MM_SHUFFLE(3, 1, 2, 3)); // (2ww-1, 2wz, -2wy, 0) + column0 = V4MulAdd(v, V4GetX(q2), a0); + + const __m128 a1 = _mm_shuffle_ps(qw2, nw2, _MM_SHUFFLE(3, 2, 0, 3)); // (2ww-1, 2wx, -2wz, 0) + column1 = V4MulAdd(v, V4GetY(q2), _mm_shuffle_ps(a1, a1, _MM_SHUFFLE(3, 1, 0, 2))); + + const __m128 a2 = _mm_shuffle_ps(qw2, nw2, _MM_SHUFFLE(3, 0, 1, 3)); // (2ww-1, 2wy, -2wx, 0) + column2 = V4MulAdd(v, V4GetZ(q2), _mm_shuffle_ps(a2, a2, _MM_SHUFFLE(3, 0, 2, 1))); +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + + +#endif // PSFOUNDATION_PSVECMATHSSE_H + diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h b/src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h new file mode 100644 index 000000000..a00a944d6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECMATHUTILITIES_H +#define PSFOUNDATION_PSVECMATHUTILITIES_H + +#include "PsVecMath.h" + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ +/*! + Extend an edge along its length by a factor + */ +PX_FORCE_INLINE void makeFatEdge(Vec3V& p0, Vec3V& p1, const FloatVArg fatCoeff) +{ + const Vec3V delta = V3Sub(p1, p0); + const FloatV m = V3Length(delta); + const BoolV con = FIsGrtr(m, FZero()); + const Vec3V fatDelta = V3Scale(V3ScaleInv(delta, m), fatCoeff); + p0 = V3Sel(con, V3Sub(p0, fatDelta), p0); + p1 = V3Sel(con, V3Add(p1, fatDelta), p1); +} +} +} +} + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsVecQuat.h b/src/PhysX/physx/source/foundation/include/PsVecQuat.h new file mode 100644 index 000000000..8eb3ea487 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecQuat.h @@ -0,0 +1,466 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECQUAT_H +#define PSFOUNDATION_PSVECQUAT_H + +//#include "PsInlineAoS.h" + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +#ifndef PX_PIDIV2 +#define PX_PIDIV2 1.570796327f +#endif + +////////////////////////////////// +// QuatV +////////////////////////////////// +PX_FORCE_INLINE QuatV QuatVLoadXYZW(const PxF32 x, const PxF32 y, const PxF32 z, const PxF32 w) +{ + return V4LoadXYZW(x, y, z, w); +} + +PX_FORCE_INLINE QuatV QuatVLoadU(const PxF32* v) +{ + return V4LoadU(v); +} + +PX_FORCE_INLINE QuatV QuatVLoadA(const PxF32* v) +{ + return V4LoadA(v); +} + +PX_FORCE_INLINE QuatV QuatV_From_RotationAxisAngle(const Vec3V u, const FloatV a) +{ + // q = cos(a/2) + u*sin(a/2) + const FloatV half = FLoad(0.5f); + const FloatV hangle = FMul(a, half); + const FloatV piByTwo(FLoad(PX_PIDIV2)); + const FloatV PiByTwoMinHangle(FSub(piByTwo, hangle)); + const Vec4V hangle2(Vec4V_From_Vec3V(V3Merge(hangle, PiByTwoMinHangle, hangle))); + + /*const FloatV sina = FSin(hangle); + const FloatV cosa = FCos(hangle);*/ + + const Vec4V _sina = V4Sin(hangle2); + const FloatV sina = V4GetX(_sina); + const FloatV cosa = V4GetY(_sina); + + const Vec3V v = V3Scale(u, sina); + // return V4Sel(BTTTF(), Vec4V_From_Vec3V(v), V4Splat(cosa)); + return V4SetW(Vec4V_From_Vec3V(v), cosa); +} + +// Normalize +PX_FORCE_INLINE QuatV QuatNormalize(const QuatV q) +{ + return V4Normalize(q); +} + +PX_FORCE_INLINE FloatV QuatLength(const QuatV q) +{ + return V4Length(q); +} + +PX_FORCE_INLINE FloatV QuatLengthSq(const QuatV q) +{ + return V4LengthSq(q); +} + +PX_FORCE_INLINE FloatV QuatDot(const QuatV a, const QuatV b) // convert this PxQuat to a unit quaternion +{ + return V4Dot(a, b); +} + +PX_FORCE_INLINE QuatV QuatConjugate(const QuatV q) +{ + return V4SetW(V4Neg(q), V4GetW(q)); +} + +PX_FORCE_INLINE Vec3V QuatGetImaginaryPart(const QuatV q) +{ + return Vec3V_From_Vec4V(q); +} + +/** brief computes rotation of x-axis */ +PX_FORCE_INLINE Vec3V QuatGetBasisVector0(const QuatV q) +{ + /*const PxF32 x2 = x*2.0f; + const PxF32 w2 = w*2.0f; + return PxVec3( (w * w2) - 1.0f + x*x2, + (z * w2) + y*x2, + (-y * w2) + z*x2);*/ + + const FloatV two = FLoad(2.f); + const FloatV w = V4GetW(q); + const Vec3V u = Vec3V_From_Vec4V(q); + + const FloatV x2 = FMul(V3GetX(u), two); + const FloatV w2 = FMul(w, two); + + const Vec3V a = V3Scale(u, x2); + const Vec3V tmp = V3Merge(w, V3GetZ(u), FNeg(V3GetY(u))); + // const Vec3V b = V3Scale(tmp, w2); + // const Vec3V ab = V3Add(a, b); + const Vec3V ab = V3ScaleAdd(tmp, w2, a); + return V3SetX(ab, FSub(V3GetX(ab), FOne())); +} + +/** brief computes rotation of y-axis */ +PX_FORCE_INLINE Vec3V QuatGetBasisVector1(const QuatV q) +{ + /*const PxF32 y2 = y*2.0f; + const PxF32 w2 = w*2.0f; + return PxVec3( (-z * w2) + x*y2, + (w * w2) - 1.0f + y*y2, + (x * w2) + z*y2);*/ + + const FloatV two = FLoad(2.f); + const FloatV w = V4GetW(q); + const Vec3V u = Vec3V_From_Vec4V(q); + + const FloatV y2 = FMul(V3GetY(u), two); + const FloatV w2 = FMul(w, two); + + const Vec3V a = V3Scale(u, y2); + const Vec3V tmp = V3Merge(FNeg(V3GetZ(u)), w, V3GetX(u)); + // const Vec3V b = V3Scale(tmp, w2); + // const Vec3V ab = V3Add(a, b); + const Vec3V ab = V3ScaleAdd(tmp, w2, a); + return V3SetY(ab, FSub(V3GetY(ab), FOne())); +} + +/** brief computes rotation of z-axis */ +PX_FORCE_INLINE Vec3V QuatGetBasisVector2(const QuatV q) +{ + /*const PxF32 z2 = z*2.0f; + const PxF32 w2 = w*2.0f; + return PxVec3( (y * w2) + x*z2, + (-x * w2) + y*z2, + (w * w2) - 1.0f + z*z2);*/ + + const FloatV two = FLoad(2.f); + const FloatV w = V4GetW(q); + const Vec3V u = Vec3V_From_Vec4V(q); + + const FloatV z2 = FMul(V3GetZ(u), two); + const FloatV w2 = FMul(w, two); + + const Vec3V a = V3Scale(u, z2); + const Vec3V tmp = V3Merge(V3GetY(u), FNeg(V3GetX(u)), w); + /*const Vec3V b = V3Scale(tmp, w2); + const Vec3V ab = V3Add(a, b);*/ + const Vec3V ab = V3ScaleAdd(tmp, w2, a); + return V3SetZ(ab, FSub(V3GetZ(ab), FOne())); +} + +PX_FORCE_INLINE Vec3V QuatRotate(const QuatV q, const Vec3V v) +{ + /* + const PxVec3 qv(x,y,z); + return (v*(w*w-0.5f) + (qv.cross(v))*w + qv*(qv.dot(v)))*2; + */ + + const FloatV two = FLoad(2.f); + // const FloatV half = FloatV_From_F32(0.5f); + const FloatV nhalf = FLoad(-0.5f); + const Vec3V u = Vec3V_From_Vec4V(q); + const FloatV w = V4GetW(q); + // const FloatV w2 = FSub(FMul(w, w), half); + const FloatV w2 = FScaleAdd(w, w, nhalf); + const Vec3V a = V3Scale(v, w2); + // const Vec3V b = V3Scale(V3Cross(u, v), w); + // const Vec3V c = V3Scale(u, V3Dot(u, v)); + // return V3Scale(V3Add(V3Add(a, b), c), two); + const Vec3V temp = V3ScaleAdd(V3Cross(u, v), w, a); + return V3Scale(V3ScaleAdd(u, V3Dot(u, v), temp), two); +} + +PX_FORCE_INLINE Vec3V QuatTransform(const QuatV q, const Vec3V p, const Vec3V v) +{ + // p + q.rotate(v) + const FloatV two = FLoad(2.f); + // const FloatV half = FloatV_From_F32(0.5f); + const FloatV nhalf = FLoad(-0.5f); + const Vec3V u = Vec3V_From_Vec4V(q); + const FloatV w = V4GetW(q); + // const FloatV w2 = FSub(FMul(w, w), half); + const FloatV w2 = FScaleAdd(w, w, nhalf); + const Vec3V a = V3Scale(v, w2); + /*const Vec3V b = V3Scale(V3Cross(u, v), w); + const Vec3V c = V3Scale(u, V3Dot(u, v)); + return V3ScaleAdd(V3Add(V3Add(a, b), c), two, p);*/ + const Vec3V temp = V3ScaleAdd(V3Cross(u, v), w, a); + const Vec3V z = V3ScaleAdd(u, V3Dot(u, v), temp); + return V3ScaleAdd(z, two, p); +} + +PX_FORCE_INLINE Vec3V QuatRotateInv(const QuatV q, const Vec3V v) +{ + + // const PxVec3 qv(x,y,z); + // return (v*(w*w-0.5f) - (qv.cross(v))*w + qv*(qv.dot(v)))*2; + + const FloatV two = FLoad(2.f); + const FloatV nhalf = FLoad(-0.5f); + const Vec3V u = Vec3V_From_Vec4V(q); + const FloatV w = V4GetW(q); + const FloatV w2 = FScaleAdd(w, w, nhalf); + const Vec3V a = V3Scale(v, w2); + /*const Vec3V b = V3Scale(V3Cross(u, v), w); + const Vec3V c = V3Scale(u, V3Dot(u, v)); + return V3Scale(V3Add(V3Sub(a, b), c), two);*/ + const Vec3V temp = V3NegScaleSub(V3Cross(u, v), w, a); + return V3Scale(V3ScaleAdd(u, V3Dot(u, v), temp), two); +} + +PX_FORCE_INLINE QuatV QuatMul(const QuatV a, const QuatV b) +{ + const Vec3V imagA = Vec3V_From_Vec4V(a); + const Vec3V imagB = Vec3V_From_Vec4V(b); + const FloatV rA = V4GetW(a); + const FloatV rB = V4GetW(b); + + const FloatV real = FSub(FMul(rA, rB), V3Dot(imagA, imagB)); + const Vec3V v0 = V3Scale(imagA, rB); + const Vec3V v1 = V3Scale(imagB, rA); + const Vec3V v2 = V3Cross(imagA, imagB); + const Vec3V imag = V3Add(V3Add(v0, v1), v2); + + return V4SetW(Vec4V_From_Vec3V(imag), real); +} + +PX_FORCE_INLINE QuatV QuatAdd(const QuatV a, const QuatV b) +{ + return V4Add(a, b); +} + +PX_FORCE_INLINE QuatV QuatNeg(const QuatV q) +{ + return V4Neg(q); +} + +PX_FORCE_INLINE QuatV QuatSub(const QuatV a, const QuatV b) +{ + return V4Sub(a, b); +} + +PX_FORCE_INLINE QuatV QuatScale(const QuatV a, const FloatV b) +{ + return V4Scale(a, b); +} + +PX_FORCE_INLINE QuatV QuatMerge(const FloatV* const floatVArray) +{ + return V4Merge(floatVArray); +} + +PX_FORCE_INLINE QuatV QuatMerge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w) +{ + return V4Merge(x, y, z, w); +} + +PX_FORCE_INLINE QuatV QuatIdentity() +{ + return V4SetW(V4Zero(), FOne()); +} + +PX_FORCE_INLINE bool isFiniteQuatV(const QuatV q) +{ + return isFiniteVec4V(q); +} + +PX_FORCE_INLINE bool isValidQuatV(const QuatV q) +{ + const FloatV unitTolerance = FLoad(1e-4f); + const FloatV tmp = FAbs(FSub(QuatLength(q), FOne())); + const BoolV con = FIsGrtr(unitTolerance, tmp); + return isFiniteVec4V(q) & (BAllEqTTTT(con) == 1); +} + +PX_FORCE_INLINE bool isSaneQuatV(const QuatV q) +{ + const FloatV unitTolerance = FLoad(1e-2f); + const FloatV tmp = FAbs(FSub(QuatLength(q), FOne())); + const BoolV con = FIsGrtr(unitTolerance, tmp); + return isFiniteVec4V(q) & (BAllEqTTTT(con) == 1); +} + +PX_FORCE_INLINE Mat33V QuatGetMat33V(const QuatVArg q) +{ + // const FloatV two = FloatV_From_F32(2.f); + // const FloatV one = FOne(); + + // const FloatV x = V4GetX(q); + // const FloatV y = V4GetY(q); + // const FloatV z = V4GetZ(q); + // const Vec4V _q = V4Mul(q, two); + // + ////const FloatV w = V4GetW(q); + + // const Vec4V t0 = V4Mul(_q, x); // 2xx, 2xy, 2xz, 2xw + // const Vec4V t1 = V4Mul(_q, y); // 2xy, 2yy, 2yz, 2yw + // const Vec4V t2 = V4Mul(_q, z); // 2xz, 2yz, 2zz, 2zw + ////const Vec4V t3 = V4Mul(_q, w); // 2xw, 2yw, 2zw, 2ww + + // const FloatV xx2 = V4GetX(t0); + // const FloatV xy2 = V4GetY(t0); + // const FloatV xz2 = V4GetZ(t0); + // const FloatV xw2 = V4GetW(t0); + + // const FloatV yy2 = V4GetY(t1); + // const FloatV yz2 = V4GetZ(t1); + // const FloatV yw2 = V4GetW(t1); + + // const FloatV zz2 = V4GetZ(t2); + // const FloatV zw2 = V4GetW(t2); + + ////const FloatV ww2 = V4GetW(t3); + + // const FloatV c00 = FSub(one, FAdd(yy2, zz2)); + // const FloatV c01 = FSub(xy2, zw2); + // const FloatV c02 = FAdd(xz2, yw2); + + // const FloatV c10 = FAdd(xy2, zw2); + // const FloatV c11 = FSub(one, FAdd(xx2, zz2)); + // const FloatV c12 = FSub(yz2, xw2); + + // const FloatV c20 = FSub(xz2, yw2); + // const FloatV c21 = FAdd(yz2, xw2); + // const FloatV c22 = FSub(one, FAdd(xx2, yy2)); + + // const Vec3V c0 = V3Merge(c00, c10, c20); + // const Vec3V c1 = V3Merge(c01, c11, c21); + // const Vec3V c2 = V3Merge(c02, c12, c22); + + // return Mat33V(c0, c1, c2); + + const FloatV one = FOne(); + const FloatV x = V4GetX(q); + const FloatV y = V4GetY(q); + const FloatV z = V4GetZ(q); + const FloatV w = V4GetW(q); + + const FloatV x2 = FAdd(x, x); + const FloatV y2 = FAdd(y, y); + const FloatV z2 = FAdd(z, z); + + const FloatV xx = FMul(x2, x); + const FloatV yy = FMul(y2, y); + const FloatV zz = FMul(z2, z); + + const FloatV xy = FMul(x2, y); + const FloatV xz = FMul(x2, z); + const FloatV xw = FMul(x2, w); + + const FloatV yz = FMul(y2, z); + const FloatV yw = FMul(y2, w); + const FloatV zw = FMul(z2, w); + + const FloatV v = FSub(one, xx); + + const Vec3V column0 = V3Merge(FSub(FSub(one, yy), zz), FAdd(xy, zw), FSub(xz, yw)); + const Vec3V column1 = V3Merge(FSub(xy, zw), FSub(v, zz), FAdd(yz, xw)); + const Vec3V column2 = V3Merge(FAdd(xz, yw), FSub(yz, xw), FSub(v, yy)); + return Mat33V(column0, column1, column2); +} + +PX_FORCE_INLINE QuatV Mat33GetQuatV(const Mat33V& a) +{ + const FloatV one = FOne(); + const FloatV zero = FZero(); + const FloatV half = FLoad(0.5f); + const FloatV two = FLoad(2.f); + const FloatV scale = FLoad(0.25f); + const FloatV a00 = V3GetX(a.col0); + const FloatV a11 = V3GetY(a.col1); + const FloatV a22 = V3GetZ(a.col2); + + const FloatV a21 = V3GetZ(a.col1); // row=2, col=1; + const FloatV a12 = V3GetY(a.col2); // row=1, col=2; + const FloatV a02 = V3GetX(a.col2); // row=0, col=2; + const FloatV a20 = V3GetZ(a.col0); // row=2, col=0; + const FloatV a10 = V3GetY(a.col0); // row=1, col=0; + const FloatV a01 = V3GetX(a.col1); // row=0, col=1; + + const Vec3V vec0 = V3Merge(a21, a02, a10); + const Vec3V vec1 = V3Merge(a12, a20, a01); + const Vec3V v = V3Sub(vec0, vec1); + const Vec3V g = V3Add(vec0, vec1); + + const FloatV trace = FAdd(a00, FAdd(a11, a22)); + + if(FAllGrtrOrEq(trace, zero)) + { + const FloatV h = FSqrt(FAdd(trace, one)); + const FloatV w = FMul(half, h); + const FloatV s = FMul(half, FRecip(h)); + const Vec3V u = V3Scale(v, s); + return V4SetW(Vec4V_From_Vec3V(u), w); + } + else + { + const FloatV ntrace = FNeg(trace); + const Vec3V d = V3Merge(a00, a11, a22); + const BoolV con0 = BAllTrue3(V3IsGrtrOrEq(V3Splat(a00), d)); + const BoolV con1 = BAllTrue3(V3IsGrtrOrEq(V3Splat(a11), d)); + + const FloatV t0 = FAdd(one, FScaleAdd(a00, two, ntrace)); + const FloatV t1 = FAdd(one, FScaleAdd(a11, two, ntrace)); + const FloatV t2 = FAdd(one, FScaleAdd(a22, two, ntrace)); + + const FloatV t = FSel(con0, t0, FSel(con1, t1, t2)); + + const FloatV h = FMul(two, FSqrt(t)); + const FloatV s = FRecip(h); + const FloatV g0 = FMul(scale, h); + const Vec3V vs = V3Scale(v, s); + const Vec3V gs = V3Scale(g, s); + const FloatV gsx = V3GetX(gs); + const FloatV gsy = V3GetY(gs); + const FloatV gsz = V3GetZ(gs); + // vs.x= (a21 - a12)*s; vs.y=(a02 - a20)*s; vs.z=(a10 - a01)*s; + // gs.x= (a21 + a12)*s; gs.y=(a02 + a20)*s; gs.z=(a10 + a01)*s; + const Vec4V v0 = V4Merge(g0, gsz, gsy, V3GetX(vs)); + const Vec4V v1 = V4Merge(gsz, g0, gsx, V3GetY(vs)); + const Vec4V v2 = V4Merge(gsy, gsx, g0, V3GetZ(vs)); + return V4Sel(con0, v0, V4Sel(con1, v1, v2)); + } +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsVecTransform.h b/src/PhysX/physx/source/foundation/include/PsVecTransform.h new file mode 100644 index 000000000..d9da8d0e4 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecTransform.h @@ -0,0 +1,283 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECTRANSFORM_H +#define PSFOUNDATION_PSVECTRANSFORM_H + +#include "PsVecMath.h" +#include "foundation/PxTransform.h" + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +class PsTransformV +{ + public: + QuatV q; + Vec3V p; + + PX_FORCE_INLINE PsTransformV(const PxTransform& orientation) + { + // const PxQuat oq = orientation.q; + // const PxF32 f[4] = {oq.x, oq.y, oq.z, oq.w}; + q = QuatVLoadXYZW(orientation.q.x, orientation.q.y, orientation.q.z, orientation.q.w); + // q = QuatV_From_F32Array(&oq.x); + p = V3LoadU(orientation.p); + } + + PX_FORCE_INLINE PsTransformV(const Vec3VArg p0 = V3Zero(), const QuatVArg q0 = QuatIdentity()) : q(q0), p(p0) + { + PX_ASSERT(isSaneQuatV(q0)); + } + + PX_FORCE_INLINE PsTransformV operator*(const PsTransformV& x) const + { + PX_ASSERT(x.isSane()); + return transform(x); + } + + PX_FORCE_INLINE PsTransformV getInverse() const + { + PX_ASSERT(isFinite()); + // return PxTransform(q.rotateInv(-p),q.getConjugate()); + return PsTransformV(QuatRotateInv(q, V3Neg(p)), QuatConjugate(q)); + } + + PX_FORCE_INLINE void normalize() + { + p = V3Zero(); + q = QuatIdentity(); + } + + PX_FORCE_INLINE void Invalidate() + { + p = V3Splat(FMax()); + q = QuatIdentity(); + } + + PX_FORCE_INLINE Vec3V transform(const Vec3VArg input) const + { + PX_ASSERT(isFinite()); + // return q.rotate(input) + p; + return QuatTransform(q, p, input); + } + + PX_FORCE_INLINE Vec3V transformInv(const Vec3VArg input) const + { + PX_ASSERT(isFinite()); + // return q.rotateInv(input-p); + return QuatRotateInv(q, V3Sub(input, p)); + } + + PX_FORCE_INLINE Vec3V rotate(const Vec3VArg input) const + { + PX_ASSERT(isFinite()); + // return q.rotate(input); + return QuatRotate(q, input); + } + + PX_FORCE_INLINE Vec3V rotateInv(const Vec3VArg input) const + { + PX_ASSERT(isFinite()); + // return q.rotateInv(input); + return QuatRotateInv(q, input); + } + + //! Transform transform to parent (returns compound transform: first src, then *this) + PX_FORCE_INLINE PsTransformV transform(const PsTransformV& src) const + { + PX_ASSERT(src.isSane()); + PX_ASSERT(isSane()); + // src = [srct, srcr] -> [r*srct + t, r*srcr] + // return PxTransform(q.rotate(src.p) + p, q*src.q); + return PsTransformV(V3Add(QuatRotate(q, src.p), p), QuatMul(q, src.q)); + } + + /** + \brief returns true if finite and q is a unit quaternion + */ + + PX_FORCE_INLINE bool isValid() const + { + // return p.isFinite() && q.isFinite() && q.isValid(); + return isFiniteVec3V(p) & isFiniteQuatV(q) & isValidQuatV(q); + } + + /** + \brief returns true if finite and quat magnitude is reasonably close to unit to allow for some accumulation of error + vs isValid + */ + + PX_FORCE_INLINE bool isSane() const + { + // return isFinite() && q.isSane(); + return isFinite() & isSaneQuatV(q); + } + + /** + \brief returns true if all elems are finite (not NAN or INF, etc.) + */ + PX_FORCE_INLINE bool isFinite() const + { + // return p.isFinite() && q.isFinite(); + return isFiniteVec3V(p) & isFiniteQuatV(q); + } + + //! Transform transform from parent (returns compound transform: first src, then this->inverse) + PX_FORCE_INLINE PsTransformV transformInv(const PsTransformV& src) const + { + PX_ASSERT(src.isSane()); + PX_ASSERT(isFinite()); + // src = [srct, srcr] -> [r^-1*(srct-t), r^-1*srcr] + /*PxQuat qinv = q.getConjugate(); + return PxTransform(qinv.rotate(src.p - p), qinv*src.q);*/ + const QuatV qinv = QuatConjugate(q); + const Vec3V v = QuatRotate(qinv, V3Sub(src.p, p)); + const QuatV rot = QuatMul(qinv, src.q); + return PsTransformV(v, rot); + } + + static PX_FORCE_INLINE PsTransformV createIdentity() + { + return PsTransformV(V3Zero()); + } +}; + +PX_FORCE_INLINE PsTransformV loadTransformA(const PxTransform& transform) +{ + const QuatV q0 = QuatVLoadA(&transform.q.x); + const Vec3V p0 = V3LoadA(&transform.p.x); + + return PsTransformV(p0, q0); +} + +PX_FORCE_INLINE PsTransformV loadTransformU(const PxTransform& transform) +{ + const QuatV q0 = QuatVLoadU(&transform.q.x); + const Vec3V p0 = V3LoadU(&transform.p.x); + + return PsTransformV(p0, q0); +} + +class PsMatTransformV +{ + public: + Mat33V rot; + Vec3V p; + + PX_FORCE_INLINE PsMatTransformV() + { + p = V3Zero(); + rot = M33Identity(); + } + PX_FORCE_INLINE PsMatTransformV(const Vec3VArg _p, const Mat33V& _rot) + { + p = _p; + rot = _rot; + } + + PX_FORCE_INLINE PsMatTransformV(const PsTransformV& other) + { + p = other.p; + QuatGetMat33V(other.q, rot.col0, rot.col1, rot.col2); + } + + PX_FORCE_INLINE PsMatTransformV(const Vec3VArg _p, const QuatV& quat) + { + p = _p; + QuatGetMat33V(quat, rot.col0, rot.col1, rot.col2); + } + + PX_FORCE_INLINE Vec3V getCol0() const + { + return rot.col0; + } + + PX_FORCE_INLINE Vec3V getCol1() const + { + return rot.col1; + } + + PX_FORCE_INLINE Vec3V getCol2() const + { + return rot.col2; + } + + PX_FORCE_INLINE void setCol0(const Vec3VArg col0) + { + rot.col0 = col0; + } + + PX_FORCE_INLINE void setCol1(const Vec3VArg col1) + { + rot.col1 = col1; + } + + PX_FORCE_INLINE void setCol2(const Vec3VArg col2) + { + rot.col2 = col2; + } + + PX_FORCE_INLINE Vec3V transform(const Vec3VArg input) const + { + return V3Add(p, M33MulV3(rot, input)); + } + + PX_FORCE_INLINE Vec3V transformInv(const Vec3VArg input) const + { + return M33TrnspsMulV3(rot, V3Sub(input, p)); // QuatRotateInv(q, V3Sub(input, p)); + } + + PX_FORCE_INLINE Vec3V rotate(const Vec3VArg input) const + { + return M33MulV3(rot, input); + } + + PX_FORCE_INLINE Vec3V rotateInv(const Vec3VArg input) const + { + return M33TrnspsMulV3(rot, input); + } + + PX_FORCE_INLINE PsMatTransformV transformInv(const PsMatTransformV& src) const + { + + const Vec3V v = M33TrnspsMulV3(rot, V3Sub(src.p, p)); + const Mat33V mat = M33MulM33(M33Trnsps(rot), src.rot); + return PsMatTransformV(v, mat); + } +}; +} +} +} + +#endif diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h new file mode 100644 index 000000000..02fa34a25 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXAOS_H +#define PSFOUNDATION_PSUNIXAOS_H + +// no includes here! this file should be included from PxcVecMath.h only!!! + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +#if PX_INTEL_FAMILY +#include "sse2/PsUnixSse2AoS.h" +#elif PX_NEON +#include "neon/PsUnixNeonAoS.h" +#else +#error No SIMD implementation for this unix platform. +#endif + +#endif // PSFOUNDATION_PSUNIXAOS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h new file mode 100644 index 000000000..3a2138fea --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXFPU_H +#define PSFOUNDATION_PSUNIXFPU_H + +#include "foundation/PxPreprocessor.h" + +#if PX_LINUX || PX_PS4 || PX_OSX + +#if PX_X86 || PX_X64 +#if PX_EMSCRIPTEN +#include +#endif +#include +#elif PX_NEON +#include +#endif + + +PX_INLINE physx::shdfnd::SIMDGuard::SIMDGuard() +{ +#if !PX_EMSCRIPTEN && (PX_X86 || PX_X64) + mControlWord = _mm_getcsr(); + // set default (disable exceptions: _MM_MASK_MASK) and FTZ (_MM_FLUSH_ZERO_ON), DAZ (_MM_DENORMALS_ZERO_ON: (1<<6)) + _mm_setcsr(_MM_MASK_MASK | _MM_FLUSH_ZERO_ON | (1 << 6)); +#endif +} + +PX_INLINE physx::shdfnd::SIMDGuard::~SIMDGuard() +{ +#if !PX_EMSCRIPTEN && (PX_X86 || PX_X64) + // restore control word and clear exception flags + // (setting exception state flags cause exceptions on the first following fp operation) + _mm_setcsr(mControlWord & ~_MM_EXCEPT_MASK); +#endif +} + +#else +#error No SIMD implementation for this unix platform. +#endif // PX_LINUX || PX_PS4 || PX_OSX + +#endif // #ifndef PSFOUNDATION_PSUNIXFPU_H diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h new file mode 100644 index 000000000..9c14ea9f8 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h @@ -0,0 +1,45 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXINLINEAOS_H +#define PSFOUNDATION_PSUNIXINLINEAOS_H + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +#if PX_INTEL_FAMILY +#include "sse2/PsUnixSse2InlineAoS.h" +#elif PX_NEON +#include "neon/PsUnixNeonInlineAoS.h" +#else +#error No SIMD implementation for this unix platform. +#endif + +#endif // PSFOUNDATION_PSUNIXINLINEAOS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h new file mode 100644 index 000000000..b69b57c54 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h @@ -0,0 +1,153 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXINTRINSICS_H +#define PSFOUNDATION_PSUNIXINTRINSICS_H + +#include "Ps.h" +#include "foundation/PxAssert.h" +#include + +#if PX_ANDROID +#include // for Ns::debugBreak() { raise(SIGTRAP); } +#endif + +#if 0 +#include +#endif + +// this file is for internal intrinsics - that is, intrinsics that are used in +// cross platform code but do not appear in the API + +#if !(PX_LINUX || PX_ANDROID || PX_PS4 || PX_APPLE_FAMILY) +#error "This file should only be included by unix builds!!" +#endif + +namespace physx +{ +namespace shdfnd +{ + +PX_FORCE_INLINE void memoryBarrier() +{ + __sync_synchronize(); +} + +/*! +Return the index of the highest set bit. Undefined for zero arg. +*/ +PX_INLINE uint32_t highestSetBitUnsafe(uint32_t v) +{ + + return 31 - __builtin_clz(v); +} + +/*! +Return the index of the highest set bit. Undefined for zero arg. +*/ +PX_INLINE int32_t lowestSetBitUnsafe(uint32_t v) +{ + return __builtin_ctz(v); +} + +/*! +Returns the index of the highest set bit. Returns 32 for v=0. +*/ +PX_INLINE uint32_t countLeadingZeros(uint32_t v) +{ + if(v) + return __builtin_clz(v); + else + return 32; +} + +/*! +Prefetch aligned 64B x86, 32b ARM around \c ptr+offset. +*/ +PX_FORCE_INLINE void prefetchLine(const void* ptr, uint32_t offset = 0) +{ + __builtin_prefetch(reinterpret_cast(ptr) + offset, 0, 3); +} + +/*! +Prefetch \c count bytes starting at \c ptr. +*/ +#if PX_ANDROID || PX_IOS +PX_FORCE_INLINE void prefetch(const void* ptr, uint32_t count = 1) +{ + const char* cp = static_cast(ptr); + size_t p = reinterpret_cast(ptr); + uint32_t startLine = uint32_t(p >> 5), endLine = uint32_t((p + count - 1) >> 5); + uint32_t lines = endLine - startLine + 1; + do + { + prefetchLine(cp); + cp += 32; + } while(--lines); +} +#else +PX_FORCE_INLINE void prefetch(const void* ptr, uint32_t count = 1) +{ + const char* cp = reinterpret_cast(ptr); + uint64_t p = size_t(ptr); + uint64_t startLine = p >> 6, endLine = (p + count - 1) >> 6; + uint64_t lines = endLine - startLine + 1; + do + { + prefetchLine(cp); + cp += 64; + } while(--lines); +} +#endif + +//! \brief platform-specific reciprocal +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipFast(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific fast reciprocal square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrtFast(float a) +{ + return 1.0f / ::sqrtf(a); +} + +//! \brief platform-specific floor +PX_CUDA_CALLABLE PX_FORCE_INLINE float floatFloor(float x) +{ + return ::floorf(x); +} + +#define NS_EXPECT_TRUE(x) x +#define NS_EXPECT_FALSE(x) x + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSUNIXINTRINSICS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h new file mode 100644 index 000000000..8753a24b8 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXTRIGCONSTANTS_H +#define PSFOUNDATION_PSUNIXTRIGCONSTANTS_H + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +//#define PX_GLOBALCONST extern const __declspec(selectany) +#define PX_GLOBALCONST extern const __attribute__((weak)) + +PX_ALIGN_PREFIX(16) +struct PX_VECTORF32 +{ + float f[4]; +} PX_ALIGN_SUFFIX(16); + +PX_GLOBALCONST PX_VECTORF32 g_PXSinCoefficients0 = { { 1.0f, -0.166666667f, 8.333333333e-3f, -1.984126984e-4f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinCoefficients1 = { { 2.755731922e-6f, -2.505210839e-8f, 1.605904384e-10f, -7.647163732e-13f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinCoefficients2 = { { 2.811457254e-15f, -8.220635247e-18f, 1.957294106e-20f, -3.868170171e-23f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXCosCoefficients0 = { { 1.0f, -0.5f, 4.166666667e-2f, -1.388888889e-3f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosCoefficients1 = { { 2.480158730e-5f, -2.755731922e-7f, 2.087675699e-9f, -1.147074560e-11f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosCoefficients2 = { { 4.779477332e-14f, -1.561920697e-16f, 4.110317623e-19f, -8.896791392e-22f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTanCoefficients0 = { { 1.0f, 0.333333333f, 0.133333333f, 5.396825397e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXTanCoefficients1 = { { 2.186948854e-2f, 8.863235530e-3f, 3.592128167e-3f, 1.455834485e-3f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXTanCoefficients2 = { { 5.900274264e-4f, 2.391290764e-4f, 9.691537707e-5f, 3.927832950e-5f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients0 = { { -0.05806367563904f, -0.41861972469416f, 0.22480114791621f, 2.17337241360606f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients1 = { { 0.61657275907170f, 4.29696498283455f, -1.18942822255452f, -6.53784832094831f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients2 = { { -1.36926553863413f, -4.48179294237210f, 1.41810672941833f, 5.48179257935713f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXATanCoefficients0 = { { 1.0f, 0.333333334f, 0.2f, 0.142857143f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanCoefficients1 = { { 1.111111111e-1f, 9.090909091e-2f, 7.692307692e-2f, 6.666666667e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanCoefficients2 = { { 5.882352941e-2f, 5.263157895e-2f, 4.761904762e-2f, 4.347826087e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinEstCoefficients = { { 1.0f, -1.66521856991541e-1f, 8.199913018755e-3f, -1.61475937228e-4f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosEstCoefficients = { { 1.0f, -4.95348008918096e-1f, 3.878259962881e-2f, -9.24587976263e-4f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTanEstCoefficients = { { 2.484f, -1.954923183e-1f, 2.467401101f, PxInvPi } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanEstCoefficients = { { 7.689891418951e-1f, 1.104742493348f, 8.661844266006e-1f, PxPiDivTwo } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinEstCoefficients = { { -1.36178272886711f, 2.37949493464538f, -8.08228565650486e-1f, 2.78440142746736e-1f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXASinEstConstants = { { 1.00000011921f, PxPiDivTwo, 0.0f, 0.0f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXPiConstants0 = { { PxPi, PxTwoPi, PxInvPi, PxInvTwoPi } }; +PX_GLOBALCONST PX_VECTORF32 g_PXReciprocalTwoPi = { { PxInvTwoPi, PxInvTwoPi, PxInvTwoPi, PxInvTwoPi } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTwoPi = { { PxTwoPi, PxTwoPi, PxTwoPi, PxTwoPi } }; + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h new file mode 100644 index 000000000..fc90c5235 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h @@ -0,0 +1,140 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXNEONAOS_H +#define PSFOUNDATION_PSUNIXNEONAOS_H + +// no includes here! this file should be included from PxcVecMath.h only!!! + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +// only ARM NEON compatible platforms should reach this +#include + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +typedef float32x2_t FloatV; +typedef float32x4_t Vec3V; +typedef float32x4_t Vec4V; +typedef uint32x4_t BoolV; +typedef float32x4_t QuatV; + +typedef uint32x4_t VecU32V; +typedef int32x4_t VecI32V; +typedef uint16x8_t VecU16V; +typedef int16x8_t VecI16V; +typedef uint8x16_t VecU8V; + +#define FloatVArg FloatV & +#define Vec3VArg Vec3V & +#define Vec4VArg Vec4V & +#define BoolVArg BoolV & +#define VecU32VArg VecU32V & +#define VecI32VArg VecI32V & +#define VecU16VArg VecU16V & +#define VecI16VArg VecI16V & +#define VecU8VArg VecU8V & +#define QuatVArg QuatV & + +// KS - TODO - make an actual VecCrossV type for NEON +#define VecCrossV Vec3V + +typedef VecI32V VecShiftV; +#define VecShiftVArg VecShiftV & + +PX_ALIGN_PREFIX(16) +struct Mat33V +{ + Mat33V() + { + } + Mat33V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat34V +{ + Mat34V() + { + } + Mat34V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2, const Vec3V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); + Vec3V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat43V +{ + Mat43V() + { + } + Mat43V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat44V +{ + Mat44V() + { + } + Mat44V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2, const Vec4V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); + Vec4V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSUNIXNEONAOS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h new file mode 100644 index 000000000..2db302e93 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h @@ -0,0 +1,3597 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXNEONINLINEAOS_H +#define PSFOUNDATION_PSUNIXNEONINLINEAOS_H + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +// improved estimates +#define VRECIPEQ recipq_newton<1> +#define VRECIPE recip_newton<1> +#define VRECIPSQRTEQ rsqrtq_newton<1> +#define VRECIPSQRTE rsqrt_newton<1> + +// "exact" +#define VRECIPQ recipq_newton<4> +#if PX_SWITCH +// StabilizationTests.AveragePoint needs more precision to succeed. +#define VRECIP recip_newton<5> +#else +#define VRECIP recip_newton<4> +#endif +#define VRECIPSQRTQ rsqrtq_newton<4> +#define VRECIPSQRT rsqrt_newton<4> + +#define VECMATH_AOS_EPSILON (1e-3f) + +////////////////////////////////////////////////////////////////////// +//Test that Vec3V and FloatV are legal +////////////////////////////////// + +#define FLOAT_COMPONENTS_EQUAL_THRESHOLD 0.01f +PX_FORCE_INLINE bool isValidFloatV(const FloatV a) +{ + /* + PX_ALIGN(16, PxF32) data[4]; + vst1_f32(reinterpret_cast(data), a); + return + PxU32* intData = reinterpret_cast(data); + return intData[0] == intData[1]; + */ + PX_ALIGN(16, PxF32) data[4]; + vst1_f32(reinterpret_cast(data), a); + const float32_t x = data[0]; + const float32_t y = data[1]; + + if (PxAbs(x - y) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + { + return true; + } + + if (PxAbs((x - y) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + { + return true; + } + + return false; +} + +PX_FORCE_INLINE bool isValidVec3V(const Vec3V a) +{ + const float32_t w = vgetq_lane_f32(a, 3); + return (0.0f == w); + //const PxU32* intData = reinterpret_cast(&w); + //return *intData == 0; +} + +PX_FORCE_INLINE bool isAligned16(const void* a) +{ + return(0 == (size_t(a) & 0x0f)); +} + +#if PX_DEBUG +#define ASSERT_ISVALIDVEC3V(a) PX_ASSERT(isValidVec3V(a)) +#define ASSERT_ISVALIDFLOATV(a) PX_ASSERT(isValidFloatV(a)) +#define ASSERT_ISALIGNED16(a) PX_ASSERT(isAligned16(static_cast(a))) +#else +#define ASSERT_ISVALIDVEC3V(a) +#define ASSERT_ISVALIDFLOATV(a) +#define ASSERT_ISALIGNED16(a) +#endif + +namespace internalUnitNeonSimd +{ +PX_FORCE_INLINE PxU32 BAllTrue4_R(const BoolV a) +{ + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + const uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + return PxU32(vget_lane_u32(finalReduce, 0) == 0xffffFFFF); +} + +PX_FORCE_INLINE PxU32 BAllTrue3_R(const BoolV a) +{ + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + const uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + return PxU32((vget_lane_u32(finalReduce, 0) & 0xffFFff) == 0xffFFff); +} + +PX_FORCE_INLINE PxU32 BAnyTrue4_R(const BoolV a) +{ + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + const uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + return PxU32(vget_lane_u32(finalReduce, 0) != 0x0); +} + +PX_FORCE_INLINE PxU32 BAnyTrue3_R(const BoolV a) +{ + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + const uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + return PxU32((vget_lane_u32(finalReduce, 0) & 0xffFFff) != 0); +} +} + +namespace _VecMathTests +{ +// PT: this function returns an invalid Vec3V (W!=0.0f) just for unit-testing 'isValidVec3V' +PX_FORCE_INLINE Vec3V getInvalidVec3V() +{ + PX_ALIGN(16, PxF32) data[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE bool allElementsEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vget_lane_u32(vceq_f32(a, b), 0) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec3V(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return V3AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec4V(const Vec4V a, const Vec4V b) +{ + return V4AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualBoolV(const BoolV a, const BoolV b) +{ + return internalUnitNeonSimd::BAllTrue4_R(vceqq_u32(a, b)) != 0; +} + +PX_FORCE_INLINE PxU32 V4U32AllEq(const VecU32V a, const VecU32V b) +{ + return internalUnitNeonSimd::BAllTrue4_R(V4IsEqU32(a, b)); +} + +PX_FORCE_INLINE bool allElementsEqualVecU32V(const VecU32V a, const VecU32V b) +{ + return V4U32AllEq(a, b) != 0; +} + +PX_FORCE_INLINE BoolV V4IsEqI32(const VecI32V a, const VecI32V b) +{ + return vceqq_s32(a, b); +} + +PX_FORCE_INLINE PxU32 V4I32AllEq(const VecI32V a, const VecI32V b) +{ + return internalUnitNeonSimd::BAllTrue4_R(V4IsEqI32(a, b)); +} + +PX_FORCE_INLINE bool allElementsEqualVecI32V(const VecI32V a, const VecI32V b) +{ + return V4I32AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsNearEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + + const float32x2_t c = vsub_f32(a, b); + const float32x2_t error = vdup_n_f32(VECMATH_AOS_EPSILON); +// absolute compare abs(error) > abs(c) + const uint32x2_t greater = vcagt_f32(error, c); + const uint32x2_t min = vpmin_u32(greater, greater); + return vget_lane_u32(min, 0) != 0x0; +} + +PX_FORCE_INLINE bool allElementsNearEqualVec3V(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + const float32x4_t c = vsubq_f32(a, b); + const float32x4_t error = vdupq_n_f32(VECMATH_AOS_EPSILON); +// absolute compare abs(error) > abs(c) + const uint32x4_t greater = vcagtq_f32(error, c); + return internalUnitNeonSimd::BAllTrue3_R(greater) != 0; +} + +PX_FORCE_INLINE bool allElementsNearEqualVec4V(const Vec4V a, const Vec4V b) +{ + const float32x4_t c = vsubq_f32(a, b); + const float32x4_t error = vdupq_n_f32(VECMATH_AOS_EPSILON); +// absolute compare abs(error) > abs(c) + const uint32x4_t greater = vcagtq_f32(error, c); + return internalUnitNeonSimd::BAllTrue4_R(greater) != 0x0; +} +} + +#if 0 // debugging printfs +#include +PX_FORCE_INLINE void printVec(const float32x4_t& v, const char* name) +{ + PX_ALIGN(16, float32_t) data[4]; + vst1q_f32(data, v); + printf("%s: (%f, %f, %f, %f)\n", name, data[0], data[1], data[2], data[3]); +} + +PX_FORCE_INLINE void printVec(const float32x2_t& v, const char* name) +{ + PX_ALIGN(16, float32_t) data[2]; + vst1_f32(data, v); + printf("%s: (%f, %f)\n", name, data[0], data[1]); +} + +PX_FORCE_INLINE void printVec(const uint32x4_t& v, const char* name) +{ + PX_ALIGN(16, uint32_t) data[4]; + vst1q_u32(data, v); + printf("%s: (0x%x, 0x%x, 0x%x, 0x%x)\n", name, data[0], data[1], data[2], data[3]); +} + +PX_FORCE_INLINE void printVec(const uint16x8_t& v, const char* name) +{ + PX_ALIGN(16, uint16_t) data[8]; + vst1q_u16(data, v); + printf("%s: (0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", name, data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7]); +} + +PX_FORCE_INLINE void printVec(const int32x4_t& v, const char* name) +{ + PX_ALIGN(16, int32_t) data[4]; + vst1q_s32(data, v); + printf("%s: (0x%x, 0x%x, 0x%x, 0x%x)\n", name, data[0], data[1], data[2], data[3]); +} + +PX_FORCE_INLINE void printVec(const int16x8_t& v, const char* name) +{ + PX_ALIGN(16, int16_t) data[8]; + vst1q_s16(data, v); + printf("%s: (0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", name, data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7]); +} + +PX_FORCE_INLINE void printVec(const uint16x4_t& v, const char* name) +{ + PX_ALIGN(16, uint16_t) data[4]; + vst1_u16(data, v); + printf("%s: (0x%x, 0x%x, 0x%x, 0x%x)\n", name, data[0], data[1], data[2], data[3]); +} + +PX_FORCE_INLINE void printVec(const uint32x2_t& v, const char* name) +{ + PX_ALIGN(16, uint32_t) data[2]; + vst1_u32(data, v); + printf("%s: (0x%x, 0x%x)\n", name, data[0], data[1]); +} + +PX_FORCE_INLINE void printVar(const PxU32 v, const char* name) +{ + printf("%s: 0x%x\n", name, v); +} + +PX_FORCE_INLINE void printVar(const PxF32 v, const char* name) +{ + printf("%s: %f\n", name, v); +} + +#define PRINT_VAR(X) printVar((X), #X) +#define PRINT_VEC(X) printVec((X), #X) +#define PRINT_VEC_TITLE(TITLE, X) printVec((X), TITLE #X) +#endif // debugging printf + +///////////////////////////////////////////////////////////////////// +////FUNCTIONS USED ONLY FOR ASSERTS IN VECTORISED IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE bool isFiniteFloatV(const FloatV a) +{ + PX_ALIGN(16, PxF32) data[4]; + vst1_f32(reinterpret_cast(data), a); + return PxIsFinite(data[0]) && PxIsFinite(data[1]); +} + +PX_FORCE_INLINE bool isFiniteVec3V(const Vec3V a) +{ + PX_ALIGN(16, PxF32) data[4]; + vst1q_f32(reinterpret_cast(data), a); + return PxIsFinite(data[0]) && PxIsFinite(data[1]) && PxIsFinite(data[2]); +} + +PX_FORCE_INLINE bool isFiniteVec4V(const Vec4V a) +{ + PX_ALIGN(16, PxF32) data[4]; + vst1q_f32(reinterpret_cast(data), a); + return PxIsFinite(data[0]) && PxIsFinite(data[1]) && PxIsFinite(data[2]) && PxIsFinite(data[3]); +} + +PX_FORCE_INLINE bool hasZeroElementinFloatV(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return vget_lane_u32(vreinterpret_u32_f32(a), 0) == 0; +} + +PX_FORCE_INLINE bool hasZeroElementInVec3V(const Vec3V a) +{ + const uint32x2_t dLow = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t dMin = vpmin_u32(dLow, dLow); + + return vget_lane_u32(dMin, 0) == 0 || vgetq_lane_u32(vreinterpretq_u32_f32(a), 2) == 0; +} + +PX_FORCE_INLINE bool hasZeroElementInVec4V(const Vec4V a) +{ + const uint32x2_t dHigh = vget_high_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t dLow = vget_low_u32(vreinterpretq_u32_f32(a)); + + const uint32x2_t dMin = vmin_u32(dHigh, dLow); + const uint32x2_t pairMin = vpmin_u32(dMin, dMin); + return vget_lane_u32(pairMin, 0) == 0; +} + +///////////////////////////////////////////////////////////////////// +////VECTORISED FUNCTION IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE FloatV FLoad(const PxF32 f) +{ + return vdup_n_f32(reinterpret_cast(f)); +} + +PX_FORCE_INLINE FloatV FLoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(f); + return vld1_f32(reinterpret_cast(f)); +} + +PX_FORCE_INLINE Vec3V V3Load(const PxF32 f) +{ + PX_ALIGN(16, PxF32) data[4] = { f, f, f, 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec4V V4Load(const PxF32 f) +{ + return vdupq_n_f32(reinterpret_cast(f)); +} + +PX_FORCE_INLINE BoolV BLoad(const bool f) +{ + const PxU32 i = static_cast(-(static_cast(f))); + return vdupq_n_u32(i); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + PX_ALIGN(16, PxF32) data[4] = { f.x, f.y, f.z, 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxVec3& f) +{ + PX_ALIGN(16, PxF32) data[4] = { f.x, f.y, f.z, 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec3V V3LoadUnsafeA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + PX_ALIGN(16, PxF32) data[4] = { f.x, f.y, f.z, 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxF32* f) +{ + ASSERT_ISALIGNED16(f); + PX_ALIGN(16, PxF32) data[4] = { f[0], f[1], f[2], 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxF32* f) +{ + PX_ALIGN(16, PxF32) data[4] = { f[0], f[1], f[2], 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V v) +{ + return vsetq_lane_f32(0.0f, v, 3); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V_WUndefined(Vec4V v) +{ + return v; +} + +PX_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f) +{ + return f; // ok if it is implemented as the same type. +} + +PX_FORCE_INLINE Vec4V Vec4V_From_FloatV(FloatV f) +{ + return vcombine_f32(f, f); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV(FloatV f) +{ + return Vec3V_From_Vec4V(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV_WUndefined(FloatV f) +{ + return Vec3V_From_Vec4V_WUndefined(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_PxVec3_WUndefined(const PxVec3& f) +{ + PX_ALIGN(16, PxF32) data[4] = { f.x, f.y, f.z, 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Mat33V Mat33V_From_PxMat33(const PxMat33& m) +{ + return Mat33V(V3LoadU(m.column0), V3LoadU(m.column1), V3LoadU(m.column2)); +} + +PX_FORCE_INLINE void PxMat33_From_Mat33V(const Mat33V& m, PxMat33& out) +{ + V3StoreU(m.col0, out.column0); + V3StoreU(m.col1, out.column1); + V3StoreU(m.col2, out.column2); +} + +PX_FORCE_INLINE Vec4V V4LoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(f); + return vld1q_f32(reinterpret_cast(f)); +} + +PX_FORCE_INLINE void V4StoreA(Vec4V a, PxF32* f) +{ + ASSERT_ISALIGNED16(f); + vst1q_f32(reinterpret_cast(f), a); +} + +PX_FORCE_INLINE void V4StoreU(const Vec4V a, PxF32* f) +{ + PX_ALIGN(16, PxF32) f2[4]; + vst1q_f32(reinterpret_cast(f2), a); + f[0] = f2[0]; + f[1] = f2[1]; + f[2] = f2[2]; + f[3] = f2[3]; +} + +PX_FORCE_INLINE void BStoreA(const BoolV a, PxU32* u) +{ + ASSERT_ISALIGNED16(u); + vst1q_u32(reinterpret_cast(u), a); +} + +PX_FORCE_INLINE void U4StoreA(const VecU32V uv, PxU32* u) +{ + ASSERT_ISALIGNED16(u); + vst1q_u32(reinterpret_cast(u), uv); +} + +PX_FORCE_INLINE void I4StoreA(const VecI32V iv, PxI32* i) +{ + ASSERT_ISALIGNED16(i); + vst1q_s32(reinterpret_cast(i), iv); +} + +PX_FORCE_INLINE Vec4V V4LoadU(const PxF32* const f) +{ + return vld1q_f32(reinterpret_cast(f)); +} + +PX_FORCE_INLINE BoolV BLoad(const bool* const f) +{ + const PX_ALIGN(16, PxU32) b[4] = { static_cast(-static_cast(f[0])), + static_cast(-static_cast(f[1])), + static_cast(-static_cast(f[2])), + static_cast(-static_cast(f[3])) }; + return vld1q_u32(b); +} + +PX_FORCE_INLINE void FStore(const FloatV a, PxF32* PX_RESTRICT f) +{ + ASSERT_ISVALIDFLOATV(a); + // vst1q_lane_f32(f, a, 0); // causes vst1 alignment bug + *f = vget_lane_f32(a, 0); +} + +PX_FORCE_INLINE void Store_From_BoolV(const BoolV a, PxU32* PX_RESTRICT f) +{ + *f = vget_lane_u32(vget_low_u32(a), 0); +} + +PX_FORCE_INLINE void V3StoreA(const Vec3V a, PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + PX_ALIGN(16, PxF32) f2[4]; + vst1q_f32(reinterpret_cast(f2), a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +PX_FORCE_INLINE void V3StoreU(const Vec3V a, PxVec3& f) +{ + PX_ALIGN(16, PxF32) f2[4]; + vst1q_f32(reinterpret_cast(f2), a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +////////////////////////////////// +// FLOATV +////////////////////////////////// + +PX_FORCE_INLINE FloatV FZero() +{ + return FLoad(0.0f); +} + +PX_FORCE_INLINE FloatV FOne() +{ + return FLoad(1.0f); +} + +PX_FORCE_INLINE FloatV FHalf() +{ + return FLoad(0.5f); +} + +PX_FORCE_INLINE FloatV FEps() +{ + return FLoad(PX_EPS_REAL); +} + +PX_FORCE_INLINE FloatV FEps6() +{ + return FLoad(1e-6f); +} + +PX_FORCE_INLINE FloatV FMax() +{ + return FLoad(PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV FNegMax() +{ + return FLoad(-PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV IZero() +{ + return vreinterpret_f32_u32(vdup_n_u32(0)); +} + +PX_FORCE_INLINE FloatV IOne() +{ + return vreinterpret_f32_u32(vdup_n_u32(1)); +} + +PX_FORCE_INLINE FloatV ITwo() +{ + return vreinterpret_f32_u32(vdup_n_u32(2)); +} + +PX_FORCE_INLINE FloatV IThree() +{ + return vreinterpret_f32_u32(vdup_n_u32(3)); +} + +PX_FORCE_INLINE FloatV IFour() +{ + return vreinterpret_f32_u32(vdup_n_u32(4)); +} + +PX_FORCE_INLINE FloatV FNeg(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return vneg_f32(f); +} + +PX_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vadd_f32(a, b); +} + +PX_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vsub_f32(a, b); +} + +PX_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vmul_f32(a, b); +} + +template +PX_FORCE_INLINE float32x2_t recip_newton(const float32x2_t& in) +{ + float32x2_t recip = vrecpe_f32(in); + for(int i = 0; i < n; ++i) + recip = vmul_f32(recip, vrecps_f32(in, recip)); + return recip; +} + +template +PX_FORCE_INLINE float32x4_t recipq_newton(const float32x4_t& in) +{ + float32x4_t recip = vrecpeq_f32(in); + for(int i = 0; i < n; ++i) + recip = vmulq_f32(recip, vrecpsq_f32(recip, in)); + return recip; +} + +template +PX_FORCE_INLINE float32x2_t rsqrt_newton(const float32x2_t& in) +{ + float32x2_t rsqrt = vrsqrte_f32(in); + for(int i = 0; i < n; ++i) + rsqrt = vmul_f32(rsqrt, vrsqrts_f32(vmul_f32(rsqrt, rsqrt), in)); + return rsqrt; +} + +template +PX_FORCE_INLINE float32x4_t rsqrtq_newton(const float32x4_t& in) +{ + float32x4_t rsqrt = vrsqrteq_f32(in); + for(int i = 0; i < n; ++i) + rsqrt = vmulq_f32(rsqrt, vrsqrtsq_f32(vmulq_f32(rsqrt, rsqrt), in)); + return rsqrt; +} + +PX_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vmul_f32(a, VRECIP(b)); +} + +PX_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vmul_f32(a, VRECIPE(b)); +} + +PX_FORCE_INLINE FloatV FRecip(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return VRECIP(a); +} + +PX_FORCE_INLINE FloatV FRecipFast(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return VRECIPE(a); +} + +PX_FORCE_INLINE FloatV FRsqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return VRECIPSQRT(a); +} + +PX_FORCE_INLINE FloatV FSqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return FSel(FIsEq(a, FZero()), a, vmul_f32(a, VRECIPSQRT(a))); +} + +PX_FORCE_INLINE FloatV FRsqrtFast(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return VRECIPSQRTE(a); +} + +PX_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return vmla_f32(c, a, b); +} + +PX_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return vmls_f32(c, a, b); +} + +PX_FORCE_INLINE FloatV FAbs(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return vabs_f32(a); +} + +PX_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b) +{ + PX_ASSERT( _VecMathTests::allElementsEqualBoolV(c, BTTTT()) || + _VecMathTests::allElementsEqualBoolV(c, BFFFF())); + ASSERT_ISVALIDFLOATV(vbsl_f32(vget_low_u32(c), a, b)); + return vbsl_f32(vget_low_u32(c), a, b); +} + +PX_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vdupq_lane_u32(vcgt_f32(a, b), 0); +} + +PX_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vdupq_lane_u32(vcge_f32(a, b), 0); +} + +PX_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vdupq_lane_u32(vceq_f32(a, b), 0); +} + +PX_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b) +{ + //ASSERT_ISVALIDFLOATV(a); + //ASSERT_ISVALIDFLOATV(b); + return vmax_f32(a, b); +} + +PX_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b) +{ + //ASSERT_ISVALIDFLOATV(a); + //ASSERT_ISVALIDFLOATV(b); + return vmin_f32(a, b); +} + +PX_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV) +{ + ASSERT_ISVALIDFLOATV(minV); + ASSERT_ISVALIDFLOATV(maxV); + return vmax_f32(vmin_f32(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 FAllGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vget_lane_u32(vcgt_f32(a, b), 0); +} + +PX_FORCE_INLINE PxU32 FAllGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vget_lane_u32(vcge_f32(a, b), 0); +} + +PX_FORCE_INLINE PxU32 FAllEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vget_lane_u32(vceq_f32(a, b), 0); +} + +PX_FORCE_INLINE FloatV FRound(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // truncate(a + (0.5f - sign(a))) + const float32x2_t half = vdup_n_f32(0.5f); + const float32x2_t sign = vcvt_f32_u32((vshr_n_u32(vreinterpret_u32_f32(a), 31))); + const float32x2_t aPlusHalf = vadd_f32(a, half); + const float32x2_t aRound = vsub_f32(aPlusHalf, sign); + int32x2_t tmp = vcvt_s32_f32(aRound); + return vcvt_f32_s32(tmp); +} + +PX_FORCE_INLINE FloatV FSin(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = FLoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = FLoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V3 = FMul(V2, V1); + const FloatV V5 = FMul(V3, V2); + const FloatV V7 = FMul(V5, V2); + const FloatV V9 = FMul(V7, V2); + const FloatV V11 = FMul(V9, V2); + const FloatV V13 = FMul(V11, V2); + const FloatV V15 = FMul(V13, V2); + const FloatV V17 = FMul(V15, V2); + const FloatV V19 = FMul(V17, V2); + const FloatV V21 = FMul(V19, V2); + const FloatV V23 = FMul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + FloatV Result; + Result = FScaleAdd(S1, V3, V1); + Result = FScaleAdd(S2, V5, Result); + Result = FScaleAdd(S3, V7, Result); + Result = FScaleAdd(S4, V9, Result); + Result = FScaleAdd(S5, V11, Result); + Result = FScaleAdd(S6, V13, Result); + Result = FScaleAdd(S7, V15, Result); + Result = FScaleAdd(S8, V17, Result); + Result = FScaleAdd(S9, V19, Result); + Result = FScaleAdd(S10, V21, Result); + Result = FScaleAdd(S11, V23, Result); + + return Result; +} + +PX_FORCE_INLINE FloatV FCos(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = FLoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = FLoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V4 = FMul(V2, V2); + const FloatV V6 = FMul(V4, V2); + const FloatV V8 = FMul(V4, V4); + const FloatV V10 = FMul(V6, V4); + const FloatV V12 = FMul(V6, V6); + const FloatV V14 = FMul(V8, V6); + const FloatV V16 = FMul(V8, V8); + const FloatV V18 = FMul(V10, V8); + const FloatV V20 = FMul(V10, V10); + const FloatV V22 = FMul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + FloatV Result; + Result = FScaleAdd(C1, V2, FOne()); + Result = FScaleAdd(C2, V4, Result); + Result = FScaleAdd(C3, V6, Result); + Result = FScaleAdd(C4, V8, Result); + Result = FScaleAdd(C5, V10, Result); + Result = FScaleAdd(C6, V12, Result); + Result = FScaleAdd(C7, V14, Result); + Result = FScaleAdd(C8, V16, Result); + Result = FScaleAdd(C9, V18, Result); + Result = FScaleAdd(C10, V20, Result); + Result = FScaleAdd(C11, V22, Result); + + return Result; +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max); + + const BoolV c = BOr(FIsGrtr(a, max), FIsGrtr(min, a)); + return PxU32(!BAllEqFFFF(c)); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max); + + const BoolV c = BAnd(FIsGrtrOrEq(a, min), FIsGrtrOrEq(max, a)); + return PxU32(BAllEqTTTT(c)); +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + const uint32x2_t greater = vcagt_f32(a, bounds); + return vget_lane_u32(greater, 0); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + const uint32x2_t geq = vcage_f32(bounds, a); + return vget_lane_u32(geq, 0); +} + +////////////////////////////////// +// VEC3V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V V3Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t uHigh = vreinterpret_u32_f32(f); + const float32x2_t dHigh = vreinterpret_f32_u32(vand_u32(uHigh, mask)); + + return vcombine_f32(f, dHigh); +} + +PX_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t dHigh = vand_u32(vreinterpret_u32_f32(z), mask); + const uint32x2_t dLow = vext_u32(vreinterpret_u32_f32(x), vreinterpret_u32_f32(y), 1); + return vreinterpretq_f32_u32(vcombine_u32(dLow, dHigh)); +} + +PX_FORCE_INLINE Vec3V V3UnitX() +{ + const float32x4_t x = { 1.0f, 0.0f, 0.0f, 0.0f }; + return x; +} + +PX_FORCE_INLINE Vec3V V3UnitY() +{ + const float32x4_t y = { 0, 1.0f, 0, 0 }; + return y; +} + +PX_FORCE_INLINE Vec3V V3UnitZ() +{ + const float32x4_t z = { 0, 0, 1.0f, 0 }; + return z; +} + +PX_FORCE_INLINE FloatV V3GetX(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + const float32x2_t fLow = vget_low_f32(f); + return vdup_lane_f32(fLow, 0); +} + +PX_FORCE_INLINE FloatV V3GetY(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + const float32x2_t fLow = vget_low_f32(f); + return vdup_lane_f32(fLow, 1); +} + +PX_FORCE_INLINE FloatV V3GetZ(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + const float32x2_t fhigh = vget_high_f32(f); + return vdup_lane_f32(fhigh, 0); +} + +PX_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + + const float32x2_t aLow = vget_low_f32(a); + const float32x2_t bLow = vget_low_f32(b); + const float32x2_t cLow = vget_low_f32(c); + const float32x2_t zero = vdup_n_f32(0.0f); + + const float32x2x2_t zipL = vzip_f32(aLow, bLow); + const float32x2x2_t zipH = vzip_f32(cLow, zero); + + return vcombine_f32(zipL.val[0], zipH.val[0]); +} + +PX_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + + const float32x2_t aLow = vget_low_f32(a); + const float32x2_t bLow = vget_low_f32(b); + const float32x2_t cLow = vget_low_f32(c); + const float32x2_t zero = vdup_n_f32(0.0f); + + const float32x2x2_t zipL = vzip_f32(aLow, bLow); + const float32x2x2_t zipH = vzip_f32(cLow, zero); + + return vcombine_f32(zipL.val[1], zipH.val[1]); +} + +PX_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + + const float32x2_t aHi = vget_high_f32(a); + const float32x2_t bHi = vget_high_f32(b); + const float32x2_t cHi = vget_high_f32(c); + + const float32x2x2_t zipL = vzip_f32(aHi, bHi); + + return vcombine_f32(zipL.val[0], cHi); +} + +PX_FORCE_INLINE Vec3V V3Zero() +{ + return vdupq_n_f32(0.0f); +} + +PX_FORCE_INLINE Vec3V V3Eps() +{ + return V3Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec3V V3One() +{ + return V3Load(1.0f); +} + +PX_FORCE_INLINE Vec3V V3Neg(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + const float32x4_t tmp = vnegq_f32(f); + return vsetq_lane_f32(0.0f, tmp, 3); +} + +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vaddq_f32(a, b); +} + +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return vaddq_f32(a, Vec3V_From_FloatV(b)); +} + +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vsubq_f32(a, b); +} + +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return vsubq_f32(a, Vec3V_From_FloatV(b)); +} + +PX_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + const float32x4_t tmp = vmulq_lane_f32(a, b, 0); + return vsetq_lane_f32(0.0f, tmp, 3); +} + +PX_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vmulq_f32(a, b); +} + +PX_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + const float32x2_t invB = VRECIP(b); + const float32x4_t tmp = vmulq_lane_f32(a, invB, 0); + return vsetq_lane_f32(0.0f, tmp, 3); +} + +PX_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + float32x4_t invB = VRECIPQ(b); + invB = vsetq_lane_f32(0.0f, invB, 3); + return vmulq_f32(a, invB); +} + +PX_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + const float32x2_t invB = VRECIPE(b); + const float32x4_t tmp = vmulq_lane_f32(a, invB, 0); + return vsetq_lane_f32(0.0f, tmp, 3); +} + +PX_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + float32x4_t invB = VRECIPEQ(b); + invB = vsetq_lane_f32(0.0f, invB, 3); + return vmulq_f32(a, invB); +} + +PX_FORCE_INLINE Vec3V V3Recip(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const float32x4_t recipA = VRECIPQ(a); + return vsetq_lane_f32(0.0f, recipA, 3); +} + +PX_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const float32x4_t recipA = VRECIPEQ(a); + return vsetq_lane_f32(0.0f, recipA, 3); +} + +PX_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const float32x4_t rSqrA = VRECIPSQRTQ(a); + return vsetq_lane_f32(0.0f, rSqrA, 3); +} + +PX_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const float32x4_t rSqrA = VRECIPSQRTEQ(a); + return vsetq_lane_f32(0.0f, rSqrA, 3); +} + +PX_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + + float32x4_t tmp = vmlaq_lane_f32(c, a, b, 0); + // using vsetq_lane_f32 resulted in failures, + // probably related to a compiler bug on + // ndk r9d-win32, gcc 4.8, cardhu/shield + + // code with issue + // return vsetq_lane_f32(0.0f, tmp, 3); + + // workaround + float32x2_t w_z = vget_high_f32(tmp); + float32x2_t y_x = vget_low_f32(tmp); + w_z = vset_lane_f32(0.0f, w_z, 1); + return vcombine_f32(y_x, w_z); +} + +PX_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + + float32x4_t tmp = vmlsq_lane_f32(c, a, b, 0); + // using vsetq_lane_f32 resulted in failures, + // probably related to a compiler bug on + // ndk r9d-win32, gcc 4.8, cardhu/shield + + // code with issue + // return vsetq_lane_f32(0.0f, tmp, 3); + + // workaround + float32x2_t w_z = vget_high_f32(tmp); + float32x2_t y_x = vget_low_f32(tmp); + w_z = vset_lane_f32(0.0f, w_z, 1); + return vcombine_f32(y_x, w_z); +} + +PX_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return vmlaq_f32(c, a, b); +} + +PX_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return vmlsq_f32(c, a, b); +} + +PX_FORCE_INLINE Vec3V V3Abs(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return vabsq_f32(a); +} + +PX_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + + // const uint32x2_t mask = {0xffffFFFF, 0x0}; + const float32x4_t tmp = vmulq_f32(a, b); + + const float32x2_t low = vget_low_f32(tmp); + const float32x2_t high = vget_high_f32(tmp); + // const float32x2_t high = vreinterpret_f32_u32(vand_u32(vreinterpret_u32_f32(high_), mask)); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {0+z, x+y} + const float32x2_t sum0ZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z, x+y+z} + + return sum0ZYX; +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + + const uint32x2_t TF = { 0xffffFFFF, 0x0 }; + const float32x2_t ay_ax = vget_low_f32(a); // d2 + const float32x2_t aw_az = vget_high_f32(a); // d3 + const float32x2_t by_bx = vget_low_f32(b); // d4 + const float32x2_t bw_bz = vget_high_f32(b); // d5 + // Hi, Lo + const float32x2_t bz_by = vext_f32(by_bx, bw_bz, 1); // bz, by + const float32x2_t az_ay = vext_f32(ay_ax, aw_az, 1); // az, ay + + const float32x2_t azbx = vmul_f32(aw_az, by_bx); // 0, az*bx + const float32x2_t aybz_axby = vmul_f32(ay_ax, bz_by); // ay*bz, ax*by + + const float32x2_t azbxSUBaxbz = vmls_f32(azbx, bw_bz, ay_ax); // 0, az*bx-ax*bz + const float32x2_t aybzSUBazby_axbySUBaybx = vmls_f32(aybz_axby, by_bx, az_ay); // ay*bz-az*by, ax*by-ay*bx + + const float32x2_t retLow = vext_f32(aybzSUBazby_axbySUBaybx, azbxSUBaxbz, 1); // az*bx-ax*bz, ay*bz-az*by + const uint32x2_t retHigh = vand_u32(TF, vreinterpret_u32_f32(aybzSUBazby_axbySUBaybx)); // 0, ax*by-ay*bx + + return vcombine_f32(retLow, vreinterpret_f32_u32(retHigh)); +} + +PX_FORCE_INLINE VecCrossV V3PrepareCross(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return a; +} + +PX_FORCE_INLINE FloatV V3Length(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // const uint32x2_t mask = {0xffffFFFF, 0x0}; + + const float32x4_t tmp = vmulq_f32(a, a); + const float32x2_t low = vget_low_f32(tmp); + const float32x2_t high = vget_high_f32(tmp); + // const float32x2_t high = vreinterpret_f32_u32(vand_u32(vreinterpret_u32_f32(high_), mask)); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {0+z, x+y} + const float32x2_t sum0ZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z, x+y+z} + + return FSqrt(sum0ZYX); +} + +PX_FORCE_INLINE FloatV V3LengthSq(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return V3Dot(a, a); +} + +PX_FORCE_INLINE Vec3V V3Normalize(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + //PX_ASSERT(!FAllEq(V4LengthSq(a), FZero())); + return V3ScaleInv(a, V3Length(a)); +} + +PX_FORCE_INLINE Vec3V V3NormalizeFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + //PX_ASSERT(!FAllEq(V4LengthSq(a), FZero())); + return V3Scale(a, VRECIPSQRTE(V3Dot(a, a))); +} + +PX_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a, const Vec3V unsafeReturnValue) +{ + ASSERT_ISVALIDVEC3V(a); + const FloatV zero = vdup_n_f32(0.0f); + const FloatV length = V3Length(a); + const uint32x4_t isGreaterThanZero = FIsGrtr(length, zero); + return V3Sel(isGreaterThanZero, V3ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V( vbslq_f32(c, a, b)); + return vbslq_f32(c, a, b); +} + +PX_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vcgtq_f32(a, b); +} + +PX_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vcgeq_f32(a, b); +} + +PX_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vceqq_f32(a, b); +} + +PX_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vmaxq_f32(a, b); +} + +PX_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vminq_f32(a, b); +} + +PX_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + + const float32x2_t zz = vdup_lane_f32(high, 0); + const float32x2_t max0 = vpmax_f32(zz, low); + const float32x2_t max1 = vpmax_f32(max0, max0); + + return max1; +} + +PX_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + + const float32x2_t zz = vdup_lane_f32(high, 0); + const float32x2_t min0 = vpmin_f32(zz, low); + const float32x2_t min1 = vpmin_f32(min0, min0); + + return min1; +} + +// return (a >= 0.0f) ? 1.0f : -1.0f; +PX_FORCE_INLINE Vec3V V3Sign(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const Vec3V zero = V3Zero(); + const Vec3V one = V3One(); + const Vec3V none = V3Neg(one); + return V3Sel(V3IsGrtrOrEq(a, zero), one, none); +} + +PX_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV) +{ + ASSERT_ISVALIDVEC3V(minV); + ASSERT_ISVALIDVEC3V(maxV); + return V3Max(V3Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V3AllGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitNeonSimd::BAllTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitNeonSimd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitNeonSimd::BAllTrue3_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE Vec3V V3Round(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + // truncate(a + (0.5f - sign(a))) + const Vec3V half = V3Load(0.5f); + const float32x4_t sign = vcvtq_f32_u32((vshrq_n_u32(vreinterpretq_u32_f32(a), 31))); + const Vec3V aPlusHalf = V3Add(a, half); + const Vec3V aRound = V3Sub(aPlusHalf, sign); + return vcvtq_f32_s32(vcvtq_s32_f32(aRound)); +} + +PX_FORCE_INLINE Vec3V V3Sin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V4Mul(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V4NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V3 = V3Mul(V2, V1); + const Vec3V V5 = V3Mul(V3, V2); + const Vec3V V7 = V3Mul(V5, V2); + const Vec3V V9 = V3Mul(V7, V2); + const Vec3V V11 = V3Mul(V9, V2); + const Vec3V V13 = V3Mul(V11, V2); + const Vec3V V15 = V3Mul(V13, V2); + const Vec3V V17 = V3Mul(V15, V2); + const Vec3V V19 = V3Mul(V17, V2); + const Vec3V V21 = V3Mul(V19, V2); + const Vec3V V23 = V3Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec3V Result; + Result = V4ScaleAdd(V3, S1, V1); + Result = V4ScaleAdd(V5, S2, Result); + Result = V4ScaleAdd(V7, S3, Result); + Result = V4ScaleAdd(V9, S4, Result); + Result = V4ScaleAdd(V11, S5, Result); + Result = V4ScaleAdd(V13, S6, Result); + Result = V4ScaleAdd(V15, S7, Result); + Result = V4ScaleAdd(V17, S8, Result); + Result = V4ScaleAdd(V19, S9, Result); + Result = V4ScaleAdd(V21, S10, Result); + Result = V4ScaleAdd(V23, S11, Result); + + return Result; +} + +PX_FORCE_INLINE Vec3V V3Cos(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V4Mul(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V4NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V4 = V3Mul(V2, V2); + const Vec3V V6 = V3Mul(V4, V2); + const Vec3V V8 = V3Mul(V4, V4); + const Vec3V V10 = V3Mul(V6, V4); + const Vec3V V12 = V3Mul(V6, V6); + const Vec3V V14 = V3Mul(V8, V6); + const Vec3V V16 = V3Mul(V8, V8); + const Vec3V V18 = V3Mul(V10, V8); + const Vec3V V20 = V3Mul(V10, V10); + const Vec3V V22 = V3Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec3V Result; + Result = V4ScaleAdd(V2, C1, V4One()); + Result = V4ScaleAdd(V4, C2, Result); + Result = V4ScaleAdd(V6, C3, Result); + Result = V4ScaleAdd(V8, C4, Result); + Result = V4ScaleAdd(V10, C5, Result); + Result = V4ScaleAdd(V12, C6, Result); + Result = V4ScaleAdd(V14, C7, Result); + Result = V4ScaleAdd(V16, C8, Result); + Result = V4ScaleAdd(V18, C9, Result); + Result = V4ScaleAdd(V20, C10, Result); + Result = V4ScaleAdd(V22, C11, Result); + + return V4ClearW(Result); +} + +PX_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const float32x2_t xy = vget_low_f32(a); + const float32x2_t zw = vget_high_f32(a); + const float32x2_t yz = vext_f32(xy, zw, 1); + return vcombine_f32(yz, zw); +} + +PX_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t xw = vand_u32(xy, mask); + return vreinterpretq_f32_u32(vcombine_u32(xy, xw)); +} + +PX_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t yz = vext_u32(xy, zw, 1); + const uint32x2_t xw = vand_u32(xy, mask); + return vreinterpretq_f32_u32(vcombine_u32(yz, xw)); +} + +PX_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t wz = vrev64_u32(zw); + + const uint32x2_t zx = vext_u32(wz, xy, 1); + const uint32x2_t yw = vext_u32(xy, wz, 1); + + return vreinterpretq_f32_u32(vcombine_u32(zx, yw)); +} + +PX_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(a)); + + const uint32x2_t wz = vrev64_u32(zw); + const uint32x2_t yw = vext_u32(xy, wz, 1); + const uint32x2_t zz = vdup_lane_u32(wz, 1); + + return vreinterpretq_f32_u32(vcombine_u32(zz, yw)); +} + +PX_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t yx = vrev64_u32(xy); + const uint32x2_t xw = vand_u32(xy, mask); + return vreinterpretq_f32_u32(vcombine_u32(yx, xw)); +} + +PX_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(v0)); + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(v1)); + const uint32x2_t wz = vrev64_u32(zw); + const uint32x2_t yw = vext_u32(xy, wz, 1); + + return vreinterpretq_f32_u32(vcombine_u32(wz, yw)); +} + +PX_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(v0)); + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(v1)); + const uint32x2_t xw = vand_u32(xy, mask); + + return vreinterpretq_f32_u32(vcombine_u32(zw, xw)); +} + +PX_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + + const uint32x2_t axy = vget_low_u32(vreinterpretq_u32_f32(v0)); + const uint32x2_t bxy = vget_low_u32(vreinterpretq_u32_f32(v1)); + const uint32x2_t byax = vext_u32(bxy, axy, 1); + const uint32x2_t ww = vdup_n_u32(0); + + return vreinterpretq_f32_u32(vcombine_u32(byax, ww)); +} + +PX_FORCE_INLINE FloatV V3SumElems(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // const uint32x2_t mask = {0xffffFFFF, 0x0}; + + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + // const float32x2_t high = vreinterpret_f32_u32(vand_u32(vreinterpret_u32_f32(high_), mask)); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {0+z, x+y} + const float32x2_t sum0ZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z, x+y+z} + + return sum0ZYX; +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + + const BoolV c = BOr(V3IsGrtr(a, max), V3IsGrtr(min, a)); + return internalUnitNeonSimd::BAnyTrue3_R(c); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + + const BoolV c = BAnd(V3IsGrtrOrEq(a, min), V3IsGrtrOrEq(max, a)); + return internalUnitNeonSimd::BAllTrue4_R(c); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds); + + const BoolV greater = V3IsGrtr(V3Abs(a), bounds); + return internalUnitNeonSimd::BAnyTrue3_R(greater); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds); + + const BoolV greaterOrEq = V3IsGrtrOrEq(bounds, V3Abs(a)); + return internalUnitNeonSimd::BAllTrue4_R(greaterOrEq); +} + +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2) +{ + ASSERT_ISVALIDVEC3V(col0); + ASSERT_ISVALIDVEC3V(col1); + ASSERT_ISVALIDVEC3V(col2); + + Vec3V col3 = V3Zero(); + const float32x4x2_t v0v1 = vzipq_f32(col0, col2); + const float32x4x2_t v2v3 = vzipq_f32(col1, col3); + const float32x4x2_t zip0 = vzipq_f32(v0v1.val[0], v2v3.val[0]); + const float32x4x2_t zip1 = vzipq_f32(v0v1.val[1], v2v3.val[1]); + col0 = zip0.val[0]; + col1 = zip0.val[1]; + col2 = zip1.val[0]; + // col3 = zip1.val[1]; +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V V4Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return vcombine_f32(f, f); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatV* const floatVArray) +{ + ASSERT_ISVALIDFLOATV(floatVArray[0]); + ASSERT_ISVALIDFLOATV(floatVArray[1]); + ASSERT_ISVALIDFLOATV(floatVArray[2]); + ASSERT_ISVALIDFLOATV(floatVArray[3]); + + const uint32x2_t xLow = vreinterpret_u32_f32(floatVArray[0]); + const uint32x2_t yLow = vreinterpret_u32_f32(floatVArray[1]); + const uint32x2_t zLow = vreinterpret_u32_f32(floatVArray[2]); + const uint32x2_t wLow = vreinterpret_u32_f32(floatVArray[3]); + + const uint32x2_t dLow = vext_u32(xLow, yLow, 1); + const uint32x2_t dHigh = vext_u32(zLow, wLow, 1); + + return vreinterpretq_f32_u32(vcombine_u32(dLow, dHigh)); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + ASSERT_ISVALIDFLOATV(w); + + const uint32x2_t xLow = vreinterpret_u32_f32(x); + const uint32x2_t yLow = vreinterpret_u32_f32(y); + const uint32x2_t zLow = vreinterpret_u32_f32(z); + const uint32x2_t wLow = vreinterpret_u32_f32(w); + + const uint32x2_t dLow = vext_u32(xLow, yLow, 1); + const uint32x2_t dHigh = vext_u32(zLow, wLow, 1); + + return vreinterpretq_f32_u32(vcombine_u32(dLow, dHigh)); +} + +PX_FORCE_INLINE Vec4V V4MergeW(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const float32x2_t xx = vget_high_f32(x); + const float32x2_t yy = vget_high_f32(y); + const float32x2_t zz = vget_high_f32(z); + const float32x2_t ww = vget_high_f32(w); + + const float32x2x2_t zipL = vzip_f32(xx, yy); + const float32x2x2_t zipH = vzip_f32(zz, ww); + + return vcombine_f32(zipL.val[1], zipH.val[1]); +} + +PX_FORCE_INLINE Vec4V V4MergeZ(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const float32x2_t xx = vget_high_f32(x); + const float32x2_t yy = vget_high_f32(y); + const float32x2_t zz = vget_high_f32(z); + const float32x2_t ww = vget_high_f32(w); + + const float32x2x2_t zipL = vzip_f32(xx, yy); + const float32x2x2_t zipH = vzip_f32(zz, ww); + + return vcombine_f32(zipL.val[0], zipH.val[0]); +} + +PX_FORCE_INLINE Vec4V V4MergeY(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const float32x2_t xx = vget_low_f32(x); + const float32x2_t yy = vget_low_f32(y); + const float32x2_t zz = vget_low_f32(z); + const float32x2_t ww = vget_low_f32(w); + + const float32x2x2_t zipL = vzip_f32(xx, yy); + const float32x2x2_t zipH = vzip_f32(zz, ww); + + return vcombine_f32(zipL.val[1], zipH.val[1]); +} + +PX_FORCE_INLINE Vec4V V4MergeX(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const float32x2_t xx = vget_low_f32(x); + const float32x2_t yy = vget_low_f32(y); + const float32x2_t zz = vget_low_f32(z); + const float32x2_t ww = vget_low_f32(w); + + const float32x2x2_t zipL = vzip_f32(xx, yy); + const float32x2x2_t zipH = vzip_f32(zz, ww); + + return vcombine_f32(zipL.val[0], zipH.val[0]); +} + +PX_FORCE_INLINE Vec4V V4UnpackXY(const Vec4VArg a, const Vec4VArg b) +{ + return vzipq_f32(a, b).val[0]; +} + +PX_FORCE_INLINE Vec4V V4UnpackZW(const Vec4VArg a, const Vec4VArg b) +{ + return vzipq_f32(a, b).val[1]; +} + +PX_FORCE_INLINE Vec4V V4UnitW() +{ + const float32x2_t zeros = vreinterpret_f32_u32(vmov_n_u32(0)); + const float32x2_t ones = vmov_n_f32(1.0f); + const float32x2_t zo = vext_f32(zeros, ones, 1); + return vcombine_f32(zeros, zo); +} + +PX_FORCE_INLINE Vec4V V4UnitX() +{ + const float32x2_t zeros = vreinterpret_f32_u32(vmov_n_u32(0)); + const float32x2_t ones = vmov_n_f32(1.0f); + const float32x2_t oz = vext_f32(ones, zeros, 1); + return vcombine_f32(oz, zeros); +} + +PX_FORCE_INLINE Vec4V V4UnitY() +{ + const float32x2_t zeros = vreinterpret_f32_u32(vmov_n_u32(0)); + const float32x2_t ones = vmov_n_f32(1.0f); + const float32x2_t zo = vext_f32(zeros, ones, 1); + return vcombine_f32(zo, zeros); +} + +PX_FORCE_INLINE Vec4V V4UnitZ() +{ + const float32x2_t zeros = vreinterpret_f32_u32(vmov_n_u32(0)); + const float32x2_t ones = vmov_n_f32(1.0f); + const float32x2_t oz = vext_f32(ones, zeros, 1); + return vcombine_f32(zeros, oz); +} + +PX_FORCE_INLINE FloatV V4GetW(const Vec4V f) +{ + const float32x2_t fhigh = vget_high_f32(f); + return vdup_lane_f32(fhigh, 1); +} + +PX_FORCE_INLINE FloatV V4GetX(const Vec4V f) +{ + const float32x2_t fLow = vget_low_f32(f); + return vdup_lane_f32(fLow, 0); +} + +PX_FORCE_INLINE FloatV V4GetY(const Vec4V f) +{ + const float32x2_t fLow = vget_low_f32(f); + return vdup_lane_f32(fLow, 1); +} + +PX_FORCE_INLINE FloatV V4GetZ(const Vec4V f) +{ + const float32x2_t fhigh = vget_high_f32(f); + return vdup_lane_f32(fhigh, 0); +} + +PX_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTTF(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec4V V4ClearW(const Vec4V v) +{ + return V4Sel(BTTTF(), v, V4Zero()); +} + +PX_FORCE_INLINE Vec4V V4PermYXWZ(const Vec4V a) +{ + const float32x2_t xy = vget_low_f32(a); + const float32x2_t zw = vget_high_f32(a); + const float32x2_t yx = vext_f32(xy, xy, 1); + const float32x2_t wz = vext_f32(zw, zw, 1); + return vcombine_f32(yx, wz); +} + +PX_FORCE_INLINE Vec4V V4PermXZXZ(const Vec4V a) +{ + const float32x2_t xy = vget_low_f32(a); + const float32x2_t zw = vget_high_f32(a); + const float32x2x2_t xzyw = vzip_f32(xy, zw); + return vcombine_f32(xzyw.val[0], xzyw.val[0]); +} + +PX_FORCE_INLINE Vec4V V4PermYWYW(const Vec4V a) +{ + const float32x2_t xy = vget_low_f32(a); + const float32x2_t zw = vget_high_f32(a); + const float32x2x2_t xzyw = vzip_f32(xy, zw); + return vcombine_f32(xzyw.val[1], xzyw.val[1]); +} + +PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V a) +{ + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t yz = vext_u32(xy, zw, 1); + const uint32x2_t xw = vrev64_u32(vext_u32(zw, xy, 1)); + return vreinterpretq_f32_u32(vcombine_u32(yz, xw)); +} + +PX_FORCE_INLINE Vec4V V4PermZWXY(const Vec4V a) +{ + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + return vcombine_f32(high, low); +} + +template +PX_FORCE_INLINE Vec4V V4Perm(const Vec4V V) +{ + static const uint32_t ControlElement[4] = + { +#if 1 + 0x03020100, // XM_SWIZZLE_X + 0x07060504, // XM_SWIZZLE_Y + 0x0B0A0908, // XM_SWIZZLE_Z + 0x0F0E0D0C, // XM_SWIZZLE_W +#else + 0x00010203, // XM_SWIZZLE_X + 0x04050607, // XM_SWIZZLE_Y + 0x08090A0B, // XM_SWIZZLE_Z + 0x0C0D0E0F, // XM_SWIZZLE_W +#endif + }; + + uint8x8x2_t tbl; + tbl.val[0] = vreinterpret_u8_f32(vget_low_f32(V)); + tbl.val[1] = vreinterpret_u8_f32(vget_high_f32(V)); + + uint8x8_t idx = + vcreate_u8(static_cast(ControlElement[E0]) | (static_cast(ControlElement[E1]) << 32)); + const uint8x8_t rL = vtbl2_u8(tbl, idx); + idx = vcreate_u8(static_cast(ControlElement[E2]) | (static_cast(ControlElement[E3]) << 32)); + const uint8x8_t rH = vtbl2_u8(tbl, idx); + return vreinterpretq_f32_u8(vcombine_u8(rL, rH)); +} + +// PT: this seems measurably slower than the hardcoded version +/*PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V a) +{ + return V4Perm<1, 2, 0, 3>(a); +}*/ + +PX_FORCE_INLINE Vec4V V4Zero() +{ + return vreinterpretq_f32_u32(vmovq_n_u32(0)); + // return vmovq_n_f32(0.0f); +} + +PX_FORCE_INLINE Vec4V V4One() +{ + return vmovq_n_f32(1.0f); +} + +PX_FORCE_INLINE Vec4V V4Eps() +{ + // return vmovq_n_f32(PX_EPS_REAL); + return V4Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec4V V4Neg(const Vec4V f) +{ + return vnegq_f32(f); +} + +PX_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b) +{ + return vaddq_f32(a, b); +} + +PX_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b) +{ + return vsubq_f32(a, b); +} + +PX_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b) +{ + return vmulq_lane_f32(a, b, 0); +} + +PX_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b) +{ + return vmulq_f32(a, b); +} + +PX_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + const float32x2_t invB = VRECIP(b); + return vmulq_lane_f32(a, invB, 0); +} + +PX_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b) +{ + const float32x4_t invB = VRECIPQ(b); + return vmulq_f32(a, invB); +} + +PX_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + const float32x2_t invB = VRECIPE(b); + return vmulq_lane_f32(a, invB, 0); +} + +PX_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b) +{ + const float32x4_t invB = VRECIPEQ(b); + return vmulq_f32(a, invB); +} + +PX_FORCE_INLINE Vec4V V4Recip(const Vec4V a) +{ + return VRECIPQ(a); +} + +PX_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a) +{ + return VRECIPEQ(a); +} + +PX_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a) +{ + return VRECIPSQRTQ(a); +} + +PX_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a) +{ + return VRECIPSQRTEQ(a); +} + +PX_FORCE_INLINE Vec4V V4Sqrt(const Vec4V a) +{ + return V4Sel(V4IsEq(a, V4Zero()), a, V4Mul(a, VRECIPSQRTQ(a))); +} + +PX_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return vmlaq_lane_f32(c, a, b, 0); +} + +PX_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return vmlsq_lane_f32(c, a, b, 0); +} + +PX_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return vmlaq_f32(c, a, b); +} + +PX_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return vmlsq_f32(c, a, b); +} + +PX_FORCE_INLINE Vec4V V4Abs(const Vec4V a) +{ + return vabsq_f32(a); +} + +PX_FORCE_INLINE FloatV V4SumElements(const Vec4V a) +{ + const Vec4V xy = V4UnpackXY(a, a); // x,x,y,y + const Vec4V zw = V4UnpackZW(a, a); // z,z,w,w + const Vec4V xz_yw = V4Add(xy, zw); // x+z,x+z,y+w,y+w + const FloatV xz = V4GetX(xz_yw); // x+z + const FloatV yw = V4GetZ(xz_yw); // y+w + return FAdd(xz, yw); // sum +} + +PX_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b) +{ + const float32x4_t tmp = vmulq_f32(a, b); + const float32x2_t low = vget_low_f32(tmp); + const float32x2_t high = vget_high_f32(tmp); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {z+w, x+y} + const float32x2_t sumWZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z+w, x+y+z+w} + return sumWZYX; +} + +PX_FORCE_INLINE FloatV V4Dot3(const Vec4V aa, const Vec4V bb) +{ + // PT: the V3Dot code relies on the fact that W=0 so we can't reuse it as-is, we need to clear W first. + // TODO: find a better implementation that does not need to clear W. + const Vec4V a = V4ClearW(aa); + const Vec4V b = V4ClearW(bb); + + const float32x4_t tmp = vmulq_f32(a, b); + const float32x2_t low = vget_low_f32(tmp); + const float32x2_t high = vget_high_f32(tmp); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {0+z, x+y} + const float32x2_t sum0ZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z, x+y+z} + return sum0ZYX; +} + +PX_FORCE_INLINE Vec4V V4Cross(const Vec4V a, const Vec4V b) +{ + const uint32x2_t TF = { 0xffffFFFF, 0x0 }; + const float32x2_t ay_ax = vget_low_f32(a); // d2 + const float32x2_t aw_az = vget_high_f32(a); // d3 + const float32x2_t by_bx = vget_low_f32(b); // d4 + const float32x2_t bw_bz = vget_high_f32(b); // d5 + // Hi, Lo + const float32x2_t bz_by = vext_f32(by_bx, bw_bz, 1); // bz, by + const float32x2_t az_ay = vext_f32(ay_ax, aw_az, 1); // az, ay + + const float32x2_t azbx = vmul_f32(aw_az, by_bx); // 0, az*bx + const float32x2_t aybz_axby = vmul_f32(ay_ax, bz_by); // ay*bz, ax*by + + const float32x2_t azbxSUBaxbz = vmls_f32(azbx, bw_bz, ay_ax); // 0, az*bx-ax*bz + const float32x2_t aybzSUBazby_axbySUBaybx = vmls_f32(aybz_axby, by_bx, az_ay); // ay*bz-az*by, ax*by-ay*bx + + const float32x2_t retLow = vext_f32(aybzSUBazby_axbySUBaybx, azbxSUBaxbz, 1); // az*bx-ax*bz, ay*bz-az*by + const uint32x2_t retHigh = vand_u32(TF, vreinterpret_u32_f32(aybzSUBazby_axbySUBaybx)); // 0, ax*by-ay*bx + + return vcombine_f32(retLow, vreinterpret_f32_u32(retHigh)); +} + +PX_FORCE_INLINE FloatV V4Length(const Vec4V a) +{ + const float32x4_t tmp = vmulq_f32(a, a); + const float32x2_t low = vget_low_f32(tmp); + const float32x2_t high = vget_high_f32(tmp); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {0+z, x+y} + const float32x2_t sumWZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z, x+y+z} + return FSqrt(sumWZYX); +} + +PX_FORCE_INLINE FloatV V4LengthSq(const Vec4V a) +{ + return V4Dot(a, a); +} + +PX_FORCE_INLINE Vec4V V4Normalize(const Vec4V a) +{ + //PX_ASSERT(!FAllEq(V4LengthSq(a), FZero())); + return V4ScaleInv(a, V4Length(a)); +} + +PX_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a) +{ + //PX_ASSERT(!FAllEq(V4LengthSq(a), FZero())); + return V4Scale(a, FRsqrtFast(V4Dot(a, a))); +} + +PX_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a, const Vec4V unsafeReturnValue) +{ + const FloatV zero = FZero(); + const FloatV length = V4Length(a); + const uint32x4_t isGreaterThanZero = FIsGrtr(length, zero); + return V4Sel(isGreaterThanZero, V4ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b) +{ + return vceqq_u32(a, b); +} + +PX_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b) +{ + return vbslq_f32(c, a, b); +} + +PX_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b) +{ + return vcgtq_f32(a, b); +} + +PX_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return vcgeq_f32(a, b); +} + +PX_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b) +{ + return vceqq_f32(a, b); +} + +PX_FORCE_INLINE Vec4V V4Max(const Vec4V a, const Vec4V b) +{ + return vmaxq_f32(a, b); +} + +PX_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b) +{ + return vminq_f32(a, b); +} + +PX_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a) +{ + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + + const float32x2_t max0 = vpmax_f32(high, low); + const float32x2_t max1 = vpmax_f32(max0, max0); + + return max1; +} + +PX_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a) +{ + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + + const float32x2_t min0 = vpmin_f32(high, low); + const float32x2_t min1 = vpmin_f32(min0, min0); + + return min1; +} + +PX_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV) +{ + return V4Max(V4Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V4AllGrtr(const Vec4V a, const Vec4V b) +{ + return internalUnitNeonSimd::BAllTrue4_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return internalUnitNeonSimd::BAllTrue4_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq3(const Vec4V a, const Vec4V b) +{ + return internalUnitNeonSimd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllEq(const Vec4V a, const Vec4V b) +{ + return internalUnitNeonSimd::BAllTrue4_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AnyGrtr3(const Vec4V a, const Vec4V b) +{ + return internalUnitNeonSimd::BAnyTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE Vec4V V4Round(const Vec4V a) +{ + // truncate(a + (0.5f - sign(a))) + const Vec4V half = V4Load(0.5f); + const float32x4_t sign = vcvtq_f32_u32((vshrq_n_u32(vreinterpretq_u32_f32(a), 31))); + const Vec4V aPlusHalf = V4Add(a, half); + const Vec4V aRound = V4Sub(aPlusHalf, sign); + return vcvtq_f32_s32(vcvtq_s32_f32(aRound)); +} + +PX_FORCE_INLINE Vec4V V4Sin(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V3 = V4Mul(V2, V1); + const Vec4V V5 = V4Mul(V3, V2); + const Vec4V V7 = V4Mul(V5, V2); + const Vec4V V9 = V4Mul(V7, V2); + const Vec4V V11 = V4Mul(V9, V2); + const Vec4V V13 = V4Mul(V11, V2); + const Vec4V V15 = V4Mul(V13, V2); + const Vec4V V17 = V4Mul(V15, V2); + const Vec4V V19 = V4Mul(V17, V2); + const Vec4V V21 = V4Mul(V19, V2); + const Vec4V V23 = V4Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec4V Result; + Result = V4ScaleAdd(V3, S1, V1); + Result = V4ScaleAdd(V5, S2, Result); + Result = V4ScaleAdd(V7, S3, Result); + Result = V4ScaleAdd(V9, S4, Result); + Result = V4ScaleAdd(V11, S5, Result); + Result = V4ScaleAdd(V13, S6, Result); + Result = V4ScaleAdd(V15, S7, Result); + Result = V4ScaleAdd(V17, S8, Result); + Result = V4ScaleAdd(V19, S9, Result); + Result = V4ScaleAdd(V21, S10, Result); + Result = V4ScaleAdd(V23, S11, Result); + + return Result; +} + +PX_FORCE_INLINE Vec4V V4Cos(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V4 = V4Mul(V2, V2); + const Vec4V V6 = V4Mul(V4, V2); + const Vec4V V8 = V4Mul(V4, V4); + const Vec4V V10 = V4Mul(V6, V4); + const Vec4V V12 = V4Mul(V6, V6); + const Vec4V V14 = V4Mul(V8, V6); + const Vec4V V16 = V4Mul(V8, V8); + const Vec4V V18 = V4Mul(V10, V8); + const Vec4V V20 = V4Mul(V10, V10); + const Vec4V V22 = V4Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec4V Result; + Result = V4ScaleAdd(V2, C1, V4One()); + Result = V4ScaleAdd(V4, C2, Result); + Result = V4ScaleAdd(V6, C3, Result); + Result = V4ScaleAdd(V8, C4, Result); + Result = V4ScaleAdd(V10, C5, Result); + Result = V4ScaleAdd(V12, C6, Result); + Result = V4ScaleAdd(V14, C7, Result); + Result = V4ScaleAdd(V16, C8, Result); + Result = V4ScaleAdd(V18, C9, Result); + Result = V4ScaleAdd(V20, C10, Result); + Result = V4ScaleAdd(V22, C11, Result); + + return Result; +} + +PX_FORCE_INLINE void V4Transpose(Vec4V& col0, Vec4V& col1, Vec4V& col2, Vec4V& col3) +{ + const float32x4x2_t v0v1 = vzipq_f32(col0, col2); + const float32x4x2_t v2v3 = vzipq_f32(col1, col3); + const float32x4x2_t zip0 = vzipq_f32(v0v1.val[0], v2v3.val[0]); + const float32x4x2_t zip1 = vzipq_f32(v0v1.val[1], v2v3.val[1]); + col0 = zip0.val[0]; + col1 = zip0.val[1]; + col2 = zip1.val[0]; + col3 = zip1.val[1]; +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +PX_FORCE_INLINE BoolV BFFFF() +{ + return vmovq_n_u32(0); +} + +PX_FORCE_INLINE BoolV BFFFT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + return vcombine_u32(zeros, zo); +} + +PX_FORCE_INLINE BoolV BFFTF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(zeros, oz); +} + +PX_FORCE_INLINE BoolV BFFTT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + return vcombine_u32(zeros, ones); +} + +PX_FORCE_INLINE BoolV BFTFF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + return vcombine_u32(zo, zeros); +} + +PX_FORCE_INLINE BoolV BFTFT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + return vcombine_u32(zo, zo); +} + +PX_FORCE_INLINE BoolV BFTTF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(zo, oz); +} + +PX_FORCE_INLINE BoolV BFTTT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + return vcombine_u32(zo, ones); +} + +PX_FORCE_INLINE BoolV BTFFF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + // const uint32x2_t zo = vext_u32(zeros, ones, 1); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(oz, zeros); +} + +PX_FORCE_INLINE BoolV BTFFT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(oz, zo); +} + +PX_FORCE_INLINE BoolV BTFTF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(oz, oz); +} + +PX_FORCE_INLINE BoolV BTFTT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(oz, ones); +} + +PX_FORCE_INLINE BoolV BTTFF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + return vcombine_u32(ones, zeros); +} + +PX_FORCE_INLINE BoolV BTTFT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + return vcombine_u32(ones, zo); +} + +PX_FORCE_INLINE BoolV BTTTF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(ones, oz); +} + +PX_FORCE_INLINE BoolV BTTTT() +{ + return vmovq_n_u32(0xffffFFFF); +} + +PX_FORCE_INLINE BoolV BXMask() +{ + return BTFFF(); +} + +PX_FORCE_INLINE BoolV BYMask() +{ + return BFTFF(); +} + +PX_FORCE_INLINE BoolV BZMask() +{ + return BFFTF(); +} + +PX_FORCE_INLINE BoolV BWMask() +{ + return BFFFT(); +} + +PX_FORCE_INLINE BoolV BGetX(const BoolV f) +{ + const uint32x2_t fLow = vget_low_u32(f); + return vdupq_lane_u32(fLow, 0); +} + +PX_FORCE_INLINE BoolV BGetY(const BoolV f) +{ + const uint32x2_t fLow = vget_low_u32(f); + return vdupq_lane_u32(fLow, 1); +} + +PX_FORCE_INLINE BoolV BGetZ(const BoolV f) +{ + const uint32x2_t fHigh = vget_high_u32(f); + return vdupq_lane_u32(fHigh, 0); +} + +PX_FORCE_INLINE BoolV BGetW(const BoolV f) +{ + const uint32x2_t fHigh = vget_high_u32(f); + return vdupq_lane_u32(fHigh, 1); +} + +PX_FORCE_INLINE BoolV BSetX(const BoolV v, const BoolV f) +{ + return vbslq_u32(BFTTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetY(const BoolV v, const BoolV f) +{ + return vbslq_u32(BTFTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetZ(const BoolV v, const BoolV f) +{ + return vbslq_u32(BTTFT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetW(const BoolV v, const BoolV f) +{ + return vbslq_u32(BTTTF(), v, f); +} + +PX_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b) +{ + return vandq_u32(a, b); +} + +PX_FORCE_INLINE BoolV BNot(const BoolV a) +{ + return vmvnq_u32(a); +} + +PX_FORCE_INLINE BoolV BAndNot(const BoolV a, const BoolV b) +{ + // return vbicq_u32(a, b); + return vandq_u32(a, vmvnq_u32(b)); +} + +PX_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b) +{ + return vorrq_u32(a, b); +} + +PX_FORCE_INLINE BoolV BAllTrue4(const BoolV a) +{ + const uint32x2_t allTrue = vmov_n_u32(0xffffFFFF); + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + const uint32x2_t result = vceq_u32(finalReduce, allTrue); + return vdupq_lane_u32(result, 0); +} + +PX_FORCE_INLINE BoolV BAnyTrue4(const BoolV a) +{ + const uint32x2_t allTrue = vmov_n_u32(0xffffFFFF); + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + const uint32x2_t result = vtst_u32(finalReduce, allTrue); + return vdupq_lane_u32(result, 0); +} + +PX_FORCE_INLINE BoolV BAllTrue3(const BoolV a) +{ + const uint32x2_t allTrue3 = vmov_n_u32(0x00ffFFFF); + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + const uint32x2_t result = vceq_u32(vand_u32(finalReduce, allTrue3), allTrue3); + return vdupq_lane_u32(result, 0); +} + +PX_FORCE_INLINE BoolV BAnyTrue3(const BoolV a) +{ + const uint32x2_t allTrue3 = vmov_n_u32(0x00ffFFFF); + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + const uint32x2_t result = vtst_u32(vand_u32(finalReduce, allTrue3), allTrue3); + return vdupq_lane_u32(result, 0); +} + +PX_FORCE_INLINE PxU32 BAllEq(const BoolV a, const BoolV b) +{ + const BoolV bTest = vceqq_u32(a, b); + return internalUnitNeonSimd::BAllTrue4_R(bTest); +} + +PX_FORCE_INLINE PxU32 BAllEqTTTT(const BoolV a) +{ + return BAllEq(a, BTTTT()); +} + +PX_FORCE_INLINE PxU32 BAllEqFFFF(const BoolV a) +{ + return BAllEq(a, BFFFF()); +} + +PX_FORCE_INLINE PxU32 BGetBitMask(const BoolV a) +{ + static PX_ALIGN(16, const PxU32) bitMaskData[4] = { 1, 2, 4, 8 }; + const uint32x4_t bitMask = *(reinterpret_cast(bitMaskData)); + const uint32x4_t t0 = vandq_u32(a, bitMask); + const uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); // Pairwise add (0 + 1), (2 + 3) + return PxU32(vget_lane_u32(vpadd_u32(t1, t1), 0)); +} + +////////////////////////////////// +// MAT33V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M33MulV3(const Mat33V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V& a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +PX_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V& A, const Vec3V b, const Vec3V c) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + Vec3V result = V3ScaleAdd(A.col0, x, c); + result = V3ScaleAdd(A.col1, y, result); + return V3ScaleAdd(A.col2, z, result); +} + +PX_FORCE_INLINE Mat33V M33MulM33(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(M33MulV3(a, b.col0), M33MulV3(a, b.col1), M33MulV3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Add(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Scale(const Mat33V& a, const FloatV& b) +{ + return Mat33V(V3Scale(a.col0, b), V3Scale(a.col1, b), V3Scale(a.col2, b)); +} + +PX_FORCE_INLINE Mat33V M33Inverse(const Mat33V& a) +{ + const float32x2_t zeros = vreinterpret_f32_u32(vmov_n_u32(0)); + const BoolV btttf = BTTTF(); + + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = FRecipFast(dot); + + const float32x4x2_t merge = vzipq_f32(cross12, cross01); + const float32x4_t mergeh = merge.val[0]; + const float32x4_t mergel = merge.val[1]; + + // const Vec3V colInv0 = XMVectorPermute(mergeh,cross20,PxPermuteControl(0,4,1,7)); + const float32x4_t colInv0_xxyy = vzipq_f32(mergeh, cross20).val[0]; + const float32x4_t colInv0 = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(colInv0_xxyy), btttf)); + + // const Vec3V colInv1 = XMVectorPermute(mergeh,cross20,PxPermuteControl(2,5,3,7)); + const float32x2_t zw0 = vget_high_f32(mergeh); + const float32x2_t xy1 = vget_low_f32(cross20); + const float32x2_t yzero1 = vext_f32(xy1, zeros, 1); + const float32x2x2_t merge1 = vzip_f32(zw0, yzero1); + const float32x4_t colInv1 = vcombine_f32(merge1.val[0], merge1.val[1]); + + // const Vec3V colInv2 = XMVectorPermute(mergel,cross20,PxPermuteControl(0,6,1,7)); + const float32x2_t x0y0 = vget_low_f32(mergel); + const float32x2_t z1w1 = vget_high_f32(cross20); + const float32x2x2_t merge2 = vzip_f32(x0y0, z1w1); + const float32x4_t colInv2 = vcombine_f32(merge2.val[0], merge2.val[1]); + + return Mat33V(vmulq_lane_f32(colInv0, invDet, 0), vmulq_lane_f32(colInv1, invDet, 0), + vmulq_lane_f32(colInv2, invDet, 0)); +} + +PX_FORCE_INLINE Mat33V M33Trnsps(const Mat33V& a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +PX_FORCE_INLINE Mat33V M33Identity() +{ + return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ()); +} + +PX_FORCE_INLINE Mat33V M33Sub(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Sub(a.col0, b.col0), V3Sub(a.col1, b.col1), V3Sub(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Neg(const Mat33V& a) +{ + return Mat33V(V3Neg(a.col0), V3Neg(a.col1), V3Neg(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Abs(const Mat33V& a) +{ + return Mat33V(V3Abs(a.col0), V3Abs(a.col1), V3Abs(a.col2)); +} + +PX_FORCE_INLINE Mat33V PromoteVec3V(const Vec3V v) +{ + const BoolV bTFFF = BTFFF(); + const BoolV bFTFF = BFTFF(); + const BoolV bFFTF = BTFTF(); + + const Vec3V zero = V3Zero(); + + return Mat33V(V3Sel(bTFFF, v, zero), V3Sel(bFTFF, v, zero), V3Sel(bFFTF, v, zero)); +} + +PX_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg d) +{ + const Vec3V x = V3Mul(V3UnitX(), d); + const Vec3V y = V3Mul(V3UnitY(), d); + const Vec3V z = V3Mul(V3UnitZ(), d); + return Mat33V(x, y, z); +} + +////////////////////////////////// +// MAT34V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M34MulV3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + const Vec3V v0PlusV1Plusv2 = V3Add(v0PlusV1, v2); + return V3Add(v0PlusV1Plusv2, a.col3); +} + +PX_FORCE_INLINE Vec3V M34Mul33V3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M34TrnspsMul33V3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +PX_FORCE_INLINE Mat34V M34MulM34(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2), M34MulV3(a, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34MulM33(const Mat34V& a, const Mat33V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M34Mul33MM34(const Mat34V& a, const Mat34V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat34V M34Add(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2), V3Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34Trnsps33(const Mat34V& a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +////////////////////////////////// +// MAT44V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V M44MulV4(const Mat44V& a, const Vec4V b) +{ + const FloatV x = V4GetX(b); + const FloatV y = V4GetY(b); + const FloatV z = V4GetZ(b); + const FloatV w = V4GetW(b); + + const Vec4V v0 = V4Scale(a.col0, x); + const Vec4V v1 = V4Scale(a.col1, y); + const Vec4V v2 = V4Scale(a.col2, z); + const Vec4V v3 = V4Scale(a.col3, w); + const Vec4V v0PlusV1 = V4Add(v0, v1); + const Vec4V v0PlusV1Plusv2 = V4Add(v0PlusV1, v2); + return V4Add(v0PlusV1Plusv2, v3); +} + +PX_FORCE_INLINE Vec4V M44TrnspsMulV4(const Mat44V& a, const Vec4V b) +{ + return V4Merge(V4Dot(a.col0, b), V4Dot(a.col1, b), V4Dot(a.col2, b), V4Dot(a.col3, b)); +} + +PX_FORCE_INLINE Mat44V M44MulM44(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(M44MulV4(a, b.col0), M44MulV4(a, b.col1), M44MulV4(a, b.col2), M44MulV4(a, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Add(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(V4Add(a.col0, b.col0), V4Add(a.col1, b.col1), V4Add(a.col2, b.col2), V4Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Trnsps(const Mat44V& a) +{ + // asm volatile( + // "vzip.f32 %q0, %q2 \n\t" + // "vzip.f32 %q1, %q3 \n\t" + // "vzip.f32 %q0, %q1 \n\t" + // "vzip.f32 %q2, %q3 \n\t" + // : "+w" (a.col0), "+w" (a.col1), "+w" (a.col2), "+w" a.col3)); + + const float32x4x2_t v0v1 = vzipq_f32(a.col0, a.col2); + const float32x4x2_t v2v3 = vzipq_f32(a.col1, a.col3); + const float32x4x2_t zip0 = vzipq_f32(v0v1.val[0], v2v3.val[0]); + const float32x4x2_t zip1 = vzipq_f32(v0v1.val[1], v2v3.val[1]); + + return Mat44V(zip0.val[0], zip0.val[1], zip1.val[0], zip1.val[1]); +} + +PX_FORCE_INLINE Mat44V M44Inverse(const Mat44V& a) +{ + float32x4_t minor0, minor1, minor2, minor3; + float32x4_t row0, row1, row2, row3; + float32x4_t det, tmp1; + + tmp1 = vmovq_n_f32(0.0f); + row1 = vmovq_n_f32(0.0f); + row3 = vmovq_n_f32(0.0f); + + row0 = a.col0; + row1 = vextq_f32(a.col1, a.col1, 2); + row2 = a.col2; + row3 = vextq_f32(a.col3, a.col3, 2); + + tmp1 = vmulq_f32(row2, row3); + tmp1 = vrev64q_f32(tmp1); + minor0 = vmulq_f32(row1, tmp1); + minor1 = vmulq_f32(row0, tmp1); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor0 = vsubq_f32(vmulq_f32(row1, tmp1), minor0); + minor1 = vsubq_f32(vmulq_f32(row0, tmp1), minor1); + minor1 = vextq_f32(minor1, minor1, 2); + + tmp1 = vmulq_f32(row1, row2); + tmp1 = vrev64q_f32(tmp1); + minor0 = vaddq_f32(vmulq_f32(row3, tmp1), minor0); + minor3 = vmulq_f32(row0, tmp1); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor0 = vsubq_f32(minor0, vmulq_f32(row3, tmp1)); + minor3 = vsubq_f32(vmulq_f32(row0, tmp1), minor3); + minor3 = vextq_f32(minor3, minor3, 2); + + tmp1 = vmulq_f32(vextq_f32(row1, row1, 2), row3); + tmp1 = vrev64q_f32(tmp1); + row2 = vextq_f32(row2, row2, 2); + minor0 = vaddq_f32(vmulq_f32(row2, tmp1), minor0); + minor2 = vmulq_f32(row0, tmp1); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor0 = vsubq_f32(minor0, vmulq_f32(row2, tmp1)); + minor2 = vsubq_f32(vmulq_f32(row0, tmp1), minor2); + minor2 = vextq_f32(minor2, minor2, 2); + + tmp1 = vmulq_f32(row0, row1); + tmp1 = vrev64q_f32(tmp1); + minor2 = vaddq_f32(vmulq_f32(row3, tmp1), minor2); + minor3 = vsubq_f32(vmulq_f32(row2, tmp1), minor3); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor2 = vsubq_f32(vmulq_f32(row3, tmp1), minor2); + minor3 = vsubq_f32(minor3, vmulq_f32(row2, tmp1)); + + tmp1 = vmulq_f32(row0, row3); + tmp1 = vrev64q_f32(tmp1); + minor1 = vsubq_f32(minor1, vmulq_f32(row2, tmp1)); + minor2 = vaddq_f32(vmulq_f32(row1, tmp1), minor2); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor1 = vaddq_f32(vmulq_f32(row2, tmp1), minor1); + minor2 = vsubq_f32(minor2, vmulq_f32(row1, tmp1)); + + tmp1 = vmulq_f32(row0, row2); + tmp1 = vrev64q_f32(tmp1); + minor1 = vaddq_f32(vmulq_f32(row3, tmp1), minor1); + minor3 = vsubq_f32(minor3, vmulq_f32(row1, tmp1)); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor1 = vsubq_f32(minor1, vmulq_f32(row3, tmp1)); + minor3 = vaddq_f32(vmulq_f32(row1, tmp1), minor3); + + det = vmulq_f32(row0, minor0); + det = vaddq_f32(vextq_f32(det, det, 2), det); + det = vaddq_f32(vrev64q_f32(det), det); + det = vdupq_lane_f32(VRECIPE(vget_low_f32(det)), 0); + + minor0 = vmulq_f32(det, minor0); + minor1 = vmulq_f32(det, minor1); + minor2 = vmulq_f32(det, minor2); + minor3 = vmulq_f32(det, minor3); + Mat44V invTrans(minor0, minor1, minor2, minor3); + return M44Trnsps(invTrans); +} + +PX_FORCE_INLINE Vec4V V4LoadXYZW(const PxF32& x, const PxF32& y, const PxF32& z, const PxF32& w) +{ + const float32x4_t ret = { x, y, z, w }; + return ret; +} + +/* +PX_FORCE_INLINE VecU16V V4U32PK(VecU32V a, VecU32V b) +{ + return vcombine_u16(vqmovn_u32(a), vqmovn_u32(b)); +} +*/ + +PX_FORCE_INLINE VecU32V V4U32Sel(const BoolV c, const VecU32V a, const VecU32V b) +{ + return vbslq_u32(c, a, b); +} + +PX_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b) +{ + return vorrq_u32(a, b); +} + +PX_FORCE_INLINE VecU32V V4U32xor(VecU32V a, VecU32V b) +{ + return veorq_u32(a, b); +} + +PX_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b) +{ + return vandq_u32(a, b); +} + +PX_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b) +{ + // return vbicq_u32(a, b); // creates gcc compiler bug in RTreeQueries.cpp + return vandq_u32(a, vmvnq_u32(b)); +} + +/* +PX_FORCE_INLINE VecU16V V4U16Or(VecU16V a, VecU16V b) +{ + return vorrq_u16(a, b); +} +*/ + +/* +PX_FORCE_INLINE VecU16V V4U16And(VecU16V a, VecU16V b) +{ + return vandq_u16(a, b); +} +*/ +/* +PX_FORCE_INLINE VecU16V V4U16Andc(VecU16V a, VecU16V b) +{ + return vbicq_u16(a, b); +} +*/ + +PX_FORCE_INLINE VecI32V I4Load(const PxI32 i) +{ + return vdupq_n_s32(i); +} + +PX_FORCE_INLINE VecI32V I4LoadU(const PxI32* i) +{ + return vld1q_s32(i); +} + +PX_FORCE_INLINE VecI32V I4LoadA(const PxI32* i) +{ + return vld1q_s32(i); +} + +PX_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b) +{ + return vaddq_s32(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b) +{ + return vsubq_s32(a, b); +} + +PX_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b) +{ + return vcgtq_s32(a, b); +} + +PX_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b) +{ + return vceqq_s32(a, b); +} + +PX_FORCE_INLINE VecI32V V4I32Sel(const BoolV c, const VecI32V a, const VecI32V b) +{ + return vbslq_s32(c, a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Zero() +{ + return vdupq_n_s32(0); +} + +PX_FORCE_INLINE VecI32V VecI32V_One() +{ + return vdupq_n_s32(1); +} + +PX_FORCE_INLINE VecI32V VecI32V_Two() +{ + return vdupq_n_s32(2); +} + +PX_FORCE_INLINE VecI32V VecI32V_MinusOne() +{ + return vdupq_n_s32(-1); +} + +PX_FORCE_INLINE VecU32V U4Zero() +{ + return U4Load(0); +} + +PX_FORCE_INLINE VecU32V U4One() +{ + return U4Load(1); +} + +PX_FORCE_INLINE VecU32V U4Two() +{ + return U4Load(2); +} + +PX_FORCE_INLINE VecShiftV VecI32V_PrepareShift(const VecI32VArg shift) +{ + return shift; +} + +PX_FORCE_INLINE VecI32V VecI32V_LeftShift(const VecI32VArg a, const VecShiftVArg count) +{ + return vshlq_s32(a, count); +} + +PX_FORCE_INLINE VecI32V VecI32V_RightShift(const VecI32VArg a, const VecShiftVArg count) +{ + return vshlq_s32(a, VecI32V_Sub(I4Load(0), count)); +} + +PX_FORCE_INLINE VecI32V VecI32V_And(const VecI32VArg a, const VecI32VArg b) +{ + return vandq_s32(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Or(const VecI32VArg a, const VecI32VArg b) +{ + return vorrq_s32(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetX(const VecI32VArg f) +{ + const int32x2_t fLow = vget_low_s32(f); + return vdupq_lane_s32(fLow, 0); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetY(const VecI32VArg f) +{ + const int32x2_t fLow = vget_low_s32(f); + return vdupq_lane_s32(fLow, 1); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetZ(const VecI32VArg f) +{ + const int32x2_t fHigh = vget_high_s32(f); + return vdupq_lane_s32(fHigh, 0); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetW(const VecI32VArg f) +{ + const int32x2_t fHigh = vget_high_s32(f); + return vdupq_lane_s32(fHigh, 1); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sel(const BoolV c, const VecI32VArg a, const VecI32VArg b) +{ + return vbslq_s32(c, a, b); +} + +PX_FORCE_INLINE void PxI32_From_VecI32V(const VecI32VArg a, PxI32* i) +{ + *i = vgetq_lane_s32(a, 0); +} + +PX_FORCE_INLINE VecI32V VecI32V_Merge(const VecI32VArg a, const VecI32VArg b, const VecI32VArg c, const VecI32VArg d) +{ + const int32x2_t aLow = vget_low_s32(a); + const int32x2_t bLow = vget_low_s32(b); + const int32x2_t cLow = vget_low_s32(c); + const int32x2_t dLow = vget_low_s32(d); + + const int32x2_t low = vext_s32(aLow, bLow, 1); + const int32x2_t high = vext_s32(cLow, dLow, 1); + + return vcombine_s32(low, high); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_BoolV(const BoolVArg a) +{ + return vreinterpretq_s32_u32(a); +} + +PX_FORCE_INLINE VecU32V VecU32V_From_BoolV(const BoolVArg a) +{ + return a; +} + +/* +template PX_FORCE_INLINE VecI32V V4ISplat() +{ + return vdupq_n_s32(a); +} + +template PX_FORCE_INLINE VecU32V V4USplat() +{ + return vdupq_n_u32(a); +} +*/ + +/* +PX_FORCE_INLINE void V4U16StoreAligned(VecU16V val, VecU16V* address) +{ + vst1q_u16((uint16_t*)address, val); +} +*/ + +PX_FORCE_INLINE void V4U32StoreAligned(VecU32V val, VecU32V* address) +{ + vst1q_u32(reinterpret_cast(address), val); +} + +PX_FORCE_INLINE Vec4V V4LoadAligned(Vec4V* addr) +{ + return vld1q_f32(reinterpret_cast(addr)); +} + +PX_FORCE_INLINE Vec4V V4LoadUnaligned(Vec4V* addr) +{ + return vld1q_f32(reinterpret_cast(addr)); +} + +PX_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b) +{ + return vreinterpretq_f32_u32(V4U32Andc(vreinterpretq_u32_f32(a), b)); +} + +PX_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b) +{ + return V4IsGrtr(a, b); +} + +PX_FORCE_INLINE VecU16V V4U16LoadAligned(VecU16V* addr) +{ + return vld1q_u16(reinterpret_cast(addr)); +} + +PX_FORCE_INLINE VecU16V V4U16LoadUnaligned(VecU16V* addr) +{ + return vld1q_u16(reinterpret_cast(addr)); +} + +PX_FORCE_INLINE VecU16V V4U16CompareGt(VecU16V a, VecU16V b) +{ + return vcgtq_u16(a, b); +} + +PX_FORCE_INLINE VecU16V V4I16CompareGt(VecI16V a, VecI16V b) +{ + return vcgtq_s16(a, b); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a) +{ + return vcvtq_f32_u32(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecI32V(VecI32V a) +{ + return vcvtq_f32_s32(a); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_Vec4V(Vec4V a) +{ + return vcvtq_s32_f32(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecU32V(VecU32V a) +{ + return vreinterpretq_f32_u32(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecI32V(VecI32V a) +{ + return vreinterpretq_f32_s32(a); +} + +PX_FORCE_INLINE VecU32V VecU32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return vreinterpretq_u32_f32(a); +} + +PX_FORCE_INLINE VecI32V VecI32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return vreinterpretq_s32_f32(a); +} + +template +PX_FORCE_INLINE BoolV BSplatElement(BoolV a) +{ + if(index < 2) + { + return vdupq_lane_u32(vget_low_u32(a), index); + } + else if(index == 2) + { + return vdupq_lane_u32(vget_high_u32(a), 0); + } + else if(index == 3) + { + return vdupq_lane_u32(vget_high_u32(a), 1); + } +} + +template +PX_FORCE_INLINE VecU32V V4U32SplatElement(VecU32V a) +{ + if(index < 2) + { + return vdupq_lane_u32(vget_low_u32(a), index); + } + else if(index == 2) + { + return vdupq_lane_u32(vget_high_u32(a), 0); + } + else if(index == 3) + { + return vdupq_lane_u32(vget_high_u32(a), 1); + } +} + +template +PX_FORCE_INLINE Vec4V V4SplatElement(Vec4V a) +{ + if(index < 2) + { + return vdupq_lane_f32(vget_low_f32(a), index); + } + else if(index == 2) + { + return vdupq_lane_f32(vget_high_f32(a), 0); + } + else if(index == 3) + { + return vdupq_lane_f32(vget_high_f32(a), 1); + } +} + +PX_FORCE_INLINE VecU32V U4LoadXYZW(PxU32 x, PxU32 y, PxU32 z, PxU32 w) +{ + const uint32x4_t ret = { x, y, z, w }; + return ret; +} + +PX_FORCE_INLINE VecU32V U4Load(const PxU32 i) +{ + return vdupq_n_u32(i); +} + +PX_FORCE_INLINE VecU32V U4LoadU(const PxU32* i) +{ + return vld1q_u32(i); +} + +PX_FORCE_INLINE VecU32V U4LoadA(const PxU32* i) +{ + return vld1q_u32(i); +} + +PX_FORCE_INLINE Vec4V V4Ceil(const Vec4V in) +{ + const float32x4_t ones = vdupq_n_f32(1.0f); + const float32x4_t rdToZero = vcvtq_f32_s32(vcvtq_s32_f32(in)); + const float32x4_t rdToZeroPlusOne = vaddq_f32(rdToZero, ones); + const uint32x4_t gt = vcgtq_f32(in, rdToZero); + return vbslq_f32(gt, rdToZeroPlusOne, rdToZero); +} + +PX_FORCE_INLINE Vec4V V4Floor(const Vec4V in) +{ + const float32x4_t ones = vdupq_n_f32(1.0f); + const float32x4_t rdToZero = vcvtq_f32_s32(vcvtq_s32_f32(in)); + const float32x4_t rdToZeroMinusOne = vsubq_f32(rdToZero, ones); + const uint32x4_t lt = vcltq_f32(in, rdToZero); + return vbslq_f32(lt, rdToZeroMinusOne, rdToZero); +} + +PX_FORCE_INLINE VecU32V V4ConvertToU32VSaturate(const Vec4V in, PxU32 power) +{ + PX_ASSERT(power == 0 && "Non-zero power not supported in convertToU32VSaturate"); + PX_UNUSED(power); // prevent warning in release builds + + return vcvtq_u32_f32(in); +} + +PX_FORCE_INLINE void QuatGetMat33V(const QuatVArg q, Vec3V& column0, Vec3V& column1, Vec3V& column2) +{ + const FloatV one = FOne(); + const FloatV x = V4GetX(q); + const FloatV y = V4GetY(q); + const FloatV z = V4GetZ(q); + const FloatV w = V4GetW(q); + + const FloatV x2 = FAdd(x, x); + const FloatV y2 = FAdd(y, y); + const FloatV z2 = FAdd(z, z); + + const FloatV xx = FMul(x2, x); + const FloatV yy = FMul(y2, y); + const FloatV zz = FMul(z2, z); + + const FloatV xy = FMul(x2, y); + const FloatV xz = FMul(x2, z); + const FloatV xw = FMul(x2, w); + + const FloatV yz = FMul(y2, z); + const FloatV yw = FMul(y2, w); + const FloatV zw = FMul(z2, w); + + const FloatV v = FSub(one, xx); + + column0 = V3Merge(FSub(FSub(one, yy), zz), FAdd(xy, zw), FSub(xz, yw)); + column1 = V3Merge(FSub(xy, zw), FSub(v, zz), FAdd(yz, xw)); + column2 = V3Merge(FAdd(xz, yw), FSub(yz, xw), FSub(v, yy)); +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSUNIXNEONINLINEAOS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h new file mode 100644 index 000000000..102746281 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h @@ -0,0 +1,191 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXSSE2AOS_H +#define PSFOUNDATION_PSUNIXSSE2AOS_H + +// no includes here! this file should be included from PxcVecMath.h only!!! + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +#if PX_EMSCRIPTEN +typedef int8_t __int8_t; +typedef int16_t __int16_t; +typedef int32_t __int32_t; +typedef int64_t __int64_t; +typedef uint16_t __uint16_t; +typedef uint32_t __uint32_t; +typedef uint64_t __uint64_t; +#endif + +typedef union UnionM128 +{ + UnionM128() + { + } + UnionM128(__m128 in) + { + m128 = in; + } + + UnionM128(__m128i in) + { + m128i = in; + } + + operator __m128() + { + return m128; + } + + operator const __m128() const + { + return m128; + } + + float m128_f32[4]; + __int8_t m128_i8[16]; + __int16_t m128_i16[8]; + __int32_t m128_i32[4]; + __int64_t m128_i64[2]; + __uint16_t m128_u16[8]; + __uint32_t m128_u32[4]; + __uint64_t m128_u64[2]; + __m128 m128; + __m128i m128i; +} UnionM128; + +typedef __m128 FloatV; +typedef __m128 Vec3V; +typedef __m128 Vec4V; +typedef __m128 BoolV; +typedef __m128 QuatV; +typedef __m128i VecI32V; +typedef UnionM128 VecU32V; +typedef UnionM128 VecU16V; +typedef UnionM128 VecI16V; +typedef UnionM128 VecU8V; + +#define FloatVArg FloatV & +#define Vec3VArg Vec3V & +#define Vec4VArg Vec4V & +#define BoolVArg BoolV & +#define VecU32VArg VecU32V & +#define VecI32VArg VecI32V & +#define VecU16VArg VecU16V & +#define VecI16VArg VecI16V & +#define VecU8VArg VecU8V & +#define QuatVArg QuatV & + +// Optimization for situations in which you cross product multiple vectors with the same vector. +// Avoids 2X shuffles per product +struct VecCrossV +{ + Vec3V mL1; + Vec3V mR1; +}; + +struct VecShiftV +{ + VecI32V shift; +}; +#define VecShiftVArg VecShiftV & + +PX_ALIGN_PREFIX(16) +struct Mat33V +{ + Mat33V() + { + } + Mat33V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat34V +{ + Mat34V() + { + } + Mat34V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2, const Vec3V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); + Vec3V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat43V +{ + Mat43V() + { + } + Mat43V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat44V +{ + Mat44V() + { + } + Mat44V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2, const Vec4V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); + Vec4V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSUNIXSSE2AOS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h new file mode 100644 index 000000000..9899af581 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h @@ -0,0 +1,3239 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXSSE2INLINEAOS_H +#define PSFOUNDATION_PSUNIXSSE2INLINEAOS_H + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +#ifdef __SSE4_2__ +#include "smmintrin.h" +#endif + +#include "../../PsVecMathSSE.h" + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +#define PX_FPCLASS_SNAN 0x0001 /* signaling NaN */ +#define PX_FPCLASS_QNAN 0x0002 /* quiet NaN */ +#define PX_FPCLASS_NINF 0x0004 /* negative infinity */ +#define PX_FPCLASS_PINF 0x0200 /* positive infinity */ + +PX_FORCE_INLINE __m128 m128_I2F(__m128i n) +{ + return _mm_castsi128_ps(n); +} +PX_FORCE_INLINE __m128i m128_F2I(__m128 n) +{ + return _mm_castps_si128(n); +} + +////////////////////////////////////////////////////////////////////// +//Test that Vec3V and FloatV are legal +////////////////////////////////////////////////////////////////////// + +#define FLOAT_COMPONENTS_EQUAL_THRESHOLD 0.01f +PX_FORCE_INLINE static bool isValidFloatV(const FloatV a) +{ + const PxF32 x = V4ReadX(a); + const PxF32 y = V4ReadY(a); + const PxF32 z = V4ReadZ(a); + const PxF32 w = V4ReadW(a); + + if ( + (PxAbs(x - y) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs(x - z) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs(x - w) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + ) + { + return true; + } + + if ( + (PxAbs((x - y) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs((x - z) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs((x - w) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + ) + { + return true; + } + + return false; +} + +PX_FORCE_INLINE bool isValidVec3V(const Vec3V a) +{ + PX_ALIGN(16, PxF32 f[4]); + V4StoreA(a, f); + return (f[3] == 0.0f); +} + +PX_FORCE_INLINE bool isFiniteLength(const Vec3V a) +{ + return !FAllEq(V4LengthSq(a), FZero()); +} + +PX_FORCE_INLINE bool isAligned16(void* a) +{ + return(0 == (size_t(a) & 0x0f)); +} + +//ASSERT_FINITELENGTH is deactivated because there is a lot of code that calls a simd normalisation function with zero length but then ignores the result. + +#if PX_DEBUG +#define ASSERT_ISVALIDVEC3V(a) PX_ASSERT(isValidVec3V(a)) +#define ASSERT_ISVALIDFLOATV(a) PX_ASSERT(isValidFloatV(a)) +#define ASSERT_ISALIGNED16(a) PX_ASSERT(isAligned16(reinterpret_cast(a))) +#define ASSERT_ISFINITELENGTH(a) //PX_ASSERT(isFiniteLength(a)) +#else +#define ASSERT_ISVALIDVEC3V(a) +#define ASSERT_ISVALIDFLOATV(a) +#define ASSERT_ISALIGNED16(a) +#define ASSERT_ISFINITELENGTH(a) +#endif + + +namespace internalUnitSSE2Simd +{ +PX_FORCE_INLINE PxU32 BAllTrue4_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32(moveMask == 0xf); +} + +PX_FORCE_INLINE PxU32 BAllTrue3_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32((moveMask & 0x7) == 0x7); +} + +PX_FORCE_INLINE PxU32 BAnyTrue4_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32(moveMask != 0x0); +} + +PX_FORCE_INLINE PxU32 BAnyTrue3_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32((moveMask & 0x7) != 0x0); +} + +PX_FORCE_INLINE PxU32 FiniteTestEq(const Vec4V a, const Vec4V b) +{ + // This is a bit of a bodge. + //_mm_comieq_ss returns 1 if either value is nan so we need to re-cast a and b with true encoded as a non-nan + // number. + // There must be a better way of doing this in sse. + const BoolV one = FOne(); + const BoolV zero = FZero(); + const BoolV a1 = V4Sel(a, one, zero); + const BoolV b1 = V4Sel(b, one, zero); + return ( + _mm_comieq_ss(a1, b1) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(1, 1, 1, 1)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(1, 1, 1, 1))) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(2, 2, 2, 2)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(2, 2, 2, 2))) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(3, 3, 3, 3)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(3, 3, 3, 3)))); +} + +#if !PX_EMSCRIPTEN +const PX_ALIGN(16, PxF32 gMaskXYZ[4]) = { physx::PxUnionCast(0xffffffff), physx::PxUnionCast(0xffffffff), + physx::PxUnionCast(0xffffffff), 0 }; +} +#else +// emscripten doesn't like the PxUnionCast data structure +// the following is what windows and xbox does -- using these for emscripten +const PX_ALIGN(16, PxU32 gMaskXYZ[4]) = { 0xffffffff, 0xffffffff, 0xffffffff, 0 }; } +#endif + +namespace _VecMathTests +{ +// PT: this function returns an invalid Vec3V (W!=0.0f) just for unit-testing 'isValidVec3V' +PX_FORCE_INLINE Vec3V getInvalidVec3V() +{ + const float f = 1.0f; + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE bool allElementsEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_comieq_ss(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec3V(const Vec3V a, const Vec3V b) +{ + return V3AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec4V(const Vec4V a, const Vec4V b) +{ + return V4AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualBoolV(const BoolV a, const BoolV b) +{ + return internalUnitSSE2Simd::BAllTrue4_R(VecI32V_IsEq(m128_F2I(a), m128_F2I(b))) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVecU32V(const VecU32V a, const VecU32V b) +{ + return internalUnitSSE2Simd::BAllTrue4_R(V4IsEqU32(a, b)) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVecI32V(const VecI32V a, const VecI32V b) +{ + BoolV c = m128_I2F(_mm_cmpeq_epi32(a, b)); + return internalUnitSSE2Simd::BAllTrue4_R(c) != 0; +} + +#define VECMATH_AOS_EPSILON (1e-3f) + +PX_FORCE_INLINE bool allElementsNearEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + const FloatV c = FSub(a, b); + const FloatV minError = FLoad(-VECMATH_AOS_EPSILON); + const FloatV maxError = FLoad(VECMATH_AOS_EPSILON); + return _mm_comigt_ss(c, minError) && _mm_comilt_ss(c, maxError); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec3V(const Vec3V a, const Vec3V b) +{ + const Vec3V c = V3Sub(a, b); + const Vec3V minError = V3Load(-VECMATH_AOS_EPSILON); + const Vec3V maxError = V3Load(VECMATH_AOS_EPSILON); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxError) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxError) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxError)); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec4V(const Vec4V a, const Vec4V b) +{ + const Vec4V c = V4Sub(a, b); + const Vec4V minError = V4Load(-VECMATH_AOS_EPSILON); + const Vec4V maxError = V4Load(VECMATH_AOS_EPSILON); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxError) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxError) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxError) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), maxError)); +} +} + +///////////////////////////////////////////////////////////////////// +////FUNCTIONS USED ONLY FOR ASSERTS IN VECTORISED IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE bool isFiniteFloatV(const FloatV a) +{ + PxF32 badNumber = + physx::PxUnionCast(PX_FPCLASS_SNAN | PX_FPCLASS_QNAN | PX_FPCLASS_NINF | PX_FPCLASS_PINF); + const FloatV vBadNum = FLoad(badNumber); + const BoolV vMask = BAnd(vBadNum, a); + return internalUnitSSE2Simd::FiniteTestEq(vMask, BFFFF()) == 1; +} + +PX_FORCE_INLINE bool isFiniteVec3V(const Vec3V a) +{ + PxF32 badNumber = + physx::PxUnionCast(PX_FPCLASS_SNAN | PX_FPCLASS_QNAN | PX_FPCLASS_NINF | PX_FPCLASS_PINF); + const Vec3V vBadNum = V3Load(badNumber); + const BoolV vMask = BAnd(BAnd(vBadNum, a), BTTTF()); + return internalUnitSSE2Simd::FiniteTestEq(vMask, BFFFF()) == 1; +} + +PX_FORCE_INLINE bool isFiniteVec4V(const Vec4V a) +{ + /*Vec4V a; + PX_ALIGN(16, PxF32 f[4]); + F32Array_Aligned_From_Vec4V(a, f); + return PxIsFinite(f[0]) + && PxIsFinite(f[1]) + && PxIsFinite(f[2]) + && PxIsFinite(f[3]);*/ + + PxF32 badNumber = + physx::PxUnionCast(PX_FPCLASS_SNAN | PX_FPCLASS_QNAN | PX_FPCLASS_NINF | PX_FPCLASS_PINF); + const Vec4V vBadNum = V4Load(badNumber); + const BoolV vMask = BAnd(vBadNum, a); + + return internalUnitSSE2Simd::FiniteTestEq(vMask, BFFFF()) == 1; +} + +PX_FORCE_INLINE bool hasZeroElementinFloatV(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) ? true : false; +} + +PX_FORCE_INLINE bool hasZeroElementInVec3V(const Vec3V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FZero())); +} + +PX_FORCE_INLINE bool hasZeroElementInVec4V(const Vec4V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)), FZero())); +} + +///////////////////////////////////////////////////////////////////// +////VECTORISED FUNCTION IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE FloatV FLoad(const PxF32 f) +{ + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE Vec3V V3Load(const PxF32 f) +{ + return _mm_set_ps(0.0f, f, f, f); +} + +PX_FORCE_INLINE Vec4V V4Load(const PxF32 f) +{ + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool f) +{ + const PxU32 i = -PxI32(f); + return _mm_load1_ps(reinterpret_cast(&i)); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(const_cast(&f)); +#if !PX_EMSCRIPTEN + return _mm_and_ps(reinterpret_cast(f), V4LoadA(internalUnitSSE2Simd::gMaskXYZ)); +#else + return _mm_and_ps((Vec3V&)f, (VecI32V&)internalUnitSSE2Simd::gMaskXYZ); +#endif +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxVec3& f) +{ + return _mm_set_ps(0.0f, f.z, f.y, f.x); +} + +PX_FORCE_INLINE Vec3V V3LoadUnsafeA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(const_cast(&f)); + return _mm_set_ps(0.0f, f.z, f.y, f.x); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(const_cast(f)); +#if !PX_EMSCRIPTEN + return _mm_and_ps(V4LoadA(f), V4LoadA(internalUnitSSE2Simd::gMaskXYZ)); +#else + return _mm_and_ps((Vec3V&)*f, (VecI32V&)internalUnitSSE2Simd::gMaskXYZ); +#endif +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxF32* const i) +{ + return _mm_set_ps(0.0f, i[2], i[1], i[0]); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V v) +{ + return V4ClearW(v); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V_WUndefined(const Vec4V v) +{ + return v; +} + +PX_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return f; // ok if it is implemented as the same type. +} + +PX_FORCE_INLINE Vec4V Vec4V_From_PxVec3_WUndefined(const PxVec3& f) +{ + return _mm_set_ps(0.0f, f.z, f.y, f.x); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_FloatV(FloatV f) +{ + return f; +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV(FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return Vec3V_From_Vec4V(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV_WUndefined(FloatV f) +{ + ASSERT_ISVALIDVEC3V(f); + return Vec3V_From_Vec4V_WUndefined(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Mat33V Mat33V_From_PxMat33(const PxMat33& m) +{ + return Mat33V(V3LoadU(m.column0), V3LoadU(m.column1), V3LoadU(m.column2)); +} + +PX_FORCE_INLINE void PxMat33_From_Mat33V(const Mat33V& m, PxMat33& out) +{ + V3StoreU(m.col0, out.column0); + V3StoreU(m.col1, out.column1); + V3StoreU(m.col2, out.column2); +} + +PX_FORCE_INLINE Vec4V V4LoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(const_cast(f)); + return _mm_load_ps(f); +} + +PX_FORCE_INLINE void V4StoreA(Vec4V a, PxF32* f) +{ + ASSERT_ISALIGNED16(f); + _mm_store_ps(f, a); +} + +PX_FORCE_INLINE void V4StoreU(const Vec4V a, PxF32* f) +{ + _mm_storeu_ps(f, a); +} + +PX_FORCE_INLINE void BStoreA(const BoolV a, PxU32* f) +{ + ASSERT_ISALIGNED16(f); + _mm_store_ps(reinterpret_cast(f), a); +} + +PX_FORCE_INLINE void U4StoreA(const VecU32V uv, PxU32* u) +{ + ASSERT_ISALIGNED16(u); + _mm_store_ps(reinterpret_cast(u), uv); +} + +PX_FORCE_INLINE void I4StoreA(const VecI32V iv, PxI32* i) +{ + ASSERT_ISALIGNED16(i); + _mm_store_ps(reinterpret_cast(i), m128_I2F(iv)); +} + +PX_FORCE_INLINE Vec4V V4LoadU(const PxF32* const f) +{ + return _mm_loadu_ps(f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool* const f) +{ + const PX_ALIGN(16, PxI32) b[4] = { -PxI32(f[0]), -PxI32(f[1]), -PxI32(f[2]), -PxI32(f[3]) }; + return _mm_load_ps(reinterpret_cast(&b)); +} + +PX_FORCE_INLINE void FStore(const FloatV a, PxF32* PX_RESTRICT f) +{ + ASSERT_ISVALIDFLOATV(a); + _mm_store_ss(f, a); +} + +PX_FORCE_INLINE void V3StoreA(const Vec3V a, PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + PX_ALIGN(16, PxF32) f2[4]; + _mm_store_ps(f2, a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +PX_FORCE_INLINE void V3StoreU(const Vec3V a, PxVec3& f) +{ + PX_ALIGN(16, PxF32) f2[4]; + _mm_store_ps(f2, a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +PX_FORCE_INLINE void Store_From_BoolV(const BoolV b, PxU32* b2) +{ + _mm_store_ss(reinterpret_cast(b2), b); +} + +PX_FORCE_INLINE VecU32V U4Load(const PxU32 i) +{ + return _mm_load1_ps(reinterpret_cast(&i)); +} + +PX_FORCE_INLINE VecU32V U4LoadU(const PxU32* i) +{ + return _mm_loadu_ps(reinterpret_cast(i)); +} + +PX_FORCE_INLINE VecU32V U4LoadA(const PxU32* i) +{ + ASSERT_ISALIGNED16(const_cast(i)); + return _mm_load_ps(reinterpret_cast(i)); +} + +////////////////////////////////// +// FLOATV +////////////////////////////////// + +PX_FORCE_INLINE FloatV FZero() +{ + return FLoad(0.0f); +} + +PX_FORCE_INLINE FloatV FOne() +{ + return FLoad(1.0f); +} + +PX_FORCE_INLINE FloatV FHalf() +{ + return FLoad(0.5f); +} + +PX_FORCE_INLINE FloatV FEps() +{ + return FLoad(PX_EPS_REAL); +} + +PX_FORCE_INLINE FloatV FEps6() +{ + return FLoad(1e-6f); +} + +PX_FORCE_INLINE FloatV FMax() +{ + return FLoad(PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV FNegMax() +{ + return FLoad(-PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV IZero() +{ + const PxU32 zero = 0; + return _mm_load1_ps(reinterpret_cast(&zero)); +} + +PX_FORCE_INLINE FloatV IOne() +{ + const PxU32 one = 1; + return _mm_load1_ps(reinterpret_cast(&one)); +} + +PX_FORCE_INLINE FloatV ITwo() +{ + const PxU32 two = 2; + return _mm_load1_ps(reinterpret_cast(&two)); +} + +PX_FORCE_INLINE FloatV IThree() +{ + const PxU32 three = 3; + return _mm_load1_ps(reinterpret_cast(&three)); +} + +PX_FORCE_INLINE FloatV IFour() +{ + PxU32 four = 4; + return _mm_load1_ps(reinterpret_cast(&four)); +} + +PX_FORCE_INLINE FloatV FNeg(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); +/* + if(!isValidFloatV(a)) + { +assert(false); + } + if(!isValidFloatV(b)) + { +assert(false); + } +*/ + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE FloatV FRecip(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_div_ps(FOne(), a); +} + +PX_FORCE_INLINE FloatV FRecipFast(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_rcp_ps(a); +} + +PX_FORCE_INLINE FloatV FRsqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_div_ps(FOne(), _mm_sqrt_ps(a)); +} + +PX_FORCE_INLINE FloatV FSqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_sqrt_ps(a); +} + +PX_FORCE_INLINE FloatV FRsqrtFast(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_rsqrt_ps(a); +} + +PX_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return FAdd(FMul(a, b), c); +} + +PX_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return FSub(c, FMul(a, b)); +} + +PX_FORCE_INLINE FloatV FAbs(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + PX_ALIGN(16, const PxU32) absMask[4] = { 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF }; + return _mm_and_ps(a, _mm_load_ps(reinterpret_cast(absMask))); +} + +PX_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b) +{ + PX_ASSERT(_VecMathTests::allElementsEqualBoolV(c,BTTTT()) || + _VecMathTests::allElementsEqualBoolV(c,BFFFF())); + ASSERT_ISVALIDFLOATV(_mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a))); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV) +{ + ASSERT_ISVALIDFLOATV(minV); + ASSERT_ISVALIDFLOATV(maxV); + return _mm_max_ps(_mm_min_ps(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 FAllGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_comigt_ss(a, b); +} + +PX_FORCE_INLINE PxU32 FAllGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_comige_ss(a, b); +} + +PX_FORCE_INLINE PxU32 FAllEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_comieq_ss(a, b); +} + +PX_FORCE_INLINE FloatV FRound(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); +#ifdef __SSE4_2__ + return _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); +#else + // return _mm_round_ps(a, 0x0); + const FloatV half = FLoad(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const FloatV aRound = FSub(FAdd(a, half), signBit); + __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +#endif +} + +PX_FORCE_INLINE FloatV FSin(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = V4LoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V3 = FMul(V2, V1); + const FloatV V5 = FMul(V3, V2); + const FloatV V7 = FMul(V5, V2); + const FloatV V9 = FMul(V7, V2); + const FloatV V11 = FMul(V9, V2); + const FloatV V13 = FMul(V11, V2); + const FloatV V15 = FMul(V13, V2); + const FloatV V17 = FMul(V15, V2); + const FloatV V19 = FMul(V17, V2); + const FloatV V21 = FMul(V19, V2); + const FloatV V23 = FMul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + FloatV Result; + Result = FScaleAdd(S1, V3, V1); + Result = FScaleAdd(S2, V5, Result); + Result = FScaleAdd(S3, V7, Result); + Result = FScaleAdd(S4, V9, Result); + Result = FScaleAdd(S5, V11, Result); + Result = FScaleAdd(S6, V13, Result); + Result = FScaleAdd(S7, V15, Result); + Result = FScaleAdd(S8, V17, Result); + Result = FScaleAdd(S9, V19, Result); + Result = FScaleAdd(S10, V21, Result); + Result = FScaleAdd(S11, V23, Result); + + return Result; +} + +PX_FORCE_INLINE FloatV FCos(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = V4LoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V4 = FMul(V2, V2); + const FloatV V6 = FMul(V4, V2); + const FloatV V8 = FMul(V4, V4); + const FloatV V10 = FMul(V6, V4); + const FloatV V12 = FMul(V6, V6); + const FloatV V14 = FMul(V8, V6); + const FloatV V16 = FMul(V8, V8); + const FloatV V18 = FMul(V10, V8); + const FloatV V20 = FMul(V10, V10); + const FloatV V22 = FMul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + FloatV Result; + Result = FScaleAdd(C1, V2, V4One()); + Result = FScaleAdd(C2, V4, Result); + Result = FScaleAdd(C3, V6, Result); + Result = FScaleAdd(C4, V8, Result); + Result = FScaleAdd(C5, V10, Result); + Result = FScaleAdd(C6, V12, Result); + Result = FScaleAdd(C7, V14, Result); + Result = FScaleAdd(C8, V16, Result); + Result = FScaleAdd(C9, V18, Result); + Result = FScaleAdd(C10, V20, Result); + Result = FScaleAdd(C11, V22, Result); + + return Result; +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max); + const BoolV c = BOr(FIsGrtr(a, max), FIsGrtr(min, a)); + return !BAllEqFFFF(c); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max) + const BoolV c = BAnd(FIsGrtrOrEq(a, min), FIsGrtrOrEq(max, a)); + return BAllEqTTTT(c); +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + return FOutOfBounds(a, FNeg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + return FInBounds(a, FNeg(bounds), bounds); +} + +////////////////////////////////// +// VEC3V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V V3Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + const __m128 zero = FZero(); + const __m128 fff0 = _mm_move_ss(f, zero); + return _mm_shuffle_ps(fff0, fff0, _MM_SHUFFLE(0, 1, 2, 3)); +} + +PX_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + // static on zero causes compiler crash on x64 debug_opt + const __m128 zero = FZero(); + const __m128 xy = _mm_move_ss(x, y); + const __m128 z0 = _mm_move_ss(zero, z); + + return _mm_shuffle_ps(xy, z0, _MM_SHUFFLE(1, 0, 0, 1)); +} + +PX_FORCE_INLINE Vec3V V3UnitX() +{ + const PX_ALIGN(16, PxF32) x[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +PX_FORCE_INLINE Vec3V V3UnitY() +{ + const PX_ALIGN(16, PxF32) y[4] = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +PX_FORCE_INLINE Vec3V V3UnitZ() +{ + const PX_ALIGN(16, PxF32) z[4] = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +PX_FORCE_INLINE FloatV V3GetX(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE FloatV V3GetY(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f) + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE FloatV V3GetZ(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 0, 3, 0)); + return V3SetY(r, V3GetX(b)); +} + +PX_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c) + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 1, 3, 1)); + return V3SetY(r, V3GetY(b)); +} + +PX_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 2, 3, 2)); + return V3SetY(r, V3GetZ(b)); +} + +PX_FORCE_INLINE Vec3V V3Zero() +{ + return V3Load(0.0f); +} + +PX_FORCE_INLINE Vec3V V3Eps() +{ + return V3Load(PX_EPS_REAL); +} +PX_FORCE_INLINE Vec3V V3One() +{ + return V3Load(1.0f); +} + +PX_FORCE_INLINE Vec3V V3Neg(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return V4ClearW(_mm_div_ps(a, b)); +} + +PX_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return V4ClearW(_mm_mul_ps(a, _mm_rcp_ps(b))); +} + +PX_FORCE_INLINE Vec3V V3Recip(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_div_ps(V3One(), a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rcp_ps(a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_div_ps(V3One(), _mm_sqrt_ps(a)); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rsqrt_ps(a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + return V3Add(V3Scale(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + return V3Sub(c, V3Scale(a, b)); +} + +PX_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return V3Add(V3Mul(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return V3Sub(c, V3Mul(a, b)); +} + +PX_FORCE_INLINE Vec3V V3Abs(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return V3Max(a, V3Neg(a)); +} + +PX_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); +#ifdef __SSE4_2__ + return _mm_dp_ps(a, b, 0x7f); +#else + const __m128 t0 = _mm_mul_ps(a, b); // aw*bw | az*bz | ay*by | ax*bx + const __m128 t1 = _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(1,0,3,2)); // ay*by | ax*bx | aw*bw | az*bz + const __m128 t2 = _mm_add_ps(t0, t1); // ay*by + aw*bw | ax*bx + az*bz | aw*bw + ay*by | az*bz + ax*bx + const __m128 t3 = _mm_shuffle_ps(t2, t2, _MM_SHUFFLE(2,3,0,1)); // ax*bx + az*bz | ay*by + aw*bw | az*bz + ax*bx | aw*bw + ay*by + return _mm_add_ps(t3, t2); // ax*bx + az*bz + ay*by + aw*bw + // ay*by + aw*bw + ax*bx + az*bz + // az*bz + ax*bx + aw*bw + ay*by + // aw*bw + ay*by + az*bz + ax*bx +#endif +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + const __m128 r1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(l1, l2), _mm_mul_ps(r1, r2)); +} + +PX_FORCE_INLINE VecCrossV V3PrepareCross(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + VecCrossV v; + v.mR1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + v.mL1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + return v; +} + +PX_FORCE_INLINE Vec3V V3Cross(const VecCrossV& a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(b); + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(a.mL1, l2), _mm_mul_ps(a.mR1, r2)); +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const VecCrossV& b) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 r2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(b.mR1, r2), _mm_mul_ps(b.mL1, l2)); +} + +PX_FORCE_INLINE Vec3V V3Cross(const VecCrossV& a, const VecCrossV& b) +{ + return _mm_sub_ps(_mm_mul_ps(a.mL1, b.mR1), _mm_mul_ps(a.mR1, b.mL1)); +} + +PX_FORCE_INLINE FloatV V3Length(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_sqrt_ps(V3Dot(a, a)); +} + +PX_FORCE_INLINE FloatV V3LengthSq(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return V3Dot(a, a); +} + +PX_FORCE_INLINE Vec3V V3Normalize(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISFINITELENGTH(a); + return V3ScaleInv(a, _mm_sqrt_ps(V3Dot(a, a))); +} + +PX_FORCE_INLINE Vec3V V3NormalizeFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISFINITELENGTH(a); + return V3Scale(a, _mm_rsqrt_ps(V3Dot(a, a))); +} + +PX_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a, const Vec3V unsafeReturnValue) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 eps = V3Eps(); + const __m128 length = V3Length(a); + const __m128 isGreaterThanZero = FIsGrtr(length, eps); + return V3Sel(isGreaterThanZero, V3ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(_mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a))); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + + return _mm_max_ps(_mm_max_ps(shuf1, shuf2), shuf3); +} + +PX_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + + return _mm_min_ps(_mm_min_ps(shuf1, shuf2), shuf3); +} + +// return (a >= 0.0f) ? 1.0f : -1.0f; +PX_FORCE_INLINE Vec3V V3Sign(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 one = V3One(); + const __m128 none = V3Neg(one); + return V3Sel(V3IsGrtrOrEq(a, zero), one, none); +} + +PX_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV) +{ + ASSERT_ISVALIDVEC3V(maxV); + ASSERT_ISVALIDVEC3V(minV); + return V3Max(V3Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V3AllGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitSSE2Simd::BAllTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitSSE2Simd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitSSE2Simd::BAllTrue3_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE Vec3V V3Round(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); +#ifdef __SSE4_2__ + return _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); +#else + // return _mm_round_ps(a, 0x0); + const Vec3V half = V3Load(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const Vec3V aRound = V3Sub(V3Add(a, half), signBit); + __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +#endif +} + +PX_FORCE_INLINE Vec3V V3Sin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V3Scale(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegScaleSub(b, twoPi, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V3 = V3Mul(V2, V1); + const Vec3V V5 = V3Mul(V3, V2); + const Vec3V V7 = V3Mul(V5, V2); + const Vec3V V9 = V3Mul(V7, V2); + const Vec3V V11 = V3Mul(V9, V2); + const Vec3V V13 = V3Mul(V11, V2); + const Vec3V V15 = V3Mul(V13, V2); + const Vec3V V17 = V3Mul(V15, V2); + const Vec3V V19 = V3Mul(V17, V2); + const Vec3V V21 = V3Mul(V19, V2); + const Vec3V V23 = V3Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec3V Result; + Result = V3ScaleAdd(V3, S1, V1); + Result = V3ScaleAdd(V5, S2, Result); + Result = V3ScaleAdd(V7, S3, Result); + Result = V3ScaleAdd(V9, S4, Result); + Result = V3ScaleAdd(V11, S5, Result); + Result = V3ScaleAdd(V13, S6, Result); + Result = V3ScaleAdd(V15, S7, Result); + Result = V3ScaleAdd(V17, S8, Result); + Result = V3ScaleAdd(V19, S9, Result); + Result = V3ScaleAdd(V21, S10, Result); + Result = V3ScaleAdd(V23, S11, Result); + + ASSERT_ISVALIDVEC3V(Result); + return Result; +} + +PX_FORCE_INLINE Vec3V V3Cos(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V3Scale(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegScaleSub(b, twoPi, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V4 = V3Mul(V2, V2); + const Vec3V V6 = V3Mul(V4, V2); + const Vec3V V8 = V3Mul(V4, V4); + const Vec3V V10 = V3Mul(V6, V4); + const Vec3V V12 = V3Mul(V6, V6); + const Vec3V V14 = V3Mul(V8, V6); + const Vec3V V16 = V3Mul(V8, V8); + const Vec3V V18 = V3Mul(V10, V8); + const Vec3V V20 = V3Mul(V10, V10); + const Vec3V V22 = V3Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec3V Result; + Result = V3ScaleAdd(V2, C1, V3One()); + Result = V3ScaleAdd(V4, C2, Result); + Result = V3ScaleAdd(V6, C3, Result); + Result = V3ScaleAdd(V8, C4, Result); + Result = V3ScaleAdd(V10, C5, Result); + Result = V3ScaleAdd(V12, C6, Result); + Result = V3ScaleAdd(V14, C7, Result); + Result = V3ScaleAdd(V16, C8, Result); + Result = V3ScaleAdd(V18, C9, Result); + Result = V3ScaleAdd(V20, C10, Result); + Result = V3ScaleAdd(V22, C11, Result); + + ASSERT_ISVALIDVEC3V(Result); + return Result; +} + +PX_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 2, 1)); +} + +PX_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 1, 0)); +} + +PX_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); +} + +PX_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); +} + +PX_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 2, 2)); +} + +PX_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 0, 1)); +} + +PX_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + return _mm_shuffle_ps(v1, v0, _MM_SHUFFLE(3, 1, 2, 3)); +} + +PX_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + return _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(3, 0, 3, 2)); +} + +PX_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + // There must be a better way to do this. + Vec3V v2 = V3Zero(); + FloatV y1 = V3GetY(v1); + FloatV x0 = V3GetX(v0); + v2 = V3SetX(v2, y1); + return V3SetY(v2, x0); +} + +PX_FORCE_INLINE FloatV V3SumElems(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); +#ifdef __SSE4_2__ + Vec3V r = _mm_hadd_ps(a, a); + r = _mm_hadd_ps(r, r); + return r; +#else + __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); // z,y,x,w + __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); // y,x,w,z + __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); // x,w,z,y + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); +#endif +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + const BoolV c = BOr(V3IsGrtr(a, max), V3IsGrtr(min, a)); + return !BAllEqFFFF(c); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + const BoolV c = BAnd(V3IsGrtrOrEq(a, min), V3IsGrtrOrEq(max, a)); + return BAllEqTTTT(c); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds); + return V3OutOfBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds) + return V3InBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2) +{ + ASSERT_ISVALIDVEC3V(col0); + ASSERT_ISVALIDVEC3V(col1); + ASSERT_ISVALIDVEC3V(col2); + + const Vec3V col3 = _mm_setzero_ps(); + Vec3V tmp0 = _mm_unpacklo_ps(col0, col1); + Vec3V tmp2 = _mm_unpacklo_ps(col2, col3); + Vec3V tmp1 = _mm_unpackhi_ps(col0, col1); + Vec3V tmp3 = _mm_unpackhi_ps(col2, col3); + col0 = _mm_movelh_ps(tmp0, tmp2); + col1 = _mm_movehl_ps(tmp2, tmp0); + col2 = _mm_movelh_ps(tmp1, tmp3); +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V V4Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + // return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0,0,0,0)); + return f; +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatV* const floatVArray) +{ + ASSERT_ISVALIDFLOATV(floatVArray[0]); + ASSERT_ISVALIDFLOATV(floatVArray[1]); + ASSERT_ISVALIDFLOATV(floatVArray[2]); + ASSERT_ISVALIDFLOATV(floatVArray[3]); + const __m128 xw = _mm_move_ss(floatVArray[1], floatVArray[0]); // y, y, y, x + const __m128 yz = _mm_move_ss(floatVArray[2], floatVArray[3]); // z, z, z, w + return _mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0)); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + ASSERT_ISVALIDFLOATV(w); + const __m128 xw = _mm_move_ss(y, x); // y, y, y, x + const __m128 yz = _mm_move_ss(z, w); // z, z, z, w + return _mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0)); +} + +PX_FORCE_INLINE Vec4V V4MergeW(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpackhi_ps(x, z); + const Vec4V yw = _mm_unpackhi_ps(y, w); + return _mm_unpackhi_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeZ(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpackhi_ps(x, z); + const Vec4V yw = _mm_unpackhi_ps(y, w); + return _mm_unpacklo_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeY(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpacklo_ps(x, z); + const Vec4V yw = _mm_unpacklo_ps(y, w); + return _mm_unpackhi_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeX(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpacklo_ps(x, z); + const Vec4V yw = _mm_unpacklo_ps(y, w); + return _mm_unpacklo_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4UnpackXY(const Vec4VArg a, const Vec4VArg b) +{ + return _mm_unpacklo_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4UnpackZW(const Vec4VArg a, const Vec4VArg b) +{ + return _mm_unpackhi_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4UnitW() +{ + const PX_ALIGN(16, PxF32) w[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + const __m128 w128 = _mm_load_ps(w); + return w128; +} + +PX_FORCE_INLINE Vec4V V4UnitX() +{ + const PX_ALIGN(16, PxF32) x[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +PX_FORCE_INLINE Vec4V V4UnitY() +{ + const PX_ALIGN(16, PxF32) y[4] = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +PX_FORCE_INLINE Vec4V V4UnitZ() +{ + const PX_ALIGN(16, PxF32) z[4] = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +PX_FORCE_INLINE FloatV V4GetW(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +PX_FORCE_INLINE FloatV V4GetX(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE FloatV V4GetY(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE FloatV V4GetZ(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTTF(), v, f); +} + +PX_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4ClearW(const Vec4V v) +{ +#if !PX_EMSCRIPTEN + return _mm_and_ps(v, V4LoadA(internalUnitSSE2Simd::gMaskXYZ)); +#else + return _mm_and_ps(v, (VecI32V&)internalUnitSSE2Simd::gMaskXYZ); +#endif +} + +PX_FORCE_INLINE Vec4V V4PermYXWZ(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 0, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermXZXZ(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 0, 2, 0)); +} + +PX_FORCE_INLINE Vec4V V4PermYWYW(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 3, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermZWXY(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); +} + +template +PX_FORCE_INLINE Vec4V V4Perm(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(w, z, y, x)); +} + +PX_FORCE_INLINE Vec4V V4Zero() +{ + return V4Load(0.0f); +} + +PX_FORCE_INLINE Vec4V V4One() +{ + return V4Load(1.0f); +} + +PX_FORCE_INLINE Vec4V V4Eps() +{ + return V4Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec4V V4Neg(const Vec4V f) +{ + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b) +{ + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b) +{ + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b) +{ + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b) +{ + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec4V V4Recip(const Vec4V a) +{ + return _mm_div_ps(V4One(), a); +} + +PX_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a) +{ + return _mm_rcp_ps(a); +} + +PX_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a) +{ + return _mm_div_ps(V4One(), _mm_sqrt_ps(a)); +} + +PX_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a) +{ + return _mm_rsqrt_ps(a); +} + +PX_FORCE_INLINE Vec4V V4Sqrt(const Vec4V a) +{ + return _mm_sqrt_ps(a); +} + +PX_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return V4Add(V4Scale(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return V4Sub(c, V4Scale(a, b)); +} + +PX_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Add(V4Mul(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Sub(c, V4Mul(a, b)); +} + +PX_FORCE_INLINE Vec4V V4Abs(const Vec4V a) +{ + return V4Max(a, V4Neg(a)); +} + +PX_FORCE_INLINE FloatV V4SumElements(const Vec4V a) +{ +#ifdef __SSE4_2__ + Vec4V r = _mm_hadd_ps(a, a); + r = _mm_hadd_ps(r, r); + return r; +#else + const Vec4V xy = V4UnpackXY(a, a); // x,x,y,y + const Vec4V zw = V4UnpackZW(a, a); // z,z,w,w + const Vec4V xz_yw = V4Add(xy, zw); // x+z,x+z,y+w,y+w + const FloatV xz = V4GetX(xz_yw); // x+z + const FloatV yw = V4GetZ(xz_yw); // y+w + return FAdd(xz, yw); // sum +#endif +} + +PX_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b) +{ +#ifdef __SSE4_2__ + return _mm_dp_ps(a, b, 0xff); +#else + const __m128 dot1 = _mm_mul_ps(a, b); // x,y,z,w + const __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 1, 0, 3)); // w,x,y,z + const __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 0, 3, 2)); // z,w,x,y + const __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 3, 2, 1)); // y,z,w,x + return _mm_add_ps(_mm_add_ps(shuf2, shuf3), _mm_add_ps(dot1, shuf1)); +#endif +} + +PX_FORCE_INLINE FloatV V4Dot3(const Vec4V a, const Vec4V b) +{ +#ifdef __SSE4_2__ + return _mm_dp_ps(a, b, 0x7f); +#else + const __m128 dot1 = _mm_mul_ps(a, b); // w,z,y,x + const __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 0, 0, 0)); // z,y,x,w + const __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 1, 1, 1)); // y,x,w,z + const __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 2, 2, 2)); // x,w,z,y + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); +#endif +} + +PX_FORCE_INLINE Vec4V V4Cross(const Vec4V a, const Vec4V b) +{ + const __m128 r1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(l1, l2), _mm_mul_ps(r1, r2)); +} + +PX_FORCE_INLINE FloatV V4Length(const Vec4V a) +{ + return _mm_sqrt_ps(V4Dot(a, a)); +} + +PX_FORCE_INLINE FloatV V4LengthSq(const Vec4V a) +{ + return V4Dot(a, a); +} + +PX_FORCE_INLINE Vec4V V4Normalize(const Vec4V a) +{ + ASSERT_ISFINITELENGTH(a); + return V4ScaleInv(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +PX_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a) +{ + ASSERT_ISFINITELENGTH(a); + return V4ScaleInvFast(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +PX_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a, const Vec3V unsafeReturnValue) +{ + const __m128 eps = V3Eps(); + const __m128 length = V4Length(a); + const __m128 isGreaterThanZero = V4IsGrtr(length, eps); + return V4Sel(isGreaterThanZero, V4ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b) +{ + return m128_I2F(_mm_cmpeq_epi32(m128_F2I(a), m128_F2I(b))); +} + +PX_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b) +{ + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b) +{ + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Max(const Vec4V a, const Vec4V b) +{ + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b) +{ + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_max_ps(_mm_max_ps(a, shuf1), _mm_max_ps(shuf2, shuf3)); +} + +PX_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_min_ps(_mm_min_ps(a, shuf1), _mm_min_ps(shuf2, shuf3)); +} + +PX_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV) +{ + return V4Max(V4Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V4AllGrtr(const Vec4V a, const Vec4V b) +{ + return internalUnitSSE2Simd::BAllTrue4_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return internalUnitSSE2Simd::BAllTrue4_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq3(const Vec4V a, const Vec4V b) +{ + return internalUnitSSE2Simd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllEq(const Vec4V a, const Vec4V b) +{ + return internalUnitSSE2Simd::BAllTrue4_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AnyGrtr3(const Vec4V a, const Vec4V b) +{ + return internalUnitSSE2Simd::BAnyTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE Vec4V V4Round(const Vec4V a) +{ +#ifdef __SSE4_2__ + return _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); +#else + // return _mm_round_ps(a, 0x0); + const Vec4V half = V4Load(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const Vec4V aRound = V4Sub(V4Add(a, half), signBit); + __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +#endif +} + +PX_FORCE_INLINE Vec4V V4Sin(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V3 = V4Mul(V2, V1); + const Vec4V V5 = V4Mul(V3, V2); + const Vec4V V7 = V4Mul(V5, V2); + const Vec4V V9 = V4Mul(V7, V2); + const Vec4V V11 = V4Mul(V9, V2); + const Vec4V V13 = V4Mul(V11, V2); + const Vec4V V15 = V4Mul(V13, V2); + const Vec4V V17 = V4Mul(V15, V2); + const Vec4V V19 = V4Mul(V17, V2); + const Vec4V V21 = V4Mul(V19, V2); + const Vec4V V23 = V4Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec4V Result; + Result = V4MulAdd(S1, V3, V1); + Result = V4MulAdd(S2, V5, Result); + Result = V4MulAdd(S3, V7, Result); + Result = V4MulAdd(S4, V9, Result); + Result = V4MulAdd(S5, V11, Result); + Result = V4MulAdd(S6, V13, Result); + Result = V4MulAdd(S7, V15, Result); + Result = V4MulAdd(S8, V17, Result); + Result = V4MulAdd(S9, V19, Result); + Result = V4MulAdd(S10, V21, Result); + Result = V4MulAdd(S11, V23, Result); + + return Result; +} + +PX_FORCE_INLINE Vec4V V4Cos(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V4 = V4Mul(V2, V2); + const Vec4V V6 = V4Mul(V4, V2); + const Vec4V V8 = V4Mul(V4, V4); + const Vec4V V10 = V4Mul(V6, V4); + const Vec4V V12 = V4Mul(V6, V6); + const Vec4V V14 = V4Mul(V8, V6); + const Vec4V V16 = V4Mul(V8, V8); + const Vec4V V18 = V4Mul(V10, V8); + const Vec4V V20 = V4Mul(V10, V10); + const Vec4V V22 = V4Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec4V Result; + Result = V4MulAdd(C1, V2, V4One()); + Result = V4MulAdd(C2, V4, Result); + Result = V4MulAdd(C3, V6, Result); + Result = V4MulAdd(C4, V8, Result); + Result = V4MulAdd(C5, V10, Result); + Result = V4MulAdd(C6, V12, Result); + Result = V4MulAdd(C7, V14, Result); + Result = V4MulAdd(C8, V16, Result); + Result = V4MulAdd(C9, V18, Result); + Result = V4MulAdd(C10, V20, Result); + Result = V4MulAdd(C11, V22, Result); + + return Result; +} + +PX_FORCE_INLINE void V4Transpose(Vec4V& col0, Vec4V& col1, Vec4V& col2, Vec4V& col3) +{ + Vec4V tmp0 = _mm_unpacklo_ps(col0, col1); + Vec4V tmp2 = _mm_unpacklo_ps(col2, col3); + Vec4V tmp1 = _mm_unpackhi_ps(col0, col1); + Vec4V tmp3 = _mm_unpackhi_ps(col2, col3); + col0 = _mm_movelh_ps(tmp0, tmp2); + col1 = _mm_movehl_ps(tmp2, tmp0); + col2 = _mm_movelh_ps(tmp1, tmp3); + col3 = _mm_movehl_ps(tmp3, tmp1); +} + +////////////////////////////////// +// BoolV +////////////////////////////////// + +PX_FORCE_INLINE BoolV BFFFF() +{ + return _mm_setzero_ps(); +} + +PX_FORCE_INLINE BoolV BFFFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0,0xFFFFFFFF}; + const __m128 ffft=_mm_load_ps((float*)&f); + return ffft;*/ + return m128_I2F(_mm_set_epi32(-1, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFFTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0}; + const __m128 fftf=_mm_load_ps((float*)&f); + return fftf;*/ + return m128_I2F(_mm_set_epi32(0, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFFTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 fftt=_mm_load_ps((float*)&f); + return fftt;*/ + return m128_I2F(_mm_set_epi32(-1, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFTFF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0}; + const __m128 ftff=_mm_load_ps((float*)&f); + return ftff;*/ + return m128_I2F(_mm_set_epi32(0, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0xFFFFFFFF}; + const __m128 ftft=_mm_load_ps((float*)&f); + return ftft;*/ + return m128_I2F(_mm_set_epi32(-1, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0xFFFFFFFF,0}; + const __m128 fttf=_mm_load_ps((float*)&f); + return fttf;*/ + return m128_I2F(_mm_set_epi32(0, -1, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 fttt=_mm_load_ps((float*)&f); + return fttt;*/ + return m128_I2F(_mm_set_epi32(-1, -1, -1, 0)); +} + +PX_FORCE_INLINE BoolV BTFFF() +{ + // const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0}; + // const __m128 tfff=_mm_load_ps((float*)&f); + // return tfff; + return m128_I2F(_mm_set_epi32(0, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0xFFFFFFFF}; + const __m128 tfft=_mm_load_ps((float*)&f); + return tfft;*/ + return m128_I2F(_mm_set_epi32(-1, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0xFFFFFFFF,0}; + const __m128 tftf=_mm_load_ps((float*)&f); + return tftf;*/ + return m128_I2F(_mm_set_epi32(0, -1, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 tftt=_mm_load_ps((float*)&f); + return tftt;*/ + return m128_I2F(_mm_set_epi32(-1, -1, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTTFF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0,0}; + const __m128 ttff=_mm_load_ps((float*)&f); + return ttff;*/ + return m128_I2F(_mm_set_epi32(0, 0, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0,0xFFFFFFFF}; + const __m128 ttft=_mm_load_ps((float*)&f); + return ttft;*/ + return m128_I2F(_mm_set_epi32(-1, 0, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0}; + const __m128 tttf=_mm_load_ps((float*)&f); + return tttf;*/ + return m128_I2F(_mm_set_epi32(0, -1, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 tttt=_mm_load_ps((float*)&f); + return tttt;*/ + return m128_I2F(_mm_set_epi32(-1, -1, -1, -1)); +} + +PX_FORCE_INLINE BoolV BXMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0}; + const __m128 tfff=_mm_load_ps((float*)&f); + return tfff;*/ + return m128_I2F(_mm_set_epi32(0, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BYMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0}; + const __m128 ftff=_mm_load_ps((float*)&f); + return ftff;*/ + return m128_I2F(_mm_set_epi32(0, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BZMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0}; + const __m128 fftf=_mm_load_ps((float*)&f); + return fftf;*/ + return m128_I2F(_mm_set_epi32(0, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BWMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0,0xFFFFFFFF}; + const __m128 ffft=_mm_load_ps((float*)&f); + return ffft;*/ + return m128_I2F(_mm_set_epi32(-1, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BGetX(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BGetY(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE BoolV BGetZ(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE BoolV BGetW(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +PX_FORCE_INLINE BoolV BSetX(const BoolV v, const BoolV f) +{ + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetY(const BoolV v, const BoolV f) +{ + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetZ(const BoolV v, const BoolV f) +{ + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetW(const BoolV v, const BoolV f) +{ + return V4Sel(BTTTF(), v, f); +} + +PX_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b) +{ + return _mm_and_ps(a, b); +} + +PX_FORCE_INLINE BoolV BNot(const BoolV a) +{ + const BoolV bAllTrue(BTTTT()); + return _mm_xor_ps(a, bAllTrue); +} + +PX_FORCE_INLINE BoolV BAndNot(const BoolV a, const BoolV b) +{ + return _mm_andnot_ps(b, a); +} + +PX_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b) +{ + return _mm_or_ps(a, b); +} + +PX_FORCE_INLINE BoolV BAllTrue4(const BoolV a) +{ + const BoolV bTmp = + _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAnyTrue4(const BoolV a) +{ + const BoolV bTmp = + _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAllTrue3(const BoolV a) +{ + const BoolV bTmp = + _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAnyTrue3(const BoolV a) +{ + const BoolV bTmp = + _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE PxU32 BAllEq(const BoolV a, const BoolV b) +{ + const BoolV bTest = m128_I2F(_mm_cmpeq_epi32(m128_F2I(a), m128_F2I(b))); + return internalUnitSSE2Simd::BAllTrue4_R(bTest); +} + +PX_FORCE_INLINE PxU32 BAllEqTTTT(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)==15); +} + +PX_FORCE_INLINE PxU32 BAllEqFFFF(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)==0); +} + +PX_FORCE_INLINE PxU32 BGetBitMask(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)); +} + +////////////////////////////////// +// MAT33V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M33MulV3(const Mat33V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V& a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +PX_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V& A, const Vec3V b, const Vec3V c) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + Vec3V result = V3ScaleAdd(A.col0, x, c); + result = V3ScaleAdd(A.col1, y, result); + return V3ScaleAdd(A.col2, z, result); +} + +PX_FORCE_INLINE Mat33V M33MulM33(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(M33MulV3(a, b.col0), M33MulV3(a, b.col1), M33MulV3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Add(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Scale(const Mat33V& a, const FloatV& b) +{ + return Mat33V(V3Scale(a.col0, b), V3Scale(a.col1, b), V3Scale(a.col2, b)); +} + +PX_FORCE_INLINE Mat33V M33Inverse(const Mat33V& a) +{ + const BoolV tfft = BTFFT(); + const BoolV tttf = BTTTF(); + const FloatV zero = FZero(); + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = _mm_rcp_ps(dot); + const Vec3V mergeh = _mm_unpacklo_ps(cross12, cross01); + const Vec3V mergel = _mm_unpackhi_ps(cross12, cross01); + Vec3V colInv0 = _mm_unpacklo_ps(mergeh, cross20); + colInv0 = _mm_or_ps(_mm_andnot_ps(tttf, zero), _mm_and_ps(tttf, colInv0)); + const Vec3V zppd = _mm_shuffle_ps(mergeh, cross20, _MM_SHUFFLE(3, 0, 0, 2)); + const Vec3V pbwp = _mm_shuffle_ps(cross20, mergeh, _MM_SHUFFLE(3, 3, 1, 0)); + const Vec3V colInv1 = _mm_or_ps(_mm_andnot_ps(BTFFT(), pbwp), _mm_and_ps(BTFFT(), zppd)); + const Vec3V xppd = _mm_shuffle_ps(mergel, cross20, _MM_SHUFFLE(3, 0, 0, 0)); + const Vec3V pcyp = _mm_shuffle_ps(cross20, mergel, _MM_SHUFFLE(3, 1, 2, 0)); + const Vec3V colInv2 = _mm_or_ps(_mm_andnot_ps(tfft, pcyp), _mm_and_ps(tfft, xppd)); + + return Mat33V(_mm_mul_ps(colInv0, invDet), _mm_mul_ps(colInv1, invDet), _mm_mul_ps(colInv2, invDet)); +} + +PX_FORCE_INLINE Mat33V M33Trnsps(const Mat33V& a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +PX_FORCE_INLINE Mat33V M33Identity() +{ + return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ()); +} + +PX_FORCE_INLINE Mat33V M33Sub(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Sub(a.col0, b.col0), V3Sub(a.col1, b.col1), V3Sub(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Neg(const Mat33V& a) +{ + return Mat33V(V3Neg(a.col0), V3Neg(a.col1), V3Neg(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Abs(const Mat33V& a) +{ + return Mat33V(V3Abs(a.col0), V3Abs(a.col1), V3Abs(a.col2)); +} + +PX_FORCE_INLINE Mat33V PromoteVec3V(const Vec3V v) +{ + const BoolV bTFFF = BTFFF(); + const BoolV bFTFF = BFTFF(); + const BoolV bFFTF = BTFTF(); + + const Vec3V zero = V3Zero(); + + return Mat33V(V3Sel(bTFFF, v, zero), V3Sel(bFTFF, v, zero), V3Sel(bFFTF, v, zero)); +} + +PX_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg d) +{ + const FloatV x = V3Mul(V3UnitX(), d); + const FloatV y = V3Mul(V3UnitY(), d); + const FloatV z = V3Mul(V3UnitZ(), d); + return Mat33V(x, y, z); +} + +////////////////////////////////// +// MAT34V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M34MulV3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + const Vec3V v0PlusV1Plusv2 = V3Add(v0PlusV1, v2); + return V3Add(v0PlusV1Plusv2, a.col3); +} + +PX_FORCE_INLINE Vec3V M34Mul33V3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M34TrnspsMul33V3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +PX_FORCE_INLINE Mat34V M34MulM34(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2), M34MulV3(a, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34MulM33(const Mat34V& a, const Mat33V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M34Mul33MM34(const Mat34V& a, const Mat34V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat34V M34Add(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2), V3Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34Trnsps33(const Mat34V& a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +////////////////////////////////// +// MAT44V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V M44MulV4(const Mat44V& a, const Vec4V b) +{ + const FloatV x = V4GetX(b); + const FloatV y = V4GetY(b); + const FloatV z = V4GetZ(b); + const FloatV w = V4GetW(b); + + const Vec4V v0 = V4Scale(a.col0, x); + const Vec4V v1 = V4Scale(a.col1, y); + const Vec4V v2 = V4Scale(a.col2, z); + const Vec4V v3 = V4Scale(a.col3, w); + const Vec4V v0PlusV1 = V4Add(v0, v1); + const Vec4V v0PlusV1Plusv2 = V4Add(v0PlusV1, v2); + return V4Add(v0PlusV1Plusv2, v3); +} + +PX_FORCE_INLINE Vec4V M44TrnspsMulV4(const Mat44V& a, const Vec4V b) +{ + PX_ALIGN(16, FloatV) dotProdArray[4] = { V4Dot(a.col0, b), V4Dot(a.col1, b), V4Dot(a.col2, b), V4Dot(a.col3, b) }; + return V4Merge(dotProdArray); +} + +PX_FORCE_INLINE Mat44V M44MulM44(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(M44MulV4(a, b.col0), M44MulV4(a, b.col1), M44MulV4(a, b.col2), M44MulV4(a, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Add(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(V4Add(a.col0, b.col0), V4Add(a.col1, b.col1), V4Add(a.col2, b.col2), V4Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Trnsps(const Mat44V& a) +{ + const Vec4V v0 = _mm_unpacklo_ps(a.col0, a.col2); + const Vec4V v1 = _mm_unpackhi_ps(a.col0, a.col2); + const Vec4V v2 = _mm_unpacklo_ps(a.col1, a.col3); + const Vec4V v3 = _mm_unpackhi_ps(a.col1, a.col3); + return Mat44V(_mm_unpacklo_ps(v0, v2), _mm_unpackhi_ps(v0, v2), _mm_unpacklo_ps(v1, v3), _mm_unpackhi_ps(v1, v3)); +} + +PX_FORCE_INLINE Mat44V M44Inverse(const Mat44V& a) +{ + __m128 minor0, minor1, minor2, minor3; + __m128 row0, row1, row2, row3; + __m128 det, tmp1; + + tmp1 = V4Zero(); + row1 = V4Zero(); + row3 = V4Zero(); + + row0 = a.col0; + row1 = _mm_shuffle_ps(a.col1, a.col1, _MM_SHUFFLE(1, 0, 3, 2)); + row2 = a.col2; + row3 = _mm_shuffle_ps(a.col3, a.col3, _MM_SHUFFLE(1, 0, 3, 2)); + + tmp1 = _mm_mul_ps(row2, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_mul_ps(row1, tmp1); + minor1 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0); + minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1); + minor1 = _mm_shuffle_ps(minor1, minor1, 0x4E); + + tmp1 = _mm_mul_ps(row1, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0); + minor3 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3); + minor3 = _mm_shuffle_ps(minor3, minor3, 0x4E); + + tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, 0x4E), row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + row2 = _mm_shuffle_ps(row2, row2, 0x4E); + minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0); + minor2 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2); + minor2 = _mm_shuffle_ps(minor2, minor2, 0x4E); + + tmp1 = _mm_mul_ps(row0, row1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1)); + + tmp1 = _mm_mul_ps(row0, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1); + minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1)); + + tmp1 = _mm_mul_ps(row0, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1)); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3); + + det = _mm_mul_ps(row0, minor0); + det = _mm_add_ps(_mm_shuffle_ps(det, det, 0x4E), det); + det = _mm_add_ss(_mm_shuffle_ps(det, det, 0xB1), det); + tmp1 = _mm_rcp_ss(det); +#if 0 + det = _mm_sub_ss(_mm_add_ss(tmp1, tmp1), _mm_mul_ss(det, _mm_mul_ss(tmp1, tmp1))); + det = _mm_shuffle_ps(det, det, 0x00); +#else + det = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(0, 0, 0, 0)); +#endif + + minor0 = _mm_mul_ps(det, minor0); + minor1 = _mm_mul_ps(det, minor1); + minor2 = _mm_mul_ps(det, minor2); + minor3 = _mm_mul_ps(det, minor3); + Mat44V invTrans(minor0, minor1, minor2, minor3); + return M44Trnsps(invTrans); +} + +PX_FORCE_INLINE Vec4V V4LoadXYZW(const PxF32& x, const PxF32& y, const PxF32& z, const PxF32& w) +{ + return _mm_set_ps(w, z, y, x); +} + +/* +// AP: work in progress - use proper SSE intrinsics where possible +PX_FORCE_INLINE VecU16V V4U32PK(VecU32V a, VecU32V b) +{ + VecU16V result; + result.m128_u16[0] = PxU16(PxClamp((a).m128_u32[0], 0, 0xFFFF)); + result.m128_u16[1] = PxU16(PxClamp((a).m128_u32[1], 0, 0xFFFF)); + result.m128_u16[2] = PxU16(PxClamp((a).m128_u32[2], 0, 0xFFFF)); + result.m128_u16[3] = PxU16(PxClamp((a).m128_u32[3], 0, 0xFFFF)); + result.m128_u16[4] = PxU16(PxClamp((b).m128_u32[0], 0, 0xFFFF)); + result.m128_u16[5] = PxU16(PxClamp((b).m128_u32[1], 0, 0xFFFF)); + result.m128_u16[6] = PxU16(PxClamp((b).m128_u32[2], 0, 0xFFFF)); + result.m128_u16[7] = PxU16(PxClamp((b).m128_u32[3], 0, 0xFFFF)); + return result; +} +*/ + +PX_FORCE_INLINE VecU32V V4U32Sel(const BoolV c, const VecU32V a, const VecU32V b) +{ + return m128_I2F(_mm_or_si128(_mm_andnot_si128(m128_F2I(c), m128_F2I(b)), _mm_and_si128(m128_F2I(c), m128_F2I(a)))); +} + +PX_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_or_si128(m128_F2I(a), m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32xor(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_xor_si128(m128_F2I(a), m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_and_si128(m128_F2I(a), m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_andnot_si128(m128_F2I(b), m128_F2I(a))); +} + +/* +PX_FORCE_INLINE VecU16V V4U16Or(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_or_si128(m128_F2I(a), m128_F2I(b))); +} +*/ + +/* +PX_FORCE_INLINE VecU16V V4U16And(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_and_si128(m128_F2I(a), m128_F2I(b))); +} +*/ + +/* +PX_FORCE_INLINE VecU16V V4U16Andc(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_andnot_si128(m128_F2I(b), m128_F2I(a))); +} +*/ + +PX_FORCE_INLINE VecI32V I4Load(const PxI32 i) +{ + return m128_F2I(_mm_load1_ps(reinterpret_cast(&i))); +} + +PX_FORCE_INLINE VecI32V I4LoadU(const PxI32* i) +{ + return m128_F2I(_mm_loadu_ps(reinterpret_cast(i))); +} + +PX_FORCE_INLINE VecI32V I4LoadA(const PxI32* i) +{ + return m128_F2I(_mm_load_ps(reinterpret_cast(i))); +} + +PX_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_add_epi32(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_sub_epi32(a, b); +} + +PX_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_cmpgt_epi32(a, b)); +} + +PX_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_cmpeq_epi32(a, b)); +} + +PX_FORCE_INLINE VecI32V V4I32Sel(const BoolV c, const VecI32V a, const VecI32V b) +{ + return _mm_or_si128(_mm_andnot_si128(m128_F2I(c), b), _mm_and_si128(m128_F2I(c), a)); +} + +PX_FORCE_INLINE VecI32V VecI32V_Zero() +{ + return _mm_setzero_si128(); +} + +PX_FORCE_INLINE VecI32V VecI32V_One() +{ + return I4Load(1); +} + +PX_FORCE_INLINE VecI32V VecI32V_Two() +{ + return I4Load(2); +} + +PX_FORCE_INLINE VecI32V VecI32V_MinusOne() +{ + return I4Load(-1); +} + +PX_FORCE_INLINE VecU32V U4Zero() +{ + return U4Load(0); +} + +PX_FORCE_INLINE VecU32V U4One() +{ + return U4Load(1); +} + +PX_FORCE_INLINE VecU32V U4Two() +{ + return U4Load(2); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sel(const BoolV c, const VecI32VArg a, const VecI32VArg b) +{ + return _mm_or_si128(_mm_andnot_si128(m128_F2I(c), b), _mm_and_si128(m128_F2I(c), a)); +} + +PX_FORCE_INLINE VecShiftV VecI32V_PrepareShift(const VecI32VArg shift) +{ + VecShiftV s; + s.shift = VecI32V_Sel(BTFFF(), shift, VecI32V_Zero()); + return s; +} + +PX_FORCE_INLINE VecI32V VecI32V_LeftShift(const VecI32VArg a, const VecShiftVArg count) +{ + return _mm_sll_epi32(a, count.shift); +} + +PX_FORCE_INLINE VecI32V VecI32V_RightShift(const VecI32VArg a, const VecShiftVArg count) +{ + return _mm_srl_epi32(a, count.shift); +} + +PX_FORCE_INLINE VecI32V VecI32V_And(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_and_si128(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Or(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_or_si128(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetX(const VecI32VArg a) +{ + return m128_F2I(_mm_shuffle_ps(m128_I2F(a), m128_I2F(a), _MM_SHUFFLE(0, 0, 0, 0))); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetY(const VecI32VArg a) +{ + return m128_F2I(_mm_shuffle_ps(m128_I2F(a), m128_I2F(a), _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetZ(const VecI32VArg a) +{ + return m128_F2I(_mm_shuffle_ps(m128_I2F(a), m128_I2F(a), _MM_SHUFFLE(2, 2, 2, 2))); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetW(const VecI32VArg a) +{ + return m128_F2I(_mm_shuffle_ps(m128_I2F(a), m128_I2F(a), _MM_SHUFFLE(3, 3, 3, 3))); +} + +PX_FORCE_INLINE void PxI32_From_VecI32V(const VecI32VArg a, PxI32* i) +{ + _mm_store_ss(reinterpret_cast(i), m128_I2F(a)); +} + +PX_FORCE_INLINE VecI32V VecI32V_Merge(const VecI32VArg x, const VecI32VArg y, const VecI32VArg z, const VecI32VArg w) +{ + const __m128 xw = _mm_move_ss(m128_I2F(y), m128_I2F(x)); // y, y, y, x + const __m128 yz = _mm_move_ss(m128_I2F(z), m128_I2F(w)); // z, z, z, w + return m128_F2I(_mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0))); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_BoolV(const BoolVArg a) +{ + return m128_F2I(a); +} + +PX_FORCE_INLINE VecU32V VecU32V_From_BoolV(const BoolVArg a) +{ + return a; +} + +/* +template PX_FORCE_INLINE VecI32V V4ISplat() +{ + VecI32V result; + result.m128_i32[0] = a; + result.m128_i32[1] = a; + result.m128_i32[2] = a; + result.m128_i32[3] = a; + return result; +} + +template PX_FORCE_INLINE VecU32V V4USplat() +{ + VecU32V result; + result.m128_u32[0] = a; + result.m128_u32[1] = a; + result.m128_u32[2] = a; + result.m128_u32[3] = a; + return result; +} +*/ + +/* +PX_FORCE_INLINE void V4U16StoreAligned(VecU16V val, VecU16V* address) +{ + *address = val; +} +*/ + +PX_FORCE_INLINE void V4U32StoreAligned(VecU32V val, VecU32V* address) +{ + *address = val; +} + +PX_FORCE_INLINE Vec4V V4LoadAligned(Vec4V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE Vec4V V4LoadUnaligned(Vec4V* addr) +{ + return V4LoadU(reinterpret_cast(addr)); +} + +PX_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b) +{ + VecU32V result32(a); + result32 = V4U32Andc(result32, b); + return Vec4V(result32); +} + +PX_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b) +{ + return V4IsGrtr(a, b); +} + +PX_FORCE_INLINE VecU16V V4U16LoadAligned(VecU16V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE VecU16V V4U16LoadUnaligned(VecU16V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE VecU16V V4U16CompareGt(VecU16V a, VecU16V b) +{ + // _mm_cmpgt_epi16 doesn't work for unsigned values unfortunately + // return m128_I2F(_mm_cmpgt_epi16(m128_F2I(a), m128_F2I(b))); + VecU16V result; + result.m128_u16[0] = (a).m128_u16[0] > (b).m128_u16[0]; + result.m128_u16[1] = (a).m128_u16[1] > (b).m128_u16[1]; + result.m128_u16[2] = (a).m128_u16[2] > (b).m128_u16[2]; + result.m128_u16[3] = (a).m128_u16[3] > (b).m128_u16[3]; + result.m128_u16[4] = (a).m128_u16[4] > (b).m128_u16[4]; + result.m128_u16[5] = (a).m128_u16[5] > (b).m128_u16[5]; + result.m128_u16[6] = (a).m128_u16[6] > (b).m128_u16[6]; + result.m128_u16[7] = (a).m128_u16[7] > (b).m128_u16[7]; + return result; +} + +PX_FORCE_INLINE VecU16V V4I16CompareGt(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_cmpgt_epi16(m128_F2I(a), m128_F2I(b))); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a) +{ + Vec4V result = V4LoadXYZW(PxF32(a.m128_u32[0]), PxF32(a.m128_u32[1]), PxF32(a.m128_u32[2]), PxF32(a.m128_u32[3])); + return result; +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecI32V(VecI32V in) +{ + return _mm_cvtepi32_ps(in); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_Vec4V(Vec4V a) +{ + return _mm_cvttps_epi32(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecU32V(VecU32V a) +{ + return Vec4V(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecI32V(VecI32V a) +{ + return m128_I2F(a); +} + +PX_FORCE_INLINE VecU32V VecU32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return VecU32V(a); +} + +PX_FORCE_INLINE VecI32V VecI32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return m128_F2I(a); +} + +/* +template PX_FORCE_INLINE BoolV BSplatElement(BoolV a) +{ + BoolV result; + result[0] = result[1] = result[2] = result[3] = a[index]; + return result; +} +*/ + +template +BoolV BSplatElement(BoolV a) +{ + float* data = reinterpret_cast(&a); + return V4Load(data[index]); +} + +template +PX_FORCE_INLINE VecU32V V4U32SplatElement(VecU32V a) +{ + VecU32V result; + result.m128_u32[0] = result.m128_u32[1] = result.m128_u32[2] = result.m128_u32[3] = a.m128_u32[index]; + return result; +} + +template +PX_FORCE_INLINE Vec4V V4SplatElement(Vec4V a) +{ + float* data = reinterpret_cast(&a); + return V4Load(data[index]); +} + +PX_FORCE_INLINE VecU32V U4LoadXYZW(PxU32 x, PxU32 y, PxU32 z, PxU32 w) +{ + VecU32V result; + result.m128_u32[0] = x; + result.m128_u32[1] = y; + result.m128_u32[2] = z; + result.m128_u32[3] = w; + return result; +} + +PX_FORCE_INLINE Vec4V V4Ceil(const Vec4V in) +{ + UnionM128 a(in); + return V4LoadXYZW(PxCeil(a.m128_f32[0]), PxCeil(a.m128_f32[1]), PxCeil(a.m128_f32[2]), PxCeil(a.m128_f32[3])); +} + +PX_FORCE_INLINE Vec4V V4Floor(const Vec4V in) +{ + UnionM128 a(in); + return V4LoadXYZW(PxFloor(a.m128_f32[0]), PxFloor(a.m128_f32[1]), PxFloor(a.m128_f32[2]), PxFloor(a.m128_f32[3])); +} + +PX_FORCE_INLINE VecU32V V4ConvertToU32VSaturate(const Vec4V in, PxU32 power) +{ + PX_ASSERT(power == 0 && "Non-zero power not supported in convertToU32VSaturate"); + PX_UNUSED(power); // prevent warning in release builds + PxF32 ffffFFFFasFloat = PxF32(0xFFFF0000); + UnionM128 a(in); + VecU32V result; + result.m128_u32[0] = PxU32(PxClamp((a).m128_f32[0], 0.0f, ffffFFFFasFloat)); + result.m128_u32[1] = PxU32(PxClamp((a).m128_f32[1], 0.0f, ffffFFFFasFloat)); + result.m128_u32[2] = PxU32(PxClamp((a).m128_f32[2], 0.0f, ffffFFFFasFloat)); + result.m128_u32[3] = PxU32(PxClamp((a).m128_f32[3], 0.0f, ffffFFFFasFloat)); + return result; +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSUNIXSSE2INLINEAOS_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h new file mode 100644 index 000000000..2a2d71063 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSAOS_H +#define PSFOUNDATION_PSWINDOWSAOS_H + +// no includes here! this file should be included from PxcVecMath.h only!!! + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +typedef __m128 FloatV; +typedef __m128 Vec3V; +typedef __m128 Vec4V; +typedef __m128 BoolV; +typedef __m128 VecU32V; +typedef __m128 VecI32V; +typedef __m128 VecU16V; +typedef __m128 VecI16V; +typedef __m128 QuatV; + +#define FloatVArg FloatV & +#define Vec3VArg Vec3V & +#define Vec4VArg Vec4V & +#define BoolVArg BoolV & +#define VecU32VArg VecU32V & +#define VecI32VArg VecI32V & +#define VecU16VArg VecU16V & +#define VecI16VArg VecI16V & +#define QuatVArg QuatV & + +// Optimization for situations in which you cross product multiple vectors with the same vector. +// Avoids 2X shuffles per product +struct VecCrossV +{ + Vec3V mL1; + Vec3V mR1; +}; + +struct VecShiftV +{ + VecI32V shift; +}; +#define VecShiftVArg VecShiftV & + +PX_ALIGN_PREFIX(16) +struct Mat33V +{ + Mat33V() + { + } + Mat33V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat34V +{ + Mat34V() + { + } + Mat34V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2, const Vec3V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); + Vec3V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat43V +{ + Mat43V() + { + } + Mat43V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat44V +{ + Mat44V() + { + } + Mat44V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2, const Vec4V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); + Vec4V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSWINDOWSAOS_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h new file mode 100644 index 000000000..019bfc9a0 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h @@ -0,0 +1,51 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSFPU_H +#define PSFOUNDATION_PSWINDOWSFPU_H + +PX_INLINE physx::shdfnd::SIMDGuard::SIMDGuard() +{ +#if !PX_ARM + mControlWord = _mm_getcsr(); + // set default (disable exceptions: _MM_MASK_MASK) and FTZ (_MM_FLUSH_ZERO_ON), DAZ (_MM_DENORMALS_ZERO_ON: (1<<6)) + _mm_setcsr(_MM_MASK_MASK | _MM_FLUSH_ZERO_ON | (1 << 6)); +#endif +} + +PX_INLINE physx::shdfnd::SIMDGuard::~SIMDGuard() +{ +#if !PX_ARM + // restore control word and clear any exception flags + // (setting exception state flags cause exceptions on the first following fp operation) + _mm_setcsr(mControlWord & ~_MM_EXCEPT_MASK); +#endif +} + +#endif // #ifndef PSFOUNDATION_PSWINDOWSFPU_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h new file mode 100644 index 000000000..4dab60145 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h @@ -0,0 +1,104 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSINCLUDE_H +#define PSFOUNDATION_PSWINDOWSINCLUDE_H + +#include "Ps.h" + +#ifndef _WIN32 +#error "This file should only be included by Windows builds!!" +#endif + +#ifdef _WINDOWS_ // windows already included +#error "Only include windows.h through this file!!" +#endif + +// We only support >= Windows XP, and we need this for critical section and +// Setting this hides some important APIs (e.g. LoadPackagedLibrary), so don't do it +#if !PX_UWP +#define _WIN32_WINNT 0x0501 +#else +#define _WIN32_WINNT 0x0602 +#endif + +// turn off as much as we can for windows. All we really need is the thread functions(critical sections/Interlocked* +// etc) +#define NOGDICAPMASKS +#define NOVIRTUALKEYCODES +#define NOWINMESSAGES +#define NOWINSTYLES +#define NOSYSMETRICS +#define NOMENUS +#define NOICONS +#define NOKEYSTATES +#define NOSYSCOMMANDS +#define NORASTEROPS +#define NOSHOWWINDOW +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCTLMGR +#define NODRAWTEXT +#define NOGDI +#define NOMB +#define NOMEMMGR +#define NOMETAFILE +#define NOMINMAX +#define NOOPENFILE +#define NOSCROLL +#define NOSERVICE +#define NOSOUND +#define NOTEXTMETRIC +#define NOWH +#define NOWINOFFSETS +#define NOCOMM +#define NOKANJI +#define NOHELP +#define NOPROFILER +#define NODEFERWINDOWPOS +#define NOMCX +#define WIN32_LEAN_AND_MEAN +// We need a slightly wider API surface for e.g. MultiByteToWideChar +#if !PX_UWP +#define NOUSER +#define NONLS +#define NOMSG +#endif + +#pragma warning(push) +#pragma warning(disable : 4668) //'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#include +#pragma warning(pop) + +#if PX_SSE2 +#include +#endif + +#endif // #ifndef PSFOUNDATION_PSWINDOWSINCLUDE_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h new file mode 100644 index 000000000..887535d4a --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h @@ -0,0 +1,3132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSINLINEAOS_H +#define PSFOUNDATION_PSWINDOWSINLINEAOS_H + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +#include "../PsVecMathSSE.h" + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +////////////////////////////////////////////////////////////////////// +//Test that Vec3V and FloatV are legal +////////////////////////////////////////////////////////////////////// + +#define FLOAT_COMPONENTS_EQUAL_THRESHOLD 0.01f +PX_FORCE_INLINE bool isValidFloatV(const FloatV a) +{ + const PxF32 x = V4ReadX(a); + const PxF32 y = V4ReadY(a); + const PxF32 z = V4ReadZ(a); + const PxF32 w = V4ReadW(a); + + if ( + (PxAbs(x - y) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs(x - z) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs(x - w) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + ) + { + return true; + } + + if ( + (PxAbs((x - y) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs((x - z) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs((x - w) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + ) + { + return true; + } + return false; +} + +PX_FORCE_INLINE bool isValidVec3V(const Vec3V a) +{ + //using _mm_comieq_ss to do the comparison doesn't work for NaN. + PX_ALIGN(16, PxF32 f[4]); + V4StoreA((const Vec4V&)a, f); + return f[3] == 0.0f; +} + +PX_FORCE_INLINE bool isFiniteLength(const Vec3V a) +{ + return !FAllEq(V4LengthSq(a), FZero()); +} + +PX_FORCE_INLINE bool isAligned16(void* a) +{ + return(0 == ((size_t)a & 0x0f)); +} + +//ASSERT_FINITELENGTH is deactivated because there is a lot of code that calls a simd normalisation function with zero length but then ignores the result. + +#if PX_DEBUG +#define ASSERT_ISVALIDVEC3V(a) PX_ASSERT(isValidVec3V(a)) +#define ASSERT_ISVALIDFLOATV(a) PX_ASSERT(isValidFloatV(a)) +#define ASSERT_ISALIGNED16(a) PX_ASSERT(isAligned16((void*)a)) +#define ASSERT_ISFINITELENGTH(a) //PX_ASSERT(isFiniteLength(a)) +#else +#define ASSERT_ISVALIDVEC3V(a) +#define ASSERT_ISVALIDFLOATV(a) +#define ASSERT_ISALIGNED16(a) +#define ASSERT_ISFINITELENGTH(a) +#endif +///////////////////////////////////////////////////////////////////// +////FUNCTIONS USED ONLY FOR ASSERTS IN VECTORISED IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// USED ONLY INTERNALLY +////////////////////////////////////////////////////////////////////// + +namespace internalWindowsSimd +{ +PX_FORCE_INLINE __m128 m128_I2F(__m128i n) +{ + return _mm_castsi128_ps(n); +} + +PX_FORCE_INLINE __m128i m128_F2I(__m128 n) +{ + return _mm_castps_si128(n); +} + +PX_FORCE_INLINE PxU32 BAllTrue4_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32(moveMask == 0xf); +} + +PX_FORCE_INLINE PxU32 BAllTrue3_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32((moveMask & 0x7) == 0x7); +} + +PX_FORCE_INLINE PxU32 BAnyTrue4_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32(moveMask != 0x0); +} + +PX_FORCE_INLINE PxU32 BAnyTrue3_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32(((moveMask & 0x7) != 0x0)); +} + +PX_FORCE_INLINE PxU32 FiniteTestEq(const Vec4V a, const Vec4V b) +{ + // This is a bit of a bodge. + //_mm_comieq_ss returns 1 if either value is nan so we need to re-cast a and b with true encoded as a non-nan + // number. + // There must be a better way of doing this in sse. + const BoolV one = FOne(); + const BoolV zero = FZero(); + const BoolV a1 = V4Sel(a, one, zero); + const BoolV b1 = V4Sel(b, one, zero); + return (PxU32( + _mm_comieq_ss(a1, b1) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(1, 1, 1, 1)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(1, 1, 1, 1))) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(2, 2, 2, 2)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(2, 2, 2, 2))) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(3, 3, 3, 3)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(3, 3, 3, 3))))); +} + +PX_FORCE_INLINE bool hasZeroElementinFloatV(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) ? true : false; +} + +PX_FORCE_INLINE bool hasZeroElementInVec3V(const Vec3V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FZero())); +} + +PX_FORCE_INLINE bool hasZeroElementInVec4V(const Vec4V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)), FZero())); +} + +const PX_ALIGN(16, PxU32 gMaskXYZ[4]) = { 0xffffffff, 0xffffffff, 0xffffffff, 0 }; +} //internalWindowsSimd + +namespace _VecMathTests +{ +// PT: this function returns an invalid Vec3V (W!=0.0f) just for unit-testing 'isValidVec3V' +PX_FORCE_INLINE Vec3V getInvalidVec3V() +{ + const float f = 1.0f; + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE bool allElementsEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_comieq_ss(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec3V(const Vec3V a, const Vec3V b) +{ + return V3AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec4V(const Vec4V a, const Vec4V b) +{ + return V4AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualBoolV(const BoolV a, const BoolV b) +{ + return internalWindowsSimd::BAllTrue4_R(VecI32V_IsEq(a, b)) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVecU32V(const VecU32V a, const VecU32V b) +{ + return internalWindowsSimd::BAllTrue4_R(V4IsEqU32(a, b)) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVecI32V(const VecI32V a, const VecI32V b) +{ + BoolV c = internalWindowsSimd::m128_I2F( + _mm_cmpeq_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); + return internalWindowsSimd::BAllTrue4_R(c) != 0; +} + +#define VECMATH_AOS_EPSILON (1e-3f) +static const FloatV minFError = FLoad(-VECMATH_AOS_EPSILON); +static const FloatV maxFError = FLoad(VECMATH_AOS_EPSILON); +static const Vec3V minV3Error = V3Load(-VECMATH_AOS_EPSILON); +static const Vec3V maxV3Error = V3Load(VECMATH_AOS_EPSILON); +static const Vec4V minV4Error = V4Load(-VECMATH_AOS_EPSILON); +static const Vec4V maxV4Error = V4Load(VECMATH_AOS_EPSILON); + +PX_FORCE_INLINE bool allElementsNearEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + const FloatV c = FSub(a, b); + return _mm_comigt_ss(c, minFError) && _mm_comilt_ss(c, maxFError); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec3V(const Vec3V a, const Vec3V b) +{ + const Vec3V c = V3Sub(a, b); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minV3Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxV3Error) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minV3Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxV3Error) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minV3Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxV3Error)); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec4V(const Vec4V a, const Vec4V b) +{ + const Vec4V c = V4Sub(a, b); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minV4Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxV4Error) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minV4Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxV4Error) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minV4Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxV4Error) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), minV4Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), maxV4Error)); +} +} //_VecMathTests + +PX_FORCE_INLINE bool isFiniteFloatV(const FloatV a) +{ + PxF32 f; + FStore(a, &f); + return PxIsFinite(f); + /* + const PxU32 badNumber = (_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF); + const FloatV vBadNum = FloatV_From_F32((PxF32&)badNumber); + const BoolV vMask = BAnd(vBadNum, a); + return FiniteTestEq(vMask, BFFFF()) == 1; + */ +} + +PX_FORCE_INLINE bool isFiniteVec3V(const Vec3V a) +{ + PX_ALIGN(16, PxF32 f[4]); + V4StoreA((Vec4V&)a, f); + return PxIsFinite(f[0]) && PxIsFinite(f[1]) && PxIsFinite(f[2]); + + /* + const PxU32 badNumber = (_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF); + const Vec3V vBadNum = Vec3V_From_F32((PxF32&)badNumber); + const BoolV vMask = BAnd(BAnd(vBadNum, a), BTTTF()); + return FiniteTestEq(vMask, BFFFF()) == 1; + */ +} + +PX_FORCE_INLINE bool isFiniteVec4V(const Vec4V a) +{ + PX_ALIGN(16, PxF32 f[4]); + V4StoreA(a, f); + return PxIsFinite(f[0]) && PxIsFinite(f[1]) && PxIsFinite(f[2]) && PxIsFinite(f[3]); + + /* + const PxU32 badNumber = (_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF); + const Vec4V vBadNum = Vec4V_From_U32((PxF32&)badNumber); + const BoolV vMask = BAnd(vBadNum, a); + + return FiniteTestEq(vMask, BFFFF()) == 1; + */ +} + +///////////////////////////////////////////////////////////////////// +////VECTORISED FUNCTION IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE FloatV FLoad(const PxF32 f) +{ + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE Vec3V V3Load(const PxF32 f) +{ + return _mm_set_ps(0.0f, f, f, f); +} + +PX_FORCE_INLINE Vec4V V4Load(const PxF32 f) +{ + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool f) +{ + const PxU32 i = PxU32(-(PxI32)f); + return _mm_load1_ps((float*)&i); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + return _mm_and_ps(_mm_load_ps(&f.x), reinterpret_cast(internalWindowsSimd::gMaskXYZ)); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxVec3& f) +{ + return _mm_set_ps(0.0f, f.z, f.y, f.x); +} + +// w component of result is undefined +PX_FORCE_INLINE Vec3V V3LoadUnsafeA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + return _mm_load_ps(&f.x); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(f); + return V4ClearW(_mm_load_ps(f)); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxF32* const i) +{ + return _mm_set_ps(0.0f, i[2], i[1], i[0]); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V v) +{ + return V4ClearW(v); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V_WUndefined(const Vec4V v) +{ + return v; +} + +PX_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f) +{ + return f; // ok if it is implemented as the same type. +} + +PX_FORCE_INLINE Vec4V Vec4V_From_FloatV(FloatV f) +{ + return f; +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV(FloatV f) +{ + return Vec3V_From_Vec4V(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV_WUndefined(FloatV f) +{ + return Vec3V_From_Vec4V_WUndefined(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_PxVec3_WUndefined(const PxVec3& f) +{ + return _mm_set_ps(0.0f, f.z, f.y, f.x); +} + +PX_FORCE_INLINE Vec4V V4LoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(f); + return _mm_load_ps(f); +} + +PX_FORCE_INLINE void V4StoreA(const Vec4V a, PxF32* f) +{ + ASSERT_ISALIGNED16(f); + _mm_store_ps(f, a); +} + +PX_FORCE_INLINE void V4StoreU(const Vec4V a, PxF32* f) +{ + _mm_storeu_ps(f, a); +} + +PX_FORCE_INLINE void BStoreA(const BoolV a, PxU32* f) +{ + ASSERT_ISALIGNED16(f); + _mm_store_ps((PxF32*)f, a); +} + +PX_FORCE_INLINE void U4StoreA(const VecU32V uv, PxU32* u) +{ + ASSERT_ISALIGNED16(u); + _mm_store_ps((PxF32*)u, uv); +} + +PX_FORCE_INLINE void I4StoreA(const VecI32V iv, PxI32* i) +{ + ASSERT_ISALIGNED16(i); + _mm_store_ps((PxF32*)i, iv); +} + +PX_FORCE_INLINE Vec4V V4LoadU(const PxF32* const f) +{ + return _mm_loadu_ps(f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool* const f) +{ + const PX_ALIGN(16, PxU32 b[4]) = { PxU32(-(PxI32)f[0]), PxU32(-(PxI32)f[1]), + PxU32(-(PxI32)f[2]), PxU32(-(PxI32)f[3]) }; + return _mm_load_ps((float*)&b); +} + +PX_FORCE_INLINE void FStore(const FloatV a, PxF32* PX_RESTRICT f) +{ + ASSERT_ISVALIDFLOATV(a); + _mm_store_ss(f, a); +} + +PX_FORCE_INLINE void V3StoreA(const Vec3V a, PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + PX_ALIGN(16, PxF32 f2[4]); + _mm_store_ps(f2, a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +PX_FORCE_INLINE void Store_From_BoolV(const BoolV b, PxU32* b2) +{ + _mm_store_ss((PxF32*)b2, b); +} + +PX_FORCE_INLINE void V3StoreU(const Vec3V a, PxVec3& f) +{ + PX_ALIGN(16, PxF32 f2[4]); + _mm_store_ps(f2, a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +PX_FORCE_INLINE Mat33V Mat33V_From_PxMat33(const PxMat33& m) +{ + return Mat33V(V3LoadU(m.column0), V3LoadU(m.column1), V3LoadU(m.column2)); +} + +PX_FORCE_INLINE void PxMat33_From_Mat33V(const Mat33V& m, PxMat33& out) +{ + ASSERT_ISALIGNED16(&out); + V3StoreU(m.col0, out.column0); + V3StoreU(m.col1, out.column1); + V3StoreU(m.col2, out.column2); +} + +////////////////////////////////// +// FLOATV +////////////////////////////////// + +PX_FORCE_INLINE FloatV FZero() +{ + return _mm_setzero_ps(); +} + +PX_FORCE_INLINE FloatV FOne() +{ + return FLoad(1.0f); +} + +PX_FORCE_INLINE FloatV FHalf() +{ + return FLoad(0.5f); +} + +PX_FORCE_INLINE FloatV FEps() +{ + return FLoad(PX_EPS_REAL); +} + +PX_FORCE_INLINE FloatV FEps6() +{ + return FLoad(1e-6f); +} + +PX_FORCE_INLINE FloatV FMax() +{ + return FLoad(PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV FNegMax() +{ + return FLoad(-PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV IZero() +{ + const PxU32 zero = 0; + return _mm_load1_ps((PxF32*)&zero); +} + +PX_FORCE_INLINE FloatV IOne() +{ + const PxU32 one = 1; + return _mm_load1_ps((PxF32*)&one); +} + +PX_FORCE_INLINE FloatV ITwo() +{ + const PxU32 two = 2; + return _mm_load1_ps((PxF32*)&two); +} + +PX_FORCE_INLINE FloatV IThree() +{ + const PxU32 three = 3; + return _mm_load1_ps((PxF32*)&three); +} + +PX_FORCE_INLINE FloatV IFour() +{ + const PxU32 four = 4; + return _mm_load1_ps((PxF32*)&four); +} + +PX_FORCE_INLINE FloatV FNeg(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE FloatV FRecip(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_div_ps(FOne(), a); +} + +PX_FORCE_INLINE FloatV FRecipFast(const FloatV a) +{ + return _mm_rcp_ps(a); +} + +PX_FORCE_INLINE FloatV FRsqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_div_ps(FOne(), _mm_sqrt_ps(a)); +} + +PX_FORCE_INLINE FloatV FSqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_sqrt_ps(a); +} + +PX_FORCE_INLINE FloatV FRsqrtFast(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_rsqrt_ps(a); +} + +PX_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return FAdd(FMul(a, b), c); +} + +PX_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return FSub(c, FMul(a, b)); +} + +PX_FORCE_INLINE FloatV FAbs(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + PX_ALIGN(16, const static PxU32 absMask[4]) = { 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF }; + return _mm_and_ps(a, _mm_load_ps((PxF32*)absMask)); +} + +PX_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b) +{ + PX_ASSERT(_VecMathTests::allElementsEqualBoolV(c, BTTTT()) || + _VecMathTests::allElementsEqualBoolV(c, BFFFF())); + ASSERT_ISVALIDFLOATV(_mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a))); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV) +{ + ASSERT_ISVALIDFLOATV(minV); + ASSERT_ISVALIDFLOATV(maxV); + return _mm_max_ps(_mm_min_ps(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 FAllGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return PxU32(_mm_comigt_ss(a, b)); +} + +PX_FORCE_INLINE PxU32 FAllGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return PxU32(_mm_comige_ss(a, b)); +} + +PX_FORCE_INLINE PxU32 FAllEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return PxU32(_mm_comieq_ss(a, b)); +} + +PX_FORCE_INLINE FloatV FRound(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + // return _mm_round_ps(a, 0x0); + const FloatV half = FLoad(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const FloatV aRound = FSub(FAdd(a, half), signBit); + __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +} + +PX_FORCE_INLINE FloatV FSin(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = V4LoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V3 = FMul(V2, V1); + const FloatV V5 = FMul(V3, V2); + const FloatV V7 = FMul(V5, V2); + const FloatV V9 = FMul(V7, V2); + const FloatV V11 = FMul(V9, V2); + const FloatV V13 = FMul(V11, V2); + const FloatV V15 = FMul(V13, V2); + const FloatV V17 = FMul(V15, V2); + const FloatV V19 = FMul(V17, V2); + const FloatV V21 = FMul(V19, V2); + const FloatV V23 = FMul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + FloatV Result; + Result = FScaleAdd(S1, V3, V1); + Result = FScaleAdd(S2, V5, Result); + Result = FScaleAdd(S3, V7, Result); + Result = FScaleAdd(S4, V9, Result); + Result = FScaleAdd(S5, V11, Result); + Result = FScaleAdd(S6, V13, Result); + Result = FScaleAdd(S7, V15, Result); + Result = FScaleAdd(S8, V17, Result); + Result = FScaleAdd(S9, V19, Result); + Result = FScaleAdd(S10, V21, Result); + Result = FScaleAdd(S11, V23, Result); + + return Result; +} + +PX_FORCE_INLINE FloatV FCos(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = V4LoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V4 = FMul(V2, V2); + const FloatV V6 = FMul(V4, V2); + const FloatV V8 = FMul(V4, V4); + const FloatV V10 = FMul(V6, V4); + const FloatV V12 = FMul(V6, V6); + const FloatV V14 = FMul(V8, V6); + const FloatV V16 = FMul(V8, V8); + const FloatV V18 = FMul(V10, V8); + const FloatV V20 = FMul(V10, V10); + const FloatV V22 = FMul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + FloatV Result; + Result = FScaleAdd(C1, V2, V4One()); + Result = FScaleAdd(C2, V4, Result); + Result = FScaleAdd(C3, V6, Result); + Result = FScaleAdd(C4, V8, Result); + Result = FScaleAdd(C5, V10, Result); + Result = FScaleAdd(C6, V12, Result); + Result = FScaleAdd(C7, V14, Result); + Result = FScaleAdd(C8, V16, Result); + Result = FScaleAdd(C9, V18, Result); + Result = FScaleAdd(C10, V20, Result); + Result = FScaleAdd(C11, V22, Result); + + return Result; +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max); + const BoolV c = BOr(FIsGrtr(a, max), FIsGrtr(min, a)); + return PxU32(!BAllEqFFFF(c)); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max); + const BoolV c = BAnd(FIsGrtrOrEq(a, min), FIsGrtrOrEq(max, a)); + return BAllEqTTTT(c); +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + return FOutOfBounds(a, FNeg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + return FInBounds(a, FNeg(bounds), bounds); +} + +////////////////////////////////// +// VEC3V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V V3Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + const __m128 zero = V3Zero(); + const __m128 fff0 = _mm_move_ss(f, zero); + return _mm_shuffle_ps(fff0, fff0, _MM_SHUFFLE(0, 1, 2, 3)); +} + +PX_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + // static on zero causes compiler crash on x64 debug_opt + const __m128 zero = V3Zero(); + const __m128 xy = _mm_move_ss(x, y); + const __m128 z0 = _mm_move_ss(zero, z); + + return _mm_shuffle_ps(xy, z0, _MM_SHUFFLE(1, 0, 0, 1)); +} + +PX_FORCE_INLINE Vec3V V3UnitX() +{ + const PX_ALIGN(16, PxF32 x[4]) = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +PX_FORCE_INLINE Vec3V V3UnitY() +{ + const PX_ALIGN(16, PxF32 y[4]) = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +PX_FORCE_INLINE Vec3V V3UnitZ() +{ + const PX_ALIGN(16, PxF32 z[4]) = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +PX_FORCE_INLINE FloatV V3GetX(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE FloatV V3GetY(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE FloatV V3GetZ(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 0, 3, 0)); + return V3SetY(r, V3GetX(b)); +} + +PX_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 1, 3, 1)); + return V3SetY(r, V3GetY(b)); +} + +PX_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 2, 3, 2)); + return V3SetY(r, V3GetZ(b)); +} + +PX_FORCE_INLINE Vec3V V3Zero() +{ + return _mm_setzero_ps(); +} + +PX_FORCE_INLINE Vec3V V3One() +{ + return V3Load(1.0f); +} + +PX_FORCE_INLINE Vec3V V3Eps() +{ + return V3Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec3V V3Neg(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return V4ClearW(_mm_div_ps(a, b)); +} + +PX_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return V4ClearW(_mm_mul_ps(a, _mm_rcp_ps(b))); +} + +PX_FORCE_INLINE Vec3V V3Recip(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_div_ps(V3One(), a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rcp_ps(a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_div_ps(V3One(), _mm_sqrt_ps(a)); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rsqrt_ps(a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + return V3Add(V3Scale(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + return V3Sub(c, V3Scale(a, b)); +} + +PX_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return V3Add(V3Mul(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return V3Sub(c, V3Mul(a, b)); +} + +PX_FORCE_INLINE Vec3V V3Abs(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return V3Max(a, V3Neg(a)); +} + +PX_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + + const __m128 t0 = _mm_mul_ps(a, b); // aw*bw | az*bz | ay*by | ax*bx + const __m128 t1 = _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(1,0,3,2)); // ay*by | ax*bx | aw*bw | az*bz + const __m128 t2 = _mm_add_ps(t0, t1); // ay*by + aw*bw | ax*bx + az*bz | aw*bw + ay*by | az*bz + ax*bx + const __m128 t3 = _mm_shuffle_ps(t2, t2, _MM_SHUFFLE(2,3,0,1)); // ax*bx + az*bz | ay*by + aw*bw | az*bz + ax*bx | aw*bw + ay*by + return _mm_add_ps(t3, t2); // ax*bx + az*bz + ay*by + aw*bw + // ay*by + aw*bw + ax*bx + az*bz + // az*bz + ax*bx + aw*bw + ay*by + // aw*bw + ay*by + az*bz + ax*bx +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + const __m128 r1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(l1, l2), _mm_mul_ps(r1, r2)); +} + +PX_FORCE_INLINE VecCrossV V3PrepareCross(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + VecCrossV v; + v.mR1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + v.mL1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + return v; +} + +PX_FORCE_INLINE Vec3V V3Cross(const VecCrossV& a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(b); + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(a.mL1, l2), _mm_mul_ps(a.mR1, r2)); +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const VecCrossV& b) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 r2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(b.mR1, r2), _mm_mul_ps(b.mL1, l2)); +} + +PX_FORCE_INLINE Vec3V V3Cross(const VecCrossV& a, const VecCrossV& b) +{ + return _mm_sub_ps(_mm_mul_ps(a.mL1, b.mR1), _mm_mul_ps(a.mR1, b.mL1)); +} + +PX_FORCE_INLINE FloatV V3Length(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_sqrt_ps(V3Dot(a, a)); +} + +PX_FORCE_INLINE FloatV V3LengthSq(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return V3Dot(a, a); +} + +PX_FORCE_INLINE Vec3V V3Normalize(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISFINITELENGTH(a); + return V3ScaleInv(a, _mm_sqrt_ps(V3Dot(a, a))); +} + +PX_FORCE_INLINE Vec3V V3NormalizeFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISFINITELENGTH(a); + return V3Scale(a, _mm_rsqrt_ps(V3Dot(a, a))); +} + +PX_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a, const Vec3V unsafeReturnValue) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 eps = FEps(); + const __m128 length = V3Length(a); + const __m128 isGreaterThanZero = FIsGrtr(length, eps); + return V3Sel(isGreaterThanZero, V3ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(_mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a))); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + return _mm_max_ps(_mm_max_ps(shuf1, shuf2), shuf3); +} + +PX_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + return _mm_min_ps(_mm_min_ps(shuf1, shuf2), shuf3); +} + +//// if(a > 0.0f) return 1.0f; else if a == 0.f return 0.f, else return -1.f; +// PX_FORCE_INLINE Vec3V V3MathSign(const Vec3V a) +//{ +// VECMATHAOS_ASSERT(isValidVec3V(a)); +// +// const __m128i ai = _mm_cvtps_epi32(a); +// const __m128i bi = _mm_cvtps_epi32(V3Neg(a)); +// const __m128 aa = _mm_cvtepi32_ps(_mm_srai_epi32(ai, 31)); +// const __m128 bb = _mm_cvtepi32_ps(_mm_srai_epi32(bi, 31)); +// return _mm_or_ps(aa, bb); +//} + +// return (a >= 0.0f) ? 1.0f : -1.0f; +PX_FORCE_INLINE Vec3V V3Sign(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 one = V3One(); + const __m128 none = V3Neg(one); + return V3Sel(V3IsGrtrOrEq(a, zero), one, none); +} + +PX_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(minV); + ASSERT_ISVALIDVEC3V(maxV); + return V3Max(V3Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V3AllGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalWindowsSimd::BAllTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalWindowsSimd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalWindowsSimd::BAllTrue3_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE Vec3V V3Round(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // return _mm_round_ps(a, 0x0); + const Vec3V half = V3Load(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const Vec3V aRound = V3Sub(V3Add(a, half), signBit); + __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +} + +PX_FORCE_INLINE Vec3V V3Sin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V3Scale(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegScaleSub(b, twoPi, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V3 = V3Mul(V2, V1); + const Vec3V V5 = V3Mul(V3, V2); + const Vec3V V7 = V3Mul(V5, V2); + const Vec3V V9 = V3Mul(V7, V2); + const Vec3V V11 = V3Mul(V9, V2); + const Vec3V V13 = V3Mul(V11, V2); + const Vec3V V15 = V3Mul(V13, V2); + const Vec3V V17 = V3Mul(V15, V2); + const Vec3V V19 = V3Mul(V17, V2); + const Vec3V V21 = V3Mul(V19, V2); + const Vec3V V23 = V3Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec3V Result; + Result = V3ScaleAdd(V3, S1, V1); + Result = V3ScaleAdd(V5, S2, Result); + Result = V3ScaleAdd(V7, S3, Result); + Result = V3ScaleAdd(V9, S4, Result); + Result = V3ScaleAdd(V11, S5, Result); + Result = V3ScaleAdd(V13, S6, Result); + Result = V3ScaleAdd(V15, S7, Result); + Result = V3ScaleAdd(V17, S8, Result); + Result = V3ScaleAdd(V19, S9, Result); + Result = V3ScaleAdd(V21, S10, Result); + Result = V3ScaleAdd(V23, S11, Result); + + ASSERT_ISVALIDVEC3V(Result); + return Result; +} + +PX_FORCE_INLINE Vec3V V3Cos(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V3Scale(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegScaleSub(b, twoPi, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V4 = V3Mul(V2, V2); + const Vec3V V6 = V3Mul(V4, V2); + const Vec3V V8 = V3Mul(V4, V4); + const Vec3V V10 = V3Mul(V6, V4); + const Vec3V V12 = V3Mul(V6, V6); + const Vec3V V14 = V3Mul(V8, V6); + const Vec3V V16 = V3Mul(V8, V8); + const Vec3V V18 = V3Mul(V10, V8); + const Vec3V V20 = V3Mul(V10, V10); + const Vec3V V22 = V3Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec3V Result; + Result = V3ScaleAdd(V2, C1, V3One()); + Result = V3ScaleAdd(V4, C2, Result); + Result = V3ScaleAdd(V6, C3, Result); + Result = V3ScaleAdd(V8, C4, Result); + Result = V3ScaleAdd(V10, C5, Result); + Result = V3ScaleAdd(V12, C6, Result); + Result = V3ScaleAdd(V14, C7, Result); + Result = V3ScaleAdd(V16, C8, Result); + Result = V3ScaleAdd(V18, C9, Result); + Result = V3ScaleAdd(V20, C10, Result); + Result = V3ScaleAdd(V22, C11, Result); + + ASSERT_ISVALIDVEC3V(Result); + return Result; +} + +PX_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 2, 1)); +} + +PX_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 1, 0)); +} + +PX_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); +} + +PX_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); +} + +PX_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 2, 2)); +} + +PX_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 0, 1)); +} + +PX_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + return _mm_shuffle_ps(v1, v0, _MM_SHUFFLE(3, 1, 2, 3)); +} + +PX_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + return _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(3, 0, 3, 2)); +} + +PX_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + // There must be a better way to do this. + Vec3V v2 = V3Zero(); + FloatV y1 = V3GetY(v1); + FloatV x0 = V3GetX(v0); + v2 = V3SetX(v2, y1); + return V3SetY(v2, x0); +} + +PX_FORCE_INLINE FloatV V3SumElems(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); // z,y,x,w + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); // y,x,w,z + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); // x,w,z,y + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + const BoolV c = BOr(V3IsGrtr(a, max), V3IsGrtr(min, a)); + return PxU32(!BAllEqFFFF(c)); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + const BoolV c = BAnd(V3IsGrtrOrEq(a, min), V3IsGrtrOrEq(max, a)); + return BAllEqTTTT(c); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds); + return V3OutOfBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds); + return V3InBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2) +{ + ASSERT_ISVALIDVEC3V(col0); + ASSERT_ISVALIDVEC3V(col1); + ASSERT_ISVALIDVEC3V(col2); + const Vec3V col3 = _mm_setzero_ps(); + Vec3V tmp0 = _mm_unpacklo_ps(col0, col1); + Vec3V tmp2 = _mm_unpacklo_ps(col2, col3); + Vec3V tmp1 = _mm_unpackhi_ps(col0, col1); + Vec3V tmp3 = _mm_unpackhi_ps(col2, col3); + col0 = _mm_movelh_ps(tmp0, tmp2); + col1 = _mm_movehl_ps(tmp2, tmp0); + col2 = _mm_movelh_ps(tmp1, tmp3); +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V V4Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + // return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0,0,0,0)); + return f; +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatV* const floatVArray) +{ + ASSERT_ISVALIDFLOATV(floatVArray[0]); + ASSERT_ISVALIDFLOATV(floatVArray[1]); + ASSERT_ISVALIDFLOATV(floatVArray[2]); + ASSERT_ISVALIDFLOATV(floatVArray[3]); + const __m128 xw = _mm_move_ss(floatVArray[1], floatVArray[0]); // y, y, y, x + const __m128 yz = _mm_move_ss(floatVArray[2], floatVArray[3]); // z, z, z, w + return _mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0)); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + ASSERT_ISVALIDFLOATV(w); + const __m128 xw = _mm_move_ss(y, x); // y, y, y, x + const __m128 yz = _mm_move_ss(z, w); // z, z, z, w + return _mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0)); +} + +PX_FORCE_INLINE Vec4V V4MergeW(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpackhi_ps(x, z); + const Vec4V yw = _mm_unpackhi_ps(y, w); + return _mm_unpackhi_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeZ(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpackhi_ps(x, z); + const Vec4V yw = _mm_unpackhi_ps(y, w); + return _mm_unpacklo_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeY(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpacklo_ps(x, z); + const Vec4V yw = _mm_unpacklo_ps(y, w); + return _mm_unpackhi_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeX(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpacklo_ps(x, z); + const Vec4V yw = _mm_unpacklo_ps(y, w); + return _mm_unpacklo_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4UnpackXY(const Vec4VArg a, const Vec4VArg b) +{ + return _mm_unpacklo_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4UnpackZW(const Vec4VArg a, const Vec4VArg b) +{ + return _mm_unpackhi_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4PermYXWZ(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 0, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermXZXZ(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 0, 2, 0)); +} + +PX_FORCE_INLINE Vec4V V4PermYWYW(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 3, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermZWXY(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); +} + +template +PX_FORCE_INLINE Vec4V V4Perm(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(w, z, y, x)); +} + +PX_FORCE_INLINE Vec4V V4UnitW() +{ + const PX_ALIGN(16, PxF32 w[4]) = { 0.0f, 0.0f, 0.0f, 1.0f }; + const __m128 w128 = _mm_load_ps(w); + return w128; +} + +PX_FORCE_INLINE Vec4V V4UnitX() +{ + const PX_ALIGN(16, PxF32 x[4]) = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +PX_FORCE_INLINE Vec4V V4UnitY() +{ + const PX_ALIGN(16, PxF32 y[4]) = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +PX_FORCE_INLINE Vec4V V4UnitZ() +{ + const PX_ALIGN(16, PxF32 z[4]) = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +PX_FORCE_INLINE FloatV V4GetW(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +PX_FORCE_INLINE FloatV V4GetX(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE FloatV V4GetY(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE FloatV V4GetZ(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTTF(), v, f); +} + +PX_FORCE_INLINE Vec4V V4ClearW(const Vec4V v) +{ + return _mm_and_ps(v, (VecI32V&)internalWindowsSimd::gMaskXYZ); +} + +PX_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4Zero() +{ + return _mm_setzero_ps(); +} + +PX_FORCE_INLINE Vec4V V4One() +{ + return V4Load(1.0f); +} + +PX_FORCE_INLINE Vec4V V4Eps() +{ + return V4Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec4V V4Neg(const Vec4V f) +{ + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b) +{ + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b) +{ + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b) +{ + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b) +{ + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec4V V4Recip(const Vec4V a) +{ + return _mm_div_ps(V4One(), a); +} + +PX_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a) +{ + return _mm_rcp_ps(a); +} + +PX_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a) +{ + return _mm_div_ps(V4One(), _mm_sqrt_ps(a)); +} + +PX_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a) +{ + return _mm_rsqrt_ps(a); +} + +PX_FORCE_INLINE Vec4V V4Sqrt(const Vec4V a) +{ + return _mm_sqrt_ps(a); +} + +PX_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return V4Add(V4Scale(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return V4Sub(c, V4Scale(a, b)); +} + +PX_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Add(V4Mul(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Sub(c, V4Mul(a, b)); +} + +PX_FORCE_INLINE Vec4V V4Abs(const Vec4V a) +{ + return V4Max(a, V4Neg(a)); +} + +PX_FORCE_INLINE FloatV V4SumElements(const Vec4V a) +{ + const Vec4V xy = V4UnpackXY(a, a); // x,x,y,y + const Vec4V zw = V4UnpackZW(a, a); // z,z,w,w + const Vec4V xz_yw = V4Add(xy, zw); // x+z,x+z,y+w,y+w + const FloatV xz = V4GetX(xz_yw); // x+z + const FloatV yw = V4GetZ(xz_yw); // y+w + return FAdd(xz, yw); // sum +} + +PX_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b) +{ + const __m128 dot1 = _mm_mul_ps(a, b); // x,y,z,w + const __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 1, 0, 3)); // w,x,y,z + const __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 0, 3, 2)); // z,w,x,y + const __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 3, 2, 1)); // y,z,w,x + return _mm_add_ps(_mm_add_ps(shuf2, shuf3), _mm_add_ps(dot1, shuf1)); + + // PT: this version has two less instructions but we should check its accuracy + // aw*bw | az*bz | ay*by | ax*bx + // const __m128 t0 = _mm_mul_ps(a, b); + // ay*by | ax*bx | aw*bw | az*bz + // const __m128 t1 = _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(1,0,3,2)); + // ay*by + aw*bw | ax*bx + az*bz | aw*bw + ay*by | az*bz + ax*bx + // const __m128 t2 = _mm_add_ps(t0, t1); + // ax*bx + az*bz | ay*by + aw*bw | az*bz + ax*bx | aw*bw + ay*by + // const __m128 t3 = _mm_shuffle_ps(t2, t2, _MM_SHUFFLE(2,3,0,1)); + // ax*bx + az*bz + ay*by + aw*bw + // return _mm_add_ps(t3, t2); + // ay*by + aw*bw + ax*bx + az*bz + // az*bz + ax*bx + aw*bw + ay*by + // aw*bw + ay*by + az*bz + ax*bx +} + +PX_FORCE_INLINE FloatV V4Dot3(const Vec4V a, const Vec4V b) +{ + const __m128 dot1 = _mm_mul_ps(a, b); // aw*bw | az*bz | ay*by | ax*bx + const __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 0, 0, 0)); // ax*bx | ax*bx | ax*bx | ax*bx + const __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 1, 1, 1)); // ay*by | ay*by | ay*by | ay*by + const __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 2, 2, 2)); // az*bz | az*bz | az*bz | az*bz + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); // ax*bx + ay*by + az*bz in each component +} + +PX_FORCE_INLINE Vec4V V4Cross(const Vec4V a, const Vec4V b) +{ + const __m128 r1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(l1, l2), _mm_mul_ps(r1, r2)); +} + +PX_FORCE_INLINE FloatV V4Length(const Vec4V a) +{ + return _mm_sqrt_ps(V4Dot(a, a)); +} + +PX_FORCE_INLINE FloatV V4LengthSq(const Vec4V a) +{ + return V4Dot(a, a); +} + +PX_FORCE_INLINE Vec4V V4Normalize(const Vec4V a) +{ + ASSERT_ISFINITELENGTH(a); + return V4ScaleInv(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +PX_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a) +{ + ASSERT_ISFINITELENGTH(a); + return V4ScaleInvFast(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +PX_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a, const Vec4V unsafeReturnValue) +{ + const __m128 eps = V3Eps(); + const __m128 length = V4Length(a); + const __m128 isGreaterThanZero = V4IsGrtr(length, eps); + return V4Sel(isGreaterThanZero, V4ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b) +{ + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b) +{ + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_cmpeq_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE Vec4V V4Max(const Vec4V a, const Vec4V b) +{ + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b) +{ + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_max_ps(_mm_max_ps(a, shuf1), _mm_max_ps(shuf2, shuf3)); +} + +PX_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_min_ps(_mm_min_ps(a, shuf1), _mm_min_ps(shuf2, shuf3)); +} + +PX_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV) +{ + return V4Max(V4Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V4AllGrtr(const Vec4V a, const Vec4V b) +{ + return internalWindowsSimd::BAllTrue4_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return internalWindowsSimd::BAllTrue4_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq3(const Vec4V a, const Vec4V b) +{ + return internalWindowsSimd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllEq(const Vec4V a, const Vec4V b) +{ + return internalWindowsSimd::BAllTrue4_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AnyGrtr3(const Vec4V a, const Vec4V b) +{ + return internalWindowsSimd::BAnyTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE Vec4V V4Round(const Vec4V a) +{ + // return _mm_round_ps(a, 0x0); + const Vec4V half = V4Load(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const Vec4V aRound = V4Sub(V4Add(a, half), signBit); + const __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +} + +PX_FORCE_INLINE Vec4V V4Sin(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V3 = V4Mul(V2, V1); + const Vec4V V5 = V4Mul(V3, V2); + const Vec4V V7 = V4Mul(V5, V2); + const Vec4V V9 = V4Mul(V7, V2); + const Vec4V V11 = V4Mul(V9, V2); + const Vec4V V13 = V4Mul(V11, V2); + const Vec4V V15 = V4Mul(V13, V2); + const Vec4V V17 = V4Mul(V15, V2); + const Vec4V V19 = V4Mul(V17, V2); + const Vec4V V21 = V4Mul(V19, V2); + const Vec4V V23 = V4Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec4V Result; + Result = V4MulAdd(S1, V3, V1); + Result = V4MulAdd(S2, V5, Result); + Result = V4MulAdd(S3, V7, Result); + Result = V4MulAdd(S4, V9, Result); + Result = V4MulAdd(S5, V11, Result); + Result = V4MulAdd(S6, V13, Result); + Result = V4MulAdd(S7, V15, Result); + Result = V4MulAdd(S8, V17, Result); + Result = V4MulAdd(S9, V19, Result); + Result = V4MulAdd(S10, V21, Result); + Result = V4MulAdd(S11, V23, Result); + + return Result; +} + +PX_FORCE_INLINE Vec4V V4Cos(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V4 = V4Mul(V2, V2); + const Vec4V V6 = V4Mul(V4, V2); + const Vec4V V8 = V4Mul(V4, V4); + const Vec4V V10 = V4Mul(V6, V4); + const Vec4V V12 = V4Mul(V6, V6); + const Vec4V V14 = V4Mul(V8, V6); + const Vec4V V16 = V4Mul(V8, V8); + const Vec4V V18 = V4Mul(V10, V8); + const Vec4V V20 = V4Mul(V10, V10); + const Vec4V V22 = V4Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec4V Result; + Result = V4MulAdd(C1, V2, V4One()); + Result = V4MulAdd(C2, V4, Result); + Result = V4MulAdd(C3, V6, Result); + Result = V4MulAdd(C4, V8, Result); + Result = V4MulAdd(C5, V10, Result); + Result = V4MulAdd(C6, V12, Result); + Result = V4MulAdd(C7, V14, Result); + Result = V4MulAdd(C8, V16, Result); + Result = V4MulAdd(C9, V18, Result); + Result = V4MulAdd(C10, V20, Result); + Result = V4MulAdd(C11, V22, Result); + + return Result; +} + +PX_FORCE_INLINE void V4Transpose(Vec4V& col0, Vec4V& col1, Vec4V& col2, Vec4V& col3) +{ + Vec4V tmp0 = _mm_unpacklo_ps(col0, col1); + Vec4V tmp2 = _mm_unpacklo_ps(col2, col3); + Vec4V tmp1 = _mm_unpackhi_ps(col0, col1); + Vec4V tmp3 = _mm_unpackhi_ps(col2, col3); + col0 = _mm_movelh_ps(tmp0, tmp2); + col1 = _mm_movehl_ps(tmp2, tmp0); + col2 = _mm_movelh_ps(tmp1, tmp3); + col3 = _mm_movehl_ps(tmp3, tmp1); +} + +////////////////////////////////// +// BoolV +////////////////////////////////// + +PX_FORCE_INLINE BoolV BFFFF() +{ + return _mm_setzero_ps(); +} + +PX_FORCE_INLINE BoolV BFFFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0,0xFFFFFFFF}; + const __m128 ffft=_mm_load_ps((float*)&f); + return ffft;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFFTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0}; + const __m128 fftf=_mm_load_ps((float*)&f); + return fftf;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFFTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 fftt=_mm_load_ps((float*)&f); + return fftt;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFTFF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0}; + const __m128 ftff=_mm_load_ps((float*)&f); + return ftff;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0xFFFFFFFF}; + const __m128 ftft=_mm_load_ps((float*)&f); + return ftft;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0xFFFFFFFF,0}; + const __m128 fttf=_mm_load_ps((float*)&f); + return fttf;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, -1, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 fttt=_mm_load_ps((float*)&f); + return fttt;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, -1, -1, 0)); +} + +PX_FORCE_INLINE BoolV BTFFF() +{ + // const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0}; + // const __m128 tfff=_mm_load_ps((float*)&f); + // return tfff; + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0xFFFFFFFF}; + const __m128 tfft=_mm_load_ps((float*)&f); + return tfft;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0xFFFFFFFF,0}; + const __m128 tftf=_mm_load_ps((float*)&f); + return tftf;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, -1, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 tftt=_mm_load_ps((float*)&f); + return tftt;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, -1, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTTFF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0,0}; + const __m128 ttff=_mm_load_ps((float*)&f); + return ttff;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, 0, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0,0xFFFFFFFF}; + const __m128 ttft=_mm_load_ps((float*)&f); + return ttft;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, 0, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0}; + const __m128 tttf=_mm_load_ps((float*)&f); + return tttf;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, -1, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 tttt=_mm_load_ps((float*)&f); + return tttt;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, -1, -1, -1)); +} + +PX_FORCE_INLINE BoolV BXMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0}; + const __m128 tfff=_mm_load_ps((float*)&f); + return tfff;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BYMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0}; + const __m128 ftff=_mm_load_ps((float*)&f); + return ftff;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BZMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0}; + const __m128 fftf=_mm_load_ps((float*)&f); + return fftf;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BWMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0,0xFFFFFFFF}; + const __m128 ffft=_mm_load_ps((float*)&f); + return ffft;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BGetX(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BGetY(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE BoolV BGetZ(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE BoolV BGetW(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +PX_FORCE_INLINE BoolV BSetX(const BoolV v, const BoolV f) +{ + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetY(const BoolV v, const BoolV f) +{ + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetZ(const BoolV v, const BoolV f) +{ + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetW(const BoolV v, const BoolV f) +{ + return V4Sel(BTTTF(), v, f); +} + +template +BoolV BSplatElement(BoolV a) +{ + return internalWindowsSimd::m128_I2F( + _mm_shuffle_epi32(internalWindowsSimd::m128_F2I(a), _MM_SHUFFLE(index, index, index, index))); +} + +PX_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b) +{ + return _mm_and_ps(a, b); +} + +PX_FORCE_INLINE BoolV BNot(const BoolV a) +{ + const BoolV bAllTrue(BTTTT()); + return _mm_xor_ps(a, bAllTrue); +} + +PX_FORCE_INLINE BoolV BAndNot(const BoolV a, const BoolV b) +{ + return _mm_andnot_ps(b, a); +} + +PX_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b) +{ + return _mm_or_ps(a, b); +} + +PX_FORCE_INLINE BoolV BAllTrue4(const BoolV a) +{ + const BoolV bTmp = + _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAnyTrue4(const BoolV a) +{ + const BoolV bTmp = + _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAllTrue3(const BoolV a) +{ + const BoolV bTmp = + _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAnyTrue3(const BoolV a) +{ + const BoolV bTmp = + _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE PxU32 BAllEq(const BoolV a, const BoolV b) +{ + const BoolV bTest = internalWindowsSimd::m128_I2F( + _mm_cmpeq_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); + return internalWindowsSimd::BAllTrue4_R(bTest); +} + +PX_FORCE_INLINE PxU32 BAllEqTTTT(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)==15); +} + +PX_FORCE_INLINE PxU32 BAllEqFFFF(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)==0); +} + +PX_FORCE_INLINE PxU32 BGetBitMask(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)); +} + +////////////////////////////////// +// MAT33V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M33MulV3(const Mat33V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V& a, const Vec3V b) +{ + Vec3V v0 = V3Mul(a.col0, b); + Vec3V v1 = V3Mul(a.col1, b); + Vec3V v2 = V3Mul(a.col2, b); + V3Transpose(v0, v1, v2); + return V3Add(V3Add(v0, v1), v2); +} + +PX_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V& A, const Vec3V b, const Vec3V c) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + Vec3V result = V3ScaleAdd(A.col0, x, c); + result = V3ScaleAdd(A.col1, y, result); + return V3ScaleAdd(A.col2, z, result); +} + +PX_FORCE_INLINE Mat33V M33MulM33(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(M33MulV3(a, b.col0), M33MulV3(a, b.col1), M33MulV3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Add(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Scale(const Mat33V& a, const FloatV& b) +{ + return Mat33V(V3Scale(a.col0, b), V3Scale(a.col1, b), V3Scale(a.col2, b)); +} + +PX_FORCE_INLINE Mat33V M33Sub(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Sub(a.col0, b.col0), V3Sub(a.col1, b.col1), V3Sub(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Neg(const Mat33V& a) +{ + return Mat33V(V3Neg(a.col0), V3Neg(a.col1), V3Neg(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Abs(const Mat33V& a) +{ + return Mat33V(V3Abs(a.col0), V3Abs(a.col1), V3Abs(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Inverse(const Mat33V& a) +{ + const BoolV tfft = BTFFT(); + const BoolV tttf = BTTTF(); + const FloatV zero = V3Zero(); + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = _mm_rcp_ps(dot); + const Vec3V mergeh = _mm_unpacklo_ps(cross12, cross01); + const Vec3V mergel = _mm_unpackhi_ps(cross12, cross01); + Vec3V colInv0 = _mm_unpacklo_ps(mergeh, cross20); + colInv0 = _mm_or_ps(_mm_andnot_ps(tttf, zero), _mm_and_ps(tttf, colInv0)); + const Vec3V zppd = _mm_shuffle_ps(mergeh, cross20, _MM_SHUFFLE(3, 0, 0, 2)); + const Vec3V pbwp = _mm_shuffle_ps(cross20, mergeh, _MM_SHUFFLE(3, 3, 1, 0)); + const Vec3V colInv1 = _mm_or_ps(_mm_andnot_ps(BTFFT(), pbwp), _mm_and_ps(BTFFT(), zppd)); + const Vec3V xppd = _mm_shuffle_ps(mergel, cross20, _MM_SHUFFLE(3, 0, 0, 0)); + const Vec3V pcyp = _mm_shuffle_ps(cross20, mergel, _MM_SHUFFLE(3, 1, 2, 0)); + const Vec3V colInv2 = _mm_or_ps(_mm_andnot_ps(tfft, pcyp), _mm_and_ps(tfft, xppd)); + + return Mat33V(_mm_mul_ps(colInv0, invDet), _mm_mul_ps(colInv1, invDet), _mm_mul_ps(colInv2, invDet)); +} + +PX_FORCE_INLINE Mat33V M33Trnsps(const Mat33V& a) +{ + Vec3V col0 = a.col0, col1 = a.col1, col2 = a.col2; + V3Transpose(col0, col1, col2); + return Mat33V(col0, col1, col2); +} + +PX_FORCE_INLINE Mat33V M33Identity() +{ + return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ()); +} + +PX_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg d) +{ + const FloatV x = V3Mul(V3UnitX(), d); + const FloatV y = V3Mul(V3UnitY(), d); + const FloatV z = V3Mul(V3UnitZ(), d); + return Mat33V(x, y, z); +} + +////////////////////////////////// +// MAT34V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M34MulV3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + const Vec3V v0PlusV1Plusv2 = V3Add(v0PlusV1, v2); + return V3Add(v0PlusV1Plusv2, a.col3); +} + +PX_FORCE_INLINE Vec3V M34Mul33V3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M34TrnspsMul33V3(const Mat34V& a, const Vec3V b) +{ + Vec3V v0 = V3Mul(a.col0, b); + Vec3V v1 = V3Mul(a.col1, b); + Vec3V v2 = V3Mul(a.col2, b); + V3Transpose(v0, v1, v2); + return V3Add(V3Add(v0, v1), v2); +} + +PX_FORCE_INLINE Mat34V M34MulM34(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2), M34MulV3(a, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34MulM33(const Mat34V& a, const Mat33V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M34Mul33MM34(const Mat34V& a, const Mat34V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat34V M34Add(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2), V3Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat34V M34Inverse(const Mat34V& a) +{ + Mat34V aInv; + const BoolV tfft = BTFFT(); + const BoolV tttf = BTTTF(); + const FloatV zero = V3Zero(); + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = _mm_rcp_ps(dot); + const Vec3V mergeh = _mm_unpacklo_ps(cross12, cross01); + const Vec3V mergel = _mm_unpackhi_ps(cross12, cross01); + Vec3V colInv0 = _mm_unpacklo_ps(mergeh, cross20); + colInv0 = _mm_or_ps(_mm_andnot_ps(tttf, zero), _mm_and_ps(tttf, colInv0)); + const Vec3V zppd = _mm_shuffle_ps(mergeh, cross20, _MM_SHUFFLE(3, 0, 0, 2)); + const Vec3V pbwp = _mm_shuffle_ps(cross20, mergeh, _MM_SHUFFLE(3, 3, 1, 0)); + const Vec3V colInv1 = _mm_or_ps(_mm_andnot_ps(BTFFT(), pbwp), _mm_and_ps(BTFFT(), zppd)); + const Vec3V xppd = _mm_shuffle_ps(mergel, cross20, _MM_SHUFFLE(3, 0, 0, 0)); + const Vec3V pcyp = _mm_shuffle_ps(cross20, mergel, _MM_SHUFFLE(3, 1, 2, 0)); + const Vec3V colInv2 = _mm_or_ps(_mm_andnot_ps(tfft, pcyp), _mm_and_ps(tfft, xppd)); + aInv.col0 = _mm_mul_ps(colInv0, invDet); + aInv.col1 = _mm_mul_ps(colInv1, invDet); + aInv.col2 = _mm_mul_ps(colInv2, invDet); + aInv.col3 = M34Mul33V3(aInv, V3Neg(a.col3)); + return aInv; +} + +PX_FORCE_INLINE Mat33V M34Trnsps33(const Mat34V& a) +{ + Vec3V col0 = a.col0, col1 = a.col1, col2 = a.col2; + V3Transpose(col0, col1, col2); + return Mat33V(col0, col1, col2); +} + +////////////////////////////////// +// MAT44V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V M44MulV4(const Mat44V& a, const Vec4V b) +{ + const FloatV x = V4GetX(b); + const FloatV y = V4GetY(b); + const FloatV z = V4GetZ(b); + const FloatV w = V4GetW(b); + + const Vec4V v0 = V4Scale(a.col0, x); + const Vec4V v1 = V4Scale(a.col1, y); + const Vec4V v2 = V4Scale(a.col2, z); + const Vec4V v3 = V4Scale(a.col3, w); + const Vec4V v0PlusV1 = V4Add(v0, v1); + const Vec4V v0PlusV1Plusv2 = V4Add(v0PlusV1, v2); + return V4Add(v0PlusV1Plusv2, v3); +} + +PX_FORCE_INLINE Vec4V M44TrnspsMulV4(const Mat44V& a, const Vec4V b) +{ + Vec4V v0 = V4Mul(a.col0, b); + Vec4V v1 = V4Mul(a.col1, b); + Vec4V v2 = V4Mul(a.col2, b); + Vec4V v3 = V4Mul(a.col3, b); + V4Transpose(v0, v1, v2, v3); + return V4Add(V4Add(v0, v1), V4Add(v2, v3)); +} + +PX_FORCE_INLINE Mat44V M44MulM44(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(M44MulV4(a, b.col0), M44MulV4(a, b.col1), M44MulV4(a, b.col2), M44MulV4(a, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Add(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(V4Add(a.col0, b.col0), V4Add(a.col1, b.col1), V4Add(a.col2, b.col2), V4Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Trnsps(const Mat44V& a) +{ + Vec4V col0 = a.col0, col1 = a.col1, col2 = a.col2, col3 = a.col3; + V4Transpose(col0, col1, col2, col3); + return Mat44V(col0, col1, col2, col3); +} + +PX_FORCE_INLINE Mat44V M44Inverse(const Mat44V& a) +{ + __m128 minor0, minor1, minor2, minor3; + __m128 row0, row1, row2, row3; + __m128 det, tmp1; + + tmp1 = V4Zero(); + row1 = V4Zero(); + row3 = V4Zero(); + + row0 = a.col0; + row1 = _mm_shuffle_ps(a.col1, a.col1, _MM_SHUFFLE(1, 0, 3, 2)); + row2 = a.col2; + row3 = _mm_shuffle_ps(a.col3, a.col3, _MM_SHUFFLE(1, 0, 3, 2)); + + tmp1 = _mm_mul_ps(row2, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_mul_ps(row1, tmp1); + minor1 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0); + minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1); + minor1 = _mm_shuffle_ps(minor1, minor1, 0x4E); + + tmp1 = _mm_mul_ps(row1, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0); + minor3 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3); + minor3 = _mm_shuffle_ps(minor3, minor3, 0x4E); + + tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, 0x4E), row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + row2 = _mm_shuffle_ps(row2, row2, 0x4E); + minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0); + minor2 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2); + minor2 = _mm_shuffle_ps(minor2, minor2, 0x4E); + + tmp1 = _mm_mul_ps(row0, row1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1)); + + tmp1 = _mm_mul_ps(row0, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1); + minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1)); + + tmp1 = _mm_mul_ps(row0, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1)); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3); + + det = _mm_mul_ps(row0, minor0); + det = _mm_add_ps(_mm_shuffle_ps(det, det, 0x4E), det); + det = _mm_add_ss(_mm_shuffle_ps(det, det, 0xB1), det); + tmp1 = _mm_rcp_ss(det); +#if 0 + det = _mm_sub_ss(_mm_add_ss(tmp1, tmp1), _mm_mul_ss(det, _mm_mul_ss(tmp1, tmp1))); + det = _mm_shuffle_ps(det, det, 0x00); +#else + det = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(0, 0, 0, 0)); +#endif + + minor0 = _mm_mul_ps(det, minor0); + minor1 = _mm_mul_ps(det, minor1); + minor2 = _mm_mul_ps(det, minor2); + minor3 = _mm_mul_ps(det, minor3); + Mat44V invTrans(minor0, minor1, minor2, minor3); + return M44Trnsps(invTrans); +} + +PX_FORCE_INLINE Vec4V V4LoadXYZW(const PxF32& x, const PxF32& y, const PxF32& z, const PxF32& w) +{ + return _mm_set_ps(w, z, y, x); +} + +PX_FORCE_INLINE VecU32V V4U32Sel(const BoolV c, const VecU32V a, const VecU32V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_or_si128(_mm_andnot_si128(internalWindowsSimd::m128_F2I(c), internalWindowsSimd::m128_F2I(b)), + _mm_and_si128(internalWindowsSimd::m128_F2I(c), internalWindowsSimd::m128_F2I(a)))); +} + +PX_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b) +{ + return internalWindowsSimd::m128_I2F(_mm_or_si128(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32xor(VecU32V a, VecU32V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_xor_si128(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_and_si128(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_andnot_si128(internalWindowsSimd::m128_F2I(b), internalWindowsSimd::m128_F2I(a))); +} + +PX_FORCE_INLINE VecI32V U4Load(const PxU32 i) +{ + return _mm_load1_ps((PxF32*)&i); +} + +PX_FORCE_INLINE VecU32V U4LoadU(const PxU32* i) +{ + return _mm_loadu_ps((PxF32*)i); +} + +PX_FORCE_INLINE VecU32V U4LoadA(const PxU32* i) +{ + ASSERT_ISALIGNED16(i); + return _mm_load_ps((PxF32*)i); +} + +PX_FORCE_INLINE VecI32V I4Load(const PxI32 i) +{ + return _mm_load1_ps((PxF32*)&i); +} + +PX_FORCE_INLINE VecI32V I4LoadU(const PxI32* i) +{ + return _mm_loadu_ps((PxF32*)i); +} + +PX_FORCE_INLINE VecI32V I4LoadA(const PxI32* i) +{ + ASSERT_ISALIGNED16(i); + return _mm_load_ps((PxF32*)i); +} + +PX_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b) +{ + return internalWindowsSimd::m128_I2F( + _mm_add_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b) +{ + return internalWindowsSimd::m128_I2F( + _mm_sub_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b) +{ + return internalWindowsSimd::m128_I2F( + _mm_cmpgt_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b) +{ + return internalWindowsSimd::m128_I2F( + _mm_cmpeq_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE VecI32V V4I32Sel(const BoolV c, const VecI32V a, const VecI32V b) +{ + return V4U32Sel(c, a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Zero() +{ + return V4Zero(); +} + +PX_FORCE_INLINE VecI32V VecI32V_One() +{ + return I4Load(1); +} + +PX_FORCE_INLINE VecI32V VecI32V_Two() +{ + return I4Load(2); +} + +PX_FORCE_INLINE VecI32V VecI32V_MinusOne() +{ + return I4Load(-1); +} + +PX_FORCE_INLINE VecU32V U4Zero() +{ + return U4Load(0); +} + +PX_FORCE_INLINE VecU32V U4One() +{ + return U4Load(1); +} + +PX_FORCE_INLINE VecU32V U4Two() +{ + return U4Load(2); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sel(const BoolV c, const VecI32VArg a, const VecI32VArg b) +{ + PX_ASSERT(_VecMathTests::allElementsEqualBoolV(c, BTTTT()) || + _VecMathTests::allElementsEqualBoolV(c, BFFFF())); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE VecShiftV VecI32V_PrepareShift(const VecI32VArg shift) +{ + VecShiftV preparedShift; + preparedShift.shift = _mm_or_ps(_mm_andnot_ps(BTFFF(), VecI32V_Zero()), _mm_and_ps(BTFFF(), shift)); + return preparedShift; +} + +PX_FORCE_INLINE VecI32V VecI32V_LeftShift(const VecI32VArg a, const VecShiftVArg count) +{ + return internalWindowsSimd::m128_I2F( + _mm_sll_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(count.shift))); +} + +PX_FORCE_INLINE VecI32V VecI32V_RightShift(const VecI32VArg a, const VecShiftVArg count) +{ + return internalWindowsSimd::m128_I2F( + _mm_srl_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(count.shift))); +} + +PX_FORCE_INLINE VecI32V VecI32V_And(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_and_ps(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Or(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_or_ps(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetX(const VecI32VArg a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetY(const VecI32VArg a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetZ(const VecI32VArg a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetW(const VecI32VArg a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)); +} + +PX_FORCE_INLINE void PxI32_From_VecI32V(const VecI32VArg a, PxI32* i) +{ + _mm_store_ss((PxF32*)i, a); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_BoolV(const BoolVArg a) +{ + return a; +} + +PX_FORCE_INLINE VecU32V VecU32V_From_BoolV(const BoolVArg a) +{ + return a; +} + +PX_FORCE_INLINE VecI32V VecI32V_Merge(const VecI32VArg a, const VecI32VArg b, const VecI32VArg c, const VecI32VArg d) +{ + const __m128 xw = _mm_move_ss(b, a); // y, y, y, x + const __m128 yz = _mm_move_ss(c, d); // z, z, z, w + return _mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0)); +} + +PX_FORCE_INLINE void V4U32StoreAligned(VecU32V val, VecU32V* address) +{ + *address = val; +} + +PX_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b) +{ + VecU32V result32(a); + result32 = V4U32Andc(result32, b); + return Vec4V(result32); +} + +PX_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b) +{ + return V4IsGrtr(a, b); +} + +PX_FORCE_INLINE VecU16V V4U16LoadAligned(VecU16V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE VecU16V V4U16LoadUnaligned(VecU16V* addr) +{ + return *addr; +} + +// unsigned compares are not supported on x86 +PX_FORCE_INLINE VecU16V V4U16CompareGt(VecU16V a, VecU16V b) +{ + // _mm_cmpgt_epi16 doesn't work for unsigned values unfortunately + // return m128_I2F(_mm_cmpgt_epi16(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); + VecU16V result; + result.m128_u16[0] = PxU16((a).m128_u16[0] > (b).m128_u16[0]); + result.m128_u16[1] = PxU16((a).m128_u16[1] > (b).m128_u16[1]); + result.m128_u16[2] = PxU16((a).m128_u16[2] > (b).m128_u16[2]); + result.m128_u16[3] = PxU16((a).m128_u16[3] > (b).m128_u16[3]); + result.m128_u16[4] = PxU16((a).m128_u16[4] > (b).m128_u16[4]); + result.m128_u16[5] = PxU16((a).m128_u16[5] > (b).m128_u16[5]); + result.m128_u16[6] = PxU16((a).m128_u16[6] > (b).m128_u16[6]); + result.m128_u16[7] = PxU16((a).m128_u16[7] > (b).m128_u16[7]); + return result; +} + +PX_FORCE_INLINE VecU16V V4I16CompareGt(VecU16V a, VecU16V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_cmpgt_epi16(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a) +{ + Vec4V result = V4LoadXYZW(PxF32(a.m128_u32[0]), PxF32(a.m128_u32[1]), PxF32(a.m128_u32[2]), PxF32(a.m128_u32[3])); + return result; +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecI32V(VecI32V a) +{ + return _mm_cvtepi32_ps(internalWindowsSimd::m128_F2I(a)); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_Vec4V(Vec4V a) +{ + return internalWindowsSimd::m128_I2F(_mm_cvttps_epi32(a)); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecU32V(VecU32V a) +{ + return Vec4V(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecI32V(VecI32V a) +{ + return Vec4V(a); +} + +PX_FORCE_INLINE VecU32V VecU32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return VecU32V(a); +} + +PX_FORCE_INLINE VecI32V VecI32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return VecI32V(a); +} + +template +PX_FORCE_INLINE VecU32V V4U32SplatElement(VecU32V a) +{ + return internalWindowsSimd::m128_I2F( + _mm_shuffle_epi32(internalWindowsSimd::m128_F2I(a), _MM_SHUFFLE(index, index, index, index))); +} + +template +PX_FORCE_INLINE Vec4V V4SplatElement(Vec4V a) +{ + return internalWindowsSimd::m128_I2F( + _mm_shuffle_epi32(internalWindowsSimd::m128_F2I(a), _MM_SHUFFLE(index, index, index, index))); +} + +PX_FORCE_INLINE VecU32V U4LoadXYZW(PxU32 x, PxU32 y, PxU32 z, PxU32 w) +{ + VecU32V result; + result.m128_u32[0] = x; + result.m128_u32[1] = y; + result.m128_u32[2] = z; + result.m128_u32[3] = w; + return result; +} + +PX_FORCE_INLINE Vec4V V4ConvertFromI32V(const VecI32V in) +{ + return _mm_cvtepi32_ps(internalWindowsSimd::m128_F2I(in)); +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSWINDOWSINLINEAOS_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h new file mode 100644 index 000000000..b8c837d9c --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h @@ -0,0 +1,193 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSINTRINSICS_H +#define PSFOUNDATION_PSWINDOWSINTRINSICS_H + +#include "Ps.h" +#include "foundation/PxAssert.h" + +// this file is for internal intrinsics - that is, intrinsics that are used in +// cross platform code but do not appear in the API + +#if !PX_WINDOWS_FAMILY +#error "This file should only be included by Windows builds!!" +#endif + +#pragma warning(push) +//'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#pragma warning(disable : 4668) +#if PX_VC == 10 +#pragma warning(disable : 4987) // nonstandard extension used: 'throw (...)' +#endif +#include +#pragma warning(pop) + +#pragma warning(push) +#pragma warning(disable : 4985) // 'symbol name': attributes not present on previous declaration +#include +#pragma warning(pop) + +#include +// do not include for ARM target +#if !PX_ARM +#include +#endif + +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) + +namespace physx +{ +namespace shdfnd +{ + +/* +* Implements a memory barrier +*/ +PX_FORCE_INLINE void memoryBarrier() +{ + _ReadWriteBarrier(); + /* long Barrier; + __asm { + xchg Barrier, eax + }*/ +} + +/*! +Returns the index of the highest set bit. Not valid for zero arg. +*/ +PX_FORCE_INLINE uint32_t highestSetBitUnsafe(uint32_t v) +{ + unsigned long retval; + _BitScanReverse(&retval, v); + return retval; +} + +/*! +Returns the index of the highest set bit. Undefined for zero arg. +*/ +PX_FORCE_INLINE uint32_t lowestSetBitUnsafe(uint32_t v) +{ + unsigned long retval; + _BitScanForward(&retval, v); + return retval; +} + +/*! +Returns the number of leading zeros in v. Returns 32 for v=0. +*/ +PX_FORCE_INLINE uint32_t countLeadingZeros(uint32_t v) +{ + if(v) + { + unsigned long bsr = (unsigned long)-1; + _BitScanReverse(&bsr, v); + return 31 - bsr; + } + else + return 32; +} + +/*! +Prefetch aligned cache size around \c ptr+offset. +*/ +#if !PX_ARM +PX_FORCE_INLINE void prefetchLine(const void* ptr, uint32_t offset = 0) +{ + // cache line on X86/X64 is 64-bytes so a 128-byte prefetch would require 2 prefetches. + // However, we can only dispatch a limited number of prefetch instructions so we opt to prefetch just 1 cache line + /*_mm_prefetch(((const char*)ptr + offset), _MM_HINT_T0);*/ + // We get slightly better performance prefetching to non-temporal addresses instead of all cache levels + _mm_prefetch(((const char*)ptr + offset), _MM_HINT_NTA); +} +#else +PX_FORCE_INLINE void prefetchLine(const void* ptr, uint32_t offset = 0) +{ + // arm does have 32b cache line size + __prefetch(((const char*)ptr + offset)); +} +#endif + +/*! +Prefetch \c count bytes starting at \c ptr. +*/ +#if !PX_ARM +PX_FORCE_INLINE void prefetch(const void* ptr, uint32_t count = 1) +{ + const char* cp = (char*)ptr; + uint64_t p = size_t(ptr); + uint64_t startLine = p >> 6, endLine = (p + count - 1) >> 6; + uint64_t lines = endLine - startLine + 1; + do + { + prefetchLine(cp); + cp += 64; + } while(--lines); +} +#else +PX_FORCE_INLINE void prefetch(const void* ptr, uint32_t count = 1) +{ + const char* cp = (char*)ptr; + uint32_t p = size_t(ptr); + uint32_t startLine = p >> 5, endLine = (p + count - 1) >> 5; + uint32_t lines = endLine - startLine + 1; + do + { + prefetchLine(cp); + cp += 32; + } while(--lines); +} +#endif + +//! \brief platform-specific reciprocal +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipFast(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific fast reciprocal square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrtFast(float a) +{ + return 1.0f / ::sqrtf(a); +} + +//! \brief platform-specific floor +PX_CUDA_CALLABLE PX_FORCE_INLINE float floatFloor(float x) +{ + return ::floorf(x); +} + +#define NS_EXPECT_TRUE(x) x +#define NS_EXPECT_FALSE(x) x + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSWINDOWSINTRINSICS_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h new file mode 100644 index 000000000..f622cfe41 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSTRIGCONSTANTS_H +#define PSFOUNDATION_PSWINDOWSTRIGCONSTANTS_H + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +#define PX_GLOBALCONST extern const __declspec(selectany) + +__declspec(align(16)) struct PX_VECTORF32 +{ + float f[4]; +}; + +//#define PX_PI 3.141592654f +//#define PX_2PI 6.283185307f +//#define PX_1DIVPI 0.318309886f +//#define PX_1DIV2PI 0.159154943f +//#define PX_PIDIV2 1.570796327f +//#define PX_PIDIV4 0.785398163f + +PX_GLOBALCONST PX_VECTORF32 g_PXSinCoefficients0 = { { 1.0f, -0.166666667f, 8.333333333e-3f, -1.984126984e-4f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinCoefficients1 = { { 2.755731922e-6f, -2.505210839e-8f, 1.605904384e-10f, -7.647163732e-13f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinCoefficients2 = { { 2.811457254e-15f, -8.220635247e-18f, 1.957294106e-20f, -3.868170171e-23f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXCosCoefficients0 = { { 1.0f, -0.5f, 4.166666667e-2f, -1.388888889e-3f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosCoefficients1 = { { 2.480158730e-5f, -2.755731922e-7f, 2.087675699e-9f, -1.147074560e-11f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosCoefficients2 = { { 4.779477332e-14f, -1.561920697e-16f, 4.110317623e-19f, -8.896791392e-22f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTanCoefficients0 = { { 1.0f, 0.333333333f, 0.133333333f, 5.396825397e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXTanCoefficients1 = { { 2.186948854e-2f, 8.863235530e-3f, 3.592128167e-3f, 1.455834485e-3f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXTanCoefficients2 = { { 5.900274264e-4f, 2.391290764e-4f, 9.691537707e-5f, 3.927832950e-5f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients0 = { { -0.05806367563904f, -0.41861972469416f, 0.22480114791621f, 2.17337241360606f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients1 = { { 0.61657275907170f, 4.29696498283455f, -1.18942822255452f, -6.53784832094831f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients2 = { { -1.36926553863413f, -4.48179294237210f, 1.41810672941833f, 5.48179257935713f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXATanCoefficients0 = { { 1.0f, 0.333333334f, 0.2f, 0.142857143f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanCoefficients1 = { { 1.111111111e-1f, 9.090909091e-2f, 7.692307692e-2f, 6.666666667e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanCoefficients2 = { { 5.882352941e-2f, 5.263157895e-2f, 4.761904762e-2f, 4.347826087e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinEstCoefficients = { { 1.0f, -1.66521856991541e-1f, 8.199913018755e-3f, -1.61475937228e-4f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosEstCoefficients = { { 1.0f, -4.95348008918096e-1f, 3.878259962881e-2f, -9.24587976263e-4f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTanEstCoefficients = { { 2.484f, -1.954923183e-1f, 2.467401101f, PxInvPi } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanEstCoefficients = { { 7.689891418951e-1f, 1.104742493348f, 8.661844266006e-1f, PxPiDivTwo } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinEstCoefficients = { { -1.36178272886711f, 2.37949493464538f, -8.08228565650486e-1f, 2.78440142746736e-1f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXASinEstConstants = { { 1.00000011921f, PxPiDivTwo, 0.0f, 0.0f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXPiConstants0 = { { PxPi, PxTwoPi, PxInvPi, PxInvTwoPi } }; +PX_GLOBALCONST PX_VECTORF32 g_PXReciprocalTwoPi = { { PxInvTwoPi, PxInvTwoPi, PxInvTwoPi, PxInvTwoPi } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTwoPi = { { PxTwoPi, PxTwoPi, PxTwoPi, PxTwoPi } }; + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/foundation/src/PsAllocator.cpp b/src/PhysX/physx/source/foundation/src/PsAllocator.cpp new file mode 100644 index 000000000..8ffff92ba --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsAllocator.cpp @@ -0,0 +1,124 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsFoundation.h" +#include "PsAllocator.h" +#include "PsHashMap.h" +#include "PsArray.h" +#include "PsMutex.h" + +namespace physx +{ +namespace shdfnd +{ + +#if PX_USE_NAMED_ALLOCATOR +namespace +{ +typedef HashMap, NonTrackingAllocator> AllocNameMap; +PX_INLINE AllocNameMap& getMap() +{ + return getFoundation().getNamedAllocMap(); +} +PX_INLINE Foundation::Mutex& getMutex() +{ + return getFoundation().getNamedAllocMutex(); +} +} + +NamedAllocator::NamedAllocator(const PxEMPTY) +{ + Foundation::Mutex::ScopedLock lock(getMutex()); + getMap().insert(this, 0); +} + +NamedAllocator::NamedAllocator(const char* name) +{ + Foundation::Mutex::ScopedLock lock(getMutex()); + getMap().insert(this, name); +} + +NamedAllocator::NamedAllocator(const NamedAllocator& other) +{ + Foundation::Mutex::ScopedLock lock(getMutex()); + const AllocNameMap::Entry* e = getMap().find(&other); + PX_ASSERT(e); + const char* name = e->second; // The copy is important because insert might invalidate the referenced hash entry + getMap().insert(this, name); +} + +NamedAllocator::~NamedAllocator() +{ + Foundation::Mutex::ScopedLock lock(getMutex()); + bool erased = getMap().erase(this); + PX_UNUSED(erased); + PX_ASSERT(erased); +} + +NamedAllocator& NamedAllocator::operator=(const NamedAllocator& other) +{ + Foundation::Mutex::ScopedLock lock(getMutex()); + const AllocNameMap::Entry* e = getMap().find(&other); + PX_ASSERT(e); + getMap()[this] = e->second; + return *this; +} + +void* NamedAllocator::allocate(size_t size, const char* filename, int line) +{ + if(!size) + return 0; + Foundation::Mutex::ScopedLock lock(getMutex()); + const AllocNameMap::Entry* e = getMap().find(this); + PX_ASSERT(e); + return getAllocator().allocate(size, e->second, filename, line); +} + +void NamedAllocator::deallocate(void* ptr) +{ + if(ptr) + getAllocator().deallocate(ptr); +} + +#endif // PX_DEBUG + +void* Allocator::allocate(size_t size, const char* file, int line) +{ + if(!size) + return 0; + return getAllocator().allocate(size, "", file, line); +} +void Allocator::deallocate(void* ptr) +{ + if(ptr) + getAllocator().deallocate(ptr); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/PsAssert.cpp b/src/PhysX/physx/source/foundation/src/PsAssert.cpp new file mode 100644 index 000000000..a6e5a6143 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsAssert.cpp @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" + +#include +#include "PsString.h" + +#if PX_WINDOWS_FAMILY +#include +#elif PX_SWITCH +#include "switch/PsSwitchAbort.h" +#endif + +namespace +{ +class DefaultAssertHandler : public physx::PxAssertHandler +{ + virtual void operator()(const char* expr, const char* file, int line, bool& ignore) + { + PX_UNUSED(ignore); // is used only in debug windows config + char buffer[1024]; +#if PX_WINDOWS_FAMILY + sprintf_s(buffer, "%s(%d) : Assertion failed: %s\n", file, line, expr); +#else + sprintf(buffer, "%s(%d) : Assertion failed: %s\n", file, line, expr); +#endif + physx::shdfnd::printString(buffer); +#if PX_WINDOWS_FAMILY&& PX_DEBUG && PX_DEBUG_CRT + // _CrtDbgReport returns -1 on error, 1 on 'retry', 0 otherwise including 'ignore'. + // Hitting 'abort' will terminate the process immediately. + int result = _CrtDbgReport(_CRT_ASSERT, file, line, NULL, "%s", buffer); + int mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_REPORT_MODE); + ignore = _CRTDBG_MODE_WNDW == mode && result == 0; + if(ignore) + return; + __debugbreak(); +#elif PX_WINDOWS_FAMILY&& PX_CHECKED + __debugbreak(); +#elif PX_SWITCH + abort(buffer); +#else + abort(); +#endif + } +}; + +DefaultAssertHandler sAssertHandler; +physx::PxAssertHandler* sAssertHandlerPtr = &sAssertHandler; +} + +namespace physx +{ + +PxAssertHandler& PxGetAssertHandler() +{ + return *sAssertHandlerPtr; +} + +void PxSetAssertHandler(PxAssertHandler& handler) +{ + sAssertHandlerPtr = &handler; +} +} // end of physx namespace diff --git a/src/PhysX/physx/source/foundation/src/PsFoundation.cpp b/src/PhysX/physx/source/foundation/src/PsFoundation.cpp new file mode 100644 index 000000000..f2edf3f87 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsFoundation.cpp @@ -0,0 +1,284 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxProfiler.h" +#include "foundation/PxErrorCallback.h" +#include "PxPhysicsVersion.h" +#include "PsFoundation.h" +#include "PsString.h" +#include "PsAllocator.h" + +namespace physx +{ +namespace shdfnd +{ + +Foundation::Foundation(PxErrorCallback& errc, PxAllocatorCallback& alloc) +: mAllocatorCallback(alloc) +, mErrorCallback(errc) +, mBroadcastingAllocator(alloc, errc) +, mBroadcastingError(errc) +, +#if PX_CHECKED + mReportAllocationNames(true) +, +#else + mReportAllocationNames(false) +, +#endif + mErrorMask(PxErrorCode::Enum(~0)) +, mErrorMutex(PX_DEBUG_EXP("Foundation::mErrorMutex")) +, mNamedAllocMutex(PX_DEBUG_EXP("Foundation::mNamedAllocMutex")) +, mTempAllocMutex(PX_DEBUG_EXP("Foundation::mTempAllocMutex")) +{ +} + +Foundation::~Foundation() +{ + // deallocate temp buffer allocations + Allocator alloc; + for(PxU32 i = 0; i < mTempAllocFreeTable.size(); ++i) + { + for(TempAllocatorChunk* ptr = mTempAllocFreeTable[i]; ptr;) + { + TempAllocatorChunk* next = ptr->mNext; + alloc.deallocate(ptr); + ptr = next; + } + } + mTempAllocFreeTable.reset(); +} + +Foundation& Foundation::getInstance() +{ + PX_ASSERT(mInstance); + return *mInstance; +} + +void Foundation::setInstance(Foundation& foundation) +{ + PX_ASSERT(mInstance == NULL || &foundation == mInstance); + mInstance = &foundation; +} + +PxU32 Foundation::getWarnOnceTimestamp() +{ + PX_ASSERT(mInstance != NULL); + return mWarnOnceTimestap; +} + +void Foundation::error(PxErrorCode::Enum c, const char* file, int line, const char* messageFmt, ...) +{ + va_list va; + va_start(va, messageFmt); + errorImpl(c, file, line, messageFmt, va); + va_end(va); +} + +void Foundation::errorImpl(PxErrorCode::Enum e, const char* file, int line, const char* messageFmt, va_list va) +{ + PX_ASSERT(messageFmt); + if(e & mErrorMask) + { + // this function is reentrant but user's error callback may not be, so... + Mutex::ScopedLock lock(mErrorMutex); + + // using a static fixed size buffer here because: + // 1. vsnprintf return values differ between platforms + // 2. va_start is only usable in functions with ellipses + // 3. ellipses (...) cannot be passed to called function + // which would be necessary to dynamically grow the buffer here + + static const size_t bufSize = 1024; + char stringBuffer[bufSize]; + shdfnd::vsnprintf(stringBuffer, bufSize, messageFmt, va); + + mBroadcastingError.reportError(e, stringBuffer, file, line); + } +} + +Foundation* Foundation::createInstance(PxU32 version, PxErrorCallback& errc, PxAllocatorCallback& alloc) +{ + if(version != PX_PHYSICS_VERSION) + { + char* buffer = new char[256]; + physx::shdfnd::snprintf(buffer, 256, "Wrong version: physics version is 0x%08x, tried to create 0x%08x", + PX_PHYSICS_VERSION, version); + errc.reportError(PxErrorCode::eINVALID_PARAMETER, buffer, __FILE__, __LINE__); + return 0; + } + + if(!mInstance) + { + // if we don't assign this here, the Foundation object can't create member + // subobjects which require the allocator + + mInstance = reinterpret_cast(alloc.allocate(sizeof(Foundation), "Foundation", __FILE__, __LINE__)); + + if(mInstance) + { + PX_PLACEMENT_NEW(mInstance, Foundation)(errc, alloc); + + PX_ASSERT(mRefCount == 0); + mRefCount = 1; + + // skip 0 which marks uninitialized timestaps in PX_WARN_ONCE + mWarnOnceTimestap = (mWarnOnceTimestap == PX_MAX_U32) ? 1 : mWarnOnceTimestap + 1; + + return mInstance; + } + else + { + errc.reportError(PxErrorCode::eINTERNAL_ERROR, "Memory allocation for foundation object failed.", __FILE__, + __LINE__); + } + } + else + { + errc.reportError(PxErrorCode::eINVALID_OPERATION, + "Foundation object exists already. Only one instance per process can be created.", __FILE__, + __LINE__); + } + + return 0; +} + +void Foundation::destroyInstance() +{ + PX_ASSERT(mInstance != NULL); + + if(mRefCount == 1) + { + PxAllocatorCallback& alloc = mInstance->getAllocatorCallback(); + mInstance->~Foundation(); + alloc.deallocate(mInstance); + mInstance = 0; + mRefCount = 0; + } + else + { + mInstance->error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Foundation destruction failed due to pending module references. Close/release all depending " + "modules first."); + } +} + +void Foundation::incRefCount() +{ + PX_ASSERT(mInstance != NULL); + + if(mRefCount > 0) + { + mRefCount++; + } + else + { + mInstance->error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Foundation: Invalid registration detected."); + } +} + +void Foundation::decRefCount() +{ + PX_ASSERT(mInstance != NULL); + + if(mRefCount > 0) + { + mRefCount--; + } + else + { + mInstance->error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Foundation: Invalid deregistration detected."); + } +} + +void Foundation::release() +{ + Foundation::destroyInstance(); +} + +PxAllocatorCallback& getAllocator() +{ + return getFoundation().getAllocator(); +} + +Foundation* Foundation::mInstance = NULL; +PxU32 Foundation::mRefCount = 0; +PxU32 Foundation::mWarnOnceTimestap = 0; + +void Foundation::registerAllocationListener(physx::shdfnd::AllocationListener& listener) +{ + Mutex::ScopedLock lock(mListenerMutex); + mBroadcastingAllocator.registerListener(listener); +} + +void Foundation::deregisterAllocationListener(physx::shdfnd::AllocationListener& listener) +{ + Mutex::ScopedLock lock(mListenerMutex); + mBroadcastingAllocator.deregisterListener(listener); +} + +void Foundation::registerErrorCallback(PxErrorCallback& callback) +{ + Mutex::ScopedLock lock(mListenerMutex); + mBroadcastingError.registerListener(callback); +} + +void Foundation::deregisterErrorCallback(PxErrorCallback& callback) +{ + Mutex::ScopedLock lock(mListenerMutex); + mBroadcastingError.deregisterListener(callback); +} + +physx::PxProfilerCallback* gProfilerCallback = NULL; + +} // namespace shdfnd +} // namespace physx + +physx::PxFoundation* PxCreateFoundation(physx::PxU32 version, physx::PxAllocatorCallback& allocator, + physx::PxErrorCallback& errorCallback) +{ + return physx::shdfnd::Foundation::createInstance(version, errorCallback, allocator); +} + +physx::PxFoundation& PxGetFoundation() +{ + return physx::shdfnd::Foundation::getInstance(); +} + +physx::PxProfilerCallback* PxGetProfilerCallback() +{ + return physx::shdfnd::gProfilerCallback; +} + +void PxSetProfilerCallback(physx::PxProfilerCallback* profiler) +{ + physx::shdfnd::gProfilerCallback = profiler; +} diff --git a/src/PhysX/physx/source/foundation/src/PsMathUtils.cpp b/src/PhysX/physx/source/foundation/src/PsMathUtils.cpp new file mode 100644 index 000000000..9aa30690b --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsMathUtils.cpp @@ -0,0 +1,212 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMat33.h" +#include "foundation/PxMathUtils.h" +#include "foundation/PxVec4.h" +#include "foundation/PxAssert.h" +#include "PsMathUtils.h" +#include "PsUtilities.h" +#include "PsBasicTemplates.h" + +using namespace physx; +using namespace physx::shdfnd; +using namespace physx::intrinsics; + +PX_FOUNDATION_API PxQuat physx::PxShortestRotation(const PxVec3& v0, const PxVec3& v1) +{ + const PxReal d = v0.dot(v1); + const PxVec3 cross = v0.cross(v1); + + PxQuat q = d > -1 ? PxQuat(cross.x, cross.y, cross.z, 1 + d) : PxAbs(v0.x) < 0.1f ? PxQuat(0.0f, v0.z, -v0.y, 0.0f) + : PxQuat(v0.y, -v0.x, 0.0f, 0.0f); + + return q.getNormalized(); +} + +namespace +{ +// indexed rotation around axis, with sine and cosine of half-angle +PxQuat indexedRotation(PxU32 axis, PxReal s, PxReal c) +{ + PxReal v[3] = { 0, 0, 0 }; + v[axis] = s; + return PxQuat(v[0], v[1], v[2], c); +} +} + +PX_FOUNDATION_API PxVec3 physx::PxDiagonalize(const PxMat33& m, PxQuat& massFrame) +{ + // jacobi rotation using quaternions (from an idea of Stan Melax, with fix for precision issues) + + const PxU32 MAX_ITERS = 24; + + PxQuat q = PxQuat(PxIdentity); + + PxMat33 d; + for(PxU32 i = 0; i < MAX_ITERS; i++) + { + PxMat33 axes(q); + d = axes.getTranspose() * m * axes; + + PxReal d0 = PxAbs(d[1][2]), d1 = PxAbs(d[0][2]), d2 = PxAbs(d[0][1]); + PxU32 a = PxU32(d0 > d1 && d0 > d2 ? 0 : d1 > d2 ? 1 : 2); // rotation axis index, from largest off-diagonal + // element + + PxU32 a1 = shdfnd::getNextIndex3(a), a2 = shdfnd::getNextIndex3(a1); + if(d[a1][a2] == 0.0f || PxAbs(d[a1][a1] - d[a2][a2]) > 2e6f * PxAbs(2.0f * d[a1][a2])) + break; + + PxReal w = (d[a1][a1] - d[a2][a2]) / (2.0f * d[a1][a2]); // cot(2 * phi), where phi is the rotation angle + PxReal absw = PxAbs(w); + + PxQuat r; + if(absw > 1000) + r = indexedRotation(a, 1 / (4 * w), 1.f); // h will be very close to 1, so use small angle approx instead + else + { + PxReal t = 1 / (absw + PxSqrt(w * w + 1)); // absolute value of tan phi + PxReal h = 1 / PxSqrt(t * t + 1); // absolute value of cos phi + + PX_ASSERT(h != 1); // |w|<1000 guarantees this with typical IEEE754 machine eps (approx 6e-8) + r = indexedRotation(a, PxSqrt((1 - h) / 2) * PxSign(w), PxSqrt((1 + h) / 2)); + } + + q = (q * r).getNormalized(); + } + + massFrame = q; + return PxVec3(d.column0.x, d.column1.y, d.column2.z); +} + +/** +\brief computes a oriented bounding box around the scaled basis. +\param basis Input = skewed basis, Output = (normalized) orthogonal basis. +\return Bounding box extent. +*/ +PxVec3 physx::shdfnd::optimizeBoundingBox(PxMat33& basis) +{ + PxVec3* PX_RESTRICT vec = &basis[0]; // PT: don't copy vectors if not needed... + + // PT: since we store the magnitudes to memory, we can avoid the FCMPs afterwards + PxVec3 magnitude(vec[0].magnitudeSquared(), vec[1].magnitudeSquared(), vec[2].magnitudeSquared()); + + // find indices sorted by magnitude + unsigned int i = magnitude[1] > magnitude[0] ? 1 : 0u; + unsigned int j = magnitude[2] > magnitude[1 - i] ? 2 : 1 - i; + const unsigned int k = 3 - i - j; + + if(magnitude[i] < magnitude[j]) + swap(i, j); + + PX_ASSERT(magnitude[i] >= magnitude[j] && magnitude[i] >= magnitude[k] && magnitude[j] >= magnitude[k]); + + // ortho-normalize basis + + PxReal invSqrt = PxRecipSqrt(magnitude[i]); + magnitude[i] *= invSqrt; + vec[i] *= invSqrt; // normalize the first axis + PxReal dotij = vec[i].dot(vec[j]); + PxReal dotik = vec[i].dot(vec[k]); + magnitude[i] += PxAbs(dotij) + PxAbs(dotik); // elongate the axis by projection of the other two + vec[j] -= vec[i] * dotij; // orthogonize the two remaining axii relative to vec[i] + vec[k] -= vec[i] * dotik; + + magnitude[j] = vec[j].normalize(); + PxReal dotjk = vec[j].dot(vec[k]); + magnitude[j] += PxAbs(dotjk); // elongate the axis by projection of the other one + vec[k] -= vec[j] * dotjk; // orthogonize vec[k] relative to vec[j] + + magnitude[k] = vec[k].normalize(); + + return magnitude; +} + +PxQuat physx::shdfnd::slerp(const PxReal t, const PxQuat& left, const PxQuat& right) +{ + const PxReal quatEpsilon = (PxReal(1.0e-8f)); + + PxReal cosine = left.dot(right); + PxReal sign = PxReal(1); + if(cosine < 0) + { + cosine = -cosine; + sign = PxReal(-1); + } + + PxReal sine = PxReal(1) - cosine * cosine; + + if(sine >= quatEpsilon * quatEpsilon) + { + sine = PxSqrt(sine); + const PxReal angle = PxAtan2(sine, cosine); + const PxReal i_sin_angle = PxReal(1) / sine; + + const PxReal leftw = PxSin(angle * (PxReal(1) - t)) * i_sin_angle; + const PxReal rightw = PxSin(angle * t) * i_sin_angle * sign; + + return left * leftw + right * rightw; + } + + return left; +} + +void physx::shdfnd::integrateTransform(const PxTransform& curTrans, const PxVec3& linvel, const PxVec3& angvel, + PxReal timeStep, PxTransform& result) +{ + result.p = curTrans.p + linvel * timeStep; + + // from void DynamicsContext::integrateAtomPose(PxsRigidBody* atom, Cm::BitMap &shapeChangedMap) const: + // Integrate the rotation using closed form quaternion integrator + PxReal w = angvel.magnitudeSquared(); + + if(w != 0.0f) + { + w = PxSqrt(w); + if(w != 0.0f) + { + const PxReal v = timeStep * w * 0.5f; + const PxReal q = PxCos(v); + const PxReal s = PxSin(v) / w; + + const PxVec3 pqr = angvel * s; + const PxQuat quatVel(pqr.x, pqr.y, pqr.z, 0); + PxQuat out; // need to have temporary, otherwise we may overwrite input if &curTrans == &result. + out = quatVel * curTrans.q; + out.x += curTrans.q.x * q; + out.y += curTrans.q.y * q; + out.z += curTrans.q.z * q; + out.w += curTrans.q.w * q; + result.q = out; + return; + } + } + // orientation stays the same - convert from quat to matrix: + result.q = curTrans.q; +} diff --git a/src/PhysX/physx/source/foundation/src/PsString.cpp b/src/PhysX/physx/source/foundation/src/PsString.cpp new file mode 100644 index 000000000..1916fb0df --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsString.cpp @@ -0,0 +1,185 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsString.h" +#include +#include +#include + +#if PX_WINDOWS_FAMILY +#pragma warning(push) +#pragma warning(disable : 4996) // unsafe string functions +#endif + +#if PX_PS4 || PX_APPLE_FAMILY +#pragma clang diagnostic push +// error : format string is not a string literal +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif + +namespace physx +{ +namespace shdfnd +{ +// cross-platform implementations + +int32_t strcmp(const char* str1, const char* str2) +{ + return ::strcmp(str1, str2); +} + +int32_t strncmp(const char* str1, const char* str2, size_t count) +{ + return ::strncmp(str1, str2, count); +} + +int32_t snprintf(char* dst, size_t dstSize, const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int32_t r = shdfnd::vsnprintf(dst, dstSize, format, arg); + va_end(arg); + return r; +} + +int32_t sscanf(const char* buffer, const char* format, ...) +{ + va_list arg; + va_start(arg, format); +#if (PX_VC < 12) && !PX_LINUX + int32_t r = ::sscanf(buffer, format, arg); +#else + int32_t r = ::vsscanf(buffer, format, arg); +#endif + va_end(arg); + + return r; +} + +size_t strlcpy(char* dst, size_t dstSize, const char* src) +{ + size_t i = 0; + if(dst && dstSize) + { + for(; i + 1 < dstSize && src[i]; i++) // copy up to dstSize-1 bytes + dst[i] = src[i]; + dst[i] = 0; // always null-terminate + } + + while(src[i]) // read any remaining characters in the src string to get the length + i++; + + return i; +} + +size_t strlcat(char* dst, size_t dstSize, const char* src) +{ + size_t i = 0, s = 0; + if(dst && dstSize) + { + s = strlen(dst); + for(; i + s + 1 < dstSize && src[i]; i++) // copy until total is at most dstSize-1 + dst[i + s] = src[i]; + dst[i + s] = 0; // always null-terminate + } + + while(src[i]) // read any remaining characters in the src string to get the length + i++; + + return i + s; +} + +void strlwr(char* str) +{ + for(; *str; str++) + if(*str >= 'A' && *str <= 'Z') + *str += 32; +} + +void strupr(char* str) +{ + for(; *str; str++) + if(*str >= 'a' && *str <= 'z') + *str -= 32; +} + +int32_t vsnprintf(char* dst, size_t dstSize, const char* src, va_list arg) +{ + +#if PX_VC // MSVC is not C99-compliant... + int32_t result = dst ? ::vsnprintf(dst, dstSize, src, arg) : -1; + if(dst && (result == int32_t(dstSize) || result < 0)) + dst[dstSize - 1] = 0; // string was truncated or there wasn't room for the NULL + if(result < 0) + result = _vscprintf(src, arg); // work out how long the answer would have been. +#else + int32_t result = ::vsnprintf(dst, dstSize, src, arg); +#endif + return result; +} + +int32_t stricmp(const char* str, const char* str1) +{ +#if PX_VC + return (::_stricmp(str, str1)); +#else + return (::strcasecmp(str, str1)); +#endif +} + +int32_t strnicmp(const char* str, const char* str1, size_t n) +{ +#if PX_VC + return (::_strnicmp(str, str1, n)); +#else + return (::strncasecmp(str, str1, n)); +#endif +} + +void printFormatted(const char* format, ...) +{ + char buf[MAX_PRINTFORMATTED_LENGTH]; + + va_list arg; + va_start(arg, format); + vsnprintf(buf, MAX_PRINTFORMATTED_LENGTH, format, arg); + va_end(arg); + + printString(buf); +} +} +} + +#if PX_PS4 || PX_APPLE_FAMILY +#pragma clang diagnostic pop +#endif + +#if PX_WINDOWS_FAMILY +#pragma warning(pop) +#endif diff --git a/src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp b/src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp new file mode 100644 index 000000000..f174168ed --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp @@ -0,0 +1,129 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMath.h" + +#include "PsFoundation.h" +#include "PsTempAllocator.h" +#include "PsArray.h" +#include "PsMutex.h" +#include "PsAtomic.h" +#include "PsIntrinsics.h" +#include "PsBitUtils.h" + +#if PX_VC +#pragma warning(disable : 4706) // assignment within conditional expression +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace +{ +typedef TempAllocatorChunk Chunk; +typedef Array AllocFreeTable; + +PX_INLINE Foundation::AllocFreeTable& getFreeTable() +{ + return getFoundation().getTempAllocFreeTable(); +} +PX_INLINE Foundation::Mutex& getMutex() +{ + return getFoundation().getTempAllocMutex(); +} + +const PxU32 sMinIndex = 8; // 256B min +const PxU32 sMaxIndex = 17; // 128kB max +} + +void* TempAllocator::allocate(size_t size, const char* filename, int line) +{ + if(!size) + return 0; + + uint32_t index = PxMax(highestSetBit(uint32_t(size) + sizeof(Chunk) - 1), sMinIndex); + + Chunk* chunk = 0; + if(index < sMaxIndex) + { + Foundation::Mutex::ScopedLock lock(getMutex()); + + // find chunk up to 16x bigger than necessary + Chunk** it = getFreeTable().begin() + index - sMinIndex; + Chunk** end = PxMin(it + 3, getFreeTable().end()); + while(it < end && !(*it)) + ++it; + + if(it < end) + { + // pop top off freelist + chunk = *it; + *it = chunk->mNext; + index = uint32_t(it - getFreeTable().begin() + sMinIndex); + } + else + // create new chunk + chunk = reinterpret_cast(NonTrackingAllocator().allocate(size_t(2 << index), filename, line)); + } + else + { + // too big for temp allocation, forward to base allocator + chunk = reinterpret_cast(NonTrackingAllocator().allocate(size + sizeof(Chunk), filename, line)); + } + + chunk->mIndex = index; + void* ret = chunk + 1; + PX_ASSERT((size_t(ret) & 0xf) == 0); // SDK types require at minimum 16 byte allignment. + return ret; +} + +void TempAllocator::deallocate(void* ptr) +{ + if(!ptr) + return; + + Chunk* chunk = reinterpret_cast(ptr) - 1; + uint32_t index = chunk->mIndex; + + if(index >= sMaxIndex) + return NonTrackingAllocator().deallocate(chunk); + + Foundation::Mutex::ScopedLock lock(getMutex()); + + index -= sMinIndex; + if(getFreeTable().size() <= index) + getFreeTable().resize(index + 1); + + chunk->mNext = getFreeTable()[index]; + getFreeTable()[index] = chunk; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/PsUtilities.cpp b/src/PhysX/physx/source/foundation/src/PsUtilities.cpp new file mode 100644 index 000000000..1d1e174ba --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsUtilities.cpp @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMat33.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsUtilities.h" +#include "PsUserAllocated.h" +#include "PsFPU.h" + +namespace physx +{ +namespace shdfnd +{ + +bool checkValid(const float& f) +{ + return PxIsFinite(f); +} +bool checkValid(const PxVec3& v) +{ + return PxIsFinite(v.x) && PxIsFinite(v.y) && PxIsFinite(v.z); +} + +bool checkValid(const PxTransform& t) +{ + return checkValid(t.p) && checkValid(t.q); +} + +bool checkValid(const PxQuat& q) +{ + return PxIsFinite(q.x) && PxIsFinite(q.y) && PxIsFinite(q.z) && PxIsFinite(q.w); +} +bool checkValid(const PxMat33& m) +{ + return PxIsFinite(m(0, 0)) && PxIsFinite(m(1, 0)) && PxIsFinite(m(2, 0)) && PxIsFinite(m(0, 1)) && + PxIsFinite(m(1, 1)) && PxIsFinite(m(2, 1)) && PxIsFinite(m(0, 3)) && PxIsFinite(m(1, 3)) && + PxIsFinite(m(2, 3)); +} +bool checkValid(const char* string) +{ + static const PxU32 maxLength = 4096; + return strnlen(string, maxLength) != maxLength; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp new file mode 100644 index 000000000..9ca9249cf --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp @@ -0,0 +1,102 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#include "Ps.h" +#include "PsAtomic.h" + +#if ! PX_EMSCRIPTEN +#define PAUSE() asm("nop") +#else +#define PAUSE() +#endif + +namespace physx +{ +namespace shdfnd +{ + +void* atomicCompareExchangePointer(volatile void** dest, void* exch, void* comp) +{ + return __sync_val_compare_and_swap(const_cast(dest), comp, exch); +} + +int32_t atomicCompareExchange(volatile int32_t* dest, int32_t exch, int32_t comp) +{ + return __sync_val_compare_and_swap(dest, comp, exch); +} + +int32_t atomicIncrement(volatile int32_t* val) +{ + return __sync_add_and_fetch(val, 1); +} + +int32_t atomicDecrement(volatile int32_t* val) +{ + return __sync_sub_and_fetch(val, 1); +} + +int32_t atomicAdd(volatile int32_t* val, int32_t delta) +{ + return __sync_add_and_fetch(val, delta); +} + +int32_t atomicMax(volatile int32_t* val, int32_t val2) +{ + int32_t oldVal, newVal; + + do + { + PAUSE(); + oldVal = *val; + + if(val2 > oldVal) + newVal = val2; + else + newVal = oldVal; + + } while(atomicCompareExchange(val, newVal, oldVal) != oldVal); + + return *val; +} + +int32_t atomicExchange(volatile int32_t* val, int32_t val2) +{ + int32_t newVal, oldVal; + + do + { + PAUSE(); + oldVal = *val; + newVal = val2; + } while(atomicCompareExchange(val, newVal, oldVal) != oldVal); + + return oldVal; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp new file mode 100644 index 000000000..dda248c39 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp @@ -0,0 +1,58 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxSimpleTypes.h" +#include "PsCpu.h" + +#if PX_X86 && !defined(__EMSCRIPTEN__) +#define cpuid(op, reg) \ + __asm__ __volatile__("pushl %%ebx \n\t" /* save %ebx */ \ + "cpuid \n\t" \ + "movl %%ebx, %1 \n\t" /* save what cpuid just put in %ebx */ \ + "popl %%ebx \n\t" /* restore the old %ebx */ \ + : "=a"(reg[0]), "=r"(reg[1]), "=c"(reg[2]), "=d"(reg[3]) \ + : "a"(op) \ + : "cc") +#else +#define cpuid(op, reg) reg[0] = reg[1] = reg[2] = reg[3] = 0; +#endif + +namespace physx +{ +namespace shdfnd +{ + +uint8_t Cpu::getCpuId() +{ + uint32_t cpuInfo[4]; + cpuid(1, cpuInfo); + return static_cast(cpuInfo[1] >> 24); // APIC Physical ID +} +} +} diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp new file mode 100644 index 000000000..b6fc57c40 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#include "PsFPU.h" + +#if !(defined(__CYGWIN__) || PX_ANDROID || PX_PS4) +#include +PX_COMPILE_TIME_ASSERT(8 * sizeof(uint32_t) >= sizeof(fenv_t)); +#endif + +#if PX_OSX +// osx defines SIMD as standard for floating point operations. +#include +#endif + +physx::shdfnd::FPUGuard::FPUGuard() +{ +#if defined(__CYGWIN__) +#pragma message "FPUGuard::FPUGuard() is not implemented" +#elif PX_ANDROID +// not supported unless ARM_HARD_FLOAT is enabled. +#elif PX_PS4 + // not supported + PX_UNUSED(mControlWords); +#elif PX_OSX + mControlWords[0] = _mm_getcsr(); + // set default (disable exceptions: _MM_MASK_MASK) and FTZ (_MM_FLUSH_ZERO_ON), DAZ (_MM_DENORMALS_ZERO_ON: (1<<6)) + _mm_setcsr(_MM_MASK_MASK | _MM_FLUSH_ZERO_ON | (1 << 6)); +#elif defined(__EMSCRIPTEN__) +// not supported +#else + PX_COMPILE_TIME_ASSERT(sizeof(fenv_t) <= sizeof(mControlWords)); + + fegetenv(reinterpret_cast(mControlWords)); + fesetenv(FE_DFL_ENV); + +#if PX_LINUX + // need to explicitly disable exceptions because fesetenv does not modify + // the sse control word on 32bit linux (64bit is fine, but do it here just be sure) + fedisableexcept(FE_ALL_EXCEPT); +#endif + +#endif +} + +physx::shdfnd::FPUGuard::~FPUGuard() +{ +#if defined(__CYGWIN__) +#pragma message "FPUGuard::~FPUGuard() is not implemented" +#elif PX_ANDROID +// not supported unless ARM_HARD_FLOAT is enabled. +#elif PX_PS4 +// not supported +#elif PX_OSX + // restore control word and clear exception flags + // (setting exception state flags cause exceptions on the first following fp operation) + _mm_setcsr(mControlWords[0] & ~_MM_EXCEPT_MASK); +#elif defined(__EMSCRIPTEN__) +// not supported +#else + fesetenv(reinterpret_cast(mControlWords)); +#endif +} + +PX_FOUNDATION_API void physx::shdfnd::enableFPExceptions() +{ +#if PX_LINUX && !defined(__EMSCRIPTEN__) + feclearexcept(FE_ALL_EXCEPT); + feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW); +#elif PX_OSX + // clear any pending exceptions + // (setting exception state flags cause exceptions on the first following fp operation) + uint32_t control = _mm_getcsr() & ~_MM_EXCEPT_MASK; + + // enable all fp exceptions except inexact and underflow (common, benign) + // note: denorm has to be disabled as well because underflow can create denorms + _mm_setcsr((control & ~_MM_MASK_MASK) | _MM_MASK_INEXACT | _MM_MASK_UNDERFLOW | _MM_MASK_DENORM); + +#endif +} + +PX_FOUNDATION_API void physx::shdfnd::disableFPExceptions() +{ +#if PX_LINUX && !defined(__EMSCRIPTEN__) + fedisableexcept(FE_ALL_EXCEPT); +#elif PX_OSX + // clear any pending exceptions + // (setting exception state flags cause exceptions on the first following fp operation) + uint32_t control = _mm_getcsr() & ~_MM_EXCEPT_MASK; + _mm_setcsr(control | _MM_MASK_MASK); +#endif +} diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp new file mode 100644 index 000000000..194e23ff3 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp @@ -0,0 +1,172 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" +#include "foundation/PxErrorCallback.h" + +#include "Ps.h" +#include "PsFoundation.h" +#include "PsUserAllocated.h" +#include "PsMutex.h" +#include "PsAtomic.h" +#include "PsThread.h" + +#include + +namespace physx +{ +namespace shdfnd +{ + +namespace +{ +struct MutexUnixImpl +{ + pthread_mutex_t lock; + Thread::Id owner; +}; + +MutexUnixImpl* getMutex(MutexImpl* impl) +{ + return reinterpret_cast(impl); +} +} + +MutexImpl::MutexImpl() +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#if !PX_ANDROID + // mimic default windows behavior where applicable + pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); +#endif + pthread_mutex_init(&getMutex(this)->lock, &attr); + pthread_mutexattr_destroy(&attr); +} + +MutexImpl::~MutexImpl() +{ + pthread_mutex_destroy(&getMutex(this)->lock); +} + +void MutexImpl::lock() +{ + int err = pthread_mutex_lock(&getMutex(this)->lock); + PX_ASSERT(!err); + PX_UNUSED(err); + +#if PX_DEBUG + getMutex(this)->owner = Thread::getId(); +#endif +} + +bool MutexImpl::trylock() +{ + bool success = !pthread_mutex_trylock(&getMutex(this)->lock); +#if PX_DEBUG + if(success) + getMutex(this)->owner = Thread::getId(); +#endif + return success; +} + +void MutexImpl::unlock() +{ +#if PX_DEBUG + if(getMutex(this)->owner != Thread::getId()) + { + shdfnd::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Mutex must be unlocked only by thread that has already acquired lock"); + return; + } +#endif + + int err = pthread_mutex_unlock(&getMutex(this)->lock); + PX_ASSERT(!err); + PX_UNUSED(err); +} + +uint32_t MutexImpl::getSize() +{ + return sizeof(MutexUnixImpl); +} + +class ReadWriteLockImpl +{ + public: + Mutex mutex; + volatile int readerCounter; +}; + +ReadWriteLock::ReadWriteLock() +{ + mImpl = reinterpret_cast(PX_ALLOC(sizeof(ReadWriteLockImpl), "ReadWriteLockImpl")); + PX_PLACEMENT_NEW(mImpl, ReadWriteLockImpl); + + mImpl->readerCounter = 0; +} + +ReadWriteLock::~ReadWriteLock() +{ + mImpl->~ReadWriteLockImpl(); + PX_FREE(mImpl); +} + +void ReadWriteLock::lockReader(bool takeLock) +{ + if(takeLock) + mImpl->mutex.lock(); + + atomicIncrement(&mImpl->readerCounter); + + if(takeLock) + mImpl->mutex.unlock(); +} + +void ReadWriteLock::lockWriter() +{ + mImpl->mutex.lock(); + + // spin lock until no readers + while(mImpl->readerCounter); +} + +void ReadWriteLock::unlockReader() +{ + atomicDecrement(&mImpl->readerCounter); +} + +void ReadWriteLock::unlockWriter() +{ + mImpl->mutex.unlock(); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp new file mode 100644 index 000000000..a19eff11e --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp @@ -0,0 +1,52 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsString.h" +#include + +#if PX_ANDROID +#include +#endif + +namespace physx +{ +namespace shdfnd +{ + +void printString(const char* str) +{ +#if PX_ANDROID + __android_log_print(ANDROID_LOG_INFO, "PsPrintString", "%s", str); +#else + puts(str); +#endif +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp new file mode 100644 index 000000000..a3b1e2e68 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp @@ -0,0 +1,156 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsAllocator.h" +#include "PsAtomic.h" +#include "PsSList.h" +#include "PsThread.h" +#include + +#if PX_IOS || PX_EMSCRIPTEN +#define USE_MUTEX +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace +{ +#if defined(USE_MUTEX) +class ScopedMutexLock +{ + pthread_mutex_t& mMutex; + + public: + PX_INLINE ScopedMutexLock(pthread_mutex_t& mutex) : mMutex(mutex) + { + pthread_mutex_lock(&mMutex); + } + + PX_INLINE ~ScopedMutexLock() + { + pthread_mutex_unlock(&mMutex); + } +}; + +typedef ScopedMutexLock ScopedLock; +#else +struct ScopedSpinLock +{ + PX_FORCE_INLINE ScopedSpinLock(volatile int32_t& lock) : mLock(lock) + { + while(__sync_lock_test_and_set(&mLock, 1)) + { + // spinning without atomics is usually + // causing less bus traffic. -> only one + // CPU is modifying the cache line. + while(lock) + PxSpinLockPause(); + } + } + + PX_FORCE_INLINE ~ScopedSpinLock() + { + __sync_lock_release(&mLock); + } + + private: + volatile int32_t& mLock; +}; + +typedef ScopedSpinLock ScopedLock; +#endif + +struct SListDetail +{ + SListEntry* head; +#if defined(USE_MUTEX) + pthread_mutex_t lock; +#else + volatile int32_t lock; +#endif +}; + +template +SListDetail* getDetail(T* impl) +{ + return reinterpret_cast(impl); +} +} + +SListImpl::SListImpl() +{ + getDetail(this)->head = NULL; + +#if defined(USE_MUTEX) + pthread_mutex_init(&getDetail(this)->lock, NULL); +#else + getDetail(this)->lock = 0; // 0 == unlocked +#endif +} + +SListImpl::~SListImpl() +{ +#if defined(USE_MUTEX) + pthread_mutex_destroy(&getDetail(this)->lock); +#endif +} + +void SListImpl::push(SListEntry* entry) +{ + ScopedLock lock(getDetail(this)->lock); + entry->mNext = getDetail(this)->head; + getDetail(this)->head = entry; +} + +SListEntry* SListImpl::pop() +{ + ScopedLock lock(getDetail(this)->lock); + SListEntry* result = getDetail(this)->head; + if(result != NULL) + getDetail(this)->head = result->mNext; + return result; +} + +SListEntry* SListImpl::flush() +{ + ScopedLock lock(getDetail(this)->lock); + SListEntry* result = getDetail(this)->head; + getDetail(this)->head = NULL; + return result; +} + +uint32_t SListImpl::getSize() +{ + return sizeof(SListDetail); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp new file mode 100644 index 000000000..0d273cc9d --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp @@ -0,0 +1,483 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIntrinsics.h" + +#include "PsSocket.h" + +#include +#include +#include +#include +#if !PX_PS4 +#include +#include +#else +#include +#endif +#include +#include +#include + +#define INVALID_SOCKET -1 + +#ifndef SOMAXCONN +#define SOMAXCONN 5 +#endif + +namespace physx +{ +namespace shdfnd +{ + +const uint32_t Socket::DEFAULT_BUFFER_SIZE = 32768; + +class SocketImpl +{ + public: + SocketImpl(bool isBlocking); + virtual ~SocketImpl(); + + bool connect(const char* host, uint16_t port, uint32_t timeout); + bool listen(uint16_t port); + bool accept(bool block); + void disconnect(); + + void setBlocking(bool blocking); + + virtual uint32_t write(const uint8_t* data, uint32_t length); + virtual bool flush(); + uint32_t read(uint8_t* data, uint32_t length); + + PX_FORCE_INLINE bool isBlocking() const + { + return mIsBlocking; + } + PX_FORCE_INLINE bool isConnected() const + { + return mIsConnected; + } + PX_FORCE_INLINE const char* getHost() const + { + return mHost; + } + PX_FORCE_INLINE uint16_t getPort() const + { + return mPort; + } + + protected: + bool nonBlockingTimeout() const; + + int32_t mSocket; + int32_t mListenSocket; + const char* mHost; + uint16_t mPort; + bool mIsConnected; + bool mIsBlocking; + bool mListenMode; +}; + +void socketSetBlockingInternal(int32_t socket, bool blocking); + +SocketImpl::SocketImpl(bool isBlocking) +: mSocket(INVALID_SOCKET) +, mListenSocket(INVALID_SOCKET) +, mHost(NULL) +, mPort(0) +, mIsConnected(false) +, mIsBlocking(isBlocking) +, mListenMode(false) +{ +} + +SocketImpl::~SocketImpl() +{ +} + +bool SocketImpl::connect(const char* host, uint16_t port, uint32_t timeout) +{ + sockaddr_in socketAddress; + intrinsics::memSet(&socketAddress, 0, sizeof(sockaddr_in)); + socketAddress.sin_family = AF_INET; + socketAddress.sin_port = htons(port); + +#if PX_PS4 + socketAddress.sin_addr.s_addr = resolveName(host, timeout); +#else + // get host + hostent* hp = gethostbyname(host); + if(!hp) + { + in_addr a; + a.s_addr = inet_addr(host); + hp = gethostbyaddr(reinterpret_cast(&a), sizeof(in_addr), AF_INET); + if(!hp) + return false; + } + intrinsics::memCopy(&socketAddress.sin_addr, hp->h_addr_list[0], hp->h_length); +#endif + // connect + mSocket = socket(AF_INET, SOCK_STREAM, 0); + if(mSocket == INVALID_SOCKET) + return false; + + socketSetBlockingInternal(mSocket, false); + + int connectRet = ::connect(mSocket, reinterpret_cast(&socketAddress), sizeof(socketAddress)); + if(connectRet < 0) + { + if(errno != EINPROGRESS) + { + disconnect(); + return false; + } + + // Setup select function call to monitor the connect call. + fd_set writefs; + fd_set exceptfs; + FD_ZERO(&writefs); + FD_ZERO(&exceptfs); + FD_SET(mSocket, &writefs); + FD_SET(mSocket, &exceptfs); + timeval timeout_; + timeout_.tv_sec = timeout / 1000; + timeout_.tv_usec = (timeout % 1000) * 1000; + int selret = ::select(mSocket + 1, NULL, &writefs, &exceptfs, &timeout_); + int excepted = FD_ISSET(mSocket, &exceptfs); + int canWrite = FD_ISSET(mSocket, &writefs); + if(selret != 1 || excepted || !canWrite) + { + disconnect(); + return false; + } + + // check if we are really connected, above code seems to return + // true if host is a unix machine even if the connection was + // not accepted. + char buffer; + if(recv(mSocket, &buffer, 0, 0) < 0) + { + if(errno != EWOULDBLOCK) + { + disconnect(); + return false; + } + } + } + + socketSetBlockingInternal(mSocket, mIsBlocking); + +#if PX_APPLE_FAMILY + int noSigPipe = 1; + setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(int)); +#endif + + mIsConnected = true; + mPort = port; + mHost = host; + return true; +} + +bool SocketImpl::listen(uint16_t port) +{ + mListenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if(mListenSocket == INVALID_SOCKET) + return false; + + // enable address reuse: "Address already in use" error message + int yes = 1; + if(setsockopt(mListenSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) + return false; + + mListenMode = true; + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + intrinsics::memSet(addr.sin_zero, '\0', sizeof addr.sin_zero); + + return bind(mListenSocket, reinterpret_cast(&addr), sizeof(addr)) != -1 && + ::listen(mListenSocket, SOMAXCONN) != -1; +} + +bool SocketImpl::accept(bool block) +{ + if(mIsConnected || !mListenMode) + return false; + + // set the listen socket to be non-blocking. + socketSetBlockingInternal(mListenSocket, block); + int32_t clientSocket = ::accept(mListenSocket, 0, 0); + if(clientSocket == INVALID_SOCKET) + return false; + + mSocket = clientSocket; + mIsConnected = true; + socketSetBlockingInternal(mSocket, mIsBlocking); // force the mode to whatever the user set + + return mIsConnected; +} + +void SocketImpl::disconnect() +{ + if(mListenSocket != INVALID_SOCKET) + { + close(mListenSocket); + mListenSocket = INVALID_SOCKET; + } + if(mSocket != INVALID_SOCKET) + { + if(mIsConnected) + { + socketSetBlockingInternal(mSocket, true); + shutdown(mSocket, SHUT_RDWR); + } + close(mSocket); + mSocket = INVALID_SOCKET; + } + mIsConnected = false; + mListenMode = false; + mPort = 0; + mHost = NULL; +} + +bool SocketImpl::nonBlockingTimeout() const +{ + return !mIsBlocking && errno == EWOULDBLOCK; +} + +#if !PX_PS4 +void socketSetBlockingInternal(int32_t socket, bool blocking) +{ + int mode = fcntl(socket, F_GETFL, 0); + if(!blocking) + mode |= O_NONBLOCK; + else + mode &= ~O_NONBLOCK; + fcntl(socket, F_SETFL, mode); +} +#endif + +// should be cross-platform from here down + +void SocketImpl::setBlocking(bool blocking) +{ + if(blocking != mIsBlocking) + { + mIsBlocking = blocking; + if(isConnected()) + socketSetBlockingInternal(mSocket, blocking); + } +} + +bool SocketImpl::flush() +{ + return true; +} + +uint32_t SocketImpl::write(const uint8_t* data, uint32_t length) +{ + if(length == 0) + return 0; + + int sent = send(mSocket, reinterpret_cast(data), int32_t(length), 0); + + if(sent <= 0 && !nonBlockingTimeout()) + disconnect(); + + return uint32_t(sent > 0 ? sent : 0); +} + +uint32_t SocketImpl::read(uint8_t* data, uint32_t length) +{ + if(length == 0) + return 0; + + int32_t received = recv(mSocket, reinterpret_cast(data), int32_t(length), 0); + + if(received <= 0 && !nonBlockingTimeout()) + disconnect(); + + return uint32_t(received > 0 ? received : 0); +} + +class BufferedSocketImpl : public SocketImpl +{ + public: + BufferedSocketImpl(bool isBlocking) : SocketImpl(isBlocking), mBufferPos(0) + { + } + virtual ~BufferedSocketImpl() + { + } + bool flush(); + uint32_t write(const uint8_t* data, uint32_t length); + + private: + uint32_t mBufferPos; + uint8_t mBuffer[Socket::DEFAULT_BUFFER_SIZE]; +}; + +bool BufferedSocketImpl::flush() +{ + uint32_t totalBytesWritten = 0; + + while(totalBytesWritten < mBufferPos && mIsConnected) + totalBytesWritten += int32_t(SocketImpl::write(mBuffer + totalBytesWritten, mBufferPos - totalBytesWritten)); + + bool ret = (totalBytesWritten == mBufferPos); + mBufferPos = 0; + return ret; +} + +uint32_t BufferedSocketImpl::write(const uint8_t* data, uint32_t length) +{ + uint32_t bytesWritten = 0; + while(mBufferPos + length >= Socket::DEFAULT_BUFFER_SIZE) + { + uint32_t currentChunk = Socket::DEFAULT_BUFFER_SIZE - mBufferPos; + intrinsics::memCopy(mBuffer + mBufferPos, data + bytesWritten, currentChunk); + bytesWritten += uint32_t(currentChunk); // for the user, this is consumed even if we fail to shove it down a + // non-blocking socket + + uint32_t sent = SocketImpl::write(mBuffer, Socket::DEFAULT_BUFFER_SIZE); + mBufferPos = Socket::DEFAULT_BUFFER_SIZE - sent; + + if(sent < Socket::DEFAULT_BUFFER_SIZE) // non-blocking or error + { + if(sent) // we can reasonably hope this is rare + intrinsics::memMove(mBuffer, mBuffer + sent, mBufferPos); + + return bytesWritten; + } + length -= currentChunk; + } + + if(length > 0) + { + intrinsics::memCopy(mBuffer + mBufferPos, data + bytesWritten, length); + bytesWritten += length; + mBufferPos += length; + } + + return bytesWritten; +} + +Socket::Socket(bool inIsBuffering, bool isBlocking) +{ + if(inIsBuffering) + { + void* mem = PX_ALLOC(sizeof(BufferedSocketImpl), "BufferedSocketImpl"); + mImpl = PX_PLACEMENT_NEW(mem, BufferedSocketImpl)(isBlocking); + } + else + { + void* mem = PX_ALLOC(sizeof(SocketImpl), "SocketImpl"); + mImpl = PX_PLACEMENT_NEW(mem, SocketImpl)(isBlocking); + } +} + +Socket::~Socket() +{ + mImpl->flush(); + mImpl->disconnect(); + mImpl->~SocketImpl(); + PX_FREE(mImpl); +} + +bool Socket::connect(const char* host, uint16_t port, uint32_t timeout) +{ + return mImpl->connect(host, port, timeout); +} + +bool Socket::listen(uint16_t port) +{ + return mImpl->listen(port); +} + +bool Socket::accept(bool block) +{ + return mImpl->accept(block); +} + +void Socket::disconnect() +{ + mImpl->disconnect(); +} + +bool Socket::isConnected() const +{ + return mImpl->isConnected(); +} + +const char* Socket::getHost() const +{ + return mImpl->getHost(); +} + +uint16_t Socket::getPort() const +{ + return mImpl->getPort(); +} + +bool Socket::flush() +{ + if(!mImpl->isConnected()) + return false; + return mImpl->flush(); +} + +uint32_t Socket::write(const uint8_t* data, uint32_t length) +{ + if(!mImpl->isConnected()) + return 0; + return mImpl->write(data, length); +} + +uint32_t Socket::read(uint8_t* data, uint32_t length) +{ + if(!mImpl->isConnected()) + return 0; + return mImpl->read(data, length); +} + +void Socket::setBlocking(bool blocking) +{ + mImpl->setBlocking(blocking); +} + +bool Socket::isBlocking() const +{ + return mImpl->isBlocking(); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp new file mode 100644 index 000000000..9336fd3e7 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp @@ -0,0 +1,164 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" + +#include "Ps.h" +#include "PsUserAllocated.h" +#include "PsSync.h" + +#include +#include +#include +#include +#include + +namespace physx +{ +namespace shdfnd +{ + +namespace +{ +class _SyncImpl +{ + public: + pthread_mutex_t mutex; + pthread_cond_t cond; + volatile int setCounter; + volatile bool is_set; +}; + +_SyncImpl* getSync(SyncImpl* impl) +{ + return reinterpret_cast<_SyncImpl*>(impl); +} +} + +uint32_t SyncImpl::getSize() +{ + return sizeof(_SyncImpl); +} + +struct PxUnixScopeLock +{ + PxUnixScopeLock(pthread_mutex_t& m) : mMutex(m) + { + pthread_mutex_lock(&mMutex); + } + + ~PxUnixScopeLock() + { + pthread_mutex_unlock(&mMutex); + } + + private: + pthread_mutex_t& mMutex; +}; + +SyncImpl::SyncImpl() +{ + int status = pthread_mutex_init(&getSync(this)->mutex, 0); + PX_ASSERT(!status); + status = pthread_cond_init(&getSync(this)->cond, 0); + PX_ASSERT(!status); + PX_UNUSED(status); + getSync(this)->is_set = false; + getSync(this)->setCounter = 0; +} + +SyncImpl::~SyncImpl() +{ + pthread_cond_destroy(&getSync(this)->cond); + pthread_mutex_destroy(&getSync(this)->mutex); +} + +void SyncImpl::reset() +{ + PxUnixScopeLock lock(getSync(this)->mutex); + getSync(this)->is_set = false; +} + +void SyncImpl::set() +{ + PxUnixScopeLock lock(getSync(this)->mutex); + if(!getSync(this)->is_set) + { + getSync(this)->is_set = true; + getSync(this)->setCounter++; + pthread_cond_broadcast(&getSync(this)->cond); + } +} + +bool SyncImpl::wait(uint32_t ms) +{ + PxUnixScopeLock lock(getSync(this)->mutex); + int lastSetCounter = getSync(this)->setCounter; + if(!getSync(this)->is_set) + { + if(ms == uint32_t(-1)) + { + // have to loop here and check is_set since pthread_cond_wait can return successfully + // even if it was not signaled by pthread_cond_broadcast (OS efficiency design decision) + int status = 0; + while(!status && !getSync(this)->is_set && (lastSetCounter == getSync(this)->setCounter)) + status = pthread_cond_wait(&getSync(this)->cond, &getSync(this)->mutex); + PX_ASSERT((!status && getSync(this)->is_set) || (lastSetCounter != getSync(this)->setCounter)); + } + else + { + timespec ts; + timeval tp; + gettimeofday(&tp, NULL); + uint32_t sec = ms / 1000; + uint32_t usec = (ms - 1000 * sec) * 1000; + + // sschirm: taking into account that us might accumulate to a second + // otherwise the pthread_cond_timedwait complains on osx. + usec = tp.tv_usec + usec; + uint32_t div_sec = usec / 1000000; + uint32_t rem_usec = usec - div_sec * 1000000; + + ts.tv_sec = tp.tv_sec + sec + div_sec; + ts.tv_nsec = rem_usec * 1000; + + // have to loop here and check is_set since pthread_cond_timedwait can return successfully + // even if it was not signaled by pthread_cond_broadcast (OS efficiency design decision) + int status = 0; + while(!status && !getSync(this)->is_set && (lastSetCounter == getSync(this)->setCounter)) + status = pthread_cond_timedwait(&getSync(this)->cond, &getSync(this)->mutex, &ts); + PX_ASSERT((!status && getSync(this)->is_set) || (status == ETIMEDOUT) || + (lastSetCounter != getSync(this)->setCounter)); + } + } + return getSync(this)->is_set || (lastSetCounter != getSync(this)->setCounter); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp new file mode 100644 index 000000000..a868512ad --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp @@ -0,0 +1,504 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" +#include "foundation/PxErrorCallback.h" + +#include "Ps.h" +#include "PsFoundation.h" +#include "PsAtomic.h" +#include "PsThread.h" + +#include +#if !PX_APPLE_FAMILY && !defined(ANDROID) && !defined(__CYGWIN__) && !PX_PS4 && !PX_EMSCRIPTEN +#include // PTHREAD_STACK_MIN +#endif +#include +#include +#include +#if !PX_PS4 +#include +#if !PX_APPLE_FAMILY && !PX_EMSCRIPTEN +#include +#include +#endif +#endif + +#if PX_APPLE_FAMILY +#include +#include +#include +#include +#endif + +// fwd +#if defined(ANDROID) +extern "C" { +int android_getCpuCount(void); +} +#endif + +#define PxSpinLockPause() asm("nop") + +namespace physx +{ +namespace shdfnd +{ + +#if PX_PS4 + uint64_t setAffinityMaskPS4(pthread_t, uint32_t); + int32_t setNamePS4(pthread_t, const char*); + int32_t pthreadCreatePS4(pthread_t *, const pthread_attr_t *, void *(*) (void *), void *, const char*); +#endif + +namespace +{ + +typedef enum +{ + _PxThreadNotStarted, + _PxThreadStarted, + _PxThreadStopped +} PxThreadState; + +class _ThreadImpl +{ + public: + ThreadImpl::ExecuteFn fn; + void* arg; + volatile int32_t quitNow; + volatile int32_t threadStarted; + volatile int32_t state; + + pthread_t thread; + pid_t tid; + + uint32_t affinityMask; + const char* name; +}; + +_ThreadImpl* getThread(ThreadImpl* impl) +{ + return reinterpret_cast<_ThreadImpl*>(impl); +} + +static void setTid(_ThreadImpl& threadImpl) +{ +// query TID +#if PX_PS4 || (defined (TARGET_OS_TV) && TARGET_OS_TV) +// AM: TODO: neither of the below are implemented +#elif PX_APPLE_FAMILY + threadImpl.tid = syscall(SYS_gettid); +#elif PX_EMSCRIPTEN + threadImpl.tid = pthread_self(); +#else + threadImpl.tid = syscall(__NR_gettid); +#endif + + // notify/unblock parent thread + atomicCompareExchange(&(threadImpl.threadStarted), 1, 0); +} + +void* PxThreadStart(void* arg) +{ + _ThreadImpl* impl = getThread(reinterpret_cast(arg)); + impl->state = _PxThreadStarted; + + // run setTid in thread's context + setTid(*impl); + + // then run either the passed in function or execute from the derived class (Runnable). + if(impl->fn) + (*impl->fn)(impl->arg); + else if(impl->arg) + (reinterpret_cast(impl->arg))->execute(); + return 0; +} +} + +uint32_t ThreadImpl::getSize() +{ + return sizeof(_ThreadImpl); +} + +ThreadImpl::Id ThreadImpl::getId() +{ + return Id(pthread_self()); +} + +ThreadImpl::ThreadImpl() +{ + getThread(this)->thread = 0; + getThread(this)->tid = 0; + getThread(this)->state = _PxThreadNotStarted; + getThread(this)->quitNow = 0; + getThread(this)->threadStarted = 0; + getThread(this)->fn = NULL; + getThread(this)->arg = NULL; + getThread(this)->affinityMask = 0; + getThread(this)->name = "set my name before starting me"; +} + +ThreadImpl::ThreadImpl(ThreadImpl::ExecuteFn fn, void* arg, const char* name) +{ + getThread(this)->thread = 0; + getThread(this)->tid = 0; + getThread(this)->state = _PxThreadNotStarted; + getThread(this)->quitNow = 0; + getThread(this)->threadStarted = 0; + getThread(this)->fn = fn; + getThread(this)->arg = arg; + getThread(this)->affinityMask = 0; + getThread(this)->name = name; + + start(0, NULL); +} + +ThreadImpl::~ThreadImpl() +{ + if(getThread(this)->state == _PxThreadStarted) + kill(); +} + +void ThreadImpl::start(uint32_t stackSize, Runnable* runnable) +{ + if(getThread(this)->state != _PxThreadNotStarted) + return; + + if(stackSize == 0) + stackSize = getDefaultStackSize(); + +#if defined(PTHREAD_STACK_MIN) && !defined(ANDROID) + if(stackSize < PTHREAD_STACK_MIN) + { + shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "ThreadImpl::start(): stack size was set below PTHREAD_STACK_MIN"); + stackSize = PTHREAD_STACK_MIN; + } +#endif + + if(runnable && !getThread(this)->arg && !getThread(this)->fn) + getThread(this)->arg = runnable; + + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + PX_ASSERT(!status); + PX_UNUSED(status); + + status = pthread_attr_setstacksize(&attr, stackSize); + PX_ASSERT(!status); +#if PX_PS4 + status = pthreadCreatePS4(&getThread(this)->thread, &attr, PxThreadStart, this, getThread(this)->name); +#else + status = pthread_create(&getThread(this)->thread, &attr, PxThreadStart, this); +#endif + PX_ASSERT(!status); + + // wait for thread to startup and write out TID + // otherwise TID dependent calls like setAffinity will fail. + while(atomicCompareExchange(&(getThread(this)->threadStarted), 1, 1) == 0) + yield(); + + // here we are sure that getThread(this)->state >= _PxThreadStarted + + status = pthread_attr_destroy(&attr); + PX_ASSERT(!status); + + // apply stored affinity mask + if(getThread(this)->affinityMask) + setAffinityMask(getThread(this)->affinityMask); + +#if !PX_PS4 + if (getThread(this)->name) + setName(getThread(this)->name); +#endif +} + +void ThreadImpl::signalQuit() +{ + atomicIncrement(&(getThread(this)->quitNow)); +} + +bool ThreadImpl::waitForQuit() +{ + if(getThread(this)->state == _PxThreadNotStarted) + return false; + + // works also with a stopped/exited thread if the handle is still valid + pthread_join(getThread(this)->thread, NULL); + return true; +} + +bool ThreadImpl::quitIsSignalled() +{ + return atomicCompareExchange(&(getThread(this)->quitNow), 0, 0) != 0; +} + +#if defined(PX_GCC_FAMILY) +__attribute__((noreturn)) +#endif + void ThreadImpl::quit() +{ + getThread(this)->state = _PxThreadStopped; + pthread_exit(0); +} + +void ThreadImpl::kill() +{ +#ifndef ANDROID + if(getThread(this)->state == _PxThreadStarted) + pthread_cancel(getThread(this)->thread); + getThread(this)->state = _PxThreadStopped; +#else + shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "ThreadImpl::kill() called, but is not implemented"); +#endif +} + +void ThreadImpl::sleep(uint32_t ms) +{ + timespec sleepTime; + uint32_t remainder = ms % 1000; + sleepTime.tv_sec = ms - remainder; + sleepTime.tv_nsec = remainder * 1000000L; + + while(nanosleep(&sleepTime, &sleepTime) == -1) + continue; +} + +void ThreadImpl::yield() +{ + sched_yield(); +} + +uint32_t ThreadImpl::setAffinityMask(uint32_t mask) +{ + // Same as windows impl if mask is zero + if(!mask) + return 0; + + getThread(this)->affinityMask = mask; + + uint64_t prevMask = 0; + + if(getThread(this)->state == _PxThreadStarted) + { +#if PX_PS4 + prevMask = setAffinityMaskPS4(getThread(this)->thread, mask); +#elif PX_EMSCRIPTEN + // not supported +#elif !PX_APPLE_FAMILY // Apple doesn't support syscall with getaffinity and setaffinity + int32_t errGet = syscall(__NR_sched_getaffinity, getThread(this)->tid, sizeof(prevMask), &prevMask); + if(errGet < 0) + return 0; + + int32_t errSet = syscall(__NR_sched_setaffinity, getThread(this)->tid, sizeof(mask), &mask); + if(errSet != 0) + return 0; +#endif + } + + return uint32_t(prevMask); +} + +void ThreadImpl::setName(const char* name) +{ + getThread(this)->name = name; + + if (getThread(this)->state == _PxThreadStarted) + { +#if(defined(ANDROID) && (__ANDROID_API__ > 8)) + pthread_setname_np(getThread(this)->thread, name); +#elif PX_PS4 + setNamePS4(getThread(this)->thread, name); +#else + // not implemented because most unix APIs expect setName() + // to be called from the thread's context. Example see next comment: + + // this works only with the current thread and can rename + // the main process if used in the wrong context: + // prctl(PR_SET_NAME, reinterpret_cast(name) ,0,0,0); + PX_UNUSED(name); +#endif + } +} + +#if !PX_APPLE_FAMILY +static ThreadPriority::Enum convertPriorityFromLinux(uint32_t inPrio, int policy) +{ + PX_COMPILE_TIME_ASSERT(ThreadPriority::eLOW > ThreadPriority::eHIGH); + PX_COMPILE_TIME_ASSERT(ThreadPriority::eHIGH == 0); + + int maxL = sched_get_priority_max(policy); + int minL = sched_get_priority_min(policy); + int rangeL = maxL - minL; + int rangeNv = ThreadPriority::eLOW - ThreadPriority::eHIGH; + + // case for default scheduler policy + if(rangeL == 0) + return ThreadPriority::eNORMAL; + + float floatPrio = (float(maxL - inPrio) * float(rangeNv)) / float(rangeL); + + return ThreadPriority::Enum(int(roundf(floatPrio))); +} + +static int convertPriorityToLinux(ThreadPriority::Enum inPrio, int policy) +{ + int maxL = sched_get_priority_max(policy); + int minL = sched_get_priority_min(policy); + int rangeL = maxL - minL; + int rangeNv = ThreadPriority::eLOW - ThreadPriority::eHIGH; + + // case for default scheduler policy + if(rangeL == 0) + return 0; + + float floatPrio = (float(ThreadPriority::eLOW - inPrio) * float(rangeL)) / float(rangeNv); + + return minL + int(roundf(floatPrio)); +} +#endif + +void ThreadImpl::setPriority(ThreadPriority::Enum val) +{ + PX_UNUSED(val); +#if !PX_APPLE_FAMILY + int policy; + sched_param s_param; + pthread_getschedparam(getThread(this)->thread, &policy, &s_param); + s_param.sched_priority = convertPriorityToLinux(val, policy); + pthread_setschedparam(getThread(this)->thread, policy, &s_param); +#endif +} + +ThreadPriority::Enum ThreadImpl::getPriority(Id pthread) +{ + PX_UNUSED(pthread); +#if !PX_APPLE_FAMILY + int policy; + sched_param s_param; + int ret = pthread_getschedparam(pthread_t(pthread), &policy, &s_param); + if(ret == 0) + return convertPriorityFromLinux(s_param.sched_priority, policy); + else + return ThreadPriority::eNORMAL; +#else + return ThreadPriority::eNORMAL; +#endif +} + +uint32_t ThreadImpl::getNbPhysicalCores() +{ +#if PX_APPLE_FAMILY + int count; + size_t size = sizeof(count); + return sysctlbyname("hw.physicalcpu", &count, &size, NULL, 0) ? 0 : count; +#elif defined(ANDROID) + return android_getCpuCount(); +#else + // Linux exposes CPU topology using /sys/devices/system/cpu + // https://www.kernel.org/doc/Documentation/cputopology.txt + if(FILE* f = fopen("/sys/devices/system/cpu/possible", "r")) + { + int minIndex, maxIndex; + int n = fscanf(f, "%d-%d", &minIndex, &maxIndex); + fclose(f); + + if(n == 2) + return (maxIndex - minIndex) + 1; + else if(n == 1) + return minIndex + 1; + } + +#if PX_PS4 + // Reducing to 6 to take into account that the OS appears to use 2 cores at peak currently. + return 6; +#else + // For non-Linux kernels this fallback is possibly the best we can do + // but will report logical (hyper-threaded) counts + int n = sysconf(_SC_NPROCESSORS_CONF); + if(n < 0) + return 0; + else + return n; +#endif +#endif +} + +uint32_t TlsAlloc() +{ + pthread_key_t key; + int status = pthread_key_create(&key, NULL); + PX_ASSERT(!status); + PX_UNUSED(status); + return uint32_t(key); +} + +void TlsFree(uint32_t index) +{ + int status = pthread_key_delete(pthread_key_t(index)); + PX_ASSERT(!status); + PX_UNUSED(status); +} + +void* TlsGet(uint32_t index) +{ + return reinterpret_cast(pthread_getspecific(pthread_key_t(index))); +} + +size_t TlsGetValue(uint32_t index) +{ + return reinterpret_cast(pthread_getspecific(pthread_key_t(index))); +} + +uint32_t TlsSet(uint32_t index, void* value) +{ + int status = pthread_setspecific(pthread_key_t(index), value); + PX_ASSERT(!status); + return !status; +} + +uint32_t TlsSetValue(uint32_t index, size_t value) +{ + int status = pthread_setspecific(pthread_key_t(index), reinterpret_cast(value)); + PX_ASSERT(!status); + return !status; +} + +// DM: On Linux x86-32, without implementation-specific restrictions +// the default stack size for a new thread should be 2 megabytes (kernel.org). +// NOTE: take care of this value on other architectures! +uint32_t ThreadImpl::getDefaultStackSize() +{ + return 1 << 21; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp new file mode 100644 index 000000000..e1fc32c1d --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "Ps.h" +#include "PsTime.h" + +#include +#include + +#if PX_APPLE_FAMILY +#include +#endif + +// Use real-time high-precision timer. +#if !PX_APPLE_FAMILY +#define CLOCKID CLOCK_REALTIME +#endif + +namespace physx +{ +namespace shdfnd +{ + +static const CounterFrequencyToTensOfNanos gCounterFreq = Time::getCounterFrequency(); + +const CounterFrequencyToTensOfNanos& Time::getBootCounterFrequency() +{ + return gCounterFreq; +} + +static Time::Second getTimeSeconds() +{ + static struct timeval _tv; + gettimeofday(&_tv, NULL); + return double(_tv.tv_sec) + double(_tv.tv_usec) * 0.000001; +} + +Time::Time() +{ + mLastTime = getTimeSeconds(); +} + +Time::Second Time::getElapsedSeconds() +{ + Time::Second curTime = getTimeSeconds(); + Time::Second diff = curTime - mLastTime; + mLastTime = curTime; + return diff; +} + +Time::Second Time::peekElapsedSeconds() +{ + Time::Second curTime = getTimeSeconds(); + Time::Second diff = curTime - mLastTime; + return diff; +} + +Time::Second Time::getLastTime() const +{ + return mLastTime; +} + +#if PX_APPLE_FAMILY +CounterFrequencyToTensOfNanos Time::getCounterFrequency() +{ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + // mach_absolute_time * (info.numer/info.denom) is in units of nano seconds + return CounterFrequencyToTensOfNanos(info.numer, info.denom * 10); +} + +uint64_t Time::getCurrentCounterValue() +{ + return mach_absolute_time(); +} + +#else + +CounterFrequencyToTensOfNanos Time::getCounterFrequency() +{ + return CounterFrequencyToTensOfNanos(1, 10); +} + +uint64_t Time::getCurrentCounterValue() +{ + struct timespec mCurrTimeInt; + clock_gettime(CLOCKID, &mCurrTimeInt); + // Convert to nanos as this doesn't cause a large divide here + return (static_cast(mCurrTimeInt.tv_sec) * 1000000000) + (static_cast(mCurrTimeInt.tv_nsec)); +} +#endif + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp new file mode 100644 index 000000000..d8ebca2da --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp @@ -0,0 +1,96 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "windows/PsWindowsInclude.h" +#include "PsAtomic.h" + +namespace physx +{ +namespace shdfnd +{ + +int32_t atomicExchange(volatile int32_t* val, int32_t val2) +{ + return (int32_t)InterlockedExchange((volatile LONG*)val, (LONG)val2); +} + +int32_t atomicCompareExchange(volatile int32_t* dest, int32_t exch, int32_t comp) +{ + return (int32_t)InterlockedCompareExchange((volatile LONG*)dest, exch, comp); +} + +void* atomicCompareExchangePointer(volatile void** dest, void* exch, void* comp) +{ + return InterlockedCompareExchangePointer((volatile PVOID*)dest, exch, comp); +} + +int32_t atomicIncrement(volatile int32_t* val) +{ + return (int32_t)InterlockedIncrement((volatile LONG*)val); +} + +int32_t atomicDecrement(volatile int32_t* val) +{ + return (int32_t)InterlockedDecrement((volatile LONG*)val); +} + +int32_t atomicAdd(volatile int32_t* val, int32_t delta) +{ + LONG newValue, oldValue; + do + { + oldValue = *val; + newValue = oldValue + delta; + } while(InterlockedCompareExchange((volatile LONG*)val, newValue, oldValue) != oldValue); + + return newValue; +} + +int32_t atomicMax(volatile int32_t* val, int32_t val2) +{ + // Could do this more efficiently in asm... + + LONG newValue, oldValue; + + do + { + oldValue = *val; + + if(val2 > oldValue) + newValue = val2; + else + newValue = oldValue; + + } while(InterlockedCompareExchange((volatile LONG*)val, newValue, oldValue) != oldValue); + + return newValue; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp new file mode 100644 index 000000000..75c308333 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsCpu.h" +#pragma warning(push) +//'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#pragma warning(disable : 4668) +#if PX_VC == 10 +#pragma warning(disable : 4987) // nonstandard extension used: 'throw (...)' +#endif +#include +#pragma warning(pop) + +namespace physx +{ +namespace shdfnd +{ + +#if PX_ARM +#define cpuid(reg) reg[0] = reg[1] = reg[2] = reg[3] = 0; + +uint8_t Cpu::getCpuId() +{ + uint32_t cpuInfo[4]; + cpuid(cpuInfo); + return static_cast(cpuInfo[1] >> 24); // APIC Physical ID +} +#else +uint8_t Cpu::getCpuId() +{ + int CPUInfo[4]; + int InfoType = 1; + __cpuid(CPUInfo, InfoType); + return static_cast(CPUInfo[1] >> 24); // APIC Physical ID +} +#endif +} +} diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp new file mode 100644 index 000000000..a8575a062 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp @@ -0,0 +1,88 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#include "PsFPU.h" +#include "float.h" +#include "PsIntrinsics.h" + +#if PX_X64 || PX_ARM +#define _MCW_ALL _MCW_DN | _MCW_EM | _MCW_RC +#else +#define _MCW_ALL _MCW_DN | _MCW_EM | _MCW_IC | _MCW_RC | _MCW_PC +#endif + +physx::shdfnd::FPUGuard::FPUGuard() +{ +// default plus FTZ and DAZ +#if PX_X64 || PX_ARM + // query current control word state + _controlfp_s(mControlWords, 0, 0); + + // set both x87 and sse units to default + DAZ + unsigned int cw; + _controlfp_s(&cw, _CW_DEFAULT | _DN_FLUSH, _MCW_ALL); +#else + // query current control word state + __control87_2(0, 0, mControlWords, mControlWords + 1); + + // set both x87 and sse units to default + DAZ + unsigned int x87, sse; + __control87_2(_CW_DEFAULT | _DN_FLUSH, _MCW_ALL, &x87, &sse); +#endif +} + +physx::shdfnd::FPUGuard::~FPUGuard() +{ + _clearfp(); + +#if PX_X64 || PX_ARM + // reset FP state + unsigned int cw; + _controlfp_s(&cw, *mControlWords, _MCW_ALL); +#else + + // reset FP state + unsigned int x87, sse; + __control87_2(mControlWords[0], _MCW_ALL, &x87, 0); + __control87_2(mControlWords[1], _MCW_ALL, 0, &sse); +#endif +} + +void physx::shdfnd::enableFPExceptions() +{ + // clear any pending exceptions + _clearfp(); + + // enable all fp exceptions except inexact and underflow (common, benign) + _controlfp_s(NULL, uint32_t(~_MCW_EM) | _EM_INEXACT | _EM_UNDERFLOW, _MCW_EM); +} + +void physx::shdfnd::disableFPExceptions() +{ + _controlfp_s(NULL, _MCW_EM, _MCW_EM); +} diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp new file mode 100644 index 000000000..45cbeafea --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp @@ -0,0 +1,162 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "windows/PsWindowsInclude.h" +#include "PsFoundation.h" +#include "PsUserAllocated.h" +#include "PsMutex.h" +#include "PsThread.h" +#include "foundation/PxErrorCallback.h" + +namespace physx +{ +namespace shdfnd +{ + +namespace +{ +struct MutexWinImpl +{ + CRITICAL_SECTION mLock; + Thread::Id mOwner; +}; + +MutexWinImpl* getMutex(MutexImpl* impl) +{ + return reinterpret_cast(impl); +} +} + +MutexImpl::MutexImpl() +{ + InitializeCriticalSection(&getMutex(this)->mLock); + getMutex(this)->mOwner = 0; +} + +MutexImpl::~MutexImpl() +{ + DeleteCriticalSection(&getMutex(this)->mLock); +} + +void MutexImpl::lock() +{ + EnterCriticalSection(&getMutex(this)->mLock); + +#if PX_DEBUG + getMutex(this)->mOwner = Thread::getId(); +#endif +} + +bool MutexImpl::trylock() +{ + bool success = TryEnterCriticalSection(&getMutex(this)->mLock) != 0; +#if PX_DEBUG + if(success) + getMutex(this)->mOwner = Thread::getId(); +#endif + return success; +} + +void MutexImpl::unlock() +{ +#if PX_DEBUG + // ensure we are already holding the lock + if(getMutex(this)->mOwner != Thread::getId()) + { + shdfnd::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Mutex must be unlocked only by thread that has already acquired lock"); + return; + } + +#endif + + LeaveCriticalSection(&getMutex(this)->mLock); +} + +uint32_t MutexImpl::getSize() +{ + return sizeof(MutexWinImpl); +} + +class ReadWriteLockImpl +{ + PX_NOCOPY(ReadWriteLockImpl) + public: + ReadWriteLockImpl() + { + } + Mutex mutex; + volatile LONG readerCount; // handle recursive writer locking +}; + +ReadWriteLock::ReadWriteLock() +{ + mImpl = reinterpret_cast(PX_ALLOC(sizeof(ReadWriteLockImpl), "ReadWriteLockImpl")); + PX_PLACEMENT_NEW(mImpl, ReadWriteLockImpl); + + mImpl->readerCount = 0; +} + +ReadWriteLock::~ReadWriteLock() +{ + mImpl->~ReadWriteLockImpl(); + PX_FREE(mImpl); +} + +void ReadWriteLock::lockReader(bool takeLock) +{ + if(takeLock) + mImpl->mutex.lock(); + + InterlockedIncrement(&mImpl->readerCount); + + if(takeLock) + mImpl->mutex.unlock(); +} + +void ReadWriteLock::lockWriter() +{ + mImpl->mutex.lock(); + + // spin lock until no readers + while(mImpl->readerCount); +} + +void ReadWriteLock::unlockReader() +{ + InterlockedDecrement(&mImpl->readerCount); +} + +void ReadWriteLock::unlockWriter() +{ + mImpl->mutex.unlock(); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp new file mode 100644 index 000000000..50a83eb46 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsString.h" +#include +#pragma warning(push) +#pragma warning(disable : 4668) //'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#include +#pragma warning(pop) + +#include +#include +#include + +namespace physx +{ +namespace shdfnd +{ + +void printString(const char* str) +{ + puts(str); // do not use printf here, since str can contain multiple % signs that will not be printed + OutputDebugStringA(str); + OutputDebugStringA("\n"); +} +} + +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp new file mode 100644 index 000000000..76647cc9c --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "windows/PsWindowsInclude.h" +#include "PsAllocator.h" +#include "PsSList.h" + +namespace physx +{ +namespace shdfnd +{ +namespace +{ +template +SLIST_HEADER* getDetail(T* impl) +{ + return reinterpret_cast(impl); +} +} + +SListImpl::SListImpl() +{ + InitializeSListHead(getDetail(this)); +} + +SListImpl::~SListImpl() +{ +} + +void SListImpl::push(SListEntry* entry) +{ + InterlockedPushEntrySList(getDetail(this), reinterpret_cast(entry)); +} + +SListEntry* SListImpl::pop() +{ + return reinterpret_cast(InterlockedPopEntrySList(getDetail(this))); +} + +SListEntry* SListImpl::flush() +{ + return reinterpret_cast(InterlockedFlushSList(getDetail(this))); +} + +uint32_t SListImpl::getSize() +{ + return sizeof(SLIST_HEADER); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp new file mode 100644 index 000000000..b50afa8e0 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp @@ -0,0 +1,450 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIntrinsics.h" + +#include "windows/PsWindowsInclude.h" +#include "PsSocket.h" +#include "PsThread.h" +#include "PsArray.h" + +#include +#pragma comment(lib, "Ws2_32") + +namespace physx +{ +namespace shdfnd +{ + +const uint32_t Socket::DEFAULT_BUFFER_SIZE = 32768; + +class SocketImpl +{ + public: + SocketImpl(bool isBlocking); + virtual ~SocketImpl(); + + bool connect(const char* host, uint16_t port, uint32_t timeout); + bool listen(uint16_t port); + bool accept(bool block); + void disconnect(); + + void setBlocking(bool blocking); + + virtual uint32_t write(const uint8_t* data, uint32_t length); + virtual bool flush(); + uint32_t read(uint8_t* data, uint32_t length); + + PX_FORCE_INLINE bool isBlocking() const + { + return mIsBlocking; + } + PX_FORCE_INLINE bool isConnected() const + { + return mIsConnected; + } + PX_FORCE_INLINE const char* getHost() const + { + return mHost; + } + PX_FORCE_INLINE uint16_t getPort() const + { + return mPort; + } + + protected: + bool nonBlockingTimeout() const; + void setBlockingInternal(SOCKET socket, bool blocking); + + mutable SOCKET mSocket; + SOCKET mListenSocket; + const char* mHost; + uint16_t mPort; + mutable bool mIsConnected; + bool mIsBlocking; + bool mListenMode; + bool mSocketLayerIntialized; +}; + +SocketImpl::SocketImpl(bool isBlocking) +: mSocket(INVALID_SOCKET) +, mListenSocket(INVALID_SOCKET) +, mPort(0) +, mHost(NULL) +, mIsConnected(false) +, mIsBlocking(isBlocking) +, mListenMode(false) +, mSocketLayerIntialized(false) +{ + WORD vreq; + WSADATA wsaData; + vreq = MAKEWORD(2, 2); + mSocketLayerIntialized = (WSAStartup(vreq, &wsaData) == 0); +} + +SocketImpl::~SocketImpl() +{ + if(mSocketLayerIntialized) + WSACleanup(); +} + +void SocketImpl::setBlockingInternal(SOCKET socket, bool blocking) +{ + uint32_t mode = uint32_t(blocking ? 0 : 1); + ioctlsocket(socket, FIONBIO, (u_long*)&mode); +} + +#ifdef PX_VC11 +#pragma warning(push) +#pragma warning(disable : 4548) // for FD_SET on vc11 only +#endif +bool SocketImpl::connect(const char* host, uint16_t port, uint32_t timeout) +{ + if(!mSocketLayerIntialized) + return false; + + sockaddr_in socketAddress; + hostent* hp; + + intrinsics::memSet(&socketAddress, 0, sizeof(sockaddr_in)); + socketAddress.sin_family = AF_INET; + socketAddress.sin_port = htons(port); + + // get host + hp = gethostbyname(host); + if(!hp) + { + in_addr a; + a.s_addr = inet_addr(host); + hp = gethostbyaddr((const char*)&a, sizeof(in_addr), AF_INET); + if(!hp) + return false; + } + intrinsics::memCopy(&socketAddress.sin_addr, hp->h_addr_list[0], (uint32_t)hp->h_length); + + // connect + mSocket = socket(PF_INET, SOCK_STREAM, 0); + if(mSocket == INVALID_SOCKET) + return false; + + setBlockingInternal(mSocket, false); + + ::connect(mSocket, (sockaddr*)&socketAddress, sizeof(socketAddress)); + // Setup select function call to monitor the connect call. + fd_set writefs; + fd_set exceptfs; + FD_ZERO(&writefs); + FD_ZERO(&exceptfs); +#pragma warning(push) +#pragma warning(disable : 4127 4548) + FD_SET(mSocket, &writefs); + FD_SET(mSocket, &exceptfs); +#pragma warning(pop) + timeval timeout_; + timeout_.tv_sec = long(timeout / 1000); + timeout_.tv_usec = long(((timeout % 1000) * 1000)); + int selret = ::select(1, NULL, &writefs, &exceptfs, &timeout_); + int excepted = FD_ISSET(mSocket, &exceptfs); + int canWrite = FD_ISSET(mSocket, &writefs); + if(selret != 1 || excepted || !canWrite) + { + disconnect(); + return false; + } + + setBlockingInternal(mSocket, mIsBlocking); + + mIsConnected = true; + mPort = port; + mHost = host; + return true; +} +#ifdef PX_VC11 +#pragma warning(pop) +#endif + +bool SocketImpl::listen(uint16_t port) +{ + if(!mSocketLayerIntialized) + return false; + + mListenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if(mListenSocket == INVALID_SOCKET) + return false; + + mListenMode = true; + + sockaddr_in addr = { 0 }; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + return bind(mListenSocket, (sockaddr*)&addr, sizeof(addr)) == 0 && ::listen(mListenSocket, SOMAXCONN) == 0; +} + +bool SocketImpl::accept(bool block) +{ + if(mIsConnected || !mListenMode) + return false; + + // set the listen socket to be non-blocking. + setBlockingInternal(mListenSocket, block); + SOCKET clientSocket = ::accept(mListenSocket, 0, 0); + if(clientSocket == INVALID_SOCKET) + return false; + + mSocket = clientSocket; + mIsConnected = true; + setBlockingInternal(mSocket, mIsBlocking); // force the mode to whatever the user set + + return mIsConnected; +} + +void SocketImpl::disconnect() +{ + if(mListenSocket != INVALID_SOCKET) + { + closesocket(mListenSocket); + mListenSocket = INVALID_SOCKET; + } + if(mSocket != INVALID_SOCKET) + { +#if PX_UWP + shutdown(mSocket, SD_SEND); +#else + WSASendDisconnect(mSocket, NULL); +#endif + closesocket(mSocket); + mSocket = INVALID_SOCKET; + } + mIsConnected = false; + mListenMode = false; + mPort = 0; + mHost = NULL; +} + +bool SocketImpl::nonBlockingTimeout() const +{ + return !mIsBlocking && WSAGetLastError() == WSAEWOULDBLOCK; +} + +// should be cross-platform from here down + +void SocketImpl::setBlocking(bool blocking) +{ + if(blocking != mIsBlocking) + { + mIsBlocking = blocking; + if(isConnected()) + setBlockingInternal(mSocket, blocking); + } +} + +bool SocketImpl::flush() +{ + return true; +} + +uint32_t SocketImpl::write(const uint8_t* data, uint32_t length) +{ + if(length == 0) + return 0; + + int sent = send(mSocket, (const char*)data, (int32_t)length, 0); + + if(sent <= 0 && !nonBlockingTimeout()) + disconnect(); + + return uint32_t(sent > 0 ? sent : 0); +} + +uint32_t SocketImpl::read(uint8_t* data, uint32_t length) +{ + if(length == 0) + return 0; + + int32_t received = recv(mSocket, (char*)data, (int32_t)length, 0); + + if(received <= 0 && !nonBlockingTimeout()) + disconnect(); + + return uint32_t(received > 0 ? received : 0); +} + +class BufferedSocketImpl : public SocketImpl +{ + public: + BufferedSocketImpl(bool isBlocking) : SocketImpl(isBlocking), mBufferPos(0) + { + } + virtual ~BufferedSocketImpl() + { + } + bool flush(); + uint32_t write(const uint8_t* data, uint32_t length); + + private: + uint32_t mBufferPos; + uint8_t mBuffer[Socket::DEFAULT_BUFFER_SIZE]; +}; + +bool BufferedSocketImpl::flush() +{ + uint32_t totalBytesWritten = 0; + + while(totalBytesWritten < mBufferPos && mIsConnected) + totalBytesWritten += (int32_t)SocketImpl::write(mBuffer + totalBytesWritten, mBufferPos - totalBytesWritten); + + bool ret = (totalBytesWritten == mBufferPos); + mBufferPos = 0; + return ret; +} + +uint32_t BufferedSocketImpl::write(const uint8_t* data, uint32_t length) +{ + uint32_t bytesWritten = 0; + while(mBufferPos + length >= Socket::DEFAULT_BUFFER_SIZE) + { + uint32_t currentChunk = Socket::DEFAULT_BUFFER_SIZE - mBufferPos; + intrinsics::memCopy(mBuffer + mBufferPos, data + bytesWritten, currentChunk); + bytesWritten += (uint32_t)currentChunk; // for the user, this is consumed even if we fail to shove it down a + // non-blocking socket + + uint32_t sent = SocketImpl::write(mBuffer, Socket::DEFAULT_BUFFER_SIZE); + mBufferPos = Socket::DEFAULT_BUFFER_SIZE - sent; + + if(sent < Socket::DEFAULT_BUFFER_SIZE) // non-blocking or error + { + if(sent) // we can reasonably hope this is rare + intrinsics::memMove(mBuffer, mBuffer + sent, mBufferPos); + + return bytesWritten; + } + length -= currentChunk; + } + + if(length > 0) + { + intrinsics::memCopy(mBuffer + mBufferPos, data + bytesWritten, length); + bytesWritten += length; + mBufferPos += length; + } + + return bytesWritten; +} + +Socket::Socket(bool inIsBuffering, bool isBlocking) +{ + if(inIsBuffering) + { + void* mem = PX_ALLOC(sizeof(BufferedSocketImpl), "BufferedSocketImpl"); + mImpl = PX_PLACEMENT_NEW(mem, BufferedSocketImpl)(isBlocking); + } + else + { + void* mem = PX_ALLOC(sizeof(SocketImpl), "SocketImpl"); + mImpl = PX_PLACEMENT_NEW(mem, SocketImpl)(isBlocking); + } +} + +Socket::~Socket() +{ + mImpl->flush(); + mImpl->disconnect(); + mImpl->~SocketImpl(); + PX_FREE(mImpl); +} + +bool Socket::connect(const char* host, uint16_t port, uint32_t timeout) +{ + return mImpl->connect(host, port, timeout); +} + +bool Socket::listen(uint16_t port) +{ + return mImpl->listen(port); +} + +bool Socket::accept(bool block) +{ + return mImpl->accept(block); +} + +void Socket::disconnect() +{ + mImpl->disconnect(); +} + +bool Socket::isConnected() const +{ + return mImpl->isConnected(); +} + +const char* Socket::getHost() const +{ + return mImpl->getHost(); +} + +uint16_t Socket::getPort() const +{ + return mImpl->getPort(); +} + +bool Socket::flush() +{ + if(!mImpl->isConnected()) + return false; + return mImpl->flush(); +} + +uint32_t Socket::write(const uint8_t* data, uint32_t length) +{ + if(!mImpl->isConnected()) + return 0; + return mImpl->write(data, length); +} + +uint32_t Socket::read(uint8_t* data, uint32_t length) +{ + if(!mImpl->isConnected()) + return 0; + return mImpl->read(data, length); +} + +void Socket::setBlocking(bool blocking) +{ + mImpl->setBlocking(blocking); +} + +bool Socket::isBlocking() const +{ + return mImpl->isBlocking(); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp new file mode 100644 index 000000000..b3675dc7a --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp @@ -0,0 +1,81 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "windows/PsWindowsInclude.h" +#include "PsUserAllocated.h" +#include "PsSync.h" + +namespace physx +{ +namespace shdfnd +{ + +namespace +{ +HANDLE& getSync(SyncImpl* impl) +{ + return *reinterpret_cast(impl); +} +} + +uint32_t SyncImpl::getSize() +{ + return sizeof(HANDLE); +} + +SyncImpl::SyncImpl() +{ + getSync(this) = CreateEvent(0, true, false, 0); +} + +SyncImpl::~SyncImpl() +{ + CloseHandle(getSync(this)); +} + +void SyncImpl::reset() +{ + ResetEvent(getSync(this)); +} + +void SyncImpl::set() +{ + SetEvent(getSync(this)); +} + +bool SyncImpl::wait(uint32_t milliseconds) +{ + if(milliseconds == -1) + milliseconds = INFINITE; + + return WaitForSingleObject(getSync(this), milliseconds) == WAIT_OBJECT_0 ? true : false; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp new file mode 100644 index 000000000..243c501dc --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp @@ -0,0 +1,425 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "windows/PsWindowsInclude.h" +#include "PsFoundation.h" +#include "PsThread.h" +#include "foundation/PxErrorCallback.h" +#include "foundation/PxAssert.h" + +// an exception for setting the thread name in Microsoft debuggers +#define NS_MS_VC_EXCEPTION 0x406D1388 + +namespace physx +{ +namespace shdfnd +{ + +namespace +{ + +#if PX_VC +#pragma warning(disable : 4061) // enumerator 'identifier' in switch of enum 'enumeration' is not handled +#pragma warning(disable : 4191) //'operator/operation' : unsafe conversion from 'type of expression' to 'type required' +#endif + +// struct for naming a thread in the debugger +#pragma pack(push, 8) + +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; + +#pragma pack(pop) + +class _ThreadImpl +{ + public: + enum State + { + NotStarted, + Started, + Stopped + }; + + HANDLE thread; + LONG quitNow; // Should be 32bit aligned on SMP systems. + State state; + DWORD threadID; + + ThreadImpl::ExecuteFn fn; + void* arg; + + uint32_t affinityMask; + const char* name; +}; + +_ThreadImpl* getThread(ThreadImpl* impl) +{ + return reinterpret_cast<_ThreadImpl*>(impl); +} + +DWORD WINAPI PxThreadStart(LPVOID arg) +{ + _ThreadImpl* impl = getThread((ThreadImpl*)arg); + + // run either the passed in function or execute from the derived class (Runnable). + if(impl->fn) + (*impl->fn)(impl->arg); + else if(impl->arg) + ((Runnable*)impl->arg)->execute(); + return 0; +} + +// cache physical thread count +uint32_t gPhysicalCoreCount = 0; +} + +uint32_t ThreadImpl::getSize() +{ + return sizeof(_ThreadImpl); +} + +ThreadImpl::Id ThreadImpl::getId() +{ + return static_cast(GetCurrentThreadId()); +} + +// fwd GetLogicalProcessorInformation() +typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); + +uint32_t ThreadImpl::getNbPhysicalCores() +{ + if(!gPhysicalCoreCount) + { + // modified example code from: http://msdn.microsoft.com/en-us/library/ms683194 + LPFN_GLPI glpi; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL; + DWORD returnLength = 0; + DWORD processorCoreCount = 0; + DWORD byteOffset = 0; + + glpi = (LPFN_GLPI)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "GetLogicalProcessorInformation"); + + if(NULL == glpi) + { + // GetLogicalProcessorInformation not supported on OS < XP Service Pack 3 + return 0; + } + + DWORD rc = (DWORD)glpi(NULL, &returnLength); + PX_ASSERT(rc == FALSE); + PX_UNUSED(rc); + + // first query reports required buffer space + if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)PxAlloca(returnLength); + } + else + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Error querying buffer size for number of physical processors"); + return 0; + } + + // retrieve data + rc = (DWORD)glpi(buffer, &returnLength); + if(rc != TRUE) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Error querying number of physical processors"); + return 0; + } + + ptr = buffer; + + while(byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) + { + switch(ptr->Relationship) + { + case RelationProcessorCore: + processorCoreCount++; + break; + default: + break; + } + + byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + ptr++; + } + + gPhysicalCoreCount = processorCoreCount; + } + + return gPhysicalCoreCount; +} + +ThreadImpl::ThreadImpl() +{ + getThread(this)->thread = NULL; + getThread(this)->state = _ThreadImpl::NotStarted; + getThread(this)->quitNow = 0; + getThread(this)->fn = NULL; + getThread(this)->arg = NULL; + getThread(this)->affinityMask = 0; + getThread(this)->name = NULL; +} + +ThreadImpl::ThreadImpl(ExecuteFn fn, void* arg, const char* name) +{ + getThread(this)->thread = NULL; + getThread(this)->state = _ThreadImpl::NotStarted; + getThread(this)->quitNow = 0; + getThread(this)->fn = fn; + getThread(this)->arg = arg; + getThread(this)->affinityMask = 0; + getThread(this)->name = name; + + start(0, NULL); +} + +ThreadImpl::~ThreadImpl() +{ + if(getThread(this)->state == _ThreadImpl::Started) + kill(); + CloseHandle(getThread(this)->thread); +} + +void ThreadImpl::start(uint32_t stackSize, Runnable* runnable) +{ + if(getThread(this)->state != _ThreadImpl::NotStarted) + return; + getThread(this)->state = _ThreadImpl::Started; + + if(runnable && !getThread(this)->arg && !getThread(this)->fn) + getThread(this)->arg = runnable; + + getThread(this)->thread = + CreateThread(NULL, stackSize, PxThreadStart, (LPVOID) this, CREATE_SUSPENDED, &getThread(this)->threadID); + if(!getThread(this)->thread) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PsWindowsThread::start: Failed to create thread."); + getThread(this)->state = _ThreadImpl::NotStarted; + return; + } + + // set affinity, set name and resume + if(getThread(this)->affinityMask) + setAffinityMask(getThread(this)->affinityMask); + + if (getThread(this)->name) + setName(getThread(this)->name); + + DWORD rc = ResumeThread(getThread(this)->thread); + if(rc == DWORD(-1)) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PsWindowsThread::start: Failed to resume thread."); + getThread(this)->state = _ThreadImpl::NotStarted; + return; + } +} + +void ThreadImpl::signalQuit() +{ + InterlockedIncrement(&(getThread(this)->quitNow)); +} + +bool ThreadImpl::waitForQuit() +{ + if(getThread(this)->state == _ThreadImpl::NotStarted) + return false; + + WaitForSingleObject(getThread(this)->thread, INFINITE); + return true; +} + +bool ThreadImpl::quitIsSignalled() +{ + return InterlockedCompareExchange(&(getThread(this)->quitNow), 0, 0) != 0; +} + +void ThreadImpl::quit() +{ + getThread(this)->state = _ThreadImpl::Stopped; + ExitThread(0); +} + +void ThreadImpl::kill() +{ + if(getThread(this)->state == _ThreadImpl::Started) + TerminateThread(getThread(this)->thread, 0); + getThread(this)->state = _ThreadImpl::Stopped; +} + +void ThreadImpl::sleep(uint32_t ms) +{ + Sleep(ms); +} + +void ThreadImpl::yield() +{ + SwitchToThread(); +} + +uint32_t ThreadImpl::setAffinityMask(uint32_t mask) +{ + if(mask) + { + // store affinity + getThread(this)->affinityMask = mask; + + // if thread already started apply immediately + if(getThread(this)->state == _ThreadImpl::Started) + { + uint32_t err = uint32_t(SetThreadAffinityMask(getThread(this)->thread, mask)); + return err; + } + } + + return 0; +} + +void ThreadImpl::setName(const char* name) +{ + getThread(this)->name = name; + + if (getThread(this)->state == _ThreadImpl::Started) + { + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = getThread(this)->threadID; + info.dwFlags = 0; + + // C++ Exceptions are disabled for this project, but SEH is not (and cannot be) + // http://stackoverflow.com/questions/943087/what-exactly-will-happen-if-i-disable-c-exceptions-in-a-project + __try + { + RaiseException(NS_MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + // this runs if not attached to a debugger (thus not really naming the thread) + } + } +} + +void ThreadImpl::setPriority(ThreadPriority::Enum prio) +{ + BOOL rc = false; + switch(prio) + { + case ThreadPriority::eHIGH: + rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_HIGHEST); + break; + case ThreadPriority::eABOVE_NORMAL: + rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_ABOVE_NORMAL); + break; + case ThreadPriority::eNORMAL: + rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_NORMAL); + break; + case ThreadPriority::eBELOW_NORMAL: + rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_BELOW_NORMAL); + break; + case ThreadPriority::eLOW: + rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_LOWEST); + break; + default: + break; + } + if(!rc) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PsWindowsThread::setPriority: Failed to set thread priority."); + } +} + +ThreadPriority::Enum ThreadImpl::getPriority(Id threadId) +{ + ThreadPriority::Enum retval = ThreadPriority::eLOW; + int priority = GetThreadPriority((HANDLE)threadId); + PX_COMPILE_TIME_ASSERT(THREAD_PRIORITY_HIGHEST > THREAD_PRIORITY_ABOVE_NORMAL); + if(priority >= THREAD_PRIORITY_HIGHEST) + retval = ThreadPriority::eHIGH; + else if(priority >= THREAD_PRIORITY_ABOVE_NORMAL) + retval = ThreadPriority::eABOVE_NORMAL; + else if(priority >= THREAD_PRIORITY_NORMAL) + retval = ThreadPriority::eNORMAL; + else if(priority >= THREAD_PRIORITY_BELOW_NORMAL) + retval = ThreadPriority::eBELOW_NORMAL; + return retval; +} + +uint32_t TlsAlloc() +{ + DWORD rv = ::TlsAlloc(); + PX_ASSERT(rv != TLS_OUT_OF_INDEXES); + return (uint32_t)rv; +} + +void TlsFree(uint32_t index) +{ + ::TlsFree(index); +} + +void* TlsGet(uint32_t index) +{ + return ::TlsGetValue(index); +} + +size_t TlsGetValue(uint32_t index) +{ + return reinterpret_cast(::TlsGetValue(index)); +} + +uint32_t TlsSet(uint32_t index, void* value) +{ + return (uint32_t)::TlsSetValue(index, value); +} + +uint32_t TlsSetValue(uint32_t index, size_t value) +{ + return (uint32_t)::TlsSetValue(index, reinterpret_cast(value)); +} + +uint32_t ThreadImpl::getDefaultStackSize() +{ + return 1048576; +}; + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp new file mode 100644 index 000000000..71d5e41c6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp @@ -0,0 +1,101 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsTime.h" +#include "windows/PsWindowsInclude.h" + +namespace +{ +int64_t getTimeTicks() +{ + LARGE_INTEGER a; + QueryPerformanceCounter(&a); + return a.QuadPart; +} + +double getTickDuration() +{ + LARGE_INTEGER a; + QueryPerformanceFrequency(&a); + return 1.0f / double(a.QuadPart); +} + +double sTickDuration = getTickDuration(); +} // namespace + +namespace physx +{ +namespace shdfnd +{ + +static const CounterFrequencyToTensOfNanos gCounterFreq = Time::getCounterFrequency(); + +const CounterFrequencyToTensOfNanos& Time::getBootCounterFrequency() +{ + return gCounterFreq; +} + +CounterFrequencyToTensOfNanos Time::getCounterFrequency() +{ + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + return CounterFrequencyToTensOfNanos(Time::sNumTensOfNanoSecondsInASecond, (uint64_t)freq.QuadPart); +} + +uint64_t Time::getCurrentCounterValue() +{ + LARGE_INTEGER ticks; + QueryPerformanceCounter(&ticks); + return (uint64_t)ticks.QuadPart; +} + +Time::Time() : mTickCount(0) +{ + getElapsedSeconds(); +} + +Time::Second Time::getElapsedSeconds() +{ + int64_t lastTickCount = mTickCount; + mTickCount = getTimeTicks(); + return (mTickCount - lastTickCount) * sTickDuration; +} + +Time::Second Time::peekElapsedSeconds() +{ + return (getTimeTicks() - mTickCount) * sTickDuration; +} + +Time::Second Time::getLastTime() const +{ + return mTickCount * sTickDuration; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/geomutils/include/GuAxes.h b/src/PhysX/physx/source/geomutils/include/GuAxes.h new file mode 100644 index 000000000..4daa0a84d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuAxes.h @@ -0,0 +1,81 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_AXES_H +#define GU_AXES_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ +namespace Gu +{ + enum PointComponent + { + X_ = 0, + Y_ = 1, + Z_ = 2, + W_ = 3, + + PC_FORCE_DWORD = 0x7fffffff + }; + + enum AxisOrder + { + AXES_XYZ = (X_)|(Y_<<2)|(Z_<<4), + AXES_XZY = (X_)|(Z_<<2)|(Y_<<4), + AXES_YXZ = (Y_)|(X_<<2)|(Z_<<4), + AXES_YZX = (Y_)|(Z_<<2)|(X_<<4), + AXES_ZXY = (Z_)|(X_<<2)|(Y_<<4), + AXES_ZYX = (Z_)|(Y_<<2)|(X_<<4), + + AXES_FORCE_DWORD = 0x7fffffff + }; + + class Axes + { + public: + + PX_FORCE_INLINE Axes(AxisOrder order) + { + mAxis0 = PxU32((order ) & 3); + mAxis1 = PxU32((order>>2) & 3); + mAxis2 = PxU32((order>>4) & 3); + } + PX_FORCE_INLINE ~Axes() {} + + PxU32 mAxis0; + PxU32 mAxis1; + PxU32 mAxis2; + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuBox.h b/src/PhysX/physx/source/geomutils/include/GuBox.h new file mode 100644 index 000000000..bdd96e3ea --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuBox.h @@ -0,0 +1,246 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BOX_H +#define GU_BOX_H + +/** \addtogroup geomutils +@{ +*/ + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "CmScaling.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" + +namespace physx +{ +namespace Gu +{ + class Capsule; + + PX_PHYSX_COMMON_API void computeOBBPoints(PxVec3* PX_RESTRICT pts, const PxVec3& center, const PxVec3& extents, const PxVec3& base0, const PxVec3& base1, const PxVec3& base2); + + + /** + \brief Represents an oriented bounding box. + + As a center point, extents(radii) and a rotation. i.e. the center of the box is at the center point, + the box is rotated around this point with the rotation and it is 2*extents in width, height and depth. + */ + + /** + Box geometry + + The rot member describes the world space orientation of the box. + The center member gives the world space position of the box. + The extents give the local space coordinates of the box corner in the positive octant. + Dimensions of the box are: 2*extent. + Transformation to world space is: worldPoint = rot * localPoint + center + Transformation to local space is: localPoint = T(rot) * (worldPoint - center) + Where T(M) denotes the transpose of M. + */ +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + class PX_PHYSX_COMMON_API Box + { + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE Box() + { + } + + /** + \brief Constructor + + \param origin Center of the OBB + \param extent Extents/radii of the obb. + \param base rotation to apply to the obb. + */ + //! Construct from center, extent and rotation + PX_FORCE_INLINE Box(const PxVec3& origin, const PxVec3& extent, const PxMat33& base) : rot(base), center(origin), extents(extent) + {} + + //! Copy constructor + PX_FORCE_INLINE Box(const Box& other) : rot(other.rot), center(other.center), extents(other.extents) + {} + + /** + \brief Destructor + */ + PX_FORCE_INLINE ~Box() + { + } + + //! Assignment operator + PX_FORCE_INLINE const Box& operator=(const Box& other) + { + rot = other.rot; + center = other.center; + extents = other.extents; + return *this; + } + + /** + \brief Setups an empty box. + */ + PX_INLINE void setEmpty() + { + center = PxVec3(0); + extents = PxVec3(-PX_MAX_REAL, -PX_MAX_REAL, -PX_MAX_REAL); + rot = PxMat33(PxIdentity); + } + + /** + \brief Checks the box is valid. + + \return true if the box is valid + */ + PX_INLINE bool isValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0.0f + if(extents.x < 0.0f) return false; + if(extents.y < 0.0f) return false; + if(extents.z < 0.0f) return false; + return true; + } + +///////////// + PX_FORCE_INLINE void setAxes(const PxVec3& axis0, const PxVec3& axis1, const PxVec3& axis2) + { + rot.column0 = axis0; + rot.column1 = axis1; + rot.column2 = axis2; + } + + PX_FORCE_INLINE PxVec3 rotate(const PxVec3& src) const + { + return rot * src; + } + + PX_FORCE_INLINE PxVec3 rotateInv(const PxVec3& src) const + { + return rot.transformTranspose(src); + } + + PX_FORCE_INLINE PxVec3 transform(const PxVec3& src) const + { + return rot * src + center; + } + + PX_FORCE_INLINE PxTransform getTransform() const + { + return PxTransform(center, PxQuat(rot)); + } + + PX_INLINE PxVec3 computeAABBExtent() const + { + const PxReal a00 = PxAbs(rot[0][0]); + const PxReal a01 = PxAbs(rot[0][1]); + const PxReal a02 = PxAbs(rot[0][2]); + + const PxReal a10 = PxAbs(rot[1][0]); + const PxReal a11 = PxAbs(rot[1][1]); + const PxReal a12 = PxAbs(rot[1][2]); + + const PxReal a20 = PxAbs(rot[2][0]); + const PxReal a21 = PxAbs(rot[2][1]); + const PxReal a22 = PxAbs(rot[2][2]); + + const PxReal ex = extents.x; + const PxReal ey = extents.y; + const PxReal ez = extents.z; + + return PxVec3( a00 * ex + a10 * ey + a20 * ez, + a01 * ex + a11 * ey + a21 * ez, + a02 * ex + a12 * ey + a22 * ez); + } + + /** + Computes the obb points. + \param pts [out] 8 box points + */ + PX_FORCE_INLINE void computeBoxPoints(PxVec3* PX_RESTRICT pts) const + { + Gu::computeOBBPoints(pts, center, extents, rot.column0, rot.column1, rot.column2); + } + + void create(const Gu::Capsule& capsule); + + PxMat33 rot; + PxVec3 center; + PxVec3 extents; + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::Box) == 60); + + //! A padded version of Gu::Box, to safely load its data using SIMD + class BoxPadded : public Box + { + public: + PX_FORCE_INLINE BoxPadded() {} + PX_FORCE_INLINE ~BoxPadded() {} + PxU32 padding; + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::BoxPadded) == 64); + + //! Transforms a shape space AABB to a vertex space AABB (conservative). + PX_FORCE_INLINE void computeVertexSpaceAABB(Gu::Box& vertexSpaceOBB, const PxBounds3& worldBounds, const PxTransform& world2Shape, const Cm::FastVertex2ShapeScaling& scaling, bool idtScaleMesh) + { + PX_ASSERT(!worldBounds.isEmpty()); + const PxBounds3 boundsInMesh = PxBounds3::transformFast(world2Shape, worldBounds); // transform bounds from world to shape (excluding mesh scale) + + vertexSpaceOBB.rot = PxMat33(PxIdentity); + if(idtScaleMesh) + { + vertexSpaceOBB.center = boundsInMesh.getCenter(); + vertexSpaceOBB.extents = boundsInMesh.getExtents(); + } + else + { + const PxBounds3 bounds = PxBounds3::basisExtent(scaling.getShape2VertexSkew() * boundsInMesh.getCenter(), scaling.getShape2VertexSkew(), boundsInMesh.getExtents()); + vertexSpaceOBB.center = bounds.getCenter(); + vertexSpaceOBB.extents = bounds.getExtents(); + } + } + +#if PX_VC + #pragma warning(pop) +#endif + +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h new file mode 100644 index 000000000..ab28b9268 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_BOX_H +#define GU_DISTANCE_SEGMENT_BOX_H + +#include "foundation/PxMat33.h" +#include "GuSegment.h" +#include "GuBox.h" + +namespace physx +{ +namespace Gu +{ + + //! Compute the smallest distance from the (finite) line segment to the box. + PX_PHYSX_COMMON_API PxReal distanceSegmentBoxSquared( const PxVec3& segmentPoint0, const PxVec3& segmentPoint1, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxReal* segmentParam = NULL, + PxVec3* boxParam = NULL); + + PX_FORCE_INLINE PxReal distanceSegmentBoxSquared(const Gu::Segment& segment, const Gu::Box& box, PxReal* t = NULL, PxVec3* p = NULL) + { + return distanceSegmentBoxSquared(segment.p0, segment.p1, box.center, box.extents, box.rot, t, p); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h new file mode 100644 index 000000000..65202aaee --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h @@ -0,0 +1,65 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_SEGMENT_H +#define GU_DISTANCE_SEGMENT_SEGMENT_H + +#include "common/PxPhysXCommonConfig.h" +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + // This version fixes accuracy issues (e.g. TTP 4617), but needs to do 2 square roots in order + // to find the normalized direction and length of the segments, and then + // a division in order to renormalize the output + + PX_PHYSX_COMMON_API PxReal distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& dir0, PxReal extent0, + const PxVec3& origin1, const PxVec3& dir1, PxReal extent1, + PxReal* s=NULL, PxReal* t=NULL); + + PX_PHYSX_COMMON_API PxReal distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& extent0, + const PxVec3& origin1, const PxVec3& extent1, + PxReal* s=NULL, PxReal* t=NULL); + + PX_FORCE_INLINE PxReal distanceSegmentSegmentSquared( const Gu::Segment& segment0, + const Gu::Segment& segment1, + PxReal* s=NULL, PxReal* t=NULL) + { + return distanceSegmentSegmentSquared( segment0.p0, segment0.computeDirection(), + segment1.p0, segment1.computeDirection(), + s, t); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h b/src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h new file mode 100644 index 000000000..1b1b8eee2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_BOX_BOX_H +#define GU_INTERSECTION_BOX_BOX_H + +#include "foundation/PxMat33.h" +#include "foundation/PxBounds3.h" +#include "GuBox.h" + +namespace physx +{ +namespace Gu +{ + PX_PHYSX_COMMON_API bool intersectOBBOBB(const PxVec3& e0, const PxVec3& c0, const PxMat33& r0, const PxVec3& e1, const PxVec3& c1, const PxMat33& r1, bool full_test); + + PX_FORCE_INLINE bool intersectOBBAABB(const Gu::Box& obb, const PxBounds3& aabb) + { + PxVec3 center = aabb.getCenter(); + PxVec3 extents = aabb.getExtents(); + return intersectOBBOBB(obb.extents, obb.center, obb.rot, extents, center, PxMat33(PxIdentity), true); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h new file mode 100644 index 000000000..160ccd66f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h @@ -0,0 +1,91 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_TRIANGLE_BOX_H +#define GU_INTERSECTION_TRIANGLE_BOX_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "foundation/PxMat33.h" + +namespace physx +{ +namespace Gu +{ + class Box; + class BoxPadded; + + /** + Tests if a triangle overlaps a box (AABB). This is the reference non-SIMD code. + + \param center [in] the box center + \param extents [in] the box extents + \param p0 [in] triangle's first point + \param p1 [in] triangle's second point + \param p2 [in] triangle's third point + \return true if triangle overlaps box + */ + PX_PHYSX_COMMON_API Ps::IntBool intersectTriangleBox_ReferenceCode(const PxVec3& center, const PxVec3& extents, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2); + + /** + Tests if a triangle overlaps a box (AABB). This is the optimized SIMD code. + + WARNING: the function has various SIMD requirements, left to the calling code: + - function will load 4 bytes after 'center'. Make sure it's safe to load from there. + - function will load 4 bytes after 'extents'. Make sure it's safe to load from there. + - function will load 4 bytes after 'p0'. Make sure it's safe to load from there. + - function will load 4 bytes after 'p1'. Make sure it's safe to load from there. + - function will load 4 bytes after 'p2'. Make sure it's safe to load from there. + If you can't guarantee these requirements, please use the non-SIMD reference code instead. + + \param center [in] the box center. + \param extents [in] the box extents + \param p0 [in] triangle's first point + \param p1 [in] triangle's second point + \param p2 [in] triangle's third point + \return true if triangle overlaps box + */ + PX_PHYSX_COMMON_API Ps::IntBool intersectTriangleBox_Unsafe(const PxVec3& center, const PxVec3& extents, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2); + + /** + Tests if a triangle overlaps a box (OBB). + + There are currently no SIMD-related requirements for p0, p1, p2. + + \param box [in] the box + \param p0 [in] triangle's first point + \param p1 [in] triangle's second point + \param p2 [in] triangle's third point + \return true if triangle overlaps box + */ + PX_PHYSX_COMMON_API Ps::IntBool intersectTriangleBox(const BoxPadded& box, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2); +} // namespace Gu +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h new file mode 100644 index 000000000..e136c8ed7 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h @@ -0,0 +1,251 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_TRIANGLE_BOX_REF_H +#define GU_INTERSECTION_TRIANGLE_BOX_REF_H + +#include "CmPhysXCommon.h" +#include "foundation/PxVec3.h" + + +/********************************************************/ +/* AABB-triangle overlap test code */ +/* by Tomas Akenine-M?r */ +/* Function: int triBoxOverlap(float boxcenter[3], */ +/* float boxhalfsize[3],float triverts[3][3]); */ +/* History: */ +/* 2001-03-05: released the code in its first version */ +/* 2001-06-18: changed the order of the tests, faster */ +/* */ +/* Acknowledgement: Many thanks to Pierre Terdiman for */ +/* suggestions and discussions on how to optimize code. */ +/* Thanks to David Hunt for finding a ">="-bug! */ +/********************************************************/ + + +namespace physx +{ + +#define CROSS(dest,v1,v2) \ + dest.x=v1.y*v2.z-v1.z*v2.y; \ + dest.y=v1.z*v2.x-v1.x*v2.z; \ + dest.z=v1.x*v2.y-v1.y*v2.x; + +#define DOT(v1,v2) (v1.x*v2.x+v1.y*v2.y+v1.z*v2.z) + +#define FINDMINMAX(x0, x1, x2, minimum, maximum) \ + minimum = physx::intrinsics::selectMin(x0, x1); \ + maximum = physx::intrinsics::selectMax(x0, x1); \ + minimum = physx::intrinsics::selectMin(minimum, x2); \ + maximum = physx::intrinsics::selectMax(maximum, x2); + + static PX_CUDA_CALLABLE PX_FORCE_INLINE Ps::IntBool planeBoxOverlap(const PxVec3& normal, PxReal d, const PxVec3& maxbox) + { + PxVec3 vmin, vmax; + + if (normal.x>0.0f) + { + vmin.x = -maxbox.x; + vmax.x = maxbox.x; + } + else + { + vmin.x = maxbox.x; + vmax.x = -maxbox.x; + } + + if (normal.y>0.0f) + { + vmin.y = -maxbox.y; + vmax.y = maxbox.y; + } + else + { + vmin.y = maxbox.y; + vmax.y = -maxbox.y; + } + + if (normal.z>0.0f) + { + vmin.z = -maxbox.z; + vmax.z = maxbox.z; + } + else + { + vmin.z = maxbox.z; + vmax.z = -maxbox.z; + } + + if (normal.dot(vmin) + d > 0.0f) return Ps::IntFalse; + if (normal.dot(vmax) + d >= 0.0f) return Ps::IntTrue; + return Ps::IntFalse; + } + + /*======================== X-tests ========================*/ +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p2 = a*v2.y - b*v2.z; \ + minimum = physx::intrinsics::selectMin(p0, p2); \ + maximum = physx::intrinsics::selectMax(p0, p2); \ + rad = fa * extents.y + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p1 = a*v1.y - b*v1.z; \ + minimum = physx::intrinsics::selectMin(p0, p1); \ + maximum = physx::intrinsics::selectMax(p0, p1); \ + rad = fa * extents.y + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + + /*======================== Y-tests ========================*/ +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p2 = -a*v2.x + b*v2.z; \ + minimum = physx::intrinsics::selectMin(p0, p2); \ + maximum = physx::intrinsics::selectMax(p0, p2); \ + rad = fa * extents.x + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p1 = -a*v1.x + b*v1.z; \ + minimum = physx::intrinsics::selectMin(p0, p1); \ + maximum = physx::intrinsics::selectMax(p0, p1); \ + rad = fa * extents.x + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + + /*======================== Z-tests ========================*/ +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1.x - b*v1.y; \ + p2 = a*v2.x - b*v2.y; \ + minimum = physx::intrinsics::selectMin(p1, p2); \ + maximum = physx::intrinsics::selectMax(p1, p2); \ + rad = fa * extents.x + fb * extents.y; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0.x - b*v0.y; \ + p1 = a*v1.x - b*v1.y; \ + minimum = physx::intrinsics::selectMin(p0, p1); \ + maximum = physx::intrinsics::selectMax(p0, p1); \ + rad = fa * extents.x + fb * extents.y; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + + namespace Gu + { + + static PX_CUDA_CALLABLE PX_FORCE_INLINE Ps::IntBool intersectTriangleBox_RefImpl(const PxVec3& boxcenter, const PxVec3& extents, const PxVec3& tp0, const PxVec3& tp1, const PxVec3& tp2) + { + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + + // This is the fastest branch on Sun - move everything so that the boxcenter is in (0,0,0) + const PxVec3 v0 = tp0 - boxcenter; + const PxVec3 v1 = tp1 - boxcenter; + const PxVec3 v2 = tp2 - boxcenter; + + // compute triangle edges + const PxVec3 e0 = v1 - v0; // tri edge 0 + const PxVec3 e1 = v2 - v1; // tri edge 1 + const PxVec3 e2 = v0 - v2; // tri edge 2 + + float minimum, maximum, rad, p0, p1, p2; + + // Bullet 3: test the 9 tests first (this was faster) + float fex = PxAbs(e0.x); + float fey = PxAbs(e0.y); + float fez = PxAbs(e0.z); + AXISTEST_X01(e0.z, e0.y, fez, fey); + AXISTEST_Y02(e0.z, e0.x, fez, fex); + AXISTEST_Z12(e0.y, e0.x, fey, fex); + + fex = PxAbs(e1.x); + fey = PxAbs(e1.y); + fez = PxAbs(e1.z); + AXISTEST_X01(e1.z, e1.y, fez, fey); + AXISTEST_Y02(e1.z, e1.x, fez, fex); + AXISTEST_Z0(e1.y, e1.x, fey, fex); + + fex = PxAbs(e2.x); + fey = PxAbs(e2.y); + fez = PxAbs(e2.z); + AXISTEST_X2(e2.z, e2.y, fez, fey); + AXISTEST_Y1(e2.z, e2.x, fez, fex); + AXISTEST_Z12(e2.y, e2.x, fey, fex); + + // Bullet 1: + // first test overlap in the {x,y,z}-directions + // find minimum, maximum of the triangle each direction, and test for overlap in + // that direction -- this is equivalent to testing a minimal AABB around + // the triangle against the AABB + + // test in X-direction + FINDMINMAX(v0.x, v1.x, v2.x, minimum, maximum); + if (minimum>extents.x || maximum<-extents.x) return Ps::IntFalse; + + // test in Y-direction + FINDMINMAX(v0.y, v1.y, v2.y, minimum, maximum); + if (minimum>extents.y || maximum<-extents.y) return Ps::IntFalse; + + // test in Z-direction + FINDMINMAX(v0.z, v1.z, v2.z, minimum, maximum); + if (minimum>extents.z || maximum<-extents.z) return Ps::IntFalse; + + // Bullet 2: + // test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + PxVec3 normal; + CROSS(normal, e0, e1); + const float d = -DOT(normal, v0); // plane eq: normal.x+d=0 + if (!planeBoxOverlap(normal, d, extents)) return Ps::IntFalse; + + return Ps::IntTrue; // box and triangle overlaps + } + } + +#undef CROSS +#undef DOT +#undef FINDMINMAX +#undef AXISTEST_X01 +#undef AXISTEST_X2 +#undef AXISTEST_Y02 +#undef AXISTEST_Y1 +#undef AXISTEST_Z12 +#undef AXISTEST_Z0 + +} + +#endif + diff --git a/src/PhysX/physx/source/geomutils/include/GuRaycastTests.h b/src/PhysX/physx/source/geomutils/include/GuRaycastTests.h new file mode 100644 index 000000000..e54e65b71 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuRaycastTests.h @@ -0,0 +1,72 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_RAYCAST_TESTS_H +#define GU_RAYCAST_TESTS_H + +#include "CmPhysXCommon.h" +#include "foundation/PxSimpleTypes.h" +#include "PxQueryReport.h" +#include "PxGeometry.h" + +namespace physx +{ + // PT: TODO: why is PxHitFlag::eMESH_MULTIPLE used in the ray-vs-hf function, but not in the ray-vs-mesh function? + + // PT: we use a define to be able to quickly change the signature of all raycast functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] geom geometry object to raycast against + // \param[in] pose pose of geometry object + // \param[in] rayOrigin ray's origin + // \param[in] rayDir ray's unit dir + // \param[in] maxDist ray's length/max distance + // \param[in] hitFlags query behavior flags + // \param[in] maxHits max number of hits = size of 'hits' buffer + // \param[out] hits result buffer where to write raycast hits + #define GU_RAY_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, \ + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits + namespace Gu + { + // PT: function pointer for Geom-indexed raycast functions + // See GU_RAY_FUNC_PARAMS for function parameters details. + // \return number of hits written to 'hits' result buffer + // \note there's no mechanism to report overflow. Returned number of hits is just clamped to maxHits. + typedef PxU32 (*RaycastFunc) (GU_RAY_FUNC_PARAMS); + + // PT: typedef for a bundle of all raycast functions, i.e. the function table itself (indexed by geom-type). + typedef RaycastFunc GeomRaycastTable[PxGeometryType::eGEOMETRY_COUNT]; + + // PT: retrieves the raycast function table (for access by external non-Gu modules) + PX_PHYSX_COMMON_API const GeomRaycastTable& getRaycastFuncTable(); + + } // namespace Gu +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h b/src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h new file mode 100644 index 000000000..a28f39f82 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SIMD_HELPERS_H +#define GU_SIMD_HELPERS_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "geometry/PxTriangle.h" +#include "foundation/PxMat33.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + //! A padded version of PxTriangle, to safely load its data using SIMD + class TrianglePadded : public PxTriangle + { + public: + PX_FORCE_INLINE TrianglePadded() {} + PX_FORCE_INLINE ~TrianglePadded() {} + PxU32 padding; + }; + + // PT: wrapper helper class to make sure we can safely load a PxVec3 using SIMD loads + // PT: TODO: refactor with PxVec3Pad + class Vec3p : public PxVec3 + { + public: + PX_FORCE_INLINE Vec3p() {} + PX_FORCE_INLINE ~Vec3p() {} + PX_FORCE_INLINE Vec3p(const PxVec3& p) : PxVec3(p) {} + PX_FORCE_INLINE Vec3p(float f) : PxVec3(f) {} + PxU32 padding; + }; + PX_COMPILE_TIME_ASSERT(sizeof(Vec3p) == 16); + + //! A padded version of PxMat33, to safely load its data using SIMD + class PxMat33Padded : public PxMat33 + { + public: + explicit PX_FORCE_INLINE PxMat33Padded(const PxQuat& q) + { + using namespace Ps::aos; + const QuatV qV = V4LoadU(&q.x); + Vec3V column0V, column1V, column2V; + QuatGetMat33V(qV, column0V, column1V, column2V); +#if defined(PX_SIMD_DISABLED) || PX_ANDROID || (PX_LINUX && (PX_ARM || PX_A64)) + V3StoreU(column0V, column0); + V3StoreU(column1V, column1); + V3StoreU(column2V, column2); +#else + V4StoreU(column0V, &column0.x); + V4StoreU(column1V, &column1.x); + V4StoreU(column2V, &column2.x); +#endif + } + PX_FORCE_INLINE ~PxMat33Padded() {} + PX_FORCE_INLINE void operator=(const PxMat33& other) + { + column0 = other.column0; + column1 = other.column1; + column2 = other.column2; + } + PxU32 padding; + }; + +} // namespace Gu +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuSegment.h b/src/PhysX/physx/source/geomutils/include/GuSegment.h new file mode 100644 index 000000000..bcee652c5 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuSegment.h @@ -0,0 +1,182 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SEGMENT_H +#define GU_SEGMENT_H +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "Ps.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + + /** + \brief Represents a line segment. + + Line segment geometry + In some cases this structure will be used to represent the infinite line that passes point0 and point1. + */ + class Segment + { + public: + /** + \brief Constructor + */ + PX_INLINE Segment() + { + } + + /** + \brief Constructor + */ + PX_INLINE Segment(const PxVec3& _p0, const PxVec3& _p1) : p0(_p0), p1(_p1) + { + } + + /** + \brief Copy constructor + */ + PX_INLINE Segment(const Segment& seg) : p0(seg.p0), p1(seg.p1) + { + } + + /** + \brief Destructor + */ + PX_INLINE ~Segment() + { + } + + //! Assignment operator + PX_INLINE Segment& operator=(const Segment& other) + { + p0 = other.p0; + p1 = other.p1; + return *this; + } + + //! Equality operator + PX_INLINE bool operator==(const Segment& other) const + { + return (p0==other.p0 && p1==other.p1); + } + + //! Inequality operator + PX_INLINE bool operator!=(const Segment& other) const + { + return (p0!=other.p0 || p1!=other.p1); + } + + PX_INLINE const PxVec3& getOrigin() const + { + return p0; + } + + //! Return the vector from point0 to point1 + PX_INLINE PxVec3 computeDirection() const + { + return p1 - p0; + } + + //! Return the vector from point0 to point1 + PX_INLINE void computeDirection(PxVec3& dir) const + { + dir = p1 - p0; + } + + //! Return the center of the segment segment + PX_INLINE PxVec3 computeCenter() const + { + return (p0 + p1)*0.5f; + } + + PX_INLINE PxF32 computeLength() const + { + return (p1-p0).magnitude(); + } + + PX_INLINE PxF32 computeSquareLength() const + { + return (p1-p0).magnitudeSquared(); + } + + // PT: TODO: remove this one + //! Return the square of the length of vector from point0 to point1 + PX_INLINE PxReal lengthSquared() const + { + return ((p1 - p0).magnitudeSquared()); + } + + // PT: TODO: remove this one + //! Return the length of vector from point0 to point1 + PX_INLINE PxReal length() const + { + return ((p1 - p0).magnitude()); + } + + /* PX_INLINE void setOriginDirection(const PxVec3& origin, const PxVec3& direction) + { + p0 = p1 = origin; + p1 += direction; + }*/ + + /** + \brief Computes a point on the segment + + \param[out] pt point on segment + \param[in] t point's parameter [t=0 => pt = mP0, t=1 => pt = mP1] + */ + PX_INLINE void computePoint(PxVec3& pt, PxF32 t) const + { + pt = p0 + t * (p1 - p0); + } + + // PT: TODO: remove this one + //! Return the point at parameter t along the line: point0 + t*(point1-point0) + PX_INLINE PxVec3 getPointAt(PxReal t) const + { + return (p1 - p0)*t + p0; + } + + PxVec3 p0; //!< Start of segment + PxVec3 p1; //!< End of segment + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::Segment) == 24); +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp new file mode 100644 index 000000000..1d9ae9dcc --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp @@ -0,0 +1,307 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuAABBTreeBuild.h" + +#include "PsMathUtils.h" +#include "PsFoundation.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; + +NodeAllocator::NodeAllocator() : mPool(NULL), mCurrentSlabIndex(0), mTotalNbNodes(0) +{ +} + +NodeAllocator::~NodeAllocator() +{ + release(); +} + +void NodeAllocator::release() +{ + const PxU32 nbSlabs = mSlabs.size(); + for (PxU32 i = 0; imNodeIndex = 0; + mPool->mNbPrimitives = nbPrimitives; + + mSlabs.pushBack(Slab(mPool, 1, estimatedFinalSize)); + mCurrentSlabIndex = 0; + mTotalNbNodes = 1; +} + +// PT: TODO: inline this? +AABBTreeBuildNode* NodeAllocator::getBiNode() +{ + mTotalNbNodes += 2; + Slab& currentSlab = mSlabs[mCurrentSlabIndex]; + if (currentSlab.mNbUsedNodes + 2 <= currentSlab.mMaxNbNodes) + { + AABBTreeBuildNode* biNode = currentSlab.mPool + currentSlab.mNbUsedNodes; + currentSlab.mNbUsedNodes += 2; + return biNode; + } + else + { + // Allocate new slab + const PxU32 size = 1024; + AABBTreeBuildNode* pool = PX_NEW(AABBTreeBuildNode)[size]; + PxMemZero(pool, sizeof(AABBTreeBuildNode)*size); + + mSlabs.pushBack(Slab(pool, 2, size)); + mCurrentSlabIndex++; + return pool; + } +} + +static PX_FORCE_INLINE float getSplittingValue(const PxBounds3& global_box, PxU32 axis) +{ + // Default split value = middle of the axis (using only the box) + return global_box.getCenter(axis); +} + +static PxU32 split(const PxBounds3& box, PxU32 nb, PxU32* const PX_RESTRICT prims, PxU32 axis, const AABBTreeBuildParams& params) +{ + // Get node split value + const float splitValue = getSplittingValue(box, axis); + + PxU32 nbPos = 0; + // Loop through all node-related primitives. Their indices range from "mNodePrimitives[0]" to "mNodePrimitives[mNbPrimitives-1]", + // with mNodePrimitives = mIndices + mNodeIndex (i.e. those indices map the global list in the tree params). + + // PT: to avoid calling the unsafe [] operator + const size_t ptrValue = size_t(params.mCache) + axis * sizeof(float); + const PxVec3* /*PX_RESTRICT*/ cache = reinterpret_cast(ptrValue); + + for (PxU32 i = 0; i splitValue) + { + // Swap entries + prims[i] = prims[nbPos]; + prims[nbPos] = index; + // Count primitives assigned to positive space + nbPos++; + } + } + return nbPos; +} + +void AABBTreeBuildNode::subdivide(const AABBTreeBuildParams& params, BuildStats& stats, NodeAllocator& allocator, PxU32* const indices) +{ + PxU32* const PX_RESTRICT primitives = indices + mNodeIndex; + const PxU32 nbPrims = mNbPrimitives; + + // Compute global box & means for current node. The box is stored in mBV. + Vec4V meansV; + { + const PxBounds3* PX_RESTRICT boxes = params.mAABBArray; + PX_ASSERT(boxes); + PX_ASSERT(primitives); + PX_ASSERT(nbPrims); + + Vec4V minV = V4LoadU(&boxes[primitives[0]].minimum.x); + Vec4V maxV = V4LoadU(&boxes[primitives[0]].maximum.x); + + meansV = V4LoadU(¶ms.mCache[primitives[0]].x); + + for (PxU32 i = 1; iparams.mLimit) + { + nbPos = nbPrims >> 1; + } + else return; + } + + // Now create children and assign their pointers. + mPos = allocator.getBiNode(); + + stats.increaseCount(2); + + // Assign children + PX_ASSERT(!isLeaf()); + AABBTreeBuildNode* Pos = const_cast(mPos); + AABBTreeBuildNode* Neg = Pos + 1; + Pos->mNodeIndex = mNodeIndex; + Pos->mNbPrimitives = nbPos; + Neg->mNodeIndex = mNodeIndex + nbPos; + Neg->mNbPrimitives = mNbPrimitives - nbPos; +} + +void AABBTreeBuildNode::_buildHierarchy(AABBTreeBuildParams& params, BuildStats& stats, NodeAllocator& nodeBase, PxU32* const indices) +{ + // Subdivide current node + subdivide(params, stats, nodeBase, indices); + + // Recurse + if (!isLeaf()) + { + AABBTreeBuildNode* Pos = const_cast(getPos()); + PX_ASSERT(Pos); + AABBTreeBuildNode* Neg = Pos + 1; + Pos->_buildHierarchy(params, stats, nodeBase, indices); + Neg->_buildHierarchy(params, stats, nodeBase, indices); + } + + stats.mTotalPrims += mNbPrimitives; +} + +bool Gu::initAABBTreeBuild(AABBTreeBuildParams& params, NodeAllocator& nodeAllocator, BuildStats& stats, PxU32*& indices) +{ + const PxU32 numPrimitives = params.mNbPrimitives; + + if(numPrimitives == 0) + return false; + + // indices already initialized! + if(indices) + return false; + + // Init stats + stats.setCount(1); + + // Initialize indices. This list will be modified during build. + indices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*numPrimitives, "AABB tree indices")); + // Identity permutation + for(PxU32 i=0;i(PX_ALLOC(sizeof(PxVec3)*(numPrimitives+1), "cache")); + const float half = 0.5f; + const FloatV halfV = FLoad(half); + for(PxU32 i=0;i_buildHierarchy(params, stats, nodeAllocator, indices); + + return true; +} + diff --git a/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h new file mode 100644 index 000000000..93dfb7f1a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h @@ -0,0 +1,185 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SQ_AABBTREE_BUILD_H +#define SQ_AABBTREE_BUILD_H + +#include "foundation/PxMemory.h" +#include "foundation/PxBounds3.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PsVecMath.h" +#include "PsArray.h" + +namespace physx +{ + + using namespace shdfnd::aos; + + namespace Gu + { +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + //! Contains AABB-tree build statistics + struct PX_PHYSX_COMMON_API BuildStats + { + BuildStats() : mCount(0), mTotalPrims(0) {} + + PxU32 mCount; //!< Number of nodes created + PxU32 mTotalPrims; //!< Total accumulated number of primitives. Should be much higher than the source + //!< number of prims, since it accumulates all prims covered by each node (i.e. internal + //!< nodes too, not just leaf ones) + + PX_FORCE_INLINE void reset() { mCount = mTotalPrims = 0; } + + PX_FORCE_INLINE void setCount(PxU32 nb) { mCount = nb; } + PX_FORCE_INLINE void increaseCount(PxU32 nb) { mCount += nb; } + PX_FORCE_INLINE PxU32 getCount() const { return mCount; } + }; + + //! Contains AABB-tree build parameters + class PX_PHYSX_COMMON_API AABBTreeBuildParams : public Ps::UserAllocated + { + public: + AABBTreeBuildParams(PxU32 limit = 1, PxU32 nb_prims = 0, const PxBounds3* boxes = NULL) : + mLimit(limit), mNbPrimitives(nb_prims), mAABBArray(boxes), mCache(NULL) {} + ~AABBTreeBuildParams() + { + reset(); + } + + PX_FORCE_INLINE void reset() + { + mLimit = mNbPrimitives = 0; + mAABBArray = NULL; + PX_FREE_AND_RESET(mCache); + } + + PxU32 mLimit; //!< Limit number of primitives / node. If limit is 1, build a complete tree (2*N-1 nodes) + PxU32 mNbPrimitives; //!< Number of (source) primitives. + const PxBounds3* mAABBArray; //!< Shortcut to an app-controlled array of AABBs. + PxVec3* mCache; //!< Cache for AABB centers - managed by build code. + }; + + class NodeAllocator; + + //! AABB tree node used for building + class PX_PHYSX_COMMON_API AABBTreeBuildNode : public Ps::UserAllocated + { + public: + PX_FORCE_INLINE AABBTreeBuildNode() {} + PX_FORCE_INLINE ~AABBTreeBuildNode() {} + + PX_FORCE_INLINE const PxBounds3& getAABB() const { return mBV; } + PX_FORCE_INLINE const AABBTreeBuildNode* getPos() const { return mPos; } + PX_FORCE_INLINE const AABBTreeBuildNode* getNeg() const { const AABBTreeBuildNode* P = mPos; return P ? P + 1 : NULL; } + + PX_FORCE_INLINE bool isLeaf() const { return !getPos(); } + + PxBounds3 mBV; //!< Global bounding-volume enclosing all the node-related primitives + const AABBTreeBuildNode* mPos; //!< "Positive" & "Negative" children + + PxU32 mNodeIndex; //!< Index of node-related primitives (in the tree's mIndices array) + PxU32 mNbPrimitives; //!< Number of primitives for this node + + // Data access + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mNbPrimitives; } + + PX_FORCE_INLINE PxU32 getNbRuntimePrimitives() const { return mNbPrimitives; } + PX_FORCE_INLINE void setNbRunTimePrimitives(PxU32 val) { mNbPrimitives = val; } + PX_FORCE_INLINE const PxU32* getPrimitives(const PxU32* base) const { return base + mNodeIndex; } + PX_FORCE_INLINE PxU32* getPrimitives(PxU32* base) { return base + mNodeIndex; } + + // Internal methods + void subdivide(const AABBTreeBuildParams& params, BuildStats& stats, NodeAllocator& allocator, PxU32* const indices); + void _buildHierarchy(AABBTreeBuildParams& params, BuildStats& stats, NodeAllocator& allocator, PxU32* const indices); + }; + + //! For complete trees we can predict the final number of nodes and preallocate them. For incomplete trees we can't. + //! But we don't want to allocate nodes one by one (which would be quite slow), so we use this helper class to + //! allocate N nodes at once, while minimizing the amount of nodes allocated for nothing. An initial amount of + //! nodes is estimated using the max number for a complete tree, and the user-defined number of primitives per leaf. + //! In ideal cases this estimated number will be quite close to the final number of nodes. When that number is not + //! enough though, slabs of N=1024 extra nodes are allocated until the build is complete. + class PX_PHYSX_COMMON_API NodeAllocator : public Ps::UserAllocated + { + public: + NodeAllocator(); + ~NodeAllocator(); + + void release(); + void init(PxU32 nbPrimitives, PxU32 limit); + AABBTreeBuildNode* getBiNode(); + + AABBTreeBuildNode* mPool; + + struct Slab + { + PX_FORCE_INLINE Slab() {} + PX_FORCE_INLINE Slab(AABBTreeBuildNode* pool, PxU32 nbUsedNodes, PxU32 maxNbNodes) : mPool(pool), mNbUsedNodes(nbUsedNodes), mMaxNbNodes(maxNbNodes) {} + AABBTreeBuildNode* mPool; + PxU32 mNbUsedNodes; + PxU32 mMaxNbNodes; + }; + Ps::Array mSlabs; + PxU32 mCurrentSlabIndex; + PxU32 mTotalNbNodes; + }; + + /* + * \brief Initialize AABBtree build from given parameters. + * \param params [in] AABBTree build params + * \param nodeAllocator [in] Node allocator + * \param stats [out] Statistics + * \param indices [out] Indices buffer allocated during build + */ + bool PX_PHYSX_COMMON_API initAABBTreeBuild(AABBTreeBuildParams& params, NodeAllocator& nodeAllocator, BuildStats& stats, PxU32*& indices); + + /* + * \brief Builds AABBtree from given parameters. + * \note Initialize will be called! + * \param params [in] AABBTree build params + * \param nodeAllocator [in] Node allocator + * \param stats [out] Statistics + * \param indices [out] Indices buffer allocated during build + */ + bool PX_PHYSX_COMMON_API buildAABBTree(AABBTreeBuildParams& params, NodeAllocator& nodeAllocator, BuildStats& stats, PxU32*& indices); +#if PX_VC + #pragma warning(pop) +#endif + + } // namespace Sq + +} + +#endif // SQ_AABBTREE_H diff --git a/src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h b/src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h new file mode 100644 index 000000000..2fbc91c95 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h @@ -0,0 +1,239 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef GU_AABBTREEQUERY_H +#define GU_AABBTREEQUERY_H + +#include "GuBVHTestsSIMD.h" +#include "PsInlineArray.h" + +namespace physx +{ + namespace Gu + { + #define RAW_TRAVERSAL_STACK_SIZE 256 + + ////////////////////////////////////////////////////////////////////////// + + static PX_FORCE_INLINE void getBoundsTimesTwo(Vec4V& center, Vec4V& extents, const PxBounds3* boxes, PxU32 poolIndex) + { + const PxBounds3* objectBounds = boxes + poolIndex; + + const Vec4V minV = V4LoadU(&objectBounds->minimum.x); + const Vec4V maxV = V4LoadU(&objectBounds->maximum.x); + + center = V4Add(maxV, minV); + extents = V4Sub(maxV, minV); + } + + ////////////////////////////////////////////////////////////////////////// + + template + class AABBTreeOverlap + { + public: + bool operator()(const Payload* objects, const PxBounds3* boxes, const Tree& tree, const Test& test, QueryCallback& visitor) + { + using namespace Cm; + + Ps::InlineArray stack; + stack.forceSize_Unsafe(RAW_TRAVERSAL_STACK_SIZE); + const Node* const nodeBase = tree.getNodes(); + stack[0] = nodeBase; + PxU32 stackIndex = 1; + + while (stackIndex > 0) + { + const Node* node = stack[--stackIndex]; + Vec3V center, extents; + node->getAABBCenterExtentsV(¢er, &extents); + while (test(center, extents)) + { + if (node->isLeaf()) + { + PxU32 nbPrims = node->getNbPrimitives(); + const bool doBoxTest = nbPrims > 1; + const PxU32* prims = node->getPrimitives(tree.getIndices()); + while (nbPrims--) + { + const PxU32* prunableIndex = prims; + prims++; + + const PxU32 poolIndex = *prunableIndex; + if (doBoxTest) + { + Vec4V center2, extents2; + getBoundsTimesTwo(center2, extents2, boxes, poolIndex); + + const float half = 0.5f; + const FloatV halfV = FLoad(half); + + const Vec4V extents_ = V4Scale(extents2, halfV); + const Vec4V center_ = V4Scale(center2, halfV); + + if (!test(Vec3V_From_Vec4V(center_), Vec3V_From_Vec4V(extents_))) + continue; + } + + PxReal unusedDistance; + if (!visitor.invoke(unusedDistance, objects[poolIndex])) + return false; + } + break; + } + + const Node* children = node->getPos(nodeBase); + + node = children; + stack[stackIndex++] = children + 1; + if(stackIndex == stack.capacity()) + stack.resizeUninitialized(stack.capacity() * 2); + node->getAABBCenterExtentsV(¢er, &extents); + } + } + return true; + } + }; + + ////////////////////////////////////////////////////////////////////////// + + template // use inflate=true for sweeps, inflate=false for raycasts + static PX_FORCE_INLINE bool doLeafTest(const Node* node, Gu::RayAABBTest& test, PxReal& md, PxReal oldMaxDist, + const Payload* objects, const PxBounds3* boxes, const Tree& tree, + PxReal& maxDist, QueryCallback& pcb) + { + PxU32 nbPrims = node->getNbPrimitives(); + const bool doBoxTest = nbPrims > 1; + const PxU32* prims = node->getPrimitives(tree.getIndices()); + while (nbPrims--) + { + const PxU32* prunableIndex = prims; + prims++; + + const PxU32 poolIndex = *prunableIndex; + if (doBoxTest) + { + Vec4V center_, extents_; + getBoundsTimesTwo(center_, extents_, boxes, poolIndex); + + if (!test.check(Vec3V_From_Vec4V(center_), Vec3V_From_Vec4V(extents_))) + continue; + } + + if (!pcb.invoke(md, objects[poolIndex])) + return false; + + if (md < oldMaxDist) + { + maxDist = md; + test.setDistance(md); + } + } + return true; + } + + ////////////////////////////////////////////////////////////////////////// + + template // use inflate=true for sweeps, inflate=false for raycasts + class AABBTreeRaycast + { + public: + bool operator()( + const Payload* objects, const PxBounds3* boxes, const Tree& tree, + const PxVec3& origin, const PxVec3& unitDir, PxReal& maxDist, const PxVec3& inflation, + QueryCallback& pcb) + { + using namespace Cm; + + // PT: we will pass center*2 and extents*2 to the ray-box code, to save some work per-box + // So we initialize the test with values multiplied by 2 as well, to get correct results + Gu::RayAABBTest test(origin*2.0f, unitDir*2.0f, maxDist, inflation*2.0f); + + Ps::InlineArray stack; + stack.forceSize_Unsafe(RAW_TRAVERSAL_STACK_SIZE); + const Node* const nodeBase = tree.getNodes(); + stack[0] = nodeBase; + PxU32 stackIndex = 1; + + PxReal oldMaxDist; + while (stackIndex--) + { + const Node* node = stack[stackIndex]; + Vec3V center, extents; + node->getAABBCenterExtentsV2(¢er, &extents); + if (test.check(center, extents)) // TODO: try timestamp ray shortening to skip this + { + PxReal md = maxDist; // has to be before the goto below to avoid compile error + while (!node->isLeaf()) + { + const Node* children = node->getPos(nodeBase); + + Vec3V c0, e0; + children[0].getAABBCenterExtentsV2(&c0, &e0); + const PxU32 b0 = test.check(c0, e0); + + Vec3V c1, e1; + children[1].getAABBCenterExtentsV2(&c1, &e1); + const PxU32 b1 = test.check(c1, e1); + + if (b0 && b1) // if both intersect, push the one with the further center on the stack for later + { + // & 1 because FAllGrtr behavior differs across platforms + const PxU32 bit = FAllGrtr(V3Dot(V3Sub(c1, c0), test.mDir), FZero()) & 1; + stack[stackIndex++] = children + bit; + node = children + (1 - bit); + if (stackIndex == stack.capacity()) + stack.resizeUninitialized(stack.capacity() * 2); + } + else if (b0) + node = children; + else if (b1) + node = children + 1; + else + goto skip_leaf_code; + } + + oldMaxDist = maxDist; // we copy since maxDist can be updated in the callback and md(node, test, md, oldMaxDist, + objects, boxes, tree, + maxDist, + pcb)) + return false; + skip_leaf_code:; + } + } + return true; + } + }; + } +} + +#endif // SQ_AABBTREEQUERY_H diff --git a/src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp new file mode 100644 index 000000000..b565da2b6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp @@ -0,0 +1,186 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuBVHStructure.h" +#include "GuAABBTreeBuild.h" +#include "GuAABBTreeQuery.h" +#include "GuSerialize.h" +#include "GuBounds.h" +#include "PsFoundation.h" +#include "CmUtils.h" + +using namespace physx; +using namespace Gu; + +BVHStructure::BVHStructure(GuMeshFactory* factory): + PxBVHStructure(PxType(PxConcreteType::eBVH_STRUCTURE), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE), + mMeshFactory(factory), + mNumVolumes(0), + mNumNodes(0), + mBounds(NULL), + mIndices(NULL), + mVolumes(NULL), + mNodes(NULL) +{ +} + +BVHStructure::BVHStructure(GuMeshFactory* factory, BVHStructureData& bvhData): + PxBVHStructure(PxType(PxConcreteType::eBVH_STRUCTURE), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE), + mMeshFactory(factory), + mNumVolumes(bvhData.mNumVolumes), + mNumNodes(bvhData.mNumNodes), + mBounds(bvhData.mBounds), + mIndices(bvhData.mIndices), + mVolumes(NULL), + mNodes(bvhData.mNodes) +{ +} + +BVHStructure::~BVHStructure() +{ +} + +bool BVHStructure::load(PxInputStream& stream) +{ + // Import header + PxU32 version; + bool mismatch; + if(!readHeader('B', 'V', 'H', 'S', version, mismatch, stream)) + return false; + + // read numVolumes, numNodes together + ReadDwordBuffer(&mNumVolumes, 2, mismatch, stream); + + // read indices + mIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mNumVolumes, "BVH indices")); + ReadDwordBuffer(mIndices, mNumVolumes, mismatch, stream); + + // read bounds + mBounds = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(mNumVolumes + 1), "BVH bounds")); + readFloatBuffer(&mBounds[0].minimum.x, mNumVolumes*(3 + 3), mismatch, stream); + + // read nodes + mNodes = reinterpret_cast(PX_ALLOC(sizeof(BVHNode)*mNumNodes, "BVH nodes")); + for(PxU32 i = 0; i < mNumNodes; i++) + { + ReadDwordBuffer(&mNodes[i].mData, 1, mismatch, stream); + + readFloatBuffer(&mNodes[i].mBV.minimum.x, 3 + 3, mismatch, stream); + } + return true; +} + +void BVHStructure::release() +{ + decRefCount(); +} + +void BVHStructure::onRefCountZero() +{ + PX_FREE_AND_RESET(mBounds); + PX_FREE_AND_RESET(mIndices); + PX_FREE_AND_RESET(mNodes); + PX_FREE_AND_RESET(mVolumes); + + mNumNodes = 0; + mNumVolumes = 0; + + if(mMeshFactory->removeBVHStructure(*this)) + { + const PxType type = getConcreteType(); + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, type); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::BVHStructure::release: double deletion detected!"); +} + +void BVHStructure::createVolumes() const +{ + if(!mVolumes) + { + mVolumes = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mNumVolumes, "BVH volume list")); + for(PxU32 i = 0; i < mNumVolumes; i++) + { + mVolumes[i] = i; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Query Implementation + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +PxU32 BVHStructure::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal maxDist, PxU32 maxHits, PxU32* PX_RESTRICT rayHits) const +{ + createVolumes(); + + BVHCallback cbk(rayHits, maxHits); + BVHTree tree(mNodes, mIndices); + AABBTreeRaycast()(mVolumes, mBounds, tree, origin, unitDir, maxDist, PxVec3(0.0f), cbk); + + return cbk.mCurrentHitsCount; +} + +PxU32 BVHStructure::sweep(const PxBounds3& aabb, const PxVec3& unitDir, PxReal maxDist, PxU32 maxHits, PxU32* PX_RESTRICT sweepHits) const +{ + createVolumes(); + + const PxVec3 extents = aabb.getExtents(); + + BVHCallback cbk(sweepHits, maxHits); + BVHTree tree(mNodes, mIndices); + + AABBTreeRaycast()(mVolumes, mBounds, tree, aabb.getCenter(), unitDir, maxDist, extents, cbk); + + return cbk.mCurrentHitsCount; +} + + +PxU32 BVHStructure::overlap(const PxBounds3& aabb, PxU32 maxHits, PxU32* PX_RESTRICT overlapHits) const +{ + createVolumes(); + + BVHCallback cbk(overlapHits, maxHits); + BVHTree tree(mNodes, mIndices); + + const Gu::AABBAABBTest test(aabb); + AABBTreeOverlap()(mVolumes, mBounds, tree, test, cbk); + + return cbk.mCurrentHitsCount; +} + diff --git a/src/PhysX/physx/source/geomutils/src/GuBVHStructure.h b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.h new file mode 100644 index 000000000..cf8b94706 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.h @@ -0,0 +1,200 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef GU_BVH_STRUCTURE_H +#define GU_BVH_STRUCTURE_H + +/** \addtogroup geomutils +@{ +*/ + +#include "PsUserAllocated.h" +#include "CmRefCountable.h" +#include "CmPhysXCommon.h" +#include "GuMeshFactory.h" +#include "PsVecMath.h" +#include "PxBVHStructure.h" + +namespace physx +{ +namespace Gu +{ + struct BVHNode + { + public: + PX_FORCE_INLINE PxU32 isLeaf() const { return mData&1; } + + PX_FORCE_INLINE const PxU32* getPrimitives(const PxU32* base) const { return base + (mData>>5); } + PX_FORCE_INLINE PxU32* getPrimitives(PxU32* base) { return base + (mData>>5); } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return (mData>>1)&15; } + + PX_FORCE_INLINE PxU32 getPosIndex() const { return mData>>1; } + PX_FORCE_INLINE PxU32 getNegIndex() const { return (mData>>1) + 1; } + PX_FORCE_INLINE const BVHNode* getPos(const BVHNode* base) const { return base + (mData>>1); } + PX_FORCE_INLINE const BVHNode* getNeg(const BVHNode* base) const { const BVHNode* P = getPos(base); return P ? P+1 : NULL;} + + PX_FORCE_INLINE BVHNode* getPos(BVHNode* base) { return base + (mData >> 1); } + PX_FORCE_INLINE BVHNode* getNeg(BVHNode* base) { BVHNode* P = getPos(base); return P ? P + 1 : NULL; } + + + PX_FORCE_INLINE void getAABBCenterExtentsV(shdfnd::aos::Vec3V* center, shdfnd::aos::Vec3V* extents) const + { + const shdfnd::aos::Vec4V minV = shdfnd::aos::V4LoadU(&mBV.minimum.x); + const shdfnd::aos::Vec4V maxV = shdfnd::aos::V4LoadU(&mBV.maximum.x); + + const float half = 0.5f; + const shdfnd::aos::FloatV halfV = shdfnd::aos::FLoad(half); + + *extents = shdfnd::aos::Vec3V_From_Vec4V(shdfnd::aos::V4Scale(shdfnd::aos::V4Sub(maxV, minV), halfV)); + *center = shdfnd::aos::Vec3V_From_Vec4V(shdfnd::aos::V4Scale(shdfnd::aos::V4Add(maxV, minV), halfV)); + } + + PX_FORCE_INLINE void getAABBCenterExtentsV2(shdfnd::aos::Vec3V* center, shdfnd::aos::Vec3V* extents) const + { + const shdfnd::aos::Vec4V minV = shdfnd::aos::V4LoadU(&mBV.minimum.x); + const shdfnd::aos::Vec4V maxV = shdfnd::aos::V4LoadU(&mBV.maximum.x); + + *extents = shdfnd::aos::Vec3V_From_Vec4V(shdfnd::aos::V4Sub(maxV, minV)); + *center = shdfnd::aos::Vec3V_From_Vec4V(shdfnd::aos::V4Add(maxV, minV)); + } + + PX_FORCE_INLINE void getAABBMinMaxV(shdfnd::aos::Vec4V* minV, shdfnd::aos::Vec4V* maxV) const + { + *minV = shdfnd::aos::V4LoadU(&mBV.minimum.x); + *maxV = shdfnd::aos::V4LoadU(&mBV.maximum.x); + } + + PxBounds3 mBV; // Global bounding-volume enclosing all the node-related primitives + PxU32 mData; // 27 bits node or prim index|4 bits #prims|1 bit leaf + }; + + struct BVHTree + { + BVHTree(const BVHNode* node, const PxU32* indices): + mRootNode(node), + mIndices(indices) + { + } + + + const BVHNode* getNodes() const { return mRootNode; } + const PxU32* getIndices() const { return mIndices; } + + const BVHNode* mRootNode; + const PxU32* mIndices; + }; + + struct BVHCallback + { + BVHCallback(PxU32* hits, PxU32 numMaxHits): + mHits(hits), + mNbMaxHits(numMaxHits), + mCurrentHitsCount(0) + { + } + + bool invoke(PxReal& , PxU32 payload) + { + mHits[mCurrentHitsCount++] = payload; + if(mCurrentHitsCount == mNbMaxHits) + return false; + + return true; + } + + PxU32* mHits; + PxU32 mNbMaxHits; + PxU32 mCurrentHitsCount; + }; + + struct BVHStructureData + { + PxU32 mNumVolumes; + PxU32 mNumNodes; + PxBounds3* mBounds; + PxU32* mIndices; + BVHNode* mNodes; + }; +/** +\brief Represents a BVH. +*/ + class BVHStructure: public PxBVHStructure, public Ps::UserAllocated, public Cm::RefCountable + { + public: + /** + \brief Constructor + */ + BVHStructure(GuMeshFactory* factory); + BVHStructure(GuMeshFactory* factory, BVHStructureData& data); + + /** + \brief Destructor + */ + ~BVHStructure(); + + + bool load(PxInputStream& desc); + + void release(); + +// PxBVHStructure + PxU32 getNbBounds() const { return mNumVolumes; } + const PxBounds3* getBounds() const { return mBounds; } + PxU32 raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal maxDist, PxU32 maxHits, PxU32* PX_RESTRICT rayHits) const; + PxU32 sweep(const PxBounds3& aabb, const PxVec3& unitDir, PxReal maxDist, PxU32 maxHits, PxU32* PX_RESTRICT sweepHits) const; + PxU32 overlap(const PxBounds3& aabb, PxU32 maxHits, PxU32* PX_RESTRICT overlapHits) const; +// ~PxBVHStructure + +// Cm::RefCountable + virtual void onRefCountZero(); +//~Cm::RefCountable + + const BVHNode* getNodes() const { return mNodes; } + const PxU32* getIndices() const { return mIndices; } + + private: + void createVolumes() const; + + private: + GuMeshFactory* mMeshFactory; + + PxU32 mNumVolumes; + PxU32 mNumNodes; + PxBounds3* mBounds; + PxU32* mIndices; + mutable PxU32* mVolumes; // used just for queries + BVHNode* mNodes; + }; +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h b/src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h new file mode 100644 index 000000000..903b4416a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h @@ -0,0 +1,259 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef GU_RAWQUERY_TESTS_SIMD_H +#define GU_RAWQUERY_TESTS_SIMD_H + +#include "foundation/PxTransform.h" +#include "foundation/PxBounds3.h" +#include "CmPhysXCommon.h" +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + +struct RayAABBTest +{ + PX_FORCE_INLINE RayAABBTest(const PxVec3& origin_, const PxVec3& unitDir_, const PxReal maxDist, const PxVec3& inflation_) + : mOrigin(V3LoadU(origin_)) + , mDir(V3LoadU(unitDir_)) + , mDirYZX(V3PermYZX(mDir)) + , mInflation(V3LoadU(inflation_)) + , mAbsDir(V3Abs(mDir)) + , mAbsDirYZX(V3PermYZX(mAbsDir)) + { + const PxVec3 ext = maxDist >= PX_MAX_F32 ? PxVec3( unitDir_.x == 0 ? origin_.x : PxSign(unitDir_.x)*PX_MAX_F32, + unitDir_.y == 0 ? origin_.y : PxSign(unitDir_.y)*PX_MAX_F32, + unitDir_.z == 0 ? origin_.z : PxSign(unitDir_.z)*PX_MAX_F32) + : origin_ + unitDir_ * maxDist; + mRayMin = V3Min(mOrigin, V3LoadU(ext)); + mRayMax = V3Max(mOrigin, V3LoadU(ext)); + } + + PX_FORCE_INLINE void setDistance(PxReal distance) + { + const Vec3V ext = V3ScaleAdd(mDir, FLoad(distance), mOrigin); + mRayMin = V3Min(mOrigin, ext); + mRayMax = V3Max(mOrigin, ext); + } + + template + PX_FORCE_INLINE PxU32 check(const Vec3V center, const Vec3V extents) const + { + const Vec3V iExt = TInflate ? V3Add(extents, mInflation) : extents; + + // coordinate axes + const Vec3V nodeMax = V3Add(center, iExt); + const Vec3V nodeMin = V3Sub(center, iExt); + + // cross axes + const Vec3V offset = V3Sub(mOrigin, center); + const Vec3V offsetYZX = V3PermYZX(offset); + const Vec3V iExtYZX = V3PermYZX(iExt); + + const Vec3V f = V3NegMulSub(mDirYZX, offset, V3Mul(mDir, offsetYZX)); + const Vec3V g = V3MulAdd(iExt, mAbsDirYZX, V3Mul(iExtYZX, mAbsDir)); + + const BoolV + maskA = V3IsGrtrOrEq(nodeMax, mRayMin), + maskB = V3IsGrtrOrEq(mRayMax, nodeMin), + maskC = V3IsGrtrOrEq(g, V3Abs(f)); + const BoolV andABCMasks = BAnd(BAnd(maskA, maskB), maskC); + + return BAllEqTTTT(andABCMasks); + } + + const Vec3V mOrigin, mDir, mDirYZX, mInflation, mAbsDir, mAbsDirYZX; + Vec3V mRayMin, mRayMax; +protected: + RayAABBTest& operator=(const RayAABBTest&); +}; + +// probably not worth having a SIMD version of this unless the traversal passes Vec3Vs +struct AABBAABBTest +{ + PX_FORCE_INLINE AABBAABBTest(const PxTransform&t, const PxBoxGeometry&b) + : mCenter(V3LoadU(t.p)) + , mExtents(V3LoadU(b.halfExtents)) + { } + + PX_FORCE_INLINE AABBAABBTest(const PxBounds3& b) + : mCenter(V3LoadU(b.getCenter())) + , mExtents(V3LoadU(b.getExtents())) + { } + + PX_FORCE_INLINE Ps::IntBool operator()(const Vec3V center, const Vec3V extents) const + { + //PxVec3 c; PxVec3_From_Vec3V(center, c); + //PxVec3 e; PxVec3_From_Vec3V(extents, e); + //if(PxAbs(c.x - mCenter.x) > mExtents.x + e.x) return Ps::IntFalse; + //if(PxAbs(c.y - mCenter.y) > mExtents.y + e.y) return Ps::IntFalse; + //if(PxAbs(c.z - mCenter.z) > mExtents.z + e.z) return Ps::IntFalse; + //return Ps::IntTrue; + return Ps::IntBool(V3AllGrtrOrEq(V3Add(mExtents, extents), V3Abs(V3Sub(center, mCenter)))); + } + +private: + AABBAABBTest& operator=(const AABBAABBTest&); + const Vec3V mCenter, mExtents; +}; + +struct SphereAABBTest +{ + PX_FORCE_INLINE SphereAABBTest(const PxTransform& t, const PxSphereGeometry& s) + : mCenter(V3LoadU(t.p)) + , mRadius2(FLoad(s.radius * s.radius)) + {} + + PX_FORCE_INLINE SphereAABBTest(const PxVec3& center, PxF32 radius) + : mCenter(V3LoadU(center)) + , mRadius2(FLoad(radius * radius)) + {} + + PX_FORCE_INLINE Ps::IntBool operator()(const Vec3V boxCenter, const Vec3V boxExtents) const + { + const Vec3V offset = V3Sub(mCenter, boxCenter); + const Vec3V closest = V3Clamp(offset, V3Neg(boxExtents), boxExtents); + const Vec3V d = V3Sub(offset, closest); + return Ps::IntBool(BAllEqTTTT(FIsGrtrOrEq(mRadius2, V3Dot(d, d)))); + } + +private: + SphereAABBTest& operator=(const SphereAABBTest&); + const Vec3V mCenter; + const FloatV mRadius2; +}; + +// The Opcode capsule-AABB traversal test seems to be *exactly* the same as the ray-box test inflated by the capsule radius (so not a true capsule/box test) +// and the code for the ray-box test is better. TODO: check the zero length case and use the sphere traversal if this one fails. +// (OTOH it's not that hard to adapt the Ray-AABB test to a capsule test) + +struct CapsuleAABBTest: private RayAABBTest +{ + PX_FORCE_INLINE CapsuleAABBTest(const PxVec3& origin, const PxVec3& unitDir, const PxReal length, const PxVec3& inflation) + : RayAABBTest(origin, unitDir, length, inflation) + {} + + PX_FORCE_INLINE Ps::IntBool operator()(const Vec3VArg center, const Vec3VArg extents) const + { + return Ps::IntBool(RayAABBTest::check(center, extents)); + } +}; + +template +struct OBBAABBTests +{ + OBBAABBTests(const PxVec3& pos, const PxMat33& rot, const PxVec3& halfExtentsInflated) + { + const Vec3V eps = V3Load(1e-6f); + + mT = V3LoadU(pos); + mExtents = V3LoadU(halfExtentsInflated); + + // storing the transpose matrices yields a simpler SIMD test + mRT = Mat33V_From_PxMat33(rot.getTranspose()); + mART = Mat33V(V3Add(V3Abs(mRT.col0), eps), V3Add(V3Abs(mRT.col1), eps), V3Add(V3Abs(mRT.col2), eps)); + mBB_xyz = M33TrnspsMulV3(mART, mExtents); + + if(fullTest) + { + const Vec3V eYZX = V3PermYZX(mExtents), eZXY = V3PermZXY(mExtents); + + mBB_123 = V3MulAdd(eYZX, V3PermZXY(mART.col0), V3Mul(eZXY, V3PermYZX(mART.col0))); + mBB_456 = V3MulAdd(eYZX, V3PermZXY(mART.col1), V3Mul(eZXY, V3PermYZX(mART.col1))); + mBB_789 = V3MulAdd(eYZX, V3PermZXY(mART.col2), V3Mul(eZXY, V3PermYZX(mART.col2))); + } + } + + // TODO: force inline it? + Ps::IntBool operator()(const Vec3V center, const Vec3V extents) const + { + const Vec3V t = V3Sub(mT, center); + + // class I - axes of AABB + if(V3OutOfBounds(t, V3Add(extents, mBB_xyz))) + return Ps::IntFalse; + + const Vec3V rX = mRT.col0, rY = mRT.col1, rZ = mRT.col2; + const Vec3V arX = mART.col0, arY = mART.col1, arZ = mART.col2; + + const FloatV eX = V3GetX(extents), eY = V3GetY(extents), eZ = V3GetZ(extents); + const FloatV tX = V3GetX(t), tY = V3GetY(t), tZ = V3GetZ(t); + + // class II - axes of OBB + { + const Vec3V v = V3ScaleAdd(rZ, tZ, V3ScaleAdd(rY, tY, V3Scale(rX, tX))); + const Vec3V v2 = V3ScaleAdd(arZ, eZ, V3ScaleAdd(arY, eY, V3ScaleAdd(arX, eX, mExtents))); + if(V3OutOfBounds(v, v2)) + return Ps::IntFalse; + } + + if(!fullTest) + return Ps::IntTrue; + + // class III - edge cross products. Almost all OBB tests early-out with type I or type II, + // so early-outs here probably aren't useful (TODO: profile) + + const Vec3V va = V3NegScaleSub(rZ, tY, V3Scale(rY, tZ)); + const Vec3V va2 = V3ScaleAdd(arY, eZ, V3ScaleAdd(arZ, eY, mBB_123)); + const BoolV ba = BOr(V3IsGrtr(va, va2), V3IsGrtr(V3Neg(va2), va)); + + const Vec3V vb = V3NegScaleSub(rX, tZ, V3Scale(rZ, tX)); + const Vec3V vb2 = V3ScaleAdd(arX, eZ, V3ScaleAdd(arZ, eX, mBB_456)); + const BoolV bb = BOr(V3IsGrtr(vb, vb2), V3IsGrtr(V3Neg(vb2), vb)); + + const Vec3V vc = V3NegScaleSub(rY, tX, V3Scale(rX, tY)); + const Vec3V vc2 = V3ScaleAdd(arX, eY, V3ScaleAdd(arY, eX, mBB_789)); + const BoolV bc = BOr(V3IsGrtr(vc, vc2), V3IsGrtr(V3Neg(vc2), vc)); + + return Ps::IntBool(BAllEqFFFF(BOr(ba, BOr(bb,bc)))); + } + + Vec3V mExtents; // extents of OBB + Vec3V mT; // translation of OBB + Mat33V mRT; // transpose of rotation matrix of OBB + Mat33V mART; // transpose of mRT, padded by epsilon + + Vec3V mBB_xyz; // extents of OBB along coordinate axes + Vec3V mBB_123; // projections of extents onto edge-cross axes + Vec3V mBB_456; + Vec3V mBB_789; +}; + +typedef OBBAABBTests OBBAABBTest; + +} +} +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuBounds.cpp b/src/PhysX/physx/source/geomutils/src/GuBounds.cpp new file mode 100644 index 000000000..38ee4c779 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBounds.cpp @@ -0,0 +1,611 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBounds.h" +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxTriangleMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "GuInternal.h" +#include "CmUtils.h" +#include "GuConvexMesh.h" +#include "GuConvexMeshData.h" +#include "GuTriangleMesh.h" +#include "GuHeightFieldData.h" +#include "GuHeightField.h" +#include "PsFoundation.h" +#include "GuConvexUtilsInternal.h" +#include "GuBoxConversion.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxMat33& rot, const PxVec3& pos, const CenterExtentsPadded& bounds) +{ + c = rot.transform(bounds.mCenter) + pos; + ext = Cm::basisExtent(rot.column0, rot.column1, rot.column2, bounds.mExtents); +} + +// PT: this one may have duplicates in GuBV4_BoxSweep_Internal.h & GuBV4_Raycast.cpp +static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33Padded& mat_Padded) +{ + Vec4V ResV = V4Scale(V4LoadU(&mat_Padded.column0.x), V4GetX(p)); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat_Padded.column1.x), V4GetY(p))); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat_Padded.column2.x), V4GetZ(p))); + return ResV; +} + +static PX_FORCE_INLINE void transformNoEmptyTestV(Vec3p& c, Vec3p& ext, const PxMat33Padded& rot, const PxVec3& pos, const CenterExtentsPadded& bounds) +{ + const Vec4V boundsCenterV = V4LoadU(&bounds.mCenter.x); // PT: this load is safe since extents follow center in the class + + // PT: unfortunately we can't V4LoadU 'pos' directly (it can come directly from users!). So we have to live with this for now: + const Vec4V posV = Vec4V_From_Vec3V(V3LoadU(&pos.x)); + // PT: but eventually we'd like to use the "unsafe" version (e.g. by switching p&q in PxTransform), which would save 6 instructions on Win32 + const Vec4V cV = V4Add(multiply3x3V(boundsCenterV, rot), posV); +// const Vec4V cV = V4Add(multiply3x3V(boundsCenterV, rot), V4LoadU(&pos.x)); // ### unsafe + V4StoreU(cV, &c.x); + + // extended basis vectors + const Vec4V boundsExtentsV = V4LoadU(&bounds.mExtents.x); // PT: this load is safe since bounds are padded + const Vec4V c0V = V4Scale(V4LoadU(&rot.column0.x), V4GetX(boundsExtentsV)); + const Vec4V c1V = V4Scale(V4LoadU(&rot.column1.x), V4GetY(boundsExtentsV)); + const Vec4V c2V = V4Scale(V4LoadU(&rot.column2.x), V4GetZ(boundsExtentsV)); + + // find combination of base vectors that produces max. distance for each component = sum of abs() + Vec4V extentsV = V4Add(V4Abs(c0V), V4Abs(c1V)); + extentsV = V4Add(extentsV, V4Abs(c2V)); + V4StoreU(extentsV, &ext.x); +} + +static PX_FORCE_INLINE PxU32 isNonIdentity(const PxVec3& scale) +{ + #define IEEE_1_0 0x3f800000 //!< integer representation of 1.0 + const PxU32* binary = reinterpret_cast(&scale.x); + return (binary[0] - IEEE_1_0)|(binary[1] - IEEE_1_0)|(binary[2] - IEEE_1_0); +} + +// PT: please don't inline this one - 300+ lines of rarely used code +static void computeScaledMatrix(PxMat33Padded& rot, const PxMeshScale& scale) +{ + rot = rot * scale.toMat33(); +} + +static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxTransform& transform, const PxMeshScale& scale, const CenterExtentsPadded& bounds) +{ + PxMat33Padded rot(transform.q); + + if(isNonIdentity(scale.scale)) + computeScaledMatrix(rot, scale); + + transformNoEmptyTestV(c, ext, rot, transform.p, bounds); +} + +static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxVec3& pos, const PxMat33Padded& rot, const PxMeshScale& scale, const CenterExtentsPadded& bounds) +{ + if(scale.isIdentity()) + transformNoEmptyTest(c, ext, rot, pos, bounds); + else + transformNoEmptyTest(c, ext, rot * scale.toMat33(), pos, bounds); +} + +static void computeMeshBounds(const PxTransform& pose, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, const PxMeshScale& meshScale, Vec3p& origin, Vec3p& extent) +{ + transformNoEmptyTest(origin, extent, pose, meshScale, *localSpaceBounds); +} + +static void computePlaneBounds(PxBounds3& bounds, const PxTransform& pose, float contactOffset, float inflation) +{ + // PT: A plane is infinite, so usually the bounding box covers the whole world. + // Now, in particular cases when the plane is axis-aligned, we can take + // advantage of this to compute a smaller bounding box anyway. + + // PT: we use PX_MAX_BOUNDS_EXTENTS to be compatible with PxBounds3::setMaximal, + // and to make sure that the value doesn't collide with the BP's sentinels. + const PxF32 bigValue = PX_MAX_BOUNDS_EXTENTS; +// const PxF32 bigValue = 1000000.0f; + PxVec3 minPt = PxVec3(-bigValue, -bigValue, -bigValue); + PxVec3 maxPt = PxVec3(bigValue, bigValue, bigValue); + + const PxVec3 planeNormal = pose.q.getBasisVector0(); + const PxPlane plane(pose.p, planeNormal); + + const float nx = PxAbs(planeNormal.x); + const float ny = PxAbs(planeNormal.y); + const float nz = PxAbs(planeNormal.z); + const float epsilon = 1e-6f; + const float oneMinusEpsilon = 1.0f - epsilon; + if(nx>oneMinusEpsilon && ny0.0f) maxPt.x = -plane.d + contactOffset; + else minPt.x = plane.d - contactOffset; + } + else if(nxoneMinusEpsilon && nz0.0f) maxPt.y = -plane.d + contactOffset; + else minPt.y = plane.d - contactOffset; + } + else if(nxoneMinusEpsilon) + { + if(planeNormal.z>0.0f) maxPt.z = -plane.d + contactOffset; + else minPt.z = plane.d - contactOffset; + } + + // PT: it is important to compute the min/max form directly without going through the + // center/extents intermediate form. With PX_MAX_BOUNDS_EXTENTS, those back-and-forth + // computations destroy accuracy. + + // PT: inflation actually destroys the bounds really. We keep it to please UTs but this is broken (DE10595). + // (e.g. for SQ 1% of PX_MAX_BOUNDS_EXTENTS is still a huge number, effectively making the AABB infinite and defeating the point of the above computation) + if(inflation!=1.0f) + { + const PxVec3 c = (maxPt + minPt)*0.5f; + const PxVec3 e = (maxPt - minPt)*0.5f*inflation; + minPt = c - e; + maxPt = c + e; + } + + bounds.minimum = minPt; + bounds.maximum = maxPt; +} + +static PX_FORCE_INLINE void inflateBounds(PxBounds3& bounds, const Vec3p& origin, const Vec3p& extents, float contactOffset, float inflation) +{ + Vec4V extentsV = V4LoadU(&extents.x); + extentsV = V4Add(extentsV, V4Load(contactOffset)); + extentsV = V4Scale(extentsV, FLoad(inflation)); + + const Vec4V originV = V4LoadU(&origin.x); + const Vec4V minV = V4Sub(originV, extentsV); + const Vec4V maxV = V4Add(originV, extentsV); + + StoreBounds(bounds, minV, maxV); +} + +static PX_FORCE_INLINE Vec4V basisExtentV(const PxMat33Padded& basis, const PxVec3& extent, float offset, float inflation) +{ + // extended basis vectors + const Vec4V c0V = V4Scale(V4LoadU(&basis.column0.x), FLoad(extent.x)); + const Vec4V c1V = V4Scale(V4LoadU(&basis.column1.x), FLoad(extent.y)); + const Vec4V c2V = V4Scale(V4LoadU(&basis.column2.x), FLoad(extent.z)); + + // find combination of base vectors that produces max. distance for each component = sum of abs() + Vec4V extentsV = V4Add(V4Abs(c0V), V4Abs(c1V)); + extentsV = V4Add(extentsV, V4Abs(c2V)); + extentsV = V4Add(extentsV, V4Load(offset)); + extentsV = V4Scale(extentsV, FLoad(inflation)); + return extentsV; +} + +void Gu::computeBounds(PxBounds3& bounds, const PxGeometry& geometry, const PxTransform& pose, float contactOffset, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, float inflation) +{ + PX_ASSERT(contactOffset==0.0f || inflation==1.0f); + + // Box, Convex, Mesh and HeightField will compute local bounds and pose to world space. + // Sphere, Capsule & Plane will compute world space bounds directly. + + switch(geometry.getType()) + { + case PxGeometryType::eSPHERE: + { + PX_ASSERT(!localSpaceBounds); + + const PxSphereGeometry& shape = static_cast(geometry); + const PxVec3 extents((shape.radius+contactOffset)*inflation); + bounds.minimum = pose.p - extents; + bounds.maximum = pose.p + extents; + } + break; + + case PxGeometryType::ePLANE: + { + PX_ASSERT(!localSpaceBounds); + + computePlaneBounds(bounds, pose, contactOffset, inflation); + } + break; + + case PxGeometryType::eCAPSULE: + { + PX_ASSERT(!localSpaceBounds); + + const PxCapsuleGeometry& shape = static_cast(geometry); + const PxVec3 d = pose.q.getBasisVector0(); + PxVec3 extents; + for(PxU32 ax = 0; ax<3; ax++) + extents[ax] = (PxAbs(d[ax]) * shape.halfHeight + shape.radius + contactOffset)*inflation; + bounds.minimum = pose.p - extents; + bounds.maximum = pose.p + extents; + } + break; + + case PxGeometryType::eBOX: + { + PX_ASSERT(!localSpaceBounds); + + const PxBoxGeometry& shape = static_cast(geometry); + + const Vec3p origin(pose.p); + + const PxMat33Padded basis(pose.q); + + const Vec4V extentsV = basisExtentV(basis, shape.halfExtents, contactOffset, inflation); + + const Vec4V originV = V4LoadU(&origin.x); + const Vec4V minV = V4Sub(originV, extentsV); + const Vec4V maxV = V4Add(originV, extentsV); + + StoreBounds(bounds, minV, maxV); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& shape = static_cast(geometry); + const Gu::ConvexHullData& hullData = static_cast(shape.convexMesh)->getHull(); + + const bool useTightBounds = shape.meshFlags & PxConvexMeshGeometryFlag::eTIGHT_BOUNDS; + if(useTightBounds) + { + PxMat33Padded rot(pose.q); + + if(isNonIdentity(shape.scale.scale)) + computeScaledMatrix(rot, shape.scale); + + PxU32 nb = hullData.mNbHullVertices; + const PxVec3* v = hullData.getHullVertices(); + Vec4V minV; + Vec4V maxV; + + { + const Vec4V vertexV = multiply3x3V(V4LoadU(&v->x), rot); + v++; + + minV = vertexV; + maxV = vertexV; + nb--; + } + + while(nb--) + { + const Vec4V vertexV = multiply3x3V(V4LoadU(&v->x), rot); + v++; + + minV = V4Min(minV, vertexV); + maxV = V4Max(maxV, vertexV); + } + + const Vec4V offsetV = V4Load(contactOffset); + minV = V4Sub(minV, offsetV); + maxV = V4Add(maxV, offsetV); + + const Vec4V posV = Vec4V_From_Vec3V(V3LoadU(&pose.p.x)); + maxV = V4Add(maxV, posV); + minV = V4Add(minV, posV); + + // Inflation + { + const Vec4V centerV = V4Scale(V4Add(maxV, minV), FLoad(0.5f)); + const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), FLoad(0.5f*inflation)); + maxV = V4Add(centerV, extentsV); + minV = V4Sub(centerV, extentsV); + } + + StoreBounds(bounds, minV, maxV); + } + else + { + Vec3p origin, extents; + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &hullData.getPaddedBounds(), shape.scale, origin, extents); + + inflateBounds(bounds, origin, extents, contactOffset, inflation); + } + } + break; + + case PxGeometryType::eTRIANGLEMESH: + { + Vec3p origin, extents; + const PxTriangleMeshGeometry& shape = static_cast(geometry); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &static_cast(shape.triangleMesh)->getPaddedBounds(), shape.scale, origin, extents); + + inflateBounds(bounds, origin, extents, contactOffset, inflation); + } + break; + + case PxGeometryType::eHEIGHTFIELD: + { + const PxHeightFieldGeometry& shape = static_cast(geometry); + const PxMeshScale scale(PxVec3(shape.rowScale, shape.heightScale, shape.columnScale), PxQuat(PxIdentity)); + + if(!localSpaceBounds) + localSpaceBounds = &static_cast(shape.heightField)->getData().getPaddedBounds(); + + //Compute and inflate the bounds from the pose, scale and center/extents. + Vec3p origin, extents; + computeMeshBounds(pose, localSpaceBounds, scale, origin, extents); + inflateBounds(bounds, origin, extents, contactOffset, inflation); + } + break; + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + PX_ASSERT(0); + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::GeometryUnion::computeBounds: Unknown shape type."); + } + } +} + +// PT: TODO: refactor this with regular function +PxF32 Gu::computeBoundsWithCCDThreshold(Vec3p& origin, Vec3p& extent, const PxGeometry& geometry, const PxTransform& pose, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds) +{ + // Box, Convex, Mesh and HeightField will compute local bounds and pose to world space. + // Sphere, Capsule & Plane will compute world space bounds directly. + + const PxReal inSphereRatio = 0.75f; + + //The CCD thresholds are as follows: + //(1) sphere = inSphereRatio * radius + //(2) plane = inf (we never need CCD against this shape) + //(3) capsule = inSphereRatio * radius + //(4) box = inSphereRatio * (box minimum extent axis) + //(5) convex = inSphereRatio * convex in-sphere * min scale + //(6) triangle mesh = 0.f (polygons have 0 thickness) + //(7) heightfields = 0.f (polygons have 0 thickness) + + //The decision to enter CCD depends on the sum of the shapes' CCD thresholds. One of the 2 shapes must be a + //sphere/capsule/box/convex so the sum of the CCD thresholds will be non-zero. + + switch (geometry.getType()) + { + case PxGeometryType::eSPHERE: + { + PX_ASSERT(!localSpaceBounds); + + const PxSphereGeometry& shape = static_cast(geometry); + origin = pose.p; + extent = PxVec3(shape.radius, shape.radius, shape.radius); + return shape.radius*inSphereRatio; + } + case PxGeometryType::ePLANE: + { + PX_ASSERT(!localSpaceBounds); + + PxBounds3 bounds; + computePlaneBounds(bounds, pose, 0.0f, 1.0f); + origin = bounds.getCenter(); + extent = bounds.getExtents(); + return PX_MAX_REAL; + } + case PxGeometryType::eCAPSULE: + { + PX_ASSERT(!localSpaceBounds); + + const PxCapsuleGeometry& shape = static_cast(geometry); + origin = pose.p; + const PxVec3 d = pose.q.getBasisVector0(); + for(PxU32 ax = 0; ax<3; ax++) + extent[ax] = PxAbs(d[ax]) * shape.halfHeight + shape.radius; + return shape.radius * inSphereRatio; + } + + case PxGeometryType::eBOX: + { + PX_ASSERT(!localSpaceBounds); + + const PxBoxGeometry& shape = static_cast(geometry); + + const PxMat33 rot(pose.q); + extent = Cm::basisExtent(rot.column0, rot.column1, rot.column2, shape.halfExtents); + + origin = pose.p; + + return PxMin(PxMin(shape.halfExtents.x, shape.halfExtents.y), shape.halfExtents.z)*inSphereRatio; + } + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& shape = static_cast(geometry); + const Gu::ConvexHullData& hullData = static_cast(shape.convexMesh)->getHull(); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &hullData.getPaddedBounds(), shape.scale, origin, extent); + return PxMin(shape.scale.scale.z, PxMin(shape.scale.scale.x, shape.scale.scale.y)) * hullData.mInternal.mRadius * inSphereRatio; + } + + case PxGeometryType::eTRIANGLEMESH: + { + const PxTriangleMeshGeometry& shape = static_cast(geometry); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &static_cast(shape.triangleMesh)->getPaddedBounds(), shape.scale, origin, extent); + return 0.0f; + } + + case PxGeometryType::eHEIGHTFIELD: + { + const PxHeightFieldGeometry& shape = static_cast(geometry); + const PxMeshScale scale(PxVec3(shape.rowScale, shape.heightScale, shape.columnScale), PxQuat(PxIdentity)); + const Gu::HeightFieldData& data = static_cast(shape.heightField)->getData(); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &data.getPaddedBounds(), scale, origin, extent); + return 0.f; + } + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + PX_ASSERT(0); + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::GeometryUnion::computeBounds: Unknown shape type."); + } + } + return PX_MAX_REAL; +} + + +static PX_FORCE_INLINE void computeBoxExtentsAroundCapsule(PxVec3& extents, const PxCapsuleGeometry& capsuleGeom, float inflation) +{ + extents.x = (capsuleGeom.radius + capsuleGeom.halfHeight) * inflation; + extents.y = capsuleGeom.radius * inflation; + extents.z = capsuleGeom.radius * inflation; +} + +static const PxReal SQ_PRUNER_INFLATION = 1.01f; // pruner test shape inflation (not narrow phase shape) + +static void computeMeshBounds(const PxVec3& pos, const PxMat33Padded& rot, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, const PxMeshScale& meshScale, Vec3p& origin, Vec3p& extent) +{ + Ps::prefetchLine(localSpaceBounds); // PT: this one helps reducing L2 misses in transformNoEmptyTest + transformNoEmptyTest(origin, extent, pos, rot, meshScale, *localSpaceBounds); +} + +// PT: warning: this writes 4 bytes after the end of 'bounds'. Calling code must ensure it is safe to do so. +static PX_FORCE_INLINE void computeMinMaxBounds(PxBounds3* PX_RESTRICT bounds, const Vec3p& c, const Vec3p& e, float prunerInflation, float offset) +{ + const Vec4V extentsV = V4Scale(V4Add(V4LoadU(&e.x), V4Load(offset)), FLoad(prunerInflation)); + const Vec4V centerV = V4LoadU(&c.x); + const Vec4V minV = V4Sub(centerV, extentsV); + const Vec4V maxV = V4Add(centerV, extentsV); + V4StoreU(minV, &bounds->minimum.x); + V4StoreU(maxV, &bounds->maximum.x); +} + +ShapeData::ShapeData(const PxGeometry& g, const PxTransform& t, PxReal inflation) +{ + using namespace physx::shdfnd::aos; + + // PT: this cast to matrix is already done in GeometryUnion::computeBounds (e.g. for boxes). So we do it first, + // then we'll pass the matrix directly to computeBoundsShapeData, to avoid the double conversion. + const bool isOBB = PxAbs(t.q.w) < 0.999999f; + if(isOBB) + { + // PT: writes 4 bytes after 'rot' but it's safe since we then write 'center' just afterwards + buildFrom(mGuBox, t.q); + } + else + { + mGuBox.rot = PxMat33(PxIdentity); + } + + // PT: can't use V4Load here since there's no guarantee on 't.p' + // PT: must store 'center' after 'rot' now + mGuBox.center = t.p; + + // Compute AABB, used by the BucketPruner as cullBox + switch(g.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& shape = static_cast(g); + computeMinMaxBounds(&mPrunerInflatedAABB, mGuBox.center, PxVec3(0.0f), SQ_PRUNER_INFLATION, shape.radius+inflation); + + // + + reinterpret_cast(mGuSphere) = Sphere(t.p, shape.radius); + } + break; + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& shape = static_cast(g); + const Vec3p extents = mGuBox.rot.column0.abs() * shape.halfHeight; + computeMinMaxBounds(&mPrunerInflatedAABB, mGuBox.center, extents, SQ_PRUNER_INFLATION, shape.radius+inflation); + + // + + Capsule& dstWorldCapsule = reinterpret_cast(mGuCapsule); // store a narrow phase version copy + getCapsule(dstWorldCapsule, shape, t); + + mGuBox.extents.x = shape.halfHeight; + + // compute PxBoxGeometry pruner geom around input capsule geom; transform remains unchanged + + computeBoxExtentsAroundCapsule(mPrunerBoxGeomExtents, shape, SQ_PRUNER_INFLATION); + } + break; + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& shape = static_cast(g); + // PT: cast is safe because 'rot' followed by other members + Vec4V extentsV = basisExtentV(static_cast(mGuBox.rot), shape.halfExtents, inflation, SQ_PRUNER_INFLATION); + + // PT: c/e-to-m/M conversion + const Vec4V centerV = V4LoadU(&mGuBox.center.x); + const Vec4V minV = V4Sub(centerV, extentsV); + const Vec4V maxV = V4Add(centerV, extentsV); + V4StoreU(minV, &mPrunerInflatedAABB.minimum.x); + V4StoreU(maxV, &mPrunerInflatedAABB.maximum.x); // PT: WARNING: writes past end of class + + // + + mGuBox.extents = shape.halfExtents; // PT: TODO: use SIMD + mPrunerBoxGeomExtents = shape.halfExtents*SQ_PRUNER_INFLATION; + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& shape = static_cast(g); + + const ConvexMesh* cm = static_cast(shape.convexMesh); + const ConvexHullData* hullData = &cm->getHull(); + + // PT: cast is safe since 'rot' is followed by other members of the box + Vec3p center, extents; + computeMeshBounds(mGuBox.center, static_cast(mGuBox.rot), &hullData->getPaddedBounds(), shape.scale, center, extents); + + computeMinMaxBounds(&mPrunerInflatedAABB, center, extents, SQ_PRUNER_INFLATION, inflation); + + // + + Box prunerBox; + computeOBBAroundConvex(prunerBox, shape, cm, t); + mGuBox.rot = prunerBox.rot; // PT: TODO: optimize this copy + + // AP: pruners are now responsible for growing the OBB by 1% for overlap/sweep/GJK accuracy + mPrunerBoxGeomExtents = prunerBox.extents*SQ_PRUNER_INFLATION; + mGuBox.center = prunerBox.center; + } + break; + + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("PhysX internal error: Invalid shape in ShapeData contructor."); + } + + // PT: WARNING: these writes must stay after the above code + mIsOBB = PxU32(isOBB); + mType = PxU16(g.getType()); +} + + + + diff --git a/src/PhysX/physx/source/geomutils/src/GuBounds.h b/src/PhysX/physx/source/geomutils/src/GuBounds.h new file mode 100644 index 000000000..6e8293f05 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBounds.h @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BOUNDS_H +#define GU_BOUNDS_H + +#include "foundation/PxBounds3.h" +#include "foundation/PxFlags.h" +#include "GuSIMDHelpers.h" +#include +#include "PxGeometry.h" +#include "GuBox.h" +#include "GuCenterExtents.h" +#include "GuSphere.h" +#include "GuCapsule.h" + +namespace physx +{ +class PxGeometry; + +namespace Gu +{ + +//For spheres, planes, capsules and boxes just set localSpaceBounds to NULL. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to the relevant pointer if it has already been pre-fetched. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to NULL if it has not already been pre-fetched. computeBounds will synchronously +//prefetch the local space bounds if localSpaceBounds is NULL. +//'contactOffset' and 'inflation' should not be used at the same time, i.e. either contactOffset==0.0f, or inflation==1.0f +PX_PHYSX_COMMON_API void computeBounds(PxBounds3& bounds, const PxGeometry& geometry, const PxTransform& transform, float contactOffset, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, float inflation); //AABB in world space. + +//For spheres, planes, capsules and boxes just set localSpaceBounds to NULL. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to the relevant pointer if it has not already been pre-fetched. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to NULL if it has not already been pre-fetched. computeBounds will synchronously +//prefetch the local space bounds if localSpaceBounds is NULL. +PX_PHYSX_COMMON_API PxF32 computeBoundsWithCCDThreshold(Vec3p& origin, Vec3p& extent, const PxGeometry& geometry, const PxTransform& transform, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds); //AABB in world space. + + +PX_FORCE_INLINE PxBounds3 computeBounds(const PxGeometry& geometry, const PxTransform& pose) +{ + PxBounds3 bounds; + computeBounds(bounds, geometry, pose, 0.0f, NULL, 1.0f); + return bounds; +} + +class ShapeData +{ +public: + + PX_PHYSX_COMMON_API ShapeData(const PxGeometry& g, const PxTransform& t, PxReal inflation); + + // PT: used by overlaps (box, capsule, convex) + PX_FORCE_INLINE const PxVec3& getPrunerBoxGeomExtentsInflated() const { return mPrunerBoxGeomExtents; } + + // PT: used by overlaps (box, capsule, convex) + PX_FORCE_INLINE const PxVec3& getPrunerWorldPos() const { return mGuBox.center; } + + PX_FORCE_INLINE const PxBounds3& getPrunerInflatedWorldAABB() const { return mPrunerInflatedAABB; } + + // PT: used by overlaps (box, capsule, convex) + PX_FORCE_INLINE const PxMat33& getPrunerWorldRot33() const { return mGuBox.rot; } + + // PT: this one only used by overlaps so far (for sphere shape, pruner level) + PX_FORCE_INLINE const Gu::Sphere& getGuSphere() const + { + PX_ASSERT(mType == PxGeometryType::eSPHERE); + return reinterpret_cast(mGuSphere); + } + + // PT: this one only used by sweeps so far (for box shape, NP level) + PX_FORCE_INLINE const Gu::Box& getGuBox() const + { + PX_ASSERT(mType == PxGeometryType::eBOX); + return mGuBox; + } + + // PT: this one used by sweeps (NP level) and overlaps (pruner level) - for capsule shape + PX_FORCE_INLINE const Gu::Capsule& getGuCapsule() const + { + PX_ASSERT(mType == PxGeometryType::eCAPSULE); + return reinterpret_cast(mGuCapsule); + } + + PX_FORCE_INLINE float getCapsuleHalfHeight() const + { + PX_ASSERT(mType == PxGeometryType::eCAPSULE); + return mGuBox.extents.x; + } + + PX_FORCE_INLINE PxU32 isOBB() const { return PxU32(mIsOBB); } + PX_FORCE_INLINE PxGeometryType::Enum getType() const { return PxGeometryType::Enum(mType); } + + PX_NOCOPY(ShapeData) +private: + + // PT: box: pre-inflated box extents + // capsule: pre-inflated extents of box-around-capsule + // convex: pre-inflated extents of box-around-convex + // sphere: not used + PxVec3 mPrunerBoxGeomExtents; // used for pruners. This volume encloses but can differ from the original shape + + // PT: + // + // box center = unchanged copy of initial shape's position, except for convex (position of box around convex) + // SIMD code will load it as a V4 (safe because member is not last of Gu structure) + // + // box rot = precomputed PxMat33 version of initial shape's rotation, except for convex (rotation of box around convex) + // SIMD code will load it as V4s (safe because member is not last of Gu structure) + // + // box extents = non-inflated initial box extents for box shape, half-height for capsule, otherwise not used + Gu::Box mGuBox; + + PxBounds3 mPrunerInflatedAABB; // precomputed AABB for the pruner shape + PxU16 mIsOBB; // true for OBB, false for AABB. Also used as padding for mPrunerInflatedAABB, don't move. + PxU16 mType; // shape's type + + // these union Gu shapes are only precomputed for narrow phase (not pruners), can be different from mPrunerVolume + // so need separate storage + union + { + PxU8 mGuCapsule[sizeof(Gu::Capsule)]; // 28 + PxU8 mGuSphere[sizeof(Gu::Sphere)]; // 16 + }; +}; + +// PT: please make sure it fits in "one" cache line +PX_COMPILE_TIME_ASSERT(sizeof(ShapeData)==128); + +} // namespace Gu + +} +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuBox.cpp b/src/PhysX/physx/source/geomutils/src/GuBox.cpp new file mode 100644 index 000000000..53c173708 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBox.cpp @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsIntrinsics.h" +#include "GuBoxConversion.h" +#include "GuCapsule.h" +#include "GuInternal.h" +#include "CmMatrix34.h" +#include "PsMathUtils.h" + +using namespace physx; + +void Gu::Box::create(const Gu::Capsule& capsule) +{ + // Box center = center of the two LSS's endpoints + center = capsule.computeCenter(); + + // Box orientation + const PxVec3 dir = capsule.p1 - capsule.p0; + const float d = dir.magnitude(); + if(d!=0.0f) + { + rot.column0 = dir / d; + Ps::computeBasis(rot.column0, rot.column1, rot.column2); + } + else + rot = PxMat33(PxIdentity); + + // Box extents + extents.x = capsule.radius + (d * 0.5f); + extents.y = capsule.radius; + extents.z = capsule.radius; +} + + +/** +Returns edges. +\return 24 indices (12 edges) indexing the list returned by ComputePoints() +*/ +const PxU8* Gu::getBoxEdges() +{ + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + static PxU8 Indices[] = { + 0, 1, 1, 2, 2, 3, 3, 0, + 7, 6, 6, 5, 5, 4, 4, 7, + 1, 5, 6, 2, + 3, 7, 4, 0 + }; + return Indices; +} + + +void Gu::computeOBBPoints(PxVec3* PX_RESTRICT pts, const PxVec3& center, const PxVec3& extents, const PxVec3& base0, const PxVec3& base1, const PxVec3& base2) +{ + PX_ASSERT(pts); + + // "Rotated extents" + const PxVec3 axis0 = base0 * extents.x; + const PxVec3 axis1 = base1 * extents.y; + const PxVec3 axis2 = base2 * extents.z; + + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + // Original code: 24 vector ops + /* pts[0] = box.center - Axis0 - Axis1 - Axis2; + pts[1] = box.center + Axis0 - Axis1 - Axis2; + pts[2] = box.center + Axis0 + Axis1 - Axis2; + pts[3] = box.center - Axis0 + Axis1 - Axis2; + pts[4] = box.center - Axis0 - Axis1 + Axis2; + pts[5] = box.center + Axis0 - Axis1 + Axis2; + pts[6] = box.center + Axis0 + Axis1 + Axis2; + pts[7] = box.center - Axis0 + Axis1 + Axis2;*/ + + // Rewritten: 12 vector ops + pts[0] = pts[3] = pts[4] = pts[7] = center - axis0; + pts[1] = pts[2] = pts[5] = pts[6] = center + axis0; + + PxVec3 tmp = axis1 + axis2; + pts[0] -= tmp; + pts[1] -= tmp; + pts[6] += tmp; + pts[7] += tmp; + + tmp = axis1 - axis2; + pts[2] += tmp; + pts[3] += tmp; + pts[4] -= tmp; + pts[5] -= tmp; +} + diff --git a/src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp b/src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp new file mode 100644 index 000000000..08751a946 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp @@ -0,0 +1,440 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTests.h" +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "PxSphereGeometry.h" +#include "GuDistanceSegmentBox.h" +#include "GuDistancePointBox.h" +#include "GuSweepBoxSphere.h" +#include "GuSweepCapsuleBox.h" +#include "GuSweepBoxBox.h" +#include "GuSweepBoxTriangle_SAT.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" +#include "PsVecMath.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace Ps::aos; + +static const bool gValidateBoxRadiusComputation = false; + +/////////////////////////////////////////// + +bool sweepCapsule_BoxGeom_Precise(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_UNUSED(inflation); + PX_UNUSED(capsulePose_); + PX_UNUSED(capsuleGeom_); + + const PxBoxGeometry& boxGeom = static_cast(geom); + + if (lss.p0 == lss.p1) // The capsule is actually a sphere + { + //TODO: Check if this is really faster than using a "sphere-aware" version of sweepCapsuleBox + + Box box; buildFrom(box, pose.p, boxGeom.halfExtents, pose.q); + if(!sweepBoxSphere(box, lss.radius, lss.p0, unitDir, distance, sweepHit.distance, sweepHit.normal, hitFlags)) + return false; + + sweepHit.normal = -sweepHit.normal; + sweepHit.flags = PxHitFlag::eNORMAL; + + if(hitFlags & PxHitFlag::ePOSITION && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + const PxVec3 newSphereCenter = lss.p0 + unitDir * sweepHit.distance; + PxVec3 closest; + const PxReal d = distancePointBoxSquared(newSphereCenter, box.center, box.extents, box.rot, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + closest = box.rotate(closest); + sweepHit.position = closest + box.center; + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + else + { + if(!sweepCapsuleBox(lss, pose, boxGeom.halfExtents, unitDir, distance, sweepHit.position, sweepHit.distance, sweepHit.normal, hitFlags)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + + if((hitFlags & PxHitFlag::ePOSITION) && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + Capsule movedCaps = lss; + movedCaps.p0 += unitDir * sweepHit.distance; + movedCaps.p1 += unitDir * sweepHit.distance; + + Box box; + buildFrom(box, pose.p, boxGeom.halfExtents, pose.q); + + PxVec3 closest; + const PxReal d = distanceSegmentBoxSquared(movedCaps, box, NULL, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + closest = pose.q.rotate(closest); + sweepHit.position = closest + pose.p; + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sweepBox_SphereGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + const PxSphereGeometry& sphereGeom = static_cast(geom); + + // PT: move to relative space + const Box relBox(box.center - pose.p, box.extents, box.rot); + + const PxReal sphereRadius = sphereGeom.radius + inflation; + + if(!sweepBoxSphere(relBox, sphereRadius, PxVec3(0), -unitDir, distance, sweepHit.distance, sweepHit.normal, hitFlags)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + + if((hitFlags & PxHitFlag::ePOSITION) && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + const PxVec3 motion = sweepHit.distance * unitDir; + const PxVec3 newSphereCenter = - motion; + PxVec3 closest; + const PxReal d = distancePointBoxSquared(newSphereCenter, relBox.center, relBox.extents, relBox.rot, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + sweepHit.position = relBox.rotate(closest) + box.center + motion; // PT: undo move to local space here + sweepHit.flags |= PxHitFlag::ePOSITION; + } + return true; +} + +bool sweepBox_CapsuleGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + PX_UNUSED(inflation); + PX_UNUSED(boxGeom_); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + // PT: move to relative space + const PxVec3 delta = box.center - pose.p; + Box relBox(delta, box.extents, box.rot); + + Capsule capsule; + const PxVec3 halfHeightVector = getCapsuleHalfHeightVector(pose, capsuleGeom); + capsule.p0 = halfHeightVector; + capsule.p1 = -halfHeightVector; + capsule.radius = capsuleGeom.radius; + + // PT: TODO: remove this. We convert to PxTansform here but inside sweepCapsuleBox we convert back to a matrix. + const PxTransform boxWorldPose(delta, boxPose_.q); + + PxVec3 n; + if(!sweepCapsuleBox(capsule, boxWorldPose, relBox.extents, -unitDir, distance, sweepHit.position, sweepHit.distance, n, hitFlags)) + return false; + + sweepHit.normal = -n; + sweepHit.flags = PxHitFlag::eNORMAL; + + if((hitFlags & PxHitFlag::ePOSITION) && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + relBox.center += (unitDir * sweepHit.distance); + PxVec3 closest; + const PxReal d = distanceSegmentBoxSquared(capsule, relBox, NULL, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + sweepHit.position = relBox.transform(closest) + pose.p; // PT: undo move to local space here + sweepHit.flags |= PxHitFlag::ePOSITION; + } + return true; +} + +bool sweepBox_BoxGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_UNUSED(inflation); + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + + const PxBoxGeometry& boxGeom = static_cast(geom); + + // PT: move to local space + const Box relBox(box.center - pose.p, box.extents, box.rot); + Box staticBox; buildFrom(staticBox, PxVec3(0), boxGeom.halfExtents, pose.q); + + if(!sweepBoxBox(relBox, staticBox, unitDir, distance, hitFlags, sweepHit)) + return false; + + if(sweepHit.distance!=0.0f) + sweepHit.position += pose.p; // PT: undo move to local space + return true; +} + +// PT: test: new version for CCT, based on code for general sweeps. Just to check it works or not with rotations +// TODO: refactor this and the similar code in sweptBox for box-vs-mesh. Not so easy though. +static bool sweepBoxVsTriangles(PxU32 nbTris, const PxTriangle* triangles, const Box& box, const PxVec3& unitDir, const PxReal distance, PxSweepHit& sweepHit, + PxHitFlags hitFlags, bool isDoubleSided, const PxU32* cachedIndex) +{ + if(!nbTris) + return false; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localMotion = localDir * distance; + + bool status = false; + sweepHit.distance = distance; //was PX_MAX_F32, but that may trigger an assert in the caller! + + const PxVec3 oneOverMotion( + localDir.x!=0.0f ? 1.0f/localMotion.x : 0.0f, + localDir.y!=0.0f ? 1.0f/localMotion.y : 0.0f, + localDir.z!=0.0f ? 1.0f/localMotion.z : 0.0f); + +// PT: experimental code, don't clean up before I test it more and validate it + +// Project box +/*float boxRadius0 = + PxAbs(dir.x) * box.extents.x + + PxAbs(dir.y) * box.extents.y + + PxAbs(dir.z) * box.extents.z;*/ + +float boxRadius = + PxAbs(localDir.x) * box.extents.x + + PxAbs(localDir.y) * box.extents.y + + PxAbs(localDir.z) * box.extents.z; + +if(gValidateBoxRadiusComputation) // PT: run this to check the box radius is correctly computed +{ + PxVec3 boxVertices2[8]; + box.computeBoxPoints(boxVertices2); + float dpmin = FLT_MAX; + float dpmax = -FLT_MAX; + for(int i=0;i<8;i++) + { + const float dp = boxVertices2[i].dot(unitDir); + if(dpdpmax) dpmax = dp; + } + const float goodRadius = (dpmax-dpmin)/2.0f; + PX_UNUSED(goodRadius); +} + +const float dpc0 = box.center.dot(unitDir); +float localMinDist = 1.0f; +#if PX_DEBUG + PxU32 totalTestsExpected = nbTris; + PxU32 totalTestsReal = 0; + PX_UNUSED(totalTestsExpected); + PX_UNUSED(totalTestsReal); +#endif + + const PxU32 idx = cachedIndex ? *cachedIndex : 0; + + PxVec3 bestTriNormal(0.0f); + + for(PxU32 ii=0;ii(geom); + + // Compute swept box + Box sweptBox; + computeSweptBox(sweptBox, box.extents, box.center, box.rot, unitDir, distance); + + //### Temp hack until we can directly collide the OBB against the HF + const PxTransform sweptBoxTR = sweptBox.getTransform(); + const PxBounds3 bounds = PxBounds3::poseExtent(sweptBoxTR, sweptBox.extents); + + sweepHit.distance = PX_MAX_F32; + + struct LocalReport : EntityReport + { + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + for(PxU32 i=0; igetTriangle(*mPose, currentTriangle, NULL, NULL, triangleIndex, true, true); + + PxSweepHit sweepHit_; + const bool b = sweepBoxVsTriangles(1, ¤tTriangle, mBox, mDir, mDist, sweepHit_, mHitFlags, mIsDoubleSided, NULL); + if(b && sweepHit_.distancedistance) + { + *mHit = sweepHit_; + mHit->faceIndex = triangleIndex; + mStatus = true; + } + } + return true; + } + + const HeightFieldUtil* mHFUtil; + const PxTransform* mPose; + PxSweepHit* mHit; + bool mStatus; + Box mBox; + PxVec3 mDir; + float mDist; + PxHitFlags mHitFlags; + bool mIsDoubleSided; + } myReport; + + HeightFieldUtil hfUtil(heightFieldGeom); + + myReport.mBox = box; + myReport.mDir = unitDir; + myReport.mDist = distance; + myReport.mHitFlags = hitFlags; + myReport.mHFUtil = &hfUtil; + myReport.mStatus = false; + myReport.mPose = &pose; + myReport.mHit = &sweepHit; + const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + myReport.mIsDoubleSided = (heightFieldGeom.heightFieldFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) || meshBothSides; + + hfUtil.overlapAABBTriangles(pose, bounds, GuHfQueryFlags::eWORLD_SPACE, &myReport); + + return myReport.mStatus; +} + +bool Gu::sweepBoxTriangles_Precise(GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxBoxGeometry)) +{ + PX_UNUSED(inflation); + + Box box; + buildFrom(box, pose.p, geom.halfExtents, pose.q); + + return sweepBoxVsTriangles(nbTris, triangles, box, unitDir, distance, hit, hitFlags, doubleSided, cachedIndex); +} diff --git a/src/PhysX/physx/source/geomutils/src/GuCapsule.cpp b/src/PhysX/physx/source/geomutils/src/GuCapsule.cpp new file mode 100644 index 000000000..f0572a9ea --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuCapsule.cpp @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "PsMathUtils.h" +#include "GuInternal.h" +#include "GuBox.h" +#include "GuCapsule.h" + +using namespace physx; + +/** +* Computes an OBB surrounding the capsule. +* \param box [out] the OBB +*/ +void Gu::computeBoxAroundCapsule(const Gu::Capsule& capsule, Gu::Box& box) +{ + // Box center = center of the two capsule's endpoints + box.center = capsule.computeCenter(); + + // Box extents + const PxF32 d = (capsule.p0 - capsule.p1).magnitude(); + box.extents.x = capsule.radius + (d * 0.5f); + box.extents.y = capsule.radius; + box.extents.z = capsule.radius; + + // Box orientation + if(d==0.0f) + { + box.rot = PxMat33(PxIdentity); + } + else + { + PxVec3 dir, right, up; + Ps::computeBasis(capsule.p0, capsule.p1, dir, right, up); + box.setAxes(dir, right, up); + } +} diff --git a/src/PhysX/physx/source/geomutils/src/GuCapsule.h b/src/PhysX/physx/source/geomutils/src/GuCapsule.h new file mode 100644 index 000000000..157bb9984 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuCapsule.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CAPSULE_H +#define GU_CAPSULE_H + +/** \addtogroup geomutils +@{ +*/ + +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + +/** +\brief Represents a capsule. +*/ + class Capsule : public Segment + { + public: + /** + \brief Constructor + */ + PX_INLINE Capsule() + { + } + + /** + \brief Constructor + + \param seg Line segment to create capsule from. + \param _radius Radius of the capsule. + */ + PX_INLINE Capsule(const Segment& seg, PxF32 _radius) : Segment(seg), radius(_radius) + { + } + + /** + \brief Constructor + + \param _p0 First segment point + \param _p1 Second segment point + \param _radius Radius of the capsule. + */ + PX_INLINE Capsule(const PxVec3& _p0, const PxVec3& _p1, PxF32 _radius) : Segment(_p0, _p1), radius(_radius) + { + } + + /** + \brief Destructor + */ + PX_INLINE ~Capsule() + { + } + + PxF32 radius; + }; +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuCenterExtents.h b/src/PhysX/physx/source/geomutils/src/GuCenterExtents.h new file mode 100644 index 000000000..f81e0a278 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuCenterExtents.h @@ -0,0 +1,131 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CENTER_EXTENTS_H +#define GU_CENTER_EXTENTS_H + +/** \addtogroup geomutils +@{ +*/ + +#include "CmMatrix34.h" +#include "CmUtils.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Gu +{ + class CenterExtents : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE CenterExtents() {} + PX_FORCE_INLINE CenterExtents(const PxBounds3& b) { mCenter = b.getCenter(); mExtents = b.getExtents(); } + PX_FORCE_INLINE ~CenterExtents() {} + + PX_FORCE_INLINE void getMin(PxVec3& min) const { min = mCenter - mExtents; } + PX_FORCE_INLINE void getMax(PxVec3& max) const { max = mCenter + mExtents; } + + PX_FORCE_INLINE float getMin(PxU32 axis) const { return mCenter[axis] - mExtents[axis]; } + PX_FORCE_INLINE float getMax(PxU32 axis) const { return mCenter[axis] + mExtents[axis]; } + + PX_FORCE_INLINE PxVec3 getMin() const { return mCenter - mExtents; } + PX_FORCE_INLINE PxVec3 getMax() const { return mCenter + mExtents; } + + PX_FORCE_INLINE void setMinMax(const PxVec3& min, const PxVec3& max) + { + mCenter = (max + min)*0.5f; + mExtents = (max - min)*0.5f; + } + + PX_FORCE_INLINE PxU32 isInside(const CenterExtents& box) const + { + if(box.getMin(0)>getMin(0)) return 0; + if(box.getMin(1)>getMin(1)) return 0; + if(box.getMin(2)>getMin(2)) return 0; + if(box.getMax(0)(geom); + if(!sphereGeom.isValid()) + return false; + break; + } + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + if(!capsuleGeom.isValid()) + return false; + break; + } + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast(geom); + if(!boxGeom.isValid()) + return false; + break; + } + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast(geom); + if(!convexGeom.isValid()) + return false; + break; + } + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + return true; +} + +bool PxGeometryQuery::sweep(const PxVec3& unitDir, const PxReal distance, + const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1, + PxSweepHit& sweepHit, PxHitFlags hitFlags, + const PxReal inflation) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "PxGeometryQuery::sweep(): pose0 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "PxGeometryQuery::sweep(): pose1 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "PxGeometryQuery::sweep(): unitDir is not valid.", false); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(distance), "PxGeometryQuery::sweep(): distance is not valid.", false); + PX_CHECK_AND_RETURN_VAL((distance >= 0.0f && !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) || distance > 0.0f, + "PxGeometryQuery::sweep(): sweep distance must be >=0 or >0 with eASSUME_NO_INITIAL_OVERLAP.", 0); +#if PX_CHECKED + if(!PxGeometryQuery::isValid(geom0)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry 0 is not valid"); + return false; + } + if(!PxGeometryQuery::isValid(geom1)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry 1 is not valid"); + return false; + } +#endif // PX_CHECKED + + const GeomSweepFuncs& sf = gGeomSweepFuncs; + + switch(geom0.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast(geom0); + + // PT: TODO: technically this capsule with 0.0 half-height is invalid ("isValid" returns false) + const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f); + + const Capsule worldCapsule(pose0.p, pose0.p, sphereGeom.radius); + + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()]; + + return func(geom1, pose1, capsuleGeom, pose0, worldCapsule, unitDir, distance, sweepHit, hitFlags, inflation); + } + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + + Capsule worldCapsule; + getCapsule(worldCapsule, capsuleGeom, pose0); + + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()]; + + return func(geom1, pose1, capsuleGeom, pose0, worldCapsule, unitDir, distance, sweepHit, hitFlags, inflation); + } + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast(geom0); + + Box box; + buildFrom(box, pose0.p, boxGeom.halfExtents, pose0.q); + + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepBoxFunc func = precise ? sf.preciseBoxMap[geom1.getType()] : sf.boxMap[geom1.getType()]; + + return func(geom1, pose1, boxGeom, pose0, box, unitDir, distance, sweepHit, hitFlags, inflation); + } + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + + const SweepConvexFunc func = sf.convexMap[geom1.getType()]; + + return func(geom1, pose1, convexGeom, pose0, unitDir, distance, sweepHit, hitFlags, inflation); + } + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_CHECK_MSG(false, "PxGeometryQuery::sweep(): first geometry object parameter must be sphere, capsule, box or convex geometry."); + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PxGeometryQuery::overlap( const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1) +{ + PX_SIMD_GUARD; + return Gu::overlap(geom0, pose0, geom1, pose1, gGeomOverlapMethodTable); +} + +/////////////////////////////////////////////////////////////////////////////// +PxU32 PxGeometryQuery::raycast( const PxVec3& rayOrigin, const PxVec3& rayDir, + const PxGeometry& geom, const PxTransform& pose, + PxReal maxDist, PxHitFlags hitFlags, + PxU32 maxHits, PxRaycastHit* PX_RESTRICT rayHits) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(rayDir.isFinite(), "PxGeometryQuery::raycast(): rayDir is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(rayOrigin.isFinite(), "PxGeometryQuery::raycast(): rayOrigin is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxGeometryQuery::raycast(): pose is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(maxDist >= 0.0f, "PxGeometryQuery::raycast(): maxDist is negative.", false); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(maxDist), "PxGeometryQuery::raycast(): maxDist is not valid.", false); + PX_CHECK_AND_RETURN_VAL(PxAbs(rayDir.magnitudeSquared()-1)<1e-4f, "PxGeometryQuery::raycast(): ray direction must be unit vector.", false); + + const RaycastFunc func = gRaycastMap[geom.getType()]; + return func(geom, pose, rayOrigin, rayDir, maxDist, hitFlags, maxHits, rayHits); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool pointConvexDistance(PxVec3& normal_, PxVec3& closestPoint_, PxReal& sqDistance, const PxVec3& pt, const ConvexMesh* convexMesh, const PxMeshScale& meshScale, const PxTransform& convexPose); + +PxReal PxGeometryQuery::pointDistance(const PxVec3& point, const PxGeometry& geom, const PxTransform& pose, PxVec3* closestPoint) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxGeometryQuery::pointDistance(): pose is not valid.", false); + + switch(geom.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast(geom); + + const PxReal r = sphereGeom.radius; + + PxVec3 delta = point - pose.p; + const PxReal d = delta.magnitude(); + if(d<=r) + return 0.0f; + + if(closestPoint) + { + delta /= d; + *closestPoint = pose.p + delta * r; + } + + return (d - r)*(d - r); + } + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsGeom = static_cast(geom); + + Capsule capsule; + getCapsule(capsule, capsGeom, pose); + + const PxReal r = capsGeom.radius; + + PxReal param; + const PxReal sqDistance = distancePointSegmentSquared(capsule, point, ¶m); + if(sqDistance<=r*r) + return 0.0f; + + const PxReal d = physx::intrinsics::sqrt(sqDistance); + + if(closestPoint) + { + const PxVec3 cp = capsule.getPointAt(param); + + PxVec3 delta = point - cp; + delta.normalize(); + + *closestPoint = cp + delta * r; + } + return (d - r)*(d - r); + } + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast(geom); + + Box obb; + buildFrom(obb, pose.p, boxGeom.halfExtents, pose.q); + + PxVec3 boxParam; + const PxReal sqDistance = distancePointBoxSquared(point, obb, &boxParam); + if(closestPoint && sqDistance!=0.0f) + { + *closestPoint = obb.transform(boxParam); + } + return sqDistance; + } + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast(geom); + + PxVec3 normal, cp; + PxReal sqDistance; + const bool intersect = pointConvexDistance(normal, cp, sqDistance, point, static_cast(convexGeom.convexMesh), convexGeom.scale, pose); + if(!intersect && closestPoint) + *closestPoint = cp; + return sqDistance; + } + case PxGeometryType::ePLANE: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_CHECK_MSG(false, "PxGeometryQuery::pointDistance(): geometry object parameter must be sphere, capsule box or convex geometry."); + break; + } + return -1.0f; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxBounds3 PxGeometryQuery::getWorldBounds(const PxGeometry& geom, const PxTransform& pose, float inflation) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxGeometryQuery::getWorldBounds(): pose is not valid.", PxBounds3::empty()); + + PxBounds3 bounds; + Gu::computeBounds(bounds, geom, pose, 0.0f, NULL, inflation); + PX_ASSERT(bounds.isValid()); + return bounds; +} + +/////////////////////////////////////////////////////////////////////////////// + +extern GeomMTDFunc gGeomMTDMethodTable[][PxGeometryType::eGEOMETRY_COUNT]; + +bool PxGeometryQuery::computePenetration( PxVec3& mtd, PxF32& depth, + const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "PxGeometryQuery::computePenetration(): pose0 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "PxGeometryQuery::computePenetration(): pose1 is not valid.", false); + + if(geom0.getType() > geom1.getType()) + { + GeomMTDFunc mtdFunc = gGeomMTDMethodTable[geom1.getType()][geom0.getType()]; + PX_ASSERT(mtdFunc); + if(!mtdFunc(mtd, depth, geom1, pose1, geom0, pose0)) + return false; + mtd = -mtd; + return true; + } + else + { + GeomMTDFunc mtdFunc = gGeomMTDMethodTable[geom0.getType()][geom1.getType()]; + PX_ASSERT(mtdFunc); + return mtdFunc(mtd, depth, geom0, pose0, geom1, pose1); + } +} diff --git a/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp new file mode 100644 index 000000000..69b74f74f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" + +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "GuHeightField.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +static PX_FORCE_INLINE Gu::ConvexMesh& getConvexMesh(PxConvexMesh* pxcm) +{ + return *static_cast(pxcm); +} + +static PX_FORCE_INLINE Gu::TriangleMesh& getTriangleMesh(PxTriangleMesh* pxtm) +{ + return *static_cast(pxtm); +} + +static PX_FORCE_INLINE Gu::HeightField& getHeightField(PxHeightField* pxhf) +{ + return *static_cast(pxhf); +} + +// PT: TODO: optimize all these data copies +void Gu::GeometryUnion::set(const PxGeometry& g) +{ + switch(g.getType()) + { + case PxGeometryType::eBOX: + { + reinterpret_cast(mGeometry) = static_cast(g); + } + break; + + case PxGeometryType::eCAPSULE: + { + reinterpret_cast(mGeometry) = static_cast(g); + } + break; + + case PxGeometryType::eSPHERE: + { + reinterpret_cast(mGeometry) = static_cast(g); + reinterpret_cast(mGeometry).halfHeight = 0.0f; //AM: make sphere geometry also castable as a zero height capsule. + } + break; + + case PxGeometryType::ePLANE: + { + reinterpret_cast(mGeometry) = static_cast(g); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + reinterpret_cast(mGeometry) = static_cast(g); + reinterpret_cast(mGeometry).hullData = &(::getConvexMesh(get().convexMesh).getHull()); + reinterpret_cast(mGeometry).gpuCompatible = ::getConvexMesh(get().convexMesh).isGpuCompatible(); + } + break; + + case PxGeometryType::eTRIANGLEMESH: + { + reinterpret_cast(mGeometry) = static_cast(g); + reinterpret_cast(mGeometry).meshData = &(::getTriangleMesh(get().triangleMesh)); + reinterpret_cast(mGeometry).materialIndices = (::getTriangleMesh(get().triangleMesh).getMaterials()); + reinterpret_cast(mGeometry).materials = MaterialIndicesStruct(); + } + break; + + case PxGeometryType::eHEIGHTFIELD: + { + reinterpret_cast(mGeometry) = static_cast(g); + reinterpret_cast(mGeometry).heightFieldData = &::getHeightField(get().heightField).getData(); + reinterpret_cast(mGeometry).materials = MaterialIndicesStruct(); + } + break; + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("geometry type not handled"); + break; + } +} + + + + diff --git a/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h new file mode 100644 index 000000000..621a2f8d1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h @@ -0,0 +1,246 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GEOMETRY_UNION_H +#define GU_GEOMETRY_UNION_H + +#include "foundation/PxBounds3.h" +#include "GuSIMDHelpers.h" +#include +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxTriangleMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "PsAllocator.h" +#include "GuBox.h" +#include "GuCenterExtents.h" +#include "GuSphere.h" +#include "GuCapsule.h" + +namespace physx +{ + +namespace Gu +{ + struct ConvexHullData; + class TriangleMesh; + struct HeightFieldData; + class GeometryUnion; +} + + +// +// Summary of our material approach: +// +// On the API level, materials are accessed via pointer. Internally we store indices into the material table. +// The material table is stored in the SDK and the materials are shared among scenes. To make this threadsafe, +// we have the following approach: +// +// - Every scene has a copy of the SDK master material table +// - At the beginning of a simulation step, the scene material table gets synced to the master material table. +// - While the simulation is running, the scene table does not get touched. +// - Each shape stores the indices of its material(s). When the simulation is not running and a user requests the +// materials of the shape, the indices are used to fetch the material from the master material table. When the +// the simulation is running then the same indices are used internally to fetch the materials from the scene +// material table. If a user changes the materials of a shape while the simulation is running, the index list +// will not get touched, instead the new materials get buffered and synced at the end of the simulation. +// - This whole scheme only works as long as the position of a material in the material table does not change +// when other materials get deleted/inserted. The data structure of the material table makes sure that is the case. +// + +struct PX_PHYSX_COMMON_API MaterialIndicesStruct +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +// PX_SERIALIZATION + MaterialIndicesStruct(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + MaterialIndicesStruct() : indices(NULL), numIndices(0) + { + } + + ~MaterialIndicesStruct() + { + } + + void allocate(PxU16 size) + { + indices = reinterpret_cast(PX_ALLOC(sizeof(PxU16) * size, "MaterialIndicesStruct::allocate")); + numIndices = size; + } + + void deallocate() + { + PX_FREE(indices); + numIndices = 0; + } + PxU16* indices; // the remap table for material index + PxU16 numIndices; // the size of the remap table + PxU16 pad; // pad for serialization +#if PX_P64_FAMILY + PxU32 pad64; // pad for serialization +#endif +}; + +struct PxConvexMeshGeometryLL: public PxConvexMeshGeometry +{ + const Gu::ConvexHullData* hullData; + bool gpuCompatible; +}; + +struct PxTriangleMeshGeometryLL: public PxTriangleMeshGeometry +{ + const Gu::TriangleMesh* meshData; + const PxU16* materialIndices; + MaterialIndicesStruct materials; +}; + +struct PxHeightFieldGeometryLL : public PxHeightFieldGeometry +{ + const Gu::HeightFieldData* heightFieldData; + MaterialIndicesStruct materials; +}; + +// We sometimes overload capsule code for spheres, so every sphere should have +// valid capsule data (height = 0). This is preferable to a typedef so that we +// can maintain traits separately for a sphere, but some care is required to deal +// with the fact that when a reference to a capsule is extracted, it may have its +// type field set to eSPHERE + +template +struct PxcGeometryTraits +{ + enum {TypeID = PxGeometryType::eINVALID }; +}; +template struct PxcGeometryTraits { enum { TypeID = PxcGeometryTraits::TypeID }; }; + +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eBOX }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eSPHERE }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eCAPSULE }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::ePLANE }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eCONVEXMESH }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eTRIANGLEMESH }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eHEIGHTFIELD }; }; +template PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType(const Gu::GeometryUnion& geometry); +template<> PX_CUDA_CALLABLE PX_INLINE void checkType(const Gu::GeometryUnion& geometry); +template<> PX_CUDA_CALLABLE PX_INLINE void checkType(const Gu::GeometryUnion& geometry); + + +namespace Gu +{ + +class InvalidGeometry : public PxGeometry +{ +public: + PX_CUDA_CALLABLE PX_FORCE_INLINE InvalidGeometry() : PxGeometry(PxGeometryType::eINVALID) {} +}; + +class PX_PHYSX_COMMON_API GeometryUnion +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + GeometryUnion(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PX_CUDA_CALLABLE PX_FORCE_INLINE GeometryUnion() { reinterpret_cast(mGeometry) = InvalidGeometry(); } + PX_CUDA_CALLABLE PX_FORCE_INLINE GeometryUnion(const PxGeometry& g) { set(g); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxGeometry& getGeometry() const { return reinterpret_cast(mGeometry); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxGeometryType::Enum getType() const { return reinterpret_cast(mGeometry).getType(); } + + PX_CUDA_CALLABLE void set(const PxGeometry& g); + + template PX_CUDA_CALLABLE PX_FORCE_INLINE Geom& get() + { + checkType(*this); + return reinterpret_cast(mGeometry); + } + + template PX_CUDA_CALLABLE PX_FORCE_INLINE const Geom& get() const + { + checkType(*this); + return reinterpret_cast(mGeometry); + } + +private: + + union { + void* alignment; // PT: Makes sure the class is at least aligned to pointer size. See DE6803. + PxU8 box[sizeof(PxBoxGeometry)]; + PxU8 sphere[sizeof(PxSphereGeometry)]; + PxU8 capsule[sizeof(PxCapsuleGeometry)]; + PxU8 plane[sizeof(PxPlaneGeometry)]; + PxU8 convex[sizeof(PxConvexMeshGeometryLL)]; + PxU8 mesh[sizeof(PxTriangleMeshGeometryLL)]; + PxU8 heightfield[sizeof(PxHeightFieldGeometryLL)]; + PxU8 invalid[sizeof(InvalidGeometry)]; + } mGeometry; +}; +} + + +template PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType(const Gu::GeometryUnion& geometry) +{ + PX_ASSERT(PxU32(geometry.getType()) == PxU32(PxcGeometryTraits::TypeID)); + PX_UNUSED(geometry); +} + +template<> PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType(const Gu::GeometryUnion& geometry) +{ + PX_ASSERT(geometry.getType() == PxGeometryType::eCAPSULE || geometry.getType() == PxGeometryType::eSPHERE); + PX_UNUSED(geometry); +} + +template<> PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType(const Gu::GeometryUnion& geometry) +{ + PX_ASSERT(geometry.getType()== PxGeometryType::eCAPSULE || geometry.getType() == PxGeometryType::eSPHERE); + PX_UNUSED(geometry); +} + +// the shape structure relies on punning capsules and spheres +PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(PxCapsuleGeometry, radius) == PX_OFFSET_OF(PxSphereGeometry, radius)); +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuInternal.cpp b/src/PhysX/physx/source/geomutils/src/GuInternal.cpp new file mode 100644 index 000000000..8cfca77c1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuInternal.cpp @@ -0,0 +1,159 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxBounds3.h" +#include "PsIntrinsics.h" +#include "GuInternal.h" +#include "GuBox.h" +#include "GuVecPlane.h" +#include "PsMathUtils.h" +#include "PxCapsuleGeometry.h" +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +using namespace physx; + +/** +Computes the aabb points. +\param pts [out] 8 box points +*/ +void Gu::computeBoxPoints(const PxBounds3& bounds, PxVec3* PX_RESTRICT pts) +{ + PX_ASSERT(pts); + + // Get box corners + const PxVec3& minimum = bounds.minimum; + const PxVec3& maximum = bounds.maximum; + + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + // Generate 8 corners of the bbox + pts[0] = PxVec3(minimum.x, minimum.y, minimum.z); + pts[1] = PxVec3(maximum.x, minimum.y, minimum.z); + pts[2] = PxVec3(maximum.x, maximum.y, minimum.z); + pts[3] = PxVec3(minimum.x, maximum.y, minimum.z); + pts[4] = PxVec3(minimum.x, minimum.y, maximum.z); + pts[5] = PxVec3(maximum.x, minimum.y, maximum.z); + pts[6] = PxVec3(maximum.x, maximum.y, maximum.z); + pts[7] = PxVec3(minimum.x, maximum.y, maximum.z); +} + +PxPlane Gu::getPlane(const PxTransform& pose) +{ + const PxVec3 n = pose.q.getBasisVector0(); + return PxPlane(n, -pose.p.dot(n)); +} + +void Gu::computeBoundsAroundVertices(PxBounds3& bounds, PxU32 nbVerts, const PxVec3* PX_RESTRICT verts) +{ + // PT: we can safely V4LoadU the first N-1 vertices. We must V3LoadU the last vertex, to make sure we don't read + // invalid memory. Since we have to special-case that last vertex anyway, we reuse that code to also initialize + // the minV/maxV values (bypassing the need for a 'setEmpty()' initialization). + + if(!nbVerts) + { + bounds.setEmpty(); + return; + } + + PxU32 nbSafe = nbVerts-1; + + // PT: read last (unsafe) vertex using V3LoadU, initialize minV/maxV + const Vec4V lastVertexV = Vec4V_From_Vec3V(V3LoadU(&verts[nbSafe].x)); + Vec4V minV = lastVertexV; + Vec4V maxV = lastVertexV; + + // PT: read N-1 first (safe) vertices using V4LoadU + while(nbSafe--) + { + const Vec4V vertexV = V4LoadU(&verts->x); + verts++; + + minV = V4Min(minV, vertexV); + maxV = V4Max(maxV, vertexV); + } + + StoreBounds(bounds, minV, maxV); +} + +void Gu::computeSweptBox(Gu::Box& dest, const PxVec3& extents, const PxVec3& center, const PxMat33& rot, const PxVec3& unitDir, const PxReal distance) +{ + PxVec3 R1, R2; + Ps::computeBasis(unitDir, R1, R2); + + PxReal dd[3]; + dd[0] = PxAbs(rot.column0.dot(unitDir)); + dd[1] = PxAbs(rot.column1.dot(unitDir)); + dd[2] = PxAbs(rot.column2.dot(unitDir)); + PxReal dmax = dd[0]; + PxU32 ax0=1; + PxU32 ax1=2; + if(dd[1]>dmax) + { + dmax=dd[1]; + ax0=0; + ax1=2; + } + if(dd[2]>dmax) + { + dmax=dd[2]; + ax0=0; + ax1=1; + } + if(dd[ax1] maxDistance) + return false; + + // PT: make it a relative epsilon to make sure it still works with large distances + distEpsilon *= PxMax(1.0f, PxMax(triImpactDistance, bestImpactDistance)); + + // If new distance is more than epsilon closer than old distance + if(triImpactDistance < bestImpactDistance - distEpsilon) + return true; + + // If new distance is no more than epsilon farther than oldDistance and "face is more opposing than previous" + if(triImpactDistance < bestImpactDistance+distEpsilon && triAlignmentValue < bestAlignmentValue) + return true; + + // If alignment value is the same, but the new triangle is closer than the best distance + if(triAlignmentValue == bestAlignmentValue && triImpactDistance < bestImpactDistance) + return true; + + // If initial overlap happens, keep the triangle + if(triImpactDistance == 0.0f) + return true; + + return false; + } + + #define StoreBounds(bounds, minV, maxV) \ + V4StoreU(minV, &bounds.minimum.x); \ + PX_ALIGN(16, PxVec4) max4; \ + V4StoreA(maxV, &max4.x); \ + bounds.maximum = PxVec3(max4.x, max4.y, max4.z); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuMTD.cpp b/src/PhysX/physx/source/geomutils/src/GuMTD.cpp new file mode 100644 index 000000000..599254609 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuMTD.cpp @@ -0,0 +1,1457 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuMTD.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentBox.h" + + +#include "GuVecBox.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuInternal.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuBoxConversion.h" +#include "GuGeometryUnion.h" +#include "GuShapeConvex.h" +#include "GuPCMShapeConvex.h" +#include "GuPCMContactGen.h" +#include "GuConvexMesh.h" +#include "GuGJK.h" + +#include "PsUtilities.h" +#include "PsVecTransform.h" +#include "PsMathUtils.h" +#include "PxMeshScale.h" +#include "PxConvexMeshGeometry.h" + +using namespace physx; +using namespace Gu; + +static PX_FORCE_INLINE PxF32 manualNormalize(PxVec3& mtd, const PxVec3& normal, PxReal lenSq) +{ + const PxF32 len = PxSqrt(lenSq); + + // We do a *manual* normalization to check for singularity condition + if(lenSq < 1e-6f) + mtd = PxVec3(1.0f, 0.0f, 0.0f); // PT: zero normal => pick up random one + else + mtd = normal * 1.0f / len; + + return len; +} + +static PX_FORCE_INLINE float validateDepth(float depth) +{ + // PT: penetration depth must always be positive or null, but FPU accuracy being what it is, we sometimes + // end up with very small, epsilon-sized negative depths. We clamp those to zero, since they don't indicate + // real bugs in the MTD functions. However anything larger than epsilon is wrong, and caught with an assert. + const float epsilon = 1.e-3f; + + //ML: because we are shrunking the shape in this moment, so the depth might be larger than eps, this condition is no longer valid + //PX_ASSERT(depth>=-epsilon); + PX_UNUSED(epsilon); + return PxMax(depth, 0.0f); +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: the function names should follow the order in which the PxGeometryTypes are listed, +// i.e. computeMTD_Type0Type1 with Type0<=Type1. This is to guarantee that the proper results +// (following the desired convention) are returned from the PxGeometryQuery-level call. + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_SphereSphere(PxVec3& mtd, PxF32& depth, const Sphere& sphere0, const Sphere& sphere1) +{ + const PxVec3 delta = sphere0.center - sphere1.center; + const PxReal d2 = delta.magnitudeSquared(); + const PxReal radiusSum = sphere0.radius + sphere1.radius; + + if(d2 > radiusSum*radiusSum) + return false; + + const PxF32 d = manualNormalize(mtd, delta, d2); + + depth = validateDepth(radiusSum - d); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_SphereCapsule(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const Capsule& capsule) +{ + const PxReal radiusSum = sphere.radius + capsule.radius; + + PxReal u; + const PxReal d2 = distancePointSegmentSquared(capsule, sphere.center, &u); + + if(d2 > radiusSum*radiusSum) + return false; + + const PxVec3 normal = sphere.center - capsule.getPointAt(u); + + const PxReal lenSq = normal.magnitudeSquared(); + const PxF32 d = manualNormalize(mtd, normal, lenSq); + + depth = validateDepth(radiusSum - d); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + + +//This version is ported 1:1 from novodex +static PX_FORCE_INLINE bool ContactSphereBox(const PxVec3& sphereOrigin, + PxReal sphereRadius, + const PxVec3& boxExtents, +// const PxcCachedTransforms& boxCacheTransform, + const PxTransform& boxTransform, + PxVec3& point, + PxVec3& normal, + PxReal& separation, + PxReal contactDistance) +{ + + //returns true on contact + const PxVec3 delta = sphereOrigin - boxTransform.p; // s1.center - s2.center; + PxVec3 dRot = boxTransform.rotateInv(delta); //transform delta into OBB body coords. + + //check if delta is outside ABB - and clip the vector to the ABB. + bool outside = false; + + if (dRot.x < -boxExtents.x) + { + outside = true; + dRot.x = -boxExtents.x; + } + else if (dRot.x > boxExtents.x) + { + outside = true; + dRot.x = boxExtents.x; + } + + if (dRot.y < -boxExtents.y) + { + outside = true; + dRot.y = -boxExtents.y; + } + else if (dRot.y > boxExtents.y) + { + outside = true; + dRot.y = boxExtents.y; + } + + if (dRot.z < -boxExtents.z) + { + outside = true; + dRot.z =-boxExtents.z; + } + else if (dRot.z > boxExtents.z) + { + outside = true; + dRot.z = boxExtents.z; + } + + if (outside) //if clipping was done, sphere center is outside of box. + { + point = boxTransform.rotate(dRot); //get clipped delta back in world coords. + normal = delta - point; //what we clipped away. + const PxReal lenSquared = normal.magnitudeSquared(); + const PxReal inflatedDist = sphereRadius + contactDistance; + if (lenSquared > inflatedDist * inflatedDist) + return false; //disjoint + + //normalize to make it into the normal: + separation = PxRecipSqrt(lenSquared); + normal *= separation; + separation *= lenSquared; + //any plane that touches the sphere is tangential, so a vector from contact point to sphere center defines normal. + //we could also use point here, which has same direction. + //this is either a faceFace or a vertexFace contact depending on whether the box's face or vertex collides, but we did not distinguish. + //We'll just use vertex face for now, this info isn't really being used anyway. + //contact point is point on surface of cube closest to sphere center. + point += boxTransform.p; + separation -= sphereRadius; + return true; + } + else + { + //center is in box, we definitely have a contact. + PxVec3 locNorm; //local coords contact normal + + PxVec3 absdRot; + absdRot = PxVec3(PxAbs(dRot.x), PxAbs(dRot.y), PxAbs(dRot.z)); + PxVec3 distToSurface = boxExtents - absdRot; //dist from embedded center to box surface along 3 dimensions. + + //find smallest element of distToSurface + if (distToSurface.y < distToSurface.x) + { + if (distToSurface.y < distToSurface.z) + { + //y + locNorm = PxVec3(0.0f, dRot.y > 0.0f ? 1.0f : -1.0f, 0.0f); + separation = -distToSurface.y; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + else + { + if (distToSurface.x < distToSurface.z) + { + //x + locNorm = PxVec3(dRot.x > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f); + separation = -distToSurface.x; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + //separation so far is just the embedding of the center point; we still have to push out all of the radius. + point = sphereOrigin; + normal = boxTransform.rotate(locNorm); + separation -= sphereRadius; + return true; + } +} + +static bool computeMTD_SphereBox(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const Box& box) +{ + PxVec3 point; + if(!ContactSphereBox( sphere.center, sphere.radius, + box.extents, PxTransform(box.center, PxQuat(box.rot)), + point, mtd, depth, 0.0f)) + return false; + depth = validateDepth(-depth); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_CapsuleCapsule(PxVec3& mtd, PxF32& depth, const Capsule& capsule0, const Capsule& capsule1) +{ + PxReal s,t; + const PxReal d2 = distanceSegmentSegmentSquared(capsule0, capsule1, &s, &t); + + const PxReal radiusSum = capsule0.radius + capsule1.radius; + + if(d2 > radiusSum*radiusSum) + return false; + + const PxVec3 normal = capsule0.getPointAt(s) - capsule1.getPointAt(t); + + const PxReal lenSq = normal.magnitudeSquared(); + const PxF32 d = manualNormalize(mtd, normal, lenSq); + + depth = validateDepth(radiusSum - d); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + + +static PX_FORCE_INLINE void reorderMTD(PxVec3& mtd, const PxVec3& center0, const PxVec3& center1) +{ + const PxVec3 witness = center0 - center1; + if(mtd.dot(witness) < 0.0f) + mtd = -mtd; +} + +static PX_FORCE_INLINE void projectBox(PxReal& min, PxReal& max, const PxVec3& axis, const Box& box) +{ + const PxReal boxCen = box.center.dot(axis); + const PxReal boxExt = + PxAbs(box.rot.column0.dot(axis)) * box.extents.x + + PxAbs(box.rot.column1.dot(axis)) * box.extents.y + + PxAbs(box.rot.column2.dot(axis)) * box.extents.z; + + min = boxCen - boxExt; + max = boxCen + boxExt; +} + +static bool PxcTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, const Box& box, PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project box + PxReal Min1, Max1; + projectBox(Min1, Max1, axis, box); + + // Test projections + if(max0=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +static bool PxcCapsuleOBBOverlap3(const Segment& segment, PxReal radius, const Box& box, PxReal* t=NULL, PxVec3* pp=NULL) +{ + PxVec3 Sep(0.0f); + PxReal PenDepth = PX_MAX_REAL; + + // Test normals + for(PxU32 i=0;i<3;i++) + { + PxReal d; + if(!PxcTestAxis(box.rot[i], segment, radius, box, d)) + return false; + + if(d capsule.radius*capsule.radius) + return false; + + if(d2 != 0.0f) + { + // PT: the capsule segment doesn't intersect the box => distance-based version + const PxVec3 onSegment = capsule.getPointAt(t); + onBox = box.center + box.rot.transform(onBox); + + PxVec3 normal = onSegment - onBox; + PxReal normalLen = normal.magnitude(); + + if(normalLen != 0.0f) + { + normal *= 1.0f/normalLen; + + mtd = normal; + depth = validateDepth(capsule.radius - PxSqrt(d2)); + return true; + } + } + + // PT: the capsule segment intersects the box => penetration-based version + return PxcCapsuleOBBOverlap3(capsule, capsule.radius, box, &depth, &mtd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool PxcTestAxis(const PxVec3& axis, const Box& box0, const Box& box1, PxReal& depth) +{ + // Project box + PxReal min0, max0; + projectBox(min0, max0, axis, box0); + + // Project box + PxReal Min1, Max1; + projectBox(Min1, Max1, axis, box1); + + // Test projections + if(max0=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +static PX_FORCE_INLINE bool testBoxBoxAxis(PxVec3& mtd, PxF32& depth, const PxVec3& axis, const Box& box0, const Box& box1) +{ + PxF32 d; + if(!PxcTestAxis(axis, box0, box1, d)) + return false; + if(dgetHull(); + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + const ConvexHullV convexHull_(hullData, zeroV, vScale, vQuat, meshScale.isIdentity()); + + const PsMatTransformV aToB(convexPose.transformInv(transform0)); + + //const CapsuleV capsule(zeroV, zeroV, FZero());//this is a point + const CapsuleV capsule_(aToB.p, FZero());//this is a point + LocalConvex capsule(capsule_); + LocalConvex convexHull(convexHull_); + + status = gjk, LocalConvex >(capsule, convexHull, aToB.p, FMax(), closA, closB, normalV, dist); + } + + bool intersect = status == GJK_CONTACT; + if(intersect) + { + sqDistance = 0.0f; + } + else + { + const FloatV sqDist = FMul(dist, dist); + FStore(sqDist, &sqDistance); + V3StoreU(normalV, normal_); + V3StoreU(closB, closestPoint_); + + normal_ = convexPose.rotate(normal_); + closestPoint_ = convexPose.transform(closestPoint_); + } + + return intersect; +} + +static bool computeMTD_SphereConvex(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + PxReal d2; + const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + PxVec3 dummy; + if(!pointConvexDistance(mtd, dummy, d2, sphere.center, convexMesh, convexGeom.scale, convexPose)) + { + if(d2 > sphere.radius*sphere.radius) + return false; + + depth = validateDepth(sphere.radius - PxSqrt(d2)); + mtd = -mtd; + return true; + } + + // PT: if we reach this place, the sphere center touched the convex => switch to penetration-based code + PxU32 nbPolygons = convexMesh->getNbPolygonsFast(); + const HullPolygonData* polygons = convexMesh->getPolygons(); + const PxVec3 localSphereCenter = convexPose.transformInv(sphere.center); + PxReal dmax = -PX_MAX_F32; + while(nbPolygons--) + { + const HullPolygonData& polygon = *polygons++; + const PxF32 d = polygon.mPlane.distance(localSphereCenter); + if(d>dmax) + { + dmax = d; + mtd = convexPose.rotate(polygon.mPlane.n); + } + } + depth = validateDepth(sphere.radius - dmax); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +//ML : capsule will be in the local space of convexHullV +static bool internalComputeMTD_CapsuleConvex(const CapsuleV& capsule, const bool idtScale, ConvexHullV& convexHullV, const Ps::aos::PsTransformV& transf1, + Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) +{ + PolygonalData polyData; + getPCMConvexData(convexHullV, idtScale, polyData); + + PxU8 buff[sizeof(SupportLocalImpl)]; + + SupportLocal* map = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(static_cast(convexHullV), transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale)) : + static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(convexHullV, transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale))); + + return computeMTD(capsule, polyData, map, penetrationDepth, normal); +} + +static bool computeMTD_CapsuleConvex(PxVec3& mtd, PxF32& depth, const Capsule& capsule, const PxTransform& capsulePose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + const FloatV capsuleHalfHeight = FLoad(capsule.length()*0.5f); + const FloatV capsuleRadius = FLoad(capsule.radius); + + const Vec3V zeroV = V3Zero(); + // Convex mesh + const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + const ConvexHullData* hull = &convexMesh->getHull(); + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHullV(hull, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + //~Convex mesh + + + const QuatV q0 = QuatVLoadU(&capsulePose.q.x); + const Vec3V p0 = V3LoadU(&capsulePose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + Vec3V normal = zeroV; + FloatV penetrationDepth = FZero(); + + CapsuleV capsuleV(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + const bool idtScale = convexGeom.scale.isIdentity(); + bool hasContacts = internalComputeMTD_CapsuleConvex(capsuleV, idtScale, convexHullV, transf1, penetrationDepth, normal); + if(hasContacts) + { + FStore(penetrationDepth, &depth); + depth = validateDepth(depth); + V3StoreU(normal, mtd); + } + + return hasContacts; +} + +/////////////////////////////////////////////////////////////////////////////// +static bool internalComputeMTD_BoxConvex(const PxVec3 halfExtents, const BoxV& box, const bool idtScale, ConvexHullV& convexHullV, const Ps::aos::PsTransformV& transf0, const Ps::aos::PsTransformV& transf1, + Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) +{ + PolygonalData polyData0; + PCMPolygonalBox polyBox0(halfExtents); + polyBox0.getPolygonalData(&polyData0); + polyData0.mPolygonVertexRefs = gPCMBoxPolygonData; + + PolygonalData polyData1; + getPCMConvexData(convexHullV, idtScale, polyData1); + + Mat33V identity = M33Identity(); + SupportLocalImpl map0(box, transf0, identity, identity, true); + + PxU8 buff[sizeof(SupportLocalImpl)]; + + SupportLocal* map1 = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(static_cast(convexHullV), transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale)) : + static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(convexHullV, transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale))); + + return computeMTD(polyData0, polyData1, &map0, map1, penetrationDepth, normal); + +} + +static bool computeMTD_BoxConvex(PxVec3& mtd, PxF32& depth, const Box& box, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + const Vec3V zeroV = V3Zero(); + const PxTransform boxPose = box.getTransform(); + const Vec3V boxExtents = V3LoadU(box.extents); + BoxV boxV(zeroV, boxExtents); + + // Convex mesh + const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + const ConvexHullData* hull = &convexMesh->getHull(); + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHullV(hull, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + //~Convex mesh + + + const QuatV q0 = QuatVLoadU(&boxPose.q.x); + const Vec3V p0 = V3LoadU(&boxPose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + + Vec3V normal=zeroV; + FloatV penetrationDepth=FZero(); + + const bool idtScale = convexGeom.scale.isIdentity(); + bool hasContacts = internalComputeMTD_BoxConvex(box.extents, boxV, idtScale, convexHullV, transf0, transf1, penetrationDepth, normal); + if(hasContacts) + { + FStore(penetrationDepth, &depth); + depth = validateDepth(depth); + V3StoreU(normal, mtd); + } + + return hasContacts; + +} + + +static bool internalComputeMTD_ConvexConvex(const bool idtScale0, const bool idtScale1, ConvexHullV& convexHullV0, ConvexHullV& convexHullV1, const Ps::aos::PsTransformV& transf0, const Ps::aos::PsTransformV& transf1, + Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) +{ + PolygonalData polyData0, polyData1; + getPCMConvexData(convexHullV0, idtScale0, polyData0); + getPCMConvexData(convexHullV1, idtScale1, polyData1); + + PxU8 buff0[sizeof(SupportLocalImpl)]; + PxU8 buff1[sizeof(SupportLocalImpl)]; + + SupportLocal* map0 = (idtScale0 ? static_cast(PX_PLACEMENT_NEW(buff0, SupportLocalImpl)(static_cast(convexHullV0), transf0, convexHullV0.vertex2Shape, convexHullV0.shape2Vertex, idtScale0)) : + static_cast(PX_PLACEMENT_NEW(buff0, SupportLocalImpl)(convexHullV0, transf0, convexHullV0.vertex2Shape, convexHullV0.shape2Vertex, idtScale0))); + + SupportLocal* map1 = (idtScale1 ? static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(static_cast(convexHullV1), transf1, convexHullV1.vertex2Shape, convexHullV1.shape2Vertex, idtScale1)) : + static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(convexHullV1, transf1, convexHullV1.vertex2Shape, convexHullV1.shape2Vertex, idtScale1))); + + return computeMTD(polyData0, polyData1, map0, map1, penetrationDepth, normal); +} + +/////////////////////////////////////////////////////////////////////////////// +static bool computeMTD_ConvexConvex(PxVec3& mtd, PxF32& depth, const PxConvexMeshGeometry& convexGeom0, const PxTransform& convexPose0, const PxConvexMeshGeometry& convexGeom1, const PxTransform& convexPose1) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + // Convex mesh + const ConvexMesh* convexMesh0 = static_cast(convexGeom0.convexMesh); + const ConvexHullData* hull0 = &convexMesh0->getHull(); + const Vec3V vScale0 = V3LoadU_SafeReadW(convexGeom0.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat0 = QuatVLoadU(&convexGeom0.scale.rotation.x); + ConvexHullV convexHullV0(hull0, zeroV, vScale0, vQuat0, convexGeom0.scale.isIdentity()); + //~Convex mesh + + // Convex mesh + const ConvexMesh* convexMesh1 = static_cast(convexGeom1.convexMesh); + const ConvexHullData* hull1 = &convexMesh1->getHull(); + const Vec3V vScale1 = V3LoadU_SafeReadW(convexGeom1.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat1 = QuatVLoadU(&convexGeom1.scale.rotation.x); + ConvexHullV convexHullV1(hull1, zeroV, vScale1, vQuat1, convexGeom1.scale.isIdentity()); + //~Convex mesh + + const QuatV q0 = QuatVLoadU(&convexPose0.q.x); + const Vec3V p0 = V3LoadU(&convexPose0.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose1.q.x); + const Vec3V p1 = V3LoadU(&convexPose1.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + + Vec3V normal = zeroV; + FloatV penetrationDepth = FZero(); + + + const bool idtScale0 = convexGeom0.scale.isIdentity(); + const bool idtScale1 = convexGeom1.scale.isIdentity(); + + bool hasContacts = internalComputeMTD_ConvexConvex(idtScale0, idtScale1, convexHullV0, convexHullV1, transf0, transf1, penetrationDepth, normal); + + if(hasContacts) + { + FStore(penetrationDepth, &depth); + depth = validateDepth(depth); + V3StoreU(normal, mtd); + } + return hasContacts; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_SpherePlane(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const PxPlane& plane) +{ + const PxReal d = plane.distance(sphere.center); + if(d>sphere.radius) + return false; + + mtd = plane.n; + depth = validateDepth(sphere.radius - d); + return true; +} + +static bool computeMTD_PlaneBox(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const Box& box) +{ + PxVec3 pts[8]; + box.computeBoxPoints(pts); + + PxReal dmin = plane.distance(pts[0]); + for(PxU32 i=1;i<8;i++) + { + const PxReal d = plane.distance(pts[i]); + dmin = physx::intrinsics::selectMin(dmin, d); + } + if(dmin>0.0f) + return false; + + mtd = -plane.n; + depth = validateDepth(-dmin); + return true; +} + +static bool computeMTD_PlaneCapsule(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const Capsule& capsule) +{ + const PxReal d0 = plane.distance(capsule.p0); + const PxReal d1 = plane.distance(capsule.p1); + const PxReal dmin = physx::intrinsics::selectMin(d0, d1) - capsule.radius; + if(dmin>0.0f) + return false; + + mtd = -plane.n; + depth = validateDepth(-dmin); + return true; +} + +static bool computeMTD_PlaneConvex(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + PxU32 nbVerts = convexMesh->getNbVerts(); + const PxVec3* PX_RESTRICT verts = convexMesh->getVerts(); + + PxReal dmin = plane.distance(convexPose.transform(verts[0])); + for(PxU32 i=1;i0.0f) + return false; + + mtd = -plane.n; + depth = validateDepth(-dmin); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool processContacts(PxVec3& mtd, PxF32& depth, PxU32 nbContacts, const ContactPoint* contacts) +{ + if(nbContacts) + { + PxVec3 mn(0.0f), mx(0.0f); + for(PxU32 i=0; i(geom0); + const PxSphereGeometry& sphereGeom1 = static_cast(geom1); + + return computeMTD_SphereSphere(mtd, depth, Sphere(pose0.p, sphereGeom0.radius), Sphere(pose1.p, sphereGeom1.radius)); +} + +static bool GeomMTDCallback_SpherePlane(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::ePLANE); + PX_UNUSED(geom1); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + return computeMTD_SpherePlane(mtd, depth, Sphere(pose0.p, sphereGeom.radius), getPlane(pose1)); +} + +static bool GeomMTDCallback_SphereCapsule(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxCapsuleGeometry& capsuleGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose1, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_SphereCapsule(mtd, depth, Sphere(pose0.p, sphereGeom.radius), capsule); +} + +static bool GeomMTDCallback_SphereBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxBoxGeometry& boxGeom = static_cast(geom1); + + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return computeMTD_SphereBox(mtd, depth, Sphere(pose0.p, sphereGeom.radius), obb); +} + +static bool GeomMTDCallback_SphereConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + return computeMTD_SphereConvex(mtd, depth, Sphere(pose0.p, sphereGeom.radius), convexGeom, pose1); +} + +static bool GeomMTDCallback_SphereMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + return computeMTD_SphereMesh(mtd, depth, Sphere(pose0.p, sphereGeom.radius), meshGeom, pose1); +} + +static bool GeomMTDCallback_PlaneCapsule(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(geom0); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose1, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_PlaneCapsule(mtd, depth, getPlane(pose0), capsule); +} + +static bool GeomMTDCallback_PlaneBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(geom0); + + const PxBoxGeometry& boxGeom = static_cast(geom1); + + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return computeMTD_PlaneBox(mtd, depth, getPlane(pose0), obb); +} + +static bool GeomMTDCallback_PlaneConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + PX_UNUSED(geom0); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + return computeMTD_PlaneConvex(mtd, depth, getPlane(pose0), convexGeom, pose1); +} + +static bool GeomMTDCallback_CapsuleCapsule(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + + const PxCapsuleGeometry& capsuleGeom0 = static_cast(geom0); + const PxCapsuleGeometry& capsuleGeom1 = static_cast(geom1); + + Capsule capsule0; + getCapsuleSegment(pose0, capsuleGeom0, capsule0); + capsule0.radius = capsuleGeom0.radius; + + Capsule capsule1; + getCapsuleSegment(pose1, capsuleGeom1, capsule1); + capsule1.radius = capsuleGeom1.radius; + + return computeMTD_CapsuleCapsule(mtd, depth, capsule0, capsule1); +} + +static bool GeomMTDCallback_CapsuleBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxBoxGeometry& boxGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return computeMTD_CapsuleBox(mtd, depth, capsule, obb); +} + +static bool GeomMTDCallback_CapsuleConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_CapsuleConvex(mtd, depth, capsule, pose0, convexGeom, pose1); +} + +static bool GeomMTDCallback_CapsuleMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_CapsuleMesh(mtd, depth, capsule, meshGeom, pose1); +} + +static bool GeomMTDCallback_BoxBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + + const PxBoxGeometry& boxGeom0 = static_cast(geom0); + const PxBoxGeometry& boxGeom1 = static_cast(geom1); + + Box obb0; + buildFrom(obb0, pose0.p, boxGeom0.halfExtents, pose0.q); + + Box obb1; + buildFrom(obb1, pose1.p, boxGeom1.halfExtents, pose1.q); + + return computeMTD_BoxBox(mtd, depth, obb0, obb1); +} + +static bool GeomMTDCallback_BoxConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + Box obb; + buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); + + return computeMTD_BoxConvex(mtd, depth, obb, convexGeom, pose1); +} + +static bool GeomMTDCallback_BoxMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + Box obb; + buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); + + return computeMTD_BoxMesh(mtd, depth, obb, meshGeom, pose1); +} + +static bool GeomMTDCallback_ConvexConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxConvexMeshGeometry& convexGeom0 = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom1 = static_cast(geom1); + + return computeMTD_ConvexConvex(mtd, depth, convexGeom0, pose0, convexGeom1, pose1); +} + +static bool GeomMTDCallback_ConvexMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + return computeMTD_ConvexMesh(mtd, depth, convexGeom, pose0, meshGeom, pose1); +} + +static bool GeomMTDCallback_SphereHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast(geom1); + + const Sphere sphere(pose0.p, sphereGeom.radius); + + return computeMTD_SphereHeightField(mtd, depth, sphere, meshGeom, pose1); +} + +static bool GeomMTDCallback_CapsuleHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_CapsuleHeightField(mtd, depth, capsule, meshGeom, pose1); +} + +static bool GeomMTDCallback_BoxHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast(geom1); + + Box obb; + buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); + + return computeMTD_BoxHeightField(mtd, depth, obb, meshGeom, pose1); +} + +static bool GeomMTDCallback_ConvexHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast(geom1); + + return computeMTD_ConvexHeightField(mtd, depth, convexGeom, pose0, meshGeom, pose1); +} + +Gu::GeomMTDFunc gGeomMTDMethodTable[][PxGeometryType::eGEOMETRY_COUNT] = +{ + //PxGeometryType::eSPHERE + { + GeomMTDCallback_SphereSphere, //PxGeometryType::eSPHERE + GeomMTDCallback_SpherePlane, //PxGeometryType::ePLANE + GeomMTDCallback_SphereCapsule, //PxGeometryType::eCAPSULE + GeomMTDCallback_SphereBox, //PxGeometryType::eBOX + GeomMTDCallback_SphereConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_SphereMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_SphereHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + GeomMTDCallback_NotSupported, //PxGeometryType::ePLANE + GeomMTDCallback_PlaneCapsule, //PxGeometryType::eCAPSULE + GeomMTDCallback_PlaneBox, //PxGeometryType::eBOX + GeomMTDCallback_PlaneConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + GeomMTDCallback_CapsuleCapsule, //PxGeometryType::eCAPSULE + GeomMTDCallback_CapsuleBox, //PxGeometryType::eBOX + GeomMTDCallback_CapsuleConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_CapsuleMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_CapsuleHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + GeomMTDCallback_BoxBox, //PxGeometryType::eBOX + GeomMTDCallback_BoxConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_BoxMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_BoxHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + GeomMTDCallback_ConvexConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_ConvexMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_ConvexHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, +}; diff --git a/src/PhysX/physx/source/geomutils/src/GuMTD.h b/src/PhysX/physx/source/geomutils/src/GuMTD.h new file mode 100644 index 000000000..572ca9437 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuMTD.h @@ -0,0 +1,60 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MTD_H +#define GU_MTD_H + +#include "PxGeometry.h" + +namespace physx +{ +namespace Gu +{ + // PT: we use a define to be able to quickly change the signature of all MTD functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[out] mtd computed depenetration dir + // \param[out] depth computed depenetration depth + // \param[in] geom0 first geometry object + // \param[in] pose0 pose of first geometry object + // \param[in] geom1 second geometry object + // \param[in] pose1 pose of second geometry object + // \param[in] cache optional cached data for triggers + #define GU_MTD_FUNC_PARAMS PxVec3& mtd, PxF32& depth, \ + const PxGeometry& geom0, const PxTransform& pose0, \ + const PxGeometry& geom1, const PxTransform& pose1 + + // PT: function pointer for Geom-indexed MTD functions + // See GU_MTD_FUNC_PARAMS for function parameters details. + // \return true if an overlap was found, false otherwise + // \note depenetration vector D is equal to mtd * depth. It should be applied to the 1st object, to get out of the 2nd object. + typedef bool (*GeomMTDFunc) (GU_MTD_FUNC_PARAMS); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp new file mode 100644 index 000000000..bd422fc6c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp @@ -0,0 +1,702 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "GuMeshFactory.h" +#include "PxHeightFieldDesc.h" +#include "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" +#include "GuTriangleMeshRTree.h" +#include "GuConvexMesh.h" +#include "GuBVHStructure.h" +#include "GuHeightField.h" +#include "GuConvexMeshData.h" +#include "CmUtils.h" +#include "GuMeshData.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + +// PT: TODO: refactor all this with a dedicated container + +GuMeshFactory::GuMeshFactory() : + mTriangleMeshes (PX_DEBUG_EXP("mesh factory triangle mesh hash")), + mConvexMeshes (PX_DEBUG_EXP("mesh factory convex mesh hash")), + mHeightFields (PX_DEBUG_EXP("mesh factory height field hash")), + mBVHStructures (PX_DEBUG_EXP("BVH structure factory hash")), + mFactoryListeners (PX_DEBUG_EXP("FactoryListeners")) +{ +} + +GuMeshFactory::~GuMeshFactory() +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +template +static void releaseObjects(Ps::CoalescedHashSet& objects) +{ + while(objects.size()) + { + T* object = objects.getEntries()[0]; + PX_ASSERT(object->getRefCount()==1); + object->release(); + } +} + +void GuMeshFactory::release() +{ + // Release all objects in case the user didn't do it + releaseObjects(mTriangleMeshes); + releaseObjects(mConvexMeshes); + releaseObjects(mHeightFields); + releaseObjects(mBVHStructures); + + PX_DELETE(this); +} + +template +static void addToHash(Ps::CoalescedHashSet& hash, T* element, Ps::Mutex* mutex) +{ + if(!element) + return; + + if(mutex) + mutex->lock(); + + hash.insert(element); + + if(mutex) + mutex->unlock(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addTriangleMesh(TriangleMesh* np, bool lock) +{ + addToHash(mTriangleMeshes, np, lock ? &mTrackingMutex : NULL); +} + +PxTriangleMesh* GuMeshFactory::createTriangleMesh(TriangleMeshData& data) +{ + TriangleMesh* np; + + if(data.mType==PxMeshMidPhase::eBVH33) + { + PX_NEW_SERIALIZED(np, RTreeTriangleMesh)(*this, data); + } + else if(data.mType==PxMeshMidPhase::eBVH34) + { + PX_NEW_SERIALIZED(np, BV4TriangleMesh)(*this, data); + } + else return NULL; + + if(np) + addTriangleMesh(np); + + return np; +} + +// data injected by cooking lib for runtime cooking +PxTriangleMesh* GuMeshFactory::createTriangleMesh(void* data) +{ + return createTriangleMesh(*reinterpret_cast(data)); +} + +static TriangleMeshData* loadMeshData(PxInputStream& stream) +{ + // Import header + PxU32 version; + bool mismatch; + if(!readHeader('M', 'E', 'S', 'H', version, mismatch, stream)) + return NULL; + + PxU32 midphaseID = PxMeshMidPhase::eBVH33; // Default before version 14 + if(version>=14) // this refers to PX_MESH_VERSION + { + midphaseID = readDword(mismatch, stream); + } + + // Check if old (incompatible) mesh format is loaded + if (version <= 9) // this refers to PX_MESH_VERSION + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Loading triangle mesh failed: " + "Deprecated mesh cooking format. Please recook your mesh in a new cooking format."); + PX_ALWAYS_ASSERT_MESSAGE("Obsolete cooked mesh found. Mesh version has been updated, please recook your meshes."); + return NULL; + } + + // Import serialization flags + const PxU32 serialFlags = readDword(mismatch, stream); + + // Import misc values + if (version <= 12) // this refers to PX_MESH_VERSION + { + // convexEdgeThreshold was removed in 3.4.0 + readFloat(mismatch, stream); + } + + TriangleMeshData* data; + if(midphaseID==PxMeshMidPhase::eBVH33) + data = PX_NEW(RTreeTriangleData); + else if(midphaseID==PxMeshMidPhase::eBVH34) + data = PX_NEW(BV4TriangleData); + else return NULL; + + // Import mesh + PxVec3* verts = data->allocateVertices(readDword(mismatch, stream)); + const PxU32 nbTris = readDword(mismatch, stream); + bool force32 = (serialFlags & (IMSF_8BIT_INDICES|IMSF_16BIT_INDICES)) == 0; + + //ML: this will allocate CPU triangle indices and GPU triangle indices if we have GRB data built + void* tris = data->allocateTriangles(nbTris, force32, serialFlags & IMSF_GRB_DATA); + + stream.read(verts, sizeof(PxVec3)*data->mNbVertices); + if(mismatch) + { + for(PxU32 i=0;imNbVertices;i++) + { + flip(verts[i].x); + flip(verts[i].y); + flip(verts[i].z); + } + } + //TODO: stop support for format conversion on load!! + const PxU32 nbIndices = 3*data->mNbTriangles; + if(serialFlags & IMSF_8BIT_INDICES) + { + PxU8 x; + if(data->has16BitIndices()) + { + PxU16* tris16 = reinterpret_cast(tris); + for(PxU32 i=0;i(tris); + for(PxU32 i=0;ihas16BitIndices()) + { + PxU16* tris16 = reinterpret_cast(tris); + stream.read(tris16, nbIndices*sizeof(PxU16)); + if(mismatch) + { + for(PxU32 i=0;i(tris); + PxU16 x; + for(PxU32 i=0;ihas16BitIndices()) + { + PxU32 x; + PxU16* tris16 = reinterpret_cast(tris); + for(PxU32 i=0;i(tris); + stream.read(tris32, nbIndices*sizeof(PxU32)); + + if(mismatch) + { + for(PxU32 i=0;iallocateMaterials(); + stream.read(materials, sizeof(PxU16)*data->mNbTriangles); + if(mismatch) + { + for(PxU32 i=0;imNbTriangles;i++) + flip(materials[i]); + } + } + if(serialFlags & IMSF_FACE_REMAP) + { + PxU32* remap = data->allocateFaceRemap(); + readIndices(readDword(mismatch, stream), data->mNbTriangles, remap, stream, mismatch); + } + + if(serialFlags & IMSF_ADJACENCIES) + { + PxU32* adj = data->allocateAdjacencies(); + stream.read(adj, sizeof(PxU32)*data->mNbTriangles*3); + if(mismatch) + { + for(PxU32 i=0;imNbTriangles*3;i++) + flip(adj[i]); + } + } + + // PT: TODO better + if(midphaseID==PxMeshMidPhase::eBVH33) + { + if(!static_cast(data)->mRTree.load(stream, version, mismatch)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "RTree binary image load error."); + PX_DELETE(data); + return NULL; + } + } + else if(midphaseID==PxMeshMidPhase::eBVH34) + { + BV4TriangleData* bv4data = static_cast(data); + if(!bv4data->mBV4Tree.load(stream, mismatch)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV4 binary image load error."); + PX_DELETE(data); + return NULL; + } + + bv4data->mMeshInterface.setNbTriangles(nbTris); + bv4data->mMeshInterface.setNbVertices(data->mNbVertices); + if(data->has16BitIndices()) + bv4data->mMeshInterface.setPointers(NULL, reinterpret_cast(tris), verts); + else + bv4data->mMeshInterface.setPointers(reinterpret_cast(tris), NULL, verts); + bv4data->mBV4Tree.mMeshInterface = &bv4data->mMeshInterface; + } + else PX_ASSERT(0); + + // Import local bounds + data->mGeomEpsilon = readFloat(mismatch, stream); + readFloatBuffer(&data->mAABB.minimum.x, 6, mismatch, stream); + + PxU32 nb = readDword(mismatch, stream); + if(nb) + { + PX_ASSERT(nb==data->mNbTriangles); + data->allocateExtraTrigData(); + // No need to convert those bytes + stream.read(data->mExtraTrigData, nb*sizeof(PxU8)); + } + + if(serialFlags & IMSF_GRB_DATA) + { + PxU32 GRB_meshAdjVerticiesTotal = 0; + if(version < 15) + GRB_meshAdjVerticiesTotal = readDword(mismatch, stream); + + //read grb triangle indices + PX_ASSERT(data->mGRB_primIndices); + + if (serialFlags & IMSF_8BIT_INDICES) + { + PxU8 x; + if (data->has16BitIndices()) + { + PxU16* tris16 = reinterpret_cast(data->mGRB_primIndices); + for (PxU32 i = 0; i(data->mGRB_primIndices); + for (PxU32 i = 0; ihas16BitIndices()) + { + PxU16* tris16 = reinterpret_cast(data->mGRB_primIndices); + stream.read(tris16, nbIndices*sizeof(PxU16)); + if (mismatch) + { + for (PxU32 i = 0; i(data->mGRB_primIndices); + PxU16 x; + for (PxU32 i = 0; ihas16BitIndices()) + { + PxU32 x; + PxU16* tris16 = reinterpret_cast(data->mGRB_primIndices); + for (PxU32 i = 0; i(data->mGRB_primIndices); + stream.read(tris32, nbIndices*sizeof(PxU32)); + + if (mismatch) + { + for (PxU32 i = 0; imGRB_primAdjacencies = static_cast(PX_NEW(PxU32)[data->mNbTriangles*4]); + data->mGRB_faceRemap = PX_NEW(PxU32)[data->mNbTriangles]; + + stream.read(data->mGRB_primAdjacencies, sizeof(PxU32)*data->mNbTriangles*4); + if (version < 15) + { + //stream.read(data->mGRB_vertValency, sizeof(PxU32)*data->mNbVertices); + for (PxU32 i = 0; i < data->mNbVertices; ++i) + readDword(mismatch, stream); + //stream.read(data->mGRB_adjVertStart, sizeof(PxU32)*data->mNbVertices); + for (PxU32 i = 0; i < data->mNbVertices; ++i) + readDword(mismatch, stream); + //stream.read(data->mGRB_adjVertices, sizeof(PxU32)*GRB_meshAdjVerticiesTotal); + for (PxU32 i = 0; i < GRB_meshAdjVerticiesTotal; ++i) + readDword(mismatch, stream); + } + stream.read(data->mGRB_faceRemap, sizeof(PxU32)*data->mNbTriangles); + + if(mismatch) + { + for(PxU32 i=0;imNbTriangles*4;i++) + flip(reinterpret_cast(data->mGRB_primIndices)[i]); + + for(PxU32 i=0;imNbTriangles*4;i++) + flip(reinterpret_cast(data->mGRB_primAdjacencies)[i]); + } + + //read BV32 + data->mGRB_BV32Tree = PX_NEW(BV32Tree); + BV32Tree* bv32Tree = static_cast(data->mGRB_BV32Tree); + if (!bv32Tree->load(stream, mismatch)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV32 binary image load error."); + PX_DELETE(data); + return NULL; + } + } + + return data; +} + +PxTriangleMesh* GuMeshFactory::createTriangleMesh(PxInputStream& desc) +{ + TriangleMeshData* data = ::loadMeshData(desc); + if(!data) + return NULL; + PxTriangleMesh* m = createTriangleMesh(*data); + PX_DELETE(data); + return m; +} + +bool GuMeshFactory::removeTriangleMesh(PxTriangleMesh& m) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + TriangleMesh* gu = static_cast(&m); + bool found = mTriangleMeshes.erase(gu); + return found; +} + +PxU32 GuMeshFactory::getNbTriangleMeshes() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mTriangleMeshes.size(); +} + +PxU32 GuMeshFactory::getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mTriangleMeshes.getEntries(), mTriangleMeshes.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addConvexMesh(ConvexMesh* np, bool lock) +{ + addToHash(mConvexMeshes, np, lock ? &mTrackingMutex : NULL); +} + +// data injected by cooking lib for runtime cooking +PxConvexMesh* GuMeshFactory::createConvexMesh(void* data) +{ + return createConvexMesh(*reinterpret_cast(data)); +} + +PxConvexMesh* GuMeshFactory::createConvexMesh(Gu::ConvexHullData& data) +{ + Gu::ConvexMesh *np; + PX_NEW_SERIALIZED(np, Gu::ConvexMesh)(*this, data); + if (np) + addConvexMesh(np); + + return np; +} + +PxConvexMesh* GuMeshFactory::createConvexMesh(PxInputStream& desc) +{ + ConvexMesh* np; + PX_NEW_SERIALIZED(np, ConvexMesh); + if(!np) + return NULL; + + np->setMeshFactory(this); + + if(!np->load(desc)) + { + np->decRefCount(); + return NULL; + } + + addConvexMesh(np); + return np; +} + +bool GuMeshFactory::removeConvexMesh(PxConvexMesh& m) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + ConvexMesh* gu = static_cast(&m); + bool found = mConvexMeshes.erase(gu); + return found; +} + +PxU32 GuMeshFactory::getNbConvexMeshes() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mConvexMeshes.size(); +} + +PxU32 GuMeshFactory::getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mConvexMeshes.getEntries(), mConvexMeshes.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addHeightField(HeightField* np, bool lock) +{ + addToHash(mHeightFields, np, lock ? &mTrackingMutex : NULL); +} + +PxHeightField* GuMeshFactory::createHeightField(void* heightFieldMeshData) +{ + HeightField* np; + PX_NEW_SERIALIZED(np, HeightField)(*this, *reinterpret_cast(heightFieldMeshData)); + if(np) + addHeightField(np); + + return np; +} + +PxHeightField* GuMeshFactory::createHeightField(PxInputStream& stream) +{ + HeightField* np; + PX_NEW_SERIALIZED(np, HeightField)(this); + if(!np) + return NULL; + + if(!np->load(stream)) + { + np->decRefCount(); + return NULL; + } + + addHeightField(np); + return np; +} + +bool GuMeshFactory::removeHeightField(PxHeightField& hf) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + HeightField* gu = static_cast(&hf); + bool found = mHeightFields.erase(gu); + return found; +} + +PxU32 GuMeshFactory::getNbHeightFields() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mHeightFields.size(); +} + +PxU32 GuMeshFactory::getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mHeightFields.getEntries(), mHeightFields.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addFactoryListener( GuMeshFactoryListener& listener ) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mFactoryListeners.pushBack( &listener ); +} + +void GuMeshFactory::removeFactoryListener( GuMeshFactoryListener& listener ) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + for ( PxU32 idx = 0; idx < mFactoryListeners.size(); ++idx ) + { + if ( mFactoryListeners[idx] == &listener ) + { + mFactoryListeners.replaceWithLast( idx ); + --idx; + } + } +} + +void GuMeshFactory::notifyFactoryListener(const PxBase* base, PxType typeID) +{ + const PxU32 nbListeners = mFactoryListeners.size(); + for(PxU32 i=0; ionGuMeshFactoryBufferRelease(base, typeID); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addBVHStructure(BVHStructure* np, bool lock) +{ + addToHash(mBVHStructures, np, lock ? &mTrackingMutex : NULL); +} + +// data injected by cooking lib for runtime cooking +PxBVHStructure* GuMeshFactory::createBVHStructure(void* data) +{ + return createBVHStructure(*reinterpret_cast(data)); +} + +PxBVHStructure* GuMeshFactory::createBVHStructure(Gu::BVHStructureData& data) +{ + Gu::BVHStructure *np; + PX_NEW_SERIALIZED(np, Gu::BVHStructure)(this, data); + if (np) + addBVHStructure(np); + + return np; +} + +PxBVHStructure* GuMeshFactory::createBVHStructure(PxInputStream& desc) +{ + BVHStructure* np; + PX_NEW_SERIALIZED(np, BVHStructure)(this); + if(!np) + return NULL; + + if(!np->load(desc)) + { + np->decRefCount(); + return NULL; + } + + addBVHStructure(np); + return np; +} + +bool GuMeshFactory::removeBVHStructure(PxBVHStructure& m) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + BVHStructure* gu = static_cast(&m); + bool found = mBVHStructures.erase(gu); + return found; +} + +PxU32 GuMeshFactory::getNbBVHStructures() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mBVHStructures.size(); +} + +PxU32 GuMeshFactory::getBVHStructures(PxBVHStructure** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mBVHStructures.getEntries(), mBVHStructures.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + diff --git a/src/PhysX/physx/source/geomutils/src/GuMeshFactory.h b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.h new file mode 100644 index 000000000..e17ca335f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MESH_FACTORY_H +#define GU_MESH_FACTORY_H + +#include "foundation/PxIO.h" +#include "PxTriangleMesh.h" +#include "PxConvexMesh.h" +#include "PxHeightField.h" +#include "PxBVHStructure.h" + +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" +#include "PsMutex.h" +#include "PsArray.h" + +#include "PsUserAllocated.h" +#include "PsHashSet.h" + +namespace physx +{ + +class PxHeightFieldDesc; + +namespace Gu +{ + class ConvexMesh; + class HeightField; + class TriangleMesh; + class TriangleMeshData; + class BVHStructure; + struct ConvexHullData; + struct BVHStructureData; +} + +class GuMeshFactoryListener +{ +protected: + virtual ~GuMeshFactoryListener(){} +public: + virtual void onGuMeshFactoryBufferRelease(const PxBase* object, PxType type) = 0; +}; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif +class PX_PHYSX_COMMON_API GuMeshFactory : public Ps::UserAllocated +{ + PX_NOCOPY(GuMeshFactory) +public: + GuMeshFactory(); +protected: + virtual ~GuMeshFactory(); + +public: + void release(); + + // Triangle meshes + void addTriangleMesh(Gu::TriangleMesh* np, bool lock=true); + PxTriangleMesh* createTriangleMesh(PxInputStream& stream); + PxTriangleMesh* createTriangleMesh(void* triangleMeshData); + bool removeTriangleMesh(PxTriangleMesh&); + PxU32 getNbTriangleMeshes() const; + PxU32 getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Convexes + void addConvexMesh(Gu::ConvexMesh* np, bool lock=true); + PxConvexMesh* createConvexMesh(PxInputStream&); + PxConvexMesh* createConvexMesh(void* convexMeshData); + bool removeConvexMesh(PxConvexMesh&); + PxU32 getNbConvexMeshes() const; + PxU32 getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Heightfields + void addHeightField(Gu::HeightField* np, bool lock=true); + PxHeightField* createHeightField(void* heightFieldMeshData); + PxHeightField* createHeightField(PxInputStream&); + bool removeHeightField(PxHeightField&); + PxU32 getNbHeightFields() const; + PxU32 getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // BVHStructure + void addBVHStructure(Gu::BVHStructure* np, bool lock=true); + PxBVHStructure* createBVHStructure(PxInputStream&); + PxBVHStructure* createBVHStructure(void* bvhData); + bool removeBVHStructure(PxBVHStructure&); + PxU32 getNbBVHStructures() const; + PxU32 getBVHStructures(PxBVHStructure** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + void addFactoryListener( GuMeshFactoryListener& listener ); + void removeFactoryListener( GuMeshFactoryListener& listener ); + void notifyFactoryListener(const PxBase*, PxType typeID); + +protected: + + PxTriangleMesh* createTriangleMesh(Gu::TriangleMeshData& data); + PxConvexMesh* createConvexMesh(Gu::ConvexHullData& data); + PxBVHStructure* createBVHStructure(Gu::BVHStructureData& data); + + mutable Ps::Mutex mTrackingMutex; +private: + Ps::CoalescedHashSet mTriangleMeshes; + Ps::CoalescedHashSet mConvexMeshes; + Ps::CoalescedHashSet mHeightFields; + Ps::CoalescedHashSet mBVHStructures; + + Ps::Array mFactoryListeners; +}; +#if PX_VC + #pragma warning(pop) +#endif +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuMetaData.cpp b/src/PhysX/physx/source/geomutils/src/GuMetaData.cpp new file mode 100644 index 000000000..2b312630e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuMetaData.cpp @@ -0,0 +1,598 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "GuHeightField.h" +#include "GuConvexMeshData.h" +#include "GuBigConvexData2.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" +#include "GuTriangleMeshRTree.h" +#include "GuGeometryUnion.h" +#include "PsIntrinsics.h" +#include "CmPhysXCommon.h" +#include "PxMetaData.h" + +using namespace physx; +using namespace Ps; +using namespace Cm; +using namespace Gu; + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_Valency(PxOutputStream& stream) +{ +// 4 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Valency) + PX_DEF_BIN_METADATA_ITEM(stream, Valency, PxU16, mCount, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Valency, PxU16, mOffset, 0) +} + +static void getBinaryMetaData_BigConvexRawData(PxOutputStream& stream) +{ +// 24 bytes + PX_DEF_BIN_METADATA_CLASS(stream, BigConvexRawData) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU16, mSubdiv, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU16, mNbSamples, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU8, mSamples, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU32, mNbVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU32, mNbAdjVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, Valency, mValencies, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU8, mAdjacentVerts, PxMetaDataFlag::ePTR) +} + +void BigConvexData::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_Valency(stream); + getBinaryMetaData_BigConvexRawData(stream); + +// 28 bytes + PX_DEF_BIN_METADATA_CLASS(stream, BigConvexData) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexData, BigConvexRawData, mData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexData, void, mVBuffer, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mData.mSamples + // PT: can't use one array of PxU16 since we don't want to flip those bytes during conversion. + // PT: We only align the first array for DE1340, but the second one shouldn't be aligned since + // both are written as one unique block of memory. + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, PxU8, mData.mNbSamples, PX_SERIAL_ALIGN, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, PxU8, mData.mNbSamples, 0, 0) + + // mData.mValencies + // PT: same here, we must only align the first array + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, Valency, mData.mNbVerts, PX_SERIAL_ALIGN, 0) + PX_DEF_BIN_METADATA_EXTRA_ALIGN(stream, BigConvexData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, PxU8, mData.mNbAdjVerts, 0, 0) +} + +static void getBinaryMetaData_InternalObjectsData(PxOutputStream& stream) +{ +// 16 bytes + PX_DEF_BIN_METADATA_CLASS(stream, InternalObjectsData) + PX_DEF_BIN_METADATA_ITEM(stream, InternalObjectsData, PxReal, mRadius, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, InternalObjectsData, PxReal, mExtents, 0) +} + +static void getBinaryMetaData_HullPolygonData(PxOutputStream& stream) +{ +// 20 bytes + PX_DEF_BIN_METADATA_CLASS(stream, HullPolygonData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, HullPolygonData, PxReal, mPlane, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HullPolygonData, PxU16, mVRef8, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HullPolygonData, PxU8, mNbVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HullPolygonData, PxU8, mMinIndex, 0) +} + +static void getBinaryMetaData_ConvexHullData(PxOutputStream& stream) +{ +// 64 bytes + PX_DEF_BIN_METADATA_CLASS(stream, ConvexHullData) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxBounds3, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxVec3, mCenterOfMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, HullPolygonData, mPolygons, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, BigConvexRawData, mBigConvexRawData, PxMetaDataFlag::ePTR) + //ML: the most significant bit of mNbEdges is used to indicate whether we have grb data or not. However, we don't support grb data + //in serialization so we have to mask the most significant bit and force the contact gen run on CPU code path + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxU16, mNbEdges, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxU8, mNbHullVertices, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxU8, mNbPolygons, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, InternalObjectsData, mInternal, 0) +} + +void Gu::ConvexMesh::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_InternalObjectsData(stream); + getBinaryMetaData_HullPolygonData(stream); + getBinaryMetaData_ConvexHullData(stream); + BigConvexData::getBinaryMetaData(stream); + +// 136 bytes + PX_DEF_BIN_METADATA_VCLASS(stream,ConvexMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream,ConvexMesh, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream,ConvexMesh, RefCountable) + + // + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, ConvexHullData, mHullData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, PxU32, mNb, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, BigConvexData, mBigConvexData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, PxReal, mMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, PxMat33, mInertia, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, GuMeshFactory, mMeshFactory, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mHullData.mPolygons (Gu::HullPolygonData, PxVec3, PxU8*2, PxU8) + // PT: we only align the first array since the other ones are contained within it + + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, HullPolygonData, mHullData.mNbPolygons, PX_SERIAL_ALIGN, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxVec3, mHullData.mNbHullVertices, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbEdges, 0, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbEdges, 0, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbHullVertices, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbHullVertices, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbHullVertices, 0, 0) + + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mNb, 0, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_EXTRA_ALIGN(stream, ConvexMesh, 4) + // mBigConvexData + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, Gu::ConvexMesh, BigConvexData, mBigConvexData, PX_SERIAL_ALIGN) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PxHeightFieldSample(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxHeightFieldSample) + PX_DEF_BIN_METADATA_ITEM(stream, PxHeightFieldSample, PxI16, height, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxHeightFieldSample, PxBitAndByte, materialIndex0, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxHeightFieldSample, PxBitAndByte, materialIndex1, 0) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxBitAndByte, PxU8) +} + +static void getBinaryMetaData_HeightFieldData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxHeightFieldFlags, PxU16) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxHeightFieldFormat::Enum, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, HeightFieldData) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxBounds3, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxU32, rows, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxU32, columns, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, rowLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, colLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, nbColumns, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxHeightFieldSample, samples, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, convexEdgeThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxHeightFieldFlags, flags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxU16, paddAfterFlags, PxMetaDataFlag::ePADDING) +#endif + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxHeightFieldFormat::Enum, format, 0) +} + +void Gu::HeightField::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PxHeightFieldSample(stream); + getBinaryMetaData_HeightFieldData(stream); + + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxMaterialTableIndex, PxU16) + + PX_DEF_BIN_METADATA_VCLASS(stream, HeightField) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, HeightField, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, HeightField, RefCountable) + + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, HeightFieldData, mData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxU32, mSampleStride, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxU32, mNbSamples, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxReal, mMinHeight, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxReal, mMaxHeight, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxU32, mModifyCount, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, GuMeshFactory, mMeshFactory, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mData.samples + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, HeightField, PxHeightFieldSample, mNbSamples, PX_SERIAL_ALIGN, 0) // PT: ### try to remove mNbSamples later +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_RTreePage(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, RTreePage) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, minx, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, miny, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, minz, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, maxx, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, maxy, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, maxz, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxU32, ptrs, 0, RTREE_N) +} + +void RTree::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_RTreePage(stream); + +// 96 bytes + PX_DEF_BIN_METADATA_CLASS(stream, RTree) + + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mBoundsMin, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mBoundsMax, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mInvDiagonal, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mDiagonalScaler, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mPageSize, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mNumRootPages, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mNumLevels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mTotalNodes, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mTotalPages, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, RTreePage, mPages, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mPages + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream,RTree, RTreePage, mTotalPages, 128, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void SourceMesh::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, SourceMesh) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxU32, mNbVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxVec3, mVerts, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxU32, mNbTris, 0) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, void, mTriangles32, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, void, mTriangles16, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxU32, mRemap, PxMetaDataFlag::ePTR) +} + +static void getBinaryMetaData_BVDataPackedQ(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, QuantizedAABB) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxU16, mData[0].mExtents, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxI16, mData[0].mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxU16, mData[1].mExtents, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxI16, mData[1].mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxU16, mData[2].mExtents, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxI16, mData[2].mCenter, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, BVDataPackedQ) + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPackedQ, QuantizedAABB, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPackedQ, PxU32, mData, 0) +} + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE +static void getBinaryMetaData_BVDataPackedNQ(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, CenterExtents) + PX_DEF_BIN_METADATA_ITEM(stream, CenterExtents, PxVec3, mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, CenterExtents, PxVec3, mExtents, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, BVDataPackedNQ) + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPackedNQ, CenterExtents, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPackedNQ, PxU32, mData, 0) +} +#endif + +void BV4Tree::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_BVDataPackedQ(stream); +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + getBinaryMetaData_BVDataPackedNQ(stream); +#endif + PX_DEF_BIN_METADATA_CLASS(stream, LocalBounds) + PX_DEF_BIN_METADATA_ITEM(stream, LocalBounds, PxVec3, mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, LocalBounds, float, mExtentsMagnitude, 0) + +// 96 bytes + PX_DEF_BIN_METADATA_CLASS(stream, BV4Tree) + + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, void, mMeshInterface, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, LocalBounds, mLocalBounds, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxU32, mNbNodes, 0) + //PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, void, mNodes, PxMetaDataFlag::eEXTRA_DATA) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxU32, mInitData, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxVec3, mCenterOrMinCoeff, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxVec3, mExtentsOrMaxCoeff, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, bool, mUserAllocated, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, bool, mQuantized, 0) + PX_DEF_BIN_METADATA_ITEMS(stream, BV4Tree, bool, mPadding, PxMetaDataFlag::ePADDING, 2) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BV4Tree, BVDataPackedQ, mNbNodes, 16, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Gu::TriangleMesh::getBinaryMetaData(PxOutputStream& stream) +{ +// 320 => 304 => 272 => 256 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, TriangleMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, TriangleMesh, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, TriangleMesh, RefCountable) + +// 224 => 208 => 192 bytes + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mNbVertices, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mNbTriangles, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxVec3, mVertices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mTriangles, PxMetaDataFlag::ePTR) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxBounds3, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU8, mExtraTrigData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxReal, mGeomEpsilon, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU8, mFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU16, mMaterialIndices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mFaceRemap, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mAdjacencies, PxMetaDataFlag::ePTR) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mGRB_triIndices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mGRB_triAdjacencies, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mGRB_faceRemap, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mGRB_BV32Tree, PxMetaDataFlag::ePTR) + + + //------ Extra-data ------ + + // mVertices + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxVec3, mVertices, mNbVertices, 0, PX_SERIAL_ALIGN) + + // mTriangles + // PT: quite tricky here: we exported either an array of PxU16s or an array of PxU32s. We trick the converter by + // pretending we exported both, with the same control variable (m16BitIndices) but opposed control flags. Also there's + // no way to capture "mNumTriangles*3" using the macros, so we just pretend we exported 3 buffers instead of 1. + // But since in reality it's all the same buffer, only the first one is declared as aligned. + + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU16, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, 0, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU16, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU16, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU32, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, PxMetaDataFlag::eCONTROL_FLIP, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU32, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, PxMetaDataFlag::eCONTROL_FLIP, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU32, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, PxMetaDataFlag::eCONTROL_FLIP, 0) + + // mExtraTrigData + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU8, mExtraTrigData, mNbTriangles, 0, PX_SERIAL_ALIGN) + + // mMaterialIndices + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU16, mMaterialIndices, mNbTriangles, 0, PX_SERIAL_ALIGN) + + // mFaceRemap + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mFaceRemap, mNbTriangles, 0, PX_SERIAL_ALIGN) + + // mAdjacencies + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mAdjacencies, mNbTriangles, 0, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mAdjacencies, mNbTriangles, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mAdjacencies, mNbTriangles, 0, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, GuMeshFactory, mMeshFactory, PxMetaDataFlag::ePTR) + + +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, TriangleMesh, PxU32, mPaddingFromInternalMesh, PxMetaDataFlag::ePADDING) +#endif +} + +void Gu::RTreeTriangleMesh::getBinaryMetaData(PxOutputStream& stream) +{ + RTree::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, RTreeTriangleMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, RTreeTriangleMesh, TriangleMesh) + + PX_DEF_BIN_METADATA_ITEM(stream, RTreeTriangleMesh, RTree, mRTree, 0) +} + +void Gu::BV4TriangleMesh::getBinaryMetaData(PxOutputStream& stream) +{ + SourceMesh::getBinaryMetaData(stream); + BV4Tree::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, BV4TriangleMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, BV4TriangleMesh, TriangleMesh) + + PX_DEF_BIN_METADATA_ITEM(stream, BV4TriangleMesh, SourceMesh, mMeshInterface, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4TriangleMesh, BV4Tree, mBV4Tree, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void MaterialIndicesStruct::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, MaterialIndicesStruct) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU16, indices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU16, numIndices, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU16, pad, PxMetaDataFlag::ePADDING) +#if PX_P64_FAMILY + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU32, pad64, PxMetaDataFlag::ePADDING) +#endif + + //------ Extra-data ------ + // indices + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, MaterialIndicesStruct, PxU16, indices, numIndices, 0, PX_SERIAL_ALIGN) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Gu::GeometryUnion::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxGeometryType::Enum, PxU32) + + // The various PxGeometry classes are all public, so I can't really put the meta-data function in there. And then + // I can't access their protected members. So we use the same trick as for the ShapeContainer + class ShadowConvexMeshGeometry : public PxConvexMeshGeometryLL + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_TYPEDEF(stream_, PxConvexMeshGeometryFlags, PxU8) + + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowConvexMeshGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxMeshScale, scale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxConvexMesh, convexMesh, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxConvexMeshGeometryFlags, meshFlags, 0) + PX_DEF_BIN_METADATA_ITEMS(stream_, ShadowConvexMeshGeometry, PxU8, paddingFromFlags, PxMetaDataFlag::ePADDING, 3) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, ConvexHullData, hullData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, bool, gpuCompatible, 0) + } + }; + ShadowConvexMeshGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxConvexMeshGeometryLL, ShadowConvexMeshGeometry) + + ///////////////// + + class ShadowTriangleMeshGeometry : public PxTriangleMeshGeometryLL + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_TYPEDEF(stream_, PxMeshGeometryFlags, PxU8) + + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowTriangleMeshGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxMeshScale, scale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxMeshGeometryFlags, meshFlags, 0) + PX_DEF_BIN_METADATA_ITEMS(stream_, ShadowTriangleMeshGeometry, PxU8, paddingFromFlags, PxMetaDataFlag::ePADDING, 3) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxTriangleMesh, triangleMesh, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, TriangleMesh, meshData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxU16, materialIndices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, MaterialIndicesStruct, materials, 0) + } + }; + ShadowTriangleMeshGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream,PxTriangleMeshGeometryLL, ShadowTriangleMeshGeometry) + + ///////////////// + + class ShadowHeightFieldGeometry : public PxHeightFieldGeometryLL + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowHeightFieldGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxHeightField, heightField, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxReal, heightScale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxReal, rowScale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxReal, columnScale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxMeshGeometryFlags, heightFieldFlags, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream_, ShadowHeightFieldGeometry, PxU8, paddingFromFlags, PxMetaDataFlag::ePADDING) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, HeightField, heightFieldData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, MaterialIndicesStruct, materials, 0) + } + }; + ShadowHeightFieldGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream,PxHeightFieldGeometryLL, ShadowHeightFieldGeometry) + + ///////////////// + + class ShadowPlaneGeometry : public PxPlaneGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowPlaneGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowPlaneGeometry, PxGeometryType::Enum, mType, 0) + } + }; + ShadowPlaneGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream,PxPlaneGeometry, ShadowPlaneGeometry) + + ///////////////// + + class ShadowSphereGeometry : public PxSphereGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowSphereGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowSphereGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowSphereGeometry, PxReal, radius, 0) + } + }; + ShadowSphereGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxSphereGeometry, ShadowSphereGeometry) + + ///////////////// + + class ShadowCapsuleGeometry : public PxCapsuleGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowCapsuleGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowCapsuleGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowCapsuleGeometry, PxReal, radius, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowCapsuleGeometry, PxReal, halfHeight, 0) + } + }; + ShadowCapsuleGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxCapsuleGeometry, ShadowCapsuleGeometry) + + ///////////////// + + class ShadowBoxGeometry : public PxBoxGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowBoxGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowBoxGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowBoxGeometry, PxVec3, halfExtents,0) + } + }; + ShadowBoxGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxBoxGeometry, ShadowBoxGeometry) + + /* + - geom union offset & size + - control type offset & size + - type-to-class mapping + */ + +// 44 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Gu::GeometryUnion) + + PX_DEF_BIN_METADATA_UNION(stream, Gu::GeometryUnion, mGeometry) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxSphereGeometry, PxGeometryType::eSPHERE) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxPlaneGeometry, PxGeometryType::ePLANE) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxCapsuleGeometry, PxGeometryType::eCAPSULE) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxBoxGeometry, PxGeometryType::eBOX) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxConvexMeshGeometryLL, PxGeometryType::eCONVEXMESH) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxTriangleMeshGeometryLL,PxGeometryType::eTRIANGLEMESH) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxHeightFieldGeometryLL, PxGeometryType::eHEIGHTFIELD) +} diff --git a/src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp new file mode 100644 index 000000000..80fab6271 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp @@ -0,0 +1,695 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuOverlapTests.h" +#include "GuIntersectionBoxBox.h" +#include "GuIntersectionSphereBox.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentBox.h" +#include "GuDistanceSegmentSegment.h" +#include "GuSphere.h" +#include "GuBoxConversion.h" +#include "GuInternal.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecBox.h" +#include "GuConvexMesh.h" +#include "GuHillClimbing.h" +#include "GuGJK.h" + +using namespace physx; +using namespace Cm; +using namespace Gu; + +// PT: TODO: why don't we use ShapeData for overlaps? + +//returns the maximal vertex in shape space +// PT: this function should be removed. We already have 2 different project hull functions in PxcShapeConvex & GuGJKObjectSupport, this one looks like a weird mix of both! +static PxVec3 projectHull_( const ConvexHullData& hull, + float& minimum, float& maximum, + const PxVec3& localDir, // expected to be normalized + const PxMat33& vert2ShapeSkew) +{ + PX_ASSERT(localDir.isNormalized()); + + //use property that x|My == Mx|y for symmetric M to avoid having to transform vertices. + const PxVec3 vertexSpaceDir = vert2ShapeSkew * localDir; + + const PxVec3* Verts = hull.getHullVertices(); + const PxVec3* bestVert = NULL; + + if(!hull.mBigConvexRawData) // Brute-force, local space. Experiments show break-even point is around 32 verts. + { + PxU32 NbVerts = hull.mNbHullVertices; + float min_ = PX_MAX_F32; + float max_ = -PX_MAX_F32; + while(NbVerts--) + { + const float dp = (*Verts).dot(vertexSpaceDir); + min_ = physx::intrinsics::selectMin(min_, dp); + if(dp > max_) { max_ = dp; bestVert = Verts; } + + Verts++; + } + minimum = min_; + maximum = max_; + + PX_ASSERT(bestVert != NULL); + + return vert2ShapeSkew * *bestVert; + } + else //*/if(1) // This version is better for objects with a lot of vertices + { + const PxU32 Offset = ComputeCubemapNearestOffset(vertexSpaceDir, hull.mBigConvexRawData->mSubdiv); + PxU32 MinID = hull.mBigConvexRawData->mSamples[Offset]; + PxU32 MaxID = hull.mBigConvexRawData->getSamples2()[Offset]; + + localSearch(MinID, -vertexSpaceDir, Verts, hull.mBigConvexRawData); + localSearch(MaxID, vertexSpaceDir, Verts, hull.mBigConvexRawData); + + minimum = (Verts[MinID].dot(vertexSpaceDir)); + maximum = (Verts[MaxID].dot(vertexSpaceDir)); + + PX_ASSERT(maximum >= minimum); + + return vert2ShapeSkew * Verts[MaxID]; + } +} + +static bool intersectSphereConvex(const PxTransform& sphereTransform, float radius, const ConvexMesh& mesh, const PxMeshScale& meshScale, const PxTransform& convexGlobalPose, + PxVec3*) +{ + using namespace Ps::aos; + const Vec3V zeroV = V3Zero(); + const ConvexHullData* hullData = &mesh.getHull(); + const FloatV sphereRadius = FLoad(radius); + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + + const PsMatTransformV aToB(convexGlobalPose.transformInv(sphereTransform)); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, meshScale.isIdentity()); + CapsuleV capsule(aToB.p, sphereRadius); + + Vec3V contactA, contactB, normal; + FloatV dist; + LocalConvex convexA(capsule); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + + GjkStatus status = gjk(convexA, convexB, initialSearchDir, FZero(), contactA, contactB, normal, dist); + + return status == GJK_CONTACT; +} + +static bool intersectCapsuleConvex( const PxCapsuleGeometry& capsGeom, const PxTransform& capsGlobalPose, + const ConvexMesh& mesh, const PxMeshScale& meshScale, const PxTransform& convexGlobalPose, + PxVec3*) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + const ConvexHullData* hull = &mesh.getHull(); + + const FloatV capsuleHalfHeight = FLoad(capsGeom.halfHeight); + const FloatV capsuleRadius = FLoad(capsGeom.radius); + + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + + const PsMatTransformV aToB(convexGlobalPose.transformInv(capsGlobalPose)); + + ConvexHullV convexHull(hull, zeroV, vScale, vQuat, meshScale.isIdentity()); + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + Vec3V contactA, contactB, normal; + FloatV dist; + LocalConvex convexA(capsule); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + + GjkStatus status = gjk(convexA, convexB, initialSearchDir, FZero(), contactA, contactB, normal, dist); + + return status == GJK_CONTACT; +} + +static bool intersectBoxConvex(const PxBoxGeometry& boxGeom, const PxTransform& boxGlobalPose, + const ConvexMesh& mesh, const PxMeshScale& meshScale, const PxTransform& convexGlobalPose, + PxVec3*) +{ + // AP: see archived non-GJK version in //sw/physx/dev/pterdiman/graveyard/contactConvexBox.cpp + using namespace Ps::aos; + const Vec3V zeroV = V3Zero(); + const ConvexHullData* hull = &mesh.getHull(); + + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + const Vec3V boxExtents = V3LoadU(boxGeom.halfExtents); + const PsMatTransformV aToB(convexGlobalPose.transformInv(boxGlobalPose)); + + ConvexHullV convexHull(hull, zeroV, vScale, vQuat, meshScale.isIdentity()); + BoxV box(zeroV, boxExtents); + + Vec3V contactA, contactB, normal; + FloatV dist; + RelativeConvex convexA(box, aToB); + LocalConvex convexB(convexHull); + + GjkStatus status = gjk(convexA, convexB, aToB.p, FZero(), contactA, contactB, normal, dist); + + //PX_PRINTF("BOX status = %i, overlap = %i, PxVec3(%f, %f, %f)\n", status, overlap, boxGlobalPose.p.x, boxGlobalPose.p.y, boxGlobalPose.p.z); + + return status == GJK_CONTACT; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxVec3* getCachedAxis(TriggerCache* cache) +{ + if(cache && cache->state==TRIGGER_OVERLAP) + return &cache->dir; + else + return NULL; +} + +static PX_FORCE_INLINE bool updateTriggerCache(bool overlap, TriggerCache* cache) +{ + if(cache) + { + if(overlap) + cache->state = TRIGGER_OVERLAP; + else + cache->state = TRIGGER_DISJOINT; + } + return overlap; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Sphere-vs-shape + +static bool GeomOverlapCallback_SphereSphere(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eSPHERE); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom0 = static_cast(geom0); + const PxSphereGeometry& sphereGeom1 = static_cast(geom1); + + const PxVec3 delta = pose1.p - pose0.p; + const PxReal r = sphereGeom0.radius + sphereGeom1.radius; + return delta.magnitudeSquared() <= r*r; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_SpherePlane(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::ePLANE); + PX_UNUSED(cache); + PX_UNUSED(geom1); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + + return getPlane(pose1).distance(pose0.p) <= sphereGeom.radius; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_SphereCapsule(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxCapsuleGeometry& capsuleGeom = static_cast(geom1); + + // PT: TODO: remove this useless conversion + const PxVec3 capsuleHalfHeightVector = getCapsuleHalfHeightVector(pose1, capsuleGeom); + const PxReal r = sphereGeom.radius + capsuleGeom.radius; + + return distancePointSegmentSquared(capsuleHalfHeightVector, -capsuleHalfHeightVector, pose0.p - pose1.p) <= r*r; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_SphereBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxBoxGeometry& boxGeom = static_cast(geom1); + + // PT: TODO: remove this useless conversion + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return intersectSphereBox(Sphere(pose0.p, sphereGeom.radius), obb); +} + +static bool GeomOverlapCallback_SphereConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + PxVec3 cachedSepAxis; + PxVec3* tmp = getCachedAxis(cache); + if(tmp) + cachedSepAxis = *tmp; + else + cachedSepAxis = PxVec3(0,0,1.f); + + const bool overlap = intersectSphereConvex(pose0, sphereGeom.radius, + *cm, + convexGeom.scale, pose1, + &cachedSepAxis); + + if(cache && overlap) + cache->dir = cachedSepAxis; + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Plane-vs-shape + +static bool GeomOverlapCallback_PlaneCapsule(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(cache); + PX_UNUSED(geom0); + +// const PxPlaneGeometry& planeGeom = static_cast(geom0); + const PxCapsuleGeometry& capsuleGeom = static_cast(geom1); + + // PT: TODO: remove this useless conversion + Capsule capsule; + getCapsule(capsule, capsuleGeom, pose1); + + const PxPlane plane = getPlane(pose0); + + // We handle the capsule-plane collision with 2 sphere-plane collisions. + // Seems ok so far, since plane is infinite. + + if(plane.distance(capsule.p0) <= capsule.radius) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + + if(plane.distance(capsule.p1) <= capsule.radius) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + + return false; +} + +/*static bool intersectPlaneBox(const PxPlane& plane, const Box& box) +{ + PxVec3 pts[8]; + box.computeBoxPoints(pts); + + for(PxU32 i=0;i<8;i++) + { + if(plane.distance(pts[i]) <= 0.0f) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + } + return false; +}*/ + +static bool GeomOverlapCallback_PlaneBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + PX_UNUSED(geom0); + +// const PxPlaneGeometry& planeGeom = static_cast(geom0); + const PxBoxGeometry& boxGeom = static_cast(geom1); + + // I currently use the same code as for contact generation but maybe we could do something faster (in theory testing + // only 2 pts is enough). + + const Matrix34 absPose(pose1); + const PxPlane worldPlane = getPlane(pose0); + + for(int vx=-1; vx<=1; vx+=2) + for(int vy=-1; vy<=1; vy+=2) + for(int vz=-1; vz<=1; vz+=2) + { + const PxVec3 v = absPose.transform(PxVec3(PxReal(vx),PxReal(vy),PxReal(vz)).multiply(boxGeom.halfExtents)); + + if(worldPlane.distance(v) <= 0.0f) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + } + return false; +} + +static bool GeomOverlapCallback_PlaneConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + PX_UNUSED(cache); + PX_UNUSED(geom0); + +// const PxPlaneGeometry& planeGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + //find plane normal in shape space of convex: + const PxTransform plane2convex = pose1.getInverse().transform(pose0); + + const PxPlane shapeSpacePlane = getPlane(plane2convex); + + PxReal minimum, maximum; + projectHull_(cm->getHull(), minimum, maximum, shapeSpacePlane.n, convexGeom.scale.toMat33()); + + return (minimum <= -shapeSpacePlane.d); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Capsule-vs-shape + +static bool GeomOverlapCallback_CapsuleCapsule(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom0 = static_cast(geom0); + const PxCapsuleGeometry& capsuleGeom1 = static_cast(geom1); + + // PT: move computation to local space for improved accuracy + const PxVec3 delta = pose1.p - pose0.p; + + // PT: TODO: remove this useless conversion + const PxVec3 capsuleHalfHeightVector0 = getCapsuleHalfHeightVector(pose0, capsuleGeom0); + const PxVec3 capsuleHalfHeightVector1 = getCapsuleHalfHeightVector(pose1, capsuleGeom1); + + const PxReal squareDist = distanceSegmentSegmentSquared(-capsuleHalfHeightVector0, capsuleHalfHeightVector0*2.0f, + delta-capsuleHalfHeightVector1, capsuleHalfHeightVector1*2.0f); + const PxReal r = capsuleGeom0.radius + capsuleGeom1.radius; + return squareDist <= r*r; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_CapsuleBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxBoxGeometry& boxGeom = static_cast(geom1); + + // PT: move computation to local space for improved accuracy + const PxVec3 delta = pose1.p - pose0.p; + + // PT: TODO: remove this useless conversion + const PxVec3 capsuleHalfHeightVector = getCapsuleHalfHeightVector(pose0, capsuleGeom); + + // PT: TODO: remove this useless conversion + const PxMat33 obbRot(pose1.q); + + // PT: objects are defined as closed, so we return 'true' in case of equality + return distanceSegmentBoxSquared(capsuleHalfHeightVector, -capsuleHalfHeightVector, delta, boxGeom.halfExtents, obbRot) <= capsuleGeom.radius*capsuleGeom.radius; +} + +static bool GeomOverlapCallback_CapsuleConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + + PxVec3 cachedSepAxis; + PxVec3* tmp = getCachedAxis(cache); + if(tmp) + cachedSepAxis = *tmp; + else + cachedSepAxis = PxVec3(0,0,1.0f); + + const bool overlap = intersectCapsuleConvex(capsuleGeom, pose0, *cm, convexGeom.scale, pose1, &cachedSepAxis); + + if(cache && overlap) + cache->dir = cachedSepAxis; + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Box-vs-shape + +static bool GeomOverlapCallback_BoxBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + + const PxBoxGeometry& boxGeom0 = static_cast(geom0); + const PxBoxGeometry& boxGeom1 = static_cast(geom1); + + // PT: TODO: remove this useless conversion + return intersectOBBOBB( boxGeom0.halfExtents, pose0.p, PxMat33Padded(pose0.q), + boxGeom1.halfExtents, pose1.p, PxMat33Padded(pose1.q), true); +} + +static bool GeomOverlapCallback_BoxConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + PxVec3 cachedSepAxis; + PxVec3* tmp = getCachedAxis(cache); + if(tmp) + cachedSepAxis = *tmp; + else + cachedSepAxis = PxVec3(0.0f, 0.0f, 1.0f); + + const bool overlap = intersectBoxConvex(boxGeom, pose0, *cm, convexGeom.scale, pose1, &cachedSepAxis); + + if(cache && overlap) + cache->dir = cachedSepAxis; + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Convex-vs-shape +static bool GeomOverlapCallback_ConvexConvex(GU_OVERLAP_FUNC_PARAMS) +{ + using namespace Ps::aos; + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const Vec3V zeroV = V3Zero(); + const PxConvexMeshGeometry& convexGeom0 = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom1 = static_cast(geom1); + const ConvexMesh* cm0 = static_cast(convexGeom0.convexMesh); + const ConvexMesh* cm1 = static_cast(convexGeom1.convexMesh); + + bool overlap; + { + const ConvexHullData* hullData0 = &cm0->getHull(); + const ConvexHullData* hullData1 = &cm1->getHull(); + + const Vec3V vScale0 = V3LoadU_SafeReadW(convexGeom0.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat0 = QuatVLoadU(&convexGeom0.scale.rotation.x); + const Vec3V vScale1 = V3LoadU_SafeReadW(convexGeom1.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat1 = QuatVLoadU(&convexGeom1.scale.rotation.x); + + const QuatV q0 = QuatVLoadU(&pose0.q.x); + const Vec3V p0 = V3LoadU(&pose0.p.x); + + const QuatV q1 = QuatVLoadU(&pose1.q.x); + const Vec3V p1 = V3LoadU(&pose1.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + + const PsMatTransformV aToB(transf1.transformInv(transf0)); + + ConvexHullV convexHull0(hullData0, zeroV, vScale0, vQuat0, convexGeom0.scale.isIdentity()); + ConvexHullV convexHull1(hullData1, zeroV, vScale1, vQuat1, convexGeom1.scale.isIdentity()); + + Vec3V contactA, contactB, normal; + FloatV dist; + RelativeConvex convexA(convexHull0, aToB); + LocalConvex convexB(convexHull1); + + GjkStatus status = gjk(convexA, convexB, aToB.p, FZero(), contactA, contactB, normal, dist); + overlap = (status == GJK_CONTACT); + } + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool GeomOverlapCallback_NotSupported(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ALWAYS_ASSERT_MESSAGE("NOT SUPPORTED"); + PX_UNUSED(cache); + PX_UNUSED(pose0); + PX_UNUSED(pose1); + PX_UNUSED(geom0); + PX_UNUSED(geom1); + return false; +} + +static bool GeomOverlapCallback_HeightfieldUnregistered(GU_OVERLAP_FUNC_PARAMS) +{ + PX_UNUSED(cache); + PX_UNUSED(geom0); + PX_UNUSED(geom1); + PX_UNUSED(pose0); + PX_UNUSED(pose1); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Overlap test called with height fields unregistered "); + return false; +} + +bool GeomOverlapCallback_SphereMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_CapsuleMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_BoxMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_ConvexMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_SphereHeightfield (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_CapsuleHeightfield (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_BoxHeightfield (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_ConvexHeightfield (GU_OVERLAP_FUNC_PARAMS); + +GeomOverlapTable gGeomOverlapMethodTable[] = +{ + //PxGeometryType::eSPHERE + { + GeomOverlapCallback_SphereSphere, //PxGeometryType::eSPHERE + GeomOverlapCallback_SpherePlane, //PxGeometryType::ePLANE + GeomOverlapCallback_SphereCapsule, //PxGeometryType::eCAPSULE + GeomOverlapCallback_SphereBox, //PxGeometryType::eBOX + GeomOverlapCallback_SphereConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_SphereMesh, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + GeomOverlapCallback_NotSupported, //PxGeometryType::ePLANE + GeomOverlapCallback_PlaneCapsule, //PxGeometryType::eCAPSULE + GeomOverlapCallback_PlaneBox, //PxGeometryType::eBOX + GeomOverlapCallback_PlaneConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + GeomOverlapCallback_CapsuleCapsule, //PxGeometryType::eCAPSULE + GeomOverlapCallback_CapsuleBox, //PxGeometryType::eBOX + GeomOverlapCallback_CapsuleConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_CapsuleMesh, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + GeomOverlapCallback_BoxBox, //PxGeometryType::eBOX + GeomOverlapCallback_BoxConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_BoxMesh, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + GeomOverlapCallback_ConvexConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_ConvexMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, +}; + +const GeomOverlapTable* Gu::getOverlapFuncTable() +{ + return gGeomOverlapMethodTable; +} + +void registerHeightFields_Raycasts(); +void registerHeightFields_Sweeps(); +void Gu::registerHeightFields() +{ + registerHeightFields_Raycasts(); + registerHeightFields_Sweeps(); + + gGeomOverlapMethodTable[PxGeometryType::eSPHERE][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_SphereHeightfield; + gGeomOverlapMethodTable[PxGeometryType::eCAPSULE][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_CapsuleHeightfield; + gGeomOverlapMethodTable[PxGeometryType::eBOX][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_BoxHeightfield; + gGeomOverlapMethodTable[PxGeometryType::eCONVEXMESH][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_ConvexHeightfield; +} diff --git a/src/PhysX/physx/source/geomutils/src/GuOverlapTests.h b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.h new file mode 100644 index 000000000..dd59a44c2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.h @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_OVERLAP_TESTS_H +#define GU_OVERLAP_TESTS_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxAssert.h" +#include "CmPhysXCommon.h" +#include "PxGeometry.h" +#include "PsFoundation.h" + +namespace physx +{ +namespace Gu +{ + class Capsule; + class Sphere; + + PX_PHYSX_COMMON_API bool checkOverlapAABB_triangleGeom (const PxGeometry& triGeom, const PxTransform& pose, const PxBounds3& box); + PX_PHYSX_COMMON_API bool checkOverlapAABB_heightFieldGeom (const PxGeometry& hfGeom, const PxTransform& pose, const PxBounds3& box); + + // PT: this is just a shadow of what it used to be. We currently don't use TRIGGER_INSIDE anymore, but I leave it for now, + // since I really want to put this back the way it was before. + enum TriggerStatus + { + TRIGGER_DISJOINT, + TRIGGER_INSIDE, + TRIGGER_OVERLAP + }; + + // PT: currently only used for convex triggers + struct TriggerCache + { + PxVec3 dir; + PxU16 state; + PxU16 gjkState; //gjk succeed or fail + }; + + // PT: we use a define to be able to quickly change the signature of all overlap functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] geom0 first geometry object + // \param[in] pose0 pose of first geometry object + // \param[in] geom1 second geometry object + // \param[in] pose1 pose of second geometry object + // \param[in] cache optional cached data for triggers + #define GU_OVERLAP_FUNC_PARAMS const PxGeometry& geom0, const PxTransform& pose0, \ + const PxGeometry& geom1, const PxTransform& pose1, \ + Gu::TriggerCache* cache + + // PT: function pointer for Geom-indexed overlap functions + // See GU_OVERLAP_FUNC_PARAMS for function parameters details. + // \return true if an overlap was found, false otherwise + typedef bool (*GeomOverlapFunc) (GU_OVERLAP_FUNC_PARAMS); + + // PT: typedef for a bundle of all overlap functions, i.e. the function table itself (indexed by geom-type). + typedef GeomOverlapFunc GeomOverlapTable[PxGeometryType::eGEOMETRY_COUNT]; + + // PT: retrieves the overlap function table (for access by external non-Gu modules) + PX_PHYSX_COMMON_API const GeomOverlapTable* getOverlapFuncTable(); + + // dynamic registration of height fields + PX_PHYSX_COMMON_API void registerHeightFields(); + + PX_FORCE_INLINE bool overlap( const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1, + const GeomOverlapTable* PX_RESTRICT overlapFuncs) + { + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "Gu::overlap(): pose0 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "Gu::overlap(): pose1 is not valid.", false); + + if(geom0.getType() > geom1.getType()) + { + GeomOverlapFunc overlapFunc = overlapFuncs[geom1.getType()][geom0.getType()]; + PX_ASSERT(overlapFunc); + return overlapFunc(geom1, pose1, geom0, pose0, NULL); + } + else + { + GeomOverlapFunc overlapFunc = overlapFuncs[geom0.getType()][geom1.getType()]; + PX_ASSERT(overlapFunc); + return overlapFunc(geom0, pose0, geom1, pose1, NULL); + } + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp b/src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp new file mode 100644 index 000000000..aba0a6b91 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp @@ -0,0 +1,564 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuMidphaseInterface.h" +#include "GuInternal.h" +#include "PxSphereGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "GuIntersectionRayCapsule.h" +#include "GuIntersectionRaySphere.h" +#include "GuIntersectionRayPlane.h" +#include "GuHeightFieldUtil.h" +#include "GuDistancePointSegment.h" +#include "GuConvexMesh.h" +#include "CmScaling.h" + +using namespace physx; +using namespace Gu; + +////////////////////////////////////////////////// raycasts ////////////////////////////////////////////////////////////////// +PxU32 raycast_box(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_ASSERT(maxHits && hits); + const PxBoxGeometry& boxGeom = static_cast(geom); + + const PxTransform& absPose = pose; + + PxVec3 localOrigin = rayOrigin - absPose.p; + localOrigin = absPose.q.rotateInv(localOrigin); + + const PxVec3 localDir = absPose.q.rotateInv(rayDir); + + PxVec3 localImpact; + PxReal t; + PxU32 rval = rayAABBIntersect2(-boxGeom.halfExtents, boxGeom.halfExtents, localOrigin, localDir, localImpact, t); + if(!rval) + return 0; + + if(t>maxDist) + return 0; + + hits->distance = t; //worldRay.orig.distance(hit.worldImpact); //should be the same, assuming ray dir was normalized!! + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + + PxHitFlags outFlags = PxHitFlags(0); + if((hitFlags & PxHitFlag::ePOSITION)) + { + outFlags |= PxHitFlag::ePOSITION; + if(t!=0.0f) + hits->position = absPose.transform(localImpact); + else + hits->position = rayOrigin; + } + + // Compute additional information if needed + if(hitFlags & PxHitFlag::eNORMAL) + { + outFlags |= PxHitFlag::eNORMAL; + + //Because rayAABBIntersect2 set t = 0 if start point inside shape + if(t == 0) + { + hits->normal = -rayDir; + } + else + { + //local space normal is: + rval--; + PxVec3 n(0.0f); + n[rval] = PxReal((localImpact[rval] > 0.0f) ? 1.0f : -1.0f); + hits->normal = absPose.q.rotate(n); + } + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = outFlags; + return 1; +} + +PxU32 raycast_sphere(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + PX_ASSERT(maxHits && hits); + + const PxSphereGeometry& sphereGeom = static_cast(geom); + + if(!intersectRaySphere(rayOrigin, rayDir, maxDist, pose.p, sphereGeom.radius, hits->distance, &hits->position)) + return 0; + + /* // PT: should be useless now + hit.distance = worldRay.orig.distance(hit.worldImpact); + if(hit.distance>maxDist) + return false; + */ + // PT: we can't avoid computing the position here since it's needed to compute the normal anyway + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + + // Compute additional information if needed + PxHitFlags outFlags = PxHitFlag::ePOSITION; + if(hitFlags & PxHitFlag::eNORMAL) + { + // User requested impact normal + //Because intersectRaySphere set distance = 0 if start point inside shape + if(hits->distance == 0.0f) + { + hits->normal = -rayDir; + } + else + { + hits->normal = hits->position - pose.p; + hits->normal.normalize(); + } + outFlags |= PxHitFlag::eNORMAL; + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = outFlags; + + return 1; +} + +PxU32 raycast_capsule(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + PX_ASSERT(maxHits && hits); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + // TODO: PT: could we simplify this ? + Capsule capsule; + getCapsuleSegment(pose, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + PxReal t = 0.0f; + if(!intersectRayCapsule(rayOrigin, rayDir, capsule, t)) + return 0; + + if(t<0.0f || t>maxDist) + return 0; + + // PT: we can't avoid computing the position here since it's needed to compute the normal anyway + hits->position = rayOrigin + rayDir*t; // PT: will be rayOrigin for t=0.0f (i.e. what the spec wants) + hits->distance = t; + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + + // Compute additional information if needed + PxHitFlags outFlags = PxHitFlag::ePOSITION; + if(hitFlags & PxHitFlag::eNORMAL) + { + outFlags |= PxHitFlag::eNORMAL; + + if(t==0.0f) + { + hits->normal = -rayDir; + } + else + { + PxReal capsuleT; + distancePointSegmentSquared(capsule, hits->position, &capsuleT); + capsule.computePoint(hits->normal, capsuleT); + hits->normal = hits->position - hits->normal; //this should never be zero. It should have a magnitude of the capsule radius. + hits->normal.normalize(); + } + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = outFlags; + + return 1; +} + +PxU32 raycast_plane(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(hitFlags); + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_ASSERT(maxHits && hits); + PX_UNUSED(geom); +// const PxPlaneGeometry& planeGeom = static_cast(geom); + + // Perform backface culling so that we can pick objects beyond planes + const PxPlane plane = getPlane(pose); + if(rayDir.dot(plane.n)>=0.0f) + return false; + + PxReal distanceAlongLine; + if(!intersectRayPlane(rayOrigin, rayDir, plane, distanceAlongLine, &hits->position)) + return 0; + + /* + PxReal test = worldRay.orig.distance(hit.worldImpact); + + PxReal dd; + PxVec3 pp; + PxSegmentPlaneIntersect(worldRay.orig, worldRay.orig+worldRay.dir*1000.0f, plane, dd, pp); + */ + + if(distanceAlongLine<0.0f) + return 0; + + if(distanceAlongLine>maxDist) + return 0; + + hits->distance = distanceAlongLine; + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + hits->flags = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL; + hits->normal = plane.n; + return 1; +} + +PxU32 raycast_convexMesh(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + PX_ASSERT(maxHits && hits); + PX_ASSERT(PxAbs(rayDir.magnitudeSquared()-1)<1e-4f); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + + PxRaycastHit& hit = *hits; + + //scaling: transform the ray to vertex space + const Cm::Matrix34 world2vertexSkew = convexGeom.scale.getInverse() * pose.getInverse(); + + //ConvexMesh* cmesh = static_cast(convexGeom.convexMesh); + const PxU32 nPolys = convexMesh->getNbPolygonsFast(); + const HullPolygonData* PX_RESTRICT polysEA = convexMesh->getPolygons(); + const HullPolygonData* polys = polysEA; + + const PxVec3 vrayOrig = world2vertexSkew.transform(rayOrigin); + const PxVec3 vrayDir = world2vertexSkew.rotate(rayDir); + + /* + Purely convex planes based algorithm + Iterate all planes of convex, with following rules: + * determine of ray origin is inside them all or not. + * planes parallel to ray direction are immediate early out if we're on the outside side (plane normal is sep axis) + * else + - for all planes the ray direction "enters" from the front side, track the one furthest along the ray direction (A) + - for all planes the ray direction "exits" from the back side, track the one furthest along the negative ray direction (B) + if the ray origin is outside the convex and if along the ray, A comes before B, the directed line stabs the convex at A + */ + bool originInsideAllPlanes = true; + PxReal latestEntry = -FLT_MAX; + PxReal earliestExit = FLT_MAX; +// PxU32 bestPolygonIndex = 0; + hit.faceIndex = 0xffffffff; + + for(PxU32 i=0;i 0.0f) + originInsideAllPlanes = false; //origin not behind plane == ray starts outside the convex. + + if(dn > 1E-7f) //the ray direction "exits" from the back side + { + earliestExit = physx::intrinsics::selectMin(earliestExit, distAlongRay); + } + else if(dn < -1E-7f) //the ray direction "enters" from the front side + { + if(distAlongRay > latestEntry) + { + latestEntry = distAlongRay; + hit.faceIndex = i; + } + } + else + { + //plane normal and ray dir are orthogonal + if(distToPlane > 0.0f) + return 0; //a plane is parallel with ray -- and we're outside the ray -- we definitely miss the entire convex! + } + } + + if(originInsideAllPlanes) //ray starts inside convex + { + hit.distance = 0.0f; + hit.faceIndex = 0xffffffff; + hit.u = 0.0f; + hit.v = 0.0f; + hit.position = rayOrigin; + hit.normal = -rayDir; + hit.flags = PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + return 1; + } + + // AP: changed to latestEntry < maxDist-1e-5f so that we have a conservatively negative result near end of ray + if(latestEntry < earliestExit && latestEntry > 0.0f && latestEntry < maxDist-1e-5f) + { + PxHitFlags outFlags = PxHitFlag::eFACE_INDEX; + if(hitFlags & PxHitFlag::ePOSITION) + { + outFlags |= PxHitFlag::ePOSITION; + const PxVec3 pointOnPlane = vrayOrig + latestEntry * vrayDir; + hit.position = pose.transform(convexGeom.scale.toMat33() * pointOnPlane); + } + hit.distance = latestEntry; + hit.u = 0.0f; + hit.v = 0.0f; + hit.normal = PxVec3(0.0f); + + // Compute additional information if needed + if(hitFlags & PxHitFlag::eNORMAL) + { + outFlags |= PxHitFlag::eNORMAL; + //when we have nonuniform scaling we actually have to transform by the transpose of the inverse of vertex2worldSkew.M == transpose of world2vertexSkew: + hit.normal = world2vertexSkew.rotateTranspose(polys[hit.faceIndex].mPlane.n); + hit.normal.normalize(); + } + hit.flags = outFlags; + return 1; + } + return 0; +} + +PxU32 raycast_triangleMesh(GU_RAY_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + PX_ASSERT(PxAbs(rayDir.magnitudeSquared()-1)<1e-4f); + + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + return Midphase::raycastTriangleMesh(meshData, meshGeom, pose, rayOrigin, rayDir, maxDist, hitFlags, maxHits, hits); +} + +namespace +{ + struct HFTraceSegmentCallback + { + PX_NOCOPY(HFTraceSegmentCallback) + public: + PxRaycastHit* mHits; + const PxU32 mMaxHits; + PxU32 mNbHits; + const HeightFieldUtil& mUtil; + const PxTransform& mPose; + const PxVec3& mRayDir; + const PxVec3& mLocalRayDir; + const PxVec3& mLocalRayOrig; + const PxHitFlags mHitFlags; + const bool mIsDoubleSided; + + HFTraceSegmentCallback( PxRaycastHit* hits, PxU32 maxHits, const PxHitFlags hitFlags, const HeightFieldUtil& hfUtil, const PxTransform& pose, + const PxVec3& rayDir, const PxVec3& localRayDir, const PxVec3& localRayOrig, + bool isDoubleSided) : + mHits (hits), + mMaxHits (maxHits), + mNbHits (0), + mUtil (hfUtil), + mPose (pose), + mRayDir (rayDir), + mLocalRayDir (localRayDir), + mLocalRayOrig (localRayOrig), + mHitFlags (hitFlags), + mIsDoubleSided (isDoubleSided) + { + PX_ASSERT(maxHits > 0); + } + + PX_FORCE_INLINE bool onEvent(PxU32 , PxU32*) + { + return true; + } + + PX_FORCE_INLINE bool underFaceHit(const HeightFieldUtil&, const PxVec3&, const PxVec3&, PxF32, PxF32, PxF32, PxU32) + { + return true; // true means continue traversal + } + + PxAgain faceHit(const HeightFieldUtil&, const PxVec3& aHitPoint, PxU32 aTriangleIndex, PxReal u, PxReal v) + { + // traversal is strictly sorted so there's no need to sort hits + if(mNbHits >= mMaxHits) + return false; // false = stop traversal + + PxRaycastHit& hit = mHits[mNbHits++]; + hit.position = aHitPoint; + hit.faceIndex = aTriangleIndex; + hit.u = u; + hit.v = v; + hit.flags = PxHitFlag::eUV | PxHitFlag::eFACE_INDEX; // UVs and face index are always set + + if(mHitFlags & PxHitFlag::eNORMAL) + { + // We need the normal for the dot product. + PxVec3 normal = mPose.q.rotate(mUtil.getNormalAtShapePoint(hit.position.x, hit.position.z)); + normal.normalize(); + if(mIsDoubleSided && normal.dot(mRayDir) > 0.0f) // comply with normal spec for double sided (should always face opposite rayDir) + hit.normal = -normal; + else + hit.normal = normal; + hit.flags |= PxHitFlag::eNORMAL; + } + + hit.distance = physx::intrinsics::selectMax(0.f, (hit.position - mLocalRayOrig).dot(mLocalRayDir)); + + if(mHitFlags & PxHitFlag::ePOSITION) + { + hit.position = mPose.transform(hit.position); + hit.flags |= PxHitFlag::ePOSITION; + } + return (mNbHits < mMaxHits); // true = continue traversal, false = stop traversal + } + }; +} + +PxU32 raycast_heightField(GU_RAY_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + PX_ASSERT(maxHits && hits); + PX_UNUSED(maxHits); + + const PxHeightFieldGeometry& hfGeom = static_cast(geom); + + const PxTransform invAbsPose = pose.getInverse(); + const PxVec3 localRayOrig = invAbsPose.transform(rayOrigin); + const PxVec3 localRayDir = invAbsPose.rotate(rayDir); + + const bool isDoubleSided = hfGeom.heightFieldFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED); + const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES); + + const HeightFieldTraceUtil hfUtil(hfGeom); + + PxVec3 normRayDir = localRayDir; + normRayDir.normalizeSafe(); // nothing will happen if length is < PX_NORMALIZATION_EPSILON + + // pretest if we intersect HF bounds. If no early exit, if yes move the origin and shorten the maxDist + // to deal with precision issues with large maxDist + PxBounds3 hfLocalBounds; + hfUtil.computeLocalBounds(hfLocalBounds); + + // PT: inflate the bounds like we do in the scene-tree (see PX-1179) + const PxVec3 center = hfLocalBounds.getCenter(); + const PxVec3 extents = hfLocalBounds.getExtents() * 1.01f; //SQ_PRUNER_INFLATION; + hfLocalBounds.minimum = center - extents; + hfLocalBounds.maximum = center + extents; + + PxVec3 localImpact; + PxReal t; // closest intersection, t==0 hit inside + PxU32 rval = rayAABBIntersect2(hfLocalBounds.minimum, hfLocalBounds.maximum, localRayOrig, localRayDir, localImpact, t); + // early exit we miss the AABB + if (!rval) + return 0; + if (t > maxDist) + return 0; + + // PT: if eMESH_ANY is used then eMESH_MULTIPLE won't be, and we'll stop the query after 1 hit is found. There is no difference + // between 'any hit' and 'closest hit' for HFs since hits are reported in order. + HFTraceSegmentCallback callback(hits, hitFlags.isSet(PxHitFlag::eMESH_MULTIPLE) ? maxHits : 1, hitFlags, hfUtil, pose, + rayDir, localRayDir, localRayOrig, + isDoubleSided); // make sure we return only 1 hit without eMESH_MULTIPLE + + PxReal offset = 0.0f; + PxReal maxDistOffset = maxDist; + PxVec3 localRayOrigOffset = localRayOrig; + + // if we don't start inside the AABB box, offset the start pos, because of precision issues with large maxDist + if(t > 0.0f) + { + offset = t - GU_RAY_SURFACE_OFFSET; + // move the rayOrig to offset start pos + localRayOrigOffset = localRayOrig + normRayDir*offset; + } + + // shorten the maxDist of the offset that was cut off and clip it + // we pick either the original maxDist, if maxDist is huge we clip it + maxDistOffset = PxMin(maxDist - offset, GU_RAY_SURFACE_OFFSET + 2.0f * PxMax(hfLocalBounds.maximum.x - hfLocalBounds.minimum.x, PxMax(hfLocalBounds.maximum.y - hfLocalBounds.minimum.y, hfLocalBounds.maximum.z - hfLocalBounds.minimum.z))); + + hfUtil.traceSegment(localRayOrigOffset, normRayDir, maxDistOffset, + &callback, hfLocalBounds, !bothSides); + return callback.mNbHits; +} + +static PxU32 raycast_heightField_unregistered(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(rayOrigin); + PX_UNUSED(rayDir); + PX_UNUSED(maxDist); + PX_UNUSED(hitFlags); + PX_UNUSED(maxHits); + PX_UNUSED(hits); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Raycast test called with height fields unregistered "); + return 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PT: table is not static because it's accessed as 'extern' within Gu (bypassing the function call). +RaycastFunc gRaycastMap[PxGeometryType::eGEOMETRY_COUNT] = +{ + raycast_sphere, + raycast_plane, + raycast_capsule, + raycast_box, + raycast_convexMesh, + raycast_triangleMesh, + raycast_heightField_unregistered +}; + +// PT: the function is used by external modules (Np, CCT, Sq) +const Gu::GeomRaycastTable& Gu::getRaycastFuncTable() +{ + return gRaycastMap; +} + +void registerHeightFields_Raycasts() +{ + gRaycastMap[PxGeometryType::eHEIGHTFIELD] = raycast_heightField; +} diff --git a/src/PhysX/physx/source/geomutils/src/GuSerialize.cpp b/src/PhysX/physx/source/geomutils/src/GuSerialize.cpp new file mode 100644 index 000000000..83205d8a2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSerialize.cpp @@ -0,0 +1,381 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "PsUtilities.h" +#include "GuSerialize.h" +#include "PsUserAllocated.h" +#include "PsAllocator.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +void physx::readChunk(PxI8& a, PxI8& b, PxI8& c, PxI8& d, PxInputStream& stream) +{ + stream.read(&a, sizeof(PxI8)); + stream.read(&b, sizeof(PxI8)); + stream.read(&c, sizeof(PxI8)); + stream.read(&d, sizeof(PxI8)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxU16 physx::readWord(bool mismatch, PxInputStream& stream) +{ + PxU16 d; + stream.read(&d, sizeof(PxU16)); + + if(mismatch) + flip(d); + return d; +} + +PxU32 physx::readDword(bool mismatch, PxInputStream& stream) +{ + PxU32 d; + stream.read(&d, sizeof(PxU32)); + + if(mismatch) + flip(d); + return d; +} + +PxF32 physx::readFloat(bool mismatch, PxInputStream& stream) +{ + union + { + PxU32 d; + PxF32 f; + } u; + + stream.read(&u.d, sizeof(PxU32)); + + if(mismatch) + flip(u.d); + return u.f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void physx::writeWord(PxU16 value, bool mismatch, PxOutputStream& stream) +{ + if(mismatch) + flip(value); + stream.write(&value, sizeof(PxU16)); +} + +void physx::writeDword(PxU32 value, bool mismatch, PxOutputStream& stream) +{ + if(mismatch) + flip(value); + stream.write(&value, sizeof(PxU32)); +} + +void physx::writeFloat(PxF32 value, bool mismatch, PxOutputStream& stream) +{ + if(mismatch) + flip(value); + stream.write(&value, sizeof(PxF32)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool physx::readFloatBuffer(PxF32* dest, PxU32 nbFloats, bool mismatch, PxInputStream& stream) +{ + stream.read(dest, sizeof(PxF32)*nbFloats); + if(mismatch) + { + for(PxU32 i=0;imaxIndex) + maxIndex = currentIndex; + } + return maxIndex; +} +PxU16 physx::computeMaxIndex(const PxU16* indices, PxU32 nbIndices) +{ + PxU16 maxIndex=0; + while(nbIndices--) + { + PxU16 currentIndex = *indices++; + if(currentIndex>maxIndex) + maxIndex = currentIndex; + } + return maxIndex; +} + +void physx::storeIndices(PxU32 maxIndex, PxU32 nbIndices, const PxU32* indices, PxOutputStream& stream, bool platformMismatch) +{ + if(maxIndex<=0xff) + { + for(PxU32 i=0;i(PxAlloca(nbIndices*sizeof(PxU8))); + stream.read(tmp, nbIndices*sizeof(PxU8)); + for(PxU32 i=0;i(PxAlloca(nbIndices*sizeof(PxU16))); + readWordBuffer(tmp, nbIndices, platformMismatch, stream); + for(PxU32 i=0;i(PxAlloca(nbIndices*sizeof(PxU8))); + stream.read(tmp, nbIndices*sizeof(PxU8)); + for(PxU32 i=0;i(&v); + PxU8 temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + PX_INLINE void flip(PxI16& v) + { + PxI8* b = reinterpret_cast(&v); + PxI8 temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + PX_INLINE void flip(PxU32& v) + { + PxU8* b = reinterpret_cast(&v); + + PxU8 temp = b[0]; + b[0] = b[3]; + b[3] = temp; + temp = b[1]; + b[1] = b[2]; + b[2] = temp; + } + + // MS: It is important to modify the value directly and not use a temporary variable or a return + // value. The reason for this is that a flipped float might have a bit pattern which indicates + // an invalid float. If such a float is assigned to another float, the bit pattern + // can change again (maybe to map invalid floats to a common invalid pattern?). + // When reading the float and flipping again, the changed bit pattern will result in a different + // float than the original one. + PX_INLINE void flip(PxF32& v) + { + PxU8* b = reinterpret_cast(&v); + + PxU8 temp = b[0]; + b[0] = b[3]; + b[3] = temp; + temp = b[1]; + b[1] = b[2]; + b[2] = temp; + } + + PX_INLINE void writeChunk(PxI8 a, PxI8 b, PxI8 c, PxI8 d, PxOutputStream& stream) + { + stream.write(&a, sizeof(PxI8)); + stream.write(&b, sizeof(PxI8)); + stream.write(&c, sizeof(PxI8)); + stream.write(&d, sizeof(PxI8)); + } + + void readChunk(PxI8& a, PxI8& b, PxI8& c, PxI8& d, PxInputStream& stream); + + PxU16 readWord(bool mismatch, PxInputStream& stream); +PX_PHYSX_COMMON_API PxU32 readDword(bool mismatch, PxInputStream& stream); + PxF32 readFloat(bool mismatch, PxInputStream& stream); + +PX_PHYSX_COMMON_API void writeWord(PxU16 value, bool mismatch, PxOutputStream& stream); +PX_PHYSX_COMMON_API void writeDword(PxU32 value, bool mismatch, PxOutputStream& stream); +PX_PHYSX_COMMON_API void writeFloat(PxF32 value, bool mismatch, PxOutputStream& stream); + + bool readFloatBuffer(PxF32* dest, PxU32 nbFloats, bool mismatch, PxInputStream& stream); +PX_PHYSX_COMMON_API void writeFloatBuffer(const PxF32* src, PxU32 nb, bool mismatch, PxOutputStream& stream); +PX_PHYSX_COMMON_API void writeWordBuffer(const PxU16* src, PxU32 nb, bool mismatch, PxOutputStream& stream); + void readWordBuffer(PxU16* dest, PxU32 nb, bool mismatch, PxInputStream& stream); + +PX_PHYSX_COMMON_API bool writeHeader(PxI8 a, PxI8 b, PxI8 c, PxI8 d, PxU32 version, bool mismatch, PxOutputStream& stream); + bool readHeader(PxI8 a, PxI8 b, PxI8 c, PxI8 d, PxU32& version, bool& mismatch, PxInputStream& stream); + +PX_INLINE bool readIntBuffer(PxU32* dest, PxU32 nbInts, bool mismatch, PxInputStream& stream) +{ + return readFloatBuffer(reinterpret_cast(dest), nbInts, mismatch, stream); +} + +PX_INLINE void writeIntBuffer(const PxU32* src, PxU32 nb, bool mismatch, PxOutputStream& stream) +{ + writeFloatBuffer(reinterpret_cast(src), nb, mismatch, stream); +} + +PX_INLINE bool ReadDwordBuffer(PxU32* dest, PxU32 nb, bool mismatch, PxInputStream& stream) +{ + return readFloatBuffer(reinterpret_cast(dest), nb, mismatch, stream); +} + +PX_INLINE void WriteDwordBuffer(const PxU32* src, PxU32 nb, bool mismatch, PxOutputStream& stream) +{ + writeFloatBuffer(reinterpret_cast(src), nb, mismatch, stream); +} + +PX_PHYSX_COMMON_API PxU32 computeMaxIndex(const PxU32* indices, PxU32 nbIndices); +PX_PHYSX_COMMON_API PxU16 computeMaxIndex(const PxU16* indices, PxU32 nbIndices); +PX_PHYSX_COMMON_API void storeIndices(PxU32 maxIndex, PxU32 nbIndices, const PxU32* indices, PxOutputStream& stream, bool platformMismatch); +PX_PHYSX_COMMON_API void readIndices(PxU32 maxIndex, PxU32 nbIndices, PxU32* indices, PxInputStream& stream, bool platformMismatch); + + // PT: see PX-1163 + PX_FORCE_INLINE bool readBigEndianVersionNumber(PxInputStream& stream, bool mismatch_, PxU32& fileVersion, bool& mismatch) + { + // PT: allright this is going to be subtle: + // - in version 1 the data was always saved in big-endian format + // - *including the version number*! + // - so we cannot just read the version "as usual" using the passed mismatch param + + // PT: mismatch value for version 1 + mismatch = (shdfnd::littleEndian() == 1); + + const PxU32 rawFileVersion = readDword(false, stream); + if(rawFileVersion==1) + { + // PT: this is a version-1 file with no flip + fileVersion = 1; + PX_ASSERT(!mismatch); + } + else + { + PxU32 fileVersionFlipped = rawFileVersion; + flip(fileVersionFlipped); + if(fileVersionFlipped==1) + { + // PT: this is a version-1 file with flip + fileVersion = 1; + PX_ASSERT(mismatch); + } + else + { + // PT: this is at least version 2 so we can process it "as usual" + mismatch = mismatch_; + fileVersion = mismatch_ ? fileVersionFlipped : rawFileVersion; + } + } + + PX_ASSERT(fileVersion<=3); + if(fileVersion>3) + return false; + return true; + } + +// PT: TODO: copied from IceSerialize.h, still needs to be refactored/cleaned up. +namespace Gu +{ + PX_PHYSX_COMMON_API bool WriteHeader(PxU8 a, PxU8 b, PxU8 c, PxU8 d, PxU32 version, bool mismatch, PxOutputStream& stream); + PX_PHYSX_COMMON_API bool ReadHeader(PxU8 a_, PxU8 b_, PxU8 c_, PxU8 d_, PxU32& version, bool& mismatch, PxInputStream& stream); + + PX_PHYSX_COMMON_API void StoreIndices(PxU32 maxIndex, PxU32 nbIndices, const PxU32* indices, PxOutputStream& stream, bool platformMismatch); + void ReadIndices(PxU32 maxIndex, PxU32 nbIndices, PxU32* indices, PxInputStream& stream, bool platformMismatch); + + PX_PHYSX_COMMON_API void StoreIndices(PxU16 maxIndex, PxU32 nbIndices, const PxU16* indices, PxOutputStream& stream, bool platformMismatch); + void ReadIndices(PxU16 maxIndex, PxU32 nbIndices, PxU16* indices, PxInputStream& stream, bool platformMismatch); +} + + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuSphere.h b/src/PhysX/physx/source/geomutils/src/GuSphere.h new file mode 100644 index 000000000..c249f4e21 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSphere.h @@ -0,0 +1,108 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SPHERE_H +#define GU_SPHERE_H +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +/** +\brief Represents a sphere defined by its center point and radius. +*/ +namespace Gu +{ + class Sphere + { + public: + /** + \brief Constructor + */ + PX_INLINE Sphere() + { + } + + /** + \brief Constructor + */ + PX_INLINE Sphere(const PxVec3& _center, PxF32 _radius) : center(_center), radius(_radius) + { + } + /** + \brief Copy constructor + */ + PX_INLINE Sphere(const Sphere& sphere) : center(sphere.center), radius(sphere.radius) + { + } + /** + \brief Destructor + */ + PX_INLINE ~Sphere() + { + } + + PX_INLINE void set(const PxVec3& _center, float _radius) { center = _center; radius = _radius; } + + /** + \brief Checks the sphere is valid. + + \return true if the sphere is valid + */ + PX_INLINE bool isValid() const + { + // Consistency condition for spheres: Radius >= 0.0f + return radius >= 0.0f; + } + + /** + \brief Tests if a point is contained within the sphere. + + \param[in] p the point to test + \return true if inside the sphere + */ + PX_INLINE bool contains(const PxVec3& p) const + { + return (center-p).magnitudeSquared() <= radius*radius; + } + + PxVec3 center; //!< Sphere's center + PxF32 radius; //!< Sphere's radius + }; +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp new file mode 100644 index 000000000..d2e8cb917 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp @@ -0,0 +1,1218 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "PxConvexMeshGeometry.h" +#include "GuConvexMesh.h" +#include "GuSweepSharedTests.h" +#include "GuConvexUtilsInternal.h" +#include "GuTriangleMesh.h" +#include "GuVecBox.h" +#include "GuVecTriangle.h" +#include "GuVecConvexHullNoScale.h" +#include "GuMidphaseInterface.h" +#include "GuPCMContactConvexCommon.h" +#include "GuSweepMTD.h" +#include "GuPCMShapeConvex.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistancePointSegment.h" +#include "GuInternal.h" +#include "GuConvexEdgeFlags.h" + +using namespace physx; +using namespace Gu; + +// PT: TODO: refactor with GuMTD.cpp versions +static PX_FORCE_INLINE PxF32 manualNormalize(PxVec3& mtd, const PxVec3& normal, PxReal lenSq) +{ + const PxF32 len = PxSqrt(lenSq); + + // We do a *manual* normalization to check for singularity condition + if(lenSq < 1e-6f) + mtd = PxVec3(1.0f, 0.0f, 0.0f); // PT: zero normal => pick up random one + else + mtd = normal * 1.0f / len; + + return len; +} + +static PX_FORCE_INLINE void getScaledTriangle(const PxTriangleMeshGeometry& triGeom, const Cm::Matrix34& vertex2worldSkew, bool flipsNormal, PxTriangle& triangle, PxTriangleID triangleIndex) +{ + TriangleMesh* tm = static_cast(triGeom.triangleMesh); + tm->computeWorldTriangle(triangle, triangleIndex, vertex2worldSkew, flipsNormal); +} + +#define BATCH_TRIANGLE_NUMBER 32u + +struct MTDTriangle : public PxTriangle +{ +public: + PxU8 extraTriData;//active edge flag data +}; + +struct MeshMTDGenerationCallback : MeshHitCallback +{ +public: + + Ps::Array& container; + + MeshMTDGenerationCallback(Ps::Array& tempContainer) + : MeshHitCallback(CallbackMode::eMULTIPLE), container(tempContainer) + { + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3&, const PxVec3&, const PxVec3&, PxReal&, const PxU32*) + { + container.pushBack(hit.faceIndex); + + return true; + } + + void operator=(const MeshMTDGenerationCallback&) {} +}; + +static bool getMTDPerTriangle(const MeshPersistentContact* manifoldContacts, const PxU32 numContacts, const PxU32 triangleIndex, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, PxU32& faceIndex, Ps::aos::FloatV& deepestPen) +{ + using namespace Ps::aos; + + FloatV deepest = V4GetW(manifoldContacts[0].mLocalNormalPen); + PxU32 index = 0; + for(PxU32 k=1; k& tempContainer) +{ + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + Box vertexSpaceBox; + computeVertexSpaceOBB(vertexSpaceBox, bound, pose, meshGeom.scale); + + MeshMTDGenerationCallback callback(tempContainer); + Midphase::intersectOBB(meshData, vertexSpaceBox, callback, true); +} + +// PT: TODO: refactor with EntityReportContainerCallback +struct MidPhaseQueryLocalReport : EntityReport +{ + MidPhaseQueryLocalReport(Ps::Array& _container) : container(_container) + { + + } + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + for(PxU32 i=0; i& container; + +private: + MidPhaseQueryLocalReport operator=(MidPhaseQueryLocalReport& report); +} ; + +static void midPhaseQuery(const HeightFieldUtil& hfUtil, const PxTransform& pose, const PxBounds3& bounds, Ps::Array& tempContainer, PxU32 flags) +{ + MidPhaseQueryLocalReport localReport(tempContainer); + hfUtil.overlapAABBTriangles(pose, bounds, flags, &localReport); +} + +static bool calculateMTD( const CapsuleV& capsuleV, const Ps::aos::FloatVArg inflatedRadiusV, const bool isDoubleSide, const MTDTriangle* triangles, const PxU32 nbTriangles, const PxU32 startIndex, MeshPersistentContact* manifoldContacts, + PxU32& numContacts, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, PxU32& faceIndex, Ps::aos::FloatV& mtd) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + bool hadContacts = false; + FloatV deepestPen = mtd; + + for(PxU32 j=0; j(triMeshGeom.triangleMesh); + const PxU8* extraTrigData = triMesh->getExtraTrigData(); + const bool flipsNormal = triMeshGeom.scale.hasNegativeDeterminant(); + + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + //inflated the capsule by 15% in case of some disagreement between sweep and mtd calculation. If sweep said initial overlap, but mtd has a positive separation, + //we are still be able to return a valid normal but we should zero the distance. + const FloatV inflatedRadiusV = FLoad(inflatedRadius*1.15f); + + bool foundInitial = false; + const PxU32 iterations = 4; + + const Cm::Matrix34 vertexToWorldSkew = pose * triMeshGeom.scale; + + Ps::Array tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i tempContainer; + tempContainer.reserve(128); + + const HeightFieldUtil hfUtil(heightFieldGeom); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i(triMeshGeom.triangleMesh); + const PxU8* extraTrigData = triMesh->getExtraTrigData(); + + const Vec3V zeroV = V3Zero(); + + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + + bool foundInitial = false; + const PxU32 iterations = 4; + + Ps::Array tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + Box box = _box; + + const QuatV q0 = QuatVLoadU(&boxTransform.q.x); + const Vec3V p0 = V3LoadU(&boxTransform.p.x); + + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV minMargin = CalculateMTDBoxMargin(boxExtents); + const FloatV inflationV = FAdd(FLoad(inflation), minMargin); + PxReal boundInflation; + FStore(inflationV, &boundInflation); + + box.extents += PxVec3(boundInflation); + const BoxV boxV(zeroV, boxExtents); + + Vec3V boxCenter = V3LoadU(box.center); + + //create the polyData based on the original data + PolygonalData polyData; + const PCMPolygonalBox polyBox(_box.extents); + polyBox.getPolygonalData(&polyData); + + const Mat33V identity = M33Identity(); + + const Cm::Matrix34 meshToWorldSkew = pose * triMeshGeom.scale; + + PsTransformV boxTransformV(p0, q0);//box + + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i boxMap(boxV, boxTransformV, identity, identity, true); + + boxMap.setShapeSpaceCenterofMass(zeroV); + // Move to AABB space + Cm::Matrix34 WorldToBox; + computeWorldToBoxMatrix(WorldToBox, box); + const Cm::Matrix34 meshToBox = WorldToBox*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToBox.m.column0), V3LoadU(meshToBox.m.column1), V3LoadU(meshToBox.m.column2)); + const Ps::aos::PsMatTransformV meshToConvex(V3LoadU(meshToBox.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER - 1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; kgetLocalTriangle(triangles[k], triangleIndex1, triMeshGeom.scale.hasNegativeDeterminant()); + triangles[k].extraTriData = getConvexEdgeFlags(extraTrigData, triangleIndex1); + } + + //ML: mtd has back face culling, so if the capsule's center is below the triangle, we won't generate any contacts + hadContacts = calculateMTD(polyData, &boxMap, boxTransformV, meshToConvex, isDoubleSided, inflationV, triangles, nbTrigs, startIndex, manifoldContacts, numContacts, normal, closestA, closestB, triangleIndex, mtd) || hadContacts; + } + + if(!hadContacts) + break; + + triangleIndex = tempContainer[triangleIndex]; + + foundInitial = true; + + distV = mtd; + worldNormal = boxTransformV.rotate(normal); + worldContactA = boxTransformV.transform(closestA); + if(FAllGrtrOrEq(FZero(), distV)) + { + const Vec3V t = V3Scale(worldNormal, mtd); + translation = V3Sub(translation, t); + boxCenter = V3Sub(boxCenter, t); + V3StoreU(boxCenter, box.center); + } + else + { + if(i == 0) + { + //First iteration so keep this normal + hit.distance = 0.f; + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + return true; + } + break; + } + } + + const FloatV translationF = V3Length(translation); + distV = FNeg(translationF); + + const BoolV con = FIsGrtr(translationF, FZero()); + worldNormal = V3Sel(con, V3ScaleInv(translation, translationF), zeroV); + + if(foundInitial) + { + //transform closestA to world space + FStore(distV, &hit.distance); + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + } + return foundInitial; +} + +bool physx::Gu::computeBox_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const Box& _box, const PxTransform& boxTransform, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + bool foundInitial = false; + const PxU32 iterations = 4; + + Ps::Array tempContainer; + tempContainer.reserve(128); + const HeightFieldUtil hfUtil(heightFieldGeom); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + Box box = _box; + + const QuatV q0 = QuatVLoadU(&boxTransform.q.x); + const Vec3V p0 = V3LoadU(&boxTransform.p.x); + + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV minMargin = CalculateMTDBoxMargin(boxExtents); + const FloatV inflationV = FAdd(FLoad(inflation), minMargin); + //const FloatV inflationV = FLoad(inflation); + + PxReal boundInflation; + FStore(inflationV, &boundInflation); + box.extents += PxVec3(boundInflation); + + const BoxV boxV(zeroV, boxExtents); + + Vec3V boxCenter = V3LoadU(box.center); + + //create the polyData based on the original box + PolygonalData polyData; + const PCMPolygonalBox polyBox(_box.extents); + polyBox.getPolygonalData(&polyData); + + const Mat33V identity = M33Identity(); + + const Cm::Matrix34 meshToWorldSkew(pose); + + PsTransformV boxTransformV(p0, q0);//box + + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i boxMap(boxV, boxTransformV, identity, identity, true); + boxMap.setShapeSpaceCenterofMass(zeroV); + // Move to AABB space + Cm::Matrix34 WorldToBox; + computeWorldToBoxMatrix(WorldToBox, box); + const Cm::Matrix34 meshToBox = WorldToBox*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToBox.m.column0), V3LoadU(meshToBox.m.column1), V3LoadU(meshToBox.m.column2)); + const Ps::aos::PsMatTransformV meshToConvex(V3LoadU(meshToBox.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER-1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; k(triMeshGeom.triangleMesh); + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + const PxU8* extraTrigData = triMesh->getExtraTrigData(); + + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + + bool foundInitial = false; + const PxU32 iterations = 2; + + ConvexHullData* hullData = &cm->getHull(); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + const PxVec3 _shapeSpaceCenterOfMass = convexScaling * hullData->mCenterOfMass; + const Vec3V shapeSpaceCenterOfMass = V3LoadU(_shapeSpaceCenterOfMass); + + const QuatV q0 = QuatVLoadU(&convexPose.q.x); + const Vec3V p0 = V3LoadU(&convexPose.p.x); + PsTransformV convexTransformV(p0, q0); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHull(hullData, V3Zero(), vScale, vQuat, idtScaleConvex); + PX_ALIGN(16, PxU8 convexBuff[sizeof(SupportLocalImpl)]); + + const FloatV convexMargin = CalculateMTDConvexMargin(hullData, vScale); + const FloatV inflationV = FAdd(FLoad(inflation), convexMargin); + PxReal boundInflation; + FStore(inflationV, &boundInflation); + + Ps::Array tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + const Cm::Matrix34 meshToWorldSkew = pose * triMeshGeom.scale; + + PolygonalData polyData; + getPCMConvexData(convexHull, idtScaleConvex, polyData); + + FloatV mtd; + FloatV distV; + Vec3V center = p0; + PxTransform tempConvexPose = convexPose; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + + for(PxU32 i=0; i(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl)(static_cast(convexHull), convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex)) : + static_cast(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl)(convexHull, convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex))); + + convexMap->setShapeSpaceCenterofMass(shapeSpaceCenterOfMass); + + Box hullOBB; + computeOBBAroundConvex(hullOBB, convexGeom, cm, tempConvexPose); + + hullOBB.extents += PxVec3(boundInflation); + + midPhaseQuery(triMeshGeom, pose, hullOBB, tempContainer); + + // Get results + const PxU32 nbTriangles = tempContainer.size(); + if(!nbTriangles) + break; + + // Move to AABB space + const Cm::Matrix34 worldToConvex(tempConvexPose.getInverse()); + const Cm::Matrix34 meshToConvex = worldToConvex*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToConvex.m.column0), V3LoadU(meshToConvex.m.column1), V3LoadU(meshToConvex.m.column2)); + const Ps::aos::PsMatTransformV meshToConvexV(V3LoadU(meshToConvex.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER-1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; kgetLocalTriangle(triangles[k], triangleIndex1, triMeshGeom.scale.hasNegativeDeterminant()); + triangles[k].extraTriData = getConvexEdgeFlags(extraTrigData, triangleIndex1); + } + + //ML: mtd has back face culling, so if the capsule's center is below the triangle, we won't generate any contacts + hadContacts = calculateMTD(polyData, convexMap, convexTransformV, meshToConvexV, isDoubleSided, inflationV, triangles, nbTrigs, startIndex, manifoldContacts, numContacts, normal, closestA, closestB, triangleIndex, mtd) || hadContacts; + } + + if(!hadContacts) + break; + + triangleIndex = tempContainer[triangleIndex]; + + foundInitial = true; + + distV = mtd; + worldNormal = convexTransformV.rotate(normal); + worldContactA = convexTransformV.transform(closestA); + if(FAllGrtrOrEq(FZero(), distV)) + { + const Vec3V t = V3Scale(worldNormal, mtd); + translation = V3Sub(translation, t); + center = V3Sub(center, t); + } + else + { + if(i == 0) + { + //First iteration so keep this normal + hit.distance = 0.f; + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + return true; + } + break; + } + } + + + const FloatV translationF = V3Length(translation); + distV = FNeg(translationF); + + const BoolV con = FIsGrtr(translationF, FZero()); + worldNormal = V3Sel(con, V3ScaleInv(translation, translationF), zeroV); + + if(foundInitial) + { + //transform closestA to world space + FStore(distV, &hit.distance); + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + } + return foundInitial; +} + +bool physx::Gu::computeConvex_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit) +{ + using namespace Ps::aos; + + const HeightFieldUtil hfUtil(heightFieldGeom); + + const Vec3V zeroV = V3Zero(); + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + + bool foundInitial = false; + const PxU32 iterations = 2; + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + ConvexHullData* hullData = &cm->getHull(); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + const PxVec3 _shapeSpaceCenterOfMass = convexScaling * hullData->mCenterOfMass; + const Vec3V shapeSpaceCenterOfMass = V3LoadU(_shapeSpaceCenterOfMass); + + const QuatV q0 = QuatVLoadU(&convexPose.q.x); + const Vec3V p0 = V3LoadU(&convexPose.p.x); + PsTransformV convexTransformV(p0, q0); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, idtScaleConvex); + PX_ALIGN(16, PxU8 convexBuff[sizeof(SupportLocalImpl)]); + + const FloatV convexMargin = CalculateMTDConvexMargin(hullData, vScale); + const FloatV inflationV = FAdd(FLoad(inflation), convexMargin); + PxReal boundInflation; + FStore(inflationV, &boundInflation); + + Ps::Array tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + PolygonalData polyData; + getPCMConvexData(convexHull, idtScaleConvex, polyData); + + FloatV mtd; + FloatV distV; + Vec3V center = p0; + PxTransform tempConvexPose = convexPose; + const Cm::Matrix34 meshToWorldSkew(pose); + + for(PxU32 i=0; i(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl)(static_cast(convexHull), convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex)) : + static_cast(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl)(convexHull, convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex))); + + convexMap->setShapeSpaceCenterofMass(shapeSpaceCenterOfMass); + + Box hullOBB; + computeOBBAroundConvex(hullOBB, convexGeom, cm, tempConvexPose); + hullOBB.extents += PxVec3(boundInflation); + + const PxBounds3 bounds = PxBounds3::basisExtent(hullOBB.center, hullOBB.rot, hullOBB.extents); + + midPhaseQuery(hfUtil, pose, bounds, tempContainer, flags); + + // Get results + const PxU32 nbTriangles = tempContainer.size(); + + if(!nbTriangles) + break; + + // Move to AABB space + const Cm::Matrix34 worldToConvex(tempConvexPose.getInverse()); + const Cm::Matrix34 meshToConvex = worldToConvex*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToConvex.m.column0), V3LoadU(meshToConvex.m.column1), V3LoadU(meshToConvex.m.column2)); + const Ps::aos::PsMatTransformV meshToConvexV(V3LoadU(meshToConvex.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER-1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; k d) + { + index = i; + dmin = d; + } + } + hit.normal = plane.n; + hit.distance = dmin; + hit.position = pts[index] - plane.n*dmin; + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool physx::Gu::computePlane_ConvexMTD(const PxPlane& plane, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, PxSweepHit& hit) +{ + const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + const Cm::FastVertex2ShapeScaling convexScaling(convexGeom.scale); + PxU32 nbVerts = convexMesh->getNbVerts(); + const PxVec3* PX_RESTRICT verts = convexMesh->getVerts(); + + PxVec3 worldPointMin = convexPose.transform(convexScaling * verts[0]); + PxReal dmin = plane.distance(worldPointMin); + for(PxU32 i=1;i d) + { + dmin = d; + worldPointMin = worldPoint; + } + } + + hit.normal = plane.n; + hit.distance = dmin; + hit.position = worldPointMin - plane.n * dmin; + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepMTD.h b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.h new file mode 100644 index 000000000..9c63c9c2f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_MTD_H +#define GU_SWEEP_MTD_H + +namespace physx +{ + class PxConvexMeshGeometry; + class PxTriangleMeshGeometry; + class PxGeometry; + class PxHeightFieldGeometry; + +namespace Gu +{ + class Sphere; + class Capsule; + + bool computeCapsule_TriangleMeshMTD(const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, Gu::CapsuleV& capsuleV, PxReal inflatedRadius, bool isDoubleSided, PxSweepHit& hit); + + bool computeCapsule_HeightFieldMTD(const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, Gu::CapsuleV& capsuleV, PxReal inflatedRadius, bool isDoubleSided, const PxU32 flags, PxSweepHit& hit); + + bool computeBox_TriangleMeshMTD(const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, const Gu::Box& box, const PxTransform& boxTransform, PxReal inflation, + bool isDoubleSided, PxSweepHit& hit); + + bool computeBox_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const Gu::Box& box, const PxTransform& boxTransform, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit); + + bool computeConvex_TriangleMeshMTD( const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexTransform, PxReal inflation, + bool isDoubleSided, PxSweepHit& hit); + + bool computeConvex_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexTransform, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit); + + bool computeSphere_SphereMTD(const Sphere& sphere0, const Sphere& sphere1, PxSweepHit& hit); + bool computeSphere_CapsuleMTD(const Sphere& sphere, const Capsule& capsule, PxSweepHit& hit); + + bool computeCapsule_CapsuleMTD(const Capsule& capsule0, const Capsule& capsule1, PxSweepHit& hit); + + bool computePlane_CapsuleMTD(const PxPlane& plane, const Capsule& capsule, PxSweepHit& hit); + bool computePlane_BoxMTD(const PxPlane& plane, const Box& box, PxSweepHit& hit); + bool computePlane_ConvexMTD(const PxPlane& plane, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, PxSweepHit& hit); + + // PT: wrapper just to avoid duplicating these lines. + PX_FORCE_INLINE void setupSweepHitForMTD(PxSweepHit& sweepHit, bool hasContacts, const PxVec3& unitDir) + { + sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + if(!hasContacts) + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + else + { + //ML: touching contact. We need to overwrite the normal to the negative of sweep direction + if (sweepHit.distance == 0.0f) + { + sweepHit.normal = -unitDir; + } + sweepHit.flags |= PxHitFlag::ePOSITION; + + } + } +} + +} + + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp new file mode 100644 index 000000000..f5b3ab00c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp @@ -0,0 +1,725 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTests.h" +#include "GuHeightFieldUtil.h" +#include "CmScaling.h" +#include "GuConvexMesh.h" +#include "GuIntersectionRayPlane.h" +#include "GuVecBox.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuSweepMTD.h" +#include "PxConvexMeshGeometry.h" +#include "PxSphereGeometry.h" +#include "GuSweepSphereCapsule.h" +#include "GuSweepCapsuleCapsule.h" +#include "GuSweepTriangleUtils.h" +#include "GuSweepCapsuleTriangle.h" +#include "GuInternal.h" +#include "GuGJKRaycast.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +static const PxReal gEpsilon = .01f; + +static PxU32 computeSweepConvexPlane( + const PxConvexMeshGeometry& convexGeom, ConvexHullData* hullData, const PxU32& nbPolys, const PxTransform& pose, + const PxVec3& impact_, const PxVec3& unitDir) +{ + PX_ASSERT(nbPolys); + + const PxVec3 impact = impact_ - unitDir * gEpsilon; + + const PxVec3 localPoint = pose.transformInv(impact); + const PxVec3 localDir = pose.rotateInv(unitDir); + + const FastVertex2ShapeScaling scaling(convexGeom.scale); + + PxU32 minIndex = 0; + PxReal minD = PX_MAX_REAL; + for(PxU32 j=0; jmPolygons[j].mPlane; + + PxPlane plane; + scaling.transformPlaneToShapeSpace(pl.n, pl.d, plane.n, plane.d); + + PxReal d = plane.distance(localPoint); + if(d<0.0f) + continue; + + const PxReal tweak = plane.n.dot(localDir) * gEpsilon; + d += tweak; + + if(dmNbPolygons, pose, sweepHit.position, unitDir); + sweepHit.flags |= PxHitFlag::eFACE_INDEX; + } + return true; +} + +static PX_FORCE_INLINE bool hasInitialOverlap(PxSweepHit& sweepHit, const PxVec3& unitDir, + const FloatVArg toi, + const Vec3VArg normal, const Vec3VArg closestA, + const PsTransformV& convexPose, + const bool isMtd, const bool impactPointOnTheOtherShape) +{ + sweepHit.flags = PxHitFlag::eNORMAL; + + const FloatV zero = FZero(); + if(FAllGrtrOrEq(zero, toi)) + { + //ML: initial overlap + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const FloatV length = toi; + const Vec3V worldPointA = convexPose.transform(closestA); + const Vec3V worldNormal = V3Normalize(convexPose.rotate(normal)); + if(impactPointOnTheOtherShape) + { + const Vec3V destWorldPointA = V3NegScaleSub(worldNormal, length, worldPointA); + V3StoreU(worldNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + } + else + { + const Vec3V destNormal = V3Neg(worldNormal); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(worldPointA, sweepHit.position); + } + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + sweepHit.faceIndex = 0xffffffff; + return true; + } + return false; +} + +///////////////////////////////////////////////// sweepCapsule/Sphere ////////////////////////////////////////////////////// +bool sweepCapsule_SphereGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + const PxSphereGeometry& sphereGeom = static_cast(geom); + + const Sphere sphere(pose.p, sphereGeom.radius+inflation); + + if(!sweepSphereCapsule(sphere, lss, -unitDir, distance, sweepHit.distance, sweepHit.position, sweepHit.normal, hitFlags)) + return false; + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + if(isMtd) + { + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + + if(sweepHit.distance == 0.f) + { + //intialOverlap + if(lss.p0 == lss.p1) + { + //sphere + return computeSphere_SphereMTD(sphere, Sphere(lss.p0, lss.radius), sweepHit); + } + else + { + //capsule + return computeSphere_CapsuleMTD(sphere, lss, sweepHit); + } + } + } + else + { + if(sweepHit.distance!=0.0f) + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + else + sweepHit.flags = PxHitFlag::eNORMAL; + } + return true; +} + +bool sweepCapsule_PlaneGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_UNUSED(geom); +// const PxPlaneGeometry& planeGeom = static_cast(geom); + + const PxPlane& worldPlane = getPlane(pose); + + const PxF32 capsuleRadius = lss.radius + inflation; + + PxU32 index = 0; + PxVec3 pts[2]; + + PxReal minDp = PX_MAX_REAL; + + sweepHit.faceIndex = 0xFFFFffff; // spec says face index is undefined for planes + + // Find extreme point on the capsule + // AP: removed if (lss.p0 == lss.p1 clause because it wasn't properly computing minDp) + pts[0] = lss.p0; + pts[1] = lss.p1; + for(PxU32 i=0; i<2; i++) + { + const PxReal dp = pts[i].dot(worldPlane.n); + if(dp 0 && sweepHit.distance <= distance) + { + sweepHit.normal = worldPlane.n; + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + return true; + } + return false; +} + +bool sweepCapsule_CapsuleGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + Capsule staticCapsule; + getCapsule(staticCapsule, capsuleGeom, pose); + staticCapsule.radius +=inflation; + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + PxU16 outFlags; + if(!sweepCapsuleCapsule(lss, staticCapsule, -unitDir, distance, sweepHit.distance, sweepHit.position, sweepHit.normal, hitFlags, outFlags)) + return false; + + sweepHit.flags = PxHitFlags(outFlags); + if(sweepHit.distance == 0.0f) + { + //initial overlap + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + return computeCapsule_CapsuleMTD(lss, staticCapsule, sweepHit); + } + } + return true; +} + +bool sweepCapsule_ConvexGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + + using namespace Ps::aos; + + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + const PxConvexMeshGeometry& convexGeom = static_cast(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + const FloatV dist = FLoad(distance); + const Vec3V worldDir = V3LoadU(unitDir); + + const PsTransformV capPose = loadTransformU(capsulePose_); + const PsTransformV convexPose = loadTransformU(pose); + + const PsMatTransformV aToB(convexPose.transformInv(capPose)); + + const FloatV capsuleHalfHeight = FLoad(capsuleGeom_.halfHeight); + const FloatV capsuleRadius = FLoad(lss.radius); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + CapsuleV capsule(aToB.p, aToB.rotate( V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + + const Vec3V dir = convexPose.rotateInv(V3Neg(V3Scale(worldDir, dist))); + + bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of convex hull + LocalConvex convexA(capsule); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(!gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, lss.radius + inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, true)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = convexPose.transform(closestA); + const FloatV length = FMul(dist, toi); + const Vec3V destNormal = V3Normalize(convexPose.rotate(normal)); + const Vec3V destWorldPointA = V3ScaleAdd(worldDir, length, worldPointA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + + return computeFaceIndex(sweepHit, hitFlags, convexGeom, hullData, pose, unitDir); +} + +///////////////////////////////////////////////// sweepBox ////////////////////////////////////////////////////// + +bool sweepBox_PlaneGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_UNUSED(geom); + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + +// const PxPlaneGeometry& planeGeom = static_cast(geom); + + sweepHit.faceIndex = 0xFFFFffff; // spec says face index is undefined for planes + + PxPlane worldPlane = getPlane(pose); + worldPlane.d -=inflation; + + // Find extreme point on the box + PxVec3 boxPts[8]; + box.computeBoxPoints(boxPts); + PxU32 index = 0; + PxReal minDp = PX_MAX_REAL; + for(PxU32 i=0;i<8;i++) + { + const PxReal dp = boxPts[i].dot(worldPlane.n); + + if(dp 0 && sweepHit.distance <= distance) + { + sweepHit.normal = worldPlane.n; + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + return true; + } + return false; +} + +bool sweepBox_ConvexGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(boxGeom_); + + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + const PxConvexMeshGeometry& convexGeom = static_cast(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + + const PsTransformV boxPose = loadTransformU(boxPose_); + const PsTransformV convexPose = loadTransformU(pose); + + const PsMatTransformV aToB(convexPose.transformInv(boxPose)); + + const Vec3V boxExtents = V3LoadU(box.extents); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + BoxV boxV(zeroV, boxExtents); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const Vec3V dir = convexPose.rotateInv(V3Neg(V3Scale(worldDir, dist))); + + bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal; + RelativeConvex convexA(boxV, aToB); + LocalConvex convexB(convexHull); + if(!gjkRaycastPenetration< RelativeConvex,LocalConvex >(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, true)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destNormal = V3Normalize(convexPose.rotate(normal)); + const FloatV length = FMul(dist, toi); + const Vec3V worldPointA = convexPose.transform(closestA); + const Vec3V destWorldPointA = V3ScaleAdd(worldDir, length, worldPointA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + + return computeFaceIndex(sweepHit, hitFlags, convexGeom, hullData, pose, unitDir); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Gu::sweepCapsuleTriangles(GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxCapsuleGeometry)) +{ + Capsule capsule; + getCapsule(capsule, geom, pose); + capsule.radius +=inflation; + + // Compute swept box + Box capsuleBox; + computeBoxAroundCapsule(capsule, capsuleBox); + + BoxPadded sweptBounds; + computeSweptBox(sweptBounds, capsuleBox.extents, capsuleBox.center, capsuleBox.rot, unitDir, distance); + + PxVec3 triNormal; + return sweepCapsuleTriangles_Precise(nbTris, triangles, capsule, unitDir, distance, cachedIndex, hit, triNormal, hitFlags, doubleSided, &sweptBounds); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sweepConvex_SphereGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + const PxSphereGeometry& sphereGeom = static_cast(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero= FZero(); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + const FloatV sphereRadius = FLoad(sphereGeom.radius); + + const PsTransformV sphereTransf = loadTransformU(pose); + const PsTransformV convexTransf = loadTransformU(convexPose); + + const PsMatTransformV aToB(convexTransf.transformInv(sphereTransf)); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const Vec3V dir = convexTransf.rotateInv(V3Scale(worldDir, dist)); + + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + //CapsuleV capsule(zeroV, sphereRadius); + CapsuleV capsule(aToB.p, sphereRadius); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal; + LocalConvex convexA(capsule); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(!gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, sphereGeom.radius+inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, false)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destNormal = V3Neg(V3Normalize(convexTransf.rotate(normal))); + const FloatV length = FMul(dist, toi); + const Vec3V destWorldPointA = convexTransf.transform(closestA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + sweepHit.faceIndex = 0xffffffff; + return true; +} + +bool sweepConvex_PlaneGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_UNUSED(hitFlags); + PX_UNUSED(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + sweepHit.faceIndex = 0xFFFFffff; // spec says face index is undefined for planes + + const PxVec3* PX_RESTRICT hullVertices = hullData->getHullVertices(); + PxU32 numHullVertices = hullData->mNbHullVertices; + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + const FastVertex2ShapeScaling convexScaling(convexGeom.scale); + + PxPlane plane = getPlane(pose); + plane.d -=inflation; + + sweepHit.distance = distance; + bool status = false; + bool initialOverlap = false; + while(numHullVertices--) + { + const PxVec3& vertex = *hullVertices++; + const PxVec3 worldPt = convexPose.transform(convexScaling * vertex); + float t; + PxVec3 pointOnPlane; + if(intersectRayPlane(worldPt, unitDir, plane, t, &pointOnPlane)) + { + if(plane.distance(worldPt) <= 0.0f) + { + initialOverlap = true; + break; + //// Convex touches plane + //sweepHit.distance = 0.0f; + //sweepHit.flags = PxHitFlag::eNORMAL; + //sweepHit.normal = -unitDir; + //return true; + } + + if(t > 0.0f && t <= sweepHit.distance) + { + sweepHit.distance = t; + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + sweepHit.position = pointOnPlane; + sweepHit.normal = plane.n; + status = true; + } + } + } + + if(initialOverlap) + { + if(isMtd) + { + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + return computePlane_ConvexMTD(plane, convexGeom, convexPose, sweepHit); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.flags = PxHitFlag::eNORMAL; + sweepHit.normal = -unitDir; + return true; + } + } + return status; +} + +bool sweepConvex_CapsuleGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + Capsule capsule; + getCapsule(capsule, capsuleGeom, pose); + + // remove PxHitFlag::eFACE_INDEX, not neeeded to compute. + PxHitFlags tempHitFlags = hitFlags; + tempHitFlags &= ~PxHitFlag::eFACE_INDEX; + + if(!sweepCapsule_ConvexGeom(convexGeom, convexPose, capsuleGeom, pose, capsule, -unitDir, distance, sweepHit, tempHitFlags, inflation)) + return false; + + if(sweepHit.flags & PxHitFlag::ePOSITION) + sweepHit.position += unitDir * sweepHit.distance; + + sweepHit.normal = -sweepHit.normal; + sweepHit.faceIndex = 0xffffffff; + return true; +} + +bool sweepConvex_BoxGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + const PxBoxGeometry& boxGeom = static_cast(geom); + + Box box; + buildFrom(box, pose.p, boxGeom.halfExtents, pose.q); + + // remove PxHitFlag::eFACE_INDEX, not neeeded to compute. + PxHitFlags tempHitFlags = hitFlags; + tempHitFlags &= ~PxHitFlag::eFACE_INDEX; + + if(!sweepBox_ConvexGeom(convexGeom, convexPose, boxGeom, pose, box, -unitDir, distance, sweepHit, tempHitFlags, inflation)) + return false; + + if(sweepHit.flags & PxHitFlag::ePOSITION) + sweepHit.position += unitDir * sweepHit.distance; + + sweepHit.normal = -sweepHit.normal; + sweepHit.faceIndex = 0xffffffff; + return true; +} + +bool sweepConvex_ConvexGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + const PxConvexMeshGeometry& otherConvexGeom = static_cast(geom); + ConvexMesh& otherConvexMesh = *static_cast(otherConvexGeom.convexMesh); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + ConvexHullData* otherHullData = &otherConvexMesh.getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + + const Vec3V otherVScale = V3LoadU_SafeReadW(otherConvexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV otherVQuat = QuatVLoadU(&otherConvexGeom.scale.rotation.x); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + const PsTransformV otherTransf = loadTransformU(pose); + const PsTransformV convexTransf = loadTransformU(convexPose); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const Vec3V dir = convexTransf.rotateInv(V3Scale(worldDir, dist)); + + const PsMatTransformV aToB(convexTransf.transformInv(otherTransf)); + + ConvexHullV otherConvexHull(otherHullData, zeroV, otherVScale, otherVQuat, otherConvexGeom.scale.isIdentity()); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal; + RelativeConvex convexA(otherConvexHull, aToB); + LocalConvex convexB(convexHull); + if(!gjkRaycastPenetration< RelativeConvex, LocalConvex >(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, false)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = convexTransf.transform(closestA); + const Vec3V destNormal = V3Neg(V3Normalize(convexTransf.rotate(normal))); + const FloatV length = FMul(dist, toi); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(worldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + + return computeFaceIndex(sweepHit, hitFlags, otherConvexGeom, otherHullData, pose, unitDir); +} diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h new file mode 100644 index 000000000..81c1ad2bd --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h @@ -0,0 +1,56 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SHARED_TESTS_H +#define GU_SWEEP_SHARED_TESTS_H + +#include "CmPhysXCommon.h" +#include "GuBoxConversion.h" + +namespace physx +{ +PX_FORCE_INLINE void computeWorldToBoxMatrix(physx::Cm::Matrix34& worldToBox, const physx::Gu::Box& box) +{ + physx::Cm::Matrix34 boxToWorld; + physx::buildMatrixFromBox(boxToWorld, box); + worldToBox = boxToWorld.getInverseRT(); +} + +PX_FORCE_INLINE PxU32 getTriangleIndex(PxU32 i, PxU32 cachedIndex) +{ + PxU32 triangleIndex; + if(i==0) triangleIndex = cachedIndex; + else if(i==cachedIndex) triangleIndex = 0; + else triangleIndex = i; + return triangleIndex; +} +} + + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp b/src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp new file mode 100644 index 000000000..8f213b38b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp @@ -0,0 +1,604 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTests.h" +#include "PxSphereGeometry.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" +#include "GuVecTriangle.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" +#include "PsFoundation.h" +#include "GuGJKRaycast.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +bool sweepCapsule_BoxGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(hitFlags); + + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + const PxBoxGeometry& boxGeom = static_cast(geom); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents0 = V3LoadU(boxGeom.halfExtents); + const FloatV dist = FLoad(distance); + const Vec3V worldDir = V3LoadU(unitDir); + + const PsTransformV capPos = loadTransformU(capsulePose_); + const PsTransformV boxPos = loadTransformU(pose); + + const PsMatTransformV aToB(boxPos.transformInv(capPos)); + + const FloatV capsuleHalfHeight = FLoad(capsuleGeom_.halfHeight); + const FloatV capsuleRadius = FLoad(lss.radius); + + BoxV box(zeroV, boxExtents0); + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + const Vec3V dir = boxPos.rotateInv(V3Neg(V3Scale(worldDir, dist))); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi = FMax(); + Vec3V closestA, normal;//closestA and normal is in the local space of box + LocalConvex convexA(capsule); + LocalConvex convexB(box); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), box.getCenter()); + if(!gjkRaycastPenetration, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, + closestA, lss.radius + inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + if(FAllGrtrOrEq(zero, toi)) + { + //initial overlap + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + const FloatV length = toi; + const Vec3V destWorldPointA = V3NegScaleSub(destNormal, length, worldPointA); + V3StoreU(destWorldPointA, sweepHit.position); + V3StoreU(destNormal, sweepHit.normal); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + const FloatV length = FMul(dist, toi); + const Vec3V destWorldPointA = V3ScaleAdd(worldDir, length, worldPointA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sweepBox_SphereGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + PX_UNUSED(hitFlags); + PX_UNUSED(boxGeom_); + + const PxSphereGeometry& sphereGeom = static_cast(geom); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV worldDist = FLoad(distance); + const Vec3V unitDirV = V3LoadU(unitDir); + + const FloatV sphereRadius = FLoad(sphereGeom.radius); + + const PsTransformV spherePos = loadTransformU(pose); + const PsTransformV boxPos = loadTransformU(boxPose_); + + const PsMatTransformV aToB(boxPos.transformInv(spherePos)); + + BoxV boxV(zeroV, boxExtents); + CapsuleV capsuleV(aToB.p, sphereRadius); + + //transform into b space + const Vec3V dir = boxPos.rotateInv(V3Scale(unitDirV, worldDist)); + + bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + const Vec3V initialSearchDir = V3Sub(capsuleV.getCenter(), boxV.getCenter()); + LocalConvex convexA(capsuleV); + LocalConvex convexB(boxV); + if(!gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, sphereGeom.radius+inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + + //initial overlap + if(FAllGrtrOrEq(zero, toi)) + { + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = V3Neg(boxPos.rotate(normal)); + const FloatV length = toi; + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = V3Neg(boxPos.rotate(normal)); + const FloatV length = FMul(worldDist, toi); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +bool sweepBox_CapsuleGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + PX_UNUSED(hitFlags); + PX_UNUSED(boxGeom_); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + const FloatV capsuleHalfHeight = FLoad(capsuleGeom.halfHeight); + const FloatV capsuleRadius = FLoad(capsuleGeom.radius); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV worldDist = FLoad(distance); + const Vec3V unitDirV = V3LoadU(unitDir); + + const PsTransformV capPos = loadTransformU(pose); + const PsTransformV boxPos = loadTransformU(boxPose_); + + const PsMatTransformV aToB(boxPos.transformInv(capPos)); + + BoxV boxV(zeroV, boxExtents); + CapsuleV capsuleV(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + //transform into b space + const Vec3V dir = boxPos.rotateInv(V3Scale(unitDirV, worldDist)); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + const Vec3V initialSearchDir = V3Sub(capsuleV.getCenter(), boxV.getCenter()); + LocalConvex convexA(capsuleV); + LocalConvex convexB(boxV); + if(!gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, capsuleGeom.radius+inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + + //initial overlap + if(FAllGrtrOrEq(zero, toi)) + { + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + //initial overlap is toi < 0 + const FloatV length = toi; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + return true; + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + const FloatV length = FMul(worldDist, toi); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +bool sweepBox_BoxGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_UNUSED(boxGeom_); + + const PxBoxGeometry& boxGeom = static_cast(geom); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents0 = V3LoadU(boxGeom.halfExtents); + const Vec3V boxExtents1 = V3LoadU(box.extents); + const FloatV worldDist = FLoad(distance); + const Vec3V unitDirV = V3LoadU(unitDir); + + const PsTransformV boxTrans0 = loadTransformU(pose); + const PsTransformV boxTrans1 = loadTransformU(boxPose_); + + const PsMatTransformV aToB(boxTrans1.transformInv(boxTrans0)); + + BoxV box0(zeroV, boxExtents0); + BoxV box1(zeroV, boxExtents1); + + //transform into b space + const Vec3V dir = boxTrans1.rotateInv(V3Scale(unitDirV, worldDist)); + const bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + RelativeConvex convexA(box0, aToB); + LocalConvex convexB(box1); + if(!gjkRaycastPenetration, LocalConvex >(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + if(FAllGrtrOrEq(zero, toi)) + { + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const FloatV length = toi; + const Vec3V destWorldPointA = boxTrans1.transform(closestA); + const Vec3V destNormal = V3Normalize(boxTrans1.rotate(normal)); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxTrans1.transform(closestA); + const Vec3V destNormal = V3Normalize(boxTrans1.rotate(normal)); + const FloatV length = FMul(worldDist, toi); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +bool Gu::sweepBoxTriangles(GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxBoxGeometry)) +{ + PX_UNUSED(hitFlags); + + if(!nbTris) + return false; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool doBackfaceCulling = !doubleSided && !meshBothSides; + + Box box; + buildFrom(box, pose.p, geom.halfExtents, pose.q); + + PxSweepHit sweepHit; + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localMotion = localDir * distance; + + const Vec3V base0 = V3LoadU(worldToBox.m.column0); + const Vec3V base1 = V3LoadU(worldToBox.m.column1); + const Vec3V base2 = V3LoadU(worldToBox.m.column2); + const Mat33V matV(base0, base1, base2); + const Vec3V p = V3LoadU(worldToBox.p); + const PsMatTransformV worldToBoxV(p, matV); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(box.extents); + const Vec3V boxDir = V3LoadU(localDir); + const FloatV inflationV = FLoad(inflation); + const Vec3V absBoxDir = V3Abs(boxDir); + const FloatV boxRadiusV = FAdd(V3Dot(absBoxDir, boxExtents), inflationV); + BoxV boxV(zeroV, boxExtents); + +#if PX_DEBUG + PxU32 totalTestsExpected = nbTris; + PxU32 totalTestsReal = 0; + PX_UNUSED(totalTestsExpected); + PX_UNUSED(totalTestsReal); +#endif + + Vec3V boxLocalMotion = V3LoadU(localMotion); + Vec3V minClosestA = zeroV, minNormal = zeroV; + PxU32 minTriangleIndex = 0; + PxVec3 bestTriNormal(0.0f); + FloatV dist = FLoad(distance); + + const PsTransformV boxPos = loadTransformU(pose); + + bool status = false; + + const PxU32 idx = cachedIndex ? *cachedIndex : 0; + + for(PxU32 ii=0;ii convexA(triangleV); + LocalConvex convexB(boxV); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter()); + if(gjkRaycastPenetration, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, boxLocalMotion, lambda, normal, closestA, inflation, false)) + { + //hitCount++; + + if(FAllGrtrOrEq(zero, lambda)) + { + hit.distance = 0.0f; + hit.faceIndex = triangleIndex; + hit.normal = -unitDir; + hit.flags = PxHitFlag::eNORMAL; + return true; + } + + dist = FMul(dist, lambda); + boxLocalMotion = V3Scale(boxDir, dist); + minClosestA = closestA; + minNormal = normal; + minTriangleIndex = triangleIndex; + V3StoreU(triNormal, bestTriNormal); + status = true; + if(hitFlags & PxHitFlag::eMESH_ANY) + break; + } + } + + if(!status) + return false; + + hit.faceIndex = minTriangleIndex; + const Vec3V destNormal = V3Neg(V3Normalize(boxPos.rotate(minNormal))); + const Vec3V destWorldPointA = boxPos.transform(minClosestA); + V3StoreU(destNormal, hit.normal); + V3StoreU(destWorldPointA, hit.position); + FStore(dist, &hit.distance); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(hit.normal, meshBothSides, doubleSided, bestTriNormal, unitDir)) + hit.normal = -hit.normal; + + hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL; + return true; +} + +bool sweepCapsule_SphereGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_PlaneGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_CapsuleGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_BoxGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_BoxGeom_Precise (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_ConvexGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_MeshGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_HeightFieldGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); + +bool sweepBox_SphereGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_SphereGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_PlaneGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_CapsuleGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_CapsuleGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_BoxGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_BoxGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_ConvexGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_MeshGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_HeightFieldGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_HeightFieldGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS); + +bool sweepConvex_SphereGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_PlaneGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_CapsuleGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_BoxGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_ConvexGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_MeshGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_HeightFieldGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); + +static bool sweepCapsule_HeightfieldUnregistered(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(lss); + PX_UNUSED(unitDir); + PX_UNUSED(distance); + PX_UNUSED(sweepHit); + PX_UNUSED(hitFlags); + PX_UNUSED(inflation); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Sweep test called with height fields unregistered "); + return false; +} +static bool sweepBox_HeightfieldUnregistered(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(box); + PX_UNUSED(unitDir); + PX_UNUSED(distance); + PX_UNUSED(sweepHit); + PX_UNUSED(hitFlags); + PX_UNUSED(inflation); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Sweep test called with height fields unregistered "); + return false; +} +static bool sweepConvex_HeightfieldUnregistered(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(convexGeom); + PX_UNUSED(convexPose); + PX_UNUSED(unitDir); + PX_UNUSED(distance); + PX_UNUSED(sweepHit); + PX_UNUSED(hitFlags); + PX_UNUSED(inflation); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Sweep test called with height fields unregistered "); + return false; +} + +Gu::GeomSweepFuncs gGeomSweepFuncs = +{ + { + sweepCapsule_SphereGeom, + sweepCapsule_PlaneGeom, + sweepCapsule_CapsuleGeom, + sweepCapsule_BoxGeom, + sweepCapsule_ConvexGeom, + sweepCapsule_MeshGeom, + sweepCapsule_HeightfieldUnregistered + }, + { + sweepCapsule_SphereGeom, + sweepCapsule_PlaneGeom, + sweepCapsule_CapsuleGeom, + sweepCapsule_BoxGeom_Precise, + sweepCapsule_ConvexGeom, + sweepCapsule_MeshGeom , + sweepCapsule_HeightfieldUnregistered + }, + { + sweepBox_SphereGeom, + sweepBox_PlaneGeom, + sweepBox_CapsuleGeom, + sweepBox_BoxGeom, + sweepBox_ConvexGeom, + sweepBox_MeshGeom, + sweepBox_HeightfieldUnregistered + }, + { + sweepBox_SphereGeom_Precise, + sweepBox_PlaneGeom, + sweepBox_CapsuleGeom_Precise, + sweepBox_BoxGeom_Precise, + sweepBox_ConvexGeom, + sweepBox_MeshGeom, + sweepBox_HeightfieldUnregistered + }, + { + sweepConvex_SphereGeom, // 0 + sweepConvex_PlaneGeom, // 1 + sweepConvex_CapsuleGeom, // 2 + sweepConvex_BoxGeom, // 3 + sweepConvex_ConvexGeom, // 4 + sweepConvex_MeshGeom, // 5 + sweepConvex_HeightfieldUnregistered // 6 + } +}; + +PX_PHYSX_COMMON_API const GeomSweepFuncs& Gu::getSweepFuncTable() +{ + return gGeomSweepFuncs; +} + +void registerHeightFields_Sweeps() +{ + gGeomSweepFuncs.capsuleMap[PxGeometryType::eHEIGHTFIELD] = sweepCapsule_HeightFieldGeom; + gGeomSweepFuncs.preciseCapsuleMap[PxGeometryType::eHEIGHTFIELD] = sweepCapsule_HeightFieldGeom; + gGeomSweepFuncs.boxMap[PxGeometryType::eHEIGHTFIELD] = sweepBox_HeightFieldGeom; + gGeomSweepFuncs.preciseBoxMap[PxGeometryType::eHEIGHTFIELD] = sweepBox_HeightFieldGeom_Precise; + gGeomSweepFuncs.convexMap[PxGeometryType::eHEIGHTFIELD] = sweepConvex_HeightFieldGeom; +} diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepTests.h b/src/PhysX/physx/source/geomutils/src/GuSweepTests.h new file mode 100644 index 000000000..a77fcd3d1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepTests.h @@ -0,0 +1,136 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_TESTS_H +#define GU_SWEEP_TESTS_H + +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" +#include "PxGeometry.h" + +namespace physx +{ + class PxConvexMeshGeometry; + class PxCapsuleGeometry; + class PxTriangle; + class PxBoxGeometry; + + // PT: TODO: unify this with raycast calls (names and order of params) + + // PT: we use defines to be able to quickly change the signature of all sweep functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] geom geometry object to sweep against + // \param[in] pose pose of geometry object + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] sweepHit hit result + // \param[in] hitFlags query behavior flags + // \param[in] inflation optional inflation value for swept shape + + // PT: sweep parameters for capsule + #define GU_CAPSULE_SWEEP_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxCapsuleGeometry& capsuleGeom_, const PxTransform& capsulePose_, const Gu::Capsule& lss, \ + const PxVec3& unitDir, PxReal distance, \ + PxSweepHit& sweepHit, const PxHitFlags hitFlags, PxReal inflation + + // PT: sweep parameters for box + #define GU_BOX_SWEEP_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxBoxGeometry& boxGeom_, const PxTransform& boxPose_, const Gu::Box& box, \ + const PxVec3& unitDir, PxReal distance, \ + PxSweepHit& sweepHit, const PxHitFlags hitFlags, PxReal inflation + + // PT: sweep parameters for convex + #define GU_CONVEX_SWEEP_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, \ + const PxVec3& unitDir, PxReal distance, \ + PxSweepHit& sweepHit, const PxHitFlags hitFlags, PxReal inflation + namespace Gu + { + class Capsule; + class Box; + + // PT: function pointer for Geom-indexed capsule sweep functions + // See GU_CAPSULE_SWEEP_FUNC_PARAMS for function parameters details. + // \return true if a hit was found, false otherwise + typedef bool (*SweepCapsuleFunc) (GU_CAPSULE_SWEEP_FUNC_PARAMS); + + // PT: function pointer for Geom-indexed box sweep functions + // See GU_BOX_SWEEP_FUNC_PARAMS for function parameters details. + // \return true if a hit was found, false otherwise + typedef bool (*SweepBoxFunc) (GU_BOX_SWEEP_FUNC_PARAMS); + + // PT: function pointer for Geom-indexed box sweep functions + // See GU_CONVEX_SWEEP_FUNC_PARAMS for function parameters details. + // \return true if a hit was found, false otherwise + typedef bool (*SweepConvexFunc) (GU_CONVEX_SWEEP_FUNC_PARAMS); + + // PT: typedef for bundles of all sweep functions, i.e. the function tables themselves (indexed by geom-type). + typedef SweepCapsuleFunc GeomSweepCapsuleTable [PxGeometryType::eGEOMETRY_COUNT]; + typedef SweepBoxFunc GeomSweepBoxTable [PxGeometryType::eGEOMETRY_COUNT]; + typedef SweepConvexFunc GeomSweepConvexTable [PxGeometryType::eGEOMETRY_COUNT]; + + struct GeomSweepFuncs + { + GeomSweepCapsuleTable capsuleMap; + GeomSweepCapsuleTable preciseCapsuleMap; + GeomSweepBoxTable boxMap; + GeomSweepBoxTable preciseBoxMap; + GeomSweepConvexTable convexMap; + }; + // PT: grabs all sweep function tables at once (for access by external non-Gu modules) + PX_PHYSX_COMMON_API const GeomSweepFuncs& getSweepFuncTable(); + + // PT: signature for sweep-vs-triangles functions. + // We use defines to be able to quickly change the signature of all sweep functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] nbTris number of triangles in input array + // \param[in] triangles array of triangles to sweep the shape against + // \param[in] doubleSided true if input triangles are double-sided + // \param[in] x geom to sweep against input triangles + // \param[in] pose pose of geom x + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] hit hit result + // \param[in] cachedIndex optional initial triangle index (must be +{ + PX_NOCOPY(AccumCallback) + public: + + Ps::InlineArray& mResult; + AccumCallback(Ps::InlineArray& result) + : MeshHitCallback(CallbackMode::eMULTIPLE), + mResult(result) + { + } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3&, const PxVec3&, const PxVec3&, PxReal&, const PxU32*) + { + mResult.pushBack(hit.faceIndex); + return true; + } +}; + +// PT: TODO: refactor with MidPhaseQueryLocalReport +struct EntityReportContainerCallback : public EntityReport +{ + Ps::InlineArray& container; + EntityReportContainerCallback(Ps::InlineArray& container_) : container(container_) + { + container.forceSize_Unsafe(0); + } + virtual ~EntityReportContainerCallback() {} + + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + for(PxU32 i=0; iget()); + + Ps::InlineArray tempContainer; + + EntityReportContainerCallback callback(tempContainer); + + PxVec3 trA = transform0.p - lastTm0.p; + PxVec3 trB = transform1.p - lastTm1.p; + + PxVec3 relTr = trA - trB; + PxVec3 halfRelTr = relTr * 0.5f; + + const PxVec3 ext = shape0.mExtents + halfRelTr.abs() + PxVec3(restDistance); + const PxVec3 cent = shape0.mCenter + halfRelTr; + + PxBounds3 bounds0(cent - ext, cent + ext); + + hfUtil.overlapAABBTriangles(transform1, bounds0, GuHfQueryFlags::eWORLD_SPACE, &callback); + + Ps::Array orderedContainer(tempContainer.size()); + + Ps::Array distanceEntries(tempContainer.size()); + + PxU32* orderedList = orderedContainer.begin(); + PxF32* distances = reinterpret_cast(distanceEntries.begin()); + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents + PxVec3(restDistance); + + PxReal minTOI = PX_MAX_REAL; + + PxU32 numTrigs = tempContainer.size(); + PxU32* trianglesIndices = tempContainer.begin(); + + PxU32 count = 0; + for(PxU32 a = 0; a < numTrigs; ++a) + { + PxTriangle tri; + hfUtil.getTriangle(shape1.mPrevTransform, tri, 0, 0, trianglesIndices[a], true, true); + + PxVec3 resultNormal = -(tri.verts[1]-tri.verts[0]).cross(tri.verts[2]-tri.verts[0]); + resultNormal.normalize(); + + if(relTr.dot(resultNormal) >= fastMovingThreshold) + { + + PxBounds3 bounds; + bounds.setEmpty(); + bounds.include(tri.verts[0]); + bounds.include(tri.verts[1]); + bounds.include(tri.verts[2]); + + PxF32 toi = sweepAABBAABB(origin, extent * 1.1f, bounds.getCenter(), (bounds.getExtents() + PxVec3(0.01f, 0.01f, 0.01f)) * 1.1f, trA, trB); + + PxU32 index = 0; + if(toi <= 1.f) + { + for(PxU32 b = count; b > 0; --b) + { + if(distances[b-1] <= toi) + { + //shuffle down and swap + index = b; + break; + } + PX_ASSERT(b > 0); + PX_ASSERT(b < numTrigs); + distances[b] = distances[b-1]; + orderedList[b] = orderedList[b-1]; + } + PX_ASSERT(index < numTrigs); + orderedList[index] = trianglesIndices[a]; + distances[index] = toi; + count++; + } + } + } + + + + worldNormal = PxVec3(PxReal(0)); + worldPoint = PxVec3(PxReal(0)); + Cm::FastVertex2ShapeScaling idScale; + PxU32 ccdFaceIndex = PXC_CONTACT_NO_FACE_INDEX; + + PxVec3 sphereCenter(shape0.mPrevTransform.p); + PxF32 inSphereRadius = shape0.mFastMovingThreshold; + PxF32 inRadSq = inSphereRadius * inSphereRadius; + + + PxVec3 sphereCenterInTr1 = transform1.transformInv(sphereCenter); + PxVec3 sphereCenterInTr1T0 = transform1.transformInv(lastTm0.p); + + PxVec3 tempWorldNormal(0.f), tempWorldPoint(0.f); + + for (PxU32 ti = 0; ti < count; ti++) + { + PxTriangle tri; + hfUtil.getTriangle(lastTm1, tri, 0, 0, orderedList[ti], false, false); + + PxVec3 resultNormal, resultPoint; + + TriangleV triangle(V3LoadU(tri.verts[0]), V3LoadU(tri.verts[1]), V3LoadU(tri.verts[2])); + + //do sweep + + PxReal res = SweepShapeTriangle( + *shape0.mGeometry, *shape1.mGeometry, transform0, transform1, lastTm0, lastTm1, restDistance, + resultNormal, resultPoint, Cm::FastVertex2ShapeScaling(), triangle, + 0.f); + + if(res <= 0.f) + { + res = 0.f; + + const PxVec3 v0 = tri.verts[1] - tri.verts[0] ; + const PxVec3 v1 = tri.verts[2] - tri.verts[0]; + + //Now we have a 0 TOI, lets see if the in-sphere hit it! + + PxF32 distanceSq = distancePointTriangleSquared( sphereCenterInTr1, tri.verts[0], v0, v1); + + if(distanceSq < inRadSq) + { + const PxVec3 nor = v0.cross(v1); + const PxF32 distance = PxSqrt(distanceSq); + res = distance - inSphereRadius; + const PxF32 d = nor.dot(tri.verts[0]); + const PxF32 dd = nor.dot(sphereCenterInTr1T0); + if((dd - d) > 0.f) + { + //back side, penetration + res = -(2.f * inSphereRadius - distance); + } + } + } + + if (res < minTOI) + { + const PxVec3 v0 = tri.verts[1] - tri.verts[0] ; + const PxVec3 v1 = tri.verts[2] - tri.verts[0]; + + PxVec3 resultNormal1 = v0.cross(v1); + resultNormal1.normalize(); + //if(norDotRel > 1e-6f) + { + tempWorldNormal = resultNormal1; + tempWorldPoint = resultPoint; + minTOI = res; + ccdFaceIndex = orderedList[ti]; + } + } + + } + + worldNormal = transform1.rotate(tempWorldNormal); + worldPoint = tempWorldPoint; + + outCCDFaceIndex = ccdFaceIndex; + + return minTOI; +} + + +PxReal SweepEstimateAnyShapeHeightfield(GU_SWEEP_ESTIMATE_ARGS) +{ + HeightFieldUtil hfUtil(shape1.mGeometry->get()); + + Ps::InlineArray tempContainer; + + EntityReportContainerCallback callback(tempContainer); + + PxVec3 trA = transform0.p - lastTr0.p; + PxVec3 trB = transform1.p - lastTr1.p; + + PxVec3 relTr = trA - trB; + PxVec3 halfRelTr = relTr * 0.5f; + + const PxVec3 extents = shape0.mExtents + halfRelTr.abs() + PxVec3(restDistance); + const PxVec3 center = shape0.mCenter + halfRelTr; + + + PxBounds3 bounds0(center - extents, center + extents); + + + hfUtil.overlapAABBTriangles(transform1, bounds0, GuHfQueryFlags::eWORLD_SPACE, &callback); + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents; + + PxReal minTOI = PX_MAX_REAL; + + PxU32 numTrigs = tempContainer.size(); + PxU32* trianglesIndices = tempContainer.begin(); + + for(PxU32 a = 0; a < numTrigs; ++a) + { + + PxTriangle tri; + hfUtil.getTriangle(shape1.mPrevTransform, tri, 0, 0, trianglesIndices[a], true, true); + + + + PxVec3 resultNormal = -(tri.verts[1]-tri.verts[0]).cross(tri.verts[2]-tri.verts[0]); + resultNormal.normalize(); + + if(relTr.dot(resultNormal) >= fastMovingThreshold) + { + + PxBounds3 bounds; + bounds.setEmpty(); + bounds.include(tri.verts[0]); + bounds.include(tri.verts[1]); + bounds.include(tri.verts[2]); + + PxF32 toi = sweepAABBAABB(origin, extent * 1.1f, bounds.getCenter(), (bounds.getExtents() + PxVec3(0.01f, 0.01f, 0.01f)) * 1.1f, trA, trB); + + minTOI = PxMin(minTOI, toi); + } + } + + return minTOI; +} + + + +PxReal SweepAnyShapeMesh(GU_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(toiEstimate); + // this is the trimesh midphase for convex vs mesh sweep. shape0 is the convex shape. + + // Get actual shape data + const PxTriangleMeshGeometryLL& shapeMesh = shape1.mGeometry->get(); + + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + /*---------------------------------------------------*\ + | + | STEP1: OPCODE Geometry collection + | + \*---------------------------------------------------*/ + + PxVec3 trA = transform0.p - lastTm0.p; + PxVec3 trB = transform1.p - lastTm1.p; + + PxVec3 relTr = trA - trB; + PxVec3 unitDir = relTr; + PxReal length = unitDir.normalize(); + + PxMat33 matRot(PxIdentity); + + + //1) Compute the swept bounds + Box sweptBox; + computeSweptBox(sweptBox, shape0.mExtents, shape0.mCenter, matRot, unitDir, length); + + Box vertexSpaceBox; + if (shapeMesh.scale.isIdentity()) + vertexSpaceBox = transformBoxOrthonormal(sweptBox, transform1.getInverse()); + else + computeVertexSpaceOBB(vertexSpaceBox, sweptBox, transform1, shapeMesh.scale); + + + vertexSpaceBox.extents += PxVec3(restDistance); + + Ps::InlineArray tempContainer; + + AccumCallback callback(tempContainer); + + // AP scaffold: early out opportunities, should probably use fat raycast + Midphase::intersectOBB(shapeMesh.meshData, vertexSpaceBox, callback, true); + + if (tempContainer.size() == 0) + return PX_MAX_REAL; + + // Intersection found, fetch triangles + PxU32 numTrigs = tempContainer.size(); + const PxU32* triangleIndices = tempContainer.begin(); + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents + PxVec3(restDistance); + + Ps::InlineArray orderedContainer; + orderedContainer.resize(tempContainer.size()); + + Ps::InlineArray distanceEntries; + distanceEntries.resize(tempContainer.size()); + + PxU32* orderedList = orderedContainer.begin(); + PxF32* distances = reinterpret_cast(distanceEntries.begin()); + + PxReal minTOI = PX_MAX_REAL; + + + PxU32 count = 0; + for(PxU32 a = 0; a < numTrigs; ++a) + { + PxU32 unused; + ConvexTriangles convexPartOfMesh1(shapeMesh, meshScaling, &triangleIndices[a], 1, &unused); + + PxVec3 resultNormal = -transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + + if(relTr.dot(resultNormal) >= fastMovingThreshold) + { + PxBounds3 bounds; + convexPartOfMesh1.getBounds(bounds, lastTm1); + //OK, we have all 3 vertices, now calculate bounds... + + PxF32 toi = sweepAABBAABB(origin, extent, bounds.getCenter(), bounds.getExtents() + PxVec3(0.02f, 0.02f, 0.02f), trA, trB); + + PxU32 index = 0; + if(toi <= 1.f) + { + for(PxU32 b = count; b > 0; --b) + { + if(distances[b-1] <= toi) + { + //shuffle down and swap + index = b; + break; + } + PX_ASSERT(b > 0); + PX_ASSERT(b < numTrigs); + distances[b] = distances[b-1]; + orderedList[b] = orderedList[b-1]; + } + PX_ASSERT(index < numTrigs); + orderedList[index] = triangleIndices[a]; + distances[index] = toi; + count++; + } + } + } + + + + PxVec3 tempWorldNormal(0.f), tempWorldPoint(0.f); + + Cm::FastVertex2ShapeScaling idScale; + PxU32 ccdFaceIndex = PXC_CONTACT_NO_FACE_INDEX; + + PxVec3 sphereCenter(lastTm1.p); + PxF32 inSphereRadius = shape0.mFastMovingThreshold; + //PxF32 inRadSq = inSphereRadius * inSphereRadius; + + PxVec3 sphereCenterInTransform1 = transform1.transformInv(sphereCenter); + + PxVec3 sphereCenterInTransform0p = transform1.transformInv(lastTm0.p); + + + for (PxU32 ti = 0; ti < count /*&& PxMax(minTOI, 0.f) >= distances[ti]*/; ti++) + { + PxU32 unused; + ConvexTriangles convexPartOfMesh1(shapeMesh, meshScaling, &orderedList[ti], 1, &unused); + + PxVec3 resultNormal, resultPoint, v0l, v1l, v2l; + TriangleVertexPointers::getTriangleVerts(shapeMesh.meshData, orderedList[ti], v0l, v1l, v2l); + const bool flipNormal = meshScaling.flipsNormal(); + + const PxVec3 v0 = meshScaling * v0l; + const PxVec3 v1 = meshScaling * (flipNormal ? v2l : v1l); + const PxVec3 v2 = meshScaling * (flipNormal ? v1l : v2l); + + TriangleV triangle(V3LoadU(v0), V3LoadU(v1), V3LoadU(v2)); + + //do sweep + PxReal res = SweepShapeTriangle( + *shape0.mGeometry, *shape1.mGeometry, transform0, transform1, lastTm0, lastTm1, restDistance, + resultNormal, resultPoint, Cm::FastVertex2ShapeScaling(), triangle, + 0.f); + + resultNormal = -resultNormal; + + if(res <= 0.f) + { + res = 0.f; + + PxF32 inRad = inSphereRadius + restDistance; + PxF32 inRadSq = inRad*inRad; + + const PxVec3 vv0 = v1 - v0 ; + const PxVec3 vv1 = v2 - v0; + const PxVec3 nor = vv0.cross(vv1); + + //Now we have a 0 TOI, lets see if the in-sphere hit it! + + PxF32 distanceSq = distancePointTriangleSquared( sphereCenterInTransform1, v0, vv0, vv1); + + if(distanceSq < inRadSq) + { + const PxF32 distance = PxSqrt(distanceSq); + res = distance - inRad; + const PxF32 d = nor.dot(v0); + const PxF32 dd = nor.dot(sphereCenterInTransform0p); + if((dd - d) < 0.f) + { + //back side, penetration + res = -(2.f * inRad - distance); + } + } + PX_ASSERT(PxIsFinite(res)); + resultNormal = transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + } + + if (res < minTOI) + { + tempWorldNormal = resultNormal;//convexPartOfMesh1.getPolygonNormal(0);//transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + tempWorldPoint = resultPoint; + minTOI = res; + ccdFaceIndex = orderedList[ti]; + } + + } + + worldNormal = tempWorldNormal;//transform1.rotate(tempWorldNormal); + worldPoint = tempWorldPoint; + outCCDFaceIndex = ccdFaceIndex; + return minTOI; +} + + +/** +\brief This code performs a conservative estimate of the TOI of a shape v mesh. +*/ +PxReal SweepEstimateAnyShapeMesh(GU_SWEEP_ESTIMATE_ARGS) +{ + // this is the trimesh midphase for convex vs mesh sweep. shape0 is the convex shape. + // Get actual shape data + const PxTriangleMeshGeometryLL& shapeMesh = shape1.mGeometry->get(); + + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + + /*---------------------------------------------------*\ + | + | STEP1: OPCODE Geometry collection + | + \*---------------------------------------------------*/ + + PxVec3 trA = transform0.p - lastTr0.p; + PxVec3 trB = transform1.p - lastTr1.p; + + PxVec3 relTr = trA - trB; + PxVec3 unitDir = relTr; + PxReal length = unitDir.normalize(); + + PxMat33 matRot(PxIdentity); + + //1) Compute the swept bounds + Box sweptBox; + computeSweptBox(sweptBox, shape0.mExtents, shape0.mCenter, matRot, unitDir, length); + + Box vertexSpaceBox; + computeVertexSpaceOBB(vertexSpaceBox, sweptBox, transform1, shapeMesh.scale); + + vertexSpaceBox.extents += PxVec3(restDistance); + + // TODO: implement a cached mode that fetches the trigs from a cache rather than per opcode if there is little motion. + + struct CB : MeshHitCallback + { + PxReal minTOI; + PxReal sumFastMovingThresh; + const PxTriangleMeshGeometryLL& shapeMesh; + const Cm::FastVertex2ShapeScaling& meshScaling; + const PxVec3& relTr; + const PxVec3& trA; + const PxVec3& trB; + const PxTransform& transform1; + const PxVec3& origin; + const PxVec3& extent; + + CB(PxReal aSumFast, const PxTriangleMeshGeometryLL& aShapeMesh, const Cm::FastVertex2ShapeScaling& aMeshScaling, + const PxVec3& aRelTr, const PxVec3& atrA, const PxVec3& atrB, const PxTransform& aTransform1, const PxVec3& aOrigin, const PxVec3& aExtent) + : MeshHitCallback(CallbackMode::eMULTIPLE), + sumFastMovingThresh(aSumFast), shapeMesh(aShapeMesh), meshScaling(aMeshScaling), relTr(aRelTr), trA(atrA), trB(atrB), + transform1(aTransform1), origin(aOrigin), extent(aExtent) + { + minTOI = PX_MAX_REAL; + } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3&, const PxVec3&, const PxVec3&, PxReal& shrunkMaxT, const PxU32*) + { + PxU32 unused; + ConvexTriangles convexPartOfMesh1(shapeMesh, meshScaling, &hit.faceIndex, 1, &unused); + PxVec3 resultNormal = -transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + if(relTr.dot(resultNormal) >= sumFastMovingThresh) + { + PxBounds3 bounds; + convexPartOfMesh1.getBounds(bounds, transform1); + //OK, we have all 3 vertices, now calculate bounds... + + PxF32 toi = sweepAABBAABB( + origin, extent * 1.1f, bounds.getCenter(), (bounds.getExtents() + PxVec3(0.01f, 0.01f, 0.01f)) * 1.1f, trA, trB); + + minTOI = PxMin(minTOI, toi); + shrunkMaxT = minTOI; + } + + return (minTOI > 0.0f); // stop traversal if minTOI == 0.0f + } + + void operator=(const CB&) {} + }; + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents + PxVec3(restDistance); + + CB callback(fastMovingThreshold, shapeMesh, meshScaling, relTr, trA, trB, transform1, origin, extent); + Midphase::intersectOBB(shapeMesh.meshData, vertexSpaceBox, callback, true); + + return callback.minTOI; +} + + +} +} + diff --git a/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h new file mode 100644 index 000000000..70eecc954 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef GU_CCD_SWEEP_H +#define GU_CCD_SWEEP_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsVecTransform.h" +#include "GuGeometryUnion.h" +#include "CmScaling.h" + +#define GU_TRIANGLE_SWEEP_METHOD_ARGS \ + const Gu::GeometryUnion& shape0, \ + const Gu::GeometryUnion& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const PxTransform& lastTm0, \ + const PxTransform& lastTm1, \ + PxReal restDistance, \ + PxVec3& worldNormal, \ + PxVec3& worldPoint, \ + const Cm::FastVertex2ShapeScaling& meshScaling, \ + Gu::TriangleV& triangle, \ + const PxF32 toiEstimate + + +#define GU_SWEEP_METHOD_ARGS \ + const Gu::CCDShape& shape0, \ + const Gu::CCDShape& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const PxTransform& lastTm0, \ + const PxTransform& lastTm1, \ + PxReal restDistance, \ + PxVec3& worldNormal, \ + PxVec3& worldPoint, \ + const PxF32 toiEstimate, \ + PxU32& outCCDFaceIndex, \ + const PxReal fastMovingThreshold + + +#define GU_SWEEP_ESTIMATE_ARGS \ + const CCDShape& shape0, \ + const CCDShape& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const PxTransform& lastTr0, \ + const PxTransform& lastTr1, \ + const PxReal restDistance, \ + const PxReal fastMovingThreshold + + +#define GU_SWEEP_METHOD_ARGS_UNUSED \ + const Gu::CCDShape& /*shape0*/, \ + const Gu::CCDShape& /*shape1*/, \ + const PxTransform& /*transform0*/, \ + const PxTransform& /*transform1*/, \ + const PxTransform& /*lastTm0*/, \ + const PxTransform& /*lastTm1*/, \ + PxReal /*restDistance*/, \ + PxVec3& /*worldNormal*/, \ + PxVec3& /*worldPoint*/, \ + const PxF32 /*toiEstimate*/, \ + PxU32& /*outCCDFaceIndex*/, \ + const PxReal /*fastMovingThreshold*/ + +namespace physx +{ +namespace Gu +{ + struct CCDShape + { + const Gu::GeometryUnion* mGeometry; + PxReal mFastMovingThreshold; //The CCD threshold for this shape + PxTransform mPrevTransform; //This shape's previous transform + PxTransform mCurrentTransform; //This shape's current transform + PxVec3 mExtents; //The extents of this shape's AABB + PxVec3 mCenter; //The center of this shape's AABB + PxU32 mUpdateCount; //How many times this shape has been updated in the CCD. This is correlated with the CCD body's update count. + }; + + PX_FORCE_INLINE PxF32 sweepAABBAABB(const PxVec3& centerA, const PxVec3& extentsA, const PxVec3& centerB, const PxVec3& extentsB, const PxVec3& trA, const PxVec3& trB) + { + //Sweep 2 AABBs against each other, return the TOI when they hit else PX_MAX_REAL if they don't hit + const PxVec3 cAcB = centerA - centerB; + const PxVec3 sumExtents = extentsA + extentsB; + + //Initial hit + if(PxAbs(cAcB.x) <= sumExtents.x && + PxAbs(cAcB.y) <= sumExtents.y && + PxAbs(cAcB.z) <= sumExtents.z) + return 0.f; + + //No initial hit - perform the sweep + const PxVec3 relTr = trB - trA; + PxF32 tfirst = 0.f; + PxF32 tlast = 1.f; + + const PxVec3 aMax = centerA + extentsA; + const PxVec3 aMin = centerA - extentsA; + const PxVec3 bMax = centerB + extentsB; + const PxVec3 bMin = centerB - extentsB; + + const PxF32 eps = 1e-6f; + + for(PxU32 a = 0; a < 3; ++a) + { + if(relTr[a] < -eps) + { + if(bMax[a] < aMin[a]) + return PX_MAX_REAL; + if(aMax[a] < bMin[a]) + tfirst = PxMax((aMax[a] - bMin[a])/relTr[a], tfirst); + if(bMax[a] > aMin[a]) + tlast = PxMin((aMin[a] - bMax[a])/relTr[a], tlast); + } + else if(relTr[a] > eps) + { + if(bMin[a] > aMax[a]) + return PX_MAX_REAL; + if(bMax[a] < aMin[a]) + tfirst = PxMax((aMin[a] - bMax[a])/relTr[a], tfirst); + if(aMax[a] > bMin[a]) + tlast = PxMin((aMax[a] - bMin[a])/relTr[a], tlast); + } + else + { + if(bMax[a] < aMin[a] || bMin[a] > aMax[a]) + return PX_MAX_REAL; + } + + //No hit + if(tfirst > tlast) + return PX_MAX_REAL; + } + //There was a hit so return the TOI + return tfirst; + } + + PX_PHYSX_COMMON_API PxReal SweepShapeShape(GU_SWEEP_METHOD_ARGS); + + PX_PHYSX_COMMON_API PxReal SweepEstimateAnyShapeHeightfield(GU_SWEEP_ESTIMATE_ARGS); + + PX_PHYSX_COMMON_API PxReal SweepEstimateAnyShapeMesh(GU_SWEEP_ESTIMATE_ARGS); + + +} +} +#endif + diff --git a/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp new file mode 100644 index 000000000..8f6b1401b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp @@ -0,0 +1,286 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "Ps.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecTriangle.h" +#include "GuGJKRaycast.h" +#include "GuCCDSweepConvexMesh.h" +#include "GuGJKType.h" + +namespace physx +{ +namespace Gu +{ + +using namespace Ps::aos; + +template PX_FORCE_INLINE PxReal getRadius(const PxGeometry&) +{ + return 0; +} + +template<> PX_FORCE_INLINE PxReal getRadius(const PxGeometry& g) +{ + PX_ASSERT(g.getType() == PxGeometryType::eCAPSULE || g.getType() == PxGeometryType::eSPHERE); + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(PxSphereGeometry, radius) == PX_OFFSET_OF(PxCapsuleGeometry, radius)); + return static_cast(g).radius; +} + + + +template +static PxReal CCDSweep(ConvexA& a, ConvexB& b, const PxTransform& transform0, const PxTransform& transform1, const PxTransform& lastTm0, const PxTransform& lastTm1, + const Ps::aos::FloatV& toiEstimate, PxVec3& worldPoint, PxVec3& worldNormal, PxReal inflation = 0.f) +{ + PX_UNUSED(toiEstimate); //KS - TODO - can we use this again? + using namespace Ps::aos; + + const Vec3V zero = V3Zero(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&lastTm0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&lastTm1.p.x); + + const PsTransformV tr0(p0, q0); + const PsTransformV tr1(p1, q1); + + const PsMatTransformV aToB(tr1.transformInv(tr0)); + + const Vec3V trans0p = V3LoadU(transform0.p); + const Vec3V trans1p = V3LoadU(transform1.p); + const Vec3V trA = V3Sub(trans0p, p0); + const Vec3V trB = V3Sub(trans1p, p1); + const Vec3V relTr = tr1.rotateInv(V3Sub(trB, trA)); + + FloatV lambda; + Vec3V closestA, normal; + const FloatV initialLambda = FZero(); + const RelativeConvex convexA(a, aToB); + const LocalConvex convexB(b); + if(gjkRaycastPenetration, LocalConvex >(convexA, convexB, aToB.p, initialLambda, zero, relTr, lambda, normal, closestA, inflation, true)) + { + //Adjust closestA because it will be on the surface of convex a in its initial position (s). If the TOI > 0, we need to move + //the point along the sweep direction to get the world-space hit position. + PxF32 res; + FStore(lambda, &res); + closestA = V3ScaleAdd(trA, FMax(lambda, FZero()), tr1.transform(closestA)); + normal = tr1.rotate(normal); + + V3StoreU(normal, worldNormal); + V3StoreU(closestA, worldPoint); + return res; + } + return PX_MAX_REAL; +} + + + +// +// lookup table for geometry-vs-geometry sweeps +// + + +PxReal UnimplementedSweep (GU_SWEEP_METHOD_ARGS_UNUSED) +{ + return PX_MAX_REAL; //no impact +} + +template +PxReal SweepGeomGeom(GU_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(outCCDFaceIndex); + PX_UNUSED(fastMovingThreshold); + + const PxGeometry& g0 = shape0.mGeometry->getGeometry(); + const PxGeometry& g1 = shape1.mGeometry->getGeometry(); + + typename ConvexGeom::Type geom0(g0); + typename ConvexGeom::Type geom1(g1); + + return CCDSweep(geom0, geom1, transform0, transform1, lastTm0, lastTm1, FLoad(toiEstimate), worldPoint, worldNormal, restDistance+getRadius(g0)+getRadius(g1) ); +} + +typedef PxReal (*SweepMethod) (GU_SWEEP_METHOD_ARGS); + +PxReal SweepAnyShapeHeightfield(GU_SWEEP_METHOD_ARGS); +PxReal SweepAnyShapeMesh(GU_SWEEP_METHOD_ARGS); + +SweepMethod g_SweepMethodTable[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT] = +{ + //PxGeometryType::eSPHERE + { + SweepGeomGeom, //PxGeometryType::eSPHERE + UnimplementedSweep, //PxGeometryType::ePLANE + SweepGeomGeom, //PxGeometryType::eCAPSULE + SweepGeomGeom, //PxGeometryType::eBOX + SweepGeomGeom, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD //TODO + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + UnimplementedSweep, //PxGeometryType::ePLANE + UnimplementedSweep, //PxGeometryType::eCAPSULE + UnimplementedSweep, //PxGeometryType::eBOX + UnimplementedSweep, //PxGeometryType::eCONVEXMESH + UnimplementedSweep, //PxGeometryType::eTRIANGLEMESH + UnimplementedSweep, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + SweepGeomGeom, //PxGeometryType::eCAPSULE + SweepGeomGeom, //PxGeometryType::eBOX + SweepGeomGeom, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + SweepGeomGeom, //PxGeometryType::eBOX + SweepGeomGeom, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + SweepGeomGeom, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + UnimplementedSweep, //PxGeometryType::eTRIANGLEMESH + UnimplementedSweep, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + UnimplementedSweep, //PxGeometryType::eHEIGHTFIELD + }, +}; + + +PxReal SweepShapeShape(GU_SWEEP_METHOD_ARGS) +{ + PxGeometryType::Enum type0 = shape0.mGeometry->getType(); + PxGeometryType::Enum type1 = shape1.mGeometry->getType(); + + return g_SweepMethodTable[type0][type1](shape0, shape1, transform0, transform1, lastTm0, lastTm1, + restDistance, worldNormal, worldPoint, toiEstimate, outCCDFaceIndex, fastMovingThreshold); + +} + +// +// lookup table for sweeps agains triangles +// + +PxReal UnimplementedTriangleSweep(GU_TRIANGLE_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(shape1); + PX_UNUSED(transform0); + PX_UNUSED(transform1); + PX_UNUSED(lastTm0); + PX_UNUSED(lastTm1); + PX_UNUSED(restDistance); + PX_UNUSED(worldNormal); + PX_UNUSED(worldPoint); + PX_UNUSED(meshScaling); + PX_UNUSED(triangle); + PX_UNUSED(toiEstimate); + + return 1e10f; //no impact +} + +template +PxReal SweepGeomTriangles(GU_TRIANGLE_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(meshScaling); + PX_UNUSED(shape1); + + const PxGeometry& g = shape0.getGeometry(); + //Geom geom(g); + typename ConvexGeom::Type geom(g); + + return CCDSweep(triangle, geom, transform1, transform0, lastTm1, lastTm0, FLoad(toiEstimate), worldPoint, worldNormal, restDistance+getRadius(g) ); +} + +typedef PxReal (*TriangleSweepMethod) (GU_TRIANGLE_SWEEP_METHOD_ARGS); +TriangleSweepMethod g_TriangleSweepMethodTable[PxGeometryType::eGEOMETRY_COUNT] = +{ + SweepGeomTriangles, //PxGeometryType::eSPHERE + UnimplementedTriangleSweep, //PxGeometryType::ePLANE + SweepGeomTriangles, //PxGeometryType::eCAPSULE + SweepGeomTriangles, //PxGeometryType::eBOX + SweepGeomTriangles, //PxGeometryType::eCONVEXMESH + UnimplementedTriangleSweep, //PxGeometryType::eTRIANGLEMESH + UnimplementedTriangleSweep, //PxGeometryType::eHEIGHTFIELD +}; + +PxReal SweepShapeTriangle(GU_TRIANGLE_SWEEP_METHOD_ARGS) +{ + const PxGeometryType::Enum type0 = shape0.getType(); + TriangleSweepMethod method = g_TriangleSweepMethodTable[type0]; + return method(shape0, shape1, transform0, transform1, lastTm0, lastTm1, restDistance, worldNormal, worldPoint, meshScaling, triangle, toiEstimate); +} + +} +} + diff --git a/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp new file mode 100644 index 000000000..a072a3918 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp @@ -0,0 +1,89 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuBarycentricCoordinates.h" + +using namespace physx; +using namespace Ps::aos; + +void Gu::barycentricCoordinates(const Vec3VArg p, const Vec3VArg a, const Vec3VArg b, FloatV& v) +{ + const Vec3V v0 = V3Sub(a, p); + const Vec3V v1 = V3Sub(b, p); + const Vec3V d = V3Sub(v1, v0); + const FloatV denominator = V3Dot(d, d); + const FloatV numerator = V3Dot(V3Neg(v0), d); + const FloatV zero = FZero(); + const FloatV denom = FSel(FIsGrtr(denominator, zero), FRecip(denominator), zero); + v = FMul(numerator, denom); +} + +void Gu::barycentricCoordinates(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, Ps::aos::FloatV& v, Ps::aos::FloatV& w) +{ + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + + const Vec3V n = V3Cross(ab, ac); + + const VecCrossV crossA = V3PrepareCross(V3Sub(a, p)); + const VecCrossV crossB = V3PrepareCross(V3Sub(b, p)); + const VecCrossV crossC = V3PrepareCross(V3Sub(c, p)); + const Vec3V bCrossC = V3Cross(crossB, crossC); + const Vec3V cCrossA = V3Cross(crossC, crossA); + const Vec3V aCrossB = V3Cross(crossA, crossB); + + const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + const FloatV totalArea =FAdd(va, FAdd(vb, vc)); + const FloatV zero = FZero(); + const FloatV denom = FSel(FIsEq(totalArea, zero), zero, FRecip(totalArea)); + v = FMul(vb, denom); + w = FMul(vc, denom); + +} + +/* + v0 = b - a; + v1 = c - a; + v2 = p - a; +*/ +void Gu::barycentricCoordinates(const Vec3VArg v0, const Vec3VArg v1, const Vec3VArg v2, FloatV& v, FloatV& w) +{ + const FloatV d00 = V3Dot(v0, v0); + const FloatV d01 = V3Dot(v0, v1); + const FloatV d11 = V3Dot(v1, v1); + const FloatV d20 = V3Dot(v2, v0); + const FloatV d21 = V3Dot(v2, v1); + const FloatV denom = FRecip(FSub(FMul(d00,d11), FMul(d01, d01))); + v = FMul(FSub(FMul(d11, d20), FMul(d01, d21)), denom); + w = FMul(FSub(FMul(d00, d21), FMul(d01, d20)), denom); +} + diff --git a/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h new file mode 100644 index 000000000..4dea0543a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BARYCENTRIC_COORDINATES_H +#define GU_BARYCENTRIC_COORDINATES_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + //calculate the barycentric coorinates for a point in a segment + void barycentricCoordinates(const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + Ps::aos::FloatV& v); + + //calculate the barycentric coorinates for a point in a triangle + void barycentricCoordinates(const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& v, + Ps::aos::FloatV& w); + + void barycentricCoordinates(const Ps::aos::Vec3VArg v0, + const Ps::aos::Vec3VArg v1, + const Ps::aos::Vec3VArg v2, + Ps::aos::FloatV& v, + Ps::aos::FloatV& w); + + PX_INLINE Ps::aos::BoolV isValidTriangleBarycentricCoord(const Ps::aos::FloatVArg v, const Ps::aos::FloatVArg w) + { + using namespace Ps::aos; + const FloatV zero = FNeg(FEps()); + const FloatV one = FAdd(FOne(), FEps()); + + const BoolV con0 = BAnd(FIsGrtrOrEq(v, zero), FIsGrtrOrEq(one, v)); + const BoolV con1 = BAnd(FIsGrtrOrEq(w, zero), FIsGrtrOrEq(one, w)); + const BoolV con2 = FIsGrtr(one, FAdd(v, w)); + return BAnd(con0, BAnd(con1, con2)); + } + + PX_INLINE Ps::aos::BoolV isValidTriangleBarycentricCoord2(const Ps::aos::Vec4VArg vwvw) + { + using namespace Ps::aos; + const Vec4V eps = V4Splat(FEps()); + const Vec4V zero =V4Neg(eps); + const Vec4V one = V4Add(V4One(), eps); + + const Vec4V v0v1v0v1 = V4PermXZXZ(vwvw); + const Vec4V w0w1w0w1 = V4PermYWYW(vwvw); + + const BoolV con0 = BAnd(V4IsGrtrOrEq(v0v1v0v1, zero), V4IsGrtrOrEq(one, v0v1v0v1)); + const BoolV con1 = BAnd(V4IsGrtrOrEq(w0w1w0w1, zero), V4IsGrtrOrEq(one, w0w1w0w1)); + const BoolV con2 = V4IsGrtr(one, V4Add(v0v1v0v1, w0w1w0w1)); + return BAnd(con0, BAnd(con1, con2)); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h b/src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h new file mode 100644 index 000000000..5d2c01c9e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h @@ -0,0 +1,121 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BOX_CONVERSION_H +#define GU_BOX_CONVERSION_H + +#include "GuBox.h" +#include "PsMathUtils.h" +#include "CmMatrix34.h" +#include "PsVecMath.h" + +namespace physx +{ + // PT: builds rot from quat. WARNING: writes 4 bytes after 'dst.rot'. + PX_FORCE_INLINE void buildFrom(Gu::Box& dst, const PxQuat& q) + { + using namespace Ps::aos; + const QuatV qV = V4LoadU(&q.x); + Vec3V column0, column1, column2; + QuatGetMat33V(qV, column0, column1, column2); + // PT: TODO: investigate if these overlapping stores are a problem + V4StoreU(Vec4V_From_Vec3V(column0), &dst.rot.column0.x); + V4StoreU(Vec4V_From_Vec3V(column1), &dst.rot.column1.x); + V4StoreU(Vec4V_From_Vec3V(column2), &dst.rot.column2.x); + } + + PX_FORCE_INLINE void buildFrom(Gu::Box& dst, const PxVec3& center, const PxVec3& extents, const PxQuat& q) + { + using namespace Ps::aos; + // PT: writes 4 bytes after 'rot' but it's safe since we then write 'center' just afterwards + buildFrom(dst, q); + dst.center = center; + dst.extents = extents; + } + + PX_FORCE_INLINE void buildMatrixFromBox(Cm::Matrix34& mat34, const Gu::Box& box) + { + mat34.m = box.rot; + mat34.p = box.center; + } + + // SD: function is now the same as FastVertex2ShapeScaling::transformQueryBounds + // PT: lots of LHS in that one. TODO: revisit... + PX_INLINE Gu::Box transform(const Cm::Matrix34& transfo, const Gu::Box& box) + { + Gu::Box ret; + PxMat33& obbBasis = ret.rot; + + obbBasis.column0 = transfo.rotate(box.rot.column0 * box.extents.x); + obbBasis.column1 = transfo.rotate(box.rot.column1 * box.extents.y); + obbBasis.column2 = transfo.rotate(box.rot.column2 * box.extents.z); + + ret.center = transfo.transform(box.center); + ret.extents = Ps::optimizeBoundingBox(obbBasis); + return ret; + } + + PX_INLINE Gu::Box transformBoxOrthonormal(const Gu::Box& box, const PxTransform& t) + { + Gu::Box ret; + PxMat33& obbBasis = ret.rot; + obbBasis.column0 = t.rotate(box.rot.column0); + obbBasis.column1 = t.rotate(box.rot.column1); + obbBasis.column2 = t.rotate(box.rot.column2); + ret.center = t.transform(box.center); + ret.extents = box.extents; + return ret; + } + + /** + \brief recomputes the OBB after an arbitrary transform by a 4x4 matrix. + \param mtx [in] the transform matrix + \param obb [out] the transformed OBB + */ + PX_INLINE void rotate(const Gu::Box& src, const Cm::Matrix34& mtx, Gu::Box& obb) + { + // The extents remain constant + obb.extents = src.extents; + // The center gets x-formed + obb.center = mtx.transform(src.center); + // Combine rotations + obb.rot = mtx.m * src.rot; + } + +// PT: TODO: move this to a better place + PX_FORCE_INLINE void getInverse(PxMat33& dstRot, PxVec3& dstTrans, const PxMat33& srcRot, const PxVec3& srcTrans) + { + const PxMat33 invRot = srcRot.getInverse(); + dstTrans = invRot.transform(-srcTrans); + dstRot = invRot; + } + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h b/src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h new file mode 100644 index 000000000..3729fc600 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h @@ -0,0 +1,85 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EDGECACHE_H +#define GU_EDGECACHE_H + +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "PsHash.h" + +namespace physx +{ +namespace Gu +{ + class EdgeCache + { +#define NUM_EDGES_IN_CACHE 64 //must be power of 2. 32 lines result in 10% extra work (due to cache misses), 64 lines in 6% extra work, 128 lines in 4%. + public: + EdgeCache() + { + PxMemZero(cacheLines, NUM_EDGES_IN_CACHE*sizeof(CacheLine)); + } + + PxU32 hash(PxU32 key) const + { + return (NUM_EDGES_IN_CACHE - 1) & Ps::hash(key); //Only a 16 bit hash would be needed here. + } + + bool isInCache(PxU8 vertex0, PxU8 vertex1) + { + PX_ASSERT(vertex1 >= vertex0); + PxU16 key = PxU16((vertex0 << 8) | vertex1); + PxU32 h = hash(key); + CacheLine& cl = cacheLines[h]; + if (cl.fullKey == key) + { + return true; + } + else //cache the line now as it's about to be processed + { + cl.fullKey = key; + return false; + } + } + + private: + struct CacheLine + { + PxU16 fullKey; + }; + CacheLine cacheLines[NUM_EDGES_IN_CACHE]; +#undef NUM_EDGES_IN_CACHE + }; +} + +} + +#endif + diff --git a/src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h b/src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h new file mode 100644 index 000000000..c32b58c94 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h @@ -0,0 +1,153 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EDGE_LIST_DATA_H +#define GU_EDGE_LIST_DATA_H + +#include "foundation/PxSimpleTypes.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + +/*! +NOTICE! + +This is a data-code separated version of PxPhysics::EdgeList. + +It is to be shared between high and low level code, so make sure both are recompiled +if any change is done here. +*/ + +// Flags +enum EdgeType +{ + PX_EDGE_UNDEFINED, + + PX_EDGE_BOUNDARY, //!< Edge belongs to a single triangle + PX_EDGE_INTERNAL, //!< Edge belongs to exactly two triangles + PX_EDGE_SINGULAR, //!< Edge belongs to three or more triangles + + PX_EDGE_FORCE_DWORD = 0x7fffffff +}; + +enum EdgeFlag +{ + PX_EDGE_ACTIVE = (1<<0) +}; + + +// Data + + +//! Basic edge-data +struct EdgeData +{ + PxU32 Ref0; //!< First vertex reference + PxU32 Ref1; //!< Second vertex reference +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeData) == 8); + + +//! Basic edge-data using 8-bit references +struct Edge8Data +{ + PxU8 Ref0; //!< First vertex reference + PxU8 Ref1; //!< Second vertex reference +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::Edge8Data) == 2); + + +//! A count/offset pair = an edge descriptor +struct EdgeDescData +{ + PxU16 Flags; + PxU16 Count; + PxU32 Offset; +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeDescData) == 8); + + +//! Edge<->triangle mapping +struct EdgeTriangleData +{ + PxU32 mLink[3]; +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeTriangleData) == 12); + + +struct EdgeListData +{ + // The edge list + PxU32 mNbEdges; //!< Number of edges in the list + Gu::EdgeData* mEdges; //!< List of edges + // Faces to edges + PxU32 mNbFaces; //!< Number of faces for which we have data + Gu::EdgeTriangleData* mEdgeFaces; //!< Array of edge-triangles referencing mEdges + // Edges to faces + Gu::EdgeDescData* mEdgeToTriangles; //!< An EdgeDesc structure for each edge + PxU32* mFacesByEdges; //!< A pool of face indices +}; +#if PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeListData) == 48); +#else +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeListData) == 24); +#endif + + +// Accessors + +enum +{ + MSH_EDGE_LINK_MASK = 0x0fffffff, + MSH_ACTIVE_EDGE_MASK = 0x80000000, + MSH_ACTIVE_VERTEX_MASK = 0x40000000 +}; + +class EdgeTriangleAC +{ +public: + PX_INLINE static PxU32 GetEdge01(const Gu::EdgeTriangleData& data) { return data.mLink[0] & MSH_EDGE_LINK_MASK; } + PX_INLINE static PxU32 GetEdge12(const Gu::EdgeTriangleData& data) { return data.mLink[1] & MSH_EDGE_LINK_MASK; } + PX_INLINE static PxU32 GetEdge20(const Gu::EdgeTriangleData& data) { return data.mLink[2] & MSH_EDGE_LINK_MASK; } + PX_INLINE static PxU32 GetEdge(const Gu::EdgeTriangleData& data, PxU32 i) { return data.mLink[i] & MSH_EDGE_LINK_MASK; } + + PX_INLINE static Ps::IntBool HasActiveEdge01(const Gu::EdgeTriangleData& data) { return Ps::IntBool(data.mLink[0] & MSH_ACTIVE_EDGE_MASK); } + PX_INLINE static Ps::IntBool HasActiveEdge12(const Gu::EdgeTriangleData& data) { return Ps::IntBool(data.mLink[1] & MSH_ACTIVE_EDGE_MASK); } + PX_INLINE static Ps::IntBool HasActiveEdge20(const Gu::EdgeTriangleData& data) { return Ps::IntBool(data.mLink[2] & MSH_ACTIVE_EDGE_MASK); } + PX_INLINE static Ps::IntBool HasActiveEdge(const Gu::EdgeTriangleData& data, PxU32 i) { return Ps::IntBool(data.mLink[i] & MSH_ACTIVE_EDGE_MASK); } +}; + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp b/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp new file mode 100644 index 000000000..8f20642b2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSeparatingAxes.h" + +using namespace physx; + +union FloatInt +{ + float f; + PxU32 i; +}; + +bool Gu::SeparatingAxes::addAxis(const PxVec3& axis) +{ + PxU32 numAxes = getNumAxes(); + const PxVec3* PX_RESTRICT axes = getAxes(); + const PxVec3* PX_RESTRICT axes_end = axes + numAxes; + while(axes0.9999f) + return false; + axes++; + } + +#ifdef SEP_AXIS_FIXED_MEMORY + if(mNbAxes (y)) +#define ABS_SMALLER_EQUAL(x, y) (PxAbs(x) <= (y)) +//#define AIR(x) ((PxU32&)(x)&SIGN_BITMASK) +//#define ABS_GREATER(x, y) (AIR(x) > IR(y)) +//#define ABS_SMALLER_EQUAL(x, y) (AIR(x) <= IR(y)) + +#if PX_X86 && !PX_OSX + + // Some float optimizations ported over from novodex. + + //returns non zero if the value is negative. + #define PXC_IS_NEGATIVE(x) (((PxU32&)(x)) & 0x80000000) + +#else + + //On most platforms using the integer rep is worse(produces LHSs) since the CPU has more registers. + + //returns non zero if the value is negative. + #define PXC_IS_NEGATIVE(x) ((x) < 0.0f) + +#endif + + +enum +{ + AXIS_A0, AXIS_A1, AXIS_A2, + AXIS_B0, AXIS_B1, AXIS_B2 +}; + +struct VertexInfo +{ + PxVec3 pos; + bool penetrate; + bool area; +}; + + +/*static PxI32 doBoxBoxContactGeneration(PxVec3 ctcPts[MAX_NB_CTCS], PxReal depths[MAX_NB_CTCS], PxVec3* ctcNrm, + const PxVec3& extents0, const PxVec3& extents1, + PxU32& collisionData, + const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance);*/ + +static PxI32 doBoxBoxContactGeneration(ContactBuffer& contactBuffer, + const PxVec3& extents0, const PxVec3& extents1, + PxU32& collisionData, + const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance); + +namespace physx +{ + +namespace Gu +{ +bool contactBoxBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + // Get actual shape data + const PxBoxGeometry& shapeBox0 = shape0.get(); + const PxBoxGeometry& shapeBox1 = shape1.get(); + + PxU32 pd = PxU32(cache.mPairData); + PxI32 Nb = doBoxBoxContactGeneration(contactBuffer, + shapeBox0.halfExtents, shapeBox1.halfExtents, + pd, + Cm::Matrix34(transform0), Cm::Matrix34(transform1), + params.mContactDistance); + + cache.mPairData = Ps::to8(pd); + + if(!Nb) + { + cache.mPairData = 0; // Mark as separated for temporal coherence + return false; // WARNING: the contact stream code below used to output stuff even for 0 contacts (!). Now we just return here. + } + return true; +} +}//Gu +}//physx + +// face => 4 vertices of a face of the cube (i.e. a quad) +static PX_FORCE_INLINE PxReal IsInYZ(const PxReal y, const PxReal z, const VertexInfo** PX_RESTRICT face) +{ + // Warning, indices have been remapped. We're now actually like this: + // + // 3+------+2 + // | | | + // | *--| + // | (y,z)| + // 0+------+1 + PxReal PreviousY = face[3]->pos.y; + PxReal PreviousZ = face[3]->pos.z; + + // Loop through quad vertices + for(PxI32 i=0; i<4; i++) + { + const PxReal CurrentY = face[i]->pos.y; + const PxReal CurrentZ = face[i]->pos.z; + + // |CurrentY - PreviousY y - PreviousY| + // |CurrentZ - PreviousZ z - PreviousZ| + // => similar to backface culling, check each one of the 4 triangles are consistent, in which case + // the point is within the parallelogram. + if((CurrentY - PreviousY)*(z - PreviousZ) - (CurrentZ - PreviousZ)*(y - PreviousY) >= 0.0f) return -1.0f; + + PreviousY = CurrentY; + PreviousZ = CurrentZ; + } + + PxReal x = face[0]->pos.x; + { + const PxReal ay = y - face[0]->pos.y; + const PxReal az = z - face[0]->pos.z; + + PxVec3 b = face[1]->pos - face[0]->pos; // ### could be precomputed ? + x += b.x * (ay*b.y + az*b.z) / b.magnitudeSquared(); // ### could be precomputed ? + + b = face[3]->pos - face[0]->pos; // ### could be precomputed ? + x += b.x * (ay*b.y + az*b.z) / b.magnitudeSquared(); // ### could be precomputed ? + } + + return x; +} + +// Test with respect to the quad defined by (0,-y1,-z1) and (0,y1,z1) +// +------+ y1 y +// | | | +// | * | | +// | | | +// +------+ -y1 *-----z +static PxI32 generateContacts(//PxVec3 ctcPts[], PxReal depths[], + ContactBuffer& contactBuffer, const PxVec3& contactNormal, + PxReal y1, PxReal z1, const PxVec3& box2, + const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance) +{ +// PxI32 NbContacts=0; + contactBuffer.reset(); + y1 += contactDistance; + z1 += contactDistance; + + const Cm::Matrix34 trans1to0 = transform0.getInverseRT() * transform1; + + VertexInfo vtx[8]; // The 8 cube vertices +// PxI32 i; + + // 6+------+7 + // /| /| + // / | / | + // / 4+---/--+5 + // 2+------+3 / y z + // | / | / | / + // |/ |/ |/ + // 0+------+1 *---x + + { + const PxVec3 ex = trans1to0.m.column0 * box2.x; + const PxVec3 ey = trans1to0.m.column1 * box2.y; + const PxVec3 ez = trans1to0.m.column2 * box2.z; + + /* + vtx[0].pos = mat.pos - ex - ey - ez; + vtx[1].pos = mat.pos + ex - ey - ez; + vtx[2].pos = mat.pos - ex + ey - ez; + vtx[3].pos = mat.pos + ex + ey - ez; + vtx[4].pos = mat.pos - ex - ey + ez; + vtx[5].pos = mat.pos + ex - ey + ez; + vtx[6].pos = mat.pos - ex + ey + ez; + vtx[7].pos = mat.pos + ex + ey + ez; + */ + + // 12 vector ops = 12*3 = 36 FPU ops + vtx[0].pos = vtx[2].pos = vtx[4].pos = vtx[6].pos = trans1to0.p - ex; + vtx[1].pos = vtx[3].pos = vtx[5].pos = vtx[7].pos = trans1to0.p + ex; + + PxVec3 e = ey+ez; + vtx[0].pos -= e; + vtx[1].pos -= e; + vtx[6].pos += e; + vtx[7].pos += e; + + e = ey-ez; + vtx[2].pos += e; + vtx[3].pos += e; + vtx[4].pos -= e; + vtx[5].pos -= e; + } + + // Create vertex info for 8 vertices + for(PxU32 i=0; i<8; i++) + { + // Vertex suivant + VertexInfo& p = vtx[i]; + // test the point with respect to the x = 0 plane + // if(p.pos.x < 0) + if(p.pos.x < -contactDistance) //if(PXC_IS_NEGATIVE(p.pos.x)) + { + p.area = false; + p.penetrate = false; + continue; + } + + { + // we penetrated the quad plane + p.penetrate = true; + // test to see if we are in the quad + // PxAbs => thus we test Y with respect to -Y1 and +Y1 (same for Z) + // if(PxAbs(p->pos.y) <= y1 && PxAbs(p->pos.z) <= z1) + if(ABS_SMALLER_EQUAL(p.pos.y, y1) && ABS_SMALLER_EQUAL(p.pos.z, z1)) + { + // the point is inside the quad + p.area=true; + // Since we are testing with respect to x = 0, the penetration is directly the x coordinate. +// depths[NbContacts] = p.pos.x; + + // We take the vertex as the impact point +// ctcPts[NbContacts++] = p.pos; + contactBuffer.contact(p.pos, contactNormal, -p.pos.x); + } + else + { + p.area=false; + } + } + } + + // Teste 12 edges on the quad + static const PxI32 indices[]={ 0,1, 1,3, 3,2, 2,0, 4,5, 5,7, 7,6, 6,4, 0,4, 1,5, 2,6, 3,7, }; + const PxI32* runningLine = indices; + const PxI32* endLine = runningLine+24; + while(runningLine!=endLine) + { + // The two vertices of the current edge + const VertexInfo* p1 = &vtx[*runningLine++]; + const VertexInfo* p2 = &vtx[*runningLine++]; + + // Penetrate|Area|Penetrate|Area => 16 cases + + // We only take the edges that at least penetrated the quad's plane into account. + if(p1->penetrate || p2->penetrate) + // if(p1->penetrate + p2->penetrate) // One branch only + { + // If at least one of the two vertices is not in the quad... + if(!p1->area || !p2->area) + // if(!p1->area + !p2->area) // One branch only + { + // Test y + if(p1->pos.y > p2->pos.y) { const VertexInfo* tmp=p1; p1=p2; p2=tmp; } + // Impact on the +Y1 edge of the quad + if(p1->pos.y < +y1 && p2->pos.y >= +y1) + // => a point under Y1, the other above + { + // Case 1 + PxReal a = (+y1 - p1->pos.y)/(p2->pos.y - p1->pos.y); + PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a; + if(PxAbs(z) <= z1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, y1, z); + contactBuffer.contact(PxVec3(x, y1, z), contactNormal, -x); + } + } + } + // Impact on the edge -Y1 of the quad + if(p1->pos.y < -y1 && p2->pos.y >= -y1) + { + // Case 2 + PxReal a = (-y1 - p1->pos.y)/(p2->pos.y - p1->pos.y); + PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a; + if(PxAbs(z) <= z1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, -y1, z); + contactBuffer.contact(PxVec3(x, -y1, z), contactNormal, -x); + } + } + } + + // Test z + if(p1->pos.z > p2->pos.z) { const VertexInfo* tmp=p1; p1=p2; p2=tmp; } + // Impact on the edge +Z1 of the quad + if(p1->pos.z < +z1 && p2->pos.z >= +z1) + { + // Case 3 + PxReal a = (+z1 - p1->pos.z)/(p2->pos.z - p1->pos.z); + PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a; + if(PxAbs(y) <= y1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, y, z1); + contactBuffer.contact(PxVec3(x, y, z1), contactNormal, -x); + } + } + } + // Impact on the edge -Z1 of the quad + if(p1->pos.z < -z1 && p2->pos.z >= -z1) + { + // Case 4 + PxReal a = (-z1 - p1->pos.z)/(p2->pos.z - p1->pos.z); + PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a; + if(PxAbs(y) <= y1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, y, -z1); + contactBuffer.contact(PxVec3(x, y, -z1), contactNormal, -x); + } + } + } + } + + // The case where one point penetrates the plane, and the other is not in the quad. + if((!p1->penetrate && !p2->area) || (!p2->penetrate && !p1->area)) + { + // Case 5 + PxReal a = (-p1->pos.x)/(p2->pos.x - p1->pos.x); + PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a; + if(PxAbs(y) <= y1) + { + PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a; + if(PxAbs(z) <= z1) + { +// depths[NbContacts] = 0; +// ctcPts[NbContacts++] = PxVec3(0, y, z); + contactBuffer.contact(PxVec3(0, y, z), contactNormal, 0); + } + } + } + + } + } + + { + // 6 quads => 6 faces of the cube + static const PxI32 face[][4]={ {0,1,3,2}, {1,5,7,3}, {5,4,6,7}, {4,0,2,6}, {2,3,7,6}, {0,4,5,1} }; + PxI32 addflg=0; + for(PxU32 i=0; i<6 && addflg!=0x0f; i++) + { + const PxI32* p = face[i]; + const VertexInfo* q[4]; + if((q[0]=&vtx[p[0]])->penetrate && (q[1]=&vtx[p[1]])->penetrate && (q[2]=&vtx[p[2]])->penetrate && (q[3]=&vtx[p[3]])->penetrate) + { + if(!q[0]->area || !q[1]->area || !q[2]->area || !q[3]->area) + { + if(!(addflg&1)) { PxReal x = IsInYZ(-y1, -z1, q); if(x>=0.0f) { addflg|=1; contactBuffer.contact(PxVec3(x, -y1, -z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, -y1, -z1);*/ } } + if(!(addflg&2)) { PxReal x = IsInYZ(+y1, -z1, q); if(x>=0.0f) { addflg|=2; contactBuffer.contact(PxVec3(x, +y1, -z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, +y1, -z1);*/ } } + if(!(addflg&4)) { PxReal x = IsInYZ(-y1, +z1, q); if(x>=0.0f) { addflg|=4; contactBuffer.contact(PxVec3(x, -y1, +z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, -y1, +z1);*/ } } + if(!(addflg&8)) { PxReal x = IsInYZ(+y1, +z1, q); if(x>=0.0f) { addflg|=8; contactBuffer.contact(PxVec3(x, +y1, +z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, +y1, +z1);*/ } } + } + } + } + } + +// for(i=0; i=0.0f && d=0 !! otherwise bug at sep = 0 + } + + collisionData = PxU32(minIndex + 1); // Leave "0" for separation + +#if PX_X86 + const PxU32 sign = PXC_IS_NEGATIVE(d1[minIndex]); +#else + const PxU32 sign = PxU32(PXC_IS_NEGATIVE(d1[minIndex])); +#endif + Cm::Matrix34 trs; + PxVec3 ctcNrm; + + switch(minIndex) + { + default: + return 0; + + case AXIS_A0: +// *ctcNrm = axis00; + if(sign) + { + ctcNrm = axis00; + trs.m = transform0.m; + trs.p = transform0.p - extents0.x*axis00; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis00; + + trs.m.column0 = -axis00; + trs.m.column1 = -axis01; + trs.m.column2 = axis02; + trs.p = transform0.p + extents0.x*axis00; + } +// return generateContacts(ctcPts, depths, extents0.y, extents0.z, extents1, trs, transform1, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents0.y, extents0.z, extents1, trs, transform1, contactDistance); + + case AXIS_A1: +// *ctcNrm = axis01; + trs.m.column2 = axis00; // Factored out + if(sign) + { + ctcNrm = axis01; + trs.m.column0 = axis01; + trs.m.column1 = axis02; + trs.p = transform0.p - extents0.y*axis01; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis01; + + trs.m.column0 = -axis01; + trs.m.column1 = -axis02; + trs.p = transform0.p + extents0.y*axis01; + } +// return generateContacts(ctcPts, depths, extents0.z, extents0.x, extents1, trs, transform1, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents0.z, extents0.x, extents1, trs, transform1, contactDistance); + + case AXIS_A2: +// *ctcNrm = axis02; + trs.m.column2 = axis01; // Factored out + + if(sign) + { + ctcNrm = axis02; + trs.m.column0 = axis02; + trs.m.column1 = axis00; + trs.p = transform0.p - extents0.z*axis02; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis02; + + trs.m.column0 = -axis02; + trs.m.column1 = -axis00; + trs.p = transform0.p + extents0.z*axis02; + } +// return generateContacts(ctcPts, depths, extents0.x, extents0.y, extents1, trs, transform1, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents0.x, extents0.y, extents1, trs, transform1, contactDistance); + + case AXIS_B0: +// *ctcNrm = axis10; + if(sign) + { + ctcNrm = axis10; + trs.m.column0 = -axis10; + trs.m.column1 = -axis11; + trs.m.column2 = axis12; + trs.p = transform1.p + extents1.x*axis10; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis10; + trs.m = transform1.m; + trs.p = transform1.p - extents1.x*axis10; + + } +// return generateContacts(ctcPts, depths, extents1.y, extents1.z, extents0, trs, transform0, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents1.y, extents1.z, extents0, trs, transform0, contactDistance); + + case AXIS_B1: +// *ctcNrm = axis11; + trs.m.column2 = axis10; // Factored out + if(sign) + { + ctcNrm = axis11; + trs.m.column0 = -axis11; + trs.m.column1 = -axis12; + trs.p = transform1.p + extents1.y*axis11; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis11; + + trs.m.column0 = axis11; + trs.m.column1 = axis12; + trs.m.column2 = axis10; + trs.p = transform1.p - extents1.y*axis11; + } +// return generateContacts(ctcPts, depths, extents1.z, extents1.x, extents0, trs, transform0, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents1.z, extents1.x, extents0, trs, transform0, contactDistance); + + case AXIS_B2: +// *ctcNrm = axis12; + trs.m.column2 = axis11; // Factored out + + if(sign) + { + ctcNrm = axis12; + trs.m.column0 = -axis12; + trs.m.column1 = -axis10; + trs.p = transform1.p + extents1.z*axis12; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis12; + + trs.m.column0 = axis12; + trs.m.column1 = axis10; + trs.p = transform1.p - extents1.z*axis12; + } + +// return generateContacts(ctcPts, depths, extents1.x, extents1.y, extents0, trs, transform0, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents1.x, extents1.y, extents0, trs, transform0, contactDistance); + } +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp new file mode 100644 index 000000000..c2502147f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp @@ -0,0 +1,457 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactBuffer.h" +#include "GuIntersectionRayBox.h" +#include "GuDistanceSegmentBox.h" +#include "GuInternal.h" +#include "GuContactMethodImpl.h" +#include "PsMathUtils.h" +#include "PsUtilities.h" +#include "GuGeometryUnion.h" +#include "GuBoxConversion.h" + +using namespace physx; +using namespace Gu; + +/*namespace Gu +{ +const PxU8* getBoxEdges(); +}*/ + +///////// + /*#include "CmRenderOutput.h" + #include "PxsContext.h" + static void gVisualizeBox(const Box& box, PxcNpThreadContext& context, PxU32 color=0xffffff) + { + PxMat33 rot(box.base.column0, box.base.column1, box.base.column2); + PxMat44 m(rot, box.origin); + + DebugBox db(box.extent); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m; + out << db; + } + static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) + { + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::LINES << a << b; + }*/ +///////// + + +static const PxReal fatBoxEdgeCoeff = 0.01f; + +static bool intersectEdgeEdgePreca(const PxVec3& p1, const PxVec3& p2, const PxVec3& v1, const PxPlane& plane, PxU32 i, PxU32 j, float coeff, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip) +{ + // if colliding edge (p3,p4) does not cross plane return no collision + // same as if p3 and p4 on same side of plane return 0 + // + // Derivation: + // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // if d3 and d4 have the same sign, they're on the same side of the plane => no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + PxVec3 v2 = p4 - p3; + + temp = plane.n.dot(v2); + if(temp==0.0f) return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i]))*coeff; + if(dist<0.0f) return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<0.0f) return true; // collision found + + return false; // no collision +} + + +static bool GuTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, const Box& box, PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project box + PxReal Min1, Max1; + { + const PxReal BoxCen = box.center.dot(axis); + const PxReal BoxExt = + PxAbs(box.rot.column0.dot(axis)) * box.extents.x + + PxAbs(box.rot.column1.dot(axis)) * box.extents.y + + PxAbs(box.rot.column2.dot(axis)) * box.extents.z; + + Min1 = BoxCen - BoxExt; + Max1 = BoxCen + BoxExt; + } + + // Test projections + if(max0=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + + +static bool GuCapsuleOBBOverlap3(const Segment& segment, PxReal radius, const Box& box, PxReal* t=NULL, PxVec3* pp=NULL) +{ + PxVec3 Sep(PxReal(0)); + PxReal PenDepth = PX_MAX_REAL; + + // Test normals + for(PxU32 i=0;i<3;i++) + { + PxReal d; + if(!GuTestAxis(box.rot[i], segment, radius, box, d)) + return false; + + if(d(); + const PxBoxGeometry& shapeBox = shape1.get(); + + // PT: TODO: move computations to local space + + // Capsule data + Segment worldSegment; + getCapsuleSegment(transform0, shapeCapsule, worldSegment); + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + // Box data + Box worldBox; + buildFrom(worldBox, transform1.p, shapeBox.halfExtents, transform1.q); + + // Collision detection + PxReal t; + PxVec3 onBox; + const PxReal squareDist = distanceSegmentBoxSquared(worldSegment.p0, worldSegment.p1, worldBox.center, worldBox.extents, worldBox.rot, &t, &onBox); + + if(squareDist >= inflatedRadius*inflatedRadius) + return false; + + PX_ASSERT(contactBuffer.count==0); + + if(squareDist != 0.0f) + { + // PT: the capsule segment doesn't intersect the box => distance-based version + const PxVec3 onSegment = worldSegment.getPointAt(t); + onBox = worldBox.center + worldBox.rot.transform(onBox); + + PxVec3 normal = onSegment - onBox; + PxReal normalLen = normal.magnitude(); + + if(normalLen > 0.0f) + { + normal *= 1.0f/normalLen; + + // PT: generate VF contacts for segment's vertices vs box + GuGenerateVFContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, normal, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + GuGenerateEEContacts2(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, normal, params.mContactDistance); + + // PT: run VF case for box-vertex-vs-capsule only if we don't have any contact yet + if(!contactBuffer.count) + contactBuffer.contact(onBox, normal, sqrtf(squareDist) - shapeCapsule.radius); + } + else + { + // On linux we encountered the following: + // For a case where a segment endpoint lies on the surface of a box, the squared distance between segment and box was tiny but still larger than 0. + // However, the computation of the normal length was exactly 0. In that case we should have switched to the penetration based version so we do it now + // instead. + goto PenetrationBasedCode; + } + } + else + { + PenetrationBasedCode: + + // PT: the capsule segment intersects the box => penetration-based version + + // PT: compute penetration vector (MTD) + PxVec3 sepAxis; + PxReal depth; + if(!GuCapsuleOBBOverlap3(worldSegment, shapeCapsule.radius, worldBox, &depth, &sepAxis)) return false; + + // PT: generate VF contacts for segment's vertices vs box + GuGenerateVFContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, sepAxis, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + GuGenerateEEContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, sepAxis); + + if(!contactBuffer.count) + { + contactBuffer.contact(worldSegment.computeCenter(), sepAxis, -(shapeCapsule.radius + depth)); + return true; + } + } + return true; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp new file mode 100644 index 000000000..13ce6e029 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp @@ -0,0 +1,155 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactBuffer.h" +#include "GuDistanceSegmentSegment.h" +#include "GuContactMethodImpl.h" +#include "GuInternal.h" +#include "GuGeometryUnion.h" + +using namespace physx; + +namespace physx +{ +namespace Gu +{ +bool contactCapsuleCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom0 = shape0.get(); + const PxCapsuleGeometry& capsuleGeom1 = shape1.get(); + + // PT: get capsules in local space + PxVec3 dir[2]; + Segment segment[2]; + { + const PxVec3 capsuleLocalSegment0 = getCapsuleHalfHeightVector(transform0, capsuleGeom0); + const PxVec3 capsuleLocalSegment1 = getCapsuleHalfHeightVector(transform1, capsuleGeom1); + + const PxVec3 delta = transform1.p - transform0.p; + segment[0].p0 = capsuleLocalSegment0; + segment[0].p1 = -capsuleLocalSegment0; + dir[0] = -capsuleLocalSegment0*2.0f; + segment[1].p0 = capsuleLocalSegment1 + delta; + segment[1].p1 = -capsuleLocalSegment1 + delta; + dir[1] = -capsuleLocalSegment1*2.0f; + } + + // PT: compute distance between capsules' segments + PxReal s,t; + const PxReal squareDist = distanceSegmentSegmentSquared(segment[0], segment[1], &s, &t); + const PxReal radiusSum = capsuleGeom0.radius + capsuleGeom1.radius; + const PxReal inflatedSum = radiusSum + params.mContactDistance; + const PxReal inflatedSumSquared = inflatedSum*inflatedSum; + + if(squareDist >= inflatedSumSquared) + return false; + + // PT: TODO: optimize this away + PxReal segLen[2]; + segLen[0] = dir[0].magnitude(); + segLen[1] = dir[1].magnitude(); + + if (segLen[0]) dir[0] *= 1.0f / segLen[0]; + if (segLen[1]) dir[1] *= 1.0f / segLen[1]; + + if (PxAbs(dir[0].dot(dir[1])) > 0.9998f) //almost parallel, ca. 1 degree difference --> generate two contact points at ends + { + PxU32 numCons = 0; + + PxReal segLenEps[2]; + segLenEps[0] = segLen[0] * 0.001f;//0.1% error is ok. + segLenEps[1] = segLen[1] * 0.001f; + + //project the two end points of each onto the axis of the other and take those 4 points. + //we could also generate a single normal at the single closest point, but this would be 'unstable'. + + for (PxU32 destShapeIndex = 0; destShapeIndex < 2; destShapeIndex ++) + { + for (PxU32 startEnd = 0; startEnd < 2; startEnd ++) + { + const PxU32 srcShapeIndex = 1-destShapeIndex; + //project start/end of srcShapeIndex onto destShapeIndex. + PxVec3 pos[2]; + pos[destShapeIndex] = startEnd ? segment[srcShapeIndex].p1 : segment[srcShapeIndex].p0; + const PxReal p = dir[destShapeIndex].dot(pos[destShapeIndex] - segment[destShapeIndex].p0); + if (p >= -segLenEps[destShapeIndex] && p <= (segLen[destShapeIndex] + segLenEps[destShapeIndex])) + { + pos[srcShapeIndex] = p * dir[destShapeIndex] + segment[destShapeIndex].p0; + + PxVec3 normal = pos[1] - pos[0]; + + const PxReal normalLenSq = normal.magnitudeSquared(); + if (normalLenSq > 1e-6f && normalLenSq < inflatedSumSquared) + { + const PxReal distance = PxSqrt(normalLenSq); + normal *= 1.0f/distance; + PxVec3 point = pos[1] - normal * (srcShapeIndex ? capsuleGeom1 : capsuleGeom0).radius; + point += transform0.p; + contactBuffer.contact(point, normal, distance - radiusSum); + numCons++; + } + } + } + } + + if (numCons) //if we did not have contacts, then we may have the case where they are parallel, but are stacked end to end, in which case the old code will generate good contacts. + return true; + } + + // Collision response + PxVec3 pos1 = segment[0].getPointAt(s); + PxVec3 pos2 = segment[1].getPointAt(t); + + PxVec3 normal = pos1 - pos2; + + const PxReal normalLenSq = normal.magnitudeSquared(); + if (normalLenSq < 1e-6f) + { + // PT: TODO: revisit this. "FW" sounds old. + // Zero normal -> pick the direction of segment 0. + // Not always accurate but consistent with FW. + if (segLen[0] > 1e-6f) + normal = dir[0]; + else + normal = PxVec3(1.0f, 0.0f, 0.0f); + } + else + { + normal *= PxRecipSqrt(normalLenSq); + } + + pos1 += transform0.p; + contactBuffer.contact(pos1 - normal * capsuleGeom0.radius, normal, PxSqrt(squareDist) - radiusSum); + return true; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp new file mode 100644 index 000000000..772f04f7b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp @@ -0,0 +1,587 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexMesh.h" +#include "GuConvexHelper.h" +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuVecConvexHull.h" +#include "GuVecCapsule.h" +#include "GuInternal.h" +#include "GuGJK.h" +#include "GuGeometryUnion.h" + +using namespace physx; +using namespace Gu; + +/////////// +// #include "CmRenderOutput.h" +// #include "PxsContext.h" +// static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) +// { +// PxMat44 m = PxMat44::identity(); +// +// Cm::RenderOutput& out = context.mRenderOutput; +// out << color << m << Cm::RenderOutput::LINES << a << b; +// } +/////////// + +static const PxReal fatConvexEdgeCoeff = 0.01f; + +static bool intersectEdgeEdgePreca(const PxVec3& p1, const PxVec3& p2, const PxVec3& v1, const PxPlane& plane, PxU32 i, PxU32 j, float coeff, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip, float limit) +{ + // if colliding edge (p3,p4) does not cross plane return no collision + // same as if p3 and p4 on same side of plane return 0 + // + // Derivation: + // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // if d3 and d4 have the same sign, they're on the same side of the plane => no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) + return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + PxVec3 v2 = p4 - p3; + + temp = plane.n.dot(v2); + if(temp==0.0f) + return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i]))*coeff; + if(distmax0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project convex + PxReal Min1, Max1; + (polyData.mProjectHull)(polyData, axis, worldTM, scaling, Min1, Max1); + + // Test projections + if(max0=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +static bool GuCapsuleConvexOverlap(const Gu::Segment& segment, PxReal radius, + const PolygonalData& polyData, + const Cm::FastVertex2ShapeScaling& scaling, + const PxTransform& transform, + PxReal* t, PxVec3* pp, bool isSphere) +{ + // TODO: + // - test normal & edge in same loop + // - local space + // - use precomputed face value + // - optimize projection + + PxVec3 Sep(0,0,0); + PxReal PenDepth = PX_MAX_REAL; + + PxU32 nbPolys = polyData.mNbPolygons; + const Gu::HullPolygonData* polys = polyData.mPolygons; + + const Cm::Matrix34 worldTM(transform); + + // Test normals + for(PxU32 i=0;i 1E-7f) //the ray direction "exits" from the back side + { + earliestExit = physx::intrinsics::selectMin(earliestExit, distAlongRay); + } + else if (dn < -1E-7f) //the ray direction "enters" from the front side + { +/* if (distAlongRay > latestEntry) + { + latestEntry = distAlongRay; + }*/ + latestEntry = physx::intrinsics::selectMax(latestEntry, distAlongRay); + } + else + { + //plane normal and ray dir are orthogonal + if(distToPlane > 0.0f) + return false; //a plane is parallel with ray -- and we're outside the ray -- we definitely miss the entire convex! + } + } + + if(latestEntry < earliestExit && latestEntry != -FLT_MAX && latestEntry < maxDist-1e-5f) + { + t = latestEntry; + return true; + } + return false; +} + +// PT: version based on Gu::raycast_convexMesh to handle scaling, but modified to make sure it works when ray starts inside the convex +static void GuGenerateVFContacts2(ContactBuffer& contactBuffer, + // + const PxTransform& convexPose, + const PolygonalData& polyData, // Convex data + const PxMeshScale& scale, + // + PxU32 nbPts, + const PxVec3* PX_RESTRICT points, + const PxReal radius, // Capsule's radius + // + const PxVec3& normal, + const PxReal contactDistance) +{ + PX_ASSERT(PxAbs(normal.magnitudeSquared()-1)<1e-4f); + + //scaling: transform the ray to vertex space + const Cm::Matrix34 world2vertexSkew = scale.getInverse() * convexPose.getInverse(); + + const PxVec3 vrayDir = world2vertexSkew.rotate( -normal ); + + const PxReal maxDist = contactDistance + radius; + + for(PxU32 i=0;i(); + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + + PxVec3 onSegment, onConvex; + PxReal distance; + PxVec3 normal_; + { + Gu::ConvexMesh* cm = static_cast(shapeConvex.convexMesh); + + using namespace Ps::aos; + Vec3V closA, closB, normalV; + GjkStatus status; + FloatV dist; + { + + const Vec3V zeroV = V3Zero(); + const Gu::ConvexHullData* hullData = &cm->getHull(); + + const FloatV capsuleHalfHeight = FLoad(shapeCapsule.halfHeight); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const PsMatTransformV aToB(transform1.transformInv(transform0)); + + Gu::ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, shapeConvex.scale.isIdentity()); + + //transform capsule(a) into the local space of convexHull(b), treat capsule as segment + Gu::CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), FZero()); + + + LocalConvex convexA(capsule); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(convexA.getCenter(), convexB.getCenter()); + + status = gjk, LocalConvex >(convexA, convexB, initialSearchDir, FMax(),closA, closB, normalV, dist); + } + + + if(status == GJK_CONTACT) + distance = 0.f; + else + { + //const FloatV sqDist = FMul(dist, dist); + V3StoreU(closB, onConvex); + FStore(dist, &distance); + V3StoreU(normalV, normal_); + onConvex = transform1.transform(onConvex); + normal_ = transform1.rotate(normal_); + } + } + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + if(distance >= inflatedRadius) + return false; + + Gu::Segment worldSegment; + getCapsuleSegment(transform0, shapeCapsule, worldSegment); + + const bool isSphere = worldSegment.p0 == worldSegment.p1; + const PxU32 nbPts = PxU32(isSphere ? 1 : 2); + + PX_ASSERT(contactBuffer.count==0); + + Cm::FastVertex2ShapeScaling convexScaling; + const bool idtConvexScale = shapeConvex.scale.isIdentity(); + if(!idtConvexScale) + convexScaling.init(shapeConvex.scale); + + PolygonalData polyData; + getPolygonalData_Convex(&polyData, shapeConvex.hullData, convexScaling); + +// if(0) + if(distance > 0.f) + { + // PT: the capsule segment doesn't intersect the convex => distance-based version + PxVec3 normal = -normal_; + + // PT: generate VF contacts for segment's vertices vs convex + GuGenerateVFContacts2( contactBuffer, + transform1, polyData, shapeConvex.scale, + nbPts, &worldSegment.p0, shapeCapsule.radius, + normal, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + if(!isSphere) + { + const Cm::Matrix34 worldTM(transform1); + GuGenerateEEContacts2b(contactBuffer, worldSegment, shapeCapsule.radius, + worldTM, polyData, convexScaling, + normal, params.mContactDistance); + } + + // PT: run VF case for convex-vertex-vs-capsule only if we don't have any contact yet + if(!contactBuffer.count) + { +// gVisualizeLine(onConvex, onConvex + normal, context, PxDebugColor::eARGB_RED); + //PxReal distance = PxSqrt(sqDistance); + contactBuffer.contact(onConvex, normal, distance - shapeCapsule.radius); + } + } + else + { + // PT: the capsule segment intersects the convex => penetration-based version +//printf("Penetration-based:\n"); + + // PT: compute penetration vector (MTD) + PxVec3 SepAxis; + if(!GuCapsuleConvexOverlap(worldSegment, shapeCapsule.radius, polyData, convexScaling, transform1, NULL, &SepAxis, isSphere)) + { +//printf("- no overlap\n"); + return false; + } + + // PT: generate VF contacts for segment's vertices vs convex + GuGenerateVFContacts2( contactBuffer, + transform1, polyData, shapeConvex.scale, + nbPts, &worldSegment.p0, shapeCapsule.radius, + SepAxis, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts +//printf("- %d VF contacts\n", contactBuffer.count); + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + if(!isSphere) + { + GuGenerateEEContacts(contactBuffer, worldSegment, shapeCapsule.radius, params.mContactDistance, polyData, transform1, convexScaling, SepAxis); +//printf("- %d total contacts\n", contactBuffer.count); + } + } + return true; +} + +} +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp new file mode 100644 index 000000000..cc56351c1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp @@ -0,0 +1,635 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionEdgeEdge.h" +#include "GuDistanceSegmentTriangle.h" +#include "GuIntersectionRayTriangle.h" +#include "GuIntersectionTriangleBox.h" +#include "GuInternal.h" +#include "GuContactMethodImpl.h" +#include "GuFeatureCode.h" +#include "GuContactBuffer.h" +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuConvexEdgeFlags.h" +#include "GuGeometryUnion.h" +#include "GuSIMDHelpers.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; + +#define DEBUG_RENDER_MESHCONTACTS 0 + +#if DEBUG_RENDER_MESHCONTACTS +#include "PxPhysics.h" +#include "PxScene.h" +#endif + +#define USE_AABB_TRI_CULLING + +//#define USE_CAPSULE_TRI_PROJ_CULLING +//#define USE_CAPSULE_TRI_SAT_CULLING +#define VISUALIZE_TOUCHED_TRIS 0 +#define VISUALIZE_CULLING_BOX 0 + +#if VISUALIZE_TOUCHED_TRIS +#include "CmRenderOutput.h" +#include "PxsContactManager.h" +#include "PxsContext.h" +static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) +{ + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::LINES << a << b; +} +static void gVisualizeTri(const PxVec3& a, const PxVec3& b, const PxVec3& c, PxcNpThreadContext& context, PxU32 color=0xffffff) +{ + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::TRIANGLES << a << b << c; +} + +static PxU32 gColors[8] = { 0xff0000ff, 0xff00ff00, 0xffff0000, + 0xff00ffff, 0xffff00ff, 0xffffff00, + 0xff000080, 0xff008000}; +#endif + +static const float fatBoxEdgeCoeff = 0.01f; + +static bool PxcTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, + const PxVec3* PX_RESTRICT triVerts, PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project triangle + float Min1, Max1; + { + Min1 = Max1 = triVerts[0].dot(axis); + const PxReal dp1 = triVerts[1].dot(axis); + Min1 = physx::intrinsics::selectMin(Min1, dp1); + Max1 = physx::intrinsics::selectMax(Max1, dp1); + const PxReal dp2 = triVerts[2].dot(axis); + Min1 = physx::intrinsics::selectMin(Min1, dp2); + Max1 = physx::intrinsics::selectMax(Max1, dp2); + } + + // Test projections + if(max0=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +PX_FORCE_INLINE static PxVec3 PxcComputeTriangleNormal(const PxVec3* PX_RESTRICT triVerts) +{ + return ((triVerts[0]-triVerts[1]).cross(triVerts[0]-triVerts[2])).getNormalized(); +} + +PX_FORCE_INLINE static PxVec3 PxcComputeTriangleCenter(const PxVec3* PX_RESTRICT triVerts) +{ + static const PxReal inv3 = 1.0f / 3.0f; + return (triVerts[0] + triVerts[1] + triVerts[2]) * inv3; +} + +static bool PxcCapsuleTriOverlap3(PxU8 edgeFlags, const Segment& segment, PxReal radius, const PxVec3* PX_RESTRICT triVerts, + PxReal* PX_RESTRICT t=NULL, PxVec3* PX_RESTRICT pp=NULL) +{ + PxReal penDepth = PX_MAX_REAL; + + // Test normal + PxVec3 sep = PxcComputeTriangleNormal(triVerts); + if(!PxcTestAxis(sep, segment, radius, triVerts, penDepth)) + return false; + + // Test edges + // ML:: use the active edge flag instead of the concave flag + const PxU32 activeEdgeFlag[] = {ETD_CONVEX_EDGE_01, ETD_CONVEX_EDGE_12, ETD_CONVEX_EDGE_20}; + const PxVec3 capsuleAxis = (segment.p1 - segment.p0).getNormalized(); + for(PxU32 i=0;i<3;i++) + { + //bool active =((edgeFlags & ignoreEdgeFlag[i]) == 0); + + if(edgeFlags & activeEdgeFlag[i]) + { + + const PxVec3 e0 = triVerts[i]; +// const PxVec3 e1 = triVerts[(i+1)%3]; + const PxVec3 e1 = triVerts[Ps::getNextIndex3(i)]; + const PxVec3 edge = e0 - e1; + + PxVec3 cross = capsuleAxis.cross(edge); + if(!Ps::isAlmostZero(cross)) + { + cross = cross.getNormalized(); + PxReal d; + if(!PxcTestAxis(cross, segment, radius, triVerts, d)) + return false; + if(dgetRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << Hit << (Hit + wn * 10.0f); + #endif + } + } +} + +// PT: PxcGenerateEEContacts2 uses a segment-triangle distance function, which breaks when the segment +// intersects the triangle, in which case you need to switch to a penetration-depth computation. +// If you don't do this thin capsules don't work. +static void PxcGenerateEEContacts( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, const PxReal radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, PxU32 triangleIndex) +{ + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff); + + for(PxU32 i=0;i<3;i++) + { + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge(triVerts[i], triVerts[Ps::getNextIndex3(i)], -normal, s0, s1, dist, ip)) + { + ip = meshAbsPose.transform(ip); + const PxVec3 wn = meshAbsPose.rotate(normal); + + contactBuffer.contact(ip, wn, - (radius + dist), triangleIndex); + #if DEBUG_RENDER_MESHCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << ip << (ip + wn * 10.0f); + #endif + } + } +} + +static void PxcGenerateEEContacts2( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, const PxReal radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, PxU32 triangleIndex, PxReal contactDistance) +{ + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff); + + for(PxU32 i=0;i<3;i++) + { + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge(triVerts[i], triVerts[Ps::getNextIndex3(i)], normal, s0, s1, dist, ip) && dist < radius+contactDistance) + { + ip = meshAbsPose.transform(ip); + const PxVec3 wn = meshAbsPose.rotate(normal); + + contactBuffer.contact(ip, wn, dist - radius, triangleIndex); + #if DEBUG_RENDER_MESHCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << ip << (ip + wn * 10.0f); + #endif + } + } +} + +namespace +{ +struct CapsuleMeshContactGeneration +{ + ContactBuffer& mContactBuffer; + const Cm::Matrix34 mMeshAbsPose; + const Segment& mMeshCapsule; +#ifdef USE_AABB_TRI_CULLING + Vec3p mBC; + Vec3p mBE; +#endif + PxReal mInflatedRadius; + PxReal mContactDistance; + PxReal mShapeCapsuleRadius; + + CapsuleMeshContactGeneration(ContactBuffer& contactBuffer, const PxTransform& transform1, const Segment& meshCapsule, PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius) : + mContactBuffer (contactBuffer), + mMeshAbsPose (Cm::Matrix34(transform1)), + mMeshCapsule (meshCapsule), + mInflatedRadius (inflatedRadius), + mContactDistance (contactDistance), + mShapeCapsuleRadius (shapeCapsuleRadius) + { + PX_ASSERT(contactBuffer.count==0); +#ifdef USE_AABB_TRI_CULLING + mBC = (meshCapsule.p0 + meshCapsule.p1)*0.5f; + const Vec3p be = (meshCapsule.p0 - meshCapsule.p1)*0.5f; + mBE.x = fabsf(be.x) + inflatedRadius; + mBE.y = fabsf(be.y) + inflatedRadius; + mBE.z = fabsf(be.z) + inflatedRadius; +#endif + } + + void processTriangle(PxU32 triangleIndex, const TrianglePadded& tri, PxU8 extraData/*, const PxU32* vertInds*/) + { +#ifdef USE_AABB_TRI_CULLING + #if VISUALIZE_CULLING_BOX + { + Cm::RenderOutput& out = context.mRenderOutput; + PxTransform idt = PxTransform(PxIdentity); + out << idt; + out << 0xffffffff; + out << Cm::DebugBox(mBC, mBE, true); + } + #endif +#endif + const PxVec3& p0 = tri.verts[0]; + const PxVec3& p1 = tri.verts[1]; + const PxVec3& p2 = tri.verts[2]; + +#ifdef USE_AABB_TRI_CULLING + // PT: this one is safe because triangle class is padded + // PT: TODO: is this test really needed? Not done in midphase already? + if(!intersectTriangleBox_Unsafe(mBC, mBE, p0, p1, p2)) + return; +#endif + +#ifdef USE_CAPSULE_TRI_PROJ_CULLING + PxVec3 triCenter = (p0 + p1 + p2)*0.33333333f; + PxVec3 delta = mBC - triCenter; + + PxReal depth; + if(!PxcTestAxis(delta, mMeshCapsule, mInflatedRadius, tri.verts, depth)) + return; +#endif + +#if VISUALIZE_TOUCHED_TRIS + gVisualizeTri(p0, p1, p2, context, PxDebugColor::eARGB_RED); +#endif + +#ifdef USE_CAPSULE_TRI_SAT_CULLING + PxVec3 SepAxis; + if(!PxcCapsuleTriOverlap3(extraData, mMeshCapsule, mInflatedRadius, tri.verts, NULL, &SepAxis)) + return; +#endif + + PxReal t,u,v; + const PxVec3 p1_p0 = p1 - p0; + const PxVec3 p2_p0 = p2 - p0; + const PxReal squareDist = distanceSegmentTriangleSquared(mMeshCapsule, p0, p1_p0, p2_p0, &t, &u, &v); + + // PT: do cheaper test first! + if(squareDist >= mInflatedRadius*mInflatedRadius) + return; + + // PT: backface culling without the normalize + // PT: TODO: consider doing before the segment-triangle distance test if it's cheaper + const PxVec3 planeNormal = p1_p0.cross(p2_p0); + const PxF32 planeD = planeNormal.dot(p0); // PT: actually -d compared to PxcPlane + if(planeNormal.dot(mBC) < planeD) + return; + + if(squareDist > 0.001f*0.001f) + { + // Contact information + PxVec3 normal; + if(selectNormal(extraData, u, v)) + { + normal = planeNormal.getNormalized(); + } + else + { + const PxVec3 pointOnTriangle = Ps::computeBarycentricPoint(p0, p1, p2, u, v); + + const PxVec3 pointOnSegment = mMeshCapsule.getPointAt(t); + normal = pointOnSegment - pointOnTriangle; + const PxReal l = normal.magnitude(); + if(l == 0.0f) + return; + normal = normal / l; + } + + PxcGenerateEEContacts2(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, normal, triangleIndex, mContactDistance); + PxcGenerateVFContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, normal, triangleIndex, mContactDistance); + } + else + { + PxVec3 SepAxis; + if(!PxcCapsuleTriOverlap3(extraData, mMeshCapsule, mInflatedRadius, tri.verts, NULL, &SepAxis)) + return; + + PxcGenerateEEContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, SepAxis, triangleIndex); + PxcGenerateVFContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, SepAxis, triangleIndex, mContactDistance); + } + } + +private: + CapsuleMeshContactGeneration& operator=(const CapsuleMeshContactGeneration&); +}; + +struct CapsuleMeshContactGenerationCallback_NoScale : MeshHitCallback +{ + CapsuleMeshContactGeneration mGeneration; + const TriangleMesh* mMeshData; + + CapsuleMeshContactGenerationCallback_NoScale( + ContactBuffer& contactBuffer, + const PxTransform& transform1, const Segment& meshCapsule, + PxReal inflatedRadius, PxReal contactDistance, + PxReal shapeCapsuleRadius, const TriangleMesh* meshData + ) : + MeshHitCallback (CallbackMode::eMULTIPLE), + mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius), + mMeshData (meshData) + { + PX_ASSERT(contactBuffer.count==0); + } + + PX_FORCE_INLINE PxAgain processTriangle(const PxRaycastHit& hit, const TrianglePadded& tri) + { + const PxU32 triangleIndex = hit.faceIndex; + + //ML::set all the edges to be active, if the mExtraTrigData exist, we overwrite this flag + const PxU8 extraData = getConvexEdgeFlags(mMeshData->getExtraTrigData(), triangleIndex); + mGeneration.processTriangle(triangleIndex, tri, extraData); + return true; + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/) + { + TrianglePadded tri; + // PT: TODO: revisit this, avoid the copy + tri.verts[0] = v0; + tri.verts[1] = v1; + tri.verts[2] = v2; + return processTriangle(hit, tri); + } + +private: + CapsuleMeshContactGenerationCallback_NoScale& operator=(const CapsuleMeshContactGenerationCallback_NoScale&); +}; + +struct CapsuleMeshContactGenerationCallback_Scale : CapsuleMeshContactGenerationCallback_NoScale +{ + const Cm::FastVertex2ShapeScaling& mScaling; + + CapsuleMeshContactGenerationCallback_Scale( + ContactBuffer& contactBuffer, + const PxTransform& transform1, const Segment& meshCapsule, + PxReal inflatedRadius, const Cm::FastVertex2ShapeScaling& scaling, PxReal contactDistance, + PxReal shapeCapsuleRadius, const TriangleMesh* meshData + ) : + CapsuleMeshContactGenerationCallback_NoScale(contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius, meshData), + mScaling (scaling) + { + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/) + { + TrianglePadded tri; + getScaledVertices(tri.verts, v0, v1, v2, false, mScaling); + return processTriangle(hit, tri); + } + +private: + CapsuleMeshContactGenerationCallback_Scale& operator=(const CapsuleMeshContactGenerationCallback_Scale&); +}; + +} + +// PT: computes local capsule without going to world-space +static PX_FORCE_INLINE Segment computeLocalCapsule(const PxTransform& transform0, const PxTransform& transform1, const PxCapsuleGeometry& shapeCapsule) +{ + const PxVec3 halfHeight = getCapsuleHalfHeightVector(transform0, shapeCapsule); + const PxVec3 delta = transform1.p - transform0.p; + return Segment( + transform1.rotateInv(halfHeight - delta), + transform1.rotateInv(-halfHeight - delta)); +} + +bool Gu::contactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate! + const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule); + + const TriangleMesh* meshData = shapeMesh.meshData; + + //bound the capsule in shape space by an OBB: + Box queryBox; + { + const Capsule queryCapsule(meshCapsule, inflatedRadius); + queryBox.create(queryCapsule); + } + + if(shapeMesh.scale.isIdentity()) + { + CapsuleMeshContactGenerationCallback_NoScale callback(contactBuffer, transform1, meshCapsule, + inflatedRadius, params.mContactDistance, shapeCapsule.radius, meshData); + + // PT: TODO: switch to capsule query here + Midphase::intersectOBB(meshData, queryBox, callback, true); + } + else + { + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + CapsuleMeshContactGenerationCallback_Scale callback(contactBuffer, transform1, meshCapsule, + inflatedRadius, meshScaling, params.mContactDistance, shapeCapsule.radius, meshData); + + //switched from capsuleCollider to boxCollider so we can support nonuniformly scaled meshes by scaling the query region: + + //apply the skew transform to the box: + meshScaling.transformQueryBounds(queryBox.center, queryBox.extents, queryBox.rot); + + Midphase::intersectOBB(meshData, queryBox, callback, true); + } + return contactBuffer.count > 0; +} + +namespace +{ +struct CapsuleHeightfieldContactGenerationCallback : EntityReport +{ + CapsuleMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + const PxTransform& mTransform1; + + CapsuleHeightfieldContactGenerationCallback( + ContactBuffer& contactBuffer, + const PxTransform& transform1, HeightFieldUtil& hfUtil, const Segment& meshCapsule, + PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius + ) : + mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius), + mHfUtil (hfUtil), + mTransform1 (transform1) + { + PX_ASSERT(contactBuffer.count==0); + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + const PxU8 nextInd[] = {2,0,1}; + + while(nb--) + { + const PxU32 triangleIndex = *indices++; + + PxU32 vertIndices[3]; + TrianglePadded currentTriangle; // in world space + PxU32 adjInds[3]; + mHfUtil.getTriangle(mTransform1, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); + + PxVec3 normal; + currentTriangle.normal(normal); + + PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF + + for(PxU32 a = 0; a < 3; ++a) + { + if(adjInds[a] != 0xFFFFFFFF) + { + PxTriangle adjTri; + mHfUtil.getTriangle(mTransform1, adjTri, NULL, NULL, adjInds[a], false, false); + //We now compare the triangles to see if this edge is active + + PxVec3 adjNormal; + adjTri.denormalizedNormal(adjNormal); + PxU32 otherIndex = nextInd[a]; + PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); + if(projD < 0.f) + { + adjNormal.normalize(); + + PxF32 proj = adjNormal.dot(normal); + + if(proj < 0.999f) + { + triFlags |= 1 << (a+3); + } + } + } + else + { + triFlags |= 1 << (a+3); + } + } + + mGeneration.processTriangle(triangleIndex, currentTriangle, triFlags); + } + return true; + } + +private: + CapsuleHeightfieldContactGenerationCallback& operator=(const CapsuleHeightfieldContactGenerationCallback&); +}; +} + +bool Gu::contactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get(); + const PxHeightFieldGeometryLL& shapeMesh = shape1.get(); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate! + const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule); + + // We must be in local space to use the cache + + HeightFieldUtil hfUtil(shapeMesh); + + CapsuleHeightfieldContactGenerationCallback callback( + contactBuffer, transform1, hfUtil, meshCapsule, inflatedRadius, params.mContactDistance, shapeCapsule.radius); + + //switched from capsuleCollider to boxCollider so we can support nonuniformly scaled meshes by scaling the query region: + + //bound the capsule in shape space by an OBB: + + PxBounds3 bounds; + bounds.maximum = PxVec3(shapeCapsule.halfHeight + inflatedRadius, inflatedRadius, inflatedRadius); + bounds.minimum = -bounds.maximum; + + bounds = PxBounds3::transformFast(transform1.transformInv(transform0), bounds); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &callback); + + return contactBuffer.count > 0; +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp new file mode 100644 index 000000000..e5ed03c3e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp @@ -0,0 +1,1033 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsMathUtils.h" +#include "GuContactPolygonPolygon.h" +#include "GuGeometryUnion.h" +#include "GuConvexHelper.h" +#include "GuInternal.h" +#include "GuSeparatingAxes.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "PsFPU.h" + +// csigg: the single reference of gEnableOptims (below) has been +// replaced with the actual value to prevent ios64 compiler crash. +// static const int gEnableOptims = 1; +#define CONVEX_CONVEX_ROUGH_FIRST_PASS +#define TEST_INTERNAL_OBJECTS +#ifdef TEST_INTERNAL_OBJECTS + #define USE_BOX_DATA +#endif + +using namespace physx; +using namespace Gu; +using namespace shdfnd::aos; +using namespace intrinsics; + +#ifdef TEST_INTERNAL_OBJECTS +#ifdef USE_BOX_DATA +PX_FORCE_INLINE void BoxSupport(const float extents[3], const PxVec3& sv, float p[3]) +{ + const PxU32* iextents = reinterpret_cast(extents); + const PxU32* isv = reinterpret_cast(&sv); + PxU32* ip = reinterpret_cast(p); + + ip[0] = iextents[0]|(isv[0]&PX_SIGN_BITMASK); + ip[1] = iextents[1]|(isv[1]&PX_SIGN_BITMASK); + ip[2] = iextents[2]|(isv[2]&PX_SIGN_BITMASK); +} +#endif + +#if PX_DEBUG +static const PxReal testInternalObjectsEpsilon = 1.0e-3f; +#endif + +#ifdef DO_NOT_REMOVE + PX_FORCE_INLINE void testInternalObjects_( const PxVec3& delta_c, const PxVec3& axis, + const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& tr0, const Cm::Matrix34& tr1, + float& dmin, float contactDistance) + { + { +/* float projected0 = axis.dot(tr0.p); + float projected1 = axis.dot(tr1.p); + float min0 = projected0 - polyData0.mInternal.mRadius; + float max0 = projected0 + polyData0.mInternal.mRadius; + float min1 = projected1 - polyData1.mInternal.mRadius; + float max1 = projected1 + polyData1.mInternal.mRadius; +*/ + float MinMaxRadius = polyData0.mInternal.mRadius + polyData1.mInternal.mRadius; + PxVec3 delta = tr0.p - tr1.p; + const PxReal dp = axis.dot(delta); +// const PxReal dp2 = axis.dot(delta_c); + +// const PxReal d0 = max0 - min1; +// const PxReal d0 = projected0 + polyData0.mInternal.mRadius - projected1 + polyData1.mInternal.mRadius; +// const PxReal d0 = projected0 - projected1 + polyData0.mInternal.mRadius + polyData1.mInternal.mRadius; +// const PxReal d0 = projected0 - projected1 + MinMaxRadius; +// const PxReal d0 = axis.dot(tr0.p) - axis.dot(tr1.p) + MinMaxRadius; +// const PxReal d0 = axis.dot(tr0.p - tr1.p) + MinMaxRadius; +// const PxReal d0 = MinMaxRadius + axis.dot(delta); + const PxReal d0 = MinMaxRadius + dp; + +// const PxReal d1 = max1 - min0; +// const PxReal d1 = projected1 + polyData1.mInternal.mRadius - projected0 + polyData0.mInternal.mRadius; +// const PxReal d1 = projected1 - projected0 + polyData1.mInternal.mRadius + polyData0.mInternal.mRadius; +// const PxReal d1 = projected1 - projected0 + MinMaxRadius; +// const PxReal d1 = axis.dot(tr1.p) - axis.dot(tr0.p) + MinMaxRadius; +// const PxReal d1 = axis.dot(tr1.p - tr0.p) + MinMaxRadius; +// const PxReal d1 = MinMaxRadius - axis.dot(delta); + const PxReal d1 = MinMaxRadius - dp; + + dmin = selectMin(d0, d1); + return; + } + + #ifdef USE_BOX_DATA + const PxVec3 localAxis0 = tr0.rotateTranspose(axis); + const PxVec3 localAxis1 = tr1.rotateTranspose(axis); + + const float dp = delta_c.dot(axis); + + float p0[3]; + BoxSupport(polyData0.mInternal.mExtents, localAxis0, p0); + float p1[3]; + BoxSupport(polyData1.mInternal.mExtents, localAxis1, p1); + + const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z; + const float Radius1 = p1[0]*localAxis1.x + p1[1]*localAxis1.y + p1[2]*localAxis1.z; + + const float MinRadius = selectMax(Radius0, polyData0.mInternal.mRadius); + const float MaxRadius = selectMax(Radius1, polyData1.mInternal.mRadius); + #else + const float dp = delta_c.dot(axis); + const float MinRadius = polyData0.mInternal.mRadius; + const float MaxRadius = polyData1.mInternal.mRadius; + #endif + const float MinMaxRadius = MaxRadius + MinRadius; + const float d0 = MinMaxRadius + dp; + const float d1 = MinMaxRadius - dp; + + dmin = selectMin(d0, d1); + } +#endif + +static PX_FORCE_INLINE bool testInternalObjects( const PxVec3& delta_c, const PxVec3& axis, + const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& tr0, const Cm::Matrix34& tr1, + float dmin) +{ +#ifdef USE_BOX_DATA + const PxVec3 localAxis0 = tr0.rotateTranspose(axis); + const PxVec3 localAxis1 = tr1.rotateTranspose(axis); + + const float dp = delta_c.dot(axis); + + float p0[3]; + BoxSupport(polyData0.mInternal.mExtents, localAxis0, p0); + float p1[3]; + BoxSupport(polyData1.mInternal.mExtents, localAxis1, p1); + + const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z; + const float Radius1 = p1[0]*localAxis1.x + p1[1]*localAxis1.y + p1[2]*localAxis1.z; + + const float MinRadius = selectMax(Radius0, polyData0.mInternal.mRadius); + const float MaxRadius = selectMax(Radius1, polyData1.mInternal.mRadius); +#else + const float dp = delta_c.dot(axis); + const float MinRadius = polyData0.mInternal.mRadius; + const float MaxRadius = polyData1.mInternal.mRadius; +#endif + const float MinMaxRadius = MaxRadius + MinRadius; + const float d0 = MinMaxRadius + dp; + const float d1 = MinMaxRadius - dp; + + const float depth = selectMin(d0, d1); + if(depth>dmin) + return false; + return true; +} +#endif + +PX_FORCE_INLINE float PxcMultiplyAdd3x4(PxU32 i, const PxVec3& p0, const PxVec3& p1, const Cm::Matrix34& world) +{ + return (p1.x + p0.x) * world.m.column0[i] + (p1.y + p0.y) * world.m.column1[i] + (p1.z + p0.z) * world.m.column2[i] + 2.0f * world.p[i]; +} + +PX_FORCE_INLINE float PxcMultiplySub3x4(PxU32 i, const PxVec3& p0, const PxVec3& p1, const Cm::Matrix34& world) +{ + return (p1.x - p0.x) * world.m.column0[i] + (p1.y - p0.y) * world.m.column1[i] + (p1.z - p0.z) * world.m.column2[i]; +} + +static PX_FORCE_INLINE bool testNormal( const PxVec3& axis, PxReal min0, PxReal max0, + const PolygonalData& polyData1, + const Cm::Matrix34& m1to0, + const Cm::FastVertex2ShapeScaling& scaling1, + PxReal& depth, PxReal contactDistance) +{ + //The separating axis we want to test is a face normal of hull0 + PxReal min1, max1; + (polyData1.mProjectHull)(polyData1, axis, m1to0, scaling1, min1, max1); + + if(max0+contactDistance boxExtent.x + fAWdU[0]) return false; + + dir.y = PxcMultiplySub3x4(1, p0, p1, world); + boxExtent.y = maximum.y - minimum.y; + diff.y = (PxcMultiplyAdd3x4(1, p0, p1, world) - (maximum.y+minimum.y)); + fAWdU[1] = PxAbs(dir.y); + if(PxAbs(diff.y)> boxExtent.y + fAWdU[1]) return false; + + dir.z = PxcMultiplySub3x4(2, p0, p1, world); + boxExtent.z = maximum.z - minimum.z; + diff.z = (PxcMultiplyAdd3x4(2, p0, p1, world) - (maximum.z+minimum.z)); + fAWdU[2] = PxAbs(dir.z); + if(PxAbs(diff.z)> boxExtent.z + fAWdU[2]) return false; + + PxReal f; + f = dir.y * diff.z - dir.z*diff.y; if(PxAbs(f)>boxExtent.y*fAWdU[2] + boxExtent.z*fAWdU[1]) return false; + f = dir.z * diff.x - dir.x*diff.z; if(PxAbs(f)>boxExtent.x*fAWdU[2] + boxExtent.z*fAWdU[0]) return false; + f = dir.x * diff.y - dir.y*diff.x; if(PxAbs(f)>boxExtent.x*fAWdU[1] + boxExtent.y*fAWdU[0]) return false; + return true; +} + +#define EXPERIMENT + +/* +Edge culling can clearly be improved : in ConvexTest02 for example, edges don't even touch the other mesh sometimes. +*/ +static void PxcFindSeparatingAxes( SeparatingAxes& sa, const PxU32* PX_RESTRICT indices, PxU32 numPolygons, + const PolygonalData& polyData, + const Cm::Matrix34& world0, const PxPlane& plane, + const Cm::Matrix34& m0to1, const PxBounds3& aabb, PxReal contactDistance, + const Cm::FastVertex2ShapeScaling& scaling) +{ +// EdgeCache edgeCache; // PT: TODO: check this is actually useful + const PxVec3* PX_RESTRICT vertices = polyData.mVerts; + const Gu::HullPolygonData* PX_RESTRICT polygons = polyData.mPolygons; + const PxU8* PX_RESTRICT vrefsBase = polyData.mPolygonVertexRefs; + + while(numPolygons--) + { + //Get current polygon + const Gu::HullPolygonData& P = polygons[*indices++]; + const PxU8* PX_RESTRICT VData = vrefsBase + P.mVRef8; + + // Loop through polygon vertices == polygon edges + PxU32 numVerts = P.mNbVerts; + +#ifdef EXPERIMENT + PxVec3 p0,p1; + PxU8 VRef0 = VData[0]; + p0 = scaling * vertices[VRef0]; + bool b0 = plane.distance(p0) <= contactDistance; +#endif + + for(PxU32 j = 0; j < numVerts; j++) + { + PxU32 j1 = j+1; + if(j1 >= numVerts) j1 = 0; + +#ifndef EXPERIMENT + PxU8 VRef0 = VData[j]; +#endif + PxU8 VRef1 = VData[j1]; + +// Ps::order(VRef0, VRef1); //make sure edge (a,b) == edge (b,a) +// if (edgeCache.isInCache(VRef0, VRef1)) +// continue; + + //transform points //TODO: once this works we could transform plan instead, that is more efficient!! + +#ifdef EXPERIMENT + p1 = scaling * vertices[VRef1]; +#else + const PxVec3 p0 = scaling * vertices[VRef0]; + const PxVec3 p1 = scaling * vertices[VRef1]; +#endif + + // Cheap but effective culling! +#ifdef EXPERIMENT + bool b1 = plane.distance(p1) <= contactDistance; + if(b0 || b1) +#else + if(plane.signedDistanceHessianNormalForm(p0) <= contactDistance || + plane.signedDistanceHessianNormalForm(p1) <= contactDistance) +#endif + { + if(PxcSegmentAABBIntersect(p0, p1, aabb.minimum, aabb.maximum, m0to1)) + { + // Create current edge. We're only interested in different edge directions so we normalize. + const PxVec3 currentEdge = world0.rotate(p0 - p1).getNormalized(); + sa.addAxis(currentEdge); + } + } +#ifdef EXPERIMENT + VRef0 = VRef1; + p0 = p1; + b0 = b1; +#endif + } + } +} + +static bool PxcTestFacesSepAxesBackface(const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m1to0, const PxVec3& delta, + PxReal& dmin, PxVec3& sep, PxU32& id, PxU32* PX_RESTRICT indices_, PxU32& numIndices, + PxReal contactDistance, float toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , const PxVec3& worldDelta +#endif + ) +{ + PX_UNUSED(toleranceLength); // Only used in Debug + id = PX_INVALID_U32; + PxU32* indices = indices_; + + const PxU32 num = polyData0.mNbPolygons; + const PxVec3* PX_RESTRICT vertices = polyData0.mVerts; + const Gu::HullPolygonData* PX_RESTRICT polygons = polyData0.mPolygons; + + //transform delta from hull0 shape into vertex space: + const PxVec3 vertSpaceDelta = scaling0 % delta; + + // PT: prefetch polygon data + { + const PxU32 dataSize = num*sizeof(Gu::HullPolygonData); + for(PxU32 offset=0; offset < dataSize; offset+=128) + Ps::prefetchLine(polygons, offset); + } + + for(PxU32 i=0; i < num; i++) + { + const Gu::HullPolygonData& P = polygons[i]; + const PxPlane& PL = P.mPlane; + + // Do backface-culling + if(PL.n.dot(vertSpaceDelta) < 0.0f) + continue; + + //normals transform by inverse transpose: (and transpose(skew) == skew as its symmetric) + PxVec3 shapeSpaceNormal = scaling0 % PL.n; + //renormalize: (Arr!) + const PxReal magnitude = shapeSpaceNormal.normalize(); // PT: We need to find a way to skip this normalize + +#ifdef TEST_INTERNAL_OBJECTS + +/* +const PxVec3 worldNormal_ = world0.rotate(shapeSpaceNormal); +PxReal d0; +bool test0 = PxcTestSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, worldNormal_, d0, contactDistance); + +PxReal d1; +const float invMagnitude0 = 1.0f / magnitude; +bool test1 = PxcTestNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude0, P.getMax() * invMagnitude0, polyData1, m1to0, scaling1, d1, contactDistance); + +PxReal d2; +testInternalObjects_(worldDelta, worldNormal_, polyData0, polyData1, world0, world1, d2, contactDistance); +*/ + + const PxVec3 worldNormal = world0.rotate(shapeSpaceNormal); + if(!testInternalObjects(worldDelta, worldNormal, polyData0, polyData1, world0, world1, dmin)) + { + #if PX_DEBUG + PxReal d; + const float invMagnitude = 1.0f / magnitude; + if(testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s. + { + PX_ASSERT(d + testInternalObjectsEpsilon*toleranceLength >= dmin); + } + #endif + continue; + } +#endif + + *indices++ = i; + + //////////////////// + /* + //gotta transform minimum and maximum from vertex to shape space! + //I think they transform like the 'd' of the plane, basically by magnitude division! + //unfortunately I am not certain of that, so let's convert them to a point in vertex space, transform that, and then convert back to a plane d. + + //let's start by transforming the plane's d: + //a point on the plane: + + PxVec3 vertSpaceDPoint = PL.normal * -PL.d; + + //make sure this is on the plane: + PxReal distZero = PL.signedDistanceHessianNormalForm(vertSpaceDPoint); //should be zero + + //transform: + + PxVec3 shapeSpaceDPoint = cache.mVertex2ShapeSkew[skewIndex] * vertSpaceDPoint; + + //make into a d offset again by projecting along the plane: + PxcPlane shapeSpacePlane(shapeSpaceNormal, shapeSpaceDPoint); + + //see what D is!! + + + //NOTE: for boxes scale[0] is always id so this is all redundant. Hopefully for convex convex it will become useful! + */ + + //////////////////// + + PxReal d; + const float invMagnitude = 1.0f / magnitude; + if(!testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s. + return false; + + if(d < dmin) + { +#ifdef TEST_INTERNAL_OBJECTS + sep = worldNormal; +#else + sep = world0.rotate(shapeSpaceNormal); +#endif + dmin = d; + id = i; + } + } + + numIndices = PxU32(indices - indices_); + + PX_ASSERT(id!=PX_INVALID_U32); //Should never happen with this version + + return true; +} + +// PT: isolating this piece of code allows us to better see what happens in PIX. For some reason it looks +// like isolating it creates *less* LHS than before, but I don't understand why. There's still a lot of +// bad stuff there anyway +//PX_FORCE_INLINE +static void prepareData(PxPlane& witnessPlane0, PxPlane& witnessPlane1, + PxBounds3& aabb0, PxBounds3& aabb1, + const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PxPlane& vertSpacePlane0, const PxPlane& vertSpacePlane1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, + PxReal contactDistance + ) +{ + scaling0.transformPlaneToShapeSpace(vertSpacePlane0.n, vertSpacePlane0.d, witnessPlane0.n, witnessPlane0.d); + scaling1.transformPlaneToShapeSpace(vertSpacePlane1.n, vertSpacePlane1.d, witnessPlane1.n, witnessPlane1.d); + + //witnessPlane0 = witnessPlane0.getTransformed(m0to1); + //witnessPlane1 = witnessPlane1.getTransformed(m1to0); + const PxVec3 newN0 = m0to1.rotate(witnessPlane0.n); + witnessPlane0 = PxPlane(newN0, witnessPlane0.d - m0to1.p.dot(newN0)); + const PxVec3 newN1 = m1to0.rotate(witnessPlane1.n); + witnessPlane1 = PxPlane(newN1, witnessPlane1.d - m1to0.p.dot(newN1)); + + aabb0 = hullBounds0; + aabb1 = hullBounds1; + + //gotta inflate // PT: perfect LHS recipe here.... + const PxVec3 inflate(contactDistance); + aabb0.minimum -= inflate; + aabb1.minimum -= inflate; + aabb0.maximum += inflate; + aabb1.maximum += inflate; +} + +static bool PxcBruteForceOverlapBackface( const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, const PxVec3& delta, + PxU32& id0, PxU32& id1, + PxReal& depth, PxVec3& sep, PxcSepAxisType& code, PxReal contactDistance, float toleranceLength) +{ + const PxVec3 localDelta0 = world0.rotateTranspose(delta); + PxU32* PX_RESTRICT indices0 = reinterpret_cast(PxAlloca(polyData0.mNbPolygons*sizeof(PxU32))); + + PxU32 numIndices0; + PxReal dmin0 = PX_MAX_REAL; + PxVec3 vec0; + if(!PxcTestFacesSepAxesBackface(polyData0, polyData1, world0, world1, scaling0, scaling1, m1to0, localDelta0, dmin0, vec0, id0, indices0, numIndices0, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , -delta +#endif + )) + return false; + + const PxVec3 localDelta1 = world1.rotateTranspose(delta); + + PxU32* PX_RESTRICT indices1 = reinterpret_cast(PxAlloca(polyData1.mNbPolygons*sizeof(PxU32))); + + PxU32 numIndices1; + PxReal dmin1 = PX_MAX_REAL; + PxVec3 vec1; + if(!PxcTestFacesSepAxesBackface(polyData1, polyData0, world1, world0, scaling1, scaling0, m0to1, -localDelta1, dmin1, vec1, id1, indices1, numIndices1, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , delta +#endif + )) + return false; + + PxReal dmin = dmin0; + PxVec3 vec = vec0; + code = SA_NORMAL0; + + if(dmin1 < dmin) + { + dmin = dmin1; + vec = vec1; + code = SA_NORMAL1; + } + + PX_ASSERT(id0!=PX_INVALID_U32); + PX_ASSERT(id1!=PX_INVALID_U32); + + // Brute-force find a separating axis + SeparatingAxes mSA0; + SeparatingAxes mSA1; + mSA0.reset(); + mSA1.reset(); + PxPlane witnessPlane0; + PxPlane witnessPlane1; + PxBounds3 aabb0, aabb1; + + prepareData(witnessPlane0, witnessPlane1, + aabb0, aabb1, + hullBounds0, hullBounds1, + polyData0.mPolygons[id0].mPlane, + polyData1.mPolygons[id1].mPlane, + scaling0, scaling1, + m0to1, m1to0, + contactDistance); + + // Find possibly separating axes + PxcFindSeparatingAxes(mSA0, indices0, numIndices0, polyData0, world0, witnessPlane1, m0to1, aabb1, contactDistance, scaling0); + PxcFindSeparatingAxes(mSA1, indices1, numIndices1, polyData1, world1, witnessPlane0, m1to0, aabb0, contactDistance, scaling1); +// PxcFindSeparatingAxes(context.mSA0, &id0, 1, hull0, world0, witnessPlane1, m0to1, aabbMin1, aabbMax1); +// PxcFindSeparatingAxes(context.mSA1, &id1, 1, hull1, world1, witnessPlane0, m1to0, aabbMin0, aabbMax0); + + const PxU32 numEdges0 = mSA0.getNumAxes(); + const PxVec3* PX_RESTRICT edges0 = mSA0.getAxes(); + + const PxU32 numEdges1 = mSA1.getNumAxes(); + const PxVec3* PX_RESTRICT edges1 = mSA1.getAxes(); + + // Worst case = convex test 02 with big meshes: 23 & 23 edges => 23*23 = 529 tests! + // printf("%d - %d\n", NbEdges0, NbEdges1); // maximum = ~20 in test scenes. + + for(PxU32 i=0; i < numEdges0; i++) + { + const PxVec3& edge0 = edges0[i]; + for(PxU32 j=0; j < numEdges1; j++) + { + const PxVec3& edge1 = edges1[j]; + + PxVec3 sepAxis = edge0.cross(edge1); + if(!Ps::isAlmostZero(sepAxis)) + { + sepAxis = sepAxis.getNormalized(); + +#ifdef TEST_INTERNAL_OBJECTS + if(!testInternalObjects(-delta, sepAxis, polyData0, polyData1, world0, world1, dmin)) + { + #if PX_DEBUG + PxReal d; + if(testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, sepAxis, d, contactDistance)) + { + PX_ASSERT(d + testInternalObjectsEpsilon*toleranceLength >= dmin); + } + #endif + continue; + } +#endif + PxReal d; + if(!testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, sepAxis, d, contactDistance)) + return false; + + if(d= dmin); + } + #endif + continue; + } +#endif + + PxReal d; + const float invMagnitude = 1.0f / magnitude; + if(!testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, /*hull1,*/ polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s. + return false; + + if(d < dmin) + { +#ifdef TEST_INTERNAL_OBJECTS + sep = worldNormal; +#else + sep = world0.rotate(shapeSpaceNormal); +#endif + dmin = d; + id = i; + } + } + + PX_ASSERT(id!=PX_INVALID_U32); //Should never happen with this version + + return true; +} + +static bool GuBruteForceOverlapBackfaceRoughPass( const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, const PxVec3& delta, + PxU32& id0, PxU32& id1, + PxReal& depth, PxVec3& sep, PxcSepAxisType& code, PxReal contactDistance, PxReal toleranceLength) +{ + PxReal dmin0 = PX_MAX_REAL; + PxReal dmin1 = PX_MAX_REAL; + PxVec3 vec0, vec1; + + const PxVec3 localDelta0 = world0.rotateTranspose(delta); + const PxVec3 localCenter1in0 = m1to0.transform(polyData1.mCenter); + if(!GuTestFacesSepAxesBackfaceRoughPass(polyData0, polyData1, world0, world1, scaling0, scaling1, m1to0, localCenter1in0, localDelta0, dmin0, vec0, id0, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , -delta +#endif + )) + return false; + + const PxVec3 localDelta1 = world1.rotateTranspose(delta); + const PxVec3 localCenter0in1 = m0to1.transform(polyData0.mCenter); + if(!GuTestFacesSepAxesBackfaceRoughPass(polyData1, polyData0, world1, world0, scaling1, scaling0, m0to1, localCenter0in1, -localDelta1, dmin1, vec1, id1, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , delta +#endif + )) + return false; + + PxReal dmin = dmin0; + PxVec3 vec = vec0; + code = SA_NORMAL0; + + if(dmin1 < dmin) + { + dmin = dmin1; + vec = vec1; + code = SA_NORMAL1; + } + + PX_ASSERT(id0!=PX_INVALID_U32); + PX_ASSERT(id1!=PX_INVALID_U32); + + depth = dmin; + sep = vec; + + return true; +} + +static bool GuContactHullHull( const PolygonalData& polyData0, const PolygonalData& polyData1, + const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PxTransform& transform0, const PxTransform& transform1, + const NarrowPhaseParams& params, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + bool idtScale0, bool idtScale1); + +namespace physx +{ + +namespace Gu +{ +// Box-convex contact generation +// this can no longer share code with convex-convex because that case needs scaling for both shapes, while this only needs it for one, which may be significant perf wise. +// +// PT: duplicating the full convex-vs-convex codepath is a lot worse. Look at it this way: if scaling is really "significant perf wise" then we made the whole convex-convex +// codepath significantly slower, and this is a lot more important than just box-vs-convex. The proper approach is to share the code and make sure scaling is NOT a perf hit. +// PT: please leave this function in the same translation unit as PxcContactHullHull. + +bool contactBoxConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxBoxGeometry& shapeBox = shape0.get(); + + Cm::FastVertex2ShapeScaling idtScaling; + + const PxBounds3 boxBounds(-shapeBox.halfExtents, shapeBox.halfExtents); + + PolygonalData polyData0; + PolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData0); + + /////// + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 convexBounds; + PolygonalData polyData1; + const bool idtScale = getConvexData(shape1, convexScaling, convexBounds, polyData1); + + return GuContactHullHull( polyData0, polyData1, boxBounds, convexBounds, + transform0, transform1, params, contactBuffer, + idtScaling, convexScaling, true, idtScale); +} + +// PT: please leave this function in the same translation unit as PxcContactHullHull. +bool contactConvexConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + Cm::FastVertex2ShapeScaling scaling0, scaling1; + PxBounds3 convexBounds0, convexBounds1; + PolygonalData polyData0, polyData1; + const bool idtScale0 = getConvexData(shape0, scaling0, convexBounds0, polyData0); + const bool idtScale1 = getConvexData(shape1, scaling1, convexBounds1, polyData1); + + return GuContactHullHull( polyData0, polyData1, convexBounds0, convexBounds1, + transform0, transform1, params, contactBuffer, + scaling0, scaling1, idtScale0, idtScale1); +} +}//Gu +}//physx + +static bool GuContactHullHull( const PolygonalData& polyData0, const PolygonalData& polyData1, + const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PxTransform& transform0, const PxTransform& transform1, + const NarrowPhaseParams& params, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + bool idtScale0, bool idtScale1) +{ + // Compute matrices + const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + + const PxVec3 worldCenter0 = world0.transform(polyData0.mCenter); + const PxVec3 worldCenter1 = world1.transform(polyData1.mCenter); + + const PxVec3 deltaC = worldCenter1 - worldCenter0; + + PxReal depth; + + /////////////////////////////////////////////////////////////////////////// + + // Early-exit test: quickly discard obviously non-colliding cases. + // PT: we used to skip this when objects were touching, which provided a small speedup (saving 2 full hull projections). + // We may want to add this back at some point in the future, if possible. + if(true) //AM: now we definitely want to test the cached axis to get the depth value for the cached axis, even if we had a contact before. + { + if(!testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, deltaC, depth, params.mContactDistance)) + //there was no contact previously and we reject using the same prev. axis. + return false; + } + + /////////////////////////////////////////////////////////////////////////// + + // Compute relative transforms + PxTransform t0to1 = transform1.transformInv(transform0); + PxTransform t1to0 = transform0.transformInv(transform1); + + PxU32 c0(0x7FFF); + PxU32 c1(0x7FFF); + PxVec3 worldNormal; + + const Cm::Matrix34 m0to1(t0to1); + const Cm::Matrix34 m1to0(t1to0); + +#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS + // PT: it is a bad idea to skip the rough pass, for two reasons: + // 1) performance. The rough pass is obviously a lot faster. + // 2) stability. The "skipIt" optimization relies on the rough pass being present to catch the cases where it fails. If you disable the rough pass + // "skipIt" can skip the whole work, contacts get lost, and we never "try again" ==> explosions +// bool TryRoughPass = (contactDistance == 0.0f); //Rough first pass doesn't work with dist based for some reason. + for(int TryRoughPass = 1 /*gEnableOptims*/; 0 <= TryRoughPass; --TryRoughPass) + { +#endif + + { + PxcSepAxisType code; + PxU32 id0, id1; + //PxReal depth; +#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS + bool Status; + if(TryRoughPass) + { + Status = GuBruteForceOverlapBackfaceRoughPass(polyData0, polyData1, world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, params.mContactDistance, params.mToleranceLength); + } + else + { + Status = PxcBruteForceOverlapBackface( +// hull0, hull1, + hullBounds0, hullBounds1, + polyData0, polyData1, world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, params.mContactDistance, params.mToleranceLength); + } + + if(!Status) +#else + if(!PxcBruteForceOverlapBackface( +// hull0, hull1, + hullBounds0, hullBounds1, + polyData0, polyData1, + world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, contactDistance)) +#endif + return false; + + if(deltaC.dot(worldNormal) < 0.0f) + worldNormal = -worldNormal; + +/* +worldNormal = -partialSep; +depth = -partialDepth; +code = SA_EE; +*/ + + if(code==SA_NORMAL0) + { + c0 = id0; + c1 = (polyData1.mSelectClosestEdgeCB)(polyData1, scaling1, world1.rotateTranspose(-worldNormal)); + } + else if(code==SA_NORMAL1) + { + c0 = (polyData0.mSelectClosestEdgeCB)(polyData0, scaling0, world0.rotateTranspose(worldNormal)); + c1 = id1; + } + else if(code==SA_EE) + { + c0 = (polyData0.mSelectClosestEdgeCB)(polyData0, scaling0, world0.rotateTranspose(worldNormal)); + c1 = (polyData1.mSelectClosestEdgeCB)(polyData1, scaling1, world1.rotateTranspose(-worldNormal)); + } + } + + const Gu::HullPolygonData& HP0 = polyData0.mPolygons[c0]; + const Gu::HullPolygonData& HP1 = polyData1.mPolygons[c1]; + // PT: prefetching those guys saves ~600.000 cycles in convex-convex benchmark + Ps::prefetchLine(&HP0.mPlane); + Ps::prefetchLine(&HP1.mPlane); + + //ok, we have a new depth value. convert to real distance. +// PxReal separation = -depth; //depth was either computed in initial cached-axis check, or better, when skipIt was false, in sep axis search. +// if (separation < 0.0f) +// separation = 0.0f; //we don't want to store penetration values. + const PxReal separation = fsel(depth, 0.0f, -depth); + + PxVec3 worldNormal0; + PX_ALIGN(16, PxPlane) shapeSpacePlane0; + if(idtScale0) + { + V4StoreA(V4LoadU(&HP0.mPlane.n.x), &shapeSpacePlane0.n.x); + worldNormal0 = world0.rotate(HP0.mPlane.n); + } + else + { + scaling0.transformPlaneToShapeSpace(HP0.mPlane.n,HP0.mPlane.d,shapeSpacePlane0.n,shapeSpacePlane0.d); + worldNormal0 = world0.rotate(shapeSpacePlane0.n); + } + + PxVec3 worldNormal1; + PX_ALIGN(16, PxPlane) shapeSpacePlane1; + if(idtScale1) + { + V4StoreA(V4LoadU(&HP1.mPlane.n.x), &shapeSpacePlane1.n.x); + worldNormal1 = world1.rotate(HP1.mPlane.n); + } + else + { + scaling1.transformPlaneToShapeSpace(HP1.mPlane.n,HP1.mPlane.d,shapeSpacePlane1.n,shapeSpacePlane1.d); + worldNormal1 = world1.rotate(shapeSpacePlane1.n); + } + + Ps::IntBool flag; + { + const PxReal d0 = PxAbs(worldNormal0.dot(worldNormal)); + const PxReal d1 = PxAbs(worldNormal1.dot(worldNormal)); + bool f = d0>d1; //which face normal is the separating axis closest to. + flag = f; + } + +////////////////////NEW DIST HANDLING////////////////////// + PX_ASSERT(separation >= 0.0f); //be sure this got fetched somewhere in the chaos above. + + const PxReal cCCDEpsilon = params.mMeshContactMargin; + const PxReal contactGenPositionShift = separation + cCCDEpsilon; //if we're at a distance, shift so we're in penetration. + + const PxVec3 contactGenPositionShiftVec = worldNormal * -contactGenPositionShift; //shift one of the bodies this distance toward the other just for Pierre's contact generation. Then the bodies should be penetrating exactly by MIN_SEPARATION_FOR_PENALTY - ideal conditions for this contact generator. + + //note: for some reason this has to change sign! + + //this will make contact gen always generate contacts at about MSP. Shift them back to the true real distance, and then to a solver compliant distance given that + //the solver converges to MSP penetration, while we want it to converge to 0 penetration. + //to real distance: + + //The system: We always shift convex 0 (arbitrary). If the contact is attached to convex 0 then we will need to shift the contact point, otherwise not. + const PxVec3 newp = world0.p - contactGenPositionShiftVec; + const Cm::Matrix34 world0_Tweaked(world0.m.column0, world0.m.column1, world0.m.column2, newp); + PxTransform shifted0(newp, transform0.q); + + t0to1 = transform1.transformInv(shifted0); + t1to0 = shifted0.transformInv(transform1); + Cm::Matrix34 m0to1_Tweaked; + Cm::Matrix34 m1to0_Tweaked; + m0to1_Tweaked.set(t0to1.q); m0to1_Tweaked.p = t0to1.p; + m1to0_Tweaked.set(t1to0.q); m1to0_Tweaked.p = t1to0.p; + +////////////////////////////////////////////////// + //pretransform convex polygon if we have scaling! + PxVec3* scaledVertices0; + PxU8* stackIndices0; + GET_SCALEX_CONVEX(scaledVertices0, stackIndices0, idtScale0, HP0.mNbVerts, scaling0, polyData0.mVerts, polyData0.getPolygonVertexRefs(HP0)) + + //pretransform convex polygon if we have scaling! + PxVec3* scaledVertices1; + PxU8* stackIndices1; + GET_SCALEX_CONVEX(scaledVertices1, stackIndices1, idtScale1, HP1.mNbVerts, scaling1, polyData1.mVerts, polyData1.getPolygonVertexRefs(HP1)) + + // So we need to switch: + // - HP0, HP1 + // - scaledVertices0, scaledVertices1 + // - stackIndices0, stackIndices1 + // - world0, world1 + // - shapeSpacePlane0, shapeSpacePlane1 + // - worldNormal0, worldNormal1 + // - m0to1, m1to0 + // - true, false + const PxMat33 RotT0 = findRotationMatrixFromZ(shapeSpacePlane0.n); + const PxMat33 RotT1 = findRotationMatrixFromZ(shapeSpacePlane1.n); + + if(flag) + { + if(contactPolygonPolygonExt(HP0.mNbVerts, scaledVertices0, stackIndices0, world0_Tweaked, shapeSpacePlane0, RotT0, + HP1.mNbVerts, scaledVertices1, stackIndices1, world1, shapeSpacePlane1, RotT1, + worldNormal0, m0to1_Tweaked, m1to0_Tweaked, + PXC_CONTACT_NO_FACE_INDEX, PXC_CONTACT_NO_FACE_INDEX, + contactBuffer, + true, contactGenPositionShiftVec, contactGenPositionShift)) + return true; + } + else + { + if(contactPolygonPolygonExt(HP1.mNbVerts, scaledVertices1, stackIndices1, world1, shapeSpacePlane1, RotT1, + HP0.mNbVerts, scaledVertices0, stackIndices0, world0_Tweaked, shapeSpacePlane0, RotT0, + worldNormal1, m1to0_Tweaked, m0to1_Tweaked, + PXC_CONTACT_NO_FACE_INDEX, PXC_CONTACT_NO_FACE_INDEX, + contactBuffer, + false, contactGenPositionShiftVec, contactGenPositionShift)) + return true; + } + +#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS + } +#endif + return false; +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp new file mode 100644 index 000000000..2f70453f8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp @@ -0,0 +1,1447 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexUtilsInternal.h" +#include "GuInternal.h" +#include "GuContactPolygonPolygon.h" +#include "GuConvexEdgeFlags.h" +#include "GuSeparatingAxes.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuMidphaseInterface.h" +#include "GuConvexHelper.h" +#include "GuTriangleCache.h" +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "GuGeometryUnion.h" +#include "GuIntersectionTriangleBox.h" +#include "CmUtils.h" +#include "PsAllocator.h" +#include "GuBox.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; +using namespace shdfnd::aos; +using namespace intrinsics; + +#define LOCAL_TOUCHED_TRIG_SIZE 192 + +//#define USE_TRIANGLE_NORMAL +#define TEST_INTERNAL_OBJECTS + +static PX_FORCE_INLINE void projectTriangle(const PxVec3& localSpaceDirection, const PxVec3* PX_RESTRICT triangle, PxReal& min1, PxReal& max1) +{ + const PxReal dp0 = triangle[0].dot(localSpaceDirection); + const PxReal dp1 = triangle[1].dot(localSpaceDirection); + min1 = selectMin(dp0, dp1); + max1 = selectMax(dp0, dp1); + + const PxReal dp2 = triangle[2].dot(localSpaceDirection); + min1 = selectMin(min1, dp2); + max1 = selectMax(max1, dp2); +} + +#ifdef TEST_INTERNAL_OBJECTS + +static PX_FORCE_INLINE void boxSupport(const float extents[3], const PxVec3& sv, float p[3]) +{ + const PxU32* iextents = reinterpret_cast(extents); + const PxU32* isv = reinterpret_cast(&sv); + PxU32* ip = reinterpret_cast(p); + + ip[0] = iextents[0]|(isv[0]&PX_SIGN_BITMASK); + ip[1] = iextents[1]|(isv[1]&PX_SIGN_BITMASK); + ip[2] = iextents[2]|(isv[2]&PX_SIGN_BITMASK); +} + +#if PX_DEBUG +static const PxReal testInternalObjectsEpsilon = 1.0e-3f; +#endif + +static PX_FORCE_INLINE bool testInternalObjects(const PxVec3& localAxis0, + const PolygonalData& polyData0, + const PxVec3* PX_RESTRICT triangleInHullSpace, + float dmin) +{ + PxReal min1, max1; + projectTriangle(localAxis0, triangleInHullSpace, min1, max1); + + const float dp = polyData0.mCenter.dot(localAxis0); + + float p0[3]; + boxSupport(polyData0.mInternal.mExtents, localAxis0, p0); + const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z; + const float bestRadius = selectMax(Radius0, polyData0.mInternal.mRadius); + const PxReal min0 = dp - bestRadius; + const PxReal max0 = dp + bestRadius; + + const PxReal d0 = max0 - min1; + const PxReal d1 = max1 - min0; + + const float depth = selectMin(d0, d1); + if(depth>dmin) + return false; + return true; +} +#endif + +static PX_FORCE_INLINE bool testNormal( const PxVec3& sepAxis, PxReal min0, PxReal max0, + const PxVec3* PX_RESTRICT triangle, + PxReal& depth, PxReal contactDistance) +{ + PxReal min1, max1; + projectTriangle(sepAxis, triangle, min1, max1); + + if(max0+contactDistance 0.0f) +#else + // ### this is dubious since the triangle center is likely to be very close to the hull, if not inside. Why not use the triangle normal? + if(PL.distance(witness) < 0.0f) +#endif + continue; //backface culled + + *hullIndices++ = i; + + const PxVec3 sepAxis = m0to1.rotate(PL.n); + const PxReal dp = sepAxis.dot(trans); + + PxReal d; + if(!testNormal(sepAxis, P.getMin(vertices) + dp, P.getMax() + dp, triangle, + d, contactDistance)) + return false; + + if(d < dmin) + { + dmin = d; + sep = sepAxis; + id = i; + } + } + } + else + { +#ifndef USE_TRIANGLE_NORMAL + //transform delta from hull0 shape into vertex space: + const PxVec3 vertSpaceWitness = convexScaling % witness; +#endif + for(PxU32 i=0; i 0.0f) +#else + // ### this is dubious since the triangle center is likely to be very close to the hull, if not inside. Why not use the triangle normal? + if(PL.distance(vertSpaceWitness) < 0.0f) +#endif + continue; //backface culled + + //normals transform by inverse transpose: (and transpose(skew) == skew as its symmetric) + PxVec3 shapeSpaceNormal = convexScaling % PL.n; + //renormalize: (Arr!) + const PxReal magnitude = shapeSpaceNormal.normalize(); + + *hullIndices++ = i; + const PxVec3 sepAxis = m0to1.rotate(shapeSpaceNormal); + PxReal d; + const PxReal dp = sepAxis.dot(trans); + + const float oneOverM = 1.0f / magnitude; + if(!testNormal(sepAxis, P.getMin(vertices) * oneOverM + dp, P.getMax() * oneOverM + dp, triangle, + d, contactDistance)) + return false; + + if(d < dmin) + { + dmin = d; + sep = sepAxis; + id = i; + } + } + } + numHullIndices = PxU32(hullIndices - hullIndices_); + } + + // Backup + if(id == PX_INVALID_U32) + { + if(idtConvexScale) + { + for(PxU32 i=0; i=numVerts) j1 = 0; + + const PxU32 VRef0 = data[j]; + const PxU32 VRef1 = data[j1]; + + if(edgeCulling(vertexSpacePlane, hullVerts[VRef0], hullVerts[VRef1], contactDistance)) + { + const PxVec3 currentHullEdge = m0to1.rotate(convexScaling * (hullVerts[VRef0] - hullVerts[VRef1])); //matrix mult is distributive! + + PxVec3 sepAxis = currentHullEdge.cross(currentPolyEdge); + if(!Ps::isAlmostZero(sepAxis)) + SA.addAxis(sepAxis.getNormalized()); + } + } + } + } + + dmin = PX_MAX_REAL; + PxU32 numAxes = SA.getNumAxes(); + const PxVec3* PX_RESTRICT axes = SA.getAxes(); + +#ifdef TEST_INTERNAL_OBJECTS + PxVec3 triangleInHullSpace[3]; + if(numAxes) + { + triangleInHullSpace[0] = m1to0.transform(triangle[0]); + triangleInHullSpace[1] = m1to0.transform(triangle[1]); + triangleInHullSpace[2] = m1to0.transform(triangle[2]); + } +#endif + + while(numAxes--) + { + const PxVec3& currentAxis = *axes++; + +#ifdef TEST_INTERNAL_OBJECTS + const PxVec3 localAxis0 = m1to0.rotate(currentAxis); + if(!testInternalObjects(localAxis0, polyData0, triangleInHullSpace, dmin)) + { + #if PX_DEBUG + PxReal dtest; + if(testSepAxis(currentAxis, polyData0, triangle, m0to1, convexScaling, dtest, contactDistance)) + { + PX_ASSERT(dtest + testInternalObjectsEpsilon*toleranceLength >= dmin); + } + #endif + continue; + } +#endif + + PxReal d; + if(!testSepAxis(currentAxis, polyData0, triangle, + m0to1, convexScaling, d, contactDistance)) + { + return false; + } + + if(d < dmin) + { + dmin = d; + vec = currentAxis; + } + } + return true; +} + +static bool triangleConvexTest( const PolygonalData& polyData0, + const PxU8 triFlags, + PxU32 index, const PxVec3* PX_RESTRICT localPoints, + const PxPlane& localPlane, + const PxVec3& groupCenterHull, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, PxReal toleranceLength, + PxVec3& groupAxis, PxReal& groupMinDepth, bool& faceContact, + bool idtConvexScale + ) +{ + PxU32 id0 = PX_INVALID_U32; + PxReal dmin0 = PX_MAX_REAL; + PxVec3 vec0; + + PxU32 numHullIndices = 0; + PxU32* PX_RESTRICT const hullIndices = reinterpret_cast(PxAlloca(polyData0.mNbPolygons*sizeof(PxU32))); + + // PT: we test the hull normals first because they don't need any hull projection. If we can early exit thanks + // to those, we completely avoid all hull projections. + bool status = testFacesSepAxesBackface(polyData0, world0, world1, m0to1, groupCenterHull, localPoints, + convexScaling, numHullIndices, hullIndices, dmin0, vec0, id0, contactDistance, idtConvexScale); + if(!status) + return false; + + groupAxis = PxVec3(0); + groupMinDepth = PX_MAX_REAL; + + const PxReal eps = 0.0001f; // DE7748 + + //Test in mesh-space + PxVec3 sepAxis; + PxReal depth; + + { + // Test triangle normal + PxReal d; + if(!testSepAxis(localPlane.n, polyData0, localPoints, + m0to1, convexScaling, d, contactDistance)) + return false; + + if(d& mDelayedContacts; + Gu::CacheMap mEdgeCache; + Gu::CacheMap mVertCache; + + const Cm::Matrix34 m0to1; + const Cm::Matrix34 m1to0; + + PxVec3 mHullCenterMesh; + PxVec3 mHullCenterWorld; + + const PolygonalData& mPolyData0; + const Cm::Matrix34& mWorld0; + const Cm::Matrix34& mWorld1; + + const Cm::FastVertex2ShapeScaling& mConvexScaling; + + PxReal mContactDistance; + PxReal mToleranceLength; + bool mIdtMeshScale, mIdtConvexScale; + PxReal mCCDEpsilon; + const PxTransform& mTransform0; + const PxTransform& mTransform1; + ContactBuffer& mContactBuffer; + bool mAnyHits; + + ConvexMeshContactGeneration( + Ps::InlineArray& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer + ); + + void processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + void generateLastContacts(); + + bool generateContacts( + const PxPlane& localPlane, + const PxVec3* PX_RESTRICT localPoints, + const PxVec3& triCenter, PxVec3& groupAxis, + PxReal groupMinDepth, PxU32 index) const; + + private: + ConvexMeshContactGeneration& operator=(const ConvexMeshContactGeneration&); + }; + +// 17 entries. 1088/17 = 64 triangles in the local array +struct SavedContactData +{ + PxU32 mTriangleIndex; + PxVec3 mVerts[3]; + PxU32 mInds[3]; + PxVec3 mGroupAxis; + PxReal mGroupMinDepth; +}; +} + +ConvexMeshContactGeneration::ConvexMeshContactGeneration( + Ps::InlineArray& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer +) : + mDelayedContacts(delayedContacts), + m0to1 (t0to1), + m1to0 (t1to0), + mPolyData0 (polyData0), + mWorld0 (world0), + mWorld1 (world1), + mConvexScaling (convexScaling), + mContactDistance(contactDistance), + mToleranceLength(toleranceLength), + mIdtConvexScale (idtConvexScale), + mCCDEpsilon (cCCDEpsilon), + mTransform0 (transform0), + mTransform1 (transform1), + mContactBuffer (contactBuffer) +{ + delayedContacts.forceSize_Unsafe(0); + mAnyHits = false; + + // Hull center in local space + const PxVec3& hullCenterLocal = mPolyData0.mCenter; + // Hull center in mesh space + mHullCenterMesh = m0to1.transform(hullCenterLocal); + // Hull center in world space + mHullCenterWorld = mWorld0.transform(hullCenterLocal); +} + +struct ConvexMeshContactGenerationCallback : MeshHitCallback +{ + ConvexMeshContactGeneration mGeneration; + const Cm::FastVertex2ShapeScaling& mMeshScaling; + const PxU8* PX_RESTRICT mExtraTrigData; + bool mIdtMeshScale; + const TriangleMesh* mMeshData; + const BoxPadded& mBox; + + ConvexMeshContactGenerationCallback( + Ps::InlineArray& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const TriangleMesh* meshData, + const PxU8* PX_RESTRICT extraTrigData, + const Cm::FastVertex2ShapeScaling& meshScaling, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtMeshScale, bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer, + const BoxPadded& box + ) : + MeshHitCallback (CallbackMode::eMULTIPLE), + mGeneration (delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, contactDistance, toleranceLength, idtConvexScale, cCCDEpsilon, transform0, transform1, contactBuffer), + mMeshScaling (meshScaling), + mExtraTrigData (extraTrigData), + mIdtMeshScale (idtMeshScale), + mMeshData (meshData), + mBox (box) + { + } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + // PT: this one is safe because incoming vertices from midphase are always safe to V4Load (by design) + // PT: TODO: is this test really needed? Not done in midphase already? + if(!intersectTriangleBox(mBox, v0, v1, v2)) + return true; + + PxVec3 verts[3]; + getScaledVertices(verts, v0, v1, v2, mIdtMeshScale, mMeshScaling); + + const PxU32 triangleIndex = hit.faceIndex; + + const PxU8 extraData = getConvexEdgeFlags(mExtraTrigData, triangleIndex); + mGeneration.processTriangle(verts, triangleIndex, extraData, vinds); + return true; + } + +protected: + ConvexMeshContactGenerationCallback &operator=(const ConvexMeshContactGenerationCallback &); +}; + +bool ConvexMeshContactGeneration::generateContacts( + const PxPlane& localPlane, + const PxVec3* PX_RESTRICT localPoints, + const PxVec3& triCenter, PxVec3& groupAxis, + PxReal groupMinDepth, PxU32 index) const +{ + const PxVec3 worldGroupCenter = mWorld1.transform(triCenter); + const PxVec3 deltaC = mHullCenterWorld - worldGroupCenter; + if(deltaC.dot(groupAxis) < 0.0f) + groupAxis = -groupAxis; + + const PxU32 id = (mPolyData0.mSelectClosestEdgeCB)(mPolyData0, mConvexScaling, mWorld0.rotateTranspose(-groupAxis)); + + const HullPolygonData& HP = mPolyData0.mPolygons[id]; + PX_ALIGN(16, PxPlane) shapeSpacePlane0; + if(mIdtConvexScale) + V4StoreA(V4LoadU(&HP.mPlane.n.x), &shapeSpacePlane0.n.x); + else + mConvexScaling.transformPlaneToShapeSpace(HP.mPlane.n, HP.mPlane.d, shapeSpacePlane0.n, shapeSpacePlane0.d); + + const PxVec3 hullNormalWorld = mWorld0.rotate(shapeSpacePlane0.n); + + const PxReal d0 = PxAbs(hullNormalWorld.dot(groupAxis)); + + const PxVec3 triNormalWorld = mWorld1.rotate(localPlane.n); + const PxReal d1 = PxAbs(triNormalWorld.dot(groupAxis)); + const bool d0biggerd1 = d0 > d1; + +////////////////////NEW DIST HANDLING////////////////////// + //TODO: skip this if there is no dist involved! + +PxReal separation = - groupMinDepth; //convert to real distance. + +separation = fsel(separation, separation, 0.0f); //don't do anything when penetrating! + +//printf("\nseparation = %f", separation); + +PxReal contactGenPositionShift = separation + mCCDEpsilon; //if we're at a distance, shift so we're within penetration. + +PxVec3 contactGenPositionShiftVec = groupAxis * contactGenPositionShift; //shift one of the bodies this distance toward the other just for Pierre's contact generation. Then the bodies should be penetrating exactly by MIN_SEPARATION_FOR_PENALTY - ideal conditions for this contact generator. + +//note: for some reason this has to change sign! + +//this will make contact gen always generate contacts at about MSP. Shift them back to the true real distance, and then to a solver compliant distance given that +//the solver converges to MSP penetration, while we want it to converge to 0 penetration. +//to real distance: +// PxReal polyPolySeparationShift = separation; //(+ or - depending on which way normal goes) + +//The system: We always shift convex 0 (arbitrary). If the contact is attached to convex 0 then we will need to shift the contact point, otherwise not. + +//TODO: make these overwrite orig location if its safe to do so. + +Cm::Matrix34 world0_(mWorld0); +PxTransform transform0_(mTransform0); + +world0_.p -= contactGenPositionShiftVec; +transform0_.p = world0_.p; //reset this too. + +PxTransform t0to1_ = mTransform1.transformInv(transform0_); +PxTransform t1to0_ = transform0_.transformInv(mTransform1); +Cm::Matrix34 m0to1_(t0to1_); +Cm::Matrix34 m1to0_(t1to0_); + + PxVec3* scaledVertices0; + PxU8* stackIndices0; + GET_SCALEX_CONVEX(scaledVertices0, stackIndices0, mIdtConvexScale, HP.mNbVerts, mConvexScaling, mPolyData0.mVerts, mPolyData0.getPolygonVertexRefs(HP)) + + const PxU8 indices[3] = {0, 1, 2}; + + const PxMat33 RotT0 = findRotationMatrixFromZ(shapeSpacePlane0.n); + const PxMat33 RotT1 = findRotationMatrixFromZ(localPlane.n); + + if(d0biggerd1) + { + if(contactPolygonPolygonExt( + HP.mNbVerts, scaledVertices0, stackIndices0, world0_, shapeSpacePlane0, + RotT0, + 3, localPoints, indices, mWorld1, localPlane, + RotT1, + hullNormalWorld, m0to1_, m1to0_, PXC_CONTACT_NO_FACE_INDEX, index, + mContactBuffer, + true, + contactGenPositionShiftVec, contactGenPositionShift)) + { + return true; + } + } + else + { + if(contactPolygonPolygonExt( + 3, localPoints, indices, mWorld1, localPlane, + RotT1, + HP.mNbVerts, scaledVertices0, stackIndices0, world0_, shapeSpacePlane0, + RotT0, + triNormalWorld, m1to0_, m0to1_, PXC_CONTACT_NO_FACE_INDEX, index, + mContactBuffer, + false, + contactGenPositionShiftVec, contactGenPositionShift)) + { + return true; + } + } + return false; +} + +enum FeatureCode +{ + FC_VERTEX0, + FC_VERTEX1, + FC_VERTEX2, + FC_EDGE01, + FC_EDGE12, + FC_EDGE20, + FC_FACE, + + FC_UNDEFINED +}; + +static FeatureCode computeFeatureCode(const PxVec3& point, const PxVec3* verts) +{ + const PxVec3& triangleOrigin = verts[0]; + const PxVec3 triangleEdge0 = verts[1] - verts[0]; + const PxVec3 triangleEdge1 = verts[2] - verts[0]; + + const PxVec3 kDiff = triangleOrigin - point; + const PxReal fA00 = triangleEdge0.magnitudeSquared(); + const PxReal fA01 = triangleEdge0.dot(triangleEdge1); + const PxReal fA11 = triangleEdge1.magnitudeSquared(); + const PxReal fB0 = kDiff.dot(triangleEdge0); + const PxReal fB1 = kDiff.dot(triangleEdge1); + const PxReal fDet = PxAbs(fA00*fA11 - fA01*fA01); + const PxReal u = fA01*fB1-fA11*fB0; + const PxReal v = fA01*fB0-fA00*fB1; + + FeatureCode fc = FC_UNDEFINED; + + if(u + v <= fDet) + { + if(u < 0.0f) + { + if(v < 0.0f) // region 4 + { + if(fB0 < 0.0f) + { + if(-fB0 >= fA00) + fc = FC_VERTEX1; + else + fc = FC_EDGE01; + } + else + { + if(fB1 >= 0.0f) + fc = FC_VERTEX0; + else if(-fB1 >= fA11) + fc = FC_VERTEX2; + else + fc = FC_EDGE20; + } + } + else // region 3 + { + if(fB1 >= 0.0f) + fc = FC_VERTEX0; + else if(-fB1 >= fA11) + fc = FC_VERTEX2; + else + fc = FC_EDGE20; + } + } + else if(v < 0.0f) // region 5 + { + if(fB0 >= 0.0f) + fc = FC_VERTEX0; + else if(-fB0 >= fA00) + fc = FC_VERTEX1; + else + fc = FC_EDGE01; + } + else // region 0 + { + // minimum at interior PxVec3 + if(fDet==0.0f) + fc = FC_VERTEX0; + else + fc = FC_FACE; + } + } + else + { + PxReal fTmp0, fTmp1, fNumer, fDenom; + + if(u < 0.0f) // region 2 + { + fTmp0 = fA01 + fB0; + fTmp1 = fA11 + fB1; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + fc = FC_VERTEX1; + else + fc = FC_EDGE12; + } + else + { + if(fTmp1 <= 0.0f) + fc = FC_VERTEX2; + else if(fB1 >= 0.0f) + fc = FC_VERTEX0; + else + fc = FC_EDGE20; + } + } + else if(v < 0.0f) // region 6 + { + fTmp0 = fA01 + fB1; + fTmp1 = fA00 + fB0; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + fc = FC_VERTEX2; + else + fc = FC_EDGE12; + } + else + { + if(fTmp1 <= 0.0f) + fc = FC_VERTEX1; + else if(fB0 >= 0.0f) + fc = FC_VERTEX0; + else + fc = FC_EDGE01; + } + } + else // region 1 + { + fNumer = fA11 + fB1 - fA01 - fB0; + if(fNumer <= 0.0f) + { + fc = FC_VERTEX2; + } + else + { + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + fc = FC_VERTEX1; + else + fc = FC_EDGE12; + } + } + } + return fc; +} + + +//static bool validateVertex(PxU32 vref, const PxU32 count, const ContactPoint* PX_RESTRICT contacts, const TriangleMesh& meshData) +//{ +// PxU32 previous = 0xffffffff; +// for(PxU32 i=0;itvref1) +// Ps::swap(tvref0, tvref1); +// +// if(tvref0==vref0 && tvref1==vref1) +// return false; +// return true; +//} + +//static bool validateEdge(PxU32 vref0, PxU32 vref1, const PxU32 count, const ContactPoint* PX_RESTRICT contacts, const TriangleMesh& meshData) +//{ +// if(vref0>vref1) +// Ps::swap(vref0, vref1); +// +// PxU32 previous = 0xffffffff; +// for(PxU32 i=0;ivref1) +// Ps::swap(vref0, vref1); +// +// for(PxU32 i=0;i(mDelayedContacts.end()); + mDelayedContacts.forceSize_Unsafe(newSize); + + cd->mTriangleIndex = triangleIndex; + cd->mVerts[0] = verts[0]; + cd->mVerts[1] = verts[1]; + cd->mVerts[2] = verts[2]; + cd->mInds[0] = vertInds[0]; + cd->mInds[1] = vertInds[1]; + cd->mInds[2] = vertInds[2]; + cd->mGroupAxis = groupAxis; + cd->mGroupMinDepth = groupMinDepth; + } +} + +void ConvexMeshContactGeneration::generateLastContacts() +{ + // Process delayed contacts + PxU32 nbEntries = mDelayedContacts.size(); + if(nbEntries) + { + nbEntries /= sizeof(SavedContactData)/sizeof(PxU32); + + // PT: TODO: replicate this fix in sphere-vs-mesh ###FIX + //const PxU32 count = mContactBuffer.count; + //const ContactPoint* PX_RESTRICT contacts = mContactBuffer.contacts; + + const SavedContactData* PX_RESTRICT cd = reinterpret_cast(mDelayedContacts.begin()); + for(PxU32 i=0;i delayedContacts; + + ConvexMeshContactGenerationCallback blockCallback( + delayedContacts, + t0to1, t1to0, polyData0, world0, world1, meshData, meshData->getExtraTrigData(), meshScaling, + convexScaling, params.mContactDistance, params.mToleranceLength, + idtMeshScale, idtConvexScale, params.mMeshContactMargin, + transform0, transform1, + contactBuffer, hullOBB + ); + + Midphase::intersectOBB(meshData, hullOBB, blockCallback, false); + + blockCallback.mGeneration.generateLastContacts(); + + return blockCallback.mGeneration.mAnyHits; +} + +///////////// + +bool Gu::contactConvexMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData0; + const bool idtScaleConvex = getConvexData(shape0, convexScaling, hullAABB, polyData0); + + return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, convexScaling, meshScaling, idtScaleConvex, idtScaleMesh); +} + +bool Gu::contactBoxMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxBoxGeometry& shapeBox = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + PolygonalData polyData0; + PolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData0); + + const PxBounds3 hullAABB(-shapeBox.halfExtents, shapeBox.halfExtents); + + const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling idtScaling; + return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, meshScaling, true, idtScaleMesh); +} + +///////////// + +namespace +{ +struct ConvexVsHeightfieldContactGenerationCallback : EntityReport +{ + ConvexMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + + ConvexVsHeightfieldContactGenerationCallback( + HeightFieldUtil& hfUtil, + Ps::InlineArray& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer + ) : mGeneration(delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, contactDistance, toleranceLength, idtConvexScale, cCCDEpsilon, transform0, + transform1, contactBuffer), + mHfUtil(hfUtil) + { + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + const PxU8 nextInd[] = {2,0,1}; + + while(nb--) + { + const PxU32 triangleIndex = *indices++; + + PxU32 vertIndices[3]; + PxTriangle currentTriangle; // in world space + PxU32 adjInds[3]; + mHfUtil.getTriangle(mGeneration.mTransform1, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); + + PxVec3 normal; + currentTriangle.normal(normal); + + PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF + + for(PxU32 a = 0; a < 3; ++a) + { + if(adjInds[a] != 0xFFFFFFFF) + { + PxTriangle adjTri; + mHfUtil.getTriangle(mGeneration.mTransform1, adjTri, NULL, NULL, adjInds[a], false, false); + //We now compare the triangles to see if this edge is active + + PxVec3 adjNormal; + adjTri.denormalizedNormal(adjNormal); + PxU32 otherIndex = nextInd[a]; + PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); + if(projD < 0.f) + { + adjNormal.normalize(); + + PxF32 proj = adjNormal.dot(normal); + + if(proj < 0.999f) + { + triFlags |= 1 << (a+3); + } + } + } + else + triFlags |= (1 << (a+3)); + } + mGeneration.processTriangle(currentTriangle.verts, triangleIndex, triFlags, vertIndices); + } + return true; + } + +protected: + ConvexVsHeightfieldContactGenerationCallback &operator=(const ConvexVsHeightfieldContactGenerationCallback &); +}; +} + +static bool contactHullHeightfield2(const PolygonalData& polyData0, const PxBounds3& hullAABB, const PxHeightFieldGeometry& shape1, + const PxTransform& transform0, const PxTransform& transform1, + const NarrowPhaseParams& params, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale) +{ + //We need to create a callback that fills triangles from the HF + + HeightFieldUtil hfUtil(shape1); + + const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + + //////////////////// + + // Compute relative transforms + const PxTransform t0to1 = transform1.transformInv(transform0); + const PxTransform t1to0 = transform0.transformInv(transform1); + + Ps::InlineArray delayedContacts; + + ConvexVsHeightfieldContactGenerationCallback blockCallback(hfUtil, delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, params.mContactDistance, params.mToleranceLength, + idtConvexScale, params.mMeshContactMargin, transform0, transform1, contactBuffer); + + hfUtil.overlapAABBTriangles(transform1, PxBounds3::transformFast(t0to1, hullAABB), 0, &blockCallback); + + blockCallback.mGeneration.generateLastContacts(); + + return blockCallback.mGeneration.mAnyHits; +} + +bool Gu::contactConvexHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + //Create a triangle cache from the HF triangles and then feed triangles to NP mesh methods + const physx::PxHeightFieldGeometryLL& shapeMesh = shape1.get(); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData0; + const bool idtScaleConvex = getConvexData(shape0, convexScaling, hullAABB, polyData0); + const PxVec3 inflation(params.mContactDistance); + hullAABB.minimum -= inflation; + hullAABB.maximum += inflation; + + return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, convexScaling, idtScaleConvex); +} + +bool Gu::contactBoxHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + //Create a triangle cache from the HF triangles and then feed triangles to NP mesh methods + const physx::PxHeightFieldGeometryLL& shapeMesh = shape1.get(); + + const PxBoxGeometry& shapeBox = shape0.get(); + + PolygonalData polyData0; + PolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData0); + + const PxVec3 inflatedExtents = shapeBox.halfExtents + PxVec3(params.mContactDistance); + + const PxBounds3 hullAABB = PxBounds3(-inflatedExtents, inflatedExtents); + + const Cm::FastVertex2ShapeScaling idtScaling; + + return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, true); +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h b/src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h new file mode 100644 index 000000000..1a73901c6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h @@ -0,0 +1,170 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONTACTMETHODIMPL_H +#define GU_CONTACTMETHODIMPL_H + +#include "foundation/PxAssert.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "collision/PxCollisionDefs.h" + +namespace physx +{ +namespace Cm +{ + class RenderOutput; +} + +namespace Gu +{ + class GeometryUnion; + class ContactBuffer; + struct NarrowPhaseParams; + class PersistentContactManifold; + class MultiplePersistentContactManifold; + + enum ManifoldFlags + { + IS_MANIFOLD = (1<<0), + IS_MULTI_MANIFOLD = (1<<1) + }; + + struct Cache : public PxCache + { + Cache() + { + } + + PX_FORCE_INLINE void setManifold(void* manifold) + { + PX_ASSERT((size_t(manifold) & 0xF) == 0); + mCachedData = reinterpret_cast(manifold); + mManifoldFlags |= IS_MANIFOLD; + } + + PX_FORCE_INLINE void setMultiManifold(void* manifold) + { + PX_ASSERT((size_t(manifold) & 0xF) == 0); + mCachedData = reinterpret_cast(manifold); + mManifoldFlags |= IS_MANIFOLD|IS_MULTI_MANIFOLD; + } + + PX_FORCE_INLINE PxU8 isManifold() const + { + return PxU8(mManifoldFlags & IS_MANIFOLD); + } + + PX_FORCE_INLINE PxU8 isMultiManifold() const + { + return PxU8(mManifoldFlags & IS_MULTI_MANIFOLD); + } + + PX_FORCE_INLINE PersistentContactManifold& getManifold() + { + PX_ASSERT(isManifold()); + PX_ASSERT(!isMultiManifold()); + PX_ASSERT((uintptr_t(mCachedData) & 0xf) == 0); + return *reinterpret_cast(mCachedData); + } + + PX_FORCE_INLINE MultiplePersistentContactManifold& getMultipleManifold() + { + PX_ASSERT(isManifold()); + PX_ASSERT(isMultiManifold()); + PX_ASSERT((uintptr_t(mCachedData) & 0xf) == 0); + return *reinterpret_cast(mCachedData); + } + }; +} + +#define GU_CONTACT_METHOD_ARGS \ + const Gu::GeometryUnion& shape0, \ + const Gu::GeometryUnion& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const Gu::NarrowPhaseParams& params, \ + Gu::Cache& cache, \ + Gu::ContactBuffer& contactBuffer, \ + Cm::RenderOutput* renderOutput + +namespace Gu +{ + PX_PHYSX_COMMON_API bool contactSphereSphere(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactSphereCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactSphereBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactConvexConvex(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool contactSphereMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactConvexMesh(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool contactSphereHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactConvexHeightfield(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool contactSpherePlane(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactPlaneBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactPlaneCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactPlaneConvex(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool pcmContactSphereMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactConvexMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereHeightField(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxHeightField(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactConvexHeightField(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool pcmContactPlaneCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactPlaneBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactPlaneConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereSphere(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSpherePlane(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactConvexConvex(GU_CONTACT_METHOD_ARGS); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp new file mode 100644 index 000000000..533ddb65b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp @@ -0,0 +1,128 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" +#include "CmMatrix34.h" + +#include "PsUtilities.h" +#include "foundation/PxUnionCast.h" + +namespace physx +{ +namespace Gu +{ +bool contactPlaneBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape0); + + // Get actual shape data + //const PxPlaneGeometry& shapePlane = shape.get(); + const PxBoxGeometry& shapeBox = shape1.get(); + + const PxVec3 negPlaneNormal = -transform0.q.getBasisVector0(); + + //Make sure we have a normalized plane + //PX_ASSERT(PxAbs(shape0.mNormal.magnitudeSquared() - 1.0f) < 0.000001f); + + Cm::Matrix34 boxMatrix(transform1); + Cm::Matrix34 boxToPlane(transform0.transformInv(transform1)); + + PxVec3 point; + + PX_ASSERT(contactBuffer.count==0); + +/* for(int vx=-1; vx<=1; vx+=2) + for(int vy=-1; vy<=1; vy+=2) + for(int vz=-1; vz<=1; vz+=2) + { + //point = boxToPlane.transform(PxVec3(shapeBox.halfExtents.x*vx, shapeBox.halfExtents.y*vy, shapeBox.halfExtents.z*vz)); + //PxReal planeEq = point.x; + //Optimized a bit + point.set(shapeBox.halfExtents.x*vx, shapeBox.halfExtents.y*vy, shapeBox.halfExtents.z*vz); + const PxReal planeEq = boxToPlane.m.column0.x*point.x + boxToPlane.m.column1.x*point.y + boxToPlane.m.column2.x*point.z + boxToPlane.p.x; + + if(planeEq <= contactDistance) + { + contactBuffer.contact(boxMatrix.transform(point), negPlaneNormal, planeEq); + + //no point in making more than 4 contacts. + if (contactBuffer.count >= 6) //was: 4) actually, with strong interpenetration more than just the bottom surface goes through, + //and we want to find the *deepest* 4 vertices, really. + return true; + } + }*/ + + // PT: the above code is shock full of LHS/FCMPs. And there's no point in limiting the number of contacts to 6 when the max possible is 8. + + const PxReal limit = params.mContactDistance - boxToPlane.p.x; + const PxReal dx = shapeBox.halfExtents.x; + const PxReal dy = shapeBox.halfExtents.y; + const PxReal dz = shapeBox.halfExtents.z; + const PxReal bxdx = boxToPlane.m.column0.x * dx; + const PxReal bxdy = boxToPlane.m.column1.x * dy; + const PxReal bxdz = boxToPlane.m.column2.x * dz; + + PxReal depths[8]; + depths[0] = bxdx + bxdy + bxdz - limit; + depths[1] = bxdx + bxdy - bxdz - limit; + depths[2] = bxdx - bxdy + bxdz - limit; + depths[3] = bxdx - bxdy - bxdz - limit; + depths[4] = - bxdx + bxdy + bxdz - limit; + depths[5] = - bxdx + bxdy - bxdz - limit; + depths[6] = - bxdx - bxdy + bxdz - limit; + depths[7] = - bxdx - bxdy - bxdz - limit; + + //const PxU32* binary = reinterpret_cast(depths); + const PxU32* binary = PxUnionCast(depths); + + if(binary[0] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, dy, dz)), negPlaneNormal, depths[0] + params.mContactDistance); + if(binary[1] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, dy, -dz)), negPlaneNormal, depths[1] + params.mContactDistance); + if(binary[2] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, -dy, dz)), negPlaneNormal, depths[2] + params.mContactDistance); + if(binary[3] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, -dy, -dz)), negPlaneNormal, depths[3] + params.mContactDistance); + if(binary[4] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, dy, dz)), negPlaneNormal, depths[4] + params.mContactDistance); + if(binary[5] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, dy, -dz)), negPlaneNormal, depths[5] + params.mContactDistance); + if(binary[6] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, -dy, dz)), negPlaneNormal, depths[6] + params.mContactDistance); + if(binary[7] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, -dy, -dz)), negPlaneNormal, depths[7] + params.mContactDistance); + + return contactBuffer.count > 0; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp new file mode 100644 index 000000000..bfde6cf2f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp @@ -0,0 +1,80 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuInternal.h" +#include "GuSegment.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactPlaneCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape0); + + // Get actual shape data + //const PxPlaneGeometry& shapePlane = shape.get(); + const PxCapsuleGeometry& shapeCapsule = shape1.get(); + + const PxTransform capsuleToPlane = transform0.transformInv(transform1); + + //Capsule in plane space + Segment segment; + getCapsuleSegment(capsuleToPlane, shapeCapsule, segment); + + const PxVec3 negPlaneNormal = transform0.q.getBasisVector0(); + + bool contact = false; + + const PxReal separation0 = segment.p0.x - shapeCapsule.radius; + const PxReal separation1 = segment.p1.x - shapeCapsule.radius; + if(separation0 <= params.mContactDistance) + { + const PxVec3 temp(segment.p0.x - shapeCapsule.radius, segment.p0.y, segment.p0.z); + const PxVec3 point = transform0.transform(temp); + contactBuffer.contact(point, -negPlaneNormal, separation0); + contact = true; + } + + if(separation1 <= params.mContactDistance) + { + const PxVec3 temp(segment.p1.x - shapeCapsule.radius, segment.p1.y, segment.p1.z); + const PxVec3 point = transform0.transform(temp); + contactBuffer.contact(point, -negPlaneNormal, separation1); + contact = true; + } + return contact; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp new file mode 100644 index 000000000..28a84855d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp @@ -0,0 +1,101 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexMeshData.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" +#include "CmScaling.h" + + +namespace physx +{ +namespace Gu +{ +bool contactPlaneConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape0); + + // Get actual shape data + //const PxPlaneGeometry& shapePlane = shape.get(); + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + + const PxVec3* PX_RESTRICT hullVertices = shapeConvex.hullData->getHullVertices(); + PxU32 numHullVertices = shapeConvex.hullData->mNbHullVertices; +// Ps::prefetch128(hullVertices); + + // Plane is implicitly <1,0,0> 0 in localspace + Cm::Matrix34 convexToPlane (transform0.transformInv(transform1)); + PxMat33 convexToPlane_rot(convexToPlane[0], convexToPlane[1], convexToPlane[2] ); + + bool idtScale = shapeConvex.scale.isIdentity(); + Cm::FastVertex2ShapeScaling convexScaling; // PT: TODO: remove default ctor + if(!idtScale) + convexScaling.init(shapeConvex.scale); + + convexToPlane = Cm::Matrix34( convexToPlane_rot * convexScaling.getVertex2ShapeSkew(), convexToPlane[3] ); + + //convexToPlane = context.mVertex2ShapeSkew[1].getVertex2WorldSkew(convexToPlane); + + const Cm::Matrix34 planeToW (transform0); + + // This is rather brute-force + + bool status = false; + + const PxVec3 contactNormal = -planeToW.m.column0; + + while(numHullVertices--) + { + const PxVec3& vertex = *hullVertices++; +// if(numHullVertices) +// Ps::prefetch128(hullVertices); + + const PxVec3 pointInPlane = convexToPlane.transform(vertex); //TODO: this multiply could be factored out! + if(pointInPlane.x <= params.mContactDistance) + { +// const PxVec3 pointInW = planeToW.transform(pointInPlane); +// contactBuffer.contact(pointInW, -planeToW.m.column0, pointInPlane.x); + status = true; + Gu::ContactPoint* PX_RESTRICT pt = contactBuffer.contact(); + if(pt) + { + pt->normal = contactNormal; + pt->point = planeToW.transform(pointInPlane); + pt->separation = pointInPlane.x; + pt->internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + } + } + return status; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp new file mode 100644 index 000000000..740c77cf4 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp @@ -0,0 +1,861 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "GuContactPolygonPolygon.h" +#include "GuShapeConvex.h" +#include "GuContactBuffer.h" +#include "PsMathUtils.h" +#include "PsAllocator.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +#define CONTACT_REDUCTION + +/* +void gVisualizeLocalLine(const PxVec3& a, const PxVec3& b, const Cm::Matrix34& m, PxsContactManager& manager) //temp debug +{ + Cm::RenderOutput out = manager.getContext()->getRenderOutput(); + out << 0xffffff << m << Cm::RenderOutput::LINES << a << b; +} +*/ + +#ifdef CONTACT_REDUCTION +static PX_FORCE_INLINE PxReal dot2D(const PxVec3& v0, const PxVec3& v1) +{ + return v0.x * v1.x + v0.y * v1.y; +} + +static void ContactReductionAllIn( ContactBuffer& contactBuffer, PxU32 nbExistingContacts, PxU32 numIn, + const PxMat33& rotT, + const PxVec3* PX_RESTRICT vertices, const PxU8* PX_RESTRICT indices) +{ + // Number of contacts created by current call + const PxU32 nbNewContacts = contactBuffer.count - nbExistingContacts; + + if(nbNewContacts<=4) + return; // no reduction for less than 4 verts + + // We have 3 different numbers here: + // - numVerts = number of vertices in the convex polygon we're dealing with + // - numIn = number of those that were "inside" the other convex polygon (should be <= numVerts) + // - contactBuffer.count = total number of contacts *from both polygons* (that's the catch here) + // The fast path can only be chosen when the contact buffer contains all the verts from current polygon, + // i.e. when contactBuffer.count == numIn == numVerts + + Gu::ContactPoint* PX_RESTRICT ctcs = contactBuffer.contacts + nbExistingContacts; + + if(numIn == nbNewContacts) + { + // Codepath 1: all vertices generated a contact + + PxReal deepestSeparation = ctcs[0].separation; + PxU32 deepestIndex = 0; + for(PxU32 i=1; i ctcs[i].separation) + { + deepestSeparation = ctcs[i].separation; + deepestIndex = i; + } + } + + PxU32 index = 0; + const PxU32 step = (numIn<<16)>>2; // Fixed point math, don't use floats here please + bool needsExtraPoint = true; + for(PxU32 i=0;i<4;i++) + { + const PxU32 contactIndex = index>>16; + ctcs[i] = ctcs[contactIndex]; + if(contactIndex==deepestIndex) + needsExtraPoint = false; + index += step; + } + + if(needsExtraPoint) + { + ctcs[4] = ctcs[deepestIndex]; + contactBuffer.count = nbExistingContacts + 5; + } + else + { + contactBuffer.count = nbExistingContacts + 4; + } + +/* PT: TODO: investigate why this one does not work + PxU32 index = deepestIndex<<16; + const PxU32 step = (numIn<<16)>>2; // Fixed point math, don't use floats here please + for(PxU32 i=0;i<4;i++) + { + PxU32 contactIndex = index>>16; + if(contactIndex>=numIn) + contactIndex -= numIn; + ctcs[i] = ctcs[contactIndex]; + index += step; + } + contactBuffer.count = nbExistingContacts + 4;*/ + } + else + { + // Codepath 2: all vertices are "in" but only some of them generated a contact + + // WARNING: this path doesn't work when the buffer contains vertices from both polys. + + // TODO: precompute those axes + const PxU32 nbAxes = 8; + PxVec3 dirs[nbAxes]; + float angle = 0.0f; + const float angleStep = Ps::degToRad(180.0f/float(nbAxes)); + for(PxU32 i=0;imaxVariance) + { + maxVariance = variance; + bestAxis = i; + } + } + + const PxVec3 u = dirs[bestAxis]; + const PxVec3 v = PxVec3(-u.y, u.x, 0.0f); + // PxVec3(1.0f, 0.0f, 0.0f) => PxVec3(0.0f, 1.0f, 0.0f) + // PxVec3(0.0f, 1.0f, 0.0f) => PxVec3(-1.0f, 0.0f, 0.0f) + // PxVec3(-1.0f, 1.0f, 0.0f) => PxVec3(-1.0f, -1.0f, 0.0f) + // PxVec3(1.0f, 1.0f, 0.0f) => PxVec3(-1.0f, 1.0f, 0.0f) + + float dpminu = PX_MAX_F32; + float dpmaxu = -PX_MAX_F32; + float dpminv = PX_MAX_F32; + float dpmaxv = -PX_MAX_F32; + PxU32 indexMinU = 0; + PxU32 indexMaxU = 0; + PxU32 indexMinV = 0; + PxU32 indexMaxV = 0; + + for(PxU32 i=0;idpmaxu) + { + dpmaxu=dpu; + indexMaxU = i; + } + + if(dpvdpmaxv) + { + dpmaxv=dpv; + indexMaxV = i; + } + } + + if(indexMaxU == indexMinU) + indexMaxU = 0xffffffff; + if(indexMinV == indexMinU || indexMinV == indexMaxU) + indexMinV = 0xffffffff; + if(indexMaxV == indexMinU || indexMaxV == indexMaxU || indexMaxV == indexMinV) + indexMaxV = 0xffffffff; + + PxU32 newCount = 0; + for(PxU32 i=0;i0.0f && y>0.0f && z<0.0f) return TRUE; + // else return FALSE; +// return (( IR(z) & ~(IR(x)|IR(y)) ) & SIGN_BITMASK) != 0; + if(x>0.0f && y>0.0f && z<0.0f) return true; + else return false; +} + + + enum OutCode + { + OUT_XP = (1<<0), + OUT_XN = (1<<1), + OUT_YP = (1<<2), + OUT_YN = (1<<3) + }; + +static +//PX_FORCE_INLINE +bool PointInConvexPolygon2D_OutCodes(const float* PX_RESTRICT pgon2D, PxU32 numVerts, const PxReal tx, const PxReal ty, const PxReal maxX, const PxReal maxY, PxU8& outCodes) +{ + PxU32 out = 0; + if(tx<0.0f) out |= OUT_XN; + if(ty<0.0f) out |= OUT_YN; + if(tx>maxX) out |= OUT_XP; + if(ty>maxY) out |= OUT_YP; + outCodes = PxU8(out); + if(out) + return false; + + if(numVerts==3) + return pointInTriangle2D( tx, ty, + pgon2D[0], pgon2D[1], + pgon2D[2] - pgon2D[0], + pgon2D[3] - pgon2D[1], + pgon2D[4] - pgon2D[0], + pgon2D[5] - pgon2D[1]); + +#define X 0 +#define Y 1 + + const PxReal* PX_RESTRICT vtx0_ = pgon2D + (numVerts-1)*2; + const PxReal* PX_RESTRICT vtx1_ = pgon2D; + + const int* PX_RESTRICT ivtx0 = reinterpret_cast(vtx0_); + const int* PX_RESTRICT ivtx1 = reinterpret_cast(vtx1_); + //const int itx = (int&)tx; + //const int ity = (int&)ty; +// const int ity = PX_SIR(ty); + const int* tmp = reinterpret_cast(&ty); + const int ity = *tmp; + + // get test bit for above/below X axis + int yflag0 = ivtx0[Y] >= ity; + + int InsideFlag = 0; + + while(numVerts--) + { + const int yflag1 = ivtx1[Y] >= ity; + if(yflag0 != yflag1) + { + const PxReal* PX_RESTRICT vtx0 = reinterpret_cast(ivtx0); + const PxReal* PX_RESTRICT vtx1 = reinterpret_cast(ivtx1); + if( ((vtx1[Y]-ty) * (vtx0[X]-vtx1[X]) > (vtx1[X]-tx) * (vtx0[Y]-vtx1[Y])) == yflag1 ) + { + if(InsideFlag == 1) return false; + + InsideFlag++; + } + } + yflag0 = yflag1; + ivtx0 = ivtx1; + ivtx1 += 2; + } +#undef X +#undef Y + + return InsideFlag & 1; +} + +// Helper function to detect contact between two edges +PX_FORCE_INLINE bool EdgeEdgeContactSpecial(const PxVec3& v1, const PxPlane& plane, + const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, + PxReal& dist, PxVec3& ip, unsigned int i, unsigned int j, float coeff) +{ + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp > 0.0f) + return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + const PxVec3 v2 = (p4-p3); + temp = plane.n.dot(v2); + if(temp == 0.0f) // ### epsilon would be better + return false; + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i])) * coeff; + if(dist < 0.0f) + return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<0.0f) + return true; // collision found + + return false; //no collision +} + +//This one can also handle 2 vertex 'polygons' (useful for capsule surface segments) and can shift the results before contact generation. +bool Gu::contactPolygonPolygonExt( PxU32 numVerts0, const PxVec3* vertices0, const PxU8* indices0, //polygon 0 + const Cm::Matrix34& world0, const PxPlane& localPlane0, //xform of polygon 0, plane of polygon + const PxMat33& rotT0, + // + PxU32 numVerts1, const PxVec3* PX_RESTRICT vertices1, const PxU8* PX_RESTRICT indices1, //polygon 1 + const Cm::Matrix34& world1, const PxPlane& localPlane1, //xform of polygon 1, plane of polygon + const PxMat33& rotT1, + // + const PxVec3& worldSepAxis, //world normal of separating plane - this is the world space normal of polygon0!! + const Cm::Matrix34& transform0to1, const Cm::Matrix34& transform1to0, //transforms between polygons + PxU32 /*polyIndex0*/, PxU32 polyIndex1, //feature indices for contact callback + ContactBuffer& contactBuffer, + bool flipNormal, const PxVec3& posShift, PxReal sepShift) // shape order, result shift +{ + const PxVec3 n = flipNormal ? -worldSepAxis : worldSepAxis; + + PX_ASSERT(indices0 != NULL && indices1 != NULL); + + // - optimize "from to" computation + // - do the raycast case && EE tests in same space as 2D case... + // - project all edges at the same time ? + PxU32 NumIn = 0; + bool status = false; + + void* PX_RESTRICT stackMemory; + { + const PxU32 maxNumVert = PxMax(numVerts0, numVerts1); + stackMemory = PxAlloca(maxNumVert * sizeof(PxVec3)); + } + + const PxU32 size0 = numVerts0 * sizeof(bool); + bool* PX_RESTRICT flags0 = reinterpret_cast(PxAlloca(size0)); + PxU8* PX_RESTRICT outCodes0 = reinterpret_cast(PxAlloca(size0)); +// Ps::memZero(flags0, size0); +// Ps::memZero(outCodes0, size0); + + const PxU32 size1 = numVerts1 * sizeof(bool); + bool* PX_RESTRICT flags1 = reinterpret_cast(PxAlloca(size1)); + PxU8* PX_RESTRICT outCodes1 = reinterpret_cast(PxAlloca(size1)); +// Ps::memZero(flags1, size1); +// Ps::memZero(outCodes1, size1); + +#ifdef CONTACT_REDUCTION + // We want to do contact reduction on newly created contacts, not on all the already existing ones... + PxU32 nbExistingContacts = contactBuffer.count; + PxU32 nbCurrentContacts=0; + PxU8 indices[ContactBuffer::MAX_CONTACTS]; +#endif + + { + //polygon 1 + float* PX_RESTRICT verts2D = NULL; + float minX=0, minY=0; + float maxX=0, maxY=0; + + const PxVec3 localDir = -world1.rotateTranspose(worldSepAxis); //contactNormal in hull1 space + //that's redundant, its equal to -localPlane1.d + const Cm::Matrix34 t0to2D = transformTranspose(rotT1, transform0to1); //transform from hull0 to RotT + + PxReal dn = localDir.dot(localPlane1.n); //if the contactNormal == +-(normal of poly0) is NOT orthogonal to poly1 ...this is just to protect the division below. + + // PT: TODO: if "numVerts1>2" we may skip more + if (numVerts1 > 2 //no need to test whether we're 'inside' ignore capsule segments and points +// if(!(-1E-7 < dn && dn < 1E-7)) + && dn >= 1E-7f) // PT: it should never be negative so this unique test is enough + { + dn = 1.0f / dn; + const float ld1 = -localPlane1.d; // PT: unavoidable "int-to-float" LHS here, so we only want to read it once! + + // Lazy-transform vertices + if(!verts2D) + { + verts2D = reinterpret_cast(stackMemory); + //Project points + transformVertices( + minX, minY, + maxX, maxY, + verts2D, numVerts1, vertices1, indices1, rotT1); + } + + for(PxU32 i=0; i < numVerts0; i++) //for all vertices of poly0 + { + const PxVec3& p = vertices0[indices0[i]]; + const float p0_z = transformZ(p, t0to2D); //transform ith vertex of poly0 to RotT + + const PxVec3 pIn1 = transform0to1.transform(p); //transform vertex to hull1 space, in which we have the poly1 vertices. + + const PxReal dd = (p0_z - ld1) * dn; //(p0_z + localPlane1.d) is the depth of the vertex behind the triangle measured along the triangle's normal. + //we convert this to being measured along the 'contact normal' using the division. + +// if(dd < 0.0f) //if the penetrating vertex will have a penetration along the contact normal: +// PX_ASSERT(dd <= 0.0f); // PT: dn is always positive, so dd is always negative + { + float px, py; + transform2DT(px, py, pIn1 - dd*localDir, rotT1); //project vertex into poly1 plane along CONTACT NORMAL - not the polygon's normal. + + const bool res = PointInConvexPolygon2D_OutCodes(verts2D, numVerts1, px-minX, py-minY, maxX, maxY, outCodes0[i]); + flags0[i] = res; + if(res) + { + NumIn++; + + if(p0_z < ld1) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { +#ifdef CONTACT_REDUCTION + indices[nbCurrentContacts++] = indices0[i]; +#endif + ctc->normal = n; + ctc->point = world0.transform(p) + (flipNormal ? posShift : PxVec3(0.0f)); + ctc->separation = dd + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + } + } + else + { + PxMemZero(flags0, size0); + PxMemZero(outCodes0, size0); + } + + if(NumIn == numVerts0) + { + //All vertices0 are inside polygon 1 +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT0, vertices0, indices); +#endif + return status; + } + +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT0, vertices0, indices); +#endif + +#ifdef CONTACT_REDUCTION + nbExistingContacts = contactBuffer.count; + nbCurrentContacts = 0; +#endif + NumIn = 0; + verts2D = NULL; + + //Polygon 0 + const Cm::Matrix34 t1to2D = transformTranspose(rotT0, transform1to0); + + if (numVerts0 > 2) //no need to test whether we're 'inside' ignore capsule segments and points + { + const float ld0 = -localPlane0.d; // PT: unavoidable "int-to-float" LHS here, so we only want to read it once! + + // Lazy-transform vertices + if(!verts2D) + { + verts2D = reinterpret_cast(stackMemory); + //Project vertices + transformVertices( + minX, minY, + maxX, maxY, + verts2D, numVerts0, vertices0, indices0, rotT0); + } + + for(PxU32 i=0; i < numVerts1; i++) + { + const PxVec3& p = vertices1[indices1[i]]; + + float px, py; + transform2D(px, py, p, t1to2D); + + const bool res = PointInConvexPolygon2D_OutCodes(verts2D, numVerts0, px-minX, py-minY, maxX, maxY, outCodes1[i]); + flags1[i] = res; + if(res) + { + NumIn++; + + const float pz = transformZ(p, t1to2D); + if(pz < ld0) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function + + // PT: in theory, with this contact point we should use "worldSepAxis" as a contact normal. + // However we want to output the same normal for all contact points not to break friction + // patches!!! In theory again, it should be exactly the same since the contact point at + // time of impact is supposed to be the same on both bodies. In practice however, and with + // a depth-based engine, this is not the case. So the contact point here is not exactly + // right, but preserving the friction patch seems more important. + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { +#ifdef CONTACT_REDUCTION + indices[nbCurrentContacts++] = indices1[i]; +#endif + ctc->normal = n; + ctc->point = world1.transform(p) + (flipNormal ? PxVec3(0.0f) : posShift); + ctc->separation = (pz - ld0) + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + + if(NumIn == numVerts1) + { + //all vertices 1 are inside polygon 0 +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT1, vertices1, indices); +#endif + return status; + } +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT1, vertices1, indices); +#endif + } + else + { + PxMemZero(flags1, size1); + PxMemZero(outCodes1, size1); + } + } + + //Edge/edge case + //Calculation done in space 0 + PxVec3* PX_RESTRICT verts1in0 = reinterpret_cast(stackMemory); + for(PxU32 i=0; i= 2 && numVerts1 >= 2)//useless if one of them is degenerate. + for(PxU32 j=0; j= numVerts1) j1 = 0; + +// if(!(flags1[j] ^ flags1[j1])) +// continue; + if(flags1[j] && flags1[j1]) + continue; + if(outCodes1[j]&outCodes1[j1]) + continue; + + const PxVec3& p0 = verts1in0[j]; + const PxVec3& p1 = verts1in0[j1]; + +// gVisualizeLocalLine(vertices1[indices1[j]], vertices1[indices1[j1]], world1, callback.getManager()); + + const PxVec3 v1 = p1-p0; + const PxVec3 planeNormal = v1.cross(localPlane0.n); + const PxPlane plane(planeNormal, -(planeNormal.dot(p0))); + + // find largest 2D plane projection + PxU32 _i, _j; + Ps::closestAxis(planeNormal, _i, _j); + + const PxReal coeff = 1.0f / (v1[_i]*localPlane0.n[_j]-v1[_j]*localPlane0.n[_i]); + + for(PxU32 i=0; i= numVerts0) i1 = 0; + +// if(!(flags0[i] ^ flags0[i1])) +// continue; + if(flags0[i] && flags0[i1]) + continue; + if(outCodes0[i]&outCodes0[i1]) + continue; + + const PxVec3& p0b = vertices0[indices0[i]]; + const PxVec3& p1b = vertices0[indices0[i1]]; + +// gVisualizeLocalLine(p0b, p1b, world0, callback.getManager()); + + PxReal dist; + PxVec3 p; + + if(EdgeEdgeContactSpecial(v1, plane, p0, p1, localPlane0.n, p0b, p1b, dist, p, _i, _j, coeff)) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function +/* p = world0.transform(p); + + //contacts are generated on the edges of polygon 1 + //we only have to shift the position of polygon 1 if flipNormal is false, because + //in this case convex 0 gets passed as polygon 1, and it is convex 0 that was shifted. + if (!flipNormal) + p += posShift; + + contactBuffer.contact(p, n, -dist + sepShift, polyIndex0, polyIndex1, convexID);*/ + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { + ctc->normal = n; + ctc->point = world0.transform(p) + (flipNormal ? PxVec3(0.0f) : posShift); + ctc->separation = -dist + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + return status; +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h new file mode 100644 index 000000000..7789b5af1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONTACTPOLYGONPOLYGON_H +#define GU_CONTACTPOLYGONPOLYGON_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ + +namespace Cm +{ + class Matrix34; + class FastVertex2ShapeScaling; +} + +namespace Gu +{ +class ContactBuffer; + +PX_PHYSX_COMMON_API PxMat33 findRotationMatrixFromZ(const PxVec3& to); + +PX_PHYSX_COMMON_API bool contactPolygonPolygonExt( PxU32 numVerts0, const PxVec3* vertices0, const PxU8* indices0,//polygon 0 + const Cm::Matrix34& world0, const PxPlane& localPlane0, //xform of polygon 0, plane of polygon + const PxMat33& RotT0, + + PxU32 numVerts1, const PxVec3* vertices1, const PxU8* indices1,//polygon 1 + const Cm::Matrix34& world1, const PxPlane& localPlane1, //xform of polygon 1, plane of polygon + const PxMat33& RotT1, + + const PxVec3& worldSepAxis, //world normal of separating plane - this is the world space normal of polygon0!! + const Cm::Matrix34& transform0to1, const Cm::Matrix34& transform1to0,//transforms between polygons + PxU32 polyIndex0, PxU32 polyIndex1, //face indices for contact callback, + ContactBuffer& contactBuffer, + bool flipNormal, const PxVec3& posShift, float sepShift + ); // shape order, post gen shift. +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp new file mode 100644 index 000000000..722364c44 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp @@ -0,0 +1,181 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" + +using namespace physx; + +//This version is ported 1:1 from novodex +static PX_FORCE_INLINE bool ContactSphereBox(const PxVec3& sphereOrigin, + PxReal sphereRadius, + const PxVec3& boxExtents, +// const PxcCachedTransforms& boxCacheTransform, + const PxTransform& boxTransform, + PxVec3& point, + PxVec3& normal, + PxReal& separation, + PxReal contactDistance) +{ +// const PxTransform& boxTransform = boxCacheTransform.getShapeToWorld(); + + //returns true on contact + const PxVec3 delta = sphereOrigin - boxTransform.p; // s1.center - s2.center; + PxVec3 dRot = boxTransform.rotateInv(delta); //transform delta into OBB body coords. + + //check if delta is outside ABB - and clip the vector to the ABB. + bool outside = false; + + if (dRot.x < -boxExtents.x) + { + outside = true; + dRot.x = -boxExtents.x; + } + else if (dRot.x > boxExtents.x) + { + outside = true; + dRot.x = boxExtents.x; + } + + if (dRot.y < -boxExtents.y) + { + outside = true; + dRot.y = -boxExtents.y; + } + else if (dRot.y > boxExtents.y) + { + outside = true; + dRot.y = boxExtents.y; + } + + if (dRot.z < -boxExtents.z) + { + outside = true; + dRot.z =-boxExtents.z; + } + else if (dRot.z > boxExtents.z) + { + outside = true; + dRot.z = boxExtents.z; + } + + if (outside) //if clipping was done, sphere center is outside of box. + { + point = boxTransform.rotate(dRot); //get clipped delta back in world coords. + normal = delta - point; //what we clipped away. + const PxReal lenSquared = normal.magnitudeSquared(); + const PxReal inflatedDist = sphereRadius + contactDistance; + if (lenSquared > inflatedDist * inflatedDist) + return false; //disjoint + + //normalize to make it into the normal: + separation = PxRecipSqrt(lenSquared); + normal *= separation; + separation *= lenSquared; + //any plane that touches the sphere is tangential, so a vector from contact point to sphere center defines normal. + //we could also use point here, which has same direction. + //this is either a faceFace or a vertexFace contact depending on whether the box's face or vertex collides, but we did not distinguish. + //We'll just use vertex face for now, this info isn't really being used anyway. + //contact point is point on surface of cube closest to sphere center. + point += boxTransform.p; + separation -= sphereRadius; + return true; + } + else + { + //center is in box, we definitely have a contact. + PxVec3 locNorm; //local coords contact normal + + /*const*/ PxVec3 absdRot; + absdRot = PxVec3(PxAbs(dRot.x), PxAbs(dRot.y), PxAbs(dRot.z)); + /*const*/ PxVec3 distToSurface = boxExtents - absdRot; //dist from embedded center to box surface along 3 dimensions. + + //find smallest element of distToSurface + if (distToSurface.y < distToSurface.x) + { + if (distToSurface.y < distToSurface.z) + { + //y + locNorm = PxVec3(0.0f, dRot.y > 0.0f ? 1.0f : -1.0f, 0.0f); + separation = -distToSurface.y; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + else + { + if (distToSurface.x < distToSurface.z) + { + //x + locNorm = PxVec3(dRot.x > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f); + separation = -distToSurface.x; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + //separation so far is just the embedding of the center point; we still have to push out all of the radius. + point = sphereOrigin; + normal = boxTransform.rotate(locNorm); + separation -= sphereRadius; + return true; + } +} + +namespace physx +{ +namespace Gu +{ +bool contactSphereBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = shape0.get(); + const PxBoxGeometry& boxGeom = shape1.get(); + + PxVec3 normal; + PxVec3 point; + PxReal separation; + if(!ContactSphereBox(transform0.p, sphereGeom.radius, boxGeom.halfExtents, transform1, point, normal, separation, params.mContactDistance)) + return false; + + contactBuffer.contact(point, normal, separation); + return true; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp new file mode 100644 index 000000000..54d7c4d93 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp @@ -0,0 +1,82 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuDistancePointSegment.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuInternal.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactSphereCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = shape0.get(); + const PxCapsuleGeometry& capsuleGeom = shape1.get(); + + // PT: get capsule in local space + const PxVec3 capsuleLocalSegment = getCapsuleHalfHeightVector(transform1, capsuleGeom); + const Segment localSegment(capsuleLocalSegment, -capsuleLocalSegment); + + // PT: get sphere in capsule space + const PxVec3 sphereCenterInCapsuleSpace = transform0.p - transform1.p; + + const PxReal radiusSum = sphereGeom.radius + capsuleGeom.radius; + const PxReal inflatedSum = radiusSum + params.mContactDistance; + + // PT: compute distance between sphere center & capsule's segment + PxReal u; + const PxReal squareDist = distancePointSegmentSquared(localSegment, sphereCenterInCapsuleSpace, &u); + if(squareDist >= inflatedSum*inflatedSum) + return false; + + // PT: compute contact normal + PxVec3 normal = sphereCenterInCapsuleSpace - localSegment.getPointAt(u); + + // We do a *manual* normalization to check for singularity condition + const PxReal lenSq = normal.magnitudeSquared(); + if(lenSq==0.0f) + normal = PxVec3(1.0f, 0.0f, 0.0f); // PT: zero normal => pick up random one + else + normal *= PxRecipSqrt(lenSq); + + // PT: compute contact point + const PxVec3 point = sphereCenterInCapsuleSpace + transform1.p - normal * sphereGeom.radius; + + // PT: output unique contact + contactBuffer.contact(point, normal, PxSqrt(squareDist) - radiusSum); + return true; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp new file mode 100644 index 000000000..2c1eeea81 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp @@ -0,0 +1,614 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuDistancePointTriangle.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" +#include "GuFeatureCode.h" +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuBox.h" +#include "PsSort.h" + +#include "CmRenderOutput.h" + +#define DEBUG_RENDER_MESHCONTACTS 0 + +using namespace physx; +using namespace Gu; + +static const bool gDrawTouchedTriangles = false; + +static void outputErrorMessage() +{ +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Dropping contacts in sphere vs mesh: exceeded limit of 64 "); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: a customized version that also returns the feature code +static PxVec3 closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t, FeatureCode& fc) +{ + // Check if P in vertex region outside A + const PxVec3 ab = b - a; + const PxVec3 ac = c - a; + const PxVec3 ap = p - a; + const float d1 = ab.dot(ap); + const float d2 = ac.dot(ap); + if(d1<=0.0f && d2<=0.0f) + { + s = 0.0f; + t = 0.0f; + fc = FC_VERTEX0; + return a; // Barycentric coords 1,0,0 + } + + // Check if P in vertex region outside B + const PxVec3 bp = p - b; + const float d3 = ab.dot(bp); + const float d4 = ac.dot(bp); + if(d3>=0.0f && d4<=d3) + { + s = 1.0f; + t = 0.0f; + fc = FC_VERTEX1; + return b; // Barycentric coords 0,1,0 + } + + // Check if P in edge region of AB, if so return projection of P onto AB + const float vc = d1*d4 - d3*d2; + if(vc<=0.0f && d1>=0.0f && d3<=0.0f) + { + const float v = d1 / (d1 - d3); + s = v; + t = 0.0f; + fc = FC_EDGE01; + return a + v * ab; // barycentric coords (1-v, v, 0) + } + + // Check if P in vertex region outside C + const PxVec3 cp = p - c; + const float d5 = ab.dot(cp); + const float d6 = ac.dot(cp); + if(d6>=0.0f && d5<=d6) + { + s = 0.0f; + t = 1.0f; + fc = FC_VERTEX2; + return c; // Barycentric coords 0,0,1 + } + + // Check if P in edge region of AC, if so return projection of P onto AC + const float vb = d5*d2 - d1*d6; + if(vb<=0.0f && d2>=0.0f && d6<=0.0f) + { + const float w = d2 / (d2 - d6); + s = 0.0f; + t = w; + fc = FC_EDGE20; + return a + w * ac; // barycentric coords (1-w, 0, w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + const float va = d3*d6 - d5*d4; + if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f) + { + const float w = (d4-d3) / ((d4 - d3) + (d5-d6)); + s = 1.0f-w; + t = w; + fc = FC_EDGE12; + return b + w * (c-b); // barycentric coords (0, 1-w, w) + } + + // P inside face region. Compute Q through its barycentric coords (u,v,w) + const float denom = 1.0f / (va + vb + vc); + const float v = vb * denom; + const float w = vc * denom; + s = v; + t = w; + fc = FC_FACE; + return a + ab*v + ac*w; +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: we use a separate structure to make sorting faster +struct SortKey +{ + float mSquareDist; + PxU32 mIndex; + + PX_FORCE_INLINE bool operator < (const SortKey& data) const + { + return mSquareDist < data.mSquareDist; + } +}; + +struct TriangleData +{ + PxVec3 mDelta; + FeatureCode mFC; + PxU32 mTriangleIndex; + PxU32 mVRef[3]; +}; + +struct CachedTriangleIndices +{ + PxU32 mVRef[3]; +}; + +static PX_FORCE_INLINE bool validateSquareDist(PxReal squareDist) +{ + return squareDist>0.0001f; +} + +static bool validateEdge(PxU32 vref0, PxU32 vref1, const CachedTriangleIndices* cachedTris, PxU32 nbCachedTris) +{ + while(nbCachedTris--) + { + const CachedTriangleIndices& inds = *cachedTris++; + const PxU32 vi0 = inds.mVRef[0]; + const PxU32 vi1 = inds.mVRef[1]; + const PxU32 vi2 = inds.mVRef[2]; + + if(vi0==vref0) + { + if(vi1==vref1 || vi2==vref1) + return false; + } + else if(vi1==vref0) + { + if(vi0==vref1 || vi2==vref1) + return false; + } + else if(vi2==vref0) + { + if(vi1==vref1 || vi0==vref1) + return false; + } + } + return true; +} + +static bool validateVertex(PxU32 vref, const CachedTriangleIndices* cachedTris, PxU32 nbCachedTris) +{ + while(nbCachedTris--) + { + const CachedTriangleIndices& inds = *cachedTris++; + if(inds.mVRef[0]==vref || inds.mVRef[1]==vref || inds.mVRef[2]==vref) + return false; + } + return true; +} + +namespace +{ + class NullAllocator + { + public: + PX_FORCE_INLINE NullAllocator() { } + PX_FORCE_INLINE void* allocate(size_t, const char*, int) { return NULL; } + PX_FORCE_INLINE void deallocate(void*) { } + }; + +struct SphereMeshContactGeneration +{ + const PxSphereGeometry& mShapeSphere; + const PxTransform& mTransform0; + const PxTransform& mTransform1; + ContactBuffer& mContactBuffer; + const PxVec3& mSphereCenterShape1Space; + PxF32 mInflatedRadius2; + PxU32 mNbDelayed; + TriangleData mSavedData[ContactBuffer::MAX_CONTACTS]; + SortKey mSortKey[ContactBuffer::MAX_CONTACTS]; + PxU32 mNbCachedTris; + CachedTriangleIndices mCachedTris[ContactBuffer::MAX_CONTACTS]; + Cm::RenderOutput* mRenderOutput; + + SphereMeshContactGeneration(const PxSphereGeometry& shapeSphere, const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, + Cm::RenderOutput* renderOutput) : + mShapeSphere (shapeSphere), + mTransform0 (transform0), + mTransform1 (transform1), + mContactBuffer (contactBuffer), + mSphereCenterShape1Space (sphereCenterShape1Space), + mInflatedRadius2 (inflatedRadius*inflatedRadius), + mNbDelayed (0), + mNbCachedTris (0), + mRenderOutput (renderOutput) + { + } + + PX_FORCE_INLINE void cacheTriangle(PxU32 ref0, PxU32 ref1, PxU32 ref2) + { + const PxU32 nb = mNbCachedTris++; + mCachedTris[nb].mVRef[0] = ref0; + mCachedTris[nb].mVRef[1] = ref1; + mCachedTris[nb].mVRef[2] = ref2; + } + + PX_FORCE_INLINE void addContact(const PxVec3& d, PxReal squareDist, PxU32 triangleIndex) + { + float dist; + PxVec3 delta; + if(validateSquareDist(squareDist)) + { + // PT: regular contact. Normalize 'delta'. + dist = PxSqrt(squareDist); + delta = d / dist; + } + else + { + // PT: singular contact: 'd' is the non-unit triangle's normal in this case. + dist = 0.0f; + delta = -d.getNormalized(); + } + + const PxVec3 worldNormal = -mTransform1.rotate(delta); + + const PxVec3 localHit = mSphereCenterShape1Space + mShapeSphere.radius*delta; + const PxVec3 hit = mTransform1.transform(localHit); + + if(!mContactBuffer.contact(hit, worldNormal, dist - mShapeSphere.radius, triangleIndex)) + outputErrorMessage(); + } + + void processTriangle(PxU32 triangleIndex, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, const PxU32* vertInds) + { + // PT: compute closest point between sphere center and triangle + PxReal u, v; + FeatureCode fc; + const PxVec3 cp = closestPtPointTriangle(mSphereCenterShape1Space, v0, v1, v2, u, v, fc); + + // PT: compute 'delta' vector between closest point and sphere center + const PxVec3 delta = cp - mSphereCenterShape1Space; + const PxReal squareDist = delta.magnitudeSquared(); + if(squareDist >= mInflatedRadius2) + return; + + // PT: backface culling without the normalize + // PT: TODO: consider doing before the pt-triangle distance test if it's cheaper + // PT: TODO: e0/e1 already computed in closestPtPointTriangle + const PxVec3 e0 = v1 - v0; + const PxVec3 e1 = v2 - v0; + const PxVec3 planeNormal = e0.cross(e1); + const PxF32 planeD = planeNormal.dot(v0); // PT: actually -d compared to PxcPlane + if(planeNormal.dot(mSphereCenterShape1Space) < planeD) + return; + + // PT: for a regular contact, 'delta' is non-zero (and so is 'squareDist'). However when the sphere's center exactly touches + // the triangle, then both 'delta' and 'squareDist' become zero. This needs to be handled as a special case to avoid dividing + // by zero. We will use the triangle's normal as a contact normal in this special case. + // + // 'validateSquareDist' is called twice because there are conflicting goals here. We could call it once now and already + // compute the proper data for generating the contact. But this would mean doing a square-root and a division right here, + // even when the contact is not actually needed in the end. We could also call it only once in "addContact', but the plane's + // normal would not always be available (in case of delayed contacts), and thus it would need to be either recomputed (slower) + // or stored within 'TriangleData' (using more memory). Calling 'validateSquareDist' twice is a better option overall. + PxVec3 d; + if(validateSquareDist(squareDist)) + d = delta; + else + d = planeNormal; + + if(fc==FC_FACE) + { + addContact(d, squareDist, triangleIndex); + + if(mNbCachedTrismDelta = d; + saved->mVRef[0] = vertInds[0]; + saved->mVRef[1] = vertInds[1]; + saved->mVRef[2] = vertInds[2]; + saved->mFC = fc; + saved->mTriangleIndex = triangleIndex; + } + else outputErrorMessage(); + } + } + + void generateLastContacts() + { + const PxU32 count = mNbDelayed; + if(!count) + return; + + Ps::sort(mSortKey, count, Ps::Less(), NullAllocator(), ContactBuffer::MAX_CONTACTS); + + TriangleData* touchedTris = mSavedData; + for(PxU32 i=0;i +{ + SphereMeshContactGeneration mGeneration; + const TriangleMesh& mMeshData; + + SphereMeshContactGenerationCallback_NoScale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, const PxTransform& transform1, ContactBuffer& contactBuffer, + const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, Cm::RenderOutput* renderOutput + ) : MeshHitCallback (CallbackMode::eMULTIPLE), + mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput), + mMeshData (meshData) + { + } + + virtual ~SphereMeshContactGenerationCallback_NoScale() + { + mGeneration.generateLastContacts(); + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + if(gDrawTouchedTriangles) + { + (*mGeneration.mRenderOutput) << 0xffffffff; + (*mGeneration.mRenderOutput) << PxMat44(PxIdentity); + const PxVec3 wp0 = mGeneration.mTransform1.transform(v0); + const PxVec3 wp1 = mGeneration.mTransform1.transform(v1); + const PxVec3 wp2 = mGeneration.mTransform1.transform(v2); + mGeneration.mRenderOutput->outputSegment(wp0, wp1); + mGeneration.mRenderOutput->outputSegment(wp1, wp2); + mGeneration.mRenderOutput->outputSegment(wp2, wp0); + } + + mGeneration.processTriangle(hit.faceIndex, v0, v1, v2, vinds); + return true; + } + +protected: + SphereMeshContactGenerationCallback_NoScale &operator=(const SphereMeshContactGenerationCallback_NoScale &); +}; + +struct SphereMeshContactGenerationCallback_Scale : SphereMeshContactGenerationCallback_NoScale +{ + const Cm::FastVertex2ShapeScaling& mMeshScaling; + + SphereMeshContactGenerationCallback_Scale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, const PxTransform& transform1, const Cm::FastVertex2ShapeScaling& meshScaling, + ContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, Cm::RenderOutput* renderOutput + ) : SphereMeshContactGenerationCallback_NoScale(meshData, shapeSphere, + transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput), + mMeshScaling (meshScaling) + { + } + + virtual ~SphereMeshContactGenerationCallback_Scale() {} + + virtual PxAgain processHit(const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + PxVec3 verts[3]; + getScaledVertices(verts, v0, v1, v2, false, mMeshScaling); + + if(gDrawTouchedTriangles) + { + (*mGeneration.mRenderOutput) << 0xffffffff; + (*mGeneration.mRenderOutput) << PxMat44(PxIdentity); + const PxVec3 wp0 = mGeneration.mTransform1.transform(verts[0]); + const PxVec3 wp1 = mGeneration.mTransform1.transform(verts[1]); + const PxVec3 wp2 = mGeneration.mTransform1.transform(verts[2]); + mGeneration.mRenderOutput->outputSegment(wp0, wp1); + mGeneration.mRenderOutput->outputSegment(wp1, wp2); + mGeneration.mRenderOutput->outputSegment(wp2, wp0); + } + + mGeneration.processTriangle(hit.faceIndex, verts[0], verts[1], verts[2], vinds); + return true; + } +protected: + SphereMeshContactGenerationCallback_Scale &operator=(const SphereMeshContactGenerationCallback_Scale &); +}; + +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Gu::contactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + // We must be in local space to use the cache + const PxVec3 sphereCenterInMeshSpace = transform1.transformInv(transform0.p); + const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + const TriangleMesh* meshData = shapeMesh.meshData; + + // mesh scale is not baked into cached verts + if(shapeMesh.scale.isIdentity()) + { + SphereMeshContactGenerationCallback_NoScale callback( + *meshData, shapeSphere, transform0, transform1, + contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + // PT: TODO: switch to sphere query here + const Box obb(sphereCenterInMeshSpace, PxVec3(inflatedRadius), PxMat33(PxIdentity)); + Midphase::intersectOBB(meshData, obb, callback, true); + } + else + { + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + SphereMeshContactGenerationCallback_Scale callback( + *meshData, shapeSphere, transform0, transform1, + meshScaling, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + PxVec3 obbCenter = sphereCenterInMeshSpace; + PxVec3 obbExtents = PxVec3(inflatedRadius); + PxMat33 obbRot(PxIdentity); + meshScaling.transformQueryBounds(obbCenter, obbExtents, obbRot); + + const Box obb(obbCenter, obbExtents, obbRot); + + Midphase::intersectOBB(meshData, obb, callback, true); + } + return contactBuffer.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +namespace +{ +struct SphereHeightfieldContactGenerationCallback : EntityReport +{ + SphereMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + + SphereHeightfieldContactGenerationCallback( + HeightFieldUtil& hfUtil, + const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, + const PxTransform& transform1, + ContactBuffer& contactBuffer, + const PxVec3& sphereCenterInMeshSpace, + PxF32 inflatedRadius, + Cm::RenderOutput* renderOutput + ) : + mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput), + mHfUtil (hfUtil) + { + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + while(nb--) + { + const PxU32 triangleIndex = *indices++; + PxU32 vertIndices[3]; + PxTriangle currentTriangle; + mHfUtil.getTriangle(mGeneration.mTransform1, currentTriangle, vertIndices, NULL, triangleIndex, false, false); + + mGeneration.processTriangle(triangleIndex, currentTriangle.verts[0], currentTriangle.verts[1], currentTriangle.verts[2], vertIndices); + } + return true; + } +protected: + SphereHeightfieldContactGenerationCallback &operator=(const SphereHeightfieldContactGenerationCallback &); +}; +} + +bool Gu::contactSphereHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxHeightFieldGeometryLL& shapeMesh = shape1.get(); + + HeightFieldUtil hfUtil(shapeMesh); + + const PxVec3 sphereCenterInMeshSpace = transform1.transformInv(transform0.p); + const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + const PxVec3 inflatedRV3(inflatedRadius); + + const PxBounds3 bounds(sphereCenterInMeshSpace - inflatedRV3, sphereCenterInMeshSpace + inflatedRV3); + + SphereHeightfieldContactGenerationCallback blockCallback(hfUtil, shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &blockCallback); + + blockCallback.mGeneration.generateLastContacts(); + + return contactBuffer.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp new file mode 100644 index 000000000..470b2d586 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactSpherePlane(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape1); + + // Get actual shape data + const PxSphereGeometry& shapeSphere = shape0.get(); + //const PxPlaneGeometry& shapePlane = shape1.get(); + + //Sphere in plane space + const PxVec3 sphere = transform1.transformInv(transform0.p); + + //Make sure we have a normalized plane + //The plane is implicitly n=<1,0,0> d=0 (in plane-space) + //PX_ASSERT(PxAbs(shape1.mNormal.magnitudeSquared() - 1.0f) < 0.000001f); + + //Separation + const PxReal separation = sphere.x - shapeSphere.radius; + + if(separation<=params.mContactDistance) + { + const PxVec3 normal = transform1.q.getBasisVector0(); + const PxVec3 point = transform0.p - normal * shapeSphere.radius; + contactBuffer.contact(point, normal, separation); + return true; + } + return false; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp new file mode 100644 index 000000000..d8ffdb4b9 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactSphereSphere(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom0 = shape0.get(); + const PxSphereGeometry& sphereGeom1 = shape1.get(); + + PxVec3 delta = transform0.p - transform1.p; + + const PxReal distanceSq = delta.magnitudeSquared(); + const PxReal radiusSum = sphereGeom0.radius + sphereGeom1.radius; + const PxReal inflatedSum = radiusSum + params.mContactDistance; + if(distanceSq >= inflatedSum*inflatedSum) + return false; + + // We do a *manual* normalization to check for singularity condition + const PxReal magn = PxSqrt(distanceSq); + if(magn<=0.00001f) + delta = PxVec3(1.0f, 0.0f, 0.0f); // PT: spheres are exactly overlapping => can't create normal => pick up random one + else + delta *= 1.0f/magn; + + // PT: TODO: why is this formula different from the original code? + const PxVec3 contact = delta * ((sphereGeom0.radius + magn - sphereGeom1.radius)*-0.5f) + transform0.p; + + contactBuffer.contact(contact, delta, magn - radiusSum); + return true; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp new file mode 100644 index 000000000..d0bc0ecf1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp @@ -0,0 +1,128 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexEdgeFlags.h" +#include "GuFeatureCode.h" + +using namespace physx; +using namespace Gu; + +static FeatureCode computeFeatureCode(PxReal u, PxReal v) +{ + // Analysis + if(u==0.0f) + { + if(v==0.0f) + { + // Vertex 0 + return FC_VERTEX0; + } + else if(v==1.0f) + { + // Vertex 2 + return FC_VERTEX2; + } + else + { + // Edge 0-2 + return FC_EDGE20; + } + } + else if(u==1.0f) + { + if(v==0.0f) + { + // Vertex 1 + return FC_VERTEX1; + } + } + else + { + if(v==0.0f) + { + // Edge 0-1 + return FC_EDGE01; + } + else + { + if((u+v)>=0.9999f) + { + // Edge 1-2 + return FC_EDGE12; + } + else + { + // Face + return FC_FACE; + } + } + } + return FC_UNDEFINED; +} + + +bool Gu::selectNormal(PxU8 data, PxReal u, PxReal v) +{ + bool useFaceNormal = false; + const FeatureCode FC = computeFeatureCode(u, v); + switch(FC) + { + case FC_VERTEX0: + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_20))) + useFaceNormal = true; + break; + case FC_VERTEX1: + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_12))) + useFaceNormal = true; + break; + case FC_VERTEX2: + if(!(data & (Gu::ETD_CONVEX_EDGE_12|Gu::ETD_CONVEX_EDGE_20))) + useFaceNormal = true; + break; + case FC_EDGE01: + if(!(data & Gu::ETD_CONVEX_EDGE_01)) + useFaceNormal = true; + break; + case FC_EDGE12: + if(!(data & Gu::ETD_CONVEX_EDGE_12)) + useFaceNormal = true; + break; + case FC_EDGE20: + if(!(data & Gu::ETD_CONVEX_EDGE_20)) + useFaceNormal = true; + break; + case FC_FACE: + useFaceNormal = true; + break; + case FC_UNDEFINED: + break; + }; + return useFaceNormal; +} + diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h new file mode 100644 index 000000000..ab1e0cb8e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h @@ -0,0 +1,56 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_FEATURE_CODE_H +#define GU_FEATURE_CODE_H + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + enum FeatureCode + { + FC_VERTEX0, + FC_VERTEX1, + FC_VERTEX2, + FC_EDGE01, + FC_EDGE12, + FC_EDGE20, + FC_FACE, + + FC_UNDEFINED + }; + + bool selectNormal(PxU8 data, PxReal u, PxReal v); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp new file mode 100644 index 000000000..52e94931d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp @@ -0,0 +1,203 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "PsUserAllocated.h" +#include "GuSerialize.h" +#include "GuBigConvexData2.h" +#include "GuCubeIndex.h" +#include "PsIntrinsics.h" +#include "CmUtils.h" +#include "PsUtilities.h" +#include "PsAllocator.h" + +using namespace physx; +using namespace Gu; + +BigConvexData::BigConvexData() : mVBuffer(NULL) +{ + mData.mSubdiv = 0; + mData.mNbSamples = 0; + mData.mSamples = NULL; + + ////// + + mData.mNbVerts = 0; + mData.mNbAdjVerts = 0; + mData.mValencies = NULL; + mData.mAdjacentVerts = NULL; +} + +BigConvexData::~BigConvexData() +{ + PX_FREE(mData.mSamples); + + /////////// + + if(mVBuffer) + { + PX_FREE(mVBuffer); + } + else + { + // Allocated from somewhere else!! + PX_FREE(mData.mValencies); + PX_FREE(mData.mAdjacentVerts); + } +} + +void BigConvexData::CreateOffsets() +{ + // Create offsets (radix style) + mData.mValencies[0].mOffset = 0; + for(PxU32 i=1;i(mVBuffer); + mData.mAdjacentVerts = (reinterpret_cast(mVBuffer)) + sizeof(Gu::Valency)*numVerts; + + PX_ASSERT(0 == (size_t(mData.mAdjacentVerts) & 0xf)); + PX_ASSERT(Version==2); + + { + PxU16* temp = reinterpret_cast(mData.mValencies); + + PxU32 MaxIndex = readDword(Mismatch, stream); + ReadIndices(Ps::to16(MaxIndex), mData.mNbVerts, temp, stream, Mismatch); + + // We transform from: + // + // |5555|4444|3333|2222|1111|----|----|----|----|----| + // + // to: + // + // |5555|4444|4444|2222|3333|----|2222|----|1111|----| + // + for(PxU32 i=0;i(PX_ALLOC(sizeof(PxU8)*mData.mNbSamples*2, "BigConvex Samples Data")); + + // These byte buffers shouldn't need converting + stream.read(mData.mSamples, sizeof(PxU8)*mData.mNbSamples*2); + + //load the valencies + return VLoad(stream); +} + +// PX_SERIALIZATION +void BigConvexData::exportExtraData(PxSerializationContext& stream) +{ + if(mData.mSamples) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData.mSamples, sizeof(PxU8)*mData.mNbSamples*2); + } + + if(mData.mValencies) + { + stream.alignData(PX_SERIAL_ALIGN); + PxU32 numVerts = (mData.mNbVerts+3)&~3; + const PxU32 TotalSize = sizeof(Gu::Valency)*numVerts + sizeof(PxU8)*mData.mNbAdjVerts; + stream.writeData(mData.mValencies, TotalSize); + } +} + +void BigConvexData::importExtraData(PxDeserializationContext& context) +{ + if(mData.mSamples) + mData.mSamples = context.readExtraData(PxU32(mData.mNbSamples*2)); + + if(mData.mValencies) + { + context.alignExtraData(); + PxU32 numVerts = (mData.mNbVerts+3)&~3; + mData.mValencies = context.readExtraData(numVerts); + mData.mAdjacentVerts = context.readExtraData(mData.mNbAdjVerts); + + } +} +//~PX_SERIALIZATION + diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h new file mode 100644 index 000000000..90af247ec --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BIG_CONVEX_DATA_H +#define GU_BIG_CONVEX_DATA_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + +class BigConvexDataBuilder; +class PxcHillClimb; +class BigConvexData; + +// Data + +namespace Gu +{ + +struct Valency +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PxU16 mCount; + PxU16 mOffset; +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::Valency) == 4); + +struct BigConvexRawData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + // Support vertex map + PxU16 mSubdiv; // "Gaussmap" subdivision + PxU16 mNbSamples; // Total #samples in gaussmap PT: this is not even needed at runtime! + + PxU8* mSamples; + PX_FORCE_INLINE const PxU8* getSamples2() const + { + return mSamples + mNbSamples; + } + //~Support vertex map + + // Valencies data + PxU32 mNbVerts; //!< Number of vertices + PxU32 mNbAdjVerts; //!< Total number of adjacent vertices ### PT: this is useless at runtime and should not be stored here + Gu::Valency* mValencies; //!< A list of mNbVerts valencies (= number of neighbors) + PxU8* mAdjacentVerts; //!< List of adjacent vertices + //~Valencies data +}; +#if PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(sizeof(Gu::BigConvexRawData) == 40); +#else +PX_COMPILE_TIME_ASSERT(sizeof(Gu::BigConvexRawData) == 24); +#endif + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h new file mode 100644 index 000000000..9fc5d717f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BIG_CONVEX_DATA2_H +#define GU_BIG_CONVEX_DATA2_H + +#include "GuBigConvexData.h" +#include "PxMetaData.h" + +namespace physx +{ + class PxSerializationContext; + class PxDeserializationContext; + + class PX_PHYSX_COMMON_API BigConvexData : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + BigConvexData(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + BigConvexData(); + ~BigConvexData(); + // Support vertex map + bool Load(PxInputStream& stream); + + PxU32 ComputeOffset(const PxVec3& dir) const; + PxU32 ComputeNearestOffset(const PxVec3& dir) const; + + // Data access + PX_INLINE PxU32 GetSubdiv() const { return mData.mSubdiv; } + PX_INLINE PxU32 GetNbSamples() const { return mData.mNbSamples; } + //~Support vertex map + + // Valencies + // Data access + PX_INLINE PxU32 GetNbVerts() const { return mData.mNbVerts; } + PX_INLINE const Gu::Valency* GetValencies() const { return mData.mValencies; } + PX_INLINE PxU16 GetValency(PxU32 i) const { return mData.mValencies[i].mCount; } + PX_INLINE PxU16 GetOffset(PxU32 i) const { return mData.mValencies[i].mOffset; } + PX_INLINE const PxU8* GetAdjacentVerts() const { return mData.mAdjacentVerts; } + + PX_INLINE PxU16 GetNbNeighbors(PxU32 i) const { return mData.mValencies[i].mCount; } + PX_INLINE const PxU8* GetNeighbors(PxU32 i) const { return &mData.mAdjacentVerts[mData.mValencies[i].mOffset]; } + +// PX_SERIALIZATION + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); +//~PX_SERIALIZATION + Gu::BigConvexRawData mData; + protected: + void* mVBuffer; + // Internal methods + void CreateOffsets(); + bool VLoad(PxInputStream& stream); + //~Valencies + friend class BigConvexDataBuilder; + }; + +} + +#endif // BIG_CONVEX_DATA_H + diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h new file mode 100644 index 000000000..9756e3de6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h @@ -0,0 +1,59 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_EDGE_FLAGS_H +#define GU_CONVEX_EDGE_FLAGS_H + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + enum ExtraTrigDataFlag + { + ETD_SILHOUETTE_EDGE_01 = (1 << 0), //First edge is a silhouette edge + ETD_SILHOUETTE_EDGE_12 = (1 << 1), //Second edge is a silhouette edge + ETD_SILHOUETTE_EDGE_20 = (1 << 2), //Third edge is a silhouette edge + ETD_CONVEX_EDGE_01 = (1<<3), // PT: important value, don't change + ETD_CONVEX_EDGE_12 = (1<<4), // PT: important value, don't change + ETD_CONVEX_EDGE_20 = (1<<5), // PT: important value, don't change + + ETD_CONVEX_EDGE_ALL = ETD_CONVEX_EDGE_01|ETD_CONVEX_EDGE_12|ETD_CONVEX_EDGE_20 + }; + + // PT: helper function to make sure we use the proper default flags everywhere + PX_FORCE_INLINE PxU8 getConvexEdgeFlags(const PxU8* extraTrigData, PxU32 triangleIndex) + { + return extraTrigData ? extraTrigData[triangleIndex] : PxU8(ETD_CONVEX_EDGE_ALL); + } +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp new file mode 100644 index 000000000..53fee1171 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp @@ -0,0 +1,137 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexHelper.h" +#include "GuGeometryUnion.h" +#include "GuInternal.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +// PT: we can't call alloca in a function and we want to avoid defines or duplicating the code. This makes it a bit tricky to write. +void Gu::getScaledConvex( PxVec3*& scaledVertices, PxU8*& scaledIndices, PxVec3* dstVertices, PxU8* dstIndices, + bool idtConvexScale, const PxVec3* srcVerts, const PxU8* srcIndices, PxU32 nbVerts, const Cm::FastVertex2ShapeScaling& convexScaling) +{ + //pretransform convex polygon if we have scaling! + if(idtConvexScale) // PT: the scale is always 1 for boxes so no need to test the type + { + scaledVertices = const_cast(srcVerts); + scaledIndices = const_cast(srcIndices); + } + else + { + scaledIndices = dstIndices; + scaledVertices = dstVertices; + for(PxU32 i=0; i(); + + const bool idtScale = shapeConvex.scale.isIdentity(); + if(!idtScale) + scaling.init(shapeConvex.scale); + + // PT: this version removes all the FCMPs and almost all LHS. This is temporary until + // the legacy 3x3 matrix totally vanishes but meanwhile do NOT do useless matrix conversions, + // it's a perfect recipe for LHS. + PX_ASSERT(!shapeConvex.hullData->mAABB.isEmpty()); + bounds = shapeConvex.hullData->mAABB.transformFast(scaling.getVertex2ShapeSkew()); + + getPolygonalData_Convex(&polyData, shapeConvex.hullData, scaling); + + // PT: non-uniform scaling invalidates the "internal objects" optimization, since our internal sphere + // might become an ellipsoid or something. Just disable the optimization if scaling is used... + if(!idtScale) + polyData.mInternal.reset(); + + return idtScale; +} + +PxU32 Gu::findUniqueConvexEdges(PxU32 maxNbEdges, ConvexEdge* PX_RESTRICT edges, PxU32 numPolygons, const Gu::HullPolygonData* PX_RESTRICT polygons, const PxU8* PX_RESTRICT vertexData) +{ + PxU32 nbEdges = 0; + + while(numPolygons--) + { + const HullPolygonData& polygon = *polygons++; + const PxU8* vRefBase = vertexData + polygon.mVRef8; + PxU32 numEdges = polygon.mNbVerts; + + PxU32 a = numEdges - 1; + PxU32 b = 0; + while(numEdges--) + { + PxU8 vi0 = vRefBase[a]; + PxU8 vi1 = vRefBase[b]; + + if(vi1 < vi0) + { + PxU8 tmp = vi0; + vi0 = vi1; + vi1 = tmp; + } + + bool found=false; + for(PxU32 i=0;i(PxAlloca(nbVerts * sizeof(PxVec3))), \ + idtScaling ? NULL : reinterpret_cast(PxAlloca(nbVerts * sizeof(PxU8))), \ + idtScaling, srcVerts, srcIndices, nbVerts, scaling); + + PX_PHYSX_COMMON_API bool getConvexData(const Gu::GeometryUnion& shape, Cm::FastVertex2ShapeScaling& scaling, PxBounds3& bounds, PolygonalData& polyData); + + struct ConvexEdge + { + PxU8 vref0; + PxU8 vref1; + PxVec3 normal; // warning: non-unit vector! + }; + + PX_PHYSX_COMMON_API PxU32 findUniqueConvexEdges(PxU32 maxNbEdges, ConvexEdge* PX_RESTRICT edges, PxU32 numPolygons, const Gu::HullPolygonData* PX_RESTRICT polygons, const PxU8* PX_RESTRICT vertexData); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp new file mode 100644 index 000000000..feb66f8da --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp @@ -0,0 +1,415 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVisualizationParameter.h" +#include "PsIntrinsics.h" +#include "CmPhysXCommon.h" +#include "CmRenderOutput.h" +#include "PsMathUtils.h" +#include "GuConvexMesh.h" +#include "GuTriangle32.h" +#include "GuBigConvexData2.h" +#include "GuSerialize.h" +#include "GuMeshFactory.h" +#include "CmUtils.h" +#include "PxMeshScale.h" +#include "PsAllocator.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + +// PX_SERIALIZATION +#include "PsIntrinsics.h" +//~PX_SERIALIZATION + +bool Gu::ConvexMesh::getPolygonData(PxU32 i, PxHullPolygon& data) const +{ + if(i>=mHullData.mNbPolygons) + return false; + + const HullPolygonData& poly = mHullData.mPolygons[i]; + data.mPlane[0] = poly.mPlane.n.x; + data.mPlane[1] = poly.mPlane.n.y; + data.mPlane[2] = poly.mPlane.n.z; + data.mPlane[3] = poly.mPlane.d; + data.mNbVerts = poly.mNbVerts; + data.mIndexBase = poly.mVRef8; + return true; +} + +/// ====================================== + +static void initConvexHullData(Gu::ConvexHullData& data) +{ + data.mAABB.setEmpty(); + data.mCenterOfMass = PxVec3(0); + data.mNbEdges = PxBitAndWord(); + data.mNbHullVertices = 0; + data.mNbPolygons = 0; + data.mPolygons = NULL; + data.mBigConvexRawData = NULL; + data.mInternal.mRadius = 0.0f; + data.mInternal.mExtents[0] = data.mInternal.mExtents[1] = data.mInternal.mExtents[2] = 0.0f; +} + +Gu::ConvexMesh::ConvexMesh() +: PxConvexMesh(PxConcreteType::eCONVEX_MESH, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mNb (0) +, mBigConvexData (NULL) +, mMass (0) +, mInertia (PxMat33(PxIdentity)) +{ + initConvexHullData(mHullData); +} + +Gu::ConvexMesh::ConvexMesh(GuMeshFactory& factory, ConvexHullData& data) +: PxConvexMesh(PxConcreteType::eCONVEX_MESH, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mNb(0) +, mBigConvexData(NULL) +, mMass(0) +, mInertia(PxMat33(PxIdentity)) +, mMeshFactory(&factory) +{ + mHullData = data; +} + +Gu::ConvexMesh::~ConvexMesh() +{ +// PX_SERIALIZATION + if(getBaseFlags()&PxBaseFlag::eOWNS_MEMORY) +//~PX_SERIALIZATION + { + PX_DELETE_POD(mHullData.mPolygons); + PX_DELETE_AND_RESET(mBigConvexData); + } +} + +bool Gu::ConvexMesh::isGpuCompatible() const +{ + return mHullData.mNbHullVertices <= 64 && + mHullData.mPolygons[0].mNbVerts < 32 && + mHullData.mNbEdges.isBitSet(); +} + +// PX_SERIALIZATION +void Gu::ConvexMesh::exportExtraData(PxSerializationContext& stream) +{ + stream.alignData(PX_SERIAL_ALIGN); + const PxU32 bufferSize = computeBufferSize(mHullData, getNb()); + stream.writeData(mHullData.mPolygons, bufferSize); + + if(mBigConvexData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mBigConvexData, sizeof(BigConvexData)); + + mBigConvexData->exportExtraData(stream); + } +} + +void Gu::ConvexMesh::importExtraData(PxDeserializationContext& context) +{ + const PxU32 bufferSize = computeBufferSize(mHullData, getNb()); + mHullData.mPolygons = reinterpret_cast(context.readExtraData(bufferSize)); + + if(mBigConvexData) + { + mBigConvexData = context.readExtraData(); + new(mBigConvexData)BigConvexData(PxEmpty); + mBigConvexData->importExtraData(context); + mHullData.mBigConvexRawData = &mBigConvexData->mData; + } +} + +Gu::ConvexMesh* Gu::ConvexMesh::createObject(PxU8*& address, PxDeserializationContext& context) +{ + ConvexMesh* obj = new (address) ConvexMesh(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(ConvexMesh); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +static bool convexHullLoad(Gu::ConvexHullData& data, PxInputStream& stream, PxBitAndDword& bufferSize) +{ + PxU32 version; + bool Mismatch; + if(!ReadHeader('C', 'L', 'H', 'L', version, Mismatch, stream)) + return false; + + if(!ReadHeader('C', 'V', 'H', 'L', version, Mismatch, stream)) + return false; + + PxU32 Nb; + + // Import figures + { + PxU32 tmp[4]; + ReadDwordBuffer(tmp, 4, Mismatch, stream); + data.mNbHullVertices = Ps::to8(tmp[0]); + data.mNbEdges = Ps::to16(tmp[1]); + data.mNbPolygons = Ps::to8(tmp[2]); + Nb = tmp[3]; + } + + //AM: In practice the old aligner approach wastes 20 bytes and there is no reason to 20 byte align this data. + //I changed the code to just 4 align for the time being. + //On consoles if anything we will need to make this stuff 16 byte align vectors to have any sense, which will have to be done by padding data structures. + PX_ASSERT(sizeof(Gu::HullPolygonData) % sizeof(PxReal) == 0); //otherwise please pad it. + PX_ASSERT(sizeof(PxVec3) % sizeof(PxReal) == 0); + + PxU32 bytesNeeded = computeBufferSize(data, Nb); + + PX_FREE(data.mPolygons); // Load() can be called for an existing convex mesh. In that case we need to free + // the memory first. + + bufferSize = Nb; + void* mDataMemory = PX_ALLOC(bytesNeeded, "ConvexHullData data"); + + PxU8* address = reinterpret_cast(mDataMemory); + + data.mPolygons = reinterpret_cast(address); address += sizeof(Gu::HullPolygonData) * data.mNbPolygons; + PxVec3* mDataHullVertices = reinterpret_cast(address); address += sizeof(PxVec3) * data.mNbHullVertices; + PxU8* mDataFacesByEdges8 = address; address += sizeof(PxU8) * data.mNbEdges * 2; + PxU8* mDataFacesByVertices8 = address; address += sizeof(PxU8) * data.mNbHullVertices * 3; + PxU16* mEdges = reinterpret_cast(address); address += data.mNbEdges.isBitSet() ? (sizeof(PxU16) * data.mNbEdges * 2) : 0; + PxU8* mDataVertexData8 = address; address += sizeof(PxU8) * Nb; // PT: leave that one last, so that we don't need to serialize "Nb" + + PX_ASSERT(!(size_t(mDataHullVertices) % sizeof(PxReal))); + PX_ASSERT(!(size_t(data.mPolygons) % sizeof(PxReal))); + PX_ASSERT(size_t(address)<=size_t(mDataMemory)+bytesNeeded); + + // Import vertices + readFloatBuffer(&mDataHullVertices->x, PxU32(3*data.mNbHullVertices), Mismatch, stream); + + if(version<=6) + { + PxU16 useUnquantizedNormals = readWord(Mismatch, stream); + PX_UNUSED(useUnquantizedNormals); + } + + // Import polygons + stream.read(data.mPolygons, data.mNbPolygons*sizeof(Gu::HullPolygonData)); + + if(Mismatch) + { + for(PxU32 i=0;iLoad(stream); + mHullData.mBigConvexRawData = &mBigConvexData->mData; + } + } + +/* + printf("\n\n"); + printf("COM: %f %f %f\n", massInfo.centerOfMass.x, massInfo.centerOfMass.y, massInfo.centerOfMass.z); + printf("BND: %f %f %f\n", mHullData.aabb.getCenter().x, mHullData.aabb.getCenter().y, mHullData.aabb.getCenter().z); + printf("CNT: %f %f %f\n", mHullData.mCenterxx.x, mHullData.mCenterxx.y, mHullData.mCenterxx.z); + printf("COM-BND: %f BND-CNT: %f, CNT-COM: %f\n", (massInfo.centerOfMass - mHullData.aabb.getCenter()).magnitude(), (mHullData.aabb.getCenter() - mHullData.mCenterxx).magnitude(), (mHullData.mCenterxx - massInfo.centerOfMass).magnitude()); +*/ + +// TEST_INTERNAL_OBJECTS + readFloatBuffer(&mHullData.mInternal.mRadius, 4, mismatch, stream); + + PX_ASSERT(PxVec3(mHullData.mInternal.mExtents[0], mHullData.mInternal.mExtents[1], mHullData.mInternal.mExtents[2]).isFinite()); + PX_ASSERT(mHullData.mInternal.mExtents[0] != 0.0f); + PX_ASSERT(mHullData.mInternal.mExtents[1] != 0.0f); + PX_ASSERT(mHullData.mInternal.mExtents[2] != 0.0f); +//~TEST_INTERNAL_OBJECTS + return true; +} + +void Gu::ConvexMesh::release() +{ + decRefCount(); +} + +void Gu::ConvexMesh::onRefCountZero() +{ + if ((!getBufferSize()) || mMeshFactory->removeConvexMesh(*this)) // when the mesh failed to load properly, it will not have been added to the convex array + { + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, PxConcreteType::eCONVEX_MESH); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::ConvexMesh::release: double deletion detected!"); +} + +void Gu::ConvexMesh::acquireReference() +{ + incRefCount(); +} + +PxU32 Gu::ConvexMesh::getReferenceCount() const +{ + return getRefCount(); +} + +void Gu::ConvexMesh::getMassInformation(PxReal& mass, PxMat33& localInertia, PxVec3& localCenterOfMass) const +{ + mass = Gu::ConvexMesh::getMass(); + localInertia = Gu::ConvexMesh::getInertia(); + localCenterOfMass = Gu::ConvexMesh::getHull().mCenterOfMass; +} + +PxBounds3 Gu::ConvexMesh::getLocalBounds() const +{ + PX_ASSERT(mHullData.mAABB.isValid()); + return PxBounds3::centerExtents(mHullData.mAABB.mCenter, mHullData.mAABB.mExtents); +} diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h new file mode 100644 index 000000000..9a7d08ed6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_COLLISION_CONVEXMESH_H +#define GU_COLLISION_CONVEXMESH_H + +#include "foundation/PxBitAndData.h" +#include "PxConvexMesh.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "CmRefCountable.h" +#include "GuConvexMeshData.h" + +// PX_SERIALIZATION +#include "PxMetaData.h" +#include "CmRenderOutput.h" +//~PX_SERIALIZATION + +namespace physx +{ + +class BigConvexData; +class GuMeshFactory; +class PxMeshScale; + + +namespace Gu +{ + struct HullPolygonData; + + PX_INLINE PxU32 computeBufferSize(const Gu::ConvexHullData& data, PxU32 nb) + { + PxU32 bytesNeeded = sizeof(Gu::HullPolygonData) * data.mNbPolygons; + bytesNeeded += sizeof(PxVec3) * data.mNbHullVertices; + bytesNeeded += sizeof(PxU8) * data.mNbEdges * 2; // mFacesByEdges8 + bytesNeeded += sizeof(PxU8) * data.mNbHullVertices * 3; // mFacesByVertices8; + bytesNeeded += data.mNbEdges.isBitSet() ? (sizeof(PxU16) * data.mNbEdges * 2) : 0; // mEdges; + bytesNeeded += sizeof(PxU8) * nb; // mVertexData8 + + //4 align the whole thing! + const PxU32 mod = bytesNeeded % sizeof(PxReal); + if (mod) + bytesNeeded += sizeof(PxReal) - mod; + return bytesNeeded; + } + + // 0: includes raycast map + // 1: discarded raycast map + // 2: support map not always there + // 3: support stackless trees for non-recursive collision queries + // 4: no more opcode model + // 5: valencies table and gauss map combined, only exported over a vertex count treshold that depends on the platform cooked for. + // 6: removed support for edgeData16. + // 7: removed support for edge8Data. + // 8: removed support for triangles. + + // 9: removed local sphere. + //10: removed geometric center. + //11: removed mFlags, and mERef16 from Poly; nbVerts is just a byte. + //12: removed explicit minimum, maximum from Poly + //13: internal objects + #define PX_CONVEX_VERSION 13 + + class ConvexMesh : public PxConvexMesh, public Ps::UserAllocated, public Cm::RefCountable + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + // PX_SERIALIZATION + PX_PHYSX_COMMON_API ConvexMesh(PxBaseFlags baseFlags) : PxConvexMesh(baseFlags), Cm::RefCountable(PxEmpty), mHullData(PxEmpty), mNb(PxEmpty) + { + mNb.setBit(); + } + + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& stream); + PX_PHYSX_COMMON_API void importExtraData(PxDeserializationContext& context); + PX_PHYSX_COMMON_API virtual void onRefCountZero(); + PX_PHYSX_COMMON_API static ConvexMesh* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); + void resolveReferences(PxDeserializationContext&) {} + virtual void requiresObjects(PxProcessPxBaseCallback&){} + //~PX_SERIALIZATION + PX_PHYSX_COMMON_API ConvexMesh(); + + ConvexMesh(GuMeshFactory& factory, ConvexHullData& data); + + PX_PHYSX_COMMON_API bool load(PxInputStream& stream); + + // PxConvexMesh + PX_PHYSX_COMMON_API virtual void release(); + PX_PHYSX_COMMON_API virtual PxU32 getNbVertices() const { return mHullData.mNbHullVertices; } + PX_PHYSX_COMMON_API virtual const PxVec3* getVertices() const { return mHullData.getHullVertices(); } + PX_PHYSX_COMMON_API virtual const PxU8* getIndexBuffer() const { return mHullData.getVertexData8(); } + PX_PHYSX_COMMON_API virtual PxU32 getNbPolygons() const { return mHullData.mNbPolygons; } + PX_PHYSX_COMMON_API virtual bool getPolygonData(PxU32 i, PxHullPolygon& data) const; + PX_PHYSX_COMMON_API virtual bool isGpuCompatible() const; + PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const; + PX_PHYSX_COMMON_API virtual void acquireReference(); + + PX_PHYSX_COMMON_API virtual void getMassInformation(PxReal& mass, PxMat33& localInertia, PxVec3& localCenterOfMass) const; + PX_PHYSX_COMMON_API virtual PxBounds3 getLocalBounds() const; + //~PxConvexMesh + + PX_FORCE_INLINE PxU32 getNbVerts() const { return mHullData.mNbHullVertices; } + PX_FORCE_INLINE const PxVec3* getVerts() const { return mHullData.getHullVertices(); } + PX_FORCE_INLINE PxU32 getNbPolygonsFast() const { return mHullData.mNbPolygons; } + PX_FORCE_INLINE const HullPolygonData& getPolygon(PxU32 i) const { return mHullData.mPolygons[i]; } + PX_FORCE_INLINE const HullPolygonData* getPolygons() const { return mHullData.mPolygons; } + PX_FORCE_INLINE PxU32 getNbEdges() const { return mHullData.mNbEdges; } + + PX_FORCE_INLINE const ConvexHullData& getHull() const { return mHullData; } + PX_FORCE_INLINE ConvexHullData& getHull() { return mHullData; } + PX_FORCE_INLINE const CenterExtents& getLocalBoundsFast() const { return mHullData.mAABB; } + PX_FORCE_INLINE PxReal getMass() const { return mMass; } + PX_FORCE_INLINE void setMass(PxReal mass) { mMass = mass; } + PX_FORCE_INLINE const PxMat33& getInertia() const { return mInertia; } + PX_FORCE_INLINE void setInertia(const PxMat33& inertia) { mInertia = inertia; } + + PX_FORCE_INLINE BigConvexData* getBigConvexData() const { return mBigConvexData; } + PX_FORCE_INLINE void setBigConvexData(BigConvexData* bcd) { mBigConvexData = bcd; } + + PX_FORCE_INLINE PxU32 getBufferSize() const { return computeBufferSize(mHullData, getNb()); } + + PX_PHYSX_COMMON_API virtual ~ConvexMesh(); + + PX_FORCE_INLINE void setMeshFactory(GuMeshFactory* f) { mMeshFactory = f; } + + PX_FORCE_INLINE void setNb(PxU32 nb) { mNb = nb; } + + protected: + ConvexHullData mHullData; + PxBitAndDword mNb; // ### PT: added for serialization. Try to remove later? + + BigConvexData* mBigConvexData; //!< optional, only for large meshes! PT: redundant with ptr in chull data? Could also be end of other buffer + PxReal mMass; //this is mass assuming a unit density that can be scaled by instances! + PxMat33 mInertia; //in local space of mesh! +private: + GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization + + PX_FORCE_INLINE PxU32 getNb() const { return mNb; } + PX_FORCE_INLINE PxU32 ownsMemory() const { return PxU32(!mNb.isBitSet()); } + }; + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h new file mode 100644 index 000000000..12e311914 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_MESH_DATA_H +#define GU_CONVEX_MESH_DATA_H + +#include "foundation/PxPlane.h" +#include "PsIntrinsics.h" +#include "GuSerialize.h" +#include "GuCenterExtents.h" +#include "foundation/PxBitAndData.h" + +// Data definition + +namespace physx +{ +namespace Gu +{ + struct BigConvexRawData; + + struct HullPolygonData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + // PT: this structure won't be allocated with PX_NEW because polygons aren't allocated alone (with a dedicated alloc). + // Instead they are part of a unique allocation/buffer containing all data for the ConvexHullData class (polygons, followed by + // hull vertices, edge data, etc). As a result, ctors for embedded classes like PxPlane won't be called. + + PxPlane mPlane; //!< Plane equation for this polygon //Could drop 4th elem as it can be computed from any vertex as: d = - p.dot(n); + PxU16 mVRef8; //!< Offset of vertex references in hull vertex data (CS: can we assume indices are tightly packed and offsets are ascending?? DrawObjects makes and uses this assumption) + PxU8 mNbVerts; //!< Number of vertices/edges in the polygon + PxU8 mMinIndex; //!< Index of the polygon vertex that has minimal projection along this plane's normal. + + PX_FORCE_INLINE PxReal getMin(const PxVec3* PX_RESTRICT hullVertices) const //minimum of projection of the hull along this plane normal + { + return mPlane.n.dot(hullVertices[mMinIndex]); + } + + PX_FORCE_INLINE PxReal getMax() const { return -mPlane.d; } //maximum of projection of the hull along this plane normal + }; + + PX_FORCE_INLINE void flipData(Gu::HullPolygonData& data) + { + flip(data.mPlane.n.x); + flip(data.mPlane.n.y); + flip(data.mPlane.n.z); + flip(data.mPlane.d); + flip(data.mVRef8); + } + // PT: if this one breaks, please make sure the 'flipData' function is properly updated. + PX_COMPILE_TIME_ASSERT(sizeof(Gu::HullPolygonData) == 20); + +// TEST_INTERNAL_OBJECTS + struct InternalObjectsData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + PxReal mRadius; + PxReal mExtents[3]; + + PX_FORCE_INLINE void reset() + { + mRadius = 0.0f; + mExtents[0] = 0.0f; + mExtents[1] = 0.0f; + mExtents[2] = 0.0f; + } + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::InternalObjectsData) == 16); +//~TEST_INTERNAL_OBJECTS + + struct ConvexHullData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading + CenterExtents mAABB; //!< bounds TODO: compute this on the fly from first 6 vertices in the vertex array. We'll of course need to sort the most extreme ones to the front. + PxVec3 mCenterOfMass; //in local space of mesh! + + // PT: WARNING: mNbHullVertices *must* appear before mBigConvexRawData for ConvX to be able to do "big raw data" surgery + + PxBitAndWord mNbEdges; //!(mAABB); + } + + PX_FORCE_INLINE const PxVec3* getHullVertices() const //!< Convex hull vertices + { + const char* tmp = reinterpret_cast(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + return reinterpret_cast(tmp); + } + + PX_FORCE_INLINE const PxU8* getFacesByEdges8() const //!< for each edge, gives 2 adjacent polygons; used by convex-convex code to come up with all the convex' edge normals. + { + const char* tmp = reinterpret_cast(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + return reinterpret_cast(tmp); + } + + PX_FORCE_INLINE const PxU8* getFacesByVertices8() const //!< for each edge, gives 2 adjacent polygons; used by convex-convex code to come up with all the convex' edge normals. + { + const char* tmp = reinterpret_cast(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + tmp += sizeof(PxU8) * mNbEdges * 2; + return reinterpret_cast(tmp); + } + + //If we don't build the convex hull with grb data, we will return NULL pointer + PX_FORCE_INLINE const PxU16* getVerticesByEdges16() const //!< Vertex indices indexed by unique edges + { + if (mNbEdges.isBitSet()) + { + const char* tmp = reinterpret_cast(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + tmp += sizeof(PxU8) * mNbEdges * 2; + tmp += sizeof(PxU8) * mNbHullVertices * 3; + return reinterpret_cast(tmp); + } + return NULL; + } + + PX_FORCE_INLINE const PxU8* getVertexData8() const //!< Vertex indices indexed by hull polygons + { + const char* tmp = reinterpret_cast(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + tmp += sizeof(PxU8) * mNbEdges * 2; + tmp += sizeof(PxU8) * mNbHullVertices * 3; + if (mNbEdges.isBitSet()) + tmp += sizeof(PxU16) * mNbEdges * 2; + return reinterpret_cast(tmp); + } + + }; + #if PX_P64_FAMILY + PX_COMPILE_TIME_ASSERT(sizeof(Gu::ConvexHullData) == 72); + #else + PX_COMPILE_TIME_ASSERT(sizeof(Gu::ConvexHullData) == 64); + #endif + + // PT: 'getPaddedBounds()' is only safe if we make sure the bounds member is followed by at least 32bits of data + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(Gu::ConvexHullData, mCenterOfMass)>=PX_OFFSET_OF(Gu::ConvexHullData, mAABB)+4); + +} // namespace Gu + +} + +//#pragma PX_POP_PACK + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp new file mode 100644 index 000000000..564f68cbb --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp @@ -0,0 +1,44 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecBox.h" + +namespace physx +{ + const Ps::aos::BoolV boxVertexTable[8] = { + Ps::aos::BFFFF(),//--- + Ps::aos::BTFFF(),//+-- + Ps::aos::BFTFF(),//-+- + Ps::aos::BTTFF(),//++- + Ps::aos::BFFTF(),//--+ + Ps::aos::BTFTF(),//+-+ + Ps::aos::BFTTF(),//-++ + Ps::aos::BTTTF(),//+++ + }; +} diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h new file mode 100644 index 000000000..1147ec982 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_SUPPORT_TABLE_H +#define GU_CONVEX_SUPPORT_TABLE_H + +#include "GuVecConvex.h" +#include "PsVecTransform.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ +namespace Gu +{ + + class TriangleV; + class CapsuleV; + class BoxV; + class ConvexHullV; + class ConvexHullNoScaleV; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + class SupportLocal + { + public: + Ps::aos::Vec3V shapeSpaceCenterOfMass; + const Ps::aos::PsTransformV& transform; + const Ps::aos::Mat33V& vertex2Shape; + const Ps::aos::Mat33V& shape2Vertex; + const bool isIdentityScale; + + SupportLocal(const Ps::aos::PsTransformV& _transform, const Ps::aos::Mat33V& _vertex2Shape, const Ps::aos::Mat33V& _shape2Vertex, const bool _isIdentityScale = true): transform(_transform), + vertex2Shape(_vertex2Shape), shape2Vertex(_shape2Vertex), isIdentityScale(_isIdentityScale) + { + } + + PX_FORCE_INLINE void setShapeSpaceCenterofMass(const Ps::aos::Vec3VArg _shapeSpaceCenterOfMass) + { + shapeSpaceCenterOfMass = _shapeSpaceCenterOfMass; + } + virtual ~SupportLocal() {} + virtual Ps::aos::Vec3V doSupport(const Ps::aos::Vec3VArg dir) const = 0; + virtual void doSupport(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max) const = 0; + virtual void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts)const = 0; + + protected: + SupportLocal& operator=(const SupportLocal&); + }; +#if PX_VC + #pragma warning(pop) +#endif + + template + class SupportLocalImpl : public SupportLocal + { + + public: + const Convex& conv; + SupportLocalImpl(const Convex& _conv, const Ps::aos::PsTransformV& _transform, const Ps::aos::Mat33V& _vertex2Shape, const Ps::aos::Mat33V& _shape2Vertex, const bool _isIdentityScale = true) : + SupportLocal(_transform, _vertex2Shape, _shape2Vertex, _isIdentityScale), conv(_conv) + { + } + + Ps::aos::Vec3V doSupport(const Ps::aos::Vec3VArg dir) const + { + //return conv.supportVertsLocal(dir); + return conv.supportLocal(dir); + } + + void doSupport(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max) const + { + return conv.supportLocal(dir, min, max); + } + + void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts) const + { + conv.populateVerts(inds, numInds, originalVerts, verts); + } + + protected: + SupportLocalImpl& operator=(const SupportLocalImpl&); + + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp new file mode 100644 index 000000000..851110773 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexUtilsInternal.h" + +#include "foundation/PxBounds3.h" +#include "CmScaling.h" +#include "GuBoxConversion.h" +#include "PxConvexMeshGeometry.h" +#include "GuConvexMesh.h" + +using namespace physx; +using namespace Gu; + +void Gu::computeHullOBB(Box& hullOBB, const PxBounds3& hullAABB, float offset, + const Cm::Matrix34& convexPose, + const Cm::Matrix34& meshPose, const Cm::FastVertex2ShapeScaling& meshScaling, bool idtScaleMesh) +{ + // transform bounds = mesh space + Cm::Matrix34 m0to1 = meshPose.transformTranspose(convexPose); + + hullOBB.extents = hullAABB.getExtents() + PxVec3(offset); + hullOBB.center = m0to1.transform(hullAABB.getCenter()); + hullOBB.rot = m0to1.m; + + if(!idtScaleMesh) + meshScaling.transformQueryBounds(hullOBB.center, hullOBB.extents, hullOBB.rot); +} + +void Gu::computeVertexSpaceOBB(Box& dst, const Box& src, const PxTransform& meshPose, const PxMeshScale& meshScale) +{ + // AP scaffold failure in x64 debug in GuConvexUtilsInternal.cpp + //PX_ASSERT("Performance warning - this path shouldn't execute for identity mesh scale." && !meshScale.isIdentity()); + + dst = transform(meshScale.getInverse() * Cm::Matrix34(meshPose.getInverse()), src); +} + +void Gu::computeOBBAroundConvex( + Box& obb, const PxConvexMeshGeometry& convexGeom, const PxConvexMesh* cm, const PxTransform& convexPose) +{ + const CenterExtents& aabb = static_cast(cm)->getLocalBoundsFast(); + + if(convexGeom.scale.isIdentity()) + { + const PxMat33 m(convexPose.q); + obb = Gu::Box(m.transform(aabb.mCenter) + convexPose.p, aabb.mExtents, m); + } + else + { + obb = transform(Cm::Matrix34(convexPose) * convexGeom.scale.toMat33(), Box(aabb.mCenter, aabb.mExtents, PxMat33(PxIdentity))); + } +} diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h new file mode 100644 index 000000000..d7b2226a8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h @@ -0,0 +1,67 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_UTILS_INTERNALS_H +#define GU_CONVEX_UTILS_INTERNALS_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +class PxMeshScale; +class PxConvexMeshGeometry; +class PxConvexMesh; + +namespace Cm +{ + class Matrix34; + class FastVertex2ShapeScaling; +} + +namespace Gu +{ + class Box; + + void computeHullOBB( + Gu::Box& hullOBB, const PxBounds3& hullAABB, float offset, const Cm::Matrix34& world0, + const Cm::Matrix34& world1, const Cm::FastVertex2ShapeScaling& meshScaling, bool idtScaleMesh); + + // src = input + // computes a box in vertex space (including skewed scale) from src world box + void computeVertexSpaceOBB(Gu::Box& dst, const Gu::Box& src, const PxTransform& meshPose, const PxMeshScale& meshScale); + + PX_PHYSX_COMMON_API void computeOBBAroundConvex( + Gu::Box& obb, const PxConvexMeshGeometry& convexGeom, const PxConvexMesh* cm, const PxTransform& convexPose); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h b/src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h new file mode 100644 index 000000000..2001ad736 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h @@ -0,0 +1,155 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CUBE_INDEX_H +#define GU_CUBE_INDEX_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PsFPU.h" + +namespace physx +{ + + enum CubeIndex + { + CUBE_RIGHT, + CUBE_LEFT, + CUBE_TOP, + CUBE_BOTTOM, + CUBE_FRONT, + CUBE_BACK, + + CUBE_FORCE_DWORD = 0x7fffffff + }; + + /* + It's pretty straightforwards in concept (though the execution in hardware is + a bit crufty and complex). You use a 3D texture coord to look up a texel in + a cube map. First you find which of the axis has the largest value (i.e. + X,Y,Z), and then the sign of that axis decides which face you are going to + use. Which is why the faces are called +X, -X, +Y, -Y, +Z, -Z - after their + principle axis. Then you scale the vector so that the largest value is +/-1. + Then use the other two as 2D coords to look up your texel (with a 0.5 scale + & offset). + + For example, vector (0.4, -0.2, -0.5). Largest value is the Z axis, and it's + -ve, so we're reading from the -Z map. Scale so that this Z axis is +/-1, + and you get the vector (0.8, -0.4, -1.0). So now use the other two values to + look up your texel. So we look up texel (0.8, -0.4). The scale & offset move + the -1->+1 range into the usual 0->1 UV range, so we actually look up texel + (0.9, 0.3). The filtering is extremely complex, especially where three maps + meet, but that's a hardware problem :-) + */ + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Cubemap lookup function. + * + * To transform returned uvs into mapping coordinates : + * u += 1.0f; u *= 0.5f; + * v += 1.0f; v *= 0.5f; + * + * \fn CubemapLookup(const PxVec3& direction, float& u, float& v) + * \param direction [in] a direction vector + * \param u [out] impact coordinate on the unit cube, in [-1,1] + * \param v [out] impact coordinate on the unit cube, in [-1,1] + * \return cubemap texture index + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PX_INLINE CubeIndex CubemapLookup(const PxVec3& direction, float& u, float& v); + + PX_INLINE PxU32 ComputeCubemapOffset(const PxVec3& dir, PxU32 subdiv) + { + float u,v; + const CubeIndex CI = CubemapLookup(dir, u, v); + + // Remap to [0, subdiv[ + const float Coeff = 0.5f * float(subdiv-1); + u += 1.0f; u *= Coeff; + v += 1.0f; v *= Coeff; + + // Compute offset + return PxU32(CI)*(subdiv*subdiv) + PxU32(u)*subdiv + PxU32(v); + } + + + PX_INLINE PxU32 ComputeCubemapNearestOffset(const PxVec3& dir, PxU32 subdiv) + { + float u,v; + const CubeIndex CI = CubemapLookup(dir, u, v); + + // Remap to [0, subdiv] + const float Coeff = 0.5f * float(subdiv-1); + u += 1.0f; u *= Coeff; + v += 1.0f; v *= Coeff; + + // Compute offset + return PxU32(CI)*(subdiv*subdiv) + PxU32(u + 0.5f)*subdiv + PxU32(v + 0.5f); + } + + + PX_INLINE CubeIndex CubemapLookup(const PxVec3& direction, float& u, float& v) + { + const PxU32* binary = reinterpret_cast(&direction.x); + + const PxU32 absPx = binary[0] & ~PX_SIGN_BITMASK; + const PxU32 absNy = binary[1] & ~PX_SIGN_BITMASK; + const PxU32 absNz = binary[2] & ~PX_SIGN_BITMASK; + + PxU32 Index1 = 0; //x biggest axis + PxU32 Index2 = 1; + PxU32 Index3 = 2; + if( (absNy > absPx) & (absNy > absNz)) + { + //y biggest + Index2 = 2; + Index3 = 0; + Index1 = 1; + } + else if(absNz > absPx) + { + //z biggest + Index2 = 0; + Index3 = 1; + Index1 = 2; + } + + const PxF32* data = &direction.x; + const float Coeff = 1.0f / fabsf(data[Index1]); + u = data[Index2] * Coeff; + v = data[Index3] * Coeff; + + const PxU32 Sign = binary[Index1]>>31; + return CubeIndex(Sign|(Index1+Index1)); + } + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp new file mode 100644 index 000000000..cb03c9953 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp @@ -0,0 +1,96 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec3.h" +#include "foundation/PxAssert.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "GuHillClimbing.h" +#include "GuBigConvexData2.h" + +namespace physx +{ + +void localSearch(PxU32& id, const PxVec3& dir, const PxVec3* verts, const Gu::BigConvexRawData* val) +{ + // WARNING: there is a problem on x86 with a naive version of this code, where truncation + // of values from 80 bits to 32 bits as they're stored in memory means that iteratively moving to + // an adjacent vertex of greater support can go into an infinite loop. So we use a version which + // never visits a vertex twice. Note - this might not be enough for GJK, since local + // termination of the support function might not be enough to ensure convergence of GJK itself. + + // if we got here, we'd better have vertices and valencies + PX_ASSERT(verts && val); + + class TinyBitMap + { + public: + PxU32 m[8]; + PX_FORCE_INLINE TinyBitMap() { m[0] = m[1] = m[2] = m[3] = m[4] = m[5] = m[6] = m[7] = 0; } + PX_FORCE_INLINE void set(PxU8 v) { m[v>>5] |= 1<<(v&31); } + PX_FORCE_INLINE bool get(PxU8 v) const { return (m[v>>5] & 1<<(v&31)) != 0; } + }; + + TinyBitMap visited; + + const Gu::Valency* Valencies = val->mValencies; + const PxU8* Adj = val->mAdjacentVerts; + + PX_ASSERT(Valencies && Adj); + + // Get the initial value and the initial vertex + float MaxVal = dir.dot(verts[id]); + PxU32 NextVtx = id; + + do + { + PxU16 NbNeighbors = Valencies[NextVtx].mCount; + const PxU8* Run = Adj + Valencies[NextVtx].mOffset; + id = NextVtx; + while(NbNeighbors--) + { + const PxU8 Neighbor = *Run++; + + if(!visited.get(Neighbor)) + { + visited.set(Neighbor); + + const float CurVal = dir.dot(verts[Neighbor]); + + if(CurVal>MaxVal) + { + MaxVal = CurVal; + NextVtx = Neighbor; + } + } + } + } while(NextVtx!=id); +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h new file mode 100644 index 000000000..0db865e86 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HILL_CLIMBING_H +#define GU_HILL_CLIMBING_H + +#include "Ps.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ + namespace Gu + { + struct BigConvexRawData; + } + + void localSearch(PxU32& id, const PxVec3& dir, const PxVec3* verts, const Gu::BigConvexRawData* val); +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp new file mode 100644 index 000000000..4b089626d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp @@ -0,0 +1,516 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuShapeConvex.h" +#include "GuBigConvexData.h" +#include "GuEdgeListData.h" +#include "GuInternal.h" + +#include "CmMatrix34.h" +#include "GuHillClimbing.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +static PX_FORCE_INLINE PxU32 selectClosestPolygon(PxReal& maxDp_, PxU32 numPolygons, const Gu::HullPolygonData* polys, const PxVec3& axis) +{ + float maxDp = polys[0].mPlane.n.dot(axis); + PxU32 closest = 0; + + // Loop through polygons + for(PxU32 i=1; i maxDp) + { + maxDp = dp; + closest = i; + } + } + maxDp_ = maxDp; + return closest; +} + +static PxU32 SelectClosestEdgeCB_Convex(const PolygonalData& data, const Cm::FastVertex2ShapeScaling& scaling, const PxVec3& localSpaceDirection) +{ + //vertex1TOShape1Skew is a symmetric matrix. + //it has the property that (vertex1TOShape1Skew * v)|localSpaceDirection == (vertex1TOShape1Skew * localSpaceDirection)|v + const PxVec3 vertexSpaceDirection = scaling * localSpaceDirection; + + const Gu::HullPolygonData* PX_RESTRICT polys = data.mPolygons; + + PxReal maxDp; + // ##might not be needed + PxU32 closest = ::selectClosestPolygon(maxDp, data.mNbPolygons, polys, vertexSpaceDirection); + + // Since the convex is closed, at least some poly must satisfy this + PX_ASSERT(maxDp>=0); + + const PxU32 numEdges = data.mNbEdges; + const PxU8* const edgeToFace = data.mFacesByEdges; + + //Loop through edges + PxU32 closestEdge = 0xffffffff; + PxReal maxDpSq = maxDp * maxDp; + for(PxU32 i=0; i < numEdges; i++) + { + const PxU8 f0 = edgeToFace[i*2]; + const PxU8 f1 = edgeToFace[i*2+1]; + + // unnormalized edge normal + const PxVec3 edgeNormal = polys[f0].mPlane.n + polys[f1].mPlane.n; + const PxReal enMagSq = edgeNormal.magnitudeSquared(); + //Test normal of current edge - squared test is valid if dp and maxDp both >= 0 + const float dp = edgeNormal.dot(vertexSpaceDirection); + if(dp>=0.0f && dp*dp>maxDpSq*enMagSq) + { + maxDpSq = dp*dp/enMagSq; + closestEdge = i; + } + } + + if(closestEdge!=0xffffffff) + { + const PxU8* FBE = edgeToFace; + + const PxU32 f0 = FBE[closestEdge*2]; + const PxU32 f1 = FBE[closestEdge*2+1]; + + const PxReal dp0 = polys[f0].mPlane.n.dot(vertexSpaceDirection); + const PxReal dp1 = polys[f1].mPlane.n.dot(vertexSpaceDirection); + if(dp0>dp1) + closest = f0; + else + closest = f1; + } + return closest; +} + +// Hull projection callback for "small" hulls +static void HullProjectionCB_SmallConvex(const PolygonalData& data, const PxVec3& dir, + const Cm::Matrix34& world, + const Cm::FastVertex2ShapeScaling& scaling, + PxReal& min, PxReal& max) +{ + const PxVec3 localSpaceDirection = world.rotateTranspose(dir); + //vertex1TOShape1Skew is a symmetric matrix. + //it has the property that (vertex1TOShape1Skew * v)|localSpaceDirection == (vertex1TOShape1Skew * localSpaceDirection)|v + const PxVec3 vertexSpaceDirection = scaling * localSpaceDirection; + + // PT: prevents aliasing + PxReal minimum = PX_MAX_REAL; + PxReal maximum = -PX_MAX_REAL; + + //brute-force, localspace + { + const PxVec3* PX_RESTRICT verts = data.mVerts; + PxU32 numVerts = data.mNbVerts; + while(numVerts--) + { + const PxReal dp = (*verts++).dot(vertexSpaceDirection); + minimum = physx::intrinsics::selectMin(minimum, dp); + maximum = physx::intrinsics::selectMax(maximum, dp); + } + + } + + const PxReal offset = world.p.dot(dir); + min = minimum + offset; + max = maximum + offset; +} + +static PxU32 computeNearestOffset(const PxU32 subdiv, const PxVec3& dir) +{ + // ComputeCubemapNearestOffset(const Point& dir, udword subdiv) + + // PT: ok so why exactly was the code duplicated here? + // PxU32 CI = CubemapLookup(dir,u,v) + + PxU32 index; + PxReal coeff; + // find largest axis + PxReal absNx = PxAbs(dir.x); + PxReal absNy = PxAbs(dir.y); + PxReal absNz = PxAbs(dir.z); + + if( absNy > absNx && + absNy > absNz) + { + //y biggest + index = 1; + coeff = 1.0f/absNy; + } + else if(absNz > absNx) + { + index = 2; + coeff = 1.0f/absNz; + } + else + { + index = 0; + coeff = 1.0f/absNx; + } + + union + { + PxU32 aU32; + PxReal aFloat; + } conv; + + conv.aFloat = dir[index]; + PxU32 sign = conv.aU32>>31; + + const PxU32 index2 = Ps::getNextIndex3(index); + const PxU32 index3 = Ps::getNextIndex3(index2); + PxReal u = dir[index2] * coeff; + PxReal v = dir[index3] * coeff; + + PxU32 CI = (sign | (index+index)); + + //Remap to [0, subdiv[ + coeff = 0.5f * PxReal(subdiv-1); + u += 1.0f; u *= coeff; + v += 1.0f; v *= coeff; + + //Round to nearest + PxU32 ui = PxU32(u); + PxU32 vi = PxU32(v); + + PxReal du = u - PxReal(ui); + PxReal dv = v - PxReal(vi); + if(du>0.5f) ui++; + if(dv>0.5f) vi++; + + //Compute offset + return CI*(subdiv*subdiv) + ui*subdiv + vi; +} + +// Hull projection callback for "big" hulls +static void HullProjectionCB_BigConvex(const PolygonalData& data, const PxVec3& dir, const Cm::Matrix34& world, const Cm::FastVertex2ShapeScaling& scaling, PxReal& minimum, PxReal& maximum) +{ + const PxVec3* PX_RESTRICT verts = data.mVerts; + + const PxVec3 localSpaceDirection = world.rotateTranspose(dir); + //vertex1TOShape1Skew is a symmetric matrix. + //it has the property that (vertex1TOShape1Skew * v)|localSpaceDirection == (vertex1TOShape1Skew * localSpaceDirection)|v + const PxVec3 vertexSpaceDirection = scaling * localSpaceDirection; //NB: triangles are always shape 1! eek! + + // This version is better for objects with a lot of vertices + const Gu::BigConvexRawData* bigData = data.mBigData; + PxU32 minID = 0, maxID = 0; + { + const PxU32 offset = computeNearestOffset(bigData->mSubdiv, -vertexSpaceDirection); + minID = bigData->mSamples[offset]; + maxID = bigData->getSamples2()[offset]; + } + + // Do hillclimbing! + localSearch(minID, -vertexSpaceDirection, verts, bigData); + localSearch(maxID, vertexSpaceDirection, verts, bigData); + + const PxReal offset = world.p.dot(dir); + minimum = offset + verts[minID].dot(vertexSpaceDirection); + maximum = offset + verts[maxID].dot(vertexSpaceDirection); + PX_ASSERT(maximum >= minimum); +} + +void Gu::getPolygonalData_Convex(PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Cm::FastVertex2ShapeScaling& scaling) +{ + dst->mCenter = scaling * src->mCenterOfMass; + dst->mNbVerts = src->mNbHullVertices; + dst->mNbPolygons = src->mNbPolygons; + dst->mNbEdges = src->mNbEdges; + dst->mPolygons = src->mPolygons; + dst->mVerts = src->getHullVertices(); + dst->mPolygonVertexRefs = src->getVertexData8(); + dst->mFacesByEdges = src->getFacesByEdges8(); + +// TEST_INTERNAL_OBJECTS + dst->mInternal = src->mInternal; +//~TEST_INTERNAL_OBJECTS + + dst->mBigData = src->mBigConvexRawData; + + // This threshold test doesnt cost much and many customers cook on PC and use this on 360. + // 360 has a much higher threshold than PC(and it makes a big difference) + // PT: the cool thing is that this test is now done once by contact generation call, not once by hull projection + if(!src->mBigConvexRawData) + dst->mProjectHull = HullProjectionCB_SmallConvex; + else + dst->mProjectHull = HullProjectionCB_BigConvex; + dst->mSelectClosestEdgeCB = SelectClosestEdgeCB_Convex; +} + +// Box emulating convex mesh + +// Face0: 0-1-2-3 +// Face1: 1-5-6-2 +// Face2: 5-4-7-6 +// Face3: 4-0-3-7 +// Face4; 3-2-6-7 +// Face5: 4-5-1-0 + +// 7+------+6 0 = --- +// /| /| 1 = +-- +// / | / | 2 = ++- +// / 4+---/--+5 3 = -+- +// 3+------+2 / y z 4 = --+ +// | / | / | / 5 = +-+ +// |/ |/ |/ 6 = +++ +// 0+------+1 *---x 7 = -++ + +static const PxU8 gPxcBoxPolygonData[] = { + 0, 1, 2, 3, + 1, 5, 6, 2, + 5, 4, 7, 6, + 4, 0, 3, 7, + 3, 2, 6, 7, + 4, 5, 1, 0, +}; + +#define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) +static PxVec3 gPxcBoxEdgeNormals[] = +{ + PxVec3(0, -INVSQRT2, -INVSQRT2), // 0-1 + PxVec3(INVSQRT2, 0, -INVSQRT2), // 1-2 + PxVec3(0, INVSQRT2, -INVSQRT2), // 2-3 + PxVec3(-INVSQRT2, 0, -INVSQRT2), // 3-0 + + PxVec3(0, INVSQRT2, INVSQRT2), // 7-6 + PxVec3(INVSQRT2, 0, INVSQRT2), // 6-5 + PxVec3(0, -INVSQRT2, INVSQRT2), // 5-4 + PxVec3(-INVSQRT2, 0, INVSQRT2), // 4-7 + + PxVec3(INVSQRT2, -INVSQRT2, 0), // 1-5 + PxVec3(INVSQRT2, INVSQRT2, 0), // 6-2 + PxVec3(-INVSQRT2, INVSQRT2, 0), // 3-7 + PxVec3(-INVSQRT2, -INVSQRT2, 0) // 4-0 +}; +#undef INVSQRT2 + +// ### needs serious checkings + // Flags(16), Count(16), Offset(32); +static Gu::EdgeDescData gPxcBoxEdgeDesc[] = { + {Gu::PX_EDGE_ACTIVE, 2, 0}, + {Gu::PX_EDGE_ACTIVE, 2, 2}, + {Gu::PX_EDGE_ACTIVE, 2, 4}, + {Gu::PX_EDGE_ACTIVE, 2, 6}, + {Gu::PX_EDGE_ACTIVE, 2, 8}, + {Gu::PX_EDGE_ACTIVE, 2, 10}, + {Gu::PX_EDGE_ACTIVE, 2, 12}, + {Gu::PX_EDGE_ACTIVE, 2, 14}, + {Gu::PX_EDGE_ACTIVE, 2, 16}, + {Gu::PX_EDGE_ACTIVE, 2, 18}, + {Gu::PX_EDGE_ACTIVE, 2, 20}, + {Gu::PX_EDGE_ACTIVE, 2, 22}, +}; + +// ### needs serious checkings +static PxU8 gPxcBoxFaceByEdge[] = { + 0,5, // Edge 0-1 + 0,1, // Edge 1-2 + 0,4, // Edge 2-3 + 0,3, // Edge 3-0 + 2,4, // Edge 7-6 + 1,2, // Edge 6-5 + 2,5, // Edge 5-4 + 2,3, // Edge 4-7 + 1,5, // Edge 1-5 + 1,4, // Edge 6-2 + 3,4, // Edge 3-7 + 3,5, // Edge 4-0 +}; + +static PxU32 SelectClosestEdgeCB_Box(const PolygonalData& data, const Cm::FastVertex2ShapeScaling& scaling, const PxVec3& localDirection) +{ + PX_UNUSED(scaling); + + PxReal maxDp; + // ##might not be needed + PxU32 closest = ::selectClosestPolygon(maxDp, 6, data.mPolygons, localDirection); + + PxU32 numEdges = 12; + const PxVec3* PX_RESTRICT edgeNormals = gPxcBoxEdgeNormals; + + //Loop through edges + PxU32 closestEdge = 0xffffffff; + for(PxU32 i=0; i < numEdges; i++) + { + //Test normal of current edge + const float dp = edgeNormals[i].dot(localDirection); + if(dp>maxDp) + { + maxDp = dp; + closestEdge = i; + } + } + + if(closestEdge!=0xffffffff) + { + const Gu::EdgeDescData* PX_RESTRICT ED = gPxcBoxEdgeDesc; + const PxU8* PX_RESTRICT FBE = gPxcBoxFaceByEdge; + + PX_ASSERT(ED[closestEdge].Count==2); + const PxU32 f0 = FBE[ED[closestEdge].Offset]; + const PxU32 f1 = FBE[ED[closestEdge].Offset+1]; + + const PxReal dp0 = data.mPolygons[f0].mPlane.n.dot(localDirection); + const PxReal dp1 = data.mPolygons[f1].mPlane.n.dot(localDirection); + if(dp0>dp1) + closest = f0; + else + closest = f1; + } + + return closest; +} + +static PX_FORCE_INLINE void projectBox(PxVec3& p, const PxVec3& localDir, const PxVec3& extents) +{ + // PT: the original code didn't have branches or FPU comparisons. Why rewrite it ? +// p.x = (localDir.x >= 0) ? extents.x : -extents.x; +// p.y = (localDir.y >= 0) ? extents.y : -extents.y; +// p.z = (localDir.z >= 0) ? extents.z : -extents.z; + p.x = physx::intrinsics::fsel(localDir.x, extents.x, -extents.x); + p.y = physx::intrinsics::fsel(localDir.y, extents.y, -extents.y); + p.z = physx::intrinsics::fsel(localDir.z, extents.z, -extents.z); +} + +static void HullProjectionCB_Box(const PolygonalData& data, const PxVec3& dir, const Cm::Matrix34& world, const Cm::FastVertex2ShapeScaling& scaling, PxReal& minimum, PxReal& maximum) +{ + PX_UNUSED(scaling); + + const PxVec3 localDir = world.rotateTranspose(dir); + + PxVec3 p; + projectBox(p, localDir, *data.mHalfSide); + + const PxReal offset = world.p.dot(dir); + const PxReal tmp = p.dot(localDir); + maximum = offset + tmp; + minimum = offset - tmp; +} + +PolygonalBox::PolygonalBox(const PxVec3& halfSide) : mHalfSide(halfSide) +{ + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + PxVec3 minimum = -mHalfSide; + PxVec3 maximum = mHalfSide; + // Generate 8 corners of the bbox + mVertices[0] = PxVec3(minimum.x, minimum.y, minimum.z); + mVertices[1] = PxVec3(maximum.x, minimum.y, minimum.z); + mVertices[2] = PxVec3(maximum.x, maximum.y, minimum.z); + mVertices[3] = PxVec3(minimum.x, maximum.y, minimum.z); + mVertices[4] = PxVec3(minimum.x, minimum.y, maximum.z); + mVertices[5] = PxVec3(maximum.x, minimum.y, maximum.z); + mVertices[6] = PxVec3(maximum.x, maximum.y, maximum.z); + mVertices[7] = PxVec3(minimum.x, maximum.y, maximum.z); + + //Setup the polygons + for(PxU8 i=0; i < 6; i++) + { + mPolygons[i].mNbVerts = 4; + mPolygons[i].mVRef8 = PxU16(i*4); + } + + // ### planes needs *very* careful checks + // X axis + mPolygons[1].mPlane.n = PxVec3(1.0f, 0.0f, 0.0f); + mPolygons[1].mPlane.d = -mHalfSide.x; + mPolygons[3].mPlane.n = PxVec3(-1.0f, 0.0f, 0.0f); + mPolygons[3].mPlane.d = -mHalfSide.x; + + mPolygons[1].mMinIndex = 0; + mPolygons[3].mMinIndex = 1; + +// mPolygons[1].mMinObsolete = -mHalfSide.x; +// mPolygons[3].mMinObsolete = -mHalfSide.x; + + PX_ASSERT(mPolygons[1].getMin(mVertices) == -mHalfSide.x); + PX_ASSERT(mPolygons[3].getMin(mVertices) == -mHalfSide.x); + + + // Y axis + mPolygons[4].mPlane.n = PxVec3(0.f, 1.0f, 0.0f); + mPolygons[4].mPlane.d = -mHalfSide.y; + mPolygons[5].mPlane.n = PxVec3(0.0f, -1.0f, 0.0f); + mPolygons[5].mPlane.d = -mHalfSide.y; + + mPolygons[4].mMinIndex = 0; + mPolygons[5].mMinIndex = 2; +// mPolygons[4].mMinObsolete = -mHalfSide.y; +// mPolygons[5].mMinObsolete = -mHalfSide.y; + + PX_ASSERT(mPolygons[4].getMin(mVertices) == -mHalfSide.y); + PX_ASSERT(mPolygons[5].getMin(mVertices) == -mHalfSide.y); + + // Z axis + mPolygons[2].mPlane.n = PxVec3(0.f, 0.0f, 1.0f); + mPolygons[2].mPlane.d = -mHalfSide.z; + mPolygons[0].mPlane.n = PxVec3(0.0f, 0.0f, -1.0f); + mPolygons[0].mPlane.d = -mHalfSide.z; + + mPolygons[2].mMinIndex = 0; + mPolygons[0].mMinIndex = 4; +// mPolygons[2].mMinObsolete = -mHalfSide.z; +// mPolygons[0].mMinObsolete = -mHalfSide.z; + PX_ASSERT(mPolygons[2].getMin(mVertices) == -mHalfSide.z); + PX_ASSERT(mPolygons[0].getMin(mVertices) == -mHalfSide.z); +} + +void PolygonalBox::getPolygonalData(PolygonalData* PX_RESTRICT dst) const +{ + dst->mCenter = PxVec3(0.0f, 0.0f, 0.0f); + dst->mNbVerts = 8; + dst->mNbPolygons = 6; + dst->mPolygons = mPolygons; + dst->mNbEdges = 0; + dst->mVerts = mVertices; + dst->mPolygonVertexRefs = gPxcBoxPolygonData; + dst->mFacesByEdges = NULL; + dst->mInternal.mRadius = 0.0f; + dst->mInternal.mExtents[0] = 0.0f; + dst->mInternal.mExtents[1] = 0.0f; + dst->mInternal.mExtents[2] = 0.0f; +// dst->mBigData = NULL; + dst->mHalfSide = &mHalfSide; + dst->mProjectHull = HullProjectionCB_Box; + dst->mSelectClosestEdgeCB = SelectClosestEdgeCB_Box; +} diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h new file mode 100644 index 000000000..8d6f81d0b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h @@ -0,0 +1,100 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SHAPECONVEX_H +#define GU_SHAPECONVEX_H + +#include "GuConvexMeshData.h" +#include "CmScaling.h" + +namespace physx +{ +namespace Gu +{ + struct PolygonalData; + typedef void (*HullPrefetchCB) (PxU32 numVerts, const PxVec3* PX_RESTRICT verts); + typedef void (*HullProjectionCB) (const PolygonalData& data, const PxVec3& dir, const Cm::Matrix34& world2hull, const Cm::FastVertex2ShapeScaling& scaling, PxReal& minimum, PxReal& maximum); + typedef PxU32 (*SelectClosestEdgeCB) (const PolygonalData& data, const Cm::FastVertex2ShapeScaling& scaling, const PxVec3& localDirection); + + struct PolygonalData + { + // Data + PxVec3 mCenter; + PxU32 mNbVerts; + PxU32 mNbPolygons; + PxU32 mNbEdges; + const Gu::HullPolygonData* mPolygons; + const PxVec3* mVerts; + const PxU8* mPolygonVertexRefs; + const PxU8* mFacesByEdges; + const PxU16* mVerticesByEdges; + + Gu::InternalObjectsData mInternal; + union + { + const Gu::BigConvexRawData* mBigData; // Only for big convexes + const PxVec3* mHalfSide; // Only for boxes + }; + + // Code + HullProjectionCB mProjectHull; + SelectClosestEdgeCB mSelectClosestEdgeCB; + + PX_FORCE_INLINE const PxU8* getPolygonVertexRefs(const Gu::HullPolygonData& poly) const + { + return mPolygonVertexRefs + poly.mVRef8; + } + }; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + class PX_PHYSX_COMMON_API PolygonalBox + { + public: + PolygonalBox(const PxVec3& halfSide); + + void getPolygonalData(PolygonalData* PX_RESTRICT dst) const; + + const PxVec3& mHalfSide; + PxVec3 mVertices[8]; + Gu::HullPolygonData mPolygons[6]; + private: + PolygonalBox& operator=(const PolygonalBox&); + }; +#if PX_VC + #pragma warning(pop) +#endif + + void getPolygonalData_Convex(PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Cm::FastVertex2ShapeScaling& scaling); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp new file mode 100644 index 000000000..e564e083e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuDistancePointBox.h" + +using namespace physx; + +PxReal Gu::distancePointBoxSquared( const PxVec3& point, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxVec3* boxParam) +{ + // Compute coordinates of point in box coordinate system + const PxVec3 diff = point - boxOrigin; + + PxVec3 closest( boxBase.column0.dot(diff), + boxBase.column1.dot(diff), + boxBase.column2.dot(diff)); + + // Project test point onto box + PxReal sqrDistance = 0.0f; + for(PxU32 ax=0; ax<3; ax++) + { + if(closest[ax] < -boxExtent[ax]) + { + const PxReal delta = closest[ax] + boxExtent[ax]; + sqrDistance += delta*delta; + closest[ax] = -boxExtent[ax]; + } + else if(closest[ax] > boxExtent[ax]) + { + const PxReal delta = closest[ax] - boxExtent[ax]; + sqrDistance += delta*delta; + closest[ax] = boxExtent[ax]; + } + } + + if(boxParam) *boxParam = closest; + + return sqrDistance; +} diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h new file mode 100644 index 000000000..8ebcc3e51 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_BOX_H +#define GU_DISTANCE_POINT_BOX_H + +#include "GuBox.h" + +namespace physx +{ +namespace Gu +{ + /** + Return the square of the minimum distance from the surface of the box to the given point. + \param point The point + \param boxOrigin The origin of the box + \param boxExtent The extent of the box + \param boxBase The orientation of the box + \param boxParam Set to coordinates of the closest point on the box in its local space + */ + PxReal distancePointBoxSquared( const PxVec3& point, + const PxVec3& boxOrigin, + const PxVec3& boxExtent, + const PxMat33& boxBase, + PxVec3* boxParam=NULL); + + /** + Return the square of the minimum distance from the surface of the box to the given point. + \param point The point + \param box The box + \param boxParam Set to coordinates of the closest point on the box in its local space + */ + PX_FORCE_INLINE PxReal distancePointBoxSquared( const PxVec3& point, + const Gu::Box& box, + PxVec3* boxParam=NULL) + { + return distancePointBoxSquared(point, box.center, box.extents, box.rot, boxParam); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h new file mode 100644 index 000000000..a441cde18 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_SEGMENT_H +#define GU_DISTANCE_POINT_SEGMENT_H + +#include "PxPhysXCommonConfig.h" +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + // dir = p1 - p0 + PX_FORCE_INLINE PxReal distancePointSegmentSquaredInternal(const PxVec3& p0, const PxVec3& dir, const PxVec3& point, PxReal* param=NULL) + { + PxVec3 diff = point - p0; + PxReal fT = diff.dot(dir); + + if(fT<=0.0f) + { + fT = 0.0f; + } + else + { + const PxReal sqrLen = dir.magnitudeSquared(); + if(fT>=sqrLen) + { + fT = 1.0f; + diff -= dir; + } + else + { + fT /= sqrLen; + diff -= fT*dir; + } + } + + if(param) + *param = fT; + + return diff.magnitudeSquared(); + } + + /** + A segment is defined by S(t) = mP0 * (1 - t) + mP1 * t, with 0 <= t <= 1 + Alternatively, a segment is S(t) = Origin + t * Direction for 0 <= t <= 1. + Direction is not necessarily unit length. The end points are Origin = mP0 and Origin + Direction = mP1. + */ + PX_FORCE_INLINE PxReal distancePointSegmentSquared(const PxVec3& p0, const PxVec3& p1, const PxVec3& point, PxReal* param=NULL) + { + return distancePointSegmentSquaredInternal(p0, p1 - p0, point, param); + } + + PX_INLINE PxReal distancePointSegmentSquared(const Gu::Segment& segment, const PxVec3& point, PxReal* param=NULL) + { + return distancePointSegmentSquared(segment.p0, segment.p1, point, param); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp new file mode 100644 index 000000000..b86a49bf8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp @@ -0,0 +1,353 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec3.h" +#include "GuDistancePointTriangle.h" +#include "GuDistancePointTriangleSIMD.h" + +using namespace physx; + +// Based on Christer Ericson's book +PxVec3 Gu::closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t) +{ + // Check if P in vertex region outside A + const PxVec3 ab = b - a; + const PxVec3 ac = c - a; + const PxVec3 ap = p - a; + const float d1 = ab.dot(ap); + const float d2 = ac.dot(ap); + if(d1<=0.0f && d2<=0.0f) + { + s = 0.0f; + t = 0.0f; + return a; // Barycentric coords 1,0,0 + } + + // Check if P in vertex region outside B + const PxVec3 bp = p - b; + const float d3 = ab.dot(bp); + const float d4 = ac.dot(bp); + if(d3>=0.0f && d4<=d3) + { + s = 1.0f; + t = 0.0f; + return b; // Barycentric coords 0,1,0 + } + + // Check if P in edge region of AB, if so return projection of P onto AB + const float vc = d1*d4 - d3*d2; + if(vc<=0.0f && d1>=0.0f && d3<=0.0f) + { + const float v = d1 / (d1 - d3); + s = v; + t = 0.0f; + return a + v * ab; // barycentric coords (1-v, v, 0) + } + + // Check if P in vertex region outside C + const PxVec3 cp = p - c; + const float d5 = ab.dot(cp); + const float d6 = ac.dot(cp); + if(d6>=0.0f && d5<=d6) + { + s = 0.0f; + t = 1.0f; + return c; // Barycentric coords 0,0,1 + } + + // Check if P in edge region of AC, if so return projection of P onto AC + const float vb = d5*d2 - d1*d6; + if(vb<=0.0f && d2>=0.0f && d6<=0.0f) + { + const float w = d2 / (d2 - d6); + s = 0.0f; + t = w; + return a + w * ac; // barycentric coords (1-w, 0, w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + const float va = d3*d6 - d5*d4; + if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f) + { + const float w = (d4-d3) / ((d4 - d3) + (d5-d6)); + s = 1.0f-w; + t = w; + return b + w * (c-b); // barycentric coords (0, 1-w, w) + } + + // P inside face region. Compute Q through its barycentric coords (u,v,w) + const float denom = 1.0f / (va + vb + vc); + const float v = vb * denom; + const float w = vc * denom; + s = v; + t = w; + return a + ab*v + ac*w; +} + +//Ps::aos::FloatV Gu::distancePointTriangleSquared( const Ps::aos::Vec3VArg p, +// const Ps::aos::Vec3VArg a, +// const Ps::aos::Vec3VArg b, +// const Ps::aos::Vec3VArg c, +// Ps::aos::FloatV& u, +// Ps::aos::FloatV& v, +// Ps::aos::Vec3V& closestP) +//{ +// using namespace Ps::aos; +// +// const FloatV zero = FZero(); +// const FloatV one = FOne(); +// //const Vec3V zero = V3Zero(); +// const Vec3V ab = V3Sub(b, a); +// const Vec3V ac = V3Sub(c, a); +// const Vec3V bc = V3Sub(c, b); +// const Vec3V ap = V3Sub(p, a); +// const Vec3V bp = V3Sub(p, b); +// const Vec3V cp = V3Sub(p, c); +// +// const FloatV d1 = V3Dot(ab, ap); // snom +// const FloatV d2 = V3Dot(ac, ap); // tnom +// const FloatV d3 = V3Dot(ab, bp); // -sdenom +// const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 +// const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 +// const FloatV d6 = V3Dot(ac, cp); // -tdenom +// const FloatV unom = FSub(d4, d3); +// const FloatV udenom = FSub(d5, d6); +// +// //check if p in vertex region outside a +// const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 +// const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 +// const BoolV con0 = BAnd(con00, con01); // vertex region a +// const FloatV u0 = zero; +// const FloatV v0 = zero; +// +// //check if p in vertex region outside b +// const BoolV con10 = FIsGrtrOrEq(d3, zero); +// const BoolV con11 = FIsGrtrOrEq(d3, d4); +// const BoolV con1 = BAnd(con10, con11); // vertex region b +// const FloatV u1 = one; +// const FloatV v1 = zero; +// +// //check if p in vertex region outside c +// const BoolV con20 = FIsGrtrOrEq(d6, zero); +// const BoolV con21 = FIsGrtrOrEq(d6, d5); +// const BoolV con2 = BAnd(con20, con21); // vertex region c +// const FloatV u2 = zero; +// const FloatV v2 = one; +// +// //check if p in edge region of AB +// const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); +// +// const BoolV con30 = FIsGrtr(zero, vc); +// const BoolV con31 = FIsGrtrOrEq(d1, zero); +// const BoolV con32 = FIsGrtr(zero, d3); +// const BoolV con3 = BAnd(con30, BAnd(con31, con32)); +// const FloatV sScale = FDiv(d1, FSub(d1, d3)); +// const Vec3V closest3 = V3Add(a, V3Scale(ab, sScale)); +// const FloatV u3 = sScale; +// const FloatV v3 = zero; +// +// //check if p in edge region of BC +// const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); +// const BoolV con40 = FIsGrtr(zero, va); +// const BoolV con41 = FIsGrtrOrEq(d4, d3); +// const BoolV con42 = FIsGrtrOrEq(d5, d6); +// const BoolV con4 = BAnd(con40, BAnd(con41, con42)); +// const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); +// const Vec3V closest4 = V3Add(b, V3Scale(bc, uScale)); +// const FloatV u4 = FSub(one, uScale); +// const FloatV v4 = uScale; +// +// //check if p in edge region of AC +// const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); +// const BoolV con50 = FIsGrtr(zero, vb); +// const BoolV con51 = FIsGrtrOrEq(d2, zero); +// const BoolV con52 = FIsGrtr(zero, d6); +// const BoolV con5 = BAnd(con50, BAnd(con51, con52)); +// const FloatV tScale = FDiv(d2, FSub(d2, d6)); +// const Vec3V closest5 = V3Add(a, V3Scale(ac, tScale)); +// const FloatV u5 = zero; +// const FloatV v5 = tScale; +// +// //P must project inside face region. Compute Q using Barycentric coordinates +// const FloatV denom = FRecip(FAdd(va, FAdd(vb, vc))); +// const FloatV t = FMul(vb, denom); +// const FloatV w = FMul(vc, denom); +// const Vec3V bCom = V3Scale(ab, t); +// const Vec3V cCom = V3Scale(ac, w); +// const Vec3V closest6 = V3Add(a, V3Add(bCom, cCom)); +// const FloatV u6 = t; +// const FloatV v6 = w; +// +// const Vec3V closest= V3Sel(con0, a, V3Sel(con1, b, V3Sel(con2, c, V3Sel(con3, closest3, V3Sel(con4, closest4, V3Sel(con5, closest5, closest6)))))); +// u = FSel(con0, u0, FSel(con1, u1, FSel(con2, u2, FSel(con3, u3, FSel(con4, u4, FSel(con5, u5, u6)))))); +// v = FSel(con0, v0, FSel(con1, v1, FSel(con2, v2, FSel(con3, v3, FSel(con4, v4, FSel(con5, v5, v6)))))); +// closestP = closest; +// +// const Vec3V vv = V3Sub(p, closest); +// +// return V3Dot(vv, vv); +//} + + +Ps::aos::FloatV Gu::distancePointTriangleSquared( const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& u, + Ps::aos::FloatV& v, + Ps::aos::Vec3V& closestP) +{ + using namespace Ps::aos; + + const FloatV zero = FZero(); + const FloatV one = FOne(); + //const Vec3V zero = V3Zero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V bp = V3Sub(p, b); + const Vec3V cp = V3Sub(p, c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + + if(BAllEqTTTT(con0)) + { + u = zero; + v = zero; + const Vec3V vv = V3Sub(p, a); + closestP = a; + return V3Dot(vv, vv); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + u = one; + v = zero; + const Vec3V vv = V3Sub(p, b); + closestP = b; + return V3Dot(vv, vv); + } + + //check if p in vertex region outside c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if(BAllEqTTTT(con2)) + { + u = zero; + v = one; + const Vec3V vv = V3Sub(p, c); + closestP = c; + return V3Dot(vv, vv); + } + + //check if p in edge region of AB + const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); + + const BoolV con30 = FIsGrtr(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtr(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + if(BAllEqTTTT(con3)) + { + const FloatV sScale = FDiv(d1, FSub(d1, d3)); + const Vec3V closest3 = V3Add(a, V3Scale(ab, sScale)); + u = sScale; + v = zero; + const Vec3V vv = V3Sub(p, closest3); + closestP = closest3; + return V3Dot(vv, vv); + } + + //check if p in edge region of BC + const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); + const BoolV con40 = FIsGrtr(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + if(BAllEqTTTT(con4)) + { + const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); + const Vec3V closest4 = V3Add(b, V3Scale(bc, uScale)); + u = FSub(one, uScale); + v = uScale; + const Vec3V vv = V3Sub(p, closest4); + closestP = closest4; + return V3Dot(vv, vv); + } + + //check if p in edge region of AC + const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); + const BoolV con50 = FIsGrtr(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtr(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + if(BAllEqTTTT(con5)) + { + const FloatV tScale = FDiv(d2, FSub(d2, d6)); + const Vec3V closest5 = V3Add(a, V3Scale(ac, tScale)); + u = zero; + v = tScale; + const Vec3V vv = V3Sub(p, closest5); + closestP = closest5; + return V3Dot(vv, vv); + } + + //P must project inside face region. Compute Q using Barycentric coordinates + const FloatV denom = FRecip(FAdd(va, FAdd(vb, vc))); + const FloatV t = FMul(vb, denom); + const FloatV w = FMul(vc, denom); + const Vec3V bCom = V3Scale(ab, t); + const Vec3V cCom = V3Scale(ac, w); + const Vec3V closest6 = V3Add(a, V3Add(bCom, cCom)); + u = t; + v = w; + closestP = closest6; + + const Vec3V vv = V3Sub(p, closest6); + + return V3Dot(vv, vv); +} diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h new file mode 100644 index 000000000..279991f54 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h @@ -0,0 +1,125 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_TRIANGLE_H +#define GU_DISTANCE_POINT_TRIANGLE_H + +#include "foundation/PxVec3.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + // PT: special version: + // - inlined + // - doesn't compute (s,t) output params + // - expects precomputed edges in input + PX_FORCE_INLINE PxVec3 closestPtPointTriangle2(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, const PxVec3& ab, const PxVec3& ac) + { + // Check if P in vertex region outside A + //const PxVec3 ab = b - a; + //const PxVec3 ac = c - a; + const PxVec3 ap = p - a; + const float d1 = ab.dot(ap); + const float d2 = ac.dot(ap); + if(d1<=0.0f && d2<=0.0f) + return a; // Barycentric coords 1,0,0 + + // Check if P in vertex region outside B + const PxVec3 bp = p - b; + const float d3 = ab.dot(bp); + const float d4 = ac.dot(bp); + if(d3>=0.0f && d4<=d3) + return b; // Barycentric coords 0,1,0 + + // Check if P in edge region of AB, if so return projection of P onto AB + const float vc = d1*d4 - d3*d2; + if(vc<=0.0f && d1>=0.0f && d3<=0.0f) + { + const float v = d1 / (d1 - d3); + return a + v * ab; // barycentric coords (1-v, v, 0) + } + + // Check if P in vertex region outside C + const PxVec3 cp = p - c; + const float d5 = ab.dot(cp); + const float d6 = ac.dot(cp); + if(d6>=0.0f && d5<=d6) + return c; // Barycentric coords 0,0,1 + + // Check if P in edge region of AC, if so return projection of P onto AC + const float vb = d5*d2 - d1*d6; + if(vb<=0.0f && d2>=0.0f && d6<=0.0f) + { + const float w = d2 / (d2 - d6); + return a + w * ac; // barycentric coords (1-w, 0, w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + const float va = d3*d6 - d5*d4; + if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f) + { + const float w = (d4-d3) / ((d4 - d3) + (d5-d6)); + return b + w * (c-b); // barycentric coords (0, 1-w, w) + } + + // P inside face region. Compute Q through its barycentric coords (u,v,w) + const float denom = 1.0f / (va + vb + vc); + const float v = vb * denom; + const float w = vc * denom; + return a + ab*v + ac*w; + } + + PX_PHYSX_COMMON_API PxVec3 closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t); + + PX_FORCE_INLINE PxReal distancePointTriangleSquared(const PxVec3& point, + const PxVec3& triangleOrigin, + const PxVec3& triangleEdge0, + const PxVec3& triangleEdge1, + PxReal* param0=NULL, + PxReal* param1=NULL) + { + const PxVec3 pt0 = triangleEdge0 + triangleOrigin; + const PxVec3 pt1 = triangleEdge1 + triangleOrigin; + float s,t; + const PxVec3 cp = closestPtPointTriangle(point, triangleOrigin, pt0, pt1, s, t); + if(param0) + *param0 = s; + if(param1) + *param1 = t; + return (cp - point).magnitudeSquared(); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h new file mode 100644 index 000000000..587952c87 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_TRIANGLE_SIMD_H +#define GU_DISTANCE_POINT_TRIANGLE_SIMD_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + + PX_PHYSX_COMMON_API Ps::aos::FloatV distancePointTriangleSquared( const Ps::aos::Vec3VArg point, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& u, + Ps::aos::FloatV& v, + Ps::aos::Vec3V& closestP); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp new file mode 100644 index 000000000..f97b039cf --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp @@ -0,0 +1,549 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuDistanceSegmentBox.h" +#include "GuDistancePointBox.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistancePointSegment.h" +#include "GuIntersectionRayBox.h" + +using namespace physx; + +static void face(unsigned int i0, unsigned int i1, unsigned int i2, PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, const PxVec3& rkPmE, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxVec3 kPpE; + PxReal fLSqr, fInv, fTmp, fParam, fT, fDelta; + + kPpE[i1] = rkPnt[i1] + extents[i1]; + kPpE[i2] = rkPnt[i2] + extents[i2]; + if(rkDir[i0]*kPpE[i1] >= rkDir[i1]*rkPmE[i0]) + { + if(rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0]) + { + // v[i1] >= -e[i1], v[i2] >= -e[i2] (distance = 0) + if(pfLParam) + { + rkPnt[i0] = extents[i0]; + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= rkDir[i1]*rkPmE[i0]*fInv; + rkPnt[i2] -= rkDir[i2]*rkPmE[i0]*fInv; + *pfLParam = -rkPmE[i0]*fInv; + } + } + else + { + // v[i1] >= -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp <= 2.0f*fLSqr*extents[i1]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } + } + else + { + if ( rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0] ) + { + // v[i1] < -e[i1], v[i2] >= -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + } + else + { + // v[i1] < -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0]+rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp >= 0.0f) + { + // v[i1]-edge is closest + if ( fTmp <= 2.0f*fLSqr*extents[i1] ) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + return; + } + + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp >= 0.0f) + { + // v[i2]-edge is closest + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + return; + } + + // (v[i1],v[i2])-corner is closest + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } +} + +static void caseNoZeros(PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxVec3 kPmE(rkPnt.x - extents.x, rkPnt.y - extents.y, rkPnt.z - extents.z); + + PxReal fProdDxPy, fProdDyPx, fProdDzPx, fProdDxPz, fProdDzPy, fProdDyPz; + + fProdDxPy = rkDir.x*kPmE.y; + fProdDyPx = rkDir.y*kPmE.x; + if(fProdDyPx >= fProdDxPy) + { + fProdDzPx = rkDir.z*kPmE.x; + fProdDxPz = rkDir.x*kPmE.z; + if(fProdDzPx >= fProdDxPz) + { + // line intersects x = e0 + face(0, 1, 2, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } + else + { + fProdDzPy = rkDir.z*kPmE.y; + fProdDyPz = rkDir.y*kPmE.z; + if(fProdDzPy >= fProdDyPz) + { + // line intersects y = e1 + face(1, 2, 0, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } +} + +static void case0(unsigned int i0, unsigned int i1, unsigned int i2, PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxReal fPmE0 = rkPnt[i0] - extents[i0]; + PxReal fPmE1 = rkPnt[i1] - extents[i1]; + PxReal fProd0 = rkDir[i1]*fPmE0; + PxReal fProd1 = rkDir[i0]*fPmE1; + PxReal fDelta, fInvLSqr, fInv; + + if(fProd0 >= fProd1) + { + // line intersects P[i0] = e[i0] + rkPnt[i0] = extents[i0]; + + PxReal fPpE1 = rkPnt[i1] + extents[i1]; + fDelta = fProd0 - rkDir[i0]*fPpE1; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i1] = -extents[i1]; + *pfLParam = -(rkDir[i0]*fPmE0+rkDir[i1]*fPpE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= fProd0*fInv; + *pfLParam = -fPmE0*fInv; + } + } + } + else + { + // line intersects P[i1] = e[i1] + rkPnt[i1] = extents[i1]; + + PxReal fPpE0 = rkPnt[i0] + extents[i0]; + fDelta = fProd1 - rkDir[i1]*fPpE0; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i0] = -extents[i0]; + *pfLParam = -(rkDir[i0]*fPpE0+rkDir[i1]*fPmE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i1]; + rkPnt[i0] -= fProd1*fInv; + *pfLParam = -fPmE1*fInv; + } + } + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = -extents[i2]; + } + else if ( rkPnt[i2] > extents[i2] ) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void case00(unsigned int i0, unsigned int i1, unsigned int i2, PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxReal fDelta; + + if(pfLParam) + *pfLParam = (extents[i0] - rkPnt[i0])/rkDir[i0]; + + rkPnt[i0] = extents[i0]; + + if(rkPnt[i1] < -extents[i1]) + { + fDelta = rkPnt[i1] + extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = -extents[i1]; + } + else if(rkPnt[i1] > extents[i1]) + { + fDelta = rkPnt[i1] - extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = extents[i1]; + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = -extents[i2]; + } + else if(rkPnt[i2] > extents[i2]) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void case000(PxVec3& rkPnt, const PxVec3& extents, PxReal& rfSqrDistance) +{ + PxReal fDelta; + + if(rkPnt.x < -extents.x) + { + fDelta = rkPnt.x + extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = -extents.x; + } + else if(rkPnt.x > extents.x) + { + fDelta = rkPnt.x - extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = extents.x; + } + + if(rkPnt.y < -extents.y) + { + fDelta = rkPnt.y + extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = -extents.y; + } + else if(rkPnt.y > extents.y) + { + fDelta = rkPnt.y - extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = extents.y; + } + + if(rkPnt.z < -extents.z) + { + fDelta = rkPnt.z + extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = -extents.z; + } + else if(rkPnt.z > extents.z) + { + fDelta = rkPnt.z - extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = extents.z; + } +} + +//! Compute the smallest distance from the (infinite) line to the box. +static PxReal distanceLineBoxSquared(const PxVec3& lineOrigin, const PxVec3& lineDirection, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxReal* lineParam, + PxVec3* boxParam) +{ + const PxVec3& axis0 = boxBase.column0; + const PxVec3& axis1 = boxBase.column1; + const PxVec3& axis2 = boxBase.column2; + + // compute coordinates of line in box coordinate system + const PxVec3 diff = lineOrigin - boxOrigin; + PxVec3 pnt(diff.dot(axis0), diff.dot(axis1), diff.dot(axis2)); + PxVec3 dir(lineDirection.dot(axis0), lineDirection.dot(axis1), lineDirection.dot(axis2)); + + // Apply reflections so that direction vector has nonnegative components. + bool reflect[3]; + for(unsigned int i=0;i<3;i++) + { + if(dir[i]<0.0f) + { + pnt[i] = -pnt[i]; + dir[i] = -dir[i]; + reflect[i] = true; + } + else + { + reflect[i] = false; + } + } + + PxReal sqrDistance = 0.0f; + + if(dir.x>0.0f) + { + if(dir.y>0.0f) + { + if(dir.z>0.0f) caseNoZeros(pnt, dir, boxExtent, lineParam, sqrDistance); // (+,+,+) + else case0(0, 1, 2, pnt, dir, boxExtent, lineParam, sqrDistance); // (+,+,0) + } + else + { + if(dir.z>0.0f) case0(0, 2, 1, pnt, dir, boxExtent, lineParam, sqrDistance); // (+,0,+) + else case00(0, 1, 2, pnt, dir, boxExtent, lineParam, sqrDistance); // (+,0,0) + } + } + else + { + if(dir.y>0.0f) + { + if(dir.z>0.0f) case0(1, 2, 0, pnt, dir, boxExtent, lineParam, sqrDistance); // (0,+,+) + else case00(1, 0, 2, pnt, dir, boxExtent, lineParam, sqrDistance); // (0,+,0) + } + else + { + if(dir.z>0.0f) case00(2, 0, 1, pnt, dir, boxExtent, lineParam, sqrDistance); // (0,0,+) + else + { + case000(pnt, boxExtent, sqrDistance); // (0,0,0) + if(lineParam) + *lineParam = 0.0f; + } + } + } + + if(boxParam) + { + // undo reflections + for(unsigned int i=0;i<3;i++) + { + if(reflect[i]) + pnt[i] = -pnt[i]; + } + + *boxParam = pnt; + } + + return sqrDistance; +} + +//! Compute the smallest distance from the (finite) line segment to the box. +PxReal Gu::distanceSegmentBoxSquared( const PxVec3& segmentPoint0, const PxVec3& segmentPoint1, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxReal* segmentParam, + PxVec3* boxParam) +{ + // compute coordinates of line in box coordinate system + + PxReal lp; + PxVec3 bp; + PxReal sqrDistance = distanceLineBoxSquared(segmentPoint0, segmentPoint1 - segmentPoint0, boxOrigin, boxExtent, boxBase, &lp, &bp); + if(lp>=0.0f) + { + if(lp<=1.0f) + { + if(segmentParam) + *segmentParam = lp; + if(boxParam) + *boxParam = bp; + return sqrDistance; + } + else + { + if(segmentParam) + *segmentParam = 1.0f; + return Gu::distancePointBoxSquared(segmentPoint1, boxOrigin, boxExtent, boxBase, boxParam); + } + } + else + { + if(segmentParam) + *segmentParam = 0.0f; + return Gu::distancePointBoxSquared(segmentPoint0, boxOrigin, boxExtent, boxBase, boxParam); + } +} diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp new file mode 100644 index 000000000..3e97cc884 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp @@ -0,0 +1,576 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentSegmentSIMD.h" + +using namespace physx; +using namespace Ps; +using namespace aos; + +static const float ZERO_TOLERANCE = 1e-06f; + +// S0 = origin + extent * dir; +// S1 = origin - extent * dir; +PxReal Gu::distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& dir0, PxReal extent0, + const PxVec3& origin1, const PxVec3& dir1, PxReal extent1, + PxReal* param0, PxReal* param1) +{ + const PxVec3 kDiff = origin0 - origin1; + const PxReal fA01 = -dir0.dot(dir1); + const PxReal fB0 = kDiff.dot(dir0); + const PxReal fB1 = -kDiff.dot(dir1); + const PxReal fC = kDiff.magnitudeSquared(); + const PxReal fDet = PxAbs(1.0f - fA01*fA01); + PxReal fS0, fS1, fSqrDist, fExtDet0, fExtDet1, fTmpS0, fTmpS1; + + if (fDet >= ZERO_TOLERANCE) + { + // segments are not parallel + fS0 = fA01*fB1-fB0; + fS1 = fA01*fB0-fB1; + fExtDet0 = extent0*fDet; + fExtDet1 = extent1*fDet; + + if (fS0 >= -fExtDet0) + { + if (fS0 <= fExtDet0) + { + if (fS1 >= -fExtDet1) + { + if (fS1 <= fExtDet1) // region 0 (interior) + { + // minimum at two interior points of 3D lines + PxReal fInvDet = 1.0f/fDet; + fS0 *= fInvDet; + fS1 *= fInvDet; + fSqrDist = fS0*(fS0+fA01*fS1+2.0f*fB0) + fS1*(fA01*fS0+fS1+2.0f*fB1)+fC; + } + else // region 3 (side) + { + fS1 = extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + } + } + else // region 7 (side) + { + fS1 = -extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + } + } + else + { + if (fS1 >= -fExtDet1) + { + if (fS1 <= fExtDet1) // region 1 (side) + { + fS0 = extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0)+fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + else // region 2 (corner) + { + fS1 = extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + else // region 8 (corner) + { + fS1 = -extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 > extent1) + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 >= -extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + } + else + { + if (fS1 >= -fExtDet1) + { + if (fS1 <= fExtDet1) // region 5 (side) + { + fS0 = -extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0)+fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + else // region 4 (corner) + { + fS1 = extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 > extent0) + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 >= -extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = -extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + else // region 6 (corner) + { + fS1 = -extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 > extent0) + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 >= -extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = -extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + } + else + { + // The segments are parallel. + PxReal fE0pE1 = extent0 + extent1; + PxReal fSign = (fA01 > 0.0f ? -1.0f : 1.0f); + PxReal b0Avr = 0.5f*(fB0 - fSign*fB1); + PxReal fLambda = -b0Avr; + if(fLambda < -fE0pE1) + { + fLambda = -fE0pE1; + } + else if(fLambda > fE0pE1) + { + fLambda = fE0pE1; + } + + fS1 = -fSign*fLambda*extent1/fE0pE1; + fS0 = fLambda + fSign*fS1; + fSqrDist = fLambda*(fLambda + 2.0f*b0Avr) + fC; + } + + if(param0) + *param0 = fS0; + if(param1) + *param1 = fS1; + + // account for numerical round-off error + return physx::intrinsics::selectMax(0.0f, fSqrDist); +} + +PxReal Gu::distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& extent0, + const PxVec3& origin1, const PxVec3& extent1, + PxReal* param0, + PxReal* param1) +{ + // Some conversion is needed between the old & new code + // Old: + // segment (s0, s1) + // origin = s0 + // extent = s1 - s0 + // + // New: + // s0 = origin + extent * dir; + // s1 = origin - extent * dir; + + // dsequeira: is this really sensible? We use a highly optimized Wild Magic routine, + // then use a segment representation that requires an expensive conversion to/from... + + PxVec3 dir0 = extent0; + const PxVec3 center0 = origin0 + extent0*0.5f; + PxReal length0 = extent0.magnitude(); //AM: change to make it work for degenerate (zero length) segments. + const bool b0 = length0 != 0.0f; + PxReal oneOverLength0 = 0.0f; + if(b0) + { + oneOverLength0 = 1.0f / length0; + dir0 *= oneOverLength0; + length0 *= 0.5f; + } + + PxVec3 dir1 = extent1; + const PxVec3 center1 = origin1 + extent1*0.5f; + PxReal length1 = extent1.magnitude(); + const bool b1 = length1 != 0.0f; + PxReal oneOverLength1 = 0.0f; + if(b1) + { + oneOverLength1 = 1.0f / length1; + dir1 *= oneOverLength1; + length1 *= 0.5f; + } + + // the return param vals have -extent = s0, extent = s1 + + const PxReal d2 = distanceSegmentSegmentSquared(center0, dir0, length0, + center1, dir1, length1, + param0, param1); + + //ML : This is wrong for some reason, I guess it has precision issue + //// renormalize into the 0 = s0, 1 = s1 range + //if (param0) + // *param0 = b0 ? ((*param0) * oneOverLength0 * 0.5f + 0.5f) : 0.0f; + //if (param1) + // *param1 = b1 ? ((*param1) * oneOverLength1 * 0.5f + 0.5f) : 0.0f; + + if(param0) + *param0 = b0 ? ((length0 + (*param0))*oneOverLength0) : 0.0f; + if(param1) + *param1 = b1 ? ((length1 + (*param1))*oneOverLength1) : 0.0f; + + return d2; +} + +/* + S0 = origin + extent * dir; + S1 = origin + extent * dir; + dir is the vector from start to end point + p1 is the start point of segment1 + d1 is the direction vector(q1 - p1) + p2 is the start point of segment2 + d2 is the direction vector(q2 - p2) +*/ + +FloatV Gu::distanceSegmentSegmentSquared( const Vec3VArg p1, + const Vec3VArg d1, + const Vec3VArg p2, + const Vec3VArg d2, + FloatV& s, + FloatV& t) +{ + const FloatV zero = FZero(); + const FloatV one = FOne(); + const FloatV eps = FEps(); + + const Vec3V r = V3Sub(p1, p2); + const Vec4V combinedDot = V3Dot4(d1, d1, d2, d2, d1, d2, d1, r); + const Vec4V combinedRecip = V4Sel(V4IsGrtr(combinedDot, V4Splat(eps)), V4Recip(combinedDot), V4Splat(zero)); + const FloatV a = V4GetX(combinedDot); + const FloatV e = V4GetY(combinedDot); + const FloatV b = V4GetZ(combinedDot); + const FloatV c = V4GetW(combinedDot); + const FloatV aRecip = V4GetX(combinedRecip);//FSel(FIsGrtr(a, eps), FRecip(a), zero); + const FloatV eRecip = V4GetY(combinedRecip);//FSel(FIsGrtr(e, eps), FRecip(e), zero); + + const FloatV f = V3Dot(d2, r); + + /* + s = (b*f - c*e)/(a*e - b*b); + t = (a*f - b*c)/(a*e - b*b); + + s = (b*t - c)/a; + t = (b*s + f)/e; + */ + + //if segments not parallel, the general non-degenerated case, compute closest point on two segments and clamp to segment1 + const FloatV denom = FSub(FMul(a, e), FMul(b, b)); + const FloatV temp = FSub(FMul(b, f), FMul(c, e)); + const FloatV s0 = FClamp(FDiv(temp, denom), zero, one); + + //if segment is parallel, demon < eps + const BoolV con2 = FIsGrtr(eps, denom);//FIsEq(denom, zero); + const FloatV sTmp = FSel(con2, FHalf(), s0); + + //compute point on segment2 closest to segment1 + //const FloatV tTmp = FMul(FAdd(FMul(b, sTmp), f), eRecip); + const FloatV tTmp = FMul(FScaleAdd(b, sTmp, f), eRecip); + + //if t is in [zero, one], done. otherwise clamp t + const FloatV t2 = FClamp(tTmp, zero, one); + + //recompute s for the new value + const FloatV comp = FMul(FSub(FMul(b,t2), c), aRecip); + const FloatV s2 = FClamp(comp, zero, one); + + s = s2; + t = t2; + + const Vec3V closest1 = V3ScaleAdd(d1, s2, p1);//V3Add(p1, V3Scale(d1, tempS)); + const Vec3V closest2 = V3ScaleAdd(d2, t2, p2);//V3Add(p2, V3Scale(d2, tempT)); + const Vec3V vv = V3Sub(closest1, closest2); + return V3Dot(vv, vv); +} + + + + +/* + segment (p, d) and segment (p02, d02) + segment (p, d) and segment (p12, d12) + segment (p, d) and segment (p22, d22) + segment (p, d) and segment (p32, d32) +*/ +Vec4V Gu::distanceSegmentSegmentSquared4( const Vec3VArg p, const Vec3VArg d0, + const Vec3VArg p02, const Vec3VArg d02, + const Vec3VArg p12, const Vec3VArg d12, + const Vec3VArg p22, const Vec3VArg d22, + const Vec3VArg p32, const Vec3VArg d32, + Vec4V& s, Vec4V& t) +{ + const Vec4V zero = V4Zero(); + const Vec4V one = V4One(); + const Vec4V eps = V4Eps(); + const Vec4V half = V4Splat(FHalf()); + + const Vec4V d0X = V4Splat(V3GetX(d0)); + const Vec4V d0Y = V4Splat(V3GetY(d0)); + const Vec4V d0Z = V4Splat(V3GetZ(d0)); + const Vec4V pX = V4Splat(V3GetX(p)); + const Vec4V pY = V4Splat(V3GetY(p)); + const Vec4V pZ = V4Splat(V3GetZ(p)); + + Vec4V d024 = Vec4V_From_Vec3V(d02); + Vec4V d124 = Vec4V_From_Vec3V(d12); + Vec4V d224 = Vec4V_From_Vec3V(d22); + Vec4V d324 = Vec4V_From_Vec3V(d32); + + Vec4V p024 = Vec4V_From_Vec3V(p02); + Vec4V p124 = Vec4V_From_Vec3V(p12); + Vec4V p224 = Vec4V_From_Vec3V(p22); + Vec4V p324 = Vec4V_From_Vec3V(p32); + + Vec4V d0123X, d0123Y, d0123Z; + Vec4V p0123X, p0123Y, p0123Z; + + PX_TRANSPOSE_44_34(d024, d124, d224, d324, d0123X, d0123Y, d0123Z); + PX_TRANSPOSE_44_34(p024, p124, p224, p324, p0123X, p0123Y, p0123Z); + + const Vec4V rX = V4Sub(pX, p0123X); + const Vec4V rY = V4Sub(pY, p0123Y); + const Vec4V rZ = V4Sub(pZ, p0123Z); + + //TODO - store this in a transposed state and avoid so many dot products? + + const FloatV dd = V3Dot(d0, d0); + + const Vec4V e = V4MulAdd(d0123Z, d0123Z, V4MulAdd(d0123X, d0123X, V4Mul(d0123Y, d0123Y))); + const Vec4V b = V4MulAdd(d0Z, d0123Z, V4MulAdd(d0X, d0123X, V4Mul(d0Y, d0123Y))); + const Vec4V c = V4MulAdd(d0Z, rZ, V4MulAdd(d0X, rX, V4Mul(d0Y, rY))); + const Vec4V f = V4MulAdd(d0123Z, rZ, V4MulAdd(d0123X, rX, V4Mul(d0123Y, rY))); + + const Vec4V a(V4Splat(dd)); + + const Vec4V aRecip(V4Recip(a)); + const Vec4V eRecip(V4Recip(e)); + + //if segments not parallell, compute closest point on two segments and clamp to segment1 + const Vec4V denom = V4Sub(V4Mul(a, e), V4Mul(b, b)); + const Vec4V temp = V4Sub(V4Mul(b, f), V4Mul(c, e)); + const Vec4V s0 = V4Clamp(V4Div(temp, denom), zero, one); + + //test whether segments are parallel + const BoolV con2 = V4IsGrtrOrEq(eps, denom); + const Vec4V sTmp = V4Sel(con2, half, s0); + + //compute point on segment2 closest to segment1 + const Vec4V tTmp = V4Mul(V4Add(V4Mul(b, sTmp), f), eRecip); + + //if t is in [zero, one], done. otherwise clamp t + const Vec4V t2 = V4Clamp(tTmp, zero, one); + + //recompute s for the new value + const Vec4V comp = V4Mul(V4Sub(V4Mul(b,t2), c), aRecip); + const BoolV aaNearZero = V4IsGrtrOrEq(eps, a); // check if aRecip is valid (aa>eps) + const Vec4V s2 = V4Sel(aaNearZero, V4Zero(), V4Clamp(comp, zero, one)); + + /* s = V4Sel(con0, zero, V4Sel(con1, cd, s2)); + t = V4Sel(con1, zero, V4Sel(con0, cg, t2)); */ + s = s2; + t = t2; + + const Vec4V closest1X = V4MulAdd(d0X, s2, pX); + const Vec4V closest1Y = V4MulAdd(d0Y, s2, pY); + const Vec4V closest1Z = V4MulAdd(d0Z, s2, pZ); + + const Vec4V closest2X = V4MulAdd(d0123X, t2, p0123X); + const Vec4V closest2Y = V4MulAdd(d0123Y, t2, p0123Y); + const Vec4V closest2Z = V4MulAdd(d0123Z, t2, p0123Z); + + const Vec4V vvX = V4Sub(closest1X, closest2X); + const Vec4V vvY = V4Sub(closest1Y, closest2Y); + const Vec4V vvZ = V4Sub(closest1Z, closest2Z); + + const Vec4V vd = V4MulAdd(vvX, vvX, V4MulAdd(vvY, vvY, V4Mul(vvZ, vvZ))); + + return vd; +} diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h new file mode 100644 index 000000000..f5d6394ac --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_SEGMENT_SIMD_H +#define GU_DISTANCE_SEGMENT_SEGMENT_SIMD_H + +#include "common/PxPhysXCommonConfig.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + PX_PHYSX_COMMON_API Ps::aos::FloatV distanceSegmentSegmentSquared( const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg d1, const Ps::aos::Vec3VArg p2, const Ps::aos::Vec3VArg d2, + Ps::aos::FloatV& param0, + Ps::aos::FloatV& param1); + + /* + This function do four segment segment closest point test in one go + */ + Ps::aos::Vec4V distanceSegmentSegmentSquared4( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg d, + const Ps::aos::Vec3VArg p02, const Ps::aos::Vec3VArg d02, + const Ps::aos::Vec3VArg p12, const Ps::aos::Vec3VArg d12, + const Ps::aos::Vec3VArg p22, const Ps::aos::Vec3VArg d22, + const Ps::aos::Vec3VArg p32, const Ps::aos::Vec3VArg d32, + Ps::aos::Vec4V& s, Ps::aos::Vec4V& t); +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp new file mode 100644 index 000000000..c2ab4e964 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp @@ -0,0 +1,541 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "GuDistanceSegmentTriangle.h" +#include "GuDistanceSegmentTriangleSIMD.h" +#include "GuDistancePointTriangle.h" +#include "GuDistancePointTriangleSIMD.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentSegmentSIMD.h" +#include "GuBarycentricCoordinates.h" + +using namespace physx; +using namespace Gu; + +// ptchernev: +// The Magic Software code uses a relative error test for parallel case. +// The Novodex code does not presumably as an optimization. +// Since the Novodex code is working in the trunk I see no reason +// to reintroduce the relative error test here. + +// PT: this might just be because the relative error test has been added +// after we grabbed the code. I don't remember making this change. A good +// idea would be NOT to refactor Magic's code, to easily grab updated +// versions from the website............................................. + +// ptchernev: +// The code has been modified to use a relative error test since the absolute +// test would break down for small geometries. (TTP 4021) + +static PX_FORCE_INLINE void updateClosestHit( PxReal fSqrDist0, PxReal fR0, PxReal fS0, PxReal fT0, + PxReal& fSqrDist, PxReal& fR, PxReal& fS, PxReal& fT) +{ + if(fSqrDist0 < fSqrDist) + { + fSqrDist = fSqrDist0; + fR = fR0; + fS = fS0; + fT = fT0; + } +} + +PxReal Gu::distanceSegmentTriangleSquared( const PxVec3& origin, const PxVec3& dir, + const PxVec3& p0, const PxVec3& triEdge0, const PxVec3& triEdge1, + PxReal* t, PxReal* u, PxReal* v) +{ + const PxReal fA00 = dir.magnitudeSquared(); + if(fA00 < 1e-6f*1e-6f) + { + if(t) + *t = 0.0f; + return distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, u, v); + } + const PxVec3 kDiff = p0 - origin; + const PxReal fA01 = -(dir.dot(triEdge0)); + const PxReal fA02 = -(dir.dot(triEdge1)); + const PxReal fA11 = triEdge0.magnitudeSquared(); + const PxReal fA12 = triEdge0.dot(triEdge1); + const PxReal fA22 = triEdge1.dot(triEdge1); + const PxReal fB0 = -(kDiff.dot(dir)); + const PxReal fB1 = kDiff.dot(triEdge0); + const PxReal fB2 = kDiff.dot(triEdge1); + const PxReal fCof00 = fA11*fA22-fA12*fA12; + const PxReal fCof01 = fA02*fA12-fA01*fA22; + const PxReal fCof02 = fA01*fA12-fA02*fA11; + const PxReal fDet = fA00*fCof00+fA01*fCof01+fA02*fCof02; + + PxReal fSqrDist, fSqrDist0, fR, fS, fT, fR0, fS0, fT0; + + // Set up for a relative error test on the angle between ray direction + // and triangle normal to determine parallel/nonparallel status. + const PxVec3 kNormal = triEdge0.cross(triEdge1); + const PxReal fDot = kNormal.dot(dir); + if(fDot*fDot >= 1e-6f*dir.magnitudeSquared()*kNormal.magnitudeSquared()) + { + const PxReal fCof11 = fA00*fA22-fA02*fA02; + const PxReal fCof12 = fA02*fA01-fA00*fA12; + const PxReal fCof22 = fA00*fA11-fA01*fA01; + const PxReal fInvDet = fDet == 0.0f ? 0.0f : 1.0f/fDet; + const PxReal fRhs0 = -fB0*fInvDet; + const PxReal fRhs1 = -fB1*fInvDet; + const PxReal fRhs2 = -fB2*fInvDet; + + fR = fCof00*fRhs0+fCof01*fRhs1+fCof02*fRhs2; + fS = fCof01*fRhs0+fCof11*fRhs1+fCof12*fRhs2; + fT = fCof02*fRhs0+fCof12*fRhs1+fCof22*fRhs2; + + if(fR < 0.0f) + { + if(fS+fT <= 1.0f) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4m + { + // minimum on face s=0 or t=0 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); + fT0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 3m + { + // minimum on face s=0 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + } + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 5m + { + // minimum on face t=0 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 0m + { + // minimum on face r=0 + fSqrDist = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS, &fT); + fR = 0.0f; + } + } + else + { + if(fS < 0.0f) // region 2m + { + // minimum on face s=0 or s+t=1 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 6m + { + // minimum on face t=0 or s+t=1 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 1m + { + // minimum on face s+t=1 or r=0 + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); + fS = 1.0f-fT; + } + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + } + else if(fR <= 1.0f) + { + if(fS+fT <= 1.0f) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4 + { + // minimum on face s=0 or t=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); + fT0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 3 + { + // minimum on face s=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + } + } + else if(fT < 0.0f) // region 5 + { + // minimum on face t=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + } + else // region 0 + { + // global minimum is interior, done + fSqrDist = fR*(fA00*fR+fA01*fS+fA02*fT+2.0f*fB0) + +fS*(fA01*fR+fA11*fS+fA12*fT+2.0f*fB1) + +fT*(fA02*fR+fA12*fS+fA22*fT+2.0f*fB2) + +kDiff.magnitudeSquared(); + } + } + else + { + if(fS < 0.0f) // region 2 + { + // minimum on face s=0 or s+t=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 6 + { + // minimum on face t=0 or s+t=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 1 + { + // minimum on face s+t=1 + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); + fS = 1.0f-fT; + } + } + } + else // fR > 1 + { + if(fS+fT <= 1.0f) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4p + { + // minimum on face s=0 or t=0 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); + fT0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 3p + { + // minimum on face s=0 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + } + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 5p + { + // minimum on face t=0 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 0p + { + // minimum face on r=1 + const PxVec3 kPt = origin+dir; + fSqrDist = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS, &fT); + fR = 1.0f; + } + } + else + { + if(fS < 0.0f) // region 2p + { + // minimum on face s=0 or s+t=1 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 6p + { + // minimum on face t=0 or s+t=1 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 1p + { + // minimum on face s+t=1 or r=1 + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); + fS = 1.0f-fT; + } + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + } + } + else + { + // segment and triangle are parallel + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR0, &fT0); + fS0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1 - triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + + if(t) *t = fR; + if(u) *u = fS; + if(v) *v = fT; + + // account for numerical round-off error + return physx::intrinsics::selectMax(0.0f, fSqrDist); +} + + +/* + closest0 is the closest point on segment pq + closest1 is the closest point on triangle abc +*/ +Ps::aos::FloatV Gu::distanceSegmentTriangleSquared( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + Ps::aos::Vec3V& closest0, Ps::aos::Vec3V& closest1) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + //const FloatV one = FOne(); + //const FloatV parallelTolerance = FloatV_From_F32(PX_PARALLEL_TOLERANCE); + + const Vec3V pq = V3Sub(q, p); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V aq = V3Sub(q, a); + + //This is used to calculate the barycentric coordinate + const FloatV d00 = V3Dot(ab,ab); + const FloatV d01 = V3Dot(ab, ac); + const FloatV d11 = V3Dot(ac, ac); + const FloatV tDenom = FSub(FMul(d00, d11), FMul(d01, d01)); + + const FloatV bdenom = FSel(FIsGrtr(tDenom, zero), FRecip(tDenom), zero); + + const Vec3V n =V3Normalize(V3Cross(ab, ac)); // normalize vector + + //compute the closest point of p and triangle plane abc + const FloatV dist3 = V3Dot(ap, n); + const FloatV sqDist3 = FMul(dist3, dist3); + + + //compute the closest point of q and triangle plane abc + const FloatV dist4 = V3Dot(aq, n); + const FloatV sqDist4 = FMul(dist4, dist4); + const FloatV dMul = FMul(dist3, dist4); + const BoolV con = FIsGrtr(zero, dMul); + + + // intersect with the plane + if(BAllEqTTTT(con)) + { + //compute the intersect point + const FloatV nom = FNeg(V3Dot(n, ap)); + const FloatV denom = FRecip(V3Dot(n, pq)); + const FloatV t = FMul(nom, denom); + const Vec3V ip = V3ScaleAdd(pq, t, p);//V3Add(p, V3Scale(pq, t)); + const Vec3V v2 = V3Sub(ip, a); + const FloatV d20 = V3Dot(v2, ab); + const FloatV d21 = V3Dot(v2, ac); + const FloatV v0 = FMul(FSub(FMul(d11, d20), FMul(d01, d21)), bdenom); + const FloatV w0 = FMul(FSub(FMul(d00, d21), FMul(d01, d20)), bdenom); + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + if(BAllEqTTTT(con0)) + { + closest0 = closest1 = ip; + return zero; + } + } + + + Vec4V t40, t41; + const Vec4V sqDist44 = distanceSegmentSegmentSquared4(p,pq,a,ab, b,bc, a,ac, a,ab, t40, t41); + + const FloatV t00 = V4GetX(t40); + const FloatV t10 = V4GetY(t40); + const FloatV t20 = V4GetZ(t40); + + const FloatV t01 = V4GetX(t41); + const FloatV t11 = V4GetY(t41); + const FloatV t21 = V4GetZ(t41); + + const FloatV sqDist0(V4GetX(sqDist44)); + const FloatV sqDist1(V4GetY(sqDist44)); + const FloatV sqDist2(V4GetZ(sqDist44)); + + const Vec3V closestP00 = V3ScaleAdd(pq, t00, p); + const Vec3V closestP01 = V3ScaleAdd(ab, t01, a); + + const Vec3V closestP10 = V3ScaleAdd(pq, t10, p); + const Vec3V closestP11 = V3ScaleAdd(bc, t11, b); + + const Vec3V closestP20 = V3ScaleAdd(pq, t20, p); + const Vec3V closestP21 = V3ScaleAdd(ac, t21, a); + + + //Get the closest point of all edges + const BoolV con20 = FIsGrtr(sqDist1, sqDist0); + const BoolV con21 = FIsGrtr(sqDist2, sqDist0); + const BoolV con2 = BAnd(con20,con21); + const BoolV con30 = FIsGrtrOrEq(sqDist0, sqDist1); + const BoolV con31 = FIsGrtr(sqDist2, sqDist1); + const BoolV con3 = BAnd(con30, con31); + const FloatV sqDistPE = FSel(con2, sqDist0, FSel(con3, sqDist1, sqDist2)); + //const FloatV tValue = FSel(con2, t00, FSel(con3, t10, t20)); + const Vec3V closestPE0 = V3Sel(con2, closestP00, V3Sel(con3, closestP10, closestP20)); // closestP on segment + const Vec3V closestPE1 = V3Sel(con2, closestP01, V3Sel(con3, closestP11, closestP21)); // closestP on triangle + + + const Vec3V closestP31 = V3NegScaleSub(n, dist3, p);//V3Sub(p, V3Scale(n, dist3)); + const Vec3V closestP30 = p; + + //Compute the barycentric coordinate for project point of q + const Vec3V pV20 = V3Sub(closestP31, a); + const FloatV pD20 = V3Dot(pV20, ab); + const FloatV pD21 = V3Dot(pV20, ac); + const FloatV v0 = FMul(FSub(FMul(d11, pD20), FMul(d01, pD21)), bdenom); + const FloatV w0 = FMul(FSub(FMul(d00, pD21), FMul(d01, pD20)), bdenom); + + //check closestP3 is inside the triangle + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + + + + const Vec3V closestP41 = V3NegScaleSub(n, dist4, q);// V3Sub(q, V3Scale(n, dist4)); + const Vec3V closestP40 = q; + + //Compute the barycentric coordinate for project point of q + const Vec3V qV20 = V3Sub(closestP41, a); + const FloatV qD20 = V3Dot(qV20, ab); + const FloatV qD21 = V3Dot(qV20, ac); + const FloatV v1 = FMul(FSub(FMul(d11, qD20), FMul(d01, qD21)), bdenom); + const FloatV w1 = FMul(FSub(FMul(d00, qD21), FMul(d01, qD20)), bdenom); + + const BoolV con1 = isValidTriangleBarycentricCoord(v1, w1); + + /* + p is interior point but not q + */ + const BoolV d0 = FIsGrtr(sqDistPE, sqDist3); + const Vec3V c00 = V3Sel(d0, closestP30, closestPE0); + const Vec3V c01 = V3Sel(d0, closestP31, closestPE1); + + /* + q is interior point but not p + */ + const BoolV d1 = FIsGrtr(sqDistPE, sqDist4); + const Vec3V c10 = V3Sel(d1, closestP40, closestPE0); + const Vec3V c11 = V3Sel(d1, closestP41, closestPE1); + + /* + p and q are interior point + */ + const BoolV d2 = FIsGrtr(sqDist4, sqDist3); + const Vec3V c20 = V3Sel(d2, closestP30, closestP40); + const Vec3V c21 = V3Sel(d2, closestP31, closestP41); + + const BoolV cond2 = BAnd(con0, con1); + + const Vec3V closestP0 = V3Sel(cond2, c20, V3Sel(con0, c00, V3Sel(con1, c10, closestPE0))); + const Vec3V closestP1 = V3Sel(cond2, c21, V3Sel(con0, c01, V3Sel(con1, c11, closestPE1))); + + const Vec3V vv = V3Sub(closestP1, closestP0); + closest0 = closestP0; + closest1 = closestP1; + return V3Dot(vv, vv); +} diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h new file mode 100644 index 000000000..f91778702 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_TRIANGLE_H +#define GU_DISTANCE_SEGMENT_TRIANGLE_H + +#include "PxPhysXCommonConfig.h" +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + + PX_PHYSX_COMMON_API PxReal distanceSegmentTriangleSquared( + const PxVec3& segmentOrigin, const PxVec3& segmentExtent, + const PxVec3& triangleOrigin, const PxVec3& triangleEdge0, const PxVec3& triangleEdge1, + PxReal* t=NULL, PxReal* u=NULL, PxReal* v=NULL); + + PX_INLINE PxReal distanceSegmentTriangleSquared( + const Gu::Segment& segment, + const PxVec3& triangleOrigin, + const PxVec3& triangleEdge0, + const PxVec3& triangleEdge1, + PxReal* t=NULL, + PxReal* u=NULL, + PxReal* v=NULL) + { + return distanceSegmentTriangleSquared( + segment.p0, segment.computeDirection(), triangleOrigin, triangleEdge0, triangleEdge1, t, u, v); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h new file mode 100644 index 000000000..1c9938943 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_TRIANGLE_SIMD_H +#define GU_DISTANCE_SEGMENT_TRIANGLE_SIMD_H + +#include "PxPhysXCommonConfig.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + + /* + closest0 is the closest point on segment pq + closest1 is the closest point on triangle abc + */ + PX_PHYSX_COMMON_API Ps::aos::FloatV distanceSegmentTriangleSquared( + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + Ps::aos::Vec3V& closest0, Ps::aos::Vec3V& closest1); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp new file mode 100644 index 000000000..f45df41c9 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp @@ -0,0 +1,615 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuEPA.h" +#include "GuEPAFacet.h" +#include "GuGJKSimplex.h" +#include "CmPriorityQueue.h" +#include "PsAllocator.h" + + + +namespace physx +{ +namespace Gu +{ + using namespace Ps::aos; + + class ConvexV; + + struct FacetDistanceComparator + { + bool operator()(const Facet* left, const Facet* right) const + { + return *left < *right; + } + }; + + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + + class EPA + { + public: + EPA(){} + GjkStatus PenetrationDepth(const GjkConvex& a, const GjkConvex& b, const Ps::aos::Vec3V* PX_RESTRICT A, const Ps::aos::Vec3V* PX_RESTRICT B, const PxU8 size, const bool takeCoreShape, + const FloatV tolerenceLength, GjkOutput& output); + bool expandPoint(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg upperBound); + bool expandSegment(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg upperBound); + bool expandTriangle(PxI32& numVerts, const FloatVArg upperBound); + + Facet* addFacet(const PxU32 i0, const PxU32 i1, const PxU32 i2, const Ps::aos::FloatVArg upper); + + bool originInTetrahedron(const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const Ps::aos::Vec3VArg p3, const Ps::aos::Vec3VArg p4); + + Cm::InlinePriorityQueue heap; + Ps::aos::Vec3V aBuf[MaxSupportPoints]; + Ps::aos::Vec3V bBuf[MaxSupportPoints]; + Facet facetBuf[MaxFacets]; + EdgeBuffer edgeBuffer; + EPAFacetManager facetManager; + + private: + PX_NOCOPY(EPA) + }; +#if PX_VC + #pragma warning(pop) +#endif + + PX_FORCE_INLINE bool EPA::originInTetrahedron(const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const Ps::aos::Vec3VArg p3, const Ps::aos::Vec3VArg p4) + { + using namespace Ps::aos; + return BAllEqFFFF(PointOutsideOfPlane4(p1, p2, p3, p4)) == 1; + } + + + static PX_FORCE_INLINE void doSupport(const GjkConvex& a, const GjkConvex& b, const Ps::aos::Vec3VArg dir, Ps::aos::Vec3V& supportA, Ps::aos::Vec3V& supportB, Ps::aos::Vec3V& support) + { + const Vec3V tSupportA = a.support(V3Neg(dir)); + const Vec3V tSupportB = b.support(dir); + //avoid LHS + supportA = tSupportA; + supportB = tSupportB; + support = V3Sub(tSupportA, tSupportB); + } + + GjkStatus epaPenetration(const GjkConvex& a, const GjkConvex& b, const PxU8* PX_RESTRICT aInd, const PxU8* PX_RESTRICT bInd, const PxU8 size, + const bool takeCoreShape, const FloatV tolerenceLength, GjkOutput& output) + { + + PX_ASSERT(size > 0 && size <=4); + + Vec3V A[4]; + Vec3V B[4]; + + //ML: we construct a simplex based on the gjk simplex indices + for(PxU32 i=0; im_FacetId = PxU8(facetId); + + const BoolV validTriangle = facet->isValid2(i0, i1, i2, aBuf, bBuf, upper); + + if(BAllEqTTTT(validTriangle)) + { + heap.push(facet); + facet->m_inHeap = true; + } + else + { + facet->m_inHeap = false; + } + return facet; + } + + //ML: this function performs a flood fill over the boundary of the current polytope. + void Facet::silhouette(const PxU32 _index, const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, EPAFacetManager& manager) + { + using namespace Ps::aos; + const FloatV zero = FZero(); + Edge stack[MaxFacets]; + stack[0] = Edge(this, _index); + PxI32 size = 1; + while(size--) + { + Facet* const PX_RESTRICT f = stack[size].m_facet; + const PxU32 index = stack[size].m_index; + PX_ASSERT(f->Valid()); + + if(!f->m_obsolete) + { + //ML: if the point is above the facet, the facet has an reflex edge, which will make the polytope concave. Therefore, we need to + //remove this facet and make sure the expanded polytope is convex. + const FloatV pointPlaneDist = f->getPlaneDist(w, aBuf, bBuf); + + if(FAllGrtr(zero, pointPlaneDist)) + { + //ML: facet isn't visible from w (we don't have a reflex edge), this facet will be on the boundary and part of the new polytope so that + //we will push it into our edgeBuffer + if(!edgeBuffer.Insert(f, index)) + return; + } + else + { + //ML:facet is visible from w, therefore, we need to remove this facet from the heap and push its adjacent facets onto the stack + f->m_obsolete = true; // Facet is visible from w + const PxU32 next(incMod3(index)); + const PxU32 next2(incMod3(next)); + stack[size++] = Edge(f->m_adjFacets[next2],PxU32(f->m_adjEdges[next2])); + stack[size++] = Edge(f->m_adjFacets[next], PxU32(f->m_adjEdges[next])); + + PX_ASSERT(size <= MaxFacets); + if(!f->m_inHeap) + { + //if the facet isn't in the heap, we can release that memory + manager.deferredFreeID(f->m_FacetId); + } + } + } + } + } + + //ML: this function perform flood fill for the adjancent facet and store the boundary facet into the edgeBuffer + void Facet::silhouette(const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, EPAFacetManager& manager) + { + m_obsolete = true; + for(PxU32 a = 0; a < 3; ++a) + { + m_adjFacets[a]->silhouette(PxU32(m_adjEdges[a]), w, aBuf, bBuf, edgeBuffer, manager); + } + } + + bool EPA::expandPoint(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg upperBound) + { + const Vec3V x = V3UnitX(); + Vec3V q0 = V3Sub(aBuf[0], bBuf[0]); + Vec3V q1; + doSupport(a, b, x, aBuf[1], bBuf[1], q1); + if (V3AllEq(q0, q1)) + return false; + return expandSegment(a, b, numVerts, upperBound); + } + + //ML: this function use the segement to create a triangle + bool EPA::expandSegment(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg upperBound) + { + const Vec3V q0 = V3Sub(aBuf[0], bBuf[0]); + const Vec3V q1 = V3Sub(aBuf[1], bBuf[1]); + const Vec3V v = V3Sub(q1, q0); + const Vec3V absV = V3Abs(v); + + const FloatV x = V3GetX(absV); + const FloatV y = V3GetY(absV); + const FloatV z = V3GetZ(absV); + + Vec3V axis = V3UnitX(); + const BoolV con0 = BAnd(FIsGrtr(x, y), FIsGrtr(z, y)); + if (BAllEqTTTT(con0)) + { + axis = V3UnitY(); + } + else if(FAllGrtr(x, z)) + { + axis = V3UnitZ(); + } + + const Vec3V n = V3Normalize(V3Cross(axis, v)); + Vec3V q2; + doSupport(a, b, n, aBuf[2], bBuf[2], q2); + + return expandTriangle(numVerts, upperBound); + } + + bool EPA::expandTriangle(PxI32& numVerts, const FloatVArg upperBound) + { + numVerts = 3; + + Facet * PX_RESTRICT f0 = addFacet(0, 1, 2, upperBound); + Facet * PX_RESTRICT f1 = addFacet(1, 0, 2, upperBound); + + if(heap.empty()) + return false; + + f0->link(0, f1, 0); + f0->link(1, f1, 2); + f0->link(2, f1, 1); + + return true; + } + + //ML: this function calculates contact information. If takeCoreShape flag is true, this means the two closest points will be on the core shape used in the support functions. + //For example, we treat sphere/capsule as a point/segment in the support function for GJK/EPA, so that the core shape for sphere/capsule is a point/segment. For PCM, we need + //to take the point from the core shape because this will allows us recycle the contacts more stably. For SQ sweeps, we need to take the point on the surface of the sphere/capsule + //when we calculate MTD because this is what will be reported to the user. Therefore, the takeCoreShape flag will be set to be false in SQ. + static void calculateContactInformation(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, Facet* facet, const GjkConvex& a, const GjkConvex& b, const bool takeCoreShape, GjkOutput& output) + { + const FloatV zero = FZero(); + Vec3V _pa, _pb; + facet->getClosestPoint(aBuf, bBuf, _pa, _pb); + + //dist > 0 means two shapes are penetrated. If dist < 0(when origin isn't inside the polytope), two shapes status are unknown + const FloatV dist = FAbs(facet->getPlaneDist()); + + const Vec3V planeNormal = V3Neg(facet->getPlaneNormal()); + + if(takeCoreShape) + { + output.closestA = _pa; + output.closestB = _pb; + output.normal = planeNormal; + output.penDep = FNeg(dist); + } + else + { + //for sphere/capusule to take the surface point + const BoolV aQuadratic = a.isMarginEqRadius(); + const BoolV bQuadratic = b.isMarginEqRadius(); + + const FloatV marginA = FSel(aQuadratic, a.getMargin(), zero); + const FloatV marginB = FSel(bQuadratic, b.getMargin(), zero); + const FloatV sumMargin = FAdd(marginA, marginB); + output.closestA = V3NegScaleSub(planeNormal, marginA, _pa); + output.closestB = V3ScaleAdd(planeNormal, marginB, _pb); + output.normal = planeNormal; + output.penDep = FNeg(FAdd(dist, sumMargin)); + } + } + + //ML: This function returns one of three status codes: + //(1)EPA_FAIL: the algorithm failed to create a valid polytope(the origin wasn't inside the polytope) from the input simplex + //(2)EPA_CONTACT : the algorithm found the MTD and converged successfully. + //(3)EPA_DEGENERATE: the algorithm cannot make further progress and the result is unknown. + GjkStatus EPA::PenetrationDepth(const GjkConvex& a, const GjkConvex& b, const Ps::aos::Vec3V* PX_RESTRICT A, const Ps::aos::Vec3V* PX_RESTRICT B, const PxU8 size, const bool takeCoreShape, + const FloatV tolerenceLength, GjkOutput& output) + { + + using namespace Ps::aos; + + PX_UNUSED(tolerenceLength); + + Ps::prefetchLine(&facetBuf[0]); + Ps::prefetchLine(&facetBuf[0], 128); + + const FloatV zero = FZero(); + + const FloatV _max = FMax(); + FloatV upper_bound(_max); + + aBuf[0]=A[0]; aBuf[1]=A[1]; aBuf[2]=A[2]; aBuf[3]=A[3]; + bBuf[0]=B[0]; bBuf[1]=B[1]; bBuf[2]=B[2]; bBuf[3]=B[3]; + + PxI32 numVertsLocal = 0; + + heap.clear(); + + //if the simplex isn't a tetrahedron, we need to construct one before we can expand it + switch (size) + { + case 1: + { + // Touching contact. Yes, we have a collision and the penetration will be zero + if(!expandPoint(a, b, numVertsLocal, upper_bound)) + return EPA_FAIL; + break; + } + case 2: + { + // We have a line segment inside the Minkowski sum containing the + // origin. we need to construct two back to back triangles which link to each other + if(!expandSegment(a, b, numVertsLocal, upper_bound)) + return EPA_FAIL; + break; + } + case 3: + { + // We have a triangle inside the Minkowski sum containing + // the origin. We need to construct two back to back triangles which link to each other + if(!expandTriangle(numVertsLocal, upper_bound)) + return EPA_FAIL; + + break; + + } + case 4: + { + //check for input face normal. All face normals in this tetrahedron should be all pointing either inwards or outwards. If all face normals are pointing outward, we are good to go. Otherwise, we need to + //shuffle the input vertexes and make sure all face normals are pointing outward + const Vec3V pa0(aBuf[0]); + const Vec3V pa1(aBuf[1]); + const Vec3V pa2(aBuf[2]); + const Vec3V pa3(aBuf[3]); + + const Vec3V pb0(bBuf[0]); + const Vec3V pb1(bBuf[1]); + const Vec3V pb2(bBuf[2]); + const Vec3V pb3(bBuf[3]); + + const Vec3V p0 = V3Sub(pa0, pb0); + const Vec3V p1 = V3Sub(pa1, pb1); + const Vec3V p2 = V3Sub(pa2, pb2); + const Vec3V p3 = V3Sub(pa3, pb3); + + const Vec3V v1 = V3Sub(p1, p0); + const Vec3V v2 = V3Sub(p2, p0); + + const Vec3V planeNormal = V3Normalize(V3Cross(v1, v2)); + + const FloatV signDist = V3Dot(planeNormal, V3Sub(p3, p0)); + + if (FAllGrtr(signDist, zero)) + { + //shuffle the input vertexes + const Vec3V tempA0 = aBuf[2]; + const Vec3V tempB0 = bBuf[2]; + aBuf[2] = aBuf[1]; + bBuf[2] = bBuf[1]; + aBuf[1] = tempA0; + bBuf[1] = tempB0; + } + + Facet * PX_RESTRICT f0 = addFacet(0, 1, 2, upper_bound); + Facet * PX_RESTRICT f1 = addFacet(0, 3, 1, upper_bound); + Facet * PX_RESTRICT f2 = addFacet(0, 2, 3, upper_bound); + Facet * PX_RESTRICT f3 = addFacet(1, 3, 2, upper_bound); + + if (heap.empty()) + return EPA_FAIL; + +#if EPA_DEBUG + PX_ASSERT(f0->m_planeDist >= -1e-2f); + PX_ASSERT(f1->m_planeDist >= -1e-2f); + PX_ASSERT(f2->m_planeDist >= -1e-2f); + PX_ASSERT(f3->m_planeDist >= -1e-2f); +#endif + + f0->link(0, f1, 2); + f0->link(1, f3, 2); + f0->link(2, f2, 0); + f1->link(0, f2, 2); + f1->link(1, f3, 0); + f2->link(1, f3, 1); + numVertsLocal = 4; + + break; + } + } + + + const FloatV minMargin = FMin(a.getMinMargin(), b.getMinMargin()); + const FloatV eps = FMul(minMargin, FLoad(0.1f)); + + Facet* PX_RESTRICT facet = NULL; + + Vec3V tempa, tempb, q; + + do + { + + facetManager.processDeferredIds(); + facet = heap.pop(); //get the shortest distance triangle of origin from the list + facet->m_inHeap = false; + + if (!facet->isObsolete()) + { + Ps::prefetchLine(edgeBuffer.m_pEdges); + Ps::prefetchLine(edgeBuffer.m_pEdges,128); + Ps::prefetchLine(edgeBuffer.m_pEdges,256); + + const Vec3V planeNormal = facet->getPlaneNormal(); + const FloatV planeDist = facet->getPlaneDist(); + + tempa = a.support(planeNormal); + tempb = b.support(V3Neg(planeNormal)); + + q = V3Sub(tempa, tempb); + + Ps::prefetchLine(&aBuf[numVertsLocal],128); + Ps::prefetchLine(&bBuf[numVertsLocal],128); + + //calculate the distance from support point to the origin along the plane normal. Because the support point is search along + //the plane normal, which means the distance should be positive. However, if the origin isn't contained in the polytope, dist + //might be negative + const FloatV dist = V3Dot(q, planeNormal); + + const BoolV con0 = FIsGrtrOrEq(eps, FAbs(FSub(dist, planeDist))); + + if (BAllEqTTTT(con0)) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, takeCoreShape, output); + + if (takeCoreShape) + { + const FloatV toleranceEps = FMul(FLoad(1e-3f), tolerenceLength); + const Vec3V dif = V3Sub(output.closestA, output.closestB); + const FloatV pen = FAdd(FAbs(output.penDep), toleranceEps); + const FloatV sqDif = V3Dot(dif, dif); + const FloatV length = FSel(FIsGrtr(sqDif, zero), FSqrt(sqDif), zero); + if (FAllGrtr(length, pen)) + return EPA_DEGENERATE; + } + return EPA_CONTACT; + + } + + //update the upper bound to the minimum between existing upper bound and the distance + upper_bound = FMin(upper_bound, dist); + + aBuf[numVertsLocal]=tempa; + bBuf[numVertsLocal]=tempb; + + const PxU32 index =PxU32(numVertsLocal++); + + // Compute the silhouette cast by the new vertex + // Note that the new vertex is on the positive side + // of the current facet, so the current facet will + // not be in the polytope. Start local search + // from this facet. + + edgeBuffer.MakeEmpty(); + + facet->silhouette(q, aBuf, bBuf, edgeBuffer, facetManager); + + if (!edgeBuffer.IsValid()) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, takeCoreShape, output); + return EPA_DEGENERATE; + } + + Edge* PX_RESTRICT edge=edgeBuffer.Get(0); + + PxU32 bufferSize=edgeBuffer.Size(); + + //check to see whether we have enough space in the facet manager to create new facets + if(bufferSize > facetManager.getNumRemainingIDs()) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, takeCoreShape, output); + return EPA_DEGENERATE; + } + + Facet *firstFacet = addFacet(edge->getTarget(), edge->getSource(),index, upper_bound); + PX_ASSERT(firstFacet); + firstFacet->link(0, edge->getFacet(), edge->getIndex()); + + Facet * PX_RESTRICT lastFacet = firstFacet; + +#if EPA_DEBUG + bool degenerate = false; + for(PxU32 i=1; (igetTarget(), edge->getSource(),index, upper_bound); + PX_ASSERT(newFacet); + const bool b0 = newFacet->link(0, edge->getFacet(), edge->getIndex()); + const bool b1 = newFacet->link(2, lastFacet, 1); + degenerate = degenerate || !b0 || !b1; + lastFacet = newFacet; + } + + if (degenerate) + Ps::debugBreak(); +#else + for (PxU32 i = 1; igetTarget(), edge->getSource(), index, upper_bound); + newFacet->link(0, edge->getFacet(), edge->getIndex()); + newFacet->link(2, lastFacet, 1); + lastFacet = newFacet; + } +#endif + + firstFacet->link(2, lastFacet, 1); + } + facetManager.freeID(facet->m_FacetId); + + } + while((heap.size() > 0) && FAllGrtr(upper_bound, heap.top()->getPlaneDist()) && numVertsLocal != MaxSupportPoints); + + calculateContactInformation(aBuf, bBuf, facet, a, b, takeCoreShape, output); + return EPA_DEGENERATE; + } +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h new file mode 100644 index 000000000..4ae929579 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h @@ -0,0 +1,60 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EPA_H +#define GU_EPA_H + +#include "GuGJKUtil.h" +#include "GuGJKType.h" + +namespace physx +{ + +namespace Gu +{ + //ML: The main entry point for EPA. + // + //This function returns one of three status codes: + //(1)EPA_FAIL: the algorithm failed to create a valid polytope(the origin wasn't inside the polytope) from the input simplex. + //(2)EPA_CONTACT : the algorithm found the MTD and converged successfully. + //(3)EPA_DEGENERATE: the algorithm cannot make further progress and the result is unknown. + + GjkStatus epaPenetration( const GjkConvex& a, //convex a in the space of convex b + const GjkConvex& b, //convex b + const PxU8* PX_RESTRICT aInd, //warm start index for convex a to create an initial simplex + const PxU8* PX_RESTRICT bInd, //warm start index for convex b to create an initial simplex + const PxU8 size, //number of warm-start indices + const bool takeCoreShape, //indicates whether we take support point from the core shape or surface of capsule/sphere + const Ps::aos::FloatV tolerenceLength, //the length of meter + GjkOutput& output); //result +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h b/src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h new file mode 100644 index 000000000..44de1afb6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h @@ -0,0 +1,306 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EPA_FACET_H +#define GU_EPA_FACET_H + +#include "CmPhysXCommon.h" +#include "PsVecMath.h" +#include "PsFPU.h" +#include "PsUtilities.h" +#include "CmIDPool.h" + +#if (defined __GNUC__ && defined _DEBUG) +#define PX_EPA_FORCE_INLINE +#else +#define PX_EPA_FORCE_INLINE PX_FORCE_INLINE +#endif + +#define EPA_DEBUG 0 + +namespace physx +{ +#define MaxEdges 32 +#define MaxFacets 64 +#define MaxSupportPoints 64 + +namespace Gu +{ + const PxU32 lookUp[3] = {1, 2, 0}; + + PX_FORCE_INLINE PxU32 incMod3(PxU32 i) { return lookUp[i]; } + + class EdgeBuffer; + class Edge; + typedef Cm::InlineDeferredIDPool EPAFacetManager; + + class Facet + { + public: + + Facet() + { + } + + PX_FORCE_INLINE Facet(const PxU32 _i0, const PxU32 _i1, const PxU32 _i2) + : m_obsolete(false), m_inHeap(false) + { + m_indices[0]= Ps::toI8(_i0); + m_indices[1]= Ps::toI8(_i1); + m_indices[2]= Ps::toI8(_i2); + + m_adjFacets[0] = m_adjFacets[1] = m_adjFacets[2] = NULL; + m_adjEdges[0] = m_adjEdges[1] = m_adjEdges[2] = -1; + } + + + PX_FORCE_INLINE void invalidate() + { + m_adjFacets[0] = m_adjFacets[1] = m_adjFacets[2] = NULL; + m_adjEdges[0] = m_adjEdges[1] = m_adjEdges[2] = -1; + } + + PX_FORCE_INLINE bool Valid() + { + return (m_adjFacets[0] != NULL) & (m_adjFacets[1] != NULL) & (m_adjFacets[2] != NULL); + } + + PX_FORCE_INLINE PxU32 operator[](const PxU32 i) const + { + return PxU32(m_indices[i]); + } + + //create ajacency information + bool link(const PxU32 edge0, Facet* PX_RESTRICT facet, const PxU32 edge1); + + PX_FORCE_INLINE bool isObsolete() const { return m_obsolete; } + + //calculate the signed distance from a point to a plane + PX_FORCE_INLINE Ps::aos::FloatV getPlaneDist(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf) const; + + //check to see whether the triangle is a valid triangle, calculate plane normal and plane distance at the same time + Ps::aos::BoolV isValid2(const PxU32 i0, const PxU32 i1, const PxU32 i2, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, + const Ps::aos::FloatVArg upper); + + //return the absolute value for the plane distance from origin + PX_FORCE_INLINE Ps::aos::FloatV getPlaneDist() const + { + return Ps::aos::FLoad(m_planeDist); + } + + //return the plane normal + PX_FORCE_INLINE Ps::aos::Vec3V getPlaneNormal()const + { + return m_planeNormal; + } + + //calculate the closest points for a shape pair + void getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, Ps::aos::Vec3V& closestA, + Ps::aos::Vec3V& closestB); + + //calculate the closest points for a shape pair + //void getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, Ps::aos::FloatV& v, Ps::aos::FloatV& w); + + //performs a flood fill over the boundary of the current polytope. + void silhouette(const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, EPAFacetManager& manager); + + + //m_planeDist is positive + bool operator <(const Facet& b) const + { + return m_planeDist < b.m_planeDist; + } + + //store all the boundary facets for the new polytope in the edgeBuffer and free indices when an old facet isn't part of the boundary anymore + PX_FORCE_INLINE void silhouette(const PxU32 index, const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, + EPAFacetManager& manager); + + Ps::aos::Vec3V m_planeNormal; //16 + PxF32 m_planeDist; +#if EPA_DEBUG + PxF32 m_lambda1; + PxF32 m_lambda2; +#endif + + Facet* PX_RESTRICT m_adjFacets[3]; //the triangle adjacent to edge i in this triangle //32 + PxI8 m_adjEdges[3]; //the edge connected with the corresponding triangle //35 + PxI8 m_indices[3]; //the index of vertices of the triangle //38 + bool m_obsolete; //a flag to denote whether the triangle are still part of the bundeary of the new polytope //39 + bool m_inHeap; //a flag to indicate whether the triangle is in the heap //40 + PxU8 m_FacetId; //41 //73 + + }; + + + class Edge + { + public: + PX_FORCE_INLINE Edge() {} + PX_FORCE_INLINE Edge(Facet * PX_RESTRICT facet, const PxU32 index) : m_facet(facet), m_index(index) {} + PX_FORCE_INLINE Edge(const Edge& other) : m_facet(other.m_facet), m_index(other.m_index){} + + PX_FORCE_INLINE Edge& operator = (const Edge& other) + { + m_facet = other.m_facet; + m_index = other.m_index; + return *this; + } + + PX_FORCE_INLINE Facet *getFacet() const { return m_facet; } + PX_FORCE_INLINE PxU32 getIndex() const { return m_index; } + + //get out the associated start vertex index in this edge from the facet + PX_FORCE_INLINE PxU32 getSource() const + { + PX_ASSERT(m_index < 3); + return (*m_facet)[m_index]; + } + + //get out the associated end vertex index in this edge from the facet + PX_FORCE_INLINE PxU32 getTarget() const + { + PX_ASSERT(m_index < 3); + return (*m_facet)[incMod3(m_index)]; + } + + Facet* PX_RESTRICT m_facet; + PxU32 m_index; + }; + + + class EdgeBuffer + { + public: + EdgeBuffer() : m_Size(0), m_OverFlow(false) + { + } + + Edge* Insert(Facet* PX_RESTRICT facet, const PxU32 index) + { + if (m_Size < MaxEdges) + { + Edge* pEdge = &m_pEdges[m_Size++]; + pEdge->m_facet = facet; + pEdge->m_index = index; + return pEdge; + } + m_OverFlow = true; + return NULL; + } + + Edge* Get(const PxU32 index) + { + PX_ASSERT(index < m_Size); + return &m_pEdges[index]; + } + + PxU32 Size() + { + return m_Size; + } + + bool IsValid() + { + return m_Size > 0 && !m_OverFlow; + } + + void MakeEmpty() + { + m_Size = 0; + m_OverFlow = false; + } + + Edge m_pEdges[MaxEdges]; + PxU32 m_Size; + bool m_OverFlow; + }; + + //ML: calculate MTD points for a shape pair + PX_FORCE_INLINE void Facet::getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, Ps::aos::Vec3V& closestA, + Ps::aos::Vec3V& closestB) + { + using namespace Ps::aos; + + const Vec3V pa0(aBuf[m_indices[0]]); + const Vec3V pa1(aBuf[m_indices[1]]); + const Vec3V pa2(aBuf[m_indices[2]]); + + const Vec3V pb0(bBuf[m_indices[0]]); + const Vec3V pb1(bBuf[m_indices[1]]); + const Vec3V pb2(bBuf[m_indices[2]]); + + const Vec3V p0 = V3Sub(pa0, pb0); + const Vec3V p1 = V3Sub(pa1, pb1); + const Vec3V p2 = V3Sub(pa2, pb2); + + const Vec3V v0 = V3Sub(p1, p0); + const Vec3V v1 = V3Sub(p2, p0); + + const Vec3V closestP = V3Scale(m_planeNormal, FLoad(m_planeDist)); + const Vec3V v2 = V3Sub(closestP, p0); + //calculate barycentric coordinates + const FloatV d00 = V3Dot(v0, v0); + const FloatV d01 = V3Dot(v0, v1); + const FloatV d11 = V3Dot(v1, v1); + + const FloatV d20 = V3Dot(v2, v0); + const FloatV d21 = V3Dot(v2, v1); + + const FloatV det = FNegScaleSub(d01, d01, FMul(d00, d11));//FSub( FMul(v1dv1, v2dv2), FMul(v1dv2, v1dv2) ); // non-negative + const FloatV recip = FSel(FIsGrtr(det, FEps()), FRecip(det), FZero()); + + const FloatV lambda1 = FMul(FNegScaleSub(d01, d21, FMul(d11, d20)), recip); + const FloatV lambda2 = FMul(FNegScaleSub(d01, d20, FMul(d00, d21)), recip); + +#if EPA_DEBUG + FStore(lambda1, &m_lambda1); + FStore(lambda2, &m_lambda2); +#endif + + const FloatV u = FSub(FOne(), FAdd(lambda1, lambda2)); + closestA = V3ScaleAdd(pa0, u, V3ScaleAdd(pa1, lambda1, V3Scale(pa2, lambda2))); + closestB = V3ScaleAdd(pb0, u, V3ScaleAdd(pb1, lambda1, V3Scale(pb2, lambda2))); + } + + //ML: create adjacency informations for both facets + PX_FORCE_INLINE bool Facet::link(const PxU32 edge0, Facet * PX_RESTRICT facet, const PxU32 edge1) + { + m_adjFacets[edge0] = facet; + m_adjEdges[edge0] = Ps::toI8(edge1); + facet->m_adjFacets[edge1] = this; + facet->m_adjEdges[edge1] = Ps::toI8(edge0); + + return (m_indices[edge0] == facet->m_indices[incMod3(edge1)]) && (m_indices[incMod3(edge0)] == facet->m_indices[edge1]); + } + +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h new file mode 100644 index 000000000..015005e76 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h @@ -0,0 +1,219 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJK_H +#define GU_GJK_H + + +#include "GuGJKType.h" +#include "GuGJKUtil.h" +#include "GuConvexSupportTable.h" +#include "GuGJKSimplex.h" +#include "PsFPU.h" + +#define GJK_SEPERATING_AXIS_VALIDATE 0 + +namespace physx +{ +namespace Gu +{ + + class ConvexV; + +#if GJK_SEPERATING_AXIS_VALIDATE + template + static void validateSeparatingAxis(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg separatingAxis) + { + using namespace Ps::aos; + const Vec3V minV0 = a.ConvexA::support(V3Neg(separatingAxis)); + const Vec3V maxV0 = a.ConvexA::support(separatingAxis); + + const Vec3V minV1 = b.ConvexB::support(V3Neg(separatingAxis)); + const Vec3V maxV1 = b.ConvexB::support(separatingAxis); + + const FloatV min0 = V3Dot(minV0, separatingAxis); + const FloatV max0 = V3Dot(maxV0, separatingAxis); + + const FloatV min1 = V3Dot(minV1, separatingAxis); + const FloatV max1 = V3Dot(maxV1, separatingAxis); + + PX_ASSERT(FAllGrtr(min1, max0) || FAllGrtr(min0, max1)); + } +#endif + + + /* + + initialSearchDir :initial search direction in the mincowsky sum, it should be in the local space of ConvexB + closestA :it is the closest point in ConvexA in the local space of ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + closestB :it is the closest point in ConvexB in the local space of ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + normal :normal pointing from ConvexA to ConvexB in the local space of ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + distance :the distance of the closest points between ConvexA and ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + contactDist :the distance which we will generate contact information if ConvexA and ConvexB are both separated within contactDist + */ + + //*Each convex has + //* a support function + //* a margin - the amount by which we shrunk the shape for a convex or box. If the shape are sphere/capsule, margin is the radius + //* a minMargin - some percentage of margin, which is used to determine the termination condition for gjk + + //*We'll report: + //* GJK_NON_INTERSECT if the sign distance between the shapes is greater than the sum of the margins and the the contactDistance + //* GJK_CLOSE if the minimum distance between the shapes is less than the sum of the margins and the the contactDistance + //* GJK_CONTACT if the two shapes are overlapped with each other + template + GjkStatus gjk(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3V& initialSearchDir, const Ps::aos::FloatV& contactDist, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, Ps::aos::Vec3V& normal, + Ps::aos::FloatV& distance) + { + using namespace Ps::aos; + Vec3V Q[4]; + Vec3V A[4]; + Vec3V B[4]; + + const FloatV zero = FZero(); + PxU32 size=0; + + //const Vec3V _initialSearchDir = aToB.p; + Vec3V closest = V3Sel(FIsGrtr(V3Dot(initialSearchDir, initialSearchDir), zero), initialSearchDir, V3UnitX()); + Vec3V v = V3Normalize(closest); + + // ML: eps2 is the square value of an epsilon value which applied in the termination condition for two shapes overlap. + // GJK will terminate based on sq(v) < eps and indicate that two shapes are overlapping. + // we calculate the eps based on 10% of the minimum margin of two shapes + const FloatV tenPerc = FLoad(0.1f); + const FloatV minMargin = FMin(a.getMinMargin(), b.getMinMargin()); + const FloatV eps = FMul(minMargin, tenPerc); + + // ML:epsRel is square value of 1.5% which applied to the distance of a closest point(v) to the origin. + // If |v|- v/|v|.dot(w) < epsRel*|v|, + // two shapes are clearly separated, GJK terminate and return non intersect. + // This adjusts the termination condition based on the length of v + // which avoids ill-conditioned terminations. + const FloatV epsRel = FLoad(0.000225f);//1.5%. + + FloatV dist = FMax(); + FloatV prevDist; + Vec3V prevClos, prevDir; + + const BoolV bTrue = BTTTT(); + BoolV bNotTerminated = bTrue; + BoolV bNotDegenerated = bTrue; + + //ML: we treate sphere as a point and capsule as segment in the support function, so that we need to add on radius + const BoolV aQuadratic = a.isMarginEqRadius(); + const BoolV bQuadratic = b.isMarginEqRadius(); + + const FloatV sumMargin = FAdd(FSel(aQuadratic, a.getMargin(), zero), FSel(bQuadratic, b.getMargin(), zero)); + const FloatV separatingDist = FAdd(sumMargin, contactDist); + const FloatV relDif = FSub(FOne(), epsRel); + + do + { + prevDist = dist; + prevClos = closest; + prevDir = v; + + //de-virtualize, we don't need to use a normalize direction to get the support point + //this will allow the cpu better pipeline the normalize calculation while it does the + //support map + const Vec3V supportA=a.ConvexA::support(V3Neg(closest)); + const Vec3V supportB=b.ConvexB::support(closest); + + //calculate the support point + const Vec3V support = V3Sub(supportA, supportB); + + const FloatV signDist = V3Dot(v, support); + + if(FAllGrtr(signDist, separatingDist)) + { + //ML:gjk found a separating axis for these two objects and the distance is large than the seperating distance, gjk might not converage so that + //we won't generate contact information +#if GJK_SEPERATING_AXIS_VALIDATE + validateSeparatingAxis(a, b, v); +#endif + return GJK_NON_INTERSECT; + } + + const BoolV con = BAnd(FIsGrtr(signDist, sumMargin), FIsGrtr(signDist, FMul(relDif, dist))); + + if(BAllEqTTTT(con)) + { + //ML:: gjk converage and we get the closest point information + Vec3V closA, closB; + //normal point from A to B + const Vec3V n = V3Neg(v); + getClosestPoint(Q, A, B, closest, closA, closB, size); + closestA = V3Sel(aQuadratic, V3ScaleAdd(n, a.getMargin(), closA), closA); + closestB = V3Sel(bQuadratic, V3NegScaleSub(n, b.getMargin(), closB), closB); + distance = FMax(zero, FSub(dist, sumMargin)); + normal = n;//V3Normalize(V3Neg(closest)); + return GJK_CLOSE; + } + + PX_ASSERT(size < 4); + A[size]=supportA; + B[size]=supportB; + Q[size++]=support; + + //calculate the closest point between two convex hull + closest = GJKCPairDoSimplex(Q, A, B, support, size); + + dist = V3Length(closest); + v = V3ScaleInv(closest, dist); + bNotDegenerated = FIsGrtr(prevDist, dist); + bNotTerminated = BAnd(FIsGrtr(dist, eps), bNotDegenerated); + }while(BAllEqTTTT(bNotTerminated)); + + if(BAllEqTTTT(bNotDegenerated)) + { + //GJK_CONTACT + distance = zero; + return GJK_CONTACT; + } + + //GJK degenerated, use the previous closest point + const FloatV acceptancePerc = FLoad(0.2f); + const FloatV acceptanceMargin = FMul(acceptancePerc, FMin(a.getMargin(), b.getMargin())); + const FloatV acceptanceDist = FSel(FIsGrtr(sumMargin, zero), sumMargin, acceptanceMargin); + Vec3V closA, closB; + const Vec3V n = V3Neg(prevDir);//V3Normalize(V3Neg(prevClos)); + getClosestPoint(Q, A, B, prevClos, closA, closB, size); + closestA = V3Sel(aQuadratic, V3ScaleAdd(n, a.getMargin(), closA), closA); + closestB = V3Sel(bQuadratic, V3NegScaleSub(n, b.getMargin(), closB), closB); + normal = n; + dist = FMax(zero, FSub(prevDist, sumMargin)); + distance = dist; + + return FAllGrtr(dist, acceptanceDist) ? GJK_CLOSE: GJK_CONTACT; + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h new file mode 100644 index 000000000..23fb003d0 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h @@ -0,0 +1,320 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJK_PENETRATION_H +#define GU_GJK_PENETRATION_H + + +#include "GuConvexSupportTable.h" +#include "GuGJKSimplex.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGJKUtil.h" +#include "PsUtilities.h" +#include "GuGJKType.h" + +#define GJK_VALIDATE 0 + + +namespace physx +{ +namespace Gu +{ + + class ConvexV; + + + PX_FORCE_INLINE void assignWarmStartValue(PxU8* PX_RESTRICT aIndices, PxU8* PX_RESTRICT bIndices, PxU8& size_, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, PxU32 size ) + { + if(aIndices) + { + PX_ASSERT(bIndices); + size_ = Ps::to8(size); + for(PxU32 i=0; i + PX_NOINLINE GjkStatus gjkPenetration(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg contactDist, const bool takeCoreShape, + PxU8* PX_RESTRICT aIndices, PxU8* PX_RESTRICT bIndices, PxU8& warmStartSize, + GjkOutput& output) + { + using namespace Ps::aos; + + //ML: eps is the threshold that uses to determine whether two (shrunk) shapes overlap. We calculate eps2 based on 10% of the minimum margin of two shapes + const FloatV minMargin = FMin(a.ConvexA::getMinMargin(), b.ConvexB::getMinMargin()); + const FloatV eps = FMul(minMargin, FLoad(0.1f)); + + //const FloatV eps2 = FMul(_eps2, _eps2); + //ML: epsRel2 is the square of 0.01. This is used to scale the square distance of a closest point to origin to determine whether two shrunk shapes overlap in the margin, but + //they don't overlap. + //const FloatV epsRel2 = FMax(FLoad(0.0001), eps2); + + // ML:epsRel is square value of 1.5% which applied to the distance of a closest point(v) to the origin. + // If |v|- v/|v|.dot(w) < epsRel*|v|=>(|v|*(1-epsRel) < v/|v|.dot(w)), + // two shapes are clearly separated, GJK terminate and return non intersect. + // This adjusts the termination condition based on the length of v + // which avoids ill-conditioned terminations. + const FloatV epsRel = FLoad(0.000225f);//1.5%. + const FloatV relDif = FSub(FOne(), epsRel); + + const FloatV zero = FZero(); + + //capsule/sphere will have margin which is its radius + const FloatV marginA = a.getMargin(); + const FloatV marginB = b.getMargin(); + + const BoolV aQuadratic = a.isMarginEqRadius(); + const BoolV bQuadratic = b.isMarginEqRadius(); + const FloatV tMarginA = FSel(aQuadratic, marginA, zero); + const FloatV tMarginB = FSel(bQuadratic, marginB, zero); + + const FloatV sumMargin = FAdd(tMarginA, tMarginB); + const FloatV sumExpandedMargin = FAdd(sumMargin, contactDist); + + FloatV dist = FMax(); + FloatV prevDist = dist; + const Vec3V zeroV = V3Zero(); + Vec3V prevClos = zeroV; + + const BoolV bTrue = BTTTT(); + BoolV bNotTerminated = bTrue; + BoolV bNotDegenerated = bTrue; + Vec3V closest; + + Vec3V Q[4]; + Vec3V A[4]; + Vec3V B[4]; + PxI32 aInd[4]; + PxI32 bInd[4]; + Vec3V supportA = zeroV, supportB = zeroV, support=zeroV; + Vec3V v; + + PxU32 size = 0;//_size; + + + //ML: if _size!=0, which means we pass in the previous frame simplex so that we can warm-start the simplex. + //In this case, GJK will normally terminate in one iteration + if(warmStartSize != 0) + { + for(PxU32 i=0; i sqMargin * sq(v), which means the original objects do not intesect, GJK terminate with GJK_NON_INTERSECT. + //(3)two shapes don't overlap. However, they interect within margin distance. if sq(v)- vw < epsRel2*sq(v), this means the shrunk shapes interect in the margin, + // GJK terminate with GJK_CONTACT. + while(BAllEqTTTT(bNotTerminated)) + { + //prevDist, prevClos are used to store the previous iteration's closest point and the square distance from the closest point + //to origin in Mincowski space + prevDist = dist; + prevClos = closest; + + //de-virtualize + supportA = a.ConvexA::support(V3Neg(closest), aInd[size]); + supportB = b.ConvexB::support(closest, bInd[size]); + + //calculate the support point + support = V3Sub(supportA, supportB); + + const FloatV vw = V3Dot(v, support); + if(FAllGrtr(vw, sumExpandedMargin)) + { + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size); + return GJK_NON_INTERSECT; + } + + //if(FAllGrtr(FMul(epsRel, dist), FSub(dist, vw))) + if(FAllGrtr(vw, FMul(dist, relDif))) + { + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size); + PX_ASSERT(FAllGrtr(dist, FEps())); + //const Vec3V n = V3ScaleInv(closest, dist);//normalise + output.normal = v; + Vec3V closA, closB; + getClosestPoint(Q, A, B, closest, closA, closB, size); + //ML: if one of the shape is sphere/capsule and the takeCoreShape flag is true, the contact point for sphere/capsule will be the sphere center or a point in the + //capsule segment. This will increase the stability for the manifold recycling code. Otherwise, we will return a contact point on the surface for sphere/capsule + //while the takeCoreShape flag is set to be false + if(takeCoreShape) + { + output.closestA= closA; + output.closestB = closB; + output.penDep = dist; + + } + else + { + //This is for capsule/sphere want to take the surface point. For box/convex, + //we need to get rid of margin + output.closestA = V3NegScaleSub(v, tMarginA, closA); + output.closestB = V3ScaleAdd(v, tMarginB, closB); + output.penDep = FSub(dist, sumMargin); + } + + return GJK_CONTACT; + } + + A[size] = supportA; + B[size] = supportB; + Q[size++]=support; + PX_ASSERT(size <= 4); + + //calculate the closest point between two convex hull + closest = GJKCPairDoSimplex(Q, A, B, aInd, bInd, support, size); + + dist = V3Length(closest); + v = V3ScaleInv(closest, dist); + + bNotDegenerated = FIsGrtr(prevDist, dist); + bNotTerminated = BAnd(FIsGrtr(dist, eps), bNotDegenerated); + } + + if(BAllEqFFFF(bNotDegenerated)) + { + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size-1); + + //Reset back to older closest point + dist = prevDist; + closest = prevClos;//V3Sub(closA, closB); + Vec3V closA, closB; + getClosestPoint(Q, A, B, closest, closA, closB, size); + + //PX_ASSERT(FAllGrtr(dist, FEps())); + const Vec3V n = V3ScaleInv(prevClos, prevDist);//normalise + output.normal = n; + + output.searchDir = v; + + if(takeCoreShape) + { + output.closestA = closA; + output.closestB = closB; + output.penDep = dist; + } + else + { + //This is for capsule/sphere want to take the surface point. For box/convex, + //we need to get rid of margin + output.closestA = V3NegScaleSub(n, tMarginA, closA); + output.closestB = V3ScaleAdd(n, tMarginB, closB); + output.penDep = FSub(dist, sumMargin); + if (FAllGrtrOrEq(sumMargin, dist)) + return GJK_CONTACT; + } + + return GJK_DEGENERATE; + } + else + { + //this two shapes are deeply intersected with each other, we need to use EPA algorithm to calculate MTD + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size); + return EPA_CONTACT; + + } + } + +}//Gu + +}//physx + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h new file mode 100644 index 000000000..79c04931c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h @@ -0,0 +1,295 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKRAYCAST_H +#define GU_GJKRAYCAST_H + +#include "GuGJKType.h" +#include "GuGJKSimplex.h" +#include "GuConvexSupportTable.h" +#include "GuGJKPenetration.h" +#include "GuEPA.h" + + +namespace physx +{ + + +namespace Gu +{ + + /* + ConvexA is in the local space of ConvexB + lambda : the time of impact(TOI) + initialLambda : the start time of impact value (disable) + s : the sweep ray origin + r : the normalized sweep ray direction scaled by the sweep distance. r should be in ConvexB's space + normal : the contact normal + inflation : the amount by which we inflate the swept shape. If the inflated shapes aren't initially-touching, + the TOI will return the time at which both shapes are at a distance equal to inflation separated. If inflation is 0 + the TOI will return the time at which both shapes are touching. + + */ + template + bool gjkRaycast(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation) + { + PX_UNUSED(initialLambda); + + using namespace Ps::aos; + + const FloatV inflation = FLoad(_inflation); + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + const FloatV one = FOne(); + const BoolV bTrue = BTTTT(); + + const FloatV maxDist = FLoad(PX_MAX_REAL); + + FloatV _lambda = zero;//initialLambda; + Vec3V x = V3ScaleAdd(r, _lambda, s); + PxU32 size=1; + + //const Vec3V dir = V3Sub(a.getCenter(), b.getCenter()); + const Vec3V _initialSearchDir = V3Sel(FIsGrtr(V3Dot(initialDir, initialDir), FEps()), initialDir, V3UnitX()); + const Vec3V initialSearchDir = V3Normalize(_initialSearchDir); + + const Vec3V initialSupportA(a.ConvexA::support(V3Neg(initialSearchDir))); + const Vec3V initialSupportB( b.ConvexB::support(initialSearchDir)); + + Vec3V Q[4] = {V3Sub(initialSupportA, initialSupportB), zeroV, zeroV, zeroV}; //simplex set + Vec3V A[4] = {initialSupportA, zeroV, zeroV, zeroV}; //ConvexHull a simplex set + Vec3V B[4] = {initialSupportB, zeroV, zeroV, zeroV}; //ConvexHull b simplex set + + + Vec3V v = V3Neg(Q[0]); + Vec3V supportA = initialSupportA; + Vec3V supportB = initialSupportB; + Vec3V support = Q[0]; + + const FloatV minMargin = FMin(a.ConvexA::getSweepMargin(), b.ConvexB::getSweepMargin()); + const FloatV eps1 = FMul(minMargin, FLoad(0.1f)); + const FloatV inflationPlusEps(FAdd(eps1, inflation)); + const FloatV eps2 = FMul(eps1, eps1); + + const FloatV inflation2 = FMul(inflationPlusEps, inflationPlusEps); + + Vec3V clos(Q[0]); + Vec3V preClos = clos; + //Vec3V closA(initialSupportA); + FloatV sDist = V3Dot(v, v); + FloatV minDist = sDist; + //Vec3V closAA = initialSupportA; + //Vec3V closBB = initialSupportB; + + BoolV bNotTerminated = FIsGrtr(sDist, eps2); + BoolV bNotDegenerated = bTrue; + + Vec3V nor = v; + + while(BAllEqTTTT(bNotTerminated)) + { + + minDist = sDist; + preClos = clos; + + const Vec3V vNorm = V3Normalize(v); + const Vec3V nvNorm = V3Neg(vNorm); + + supportA=a.ConvexA::support(vNorm); + supportB=V3Add(x, b.ConvexB::support(nvNorm)); + + //calculate the support point + support = V3Sub(supportA, supportB); + const Vec3V w = V3Neg(support); + const FloatV vw = FSub(V3Dot(vNorm, w), inflationPlusEps); + if(FAllGrtr(vw, zero)) + { + const FloatV vr = V3Dot(vNorm, r); + if(FAllGrtrOrEq(vr, zero)) + { + return false; + } + else + { + const FloatV _oldLambda = _lambda; + _lambda = FSub(_lambda, FDiv(vw, vr)); + if(FAllGrtr(_lambda, _oldLambda)) + { + if(FAllGrtr(_lambda, one)) + { + return false; + } + const Vec3V bPreCenter = x; + x = V3ScaleAdd(r, _lambda, s); + + const Vec3V offSet =V3Sub(x, bPreCenter); + const Vec3V b0 = V3Add(B[0], offSet); + const Vec3V b1 = V3Add(B[1], offSet); + const Vec3V b2 = V3Add(B[2], offSet); + + B[0] = b0; + B[1] = b1; + B[2] = b2; + + Q[0]=V3Sub(A[0], b0); + Q[1]=V3Sub(A[1], b1); + Q[2]=V3Sub(A[2], b2); + + supportB = V3Add(x, b.ConvexB::support(nvNorm)); + support = V3Sub(supportA, supportB); + minDist = maxDist; + nor = v; + //size=0; + } + } + } + + PX_ASSERT(size < 4); + A[size]=supportA; + B[size]=supportB; + Q[size++]=support; + + //calculate the closest point between two convex hull + clos = GJKCPairDoSimplex(Q, A, B, support, size); + v = V3Neg(clos); + sDist = V3Dot(clos, clos); + + bNotDegenerated = FIsGrtr(minDist, sDist); + bNotTerminated = BAnd(FIsGrtr(sDist, inflation2), bNotDegenerated); + } + + + const BoolV aQuadratic = a.isMarginEqRadius(); + //ML:if the Minkowski sum of two objects are too close to the original(eps2 > sDist), we can't take v because we will lose lots of precision. Therefore, we will take + //previous configuration's normal which should give us a reasonable approximation. This effectively means that, when we do a sweep with inflation, we always keep v because + //the shapes converge separated. If we do a sweep without inflation, we will usually use the previous configuration's normal. + nor = V3Sel(BAnd(FIsGrtr(sDist, eps2), bNotDegenerated), v, nor); + nor = V3Neg(V3NormalizeSafe(nor, V3Zero())); + normal = nor; + lambda = _lambda; + + const Vec3V closestP = V3Sel(bNotDegenerated, clos, preClos); + Vec3V closA = zeroV, closB = zeroV; + getClosestPoint(Q, A, B, closestP, closA, closB, size); + closestA = V3Sel(aQuadratic, V3NegScaleSub(nor, a.getMargin(), closA), closA); + + return true; + } + + + + /* + ConvexA is in the local space of ConvexB + lambda : the time of impact(TOI) + initialLambda : the start time of impact value (disable) + s : the sweep ray origin in ConvexB's space + r : the normalized sweep ray direction scaled by the sweep distance. r should be in ConvexB's space + normal : the contact normal in ConvexB's space + closestA : the tounching contact in ConvexB's space + inflation : the amount by which we inflate the swept shape. If the inflated shapes aren't initially-touching, + the TOI will return the time at which both shapes are at a distance equal to inflation separated. If inflation is 0 + the TOI will return the time at which both shapes are touching. + + */ + template + bool gjkRaycastPenetration(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, + Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation, const bool initialOverlap) + { + using namespace Ps::aos; + Vec3V closA; + Vec3V norm; + FloatV _lambda; + if(gjkRaycast(a, b, initialDir, initialLambda, s, r, _lambda, norm, closA, _inflation)) + { + const FloatV zero = FZero(); + lambda = _lambda; + if(FAllEq(_lambda, zero) && initialOverlap) + { + + //time of impact is zero, the sweep shape is intesect, use epa to get the normal and contact point + const FloatV contactDist = getSweepContactEps(a.getMargin(), b.getMargin()); + + FloatV sDist(zero); + PxU8 aIndices[4]; + PxU8 bIndices[4]; + PxU8 size=0; + GjkOutput output; + + //PX_COMPILE_TIME_ASSERT(typename Shrink::Type != Gu::BoxV); + + typename ConvexA::ConvexGeomType convexA = a.getGjkConvex(); + typename ConvexB::ConvexGeomType convexB = b.getGjkConvex(); + + GjkStatus status = gjkPenetration(convexA, convexB, + initialDir, contactDist, false, aIndices, bIndices, size, output); + //norm = V3Neg(norm); + if(status == GJK_CONTACT) + { + closA = output.closestA; + sDist = output.penDep; + norm = output.normal; + } + else if(status == EPA_CONTACT) + { + status = epaPenetration(a, b, aIndices, bIndices, size, false, FLoad(1.f), output); + + if (status == EPA_CONTACT || status == EPA_DEGENERATE) + { + closA = output.closestA; + sDist = output.penDep; + norm = output.normal; + } + else + { + //ML: if EPA fail, we will use the ray direction as the normal and set pentration to be zero + closA = V3Zero(); + sDist = zero; + norm = V3Normalize(V3Neg(r)); + } + } + else + { + //ML:: this will be gjk degenerate case. However, we can still + //reply on the previous iteration's closest feature information + closA = output.closestA; + sDist = output.penDep; + norm = output.normal; + } + lambda = FMin(zero, sDist); + } + closestA = closA; + normal = norm; + return true; + } + return false; + } +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp new file mode 100644 index 000000000..9dc648cf5 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp @@ -0,0 +1,216 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGJKSimplex.h" + +namespace physx +{ +namespace Gu +{ + + using namespace Ps::aos; + + static Vec3V getClosestPtPointTriangle(Vec3V* PX_RESTRICT Q, const BoolVArg bIsOutside4, PxU32* indices, PxU32& size) + { + FloatV bestSqDist = FMax(); + + PxU32 _indices[3] = {0, 1, 2}; + + Vec3V closestPt = V3Zero(); + + if(BAllEqTTTT(BGetX(bIsOutside4))) + { + //use the original indices, size, v and w + bestSqDist = closestPtPointTriangleBaryCentric(Q[0], Q[1], Q[2], indices, size, closestPt); + } + + if(BAllEqTTTT(BGetY(bIsOutside4))) + { + + PxU32 _size = 3; + _indices[0] = 0; _indices[1] = 2; _indices[2] = 3; + Vec3V tClosestPt; + const FloatV sqDist = closestPtPointTriangleBaryCentric(Q[0], Q[2], Q[3], _indices, _size, tClosestPt); + + const BoolV con = FIsGrtr(bestSqDist, sqDist); + if(BAllEqTTTT(con)) + { + closestPt = tClosestPt; + bestSqDist = sqDist; + + indices[0] = _indices[0]; + indices[1] = _indices[1]; + indices[2] = _indices[2]; + + size = _size; + } + } + + if(BAllEqTTTT(BGetZ(bIsOutside4))) + { + PxU32 _size = 3; + + _indices[0] = 0; _indices[1] = 3; _indices[2] = 1; + + Vec3V tClosestPt; + const FloatV sqDist = closestPtPointTriangleBaryCentric(Q[0], Q[3], Q[1], _indices, _size, tClosestPt); + + const BoolV con = FIsGrtr(bestSqDist, sqDist); + if(BAllEqTTTT(con)) + { + closestPt = tClosestPt; + bestSqDist = sqDist; + + indices[0] = _indices[0]; + indices[1] = _indices[1]; + indices[2] = _indices[2]; + + size = _size; + } + + } + + if(BAllEqTTTT(BGetW(bIsOutside4))) + { + + + PxU32 _size = 3; + _indices[0] = 1; _indices[1] = 3; _indices[2] = 2; + Vec3V tClosestPt; + const FloatV sqDist = closestPtPointTriangleBaryCentric(Q[1], Q[3], Q[2], _indices, _size, tClosestPt); + + const BoolV con = FIsGrtr(bestSqDist, sqDist); + + if(BAllEqTTTT(con)) + { + closestPt = tClosestPt; + bestSqDist = sqDist; + + indices[0] = _indices[0]; + indices[1] = _indices[1]; + indices[2] = _indices[2]; + + size = _size; + } + } + + return closestPt; + } + + PX_NOALIAS Vec3V closestPtPointTetrahedron(Vec3V* PX_RESTRICT Q, Vec3V* PX_RESTRICT A, Vec3V* PX_RESTRICT B, PxU32& size) + { + + const FloatV eps = FLoad(1e-4f); + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V d = Q[3]; + + //degenerated + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V n = V3Normalize(V3Cross(ab, ac)); + const FloatV signDist = V3Dot(n, V3Sub(d, a)); + if(FAllGrtr(eps, FAbs(signDist))) + { + size = 3; + return closestPtPointTriangle(Q, A, B, size); + } + + const BoolV bIsOutside4 = PointOutsideOfPlane4(a, b, c, d); + + if(BAllEqFFFF(bIsOutside4)) + { + //All inside + return V3Zero(); + } + + PxU32 indices[3] = {0, 1, 2}; + + const Vec3V closest = getClosestPtPointTriangle(Q, bIsOutside4, indices, size); + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; const Vec3V q2 = Q[indices[2]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; const Vec3V a2 = A[indices[2]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; const Vec3V b2 = B[indices[2]]; + Q[0] = q0; Q[1] = q1; Q[2] = q2; + A[0] = a0; A[1] = a1; A[2] = a2; + B[0] = b0; B[1] = b1; B[2] = b2; + + return closest; + } + + PX_NOALIAS Vec3V closestPtPointTetrahedron(Vec3V* PX_RESTRICT Q, Vec3V* PX_RESTRICT A, Vec3V* PX_RESTRICT B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, PxU32& size) + { + + const FloatV eps = FLoad(1e-4f); + const Vec3V zeroV = V3Zero(); + + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V d = Q[3]; + + //degenerated + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V n = V3Normalize(V3Cross(ab, ac)); + const FloatV signDist = V3Dot(n, V3Sub(d, a)); + if(FAllGrtr(eps, FAbs(signDist))) + { + size = 3; + return closestPtPointTriangle(Q, A, B, aInd, bInd, size); + } + + const BoolV bIsOutside4 = PointOutsideOfPlane4(a, b, c, d); + + if(BAllEqFFFF(bIsOutside4)) + { + //All inside + return zeroV; + } + + PxU32 indices[3] = {0, 1, 2}; + const Vec3V closest = getClosestPtPointTriangle(Q, bIsOutside4, indices, size); + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; const Vec3V q2 = Q[indices[2]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; const Vec3V a2 = A[indices[2]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; const Vec3V b2 = B[indices[2]]; + const PxI32 _aInd0 = aInd[indices[0]]; const PxI32 _aInd1 = aInd[indices[1]]; const PxI32 _aInd2 = aInd[indices[2]]; + const PxI32 _bInd0 = bInd[indices[0]]; const PxI32 _bInd1 = bInd[indices[1]]; const PxI32 _bInd2 = bInd[indices[2]]; + Q[0] = q0; Q[1] = q1; Q[2] = q2; + A[0] = a0; A[1] = a1; A[2] = a2; + B[0] = b0; B[1] = b1; B[2] = b2; + aInd[0] = _aInd0; aInd[1] = _aInd1; aInd[2] = _aInd2; + bInd[0] = _bInd0; bInd[1] = _bInd1; bInd[2] = _bInd2; + + return closest; + } +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h new file mode 100644 index 000000000..415d94070 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h @@ -0,0 +1,473 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKSIMPLEX_H +#define GU_GJKSIMPLEX_H + +#include "CmPhysXCommon.h" +#include "PsVecMath.h" +#include "GuBarycentricCoordinates.h" + +#if (defined __GNUC__ && defined _DEBUG) +#define PX_GJK_INLINE PX_INLINE +#define PX_GJK_FORCE_INLINE PX_INLINE +#else +#define PX_GJK_INLINE PX_INLINE +#define PX_GJK_FORCE_INLINE PX_FORCE_INLINE +#endif + + +namespace physx +{ +namespace Gu +{ + + + PX_NOALIAS Ps::aos::Vec3V closestPtPointTetrahedron(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, PxU32& size); + + PX_NOALIAS Ps::aos::Vec3V closestPtPointTetrahedron(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, + PxU32& size); + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::BoolV PointOutsideOfPlane4(const Ps::aos::Vec3VArg _a, const Ps::aos::Vec3VArg _b, const Ps::aos::Vec3VArg _c, const Ps::aos::Vec3VArg _d) + { + using namespace Ps::aos; + + // this is not 0 because of the following scenario: + // All the points lie on the same plane and the plane goes through the origin (0,0,0). + // On the Wii U, the math below has the problem that when point A gets projected on the + // plane cumputed by A, B, C, the distance to the plane might not be 0 for the mentioned + // scenario but a small positive or negative value. This can lead to the wrong boolean + // results. Using a small negative value as threshold is more conservative but safer. + const Vec4V zero = V4Load(-1e-6f); + + const Vec3V ab = V3Sub(_b, _a); + const Vec3V ac = V3Sub(_c, _a); + const Vec3V ad = V3Sub(_d, _a); + const Vec3V bd = V3Sub(_d, _b); + const Vec3V bc = V3Sub(_c, _b); + + const Vec3V v0 = V3Cross(ab, ac); + const Vec3V v1 = V3Cross(ac, ad); + const Vec3V v2 = V3Cross(ad, ab); + const Vec3V v3 = V3Cross(bd, bc); + + const FloatV signa0 = V3Dot(v0, _a); + const FloatV signa1 = V3Dot(v1, _a); + const FloatV signa2 = V3Dot(v2, _a); + const FloatV signd3 = V3Dot(v3, _a); + + const FloatV signd0 = V3Dot(v0, _d); + const FloatV signd1 = V3Dot(v1, _b); + const FloatV signd2 = V3Dot(v2, _c); + const FloatV signa3 = V3Dot(v3, _b); + + const Vec4V signa = V4Merge(signa0, signa1, signa2, signa3); + const Vec4V signd = V4Merge(signd0, signd1, signd2, signd3); + return V4IsGrtrOrEq(V4Mul(signa, signd), zero);//same side, outside of the plane + + + } + + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::Vec3V closestPtPointSegment(Ps::aos::Vec3V* PX_RESTRICT Q, PxU32& size) + { + using namespace Ps::aos; + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + + //const Vec3V origin = V3Zero(); + const FloatV zero = FZero(); + const FloatV one = FOne(); + + //Test degenerated case + const Vec3V ab = V3Sub(b, a); + const FloatV denom = V3Dot(ab, ab); + const Vec3V ap = V3Neg(a);//V3Sub(origin, a); + const FloatV nom = V3Dot(ap, ab); + const BoolV con = FIsGrtrOrEq(FEps(), denom);//FIsEq(denom, zero); + //TODO - can we get rid of this branch? The problem is size, which isn't a vector! + if(BAllEqTTTT(con)) + { + size = 1; + return Q[0]; + } + + /* const PxU32 count = BAllEq(con, bTrue); + size = 2 - count;*/ + + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + return V3ScaleAdd(ab, tValue, a); + } + + + PX_FORCE_INLINE void getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT Q, const Ps::aos::Vec3V* PX_RESTRICT A, const Ps::aos::Vec3V* PX_RESTRICT B, const Ps::aos::Vec3VArg closest, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, const PxU32 size) + { + using namespace Ps::aos; + + switch(size) + { + case 1: + { + closestA = A[0]; + closestB = B[0]; + break; + } + case 2: + { + FloatV v; + barycentricCoordinates(closest, Q[0], Q[1], v); + const Vec3V av = V3Sub(A[1], A[0]); + const Vec3V bv = V3Sub(B[1], B[0]); + closestA = V3ScaleAdd(av, v, A[0]); + closestB = V3ScaleAdd(bv, v, B[0]); + + break; + } + case 3: + { + //calculate the Barycentric of closest point p in the mincowsky sum + FloatV v, w; + barycentricCoordinates(closest, Q[0], Q[1], Q[2], v, w); + + const Vec3V av0 = V3Sub(A[1], A[0]); + const Vec3V av1 = V3Sub(A[2], A[0]); + const Vec3V bv0 = V3Sub(B[1], B[0]); + const Vec3V bv1 = V3Sub(B[2], B[0]); + + closestA = V3Add(A[0], V3Add(V3Scale(av0, v), V3Scale(av1, w))); + closestB = V3Add(B[0], V3Add(V3Scale(bv0, v), V3Scale(bv1, w))); + } + }; + } + + PX_NOALIAS PX_GJK_FORCE_INLINE Ps::aos::FloatV closestPtPointTriangleBaryCentric(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + PxU32* PX_RESTRICT indices, PxU32& size, Ps::aos::Vec3V& closestPt) + { + using namespace Ps::aos; + + size = 3; + const FloatV zero = FZero(); + const FloatV eps = FEps(); + + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + + const Vec3V n = V3Cross(ab, ac); + //ML: if the shape is oblong, the degeneracy test sometime can't catch the degeneracy in the tetraheron. Therefore, we need to make sure we still can ternimate with the previous + //triangle by returning the maxinum distance. + const FloatV nn = V3Dot(n, n); + if (FAllEq(nn, zero)) + return FMax(); + + //const FloatV va = FNegScaleSub(d5, d4, FMul(d3, d6));//edge region of BC + //const FloatV vb = FNegScaleSub(d1, d6, FMul(d5, d2));//edge region of AC + //const FloatV vc = FNegScaleSub(d3, d2, FMul(d1, d4));//edge region of AB + + //const FloatV va = V3Dot(n, V3Cross(b, c));//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + //const FloatV vb = V3Dot(n, V3Cross(c, a));//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + //const FloatV vc = V3Dot(n, V3Cross(a, b));//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + + const VecCrossV crossA = V3PrepareCross(a); + const VecCrossV crossB = V3PrepareCross(b); + const VecCrossV crossC = V3PrepareCross(c); + const Vec3V bCrossC = V3Cross(crossB, crossC); + const Vec3V cCrossA = V3Cross(crossC, crossA); + const Vec3V aCrossB = V3Cross(crossA, crossB); + + const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + + const BoolV isFacePoints = BAnd(FIsGrtrOrEq(va, zero), BAnd(FIsGrtrOrEq(vb, zero), FIsGrtrOrEq(vc, zero))); + + + //face region + if(BAllEqTTTT(isFacePoints)) + { + const FloatV t = FDiv(V3Dot(n, a), nn); + const Vec3V q = V3Scale(n, t); + closestPt = q; + return V3Dot(q, q); + } + + const Vec3V ap = V3Neg(a); + const Vec3V bp = V3Neg(b); + const Vec3V cp = V3Neg(c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + + + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + size = 2; + //check if p in edge region of AB + const BoolV con30 = FIsGrtrOrEq(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtrOrEq(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32));//edge AB region + if(BAllEqTTTT(con3)) + { + const FloatV toRecipAB = FSub(d1, d3); + const FloatV recipAB = FSel(FIsGrtr(FAbs(toRecipAB), eps), FRecip(toRecipAB), zero); + const FloatV t = FMul(d1, recipAB); + const Vec3V q = V3ScaleAdd(ab, t, a); + closestPt = q; + return V3Dot(q, q); + } + + //check if p in edge region of BC + const BoolV con40 = FIsGrtrOrEq(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); //edge BC region + if(BAllEqTTTT(con4)) + { + const Vec3V bc = V3Sub(c, b); + const FloatV toRecipBC = FAdd(unom, udenom); + const FloatV recipBC = FSel(FIsGrtr(FAbs(toRecipBC), eps), FRecip(toRecipBC), zero); + const FloatV t = FMul(unom, recipBC); + indices[0] = indices[1]; + indices[1] = indices[2]; + const Vec3V q = V3ScaleAdd(bc, t, b); + closestPt = q; + return V3Dot(q, q); + } + + //check if p in edge region of AC + const BoolV con50 = FIsGrtrOrEq(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtrOrEq(zero, d6); + + const BoolV con5 = BAnd(con50, BAnd(con51, con52));//edge AC region + if(BAllEqTTTT(con5)) + { + const FloatV toRecipAC = FSub(d2, d6); + const FloatV recipAC = FSel(FIsGrtr(FAbs(toRecipAC), eps), FRecip(toRecipAC), zero); + const FloatV t = FMul(d2, recipAC); + indices[1]=indices[2]; + const Vec3V q = V3ScaleAdd(ac, t, a); + closestPt = q; + return V3Dot(q, q); + } + + size = 1; + //check if p in vertex region outside a + const BoolV con00 = FIsGrtrOrEq(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtrOrEq(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + if(BAllEqTTTT(con0)) + { + closestPt = a; + return V3Dot(a, a); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + indices[0] = indices[1]; + closestPt = b; + return V3Dot(b, b); + } + + //p is in vertex region outside c + indices[0] = indices[2]; + closestPt = c; + return V3Dot(c, c); + + } + + PX_NOALIAS PX_GJK_FORCE_INLINE Ps::aos::Vec3V closestPtPointTriangle(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* A, Ps::aos::Vec3V* B, PxU32& size) + { + + using namespace Ps::aos; + + size = 3; + + const FloatV eps = FEps(); + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V signArea = V3Cross(ab, ac);//0.5*(abXac) + const FloatV area = V3Dot(signArea, signArea); + if(FAllGrtrOrEq(eps, area)) + { + //degenerate + size = 2; + return closestPtPointSegment(Q, size); + } + + PxU32 _size; + PxU32 indices[3]={0, 1, 2}; + Vec3V closestPt; + closestPtPointTriangleBaryCentric(a, b, c, indices, _size, closestPt); + + if(_size != 3) + { + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; + + Q[0] = q0; Q[1] = q1; + A[0] = a0; A[1] = a1; + B[0] = b0; B[1] = b1; + + size = _size; + } + + return closestPt; + } + + PX_NOALIAS PX_GJK_FORCE_INLINE Ps::aos::Vec3V closestPtPointTriangle(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* A, Ps::aos::Vec3V* B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, + PxU32& size) + { + + using namespace Ps::aos; + + size = 3; + + const FloatV eps = FEps(); + + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V signArea = V3Cross(ab, ac);//0.5*(abXac) + const FloatV area = V3Dot(signArea, signArea); + if(FAllGrtrOrEq(eps, area)) + { + //degenerate + size = 2; + return closestPtPointSegment(Q, size); + } + + PxU32 _size; + PxU32 indices[3]={0, 1, 2}; + Vec3V closestPt; + closestPtPointTriangleBaryCentric(a, b, c, indices, _size, closestPt); + + if(_size != 3) + { + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; + const PxI32 aInd0 = aInd[indices[0]]; const PxI32 aInd1 = aInd[indices[1]]; + const PxI32 bInd0 = bInd[indices[0]]; const PxI32 bInd1 = bInd[indices[1]]; + + Q[0] = q0; Q[1] = q1; + A[0] = a0; A[1] = a1; + B[0] = b0; B[1] = b1; + aInd[0] = aInd0; aInd[1] = aInd1; + bInd[0] = bInd0; bInd[1] = bInd1; + + size = _size; + } + + return closestPt; + } + + + + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::Vec3V GJKCPairDoSimplex(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, const Ps::aos::Vec3VArg support, + PxU32& size) + { + using namespace Ps::aos; + + //const PxU32 tempSize = size; + //calculate a closest from origin to the simplex + switch(size) + { + case 1: + { + return support; + } + case 2: + { + return closestPtPointSegment(Q, size); + } + case 3: + { + return closestPtPointTriangle(Q, A, B, size); + } + case 4: + return closestPtPointTetrahedron(Q, A, B, size); + default: + PX_ASSERT(0); + } + return support; + } + + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::Vec3V GJKCPairDoSimplex(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, + const Ps::aos::Vec3VArg support, PxU32& size) + { + using namespace Ps::aos; + + //const PxU32 tempSize = size; + //calculate a closest from origin to the simplex + switch(size) + { + case 1: + { + return support; + } + case 2: + { + return closestPtPointSegment(Q, size); + } + case 3: + { + return closestPtPointTriangle(Q, A, B, aInd, bInd, size); + } + case 4: + return closestPtPointTetrahedron(Q, A, B, aInd, bInd, size); + default: + PX_ASSERT(0); + } + return support; + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp new file mode 100644 index 000000000..3e252d511 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuGJK.h" +#include "GuGJKRaycast.h" +#include "GuGJKPenetration.h" +#include "GuGJKTest.h" + +namespace physx +{ +namespace Gu +{ + +using namespace Ps::aos; + +GjkStatus testGjk(GjkConvex& a, GjkConvex& b, const Vec3VArg initialSearchDir, const FloatVArg contactDist, Vec3V& closestA, Vec3V& closestB, Vec3V& normal, FloatV& dist) +{ + return gjk(a, b, initialSearchDir, contactDist, closestA, closestB, normal, dist); +} + + +bool testGjkRaycast(GjkConvex& a, GjkConvex& b, const Vec3VArg initialSearchDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, + Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal inflation) +{ + return gjkRaycast(a, b, initialSearchDir, initialLambda, s, r, lambda, normal, closestA, inflation); +} + +GjkStatus testGjkPenetration(GjkConvex& a, GjkConvex& b, const Vec3VArg initialSearchDir, const FloatVArg contactDist, + PxU8* aIndices, PxU8* bIndices, PxU8& size, GjkOutput& output) +{ + return gjkPenetration(a, b, initialSearchDir, contactDist, true, + aIndices, bIndices, size, output); +} + +GjkStatus testEpaPenetration(GjkConvex& a, GjkConvex& b, const PxU8* aIndices, const PxU8* bIndices, const PxU8 size, + GjkOutput& output) +{ + return epaPenetration(a, b, aIndices, bIndices, size, true, Ps::aos::FLoad(1.f), output); +} + +} +} + diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h new file mode 100644 index 000000000..412b9a438 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h @@ -0,0 +1,58 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJK_TEST_H +#define GU_GJK_TEST_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "GuGJKUtil.h" + +namespace physx +{ +namespace Gu +{ + struct GjkConvex; + + + PX_PHYSX_COMMON_API GjkStatus testGjk(GjkConvex& a, GjkConvex& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, + Ps::aos::Vec3V& normal, Ps::aos::FloatV& dist); + + PX_PHYSX_COMMON_API bool testGjkRaycast(GjkConvex& a, GjkConvex& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, + Ps::aos::FloatV& lambda, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation, const bool initialOverlap); + + PX_PHYSX_COMMON_API GjkStatus testGjkPenetration(GjkConvex& a, GjkConvex& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg contactDist, + PxU8* aIndices, PxU8* bIndices, PxU8& size, GjkOutput& output); + + PX_PHYSX_COMMON_API GjkStatus testEpaPenetration(GjkConvex& a, GjkConvex& b, const PxU8* aIndices, const PxU8* bIndices, const PxU8 size, + GjkOutput& output); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h new file mode 100644 index 000000000..6856a2611 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h @@ -0,0 +1,177 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKTYPE_H +#define GU_GJKTYPE_H + +#include "GuVecConvex.h" +#include "PsVecTransform.h" + +namespace physx +{ +namespace Gu +{ + class ConvexHullV; + class ConvexHullNoScaleV; + class BoxV; + + template struct ConvexGeom { typedef Convex Type; }; + template <> struct ConvexGeom { typedef ConvexHullV Type; }; + template <> struct ConvexGeom { typedef ConvexHullNoScaleV Type; }; + template <> struct ConvexGeom { typedef BoxV Type; }; + + struct GjkConvexBase + { + + GjkConvexBase(const ConvexV& convex) : mConvex(convex){} + PX_FORCE_INLINE Ps::aos::FloatV getMinMargin() const { return mConvex.getMinMargin(); } + PX_FORCE_INLINE Ps::aos::BoolV isMarginEqRadius() const { return mConvex.isMarginEqRadius(); } + PX_FORCE_INLINE bool getMarginIsRadius() const { return mConvex.getMarginIsRadius(); } + PX_FORCE_INLINE Ps::aos::FloatV getMargin() const { return mConvex.getMargin(); } + + + template + PX_FORCE_INLINE const Convex& getConvex() const { return static_cast(mConvex); } + + virtual Ps::aos::Vec3V supportPoint(const PxI32 index) const = 0; + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const = 0; + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index) const = 0; + virtual Ps::aos::FloatV getSweepMargin() const = 0; + virtual Ps::aos::Vec3V getCenter() const = 0; + virtual ~GjkConvexBase(){} + + + private: + GjkConvexBase& operator = (const GjkConvexBase&); + protected: + const ConvexV& mConvex; + }; + + struct GjkConvex : public GjkConvexBase + { + GjkConvex(const ConvexV& convex) : GjkConvexBase(convex) { } + + virtual Ps::aos::Vec3V supportPoint(const PxI32 index) const { return doVirtualSupportPoint(index); } + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const { return doVirtualSupport(v); } + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index) const{ return doVirtualSupport(dir, index); } + + virtual Ps::aos::FloatV getSweepMargin() const { return doVirtualGetSweepMargin(); } + + private: + Ps::aos::Vec3V doVirtualSupportPoint(const PxI32 index) const + { + return supportPoint(index); //Call the v-table + } + Ps::aos::Vec3V doVirtualSupport(const Ps::aos::Vec3VArg v) const + { + return support(v); //Call the v-table + } + Ps::aos::Vec3V doVirtualSupport(const Ps::aos::Vec3VArg dir, PxI32& index) const + { + return support(dir, index); //Call the v-table + } + + Ps::aos::FloatV doVirtualGetSweepMargin() const { return getSweepMargin(); } + + GjkConvex& operator = (const GjkConvex&); + }; + + template + struct LocalConvex : public GjkConvex + { + LocalConvex(const Convex& convex) : GjkConvex(convex){} + + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index) const { return getConvex().supportPoint(index); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const { return getConvex().supportLocal(v); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index) const + { + return getConvex().supportLocal(dir, index); + } + + virtual Ps::aos::Vec3V getCenter() const { return getConvex().getCenter(); } + + //ML: we can't force inline function, otherwise win modern will throw compiler error + PX_INLINE LocalConvex::Type > getGjkConvex() const + { + return LocalConvex::Type >(static_cast::Type&>(GjkConvex::mConvex)); + } + + PX_INLINE Ps::aos::FloatV getSweepMargin() const { return getConvex().getSweepMargin(); } + + typedef LocalConvex::Type > ConvexGeomType; + + typedef Convex Type; + + + private: + LocalConvex& operator = (const LocalConvex&); + }; + + template + struct RelativeConvex : public GjkConvex + { + RelativeConvex(const Convex& convex, const Ps::aos::PsMatTransformV& aToB) : GjkConvex(convex), mAToB(aToB), mAToBTransposed(aToB) + { + shdfnd::aos::V3Transpose(mAToBTransposed.rot.col0, mAToBTransposed.rot.col1, mAToBTransposed.rot.col2); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index) const { return mAToB.transform(getConvex().supportPoint(index)); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const { return getConvex().supportRelative(v, mAToB, mAToBTransposed); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index) const + { + return getConvex().supportRelative(dir, mAToB, mAToBTransposed, index); + } + + virtual Ps::aos::Vec3V getCenter() const { return mAToB.transform(getConvex().getCenter()); } + + PX_FORCE_INLINE Ps::aos::PsMatTransformV& getRelativeTransform(){ return mAToB; } + + //ML: we can't force inline function, otherwise win modern will throw compiler error + PX_INLINE RelativeConvex::Type > getGjkConvex() const + { + return RelativeConvex::Type >(static_cast::Type&>(GjkConvex::mConvex), mAToB); + } + + PX_INLINE Ps::aos::FloatV getSweepMargin() const { return getConvex().getSweepMargin(); } + + typedef RelativeConvex::Type > ConvexGeomType; + + typedef Convex Type; + + private: + RelativeConvex& operator = (const RelativeConvex&); + const Ps::aos::PsMatTransformV& mAToB; + Ps::aos::PsMatTransformV mAToBTransposed; // PT: precomputed mAToB transpose (because 'rotate' is faster than 'rotateInv') + }; + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h new file mode 100644 index 000000000..b83364d0c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKUTIL_H +#define GU_GJKUTIL_H + +#include "PsVecMath.h" +#include "CmPhysXCommon.h" + +/* + This file is used to avoid the inner loop cross DLL calls +*/ +namespace physx +{ +namespace Gu +{ + +enum GjkStatus +{ + GJK_NON_INTERSECT, // two shapes doesn't intersect + GJK_CLOSE, // two shapes doesn't intersect and gjk algorithm will return closest point information + GJK_CONTACT, // two shapes overlap within margin + GJK_UNDEFINED, // undefined status + GJK_DEGENERATE, // gjk can't converage + + EPA_CONTACT, // two shapes intersect + EPA_DEGENERATE, // epa can't converage + EPA_FAIL // epa fail to construct an initial polygon to work with +}; + +struct GjkOutput +{ +public: + GjkOutput() + { + using namespace Ps::aos; + closestA = closestB = normal = V3Zero(); + penDep = FZero(); + } + Ps::aos::Vec3V closestA; + Ps::aos::Vec3V closestB; + Ps::aos::Vec3V normal; + Ps::aos::Vec3V searchDir; + Ps::aos::FloatV penDep; +}; + +}//Gu +}//physx + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h new file mode 100644 index 000000000..d8e3d167e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h @@ -0,0 +1,226 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_BOX_H +#define GU_VEC_BOX_H + +/** \addtogroup geomutils +@{ +*/ +#include "foundation/PxTransform.h" +#include "PxPhysXCommonConfig.h" +#include "GuVecConvex.h" +#include "PsVecTransform.h" +#include "GuConvexSupportTable.h" +#include "PxBoxGeometry.h" + +namespace physx +{ +PX_PHYSX_COMMON_API extern const Ps::aos::BoolV boxVertexTable[8]; + +namespace Gu +{ + +#define BOX_MARGIN_RATIO 0.15f +#define BOX_MIN_MARGIN_RATIO 0.05f +#define BOX_SWEEP_MARGIN_RATIO 0.05f + +#define BOX_MARGIN_CCD_RATIO 0.01f +#define BOX_MIN_MARGIN_CCD_RATIO 0.005f + + + class CapsuleV; + + + PX_FORCE_INLINE void CalculateBoxMargin(const Ps::aos::Vec3VArg extent, PxReal& margin, PxReal& minMargin, PxReal& sweepMargin, + const PxReal marginR = BOX_MARGIN_RATIO, const PxReal minMarginR = BOX_MIN_MARGIN_RATIO) + { + using namespace Ps::aos; + + PxReal minExtent; + const FloatV min = V3ExtractMin(extent); + FStore(min, &minExtent); + + margin = minExtent * marginR; + minMargin = minExtent * minMarginR; + sweepMargin = minExtent * BOX_SWEEP_MARGIN_RATIO; + } + + PX_FORCE_INLINE Ps::aos::FloatV CalculateBoxTolerance(const Ps::aos::Vec3VArg extent) + { + using namespace Ps::aos; + + const FloatV r0 = FLoad(0.01f); + const FloatV min = V3ExtractMin(extent);//FMin(V3GetX(extent), FMin(V3GetY(extent), V3GetZ(extent))); + return FMul(min, r0); + } + + //This method is called in the PCM contact gen for the refreshing contacts + PX_FORCE_INLINE Ps::aos::FloatV CalculatePCMBoxMargin(const Ps::aos::Vec3VArg extent, const PxReal toleranceLength, const PxReal toleranceMarginRatio = BOX_MARGIN_RATIO) + { + using namespace Ps::aos; + + const FloatV min = V3ExtractMin(extent);//FMin(V3GetX(extent), FMin(V3GetY(extent), V3GetZ(extent))); + const FloatV toleranceMargin = FLoad(toleranceLength * toleranceMarginRatio); + return FMin(FMul(min, FLoad(BOX_MARGIN_RATIO)), toleranceMargin); + } + + PX_FORCE_INLINE Ps::aos::FloatV CalculateMTDBoxMargin(const Ps::aos::Vec3VArg extent) + { + using namespace Ps::aos; + + const FloatV min = V3ExtractMin(extent);//FMin(V3GetX(extent), FMin(V3GetY(extent), V3GetZ(extent))); + return FMul(min, FLoad(BOX_MARGIN_RATIO)); + } + + class BoxV : public ConvexV + { + public: + + /** + \brief Constructor + */ + PX_INLINE BoxV() : ConvexV(ConvexType::eBOX) + { + } + + PX_FORCE_INLINE BoxV(const Ps::aos::Vec3VArg origin, const Ps::aos::Vec3VArg extent) : + ConvexV(ConvexType::eBOX, origin), extents(extent) + { + CalculateBoxMargin(extent, margin, minMargin, sweepMargin); + } + + //this constructor is used by the CCD system + PX_FORCE_INLINE BoxV(const PxGeometry& geom) : ConvexV(ConvexType::eBOX, Ps::aos::V3Zero()) + { + using namespace Ps::aos; + const PxBoxGeometry& boxGeom = static_cast(geom); + const Vec3V extent = Ps::aos::V3LoadU(boxGeom.halfExtents); + extents = extent; + CalculateBoxMargin(extent, margin, minMargin, sweepMargin, BOX_MARGIN_CCD_RATIO, BOX_MIN_MARGIN_CCD_RATIO); + } + + /** + \brief Destructor + */ + PX_INLINE ~BoxV() + { + } + + PX_FORCE_INLINE void resetMargin(const PxReal toleranceLength) + { + minMargin = PxMin(toleranceLength * BOX_MIN_MARGIN_RATIO, minMargin); + } + + //! Assignment operator + PX_FORCE_INLINE const BoxV& operator=(const BoxV& other) + { + center = other.center; + extents = other.extents; + margin = other.margin; + minMargin = other.minMargin; + sweepMargin = other.sweepMargin; + return *this; + } + + PX_FORCE_INLINE void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts)const + { + using namespace Ps::aos; + + for(PxU32 i=0; i(geom); + + const Vec3V axis = V3Scale(V3UnitX(), FLoad(capsuleGeom.halfHeight)); + const FloatV r = FLoad(capsuleGeom.radius); + p0 = axis; + p1 = V3Neg(axis); + radius = r; + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + bMarginIsRadius = true; + } + + /** + \brief Constructor + + \param _radius Radius of the capsule. + */ + + /** + \brief Destructor + */ + PX_INLINE ~CapsuleV() + { + } + + PX_FORCE_INLINE void initialize(const Ps::aos::Vec3VArg _p0, const Ps::aos::Vec3VArg _p1, const Ps::aos::FloatVArg _radius) + { + using namespace Ps::aos; + radius = _radius; + p0 = _p0; + p1 = _p1; + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + center = V3Scale(V3Add(_p0, _p1), FHalf()); + } + + PX_INLINE Ps::aos::Vec3V computeDirection() const + { + return Ps::aos::V3Sub(p1, p0); + } + + PX_FORCE_INLINE Ps::aos::FloatV getRadius() const + { + return radius; + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index)const + { + return (&p0)[1-index]; + } + + PX_FORCE_INLINE void getIndex(const Ps::aos::BoolV con, PxI32& index)const + { + using namespace Ps::aos; + const VecI32V v = VecI32V_From_BoolV(con); + const VecI32V t = VecI32V_And(v, VecI32V_One()); + PxI32_From_VecI32V(t, &index); + } + + PX_FORCE_INLINE void setCenter(const Ps::aos::Vec3VArg _center) + { + using namespace Ps::aos; + Vec3V offset = V3Sub(_center, center); + center = _center; + + p0 = V3Add(p0, offset); + p1 = V3Add(p1, offset); + } + + //dir, p0 and p1 are in the local space of dir + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir)const + { + using namespace Ps::aos; + //const Vec3V _dir = V3Normalize(dir); + const FloatV dist0 = V3Dot(p0, dir); + const FloatV dist1 = V3Dot(p1, dir); + return V3Sel(FIsGrtr(dist0, dist1), p0, p1); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportRelative(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aToB, const Ps::aos::PsMatTransformV& aTobT) const + { + using namespace Ps::aos; + //transform dir into the local space of a +// const Vec3V _dir = aToB.rotateInv(dir); + const Vec3V _dir = aTobT.rotate(dir); + const Vec3V p = supportLocal(_dir); + //transform p back to the local space of b + return aToB.transform(p); + } + + //dir, p0 and p1 are in the local space of dir + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir, PxI32& index)const + { + using namespace Ps::aos; + + const FloatV dist0 = V3Dot(p0, dir); + const FloatV dist1 = V3Dot(p1, dir); + const BoolV comp = FIsGrtr(dist0, dist1); + getIndex(comp, index); + return V3Sel(comp, p0, p1); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportRelative( const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aToB, + const Ps::aos::PsMatTransformV& aTobT, PxI32& index)const + { + using namespace Ps::aos; + //transform dir into the local space of a +// const Vec3V _dir = aToB.rotateInv(dir); + const Vec3V _dir = aTobT.rotate(dir); + + const Vec3V p = supportLocal(_dir, index); + //transform p back to the local space of b + return aToB.transform(p); + } + + + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(Ps::aos::Vec3V& support, const PxI32& index, const Ps::aos::BoolV comp)const + { + PX_UNUSED(index); + + using namespace Ps::aos; + const Vec3V p = V3Sel(comp, p0, p1); + support = p; + return p; + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepMargin() const + { + return Ps::aos::FZero(); + } + + //don't change the order of p0 and p1, the getPoint function depend on the order + Ps::aos::Vec3V p0; //!< Start of segment + Ps::aos::Vec3V p1; //!< End of segment + Ps::aos::FloatV radius; + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h new file mode 100644 index 000000000..4133bc1e9 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h @@ -0,0 +1,179 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_CONVEX_H +#define GU_VEC_CONVEX_H + +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +#define PX_SUPPORT_INLINE PX_FORCE_INLINE +#define PX_SUPPORT_FORCE_INLINE PX_FORCE_INLINE + +namespace physx +{ +namespace Gu +{ + + struct ConvexType + { + enum Type + { + eCONVEXHULL = 0, + eCONVEXHULLNOSCALE = 1, + eSPHERE = 2, + eBOX = 3, + eCAPSULE = 4, + eTRIANGLE = 5 + }; + }; + + class ConvexV + { + public: + + PX_FORCE_INLINE ConvexV(const ConvexType::Type type_) : type(type_), bMarginIsRadius(false) + { + margin = 0.f; + minMargin = 0.f; + sweepMargin = 0.f; + center = Ps::aos::V3Zero(); + } + + PX_FORCE_INLINE ConvexV(const ConvexType::Type type_, const Ps::aos::Vec3VArg center_) : type(type_), bMarginIsRadius(false) + { + using namespace Ps::aos; + center = center_; + margin = 0.f; + minMargin = 0.f; + sweepMargin = 0.f; + } + + //everytime when someone transform the object, they need to up + PX_FORCE_INLINE void setCenter(const Ps::aos::Vec3VArg _center) + { + center = _center; + } + + PX_FORCE_INLINE void setMargin(const Ps::aos::FloatVArg margin_) + { + Ps::aos::FStore(margin_, &margin); + } + + PX_FORCE_INLINE void setMargin(const PxReal margin_) + { + margin = margin_; + } + + + PX_FORCE_INLINE void setMinMargin(const Ps::aos::FloatVArg minMargin_) + { + Ps::aos::FStore(minMargin_, & minMargin); + } + + PX_FORCE_INLINE void setSweepMargin(const Ps::aos::FloatVArg sweepMargin_) + { + Ps::aos::FStore(sweepMargin_, &sweepMargin); + } + + PX_FORCE_INLINE Ps::aos::Vec3V getCenter()const + { + return center; + } + + PX_FORCE_INLINE Ps::aos::FloatV getMargin() const + { + return Ps::aos::FLoad(margin); + } + + PX_FORCE_INLINE Ps::aos::FloatV getMinMargin() const + { + return Ps::aos::FLoad(minMargin); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepMargin() const + { + return Ps::aos::FLoad(sweepMargin); + } + + PX_FORCE_INLINE ConvexType::Type getType() const + { + return type; + } + + PX_FORCE_INLINE Ps::aos::BoolV isMarginEqRadius()const + { + return Ps::aos::BLoad(bMarginIsRadius); + } + + PX_FORCE_INLINE bool getMarginIsRadius() const + { + return bMarginIsRadius; + } + + PX_FORCE_INLINE PxReal getMarginF() const + { + return margin; + } + + + protected: + ~ConvexV(){} + Ps::aos::Vec3V center; + PxReal margin; //margin is the amount by which we shrunk the shape for a convex or box. If the shape are sphere/capsule, margin is the radius + PxReal minMargin; //minMargin is some percentage of marginBase, which is used to determine the termination condition for gjk + PxReal sweepMargin; //sweepMargin minMargin is some percentage of marginBase, which is used to determine the termination condition for gjkRaycast + ConvexType::Type type; + bool bMarginIsRadius; + }; + + PX_FORCE_INLINE Ps::aos::FloatV getContactEps(const Ps::aos::FloatV& _marginA, const Ps::aos::FloatV& _marginB) + { + using namespace Ps::aos; + + const FloatV ratio = FLoad(0.25f); + const FloatV minMargin = FMin(_marginA, _marginB); + + return FMul(minMargin, ratio); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepContactEps(const Ps::aos::FloatV& _marginA, const Ps::aos::FloatV& _marginB) + { + using namespace Ps::aos; + + const FloatV ratio = FLoad(100.f); + const FloatV minMargin = FAdd(_marginA, _marginB); + + return FMul(minMargin, ratio); + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h new file mode 100644 index 000000000..ff675fdb7 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h @@ -0,0 +1,501 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_CONVEXHULL_H +#define GU_VEC_CONVEXHULL_H + +#include "PxPhysXCommonConfig.h" +#include "GuVecConvex.h" +#include "GuConvexMeshData.h" +#include "GuBigConvexData.h" +#include "GuConvexSupportTable.h" +#include "GuCubeIndex.h" +#include "PsFPU.h" +#include "GuGeometryUnion.h" +#include "PsVecQuat.h" +#include "PxMeshScale.h" + +namespace physx +{ +namespace Gu +{ +#define CONVEX_MARGIN_RATIO 0.1f +#define CONVEX_MIN_MARGIN_RATIO 0.05f +#define CONVEX_SWEEP_MARGIN_RATIO 0.025f +#define TOLERANCE_MARGIN_RATIO 0.08f +#define TOLERANCE_MIN_MARGIN_RATIO 0.05f + + + //This margin is used in Persistent contact manifold + PX_SUPPORT_FORCE_INLINE Ps::aos::FloatV CalculatePCMConvexMargin(const Gu::ConvexHullData* hullData, const Ps::aos::Vec3VArg scale, + const PxReal toleranceLength, const PxReal toleranceRatio = TOLERANCE_MIN_MARGIN_RATIO) + { + + using namespace Ps::aos; + const Vec3V extents= V3Mul(V3LoadU(hullData->mInternal.mExtents), scale); + const FloatV min = V3ExtractMin(extents); + const FloatV toleranceMargin = FLoad(toleranceLength * toleranceRatio); + //ML: 25% of the minimum extents of the internal AABB as this convex hull's margin + return FMin(FMul(min, FLoad(0.25f)), toleranceMargin); + } + + PX_SUPPORT_FORCE_INLINE Ps::aos::FloatV CalculateMTDConvexMargin(const Gu::ConvexHullData* hullData, const Ps::aos::Vec3VArg scale) + { + using namespace Ps::aos; + const Vec3V extents = V3Mul(V3LoadU(hullData->mInternal.mExtents), scale); + const FloatV min = V3ExtractMin(extents); + //ML: 25% of the minimum extents of the internal AABB as this convex hull's margin + return FMul(min, FLoad(0.25f)); + } + + + //This minMargin is used in PCM contact gen + PX_SUPPORT_FORCE_INLINE void CalculateConvexMargin(const Gu::ConvexHullData* hullData, PxReal& margin, PxReal& minMargin, PxReal& sweepMargin, + const Ps::aos::Vec3VArg scale) + { + using namespace Ps::aos; + + const Vec3V extents = V3Mul(V3LoadU(hullData->mInternal.mExtents), scale); + const FloatV min_ = V3ExtractMin(extents); + + PxReal minExtent; + FStore(min_, &minExtent); + + //margin is used as acceptanceTolerance for overlap. + margin = minExtent * CONVEX_MARGIN_RATIO; + //minMargin is used in the GJK termination condition + minMargin = minExtent * CONVEX_MIN_MARGIN_RATIO; + //this is used for sweep(gjkRaycast) + sweepMargin = minExtent * CONVEX_SWEEP_MARGIN_RATIO; + } + + PX_SUPPORT_FORCE_INLINE Ps::aos::Mat33V ConstructSkewMatrix(const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg rotation) + { + using namespace Ps::aos; + Mat33V rot; + QuatGetMat33V(rotation, rot.col0, rot.col1, rot.col2); + Mat33V trans = M33Trnsps(rot); + trans.col0 = V3Scale(trans.col0, V3GetX(scale)); + trans.col1 = V3Scale(trans.col1, V3GetY(scale)); + trans.col2 = V3Scale(trans.col2, V3GetZ(scale)); + return M33MulM33(trans, rot); + } + + PX_SUPPORT_FORCE_INLINE void ConstructSkewMatrix(const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg rotation, Ps::aos::Mat33V& vertex2Shape, Ps::aos::Mat33V& shape2Vertex, Ps::aos::Vec3V& center, const bool idtScale) + { + using namespace Ps::aos; + + PX_ASSERT(!V3AllEq(scale, V3Zero())); + + if(idtScale) + { + //create identity buffer + const Mat33V identity = M33Identity(); + vertex2Shape = identity; + shape2Vertex = identity; + } + else + { + const FloatV scaleX = V3GetX(scale); + const Vec3V invScale = V3Recip(scale); + + //this is uniform scale + if(V3AllEq(V3Splat(scaleX), scale)) + { + vertex2Shape = M33Diagonal(scale); + shape2Vertex = M33Diagonal(invScale); + } + else + { + Mat33V rot; + QuatGetMat33V(rotation, rot.col0, rot.col1, rot.col2); + const Mat33V trans = M33Trnsps(rot); + /* + vertex2shape + skewMat = Inv(R)*Diagonal(scale)*R; + */ + + const Mat33V temp(V3Scale(trans.col0, scaleX), V3Scale(trans.col1, V3GetY(scale)), V3Scale(trans.col2, V3GetZ(scale))); + vertex2Shape = M33MulM33(temp, rot); + + //don't need it in the support function + /* + shape2Vertex + invSkewMat =(invSkewMat)= Inv(R)*Diagonal(1/scale)*R; + */ + + shape2Vertex.col0 = V3Scale(trans.col0, V3GetX(invScale)); + shape2Vertex.col1 = V3Scale(trans.col1, V3GetY(invScale)); + shape2Vertex.col2 = V3Scale(trans.col2, V3GetZ(invScale)); + shape2Vertex = M33MulM33(shape2Vertex, rot); + + //shape2Vertex = M33Inverse(vertex2Shape); + } + + //transform center to shape space + center = M33MulV3(vertex2Shape, center); + } + } + + PX_SUPPORT_FORCE_INLINE Ps::aos::Mat33V ConstructVertex2ShapeMatrix(const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg rotation) + { + using namespace Ps::aos; + Mat33V rot; + QuatGetMat33V(rotation, rot.col0, rot.col1, rot.col2); + const Mat33V trans = M33Trnsps(rot); + /* + vertex2shape + skewMat = Inv(R)*Diagonal(scale)*R; + */ + + const Mat33V temp(V3Scale(trans.col0, V3GetX(scale)), V3Scale(trans.col1, V3GetY(scale)), V3Scale(trans.col2, V3GetZ(scale))); + return M33MulM33(temp, rot); + } + + + class ConvexHullV : public ConvexV + { + + class TinyBitMap + { + public: + PxU32 m[8]; + PX_FORCE_INLINE TinyBitMap() { m[0] = m[1] = m[2] = m[3] = m[4] = m[5] = m[6] = m[7] = 0; } + PX_FORCE_INLINE void set(PxU8 v) { m[v >> 5] |= 1 << (v & 31); } + PX_FORCE_INLINE bool get(PxU8 v) const { return (m[v >> 5] & 1 << (v & 31)) != 0; } + }; + + + public: + /** + \brief Constructor + */ + PX_SUPPORT_INLINE ConvexHullV() : ConvexV(ConvexType::eCONVEXHULL) + { + } + + PX_SUPPORT_INLINE ConvexHullV(const Gu::ConvexHullData* _hullData, const Ps::aos::Vec3VArg _center, const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg scaleRot, + const bool idtScale) : + ConvexV(ConvexType::eCONVEXHULL, _center) + { + using namespace Ps::aos; + + hullData = _hullData; + const PxVec3* PX_RESTRICT tempVerts = _hullData->getHullVertices(); + verts = tempVerts; + numVerts = _hullData->mNbHullVertices; + CalculateConvexMargin(_hullData, margin, minMargin, sweepMargin, scale); + ConstructSkewMatrix(scale, scaleRot, vertex2Shape, shape2Vertex, center, idtScale); + data = _hullData->mBigConvexRawData; + } + + //this is used by CCD system + PX_SUPPORT_INLINE ConvexHullV(const PxGeometry& geom) : ConvexV(ConvexType::eCONVEXHULL, Ps::aos::V3Zero()) + { + using namespace Ps::aos; + const PxConvexMeshGeometryLL& convexGeom = static_cast(geom); + const Gu::ConvexHullData* hData = convexGeom.hullData; + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vRot = QuatVLoadU(&convexGeom.scale.rotation.x); + const bool idtScale = convexGeom.scale.isIdentity(); + + hullData = hData; + const PxVec3* PX_RESTRICT tempVerts = hData->getHullVertices(); + verts = tempVerts; + numVerts = hData->mNbHullVertices; + CalculateConvexMargin(hData, margin, minMargin, sweepMargin, vScale); + ConstructSkewMatrix(vScale, vRot, vertex2Shape, shape2Vertex, center, idtScale); + + data = hData->mBigConvexRawData; + + } + + PX_SUPPORT_INLINE void initialize(const Gu::ConvexHullData* _hullData, const Ps::aos::Vec3VArg _center, const Ps::aos::Vec3VArg scale, + const Ps::aos::QuatVArg scaleRot, const bool idtScale) + { + using namespace Ps::aos; + + const PxVec3* tempVerts = _hullData->getHullVertices(); + CalculateConvexMargin(_hullData, margin, minMargin, sweepMargin, scale); + ConstructSkewMatrix(scale, scaleRot, vertex2Shape, shape2Vertex, center, idtScale); + + verts = tempVerts; + numVerts = _hullData->mNbHullVertices; + //rot = _rot; + + center = _center; + + // searchIndex = 0; + data = _hullData->mBigConvexRawData; + + hullData = _hullData; + if (_hullData->mBigConvexRawData) + { + Ps::prefetchLine(hullData->mBigConvexRawData->mValencies); + Ps::prefetchLine(hullData->mBigConvexRawData->mValencies, 128); + Ps::prefetchLine(hullData->mBigConvexRawData->mAdjacentVerts); + } + } + + + PX_FORCE_INLINE void resetMargin(const PxReal toleranceLength) + { + const PxReal toleranceMinMargin = toleranceLength * TOLERANCE_MIN_MARGIN_RATIO; + const PxReal toleranceMargin = toleranceLength * TOLERANCE_MARGIN_RATIO; + + margin = PxMin(margin, toleranceMargin); + minMargin = PxMin(minMargin, toleranceMinMargin); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index)const + { + using namespace Ps::aos; + + return M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[index])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + } + + PX_NOINLINE PxU32 hillClimbing(const Ps::aos::Vec3VArg _dir)const + { + using namespace Ps::aos; + + const Gu::Valency* valency = data->mValencies; + const PxU8* adjacentVerts = data->mAdjacentVerts; + + //NotSoTinyBitMap visited; + PxU32 smallBitMap[8] = {0,0,0,0,0,0,0,0}; + + // PxU32 index = searchIndex; + PxU32 index = 0; + + { + PxVec3 vertexSpaceDirection; + V3StoreU(_dir, vertexSpaceDirection); + const PxU32 offset = ComputeCubemapNearestOffset(vertexSpaceDirection, data->mSubdiv); + //const PxU32 offset = ComputeCubemapOffset(vertexSpaceDirection, data->mSubdiv); + index = data->mSamples[offset]; + } + + Vec3V maxPoint = V3LoadU_SafeReadW(verts[index]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + FloatV max = V3Dot(maxPoint, _dir); + + PxU32 initialIndex = index; + + do + { + initialIndex = index; + const PxU32 numNeighbours = valency[index].mCount; + const PxU32 offset = valency[index].mOffset; + + for(PxU32 a = 0; a < numNeighbours; ++a) + { + const PxU32 neighbourIndex = adjacentVerts[offset + a]; + + const Vec3V vertex = V3LoadU_SafeReadW(verts[neighbourIndex]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + const FloatV dist = V3Dot(vertex, _dir); + if(FAllGrtr(dist, max)) + { + const PxU32 ind = neighbourIndex>>5; + const PxU32 mask = PxU32(1 << (neighbourIndex & 31)); + if((smallBitMap[ind] & mask) == 0) + { + smallBitMap[ind] |= mask; + max = dist; + index = neighbourIndex; + } + } + } + + }while(index != initialIndex); + + return index; + } + + PX_SUPPORT_INLINE PxU32 bruteForceSearch(const Ps::aos::Vec3VArg _dir)const + { + using namespace Ps::aos; + //brute force + PxVec3 dir; + V3StoreU(_dir, dir); + + PxReal max = verts[0].dot(dir); + PxU32 maxIndex = 0; + + for (PxU32 i = 1; i < numVerts; ++i) + { + const PxReal dist = verts[i].dot(dir); + if (dist > max) + { + max = dist; + maxIndex = i; + } + } + return maxIndex; + } + + //points are in vertex space, _dir in vertex space + PX_NOINLINE PxU32 supportVertexIndex(const Ps::aos::Vec3VArg _dir)const + { + using namespace Ps::aos; + if(data) + return hillClimbing(_dir); + else + return bruteForceSearch(_dir); + } + + //dir is in the vertex space + PX_SUPPORT_INLINE void bruteForceSearchMinMax(const Ps::aos::Vec3VArg _dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + using namespace Ps::aos; + + //brute force + PxVec3 dir; + V3StoreU(_dir, dir); + //get the support point from the orignal margin + PxReal _max = verts[0].dot(dir); + PxReal _min = _max; + + for(PxU32 i = 1; i < numVerts; ++i) + { + const PxReal dist = verts[i].dot(dir); + _max = PxMax(dist, _max); + _min = PxMin(dist, _min); + } + min = FLoad(_min); + max = FLoad(_max); + } + + //This function is used in the full contact manifold generation code, points are in vertex space. + //This function support scaling, _dir is in the shape space + PX_SUPPORT_INLINE void supportVertexMinMax(const Ps::aos::Vec3VArg _dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + using namespace Ps::aos; + + //dir is in the vertex space + const Vec3V dir = M33TrnspsMulV3(vertex2Shape, _dir); + + if(data) + { + const PxU32 maxIndex= hillClimbing(dir); + const PxU32 minIndex= hillClimbing(V3Neg(dir)); + const Vec3V maxPoint= M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[maxIndex])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + const Vec3V minPoint= M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[minIndex])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + min = V3Dot(_dir, minPoint); + max = V3Dot(_dir, maxPoint); + } + else + { + //dir is in the vertex space + bruteForceSearchMinMax(dir, min, max); + } + } + + //This function is used in the full contact manifold generation code + PX_SUPPORT_INLINE void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* _verts)const + { + using namespace Ps::aos; + + for(PxU32 i=0; i(x)) +} + +} + +#endif // diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h new file mode 100644 index 000000000..ca95a2d87 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h @@ -0,0 +1,224 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_PLANE_H +#define GU_VEC_PLANE_H +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxPlane.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +/** +\brief Representation of a plane. + +Plane equation used: a*x + b*y + c*z + d = 0 +*/ +namespace physx +{ +namespace Gu +{ + + class PlaneV + { + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE PlaneV() + { + } + + /** + \brief Constructor from a normal and a distance + */ + PX_FORCE_INLINE PlaneV(const Ps::aos::FloatVArg nx, const Ps::aos::FloatVArg ny, const Ps::aos::FloatVArg nz, const Ps::aos::FloatVArg _d) + { + set(nx, ny, nz, _d); + } + + PX_FORCE_INLINE PlaneV(const PxPlane& plane) + { + using namespace Ps::aos; + const Vec3V _n = V3LoadU(plane.n); + const FloatV _d = FLoad(plane.d); + nd = V4SetW(Vec4V_From_Vec3V(_n), _d); + } + + + /** + \brief Constructor from three points + */ + PX_FORCE_INLINE PlaneV(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2) + { + set(p0, p1, p2); + } + + /** + \brief Constructor from a normal and a distance + */ + PX_FORCE_INLINE PlaneV(const Ps::aos::Vec3VArg _n, const Ps::aos::FloatVArg _d) + { + nd = Ps::aos::V4SetW(Ps::aos::Vec4V_From_Vec3V(_n), _d); + } + + /** + \brief Copy constructor + */ + PX_FORCE_INLINE PlaneV(const PlaneV& plane) : nd(plane.nd) + { + } + + /** + \brief Destructor + */ + PX_FORCE_INLINE ~PlaneV() + { + } + + /** + \brief Sets plane to zero. + */ + PX_FORCE_INLINE PlaneV& setZero() + { + nd = Ps::aos::V4Zero(); + return *this; + } + + PX_FORCE_INLINE PlaneV& set(const Ps::aos::FloatVArg nx, const Ps::aos::FloatVArg ny, const Ps::aos::FloatVArg nz, const Ps::aos::FloatVArg _d) + { + + using namespace Ps::aos; + const Vec3V n= V3Merge(nx, ny, nz); + nd = V4SetW(Vec4V_From_Vec3V(n), _d); + return *this; + } + + PX_FORCE_INLINE PlaneV& set(const Ps::aos::Vec3VArg _normal, Ps::aos::FloatVArg _d) + { + nd = Ps::aos::V4SetW(Ps::aos::Vec4V_From_Vec3V(_normal), _d); + return *this; + } + + /** + \brief Computes the plane equation from 3 points. + */ + PlaneV& set(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2) + { + using namespace Ps::aos; + const Vec3V edge0 = V3Sub(p1, p0); + const Vec3V edge1 = V3Sub(p2, p0); + + const Vec3V n = V3Normalize(V3Cross(edge0, edge1)); + // See comments in set() for computation of d + const FloatV d = FNeg(V3Dot(p0, n)); + nd = V4SetW(Vec4V_From_Vec3V(n), d); + return *this; + } + + /*** + \brief Computes distance, assuming plane is normalized + \sa normalize + */ + PX_FORCE_INLINE Ps::aos::FloatV distance(const Ps::aos::Vec3VArg p) const + { + // Valid for plane equation a*x + b*y + c*z + d = 0 + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return FAdd(V3Dot(p, n), V4GetW(nd)); + } + + PX_FORCE_INLINE Ps::aos::BoolV belongs(const Ps::aos::Vec3VArg p) const + { + using namespace Ps::aos; + const FloatV eps = FLoad(1.0e-7f); + return FIsGrtr(eps, FAbs(distance(p))); + } + + /** + \brief projects p into the plane + */ + PX_FORCE_INLINE Ps::aos::Vec3V project(const Ps::aos::Vec3VArg p) const + { + // Pretend p is on positive side of plane, i.e. plane.distance(p)>0. + // To project the point we have to go in a direction opposed to plane's normal, i.e.: + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return V3Sub(p, V3Scale(n, V4GetW(nd))); + + } + + PX_FORCE_INLINE Ps::aos::FloatV signedDistanceHessianNormalForm(const Ps::aos::Vec3VArg point) const + { + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return FAdd(V3Dot(n, point), V4GetW(nd)); + } + + PX_FORCE_INLINE Ps::aos::Vec3V getNormal() const + { + return Ps::aos::Vec3V_From_Vec4V(nd); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSignDist() const + { + return Ps::aos::V4GetW(nd); + } + + /** + \brief find an arbitrary point in the plane + */ + PX_FORCE_INLINE Ps::aos::Vec3V pointInPlane() const + { + // Project origin (0,0,0) to plane: + // (0) - normal * distance(0) = - normal * ((p|(0)) + d) = -normal*d + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return V3Neg(V3Scale(n, V4GetW(nd))); + } + + PX_FORCE_INLINE void normalize() + { + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + const FloatV denom = FRecip(V3Length(n)); + V4Scale(nd, denom); + } + + Ps::aos::Vec4V nd; //!< The normal to the plan , w store the distance from the origin + }; +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h new file mode 100644 index 000000000..95845b192 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h @@ -0,0 +1,243 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_SPHERE_H +#define GU_VEC_SPHERE_H +/** \addtogroup geomutils +@{ +*/ + +#include "GuVecConvex.h" +#include "GuConvexSupportTable.h" +#include "PxSphereGeometry.h" + +/** +\brief Represents a sphere defined by its center point and radius. +*/ +namespace physx +{ +namespace Gu +{ + class SphereV : public ConvexV + { + public: + /** + \brief Constructor + */ + PX_INLINE SphereV(): ConvexV(ConvexType::eSPHERE) + { + radius = Ps::aos::FZero(); + bMarginIsRadius = true; + } + + PX_INLINE SphereV(const Ps::aos::Vec3VArg _center, const Ps::aos::FloatV _radius) : ConvexV(ConvexType::eSPHERE, _center) + { + using namespace Ps::aos; + radius = _radius; + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + bMarginIsRadius = true; + } + + + /** + \brief Copy constructor + */ + PX_INLINE SphereV(const SphereV& sphere) : ConvexV(ConvexType::eSPHERE), radius(sphere.radius) + { + + margin = sphere.margin; + minMargin = sphere.minMargin; + sweepMargin = sphere.sweepMargin; + bMarginIsRadius = true; + } + + PX_INLINE SphereV(const PxGeometry& geom) : ConvexV(ConvexType::eSPHERE, Ps::aos::V3Zero()) + { + using namespace Ps::aos; + const PxSphereGeometry& sphereGeom = static_cast(geom); + const FloatV r = FLoad(sphereGeom.radius); + radius = r; + margin = sphereGeom.radius; + minMargin = sphereGeom.radius; + sweepMargin = sphereGeom.radius; + bMarginIsRadius = true; + } + + /** + \brief Destructor + */ + PX_INLINE ~SphereV() + { + } + + PX_INLINE void setV(const Ps::aos::Vec3VArg _center, const Ps::aos::FloatVArg _radius) + { + center = _center; + radius = _radius; + } + + /** + \brief Checks the sphere is valid. + + \return true if the sphere is valid + */ + PX_INLINE bool isValid() const + { + // Consistency condition for spheres: Radius >= 0.0f + using namespace Ps::aos; + return BAllEqTTTT(FIsGrtrOrEq(radius, FZero())) != 0; + } + + /** + \brief Tests if a point is contained within the sphere. + + \param[in] p the point to test + \return true if inside the sphere + */ + PX_INLINE bool contains(const Ps::aos::Vec3VArg p) const + { + using namespace Ps::aos; + const FloatV rr = FMul(radius, radius); + const FloatV cc = V3LengthSq(V3Sub(center, p)); + return FAllGrtrOrEq(rr, cc) != 0; + } + + /** + \brief Tests if a sphere is contained within the sphere. + + \param sphere [in] the sphere to test + \return true if inside the sphere + */ + PX_INLINE bool contains(const SphereV& sphere) const + { + using namespace Ps::aos; + + const Vec3V centerDif= V3Sub(center, sphere.center); + const FloatV radiusDif = FSub(radius, sphere.radius); + const FloatV cc = V3Dot(centerDif, centerDif); + const FloatV rr = FMul(radiusDif, radiusDif); + + const BoolV con0 = FIsGrtrOrEq(radiusDif, FZero());//might contain + const BoolV con1 = FIsGrtr(rr, cc);//return true + return BAllEqTTTT(BAnd(con0, con1))==1; + } + + /** + \brief Tests if a box is contained within the sphere. + + \param minimum [in] minimum value of the box + \param maximum [in] maximum value of the box + \return true if inside the sphere + */ + PX_INLINE bool contains(const Ps::aos::Vec3VArg minimum, const Ps::aos::Vec3VArg maximum) const + { + + //compute the sphere which wrap around the box + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV half = FHalf(); + + const Vec3V boxSphereCenter = V3Scale(V3Add(maximum, minimum), half); + const Vec3V v = V3Scale(V3Sub(maximum, minimum), half); + const FloatV boxSphereR = V3Length(v); + + const Vec3V w = V3Sub(center, boxSphereCenter); + const FloatV wLength = V3Length(w); + const FloatV dif = FSub(FSub(radius, wLength), boxSphereR); + + return FAllGrtrOrEq(dif, zero) != 0; + } + + /** + \brief Tests if the sphere intersects another sphere + + \param sphere [in] the other sphere + \return true if spheres overlap + */ + PX_INLINE bool intersect(const SphereV& sphere) const + { + using namespace Ps::aos; + const Vec3V centerDif = V3Sub(center, sphere.center); + const FloatV cc = V3Dot(centerDif, centerDif); + const FloatV r = FAdd(radius, sphere.radius); + const FloatV rr = FMul(r, r); + return FAllGrtrOrEq(rr, cc) != 0; + } + + //return point in local space + PX_FORCE_INLINE Ps::aos::Vec3V getPoint(const PxU8) + { + return Ps::aos::V3Zero(); + } + // + //sweep code need to have full version + PX_FORCE_INLINE Ps::aos::Vec3V supportSweep(const Ps::aos::Vec3VArg dir)const + { + using namespace Ps::aos; + const Vec3V _dir = V3Normalize(dir); + return V3ScaleAdd(_dir, radius, center); + } + + //make the support function the same as support margin + PX_FORCE_INLINE Ps::aos::Vec3V support(const Ps::aos::Vec3VArg)const + { + return center;//_margin is the same as radius + } + + + PX_FORCE_INLINE Ps::aos::Vec3V supportMargin(const Ps::aos::Vec3VArg dir, const Ps::aos::FloatVArg _margin, Ps::aos::Vec3V& support)const + { + PX_UNUSED(_margin); + PX_UNUSED(dir); + + support = center; + return center;//_margin is the same as radius + } + + PX_FORCE_INLINE Ps::aos::BoolV isMarginEqRadius()const + { + return Ps::aos::BTTTT(); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepMargin() const + { + return Ps::aos::FZero(); + } + + + Ps::aos::FloatV radius; //!< Sphere's center, w component is radius + + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h new file mode 100644 index 000000000..6dd676ab5 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h @@ -0,0 +1,268 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_TRIANGLE_H +#define GU_VEC_TRIANGLE_H +/** \addtogroup geomutils + @{ +*/ + +#include "GuVecConvex.h" +#include "GuConvexSupportTable.h" +#include "GuDistancePointTriangleSIMD.h" + +namespace physx +{ +namespace Gu +{ + + + class TriangleV : public ConvexV + { + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE TriangleV() : ConvexV(ConvexType::eTRIANGLE) + { + margin = 0.02f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + /** + \brief Constructor + + \param[in] p0 Point 0 + \param[in] p1 Point 1 + \param[in] p2 Point 2 + */ + + PX_FORCE_INLINE TriangleV(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2): ConvexV(ConvexType::eTRIANGLE) + { + using namespace Ps::aos; + //const FloatV zero = FZero(); + const FloatV num = FLoad(0.333333f); + center = V3Scale(V3Add(V3Add(p0, p1), p2), num); + verts[0] = p0; + verts[1] = p1; + verts[2] = p2; + margin = 0.f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + + PX_FORCE_INLINE TriangleV(const PxVec3* pts) : ConvexV(ConvexType::eTRIANGLE) + { + using namespace Ps::aos; + const Vec3V p0 = V3LoadU(pts[0]); + const Vec3V p1 = V3LoadU(pts[1]); + const Vec3V p2 = V3LoadU(pts[2]); + const FloatV num = FLoad(0.333333f); + center = V3Scale(V3Add(V3Add(p0, p1), p2), num); + verts[0] = p0; + verts[1] = p1; + verts[2] = p2; + margin = 0.f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + + /** + \brief Copy constructor + + \param[in] triangle Tri to copy + */ + PX_FORCE_INLINE TriangleV(const Gu::TriangleV& triangle) : ConvexV(ConvexType::eTRIANGLE) + { + using namespace Ps::aos; + verts[0] = triangle.verts[0]; + verts[1] = triangle.verts[1]; + verts[2] = triangle.verts[2]; + + center = triangle.center; + margin = 0.f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + /** + \brief Destructor + */ + PX_FORCE_INLINE ~TriangleV() + { + } + + PX_FORCE_INLINE void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* vertexs)const + { + using namespace Ps::aos; + + for(PxU32 i=0; i +class EntityReport +{ + public: + + virtual ~EntityReport() {} + + virtual PxAgain onEvent(PxU32 nbEntities, T* entities) = 0; +}; + + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp new file mode 100644 index 000000000..d7e795d61 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp @@ -0,0 +1,711 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxMemory.h" +#include "PsIntrinsics.h" +#include "GuHeightField.h" +#include "PsAllocator.h" +#include "PsUtilities.h" +#include "GuMeshFactory.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "CmBitMap.h" +#include "PsFoundation.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Gu::HeightField::HeightField(GuMeshFactory* meshFactory) +: PxHeightField(PxConcreteType::eHEIGHTFIELD, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mSampleStride (0) +, mNbSamples (0) +, mMinHeight (0.0f) +, mMaxHeight (0.0f) +, mModifyCount (0) +, mMeshFactory (meshFactory) +{ + mData.format = PxHeightFieldFormat::eS16_TM; + mData.rows = 0; + mData.columns = 0; + mData.convexEdgeThreshold = 0; + mData.flags = PxHeightFieldFlags(); + mData.samples = NULL; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Gu::HeightField::HeightField(GuMeshFactory& factory, Gu::HeightFieldData& data) +: PxHeightField(PxConcreteType::eHEIGHTFIELD, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mSampleStride (0) +, mNbSamples (0) +, mMinHeight (0.0f) +, mMaxHeight (0.0f) +, mModifyCount (0) +, mMeshFactory (&factory) +{ + mData = data; + data.samples = NULL; // set to null so that we don't release the memory +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Gu::HeightField::~HeightField() +{ + releaseMemory(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PX_SERIALIZATION +void Gu::HeightField::onRefCountZero() +{ + PX_ASSERT(mMeshFactory); + if(mMeshFactory->removeHeightField(*this)) + { + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, PxConcreteType::eHEIGHTFIELD); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::HeightField::onRefCountZero: double deletion detected!"); +} + +void Gu::HeightField::exportExtraData(PxSerializationContext& stream) +{ + // PT: warning, order matters for the converter. Needs to export the base stuff first + const PxU32 size = mData.rows * mData.columns * sizeof(PxHeightFieldSample); + stream.alignData(PX_SERIAL_ALIGN); // PT: generic align within the generic allocator + stream.writeData(mData.samples, size); +} + +void Gu::HeightField::importExtraData(PxDeserializationContext& context) +{ + mData.samples = context.readExtraData(mData.rows * mData.columns); +} + +Gu::HeightField* Gu::HeightField::createObject(PxU8*& address, PxDeserializationContext& context) +{ + HeightField* obj = new (address) HeightField(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(HeightField); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +//~PX_SERIALIZATION + +void Gu::HeightField::release() +{ + decRefCount(); +} + +void Gu::HeightField::acquireReference() +{ + incRefCount(); +} + +PxU32 Gu::HeightField::getReferenceCount() const +{ + return getRefCount(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Gu::HeightField::modifySamples(PxI32 startCol, PxI32 startRow, const PxHeightFieldDesc& desc, bool shrinkBounds) +{ + const PxU32 nbCols = getNbColumns(); + const PxU32 nbRows = getNbRows(); + PX_CHECK_AND_RETURN_NULL(desc.format == mData.format, "Gu::HeightField::modifySamples: desc.format mismatch"); + //PX_CHECK_AND_RETURN_NULL(startCol + desc.nbColumns <= nbCols, + // "Gu::HeightField::modifySamples: startCol + nbColumns out of range"); + //PX_CHECK_AND_RETURN_NULL(startRow + desc.nbRows <= nbRows, + // "Gu::HeightField::modifySamples: startRow + nbRows out of range"); + //PX_CHECK_AND_RETURN_NULL(desc.samples.stride == mSampleStride, "Gu::HeightField::modifySamples: desc.samples.stride mismatch"); + + // by default bounds don't shrink since the whole point of this function is to avoid modifying the whole HF + // unless shrinkBounds is specified. then the bounds will be fully recomputed later + PxReal minHeight = mMinHeight; + PxReal maxHeight = mMaxHeight; + PxU32 hiRow = PxMin(PxU32(PxMax(0, startRow + PxI32(desc.nbRows))), nbRows); + PxU32 hiCol = PxMin(PxU32(PxMax(0, startCol + PxI32(desc.nbColumns))), nbCols); + for (PxU32 row = PxU32(PxMax(startRow, 0)); row < hiRow; row++) + { + for (PxU32 col = PxU32(PxMax(startCol, 0)); col < hiCol; col++) + { + const PxU32 vertexIndex = col + row*nbCols; + PxHeightFieldSample* targetSample = &mData.samples[vertexIndex]; + + // update target sample from source sample + const PxHeightFieldSample& sourceSample = + (reinterpret_cast(desc.samples.data))[col - startCol + (row - startRow) * desc.nbColumns]; + *targetSample = sourceSample; + + if(isCollisionVertexPreca(vertexIndex, row, col, PxHeightFieldMaterial::eHOLE)) + targetSample->materialIndex1.setBit(); + else + targetSample->materialIndex1.clearBit(); + + // grow (but not shrink) the height extents + const PxReal h = getHeight(vertexIndex); + minHeight = physx::intrinsics::selectMin(h, minHeight); + maxHeight = physx::intrinsics::selectMax(h, maxHeight); + } + } + + if (shrinkBounds) + { + // do a full recompute on vertical bounds to allow shrinking + minHeight = PX_MAX_REAL; + maxHeight = -PX_MAX_REAL; + // have to recompute the min&max from scratch... + for (PxU32 vertexIndex = 0; vertexIndex < nbRows * nbCols; vertexIndex ++) + { + // update height extents + const PxReal h = getHeight(vertexIndex); + minHeight = physx::intrinsics::selectMin(h, minHeight); + maxHeight = physx::intrinsics::selectMax(h, maxHeight); + } + } + mMinHeight = minHeight; + mMaxHeight = maxHeight; + + // update local space aabb + CenterExtents& bounds = mData.mAABB; + bounds.mCenter.y = (maxHeight + minHeight)*0.5f; + bounds.mExtents.y = (maxHeight - minHeight)*0.5f; + + mModifyCount++; + + return true; +} + +bool Gu::HeightField::load(PxInputStream& stream) +{ + // release old memory + releaseMemory(); + + // Import header + PxU32 version; + bool endian; + if(!readHeader('H', 'F', 'H', 'F', version, endian, stream)) + return false; + + // load mData + mData.rows = readDword(endian, stream); + mData.columns = readDword(endian, stream); + mData.rowLimit = readFloat(endian, stream); + mData.colLimit = readFloat(endian, stream); + mData.nbColumns = readFloat(endian, stream); + const float thickness = readFloat(endian, stream); + PX_UNUSED(thickness); + mData.convexEdgeThreshold = readFloat(endian, stream); + + PxU16 flags = readWord(endian, stream); + mData.flags = PxHeightFieldFlags(flags); + + PxU32 format = readDword(endian, stream); + mData.format = PxHeightFieldFormat::Enum(format); + + PxBounds3 minMaxBounds; + minMaxBounds.minimum.x = readFloat(endian, stream); + minMaxBounds.minimum.y = readFloat(endian, stream); + minMaxBounds.minimum.z = readFloat(endian, stream); + minMaxBounds.maximum.x = readFloat(endian, stream); + minMaxBounds.maximum.y = readFloat(endian, stream); + minMaxBounds.maximum.z = readFloat(endian, stream); + mData.mAABB = CenterExtents(minMaxBounds); + + mSampleStride = readDword(endian, stream); + mNbSamples = readDword(endian, stream); + mMinHeight = readFloat(endian, stream); + mMaxHeight = readFloat(endian, stream); + + // allocate height samples + mData.samples = NULL; + const PxU32 nbVerts = mData.rows * mData.columns; + if (nbVerts > 0) + { + mData.samples = reinterpret_cast(PX_ALLOC(nbVerts*sizeof(PxHeightFieldSample), "PxHeightFieldSample")); + if (mData.samples == NULL) + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Gu::HeightField::load: PX_ALLOC failed!"); + return false; + } + stream.read(mData.samples, mNbSamples*sizeof(PxHeightFieldSample)); + if (endian) + for(PxU32 i = 0; i < mNbSamples; i++) + { + PxHeightFieldSample& s = mData.samples[i]; + PX_ASSERT(sizeof(PxU16) == sizeof(s.height)); + flip(s.height); + } + } + + return true; +} + +bool Gu::HeightField::loadFromDesc(const PxHeightFieldDesc& desc) +{ + // verify descriptor + PX_CHECK_AND_RETURN_NULL(desc.isValid(), "Gu::HeightField::loadFromDesc: desc.isValid() failed!"); + + // release old memory + releaseMemory(); + + // copy trivial data + mData.format = desc.format; + mData.rows = desc.nbRows; + mData.columns = desc.nbColumns; + mData.convexEdgeThreshold = desc.convexEdgeThreshold; + mData.flags = desc.flags; + mSampleStride = desc.samples.stride; + + // PT: precompute some data - mainly for Xbox + mData.rowLimit = float(mData.rows - 2); + mData.colLimit = float(mData.columns - 2); + mData.nbColumns = float(desc.nbColumns); + + // allocate and copy height samples + // compute extents too + mData.samples = NULL; + const PxU32 nbVerts = desc.nbRows * desc.nbColumns; + mMinHeight = PX_MAX_REAL; + mMaxHeight = -PX_MAX_REAL; + + if(nbVerts > 0) + { + mData.samples = reinterpret_cast(PX_ALLOC(nbVerts*sizeof(PxHeightFieldSample), "PxHeightFieldSample")); + if(!mData.samples) + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Gu::HeightField::load: PX_ALLOC failed!"); + return false; + } + const PxU8* PX_RESTRICT src = reinterpret_cast(desc.samples.data); + PxHeightFieldSample* PX_RESTRICT dst = mData.samples; + PxI16 minHeight = PX_MAX_I16; + PxI16 maxHeight = PX_MIN_I16; + for(PxU32 i=0;i(src); + *dst++ = sample; + const PxI16 height = sample.height; + minHeight = height < minHeight ? height : minHeight; + maxHeight = height > maxHeight ? height : maxHeight; + src += desc.samples.stride; + } + mMinHeight = PxReal(minHeight); + mMaxHeight = PxReal(maxHeight); + } + + PX_ASSERT(mMaxHeight >= mMinHeight); + + parseTrianglesForCollisionVertices(PxHeightFieldMaterial::eHOLE); + +// PT: "mNbSamples" only used by binary converter + mNbSamples = mData.rows * mData.columns; + + //Compute local space aabb. + PxBounds3 bounds; + bounds.minimum.y = getMinHeight(); + bounds.maximum.y = getMaxHeight(); + + bounds.minimum.x = 0; + bounds.maximum.x = PxReal(getNbRowsFast() - 1); + bounds.minimum.z = 0; + bounds.maximum.z = PxReal(getNbColumnsFast() - 1); + mData.mAABB=bounds; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxU32 Gu::HeightField::saveCells(void* destBuffer, PxU32 destBufferSize) const +{ + PxU32 n = mData.columns * mData.rows * sizeof(PxHeightFieldSample); + if (n > destBufferSize) n = destBufferSize; + PxMemCopy(destBuffer, mData.samples, n); + + return n; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PX_PHYSX_COMMON_API void Gu::HeightField::releaseMemory() +{ +// PX_SERIALIZATION + if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) +//~PX_SERIALIZATION + { + PX_FREE(mData.samples); + mData.samples = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: TODO: use those faster functions everywhere +namespace physx +{ + +PX_PHYSX_COMMON_API PxU32 getVertexEdgeIndices(const Gu::HeightField& heightfield, PxU32 vertexIndex, PxU32 row, PxU32 column, EdgeData edgeIndices[8]) +{ + const PxU32 nbColumns = heightfield.getData().columns; + const PxU32 nbRows = heightfield.getData().rows; + PX_ASSERT((vertexIndex / nbColumns)==row); + PX_ASSERT((vertexIndex % nbColumns)==column); + + PxU32 count = 0; + + if (row > 0) + { +// edgeIndices[count++] = 3 * (vertexIndex - nbColumns) + 2; + const PxU32 cell = vertexIndex - nbColumns; + edgeIndices[count].edgeIndex = 3 * cell + 2; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row-1; + edgeIndices[count].column = column; + count++; + } + + if (column < nbColumns-1) + { + if (row > 0) + { + if (!heightfield.isZerothVertexShared(vertexIndex - nbColumns)) + { +// edgeIndices[count++] = 3 * (vertexIndex - nbColumns) + 1; + const PxU32 cell = vertexIndex - nbColumns; + edgeIndices[count].edgeIndex = 3 * cell + 1; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row-1; + edgeIndices[count].column = column; + count++; + } + } +// edgeIndices[count++] = 3 * vertexIndex; + edgeIndices[count].edgeIndex = 3 * vertexIndex; + edgeIndices[count].cell = vertexIndex; + edgeIndices[count].row = row; + edgeIndices[count].column = column; + count++; + + if (row < nbRows - 1) + { + if (heightfield.isZerothVertexShared(vertexIndex)) + { +// edgeIndices[count++] = 3 * vertexIndex + 1; + edgeIndices[count].edgeIndex = 3 * vertexIndex + 1; + edgeIndices[count].cell = vertexIndex; + edgeIndices[count].row = row; + edgeIndices[count].column = column; + count++; + } + } + } + + if (row < nbRows - 1) + { +// edgeIndices[count++] = 3 * vertexIndex + 2; + edgeIndices[count].edgeIndex = 3 * vertexIndex + 2; + edgeIndices[count].cell = vertexIndex; + edgeIndices[count].row = row; + edgeIndices[count].column = column; + count++; + } + + if (column > 0) + { + if (row < nbRows - 1) + { + if (!heightfield.isZerothVertexShared(vertexIndex - 1)) + { +// edgeIndices[count++] = 3 * (vertexIndex - 1) + 1; + const PxU32 cell = vertexIndex - 1; + edgeIndices[count].edgeIndex = 3 * cell + 1; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row; + edgeIndices[count].column = column-1; + count++; + } + } +// edgeIndices[count++] = 3 * (vertexIndex - 1); + const PxU32 cell = vertexIndex - 1; + edgeIndices[count].edgeIndex = 3 * cell; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row; + edgeIndices[count].column = column-1; + count++; + if (row > 0) + { + if (heightfield.isZerothVertexShared(vertexIndex - nbColumns - 1)) + { +// edgeIndices[count++] = 3 * (vertexIndex - nbColumns - 1) + 1; + const PxU32 cell1 = vertexIndex - nbColumns - 1; + edgeIndices[count].edgeIndex = 3 * cell1 + 1; + edgeIndices[count].cell = cell1; + edgeIndices[count].row = row-1; + edgeIndices[count].column = column-1; + count++; + } + } + } + return count; +} + +PX_PHYSX_COMMON_API PxU32 getEdgeTriangleIndices(const Gu::HeightField& heightfield, const EdgeData& edgeData, PxU32* PX_RESTRICT triangleIndices) +{ + const PxU32 nbColumns = heightfield.getData().columns; + const PxU32 nbRows = heightfield.getData().rows; + + const PxU32 edgeIndex = edgeData.edgeIndex; + const PxU32 cell = edgeData.cell; + const PxU32 row = edgeData.row; + const PxU32 column = edgeData.column; + PX_ASSERT(cell==edgeIndex / 3); + PX_ASSERT(row==cell / nbColumns); + PX_ASSERT(column==cell % nbColumns); + PxU32 count = 0; + switch (edgeIndex - cell*3) + { + case 0: + if (column < nbColumns - 1) + { + if (row > 0) + { + if (heightfield.isZerothVertexShared(cell - nbColumns)) + triangleIndices[count++] = ((cell - nbColumns) << 1); + else + triangleIndices[count++] = ((cell - nbColumns) << 1) + 1; + } + if (row < nbRows - 1) + { + if (heightfield.isZerothVertexShared(cell)) + triangleIndices[count++] = (cell << 1) + 1; + else + triangleIndices[count++] = cell << 1; + } + } + break; + case 1: + if ((row < nbRows - 1) && (column < nbColumns - 1)) + { + triangleIndices[count++] = cell << 1; + triangleIndices[count++] = (cell << 1) + 1; + } + break; + case 2: + if (row < nbRows - 1) + { + if (column > 0) + { + triangleIndices[count++] = ((cell - 1) << 1) + 1; + } + if (column < nbColumns - 1) + { + triangleIndices[count++] = cell << 1; + } + } + break; + } + + return count; +} + +} + +PX_FORCE_INLINE PxU32 anyHole(PxU32 doubleMatIndex, PxU16 holeMaterialIndex) +{ + return PxU32((doubleMatIndex & 0xFFFF) == holeMaterialIndex) | (PxU32(doubleMatIndex >> 16) == holeMaterialIndex); +} + +void Gu::HeightField::parseTrianglesForCollisionVertices(PxU16 holeMaterialIndex) +{ + const PxU32 nbColumns = getNbColumnsFast(); + const PxU32 nbRows = getNbRowsFast(); + + Cm::BitMap rowHoles[2]; + rowHoles[0].resizeAndClear(nbColumns + 1); + rowHoles[1].resizeAndClear(nbColumns + 1); + + for (PxU32 iCol = 0; iCol < nbColumns; iCol++) + { + if (anyHole(getMaterialIndex01(iCol), holeMaterialIndex)) + { + rowHoles[0].set(iCol); + rowHoles[0].set(iCol + 1); + } + PxU32 vertIndex = iCol; + if(isCollisionVertexPreca(vertIndex, 0, iCol, holeMaterialIndex)) + mData.samples[vertIndex].materialIndex1.setBit(); + else + mData.samples[vertIndex].materialIndex1.clearBit(); + } + + PxU32 nextRow = 1, currentRow = 0; + for (PxU32 iRow = 1; iRow < nbRows; iRow++) + { + PxU32 rowOffset = iRow*nbColumns; + for (PxU32 iCol = 0; iCol < nbColumns; iCol++) + { + const PxU32 vertIndex = rowOffset + iCol; // column index plus current row offset (vertex/cell index) + if(anyHole(getMaterialIndex01(vertIndex), holeMaterialIndex)) + { + rowHoles[currentRow].set(iCol); + rowHoles[currentRow].set(iCol + 1); + rowHoles[nextRow].set(iCol); + rowHoles[nextRow].set(iCol + 1); + } + + if ((iCol == 0) || (iCol == nbColumns - 1) || (iRow == nbRows - 1) || rowHoles[currentRow].test(iCol)) + { + if(isCollisionVertexPreca(vertIndex, iRow, iCol, holeMaterialIndex)) + mData.samples[vertIndex].materialIndex1.setBit(); + else + mData.samples[vertIndex].materialIndex1.clearBit(); + } else + { + if (isConvexVertex(vertIndex, iRow, iCol)) + mData.samples[vertIndex].materialIndex1.setBit(); + } + } + + rowHoles[currentRow].clear(); + + // swap prevRow and prevPrevRow + nextRow ^= 1; currentRow ^= 1; + } +} + +bool Gu::HeightField::isSolidVertex(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex, bool& nbSolid) const +{ + // check if solid and boundary + // retrieve edge indices for current vertexIndex + EdgeData edgeIndices[8]; + const PxU32 edgeCount = ::getVertexEdgeIndices(*this, vertexIndex, row, column, edgeIndices); + + PxU32 faceCounts[8]; + PxU32 faceIndices[2 * 8]; + PxU32* dst = faceIndices; + for (PxU32 i = 0; i < edgeCount; i++) + { + faceCounts[i] = ::getEdgeTriangleIndices(*this, edgeIndices[i], dst); + dst += 2; + } + + nbSolid = false; + const PxU32* currentfaceIndices = faceIndices; // parallel array of pairs of face indices per edge index + for (PxU32 i = 0; i < edgeCount; i++) + { + if (faceCounts[i] > 1) + { + const PxU16& material0 = getTriangleMaterial(currentfaceIndices[0]); + const PxU16& material1 = getTriangleMaterial(currentfaceIndices[1]); + // ptchernev TODO: this is a bit arbitrary + if (material0 != holeMaterialIndex) + { + nbSolid = true; + if (material1 == holeMaterialIndex) + return true; // edge between solid and hole => return true + } + if (material1 != holeMaterialIndex) + { + nbSolid = true; + if (material0 == holeMaterialIndex) + return true; // edge between hole and solid => return true + } + } + else + { + if (getTriangleMaterial(currentfaceIndices[0]) != holeMaterialIndex) + return true; + } + currentfaceIndices += 2; // 2 face indices per edge + } + return false; +} + +bool Gu::HeightField::isCollisionVertexPreca(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(isValidVertex(vertexIndex)); +#endif + PX_ASSERT((vertexIndex / getNbColumnsFast()) == row); + PX_ASSERT((vertexIndex % getNbColumnsFast()) == column); + + // check boundary conditions - boundary edges shouldn't produce collision with eNO_BOUNDARY_EDGES flag + if(mData.flags & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) + if ((row == 0) || (column == 0) || (row >= mData.rows-1) || (column >= mData.columns-1)) + return false; + + bool nbSolid; + if(isSolidVertex(vertexIndex, row, column, holeMaterialIndex, nbSolid)) + return true; + + // return true if it is boundary or solid and convex + return (nbSolid && isConvexVertex(vertexIndex, row, column)); +} + +// AP: this naming is confusing and inconsistent with return value. the function appears to compute vertex coord rather than cell coords +// it would most likely be better to stay in cell coords instead, since fractional vertex coords just do not make any sense +PxU32 Gu::HeightField::computeCellCoordinates(PxReal x, PxReal z, PxReal& fracX, PxReal& fracZ) const +{ + namespace i = physx::intrinsics; + + x = i::selectMax(x, 0.0f); + z = i::selectMax(z, 0.0f); +#if 0 // validation code for scaled clamping epsilon computation + for (PxReal ii = 1.0f; ii < 100000.0f; ii+=1.0f) + { + PX_UNUSED(ii); + PX_ASSERT(PxFloor(ii+(1-1e-7f*ii)) == ii); + } +#endif + PxF32 epsx = 1.0f - PxAbs(x+1.0f) * 1e-6f; // epsilon needs to scale with values of x,z... + PxF32 epsz = 1.0f - PxAbs(z+1.0f) * 1e-6f; + PxF32 x1 = i::selectMin(x, mData.rowLimit+epsx); + PxF32 z1 = i::selectMin(z, mData.colLimit+epsz); + x = PxFloor(x1); + fracX = x1 - x; + z = PxFloor(z1); + fracZ = z1 - z; + PX_ASSERT(x >= 0.0f && x < PxF32(mData.rows)); + PX_ASSERT(z >= 0.0f && z < PxF32(mData.columns)); + + const PxU32 vertexIndex = PxU32(x * (mData.nbColumns) + z); + PX_ASSERT(vertexIndex < (mData.rows)*(mData.columns)); + + return vertexIndex; +} + diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h new file mode 100644 index 000000000..7a1d36330 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h @@ -0,0 +1,1266 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HEIGHTFIELD_H +#define GU_HEIGHTFIELD_H + +#include "PsUserAllocated.h" +#include "CmRefCountable.h" +#include "PsMathUtils.h" +#include "GuSphere.h" +#include "PxHeightFieldSample.h" +#include "PxHeightFieldDesc.h" +#include "GuHeightFieldData.h" +#include "PxHeightField.h" + +//#define PX_HEIGHTFIELD_VERSION 0 +#define PX_HEIGHTFIELD_VERSION 1 // tiled version that was needed for PS3 only has been removed + +namespace physx +{ +class GuMeshFactory; +class PxHeightFieldDesc; +} + +namespace physx +{ +namespace Gu +{ +class HeightField : public PxHeightField, public Ps::UserAllocated, public Cm::RefCountable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + HeightField(PxBaseFlags baseFlags) : PxHeightField(baseFlags), Cm::RefCountable(PxEmpty), mData(PxEmpty), mModifyCount(0) {} + + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext&); + PX_PHYSX_COMMON_API void importExtraData(PxDeserializationContext& context); + PX_FORCE_INLINE void setMeshFactory(GuMeshFactory* f) { mMeshFactory = f; } + PX_PHYSX_COMMON_API static HeightField* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); + void resolveReferences(PxDeserializationContext&) {} + + virtual void requiresObjects(PxProcessPxBaseCallback&){} +//~PX_SERIALIZATION + + PX_PHYSX_COMMON_API HeightField(GuMeshFactory* meshFactory); + PX_PHYSX_COMMON_API HeightField(GuMeshFactory& factory, Gu::HeightFieldData& data); + + // PxHeightField + PX_PHYSX_COMMON_API virtual void release(); + PX_PHYSX_COMMON_API virtual PxU32 saveCells(void* destBuffer, PxU32 destBufferSize) const; + PX_PHYSX_COMMON_API virtual bool modifySamples(PxI32 startCol, PxI32 startRow, const PxHeightFieldDesc& subfieldDesc, bool shrinkBounds); + PX_PHYSX_COMMON_API virtual PxU32 getNbRows() const { return mData.rows; } + PX_PHYSX_COMMON_API virtual PxU32 getNbColumns() const { return mData.columns; } + PX_PHYSX_COMMON_API virtual PxHeightFieldFormat::Enum getFormat() const { return mData.format; } + PX_PHYSX_COMMON_API virtual PxU32 getSampleStride() const { return sizeof(PxHeightFieldSample); } + PX_PHYSX_COMMON_API virtual PxReal getConvexEdgeThreshold() const { return mData.convexEdgeThreshold; } + PX_PHYSX_COMMON_API virtual PxHeightFieldFlags getFlags() const { return mData.flags; } + PX_PHYSX_COMMON_API virtual PxReal getHeight(PxReal x, PxReal z) const { return getHeightInternal(x, z); } + + PX_PHYSX_COMMON_API virtual void acquireReference(); + PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const; + //~PxHeightField + + // RefCountable + PX_PHYSX_COMMON_API virtual void onRefCountZero(); + //~RefCountable + PX_PHYSX_COMMON_API virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const + { + return getTriangleMaterial(triangleIndex); + } + + PX_PHYSX_COMMON_API virtual PxVec3 getTriangleNormal(PxTriangleID triangleIndex) const + { + return getTriangleNormalInternal(triangleIndex); + } + + PX_PHYSX_COMMON_API virtual const PxHeightFieldSample& getSample(PxU32 row, PxU32 column) const + { + const PxU32 cell = row * getNbColumnsFast() + column; + return getSample(cell); + } + /** + \brief Returns the number of times the heightfield data has been modified + + Each time the heightfield is changed via 'modifySamples' this increments a counter. This method will return + the number of times the heightfield has been modified so that rendering code can know whether or not it needs to + rebuild the graphics representation of the mesh. + + \return the number of times the heightfield sample data has been modified. + */ + PX_PHYSX_COMMON_API virtual PxU32 getTimestamp() const { return mModifyCount; } + + PX_PHYSX_COMMON_API bool loadFromDesc(const PxHeightFieldDesc&); + PX_PHYSX_COMMON_API bool load(PxInputStream&); + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbRowsFast() const { return mData.rows; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbColumnsFast() const { return mData.columns; } + PX_FORCE_INLINE PxHeightFieldFormat::Enum getFormatFast() const { return mData.format; } + PX_FORCE_INLINE PxU32 getFlagsFast() const { return mData.flags; } + + PX_FORCE_INLINE bool isDeltaHeightInsideExtent(PxReal dy, PxReal eps = 0.0f) const + { + const float thickness = 0.0f; + return (thickness <= 0.0f && dy <= eps && dy >= thickness) || + (thickness > 0.0f && dy > -eps && dy < thickness); + } + + PX_FORCE_INLINE bool isDeltaHeightOppositeExtent(PxReal dy) const + { + const float thickness = 0.0f; + return (thickness <= 0.0f && dy > 0.0f) || (thickness > 0.0f && dy < 0.0f); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isZerothVertexShared(PxU32 vertexIndex) const + { +// return (getSample(vertexIndex).tessFlag & PxHeightFieldTessFlag::e0TH_VERTEX_SHARED); + return getSample(vertexIndex).tessFlag() != 0; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex0(PxU32 vertexIndex) const { return getSample(vertexIndex).materialIndex0; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex1(PxU32 vertexIndex) const { return getSample(vertexIndex).materialIndex1; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaterialIndex01(PxU32 vertexIndex) const + { + const PxHeightFieldSample& sample = getSample(vertexIndex); + return PxU32(sample.materialIndex0 | (sample.materialIndex1 << 16)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getHeight(PxU32 vertexIndex) const + { + return PxReal(getSample(vertexIndex).height); + } + + PX_INLINE PxReal getHeightInternal2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const; + PX_FORCE_INLINE PxReal getHeightInternal(PxReal x, PxReal z) const + { + PxReal fracX, fracZ; + const PxU32 vertexIndex = computeCellCoordinates(x, z, fracX, fracZ); + + return getHeightInternal2(vertexIndex, fracX, fracZ); + } + + PX_FORCE_INLINE bool isValidVertex(PxU32 vertexIndex) const { return vertexIndex < mData.rows*mData.columns; } + + PX_INLINE PxVec3 getVertex(PxU32 vertexIndex) const; + PX_INLINE bool isConvexVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const; + + PX_INLINE bool isValidEdge(PxU32 edgeIndex) const; + PX_INLINE PxU32 getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2]) const; + PX_INLINE PxU32 getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2], PxU32 cell, PxU32 row, PxU32 column) const; + PX_INLINE void getEdgeVertexIndices(PxU32 edgeIndex, PxU32& vertexIndex0, PxU32& vertexIndex1) const; +// PX_INLINE bool isConvexEdge(PxU32 edgeIndex) const; + PX_INLINE bool isConvexEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const; + PX_FORCE_INLINE bool isConvexEdge(PxU32 edgeIndex) const + { + const PxU32 cell = edgeIndex / 3; + const PxU32 row = cell / mData.columns; + const PxU32 column = cell % mData.columns; + return isConvexEdge(edgeIndex, cell, row, column); + } + + PX_PHYSX_COMMON_API PxU32 computeCellCoordinates(PxReal x, PxReal z, PxReal& fracX, PxReal& fracZ) const; + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMin(PxReal x, PxU32 nb) const + { + if(x<0.0f) + return 0; + if(x>PxReal(nb)) + return nb; + + const PxReal cx = Ps::floor(x); + const PxU32 icx = PxU32(cx); + return icx; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMax(PxReal x, PxU32 nb) const + { + if(x<0.0f) + return 0; + if(x>PxReal(nb)) + return nb; + + const PxReal cx = Ps::ceil(x); + const PxU32 icx = PxU32(cx); + return icx; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMinRow(PxReal x) const { return getMin(x, mData.rows-2); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaxRow(PxReal x) const { return getMax(x, mData.rows-1); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMinColumn(PxReal z) const { return getMin(z, mData.columns-2); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaxColumn(PxReal z) const { return getMax(z, mData.columns-1); } + + PX_CUDA_CALLABLE PX_INLINE bool isValidTriangle(PxU32 triangleIndex) const; + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFirstTriangle(PxU32 triangleIndex) const { return ((triangleIndex & 0x1) == 0); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getTriangleMaterial(PxU32 triangleIndex) const + { + return isFirstTriangle(triangleIndex) ? getMaterialIndex0(triangleIndex >> 1) : getMaterialIndex1(triangleIndex >> 1); + } + + PX_CUDA_CALLABLE PX_INLINE void getTriangleVertexIndices(PxU32 triangleIndex, PxU32& vertexIndex0, PxU32& vertexIndex1, PxU32& vertexIndex2) const; + PX_CUDA_CALLABLE PX_INLINE PxVec3 getTriangleNormalInternal(PxU32 triangleIndex) const; + PX_INLINE void getTriangleAdjacencyIndices(PxU32 triangleIndex,PxU32 vertexIndex0, PxU32 vertexIndex1, PxU32 vertexIndex2, PxU32& adjacencyIndex0, PxU32& adjacencyIndex1, PxU32& adjacencyIndex2) const; + + PX_INLINE PxVec3 getNormal_2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const; + PX_FORCE_INLINE PxVec3 getNormal_(PxReal x, PxReal z, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const + { + PxReal fracX, fracZ; + const PxU32 vertexIndex = computeCellCoordinates(x, z, fracX, fracZ); + + return getNormal_2(vertexIndex, fracX, fracZ, xcoeff, ycoeff, zcoeff); + } + + PX_INLINE PxU32 getTriangleIndex(PxReal x, PxReal z) const; + PX_INLINE PxU32 getTriangleIndex2(PxU32 cell, PxReal fracX, PxReal fracZ) const; + PX_FORCE_INLINE PxU16 getMaterial(PxReal x, PxReal z) const + { + return getTriangleMaterial(getTriangleIndex(x, z)); + } + + PX_FORCE_INLINE PxReal getMinHeight() const { return mMinHeight; } + PX_FORCE_INLINE PxReal getMaxHeight() const { return mMaxHeight; } + + PX_FORCE_INLINE const Gu::HeightFieldData& getData() const { return mData; } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void getTriangleVertices(PxU32 triangleIndex, PxU32 row, PxU32 column, PxVec3& v0, PxVec3& v1, PxVec3& v2) const; + + // checks if current vertex is solid or not + bool isSolidVertex(PxU32 vertexIndex, PxU32 row, PxU32 coloumn, PxU16 holeMaterialIndex, bool& nbSolid) const; + + // if precomputed bitmap define is used, the collision vertex information + // is precomputed during create height field and stored as a bit in materialIndex1 + PX_PHYSX_COMMON_API bool isCollisionVertexPreca(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex) const; + PX_FORCE_INLINE bool isCollisionVertex(PxU32 vertexIndex, PxU32, PxU32, PxU16) const + { + return getSample(vertexIndex).materialIndex1.isBitSet()!=0; + } + void parseTrianglesForCollisionVertices(PxU16 holeMaterialIndex); + + PX_FORCE_INLINE + PX_CUDA_CALLABLE const PxHeightFieldSample& getSample(PxU32 vertexIndex) const + { + PX_ASSERT(isValidVertex(vertexIndex)); + return mData.samples[vertexIndex]; + } + +#ifdef __CUDACC__ + PX_CUDA_CALLABLE void setSamplePtr(PxHeightFieldSample* s) { mData.samples = s; } +#endif + + Gu::HeightFieldData mData; + PxU32 mSampleStride; + PxU32 mNbSamples; // PT: added for platform conversion. Try to remove later. + PxReal mMinHeight; + PxReal mMaxHeight; + PxU32 mModifyCount; + // methods + PX_PHYSX_COMMON_API void releaseMemory(); + + PX_PHYSX_COMMON_API virtual ~HeightField(); + +private: + GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization +}; + +} // namespace Gu + +PX_INLINE PxVec3 Gu::HeightField::getVertex(PxU32 vertexIndex) const +{ + const PxU32 row = vertexIndex / mData.columns; + const PxU32 column = vertexIndex % mData.columns; +// return PxVec3(PxReal(row), getHeight(row * mData.columns + column), PxReal(column)); + return PxVec3(PxReal(row), getHeight(vertexIndex), PxReal(column)); +} + +// PT: only called from "isCollisionVertex", should move +PX_INLINE bool Gu::HeightField::isConvexVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(isValidVertex(vertexIndex)); +#endif + PX_ASSERT((vertexIndex / mData.columns)==row); + PX_ASSERT((vertexIndex % mData.columns)==column); + +// PxReal h0 = PxReal(2) * getHeight(vertexIndex); + PxI32 h0 = getSample(vertexIndex).height; + h0 += h0; + + bool definedInX, definedInZ; + PxI32 convexityX, convexityZ; + + if ((row > 0) && (row < mData.rows - 1)) + { +// convexityX = h0 - getHeight(vertexIndex + mData.columns) - getHeight(vertexIndex - mData.columns); + convexityX = h0 - getSample(vertexIndex + mData.columns).height - getSample(vertexIndex - mData.columns).height; + definedInX = true; + } + else + { + convexityX = 0; + definedInX = false; + } + + if ((column > 0) && (column < mData.columns - 1)) + { +// convexityZ = h0 - getHeight(vertexIndex + 1) - getHeight(vertexIndex - 1); + convexityZ = h0 - getSample(vertexIndex + 1).height - getSample(vertexIndex - 1).height; + definedInZ = true; + } + else + { + convexityZ = 0; + definedInZ = false; + } + + if(definedInX || definedInZ) + { + // PT: use XOR here + // saddle points +/* if ((convexityX > 0) && (convexityZ < 0)) + return false; + if ((convexityX < 0) && (convexityZ > 0)) + return false;*/ + if(((convexityX ^ convexityZ) & 0x80000000)==0) + return false; + + const PxReal value = PxReal(convexityX + convexityZ); + return value > mData.convexEdgeThreshold; + } + + // this has to be one of the two corner vertices + return true; +} + +PX_INLINE bool Gu::HeightField::isValidEdge(PxU32 edgeIndex) const +{ + const PxU32 cell = (edgeIndex / 3); + const PxU32 row = cell / mData.columns; + const PxU32 column = cell % mData.columns; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (row > mData.rows - 1) return false; + if (column >= mData.columns - 1) return false; + break; + case 1: + if (row >= mData.rows - 1) return false; + if (column >= mData.columns - 1) return false; + break; + case 2: + if (row >= mData.rows - 1) return false; + if (column > mData.columns - 1) return false; + break; + } + return true; +} + +PX_INLINE PxU32 Gu::HeightField::getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2]) const +{ + const PxU32 cell = edgeIndex / 3; + const PxU32 row = cell / mData.columns; + const PxU32 column = cell % mData.columns; + PxU32 count = 0; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (column < mData.columns - 1) + { + if (row > 0) + { +/* if (isZerothVertexShared(cell - mData.columns)) + triangleIndices[count++] = ((cell - mData.columns) << 1); + else + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1;*/ + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1 - isZerothVertexShared(cell - mData.columns); + } + if (row < mData.rows - 1) + { +/* if (isZerothVertexShared(cell)) + triangleIndices[count++] = (cell << 1) + 1; + else + triangleIndices[count++] = cell << 1;*/ + triangleIndices[count++] = (cell << 1) + isZerothVertexShared(cell); + } + } + break; + case 1: + if ((row < mData.rows - 1) && (column < mData.columns - 1)) + { + triangleIndices[count++] = cell << 1; + triangleIndices[count++] = (cell << 1) + 1; + } + break; + case 2: + if (row < mData.rows - 1) + { + if (column > 0) + triangleIndices[count++] = ((cell - 1) << 1) + 1; + if (column < mData.columns - 1) + triangleIndices[count++] = cell << 1; + } + break; + } + return count; +} + +PX_INLINE PxU32 Gu::HeightField::getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2], PxU32 cell, PxU32 row, PxU32 column) const +{ +// const PxU32 cell = edgeIndex / 3; +// const PxU32 row = cell / mData.columns; +// const PxU32 column = cell % mData.columns; + PxU32 count = 0; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (column < mData.columns - 1) + { + if (row > 0) + { +/* if (isZerothVertexShared(cell - mData.columns)) + triangleIndices[count++] = ((cell - mData.columns) << 1); + else + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1;*/ + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1 - isZerothVertexShared(cell - mData.columns); + } + if (row < mData.rows - 1) + { +/* if (isZerothVertexShared(cell)) + triangleIndices[count++] = (cell << 1) + 1; + else + triangleIndices[count++] = cell << 1;*/ + triangleIndices[count++] = (cell << 1) + isZerothVertexShared(cell); + } + } + break; + case 1: + if ((row < mData.rows - 1) && (column < mData.columns - 1)) + { + triangleIndices[count++] = cell << 1; + triangleIndices[count++] = (cell << 1) + 1; + } + break; + case 2: + if (row < mData.rows - 1) + { + if (column > 0) + triangleIndices[count++] = ((cell - 1) << 1) + 1; + if (column < mData.columns - 1) + triangleIndices[count++] = cell << 1; + } + break; + } + return count; +} + +PX_INLINE void Gu::HeightField::getEdgeVertexIndices(PxU32 edgeIndex, PxU32& vertexIndex0, PxU32& vertexIndex1) const +{ + const PxU32 cell = edgeIndex / 3; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + vertexIndex0 = cell; + vertexIndex1 = cell + 1; + break; + case 1: + { +/* if (isZerothVertexShared(cell)) + { + vertexIndex0 = cell; + vertexIndex1 = cell + mData.columns + 1; + } + else + { + vertexIndex0 = cell + 1; + vertexIndex1 = cell + mData.columns; + }*/ + const bool b = isZerothVertexShared(cell); + vertexIndex0 = cell + 1 - b; + vertexIndex1 = cell + mData.columns + b; + } + break; + case 2: + vertexIndex0 = cell; + vertexIndex1 = cell + mData.columns; + break; + } +} + +PX_INLINE bool Gu::HeightField::isConvexEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const +{ +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); + +// const PxU32 row = cell / mData.columns; + PX_ASSERT(row == cell / mData.columns); + if (row > mData.rows-2) return false; + +// const PxU32 column = cell % mData.columns; + PX_ASSERT(column == cell % mData.columns); + if (column > mData.columns-2) return false; + +// PxReal h0 = 0, h1 = 0, h2 = 0, h3 = 0; +// PxReal convexity = 0; + PxI32 h0 = 0, h1 = 0, h2 = 0, h3 = 0; + PxI32 convexity = 0; + +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + { + if (row < 1) return false; +/* if(isZerothVertexShared(cell - mData.columns)) + { + // <------ COL + // +----+ 0 R + // | / /# O + // | / / # W + // | / / # | + // |/ / # | + // + +====1 | + // | + // | + // | + // | + // | + // | + // V + // +// h0 = getHeight(cell - mData.columns); +// h1 = getHeight(cell); + h0 = getSample(cell - mData.columns).height; + h1 = getSample(cell).height; + } + else + { + // <------ COL + // 0 +----+ R + // #\ \ | O + // # \ \ | W + // # \ \ | | + // # \ \| | + // 1====+ + | + // | + // | + // | + // | + // | + // | + // V + // +// h0 = getHeight(cell - mData.columns + 1); +// h1 = getHeight(cell + 1); + h0 = getSample(cell - mData.columns + 1).height; + h1 = getSample(cell + 1).height; + }*/ + const bool b0 = !isZerothVertexShared(cell - mData.columns); + h0 = getSample(cell - mData.columns + b0).height; + h1 = getSample(cell + b0).height; + +/* if(isZerothVertexShared(cell)) + { + // <------ COL + // R + // O + // W + // | + // | + // | + // 2====+ 0 | + // # / /| | + // # / / | | + // # / / | | + // #/ / | | + // 3 +----+ | + // V + // +// h2 = getHeight(cell + 1); +// h3 = getHeight(cell + mData.columns + 1); + h2 = getSample(cell + 1).height; + h3 = getSample(cell + mData.columns + 1).height; + } + else + { + // <------ COL + // R + // O + // W + // | + // | + // | + // + +====2 | + // |\ \ # | + // | \ \ # | + // | \ \ # | + // | \ \# | + // +----+ 3 | + // V + // +// h2 = getHeight(cell); +// h3 = getHeight(cell + mData.columns); + h2 = getSample(cell).height; + h3 = getSample(cell + mData.columns).height; + }*/ + const bool b1 = isZerothVertexShared(cell); + h2 = getSample(cell + b1).height; + h3 = getSample(cell + mData.columns + b1).height; + + //convex = (h3-h2) < (h1-h0); + convexity = (h1-h0) - (h3-h2); + } + break; + case 1: +// h0 = getHeight(cell); +// h1 = getHeight(cell + 1); +// h2 = getHeight(cell + mData.columns); +// h3 = getHeight(cell + mData.columns + 1); + h0 = getSample(cell).height; + h1 = getSample(cell + 1).height; + h2 = getSample(cell + mData.columns).height; + h3 = getSample(cell + mData.columns + 1).height; + if (isZerothVertexShared(cell)) + //convex = (h0 + h3) > (h1 + h2); + convexity = (h0 + h3) - (h1 + h2); + else + //convex = (h2 + h1) > (h0 + h3); + convexity = (h2 + h1) - (h0 + h3); + break; + case 2: + { + if (column < 1) return false; +/* if(isZerothVertexShared(cell-1)) + { + // <-------------- COL + // 1====0 + R + // + / /| O + // + / / | W + // + / / | | + // +/ / | | + // + +----+ V + // +// h0 = getHeight(cell - 1); +// h1 = getHeight(cell); + h0 = getSample(cell - 1).height; + h1 = getSample(cell).height; + } + else + { + // <-------------- COL + // + +----+ R + // +\ \ | O + // + \ \ | W + // + \ \ | | + // + \ \| | + // 1====0 + V + // +// h0 = getHeight(cell - 1 + mData.columns); +// h1 = getHeight(cell + mData.columns); + h0 = getSample(cell - 1 + mData.columns).height; + h1 = getSample(cell + mData.columns).height; + }*/ + const PxU32 offset0 = isZerothVertexShared(cell-1) ? 0 : mData.columns; + h0 = getSample(cell - 1 + offset0).height; + h1 = getSample(cell + offset0).height; + +/* if(isZerothVertexShared(cell)) + { + // <-------------- COL + // +----+ + R + // | / /+ O + // | / / + W + // | / / + | + // |/ / + | + // + 3====2 V + // +// h2 = getHeight(cell + mData.columns); +// h3 = getHeight(cell + mData.columns + 1); + h2 = getSample(cell + mData.columns).height; + h3 = getSample(cell + mData.columns + 1).height; + } + else + { + // <-------------- COL + // + 3====2 R + // |\ \ + O + // | \ \ + W + // | \ \ + | + // | \ \+ | + // +----+ + V + // +// h2 = getHeight(cell); +// h3 = getHeight(cell + 1); + h2 = getSample(cell).height; + h3 = getSample(cell + 1).height; + }*/ + const PxU32 offset1 = isZerothVertexShared(cell) ? mData.columns : 0; + h2 = getSample(cell + offset1).height; + h3 = getSample(cell + offset1 + 1).height; + + //convex = (h3-h2) < (h1-h0); + convexity = (h1-h0) - (h3-h2); + } + break; + } + + const PxI32 threshold = PxI32(mData.convexEdgeThreshold); + return convexity > threshold; +} + +PX_INLINE bool Gu::HeightField::isValidTriangle(PxU32 triangleIndex) const +{ + const PxU32 cell = triangleIndex >> 1; + const PxU32 row = cell / mData.columns; + if (row >= (mData.rows - 1)) return false; + const PxU32 column = cell % mData.columns; + if (column >= (mData.columns - 1)) return false; + return true; +} + +PX_INLINE void Gu::HeightField::getTriangleVertexIndices(PxU32 triangleIndex, PxU32& vertexIndex0, PxU32& vertexIndex1, PxU32& vertexIndex2) const +{ + const PxU32 cell = triangleIndex >> 1; + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 1 R + // | 1 / /| O + // | / / | W + // | / / | | + // |/ / 0 | | + // 1 2----0 V + // + if (isFirstTriangle(triangleIndex)) + { + vertexIndex0 = cell + mData.columns; + vertexIndex1 = cell; + vertexIndex2 = cell + mData.columns + 1; + } + else + { + vertexIndex0 = cell + 1; + vertexIndex1 = cell + mData.columns + 1; + vertexIndex2 = cell; + } + } + else + { + // <---- COL + // 2 1----0 R + // |\ \ 0 | O + // | \ \ | W + // | \ \ | | + // | 1 \ \| | + // 0----1 2 V + // + if (isFirstTriangle(triangleIndex)) + { + vertexIndex0 = cell; + vertexIndex1 = cell + 1; + vertexIndex2 = cell + mData.columns; + } + else + { + vertexIndex0 = cell + mData.columns + 1; + vertexIndex1 = cell + mData.columns; + vertexIndex2 = cell + 1; + } + } +} + +PX_INLINE void Gu::HeightField::getTriangleAdjacencyIndices(PxU32 triangleIndex, PxU32 vertexIndex0, PxU32 vertexIndex1, PxU32 vertexIndex2, PxU32& adjacencyIndex0, PxU32& adjacencyIndex1, PxU32& adjacencyIndex2) const +{ + PX_UNUSED(vertexIndex0); + PX_UNUSED(vertexIndex1); + PX_UNUSED(vertexIndex2); + + const PxU32 cell = triangleIndex >> 1; + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 1 R + // | 1 / /| O + // | / / | W + // | / / | | + // |/ / 0 | | + // 1 2----0 V + // + if (isFirstTriangle(triangleIndex)) + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex + 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if((cell % (mData.columns) != 0)) + { + adjacencyIndex0 = triangleIndex - 1; + } + + if((cell / mData.columns != mData.rows - 2)) + { + const PxU32 tMod = isZerothVertexShared(cell + mData.columns) ? 1u : 0u; + adjacencyIndex2 = ((cell + mData.columns) * 2) + tMod; + } + } + else + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex - 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if(cell % (mData.columns) < (mData.columns - 2)) + { + adjacencyIndex0 = triangleIndex + 1; + } + + if(cell >= mData.columns - 1) + { + const PxU32 tMod = isZerothVertexShared(cell - mData.columns) ? 0u : 1u; + adjacencyIndex2 = ((cell - mData.columns) * 2) + tMod; + } + } + } + else + { + // <---- COL + // 2 1----0 R + // |\ \ 0 | O + // | \ \ | W + // | \ \ | | + // | 1 \ \| | + // 0----1 2 V + // + if (isFirstTriangle(triangleIndex)) + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex + 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if(cell >= mData.columns - 1) + { + const PxU32 tMod = isZerothVertexShared(cell - mData.columns) ? 0u : 1u; + adjacencyIndex0 = ((cell - (mData.columns)) * 2) + tMod; + } + + if((cell % (mData.columns) != 0)) + { + adjacencyIndex2 = triangleIndex - 1; + } + } + else + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex - 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if((cell / mData.columns != mData.rows - 2)) + { + const PxU32 tMod = isZerothVertexShared(cell + mData.columns) ? 1u : 0u; + adjacencyIndex0 = (cell + (mData.columns)) * 2 + tMod; + } + + if(cell % (mData.columns) < (mData.columns - 2)) + { + adjacencyIndex2 = triangleIndex + 1; + } + } + } +} + +PX_INLINE PxVec3 Gu::HeightField::getTriangleNormalInternal(PxU32 triangleIndex) const +{ + PxU32 v0, v1, v2; + getTriangleVertexIndices(triangleIndex, v0, v1, v2); + +// const PxReal h0 = getHeight(v0); +// const PxReal h1 = getHeight(v1); +// const PxReal h2 = getHeight(v2); + const PxI32 h0 = getSample(v0).height; + const PxI32 h1 = getSample(v1).height; + const PxI32 h2 = getSample(v2).height; + + const float thickness = 0.0f; + const PxReal coeff = physx::intrinsics::fsel(thickness, -1.0f, 1.0f); + +// PxVec3 n(0,1,0); + const PxU32 cell = triangleIndex >> 1; + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 1 R + // | 1 / /| O + // | / / | W + // | / / | | + // |/ / 0 | | + // 1 2----0 V + // + if (isFirstTriangle(triangleIndex)) + { +// n.x = -(h0-h1); +// n.z = -(h2-h0); + return PxVec3(coeff*PxReal(h1-h0), coeff, coeff*PxReal(h0-h2)); + } + else + { +// n.x = -(h1-h0); +// n.z = -(h0-h2); + return PxVec3(coeff*PxReal(h0-h1), coeff, coeff*PxReal(h2-h0)); + } + } + else + { + // <---- COL + // 2 1----0 R + // |\ \ 0 | O + // | \ \ | W + // | \ \ | | + // | 1 \ \| | + // 0----1 2 V + // + if (isFirstTriangle(triangleIndex)) + { +// n.x = -(h2-h0); +// n.z = -(h1-h0); + return PxVec3(coeff*PxReal(h0-h2), coeff, coeff*PxReal(h0-h1)); + } + else + { +// n.x = -(h0-h2); +// n.z = -(h0-h1); + return PxVec3(coeff*PxReal(h2-h0), coeff, coeff*PxReal(h1-h0)); + } + } +// return n; +} + +PX_INLINE PxReal Gu::HeightField::getHeightInternal2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const +{ + if (isZerothVertexShared(vertexIndex)) + { + // <----Z---+ + // +----+ | + // | /| | + // | / | X + // | / | | + // |/ | | + // +----+ | + // V + const PxReal h0 = getHeight(vertexIndex); + const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); + if (fracZ > fracX) + { + // <----Z---+ + // 1----0 | + // | / | + // | / X + // | / | + // |/ | + // 2 | + // V + const PxReal h1 = getHeight(vertexIndex + 1); + return h0 + fracZ*(h1-h0) + fracX*(h2-h1); + } + else + { + // <----Z---+ + // 0 | + // /| | + // / | X + // / | | + // / | | + // 2----1 | + // V + const PxReal h1 = getHeight(vertexIndex + mData.columns); + return h0 + fracX*(h1-h0) + fracZ*(h2-h1); + } + } + else + { + // <----Z---+ + // +----+ | + // |\ | | + // | \ | X + // | \ | | + // | \| | + // +----+ | + // V + const PxReal h2 = getHeight(vertexIndex + mData.columns); + const PxReal h1 = getHeight(vertexIndex + 1); + if (fracX + fracZ < 1.0f) + { + // <----Z---+ + // 1----0 | + // \ | | + // \ | X + // \ | | + // \| | + // 2 | + // V + const PxReal h0 = getHeight(vertexIndex); + return h0 + fracZ*(h1-h0) + fracX*(h2-h0); + } + else + { + // <----Z---+ + // 1 | + // |\ | + // | \ X + // | \ | + // | \ | + // 0----2 | + // V + // + // Note that we need to flip fracX and fracZ since we are moving the origin + const PxReal h0 = getHeight(vertexIndex + mData.columns + 1); + return h0 + (1.0f - fracZ)*(h2-h0) + (1.0f - fracX)*(h1-h0); + } + } +} + +PX_INLINE PxVec3 Gu::HeightField::getNormal_2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const +{ + PxVec3 normal; + if (isZerothVertexShared(vertexIndex)) + { + // <----Z---+ + // +----+ | + // | /| | + // | / | X + // | / | | + // |/ | | + // +----+ | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); + const PxI32 ih0 = getSample(vertexIndex).height; + const PxI32 ih2 = getSample(vertexIndex + mData.columns + 1).height; + if (fracZ >= fracX) + { + // <----Z---+ + // 1----0 | + // | / | + // | / X + // | / | + // |/ | + // 2 | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h1 = getHeight(vertexIndex + 1); +// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); +// normal.set(-(h2-h1), 1.0f, -(h1-h0)); + const PxI32 ih1 = getSample(vertexIndex + 1).height; + normal = PxVec3(PxReal(ih1 - ih2)*xcoeff, ycoeff, PxReal(ih0 - ih1)*zcoeff); + } + else + { + // <----Z---+ + // 0 | + // /| | + // / | X + // / | | + // / | | + // 2----1 | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h1 = getHeight(vertexIndex + mData.columns); +// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); +// normal.set(-(h1-h0), 1.0f, -(h2-h1)); + const PxI32 ih1 = getSample(vertexIndex + mData.columns).height; + normal = PxVec3(PxReal(ih0 - ih1)*xcoeff, ycoeff, PxReal(ih1 - ih2)*zcoeff); + } + } + else + { + // <----Z---+ + // +----+ | + // |\ | | + // | \ | X + // | \ | | + // | \| | + // +----+ | + // V + const PxI32 ih1 = getSample(vertexIndex + 1).height; + const PxI32 ih2 = getSample(vertexIndex + mData.columns).height; + if (fracX + fracZ <= PxReal(1)) + { + // <----Z---+ + // 1----0 | + // \ | | + // \ | X + // \ | | + // \| | + // 2 | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h1 = getHeight(vertexIndex + 1); +// const PxReal h2 = getHeight(vertexIndex + mData.columns); +// normal.set(-(h2-h0), 1.0f, -(h1-h0)); + const PxI32 ih0 = getSample(vertexIndex).height; +// const PxI32 ih1 = getSample(vertexIndex + 1).height; +// const PxI32 ih2 = getSample(vertexIndex + mData.columns).height; + normal = PxVec3(PxReal(ih0 - ih2)*xcoeff, ycoeff, PxReal(ih0 - ih1)*zcoeff); + } + else + { + // <----Z---+ + // 2 | + // |\ | + // | \ X + // | \ | + // | \ | + // 0----1 | + // V + // + // Note that we need to flip fracX and fracZ since we are moving the origin +// const PxReal h2 = getHeight(vertexIndex + 1); +// const PxReal h1 = getHeight(vertexIndex + mData.columns); +// const PxReal h0 = getHeight(vertexIndex + mData.columns + 1); +// normal.set(-(h0-h2), 1.0f, -(h0-h1)); +// const PxI32 ih2 = getSample(vertexIndex + 1).height; +// const PxI32 ih1 = getSample(vertexIndex + mData.columns).height; + const PxI32 ih0 = getSample(vertexIndex + mData.columns + 1).height; +// normal.set(PxReal(ih2 - ih0), 1.0f, PxReal(ih1b - ih0)); + normal = PxVec3(PxReal(ih1 - ih0)*xcoeff, ycoeff, PxReal(ih2 - ih0)*zcoeff); + } + } + return normal; +} + +PX_INLINE PxU32 Gu::HeightField::getTriangleIndex2(PxU32 cell, PxReal fracX, PxReal fracZ) const +{ + if (isZerothVertexShared(cell)) + return (fracZ > fracX) ? (cell << 1) + 1 : (cell << 1); + else + return (fracX + fracZ > 1) ? (cell << 1) + 1 : (cell << 1); +} + +PX_INLINE PxU32 Gu::HeightField::getTriangleIndex(PxReal x, PxReal z) const +{ + PxReal fracX, fracZ; + const PxU32 cell = computeCellCoordinates(x, z, fracX, fracZ); + + return getTriangleIndex2(cell, fracX, fracZ); +} + +PX_FORCE_INLINE void Gu::HeightField::getTriangleVertices(PxU32 triangleIndex, PxU32 row, PxU32 column, PxVec3& v0, PxVec3& v1, PxVec3& v2) const +{ + PxU32 cell = triangleIndex >> 1; + PX_ASSERT(row * getNbColumnsFast() + column == cell); + + PxReal h0 = getHeight(cell); + PxReal h1 = getHeight(cell + 1); + PxReal h2 = getHeight(cell + getNbColumnsFast()); + PxReal h3 = getHeight(cell + getNbColumnsFast() + 1); + + if (isFirstTriangle(triangleIndex)) + { + if (isZerothVertexShared(cell)) + { + // <---- COL + // 1 R + // /| O + // / | W + // / | | + // / 0 | | + // 2----0 V + // + v0 = PxVec3(PxReal(row + 1), h2, PxReal(column )); + v1 = PxVec3(PxReal(row ), h0, PxReal(column )); + v2 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1)); + } + else + { + // <---- COL + // 1----0 R + // \ 0 | O + // \ | W + // \ | | + // \| | + // 2 V + // + v0 = PxVec3(PxReal(row ), h0, PxReal(column )); + v1 = PxVec3(PxReal(row ), h1, PxReal(column + 1)); + v2 = PxVec3(PxReal(row + 1), h2, PxReal(column )); + } + } + else + { + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 R + // | 1 / O + // | / W + // | / | + // |/ | + // 1 V + // + v0 = PxVec3(PxReal(row ), h1, PxReal(column + 1)); + v1 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1)); + v2 = PxVec3(PxReal(row ), h0, PxReal(column )); + } + else + { + // <---- COL + // 2 R + // |\ O + // | \ W + // | \ | + // | 1 \ | + // 0----1 V + // + v0 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1)); + v1 = PxVec3(PxReal(row + 1), h2, PxReal(column )); + v2 = PxVec3(PxReal(row ), h1, PxReal(column + 1)); + } + } +} + +struct EdgeData +{ + PxU32 edgeIndex; + PxU32 cell; + PxU32 row; + PxU32 column; +}; +PX_PHYSX_COMMON_API PxU32 getVertexEdgeIndices(const Gu::HeightField& heightfield, PxU32 vertexIndex, PxU32 row, PxU32 column, EdgeData edgeIndices[8]); +PX_PHYSX_COMMON_API PxU32 getEdgeTriangleIndices(const Gu::HeightField& heightfield, const EdgeData& edgeData, PxU32* PX_RESTRICT triangleIndices); + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h new file mode 100644 index 000000000..2e165936d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HEIGHTFIELD_DATA_H +#define GU_HEIGHTFIELD_DATA_H + +#include "foundation/PxSimpleTypes.h" +#include "PxHeightFieldFlag.h" +#include "PxHeightFieldSample.h" +#include "GuCenterExtents.h" + +namespace physx +{ + +namespace Gu +{ + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif +struct PX_PHYSX_COMMON_API HeightFieldData +{ +// PX_SERIALIZATION + PX_FORCE_INLINE HeightFieldData() {} + PX_FORCE_INLINE HeightFieldData(const PxEMPTY) : flags(PxEmpty) {} +//~PX_SERIALIZATION + + //properties + // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading + CenterExtents mAABB; + PxU32 rows; // PT: WARNING: don't change this member's name (used in ConvX) + PxU32 columns; // PT: WARNING: don't change this member's name (used in ConvX) + PxReal rowLimit; // PT: to avoid runtime int-to-float conversions on Xbox + PxReal colLimit; // PT: to avoid runtime int-to-float conversions on Xbox + PxReal nbColumns; // PT: to avoid runtime int-to-float conversions on Xbox + PxHeightFieldSample* samples; // PT: WARNING: don't change this member's name (used in ConvX) + PxReal convexEdgeThreshold; + + PxHeightFieldFlags flags; + + PxHeightFieldFormat::Enum format; + + PX_FORCE_INLINE const CenterExtentsPadded& getPaddedBounds() const + { + // PT: see compile-time assert below + return static_cast(mAABB); + } +}; +#if PX_VC + #pragma warning(pop) +#endif + + // PT: 'getPaddedBounds()' is only safe if we make sure the bounds member is followed by at least 32bits of data + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(Gu::HeightFieldData, rows)>=PX_OFFSET_OF(Gu::HeightFieldData, mAABB)+4); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp new file mode 100644 index 000000000..d4e9b9bb8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp @@ -0,0 +1,832 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "GuHeightFieldUtil.h" +#include "GuSweepSharedTests.h" + +#include "PsFoundation.h" +#include "GuHeightField.h" +#include "GuEntityReport.h" +#include "PxMeshScale.h" + +using namespace physx; + +void Gu::HeightFieldUtil::computeLocalBounds(PxBounds3& bounds) const +{ + const PxMeshScale scale(PxVec3(mHfGeom->rowScale, mHfGeom->heightScale, mHfGeom->columnScale), PxQuat(PxIdentity)); + const PxMat33 mat33 = scale.toMat33(); + + bounds.minimum = mat33.transform(mHeightField->getData().mAABB.getMin()); + bounds.maximum = mat33.transform(mHeightField->getData().mAABB.getMax()); + + // PT: HFs will assert in Gu::intersectRayAABB2() if we don't deal with that + const float deltaY = GU_MIN_AABB_EXTENT*0.5f - (bounds.maximum.y - bounds.minimum.y); + if(deltaY>0.0f) + { + bounds.maximum.y += deltaY*0.6f; + bounds.minimum.y -= deltaY*0.6f; + } +} + +PxU32 Gu::HeightFieldUtil::getFaceIndexAtShapePoint(PxReal x, PxReal z) const +{ + if (isShapePointOnHeightField(x, z)) + { + const PxU32 triangleIndex = mHeightField->getTriangleIndex(x * mOneOverRowScale, z * mOneOverColumnScale); + return (mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE) ? triangleIndex : 0xffffffff; + } + return 0xffffffff; +} + +PxVec3 Gu::HeightFieldUtil::getVertexNormal(PxU32 vertexIndex, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidVertex(vertexIndex)); +#endif + // PxU32 edges[8]; + // const PxU32 edgeCount = mHeightField.getVertexEdgeIndices(vertexIndex, edges); + + //const PxU32 nbColumns = mHeightField.getData().columns; + //const PxU32 row = vertexIndex / nbColumns; + //const PxU32 column = vertexIndex % nbColumns; + PX_ASSERT(row == vertexIndex / mHeightField->getData().columns); + PX_ASSERT(column == vertexIndex % mHeightField->getData().columns); + EdgeData edgeIndices[8]; + const PxU32 edgeCount = ::getVertexEdgeIndices(*mHeightField, vertexIndex, row, column, edgeIndices); + + PxVec3 n(0.0f); + PxVec3 tn; + for (PxU32 i=0; igetTriangleMaterial(faces[j]) != PxHeightFieldMaterial::eHOLE) + { + tn = hf2shapen(mHeightField->getTriangleNormalInternal(faces[j])).getNormalized(); + n+=tn; + } + } + } + + return n.getNormalized(); +} + +PxU32 Gu::HeightFieldUtil::findClosestPointsOnCell( + PxU32 row, PxU32 column, PxVec3 point, + PxVec3* PX_RESTRICT closestPoints, PxU32* PX_RESTRICT featureCodes, + bool testFaces, bool testEdges, bool skipEdgesIfFaceHits) const +{ + PxU32 count = 0; + + const PxU32 offset = row * mHeightField->getNbColumnsFast() + column; + const PxU32 firstEdgeIndex = 3 * offset; + + // ptchernev TODO: + // move the material assignments to an else in the ifs on triangle material + // instead of doing it all the time + + PX_ASSERT(row < (mHeightField->getNbRowsFast() - 1)); + PX_ASSERT(column < (mHeightField->getNbColumnsFast() - 1)); + const bool lastRow = (row == (mHeightField->getNbRowsFast() - 2)); + const bool lastColumn = (column == (mHeightField->getNbColumnsFast() - 2)); + + bool testVertex0 = testEdges; + bool testColumnEdge0 = testEdges; + bool testRowEdge0 = testEdges; + bool testDiagonal = testEdges; + bool testVertex1 = lastColumn && testEdges; + bool testVertex2 = lastRow && testEdges; + + bool testRowEdge1 = lastColumn && testEdges; + bool testColumnEdge1 = lastRow && testEdges; + bool testVertex3 = lastRow && lastColumn && testEdges; + + const PxU32 triangleIndex0 = offset << 1; + const PxMaterialTableIndex materialIndex0 = mHeightField->getTriangleMaterial(triangleIndex0); + const PxU32 triangleIndex1 = triangleIndex0 + 1; + const PxMaterialTableIndex materialIndex1 = mHeightField->getTriangleMaterial(triangleIndex1); + + if (testFaces) + { + if (materialIndex0 != PxHeightFieldMaterial::eHOLE) + { + // face 0 + PxVec3 closestPoint; + if (findProjectionOnTriangle(triangleIndex0, row, column, point, closestPoint)) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(triangleIndex0, eFACE); + count++; + testRowEdge0 = false; + testVertex0 = false; + testVertex2 = false; + testDiagonal = false; + } + } + + if (materialIndex1 != PxHeightFieldMaterial::eHOLE) + { + // face 1 + PxVec3 closestPoint; + if (findProjectionOnTriangle(triangleIndex1, row, column, point, closestPoint)) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(triangleIndex1, eFACE); + count++; + testRowEdge1 = false; + testVertex1 = false; + testVertex3 = false; + testDiagonal = false; + } + } + if (!testEdges) + return count; + } + + // if there were any face contacts and we asked to skip edges if face contacts, return current count here + if (count && skipEdgesIfFaceHits) + return count; + + const PxU32 nbColumns = mHeightField->getNbColumnsFast(); + if (testVertex0 || testColumnEdge0 || testVertex1) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex, offset, row, column, point, closestPoint); + if (t <= 0) + { + if (testVertex0 && 0xffffffff != (getVertexFaceIndex(offset, row, column))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column, eVERTEX); + count++; + } + testVertex0 = false; + } + else if (t < 1) + { + if (testColumnEdge0 && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex, eEDGE); + count++; + } + } + else + { + if (testVertex1 && 0xffffffff != (getVertexFaceIndex(offset + 1, row, column + 1))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column+1, eVERTEX); + count++; + } + } + } + + if (testVertex0 || testRowEdge0 || testVertex2) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 2, offset, row, column, point, closestPoint); + if (t <= 0) + { + if (testVertex0 && 0xffffffff != (getVertexFaceIndex(offset, row, column))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column, eVERTEX); + count++; + } + } + else if(t < 1) + { + if (testRowEdge0 && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 2))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+2, eEDGE); + count++; + } + } + else + { + if (testVertex2 && 0xffffffff != (getVertexFaceIndex(offset + nbColumns, row + 1, column))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode((row+1)*nbColumns+column, eVERTEX); + count++; + } + } + } + + if (testColumnEdge1) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 3 * nbColumns, offset + nbColumns, row + 1, column, point, closestPoint); + if (t <= 0) + ; // do nothing + else if (t < 1) + { + const PxU32 edgeIndex3 = firstEdgeIndex + 3 * nbColumns; + if (0xffffffff != (getEdgeFaceIndex(edgeIndex3))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(edgeIndex3, eEDGE); + count++; + } + } + } + + if (testRowEdge1) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 5, offset + 1, row, column + 1, point, closestPoint); + if (t <= 0) + ; // do nothing + else if (t < 1) + { + if (0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 5))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+5, eEDGE); + count++; + } + } + } + + if (testVertex3 && 0xffffffff != (getVertexFaceIndex(offset + nbColumns + 1, row + 1, column + 1))) + { + closestPoints[count] = PxVec3((row + 1) * mHfGeom->rowScale, mHfGeom->heightScale * mHeightField->getHeight(offset + mHeightField->getNbColumnsFast() + 1), (column + 1) * mHfGeom->columnScale); + if (featureCodes) featureCodes[count] = makeFeatureCode((row+1)*nbColumns+column+1, eVERTEX); + count++; + } + + if (testDiagonal && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 1))) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 1, offset, row, column, point, closestPoint); + if (t <= 0) + ; // do nothing + else if (t < 1) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+1, eEDGE); + count++; + } + } + + return count; +} + +//PxReal Gu::HeightFieldUtil::findClosestPointOnEdge(PxU32 edgeIndex, const PxVec3& point, PxVec3& closestPoint) const +PxReal Gu::HeightFieldUtil::findClosestPointOnEdge( + PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& closestPoint) const +{ +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); +// const PxU32 row = cell / mHeightField->getNbColumnsFast(); + PX_ASSERT(row == cell / mHeightField->getNbColumnsFast()); +// const PxU32 column = cell % mHeightField->getNbColumnsFast(); + PX_ASSERT(column == cell % mHeightField->getNbColumnsFast()); + + PxVec3 origin, direction; + PxReal lengthSquared; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + const PxReal dy = y1 - y0; + direction = PxVec3(0, dy, mHfGeom->columnScale); + lengthSquared = mHfGeom->columnScale * mHfGeom->columnScale + dy * dy; + } + break; + case 1: + if (mHeightField->isZerothVertexShared(cell)) + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + const PxReal dy = y3 - y0; + direction = PxVec3(mHfGeom->rowScale, dy, mHfGeom->columnScale); + lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + mHfGeom->columnScale * mHfGeom->columnScale + dy * dy; + } + else + { + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale); + const PxReal dy = y2 - y1; + direction = PxVec3(mHfGeom->rowScale, dy, -mHfGeom->columnScale); + lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + mHfGeom->columnScale * mHfGeom->columnScale + dy * dy; + } + break; + case 2: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + const PxReal dy = y2 - y0; + direction = PxVec3(mHfGeom->rowScale, dy, 0); + lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + dy * dy; + } + break; + default: + origin = direction = PxVec3(PxReal(0)); + lengthSquared = 0.0f; + PX_ALWAYS_ASSERT_MESSAGE("Invalid edge index in findClosestPointOnEdge"); + } // switch (edgeIndex % 3) + + const PxVec3 relative = point - origin; + const PxReal t = relative.dot(direction) / lengthSquared; + if (t < 0) + closestPoint = origin; + else if (t > 1) + closestPoint = origin + direction; + else + closestPoint = origin + direction * t; + + return t; +} + +PxU32 Gu::HeightFieldUtil::getVertexFaceIndex(PxU32 vertexIndex, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidVertex(vertexIndex)); +#endif + +// PxU32 edgeIndices[8]; +// const PxU32 count = mHeightField->getVertexEdgeIndices(vertexIndex, edgeIndices); + +//const PxU32 nbColumns = mHeightField->getData().columns; +//const PxU32 row = vertexIndex / nbColumns; +//const PxU32 column = vertexIndex % nbColumns; +PX_ASSERT(row == vertexIndex / mHeightField->getData().columns); +PX_ASSERT(column == vertexIndex % mHeightField->getData().columns); +EdgeData edgeIndices[8]; +const PxU32 count = ::getVertexEdgeIndices(*mHeightField, vertexIndex, row, column, edgeIndices); + + for (PxU32 i = 0; iisValidEdge(edgeIndex)); +#endif + PxU32 faceIndices[2]; + const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices); + if (count > 1) + { + // ptchernev TODO: this is a bit arbitrary + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1]; + } + else + { + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + } + return 0xffffffff; +} + +PxU32 Gu::HeightFieldUtil::getEdgeFaceIndex(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif + PxU32 faceIndices[2]; + const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices, cell, row, column); + if (count > 1) + { + // ptchernev TODO: this is a bit arbitrary + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1]; + } + else + { + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + } + return 0xffffffff; +} + +PxU32 Gu::HeightFieldUtil::getEdgeFaceIndex(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices) const +{ + PX_UNUSED(edgeIndex); + +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif +// PxU32 faceIndices[2]; +// const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices); + if (count > 1) + { + // ptchernev TODO: this is a bit arbitrary + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1]; + } + else + { + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + } + return 0xffffffff; +} + +bool Gu::HeightFieldUtil::findProjectionOnTriangle(PxU32 triangleIndex, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& projection) const +{ + const PxU32 cell = (triangleIndex >> 1); + PX_ASSERT(row == cell / mHeightField->getNbColumnsFast()); + PX_ASSERT(column == cell % mHeightField->getNbColumnsFast()); + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1); + PxVec3 origin; + PxReal h0, h1, h2, uInvScale, vInvScale; + + // specify a triangle according to current triangle index as origin, 3 heights and (uInvScale, vInvScale) vector for (z,x) + // set uInvScale in h1-h0; vScale in h2-h0 direction + if (mHeightField->isZerothVertexShared(cell)) + { + // COLUMN --> + // + // R 0---1 + // O |\ 1| + // W | \ | + // | |0 \| + // | 2---3 + // V + if ((triangleIndex & 1) == 0) + { + // case 0 + // face 0 + origin = PxVec3((row + 1) * mHfGeom->rowScale, y2, column * mHfGeom->columnScale); + // origin -> 2 + // verts -> 2,3,0 + h0 = y2; + h1 = y3; + h2 = y0; + uInvScale = mOneOverColumnScale; + vInvScale = -mOneOverRowScale; + } + else // if (testFace1) + { + // case 1 + // face 1 + origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale); + // origin -> 1 + // verts -> 1,0,3 + h0 = y1; + h1 = y0; + h2 = y3; + uInvScale = -mOneOverColumnScale; + vInvScale = mOneOverRowScale; + } + } + else + { + // COLUMN --> + // + // R 0---1 + // O |0 /| + // W | / | + // | |/ 1| + // | 2---3 + // V + if ((triangleIndex & 1) == 0) + { + // case 2 + // face 0 + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + // origin -> 0 + // verts -> 0,1,2 + h0 = y0; + h1 = y1; + h2 = y2; + uInvScale = mOneOverColumnScale; + vInvScale = mOneOverRowScale; + } + else + { + // case 3 + // face 1 + origin = PxVec3((row + 1) * mHfGeom->rowScale, y3, (column + 1) * mHfGeom->columnScale); + // origin -> 3 + // verts -> 3,2,1 + h0 = y3; + h1 = y2; + h2 = y1; + uInvScale = -mOneOverColumnScale; + vInvScale = -mOneOverRowScale; + } + } + + // vector from triangle origin to point we want to project + const PxVec3 relative = point - origin; + + // Looking at the triangle diagram for case 2 + // The normal computation should be + // n = (p1-p0) x (p2-p0) + // For a right handed cross product that's pointing into the screen (negative h), so -n is in the direction of increasing h + // cs = column scale, rs = row scale, h10 = h1-h0; u=column, v=row + // (i j k); + // p1-p0 = (cs, h10, 0); this is column, u and z + // p2-p0 = (0, h20, rs); this is row, v and x + // n = (h10*rs, -cs*rs, +cs*h20) + // n/(cs*rs) = (h10/cs, -1, h20/rs) + // -n = (-h10/cs, 1, -h20/rs) + PxReal h10 = h1-h0, h20 = h2-h0; + PxReal nu = -h10 * uInvScale; + PxReal nv = -h20 * vInvScale; + + PxVec3 n(nv, 1.0f, nu); // for whatever reason.. x is v, z is u. + //n *= 1.0f / PxSqrt(nu*nu + nv*nv + 1.0f); // technically we need to do this but since later + + // project relative onto the n plane, it gives us unclipped projection onto the triangle + // the computation without sqrt shortcut is relPrj = relative - n.dot(relative)*n, but because we divide by sqrt^2 we skip sqrt + PxVec3 relPrj = relative - n.dot(relative)* (1.0f/(nu*nu+nv*nv+1.0f)) * n; + + // project relPrj onto 2d UV plane with h = 0 oriented according to vInvScale, uInvScale (for x and z) + // to convert to HF cell coords we'd multiply by inv scale, after that the coords should be >0 and the sum within 1 to be + // inside of a 2d triangle + PxReal scaledX = relPrj.x * vInvScale, scaledZ = relPrj.z * uInvScale; + //PxVec3 testProjection = relPrj + origin; + //PxVec3 testN = (point - testProjection).getNormalized(); + if (scaledX > 0.0f && scaledZ > 0.0f && scaledX + scaledZ < 1.0f) + { + projection = relPrj + origin; + return true; + } + + return false; +} + +void Gu::HeightFieldUtil::getEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, PxVec3& origin, PxVec3& extent) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); +// const PxU32 row = cell / mHeightField->getNbColumnsFast(); + PX_ASSERT(row == cell / mHeightField->getNbColumnsFast()); +// const PxU32 column = cell % mHeightField->getNbColumnsFast(); + PX_ASSERT(column == cell % mHeightField->getNbColumnsFast()); + +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + extent = PxVec3(0, y1 - y0, mHfGeom->columnScale); + } + break; + case 1: + if (mHeightField->isZerothVertexShared(cell)) + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + extent = PxVec3(mHfGeom->rowScale, y3 - y0, mHfGeom->columnScale); + } + else + { + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale); + extent = PxVec3(mHfGeom->rowScale, y2 - y1, -mHfGeom->columnScale); + } + break; + case 2: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + extent = PxVec3(mHfGeom->rowScale, y2 - y0, 0); + } + break; + } +} + +bool Gu::HeightFieldUtil::overlapAABBTriangles(const PxTransform& pose, const PxBounds3& bounds, PxU32 flags, EntityReport* callback) const +{ + PX_ASSERT(!bounds.isEmpty()); + + PxBounds3 localBounds = (flags & GuHfQueryFlags::eWORLD_SPACE) ? PxBounds3::transformFast(pose.getInverse(), bounds) : bounds; + + localBounds.minimum.x *= mOneOverRowScale; + localBounds.minimum.y *= mOneOverHeightScale; + localBounds.minimum.z *= mOneOverColumnScale; + + localBounds.maximum.x *= mOneOverRowScale; + localBounds.maximum.y *= mOneOverHeightScale; + localBounds.maximum.z *= mOneOverColumnScale; + + if(mHfGeom->rowScale < 0.0f) + Ps::swap(localBounds.minimum.x, localBounds.maximum.x); + + if(mHfGeom->columnScale < 0.0f) + Ps::swap(localBounds.minimum.z, localBounds.maximum.z); + + // early exit for aabb does not overlap in XZ plane + // DO NOT MOVE: since rowScale / columnScale may be negative this has to be done after scaling localBounds + const PxU32 nbRows = mHeightField->getNbRowsFast(); + const PxU32 nbColumns = mHeightField->getNbColumnsFast(); + if(localBounds.minimum.x > float(nbRows - 1)) + return false; + if(localBounds.minimum.z > float(nbColumns - 1)) + return false; + if(localBounds.maximum.x < 0.0f) + return false; + if(localBounds.maximum.z < 0.0f) + return false; + + const PxU32 minRow = mHeightField->getMinRow(localBounds.minimum.x); + const PxU32 maxRow = mHeightField->getMaxRow(localBounds.maximum.x); + const PxU32 minColumn = mHeightField->getMinColumn(localBounds.minimum.z); + const PxU32 maxColumn = mHeightField->getMaxColumn(localBounds.maximum.z); + + PxU32 maxNbTriangles = 2 * (maxColumn - minColumn) * (maxRow - minRow); + + if(!maxNbTriangles) + return false; + + if(flags & GuHfQueryFlags::eFIRST_CONTACT) + maxNbTriangles = 1; + + const PxU32 bufferSize = HF_SWEEP_REPORT_BUFFER_SIZE; + PxU32 indexBuffer[bufferSize]; + PxU32 indexBufferUsed = 0; + PxU32 nb = 0; + + PxU32 offset = minRow * mHeightField->getNbColumnsFast() + minColumn; + + const PxReal miny = localBounds.minimum.y; + const PxReal maxy = localBounds.maximum.y; + + for(PxU32 row=minRow; rowgetHeight(offset); + const PxReal h1 = mHeightField->getHeight(offset + 1); + const PxReal h2 = mHeightField->getHeight(offset + mHeightField->getNbColumnsFast()); + const PxReal h3 = mHeightField->getHeight(offset + mHeightField->getNbColumnsFast() + 1); + if(!((maxy < h0 && maxy < h1 && maxy < h2 && maxy < h3) || (miny > h0 && miny > h1 && miny > h2 && miny > h3))) + { + const PxU32 material0 = mHeightField->getMaterialIndex0(offset); + if(material0 != PxHeightFieldMaterial::eHOLE) + { + if(indexBufferUsed >= bufferSize) + { + callback->onEvent(indexBufferUsed, indexBuffer); + indexBufferUsed = 0; + } + + indexBuffer[indexBufferUsed++] = offset << 1; + nb++; + + if(flags & GuHfQueryFlags::eFIRST_CONTACT) + goto search_done; + } + + const PxU32 material1 = mHeightField->getMaterialIndex1(offset); + if(material1 != PxHeightFieldMaterial::eHOLE) + { + if(indexBufferUsed >= bufferSize) + { + callback->onEvent(indexBufferUsed, indexBuffer); + indexBufferUsed = 0; + } + + indexBuffer[indexBufferUsed++] = (offset << 1) + 1; + nb++; + + if(flags & GuHfQueryFlags::eFIRST_CONTACT) + goto search_done; + } + } + offset++; + } + offset += (mHeightField->getNbColumnsFast() - (maxColumn - minColumn)); + } + +search_done: + + if(indexBufferUsed > 0) + callback->onEvent(indexBufferUsed, indexBuffer); + + return nb > 0; +} + +PxU32 Gu::HeightFieldUtil::getTriangle(const PxTransform& pose, PxTriangle& worldTri, + PxU32* _vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation, bool worldSpaceRotation) const +{ +#if PX_CHECKED + if (!mHeightField->isValidTriangle(triangleIndex)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "HeightFieldShape::getTriangle: Invalid triangle index!"); + return 0; + } +#endif + + PxVec3 handedness(1.0f); // Vector to invert normal coordinates according to the heightfield scales + bool wrongHanded = false; + if (mHfGeom->columnScale < 0) + { + wrongHanded = !wrongHanded; + handedness.z = -1.0f; + } + if (mHfGeom->rowScale < 0) + { + wrongHanded = !wrongHanded; + handedness.x = -1.0f; + } + +/* if (0) // ptchernev: Iterating over triangles becomes a pain. + { + if (mHeightField.getTriangleMaterial(triangleIndex) == mHfGeom.holeMaterialIndex) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "HeightFieldShape::getTriangle: Non-existing triangle (triangle has hole material)!"); + return 0; + } + }*/ + + PxU32 vertexIndices[3]; + mHeightField->getTriangleVertexIndices(triangleIndex, vertexIndices[0], vertexIndices[1+wrongHanded], vertexIndices[2-wrongHanded]); + + if(adjacencyIndices) + { + mHeightField->getTriangleAdjacencyIndices( triangleIndex, vertexIndices[0], vertexIndices[1+wrongHanded], vertexIndices[2-wrongHanded], + adjacencyIndices[wrongHanded ? 2 : 0], adjacencyIndices[1], adjacencyIndices[wrongHanded ? 0 : 2]); + } + + if(_vertexIndices) + { + _vertexIndices[0] = vertexIndices[0]; + _vertexIndices[1] = vertexIndices[1]; + _vertexIndices[2] = vertexIndices[2]; + } + + if (worldSpaceRotation) + { + if (worldSpaceTranslation) + { + for (PxU32 vi = 0; vi < 3; vi++) + worldTri.verts[vi] = hf2worldp(pose, mHeightField->getVertex(vertexIndices[vi])); + } + else + { + for (PxU32 vi = 0; vi < 3; vi++) + { + // TTP 2390 + // local space here is rotated (but not translated) world space + worldTri.verts[vi] = pose.q.rotate(hf2shapep(mHeightField->getVertex(vertexIndices[vi]))); + } + } + } + else + { + const PxVec3 offset = worldSpaceTranslation ? pose.p : PxVec3(0.0f); + for (PxU32 vi = 0; vi < 3; vi++) + worldTri.verts[vi] = hf2shapep(mHeightField->getVertex(vertexIndices[vi])) + offset; + } + return PxU32(mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE); +} diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h new file mode 100644 index 000000000..18e52420a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h @@ -0,0 +1,904 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HEIGHTFIELD_UTIL_H +#define GU_HEIGHTFIELD_UTIL_H + +#include "PxHeightFieldGeometry.h" +#include "GuHeightField.h" +#include "PxTriangle.h" +#include "../intersection/GuIntersectionRayTriangle.h" +#include "../intersection/GuIntersectionRayBox.h" +#include "PsBasicTemplates.h" + +namespace physx +{ +#define HF_SWEEP_REPORT_BUFFER_SIZE 64 + +/** +\brief Used to control contact queries. +*/ +struct GuHfQueryFlags +{ + enum Enum + { + eWORLD_SPACE = (1<<0), //!< world-space parameter, else object space + eFIRST_CONTACT = (1<<1) //!< returns first contact only, else returns all contacts + }; +}; + +namespace Gu +{ + template class EntityReport; + + class PX_PHYSX_COMMON_API HeightFieldUtil + { + public: + PxReal mOneOverRowScale; + PxReal mOneOverHeightScale; + PxReal mOneOverColumnScale; + const Gu::HeightField* mHeightField; + const PxHeightFieldGeometry* mHfGeom; + + PX_FORCE_INLINE HeightFieldUtil(const PxHeightFieldGeometry& hfGeom) : mHeightField(static_cast(hfGeom.heightField)), mHfGeom(&hfGeom) + { + const PxReal absRowScale = PxAbs(mHfGeom->rowScale); + const PxReal absColScale = PxAbs(mHfGeom->columnScale); + //warning #1931-D on WIIU: sizeof is not a type, variable, or dereferenced pointer expression + PX_ASSERT(sizeof(reinterpret_cast(0)->height) == 2); + //PxReal minHeightPerSample = PX_MIN_HEIGHTFIELD_Y_SCALE; + PX_ASSERT(mHfGeom->heightScale >= PX_MIN_HEIGHTFIELD_Y_SCALE); + PX_ASSERT(absRowScale >= PX_MIN_HEIGHTFIELD_XZ_SCALE); + PX_ASSERT(absColScale >= PX_MIN_HEIGHTFIELD_XZ_SCALE); + PX_UNUSED(absRowScale); + PX_UNUSED(absColScale); + //using physx::intrinsics::fsel; + //mOneOverHeightScale = fsel(mHfGeom->heightScale - minHeightPerSample, 1.0f / mHfGeom->heightScale, 1.0f / minHeightPerSample); + mOneOverHeightScale = 1.0f / mHfGeom->heightScale; + mOneOverRowScale = 1.0f / mHfGeom->rowScale; + mOneOverColumnScale = 1.0f / mHfGeom->columnScale; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const Gu::HeightField& getHeightField() const { return *mHeightField; } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxHeightFieldGeometry& getHeightFieldGeometry() const { return *mHfGeom; } + + PX_FORCE_INLINE PxReal getOneOverRowScale() const { return mOneOverRowScale; } + PX_FORCE_INLINE PxReal getOneOverHeightScale() const { return mOneOverHeightScale; } + PX_FORCE_INLINE PxReal getOneOverColumnScale() const { return mOneOverColumnScale; } + + void computeLocalBounds(PxBounds3& bounds) const; + PX_FORCE_INLINE bool isCollisionVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const + { + return mHeightField->isCollisionVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE); + } + PX_FORCE_INLINE PxReal getHeightAtShapePoint(PxReal x, PxReal z) const + { + return mHfGeom->heightScale * mHeightField->getHeightInternal(x * mOneOverRowScale, z * mOneOverColumnScale); + } + + PX_FORCE_INLINE PxVec3 getNormalAtShapePoint(PxReal x, PxReal z) const + { + return mHeightField->getNormal_(x * mOneOverRowScale, z * mOneOverColumnScale, mOneOverRowScale, mOneOverHeightScale, mOneOverColumnScale); + } + + PxU32 getFaceIndexAtShapePoint(PxReal x, PxReal z) const; + + PxVec3 getVertexNormal(PxU32 vertexIndex, PxU32 row, PxU32 column) const; + PX_FORCE_INLINE PxVec3 getVertexNormal(PxU32 vertexIndex) const + { + const PxU32 nbColumns = mHeightField->getData().columns; + const PxU32 row = vertexIndex / nbColumns; + const PxU32 column = vertexIndex % nbColumns; + return getVertexNormal(vertexIndex, row, column); + } + + PxU32 getVertexFaceIndex(PxU32 vertexIndex, PxU32 row, PxU32 column) const; + void getEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, PxVec3& origin, PxVec3& extent) const; + + PxU32 getEdgeFaceIndex(PxU32 edgeIndex) const; + PxU32 getEdgeFaceIndex(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const; + PxU32 getEdgeFaceIndex(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices) const; + + enum Feature { eFACE, eEDGE, eVERTEX }; + static PX_INLINE Feature getFeatureType(PxU32 featureCode) { return Feature(featureCode >> 30); } + static PX_INLINE PxU32 getFeatureIndex(PxU32 featureCode) { return (featureCode & ~0xC0000000); } + static PX_INLINE PxU32 makeFeatureCode(PxU32 index, Feature type) { return (index | (PxU32(type) << 30)); } + + // possible improvement: face index and edge index can be folded into one incremental feature index (along with vert index) + // such as, 0: vert index for cell, 1,2: edge indices, 3,4: face indices, then wrap around in multiples of 5 or 8 + // all return arrays must have a size of 11 to accomodate the possible 11 contacts + // the feature index in featureIndices array is encoded as follows: + // the high 2 bits are 00 - face, 01 - edge, 10 - vertex, 11 - unused/hole + PxU32 findClosestPointsOnCell( + PxU32 row, PxU32 column, PxVec3 point, + PxVec3* PX_RESTRICT closestPoints, PxU32* PX_RESTRICT featureCodes, + bool testFaces, bool testEdges, bool skipEdgesIfFaceHits) const; + + bool findProjectionOnTriangle(PxU32 triangleIndex, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& projection) const; + + PxReal findClosestPointOnEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& closestPoint) const; + + PxU32 getTriangle(const PxTransform&, PxTriangle& worldTri, + PxU32* vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation=true, bool worldSpaceRotation=true) const; + bool overlapAABBTriangles(const PxTransform&, const PxBounds3& bounds, PxU32 flags, EntityReport* callback) const; + + // check's if vertex can be used for scene query - note it is not the same as collision vertex + // the difference here is because of the eNO_BOUNDARY_EDGES flag, which should not ignore boundary edges. + // We don t have precomputed data for this case, we need to manually check these vertices if they are inside + // a hole or not - check if the vertex isSolid + PX_FORCE_INLINE bool isQueryVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const + { + // if noBoundaryEdges flag is on, we need to check the solid vertices manually for + // vertices which are on the boundaries, as those data are not precomputed + if((mHeightField->getFlags() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) && + (row == 0 || column == 0 || (row >= mHeightField->getNbRowsFast() - 1) || (column >= mHeightField->getNbColumnsFast() - 1))) + { + // early exit if the material0 for the vertex is not a hole + if(mHeightField->getMaterialIndex0(vertexIndex) != PxHeightFieldMaterial::eHOLE) + return true; + bool nbSolid; + return mHeightField->isSolidVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE, nbSolid); + } + else + { + return mHeightField->isCollisionVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE); + } + } + + PX_FORCE_INLINE PxVec3 computePointNormal(PxU32 meshFlags, const PxVec3& d, const PxTransform& transform, PxReal l_sqr, PxReal x, PxReal z, PxReal epsilon, PxReal& l) const + { + PX_UNUSED(meshFlags); + + PxVec3 n; + if(l_sqr > epsilon) + { + n = transform.rotate(d); + l = n.normalize(); + } + else // l == 0 + { + n = transform.rotate(getNormalAtShapePoint(x, z)); + n = n.getNormalized(); + l = PxSqrt(l_sqr); + } + return n; + } + + PX_INLINE bool isShapePointOnHeightField(PxReal x, PxReal z) const + { + x *= mOneOverRowScale; + z *= mOneOverColumnScale; +/* return ((!(x < 0)) + && (!(z < 0)) + && (x < (mHeightField.getNbRowsFast()-1)) + && (z < (mHeightField.getNbColumnsFast()-1)));*/ + return ((x >= 0.0f) + && (z >= 0.0f) + && (x < (mHeightField->getData().rowLimit+1.0f)) + && (z < (mHeightField->getData().colLimit+1.0f))); + } + + PX_FORCE_INLINE PxVec3 hf2shapen(const PxVec3& v) const + { + return PxVec3(v.x * mOneOverRowScale, v.y * mOneOverHeightScale, v.z * mOneOverColumnScale); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 shape2hfp(const PxVec3& v) const + { + return PxVec3(v.x * mOneOverRowScale, v.y * mOneOverHeightScale, v.z * mOneOverColumnScale); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 hf2shapep(const PxVec3& v) const + { + return PxVec3(v.x * mHfGeom->rowScale, v.y * mHfGeom->heightScale, v.z * mHfGeom->columnScale); + } + + PX_INLINE PxVec3 hf2worldp(const PxTransform& pose, const PxVec3& v) const + { + const PxVec3 s = hf2shapep(v); + return pose.transform(s); + } + + PX_INLINE PxVec3 hf2worldn(const PxTransform& pose, const PxVec3& v) const + { + const PxVec3 s = hf2shapen(v); + return pose.q.rotate(s); + } + }; + + class PX_PHYSX_COMMON_API HeightFieldTraceUtil : public HeightFieldUtil + { + public: + PX_FORCE_INLINE HeightFieldTraceUtil(const PxHeightFieldGeometry& hfGeom) : HeightFieldUtil(hfGeom) {} + + // floor and ceil don't clamp down exact integers but we want that + static PX_FORCE_INLINE PxF32 floorDown(PxF32 x) { PxF32 f = PxFloor(x); return (f == x) ? f-1 : f; } + static PX_FORCE_INLINE PxF32 ceilUp (PxF32 x) { PxF32 f = PxCeil (x); return (f == x) ? f+1 : f; } + + // helper class for testing triangle height and reporting the overlapped triangles + template + class OverlapTraceSegment + { + public: + // helper rectangle struct + struct OverlapRectangle + { + PxI32 mMinu; + PxI32 mMaxu; + PxI32 mMinv; + PxI32 mMaxv; + + void invalidate() + { + mMinu = 1; + mMaxu = -1; + mMinv = 1; + mMaxv = -1; + } + }; + + // helper line struct + struct OverlapLine + { + bool mColumn; + PxI32 mLine; + PxI32 mMin; + PxI32 mMax; + + void invalidate() + { + mMin = 1; + mMax = -1; + } + }; + + public: + void operator = (OverlapTraceSegment&) {} + + OverlapTraceSegment(const HeightFieldUtil& hfUtil,const Gu::HeightField& hf) + : mInitialized(false), mHfUtil(hfUtil), mHf(hf), mNbIndices(0) {} + + PX_FORCE_INLINE bool initialized() const { return mInitialized; } + + // prepare for iterations, set the expand u|v + PX_INLINE void prepare(const PxVec3& aP0, const PxVec3& aP1, const PxVec3& overlapObjectExtent, PxF32& expandu, PxF32& expandv) + { + // height test bounds + mMinY = (PxMin(aP1.y,aP0.y) - overlapObjectExtent.y) * mHfUtil.getOneOverHeightScale(); + mMaxY = (PxMax(aP1.y,aP0.y) + overlapObjectExtent.y) * mHfUtil.getOneOverHeightScale(); + + // sets the clipping variables + mMinRow = PxI32(mHf.getMinRow((PxMin(aP1.x,aP0.x) - overlapObjectExtent.x)* mHfUtil.getOneOverRowScale())); + mMaxRow = PxI32(mHf.getMaxRow((PxMax(aP1.x,aP0.x) + overlapObjectExtent.x)* mHfUtil.getOneOverRowScale())); + mMinColumn = PxI32(mHf.getMinColumn((PxMin(aP1.z,aP0.z) - overlapObjectExtent.z)* mHfUtil.getOneOverColumnScale())); + mMaxColumn = PxI32(mHf.getMaxColumn((PxMax(aP1.z,aP0.z) + overlapObjectExtent.z)* mHfUtil.getOneOverColumnScale())); + + // sets the expanded u|v coordinates + expandu = PxCeil(overlapObjectExtent.x*mHfUtil.getOneOverRowScale()); + expandv = PxCeil(overlapObjectExtent.z*mHfUtil.getOneOverColumnScale()); + + // sets the offset that will be overlapped in each axis + mOffsetU = PxI32(expandu) + 1; + mOffsetV = PxI32(expandv) + 1; + } + + // sets all necessary variables and makes initial rectangle setup and overlap + PX_INLINE bool init(const PxI32 ui, const PxI32 vi, const PxI32 nbVi, const PxI32 step_ui, const PxI32 step_vi, T* aCallback) + { + mInitialized = true; + mCallback = aCallback; + mNumColumns = nbVi; + mStep_ui = step_ui > 0 ? 0 : -1; + mStep_vi = step_vi > 0 ? 0 : -1; + + // sets the rectangles + mCurrentRectangle.invalidate(); + mPreviousRectangle.mMinu = ui - mOffsetU; + mPreviousRectangle.mMaxu = ui + mOffsetU; + mPreviousRectangle.mMinv = vi - mOffsetV; + mPreviousRectangle.mMaxv = vi + mOffsetV; + + // visits all cells in given initial rectangle + if(!visitCells(mPreviousRectangle)) + return false; + + // reports all overlaps + if(!reportOverlaps()) + return false; + + return true; + } + + // u|v changed, check for new rectangle - compare with previous one and parse + // the added line, which is a result from the rectangle compare + PX_INLINE bool step(const PxI32 ui, const PxI32 vi) + { + mCurrentRectangle.mMinu = ui - mOffsetU; + mCurrentRectangle.mMaxu = ui + mOffsetU; + mCurrentRectangle.mMinv = vi - mOffsetV; + mCurrentRectangle.mMaxv = vi + mOffsetV; + OverlapLine line; + computeRectangleDifference(mCurrentRectangle,mPreviousRectangle,line); + + if(!visitCells(line)) + return false; + if(!reportOverlaps()) + return false; + + mPreviousRectangle = mCurrentRectangle; + return true; + } + + PX_INLINE void computeRectangleDifference(const OverlapRectangle& currentRectangle, const OverlapRectangle& previousRectangle, OverlapLine& line) + { + // check if u changes - add the row for visit + if(currentRectangle.mMinu != previousRectangle.mMinu) + { + line.mColumn = false; + line.mLine = currentRectangle.mMinu < previousRectangle.mMinu ? currentRectangle.mMinu : currentRectangle.mMaxu; + line.mMin = currentRectangle.mMinv; + line.mMax = currentRectangle.mMaxv; + return; + } + + // check if v changes - add the column for visit + if(currentRectangle.mMinv != previousRectangle.mMinv) + { + line.mColumn = true; + line.mLine = currentRectangle.mMinv < previousRectangle.mMinv ? currentRectangle.mMinv : currentRectangle.mMaxv; + line.mMin = currentRectangle.mMinu; + line.mMax = currentRectangle.mMaxu; + } + } + + // visits all cells in given rectangle + PX_INLINE bool visitCells(const OverlapRectangle& rectangle) + { + for(PxI32 ui = rectangle.mMinu + mStep_ui; ui <= rectangle.mMaxu + mStep_ui; ui++) + { + if(ui < mMinRow) + continue; + if(ui >= mMaxRow) + break; + for(PxI32 vi = rectangle.mMinv + mStep_vi; vi <= rectangle.mMaxv + mStep_vi; vi++) + { + if(vi < mMinColumn) + continue; + if(vi >= mMaxColumn) + break; + const PxI32 vertexIndex = ui*mNumColumns + vi; + if(!testVertexIndex(PxU32(vertexIndex))) + return false; + } + } + return true; + } + + // visits all cells in given line - can be row or column + PX_INLINE bool visitCells(const OverlapLine& line) + { + if(line.mMin > line.mMax) + return true; + + if(line.mColumn) + { + const PxI32 vi = line.mLine + mStep_vi; + // early exit if column is out of hf clip area + if(vi < mMinColumn) + return true; + if(vi >= mMaxColumn) + return true; + + for(PxI32 ui = line.mMin + mStep_ui; ui <= line.mMax + mStep_ui; ui++) + { + // early exit or continue if row is out of hf clip area + if(ui >= mMaxRow) + break; + // continue if we did not reach the valid area, we can still get there + if(ui < mMinRow) + continue; + // if the cell has not been tested test and report + if(!testVertexIndex(PxU32(mNumColumns * ui + vi))) + return false; + } + } + else + { + const PxI32 ui = line.mLine + mStep_ui; + // early exit if row is out of hf clip area + if(ui < mMinRow) + return true; + if(ui >= mMaxRow) + return true; + + for(PxI32 vi = line.mMin + mStep_vi; vi <= line.mMax + mStep_vi; vi++) + { + // early exit or continue if column is out of hf clip area + if(vi >= mMaxColumn) + break; + // continue if we did not reach the valid area, we can still get there + if(vi < mMinColumn) + continue; + // if the cell has not been tested test and report + if(!testVertexIndex(PxU32(mNumColumns * ui + vi))) + return false; + } + } + return true; + } + + // does height check and if succeeded adds to report + PX_INLINE bool testVertexIndex(const PxU32 vertexIndex) + { +#define ISHOLE0 (mHf.getMaterialIndex0(vertexIndex) == PxHeightFieldMaterial::eHOLE) +#define ISHOLE1 (mHf.getMaterialIndex1(vertexIndex) == PxHeightFieldMaterial::eHOLE) + const PxReal h0 = mHf.getHeight(vertexIndex); + const PxReal h1 = mHf.getHeight(vertexIndex + 1); + const PxReal h2 = mHf.getHeight(vertexIndex + mNumColumns); + const PxReal h3 = mHf.getHeight(vertexIndex + mNumColumns + 1); + // actual height test, if some height pass we accept the cell + if(!((mMaxY < h0 && mMaxY < h1 && mMaxY < h2 && mMaxY < h3) || (mMinY > h0 && mMinY > h1 && mMinY > h2 && mMinY > h3))) + { + // check if the triangle is not a hole + if(!ISHOLE0) + { + if(!addIndex(vertexIndex*2)) + return false; + } + if(!ISHOLE1) + { + if(!addIndex(vertexIndex*2 + 1)) + return false; + } + } +#undef ISHOLE0 +#undef ISHOLE1 + return true; + } + + // add triangle index, if we get out of buffer size, report them + bool addIndex(PxU32 triangleIndex) + { + if(mNbIndices == HF_SWEEP_REPORT_BUFFER_SIZE) + { + if(!reportOverlaps()) + return false; + } + + mIndexBuffer[mNbIndices++] = triangleIndex; + return true; + } + + PX_FORCE_INLINE bool reportOverlaps() + { + if(mNbIndices) + { + if(!mCallback->onEvent(mNbIndices, mIndexBuffer)) + return false; + mNbIndices = 0; + } + return true; + } + + private: + bool mInitialized; + const HeightFieldUtil& mHfUtil; + const Gu::HeightField& mHf; + T* mCallback; + PxI32 mOffsetU; + PxI32 mOffsetV; + float mMinY; + float mMaxY; + PxI32 mMinRow; + PxI32 mMaxRow; + PxI32 mMinColumn; + PxI32 mMaxColumn; + PxI32 mNumColumns; + PxI32 mStep_ui; + PxI32 mStep_vi; + OverlapRectangle mPreviousRectangle; + OverlapRectangle mCurrentRectangle; + PxU32 mIndexBuffer[HF_SWEEP_REPORT_BUFFER_SIZE]; + PxU32 mNbIndices; + }; + + // If useUnderFaceCalblack is false, traceSegment will report segment/triangle hits via + // faceHit(const Gu::HeightFieldUtil& hf, const PxVec3& point, PxU32 triangleIndex) + // Otherwise traceSegment will report all triangles the segment passes under via + // underFaceHit(const Gu::HeightFieldUtil& hf, const PxVec3& triNormal, const PxVec3& crossedEdge, + // PxF32 x, PxF32 z, PxF32 rayHeight, PxU32 triangleIndex) + // where x,z is the point of previous intercept in hf coords, rayHeight is at that same point + // crossedEdge is the edge vector crossed from last call to underFaceHit, undefined for first call + // Note that underFaceHit can be called when a line is above a triangle if it's within AABB for that hf cell + // Note that backfaceCull is ignored if useUnderFaceCallback is true + // overlapObjectExtent (localSpace) and overlap are used for triangle collecting using an inflated tracesegment + // Note that hfLocalBounds are passed as a parameter instead of being computed inside the traceSegment. + // The localBounds can be obtained: PxBounds3 hfLocalBounds; hfUtil.computeLocalBounds(hfLocalBounds); and passed as + // a parameter. + template + PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDir, const float rayLength , T* aCallback, const PxBounds3& hfLocalBounds, bool backfaceCull, + const PxVec3* overlapObjectExtent = NULL) const + { + PxF32 tnear, tfar; + if(!Gu::intersectRayAABB2(hfLocalBounds.minimum, hfLocalBounds.maximum, aP0, rayDir, rayLength, tnear, tfar)) + return; + + const PxVec3 p0 = aP0 + rayDir * tnear; + const PxVec3 p1 = aP0 + rayDir * tfar; + + // helper class used for overlap tests + OverlapTraceSegment overlapTraceSegment(*this, *mHeightField); + + // values which expand the HF area + PxF32 expandu = 0.0f, expandv = 0.0f; + + if (overlap) + { + // setup overlap variables + overlapTraceSegment.prepare(aP0,aP0 + rayDir*rayLength,*overlapObjectExtent,expandu,expandv); + } + + // row = x|u, column = z|v + const PxF32 rowScale = mHfGeom->rowScale, columnScale = mHfGeom->columnScale, heightScale = mHfGeom->heightScale; + const PxI32 nbVi = PxI32(mHeightField->getNbColumnsFast()), nbUi = PxI32(mHeightField->getNbRowsFast()); + PX_ASSERT(nbVi > 0 && nbUi > 0); + + // clampEps is chosen so that we get a reasonable clamp value for 65536*0.9999999f = 65535.992187500000 + const PxF32 clampEps = 1e-7f; // shrink u,v to within 1e-7 away from the world bounds + + // we now clamp uvs to [1e-7, rowLimit-1e-7] to avoid out of range uvs and eliminate related checks in the loop + const PxF32 nbUcells = PxF32(nbUi-1)*(1.0f-clampEps), nbVcells = PxF32(nbVi-1)*(1.0f-clampEps); + + // if u0,v0 is near an integer, shift up or down in direction opposite to du,dv by PxMax(|u,v|*1e-7, 1e-7) + // (same direction as du,dv for u1,v1) + // we do this to ensure that we get at least one intersection with u or v when near the cell edge to eliminate special cases in the loop + // we need to extend the field for the inflated radius, we will now operate even with negative u|v + + // map p0 from (x, z, y) to (u0, v0, h0) + // we need to use the unclamped values, otherwise we change the direction of the traversal + const PxF32 uu0 = p0.x * mOneOverRowScale; + PxF32 u0 = PxMin(PxMax(uu0, 1e-7f - expandu), nbUcells + expandu); // multiplication rescales the u,v grid steps to 1 + const PxF32 uv0 = p0.z * mOneOverColumnScale; + PxF32 v0 = PxMin(PxMax(uv0, 1e-7f - expandv), nbVcells + expandv); + const PxReal h0 = p0.y; // we don't scale y + + // map p1 from (x, z, y) to (u1, v1, h1) + // we need to use the unclamped values, otherwise we change the direction of the traversal + const PxF32 uu1 = p1.x * mOneOverRowScale; + const PxF32 uv1 = p1.z * mOneOverColumnScale; + const PxReal h1 = p1.y; // we don't scale y + + PxF32 du = uu1 - uu0, dv = uv1 - uv0; // recompute du, dv from adjusted uvs + const PxReal dh = h1 - h0; + + // grid u&v step is always either 1 or -1, we precompute as both integers and floats to avoid conversions + // so step_uif is +/-1.0f, step_ui is +/-1 + const PxF32 step_uif = PxSign(du), step_vif = PxSign(dv); + const PxI32 step_ui = PxI32(step_uif), step_vi = PxI32(step_vif); + + // clamp magnitude of du, dv to at least clampEpsilon to avoid special cases when dividing + const PxF32 divEpsilon = 1e-10f; + if(PxAbs(du) < divEpsilon) + du = step_uif * divEpsilon; + if(PxAbs(dv) < divEpsilon) + dv = step_vif * divEpsilon; + + const PxVec3 auhP0(aP0.x*mOneOverRowScale, aP0.y, aP0.z*mOneOverColumnScale); + const PxVec3 duhv(rayDir.x*rayLength*mOneOverRowScale, rayDir.y*rayLength, rayDir.z*rayLength*mOneOverColumnScale); + const PxReal duhvLength = duhv.magnitude(); + PxVec3 duhvNormalized = duhv; + if(duhvLength > PX_NORMALIZATION_EPSILON) + duhvNormalized *= 1.0f/duhvLength; + + // Math derivation: + // points on 2d segment are parametrized as: [u0,v0] + t [du, dv]. We solve for t_u[n], t for nth u-intercept + // u0 + t_un du = un + // t_un = (un-u0) / du + // t_un1 = (un+1-u0) / du ; we use +1 since we rescaled the grid step to 1 + // therefore step_tu = t_un - t_un1 = 1/du + + // seed the initial integer cell coordinates with u0, v0 rounded up or down with standard PxFloor/Ceil behavior + // to ensure we have the correct first cell between (ui,vi) and (ui+step_ui,vi+step_vi) + PxI32 ui = (du > 0.0f) ? PxI32(PxFloor(u0)) : PxI32(PxCeil(u0)); + PxI32 vi = (dv > 0.0f) ? PxI32(PxFloor(v0)) : PxI32(PxCeil(v0)); + + // find the nearest integer u, v in ray traversal direction and corresponding tu and tv + const PxReal uhit0 = du > 0.0f ? ceilUp(u0) : floorDown(u0); + const PxReal vhit0 = dv > 0.0f ? ceilUp(v0) : floorDown(v0); + + // tu, tv can be > 1 but since the loop is structured as do {} while(tMin < tEnd) we still visit the first cell + PxF32 last_tu = 0.0f, last_tv = 0.0f; + PxReal tu = (uhit0 - uu0) / du; + PxReal tv = (vhit0 - uv0) / dv; + if(tu < 0.0f) // negative value may happen, as we may have started out of the AABB (since we did enlarge it) + tu = PxAbs(clampEps / du); + if(tv < 0.0f) // negative value may happen, as we may have started out of the AABB (since we did enlarge it) + tv = PxAbs(clampEps / dv); + + // compute step_tu and step_tv; t steps per grid cell in u and v direction + const PxReal step_tu = 1.0f / PxAbs(du), step_tv = 1.0f / PxAbs(dv); + + // t advances at the same rate for u, v and h therefore we can compute h at u,v grid intercepts + #define COMPUTE_H_FROM_T(t) (h0 + (t) * dh) + + const PxF32 hEpsilon = 1e-4f; + PxF32 uif = PxF32(ui), vif = PxF32(vi); + + // these are used to remap h values to correspond to u,v increasing order + PxI32 uflip = 1-step_ui; /*0 or 2*/ + PxI32 vflip = (1-step_vi)/2; /*0 or 1*/ + + // this epsilon is needed to ensure that we include the last [t, t+1] range in the do {} while(t= 0 - expandu && ui < nbUi + expandu && vi >= 0 - expandv && vi < nbVi + expandv); + PX_ASSERT(ui+step_ui >= 0 - expandu && ui+step_ui < nbUi + expandu && vi+step_vi >= 0 - expandv && vi+step_vi < nbVi + expandv); + + // handle overlap in overlapCallback + if(overlap) + { + if(!overlapTraceSegment.initialized()) + { + // initial overlap and setup + if(!overlapTraceSegment.init(ui,vi,nbVi,step_ui,step_vi,aCallback)) + return; + } + else + { + // overlap step + if(!overlapTraceSegment.step(ui,vi)) + return; + } + } + else + { + const PxU32 colIndex0 = PxU32(nbVi * ui + vi); + const PxU32 colIndex1 = PxU32(nbVi * (ui + step_ui) + vi); + const PxReal h[4] = { // h[0]=h00, h[1]=h01, h[2]=h10, h[3]=h11 - oriented relative to step_uv + hf.getHeight(colIndex0) * heightScale, hf.getHeight(colIndex0 + step_vi) * heightScale, + hf.getHeight(colIndex1) * heightScale, hf.getHeight(colIndex1 + step_vi) * heightScale }; + + PxF32 minH = PxMin(PxMin(h[0], h[1]), PxMin(h[2], h[3])); + PxF32 maxH = PxMax(PxMax(h[0], h[1]), PxMax(h[2], h[3])); + + // how much space in h have we covered from previous to current u or v intercept + PxF32 hLineCellRangeMin = PxMin(hLinePrev, hLineNext); + PxF32 hLineCellRangeMax = PxMax(hLinePrev, hLineNext); + + // do a quick overlap test in h, this should be rejecting the vast majority of tests + if(!(hLineCellRangeMin-hEpsilon > maxH || hLineCellRangeMax+hEpsilon < minH) || + (useUnderFaceCallback && hLineCellRangeMax < maxH)) + { + // arrange h so that h00 corresponds to min(uif, uif+step_uif) h10 to max et c. + // this is only needed for backface culling to work so we know the proper winding order without branches + // uflip is 0 or 2, vflip is 0 or 1 (corresponding to positive and negative ui_step and vi_step) + PxF32 h00 = h[0+uflip+vflip]; + PxF32 h01 = h[1+uflip-vflip]; + PxF32 h10 = h[2-uflip+vflip]; + PxF32 h11 = h[3-uflip-vflip]; + + PxF32 minuif = PxMin(uif, uif+step_uif); + PxF32 maxuif = PxMax(uif, uif+step_uif); + PxF32 minvif = PxMin(vif, vif+step_vif); + PxF32 maxvif = PxMax(vif, vif+step_vif); + PxVec3 p00(minuif, h00, minvif); + PxVec3 p01(minuif, h01, maxvif); + PxVec3 p10(maxuif, h10, minvif); + PxVec3 p11(maxuif, h11, maxvif); + + const PxF32 enlargeEpsilon = 0.0001f; + const PxVec3* p00a = &p00, *p01a = &p01, *p10a = &p10, *p11a = &p11; + PxU32 minui = PxU32(PxMin(ui+step_ui, ui)), minvi = PxU32(PxMin(vi+step_vi, vi)); + + // row = x|u, column = z|v + const PxU32 vertIndex = nbVi * minui + minvi; + const PxU32 cellIndex = vertIndex; // this adds a dummy unused cell in the end of each row; was -minui + bool isZVS = hf.isZerothVertexShared(vertIndex); + if(!isZVS) + { + // rotate the pointers for flipped edge cells + p10a = &p00; + p00a = &p01; + p01a = &p11; + p11a = &p10; + } + + // For triangle index computation, see illustration in Gu::HeightField::getTriangleNormal() + // Since row = u, column = v + // for zeroth vert shared the 10 index is the corner of the 0-index triangle, and 01 is 1-index + // if zeroth vertex is not shared, the 00 index is the corner of 0-index triangle + if(!useUnderFaceCallback) + { + #define ISHOLE0 (hf.getMaterialIndex0(vertIndex) == PxHeightFieldMaterial::eHOLE) + #define ISHOLE1 (hf.getMaterialIndex1(vertIndex) == PxHeightFieldMaterial::eHOLE) + PxReal triT0 = PX_MAX_REAL, triT1 = PX_MAX_REAL; + bool hit0 = false, hit1 = false; + PxF32 triU0, triV0, triU1, triV1; + if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p10a, *p00a, *p11a, triT0, triU0, triV0, backfaceCull, enlargeEpsilon) && + triT0 >= 0.0f && triT0 <= duhvLength && !ISHOLE0) + { + hit0 = true; + } else + triT0 = PX_MAX_REAL; + if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p01a, *p11a, *p00a, triT1, triU1, triV1, backfaceCull, enlargeEpsilon) + && triT1 >= 0.0f && triT1 <= duhvLength && !ISHOLE1) + { + hit1 = true; + } else + triT1 = PX_MAX_REAL; + + if(hit0 && triT0 <= triT1) + { + const PxVec3 hitPoint((auhP0.x + duhvNormalized.x*triT0) * rowScale, auhP0.y + duhvNormalized.y * triT0, (auhP0.z + duhvNormalized.z*triT0) * columnScale); + if(!aCallback->faceHit(*this, hitPoint, cellIndex*2, triU0, triV0)) + return; + if(hit1) // possible to hit both triangles in a cell with eMESH_MULTIPLE + { + PxVec3 hitPoint1((auhP0.x + duhvNormalized.x*triT1) * rowScale, auhP0.y + duhvNormalized.y * triT1, (auhP0.z + duhvNormalized.z*triT1) * columnScale); + if(!aCallback->faceHit(*this, hitPoint1, cellIndex*2 + 1, triU1, triV1)) + return; + } + } + else if(hit1 && triT1 <= triT0) + { + PxVec3 hitPoint((auhP0.x + duhvNormalized.x*triT1) * rowScale, auhP0.y + duhvNormalized.y * triT1, (auhP0.z + duhvNormalized.z*triT1) * columnScale); + if(!aCallback->faceHit(*this, hitPoint, cellIndex*2 + 1, triU1, triV1)) + return; + if(hit0) // possible to hit both triangles in a cell with eMESH_MULTIPLE + { + PxVec3 hitPoint1((auhP0.x + duhvNormalized.x*triT0) * rowScale, auhP0.y + duhvNormalized.y * triT0, (auhP0.z + duhvNormalized.z*triT0) * columnScale); + if(!aCallback->faceHit(*this, hitPoint1, cellIndex*2, triU0, triV0)) + return; + } + } + #undef ISHOLE0 + #undef ISHOLE1 + } + else + { + // TODO: quite a few optimizations are possible here. edges can be shared, intersectRayTriangle inlined etc + // Go to shape space. Height is already in shape space so we only scale x and z + PxVec3 p00s(p00a->x * rowScale, p00a->y, p00a->z * columnScale); + PxVec3 p01s(p01a->x * rowScale, p01a->y, p01a->z * columnScale); + PxVec3 p10s(p10a->x * rowScale, p10a->y, p10a->z * columnScale); + PxVec3 p11s(p11a->x * rowScale, p11a->y, p11a->z * columnScale); + + PxVec3 triNormals[2] = { (p00s - p10s).cross(p11s - p10s), (p11s - p01s).cross(p00s-p01s) }; + triNormals[0] *= PxRecipSqrt(triNormals[0].magnitudeSquared()); + triNormals[1] *= PxRecipSqrt(triNormals[1].magnitudeSquared()); + // since the heightfield can be mirrored with negative rowScale or columnScale, this assert doesn't hold + //PX_ASSERT(triNormals[0].y >= 0.0f && triNormals[1].y >= 0.0f); + + // at this point we need to compute the edge direction that we crossed + // also since we don't DDA the w we need to find u,v for w-intercept (w refers to diagonal adjusted with isZVS) + PxF32 wnu = isZVS ? -1.0f : 1.0f, wnv = 1.0f; // uv-normal to triangle edge that splits the cell + PxF32 wpu = uif + 0.5f * step_uif, wpv = vif + 0.5f * step_vif; // a point on triangle edge that splits the cell + // note that (wpu, wpv) is on both edges (for isZVS and non-ZVS cases) which is nice + + // we clamp tNext to 1 because we still want to issue callbacks even if we stay in one cell + // note that tNext can potentially be arbitrarily large for a segment contained within a cell + PxF32 tNext = PxMin(PxMin(tu, tv), 1.0f), tPrev = PxMax(last_tu, last_tv); + + // compute uvs corresponding to tPrev, tNext + PxF32 unext = u0 + tNext*du, vnext = v0 + tNext*dv; + PxF32 uprev = u0 + tPrev*du, vprev = v0 + tPrev*dv; + + const PxReal& h00_ = h[0], &h01_ = h[1], &h10_ = h[2]/*, h11_ = h[3]*/; // aliases for step-oriented h + + // (wpu, wpv) is a point on the diagonal + // we compute a dot of ((unext, vnext) - (wpu, wpv), wn) to see on which side of triangle edge we are + // if the dot is positive we need to add 1 to triangle index + PxU32 dotPrevGtz = PxU32(((uprev - wpu) * wnu + (vprev - wpv) * wnv) > 0); + PxU32 dotNextGtz = PxU32(((unext - wpu) * wnu + (vnext - wpv) * wnv) > 0); + PxU32 triIndex0 = cellIndex*2 + dotPrevGtz; + PxU32 triIndex1 = cellIndex*2 + dotNextGtz; + PxU32 isHole0 = PxU32(hf.getMaterialIndex0(vertIndex) == PxHeightFieldMaterial::eHOLE); + PxU32 isHole1 = PxU32(hf.getMaterialIndex1(vertIndex) == PxHeightFieldMaterial::eHOLE); + if(triIndex0 > triIndex1) + shdfnd::swap(isHole0, isHole1); + + // TODO: compute height at u,v inside here, change callback param to PxVec3 + PxVec3 crossedEdge; + if(last_tu > last_tv) // previous intercept was at u, so we use u=const edge + crossedEdge = PxVec3(0.0f, h01_-h00_, step_vif * columnScale); + else // previous intercept at v, use v=const edge + crossedEdge = PxVec3(step_uif * rowScale, h10_-h00_, 0.0f); + + if(!isHole0 && !aCallback->underFaceHit(*this, triNormals[dotPrevGtz], crossedEdge, + uprev * rowScale, vprev * columnScale, COMPUTE_H_FROM_T(tPrev), triIndex0)) + return; + + if(triIndex1 != triIndex0 && !isHole1) // if triIndex0 != triIndex1 that means we cross the triangle edge + { + // Need to compute tw, the t for ray intersecting the diagonal within the current cell + // dot((wnu, wnv), (u0+tw*du, v0+tw*dv)-(wpu, wpv)) = 0 + // wnu*(u0+tw*du-wpu) + wnv*(v0+tw*dv-wpv) = 0 + // wnu*u0+wnv*v0-wnu*wpu-wnv*wpv + tw*(du*wnu + dv*wnv) = 0 + const PxF32 denom = du*wnu + dv*wnv; + if(PxAbs(denom) > 1e-6f) + { + const PxF32 tw = (wnu*(wpu-u0)+wnv*(wpv-v0)) / denom; + if(!aCallback->underFaceHit(*this, triNormals[dotNextGtz], p10s-p01s, + (u0+tw*du) * rowScale, (v0+tw*dv) * columnScale, COMPUTE_H_FROM_T(tw), triIndex1)) + return; + } + } + } + } + } + + if(tu < tv) + { + last_tu = tu; + ui += step_ui; + // AP: very rare condition, wasn't able to repro but we need this if anyway (DE6565) + if(ui+step_ui< (0 - expandu) || ui+step_ui>=(nbUi + expandu)) // should hold true for ui without step from previous iteration + break; + uif += step_uif; + tu += step_tu; + } + else + { + last_tv = tv; + vi += step_vi; + // AP: very rare condition, wasn't able to repro but we need this if anyway (DE6565) + if(vi+step_vi< (0 - expandv) || vi+step_vi>=(nbVi + expandv)) // should hold true for vi without step from previous iteration + break; + vif += step_vif; + tv += step_tv; + } + hLinePrev = hLineNext; + } + // since min(tu,tv) is the END of the active interval we need to check if PREVIOUS min(tu,tv) was past interval end + // since we update tMinUV in the beginning of the loop, at this point it stores the min(last tu,last tv) + while (tMinUV < tEnd); + #undef COMPUTE_H_FROM_T + } + }; + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp new file mode 100644 index 000000000..4b14b885e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp @@ -0,0 +1,751 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "PsAllocator.h" +#include "GuOverlapTests.h" + +#include "PsUtilities.h" +#include "PsVecMath.h" + +#include "GuHeightFieldUtil.h" +#include "GuIntersectionBoxBox.h" +#include "GuIntersectionTriangleBox.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentBox.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentSegmentSIMD.h" + +#include "PxSphereGeometry.h" +#include "PxBoxGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" + +#include "GuCapsule.h" +#include "GuEdgeCache.h" +#include "GuBoxConversion.h" + +#include "GuInternal.h" +#include "GuConvexUtilsInternal.h" + +#include "GuVecTriangle.h" +#include "GuVecSphere.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuConvexMesh.h" + +using namespace physx; +using namespace Cm; +using namespace Gu; +using namespace Ps::aos; + +static bool intersectHeightFieldSphere(const HeightFieldUtil& hfUtil, const Sphere& sphereInHfShape) +{ + const HeightField& hf = hfUtil.getHeightField(); + + // sample the sphere center in the heightfield to find out + // if we have penetration with more than the sphere radius + if(hfUtil.isShapePointOnHeightField(sphereInHfShape.center.x, sphereInHfShape.center.z)) + { + // The sphere origin projects within the bounds of the heightfield in the X-Z plane + PxReal sampleHeight = hfUtil.getHeightAtShapePoint(sphereInHfShape.center.x, sphereInHfShape.center.z); + PxReal deltaHeight = sphereInHfShape.center.y - sampleHeight; + if(hf.isDeltaHeightInsideExtent(deltaHeight)) + { + // The sphere origin is 'below' the heightfield surface + PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(sphereInHfShape.center.x, sphereInHfShape.center.z); + if(faceIndex != 0xffffffff) + { + return true; + } + return false; + } + } + + const PxReal radiusSquared = sphereInHfShape.radius * sphereInHfShape.radius; + + const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape.center); + + const PxReal radiusOverRowScale = sphereInHfShape.radius * PxAbs(hfUtil.getOneOverRowScale()); + const PxReal radiusOverColumnScale = sphereInHfShape.radius * PxAbs(hfUtil.getOneOverColumnScale()); + + const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale); + const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale); + const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale); + const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale); + + for(PxU32 r = minRow; r < maxRow; r++) + { + for(PxU32 c = minColumn; c < maxColumn; c++) + { + + // x--x--x + // | x | + // x x x + // | x | + // x--x--x + PxVec3 pcp[11]; + PxU32 npcp = 0; + npcp = hfUtil.findClosestPointsOnCell(r, c, sphereInHfShape.center, pcp, NULL, true, true, true); + + for(PxU32 pi = 0; pi < npcp; pi++) + { + PxVec3 d = sphereInHfShape.center - pcp[pi]; + + PxReal ll = d.magnitudeSquared(); + + if(ll > radiusSquared) + // Too far + continue; + + return true; + } + } + } + return false; +} + +static bool intersectHeightFieldCapsule(const HeightFieldUtil& hfUtil, const PxCapsuleGeometry& capsuleGeom, const PxTransform& capsulePose) +{ + const HeightField& hf = hfUtil.getHeightField(); + + PxVec3 verticesInHfShape[2]; + PxVec3 capsuleOrigin, capsuleExtent; + { + const PxVec3 capsuleHalfHeightVector = getCapsuleHalfHeightVector(capsulePose, capsuleGeom); + + capsuleOrigin = capsulePose.p + capsuleHalfHeightVector; + capsuleExtent = -capsuleHalfHeightVector*2.0f; + + verticesInHfShape[0] = capsuleOrigin; + verticesInHfShape[1] = capsulePose.p - capsuleHalfHeightVector; + } + + const PxReal radius = capsuleGeom.radius; + const PxReal radiusOverRowScale = radius * PxAbs(hfUtil.getOneOverRowScale()); + const PxReal radiusOverColumnScale = radius * PxAbs(hfUtil.getOneOverColumnScale()); + + PxU32 absMinRow = 0xffffffff; + PxU32 absMaxRow = 0; + PxU32 absMinColumn = 0xffffffff; + PxU32 absMaxColumn = 0; + + PxReal radiusSquared = radius * radius; + + // test both of capsule's corner vertices+radius for HF overlap + for(PxU32 i = 0; i<2; i++) + { + const PxVec3& sphereInHfShape = verticesInHfShape[i]; + + // we have to do this first to update the absMin / absMax correctly even if + // we decide to continue from inside the deep penetration code. + + const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape); + + const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale); + const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale); + const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale); + const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale); + + if(minRow < absMinRow) absMinRow = minRow; + if(minColumn < absMinColumn) absMinColumn = minColumn; + if(maxRow > absMaxRow) absMaxRow = maxRow; + if(maxColumn > absMaxColumn) absMaxColumn = maxColumn; + + if(hfUtil.isShapePointOnHeightField(sphereInHfShape.x, sphereInHfShape.z)) + { + // The sphere origin projects within the bounds of the heightfield in the X-Z plane + const PxReal sampleHeight = hfUtil.getHeightAtShapePoint(sphereInHfShape.x, sphereInHfShape.z); + const PxReal deltaHeight = sphereInHfShape.y - sampleHeight; + if(hf.isDeltaHeightInsideExtent(deltaHeight)) + { + // The sphere origin is 'below' the heightfield surface + const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(sphereInHfShape.x, sphereInHfShape.z); + if(faceIndex != 0xffffffff) + { + return true; + } + continue; + } + } + + for(PxU32 r = minRow; r < maxRow; r++) + { + for(PxU32 c = minColumn; c < maxColumn; c++) + { + + // x--x--x + // | x | + // x x x + // | x | + // x--x--x + PxVec3 pcp[11]; + PxU32 npcp = 0; + npcp = hfUtil.findClosestPointsOnCell(r, c, sphereInHfShape, pcp, NULL, true, true, true); + + for(PxU32 pi = 0; pi < npcp; pi++) + { + const PxVec3 d = sphereInHfShape - pcp[pi]; + + if(hf.isDeltaHeightOppositeExtent(d.y)) + { + // We are 'above' the heightfield + + const PxReal ll = d.magnitudeSquared(); + if(ll > radiusSquared) + // Too far above + continue; + + return true; + } + } + } + } + } + + const Vec3V p1 = V3LoadU(capsuleOrigin); + const Vec3V d1 = V3LoadU(capsuleExtent); + + // now test capsule's inflated segment for overlap with HF edges + PxU32 row, column; + for(row = absMinRow; row <= absMaxRow; row++) + { + for(column = absMinColumn; column <= absMaxColumn; column++) + { + const PxU32 vertexIndex = row * hf.getNbColumnsFast() + column; + const PxU32 firstEdge = 3 * vertexIndex; + // omg I am sorry about this code but I can't find a simpler way: + // last column will only test edge 2 + // last row will only test edge 0 + // and most importantly last row and column will not go inside the for + const PxU32 minEi = (column == absMaxColumn) ? 2u : 0; + const PxU32 maxEi = (row == absMaxRow ) ? 1u : 3u; + for(PxU32 ei = minEi; ei < maxEi; ei++) + { + const PxU32 edgeIndex = firstEdge + ei; + + const PxU32 cell = vertexIndex; + PX_ASSERT(cell == edgeIndex / 3); + const PxU32 row_ = row; + PX_ASSERT(row_ == cell / hf.getNbColumnsFast()); + const PxU32 column_ = column; + PX_ASSERT(column_ == cell % hf.getNbColumnsFast()); + + const PxU32 faceIndex = hfUtil.getEdgeFaceIndex(edgeIndex, cell, row_, column_); + if(faceIndex != 0xffffffff) + { + PxVec3 origin; + PxVec3 direction; + hfUtil.getEdge(edgeIndex, cell, row_, column_, origin, direction); + + const Vec3V p2 = V3LoadU(origin); + const Vec3V d2 = V3LoadU(direction); + FloatV s, t; + const FloatV llV = Gu::distanceSegmentSegmentSquared(p1, d1, p2, d2, s, t); + + PxReal ll; + FStore(llV, &ll); + + if(ll < radiusSquared) + return true; + } + } + } + } + return false; +} + +namespace physx +{ +namespace Gu +{ + const PxReal signs[24] = + { + -1,-1,-1, + -1,-1, 1, + -1, 1,-1, + -1, 1, 1, + 1,-1,-1, + 1,-1, 1, + 1, 1,-1, + 1, 1, 1, + }; + + const char edges[24] = + { + 0,1, + 1,3, + 3,2, + 2,0, + 4,5, + 5,7, + 7,6, + 6,4, + 0,4, + 1,5, + 2,6, + 3,7, + }; + + struct TriggerTraceSegmentCallback + { + bool intersection; + + PX_INLINE TriggerTraceSegmentCallback() : intersection(false) + { + } + + PX_INLINE bool underFaceHit( + const HeightFieldUtil&, const PxVec3&, + const PxVec3&, PxF32, PxF32, PxF32, PxU32) + { + return true; + } + + PX_INLINE bool faceHit(const HeightFieldUtil&, const PxVec3&, PxU32, PxReal, PxReal) + { + intersection = true; + return false; + } + bool onEvent(PxU32 , PxU32* ) + { + return true; + } + }; + + + class OverlapHeightfieldTraceSegmentHelper + { + PX_NOCOPY(OverlapHeightfieldTraceSegmentHelper) + public: + OverlapHeightfieldTraceSegmentHelper(const HeightFieldTraceUtil& hfUtil) : mHfUtil(hfUtil) + { + mHfUtil.computeLocalBounds(mLocalBounds); + } + + PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& aP1, TriggerTraceSegmentCallback* aCallback) const + { + mHfUtil.traceSegment(aP0, aP1 - aP0, 1.0f, aCallback, mLocalBounds, false, NULL); + } + + private: + const HeightFieldTraceUtil& mHfUtil; + PxBounds3 mLocalBounds; + }; + +} // namespace +} + +static bool intersectHeightFieldBox(const HeightFieldTraceUtil& hfUtil, const Box& boxInHfShape) +{ + const HeightField& hf = hfUtil.getHeightField(); + + // Get box vertices + PxVec3 boxVertices[8]; + for(PxU32 i=0; i<8; i++) + boxVertices[i] = PxVec3(boxInHfShape.extents.x*signs[3*i], boxInHfShape.extents.y*signs[3*i+1], boxInHfShape.extents.z*signs[3*i+2]); + + // Transform box vertices to HeightFieldShape space + PxVec3 boxVerticesInHfShape[8]; + for(PxU32 i=0; i<8; i++) + boxVerticesInHfShape[i] = boxInHfShape.transform(boxVertices[i]); + + // Test box vertices. + { + for(PxU32 i=0; i<8; i++) + { + const PxVec3& boxVertexInHfShape = boxVerticesInHfShape[i]; + if(hfUtil.isShapePointOnHeightField(boxVertexInHfShape.x, boxVertexInHfShape.z)) + { + const PxReal y = hfUtil.getHeightAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z); + const PxReal dy = boxVertexInHfShape.y - y; + if(hf.isDeltaHeightInsideExtent(dy)) + { + PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z); + if(faceIndex != 0xffffffff) + { + return true; + } + } + } + } + } + + // Test box edges. + { + OverlapHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil); + + for(PxU32 i=0; i<12; i++) + { + const PxVec3 v0 = boxVerticesInHfShape[PxU8(edges[2*i])]; + const PxVec3 v1 = boxVerticesInHfShape[PxU8(edges[2*i+1])]; + TriggerTraceSegmentCallback cb; + traceSegmentHelper.traceSegment(v0, v1, &cb); + if(cb.intersection) + return true; + } + } + + // Test HeightField vertices. + { + PsTransformV _hfShape2BoxShape; + const PxQuat bq(boxInHfShape.rot); + const QuatV q1 = QuatVLoadU(&bq.x); + const Vec3V p1 = V3LoadU(&boxInHfShape.center.x); + const PsTransformV _boxPose(p1, q1); + _hfShape2BoxShape = _boxPose.getInverse(); + + PxReal minx(PX_MAX_REAL); + PxReal minz(PX_MAX_REAL); + PxReal maxx(-PX_MAX_REAL); + PxReal maxz(-PX_MAX_REAL); + + for(PxU32 i=0; i<8; i++) + { + const PxVec3& boxVertexInHfShape = boxVerticesInHfShape[i]; + +/* if(boxVertexInHfShape.x < minx) minx = boxVertexInHfShape.x; + if(boxVertexInHfShape.z < minz) minz = boxVertexInHfShape.z; + if(boxVertexInHfShape.x > maxx) maxx = boxVertexInHfShape.x; + if(boxVertexInHfShape.z > maxz) maxz = boxVertexInHfShape.z;*/ + minx = physx::intrinsics::selectMin(boxVertexInHfShape.x, minx); + minz = physx::intrinsics::selectMin(boxVertexInHfShape.z, minz); + maxx = physx::intrinsics::selectMax(boxVertexInHfShape.x, maxx); + maxz = physx::intrinsics::selectMax(boxVertexInHfShape.z, maxz); + } + + const PxReal oneOverRowScale = hfUtil.getOneOverRowScale(); + const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale(); + const PxU32 minRow = hf.getMinRow(minx * oneOverRowScale); + const PxU32 maxRow = hf.getMaxRow(maxx * oneOverRowScale); + const PxU32 minColumn = hf.getMinColumn(minz * oneOverColumnScale); + const PxU32 maxColumn = hf.getMaxColumn(maxz * oneOverColumnScale); + + const Vec4V extentV = V4LoadXYZW(boxInHfShape.extents.x, boxInHfShape.extents.y, boxInHfShape.extents.z, PX_MAX_REAL); + const PxHeightFieldGeometry& geom = hfUtil.getHeightFieldGeometry(); + + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + PxU32 vertexIndex = row * hf.getNbColumnsFast() + column; + if(hfUtil.isQueryVertex(vertexIndex, row, column)) + { + // check if hf vertex is inside the box + const Vec4V hfVertex = V4LoadXYZW(geom.rowScale * row, geom.heightScale * hf.getHeight(vertexIndex), geom.columnScale * column, 0.0f); + const Vec4V hfVertexInBoxShape = Vec4V_From_Vec3V(_hfShape2BoxShape.transform(Vec3V_From_Vec4V(hfVertex))); + const Vec4V hfVertexInBoxShapeAbs = V4Abs(hfVertexInBoxShape); + + if(V4AllGrtr(extentV, hfVertexInBoxShapeAbs)) + { + return true; + } + } + } + } + } + return false; +} + +static Matrix34 multiplyInverseRTLeft(const Matrix34& left, const Matrix34& right) +{ +// t = left.M % (right.t - left.t); + PxVec3 t = left.rotateTranspose(right.p - left.p); + +// M.setMultiplyTransposeLeft(left.M, right.M); + const PxMat33& left33 = left.m; + const PxMat33& right33 = right.m; + PxMat33 multiplyTransposeLeft33 = (left33.getTranspose()) * right33; + + return Matrix34(multiplyTransposeLeft33, t); +} + +static bool intersectHeightFieldConvex( + const HeightFieldTraceUtil& hfUtil, const PxTransform& _hfAbsPose, const ConvexMesh& convexMesh, + const PxTransform& _convexAbsPose, const PxMeshScale& convexMeshScaling) +{ + const Matrix34 hfAbsPose34(_hfAbsPose); + const Matrix34 convexAbsPose34(_convexAbsPose); + const Matrix34 vertexToShapeSkew34(convexMeshScaling.toMat33()); + const Matrix34 temp34 = convexAbsPose34 * vertexToShapeSkew34; + const Matrix34 convexShape2HfShapeSkew34 = multiplyInverseRTLeft(hfAbsPose34, temp34); + + const ConvexHullData* hull = &convexMesh.getHull(); + + // Allocate space for transformed vertices. + PxVec3* convexVerticesInHfShape = reinterpret_cast(PxAlloca(hull->mNbHullVertices*sizeof(PxVec3))); + + // Transform vertices to height field shape + const PxVec3* hullVerts = hull->getHullVertices(); + for(PxU32 i=0; imNbHullVertices; i++) + convexVerticesInHfShape[i] = convexShape2HfShapeSkew34.transform(hullVerts[i]); + + // Compute bounds of convex in hf space + PxBounds3 convexBoundsInHfShape; + computeBoundsAroundVertices(convexBoundsInHfShape, hull->mNbHullVertices, convexVerticesInHfShape); + + // Compute the height field extreme over the bounds area. + const HeightField& hf = hfUtil.getHeightField(); + PxReal hfExtreme = -PX_MAX_REAL; + const PxReal oneOverRowScale = hfUtil.getOneOverRowScale(); + const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale(); + const PxReal rowScale = (1.0f / hfUtil.getOneOverRowScale()); + const PxReal columnScale = (1.0f / hfUtil.getOneOverColumnScale()); + const PxReal heightScale = (1.0f / hfUtil.getOneOverHeightScale()); + + // negative scale support + PxU32 minRow; + PxU32 maxRow; + if(oneOverRowScale > 0.0f) + { + minRow = hf.getMinRow(convexBoundsInHfShape.minimum.x * oneOverRowScale); + maxRow = hf.getMaxRow(convexBoundsInHfShape.maximum.x * oneOverRowScale); + } + else + { + minRow = hf.getMinRow(convexBoundsInHfShape.maximum.x * oneOverRowScale); + maxRow = hf.getMaxRow(convexBoundsInHfShape.minimum.x * oneOverRowScale); + } + + PxU32 minColumn; + PxU32 maxColumn; + if(oneOverColumnScale > 0.0f) + { + minColumn = hf.getMinColumn(convexBoundsInHfShape.minimum.z * oneOverColumnScale); + maxColumn = hf.getMaxColumn(convexBoundsInHfShape.maximum.z * oneOverColumnScale); + } + else + { + minColumn = hf.getMinColumn(convexBoundsInHfShape.maximum.z * oneOverColumnScale); + maxColumn = hf.getMaxColumn(convexBoundsInHfShape.minimum.z * oneOverColumnScale); + } + + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + const PxReal h = hf.getHeight(row * hf.getNbColumnsFast() + column); + hfExtreme = PxMax(hfExtreme, h); + } + } + hfExtreme *= heightScale; + + + // Return if convex is on the wrong side of the extreme. + if(convexBoundsInHfShape.minimum.y > hfExtreme) + return false; + + // Test convex vertices + { + for(PxU32 i=0; imNbHullVertices; i++) + { + const PxVec3& convexVertexInHfShape = convexVerticesInHfShape[i]; + bool insideExtreme = convexVertexInHfShape.y < hfExtreme; + if(insideExtreme && hfUtil.isShapePointOnHeightField(convexVertexInHfShape.x, convexVertexInHfShape.z)) + { + const PxReal y = hfUtil.getHeightAtShapePoint(convexVertexInHfShape.x, convexVertexInHfShape.z); + const PxReal dy = convexVertexInHfShape.y - y; + if(hf.isDeltaHeightInsideExtent(dy)) + { + const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(convexVertexInHfShape.x, convexVertexInHfShape.z); + if(faceIndex != 0xffffffff) + return true; + } + } + } + } + + // Test convex edges. + { + EdgeCache edgeCache; + PxU32 numPolygons = hull->mNbPolygons; + const HullPolygonData* polygons = hull->mPolygons; + const PxU8* const vertexData = hull->getVertexData8(); + + OverlapHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil); + + while(numPolygons--) + { + const HullPolygonData& polygon = *polygons++; + + const PxU8* verts = vertexData + polygon.mVRef8; + + PxU32 numEdges = polygon.mNbVerts; + + PxU32 a = numEdges - 1; + PxU32 b = 0; + while(numEdges--) + { + PxU8 vi0 = verts[a]; + PxU8 vi1 = verts[b]; + + if(vi1 < vi0) + { + PxU8 tmp = vi0; + vi0 = vi1; + vi1 = tmp; + } + + if(edgeCache.isInCache(vi0, vi1)) //avoid processing edges 2x if possible (this will typically have cache misses about 5% of the time leading to 5% redundant work. + continue; + + const PxVec3& sv0 = convexVerticesInHfShape[vi0]; + const PxVec3& sv1 = convexVerticesInHfShape[vi1]; + a = b; + b++; + + + if((sv0.y > hfExtreme) && (sv1.y > hfExtreme)) + continue; + + const PxVec3 v0 = sv0; + const PxVec3 v1 = sv1; + TriggerTraceSegmentCallback cb; + traceSegmentHelper.traceSegment(v0, v1, &cb); + if(cb.intersection) + return true; + } + } + } + + // Test HeightField vertices + { + const Matrix34 tmp34 = multiplyInverseRTLeft(convexAbsPose34, hfAbsPose34); + const Matrix34 hfShape2ConvexShapeSkew34 = vertexToShapeSkew34 * tmp34; + + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + const PxU32 hfVertexIndex = row * hf.getNbColumnsFast() + column; + if(hfUtil.isQueryVertex(hfVertexIndex, row, column)) + { + // Check if hf vertex is inside the convex + const PxVec3 hfVertex(rowScale * row, heightScale * hf.getHeight(hfVertexIndex), columnScale * column); + const PxVec3 hfVertexInConvexShape = hfShape2ConvexShapeSkew34.transform(hfVertex); + + bool inside = true; + for(PxU32 poly = 0; poly < hull->mNbPolygons; poly++) + { + PxReal d = hull->mPolygons[poly].mPlane.distance(hfVertexInConvexShape); + if(d >= 0) + { + inside = false; + break; + } + } + if(inside) + return true; + } + } + } + } + return false; +} + +bool Gu::checkOverlapAABB_heightFieldGeom(const PxGeometry& geom, const PxTransform& pose, const PxBounds3& box) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + const PxHeightFieldGeometry& hfGeom = static_cast(geom); + + const Matrix34 invAbsPose(pose.getInverse()); + + const Box boxInHfShape( + invAbsPose.transform(box.getCenter()), + box.getExtents(), + invAbsPose.m); + + HeightFieldTraceUtil hfUtil(hfGeom); + return intersectHeightFieldBox(hfUtil, boxInHfShape); +} + +bool GeomOverlapCallback_SphereHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast(geom1); + + const Sphere sphereInHf(pose1.transformInv(pose0.p), sphereGeom.radius); + + HeightFieldUtil hfUtil(hfGeom); + return intersectHeightFieldSphere(hfUtil, sphereInHf); +} + +bool GeomOverlapCallback_CapsuleHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast(geom1); + + const PxTransform capsuleShapeToHfShape = pose1.transformInv(pose0); + + const HeightFieldUtil hfUtil(hfGeom); + return intersectHeightFieldCapsule(hfUtil, capsuleGeom, capsuleShapeToHfShape); +} + +bool GeomOverlapCallback_BoxHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast(geom1); + + const PxTransform boxShape2HfShape = pose1.transformInv(pose0); + + Box box; + buildFrom(box, boxShape2HfShape.p, boxGeom.halfExtents, boxShape2HfShape.q); + + HeightFieldTraceUtil hfUtil(hfGeom); + return intersectHeightFieldBox(hfUtil, box); +} + +/////////////////////////////////////////////////////////////////////////////// +bool GeomOverlapCallback_ConvexHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + HeightFieldTraceUtil hfUtil(hfGeom); + return intersectHeightFieldConvex(hfUtil, pose1, *cm, pose0, convexGeom.scale); +} diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp new file mode 100644 index 000000000..32e400b0a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp @@ -0,0 +1,606 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTests.h" +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "GuVecCapsule.h" +#include "GuSweepMTD.h" +#include "GuSweepTriangleUtils.h" +#include "GuVecBox.h" +#include "CmScaling.h" +#include "GuSweepCapsuleTriangle.h" +#include "GuInternal.h" +#include "GuGJKRaycast.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +#include "GuSweepConvexTri.h" + +#define AbortTraversal false +#define ContinueTraversal true + +/////////////////////////////////////////////////////////////////////////////// + +class HeightFieldTraceSegmentSweepHelper +{ + PX_NOCOPY(HeightFieldTraceSegmentSweepHelper) +public: + HeightFieldTraceSegmentSweepHelper(const HeightFieldTraceUtil& hfUtil, const PxVec3& aabbExtentHfLocalSpace) + : mHfUtil(hfUtil), mOverlapObjectExtent(aabbExtentHfLocalSpace) + { + mHfUtil.computeLocalBounds(mLocalBounds); + // extend the bounds + mLocalBounds.minimum = mLocalBounds.minimum - aabbExtentHfLocalSpace; + mLocalBounds.maximum = mLocalBounds.maximum + aabbExtentHfLocalSpace; + } + + template + PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDirNorm, const float rayLength, T* aCallback) const + { + mHfUtil.traceSegment(aP0, rayDirNorm, rayLength, aCallback, mLocalBounds, false, &mOverlapObjectExtent); + } + +private: + const HeightFieldTraceUtil& mHfUtil; + const PxVec3& mOverlapObjectExtent; + PxBounds3 mLocalBounds; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class HeightFieldTraceSegmentReport : public EntityReport +{ + PX_NOCOPY(HeightFieldTraceSegmentReport) +public: + + HeightFieldTraceSegmentReport(const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags) : + mHfUtil (hfUtil), + mHitFlags (hitFlags), + mStatus (false), + mInitialOverlap (false), + mIsDoubleSided ((hfUtil.getHeightFieldGeometry().heightFieldFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES)), + mIsAnyHit (hitFlags & PxHitFlag::eMESH_ANY) + { + } + + bool underFaceHit(const Gu::HeightFieldUtil&, const PxVec3&, const PxVec3&, PxF32, PxF32, PxF32, PxU32) + { + return true; + } + + bool faceHit(const Gu::HeightFieldUtil&, const PxVec3&, PxU32, PxReal, PxReal) + { + return true; + } + + protected: + const HeightFieldUtil& mHfUtil; + const PxHitFlags mHitFlags; + bool mStatus; + bool mInitialOverlap; + const bool mIsDoubleSided; + const bool mIsAnyHit; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class CapsuleTraceSegmentReport : public HeightFieldTraceSegmentReport +{ + PX_NOCOPY(CapsuleTraceSegmentReport) +public: + CapsuleTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags, + const Capsule& inflatedCapsule, + const PxVec3& unitDir, PxSweepHit& sweepHit, const PxTransform& pose, PxReal distance) : + HeightFieldTraceSegmentReport (hfUtil, hitFlags), + mInflatedCapsule (inflatedCapsule), + mUnitDir (unitDir), + mSweepHit (sweepHit), + mPose (pose), + mDistance (distance) + { + mSweepHit.faceIndex = 0xFFFFffff; + } + + virtual PxAgain onEvent(PxU32 nb, PxU32* indices) + { + PX_ALIGN_PREFIX(16) PxU8 tribuf[HF_SWEEP_REPORT_BUFFER_SIZE*sizeof(PxTriangle)] PX_ALIGN_SUFFIX(16); + PxTriangle* tmpT = reinterpret_cast(tribuf); + PX_ASSERT(nb <= HF_SWEEP_REPORT_BUFFER_SIZE); + for(PxU32 i=0; i(geom); + + const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); + + // Compute swept box + Box capsuleBox; + computeBoxAroundCapsule(inflatedCapsule, capsuleBox); + + const PxVec3 capsuleAABBExtents = capsuleBox.computeAABBExtent(); + + const HeightFieldTraceUtil hfUtil(hfGeom); + CapsuleTraceSegmentReport myReport(hfUtil, hitFlags, inflatedCapsule, unitDir, sweepHit, pose, distance); + + sweepHit.distance = PX_MAX_F32; + + // need hf local space stuff + const PxTransform inversePose = pose.getInverse(); + const PxVec3 centerLocalSpace = inversePose.transform(capsuleBox.center); + const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); + const PxVec3 capsuleAABBBExtentHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), capsuleAABBExtents).getExtents(); + + HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, capsuleAABBBExtentHfLocalSpace); + traceSegmentHelper.traceSegment(centerLocalSpace, sweepDirLocalSpace, distance, &myReport); + + return myReport.finalizeHit(sweepHit, hfGeom, pose, lss, inflatedCapsule, unitDir); +} + +/////////////////////////////////////////////////////////////////////////////// + +class ConvexTraceSegmentReport : public HeightFieldTraceSegmentReport +{ + PX_NOCOPY(ConvexTraceSegmentReport) +public: + ConvexTraceSegmentReport( const HeightFieldUtil& hfUtil, const ConvexHullData& hull, const PxMeshScale& convexScale, + const PxTransform& convexPose, const PxTransform& heightFieldPose, + const PxVec3& unitDir, PxReal distance, PxHitFlags hitFlags, PxReal inflation) : + HeightFieldTraceSegmentReport (hfUtil, hitFlags), + mUnitDir (unitDir), + mInflation (inflation) + { + using namespace Ps::aos; + mSweepHit.faceIndex = 0xFFFFffff; + mSweepHit.distance = distance; + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const QuatV q0 = QuatVLoadU(&heightFieldPose.q.x); + const Vec3V p0 = V3LoadU(&heightFieldPose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV meshTransf(p0, q0); + const PsTransformV convexTransf(p1, q1); + + mMeshToConvex = convexTransf.transformInv(meshTransf); + mConvexPoseV = convexTransf; + mConvexSpaceDir = convexTransf.rotateInv(V3Neg(V3Scale(worldDir, dist))); + mDistance = dist; + + const Vec3V vScale = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexScale.rotation.x); + + mMeshSpaceUnitDir = heightFieldPose.rotateInv(unitDir); + mConvexHull.initialize(&hull, V3Zero(), vScale, vQuat, convexScale.isIdentity()); + } + + virtual PxAgain onEvent(PxU32 nbEntities, PxU32* entities) + { + const PxTransform idt(PxIdentity); + for(PxU32 i=0; i(geom); + + const Matrix34 convexTM(convexPose); + const Matrix34 meshTM(pose); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + + FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + PX_ASSERT(!convexMesh->getLocalBoundsFast().isEmpty()); + const PxBounds3 hullAABBLocalSpace = convexMesh->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew()); + + const HeightFieldTraceUtil hfUtil(hfGeom); + ConvexTraceSegmentReport entityReport( + hfUtil, convexMesh->getHull(), convexGeom.scale, convexPose, pose, -unitDir, distance, hitFlags, inflation); + + // need hf local space stuff + const PxBounds3 hullAABB = PxBounds3::transformFast(convexPose, hullAABBLocalSpace); + const PxVec3 aabbExtents = hullAABB.getExtents() + PxVec3(inflation); + const PxTransform inversePose = pose.getInverse(); + const PxVec3 centerLocalSpace = inversePose.transform(hullAABB.getCenter()); + const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); + const PxVec3 convexAABBExtentHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), aabbExtents).getExtents(); + + HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, convexAABBExtentHfLocalSpace); + traceSegmentHelper.traceSegment(centerLocalSpace, sweepDirLocalSpace, distance, &entityReport); + + return entityReport.finalizeHit(sweepHit, hfGeom, pose, convexGeom, convexPose, unitDir, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +class BoxTraceSegmentReport : public HeightFieldTraceSegmentReport +{ + PX_NOCOPY(BoxTraceSegmentReport) +public: + BoxTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags, + const PsTransformV& worldToBoxV, const PxTransform& pose, const BoxV& box, const PxVec3& localMotion, + PxSweepHit& sweepHit, PxReal inflation) : + HeightFieldTraceSegmentReport (hfUtil, hitFlags), + mWorldToBoxV (worldToBoxV), + mPose (pose), + mBox (box), + mLocalMotion (localMotion), + mSweepHit (sweepHit), + mInflation (inflation) + { + mMinToi = FMax(); + mSweepHit.faceIndex = 0xFFFFffff; + } + + virtual PxAgain onEvent(PxU32 nb, PxU32* indices) + { + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V dir = V3LoadU(mLocalMotion); + //FloatV minToi = FMax(); + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + + for(PxU32 i=0; i convexA(triangle); + const LocalConvex convexB(mBox); + const Vec3V initialSearchDir = V3Sub(triangle.getCenter(), mBox.getCenter()); + + if(gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, mInflation, false)) + { + mStatus = true; + if(FAllGrtr(toi, zero)) + { + if(FAllGrtr(mMinToi, toi)) + { + mMinToi = toi; + FStore(toi, &mSweepHit.distance); + V3StoreU(normal, mSweepHit.normal); + V3StoreU(closestA, mSweepHit.position); + mSweepHit.faceIndex = triangleIndex; + + if(mIsAnyHit) + return AbortTraversal; + } + } + else + { + mSweepHit.distance = 0.0f; + mSweepHit.faceIndex = triangleIndex; + mInitialOverlap = true; + return AbortTraversal; + } + } + } + return ContinueTraversal; + } + + bool finalizeHit(PxSweepHit& sweepHit, + const PxHeightFieldGeometry& hfGeom, const PxTransform& pose, + const PxTransform& boxPose_, const Box& box, + const PxVec3& unitDir, PxReal distance, PxReal inflation) + { + if(!mStatus) + return false; + + if(mInitialOverlap) + { + // PT: TODO: consider using 'setInitialOverlapResults' here + + sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + + if(mHitFlags & PxHitFlag::eMTD) + { + const bool hasContacts = computeBox_HeightFieldMTD(hfGeom, pose, box, boxPose_, inflation, mIsDoubleSided, GuHfQueryFlags::eWORLD_SPACE, sweepHit); + + //ML: the center of mass is below the surface, we won't have MTD contact generate + if(!hasContacts) + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + PxVec3 n = sweepHit.normal.getNormalized(); + if((n.dot(mLocalMotion))>0.0f) + n = -n; + + sweepHit.distance *= distance; // stored as toi [0,1] during computation -> scale + sweepHit.normal = boxPose_.rotate(n); + sweepHit.position = boxPose_.transform(sweepHit.position); + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + } + return true; + } + + private: + const PsTransformV& mWorldToBoxV; + const PxTransform& mPose; + const BoxV& mBox; + FloatV mMinToi; + const PxVec3 mLocalMotion; + PxSweepHit& mSweepHit; + const PxReal mInflation; +}; + +#if PX_VC + #pragma warning(pop) +#endif + +bool sweepBox_HeightFieldGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(boxGeom_); + PX_UNUSED(hitFlags); + + const PxHeightFieldGeometry& hfGeom = static_cast(geom); + + const PxVec3 boxAABBExtent = box.computeAABBExtent() + PxVec3(inflation); + + // Move to AABB space + PX_ALIGN_PREFIX(16) PxTransform WorldToBox PX_ALIGN_SUFFIX(16); + WorldToBox = boxPose_.getInverse(); + + const QuatV q1 = QuatVLoadA(&WorldToBox.q.x); + const Vec3V p1 = V3LoadA(&WorldToBox.p.x); + const PsTransformV WorldToBoxV(p1, q1); + + const PxVec3 motion = unitDir * distance; + const PxVec3 localMotion = WorldToBox.rotate(motion); + + const BoxV boxV(V3Zero(), V3LoadU(box.extents)); + + sweepHit.distance = PX_MAX_F32; + + const HeightFieldTraceUtil hfUtil(hfGeom); + BoxTraceSegmentReport myReport(hfUtil, hitFlags, WorldToBoxV, pose, boxV, localMotion, sweepHit, inflation); + + // need hf local space stuff + const PxTransform inversePose = pose.getInverse(); + const PxVec3 centerLocalSpace = inversePose.transform(box.center); + const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); + const PxVec3 boxAABBExtentInHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), boxAABBExtent).getExtents(); + + HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, boxAABBExtentInHfLocalSpace); + traceSegmentHelper.traceSegment(centerLocalSpace, sweepDirLocalSpace, distance, &myReport); + + return myReport.finalizeHit(sweepHit, hfGeom, pose, boxPose_, box, unitDir, distance, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp new file mode 100644 index 000000000..120a81c42 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp @@ -0,0 +1,139 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionBoxBox.h" + +using namespace physx; + +bool Gu::intersectOBBOBB(const PxVec3& e0, const PxVec3& c0, const PxMat33& r0, const PxVec3& e1, const PxVec3& c1, const PxMat33& r1, bool full_test) +{ + // Translation, in parent frame + const PxVec3 v = c1 - c0; + // Translation, in A's frame + const PxVec3 T(v.dot(r0[0]), v.dot(r0[1]), v.dot(r0[2])); + + // B's basis with respect to A's local frame + PxReal R[3][3]; + PxReal FR[3][3]; + PxReal ra, rb, t; + + // Calculate rotation matrix + for(PxU32 i=0;i<3;i++) + { + for(PxU32 k=0;k<3;k++) + { + R[i][k] = r0[i].dot(r1[k]); + FR[i][k] = 1e-6f + PxAbs(R[i][k]); // Precompute fabs matrix + } + } + + // A's basis vectors + for(PxU32 i=0;i<3;i++) + { + ra = e0[i]; + + rb = e1[0]*FR[i][0] + e1[1]*FR[i][1] + e1[2]*FR[i][2]; + + t = PxAbs(T[i]); + + if(t > ra + rb) return false; + } + + // B's basis vectors + for(PxU32 k=0;k<3;k++) + { + ra = e0[0]*FR[0][k] + e0[1]*FR[1][k] + e0[2]*FR[2][k]; + + rb = e1[k]; + + t = PxAbs(T[0]*R[0][k] + T[1]*R[1][k] + T[2]*R[2][k]); + + if( t > ra + rb ) return false; + } + + if(full_test) + { + //9 cross products + + //L = A0 x B0 + ra = e0[1]*FR[2][0] + e0[2]*FR[1][0]; + rb = e1[1]*FR[0][2] + e1[2]*FR[0][1]; + t = PxAbs(T[2]*R[1][0] - T[1]*R[2][0]); + if(t > ra + rb) return false; + + //L = A0 x B1 + ra = e0[1]*FR[2][1] + e0[2]*FR[1][1]; + rb = e1[0]*FR[0][2] + e1[2]*FR[0][0]; + t = PxAbs(T[2]*R[1][1] - T[1]*R[2][1]); + if(t > ra + rb) return false; + + //L = A0 x B2 + ra = e0[1]*FR[2][2] + e0[2]*FR[1][2]; + rb = e1[0]*FR[0][1] + e1[1]*FR[0][0]; + t = PxAbs(T[2]*R[1][2] - T[1]*R[2][2]); + if(t > ra + rb) return false; + + //L = A1 x B0 + ra = e0[0]*FR[2][0] + e0[2]*FR[0][0]; + rb = e1[1]*FR[1][2] + e1[2]*FR[1][1]; + t = PxAbs(T[0]*R[2][0] - T[2]*R[0][0]); + if(t > ra + rb) return false; + + //L = A1 x B1 + ra = e0[0]*FR[2][1] + e0[2]*FR[0][1]; + rb = e1[0]*FR[1][2] + e1[2]*FR[1][0]; + t = PxAbs(T[0]*R[2][1] - T[2]*R[0][1]); + if(t > ra + rb) return false; + + //L = A1 x B2 + ra = e0[0]*FR[2][2] + e0[2]*FR[0][2]; + rb = e1[0]*FR[1][1] + e1[1]*FR[1][0]; + t = PxAbs(T[0]*R[2][2] - T[2]*R[0][2]); + if(t > ra + rb) return false; + + //L = A2 x B0 + ra = e0[0]*FR[1][0] + e0[1]*FR[0][0]; + rb = e1[1]*FR[2][2] + e1[2]*FR[2][1]; + t = PxAbs(T[1]*R[0][0] - T[0]*R[1][0]); + if(t > ra + rb) return false; + + //L = A2 x B1 + ra = e0[0]*FR[1][1] + e0[1]*FR[0][1]; + rb = e1[0] *FR[2][2] + e1[2]*FR[2][0]; + t = PxAbs(T[1]*R[0][1] - T[0]*R[1][1]); + if(t > ra + rb) return false; + + //L = A2 x B2 + ra = e0[0]*FR[1][2] + e0[1]*FR[0][2]; + rb = e1[0]*FR[2][1] + e1[1]*FR[2][0]; + t = PxAbs(T[1]*R[0][2] - T[0]*R[1][2]); + if(t > ra + rb) return false; + } + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp new file mode 100644 index 000000000..2cbb0eb5c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp @@ -0,0 +1,61 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionCapsuleTriangle.h" +#include "GuDistancePointSegment.h" + +using namespace physx; +using namespace Gu; + +bool Gu::intersectCapsuleTriangle(const PxVec3& N, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const Gu::Capsule& capsule, const CapsuleTriangleOverlapData& params) +{ + PX_ASSERT(capsule.p0!=capsule.p1); + + { + const PxReal d2 = distancePointSegmentSquaredInternal(capsule.p0, params.mCapsuleDir, p0); + if(d2<=capsule.radius*capsule.radius) + return true; + } + +// const PxVec3 N = (p0 - p1).cross(p0 - p2); + + if(!testAxis(p0, p1, p2, capsule, N)) + return false; + + if(!testAxis(p0, p1, p2, capsule, computeEdgeAxis(p0, p1 - p0, capsule.p0, params.mCapsuleDir, params.mBDotB, params.mOneOverBDotB))) + return false; + + if(!testAxis(p0, p1, p2, capsule, computeEdgeAxis(p1, p2 - p1, capsule.p0, params.mCapsuleDir, params.mBDotB, params.mOneOverBDotB))) + return false; + + if(!testAxis(p0, p1, p2, capsule, computeEdgeAxis(p2, p0 - p2, capsule.p0, params.mCapsuleDir, params.mBDotB, params.mOneOverBDotB))) + return false; + + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h new file mode 100644 index 000000000..72c67d6ba --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h @@ -0,0 +1,137 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_CAPSULE_TRIANGLE_H +#define GU_INTERSECTION_CAPSULE_TRIANGLE_H + +#include "CmPhysXCommon.h" +#include "GuCapsule.h" +#include "PsUtilities.h" + +namespace physx +{ +namespace Gu +{ + // PT: precomputed data for capsule-triangle test. Useful when testing the same capsule vs several triangles. + struct CapsuleTriangleOverlapData + { + PxVec3 mCapsuleDir; + float mBDotB; + float mOneOverBDotB; + + void init(const Capsule& capsule) + { + const PxVec3 dir = capsule.p1 - capsule.p0; + const float BDotB = dir.dot(dir); + mCapsuleDir = dir; + mBDotB = BDotB; + mOneOverBDotB = BDotB!=0.0f ? 1.0f/BDotB : 0.0f; + } + }; + + // PT: tests if projections of capsule & triangle overlap on given axis + PX_FORCE_INLINE PxU32 testAxis(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const Capsule& capsule, const PxVec3& axis) + { + // Project capsule + float min0 = capsule.p0.dot(axis); + float max0 = capsule.p1.dot(axis); + if(min0>max0) + Ps::swap(min0, max0); + const float MR = axis.magnitude()*capsule.radius; + min0 -= MR; + max0 += MR; + + // Project triangle + float min1, max1; + { + min1 = max1 = p0.dot(axis); + float dp = p1.dot(axis); + if(dpmax1) max1 = dp; + dp = p2.dot(axis); + if(dpmax1) max1 = dp; + } + + // Test projections + if(max01.0f) + { + u = 1.0f; + t = (ADotB + ADotT) / ADotA; + t = PxClamp(t, 0.0f, 1.0f); + } + return T + b*u - a*t; + } + + /** + * Checks if a capsule intersects a triangle. + * + * \param normal [in] triangle normal (orientation does not matter) + * \param p0 [in] triangle's first point + * \param p1 [in] triangle's second point + * \param p2 [in] triangle's third point + * \param capsule [in] capsule + * \param params [in] precomputed capsule params + * \return true if capsule overlaps triangle + */ + bool intersectCapsuleTriangle(const PxVec3& normal, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const Gu::Capsule& capsule, const CapsuleTriangleOverlapData& params); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp new file mode 100644 index 000000000..4a5be9d0e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp @@ -0,0 +1,84 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionEdgeEdge.h" +#include "PsMathUtils.h" +#include "CmPhysXCommon.h" + +using namespace physx; + +bool Gu::intersectEdgeEdge(const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip) +{ + const PxVec3 v1 = p2 - p1; + + // Build plane P based on edge (p1, p2) and direction (dir) + PxPlane plane; + plane.n = v1.cross(dir); + plane.d = -(plane.n.dot(p1)); + + // if colliding edge (p3,p4) does not cross plane return no collision + // same as if p3 and p4 on same side of plane return 0 + // + // Derivation: + // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // if d3 and d4 have the same sign, they're on the same side of the plane => no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + PxVec3 v2 = p4 - p3; + + temp = plane.n.dot(v2); + if(temp==0.0f) return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // find largest 2D plane projection + PxU32 i,j; + Ps::closestAxis(plane.n, i, j); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i]))/(v1[i]*dir[j]-v1[j]*dir[i]); + if(dist<0.0f) return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<1e-3f) return true; // collision found + + return false; // no collision +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h new file mode 100644 index 000000000..d9a4cd7c6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h @@ -0,0 +1,52 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_EDGE_EDGE_H +#define GU_INTERSECTION_EDGE_EDGE_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + + // collide edge (p1,p2) moving in direction (dir) colliding + // width edge (p3,p4). Return true on a collision with + // collision distance (dist) and intersection point (ip) + // note: dist and ip are invalid if function returns false. + // note: ip is on (p1,p2), not (p1+dist*dir,p2+dist*dir) + PX_PHYSX_COMMON_API bool intersectEdgeEdge(const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h new file mode 100644 index 000000000..2e120457e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h @@ -0,0 +1,38 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_H +#define GU_INTERSECTION_RAY_H + +// PT: small distance between a ray origin and a potentially hit surface. Should be small enough to +// limit accuracy issues coming from large distance values, but not too close to the surface to make +// sure we don't start inside the shape. +#define GU_RAY_SURFACE_OFFSET 10.0f + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp new file mode 100644 index 000000000..3b6c3fe95 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp @@ -0,0 +1,449 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec3.h" +#include "foundation/PxIntrinsics.h" +#include "PsFPU.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionRayBoxSIMD.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Computes a ray-AABB intersection. +* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 +* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) +* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) +* +* Hence this version is faster as well as more robust than the original one. +* +* Should work provided: +* 1) the integer representation of 0.0f is 0x00000000 +* 2) the sign bit of the float is the most significant one +* +* Report bugs: p.terdiman@codercorner.com +* +* \param aabb [in] the axis-aligned bounding box +* \param origin [in] ray origin +* \param dir [in] ray direction +* \param coord [out] impact coordinates +* \return true if ray intersects AABB +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define RAYAABB_EPSILON 0.00001f +bool Gu::rayAABBIntersect(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& origin, const PxVec3& _dir, PxVec3& coord) +{ + Ps::IntBool Inside = Ps::IntTrue; + PxVec3 MaxT(-1.0f, -1.0f, -1.0f); + const PxReal* dir = &_dir.x; + const PxU32* idir = reinterpret_cast(dir); + // Find candidate planes. + for(PxU32 i=0;i<3;i++) + { + if(origin[i] < minimum[i]) + { + coord[i] = minimum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (minimum[i] - origin[i]) / dir[i]; + } + else if(origin[i] > maximum[i]) + { + coord[i] = maximum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (maximum[i] - origin[i]) / dir[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord = origin; + return true; + } + + // Get largest of the maxT's for final choice of intersection + PxU32 WhichPlane = 0; + if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; + if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + const PxU32* tmp = reinterpret_cast(&MaxT[WhichPlane]); + if((*tmp)&PX_SIGN_BITMASK) +// if(PX_IR(MaxT[WhichPlane])&PX_SIGN_BITMASK) + return false; + + for(PxU32 i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord[i] = origin[i] + MaxT[WhichPlane] * dir[i]; +#ifdef RAYAABB_EPSILON + if(coord[i] < minimum[i] - RAYAABB_EPSILON || coord[i] > maximum[i] + RAYAABB_EPSILON) +#else + if(coord[i] < minimum[i] || coord[i] > maximum[i]) +#endif + return false; + } + } + return true; // ray hits box +} + + + +/** +* Computes a ray-AABB intersection. +* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 +* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) +* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) +* Return of intersected face code and parameter by Adam! Also modified behavior for ray starts inside AABB. 2004 :-p +* +* Hence this version is faster as well as more robust than the original one. +* +* Should work provided: +* 1) the integer representation of 0.0f is 0x00000000 +* 2) the sign bit of the float is the most significant one +* +* Report bugs: p.terdiman@codercorner.com +* +* \param minimum [in] the smaller corner of the bounding box +* \param maximum [in] the larger corner of the bounding box +* \param origin [in] ray origin +* \param _dir [in] ray direction +* \param coord [out] impact coordinates +* \param t [out] t such that coord = origin + dir * t +* \return false if ray does not intersect AABB, or ray origin is inside AABB. Else: + 1 + coordinate index of box axis that was hit + + Note: sign bit that determines if the minimum (0) or maximum (1) of the axis was hit is equal to sign(coord[returnVal-1]). +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PxU32 Gu::rayAABBIntersect2(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& origin, const PxVec3& _dir, PxVec3& coord, PxReal & t) +{ + Ps::IntBool Inside = Ps::IntTrue; + PxVec3 MaxT(-1.0f, -1.0f, -1.0f); + const PxReal* dir = &_dir.x; + const PxU32* idir = reinterpret_cast(dir); + // Find candidate planes. + for(PxU32 i=0;i<3;i++) + { + if(origin[i] < minimum[i]) + { + coord[i] = minimum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (minimum[i] - origin[i]) / dir[i]; + } + else if(origin[i] > maximum[i]) + { + coord[i] = maximum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (maximum[i] - origin[i]) / dir[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord = origin; + t = 0; + return 1; + } + + // Get largest of the maxT's for final choice of intersection + PxU32 WhichPlane = 0; + if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; + if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + const PxU32* tmp = reinterpret_cast(&MaxT[WhichPlane]); + if((*tmp)&PX_SIGN_BITMASK) +// if(PX_IR(MaxT[WhichPlane])&PX_SIGN_BITMASK) + return 0; + + for(PxU32 i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord[i] = origin[i] + MaxT[WhichPlane] * dir[i]; +#ifdef RAYAABB_EPSILON + if(coord[i] < minimum[i] - RAYAABB_EPSILON || coord[i] > maximum[i] + RAYAABB_EPSILON) return 0; +#else + if(coord[i] < minimum[i] || coord[i] > maximum[i]) return 0; +#endif + } + } + t = MaxT[WhichPlane]; + return 1 + WhichPlane; // ray hits box +} + +// Collide ray defined by ray origin (ro) and ray direction (rd) +// with the bounding box. Returns -1 on no collision and the face index +// for first intersection if a collision is found together with +// the distance to the collision points (tnear and tfar) + +// ptchernev: +// Should we use an enum, or should we keep the anonymous ints? +// Should we increment the return code by one (return 0 for non intersection)? + +int Gu::intersectRayAABB(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& ro, const PxVec3& rd, float& tnear, float& tfar) +{ + // Refactor + int ret=-1; + + tnear = -PX_MAX_F32; + tfar = PX_MAX_F32; + // PT: why did we change the initial epsilon value? + #define LOCAL_EPSILON PX_EPS_F32 + //#define LOCAL_EPSILON 0.0001f + + for(unsigned int a=0;a<3;a++) + { + if(rd[a]>-LOCAL_EPSILON && rd[a]maximum[a]) + return -1; + } + else + { + const PxReal OneOverDir = 1.0f / rd[a]; + PxReal t1 = (minimum[a]-ro[a]) * OneOverDir; + PxReal t2 = (maximum[a]-ro[a]) * OneOverDir; + + unsigned int b = a; + if(t1>t2) + { + PxReal t=t1; + t1=t2; + t2=t; + b += 3; + } + + if(t1>tnear) + { + tnear = t1; + ret = int(b); + } + if(t2tfar || tfartfar || tfar-LOCAL_EPSILON && rd.xmaximum.x) + return -1; + if(physx::intrinsics::abs(rd.y)-LOCAL_EPSILON && rd.ymaximum.y) + return -1; + if(physx::intrinsics::abs(rd.z)-LOCAL_EPSILON && rd.zmaximum.z) + return -1; + + PxReal t1x = (minimum.x - ro.x) * oneOverDir.x; + PxReal t2x = (maximum.x - ro.x) * oneOverDir.x; + PxReal t1y = (minimum.y - ro.y) * oneOverDir.y; + PxReal t2y = (maximum.y - ro.y) * oneOverDir.y; + PxReal t1z = (minimum.z - ro.z) * oneOverDir.z; + PxReal t2z = (maximum.z - ro.z) * oneOverDir.z; + + int bx; + int by; + int bz; + + if(t1x>t2x) + { + PxReal t=t1x; t1x=t2x; t2x=t; + bx = 3; + } + else + { + bx = 0; + } + + if(t1y>t2y) + { + PxReal t=t1y; t1y=t2y; t2y=t; + by = 4; + } + else + { + by = 1; + } + + if(t1z>t2z) + { + PxReal t=t1z; t1z=t2z; t2z=t; + bz = 5; + } + else + { + bz = 2; + } + + int ret; +// if(t1x>tnear) // PT: no need to test for the first value + { + tnear = t1x; + ret = bx; + } +// tfar = PxMin(tfar, t2x); + tfar = t2x; // PT: no need to test for the first value + + if(t1y>tnear) + { + tnear = t1y; + ret = by; + } + tfar = PxMin(tfar, t2y); + + if(t1z>tnear) + { + tnear = t1z; + ret = bz; + } + tfar = PxMin(tfar, t2z); + + if(tnear>tfar || tfar= GU_MIN_AABB_EXTENT*0.5f); + PX_ASSERT(maximum.y-minimum.y >= GU_MIN_AABB_EXTENT*0.5f); + PX_ASSERT(maximum.z-minimum.z >= GU_MIN_AABB_EXTENT*0.5f); + // not using vector math due to vector to integer pipeline penalties. TODO: verify that it's indeed faster + namespace i = physx::intrinsics; + + // P+tD=a; t=(a-P)/D + // t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x) + const PxF32 dEpsilon = 1e-9f; + // using recipFast fails height field unit tests case where a ray cast from y=10000 to 0 gets clipped to 0.27 in y + PxF32 invDx = i::recip(i::selectMax(i::abs(rd.x), dEpsilon) * i::sign(rd.x)); +#ifdef RAYAABB_EPSILON + PxF32 tx0 = (minimum.x - RAYAABB_EPSILON - ro.x) * invDx; + PxF32 tx1 = (maximum.x + RAYAABB_EPSILON - ro.x) * invDx; +#else + PxF32 tx0 = (minimum.x - ro.x) * invDx; + PxF32 tx1 = (maximum.x - ro.x) * invDx; +#endif + PxF32 txMin = i::selectMin(tx0, tx1); + PxF32 txMax = i::selectMax(tx0, tx1); + + PxF32 invDy = i::recip(i::selectMax(i::abs(rd.y), dEpsilon) * i::sign(rd.y)); +#ifdef RAYAABB_EPSILON + PxF32 ty0 = (minimum.y - RAYAABB_EPSILON - ro.y) * invDy; + PxF32 ty1 = (maximum.y + RAYAABB_EPSILON - ro.y) * invDy; +#else + PxF32 ty0 = (minimum.y - ro.y) * invDy; + PxF32 ty1 = (maximum.y - ro.y) * invDy; +#endif + PxF32 tyMin = i::selectMin(ty0, ty1); + PxF32 tyMax = i::selectMax(ty0, ty1); + + PxF32 invDz = i::recip(i::selectMax(i::abs(rd.z), dEpsilon) * i::sign(rd.z)); +#ifdef RAYAABB_EPSILON + PxF32 tz0 = (minimum.z - RAYAABB_EPSILON - ro.z) * invDz; + PxF32 tz1 = (maximum.z + RAYAABB_EPSILON - ro.z) * invDz; +#else + PxF32 tz0 = (minimum.z - ro.z) * invDz; + PxF32 tz1 = (maximum.z - ro.z) * invDz; +#endif + PxF32 tzMin = i::selectMin(tz0, tz1); + PxF32 tzMax = i::selectMax(tz0, tz1); + + PxF32 maxOfNears = i::selectMax(i::selectMax(txMin, tyMin), tzMin); + PxF32 minOfFars = i::selectMin(i::selectMin(txMax, tyMax), tzMax); + + tnear = i::selectMax(maxOfNears, 0.0f); + tfar = i::selectMin(minOfFars, maxDist); + + return (tnear 0.0f) + { + PxReal fInvLength; + if ( PxAbs(kW.x) >= PxAbs(kW.y) ) + { + // W.x or W.z is the largest magnitude component, swap them + fInvLength = PxRecipSqrt(kW.x*kW.x + kW.z*kW.z); + kU.x = -kW.z*fInvLength; + kU.y = 0.0f; + kU.z = kW.x*fInvLength; + } + else + { + // W.y or W.z is the largest magnitude component, swap them + fInvLength = PxRecipSqrt(kW.y*kW.y + kW.z*kW.z); + kU.x = 0.0f; + kU.y = kW.z*fInvLength; + kU.z = -kW.y*fInvLength; + } + } + + PxVec3 kV = kW.cross(kU); + kV.normalize(); // PT: fixed november, 24, 2004. This is a bug in Magic. + + // compute intersection + + PxVec3 kD(kU.dot(dir), kV.dot(dir), kW.dot(dir)); + const float fDLength = kD.magnitude(); + const float fInvDLength = fDLength!=0.0f ? 1.0f/fDLength : 0.0f; + kD *= fInvDLength; + + const PxVec3 kDiff = origin - p0; + const PxVec3 kP(kU.dot(kDiff), kV.dot(kDiff), kW.dot(kDiff)); + const PxReal fRadiusSqr = radius*radius; + + // Is the velocity parallel to the capsule direction? (or zero) + if ( PxAbs(kD.z) >= 1.0f - PX_EPS_REAL || fDLength < PX_EPS_REAL ) + { + const float fAxisDir = dir.dot(kW); + + const PxReal fDiscr = fRadiusSqr - kP.x*kP.x - kP.y*kP.y; + if ( fAxisDir < 0 && fDiscr >= 0.0f ) + { + // Velocity anti-parallel to the capsule direction + const PxReal fRoot = PxSqrt(fDiscr); + s[0] = (kP.z + fRoot)*fInvDLength; + s[1] = -(fWLength - kP.z + fRoot)*fInvDLength; + return 2; + } + else if ( fAxisDir > 0 && fDiscr >= 0.0f ) + { + // Velocity parallel to the capsule direction + const PxReal fRoot = PxSqrt(fDiscr); + s[0] = -(kP.z + fRoot)*fInvDLength; + s[1] = (fWLength - kP.z + fRoot)*fInvDLength; + return 2; + } + else + { + // sphere heading wrong direction, or no velocity at all + return 0; + } + } + + // test intersection with infinite cylinder + PxReal fA = kD.x*kD.x + kD.y*kD.y; + PxReal fB = kP.x*kD.x + kP.y*kD.y; + PxReal fC = kP.x*kP.x + kP.y*kP.y - fRadiusSqr; + PxReal fDiscr = fB*fB - fA*fC; + if ( fDiscr < 0.0f ) + { + // line does not intersect infinite cylinder + return 0; + } + + PxU32 iQuantity = 0; + + if ( fDiscr > 0.0f ) + { + // line intersects infinite cylinder in two places + const PxReal fRoot = PxSqrt(fDiscr); + const PxReal fInv = 1.0f/fA; + PxReal fT = (-fB - fRoot)*fInv; + PxReal fTmp = kP.z + fT*kD.z; + const float epsilon = 1e-3f; // PT: see TA35174 + if ( fTmp >= -epsilon && fTmp <= fWLength+epsilon ) + s[iQuantity++] = fT*fInvDLength; + + fT = (-fB + fRoot)*fInv; + fTmp = kP.z + fT*kD.z; + if ( fTmp >= -epsilon && fTmp <= fWLength+epsilon ) + s[iQuantity++] = fT*fInvDLength; + + if ( iQuantity == 2 ) + { + // line intersects capsule wall in two places + return 2; + } + } + else + { + // line is tangent to infinite cylinder + const PxReal fT = -fB/fA; + const PxReal fTmp = kP.z + fT*kD.z; + if ( 0.0f <= fTmp && fTmp <= fWLength ) + { + s[0] = fT*fInvDLength; + return 1; + } + } + + // test intersection with bottom hemisphere + // fA = 1 + fB += kP.z*kD.z; + fC += kP.z*kP.z; + fDiscr = fB*fB - fC; + if ( fDiscr > 0.0f ) + { + const PxReal fRoot = PxSqrt(fDiscr); + PxReal fT = -fB - fRoot; + PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp <= 0.0f ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + + fT = -fB + fRoot; + fTmp = kP.z + fT*kD.z; + if ( fTmp <= 0.0f ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + else if ( fDiscr == 0.0f ) + { + const PxReal fT = -fB; + const PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp <= 0.0f ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + + // test intersection with top hemisphere + // fA = 1 + fB -= kD.z*fWLength; + fC += fWLength*(fWLength - 2.0f*kP.z); + + fDiscr = fB*fB - fC; + if ( fDiscr > 0.0f ) + { + const PxReal fRoot = PxSqrt(fDiscr); + PxReal fT = -fB - fRoot; + PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp >= fWLength ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + + fT = -fB + fRoot; + fTmp = kP.z + fT*kD.z; + if ( fTmp >= fWLength ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + else if ( fDiscr == 0.0f ) + { + const PxReal fT = -fB; + const PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp >= fWLength ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + return iQuantity; +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h new file mode 100644 index 000000000..bac7c0b52 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_CAPSULE_H +#define GU_INTERSECTION_RAY_CAPSULE_H + +#include "CmPhysXCommon.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuIntersectionRay.h" + +namespace physx +{ +namespace Gu +{ + PxU32 intersectRayCapsuleInternal(const PxVec3& origin, const PxVec3& dir, const PxVec3& p0, const PxVec3& p1, float radius, PxReal s[2]); + + PX_FORCE_INLINE bool intersectRayCapsule(const PxVec3& origin, const PxVec3& dir, const PxVec3& p0, const PxVec3& p1, float radius, PxReal& t) + { + // PT: move ray origin close to capsule, to solve accuracy issues. + // We compute the distance D between the ray origin and the capsule's segment. + // Then E = D - radius = distance between the ray origin and the capsule. + // We can move the origin freely along 'dir' up to E units before touching the capsule. + PxReal l = distancePointSegmentSquaredInternal(p0, p1 - p0, origin); + l = PxSqrt(l) - radius; + + // PT: if this becomes negative or null, the ray starts inside the capsule and we can early exit + if(l<=0.0f) + { + t = 0.0f; + return true; + } + + // PT: we remove an arbitrary GU_RAY_SURFACE_OFFSET units to E, to make sure we don't go close to the surface. + // If we're moving in the direction of the capsule, the origin is now about GU_RAY_SURFACE_OFFSET units from it. + // If we're moving away from the capsule, the ray won't hit the capsule anyway. + // If l is smaller than GU_RAY_SURFACE_OFFSET we're close enough, accuracy is good, there is nothing to do. + if(l>GU_RAY_SURFACE_OFFSET) + l -= GU_RAY_SURFACE_OFFSET; + else + l = 0.0f; + + // PT: move origin closer to capsule and do the raycast + PxReal s[2]; + const PxU32 nbHits = Gu::intersectRayCapsuleInternal(origin + l*dir, dir, p0, p1, radius, s); + if(!nbHits) + return false; + + // PT: keep closest hit only + if(nbHits == 1) + t = s[0]; + else + t = (s[0] < s[1]) ? s[0] : s[1]; + + // PT: fix distance (smaller than expected after moving ray close to capsule) + t += l; + return true; + } + + PX_FORCE_INLINE bool intersectRayCapsule(const PxVec3& origin, const PxVec3& dir, const Gu::Capsule& capsule, PxReal& t) + { + return Gu::intersectRayCapsule(origin, dir, capsule.p0, capsule.p1, capsule.radius, t); + } +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h new file mode 100644 index 000000000..0418f1f49 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h @@ -0,0 +1,58 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_PLANE_H +#define GU_INTERSECTION_RAY_PLANE_H + +#include "foundation/PxPlane.h" + +namespace physx +{ +namespace Gu +{ + // Returns true if line and plane are not parallel + PX_INLINE bool intersectRayPlane(const PxVec3& orig, const PxVec3& dir, const PxPlane& plane, float& distanceAlongLine, PxVec3* pointOnPlane = NULL) + { + const float dn = dir.dot(plane.n); + if(-1E-7f < dn && dn < 1E-7f) + return false; // parallel + + distanceAlongLine = -plane.distance(orig)/dn; + + if(pointOnPlane) + *pointOnPlane = orig + distanceAlongLine * dir; + + return true; + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp new file mode 100644 index 000000000..d6244b714 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp @@ -0,0 +1,105 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec3.h" +#include "GuIntersectionRaySphere.h" +#include "GuIntersectionRay.h" + +using namespace physx; + +// Based on GD Mag code, but now works correctly when origin is inside the sphere. +// This version has limited accuracy. +bool Gu::intersectRaySphereBasic(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos) +{ + // get the offset vector + const PxVec3 offset = center - origin; + + // get the distance along the ray to the center point of the sphere + const PxReal ray_dist = dir.dot(offset); + + // get the squared distances + const PxReal off2 = offset.dot(offset); + const PxReal rad_2 = radius * radius; + if(off2 <= rad_2) + { + // we're in the sphere + if(hit_pos) + *hit_pos = origin; + dist = 0.0f; + return true; + } + + if(ray_dist <= 0 || (ray_dist - length) > radius) + { + // moving away from object or too far away + return false; + } + + // find hit distance squared + const PxReal d = rad_2 - (off2 - ray_dist * ray_dist); + if(d<0.0f) + { + // ray passes by sphere without hitting + return false; + } + + // get the distance along the ray + dist = ray_dist - PxSqrt(d); + if(dist > length) + { + // hit point beyond length + return false; + } + + // sort out the details + if(hit_pos) + *hit_pos = origin + dir * dist; + return true; +} + +// PT: modified version calls the previous function, but moves the ray origin closer to the sphere. The test accuracy is +// greatly improved as a result. This is an idea proposed on the GD-Algorithms list by Eddie Edwards. +// See: http://www.codercorner.com/blog/?p=321 +bool Gu::intersectRaySphere(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos) +{ + const PxVec3 x = origin - center; + PxReal l = PxSqrt(x.dot(x)) - radius - GU_RAY_SURFACE_OFFSET; + +// if(l<0.0f) +// l=0.0f; + l = physx::intrinsics::selectMax(l, 0.0f); + + bool status = intersectRaySphereBasic(origin + l*dir, dir, length - l, center, radius, dist, hit_pos); + if(status) + { +// dist += l/length; + dist += l; + } + return status; +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h new file mode 100644 index 000000000..f97d892a8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h @@ -0,0 +1,50 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_SPHERE_H +#define GU_INTERSECTION_RAY_SPHERE_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + // PT: basic version, limited accuracy, might fail for long rays vs small spheres + PX_PHYSX_COMMON_API bool intersectRaySphereBasic(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos = NULL); + + // PT: version with improved accuracy + PX_PHYSX_COMMON_API bool intersectRaySphere(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos = NULL); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h new file mode 100644 index 000000000..25c081f5a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h @@ -0,0 +1,179 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_TRIANGLE_H +#define GU_INTERSECTION_RAY_TRIANGLE_H + +#include "foundation/PxVec3.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +namespace Gu +{ + // PT: this is used for backface culling. It existed in Moller's original code already. Basically this is only to avoid dividing by zero. + // This should not depend on what units are used, and neither should it depend on the size of triangles. A large triangle with the same + // orientation as a small triangle should be backface culled the same way. A triangle whose orientation does not change should not suddenly + // become culled or visible when we scale it. + // + // An absolute epsilon is fine here. The computation will work fine for small triangles, and large triangles will simply make 'det' larger, + // more and more inaccurate, but it won't suddenly make it negative. + // + // Using FLT_EPSILON^2 ensures that triangles whose edges are smaller than FLT_EPSILON long are rejected. This epsilon makes the code work + // for very small triangles, while still preventing divisions by too small values. + #define GU_CULLING_EPSILON_RAY_TRIANGLE FLT_EPSILON*FLT_EPSILON + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes a ray-triangle intersection test. + * From Tomas Moeller's "Fast Minimum Storage Ray-Triangle Intersection" + * Could be optimized and cut into 2 methods (culled or not). Should make a batch one too to avoid the call overhead, or make it inline. + * + * \param orig [in] ray origin + * \param dir [in] ray direction + * \param vert0 [in] triangle vertex + * \param vert1 [in] triangle vertex + * \param vert2 [in] triangle vertex + * \param at [out] distance + * \param au [out] impact barycentric coordinate + * \param av [out] impact barycentric coordinate + * \param cull [in] true to use backface culling + * \param enlarge [in] enlarge triangle by specified epsilon in UV space to avoid false near-edge rejections + * \return true on overlap + * \note u, v and t will remain unchanged if false is returned. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PX_FORCE_INLINE bool intersectRayTriangle( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, + PxReal& at, PxReal& au, PxReal& av, + bool cull, float enlarge=0.0f) + { + // Find vectors for two edges sharing vert0 + const PxVec3 edge1 = vert1 - vert0; + const PxVec3 edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); // error ~ |v2-v0| + + // If determinant is near zero, ray lies in plane of triangle + const PxReal det = edge1.dot(pvec); // error ~ |v2-v0|*|v1-v0| + + if(cull) + { + if(detuvlimit2) + return false; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const PxReal v = dir.dot(qvec); + if(vuvlimit2) + return false; + + // Calculate t, scale parameters, ray intersects triangle + const PxReal t = edge2.dot(qvec); + + const PxReal inv_det = 1.0f / det; + at = t*inv_det; + au = u*inv_det; + av = v*inv_det; + } + else + { + // the non-culling branch + if(PxAbs(det)1.0f+enlarge) + return false; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const PxReal v = dir.dot(qvec) * inv_det; + if(v<-enlarge || (u+v)>1.0f+enlarge) + return false; + + // Calculate t, ray intersects triangle + const PxReal t = edge2.dot(qvec) * inv_det; + + at = t; + au = u; + av = v; + } + return true; + } + + /* \note u, v and t will remain unchanged if false is returned. */ + PX_FORCE_INLINE bool intersectRayTriangleCulling( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, + PxReal& t, PxReal& u, PxReal& v, + float enlarge=0.0f) + { + return intersectRayTriangle(orig, dir, vert0, vert1, vert2, t, u, v, true, enlarge); + } + + /* \note u, v and t will remain unchanged if false is returned. */ + PX_FORCE_INLINE bool intersectRayTriangleNoCulling( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, + PxReal& t, PxReal& u, PxReal& v, + float enlarge=0.0f) + { + return intersectRayTriangle(orig, dir, vert0, vert1, vert2, t, u, v, false, enlarge); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp new file mode 100644 index 000000000..26638001b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp @@ -0,0 +1,88 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionSphereBox.h" +#include "GuSphere.h" +#include "GuBox.h" + +using namespace physx; + +bool Gu::intersectSphereBox(const Sphere& sphere, const Box& box) +{ + const PxVec3 delta = sphere.center - box.center; + PxVec3 dRot = box.rot.transformTranspose(delta); //transform delta into OBB body coords. (use method call!) + + //check if delta is outside AABB - and clip the vector to the AABB. + bool outside = false; + + if(dRot.x < -box.extents.x) + { + outside = true; + dRot.x = -box.extents.x; + } + else if(dRot.x > box.extents.x) + { + outside = true; + dRot.x = box.extents.x; + } + + if(dRot.y < -box.extents.y) + { + outside = true; + dRot.y = -box.extents.y; + } + else if(dRot.y > box.extents.y) + { + outside = true; + dRot.y = box.extents.y; + } + + if(dRot.z < -box.extents.z) + { + outside = true; + dRot.z = -box.extents.z; + } + else if(dRot.z > box.extents.z) + { + outside = true; + dRot.z = box.extents.z; + } + + if(outside) //if clipping was done, sphere center is outside of box. + { + const PxVec3 clippedDelta = box.rot.transform(dRot); //get clipped delta back in world coords. + + const PxVec3 clippedVec = delta - clippedDelta; //what we clipped away. + const PxReal lenSquared = clippedVec.magnitudeSquared(); + const PxReal radius = sphere.radius; + if(lenSquared > radius * radius) // PT: objects are defined as closed, so we return 'true' in case of equality + return false; //disjoint + } + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h new file mode 100644 index 000000000..e787c39d0 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_SPHERE_BOX_H +#define GU_INTERSECTION_SPHERE_BOX_H + +namespace physx +{ +namespace Gu +{ + class Sphere; + class Box; + + /** + Checks if a sphere intersects a box. Based on: Jim Arvo, A Simple Method for Box-Sphere Intersection Testing, Graphics Gems, pp. 247-250. + + \param sphere [in] sphere + \param box [in] box + + \return true if sphere overlaps box (or exactly touches it) + */ + bool intersectSphereBox(const Gu::Sphere& sphere, const Gu::Box& box); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp new file mode 100644 index 000000000..854fe53e0 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp @@ -0,0 +1,198 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionTriangleBox.h" +#include "GuIntersectionTriangleBoxRef.h" +#include "CmMatrix34.h" +#include "PsVecMath.h" +#include "GuBox.h" +#include "GuSIMDHelpers.h" + +using namespace physx; + + +Ps::IntBool Gu::intersectTriangleBox_ReferenceCode(const PxVec3& boxcenter, const PxVec3& extents, const PxVec3& tp0, const PxVec3& tp1, const PxVec3& tp2) +{ + return intersectTriangleBox_RefImpl(boxcenter, extents, tp0, tp1, tp2); +} + + +using namespace Ps::aos; + +static PX_FORCE_INLINE int testClassIIIAxes(const Vec4V& e0V, const Vec4V v0V, const Vec4V v1V, const Vec4V v2V, const PxVec3& extents) +{ + const Vec4V e0XZY_V = V4PermYZXW(e0V); + + const Vec4V v0XZY_V = V4PermYZXW(v0V); + const Vec4V p0V = V4NegMulSub(v0XZY_V, e0V, V4Mul(v0V, e0XZY_V)); + + const Vec4V v1XZY_V = V4PermYZXW(v1V); + const Vec4V p1V = V4NegMulSub(v1XZY_V, e0V, V4Mul(v1V, e0XZY_V)); + + const Vec4V v2XZY_V = V4PermYZXW(v2V); + const Vec4V p2V = V4NegMulSub(v2XZY_V, e0V, V4Mul(v2V, e0XZY_V)); + + Vec4V minV = V4Min(p0V, p1V); + minV = V4Min(minV, p2V); + + const Vec4V extentsV = V4LoadU(&extents.x); + const Vec4V fe0ZYX_V = V4Abs(e0V); + + const Vec4V fe0XZY_V = V4PermYZXW(fe0ZYX_V); + const Vec4V extentsXZY_V = V4PermYZXW(extentsV); + Vec4V radV = V4MulAdd(extentsV, fe0XZY_V, V4Mul(extentsXZY_V, fe0ZYX_V)); + + if(V4AnyGrtr3(minV, radV)) + return 0; + + Vec4V maxV = V4Max(p0V, p1V); + maxV = V4Max(maxV, p2V); + + radV = V4Sub(V4Zero(), radV); + + if(V4AnyGrtr3(radV, maxV)) + return 0; + return 1; +} + +static const VecU32V signV = U4LoadXYZW(0x80000000, 0x80000000, 0x80000000, 0x80000000); + +static PX_FORCE_INLINE Ps::IntBool intersectTriangleBoxInternal(const Vec4V v0V, const Vec4V v1V, const Vec4V v2V, const PxVec3& extents) +{ + // Test box axes + { + Vec4V extentsV = V4LoadU(&extents.x); + + { + const Vec4V cV = V4Abs(v0V); + if(V4AllGrtrOrEq3(extentsV, cV)) + return 1; + } + + Vec4V minV = V4Min(v0V, v1V); + minV = V4Min(minV, v2V); + + if(V4AnyGrtr3(minV, extentsV)) + return 0; + + Vec4V maxV = V4Max(v0V, v1V); + maxV = V4Max(maxV, v2V); + extentsV = V4Sub(V4Zero(), extentsV); + + if(V4AnyGrtr3(extentsV, maxV)) + return 0; + } + + // Test if the box intersects the plane of the triangle + const Vec4V e0V = V4Sub(v1V, v0V); + const Vec4V e1V = V4Sub(v2V, v1V); + { + const Vec4V normalV = V4Cross(e0V, e1V); + const Vec4V dV = Vec4V_From_FloatV(V4Dot3(normalV, v0V)); + + const Vec4V extentsV = V4LoadU(&extents.x); + VecU32V normalSignsV = V4U32and(VecU32V_ReinterpretFrom_Vec4V(normalV), signV); + const Vec4V maxV = Vec4V_ReinterpretFrom_VecU32V(V4U32or(VecU32V_ReinterpretFrom_Vec4V(extentsV), normalSignsV)); + + Vec4V tmpV = Vec4V_From_FloatV(V4Dot3(normalV, maxV)); + if(V4AnyGrtr3(dV, tmpV)) + return 0; + + normalSignsV = V4U32xor(normalSignsV, signV); + const Vec4V minV = Vec4V_ReinterpretFrom_VecU32V(V4U32or(VecU32V_ReinterpretFrom_Vec4V(extentsV), normalSignsV)); + + tmpV = Vec4V_From_FloatV(V4Dot3(normalV, minV)); + if(V4AnyGrtr3(tmpV, dV)) + return 0; + } + + // Edge-edge tests + { + if(!testClassIIIAxes(e0V, v0V, v1V, v2V, extents)) + return 0; + if(!testClassIIIAxes(e1V, v0V, v1V, v2V, extents)) + return 0; + const Vec4V e2V = V4Sub(v0V, v2V); + if(!testClassIIIAxes(e2V, v0V, v1V, v2V, extents)) + return 0; + } + return 1; +} + +// PT: a SIMD version of Tomas Moller's triangle-box SAT code +Ps::IntBool Gu::intersectTriangleBox_Unsafe(const PxVec3& center, const PxVec3& extents, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) +{ + // Move everything so that the boxcenter is in (0,0,0) + const Vec4V BoxCenterV = V4LoadU(¢er.x); + const Vec4V v0V = V4Sub(V4LoadU(&p0.x), BoxCenterV); + const Vec4V v1V = V4Sub(V4LoadU(&p1.x), BoxCenterV); + const Vec4V v2V = V4Sub(V4LoadU(&p2.x), BoxCenterV); + + return intersectTriangleBoxInternal(v0V, v1V, v2V, extents); +} + +Ps::IntBool Gu::intersectTriangleBox(const BoxPadded& box, const PxVec3& p0_, const PxVec3& p1_, const PxVec3& p2_) +{ + // PT: TODO: SIMDify this part + + // Vec3p ensures we can safely V4LoadU the data + const Vec3p p0 = box.rotateInv(p0_ - box.center); + const Vec3p p1 = box.rotateInv(p1_ - box.center); + const Vec3p p2 = box.rotateInv(p2_ - box.center); + + const Vec4V v0V = V4LoadU(&p0.x); + const Vec4V v1V = V4LoadU(&p1.x); + const Vec4V v2V = V4LoadU(&p2.x); + + return intersectTriangleBoxInternal(v0V, v1V, v2V, box.extents); +} + +static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33& mat) +{ + const FloatV xxxV = V4GetX(p); + const FloatV yyyV = V4GetY(p); + const FloatV zzzV = V4GetZ(p); + + Vec4V ResV = V4Scale(V4LoadU(&mat.column0.x), xxxV); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat.column1.x), yyyV)); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat.column2.x), zzzV)); + return ResV; +} + +// PT: warning: all params must be safe to V4LoadU +Ps::IntBool intersectTriangleBoxBV4(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, + const PxMat33& rotModelToBox, const PxVec3& transModelToBox, const PxVec3& extents) +{ + const Vec4V transModelToBoxV = V4LoadU(&transModelToBox.x); + const Vec4V v0V = V4Add(multiply3x3V(V4LoadU(&p0.x), rotModelToBox), transModelToBoxV); + const Vec4V v1V = V4Add(multiply3x3V(V4LoadU(&p1.x), rotModelToBox), transModelToBoxV); + const Vec4V v2V = V4Add(multiply3x3V(V4LoadU(&p2.x), rotModelToBox), transModelToBoxV); + + return intersectTriangleBoxInternal(v0V, v1V, v2V, extents); +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp new file mode 100644 index 000000000..e049f883e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp @@ -0,0 +1,276 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "GuBV32.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + + +BV32Tree::BV32Tree(SourceMesh* meshInterface, const PxBounds3& localBounds) +{ + reset(); + init(meshInterface, localBounds); +} + +BV32Tree::BV32Tree() +{ + reset(); +} + +void BV32Tree::release() +{ + if (!mUserAllocated) + { + DELETEARRAY(mNodes); + PX_FREE_AND_RESET(mPackedNodes); + } + mNodes = NULL; + mNbNodes = 0; +} + +BV32Tree::~BV32Tree() +{ + release(); +} + +void BV32Tree::reset() +{ + mMeshInterface = NULL; + mNbNodes = 0; + mNodes = NULL; + mNbPackedNodes = 0; + mPackedNodes = NULL; + mInitData = 0; + mUserAllocated = false; +} + +void BV32Tree::operator=(BV32Tree& v) +{ + mMeshInterface = v.mMeshInterface; + mLocalBounds = v.mLocalBounds; + mNbNodes = v.mNbNodes; + mNodes = v.mNodes; + mInitData = v.mInitData; + mUserAllocated = v.mUserAllocated; + v.reset(); +} + +bool BV32Tree::init(SourceMesh* meshInterface, const PxBounds3& localBounds) +{ + mMeshInterface = meshInterface; + mLocalBounds.init(localBounds); + return true; +} + +// PX_SERIALIZATION +BV32Tree::BV32Tree(const PxEMPTY) +{ + mUserAllocated = true; +} + +void BV32Tree::exportExtraData(PxSerializationContext& stream) +{ + stream.alignData(16); + stream.writeData(mPackedNodes, mNbNodes*sizeof(BV32DataPacked)); +} + +void BV32Tree::importExtraData(PxDeserializationContext& context) +{ + context.alignExtraData(16); + mPackedNodes = context.readExtraData(mNbNodes); +} +//~PX_SERIALIZATION + +bool BV32Tree::load(PxInputStream& stream, bool mismatch_) +{ + PX_ASSERT(!mUserAllocated); + + release(); + + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if(a != 'B' || b != 'V' || c != '3' || d != '2') + return false; + + bool mismatch; + PxU32 fileVersion; + if(!readBigEndianVersionNumber(stream, mismatch_, fileVersion, mismatch)) + return false; + + mLocalBounds.mCenter.x = readFloat(mismatch, stream); + mLocalBounds.mCenter.y = readFloat(mismatch, stream); + mLocalBounds.mCenter.z = readFloat(mismatch, stream); + mLocalBounds.mExtentsMagnitude = readFloat(mismatch, stream); + + mInitData = readDword(mismatch, stream); + + /*const PxU32 nbNodes = readDword(mismatch, stream); + mNbNodes = nbNodes; + + if (nbNodes) + { + BV32Data* nodes = PX_NEW(BV32Data)[nbNodes]; + + mNodes = nodes; + Cm::markSerializedMem(nodes, sizeof(BV32Data)*nbNodes); + + for (PxU32 i = 0; i(PX_ALLOC(sizeof(BV32DataPacked)*nbPackedNodes, "BV32DataPacked")); + + Cm::markSerializedMem(mPackedNodes, sizeof(BV32DataPacked)*nbPackedNodes); + + for (PxU32 i = 0; i < nbPackedNodes; ++i) + { + BV32DataPacked& node = mPackedNodes[i]; + node.mNbNodes = readDword(mismatch, stream); + PX_ASSERT(node.mNbNodes > 0); + ReadDwordBuffer(node.mData, node.mNbNodes, mismatch, stream); + const PxU32 nbElements = 4 * node.mNbNodes; + readFloatBuffer(&node.mCenter[0].x, nbElements, mismatch, stream); + readFloatBuffer(&node.mExtents[0].x, nbElements, mismatch, stream); + + } + } + + return true; +} + + +void BV32Tree::calculateLeafNode(BV32Data& node) +{ + if (!node.isLeaf()) + { + const PxU32 nbChildren = node.getNbChildren(); + const PxU32 offset = node.getChildOffset(); + //calcualte how many children nodes are leaf nodes + PxU32 nbLeafNodes = 0; + for (PxU32 i = 0; i < nbChildren; ++i) + { + BV32Data& child = mNodes[offset + i]; + + if (child.isLeaf()) + { + nbLeafNodes++; + } + } + + node.mNbLeafNodes = nbLeafNodes; + for (PxU32 i = 0; i < nbChildren; ++i) + { + BV32Data& child = mNodes[offset + i]; + calculateLeafNode(child); + } + + } +} + + + +void BV32Tree::createSOAformatNode(BV32DataPacked& packedData, const BV32Data& node, const PxU32 childOffset, PxU32& currentIndex, PxU32& nbPackedNodes) +{ + + //found the next 32 nodes and fill it in SOA format + + const PxU32 nbChildren = node.getNbChildren(); + const PxU32 offset = node.getChildOffset(); + + + for (PxU32 i = 0; i < nbChildren; ++i) + { + BV32Data& child = mNodes[offset + i]; + + packedData.mCenter[i] = PxVec4(child.mCenter, 0.f); + packedData.mExtents[i] = PxVec4(child.mExtents, 0.f); + packedData.mData[i] = PxU32(child.mData); + } + + packedData.mNbNodes = nbChildren; + + PxU32 NbToGo = 0; + PxU32 NextIDs[32]; + memset(NextIDs, PX_INVALID_U32, sizeof(PxU32) * 32); + const BV32Data* ChildNodes[32]; + memset(ChildNodes, 0, sizeof(BV32Data*) * 32); + + + for (PxU32 i = 0; i< nbChildren; i++) + { + BV32Data& child = mNodes[offset + i]; + + if (!child.isLeaf()) + { + const PxU32 NextID = currentIndex; + + const PxU32 ChildSize = child.getNbChildren() - child.mNbLeafNodes; + currentIndex += ChildSize; + + //packedData.mData[i] = (packedData.mData[i] & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) | (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT); + packedData.mData[i] = (packedData.mData[i] & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) | ((childOffset + NbToGo) << GU_BV4_CHILD_OFFSET_SHIFT_COUNT); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = &child; + NbToGo++; + } + } + + nbPackedNodes += NbToGo; + for (PxU32 i = 0; i < NbToGo; ++i) + { + const BV32Data& child = *ChildNodes[i]; + + BV32DataPacked& childData = mPackedNodes[childOffset+i]; + + createSOAformatNode(childData, child, NextIDs[i], currentIndex, nbPackedNodes); + + } + +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h new file mode 100644 index 000000000..1ac5d53c2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h @@ -0,0 +1,146 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV32_H +#define GU_BV32_H + +#include "foundation/PxBounds3.h" +#include "PxSerialFramework.h" +#include "PsUserAllocated.h" +#include "GuBV4.h" +#include "CmPhysXCommon.h" +#include "PsArray.h" +#include "foundation/PxVec4.h" + +namespace physx +{ + namespace Gu + { + struct BV32Data : public physx::shdfnd::UserAllocated + { + PxVec3 mCenter; + PxU32 mNbLeafNodes; + PxVec3 mExtents; + size_t mData; + + + PX_FORCE_INLINE BV32Data() : mNbLeafNodes(0), mData(PX_INVALID_U32) + { + setEmpty(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 isLeaf() const { return mData & 1; } + + //if the node is leaf, + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbReferencedTriangles() const { PX_ASSERT(isLeaf()); return PxU32((mData >>1)&63); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getTriangleStartIndex() const { PX_ASSERT(isLeaf()); return PxU32(mData >> 7); } + + //PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getPrimitive() const { return mData >> 1; } + //if the node isn't leaf, we will get the childOffset + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getChildOffset() const { PX_ASSERT(!isLeaf()); return PxU32(mData >> GU_BV4_CHILD_OFFSET_SHIFT_COUNT); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbChildren() const { PX_ASSERT(!isLeaf()); return ((mData) & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1))>>1; } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void getMinMax(PxVec3& min, PxVec3& max) const + { + min = mCenter - mExtents; + max = mCenter + mExtents; + } + + PX_FORCE_INLINE void setEmpty() + { + mCenter = PxVec3(0.0f, 0.0f, 0.0f); + mExtents = PxVec3(-1.0f, -1.0f, -1.0f); + } + + }; + + PX_ALIGN_PREFIX(16) + struct BV32DataPacked + { + PxVec4 mCenter[32]; + PxVec4 mExtents[32]; + PxU32 mData[32]; + PxU32 mNbNodes; + PxU32 pad[3]; + + PX_CUDA_CALLABLE PX_FORCE_INLINE BV32DataPacked() : mNbNodes(0) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 isLeaf(const PxU32 index) const { return mData[index] & 1; } + //if the node is leaf, + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbReferencedTriangles(const PxU32 index) const { PX_ASSERT(isLeaf(index)); return (mData[index] >> 1) & 63; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getTriangleStartIndex(const PxU32 index) const { PX_ASSERT(isLeaf(index)); return (mData[index] >> 7); } + //if the node isn't leaf, we will get the childOffset + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getChildOffset(const PxU32 index) const { PX_ASSERT(!isLeaf(index)); return mData[index] >> GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbChildren(const PxU32 index) const { PX_ASSERT(!isLeaf(index)); return ((mData[index])& ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) >> 1; } + } + PX_ALIGN_SUFFIX(16); + + class BV32Tree : public physx::shdfnd::UserAllocated + { + public: + // PX_SERIALIZATION + BV32Tree(const PxEMPTY); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + PX_PHYSX_COMMON_API BV32Tree(); + PX_PHYSX_COMMON_API BV32Tree(SourceMesh* meshInterface, const PxBounds3& localBounds); + PX_PHYSX_COMMON_API ~BV32Tree(); + + bool load(PxInputStream& stream, bool mismatch); + + void calculateLeafNode(BV32Data& node); + void createSOAformatNode(BV32DataPacked& packedData, const BV32Data& node, const PxU32 childOffset, PxU32& currentIndex, PxU32& nbPackedNodes); + + void reset(); + void operator = (BV32Tree& v); + + bool init(SourceMesh* meshInterface, const PxBounds3& localBounds); + void release(); + + SourceMesh* mMeshInterface; + LocalBounds mLocalBounds; + + PxU32 mNbNodes; + BV32Data* mNodes; + BV32DataPacked* mPackedNodes; + PxU32 mNbPackedNodes; + PxU32 mInitData; + bool mUserAllocated; // PT: please keep these 4 bytes right after mCenterOrMinCoeff/mExtentsOrMaxCoeff for safe V4 loading + bool mPadding[3]; + }; + + } // namespace Gu +} + +#endif // GU_BV32_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp new file mode 100644 index 000000000..2f2e633f4 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp @@ -0,0 +1,530 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec4.h" +#include "GuBV32Build.h" +#include "GuBV32.h" +#include "PxTriangle.h" +#include "CmPhysXCommon.h" +#include "PsBasicTemplates.h" +#include "GuCenterExtents.h" +#include "GuBV4Build.h" +#include "PsAllocator.h" + +using namespace physx; +using namespace Gu; + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#define DELETESINGLE(x) if (x) { delete x; x = NULL; } +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +struct BV32Node : public physx::shdfnd::UserAllocated +{ + BV32Node() : mNbChildBVNodes(0) + {} + + BV32Data mBVData[32]; + PxU32 mNbChildBVNodes; + + PX_FORCE_INLINE size_t isLeaf(PxU32 i) const { return mBVData[i].mData & 1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return PxU32(mBVData[i].mData >> 1); } + PX_FORCE_INLINE const BV32Node* getChild(PxU32 i) const { return reinterpret_cast(mBVData[i].mData); } + + + PxU32 getSize() const + { + return sizeof(BV32Data)*mNbChildBVNodes; + } +}; + + +static void fillInNodes(const AABBTreeNode* current_node, const PxU32 startIndex, const PxU32 endIndex, const AABBTreeNode** NODES, PxU32& stat) +{ + + if (startIndex + 1 == endIndex) + { + //fill in nodes + const AABBTreeNode* P = current_node->getPos(); + const AABBTreeNode* N = current_node->getNeg(); + NODES[startIndex] = P; + NODES[endIndex] = N; + stat += 2; + } + else + { + const AABBTreeNode* P = current_node->getPos(); + const AABBTreeNode* N = current_node->getNeg(); + const PxU32 midIndex = startIndex + ((endIndex - startIndex) / 2); + if (!P->isLeaf()) + fillInNodes(P, startIndex, midIndex, NODES, stat); + else + { + NODES[startIndex] = P; + stat++; + } + + if (!N->isLeaf()) + fillInNodes(N, midIndex + 1, endIndex, NODES, stat); + else + { + NODES[midIndex + 1] = N; + stat++; + } + } +} + + + +static void setPrimitive(const AABBTree& source, BV32Node* node32, PxU32 i, const AABBTreeNode* node, float epsilon) +{ + const PxU32 nbPrims = node->getNbPrimitives(); + PX_ASSERT(nbPrims<=32); + const PxU32* indexBase = source.getIndices(); + const PxU32* prims = node->getPrimitives(); + const PxU32 offset = PxU32(prims - indexBase); + +#if BV32_VALIDATE + for (PxU32 j = 0; jmBVData[i].mCenter = node->getAABB().getCenter(); + node32->mBVData[i].mExtents = node->getAABB().getExtents(); + if (epsilon != 0.0f) + node32->mBVData[i].mExtents += PxVec3(epsilon, epsilon, epsilon); + node32->mBVData[i].mData = (primitiveIndex << 1) | 1; +} + +static BV32Node* setNode(const AABBTree& source, BV32Node* node32, PxU32 i, const AABBTreeNode* node, float epsilon) +{ + BV32Node* child = NULL; + + if (node) + { + if (node->isLeaf()) + { + setPrimitive(source, node32, i, node, epsilon); + } + else + { + node32->mBVData[i].mCenter = node->getAABB().getCenter(); + node32->mBVData[i].mExtents = node->getAABB().getExtents(); + if (epsilon != 0.0f) + node32->mBVData[i].mExtents += PxVec3(epsilon, epsilon, epsilon); + + child = PX_NEW(BV32Node); + node32->mBVData[i].mData = size_t(child); + } + } + + return child; +} + + +static void _BuildBV32(const AABBTree& source, BV32Node* tmp, const AABBTreeNode* current_node, float epsilon, PxU32& nbNodes) +{ + PX_ASSERT(!current_node->isLeaf()); + + const AABBTreeNode* NODES[32]; + memset(NODES, 0, sizeof(AABBTreeNode*) * 32); + + fillInNodes(current_node, 0, 31, NODES, tmp->mNbChildBVNodes); + + PxU32 left = 0; + PxU32 right = 31; + + while (left < right) + { + + //sweep from the front + while (leftmNbChildBVNodes; + + for (PxU32 i = 0; i < tmp->mNbChildBVNodes; ++i) + { + const AABBTreeNode* tempNode = NODES[i]; + BV32Node* Child = setNode(source, tmp, i, tempNode, epsilon); + if (Child) + { + _BuildBV32(source, Child, tempNode, epsilon, nbNodes); + } + } + +} + +// +//static void validateTree(const AABBTree& Source, const AABBTreeNode* currentNode) +//{ +// if (currentNode->isLeaf()) +// { +// const PxU32* indexBase = Source.getIndices(); +// const PxU32* prims = currentNode->getPrimitives(); +// const PxU32 offset = PxU32(prims - indexBase); +// const PxU32 nbPrims = currentNode->getNbPrimitives(); +// for (PxU32 j = 0; jgetPos(); +// validateTree(Source, pos); +// const AABBTreeNode* neg = currentNode->getNeg(); +// validateTree(Source, neg); +// } +//} + +#if BV32_VALIDATE +static void validateNodeBound(const BV32Node* currentNode, SourceMesh* mesh) +{ + const PxU32 nbNodes = currentNode->mNbChildBVNodes; + for (PxU32 i = 0; i < nbNodes; ++i) + { + const BV32Node* node = currentNode->getChild(i); + if (currentNode->isLeaf(i)) + { + BV32Data data = currentNode->mBVData[i]; + PxU32 nbTriangles = data.getNbReferencedTriangles(); + PxU32 startIndex = data.getTriangleStartIndex(); + const IndTri32* triIndices = mesh->getTris32(); + const PxVec3* verts = mesh->getVerts(); + PxVec3 min(PX_MAX_F32, PX_MAX_F32, PX_MAX_F32); + PxVec3 max(-PX_MAX_F32, -PX_MAX_F32, -PX_MAX_F32); + for (PxU32 j = 0; j < nbTriangles; ++j) + { + IndTri32 index = triIndices[startIndex + j]; + + for (PxU32 k = 0; k < 3; ++k) + { + const PxVec3& v = verts[index.mRef[k]]; + + min.x = (min.x > v.x) ? v.x : min.x; + min.y = (min.y > v.y) ? v.y : min.y; + min.z = (min.z > v.z) ? v.z : min.z; + + max.x = (max.x < v.x) ? v.x : max.x; + max.y = (max.y > v.y) ? v.y : max.y; + max.z = (max.z > v.z) ? v.z : max.z; + } + } + + PxVec3 dMin, dMax; + data.getMinMax(dMin, dMax); + PX_ASSERT(dMin.x <= min.x && dMin.y <= min.y && dMin.z <= min.z); + PX_ASSERT(dMax.x >= max.x && dMax.y >= max.y && dMax.z >= min.z); + + } + else + { + validateNodeBound(node, mesh); + } + } +} +#endif + +static bool BuildBV32Internal(BV32Tree& bv32Tree, const AABBTree& Source, SourceMesh* mesh, float epsilon) +{ + if (mesh->getNbTriangles() <= 32) + { + bv32Tree.mNbPackedNodes = 1; + bv32Tree.mPackedNodes = reinterpret_cast(PX_ALLOC(sizeof(BV32DataPacked), "BV32DataPacked")); + BV32DataPacked& packedData = bv32Tree.mPackedNodes[0]; + packedData.mNbNodes = 1; + packedData.mCenter[0] = PxVec4(Source.getBV().getCenter(), 0.f); + packedData.mExtents[0] = PxVec4(Source.getBV().getExtents(), 0.f); + packedData.mData[0] = (mesh->getNbTriangles() << 1) | 1; + return bv32Tree.init(mesh, Source.getBV()); + } + + { + struct Local + { + static void _CheckMD(const AABBTreeNode* current_node, PxU32& md, PxU32& cd) + { + cd++; + md = PxMax(md, cd); + + if (current_node->getPos()) { _CheckMD(current_node->getPos(), md, cd); cd--; } + if (current_node->getNeg()) { _CheckMD(current_node->getNeg(), md, cd); cd--; } + } + + static void _Check(AABBTreeNode* current_node) + { + if (current_node->isLeaf()) + return; + + AABBTreeNode* P = const_cast(current_node->getPos()); + AABBTreeNode* N = const_cast(current_node->getNeg()); + { + PxU32 MDP = 0; PxU32 CDP = 0; _CheckMD(P, MDP, CDP); + PxU32 MDN = 0; PxU32 CDN = 0; _CheckMD(N, MDN, CDN); + + if (MDP>MDN) + // if(MDP(Source.getNodes())); + } + + + PxU32 nbNodes = 1; + BV32Node* Root32 = PX_NEW(BV32Node); + + + _BuildBV32(Source, Root32, Source.getNodes(), epsilon, nbNodes); + +#if BV32_VALIDATE + validateNodeBound(Root32, mesh); +#endif + + if (!bv32Tree.init(mesh, Source.getBV())) + return false; + BV32Tree* T = &bv32Tree; + + // Version with variable-sized nodes in single stream + { + struct Local + { + static void _Flatten(BV32Data* const dest, const PxU32 box_id, PxU32& current_id, const BV32Node* current, PxU32& max_depth, PxU32& current_depth, const PxU32 nb_nodes) + { + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if (current_depth>max_depth) + max_depth = current_depth; + + for (PxU32 i = 0; imNbChildBVNodes; i++) + { + dest[box_id + i].mCenter = current->mBVData[i].mCenter; + dest[box_id + i].mExtents = current->mBVData[i].mExtents; + dest[box_id + i].mData = PxU32(current->mBVData[i].mData); + + PX_ASSERT(box_id + i < nb_nodes); + } + + PxU32 NbToGo = 0; + PxU32 NextIDs[32]; + memset(NextIDs, PX_INVALID_U32, sizeof(PxU32)*32); + const BV32Node* ChildNodes[32]; + memset(ChildNodes, 0, sizeof(BV32Node*)*32); + + BV32Data* data = dest + box_id; + for (PxU32 i = 0; imNbChildBVNodes; i++) + { + PX_ASSERT(current->mBVData[i].mData != PX_INVALID_U32); + + if (!current->isLeaf(i)) + { + + const BV32Node* ChildNode = current->getChild(i); + + const PxU32 NextID = current_id; + + const PxU32 ChildSize = ChildNode->mNbChildBVNodes; + current_id += ChildSize; + + const PxU32 ChildType = ChildNode->mNbChildBVNodes << 1; + data[i].mData = size_t(ChildType + (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + //PX_ASSERT(data[i].mData == size_t(ChildType+(NextID<<3))); + + PX_ASSERT(box_id + i < nb_nodes); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = ChildNode; + NbToGo++; + } + } + + + + for (PxU32 i = 0; imNbChildBVNodes+1; + + BV32Data* Nodes = PX_NEW(BV32Data)[nbNodes]; + Nodes[0].mCenter = Source.getBV().getCenter(); + Nodes[0].mExtents = Source.getBV().getExtents(); + + const PxU32 ChildType = Root32->mNbChildBVNodes << 1; + Nodes[0].mData = size_t(ChildType + (1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + + const PxU32 nbChilden = Nodes[0].getNbChildren(); + + PX_UNUSED(nbChilden); + + + T->mInitData = CurID; + PxU32 MaxDepth = 0; + PxU32 CurrentDepth = 0; + + Local::_Flatten(Nodes, 1, CurID, Root32, MaxDepth, CurrentDepth, nbNodes); + + PX_ASSERT(CurID == nbNodes); + + T->mNbNodes = nbNodes; + + T->mNodes = Nodes; + } + + + bv32Tree.calculateLeafNode(bv32Tree.mNodes[0]); + + bv32Tree.mPackedNodes = reinterpret_cast(PX_ALLOC(sizeof(BV32DataPacked)*nbNodes, "BV32DataPacked")); + bv32Tree.mNbPackedNodes = nbNodes; + + PxU32 nbPackedNodes = 1; + PxU32 currentIndex = bv32Tree.mNodes[0].getNbChildren() - bv32Tree.mNodes[0].mNbLeafNodes + 1; + BV32DataPacked& packedData = bv32Tree.mPackedNodes[0]; + bv32Tree.createSOAformatNode(packedData, bv32Tree.mNodes[0], 1, currentIndex, nbPackedNodes); + + bv32Tree.mNbPackedNodes = nbPackedNodes; + + PX_ASSERT(nbPackedNodes == currentIndex); + PX_ASSERT(nbPackedNodes > 0); + + return true; +} + +///// + +struct ReorderData32 +{ + const SourceMesh* mMesh; + PxU32* mOrder; + PxU32 mNbTrisPerLeaf; + PxU32 mIndex; + PxU32 mNbTris; + PxU32 mStats[32]; +}; + +static bool gReorderCallback(const AABBTreeNode* current, PxU32 /*depth*/, void* userData) +{ + ReorderData32* Data = reinterpret_cast(userData); + if (current->isLeaf()) + { + const PxU32 n = current->getNbPrimitives(); + PX_ASSERT(n > 0); + PX_ASSERT(n <= Data->mNbTrisPerLeaf); + Data->mStats[n-1]++; + PxU32* Prims = const_cast(current->getPrimitives()); + + for (PxU32 i = 0; imNbTris); + Data->mOrder[Data->mIndex] = Prims[i]; + PX_ASSERT(Data->mIndexmNbTris); + Prims[i] = Data->mIndex; + Data->mIndex++; + } + } + return true; +} + + +bool physx::Gu::BuildBV32Ex(BV32Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf) +{ + const PxU32 nbTris = mesh.mNbTris; + + AABBTree Source; + if (!Source.buildFromMesh(mesh, nbTrisPerLeaf)) + return false; + + + { + PxU32* order = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTris, "BV32")); + ReorderData32 RD; + RD.mMesh = &mesh; + RD.mOrder = order; + RD.mNbTrisPerLeaf = nbTrisPerLeaf; + RD.mIndex = 0; + RD.mNbTris = nbTris; + for (PxU32 i = 0; i<32; i++) + RD.mStats[i] = 0; + Source.walk(gReorderCallback, &RD); + PX_ASSERT(RD.mIndex == nbTris); + mesh.remapTopology(order); + PX_FREE(order); + // for(PxU32 i=0;i<16;i++) + // printf("%d: %d\n", i, RD.mStats[i]); + } + + + //if (mesh.getNbTriangles() <= nbTrisPerLeaf) + // return tree.init(&mesh, Source.getBV()); + + return BuildBV32Internal(tree, Source, &mesh, epsilon); +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h new file mode 100644 index 000000000..7c03e8193 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h @@ -0,0 +1,50 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV32_BUILD_H +#define GU_BV32_BUILD_H + +#include "foundation/PxSimpleTypes.h" +#include "common/PxPhysXCommonConfig.h" + +#define BV32_VALIDATE 0 + +namespace physx +{ + namespace Gu + { + class BV32Tree; + class SourceMesh; + + PX_PHYSX_COMMON_API bool BuildBV32Ex(BV32Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf); + + } // namespace Gu +} + +#endif // GU_BV32_BUILD_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp new file mode 100644 index 000000000..8d7896148 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp @@ -0,0 +1,390 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "GuBV4.h" +#include "GuBV4_Common.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +SourceMeshBase::SourceMeshBase() +{ + mNbVerts = 0; + mVerts = NULL; + mRemap = NULL; +} + +SourceMeshBase::~SourceMeshBase() +{ + PX_FREE_AND_RESET(mRemap); +} + +SourceMesh::SourceMesh() +{ + reset(); +} + +SourceMesh::~SourceMesh() +{ + +} + +void SourceMesh::reset() +{ + mNbVerts = 0; + mVerts = NULL; + mNbTris = 0; + mTriangles32 = NULL; + mTriangles16 = NULL; + mRemap = NULL; +} + +void SourceMesh::operator=(SourceMesh& v) +{ + mNbVerts = v.mNbVerts; + mVerts = v.mVerts; + mNbTris = v.mNbTris; + mTriangles32 = v.mTriangles32; + mTriangles16 = v.mTriangles16; + mRemap = v.mRemap; + v.reset(); +} + +void SourceMesh::remapTopology(const PxU32* order) +{ + if(!mNbTris) + return; + + if(mTriangles32) + { + IndTri32* newTopo = PX_NEW(IndTri32)[mNbTris]; + for(PxU32 i=0;i(PX_ALLOC(sizeof(PxU32)*mNbTris, "OPC2")); + for(PxU32 i=0;i(mNbNodes); + else + mNodes = context.readExtraData(mNbNodes); +#else + mNodes = context.readExtraData(mNbNodes); +#endif + } +} +//~PX_SERIALIZATION + +bool BV4Tree::load(PxInputStream& stream, bool mismatch_) +{ + PX_ASSERT(!mUserAllocated); + + release(); + + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if(a!='B' || b!='V' || c!='4' || d!=' ') + return false; + + bool mismatch; + PxU32 fileVersion; + if(!readBigEndianVersionNumber(stream, mismatch_, fileVersion, mismatch)) + return false; + + readFloatBuffer(&mLocalBounds.mCenter.x, 3, mismatch, stream); + mLocalBounds.mExtentsMagnitude = readFloat(mismatch, stream); + + mInitData = readDword(mismatch, stream); + + readFloatBuffer(&mCenterOrMinCoeff.x, 3, mismatch, stream); + readFloatBuffer(&mExtentsOrMaxCoeff.x, 3, mismatch, stream); + + // PT: version 3 + if(fileVersion>=3) + { + const PxU32 Quantized = readDword(mismatch, stream); + mQuantized = Quantized!=0; + } + else + mQuantized = true; + + const PxU32 nbNodes = readDword(mismatch, stream); + mNbNodes = nbNodes; + + if(nbNodes) + { + PxU32 dataSize = 0; +#ifdef GU_BV4_USE_SLABS + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + const PxU32 nodeSize = mQuantized ? sizeof(BVDataPackedQ) : sizeof(BVDataPackedNQ); + #else + const PxU32 nodeSize = sizeof(BVDataPackedQ); + #endif + dataSize = nodeSize*nbNodes; + void* nodes = PX_ALLOC(dataSize, "BV4 nodes"); // PT: PX_NEW breaks alignment here +// BVDataPacked* nodes = reinterpret_cast(PX_ALLOC(sizeof(BVDataPacked)*nbNodes, "BV4 nodes")); // PT: PX_NEW breaks alignment here + mNodes = nodes; +#else + BVDataPacked* nodes = PX_NEW(BVDataPacked)[nbNodes]; + mNodes = nodes; +#endif +// Cm::markSerializedMem(nodes, dataSize); + + stream.read(nodes, dataSize); + PX_ASSERT(!mismatch); + } + else mNodes = NULL; + + return true; +} + +void BV4Tree::refit(float epsilon) +{ + if(mQuantized) + return; + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + // PT: TODO: SIMD + const PxVec3 eps(epsilon); + PX_ASSERT(!(mNbNodes&3)); + PxU32 nb = mNbNodes/4; + BVDataSwizzledNQ* data = reinterpret_cast(mNodes); + while(nb--) + { + BVDataSwizzledNQ* current = data + nb; + + for(PxU32 j=0;j<4;j++) + { + if(current->getChildData(j)==PX_INVALID_U32) + continue; + + PxBounds3 refitBox; + refitBox.setEmpty(); + + if(current->isLeaf(j)) + { + PxU32 primIndex = current->getPrimitive(j); + + PxU32 nbToGo = getNbPrimitives(primIndex); + VertexPointers VP; + do + { + PX_ASSERT(primIndexgetNbTriangles()); + mMeshInterface->getTriangle(VP, primIndex); + + refitBox.include(*VP.Vertex[0]); + refitBox.include(*VP.Vertex[1]); + refitBox.include(*VP.Vertex[2]); + + primIndex++; + }while(nbToGo--); + } + else + { + PxU32 childOffset = current->getChildOffset(j); + PX_ASSERT(!(childOffset&3)); + childOffset>>=2; + PX_ASSERT(childOffset>nb); + const PxU32 childType = current->getChildType(j); + + const BVDataSwizzledNQ* next = data + childOffset; + { + if(childType>1) + { + const PxBounds3 childBox( PxVec3(next->mMinX[3], next->mMinY[3], next->mMinZ[3]), + PxVec3(next->mMaxX[3], next->mMaxY[3], next->mMaxZ[3])); + refitBox.include(childBox); + } + + if(childType>0) + { + const PxBounds3 childBox( PxVec3(next->mMinX[2], next->mMinY[2], next->mMinZ[2]), + PxVec3(next->mMaxX[2], next->mMaxY[2], next->mMaxZ[2])); + refitBox.include(childBox); + } + + { + const PxBounds3 childBox( PxVec3(next->mMinX[1], next->mMinY[1], next->mMinZ[1]), + PxVec3(next->mMaxX[1], next->mMaxY[1], next->mMaxZ[1])); + refitBox.include(childBox); + } + + { + const PxBounds3 childBox( PxVec3(next->mMinX[0], next->mMinY[0], next->mMinZ[0]), + PxVec3(next->mMaxX[0], next->mMaxY[0], next->mMaxZ[0])); + refitBox.include(childBox); + } + } + } + refitBox.minimum -= eps; + refitBox.maximum += eps; + + current->mMinX[j] = refitBox.minimum.x; + current->mMinY[j] = refitBox.minimum.y; + current->mMinZ[j] = refitBox.minimum.z; + current->mMaxX[j] = refitBox.maximum.x; + current->mMaxY[j] = refitBox.maximum.y; + current->mMaxZ[j] = refitBox.maximum.z; + } + } +#else + PX_UNUSED(epsilon); +#endif +} + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h new file mode 100644 index 000000000..098205a5e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h @@ -0,0 +1,283 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_H +#define GU_BV4_H + +#include "foundation/PxBounds3.h" +#include "PxSerialFramework.h" +#include "PsUserAllocated.h" +#include "GuBV4Settings.h" +#include "GuCenterExtents.h" + +#define V4LoadU_Safe V4LoadU +#define V4LoadA_Safe V4LoadA +#define V4StoreA_Safe V4StoreA +#define V4StoreU_Safe V4StoreU + +namespace physx +{ +namespace Gu +{ + + struct VertexPointers + { + const PxVec3* Vertex[3]; + }; + + class IndTri32 : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE IndTri32() {} + PX_FORCE_INLINE IndTri32(PxU32 r0, PxU32 r1, PxU32 r2) { mRef[0]=r0; mRef[1]=r1; mRef[2]=r2; } + PX_FORCE_INLINE IndTri32(const IndTri32& triangle) + { + mRef[0] = triangle.mRef[0]; + mRef[1] = triangle.mRef[1]; + mRef[2] = triangle.mRef[2]; + } + PX_FORCE_INLINE ~IndTri32() {} + PxU32 mRef[3]; + }; + PX_COMPILE_TIME_ASSERT(sizeof(IndTri32)==12); + + class IndTri16 : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE IndTri16() {} + PX_FORCE_INLINE IndTri16(PxU16 r0, PxU16 r1, PxU16 r2) { mRef[0]=r0; mRef[1]=r1; mRef[2]=r2; } + PX_FORCE_INLINE IndTri16(const IndTri16& triangle) + { + mRef[0] = triangle.mRef[0]; + mRef[1] = triangle.mRef[1]; + mRef[2] = triangle.mRef[2]; + } + PX_FORCE_INLINE ~IndTri16() {} + PxU16 mRef[3]; + }; + PX_COMPILE_TIME_ASSERT(sizeof(IndTri16)==6); + + PX_FORCE_INLINE void getVertexReferences(PxU32& vref0, PxU32& vref1, PxU32& vref2, PxU32 index, const IndTri32* T32, const IndTri16* T16) + { + if(T32) + { + const IndTri32* PX_RESTRICT tri = T32 + index; + vref0 = tri->mRef[0]; + vref1 = tri->mRef[1]; + vref2 = tri->mRef[2]; + } + else + { + const IndTri16* PX_RESTRICT tri = T16 + index; + vref0 = tri->mRef[0]; + vref1 = tri->mRef[1]; + vref2 = tri->mRef[2]; + } + } + + + class SourceMeshBase : public physx::shdfnd::UserAllocated + { + public: + PX_PHYSX_COMMON_API SourceMeshBase(); + PX_PHYSX_COMMON_API ~SourceMeshBase(); + + SourceMeshBase(const PxEMPTY) {} + + PxU32 mNbVerts; + const PxVec3* mVerts; + + PX_FORCE_INLINE PxU32 getNbVertices() const { return mNbVerts; } + PX_FORCE_INLINE const PxVec3* getVerts() const { return mVerts; } + + PX_FORCE_INLINE void setNbVertices(PxU32 nb) { mNbVerts = nb; } + + PX_FORCE_INLINE void initRemap() { mRemap = NULL; } + PX_FORCE_INLINE const PxU32* getRemap() const { return mRemap; } + PX_FORCE_INLINE void releaseRemap() { PX_FREE_AND_RESET(mRemap); } + + protected: + PxU32* mRemap; + + }; + + class SourceMesh : public SourceMeshBase + { + public: + PX_PHYSX_COMMON_API SourceMesh(); + PX_PHYSX_COMMON_API ~SourceMesh(); + // PX_SERIALIZATION + SourceMesh(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + void reset(); + void operator = (SourceMesh& v); + + PxU32 mNbTris; + IndTri32* mTriangles32; + IndTri16* mTriangles16; + + PX_FORCE_INLINE PxU32 getNbTriangles() const { return mNbTris; } + //PX_FORCE_INLINE PxU32 getNbVertices() const { return mNbVerts; } + PX_FORCE_INLINE const IndTri32* getTris32() const { return mTriangles32; } + PX_FORCE_INLINE const IndTri16* getTris16() const { return mTriangles16; } + //PX_FORCE_INLINE const PxVec3* getVerts() const { return mVerts; } + + PX_FORCE_INLINE void setNbTriangles(PxU32 nb) { mNbTris = nb; } + //PX_FORCE_INLINE void setNbVertices(PxU32 nb) { mNbVerts = nb; } + + PX_FORCE_INLINE void setPointers(IndTri32* tris32, IndTri16* tris16, const PxVec3* verts) + { + mTriangles32 = tris32; + mTriangles16 = tris16; + mVerts = verts; + } + + void remapTopology(const PxU32* order); + + bool isValid() const; + + PX_FORCE_INLINE void getTriangle(VertexPointers& vp, PxU32 index) const + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, index, mTriangles32, mTriangles16); + vp.Vertex[0] = mVerts + VRef0; + vp.Vertex[1] = mVerts + VRef1; + vp.Vertex[2] = mVerts + VRef2; + } + + }; + + struct LocalBounds + { + // PX_SERIALIZATION + LocalBounds(const PxEMPTY) {} + //~PX_SERIALIZATION + LocalBounds() : mCenter(PxVec3(0.0f)), mExtentsMagnitude(0.0f) {} + + PxVec3 mCenter; + float mExtentsMagnitude; + + PX_FORCE_INLINE void init(const PxBounds3& bounds) + { + mCenter = bounds.getCenter(); + // PT: TODO: compute mag first, then multiplies by 0.5f (TA34704) + mExtentsMagnitude = bounds.getExtents().magnitude(); + } + }; + + class QuantizedAABB + { + public: + + struct Data + { + PxU16 mExtents; //!< Quantized extents + PxI16 mCenter; //!< Quantized center + }; + Data mData[3]; + }; + PX_COMPILE_TIME_ASSERT(sizeof(QuantizedAABB)==12); + + ///// + + #define GU_BV4_CHILD_OFFSET_SHIFT_COUNT 11 + static PX_FORCE_INLINE PxU32 getChildOffset(PxU32 data) { return data>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + static PX_FORCE_INLINE PxU32 getChildType(PxU32 data) { return (data>>1)&3; } + + template + struct BVDataPackedT : public physx::shdfnd::UserAllocated + { + BoxType mAABB; + PxU32 mData; + + PX_FORCE_INLINE PxU32 isLeaf() const { return mData&1; } + PX_FORCE_INLINE PxU32 getPrimitive() const { return mData>>1; } + PX_FORCE_INLINE PxU32 getChildOffset() const { return mData>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT;} + PX_FORCE_INLINE PxU32 getChildType() const { return (mData>>1)&3; } + PX_FORCE_INLINE PxU32 getChildData() const { return mData; } + + PX_FORCE_INLINE void encodePNS(PxU32 code) + { + PX_ASSERT(code<256); + mData |= code<<3; + } + PX_FORCE_INLINE PxU32 decodePNSNoShift() const { return mData; } + }; + + typedef BVDataPackedT BVDataPackedQ; +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + typedef BVDataPackedT BVDataPackedNQ; +#endif + + // PT: TODO: align class to 16? (TA34704) + class BV4Tree : public physx::shdfnd::UserAllocated + { + public: + // PX_SERIALIZATION + BV4Tree(const PxEMPTY); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + PX_PHYSX_COMMON_API BV4Tree(); + PX_PHYSX_COMMON_API BV4Tree(SourceMesh* meshInterface, const PxBounds3& localBounds); + PX_PHYSX_COMMON_API ~BV4Tree(); + + bool load(PxInputStream& stream, bool mismatch); + + void reset(); + void operator = (BV4Tree& v); + + bool init(SourceMesh* meshInterface, const PxBounds3& localBounds); + void release(); + + void refit(float epsilon); + + // PT: TODO: use a single pointer to SourceMeshBase + SourceMesh* mMeshInterface; + LocalBounds mLocalBounds; + + PxU32 mNbNodes; + void* mNodes; // PT: BVDataPacked / BVDataSwizzled + PxU32 mInitData; + // PT: the dequantization coeffs are only used for quantized trees + PxVec3 mCenterOrMinCoeff; // PT: dequantization coeff, either for Center or Min (depending on AABB format) + PxVec3 mExtentsOrMaxCoeff; // PT: dequantization coeff, either for Extents or Max (depending on AABB format) + bool mUserAllocated; // PT: please keep these 4 bytes right after mCenterOrMinCoeff/mExtentsOrMaxCoeff for safe V4 loading + bool mQuantized; // PT: true for quantized trees + bool mPadding[2]; + }; + +} // namespace Gu +} + +#endif // GU_BV4_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp new file mode 100644 index 000000000..6cf6efabc --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp @@ -0,0 +1,1503 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec4.h" +#include "foundation/PxMemory.h" +#include "GuBV4Build.h" +#include "GuBV4.h" +#include "PxTriangle.h" +#include "CmPhysXCommon.h" +#include "PsBasicTemplates.h" +#include "GuCenterExtents.h" +#include + +using namespace physx; +using namespace Gu; + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#define GU_BV4_USE_NODE_POOLS + +#define DELETESINGLE(x) if (x) { delete x; x = NULL; } +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +static PX_FORCE_INLINE PxU32 largestAxis(const PxVec4& v) +{ + const float* Vals = &v.x; + PxU32 m = 0; + if(Vals[1] > Vals[m]) m = 1; + if(Vals[2] > Vals[m]) m = 2; + return m; +} + +AABBTree::AABBTree() : mIndices(NULL), mPool(NULL), mTotalNbNodes(0) +{ +} + +AABBTree::~AABBTree() +{ + release(); +} + +void AABBTree::release() +{ + DELETEARRAY(mPool); + PX_FREE_AND_RESET(mIndices); +} + +static PxU32 local_Split(const AABBTreeNode* PX_RESTRICT node, const PxBounds3* PX_RESTRICT /*Boxes*/, const PxVec3* PX_RESTRICT centers, PxU32 axis) +{ + const PxU32 nb = node->mNbPrimitives; + PxU32* PX_RESTRICT prims = node->mNodePrimitives; + + // Get node split value + const float splitValue = node->mBV.getCenter(axis); + + PxU32 nbPos = 0; + // Loop through all node-related primitives. Their indices range from mNodePrimitives[0] to mNodePrimitives[mNbPrimitives-1]. + // Those indices map the global list in the tree builder. + const size_t ptrValue = size_t(centers) + axis*sizeof(float); + const PxVec3* PX_RESTRICT centersX = reinterpret_cast(ptrValue); + + for(PxU32 i=0;i splitValue) + { + // Swap entries + prims[i] = prims[nbPos]; + prims[nbPos] = index; + // Count primitives assigned to positive space + nbPos++; + } + } + return nbPos; +} + +static bool local_Subdivide(AABBTreeNode* PX_RESTRICT node, const PxBounds3* PX_RESTRICT boxes, const PxVec3* PX_RESTRICT centers, BuildStats& stats, const AABBTreeNode* const PX_RESTRICT node_base, PxU32 limit) +{ + const PxU32* PX_RESTRICT prims = node->mNodePrimitives; + const PxU32 nb = node->mNbPrimitives; + + // Compute bv & means at the same time + Vec4V meansV; + { + Vec4V minV = V4LoadU(&boxes[prims[0]].minimum.x); + Vec4V maxV = V4LoadU(&boxes[prims[0]].maximum.x); + meansV = V4LoadU(¢ers[prims[0]].x); + + for(PxU32 i=1;imBV.minimum = PxVec3(mergedMin.x, mergedMin.y, mergedMin.z); + node->mBV.maximum = PxVec3(mergedMax.x, mergedMax.y, mergedMax.z); + } + +// // Stop subdividing if we reach a leaf node. This is always performed here, +// // else we could end in trouble if user overrides this. +// if(nb==1) +// return false; + if(nb<=limit) + return false; + + bool validSplit = true; + PxU32 nbPos; + { + // Compute variances + Vec4V varsV = V4Zero(); + for(PxU32 i=0;ilimit) + { + nbPos = node->mNbPrimitives>>1; + + if(1) + { + // Test 3 axis, take the best + float results[3]; + nbPos = local_Split(node, boxes, centers, 0); results[0] = float(nbPos)/float(node->mNbPrimitives); + nbPos = local_Split(node, boxes, centers, 1); results[1] = float(nbPos)/float(node->mNbPrimitives); + nbPos = local_Split(node, boxes, centers, 2); results[2] = float(nbPos)/float(node->mNbPrimitives); + results[0]-=0.5f; results[0]*=results[0]; + results[1]-=0.5f; results[1]*=results[1]; + results[2]-=0.5f; results[2]*=results[2]; + PxU32 Min=0; + if(results[1]mNbPrimitives) + nbPos = node->mNbPrimitives>>1; + } + } + //else return + } + + // Now create children and assign their pointers. + // We use a pre-allocated linear pool for complete trees [Opcode 1.3] + const PxU32 count = stats.getCount(); + node->mPos = size_t(node_base + count); + + // Update stats + stats.increaseCount(2); + + // Assign children + AABBTreeNode* pos = const_cast(node->getPos()); + AABBTreeNode* neg = const_cast(node->getNeg()); + pos->mNodePrimitives = node->mNodePrimitives; + pos->mNbPrimitives = nbPos; + neg->mNodePrimitives = node->mNodePrimitives + nbPos; + neg->mNbPrimitives = node->mNbPrimitives - nbPos; + return true; +} + +static void local_BuildHierarchy(AABBTreeNode* PX_RESTRICT node, const PxBounds3* PX_RESTRICT Boxes, const PxVec3* PX_RESTRICT centers, BuildStats& stats, const AABBTreeNode* const PX_RESTRICT node_base, PxU32 limit) +{ + if(local_Subdivide(node, Boxes, centers, stats, node_base, limit)) + { + AABBTreeNode* pos = const_cast(node->getPos()); + AABBTreeNode* neg = const_cast(node->getNeg()); + local_BuildHierarchy(pos, Boxes, centers, stats, node_base, limit); + local_BuildHierarchy(neg, Boxes, centers, stats, node_base, limit); + } +} + +bool AABBTree::buildFromMesh(SourceMesh& mesh, PxU32 limit) +{ + const PxU32 nbBoxes = mesh.getNbTriangles(); + if(!nbBoxes) + return false; + PxBounds3* boxes = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(nbBoxes+1), "BV4")); // PT: +1 to safely V4Load/V4Store the last element + PxVec3* centers = reinterpret_cast(PX_ALLOC(sizeof(PxVec3)*(nbBoxes+1), "BV4")); // PT: +1 to safely V4Load/V4Store the last element + const FloatV halfV = FLoad(0.5f); + for(PxU32 i=0;ix); + const Vec4V v1V = V4LoadU(&VP.Vertex[1]->x); + const Vec4V v2V = V4LoadU(&VP.Vertex[2]->x); + Vec4V minV = V4Min(v0V, v1V); + minV = V4Min(minV, v2V); + Vec4V maxV = V4Max(v0V, v1V); + maxV = V4Max(maxV, v2V); + V4StoreU_Safe(minV, &boxes[i].minimum.x); // PT: safe because 'maximum' follows 'minimum' + V4StoreU_Safe(maxV, &boxes[i].maximum.x); // PT: safe because we allocated one more box + + const Vec4V centerV = V4Scale(V4Add(maxV, minV), halfV); + V4StoreU_Safe(centerV, ¢ers[i].x); // PT: safe because we allocated one more PxVec3 + } + + { + // Release previous tree + release(); + + // Init stats + BuildStats Stats; + Stats.setCount(1); + + // Initialize indices. This list will be modified during build. + mIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbBoxes, "BV4 indices")); + // Identity permutation + for(PxU32 i=0;imNodePrimitives = mIndices; + mPool->mNbPrimitives = nbBoxes; + + // Build the hierarchy + local_BuildHierarchy(mPool, boxes, centers, Stats, mPool, limit); + + // Get back total number of nodes + mTotalNbNodes = Stats.getCount(); + } + + PX_FREE(centers); + PX_FREE(boxes); + return true; +} + +PxU32 AABBTree::walk(WalkingCallback cb, void* userData) const +{ + // Call it without callback to compute max depth + PxU32 maxDepth = 0; + PxU32 currentDepth = 0; + + struct Local + { + static void _Walk(const AABBTreeNode* current_node, PxU32& max_depth, PxU32& current_depth, WalkingCallback callback, void* userData_) + { + // Checkings + if(!current_node) + return; + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if(current_depth>max_depth) + max_depth = current_depth; + + // Callback + if(callback && !(callback)(current_node, current_depth, userData_)) + return; + + // Recurse + if(current_node->getPos()) { _Walk(current_node->getPos(), max_depth, current_depth, callback, userData_); current_depth--; } + if(current_node->getNeg()) { _Walk(current_node->getNeg(), max_depth, current_depth, callback, userData_); current_depth--; } + } + }; + Local::_Walk(mPool, maxDepth, currentDepth, cb, userData); + return maxDepth; +} + + + + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT +// PT: see http://www.codercorner.com/blog/?p=734 +static PxU32 precomputeNodeSorting(const PxBounds3& box0, const PxBounds3& box1) +{ + const PxVec3 C0 = box0.getCenter(); + const PxVec3 C1 = box1.getCenter(); + + PxVec3 dirPPP(1.0f, 1.0f, 1.0f); dirPPP.normalize(); + PxVec3 dirPPN(1.0f, 1.0f, -1.0f); dirPPN.normalize(); + PxVec3 dirPNP(1.0f, -1.0f, 1.0f); dirPNP.normalize(); + PxVec3 dirPNN(1.0f, -1.0f, -1.0f); dirPNN.normalize(); + PxVec3 dirNPP(-1.0f, 1.0f, 1.0f); dirNPP.normalize(); + PxVec3 dirNPN(-1.0f, 1.0f, -1.0f); dirNPN.normalize(); + PxVec3 dirNNP(-1.0f, -1.0f, 1.0f); dirNNP.normalize(); + PxVec3 dirNNN(-1.0f, -1.0f, -1.0f); dirNNN.normalize(); + + const PxVec3 deltaC = C0 - C1; + const bool bPPP = deltaC.dot(dirPPP)<0.0f; + const bool bPPN = deltaC.dot(dirPPN)<0.0f; + const bool bPNP = deltaC.dot(dirPNP)<0.0f; + const bool bPNN = deltaC.dot(dirPNN)<0.0f; + const bool bNPP = deltaC.dot(dirNPP)<0.0f; + const bool bNPN = deltaC.dot(dirNPN)<0.0f; + const bool bNNP = deltaC.dot(dirNNP)<0.0f; + const bool bNNN = deltaC.dot(dirNNN)<0.0f; + + PxU32 code = 0; + if(!bPPP) + code |= (1<<7); // Bit 0: PPP + if(!bPPN) + code |= (1<<6); // Bit 1: PPN + if(!bPNP) + code |= (1<<5); // Bit 2: PNP + if(!bPNN) + code |= (1<<4); // Bit 3: PNN + if(!bNPP) + code |= (1<<3); // Bit 4: NPP + if(!bNPN) + code |= (1<<2); // Bit 5: NPN + if(!bNNP) + code |= (1<<1); // Bit 6: NNP + if(!bNNN) + code |= (1<<0); // Bit 7: NNN + return code; +} +#endif + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Common.h" +#endif + +static void setEmpty(CenterExtents& box) +{ + box.mCenter = PxVec3(0.0f, 0.0f, 0.0f); + box.mExtents = PxVec3(-1.0f, -1.0f, -1.0f); +} + +// Data: +// 1 bit for leaf/no leaf +// 2 bits for child-node type +// 8 bits for PNS +// => 32 - 1 - 2 - 8 = 21 bits left for encoding triangle index or node *offset* +// => limited to 2.097.152 triangles +// => and 2Mb-large trees (this one may not work out well in practice) +// ==> lines marked with //* have been changed to address this. Now we don't store offsets in bytes directly +// but in BVData indices. There's more work at runtime calculating addresses, but now the format can support +// 2 million single nodes. +// +// That being said we only need 3*8 = 24 bits in total, so that could be only 6 bits in each BVData. +// For type0: we have 2 nodes, we need 8 bits => 6 bits/node = 12 bits available, ok +// For type1: we have 3 nodes, we need 8*2 = 16 bits => 6 bits/node = 18 bits available, ok +// For type2: we have 4 nodes, we need 8*3 = 24 bits => 6 bits/node = 24 bits available, ok +//#pragma pack(1) +struct BVData : public physx::shdfnd::UserAllocated +{ + BVData(); + CenterExtents mAABB; + size_t mData; +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + PxU32 mTempPNS; +#endif +}; +//#pragma pack() + +BVData::BVData() : mData(PX_INVALID_U32) +{ + setEmpty(mAABB); +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + mTempPNS = 0; +#endif +} + +struct BV4Node : public physx::shdfnd::UserAllocated +{ + PX_FORCE_INLINE BV4Node() {} + PX_FORCE_INLINE ~BV4Node() {} + + BVData mBVData[4]; + + PX_FORCE_INLINE size_t isLeaf(PxU32 i) const { return mBVData[i].mData&1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return PxU32(mBVData[i].mData>>1); } + PX_FORCE_INLINE const BV4Node* getChild(PxU32 i) const { return reinterpret_cast(mBVData[i].mData); } + + PxU32 getType() const + { + PxU32 Nb=0; + for(PxU32 i=0;i<4;i++) + { + if(mBVData[i].mData!=PX_INVALID_U32) + Nb++; + } + return Nb; + } + + PxU32 getSize() const + { + const PxU32 type = getType(); + return sizeof(BVData)*type; + } +}; + +#define NB_NODES_PER_SLAB 256 +struct BV4BuildParams +{ + PX_FORCE_INLINE BV4BuildParams(float epsilon) : mEpsilon(epsilon) +#ifdef GU_BV4_USE_NODE_POOLS + ,mTop(NULL) +#endif + {} + ~BV4BuildParams(); + + // Stats + PxU32 mNbNodes; + PxU32 mStats[4]; + + // + float mEpsilon; + +#ifdef GU_BV4_USE_NODE_POOLS + // + struct Slab : public physx::shdfnd::UserAllocated + { + BV4Node mNodes[NB_NODES_PER_SLAB]; + PxU32 mNbUsedNodes; + Slab* mNext; + }; + Slab* mTop; + + BV4Node* allocateNode(); + void releaseNodes(); +#endif +}; + +BV4BuildParams::~BV4BuildParams() +{ +#ifdef GU_BV4_USE_NODE_POOLS + releaseNodes(); +#endif +} + +#ifdef GU_BV4_USE_NODE_POOLS +BV4Node* BV4BuildParams::allocateNode() +{ + if(!mTop || mTop->mNbUsedNodes==NB_NODES_PER_SLAB) + { + Slab* newSlab = PX_NEW(Slab); + newSlab->mNbUsedNodes = 0; + newSlab->mNext = mTop; + mTop = newSlab; + } + return &mTop->mNodes[mTop->mNbUsedNodes++]; +} + +void BV4BuildParams::releaseNodes() +{ + Slab* current = mTop; + while(current) + { + Slab* next = current->mNext; + PX_DELETE(current); + current = next; + } + mTop = NULL; +} +#endif + +static void setPrimitive(const AABBTree& source, BV4Node* node4, PxU32 i, const AABBTreeNode* node, float epsilon) +{ + const PxU32 nbPrims = node->getNbPrimitives(); + PX_ASSERT(nbPrims<16); + const PxU32* indexBase = source.getIndices(); + const PxU32* prims = node->getPrimitives(); + const PxU32 offset = PxU32(prims - indexBase); + for(PxU32 j=0;jmBVData[i].mAABB = node->getAABB(); + if(epsilon!=0.0f) + node4->mBVData[i].mAABB.mExtents += PxVec3(epsilon); + node4->mBVData[i].mData = (primitiveIndex<<1)|1; +} + +static BV4Node* setNode(const AABBTree& source, BV4Node* node4, PxU32 i, const AABBTreeNode* node, BV4BuildParams& params) +{ + BV4Node* child = NULL; + if(node->isLeaf()) + { + setPrimitive(source, node4, i, node, params.mEpsilon); + } + else + { + node4->mBVData[i].mAABB = node->getAABB(); + if(params.mEpsilon!=0.0f) + node4->mBVData[i].mAABB.mExtents += PxVec3(params.mEpsilon); + + params.mNbNodes++; +#ifdef GU_BV4_USE_NODE_POOLS + child = params.allocateNode(); +#else + child = PX_NEW(BV4Node); +#endif + node4->mBVData[i].mData = size_t(child); + } + return child; +} + +static void _BuildBV4(const AABBTree& source, BV4Node* tmp, const AABBTreeNode* current_node, BV4BuildParams& params) +{ + PX_ASSERT(!current_node->isLeaf()); + + // In the regular tree we have current node A, and: + // ____A____ + // P N + // __|__ __|__ + // PP PN NP NN + // + // For PNS we have: + // bit0 to sort P|N + // bit1 to sort PP|PN + // bit2 to sort NP|NN + // + // As much as possible we need to preserve the original order in BV4, if we want to reuse the same PNS bits. + // + // bit0|bit1|bit2 Order 8bits code + // 0 0 0 PP PN NP NN 0 1 2 3 + // 0 0 1 PP PN NN NP 0 1 3 2 + // 0 1 0 PN PP NP NN 1 0 2 3 + // 0 1 1 PN PP NN NP 1 0 3 2 + // 1 0 0 NP NN PP PN 2 3 0 1 + // 1 0 1 NN NP PP PN 3 2 0 1 + // 1 1 0 NP NN PN PP 2 3 1 0 + // 1 1 1 NN NP PN PP 3 2 1 0 + // + // So we can fetch/compute the sequence from the bits, combine it with limitations from the node type, and process the nodes in order. In theory. + // 8*8bits => the whole thing fits in a single 64bit register, so we could potentially use a "register LUT" here. + + const AABBTreeNode* P = current_node->getPos(); + const AABBTreeNode* N = current_node->getNeg(); + + const bool PLeaf = P->isLeaf(); + const bool NLeaf = N->isLeaf(); + + if(PLeaf) + { + if(NLeaf) + { + // Case 1: P and N are both leaves: + // ____A____ + // P N + // => store as (P,N) and keep bit0 + params.mStats[0]++; + // PN leaves => store 2 triangle pointers, lose 50% of node space + setPrimitive(source, tmp, 0, P, params.mEpsilon); + setPrimitive(source, tmp, 1, N, params.mEpsilon); + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); +#endif + } + else + { + // Case 2: P leaf, N no leaf + // ____A____ + // P N + // __|__ + // NP NN + // => store as (P,NP,NN), keep bit0 and bit2 + params.mStats[1]++; + // P leaf => store 1 triangle pointers and 2 node pointers + // => 3 slots used, 25% wasted + setPrimitive(source, tmp, 0, P, params.mEpsilon); + + // + + const AABBTreeNode* NP = N->getPos(); + const AABBTreeNode* NN = N->getNeg(); + +//#define NODE_FUSION +#ifdef NODE_FUSION + PxU32 c=0; + BV4Node* ChildNP; + if(!NP->isLeaf() && NP->getPos()->isLeaf() && NP->getNeg()->isLeaf()) + { + // Drag the terminal leaves directly into this BV4 node, drop internal node NP + setPrimitive(source, tmp, 1, NP->getPos(), params.mEpsilon); + setPrimitive(source, tmp, 2, NP->getNeg(), params.mEpsilon); + ChildNP = NULL; + params.mStats[1]--; + params.mStats[3]++; + c=1; + } + else + { + ChildNP = setNode(source, tmp, 1, NP, params); + } + + BV4Node* ChildNN; + if(c==0 && !NN->isLeaf() && NN->getPos()->isLeaf() && NN->getNeg()->isLeaf()) + { + // Drag the terminal leaves directly into this BV4 node, drop internal node NN + setPrimitive(source, tmp, 2, NN->getPos(), params.mEpsilon); + setPrimitive(source, tmp, 3, NN->getNeg(), params.mEpsilon); + ChildNN = NULL; + params.mStats[1]--; + params.mStats[3]++; + } + else + { + ChildNN = setNode(source, tmp, 2+c, NN, params); + } + + //BV4Node* ChildNN = setNode(tmp, 2+c, NN, epsilon, params); +#else + BV4Node* ChildNP = setNode(source, tmp, 1, NP, params); + BV4Node* ChildNN = setNode(source, tmp, 2, NN, params); +#endif + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); + tmp->mBVData[2].mTempPNS = precomputeNodeSorting(NP->mBV, NN->mBV); +#endif + if(ChildNP) + _BuildBV4(source, ChildNP, NP, params); + if(ChildNN) + _BuildBV4(source, ChildNN, NN, params); + } + } + else + { + if(NLeaf) + { + // Case 3: P no leaf, N leaf + // ____A____ + // P N + // __|__ + // PP PN + // => store as (PP,PN,N), keep bit0 and bit1 + params.mStats[2]++; + + // N leaf => store 1 triangle pointers and 2 node pointers + // => 3 slots used, 25% wasted + setPrimitive(source, tmp, 2, N, params.mEpsilon); + + // + + const AABBTreeNode* PP = P->getPos(); + const AABBTreeNode* PN = P->getNeg(); + + BV4Node* ChildPP = setNode(source, tmp, 0, PP, params); + BV4Node* ChildPN = setNode(source, tmp, 1, PN, params); + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); + tmp->mBVData[1].mTempPNS = precomputeNodeSorting(PP->mBV, PN->mBV); +#endif + if(ChildPP) + _BuildBV4(source, ChildPP, PP, params); + if(ChildPN) + _BuildBV4(source, ChildPN, PN, params); + } + else + { + // Case 4: P and N are no leaves: + // => store as (PP,PN,NP,NN), keep bit0/bit1/bit2 + params.mStats[3]++; + + // No leaves => store 4 node pointers + const AABBTreeNode* PP = P->getPos(); + const AABBTreeNode* PN = P->getNeg(); + const AABBTreeNode* NP = N->getPos(); + const AABBTreeNode* NN = N->getNeg(); + + BV4Node* ChildPP = setNode(source, tmp, 0, PP, params); + BV4Node* ChildPN = setNode(source, tmp, 1, PN, params); + BV4Node* ChildNP = setNode(source, tmp, 2, NP, params); + BV4Node* ChildNN = setNode(source, tmp, 3, NN, params); + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); + tmp->mBVData[1].mTempPNS = precomputeNodeSorting(PP->mBV, PN->mBV); + tmp->mBVData[2].mTempPNS = precomputeNodeSorting(NP->mBV, NN->mBV); +#endif + if(ChildPP) + _BuildBV4(source, ChildPP, PP, params); + if(ChildPN) + _BuildBV4(source, ChildPN, PN, params); + if(ChildNP) + _BuildBV4(source, ChildNP, NP, params); + if(ChildNN) + _BuildBV4(source, ChildNN, NN, params); + } + } +} + +#ifdef GU_BV4_USE_SLABS +static void _ComputeMaxValues(const BV4Node* current, PxVec3& MinMax, PxVec3& MaxMax) +#else +static void _ComputeMaxValues(const BV4Node* current, PxVec3& CMax, PxVec3& EMax) +#endif +{ + for(PxU32 i=0; i<4; i++) + { + if(current->mBVData[i].mData != PX_INVALID_U32) + { + const CenterExtents& Box = current->mBVData[i].mAABB; +#ifdef GU_BV4_USE_SLABS + const PxVec3 Min = Box.mCenter - Box.mExtents; + const PxVec3 Max = Box.mCenter + Box.mExtents; + if(fabsf(Min.x)>MinMax.x) MinMax.x = fabsf(Min.x); + if(fabsf(Min.y)>MinMax.y) MinMax.y = fabsf(Min.y); + if(fabsf(Min.z)>MinMax.z) MinMax.z = fabsf(Min.z); + if(fabsf(Max.x)>MaxMax.x) MaxMax.x = fabsf(Max.x); + if(fabsf(Max.y)>MaxMax.y) MaxMax.y = fabsf(Max.y); + if(fabsf(Max.z)>MaxMax.z) MaxMax.z = fabsf(Max.z); +#else + if(fabsf(Box.mCenter.x)>CMax.x) CMax.x = fabsf(Box.mCenter.x); + if(fabsf(Box.mCenter.y)>CMax.y) CMax.y = fabsf(Box.mCenter.y); + if(fabsf(Box.mCenter.z)>CMax.z) CMax.z = fabsf(Box.mCenter.z); + if(fabsf(Box.mExtents.x)>EMax.x) EMax.x = fabsf(Box.mExtents.x); + if(fabsf(Box.mExtents.y)>EMax.y) EMax.y = fabsf(Box.mExtents.y); + if(fabsf(Box.mExtents.z)>EMax.z) EMax.z = fabsf(Box.mExtents.z); +#endif + if(!current->isLeaf(i)) + { + const BV4Node* ChildNode = current->getChild(i); +#ifdef GU_BV4_USE_SLABS + _ComputeMaxValues(ChildNode, MinMax, MaxMax); +#else + _ComputeMaxValues(ChildNode, CMax, EMax); +#endif + } + } + } +} + +// PT: duplicated for now.... + +static void _FlattenQ( BVDataPackedQ* const dest, const PxU32 box_id, PxU32& current_id, const BV4Node* current, PxU32& max_depth, PxU32& current_depth, + const PxVec3& CQuantCoeff, const PxVec3& EQuantCoeff, const PxVec3& mCenterCoeff, const PxVec3& mExtentsCoeff) +{ + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if(current_depth>max_depth) + max_depth = current_depth; + +// dest[box_id] = *current; + const PxU32 CurrentType = current->getType(); + for(PxU32 i=0; imBVData[i].mAABB; +#ifdef GU_BV4_USE_SLABS + const PxVec3 m = Box.mCenter - Box.mExtents; + const PxVec3 M = Box.mCenter + Box.mExtents; + + dest[box_id + i].mAABB.mData[0].mCenter = PxI16(m.x * CQuantCoeff.x); + dest[box_id + i].mAABB.mData[1].mCenter = PxI16(m.y * CQuantCoeff.y); + dest[box_id + i].mAABB.mData[2].mCenter = PxI16(m.z * CQuantCoeff.z); + dest[box_id + i].mAABB.mData[0].mExtents = PxU16(PxI16(M.x * EQuantCoeff.x)); + dest[box_id + i].mAABB.mData[1].mExtents = PxU16(PxI16(M.y * EQuantCoeff.y)); + dest[box_id + i].mAABB.mData[2].mExtents = PxU16(PxI16(M.z * EQuantCoeff.z)); + + if (1) + { + for (PxU32 j = 0; j<3; j++) + { + // Dequantize the min/max + // const float qmin = float(dest[box_id+i].mAABB.mData[j].mCenter) * mCenterCoeff[j]; + // const float qmax = float(PxI16(dest[box_id+i].mAABB.mData[j].mExtents)) * mExtentsCoeff[j]; + // Compare real & dequantized values + /* if(qmaxm[j]) + { + int stop=1; + }*/ + bool CanLeave; + do + { + CanLeave = true; + const float qmin = float(dest[box_id + i].mAABB.mData[j].mCenter) * mCenterCoeff[j]; + const float qmax = float(PxI16(dest[box_id + i].mAABB.mData[j].mExtents)) * mExtentsCoeff[j]; + + if (qmaxm[j]) + { + if (dest[box_id + i].mAABB.mData[j].mCenter) + { + dest[box_id + i].mAABB.mData[j].mCenter--; + CanLeave = false; + } + } + } while (!CanLeave); + } + } +#else // GU_BV4_USE_SLABS + dest[box_id + i].mAABB.mData[0].mCenter = PxI16(Box.mCenter.x * CQuantCoeff.x); + dest[box_id + i].mAABB.mData[1].mCenter = PxI16(Box.mCenter.y * CQuantCoeff.y); + dest[box_id + i].mAABB.mData[2].mCenter = PxI16(Box.mCenter.z * CQuantCoeff.z); + dest[box_id + i].mAABB.mData[0].mExtents = PxU16(Box.mExtents.x * EQuantCoeff.x); + dest[box_id + i].mAABB.mData[1].mExtents = PxU16(Box.mExtents.y * EQuantCoeff.y); + dest[box_id + i].mAABB.mData[2].mExtents = PxU16(Box.mExtents.z * EQuantCoeff.z); + + // Fix quantized boxes + if (1) + { + // Make sure the quantized box is still valid + const PxVec3 Max = Box.mCenter + Box.mExtents; + const PxVec3 Min = Box.mCenter - Box.mExtents; + // For each axis + for (PxU32 j = 0; j<3; j++) + { // Dequantize the box center + const float qc = float(dest[box_id + i].mAABB.mData[j].mCenter) * mCenterCoeff[j]; + bool FixMe = true; + do + { // Dequantize the box extent + const float qe = float(dest[box_id + i].mAABB.mData[j].mExtents) * mExtentsCoeff[j]; + // Compare real & dequantized values + if (qc + qeMin[j]) dest[box_id + i].mAABB.mData[j].mExtents++; + else FixMe = false; + // Prevent wrapping + if (!dest[box_id + i].mAABB.mData[j].mExtents) + { + dest[box_id + i].mAABB.mData[j].mExtents = 0xffff; + FixMe = false; + } + } while (FixMe); + } + } +#endif // GU_BV4_USE_SLABS + + dest[box_id + i].mData = PxU32(current->mBVData[i].mData); +// dest[box_id+i].encodePNS(current->mBVData[i].mTempPNS); + } + + PxU32 NbToGo = 0; + PxU32 NextIDs[4] = { PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32 }; + const BV4Node* ChildNodes[4] = { NULL,NULL,NULL,NULL }; + + BVDataPackedQ* data = dest + box_id; + for(PxU32 i=0; i<4; i++) + { + if(current->mBVData[i].mData != PX_INVALID_U32 && !current->isLeaf(i)) + { + const BV4Node* ChildNode = current->getChild(i); + + const PxU32 NextID = current_id; +#ifdef GU_BV4_USE_SLABS + current_id += 4; +#else + const PxU32 ChildSize = ChildNode->getType(); + current_id += ChildSize; +#endif + const PxU32 ChildType = (ChildNode->getType() - 2) << 1; + data[i].mData = size_t(ChildType + (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + //PX_ASSERT(data[i].mData == size_t(ChildType+(NextID<<3))); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = ChildNode; + NbToGo++; + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + data[i].encodePNS(current->mBVData[i].mTempPNS); +#endif +//#define DEPTH_FIRST +#ifdef DEPTH_FIRST + _Flatten(dest, NextID, current_id, ChildNode, max_depth, current_depth, CQuantCoeff, EQuantCoeff, mCenterCoeff, mExtentsCoeff, quantized); + current_depth--; +#endif + } +#ifdef GU_BV4_USE_SLABS + if (current->mBVData[i].mData == PX_INVALID_U32) + { + data[i].mAABB.mData[0].mExtents = 0; + data[i].mAABB.mData[1].mExtents = 0; + data[i].mAABB.mData[2].mExtents = 0; + data[i].mAABB.mData[0].mCenter = 0; + data[i].mAABB.mData[1].mCenter = 0; + data[i].mAABB.mData[2].mCenter = 0; + data[i].mData = PX_INVALID_U32; + } +#endif + } + +#ifndef DEPTH_FIRST + for(PxU32 i=0; i increase depth + current_depth++; + // Keep track of max depth + if(current_depth>max_depth) + max_depth = current_depth; + +// dest[box_id] = *current; + const PxU32 CurrentType = current->getType(); + for(PxU32 i=0; imBVData[i].mAABB; + dest[box_id + i].mAABB.mCenter = Box.mCenter - Box.mExtents; + dest[box_id + i].mAABB.mExtents = Box.mCenter + Box.mExtents; +#else // GU_BV4_USE_SLABS + dest[box_id + i].mAABB = current->mBVData[i].mAABB; +#endif // GU_BV4_USE_SLABS + + dest[box_id + i].mData = PxU32(current->mBVData[i].mData); +// dest[box_id+i].encodePNS(current->mBVData[i].mTempPNS); + } + + PxU32 NbToGo = 0; + PxU32 NextIDs[4] = { PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32 }; + const BV4Node* ChildNodes[4] = { NULL,NULL,NULL,NULL }; + + BVDataPackedNQ* data = dest + box_id; + for(PxU32 i=0; i<4; i++) + { + if(current->mBVData[i].mData != PX_INVALID_U32 && !current->isLeaf(i)) + { + const BV4Node* ChildNode = current->getChild(i); + + const PxU32 NextID = current_id; +#ifdef GU_BV4_USE_SLABS + current_id += 4; +#else + const PxU32 ChildSize = ChildNode->getType(); + current_id += ChildSize; +#endif + const PxU32 ChildType = (ChildNode->getType() - 2) << 1; + data[i].mData = size_t(ChildType + (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + //PX_ASSERT(data[i].mData == size_t(ChildType+(NextID<<3))); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = ChildNode; + NbToGo++; + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + data[i].encodePNS(current->mBVData[i].mTempPNS); +#endif +//#define DEPTH_FIRST +#ifdef DEPTH_FIRST + _Flatten(dest, NextID, current_id, ChildNode, max_depth, current_depth, CQuantCoeff, EQuantCoeff, mCenterCoeff, mExtentsCoeff, quantized); + current_depth--; +#endif + } +#ifdef GU_BV4_USE_SLABS + if (current->mBVData[i].mData == PX_INVALID_U32) + { + data[i].mAABB.mCenter = PxVec3(0.0f); + data[i].mAABB.mExtents = PxVec3(0.0f); + data[i].mData = PX_INVALID_U32; + } +#endif + } + +#ifndef DEPTH_FIRST + for(PxU32 i=0; imQuantized = quantized; +#else + T->mQuantized = true; +#endif + + // Version with variable-sized nodes in single stream + { + const PxU32 NbSingleNodes = Params.mStats[0] * 2 + (Params.mStats[1] + Params.mStats[2]) * 3 + Params.mStats[3] * 4; + + PxU32 CurID = Root->getType(); + PxU32 InitData = PX_INVALID_U32; +#ifdef GU_BV4_USE_SLABS + PX_UNUSED(NbSingleNodes); + const PxU32 NbNeeded = (Params.mStats[0] + Params.mStats[1] + Params.mStats[2] + Params.mStats[3]) * 4; + + // PT: TODO: refactor with code in BV4Tree::load +// BVDataPacked* Nodes = reinterpret_cast(PX_ALLOC(sizeof(BVDataPacked)*NbNeeded, "BV4 nodes")); // PT: PX_NEW breaks alignment here +// BVDataPacked* Nodes = PX_NEW(BVDataPacked)[NbNeeded]; + void* nodes; + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + const PxU32 nodeSize = T->mQuantized ? sizeof(BVDataPackedQ) : sizeof(BVDataPackedNQ); + #else + const PxU32 nodeSize = sizeof(BVDataPackedQ); + #endif + const PxU32 dataSize = nodeSize*NbNeeded; + nodes = PX_ALLOC(dataSize, "BV4 nodes"); // PT: PX_NEW breaks alignment here + } + + if (CurID == 2) + { + InitData = 0; + } + else if (CurID == 3) + { + InitData = 2; + } + else if (CurID == 4) + { + InitData = 4; + } + + CurID = 4; +// PxU32 CurID = 4; +// PxU32 InitData = 4; +#else + BVDataPacked* Nodes = PX_NEW(BVDataPacked)[NbSingleNodes]; + + if (CurID == 2) + { + InitData = 0; + } + else if (CurID == 3) + { + InitData = 2; + } + else if (CurID == 4) + { + InitData = 4; + } +#endif + + T->mInitData = InitData; + PxU32 MaxDepth = 0; + PxU32 CurrentDepth = 0; + + PxVec3 CQuantCoeff(0.0f); + PxVec3 EQuantCoeff(0.0f); + if(T->mQuantized) + { +#ifdef GU_BV4_USE_SLABS + PxVec3 MinQuantCoeff, MaxQuantCoeff; + + // Get max values + PxVec3 MinMax(-FLT_MAX); + PxVec3 MaxMax(-FLT_MAX); + _ComputeMaxValues(Root, MinMax, MaxMax); + + const PxU32 nbm = 15; + + // Compute quantization coeffs + const float MinCoeff = float((1 << nbm) - 1); + const float MaxCoeff = float((1 << nbm) - 1); + MinQuantCoeff.x = MinMax.x != 0.0f ? MinCoeff / MinMax.x : 0.0f; + MinQuantCoeff.y = MinMax.y != 0.0f ? MinCoeff / MinMax.y : 0.0f; + MinQuantCoeff.z = MinMax.z != 0.0f ? MinCoeff / MinMax.z : 0.0f; + MaxQuantCoeff.x = MaxMax.x != 0.0f ? MaxCoeff / MaxMax.x : 0.0f; + MaxQuantCoeff.y = MaxMax.y != 0.0f ? MaxCoeff / MaxMax.y : 0.0f; + MaxQuantCoeff.z = MaxMax.z != 0.0f ? MaxCoeff / MaxMax.z : 0.0f; + // Compute and save dequantization coeffs + T->mCenterOrMinCoeff.x = MinMax.x / MinCoeff; + T->mCenterOrMinCoeff.y = MinMax.y / MinCoeff; + T->mCenterOrMinCoeff.z = MinMax.z / MinCoeff; + T->mExtentsOrMaxCoeff.x = MaxMax.x / MaxCoeff; + T->mExtentsOrMaxCoeff.y = MaxMax.y / MaxCoeff; + T->mExtentsOrMaxCoeff.z = MaxMax.z / MaxCoeff; + + CQuantCoeff = MinQuantCoeff; + EQuantCoeff = MaxQuantCoeff; +#else + // Get max values + PxVec3 CMax(-FLT_MAX); + PxVec3 EMax(-FLT_MAX); + _ComputeMaxValues(Root, CMax, EMax); + + const PxU32 nbc = 15; + const PxU32 nbe = 16; +// const PxU32 nbc=7; +// const PxU32 nbe=8; + + const float UnitQuantError = 2.0f / 65535.0f; + EMax.x += CMax.x*UnitQuantError; + EMax.y += CMax.y*UnitQuantError; + EMax.z += CMax.z*UnitQuantError; + + // Compute quantization coeffs + const float CCoeff = float((1 << nbc) - 1); + CQuantCoeff.x = CMax.x != 0.0f ? CCoeff / CMax.x : 0.0f; + CQuantCoeff.y = CMax.y != 0.0f ? CCoeff / CMax.y : 0.0f; + CQuantCoeff.z = CMax.z != 0.0f ? CCoeff / CMax.z : 0.0f; + const float ECoeff = float((1 << nbe) - 32); + EQuantCoeff.x = EMax.x != 0.0f ? ECoeff / EMax.x : 0.0f; + EQuantCoeff.y = EMax.y != 0.0f ? ECoeff / EMax.y : 0.0f; + EQuantCoeff.z = EMax.z != 0.0f ? ECoeff / EMax.z : 0.0f; + // Compute and save dequantization coeffs + T->mCenterOrMinCoeff.x = CMax.x / CCoeff; + T->mCenterOrMinCoeff.y = CMax.y / CCoeff; + T->mCenterOrMinCoeff.z = CMax.z / CCoeff; + T->mExtentsOrMaxCoeff.x = EMax.x / ECoeff; + T->mExtentsOrMaxCoeff.y = EMax.y / ECoeff; + T->mExtentsOrMaxCoeff.z = EMax.z / ECoeff; +#endif + _FlattenQ(reinterpret_cast(nodes), 0, CurID, Root, MaxDepth, CurrentDepth, CQuantCoeff, EQuantCoeff, T->mCenterOrMinCoeff, T->mExtentsOrMaxCoeff); + } + else + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + _FlattenNQ(reinterpret_cast(nodes), 0, CurID, Root, MaxDepth, CurrentDepth, CQuantCoeff, EQuantCoeff, T->mCenterOrMinCoeff, T->mExtentsOrMaxCoeff); + #else + PX_ASSERT(0); + #endif + } + +#ifdef GU_BV4_USE_NODE_POOLS + Params.releaseNodes(); +#endif + +#ifdef GU_BV4_USE_SLABS + // PT: TODO: revisit this, don't duplicate everything + if(T->mQuantized) + { + BVDataPackedQ* _Nodes = reinterpret_cast(nodes); + + PX_COMPILE_TIME_ASSERT(sizeof(BVDataSwizzledQ) == sizeof(BVDataPackedQ) * 4); + BVDataPackedQ* Copy = PX_NEW(BVDataPackedQ)[NbNeeded]; + PxMemCopy(Copy, nodes, sizeof(BVDataPackedQ)*NbNeeded); + for (PxU32 i = 0; i(_Nodes + i * 4); + for (PxU32 j = 0; j<4; j++) + { + // We previously stored m/M within c/e so we just need to swizzle now + const QuantizedAABB& Box = Src[j].mAABB; + Dst->mX[j].mMin = Box.mData[0].mCenter; + Dst->mY[j].mMin = Box.mData[1].mCenter; + Dst->mZ[j].mMin = Box.mData[2].mCenter; + Dst->mX[j].mMax = PxI16(Box.mData[0].mExtents); + Dst->mY[j].mMax = PxI16(Box.mData[1].mExtents); + Dst->mZ[j].mMax = PxI16(Box.mData[2].mExtents); + Dst->mData[j] = Src[j].mData; + } + } + DELETEARRAY(Copy); + } + else + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + BVDataPackedNQ* _Nodes = reinterpret_cast(nodes); + + PX_COMPILE_TIME_ASSERT(sizeof(BVDataSwizzledNQ) == sizeof(BVDataPackedNQ) * 4); + BVDataPackedNQ* Copy = PX_NEW(BVDataPackedNQ)[NbNeeded]; + PxMemCopy(Copy, nodes, sizeof(BVDataPackedNQ)*NbNeeded); + for (PxU32 i = 0; i(_Nodes + i * 4); + for (PxU32 j = 0; j<4; j++) + { + // We previously stored m/M within c/e so we just need to swizzle now + const CenterExtents& Box = Src[j].mAABB; + Dst->mMinX[j] = Box.mCenter.x; + Dst->mMinY[j] = Box.mCenter.y; + Dst->mMinZ[j] = Box.mCenter.z; + Dst->mMaxX[j] = Box.mExtents.x; + Dst->mMaxY[j] = Box.mExtents.y; + Dst->mMaxZ[j] = Box.mExtents.z; + Dst->mData[j] = Src[j].mData; + } + } + DELETEARRAY(Copy); + + if(0) + { + const PxVec3 eps(epsilon); + float maxError = 0.0f; + PxU32 nb = NbNeeded/4; + BVDataSwizzledNQ* data = reinterpret_cast(nodes); + while(nb--) + { + BVDataSwizzledNQ* current = data + nb; + + for(PxU32 j=0;j<4;j++) + { + if(current->getChildData(j)==PX_INVALID_U32) + continue; + + const PxBounds3 localBox( PxVec3(current->mMinX[j], current->mMinY[j], current->mMinZ[j]), + PxVec3(current->mMaxX[j], current->mMaxY[j], current->mMaxZ[j])); + PxBounds3 refitBox; + refitBox.setEmpty(); + + if(current->isLeaf(j)) + { + PxU32 primIndex = current->getPrimitive(j); + + PxU32 nbToGo = getNbPrimitives(primIndex); + VertexPointers VP; + do + { + PX_ASSERT(primIndexmMeshInterface->getNbTriangles()); + T->mMeshInterface->getTriangle(VP, primIndex); + + refitBox.include(*VP.Vertex[0]); + refitBox.include(*VP.Vertex[1]); + refitBox.include(*VP.Vertex[2]); + + primIndex++; + }while(nbToGo--); + } + else + { + PxU32 childOffset = current->getChildOffset(j); + PX_ASSERT(!(childOffset&3)); + childOffset>>=2; + PX_ASSERT(childOffset>nb); + const PxU32 childType = current->getChildType(j); + + const BVDataSwizzledNQ* next = data + childOffset; + { + if(childType>1) + { + const PxBounds3 childBox( PxVec3(next->mMinX[3], next->mMinY[3], next->mMinZ[3]), + PxVec3(next->mMaxX[3], next->mMaxY[3], next->mMaxZ[3])); + refitBox.include(childBox); + } + + if(childType>0) + { + const PxBounds3 childBox( PxVec3(next->mMinX[2], next->mMinY[2], next->mMinZ[2]), + PxVec3(next->mMaxX[2], next->mMaxY[2], next->mMaxZ[2])); + refitBox.include(childBox); + } + + { + const PxBounds3 childBox( PxVec3(next->mMinX[1], next->mMinY[1], next->mMinZ[1]), + PxVec3(next->mMaxX[1], next->mMaxY[1], next->mMaxZ[1])); + refitBox.include(childBox); + } + + { + const PxBounds3 childBox( PxVec3(next->mMinX[0], next->mMinY[0], next->mMinZ[0]), + PxVec3(next->mMaxX[0], next->mMaxY[0], next->mMaxZ[0])); + refitBox.include(childBox); + } + } + } + refitBox.minimum -= eps; + refitBox.maximum += eps; + { + float error = (refitBox.minimum - localBox.minimum).magnitude(); + if(error>maxError) + maxError = error; + } + { + float error = (refitBox.maximum - localBox.maximum).magnitude(); + if(error>maxError) + maxError = error; + } + } + } + printf("maxError: %f\n", maxError); + } +#else + PX_ASSERT(0); +#endif + } + T->mNbNodes = NbNeeded; +#else + PX_ASSERT(CurID == NbSingleNodes); + T->mNbNodes = NbSingleNodes; +#endif + T->mNodes = nodes; + } + return true; +} + +static bool BuildBV4Internal(BV4Tree& tree, const AABBTree& Source, SourceMesh* mesh, float epsilon, bool quantized) +{ + if(mesh->getNbTriangles()<=4) + return tree.init(mesh, Source.getBV()); + + { + struct Local + { + static void _CheckMD(const AABBTreeNode* current_node, PxU32& md, PxU32& cd) + { + cd++; + md = PxMax(md, cd); + + if(current_node->getPos()) { _CheckMD(current_node->getPos(), md, cd); cd--; } + if(current_node->getNeg()) { _CheckMD(current_node->getNeg(), md, cd); cd--; } + } + + static void _Check(AABBTreeNode* current_node) + { + if(current_node->isLeaf()) + return; + + AABBTreeNode* P = const_cast(current_node->getPos()); + AABBTreeNode* N = const_cast(current_node->getNeg()); + { + PxU32 MDP = 0; PxU32 CDP = 0; _CheckMD(P, MDP, CDP); + PxU32 MDN = 0; PxU32 CDN = 0; _CheckMD(N, MDN, CDN); + + if(MDP>MDN) +// if(MDP(Source.getNodes())); + } + + BV4BuildParams Params(epsilon); + Params.mNbNodes=1; // Root node + Params.mStats[0]=0; + Params.mStats[1]=0; + Params.mStats[2]=0; + Params.mStats[3]=0; + +#ifdef GU_BV4_USE_NODE_POOLS + BV4Node* Root = Params.allocateNode(); +#else + BV4Node* Root = PX_NEW(BV4Node); +#endif + _BuildBV4(Source, Root, Source.getNodes(), Params); + + if(!tree.init(mesh, Source.getBV())) + return false; + + return BuildBV4FromRoot(tree, Root, Params, quantized, epsilon); +} + +///// + +#define REORDER_STATS_SIZE 16 +struct ReorderDataBase +{ +public: + PxU32* mOrder; + PxU32 mNbPrimsPerLeaf; + PxU32 mIndex; + PxU32 mNbPrims; + PxU32 mStats[REORDER_STATS_SIZE]; +}; + +struct ReorderData : public ReorderDataBase +{ +public: + const SourceMesh* mMesh; +}; + +static bool gReorderCallback(const AABBTreeNode* current, PxU32 /*depth*/, void* userData) +{ + ReorderData* Data = reinterpret_cast(userData); + if(current->isLeaf()) + { + const PxU32 n = current->getNbPrimitives(); + PX_ASSERT(n<=Data->mNbPrimsPerLeaf); + Data->mStats[n]++; + PxU32* Prims = const_cast(current->getPrimitives()); + + for(PxU32 i=0;imNbPrims); + Data->mOrder[Data->mIndex] = Prims[i]; + PX_ASSERT(Data->mIndexmNbPrims); + Prims[i] = Data->mIndex; + Data->mIndex++; + } + } + return true; +} + +bool physx::Gu::BuildBV4Ex(BV4Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf) +{ + const PxU32 nbTris = mesh.mNbTris; + + AABBTree Source; + if(!Source.buildFromMesh(mesh, nbTrisPerLeaf)) + return false; + + { + PxU32* order = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTris, "BV4")); + ReorderData RD; + RD.mMesh = &mesh; + RD.mOrder = order; + RD.mNbPrimsPerLeaf = nbTrisPerLeaf; + RD.mIndex = 0; + RD.mNbPrims = nbTris; + for(PxU32 i=0;i no release + mNbPrimitives = 0; + } + // Data access + PX_FORCE_INLINE const PxBounds3& getAABB() const { return mBV; } + + PX_FORCE_INLINE const AABBTreeNode* getPos() const { return reinterpret_cast(mPos); } + PX_FORCE_INLINE const AABBTreeNode* getNeg() const { const AABBTreeNode* P = getPos(); return P ? P+1 : NULL; } + + PX_FORCE_INLINE bool isLeaf() const { return !getPos(); } + + PxBounds3 mBV; // Global bounding-volume enclosing all the node-related primitives + size_t mPos; // "Positive" & "Negative" children + + // Data access + PX_FORCE_INLINE const PxU32* getPrimitives() const { return mNodePrimitives; } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mNbPrimitives; } + + PxU32* mNodePrimitives; //!< Node-related primitives (shortcut to a position in mIndices below) + PxU32 mNbPrimitives; //!< Number of primitives for this node + }; + + typedef bool (*WalkingCallback) (const AABBTreeNode* current, PxU32 depth, void* userData); + + // PT: TODO: refactor with SQ version (TA34704) + class AABBTree : public physx::shdfnd::UserAllocated + { + public: + AABBTree(); + ~AABBTree(); + + bool buildFromMesh(SourceMesh& mesh, PxU32 limit); + void release(); + + PX_FORCE_INLINE const PxU32* getIndices() const { return mIndices; } //!< Catch the indices + PX_FORCE_INLINE PxU32 getNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes + + PX_FORCE_INLINE const PxU32* getPrimitives() const { return mPool->mNodePrimitives; } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mPool->mNbPrimitives; } + PX_FORCE_INLINE const AABBTreeNode* getNodes() const { return mPool; } + PX_FORCE_INLINE const PxBounds3& getBV() const { return mPool->mBV; } + + PxU32 walk(WalkingCallback callback, void* userData) const; + private: + PxU32* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation). + AABBTreeNode* mPool; //!< Linear pool of nodes for complete trees. Null otherwise. [Opcode 1.3] + PxU32 mTotalNbNodes; //!< Number of nodes in the tree. + }; + + PX_PHYSX_COMMON_API bool BuildBV4Ex(BV4Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf); + +} // namespace Gu +} + +#endif // GU_BV4_BUILD_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h new file mode 100644 index 000000000..f0dca6e4a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h @@ -0,0 +1,40 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SETTINGS_H +#define GU_BV4_SETTINGS_H + + // PT: "BV4" ported from "Opcode 2.0". Available compile-time options are: + #define GU_BV4_STACK_SIZE 256 // Default size of local stacks for non-recursive traversals. + #define GU_BV4_PRECOMPUTED_NODE_SORT // Use node sorting or not. This should probably always be enabled. +// #define GU_BV4_QUANTIZED_TREE // Use AABB quantization/compression or not. + #define GU_BV4_USE_SLABS // Use swizzled data format or not. Swizzled = faster raycasts, but slower overlaps & larger trees. +// #define GU_BV4_COMPILE_NON_QUANTIZED_TREE // + +#endif // GU_BV4_SETTINGS_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h new file mode 100644 index 000000000..a6cdd6978 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h @@ -0,0 +1,114 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_AABB_AABB_SWEEP_TEST_H +#define GU_BV4_AABB_AABB_SWEEP_TEST_H + +#ifndef GU_BV4_USE_SLABS +#if PX_INTEL_FAMILY + PX_FORCE_INLINE Ps::IntBool BV4_SegmentAABBOverlap(const PxVec3& center, const PxVec3& extents, const PxVec3& extents2, const RayParams* PX_RESTRICT params) + { + const PxU32 maskI = 0x7fffffff; + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V extentsV = V4Add(V4LoadU(&extents.x), V4LoadU(&extents2.x)); + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), V4LoadU(¢er.x)); + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); + absDV = _mm_cmpgt_ps(absDV, V4Add(extentsV, fdirV)); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + if(test2&7) + return 0; + return 1; + } + } + +#ifdef GU_BV4_QUANTIZED_TREE + template + PX_FORCE_INLINE Ps::IntBool BV4_SegmentAABBOverlap(const T* PX_RESTRICT node, const PxVec3& extents2, const RayParams* PX_RESTRICT params) + { + const __m128i testV = _mm_load_si128((__m128i*)node->mAABB.mData); + const __m128i qextentsV = _mm_and_si128(testV, _mm_set1_epi32(0x0000ffff)); + const __m128i qcenterV = _mm_srai_epi32(testV, 16); + const Vec4V centerV0 = V4Mul(_mm_cvtepi32_ps(qcenterV), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + const Vec4V extentsV0 = V4Mul(_mm_cvtepi32_ps(qextentsV), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + + const PxU32 maskI = 0x7fffffff; + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V extentsV = V4Add(extentsV0, V4LoadU(&extents2.x)); + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), centerV0); + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); + absDV = _mm_cmpgt_ps(absDV, V4Add(extentsV, fdirV)); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + if(test2&7) + return 0; + return 1; + } + } +#endif +#endif +#endif + +#endif // GU_BV4_AABB_AABB_SWEEP_TEST_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp new file mode 100644 index 000000000..17188acfa --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp @@ -0,0 +1,39 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) +#define SWEEP_AABB_IMPL +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; +#include "GuBV4_BoxSweep_Internal.h" +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h new file mode 100644 index 000000000..29bd6a888 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h @@ -0,0 +1,201 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_BOX_BOX_OVERLAP_TEST_H +#define GU_BV4_BOX_BOX_OVERLAP_TEST_H + +#if PX_INTEL_FAMILY +#ifndef GU_BV4_USE_SLABS + PX_FORCE_INLINE Ps::IntBool BV4_BoxBoxOverlap(const PxVec3& extents, const PxVec3& center, const OBBTestParams* PX_RESTRICT params) + { + const PxU32 maskI = 0x7fffffff; + + const Vec4V extentsV = V4LoadU(&extents.x); + + const Vec4V TV = V4Sub(V4LoadA_Safe(¶ms->mTBoxToModel_PaddedAligned.x), V4LoadU(¢er.x)); + { + __m128 absTV = _mm_and_ps(TV, _mm_load1_ps((float*)&maskI)); + absTV = _mm_cmpgt_ps(absTV, V4Add(extentsV, V4LoadA_Safe(¶ms->mBB_PaddedAligned.x))); + const PxU32 test = (PxU32)_mm_movemask_ps(absTV); + if(test&7) + return 0; + } + + __m128 tV; + { + const __m128 T_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,0,2,1))); + const __m128 T_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,1,0,2))); + + tV = V4Mul(TV, V4LoadA_Safe(¶ms->mPreca0_PaddedAligned.x)); + tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(¶ms->mPreca1_PaddedAligned.x))); + tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(¶ms->mPreca2_PaddedAligned.x))); + } + + __m128 t2V; + { + const __m128 extents_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 extents_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,1,0,2))); + + t2V = V4Mul(extentsV, V4LoadA_Safe(¶ms->mPreca0b_PaddedAligned.x)); + t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(¶ms->mPreca1b_PaddedAligned.x))); + t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(¶ms->mPreca2b_PaddedAligned.x))); + t2V = V4Add(t2V, V4LoadA_Safe(¶ms->mBoxExtents_PaddedAligned.x)); + } + + { + __m128 abstV = _mm_and_ps(tV, _mm_load1_ps((float*)&maskI)); + abstV = _mm_cmpgt_ps(abstV, t2V); + const PxU32 test = (PxU32)_mm_movemask_ps(abstV); + if(test&7) + return 0; + } + return 1; + } + +#ifdef GU_BV4_QUANTIZED_TREE + template + PX_FORCE_INLINE Ps::IntBool BV4_BoxBoxOverlap(const T* PX_RESTRICT node, const OBBTestParams* PX_RESTRICT params) + { +#define NEW_VERSION +#ifdef NEW_VERSION + SSE_CONST4(maskV, 0x7fffffff); + SSE_CONST4(maskQV, 0x0000ffff); +#else + const PxU32 maskI = 0x7fffffff; +#endif + + Vec4V centerV = V4LoadA((float*)node->mAABB.mData); +#ifdef NEW_VERSION + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), SSE_CONST(maskQV))); +#else + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), _mm_set1_epi32(0x0000ffff))); +#endif + extentsV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(extentsV)), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + centerV = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(centerV), 16)); + centerV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(centerV)), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + + const Vec4V TV = V4Sub(V4LoadA_Safe(¶ms->mTBoxToModel_PaddedAligned.x), centerV); + { +#ifdef NEW_VERSION + __m128 absTV = _mm_and_ps(TV, SSE_CONSTF(maskV)); +#else + __m128 absTV = _mm_and_ps(TV, _mm_load1_ps((float*)&maskI)); +#endif + + absTV = _mm_cmpgt_ps(absTV, V4Add(extentsV, V4LoadA_Safe(¶ms->mBB_PaddedAligned.x))); + const PxU32 test = (PxU32)_mm_movemask_ps(absTV); + if(test&7) + return 0; + } + + __m128 tV; + { + const __m128 T_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,0,2,1))); + const __m128 T_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,1,0,2))); + + tV = V4Mul(TV, V4LoadA_Safe(¶ms->mPreca0_PaddedAligned.x)); + tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(¶ms->mPreca1_PaddedAligned.x))); + tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(¶ms->mPreca2_PaddedAligned.x))); + } + + __m128 t2V; + { + const __m128 extents_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 extents_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,1,0,2))); + + t2V = V4Mul(extentsV, V4LoadA_Safe(¶ms->mPreca0b_PaddedAligned.x)); + t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(¶ms->mPreca1b_PaddedAligned.x))); + t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(¶ms->mPreca2b_PaddedAligned.x))); + t2V = V4Add(t2V, V4LoadA_Safe(¶ms->mBoxExtents_PaddedAligned.x)); + } + + { +#ifdef NEW_VERSION + __m128 abstV = _mm_and_ps(tV, SSE_CONSTF(maskV)); +#else + __m128 abstV = _mm_and_ps(tV, _mm_load1_ps((float*)&maskI)); +#endif + abstV = _mm_cmpgt_ps(abstV, t2V); + const PxU32 test = (PxU32)_mm_movemask_ps(abstV); + if(test&7) + return 0; + } + return 1; + } +#endif // GU_BV4_QUANTIZED_TREE +#endif // GU_BV4_USE_SLABS + +#ifdef GU_BV4_USE_SLABS + PX_FORCE_INLINE Ps::IntBool BV4_BoxBoxOverlap(const __m128 boxCenter, const __m128 extentsV, const OBBTestParams* PX_RESTRICT params) + { + const PxU32 maskI = 0x7fffffff; + + const Vec4V TV = V4Sub(V4LoadA_Safe(¶ms->mTBoxToModel_PaddedAligned.x), boxCenter); + { + __m128 absTV = _mm_and_ps(TV, _mm_load1_ps(reinterpret_cast(&maskI))); + absTV = _mm_cmpgt_ps(absTV, V4Add(extentsV, V4LoadA_Safe(¶ms->mBB_PaddedAligned.x))); + const PxU32 test = PxU32(_mm_movemask_ps(absTV)); + if(test&7) + return 0; + } + + __m128 tV; + { + const __m128 T_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,0,2,1))); + const __m128 T_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,1,0,2))); + + tV = V4Mul(TV, V4LoadA_Safe(¶ms->mPreca0_PaddedAligned.x)); + tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(¶ms->mPreca1_PaddedAligned.x))); + tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(¶ms->mPreca2_PaddedAligned.x))); + } + + __m128 t2V; + { + const __m128 extents_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 extents_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,1,0,2))); + + t2V = V4Mul(extentsV, V4LoadA_Safe(¶ms->mPreca0b_PaddedAligned.x)); + t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(¶ms->mPreca1b_PaddedAligned.x))); + t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(¶ms->mPreca2b_PaddedAligned.x))); + t2V = V4Add(t2V, V4LoadA_Safe(¶ms->mBoxExtents_PaddedAligned.x)); + } + + { + __m128 abstV = _mm_and_ps(tV, _mm_load1_ps(reinterpret_cast(&maskI))); + abstV = _mm_cmpgt_ps(abstV, t2V); + const PxU32 test = PxU32(_mm_movemask_ps(abstV)); + if(test&7) + return 0; + } + return 1; + } +#endif // GU_BV4_USE_SLABS +#endif // PX_INTEL_FAMILY + +#endif // GU_BV4_BOX_BOX_OVERLAP_TEST_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp new file mode 100644 index 000000000..64603e3ba --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp @@ -0,0 +1,463 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuInternal.h" +#include "GuDistancePointSegment.h" +#include "GuIntersectionCapsuleTriangle.h" +#include "GuIntersectionTriangleBox.h" + +#include "GuBV4_BoxOverlap_Internal.h" +#include "GuBV4_BoxBoxOverlapTest.h" + +// Box overlap any + +struct OBBParams : OBBTestParams +{ + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + + PxMat33 mRModelToBox_Padded; //!< Rotation from model space to obb space + Vec3p mTModelToBox_Padded; //!< Translation from model space to obb space +}; + +// PT: TODO: this used to be inlined so we lost some perf by moving to PhysX's version. Revisit. (TA34704) +Ps::IntBool intersectTriangleBoxBV4(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, + const PxMat33& rotModelToBox, const PxVec3& transModelToBox, const PxVec3& extents); +namespace +{ +class LeafFunction_BoxOverlapAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +template +static PX_FORCE_INLINE void setupBoxParams(ParamsT* PX_RESTRICT params, const Box& localBox, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh) +{ + invertBoxMatrix(params->mRModelToBox_Padded, params->mTModelToBox_Padded, localBox); + params->mTBoxToModel_PaddedAligned = localBox.center; + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + params->precomputeBoxData(localBox.extents, &localBox.rot); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "GuBV4_BoxBoxOverlapTest.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamNoOrder_OBBOBB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_SwizzledNoOrder.h" +#endif + +#define GU_BV4_PROCESS_STREAM_NO_ORDER +#include "GuBV4_Internal.h" + +Ps::IntBool BV4_OverlapBoxAny(const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + Box localBox; + computeLocalBox(localBox, box, worldm_Aligned); + + OBBParams Params; + setupBoxParams(&Params, localBox, &tree, mesh); + + if(tree.mNodes) + return processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + return LeafFunction_BoxOverlapAny::doLeafTest(&Params, nbTris); + } +} + + +// Box overlap all + +struct OBBParamsAll : OBBParams +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxU32* mHits; +}; + +namespace +{ +class LeafFunction_BoxOverlapAll +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned)) + { + OBBParamsAll* ParamsAll = static_cast(params); + if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits) + return 1; + ParamsAll->mHits[ParamsAll->mNbHits] = primIndex; + ParamsAll->mNbHits++; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +} + +PxU32 BV4_OverlapBoxAll(const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + Box localBox; + computeLocalBox(localBox, box, worldm_Aligned); + + OBBParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = size; + Params.mHits = results; + setupBoxParams(&Params, localBox, &tree, mesh); + + if(tree.mNodes) + overflow = processStreamNoOrder(tree, &Params)!=0; + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + overflow = LeafFunction_BoxOverlapAll::doLeafTest(&Params, nbTris)!=0; + } + return Params.mNbHits; +} + +// Box overlap - callback version + +struct OBBParamsCB : OBBParams +{ + MeshOverlapCallback mCallback; + void* mUserData; +}; + +namespace +{ +class LeafFunction_BoxOverlapCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const OBBParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned)) + { + const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + if((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], primIndex, vrefs)) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +void BV4_OverlapBoxCB(const Box& localBox, const BV4Tree& tree, MeshOverlapCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + OBBParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupBoxParams(&Params, localBox, &tree, mesh); + + if(tree.mNodes) + processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + LeafFunction_BoxOverlapCB::doLeafTest(&Params, nbTris); + } +} + +// Capsule overlap any + +struct CapsuleParamsAny : OBBParams +{ + Capsule mLocalCapsule; // Capsule in mesh space + CapsuleTriangleOverlapData mData; +}; + +// PT: TODO: try to refactor this one with the PhysX version (TA34704) +static bool CapsuleVsTriangle_SAT(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const CapsuleParamsAny* PX_RESTRICT params) +{ +// PX_ASSERT(capsule.p0!=capsule.p1); + + { + const PxReal d2 = distancePointSegmentSquaredInternal(params->mLocalCapsule.p0, params->mData.mCapsuleDir, p0); + if(d2<=params->mLocalCapsule.radius*params->mLocalCapsule.radius) + return 1; + } + + const PxVec3 N = (p0 - p1).cross(p0 - p2); + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, N)) + return 0; + + const float BDotB = params->mData.mBDotB; + const float oneOverBDotB = params->mData.mOneOverBDotB; + const PxVec3& capP0 = params->mLocalCapsule.p0; + const PxVec3& capDir = params->mData.mCapsuleDir; + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p0, p1 - p0, capP0, capDir, BDotB, oneOverBDotB))) + return 0; + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p1, p2 - p1, capP0, capDir, BDotB, oneOverBDotB))) + return 0; + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p2, p0 - p2, capP0, capDir, BDotB, oneOverBDotB))) + return 0; + + return 1; +} + +static Ps::IntBool PX_FORCE_INLINE __CapsuleTriangle(const CapsuleParamsAny* PX_RESTRICT params, PxU32 primIndex) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + return CapsuleVsTriangle_SAT(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params); +} + +namespace +{ +class LeafFunction_CapsuleOverlapAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__CapsuleTriangle(static_cast(params), primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +template +static PX_FORCE_INLINE void setupCapsuleParams(ParamsT* PX_RESTRICT params, const Capsule& capsule, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh) +{ + computeLocalCapsule(params->mLocalCapsule, capsule, worldm_Aligned); + + params->mData.init(params->mLocalCapsule); + + Box localBox; + computeBoxAroundCapsule(params->mLocalCapsule, localBox); + + setupBoxParams(params, localBox, tree, mesh); +} + +Ps::IntBool BV4_OverlapCapsuleAny(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleParamsAny Params; + setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + return processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + return LeafFunction_CapsuleOverlapAny::doLeafTest(&Params, nbTris); + } +} + + +// Capsule overlap all + +struct CapsuleParamsAll : CapsuleParamsAny +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxU32* mHits; +}; + +namespace +{ +class LeafFunction_CapsuleOverlapAll +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + CapsuleParamsAll* ParamsAll = static_cast(params); + + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__CapsuleTriangle(ParamsAll, primIndex)) + { + if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits) + return 1; + ParamsAll->mHits[ParamsAll->mNbHits] = primIndex; + ParamsAll->mNbHits++; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +PxU32 BV4_OverlapCapsuleAll(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = size; + Params.mHits = results; + setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + overflow = processStreamNoOrder(tree, &Params)!=0; + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + overflow = LeafFunction_CapsuleOverlapAll::doLeafTest(&Params, nbTris)!=0; + } + return Params.mNbHits; +} + +// Capsule overlap - callback version + +struct CapsuleParamsCB : CapsuleParamsAny +{ + MeshOverlapCallback mCallback; + void* mUserData; +}; + +namespace +{ +class LeafFunction_CapsuleOverlapCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const CapsuleParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + if(CapsuleVsTriangle_SAT(p0, p1, p2, params)) + { + const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + if((params->mCallback)(params->mUserData, p0, p1, p2, primIndex, vrefs)) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: this one is currently not used +void BV4_OverlapCapsuleCB(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + LeafFunction_CapsuleOverlapCB::doLeafTest(&Params, nbTris); + } +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h new file mode 100644 index 000000000..5d89f5012 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_BOX_OVERLAP_INTERNAL_H +#define GU_BV4_BOX_OVERLAP_INTERNAL_H + +#include "GuBV4_Common.h" + + template + PX_FORCE_INLINE void precomputeData(ParamsT* PX_RESTRICT dst, PxMat33* PX_RESTRICT absRot, const PxMat33* PX_RESTRICT boxToModelR) + { + // Precompute absolute box-to-model rotation matrix + dst->mPreca0_PaddedAligned.x = boxToModelR->column0.x; + dst->mPreca0_PaddedAligned.y = boxToModelR->column1.y; + dst->mPreca0_PaddedAligned.z = boxToModelR->column2.z; + + dst->mPreca1_PaddedAligned.x = boxToModelR->column0.y; + dst->mPreca1_PaddedAligned.y = boxToModelR->column1.z; + dst->mPreca1_PaddedAligned.z = boxToModelR->column2.x; + + dst->mPreca2_PaddedAligned.x = boxToModelR->column0.z; + dst->mPreca2_PaddedAligned.y = boxToModelR->column1.x; + dst->mPreca2_PaddedAligned.z = boxToModelR->column2.y; + + // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID) + const PxReal epsilon = 1e-6f; + absRot->column0.x = dst->mPreca0b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.x); + absRot->column0.y = dst->mPreca1b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.y); + absRot->column0.z = dst->mPreca2b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.z); + + absRot->column1.x = dst->mPreca2b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.x); + absRot->column1.y = dst->mPreca0b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.y); + absRot->column1.z = dst->mPreca1b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.z); + + absRot->column2.x = dst->mPreca1b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.x); + absRot->column2.y = dst->mPreca2b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.y); + absRot->column2.z = dst->mPreca0b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.z); + } + + template + PX_FORCE_INLINE void setupBoxData(ParamsT* PX_RESTRICT dst, const PxVec3& extents, const PxMat33* PX_RESTRICT mAR) + { + dst->mBoxExtents_PaddedAligned = extents; + + const float Ex = extents.x; + const float Ey = extents.y; + const float Ez = extents.z; + dst->mBB_PaddedAligned.x = Ex*mAR->column0.x + Ey*mAR->column1.x + Ez*mAR->column2.x; + dst->mBB_PaddedAligned.y = Ex*mAR->column0.y + Ey*mAR->column1.y + Ez*mAR->column2.y; + dst->mBB_PaddedAligned.z = Ex*mAR->column0.z + Ey*mAR->column1.z + Ez*mAR->column2.z; + } + + struct OBBTestParams // Data needed to perform the OBB-OBB overlap test + { + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mTBoxToModel_PaddedAligned); //!< Translation from obb space to model space + BV4_ALIGN16(Vec3p mBB_PaddedAligned); + BV4_ALIGN16(Vec3p mBoxExtents_PaddedAligned); + + BV4_ALIGN16(Vec3p mPreca0_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca1_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca2_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca0b_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca1b_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca2b_PaddedAligned); + + PX_FORCE_INLINE void precomputeBoxData(const PxVec3& extents, const PxMat33* PX_RESTRICT box_to_model) + { + PxMat33 absRot; //!< Absolute rotation matrix + precomputeData(this, &absRot, box_to_model); + + setupBoxData(this, extents, &absRot); + } + }; + +#endif // GU_BV4_BOX_OVERLAP_INTERNAL_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h new file mode 100644 index 000000000..a5ba47544 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h @@ -0,0 +1,518 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTriangleUtils.h" +#include "GuSweepBoxTriangle_FeatureBased.h" +#include "GuSweepBoxTriangle_SAT.h" +#include "GuBV4_BoxOverlap_Internal.h" + +// PT: for box-sweeps please refer to %SDKRoot%\InternalDocumentation\GU\Sweep strategies.ppt. +// We use: +// - method 3 if the box is an AABB (SWEEP_AABB_IMPL is defined) +// - method 2 if the box is an OBB (SWEEP_AABB_IMPL is undefined) + +#ifdef SWEEP_AABB_IMPL + // PT: TODO: refactor structure (TA34704) + struct RayParams + { + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); + #ifndef GU_BV4_USE_SLABS + BV4_ALIGN16(Vec3p mData2_PaddedAligned); + BV4_ALIGN16(Vec3p mFDir_PaddedAligned); + BV4_ALIGN16(Vec3p mData_PaddedAligned); + BV4_ALIGN16(Vec3p mLocalDir_PaddedAligned); + #endif + BV4_ALIGN16(Vec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) + }; + + #include "GuBV4_AABBAABBSweepTest.h" +#else + #include "GuBV4_BoxBoxOverlapTest.h" +#endif + +#include "GuBV4_BoxSweep_Params.h" + +static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33& mat_Padded) +{ + const FloatV xxxV = V4GetX(p); + const FloatV yyyV = V4GetY(p); + const FloatV zzzV = V4GetZ(p); + + Vec4V ResV = V4Scale(V4LoadU_Safe(&mat_Padded.column0.x), xxxV); + ResV = V4Add(ResV, V4Scale(V4LoadU_Safe(&mat_Padded.column1.x), yyyV)); + ResV = V4Add(ResV, V4Scale(V4LoadU_Safe(&mat_Padded.column2.x), zzzV)); + + return ResV; +} + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ triBoxSweep(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex, bool nodeSorting=true) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + // Don't bother doing the actual sweep test if the triangle is too far away + if(1) + { + const float dp0 = p0.dot(params->mLocalDir_Padded); + const float dp1 = p1.dot(params->mLocalDir_Padded); + const float dp2 = p2.dot(params->mLocalDir_Padded); + + float TriMin = PxMin(dp0, dp1); + TriMin = PxMin(TriMin, dp2); + + if(TriMin >= params->mOffset + params->mStabbedFace.mDistance) + return false; + } + + TrianglePadded triBoxSpace; + const Vec4V transModelToBoxV = V4LoadU_Safe(¶ms->mTModelToBox_Padded.x); + const Vec4V v0V = V4Add(multiply3x3V(V4LoadU_Safe(&p0.x), params->mRModelToBox_Padded), transModelToBoxV); + V4StoreU_Safe(v0V, &triBoxSpace.verts[0].x); + const Vec4V v1V = V4Add(multiply3x3V(V4LoadU_Safe(&p1.x), params->mRModelToBox_Padded), transModelToBoxV); + V4StoreU_Safe(v1V, &triBoxSpace.verts[1].x); + const Vec4V v2V = V4Add(multiply3x3V(V4LoadU_Safe(&p2.x), params->mRModelToBox_Padded), transModelToBoxV); + V4StoreU_Safe(v2V, &triBoxSpace.verts[2].x); + + float Dist; + if(triBoxSweepTestBoxSpace_inlined(triBoxSpace, params->mOriginalExtents_Padded, params->mOriginalDir_Padded*params->mStabbedFace.mDistance, params->mOneOverDir_Padded, 1.0f, Dist, params->mBackfaceCulling)) + { + // PT: TODO: these muls & divs may not be needed at all - we just pass the unit dir/inverse dir to the sweep code. Revisit. (TA34704) + Dist *= params->mStabbedFace.mDistance; + params->mOneOverDir_Padded = params->mOneOverOriginalDir / Dist; + params->mStabbedFace.mDistance = Dist; + params->mStabbedFace.mTriangleID = primIndex; + // PT: TODO: revisit this (TA34704) + params->mP0 = triBoxSpace.verts[0]; + params->mP1 = triBoxSpace.verts[1]; + params->mP2 = triBoxSpace.verts[2]; +// V4StoreU_Safe(v0V, ¶ms->mP0.x); +// V4StoreU_Safe(v1V, ¶ms->mP1.x); +// V4StoreU_Safe(v2V, ¶ms->mP2.x); + + if(nodeSorting) + { +#ifdef SWEEP_AABB_IMPL + #ifndef GU_BV4_USE_SLABS + setupRayData(params, Dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned); + #endif +#else + params->ShrinkOBB(Dist); +#endif + } + return true; + } + return false; +} + +namespace +{ +class LeafFunction_BoxSweepClosest +{ +public: + static PX_FORCE_INLINE void doLeafTest(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + triBoxSweep(params, primIndex); + primIndex++; + }while(nbToGo--); + } +}; + +class LeafFunction_BoxSweepAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triBoxSweep(params, primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: TODO: refactor with sphere/capsule versions (TA34704) +static PX_FORCE_INLINE bool computeImpactData(const Box& box, const PxVec3& dir, SweepHit* PX_RESTRICT hit, const BoxSweepParams* PX_RESTRICT params, bool isDoubleSided, bool meshBothSides) +{ + if(params->mStabbedFace.mTriangleID==PX_INVALID_U32) + return false; // We didn't touch any triangle + + if(hit) + { + const float t = params->mStabbedFace.mDistance; + hit->mTriangleID = params->mStabbedFace.mTriangleID; + hit->mDistance = t; + + if(t==0.0f) + { + hit->mPos = PxVec3(0.0f); + hit->mNormal = -dir; + } + else + { + // PT: TODO: revisit/optimize/use this (TA34704) + const PxTriangle triInBoxSpace(params->mP0, params->mP1, params->mP2); + PxHitFlags outFlags = PxHitFlag::Enum(0); + computeBoxLocalImpact(hit->mPos, hit->mNormal, outFlags, box, params->mOriginalDir_Padded, triInBoxSpace, PxHitFlag::ePOSITION|PxHitFlag::eNORMAL, isDoubleSided, meshBothSides, t); + } + } + return true; +} + +template +static PX_FORCE_INLINE void setupBoxSweepParams(ParamsT* PX_RESTRICT params, const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh, PxU32 flags) +{ + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + setupParamsFlags(params, flags); + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + prepareSweepData(localBox, localDir, maxDist, params); + +#ifdef SWEEP_AABB_IMPL + params->mOrigin_Padded = localBox.center; + #ifndef GU_BV4_USE_SLABS + params->mLocalDir_PaddedAligned = localDir; + setupRayData(params, maxDist, localBox.center, localDir); + #endif +#endif +} + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#ifdef SWEEP_AABB_IMPL + #include "GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h" + #include "GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h" + #ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_KajiyaNoOrder.h" + #include "GuBV4_Slabs_KajiyaOrdered.h" + #endif +#else + #include "GuBV4_ProcessStreamOrdered_OBBOBB.h" + #include "GuBV4_ProcessStreamNoOrder_OBBOBB.h" + #ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_SwizzledNoOrder.h" + #include "GuBV4_Slabs_SwizzledOrdered.h" + #endif +#endif + +#ifdef SWEEP_AABB_IMPL + #define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER + #define GU_BV4_PROCESS_STREAM_RAY_ORDERED +#else + #define GU_BV4_PROCESS_STREAM_NO_ORDER + #define GU_BV4_PROCESS_STREAM_ORDERED +#endif +#include "GuBV4_Internal.h" + +#ifdef SWEEP_AABB_IMPL +Ps::IntBool Sweep_AABB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) +#else +Ps::IntBool Sweep_OBB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) +#endif +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + BoxSweepParams Params; + setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags); + + if(tree.mNodes) + { +#ifdef SWEEP_AABB_IMPL + if(Params.mEarlyExit) + processStreamRayNoOrder<1, LeafFunction_BoxSweepAny>(tree, &Params); + else + processStreamRayOrdered<1, LeafFunction_BoxSweepClosest>(tree, &Params); +#else + if(Params.mEarlyExit) + processStreamNoOrder(tree, &Params); + else + processStreamOrdered(tree, &Params); +#endif + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); + + return computeImpactData(localBox, localDir, hit, &Params, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + + + +// PT: box sweep callback version - currently not used + +namespace +{ + struct BoxSweepParamsCB : BoxSweepParams + { + // PT: these new members are only here to call computeImpactData during traversal :( + // PT: TODO: most of them may not be needed + Box mBoxCB; // Box in original space (maybe not local/mesh space) + PxVec3 mDirCB; // Dir in original space (maybe not local/mesh space) + const PxMat44* mWorldm_Aligned; + PxU32 mFlags; + + SweepUnlimitedCallback mCallback; + void* mUserData; + float mMaxDist; + bool mNodeSorting; + }; + +class LeafFunction_BoxSweepCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(BoxSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triBoxSweep(params, primIndex, params->mNodeSorting)) + { + // PT: TODO: in this version we must compute the impact data immediately, + // which is a terrible idea in general, but I'm not sure what else I can do. + SweepHit hit; + const bool b = computeImpactData(params->mBoxCB, params->mDirCB, &hit, params, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); + PX_ASSERT(b); + + // PT: then replicate part from BV4_BoxSweepSingle: + if(b && params->mWorldm_Aligned) + { + // Move to world space + // PT: TODO: optimize (TA34704) + hit.mPos = params->mWorldm_Aligned->transform(hit.mPos); + hit.mNormal = params->mWorldm_Aligned->rotate(hit.mNormal); + } + + reportUnlimitedCallbackHit(params, hit); + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +// PT: 'worldm_Aligned' is only here to move back results to world space, but input is already in local space. +#ifdef SWEEP_AABB_IMPL +void Sweep_AABB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +#else +void Sweep_OBB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +#endif +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + BoxSweepParamsCB Params; + Params.mBoxCB = localBox; + Params.mDirCB = localDir; + Params.mWorldm_Aligned = worldm_Aligned; + Params.mFlags = flags; + + Params.mCallback = callback; + Params.mUserData = userData; + Params.mMaxDist = maxDist; + Params.mNodeSorting = nodeSorting; + setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags); + + PX_ASSERT(!Params.mEarlyExit); + + if(tree.mNodes) + { + if(nodeSorting) + { +#ifdef SWEEP_AABB_IMPL + processStreamRayOrdered<1, LeafFunction_BoxSweepCB>(tree, &Params); +#else + processStreamOrdered(tree, &Params); +#endif + } + else + { +#ifdef SWEEP_AABB_IMPL + processStreamRayNoOrder<1, LeafFunction_BoxSweepCB>(tree, &Params); +#else + processStreamNoOrder(tree, &Params); +#endif + } + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); +} + + + + +// New callback-based box sweeps. Reuses code above, allow early exits. Some init code may be done in vain +// since the leaf tests are not performed (we don't do box-sweeps-vs-tri since the box is only a BV around +// the actual shape, say a convex) + +namespace +{ +struct GenericSweepParamsCB : BoxSweepParams +{ + MeshSweepCallback mCallback; + void* mUserData; +}; + +class LeafFunction_BoxSweepClosestCB +{ +public: + static PX_FORCE_INLINE void doLeafTest(GenericSweepParamsCB* PX_RESTRICT params, PxU32 prim_index) + { + PxU32 nbToGo = getNbPrimitives(prim_index); + do + { + // PT: in the regular version we'd do a box-vs-triangle sweep test here + // Instead we just grab the triangle and send it to the callback + // + // This can be used for regular "closest hit" sweeps, when the scale is not identity or + // when the box is just around a more complex shape (e.g. convex). In this case we want + // the calling code to compute a convex-triangle distance, and then we want to shrink + // the ray/box while doing an ordered traversal. + // + // For "sweep all" or "sweep any" purposes we want to either report all hits or early exit + // as soon as we find one. There is no need for shrinking or ordered traversals here. + + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, prim_index, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + // Don't bother doing the actual sweep test if the triangle is too far away + const float dp0 = p0.dot(params->mLocalDir_Padded); + const float dp1 = p1.dot(params->mLocalDir_Padded); + const float dp2 = p2.dot(params->mLocalDir_Padded); + + float TriMin = PxMin(dp0, dp1); + TriMin = PxMin(TriMin, dp2); + + if(TriMin < params->mOffset + params->mStabbedFace.mDistance) + { +// const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + float Dist = params->mStabbedFace.mDistance; + if((params->mCallback)(params->mUserData, p0, p1, p2, prim_index, /*vrefs,*/ Dist)) + return; // PT: TODO: we return here but the ordered path doesn't really support early exits (TA34704) + + if(DistmStabbedFace.mDistance) + { + params->mStabbedFace.mDistance = Dist; + params->mStabbedFace.mTriangleID = prim_index; +#ifdef SWEEP_AABB_IMPL + #ifndef GU_BV4_USE_SLABS + setupRayData(params, Dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned); + #endif +#else + params->ShrinkOBB(Dist); +#endif + } + } + + prim_index++; + }while(nbToGo--); + } +}; + +class LeafFunction_BoxSweepAnyCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(GenericSweepParamsCB* PX_RESTRICT params, PxU32 prim_index) + { + PxU32 nbToGo = getNbPrimitives(prim_index); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, prim_index, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + { +// const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + float Dist = params->mStabbedFace.mDistance; + if((params->mCallback)(params->mUserData, p0, p1, p2, prim_index, /*vrefs,*/ Dist)) + return 1; + } + + prim_index++; + }while(nbToGo--); + + return 0; + } +}; +} + +#ifdef SWEEP_AABB_IMPL +void GenericSweep_AABB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags) +#else +void GenericSweep_OBB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags) +#endif +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + GenericSweepParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags); + + if(tree.mNodes) + { +#ifdef SWEEP_AABB_IMPL + if(Params.mEarlyExit) + processStreamRayNoOrder<1, LeafFunction_BoxSweepAnyCB>(tree, &Params); + else + processStreamRayOrdered<1, LeafFunction_BoxSweepClosestCB>(tree, &Params); +#else + if(Params.mEarlyExit) + processStreamNoOrder(tree, &Params); + else + processStreamOrdered(tree, &Params); +#endif + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h new file mode 100644 index 000000000..a80e18e8c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h @@ -0,0 +1,211 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This is used by the box-sweep & capsule-sweep code + +#if PX_VC + #pragma warning(disable: 4505) // unreferenced local function has been removed +#endif + +#include "PsBasicTemplates.h" + +namespace +{ +#ifdef SWEEP_AABB_IMPL +struct BoxSweepParams : RayParams +#else +struct BoxSweepParams : OBBTestParams +#endif +{ + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + +#ifndef SWEEP_AABB_IMPL + Box mLocalBox; +#endif + PxVec3 mLocalDir_Padded; + RaycastHitInternal mStabbedFace; + + PxU32 mBackfaceCulling; + PxU32 mEarlyExit; + + PxVec3 mP0, mP1, mP2; + PxVec3 mBestTriNormal; + + float mOffset; + PxVec3 mProj; + PxVec3 mDP; + +#ifndef SWEEP_AABB_IMPL + PxMat33 mAR; //!< Absolute rotation matrix +#endif + + PxMat33 mRModelToBox_Padded; //!< Rotation from model space to obb space + PxVec3 mTModelToBox_Padded; //!< Translation from model space to obb space + PxVec3 mOriginalExtents_Padded; + PxVec3 mOriginalDir_Padded; + PxVec3 mOneOverDir_Padded; + PxVec3 mOneOverOriginalDir; + +#ifndef SWEEP_AABB_IMPL + PX_FORCE_INLINE void ShrinkOBB(float d) + { + const PxVec3 BoxExtents = mDP + d * mProj; + mTBoxToModel_PaddedAligned = mLocalBox.center + mLocalDir_Padded*d*0.5f; + + setupBoxData(this, BoxExtents, &mAR); + } +#endif +}; +} + +// PT: TODO: check asm again in PhysX version, compare to original (TA34704) +static void prepareSweepData(const Box& box, const PxVec3& dir, float maxDist, BoxSweepParams* PX_RESTRICT params) +{ + invertBoxMatrix(params->mRModelToBox_Padded, params->mTModelToBox_Padded, box); + + params->mOriginalExtents_Padded = box.extents; + + const PxVec3 OriginalDir = params->mRModelToBox_Padded.transform(dir); + params->mOriginalDir_Padded = OriginalDir; + + const PxVec3 OneOverOriginalDir(OriginalDir.x!=0.0f ? 1.0f/OriginalDir.x : 0.0f, + OriginalDir.y!=0.0f ? 1.0f/OriginalDir.y : 0.0f, + OriginalDir.z!=0.0f ? 1.0f/OriginalDir.z : 0.0f); + + params->mOneOverOriginalDir = OneOverOriginalDir; + params->mOneOverDir_Padded = OneOverOriginalDir / maxDist; + + { + const Box& LocalBox = box; + const PxVec3& LocalDir = dir; + + params->mLocalDir_Padded = LocalDir; + params->mStabbedFace.mDistance = maxDist; +#ifndef SWEEP_AABB_IMPL + params->mLocalBox = LocalBox; // PT: TODO: check asm for operator= +#endif + + PxMat33 boxToModelR; + + // Original code: + // OBB::CreateOBB(LocalBox, LocalDir, 0.5f) + { + PxVec3 R1, R2; + { + float dd[3]; + dd[0] = fabsf(LocalBox.rot.column0.dot(LocalDir)); + dd[1] = fabsf(LocalBox.rot.column1.dot(LocalDir)); + dd[2] = fabsf(LocalBox.rot.column2.dot(LocalDir)); + float dmax = dd[0]; + PxU32 ax0=1; + PxU32 ax1=2; + if(dd[1]>dmax) + { + dmax=dd[1]; + ax0=0; + ax1=2; + } + if(dd[2]>dmax) + { + dmax=dd[2]; + ax0=0; + ax1=1; + } + if(dd[ax1]mRBoxToModel + boxToModelR.column0 = LocalDir; + boxToModelR.column1 = R1; + boxToModelR.column2 = R2; + + // Original code: + // float Offset[3]; + // 0.5f comes from the Offset[r]*0.5f, doesn't mean 'd' is 0.5f + params->mProj.x = 0.5f; + params->mProj.y = LocalDir.dot(R1)*0.5f; + params->mProj.z = LocalDir.dot(R2)*0.5f; + + // Original code: + //mExtents[r] = Offset[r]*0.5f + fabsf(box.mRot[0]|R)*box.mExtents.x + fabsf(box.mRot[1]|R)*box.mExtents.y + fabsf(box.mRot[2]|R)*box.mExtents.z; + // => we store the first part of the computation, minus 'Offset[r]*0.5f' + for(PxU32 r=0;r<3;r++) + { + const PxVec3& R = boxToModelR[r]; + params->mDP[r] = fabsf(LocalBox.rot.column0.dot(R)*LocalBox.extents.x) + + fabsf(LocalBox.rot.column1.dot(R)*LocalBox.extents.y) + + fabsf(LocalBox.rot.column2.dot(R)*LocalBox.extents.z); + } + // In the original code, both mCenter & mExtents depend on 'd', and thus we will need to recompute these two members. + // + // For mExtents we have: + // + // float Offset[3]; + // Offset[0] = d; + // Offset[1] = d*(dir|R1); + // Offset[2] = d*(dir|R2); + // + // mExtents[r] = Offset[r]*0.5f + fabsf(box.mRot[0]|R)*box.mExtents.x + fabsf(box.mRot[1]|R)*box.mExtents.y + fabsf(box.mRot[2]|R)*box.mExtents.z; + // <=> mExtents[r] = Offset[r]*0.5f + Params.mDP[r]; We precompute the second part that doesn't depend on d, stored in mDP + // <=> mExtents[r] = Params.mProj[r]*d + Params.mDP[r]; We extract d from the first part, store what is left in mProj + // + // Thus in ShrinkOBB the code needed to update the extents is just: + // mBoxExtents = mDP + d * mProj; + // + // For mCenter we have: + // + // mCenter = box.mCenter + dir*d*0.5f; + // + // So we simply use this formula directly, with the new d. Result is stored in 'mTBoxToModel' +/* + PX_FORCE_INLINE void ShrinkOBB(float d) + { + mBoxExtents = mDP + d * mProj; + mTBoxToModel = mLocalBox.mCenter + mLocalDir*d*0.5f; +*/ + } + + // This one is for culling tris, unrelated to CreateOBB + params->mOffset = params->mDP.x + LocalBox.center.dot(LocalDir); + +#ifndef SWEEP_AABB_IMPL + precomputeData(params, ¶ms->mAR, &boxToModelR); + + params->ShrinkOBB(maxDist); +#endif + } +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp new file mode 100644 index 000000000..f0ee08a08 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp @@ -0,0 +1,173 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +#include "GuSweepSphereTriangle.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuSIMDHelpers.h" +#include "GuInternal.h" + +#include "GuBV4_BoxOverlap_Internal.h" +#include "GuBV4_BoxSweep_Params.h" + +namespace +{ + struct CapsuleSweepParams : BoxSweepParams + { + Capsule mLocalCapsule; + PxVec3 mCapsuleCenter; + PxVec3 mExtrusionDir; + float mBestAlignmentValue; + float mBestDistance; + float mMaxDist; + }; +} + +#include "GuBV4_CapsuleSweep_Internal.h" +#include "GuBV4_BoxBoxOverlapTest.h" + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamOrdered_OBBOBB.h" +#include "GuBV4_ProcessStreamNoOrder_OBBOBB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_SwizzledNoOrder.h" + #include "GuBV4_Slabs_SwizzledOrdered.h" +#endif + +#define GU_BV4_PROCESS_STREAM_NO_ORDER +#define GU_BV4_PROCESS_STREAM_ORDERED +#include "GuBV4_Internal.h" + +Ps::IntBool BV4_CapsuleSweepSingle(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleSweepParams Params; + setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamNoOrder(tree, &Params); + else + processStreamOrdered(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); + + return computeImpactDataT(capsule, dir, hit, &Params, NULL, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + +// PT: capsule sweep callback version - currently not used + +namespace +{ + struct CapsuleSweepParamsCB : CapsuleSweepParams + { + // PT: these new members are only here to call computeImpactDataT during traversal :( + // PT: TODO: most of them may not be needed + // PT: TODO: for example mCapsuleCB probably dup of mLocalCapsule + Capsule mCapsuleCB; // Capsule in original space (maybe not local/mesh space) + PxVec3 mDirCB; // Dir in original space (maybe not local/mesh space) + const PxMat44* mWorldm_Aligned; + PxU32 mFlags; + + SweepUnlimitedCallback mCallback; + void* mUserData; + bool mNodeSorting; + }; + +class LeafFunction_CapsuleSweepCB +{ +public: + + static PX_FORCE_INLINE Ps::IntBool doLeafTest(CapsuleSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triCapsuleSweep(params, primIndex, params->mNodeSorting)) + { + // PT: TODO: in this version we must compute the impact data immediately, + // which is a terrible idea in general, but I'm not sure what else I can do. + SweepHit hit; + const bool b = computeImpactDataT(params->mCapsuleCB, params->mDirCB, &hit, params, params->mWorldm_Aligned, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); + PX_ASSERT(b); + PX_UNUSED(b); + + reportUnlimitedCallbackHit(params, hit); + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +void BV4_CapsuleSweepCB(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleSweepParamsCB Params; + Params.mCapsuleCB = capsule; + Params.mDirCB = dir; + Params.mWorldm_Aligned = worldm_Aligned; + Params.mFlags = flags; + + Params.mCallback = callback; + Params.mUserData = userData; + Params.mMaxDist = maxDist; + Params.mNodeSorting = nodeSorting; + setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags); + + PX_ASSERT(!Params.mEarlyExit); + + if(tree.mNodes) + { + if(nodeSorting) + processStreamOrdered(tree, &Params); + else + processStreamNoOrder(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp new file mode 100644 index 000000000..ea1d77bf9 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +#include "GuSweepSphereTriangle.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuBV4_Common.h" +#include "GuInternal.h" + +#define SWEEP_AABB_IMPL + + // PT: TODO: refactor structure (TA34704) + struct RayParams + { + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); + #ifndef GU_BV4_USE_SLABS + BV4_ALIGN16(Vec3p mData2_PaddedAligned); + BV4_ALIGN16(Vec3p mFDir_PaddedAligned); + BV4_ALIGN16(Vec3p mData_PaddedAligned); + BV4_ALIGN16(Vec3p mLocalDir_PaddedAligned); + #endif + BV4_ALIGN16(Vec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) + }; + +#include "GuBV4_BoxSweep_Params.h" + +namespace +{ + struct CapsuleSweepParams : BoxSweepParams + { + Capsule mLocalCapsule; + PxVec3 mCapsuleCenter; + PxVec3 mExtrusionDir; + float mBestAlignmentValue; + float mBestDistance; + float mMaxDist; + }; +} + +#include "GuBV4_CapsuleSweep_Internal.h" +#include "GuBV4_AABBAABBSweepTest.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h" +#include "GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_KajiyaNoOrder.h" + #include "GuBV4_Slabs_KajiyaOrdered.h" +#endif + +#define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER +#define GU_BV4_PROCESS_STREAM_RAY_ORDERED +#include "GuBV4_Internal.h" + +Ps::IntBool BV4_CapsuleSweepSingleAA(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleSweepParams Params; + setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamRayNoOrder<1, LeafFunction_CapsuleSweepAny>(tree, &Params); + else + processStreamRayOrdered<1, LeafFunction_CapsuleSweepClosest>(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); + + return computeImpactDataT(capsule, dir, hit, &Params, NULL, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h new file mode 100644 index 000000000..276fe4e8c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h @@ -0,0 +1,434 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_CAPSULE_SWEEP_INTERNAL_H +#define GU_BV4_CAPSULE_SWEEP_INTERNAL_H + +// PT: for capsule-sweeps please refer to %SDKRoot%\InternalDocumentation\GU\Sweep strategies.ppt. +// We use: +// - method 3 if the capsule is axis-aligned (SWEEP_AABB_IMPL is defined) +// - method 2 otherwise (SWEEP_AABB_IMPL is undefined) + +// PT: TODO: get rid of that one +static PX_FORCE_INLINE bool sweepSphereVSTriangle( const PxVec3& center, const float radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& triUnitNormal, + const PxVec3& unitDir, + float& curT, bool& directHit) +{ + float currentDistance; + if(!sweepSphereVSTri(triVerts, triUnitNormal, center, radius, unitDir, currentDistance, directHit, true)) + return false; + + // PT: using ">" or ">=" is enough to block the CCT or not in the DE5967 visual test. Change to ">=" if a repro is needed. + if(currentDistance > curT) + return false; + curT = currentDistance; + return true; +} + +static PX_FORCE_INLINE bool sweepSphereVSQuad( const PxVec3& center, const float radius, + const PxVec3* PX_RESTRICT quadVerts, const PxVec3& quadUnitNormal, + const PxVec3& unitDir, + float& curT) +{ + float currentDistance; + if(!sweepSphereVSQuad(quadVerts, quadUnitNormal, center, radius, unitDir, currentDistance)) + return false; + + // PT: using ">" or ">=" is enough to block the CCT or not in the DE5967 visual test. Change to ">=" if a repro is needed. + if(currentDistance > curT) + return false; + curT = currentDistance; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ testTri( const CapsuleSweepParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& N, + const PxVec3& unitDir, const float capsuleRadius, const float dpc0, float& curT, bool& status) +{ + // PT: TODO: check the assembly here (TA34704) + PxVec3 currentTri[3]; + // PT: TODO: optimize this copy (TA34704) + currentTri[0] = p0; + currentTri[1] = p1; + currentTri[2] = p2; + + // PT: beware, culling is only ok on the sphere I think + if(rejectTriangle(params->mCapsuleCenter, unitDir, curT, capsuleRadius, currentTri, dpc0)) + return false; + + float magnitude = N.magnitude(); + if(magnitude==0.0f) + return false; + + PxVec3 triNormal = N / magnitude; + + bool DirectHit; + if(sweepSphereVSTriangle(params->mCapsuleCenter, capsuleRadius, currentTri, triNormal, unitDir, curT, DirectHit)) + { + status = true; + } + return DirectHit; +} + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static void /*__fastcall*/ testQuad(const CapsuleSweepParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& p3, const PxVec3& N, + const PxVec3& unitDir, const float capsuleRadius, const float dpc0, float& curT, bool& status) +{ + // PT: TODO: optimize this copy (TA34704) + PxVec3 currentQuad[4]; + currentQuad[0] = p0; + currentQuad[1] = p1; + currentQuad[2] = p2; + currentQuad[3] = p3; + + // PT: beware, culling is only ok on the sphere I think + if(rejectQuad(params->mCapsuleCenter, unitDir, curT, capsuleRadius, currentQuad, dpc0)) + return; + + float magnitude = N.magnitude(); + if(magnitude==0.0f) + return; + + PxVec3 triNormal = N / magnitude; + + if(sweepSphereVSQuad(params->mCapsuleCenter, capsuleRadius, currentQuad, triNormal, unitDir, curT)) + { + status = true; + } +} + +static PX_FORCE_INLINE float Set2(const PxVec3& p0, const PxVec3& n, const PxVec3& p) +{ + return (p-p0).dot(n); +} + +static PX_FORCE_INLINE bool sweepCapsuleVsTriangle(const CapsuleSweepParams* PX_RESTRICT params, const PxTriangle& triangle, float& t, bool isDoubleSided, PxVec3& normal) +{ + const PxVec3& unitDir = params->mLocalDir_Padded; + + // Create triangle normal + PxVec3 denormalizedNormal = (triangle.verts[0] - triangle.verts[1]).cross(triangle.verts[0] - triangle.verts[2]); + + normal = denormalizedNormal; + + // Backface culling + const bool culled = denormalizedNormal.dot(unitDir) > 0.0f; + if(culled) + { + if(!isDoubleSided) + return false; + + denormalizedNormal = -denormalizedNormal; + } + + const float capsuleRadius = params->mLocalCapsule.radius; + float curT = params->mStabbedFace.mDistance; + const float dpc0 = params->mCapsuleCenter.dot(unitDir); + + bool status = false; + + // Extrude mesh on the fly + const PxVec3 p0 = triangle.verts[0] - params->mExtrusionDir; + const PxVec3 p1 = triangle.verts[1+culled] - params->mExtrusionDir; + const PxVec3 p2 = triangle.verts[2-culled] - params->mExtrusionDir; + + const PxVec3 p0b = triangle.verts[0] + params->mExtrusionDir; + const PxVec3 p1b = triangle.verts[1+culled] + params->mExtrusionDir; + const PxVec3 p2b = triangle.verts[2-culled] + params->mExtrusionDir; + + const float extrusionSign = denormalizedNormal.dot(params->mExtrusionDir); + + const PxVec3 p2b_p1b = p2b - p1b; + const PxVec3 p0b_p1b = p0b - p1b; + const PxVec3 p2b_p2 = 2.0f * params->mExtrusionDir; + const PxVec3 p1_p1b = -p2b_p2; + + const PxVec3 N1 = p2b_p1b.cross(p0b_p1b); + const float dp0 = Set2(p0b, N1, params->mCapsuleCenter); + + const PxVec3 N2 = (p2 - p1).cross(p0 - p1); + const float dp1 = -Set2(p0, N2, params->mCapsuleCenter); + + bool directHit; + if(extrusionSign >= 0.0f) + directHit = testTri(params, p0b, p1b, p2b, N1, unitDir, capsuleRadius, dpc0, curT, status); + else + directHit = testTri(params, p0, p1, p2, N2, unitDir, capsuleRadius, dpc0, curT, status); + + const PxVec3 N3 = p2b_p1b.cross(p1_p1b); + const float dp2 = -Set2(p1, N3, params->mCapsuleCenter); + if(!directHit) + { + const float dp = N3.dot(unitDir); + if(dp*extrusionSign>=0.0f) + testQuad(params, p1, p1b, p2, p2b, N3, unitDir, capsuleRadius, dpc0, curT, status); + } + + const PxVec3 N5 = p2b_p2.cross(p0 - p2); + const float dp3 = -Set2(p0, N5, params->mCapsuleCenter); + if(!directHit) + { + const float dp = N5.dot(unitDir); + if(dp*extrusionSign>=0.0f) + testQuad(params, p2, p2b, p0, p0b, N5, unitDir, capsuleRadius, dpc0, curT, status); + } + + const PxVec3 N7 = p1_p1b.cross(p0b_p1b); + const float dp4 = -Set2(p0b, N7, params->mCapsuleCenter); + if(!directHit) + { + const float dp = N7.dot(unitDir); + if(dp*extrusionSign>=0.0f) + testQuad(params, p0, p0b, p1, p1b, N7, unitDir, capsuleRadius, dpc0, curT, status); + } + + if(1) + { + bool originInside = true; + if(extrusionSign<0.0f) + { + if(dp0<0.0f || dp1<0.0f || dp2<0.0f || dp3<0.0f || dp4<0.0f) + originInside = false; + } + else + { + if(dp0>0.0f || dp1>0.0f || dp2>0.0f || dp3>0.0f || dp4>0.0f) + originInside = false; + } + if(originInside) + { + t = 0.0f; + return true; + } + } + + if(!status) + return false; // We didn't touch any triangle + + t = curT; + + return true; +} + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ triCapsuleSweep(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex, bool nodeSorting=true) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + const PxTriangle Tri(p0, p1, p2); // PT: TODO: check calls to empty ctor/dtor here (TA34704) + + const bool isDoubleSided = params->mBackfaceCulling==0; + + float Dist; + PxVec3 denormalizedNormal; + if(sweepCapsuleVsTriangle(params, Tri, Dist, isDoubleSided, denormalizedNormal)) + { + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal alignmentValue = computeAlignmentValue(denormalizedNormal, params->mLocalDir_Padded); + + if(keepTriangle(Dist, alignmentValue, params->mBestDistance, params->mBestAlignmentValue, params->mMaxDist, distEpsilon)) + { + params->mStabbedFace.mDistance = Dist; + params->mStabbedFace.mTriangleID = primIndex; + + params->mP0 = p0; + params->mP1 = p1; + params->mP2 = p2; + + params->mBestDistance = PxMin(params->mBestDistance, Dist); // exact lower bound + params->mBestAlignmentValue = alignmentValue; + params->mBestTriNormal = denormalizedNormal; + + if(nodeSorting) + { +#ifdef SWEEP_AABB_IMPL + #ifndef GU_BV4_USE_SLABS + setupRayData(params, Dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned); + #endif +#else + params->ShrinkOBB(Dist); +#endif + } + return true; + } + } + return false; +} + +#include "GuDistanceSegmentTriangleSIMD.h" + +namespace +{ +class LeafFunction_CapsuleSweepClosest +{ +public: + static PX_FORCE_INLINE void doLeafTest(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + triCapsuleSweep(params, primIndex); + primIndex++; + }while(nbToGo--); + } +}; + +class LeafFunction_CapsuleSweepAny +{ +public: + + static PX_FORCE_INLINE Ps::IntBool doLeafTest(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triCapsuleSweep(params, primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +class ImpactFunctionCapsule +{ +public: + static PX_FORCE_INLINE void computeImpact(PxVec3& impactPos, PxVec3& impactNormal, const Capsule& capsule, const PxVec3& dir, const PxReal t, const TrianglePadded& triangle) + { + const PxVec3 delta = dir * t; + const Vec3p P0 = capsule.p0 + delta; + const Vec3p P1 = capsule.p1 + delta; + Vec3V pointOnSeg, pointOnTri; + distanceSegmentTriangleSquared( + // PT: we use Vec3p so it is safe to V4LoadU P0 and P1 + V3LoadU_SafeReadW(P0), V3LoadU_SafeReadW(P1), + // PT: we use TrianglePadded so it is safe to V4LoadU the triangle vertices + V3LoadU_SafeReadW(triangle.verts[0]), V3LoadU_SafeReadW(triangle.verts[1]), V3LoadU_SafeReadW(triangle.verts[2]), + pointOnSeg, pointOnTri); + + PxVec3 localImpactPos, tmp; + V3StoreU(pointOnTri, localImpactPos); + V3StoreU(pointOnSeg, tmp); + + // PT: TODO: refactor with computeSphereTriImpactData (TA34704) + PxVec3 localImpactNormal = tmp - localImpactPos; + const float M = localImpactNormal.magnitude(); + if(M<1e-3f) + { + localImpactNormal = (triangle.verts[0] - triangle.verts[1]).cross(triangle.verts[0] - triangle.verts[2]); + localImpactNormal.normalize(); + } + else + localImpactNormal /= M; + + impactPos = localImpactPos; + impactNormal = localImpactNormal; + } +}; +} + +static void computeBoxAroundCapsule(const Capsule& capsule, Box& box, PxVec3& extrusionDir) +{ + // Box center = center of the two capsule's endpoints + box.center = capsule.computeCenter(); + + extrusionDir = (capsule.p0 - capsule.p1)*0.5f; + const PxF32 d = extrusionDir.magnitude(); + + // Box extents + box.extents.x = capsule.radius + d; + box.extents.y = capsule.radius; + box.extents.z = capsule.radius; + + // Box orientation + if(d==0.0f) + { + box.rot = PxMat33(PxIdentity); + } + else + { + PxVec3 dir, right, up; + Ps::computeBasis(capsule.p0, capsule.p1, dir, right, up); + box.setAxes(dir, right, up); + } +} + +template +static PX_FORCE_INLINE void setupCapsuleParams(ParamsT* PX_RESTRICT params, const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh, PxU32 flags) +{ + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + params->mBestAlignmentValue = 2.0f; + params->mBestDistance = maxDist + GU_EPSILON_SAME_DISTANCE; + params->mMaxDist = maxDist; + + setupParamsFlags(params, flags); + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + params->mLocalCapsule = capsule; + + Box localBox; + computeBoxAroundCapsule(capsule, localBox, params->mExtrusionDir); + + params->mCapsuleCenter = localBox.center; + + const PxVec3& localDir = dir; + +#ifdef SWEEP_AABB_IMPL + const PxVec3& localP0 = params->mLocalCapsule.p0; + const PxVec3& localP1 = params->mLocalCapsule.p1; + const PxVec3 sweepOrigin = (localP0+localP1)*0.5f; + const PxVec3 sweepExtents = PxVec3(params->mLocalCapsule.radius) + (localP0-localP1).abs()*0.5f; + + #ifndef GU_BV4_USE_SLABS + params->mLocalDir_PaddedAligned = localDir; + #endif + params->mOrigin_Padded = sweepOrigin; + + const Box aabb(sweepOrigin, sweepExtents, PxMat33(PxIdentity)); + prepareSweepData(aabb, localDir, maxDist, params); // PT: TODO: optimize this call for idt rotation (TA34704) + + #ifndef GU_BV4_USE_SLABS + setupRayData(params, maxDist, sweepOrigin, localDir); + #endif +#else + prepareSweepData(localBox, localDir, maxDist, params); +#endif +} + +#endif // GU_BV4_CAPSULE_SWEEP_INTERNAL_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h new file mode 100644 index 000000000..3ba60c972 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h @@ -0,0 +1,452 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef GU_BV4_COMMON_H +#define GU_BV4_COMMON_H + +#include "foundation/PxMat44.h" +#include "GuBox.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuSIMDHelpers.h" + +#define BV4_ALIGN16(x) PX_ALIGN_PREFIX(16) x PX_ALIGN_SUFFIX(16) + +namespace physx +{ +namespace Gu +{ + enum QueryModifierFlag + { + QUERY_MODIFIER_ANY_HIT = (1<<0), + QUERY_MODIFIER_DOUBLE_SIDED = (1<<1), + QUERY_MODIFIER_MESH_BOTH_SIDES = (1<<2) + }; + + template + PX_FORCE_INLINE void setupParamsFlags(ParamsT* PX_RESTRICT params, PxU32 flags) + { + params->mBackfaceCulling = (flags & (QUERY_MODIFIER_DOUBLE_SIDED|QUERY_MODIFIER_MESH_BOTH_SIDES)) ? 0 : 1u; + params->mEarlyExit = flags & QUERY_MODIFIER_ANY_HIT; + } + + enum HitCode + { + HIT_NONE = 0, //!< No hit + HIT_CONTINUE = 1, //!< Hit found, but keep looking for closer one + HIT_EXIT = 2 //!< Hit found, you can early-exit (raycast any) + }; + + class RaycastHitInternal : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE RaycastHitInternal() {} + PX_FORCE_INLINE ~RaycastHitInternal() {} + + float mDistance; + PxU32 mTriangleID; + }; + + class SweepHit : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE SweepHit() {} + PX_FORCE_INLINE ~SweepHit() {} + + PxU32 mTriangleID; //!< Index of touched face + float mDistance; //!< Impact distance + + PxVec3 mPos; + PxVec3 mNormal; + }; + + typedef HitCode (*MeshRayCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, float dist, float u, float v); + typedef bool (*MeshOverlapCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* vertexIndices); + typedef bool (*MeshSweepCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist); + typedef bool (*SweepUnlimitedCallback) (void* userData, const SweepHit& hit); + + template + PX_FORCE_INLINE void reportUnlimitedCallbackHit(ParamsT* PX_RESTRICT params, const SweepHit& hit) + { + // PT: we can't reuse the MeshSweepCallback here since it's designed for doing the sweep test inside the callback + // (in the user's code) rather than inside the traversal code. So we use the SweepUnlimitedCallback instead to + // report the already fully computed hit to users. + // PT: TODO: this may not be very efficient, since computing the full hit is expensive. If we use this codepath + // to implement the Epic Tweak, the resulting code will not be optimal. + (params->mCallback)(params->mUserData, hit); + + // PT: the existing traversal code already shrunk the ray. For real "sweep all" calls we must undo that by reseting the max dist. + // (params->mStabbedFace.mDistance is used in computeImpactDataX code, so we need it before that point - we can't simply avoid + // modifying this value before this point). + if(!params->mNodeSorting) + params->mStabbedFace.mDistance = params->mMaxDist; + } + + PX_FORCE_INLINE void invertPRMatrix(PxMat44* PX_RESTRICT dest, const PxMat44* PX_RESTRICT src) + { + const float m30 = src->column3.x; + const float m31 = src->column3.y; + const float m32 = src->column3.z; + + const float m00 = src->column0.x; + const float m01 = src->column0.y; + const float m02 = src->column0.z; + + dest->column0.x = m00; + dest->column1.x = m01; + dest->column2.x = m02; + dest->column3.x = -(m30*m00 + m31*m01 + m32*m02); + + const float m10 = src->column1.x; + const float m11 = src->column1.y; + const float m12 = src->column1.z; + + dest->column0.y = m10; + dest->column1.y = m11; + dest->column2.y = m12; + dest->column3.y = -(m30*m10 + m31*m11 + m32*m12); + + const float m20 = src->column2.x; + const float m21 = src->column2.y; + const float m22 = src->column2.z; + + dest->column0.z = m20; + dest->column1.z = m21; + dest->column2.z = m22; + dest->column3.z = -(m30*m20 + m31*m21 + m32*m22); + + dest->column0.w = 0.0f; + dest->column1.w = 0.0f; + dest->column2.w = 0.0f; + dest->column3.w = 1.0f; + } + + PX_FORCE_INLINE void invertBoxMatrix(PxMat33& m, PxVec3& t, const Gu::Box& box) + { + const float m30 = box.center.x; + const float m31 = box.center.y; + const float m32 = box.center.z; + + const float m00 = box.rot.column0.x; + const float m01 = box.rot.column0.y; + const float m02 = box.rot.column0.z; + + m.column0.x = m00; + m.column1.x = m01; + m.column2.x = m02; + t.x = -(m30*m00 + m31*m01 + m32*m02); + + const float m10 = box.rot.column1.x; + const float m11 = box.rot.column1.y; + const float m12 = box.rot.column1.z; + + m.column0.y = m10; + m.column1.y = m11; + m.column2.y = m12; + t.y = -(m30*m10 + m31*m11 + m32*m12); + + const float m20 = box.rot.column2.x; + const float m21 = box.rot.column2.y; + const float m22 = box.rot.column2.z; + + m.column0.z = m20; + m.column1.z = m21; + m.column2.z = m22; + t.z = -(m30*m20 + m31*m21 + m32*m22); + } + +#ifdef GU_BV4_USE_SLABS + // PT: this class moved here to make things compile with pedantic compilers. + + // PT: now duplicated because not easy to do otherwise + + struct BVDataSwizzledQ : public physx::shdfnd::UserAllocated + { + struct Data + { + PxI16 mMin; //!< Quantized min + PxI16 mMax; //!< Quantized max + }; + + Data mX[4]; + Data mY[4]; + Data mZ[4]; + + PxU32 mData[4]; + + PX_FORCE_INLINE PxU32 isLeaf(PxU32 i) const { return mData[i]&1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return mData[i]>>1; } + PX_FORCE_INLINE PxU32 getChildOffset(PxU32 i) const { return mData[i]>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + PX_FORCE_INLINE PxU32 getChildType(PxU32 i) const { return (mData[i]>>1)&3; } + PX_FORCE_INLINE PxU32 getChildData(PxU32 i) const { return mData[i]; } + PX_FORCE_INLINE PxU32 decodePNSNoShift(PxU32 i) const { return mData[i]; } + }; + + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + struct BVDataSwizzledNQ : public physx::shdfnd::UserAllocated + { + float mMinX[4]; + float mMinY[4]; + float mMinZ[4]; + float mMaxX[4]; + float mMaxY[4]; + float mMaxZ[4]; + + PxU32 mData[4]; + + PX_FORCE_INLINE PxU32 isLeaf(PxU32 i) const { return mData[i]&1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return mData[i]>>1; } + PX_FORCE_INLINE PxU32 getChildOffset(PxU32 i) const { return mData[i]>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + PX_FORCE_INLINE PxU32 getChildType(PxU32 i) const { return (mData[i]>>1)&3; } + PX_FORCE_INLINE PxU32 getChildData(PxU32 i) const { return mData[i]; } + PX_FORCE_INLINE PxU32 decodePNSNoShift(PxU32 i) const { return mData[i]; } + }; + #endif + +#else + #define SSE_CONST4(name, val) static const __declspec(align(16)) PxU32 name[4] = { (val), (val), (val), (val) } + #define SSE_CONST(name) *(const __m128i *)&name + #define SSE_CONSTF(name) *(const __m128 *)&name +#endif + + PX_FORCE_INLINE PxU32 getNbPrimitives(PxU32& primIndex) + { + PxU32 NbToGo = (primIndex & 15)-1; + primIndex>>=4; + return NbToGo; + } + + template + PX_FORCE_INLINE void setupMeshPointersAndQuantizedCoeffs(ParamsT* PX_RESTRICT params, const SourceMesh* PX_RESTRICT mesh, const BV4Tree* PX_RESTRICT tree) + { + using namespace physx::shdfnd::aos; + + params->mTris32 = mesh->getTris32(); + params->mTris16 = mesh->getTris16(); + params->mVerts = mesh->getVerts(); + + V4StoreA_Safe(V4LoadU_Safe(&tree->mCenterOrMinCoeff.x), ¶ms->mCenterOrMinCoeff_PaddedAligned.x); + V4StoreA_Safe(V4LoadU_Safe(&tree->mExtentsOrMaxCoeff.x), ¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + } + + PX_FORCE_INLINE void rotateBox(Gu::Box& dst, const PxMat44& m, const Gu::Box& src) + { + // The extents remain constant + dst.extents = src.extents; + // The center gets x-formed + dst.center = m.transform(src.center); + // Combine rotations + // PT: TODO: revisit.. this is awkward... grab 3x3 part of 4x4 matrix (TA34704) + const PxMat33 tmp( PxVec3(m.column0.x, m.column0.y, m.column0.z), + PxVec3(m.column1.x, m.column1.y, m.column1.z), + PxVec3(m.column2.x, m.column2.y, m.column2.z)); + dst.rot = tmp * src.rot; + } + + PX_FORCE_INLINE PxVec3 inverseRotate(const PxMat44* PX_RESTRICT src, const PxVec3& p) + { + const float m00 = src->column0.x; + const float m01 = src->column0.y; + const float m02 = src->column0.z; + + const float m10 = src->column1.x; + const float m11 = src->column1.y; + const float m12 = src->column1.z; + + const float m20 = src->column2.x; + const float m21 = src->column2.y; + const float m22 = src->column2.z; + + return PxVec3( m00*p.x + m01*p.y + m02*p.z, + m10*p.x + m11*p.y + m12*p.z, + m20*p.x + m21*p.y + m22*p.z); + } + + PX_FORCE_INLINE PxVec3 inverseTransform(const PxMat44* PX_RESTRICT src, const PxVec3& p) + { + const float m30 = src->column3.x; + const float m31 = src->column3.y; + const float m32 = src->column3.z; + + const float m00 = src->column0.x; + const float m01 = src->column0.y; + const float m02 = src->column0.z; + + const float m10 = src->column1.x; + const float m11 = src->column1.y; + const float m12 = src->column1.z; + + const float m20 = src->column2.x; + const float m21 = src->column2.y; + const float m22 = src->column2.z; + + return PxVec3( m00*p.x + m01*p.y + m02*p.z -(m30*m00 + m31*m01 + m32*m02), + m10*p.x + m11*p.y + m12*p.z -(m30*m10 + m31*m11 + m32*m12), + m20*p.x + m21*p.y + m22*p.z -(m30*m20 + m31*m21 + m32*m22)); + } + + PX_FORCE_INLINE void computeLocalRay(PxVec3& localDir, PxVec3& localOrigin, const PxVec3& dir, const PxVec3& origin, const PxMat44* PX_RESTRICT worldm_Aligned) + { + if(worldm_Aligned) + { + localDir = inverseRotate(worldm_Aligned, dir); + localOrigin = inverseTransform(worldm_Aligned, origin); + } + else + { + localDir = dir; + localOrigin = origin; + } + } + + PX_FORCE_INLINE void computeLocalSphere(float& radius2, PxVec3& local_center, const Sphere& sphere, const PxMat44* PX_RESTRICT worldm_Aligned) + { + radius2 = sphere.radius * sphere.radius; + if(worldm_Aligned) + { + local_center = inverseTransform(worldm_Aligned, sphere.center); + } + else + { + local_center = sphere.center; + } + } + + PX_FORCE_INLINE void computeLocalCapsule(Capsule& localCapsule, const Capsule& capsule, const PxMat44* PX_RESTRICT worldm_Aligned) + { + localCapsule.radius = capsule.radius; + if(worldm_Aligned) + { + localCapsule.p0 = inverseTransform(worldm_Aligned, capsule.p0); + localCapsule.p1 = inverseTransform(worldm_Aligned, capsule.p1); + } + else + { + localCapsule.p0 = capsule.p0; + localCapsule.p1 = capsule.p1; + } + } + + PX_FORCE_INLINE void computeLocalBox(Gu::Box& dst, const Gu::Box& src, const PxMat44* PX_RESTRICT worldm_Aligned) + { + if(worldm_Aligned) + { + PxMat44 invWorldM; + invertPRMatrix(&invWorldM, worldm_Aligned); + + rotateBox(dst, invWorldM, src); + } + else + { + dst = src; // PT: TODO: check asm for operator= (TA34704) + } + } + + template + static PX_FORCE_INLINE bool computeImpactDataT(const ShapeT& shape, const PxVec3& dir, SweepHit* PX_RESTRICT hit, const ParamsT* PX_RESTRICT params, const PxMat44* PX_RESTRICT worldm, bool isDoubleSided, bool meshBothSides) + { + if(params->mStabbedFace.mTriangleID==PX_INVALID_U32) + return false; // We didn't touch any triangle + + if(hit) + { + const float t = params->mStabbedFace.mDistance; + hit->mTriangleID = params->mStabbedFace.mTriangleID; + hit->mDistance = t; + + if(t==0.0f) + { + hit->mPos = PxVec3(0.0f); + hit->mNormal = -dir; + } + else + { + // PT: TODO: we shouldn't compute impact in world space, and in fact moving this to local space is necessary if we want to reuse this for box-sweeps (TA34704) + TrianglePadded WP; + if(worldm) + { + WP.verts[0] = worldm->transform(params->mP0); + WP.verts[1] = worldm->transform(params->mP1); + WP.verts[2] = worldm->transform(params->mP2); + } + else + { + WP.verts[0] = params->mP0; + WP.verts[1] = params->mP1; + WP.verts[2] = params->mP2; + } + + PxVec3 impactNormal; + ImpactFunctionT::computeImpact(hit->mPos, impactNormal, shape, dir, t, WP); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(impactNormal, meshBothSides, isDoubleSided, params->mBestTriNormal, dir)) + impactNormal = -impactNormal; + + hit->mNormal = impactNormal; + } + } + return true; + } + + // PT: we don't create a structure for small meshes with just a few triangles. We use brute-force tests on these. + template + void doBruteForceTests(PxU32 nbTris, ParamsT* PX_RESTRICT params) + { + PX_ASSERT(nbTris<16); + if(params->mEarlyExit) + LeafFunction_AnyT::doLeafTest(params, nbTris); + else + LeafFunction_ClosestT::doLeafTest(params, nbTris); + } + +#if PX_INTEL_FAMILY +#ifndef GU_BV4_USE_SLABS + template + PX_FORCE_INLINE void setupRayData(ParamsT* PX_RESTRICT params, float max_dist, const PxVec3& origin, const PxVec3& dir) + { + const float Half = 0.5f*max_dist; + const FloatV HalfV = FLoad(Half); + const Vec4V DataV = V4Scale(V4LoadU(&dir.x), HalfV); + const Vec4V Data2V = V4Add(V4LoadU(&origin.x), DataV); + const PxU32 MaskI = 0x7fffffff; + const Vec4V FDirV = _mm_and_ps(_mm_load1_ps((float*)&MaskI), DataV); + V4StoreA_Safe(DataV, ¶ms->mData_PaddedAligned.x); + V4StoreA_Safe(Data2V, ¶ms->mData2_PaddedAligned.x); + V4StoreA_Safe(FDirV, ¶ms->mFDir_PaddedAligned.x); + } +#endif +#endif + +} +} + +#endif // GU_BV4_COMMON_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h new file mode 100644 index 000000000..5f24bdcbb --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h @@ -0,0 +1,318 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_INTERNAL_H +#define GU_BV4_INTERNAL_H + +#include "CmPhysXCommon.h" +#include "PsFPU.h" + + // PT: the general structure is that there is a root "process stream" function which is the entry point for the query. + // It then calls "process node" functions for each traversed node, except for the Slabs-based raycast versions that deal + // with 4 nodes at a time within the "process stream" function itself. When a leaf is found, "doLeafTest" functors + // passed to the "process stream" entry point are called. +#ifdef GU_BV4_USE_SLABS + // PT: Linux tries to compile templates even when they're not used so I had to wrap them all with defines to avoid build errors. Blame the only platform that does this. + #ifdef GU_BV4_PROCESS_STREAM_NO_ORDER + template + PX_FORCE_INLINE Ps::IntBool processStreamNoOrder(const BV4Tree& tree, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + if(tree.mQuantized) + #endif + return BV4_ProcessStreamSwizzledNoOrderQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + else + return BV4_ProcessStreamSwizzledNoOrderNQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #endif + } + #endif + + #ifdef GU_BV4_PROCESS_STREAM_ORDERED + template + PX_FORCE_INLINE void processStreamOrdered(const BV4Tree& tree, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + if(tree.mQuantized) + #endif + BV4_ProcessStreamSwizzledOrderedQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + else + BV4_ProcessStreamSwizzledOrderedNQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #endif + } + #endif + + #ifdef GU_BV4_PROCESS_STREAM_RAY_NO_ORDER + template + PX_FORCE_INLINE Ps::IntBool processStreamRayNoOrder(const BV4Tree& tree, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + if(tree.mQuantized) + #endif + return BV4_ProcessStreamKajiyaNoOrderQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + else + return BV4_ProcessStreamKajiyaNoOrderNQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #endif + } + #endif + + #ifdef GU_BV4_PROCESS_STREAM_RAY_ORDERED + template + PX_FORCE_INLINE void processStreamRayOrdered(const BV4Tree& tree, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + if(tree.mQuantized) + #endif + BV4_ProcessStreamKajiyaOrderedQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + else + BV4_ProcessStreamKajiyaOrderedNQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #endif + } + #endif +#else + #define processStreamNoOrder BV4_ProcessStreamNoOrder + #define processStreamOrdered BV4_ProcessStreamOrdered2 + #define processStreamRayNoOrder(a, b) BV4_ProcessStreamNoOrder + #define processStreamRayOrdered(a, b) BV4_ProcessStreamOrdered2 +#endif + +#ifndef GU_BV4_USE_SLABS +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + // PT: see http://www.codercorner.com/blog/?p=734 + + // PT: TODO: refactor with dup in bucket pruner (TA34704) + PX_FORCE_INLINE PxU32 computeDirMask(const PxVec3& dir) + { + // XYZ + // --- + // --+ + // -+- + // -++ + // +-- + // +-+ + // ++- + // +++ + + const PxU32 X = PX_IR(dir.x)>>31; + const PxU32 Y = PX_IR(dir.y)>>31; + const PxU32 Z = PX_IR(dir.z)>>31; + const PxU32 bitIndex = Z|(Y<<1)|(X<<2); + return 1u< + static Ps::IntBool BV4_ProcessStreamNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + const PxU32 nodeType = getChildType(childData); + + if(nodeType>1 && BV4_ProcessNodeNoOrder(stack, nb, node, params)) + return 1; + if(nodeType>0 && BV4_ProcessNodeNoOrder(stack, nb, node, params)) + return 1; + if(BV4_ProcessNodeNoOrder(stack, nb, node, params)) + return 1; + if(BV4_ProcessNodeNoOrder(stack, nb, node, params)) + return 1; + + }while(nb); + + return 0; + } + + template + static void BV4_ProcessStreamOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32 dirMask = computeDirMask(params->mLocalDir)<<3; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const PxU8* PX_RESTRICT ord = order + decodePNS(node, dirMask)*4; + const PxU32 limit = 2 + getChildType(childData); + + BV4_ProcessNodeOrdered(stack, nb, node, params, ord[0], limit); + BV4_ProcessNodeOrdered(stack, nb, node, params, ord[1], limit); + BV4_ProcessNodeOrdered(stack, nb, node, params, ord[2], limit); + BV4_ProcessNodeOrdered(stack, nb, node, params, ord[3], limit); + }while(Nb); + } + + // Alternative, experimental version using PNS + template + static void BV4_ProcessStreamOrdered2(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; + const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; + const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<(code, node, params); + BV4_ProcessNodeOrdered2(code, node, params); + if(nodeType>0) + BV4_ProcessNodeOrdered2(code, node, params); + if(nodeType>1) + BV4_ProcessNodeOrdered2(code, node, params); + + if(code) + { + // PT: TODO: check which implementation is best on each platform (TA34704) +#define FOURTH_TEST // Version avoids computing the PNS index, and also avoids all non-constant shifts. Full of branches though. Fastest on Win32. +#ifdef FOURTH_TEST + { + if(node[0].decodePNSNoShift() & dirMask) // Bit2 + { + if(node[1].decodePNSNoShift() & dirMask) // Bit1 + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(3,2,1,0) // 7 + else + PNS_BLOCK2(2,3,1,0) // 6 + } + else + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(3,2,0,1) // 5 + else + PNS_BLOCK2(2,3,0,1) // 4 + } + } + else + { + if(node[1].decodePNSNoShift() & dirMask) // Bit1 + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(1,0,3,2) // 3 + else + PNS_BLOCK2(1,0,2,3) // 2 + } + else + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(0,1,3,2) // 1 + else + PNS_BLOCK2(0,1,2,3) // 0 + } + } + } +#endif + } + }while(nb); + } +#endif // PX_INTEL_FAMILY +#endif // GU_BV4_USE_SLABS + +#endif // GU_BV4_INTERNAL_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp new file mode 100644 index 000000000..fb87e71ce --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp @@ -0,0 +1,170 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; +#include "GuBV4_BoxSweep_Internal.h" + +Ps::IntBool Sweep_AABB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags); +void GenericSweep_AABB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags); +void Sweep_AABB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting); + +// PT: TODO: optimize this (TA34704) +static PX_FORCE_INLINE void computeLocalData(Box& localBox, PxVec3& localDir, const Box& box, const PxVec3& dir, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + if(worldm_Aligned) + { + PxMat44 IWM; + invertPRMatrix(&IWM, worldm_Aligned); + + localDir = IWM.rotate(dir); + + rotateBox(localBox, IWM, box); + } + else + { + localDir = dir; + localBox = box; // PT: TODO: check asm for operator= (TA34704) + } +} + +static PX_FORCE_INLINE bool isAxisAligned(const PxVec3& axis) +{ + const PxReal minLimit = 1e-3f; + const PxReal maxLimit = 1.0f - 1e-3f; + + const PxReal absX = PxAbs(axis.x); + if(absX>minLimit && absXminLimit && absYminLimit && absZmPos = worldm_Aligned->transform(hit->mPos); + hit->mNormal = worldm_Aligned->rotate(hit->mNormal); + } + return Status; +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +void BV4_BoxSweepCB(const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +{ + Box localBox; + PxVec3 localDir; + computeLocalData(localBox, localDir, box, dir, worldm_Aligned); + + if(isAABB(localBox)) + Sweep_AABB_BV4_CB(localBox, localDir, maxDist, tree, worldm_Aligned, callback, userData, flags, nodeSorting); + else + Sweep_OBB_BV4_CB(localBox, localDir, maxDist, tree, worldm_Aligned, callback, userData, flags, nodeSorting); +} + + +// PT: this generic sweep uses an OBB because this is the most versatile volume, but it does not mean this function is +// a "box sweep function" per-se. In fact it could be used all alone to implement all sweeps in the SDK (but that would +// have an impact on performance). +// +// So the idea here is simply to provide and use a generic function for everything that the BV4 code does not support directly. +// In particular this should be used: +// - for convex sweeps (where the OBB is the box around the swept convex) +// - for non-trivial sphere/capsule/box sweeps where mesh scaling or inflation +// +// By design we don't do leaf tests inside the BV4 traversal code here (because we don't support them, e.g. convex +// sweeps. If we could do them inside the BV4 traversal code, like we do for regular sweeps, then this would not be a generic +// sweep function, but instead a built-in, natively supported query). So the leaf tests are performed outside of BV4, in the +// client code, through MeshSweepCallback. This has a direct impact on the design & parameters of MeshSweepCallback. +// +// On the other hand this is used for "regular sweeps with shapes we don't natively support", i.e. SweepSingle kind of queries. +// This means that we need to support an early-exit codepath (without node-sorting) and a regular sweep single codepath (with +// node sorting) for this generic function. The leaf tests are external, but everything traversal-related should be exactly the +// same as the regular box-sweep function otherwise. +// +// As a consequence, this function is not well-suited to implement "unlimited results" kind of queries, a.k.a. "sweep all": +// +// - for regular sphere/capsule/box "sweep all" queries, the leaf tests should be internal (same as sweep single queries). This +// means the existing MeshSweepCallback can't be reused. +// +// - there is no need to support "sweep any" (it is already supported by the other sweep functions). +// +// - there may be no need for ordered traversal/node sorting/ray shrinking, since we want to return all results anyway. But this +// may not be true if the "sweep all" function is used to emulate the Epic Tweak. In that case we still want to shrink the ray +// and use node sorting. Since both versions are useful, we should probably have a bool param to enable/disable node sorting. +// +// - we are interested in all hits so we can't delay the computation of impact data (computing it only once in the end, for the +// closest hit). We actually need to compute the data for all hits, possibly within the traversal code. +void BV4_GenericSweepCB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, bool anyHit) +{ + const PxU32 flags = anyHit ? PxU32(QUERY_MODIFIER_ANY_HIT) : 0; + + if(isAABB(localBox)) + GenericSweep_AABB_CB(localBox, localDir, maxDist, tree, callback, userData, flags); + else + GenericSweep_OBB_CB(localBox, localDir, maxDist, tree, callback, userData, flags); +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h new file mode 100644 index 000000000..47093e431 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h @@ -0,0 +1,111 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H +#define GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H + +#ifdef GU_BV4_USE_SLABS +/* template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_Swizzled(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CE(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + }*/ + + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_SwizzledQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CEQ(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + } + + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_SwizzledNQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledNQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CENQ(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + } + #endif +#else + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_BoxBoxOverlap(node+i, params)) + #else + if(BV4_BoxBoxOverlap(node[i].mAABB.mExtents, node[i].mAABB.mCenter, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h new file mode 100644 index 000000000..327c4105e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h @@ -0,0 +1,55 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H +#define GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H + +#ifndef GU_BV4_USE_SLABS + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h new file mode 100644 index 000000000..a613ffdbd --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h @@ -0,0 +1,55 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H +#define GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H + +#ifndef GU_BV4_USE_SLABS + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params->mOriginalExtents_Padded, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params->mOriginalExtents_Padded, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h new file mode 100644 index 000000000..bce32cf01 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h @@ -0,0 +1,113 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H +#define GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H + +#ifdef GU_BV4_USE_SLABS +/* template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_Swizzled(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { +// OPC_SLABS_GET_CE(i) + OPC_SLABS_GET_CE2(i) + + if(BV4_SphereAABBOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + }*/ + + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_SwizzledQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CE2Q(i) + + if(BV4_SphereAABBOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + } + + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_SwizzledNQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledNQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CE2NQ(i) + + if(BV4_SphereAABBOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + } + #endif + +#else + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SphereAABBOverlap(node+i, params)) + #else + if(BV4_SphereAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h new file mode 100644 index 000000000..ce1ec85ff --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h @@ -0,0 +1,111 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_ORDERED_OBB_OBB_H +#define GU_BV4_PROCESS_STREAM_ORDERED_OBB_OBB_H + +#ifdef GU_BV4_USE_SLABS +/* template + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2_Swizzled(PxU32& code, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CE(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + LeafTestT::doLeafTest(params, node->getPrimitive(i)); + else + code |= 1< + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2_SwizzledQ(PxU32& code, const BVDataSwizzledQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CEQ(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + LeafTestT::doLeafTest(params, node->getPrimitive(i)); + else + code |= 1< + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2_SwizzledNQ(PxU32& code, const BVDataSwizzledNQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CENQ(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + LeafTestT::doLeafTest(params, node->getPrimitive(i)); + else + code |= 1< + PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(i + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_BoxBoxOverlap(node+i, params)) + #else + if(BV4_BoxBoxOverlap(node[i].mAABB.mExtents, node[i].mAABB.mCenter, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + code |= 1< + PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(i + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + code |= 1< + PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(imOriginalExtents, params)) + #else + if(imOriginalExtents, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + Stack[Nb++] = node[i].getChildData(); + } + } + + template + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params->mOriginalExtents_Padded, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params->mOriginalExtents_Padded, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + code |= 1< +PX_FORCE_INLINE Ps::IntBool RayTriOverlapT(PxRaycastHit& mStabbedFace, const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, const T* PX_RESTRICT params) +{ + // Find vectors for two edges sharing vert0 + const PxVec3 edge1 = vert1 - vert0; + const PxVec3 edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = params->mLocalDir_Padded.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const float det = edge1.dot(pvec); + + if(params->mBackfaceCulling) + { + if(detmOrigin_Padded - vert0; + + // Calculate U parameter and test bounds + const float u = tvec.dot(pvec); + + const PxReal enlargeCoeff = params->mGeomEpsilon*det; + const PxReal uvlimit = -enlargeCoeff; + const PxReal uvlimit2 = det + enlargeCoeff; + + if(u < uvlimit || u > uvlimit2) + return 0; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const float v = params->mLocalDir_Padded.dot(qvec); + if(v < uvlimit || (u + v) > uvlimit2) + return 0; + + // Calculate t, scale parameters, ray intersects triangle + const float d = edge2.dot(qvec); + // Det > 0 so we can early exit here + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(d<0.0f) + return 0; + + // Else go on + const float OneOverDet = 1.0f / det; + mStabbedFace.distance = d * OneOverDet; + mStabbedFace.u = u * OneOverDet; + mStabbedFace.v = v * OneOverDet; + } + else + { + if(PxAbs(det)mOrigin_Padded - vert0; + + const float u = tvec.dot(pvec) * OneOverDet; + if(u<-params->mGeomEpsilon || u>1.0f+params->mGeomEpsilon) + return 0; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const float v = params->mLocalDir_Padded.dot(qvec) * OneOverDet; + if(v < -params->mGeomEpsilon || (u + v) > 1.0f + params->mGeomEpsilon) + return 0; + + // Calculate t, ray intersects triangle + const float d = edge2.dot(qvec) * OneOverDet; + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(d<0.0f) + return 0; + mStabbedFace.distance = d; + mStabbedFace.u = u; + mStabbedFace.v = v; + } + return 1; +} + +#if PX_VC +#pragma warning ( disable : 4324 ) +#endif + +struct RayParams +{ + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); +// Organized in the order they are accessed +#ifndef GU_BV4_USE_SLABS + BV4_ALIGN16(Vec3p mData2_PaddedAligned); + BV4_ALIGN16(Vec3p mFDir_PaddedAligned); + BV4_ALIGN16(Vec3p mData_PaddedAligned); +#endif + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + PxVec3 mLocalDir_Padded; + PxVec3 mOrigin_Padded; + + float mGeomEpsilon; + PxU32 mBackfaceCulling; + + RaycastHitInternalUV mStabbedFace; + PxU32 mEarlyExit; + + PxVec3 mOriginalExtents_Padded; // Added to please the slabs code + + BV4_ALIGN16(Vec3p mP0_PaddedAligned); + BV4_ALIGN16(Vec3p mP1_PaddedAligned); + BV4_ALIGN16(Vec3p mP2_PaddedAligned); +}; + +/////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE void updateParamsAfterImpact(RayParams* PX_RESTRICT params, PxU32 primIndex, PxU32 VRef0, PxU32 VRef1, PxU32 VRef2, const PxRaycastHit& StabbedFace) +{ + V4StoreA_Safe(V4LoadU_Safe(¶ms->mVerts[VRef0].x), ¶ms->mP0_PaddedAligned.x); + V4StoreA_Safe(V4LoadU_Safe(¶ms->mVerts[VRef1].x), ¶ms->mP1_PaddedAligned.x); + V4StoreA_Safe(V4LoadU_Safe(¶ms->mVerts[VRef2].x), ¶ms->mP2_PaddedAligned.x); + + params->mStabbedFace.mTriangleID = primIndex; + params->mStabbedFace.mDistance = StabbedFace.distance; + params->mStabbedFace.mU = StabbedFace.u; + params->mStabbedFace.mV = StabbedFace.v; +} + +namespace +{ +class LeafFunction_RaycastClosest +{ +public: + static /*PX_FORCE_INLINE*/ Ps::IntBool doLeafTest(RayParams* PX_RESTRICT params, PxU32 primIndex) + { + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& StabbedFace = reinterpret_cast(buffer); + + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(RayTriOverlapT(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params)) + { + if(StabbedFace.distancemStabbedFace.mDistance) //### just for a corner case UT in PhysX :( + { + updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace); + +#ifndef GU_BV4_USE_SLABS + setupRayData(params, StabbedFace.distance, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif + } + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +class LeafFunction_RaycastAny +{ +public: + static /*PX_FORCE_INLINE*/ Ps::IntBool doLeafTest(RayParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& StabbedFace = reinterpret_cast(buffer); + if(RayTriOverlapT(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params)) + { + if(StabbedFace.distancemStabbedFace.mDistance) //### just for a corner case UT in PhysX :( + { + updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace); + return 1; + } + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +static PX_FORCE_INLINE Vec4V multiply3x3V_Aligned(const Vec4V p, const PxMat44* PX_RESTRICT mat) +{ + const FloatV xxxV = V4GetX(p); + const FloatV yyyV = V4GetY(p); + const FloatV zzzV = V4GetZ(p); + + Vec4V ResV = V4Scale(V4LoadA(&mat->column0.x), xxxV); + ResV = V4Add(ResV, V4Scale(V4LoadA(&mat->column1.x), yyyV)); + ResV = V4Add(ResV, V4Scale(V4LoadA(&mat->column2.x), zzzV)); + return ResV; +} + +static PX_FORCE_INLINE Ps::IntBool computeImpactData(PxRaycastHit* PX_RESTRICT hit, const RayParams* PX_RESTRICT params, const PxMat44* PX_RESTRICT worldm_Aligned, PxHitFlags /*hitFlags*/) +{ + if(params->mStabbedFace.mTriangleID!=PX_INVALID_U32 /*&& !params->mEarlyExit*/) //### PhysX needs the raycast data even for "any hit" :( + { + const float u = params->mStabbedFace.mU; + const float v = params->mStabbedFace.mV; + const float d = params->mStabbedFace.mDistance; + const PxU32 id = params->mStabbedFace.mTriangleID; + hit->u = u; + hit->v = v; + hit->distance = d; + hit->faceIndex = id; + + { + const Vec4V P0V = V4LoadA_Safe(¶ms->mP0_PaddedAligned.x); + const Vec4V P1V = V4LoadA_Safe(¶ms->mP1_PaddedAligned.x); + const Vec4V P2V = V4LoadA_Safe(¶ms->mP2_PaddedAligned.x); + + const FloatV uV = FLoad(params->mStabbedFace.mU); + const FloatV vV = FLoad(params->mStabbedFace.mV); + const float w = 1.0f - params->mStabbedFace.mU - params->mStabbedFace.mV; + const FloatV wV = FLoad(w); + //pt = (1.0f - u - v)*p0 + u*p1 + v*p2; + Vec4V LocalPtV = V4Scale(P1V, uV); + LocalPtV = V4Add(LocalPtV, V4Scale(P2V, vV)); + LocalPtV = V4Add(LocalPtV, V4Scale(P0V, wV)); + + const Vec4V LocalNormalV = V4Cross(V4Sub(P0V, P1V), V4Sub(P0V, P2V)); + + BV4_ALIGN16(Vec3p tmp_PaddedAligned); + if(worldm_Aligned) + { + const Vec4V TransV = V4LoadA(&worldm_Aligned->column3.x); + V4StoreU_Safe(V4Add(multiply3x3V_Aligned(LocalPtV, worldm_Aligned), TransV), &hit->position.x); + V4StoreA_Safe(multiply3x3V_Aligned(LocalNormalV, worldm_Aligned), &tmp_PaddedAligned.x); + } + else + { + V4StoreU_Safe(LocalPtV, &hit->position.x); + V4StoreA_Safe(LocalNormalV, &tmp_PaddedAligned.x); + } + tmp_PaddedAligned.normalize(); + hit->normal = tmp_PaddedAligned; // PT: TODO: check asm here (TA34704) + } + } + return params->mStabbedFace.mTriangleID!=PX_INVALID_U32; +} + +static PX_FORCE_INLINE float clipRay(const PxVec3& ray_orig, const PxVec3& ray_dir, const LocalBounds& local_bounds) +{ + const float dpc = local_bounds.mCenter.dot(ray_dir); + const float dpMin = dpc - local_bounds.mExtentsMagnitude; + const float dpMax = dpc + local_bounds.mExtentsMagnitude; + const float dpO = ray_orig.dot(ray_dir); + const float boxLength = local_bounds.mExtentsMagnitude * 2.0f; + const float distToBox = PxMin(fabsf(dpMin - dpO), fabsf(dpMax - dpO)); + return distToBox + boxLength * 2.0f; +} + +template +static PX_FORCE_INLINE void setupRayParams(ParamsT* PX_RESTRICT params, const PxVec3& origin, const PxVec3& dir, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT world, const SourceMesh* PX_RESTRICT mesh, float maxDist, float geomEpsilon, PxU32 flags) +{ + params->mGeomEpsilon = geomEpsilon; + setupParamsFlags(params, flags); + + computeLocalRay(params->mLocalDir_Padded, params->mOrigin_Padded, dir, origin, world); + + // PT: TODO: clipRay may not be needed with GU_BV4_USE_SLABS (TA34704) + const float MaxDist = clipRay(params->mOrigin_Padded, params->mLocalDir_Padded, tree->mLocalBounds); + maxDist = PxMin(maxDist, MaxDist); + params->mStabbedFace.mDistance = maxDist; + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + +#ifndef GU_BV4_USE_SLABS + setupRayData(params, maxDist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif +} + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamOrdered_SegmentAABB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_KajiyaNoOrder.h" + #include "GuBV4_Slabs_KajiyaOrdered.h" +#endif + +#define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER +#define GU_BV4_PROCESS_STREAM_RAY_ORDERED +#include "GuBV4_Internal.h" + +#ifndef GU_BV4_USE_SLABS +#ifdef GU_BV4_QUANTIZED_TREE + +#define NEW_VERSION + +static PX_FORCE_INLINE /*PX_NOINLINE*/ Ps::IntBool BV4_SegmentAABBOverlap(const BVDataPacked* PX_RESTRICT node, const RayParams* PX_RESTRICT params) +{ +#ifdef NEW_VERSION + SSE_CONST4(maskV, 0x7fffffff); + SSE_CONST4(maskQV, 0x0000ffff); +#else + const PxU32 maskI = 0x7fffffff; +#endif + + Vec4V centerV = V4LoadA((float*)node->mAABB.mData); +#ifdef NEW_VERSION + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), SSE_CONST(maskQV))); +#else + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), _mm_set1_epi32(0x0000ffff))); +#endif + extentsV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(extentsV)), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + centerV = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(centerV), 16)); + centerV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(centerV)), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), centerV); + +#ifdef NEW_VERSION + __m128 absDV = _mm_and_ps(DV, SSE_CONSTF(maskV)); +#else + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); +#endif + + absDV = V4Sub(V4Add(extentsV, fdirV), absDV); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + +#ifdef NEW_VERSION + __m128 absfV = _mm_and_ps(fV, SSE_CONSTF(maskV)); +#else + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); +#endif + absfV = V4Sub(fg, absfV); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + + if(test2&7) + return 0; + return 1; + } +} +#else +static PX_FORCE_INLINE /*PX_NOINLINE*/ Ps::IntBool BV4_SegmentAABBOverlap(const PxVec3& center, const PxVec3& extents, const RayParams* PX_RESTRICT params) +{ + const PxU32 maskI = 0x7fffffff; + + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V extentsV = V4LoadU(&extents.x); + + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), V4LoadU(¢er.x)); //###center should be aligned + + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); + + absDV = _mm_cmpgt_ps(absDV, V4Add(extentsV, fdirV)); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + if(test2&7) + return 0; + return 1; + } +} +#endif +#endif + +Ps::IntBool BV4_RaycastSingle(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hit, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + RayParams Params; + setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamRayNoOrder<0, LeafFunction_RaycastAny>(tree, &Params); + else + processStreamRayOrdered<0, LeafFunction_RaycastClosest>(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); + + return computeImpactData(hit, &Params, worldm_Aligned, hitFlags); +} + + + +// Callback-based version + +namespace +{ + +struct RayParamsCB : RayParams +{ + MeshRayCallback mCallback; + void* mUserData; +}; + +class LeafFunction_RaycastCB +{ +public: + static Ps::IntBool doLeafTest(RayParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& StabbedFace = reinterpret_cast(buffer); + if(RayTriOverlapT(StabbedFace, p0, p1, p2, params)) + { + HitCode Code = (params->mCallback)(params->mUserData, p0, p1, p2, primIndex, StabbedFace.distance, StabbedFace.u, StabbedFace.v); + if(Code==HIT_EXIT) + return 1; + + // PT: TODO: no shrinking here? (TA34704) + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +} + +#include "GuBV4_ProcessStreamNoOrder_SegmentAABB.h" + +void BV4_RaycastCB(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, float maxDist, float geomEpsilon, PxU32 flags, MeshRayCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + //### beware, some parameters in the struct aren't used + RayParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags); + + if(tree.mNodes) + processStreamRayNoOrder<0, LeafFunction_RaycastCB>(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); +// if(Params.mEarlyExit) +// LeafFunction_BoxSweepAnyCB::doLeafTest(&Params, nbTris); +// else + LeafFunction_RaycastCB::doLeafTest(&Params, nbTris); + } +} + +// Raycast all + +namespace +{ +struct RayParamsAll : RayParams +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxRaycastHit* mHits; + const PxMat44* mWorld_Aligned; + PxHitFlags mHitFlags; +}; + +class LeafFunction_RaycastAll +{ +public: + static /*PX_FORCE_INLINE*/ Ps::IntBool doLeafTest(RayParams* PX_RESTRICT p, PxU32 primIndex) + { + RayParamsAll* params = static_cast(p); + + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + PxRaycastHit& StabbedFace = params->mHits[params->mNbHits]; + if(RayTriOverlapT(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params)) + { + updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace); + + computeImpactData(&StabbedFace, params, params->mWorld_Aligned, params->mHitFlags); + + params->mNbHits++; + if(params->mNbHits==params->mMaxNbHits) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: this function is not used yet, but eventually it should be +PxU32 BV4_RaycastAll(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hits, PxU32 maxNbHits, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + RayParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = maxNbHits; + Params.mHits = hits; + Params.mWorld_Aligned = worldm_Aligned; + Params.mHitFlags = hitFlags; + setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags); + + if(tree.mNodes) + processStreamRayNoOrder<0, LeafFunction_RaycastAll>(tree, &Params); + else + { + PX_ASSERT(0); + } + return Params.mNbHits; +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h new file mode 100644 index 000000000..23ed4288e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h @@ -0,0 +1,193 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_H +#define GU_BV4_SLABS_H + +#include "PsFPU.h" +#include "GuBV4_Common.h" + +#ifdef GU_BV4_USE_SLABS + + // PT: contains code for tree-traversal using the swizzled format. + // PT: ray traversal based on Kay & Kajiya's slab intersection code, but using SIMD to do 4 ray-vs-AABB tests at a time. + // PT: other (ordered or unordered) traversals just process one node at a time, similar to the non-swizzled format. + + #define BV4_SLABS_FIX + #define BV4_SLABS_SORT + + #define PNS_BLOCK3(a, b, c, d) { \ + if(code2 & (1<getChildData(a); } \ + if(code2 & (1<getChildData(b); } \ + if(code2 & (1<getChildData(c); } \ + if(code2 & (1<getChildData(d); } } \ + + #define OPC_SLABS_GET_MIN_MAX(i) \ + const __m128i minVi = _mm_set_epi32(0, node->mZ[i].mMin, node->mY[i].mMin, node->mX[i].mMin); \ + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); \ + Vec4V minV = V4Mul(_mm_cvtepi32_ps(minVi), minCoeffV); \ + const __m128i maxVi = _mm_set_epi32(0, node->mZ[i].mMax, node->mY[i].mMax, node->mX[i].mMax); \ + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); \ + Vec4V maxV = V4Mul(_mm_cvtepi32_ps(maxVi), maxCoeffV); \ + + #define OPC_SLABS_GET_CEQ(i) \ + OPC_SLABS_GET_MIN_MAX(i) \ + const FloatV HalfV = FLoad(0.5f); \ + const Vec4V centerV = V4Scale(V4Add(maxV, minV), HalfV); \ + const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), HalfV); + + #define OPC_SLABS_GET_CE2Q(i) \ + OPC_SLABS_GET_MIN_MAX(i) \ + const Vec4V centerV = V4Add(maxV, minV); \ + const Vec4V extentsV = V4Sub(maxV, minV); + + #define OPC_SLABS_GET_CENQ(i) \ + const FloatV HalfV = FLoad(0.5f); \ + const Vec4V minV = _mm_set_ps(0.0f, node->mMinZ[i], node->mMinY[i], node->mMinX[i]); \ + const Vec4V maxV = _mm_set_ps(0.0f, node->mMaxZ[i], node->mMaxY[i], node->mMaxX[i]); \ + const Vec4V centerV = V4Scale(V4Add(maxV, minV), HalfV); \ + const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), HalfV); + + #define OPC_SLABS_GET_CE2NQ(i) \ + const Vec4V minV = _mm_set_ps(0.0f, node->mMinZ[i], node->mMinY[i], node->mMinX[i]); \ + const Vec4V maxV = _mm_set_ps(0.0f, node->mMaxZ[i], node->mMaxY[i], node->mMaxX[i]); \ + const Vec4V centerV = V4Add(maxV, minV); \ + const Vec4V extentsV = V4Sub(maxV, minV); + +#if PX_PS4 + // PT: TODO: for some reason using the intrinsics directly produces a compile error on PS4. TODO: find a better fix. + PX_FORCE_INLINE __m128i my_mm_srai_epi32(__m128i a, int count) + { + return _mm_srai_epi32(a, count); + } + + PX_FORCE_INLINE __m128i my_mm_slli_epi32(__m128i a, int count) + { + return _mm_slli_epi32(a, count); + } +#else + #define my_mm_srai_epi32 _mm_srai_epi32 + #define my_mm_slli_epi32 _mm_slli_epi32 +#endif + +#define OPC_DEQ4(part2xV, part1xV, mMember, minCoeff, maxCoeff) \ +{ \ + part2xV = V4LoadA(reinterpret_cast(tn->mMember)); \ + part1xV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(part2xV), _mm_set1_epi32(0x0000ffff))); \ + part1xV = _mm_castsi128_ps(my_mm_srai_epi32(my_mm_slli_epi32(_mm_castps_si128(part1xV), 16), 16)); \ + part1xV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(part1xV)), minCoeff); \ + part2xV = _mm_castsi128_ps(my_mm_srai_epi32(_mm_castps_si128(part2xV), 16)); \ + part2xV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(part2xV)), maxCoeff); \ +} + +#define SLABS_INIT\ + Vec4V maxT4 = V4Load(params->mStabbedFace.mDistance);\ + const Vec4V rayP = V4LoadU_Safe(¶ms->mOrigin_Padded.x);\ + Vec4V rayD = V4LoadU_Safe(¶ms->mLocalDir_Padded.x);\ + const VecU32V raySign = V4U32and(VecU32V_ReinterpretFrom_Vec4V(rayD), signMask);\ + const Vec4V rayDAbs = V4Abs(rayD);\ + Vec4V rayInvD = Vec4V_ReinterpretFrom_VecU32V(V4U32or(raySign, VecU32V_ReinterpretFrom_Vec4V(V4Max(rayDAbs, epsFloat4))));\ + rayD = rayInvD;\ + rayInvD = V4RecipFast(rayInvD);\ + rayInvD = V4Mul(rayInvD, V4NegMulSub(rayD, rayInvD, twos));\ + const Vec4V rayPinvD = V4NegMulSub(rayInvD, rayP, zeroes);\ + const Vec4V rayInvDsplatX = V4SplatElement<0>(rayInvD);\ + const Vec4V rayInvDsplatY = V4SplatElement<1>(rayInvD);\ + const Vec4V rayInvDsplatZ = V4SplatElement<2>(rayInvD);\ + const Vec4V rayPinvDsplatX = V4SplatElement<0>(rayPinvD);\ + const Vec4V rayPinvDsplatY = V4SplatElement<1>(rayPinvD);\ + const Vec4V rayPinvDsplatZ = V4SplatElement<2>(rayPinvD); + +#define SLABS_TEST\ + const Vec4V tminxa0 = V4MulAdd(minx4a, rayInvDsplatX, rayPinvDsplatX);\ + const Vec4V tminya0 = V4MulAdd(miny4a, rayInvDsplatY, rayPinvDsplatY);\ + const Vec4V tminza0 = V4MulAdd(minz4a, rayInvDsplatZ, rayPinvDsplatZ);\ + const Vec4V tmaxxa0 = V4MulAdd(maxx4a, rayInvDsplatX, rayPinvDsplatX);\ + const Vec4V tmaxya0 = V4MulAdd(maxy4a, rayInvDsplatY, rayPinvDsplatY);\ + const Vec4V tmaxza0 = V4MulAdd(maxz4a, rayInvDsplatZ, rayPinvDsplatZ);\ + const Vec4V tminxa = V4Min(tminxa0, tmaxxa0);\ + const Vec4V tmaxxa = V4Max(tminxa0, tmaxxa0);\ + const Vec4V tminya = V4Min(tminya0, tmaxya0);\ + const Vec4V tmaxya = V4Max(tminya0, tmaxya0);\ + const Vec4V tminza = V4Min(tminza0, tmaxza0);\ + const Vec4V tmaxza = V4Max(tminza0, tmaxza0);\ + const Vec4V maxOfNeasa = V4Max(V4Max(tminxa, tminya), tminza);\ + const Vec4V minOfFarsa = V4Min(V4Min(tmaxxa, tmaxya), tmaxza);\ + + #define SLABS_TEST2\ + __m128 ignore4a = _mm_cmpgt_ps(epsFloat4, minOfFarsa); /* if tfar is negative, ignore since its a ray, not a line */\ + ignore4a = _mm_or_ps(ignore4a, _mm_cmpgt_ps(maxOfNeasa, maxT4)); /* if tnear is over maxT, ignore this result */\ + __m128 resa4 = _mm_cmpgt_ps(maxOfNeasa, minOfFarsa); /* if 1 => fail */\ + resa4 = _mm_or_ps(resa4, ignore4a);\ + const int code = _mm_movemask_ps(resa4);\ + if(code==15)\ + continue; + +#define SLABS_PNS \ + if(code2) \ + { \ + if(tn->decodePNSNoShift(0) & dirMask) \ + { \ + if(tn->decodePNSNoShift(1) & dirMask) \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(3,2,1,0) \ + else \ + PNS_BLOCK3(2,3,1,0) \ + } \ + else \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(3,2,0,1) \ + else \ + PNS_BLOCK3(2,3,0,1) \ + } \ + } \ + else \ + { \ + if(tn->decodePNSNoShift(1) & dirMask) \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(1,0,3,2) \ + else \ + PNS_BLOCK3(1,0,2,3) \ + } \ + else \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(0,1,3,2) \ + else \ + PNS_BLOCK3(0,1,2,3) \ + } \ + } \ + } + +#endif // GU_BV4_USE_SLABS + +#endif // GU_BV4_SLABS_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h new file mode 100644 index 000000000..a36f76d97 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h @@ -0,0 +1,309 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_KAJIYA_NO_ORDER_H +#define GU_BV4_SLABS_KAJIYA_NO_ORDER_H + +#include "GuBVConstants.h" + +#ifdef REMOVED + // Kajiya, no sort + template + static Ps::IntBool BV4_ProcessStreamKajiyaNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + /// + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + if(inflateT) + { + Vec4V fattenAABBs4 = V4LoadU_Safe(¶ms->mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + +#ifdef GU_BV4_QUANTIZED_TREE + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV); + const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV); + const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV); + const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV); + const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV); + const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV); +#endif + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzled* tn = reinterpret_cast(node); + +#ifdef GU_BV4_QUANTIZED_TREE + Vec4V minx4a; + Vec4V maxx4a; + OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV) + + Vec4V miny4a; + Vec4V maxy4a; + OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV) + + Vec4V minz4a; + Vec4V maxz4a; + OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV) +#else + Vec4V minx4a = V4LoadA(tn->mMinX); + Vec4V miny4a = V4LoadA(tn->mMinY); + Vec4V minz4a = V4LoadA(tn->mMinZ); + + Vec4V maxx4a = V4LoadA(tn->mMaxX); + Vec4V maxy4a = V4LoadA(tn->mMaxY); + Vec4V maxz4a = V4LoadA(tn->mMaxZ); +#endif + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + + SLABS_TEST2 + +#define DO_LEAF_TEST(x) \ + {if(tn->isLeaf(x)) \ + { \ + if(LeafTestT::doLeafTest(params, tn->getPrimitive(x))) \ + return 1; \ + } \ + else \ + stack[nb++] = tn->getChildData(x);} + + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + }while(nb); + + return 0; + } +#undef DO_LEAF_TEST +#endif + + + + + +#define DO_LEAF_TEST(x) \ + {if(tn->isLeaf(x)) \ + { \ + if(LeafTestT::doLeafTest(params, tn->getPrimitive(x))) \ + return 1; \ + } \ + else \ + stack[nb++] = tn->getChildData(x);} + + + // Kajiya, no sort + template + static Ps::IntBool BV4_ProcessStreamKajiyaNoOrderQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + /// + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + if(inflateT) + { + Vec4V fattenAABBs4 = V4LoadU_Safe(¶ms->mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV); + const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV); + const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV); + const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV); + const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV); + const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV); + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledQ* tn = reinterpret_cast(node); + + Vec4V minx4a; + Vec4V maxx4a; + OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV) + + Vec4V miny4a; + Vec4V maxy4a; + OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV) + + Vec4V minz4a; + Vec4V maxz4a; + OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV) + + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + + SLABS_TEST2 + + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + }while(nb); + + return 0; + } + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + // Kajiya, no sort + template + static Ps::IntBool BV4_ProcessStreamKajiyaNoOrderNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedNQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + /// + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + if(inflateT) + { + Vec4V fattenAABBs4 = V4LoadU_Safe(¶ms->mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledNQ* tn = reinterpret_cast(node); + + Vec4V minx4a = V4LoadA(tn->mMinX); + Vec4V miny4a = V4LoadA(tn->mMinY); + Vec4V minz4a = V4LoadA(tn->mMinZ); + + Vec4V maxx4a = V4LoadA(tn->mMaxX); + Vec4V maxy4a = V4LoadA(tn->mMaxY); + Vec4V maxz4a = V4LoadA(tn->mMaxZ); + + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + + SLABS_TEST2 + + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + }while(nb); + + return 0; + } +#endif + +#undef DO_LEAF_TEST + +#endif // GU_BV4_SLABS_KAJIYA_NO_ORDER_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h new file mode 100644 index 000000000..e9d3ec241 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h @@ -0,0 +1,552 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_KAJIYA_ORDERED_H +#define GU_BV4_SLABS_KAJIYA_ORDERED_H + +#include "GuBVConstants.h" + +#ifdef REMOVED + // Kajiya + PNS + template + static void BV4_ProcessStreamKajiyaOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + +#ifdef BV4_SLABS_SORT + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + +#ifdef GU_BV4_QUANTIZED_TREE + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV); + const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV); + const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV); + const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV); + const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV); + const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV); +#endif + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzled* tn = reinterpret_cast(node); + +#ifdef GU_BV4_QUANTIZED_TREE + Vec4V minx4a; + Vec4V maxx4a; + OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV) + + Vec4V miny4a; + Vec4V maxy4a; + OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV) + + Vec4V minz4a; + Vec4V maxz4a; + OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV) +#else + Vec4V minx4a = V4LoadA(tn->mMinX); + Vec4V miny4a = V4LoadA(tn->mMinY); + Vec4V minz4a = V4LoadA(tn->mMinZ); + + Vec4V maxx4a = V4LoadA(tn->mMaxX); + Vec4V maxy4a = V4LoadA(tn->mMaxY); + Vec4V maxz4a = V4LoadA(tn->mMaxZ); +#endif + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + +#ifdef BV4_SLABS_FIX + if(inflateT) + _mm_store_ps(distances4, maxOfNeasa); +#endif + + SLABS_TEST2 + +#ifdef BV4_SLABS_SORT + #ifdef BV4_SLABS_FIX + // PT: for some unknown reason the PS4/Linux/OSX compilers fail to understand this version +/* #define DO_LEAF_TEST(x) \ + { \ + if(!inflateT) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<mStabbedFace.mDistance) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<mStabbedFace.mDistance + GU_EPSILON_SAME_DISTANCE) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + SLABS_PNS +#else + #define DO_LEAF_TEST(x) \ + {if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + stack[nb++] = tn->getChildData(x); \ + }} + + + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) +#endif + + }while(nb); + } +#undef DO_LEAF_TEST +#endif + + + +#ifdef BV4_SLABS_SORT + #ifdef BV4_SLABS_FIX + // PT: for some unknown reason the PS4/Linux/OSX compilers fail to understand this version +/* #define DO_LEAF_TEST(x) \ + { \ + if(!inflateT) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<mStabbedFace.mDistance) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<mStabbedFace.mDistance + GU_EPSILON_SAME_DISTANCE) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + stack[nb++] = tn->getChildData(x); \ + }} +#endif + + // Kajiya + PNS + template + static void BV4_ProcessStreamKajiyaOrderedQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + +#ifdef BV4_SLABS_SORT + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV); + const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV); + const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV); + const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV); + const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV); + const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV); + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledQ* tn = reinterpret_cast(node); + + Vec4V minx4a; + Vec4V maxx4a; + OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV) + + Vec4V miny4a; + Vec4V maxy4a; + OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV) + + Vec4V minz4a; + Vec4V maxz4a; + OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV) + + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + +#ifdef BV4_SLABS_FIX + if(inflateT) + _mm_store_ps(distances4, maxOfNeasa); +#endif + + SLABS_TEST2 + +#ifdef BV4_SLABS_SORT + PxU32 code2 = 0; + const PxU32 nodeType = getChildType(childData); + + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + SLABS_PNS +#else + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) +#endif + + }while(nb); + } + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + // Kajiya + PNS + template + static void BV4_ProcessStreamKajiyaOrderedNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedNQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + +#ifdef BV4_SLABS_SORT + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledNQ* tn = reinterpret_cast(node); + + Vec4V minx4a = V4LoadA(tn->mMinX); + Vec4V miny4a = V4LoadA(tn->mMinY); + Vec4V minz4a = V4LoadA(tn->mMinZ); + + Vec4V maxx4a = V4LoadA(tn->mMaxX); + Vec4V maxy4a = V4LoadA(tn->mMaxY); + Vec4V maxz4a = V4LoadA(tn->mMaxZ); + + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + +#ifdef BV4_SLABS_FIX + if(inflateT) + _mm_store_ps(distances4, maxOfNeasa); +#endif + + SLABS_TEST2 + +#ifdef BV4_SLABS_SORT + PxU32 code2 = 0; + const PxU32 nodeType = getChildType(childData); + + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + SLABS_PNS +#else + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) +#endif + + }while(nb); + } +#endif +#undef DO_LEAF_TEST + +#endif // GU_BV4_SLABS_KAJIYA_ORDERED_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h new file mode 100644 index 000000000..decaebad4 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h @@ -0,0 +1,133 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_SWIZZLED_NO_ORDER_H +#define GU_BV4_SLABS_SWIZZLED_NO_ORDER_H + + // Generic, no sort +/* template + static Ps::IntBool BV4_ProcessStreamSwizzledNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzled* tn = reinterpret_cast(node); + + const PxU32 nodeType = getChildType(childData); + + if(nodeType>1 && BV4_ProcessNodeNoOrder_Swizzled(stack, nb, tn, params)) + return 1; + if(nodeType>0 && BV4_ProcessNodeNoOrder_Swizzled(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_Swizzled(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_Swizzled(stack, nb, tn, params)) + return 1; + + }while(nb); + + return 0; + }*/ + + + template + static Ps::IntBool BV4_ProcessStreamSwizzledNoOrderQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledQ* tn = reinterpret_cast(node); + + const PxU32 nodeType = getChildType(childData); + + if(nodeType>1 && BV4_ProcessNodeNoOrder_SwizzledQ(stack, nb, tn, params)) + return 1; + if(nodeType>0 && BV4_ProcessNodeNoOrder_SwizzledQ(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_SwizzledQ(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_SwizzledQ(stack, nb, tn, params)) + return 1; + + }while(nb); + + return 0; + } + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + template + static Ps::IntBool BV4_ProcessStreamSwizzledNoOrderNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedNQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledNQ* tn = reinterpret_cast(node); + + const PxU32 nodeType = getChildType(childData); + + if(nodeType>1 && BV4_ProcessNodeNoOrder_SwizzledNQ(stack, nb, tn, params)) + return 1; + if(nodeType>0 && BV4_ProcessNodeNoOrder_SwizzledNQ(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_SwizzledNQ(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_SwizzledNQ(stack, nb, tn, params)) + return 1; + + }while(nb); + + return 0; + } +#endif + +#endif // GU_BV4_SLABS_SWIZZLED_NO_ORDER_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h new file mode 100644 index 000000000..c00d72037 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h @@ -0,0 +1,158 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_SWIZZLED_ORDERED_H +#define GU_BV4_SLABS_SWIZZLED_ORDERED_H + + // Generic + PNS +/* template + static void BV4_ProcessStreamSwizzledOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<(node); + + PxU32 code2 = 0; + BV4_ProcessNodeOrdered2_Swizzled(code2, tn, params); + BV4_ProcessNodeOrdered2_Swizzled(code2, tn, params); + if(nodeType>0) + BV4_ProcessNodeOrdered2_Swizzled(code2, tn, params); + if(nodeType>1) + BV4_ProcessNodeOrdered2_Swizzled(code2, tn, params); + + SLABS_PNS + + }while(nb); + }*/ + + // Generic + PNS + template + static void BV4_ProcessStreamSwizzledOrderedQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<(node); + + PxU32 code2 = 0; + BV4_ProcessNodeOrdered2_SwizzledQ(code2, tn, params); + BV4_ProcessNodeOrdered2_SwizzledQ(code2, tn, params); + if(nodeType>0) + BV4_ProcessNodeOrdered2_SwizzledQ(code2, tn, params); + if(nodeType>1) + BV4_ProcessNodeOrdered2_SwizzledQ(code2, tn, params); + + SLABS_PNS + + }while(nb); + } + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + // Generic + PNS + template + static void BV4_ProcessStreamSwizzledOrderedNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedNQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<(node); + + PxU32 code2 = 0; + BV4_ProcessNodeOrdered2_SwizzledNQ(code2, tn, params); + BV4_ProcessNodeOrdered2_SwizzledNQ(code2, tn, params); + if(nodeType>0) + BV4_ProcessNodeOrdered2_SwizzledNQ(code2, tn, params); + if(nodeType>1) + BV4_ProcessNodeOrdered2_SwizzledNQ(code2, tn, params); + + SLABS_PNS + + }while(nb); + } +#endif + +#endif // GU_BV4_SLABS_SWIZZLED_ORDERED_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp new file mode 100644 index 000000000..362ca6135 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp @@ -0,0 +1,326 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuBV4_Common.h" +#include "GuSphere.h" +#include "GuDistancePointTriangle.h" +#include "PsVecMath.h" + +using namespace physx::shdfnd::aos; + +#if PX_VC +#pragma warning ( disable : 4324 ) +#endif + +// Sphere overlap any + +struct SphereParams +{ + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); + + BV4_ALIGN16(PxVec3 mCenter_PaddedAligned); float mRadius2; +#ifdef GU_BV4_USE_SLABS + BV4_ALIGN16(PxVec3 mCenter_PaddedAligned2); float mRadius22; +#endif +}; + +#ifndef GU_BV4_USE_SLABS +#ifndef GU_BV4_QUANTIZED_TREE +// PT: TODO: refactor with bucket pruner code (TA34704) +static PX_FORCE_INLINE Ps::IntBool BV4_SphereAABBOverlap(const PxVec3& center, const PxVec3& extents, const SphereParams* PX_RESTRICT params) +{ + const Vec4V mCenter = V4LoadA_Safe(¶ms->mCenter_PaddedAligned.x); + const FloatV mRadius2 = FLoad(params->mRadius2); + + const Vec4V boxCenter = V4LoadU(¢er.x); + const Vec4V boxExtents = V4LoadU(&extents.x); + + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const PxU32 test = (PxU32)_mm_movemask_ps(FIsGrtrOrEq(mRadius2, V4Dot3(d, d))); + return (test & 0x7) == 0x7; +} +#endif +#endif + +static PX_FORCE_INLINE Ps::IntBool __SphereTriangle(const SphereParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) +{ + { + const float sqrDist = (p0 - params->mCenter_PaddedAligned).magnitudeSquared(); + if(sqrDist <= params->mRadius2) + return 1; + } + + const PxVec3 edge10 = p1 - p0; + const PxVec3 edge20 = p2 - p0; + const PxVec3 cp = closestPtPointTriangle2(params->mCenter_PaddedAligned, p0, p1, p2, edge10, edge20); + const float sqrDist = (cp - params->mCenter_PaddedAligned).magnitudeSquared(); + return sqrDist <= params->mRadius2; +} + +// PT: TODO: evaluate if SIMD distance function would be faster here (TA34704) +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static /*PX_FORCE_INLINE*/ Ps::IntBool /*__fastcall*/ __SphereTriangle(const SphereParams* PX_RESTRICT params, PxU32 primIndex) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + return __SphereTriangle(params, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2]); +} + +namespace +{ +class LeafFunction_SphereOverlapAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const SphereParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__SphereTriangle(params, primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +template +static PX_FORCE_INLINE void setupSphereParams(ParamsT* PX_RESTRICT params, const Sphere& sphere, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh) +{ + computeLocalSphere(params->mRadius2, params->mCenter_PaddedAligned, sphere, worldm_Aligned); + +#ifdef GU_BV4_USE_SLABS + params->mCenter_PaddedAligned2 = params->mCenter_PaddedAligned*2.0f; + params->mRadius22 = params->mRadius2*4.0f; +#endif + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); +} + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" + + static PX_FORCE_INLINE Ps::IntBool BV4_SphereAABBOverlap(const Vec4V boxCenter, const Vec4V boxExtents, const SphereParams* PX_RESTRICT params) + { + const Vec4V mCenter = V4LoadA_Safe(¶ms->mCenter_PaddedAligned2.x); + const FloatV mRadius2 = FLoad(params->mRadius22); + + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const PxU32 test = PxU32(_mm_movemask_ps(FIsGrtrOrEq(mRadius2, V4Dot3(d, d)))); + return (test & 0x7) == 0x7; + } +#else + #ifdef GU_BV4_QUANTIZED_TREE + static PX_FORCE_INLINE Ps::IntBool BV4_SphereAABBOverlap(const BVDataPacked* PX_RESTRICT node, const SphereParams* PX_RESTRICT params) + { + const __m128i testV = _mm_load_si128((__m128i*)node->mAABB.mData); + const __m128i qextentsV = _mm_and_si128(testV, _mm_set1_epi32(0x0000ffff)); + const __m128i qcenterV = _mm_srai_epi32(testV, 16); + const Vec4V boxCenter = V4Mul(_mm_cvtepi32_ps(qcenterV), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + const Vec4V boxExtents = V4Mul(_mm_cvtepi32_ps(qextentsV), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + + const Vec4V mCenter = V4LoadA_Safe(¶ms->mCenter_PaddedAligned.x); + const FloatV mRadius2 = FLoad(params->mRadius2); + + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const PxU32 test = (PxU32)_mm_movemask_ps(FIsGrtrOrEq(mRadius2, V4Dot3(d, d))); + return (test & 0x7) == 0x7; + } + #endif +#endif + +#include "GuBV4_ProcessStreamNoOrder_SphereAABB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_SwizzledNoOrder.h" +#endif + +#define GU_BV4_PROCESS_STREAM_NO_ORDER +#include "GuBV4_Internal.h" + +Ps::IntBool BV4_OverlapSphereAny(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereParams Params; + setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + return processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + return LeafFunction_SphereOverlapAny::doLeafTest(&Params, nbTris); + } +} + +// Sphere overlap all + +struct SphereParamsAll : SphereParams +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxU32* mHits; +}; + +namespace +{ +class LeafFunction_SphereOverlapAll +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(SphereParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__SphereTriangle(params, primIndex)) + { + SphereParamsAll* ParamsAll = static_cast(params); + if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits) + return 1; + ParamsAll->mHits[ParamsAll->mNbHits] = primIndex; + ParamsAll->mNbHits++; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +PxU32 BV4_OverlapSphereAll(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = size; + Params.mHits = results; + + setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + overflow = processStreamNoOrder(tree, &Params)!=0; + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + overflow = LeafFunction_SphereOverlapAll::doLeafTest(&Params, nbTris)!=0; + } + return Params.mNbHits; +} + + +// Sphere overlap - callback version + +struct SphereParamsCB : SphereParams +{ + MeshOverlapCallback mCallback; + void* mUserData; +}; + +namespace +{ +class LeafFunction_SphereOverlapCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const SphereParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + if(__SphereTriangle(params, p0, p1, p2)) + { + const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + if((params->mCallback)(params->mUserData, p0, p1, p2, primIndex, vrefs)) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: this one is currently not used +void BV4_OverlapSphereCB(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + LeafFunction_SphereOverlapCB::doLeafTest(&Params, nbTris); + } +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp new file mode 100644 index 000000000..8250997ed --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp @@ -0,0 +1,386 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxMat44.h" +#include "GuBV4.h" +#include "GuBox.h" +#include "GuSphere.h" +#include "GuSIMDHelpers.h" +#include "GuSweepSphereTriangle.h" + +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuBV4_Common.h" + +// PT: for sphere-sweeps we use method 3 in %SDKRoot%\InternalDocumentation\GU\Sweep strategies.ppt + +namespace +{ + // PT: TODO: refactor structure (TA34704) + struct RayParams + { + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); +#ifndef GU_BV4_USE_SLABS + BV4_ALIGN16(Vec3p mData2_PaddedAligned); + BV4_ALIGN16(Vec3p mFDir_PaddedAligned); + BV4_ALIGN16(Vec3p mData_PaddedAligned); +#endif + BV4_ALIGN16(Vec3p mLocalDir_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) + BV4_ALIGN16(Vec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) + }; + + struct SphereSweepParams : RayParams + { + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + + PxVec3 mOriginalExtents_Padded; + + RaycastHitInternal mStabbedFace; + PxU32 mBackfaceCulling; + PxU32 mEarlyExit; + + PxVec3 mP0, mP1, mP2; + PxVec3 mBestTriNormal; + float mBestAlignmentValue; + float mBestDistance; + float mMaxDist; + }; +} + +#include "GuBV4_AABBAABBSweepTest.h" + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ triSphereSweep(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex, bool nodeSorting=true) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + PxVec3 normal = (p1 - p0).cross(p2 - p0); + + // Backface culling + const bool culled = params->mBackfaceCulling && normal.dot(params->mLocalDir_Padded) > 0.0f; + if(culled) + return false; + + const PxTriangle T(p0, p1, p2); // PT: TODO: check potential bad ctor/dtor here (TA34704) <= or avoid creating the tri, not needed anymore + + normal.normalize(); + + // PT: TODO: we lost some perf when switching to PhysX version. Revisit/investigate. (TA34704) + float dist; + bool directHit; + if(!sweepSphereVSTri(T.verts, normal, params->mOrigin_Padded, params->mOriginalExtents_Padded.x, params->mLocalDir_Padded, dist, directHit, true)) + return false; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal alignmentValue = computeAlignmentValue(normal, params->mLocalDir_Padded); + if(keepTriangle(dist, alignmentValue, params->mBestDistance, params->mBestAlignmentValue, params->mMaxDist, distEpsilon)) + { + params->mStabbedFace.mDistance = dist; + params->mStabbedFace.mTriangleID = primIndex; + params->mP0 = p0; + params->mP1 = p1; + params->mP2 = p2; + params->mBestDistance = PxMin(params->mBestDistance, dist); // exact lower bound + params->mBestAlignmentValue = alignmentValue; + params->mBestTriNormal = normal; + if(nodeSorting) + { +#ifndef GU_BV4_USE_SLABS + setupRayData(params, dist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif + } + return true; + } + return false; +} + +namespace +{ +class LeafFunction_SphereSweepClosest +{ +public: + static PX_FORCE_INLINE void doLeafTest(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + triSphereSweep(params, primIndex); + primIndex++; + }while(nbToGo--); + } +}; + +class LeafFunction_SphereSweepAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triSphereSweep(params, primIndex)) + return 1; + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +class ImpactFunctionSphere +{ +public: + static PX_FORCE_INLINE void computeImpact(PxVec3& impactPos, PxVec3& impactNormal, const Sphere& sphere, const PxVec3& dir, const PxReal t, const TrianglePadded& triangle) + { + computeSphereTriImpactData(impactPos, impactNormal, sphere.center, dir, t, triangle); + } +}; +} + +template +static PX_FORCE_INLINE void setupSphereParams(ParamsT* PX_RESTRICT params, const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh, PxU32 flags) +{ + params->mOriginalExtents_Padded = PxVec3(sphere.radius); + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + params->mStabbedFace.mDistance = maxDist; + params->mBestDistance = PX_MAX_REAL; + params->mBestAlignmentValue = 2.0f; + params->mMaxDist = maxDist; + setupParamsFlags(params, flags); + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + computeLocalRay(params->mLocalDir_Padded, params->mOrigin_Padded, dir, sphere.center, worldm_Aligned); + +#ifndef GU_BV4_USE_SLABS + setupRayData(params, maxDist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif +} + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h" +#include "GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_KajiyaNoOrder.h" + #include "GuBV4_Slabs_KajiyaOrdered.h" +#endif + +#define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER +#define GU_BV4_PROCESS_STREAM_RAY_ORDERED +#include "GuBV4_Internal.h" + +Ps::IntBool BV4_SphereSweepSingle(const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereSweepParams Params; + setupSphereParams(&Params, sphere, dir, maxDist, &tree, worldm_Aligned, mesh, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamRayNoOrder<1, LeafFunction_SphereSweepAny>(tree, &Params); + else + processStreamRayOrdered<1, LeafFunction_SphereSweepClosest>(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); + + return computeImpactDataT(sphere, dir, hit, &Params, worldm_Aligned, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + +// PT: sphere sweep callback version - currently not used + +namespace +{ + struct SphereSweepParamsCB : SphereSweepParams + { + // PT: these new members are only here to call computeImpactDataT during traversal :( + // PT: TODO: most of them may not be needed if we just move sphere to local space before traversal + Sphere mSphere; // Sphere in original space (maybe not local/mesh space) + PxVec3 mDir; // Dir in original space (maybe not local/mesh space) + const PxMat44* mWorldm_Aligned; + PxU32 mFlags; + + SweepUnlimitedCallback mCallback; + void* mUserData; + bool mNodeSorting; + }; + +class LeafFunction_SphereSweepCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(SphereSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triSphereSweep(params, primIndex, params->mNodeSorting)) + { + // PT: TODO: in this version we must compute the impact data immediately, + // which is a terrible idea in general, but I'm not sure what else I can do. + SweepHit hit; + const bool b = computeImpactDataT(params->mSphere, params->mDir, &hit, params, params->mWorldm_Aligned, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); + PX_ASSERT(b); + PX_UNUSED(b); + + reportUnlimitedCallbackHit(params, hit); + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +void BV4_SphereSweepCB(const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereSweepParamsCB Params; + Params.mSphere = sphere; + Params.mDir = dir; + Params.mWorldm_Aligned = worldm_Aligned; + Params.mFlags = flags; + + Params.mCallback = callback; + Params.mUserData = userData; + Params.mMaxDist = maxDist; + Params.mNodeSorting = nodeSorting; + setupSphereParams(&Params, sphere, dir, maxDist, &tree, worldm_Aligned, mesh, flags); + + PX_ASSERT(!Params.mEarlyExit); + + if(tree.mNodes) + { + if(nodeSorting) + processStreamRayOrdered<1, LeafFunction_SphereSweepCB>(tree, &Params); + else + processStreamRayNoOrder<1, LeafFunction_SphereSweepCB>(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); +} + + +// Old box sweep callback version, using sphere code + +namespace +{ +struct BoxSweepParamsCB : SphereSweepParams +{ + MeshSweepCallback mCallback; + void* mUserData; +}; + +class ExLeafTestSweepCB +{ +public: + static PX_FORCE_INLINE void doLeafTest(BoxSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + { +// const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + float dist = params->mStabbedFace.mDistance; + if((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], primIndex, /*vrefs,*/ dist)) + return; + + if(distmStabbedFace.mDistance) + { + params->mStabbedFace.mDistance = dist; +#ifndef GU_BV4_USE_SLABS + setupRayData(params, dist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif + } + } + + primIndex++; + }while(nbToGo--); + } +}; +} + +void BV4_GenericSweepCB_Old(const PxVec3& origin, const PxVec3& extents, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshSweepCallback callback, void* userData) +{ + BoxSweepParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + Params.mOriginalExtents_Padded = extents; + + Params.mStabbedFace.mTriangleID = PX_INVALID_U32; + Params.mStabbedFace.mDistance = maxDist; + + computeLocalRay(Params.mLocalDir_Padded, Params.mOrigin_Padded, dir, origin, worldm_Aligned); + +#ifndef GU_BV4_USE_SLABS + setupRayData(&Params, maxDist, Params.mOrigin_Padded, Params.mLocalDir_Padded); +#endif + + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + setupMeshPointersAndQuantizedCoeffs(&Params, mesh, &tree); + + if(tree.mNodes) + processStreamRayOrdered<1, ExLeafTestSweepCB>(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + ExLeafTestSweepCB::doLeafTest(&Params, nbTris); + } +} + +#endif + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h new file mode 100644 index 000000000..7363fa62d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h @@ -0,0 +1,44 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV_CONSTANTS_H +#define GU_BV_CONSTANTS_H + +#include "PsVecMath.h" + +namespace +{ + const physx::Ps::aos::VecU32V signMask = physx::Ps::aos::U4LoadXYZW((physx::PxU32(1)<<31), (physx::PxU32(1)<<31), (physx::PxU32(1)<<31), (physx::PxU32(1)<<31)); + const physx::Ps::aos::Vec4V epsFloat4 = physx::Ps::aos::V4Load(1e-9f); + const physx::Ps::aos::Vec4V zeroes = physx::Ps::aos::V4Zero(); + const physx::Ps::aos::Vec4V twos = physx::Ps::aos::V4Load(2.0f); + const physx::Ps::aos::Vec4V epsInflateFloat4 = physx::Ps::aos::V4Load(1e-7f); +} + +#endif // GU_BV_CONSTANTS_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h new file mode 100644 index 000000000..bd4fc0dfc --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h @@ -0,0 +1,300 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MESH_DATA_H +#define GU_MESH_DATA_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec4.h" +#include "foundation/PxBounds3.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsAllocator.h" +#include "PxTriangleMesh.h" +#include "GuRTree.h" +#include "GuBV4.h" +#include "GuBV32.h" + +namespace physx +{ +namespace Gu { + +// 1: support stackless collision trees for non-recursive collision queries +// 2: height field functionality not supported anymore +// 3: mass struct removed +// 4: bounding sphere removed +// 5: RTree added, opcode tree still in the binary image, physx 3.0 +// 6: opcode tree removed from binary image +// 7: convex decomposition is out +// 8: adjacency information added +// 9: removed leaf triangles and most of opcode data, changed rtree layout +// 10: float rtrees +// 11: new build, isLeaf added to page +// 12: isLeaf is now the lowest bit in ptrs +// 13: TA30159 removed deprecated convexEdgeThreshold and bumped version +// 14: added midphase ID +// 15: GPU data simplification + +#define PX_MESH_VERSION 15 + +// these flags are used to indicate/validate the contents of a cooked mesh file +enum InternalMeshSerialFlag +{ + IMSF_MATERIALS = (1<<0), //!< if set, the cooked mesh file contains per-triangle material indices + IMSF_FACE_REMAP = (1<<1), //!< if set, the cooked mesh file contains a remap table + IMSF_8BIT_INDICES = (1<<2), //!< if set, the cooked mesh file contains 8bit indices (topology) + IMSF_16BIT_INDICES = (1<<3), //!< if set, the cooked mesh file contains 16bit indices (topology) + IMSF_ADJACENCIES = (1<<4), //!< if set, the cooked mesh file contains adjacency structures + IMSF_GRB_DATA = (1<<5) //!< if set, the cooked mesh file contains GRB data structures +}; + + + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + class MeshDataBase : public Ps::UserAllocated + { + public: + PxMeshMidPhase::Enum mType; + PxU8 mFlags; + PxU32 mNbVertices; + PxVec3* mVertices; + + PxBounds3 mAABB; + PxReal mGeomEpsilon; + + PxU32* mFaceRemap; + PxU32* mAdjacencies; + + // GRB data ------------------------- + void * mGRB_primIndices; //!< GRB: GPU-friendly primitive indices(either triangle) + // TODO avoroshilov: adjacency info - duplicated, remove it and use 'mAdjacencies' and 'mExtraTrigData' see GuTriangleMesh.cpp:325 + void * mGRB_primAdjacencies; //!< GRB: adjacency data, with BOUNDARY and NONCONVEX flags (flags replace adj indices where applicable) [uin4] + PxU32* mGRB_faceRemap; //!< GRB: this remap the GPU triangle indices to CPU triangle indices + // End of GRB data ------------------ + + + MeshDataBase() : + mFlags(0), + mNbVertices(0), + mVertices(NULL), + mAABB(PxBounds3::empty()), + mGeomEpsilon(0.0f), + + mFaceRemap(NULL), + mAdjacencies(NULL), + + mGRB_primIndices(NULL), + mGRB_primAdjacencies(NULL), + mGRB_faceRemap(NULL) + { + } + + virtual ~MeshDataBase() + { + if (mVertices) + PX_FREE(mVertices); + if (mFaceRemap) + PX_DELETE_POD(mFaceRemap); + if (mAdjacencies) + PX_DELETE_POD(mAdjacencies); + + + if (mGRB_primIndices) + PX_FREE(mGRB_primIndices); + if (mGRB_primAdjacencies) + PX_DELETE_POD(mGRB_primAdjacencies); + + if (mGRB_faceRemap) + PX_DELETE_POD(mGRB_faceRemap); + } + + + PxVec3* allocateVertices(PxU32 nbVertices) + { + PX_ASSERT(!mVertices); + // PT: we allocate one more vertex to make sure it's safe to V4Load the last one + const PxU32 nbAllocatedVerts = nbVertices + 1; + mVertices = reinterpret_cast(PX_ALLOC(nbAllocatedVerts * sizeof(PxVec3), "PxVec3")); + mNbVertices = nbVertices; + return mVertices; + } + + + PX_FORCE_INLINE bool has16BitIndices() const + { + return (mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? true : false; + } + }; + + class TriangleMeshData : public MeshDataBase + { + public: + + PxU32 mNbTriangles; + void* mTriangles; + + PxU8* mExtraTrigData; + PxU16* mMaterialIndices; + + // GRB data ------------------------- + void* mGRB_BV32Tree; + // End of GRB data ------------------ + + TriangleMeshData() : + mNbTriangles (0), + mTriangles (NULL), + mExtraTrigData (NULL), + mMaterialIndices (NULL), + mGRB_BV32Tree (NULL) + { + } + + virtual ~TriangleMeshData() + { + + if(mTriangles) + PX_FREE(mTriangles); + if(mMaterialIndices) + PX_DELETE_POD(mMaterialIndices); + if(mExtraTrigData) + PX_DELETE_POD(mExtraTrigData); + + if (mGRB_BV32Tree) + { + Gu::BV32Tree* bv32Tree = reinterpret_cast(mGRB_BV32Tree); + PX_DELETE(bv32Tree); + mGRB_BV32Tree = NULL; + } + + } + + PxU32* allocateAdjacencies() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mAdjacencies); + mAdjacencies = PX_NEW(PxU32)[mNbTriangles * 3]; + mFlags |= PxTriangleMeshFlag::eADJACENCY_INFO; + return mAdjacencies; + } + + PxU32* allocateFaceRemap() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mFaceRemap); + mFaceRemap = PX_NEW(PxU32)[mNbTriangles]; + return mFaceRemap; + } + + void* allocateTriangles(PxU32 nbTriangles, bool force32Bit, PxU32 allocateGPUData = 0) + { + PX_ASSERT(mNbVertices); + PX_ASSERT(!mTriangles); + + bool index16 = mNbVertices <= 0xffff && !force32Bit; + if(index16) + mFlags |= PxTriangleMeshFlag::e16_BIT_INDICES; + + mTriangles = PX_ALLOC(nbTriangles * (index16 ? sizeof(PxU16) : sizeof(PxU32)) * 3, "mTriangles"); + if (allocateGPUData) + mGRB_primIndices = PX_ALLOC(nbTriangles * (index16 ? sizeof(PxU16) : sizeof(PxU32)) * 3, "mGRB_triIndices"); + mNbTriangles = nbTriangles; + return mTriangles; + } + + PxU16* allocateMaterials() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mMaterialIndices); + mMaterialIndices = PX_NEW(PxU16)[mNbTriangles]; + return mMaterialIndices; + } + + PxU8* allocateExtraTrigData() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mExtraTrigData); + mExtraTrigData = PX_NEW(PxU8)[mNbTriangles]; + return mExtraTrigData; + } + + PX_FORCE_INLINE void setTriangleAdjacency(PxU32 triangleIndex, PxU32 adjacency, PxU32 offset) + { + PX_ASSERT(mAdjacencies); + mAdjacencies[triangleIndex*3 + offset] = adjacency; + } + + + }; + + class RTreeTriangleData : public TriangleMeshData + { + public: + RTreeTriangleData() { mType = PxMeshMidPhase::eBVH33; } + virtual ~RTreeTriangleData() {} + + Gu::RTree mRTree; + }; + + class BV4TriangleData : public TriangleMeshData + { + public: + BV4TriangleData() { mType = PxMeshMidPhase::eBVH34; } + virtual ~BV4TriangleData() {} + + Gu::SourceMesh mMeshInterface; + Gu::BV4Tree mBV4Tree; + }; + + + class BV32TriangleData : public TriangleMeshData + { + public: + //using the same type as BV4 + BV32TriangleData() { mType = PxMeshMidPhase::eBVH34; } + virtual ~BV32TriangleData() {} + + Gu::SourceMesh mMeshInterface; + Gu::BV32Tree mBV32Tree; + }; + + +#if PX_VC +#pragma warning(pop) +#endif + + +} // namespace Gu + +} + +#endif // #ifdef GU_MESH_DATA_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp new file mode 100644 index 000000000..c4e2be499 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp @@ -0,0 +1,304 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "PxMeshQuery.h" +#include "GuInternal.h" +#include "PxSphereGeometry.h" +#include "PxGeometryQuery.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuBoxConversion.h" +#include "GuIntersectionTriangleBox.h" +#include "CmScaling.h" +#include "GuSweepTests.h" +#include "GuSIMDHelpers.h" +#include "GuMidphaseInterface.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +namespace { + + class HfTrianglesEntityReport2 : public EntityReport, public LimitedResults + { + public: + HfTrianglesEntityReport2( + PxU32* results, PxU32 maxResults, PxU32 startIndex, + HeightFieldUtil& hfUtil, + const PxVec3& boxCenter, const PxVec3& boxExtents, const PxQuat& boxRot, + bool aabbOverlap) : + LimitedResults (results, maxResults, startIndex), + mHfUtil (hfUtil), + mAABBOverlap (aabbOverlap) + { + buildFrom(mBox2Hf, boxCenter, boxExtents, boxRot); + } + + virtual bool onEvent(PxU32 nbEntities, PxU32* entities) + { + if(mAABBOverlap) + { + while(nbEntities--) + if(!add(*entities++)) + return false; + } + else + { + const PxTransform idt(PxIdentity); + for(PxU32 i=0; i(triGeom.triangleMesh); + + PX_CHECK_AND_RETURN(triangleIndexgetNbTriangles(), "PxMeshQuery::getTriangle: triangle index is out of bounds"); + + if(adjacencyIndices && !tm->getAdjacencies()) + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacency information not created. Set buildTriangleAdjacencies on Cooking params."); + + const Cm::Matrix34 vertex2worldSkew = globalPose * triGeom.scale; + tm->computeWorldTriangle(triangle, triangleIndex, vertex2worldSkew, triGeom.scale.hasNegativeDeterminant(), vertexIndices, adjacencyIndices); +} + +/////////////////////////////////////////////////////////////////////////////// + +void physx::PxMeshQuery::getTriangle(const PxHeightFieldGeometry& hfGeom, const PxTransform& globalPose, PxTriangleID triangleIndex, PxTriangle& triangle, PxU32* vertexIndices, PxU32* adjacencyIndices) +{ + HeightFieldUtil hfUtil(hfGeom); + + hfUtil.getTriangle(globalPose, triangle, vertexIndices, adjacencyIndices, triangleIndex, true, true); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 physx::PxMeshQuery::findOverlapTriangleMesh( + const PxGeometry& geom, const PxTransform& geomPose, + const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose, + PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow) +{ + PX_SIMD_GUARD; + + LimitedResults limitedResults(results, maxResults, startIndex); + + TriangleMesh* tm = static_cast(meshGeom.triangleMesh); + + switch(geom.getType()) + { + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast(geom); + + Box box; + buildFrom(box, geomPose.p, boxGeom.halfExtents, geomPose.q); + + Midphase::intersectBoxVsMesh(box, *tm, meshPose, meshGeom.scale, &limitedResults); + break; + } + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsGeom = static_cast(geom); + + Capsule capsule; + getCapsule(capsule, capsGeom, geomPose); + + Midphase::intersectCapsuleVsMesh(capsule, *tm, meshPose, meshGeom.scale, &limitedResults); + break; + } + + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast(geom); + Midphase::intersectSphereVsMesh(Sphere(geomPose.p, sphereGeom.radius), *tm, meshPose, meshGeom.scale, &limitedResults); + break; + } + + case PxGeometryType::ePLANE: + case PxGeometryType::eCONVEXMESH: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + PX_CHECK_MSG(false, "findOverlapTriangleMesh: Only box, capsule and sphere geometries are supported."); + } + } + + overflow = limitedResults.mOverflow; + return limitedResults.mNbResults; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 physx::PxMeshQuery::findOverlapHeightField( const PxGeometry& geom, const PxTransform& geomPose, + const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose, + PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow) +{ + PX_SIMD_GUARD; + const PxTransform localPose0 = hfPose.transformInv(geomPose); + PxBoxGeometry boxGeom; + + switch(geom.getType()) + { + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& cap = static_cast(geom); + boxGeom.halfExtents = PxVec3(cap.halfHeight+cap.radius, cap.radius, cap.radius); + } + break; + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sph = static_cast(geom); + boxGeom.halfExtents = PxVec3(sph.radius, sph.radius, sph.radius); + } + break; + case PxGeometryType::eBOX: + boxGeom = static_cast(geom); + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eCONVEXMESH: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + overflow = false; + PX_CHECK_AND_RETURN_VAL(false, "findOverlapHeightField: Only box, sphere and capsule queries are supported.", false); + } + } + + const bool isAABB = ((localPose0.q.x == 0.0f) && (localPose0.q.y == 0.0f) && (localPose0.q.z == 0.0f)); + + PxBounds3 bounds; + if (isAABB) + bounds = PxBounds3::centerExtents(localPose0.p, boxGeom.halfExtents); + else + bounds = PxBounds3::poseExtent(localPose0, boxGeom.halfExtents); // box.halfExtents is really extent + + HeightFieldUtil hfUtil(hfGeom); + HfTrianglesEntityReport2 entityReport(results, maxResults, startIndex, hfUtil, localPose0.p, boxGeom.halfExtents, localPose0.q, isAABB); + + hfUtil.overlapAABBTriangles(hfPose, bounds, 0, &entityReport); + overflow = entityReport.mOverflow; + return entityReport.mNbResults; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool physx::PxMeshQuery::sweep( const PxVec3& unitDir, const PxReal maxDistance, + const PxGeometry& geom, const PxTransform& pose, + PxU32 triangleCount, const PxTriangle* triangles, + PxSweepHit& sweepHit, PxHitFlags hitFlags, + const PxU32* cachedIndex, const PxReal inflation, bool doubleSided) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxMeshQuery::sweep(): pose is not valid.", false); + PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "PxMeshQuery::sweep(): unitDir is not valid.", false); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(maxDistance), "PxMeshQuery::sweep(): distance is not valid.", false); + PX_CHECK_AND_RETURN_VAL(maxDistance > 0, "PxMeshQuery::sweep(): sweep distance must be greater than 0.", false); + + PX_PROFILE_ZONE("MeshQuery.sweep", 0); + + const PxReal distance = PxMin(maxDistance, PX_MAX_SWEEP_DISTANCE); + + switch(geom.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast(geom); + + // PT: TODO: technically this capsule with 0.0 half-height is invalid ("isValid" returns false) + const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f); + + return sweepCapsuleTriangles( triangleCount, triangles, doubleSided, capsuleGeom, pose, unitDir, distance, + sweepHit, cachedIndex, inflation, hitFlags); + } + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + return sweepCapsuleTriangles( triangleCount, triangles, doubleSided, capsuleGeom, pose, unitDir, distance, + sweepHit, cachedIndex, inflation, hitFlags); + } + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast(geom); + + if(hitFlags & PxHitFlag::ePRECISE_SWEEP) + { + return sweepBoxTriangles_Precise( triangleCount, triangles, doubleSided, boxGeom, pose, unitDir, distance, sweepHit, cachedIndex, + inflation, hitFlags); + } + else + { + return sweepBoxTriangles( triangleCount, triangles, doubleSided, boxGeom, pose, unitDir, distance, sweepHit, cachedIndex, + inflation, hitFlags); + } + } + case PxGeometryType::ePLANE: + case PxGeometryType::eCONVEXMESH: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_CHECK_MSG(false, "PxMeshQuery::sweep(): geometry object parameter must be sphere, capsule or box geometry."); + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp new file mode 100644 index 000000000..0e141cbc1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp @@ -0,0 +1,999 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +using namespace physx; +using namespace Gu; + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuSweepMesh.h" +#include "GuBV4Build.h" +#include "GuBV4_Common.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuBoxConversion.h" +#include "GuConvexUtilsInternal.h" +#include "GuVecTriangle.h" +#include "GuIntersectionTriangleBox.h" +#include "GuIntersectionCapsuleTriangle.h" +#include "GuIntersectionRayBox.h" +#include "PxTriangleMeshGeometry.h" +#include "CmScaling.h" +#include "GuTriangleMeshBV4.h" + +// This file contains code specific to the BV4 midphase. + +// PT: TODO: revisit/inline static sweep functions (TA34704) + +using namespace physx; +using namespace Gu; +using namespace Cm; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) +Ps::IntBool BV4_RaycastSingle (const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hit, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags); +PxU32 BV4_RaycastAll (const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hits, PxU32 maxNbHits, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags); +void BV4_RaycastCB (const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, float maxDist, float geomEpsilon, PxU32 flags, MeshRayCallback callback, void* userData); + +Ps::IntBool BV4_OverlapSphereAny (const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned); +PxU32 BV4_OverlapSphereAll (const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow); +void BV4_OverlapSphereCB (const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData); + +Ps::IntBool BV4_OverlapBoxAny (const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned); +PxU32 BV4_OverlapBoxAll (const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow); +void BV4_OverlapBoxCB (const Box& box, const BV4Tree& tree, MeshOverlapCallback callback, void* userData); + +Ps::IntBool BV4_OverlapCapsuleAny (const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned); +PxU32 BV4_OverlapCapsuleAll (const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow); +void BV4_OverlapCapsuleCB (const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData); + +Ps::IntBool BV4_SphereSweepSingle (const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags); +void BV4_SphereSweepCB (const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting); + +Ps::IntBool BV4_BoxSweepSingle (const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags); +void BV4_BoxSweepCB (const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting); + +Ps::IntBool BV4_CapsuleSweepSingle (const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags); +Ps::IntBool BV4_CapsuleSweepSingleAA(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags); +void BV4_CapsuleSweepCB (const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags); +void BV4_CapsuleSweepAACB (const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags); + +void BV4_GenericSweepCB_Old (const PxVec3& origin, const PxVec3& extents, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshSweepCallback callback, void* userData); +void BV4_GenericSweepCB (const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, bool anyHit); + +static PX_FORCE_INLINE void setIdentity(PxMat44& m) +{ + m.column0 = PxVec4(1.0f, 0.0f, 0.0f, 0.0f); + m.column1 = PxVec4(0.0f, 1.0f, 0.0f, 0.0f); + m.column2 = PxVec4(0.0f, 0.0f, 1.0f, 0.0f); + m.column3 = PxVec4(0.0f, 0.0f, 0.0f, 1.0f); +} + +static PX_FORCE_INLINE void setRotation(PxMat44& m, const PxQuat& q) +{ + const PxReal x = q.x; + const PxReal y = q.y; + const PxReal z = q.z; + const PxReal w = q.w; + + const PxReal x2 = x + x; + const PxReal y2 = y + y; + const PxReal z2 = z + z; + + const PxReal xx = x2*x; + const PxReal yy = y2*y; + const PxReal zz = z2*z; + + const PxReal xy = x2*y; + const PxReal xz = x2*z; + const PxReal xw = x2*w; + + const PxReal yz = y2*z; + const PxReal yw = y2*w; + const PxReal zw = z2*w; + + m.column0 = PxVec4(1.0f - yy - zz, xy + zw, xz - yw, 0.0f); + m.column1 = PxVec4(xy - zw, 1.0f - xx - zz, yz + xw, 0.0f); + m.column2 = PxVec4(xz + yw, yz - xw, 1.0f - xx - yy, 0.0f); +} + +#define IEEE_1_0 0x3f800000 //!< integer representation of 1.0 +static PX_FORCE_INLINE const PxMat44* setupWorldMatrix(PxMat44& world, const float* meshPos, const float* meshRot) +{ +// world = PxMat44(PxIdentity); + setIdentity(world); + + bool isIdt = true; + if(meshRot) + { + const PxU32* Bin = reinterpret_cast(meshRot); + if(Bin[0]!=0 || Bin[1]!=0 || Bin[2]!=0 || Bin[3]!=IEEE_1_0) + { +// const PxQuat Q(meshRot[0], meshRot[1], meshRot[2], meshRot[3]); +// world = PxMat44(Q); + setRotation(world, PxQuat(meshRot[0], meshRot[1], meshRot[2], meshRot[3])); + isIdt = false; + } + } + + if(meshPos) + { + const PxU32* Bin = reinterpret_cast(meshPos); + if(Bin[0]!=0 || Bin[1]!=0 || Bin[2]!=0) + { +// world.setPosition(PxVec3(meshPos[0], meshPos[1], meshPos[2])); + world.column3.x = meshPos[0]; + world.column3.y = meshPos[1]; + world.column3.z = meshPos[2]; + isIdt = false; + } + } + return isIdt ? NULL : &world; +} + +static PX_FORCE_INLINE PxU32 setupFlags(bool anyHit, bool doubleSided, bool meshBothSides) +{ + PxU32 flags = 0; + if(anyHit) + flags |= QUERY_MODIFIER_ANY_HIT; + if(doubleSided) + flags |= QUERY_MODIFIER_DOUBLE_SIDED; + if(meshBothSides) + flags |= QUERY_MODIFIER_MESH_BOTH_SIDES; + return flags; +} + +static Ps::IntBool boxSweepVsMesh(SweepHit& h, const BV4Tree& tree, const float* meshPos, const float* meshRot, const Box& box, const PxVec3& dir, float maxDist, bool anyHit, bool doubleSided, bool meshBothSides) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + const PxU32 flags = setupFlags(anyHit, doubleSided, meshBothSides); + return BV4_BoxSweepSingle(box, dir, maxDist, tree, TM, &h, flags); +} + +static Ps::IntBool sphereSweepVsMesh(SweepHit& h, const BV4Tree& tree, const PxVec3& center, float radius, const PxVec3& dir, float maxDist, const PxMat44* TM, const PxU32 flags) +{ + // PT: TODO: avoid this copy (TA34704) + const Sphere tmp(center, radius); + + return BV4_SphereSweepSingle(tmp, dir, maxDist, tree, TM, &h, flags); +} + +static bool capsuleSweepVsMesh(SweepHit& h, const BV4Tree& tree, const Capsule& capsule, const PxVec3& dir, float maxDist, const PxMat44* TM, const PxU32 flags) +{ + Capsule localCapsule; + computeLocalCapsule(localCapsule, capsule, TM); + + // PT: TODO: optimize + PxVec3 localDir, unused; + computeLocalRay(localDir, unused, dir, dir, TM); + + const PxVec3 capsuleDir = localCapsule.p1 - localCapsule.p0; + PxU32 nbNullComponents = 0; + const float epsilon = 1e-3f; + if(PxAbs(capsuleDir.x)transform(h.mPos); + h.mNormal = TM->rotate(h.mNormal); + } + return status!=0; +} + +static PX_FORCE_INLINE void boxSweepVsMeshCBOld(const BV4Tree& tree, const float* meshPos, const float* meshRot, const PxVec3& center, const PxVec3& extents, const PxVec3& dir, float maxDist, MeshSweepCallback callback, void* userData) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + BV4_GenericSweepCB_Old(center, extents, dir, maxDist, tree, TM, callback, userData); +} + +// + +static PX_FORCE_INLINE bool raycastVsMesh(PxRaycastHit& hitData, const BV4Tree& tree, const float* meshPos, const float* meshRot, const PxVec3& orig, const PxVec3& dir, float maxDist, float geomEpsilon, bool doubleSided, PxHitFlags hitFlags) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + const PxU32 flags = setupFlags(anyHit, doubleSided, false); + + if(!BV4_RaycastSingle(orig, dir, tree, TM, &hitData, maxDist, geomEpsilon, flags, hitFlags)) + return false; + + return true; +} + +/*static PX_FORCE_INLINE PxU32 raycastVsMeshAll(PxRaycastHit* hits, PxU32 maxNbHits, const BV4Tree& tree, const float* meshPos, const float* meshRot, const PxVec3& orig, const PxVec3& dir, float maxDist, float geomEpsilon, bool doubleSided, PxHitFlags hitFlags) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + const PxU32 flags = setupFlags(anyHit, doubleSided, false); + + return BV4_RaycastAll(orig, dir, tree, TM, hits, maxNbHits, maxDist, geomEpsilon, flags, hitFlags); +}*/ + +static PX_FORCE_INLINE void raycastVsMeshCB(const BV4Tree& tree, const PxVec3& orig, const PxVec3& dir, float maxDist, float geomEpsilon, bool doubleSided, MeshRayCallback callback, void* userData) +{ + const PxU32 flags = setupFlags(false, doubleSided, false); + BV4_RaycastCB(orig, dir, tree, NULL, maxDist, geomEpsilon, flags, callback, userData); +} + +struct BV4RaycastCBParams +{ + PX_FORCE_INLINE BV4RaycastCBParams( PxRaycastHit* hits, PxU32 maxHits, const PxMeshScale* scale, const PxTransform* pose, + const Cm::Matrix34* world2vertexSkew, PxU32 hitFlags, + const PxVec3& rayDir, bool isDoubleSided, float distCoeff) : + mDstBase (hits), + mHitNum (0), + mMaxHits (maxHits), + mScale (scale), + mPose (pose), + mWorld2vertexSkew (world2vertexSkew), + mHitFlags (hitFlags), + mRayDir (rayDir), + mIsDoubleSided (isDoubleSided), + mDistCoeff (distCoeff) + { + } + + PxRaycastHit* mDstBase; + PxU32 mHitNum; + PxU32 mMaxHits; + const PxMeshScale* mScale; + const PxTransform* mPose; + const Cm::Matrix34* mWorld2vertexSkew; + PxU32 mHitFlags; + const PxVec3& mRayDir; + bool mIsDoubleSided; + float mDistCoeff; + +private: + BV4RaycastCBParams& operator=(const BV4RaycastCBParams&); +}; + +static PX_FORCE_INLINE PxVec3 processLocalNormal(const Cm::Matrix34* PX_RESTRICT world2vertexSkew, const PxTransform* PX_RESTRICT pose, const PxVec3& localNormal, const PxVec3& rayDir, const bool isDoubleSided) +{ + PxVec3 normal; + if(world2vertexSkew) + normal = world2vertexSkew->rotateTranspose(localNormal); + else + normal = pose->rotate(localNormal); + normal.normalize(); + + // PT: figure out correct normal orientation (DE7458) + // - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES. + // - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction. + if(isDoubleSided && normal.dot(rayDir) > 0.0f) + normal = -normal; + return normal; +} + +static HitCode gRayCallback(void* userData, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxU32 triangleIndex, float dist, float u, float v) +{ + BV4RaycastCBParams* params = reinterpret_cast(userData); + +//const bool last = params->mHitNum == params->mMaxHits; + + //not worth concatenating to do 1 transform: PxMat34Legacy vertex2worldSkew = scaling.getVertex2WorldSkew(absPose); + // PT: TODO: revisit this for N hits + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& hit = reinterpret_cast(buffer); +//PxRaycastHit& hit = last ? (PxRaycastHit&)buffer : params->mDstBase[params->mHitNum]; + + hit.distance = dist * params->mDistCoeff; + hit.u = u; + hit.v = v; + hit.faceIndex = triangleIndex; + + PxVec3 localImpact = (1.0f - u - v)*lp0 + u*lp1 + v*lp2; + if(params->mWorld2vertexSkew) + { + localImpact = params->mScale->transform(localImpact); + if(params->mScale->hasNegativeDeterminant()) + Ps::swap(hit.u, hit.v); // have to swap the UVs though since they were computed in mesh local space + } + + hit.position = params->mPose->transform(localImpact); + hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + + PxVec3 normal(0.0f); + // Compute additional information if needed + if(params->mHitFlags & PxHitFlag::eNORMAL) + { + const PxVec3 localNormal = (lp1 - lp0).cross(lp2 - lp0); + normal = processLocalNormal(params->mWorld2vertexSkew, params->mPose, localNormal, params->mRayDir, params->mIsDoubleSided); + hit.flags |= PxHitFlag::eNORMAL; + } + hit.normal = normal; + + // PT: no callback => store results in provided buffer + if(params->mHitNum == params->mMaxHits) +// if(last) + return HIT_EXIT; + + params->mDstBase[params->mHitNum++] = hit; +// params->mHitNum++; + + return HIT_NONE; +} + +PxU32 physx::Gu::raycast_triangleMesh_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast(mesh); + + const bool multipleHits = (maxHits > 1); + const bool idtScale = meshGeom.scale.isIdentity(); + + const bool isDoubleSided = meshGeom.meshFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED); + const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES); + + const BV4Tree& tree = static_cast(meshData)->getBV4Tree(); + if(idtScale && !multipleHits) + { + bool b = raycastVsMesh(*hits, tree, &pose.p.x, &pose.q.x, rayOrigin, rayDir, maxDist, meshData->getGeomEpsilon(), bothSides, hitFlags); + if(b) + { + PxHitFlags dstFlags = PxHitFlag::ePOSITION|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + + // PT: TODO: pass flags to BV4 code (TA34704) + if(hitFlags & PxHitFlag::eNORMAL) + { + dstFlags |= PxHitFlag::eNORMAL; + if(isDoubleSided) + { + PxVec3 normal = hits->normal; + // PT: figure out correct normal orientation (DE7458) + // - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES. + // - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction. + if(normal.dot(rayDir) > 0.0f) + normal = -normal; + hits->normal = normal; + } + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = dstFlags; + } + return PxU32(b); + } + +/* + if(idtScale && multipleHits) + { + PxU32 nbHits = raycastVsMeshAll(hits, maxHits, tree, &pose.p.x, &pose.q.x, rayOrigin, rayDir, maxDist, meshData->getGeomEpsilon(), bothSides, hitFlags); + + return nbHits; + } +*/ + + //scaling: transform the ray to vertex space + PxVec3 orig, dir; + Cm::Matrix34 world2vertexSkew; + Cm::Matrix34* world2vertexSkewP = NULL; + PxReal distCoeff = 1.0f; + if(idtScale) + { + orig = pose.transformInv(rayOrigin); + dir = pose.rotateInv(rayDir); + } + else + { + world2vertexSkew = meshGeom.scale.getInverse() * pose.getInverse(); + world2vertexSkewP = &world2vertexSkew; + orig = world2vertexSkew.transform(rayOrigin); + dir = world2vertexSkew.rotate(rayDir); + { + distCoeff = dir.normalize(); + maxDist *= distCoeff; + maxDist += 1e-3f; + distCoeff = 1.0f/distCoeff; + } + } + + if(!multipleHits) + { + bool b = raycastVsMesh(*hits, tree, NULL, NULL, orig, dir, maxDist, meshData->getGeomEpsilon(), bothSides, hitFlags); + if(b) + { + hits->distance *= distCoeff; + hits->position = pose.transform(meshGeom.scale.transform(hits->position)); + PxHitFlags dstFlags = PxHitFlag::ePOSITION|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + + if(meshGeom.scale.hasNegativeDeterminant()) + Ps::swap(hits->u, hits->v); // have to swap the UVs though since they were computed in mesh local space + + // PT: TODO: pass flags to BV4 code (TA34704) + // Compute additional information if needed + if(hitFlags & PxHitFlag::eNORMAL) + { + dstFlags |= PxHitFlag::eNORMAL; + hits->normal = processLocalNormal(world2vertexSkewP, &pose, hits->normal, rayDir, isDoubleSided); + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = dstFlags; + } + return PxU32(b); + } + + BV4RaycastCBParams callback(hits, maxHits, &meshGeom.scale, &pose, world2vertexSkewP, hitFlags, rayDir, isDoubleSided, distCoeff); + + raycastVsMeshCB( static_cast(meshData)->getBV4Tree(), + orig, dir, + maxDist, meshData->getGeomEpsilon(), bothSides, + gRayCallback, &callback); + return callback.mHitNum; +} + +namespace +{ +struct IntersectShapeVsMeshCallback +{ + IntersectShapeVsMeshCallback(LimitedResults* results, bool flipNormal) : mResults(results), mAnyHits(false), mFlipNormal(flipNormal) {} + + LimitedResults* mResults; + bool mAnyHits; + bool mFlipNormal; + + PX_FORCE_INLINE bool recordHit(PxU32 faceIndex, Ps::IntBool hit) + { + if(hit) + { + mAnyHits = true; + if(mResults) + mResults->add(faceIndex); + else + return false; // abort traversal if we are only interested in firstContact (mResults is NULL) + } + return true; // if we are here, either no triangles were hit or multiple results are expected => continue traversal + } +}; + +// PT: TODO: get rid of this (TA34704) +struct IntersectSphereVsMeshCallback : IntersectShapeVsMeshCallback +{ + PX_FORCE_INLINE IntersectSphereVsMeshCallback(const PxMeshScale& meshScale, const PxTransform& meshTransform, const Sphere& sphere, LimitedResults* r, bool flipNormal) + : IntersectShapeVsMeshCallback(r, flipNormal) + { + mVertexToShapeSkew = meshScale.toMat33(); + mLocalCenter = meshTransform.transformInv(sphere.center); // sphereCenterInMeshSpace + mSphereRadius2 = sphere.radius*sphere.radius; + } + + PxMat33 mVertexToShapeSkew; + PxVec3 mLocalCenter; // PT: sphere center in local/mesh space + PxF32 mSphereRadius2; + + PX_FORCE_INLINE PxAgain processHit(PxU32 faceIndex, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2) + { + const Vec3V v0 = V3LoadU(mVertexToShapeSkew * av0); + const Vec3V v1 = V3LoadU(mVertexToShapeSkew * (mFlipNormal ? av2 : av1)); + const Vec3V v2 = V3LoadU(mVertexToShapeSkew * (mFlipNormal ? av1 : av2)); + + FloatV dummy1, dummy2; + Vec3V closestP; + PxReal dist2; + FStore(distancePointTriangleSquared(V3LoadU(mLocalCenter), v0, v1, v2, dummy1, dummy2, closestP), &dist2); + return recordHit(faceIndex, dist2 <= mSphereRadius2); + } +}; + +// PT: TODO: get rid of this (TA34704) +struct IntersectCapsuleVsMeshCallback : IntersectShapeVsMeshCallback +{ + PX_FORCE_INLINE IntersectCapsuleVsMeshCallback(const PxMeshScale& meshScale, const PxTransform& meshTransform, const Capsule& capsule, LimitedResults* r, bool flipNormal) + : IntersectShapeVsMeshCallback(r, flipNormal) + { + mVertexToShapeSkew = meshScale.toMat33(); + + // transform world capsule to mesh shape space + mLocalCapsule.p0 = meshTransform.transformInv(capsule.p0); + mLocalCapsule.p1 = meshTransform.transformInv(capsule.p1); + mLocalCapsule.radius = capsule.radius; + mParams.init(mLocalCapsule); + } + + PxMat33 mVertexToShapeSkew; + Capsule mLocalCapsule; // PT: capsule in mesh/local space + CapsuleTriangleOverlapData mParams; + + PX_FORCE_INLINE PxAgain processHit(PxU32 faceIndex, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2) + { + const PxVec3 v0 = mVertexToShapeSkew * av0; + const PxVec3 v1 = mVertexToShapeSkew * (mFlipNormal ? av2 : av1); + const PxVec3 v2 = mVertexToShapeSkew * (mFlipNormal ? av1 : av2); + const PxVec3 normal = (v0 - v1).cross(v0 - v2); + bool hit = intersectCapsuleTriangle(normal, v0, v1, v2, mLocalCapsule, mParams); + return recordHit(faceIndex, hit); + } +}; + +// PT: TODO: get rid of this (TA34704) +struct IntersectBoxVsMeshCallback : IntersectShapeVsMeshCallback +{ + PX_FORCE_INLINE IntersectBoxVsMeshCallback(const PxMeshScale& meshScale, const PxTransform& meshTransform, const Box& box, LimitedResults* r, bool flipNormal) + : IntersectShapeVsMeshCallback(r, flipNormal) + { + const PxMat33 vertexToShapeSkew = meshScale.toMat33(); + + // mesh scale needs to be included - inverse transform and optimize the box + const PxMat33 vertexToWorldSkew_Rot = PxMat33Padded(meshTransform.q) * vertexToShapeSkew; + const PxVec3& vertexToWorldSkew_Trans = meshTransform.p; + + Matrix34 tmp; + buildMatrixFromBox(tmp, box); + const Matrix34 inv = tmp.getInverseRT(); + const Matrix34 _vertexToWorldSkew(vertexToWorldSkew_Rot, vertexToWorldSkew_Trans); + + mVertexToBox = inv * _vertexToWorldSkew; + mBoxCenter = PxVec3(0.0f); + mBoxExtents = box.extents; // extents do not change + } + + Matrix34 mVertexToBox; + Vec3p mBoxExtents, mBoxCenter; + + PX_FORCE_INLINE PxAgain processHit(PxU32 faceIndex, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2) + { + const Vec3p v0 = mVertexToBox.transform(av0); + const Vec3p v1 = mVertexToBox.transform(mFlipNormal ? av2 : av1); + const Vec3p v2 = mVertexToBox.transform(mFlipNormal ? av1 : av2); + + // PT: this one is safe because we're using Vec3p for all parameters + const Ps::IntBool hit = intersectTriangleBox_Unsafe(mBoxCenter, mBoxExtents, v0, v1, v2); + return recordHit(faceIndex, hit); + } +}; +} + +static bool gSphereVsMeshCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* /*vertexIndices*/) +{ + IntersectSphereVsMeshCallback* callback = reinterpret_cast(userData); + return !callback->processHit(triangleIndex, p0, p1, p2); +} + +static bool gCapsuleVsMeshCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* /*vertexIndices*/) +{ + IntersectCapsuleVsMeshCallback* callback = reinterpret_cast(userData); + return !callback->processHit(triangleIndex, p0, p1, p2); +} + +static bool gBoxVsMeshCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* /*vertexIndices*/) +{ + IntersectBoxVsMeshCallback* callback = reinterpret_cast(userData); + return !callback->processHit(triangleIndex, p0, p1, p2); +} + +bool physx::Gu::intersectSphereVsMesh_BV4(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4Tree& tree = static_cast(triMesh).getBV4Tree(); + + if(meshScale.isIdentity()) + { + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &meshTransform.p.x, &meshTransform.q.x); + if(results) + { + const PxU32 nbResults = BV4_OverlapSphereAll(sphere, tree, TM, results->mResults, results->mMaxResults, results->mOverflow); + results->mNbResults = nbResults; + return nbResults!=0; + } + else + { + return BV4_OverlapSphereAny(sphere, tree, TM)!=0; + } + } + else + { + // PT: TODO: we don't need to use this callback here (TA34704) + IntersectSphereVsMeshCallback callback(meshScale, meshTransform, sphere, results, meshScale.hasNegativeDeterminant()); + + const Box worldOBB_(sphere.center, PxVec3(sphere.radius), PxMat33(PxIdentity)); + Box vertexOBB; + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + + BV4_OverlapBoxCB(vertexOBB, tree, gSphereVsMeshCallback, &callback); + return callback.mAnyHits; + } +} + +bool physx::Gu::intersectBoxVsMesh_BV4(const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4Tree& tree = static_cast(triMesh).getBV4Tree(); + + if(meshScale.isIdentity()) + { + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &meshTransform.p.x, &meshTransform.q.x); + if(results) + { + const PxU32 nbResults = BV4_OverlapBoxAll(box, tree, TM, results->mResults, results->mMaxResults, results->mOverflow); + results->mNbResults = nbResults; + return nbResults!=0; + } + else + { + return BV4_OverlapBoxAny(box, tree, TM)!=0; + } + } + else + { + // PT: TODO: we don't need to use this callback here (TA34704) + IntersectBoxVsMeshCallback callback(meshScale, meshTransform, box, results, meshScale.hasNegativeDeterminant()); + + Box vertexOBB; // query box in vertex space + computeVertexSpaceOBB(vertexOBB, box, meshTransform, meshScale); + + BV4_OverlapBoxCB(vertexOBB, tree, gBoxVsMeshCallback, &callback); + return callback.mAnyHits; + } +} + +bool physx::Gu::intersectCapsuleVsMesh_BV4(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4Tree& tree = static_cast(triMesh).getBV4Tree(); + + if(meshScale.isIdentity()) + { + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &meshTransform.p.x, &meshTransform.q.x); + if(results) + { + const PxU32 nbResults = BV4_OverlapCapsuleAll(capsule, tree, TM, results->mResults, results->mMaxResults, results->mOverflow); + results->mNbResults = nbResults; + return nbResults!=0; + } + else + { + return BV4_OverlapCapsuleAny(capsule, tree, TM)!=0; + } + } + else + { + // PT: TODO: we don't need to use this callback here (TA34704) + IntersectCapsuleVsMeshCallback callback(meshScale, meshTransform, capsule, results, meshScale.hasNegativeDeterminant()); + + // make vertex space OBB + Box vertexOBB; + Box worldOBB_; + worldOBB_.create(capsule); // AP: potential optimization (meshTransform.inverse is already in callback.mCapsule) + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + + BV4_OverlapBoxCB(vertexOBB, tree, gCapsuleVsMeshCallback, &callback); + return callback.mAnyHits; + } +} + +// PT: TODO: get rid of this (TA34704) +static bool gVolumeCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* vertexIndices) +{ + MeshHitCallback* callback = reinterpret_cast*>(userData); + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& hit = reinterpret_cast(buffer); + hit.faceIndex = triangleIndex; + PxReal dummy; + return !callback->processHit(hit, p0, p1, p2, dummy, vertexIndices); +} + +void physx::Gu::intersectOBB_BV4(const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned) +{ + PX_UNUSED(checkObbIsAligned); + PX_UNUSED(bothTriangleSidesCollide); + BV4_OverlapBoxCB(obb, static_cast(mesh)->getBV4Tree(), gVolumeCallback, &callback); +} + + + + +#include "GuVecCapsule.h" +#include "GuSweepMTD.h" + +static bool gCapsuleMeshSweepCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist) +{ + SweepCapsuleMeshHitCallback* callback = reinterpret_cast(userData); + PxRaycastHit meshHit; + meshHit.faceIndex = triangleIndex; + return !callback->SweepCapsuleMeshHitCallback::processHit(meshHit, p0, p1, p2, dist, NULL/*vertexIndices*/); +} + +// PT: TODO: refactor/share bits of this (TA34704) +bool physx::Gu::sweepCapsule_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast(mesh); + + const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + bool isDoubleSided = (triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED); + const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + + if(isIdentity) + { + const BV4Tree& tree = meshData->getBV4Tree(); + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &pose.p.x, &pose.q.x); + + const PxU32 flags = setupFlags(anyHit, isDoubleSided, meshBothSides!=0); + + SweepHit hitData; + if(lss.p0==lss.p1) + { + if(!sphereSweepVsMesh(hitData, tree, inflatedCapsule.p0, inflatedCapsule.radius, unitDir, distance, TM, flags)) + return false; + } + else + { + if(!capsuleSweepVsMesh(hitData, tree, inflatedCapsule, unitDir, distance, TM, flags)) + return false; + } + + sweepHit.distance = hitData.mDistance; + sweepHit.position = hitData.mPos; + sweepHit.normal = hitData.mNormal; + sweepHit.faceIndex = hitData.mTriangleID; + + if(hitData.mDistance==0.0f) + { + sweepHit.flags = PxHitFlag::eNORMAL; + + if(meshBothSides) + isDoubleSided = true; + + // PT: TODO: consider using 'setInitialOverlapResults' here + bool hasContacts = false; + if(hitFlags & PxHitFlag::eMTD) + { + const Vec3V p0 = V3LoadU(inflatedCapsule.p0); + const Vec3V p1 = V3LoadU(inflatedCapsule.p1); + const FloatV radius = FLoad(lss.radius); + CapsuleV capsuleV; + capsuleV.initialize(p0, p1, radius); + + //we need to calculate the MTD + hasContacts = computeCapsule_TriangleMeshMTD(triMeshGeom, pose, capsuleV, inflatedCapsule.radius, isDoubleSided, sweepHit); + } + setupSweepHitForMTD(sweepHit, hasContacts, unitDir); + } + else + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + return true; + } + + // compute sweptAABB + const PxVec3 localP0 = pose.transformInv(inflatedCapsule.p0); + const PxVec3 localP1 = pose.transformInv(inflatedCapsule.p1); + PxVec3 sweepOrigin = (localP0+localP1)*0.5f; + PxVec3 sweepDir = pose.rotateInv(unitDir); + PxVec3 sweepExtents = PxVec3(inflatedCapsule.radius) + (localP0-localP1).abs()*0.5f; + PxReal distance1 = distance; + PxReal distCoef = 1.0f; + Matrix34 poseWithScale; + if(!isIdentity) + { + poseWithScale = pose * triMeshGeom.scale; + distance1 = computeSweepData(triMeshGeom, sweepOrigin, sweepExtents, sweepDir, distance); + distCoef = distance1 / distance; + } else + poseWithScale = Matrix34(pose); + + SweepCapsuleMeshHitCallback callback(sweepHit, poseWithScale, distance, isDoubleSided, inflatedCapsule, unitDir, hitFlags, triMeshGeom.scale.hasNegativeDeterminant(), distCoef); + + boxSweepVsMeshCBOld(meshData->getBV4Tree(), NULL, NULL, sweepOrigin, sweepExtents, sweepDir, distance1, gCapsuleMeshSweepCallback, &callback); + + if(meshBothSides) + isDoubleSided = true; + + return callback.finalizeHit(sweepHit, inflatedCapsule, triMeshGeom, pose, isDoubleSided); +} + +#include "GuSweepSharedTests.h" +static bool gBoxMeshSweepCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist) +{ + SweepBoxMeshHitCallback* callback = reinterpret_cast(userData); + PxRaycastHit meshHit; + meshHit.faceIndex = triangleIndex; + return !callback->SweepBoxMeshHitCallback::processHit(meshHit, p0, p1, p2, dist, NULL/*vertexIndices*/); +} + +// PT: TODO: refactor/share bits of this (TA34704) +bool physx::Gu::sweepBox_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast(mesh); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool isDoubleSided = triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED; + + if(isIdentity && inflation==0.0f) + { + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + + // PT: TODO: this is wrong, we shouldn't actually sweep the inflated version +// const PxVec3 inflated = (box.extents + PxVec3(inflation)) * 1.01f; + // PT: TODO: avoid this copy +// const Box tmp(box.center, inflated, box.rot); + + SweepHit hitData; +// if(!boxSweepVsMesh(hitData, meshData->getBV4Tree(), &pose.p.x, &pose.q.x, tmp, unitDir, distance, anyHit, isDoubleSided, meshBothSides)) + if(!boxSweepVsMesh(hitData, meshData->getBV4Tree(), &pose.p.x, &pose.q.x, box, unitDir, distance, anyHit, isDoubleSided, meshBothSides)) + return false; + + sweepHit.distance = hitData.mDistance; + sweepHit.position = hitData.mPos; + sweepHit.normal = hitData.mNormal; + sweepHit.faceIndex = hitData.mTriangleID; + + if(hitData.mDistance==0.0f) + { + sweepHit.flags = PxHitFlag::eNORMAL; + + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + const PxTransform boxTransform = box.getTransform(); + + bool hasContacts = false; + if(hitFlags & PxHitFlag::eMTD) + hasContacts = computeBox_TriangleMeshMTD(triMeshGeom, pose, box, boxTransform, inflation, bothTriangleSidesCollide, sweepHit); + + setupSweepHitForMTD(sweepHit, hasContacts, unitDir); + } + else + { + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + } + return true; + } + + // PT: TODO: revisit this codepath, we don't need to sweep an AABB all the time (TA34704) + + Matrix34 meshToWorldSkew; + PxVec3 sweptAABBMeshSpaceExtents, meshSpaceOrigin, meshSpaceDir; + + // Input sweep params: geom, pose, box, unitDir, distance + // We convert the origin from world space to mesh local space + // and convert the box+pose to mesh space AABB + if(isIdentity) + { + meshToWorldSkew = Matrix34(pose); + PxMat33 worldToMeshRot(pose.q.getConjugate()); // extract rotation matrix from pose.q + meshSpaceOrigin = worldToMeshRot.transform(box.center - pose.p); + meshSpaceDir = worldToMeshRot.transform(unitDir) * distance; + PxMat33 boxToMeshRot = worldToMeshRot * box.rot; + sweptAABBMeshSpaceExtents = boxToMeshRot.column0.abs() * box.extents.x + + boxToMeshRot.column1.abs() * box.extents.y + + boxToMeshRot.column2.abs() * box.extents.z; + } + else + { + meshToWorldSkew = pose * triMeshGeom.scale; + const PxMat33 meshToWorldSkew_Rot = PxMat33Padded(pose.q) * triMeshGeom.scale.toMat33(); + const PxVec3& meshToWorldSkew_Trans = pose.p; + + PxMat33 worldToVertexSkew_Rot; + PxVec3 worldToVertexSkew_Trans; + getInverse(worldToVertexSkew_Rot, worldToVertexSkew_Trans, meshToWorldSkew_Rot, meshToWorldSkew_Trans); + + //make vertex space OBB + Box vertexSpaceBox1; + const Matrix34 worldToVertexSkew(worldToVertexSkew_Rot, worldToVertexSkew_Trans); + vertexSpaceBox1 = transform(worldToVertexSkew, box); + // compute swept aabb + sweptAABBMeshSpaceExtents = vertexSpaceBox1.computeAABBExtent(); + + meshSpaceOrigin = worldToVertexSkew.transform(box.center); + meshSpaceDir = worldToVertexSkew.rotate(unitDir*distance); // also applies scale to direction/length + } + + sweptAABBMeshSpaceExtents += PxVec3(inflation); // inflate the bounds with additive inflation + sweptAABBMeshSpaceExtents *= 1.01f; // fatten the bounds to account for numerical discrepancies + + PxReal dirLen = PxMax(meshSpaceDir.magnitude(), 1e-5f); + PxReal distCoeff = 1.0f; + if (!isIdentity) + distCoeff = dirLen / distance; + + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + + const Matrix34Padded meshToBox = worldToBox*meshToWorldSkew; + const PxTransform boxTransform = box.getTransform(); // PT: TODO: this is not needed when there's no hit (TA34704) + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localDirDist = localDir*distance; + SweepBoxMeshHitCallback callback( // using eMULTIPLE with shrinkMaxT + CallbackMode::eMULTIPLE, meshToBox, distance, bothTriangleSidesCollide, box, localDirDist, localDir, unitDir, hitFlags, inflation, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff); + + const PxVec3 dir = meshSpaceDir/dirLen; + boxSweepVsMeshCBOld(meshData->getBV4Tree(), NULL, NULL, meshSpaceOrigin, sweptAABBMeshSpaceExtents, dir, dirLen, gBoxMeshSweepCallback, &callback); + + return callback.finalizeHit(sweepHit, triMeshGeom, pose, boxTransform, localDir, meshBothSides, isDoubleSided); +} + +static bool gConvexVsMeshSweepCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist) +{ + SweepConvexMeshHitCallback* callback = reinterpret_cast(userData); + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& hit = reinterpret_cast(buffer); + hit.faceIndex = triangleIndex; + return !callback->SweepConvexMeshHitCallback::processHit(hit, p0, p1, p2, dist, NULL/*vertexIndices*/); +} + +void physx::Gu::sweepConvex_MeshGeom_BV4(const TriangleMesh* mesh, const Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast(mesh); + BV4_GenericSweepCB(hullBox, localDir, distance, meshData->getBV4Tree(), gConvexVsMeshSweepCallback, &callback, anyHit); +} + +#endif + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h new file mode 100644 index 000000000..d9574476b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h @@ -0,0 +1,417 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MIDPHASE_INTERFACE_H +#define GU_MIDPHASE_INTERFACE_H + +#include "GuOverlapTests.h" +#include "GuRaycastTests.h" +#include "GuTriangleMesh.h" +#include "PsVecMath.h" + +// PT: this file contains the common interface for all midphase implementations. Specifically the Midphase namespace contains the +// midphase-related entry points, dispatching calls to the proper implementations depending on the triangle mesh's type. The rest of it +// is simply classes & structs shared by all implementations. + +namespace physx +{ + class PxMeshScale; + class PxTriangleMeshGeometry; +namespace Cm +{ + class Matrix34; + class FastVertex2ShapeScaling; +} + +namespace Gu +{ + struct ConvexHullData; + + struct CallbackMode { enum Enum { eANY, eCLOSEST, eMULTIPLE }; }; + + template + struct MeshHitCallback + { + CallbackMode::Enum mode; + + MeshHitCallback(CallbackMode::Enum aMode) : mode(aMode) {} + + PX_FORCE_INLINE bool inAnyMode() const { return mode == CallbackMode::eANY; } + PX_FORCE_INLINE bool inClosestMode() const { return mode == CallbackMode::eCLOSEST; } + PX_FORCE_INLINE bool inMultipleMode() const { return mode == CallbackMode::eMULTIPLE; } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const HitType& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32* vIndices) = 0; + + virtual ~MeshHitCallback() {} + }; + + struct SweepConvexMeshHitCallback; + + struct LimitedResults + { + PxU32* mResults; + PxU32 mNbResults; + PxU32 mMaxResults; + PxU32 mStartIndex; + PxU32 mNbSkipped; + bool mOverflow; + + PX_FORCE_INLINE LimitedResults(PxU32* results, PxU32 maxResults, PxU32 startIndex) + : mResults(results), mMaxResults(maxResults), mStartIndex(startIndex) + { + reset(); + } + + PX_FORCE_INLINE void reset() + { + mNbResults = 0; + mNbSkipped = 0; + mOverflow = false; + } + + PX_FORCE_INLINE bool add(PxU32 index) + { + if(mNbResults>=mMaxResults) + { + mOverflow = true; + return false; + } + + if(mNbSkipped>=mStartIndex) + mResults[mNbResults++] = index; + else + mNbSkipped++; + + return true; + } + }; + + // RTree forward declarations + PX_PHYSX_COMMON_API PxU32 raycast_triangleMesh_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits); + PX_PHYSX_COMMON_API bool intersectSphereVsMesh_RTREE(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectBoxVsMesh_RTREE (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectCapsuleVsMesh_RTREE(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API void intersectOBB_RTREE(const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned); + PX_PHYSX_COMMON_API bool sweepCapsule_MeshGeom_RTREE( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API bool sweepBox_MeshGeom_RTREE( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API void sweepConvex_MeshGeom_RTREE(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit); + +#if PX_INTEL_FAMILY + // BV4 forward declarations + PX_PHYSX_COMMON_API PxU32 raycast_triangleMesh_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits); + PX_PHYSX_COMMON_API bool intersectSphereVsMesh_BV4 (const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectBoxVsMesh_BV4 (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectCapsuleVsMesh_BV4 (const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API void intersectOBB_BV4(const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned); + PX_PHYSX_COMMON_API bool sweepCapsule_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API bool sweepBox_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API void sweepConvex_MeshGeom_BV4(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit); +#endif + + typedef PxU32 (*MidphaseRaycastFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits); + + typedef bool (*MidphaseSphereOverlapFunction) (const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + typedef bool (*MidphaseBoxOverlapFunction) (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + typedef bool (*MidphaseCapsuleOverlapFunction) (const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + typedef void (*MidphaseBoxCBOverlapFunction) (const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned); + + typedef bool (*MidphaseCapsuleSweepFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + typedef bool (*MidphaseBoxSweepFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + typedef void (*MidphaseConvexSweepFunction)( const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit); + +namespace Midphase +{ + PX_FORCE_INLINE bool outputError() + { + static bool reportOnlyOnce = false; + if(!reportOnlyOnce) + { + reportOnlyOnce = true; + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "BV4 midphase only supported on Intel platforms."); + } + return false; + } +} + + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + #else + static PxU32 unsupportedMidphase( const TriangleMesh*, const PxTriangleMeshGeometry&, const PxTransform&, + const PxVec3&, const PxVec3&, PxReal, + PxHitFlags, PxU32, PxRaycastHit* PX_RESTRICT) + { + return PxU32(Midphase::outputError()); + } + static bool unsupportedSphereOverlapMidphase(const Sphere&, const TriangleMesh&, const PxTransform&, const PxMeshScale&, LimitedResults*) + { + return Midphase::outputError(); + } + static bool unsupportedBoxOverlapMidphase(const Box&, const TriangleMesh&, const PxTransform&, const PxMeshScale&, LimitedResults*) + { + return Midphase::outputError(); + } + static bool unsupportedCapsuleOverlapMidphase(const Capsule&, const TriangleMesh&, const PxTransform&, const PxMeshScale&, LimitedResults*) + { + return Midphase::outputError(); + } + static void unsupportedBoxCBOverlapMidphase(const TriangleMesh*, const Box&, MeshHitCallback&, bool, bool) + { + Midphase::outputError(); + } + static bool unsupportedBoxSweepMidphase(const TriangleMesh*, const PxTriangleMeshGeometry&, const PxTransform&, const Gu::Box&, const PxVec3&, const PxReal, PxSweepHit&, PxHitFlags, const PxReal) + { + return Midphase::outputError(); + } + static bool unsupportedCapsuleSweepMidphase(const TriangleMesh*, const PxTriangleMeshGeometry&, const PxTransform&, const Gu::Capsule&, const PxVec3&, const PxReal, PxSweepHit&, PxHitFlags, const PxReal) + { + return Midphase::outputError(); + } + static void unsupportedConvexSweepMidphase(const TriangleMesh*, const Gu::Box&, const PxVec3&, const PxReal, SweepConvexMeshHitCallback&, bool) + { + Midphase::outputError(); + } + #endif + + static const MidphaseRaycastFunction gMidphaseRaycastTable[PxMeshMidPhase::eLAST] = + { + raycast_triangleMesh_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + raycast_triangleMesh_BV4, + #else + unsupportedMidphase, + #endif + }; + + static const MidphaseSphereOverlapFunction gMidphaseSphereOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectSphereVsMesh_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + intersectSphereVsMesh_BV4, + #else + unsupportedSphereOverlapMidphase, + #endif + }; + + static const MidphaseBoxOverlapFunction gMidphaseBoxOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectBoxVsMesh_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + intersectBoxVsMesh_BV4, + #else + unsupportedBoxOverlapMidphase, + #endif + }; + + static const MidphaseCapsuleOverlapFunction gMidphaseCapsuleOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectCapsuleVsMesh_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + intersectCapsuleVsMesh_BV4, + #else + unsupportedCapsuleOverlapMidphase, + #endif + }; + + static const MidphaseBoxCBOverlapFunction gMidphaseBoxCBOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectOBB_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + intersectOBB_BV4, + #else + unsupportedBoxCBOverlapMidphase, + #endif + }; + + static const MidphaseBoxSweepFunction gMidphaseBoxSweepTable[PxMeshMidPhase::eLAST] = + { + sweepBox_MeshGeom_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + sweepBox_MeshGeom_BV4, + #else + unsupportedBoxSweepMidphase, + #endif + }; + + static const MidphaseCapsuleSweepFunction gMidphaseCapsuleSweepTable[PxMeshMidPhase::eLAST] = + { + sweepCapsule_MeshGeom_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + sweepCapsule_MeshGeom_BV4, + #else + unsupportedCapsuleSweepMidphase, + #endif + }; + + static const MidphaseConvexSweepFunction gMidphaseConvexSweepTable[PxMeshMidPhase::eLAST] = + { + sweepConvex_MeshGeom_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + sweepConvex_MeshGeom_BV4, + #else + unsupportedConvexSweepMidphase, + #endif + }; + +namespace Midphase +{ + // \param[in] mesh triangle mesh to raycast against + // \param[in] meshGeom geometry object associated with the mesh + // \param[in] meshTransform pose/transform of geometry object + // \param[in] rayOrigin ray's origin + // \param[in] rayDir ray's unit dir + // \param[in] maxDist ray's length/max distance + // \param[in] hitFlags query behavior flags + // \param[in] maxHits max number of hits = size of 'hits' buffer + // \param[out] hits result buffer where to write raycast hits + // \return number of hits written to 'hits' result buffer + // \note there's no mechanism to report overflow. Returned number of hits is just clamped to maxHits. + PX_FORCE_INLINE PxU32 raycastTriangleMesh( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseRaycastTable[index](mesh, meshGeom, meshTransform, rayOrigin, rayDir, maxDist, hitFlags, maxHits, hits); + } + + // \param[in] sphere sphere + // \param[in] mesh triangle mesh + // \param[in] meshTransform pose/transform of triangle mesh + // \param[in] meshScale mesh scale + // \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough + // \return true if at least one overlap has been found + PX_FORCE_INLINE bool intersectSphereVsMesh(const Sphere& sphere, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) + { + const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseSphereOverlapTable[index](sphere, mesh, meshTransform, meshScale, results); + } + + // \param[in] box box + // \param[in] mesh triangle mesh + // \param[in] meshTransform pose/transform of triangle mesh + // \param[in] meshScale mesh scale + // \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough + // \return true if at least one overlap has been found + PX_FORCE_INLINE bool intersectBoxVsMesh(const Box& box, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) + { + const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseBoxOverlapTable[index](box, mesh, meshTransform, meshScale, results); + } + + // \param[in] capsule capsule + // \param[in] mesh triangle mesh + // \param[in] meshTransform pose/transform of triangle mesh + // \param[in] meshScale mesh scale + // \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough + // \return true if at least one overlap has been found + PX_FORCE_INLINE bool intersectCapsuleVsMesh(const Capsule& capsule, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) + { + const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseCapsuleOverlapTable[index](capsule, mesh, meshTransform, meshScale, results); + } + + // \param[in] mesh triangle mesh + // \param[in] box box + // \param[in] callback callback object, called each time a hit is found + // \param[in] bothTriangleSidesCollide true for double-sided meshes + // \param[in] checkObbIsAligned true to use a dedicated codepath for axis-aligned boxes + PX_FORCE_INLINE void intersectOBB(const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned = true) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + gMidphaseBoxCBOverlapTable[index](mesh, obb, callback, bothTriangleSidesCollide, checkObbIsAligned); + } + + // \param[in] mesh triangle mesh + // \param[in] meshGeom geometry object associated with the mesh + // \param[in] meshTransform pose/transform of geometry object + // \param[in] capsule swept capsule + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] sweepHit hit result + // \param[in] hitFlags query behavior flags + // \param[in] inflation optional inflation value for swept shape + // \return true if a hit was found, false otherwise + PX_FORCE_INLINE bool sweepCapsuleVsMesh(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform, + const Gu::Capsule& capsule, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseCapsuleSweepTable[index](mesh, meshGeom, meshTransform, capsule, unitDir, distance, sweepHit, hitFlags, inflation); + } + + // \param[in] mesh triangle mesh + // \param[in] meshGeom geometry object associated with the mesh + // \param[in] meshTransform pose/transform of geometry object + // \param[in] box swept box + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] sweepHit hit result + // \param[in] hitFlags query behavior flags + // \param[in] inflation optional inflation value for swept shape + // \return true if a hit was found, false otherwise + PX_FORCE_INLINE bool sweepBoxVsMesh(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseBoxSweepTable[index](mesh, meshGeom, meshTransform, box, unitDir, distance, sweepHit, hitFlags, inflation); + } + + // \param[in] mesh triangle mesh + // \param[in] hullBox hull's bounding box + // \param[in] localDir sweep's unit dir, in local/mesh space + // \param[in] distance sweep's length/max distance + // \param[in] callback callback object, called each time a hit is found + // \param[in] anyHit true for PxHitFlag::eMESH_ANY queries + PX_FORCE_INLINE void sweepConvexVsMesh(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + gMidphaseConvexSweepTable[index](mesh, hullBox, localDir, distance, callback, anyHit); + } +} +} +} +#endif // GU_MIDPHASE_INTERFACE_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp new file mode 100644 index 000000000..f88f55381 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp @@ -0,0 +1,887 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "GuSweepMesh.h" +#include "GuIntersectionRayTriangle.h" +#include "GuIntersectionCapsuleTriangle.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionRayBoxSIMD.h" +#include "GuSphere.h" +#include "GuBoxConversion.h" +#include "GuConvexUtilsInternal.h" +#include "GuVecTriangle.h" +#include "GuIntersectionTriangleBox.h" +#include "GuSIMDHelpers.h" +#include "GuTriangleVertexPointers.h" +#include "GuRTree.h" +#include "GuTriangleMeshRTree.h" +#include "GuInternal.h" + +// This file contains code specific to the RTree midphase. + +using namespace physx; +using namespace Cm; +using namespace Gu; +using namespace physx::shdfnd::aos; + +struct MeshRayCollider +{ + template + PX_PHYSX_COMMON_API static void collide( + const PxVec3& orig, const PxVec3& dir, // dir is not normalized (full length), both in mesh space (unless meshWorld is non-zero) + PxReal maxT, // maxT is from [0,1], if maxT is 0.0f, AABB traversal will be used + bool bothTriangleSidesCollide, const RTreeTriangleMesh* mesh, MeshHitCallback& callback, + const PxVec3* inflate = NULL); + + PX_PHYSX_COMMON_API static void collideOBB( + const Box& obb, bool bothTriangleSidesCollide, const RTreeTriangleMesh* mesh, MeshHitCallback& callback, + bool checkObbIsAligned = true); // perf hint, pass false if obb is rarely axis aligned +}; + +class SimpleRayTriOverlap +{ +public: + PX_FORCE_INLINE SimpleRayTriOverlap(const PxVec3& origin, const PxVec3& dir, bool bothSides, PxReal geomEpsilon) + : mOrigin(origin), mDir(dir), mBothSides(bothSides), mGeomEpsilon(geomEpsilon) + { + } + + PX_FORCE_INLINE Ps::IntBool overlap(const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, PxRaycastHit& hit) const + { + if(!intersectRayTriangle(mOrigin, mDir, vert0, vert1, vert2, hit.distance, hit.u, hit.v, !mBothSides, mGeomEpsilon)) + return false; + + if(hit.distance< 0.0f) // test if the ray intersection t is negative + return false; + + return true; + } + + PxVec3 mOrigin; + PxVec3 mDir; + bool mBothSides; + PxReal mGeomEpsilon; +}; + +using Gu::RTree; + +// This callback comes from RTree and decodes LeafTriangle indices stored in rtree into actual triangles +// This callback is needed because RTree doesn't know that it stores triangles since it's a general purpose spatial index + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +template +struct RayRTreeCallback : RTree::CallbackRaycast, RTree::Callback +{ + MeshHitCallback& outerCallback; + PxI32 has16BitIndices; + const void* mTris; + const PxVec3* mVerts; + const PxVec3* mInflate; + const SimpleRayTriOverlap rayCollider; + PxReal maxT; + PxRaycastHit closestHit; // recorded closest hit over the whole traversal (only for callback mode eCLOSEST) + PxVec3 cv0, cv1, cv2; // PT: make sure these aren't last in the class, to safely V4Load them + PxU32 cis[3]; + bool hadClosestHit; + const bool closestMode; + Vec3V inflateV, rayOriginV, rayDirV; + + RayRTreeCallback( + PxReal geomEpsilon, MeshHitCallback& callback, + PxI32 has16BitIndices_, const void* tris, const PxVec3* verts, + const PxVec3& origin, const PxVec3& dir, PxReal maxT_, bool bothSides, const PxVec3* inflate) + : outerCallback(callback), has16BitIndices(has16BitIndices_), + mTris(tris), mVerts(verts), mInflate(inflate), rayCollider(origin, dir, bothSides, geomEpsilon), + maxT(maxT_), closestMode(callback.inClosestMode()) + { + PX_ASSERT(closestHit.distance == PX_MAX_REAL); + hadClosestHit = false; + if (tInflate) + inflateV = V3LoadU(*mInflate); + rayOriginV = V3LoadU(rayCollider.mOrigin); + rayDirV = V3LoadU(rayCollider.mDir); + } + + PX_FORCE_INLINE void getVertIndices(PxU32 triIndex, PxU32& i0, PxU32 &i1, PxU32 &i2) + { + if(has16BitIndices) + { + const PxU16* p = reinterpret_cast(mTris) + triIndex*3; + i0 = p[0]; i1 = p[1]; i2 = p[2]; + } + else + { + const PxU32* p = reinterpret_cast(mTris) + triIndex*3; + i0 = p[0]; i1 = p[1]; i2 = p[2]; + } + } + + virtual PX_FORCE_INLINE bool processResults(PxU32 NumTouched, PxU32* Touched, PxF32& newMaxT) + { + PX_ASSERT(NumTouched > 0); + // Loop through touched leaves + PxRaycastHit tempHit; + for(PxU32 leaf = 0; leaf0 so we can use integers + if (closestMode) + { + if(tempHit.distance < closestHit.distance) + { + closestHit = tempHit; + newMaxT = PxMin(tempHit.distance, newMaxT); + cv0 = v0; cv1 = v1; cv2 = v2; + cis[0] = vinds[0]; cis[1] = vinds[1]; cis[2] = vinds[2]; + hadClosestHit = true; + } + } else + { + PxReal shrunkMaxT = newMaxT; + PxAgain again = outerCallback.processHit(tempHit, v0, v1, v2, shrunkMaxT, vinds); + if (!again) + return false; + if (shrunkMaxT < newMaxT) + { + newMaxT = shrunkMaxT; + maxT = shrunkMaxT; + } + } + + if (outerCallback.inAnyMode()) // early out if in ANY mode + return false; + } + + } // for(PxU32 leaf = 0; leaf& callback, + bool checkObbIsAligned) +{ + const PxU32 maxResults = RTREE_N; // maxResults=rtree page size for more efficient early out + PxU32 buf[maxResults]; + RayRTreeCallback rTreeCallback( + mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(), + PxVec3(0), PxVec3(0), 0.0f, bothTriangleSidesCollide, NULL); + if (checkObbIsAligned && PxAbs(PxQuat(obb.rot).w) > 0.9999f) + { + PxVec3 aabbExtents = obb.computeAABBExtent(); + mi->getRTree().traverseAABB(obb.center - aabbExtents, obb.center + aabbExtents, maxResults, buf, &rTreeCallback); + } else + mi->getRTree().traverseOBB(obb, maxResults, buf, &rTreeCallback); +} + +template +void MeshRayCollider::collide( + const PxVec3& orig, const PxVec3& dir, PxReal maxT, bool bothSides, + const RTreeTriangleMesh* mi, MeshHitCallback& callback, + const PxVec3* inflate) +{ + const PxU32 maxResults = RTREE_N; // maxResults=rtree page size for more efficient early out + PxU32 buf[maxResults]; + if (maxT == 0.0f) // AABB traversal path + { + RayRTreeCallback rTreeCallback( + mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(), + orig, dir, maxT, bothSides, inflate); + PxVec3 inflate1 = tInflate ? *inflate : PxVec3(0); // both maxT and inflate can be zero, so need to check tInflate + mi->getRTree().traverseAABB(orig-inflate1, orig+inflate1, maxResults, buf, &rTreeCallback); + } + else // ray traversal path + { + RayRTreeCallback rTreeCallback( + mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(), + orig, dir, maxT, bothSides, inflate); + mi->getRTree().traverseRay(orig, dir, maxResults, buf, &rTreeCallback, inflate, maxT); + } +} + + +#define TINST(a,b) \ +template void MeshRayCollider::collide( \ + const PxVec3& orig, const PxVec3& dir, PxReal maxT, bool bothSides, const RTreeTriangleMesh* mesh, \ + MeshHitCallback& callback, const PxVec3* inflate); + +TINST(0,0) +TINST(1,0) +TINST(0,1) +TINST(1,1) + +#undef TINST + +#include "GuRaycastTests.h" +#include "PxTriangleMeshGeometry.h" +#include "GuTriangleMesh.h" +#include "CmScaling.h" + +struct RayMeshColliderCallback : public MeshHitCallback +{ + PxRaycastHit* mDstBase; + PxU32 mHitNum; + PxU32 mMaxHits; + const PxMeshScale* mScale; + const PxTransform* mPose; + const Matrix34* mWorld2vertexSkew; + PxU32 mHitFlags; + const PxVec3& mRayDir; + bool mIsDoubleSided; + float mDistCoeff; + + RayMeshColliderCallback( + CallbackMode::Enum mode_, PxRaycastHit* hits, PxU32 maxHits, const PxMeshScale* scale, const PxTransform* pose, + const Matrix34* world2vertexSkew, PxU32 hitFlags, const PxVec3& rayDir, bool isDoubleSided, float distCoeff) : + MeshHitCallback (mode_), + mDstBase (hits), + mHitNum (0), + mMaxHits (maxHits), + mScale (scale), + mPose (pose), + mWorld2vertexSkew (world2vertexSkew), + mHitFlags (hitFlags), + mRayDir (rayDir), + mIsDoubleSided (isDoubleSided), + mDistCoeff (distCoeff) + { + } + + // return false for early out + virtual bool processHit( + const PxRaycastHit& lHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal&, const PxU32*) + { + const PxReal u = lHit.u, v = lHit.v; + const PxVec3 localImpact = (1.0f - u - v)*lp0 + u*lp1 + v*lp2; + + //not worth concatenating to do 1 transform: PxMat34Legacy vertex2worldSkew = scaling.getVertex2WorldSkew(absPose); + // PT: TODO: revisit this for N hits + PxRaycastHit hit = lHit; + hit.position = mPose->transform(mScale->transform(localImpact)); + hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + hit.normal = PxVec3(0.0f); + hit.distance *= mDistCoeff; + + // Compute additional information if needed + if(mHitFlags & PxHitFlag::eNORMAL) + { + // User requested impact normal + const PxVec3 localNormal = (lp1 - lp0).cross(lp2 - lp0); + + if(mWorld2vertexSkew) + { + hit.normal = mWorld2vertexSkew->rotateTranspose(localNormal); + if (mScale->hasNegativeDeterminant()) + Ps::swap(hit.u, hit.v); // have to swap the UVs though since they were computed in mesh local space + } + else + hit.normal = hit.normal = mPose->rotate(localNormal); + hit.normal.normalize(); + + // PT: figure out correct normal orientation (DE7458) + // - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES. + // - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction. + if(mIsDoubleSided && hit.normal.dot(mRayDir) > 0.0f) + hit.normal = -hit.normal; + + hit.flags |= PxHitFlag::eNORMAL; + } + + // PT: no callback => store results in provided buffer + if(mHitNum == mMaxHits) + return false; + + mDstBase[mHitNum++] = hit; + return true; + } + +private: + RayMeshColliderCallback& operator=(const RayMeshColliderCallback&); +}; + +PxU32 physx::Gu::raycast_triangleMesh_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + + const RTreeTriangleMesh* meshData = static_cast(mesh); + + //scaling: transform the ray to vertex space + + PxVec3 orig, dir; + Matrix34 world2vertexSkew; + Matrix34* world2vertexSkewP = NULL; + PxReal distCoeff = 1.0f; + if(meshGeom.scale.isIdentity()) + { + orig = pose.transformInv(rayOrigin); + dir = pose.rotateInv(rayDir); + } + else + { + world2vertexSkew = meshGeom.scale.getInverse() * pose.getInverse(); + world2vertexSkewP = &world2vertexSkew; + orig = world2vertexSkew.transform(rayOrigin); + dir = world2vertexSkew.rotate(rayDir); + { + distCoeff = dir.normalize(); + maxDist *= distCoeff; + maxDist += 1e-3f; + distCoeff = 1.0f / distCoeff; + } + } + + const bool isDoubleSided = meshGeom.meshFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED); + const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES); + + RayMeshColliderCallback callback( + (maxHits > 1) ? CallbackMode::eMULTIPLE : (hitFlags & PxHitFlag::eMESH_ANY ? CallbackMode::eANY : CallbackMode::eCLOSEST), + hits, maxHits, &meshGeom.scale, &pose, world2vertexSkewP, hitFlags, rayDir, isDoubleSided, distCoeff); + + MeshRayCollider::collide<0, 1>(orig, dir, maxDist, bothSides, static_cast(meshData), callback, NULL); + return callback.mHitNum; +} + + +static PX_INLINE void computeSweptAABBAroundOBB( + const Box& obb, PxVec3& sweepOrigin, PxVec3& sweepExtents, PxVec3& sweepDir, PxReal& sweepLen) +{ + PxU32 other1, other2; + // largest axis of the OBB is the sweep direction, sum of abs of two other is the swept AABB extents + PxU32 lai = Ps::largestAxis(obb.extents, other1, other2); + PxVec3 longestAxis = obb.rot[lai]*obb.extents[lai]; + PxVec3 absOther1 = obb.rot[other1].abs()*obb.extents[other1]; + PxVec3 absOther2 = obb.rot[other2].abs()*obb.extents[other2]; + sweepOrigin = obb.center - longestAxis; + sweepExtents = absOther1 + absOther2 + PxVec3(GU_MIN_AABB_EXTENT); // see comments for GU_MIN_AABB_EXTENT + sweepLen = 2.0f; // length is already included in longestAxis + sweepDir = longestAxis; +} + +enum { eSPHERE, eCAPSULE, eBOX }; // values for tSCB + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. + #pragma warning( disable : 4512 ) // assignment operator could not be generated +#endif + +namespace +{ +struct IntersectShapeVsMeshCallback : MeshHitCallback +{ + PX_NOCOPY(IntersectShapeVsMeshCallback) +public: + IntersectShapeVsMeshCallback(const PxMat33& vertexToShapeSkew, LimitedResults* results, bool flipNormal) + : MeshHitCallback(CallbackMode::eMULTIPLE), + mVertexToShapeSkew (vertexToShapeSkew), + mResults (results), + mAnyHits (false), + mFlipNormal (flipNormal) + { + } + virtual ~IntersectShapeVsMeshCallback(){} + + const PxMat33& mVertexToShapeSkew; // vertex to box without translation for boxes + LimitedResults* mResults; + bool mAnyHits; + bool mFlipNormal; + + PX_FORCE_INLINE bool recordHit(const PxRaycastHit& aHit, Ps::IntBool hit) + { + if(hit) + { + mAnyHits = true; + if(mResults) + mResults->add(aHit.faceIndex); + else + return false; // abort traversal if we are only interested in firstContact (mResults is NULL) + } + return true; // if we are here, either no triangles were hit or multiple results are expected => continue traversal + } +}; + +template +struct IntersectSphereVsMeshCallback : IntersectShapeVsMeshCallback +{ + IntersectSphereVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {} + virtual ~IntersectSphereVsMeshCallback(){} + PxF32 mMinDist2; + PxVec3 mLocalCenter; // PT: sphere center in local/mesh space + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*) + { + const Vec3V v0 = V3LoadU(tScaleIsIdentity ? av0 : mVertexToShapeSkew * av0); + const Vec3V v1 = V3LoadU(tScaleIsIdentity ? av1 : mVertexToShapeSkew * (mFlipNormal ? av2 : av1)); + const Vec3V v2 = V3LoadU(tScaleIsIdentity ? av2 : mVertexToShapeSkew * (mFlipNormal ? av1 : av2)); + + FloatV dummy1, dummy2; + Vec3V closestP; + PxReal dist2; + FStore(distancePointTriangleSquared(V3LoadU(mLocalCenter), v0, v1, v2, dummy1, dummy2, closestP), &dist2); + return recordHit(aHit, dist2 <= mMinDist2); + } +}; + +template +struct IntersectCapsuleVsMeshCallback : IntersectShapeVsMeshCallback +{ + IntersectCapsuleVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {} + virtual ~IntersectCapsuleVsMeshCallback(){} + + Capsule mLocalCapsule; // PT: capsule in mesh/local space + CapsuleTriangleOverlapData mParams; + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*) + { + bool hit; + if(tScaleIsIdentity) + { + const PxVec3 normal = (av0 - av1).cross(av0 - av2); + hit = intersectCapsuleTriangle(normal, av0, av1, av2, mLocalCapsule, mParams); + } + else + { + const PxVec3 v0 = mVertexToShapeSkew * av0; + const PxVec3 v1 = mVertexToShapeSkew * (mFlipNormal ? av2 : av1); + const PxVec3 v2 = mVertexToShapeSkew * (mFlipNormal ? av1 : av2); + const PxVec3 normal = (v0 - v1).cross(v0 - v2); + hit = intersectCapsuleTriangle(normal, v0, v1, v2, mLocalCapsule, mParams); + } + return recordHit(aHit, hit); + } +}; + +template +struct IntersectBoxVsMeshCallback : IntersectShapeVsMeshCallback +{ + IntersectBoxVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {} + virtual ~IntersectBoxVsMeshCallback(){} + + Matrix34 mVertexToBox; + Vec3p mBoxExtents, mBoxCenter; + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*) + { + Vec3p v0, v1, v2; + if(tScaleIsIdentity) + { + v0 = mVertexToShapeSkew * av0; // transform from skewed mesh vertex to box space, + v1 = mVertexToShapeSkew * av1; // this includes inverse skew, inverse mesh shape transform and inverse box basis + v2 = mVertexToShapeSkew * av2; + } + else + { + v0 = mVertexToBox.transform(av0); + v1 = mVertexToBox.transform(mFlipNormal ? av2 : av1); + v2 = mVertexToBox.transform(mFlipNormal ? av1 : av2); + } + + // PT: this one is safe because we're using Vec3p for all parameters + const Ps::IntBool hit = intersectTriangleBox_Unsafe(mBoxCenter, mBoxExtents, v0, v1, v2); + return recordHit(aHit, hit); + } +}; +} + +#if PX_VC + #pragma warning(pop) +#endif + +template +static bool intersectAnyVsMeshT( + const Sphere* worldSphere, const Capsule* worldCapsule, const Box* worldOBB, + const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, + LimitedResults* results) +{ + const bool flipNormal = meshScale.hasNegativeDeterminant(); + PxMat33 shapeToVertexSkew, vertexToShapeSkew; + if (!idtMeshScale && tSCB != eBOX) + { + vertexToShapeSkew = meshScale.toMat33(); + shapeToVertexSkew = vertexToShapeSkew.getInverse(); + } + + if (tSCB == eSPHERE) + { + IntersectSphereVsMeshCallback callback(vertexToShapeSkew, results, flipNormal); + // transform sphere center from world to mesh shape space + const PxVec3 center = meshTransform.transformInv(worldSphere->center); + + // callback will transform verts + callback.mLocalCenter = center; + callback.mMinDist2 = worldSphere->radius*worldSphere->radius; + + PxVec3 sweepOrigin, sweepDir, sweepExtents; + PxReal sweepLen; + if (!idtMeshScale) + { + // AP: compute a swept AABB around an OBB around a skewed sphere + // TODO: we could do better than an AABB around OBB actually because we can slice off the corners.. + const Box worldOBB_(worldSphere->center, PxVec3(worldSphere->radius), PxMat33(PxIdentity)); + Box vertexOBB; + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + computeSweptAABBAroundOBB(vertexOBB, sweepOrigin, sweepExtents, sweepDir, sweepLen); + } else + { + sweepOrigin = center; + sweepDir = PxVec3(1.0f,0,0); + sweepLen = 0.0f; + sweepExtents = PxVec3(PxMax(worldSphere->radius, GU_MIN_AABB_EXTENT)); + } + + MeshRayCollider::collide<1, 1>(sweepOrigin, sweepDir, sweepLen, true, static_cast(&triMesh), callback, &sweepExtents); + + return callback.mAnyHits; + } + else if (tSCB == eCAPSULE) + { + IntersectCapsuleVsMeshCallback callback(vertexToShapeSkew, results, flipNormal); + const PxF32 radius = worldCapsule->radius; + + // transform world capsule to mesh shape space + callback.mLocalCapsule.p0 = meshTransform.transformInv(worldCapsule->p0); + callback.mLocalCapsule.p1 = meshTransform.transformInv(worldCapsule->p1); + callback.mLocalCapsule.radius = radius; + callback.mParams.init(callback.mLocalCapsule); + + if (idtMeshScale) + { + // traverse a sweptAABB around the capsule + const PxVec3 radius3(radius); + MeshRayCollider::collide<1, 0>(callback.mLocalCapsule.p0, callback.mLocalCapsule.p1-callback.mLocalCapsule.p0, 1.0f, true, static_cast(&triMesh), callback, &radius3); + } + else + { + // make vertex space OBB + Box vertexOBB; + Box worldOBB_; + worldOBB_.create(*worldCapsule); // AP: potential optimization (meshTransform.inverse is already in callback.mCapsule) + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + + MeshRayCollider::collideOBB(vertexOBB, true, static_cast(&triMesh), callback); + } + return callback.mAnyHits; + } + else if (tSCB == eBOX) + { + Box vertexOBB; // query box in vertex space + if (idtMeshScale) + { + // mesh scale is identity - just inverse transform the box without optimization + vertexOBB = transformBoxOrthonormal(*worldOBB, meshTransform.getInverse()); + // mesh vertices will be transformed from skewed vertex space directly to box AABB space + // box inverse rotation is baked into the vertexToShapeSkew transform + // if meshScale is not identity, vertexOBB already effectively includes meshScale transform + PxVec3 boxCenter; + getInverse(vertexToShapeSkew, boxCenter, vertexOBB.rot, vertexOBB.center); + IntersectBoxVsMeshCallback callback(vertexToShapeSkew, results, flipNormal); + + callback.mBoxCenter = -boxCenter; + callback.mBoxExtents = worldOBB->extents; // extents do not change + + MeshRayCollider::collideOBB(vertexOBB, true, static_cast(&triMesh), callback); + + return callback.mAnyHits; + } else + { + computeVertexSpaceOBB(vertexOBB, *worldOBB, meshTransform, meshScale); + + // mesh scale needs to be included - inverse transform and optimize the box + const PxMat33 vertexToWorldSkew_Rot = PxMat33Padded(meshTransform.q) * meshScale.toMat33(); + const PxVec3& vertexToWorldSkew_Trans = meshTransform.p; + + Matrix34 tmp; + buildMatrixFromBox(tmp, *worldOBB); + const Matrix34 inv = tmp.getInverseRT(); + const Matrix34 _vertexToWorldSkew(vertexToWorldSkew_Rot, vertexToWorldSkew_Trans); + + IntersectBoxVsMeshCallback callback(vertexToShapeSkew, results, flipNormal); + callback.mVertexToBox = inv * _vertexToWorldSkew; + callback.mBoxCenter = PxVec3(0.0f); + callback.mBoxExtents = worldOBB->extents; // extents do not change + + MeshRayCollider::collideOBB(vertexOBB, true, static_cast(&triMesh), callback); + + return callback.mAnyHits; + } + } + else + { + PX_ASSERT(0); + return false; + } +} + +template +static bool intersectAnyVsMesh( + const Sphere* worldSphere, const Capsule* worldCapsule, const Box* worldOBB, + const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, + LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + if (meshScale.isIdentity()) + return intersectAnyVsMeshT(worldSphere, worldCapsule, worldOBB, triMesh, meshTransform, meshScale, results); + else + return intersectAnyVsMeshT(worldSphere, worldCapsule, worldOBB, triMesh, meshTransform, meshScale, results); +} + +bool physx::Gu::intersectSphereVsMesh_RTREE(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + return intersectAnyVsMesh(&sphere, NULL, NULL, triMesh, meshTransform, meshScale, results); +} + +bool physx::Gu::intersectBoxVsMesh_RTREE(const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + return intersectAnyVsMesh(NULL, NULL, &box, triMesh, meshTransform, meshScale, results); +} + +bool physx::Gu::intersectCapsuleVsMesh_RTREE(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + return intersectAnyVsMesh(NULL, &capsule, NULL, triMesh, meshTransform, meshScale, results); +} + +void physx::Gu::intersectOBB_RTREE(const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned) +{ + MeshRayCollider::collideOBB(obb, bothTriangleSidesCollide, static_cast(mesh), callback, checkObbIsAligned); +} + +// PT: TODO: refactor/share bits of this +bool physx::Gu::sweepCapsule_MeshGeom_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + const RTreeTriangleMesh* meshData = static_cast(mesh); + + const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + bool isDoubleSided = (triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED); + const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + + // compute sweptAABB + const PxVec3 localP0 = pose.transformInv(inflatedCapsule.p0); + const PxVec3 localP1 = pose.transformInv(inflatedCapsule.p1); + PxVec3 sweepOrigin = (localP0+localP1)*0.5f; + PxVec3 sweepDir = pose.rotateInv(unitDir); + PxVec3 sweepExtents = PxVec3(inflatedCapsule.radius) + (localP0-localP1).abs()*0.5f; + PxReal distance1 = distance; + PxReal distCoeff = 1.0f; + Matrix34 poseWithScale; + if(!isIdentity) + { + poseWithScale = pose * triMeshGeom.scale; + distance1 = computeSweepData(triMeshGeom, sweepOrigin, sweepExtents, sweepDir, distance); + distCoeff = distance1 / distance; + } else + poseWithScale = Matrix34(pose); + + SweepCapsuleMeshHitCallback callback(sweepHit, poseWithScale, distance, isDoubleSided, inflatedCapsule, unitDir, hitFlags, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff); + + MeshRayCollider::collide<1, 1>(sweepOrigin, sweepDir, distance1, true, meshData, callback, &sweepExtents); + + if(meshBothSides) + isDoubleSided = true; + + return callback.finalizeHit(sweepHit, inflatedCapsule, triMeshGeom, pose, isDoubleSided); +} + +#include "GuSweepSharedTests.h" + +// PT: TODO: refactor/share bits of this +bool physx::Gu::sweepBox_MeshGeom_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + const RTreeTriangleMesh* meshData = static_cast(mesh); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool isDoubleSided = triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED; + + Matrix34 meshToWorldSkew; + PxVec3 sweptAABBMeshSpaceExtents, meshSpaceOrigin, meshSpaceDir; + + // Input sweep params: geom, pose, box, unitDir, distance + // We convert the origin from world space to mesh local space + // and convert the box+pose to mesh space AABB + if(isIdentity) + { + meshToWorldSkew = Matrix34(pose); + PxMat33 worldToMeshRot(pose.q.getConjugate()); // extract rotation matrix from pose.q + meshSpaceOrigin = worldToMeshRot.transform(box.center - pose.p); + meshSpaceDir = worldToMeshRot.transform(unitDir) * distance; + PxMat33 boxToMeshRot = worldToMeshRot * box.rot; + sweptAABBMeshSpaceExtents = boxToMeshRot.column0.abs() * box.extents.x + + boxToMeshRot.column1.abs() * box.extents.y + + boxToMeshRot.column2.abs() * box.extents.z; + } + else + { + meshToWorldSkew = pose * triMeshGeom.scale; + const PxMat33 meshToWorldSkew_Rot = PxMat33Padded(pose.q) * triMeshGeom.scale.toMat33(); + const PxVec3& meshToWorldSkew_Trans = pose.p; + + PxMat33 worldToVertexSkew_Rot; + PxVec3 worldToVertexSkew_Trans; + getInverse(worldToVertexSkew_Rot, worldToVertexSkew_Trans, meshToWorldSkew_Rot, meshToWorldSkew_Trans); + + //make vertex space OBB + Box vertexSpaceBox1; + const Matrix34 worldToVertexSkew(worldToVertexSkew_Rot, worldToVertexSkew_Trans); + vertexSpaceBox1 = transform(worldToVertexSkew, box); + // compute swept aabb + sweptAABBMeshSpaceExtents = vertexSpaceBox1.computeAABBExtent(); + + meshSpaceOrigin = worldToVertexSkew.transform(box.center); + meshSpaceDir = worldToVertexSkew.rotate(unitDir*distance); // also applies scale to direction/length + } + + sweptAABBMeshSpaceExtents += PxVec3(inflation); // inflate the bounds with additive inflation + sweptAABBMeshSpaceExtents *= 1.01f; // fatten the bounds to account for numerical discrepancies + + PxReal dirLen = PxMax(meshSpaceDir.magnitude(), 1e-5f); + PxReal distCoeff = 1.0f; + if (!isIdentity) + distCoeff = dirLen / distance; + + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + + const Matrix34Padded meshToBox = worldToBox*meshToWorldSkew; + const PxTransform boxTransform = box.getTransform(); + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localDirDist = localDir*distance; + SweepBoxMeshHitCallback callback( // using eMULTIPLE with shrinkMaxT + CallbackMode::eMULTIPLE, meshToBox, distance, bothTriangleSidesCollide, box, localDirDist, localDir, unitDir, hitFlags, inflation, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff); + + MeshRayCollider::collide<1, 1>(meshSpaceOrigin, meshSpaceDir/dirLen, dirLen, bothTriangleSidesCollide, meshData, callback, &sweptAABBMeshSpaceExtents); + + return callback.finalizeHit(sweepHit, triMeshGeom, pose, boxTransform, localDir, meshBothSides, isDoubleSided); +} + +#include "GuInternal.h" +void physx::Gu::sweepConvex_MeshGeom_RTREE(const TriangleMesh* mesh, const Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + const RTreeTriangleMesh* meshData = static_cast(mesh); + + // create temporal bounds + Box querySweptBox; + computeSweptBox(querySweptBox, hullBox.extents, hullBox.center, hullBox.rot, localDir, distance); + + MeshRayCollider::collideOBB(querySweptBox, true, meshData, callback); +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp new file mode 100644 index 000000000..bc5ced894 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp @@ -0,0 +1,241 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuMidphaseInterface.h" +#include "CmScaling.h" +#include "GuSphere.h" +#include "GuInternal.h" +#include "GuConvexUtilsInternal.h" +#include "GuVecTriangle.h" +#include "GuVecConvexHull.h" +#include "GuConvexMesh.h" +#include "GuGJK.h" +#include "GuSweepSharedTests.h" + +using namespace physx; +using namespace Cm; +using namespace Gu; +using namespace physx::shdfnd::aos; + +// PT: TODO: remove this function, replace with Midphase:: call at calling sites (TA34704) +bool Gu::checkOverlapAABB_triangleGeom(const PxGeometry& geom, const PxTransform& pose, const PxBounds3& box) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + + // PT: TODO: pass AABB directly to interface + const Box obb(box.getCenter(), box.getExtents(), PxMat33(PxIdentity)); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + return Midphase::intersectBoxVsMesh(obb, *meshData, pose, meshGeom.scale, NULL); +} + +bool GeomOverlapCallback_SphereMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + const Sphere worldSphere(pose0.p, sphereGeom.radius); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + return Midphase::intersectSphereVsMesh(worldSphere, *meshData, pose1, meshGeom.scale, NULL); +} + +bool GeomOverlapCallback_CapsuleMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + Capsule capsule; + getCapsule(capsule, capsuleGeom, pose0); + return Midphase::intersectCapsuleVsMesh(capsule, *meshData, pose1, meshGeom.scale, NULL); +} + +bool GeomOverlapCallback_BoxMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + Box box; + buildFrom(box, pose0.p, boxGeom.halfExtents, pose0.q); + return Midphase::intersectBoxVsMesh(box, *meshData, pose1, meshGeom.scale, NULL); +} + +/////////////////////////////////////////////////////////////////////////////// +struct ConvexVsMeshOverlapCallback : MeshHitCallback +{ + PsMatTransformV MeshToBoxV; + Vec3V boxExtents; + + ConvexVsMeshOverlapCallback( + const ConvexMesh& cm, const PxMeshScale& convexScale, const FastVertex2ShapeScaling& meshScale, + const PxTransform& tr0, const PxTransform& tr1, bool identityScale, const Box& meshSpaceOBB) + : + MeshHitCallback(CallbackMode::eMULTIPLE), + mAnyHit (false), + mIdentityScale (identityScale) + { + if (!mIdentityScale) // not done in initializer list for performance + mMeshScale = Ps::aos::Mat33V( + V3LoadU(meshScale.getVertex2ShapeSkew().column0), + V3LoadU(meshScale.getVertex2ShapeSkew().column1), + V3LoadU(meshScale.getVertex2ShapeSkew().column2) ); + using namespace Ps::aos; + + const ConvexHullData* hullData = &cm.getHull(); + + const Vec3V vScale0 = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat0 = QuatVLoadU(&convexScale.rotation.x); + + mConvex = ConvexHullV(hullData, V3Zero(), vScale0, vQuat0, convexScale.isIdentity()); + aToB = PsMatTransformV(tr0.transformInv(tr1)); + + mIdentityScale = identityScale; + + { + // Move to AABB space + Matrix34 MeshToBox; + computeWorldToBoxMatrix(MeshToBox, meshSpaceOBB); + + const Vec3V base0 = V3LoadU(MeshToBox.m.column0); + const Vec3V base1 = V3LoadU(MeshToBox.m.column1); + const Vec3V base2 = V3LoadU(MeshToBox.m.column2); + const Mat33V matV(base0, base1, base2); + const Vec3V p = V3LoadU(MeshToBox.p); + MeshToBoxV = PsMatTransformV(p, matV); + boxExtents = V3LoadU(meshSpaceOBB.extents+PxVec3(0.001f)); + } + } + virtual ~ConvexVsMeshOverlapCallback() {} + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit&, const PxVec3& v0a, const PxVec3& v1a, const PxVec3& v2a, PxReal&, const PxU32*) + { + using namespace Ps::aos; + Vec3V v0 = V3LoadU(v0a), v1 = V3LoadU(v1a), v2 = V3LoadU(v2a); + + // test triangle AABB in box space vs box AABB in box local space + const Vec3V triV0 = MeshToBoxV.transform(v0); // AP: MeshToBoxV already includes mesh scale so we have to use unscaled verts here + const Vec3V triV1 = MeshToBoxV.transform(v1); + const Vec3V triV2 = MeshToBoxV.transform(v2); + Vec3V triMn = V3Min(V3Min(triV0, triV1), triV2); + Vec3V triMx = V3Max(V3Max(triV0, triV1), triV2); + Vec3V negExtents = V3Neg(boxExtents); + BoolV minSeparated = V3IsGrtr(triMn, boxExtents), maxSeparated = V3IsGrtr(negExtents, triMx); + BoolV bSeparated = BAnyTrue3(BOr(minSeparated, maxSeparated)); + if (BAllEqTTTT(bSeparated)) + return true; // continue traversal + + if (!mIdentityScale) + { + v0 = M33MulV3(mMeshScale, v0); + v1 = M33MulV3(mMeshScale, v1); + v2 = M33MulV3(mMeshScale, v2); + } + + TriangleV triangle(v0, v1, v2); + Vec3V contactA, contactB, normal; + FloatV dist; + GjkStatus status; + RelativeConvex convexA(triangle, aToB); + LocalConvex convexB(mConvex); + status = gjk(convexA, convexB, aToB.p, FZero(), contactA, contactB, normal, dist); + if (status == GJK_CONTACT)// || FAllGrtrOrEq(mSqTolerance, sqDist)) + { + mAnyHit = true; + return false; // abort traversal + } + return true; // continue traversal + } + + ConvexHullV mConvex; + PsMatTransformV aToB; + Ps::aos::Mat33V mMeshScale; + bool mAnyHit; + bool mIdentityScale; + +private: + ConvexVsMeshOverlapCallback& operator=(const ConvexVsMeshOverlapCallback&); +}; + +// PT: TODO: refactor bits of this with convex-vs-mesh code +bool GeomOverlapCallback_ConvexMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + const bool idtScaleMesh = meshGeom.scale.isIdentity(); + + FastVertex2ShapeScaling convexScaling; + if (!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + FastVertex2ShapeScaling meshScaling; + if (!idtScaleMesh) + meshScaling.init(meshGeom.scale); + + const Matrix34 world0(pose0); + const Matrix34 world1(pose1); + + PX_ASSERT(!cm->getLocalBoundsFast().isEmpty()); + const PxBounds3 hullAABB = cm->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew()); + + Box hullOBB; + computeHullOBB(hullOBB, hullAABB, 0.0f, world0, world1, meshScaling, idtScaleMesh); + + ConvexVsMeshOverlapCallback cb(*cm, convexGeom.scale, meshScaling, pose0, pose1, idtScaleMesh, hullOBB); + Midphase::intersectOBB(meshData, hullOBB, cb, true, false); + + return cb.mAnyHit; +} + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp new file mode 100644 index 000000000..7034f90c3 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp @@ -0,0 +1,430 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" + +#define RTREE_TEXT_DUMP_ENABLE 0 +#if PX_P64_FAMILY +#define RTREE_PAGES_PER_POOL_SLAB 16384 // preallocate all pages in first batch to make sure we stay within 32 bits for relative pointers.. this is 2 megs +#else +#define RTREE_PAGES_PER_POOL_SLAB 128 +#endif + +#define INSERT_SCAN_LOOKAHEAD 1 // enable one level lookahead scan for determining which child page is best to insert a node into + +#define RTREE_INFLATION_EPSILON 5e-4f + +#include "GuRTree.h" +#include "PsSort.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +using namespace physx; +#if PX_ENABLE_DYNAMIC_MESH_RTREE +using namespace shdfnd::aos; +#endif +using Ps::Array; +using Ps::sort; +using namespace Gu; + +namespace physx +{ +namespace Gu { + +bool RTree::load(PxInputStream& stream, PxU32 meshVersion, bool mismatch_) // PT: 'meshVersion' is the PX_MESH_VERSION from cooked file +{ + PX_UNUSED(meshVersion); + + release(); + + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if(a!='R' || b!='T' || c!='R' || d!='E') + return false; + + bool mismatch; + PxU32 fileVersion; + if(!readBigEndianVersionNumber(stream, mismatch_, fileVersion, mismatch)) + return false; + + readFloatBuffer(&mBoundsMin.x, 4, mismatch, stream); + readFloatBuffer(&mBoundsMax.x, 4, mismatch, stream); + readFloatBuffer(&mInvDiagonal.x, 4, mismatch, stream); + readFloatBuffer(&mDiagonalScaler.x, 4, mismatch, stream); + mPageSize = readDword(mismatch, stream); + mNumRootPages = readDword(mismatch, stream); + mNumLevels = readDword(mismatch, stream); + mTotalNodes = readDword(mismatch, stream); + mTotalPages = readDword(mismatch, stream); + PxU32 unused = readDword(mismatch, stream); PX_UNUSED(unused); // backwards compatibility + mPages = static_cast(Ps::AlignedAllocator<128>().allocate(sizeof(RTreePage)*mTotalPages, __FILE__, __LINE__)); + Cm::markSerializedMem(mPages, sizeof(RTreePage)*mTotalPages); + for(PxU32 j=0; jnodeCount(); + PX_ASSERT(nc > 0 && nc <= RTREE_N); + // old version pointer, up to PX_MESH_VERSION 8 + PxU32 ptr = (rightMostPage->ptrs[nc-1]) * multiplier; + PX_ASSERT(ptr % sizeof(RTreePage) == 0); + const RTreePage* rightMostPageNext = mPages + (ptr / sizeof(RTreePage)); + curCount = PxU32(rightMostPageNext - rightMostPage); + rightMostPage = rightMostPageNext; + } + + return mTotalPages - topCount; +} + +///////////////////////////////////////////////////////////////////////// +RTree::RTree(const PxEMPTY) +{ + mFlags |= USER_ALLOCATED; +} + + +// PX_SERIALIZATION +///////////////////////////////////////////////////////////////////////// +void RTree::exportExtraData(PxSerializationContext& stream) +{ + stream.alignData(128); + stream.writeData(mPages, mTotalPages*sizeof(RTreePage)); +} + +///////////////////////////////////////////////////////////////////////// +void RTree::importExtraData(PxDeserializationContext& context) +{ + context.alignExtraData(128); + mPages = context.readExtraData(mTotalPages); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE PxU32 RTreePage::nodeCount() const +{ + for (int j = 0; j < RTREE_N; j ++) + if (minx[j] == MX) + return PxU32(j); + + return RTREE_N; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::clearNode(PxU32 nodeIndex) +{ + PX_ASSERT(nodeIndex < RTREE_N); + minx[nodeIndex] = miny[nodeIndex] = minz[nodeIndex] = MX; // initialize empty node with sentinels + maxx[nodeIndex] = maxy[nodeIndex] = maxz[nodeIndex] = MN; + ptrs[nodeIndex] = 0; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::getNode(const PxU32 nodeIndex, RTreeNodeQ& r) const +{ + PX_ASSERT(nodeIndex < RTREE_N); + r.minx = minx[nodeIndex]; + r.miny = miny[nodeIndex]; + r.minz = minz[nodeIndex]; + r.maxx = maxx[nodeIndex]; + r.maxy = maxy[nodeIndex]; + r.maxz = maxz[nodeIndex]; + r.ptr = ptrs[nodeIndex]; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::setEmpty(PxU32 startIndex) +{ + PX_ASSERT(startIndex < RTREE_N); + for (PxU32 j = startIndex; j < RTREE_N; j ++) + clearNode(j); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::computeBounds(RTreeNodeQ& newBounds) +{ + RTreeValue _minx = MX, _miny = MX, _minz = MX, _maxx = MN, _maxy = MN, _maxz = MN; + for (PxU32 j = 0; j < RTREE_N; j++) + { + if (isEmpty(j)) + continue; + _minx = PxMin(_minx, minx[j]); + _miny = PxMin(_miny, miny[j]); + _minz = PxMin(_minz, minz[j]); + _maxx = PxMax(_maxx, maxx[j]); + _maxy = PxMax(_maxy, maxy[j]); + _maxz = PxMax(_maxz, maxz[j]); + } + newBounds.minx = _minx; + newBounds.miny = _miny; + newBounds.minz = _minz; + newBounds.maxx = _maxx; + newBounds.maxy = _maxy; + newBounds.maxz = _maxz; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::adjustChildBounds(PxU32 index, const RTreeNodeQ& adjChild) +{ + PX_ASSERT(index < RTREE_N); + minx[index] = adjChild.minx; + miny[index] = adjChild.miny; + minz[index] = adjChild.minz; + maxx[index] = adjChild.maxx; + maxy[index] = adjChild.maxy; + maxz[index] = adjChild.maxz; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::growChildBounds(PxU32 index, const RTreeNodeQ& child) +{ + PX_ASSERT(index < RTREE_N); + minx[index] = PxMin(minx[index], child.minx); + miny[index] = PxMin(miny[index], child.miny); + minz[index] = PxMin(minz[index], child.minz); + maxx[index] = PxMax(maxx[index], child.maxx); + maxy[index] = PxMax(maxy[index], child.maxy); + maxz[index] = PxMax(maxz[index], child.maxz); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::copyNode(PxU32 targetIndex, const RTreePage& sourcePage, PxU32 sourceIndex) +{ + PX_ASSERT(targetIndex < RTREE_N); + PX_ASSERT(sourceIndex < RTREE_N); + minx[targetIndex] = sourcePage.minx[sourceIndex]; + miny[targetIndex] = sourcePage.miny[sourceIndex]; + minz[targetIndex] = sourcePage.minz[sourceIndex]; + maxx[targetIndex] = sourcePage.maxx[sourceIndex]; + maxy[targetIndex] = sourcePage.maxy[sourceIndex]; + maxz[targetIndex] = sourcePage.maxz[sourceIndex]; + ptrs[targetIndex] = sourcePage.ptrs[sourceIndex]; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::setNode(PxU32 targetIndex, const RTreeNodeQ& sourceNode) +{ + PX_ASSERT(targetIndex < RTREE_N); + minx[targetIndex] = sourceNode.minx; + miny[targetIndex] = sourceNode.miny; + minz[targetIndex] = sourceNode.minz; + maxx[targetIndex] = sourceNode.maxx; + maxy[targetIndex] = sourceNode.maxy; + maxz[targetIndex] = sourceNode.maxz; + ptrs[targetIndex] = sourceNode.ptr; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreeNodeQ::grow(const RTreePage& page, int nodeIndex) +{ + PX_ASSERT(nodeIndex < RTREE_N); + minx = PxMin(minx, page.minx[nodeIndex]); + miny = PxMin(miny, page.miny[nodeIndex]); + minz = PxMin(minz, page.minz[nodeIndex]); + maxx = PxMax(maxx, page.maxx[nodeIndex]); + maxy = PxMax(maxy, page.maxy[nodeIndex]); + maxz = PxMax(maxz, page.maxz[nodeIndex]); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreeNodeQ::grow(const RTreeNodeQ& node) +{ + minx = PxMin(minx, node.minx); miny = PxMin(miny, node.miny); minz = PxMin(minz, node.minz); + maxx = PxMax(maxx, node.maxx); maxy = PxMax(maxy, node.maxy); maxz = PxMax(maxz, node.maxz); +} + +///////////////////////////////////////////////////////////////////////// +#if PX_ENABLE_DYNAMIC_MESH_RTREE +void RTree::validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page, CallbackRefit* cbLeaf) +#else +void RTree::validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page) +#endif +{ + PX_UNUSED(parentBounds); + + static PxU32 validateCounter = 0; // this is to suppress a warning that recursive call has no side effects + validateCounter++; + + RTreeNodeQ n; + PxU32 pageNodeCount = page->nodeCount(); + for (PxU32 j = 0; j < pageNodeCount; j++) + { + page->getNode(j, n); + if (page->isEmpty(j)) + continue; + PX_ASSERT(n.minx >= parentBounds.minx); PX_ASSERT(n.miny >= parentBounds.miny); PX_ASSERT(n.minz >= parentBounds.minz); + PX_ASSERT(n.maxx <= parentBounds.maxx); PX_ASSERT(n.maxy <= parentBounds.maxy); PX_ASSERT(n.maxz <= parentBounds.maxz); + if (!n.isLeaf()) + { + PX_ASSERT((n.ptr&1) == 0); + RTreePage* childPage = reinterpret_cast(size_t(mPages) + n.ptr); +#if PX_ENABLE_DYNAMIC_MESH_RTREE + validateRecursive(level+1, n, childPage, cbLeaf); + } else if (cbLeaf) + { + Vec3V mnv, mxv; + cbLeaf->recomputeBounds(page->ptrs[j] & ~1, mnv, mxv); + PxVec3 mn3, mx3; V3StoreU(mnv, mn3); V3StoreU(mxv, mx3); + const PxBounds3 lb(mn3, mx3); + const PxVec3& mn = lb.minimum; const PxVec3& mx = lb.maximum; PX_UNUSED(mn); PX_UNUSED(mx); + PX_ASSERT(mn.x >= n.minx); PX_ASSERT(mn.y >= n.miny); PX_ASSERT(mn.z >= n.minz); + PX_ASSERT(mx.x <= n.maxx); PX_ASSERT(mx.y <= n.maxy); PX_ASSERT(mx.z <= n.maxz); + } +#else + validateRecursive(level+1, n, childPage); + } +#endif + } + RTreeNodeQ recomputedBounds; + page->computeBounds(recomputedBounds); + PX_ASSERT((recomputedBounds.minx - parentBounds.minx)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.miny - parentBounds.miny)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.minz - parentBounds.minz)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.maxx - parentBounds.maxx)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.maxy - parentBounds.maxy)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.maxz - parentBounds.maxz)<=RTREE_INFLATION_EPSILON); +} + +///////////////////////////////////////////////////////////////////////// +#if PX_ENABLE_DYNAMIC_MESH_RTREE +void RTree::validate(CallbackRefit* cbLeaf) +#else +void RTree::validate() +#endif +{ + for (PxU32 j = 0; j < mNumRootPages; j++) + { + RTreeNodeQ rootBounds; + mPages[j].computeBounds(rootBounds); +#if PX_ENABLE_DYNAMIC_MESH_RTREE + validateRecursive(0, rootBounds, mPages+j, cbLeaf); +#else + validateRecursive(0, rootBounds, mPages+j); +#endif + } +} + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +void RTree::refitAllStaticTree(CallbackRefit& cb, PxBounds3* retBounds) +{ + PxU8* treeNodes8 = reinterpret_cast(mPages); + + // since pages are ordered we can scan back to front and the hierarchy will be updated + for (PxI32 iPage = PxI32(mTotalPages)-1; iPage>=0; iPage--) + { + RTreePage& page = mPages[iPage]; + for (PxU32 j = 0; j < RTREE_N; j++) + { + if (page.isEmpty(j)) + continue; + if (page.isLeaf(j)) + { + Vec3V childMn, childMx; + cb.recomputeBounds(page.ptrs[j]-1, childMn, childMx); // compute the bound around triangles + PxVec3 mn3, mx3; + V3StoreU(childMn, mn3); + V3StoreU(childMx, mx3); + page.minx[j] = mn3.x; page.miny[j] = mn3.y; page.minz[j] = mn3.z; + page.maxx[j] = mx3.x; page.maxy[j] = mx3.y; page.maxz[j] = mx3.z; + } else + { + const RTreePage* child = reinterpret_cast(treeNodes8 + page.ptrs[j]); + PX_COMPILE_TIME_ASSERT(RTREE_N == 4); + bool first = true; + for (PxU32 k = 0; k < RTREE_N; k++) + { + if (child->isEmpty(k)) + continue; + if (first) + { + page.minx[j] = child->minx[k]; page.miny[j] = child->miny[k]; page.minz[j] = child->minz[k]; + page.maxx[j] = child->maxx[k]; page.maxy[j] = child->maxy[k]; page.maxz[j] = child->maxz[k]; + first = false; + } else + { + page.minx[j] = PxMin(page.minx[j], child->minx[k]); + page.miny[j] = PxMin(page.miny[j], child->miny[k]); + page.minz[j] = PxMin(page.minz[j], child->minz[k]); + page.maxx[j] = PxMax(page.maxx[j], child->maxx[k]); + page.maxy[j] = PxMax(page.maxy[j], child->maxy[k]); + page.maxz[j] = PxMax(page.maxz[j], child->maxz[k]); + } + } + } + } + } + + if (retBounds) + { + RTreeNodeQ bound1; + for (PxU32 ii = 0; iiminimum = PxVec3(bound1.minx, bound1.miny, bound1.minz); + retBounds->maximum = PxVec3(bound1.maxx, bound1.maxy, bound1.maxz); + } else + { + retBounds->minimum = retBounds->minimum.minimum(PxVec3(bound1.minx, bound1.miny, bound1.minz)); + retBounds->maximum = retBounds->maximum.maximum(PxVec3(bound1.maxx, bound1.maxy, bound1.maxz)); + } + } + } + +#if PX_CHECKED + validate(&cb); +#endif +} +#endif // PX_ENABLE_DYNAMIC_MESH_RTREE + +//~PX_SERIALIZATION +const RTreeValue RTreePage::MN = -PX_MAX_F32; +const RTreeValue RTreePage::MX = PX_MAX_F32; + +} // namespace Gu + +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h new file mode 100644 index 000000000..4cd3ad9c5 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h @@ -0,0 +1,297 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_RTREE_H +#define GU_RTREE_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec4.h" +#include "foundation/PxBounds3.h" +#include "foundation/PxAssert.h" +#include "PsUserAllocated.h" // for PxSerializationContext +#include "PxSerialFramework.h" +#include "PxTriangleMesh.h" +#include "PsAlignedMalloc.h" + + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +#include "PsVecMath.h" +#endif + +#define RTREE_N 4 // changing this number will affect the mesh format +PX_COMPILE_TIME_ASSERT(RTREE_N == 4 || RTREE_N == 8); // using the low 5 bits for storage of index(childPtr) for dynamic rtree + +namespace physx +{ + + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +namespace Gu { + + class Box; + struct RTreePage; + + typedef PxF32 RTreeValue; + + ///////////////////////////////////////////////////////////////////////// + // quantized untransposed RTree node - used for offline build and dynamic insertion + struct RTreeNodeQ + { + RTreeValue minx, miny, minz, maxx, maxy, maxz; + PxU32 ptr; // lowest bit is leaf flag + + PX_FORCE_INLINE void setLeaf(bool set) { if (set) ptr |= 1; else ptr &= ~1; } + PX_FORCE_INLINE PxU32 isLeaf() const { return ptr & 1; } + PX_FORCE_INLINE void setEmpty(); + PX_FORCE_INLINE void grow(const RTreePage& page, int nodeIndex); + PX_FORCE_INLINE void grow(const RTreeNodeQ& node); + }; + + ///////////////////////////////////////////////////////////////////////// + // RTreePage data structure, holds RTREE_N transposed nodes + + // RTreePage data structure, holds 8 transposed nodes + PX_ALIGN_PREFIX(16) + struct RTreePage + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + static const RTreeValue MN, MX; + + RTreeValue minx[RTREE_N]; // [min=MX, max=MN] is used as a sentinel range for empty bounds + RTreeValue miny[RTREE_N]; + RTreeValue minz[RTREE_N]; + RTreeValue maxx[RTREE_N]; + RTreeValue maxy[RTREE_N]; + RTreeValue maxz[RTREE_N]; + PxU32 ptrs[RTREE_N]; // for static rtree this is an offset relative to the first page divided by 16, for dynamics it's an absolute pointer divided by 16 + + PX_FORCE_INLINE PxU32 nodeCount() const; // returns the number of occupied nodes in this page + PX_FORCE_INLINE void setEmpty(PxU32 startIndex = 0); + PX_FORCE_INLINE bool isEmpty(PxU32 index) const { return minx[index] > maxx[index]; } + PX_FORCE_INLINE void copyNode(PxU32 targetIndex, const RTreePage& sourcePage, PxU32 sourceIndex); + PX_FORCE_INLINE void setNode(PxU32 targetIndex, const RTreeNodeQ& node); + PX_FORCE_INLINE void clearNode(PxU32 nodeIndex); + PX_FORCE_INLINE void getNode(PxU32 nodeIndex, RTreeNodeQ& result) const; + PX_FORCE_INLINE void computeBounds(RTreeNodeQ& bounds); + PX_FORCE_INLINE void adjustChildBounds(PxU32 index, const RTreeNodeQ& adjustedChildBounds); + PX_FORCE_INLINE void growChildBounds(PxU32 index, const RTreeNodeQ& adjustedChildBounds); + PX_FORCE_INLINE PxU32 getNodeHandle(PxU32 index) const; + PX_FORCE_INLINE PxU32 isLeaf(PxU32 index) const { return ptrs[index] & 1; } + } PX_ALIGN_SUFFIX(16); + + ///////////////////////////////////////////////////////////////////////// + // RTree root data structure + PX_ALIGN_PREFIX(16) + struct RTree + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + // PX_SERIALIZATION + RTree(const PxEMPTY); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + PX_INLINE RTree(); // offline static rtree constructor used with cooking + + ~RTree() { release(); } + + PX_INLINE void release(); + bool load(PxInputStream& stream, PxU32 meshVersion, bool mismatch); + + //////////////////////////////////////////////////////////////////////////// + // QUERIES + struct Callback + { + // result buffer should have room for at least RTREE_N items + // should return true to continue traversal. If false is returned, traversal is aborted + virtual bool processResults(PxU32 count, PxU32* buf) = 0; + virtual void profile() {} + virtual ~Callback() {} + }; + + struct CallbackRaycast + { + // result buffer should have room for at least RTREE_N items + // should return true to continue traversal. If false is returned, traversal is aborted + // newMaxT serves as both input and output, as input it's the maxT so far + // set it to a new value (which should be smaller) and it will become the new far clip t + virtual bool processResults(PxU32 count, PxU32* buf, PxF32& newMaxT) = 0; + virtual ~CallbackRaycast() {} + }; + + // callback will be issued as soon as the buffer overflows maxResultsPerBlock-RTreePage:SIZE entries + // use maxResults = RTreePage:SIZE and return false from callback for "first hit" early out + void traverseAABB( + const PxVec3& boxMin, const PxVec3& boxMax, + const PxU32 maxResultsPerBlock, PxU32* resultsBlockBuf, Callback* processResultsBlockCallback) const; + void traverseOBB( + const Gu::Box& obb, + const PxU32 maxResultsPerBlock, PxU32* resultsBlockBuf, Callback* processResultsBlockCallback) const; + + template + void traverseRay( + const PxVec3& rayOrigin, const PxVec3& rayDir, // dir doesn't have to be normalized and is B-A for raySegment + const PxU32 maxResults, PxU32* resultsPtr, + Gu::RTree::CallbackRaycast* callback, + const PxVec3* inflateAABBs, // inflate tree's AABBs by this amount. This function turns into AABB sweep. + PxF32 maxT = PX_MAX_F32 // maximum ray t parameter, p(t)=origin+t*dir; use 1.0f for ray segment + ) const; + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + struct CallbackRefit + { + // In this callback index is the number stored in the RTree, which is a LeafTriangles object for current PhysX mesh + virtual void recomputeBounds(PxU32 index, shdfnd::aos::Vec3V& mn, shdfnd::aos::Vec3V& mx) = 0; + virtual ~CallbackRefit() {} + }; + void refitAllStaticTree(CallbackRefit& cb, PxBounds3* resultMeshBounds); // faster version of refit for static RTree only +#endif + + + //////////////////////////////////////////////////////////////////////////// + // DEBUG HELPER FUNCTIONS +#if PX_ENABLE_DYNAMIC_MESH_RTREE + PX_PHYSX_COMMON_API void validate(CallbackRefit* cb = NULL); // verify that all children are indeed included in parent bounds +#else + PX_PHYSX_COMMON_API void validate(); // verify that all children are indeed included in parent bounds +#endif + void openTextDump(); + void closeTextDump(); + void textDump(const char* prefix); + void maxscriptExport(); + PxU32 computeBottomLevelCount(PxU32 storedToMemMultiplier) const; + + //////////////////////////////////////////////////////////////////////////// + // DATA + // remember to update save() and load() when adding or removing data + PxVec4 mBoundsMin, mBoundsMax, mInvDiagonal, mDiagonalScaler; // 16 + PxU32 mPageSize; + PxU32 mNumRootPages; + PxU32 mNumLevels; + PxU32 mTotalNodes; // 16 + PxU32 mTotalPages; + PxU32 mFlags; enum { USER_ALLOCATED = 0x1, IS_EDGE_SET = 0x2 }; + RTreePage* mPages; + + protected: + typedef PxU32 NodeHandle; +#if PX_ENABLE_DYNAMIC_MESH_RTREE + void validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page, CallbackRefit* cb = NULL); +#else + void validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page); +#endif + + friend struct RTreePage; + } PX_ALIGN_SUFFIX(16); + +#if PX_SUPPORT_EXTERN_TEMPLATE + //explicit template instantiation declaration + extern template + void RTree::traverseRay<0>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const; + + extern template + void RTree::traverseRay<1>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const; +#endif + +#if PX_VC +#pragma warning(pop) +#endif + + ///////////////////////////////////////////////////////////////////////// + PX_INLINE RTree::RTree() + { + mFlags = 0; + mPages = NULL; + mTotalNodes = 0; + mNumLevels = 0; + mPageSize = RTREE_N; + } + + ///////////////////////////////////////////////////////////////////////// + PX_INLINE void RTree::release() + { + if ((mFlags & USER_ALLOCATED) == 0 && mPages) + { + physx::shdfnd::AlignedAllocator<128>().deallocate(mPages); + mPages = NULL; + } + } + + ///////////////////////////////////////////////////////////////////////// + PX_FORCE_INLINE void RTreeNodeQ::setEmpty() + { + minx = miny = minz = RTreePage::MX; + maxx = maxy = maxz = RTreePage::MN; + } + + + // bit 1 is always expected to be set to differentiate between leaf and non-leaf node + PX_FORCE_INLINE PxU32 LeafGetNbTriangles(PxU32 Data) { return ((Data>>1) & 15)+1; } + PX_FORCE_INLINE PxU32 LeafGetTriangleIndex(PxU32 Data) { return Data>>5; } + PX_FORCE_INLINE PxU32 LeafSetData(PxU32 nb, PxU32 index) + { + PX_ASSERT(nb>0 && nb<=16); PX_ASSERT(index < (1<<27)); + return (index<<5)|(((nb-1)&15)<<1) | 1; + } + + struct LeafTriangles + { + PxU32 Data; + + // Gets number of triangles in the leaf, returns the number of triangles N, with 0 < N <= 16 + PX_FORCE_INLINE PxU32 GetNbTriangles() const { return LeafGetNbTriangles(Data); } + + // Gets triangle index for this leaf. Indexed model's array of indices retrieved with RTreeMidphase::GetIndices() + PX_FORCE_INLINE PxU32 GetTriangleIndex() const { return LeafGetTriangleIndex(Data); } + PX_FORCE_INLINE void SetData(PxU32 nb, PxU32 index) { Data = LeafSetData(nb, index); } + }; + + PX_COMPILE_TIME_ASSERT(sizeof(LeafTriangles)==4); // RTree has space for 4 bytes + +} // namespace Gu + +} + +#endif // #ifdef PX_COLLISION_RTREE diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp new file mode 100644 index 000000000..4030ba0d7 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp @@ -0,0 +1,573 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/* +General notes: + + rtree depth-first traversal looks like this: + push top level page onto stack + + pop page from stack + for each node in page + if node overlaps with testrect + push node's subpage + + we want to efficiently keep track of current stack level to know if the current page is a leaf or not + (since we don't store a flag with the page due to no space, we can't determine it just by looking at current page) + since we traverse depth first, the levels for nodes on the stack look like this: + l0 l0 l1 l2 l2 l3 l3 l3 l4 + + we can encode this as an array of 4 bits per level count into a 32-bit integer + to simplify the code->level computation we also keep track of current level by incrementing the level whenever any subpages + from current test page are pushed onto the stack + when we pop a page off the stack we use this encoding to determine if we should decrement the stack level +*/ + +#include "foundation/PxBounds3.h" +#include "GuRTree.h" +#include "PsIntrinsics.h" +#include "GuBox.h" +#include "PsVecMath.h" +#include "PxQueryReport.h" // for PxAgain +#include "PsBitUtils.h" +#include "GuBVConstants.h" + +//#define VERIFY_RTREE +#ifdef VERIFY_RTREE +#include "GuIntersectionRayBox.h" +#include "GuIntersectionBoxBox.h" +#include "stdio.h" +#endif + +using namespace physx; +using namespace physx::shdfnd; +using namespace Ps::aos; + +namespace physx +{ +namespace Gu { + +using namespace Ps::aos; + +#define v_absm(a) V4Andc(a, signMask) +#define V4FromF32A(x) V4LoadA(x) +#define PxF32FV(x) FStore(x) +#define CAST_U8(a) reinterpret_cast(a) + +///////////////////////////////////////////////////////////////////////// +void RTree::traverseAABB(const PxVec3& boxMin, const PxVec3& boxMax, const PxU32 maxResults, PxU32* resultsPtr, Callback* callback) const +{ + PX_UNUSED(resultsPtr); + + PX_ASSERT(callback); + PX_ASSERT(maxResults >= mPageSize); + PX_UNUSED(maxResults); + + const PxU32 maxStack = 128; + PxU32 stack1[maxStack]; + PxU32* stack = stack1+1; + + PX_ASSERT(mPages); + PX_ASSERT((uintptr_t(mPages) & 127) == 0); + PX_ASSERT((uintptr_t(this) & 15) == 0); + + // conservatively quantize the input box + Vec4V nqMin = Vec4V_From_PxVec3_WUndefined(boxMin); + Vec4V nqMax = Vec4V_From_PxVec3_WUndefined(boxMax); + + Vec4V nqMinx4 = V4SplatElement<0>(nqMin); + Vec4V nqMiny4 = V4SplatElement<1>(nqMin); + Vec4V nqMinz4 = V4SplatElement<2>(nqMin); + Vec4V nqMaxx4 = V4SplatElement<0>(nqMax); + Vec4V nqMaxy4 = V4SplatElement<1>(nqMax); + Vec4V nqMaxz4 = V4SplatElement<2>(nqMax); + + // on 64-bit platforms the dynamic rtree pointer is also relative to mPages + PxU8* treeNodes8 = CAST_U8(mPages); + PxU32* stackPtr = stack; + + // AP potential perf optimization - fetch the top level right away + PX_ASSERT(RTREE_N == 4 || RTREE_N == 8); + PX_ASSERT(Ps::isPowerOfTwo(mPageSize)); + + for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --) + *stackPtr++ = j*sizeof(RTreePage); + + PxU32 cacheTopValid = true; + PxU32 cacheTop = 0; + + do { + stackPtr--; + PxU32 top; + if (cacheTopValid) // branch is faster than lhs + top = cacheTop; + else + top = stackPtr[0]; + PX_ASSERT(!cacheTopValid || stackPtr[0] == cacheTop); + RTreePage* PX_RESTRICT tn = reinterpret_cast(treeNodes8 + top); + const PxU32* ptrs = (reinterpret_cast(tn))->ptrs; + + Vec4V minx4 = V4LoadA(tn->minx); + Vec4V miny4 = V4LoadA(tn->miny); + Vec4V minz4 = V4LoadA(tn->minz); + Vec4V maxx4 = V4LoadA(tn->maxx); + Vec4V maxy4 = V4LoadA(tn->maxy); + Vec4V maxz4 = V4LoadA(tn->maxz); + + // AABB/AABB overlap test + BoolV res0 = V4IsGrtr(nqMinx4, maxx4); BoolV res1 = V4IsGrtr(nqMiny4, maxy4); BoolV res2 = V4IsGrtr(nqMinz4, maxz4); + BoolV res3 = V4IsGrtr(minx4, nqMaxx4); BoolV res4 = V4IsGrtr(miny4, nqMaxy4); BoolV res5 = V4IsGrtr(minz4, nqMaxz4); + BoolV resx = BOr(BOr(BOr(res0, res1), BOr(res2, res3)), BOr(res4, res5)); + PX_ALIGN_PREFIX(16) PxU32 resa[RTREE_N] PX_ALIGN_SUFFIX(16); + + VecU32V res4x = VecU32V_From_BoolV(resx); + U4StoreA(res4x, resa); + + cacheTopValid = false; + for (PxU32 i = 0; i < RTREE_N; i++) + { + PxU32 ptr = ptrs[i] & ~1; // clear the isLeaf bit + if (resa[i]) + continue; + if (tn->isLeaf(i)) + { + if (!callback->processResults(1, &ptr)) + return; + } + else + { + *(stackPtr++) = ptr; + cacheTop = ptr; + cacheTopValid = true; + } + } + } while (stackPtr > stack); +} + +///////////////////////////////////////////////////////////////////////// +template +void RTree::traverseRay( + const PxVec3& rayOrigin, const PxVec3& rayDir, + const PxU32 maxResults, PxU32* resultsPtr, Gu::RTree::CallbackRaycast* callback, + const PxVec3* fattenAABBs, PxF32 maxT) const +{ + // implements Kay-Kajiya 4-way SIMD test + PX_UNUSED(resultsPtr); + PX_UNUSED(maxResults); + + const PxU32 maxStack = 128; + PxU32 stack1[maxStack]; + PxU32* stack = stack1+1; + + PX_ASSERT(mPages); + PX_ASSERT((uintptr_t(mPages) & 127) == 0); + PX_ASSERT((uintptr_t(this) & 15) == 0); + + PxU8* treeNodes8 = CAST_U8(mPages); + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + PX_UNUSED(fattenAABBsX); PX_UNUSED(fattenAABBsY); PX_UNUSED(fattenAABBsZ); + if (inflate) + { + Vec4V fattenAABBs4 = Vec4V_From_PxVec3_WUndefined(*fattenAABBs); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + Vec4V maxT4; + maxT4 = V4Load(maxT); + Vec4V rayP = Vec4V_From_PxVec3_WUndefined(rayOrigin); + Vec4V rayD = Vec4V_From_PxVec3_WUndefined(rayDir); + VecU32V raySign = V4U32and(VecU32V_ReinterpretFrom_Vec4V(rayD), signMask); + Vec4V rayDAbs = V4Abs(rayD); // abs value of rayD + Vec4V rayInvD = Vec4V_ReinterpretFrom_VecU32V(V4U32or(raySign, VecU32V_ReinterpretFrom_Vec4V(V4Max(rayDAbs, epsFloat4)))); // clamp near-zero components up to epsilon + rayD = rayInvD; + + //rayInvD = V4Recip(rayInvD); + // Newton-Raphson iteration for reciprocal (see wikipedia): + // X[n+1] = X[n]*(2-original*X[n]), X[0] = V4RecipFast estimate + //rayInvD = rayInvD*(twos-rayD*rayInvD); + rayInvD = V4RecipFast(rayInvD); // initial estimate, not accurate enough + rayInvD = V4Mul(rayInvD, V4NegMulSub(rayD, rayInvD, twos)); + + // P+tD=a; t=(a-P)/D + // t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x) + Vec4V rayPinvD = V4NegMulSub(rayInvD, rayP, zeroes); + Vec4V rayInvDsplatX = V4SplatElement<0>(rayInvD); + Vec4V rayInvDsplatY = V4SplatElement<1>(rayInvD); + Vec4V rayInvDsplatZ = V4SplatElement<2>(rayInvD); + Vec4V rayPinvDsplatX = V4SplatElement<0>(rayPinvD); + Vec4V rayPinvDsplatY = V4SplatElement<1>(rayPinvD); + Vec4V rayPinvDsplatZ = V4SplatElement<2>(rayPinvD); + + PX_ASSERT(RTREE_N == 4 || RTREE_N == 8); + PX_ASSERT(mNumRootPages > 0); + + PxU32 stackPtr = 0; + for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --) + stack[stackPtr++] = j*sizeof(RTreePage); + + PX_ALIGN_PREFIX(16) PxU32 resa[4] PX_ALIGN_SUFFIX(16); + + while (stackPtr) + { + PxU32 top = stack[--stackPtr]; + if (top&1) // isLeaf test + { + top--; + PxF32 newMaxT = maxT; + if (!callback->processResults(1, &top, newMaxT)) + return; + /* shrink the ray if newMaxT is reduced compared to the original maxT */ + if (maxT != newMaxT) + { + PX_ASSERT(newMaxT < maxT); + maxT = newMaxT; + maxT4 = V4Load(newMaxT); + } + continue; + } + + RTreePage* PX_RESTRICT tn = reinterpret_cast(treeNodes8 + top); + + // 6i load + Vec4V minx4a = V4LoadA(tn->minx), miny4a = V4LoadA(tn->miny), minz4a = V4LoadA(tn->minz); + Vec4V maxx4a = V4LoadA(tn->maxx), maxy4a = V4LoadA(tn->maxy), maxz4a = V4LoadA(tn->maxz); + + // 1i disabled test + // AP scaffold - optimization opportunity - can save 2 instructions here + VecU32V ignore4a = V4IsGrtrV32u(minx4a, maxx4a); // 1 if degenerate box (empty slot in the page) + + if (inflate) + { + // 6i + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + // P+tD=a; t=(a-P)/D + // t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x) + // 6i + Vec4V tminxa0 = V4MulAdd(minx4a, rayInvDsplatX, rayPinvDsplatX); + Vec4V tminya0 = V4MulAdd(miny4a, rayInvDsplatY, rayPinvDsplatY); + Vec4V tminza0 = V4MulAdd(minz4a, rayInvDsplatZ, rayPinvDsplatZ); + Vec4V tmaxxa0 = V4MulAdd(maxx4a, rayInvDsplatX, rayPinvDsplatX); + Vec4V tmaxya0 = V4MulAdd(maxy4a, rayInvDsplatY, rayPinvDsplatY); + Vec4V tmaxza0 = V4MulAdd(maxz4a, rayInvDsplatZ, rayPinvDsplatZ); + + // test half-spaces + // P+tD=dN + // t = (d(N,D)-(P,D))/(D,D) , (D,D)=1 + + // compute 4x dot products (N,D) and (P,N) for each AABB in the page + + // 6i + // now compute tnear and tfar for each pair of planes for each box + Vec4V tminxa = V4Min(tminxa0, tmaxxa0); Vec4V tmaxxa = V4Max(tminxa0, tmaxxa0); + Vec4V tminya = V4Min(tminya0, tmaxya0); Vec4V tmaxya = V4Max(tminya0, tmaxya0); + Vec4V tminza = V4Min(tminza0, tmaxza0); Vec4V tmaxza = V4Max(tminza0, tmaxza0); + + // 8i + Vec4V maxOfNeasa = V4Max(V4Max(tminxa, tminya), tminza); + Vec4V minOfFarsa = V4Min(V4Min(tmaxxa, tmaxya), tmaxza); + ignore4a = V4U32or(ignore4a, V4IsGrtrV32u(epsFloat4, minOfFarsa)); // if tfar is negative, ignore since its a ray, not a line + // AP scaffold: update the build to eliminate 3 more instructions for ignore4a above + //VecU32V ignore4a = V4IsGrtrV32u(epsFloat4, minOfFarsa); // if tfar is negative, ignore since its a ray, not a line + ignore4a = V4U32or(ignore4a, V4IsGrtrV32u(maxOfNeasa, maxT4)); // if tnear is over maxT, ignore this result + + // 2i + VecU32V resa4 = V4IsGrtrV32u(maxOfNeasa, minOfFarsa); // if 1 => fail + resa4 = V4U32or(resa4, ignore4a); + + // 1i + V4U32StoreAligned(resa4, reinterpret_cast(resa)); + + PxU32* ptrs = (reinterpret_cast(tn))->ptrs; + + stack[stackPtr] = ptrs[0]; stackPtr += (1+resa[0]); // AP scaffold TODO: use VecU32add + stack[stackPtr] = ptrs[1]; stackPtr += (1+resa[1]); + stack[stackPtr] = ptrs[2]; stackPtr += (1+resa[2]); + stack[stackPtr] = ptrs[3]; stackPtr += (1+resa[3]); + } +} + +//explicit template instantiation +template void RTree::traverseRay<0>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const; + +template void RTree::traverseRay<1>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const; + +///////////////////////////////////////////////////////////////////////// +void RTree::traverseOBB( + const Gu::Box& obb, const PxU32 maxResults, PxU32* resultsPtr, Gu::RTree::Callback* callback) const +{ + PX_UNUSED(resultsPtr); + PX_UNUSED(maxResults); + + const PxU32 maxStack = 128; + PxU32 stack[maxStack]; + + PX_ASSERT(mPages); + PX_ASSERT((uintptr_t(mPages) & 127) == 0); + PX_ASSERT((uintptr_t(this) & 15) == 0); + + PxU8* treeNodes8 = CAST_U8(mPages); + PxU32* stackPtr = stack; + + Vec4V ones, halves, eps; + ones = V4Load(1.0f); + halves = V4Load(0.5f); + eps = V4Load(1e-6f); + + PX_UNUSED(ones); + + Vec4V obbO = Vec4V_From_PxVec3_WUndefined(obb.center); + Vec4V obbE = Vec4V_From_PxVec3_WUndefined(obb.extents); + // Gu::Box::rot matrix columns are the OBB axes + Vec4V obbX = Vec4V_From_PxVec3_WUndefined(obb.rot.column0); + Vec4V obbY = Vec4V_From_PxVec3_WUndefined(obb.rot.column1); + Vec4V obbZ = Vec4V_From_PxVec3_WUndefined(obb.rot.column2); + +#if PX_WINDOWS || PX_XBOXONE + // Visual Studio compiler hangs with #defines + // On VMX platforms we use #defines in the other branch of this #ifdef to avoid register spills (LHS) + Vec4V obbESplatX = V4SplatElement<0>(obbE); + Vec4V obbESplatY = V4SplatElement<1>(obbE); + Vec4V obbESplatZ = V4SplatElement<2>(obbE); + Vec4V obbESplatNegX = V4Sub(zeroes, obbESplatX); + Vec4V obbESplatNegY = V4Sub(zeroes, obbESplatY); + Vec4V obbESplatNegZ = V4Sub(zeroes, obbESplatZ); + Vec4V obbXE = V4MulAdd(obbX, obbESplatX, zeroes); // scale axii by E + Vec4V obbYE = V4MulAdd(obbY, obbESplatY, zeroes); // scale axii by E + Vec4V obbZE = V4MulAdd(obbZ, obbESplatZ, zeroes); // scale axii by E + Vec4V obbOSplatX = V4SplatElement<0>(obbO); + Vec4V obbOSplatY = V4SplatElement<1>(obbO); + Vec4V obbOSplatZ = V4SplatElement<2>(obbO); + Vec4V obbXSplatX = V4SplatElement<0>(obbX); + Vec4V obbXSplatY = V4SplatElement<1>(obbX); + Vec4V obbXSplatZ = V4SplatElement<2>(obbX); + Vec4V obbYSplatX = V4SplatElement<0>(obbY); + Vec4V obbYSplatY = V4SplatElement<1>(obbY); + Vec4V obbYSplatZ = V4SplatElement<2>(obbY); + Vec4V obbZSplatX = V4SplatElement<0>(obbZ); + Vec4V obbZSplatY = V4SplatElement<1>(obbZ); + Vec4V obbZSplatZ = V4SplatElement<2>(obbZ); + Vec4V obbXESplatX = V4SplatElement<0>(obbXE); + Vec4V obbXESplatY = V4SplatElement<1>(obbXE); + Vec4V obbXESplatZ = V4SplatElement<2>(obbXE); + Vec4V obbYESplatX = V4SplatElement<0>(obbYE); + Vec4V obbYESplatY = V4SplatElement<1>(obbYE); + Vec4V obbYESplatZ = V4SplatElement<2>(obbYE); + Vec4V obbZESplatX = V4SplatElement<0>(obbZE); + Vec4V obbZESplatY = V4SplatElement<1>(obbZE); + Vec4V obbZESplatZ = V4SplatElement<2>(obbZE); +#else + #define obbESplatX V4SplatElement<0>(obbE) + #define obbESplatY V4SplatElement<1>(obbE) + #define obbESplatZ V4SplatElement<2>(obbE) + #define obbESplatNegX V4Sub(zeroes, obbESplatX) + #define obbESplatNegY V4Sub(zeroes, obbESplatY) + #define obbESplatNegZ V4Sub(zeroes, obbESplatZ) + #define obbXE V4MulAdd(obbX, obbESplatX, zeroes) + #define obbYE V4MulAdd(obbY, obbESplatY, zeroes) + #define obbZE V4MulAdd(obbZ, obbESplatZ, zeroes) + #define obbOSplatX V4SplatElement<0>(obbO) + #define obbOSplatY V4SplatElement<1>(obbO) + #define obbOSplatZ V4SplatElement<2>(obbO) + #define obbXSplatX V4SplatElement<0>(obbX) + #define obbXSplatY V4SplatElement<1>(obbX) + #define obbXSplatZ V4SplatElement<2>(obbX) + #define obbYSplatX V4SplatElement<0>(obbY) + #define obbYSplatY V4SplatElement<1>(obbY) + #define obbYSplatZ V4SplatElement<2>(obbY) + #define obbZSplatX V4SplatElement<0>(obbZ) + #define obbZSplatY V4SplatElement<1>(obbZ) + #define obbZSplatZ V4SplatElement<2>(obbZ) + #define obbXESplatX V4SplatElement<0>(obbXE) + #define obbXESplatY V4SplatElement<1>(obbXE) + #define obbXESplatZ V4SplatElement<2>(obbXE) + #define obbYESplatX V4SplatElement<0>(obbYE) + #define obbYESplatY V4SplatElement<1>(obbYE) + #define obbYESplatZ V4SplatElement<2>(obbYE) + #define obbZESplatX V4SplatElement<0>(obbZE) + #define obbZESplatY V4SplatElement<1>(obbZE) + #define obbZESplatZ V4SplatElement<2>(obbZE) +#endif + + PX_ASSERT(mPageSize == 4 || mPageSize == 8); + PX_ASSERT(mNumRootPages > 0); + + for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --) + *stackPtr++ = j*sizeof(RTreePage); + PxU32 cacheTopValid = true; + PxU32 cacheTop = 0; + + PX_ALIGN_PREFIX(16) PxU32 resa_[4] PX_ALIGN_SUFFIX(16); + + do { + stackPtr--; + + PxU32 top; + if (cacheTopValid) // branch is faster than lhs + top = cacheTop; + else + top = stackPtr[0]; + PX_ASSERT(!cacheTopValid || top == cacheTop); + RTreePage* PX_RESTRICT tn = reinterpret_cast(treeNodes8 + top); + + const PxU32 offs = 0; + PxU32* ptrs = (reinterpret_cast(tn))->ptrs; + + // 6i + Vec4V minx4a = V4LoadA(tn->minx+offs); + Vec4V miny4a = V4LoadA(tn->miny+offs); + Vec4V minz4a = V4LoadA(tn->minz+offs); + Vec4V maxx4a = V4LoadA(tn->maxx+offs); + Vec4V maxy4a = V4LoadA(tn->maxy+offs); + Vec4V maxz4a = V4LoadA(tn->maxz+offs); + + VecU32V noOverlapa; + VecU32V resa4u; + { + // PRECOMPUTE FOR A BLOCK + // 109 instr per 4 OBB/AABB + // ABB iteration 1, start with OBB origin as other point -- 6 + Vec4V p1ABBxa = V4Max(minx4a, V4Min(maxx4a, obbOSplatX)); + Vec4V p1ABBya = V4Max(miny4a, V4Min(maxy4a, obbOSplatY)); + Vec4V p1ABBza = V4Max(minz4a, V4Min(maxz4a, obbOSplatZ)); + + // OBB iteration 1, move to OBB space first -- 12 + Vec4V p1ABBOxa = V4Sub(p1ABBxa, obbOSplatX); + Vec4V p1ABBOya = V4Sub(p1ABBya, obbOSplatY); + Vec4V p1ABBOza = V4Sub(p1ABBza, obbOSplatZ); + Vec4V obbPrjXa = V4MulAdd(p1ABBOxa, obbXSplatX, V4MulAdd(p1ABBOya, obbXSplatY, V4MulAdd(p1ABBOza, obbXSplatZ, zeroes))); + Vec4V obbPrjYa = V4MulAdd(p1ABBOxa, obbYSplatX, V4MulAdd(p1ABBOya, obbYSplatY, V4MulAdd(p1ABBOza, obbYSplatZ, zeroes))); + Vec4V obbPrjZa = V4MulAdd(p1ABBOxa, obbZSplatX, V4MulAdd(p1ABBOya, obbZSplatY, V4MulAdd(p1ABBOza, obbZSplatZ, zeroes))); + // clamp AABB point in OBB space to OBB extents. Since we scaled the axii, the extents are [-1,1] -- 6 + Vec4V pOBBxa = V4Max(obbESplatNegX, V4Min(obbPrjXa, obbESplatX)); + Vec4V pOBBya = V4Max(obbESplatNegY, V4Min(obbPrjYa, obbESplatY)); + Vec4V pOBBza = V4Max(obbESplatNegZ, V4Min(obbPrjZa, obbESplatZ)); + // go back to AABB space. we have x,y,z in obb space, need to multiply by axii -- 9 + Vec4V p1OBBxa = V4MulAdd(pOBBxa, obbXSplatX, V4MulAdd(pOBBya, obbYSplatX, V4MulAdd(pOBBza, obbZSplatX, obbOSplatX))); + Vec4V p1OBBya = V4MulAdd(pOBBxa, obbXSplatY, V4MulAdd(pOBBya, obbYSplatY, V4MulAdd(pOBBza, obbZSplatY, obbOSplatY))); + Vec4V p1OBBza = V4MulAdd(pOBBxa, obbXSplatZ, V4MulAdd(pOBBya, obbYSplatZ, V4MulAdd(pOBBza, obbZSplatZ, obbOSplatZ))); + + // ABB iteration 2 -- 6 instructions + Vec4V p2ABBxa = V4Max(minx4a, V4Min(maxx4a, p1OBBxa)); + Vec4V p2ABBya = V4Max(miny4a, V4Min(maxy4a, p1OBBya)); + Vec4V p2ABBza = V4Max(minz4a, V4Min(maxz4a, p1OBBza)); + // above blocks add up to 12+12+15=39 instr + // END PRECOMPUTE FOR A BLOCK + + // for AABBs precompute extents and center -- 9i + Vec4V abbCxa = V4MulAdd(V4Add(maxx4a, minx4a), halves, zeroes); + Vec4V abbCya = V4MulAdd(V4Add(maxy4a, miny4a), halves, zeroes); + Vec4V abbCza = V4MulAdd(V4Add(maxz4a, minz4a), halves, zeroes); + Vec4V abbExa = V4Sub(maxx4a, abbCxa); + Vec4V abbEya = V4Sub(maxy4a, abbCya); + Vec4V abbEza = V4Sub(maxz4a, abbCza); + + // now test separating axes D1 = p1OBB-p1ABB and D2 = p1OBB-p2ABB -- 37 instructions per axis + // D1 first -- 3 instructions + Vec4V d1xa = V4Sub(p1OBBxa, p1ABBxa), d1ya = V4Sub(p1OBBya, p1ABBya), d1za = V4Sub(p1OBBza, p1ABBza); + + // for AABB compute projections of extents and center -- 6 + Vec4V abbExd1Prja = V4MulAdd(d1xa, abbExa, zeroes); + Vec4V abbEyd1Prja = V4MulAdd(d1ya, abbEya, zeroes); + Vec4V abbEzd1Prja = V4MulAdd(d1za, abbEza, zeroes); + Vec4V abbCd1Prja = V4MulAdd(d1xa, abbCxa, V4MulAdd(d1ya, abbCya, V4MulAdd(d1za, abbCza, zeroes))); + + // for obb project each halfaxis and origin and add abs values of half-axis projections -- 12 instructions + Vec4V obbXEd1Prja = V4MulAdd(d1xa, obbXESplatX, V4MulAdd(d1ya, obbXESplatY, V4MulAdd(d1za, obbXESplatZ, zeroes))); + Vec4V obbYEd1Prja = V4MulAdd(d1xa, obbYESplatX, V4MulAdd(d1ya, obbYESplatY, V4MulAdd(d1za, obbYESplatZ, zeroes))); + Vec4V obbZEd1Prja = V4MulAdd(d1xa, obbZESplatX, V4MulAdd(d1ya, obbZESplatY, V4MulAdd(d1za, obbZESplatZ, zeroes))); + Vec4V obbOd1Prja = V4MulAdd(d1xa, obbOSplatX, V4MulAdd(d1ya, obbOSplatY, V4MulAdd(d1za, obbOSplatZ, zeroes))); + + // compare lengths between projected centers with sum of projected radii -- 16i + Vec4V originDiffd1a = v_absm(V4Sub(abbCd1Prja, obbOd1Prja)); + Vec4V absABBRd1a = V4Add(V4Add(v_absm(abbExd1Prja), v_absm(abbEyd1Prja)), v_absm(abbEzd1Prja)); + Vec4V absOBBRd1a = V4Add(V4Add(v_absm(obbXEd1Prja), v_absm(obbYEd1Prja)), v_absm(obbZEd1Prja)); + VecU32V noOverlapd1a = V4IsGrtrV32u(V4Sub(originDiffd1a, eps), V4Add(absABBRd1a, absOBBRd1a)); + VecU32V epsNoOverlapd1a = V4IsGrtrV32u(originDiffd1a, eps); + + // D2 next (35 instr) + // 3i + Vec4V d2xa = V4Sub(p1OBBxa, p2ABBxa), d2ya = V4Sub(p1OBBya, p2ABBya), d2za = V4Sub(p1OBBza, p2ABBza); + // for AABB compute projections of extents and center -- 6 + Vec4V abbExd2Prja = V4MulAdd(d2xa, abbExa, zeroes); + Vec4V abbEyd2Prja = V4MulAdd(d2ya, abbEya, zeroes); + Vec4V abbEzd2Prja = V4MulAdd(d2za, abbEza, zeroes); + Vec4V abbCd2Prja = V4MulAdd(d2xa, abbCxa, V4MulAdd(d2ya, abbCya, V4MulAdd(d2za, abbCza, zeroes))); + // for obb project each halfaxis and origin and add abs values of half-axis projections -- 12i + Vec4V obbXEd2Prja = V4MulAdd(d2xa, obbXESplatX, V4MulAdd(d2ya, obbXESplatY, V4MulAdd(d2za, obbXESplatZ, zeroes))); + Vec4V obbYEd2Prja = V4MulAdd(d2xa, obbYESplatX, V4MulAdd(d2ya, obbYESplatY, V4MulAdd(d2za, obbYESplatZ, zeroes))); + Vec4V obbZEd2Prja = V4MulAdd(d2xa, obbZESplatX, V4MulAdd(d2ya, obbZESplatY, V4MulAdd(d2za, obbZESplatZ, zeroes))); + Vec4V obbOd2Prja = V4MulAdd(d2xa, obbOSplatX, V4MulAdd(d2ya, obbOSplatY, V4MulAdd(d2za, obbOSplatZ, zeroes))); + // compare lengths between projected centers with sum of projected radii -- 16i + Vec4V originDiffd2a = v_absm(V4Sub(abbCd2Prja, obbOd2Prja)); + Vec4V absABBRd2a = V4Add(V4Add(v_absm(abbExd2Prja), v_absm(abbEyd2Prja)), v_absm(abbEzd2Prja)); + Vec4V absOBBRd2a = V4Add(V4Add(v_absm(obbXEd2Prja), v_absm(obbYEd2Prja)), v_absm(obbZEd2Prja)); + VecU32V noOverlapd2a = V4IsGrtrV32u(V4Sub(originDiffd2a, eps), V4Add(absABBRd2a, absOBBRd2a)); + VecU32V epsNoOverlapd2a = V4IsGrtrV32u(originDiffd2a, eps); + + // 8i + noOverlapa = V4U32or(V4U32and(noOverlapd1a, epsNoOverlapd1a), V4U32and(noOverlapd2a, epsNoOverlapd2a)); + VecU32V ignore4a = V4IsGrtrV32u(minx4a, maxx4a); // 1 if degenerate box (empty slot) + noOverlapa = V4U32or(noOverlapa, ignore4a); + resa4u = V4U32Andc(U4Load(1), noOverlapa); // 1 & ~noOverlap + V4U32StoreAligned(resa4u, reinterpret_cast(resa_)); + ///// 8+16+12+6+3+16+12+6+3+9+6+9+6+12+6+6=136i from load to result + } + + cacheTopValid = false; + for (PxU32 i = 0; i < 4; i++) + { + PxU32 ptr = ptrs[i+offs] & ~1; // clear the isLeaf bit + if (resa_[i]) + { + if (tn->isLeaf(i)) + { + if (!callback->processResults(1, &ptr)) + return; + } + else + { + *(stackPtr++) = ptr; + cacheTop = ptr; + cacheTopValid = true; + } + } + } + } while (stackPtr > stack); +} + +} // namespace Gu + +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h new file mode 100644 index 000000000..d6d803174 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h @@ -0,0 +1,105 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_CONVEX_TRI +#define GU_SWEEP_CONVEX_TRI + +#include "GuVecTriangle.h" +#include "GuVecConvexHull.h" +#include "GuConvexMesh.h" +#include "PxConvexMeshGeometry.h" +#include "GuGJKRaycast.h" + +// return true if hit, false if no hit +static PX_FORCE_INLINE bool sweepConvexVsTriangle( + const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, + ConvexHullV& convexHull, const Ps::aos::PsMatTransformV& meshToConvex, const Ps::aos::PsTransformV& convexTransfV, + const Ps::aos::Vec3VArg convexSpaceDir, const PxVec3& unitDir, const PxVec3& meshSpaceUnitDir, + const Ps::aos::FloatVArg fullDistance, PxReal shrunkDistance, + PxSweepHit& hit, bool isDoubleSided, const PxReal inflation, bool& initialOverlap, PxU32 faceIndex) +{ + using namespace Ps::aos; + if(!isDoubleSided) + { + // Create triangle normal + const PxVec3 denormalizedNormal = (v1 - v0).cross(v2 - v1); + + // Backface culling + // PT: WARNING, the test is reversed compared to usual because we pass -unitDir to this function + const bool culled = denormalizedNormal.dot(meshSpaceUnitDir) <= 0.0f; + if(culled) + return false; + } + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + + const Vec3V p0 = V3LoadU(v0); // in mesh local space + const Vec3V p1 = V3LoadU(v1); + const Vec3V p2 = V3LoadU(v2); + + // transform triangle verts from mesh local to convex local space + TriangleV triangleV(meshToConvex.transform(p0), meshToConvex.transform(p1), meshToConvex.transform(p2)); + + FloatV toi; + Vec3V closestA,normal; + + LocalConvex convexA(triangleV); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), convexHull.getCenter()); + // run GJK raycast + // sweep triangle in convex local space vs convex, closestA will be the impact point in convex local space + const bool gjkHit = gjkRaycastPenetration, LocalConvex >( + convexA, convexB, initialSearchDir, zero, zeroV, convexSpaceDir, toi, normal, closestA, inflation, false); + if(!gjkHit) + return false; + + if(FAllGrtrOrEq(zero, toi)) + { + initialOverlap = true; // PT: TODO: redundant with hit distance, consider removing + return setInitialOverlapResults(hit, unitDir, faceIndex); + } + + const FloatV minDist = FLoad(shrunkDistance); + const FloatV dist = FMul(toi, fullDistance); // scale the toi to original full sweep distance + if(FAllGrtr(minDist, dist)) // is current dist < minDist? + { + hit.faceIndex = faceIndex; + hit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + const Vec3V destWorldPointA = convexTransfV.transform(closestA); + const Vec3V destNormal = V3Normalize(convexTransfV.rotate(normal)); + V3StoreU(destWorldPointA, hit.position); + V3StoreU(destNormal, hit.normal); + FStore(dist, &hit.distance); + return true; // report a hit + } + return false; // report no hit +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h new file mode 100644 index 000000000..7d568acb9 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h @@ -0,0 +1,169 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_MESH_H +#define GU_SWEEP_MESH_H + +#include "GuMidphaseInterface.h" +#include "GuVecConvexHull.h" + +namespace physx +{ + +namespace Gu +{ + // PT: class to make sure we can safely V4Load Matrix34's last column + class Matrix34Padded : public Cm::Matrix34 + { + public: + PX_FORCE_INLINE Matrix34Padded(const Matrix34& src) : Matrix34(src) {} + PX_FORCE_INLINE Matrix34Padded() {} + PX_FORCE_INLINE ~Matrix34Padded() {} + PxU32 padding; + }; + PX_COMPILE_TIME_ASSERT(0==(sizeof(Matrix34Padded)==16)); + + // PT: intermediate class containing shared bits of code & members + struct SweepShapeMeshHitCallback : MeshHitCallback + { + SweepShapeMeshHitCallback(CallbackMode::Enum mode, const PxHitFlags& hitFlags, bool flipNormal, float distCoef); + + const PxHitFlags mHitFlags; + bool mStatus; // Default is false, set to true if a valid hit is found. Stays true once true. + bool mInitialOverlap; // Default is false, set to true if an initial overlap hit is found. Reset for each hit. + bool mFlipNormal; // If negative scale is used we need to flip normal + PxReal mDistCoeff; // dist coeff from unscaled to scaled distance + + void operator=(const SweepShapeMeshHitCallback&) {} + }; + + struct SweepCapsuleMeshHitCallback : SweepShapeMeshHitCallback + { + PxSweepHit& mSweepHit; + const Cm::Matrix34& mVertexToWorldSkew; + const PxReal mTrueSweepDistance; // max sweep distance that can be used + PxReal mBestAlignmentValue; // best alignment value for triangle normal + PxReal mBestDist; // best distance, not the same as sweepHit.distance, can be shorter by epsilon + const Capsule& mCapsule; + const PxVec3& mUnitDir; + const bool mMeshDoubleSided; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED + const bool mIsSphere; + + SweepCapsuleMeshHitCallback(PxSweepHit& sweepHit, const Cm::Matrix34& worldMatrix, PxReal distance, bool meshDoubleSided, + const Capsule& capsule, const PxVec3& unitDir, const PxHitFlags& hitFlags, bool flipNormal, float distCoef); + + virtual PxAgain processHit(const PxRaycastHit& aHit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32*); + + // PT: TODO: unify these operators + void operator=(const SweepCapsuleMeshHitCallback&) {} + + bool finalizeHit( PxSweepHit& sweepHit, const Capsule& lss, const PxTriangleMeshGeometry& triMeshGeom, + const PxTransform& pose, bool isDoubleSided) const; + }; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + struct SweepBoxMeshHitCallback : SweepShapeMeshHitCallback + { + const Matrix34Padded& mMeshToBox; + PxReal mDist, mDist0; + physx::shdfnd::aos::FloatV mDistV; + const Box& mBox; + const PxVec3& mLocalDir; + const PxVec3& mWorldUnitDir; + PxReal mInflation; + PxTriangle mHitTriangle; + physx::shdfnd::aos::Vec3V mMinClosestA; + physx::shdfnd::aos::Vec3V mMinNormal; + physx::shdfnd::aos::Vec3V mLocalMotionV; + PxU32 mMinTriangleIndex; + PxVec3 mOneOverDir; + const bool mBothTriangleSidesCollide; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED || PxHitFlag::eMESH_BOTH_SIDES + + SweepBoxMeshHitCallback(CallbackMode::Enum mode_, const Matrix34Padded& meshToBox, PxReal distance, bool bothTriangleSidesCollide, + const Box& box, const PxVec3& localMotion, const PxVec3& localDir, const PxVec3& unitDir, + const PxHitFlags& hitFlags, const PxReal inflation, bool flipNormal, float distCoef); + + virtual ~SweepBoxMeshHitCallback() {} + + virtual PxAgain processHit(const PxRaycastHit& meshHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal& shrinkMaxT, const PxU32*); + + bool finalizeHit( PxSweepHit& sweepHit, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const PxTransform& boxTransform, const PxVec3& localDir, + bool meshBothSides, bool isDoubleSided) const; + + private: + SweepBoxMeshHitCallback& operator=(const SweepBoxMeshHitCallback&); + }; + + struct SweepConvexMeshHitCallback : SweepShapeMeshHitCallback + { + PxTriangle mHitTriangle; + ConvexHullV mConvexHull; + physx::shdfnd::aos::PsMatTransformV mMeshToConvex; + physx::shdfnd::aos::PsTransformV mConvexPoseV; + const Cm::FastVertex2ShapeScaling& mMeshScale; + PxSweepHit mSweepHit; // stores either the closest or any hit depending on value of mAnyHit + physx::shdfnd::aos::FloatV mInitialDistance; + physx::shdfnd::aos::Vec3V mConvexSpaceDir; // convexPose.rotateInv(-unit*distance) + PxVec3 mUnitDir; + PxVec3 mMeshSpaceUnitDir; + PxReal mInflation; + const bool mAnyHit; + const bool mBothTriangleSidesCollide; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED || PxHitFlag::eMESH_BOTH_SIDES + + SweepConvexMeshHitCallback( const ConvexHullData& hull, const PxMeshScale& convexScale, const Cm::FastVertex2ShapeScaling& meshScale, + const PxTransform& convexPose, const PxTransform& meshPose, + const PxVec3& unitDir, const PxReal distance, PxHitFlags hitFlags, const bool bothTriangleSidesCollide, const PxReal inflation, + const bool anyHit, float distCoef); + + virtual ~SweepConvexMeshHitCallback() {} + + virtual PxAgain processHit(const PxRaycastHit& hit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal& shrunkMaxT, const PxU32*); + + bool finalizeHit(PxSweepHit& sweepHit, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, + const PxVec3& unitDir, PxReal inflation, + bool isMtd, bool meshBothSides, bool isDoubleSided, bool bothTriangleSidesCollide); + + private: + SweepConvexMeshHitCallback& operator=(const SweepConvexMeshHitCallback&); + }; + +#if PX_VC + #pragma warning(pop) +#endif + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp new file mode 100644 index 000000000..b4028ef8c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp @@ -0,0 +1,603 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTests.h" +#include "GuSweepMesh.h" +#include "GuInternal.h" +#include "GuConvexUtilsInternal.h" +#include "CmScaling.h" +#include "GuSweepMTD.h" +#include "GuVecBox.h" +#include "GuVecCapsule.h" +#include "GuSweepBoxTriangle_SAT.h" +#include "GuSweepCapsuleTriangle.h" +#include "GuSweepSphereTriangle.h" +#include "GuDistancePointTriangle.h" +#include "GuCapsule.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +#include "GuSweepConvexTri.h" + +/////////////////////////////////////////////////////////////////////////////// + +static bool sweepSphereTriangle(const PxTriangle& tri, + const PxVec3& center, PxReal radius, + const PxVec3& unitDir, const PxReal distance, + PxSweepHit& hit, PxVec3& triNormalOut, + PxHitFlags hitFlags, bool isDoubleSided) +{ + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + + // PT: test if shapes initially overlap + // PT: add culling here for now, but could be made more efficiently... + + // Create triangle normal + PxVec3 denormalizedNormal; + tri.denormalizedNormal(denormalizedNormal); + + // Backface culling + if(doBackfaceCulling && (denormalizedNormal.dot(unitDir) > 0.0f)) + return false; + + float s_unused, t_unused; + const PxVec3 cp = closestPtPointTriangle(center, tri.verts[0], tri.verts[1], tri.verts[2], s_unused, t_unused); + const PxReal dist2 = (cp - center).magnitudeSquared(); + if(dist2<=radius*radius) + { + triNormalOut = denormalizedNormal.getNormalized(); + return setInitialOverlapResults(hit, unitDir, 0); + } + } + + return sweepSphereTriangles(1, &tri, + center, radius, + unitDir, distance, + NULL, + hit, triNormalOut, + isDoubleSided, meshBothSides, false, false); +} + +/////////////////////////////////////////////////////////////////////////////// + +SweepShapeMeshHitCallback::SweepShapeMeshHitCallback(CallbackMode::Enum mode, const PxHitFlags& hitFlags, bool flipNormal, float distCoef) : + MeshHitCallback (mode), + mHitFlags (hitFlags), + mStatus (false), + mInitialOverlap (false), + mFlipNormal (flipNormal), + mDistCoeff (distCoef) +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +SweepCapsuleMeshHitCallback::SweepCapsuleMeshHitCallback( + PxSweepHit& sweepHit, const Matrix34& worldMatrix, PxReal distance, bool meshDoubleSided, + const Capsule& capsule, const PxVec3& unitDir, const PxHitFlags& hitFlags, bool flipNormal, float distCoef) : + SweepShapeMeshHitCallback (CallbackMode::eMULTIPLE, hitFlags, flipNormal, distCoef), + mSweepHit (sweepHit), + mVertexToWorldSkew (worldMatrix), + mTrueSweepDistance (distance), + mBestAlignmentValue (2.0f), + mBestDist (distance + GU_EPSILON_SAME_DISTANCE), + mCapsule (capsule), + mUnitDir (unitDir), + mMeshDoubleSided (meshDoubleSided), + mIsSphere (capsule.p0 == capsule.p1) +{ + mSweepHit.distance = mTrueSweepDistance; +} + +PxAgain SweepCapsuleMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32*) +{ + const PxTriangle tmpt( mVertexToWorldSkew.transform(v0), + mVertexToWorldSkew.transform(mFlipNormal ? v2 : v1), + mVertexToWorldSkew.transform(mFlipNormal ? v1 : v2)); + + PxSweepHit localHit; // PT: TODO: ctor! + PxVec3 triNormal; + // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + // make it a relative epsilon to make sure it still works with large distances + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE * PxMax(1.0f, mSweepHit.distance); + const float minD = mSweepHit.distance + distEpsilon; + if(mIsSphere) + { + if(!::sweepSphereTriangle( tmpt, + mCapsule.p0, mCapsule.radius, + mUnitDir, minD, + localHit, triNormal, + mHitFlags, mMeshDoubleSided)) + return true; + } + else + { + // PT: this one is safe because cullbox is NULL (no need to allocate one more triangle) + if(!sweepCapsuleTriangles_Precise( 1, &tmpt, + mCapsule, + mUnitDir, minD, + NULL, + localHit, triNormal, + mHitFlags, mMeshDoubleSided, + NULL)) + return true; + } + + const PxReal alignmentValue = computeAlignmentValue(triNormal, mUnitDir); +// if(keepTriangle(localHit.distance, alignmentValue, mBestDist, mBestAlignmentValue, mTrueSweepDistance, distEpsilon)) + if(keepTriangle(localHit.distance, alignmentValue, mBestDist, mBestAlignmentValue, mTrueSweepDistance, GU_EPSILON_SAME_DISTANCE)) + { + mBestAlignmentValue = alignmentValue; + + // AP: need to shrink the sweep distance passed into sweepCapsuleTriangles for correctness so that next sweep is closer + shrunkMaxT = localHit.distance * mDistCoeff; // shrunkMaxT is scaled + + mBestDist = PxMin(mBestDist, localHit.distance); // exact lower bound + mSweepHit.flags = localHit.flags; + mSweepHit.distance = localHit.distance; + mSweepHit.normal = localHit.normal; + mSweepHit.position = localHit.position; + mSweepHit.faceIndex = aHit.faceIndex; + + mStatus = true; + //ML:this is the initial overlap condition + if(localHit.distance == 0.0f) + { + mInitialOverlap = true; + return false; + } + if(mHitFlags & PxHitFlag::eMESH_ANY) + return false; // abort traversal + } + return true; +} + +bool SweepCapsuleMeshHitCallback::finalizeHit( PxSweepHit& sweepHit, const Capsule& lss, const PxTriangleMeshGeometry& triMeshGeom, + const PxTransform& pose, bool isDoubleSided) const +{ + if(!mStatus) + return false; + + if(mInitialOverlap) + { + // PT: TODO: consider using 'setInitialOverlapResults' here + bool hasContacts = false; + if(mHitFlags & PxHitFlag::eMTD) + { + const Vec3V p0 = V3LoadU(mCapsule.p0); + const Vec3V p1 = V3LoadU(mCapsule.p1); + const FloatV radius = FLoad(lss.radius); + CapsuleV capsuleV; + capsuleV.initialize(p0, p1, radius); + + //we need to calculate the MTD + hasContacts = computeCapsule_TriangleMeshMTD(triMeshGeom, pose, capsuleV, mCapsule.radius, isDoubleSided, sweepHit); + } + setupSweepHitForMTD(sweepHit, hasContacts, mUnitDir); + } + else + { + sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::ePOSITION | PxHitFlag::eFACE_INDEX; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool sweepCapsule_MeshGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + return Midphase::sweepCapsuleVsMesh(meshData, meshGeom, pose, lss, unitDir, distance, sweepHit, hitFlags, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// + + // same as 'mat.transform(p)' but using SIMD + static PX_FORCE_INLINE Vec4V transformV(const Vec4V p, const Matrix34Padded& mat) + { + Vec4V ResV = V4Scale(V4LoadU(&mat.m.column0.x), V4GetX(p)); + ResV = V4ScaleAdd(V4LoadU(&mat.m.column1.x), V4GetY(p), ResV); + ResV = V4ScaleAdd(V4LoadU(&mat.m.column2.x), V4GetZ(p), ResV); + ResV = V4Add(ResV, V4LoadU(&mat.p.x)); // PT: this load is safe thanks to padding + return ResV; + } + +/////////////////////////////////////////////////////////////////////////////// + +SweepBoxMeshHitCallback::SweepBoxMeshHitCallback( CallbackMode::Enum mode_, const Matrix34Padded& meshToBox, PxReal distance, bool bothTriangleSidesCollide, + const Box& box, const PxVec3& localMotion, const PxVec3& localDir, const PxVec3& unitDir, + const PxHitFlags& hitFlags, const PxReal inflation, bool flipNormal, float distCoef) : + SweepShapeMeshHitCallback (mode_, hitFlags, flipNormal,distCoef), + mMeshToBox (meshToBox), + mDist (distance), + mBox (box), + mLocalDir (localDir), + mWorldUnitDir (unitDir), + mInflation (inflation), + mBothTriangleSidesCollide (bothTriangleSidesCollide) +{ + mLocalMotionV = V3LoadU(localMotion); + mDistV = FLoad(distance); + mDist0 = distance; + mOneOverDir = PxVec3( + mLocalDir.x!=0.0f ? 1.0f/mLocalDir.x : 0.0f, + mLocalDir.y!=0.0f ? 1.0f/mLocalDir.y : 0.0f, + mLocalDir.z!=0.0f ? 1.0f/mLocalDir.z : 0.0f); +} + +PxAgain SweepBoxMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& meshHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal& shrinkMaxT, const PxU32*) +{ + if(mHitFlags & PxHitFlag::ePRECISE_SWEEP) + { + const PxTriangle currentTriangle( + mMeshToBox.transform(lp0), + mMeshToBox.transform(mFlipNormal ? lp2 : lp1), + mMeshToBox.transform(mFlipNormal ? lp1 : lp2)); + + PxF32 t = PX_MAX_REAL; // PT: could be better! + if(!triBoxSweepTestBoxSpace(currentTriangle, mBox.extents, mLocalDir, mOneOverDir, mDist, t, !mBothTriangleSidesCollide)) + return true; + + if(t <= mDist) + { + // PT: test if shapes initially overlap + mDist = t; + shrinkMaxT = t * mDistCoeff; // shrunkMaxT is scaled + mMinClosestA = V3LoadU(currentTriangle.verts[0]); // PT: this is arbitrary + mMinNormal = V3LoadU(-mWorldUnitDir); + mStatus = true; + mMinTriangleIndex = meshHit.faceIndex; + mHitTriangle = currentTriangle; + if(t == 0.0f) + { + mInitialOverlap = true; + return false; // abort traversal + } + } + } + else + { + const FloatV zero = FZero(); + + // PT: SIMD code similar to: + // const Vec3V triV0 = V3LoadU(mMeshToBox.transform(lp0)); + // const Vec3V triV1 = V3LoadU(mMeshToBox.transform(lp1)); + // const Vec3V triV2 = V3LoadU(mMeshToBox.transform(lp2)); + // + // SIMD version works but we need to ensure all loads are safe. + // For incoming vertices they should either come from the vertex array or from a binary deserialized file. + // For the vertex array we can just allocate one more vertex. For the binary file it should be ok as soon + // as vertices aren't the last thing serialized in the file. + // For the matrix only the last column is a problem, and we can easily solve that with some padding in the local class. + const Vec3V triV0 = Vec3V_From_Vec4V(transformV(V4LoadU(&lp0.x), mMeshToBox)); + const Vec3V triV1 = Vec3V_From_Vec4V(transformV(V4LoadU(mFlipNormal ? &lp2.x : &lp1.x), mMeshToBox)); + const Vec3V triV2 = Vec3V_From_Vec4V(transformV(V4LoadU(mFlipNormal ? &lp1.x : &lp2.x), mMeshToBox)); + + if(!mBothTriangleSidesCollide) + { + const Vec3V triNormal = V3Cross(V3Sub(triV2, triV1),V3Sub(triV0, triV1)); + if(FAllGrtrOrEq(V3Dot(triNormal, mLocalMotionV), zero)) + return true; + } + + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(mBox.extents); + const BoxV boxV(zeroV, boxExtents); + + const TriangleV triangleV(triV0, triV1, triV2); + + FloatV lambda; + Vec3V closestA, normal;//closestA and normal is in the local space of convex hull + LocalConvex convexA(triangleV); + LocalConvex convexB(boxV); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter()); + if(!gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, mLocalMotionV, lambda, normal, closestA, mInflation, false)) + return true; + + mStatus = true; + mMinClosestA = closestA; + mMinTriangleIndex = meshHit.faceIndex; + if(FAllGrtrOrEq(zero, lambda)) // lambda < 0? => initial overlap + { + mInitialOverlap = true; + shrinkMaxT = 0.0f; + mDistV = zero; + mDist = 0.0f; + mMinNormal = V3LoadU(-mWorldUnitDir); + return false; + } + + PxF32 f; + FStore(lambda, &f); + mDist = f*mDist; // shrink dist + mLocalMotionV = V3Scale(mLocalMotionV, lambda); // shrink localMotion + mDistV = FMul(mDistV, lambda); // shrink distV + mMinNormal = normal; + if(mDist * mDistCoeff < shrinkMaxT) // shrink shrinkMaxT + shrinkMaxT = mDist * mDistCoeff; // shrunkMaxT is scaled + + //mHitTriangle = currentTriangle; + V3StoreU(triV0, mHitTriangle.verts[0]); + V3StoreU(triV1, mHitTriangle.verts[1]); + V3StoreU(triV2, mHitTriangle.verts[2]); + } + return true; +} + +bool SweepBoxMeshHitCallback::finalizeHit( PxSweepHit& sweepHit, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const PxTransform& boxTransform, const PxVec3& localDir, + bool meshBothSides, bool isDoubleSided) const +{ + if(!mStatus) + return false; + + Vec3V minClosestA = mMinClosestA; + Vec3V minNormal = mMinNormal; + sweepHit.faceIndex = mMinTriangleIndex; + + if(mInitialOverlap) + { + bool hasContacts = false; + if(mHitFlags & PxHitFlag::eMTD) + hasContacts = computeBox_TriangleMeshMTD(triMeshGeom, pose, mBox, boxTransform, mInflation, mBothTriangleSidesCollide, sweepHit); + + setupSweepHitForMTD(sweepHit, hasContacts, mWorldUnitDir); + } + else + { + sweepHit.distance = mDist; + sweepHit.flags = PxHitFlag::eFACE_INDEX; + + // PT: we need the "best triangle" normal in order to call 'shouldFlipNormal'. We stored the best + // triangle in both GJK & precise codepaths (in box space). We use a dedicated 'shouldFlipNormal' + // function that delays computing the triangle normal. + // TODO: would still be more efficient to store the best normal directly, it's already computed at least + // in the GJK codepath. + + const Vec3V p0 = V3LoadU(&boxTransform.p.x); + const QuatV q0 = QuatVLoadU(&boxTransform.q.x); + const PsTransformV boxPos(p0, q0); + + if(mHitFlags & PxHitFlag::ePRECISE_SWEEP) + { + computeBoxLocalImpact(sweepHit.position, sweepHit.normal, sweepHit.flags, mBox, localDir, mHitTriangle, mHitFlags, isDoubleSided, meshBothSides, mDist); + } + else + { + sweepHit.flags |= PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + + // PT: now for the GJK path, we must first always negate the returned normal. Similar to what happens in the precise path, + // we can't delay this anymore: our normal must be properly oriented in order to call 'shouldFlipNormal'. + minNormal = V3Neg(minNormal); + + // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention + PxVec3 tmp; + V3StoreU(minNormal, tmp); + + if(shouldFlipNormal(tmp, meshBothSides, isDoubleSided, mHitTriangle, localDir, NULL)) + minNormal = V3Neg(minNormal); + + // PT: finally, this moves everything back to world space + V3StoreU(boxPos.rotate(minNormal), sweepHit.normal); + V3StoreU(boxPos.transform(minClosestA), sweepHit.position); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool sweepBox_MeshGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + return Midphase::sweepBoxVsMesh(meshData, meshGeom, pose, box, unitDir, distance, sweepHit, hitFlags, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// + +SweepConvexMeshHitCallback::SweepConvexMeshHitCallback( const ConvexHullData& hull, const PxMeshScale& convexScale, const FastVertex2ShapeScaling& meshScale, + const PxTransform& convexPose, const PxTransform& meshPose, + const PxVec3& unitDir, const PxReal distance, PxHitFlags hitFlags, const bool bothTriangleSidesCollide, const PxReal inflation, + const bool anyHit, float distCoef) : + SweepShapeMeshHitCallback (CallbackMode::eMULTIPLE, hitFlags, meshScale.flipsNormal(), distCoef), + mMeshScale (meshScale), + mUnitDir (unitDir), + mInflation (inflation), + mAnyHit (anyHit), + mBothTriangleSidesCollide (bothTriangleSidesCollide) +{ + mSweepHit.distance = distance; // this will be shrinking progressively as we sweep and clip the sweep length + mSweepHit.faceIndex = 0xFFFFFFFF; + + mMeshSpaceUnitDir = meshPose.rotateInv(unitDir); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const QuatV q0 = QuatVLoadU(&meshPose.q.x); + const Vec3V p0 = V3LoadU(&meshPose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV meshPoseV(p0, q0); + const PsTransformV convexPoseV(p1, q1); + + mMeshToConvex = convexPoseV.transformInv(meshPoseV); + mConvexPoseV = convexPoseV; + mConvexSpaceDir = convexPoseV.rotateInv(V3Neg(V3Scale(worldDir, dist))); + mInitialDistance = dist; + + const Vec3V vScale = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexScale.rotation.x); + mConvexHull.initialize(&hull, V3Zero(), vScale, vQuat, convexScale.isIdentity()); +} + +PxAgain SweepConvexMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal& shrunkMaxT, const PxU32*) +{ + const PxVec3 v0 = mMeshScale * av0; + const PxVec3 v1 = mMeshScale * (mFlipNormal ? av2 : av1); + const PxVec3 v2 = mMeshScale * (mFlipNormal ? av1 : av2); + + // mSweepHit will be updated if sweep distance is < input mSweepHit.distance + const PxReal oldDist = mSweepHit.distance; + if(sweepConvexVsTriangle( + v0, v1, v2, mConvexHull, mMeshToConvex, mConvexPoseV, mConvexSpaceDir, + mUnitDir, mMeshSpaceUnitDir, mInitialDistance, oldDist, mSweepHit, mBothTriangleSidesCollide, + mInflation, mInitialOverlap, hit.faceIndex)) + { + mStatus = true; + shrunkMaxT = mSweepHit.distance * mDistCoeff; // shrunkMaxT is scaled + + // PT: added for 'shouldFlipNormal' + mHitTriangle.verts[0] = v0; + mHitTriangle.verts[1] = v1; + mHitTriangle.verts[2] = v2; + + if(mAnyHit) + return false; // abort traversal + + if(mSweepHit.distance == 0.0f) + return false; + } + return true; // continue traversal +} + +bool SweepConvexMeshHitCallback::finalizeHit( PxSweepHit& sweepHit, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, + const PxVec3& unitDir, PxReal inflation, + bool isMtd, bool meshBothSides, bool isDoubleSided, bool bothTriangleSidesCollide) +{ + if(!mStatus) + return false; + + if(mInitialOverlap) + { + bool hasContacts = false; + if(isMtd) + hasContacts = computeConvex_TriangleMeshMTD(meshGeom, pose, convexGeom, convexPose, inflation, bothTriangleSidesCollide, sweepHit); + + setupSweepHitForMTD(sweepHit, hasContacts, unitDir); + + sweepHit.faceIndex = mSweepHit.faceIndex; + } + else + { + sweepHit = mSweepHit; + //sweepHit.position += unitDir * sweepHit.distance; + sweepHit.normal = -sweepHit.normal; + sweepHit.normal.normalize(); + + // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention + // PT: beware, the best triangle is in mesh-space, but the impact data is in world-space already + if(shouldFlipNormal(sweepHit.normal, meshBothSides, isDoubleSided, mHitTriangle, unitDir, &pose)) + sweepHit.normal = -sweepHit.normal; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool sweepConvex_MeshGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + const bool idtScaleMesh = meshGeom.scale.isIdentity(); + + FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(meshGeom.scale); + + PX_ASSERT(!convexMesh->getLocalBoundsFast().isEmpty()); + const PxBounds3 hullAABB = convexMesh->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew()); + + Box hullOBB; + computeHullOBB(hullOBB, hullAABB, 0.0f, Matrix34(convexPose), Matrix34(pose), meshScaling, idtScaleMesh); + + hullOBB.extents.x += inflation; + hullOBB.extents.y += inflation; + hullOBB.extents.z += inflation; + + const PxVec3 localDir = pose.rotateInv(unitDir); + + // inverse transform the sweep direction and distance to mesh space + PxVec3 meshSpaceSweepVector = meshScaling.getShape2VertexSkew().transform(localDir*distance); + const PxReal meshSpaceSweepDist = meshSpaceSweepVector.normalize(); + + PxReal distCoeff = 1.0f; + if (!idtScaleMesh) + distCoeff = meshSpaceSweepDist / distance; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool isDoubleSided = meshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED; + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + SweepConvexMeshHitCallback callback( + convexMesh->getHull(), convexGeom.scale, meshScaling, convexPose, pose, -unitDir, distance, hitFlags, + bothTriangleSidesCollide, inflation, anyHit, distCoeff); + + Midphase::sweepConvexVsMesh(meshData, hullOBB, meshSpaceSweepVector, meshSpaceSweepDist, callback, anyHit); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + return callback.finalizeHit(sweepHit, meshGeom, pose, convexGeom, convexPose, unitDir, inflation, isMtd, meshBothSides, isDoubleSided, bothTriangleSidesCollide); +} + +/////////////////////////////////////////////////////////////////////////////// + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h new file mode 100644 index 000000000..421198706 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLE32_H +#define GU_TRIANGLE32_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PsUtilities.h" + +namespace physx +{ +namespace Gu +{ + /** + \brief Structure used to store indices for a triangles points. T is either PxU32 or PxU16 + + */ + + template + struct TriangleT// : public Ps::UserAllocated + { + PX_INLINE TriangleT() {} + PX_INLINE TriangleT(T a, T b, T c) { v[0] = a; v[1] = b; v[2] = c; } + template + PX_INLINE TriangleT(const TriangleT& other) { v[0] = other[0]; v[1] = other[1]; v[2] = other[2]; } + PX_INLINE T& operator[](T i) { return v[i]; } + template//any type of TriangleT<>, possibly with different T + PX_INLINE TriangleT& operator=(const TriangleT& i) { v[0]=i[0]; v[1]=i[1]; v[2]=i[2]; return *this; } + PX_INLINE const T& operator[](T i) const { return v[i]; } + + void flip() + { + Ps::swap(v[1], v[2]); + } + + PX_INLINE void center(const PxVec3* verts, PxVec3& center) const + { + const PxVec3& p0 = verts[v[0]]; + const PxVec3& p1 = verts[v[1]]; + const PxVec3& p2 = verts[v[2]]; + center = (p0+p1+p2)*0.33333333333333333333f; + } + + float area(const PxVec3* verts) const + { + const PxVec3& p0 = verts[v[0]]; + const PxVec3& p1 = verts[v[1]]; + const PxVec3& p2 = verts[v[2]]; + return ((p0-p1).cross(p0-p2)).magnitude() * 0.5f; + } + + PxU8 findEdge(T vref0, T vref1) const + { + if(v[0]==vref0 && v[1]==vref1) return 0; + else if(v[0]==vref1 && v[1]==vref0) return 0; + else if(v[0]==vref0 && v[2]==vref1) return 1; + else if(v[0]==vref1 && v[2]==vref0) return 1; + else if(v[1]==vref0 && v[2]==vref1) return 2; + else if(v[1]==vref1 && v[2]==vref0) return 2; + return 0xff; + } + + // counter clock wise order + PxU8 findEdgeCCW(T vref0, T vref1) const + { + if(v[0]==vref0 && v[1]==vref1) return 0; + else if(v[0]==vref1 && v[1]==vref0) return 0; + else if(v[0]==vref0 && v[2]==vref1) return 2; + else if(v[0]==vref1 && v[2]==vref0) return 2; + else if(v[1]==vref0 && v[2]==vref1) return 1; + else if(v[1]==vref1 && v[2]==vref0) return 1; + return 0xff; + } + + bool replaceVertex(T oldref, T newref) + { + if(v[0]==oldref) { v[0] = newref; return true; } + else if(v[1]==oldref) { v[1] = newref; return true; } + else if(v[2]==oldref) { v[2] = newref; return true; } + return false; + } + + bool isDegenerate() const + { + if(v[0]==v[1]) return true; + if(v[1]==v[2]) return true; + if(v[2]==v[0]) return true; + return false; + } + + PX_INLINE void denormalizedNormal(const PxVec3* verts, PxVec3& normal) const + { + const PxVec3& p0 = verts[v[0]]; + const PxVec3& p1 = verts[v[1]]; + const PxVec3& p2 = verts[v[2]]; + normal = ((p2 - p1).cross(p0 - p1)); + } + + T v[3]; //vertex indices + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h new file mode 100644 index 000000000..5c46b8857 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h @@ -0,0 +1,207 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLE_CACHE_H +#define GU_TRIANGLE_CACHE_H +#include "PsHash.h" +#include "PsUtilities.h" + +namespace physx +{ + namespace Gu + { + struct CachedEdge + { + protected: + PxU32 mId0, mId1; + public: + CachedEdge(PxU32 i0, PxU32 i1) + { + mId0 = PxMin(i0, i1); + mId1 = PxMax(i0, i1); + } + + CachedEdge() + { + } + + PxU32 getId0() const { return mId0; } + PxU32 getId1() const { return mId1; } + + bool operator == (const CachedEdge& other) const + { + return mId0 == other.mId0 && mId1 == other.mId1; + } + + PxU32 getHashCode() const + { + return Ps::hash(mId0 << 16 | mId1); + } + }; + + struct CachedVertex + { + private: + PxU32 mId; + public: + CachedVertex(PxU32 id) + { + mId = id; + } + + CachedVertex() + { + } + + PxU32 getId() const { return mId; } + + PxU32 getHashCode() const + { + return mId; + } + + bool operator == (const CachedVertex& other) const + { + return mId == other.mId; + } + }; + + template + struct CacheMap + { + PX_COMPILE_TIME_ASSERT(MaxCount < 0xFF); + Elem mCache[MaxCount]; + PxU8 mNextInd[MaxCount]; + PxU8 mIndex[MaxCount]; + PxU32 mSize; + + CacheMap() : mSize(0) + { + for(PxU32 a = 0; a < MaxCount; ++a) + { + mIndex[a] = 0xFF; + } + } + + bool addData(const Elem& data) + { + if(mSize == MaxCount) + return false; + + const PxU8 hash = PxU8(data.getHashCode() % MaxCount); + + PxU8 index = hash; + PxU8 nextInd = mIndex[hash]; + while(nextInd != 0xFF) + { + index = nextInd; + if(mCache[index] == data) + return false; + nextInd = mNextInd[nextInd]; + } + + if(mIndex[hash] == 0xFF) + { + mIndex[hash] = Ps::to8(mSize); + } + else + { + mNextInd[index] = Ps::to8(mSize); + } + mNextInd[mSize] = 0xFF; + mCache[mSize++] = data; + return true; + } + + bool contains(const Elem& data) const + { + PxU32 hash = (data.getHashCode() % MaxCount); + PxU8 index = mIndex[hash]; + + while(index != 0xFF) + { + if(mCache[index] == data) + return true; + index = mNextInd[index]; + } + return false; + } + + const Elem* get(const Elem& data) const + { + PxU32 hash = (data.getHashCode() % MaxCount); + PxU8 index = mIndex[hash]; + + while(index != 0xFF) + { + if(mCache[index] == data) + return &mCache[index]; + index = mNextInd[index]; + } + return NULL; + } + }; + + template + struct TriangleCache + { + PxVec3 mVertices[3*MaxTriangles]; + PxU32 mIndices[3*MaxTriangles]; + PxU32 mTriangleIndex[MaxTriangles]; + PxU8 mEdgeFlags[MaxTriangles]; + PxU32 mNumTriangles; + + TriangleCache() : mNumTriangles(0) + { + } + + PX_FORCE_INLINE bool isEmpty() const { return mNumTriangles == 0; } + PX_FORCE_INLINE bool isFull() const { return mNumTriangles == MaxTriangles; } + PX_FORCE_INLINE void reset() { mNumTriangles = 0; } + + void addTriangle(const PxVec3* verts, const PxU32* indices, PxU32 triangleIndex, PxU8 edgeFlag) + { + PX_ASSERT(mNumTriangles < MaxTriangles); + PxU32 triInd = mNumTriangles++; + PxU32 triIndMul3 = triInd*3; + mVertices[triIndMul3] = verts[0]; + mVertices[triIndMul3+1] = verts[1]; + mVertices[triIndMul3+2] = verts[2]; + mIndices[triIndMul3] = indices[0]; + mIndices[triIndMul3+1] = indices[1]; + mIndices[triIndMul3+2] = indices[2]; + mTriangleIndex[triInd] = triangleIndex; + mEdgeFlags[triInd] = edgeFlag; + } + }; + } +} + +#endif + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp new file mode 100644 index 000000000..dfe813423 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp @@ -0,0 +1,232 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "GuMidphaseInterface.h" +#include "GuSerialize.h" +#include "GuMeshFactory.h" +#include "CmRenderOutput.h" +#include "PxVisualizationParameter.h" +#include "GuBox.h" +#include "PxMeshScale.h" +#include "CmUtils.h" + +using namespace physx; + +namespace physx +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PxConcreteType::Enum gTable[] = { PxConcreteType::eTRIANGLE_MESH_BVH33, + PxConcreteType::eTRIANGLE_MESH_BVH34 + }; + +Gu::TriangleMesh::TriangleMesh(GuMeshFactory& factory, TriangleMeshData& d) +: PxTriangleMesh(PxType(gTable[d.mType]), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mNbVertices (d.mNbVertices) +, mNbTriangles (d.mNbTriangles) +, mVertices (d.mVertices) +, mTriangles (d.mTriangles) +, mAABB (d.mAABB) +, mExtraTrigData (d.mExtraTrigData) +, mGeomEpsilon (d.mGeomEpsilon) +, mFlags (d.mFlags) +, mMaterialIndices (d.mMaterialIndices) +, mFaceRemap (d.mFaceRemap) +, mAdjacencies (d.mAdjacencies) + +, mMeshFactory (&factory) + +, mGRB_triIndices (d.mGRB_primIndices) + +, mGRB_triAdjacencies (d.mGRB_primAdjacencies) + +, mGRB_faceRemap (d.mGRB_faceRemap) +, mGRB_BV32Tree (d.mGRB_BV32Tree) +{ + // this constructor takes ownership of memory from the data object + d.mVertices = 0; + d.mTriangles = 0; + d.mExtraTrigData = 0; + d.mFaceRemap = 0; + d.mAdjacencies = 0; + d.mMaterialIndices = 0; + + d.mGRB_primIndices = 0; + + d.mGRB_primAdjacencies = 0; + d.mGRB_faceRemap = 0; + d.mGRB_BV32Tree = 0; + + // PT: 'getPaddedBounds()' is only safe if we make sure the bounds member is followed by at least 32bits of data + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(Gu::TriangleMesh, mExtraTrigData)>=PX_OFFSET_OF(Gu::TriangleMesh, mAABB)+4); + +} + +Gu::TriangleMesh::~TriangleMesh() +{ + if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + { + PX_FREE_AND_RESET(mExtraTrigData); + PX_FREE_AND_RESET(mFaceRemap); + PX_FREE_AND_RESET(mAdjacencies); + PX_FREE_AND_RESET(mMaterialIndices); + PX_FREE_AND_RESET(mTriangles); + PX_FREE_AND_RESET(mVertices); + + PX_FREE_AND_RESET(mGRB_triIndices); + + PX_FREE_AND_RESET(mGRB_triAdjacencies); + PX_FREE_AND_RESET(mGRB_faceRemap); + + BV32Tree* bv32Tree = reinterpret_cast(mGRB_BV32Tree); + PX_DELETE_AND_RESET(bv32Tree); + + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: used to be automatic but making it manual saves bytes in the internal mesh + +void Gu::TriangleMesh::exportExtraData(PxSerializationContext& stream) +{ + //PX_DEFINE_DYNAMIC_ARRAY(TriangleMesh, mVertices, PxField::eVEC3, mNbVertices, Ps::PxFieldFlag::eSERIALIZE), + if(mVertices) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mVertices, mNbVertices * sizeof(PxVec3)); + } + + if(mTriangles) + { + const PxU32 triangleSize = mFlags & PxTriangleMeshFlag::e16_BIT_INDICES ? sizeof(PxU16) : sizeof(PxU32); + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mTriangles, mNbTriangles * 3 * triangleSize); + } + + //PX_DEFINE_DYNAMIC_ARRAY(TriangleMesh, mExtraTrigData, PxField::eBYTE, mNbTriangles, Ps::PxFieldFlag::eSERIALIZE), + if(mExtraTrigData) + { + // PT: it might not be needed to 16-byte align this array of PxU8.... + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mExtraTrigData, mNbTriangles * sizeof(PxU8)); + } + + if(mMaterialIndices) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mMaterialIndices, mNbTriangles * sizeof(PxU16)); + } + + if(mFaceRemap) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mFaceRemap, mNbTriangles * sizeof(PxU32)); + } + + if(mAdjacencies) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mAdjacencies, mNbTriangles * sizeof(PxU32) * 3); + } +} + +void Gu::TriangleMesh::importExtraData(PxDeserializationContext& context) +{ + // PT: vertices are followed by indices, so it will be safe to V4Load vertices from a deserialized binary file + if(mVertices) + mVertices = context.readExtraData(mNbVertices); + + if(mTriangles) + { + if(mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) + mTriangles = context.readExtraData(3*mNbTriangles); + else + mTriangles = context.readExtraData(3*mNbTriangles); + } + + if(mExtraTrigData) + mExtraTrigData = context.readExtraData(mNbTriangles); + + if(mMaterialIndices) + mMaterialIndices = context.readExtraData(mNbTriangles); + + if(mFaceRemap) + mFaceRemap = context.readExtraData(mNbTriangles); + + if(mAdjacencies) + mAdjacencies = context.readExtraData(3*mNbTriangles); + + mGRB_triIndices = NULL; + mGRB_triAdjacencies = NULL; + mGRB_faceRemap = NULL; + mGRB_BV32Tree = NULL; +} + +void Gu::TriangleMesh::onRefCountZero() +{ + if(mMeshFactory->removeTriangleMesh(*this)) + { + const PxType type = getConcreteType(); + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, type); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::TriangleMesh::release: double deletion detected!"); +} +//~PX_SERIALIZATION + +void Gu::TriangleMesh::release() +{ + decRefCount(); +} + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +PxVec3* Gu::TriangleMesh::getVerticesForModification() +{ + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxTriangleMesh::getVerticesForModification() is only supported for meshes with PxMeshMidPhase::eBVH33."); + + return NULL; +} + +PxBounds3 Gu::TriangleMesh::refitBVH() +{ + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxTriangleMesh::refitBVH() is only supported for meshes with PxMeshMidPhase::eBVH33."); + + return PxBounds3(mAABB.getMin(), mAABB.getMax()); +} +#endif + +} // namespace physx diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h new file mode 100644 index 000000000..20bdb5c4b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h @@ -0,0 +1,284 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLEMESH_H +#define GU_TRIANGLEMESH_H + +#include "foundation/PxIO.h" +#include "PxSimpleTriangleMesh.h" +#include "PxTriangleMeshGeometry.h" +#include "CmScaling.h" +#include "GuTriangle32.h" +#include "CmRefCountable.h" +#include "PxTriangle.h" +#include "PxTriangleMesh.h" +#include "CmRenderOutput.h" +#include "GuMeshData.h" +#include "GuCenterExtents.h" + +namespace physx +{ + +class GuMeshFactory; +class PxMeshScale; + +namespace Gu +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +// Possible optimization: align the whole struct to cache line +class TriangleMesh : public PxTriangleMesh, public Ps::UserAllocated, public Cm::RefCountable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + +// PX_SERIALIZATION + TriangleMesh(PxBaseFlags baseFlags) : PxTriangleMesh(baseFlags), Cm::RefCountable(PxEmpty) {} + virtual void exportExtraData(PxSerializationContext& ctx); + void importExtraData(PxDeserializationContext&); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); + virtual void release(); + + void resolveReferences(PxDeserializationContext& ) {} + virtual void requiresObjects(PxProcessPxBaseCallback&){} +//~PX_SERIALIZATION + +// Cm::RefCountable + virtual void onRefCountZero(); +//~Cm::RefCountable + + TriangleMesh(GuMeshFactory& factory, TriangleMeshData& data); + virtual ~TriangleMesh(); + +// PxTriangleMesh + virtual PxU32 getNbVertices() const { return mNbVertices; } + virtual const PxVec3* getVertices() const { return mVertices; } + virtual const PxU32* getTrianglesRemap() const { return mFaceRemap; } + virtual PxU32 getNbTriangles() const { return mNbTriangles; } + virtual const void* getTriangles() const { return mTriangles; } + virtual PxTriangleMeshFlags getTriangleMeshFlags() const { return PxTriangleMeshFlags(mFlags); } + virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const { + return hasPerTriangleMaterials() ? getMaterials()[triangleIndex] : PxMaterialTableIndex(0xffff); } + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + virtual PxVec3* getVerticesForModification(); + virtual PxBounds3 refitBVH(); +#endif + + virtual PxBounds3 getLocalBounds() const + { + PX_ASSERT(mAABB.isValid()); + return PxBounds3::centerExtents(mAABB.mCenter, mAABB.mExtents); + } + + virtual void acquireReference() { incRefCount(); } + virtual PxU32 getReferenceCount() const { return getRefCount(); } +//~PxTriangleMesh + // PT: this one is just to prevent instancing Gu::TriangleMesh. + // But you should use PxBase::getConcreteType() instead to avoid the virtual call. + virtual PxMeshMidPhase::Enum getMidphaseID() const = 0; + + PX_FORCE_INLINE const PxU32* getFaceRemap() const { return mFaceRemap; } + PX_FORCE_INLINE bool has16BitIndices() const { return (mFlags & PxMeshFlag::e16_BIT_INDICES) ? true : false; } + PX_FORCE_INLINE bool hasPerTriangleMaterials() const { return mMaterialIndices != NULL; } + PX_FORCE_INLINE PxU32 getNbVerticesFast() const { return mNbVertices; } + PX_FORCE_INLINE PxU32 getNbTrianglesFast() const { return mNbTriangles; } + PX_FORCE_INLINE const void* getTrianglesFast() const { return mTriangles; } + PX_FORCE_INLINE const PxVec3* getVerticesFast() const { return mVertices; } + PX_FORCE_INLINE const PxU32* getAdjacencies() const { return mAdjacencies; } + PX_FORCE_INLINE PxReal getGeomEpsilon() const { return mGeomEpsilon; } + PX_FORCE_INLINE const CenterExtents& getLocalBoundsFast() const { return mAABB; } + PX_FORCE_INLINE const PxU16* getMaterials() const { return mMaterialIndices; } + PX_FORCE_INLINE const PxU8* getExtraTrigData() const { return mExtraTrigData; } + + PX_FORCE_INLINE const CenterExtentsPadded& getPaddedBounds() const + { + // PT: see compile-time assert in cpp + return static_cast(mAABB); + } + + PX_FORCE_INLINE void computeWorldTriangle( + PxTriangle& worldTri, PxTriangleID triangleIndex, const Cm::Matrix34& worldMatrix, bool flipNormal = false, + PxU32* PX_RESTRICT vertexIndices=NULL, PxU32* PX_RESTRICT adjacencyIndices=NULL) const; + PX_FORCE_INLINE void getLocalTriangle(PxTriangle& localTri, PxTriangleID triangleIndex, bool flipNormal = false) const; + + void setMeshFactory(GuMeshFactory* factory) { mMeshFactory = factory; } + +protected: + PxU32 mNbVertices; + PxU32 mNbTriangles; + PxVec3* mVertices; + void* mTriangles; //!< 16 (<= 0xffff #vertices) or 32 bit trig indices (mNbTriangles * 3) + // 16 bytes block + + // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading + CenterExtents mAABB; + PxU8* mExtraTrigData; //one per trig + PxReal mGeomEpsilon; //!< see comments in cooking code referencing this variable + // 16 bytes block + /* + low 3 bits (mask: 7) are the edge flags: + b001 = 1 = ignore edge 0 = edge v0-->v1 + b010 = 2 = ignore edge 1 = edge v0-->v2 + b100 = 4 = ignore edge 2 = edge v1-->v2 + */ + PxU8 mFlags; //!< Flag whether indices are 16 or 32 bits wide + //!< Flag whether triangle adajacencies are build + PxU16* mMaterialIndices; //!< the size of the array is numTriangles. + PxU32* mFaceRemap; //!< new faces to old faces mapping (after cleaning, etc). Usage: old = faceRemap[new] + PxU32* mAdjacencies; //!< Adjacency information for each face - 3 adjacent faces + //!< Set to 0xFFFFffff if no adjacent face + + GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization +public: + + // GRB data ------------------------- + void * mGRB_triIndices; //!< GRB: GPU-friendly tri indices [uint4] + + // TODO avoroshilov: cooking - adjacency info - duplicated, remove it and use 'mAdjacencies' and 'mExtraTrigData' see GuTriangleMesh.cpp:325 + void * mGRB_triAdjacencies; //!< GRB: adjacency data, with BOUNDARY and NONCONVEX flags (flags replace adj indices where applicable) + + PxU32* mGRB_faceRemap; //!< GRB : gpu to cpu triangle indice remap + void* mGRB_BV32Tree; //!< GRB: BV32 tree + // End of GRB data ------------------ + +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Gu + +PX_FORCE_INLINE void Gu::TriangleMesh::computeWorldTriangle(PxTriangle& worldTri, PxTriangleID triangleIndex, const Cm::Matrix34& worldMatrix, bool flipNormal, + PxU32* PX_RESTRICT vertexIndices, PxU32* PX_RESTRICT adjacencyIndices) const +{ + PxU32 vref0, vref1, vref2; + if(has16BitIndices()) + { + const Gu::TriangleT& T = (reinterpret_cast*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + else + { + const Gu::TriangleT& T = (reinterpret_cast*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + if (flipNormal) + Ps::swap(vref1, vref2); + const PxVec3* PX_RESTRICT vertices = getVerticesFast(); + worldTri.verts[0] = worldMatrix.transform(vertices[vref0]); + worldTri.verts[1] = worldMatrix.transform(vertices[vref1]); + worldTri.verts[2] = worldMatrix.transform(vertices[vref2]); + + if(vertexIndices) + { + vertexIndices[0] = vref0; + vertexIndices[1] = vref1; + vertexIndices[2] = vref2; + } + + if(adjacencyIndices) + { + if(getAdjacencies()) + { + adjacencyIndices[0] = flipNormal ? getAdjacencies()[triangleIndex*3 + 2] : getAdjacencies()[triangleIndex*3 + 0]; + adjacencyIndices[1] = getAdjacencies()[triangleIndex*3 + 1]; + adjacencyIndices[2] = flipNormal ? getAdjacencies()[triangleIndex*3 + 0] : getAdjacencies()[triangleIndex*3 + 2]; + } + else + { + adjacencyIndices[0] = 0xffffffff; + adjacencyIndices[1] = 0xffffffff; + adjacencyIndices[2] = 0xffffffff; + } + } +} + +PX_FORCE_INLINE void Gu::TriangleMesh::getLocalTriangle(PxTriangle& localTri, PxTriangleID triangleIndex, bool flipNormal) const +{ + PxU32 vref0, vref1, vref2; + if(has16BitIndices()) + { + const Gu::TriangleT& T = (reinterpret_cast*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + else + { + const Gu::TriangleT& T = (reinterpret_cast*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + if (flipNormal) + Ps::swap(vref1, vref2); + const PxVec3* PX_RESTRICT vertices = getVerticesFast(); + localTri.verts[0] = vertices[vref0]; + localTri.verts[1] = vertices[vref1]; + localTri.verts[2] = vertices[vref2]; +} + +PX_INLINE float computeSweepData(const PxTriangleMeshGeometry& triMeshGeom, /*const Cm::FastVertex2ShapeScaling& scaling,*/ PxVec3& sweepOrigin, PxVec3& sweepExtents, PxVec3& sweepDir, float distance) +{ + PX_ASSERT(!Cm::isEmpty(sweepOrigin, sweepExtents)); + + const PxVec3 endPt = sweepOrigin + sweepDir*distance; + PX_ASSERT(!Cm::isEmpty(endPt, sweepExtents)); + + const Cm::FastVertex2ShapeScaling meshScaling(triMeshGeom.scale.getInverse()); // shape to vertex transform + + const PxMat33& vertex2ShapeSkew = meshScaling.getVertex2ShapeSkew(); + + const PxVec3 originBoundsCenter = vertex2ShapeSkew * sweepOrigin; + const PxVec3 originBoundsExtents = Cm::basisExtent(vertex2ShapeSkew.column0, vertex2ShapeSkew.column1, vertex2ShapeSkew.column2, sweepExtents); + + sweepOrigin = originBoundsCenter; + sweepExtents = originBoundsExtents; + sweepDir = (vertex2ShapeSkew * endPt) - originBoundsCenter; + return sweepDir.normalizeSafe(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp new file mode 100644 index 000000000..71945ef5e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" + +using namespace physx; + +namespace physx +{ + +Gu::BV4TriangleMesh::BV4TriangleMesh(GuMeshFactory& factory, TriangleMeshData& d) +: TriangleMesh(factory, d) +{ + PX_ASSERT(d.mType==PxMeshMidPhase::eBVH34); + + BV4TriangleData& bv4Data = static_cast(d); + mMeshInterface = bv4Data.mMeshInterface; + mBV4Tree = bv4Data.mBV4Tree; + mBV4Tree.mMeshInterface = &mMeshInterface; +} + +Gu::TriangleMesh* Gu::BV4TriangleMesh::createObject(PxU8*& address, PxDeserializationContext& context) +{ + BV4TriangleMesh* obj = new (address) BV4TriangleMesh(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(BV4TriangleMesh); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void Gu::BV4TriangleMesh::exportExtraData(PxSerializationContext& stream) +{ + mBV4Tree.exportExtraData(stream); + TriangleMesh::exportExtraData(stream); +} + +void Gu::BV4TriangleMesh::importExtraData(PxDeserializationContext& context) +{ + mBV4Tree.importExtraData(context); + TriangleMesh::importExtraData(context); + + if(has16BitIndices()) + mMeshInterface.setPointers(NULL, const_cast(reinterpret_cast(getTrianglesFast())), getVerticesFast()); + else + mMeshInterface.setPointers(const_cast(reinterpret_cast(getTrianglesFast())), NULL, getVerticesFast()); + mBV4Tree.mMeshInterface = &mMeshInterface; +} + +} // namespace physx diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h new file mode 100644 index 000000000..558e2debd --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLEMESH_BV4_H +#define GU_TRIANGLEMESH_BV4_H + +#include "GuTriangleMesh.h" + +namespace physx +{ +class GuMeshFactory; + +namespace Gu +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +class BV4TriangleMesh : public TriangleMesh +{ + public: + virtual const char* getConcreteTypeName() const { return "PxBVH34TriangleMesh"; } +// PX_SERIALIZATION + BV4TriangleMesh(PxBaseFlags baseFlags) : TriangleMesh(baseFlags), mMeshInterface(PxEmpty), mBV4Tree(PxEmpty) {} + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& ctx); + void importExtraData(PxDeserializationContext&); + PX_PHYSX_COMMON_API static TriangleMesh* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + BV4TriangleMesh(GuMeshFactory& factory, TriangleMeshData& data); + virtual ~BV4TriangleMesh(){} + + virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH34; } + PX_FORCE_INLINE const Gu::BV4Tree& getBV4Tree() const { return mBV4Tree; } + private: + Gu::SourceMesh mMeshInterface; + Gu::BV4Tree mBV4Tree; +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp new file mode 100644 index 000000000..3c2710c4c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp @@ -0,0 +1,151 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuTriangleMesh.h" +#include "GuTriangleMeshRTree.h" +#if PX_ENABLE_DYNAMIC_MESH_RTREE +#include "GuConvexEdgeFlags.h" +#endif + +using namespace physx; + +namespace physx +{ + +Gu::RTreeTriangleMesh::RTreeTriangleMesh(GuMeshFactory& factory, TriangleMeshData& d) +: TriangleMesh(factory, d) +{ + PX_ASSERT(d.mType==PxMeshMidPhase::eBVH33); + + RTreeTriangleData& rtreeData = static_cast(d); + mRTree = rtreeData.mRTree; + rtreeData.mRTree.mPages = NULL; +} + +Gu::TriangleMesh* Gu::RTreeTriangleMesh::createObject(PxU8*& address, PxDeserializationContext& context) +{ + RTreeTriangleMesh* obj = new (address) RTreeTriangleMesh(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(RTreeTriangleMesh); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void Gu::RTreeTriangleMesh::exportExtraData(PxSerializationContext& stream) +{ + mRTree.exportExtraData(stream); + TriangleMesh::exportExtraData(stream); +} + +void Gu::RTreeTriangleMesh::importExtraData(PxDeserializationContext& context) +{ + mRTree.importExtraData(context); + TriangleMesh::importExtraData(context); +} + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +PxVec3 * Gu::RTreeTriangleMesh::getVerticesForModification() +{ + return const_cast(getVertices()); +} + +template +struct RefitCallback : Gu::RTree::CallbackRefit +{ + const PxVec3* newPositions; + const IndexType* indices; + + RefitCallback(const PxVec3* aNewPositions, const IndexType* aIndices) : newPositions(aNewPositions), indices(aIndices) {} + PX_FORCE_INLINE ~RefitCallback() {} + + virtual void recomputeBounds(PxU32 index, shdfnd::aos::Vec3V& aMn, shdfnd::aos::Vec3V& aMx) + { + using namespace shdfnd::aos; + + // Each leaf box has a set of triangles + Gu::LeafTriangles currentLeaf; currentLeaf.Data = index; + PxU32 nbTris = currentLeaf.GetNbTriangles(); + PxU32 baseTri = currentLeaf.GetTriangleIndex(); + PX_ASSERT(nbTris > 0); + const IndexType* vInds = indices + 3 * baseTri; + Vec3V vPos = V3LoadU(newPositions[vInds[0]]); + Vec3V mn = vPos, mx = vPos; + //PxBounds3 result(newPositions[vInds[0]], newPositions[vInds[0]]); + vPos = V3LoadU(newPositions[vInds[1]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + vPos = V3LoadU(newPositions[vInds[2]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + for (PxU32 i = 1; i < nbTris; i++) + { + const IndexType* vInds1 = indices + 3 * (baseTri + i); + vPos = V3LoadU(newPositions[vInds1[0]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + vPos = V3LoadU(newPositions[vInds1[1]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + vPos = V3LoadU(newPositions[vInds1[2]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + } + + aMn = mn; + aMx = mx; + } +}; + +PxBounds3 Gu::RTreeTriangleMesh::refitBVH() +{ + PxBounds3 meshBounds; + if (has16BitIndices()) + { + RefitCallback cb(mVertices, static_cast(mTriangles)); + mRTree.refitAllStaticTree(cb, &meshBounds); + } + else + { + RefitCallback cb(mVertices, static_cast(mTriangles)); + mRTree.refitAllStaticTree(cb, &meshBounds); + } + + // reset edge flags and remember we did that using a mesh flag (optimization) + if ((mRTree.mFlags & RTree::IS_EDGE_SET) == 0) + { + mRTree.mFlags |= RTree::IS_EDGE_SET; + if(mExtraTrigData) + { + const PxU32 nbTris = getNbTriangles(); + for(PxU32 i = 0; i < nbTris; i++) + mExtraTrigData[i] |= ETD_CONVEX_EDGE_ALL; + } + } + + mAABB = meshBounds; + return meshBounds; +} +#endif + +} // namespace physx diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h new file mode 100644 index 000000000..168281546 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h @@ -0,0 +1,81 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLEMESH_RTREE_H +#define GU_TRIANGLEMESH_RTREE_H + +#include "GuTriangleMesh.h" + +namespace physx +{ +class GuMeshFactory; + +namespace Gu +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +class RTreeTriangleMesh : public TriangleMesh +{ + public: + virtual const char* getConcreteTypeName() const { return "PxBVH33TriangleMesh"; } +// PX_SERIALIZATION + RTreeTriangleMesh(PxBaseFlags baseFlags) : TriangleMesh(baseFlags), mRTree(PxEmpty) {} + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& ctx); + void importExtraData(PxDeserializationContext&); + PX_PHYSX_COMMON_API static TriangleMesh* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + RTreeTriangleMesh(GuMeshFactory& factory, TriangleMeshData& data); + virtual ~RTreeTriangleMesh(){} + + virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH33; } + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + virtual PxVec3* getVerticesForModification(); + virtual PxBounds3 refitBVH(); +#endif + + PX_FORCE_INLINE const Gu::RTree& getRTree() const { return mRTree; } + private: + Gu::RTree mRTree; +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h new file mode 100644 index 000000000..e32534bff --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h @@ -0,0 +1,65 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLE_VERTEX_POINTERS_H +#define GU_TRIANGLE_VERTEX_POINTERS_H + +#include "PxTriangleMesh.h" +#include "GuTriangleMesh.h" + +namespace physx { + namespace Gu { + + // PT: TODO: replace with Gu::TriangleMesh::getLocalTriangle(...) + struct TriangleVertexPointers + { + static void PX_FORCE_INLINE getTriangleVerts(const TriangleMesh* mesh, PxU32 triangleIndex, PxVec3& v0, PxVec3& v1, PxVec3& v2) + { + const PxVec3* verts = mesh->getVerticesFast(); + if(mesh->has16BitIndices()) + { + const PxU16* tris = reinterpret_cast(mesh->getTrianglesFast()); + const PxU16* inds = tris+triangleIndex*3; + v0 = verts[inds[0]]; + v1 = verts[inds[1]]; + v2 = verts[inds[2]]; + } + else + { + const PxU32* tris = reinterpret_cast(mesh->getTrianglesFast()); + const PxU32* inds = tris+triangleIndex*3; + v0 = verts[inds[0]]; + v1 = verts[inds[1]]; + v2 = verts[inds[2]]; + } + } + }; +} } // physx, Gu + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp new file mode 100644 index 000000000..59a8faa08 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp @@ -0,0 +1,1030 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecBox.h" +#include "GuGeometryUnion.h" + +#include "GuConvexHelper.h" +#include "GuPCMShapeConvex.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGenUtil.h" + + +namespace physx +{ + +namespace Gu +{ + + static void getIncidentPolygon(Ps::aos::Vec3V* pts, Ps::aos::Vec3V& faceNormal, const Ps::aos::Vec3VArg axis, const Ps::aos::PsMatTransformV& transf1To0, + const Ps::aos::Vec3VArg extents) + { + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + FloatV ex = V3GetX(extents); + FloatV ey = V3GetY(extents); + FloatV ez = V3GetZ(extents); + + const Vec3V u0 = transf1To0.getCol0(); + const Vec3V u1 = transf1To0.getCol1(); + const Vec3V u2 = transf1To0.getCol2(); + + //calcaulte the insident face for b + const FloatV d0 = V3Dot(u0, axis); + const FloatV d1 = V3Dot(u1, axis); + const FloatV d2 = V3Dot(u2, axis); + + const FloatV absd0 = FAbs(d0); + const FloatV absd1 = FAbs(d1); + const FloatV absd2 = FAbs(d2); + + Vec3V r0, r1, r2; + + + if(FAllGrtrOrEq(absd0, absd1) && FAllGrtrOrEq(absd0, absd2)) + { + //the incident face is on u0 + const BoolV con = FIsGrtr(d0, zero); + faceNormal = V3Sel(con, V3Neg(u0), u0); + ex = FSel(con, FNeg(ex), ex); + r0 = V3Scale(u0, ex); + r1 = V3Scale(u1, ey); + r2 = V3Scale(u2, ez); + + const Vec3V temp0 = V3Add(transf1To0.p, r0); + const Vec3V temp1 = V3Add(r1, r2); + const Vec3V temp2 = V3Sub(r1, r2); + + + pts[0] = V3Add(temp0, temp1); // (-x/x, y, z) + pts[1] = V3Add(temp0, temp2); // (-x/x, y, -z) + pts[2] = V3Sub(temp0, temp1); // (-x/x, -y, -z) + pts[3] = V3Sub(temp0, temp2); // (-x/x, -y, z) + + } + else if(FAllGrtrOrEq(absd1, absd2)) + { + //the incident face is on u1 + const BoolV con = FIsGrtr(d1, zero); + faceNormal = V3Sel(con, V3Neg(u1), u1); + ey = FSel(con, FNeg(ey), ey); + r0 = V3Scale(u0, ex); + r1 = V3Scale(u1, ey); + r2 = V3Scale(u2, ez); + + const Vec3V temp0 = V3Add(transf1To0.p, r1); + const Vec3V temp1 = V3Add(r0, r2); + const Vec3V temp2 = V3Sub(r0, r2); + + pts[0] = V3Add(temp0, temp1); // (x, -y/y, z) + pts[1] = V3Add(temp0, temp2); // (x, -y/y, -z) + pts[2] = V3Sub(temp0, temp1); // (-x, -y/y, -z) + pts[3] = V3Sub(temp0, temp2); // (-x, -y/y, z) + + } + else + { + //the incident face is on u2 + const BoolV con = FIsGrtr(d2, zero); + faceNormal = V3Sel(con, V3Neg(u2), u2); + ez = FSel(con, FNeg(ez), ez); + r0 = V3Scale(u0, ex); + r1 = V3Scale(u1, ey); + r2 = V3Scale(u2, ez); + + const Vec3V temp0 = V3Add(transf1To0.p, r2); + const Vec3V temp1 = V3Add(r0, r1); + const Vec3V temp2 = V3Sub(r0, r1); + + pts[0] = V3Add(temp0, temp1); // ( x, y, z) + pts[1] = V3Add(temp0, temp2); // ( x, -y, z) + pts[2] = V3Sub(temp0, temp1); // (-x, -y, z) + pts[3] = V3Sub(temp0, temp2); // (-x, y, z) + } + } + + + //p0 and p1 is in the local space of AABB + static bool intersectSegmentAABB(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg d, const Ps::aos::Vec3VArg max, const Ps::aos::Vec3VArg min, Ps::aos::FloatV& tmin, Ps::aos::FloatV& tmax) + { + using namespace Ps::aos; + + const Vec3V eps = V3Load(1e-6f); + const Vec3V absV = V3Abs(d); + const FloatV one = FOne(); + const Vec3V zero = V3Zero(); + const Vec3V fMax = Vec3V_From_FloatV(FMax()); + + FloatV tminf = FZero(); + FloatV tmaxf = one; + const BoolV isParallel = V3IsGrtr(eps, absV); + const BoolV isOutsideOfRange = BOr(V3IsGrtr(p0, max), V3IsGrtr(min, p0)); + //const BoolV isParallelAndOutOfRange = BAnd(isParallel, isOutsideOfRange); + + if(!BAllEqFFFF(BAnd(isParallel, isOutsideOfRange))) + { + return false; + } + + const Vec3V odd = V3RecipFast(d); + const Vec3V t1 = V3Sel(isParallel, zero, V3Mul(V3Sub(min, p0), odd)); + const Vec3V t2 = V3Sel(isParallel, fMax, V3Mul(V3Sub(max, p0), odd)); + + const Vec3V tt1 = V3Min(t1, t2); + const Vec3V tt2 = V3Max(t1, t2); + + const FloatV ft1 = V3ExtractMax(tt1); + const FloatV ft2 = V3ExtractMin(tt2); + + tminf = FMax(ft1, tminf); + tmaxf = FMin(tmaxf, ft2); + + tmin = tminf; + tmax = tmaxf; + + const BoolV con0 = FIsGrtr(tminf, tmaxf); + const BoolV con1 = FIsGrtr(tminf, one); + const BoolV isNotIntersect = BOr(con0, con1); + return BAllEqFFFF(isNotIntersect) == 1; + } + + + //pts, faceNormal and contact normal are in the local space of new space + static void calculateContacts( const Ps::aos::FloatVArg extentX, const Ps::aos::FloatVArg extentY, Ps::aos::Vec3V* pts, const Ps::aos::Vec3VArg incidentFaceNormalInNew, const Ps::aos::Vec3VArg localNormal, Gu::PersistentContact* manifoldContacts, PxU32& numContacts, const Ps::aos::FloatVArg contactDist) + { + using namespace Ps::aos; + + const FloatV zero = FZero(); + const FloatV max = FMax(); + + const FloatV nExtentX = FNeg(extentX); + const FloatV nExtentY = FNeg(extentY); + + bool pPenetration[4]; + bool pArea[4]; + + Vec3V bmin = V3Splat(max); + Vec3V bmax = V3Neg(bmin); + + const Vec3V bound = V3Merge(extentX, extentY, max); + + + //get the projection point of pts + for(PxU32 i=0; i< 4; ++i) + { + bmin = V3Min(bmin, pts[i]); + bmax = V3Max(bmax, pts[i]); + const FloatV z = FNeg(V3GetZ(pts[i])); + if(FAllGrtr(contactDist, z)) + { + + pPenetration[i] = true; + + const Vec3V absPt= V3Abs(pts[i]); + const BoolV con = V3IsGrtrOrEq(bound, absPt); + if(BAllEqTTTT(con)) + { + pArea[i] = true; + + //Add the point to the manifold + manifoldContacts[numContacts].mLocalPointA = V3SetZ(pts[i], zero); //transformNewTo0.transform(localPointA); + manifoldContacts[numContacts].mLocalPointB = pts[i];//transform1ToNew.transformInv(pts[i]); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), z); + } + else + { + pArea[i] = false; + } + } + else + { + pPenetration[i] = false; + pArea[i] = false; + } + + } + + if(numContacts == 4) + return; + + //if(pPenetration[0] && pPenetration[1] && pPenetration[2] && pPenetration[3]) + { + //if(!pArea[0] || !pArea[1] || !pArea[2] || !pArea[3]) + { + const FloatV denom = V3GetZ(incidentFaceNormalInNew); + { + const Vec3V q0 = V3Merge(extentX, extentY, zero); + + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + + { + const Vec3V q0 = V3Merge(extentX, nExtentY, zero); + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + + { + const Vec3V q0 = V3Merge( nExtentX, extentY, zero); + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + + { + const Vec3V q0 = V3Merge(nExtentX, nExtentY, zero); + + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + } + } + + + + const Vec3V ext = V3Merge(extentX, extentY, max); + const Vec3V negExt = V3Merge(nExtentX, nExtentY, FNeg(FAdd(contactDist, FEps()))); + + for (PxU32 rStart = 0, rEnd = 3; rStart < 4; rEnd = rStart++) + { + const Vec3V p0 = pts[rStart]; + const Vec3V p1 = pts[rEnd]; + + if(!pPenetration[rStart] && !pPenetration[rEnd]) + continue; + + const bool con0 = pPenetration[rStart] && pArea[rStart]; + const bool con1 = pPenetration[rEnd] && pArea[rEnd]; + if(con0 && con1) + continue; + + //intersect t value with x plane + const Vec3V p0p1 = V3Sub(p1, p0); + + FloatV tmin, tmax; + if(Gu::intersectSegmentAABB(p0, p0p1, ext, negExt, tmin, tmax)) + { + if(!con0) + { + const Vec3V intersectP = V3ScaleAdd(p0p1, tmin, p0); + manifoldContacts[numContacts].mLocalPointA = V3SetZ(intersectP, zero); + manifoldContacts[numContacts].mLocalPointB = intersectP; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), FNeg(V3GetZ(intersectP))); + } + if(!con1) + { + const Vec3V intersectP = V3ScaleAdd(p0p1, tmax, p0); + manifoldContacts[numContacts].mLocalPointA = V3SetZ(intersectP, zero); + manifoldContacts[numContacts].mLocalPointB = intersectP; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), FNeg(V3GetZ(intersectP))); + } + } + } +} + + +static PxU32 doBoxBoxGenerateContacts(const Ps::aos::Vec3VArg box0Extent, const Ps::aos::Vec3VArg box1Extent, const Ps::aos::PsMatTransformV& transform0, const Ps::aos::PsMatTransformV& transform1, const Ps::aos::FloatVArg contactDist, Gu::PersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + + const FloatV ea0 = V3GetX(box0Extent); + const FloatV ea1 = V3GetY(box0Extent); + const FloatV ea2 = V3GetZ(box0Extent); + + const FloatV eb0 = V3GetX(box1Extent); + const FloatV eb1 = V3GetY(box1Extent); + const FloatV eb2 = V3GetZ(box1Extent); + + + const PsMatTransformV transform1To0 = transform0.transformInv(transform1); + const Mat33V rot0To1 =M33Trnsps(transform1To0.rot); + + const Vec3V uEps = V3Load(1e-6f); + + const FloatV zero = FZero(); + + const FloatV tx = V3GetX(transform1To0.p); + const FloatV ty = V3GetY(transform1To0.p); + const FloatV tz = V3GetZ(transform1To0.p); + const Vec3V col0 = transform1To0.getCol0(); + const Vec3V col1 = transform1To0.getCol1(); + const Vec3V col2 = transform1To0.getCol2(); + + const Vec3V abs1To0Col0 = V3Add(V3Abs(col0), uEps); + const Vec3V abs1To0Col1 = V3Add(V3Abs(col1), uEps); + const Vec3V abs1To0Col2 = V3Add(V3Abs(col2), uEps); + + const Vec3V abs0To1Col0 = V3Add(V3Abs(rot0To1.col0), uEps); + const Vec3V abs0To1Col1 = V3Add(V3Abs(rot0To1.col1), uEps); + const Vec3V abs0To1Col2 = V3Add(V3Abs(rot0To1.col2), uEps); + + + FloatV sign[6]; + FloatV overlap[6]; + + FloatV ra, rb, radiusSum; + //ua0 + { + + sign[0] = tx; + + const Vec3V vtemp3 = V3Mul(abs0To1Col0, box1Extent); + rb = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ea0, rb); + overlap[0] = FAdd(FSub(radiusSum, FAbs(sign[0])), contactDist); + if(FAllGrtr(zero, overlap[0])) + return false; + } + + //ua1 + { + sign[1] = ty; + + const Vec3V vtemp3 = V3Mul(abs0To1Col1, box1Extent); + rb = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ea1, rb); + overlap[1] = FAdd(FSub(radiusSum, FAbs(sign[1])), contactDist); + if(FAllGrtr(zero, overlap[1])) + return false; + + } + + + //ua2 + { + sign[2] = tz; + ra = ea2; + + const Vec3V vtemp3 = V3Mul(abs0To1Col2, box1Extent); + rb = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ea2, rb); + overlap[2] = FAdd(FSub(radiusSum, FAbs(sign[2])), contactDist); + if(FAllGrtr(zero, overlap[2])) + return false; + + } + + //ub0 + { + sign[3] = V3Dot(transform1To0.p, col0); + + const Vec3V vtemp3 = V3Mul(abs1To0Col0, box0Extent); + ra = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ra, eb0); + overlap[3] = FAdd(FSub(radiusSum, FAbs(sign[3])), contactDist); + if(FAllGrtr(zero, overlap[3])) + return false; + + } + + //ub1 + { + sign[4] = V3Dot(transform1To0.p, col1); + + const Vec3V vtemp3 = V3Mul(abs1To0Col1, box0Extent); + ra = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ra, eb1); + overlap[4] = FAdd(FSub(radiusSum, FAbs(sign[4])), contactDist); + if(FAllGrtr(zero, overlap[4])) + return false; + } + + //ub2 + { + sign[5] = V3Dot(transform1To0.p, col2); + + const Vec3V vtemp3 = V3Mul(abs1To0Col2, box0Extent); + ra = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ra, eb2); + overlap[5] = FAdd(FSub(radiusSum, FAbs(sign[5])), contactDist); + if(FAllGrtr(zero, overlap[5])) + return false; + } + + + //ua0 X ub0 + { + //B into A's space, ua0Xub0[0,-b3, b2] + const FloatV absSign = FAbs(FSub(FMul(V3GetY(col0), tz), FMul(V3GetZ(col0), ty))); + + //B into A's space, ua0Xub0[0,-b3, b2] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col0), ea1); + const FloatV vtemp1 = FMul(V3GetY(abs1To0Col0), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub0[0, a3, -a2] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col0), eb1); + const FloatV vtemp02 = FMul(V3GetY(abs0To1Col0), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum)) return false; + } + + //ua0 X ub1 + { + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV absSign = FAbs(FSub(FMul(V3GetY(col1), tz), FMul(V3GetZ(col1), ty))); + + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col1), ea1); + const FloatV vtemp1 = FMul(V3GetY(abs1To0Col1), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[-a3, 0, a1] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col0), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col0), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + + if(FAllGrtr(absSign, radiusSum)) return false; + + } + + //ua0 X ub2 + { + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV absSign = FAbs(FSub(FMul(V3GetY(col2), tz), FMul(V3GetZ(col2), ty))); + + + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col2), ea1); + const FloatV vtemp1 = FMul(V3GetY(abs1To0Col2), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[a2, -a1, 0] + const FloatV vtemp01 = FMul(V3GetY(abs0To1Col0), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col0), eb1); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum)) return false; + + } + + //ua1 X ub0 + { + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV absSign = FAbs(FSub(FMul(V3GetZ(col0), tx), FMul(V3GetX(col0), tz))); + + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col0), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col0), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[0, a3, -a2] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col1), eb1); + const FloatV vtemp02 = FMul(V3GetY(abs0To1Col1), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + + } + + //ua1 X ub1 + { + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV absSign = FAbs(FSub(FMul(V3GetZ(col1), tx), FMul(V3GetX(col1), tz))); + + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col1), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col1), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[-a3, 0, -a1] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col1), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col1), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua1 X ub2 + { + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV absSign=FAbs(FSub(FMul(V3GetZ(col2), tx), FMul(V3GetX(col2), tz))); + + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col2), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col2), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[a2, -a1, 0] + const FloatV vtemp01 = FMul(V3GetY(abs0To1Col1), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col1), eb1); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua2 X ub0 + { + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV absSign = FAbs(FSub(FMul(V3GetX(col0), ty), FMul(V3GetY(col0), tx))); + + + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV vtemp0 = FMul(V3GetY(abs1To0Col0), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col0), ea1); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[0, a3, -a2] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col2), eb1); + const FloatV vtemp02 = FMul(V3GetY(abs0To1Col2), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua2 X ub1 + { + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV absSign = FAbs(FSub(FMul(V3GetX(col1), ty), FMul(V3GetY(col1), tx))); + + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV vtemp0 = FMul(V3GetY(abs1To0Col1), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col1), ea1); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[-a3, 0, a1] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col2), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col2), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua2 X ub2 + { + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV absSign=FAbs(FSub(FMul(V3GetX(col2), ty), FMul(V3GetY(col2), tx))); + + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV vtemp0 = FMul(V3GetY(abs1To0Col2), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col2), ea1); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[a2, -a1, 0] + const FloatV vtemp01 = FMul(V3GetY(abs0To1Col2), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col2), eb1); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + Vec3V mtd; + + PxU32 feature = 0; + FloatV minOverlap = overlap[0]; + + for(PxU32 i=1; i<6; ++i) + { + if(FAllGrtr(minOverlap, overlap[i])) + { + minOverlap = overlap[i]; + feature = i; + } + } + + + PsMatTransformV newTransformV; + const Vec3V axis00 = transform0.getCol0(); + const Vec3V axis01 = transform0.getCol1(); + const Vec3V axis02 = transform0.getCol2(); + const Vec3V axis10 = transform1.getCol0(); + const Vec3V axis11 = transform1.getCol1(); + const Vec3V axis12 = transform1.getCol2(); + + Vec3V incidentFaceNormalInNew; + Vec3V pts[4]; + bool flip = false; + switch(feature) + { + case 0: //ua0 + { + + + if(FAllGrtrOrEq(zero, sign[0])) + { + mtd = axis00; + newTransformV.rot.col0 = V3Neg(axis02); + newTransformV.rot.col1 = axis01; + newTransformV.rot.col2 = axis00; + newTransformV.p = V3NegScaleSub(axis00, ea0, transform0.p); + } + else + { + + const Vec3V nAxis00 = V3Neg(axis00); + mtd = nAxis00; + newTransformV.rot.col0 = axis02; + newTransformV.rot.col1 = axis01; + newTransformV.rot.col2 = nAxis00; + newTransformV.p = V3ScaleAdd(axis00, ea0, transform0.p); + + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform1); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, V3Neg(localNormal), transform1ToNew, box1Extent); + + calculateContacts(ea2, ea1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + + break; + }; + case 1: //ua1 + { + + if(FAllGrtrOrEq(zero, sign[1])) + { + mtd = axis01; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = V3Neg(axis02); + newTransformV.rot.col2 = axis01; + newTransformV.p = V3NegScaleSub(axis01, ea1, transform0.p); + + } + else + { + + + const Vec3V nAxis01 = V3Neg(axis01); + mtd = nAxis01; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = axis02; + newTransformV.rot.col2 = nAxis01; + newTransformV.p = V3ScaleAdd(axis01, ea1, transform0.p); + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform1); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, V3Neg(localNormal), transform1ToNew, box1Extent); + + calculateContacts(ea0, ea2, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + break; + + }; + case 2: //ua2 + { + + if(FAllGrtrOrEq(zero, sign[2])) + { + mtd = axis02; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = axis01; + newTransformV.rot.col2 = axis02; + + newTransformV.p = V3NegScaleSub(axis02, ea2, transform0.p); + } + else + { + const Vec3V nAxis02 = V3Neg(axis02); + mtd = nAxis02; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = V3Neg(axis01); + newTransformV.rot.col2 = nAxis02; + newTransformV.p = V3ScaleAdd(axis02, ea2, transform0.p); + + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform1); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, V3Neg(localNormal), transform1ToNew, box1Extent); + + calculateContacts(ea0, ea1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + + break; + }; + case 3: //ub0 + { + + flip = true; + if(FAllGrtrOrEq(zero, sign[3])) + { + mtd = axis10; + newTransformV.rot.col0 = axis12; + newTransformV.rot.col1 = axis11; + newTransformV.rot.col2 = V3Neg(axis10); + newTransformV.p = V3ScaleAdd(axis10, eb0, transform1.p); //transform0.p - extents0.x*axis00; + } + else + { + mtd = V3Neg(axis10); + newTransformV.rot.col0 = V3Neg(axis12); + newTransformV.rot.col1 = axis11; + newTransformV.rot.col2 = axis10; + newTransformV.p =V3NegScaleSub(axis10, eb0, transform1.p);//transform0.p + extents0.x*axis00; + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform0); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, localNormal, transform1ToNew, box0Extent); + + calculateContacts(eb2, eb1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + break; + }; + case 4: //ub1; + { + flip = true; + if(FAllGrtrOrEq(zero, sign[4])) + { + mtd = axis11; + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = axis12; + newTransformV.rot.col2 = V3Neg(axis11); + + newTransformV.p = V3ScaleAdd(axis11, eb1, transform1.p); + + } + else + { + mtd = V3Neg(axis11); + + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = V3Neg(axis12); + newTransformV.rot.col2 = axis11; + newTransformV.p = V3NegScaleSub(axis11, eb1, transform1.p); //transform0.p + extents0.x*axis00; + + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform0); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, localNormal, transform1ToNew, box0Extent); + calculateContacts(eb0, eb2, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + break; + } + case 5: //ub2; + { + + flip = true; + if(FAllGrtrOrEq(zero, sign[5])) + { + mtd = axis12; + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = V3Neg(axis11); + newTransformV.rot.col2 = V3Neg(axis12); + newTransformV.p = V3ScaleAdd(axis12, eb2, transform1.p); + } + else + { + mtd = V3Neg(axis12); + + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = axis11; + newTransformV.rot.col2 = axis12; + newTransformV.p = V3NegScaleSub(axis12, eb2, transform1.p); + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform0); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, localNormal, transform1ToNew, box0Extent); + + calculateContacts(eb0, eb1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + break; + }; + default: + return false; + } + + if(flip) + { + for(PxU32 i=0; i(); + const PxBoxGeometry& shapeBox1 = shape1.get(); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + + const FloatV contactDist = FLoad(params.mContactDistance); + const Vec3V boxExtents0 = V3LoadU(shapeBox0.halfExtents); + const Vec3V boxExtents1 = V3LoadU(shapeBox1.halfExtents); + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV boxMargin0 = Gu::CalculatePCMBoxMargin(boxExtents0, toleranceLength); + const FloatV boxMargin1 = Gu::CalculatePCMBoxMargin(boxExtents1, toleranceLength); + const FloatV minMargin = FMin(boxMargin0, boxMargin1); + + const PxU32 initialContacts = manifold.mNumContacts; + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, contactDist); + + const PxU32 newContacts = manifold.mNumContacts; + + const bool bLostContacts = (newContacts != initialContacts); + + PX_UNUSED(bLostContacts); + + if(bLostContacts || manifold.invalidate_BoxConvex(curRTrans, minMargin)) + { + + manifold.setRelativeTransform(curRTrans); + + const PsMatTransformV transfV0(transf0); + const PsMatTransformV transfV1(transf1); + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + + if(doBoxBoxGenerateContacts(boxExtents0, boxExtents1, transfV0, transfV1, contactDist, manifoldContacts, numContacts)) + { + if(numContacts > 0) + { + + manifold.addBatchManifoldContacts(manifoldContacts, numContacts, toleranceLength); + const Vec3V worldNormal = V3Normalize(transfV1.rotate(Vec3V_From_Vec4V(manifold.mContactPoints[0].mLocalNormalPen))); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transfV1); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + else + { + const Vec3V zeroV = V3Zero(); + BoxV box0(zeroV, boxExtents0); + BoxV box1(zeroV, boxExtents1); + + manifold.mNumWarmStartPoints = 0; + RelativeConvex convexA(box0, aToB); + LocalConvex convexB(box1); + GjkOutput output; + + GjkStatus status = gjkPenetration, LocalConvex >(convexA, convexB, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + if(status == EPA_CONTACT) + { + + RelativeConvex convexA1(box0, aToB); + LocalConvex convexB1(box1); + + status= epaPenetration(convexA1, convexB1, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + } + + if(status == GJK_CONTACT || status == EPA_CONTACT) + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + const Vec3V localPointA = aToB.transformInv(output.closestA);//curRTrans.transformInv(closestA); + const Vec3V localPointB = output.closestB; + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + numContacts += manifold.addManifoldPoint(localPointA, localPointB, localNormalPen, replaceBreakingThreshold); + + //transform the normal back to world space + const Vec3V worldNormal = V3Normalize(transf1.rotate(output.normal)); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + + return true; + } + + } + } + } + else if(manifold.getNumContacts() > 0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + + return false; +} + +} +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp new file mode 100644 index 000000000..8e118556b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp @@ -0,0 +1,274 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "GuContactBuffer.h" + + + +#define PCM_BOX_HULL_DEBUG 0 + +namespace physx +{ + +using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationBoxConvex(const GjkConvex* relativeConvex, const GjkConvex* localConvex, const PsTransformV& transf0, const PsTransformV& transf1, + PersistentContact* manifoldContacts, ContactBuffer& contactBuffer, Gu::PersistentContactManifold& manifold, Vec3VArg normal, + const Vec3VArg closestA, const Vec3VArg closestB, const FloatVArg contactDist, const bool idtScale, const bool doOverlapTest, Cm::RenderOutput* renderOutput, + const PxReal toleranceLength) +{ + Gu::PolygonalData polyData0; + + const BoxV& box = relativeConvex->getConvex(); + const ConvexHullV& convexHull = localConvex->getConvex(); + + PxVec3 halfExtents; + V3StoreU(box.extents, halfExtents); + PCMPolygonalBox polyBox0(halfExtents); + polyBox0.getPolygonalData(&polyData0); + polyData0.mPolygonVertexRefs = gPCMBoxPolygonData; + + Gu::PolygonalData polyData1; + getPCMConvexData(convexHull, idtScale, polyData1); + + Mat33V identity = M33Identity(); + SupportLocalImpl map0(box, transf0, identity, identity, true); + + PxU8 buff1[sizeof(SupportLocalImpl)]; + + SupportLocal* map1 = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(static_cast(convexHull), transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale)) : + static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(convexHull, transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale))); + + PxU32 numContacts = 0; + if(generateFullContactManifold(polyData0, polyData1, &map0, map1, manifoldContacts, numContacts, contactDist, normal, closestA, closestB, box.getMarginF(), convexHull.getMarginF(), + doOverlapTest, renderOutput, toleranceLength)) + { + if (numContacts > 0) + { + //reduce contacts + manifold.addBatchManifoldContacts(manifoldContacts, numContacts, toleranceLength); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + } + else + { + //if doOverlapTest is true, which means GJK/EPA degenerate so we won't have any contact in the manifoldContacts array + if (!doOverlapTest) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + } + } + + return true; + } + + return false; + +} + +static bool generateOrProcessContactsBoxConvex(const GjkConvex* relativeConvex, const GjkConvex* localConvex, const PsTransformV& transf0, const PsTransformV& transf1, + const PsMatTransformV& aToB, GjkStatus status, GjkOutput& output, PersistentContactManifold& manifold, ContactBuffer& contactBuffer, + const PxU32 initialContacts, const FloatV minMargin, const FloatV contactDist, + const bool idtScale, const PxReal toleranceLength, Cm::RenderOutput* renderOutput) +{ + + if (status == GJK_NON_INTERSECT) + { + return false; + } + else + { + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + + const Vec3V localNor = manifold.mNumContacts ? manifold.getLocalNormal() : V3Zero(); + + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + //addGJKEPAContacts will increase the number of contacts in manifold. If status == EPA_CONTACT, we need to run epa algorithm and generate closest points, normal and + //pentration. If epa doesn't degenerate, we will store the contacts information in the manifold. Otherwise, we will return true to do the fallback test + const bool doOverlapTest = addGJKEPAContacts(relativeConvex, localConvex, aToB, status, manifoldContacts, replaceBreakingThreshold, FLoad(toleranceLength), output, manifold); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + //ML: after we refresh the contacts(newContacts) and generate a GJK/EPA contacts(we will store that in the manifold), if the number of contacts is still less than the original contacts, + //which means we lose too mang contacts and we should regenerate all the contacts in the current configuration + //Also, we need to look at the existing contacts, if the existing contacts has very different normal than the GJK/EPA contacts, + //which means we should throw away the existing contacts and do full contact gen + const bool fullContactGen = FAllGrtr(FLoad(0.707106781f), V3Dot(localNor, output.normal)) || (manifold.mNumContacts < initialContacts); + + if (fullContactGen || doOverlapTest) + { + return fullContactsGenerationBoxConvex(relativeConvex, localConvex, transf0, transf1, manifoldContacts, contactBuffer, + manifold, output.normal, output.closestA, output.closestB, contactDist, idtScale, doOverlapTest, renderOutput, toleranceLength); + } + else + { + const Vec3V newLocalNor = V3Add(localNor, output.normal); + const Vec3V worldNormal = V3Normalize(transf1.rotate(newLocalNor)); + //const Vec3V worldNormal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + return true; + } + } +} + +bool pcmContactBoxConvex(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + const PxBoxGeometry& shapeBox = shape0.get(); + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(shapeConvex.hullData); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const FloatV contactDist = FLoad(params.mContactDistance); + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const PxReal toleranceLength = params.mToleranceLength; + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale, toleranceLength); + const FloatV boxMargin = Gu::CalculatePCMBoxMargin(boxExtents, toleranceLength); + + +#if PCM_BOX_HULL_DEBUG + const PxVec3* verts = hullData->getHullVertices(); + + for (PxU32 i = 0; i < hullData->mNbPolygons; ++i) + { + const HullPolygonData& polygon = hullData->mPolygons[i]; + const PxU8* inds = hullData->getVertexData8() + polygon.mVRef8; + Vec3V* points = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*polygon.mNbVerts, 16)); + + for (PxU32 j = 0; j < polygon.mNbVerts; ++j) + { + points[j] = V3LoadU_SafeReadW(verts[inds[j]]); + } + + Gu::PersistentContactManifold::drawPolygon(*renderOutput, transf1, points, (PxU32)polygon.mNbVerts, 0x00ff0000); + } +#endif + + const FloatV minMargin = FMin(convexMargin, boxMargin);//FMin(boxMargin, convexMargin); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + const PxU32 initialContacts = manifold.mNumContacts; + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, contactDist); + + //After the refresh contact points, the numcontacts in the manifold will be changed + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + if(bLostContacts || manifold.invalidate_BoxConvex(curRTrans, minMargin)) + { + + manifold.setRelativeTransform(curRTrans); + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + const bool idtScale = shapeConvex.scale.isIdentity(); + Gu::ConvexHullV convexHull(hullData, V3LoadU(hullData->mCenterOfMass), vScale, vQuat, idtScale); + Gu::BoxV box(V3Zero(), boxExtents); + GjkOutput output; + + RelativeConvex relativeConvex(box, aToB); + + if(idtScale) + { + + LocalConvex localConvex(static_cast(convexHull)); + + status = gjkPenetration, LocalConvex >(relativeConvex, localConvex, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + return generateOrProcessContactsBoxConvex(&relativeConvex, &localConvex, transf0, transf1, aToB, + status, output, manifold, contactBuffer, initialContacts, + minMargin, contactDist, idtScale, toleranceLength, renderOutput); + + } + else + { + LocalConvex localConvex(convexHull); + + status = gjkPenetration, LocalConvex >(relativeConvex, localConvex, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + return generateOrProcessContactsBoxConvex(&relativeConvex, &localConvex, transf0, transf1, aToB, + status, output, manifold, contactBuffer, initialContacts, + minMargin, contactDist, idtScale, toleranceLength, renderOutput); + } + } + else if(manifold.getNumContacts()>0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + + return false; + +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp new file mode 100644 index 000000000..cb77a96d8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp @@ -0,0 +1,224 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecBox.h" +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "GuGJKPenetration.h" +#include "GuEPA.h" + + +namespace physx +{ + using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationCapsuleBox(const CapsuleV& capsule, const BoxV& box, const PxVec3 halfExtents, const PsMatTransformV& aToB, const PsTransformV& transf0, const PsTransformV& transf1, + PersistentContact* manifoldContacts, PxU32& numContacts, ContactBuffer& contactBuffer, PersistentContactManifold& manifold, Vec3V& normal, const Vec3VArg closest, + const PxReal boxMargin, const FloatVArg contactDist, const bool doOverlapTest, const PxReal toleranceScale) +{ + + PolygonalData polyData; + PCMPolygonalBox polyBox(halfExtents); + polyBox.getPolygonalData(&polyData); + + Mat33V identity = M33Identity(); + SupportLocalImpl map(box, transf1, identity, identity); + + PxU32 origContacts = numContacts; + if (generateCapsuleBoxFullContactManifold(capsule, polyData, &map, aToB, manifoldContacts, numContacts, contactDist, normal, closest, boxMargin, doOverlapTest, toleranceScale)) + { + //EPA has contacts and we have new contacts, we discard the EPA contacts + if(origContacts != 0 && numContacts != origContacts) + { + numContacts--; + manifoldContacts++; + } + + manifold.addBatchManifoldContacts2(manifoldContacts, numContacts); + + normal = transf1.rotate(normal); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsule.radius, contactDist); + + return true; + + } + + return false; + +} + + +bool pcmContactCapsuleBox(GU_CONTACT_METHOD_ARGS) +{ + + PX_UNUSED(renderOutput); + + PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + const PxCapsuleGeometry& shapeCapsule = shape0.get(); + const PxBoxGeometry& shapeBox = shape1.get(); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + + const PsTransformV curRTrans = transf1.transformInv(transf0); + const PsMatTransformV aToB_(curRTrans); + + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV capsuleHalfHeight = FLoad(shapeCapsule.halfHeight); + + const PxU32 initialContacts = manifold.mNumContacts; + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV boxMargin = Gu::CalculatePCMBoxMargin(boxExtents, toleranceLength); + + const FloatV minMargin = FMin(boxMargin, capsuleRadius); + + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + + const FloatV refreshDist = FAdd(contactDist, capsuleRadius); + //refreshContactPoints remove invalid contacts from the manifold and update the number correspondingly + manifold.refreshContactPoints(aToB_, projectBreakingThreshold, refreshDist); + + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + if(bLostContacts || manifold.invalidate_SphereCapsule(curRTrans, minMargin)) + { + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + manifold.setRelativeTransform(curRTrans); + const PsMatTransformV aToB(curRTrans); + + BoxV box(transf1.p, boxExtents); + //transform capsule into the local space of box + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + LocalConvex convexA(capsule); + LocalConvex convexB(box); + GjkOutput output; + + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), box.getCenter()); + status = gjkPenetration, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + bool doOverlapTest = false; + if(status == GJK_NON_INTERSECT) + { + return false; + } + else if(status == GJK_DEGENERATE) + { + return fullContactsGenerationCapsuleBox(capsule, box, shapeBox.halfExtents, aToB, transf0, transf1, manifoldContacts, numContacts, contactBuffer, + manifold, output.normal, output.closestB, box.getMarginF(), contactDist, true, params.mToleranceLength); + } + else + { + if(status == GJK_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(output.closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + //Add contact to contact stream + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = output.closestB; + manifoldContacts[numContacts++].mLocalNormalPen = localNormalPen; + } + else + { + PX_ASSERT(status == EPA_CONTACT); + + status= epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + if(status == EPA_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(output.closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + //Add contact to contact stream + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = output.closestB; + manifoldContacts[numContacts++].mLocalNormalPen = localNormalPen; + + } + else + { + doOverlapTest = true; + } + + } + + + if(initialContacts == 0 || bLostContacts || doOverlapTest) + { + return fullContactsGenerationCapsuleBox(capsule, box, shapeBox.halfExtents, aToB, transf0, transf1, manifoldContacts, numContacts, contactBuffer, manifold, output.normal, + output.closestB, box.getMarginF(), contactDist, doOverlapTest, params.mToleranceLength); + } + else + { + + //The contacts is either come from GJK or EPA + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.1f)); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + manifold.addManifoldPoint2(curRTrans.transformInv(output.closestA), output.closestB, localNormalPen, replaceBreakingThreshold); + + const Vec3V normal = transf1.rotate(output.normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsuleRadius, contactDist); + + return true; + } + } + } + else if(manifold.getNumContacts() > 0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, worldNormal, transf0, capsuleRadius, contactDist); + return true; + } + + return false; + +} +} +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp new file mode 100644 index 000000000..708c2cfb8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp @@ -0,0 +1,297 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuDistanceSegmentSegmentSIMD.h" + +using namespace physx; +using namespace Gu; +using namespace Ps; +using namespace aos; + +static Vec4V pcmDistancePointSegmentTValue22( const Vec3VArg a0, const Vec3VArg b0, + const Vec3VArg a1, const Vec3VArg b1, + const Vec3VArg p0, const Vec3VArg p1, + const Vec3VArg p2, const Vec3VArg p3) +{ + const Vec4V zero = V4Zero(); + const Vec3V ap00 = V3Sub(p0, a0); + const Vec3V ap10 = V3Sub(p1, a0); + const Vec3V ap01 = V3Sub(p2, a1); + const Vec3V ap11 = V3Sub(p3, a1); + + const Vec3V ab0 = V3Sub(b0, a0); + const Vec3V ab1 = V3Sub(b1, a1); + +/* const FloatV nom00 = V3Dot(ap00, ab0); + const FloatV nom10 = V3Dot(ap10, ab0); + const FloatV nom01 = V3Dot(ap01, ab1); + const FloatV nom11 = V3Dot(ap11, ab1);*/ + + const Vec4V combinedDot = V3Dot4(ap00, ab0, ap10, ab0, ap01, ab1, ap11, ab1); + const FloatV nom00 = V4GetX(combinedDot); + const FloatV nom10 = V4GetY(combinedDot); + const FloatV nom01 = V4GetZ(combinedDot); + const FloatV nom11 = V4GetW(combinedDot); + + const FloatV denom0 = V3Dot(ab0, ab0); + const FloatV denom1 = V3Dot(ab1, ab1); + + const Vec4V nom = V4Merge(nom00, nom10, nom01, nom11); + const Vec4V denom = V4Merge(denom0, denom0, denom1, denom1); + + const Vec4V tValue = V4Div(nom, denom); + return V4Sel(V4IsEq(denom, zero), zero, tValue); +} + +namespace physx +{ +namespace Gu +{ + + static void storeContact(const Vec3VArg contact, const Vec3VArg normal, const FloatVArg separation, Gu::ContactBuffer& buffer) + { + Gu::ContactPoint& point = buffer.contacts[buffer.count++]; + + const Vec4V normalSep = Ps::aos::V4SetW(Vec4V_From_Vec3V(normal), separation); + + V4StoreA(normalSep, &point.normal.x); + V3StoreA(contact, point.point); + point.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + } + +bool pcmContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + // Get actual shape data + const PxCapsuleGeometry& shapeCapsule0 = shape0.get(); + const PxCapsuleGeometry& shapeCapsule1 = shape1.get(); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V _p0 = V3LoadA(&transform0.p.x); + const QuatV q0 = QuatVLoadA(&transform0.q.x); + + const Vec3V _p1 = V3LoadA(&transform1.p.x); + const QuatV q1 = QuatVLoadA(&transform1.q.x); + + /*PsTransformV transf0(p0, q0); + PsTransformV transf1(p1, q1);*/ + + + const FloatV r0 = FLoad(shapeCapsule0.radius); + const FloatV halfHeight0 = FLoad(shapeCapsule0.halfHeight); + + const FloatV r1 = FLoad(shapeCapsule1.radius); + const FloatV halfHeight1 = FLoad(shapeCapsule1.halfHeight); + + const FloatV cDist = FLoad(params.mContactDistance); + + const Vec3V positionOffset = V3Scale(V3Add(_p0, _p1), FHalf()); + const Vec3V p0 = V3Sub(_p0, positionOffset); + const Vec3V p1 = V3Sub(_p1, positionOffset); + + const FloatV zero = FZero(); + //const FloatV one = FOne(); + const Vec3V zeroV = V3Zero(); + + + /*const Vec3V positionOffset = V3Scale(V3Add(transf0.p, transf1.p), FloatV_From_F32(0.5f)); + transf0.p = V3Sub(transf0.p, positionOffset); + transf1.p = V3Sub(transf1.p, positionOffset);*/ + + const Vec3V basisVector0 = QuatGetBasisVector0(q0); + const Vec3V tmp0 = V3Scale(basisVector0, halfHeight0); + const Vec3V s0 = V3Add(p0, tmp0); + const Vec3V e0 = V3Sub(p0, tmp0); + const Vec3V d0 = V3Sub(e0, s0); + + const Vec3V basisVector1 = QuatGetBasisVector0(q1); + const Vec3V tmp1 = V3Scale(basisVector1, halfHeight1); + const Vec3V s1 = V3Add(p1, tmp1); + const Vec3V e1 = V3Sub(p1, tmp1); + + const Vec3V d1 = V3Sub(e1, s1); + + const FloatV sumRadius = FAdd(r0, r1); + const FloatV inflatedSum = FAdd(sumRadius, cDist); + const FloatV inflatedSumSquared = FMul(inflatedSum, inflatedSum); + const FloatV a = V3Dot(d0, d0);//squared length of segment1 + const FloatV e = V3Dot(d1, d1);//squared length of segment2 + const FloatV eps = FLoad(1e-6f);//FEps(); + + FloatV t0, t1; + const FloatV sqDist0 = distanceSegmentSegmentSquared(s0, d0, s1, d1, t0, t1); + + if(FAllGrtrOrEq(inflatedSumSquared, sqDist0)) + { + const Vec4V zeroV4 = V4Zero(); + const Vec4V oneV4 = V4One(); + //check to see whether these two capsule are paralle + const FloatV parallelTolerance = FLoad(0.9998f); + + + const BoolV con0 = FIsGrtr(eps, a); + const BoolV con1 = FIsGrtr(eps, e); + const Vec3V dir0 = V3Sel(con0, zeroV, V3ScaleInv(d0, FSqrt(a))); + const Vec3V dir1 = V3Sel(con1, zeroV, V3ScaleInv(d1, FSqrt(e))); + + const FloatV cos = FAbs(V3Dot(dir0, dir1)); + if(FAllGrtr(cos, parallelTolerance))//paralle + { + //project s, e into s1e1 + const Vec4V t= pcmDistancePointSegmentTValue22(s0, e0, s1, e1, + s1, e1, s0, e0); + + const BoolV con = BAnd(V4IsGrtrOrEq(t, zeroV4), V4IsGrtrOrEq(oneV4, t)); + const BoolV con00 = BGetX(con); + const BoolV con01 = BGetY(con); + const BoolV con10 = BGetZ(con); + const BoolV con11 = BGetW(con); + + /* PX_ALIGN(16, PxU32 conditions[4]); + F32Array_Aligned_From_Vec4V(con, (PxF32*)conditions);*/ + + + PxU32 numContact=0; + + if(BAllEqTTTT(con00)) + { + const Vec3V projS1 = V3ScaleAdd(d0, V4GetX(t), s0); + const Vec3V v = V3Sub(projS1, s1); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, projS1); + const Vec3V p = V3Add(_p, positionOffset); + + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + if(BAllEqTTTT(con01)) + { + const Vec3V projE1 = V3ScaleAdd(d0, V4GetY(t), s0); + const Vec3V v = V3Sub(projE1, e1); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, projE1); + const Vec3V p = V3Add(_p, positionOffset); + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + + if(BAllEqTTTT(con10)) + { + const Vec3V projS0 = V3ScaleAdd(d1, V4GetZ(t), s1); + const Vec3V v = V3Sub(s0, projS0); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, s0); + const Vec3V p = V3Add(_p, positionOffset); + //const Vec3V p = V3ScaleAdd(normal, r0, s0); + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + + if(BAllEqTTTT(con11)) + { + const Vec3V projE0 = V3ScaleAdd(d1, V4GetW(t), s1); + const Vec3V v = V3Sub(e0, projE0); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, e0); + const Vec3V p = V3Add(_p, positionOffset); + //const Vec3V p = V3ScaleAdd(normal, r0, e0); + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + + if(numContact) + return true; + + } + + const Vec3V closestA = V3ScaleAdd(d0, t0, s0); + const Vec3V closestB = V3ScaleAdd(d1, t1, s1); + + const BoolV con = FIsGrtr(eps, sqDist0); + //const Vec3V normal = V3Sel(FIsEq(dist, zero), V3Sel(FIsGrtr(a, eps), V3Normalise(d0), V3Scale(V3Sub(closestA, closestB), FRecip(dist))); + const Vec3V _normal = V3Sel(con, V3Sel(FIsGrtr(a, eps), d0, V3UnitX()), V3Sub(closestA, closestB)); + const Vec3V normal = V3Normalize(_normal); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _point = V3NegScaleSub(normal, r0, closestA); + const Vec3V p = V3Add(_point, positionOffset); + const FloatV dist = FSel(con, zero, FSqrt(sqDist0)); + const FloatV pen = FSub(dist, sumRadius); + //PX_ASSERT(FAllGrtrOrEq(zero, pen)); + storeContact(p, normal, pen, contactBuffer); + return true; + } + return false; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp new file mode 100644 index 000000000..d7a637c1f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp @@ -0,0 +1,266 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" + +namespace physx +{ + +using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationCapsuleConvex(const CapsuleV& capsule, const ConvexHullV& convexHull, const PsMatTransformV& aToB, const PsTransformV& transf0,const PsTransformV& transf1, + PersistentContact* manifoldContacts, ContactBuffer& contactBuffer, const bool idtScale, PersistentContactManifold& manifold, Vec3VArg normal, + const Vec3VArg closest, const PxReal tolerance, const FloatVArg contactDist, const bool doOverlapTest, Cm::RenderOutput* renderOutput, const PxReal toleranceLength) +{ + + PX_UNUSED(renderOutput); + Gu::PolygonalData polyData; + getPCMConvexData(convexHull,idtScale, polyData); + + PxU8 buff[sizeof(SupportLocalImpl)]; + SupportLocal* map = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(static_cast(convexHull), transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale)) : + static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(convexHull, transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale))); + + PxU32 numContacts = 0; + if (generateFullContactManifold(capsule, polyData, map, aToB, manifoldContacts, numContacts, contactDist, normal, closest, tolerance, doOverlapTest, toleranceLength)) + { + + if (numContacts > 0) + { + manifold.addBatchManifoldContacts2(manifoldContacts, numContacts); + //transform normal into the world space + normal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsule.radius, contactDist); + } + else + { + if (!doOverlapTest) + { + normal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsule.radius, contactDist); + } + } + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + + } + return false; + +} + +bool pcmContactCapsuleConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + const PxCapsuleGeometry& shapeCapsule = shape0.get(); + + PersistentContactManifold& manifold = cache.getManifold(); + + Ps::prefetchLine(shapeConvex.hullData); + + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + + const FloatV contactDist = FLoad(params.mContactDistance); + const FloatV capsuleHalfHeight = FLoad(shapeCapsule.halfHeight); + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const ConvexHullData* hullData =shapeConvex.hullData; + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const PxReal toleranceLength = params.mToleranceLength ; + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale, toleranceLength); + const FloatV capsuleMinMargin = Gu::CalculateCapsuleMinMargin(capsuleRadius); + const FloatV minMargin = FMin(convexMargin, capsuleMinMargin); + + const PxU32 initialContacts = manifold.mNumContacts; + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(1.25f)); + const FloatV refreshDist = FAdd(contactDist, capsuleRadius); + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, refreshDist); + + //ML: after refreshContactPoints, we might lose some contacts + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + PX_UNUSED(bLostContacts); + if(bLostContacts || manifold.invalidate_SphereCapsule(curRTrans, minMargin)) + { + const bool idtScale = shapeConvex.scale.isIdentity(); + + manifold.setRelativeTransform(curRTrans); + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + ConvexHullV convexHull(hullData, V3LoadU(hullData->mCenterOfMass), vScale, vQuat, idtScale); + + //transform capsule(a) into the local space of convexHull(b) + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + GjkOutput output; + LocalConvex convexA(capsule); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(idtScale) + { + LocalConvex convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + + status = gjkPenetration, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + } + else + { + LocalConvex convexB(convexHull); + status = gjkPenetration, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + } + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + bool doOverlapTest = false; + if(status == GJK_NON_INTERSECT) + { + return false; + } + else if(status == GJK_DEGENERATE) + { + return fullContactsGenerationCapsuleConvex(capsule, convexHull, aToB, transf0, transf1, manifoldContacts, contactBuffer, idtScale, manifold, output.normal, + output.closestB, convexHull.getMarginF(), contactDist, true, renderOutput, toleranceLength); + } + else + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + if(status == GJK_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(output.closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = output.closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint2(localPointA, output.closestB, localNormalPen, replaceBreakingThreshold); + } + else + { + PX_ASSERT(status == EPA_CONTACT); + + if(idtScale) + { + LocalConvex convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + } + else + { + LocalConvex convexB(convexHull); + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + } + + + if(status == EPA_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(output.closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = output.closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint2(localPointA, output.closestB, localNormalPen, replaceBreakingThreshold); + + + } + else + { + doOverlapTest = true; + } + } + + + if(initialContacts == 0 || bLostContacts || doOverlapTest) + { + return fullContactsGenerationCapsuleConvex(capsule, convexHull, aToB, transf0, transf1, manifoldContacts, contactBuffer, idtScale, manifold, output.normal, + output.closestB, convexHull.getMarginF(), contactDist, doOverlapTest, renderOutput, toleranceLength); + } + else + { + //This contact is either come from GJK or EPA + const Vec3V normal = transf1.rotate(output.normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsuleRadius, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + } + } + else if (manifold.getNumContacts() > 0) + { + const Vec3V normal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsuleRadius, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + return false; +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp new file mode 100644 index 000000000..b8b4d6a6b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp @@ -0,0 +1,158 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsVecMath.h" +#include "PsVecTransform.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuHeightField.h" +#include "GuPCMContactConvexCommon.h" +#include "GuSegment.h" +#include "GuInternal.h" +#include "GuPCMContactMeshCallback.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +namespace physx +{ + +struct PCMCapsuleVsHeightfieldContactGenerationCallback : PCMHeightfieldContactGenerationCallback +{ + PCMCapsuleVsHeightfieldContactGenerationCallback& operator=(const PCMCapsuleVsHeightfieldContactGenerationCallback&); + +public: + PCMCapsuleVsMeshContactGeneration mGeneration; + + PCMCapsuleVsHeightfieldContactGenerationCallback( + const Gu::CapsuleV& capsule, + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + + const PsTransformV& capsuleTransform, + const PsTransformV& heightfieldTransform, + const PxTransform& heightfieldTransform1, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Ps::InlineArray* deferredContacts, + Gu::HeightFieldUtil& hfUtil + + + ) : + PCMHeightfieldContactGenerationCallback(hfUtil, heightfieldTransform1), + mGeneration(capsule, contactDistance, replaceBreakingThreshold, capsuleTransform, heightfieldTransform, multiManifold, + contactBuffer, deferredContacts) + { + } + + template + void processTriangleCache(Gu::TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + +bool Gu::pcmContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get(); + const PxHeightFieldGeometryLL& shapeHeight = shape1.get(); + + Gu::MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV capsuleTransform = loadTransformA(transform0);//capsule transform + const PsTransformV heightfieldTransform = loadTransformA(transform1);//height feild + + const PsTransformV curTransform = heightfieldTransform.transformInv(capsuleTransform); + + const FloatV replaceBreakingThreshold = FMul(capsuleRadius, FLoad(0.001f)); + + if(multiManifold.invalidate(curTransform, capsuleRadius, FLoad(0.02f))) + { + + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + Gu::HeightFieldUtil hfUtil(shapeHeight); + + const PxVec3 tmp = getCapsuleHalfHeightVector(transform0, shapeCapsule); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + const PxVec3 capsuleCenterInMesh = transform1.transformInv(transform0.p); + const PxVec3 capsuleDirInMesh = transform1.rotateInv(tmp); + const Gu::CapsuleV capsule(V3LoadU(capsuleCenterInMesh), V3LoadU(capsuleDirInMesh), capsuleRadius); + + PCMCapsuleVsHeightfieldContactGenerationCallback callback( + capsule, + contactDist, + replaceBreakingThreshold, + capsuleTransform, + heightfieldTransform, + transform1, + multiManifold, + contactBuffer, + NULL, + hfUtil + ); + + PxBounds3 bounds; + bounds.maximum = PxVec3(shapeCapsule.halfHeight + inflatedRadius, inflatedRadius, inflatedRadius); + bounds.minimum = -bounds.maximum; + + bounds = PxBounds3::transformFast(transform1.transformInv(transform0), bounds); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &callback); + + callback.mGeneration.processContacts(GU_CAPSULE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + // We must be in local space to use the cache + const FloatV projectBreakingThreshold = FMul(capsuleRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(capsuleRadius, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + + } + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, capsuleTransform, heightfieldTransform, capsuleRadius); +} + + +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp new file mode 100644 index 000000000..57bcf18df --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp @@ -0,0 +1,185 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecTriangle.h" +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" +#include "PsVecMath.h" +#include "PsVecTransform.h" + +#include "GuContactMethodImpl.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuPCMContactConvexCommon.h" +#include "GuSegment.h" +#include "GuVecCapsule.h" +#include "GuInternal.h" +#include "GuPCMContactMeshCallback.h" +#include "GuConvexEdgeFlags.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMCapsuleVsMeshContactGenerationCallback : PCMMeshContactGenerationCallback< PCMCapsuleVsMeshContactGenerationCallback > +{ + PCMCapsuleVsMeshContactGenerationCallback& operator=(const PCMCapsuleVsMeshContactGenerationCallback&); +public: + PCMCapsuleVsMeshContactGeneration mGeneration; + + PCMCapsuleVsMeshContactGenerationCallback( + const CapsuleV& capsule, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const PsTransformV& sphereTransform, + const PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + const PxU8* extraTriData, + const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtMeshScale, + Ps::InlineArray* deferredContacts, + Cm::RenderOutput* renderOutput = NULL + ) : + PCMMeshContactGenerationCallback(meshScaling, extraTriData, idtMeshScale), + mGeneration(capsule, contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, contactBuffer, + deferredContacts, renderOutput) + { + } + + PX_FORCE_INLINE bool doTest(const PxVec3&, const PxVec3&, const PxVec3&) { return true; } + + template + void processTriangleCache(TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + +bool Gu::pcmContactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + const PxCapsuleGeometry& shapeCapsule= shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + //gRenderOutPut = cache.mRenderOutput; + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV capsuleTransform = loadTransformA(transform0);//capsule transform + const PsTransformV meshTransform = loadTransformA(transform1);//triangleMesh + + const PsTransformV curTransform = meshTransform.transformInv(capsuleTransform); + + // We must be in local space to use the cache + if(multiManifold.invalidate(curTransform, capsuleRadius, FLoad(0.02f))) + { + const FloatV replaceBreakingThreshold = FMul(capsuleRadius, FLoad(0.001f)); + //const FloatV capsuleHalfHeight = FloatV_From_F32(shapeCapsule.halfHeight); + Cm::FastVertex2ShapeScaling meshScaling; + const bool idtMeshScale = shapeMesh.scale.isIdentity(); + if(!idtMeshScale) + meshScaling.init(shapeMesh.scale); + + // Capsule data + const PxVec3 tmp = getCapsuleHalfHeightVector(transform0, shapeCapsule); + Segment worldCapsule; + worldCapsule.p0 = transform0.p + tmp; + worldCapsule.p1 = transform0.p - tmp; + + + const Segment meshCapsule( // Capsule in mesh space + transform1.transformInv(worldCapsule.p0), + transform1.transformInv(worldCapsule.p1)); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + const PxVec3 capsuleCenterInMesh = transform1.transformInv(transform0.p); + const PxVec3 capsuleDirInMesh = transform1.rotateInv(tmp); + const CapsuleV capsule(V3LoadU(capsuleCenterInMesh), V3LoadU(capsuleDirInMesh), capsuleRadius); + + // We must be in local space to use the cache + const Capsule queryCapsule(meshCapsule, inflatedRadius); + + const TriangleMesh* meshData = shapeMesh.meshData; + + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + const PxU8* PX_RESTRICT extraData = meshData->getExtraTrigData(); + // mesh scale is not baked into cached verts + PCMCapsuleVsMeshContactGenerationCallback callback( + capsule, + contactDist, + replaceBreakingThreshold, + capsuleTransform, + meshTransform, + multiManifold, + contactBuffer, + extraData, + meshScaling, + idtMeshScale, + NULL, + renderOutput); + + //bound the capsule in shape space by an OBB: + Box queryBox; + queryBox.create(queryCapsule); + + //apply the skew transform to the box: + if(!idtMeshScale) + meshScaling.transformQueryBounds(queryBox.center, queryBox.extents, queryBox.rot); + + Midphase::intersectOBB(meshData, queryBox, callback, true); + + callback.flushCache(); + + callback.mGeneration.processContacts(GU_CAPSULE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(capsuleRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(capsuleRadius, contactDist); + //multiManifold.refreshManifold(aToB, projectBreakingThreshold, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + } + + //multiManifold.drawManifold(*gRenderOutPut, capsuleTransform, meshTransform); + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, capsuleTransform, meshTransform, capsuleRadius); +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp new file mode 100644 index 000000000..be2692f68 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp @@ -0,0 +1,1354 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecTriangle.h" +#include "GuPCMContactConvexCommon.h" +#include "GuConvexEdgeFlags.h" +#include "GuBarycentricCoordinates.h" +#include "PsSort.h" + +namespace physx +{ + +namespace Gu +{ +/* + This function adds the newly created manifold contacts to a new patch or existing patches +*/ +void PCMConvexVsMeshContactGeneration::addContactsToPatch(const Ps::aos::Vec3VArg patchNormal, const PxU32 previousNumContacts) +{ + using namespace Ps::aos; + + const Vec3V patchNormalInTriangle = mMeshToConvex.rotateInv(patchNormal); + + + const PxU32 newContacts = mNumContacts - previousNumContacts; + + if(newContacts > GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE) + { + //if the current created manifold contacts are more than GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points, we will reduce the total numContacts + //to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE. However, after we add these points into a patch, the patch contacts will be variable. Then we will + //do contact reduction for that patch in the processContacts. After the contact reduction, there will be no more than GU_SINGLE_MANIFOLD_CACHE_SIZE(6) + //contacts inside a signlePersistentContactManifold + Gu::SinglePersistentContactManifold::reduceContacts(&mManifoldContacts[previousNumContacts], newContacts); + mNumContacts = previousNumContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + + //get rid of duplicate manifold contacts for the newly created contacts + for(PxU32 i = previousNumContacts; i= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + PX_ASSERT(mNumContacts <= ContactBuffer::MAX_CONTACTS); + processContacts(GU_SINGLE_MANIFOLD_CACHE_SIZE); + } +} + + +void PCMConvexVsMeshContactGeneration::generateLastContacts() +{ + using namespace Ps::aos; + // Process delayed contacts + PxU32 nbEntries = mDeferredContacts->size(); + + if(nbEntries) + { + nbEntries /= sizeof(PCMDeferredPolyData)/sizeof(PxU32); + + const PCMDeferredPolyData* PX_RESTRICT cd = reinterpret_cast(mDeferredContacts->begin()); + for(PxU32 i=0;ipreviousNumContacts; --j) + { + PxU32 ind = j-1; + //calculate the barycentric coordinate of the contacts in localTriangle, p = a + v(b-a) + w(c-a)., p=ua+vb+wc + barycentricCoordinates(mManifoldContacts[ind].mLocalPointB, localTriangle.verts[0], localTriangle.verts[1], localTriangle.verts[2], v, w); + //const FloatV u = FSub(one, FAdd(v, w)); + + bool keepContact = true; + if(FAllGrtr(v, upperBound))//v > upperBound + { + //vertex1 + keepContact = !mVertexCache.contains(Gu::CachedVertex(ref1)); + } + else if(FAllGrtr(w, upperBound))// w > upperBound + { + //vertex2 + keepContact = !mVertexCache.contains(Gu::CachedVertex(ref2)); + } + else if(FAllGrtrOrEq(lowerBound, FAdd(v, w))) // u(1-(v+w)) > upperBound + { + //vertex0 + keepContact = !mVertexCache.contains(Gu::CachedVertex(ref0)); + } + + if(!keepContact) + { + //ML: if feature code is any of the vertex in this triangle and we have generated contacts with any other triangles which contains this vertex, we should drop it + currentContacts--; + + for(PxU32 k = ind; k < currentContacts; ++k) + { + mManifoldContacts[k] = mManifoldContacts[k+1]; + } + } + } + + mNumContacts = currentContacts; + + if(currentContacts > previousNumContacts) + { + addContactsToPatch(patchNormal, previousNumContacts); + } + + } + } + } + +} + +bool PCMConvexVsMeshContactGeneration::processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds) +{ + using namespace Ps::aos; + + + const Mat33V identity = M33Identity(); + const FloatV zero = FZero(); + + const Vec3V v0 = V3LoadU(verts[0]); + const Vec3V v1 = V3LoadU(verts[1]); + const Vec3V v2 = V3LoadU(verts[2]); + + + + const Vec3V v10 = V3Sub(v1, v0); + const Vec3V v20 = V3Sub(v2, v0); + + const Vec3V n = V3Normalize(V3Cross(v10, v20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(v0, n);//d = -p0.dot(n); + + const FloatV dist = FSub(V3Dot(mHullCenterMesh, n), d);//p.dot(n) + d; + + // Backface culling + if(FAllGrtr(zero, dist)) + return false; + + + //tranform verts into the box local space + const Vec3V locV0 = mMeshToConvex.transform(v0); + const Vec3V locV1 = mMeshToConvex.transform(v1); + const Vec3V locV2 = mMeshToConvex.transform(v2); + + Gu::TriangleV localTriangle(locV0, locV1, locV2); + + { + + SupportLocalImpl localTriMap(localTriangle, mConvexTransform, identity, identity, true); + + const PxU32 previousNumContacts = mNumContacts; + Vec3V patchNormal; + + generateTriangleFullContactManifold(localTriangle, triangleIndex, vertInds, triFlags, mPolyData, &localTriMap, mPolyMap, mManifoldContacts, mNumContacts, mContactDist, patchNormal); + + if(mNumContacts > previousNumContacts) + { +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawTriangle(*mRenderOutput, mMeshTransform.transform(v0), mMeshTransform.transform(v1), mMeshTransform.transform(v2), 0x00ff00); +#endif + + bool inActiveEdge0 = (triFlags & ETD_CONVEX_EDGE_01) == 0; + bool inActiveEdge1 = (triFlags & ETD_CONVEX_EDGE_12) == 0; + bool inActiveEdge2 = (triFlags & ETD_CONVEX_EDGE_20) == 0; + + if(inActiveEdge0) + mEdgeCache.addData(CachedEdge(vertInds[0], vertInds[1])); + if(inActiveEdge1) + mEdgeCache.addData(CachedEdge(vertInds[1], vertInds[2])); + if(inActiveEdge2) + mEdgeCache.addData(CachedEdge(vertInds[2], vertInds[0])); + + mVertexCache.addData(CachedVertex(vertInds[0])); + mVertexCache.addData(CachedVertex(vertInds[1])); + mVertexCache.addData(CachedVertex(vertInds[2])); + + addContactsToPatch(patchNormal, previousNumContacts); + } + } + + + + return true; +} + + +bool PCMConvexVsMeshContactGeneration::processTriangle(const Gu::PolygonalData& polyData, SupportLocal* polyMap, const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags,const Ps::aos::FloatVArg inflation, const bool isDoubleSided, + const Ps::aos::PsTransformV& convexTransform, const Ps::aos::PsMatTransformV& meshToConvex, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + + + const Mat33V identity = M33Identity(); + const FloatV zero = FZero(); + + const Vec3V v0 = V3LoadU(verts[0]); + const Vec3V v1 = V3LoadU(verts[1]); + const Vec3V v2 = V3LoadU(verts[2]); + + //tranform verts into the box local space + const Vec3V locV0 = meshToConvex.transform(v0); + const Vec3V locV1 = meshToConvex.transform(v1); + const Vec3V locV2 = meshToConvex.transform(v2); + + const Vec3V v10 = V3Sub(locV1, locV0); + const Vec3V v20 = V3Sub(locV2, locV0); + + const Vec3V n = V3Normalize(V3Cross(v10, v20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(locV0, n);//d = -p0.dot(n); + + const FloatV dist = FSub(V3Dot(polyMap->shapeSpaceCenterOfMass, n), d);//p.dot(n) + d; + + // Backface culling + const bool culled = !isDoubleSided && (FAllGrtr(zero, dist)); + if(culled) + return false; + + + Gu::TriangleV localTriangle(locV0, locV1, locV2); + + SupportLocalImpl localTriMap(localTriangle, convexTransform, identity, identity, true); + + Vec3V patchNormal; + + generateTriangleFullContactManifold(localTriangle, triangleIndex, triFlags, polyData, &localTriMap, polyMap, manifoldContacts, numContacts, inflation, patchNormal); + + return true; +} + +PX_FORCE_INLINE Ps::aos::Vec4V pcmDistanceSegmentSegmentSquared4( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg d0, + const Ps::aos::Vec3VArg p02, const Ps::aos::Vec3VArg d02, + const Ps::aos::Vec3VArg p12, const Ps::aos::Vec3VArg d12, + const Ps::aos::Vec3VArg p22, const Ps::aos::Vec3VArg d22, + const Ps::aos::Vec3VArg p32, const Ps::aos::Vec3VArg d32, + Ps::aos::Vec4V& s, Ps::aos::Vec4V& t) +{ + using namespace Ps::aos; + const Vec4V zero = V4Zero(); + const Vec4V one = V4One(); + const Vec4V eps = V4Eps(); + const Vec4V half = V4Splat(FHalf()); + + const Vec4V d0X = V4Splat(V3GetX(d0)); + const Vec4V d0Y = V4Splat(V3GetY(d0)); + const Vec4V d0Z = V4Splat(V3GetZ(d0)); + const Vec4V pX = V4Splat(V3GetX(p)); + const Vec4V pY = V4Splat(V3GetY(p)); + const Vec4V pZ = V4Splat(V3GetZ(p)); + + Vec4V d024 = Vec4V_From_Vec3V(d02); + Vec4V d124 = Vec4V_From_Vec3V(d12); + Vec4V d224 = Vec4V_From_Vec3V(d22); + Vec4V d324 = Vec4V_From_Vec3V(d32); + + Vec4V p024 = Vec4V_From_Vec3V(p02); + Vec4V p124 = Vec4V_From_Vec3V(p12); + Vec4V p224 = Vec4V_From_Vec3V(p22); + Vec4V p324 = Vec4V_From_Vec3V(p32); + + Vec4V d0123X, d0123Y, d0123Z; + Vec4V p0123X, p0123Y, p0123Z; + + PX_TRANSPOSE_44_34(d024, d124, d224, d324, d0123X, d0123Y, d0123Z); + PX_TRANSPOSE_44_34(p024, p124, p224, p324, p0123X, p0123Y, p0123Z); + + const Vec4V rX = V4Sub(pX, p0123X); + const Vec4V rY = V4Sub(pY, p0123Y); + const Vec4V rZ = V4Sub(pZ, p0123Z); + + //TODO - store this in a transposed state and avoid so many dot products? + + const FloatV dd = V3Dot(d0, d0); + + const Vec4V e = V4MulAdd(d0123Z, d0123Z, V4MulAdd(d0123X, d0123X, V4Mul(d0123Y, d0123Y))); + const Vec4V b = V4MulAdd(d0Z, d0123Z, V4MulAdd(d0X, d0123X, V4Mul(d0Y, d0123Y))); + const Vec4V c = V4MulAdd(d0Z, rZ, V4MulAdd(d0X, rX, V4Mul(d0Y, rY))); + const Vec4V f = V4MulAdd(d0123Z, rZ, V4MulAdd(d0123X, rX, V4Mul(d0123Y, rY))); + + const Vec4V a(V4Splat(dd)); + + const Vec4V aRecip(V4Recip(a)); + const Vec4V eRecip(V4Recip(e)); + + //if segments not parallell, compute closest point on two segments and clamp to segment1 + const Vec4V denom = V4Sub(V4Mul(a, e), V4Mul(b, b)); + const Vec4V temp = V4Sub(V4Mul(b, f), V4Mul(c, e)); + //const Vec4V s0 = V4Clamp(V4Div(temp, denom), zero, one); + //In PS3, 0(temp)/0(denom) will produce QNaN and V4Clamp can't clamp the value to zero and one. In PC, 0/0 will produce inf and V4Clamp clamp the value to be one. + //Therefore, we need to add the select code to protect against this case + const Vec4V value = V4Sel(V4IsEq(denom, zero), one, V4Div(temp, denom)); + const Vec4V s0 = V4Clamp(value, zero, one); + + //test whether segments are parallel + const BoolV con2 = V4IsGrtrOrEq(eps, denom); + const Vec4V sTmp = V4Sel(con2, half, s0); + + //compute point on segment2 closest to segment1 + //const Vec4V tTmp = V4Mul(V4Add(V4Mul(b, sTmp), f), eRecip); + const Vec4V tTmp = V4Sel(V4IsEq(e, zero), one, V4Mul(V4Add(V4Mul(b, sTmp), f), eRecip)); + + //if t is in [zero, one], done. otherwise clamp t + const Vec4V t2 = V4Clamp(tTmp, zero, one); + + //recompute s for the new value + //const Vec4V comp = V4Mul(V4Sub(V4Mul(b,t2), c), aRecip); + const Vec4V comp = V4Sel(V4IsEq(a, zero), one, V4Mul(V4Sub(V4Mul(b,t2), c), aRecip)); + const Vec4V s2 = V4Clamp(comp, zero, one); + + s = s2; + t = t2; + + const Vec4V closest1X = V4MulAdd(d0X, s2, pX); + const Vec4V closest1Y = V4MulAdd(d0Y, s2, pY); + const Vec4V closest1Z = V4MulAdd(d0Z, s2, pZ); + + const Vec4V closest2X = V4MulAdd(d0123X, t2, p0123X); + const Vec4V closest2Y = V4MulAdd(d0123Y, t2, p0123Y); + const Vec4V closest2Z = V4MulAdd(d0123Z, t2, p0123Z); + + const Vec4V vvX = V4Sub(closest1X, closest2X); + const Vec4V vvY = V4Sub(closest1Y, closest2Y); + const Vec4V vvZ = V4Sub(closest1Z, closest2Z); + + const Vec4V vd = V4MulAdd(vvX, vvX, V4MulAdd(vvY, vvY, V4Mul(vvZ, vvZ))); + + return vd; +} + +static Ps::aos::FloatV pcmDistancePointTriangleSquared( const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + const PxU8 triFlags, + Ps::aos::Vec3V& closestP, + bool& generateContact, + bool& faceContact) +{ + using namespace Ps::aos; + + faceContact = false; + const FloatV zero = FZero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V bp = V3Sub(p, b); + const Vec3V cp = V3Sub(p, c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + const Vec3V n = V3Cross(ab, ac); + const VecCrossV crossA = V3PrepareCross(ap); + const VecCrossV crossB = V3PrepareCross(bp); + const VecCrossV crossC = V3PrepareCross(cp); + const Vec3V bCrossC = V3Cross(crossB, crossC); + const Vec3V cCrossA = V3Cross(crossC, crossA); + const Vec3V aCrossB = V3Cross(crossA, crossB); + + //const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + //const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + //const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + + if(BAllEqTTTT(con0)) + { + //Vertex 0 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_01) || (triFlags & Gu::ETD_CONVEX_EDGE_20); + closestP = a; + return V3Dot(ap, ap); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + //Vertex 1 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_01) || (triFlags & Gu::ETD_CONVEX_EDGE_12); + closestP = b; + return V3Dot(bp, bp); + } + + //check if p in vertex region outside c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if(BAllEqTTTT(con2)) + { + //Vertex 2 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_12) || (triFlags & Gu::ETD_CONVEX_EDGE_20); + closestP = c; + return V3Dot(cp, cp); + } + + //check if p in edge region of AB + //const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); + const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + const BoolV con30 = FIsGrtr(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtr(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + if(BAllEqTTTT(con3)) + { + // Edge 01 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_01) != 0; + const FloatV sScale = FDiv(d1, FSub(d1, d3)); + const Vec3V closest3 = V3ScaleAdd(ab, sScale, a);//V3Add(a, V3Scale(ab, sScale)); + const Vec3V vv = V3Sub(p, closest3); + closestP = closest3; + return V3Dot(vv, vv); + } + + //check if p in edge region of BC + //const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); + const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + + const BoolV con40 = FIsGrtr(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + if(BAllEqTTTT(con4)) + { + // Edge 12 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_12) != 0; + const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); + const Vec3V closest4 = V3ScaleAdd(bc, uScale, b);//V3Add(b, V3Scale(bc, uScale)); + const Vec3V vv = V3Sub(p, closest4); + closestP = closest4; + return V3Dot(vv, vv); + } + + //check if p in edge region of AC + //const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); + const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + const BoolV con50 = FIsGrtr(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtr(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + if(BAllEqTTTT(con5)) + { + //Edge 20 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_20) != 0; + const FloatV tScale = FDiv(d2, FSub(d2, d6)); + const Vec3V closest5 = V3ScaleAdd(ac, tScale, a);//V3Add(a, V3Scale(ac, tScale)); + const Vec3V vv = V3Sub(p, closest5); + closestP = closest5; + return V3Dot(vv, vv); + } + + generateContact = true; + faceContact = true; + + //P must project inside face region. Compute Q using Barycentric coordinates + const FloatV nn = V3Dot(n, n); + const FloatV t = FDiv(V3Dot(n, V3Sub(a, p)), nn); + const Vec3V vv = V3Scale(n, t); + closestP = V3Add(p, vv); + return V3Dot(vv, vv); +} + +bool Gu::PCMSphereVsMeshContactGeneration::processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds) +{ + PX_UNUSED(triangleIndex); + PX_UNUSED(vertInds); + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const Vec3V v0 = V3LoadU(verts[0]); + const Vec3V v1 = V3LoadU(verts[1]); + const Vec3V v2 = V3LoadU(verts[2]); + + const Vec3V v10 = V3Sub(v1, v0); + const Vec3V v20 = V3Sub(v2, v0); + + const Vec3V n = V3Normalize(V3Cross(v10, v20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(v0, n);//d = -p0.dot(n); + + const FloatV dist0 = FSub(V3Dot(mSphereCenter, n), d);//p.dot(n) + d; + + // Backface culling + if(FAllGrtr(zero, dist0)) + return false; + + + const FloatV tolerance = FLoad(0.996f);//around 5 degree + //FloatV u, v; + Vec3V closestP; + //mSphereCenter will be in the local space of the triangle mesh + bool generateContact = false; + bool faceContact = false; + FloatV sqDist = pcmDistancePointTriangleSquared(mSphereCenter, v0, v1, v2, triFlags, closestP, generateContact, faceContact); + + //sphere overlap with triangles + if (FAllGrtr(mSqInflatedSphereRadius, sqDist)) + { + + //sphere center is on the triangle surface, we take triangle normal as the patchNormal. Otherwise, we need to calculate the patchNormal + Vec3V patchNormal = n; + if (!faceContact) + patchNormal = V3Normalize(V3Sub(mSphereCenter, closestP)); + + const FloatV cosTheta = V3Dot(patchNormal, n); + + //two normal less than 5 degree, generate contacts + if (FAllGrtr(cosTheta, tolerance)) + { + const FloatV dist = FSqrt(sqDist); + + mEdgeCache.addData(CachedEdge(vertInds[0], vertInds[1])); + mEdgeCache.addData(CachedEdge(vertInds[1], vertInds[2])); + mEdgeCache.addData(CachedEdge(vertInds[2], vertInds[0])); + + addToPatch(closestP, patchNormal, dist, triangleIndex); + } + else + { + //ML : defer the contacts generation + const PxU32 nb = sizeof(PCMDeferredPolyData) / sizeof(PxU32); + PxU32 newSize = nb + mDeferredContacts->size(); + mDeferredContacts->reserve(newSize); + PCMDeferredPolyData* PX_RESTRICT data = reinterpret_cast(mDeferredContacts->end()); + mDeferredContacts->forceSize_Unsafe(newSize); + + SortedTriangle sortedTriangle; + sortedTriangle.mSquareDist = sqDist; + sortedTriangle.mIndex = mSortedTriangle.size(); + mSortedTriangle.pushBack(sortedTriangle); + + data->mTriangleIndex = triangleIndex; + data->mFeatureIndex = 0; + data->triFlags = PxU8(generateContact); + data->mInds[0] = vertInds[0]; + data->mInds[1] = vertInds[1]; + data->mInds[2] = vertInds[2]; + V3StoreU(closestP, data->mVerts[0]); + V3StoreU(patchNormal, data->mVerts[1]); + V3StoreU(V3Splat(sqDist), data->mVerts[2]); + + } + } + return true; +} + +void Gu::PCMSphereVsMeshContactGeneration::addToPatch(const Ps::aos::Vec3VArg contactP, const Ps::aos::Vec3VArg patchNormal, const Ps::aos::FloatV dist, + const PxU32 triangleIndex) +{ + using namespace Ps::aos; + + PX_ASSERT(mNumContactPatch < PCM_MAX_CONTACTPATCH_SIZE); + + const Vec3V sphereCenter = V3Zero(); // in sphere local space + + bool foundPatch = false; + if (mNumContactPatch > 0) + { + if (FAllGrtr(V3Dot(mContactPatch[mNumContactPatch - 1].mPatchNormal, patchNormal), mAcceptanceEpsilon)) + { + PCMContactPatch& patch = mContactPatch[mNumContactPatch - 1]; + + PX_ASSERT((patch.mEndIndex - patch.mStartIndex) == 1); + + if (FAllGrtr(patch.mPatchMaxPen, dist)) + { + //overwrite the old contact + mManifoldContacts[patch.mStartIndex].mLocalPointA = sphereCenter;//in sphere's space + mManifoldContacts[patch.mStartIndex].mLocalPointB = contactP; + mManifoldContacts[patch.mStartIndex].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(patchNormal), dist); + mManifoldContacts[patch.mStartIndex].mFaceIndex = triangleIndex; + patch.mPatchMaxPen = dist; + } + + foundPatch = true; + } + } + if (!foundPatch) + { + mManifoldContacts[mNumContacts].mLocalPointA = sphereCenter;//in sphere's space + mManifoldContacts[mNumContacts].mLocalPointB = contactP; + mManifoldContacts[mNumContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(patchNormal), dist); + mManifoldContacts[mNumContacts++].mFaceIndex = triangleIndex; + + mContactPatch[mNumContactPatch].mStartIndex = mNumContacts - 1; + mContactPatch[mNumContactPatch].mEndIndex = mNumContacts; + mContactPatch[mNumContactPatch].mPatchMaxPen = dist; + mContactPatch[mNumContactPatch++].mPatchNormal = patchNormal; + } + + PX_ASSERT(mNumContactPatch < PCM_MAX_CONTACTPATCH_SIZE); + + if (mNumContacts >= 16) + { + PX_ASSERT(mNumContacts <= 64); + processContacts(GU_SPHERE_MANIFOLD_CACHE_SIZE); + } +} + +void Gu::PCMSphereVsMeshContactGeneration::generateLastContacts() +{ + using namespace Ps::aos; + // Process delayed contacts + PxU32 nbSortedTriangle = mSortedTriangle.size(); + + if (nbSortedTriangle) + { + Ps::sort(mSortedTriangle.begin(), mSortedTriangle.size(), Ps::Less()); + + const PCMDeferredPolyData* PX_RESTRICT cd = reinterpret_cast(mDeferredContacts->begin()); + + for (PxU32 i = 0; i < nbSortedTriangle; ++i) + { + const PCMDeferredPolyData& currentContact = cd[mSortedTriangle[i].mIndex]; + const PxU32 ref0 = currentContact.mInds[0]; + const PxU32 ref1 = currentContact.mInds[1]; + const PxU32 ref2 = currentContact.mInds[2]; + + PxU8 generateContacts = currentContact.triFlags; + + //if addData sucessful, which means mEdgeCache doesn't has the edge + const bool noEdge01 = mEdgeCache.addData(CachedEdge(ref0, ref1)); + const bool noEdge12 = mEdgeCache.addData(CachedEdge(ref1, ref2)); + const bool noEdge20 = mEdgeCache.addData(CachedEdge(ref2, ref0)); + + const bool needsProcessing = (noEdge01 && noEdge12 && noEdge20 && generateContacts); + + if (needsProcessing) + { + //we store the contact, patch normal and sq distance in the vertex memory in PCMDeferredPolyData + const Vec3V contactP = V3LoadU(currentContact.mVerts[0]); + const Vec3V patchNormal = V3LoadU(currentContact.mVerts[1]); + const FloatV sqDist = FLoad(currentContact.mVerts[2].x); + const FloatV dist = FSqrt(sqDist); + addToPatch(contactP, patchNormal, dist, currentContact.mTriangleIndex); + } + + } + } +} + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEE(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, + const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V n = V3Cross(ab, normal); + const FloatV d = V3Dot(n, a); + const FloatV np = V3Dot(n, p); + const FloatV nq = V3Dot(n,q); + const FloatV signP = FSub(np, d); + const FloatV signQ = FSub(nq, d); + const FloatV temp = FMul(signP, signQ); + if(FAllGrtr(temp, zero)) return;//If both points in the same side as the plane, no intersect points + + // if colliding edge (p3,p4) and plane are parallel return no collision + const Vec3V pq = V3Sub(q, p); + const FloatV npq= V3Dot(n, pq); + if(FAllEq(npq, zero)) return; + + const FloatV one = FOne(); + //calculate the intersect point in the segment pq + const FloatV segTValue = FDiv(FSub(d, np), npq); + const Vec3V localPointA = V3ScaleAdd(pq, segTValue, p); + + //calculate a normal perpendicular to ray localPointA + normal, 2D segment segment intersection + const Vec3V perNormal = V3Cross(normal, pq); + const Vec3V ap = V3Sub(localPointA, a); + const FloatV nom = V3Dot(perNormal, ap); + const FloatV denom = V3Dot(perNormal, ab); + + //const FloatV tValue = FClamp(FDiv(nom, denom), zero, FOne()); + const FloatV tValue = FDiv(nom, denom); + const BoolV con = BAnd(FIsGrtrOrEq(one, tValue), FIsGrtrOrEq(tValue, zero)); + if(BAllEqFFFF(con)) + return; + + //const Vec3V localPointB = V3ScaleAdd(ab, tValue, a); v = V3Sub(localPointA, localPointB); v = V3NegScaleSub(ab, tValue, tap) + const Vec3V v = V3NegScaleSub(ab, tValue, ap); + const FloatV sqDist = V3Dot(v, v); + + if(FAllGrtr(sqInflatedRadius, sqDist)) + { + + const Vec3V localPointB = V3Sub(localPointA, v); + const FloatV signedDist = V3Dot(v, normal); + + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = localPointB; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), signedDist); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + } +} + + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEEContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, + const PxU32 triangleIndex, const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + + using namespace Ps::aos; + generateEE(p, q, sqInflatedRadius, normal, triangleIndex, a, b, manifoldContacts, numContacts); + generateEE(p, q, sqInflatedRadius, normal, triangleIndex, b, c, manifoldContacts, numContacts); + generateEE(p, q, sqInflatedRadius, normal, triangleIndex, a, c, manifoldContacts, numContacts); + +} + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEEMTD(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, + const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V n = V3Cross(ab, normal); + const FloatV d = V3Dot(n, a); + const FloatV np = V3Dot(n, p); + const FloatV nq = V3Dot(n,q); + const FloatV signP = FSub(np, d); + const FloatV signQ = FSub(nq, d); + const FloatV temp = FMul(signP, signQ); + if(FAllGrtr(temp, zero)) return;//If both points in the same side as the plane, no intersect points + + // if colliding edge (p3,p4) and plane are parallel return no collision + const Vec3V pq = V3Sub(q, p); + const FloatV npq= V3Dot(n, pq); + if(FAllEq(npq, zero)) return; + + //calculate the intersect point in the segment pq + const FloatV segTValue = FDiv(FSub(d, np), npq); + const Vec3V localPointA = V3ScaleAdd(pq, segTValue, p); + + //calculate a normal perpendicular to ray localPointA + normal, 2D segment segment intersection + const Vec3V perNormal = V3Cross(normal, pq); + const Vec3V ap = V3Sub(localPointA, a); + const FloatV nom = V3Dot(perNormal, ap); + const FloatV denom = V3Dot(perNormal, ab); + + const FloatV tValue = FClamp(FDiv(nom, denom), zero, FOne()); + + const Vec3V v = V3NegScaleSub(ab, tValue, ap); + const FloatV signedDist = V3Dot(v, normal); + + if(FAllGrtr(inflatedRadius, signedDist)) + { + + const Vec3V localPointB = V3Sub(localPointA, v); + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = localPointB; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), signedDist); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + } +} + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEEContactsMTD(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, + const PxU32 triangleIndex, const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + + using namespace Ps::aos; + generateEEMTD(p, q, inflatedRadius, normal, triangleIndex, a, b, manifoldContacts, numContacts); + generateEEMTD(p, q, inflatedRadius, normal, triangleIndex, b, c, manifoldContacts, numContacts); + generateEEMTD(p, q, inflatedRadius, normal, triangleIndex, a, c, manifoldContacts, numContacts); + +} + +bool Gu::PCMCapsuleVsMeshContactGeneration::generateContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg planeNormal, + const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::FloatVArg inflatedRadius, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + + using namespace Ps::aos; + + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V ap = V3Sub(p, a); + const Vec3V aq = V3Sub(q, a); + + //This is used to calculate the barycentric coordinate + const FloatV d00 = V3Dot(ab, ab); + const FloatV d01 = V3Dot(ab, ac); + const FloatV d11 = V3Dot(ac, ac); + const FloatV bdenom = FRecip(FSub(FMul(d00,d11), FMul(d01, d01))); + + //compute the intersect point of p and triangle plane abc + const FloatV inomp = V3Dot(planeNormal, V3Neg(ap)); + const FloatV ideom = V3Dot(planeNormal, normal); + + const FloatV ipt = FSel(FIsGrtr(ideom, FZero()), FDiv(inomp, ideom), FZero()); + //compute the distance from triangle plane abc + const FloatV dist3 = V3Dot(ap, planeNormal); + + const Vec3V closestP31 = V3ScaleAdd(normal, ipt, p); + const Vec3V closestP30 = p; + + + //Compute the barycentric coordinate for project point of q + const Vec3V pV20 = V3Sub(closestP31, a); + const FloatV pD20 = V3Dot(pV20, ab); + const FloatV pD21 = V3Dot(pV20, ac); + const FloatV v0 = FMul(FSub(FMul(d11, pD20), FMul(d01, pD21)), bdenom); + const FloatV w0 = FMul(FSub(FMul(d00, pD21), FMul(d01, pD20)), bdenom); + + //check closestP3 is inside the triangle + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + + + const BoolV tempCon0 = BAnd(con0, FIsGrtr(inflatedRadius, dist3)); + if(BAllEqTTTT(tempCon0)) + { + manifoldContacts[numContacts].mLocalPointA = closestP30;//transform to B space, it will get convert to A space later + manifoldContacts[numContacts].mLocalPointB = closestP31; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), FNeg(ipt)); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + } + + const FloatV inomq = V3Dot(planeNormal, V3Neg(aq)); + + //compute the distance of q and triangle plane abc + const FloatV dist4 = V3Dot(aq, planeNormal); + + const FloatV iqt = FSel(FIsGrtr(ideom, FZero()), FDiv(inomq, ideom), FZero()); + + const Vec3V closestP41 = V3ScaleAdd(normal, iqt, q); + const Vec3V closestP40 = q; + + //Compute the barycentric coordinate for project point of q + const Vec3V qV20 = V3Sub(closestP41, a); + const FloatV qD20 = V3Dot(qV20, ab); + const FloatV qD21 = V3Dot(qV20, ac); + const FloatV v1 = FMul(FSub(FMul(d11, qD20), FMul(d01, qD21)), bdenom); + const FloatV w1 = FMul(FSub(FMul(d00, qD21), FMul(d01, qD20)), bdenom); + + + const BoolV con1 = isValidTriangleBarycentricCoord(v1, w1); + + const BoolV tempCon1 = BAnd(con1, FIsGrtr(inflatedRadius, dist4)); + if(BAllEqTTTT(tempCon1)) + { + manifoldContacts[numContacts].mLocalPointA = closestP40; + manifoldContacts[numContacts].mLocalPointB = closestP41; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal),FNeg(iqt)); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + + } + + return false; +} + + +/* + t is the barycenteric coordinate of a segment + u is the barycenteric coordinate of a triangle + v is the barycenteric coordinate of a triangle +*/ +Ps::aos::FloatV pcmDistanceSegmentTriangleSquared( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& t, Ps::aos::FloatV& u, Ps::aos::FloatV& v) +{ + using namespace Ps::aos; + + const FloatV one = FOne(); + const FloatV zero = FZero(); + + const Vec3V pq = V3Sub(q, p); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V aq = V3Sub(q, a); + + const Vec3V n =V3Normalize(V3Cross(ab, ac)); // normalize vector + + const Vec4V combinedDot = V3Dot4(ab, ab, ab, ac, ac, ac, ap, n); + const FloatV d00 = V4GetX(combinedDot); + const FloatV d01 = V4GetY(combinedDot); + const FloatV d11 = V4GetZ(combinedDot); + const FloatV dist3 = V4GetW(combinedDot); + + const FloatV bdenom = FRecip(FSub(FMul(d00,d11), FMul(d01, d01))); + + + const FloatV sqDist3 = FMul(dist3, dist3); + + + //compute the closest point of q and triangle plane abc + const FloatV dist4 = V3Dot(aq, n); + const FloatV sqDist4 = FMul(dist4, dist4); + const FloatV dMul = FMul(dist3, dist4); + const BoolV con = FIsGrtr(zero, dMul); + + if(BAllEqTTTT(con)) + { + //compute the intersect point + const FloatV nom = FNeg(V3Dot(n, ap)); + const FloatV denom = FRecip(V3Dot(n, pq)); + const FloatV t0 = FMul(nom, denom); + const Vec3V ip = V3ScaleAdd(pq, t0, p);//V3Add(p, V3Scale(pq, t)); + const Vec3V v2 = V3Sub(ip, a); + const FloatV d20 = V3Dot(v2, ab); + const FloatV d21 = V3Dot(v2, ac); + + const FloatV v0 = FMul(FNegScaleSub(d01, d21, FMul(d11, d20)), bdenom); + const FloatV w0 = FMul(FNegScaleSub(d01, d20, FMul(d00, d21)), bdenom); + + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + if(BAllEqTTTT(con0)) + { + t = t0; + u = v0; + v = w0; + return zero; + } + } + + const Vec3V closestP31 = V3NegScaleSub(n, dist3, p);//V3Sub(p, V3Scale(n, dist3)); + const Vec3V closestP41 = V3NegScaleSub(n, dist4, q);// V3Sub(q, V3Scale(n, dist4)); + + //Compute the barycentric coordinate for project point of q + const Vec3V pV20 = V3Sub(closestP31, a); + const Vec3V qV20 = V3Sub(closestP41, a); + + const Vec4V pD2 = V3Dot4(pV20, ab, pV20, ac, qV20, ab, qV20, ac); + + const Vec4V pD2Swizzle = V4PermYXWZ(pD2); + + const Vec4V d11d00 = V4UnpackXY(V4Splat(d11), V4Splat(d00)); + + const Vec4V v0w0v1w1 = V4Scale(V4NegMulSub(V4Splat(d01), pD2Swizzle, V4Mul(d11d00, pD2)), bdenom); + + const FloatV v0 = V4GetX(v0w0v1w1); + const FloatV w0 = V4GetY(v0w0v1w1); + const FloatV v1 = V4GetZ(v0w0v1w1); + const FloatV w1 = V4GetW(v0w0v1w1); + + const BoolV _con = isValidTriangleBarycentricCoord2(v0w0v1w1); + + const BoolV con0 = BGetX(_con); + const BoolV con1 = BGetY(_con); + + + const BoolV cond2 = BAnd(con0, con1); + + if(BAllEqTTTT(cond2)) + { + /* + both p and q project points are interior point + */ + const BoolV d2 = FIsGrtr(sqDist4, sqDist3); + t = FSel(d2, zero, one); + u = FSel(d2, v0, v1); + v = FSel(d2, w0, w1); + return FSel(d2, sqDist3, sqDist4); + } + else + { + Vec4V t40, t41; + const Vec4V sqDist44 = pcmDistanceSegmentSegmentSquared4(p,pq,a,ab, b,bc, a,ac, a,ab, t40, t41); + + const FloatV t00 = V4GetX(t40); + const FloatV t10 = V4GetY(t40); + const FloatV t20 = V4GetZ(t40); + + const FloatV t01 = V4GetX(t41); + const FloatV t11 = V4GetY(t41); + const FloatV t21 = V4GetZ(t41); + + //edge ab + const FloatV u01 = t01; + const FloatV v01 = zero; + + //edge bc + const FloatV u11 = FSub(one, t11); + const FloatV v11 = t11; + + //edge ac + const FloatV u21 = zero; + const FloatV v21 = t21; + + const FloatV sqDist0(V4GetX(sqDist44)); + const FloatV sqDist1(V4GetY(sqDist44)); + const FloatV sqDist2(V4GetZ(sqDist44)); + + const BoolV con2 = BAnd(FIsGrtr(sqDist1, sqDist0), FIsGrtr(sqDist2, sqDist0)); + const BoolV con3 = FIsGrtr(sqDist2, sqDist1); + const FloatV sqDistPE = FSel(con2, sqDist0, FSel(con3, sqDist1, sqDist2)); + const FloatV uEdge = FSel(con2, u01, FSel(con3, u11, u21)); + const FloatV vEdge = FSel(con2, v01, FSel(con3, v11, v21)); + const FloatV tSeg = FSel(con2, t00, FSel(con3, t10, t20)); + + + if(BAllEqTTTT(con0)) + { + //p's project point is an interior point + const BoolV d2 = FIsGrtr(sqDistPE, sqDist3); + t = FSel(d2, zero, tSeg); + u = FSel(d2, v0, uEdge); + v = FSel(d2, w0, vEdge); + return FSel(d2, sqDist3, sqDistPE); + } + else if(BAllEqTTTT(con1)) + { + //q's project point is an interior point + const BoolV d2 = FIsGrtr(sqDistPE, sqDist4); + t = FSel(d2, one, tSeg); + u = FSel(d2, v1, uEdge); + v = FSel(d2, w1, vEdge); + return FSel(d2, sqDist4, sqDistPE); + } + else + { + t = tSeg; + u = uEdge; + v = vEdge; + return sqDistPE; + } + + } + +} + + +static bool selectNormal(const Ps::aos::FloatVArg u, Ps::aos::FloatVArg v, PxU8 data) +{ + using namespace Ps::aos; + const FloatV zero = FLoad(1e-6f); + const FloatV one = FLoad(0.999999f); + // Analysis + if(FAllGrtr(zero, u)) + { + if(FAllGrtr(zero, v)) + { + // Vertex 0 + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_20))) + return true; + } + else if(FAllGrtr(v, one)) + { + // Vertex 2 + if(!(data & (Gu::ETD_CONVEX_EDGE_12|Gu::ETD_CONVEX_EDGE_20))) + return true; + } + else + { + // Edge 0-2 + if(!(data & Gu::ETD_CONVEX_EDGE_20)) + return true; + } + } + else if(FAllGrtr(u,one)) + { + if(FAllGrtr(zero, v)) + { + // Vertex 1 + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_12))) + return true; + + } + } + else + { + if(FAllGrtr(zero, v)) + { + // Edge 0-1 + if(!(data & Gu::ETD_CONVEX_EDGE_01)) + return true; + } + else + { + const FloatV threshold = FLoad(0.9999f); + const FloatV temp = FAdd(u, v); + if(FAllGrtrOrEq(temp, threshold)) + { + // Edge 1-2 + if(!(data & Gu::ETD_CONVEX_EDGE_12)) + return true; + } + else + { + // Face + return true; + } + } + } + return false; +} + + +bool Gu::PCMCapsuleVsMeshContactGeneration::processTriangle(const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds) +{ + PX_UNUSED(triangleIndex); + PX_UNUSED(vertInds); + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const Vec3V p0 = V3LoadU(verts[0]); + const Vec3V p1 = V3LoadU(verts[1]); + const Vec3V p2 = V3LoadU(verts[2]); + + const Vec3V p10 = V3Sub(p1, p0); + const Vec3V p20 = V3Sub(p2, p0); + + const Vec3V n = V3Normalize(V3Cross(p10, p20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(p0, n);//d = -p0.dot(n); + + const FloatV dist = FSub(V3Dot(mCapsule.getCenter(), n), d);//p.dot(n) + d; + + // Backface culling + if(FAllGrtr(zero, dist)) + return false; + + + FloatV t, u, v; + const FloatV sqDist = pcmDistanceSegmentTriangleSquared(mCapsule.p0, mCapsule.p1, p0, p1, p2, t, u, v); + + if(FAllGrtr(mSqInflatedRadius, sqDist)) + { + + Vec3V patchNormalInTriangle; + if(selectNormal(u, v, triFlags)) + { + patchNormalInTriangle = n; + } + else + { + if(FAllEq(sqDist, zero)) + { + //segment intersect with the triangle + patchNormalInTriangle = n; + } + else + { + const Vec3V pq = V3Sub(mCapsule.p1, mCapsule.p0); + const Vec3V pointOnSegment = V3ScaleAdd(pq, t, mCapsule.p0); + const FloatV w = FSub(FOne(), FAdd(u, v)); + const Vec3V pointOnTriangle = V3ScaleAdd(p0, w, V3ScaleAdd(p1, u, V3Scale(p2, v))); + patchNormalInTriangle = V3Normalize(V3Sub(pointOnSegment, pointOnTriangle)); + + } + } + + const PxU32 previousNumContacts = mNumContacts; + + generateContacts(p0, p1, p2, n, patchNormalInTriangle, triangleIndex, mCapsule.p0, mCapsule.p1, mInflatedRadius, mManifoldContacts, mNumContacts); + //ML: this need to use the sqInflatedRadius to avoid some bad contacts + generateEEContacts(p0, p1, p2,patchNormalInTriangle, triangleIndex, mCapsule.p0, mCapsule.p1, mSqInflatedRadius, mManifoldContacts, mNumContacts); + + + PxU32 numContacts = mNumContacts - previousNumContacts; + + if(numContacts > 0) + { + FloatV maxPen = FMax(); + for(PxU32 i = previousNumContacts; i= 16) + { + PX_ASSERT(mNumContacts <= 64); + processContacts(GU_CAPSULE_MANIFOLD_CACHE_SIZE); + } + } + } + + return true; +} + +bool Gu::PCMCapsuleVsMeshContactGeneration::processTriangle(const TriangleV& triangleV, const PxU32 triangleIndex, const CapsuleV& capsule, const Ps::aos::FloatVArg inflatedRadius, const PxU8 trigFlag, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const Vec3V p0 = triangleV.verts[0]; + const Vec3V p1 = triangleV.verts[1]; + const Vec3V p2 = triangleV.verts[2]; + + const Vec3V n = triangleV.normal(); + + const FloatV sqInflatedRadius = FMul(inflatedRadius, inflatedRadius); + + + FloatV t, u, v; + const FloatV sqDist = pcmDistanceSegmentTriangleSquared(capsule.p0, capsule.p1, p0, p1, p2, t, u, v); + + if(FAllGrtr(sqInflatedRadius, sqDist)) + { + + Vec3V patchNormalInTriangle; + if(selectNormal(u, v, trigFlag)) + { + patchNormalInTriangle = n; + } + else + { + if(FAllEq(sqDist, zero)) + { + //segment intersect with the triangle + patchNormalInTriangle = n; + } + else + { + const Vec3V pq = V3Sub(capsule.p1, capsule.p0); + const Vec3V pointOnSegment = V3ScaleAdd(pq, t, capsule.p0); + const FloatV w = FSub(FOne(), FAdd(u, v)); + const Vec3V pointOnTriangle = V3ScaleAdd(p0, w, V3ScaleAdd(p1, u, V3Scale(p2, v))); + patchNormalInTriangle = V3Normalize(V3Sub(pointOnSegment, pointOnTriangle)); + } + } + + generateContacts(p0, p1, p2, n, patchNormalInTriangle, triangleIndex, capsule.p0, capsule.p1, inflatedRadius, manifoldContacts, numContacts); + + generateEEContactsMTD(p0, p1, p2, patchNormalInTriangle, triangleIndex, capsule.p0, capsule.p1, inflatedRadius, manifoldContacts, numContacts); + + + } + + return true; + +} + +} +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h new file mode 100644 index 000000000..2e46431b6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h @@ -0,0 +1,437 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_CONVEX_COMMON_H +#define GU_PCM_CONTACT_CONVEX_COMMON_H + +#define PCM_MAX_CONTACTPATCH_SIZE 32 + +#include "GuContactBuffer.h" +#include "GuVecCapsule.h" +#include "GuPCMTriangleContactGen.h" +#include "PsInlineArray.h" +#include "GuTriangleCache.h" + +namespace physx +{ + +namespace Gu +{ + +#define MAX_CACHE_SIZE 128 + +class PCMMeshContactGeneration +{ + PX_NOCOPY(PCMMeshContactGeneration) +public: + PCMContactPatch mContactPatch[PCM_MAX_CONTACTPATCH_SIZE]; + PCMContactPatch* mContactPatchPtr[PCM_MAX_CONTACTPATCH_SIZE]; + const Ps::aos::FloatV mContactDist; + const Ps::aos::FloatV mReplaceBreakingThreshold; + const Ps::aos::PsTransformV& mConvexTransform; + const Ps::aos::PsTransformV& mMeshTransform; + Gu::MultiplePersistentContactManifold& mMultiManifold; + Gu::ContactBuffer& mContactBuffer; + + Ps::aos::FloatV mAcceptanceEpsilon; + Ps::aos::FloatV mSqReplaceBreakingThreshold; + Ps::aos::PsMatTransformV mMeshToConvex; + Gu::MeshPersistentContact* mManifoldContacts; + PxU32 mNumContacts; + PxU32 mNumContactPatch; + PxU32 mNumCalls; + Gu::CacheMap mEdgeCache; + Ps::InlineArray* mDeferredContacts; + Cm::RenderOutput* mRenderOutput; + + PCMMeshContactGeneration( + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& convexTransform, + const Ps::aos::PsTransformV& meshTransform, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Ps::InlineArray* deferredContacts, + Cm::RenderOutput* renderOutput + + ) : + mContactDist(contactDist), + mReplaceBreakingThreshold(replaceBreakingThreshold), + mConvexTransform(convexTransform), + mMeshTransform(meshTransform), + mMultiManifold(multiManifold), + mContactBuffer(contactBuffer), + mDeferredContacts(deferredContacts), + mRenderOutput(renderOutput) + + { + using namespace Ps::aos; + mNumContactPatch = 0; + mNumContacts = 0; + mNumCalls = 0; + + mMeshToConvex = mConvexTransform.transformInv(mMeshTransform); + + //Assign the PCMContactPatch to the PCMContactPathPtr + for(PxU32 i=0; i + bool processTriangleCache(Gu::TriangleCache& cache) + { + PxU32 count = cache.mNumTriangles; + PxVec3* verts = cache.mVertices; + PxU32* vertInds = cache.mIndices; + PxU32* triInds = cache.mTriangleIndex; + PxU8* edgeFlags = cache.mEdgeFlags; + while(count--) + { + (static_cast(this))->processTriangle(verts, *triInds, *edgeFlags, vertInds); + verts += 3; + vertInds += 3; + triInds++; + edgeFlags++; + } + return true; + } + void prioritizeContactPatches(); + void addManifoldPointToPatch(const Ps::aos::Vec3VArg currentPatchNormal, const Ps::aos::FloatVArg maxPen, const PxU32 previousNumContacts); + void processContacts(const PxU8 maxContactPerManifold, const bool isNotLastPatch = true); +}; + +/* + This function is based on the current patch normal to either create a new patch or merge the manifold contacts in this patch with the manifold contacts in the last existing + patch. This means there might be more than GU_SINGLE_MANIFOLD_CACHE_SIZE in a SinglePersistentContactManifold. +*/ +PX_FORCE_INLINE void PCMMeshContactGeneration::addManifoldPointToPatch(const Ps::aos::Vec3VArg currentPatchNormal, const Ps::aos::FloatVArg maxPen, const PxU32 previousNumContacts) +{ + using namespace Ps::aos; + + bool foundPatch = false; + //we have existing patch + if(mNumContactPatch > 0) + { + //if the direction between the last existing patch normal and the current patch normal are within acceptance epsilon, which means we will be + //able to merge the last patch's contacts with the current patch's contacts. This is just to avoid to create an extra patch. We have some logic + //later to refine the patch again + if(FAllGrtr(V3Dot(mContactPatch[mNumContactPatch-1].mPatchNormal, currentPatchNormal), mAcceptanceEpsilon)) + { + //get the last patch + PCMContactPatch& patch = mContactPatch[mNumContactPatch-1]; + + //remove duplicate contacts + for(PxU32 i = patch.mStartIndex; imPatchMaxPen, mContactPatchPtr[i]->mPatchMaxPen)) + { + //swap + PCMContactPatch* tmp = mContactPatchPtr[indexi]; + mContactPatchPtr[indexi] = mContactPatchPtr[i]; + mContactPatchPtr[i] = tmp; + + for(PxI32 j=PxI32(i-2); j>=0; j--) + { + const PxU32 indexj = PxU32(j+1); + if(FAllGrtrOrEq(mContactPatchPtr[indexj]->mPatchMaxPen, mContactPatchPtr[j]->mPatchMaxPen)) + break; + //swap + PCMContactPatch* temp = mContactPatchPtr[indexj]; + mContactPatchPtr[indexj] = mContactPatchPtr[j]; + mContactPatchPtr[j] = temp; + } + } + } +} + + +PX_FORCE_INLINE void PCMMeshContactGeneration::processContacts(const PxU8 maxContactPerManifold, bool isNotLastPatch) +{ + using namespace Ps::aos; + + if(mNumContacts != 0) + { + //reorder the contact patches based on the max penetration + prioritizeContactPatches(); + //connect the patches which's angle between patch normals are within 5 degree + mMultiManifold.refineContactPatchConnective(mContactPatchPtr, mNumContactPatch, mManifoldContacts, mAcceptanceEpsilon); + //get rid of duplicate manifold contacts in connected contact patches + mMultiManifold.reduceManifoldContactsInDifferentPatches(mContactPatchPtr, mNumContactPatch, mManifoldContacts, mNumContacts, mSqReplaceBreakingThreshold); + //add the manifold contact to the corresponding manifold + mMultiManifold.addManifoldContactPoints(mManifoldContacts, mNumContacts, mContactPatchPtr, mNumContactPatch, mSqReplaceBreakingThreshold, mAcceptanceEpsilon, maxContactPerManifold); + + mNumContacts = 0; + mNumContactPatch = 0; + + if(isNotLastPatch) + { + //remap the contact patch pointer to contact patch + for(PxU32 i=0; i mVertexCache; + Ps::aos::Vec3V mHullCenterMesh; + + const Gu::PolygonalData& mPolyData; + SupportLocal* mPolyMap; + const Cm::FastVertex2ShapeScaling& mConvexScaling; + bool mIdtConvexScale; + bool mSilhouetteEdgesAreActive; + + PCMConvexVsMeshContactGeneration( + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& convexTransform, + const Ps::aos::PsTransformV& meshTransform, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + + const Gu::PolygonalData& polyData, + SupportLocal* polyMap, + Ps::InlineArray* delayedContacts, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale, + bool silhouetteEdgesAreActive, + Cm::RenderOutput* renderOutput + + ) : PCMMeshContactGeneration(contactDistance, replaceBreakingThreshold, convexTransform, meshTransform, multiManifold, contactBuffer, + delayedContacts, renderOutput), + mPolyData(polyData), + mPolyMap(polyMap), + mConvexScaling(convexScaling), + mIdtConvexScale(idtConvexScale), + mSilhouetteEdgesAreActive(silhouetteEdgesAreActive) + { + using namespace Ps::aos; + + // Hull center in local space + const Vec3V hullCenterLocal = V3LoadU(mPolyData.mCenter); + // Hull center in mesh space + mHullCenterMesh = mMeshToConvex.transformInv(hullCenterLocal); + + } + + bool generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU32* triIndices, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal); + + bool generatePolyDataContactManifold(Gu::TriangleV& localTriangle, const PxU32 featureIndex, const PxU32 triangleIndex, const PxU8 triFlags, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal); + void generateLastContacts(); + void addContactsToPatch(const Ps::aos::Vec3VArg patchNormal, const PxU32 previousNumContacts); + + bool processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + + static bool generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal, Cm::RenderOutput* renderOutput = NULL); + + static bool processTriangle(const Gu::PolygonalData& polyData, SupportLocal* polyMap, const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags, const Ps::aos::FloatVArg inflation, const bool isDoubleSided, + const Ps::aos::PsTransformV& convexTransform, const Ps::aos::PsMatTransformV& meshToConvex, Gu::MeshPersistentContact* manifoldContact, PxU32& numContacts); +}; + +#if PX_VC + #pragma warning(pop) +#endif + +struct SortedTriangle +{ + Ps::aos::FloatV mSquareDist; + PxU32 mIndex; + + PX_FORCE_INLINE bool operator < (const SortedTriangle& data) const + { + return Ps::aos::FAllGrtrOrEq(mSquareDist, data.mSquareDist) ==0; + } +}; + +class PCMSphereVsMeshContactGeneration : public PCMMeshContactGeneration +{ +public: + Ps::aos::Vec3V mSphereCenter; + Ps::aos::FloatV mSphereRadius; + Ps::aos::FloatV mSqInflatedSphereRadius; + Ps::InlineArray mSortedTriangle; + + PCMSphereVsMeshContactGeneration( + const Ps::aos::Vec3VArg sphereCenter, + const Ps::aos::FloatVArg sphereRadius, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& sphereTransform, + const Ps::aos::PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + Ps::InlineArray* deferredContacts, + Cm::RenderOutput* renderOutput = NULL + + ) : PCMMeshContactGeneration(contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, + contactBuffer, deferredContacts, renderOutput), + mSphereCenter(sphereCenter), + mSphereRadius(sphereRadius) + { + using namespace Ps::aos; + const FloatV inflatedSphereRadius = FAdd(sphereRadius, contactDist); + mSqInflatedSphereRadius = FMul(inflatedSphereRadius, inflatedSphereRadius); + } + + bool processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + void generateLastContacts(); + void addToPatch(const Ps::aos::Vec3VArg contactP, const Ps::aos::Vec3VArg patchNormal, + const Ps::aos::FloatV pen, const PxU32 triangleIndex); +}; + +class PCMCapsuleVsMeshContactGeneration : public PCMMeshContactGeneration +{ + PCMCapsuleVsMeshContactGeneration &operator=(PCMCapsuleVsMeshContactGeneration &); +public: + Ps::aos::FloatV mInflatedRadius; + Ps::aos::FloatV mSqInflatedRadius; + const CapsuleV& mCapsule; + + + PCMCapsuleVsMeshContactGeneration( + const CapsuleV& capsule, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& sphereTransform, + const Ps::aos::PsTransformV& meshTransform, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Ps::InlineArray* deferredContacts, + Cm::RenderOutput* renderOutput = NULL + + ) : PCMMeshContactGeneration(contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, contactBuffer, + deferredContacts, renderOutput), + mCapsule(capsule) + { + using namespace Ps::aos; + mInflatedRadius = FAdd(capsule.radius, contactDist); + mSqInflatedRadius = FMul(mInflatedRadius, mInflatedRadius); + } + + void generateEEContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b,const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + void generateEE(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + static bool generateContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b,const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg planeNormal, const Ps::aos::Vec3VArg normal, + const PxU32 triangleIndex, const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + static void generateEEContactsMTD(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b,const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + static void generateEEMTD(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, const Ps::aos::Vec3VArg normal, const PxU32 trianlgeIndex, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + bool processTriangle(const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + + static bool processTriangle(const TriangleV& triangle, const PxU32 triangleIndex, const CapsuleV& capsule, const Ps::aos::FloatVArg inflatedRadius, const PxU8 triFlag, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); +}; + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp new file mode 100644 index 000000000..27deee46c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp @@ -0,0 +1,294 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMShapeConvex.h" +#include "GuPCMContactGen.h" +#include "GuContactBuffer.h" + + +namespace physx +{ +using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationConvexConvex(const GjkConvex* relativeConvex, const GjkConvex* localConvex, const PsTransformV& transf0, const PsTransformV& transf1, + const bool idtScale0, const bool idtScale1, PersistentContact* manifoldContacts, ContactBuffer& contactBuffer, + PersistentContactManifold& manifold, Vec3VArg normal, const Vec3VArg closestA, const Vec3VArg closestB, + const FloatVArg contactDist, const bool doOverlapTest, Cm::RenderOutput* renderOutput, const PxReal toleranceLength) +{ + Gu::PolygonalData polyData0, polyData1; + const ConvexHullV& convexHull0 = relativeConvex->getConvex(); + const ConvexHullV& convexHull1 = localConvex->getConvex(); + getPCMConvexData(convexHull0, idtScale0, polyData0); + getPCMConvexData(convexHull1, idtScale1, polyData1); + + PxU8 buff0[sizeof(SupportLocalImpl)]; + PxU8 buff1[sizeof(SupportLocalImpl)]; + + SupportLocal* map0 = (idtScale0 ? static_cast(PX_PLACEMENT_NEW(buff0, SupportLocalImpl)(static_cast(convexHull0), transf0, convexHull0.vertex2Shape, convexHull0.shape2Vertex, idtScale0)) : + static_cast(PX_PLACEMENT_NEW(buff0, SupportLocalImpl)(convexHull0, transf0, convexHull0.vertex2Shape, convexHull0.shape2Vertex, idtScale0))); + + SupportLocal* map1 = (idtScale1 ? static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(static_cast(convexHull1), transf1, convexHull1.vertex2Shape, convexHull1.shape2Vertex, idtScale1)) : + static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(convexHull1, transf1, convexHull1.vertex2Shape, convexHull1.shape2Vertex, idtScale1))); + + + PxU32 numContacts = 0; + + if(generateFullContactManifold(polyData0, polyData1, map0, map1, manifoldContacts, numContacts, contactDist, normal, closestA, closestB, convexHull0.getMarginF(), + convexHull1.getMarginF(), doOverlapTest, renderOutput, toleranceLength)) + { + + if (numContacts > 0) + { + //reduce contacts + manifold.addBatchManifoldContacts(manifoldContacts, numContacts, toleranceLength); + + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + //add the manifold contacts; + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + } + else + { + //if doOverlapTest is true, which means GJK/EPA degenerate so we won't have any contact in the manifoldContacts array + if (!doOverlapTest) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + } + + } + return true; + + } + + return false; + +} + +static bool generateOrProcessContactsConvexConvex(const GjkConvex* relativeConvex, const GjkConvex* localConvex, const PsTransformV& transf0, const PsTransformV& transf1, + const PsMatTransformV& aToB, GjkStatus status, GjkOutput& output, PersistentContactManifold& manifold, ContactBuffer& contactBuffer, + const PxU32 initialContacts, const FloatV minMargin, const FloatV contactDist, + const bool idtScale0, const bool idtScale1, const PxReal toleranceLength, Cm::RenderOutput* renderOutput) +{ + + if (status == GJK_NON_INTERSECT) + { + return false; + } + else + { + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + + const Vec3V localNor = manifold.mNumContacts ? manifold.getLocalNormal() : V3Zero(); + + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + //addGJKEPAContacts will increase the number of contacts in manifold. If status == EPA_CONTACT, we need to run epa algorithm and generate closest points, normal and + //pentration. If epa doesn't degenerate, we will store the contacts information in the manifold. Otherwise, we will return true to do the fallback test + const bool doOverlapTest = addGJKEPAContacts(relativeConvex, localConvex, aToB, status, manifoldContacts, replaceBreakingThreshold, FLoad(toleranceLength), output, manifold); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + //ML: after we refresh the contacts(newContacts) and generate a GJK/EPA contacts(we will store that in the manifold), if the number of contacts is still less than the original contacts, + //which means we lose too mang contacts and we should regenerate all the contacts in the current configuration + //Also, we need to look at the existing contacts, if the existing contacts has very different normal than the GJK/EPA contacts, + //which means we should throw away the existing contacts and do full contact gen + const bool fullContactGen = FAllGrtr(FLoad(0.707106781f), V3Dot(localNor, output.normal)) || (manifold.mNumContacts < initialContacts); + + if (fullContactGen || doOverlapTest) + { + return fullContactsGenerationConvexConvex(relativeConvex, localConvex, transf0, transf1, idtScale0, idtScale1, manifoldContacts, contactBuffer, + manifold, output.normal, output.closestA, output.closestB, contactDist, doOverlapTest, renderOutput, toleranceLength); + } + else + { + const Vec3V newLocalNor = V3Add(localNor, output.normal); + const Vec3V worldNormal = V3Normalize(transf1.rotate(newLocalNor)); + //const Vec3V worldNormal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + return true; + } + } +} + +static bool convexHullNoScale0(const ConvexHullV& convexHull0, const ConvexHullV& convexHull1, const PsTransformV& transf0, const PsTransformV& transf1, + const PsMatTransformV& aToB, GjkOutput& output, PersistentContactManifold& manifold, ContactBuffer& contactBuffer, + const PxU32 initialContacts, const FloatV minMargin, const FloatV contactDist, + const bool idtScale1, const PxReal toleranceLength, Cm::RenderOutput* renderOutput) +{ + + const RelativeConvex convexA(static_cast(convexHull0), aToB); + if(idtScale1) + { + const LocalConvex convexB(static_cast(convexHull1)); + GjkStatus status = gjkPenetration, LocalConvex >(convexA, convexB, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + return generateOrProcessContactsConvexConvex(&convexA, &convexB, transf0, transf1, aToB, status, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, true, true, toleranceLength, renderOutput); + + } + else + { + const LocalConvex convexB(convexHull1); + GjkStatus status = gjkPenetration, LocalConvex >(convexA, convexB, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + + return generateOrProcessContactsConvexConvex(&convexA, &convexB, transf0, transf1, aToB, status, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, true, false, toleranceLength, renderOutput); + } +} + +static bool convexHullHasScale0(const ConvexHullV& convexHull0, const ConvexHullV& convexHull1, const PsTransformV& transf0, const PsTransformV& transf1, + const PsMatTransformV& aToB, GjkOutput& output, PersistentContactManifold& manifold, ContactBuffer& contactBuffer, + const PxU32 initialContacts, const FloatV minMargin, const FloatV contactDist, + const bool idtScale1, const PxReal toleranceLength, Cm::RenderOutput* renderOutput) +{ + + RelativeConvex convexA(convexHull0, aToB); + if(idtScale1) + { + LocalConvex convexB(static_cast(convexHull1)); + GjkStatus status = gjkPenetration< RelativeConvex, LocalConvex >(convexA, convexB, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints,output); + + return generateOrProcessContactsConvexConvex(&convexA, &convexB, transf0, transf1, aToB, status, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, false, true, toleranceLength, renderOutput); + + } + else + { + LocalConvex convexB(convexHull1); + GjkStatus status = gjkPenetration, LocalConvex >(convexA, convexB, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + return generateOrProcessContactsConvexConvex(&convexA, &convexB, transf0, transf1, aToB, status, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, false, false, toleranceLength, renderOutput); + } +} + + +bool pcmContactConvexConvex(GU_CONTACT_METHOD_ARGS) +{ + const PxConvexMeshGeometryLL& shapeConvex0 = shape0.get(); + const PxConvexMeshGeometryLL& shapeConvex1 = shape1.get(); + PersistentContactManifold& manifold = cache.getManifold(); + + Ps::prefetchLine(shapeConvex0.hullData); + Ps::prefetchLine(shapeConvex1.hullData); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V vScale0 = V3LoadU_SafeReadW(shapeConvex0.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const Vec3V vScale1 = V3LoadU_SafeReadW(shapeConvex1.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const FloatV contactDist = FLoad(params.mContactDistance); + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const Gu::ConvexHullData* hullData0 = shapeConvex0.hullData; + const Gu::ConvexHullData* hullData1 = shapeConvex1.hullData; + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV convexMargin0 = Gu::CalculatePCMConvexMargin(hullData0, vScale0, toleranceLength); + const FloatV convexMargin1 = Gu::CalculatePCMConvexMargin(hullData1, vScale1, toleranceLength); + + const PxU32 initialContacts = manifold.mNumContacts; + + const FloatV minMargin = FMin(convexMargin0, convexMargin1); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, contactDist); + + //ML: after refreshContactPoints, we might lose some contacts + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + if(bLostContacts || manifold.invalidate_BoxConvex(curRTrans, minMargin)) + { + manifold.setRelativeTransform(curRTrans); + + const bool idtScale0 = shapeConvex0.scale.isIdentity(); + const bool idtScale1 = shapeConvex1.scale.isIdentity(); + const QuatV vQuat0 = QuatVLoadU(&shapeConvex0.scale.rotation.x); + const QuatV vQuat1 = QuatVLoadU(&shapeConvex1.scale.rotation.x); + + Gu::ConvexHullV convexHull0(hullData0, V3LoadU(hullData0->mCenterOfMass), vScale0, vQuat0, idtScale0); + Gu::ConvexHullV convexHull1(hullData1, V3LoadU(hullData1->mCenterOfMass), vScale1, vQuat1, idtScale1); + + GjkOutput output; + + if(idtScale0) + { + return convexHullNoScale0(convexHull0, convexHull1, transf0, transf1, aToB, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, idtScale1, toleranceLength, renderOutput); + } + else + { + return convexHullHasScale0(convexHull0, convexHull1, transf0, transf1, aToB, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, idtScale1, toleranceLength, renderOutput); + } + + } + else if(manifold.getNumContacts()> 0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + + return false; + +} +} +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp new file mode 100644 index 000000000..afcceee2b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp @@ -0,0 +1,267 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMShapeConvex.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuHeightField.h" +#include "GuHeightFieldUtil.h" +#include "GuPCMContactConvexCommon.h" +#include "GuPCMContactMeshCallback.h" + +#include "PsVecMath.h" + + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMConvexVsHeightfieldContactGenerationCallback + : PCMHeightfieldContactGenerationCallback< PCMConvexVsHeightfieldContactGenerationCallback > +{ + PCMConvexVsHeightfieldContactGenerationCallback& operator=(const PCMConvexVsHeightfieldContactGenerationCallback&); +public: + PCMConvexVsMeshContactGeneration mGeneration; + + PCMConvexVsHeightfieldContactGenerationCallback( + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Gu::PolygonalData& polyData, + SupportLocal* polyMap, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale, + const PsTransformV& convexTransform, + const PsTransformV& heightfieldTransform, + const PxTransform& heightfieldTransform1, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Gu::HeightFieldUtil& hfUtil, + Ps::InlineArray* delayedContacts, + bool silhouetteEdgesAreActive, + Cm::RenderOutput* renderOutput = NULL + + ) : + PCMHeightfieldContactGenerationCallback< PCMConvexVsHeightfieldContactGenerationCallback >(hfUtil, heightfieldTransform1), + mGeneration(contactDistance, replaceBreakingThreshold, convexTransform, heightfieldTransform, multiManifold, + contactBuffer, polyData, polyMap, delayedContacts, convexScaling, idtConvexScale, silhouetteEdgesAreActive, renderOutput) + { + } + + template + void processTriangleCache(Gu::TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + +bool Gu::PCMContactConvexHeightfield( + const Gu::PolygonalData& polyData, Gu::SupportLocal* polyMap, const Ps::aos::FloatVArg minMargin, + const PxBounds3& hullAABB, const PxHeightFieldGeometry& shapeHeightfield, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, Gu::ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, bool idtConvexScale, + Gu::MultiplePersistentContactManifold& multiManifold, Cm::RenderOutput* renderOutput) + +{ + + using namespace Ps::aos; + using namespace Gu; + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV contactDist = FLoad(contactDistance); + //Transfer A into the local space of B + const PsTransformV convexTransform(p0, q0);//box + const PsTransformV heightfieldTransform(p1, q1);//heightfield + const PsTransformV curTransform = heightfieldTransform.transformInv(convexTransform); + + + if(multiManifold.invalidate(curTransform, minMargin)) + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + //////////////////// + + const PxTransform t0to1 = transform1.transformInv(transform0); + + Gu::HeightFieldUtil hfUtil(shapeHeightfield); + const Gu::HeightField& hf = hfUtil.getHeightField(); + + //////////////////// + + /*const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + + const PxU8* PX_RESTRICT extraData = meshData->mExtraTrigData;*/ + + Ps::InlineArray delayedContacts; + + PCMConvexVsHeightfieldContactGenerationCallback blockCallback( + contactDist, + replaceBreakingThreshold, + polyData, + polyMap, + convexScaling, + idtConvexScale, + convexTransform, + heightfieldTransform, + transform1, + multiManifold, + contactBuffer, + hfUtil, + &delayedContacts, + !(hf.getFlags() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES), + renderOutput + ); + + hfUtil.overlapAABBTriangles(transform1, PxBounds3::transformFast(t0to1, hullAABB), 0, &blockCallback); + + PX_ASSERT(multiManifold.mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + blockCallback.mGeneration.generateLastContacts(); + blockCallback.mGeneration.processContacts(GU_SINGLE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.6f)); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, contactDist); + } + +#if PCM_LOW_LEVEL_DEBUG + multiManifold.drawManifold(*renderOutput, convexTransform, heightfieldTransform); +#endif + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, heightfieldTransform); + +} + + +bool Gu::pcmContactConvexHeightField(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + const PxConvexMeshGeometryLL& shapeConvex = shape0.get(); + const physx::PxHeightFieldGeometryLL& shapHeightField = shape1.get(); + + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + Gu::MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const PsTransformV convexTransform(p0, q0); + + //const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + //Cm::FastVertex2ShapeScaling meshScaling; + //if(!idtScaleMesh) + // meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData; + const bool idtScaleConvex = getPCMConvexData(shape0, convexScaling, hullAABB, polyData); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV minMargin = Gu::CalculatePCMConvexMargin(hullData, vScale, toleranceLength, GU_PCM_MESH_MANIFOLD_EPSILON); + + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + Gu::ConvexHullV convexHull(hullData, V3Zero(), vScale, vQuat, shapeConvex.scale.isIdentity()); + + if(idtScaleConvex) + { + SupportLocalImpl convexMap(static_cast(convexHull), convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex); + return Gu::PCMContactConvexHeightfield(polyData, &convexMap, minMargin, hullAABB, shapHeightField, transform0, transform1, params.mContactDistance, contactBuffer, convexScaling, + idtScaleConvex, multiManifold, renderOutput); + } + else + { + SupportLocalImpl convexMap(convexHull, convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex); + return Gu::PCMContactConvexHeightfield(polyData, &convexMap, minMargin, hullAABB, shapHeightField, transform0, transform1, params.mContactDistance, contactBuffer, convexScaling, + idtScaleConvex, multiManifold, renderOutput); + } +} + +bool Gu::pcmContactBoxHeightField(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const PxBoxGeometry& shapeBox = shape0.get(); + const physx::PxHeightFieldGeometryLL& shapHeightField = shape1.get(); + + const PxVec3 ext = shapeBox.halfExtents + PxVec3(params.mContactDistance); + const PxBounds3 hullAABB(-ext, ext); + + Cm::FastVertex2ShapeScaling idtScaling; + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const PxReal toranceLength = params.mToleranceLength; + const FloatV minMargin = Gu::CalculatePCMBoxMargin(boxExtents, toranceLength, GU_PCM_MESH_MANIFOLD_EPSILON); + + Gu::BoxV boxV(V3Zero(), boxExtents); + + const PsTransformV boxTransform(p0, q0);//box + + Gu::PolygonalData polyData; + Gu::PCMPolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData); + + Mat33V identity = M33Identity(); + //SupportLocalImpl boxMap(boxV, boxTransform, identity, identity); + SupportLocalImpl boxMap(boxV, boxTransform, identity, identity, true); + + return Gu::PCMContactConvexHeightfield(polyData, &boxMap, minMargin, hullAABB, shapHeightField, transform0, transform1, params.mContactDistance, contactBuffer, + idtScaling, true, multiManifold, renderOutput); +} +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp new file mode 100644 index 000000000..07ca96521 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp @@ -0,0 +1,259 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMShapeConvex.h" +#include "GuConvexUtilsInternal.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuPCMContactConvexCommon.h" +#include "GuPCMContactMeshCallback.h" +#include "GuIntersectionTriangleBox.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMConvexVsMeshContactGenerationCallback : PCMMeshContactGenerationCallback +{ + PCMConvexVsMeshContactGenerationCallback& operator=(const PCMConvexVsMeshContactGenerationCallback&); +public: + PCMConvexVsMeshContactGeneration mGeneration; + const BoxPadded& mBox; + + PCMConvexVsMeshContactGenerationCallback( + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const PsTransformV& convexTransform, + const PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + const PolygonalData& polyData, + SupportLocal* polyMap, + Ps::InlineArray* delayedContacts, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale, + const Cm::FastVertex2ShapeScaling& meshScaling, + const PxU8* extraTriData, + bool idtMeshScale, + bool silhouetteEdgesAreActive, + const BoxPadded& box, + Cm::RenderOutput* renderOutput = NULL + + ) : + PCMMeshContactGenerationCallback(meshScaling, extraTriData, idtMeshScale), + mGeneration(contactDistance, replaceBreakingThreshold, convexTransform, meshTransform, multiManifold, contactBuffer, polyData, + polyMap, delayedContacts, convexScaling, idtConvexScale, silhouetteEdgesAreActive, renderOutput), + mBox(box) + { + } + + PX_FORCE_INLINE Ps::IntBool doTest(const PxVec3& v0, const PxVec3& v1, const PxVec3& v2) + { + // PT: this one is safe because midphase vertices are directly passed to the function + return intersectTriangleBox(mBox, v0, v1, v2); + } + + template + void processTriangleCache(TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + + +bool Gu::PCMContactConvexMesh(const PolygonalData& polyData, SupportLocal* polyMap, const Ps::aos::FloatVArg minMargin, const PxBounds3& hullAABB, const PxTriangleMeshGeometryLL& shapeMesh, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtConvexScale, bool idtMeshScale, Gu::MultiplePersistentContactManifold& multiManifold, + Cm::RenderOutput* renderOutput) + +{ + using namespace Ps::aos; + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV contactDist = FLoad(contactDistance); + //Transfer A into the local space of B + const PsTransformV convexTransform(p0, q0);//box + const PsTransformV meshTransform(p1, q1);//triangleMesh + const PsTransformV curTransform = meshTransform.transformInv(convexTransform); + + + if(multiManifold.invalidate(curTransform, minMargin)) + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + + //////////////////// + const TriangleMesh* PX_RESTRICT meshData = shapeMesh.meshData; + + const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + BoxPadded hullOBB; + computeHullOBB(hullOBB, hullAABB, contactDistance, world0, world1, meshScaling, idtMeshScale); + + // Setup the collider + + Ps::InlineArray delayedContacts; + + const PxU8* PX_RESTRICT extraData = meshData->getExtraTrigData(); + PCMConvexVsMeshContactGenerationCallback blockCallback( + contactDist, replaceBreakingThreshold, convexTransform, meshTransform, multiManifold, contactBuffer, + polyData, polyMap, &delayedContacts, convexScaling, idtConvexScale, meshScaling, extraData, idtMeshScale, true, + hullOBB, renderOutput); + + Midphase::intersectOBB(meshData, hullOBB, blockCallback, true); + + PX_ASSERT(multiManifold.mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + + blockCallback.flushCache(); + //This is very important + blockCallback.mGeneration.generateLastContacts(); + blockCallback.mGeneration.processContacts(GU_SINGLE_MANIFOLD_CACHE_SIZE, false); + +#if PCM_LOW_LEVEL_DEBUG + multiManifold.drawManifold(*renderOutput, transform0, transform1); +#endif + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, contactDist); + } + + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, meshTransform); +} + +bool Gu::pcmContactConvexMesh(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + PX_UNUSED(renderOutput); + + const PxConvexMeshGeometryLL& shapeConvex = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + const ConvexHullData* hullData = shapeConvex.hullData; + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const PsTransformV convexTransform = loadTransformA(transform0); + + const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData; + const bool idtScaleConvex = getPCMConvexData(shape0, convexScaling, hullAABB, polyData); + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + + const PxReal toleranceScale = params.mToleranceLength; + const FloatV minMargin = CalculatePCMConvexMargin(hullData, vScale, toleranceScale, GU_PCM_MESH_MANIFOLD_EPSILON); + + ConvexHullV convexHull(hullData, V3Zero(), vScale, vQuat, idtScaleConvex); + + if(idtScaleConvex) + { + SupportLocalImpl convexMap(static_cast(convexHull), convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, true); + return Gu::PCMContactConvexMesh(polyData, &convexMap, minMargin, hullAABB, shapeMesh,transform0,transform1, params.mContactDistance, contactBuffer, convexScaling, + meshScaling, idtScaleConvex, idtScaleMesh, multiManifold, renderOutput); + } + else + { + SupportLocalImpl convexMap(convexHull, convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, false); + return Gu::PCMContactConvexMesh(polyData, &convexMap, minMargin, hullAABB, shapeMesh,transform0,transform1, params.mContactDistance, contactBuffer, convexScaling, + meshScaling, idtScaleConvex, idtScaleMesh, multiManifold, renderOutput); + } +} + +bool Gu::pcmContactBoxMesh(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + PX_UNUSED(renderOutput); + + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const PxBoxGeometry& shapeBox = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + const PxBounds3 hullAABB(-shapeBox.halfExtents, shapeBox.halfExtents); + + const bool idtMeshScale = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtMeshScale) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling idtScaling; + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + const PxReal toleranceLength = params.mToleranceLength; + const FloatV minMargin = Gu::CalculatePCMBoxMargin(boxExtents, toleranceLength, GU_PCM_MESH_MANIFOLD_EPSILON); + + BoxV boxV(V3Zero(), boxExtents); + + const PsTransformV boxTransform = loadTransformA(transform0);//box + + PolygonalData polyData; + PCMPolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData); + + Mat33V identity = M33Identity(); + SupportLocalImpl boxMap(boxV, boxTransform, identity, identity, true); + + return Gu::PCMContactConvexMesh(polyData, &boxMap, minMargin, hullAABB, shapeMesh,transform0,transform1, params.mContactDistance, contactBuffer, idtScaling, meshScaling, + true, idtMeshScale, multiManifold, renderOutput); +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h new file mode 100644 index 000000000..c072acd4c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h @@ -0,0 +1,82 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_GEN_H +#define GU_PCM_CONTACT_GEN_H + + +#include "GuConvexSupportTable.h" +#include "GuPersistentContactManifold.h" +#include "GuShapeConvex.h" +#include "GuSeparatingAxes.h" +#include "GuGJKType.h" +#include "GuGJKUtil.h" + +namespace physx +{ + +namespace Gu +{ + + + //full contact gen code for box/convexhull vs convexhull + bool generateFullContactManifold(Gu::PolygonalData& polyData0, Gu::PolygonalData& polyData1, Gu::SupportLocal* map0, Gu::SupportLocal* map1, Gu::PersistentContact* manifoldContacts, + PxU32& numContacts, const Ps::aos::FloatVArg contactDist, const Ps::aos::Vec3VArg normal, const Ps::aos::Vec3VArg closestA, const Ps::aos::Vec3VArg closestB, + const PxReal toleranceA, const PxReal toleranceB, bool doOverlapTest, Cm::RenderOutput* renderOutput, const PxReal toleranceLength); + + //full contact gen code for capsule vs convexhulll + bool generateFullContactManifold(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, const Ps::aos::PsMatTransformV& aToB, Gu::PersistentContact* manifoldContacts, + PxU32& numContacts, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& normal, const Ps::aos::Vec3VArg closest, const PxReal tolerance, bool doOverlapTest, const PxReal toleranceScale); + + //full contact gen code for capsule vs box + bool generateCapsuleBoxFullContactManifold(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, const Ps::aos::PsMatTransformV& aToB, Gu::PersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& normal, const Ps::aos::Vec3VArg closest, const PxReal boxMargin, const bool doOverlapTest, const PxReal toeranceScale); + + //based on the gjk status to decide whether we should do full contact gen with GJK/EPA normal. Also, this method store + //GJK/EPA point to the manifold in case full contact gen doesn't generate any contact + bool addGJKEPAContacts(const Gu::GjkConvex* relativeConvex, const Gu::GjkConvex* localConvex, const Ps::aos::PsMatTransformV& aToB, Gu::GjkStatus status, + Gu::PersistentContact* manifoldContacts, const Ps::aos::FloatV replaceBreakingThreshold, const Ps::aos::FloatV toleranceLength, GjkOutput& output, + Gu::PersistentContactManifold& manifold); + + //MTD code for box/convexhull vs box/convexhull + bool computeMTD(Gu::PolygonalData& polyData0, Gu::PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, Ps::aos::FloatV& penDepth, Ps::aos::Vec3V& normal); + + //MTD code for capsule vs box/convexhull + bool computeMTD(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, Ps::aos::FloatV& penDepth, Ps::aos::Vec3V& normal); + + void buildPartialHull(const Gu::PolygonalData& polyData, SupportLocal* map, Gu::SeparatingAxes& validAxes, const Ps::aos::Vec3VArg v, const Ps::aos::Vec3VArg _dir); + + //full contact gen code for sphere vs convexhull + bool generateSphereFullContactManifold(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, Gu::PersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& normal, const bool doOverlapTest); + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp new file mode 100644 index 000000000..6bb333116 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp @@ -0,0 +1,811 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "CmRenderOutput.h" +#include "GuPCMContactGenUtil.h" +#include "PsVecMath.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" +#include "GuEPA.h" + + +#define PCM_USE_INTERNAL_OBJECT 1 + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + +namespace physx +{ + +namespace Gu +{ + + static bool testFaceNormal(const PolygonalData& polyData0, const PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, const PsMatTransformV& transform0To1, const PsMatTransformV& transform1To0, const FloatVArg contactDist, + FloatV& minOverlap, PxU32& feature, Vec3V& faceNormal, const FeatureStatus faceStatus, FeatureStatus& status) + { + PX_UNUSED(polyData1); + + FloatV _minOverlap = FMax();//minOverlap; + PxU32 _feature = 0; + Vec3V _faceNormal = faceNormal; + FloatV min0, max0; + FloatV min1, max1; + + const Vec3V center1To0 = transform1To0.p; + +#if PCM_USE_INTERNAL_OBJECT + const Vec3V zeroV = V3Zero(); + const Vec3V shapeSpaceCenter1 = V3LoadU(polyData1.mCenter); + const Vec3V internalCenter1In0 = transform1To0.transform(shapeSpaceCenter1); + const FloatV internalRadius1 = FLoad(polyData1.mInternal.mRadius); + const Vec3V internalExtents1 = V3LoadU(polyData1.mInternal.mExtents); + const Vec3V negInternalExtents1 = V3Neg(internalExtents1); + +#endif + + //in the local space of polyData0 + for(PxU32 i=0; ishape2Vertex, vertexSpacePlaneNormal); + + const FloatV magnitude = FRecip(V3Length(shapeSpacePlaneNormal)); + //ML::use this to avoid LHS + min0 = FMul(V3Dot(vertexSpacePlaneNormal, minVert), magnitude); + max0 = FMul(FNeg(planeDist), magnitude); + + //normalize shape space normal + const Vec3V n0 = V3Scale(shapeSpacePlaneNormal, magnitude); + + //calculate polyData1 projection + //rotate polygon's normal into the local space of polyData1 + const Vec3V n1 = transform0To1.rotate(n0); + + +#if PCM_USE_INTERNAL_OBJECT + + //test internal object + //ML: we don't need to transform the normal into the vertex space. If polyData1 don't have scale, + //the vertex2Shape matrix will be identity, shape space normal will be the same as vertex space's normal. + //If polyData0 have scale, internalExtens1 will be 0. + const Vec3V proj = V3Sel(V3IsGrtr(n1, zeroV), internalExtents1, negInternalExtents1); + const FloatV radius = FMax(V3Dot(n1, proj), internalRadius1); + const FloatV internalTrans = V3Dot(internalCenter1In0, n0); + + const FloatV _min1 = FSub(internalTrans, radius); + const FloatV _max1 = FAdd(internalTrans, radius); + + const FloatV _min = FMax(min0, _min1); + const FloatV _max = FMin(max0, _max1); + + const FloatV _tempOverlap = FSub(_max, _min); + //const FloatV _tempOverlap = FSub(max0, _min1); + + //Internal object overlaps more than current min, so can skip it + //because (a) it isn't a separating axis and (b) it isn't the smallest axis + if(FAllGrtr(_tempOverlap, _minOverlap)) + { + continue; + } + +#endif + + const FloatV translate = V3Dot(center1To0, n0); + map1->doSupport(n1, min1, max1); + + min1 = FAdd(translate, min1); + max1 = FAdd(translate, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + _feature = i; + _faceNormal = n0; + } + } + + if(FAllGrtr(minOverlap, _minOverlap)) + { + faceNormal = _faceNormal; + minOverlap = _minOverlap; + status = faceStatus; + } + + + + feature = _feature; + + return true; + + } + + + //plane is in the shape space of polyData + void buildPartialHull(const PolygonalData& polyData, SupportLocal* map, SeparatingAxes& validAxes, const Vec3VArg planeP, const Vec3VArg planeDir) + { + const FloatV zero = FZero(); + const Vec3V dir = V3Normalize(planeDir); + for(PxU32 i=0; ivertex2Shape, V3LoadU_SafeReadW(polyData.mVerts[inds[0]])); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + FloatV dist0 = V3Dot(dir, V3Sub(v0, planeP)); + + for (PxU32 iStart = 0, iEnd = PxU32(polygon.mNbVerts - 1); iStart < polygon.mNbVerts; iEnd = iStart++) + { + const Vec3V v1 = M33MulV3(map->vertex2Shape, V3LoadU_SafeReadW(polyData.mVerts[inds[iEnd]])); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + const FloatV dist1 = V3Dot(dir, V3Sub(v1, planeP)); + + const BoolV con = BOr(FIsGrtr(dist0, zero), FIsGrtr(dist1, zero)); + + //cull edge if either of the vertex will on the positive size of the plane + if(BAllEqTTTT(con)) + { + const Vec3V tempV = V3Sub(v0, v1); + PxVec3 temp; + V3StoreU(tempV, temp); + validAxes.addAxis(temp.getNormalized()); + } + + v0 = v1; + dist0 = dist1; + } + } + } + + + static bool testEdgeNormal(const PolygonalData& polyData0, const PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, const PsMatTransformV& transform0To1, const PsMatTransformV& transform1To0, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& edgeNormalIn0, const FeatureStatus edgeStatus, FeatureStatus& status) + { + + FloatV overlap = minOverlap; + FloatV min0, max0; + FloatV min1, max1; + const FloatV eps = FEps(); + + const Vec3V shapeSpaceCenter0 = V3LoadU(polyData0.mCenter); + const Vec3V shapeSpaceCenter1 = V3LoadU(polyData1.mCenter); + +#if PCM_USE_INTERNAL_OBJECT + const Vec3V zeroV = V3Zero(); + const Vec3V internalCenter1In0 = V3Sub(transform1To0.transform(shapeSpaceCenter1), shapeSpaceCenter0); + const FloatV internalRadius1 = FLoad(polyData1.mInternal.mRadius); + const Vec3V internalExtents1 = V3LoadU(polyData1.mInternal.mExtents); + const Vec3V negInternalExtents1 = V3Neg(internalExtents1); + + const FloatV internalRadius0 = FLoad(polyData0.mInternal.mRadius); + const Vec3V internalExtents0 = V3LoadU(polyData0.mInternal.mExtents); + const Vec3V negInternalExtents0 = V3Neg(internalExtents0); +#endif + + const Vec3V center1To0 = transform1To0.p; + + //in polyData0 shape space + const Vec3V dir0 = V3Sub(transform1To0.transform(shapeSpaceCenter1), shapeSpaceCenter0); + const Vec3V support0 = map0->doSupport(dir0); + + //in polyData1 shape space + const Vec3V dir1 = transform0To1.rotate(V3Neg(dir0)); + const Vec3V support1 = map1->doSupport(dir1); + + const Vec3V support0In1 = transform0To1.transform(support0); + const Vec3V support1In0 = transform1To0.transform(support1); + + SeparatingAxes mSA0; + SeparatingAxes mSA1; + mSA0.reset(); + mSA1.reset(); + + buildPartialHull(polyData0, map0, mSA0, support1In0, dir0); + buildPartialHull(polyData1, map1, mSA1, support0In1, dir1); + + const PxVec3* axe0 = mSA0.getAxes(); + const PxVec3* axe1 = mSA1.getAxes(); + const PxU32 numAxe0 = mSA0.getNumAxes(); + const PxU32 numAxe1 = mSA1.getNumAxes(); + for(PxU32 i=0; i < numAxe0; ++i) + { + //axe0[i] is in the shape space of polyData0 + const Vec3V v0 = V3LoadU(axe0[i]); + + for(PxU32 j=0; j< numAxe1; ++j) + { + //axe1[j] is in the shape space of polyData1 + const Vec3V v1 = V3LoadU(axe1[j]); + + const Vec3V dir = V3Cross(v0, transform1To0.rotate(v1)); + const FloatV lenSq = V3Dot(dir, dir); + if(FAllGrtr(eps, lenSq)) + continue; + + //n0 is in polyData0's local space + const Vec3V n0 = V3Scale(dir, FRsqrt(lenSq)); + + //n1 is in polyData1's local space + const Vec3V n1 = transform0To1.rotate(n0); + +#if PCM_USE_INTERNAL_OBJECT + + //ML: we don't need to transform the normal into the vertex space. If polyData1 don't have scale, + //the vertex2Shape matrix will be identity, shape space normal will be the same as vertex space's normal. + //If polyData0 have scale, internalExtens1 will be 0. + //vertex space n1 + const Vec3V proj = V3Sel(V3IsGrtr(n1, zeroV), internalExtents1, negInternalExtents1); + const FloatV radius = FMax(V3Dot(n1, proj), internalRadius1); + + const FloatV internalTrans = V3Dot(internalCenter1In0, n0); + + const FloatV _min1 = FSub(internalTrans, radius); + const FloatV _max1 = FAdd(internalTrans, radius); + + const Vec3V proj0 = V3Sel(V3IsGrtr(n0, zeroV), internalExtents0, negInternalExtents0); + const FloatV radius0 = FMax(V3Dot(n0, proj0), internalRadius0); + + const FloatV _max0 = radius0; + const FloatV _min0 = FNeg(radius0); + + PX_ASSERT(FAllGrtrOrEq(_max0, _min0)); + PX_ASSERT(FAllGrtrOrEq(_max1, _min1)); + + + const FloatV _min = FMax(_min0, _min1); + const FloatV _max = FMin(_max0, _max1); + + const FloatV _tempOverlap = FSub(_max, _min); + + //Internal object overlaps more than current min, so can skip it + //because (a) it isn't a separating axis and (b) it isn't the smallest axis + if(FAllGrtr(_tempOverlap, overlap)) + { + continue; + } + +#endif + //get polyData0's projection + map0->doSupport(n0, min0, max0); + + const FloatV translate = V3Dot(center1To0, n0); + + //get polyData1's projection + map1->doSupport(n1, min1, max1); + + min1 = FAdd(translate, min1); + max1 = FAdd(translate, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + +#if PCM_USE_INTERNAL_OBJECT + PX_ASSERT(FAllGrtrOrEq(tempOverlap, _tempOverlap)); +#endif + + if(FAllGrtr(overlap, tempOverlap)) + { + overlap = tempOverlap; + edgeNormalIn0 = n0; + status = edgeStatus; + } + } + } + + minOverlap = overlap; + + return true; + + } + + + //contactNormal is in the space of polyData0 + void generatedContacts(PolygonalData& polyData0, PolygonalData& polyData1, const HullPolygonData& referencePolygon, const HullPolygonData& incidentPolygon, + SupportLocal* map0, SupportLocal* map1, const PsMatTransformV& transform0To1, PersistentContact* manifoldContacts, + PxU32& numContacts, const FloatVArg contactDist, Cm::RenderOutput* renderOutput) + { + + PX_UNUSED(renderOutput); + const FloatV zero = FZero(); + + const PxU8* inds0 = polyData0.mPolygonVertexRefs + referencePolygon.mVRef8; + + //transform the plane normal to shape space + const Vec3V contactNormal = V3Normalize(M33TrnspsMulV3(map0->shape2Vertex, V3LoadU(referencePolygon.mPlane.n))); + + //this is the matrix transform all points to the 2d plane + const Mat33V rot = findRotationMatrixFromZAxis(contactNormal); + + const PxU8* inds1 = polyData1.mPolygonVertexRefs + incidentPolygon.mVRef8; + + + Vec3V* points0In0 = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + Vec3V* points1In0 = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*incidentPolygon.mNbVerts, 16)); + bool* points1In0Penetration = reinterpret_cast(PxAlloca(sizeof(bool)*incidentPolygon.mNbVerts)); + FloatV* points1In0TValue = reinterpret_cast(PxAllocaAligned(sizeof(FloatV)*incidentPolygon.mNbVerts, 16)); + + //Transform all the verts from vertex space to shape space + map0->populateVerts(inds0, referencePolygon.mNbVerts, polyData0.mVerts, points0In0); + map1->populateVerts(inds1, incidentPolygon.mNbVerts, polyData1.mVerts, points1In0); + +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map0->transform, points0In0, (PxU32)referencePolygon.mNbVerts, 0x00ff0000); + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map1->transform, points1In0, (PxU32)incidentPolygon.mNbVerts, 0x0000ff00); +#endif + + //This is used to calculate the project point when the 2D reference face points is inside the 2D incident face point + const Vec3V sPoint = points1In0[0]; + + PX_ASSERT(incidentPolygon.mNbVerts <= 64); + + Vec3V eps = Vec3V_From_FloatV(FEps()); + Vec3V max = Vec3V_From_FloatV(FMax()); + Vec3V nmax = V3Neg(max); + + //transform reference polygon to 2d, calculate min and max + Vec3V rPolygonMin= max; + Vec3V rPolygonMax = nmax; + for(PxU32 i=0; ishape2Vertex, V3LoadU(incidentPolygon.mPlane.n))); + + const Vec3V contactNormalIn1 = transform0To1.rotate(contactNormal); + + for(PxU32 i=0; itransform.transformInv(map1->transform); + const PsMatTransformV transform0To1V = map1->transform.transformInv(map0->transform); + + + if(doOverlapTest) + { + //if gjk fail, SAT based yes/no test + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + Vec3V minNormal = V3Zero(); + + PxU32 feature0; + //in the local space of polyData0, minNormal is in polyData0 space + if(!testFaceNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + //in the local space of polyData1, if minOverlap is overwrite inside this function, minNormal will be in polyData1 space + if(!testFaceNormal(polyData1, polyData0, map1, map0, transform1To0V, transform0To1V, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + + bool doEdgeTest = false; + +EdgeTest: + if(doEdgeTest) + { + + if(!testEdgeNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + if(status != EDGE) + return true; + } + + + if(status == POLYDATA0) + { + //minNormal is in the local space of polydata0 + + const HullPolygonData& referencePolygon = polyData0.mPolygons[feature0]; + const Vec3V n = transform0To1V.rotate(minNormal); + const HullPolygonData& incidentPolygon = polyData1.mPolygons[getPolygonIndex(polyData1, map1, n)]; + + generatedContacts(polyData0, polyData1, referencePolygon, incidentPolygon, map0, map1, transform0To1V, manifoldContacts, numContacts, contactDist, renderOutput); + + if (numContacts > 0) + { + const Vec3V nn = V3Neg(n); + //flip the contacts + for(PxU32 i=0; ishape2Vertex, V3LoadU(referencePolygon.mPlane.n))); + const Vec3V incidentNormal = V3Normalize(M33TrnspsMulV3(map0->shape2Vertex, V3LoadU(incidentPolygon.mPlane.n))); + + const FloatV referenceProject = FAbs(V3Dot(referenceNormal, negNormal)); + const FloatV incidentProject = FAbs(V3Dot(incidentNormal, normalIn0)); + + if (FAllGrtrOrEq(referenceProject, incidentProject)) + { + generatedContacts(polyData1, polyData0, referencePolygon, incidentPolygon, map1, map0, transform1To0V, manifoldContacts, numContacts, contactDist, renderOutput); + } + else + { + generatedContacts(polyData0, polyData1, incidentPolygon, referencePolygon, map0, map1, transform0To1V, manifoldContacts, numContacts, contactDist, renderOutput); + + if (numContacts > 0) + { + const Vec3V n = transform0To1V.rotate(incidentNormal); + + const Vec3V nn = V3Neg(n); + //flip the contacts + for (PxU32 i = 0; igetCenter(); + const Vec3V centreB = localConvex->getCenter(); + + const Vec3V dir = V3Normalize(V3Sub(centreA, centreB)); + const FloatV theta = V3Dot(dir, normal); + + if (FAllGrtr(theta, FLoad(0.707f))) + { + addManifoldPoint(manifoldContacts, manifold, output, aToB, replaceBreakingThreshold); + } + else + { + doOverlapTest = true; + } + } + else + { + doOverlapTest = true; + } + } + else if (status == GJK_CONTACT) + { + addManifoldPoint(manifoldContacts, manifold, output, aToB, replaceBreakingThreshold); + } + else + { + PX_ASSERT(status == EPA_CONTACT); + + status = epaPenetration(*relativeConvex, *localConvex, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, tolerenceLength, output); + if (status == EPA_CONTACT) + { + addManifoldPoint(manifoldContacts, manifold, output, aToB, replaceBreakingThreshold); + } + else + { + doOverlapTest = true; + } + } + + return doOverlapTest; + } + + + bool computeMTD(Gu::PolygonalData& polyData0, Gu::PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, Ps::aos::FloatV& penDepth, Ps::aos::Vec3V& normal) + { + + using namespace Ps::aos; + + const PsMatTransformV transform1To0V = map0->transform.transformInv(map1->transform); + const PsMatTransformV transform0To1V = map1->transform.transformInv(map0->transform); + + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + Vec3V minNormal = V3Zero(); + const FloatV contactDist = FZero(); + + PxU32 feature0; + //in the local space of polyData0, minNormal is in polyData0 space + if(!testFaceNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + //in the local space of polyData1, if minOverlap is overwrite inside this function, minNormal will be in polyData1 space + if(!testFaceNormal(polyData1, polyData0, map1, map0, transform1To0V, transform0To1V, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + if(!testEdgeNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + penDepth = minOverlap; + if(status == POLYDATA1) + { + //minNormal is in the local space of polydata1 + normal = map1->transform.rotate(minNormal); + } + else + { + PX_ASSERT(status == POLYDATA0 || status == EDGE); + //ML: status == POLYDATA0 or status == EDGE, minNormal is in the local space of polydata0 + normal = V3Neg(map0->transform.rotate(minNormal)); + } + + return true; + } + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp new file mode 100644 index 000000000..25d92d74c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp @@ -0,0 +1,444 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "CmRenderOutput.h" +#include "GuPCMContactGenUtil.h" +#include "PsVecMath.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + + + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + +namespace physx +{ + +namespace Gu +{ + + static bool testSATCapsulePoly(const CapsuleV& capsule, const PolygonalData& polyData, SupportLocal* map, const FloatVArg contactDist, FloatV& minOverlap, Vec3V& separatingAxis) + { + using namespace Ps::aos; + FloatV _minOverlap = FMax();//minOverlap; + FloatV min0, max0; + FloatV min1, max1; + Vec3V tempAxis = V3UnitY(); + const FloatV eps = FEps(); + + //ML: project capsule to polydata axis + if(!testPolyDataAxis(capsule, polyData, map, contactDist, _minOverlap, tempAxis)) + return false; + + const Vec3V capsuleAxis = V3Sub(capsule.p1, capsule.p0); + + for(PxU32 i=0;ishape2Vertex, vertexSpaceV); + + const Vec3V dir = V3Cross(capsuleAxis, shapeSpaceV); + const FloatV lenSq = V3Dot(dir, dir); + if(FAllGrtr(eps, lenSq)) + continue; + const Vec3V normal = V3ScaleInv(dir, FSqrt(lenSq)); + + map->doSupport(normal, min0, max0); + + const FloatV tempMin = V3Dot(capsule.p0, normal); + const FloatV tempMax = V3Dot(capsule.p1, normal); + min1 = FMin(tempMin, tempMax); + max1 = FMax(tempMin, tempMax); + + min1 = FSub(min1, capsule.radius); + max1 = FAdd(max1, capsule.radius); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + tempAxis = normal; + } + } + } + + + separatingAxis = tempAxis; + minOverlap = _minOverlap; + + return true; + + } + + + void generatedCapsuleBoxFaceContacts(const CapsuleV& capsule, PolygonalData& polyData, const HullPolygonData& referencePolygon, SupportLocal* map, const PsMatTransformV& aToB, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, const Vec3VArg normal) + { + + const FloatV radius = FAdd(capsule.radius, contactDist); + + //calculate the intersect point of ray to plane + const Vec3V planeNormal = V3Normalize(M33TrnspsMulV3(map->shape2Vertex, V3LoadU(referencePolygon.mPlane.n))); + const PxU8* inds = polyData.mPolygonVertexRefs + referencePolygon.mVRef8; + const Vec3V a = M33MulV3(map->vertex2Shape, V3LoadU_SafeReadW(polyData.mVerts[inds[0]])); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + const FloatV denom0 = V3Dot(planeNormal, V3Sub(capsule.p0, a)); + const FloatV denom1 = V3Dot(planeNormal, V3Sub(capsule.p1, a)); + const FloatV projPlaneN = V3Dot(planeNormal, normal); + const FloatV numer = FSel(FIsGrtr(projPlaneN, FZero()), FRecip(projPlaneN), FZero()); + const FloatV t0 = FMul(denom0, numer); + const FloatV t1 = FMul(denom1, numer); + + const BoolV con0 = FIsGrtrOrEq(radius, t0); + const BoolV con1 = FIsGrtrOrEq(radius, t1); + if(BAllEqTTTT(BOr(con0, con1))) + { + + const Mat33V rot = findRotationMatrixFromZAxis(planeNormal); + Vec3V* points0In0 = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + map->populateVerts(inds, referencePolygon.mNbVerts, polyData.mVerts, points0In0); + + Vec3V rPolygonMin = V3Splat(FMax()); + Vec3V rPolygonMax = V3Neg(rPolygonMin); + for(PxU32 i=0; ishape2Vertex, dir); + const Vec3V p0 = M33MulV3(map->shape2Vertex, capsule.p0); + + if(intersectSegmentPolyhedron(p0, vertexSpaceDir, polyData, tEnter, tExit) && FAllGrtrOrEq(inflatedRadius, tEnter)) + { + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(capsule.p0); + manifoldContacts[numContacts].mLocalPointB = V3ScaleAdd(dir, tEnter, capsule.p0); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), tEnter); + } + + const Vec3V p1 = M33MulV3(map->shape2Vertex, capsule.p1); + if(intersectSegmentPolyhedron(p1, vertexSpaceDir, polyData, tEnter, tExit) && FAllGrtrOrEq(inflatedRadius, tEnter)) + { + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(capsule.p1); + manifoldContacts[numContacts].mLocalPointB = V3ScaleAdd(dir, tEnter, capsule.p1); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), tEnter); + } + + } + + + static void generateEE(const Vec3VArg p, const Vec3VArg q, const Vec3VArg normal, const Vec3VArg a, const Vec3VArg b, const PsMatTransformV& aToB, + PersistentContact* manifoldContacts, PxU32& numContacts, const FloatVArg inflatedRadius) + { + + const FloatV zero = FZero(); + const FloatV expandedRatio = FLoad(0.005f); + const Vec3V ab = V3Sub(b, a); + const Vec3V n = V3Cross(ab, normal); + const FloatV d = V3Dot(n, a); + const FloatV np = V3Dot(n, p); + const FloatV nq = V3Dot(n,q); + const FloatV signP = FSub(np, d); + const FloatV signQ = FSub(nq, d); + const FloatV temp = FMul(signP, signQ); + if(FAllGrtr(temp, zero)) return;//If both points in the same side as the plane, no intersect points + + // if colliding edge (p3,p4) and plane are parallel return no collision + const Vec3V pq = V3Sub(q, p); + const FloatV npq= V3Dot(n, pq); + if(FAllEq(npq, zero)) return; + + + //calculate the intersect point in the segment pq with plane n(x - a). + const FloatV segTValue = FDiv(FSub(d, np), npq); + const Vec3V localPointA = V3ScaleAdd(pq, segTValue, p); + + //ML: ab, localPointA and normal is in the same plane, so that we can do 2D segment segment intersection + //calculate a normal perpendicular to ray localPointA + normal*t, then do 2D segment segment intersection + const Vec3V perNormal = V3Cross(normal, pq); + const Vec3V ap = V3Sub(localPointA, a); + const FloatV nom = V3Dot(perNormal, ap); + const FloatV denom = V3Dot(perNormal, ab); + + const FloatV tValue = FDiv(nom, denom); + + const FloatV max = FAdd(FOne(), expandedRatio); + const FloatV min = FSub(zero, expandedRatio); + if(FAllGrtr(tValue, max) || FAllGrtr(min, tValue)) + return; + + const Vec3V v = V3NegScaleSub(ab, tValue, ap); + const FloatV signedDist = V3Dot(v, normal); + if(FAllGrtrOrEq(inflatedRadius, signedDist)) + { + const Vec3V localPointB = V3Sub(localPointA, v); + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(localPointA); + manifoldContacts[numContacts].mLocalPointB = localPointB; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), signedDist); + + } + } + + + void generatedContactsEEContacts(const CapsuleV& capsule, PolygonalData& polyData, const HullPolygonData& referencePolygon, SupportLocal* map, const PsMatTransformV& aToB, + PersistentContact* manifoldContacts, PxU32& numContacts, const FloatVArg contactDist, const Vec3VArg contactNormal) + { + + const PxU8* inds = polyData.mPolygonVertexRefs + referencePolygon.mVRef8; + + Vec3V* points0In0 = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + + + //Transform all the verts from vertex space to shape space + map->populateVerts(inds, referencePolygon.mNbVerts, polyData.mVerts, points0In0); + + const FloatV inflatedRadius = FAdd(capsule.radius, contactDist); + + for (PxU32 rStart = 0, rEnd = PxU32(referencePolygon.mNbVerts - 1); rStart < referencePolygon.mNbVerts; rEnd = rStart++) + { + generateEE(capsule.p0, capsule.p1, contactNormal,points0In0[rStart], points0In0[rEnd], aToB, manifoldContacts, numContacts, inflatedRadius); + + } + } + + + bool generateCapsuleBoxFullContactManifold(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, const PsMatTransformV& aToB, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, Vec3V& normal, const Vec3VArg closest, const PxReal margin, const bool doOverlapTest, const PxReal toleranceScale) + { + + const PxU32 originalContacts = numContacts; + + const Gu::HullPolygonData* referencePolygon = NULL; + + if(doOverlapTest) + { + Ps::aos::FloatV minOverlap; + //overwrite the normal + if(!testSATCapsulePoly(capsule, polyData, map, contactDist, minOverlap, normal)) + return false; + + referencePolygon = &polyData.mPolygons[getPolygonIndex(polyData, map, V3Neg(normal))]; + } + else + { + const PxReal lowerEps = toleranceScale * PCM_WITNESS_POINT_LOWER_EPS; + const PxReal upperEps = toleranceScale * PCM_WITNESS_POINT_UPPER_EPS; + const PxReal tolerance = PxClamp(margin, lowerEps, upperEps); + + const PxU32 featureIndex = getWitnessPolygonIndex(polyData, map, V3Neg(normal), closest, tolerance); + referencePolygon = &polyData.mPolygons[featureIndex]; + + } + + generatedCapsuleBoxFaceContacts(capsule, polyData, *referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, normal); + const PxU32 faceContacts = numContacts - originalContacts; + if(faceContacts < 2) + { + generatedContactsEEContacts(capsule, polyData, *referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, normal); + } + + return true; + } + + //capsule is in the local space of polyData + bool generateFullContactManifold(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, const PsMatTransformV& aToB, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, Vec3V& normal, const Vec3VArg closest, const PxReal margin, const bool doOverlapTest, const PxReal toleranceLength) + { + const PxU32 originalContacts = numContacts; + + Vec3V tNormal = normal; + + if(doOverlapTest) + { + FloatV minOverlap; + + //overwrite the normal with the sperating axis + if(!testSATCapsulePoly(capsule, polyData, map, contactDist, minOverlap, tNormal)) + return false; + + generatedFaceContacts(capsule, polyData, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + + const PxU32 faceContacts = numContacts - originalContacts; + if(faceContacts < 2) + { + const Gu::HullPolygonData& referencePolygon = polyData.mPolygons[getPolygonIndex(polyData, map, V3Neg(tNormal))]; + generatedContactsEEContacts(capsule, polyData,referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + } + } + else + { + generatedFaceContacts(capsule, polyData, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + + const PxU32 faceContacts = numContacts - originalContacts; + if(faceContacts < 2) + { + + const PxReal lowerEps = toleranceLength * PCM_WITNESS_POINT_LOWER_EPS; + const PxReal upperEps = toleranceLength * PCM_WITNESS_POINT_UPPER_EPS; + const PxReal tolerance = PxClamp(margin, lowerEps, upperEps); + + const PxU32 featureIndex = getWitnessPolygonIndex(polyData, map, V3Neg(tNormal), closest, tolerance); + const Gu::HullPolygonData& referencePolygon = polyData.mPolygons[featureIndex]; + generatedContactsEEContacts(capsule, polyData,referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + } + } + + normal = tNormal; + + return true; + } + + //sphere is in the local space of polyData, we treate sphere as capsule + bool generateSphereFullContactManifold(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, Vec3V& normal, const bool doOverlapTest) + { + + if(doOverlapTest) + { + FloatV minOverlap; + //overwrite the normal with the sperating axis + if(!testPolyDataAxis(capsule, polyData, map, contactDist, minOverlap, normal)) + return false; + } + + const FloatV zero = FZero(); + FloatV tEnter=zero, tExit=zero; + const FloatV inflatedRadius = FAdd(capsule.radius, contactDist); + const Vec3V dir = V3Neg(normal); + const Vec3V vertexSpaceDir = M33MulV3(map->shape2Vertex, dir); + const Vec3V p0 = M33MulV3(map->shape2Vertex, capsule.p0); + if(intersectSegmentPolyhedron(p0, vertexSpaceDir, polyData, tEnter, tExit) && FAllGrtrOrEq(inflatedRadius, tEnter)) + { + //ML: for sphere, the contact point A is always zeroV in its local space + manifoldContacts[numContacts].mLocalPointA = V3Zero(); + manifoldContacts[numContacts].mLocalPointB = V3ScaleAdd(dir, tEnter, capsule.p0); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), tEnter); + } + + return true; + + } + + + //capsule is in the shape space of polyData + bool computeMTD(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, FloatV& penDepth, Vec3V& normal) + { + const FloatV contactDist = FZero(); + Vec3V separatingAxis; + FloatV minOverlap; + if(!testSATCapsulePoly(capsule, polyData, map, contactDist, minOverlap, separatingAxis)) + return false; + + //transform normal in to the worl space + normal = map->transform.rotate(separatingAxis); + penDepth = minOverlap; + + return true; + } + + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h new file mode 100644 index 000000000..9e1084a53 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h @@ -0,0 +1,464 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_GEN_UTIL_H +#define GU_PCM_CONTACT_GEN_UTIL_H + +#include "PsVecMath.h" +#include "CmPhysXCommon.h" +#include "GuShapeConvex.h" +#include "GuVecCapsule.h" +#include "GuConvexSupportTable.h" + +//The smallest epsilon we will permit (scaled by PxTolerancesScale.length) +#define PCM_WITNESS_POINT_LOWER_EPS 1e-2f +//The largest epsilon we will permit (scaled by PxTolerancesScale.length) +#define PCM_WITNESS_POINT_UPPER_EPS 5e-2f + +namespace physx +{ + +namespace Gu +{ + + enum FeatureStatus + { + POLYDATA0, + POLYDATA1, + EDGE + }; + + + PX_FORCE_INLINE bool contains(Ps::aos::Vec3V* verts, const PxU32 numVerts, const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg min, const Ps::aos::Vec3VArg max) + { + using namespace Ps::aos; + + const BoolV tempCon = BOr(V3IsGrtr(min, p), V3IsGrtr(p, max)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if (BAllEqTTTT(con)) + return false; + + const FloatV tx = V3GetX(p); + const FloatV ty = V3GetY(p); + + const FloatV eps = FEps(); + const FloatV zero = FZero(); + PxU32 intersectionPoints = 0; + + PxU32 i = 0, j = numVerts - 1; + + for (; i < numVerts; j = i++) + { + const FloatV jy = V3GetY(verts[j]); + const FloatV iy = V3GetY(verts[i]); + + const FloatV jx = V3GetX(verts[j]); + const FloatV ix = V3GetX(verts[i]); + + //if p is one of the end point, we will return intersect + const BoolV con0 = BAnd(FIsEq(tx, jx), FIsEq(ty, jy)); + const BoolV con1 = BAnd(FIsEq(tx, ix), FIsEq(ty, iy)); + + if (BAllEqTTTT(BOr(con0, con1))) + { + return true; + } + + //(verts[i].y > test.y) != (points[j].y > test.y) + const PxU32 yflag0 = FAllGrtr(jy, ty); + const PxU32 yflag1 = FAllGrtr(iy, ty); + + //ML: the only case the ray will intersect this segment is when the p's y is in between two segments y + if (yflag0 != yflag1) + { + + //ML: choose ray, which start at p and every points in the ray will have the same y component + //t1 = (yp - yj)/(yi - yj) + //qx = xj + t1*(xi-xj) + //t = qx - xp > 0 for the ray and segment intersection happen + const FloatV jix = FSub(ix, jx); + const FloatV jiy = FSub(iy, jy); + //const FloatV jtx = FSub(tx, jy); + const FloatV jty = FSub(ty, jy); + const FloatV part1 = FMul(jty, jix); + //const FloatV part2 = FMul(jx, jiy); + //const FloatV part3 = FMul(V3Sub(tx, eps), jiy); + const FloatV part2 = FMul(FAdd(jx, eps), jiy); + const FloatV part3 = FMul(tx, jiy); + + const BoolV comp = FIsGrtr(jiy, zero); + const FloatV tmp = FAdd(part1, part2); + const FloatV comp1 = FSel(comp, tmp, part3); + const FloatV comp2 = FSel(comp, part3, tmp); + + + if (FAllGrtrOrEq(comp1, comp2)) + { + if (intersectionPoints == 1) + { + return false; + } + intersectionPoints++; + } + } + } + return intersectionPoints> 0; + } + + + PX_FORCE_INLINE bool boxContainsInXY(const Ps::aos::FloatVArg x, const Ps::aos::FloatVArg y, const Ps::aos::Vec3VArg p, const Ps::aos::Vec3V* verts, const Ps::aos::Vec3VArg min, const Ps::aos::Vec3VArg max) + { + using namespace Ps::aos; + + const BoolV tempCon = BOr(V3IsGrtr(min, p), V3IsGrtr(p, max)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if(BAllEqTTTT(con)) + return false; + + const FloatV zero = FZero(); + FloatV PreviousX = V3GetX(verts[3]); + FloatV PreviousY = V3GetY(verts[3]); + + + // Loop through quad vertices + for(PxI32 i=0; i<4; i++) + { + const FloatV CurrentX = V3GetX(verts[i]); + const FloatV CurrentY = V3GetY(verts[i]); + + // |CurrentX - PreviousX x - PreviousX| + // |CurrentY - PreviousY y - PreviousY| + // => similar to backface culling, check each one of the 4 triangles are consistent, in which case + // the point is within the parallelogram. + const FloatV v00 = FSub(CurrentX, PreviousX); + const FloatV v01 = FSub(y, PreviousY); + const FloatV v10 = FSub(CurrentY, PreviousY); + const FloatV v11 = FSub(x, PreviousX); + const FloatV temp0 = FMul(v00, v01); + const FloatV temp1 = FMul(v10, v11); + if(FAllGrtrOrEq(FSub(temp0, temp1), zero)) + return false; + + PreviousX = CurrentX; + PreviousY = CurrentY; + } + + return true; + + } + + + PX_FORCE_INLINE Ps::aos::FloatV signed2DTriArea(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c) + { + using namespace Ps::aos; + const Vec3V ca = V3Sub(a, c); + const Vec3V cb = V3Sub(b, c); + + const FloatV t0 = FMul(V3GetX(ca), V3GetY(cb)); + const FloatV t1 = FMul(V3GetY(ca), V3GetX(cb)); + + return FSub(t0, t1); + } + + + PX_FORCE_INLINE PxI32 getPolygonIndex(const Gu::PolygonalData& polyData, SupportLocal* map, const Ps::aos::Vec3VArg normal) + { + using namespace Ps::aos; + + //normal is in shape space, need to transform the vertex space + const Vec3V n = M33TrnspsMulV3(map->vertex2Shape, normal); + const Vec3V nnormal = V3Neg(n); + const Vec3V planeN_ = V3LoadU_SafeReadW(polyData.mPolygons[0].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + FloatV minProj = V3Dot(n, planeN_); + + const FloatV zero = FZero(); + PxI32 closestFaceIndex = 0; + + for(PxU32 i=1; i< polyData.mNbPolygons; ++i) + { + const Vec3V planeN = V3LoadU_SafeReadW(polyData.mPolygons[i].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + const FloatV proj = V3Dot(n, planeN); + if(FAllGrtr(minProj, proj)) + { + minProj = proj; + closestFaceIndex = PxI32(i); + } + } + + const PxU32 numEdges = polyData.mNbEdges; + const PxU8* const edgeToFace = polyData.mFacesByEdges; + + //Loop through edges + PxU32 closestEdge = 0xffffffff; + FloatV maxDpSq = FMul(minProj, minProj); + + for(PxU32 i=0; i < numEdges; ++i)//, inc = VecI32V_Add(inc, vOne)) + { + const PxU32 index = i*2; + const PxU8 f0 = edgeToFace[index]; + const PxU8 f1 = edgeToFace[index+1]; + + const Vec3V planeNormal0 = V3LoadU_SafeReadW(polyData.mPolygons[f0].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + const Vec3V planeNormal1 = V3LoadU_SafeReadW(polyData.mPolygons[f1].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + + // unnormalized edge normal + const Vec3V edgeNormal = V3Add(planeNormal0, planeNormal1);//polys[f0].mPlane.n + polys[f1].mPlane.n; + const FloatV enMagSq = V3Dot(edgeNormal, edgeNormal);//edgeNormal.magnitudeSquared(); + //Test normal of current edge - squared test is valid if dp and maxDp both >= 0 + const FloatV dp = V3Dot(edgeNormal, nnormal);//edgeNormal.dot(normal); + const FloatV sqDp = FMul(dp, dp); + + const BoolV con0 = FIsGrtrOrEq(dp, zero); + const BoolV con1 = FIsGrtr(sqDp, FMul(maxDpSq, enMagSq)); + const BoolV con = BAnd(con0, con1); + if(BAllEqTTTT(con)) + { + maxDpSq = FDiv(sqDp, enMagSq); + closestEdge = i; + } + } + + if(closestEdge!=0xffffffff) + { + const PxU8* FBE = edgeToFace; + + const PxU32 index = closestEdge*2; + const PxU32 f0 = FBE[index]; + const PxU32 f1 = FBE[index+1]; + + const Vec3V planeNormal0 = V3LoadU_SafeReadW(polyData.mPolygons[f0].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + const Vec3V planeNormal1 = V3LoadU_SafeReadW(polyData.mPolygons[f1].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + + const FloatV dp0 = V3Dot(planeNormal0, nnormal); + const FloatV dp1 = V3Dot(planeNormal1, nnormal); + if(FAllGrtr(dp0, dp1)) + { + closestFaceIndex = PxI32(f0); + } + else + { + closestFaceIndex = PxI32(f1); + } + } + + return closestFaceIndex; + + } + + + PX_FORCE_INLINE PxU32 getWitnessPolygonIndex(const Gu::PolygonalData& polyData, SupportLocal* map, const Ps::aos::Vec3VArg normal, + const Ps::aos::Vec3VArg closest, const PxReal tolerance) + { + using namespace Ps::aos; + + PxReal pd[256]; + //first pass : calculate the smallest distance from the closest point to the polygon face + + //transform the closest p to vertex space + const Vec3V p = M33MulV3(map->shape2Vertex, closest); + PxU32 closestFaceIndex = 0; + + PxVec3 closestP; + V3StoreU(p, closestP); + + const PxReal eps = -tolerance; + + PxPlane plane = polyData.mPolygons[0].mPlane; + PxReal dist = plane.distance(closestP); + PxReal minDist = dist >= eps ? dist : PX_MAX_F32; + pd[0] = minDist; + PxReal maxDist = dist; + PxU32 maxFaceIndex = 0; + + for (PxU32 i = 1; i < polyData.mNbPolygons; ++i) + { + plane = polyData.mPolygons[i].mPlane; + dist = plane.distance(closestP); + pd[i] = dist >= eps ? dist : PX_MAX_F32; + if (minDist > pd[i]) + { + minDist = pd[i]; + closestFaceIndex = i; + } + if (dist > maxDist) + { + maxDist = dist; + maxFaceIndex = i; + } + } + + if (minDist == PX_MAX_F32) + return maxFaceIndex; + + //second pass : select the face which has the normal most close to the gjk/epa normal + Vec4V plane4 = V4LoadU(&polyData.mPolygons[closestFaceIndex].mPlane.n.x); + Vec3V n = Vec3V_From_Vec4V(plane4); + n = V3Normalize(M33TrnspsMulV3(map->shape2Vertex, n)); + FloatV bestProj = V3Dot(n, normal); + + const PxU32 firstPassIndex = closestFaceIndex; + + for (PxU32 i = 0; i< polyData.mNbPolygons; ++i) + { + //if the difference between the minimum distance and the distance of p to plane i is within tolerance, we use the normal to chose the best plane + if ((tolerance >(pd[i] - minDist)) && (firstPassIndex != i)) + { + plane4 = V4LoadU(&polyData.mPolygons[i].mPlane.n.x); + n = Vec3V_From_Vec4V(plane4); + //rotate the plane normal to shape space. We can roate normal into the vertex space. The reason for + //that is because it will lose some numerical precision if the object has large scale and this algorithm + //will choose different face + n = V3Normalize(M33TrnspsMulV3(map->shape2Vertex, n)); + FloatV proj = V3Dot(n, normal); + + if (FAllGrtr(bestProj, proj)) + { + closestFaceIndex = i; + bestProj = proj; + } + } + } + + return closestFaceIndex; + } + + //ML: this function is shared by the sphere/capsule vs convex hulls full contact gen, capsule in the local space of polyData + PX_FORCE_INLINE bool testPolyDataAxis(const Gu::CapsuleV& capsule, const Gu::PolygonalData& polyData, SupportLocal* map, const Ps::aos::FloatVArg contactDist, Ps::aos::FloatV& minOverlap, Ps::aos::Vec3V& separatingAxis) + { + using namespace Ps::aos; + FloatV _minOverlap = FMax();//minOverlap; + FloatV min0, max0; + FloatV min1, max1; + Vec3V tempAxis = V3UnitY(); + + //capsule in the local space of polyData + for(PxU32 i=0; ishape2Vertex, vertexSpacePlaneNormal); + + const FloatV magnitude = FRecip(V3Length(shapeSpacePlaneNormal)); + //normalize shape space normal + const Vec3V planeN = V3Scale(shapeSpacePlaneNormal, magnitude); + //ML::use this to avoid LHS + min0 = FMul(V3Dot(vertexSpacePlaneNormal, minVert), magnitude); + max0 = FMul(FNeg(planeDist), magnitude); + + + const FloatV tempMin = V3Dot(capsule.p0, planeN); + const FloatV tempMax = V3Dot(capsule.p1, planeN); + min1 = FMin(tempMin, tempMax); + max1 = FMax(tempMin, tempMax); + + min1 = FSub(min1, capsule.radius); + max1 = FAdd(max1, capsule.radius); + + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + tempAxis = planeN; + } + } + + separatingAxis = tempAxis; + minOverlap = _minOverlap; + + return true; + + } + + //ML: this function is shared by sphere/capsule. Point a and direction d need to be in the local space of polyData + PX_FORCE_INLINE bool intersectSegmentPolyhedron(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg dir, const PolygonalData& polyData, Ps::aos::FloatV& tEnter, Ps::aos::FloatV& tExit) + { + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV one = FOne(); + const FloatV eps = FLoad(1e-7f); + FloatV tFirst = zero; + FloatV tLast= one; + + for(PxU32 k=0; k 0 means the ray is exiting halfspace + const BoolV con = FIsGrtr(zero, denominator); + const BoolV con0= FIsGrtr(tTemp, tFirst); + const BoolV con1 = FIsGrtr(tLast, tTemp); + + tFirst = FSel(BAnd(con, con0), tTemp, tFirst); + tLast = FSel(BAndNot(con1, con), tTemp, tLast); + } + + if(FAllGrtr(tFirst, tLast)) + return false; + } + + //calculate the intersect p in the local space + tEnter = tFirst; + tExit = tLast; + + return true; + } + +}//Gu +}//physx + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h new file mode 100644 index 000000000..c96349973 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h @@ -0,0 +1,208 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_MESH_CALLBACK_H +#define GU_PCM_CONTACT_MESH_CALLBACK_H + +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuTriangleCache.h" +#include "GuConvexEdgeFlags.h" + +namespace physx +{ + +namespace Gu +{ + +template +struct PCMMeshContactGenerationCallback : MeshHitCallback +{ +public: + const Cm::FastVertex2ShapeScaling& mMeshScaling; + const PxU8* PX_RESTRICT mExtraTrigData; + bool mIdtMeshScale; + static const PxU32 CacheSize = 16; + Gu::TriangleCache mCache; + + PCMMeshContactGenerationCallback(const Cm::FastVertex2ShapeScaling& meshScaling, const PxU8* extraTrigData, bool idtMeshScale) + : MeshHitCallback(CallbackMode::eMULTIPLE), + mMeshScaling(meshScaling), mExtraTrigData(extraTrigData), mIdtMeshScale(idtMeshScale) + { + } + + void flushCache() + { + if (!mCache.isEmpty()) + { + (static_cast(this))->template processTriangleCache< CacheSize >(mCache); + mCache.reset(); + } + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + if (!(static_cast(this))->doTest(v0, v1, v2)) + return true; + + PxVec3 v[3]; + if(mIdtMeshScale) + { + v[0] = v0; + v[1] = v1; + v[2] = v2; + } + else + { + const PxI32 winding = mMeshScaling.flipsNormal() ? 1 : 0; + v[0] = mMeshScaling * v0; + v[1 + winding] = mMeshScaling * v1; + v[2 - winding] = mMeshScaling * v2; + } + + const PxU32 triangleIndex = hit.faceIndex; + const PxU8 extraData = getConvexEdgeFlags(mExtraTrigData, triangleIndex); + + if (mCache.isFull()) + { + (static_cast(this))->template processTriangleCache< CacheSize >(mCache); + mCache.reset(); + } + mCache.addTriangle(v, vinds, triangleIndex, extraData); + + return true; + } + +protected: + PCMMeshContactGenerationCallback& operator=(const PCMMeshContactGenerationCallback&); +}; + +template +struct PCMHeightfieldContactGenerationCallback : Gu::EntityReport +{ +public: + const Gu::HeightFieldUtil& mHfUtil; + const PxTransform& mHeightfieldTransform; + bool mBoundaryCollisions; + + PCMHeightfieldContactGenerationCallback(const Gu::HeightFieldUtil& hfUtil, const PxTransform& heightfieldTransform) : + mHfUtil(hfUtil), mHeightfieldTransform(heightfieldTransform) + { + mBoundaryCollisions = !(hfUtil.getHeightField().getFlags() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES); + } + + // PT: TODO: refactor/unify with similar code in other places + virtual PxAgain onEvent(PxU32 nb, PxU32* indices) + { + const PxU32 CacheSize = 16; + Gu::TriangleCache cache; + + const PxU32 nbPasses = (nb+(CacheSize-1))/CacheSize; + PxU32 nbTrigs = nb; + PxU32* inds0 = indices; + + const PxU8 nextInd[] = {2,0,1}; + + for(PxU32 i = 0; i < nbPasses; ++i) + { + cache.mNumTriangles = 0; + PxU32 trigCount = PxMin(nbTrigs, CacheSize); + nbTrigs -= trigCount; + while(trigCount--) + { + PxU32 triangleIndex = *(inds0++); + PxU32 vertIndices[3]; + + PxTriangle currentTriangle; // in world space + + PxU32 adjInds[3]; + mHfUtil.getTriangle(mHeightfieldTransform, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); + + PxVec3 normal; + currentTriangle.normal(normal); + + PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF + + for(PxU32 a = 0; a < 3; ++a) + { + + if (adjInds[a] != 0xFFFFFFFF) + { + PxTriangle adjTri; + PxU32 inds[3]; + mHfUtil.getTriangle(mHeightfieldTransform, adjTri, inds, NULL, adjInds[a], false, false); + //We now compare the triangles to see if this edge is active + + PX_ASSERT(inds[0] == vertIndices[a] || inds[1] == vertIndices[a] || inds[2] == vertIndices[a]); + PX_ASSERT(inds[0] == vertIndices[(a + 1) % 3] || inds[1] == vertIndices[(a + 1) % 3] || inds[2] == vertIndices[(a + 1) % 3]); + + + PxVec3 adjNormal; + adjTri.denormalizedNormal(adjNormal); + PxU32 otherIndex = nextInd[a]; + PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); + + if (projD < 0.f) + { + adjNormal.normalize(); + + PxF32 proj = adjNormal.dot(normal); + + if (proj < 0.997f) + { + triFlags |= (1 << (a + 3)); + } + } + } + else if (mBoundaryCollisions) + { + triFlags |= (1 << (a + 3)); //Mark boundary edge active + } + else + triFlags |= (1 << a); //Mark as silhouette edge + } + + cache.addTriangle(currentTriangle.verts, vertIndices, triangleIndex, triFlags); + } + PX_ASSERT(cache.mNumTriangles <= 16); + + (static_cast(this))->template processTriangleCache< CacheSize >(cache); + } + return true; + } +protected: + PCMHeightfieldContactGenerationCallback& operator=(const PCMHeightfieldContactGenerationCallback&); +}; + +}//Gu +}//physx + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp new file mode 100644 index 000000000..fd184ab8f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp @@ -0,0 +1,219 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecBox.h" +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + +#include "GuPersistentContactManifold.h" + +namespace physx +{ +namespace Gu +{ +bool pcmContactPlaneBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + + // Get actual shape data + const PxBoxGeometry& shapeBox = shape1.get(); + + const PsTransformV transf0 = loadTransformA(transform1);//box transform + const PsTransformV transf1 = loadTransformA(transform0);//plane transform + + //box to plane + const PsTransformV curTransf(transf1.transformInv(transf0)); + + //in world space + const Vec3V negPlaneNormal = V3Normalize(V3Neg(QuatGetBasisVector0(transf1.q))); + + const FloatV contactDist = FLoad(params.mContactDistance); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV boxMargin = CalculatePCMBoxMargin(boxExtents, toleranceLength); + const FloatV projectBreakingThreshold = FMul(boxMargin, FLoad(0.2f)); + const PxU32 initialContacts = manifold.mNumContacts; + + manifold.refreshContactPoints(curTransf, projectBreakingThreshold, contactDist); + + const PxU32 newContacts = manifold.mNumContacts; + const bool bLostContacts = (newContacts != initialContacts);//((initialContacts == 0) || (newContacts != initialContacts)); + + if(bLostContacts || manifold.invalidate_PrimitivesPlane(curTransf, boxMargin, FLoad(0.2f))) + { + //ML:localNormal is the local space of plane normal, however, because shape1 is box and shape0 is plane, we need to use the reverse of contact normal(which will be the plane normal) to make the refreshContactPoints + //work out the correct pentration for points + const Vec3V localNormal = V3UnitX(); + + manifold.mNumContacts = 0; + manifold.setRelativeTransform(curTransf); + + const PsMatTransformV aToB(curTransf); + const FloatV bx = V3GetX(boxExtents); + const FloatV by = V3GetY(boxExtents); + const FloatV bz = V3GetZ(boxExtents); + + const FloatV nbx = FNeg(bx); + const FloatV nby = FNeg(by); + const FloatV nbz = FNeg(bz); + + const Vec3V temp0 = V3Scale(aToB.getCol0(), bx); + const Vec3V temp1 = V3Scale(aToB.getCol1(), by); + const Vec3V temp2 = V3Scale(aToB.getCol2(), bz); + + const Vec3V ntemp2 = V3Neg(temp2); + + const FloatV px = V3GetX(aToB.p); + + //box's points in the local space of plane + const Vec3V temp01 = V3Add(temp0, temp1);//(x, y) + const Vec3V temp02 = V3Sub(temp0, temp1);//(x, -y) + + const FloatV s0 = V3GetX(V3Add(temp2, temp01));//(x, y, z) + const FloatV s1 = V3GetX(V3Add(ntemp2, temp01));//(x, y, -z) + const FloatV s2 = V3GetX(V3Add(temp2, temp02));//(x, -y, z) + const FloatV s3 = V3GetX(V3Add(ntemp2, temp02));//(x, -y, -z) + const FloatV s4 = V3GetX(V3Sub(temp2, temp02));//(-x, y, z) + const FloatV s5 = V3GetX(V3Sub(ntemp2, temp02));//(-x, y, -z) + const FloatV s6 = V3GetX(V3Sub(temp2, temp01));//(-x, -y, z) + const FloatV s7 = V3GetX(V3Sub(ntemp2, temp01));//(-x, -y, -z) + + const FloatV acceptanceDist = FSub(contactDist, px); + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + + if(FAllGrtr(acceptanceDist, s0)) + { + const FloatV pen = FAdd(s0, px); + //(x, y, z) + manifoldContacts[numContacts].mLocalPointA = boxExtents;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(boxExtents)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s1)) + { + const FloatV pen = FAdd(s1, px); + //(x, y, -z) + const Vec3V p = V3Merge(bx, by, nbz); + //add to contact stream + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s2)) + { + const FloatV pen = FAdd(s2, px); + //(x, -y, z) + const Vec3V p = V3Merge(bx, nby, bz); + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s3)) + { + const FloatV pen = FAdd(s3, px); + //(x, -y, -z) + const Vec3V p = V3Merge(bx, nby, nbz); + manifoldContacts[numContacts].mLocalPointA = p; + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s4)) + { + const FloatV pen = FAdd(s4, px); + //(-x, y, z) + const Vec3V p =V3Merge(nbx, by, bz); + manifoldContacts[numContacts].mLocalPointA = p; + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s5)) + { + const FloatV pen = FAdd(s5, px); + //(-x, y, -z) + const Vec3V p = V3Merge(nbx, by, nbz); + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + + if(FAllGrtr(acceptanceDist, s6)) + { + const FloatV pen = FAdd(s6, px); + //(-x, -y, z) + const Vec3V p = V3Merge(nbx, nby, bz); + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s7)) + { + const FloatV pen = FAdd(s7, px); + //(-x, -y, -z) + const Vec3V p = V3Merge(nbx, nby, nbz); + manifoldContacts[numContacts].mLocalPointA = p; + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + //reduce contacts + manifold.addBatchManifoldContactsCluster(manifoldContacts, numContacts); + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf1, contactDist); + + return manifold.getNumContacts() > 0; + } + else + { + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf1, contactDist); + + //manifold.drawManifold(*gRenderOutPut, transf0, transf1); + return manifold.getNumContacts() > 0; + } +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp new file mode 100644 index 000000000..9e0023c9e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp @@ -0,0 +1,133 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPersistentContactManifold.h" + +namespace physx +{ +namespace Gu +{ +bool pcmContactPlaneCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + + // Get actual shape data + const PxCapsuleGeometry& shapeCapsule = shape1.get(); + + const PsTransformV transf0 = loadTransformA(transform1);//capsule transform + const PsTransformV transf1 = loadTransformA(transform0);//plane transform + //capsule to plane + const PsTransformV aToB(transf1.transformInv(transf0)); + + //in world space + const Vec3V planeNormal = V3Normalize(QuatGetBasisVector0(transf1.q)); + const Vec3V contactNormal = V3Neg(planeNormal); + + //ML:localNormal is the local space of plane normal, however, because shape1 is capulse and shape0 is plane, we need to use the reverse of contact normal(which will be the plane normal) to make the refreshContactPoints + //work out the correct pentration for points + const Vec3V localNormal = V3UnitX(); + + const FloatV contactDist = FLoad(params.mContactDistance); + + const FloatV radius = FLoad(shapeCapsule.radius); + const FloatV halfHeight = FLoad(shapeCapsule.halfHeight); + + //capsule is in the local space of plane(n = (1.f, 0.f, 0.f), d=0.f) + const Vec3V basisVector = QuatGetBasisVector0(aToB.q); + const Vec3V tmp = V3Scale(basisVector, halfHeight); + const Vec3V s = V3Add(aToB.p, tmp); + const Vec3V e = V3Sub(aToB.p, tmp); + + const FloatV inflatedRadius = FAdd(radius, contactDist); + const FloatV replaceBreakingThreshold = FMul(radius, FLoad(0.001f)); + const FloatV projectBreakingThreshold = FMul(radius, FLoad(0.05f)); + const PxU32 initialContacts = manifold.mNumContacts; + + //manifold.refreshContactPoints(curRTrans, projectBreakingThreshold, contactDist); + const FloatV refreshDist = FAdd(contactDist, radius); + manifold.refreshContactPoints(aToB, projectBreakingThreshold, refreshDist); + + const PxU32 newContacts = manifold.mNumContacts; + const bool bLostContacts = (newContacts != initialContacts);//((initialContacts == 0) || (newContacts != initialContacts)); + + if(bLostContacts || manifold.invalidate_PrimitivesPlane(aToB, radius, FLoad(0.02f))) + { + manifold.mNumContacts = 0; + manifold.setRelativeTransform(aToB); + //calculate the distance from s to the plane + const FloatV signDist0 = V3GetX(s);//V3Dot(localNormal, s); + if(FAllGrtr(inflatedRadius, signDist0)) + { + const Vec3V localPointA = aToB.transformInv(s); + const Vec3V localPointB = V3NegScaleSub(localNormal, signDist0, s); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), signDist0); + //add to manifold + manifold.addManifoldPoint2(localPointA, localPointB, localNormalPen, replaceBreakingThreshold); + } + + const FloatV signDist1 = V3GetX(e);//V3Dot(localNormal, e); + if(FAllGrtr(inflatedRadius, signDist1)) + { + const Vec3V localPointA = aToB.transformInv(e); + + const Vec3V localPointB = V3NegScaleSub(localNormal, signDist1, e); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), signDist1); + //add to manifold + manifold.addManifoldPoint2(localPointA, localPointB, localNormalPen, replaceBreakingThreshold); + } + + manifold.addManifoldContactsToContactBuffer(contactBuffer, contactNormal, planeNormal, transf0, radius, contactDist); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + + return manifold.getNumContacts() > 0; + } + else + { + manifold.addManifoldContactsToContactBuffer(contactBuffer, contactNormal, planeNormal, transf0, radius, contactDist); + return manifold.getNumContacts() > 0; + } +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp new file mode 100644 index 000000000..43b6dc00d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp @@ -0,0 +1,159 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecConvexHull.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPersistentContactManifold.h" + + +namespace physx +{ +namespace Gu +{ +bool pcmContactPlaneConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + + // Get actual shape data + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + + const PsTransformV transf0 = loadTransformA(transform1);//convex transform + const PsTransformV transf1 = loadTransformA(transform0);//plane transform + //convex to plane + const PsTransformV curTransf(transf1.transformInv(transf0)); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale, toleranceLength); + + //in world space + const Vec3V planeNormal = V3Normalize(QuatGetBasisVector0(transf1.q)); + const Vec3V negPlaneNormal = V3Neg(planeNormal); + + const FloatV contactDist = FLoad(params.mContactDistance); + + //const FloatV replaceBreakingThreshold = FMul(convexMargin, FLoad(0.001f)); + const FloatV projectBreakingThreshold = FMul(convexMargin, FLoad(0.2f)); + const PxU32 initialContacts = manifold.mNumContacts; + + manifold.refreshContactPoints(curTransf, projectBreakingThreshold, contactDist); + + const PxU32 newContacts = manifold.mNumContacts; + const bool bLostContacts = (newContacts != initialContacts);//((initialContacts == 0) || (newContacts != initialContacts)); + + + if(bLostContacts || manifold.invalidate_PrimitivesPlane(curTransf, convexMargin, FLoad(0.2f))) + { + const PsMatTransformV aToB(curTransf); + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const Mat33V vertex2Shape = ConstructVertex2ShapeMatrix(vScale, vQuat); + + //ML:localNormal is the local space of plane normal, however, because shape1 is box and shape0 is plane, we need to use the reverse of contact normal(which will be the plane normal) to make the refreshContactPoints + //work out the correct pentration for points + const Vec3V localNormal = V3UnitX(); + + manifold.mNumContacts = 0; + manifold.setRelativeTransform(curTransf); + const PxVec3* PX_RESTRICT verts = hullData->getHullVertices(); + const PxU8 numVerts = hullData->mNbHullVertices; + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + + const PsMatTransformV aToBVertexSpace(aToB.p, M33MulM33(aToB.rot, vertex2Shape)); + //brute force each points + for(PxU8 i=0; i= Gu::ContactBuffer::MAX_CONTACTS) + { + //ML: number of contacts are more than MAX_CONTACTS, we need to force contact reduction + manifold.reduceBatchContacts(manifoldContacts, numContacts, toleranceLength); + numContacts = GU_MANIFOLD_CACHE_SIZE; + for(PxU32 j=0; j 0; + } + else + { + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return manifold.getNumContacts() > 0; + } +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp new file mode 100644 index 000000000..a9e057ea0 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp @@ -0,0 +1,156 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +//#include "GuGJKWrapper.h" +#include "GuVecBox.h" +#include "GuVecSphere.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + + + +namespace physx +{ + +namespace Gu +{ +bool pcmContactSphereBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + using namespace Ps::aos; + // Get actual shape data + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxBoxGeometry& shapeBox = shape1.get(); + // + + //const PsTransformV transf0(transform0); + const Vec3V sphereOrigin = V3LoadA(&transform0.p.x); + //const PsTransformV transf1(transform1); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV radius = FLoad(shapeSphere.radius); + + const PsTransformV transf1(p1, q1); + + const FloatV cDist = FLoad(params.mContactDistance); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + //translate sphere center into the box space + const Vec3V sphereCenter = transf1.transformInv(sphereOrigin); + + const Vec3V nBoxExtents = V3Neg(boxExtents); + + //const FloatV radSq = FMul(radius, radius); + + const FloatV inflatedSum = FAdd(radius, cDist); + const FloatV sqInflatedSum = FMul(inflatedSum, inflatedSum); + + const Vec3V p = V3Clamp(sphereCenter, nBoxExtents, boxExtents); + const Vec3V v = V3Sub(sphereCenter, p); + const FloatV lengthSq = V3Dot(v, v); + + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + + if(FAllGrtr(sqInflatedSum, lengthSq))//intersect + { + //check whether the spherCenter is inside the box + const BoolV bInsideBox = V3IsGrtrOrEq(boxExtents, V3Abs(sphereCenter)); + // PT: TODO: ??? revisit this, why do we have both BAllEqTTTT and BAllTrue3? + if(BAllEqTTTT(BAllTrue3(bInsideBox)))//sphere center inside the box + { + //Pick directions and sign + const Vec3V absP = V3Abs(p); + const Vec3V distToSurface = V3Sub(boxExtents, absP);//dist from embedded center to box surface along 3 dimensions. + + const FloatV x = V3GetX(distToSurface); + const FloatV y = V3GetY(distToSurface); + const FloatV z = V3GetZ(distToSurface); + + const Vec3V xV = V3Splat(x); + const Vec3V zV = V3Splat(z); + + //find smallest element of distToSurface + const BoolV con0 = BAllTrue3(V3IsGrtrOrEq(distToSurface, zV)); + const BoolV con1 = BAllTrue3(V3IsGrtrOrEq(distToSurface, xV)); + const Vec3V sign = V3Sign(p); + + const Vec3V tmpX = V3Mul(V3UnitX(), sign); + const Vec3V tmpY = V3Mul(V3UnitY(), sign); + const Vec3V tmpZ = V3Mul(V3UnitZ(), sign); + + const Vec3V locNorm= V3Sel(con0, tmpZ, V3Sel(con1, tmpX, tmpY));////local coords contact normal + const FloatV dist = FNeg(FSel(con0, z, FSel(con1, x, y))); + + //separation so far is just the embedding of the center point; we still have to push out all of the radius. + const Vec3V normal = transf1.rotate(locNorm); + const FloatV penetration = FSub(dist, radius); + const Vec3V point = V3Sub(sphereOrigin , V3Scale(normal, dist)); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(penetration, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + //context.mContactBuffer.contact(point, normal, penetration); + } + else + { + //get the closest point from the center to the box surface + const FloatV recipLength = FRsqrt(lengthSq); + const FloatV length = FRecip(recipLength); + const Vec3V locNorm = V3Scale(v, recipLength); + const FloatV penetration = FSub(length, radius); + const Vec3V normal = transf1.rotate(locNorm); + const Vec3V point = transf1.transform(p); + + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(penetration, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + //context.mContactBuffer.contact(point, normal, penetration); + } + return true; + } + return false; +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp new file mode 100644 index 000000000..a7851fe59 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp @@ -0,0 +1,121 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +//#include "GuGJKWrapper.h" +#include "GuVecSphere.h" +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + + +namespace physx +{ + +namespace Gu +{ +PX_FORCE_INLINE Ps::aos::FloatV PxcDistancePointSegmentSquared(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg p, Ps::aos::FloatV& param) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV one = FOne(); + + const Vec3V ap = V3Sub(p, a); + const Vec3V ab = V3Sub(b, a); + const FloatV nom = V3Dot(ap, ab); + + const FloatV denom = V3Dot(ab, ab); + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + + const FloatV t = FSel(FIsEq(denom, zero), zero, tValue); + const Vec3V v = V3NegScaleSub(ab, t, ap); + param = t; + return V3Dot(v, v); +} + +bool pcmContactSphereCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxCapsuleGeometry& shapeCapsule = shape1.get(); + + //Sphere in world space + const Vec3V sphereCenter = V3LoadA(&transform0.p.x); + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV cDist = FLoad(params.mContactDistance); + + //const FloatV r0 = FloatV_From_F32(shapeCapsule.radius); + const FloatV halfHeight0 = FLoad(shapeCapsule.halfHeight); + const Vec3V basisVector0 = QuatGetBasisVector0(q1); + const Vec3V tmp0 = V3Scale(basisVector0, halfHeight0); + const Vec3V s = V3Add(p1, tmp0); + const Vec3V e = V3Sub(p1, tmp0); + + + const FloatV radiusSum = FAdd(sphereRadius, capsuleRadius); + const FloatV inflatedSum = FAdd(radiusSum, cDist); + + // Collision detection + FloatV t; + const FloatV squareDist = PxcDistancePointSegmentSquared(s, e, sphereCenter, t); + const FloatV sqInflatedSum = FMul(inflatedSum, inflatedSum); + + if(FAllGrtr(sqInflatedSum, squareDist))//BAllEq(con, bTrue)) + { + const Vec3V p = V3ScaleAdd(V3Sub(e, s), t, s); + const Vec3V dir = V3Sub(sphereCenter, p); + const Vec3V normal = V3NormalizeSafe(dir, V3UnitX()); + const Vec3V point = V3NegScaleSub(normal, sphereRadius, sphereCenter);//transform back to the world space + + const FloatV dist = FSub(FSqrt(squareDist), radiusSum); + //context.mContactBuffer.contact(point, normal, FSub(FSqrt(squareDist), radiusSum)); + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(dist, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + return true; + } + return false; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp new file mode 100644 index 000000000..2bc568c42 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp @@ -0,0 +1,284 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" + +namespace physx +{ +namespace Gu +{ + +static void addToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg worldNormal, const Ps::aos::Vec3VArg worldPoint, const Ps::aos::FloatVArg penDep) +{ + using namespace Ps::aos; + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(worldNormal), reinterpret_cast(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldPoint), reinterpret_cast(&contact.point.x)); + FStore(penDep, &contact.separation); + + PX_ASSERT(contact.point.isFinite()); + PX_ASSERT(contact.normal.isFinite()); + PX_ASSERT(PxIsFinite(contact.separation)); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + +} + +static bool fullContactsGenerationSphereConvex(const Gu::CapsuleV& capsule, const Gu::ConvexHullV& convexHull, const Ps::aos::PsTransformV& transf0,const Ps::aos::PsTransformV& transf1, + Gu::PersistentContact* manifoldContacts, Gu::ContactBuffer& contactBuffer, const bool idtScale, Gu::PersistentContactManifold& manifold, + Ps::aos::Vec3VArg normal, const Ps::aos::FloatVArg contactDist, bool doOverlapTest, Cm::RenderOutput* renderOutput) +{ + PX_UNUSED(renderOutput); + using namespace Ps::aos; + Gu::PolygonalData polyData; + getPCMConvexData(convexHull,idtScale, polyData); + + PxU8 buff[sizeof(SupportLocalImpl)]; + SupportLocal* map = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(static_cast(convexHull), transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale)) : + static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(convexHull, transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale))); + + PxU32 numContacts = 0; + if(generateSphereFullContactManifold(capsule, polyData, map, manifoldContacts, numContacts, contactDist, normal, doOverlapTest)) + { + + if(numContacts > 0) + { + + Gu::PersistentContact& p = manifold.getContactPoint(0); + + p.mLocalPointA = manifoldContacts[0].mLocalPointA; + p.mLocalPointB = manifoldContacts[0].mLocalPointB; + p.mLocalNormalPen = manifoldContacts[0].mLocalNormalPen; + manifold.mNumContacts =1; + + //transform normal to world space + const Vec3V worldNormal = transf1.rotate(normal); + const Vec3V worldP = V3NegScaleSub(worldNormal, capsule.radius, transf0.p); + const FloatV penDep = FSub(V4GetW(manifoldContacts[0].mLocalNormalPen), capsule.radius); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, capsule.radius); +#endif + + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + + return true; + } + + } + + return false; +} + +bool pcmContactSphereConvex(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + const PxSphereGeometry& shapeSphere = shape0.get(); + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + + const Vec3V zeroV = V3Zero(); + + Ps::prefetchLine(shapeConvex.hullData); + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale, toleranceLength); + + const PxU32 initialContacts = manifold.mNumContacts; + const FloatV minMargin = FMin(convexMargin, sphereRadius); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + const FloatV refreshDistance = FAdd(sphereRadius, contactDist); + manifold.refreshContactPoints(aToB, projectBreakingThreshold, refreshDistance); + //ML: after refreshContactPoints, we might lose some contacts + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + if(bLostContacts || manifold.invalidate_SphereCapsule(curRTrans, minMargin)) + { + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + manifold.setRelativeTransform(curRTrans); + + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const bool idtScale = shapeConvex.scale.isIdentity(); + //use the original shape + ConvexHullV convexHull(hullData, V3LoadU(hullData->mCenterOfMass), vScale, vQuat, idtScale); + //transform capsule into the local space of convexHull + CapsuleV capsule(aToB.p, sphereRadius); + + GjkOutput output; + + LocalConvex convexA(capsule); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(idtScale) + { + LocalConvex convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + status = gjkPenetration, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + } + else + { + LocalConvex convexB(convexHull); + status = gjkPenetration, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + } + + if(status == GJK_NON_INTERSECT) + { + return false; + } + else if(status == GJK_CONTACT) + { + Gu::PersistentContact& p = manifold.getContactPoint(0); + p.mLocalPointA = zeroV;//sphere center + p.mLocalPointB = output.closestB; + p.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + manifold.mNumContacts =1; + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, capsule.radius); +#endif + + //transform normal to world space + const Vec3V worldNormal = transf1.rotate(output.normal); + const Vec3V worldP = V3NegScaleSub(worldNormal, sphereRadius, transf0.p); + const FloatV penDep = FSub(output.penDep, sphereRadius); + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + return true; + + } + else if(status == GJK_DEGENERATE) + { + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + + return fullContactsGenerationSphereConvex(capsule, convexHull, transf0, transf1, manifoldContacts, contactBuffer, idtScale, + manifold, output.normal, contactDist, true, renderOutput); + } + else if(status == EPA_CONTACT) + { + + if(idtScale) + { + LocalConvex convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + + } + else + { + LocalConvex convexB(convexHull); + + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + + } + + if(status == EPA_CONTACT) + { + Gu::PersistentContact& p = manifold.getContactPoint(0); + p.mLocalPointA = zeroV;//sphere center + p.mLocalPointB = output.closestB; + p.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + manifold.mNumContacts =1; + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, capsule.radius); +#endif + + //transform normal to world space + const Vec3V worldNormal = transf1.rotate(output.normal); + const Vec3V worldP = V3NegScaleSub(worldNormal, sphereRadius, transf0.p); + const FloatV penDep = FSub(output.penDep, sphereRadius); + + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + return true; + } + else + { + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + return fullContactsGenerationSphereConvex(capsule, convexHull, transf0, transf1, manifoldContacts, contactBuffer, idtScale, + manifold, output.normal, contactDist, true, renderOutput); + + } + + } + } + else if(manifold.mNumContacts > 0) + { + //ML:: the manifold originally has contacts + Gu::PersistentContact& p = manifold.getContactPoint(0); + const Vec3V worldNormal = transf1.rotate(Vec3V_From_Vec4V(p.mLocalNormalPen)); + const Vec3V worldP = V3NegScaleSub(worldNormal, sphereRadius, transf0.p); + const FloatV penDep = FSub(V4GetW(p.mLocalNormalPen), sphereRadius); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, sphereRadius); +#endif + + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + return true; + } + + return false; + +} +}//Gu +}//phyxs + diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp new file mode 100644 index 000000000..fb8288271 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp @@ -0,0 +1,159 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuHeightField.h" +#include "GuPCMContactConvexCommon.h" +#include "GuPCMContactMeshCallback.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMSphereVsHeightfieldContactGenerationCallback : PCMHeightfieldContactGenerationCallback +{ + +public: + PCMSphereVsMeshContactGeneration mGeneration; + + PCMSphereVsHeightfieldContactGenerationCallback( + const Ps::aos::Vec3VArg sphereCenter, + const Ps::aos::FloatVArg sphereRadius, + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + + const PsTransformV& sphereTransform, + const PsTransformV& heightfieldTransform, + const PxTransform& heightfieldTransform1, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Ps::InlineArray* deferredContacts, + Gu::HeightFieldUtil& hfUtil + + + ) : + PCMHeightfieldContactGenerationCallback(hfUtil, heightfieldTransform1), + mGeneration(sphereCenter, sphereRadius, contactDistance, replaceBreakingThreshold, sphereTransform, + heightfieldTransform, multiManifold, contactBuffer, deferredContacts) + { + } + + template + void processTriangleCache(Gu::TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + + +bool Gu::pcmContactSphereHeightField(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxHeightFieldGeometryLL& shapeHeight = shape1.get(); + + Gu::MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV sphereTransform(p0, q0);//sphere transform + const PsTransformV heightfieldTransform(p1, q1);//height feild + const PsTransformV curTransform = heightfieldTransform.transformInv(sphereTransform); + + + // We must be in local space to use the cache + + if(multiManifold.invalidate(curTransform, sphereRadius, FLoad(0.02f))) + { + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + const FloatV replaceBreakingThreshold = FMul(sphereRadius, FLoad(0.001f)); + Gu::HeightFieldUtil hfUtil(shapeHeight); + const PxVec3 sphereCenterShape1Space = transform1.transformInv(transform0.p); + const Vec3V sphereCenter = V3LoadU(sphereCenterShape1Space); + PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + PxVec3 inflatedRadiusV(inflatedRadius); + + PxBounds3 bounds(sphereCenterShape1Space - inflatedRadiusV, sphereCenterShape1Space + inflatedRadiusV); + + Ps::InlineArray delayedContacts; + + PCMSphereVsHeightfieldContactGenerationCallback blockCallback( + sphereCenter, + sphereRadius, + contactDist, + replaceBreakingThreshold, + sphereTransform, + heightfieldTransform, + transform1, + multiManifold, + contactBuffer, + &delayedContacts, + hfUtil); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &blockCallback); + + blockCallback.mGeneration.generateLastContacts(); + blockCallback.mGeneration.processContacts(GU_SPHERE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(sphereRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(sphereRadius, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + + } + + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, sphereTransform, heightfieldTransform, sphereRadius); +} + + +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp new file mode 100644 index 000000000..549b78c79 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuConvexUtilsInternal.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuTriangleCache.h" +#include "GuPCMContactConvexCommon.h" +#include "GuHeightFieldUtil.h" +#include "GuPCMContactMeshCallback.h" +#include "GuBox.h" + + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + + +namespace physx +{ + + +struct PCMSphereVsMeshContactGenerationCallback : PCMMeshContactGenerationCallback< PCMSphereVsMeshContactGenerationCallback > +{ + +public: + PCMSphereVsMeshContactGeneration mGeneration; + + + PCMSphereVsMeshContactGenerationCallback( + const Ps::aos::Vec3VArg sphereCenter, + const Ps::aos::FloatVArg sphereRadius, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const PsTransformV& sphereTransform, + const PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + const PxU8* extraTriData, + const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtMeshScale, + Ps::InlineArray* deferredContacts, + Cm::RenderOutput* renderOutput = NULL + ) : + PCMMeshContactGenerationCallback(meshScaling, extraTriData, idtMeshScale), + mGeneration(sphereCenter, sphereRadius, contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, contactBuffer, deferredContacts, renderOutput) + { + } + + PX_FORCE_INLINE bool doTest(const PxVec3&, const PxVec3&, const PxVec3&) { return true; } + + template + void processTriangleCache(TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + + +bool Gu::pcmContactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV sphereTransform(p0, q0);//sphere transform + const PsTransformV meshTransform(p1, q1);//triangleMesh + const PsTransformV curTransform = meshTransform.transformInv(sphereTransform); + + // We must be in local space to use the cache + if(multiManifold.invalidate(curTransform, sphereRadius, FLoad(0.02f))) + { + const FloatV replaceBreakingThreshold = FMul(sphereRadius, FLoad(0.001f)); + const PxVec3 sphereCenterShape1Space = transform1.transformInv(transform0.p); + PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + + const Vec3V sphereCenter = V3LoadU(sphereCenterShape1Space); + + const TriangleMesh* meshData = shapeMesh.meshData; + + Cm::FastVertex2ShapeScaling meshScaling; // PT: TODO: get rid of default ctor :( + const bool idtMeshScale = shapeMesh.scale.isIdentity(); + if(!idtMeshScale) + meshScaling.init(shapeMesh.scale); + + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + Ps::InlineArray delayedContacts; + + const PxU8* PX_RESTRICT extraData = meshData->getExtraTrigData(); + // mesh scale is not baked into cached verts + PCMSphereVsMeshContactGenerationCallback callback( + sphereCenter, + sphereRadius, + contactDist, + replaceBreakingThreshold, + sphereTransform, + meshTransform, + multiManifold, + contactBuffer, + extraData, + meshScaling, + idtMeshScale, + &delayedContacts, + renderOutput); + + PxVec3 obbCenter = sphereCenterShape1Space; + PxVec3 obbExtents = PxVec3(inflatedRadius); + PxMat33 obbRot(PxIdentity); + if(!idtMeshScale) + meshScaling.transformQueryBounds(obbCenter, obbExtents, obbRot); + const Box obb(obbCenter, obbExtents, obbRot); + + Midphase::intersectOBB(meshData, obb, callback, true); + + callback.flushCache(); + + callback.mGeneration.generateLastContacts(); + callback.mGeneration.processContacts(GU_SPHERE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(sphereRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(sphereRadius, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + } + + //multiManifold.drawManifold(*gRenderOutPut, sphereTransform, meshTransform); + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, sphereTransform, meshTransform, sphereRadius); +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp new file mode 100644 index 000000000..203ed52a1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp @@ -0,0 +1,86 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactBuffer.h" +#include "PsVecTransform.h" + +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" + +namespace physx +{ + +namespace Gu +{ +bool pcmContactSpherePlane(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape1); + + // Get actual shape data + const PxSphereGeometry& shapeSphere = shape0.get(); + + //sphere transform + const Vec3V p0 = V3LoadU_SafeReadW(transform0.p); // PT: safe because 'mRefCount' follows 'mTransform' in PxsTransform + + //plane transform + const Vec3V p1 = V3LoadU_SafeReadW(transform1.p); // PT: safe because 'mRefCount' follows 'mTransform' in PxsTransform + const QuatV q1 = QuatVLoadU(&transform1.q.x); + + const FloatV radius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV transf1(p1, q1); + //Sphere in plane space + const Vec3V sphereCenterInPlaneSpace = transf1.transformInv(p0); + + + //Separation + const FloatV separation = FSub(V3GetX(sphereCenterInPlaneSpace), radius); + + if(FAllGrtrOrEq(contactDist, separation)) + { + //get the plane normal + const Vec3V worldNormal = QuatGetBasisVector0(q1); + const Vec3V worldPoint = V3NegScaleSub(worldNormal, radius, p0); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(worldNormal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(worldPoint), &contact.point.x); + FStore(separation, &contact.separation); + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + return true; + } + return false; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp new file mode 100644 index 000000000..5ba3fee61 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp @@ -0,0 +1,84 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" + +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "PsVecTransform.h" + +namespace physx +{ +namespace Gu +{ +bool pcmContactSphereSphere(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + const PxSphereGeometry& shapeSphere0 = shape0.get(); + const PxSphereGeometry& shapeSphere1 = shape1.get(); + + const FloatV cDist = FLoad(params.mContactDistance); + const Vec3V p0 = V3LoadA(&transform0.p.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV r0 = FLoad(shapeSphere0.radius); + const FloatV r1 = FLoad(shapeSphere1.radius); + + + const Vec3V _delta = V3Sub(p0, p1); + const FloatV distanceSq = V3Dot(_delta, _delta); + const FloatV radiusSum = FAdd(r0, r1); + const FloatV inflatedSum = FAdd(radiusSum, cDist); + + if(FAllGrtr(FMul(inflatedSum, inflatedSum), distanceSq)) + { + const FloatV eps = FLoad(0.00001f); + const FloatV dist = FSqrt(distanceSq); + const BoolV bCon = FIsGrtrOrEq(eps, dist); + const Vec3V normal = V3Sel(bCon, V3UnitX(), V3ScaleInv(_delta, dist)); + const Vec3V point = V3ScaleAdd(normal, r1, p1); + const FloatV pen = FSub(dist, radiusSum); + + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(pen, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + return true; + } + return false; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp new file mode 100644 index 000000000..9f805a02c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp @@ -0,0 +1,210 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" +#include "GuPCMShapeConvex.h" +#include "GuVecConvexHull.h" +#include "GuPCMContactGen.h" +#include "CmRenderOutput.h" + +using namespace physx; +using namespace Gu; + +namespace physx +{ +namespace Gu +{ + const PxU8 gPCMBoxPolygonData[24] = + { + 0, 3, 2, 1, + 1, 2, 6, 5, + 5, 6, 7, 4, + 4, 7, 3, 0, + 3, 7, 6, 2, + 4, 0, 1, 5, + }; + + + Gu::PCMPolygonalBox::PCMPolygonalBox(const PxVec3& halfSide) : mHalfSide(halfSide) + { + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + PxVec3 minimum = -mHalfSide; + PxVec3 maximum = mHalfSide; + // Generate 8 corners of the bbox + mVertices[0] = PxVec3(minimum.x, minimum.y, minimum.z); + mVertices[1] = PxVec3(maximum.x, minimum.y, minimum.z); + mVertices[2] = PxVec3(maximum.x, maximum.y, minimum.z); + mVertices[3] = PxVec3(minimum.x, maximum.y, minimum.z); + mVertices[4] = PxVec3(minimum.x, minimum.y, maximum.z); + mVertices[5] = PxVec3(maximum.x, minimum.y, maximum.z); + mVertices[6] = PxVec3(maximum.x, maximum.y, maximum.z); + mVertices[7] = PxVec3(minimum.x, maximum.y, maximum.z); + + //Setup the polygons + for(PxU8 i=0; i < 6; i++) + { + mPolygons[i].mNbVerts = 4; + mPolygons[i].mVRef8 = PxU16(i*4); + } + + // ### planes needs *very* careful checks + // X axis + mPolygons[1].mPlane.n = PxVec3(1.0f, 0.0f, 0.0f); + mPolygons[1].mPlane.d = -mHalfSide.x; + mPolygons[3].mPlane.n = PxVec3(-1.0f, 0.0f, 0.0f); + mPolygons[3].mPlane.d = -mHalfSide.x; + + mPolygons[1].mMinIndex = 0; + mPolygons[3].mMinIndex = 1; + + PX_ASSERT(mPolygons[1].getMin(mVertices) == -mHalfSide.x); + PX_ASSERT(mPolygons[3].getMin(mVertices) == -mHalfSide.x); + + + // Y axis + mPolygons[4].mPlane.n = PxVec3(0.f, 1.0f, 0.0f); + mPolygons[4].mPlane.d = -mHalfSide.y; + mPolygons[5].mPlane.n = PxVec3(0.0f, -1.0f, 0.0f); + mPolygons[5].mPlane.d = -mHalfSide.y; + + mPolygons[4].mMinIndex = 0; + mPolygons[5].mMinIndex = 2; + + + PX_ASSERT(mPolygons[4].getMin(mVertices) == -mHalfSide.y); + PX_ASSERT(mPolygons[5].getMin(mVertices) == -mHalfSide.y); + + // Z axis + mPolygons[2].mPlane.n = PxVec3(0.f, 0.0f, 1.0f); + mPolygons[2].mPlane.d = -mHalfSide.z; + mPolygons[0].mPlane.n = PxVec3(0.0f, 0.0f, -1.0f); + mPolygons[0].mPlane.d = -mHalfSide.z; + + mPolygons[2].mMinIndex = 0; + mPolygons[0].mMinIndex = 4; + PX_ASSERT(mPolygons[2].getMin(mVertices) == -mHalfSide.z); + PX_ASSERT(mPolygons[0].getMin(mVertices) == -mHalfSide.z); + } + + void Gu::PCMPolygonalBox::getPolygonalData(Gu::PolygonalData* PX_RESTRICT dst) const + { + dst->mCenter = PxVec3(0.0f, 0.0f, 0.0f); + dst->mNbVerts = 8; + dst->mNbPolygons = 6; + dst->mPolygons = mPolygons; + dst->mNbEdges = 0; + dst->mVerts = mVertices; + dst->mPolygonVertexRefs = gPCMBoxPolygonData; + dst->mFacesByEdges = NULL; + dst->mVerticesByEdges = NULL; + dst->mInternal.mRadius = 0.0f; + dst->mInternal.mExtents[0] = 0.0f; + dst->mInternal.mExtents[1] = 0.0f; + dst->mInternal.mExtents[2] = 0.0f; + } + + static void getPCMPolygonalData_Convex(Gu::PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Ps::aos::Mat33V& vertexToShape) + { + using namespace Ps::aos; + const Vec3V vertexSpaceCenterOfMass = V3LoadU(src->mCenterOfMass); + const Vec3V shapeSpaceCenterOfMass = M33MulV3(vertexToShape, vertexSpaceCenterOfMass); + V3StoreU(shapeSpaceCenterOfMass, dst->mCenter); + dst->mNbVerts = src->mNbHullVertices; + dst->mNbPolygons = src->mNbPolygons; + dst->mNbEdges = src->mNbEdges; + dst->mPolygons = src->mPolygons; + dst->mVerts = src->getHullVertices(); + dst->mPolygonVertexRefs = src->getVertexData8(); + dst->mFacesByEdges = src->getFacesByEdges8(); + dst->mVerticesByEdges = src->getVerticesByEdges16(); + + dst->mBigData = src->mBigConvexRawData; + + dst->mInternal = src->mInternal; + } + + + static void getPCMPolygonalData_Convex(Gu::PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Cm::FastVertex2ShapeScaling& scaling) + { + dst->mCenter = scaling * src->mCenterOfMass; + dst->mNbVerts = src->mNbHullVertices; + dst->mNbPolygons = src->mNbPolygons; + dst->mNbEdges = src->mNbEdges; + dst->mPolygons = src->mPolygons; + dst->mVerts = src->getHullVertices(); + dst->mPolygonVertexRefs = src->getVertexData8(); + dst->mFacesByEdges = src->getFacesByEdges8(); + dst->mVerticesByEdges = src->getVerticesByEdges16(); + + dst->mBigData = src->mBigConvexRawData; + dst->mInternal = src->mInternal; + } + + + void getPCMConvexData(const Gu::ConvexHullV& convexHull, const bool idtScale, PolygonalData& polyData) + { + + PX_ASSERT(!convexHull.hullData->mAABB.isEmpty()); + + //this is used to calculate the convex hull's center of mass + getPCMPolygonalData_Convex(&polyData, convexHull.hullData, convexHull.vertex2Shape); + + if(!idtScale) + polyData.mInternal.reset(); + + } + + bool getPCMConvexData(const Gu::GeometryUnion& shape, Cm::FastVertex2ShapeScaling& scaling, PxBounds3& bounds, PolygonalData& polyData) + { + const PxConvexMeshGeometryLL& shapeConvex = shape.get(); + + const bool idtScale = shapeConvex.scale.isIdentity(); + if(!idtScale) + scaling.init(shapeConvex.scale); + + PX_ASSERT(!shapeConvex.hullData->mAABB.isEmpty()); + bounds = shapeConvex.hullData->mAABB.transformFast(scaling.getVertex2ShapeSkew()); + + getPCMPolygonalData_Convex(&polyData, shapeConvex.hullData, scaling); + + return idtScale; + } + +} +} + diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h new file mode 100644 index 000000000..a86b06b1d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_SHAPE_CONVEX_H +#define GU_PCM_SHAPE_CONVEX_H + +#include "GuConvexSupportTable.h" +#include "GuPersistentContactManifold.h" +#include "GuShapeConvex.h" + + +namespace physx +{ + +namespace Gu +{ + class GeometryUnion; + + extern const PxU8 gPCMBoxPolygonData[24]; + + class PCMPolygonalBox + { + public: + PCMPolygonalBox(const PxVec3& halfSide); + + void getPolygonalData(Gu::PolygonalData* PX_RESTRICT dst) const; + + const PxVec3& mHalfSide; + PxVec3 mVertices[8]; + Gu::HullPolygonData mPolygons[6]; + private: + PCMPolygonalBox& operator=(const PCMPolygonalBox&); + }; + + + void getPCMConvexData(const Gu::ConvexHullV& convexHull, const bool idtScale, Gu::PolygonalData& polyData); + bool getPCMConvexData(const Gu::GeometryUnion& shape, Cm::FastVertex2ShapeScaling& scaling, PxBounds3& bounds, Gu::PolygonalData& polyData); + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp new file mode 100644 index 000000000..efd27ee93 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp @@ -0,0 +1,1254 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" +#include "GuPCMTriangleContactGen.h" +#include "GuPCMContactConvexCommon.h" +#include "GuVecTriangle.h" +#include "GuBarycentricCoordinates.h" +#include "GuConvexEdgeFlags.h" + +#if PCM_LOW_LEVEL_DEBUG +#include "PxRenderBuffer.h" +#endif + +#define EDGE_EDGE_GAUSS_MAP 0 +#define BRUTE_FORCE_EDGE_EDGE 0 + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +namespace physx +{ + static bool testPolyFaceNormal(const Gu::TriangleV& triangle, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, PxU32& feature, Vec3V& faceNormal, const FeatureStatus faceStatus, FeatureStatus& status) + { + PX_UNUSED(triangle); + + FloatV _minOverlap = FMax(); + PxU32 _feature = 0; + Vec3V _faceNormal = faceNormal; + FloatV min0, max0; + FloatV min1, max1; + const FloatV eps = FEps(); + + if(polyMap->isIdentityScale) + { + //in the local space of polyData0 + for(PxU32 i=0; idoSupport(planeNormal, min1, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + _feature = i; + _faceNormal = planeNormal; + } + } + } + else + { + + //in the local space of polyData0 + for(PxU32 i=0; ishape2Vertex, vertexSpacePlaneNormal); + + const FloatV magnitude = FRsqrtFast(V3LengthSq(shapeSpacePlaneNormal)); //FRecip(V3Length(shapeSpacePlaneNormal)); + + //ML::avoid lHS, don't use the exiting function + min0 = FMul(V3Dot(vertexSpacePlaneNormal, minVert), magnitude); + max0 = FMul(FNeg(planeDist), magnitude); + + //normalize the shapeSpacePlaneNormal + const Vec3V planeN = V3Scale(shapeSpacePlaneNormal, magnitude); + + triMap->doSupport(planeN, min1, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + _feature = i; + _faceNormal = planeN; + } + } + } + + if(FAllGrtr(minOverlap, FAdd(_minOverlap, eps))) + { + faceNormal = _faceNormal; + minOverlap = _minOverlap; + status = faceStatus; + } + + feature = _feature; + + return true; + + } + + + + //triangle is in the local space of polyData + static bool testTriangleFaceNormal(const TriangleV& triangle, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, PxU32& feature, Vec3V& faceNormal, const FeatureStatus faceStatus, FeatureStatus& status) + { + PX_UNUSED(triMap); + PX_UNUSED(polyData); + + FloatV min1, max1; + const FloatV eps = FEps(); + + const Vec3V triangleLocNormal = triangle.normal(); + + const FloatV min0 = V3Dot(triangleLocNormal, triangle.verts[0]); + const FloatV max0 = min0; + + //triangle normal is in the vertex space + polyMap->doSupport(triangleLocNormal, min1, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + minOverlap = FSub(FSub(max0, min1), eps); + status = faceStatus; + feature = 0; + faceNormal=triangleLocNormal; + + return true; + + } + + static bool testPolyEdgeNormal(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status) + { + PX_UNUSED(triFlags); + FloatV overlap = minOverlap; + FloatV min0, max0; + FloatV min1, max1; + const FloatV zero = FZero(); + const Vec3V eps2 = V3Splat(FLoad(1e-6f)); + + const Vec3V v0 = M33MulV3(polyMap->shape2Vertex, triangle.verts[0]); + const Vec3V v1 = M33MulV3(polyMap->shape2Vertex, triangle.verts[1]); + const Vec3V v2 = M33MulV3(polyMap->shape2Vertex, triangle.verts[2]); + + TriangleV vertexSpaceTriangle(v0, v1, v2); + + PxU32 nbTriangleAxes = 0; + Vec3V triangleAxes[3]; + for(PxI8 kStart = 0, kEnd =2; kStart<3; kEnd = kStart++) + { + bool active = (triFlags & (1 << (kEnd+3))) != 0; + + if(active) + { + const Vec3V p00 = vertexSpaceTriangle.verts[kStart]; + const Vec3V p01 = vertexSpaceTriangle.verts[kEnd]; + triangleAxes[nbTriangleAxes++] = V3Sub(p01, p00); + } + } + + if(nbTriangleAxes == 0) + return true; + + //create localTriPlane in the vertex space + const Vec3V vertexSpaceTriangleNormal = vertexSpaceTriangle.normal(); + + for(PxU32 i =0; ishape2Vertex, v); + const Vec3V n0 = V3Normalize(shapeSpaceV); + triMap->doSupport(n0, min0, max0); + polyMap->doSupport(n0, min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if (BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if (FAllGrtr(overlap, tempOverlap)) + { + overlap = tempOverlap; + minNormal = n0; + status = edgeStatus; + } + + } + } + + } + } + minOverlap = overlap; + + return true; + + } + +#if BRUTE_FORCE_EDGE_EDGE + + + bool testPolyEdgeNormalBruteForce(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status) + { + PX_UNUSED(triFlags); + FloatV min0, max0; + FloatV min1, max1; + + FloatV bestDist = FLoad(PX_MAX_F32); + Vec3V bestAxis = V3Zero(); + + const Vec3V eps2 = V3Splat(FLoad(1e-6)); + + PxU32 bestPolyIndex = 0; + PxU32 bestStart = 0; + PxU32 bestEnd = 0; + PxI8 bestTriStart = 0; + PxI8 bestTriEnd = 0; + + for(PxU32 i =0; ivertex2Shape, p10); + const Vec3V vertex11 = M33MulV3(polyMap->vertex2Shape, p11); + + const Vec3V convexEdge = V3Sub(vertex11, vertex10); + + for (PxI8 kEnd = 0, kStart = 2; kEnd<3; kStart = kEnd++) + { + + const Vec3V triVert0 = triangle.verts[kStart]; + const Vec3V triVert1 = triangle.verts[kEnd]; + + const Vec3V triangleEdge = V3Sub(triVert1, triVert0); + const Vec3V v = V3Cross(convexEdge, triangleEdge); + + if (!V3AllGrtr(eps2, V3Abs(v))) + { + //transform the v back to the shape space + const Vec3V n0 = V3Normalize(v); + triMap->doSupport(n0, min0, max0); + polyMap->doSupport(n0, min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if (FAllGrtr(bestDist, tempOverlap)) + { + bestDist = tempOverlap; + bestAxis = n0; + bestPolyIndex = i; + bestStart = lStart; + bestEnd = lEnd; + bestTriStart = kStart; + bestTriEnd = kEnd; + } + + } + } + } + } + + if (FAllGrtr(minOverlap, bestDist)) + { + minOverlap = bestDist; + minNormal = bestAxis; + status = edgeStatus; + } + + return true; + + } + + bool testPolyEdgeNormalBruteForceVertsByEdges(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status, PxU32& bestEdgeIndex, PxI8& bestTriStart, PxI8& bestTriEnd) + { + + PX_UNUSED(triFlags); + + const PxU32 numConvexEdges = polyData.mNbEdges; + const PxU16* verticesByEdges16 = polyData.mVerticesByEdges; + const PxVec3* vertices = polyData.mVerts; + + FloatV bestDist = FLoad(PX_MAX_F32); + Vec3V bestAxis = V3Zero(); + const Vec3V eps2 = V3Splat(FLoad(1e-6)); + + for (PxU32 convexEdgeIdx = 0; convexEdgeIdx < numConvexEdges; ++convexEdgeIdx) + { + const PxU16 v0idx1 = verticesByEdges16[convexEdgeIdx * 2]; + const PxU32 v1idx1 = verticesByEdges16[convexEdgeIdx * 2 + 1]; + + //shape sapce + const Vec3V vertex10 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v0idx1])); + const Vec3V vertex11 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v1idx1])); + + Vec3V convexEdge = V3Sub(vertex11, vertex10); + + + for (PxI8 kEnd = 0, kStart = 2; kEnd<3; kStart = kEnd++) + { + const Vec3V triVert0 = triangle.verts[kStart]; + const Vec3V triVert1 = triangle.verts[kEnd]; + const Vec3V triEdge = V3Sub(triVert1, triVert0); + + // compute the separation along this axis in vertex space + Vec3V axis = V3Cross(convexEdge, triEdge); + + if (!V3AllGrtr(eps2, V3Abs(axis))) + { + axis = V3Normalize(axis); + + FloatV min0, max0; + FloatV min1, max1; + triMap->doSupport(axis, min0, max0); + polyMap->doSupport(axis, min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if (BAllEqTTTT(con)) + return false; + + const FloatV dist0 = FSub(max0, min1); + const FloatV dist1 = FSub(max1, min0); + + if (FAllGrtr(dist0, dist1)) + axis = V3Neg(axis); + + const FloatV dist = FMin(dist0, dist1); + + if (FAllGrtr(bestDist, dist)) + { + bestDist = dist; + bestAxis = axis; + + bestEdgeIndex = convexEdgeIdx; + bestTriStart = kStart; + bestTriEnd = kEnd; + } + } + } + + } + + if (FAllGrtr(minOverlap, bestDist)) + { + minOverlap = bestDist; + minNormal = bestAxis; + status = edgeStatus; + + } + return true; + + } + + +#endif + +#if EDGE_EDGE_GAUSS_MAP + + + + bool isMinkowskiFace(const Vec3V& A, const Vec3V& B, const Vec3V& B_x_A, const Vec3V& C, const Vec3V& D, const Vec3V& D_x_C) + { + + const FloatV zero = FZero(); + // Two edges build a face on the Minkowski sum if the associated arcs AB and CD intersect on the Gauss map. + // The associated arcs are defined by the adjacent face normals of each edge. + const FloatV CBA = V3Dot(C, B_x_A); + const FloatV DBA = V3Dot(D, B_x_A); + const FloatV ADC = V3Dot(A, D_x_C); + const FloatV BDC = V3Dot(B, D_x_C); + + const BoolV con0 = FIsGrtrOrEq(zero, FMul(CBA, DBA)); + const BoolV con1 = FIsGrtrOrEq(zero, FMul(ADC, BDC)); + const BoolV con2 = FIsGrtr(FMul(CBA, BDC), zero); + + const BoolV con = BAnd(con2, BAnd(con0, con1)); + + return (BAllEqTTTT(con) != 0); + + //return CBA * DBA < 0.0f && ADC * BDC < 0.0f && CBA * BDC > 0.0f; + } + +#define SAT_VARIFY 0 + + bool testPolyEdgeNormalGaussMap(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status, PxU32& gaussMapBestEdgeIndex, PxI8& gaussMapBestTriStart, PxI8& gaussMapBestTriEnd) + { + + PX_UNUSED(triFlags); + const FloatV zero = FZero(); + + const Vec3V triNormal = triangle.normal(); + + const PxU32 numConvexEdges = polyData.mNbEdges; + const PxU16* verticesByEdges16 = polyData.mVerticesByEdges; + const PxU8* facesByEdges8 = polyData.mFacesByEdges; + const PxVec3* vertices = polyData.mVerts; + + FloatV bestDist = FLoad(-PX_MAX_F32); + Vec3V axis = V3Zero(); + Vec3V bestAxis = V3Zero(); + const Vec3V eps2 = V3Splat(FLoad(1e-6)); + + + //Center is in shape space + const Vec3V shapeSpaceCOM =V3LoadU(polyData.mCenter); + const Vec3V vertexSpaceCOM = M33MulV3(polyMap->shape2Vertex, shapeSpaceCOM); + + PxVec3 vertexCOM; + V3StoreU(vertexSpaceCOM, vertexCOM); + + for (PxU32 convexEdgeIdx = 0; convexEdgeIdx < numConvexEdges; ++convexEdgeIdx) + { + + const PxU16 v0idx1 = verticesByEdges16[convexEdgeIdx*2]; + const PxU32 v1idx1 = verticesByEdges16[convexEdgeIdx * 2 + 1]; + + const PxU8 f10 = facesByEdges8[convexEdgeIdx * 2]; + const PxU8 f11 = facesByEdges8[convexEdgeIdx * 2 + 1]; + + //shape sapce + const Vec3V vertex10 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v0idx1])); + const Vec3V vertex11 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v1idx1])); + + Vec3V convexEdge = V3Sub(vertex11, vertex10); + + const Gu::HullPolygonData& face0 = polyData.mPolygons[f10]; + const Gu::HullPolygonData& face1 = polyData.mPolygons[f11]; + + const Vec3V convexNormal0 = M33TrnspsMulV3(polyMap->shape2Vertex, V3LoadU(face0.mPlane.n)); + const Vec3V convexNormal1 = M33TrnspsMulV3(polyMap->shape2Vertex, V3LoadU(face1.mPlane.n)); + + float signDist0 = face0.mPlane.distance(vertexCOM); + float signDist1 = face1.mPlane.distance(vertexCOM); + + PX_ASSERT(signDist0 < 0.f); + PX_ASSERT(signDist1 < 0.f); + + for (PxI8 kEnd = 0, kStart = 2; kEnd<3; kStart = kEnd++) + { + + const Vec3V triVert0 = triangle.verts[kStart]; + const Vec3V triVert1 = triangle.verts[kEnd]; + const Vec3V triEdge = V3Sub(triVert1, triVert0); + + //if (isMinkowskiFace(convexNormal0, convexNormal1, V3Neg(convexEdge), V3Neg(triNormal), triNormal, V3Neg(triEdge))) + if (isMinkowskiFace(convexNormal0, convexNormal1, convexEdge, V3Neg(triNormal), triNormal, triEdge)) + { + + // compute the separation along this axis in vertex space + axis = V3Cross(convexEdge, triEdge); + + if (!V3AllGrtr(eps2, V3Abs(axis))) + { + axis = V3Normalize(axis); + + const Vec3V v = V3Sub(vertex10, shapeSpaceCOM); + + // ensure the axis is outward pointing on the edge on the minkowski sum. Assure normal points from convex hull to triangle + const FloatV temp = V3Dot(axis, v); + if (FAllGrtr(zero, temp)) + axis = V3Neg(axis); + + //compute the distance from any of the verts in the triangle to plane(axis, vertex10)(n.dot(p-a)) + const Vec3V ap = V3Sub(triVert0, vertex10); + + const FloatV dist = V3Dot(axis, ap); + + if (FAllGrtr(dist, contactDist)) + return false; + +#if SAT_VARIFY + FloatV min0, max0; + FloatV min1, max1; + triMap->doSupport(V3Neg(axis), min0, max0); + polyMap->doSupport(V3Neg(axis), min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if (BAllEqTTTT(con)) + return false; + + /*const FloatV dist = FSub(max0, min1); + + if (FAllGrtr(bestDist, dist)) + { + bestDist = dist; + bestAxis = axis; + }*/ + const FloatV tempDist = FSub(max0, min1); + + const FloatV dif = FAbs(FAdd(tempDist, dist)); + PX_UNUSED(dif); + PX_ASSERT(FAllGrtr(FLoad(1e-4f), dif)); + +#endif + + if (FAllGrtr(dist, bestDist)) + { + bestDist = dist; + bestAxis = axis; + + gaussMapBestEdgeIndex = convexEdgeIdx; + gaussMapBestTriStart = kStart; + gaussMapBestTriEnd = kEnd; + } + } + } + } + + } + + if (FAllGrtr(minOverlap, bestDist)) + { + minOverlap = bestDist; + minNormal = bestAxis; + status = edgeStatus; + } + return true; + + } + +#endif + + static PX_FORCE_INLINE PxU32 addMeshContacts(MeshPersistentContact* manifoldContacts, const Vec3V& pA, const Vec3V& pB, const Vec4V& normalPen, const PxU32 triangleIndex, const PxU32 numContacts) + { + manifoldContacts[numContacts].mLocalPointA = pA; + manifoldContacts[numContacts].mLocalPointB = pB; + manifoldContacts[numContacts].mLocalNormalPen = normalPen; + manifoldContacts[numContacts].mFaceIndex = triangleIndex; + return numContacts+1; + } + + + static void generatedTriangleContacts(const Gu::TriangleV& triangle, const PxU32 triangleIndex, const PxU8/* _triFlags*/, const Gu::PolygonalData& polyData1, const Gu::HullPolygonData& incidentPolygon, Gu::SupportLocal* map1, Gu::MeshPersistentContact* manifoldContacts, PxU32& numManifoldContacts, + const Ps::aos::FloatVArg contactDist, const Ps::aos::Vec3VArg contactNormal, Cm::RenderOutput* renderOutput) + { + + PX_UNUSED(renderOutput); + using namespace Ps::aos; + + //PxU8 triFlags = _triFlags; + const PxU32 previousContacts = numManifoldContacts; + + const FloatV zero = FZero(); + + const Mat33V rot = findRotationMatrixFromZAxis(contactNormal); + + const PxU8* inds1 = polyData1.mPolygonVertexRefs + incidentPolygon.mVRef8; + + Vec3V points0In0[3]; + Vec3V* points1In0 = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*incidentPolygon.mNbVerts, 16)); + FloatV* points1In0TValue = reinterpret_cast(PxAllocaAligned(sizeof(FloatV)*incidentPolygon.mNbVerts, 16)); + bool* points1In0Penetration = reinterpret_cast(PxAlloca(sizeof(bool)*incidentPolygon.mNbVerts)); + + + points0In0[0] = triangle.verts[0]; + points0In0[1] = triangle.verts[1]; + points0In0[2] = triangle.verts[2]; + + + + //Transform all the verts from vertex space to shape space + map1->populateVerts(inds1, incidentPolygon.mNbVerts, polyData1.mVerts, points1In0); + +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map1->transform, points1In0, incidentPolygon.mNbVerts, (PxU32)PxDebugColor::eARGB_RED); + //Gu::PersistentContactManifold::drawTriangle(*renderOutput, map1->transform.transform(points1In0[0]), map1->transform.transform(points0In0[1]), map1->transform.transform(points0In0[2]), (PxU32)PxDebugColor::eARGB_BLUE); +#endif + + Vec3V eps = Vec3V_From_FloatV(FEps()); + Vec3V max = Vec3V_From_FloatV(FMax()); + Vec3V nmax = V3Neg(max); + + //transform reference polygon to 2d, calculate min and max + Vec3V rPolygonMin= max; + Vec3V rPolygonMax = nmax; + for(PxU32 i=0; i<3; ++i) + { + points0In0[i] = M33MulV3(rot, points0In0[i]); + rPolygonMin = V3Min(rPolygonMin, points0In0[i]); + rPolygonMax = V3Max(rPolygonMax, points0In0[i]); + } + + + rPolygonMin = V3Sub(rPolygonMin, eps); + rPolygonMax = V3Add(rPolygonMax, eps); + + const FloatV d = V3GetZ(points0In0[0]); + const FloatV rd = FAdd(d, contactDist); + + Vec3V iPolygonMin= max; + Vec3V iPolygonMax = nmax; + + + PxU32 inside = 0; + for(PxU32 i=0; i= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + + points1In0Penetration[i] = penetrated; + } + + + + if(inside == incidentPolygon.mNbVerts) + { + return; + } + + inside = 0; + iPolygonMin = V3Sub(iPolygonMin, eps); + iPolygonMax = V3Add(iPolygonMax, eps); + + const Vec3V incidentNormal = V3Normalize(M33TrnspsMulV3(map1->shape2Vertex, V3LoadU(incidentPolygon.mPlane.n))); + const FloatV iPlaneD = V3Dot(incidentNormal, M33MulV3(map1->vertex2Shape, V3LoadU(polyData1.mVerts[inds1[0]]))); + + for(PxU32 i=0; i<3; ++i) + { + if(contains(points1In0, incidentPolygon.mNbVerts, points0In0[i], iPolygonMin, iPolygonMax)) + { + + inside++; + + const Vec3V vert0 = M33TrnspsMulV3(rot, points0In0[i]); + const FloatV t = FSub(V3Dot(incidentNormal, vert0), iPlaneD); + + if(FAllGrtr(t, contactDist)) + continue; + + + const Vec3V projPoint = V3NegScaleSub(incidentNormal, t, vert0); + + const Vec3V v = V3Sub(projPoint, vert0); + const FloatV t3 = V3Dot(v, contactNormal); + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(contactNormal), t3); + numManifoldContacts = addMeshContacts(manifoldContacts, projPoint, vert0, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if(numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + + } + + + if(inside == 3) + return; + + //(2) segment intesection + for (PxU32 rStart = 0, rEnd = 2; rStart < 3; rEnd = rStart++) + { + + + const Vec3V rpA = points0In0[rStart]; + const Vec3V rpB = points0In0[rEnd]; + + const Vec3V rMin = V3Min(rpA, rpB); + const Vec3V rMax = V3Max(rpA, rpB); + + for (PxU32 iStart = 0, iEnd = PxU32(incidentPolygon.mNbVerts - 1); iStart < incidentPolygon.mNbVerts; iEnd = iStart++) + { + if ((!points1In0Penetration[iStart] && !points1In0Penetration[iEnd]))//|| (points1In0[i].status == POINT_OUTSIDE && points1In0[incidentIndex].status == POINT_OUTSIDE)) + continue; + + const Vec3V ipA = points1In0[iStart]; + const Vec3V ipB = points1In0[iEnd]; + + const Vec3V iMin = V3Min(ipA, ipB); + const Vec3V iMax = V3Max(ipA, ipB); + + const BoolV tempCon = BOr(V3IsGrtr(iMin, rMax), V3IsGrtr(rMin, iMax)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if (BAllEqTTTT(con)) + continue; + + FloatV a1 = signed2DTriArea(rpA, rpB, ipA); + FloatV a2 = signed2DTriArea(rpA, rpB, ipB); + + if (FAllGrtr(zero, FMul(a1, a2))) + { + FloatV a3 = signed2DTriArea(ipA, ipB, rpA); + FloatV a4 = signed2DTriArea(ipA, ipB, rpB); + + if (FAllGrtr(zero, FMul(a3, a4))) + { + + //these two segment intersect in 2d + const FloatV t = FMul(a1, FRecip(FSub(a2, a1))); + + const Vec3V ipAOri = V3SetZ(points1In0[iStart], FAdd(points1In0TValue[iStart], d)); + const Vec3V ipBOri = V3SetZ(points1In0[iEnd], FAdd(points1In0TValue[iEnd], d)); + + const Vec3V pBB = V3NegScaleSub(V3Sub(ipBOri, ipAOri), t, ipAOri); + const Vec3V pAA = V3SetZ(pBB, d); + const Vec3V pA = M33TrnspsMulV3(rot, pAA); + const Vec3V pB = M33TrnspsMulV3(rot, pBB); + const FloatV pen = FSub(V3GetZ(pBB), V3GetZ(pAA)); + + if (FAllGrtr(pen, contactDist)) + continue; + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(contactNormal), pen); + numManifoldContacts = addMeshContacts(manifoldContacts, pB, pA, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if (numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + } + + } + + } + + + static void generatedPolyContacts(const Gu::PolygonalData& polyData0, const Gu::HullPolygonData& referencePolygon, const Gu::TriangleV& triangle, const PxU32 triangleIndex, const PxU8 triFlags, + Gu::SupportLocal* map0, Gu::MeshPersistentContact* manifoldContacts, PxU32& numManifoldContacts, const Ps::aos::FloatVArg contactDist, const Ps::aos::Vec3VArg contactNormal, + Cm::RenderOutput* renderOutput) + { + PX_UNUSED(triFlags); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const PxU32 previousContacts = numManifoldContacts; + + const PxU8* inds0 = polyData0.mPolygonVertexRefs + referencePolygon.mVRef8; + + const Vec3V nContactNormal = V3Neg(contactNormal); + + //this is the matrix transform all points to the 2d plane + const Mat33V rot = findRotationMatrixFromZAxis(contactNormal); + + Vec3V* points0In0=reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + Vec3V points1In0[3]; + FloatV points1In0TValue[3]; + + bool points1In0Penetration[3]; + + //Transform all the verts from vertex space to shape space + map0->populateVerts(inds0, referencePolygon.mNbVerts, polyData0.mVerts, points0In0); + + points1In0[0] = triangle.verts[0]; + points1In0[1] = triangle.verts[1]; + points1In0[2] = triangle.verts[2]; + + +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map0->transform, points0In0, referencePolygon.mNbVerts, (PxU32)PxDebugColor::eARGB_GREEN); + //Gu::PersistentContactManifold::drawTriangle(*gRenderOutPut, map0->transform.transform(points1In0[0]), map0->transform.transform(points1In0[1]), map0->transform.transform(points1In0[2]), (PxU32)PxDebugColor::eARGB_BLUE); +#endif + + //the first point in the reference plane + const Vec3V referencePoint = points0In0[0]; + + Vec3V eps = Vec3V_From_FloatV(FEps()); + Vec3V max = Vec3V_From_FloatV(FMax()); + Vec3V nmax = V3Neg(max); + + //transform reference polygon to 2d, calculate min and max + Vec3V rPolygonMin= max; + Vec3V rPolygonMax = nmax; + for(PxU32 i=0; i= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + + } + + + if(inside == 3) + { + return; + } + + inside = 0; + iPolygonMin = V3Sub(iPolygonMin, eps); + iPolygonMax = V3Add(iPolygonMax, eps); + + const Vec3V incidentNormal = triangle.normal(); + const FloatV iPlaneD = V3Dot(incidentNormal, triangle.verts[0]); + const FloatV one = FOne(); + for(PxU32 i=0; i= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + + } + + } + + if(inside == referencePolygon.mNbVerts) + return; + + + + //Always generate segment contacts + //(2) segment intesection + for (PxU32 iStart = 0, iEnd = 2; iStart < 3; iEnd = iStart++) + { + if((!points1In0Penetration[iStart] && !points1In0Penetration[iEnd] ) ) + continue; + + const Vec3V ipA = points1In0[iStart]; + const Vec3V ipB = points1In0[iEnd]; + + const Vec3V iMin = V3Min(ipA, ipB); + const Vec3V iMax = V3Max(ipA, ipB); + + for (PxU32 rStart = 0, rEnd = PxU32(referencePolygon.mNbVerts - 1); rStart < referencePolygon.mNbVerts; rEnd = rStart++) + { + + const Vec3V rpA = points0In0[rStart]; + const Vec3V rpB = points0In0[rEnd]; + + const Vec3V rMin = V3Min(rpA, rpB); + const Vec3V rMax = V3Max(rpA, rpB); + + const BoolV tempCon =BOr(V3IsGrtr(iMin, rMax), V3IsGrtr(rMin, iMax)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if(BAllEqTTTT(con)) + continue; + + + FloatV a1 = signed2DTriArea(rpA, rpB, ipA); + FloatV a2 = signed2DTriArea(rpA, rpB, ipB); + + + if(FAllGrtr(zero, FMul(a1, a2))) + { + FloatV a3 = signed2DTriArea(ipA, ipB, rpA); + FloatV a4 = signed2DTriArea(ipA, ipB, rpB); + + if(FAllGrtr(zero, FMul(a3, a4))) + { + + //these two segment intersect + const FloatV t = FMul(a1, FRecip(FSub(a2, a1))); + + const Vec3V ipAOri = V3SetZ(points1In0[iStart], FAdd(points1In0TValue[iStart], d)); + const Vec3V ipBOri = V3SetZ(points1In0[iEnd], FAdd(points1In0TValue[iEnd], d)); + + const Vec3V pBB = V3NegScaleSub(V3Sub(ipBOri, ipAOri), t, ipAOri); + const Vec3V pAA = V3SetZ(pBB, d); + const Vec3V pA = M33TrnspsMulV3(rot, pAA); + const Vec3V pB = M33TrnspsMulV3(rot, pBB); + const FloatV pen = FSub(V3GetZ(pBB), V3GetZ(pAA)); + + if(FAllGrtr(pen, contactDist)) + continue; + + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(nContactNormal), pen); + numManifoldContacts = addMeshContacts(manifoldContacts, pA, pB, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if(numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + } + } + + } + + + bool Gu::PCMConvexVsMeshContactGeneration::generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU32* triIndices, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal) + { + + using namespace Ps::aos; + + { + + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + //minNormal will be in the local space of polyData + Vec3V minNormal = V3Zero(); + + + PxU32 feature0; + if(!testTriangleFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + if(!testPolyFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + + if (!testPolyEdgeNormal(localTriangle, triFlags, polyData, localTriMap, polyMap, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + + const Vec3V triNormal = localTriangle.normal(); + + if(status == POLYDATA0) + { + //minNormal is the triangle normal and it is in the local space of polydata0 + const Gu::HullPolygonData& referencePolygon = polyData.mPolygons[getPolygonIndex(polyData, polyMap, minNormal)]; + + patchNormal = triNormal; + generatedTriangleContacts(localTriangle, triangleIndex, triFlags, polyData, referencePolygon, polyMap, manifoldContacts, numContacts, contactDist, triNormal, mRenderOutput); + + } + else + { + + if(status == POLYDATA1) + { + const Gu::HullPolygonData* referencePolygon = &polyData.mPolygons[feature1]; + + const FloatV cosTheta = V3Dot(V3Neg(minNormal), triNormal); + + const FloatV threshold = FLoad(0.707106781f);//about 45 degree0 + + if(FAllGrtr(cosTheta, threshold)) + { + patchNormal = triNormal; + generatedTriangleContacts(localTriangle, triangleIndex, triFlags, polyData, *referencePolygon, polyMap, manifoldContacts, numContacts, contactDist, triNormal, mRenderOutput); + } + else + { + //ML : defer the contacts generation + + if (mSilhouetteEdgesAreActive || + !(triFlags & (ETD_SILHOUETTE_EDGE_01 | ETD_SILHOUETTE_EDGE_12 | ETD_SILHOUETTE_EDGE_20))) + { + const PxU32 nb = sizeof(PCMDeferredPolyData) / sizeof(PxU32); + PxU32 newSize = nb + mDeferredContacts->size(); + mDeferredContacts->reserve(newSize); + PCMDeferredPolyData* PX_RESTRICT data = reinterpret_cast(mDeferredContacts->end()); + mDeferredContacts->forceSize_Unsafe(newSize); + + data->mTriangleIndex = triangleIndex; + data->mFeatureIndex = feature1; + data->triFlags = triFlags; + data->mInds[0] = triIndices[0]; + data->mInds[1] = triIndices[1]; + data->mInds[2] = triIndices[2]; + V3StoreU(localTriangle.verts[0], data->mVerts[0]); + V3StoreU(localTriangle.verts[1], data->mVerts[1]); + V3StoreU(localTriangle.verts[2], data->mVerts[2]); + } + return true; + } + } + else + { + feature1 = PxU32(getPolygonIndex(polyData, polyMap, minNormal)); + const Gu::HullPolygonData* referencePolygon = &polyData.mPolygons[feature1]; + + const Vec3V contactNormal = V3Normalize(M33TrnspsMulV3(polyMap->shape2Vertex, V3LoadU(referencePolygon->mPlane.n))); + const Vec3V nContactNormal = V3Neg(contactNormal); + + //if the minimum sperating axis is edge case, we don't defer it because it is an activeEdge + patchNormal = nContactNormal; + generatedPolyContacts(polyData, *referencePolygon, localTriangle, triangleIndex, triFlags, polyMap, manifoldContacts, numContacts, contactDist, contactNormal, mRenderOutput); + + } + } + } + + return true; + } + + + bool Gu::PCMConvexVsMeshContactGeneration::generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal, Cm::RenderOutput* renderOutput) + { + + using namespace Ps::aos; + + const FloatV threshold = FLoad(0.7071f);//about 45 degree + PX_UNUSED(threshold); + { + + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + //minNormal will be in the local space of polyData + Vec3V minNormal = V3Zero(); + + PxU32 feature0; + if(!testTriangleFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + if(!testPolyFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + if(!testPolyEdgeNormal(localTriangle, triFlags, polyData, localTriMap, polyMap, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + const Vec3V triNormal = localTriangle.normal(); + patchNormal = triNormal; + + const Gu::HullPolygonData* referencePolygon = &polyData.mPolygons[getPolygonIndex(polyData, polyMap, triNormal)]; + generatedTriangleContacts(localTriangle, triangleIndex, triFlags, polyData, *referencePolygon, polyMap, manifoldContacts, numContacts, contactDist, triNormal, renderOutput); + + } + + return true; + } + + bool Gu::PCMConvexVsMeshContactGeneration::generatePolyDataContactManifold(Gu::TriangleV& localTriangle, const PxU32 featureIndex, const PxU32 triangleIndex, const PxU8 triFlags, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal) + { + + using namespace Ps::aos; + + const Gu::HullPolygonData* referencePolygon = &mPolyData.mPolygons[featureIndex]; + + const Vec3V contactNormal = V3Normalize(M33TrnspsMulV3(mPolyMap->shape2Vertex, V3LoadU(referencePolygon->mPlane.n))); + const Vec3V nContactNormal = V3Neg(contactNormal); + + patchNormal = nContactNormal; + generatedPolyContacts(mPolyData, *referencePolygon, localTriangle, triangleIndex, triFlags, mPolyMap, manifoldContacts, numContacts, contactDist, contactNormal, mRenderOutput); + + return true; + } + + +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h new file mode 100644 index 000000000..ece7b1050 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_TRIANGLE_CONTACT_GEN_H +#define GU_PCM_TRIANGLE_CONTACT_GEN_H + +#include "GuPCMContactGenUtil.h" +#include "GuPersistentContactManifold.h" + +namespace physx +{ + struct PxTriangleMeshGeometryLL; + class PxHeightFieldGeometry; + +namespace Gu +{ + + bool PCMContactConvexMesh(const Gu::PolygonalData& polyData0, + Gu::SupportLocal* polyMap, + const Ps::aos::FloatVArg minMargin, + const PxBounds3& hullAABB, + const PxTriangleMeshGeometryLL& shapeMesh, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, Gu::ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtConvexScale, bool idtMeshScale, Gu::MultiplePersistentContactManifold& multiManifold, + Cm::RenderOutput* renderOutput); + + bool PCMContactConvexHeightfield(const Gu::PolygonalData& polyData0, + Gu::SupportLocal* polyMap, + const Ps::aos::FloatVArg minMargin, + const PxBounds3& hullAABB, + const PxHeightFieldGeometry& shapeHeightfield, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, Gu::ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, bool idtConvexScale, Gu::MultiplePersistentContactManifold& multiManifold, + Cm::RenderOutput* renderOutput); + + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp new file mode 100644 index 000000000..1360be1c7 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp @@ -0,0 +1,2432 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "GuPersistentContactManifold.h" +#include "GuContactBuffer.h" +#include "PsAllocator.h" +#include "PsVecTransform.h" +#include "PsUtilities.h" +#include "GuGJKUtil.h" + +using namespace physx; + +namespace physx +{ +namespace Gu +{ + +/* + This local function is to avoid DLL call +*/ +static Ps::aos::FloatV distancePointSegmentSquaredLocal(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg p) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV one = FOne(); + + const Vec3V ap = V3Sub(p, a); + const Vec3V ab = V3Sub(b, a); + const FloatV nom = V3Dot(ap, ab); + + const FloatV denom = V3Dot(ab, ab); + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + + const FloatV t = FSel(FIsEq(denom, zero), zero, tValue); + const Vec3V v = V3NegScaleSub(ab, t, ap); + return V3Dot(v, v); +} + +/* + This local function is to avoid DLL call +*/ +static Ps::aos::FloatV distancePointTriangleSquaredLocal( const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c) +{ + using namespace Ps::aos; + + const FloatV zero = FZero(); + //const Vec3V zero = V3Zero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V bp = V3Sub(p, b); + const Vec3V cp = V3Sub(p, c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + + if(BAllEqTTTT(con0)) + { + const Vec3V vv = V3Sub(p, a); + return V3Dot(vv, vv); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + const Vec3V vv = V3Sub(p, b); + return V3Dot(vv, vv); + } + + //check if p in vertex region outside c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if(BAllEqTTTT(con2)) + { + const Vec3V vv = V3Sub(p, c); + return V3Dot(vv, vv); + } + + //check if p in edge region of AB + const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); + + const BoolV con30 = FIsGrtr(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtr(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + if(BAllEqTTTT(con3)) + { + const FloatV sScale = FDiv(d1, FSub(d1, d3)); + const Vec3V closest3 = V3ScaleAdd(ab, sScale, a);//V3Add(a, V3Scale(ab, sScale)); + const Vec3V vv = V3Sub(p, closest3); + return V3Dot(vv, vv); + } + + //check if p in edge region of BC + const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); + const BoolV con40 = FIsGrtr(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + if(BAllEqTTTT(con4)) + { + const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); + const Vec3V closest4 = V3ScaleAdd(bc, uScale, b);//V3Add(b, V3Scale(bc, uScale)); + const Vec3V vv = V3Sub(p, closest4); + return V3Dot(vv, vv); + } + + //check if p in edge region of AC + const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); + const BoolV con50 = FIsGrtr(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtr(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + if(BAllEqTTTT(con5)) + { + const FloatV tScale = FDiv(d2, FSub(d2, d6)); + const Vec3V closest5 = V3ScaleAdd(ac, tScale, a);//V3Add(a, V3Scale(ac, tScale)); + const Vec3V vv = V3Sub(p, closest5); + return V3Dot(vv, vv); + } + + //P must project inside face region. Compute Q using Barycentric coordinates + const Vec3V n = V3Cross(ab, ac); + const FloatV nn = V3Dot(n,n); + const FloatV t = FSel(FIsGrtr(nn, zero),FDiv(V3Dot(n, V3Sub(a, p)), nn), zero); + const Vec3V closest6 = V3Add(p, V3Scale(n, t)); + + const Vec3V vv = V3Sub(p, closest6); + + return V3Dot(vv, vv); +} + + + + +//This is the translational threshold used in invalidate_BoxConvexHull. 0.5 is 50% of the object margin. we use different threshold between +//0 and 4 points. This threashold is a scale that is multiplied by the objects' margins. +const PxF32 invalidateThresholds[5] = { 0.5f, 0.125f, 0.25f, 0.375f, 0.375f }; + +//This is the translational threshold used in invalidate_SphereCapsule. 0.5 is 50% of the object margin, we use different threshold between +//0 and 2 points. This threshold is a scale that is multiplied by the objects' margin + +const PxF32 invalidateThresholds2[3] = { 0.5f, 0.1f, 0.75f }; + +//This is the rotational threshold used in invalidate_BoxConvexHull. 0.9998 is a threshold for quat difference +//between previous and current frame +const PxF32 invalidateQuatThresholds[5] = { 0.9998f, 0.9999f, 0.9999f, 0.9999f, 0.9999f }; + + +//This is the rotational threshold used in invalidate_SphereCapsule. 0.9995f is a threshold for quat difference +//between previous and current frame +const PxF32 invalidateQuatThresholds2[3] = { 0.9995f, 0.9999f, 0.9997f }; + +} +} + + +#if VISUALIZE_PERSISTENT_CONTACT +#include "CmRenderOutput.h" + +static void drawManifoldPoint(const Gu::PersistentContact& manifold, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, Cm::RenderOutput& out, PxU32 color=0xffffff) +{ + PX_UNUSED(color); + + using namespace Ps::aos; + const Vec3V worldA = trA.transform(manifold.mLocalPointA); + const Vec3V worldB = trB.transform(manifold.mLocalPointB); + const Vec3V localNormal = Vec3V_From_Vec4V(manifold.mLocalNormalPen); + const FloatV pen = V4GetW(manifold.mLocalNormalPen); + + const Vec3V worldNormal = trB.rotate(localNormal); + PxVec3 a, b, v; + V3StoreU(worldA, a); + V3StoreU(worldB, b); + V3StoreU(worldNormal, v); + PxF32 dist; + FStore(pen, &dist); + PxVec3 e = a - v*dist; + + PxF32 size = 0.05f; + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxF32 size2 = 0.1f; + const PxVec3 up2(0.f, size2, 0.f); + const PxVec3 right2(size2, 0.f, 0.f); + const PxVec3 forwards2(0.f, 0.f, size2); + + PxMat44 m = PxMat44(PxIdentity); + + out << 0xffff00ff << m << Cm::RenderOutput::LINES << a << e; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + up << a - up; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + right << a - right; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + up2 << b - up2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + right2 << b - right2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + forwards2 << b - forwards2; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << a << b; + + PxVec3 c = a - v*10.f; + out << 0xffff00ff << m << Cm::RenderOutput::LINES << a << c; + +} + +static void drawManifoldPoint(const Gu::PersistentContact& manifold, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius, Cm::RenderOutput& out, PxU32 color=0xffffff) +{ + PX_UNUSED(color); + + using namespace Ps::aos; + + const Vec3V localNormal = Vec3V_From_Vec4V(manifold.mLocalNormalPen); + const Vec3V worldNormal = trB.rotate(localNormal); + const Vec3V worldA = V3NegScaleSub(worldNormal, radius, trA.transform(manifold.mLocalPointA)); + const Vec3V worldB = trB.transform(manifold.mLocalPointB); + const FloatV pen = FSub(V4GetW(manifold.mLocalNormalPen), radius); + + PxVec3 a, b, v; + V3StoreU(worldA, a); + V3StoreU(worldB, b); + V3StoreU(worldNormal, v); + PxF32 dist; + FStore(pen, &dist); + PxVec3 e = a - v*dist; + + PxF32 size = 0.05f; + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxF32 size2 = 0.1f; + const PxVec3 up2(0.f, size2, 0.f); + const PxVec3 right2(size2, 0.f, 0.f); + const PxVec3 forwards2(0.f, 0.f, size2); + + PxMat44 m = PxMat44(PxIdentity); + + out << 0xffff00ff << m << Cm::RenderOutput::LINES << a << e; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + up << a - up; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + right << a - right; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + up2 << b - up2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + right2 << b - right2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + forwards2 << b - forwards2; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << a << b; + +} + + +static PxU32 gColors[8] = { 0xff0000ff, 0xff00ff00, 0xffff0000, + 0xff00ffff, 0xffff00ff, 0xffffff00, + 0xff000080, 0xff008000}; + +#endif + +/* + SIMD version +*/ +Ps::aos::Mat33V Gu::findRotationMatrixFromZAxis(const Ps::aos::Vec3VArg to) +{ + using namespace Ps::aos; + + const FloatV one = FOne(); + const FloatV threshold = FLoad(0.9999f); + + const FloatV e = V3GetZ(to); + const FloatV f = FAbs(e); + + if(FAllGrtr(threshold, f)) + { + const FloatV vx = FNeg(V3GetY(to)); + const FloatV vy = V3GetX(to); + const FloatV h = FRecip(FAdd(one, e)); + const FloatV hvx = FMul(h,vx); + const FloatV hvxy = FMul(hvx, vy); + + const Vec3V col0 = V3Merge(FScaleAdd(hvx, vx, e), hvxy, vy); + const Vec3V col1 = V3Merge(hvxy, FScaleAdd(h, FMul(vy, vy), e), FNeg(vx)); + const Vec3V col2 = V3Merge(FNeg(vy), vx, e); + + return Mat33V(col0, col1, col2); + + } + else + { + + const FloatV two = FLoad(2.f); + const Vec3V from = V3UnitZ(); + const Vec3V absFrom = V3UnitY(); + + const Vec3V u = V3Sub(absFrom, from); + const Vec3V v = V3Sub(absFrom, to); + + const FloatV dotU = V3Dot(u, u); + const FloatV dotV = V3Dot(v, v); + const FloatV dotUV = V3Dot(u, v); + + const FloatV c1 = FNeg(FDiv(two, dotU)); + const FloatV c2 = FNeg(FDiv(two, dotV)); + const FloatV c3 = FMul(c1, FMul(c2, dotUV)); + + const Vec3V c1u = V3Scale(u, c1); + const Vec3V c2v = V3Scale(v, c2); + const Vec3V c3v = V3Scale(v, c3); + + + FloatV temp0 = V3GetX(c1u); + FloatV temp1 = V3GetX(c2v); + FloatV temp2 = V3GetX(c3v); + + Vec3V col0 = V3ScaleAdd(u, temp0, V3ScaleAdd(v, temp1, V3Scale(u, temp2))); + col0 = V3SetX(col0, FAdd(V3GetX(col0), one)); + + temp0 = V3GetY(c1u); + temp1 = V3GetY(c2v); + temp2 = V3GetY(c3v); + + Vec3V col1 = V3ScaleAdd(u, temp0, V3ScaleAdd(v, temp1, V3Scale(u, temp2))); + col1 = V3SetY(col1, FAdd(V3GetY(col1), one)); + + temp0 = V3GetZ(c1u); + temp1 = V3GetZ(c2v); + temp2 = V3GetZ(c3v); + + Vec3V col2 = V3ScaleAdd(u, temp0, V3ScaleAdd(v, temp1, V3Scale(u, temp2))); + col2 = V3SetZ(col2, FAdd(V3GetZ(col2), one)); + + return Mat33V(col0, col1, col2); + + } +} + + +void Gu::PersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxVec3 a, b; + V3StoreU(trA.p, a); + V3StoreU(trB.p, b); + + for(PxU32 i = 0; i< mNumContacts; ++i) + { + Gu::PersistentContact& m = mContactPoints[i]; + drawManifoldPoint(m, trA, trB, out, gColors[i]); + } +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); +#endif +} + +void Gu::PersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxVec3 a, b; + V3StoreU(trA.p, a); + V3StoreU(trB.p, b); + + for(PxU32 i = 0; i< mNumContacts; ++i) + { + Gu::PersistentContact& m = mContactPoints[i]; + drawManifoldPoint(m, trA, trB, radius, out, gColors[i]); + } +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); + PX_UNUSED(radius); +#endif +} + +void Gu::PersistentContactManifold::drawManifold(const Gu::PersistentContact& m, Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ +#if VISUALIZE_PERSISTENT_CONTACT + drawManifoldPoint(m, trA, trB, out, gColors[0]); +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); + PX_UNUSED(m); +#endif +} + +void Gu::PersistentContactManifold::drawPoint(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p, const PxF32 size, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxVec3 a; + V3StoreU(p, a); + + PxMat44 m = PxMat44(PxIdentity); + + out << color << m << Cm::RenderOutput::LINES << a + up << a - up; + out << color << m << Cm::RenderOutput::LINES << a + right << a - right; + out << color << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; +#else + PX_UNUSED(out); + PX_UNUSED(p); + PX_UNUSED(size); + PX_UNUSED(color); +#endif +} + +void Gu::PersistentContactManifold::drawLine(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + PxVec3 a, b; + V3StoreU(p0, a); + V3StoreU(p1, b); + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::LINES << a << b; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(color); +#endif +} + +void Gu::PersistentContactManifold::drawTriangle(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + PxVec3 a, b, c; + V3StoreU(p0, a); + V3StoreU(p1, b); + V3StoreU(p2, c); + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::TRIANGLES << a << b << c; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(p2); + PX_UNUSED(color); +#endif +} + +void Gu::PersistentContactManifold::drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + for(PxU32 i=0; i(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast(&contact.point.x)); + FStore(dist, &contact.separation); + + PX_ASSERT(contact.point.isFinite()); + PX_ASSERT(contact.normal.isFinite()); + PX_ASSERT(PxIsFinite(contact.separation)); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + + } + + contactBuffer.count = contactCount; +} + +/* + This function is for direct implementation for box vs box. We don't need to discard the contacts based on whether the contact offset is larger than penetration/dist because + the direct implementation will guarantee the penetration/dist won't be larger than the contact offset. Also, we won't have points from the cache if the direct implementation + of box vs box is called. +*/ +void Gu::PersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsMatTransformV& transf1) +{ + using namespace Ps::aos; + + //add the manifold contacts; + PxU32 contactCount = 0;//contactBuffer.count; + for(PxU32 i=0; (i< mNumContacts) & (contactCount < Gu::ContactBuffer::MAX_CONTACTS); ++i) + { + + PersistentContact& p = getContactPoint(i); + + const Vec3V worldP =transf1.transform(p.mLocalPointB); + const FloatV dist = V4GetW(p.mLocalNormalPen); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast(&contact.point.x)); + FStore(dist, &contact.separation); + PX_ASSERT(PxIsFinite(contact.point.x)); + PX_ASSERT(PxIsFinite(contact.point.y)); + PX_ASSERT(PxIsFinite(contact.point.z)); + + //PX_ASSERT(contact.separation > -0.2f); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + + contactBuffer.count = contactCount; +} + +/* + This function is for sphere/capsule vs other primitives. We treat sphere as a point and capsule as a segment in the contact gen and store the sphere center or a point in the segment for capsule + in the manifold. +*/ +void Gu::PersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::Vec3VArg projectionNormal, + const Ps::aos::PsTransformV& transf0, const Ps::aos::FloatVArg radius, const Ps::aos::FloatVArg contactOffset) +{ + using namespace Ps::aos; + + //add the manifold contacts; + PxU32 contactCount = 0;//contactBuffer.count; + for(PxU32 i=0; (i< mNumContacts) & (contactCount < Gu::ContactBuffer::MAX_CONTACTS); ++i) + { + + PersistentContact& p = getContactPoint(i); + const FloatV dist = FSub(V4GetW(p.mLocalNormalPen), radius); + + //The newly created points should have a dist < contactOffset. However, points might come from the PCM contact cache so we might still have points which are + //larger than contactOffset. The reason why we don't want to discard the contacts in the contact cache whose dist > contactOffset is because the contacts may + //only temporarily become separated. The points still project onto roughly the same spot but so, if the bodies move together again, the contacts may be valid again. + //This is important when simulating at large time-steps because GJK can only generate 1 point of contact and missing contacts for just a single frame with large time-steps + //may introduce noticeable instability. + if(FAllGrtrOrEq(contactOffset, dist)) + { + const Vec3V worldP =V3NegScaleSub(projectionNormal, radius, transf0.transform(p.mLocalPointA)); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast(&contact.point.x)); + FStore(dist, &contact.separation); + //PX_ASSERT(PxAbs(contact.separation) < 2.f); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + } + + contactBuffer.count = contactCount; +} + + +/* + This function is used in the box/convexhull full manifold contact genenation. We will pass in a list of manifold contacts. If the number of contacts are more than + GU_MANIFOLD_CACHE_SIZE, we will need to do contact reduction while we are storing the chosen manifold contacts from the manifold contact list to the manifold contact + buffer. +*/ +void Gu::PersistentContactManifold::addBatchManifoldContacts(const PersistentContact* manifoldContacts, + const PxU32 numPoints, const PxReal toleranceLength) +{ + + if(numPoints <= GU_MANIFOLD_CACHE_SIZE) + { + for(PxU32 i=0; i 4, we will reduce the contact points to 4 +*/ +void Gu::PersistentContactManifold::reduceBatchContacts(const PersistentContact* manifoldPoints, + const PxU32 numPoints, const PxReal tolereanceLength) +{ + using namespace Ps::aos; + + PxU8 chosenIndices[4]; + PxU8 candidates[64]; + + const FloatV zero = FZero(); + const FloatV max = FMax(); + const FloatV nmax = FNeg(max); + FloatV maxPen = V4GetW(manifoldPoints[0].mLocalNormalPen); + FloatV minPen = nmax; + PxU32 index = 0; + candidates[0] = 0; + PxU32 candidateIndex = 0; + + PxU32 nbCandiates = numPoints; + //keep the deepest point, candidateIndex will be the same as index + for (PxU32 i = 1; i 2, we will reduce the contact points to 2 +*/ +void Gu::PersistentContactManifold::reduceBatchContacts2(const PersistentContact* manifoldPoints, const PxU32 numPoints) +{ + using namespace Ps::aos; + + PX_ASSERT(numPoints < 64); + bool chosen[64]; + physx::PxMemZero(chosen, sizeof(bool)*numPoints); + FloatV maxDis = V4GetW(manifoldPoints[0].mLocalNormalPen); + PxI32 index = 0; + //keep the deepest point + for(PxU32 i=1; imStartIndex; jmEndIndex; ++j) + { + mContactPoints[tempNumContacts++] = manifoldContact[j]; + } + currentPatch = currentPatch->mNextPatch; + } + mNumContacts = tempNumContacts; + return patch.mPatchMaxPen; + } + else + { + //contact reduction + const FloatV maxPen = reduceBatchContactsConvex(manifoldContact, numContactExt, patch); + mNumContacts = GU_SINGLE_MANIFOLD_CACHE_SIZE; + return maxPen; + } +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::addBatchManifoldContactsSphere(const MeshPersistentContact* manifoldContact, const PxU32 numContactExt, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold) +{ + PX_UNUSED(replaceBreakingThreshold); + + using namespace Ps::aos; + + const FloatV maxPen = reduceBatchContactsSphere(manifoldContact, numContactExt, patch); + mNumContacts = 1; + return maxPen; +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::addBatchManifoldContactsCapsule(const MeshPersistentContact* manifoldContact, const PxU32 numContactExt, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold) +{ + PX_UNUSED(replaceBreakingThreshold); + + using namespace Ps::aos; + + if(patch.mTotalSize <=GU_CAPSULE_MANIFOLD_CACHE_SIZE) + { + PCMContactPatch* currentPatch = &patch; + + //this is because we already add the manifold contacts into manifoldContact array + PxU32 tempNumContacts = 0; + while(currentPatch) + { + for(PxU32 j=currentPatch->mStartIndex; jmEndIndex; ++j) + { + mContactPoints[tempNumContacts++] = manifoldContact[j]; + } + currentPatch = currentPatch->mNextPatch; + } + mNumContacts = tempNumContacts; + return patch.mPatchMaxPen; + } + else + { + + const FloatV maxPen = reduceBatchContactsCapsule(manifoldContact, numContactExt, patch); + mNumContacts = GU_CAPSULE_MANIFOLD_CACHE_SIZE; + return maxPen; + } + +} + + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::reduceBatchContactsSphere(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch) +{ + PX_UNUSED(numContacts); + + using namespace Ps::aos; + FloatV max = FMax(); + FloatV maxDist = max; + PxI32 index = -1; + + + PCMContactPatch* currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + if(FAllGrtr(maxDist, pen)) + { + maxDist = pen; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + PX_ASSERT(index!=-1); + mContactPoints[0] = manifoldContactExt[index]; + + return maxDist; + +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::reduceBatchContactsCapsule(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch) +{ + using namespace Ps::aos; + + bool* chosen = reinterpret_cast(PxAlloca(sizeof(bool)*numContacts)); + physx::PxMemZero(chosen, sizeof(bool)*numContacts); + const FloatV max = FMax(); + FloatV maxDis = max; + PxI32 index = -1; + + FloatV maxPen = max; + + + PCMContactPatch* currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + if(FAllGrtr(maxDis, pen)) + { + maxDis = pen; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + chosen[index] = true; + mContactPoints[0] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + + //calculate the furthest away points + Vec3V v = V3Sub(manifoldContactExt[patch.mStartIndex].mLocalPointB, mContactPoints[0].mLocalPointB); + maxDis = V3Dot(v, v); + index = PxI32(patch.mStartIndex); + + currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(v, v); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + mContactPoints[1] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + //keep the second deepest point + maxDis = max; + currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + if(!chosen[i]) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + //const FloatV v = V3Dot(manifoldContactExt[i].mLocalPointB, manifoldContactExt[i].mLocalPointB); + if(FAllGrtr(maxDis, pen)) + { + maxDis = pen; + index = PxI32(i); + } + } + } + currentPatch = currentPatch->mNextPatch; + } + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + mContactPoints[2] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + return maxPen; +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::reduceBatchContactsConvex(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch) +{ + using namespace Ps::aos; + + bool* chosen = reinterpret_cast(PxAlloca(sizeof(bool)*numContacts)); + physx::PxMemZero(chosen, sizeof(bool)*numContacts); + const FloatV max = FMax(); + const FloatV nmax = FNeg(max); + FloatV maxDis = nmax; + PxU32 index = GU_MANIFOLD_INVALID_INDEX; + + PCMContactPatch* currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + //const FloatV pen = V4GetW(manifoldContactExt[i].localNormalPen); + const FloatV v = V3Dot(manifoldContactExt[i].mLocalPointB, manifoldContactExt[i].mLocalPointB); + if(FAllGrtr(v, maxDis)) + { + maxDis = v; + index = i; + } + } + currentPatch = currentPatch->mNextPatch; + } + + chosen[index] = true; + + const Vec3V contact0 = manifoldContactExt[index].mLocalPointB; + mContactPoints[0] = manifoldContactExt[index]; + + FloatV maxPen = V4GetW(manifoldContactExt[index].mLocalNormalPen); + + //calculate the furthest away points + Vec3V v = V3Sub(manifoldContactExt[patch.mStartIndex].mLocalPointB, contact0); + maxDis = V3Dot(v, v); + index = patch.mStartIndex; + + currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, contact0); + const FloatV d = V3Dot(v, v); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = i; + } + } + currentPatch = currentPatch->mNextPatch; + } + + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + const Vec3V contact1 = manifoldContactExt[index].mLocalPointB; + mContactPoints[1] = manifoldContactExt[index]; + + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + maxDis = nmax; + index = GU_MANIFOLD_INVALID_INDEX; + v = V3Sub(contact1, contact0); + const Vec3V cn0 = Vec3V_From_Vec4V(mContactPoints[0].mLocalNormalPen); + Vec3V norm = V3Cross(v, cn0); + const FloatV sqLen = V3Dot(norm, norm); + norm = V3Sel(FIsGrtr(sqLen, FZero()), V3ScaleInv(norm, FSqrt(sqLen)), cn0); + FloatV minDis = max; + PxU32 index1 = GU_MANIFOLD_INVALID_INDEX; + + + //calculate the point furthest way to the segment + currentPatch = &patch; + + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, contact0); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = i; + } + + if(FAllGrtr(minDis, d)) + { + minDis = d; + index1 = i; + } + } + } + currentPatch = currentPatch->mNextPatch; + } + + //PX_ASSERT(chosen[index] == false); + + chosen[index] = true; + mContactPoints[2] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + //if min and max in the same side, choose again + const FloatV temp = FMul(minDis, maxDis); + if(FAllGrtr(temp, FZero())) + { + //choose again + maxDis = nmax; + //calculate the point furthest way to the segment + currentPatch = &patch; + + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, contact0); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index1 = i; + } + } + } + currentPatch = currentPatch->mNextPatch; + } + } + + //PX_ASSERT(chosen[index1] == false); + + chosen[index1] = true; + mContactPoints[3] = manifoldContactExt[index1]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index1].mLocalNormalPen)); + + const PxU32 NB_TO_ADD = GU_SINGLE_MANIFOLD_CACHE_SIZE - 4; + + FloatV pens[NB_TO_ADD]; + PxU32 inds[NB_TO_ADD]; + for (PxU32 a = 0; a < NB_TO_ADD; ++a) + { + pens[a] = FMax(); + inds[a] = 0; + } + { + currentPatch = &patch; + + while (currentPatch) + { + for (PxU32 i = currentPatch->mStartIndex; i < currentPatch->mEndIndex; ++i) + { + if (!chosen[i]) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + for (PxU32 a = 0; a < NB_TO_ADD; ++a) + { + if (FAllGrtr(pens[a], pen)) + { + for (PxU32 b = a + 1; b < NB_TO_ADD; ++b) + { + pens[b] = pens[b - 1]; + inds[b] = inds[b - 1]; + } + pens[a] = pen; + inds[a] = i; + break; + } + } + } + } + currentPatch = currentPatch->mNextPatch; + } + for (PxU32 i = 0; i < NB_TO_ADD; ++i) + { + mContactPoints[i + 4] = manifoldContactExt[inds[i]]; + maxPen = FMin(maxPen, pens[i]); + } + } + return maxPen; +} + +PxU32 Gu::SinglePersistentContactManifold::reduceContacts(MeshPersistentContact* manifoldPoints, PxU32 numPoints) +{ + using namespace Ps::aos; + + PxU8 chosenIndices[GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE]; + PxU8* candidates = reinterpret_cast(PxAlloca(sizeof(PxU8) * numPoints)); + + const FloatV max = FMax(); + const FloatV nmax = FNeg(max); + const FloatV zero = FZero(); + FloatV maxPen = V4GetW(manifoldPoints[0].mLocalNormalPen); + PxU32 index = 0; + candidates[0] = 0; + PxU32 candidateIndex = 0; + PxU32 nbCandiates = numPoints; + + MeshPersistentContact newManifold[GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE]; + + //keep the deepest point + for (PxU32 i = 1; i 0; --i) + { + MeshPersistentContact& manifoldPoint = mContactPoints[i-1]; + const Vec3V localAInB = aToB.transform( manifoldPoint.mLocalPointA ); // from a to b + const Vec3V localBInB = manifoldPoint.mLocalPointB; + const Vec3V v = V3Sub(localAInB, localBInB); + + const Vec3V localNormal = Vec3V_From_Vec4V(manifoldPoint.mLocalNormalPen); // normal in b space + const FloatV dist= V3Dot(v, localNormal); + + const Vec3V projectedPoint = V3NegScaleSub(localNormal, dist, localAInB);//manifoldPoint.worldPointA - manifoldPoint.worldPointB * manifoldPoint.m_distance1; + const Vec3V projectedDifference = V3Sub(localBInB, projectedPoint); + + const FloatV distance2d = V3Dot(projectedDifference, projectedDifference); + //const BoolV con = BOr(FIsGrtr(dist, contactOffset), FIsGrtr(distance2d, sqProjectBreakingThreshold)); + const BoolV con = FIsGrtr(distance2d, sqProjectBreakingThreshold); + if(BAllEqTTTT(con)) + { + removeContactPoint(i-1); + } + else + { + manifoldPoint.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), dist); + maxPen = FMin(maxPen, dist); + } + } + + return maxPen; +} + + +void Gu::SinglePersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxVec3 a, b; + V3StoreU(trA.p, a); + V3StoreU(trB.p, b); + + for(PxU32 i = 0; i< mNumContacts; ++i) + { + Gu::MeshPersistentContact& m = mContactPoints[i]; + drawManifoldPoint(m, trA, trB, out, gColors[i]); + } +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); +#endif +} + +void Gu::MultiplePersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ + for(PxU32 i=0; idrawManifold(out, trA, trB); + } +} + +void Gu::MultiplePersistentContactManifold::drawLine(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + PxVec3 a, b; + V3StoreU(p0, a); + V3StoreU(p1, b); + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::LINES << a << b; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(color); + +#endif +} + +void Gu::MultiplePersistentContactManifold::drawLine(Cm::RenderOutput& out, const PxVec3 p0, const PxVec3 p1, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::LINES << p0 << p1; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(color); +#endif +} + +void Gu::MultiplePersistentContactManifold::drawPoint(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p, const PxF32 size, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxVec3 a; + V3StoreU(p, a); + + PxMat44 m = PxMat44(PxIdentity); + + out << color << m << Cm::RenderOutput::LINES << a + up << a - up; + out << color << m << Cm::RenderOutput::LINES << a + right << a - right; + out << color << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; +#else + PX_UNUSED(out); + PX_UNUSED(p); + PX_UNUSED(size); + PX_UNUSED(color); +#endif +} + + +void Gu::MultiplePersistentContactManifold::drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + for(PxU32 i=0; iaddBatchManifoldContactsSphere(manifoldContact, numManifoldContacts, *patch, sqReplaceBreakingThreshold); + case GU_CAPSULE_MANIFOLD_CACHE_SIZE://capsule, need to implement keep two deepest + return manifold->addBatchManifoldContactsCapsule(manifoldContact, numManifoldContacts, *patch, sqReplaceBreakingThreshold); + default://cache size GU_SINGLE_MANIFOLD_CACHE_SIZE + return manifold->addBatchManifoldContactsConvex(manifoldContact, numManifoldContacts, *patch, sqReplaceBreakingThreshold); + }; +} + +/* + This function adds the manifold contacts with different patches into the corresponding single persistent contact manifold +*/ +void Gu::MultiplePersistentContactManifold::addManifoldContactPoints(MeshPersistentContact* manifoldContact, PxU32 numManifoldContacts, PCMContactPatch** contactPatch, const PxU32 numPatch, const Ps::aos::FloatVArg sqReplaceBreakingThreshold, const Ps::aos::FloatVArg acceptanceEpsilon, PxU8 maxContactsPerManifold) +{ + using namespace Ps::aos; + + if(mNumManifolds == 0) + { + for(PxU32 i=0; imRoot == patch) + { + + SinglePersistentContactManifold* manifold = getEmptyManifold(); + if(manifold) + { + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(manifold, manifoldContact, numManifoldContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[mNumManifolds]]); + mNumManifolds++; + } + else + { + //We already pre-sorted the patches so we know we can return here + return; + } + + } + } + + + } + else + { + //we do processContacts() when the number of contacts are more than 16, such that, we might call processContacts() multiple times when we have very detailed mesh + //or very large contact offset/objects. In this case, the mNumManifolds will be large than 0. + PCMContactPatch tempPatch; + for(PxU32 i=0; imRoot == patch) + { + PX_ASSERT(mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + for(PxU32 j=0; jmPatchNormal, pNor); + + if(FAllGrtrOrEq(d, acceptanceEpsilon)) + { + //appending the existing contacts to the manifold contact stream + for(PxU32 k=0; k< manifold.mNumContacts; ++k) + { + PxU32 index = k + numManifoldContacts; + //not duplicate point + PX_ASSERT(index < 64); + manifoldContact[index] = manifold.mContactPoints[k]; + } + + //create a new patch for the exiting manifold + tempPatch.mStartIndex = numManifoldContacts; + tempPatch.mEndIndex = numManifoldContacts + manifold.mNumContacts; + tempPatch.mPatchNormal = pNor;//manifold.getLocalNormal(); + tempPatch.mRoot = patch; + tempPatch.mNextPatch = NULL; + + patch->mEndPatch->mNextPatch = &tempPatch; + + //numManifoldContacts += manifold.numContacts; + patch->mTotalSize += manifold.mNumContacts; + patch->mPatchMaxPen = FMin(patch->mPatchMaxPen, FLoad(mMaxPen[mManifoldIndices[j]])); + + //manifold.numContacts = 0; + + PX_ASSERT((numManifoldContacts+manifold.mNumContacts) <= 64); + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(&manifold, manifoldContact, numManifoldContacts+manifold.mNumContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[j]]); + found = true; + break; + } + } + + if(!found)// && numManifolds < 4) + { + SinglePersistentContactManifold* manifold = getEmptyManifold(); + //we still have slot to create a new manifold + if(manifold) + { + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(manifold, manifoldContact, numManifoldContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[mNumManifolds]]); + mNumManifolds++; + } + else + { + //we can't allocate a new manifold and no existing manifold has the same normal as this patch, we need to find the shallowest penetration manifold. If this manifold is shallower than + //the current patch, replace the manifold with the current patch + PxU32 index = 0; + for(PxU32 j=1; j mMaxPen[mManifoldIndices[index]]) + { + index = j; + } + } + + if(FAllGrtr(FLoad(mMaxPen[mManifoldIndices[index]]), patch->mPatchMaxPen)) + { + manifold = getManifold(index); + manifold->mNumContacts = 0; + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(manifold, manifoldContact, numManifoldContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[index]]); + + } + return; + } + } + } + } + } +} + +//This function adds the multi manifold contacts to the contact buffer for box/convexhull +bool Gu::MultiplePersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::PsTransformV& meshTransform) +{ + using namespace Ps::aos; + PxU32 contactCount = 0;//contactBuffer.count; + PxU32 numContacts = 0; + mNumTotalContacts = 0; + //drawManifold(*gRenderOutPut, convexTransform, meshTransform); + for(PxU32 i=0; i < mNumManifolds; ++i) + { + Gu::SinglePersistentContactManifold& manifold = *getManifold(i); + numContacts = manifold.getNumContacts(); + PX_ASSERT(mNumTotalContacts + numContacts <= 0xFF); + mNumTotalContacts += Ps::to8(numContacts); + const Vec3V normal = manifold.getWorldNormal(meshTransform); + + for(PxU32 j=0; (j< numContacts) & (contactCount < ContactBuffer::MAX_CONTACTS); ++j) + { + Gu::MeshPersistentContact& p = manifold.getContactPoint(j); + + const Vec3V worldP =meshTransform.transform(p.mLocalPointB); + const FloatV dist = V4GetW(p.mLocalNormalPen); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast(&contact.point.x)); + FStore(dist, &contact.separation); + + contact.internalFaceIndex1 = p.mFaceIndex; + + } + } + + PX_ASSERT(contactCount <= 64); + contactBuffer.count = contactCount; + return contactCount > 0; +} + +//This function adds the multi manifold contacts to the contact buffer for sphere and capsule, radius is corresponding to the sphere radius/capsule radius +bool Gu::MultiplePersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius) +{ + using namespace Ps::aos; + PxU32 contactCount = 0; + PxU32 numContacts = 0; + mNumTotalContacts = 0; + + for(PxU32 i=0; i < mNumManifolds; ++i) + { + //get a single manifold + Gu::SinglePersistentContactManifold& manifold = *getManifold(i); + numContacts = manifold.getNumContacts(); + PX_ASSERT(mNumTotalContacts + numContacts <= 0xFF); + mNumTotalContacts += Ps::to8(numContacts); + const Vec3V normal = manifold.getWorldNormal(trB); + + //iterate all the contacts in this single manifold and add contacts to the contact buffer + //each manifold contact point store two points which are in each other's local space. In this + //case, mLocalPointA is stored as the center of sphere/a point in the segment for capsule + for(PxU32 j=0; (j< numContacts) & (contactCount < ContactBuffer::MAX_CONTACTS); ++j) + { + Gu::MeshPersistentContact& p = manifold.getContactPoint(j); + + const Vec3V worldP =V3NegScaleSub(normal, radius, trA.transform(p.mLocalPointA)); + const FloatV dist = FSub(V4GetW(p.mLocalNormalPen), radius); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast(&contact.point.x)); + FStore(dist, &contact.separation); + + contact.internalFaceIndex1 = p.mFaceIndex; + + } + } + + PX_ASSERT(contactCount <= 64); + contactBuffer.count = contactCount; + return contactCount > 0; +} + + +/* + This function copies the mesh persistent contact from compress buffer(NpCacheStreamPair in the PxcNpThreadContext) to the multiple manifold +*/ +// PT: function moved to cpp to go around a compiler bug on PS4 +void physx::Gu::MultiplePersistentContactManifold::fromBuffer(PxU8* PX_RESTRICT buffer) +{ + using namespace Ps::aos; + PxU32 numManifolds = 0; + if (buffer != NULL) + { + PX_ASSERT(((uintptr_t(buffer)) & 0xF) == 0); + PxU8* PX_RESTRICT buff = buffer; + MultiPersistentManifoldHeader* PX_RESTRICT header = reinterpret_cast(buff); + buff += sizeof(MultiPersistentManifoldHeader); + + numManifolds = header->mNumManifolds; + + PX_ASSERT(numManifolds <= GU_MAX_MANIFOLD_SIZE); + mRelativeTransform = header->mRelativeTransform; + + for (PxU32 a = 0; a < numManifolds; ++a) + { + mManifoldIndices[a] = PxU8(a); + SingleManifoldHeader* PX_RESTRICT manHeader = reinterpret_cast(buff); + buff += sizeof(SingleManifoldHeader); + PxU32 numContacts = manHeader->mNumContacts; + PX_ASSERT(numContacts <= GU_SINGLE_MANIFOLD_CACHE_SIZE); + SinglePersistentContactManifold& manifold = mManifolds[a]; + manifold.mNumContacts = numContacts; + PX_ASSERT((uintptr_t(buff) & 0xf) == 0); + CachedMeshPersistentContact* contacts = reinterpret_cast(buff); + for (PxU32 b = 0; b < manifold.mNumContacts; ++b) + { + manifold.mContactPoints[b].mLocalPointA = Vec3V_From_Vec4V(V4LoadA(&contacts[b].mLocalPointA.x)); + manifold.mContactPoints[b].mLocalPointB = Vec3V_From_Vec4V(V4LoadA(&contacts[b].mLocalPointB.x)); + manifold.mContactPoints[b].mLocalNormalPen = V4LoadA(&contacts[b].mLocalNormal.x); + manifold.mContactPoints[b].mFaceIndex = contacts[b].mFaceIndex; + } + buff += sizeof(Gu::CachedMeshPersistentContact) * numContacts; + } + } + else + { + mRelativeTransform.Invalidate(); + } + mNumManifolds = PxU8(numManifolds); + for (PxU32 a = numManifolds; a < GU_MAX_MANIFOLD_SIZE; ++a) + { + mManifoldIndices[a] = PxU8(a); + } +} + +void Gu::addManifoldPoint(Gu::PersistentContact* manifoldContacts, Gu::PersistentContactManifold& manifold, GjkOutput& output, + const Ps::aos::PsMatTransformV& aToB, const Ps::aos::FloatV replaceBreakingThreshold) +{ + using namespace Ps::aos; + + const Vec3V localPointA = aToB.transformInv(output.closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = output.closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint(localPointA, output.closestB, localNormalPen, replaceBreakingThreshold); +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h new file mode 100644 index 000000000..5dc8dd965 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h @@ -0,0 +1,855 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef Gu_PERSISTENTCONTACTMANIFOLD_H +#define Gu_PERSISTENTCONTACTMANIFOLD_H + +#include "PxPhysXCommonConfig.h" +#include "foundation/PxUnionCast.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "PsVecTransform.h" + +#define PCM_LOW_LEVEL_DEBUG 0 + +namespace physx +{ + +#define VISUALIZE_PERSISTENT_CONTACT 1 +//This is for pritimives vs primitives +#define GU_MANIFOLD_CACHE_SIZE 4 +//These are for pritimives vs mesh +#define GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE 5 +#define GU_SINGLE_MANIFOLD_CACHE_SIZE 6 +#define GU_SPHERE_MANIFOLD_CACHE_SIZE 1 +#define GU_CAPSULE_MANIFOLD_CACHE_SIZE 3 +#define GU_MAX_MANIFOLD_SIZE 6 +#define GU_MESH_CONTACT_REDUCTION_THRESHOLD 16 + +#define GU_MANIFOLD_INVALID_INDEX 0xffffffff + + +//ML: this is used to compared with the shape's margin to decide the final tolerance used in the manifold to validate the existing contacts. +//In the case of big shape and relatively speaking small triangles in the mesh, we need to take a smaller margin. This helps because the PCM +//recycling thresholds are proportionate to margin so it makes it less likely to discard previous contacts due to separation +#define GU_PCM_MESH_MANIFOLD_EPSILON 0.05f + + +namespace Cm +{ + class RenderOutput; +} + + +namespace Gu +{ + struct ContactPoint; + class ContactBuffer; + struct GjkOutput; + +extern const PxF32 invalidateThresholds[5]; +extern const PxF32 invalidateQuatThresholds[5]; +extern const PxF32 invalidateThresholds2[3]; +extern const PxF32 invalidateQuatThresholds2[3]; + + +Ps::aos::Mat33V findRotationMatrixFromZAxis(const Ps::aos::Vec3VArg to); + +//This contact is used in the primitives vs primitives contact gen +class PersistentContact +{ +public: + PersistentContact() + { + } + + PersistentContact(Ps::aos::Vec3V _localPointA, Ps::aos::Vec3V _localPointB, Ps::aos::Vec4V _localNormalPen) : + mLocalPointA(_localPointA), mLocalPointB(_localPointB), mLocalNormalPen(_localNormalPen) + { + + } + + Ps::aos::Vec3V mLocalPointA; + Ps::aos::Vec3V mLocalPointB; + Ps::aos::Vec4V mLocalNormalPen; // the (x, y, z) is the local normal, and the w is the penetration depth +}; + +//This contact is used in the mesh contact gen to store an extra variable +class MeshPersistentContact : public PersistentContact +{ +public: + PxU32 mFaceIndex; +}; + +//This contact is used in the compress stream buffer(NpCacheStreamPair in the PxcNpThreadContext) to store the data we need +PX_ALIGN_PREFIX(16) +class CachedMeshPersistentContact +{ +public: + PxVec3 mLocalPointA; //16 byte aligned + PxU32 mFaceIndex; //face index + PxVec3 mLocalPointB; //16 byte aligned + PxU32 mPad; //pad + PxVec3 mLocalNormal; //16 byte aligned + PxReal mPen; //penetration +}PX_ALIGN_SUFFIX(16); + + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +//This class is used to store the start and end index of the mesh persistent contacts in an array. +struct PCMContactPatch +{ +public: + PCMContactPatch() + { + mNextPatch = NULL; + mEndPatch = NULL; + mRoot = this; + mPatchMaxPen = Ps::aos::FMax(); + } + Ps::aos::Vec3V mPatchNormal; + PCMContactPatch* mNextPatch;//store the next patch pointer in the patch list + PCMContactPatch* mEndPatch;//store the last patch pointer in the patch list + PCMContactPatch* mRoot;//if this is the start of the patch list which has very similar patch normal, the root will be itself + Ps::aos::FloatV mPatchMaxPen;//store the deepest penetration of the whole patch + PxU32 mStartIndex;//store the start index of the manifold contacts in the manifold contacts stream + PxU32 mEndIndex;//store the end index of the manifold contacts in the manifold contacts stream + PxU32 mTotalSize;//if this is the root, the total size will store the total number of manifold contacts in the whole patch list +}; + +#if PX_VC + #pragma warning(pop) +#endif + +//ML: this is needed because it seems NEON doesn't force the alignment on SIMD type, which cause some of the PCM unit tests fail +PX_ALIGN_PREFIX(16) +class PersistentContactManifold +{ +public: + + PersistentContactManifold(PersistentContact* contactPointsBuff, PxU8 capacity): mNumContacts(0), mCapacity(capacity), mNumWarmStartPoints(0), mContactPoints(contactPointsBuff) + { + mRelativeTransform.Invalidate(); + } + + PX_FORCE_INLINE PxU32 getNumContacts() const { return mNumContacts;} + + PX_FORCE_INLINE bool isEmpty() { return mNumContacts==0; } + + PX_FORCE_INLINE PersistentContact& getContactPoint(const PxU32 index) + { + PX_ASSERT(index < GU_MANIFOLD_CACHE_SIZE); + return mContactPoints[index]; + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformdelta(const Ps::aos::PsTransformV& curTransform) + { + using namespace Ps::aos; + const Vec4V p0 = Vec4V_From_Vec3V(mRelativeTransform.p); + const Vec4V q0 = mRelativeTransform.q; + const Vec4V p1 = Vec4V_From_Vec3V(curTransform.p); + const Vec4V q1 = curTransform.q; + + const Vec4V dp = V4Abs(V4Sub(p1, p0)); + const Vec4V dq = V4Abs(V4Sub(q1, q0)); + + const Vec4V max0 = V4Max(dp, dq); + + //need to work out max from a single vector... + return V4ExtractMax(max0); + } + + PX_FORCE_INLINE Ps::aos::Vec4V maxTransformdelta2(const Ps::aos::PsTransformV& curTransform) + { + using namespace Ps::aos; + const Vec4V p0 = Vec4V_From_Vec3V(mRelativeTransform.p); + const Vec4V q0 = mRelativeTransform.q; + const Vec4V p1 = Vec4V_From_Vec3V(curTransform.p); + const Vec4V q1 = curTransform.q; + + const Vec4V dp = V4Abs(V4Sub(p1, p0)); + const Vec4V dq = V4Abs(V4Sub(q1, q0)); + + const Vec4V max0 = V4Max(dp, dq); + + //need to work out max from a single vector... + return max0; + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformPositionDelta(const Ps::aos::Vec3V& curP) + { + using namespace Ps::aos; + + const Vec3V deltaP = V3Sub(curP, mRelativeTransform.p); + const Vec4V delta = Vec4V_From_Vec3V(V3Abs(deltaP)); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformQuatDelta(const Ps::aos::QuatV& curQ) + { + using namespace Ps::aos; + + const Vec4V deltaQ = V4Sub(curQ, mRelativeTransform.q); + const Vec4V delta = V4Abs(deltaQ); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE void setRelativeTransform(const Ps::aos::PsTransformV& transform) + { + mRelativeTransform = transform; + } + + //This is used for the box/convexhull vs box/convexhull contact gen to decide whether the relative movement of a pair of objects are + //small enough. In this case, we can skip the collision detection all together + PX_FORCE_INLINE PxU32 invalidate_BoxConvex(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin) + { + using namespace Ps::aos; + PX_ASSERT(mNumContacts <= GU_MANIFOLD_CACHE_SIZE); + const FloatV ratio = FLoad(invalidateThresholds[mNumContacts]); + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + + const FloatV thresholdQ = FLoad(invalidateQuatThresholds[mNumContacts]); + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + //This is used for the sphere/capsule vs other primitives contact gen to decide whether the relative movement of a pair of objects are + //small enough. In this case, we can skip the collision detection all together + PX_FORCE_INLINE PxU32 invalidate_SphereCapsule(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin) + { + using namespace Ps::aos; + PX_ASSERT(mNumContacts <= 2); + const FloatV ratio = FLoad(invalidateThresholds2[mNumContacts]); + + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + + const FloatV thresholdQ = FLoad(invalidateQuatThresholds2[mNumContacts]); + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + //This is used for plane contact gen to decide whether the relative movement of a pair of objects are small enough. In this case, + //we can skip the collision detection all together + PX_FORCE_INLINE PxU32 invalidate_PrimitivesPlane(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin, const Ps::aos::FloatVArg ratio) + { + using namespace Ps::aos; + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + + const FloatV thresholdQ = FLoad(0.9998f);//about 1 degree + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + PX_FORCE_INLINE void removeContactPoint (PxU32 index) + { + mNumContacts--; + mContactPoints[index] = mContactPoints[mNumContacts]; + } + + bool validContactDistance(const PersistentContact& pt, const Ps::aos::FloatVArg breakingThreshold) const + { + using namespace Ps::aos; + const FloatV dist = V4GetW(pt.mLocalNormalPen); + return FAllGrtr(breakingThreshold, dist) != 0; + } + + PX_FORCE_INLINE void clearManifold() + { + mNumWarmStartPoints = 0; + mNumContacts = 0; + mRelativeTransform.Invalidate(); + } + + PX_FORCE_INLINE void initialize() + { + clearManifold(); + } + + //This function is used to replace the existing contact with the newly created contact if their distance are within some threshold + bool replaceManifoldPoint(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen, const Ps::aos::FloatVArg replaceBreakingThreshold); + + //This function is to add a point(in box/convexhull contact gen) to the exising manifold. If the number of manifold is more than 4, we need to do contact reduction + PxU32 addManifoldPoint( const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalAPen, const Ps::aos::FloatVArg replaceBreakingThreshold); + //This function is to add a point(in capsule contact gen) to the exising manifold. If the number of manifold is more than 4, we need to do contact reduction + PxU32 addManifoldPoint2( const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalAPen, const Ps::aos::FloatVArg replaceBreakingThreshold);//max two points of contacts + //This function is used in box-plane contact gen to add the plane contacts to the manifold + void addBatchManifoldContactsCluster( const PersistentContact* manifoldPoints, const PxU32 numPoints); + + //This function is used in the capsule full manifold contact genenation(maximum 2 points). + void addBatchManifoldContacts2( const PersistentContact* manifoldPoints, const PxU32 numPoints);//max two points of contacts + + //This function is used in the box/convexhull full manifold contact generation(maximum 4 points). + void addBatchManifoldContacts(const PersistentContact* manifoldPoints, const PxU32 numPoints, const PxReal toleranceLength); + //This function is using the cluster algorithm to reduce contacts + void reduceBatchContactsCluster(const PersistentContact* manifoldPoints, const PxU32 numPoints); + //This function is called by addBatchManifoldContacts2 to reduce the manifold contacts to 2 points; + void reduceBatchContacts2(const PersistentContact* manifoldPoints, const PxU32 numPoints); + //This function is called by addBatchManifoldContacts to reduce the manifold contacts to 4 points + void reduceBatchContacts(const PersistentContact* manifoldPoints, const PxU32 numPoints, const PxReal toleranceLength); + + + //This function is used for incremental manifold contact reduction for box/convexhull + PxU32 reduceContactsForPCM(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen); + //This function is used for incremental manifold contact reduction for capsule + PxU32 reduceContactSegment(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen); + + + /* + This function recalculate the contacts in the manifold based on the current relative transform between a pair of objects. If the recalculated contacts are within some threshold, + we will keep the contacts; Otherwise, we will remove the contacts. + */ + void refreshContactPoints(const Ps::aos::PsMatTransformV& relTra, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg contactOffset); + //This function is just used in boxbox contact gen for fast transform + void addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsMatTransformV& transf1); + //This function is for adding box/convexhull manifold contacts to the contact buffer + void addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsTransformV& transf1, const Ps::aos::FloatVArg contactOffset); + //This function is for adding sphere/capsule manifold contacts to the contact buffer + void addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::Vec3VArg projectionNormal, const Ps::aos::PsTransformV& transf0, const Ps::aos::FloatVArg radius, const Ps::aos::FloatVArg contactOffset); + + //get the average normal in the manifold in world space + Ps::aos::Vec3V getWorldNormal(const Ps::aos::PsTransformV& trB); + //get the average normal in the manifold in local B object space + Ps::aos::Vec3V getLocalNormal(); + + void recordWarmStart(PxU8* aIndices, PxU8* bIndices, PxU8& nbWarmStartPoints); + void setWarmStart(const PxU8* aIndices, const PxU8* bIndices, const PxU8 nbWarmStartPoints); + void drawManifold(Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB); + void drawManifold(Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius); + void drawManifold(const PersistentContact& m, Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB); + static void drawPoint(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p, const PxF32 size, const PxU32 color = 0x0000ff00); + static void drawLine(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const PxU32 color = 0xff00ffff); + static void drawTriangle(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const PxU32 color = 0xffff0000); + static void drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color = 0xff00ffff); + static void drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsMatTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color = 0xff00ffff); + + Ps::aos::PsTransformV mRelativeTransform;//aToB + PxU8 mNumContacts; + PxU8 mCapacity; + PxU8 mNumWarmStartPoints; + PxU8 mAIndice[4]; + PxU8 mBIndice[4]; + PersistentContact* mContactPoints; +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +class LargePersistentContactManifold : public PersistentContactManifold +{ +public: + LargePersistentContactManifold() : PersistentContactManifold(mContactPointsBuff, GU_MANIFOLD_CACHE_SIZE) + { + } + + PersistentContact mContactPointsBuff[GU_MANIFOLD_CACHE_SIZE]; +}PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +class SpherePersistentContactManifold : public PersistentContactManifold +{ +public: + SpherePersistentContactManifold() : PersistentContactManifold(mContactPointsBuff, GU_SPHERE_MANIFOLD_CACHE_SIZE) + { + } + + PersistentContact mContactPointsBuff[GU_SPHERE_MANIFOLD_CACHE_SIZE]; +}PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +class SinglePersistentContactManifold +{ +public: + + SinglePersistentContactManifold(): mNumContacts(0) + { + } + + PX_FORCE_INLINE PxU32 getNumContacts() const { return mNumContacts;} + + PX_FORCE_INLINE bool isEmpty() { return mNumContacts==0; } + + PX_FORCE_INLINE MeshPersistentContact& getContactPoint(const PxU32 index) + { + PX_ASSERT(index < GU_SINGLE_MANIFOLD_CACHE_SIZE); + return mContactPoints[index]; + } + + PX_FORCE_INLINE void removeContactPoint (PxU32 index) + { + mNumContacts--; + mContactPoints[index] = mContactPoints[mNumContacts]; + } + + PX_FORCE_INLINE void clearManifold() + { + mNumContacts = 0; + } + + PX_FORCE_INLINE void initialize() + { + clearManifold(); + } + + + PX_FORCE_INLINE Ps::aos::Vec3V getWorldNormal(const Ps::aos::PsTransformV& trB) + { + using namespace Ps::aos; + Vec4V nPen = mContactPoints[0].mLocalNormalPen; + for(PxU32 i =1; i < mNumContacts; ++i) + { + nPen = V4Add(nPen, mContactPoints[i].mLocalNormalPen); + } + + const Vec3V n = Vec3V_From_Vec4V(nPen); + return V3Normalize(trB.rotate(n)); + } + + PX_FORCE_INLINE Ps::aos::Vec3V getLocalNormal() + { + using namespace Ps::aos; + Vec4V nPen = mContactPoints[0].mLocalNormalPen; + for(PxU32 i =1; i < mNumContacts; ++i) + { + nPen = V4Add(nPen, mContactPoints[i].mLocalNormalPen); + } + return V3Normalize(Vec3V_From_Vec4V(nPen)); + } + + //This function reduces the manifold contact list in a patch for box/convexhull vs mesh + Ps::aos::FloatV reduceBatchContactsConvex(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch); + //This function reduces the manifold contact list in a patch for sphere vs mesh + Ps::aos::FloatV reduceBatchContactsSphere(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch); + //This function reduces the manifold contact list in a pathc for capsuel vs mesh + Ps::aos::FloatV reduceBatchContactsCapsule(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch); + + //This function adds the manifold contact list in a patch for box/convexhull vs mesh + Ps::aos::FloatV addBatchManifoldContactsConvex(const MeshPersistentContact* manifoldContact, const PxU32 numContacts, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold); + //This function adds the manifold contact list in a patch for sphere vs mesh + Ps::aos::FloatV addBatchManifoldContactsSphere(const MeshPersistentContact* manifoldContact, const PxU32 numContacts, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold); + //This function adds the manifold contact list in a patch for capsule vs mesh + Ps::aos::FloatV addBatchManifoldContactsCapsule(const MeshPersistentContact* manifoldContact, const PxU32 numContacts, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold); + + //This is used for in the addContactsToPatch for convex mesh contact gen. + static PxU32 reduceContacts(MeshPersistentContact* manifoldContactExt, PxU32 numContacts); + + //This function is to recalculate the contacts based on the relative transform between a pair of objects + Ps::aos::FloatV refreshContactPoints(const Ps::aos::PsMatTransformV& relTra, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg contactOffset); + + void drawManifold(Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB); + + MeshPersistentContact mContactPoints[GU_SINGLE_MANIFOLD_CACHE_SIZE];//384 bytes + PxU32 mNumContacts;//400 bytes + +} PX_ALIGN_SUFFIX(16); + +//This is a structure used to cache a multi-persistent-manifold in the cache stream +struct MultiPersistentManifoldHeader +{ + Ps::aos::PsTransformV mRelativeTransform;//aToB + PxU32 mNumManifolds; + PxU32 pad[3]; +}; + +struct SingleManifoldHeader +{ + PxU32 mNumContacts; + PxU32 pad[3]; +}; + +#if PX_VC +#pragma warning(push) +#pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + +PX_ALIGN_PREFIX(16) +class PX_PHYSX_COMMON_API MultiplePersistentContactManifold +{ +public: + MultiplePersistentContactManifold():mNumManifolds(0), mNumTotalContacts(0) + { + mRelativeTransform.Invalidate(); + } + + PX_FORCE_INLINE void setRelativeTransform(const Ps::aos::PsTransformV& transform) + { + mRelativeTransform = transform; + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformPositionDelta(const Ps::aos::Vec3V& curP) + { + using namespace Ps::aos; + + const Vec3V deltaP = V3Sub(curP, mRelativeTransform.p); + const Vec4V delta = Vec4V_From_Vec3V(V3Abs(deltaP)); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformQuatDelta(const Ps::aos::QuatV& curQ) + { + using namespace Ps::aos; + + const Vec4V deltaQ = V4Sub(curQ, mRelativeTransform.q); + const Vec4V delta = V4Abs(deltaQ); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE PxU32 invalidate(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin, const Ps::aos::FloatVArg ratio) + { + using namespace Ps::aos; + + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + const FloatV thresholdQ = FLoad(0.9998f);//about 1 degree + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + PX_FORCE_INLINE PxU32 invalidate(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin) + { + using namespace Ps::aos; + return invalidate(curRTrans, minMargin, FLoad(0.2f)); + } + + /* + This function work out the contact patch connectivity. If two patches's normal are within 5 degree, we would link these two patches together and reset the total size. + */ + PX_FORCE_INLINE void refineContactPatchConnective(PCMContactPatch** contactPatch, PxU32 numContactPatch, MeshPersistentContact* manifoldContacts, const Ps::aos::FloatVArg acceptanceEpsilon) + { + PX_UNUSED(manifoldContacts); + + using namespace Ps::aos; + + //work out the contact patch connectivity, the patchNormal should be in the local space of mesh + for(PxU32 i=0; imRoot = patch; + patch->mEndPatch = patch; + patch->mTotalSize = patch->mEndIndex - patch->mStartIndex; + patch->mNextPatch = NULL; + + for(PxU32 j=i; j>0; --j) + { + PCMContactPatch* other = contactPatch[j-1]; + const FloatV d = V3Dot(patch->mPatchNormal, other->mRoot->mPatchNormal); + if(FAllGrtrOrEq(d, acceptanceEpsilon))//less than 5 degree + { + + other->mNextPatch = patch; + other->mRoot->mEndPatch = patch; + patch->mRoot = other->mRoot; + other->mRoot->mTotalSize += patch->mEndIndex - patch->mStartIndex; + break; + } + } + } + } + + + /* + This function uses to reduce the manifold contacts which are in different connected patchs but are within replace breaking threshold + */ + PX_FORCE_INLINE PxU32 reduceManifoldContactsInDifferentPatches(PCMContactPatch** contactPatch, PxU32 numContactPatch, MeshPersistentContact* manifoldContacts, PxU32 numContacts, const Ps::aos::FloatVArg sqReplaceBreaking) + { + using namespace Ps::aos; + + for(PxU32 i=0; imRoot == currentPatch) + { + while(currentPatch) + { + PCMContactPatch* nextPatch = currentPatch->mNextPatch; + if(nextPatch) + { + for(PxU32 k = currentPatch->mStartIndex; kmEndIndex; ++k) + { + for(PxU32 l = nextPatch->mStartIndex; l < nextPatch->mEndIndex; ++l) + { + Vec3V dif = V3Sub(manifoldContacts[l].mLocalPointB, manifoldContacts[k].mLocalPointB); + FloatV d = V3Dot(dif, dif); + if(FAllGrtr(sqReplaceBreaking, d)) + { + //if two manifold contacts are within threshold, we will get rid of the manifold contacts in the other contact patch + manifoldContacts[l] = manifoldContacts[nextPatch->mEndIndex-1]; + nextPatch->mEndIndex--; + numContacts--; + l--; + } + } + } + } + currentPatch = nextPatch; + } + } + } + + return numContacts; + } + + + /* + This function is for the multiple manifold loop through each individual single manifold to recalculate the contacts based on the relative transform between a pair of objects + */ + PX_FORCE_INLINE void refreshManifold(const Ps::aos::PsMatTransformV& relTra, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg contactDist) + { + using namespace Ps::aos; + + //refresh manifold contacts + for(PxU32 i=0; i < mNumManifolds; ++i) + { + PxU8 ind = mManifoldIndices[i]; + PX_ASSERT(mManifoldIndices[i] < GU_MAX_MANIFOLD_SIZE); + PxU32 nextInd = PxMin(i, mNumManifolds-2u)+1; + Ps::prefetchLine(&mManifolds[mManifoldIndices[nextInd]]); + Ps::prefetchLine(&mManifolds[mManifoldIndices[nextInd]],128); + Ps::prefetchLine(&mManifolds[mManifoldIndices[nextInd]],256); + FloatV _maxPen = mManifolds[ind].refreshContactPoints(relTra, projectBreakingThreshold, contactDist); + if(mManifolds[ind].isEmpty()) + { + //swap the index with the next manifolds + PxU8 index = mManifoldIndices[--mNumManifolds]; + mManifoldIndices[mNumManifolds] = ind; + mManifoldIndices[i] = index; + i--; + } + else + { + FStore(_maxPen, &mMaxPen[ind]); + } + } + } + + + PX_FORCE_INLINE void initialize() + { + mNumManifolds = 0; + mNumTotalContacts = 0; + mRelativeTransform.Invalidate(); + for(PxU8 i=0; i 0; --i) + { + PersistentContact& manifoldPoint = mContactPoints[i-1]; + const Vec3V localAInB = aToB.transform( manifoldPoint.mLocalPointA ); // from a to b + const Vec3V localBInB = manifoldPoint.mLocalPointB; + const Vec3V v = V3Sub(localAInB, localBInB); + + const Vec3V localNormal = Vec3V_From_Vec4V(manifoldPoint.mLocalNormalPen); // normal in b space + const FloatV dist= V3Dot(v, localNormal); + + const Vec3V projectedPoint = V3NegScaleSub(localNormal, dist, localAInB);//manifoldPoint.worldPointA - manifoldPoint.worldPointB * manifoldPoint.m_distance1; + const Vec3V projectedDifference = V3Sub(localBInB, projectedPoint); + + const FloatV distance2d = V3Dot(projectedDifference, projectedDifference); + //const BoolV con = BOr(FIsGrtr(dist, contactOffset), FIsGrtr(distance2d, sqProjectBreakingThreshold)); + const BoolV con = FIsGrtr(distance2d, sqProjectBreakingThreshold); + if(BAllEqTTTT(con)) + { + removeContactPoint(i-1); + } + else + { + manifoldPoint.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), dist); + } + } +} + +/* + This function copies the mesh persistent contact from the multiple manifold to compress buffer(NpCacheStreamPair in the PxcNpThreadContext) +*/ +PX_INLINE void MultiplePersistentContactManifold::toBuffer(PxU8* PX_RESTRICT buffer) +{ + using namespace Ps::aos; + PxU8* buff = buffer; + + PX_ASSERT(((uintptr_t(buff)) & 0xF) == 0); + MultiPersistentManifoldHeader* PX_RESTRICT header = reinterpret_cast(buff); + buff += sizeof(MultiPersistentManifoldHeader); + + PX_ASSERT(mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + header->mNumManifolds = mNumManifolds; + header->mRelativeTransform = mRelativeTransform; + + for(PxU32 a = 0; a < mNumManifolds; ++a) + { + SingleManifoldHeader* manHeader = reinterpret_cast(buff); + buff += sizeof(SingleManifoldHeader); + SinglePersistentContactManifold& manifold = *getManifold(a); + manHeader->mNumContacts = manifold.mNumContacts; + PX_ASSERT((uintptr_t(buff) & 0xf) == 0); + CachedMeshPersistentContact* contacts = reinterpret_cast(buff); + //convert the mesh persistent contact to cached mesh persistent contact to save 16 byte memory per contact + for(PxU32 b = 0; b(contactPoint)) //this is used in the normal pcm contact gen +#define PX_CP_TO_MPCP(contactPoint) (reinterpret_cast(contactPoint))//this is used in the mesh pcm contact gen + +void addManifoldPoint(PersistentContact* manifoldContacts, PersistentContactManifold& manifold, GjkOutput& output, + const Ps::aos::PsMatTransformV& aToB, const Ps::aos::FloatV replaceBreakingThreshold); + +}//Gu +}//physx + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp new file mode 100644 index 000000000..a7210a921 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp @@ -0,0 +1,272 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepBoxBox.h" +#include "GuBox.h" +#include "GuIntersectionBoxBox.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionEdgeEdge.h" +#include "GuSweepSharedTests.h" +#include "CmMatrix34.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +namespace +{ +// PT: TODO: get rid of this copy +static const PxReal gFatBoxEdgeCoeff = 0.01f; + +// PT: TODO: get rid of this copy +static const PxVec3 gNearPlaneNormal[] = +{ + PxVec3(1.0f, 0.0f, 0.0f), + PxVec3(0.0f, 1.0f, 0.0f), + PxVec3(0.0f, 0.0f, 1.0f), + PxVec3(-1.0f, 0.0f, 0.0f), + PxVec3(0.0f, -1.0f, 0.0f), + PxVec3(0.0f, 0.0f, -1.0f) +}; + +#define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) + +static PxVec3 EdgeNormals[] = +{ + PxVec3(0, -INVSQRT2, -INVSQRT2), // 0-1 + PxVec3(INVSQRT2, 0, -INVSQRT2), // 1-2 + PxVec3(0, INVSQRT2, -INVSQRT2), // 2-3 + PxVec3(-INVSQRT2, 0, -INVSQRT2), // 3-0 + + PxVec3(0, INVSQRT2, INVSQRT2), // 7-6 + PxVec3(INVSQRT2, 0, INVSQRT2), // 6-5 + PxVec3(0, -INVSQRT2, INVSQRT2), // 5-4 + PxVec3(-INVSQRT2, 0, INVSQRT2), // 4-7 + + PxVec3(INVSQRT2, -INVSQRT2, 0), // 1-5 + PxVec3(INVSQRT2, INVSQRT2, 0), // 6-2 + PxVec3(-INVSQRT2, INVSQRT2, 0), // 3-7 + PxVec3(-INVSQRT2, -INVSQRT2, 0) // 4-0 +}; + +// PT: TODO: get rid of this copy +static const PxVec3* getBoxLocalEdgeNormals() +{ + return EdgeNormals; +} + +/** +Returns world edge normal +\param edgeIndex [in] 0 <= edge index < 12 +\param worldNormal [out] edge normal in world space +*/ +static void computeBoxWorldEdgeNormal(const Box& box, PxU32 edgeIndex, PxVec3& worldNormal) +{ + PX_ASSERT(edgeIndex<12); + worldNormal = box.rotate(getBoxLocalEdgeNormals()[edgeIndex]); +} + +} + +// ### optimize! and refactor. And optimize for aabbs +bool Gu::sweepBoxBox(const Box& box0, const Box& box1, const PxVec3& dir, PxReal length, PxHitFlags hitFlags, PxSweepHit& sweepHit) +{ + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(intersectOBBOBB(box0.extents, box0.center, box0.rot, box1.extents, box1.center, box1.rot, true)) + { + sweepHit.flags = PxHitFlag::eNORMAL; + sweepHit.distance = 0.0f; + sweepHit.normal = -dir; + return true; + } + } + + PxVec3 boxVertices0[8]; box0.computeBoxPoints(boxVertices0); + PxVec3 boxVertices1[8]; box1.computeBoxPoints(boxVertices1); + + // float MinDist = PX_MAX_F32; + PxReal MinDist = length; + int col = -1; + + // In following VF tests: + // - the direction is FW/BK since we project one box onto the other *and vice versa* + // - the normal reaction is FW/BK for the same reason + + // Vertices1 against Box0 + { + // We need: + + // - Box0 in local space + const PxVec3 Min0 = -box0.extents; + const PxVec3 Max0 = box0.extents; + + // - Vertices1 in Box0 space + Matrix34 worldToBox0; + computeWorldToBoxMatrix(worldToBox0, box0); + + // - the dir in Box0 space + const PxVec3 localDir0 = worldToBox0.rotate(dir); + + const PxVec3* boxNormals0 = gNearPlaneNormal; + + for(PxU32 i=0; i<8; i++) + { + PxReal tnear, tfar; + const int plane = intersectRayAABB(Min0, Max0, worldToBox0.transform(boxVertices1[i]), -localDir0, tnear, tfar); + + if(plane==-1 || tnear<0.0f) + continue; + + if(tnear <= MinDist) + { + MinDist = tnear; + sweepHit.normal = box0.rotate(boxNormals0[plane]); + sweepHit.position = boxVertices1[i]; + col = 0; + } + } + } + + // Vertices0 against Box1 + { + // We need: + + // - Box1 in local space + const PxVec3 Min1 = -box1.extents; + const PxVec3 Max1 = box1.extents; + + // - Vertices0 in Box1 space + Matrix34 worldToBox1; + computeWorldToBoxMatrix(worldToBox1, box1); + + // - the dir in Box1 space + const PxVec3 localDir1 = worldToBox1.rotate(dir); + + const PxVec3* boxNormals1 = gNearPlaneNormal; + + for(PxU32 i=0; i<8; i++) + { + PxReal tnear, tfar; + const int plane = intersectRayAABB(Min1, Max1, worldToBox1.transform(boxVertices0[i]), localDir1, tnear, tfar); + + if(plane==-1 || tnear<0.0f) + continue; + + if(tnear <= MinDist) + { + MinDist = tnear; + sweepHit.normal = box1.rotate(-boxNormals1[plane]); + sweepHit.position = boxVertices0[i] + tnear * dir; + col = 1; + } + } + } + + PxVec3 p1s, p2s, p3s, p4s; + { + const PxU8* PX_RESTRICT edges0 = getBoxEdges(); + const PxU8* PX_RESTRICT edges1 = getBoxEdges(); + + PxVec3 edgeNormals0[12]; + PxVec3 edgeNormals1[12]; + for(PxU32 i=0; i<12; i++) + computeBoxWorldEdgeNormal(box0, i, edgeNormals0[i]); + for(PxU32 i=0; i<12; i++) + computeBoxWorldEdgeNormal(box1, i, edgeNormals1[i]); + + // Loop through box edges + for(PxU32 i=0; i<12; i++) // 12 edges + { + if(!(edgeNormals0[i].dot(dir) >= 0.0f)) + continue; + + // Catch current box edge // ### one vertex already known using line-strips + + // Make it fat ### + PxVec3 p1 = boxVertices0[edges0[i*2+0]]; + PxVec3 p2 = boxVertices0[edges0[i*2+1]]; + Ps::makeFatEdge(p1, p2, gFatBoxEdgeCoeff); + + // Loop through box edges + for(PxU32 j=0;j<12;j++) + { + if(edgeNormals1[j].dot(dir) >= 0.0f) + continue; + + // Orientation culling + // PT: this was commented for some reason, but it fixes the "stuck" bug reported by Ubi. + // So I put it back. We'll have to see whether it produces Bad Things in particular cases. + if(edgeNormals0[i].dot(edgeNormals1[j]) >= 0.0f) + continue; + + // Catch current box edge + + // Make it fat ### + PxVec3 p3 = boxVertices1[edges1[j*2+0]]; + PxVec3 p4 = boxVertices1[edges1[j*2+1]]; + Ps::makeFatEdge(p3, p4, gFatBoxEdgeCoeff); + + PxReal Dist; + PxVec3 ip; + if(intersectEdgeEdge(p1, p2, dir, p3, p4, Dist, ip)) + { + if(Dist<=MinDist) + { + p1s = p1; + p2s = p2; + p3s = p3; + p4s = p4; + + sweepHit.position = ip + Dist * dir; + + col = 2; + MinDist = Dist; + } + } + } + } + } + + if(col==-1) + return false; + + if(col==2) + { + computeEdgeEdgeNormal(sweepHit.normal, p1s, p2s-p1s, p3s, p4s-p3s, dir, MinDist); + sweepHit.normal.normalize(); + } + + sweepHit.flags = PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + sweepHit.distance = MinDist; + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h new file mode 100644 index 000000000..efb643ca3 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h @@ -0,0 +1,49 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_BOX_H +#define GU_SWEEP_BOX_BOX_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Box; + + bool sweepBoxBox(const Box& box0, const Box& box1, const PxVec3& dir, PxReal length, PxHitFlags hitFlags, PxSweepHit& sweepHit); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp new file mode 100644 index 000000000..21a99edfd --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp @@ -0,0 +1,158 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepBoxSphere.h" +#include "GuOverlapTests.h" +#include "GuSphere.h" +#include "GuBoxConversion.h" +#include "GuCapsule.h" +#include "GuIntersectionRayCapsule.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionSphereBox.h" +#include "GuDistancePointSegment.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +namespace +{ +// PT: TODO: get rid of this copy +static const PxVec3 gNearPlaneNormal[] = +{ + PxVec3(1.0f, 0.0f, 0.0f), + PxVec3(0.0f, 1.0f, 0.0f), + PxVec3(0.0f, 0.0f, 1.0f), + PxVec3(-1.0f, 0.0f, 0.0f), + PxVec3(0.0f, -1.0f, 0.0f), + PxVec3(0.0f, 0.0f, -1.0f) +}; + +} + +bool Gu::sweepBoxSphere(const Box& box, PxReal sphereRadius, const PxVec3& spherePos, const PxVec3& dir, PxReal length, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags) +{ + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(intersectSphereBox(Sphere(spherePos, sphereRadius), box)) + { + // Overlap + min_dist = 0.0f; + normal = -dir; + return true; + } + } + + PxVec3 boxPts[8]; + box.computeBoxPoints(boxPts); + const PxU8* PX_RESTRICT edges = getBoxEdges(); + PxReal MinDist = length; + bool Status = false; + for(PxU32 i=0; i<12; i++) + { + const PxU8 e0 = *edges++; + const PxU8 e1 = *edges++; + const Capsule capsule(boxPts[e0], boxPts[e1], sphereRadius); + + PxReal t; + if(intersectRayCapsule(spherePos, dir, capsule, t)) + { + if(t>=0.0f && t<=MinDist) + { + MinDist = t; + + const PxVec3 ip = spherePos + t*dir; + distancePointSegmentSquared(capsule, ip, &t); + + PxVec3 ip2; + capsule.computePoint(ip2, t); + + normal = (ip2 - ip); + normal.normalize(); + Status = true; + } + } + } + + PxVec3 localPt; + { + Matrix34 M2; + buildMatrixFromBox(M2, box); + + localPt = M2.rotateTranspose(spherePos - M2.p); + } + + const PxVec3* boxNormals = gNearPlaneNormal; + + const PxVec3 localDir = box.rotateInv(dir); + + // PT: when the box exactly touches the sphere, the test for initial overlap can fail on some platforms. + // In this case we reach the sweep code below, which may return a slightly negative time of impact (it should be 0.0 + // but it ends up a bit negative because of limited FPU accuracy). The epsilon ensures that we correctly detect a hit + // in this case. + const PxReal epsilon = -1e-5f; + + PxReal tnear, tfar; + + PxVec3 extents = box.extents; + extents.x += sphereRadius; + int plane = intersectRayAABB(-extents, extents, localPt, localDir, tnear, tfar); + if(plane!=-1 && tnear>=epsilon && tnear <= MinDist) + { + MinDist = PxMax(tnear, 0.0f); + normal = box.rotate(boxNormals[plane]); + Status = true; + } + + extents = box.extents; + extents.y += sphereRadius; + plane = intersectRayAABB(-extents, extents, localPt, localDir, tnear, tfar); + if(plane!=-1 && tnear>=epsilon && tnear <= MinDist) + { + MinDist = PxMax(tnear, 0.0f); + normal = box.rotate(boxNormals[plane]); + Status = true; + } + + extents = box.extents; + extents.z += sphereRadius; + plane = intersectRayAABB(-extents, extents, localPt, localDir, tnear, tfar); + if(plane!=-1 && tnear>=epsilon && tnear <= MinDist) + { + MinDist = PxMax(tnear, 0.0f); + normal = box.rotate(boxNormals[plane]); + Status = true; + } + + min_dist = MinDist; + + return Status; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h new file mode 100644 index 000000000..0e561f2c4 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h @@ -0,0 +1,49 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_SPHERE_H +#define GU_SWEEP_BOX_SPHERE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Box; + + bool sweepBoxSphere(const Box& box, PxReal sphereRadius, const PxVec3& spherePos, const PxVec3& dir, PxReal length, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp new file mode 100644 index 000000000..659e40023 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp @@ -0,0 +1,622 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxBounds3.h" +#include "GuSweepBoxTriangle_FeatureBased.h" +#include "GuIntersectionRayBox.h" +#include "PxTriangle.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; + +#define LOCAL_EPSILON 0.00001f // PT: this value makes the 'basicAngleTest' pass. Fails because of a ray almost parallel to a triangle + +namespace +{ +static const PxReal gFatTriangleCoeff = 0.02f; + +static const PxVec3 gNearPlaneNormal[] = +{ + PxVec3(1.0f, 0.0f, 0.0f), + PxVec3(0.0f, 1.0f, 0.0f), + PxVec3(0.0f, 0.0f, 1.0f), + PxVec3(-1.0f, 0.0f, 0.0f), + PxVec3(0.0f, -1.0f, 0.0f), + PxVec3(0.0f, 0.0f, -1.0f) +}; +} + +#define INVSQRT3 0.577350269189f //!< 1 / sqrt(3) + +/** +Returns vertex normals. +\return 24 floats (8 normals) +*/ +static const PxF32* getBoxVertexNormals() +{ + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + static PxF32 VertexNormals[] = + { + -INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, INVSQRT3, INVSQRT3, + -INVSQRT3, INVSQRT3, INVSQRT3 + }; + + return VertexNormals; +} + +static PxTriangle inflateTriangle(const PxTriangle& triangle, PxReal fat_coeff) +{ + PxTriangle fatTri = triangle; + + // Compute triangle center + const PxVec3& p0 = triangle.verts[0]; + const PxVec3& p1 = triangle.verts[1]; + const PxVec3& p2 = triangle.verts[2]; + const PxVec3 center = (p0 + p1 + p2)*0.333333333f; + + // Don't normalize? + // Normalize => add a constant border, regardless of triangle size + // Don't => add more to big triangles + for(PxU32 i=0;i<3;i++) + { + const PxVec3 v = fatTri.verts[i] - center; + fatTri.verts[i] += v * fat_coeff; + } + return fatTri; +} + +// PT: special version to fire N parallel rays against the same tri +static PX_FORCE_INLINE Ps::IntBool rayTriPrecaCull( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, const PxVec3& pvec, + PxReal det, PxReal oneOverDet, PxReal& t) +{ + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter and test bounds + PxReal u = tvec.dot(pvec); + if((u < 0.0f) || u>det) + return 0; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + PxReal v = dir.dot(qvec); + if((v < 0.0f) || u+v>det) + return 0; + + // Calculate t, scale parameters, ray intersects triangle + t = edge2.dot(qvec); + t *= oneOverDet; + return 1; +} + +static PX_FORCE_INLINE Ps::IntBool rayTriPrecaNoCull( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, const PxVec3& pvec, + PxReal /*det*/, PxReal oneOverDet, PxReal& t) +{ + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter and test bounds + PxReal u = (tvec.dot(pvec)) * oneOverDet; + if((u < 0.0f) || u>1.0f) + return 0; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + PxReal v = (dir.dot(qvec)) * oneOverDet; + if((v < 0.0f) || u+v>1.0f) + return 0; + + // Calculate t, ray intersects triangle + t = (edge2.dot(qvec)) * oneOverDet; + return 1; +} + +// PT: specialized version where oneOverDir is available +// PT: why did we change the initial epsilon value? +#define LOCAL_EPSILON_RAY_BOX PX_EPS_F32 +//#define LOCAL_EPSILON_RAY_BOX 0.0001f +static PX_FORCE_INLINE int intersectRayAABB2(const PxVec3& minimum, const PxVec3& maximum, + const PxVec3& ro, const PxVec3& /*rd*/, const PxVec3& oneOverDir, + float& tnear, float& tfar, + bool fbx, bool fby, bool fbz) +{ + // PT: this unrolled loop is a lot faster on Xbox + + if(fbx) + if(ro.xmaximum.x) + { +// tnear = FLT_MAX; + return -1; + } + if(fby) + if(ro.ymaximum.y) + { +// tnear = FLT_MAX; + return -1; + } + if(fbz) + if(ro.zmaximum.z) + { +// tnear = FLT_MAX; + return -1; + } + + PxReal t1x = (minimum.x - ro.x) * oneOverDir.x; + PxReal t2x = (maximum.x - ro.x) * oneOverDir.x; + PxReal t1y = (minimum.y - ro.y) * oneOverDir.y; + PxReal t2y = (maximum.y - ro.y) * oneOverDir.y; + PxReal t1z = (minimum.z - ro.z) * oneOverDir.z; + PxReal t2z = (maximum.z - ro.z) * oneOverDir.z; + + int bx; + int by; + int bz; + + if(t1x>t2x) + { + PxReal t=t1x; t1x=t2x; t2x=t; + bx = 3; + } + else + { + bx = 0; + } + + if(t1y>t2y) + { + PxReal t=t1y; t1y=t2y; t2y=t; + by = 4; + } + else + { + by = 1; + } + + if(t1z>t2z) + { + PxReal t=t1z; t1z=t2z; t2z=t; + bz = 5; + } + else + { + bz = 2; + } + + int ret; + if(!fbx) + { +// if(t1x>tnear) // PT: no need to test for the first value + { + tnear = t1x; + ret = bx; + } +// tfar = Px::intrinsics::selectMin(tfar, t2x); + tfar = t2x; // PT: no need to test for the first value + } + else + { + ret=-1; + tnear = -PX_MAX_F32; + tfar = PX_MAX_F32; + } + + if(!fby) + { + if(t1y>tnear) + { + tnear = t1y; + ret = by; + } + tfar = physx::intrinsics::selectMin(tfar, t2y); + } + + if(!fbz) + { + if(t1z>tnear) + { + tnear = t1z; + ret = bz; + } + tfar = physx::intrinsics::selectMin(tfar, t2z); + } + + if(tnear>tfar || tfar no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + + const PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + const PxVec3 v2 = p4 - p3; + + const PxReal temp2 = plane.n.dot(v2); + if(temp2==0.0f) return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp2); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i])) * coeff; + if(dist<0.0f) return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + const PxReal temp3 = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + return temp3<0.0f; +} + +namespace +{ +static const PxReal gFatBoxEdgeCoeff = 0.01f; +#define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) + +static const PxVec3 EdgeNormals[] = +{ + PxVec3(0, -INVSQRT2, -INVSQRT2), // 0-1 + PxVec3(INVSQRT2, 0, -INVSQRT2), // 1-2 + PxVec3(0, INVSQRT2, -INVSQRT2), // 2-3 + PxVec3(-INVSQRT2, 0, -INVSQRT2), // 3-0 + + PxVec3(0, INVSQRT2, INVSQRT2), // 7-6 + PxVec3(INVSQRT2, 0, INVSQRT2), // 6-5 + PxVec3(0, -INVSQRT2, INVSQRT2), // 5-4 + PxVec3(-INVSQRT2, 0, INVSQRT2), // 4-7 + + PxVec3(INVSQRT2, -INVSQRT2, 0), // 1-5 + PxVec3(INVSQRT2, INVSQRT2, 0), // 6-2 + PxVec3(-INVSQRT2, INVSQRT2, 0), // 3-7 + PxVec3(-INVSQRT2, -INVSQRT2, 0) // 4-0 +}; + +static const PxVec3* getBoxLocalEdgeNormals() +{ + return EdgeNormals; +} +} + +static PX_FORCE_INLINE void closestAxis2(const PxVec3& v, PxU32& j, PxU32& k) +{ + // find largest 2D plane projection + const PxF32 absPx = physx::intrinsics::abs(v.x); + const PxF32 absPy = physx::intrinsics::abs(v.y); + const PxF32 absPz = physx::intrinsics::abs(v.z); + //PxU32 m = 0; //x biggest axis + j = 1; + k = 2; + if( absPy > absPx && absPy > absPz) + { + //y biggest + j = 2; + k = 0; + //m = 1; + } + else if(absPz > absPx) + { + //z biggest + j = 0; + k = 1; + //m = 2; + } +// return m; +} + +bool Gu::sweepBoxTriangle( const PxTriangle& tri, const PxBounds3& box, + const PxVec3& motion, const PxVec3& oneOverMotion, + PxVec3& hit, PxVec3& normal, PxReal& d, bool isDoubleSided) +{ + // Create triangle normal + PxVec3 denormalizedTriNormal; + tri.denormalizedNormal(denormalizedTriNormal); + + // Backface culling + const bool doBackfaceCulling = !isDoubleSided; + if(doBackfaceCulling && (denormalizedTriNormal.dot(motion)) >= 0.0f) // ">=" is important ! + return false; + + ///////////////////////// + + PxVec3 boxVertices[8]; + computeBoxPoints(box, boxVertices); + + ///////////////////////// + + // Make fat triangle + const PxTriangle fatTri = inflateTriangle(tri, gFatTriangleCoeff); + + PxReal minDist = d; // Initialize with current best distance + int col = -1; + + // Box vertices VS triangle + { + // ### cull using box-plane distance ? + const PxVec3 edge1 = fatTri.verts[1] - fatTri.verts[0]; + const PxVec3 edge2 = fatTri.verts[2] - fatTri.verts[0]; + const PxVec3 PVec = motion.cross(edge2); + const PxReal Det = edge1.dot(PVec); + + // We can't use stamps here since we can still find a better TOI for a given vertex, + // even if that vertex has already been tested successfully against another triangle. + const PxVec3* VN = reinterpret_cast(getBoxVertexNormals()); + + const PxReal oneOverDet = Det!=0.0f ? 1.0f / Det : 0.0f; + + PxU32 hitIndex=0; + if(doBackfaceCulling) + { + if(Det>=LOCAL_EPSILON) + { + for(PxU32 i=0;i<8;i++) + { + // Orientation culling + if((VN[i].dot(denormalizedTriNormal) >= 0.0f)) // Can't rely on triangle normal for double-sided faces + continue; + + // ### test this + // ### ok, this causes the bug in level3's v-shaped desk. Not really a real "bug", it just happens + // that this VF test fixes this case, so it's a bad idea to cull it. Oh, well. + // If we use a penetration-depth code to fixup bad cases, we can enable this culling again. (also + // if we find a better way to handle that desk) + // Discard back vertices +// if(VN[i].dot(motion)<0.0f) +// continue; + + // Shoot a ray from vertex against triangle, in direction "motion" + PxReal t; + if(!rayTriPrecaCull(boxVertices[i], motion, fatTri.verts[0], edge1, edge2, PVec, Det, oneOverDet, t)) + continue; + + //if(t<=OffsetLength) t=0.0f; + // Only consider positive distances, closer than current best + // ### we could test that first on tri vertices & discard complete tri if it's further than current best (or equal!) + if(t < 0.0f || t > minDist) + continue; + + minDist = t; + col = 0; +// hit = boxVertices[i] + t * motion; + hitIndex = i; + } + } + } + else + { + if(Det<=-LOCAL_EPSILON || Det>=LOCAL_EPSILON) + { + for(PxU32 i=0;i<8;i++) + { + // ### test this + // ### ok, this causes the bug in level3's v-shaped desk. Not really a real "bug", it just happens + // that this VF test fixes this case, so it's a bad idea to cull it. Oh, well. + // If we use a penetration-depth code to fixup bad cases, we can enable this culling again. (also + // if we find a better way to handle that desk) + // Discard back vertices + // if(!VN[i].SameDirection(motion)) + // continue; + + // Shoot a ray from vertex against triangle, in direction "motion" + PxReal t; + if(!rayTriPrecaNoCull(boxVertices[i], motion, fatTri.verts[0], edge1, edge2, PVec, Det, oneOverDet, t)) + continue; + + //if(t<=OffsetLength) t=0.0f; + // Only consider positive distances, closer than current best + // ### we could test that first on tri vertices & discard complete tri if it's further than current best (or equal!) + if(t < 0.0f || t > minDist) + continue; + + minDist = t; + col = 0; +// hit = boxVertices[i] + t * motion; + hitIndex = i; + } + } + } + + // Only copy this once, if needed + if(col==0) + { + // PT: hit point on triangle + hit = boxVertices[hitIndex] + minDist * motion; + normal = denormalizedTriNormal; + } + } + + // Triangle vertices VS box + { + const PxVec3 negMotion = -motion; + const PxVec3 negInvMotion = -oneOverMotion; + + // PT: precompute fabs-test for ray-box + // - doing this outside of the ray-box function gets rid of 3 fabs/fcmp per call + // - doing this with integer code removes the 3 remaining fabs/fcmps totally + // - doing this outside reduces the LHS + const bool b0 = physx::intrinsics::abs(negMotion.x) flips normals + normal = boxNormals[plane]; + col = 1; + + // PT: hit point on triangle + hit = tri.verts[i]; + } + } + } + + PxU32 saved_j = PX_INVALID_U32; + PxU32 saved_k = PX_INVALID_U32; + PxVec3 p1s; + PxVec3 v1s; + + // Edge-vs-edge + { + // Loop through box edges + const PxU8* PX_RESTRICT edges = getBoxEdges(); + const PxVec3* PX_RESTRICT edgeNormals = getBoxLocalEdgeNormals(); + for(PxU32 i=0;i<12;i++) // 12 edges + { + // PT: TODO: skip this if edge is culled + PxVec3 p1 = boxVertices[*edges++]; + PxVec3 p2 = boxVertices[*edges++]; + Ps::makeFatEdge(p1, p2, gFatBoxEdgeCoeff); + + if(edgeNormals[i].dot(motion) < 0.0f) + continue; + + // While we're at it, precompute some more data for EE tests + const PxVec3 v1 = p2 - p1; + + // Build plane P based on edge (p1, p2) and direction (dir) + const PxVec3 planeNormal = v1.cross(motion); + const PxPlane plane(planeNormal, -(planeNormal.dot(p1))); + + // find largest 2D plane projection + PxU32 closest_i, closest_j; + // Ps::closestAxis(plane.normal, ii, jj); + closestAxis2(planeNormal, closest_i, closest_j); + + const PxReal coeff = 1.0f / (v1[closest_i]*motion[closest_j] - v1[closest_j]*motion[closest_i]); + + // Loop through triangle edges + for(PxU32 j=0; j<3; j++) + { + // Catch current triangle edge + // j=0 => 0-1 + // j=1 => 1-2 + // j=2 => 2-0 + // => this is compatible with EdgeList + const PxU32 k = Ps::getNextIndex3(j); + + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge3(plane, p1, p2, motion, v1, tri.verts[j], tri.verts[k], dist, ip, closest_i, closest_j, coeff)) + { + if(dist<=minDist) + { + p1s = p1; + v1s = v1; + saved_j = j; + saved_k = k; + + col = 2; + minDist = dist; + + // PT: hit point on triangle + hit = ip + motion*dist; + } + } + } + } + } + + if(col==-1) + return false; + + if(col==2) + { + PX_ASSERT(saved_j != PX_INVALID_U32); + PX_ASSERT(saved_k != PX_INVALID_U32); + const PxVec3& p3 = tri.verts[saved_j]; + const PxVec3& p4 = tri.verts[saved_k]; + computeEdgeEdgeNormal(normal, p1s, v1s, p3, p4-p3, motion, minDist); + } + + d = minDist; + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h new file mode 100644 index 000000000..81ebc2878 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h @@ -0,0 +1,67 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_TRIANGLE_FEATURE_BASED_H +#define GU_SWEEP_BOX_TRIANGLE_FEATURE_BASED_H + +#include "foundation/PxVec3.h" +#include "foundation/PxPlane.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + class PxTriangle; + + namespace Gu + { + /** + Sweeps a box against a triangle, using a 'feature-based' approach. + + This is currently only used for computing the box-sweep impact data, in a second pass, + after the best triangle has been identified using faster approaches (SAT/GJK). + + \warning Returned impact normal is not normalized + + \param tri [in] the triangle + \param box [in] the box + \param motion [in] (box) motion vector + \param oneOverMotion [in] precomputed inverse of motion vector + \param hit [out] impact point + \param normal [out] impact normal (warning: not normalized) + \param d [in/out] impact distance (please initialize with best current distance) + \param isDoubleSided [in] whether triangle is double-sided or not + \return true if an impact has been found + */ + bool sweepBoxTriangle( const PxTriangle& tri, const PxBounds3& box, + const PxVec3& motion, const PxVec3& oneOverMotion, + PxVec3& hit, PxVec3& normal, PxReal& d, bool isDoubleSided=false); + } // namespace Gu +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp new file mode 100644 index 000000000..13219ea87 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp @@ -0,0 +1,39 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepBoxTriangle_SAT.h" + +using namespace physx; +using namespace Gu; + +// PT: SAT-based version, in box space +int Gu::triBoxSweepTestBoxSpace(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& toi, bool doBackfaceCulling) +{ + return triBoxSweepTestBoxSpace_inlined(tri, extents, dir, oneOverDir, tmax, toi, PxU32(doBackfaceCulling)); +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h new file mode 100644 index 000000000..3116f86e2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h @@ -0,0 +1,235 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_TRIANGLE_SAT_H +#define GU_SWEEP_BOX_TRIANGLE_SAT_H + +#include "GuSweepSharedTests.h" +#include "PxTriangle.h" + + #define RetType int + #define MTDType bool + +namespace physx +{ +namespace Gu +{ + +// We have separation if one of those conditions is true: +// -BoxExt > TriMax (box strictly to the right of the triangle) +// BoxExt < TriMin (box strictly to the left of the triangle +// <=> d0 = -BoxExt - TriMax > 0 +// d1 = BoxExt - TriMin < 0 +// Hence we have overlap if d0 <= 0 and d1 >= 0 +// overlap = (d0<=0.0f && d1>=0.0f) +#define TEST_OVERLAP \ + const float d0 = -BoxExt - TriMax; \ + const float d1 = BoxExt - TriMin; \ + const bool bIntersect = (d0<=0.0f && d1>=0.0f); \ + bValidMTD &= bIntersect; + + // PT: inlining this one is important. Returning floats looks bad but is faster on Xbox. + static PX_FORCE_INLINE RetType testAxis(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& axis, MTDType& bValidMTD, float& tfirst, float& tlast) + { + const float d0t = tri.verts[0].dot(axis); + const float d1t = tri.verts[1].dot(axis); + const float d2t = tri.verts[2].dot(axis); + + float TriMin = PxMin(d0t, d1t); + float TriMax = PxMax(d0t, d1t); + TriMin = PxMin(TriMin, d2t); + TriMax = PxMax(TriMax, d2t); + + //////// + + const float BoxExt = PxAbs(axis.x)*extents.x + PxAbs(axis.y)*extents.y + PxAbs(axis.z)*extents.z; + TEST_OVERLAP + + const float v = dir.dot(axis); + if(PxAbs(v) < 1.0E-6f) + return bIntersect; + const float oneOverV = -1.0f / v; + + // float t0 = d0 * oneOverV; + // float t1 = d1 * oneOverV; + // if(t0 > t1) TSwap(t0, t1); + const float t0_ = d0 * oneOverV; + const float t1_ = d1 * oneOverV; + float t0 = PxMin(t0_, t1_); + float t1 = PxMax(t0_, t1_); + + if(t0 > tlast) return false; + if(t1 < tfirst) return false; + + // if(t1 < tlast) tlast = t1; + tlast = PxMin(t1, tlast); + + // if(t0 > tfirst) tfirst = t0; + tfirst = PxMax(t0, tfirst); + + return true; + } + + template + static PX_FORCE_INLINE RetType testAxisXYZ(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, float oneOverDir, MTDType& bValidMTD, float& tfirst, float& tlast) + { + const float d0t = tri.verts[0][XYZ]; + const float d1t = tri.verts[1][XYZ]; + const float d2t = tri.verts[2][XYZ]; + + float TriMin = PxMin(d0t, d1t); + float TriMax = PxMax(d0t, d1t); + TriMin = PxMin(TriMin, d2t); + TriMax = PxMax(TriMax, d2t); + + //////// + + const float BoxExt = extents[XYZ]; + TEST_OVERLAP + + const float v = dir[XYZ]; + if(PxAbs(v) < 1.0E-6f) + return bIntersect; + + const float oneOverV = -oneOverDir; + + // float t0 = d0 * oneOverV; + // float t1 = d1 * oneOverV; + // if(t0 > t1) TSwap(t0, t1); + const float t0_ = d0 * oneOverV; + const float t1_ = d1 * oneOverV; + float t0 = PxMin(t0_, t1_); + float t1 = PxMax(t0_, t1_); + + if(t0 > tlast) return false; + if(t1 < tfirst) return false; + + // if(t1 < tlast) tlast = t1; + tlast = PxMin(t1, tlast); + + // if(t0 > tfirst) tfirst = t0; + tfirst = PxMax(t0, tfirst); + + return true; + } + + PX_FORCE_INLINE int testSeparationAxes( const PxTriangle& tri, const PxVec3& extents, + const PxVec3& normal, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& tcoll) + { + bool bValidMTD = true; + float tfirst = -FLT_MAX; + float tlast = FLT_MAX; + + // Triangle normal + if(!testAxis(tri, extents, dir, normal, bValidMTD, tfirst, tlast)) + return 0; + + // Box normals + if(!testAxisXYZ<0>(tri, extents, dir, oneOverDir.x, bValidMTD, tfirst, tlast)) + return 0; + if(!testAxisXYZ<1>(tri, extents, dir, oneOverDir.y, bValidMTD, tfirst, tlast)) + return 0; + if(!testAxisXYZ<2>(tri, extents, dir, oneOverDir.z, bValidMTD, tfirst, tlast)) + return 0; + + // Edges + for(PxU32 i=0; i<3; i++) + { + int ip1 = int(i+1); + if(i>=2) ip1 = 0; + const PxVec3 TriEdge = tri.verts[ip1] - tri.verts[i]; + + { + const PxVec3 Sep = Ps::cross100(TriEdge); + if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)) + return 0; + } + { + const PxVec3 Sep = Ps::cross010(TriEdge); + if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)) + return 0; + } + { + const PxVec3 Sep = Ps::cross001(TriEdge); + if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)) + return 0; + } + } + + if(tfirst > tmax || tlast < 0.0f) + return 0; + + if(tfirst <= 0.0f) + { + if(!bValidMTD) + return 0; + tcoll = 0.0f; + } + else tcoll = tfirst; + + return 1; + } + + //! Inlined version of triBoxSweepTestBoxSpace. See that other function for comments. + PX_FORCE_INLINE int triBoxSweepTestBoxSpace_inlined(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& toi, PxU32 doBackfaceCulling) + { + // Create triangle normal + PxVec3 triNormal; + tri.denormalizedNormal(triNormal); + + // Backface culling + if(doBackfaceCulling && (triNormal.dot(dir)) >= 0.0f) // ">=" is important ! + return 0; + + // The SAT test will properly detect initial overlaps, no need for extra tests + return testSeparationAxes(tri, extents, triNormal, dir, oneOverDir, tmax, toi); + } + + /** + Sweeps a box against a triangle, using a 'SAT' approach (Separating Axis Theorem). + + The test is performed in box-space, i.e. the box is axis-aligned and its center is (0,0,0). In other words it is + defined by its extents alone. The triangle must have been transformed to this "box-space" before calling the function. + + \param tri [in] triangle in box-space + \param extents [in] box extents + \param dir [in] sweep direction. Does not need to be normalized. + \param oneOverDir [in] precomputed inverse of sweep direction + \param tmax [in] sweep length + \param toi [out] time of impact/impact distance. Does not need to be initialized before calling the function. + \param doBackfaceCulling [in] true to enable backface culling, false for double-sided triangles + \return non-zero value if an impact has been found (in which case returned 'toi' value is valid) + */ + int triBoxSweepTestBoxSpace(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& toi, bool doBackfaceCulling); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp new file mode 100644 index 000000000..36dafac7c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxBounds3.h" +#include "foundation/PxTransform.h" +#include "GuSweepCapsuleBox.h" +#include "GuSweepSphereTriangle.h" +#include "GuCapsule.h" +#include "PxTriangle.h" +#include "GuDistanceSegmentBox.h" +#include "GuInternal.h" +#include "PsAlloca.h" +#include "GuSIMDHelpers.h" + +using namespace physx; +using namespace Gu; + +namespace +{ +/** +* Returns triangles. +* \return 36 indices (12 triangles) indexing the list returned by ComputePoints() +*/ +static const PxU8* getBoxTriangles() +{ + static PxU8 Indices[] = { + 0,2,1, 0,3,2, + 1,6,5, 1,2,6, + 5,7,4, 5,6,7, + 4,3,0, 4,7,3, + 3,6,2, 3,7,6, + 5,0,1, 5,4,0 + }; + return Indices; +} +} + +#define OUTPUT_TRI(t, p0, p1, p2){ \ +t->verts[0] = p0; \ +t->verts[1] = p1; \ +t->verts[2] = p2; \ +t++;} + +#define OUTPUT_TRI2(t, p0, p1, p2, d){ \ +t->verts[0] = p0; \ +t->verts[1] = p1; \ +t->verts[2] = p2; \ +t->denormalizedNormal(denormalizedNormal); \ +if((denormalizedNormal.dot(d))>0.0f) { \ +PxVec3 Tmp = t->verts[1]; \ +t->verts[1] = t->verts[2]; \ +t->verts[2] = Tmp; \ +} \ +t++; *ids++ = i; } + +static PxU32 extrudeMesh( PxU32 nbTris, const PxTriangle* triangles, + const PxVec3& extrusionDir, PxTriangle* tris, PxU32* ids, const PxVec3& dir) +{ + const PxU32* base = ids; + + for(PxU32 i=0; i 0.0f; + if(culled) continue; + + PxVec3 p0 = currentTriangle.verts[0]; + PxVec3 p1 = currentTriangle.verts[1]; + PxVec3 p2 = currentTriangle.verts[2]; + + PxVec3 p0b = p0 + extrusionDir; + PxVec3 p1b = p1 + extrusionDir; + PxVec3 p2b = p2 + extrusionDir; + + p0 -= extrusionDir; + p1 -= extrusionDir; + p2 -= extrusionDir; + + if(denormalizedNormal.dot(extrusionDir) >= 0.0f) OUTPUT_TRI(tris, p0b, p1b, p2b) + else OUTPUT_TRI(tris, p0, p1, p2) + *ids++ = i; + + // ### it's probably useless to extrude all the shared edges !!!!! + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE12) + { + OUTPUT_TRI2(tris, p1, p1b, p2b, dir) + OUTPUT_TRI2(tris, p1, p2b, p2, dir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE20) + { + OUTPUT_TRI2(tris, p0, p2, p2b, dir) + OUTPUT_TRI2(tris, p0, p2b, p0b, dir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE01) + { + OUTPUT_TRI2(tris, p0b, p1b, p1, dir) + OUTPUT_TRI2(tris, p0b, p1, p0, dir) + } + } + return PxU32(ids-base); +} + +static PxU32 extrudeBox(const PxBounds3& localBox, const PxTransform* world, const PxVec3& extrusionDir, PxTriangle* tris, const PxVec3& dir) +{ + // Handle the box as a mesh + + PxTriangle boxTris[12]; + + PxVec3 p[8]; + computeBoxPoints(localBox, p); + + const PxU8* PX_RESTRICT indices = getBoxTriangles(); + + for(PxU32 i=0; i<12; i++) + { + const PxU8 VRef0 = indices[i*3+0]; + const PxU8 VRef1 = indices[i*3+1]; + const PxU8 VRef2 = indices[i*3+2]; + + PxVec3 p0 = p[VRef0]; + PxVec3 p1 = p[VRef1]; + PxVec3 p2 = p[VRef2]; + if(world) + { + p0 = world->transform(p0); + p1 = world->transform(p1); + p2 = world->transform(p2); + } + + boxTris[i].verts[0] = p0; + boxTris[i].verts[1] = p1; + boxTris[i].verts[2] = p2; + } + PxU32 fakeIDs[12*7]; + return extrudeMesh(12, boxTris, extrusionDir, tris, fakeIDs, dir); +} + +// +// The problem of testing a swept capsule against a box is transformed into sweeping a sphere (lying at the center +// of the capsule) against the extruded triangles of the box. The box triangles are extruded along the +// capsule segment axis. +// +bool Gu::sweepCapsuleBox(const Capsule& capsule, const PxTransform& boxWorldPose, const PxVec3& boxDim, const PxVec3& dir, PxReal length, PxVec3& hit, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags) +{ + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(distanceSegmentBoxSquared(capsule.p0, capsule.p1, boxWorldPose.p, boxDim, PxMat33Padded(boxWorldPose.q)) < capsule.radius*capsule.radius) + { + min_dist = 0.0f; + normal = -dir; + return true; + } + } + + // Extrusion dir = capsule segment + const PxVec3 extrusionDir = (capsule.p1 - capsule.p0)*0.5f; + + // Extrude box + PxReal MinDist = length; + bool Status = false; + { + const PxBounds3 aabb(-boxDim, boxDim); + + PX_ALLOCA(triangles, PxTriangle, 12*7); + const PxU32 nbTris = extrudeBox(aabb, &boxWorldPose, extrusionDir, triangles, dir); + PX_ASSERT(nbTris<=12*7); + + // Sweep sphere vs extruded box + PxSweepHit h; // PT: TODO: ctor! + PxVec3 bestNormal; + if(sweepSphereTriangles(nbTris, triangles, capsule.computeCenter(), capsule.radius, dir, length, NULL, h, bestNormal, false, false, false, false)) + { + hit = h.position; + MinDist = h.distance; + normal = h.normal; + Status = true; + } + } + + min_dist = MinDist; + return Status; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h new file mode 100644 index 000000000..55b094c02 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h @@ -0,0 +1,49 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_CAPSULE_BOX_H +#define GU_SWEEP_CAPSULE_BOX_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Capsule; + + bool sweepCapsuleBox(const Capsule& capsule, const PxTransform& boxWorldPose, const PxVec3& boxDim, const PxVec3& dir, PxReal length, PxVec3& hit, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp new file mode 100644 index 000000000..5b05d0d75 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp @@ -0,0 +1,344 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepCapsuleCapsule.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentSegment.h" +#include "GuIntersectionRayCapsule.h" +#include "PxQueryReport.h" +#include "PxTriangle.h" + +using namespace physx; +using namespace Gu; + +#define LOCAL_EPSILON 0.00001f // PT: this value makes the 'basicAngleTest' pass. Fails because of a ray almost parallel to a triangle + +static void edgeEdgeDist(PxVec3& x, PxVec3& y, // closest points + const PxVec3& p, const PxVec3& a, // seg 1 origin, vector + const PxVec3& q, const PxVec3& b) // seg 2 origin, vector +{ + const PxVec3 T = q - p; + const PxReal ADotA = a.dot(a); + const PxReal BDotB = b.dot(b); + const PxReal ADotB = a.dot(b); + const PxReal ADotT = a.dot(T); + const PxReal BDotT = b.dot(T); + + // t parameterizes ray (p, a) + // u parameterizes ray (q, b) + + // Compute t for the closest point on ray (p, a) to ray (q, b) + const PxReal Denom = ADotA*BDotB - ADotB*ADotB; + + PxReal t; + if(Denom!=0.0f) + { + t = (ADotT*BDotB - BDotT*ADotB) / Denom; + + // Clamp result so t is on the segment (p, a) + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + + // find u for point on ray (q, b) closest to point at t + PxReal u; + if(BDotB!=0.0f) + { + u = (t*ADotB - BDotT) / BDotB; + + // if u is on segment (q, b), t and u correspond to closest points, otherwise, clamp u, recompute and clamp t + if(u<0.0f) + { + u = 0.0f; + if(ADotA!=0.0f) + { + t = ADotT / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + } + else if(u > 1.0f) + { + u = 1.0f; + if(ADotA!=0.0f) + { + t = (ADotB + ADotT) / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + } + } + else + { + u = 0.0f; + + if(ADotA!=0.0f) + { + t = ADotT / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + } + + x = p + a * t; + y = q + b * u; +} + +static bool rayQuad(const PxVec3& orig, const PxVec3& dir, const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, PxReal& t, PxReal& u, PxReal& v, bool cull) +{ + // Find vectors for two edges sharing vert0 + const PxVec3 edge1 = vert1 - vert0; + const PxVec3 edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const PxReal det = edge1.dot(pvec); + + if(cull) + { + if(detdet) + return false; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + v = dir.dot(qvec); + if(v<0.0f || v>det) + return false; + + // Calculate t, scale parameters, ray intersects triangle + t = edge2.dot(qvec); + const PxReal oneOverDet = 1.0f / det; + t *= oneOverDet; + u *= oneOverDet; + v *= oneOverDet; + } + else + { + // the non-culling branch + if(det>-LOCAL_EPSILON && det1.0f) + return false; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + v = (dir.dot(qvec)) * oneOverDet; + if(v<0.0f || v>1.0f) + return false; + + // Calculate t, ray intersects triangle + t = (edge2.dot(qvec)) * oneOverDet; + } + return true; +} + +bool Gu::sweepCapsuleCapsule(const Capsule& capsule0, const Capsule& capsule1, const PxVec3& dir, PxReal length, PxReal& min_dist, PxVec3& ip, PxVec3& normal, PxU32 inHitFlags, PxU16& outHitFlags) +{ + const PxReal radiusSum = capsule0.radius + capsule1.radius; + + if(!(inHitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + + // PT: It would be better not to use the same code path for spheres and capsules. The segment-segment distance + // function doesn't work for degenerate capsules so we need to test all combinations here anyway. + bool initialOverlapStatus; + if(capsule0.p0==capsule0.p1) + initialOverlapStatus = distancePointSegmentSquared(capsule1, capsule0.p0)= 0) // Same direction + { + Normal *= radiusSum; + pc = p0 - Normal; + pa = p1 - Normal; + pb = p1b - Normal; + } + else + { + Normal *= radiusSum; + pb = p0 + Normal; + pa = p1 + Normal; + pc = p1b + Normal; + } + PxReal t, u, v; + const PxVec3 center = capsule1.computeCenter(); + if(rayQuad(center, dir, pa, pb, pc, t, u, v, true) && t>=0.0f && t=0.0f && w<= MinDist) + { + MinDist = w; + Status = true; + } + } + } + } + + if(Status) + { + outHitFlags = PxHitFlags(0); + if(inHitFlags & PxU32(PxHitFlag::ePOSITION|PxHitFlag::eNORMAL)) + { + const PxVec3 p00 = capsule0.p0 - MinDist * dir; + const PxVec3 p01 = capsule0.p1 - MinDist * dir; +// const PxVec3 p10 = capsule1.p0;// - MinDist * dir; +// const PxVec3 p11 = capsule1.p1;// - MinDist * dir; + + const PxVec3 edge0 = p01 - p00; + const PxVec3 edge1 = capsuleExtent1; + + PxVec3 x, y; + edgeEdgeDist(x, y, p00, edge0, capsule1.p0, edge1); + + if(inHitFlags & PxHitFlag::eNORMAL) + { + normal = (x - y); + const float epsilon = 0.001f; + if(normal.normalize()0.0f) { \ + PxVec3 tmp = tri.verts[1]; \ + tri.verts[1] = tri.verts[2]; \ + tri.verts[2] = tmp; \ + nrm = -nrm; \ + } \ + extrudedTrisNormals[nbExtrudedTris] = nrm; \ + nbExtrudedTris++; } + + +bool Gu::sweepCapsuleTriangles_Precise( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const Capsule& capsule, // Capsule data + const PxVec3& unitDir, const PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& hit, PxVec3& triNormalOut, // Results + PxHitFlags hitFlags, bool isDoubleSided, // Query modifiers + const BoxPadded* cullBox) // Cull data +{ + if(!nbTris) + return false; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + const bool testInitialOverlap = !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP); + + // PT: we can fallback to sphere sweep: + // - if the capsule is degenerate (i.e. it's a sphere) + // - if the sweep direction is the same as the capsule axis, in which case we can just sweep the top or bottom sphere + + const PxVec3 extrusionDir = (capsule.p0 - capsule.p1)*0.5f; // Extrusion dir = capsule segment + const PxReal halfHeight = extrusionDir.magnitude(); + bool mustExtrude = halfHeight!=0.0f; + if(!mustExtrude) + { + // PT: capsule is a sphere. Switch to sphere path (intersectCapsuleTriangle doesn't work for degenerate capsules) + return sweepSphereTriangles(nbTris, triangles, capsule.p0, capsule.radius, unitDir, distance, cachedIndex, hit, triNormalOut, isDoubleSided, meshBothSides, anyHit, testInitialOverlap); + } + else + { + const PxVec3 capsuleAxis = extrusionDir/halfHeight; + const PxReal colinearity = PxAbs(capsuleAxis.dot(unitDir)); + mustExtrude = (colinearity < (1.0f - COLINEARITY_EPSILON)); + } + + const PxVec3 capsuleCenter = capsule.computeCenter(); + + if(!mustExtrude) + { + CapsuleTriangleOverlapData params; + params.init(capsule); + // PT: unfortunately we need to do IO test with the *capsule*, even though we're in the sphere codepath. So we + // can't directly reuse the sphere function. + const PxVec3 sphereCenter = capsuleCenter + unitDir * halfHeight; + // PT: this is a copy of 'sweepSphereTriangles' but with a capsule IO test. Saves double backface culling.... + { + PxU32 index = PX_INVALID_U32; + const PxU32 initIndex = getInitIndex(cachedIndex, nbTris); + + PxReal curT = distance; + const PxReal dpc0 = sphereCenter.dot(unitDir); + + PxReal bestAlignmentValue = 2.0f; + + PxVec3 bestTriNormal(0.0f); + + for(PxU32 ii=0; ii 0.0f)) + continue; + + if(testInitialOverlap && intersectCapsuleTriangle(triNormal, currentTri.verts[0], currentTri.verts[1], currentTri.verts[2], capsule, params)) + { + triNormalOut = -unitDir; + return setInitialOverlapResults(hit, unitDir, i); + } + + const PxReal magnitude = triNormal.magnitude(); + if(magnitude==0.0f) + continue; + + triNormal /= magnitude; + + PxReal currentDistance; + bool unused; + if(!sweepSphereVSTri(currentTri.verts, triNormal, sphereCenter, capsule.radius, unitDir, currentDistance, unused, false)) + continue; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal hitDot = computeAlignmentValue(triNormal, unitDir); + if(!keepTriangle(currentDistance, hitDot, curT, bestAlignmentValue, distance, distEpsilon)) + continue; + + curT = currentDistance; + index = i; + bestAlignmentValue = hitDot; + bestTriNormal = triNormal; + if(anyHit) + break; + } + return computeSphereTriangleImpactData(hit, triNormalOut, index, curT, sphereCenter, unitDir, bestTriNormal, triangles, isDoubleSided, meshBothSides); + } + } + + // PT: extrude mesh on the fly. This is a modified copy of sweepSphereTriangles, unfortunately + PxTriangle extrudedTris[7]; + PxVec3 extrudedTrisNormals[7]; // Not normalized + + hit.faceIndex = PX_INVALID_U32; + const PxU32 initIndex = getInitIndex(cachedIndex, nbTris); + + const PxReal radius = capsule.radius; + PxReal curT = distance; + const PxReal dpc0 = capsuleCenter.dot(unitDir); + + // PT: we will copy the best triangle here. Using indices alone doesn't work + // since we extrude on-the-fly (and we don't want to re-extrude later) + PxTriangle bestTri; + PxVec3 bestTriNormal(0.0f); + PxReal mostOpposingHitDot = 2.0f; + + CapsuleTriangleOverlapData params; + params.init(capsule); + + for(PxU32 ii=0; ii 0.0f)) + continue; + + if(cullBox) + { + if(!intersectTriangleBox(*cullBox, currentSrcTri.verts[0], currentSrcTri.verts[1], currentSrcTri.verts[2])) + continue; + } + + if(testInitialOverlap && intersectCapsuleTriangle(denormalizedNormal, currentSrcTri.verts[0], currentSrcTri.verts[1], currentSrcTri.verts[2], capsule, params)) + { + triNormalOut = -unitDir; + return setInitialOverlapResults(hit, unitDir, i); + } + + // Extrude mesh on the fly + PxU32 nbExtrudedTris=0; + + const PxVec3 p0 = currentSrcTri.verts[0] - extrusionDir; + const PxVec3 p1 = currentSrcTri.verts[1] - extrusionDir; + const PxVec3 p2 = currentSrcTri.verts[2] - extrusionDir; + + const PxVec3 p0b = currentSrcTri.verts[0] + extrusionDir; + const PxVec3 p1b = currentSrcTri.verts[1] + extrusionDir; + const PxVec3 p2b = currentSrcTri.verts[2] + extrusionDir; + + if(denormalizedNormal.dot(extrusionDir) >= 0.0f) OUTPUT_TRI(p0b, p1b, p2b) + else OUTPUT_TRI(p0, p1, p2) + + // ### it's probably useless to extrude all the shared edges !!!!! + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE12) + { + OUTPUT_TRI2(p1, p1b, p2b, unitDir) + OUTPUT_TRI2(p1, p2b, p2, unitDir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE20) + { + OUTPUT_TRI2(p0, p2, p2b, unitDir) + OUTPUT_TRI2(p0, p2b, p0b, unitDir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE01) + { + OUTPUT_TRI2(p0b, p1b, p1, unitDir) + OUTPUT_TRI2(p0b, p1, p0, unitDir) + } +///////////// + + // PT: TODO: this one is new, to fix the tweak issue. However this wasn't + // here before so the perf hit should be analyzed. + denormalizedNormal.normalize(); + const PxReal hitDot1 = computeAlignmentValue(denormalizedNormal, unitDir); + + for(PxU32 j=0;j 0.0f) + continue; + + // PT: beware, culling is only ok on the sphere I think + if(rejectTriangle(capsuleCenter, unitDir, curT, radius, currentTri.verts, dpc0)) + continue; + + const PxReal magnitude = triNormal.magnitude(); + if(magnitude==0.0f) + continue; + + triNormal /= magnitude; + + PxReal currentDistance; + bool unused; + if(!sweepSphereVSTri(currentTri.verts, triNormal, capsuleCenter, radius, unitDir, currentDistance, unused, false)) + continue; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + if(!keepTriangle(currentDistance, hitDot1, curT, mostOpposingHitDot, distance, distEpsilon)) + continue; + + curT = currentDistance; + hit.faceIndex = i; + mostOpposingHitDot = hitDot1; // arbitrary bias. works for hitDot1=-1, prevHitDot=0 + bestTri = currentTri; + bestTriNormal = denormalizedNormal; + if(anyHit) + goto Exit; // PT: using goto to have one test per hit, not test per triangle ('break' doesn't work here) + } + } +Exit: + if(hit.faceIndex==PX_INVALID_U32) + return false; // We didn't touch any triangle + + hit.distance = curT; + + triNormalOut = bestTriNormal; + + // Compute impact data only once, using best triangle + computeSphereTriImpactData(hit.position, hit.normal, capsuleCenter, unitDir, hit.distance, bestTri); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(hit.normal, meshBothSides, isDoubleSided, bestTriNormal, unitDir)) + hit.normal = -hit.normal; + + // PT: revisit this + if(hit.faceIndex!=PX_INVALID_U32) + { + // PT: we need to recompute a hit here because the hit between the *capsule* and the source mesh can be very + // different from the hit between the *sphere* and the extruded mesh. + + // Touched tri + const PxVec3& p0 = triangles[hit.faceIndex].verts[0]; + const PxVec3& p1 = triangles[hit.faceIndex].verts[1]; + const PxVec3& p2 = triangles[hit.faceIndex].verts[2]; + + // AP: measured to be a bit faster than the scalar version + const PxVec3 delta = unitDir*hit.distance; + Vec3V pointOnSeg, pointOnTri; + distanceSegmentTriangleSquared( + V3LoadU(capsule.p0 + delta), V3LoadU(capsule.p1 + delta), + V3LoadU(p0), V3LoadU(p1), V3LoadU(p2), + pointOnSeg, pointOnTri); + V3StoreU(pointOnTri, hit.position); + + hit.flags = PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + } + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h new file mode 100644 index 000000000..fa5dfdec0 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h @@ -0,0 +1,75 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_CAPSULE_TRIANGLE_H +#define GU_SWEEP_CAPSULE_TRIANGLE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ + class PxTriangle; + +namespace Gu +{ + class BoxPadded; + class Capsule; + + /** + Sweeps a capsule against a set of triangles. + + \param nbTris [in] number of triangles in input array + \param triangles [in] array of input triangles + \param capsule [in] the capsule + \param unitDir [in] sweep's unit direcion + \param distance [in] sweep's length + \param cachedIndex [in] cached triangle index, or NULL. Cached triangle will be tested first. + \param hit [out] results + \param triNormalOut [out] triangle normal + \param hitFlags [in] query modifiers + \param isDoubleSided [in] true if input triangles are double-sided + \param cullBox [in] additional/optional culling box. Triangles not intersecting the box are quickly discarded. + \warning if using a cullbox, make sure all triangles can be safely V4Loaded (i.e. allocate 4 more bytes after last triangle) + \return true if an impact has been found + */ + bool sweepCapsuleTriangles_Precise( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const Capsule& capsule, // Capsule data + const PxVec3& unitDir, const PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& hit, PxVec3& triNormalOut, // Results + PxHitFlags hitFlags, bool isDoubleSided, // Query modifiers + const BoxPadded* cullBox=NULL); // Cull data + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp new file mode 100644 index 000000000..740c8d7b2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepSphereCapsule.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuSweepSphereSphere.h" +#include "GuIntersectionRayCapsule.h" + +using namespace physx; +using namespace Gu; + +bool Gu::sweepSphereCapsule(const Sphere& sphere, const Capsule& lss, const PxVec3& dir, PxReal length, PxReal& d, PxVec3& ip, PxVec3& nrm, PxHitFlags hitFlags) +{ + const PxReal radiusSum = lss.radius + sphere.radius; + + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(distancePointSegmentSquared(lss.p0, lss.p1, sphere.center)=0.0f && t<=length) + { + d = t; + +// PT: TODO: +// const Ps::IntBool needsImpactPoint = hitFlags & PxHitFlag::ePOSITION; +// if(needsImpactPoint || hitFlags & PxHitFlag::eNORMAL) + { + // Move capsule against sphere + const PxVec3 tdir = t*dir; + Inflated.p0 -= tdir; + Inflated.p1 -= tdir; + + // Compute closest point between moved capsule & sphere + distancePointSegmentSquared(Inflated, sphere.center, &t); + Inflated.computePoint(ip, t); + + // Normal + nrm = (ip - sphere.center); + nrm.normalize(); + +// if(needsImpactPoint) // PT: TODO + ip -= nrm * lss.radius; + } + return true; + } + } + return false; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h new file mode 100644 index 000000000..98e7e5691 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h @@ -0,0 +1,50 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SPHERE_CAPSULE_H +#define GU_SWEEP_SPHERE_CAPSULE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Sphere; + class Capsule; + + bool sweepSphereCapsule(const Sphere& sphere, const Capsule& lss, const PxVec3& dir, PxReal length, PxReal& d, PxVec3& ip, PxVec3& nrm, PxHitFlags hitFlags); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp new file mode 100644 index 000000000..a9378d64a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp @@ -0,0 +1,116 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepSphereSphere.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +// Adapted from Gamasutra (Gomez article) +// Return true if r1 and r2 are real +static PX_FORCE_INLINE bool quadraticFormula(const PxReal a, const PxReal b, const PxReal c, PxReal& r1, PxReal& r2) +{ + const PxReal q = b*b - 4*a*c; + if(q>=0.0f) + { + PX_ASSERT(a!=0.0f); + const PxReal sq = PxSqrt(q); + const PxReal d = 1.0f / (2.0f*a); + r1 = (-b + sq) * d; + r2 = (-b - sq) * d; + return true;//real roots + } + else + { + return false;//complex roots + } +} + +static bool sphereSphereSweep( const PxReal ra, //radius of sphere A + const PxVec3& A0, //previous position of sphere A + const PxVec3& A1, //current position of sphere A + const PxReal rb, //radius of sphere B + const PxVec3& B0, //previous position of sphere B + const PxVec3& B1, //current position of sphere B + PxReal& u0, //normalized time of first collision + PxReal& u1 //normalized time of second collision + ) +{ + const PxVec3 va = A1 - A0; + const PxVec3 vb = B1 - B0; + const PxVec3 AB = B0 - A0; + const PxVec3 vab = vb - va; // relative velocity (in normalized time) + const PxReal rab = ra + rb; + + const PxReal a = vab.dot(vab); //u*u coefficient + const PxReal b = 2.0f*(vab.dot(AB)); //u coefficient + + const PxReal c = (AB.dot(AB)) - rab*rab; //constant term + + //check if they're currently overlapping + if(c<=0.0f || a==0.0f) + { + u0 = 0.0f; + u1 = 0.0f; + return true; + } + + //check if they hit each other during the frame + if(quadraticFormula(a, b, c, u0, u1)) + { + if(u0>u1) + Ps::swap(u0, u1); + + // u01.0f) return false; + if(u1<0.0f || u0>1.0f) return false; + + return true; + } + return false; +} + +bool Gu::sweepSphereSphere(const PxVec3& center0, PxReal radius0, const PxVec3& center1, PxReal radius1, const PxVec3& motion, PxReal& d, PxVec3& nrm) +{ + const PxVec3 movedCenter = center1 + motion; + + PxReal tmp; + if(!sphereSphereSweep(radius0, center0, center0, radius1, center1, movedCenter, d, tmp)) + return false; + + // Compute normal + // PT: if spheres initially overlap, the convention is that returned normal = -sweep direction + if(d==0.0f) + nrm = -motion; + else + nrm = (center1 + d * motion) - center0; + nrm.normalize(); + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h new file mode 100644 index 000000000..1c80b0273 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SPHERE_SPHERE_H +#define GU_SWEEP_SPHERE_SPHERE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + bool sweepSphereSphere(const PxVec3& center0, PxReal radius0, const PxVec3& center1, PxReal radius1, const PxVec3& motion, PxReal& d, PxVec3& nrm); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp new file mode 100644 index 000000000..30728ac5a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp @@ -0,0 +1,524 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepSphereTriangle.h" +#include "GuIntersectionRaySphere.h" +#include "GuIntersectionRayCapsule.h" +#include "GuIntersectionRayTriangle.h" +#include "GuCapsule.h" +#include "GuInternal.h" +#include "PsUtilities.h" +#include "GuDistancePointTriangle.h" + +using namespace physx; +using namespace Gu; + +// PT: using GU_CULLING_EPSILON_RAY_TRIANGLE fails here, in capsule-vs-mesh's triangle extrusion, when +// the sweep dir is almost the same as the capsule's dir (i.e. when we usually fallback to the sphere codepath). +// I suspect det becomes so small that we lose all accuracy when dividing by det and using the result in computing +// impact distance. +#define LOCAL_EPSILON 0.00001f + +// PT: special version computing (u,v) even when the ray misses the tri. Version working on precomputed edges. +static PX_FORCE_INLINE PxU32 rayTriSpecial(const PxVec3& orig, const PxVec3& dir, const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, PxReal& t, PxReal& u, PxReal& v) +{ + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const PxReal det = edge1.dot(pvec); + + // the non-culling branch +// if(det>-GU_CULLING_EPSILON_RAY_TRIANGLE && det-LOCAL_EPSILON && det1.0f) + return 1; + if(v<0.0f || u+v>1.0f) + return 1; + + // Calculate t, ray intersects triangle + t = (edge2.dot(qvec)) * oneOverDet; + + return 2; +} + +// Returns true if sphere can be tested against triangle vertex, false if edge test should be performed +// +// Uses a conservative approach to work for "sliver triangles" (long & thin) as well. +static PX_FORCE_INLINE bool edgeOrVertexTest(const PxVec3& planeIntersectPoint, const PxVec3* PX_RESTRICT tri, PxU32 vertIntersectCandidate, PxU32 vert0, PxU32 vert1, PxU32& secondEdgeVert) +{ + { + const PxVec3 edge0 = tri[vertIntersectCandidate] - tri[vert0]; + const PxReal edge0LengthSqr = edge0.dot(edge0); + + const PxVec3 diff = planeIntersectPoint - tri[vert0]; + + if (edge0.dot(diff) < edge0LengthSqr) // If the squared edge length is used for comparison, the edge vector does not need to be normalized + { + secondEdgeVert = vert0; + return false; + } + } + + { + const PxVec3 edge1 = tri[vertIntersectCandidate] - tri[vert1]; + const PxReal edge1LengthSqr = edge1.dot(edge1); + + const PxVec3 diff = planeIntersectPoint - tri[vert1]; + + if (edge1.dot(diff) < edge1LengthSqr) + { + secondEdgeVert = vert1; + return false; + } + } + return true; +} + +static PX_FORCE_INLINE bool testRayVsSphereOrCapsule(PxReal& impactDistance, bool testSphere, const PxVec3& center, PxReal radius, const PxVec3& dir, const PxVec3* PX_RESTRICT verts, PxU32 e0, PxU32 e1) +{ + if(testSphere) + { + PxReal t; + if(intersectRaySphere(center, dir, PX_MAX_F32, verts[e0], radius, t)) + { + impactDistance = t; + return true; + } + } + else + { + PxReal t; + if(intersectRayCapsule(center, dir, verts[e0], verts[e1], radius, t)) + { + if(t>=0.0f/* && t= 0.0f) + R = -R; + + // The first point of the sphere to hit the triangle plane is the point of the sphere nearest to + // the triangle plane. Hence, we use center - (normal*radius) below. + + // PT: casting against the extruded triangle in direction R is the same as casting from a ray moved by -R + PxReal t; + const PxU32 r = rayTriSpecial(center-R, dir, triVerts[0], edge10, edge20, t, u, v); + if(!r) + return false; + if(r==2) + { + if(t<0.0f) + return false; + impactDistance = t; + directHit = true; + return true; + } + } + + // + // Let's do some art! + // + // The triangle gets divided into the following areas (based on the barycentric coordinates (u,v)): + // + // \ A0 / + // \ / + // \ / + // \/ 0 + // A02 * A01 + // u / / \ \ v + // * / \ * + // / \ . + // 2 / \ 1 + // ------*--------------*------- + // / \ . + // A2 / A12 \ A1 + // + // + // Based on the area where the computed triangle plane intersection point lies in, a different sweep test will be applied. + // + // A) A01, A02, A12 : Test sphere against the corresponding edge + // B) A0, A1, A2 : Test sphere against the corresponding vertex + // + // Unfortunately, B) does not work for long, thin triangles. Hence there is some extra code which does a conservative check and + // switches to edge tests if necessary. + // + + bool TestSphere; + PxU32 e0,e1; + if(u<0.0f) + { + if(v<0.0f) + { + // 0 or 0-1 or 0-2 + e0 = 0; + const PxVec3 intersectPoint = INTERSECT_POINT; + TestSphere = edgeOrVertexTest(intersectPoint, triVerts, 0, 1, 2, e1); + } + else if(u+v>1.0f) + { + // 2 or 2-0 or 2-1 + e0 = 2; + const PxVec3 intersectPoint = INTERSECT_POINT; + TestSphere = edgeOrVertexTest(intersectPoint, triVerts, 2, 0, 1, e1); + } + else + { + // 0-2 + TestSphere = false; + e0 = 0; + e1 = 2; + } + } + else + { + if(v<0.0f) + { + if(u+v>1.0f) + { + // 1 or 1-0 or 1-2 + e0 = 1; + const PxVec3 intersectPoint = INTERSECT_POINT; + TestSphere = edgeOrVertexTest(intersectPoint, triVerts, 1, 0, 2, e1); + } + else + { + // 0-1 + TestSphere = false; + e0 = 0; + e1 = 1; + } + } + else + { + PX_ASSERT(u+v>=1.0f); // Else hit triangle + // 1-2 + TestSphere = false; + e0 = 1; + e1 = 2; + } + } + return testRayVsSphereOrCapsule(impactDistance, TestSphere, center, radius, dir, triVerts, e0, e1); +} + +bool Gu::sweepSphereTriangles( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const PxVec3& center, const PxReal radius, // Sphere data + const PxVec3& unitDir, PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& h, PxVec3& triNormalOut, // Results + bool isDoubleSided, bool meshBothSides, bool anyHit, bool testInitialOverlap) // Query modifiers +{ + if(!nbTris) + return false; + + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + + PxU32 index = PX_INVALID_U32; + const PxU32 initIndex = getInitIndex(cachedIndex, nbTris); + + PxReal curT = distance; + const PxReal dpc0 = center.dot(unitDir); + + PxReal bestAlignmentValue = 2.0f; + + PxVec3 bestTriNormal(0.0f); + + for(PxU32 ii=0; ii 0.0f)) + continue; + + const PxReal magnitude = triNormal.magnitude(); + if(magnitude==0.0f) + continue; + + triNormal /= magnitude; + + PxReal currentDistance; + bool unused; + if(!sweepSphereVSTri(currentTri.verts, triNormal, center, radius, unitDir, currentDistance, unused, testInitialOverlap)) + continue; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal hitDot = computeAlignmentValue(triNormal, unitDir); + if(!keepTriangle(currentDistance, hitDot, curT, bestAlignmentValue, distance, distEpsilon)) + continue; + + if(currentDistance==0.0f) + { + triNormalOut = -unitDir; + return setInitialOverlapResults(h, unitDir, i); + } + + curT = currentDistance; + index = i; + bestAlignmentValue = hitDot; + bestTriNormal = triNormal; + if(anyHit) + break; + } + return computeSphereTriangleImpactData(h, triNormalOut, index, curT, center, unitDir, bestTriNormal, triangles, isDoubleSided, meshBothSides); +} + +static PX_FORCE_INLINE PxU32 rayQuadSpecial2(const PxVec3& orig, const PxVec3& dir, const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, float& t, float& u, float& v) +{ + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const float det = edge1.dot(pvec); + + // the non-culling branch + if(det>-LOCAL_EPSILON && det1.0f) + return 1; + if(v<0.0f || v>1.0f) + return 1; + + // Calculate t, ray intersects triangle + t = edge2.dot(qvec) * OneOverDet; + + return 2; +} + +bool Gu::sweepSphereVSQuad(const PxVec3* PX_RESTRICT quadVerts, const PxVec3& normal, const PxVec3& center, float radius, const PxVec3& dir, float& impactDistance) +{ + // Quad formed by 2 tris: + // p0 p1 p2 + // p2 p1 p3 = p3 p2 p1 + // + // p0___p2 + // | /| + // | / | + // | / | + // |/ | + // p1---p3 + // + // Edge10 = p1 - p0 + // Edge20 = p2 - p0 + // Impact point = Edge10*u + Edge20*v + p0 + // => u is along Y, between 0.0 (p0;p2) and 1.0 (p1;p3) + // => v is along X, between 0.0 (p0;p1) and 1.0 (p2;p3) + // + // For the second triangle, + // Edge10b = p2 - p3 = -Edge10 + // Edge20b = p1 - p3 = -Edge20 + + const PxVec3 Edge10 = quadVerts[1] - quadVerts[0]; + const PxVec3 Edge20 = quadVerts[2] - quadVerts[0]; + + if(1) // ### brute force version that always works, but we can probably do better + { + const float r2 = radius*radius; + { + const PxVec3 Cp = closestPtPointTriangle2(center, quadVerts[0], quadVerts[1], quadVerts[2], Edge10, Edge20); + if((Cp - center).magnitudeSquared() <= r2) + { + impactDistance = 0.0f; + return true; + } + } + { + const PxVec3 Cp = closestPtPointTriangle2(center, quadVerts[3], quadVerts[2], quadVerts[1], -Edge10, -Edge20); + if((Cp - center).magnitudeSquared() <= r2) + { + impactDistance = 0.0f; + return true; + } + } + } + + float u,v; + if(1) + { + PxVec3 R = normal * radius; + if(dir.dot(R) >= 0.0f) + R = -R; + + // The first point of the sphere to hit the quad plane is the point of the sphere nearest to + // the quad plane. Hence, we use center - (normal*radius) below. + + // PT: casting against the extruded quad in direction R is the same as casting from a ray moved by -R + float t; + PxU32 r = rayQuadSpecial2(center-R, dir, quadVerts[0], Edge10, Edge20, t, u, v); + if(!r) + return false; + if(r==2) + { + if(t<0.0f) + return false; + impactDistance = t; + return true; + } + } + + #define INTERSECT_POINT_Q (quadVerts[1]*u) + (quadVerts[2]*v) + (quadVerts[0] * (1.0f-u-v)) + + Ps::swap(u,v); + bool TestSphere; + PxU32 e0,e1; + if(u<0.0f) + { + if(v<0.0f) + { + // 0 or 0-1 or 0-2 + e0 = 0; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 0, 1, 2, e1); + } + else if(v>1.0f) + { + // 1 or 1-0 or 1-3 + e0 = 1; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 1, 0, 3, e1); + } + else + { + // 0-1 + TestSphere = false; + e0 = 0; + e1 = 1; + } + } + else if(u>1.0f) + { + if(v<0.0f) + { + // 2 or 2-0 or 2-3 + e0 = 2; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 2, 0, 3, e1); + } + else if(v>1.0f) + { + // 3 or 3-1 or 3-2 + e0 = 3; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 3, 1, 2, e1); + } + else + { + // 2-3 + TestSphere = false; + e0 = 2; + e1 = 3; + } + } + else + { + if(v<0.0f) + { + // 0-2 + TestSphere = false; + e0 = 0; + e1 = 2; + } + else + { + PX_ASSERT(v>=1.0f); // Else hit quad + // 1-3 + TestSphere = false; + e0 = 1; + e1 = 3; + } + } + return testRayVsSphereOrCapsule(impactDistance, TestSphere, center, radius, dir, quadVerts, e0, e1); +} + diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h new file mode 100644 index 000000000..ecfe6ab00 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h @@ -0,0 +1,156 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SPHERE_TRIANGLE_H +#define GU_SWEEP_SPHERE_TRIANGLE_H + +#include "GuSweepTriangleUtils.h" + +namespace physx +{ + +namespace Gu +{ + /** + Sweeps a sphere against a triangle. + + All input parameters (sphere, triangle, sweep direction) must be in the same space. Sweep length is assumed to be infinite. + + By default, 'testInitialOverlap' must be set to true to properly handle the case where the sphere already overlaps the triangle + at the start of the sweep. In such a case, returned impact distance is exactly 0.0f. If it is known ahead of time that the sphere + cannot overlap the triangle at t=0.0, then 'testInitialOverlap' can be set to false to skip the initial overlap test and make the + function run faster. + + If the ray defined by the sphere's center and the unit direction directly intersects the triangle-related part of the TSS (*) (i.e. + the prism from the Minkowski sum of the inflated triangle) then 'directHit' is set to true. Otherwise it is set to false. + + (*) For Triangle Swept Sphere, see http://gamma.cs.unc.edu/SSV/ssv.pdf for the origin of these names. + + \param triVerts [in] triangle vertices + \param triUnitNormal [in] triangle's normalized normal + \param sphereCenter [in] sphere's center + \param sphereRadius [in] sphere's radius + \param unitDir [in] normalized sweep direction. + \param impactDistance [out] impact distance, if a hit has been found. Does not need to be initialized before calling the function. + \param directHit [out] true if a direct hit has been found, see comments above. + \param testInitialOverlap [in] true if an initial sphere-vs-triangle overlap test must be performed, see comments above. + + \return true if an impact has been found (in which case returned result values are valid) + */ + bool sweepSphereVSTri( const PxVec3* PX_RESTRICT triVerts, const PxVec3& triUnitNormal,// Triangle data + const PxVec3& sphereCenter, PxReal sphereRadius, // Sphere data + const PxVec3& unitDir, // Ray data + PxReal& impactDistance, bool& directHit, // Results + bool testInitialOverlap); // Query modifier + + /** + Sweeps a sphere against a quad. + + All input parameters (sphere, quad, sweep direction) must be in the same space. Sweep length is assumed to be infinite. + + Quad must be formed by 2 tris like this: + + p0___p2 + | /| + | / | + | / | + |/ | + p1---p3 + + \param quadVerts [in] quad vertices + \param quadUnitNormal [in] quad's normalized normal + \param sphereCenter [in] sphere's center + \param sphereRadius [in] sphere's radius + \param unitDir [in] normalized sweep direction. + \param impactDistance [out] impact distance, if a hit has been found. Does not need to be initialized before calling the function. + + \return true if an impact has been found (in which case returned result values are valid) + */ + bool sweepSphereVSQuad( const PxVec3* PX_RESTRICT quadVerts, const PxVec3& quadUnitNormal, // Quad data + const PxVec3& sphereCenter, float sphereRadius, // Sphere data + const PxVec3& unitDir, // Ray data + float& impactDistance); // Results + + + // PT: computes proper impact data for sphere-sweep-vs-tri, after the closest tri has been found + PX_FORCE_INLINE bool computeSphereTriangleImpactData(PxSweepHit& h, PxVec3& triNormalOut, PxU32 index, PxReal curT, + const PxVec3& center, const PxVec3& unitDir, const PxVec3& bestTriNormal, + const PxTriangle* PX_RESTRICT triangles, + bool isDoubleSided, bool meshBothSides) + { + if(index==PX_INVALID_U32) + return false; // We didn't touch any triangle + + // Compute impact data only once, using best triangle + PxVec3 hitPos, normal; + computeSphereTriImpactData(hitPos, normal, center, unitDir, curT, triangles[index]); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(normal, meshBothSides, isDoubleSided, bestTriNormal, unitDir)) + normal = -normal; + + h.position = hitPos; + h.normal = normal; + h.distance = curT; + h.faceIndex = index; + h.flags = PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + triNormalOut = bestTriNormal; + return true; + } + + /** + Sweeps a sphere against a set of triangles. + + \param nbTris [in] number of triangles in input array + \param triangles [in] array of input triangles + \param center [in] sphere's center + \param radius [in] sphere's radius + \param unitDir [in] sweep's unit direcion + \param distance [in] sweep's length + \param cachedIndex [in] cached triangle index, or NULL. Cached triangle will be tested first. + \param hit [out] results + \param triNormalOut [out] triangle normal + \param isDoubleSided [in] true if input triangles are double-sided + \param meshBothSides [in] true if PxHitFlag::eMESH_BOTH_SIDES is used + \param anyHit [in] true if PxHitFlag::eMESH_ANY is used + \param testInitialOverlap [in] true if PxHitFlag::eASSUME_NO_INITIAL_OVERLAP is not used + \return true if an impact has been found + */ + bool sweepSphereTriangles( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const PxVec3& center, const PxReal radius, // Sphere data + const PxVec3& unitDir, PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& hit, PxVec3& triNormalOut, // Results + bool isDoubleSided, bool meshBothSides, bool anyHit, bool testInitialOverlap); // Query modifiers + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp new file mode 100644 index 000000000..3960e0025 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp @@ -0,0 +1,223 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxBounds3.h" +#include "GuSweepTriangleUtils.h" +#include "GuDistancePointTriangle.h" +#include "GuVecTriangle.h" +#include "GuVecBox.h" +#include "GuSweepBoxTriangle_FeatureBased.h" +#include "GuInternal.h" +#include "GuGJK.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +#define GU_SAFE_DISTANCE_FOR_NORMAL_COMPUTATION 0.1f + +void Gu::computeSphereTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& center, const PxVec3& dir, float t, const PxTriangle& tri) +{ + const PxVec3 newSphereCenter = center + dir*t; + + // We need the impact point, not computed by the new code + PxReal u_unused, v_unused; + const PxVec3 localHit = closestPtPointTriangle(newSphereCenter, tri.verts[0], tri.verts[1], tri.verts[2], u_unused, v_unused); + PX_UNUSED(u_unused); + PX_UNUSED(v_unused); + + // This is responsible for the cap-vs-box stuck while jumping. However it's needed to slide on box corners! + // PT: this one is also dubious since the sphere/capsule center can be far away from the hit point when the radius is big! + PxVec3 localNormal = newSphereCenter - localHit; + const PxReal m = localNormal.normalize(); + if(m<1e-3f) + tri.normal(localNormal); + + hit = localHit; + normal = localNormal; +} + +// PT: not inlining this rarely-run function makes the benchmark ~500.000 cycles faster... +// PT: using this version all the time makes the benchmark ~300.000 cycles slower. So we just use it as a backup. +static bool runBackupProcedure(PxVec3& hit, PxVec3& normal, const PxVec3& localMotion, const PxVec3& boxExtents, const PxTriangle& triInBoxSpace) +{ + const Vec3V v0 = V3LoadU(triInBoxSpace.verts[0]); + const Vec3V v1 = V3LoadU(triInBoxSpace.verts[1]); + const Vec3V v2 = V3LoadU(triInBoxSpace.verts[2]); + + const TriangleV triangleV(v0, v1, v2); + + // PT: the box is in the triangle's space already + //BoxV boxV(V3LoadU(PxVec3(0.0f)), V3LoadU(boxExtents), + // V3LoadU(PxVec3(1.0f, 0.0f, 0.0f)), V3LoadU(PxVec3(0.0f, 1.0f, 0.0f)), V3LoadU(PxVec3(0.0f, 0.0f, 1.0f))); + + const BoxV boxV(V3Zero(), V3LoadU(boxExtents)); + + Vec3V closestA; + Vec3V closestB; + Vec3V normalV; + FloatV distV; + LocalConvex convexA(triangleV); + LocalConvex convexB(boxV); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter()); + const FloatV contactDist = FMax(); + GjkStatus status_ = gjk, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, closestA, closestB, normalV, distV); + + if(status_==GJK_CONTACT) + return false; + + PxVec3 ml_closestB; + PxVec3 ml_normal; + V3StoreU(closestB, ml_closestB); + V3StoreU(normalV, ml_normal); + + hit = ml_closestB + localMotion; +// normal = -ml_normal; + if((ml_normal.dot(localMotion))>0.0f) + ml_normal = -ml_normal; + normal = ml_normal; + return true; +} + +void Gu::computeBoxTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& boxExtents, const PxVec3& localDir, const PxTriangle& triInBoxSpace, PxReal impactDist) +{ + // PT: the triangle is in "box space", i.e. the box can be seen as an AABB centered around the origin. + + // PT: compute impact point/normal in a second pass. Here we simply re-sweep the box against the best triangle, + // using the feature-based code (which computes impact point and normal). This is not great because: + // - we know there's an impact so why do all tests again? + // - the SAT test & the feature-based tests could return different results because of FPU accuracy. + // The backup procedure makes sure we compute a proper answer even when the SAT and feature-based versions differ. + const PxBounds3 aabb(-boxExtents, boxExtents); + + const PxVec3 oneOverDir( + localDir.x!=0.0f ? 1.0f/localDir.x : 0.0f, + localDir.y!=0.0f ? 1.0f/localDir.y : 0.0f, + localDir.z!=0.0f ? 1.0f/localDir.z : 0.0f); + + // PT: TODO: this is the only place left using sweepBoxTriangle() + // Backface culling could be removed here since we know we want a hit no matter what. Plus, it's sometimes + // incorrectly culled and we hit the backup procedure for no reason. On Win32Modern for unknown reasons + // returned normal is sometimes (0,0,0). In these cases we also switch to the backup procedure. + float t = PX_MAX_F32; // PT: no need to initialize with best dist here since we want a hit no matter what + if(!sweepBoxTriangle(triInBoxSpace, aabb, localDir, oneOverDir, hit, normal, t) || normal.isZero()) + { + // PT: move triangle close to box + const PxVec3 localMotion = localDir*impactDist; + + const PxVec3 delta = localMotion - localDir*GU_SAFE_DISTANCE_FOR_NORMAL_COMPUTATION; + const PxTriangle movedTriangle( + triInBoxSpace.verts[0] - delta, + triInBoxSpace.verts[1] - delta, + triInBoxSpace.verts[2] - delta); + + if(!runBackupProcedure(hit, normal, localMotion, boxExtents, movedTriangle)) + { + // PT: if the backup procedure fails, we give up + hit = PxVec3(0.0f); + normal = -localDir; + } + } +} + +// PT: copy where we know that input vectors are not zero +static PX_FORCE_INLINE void edgeEdgeDistNoZeroVector( PxVec3& x, PxVec3& y, // closest points + const PxVec3& p, const PxVec3& a, // seg 1 origin, vector + const PxVec3& q, const PxVec3& b) // seg 2 origin, vector +{ + const PxVec3 T = q - p; + const PxReal ADotA = a.dot(a); + const PxReal BDotB = b.dot(b); + PX_ASSERT(ADotA!=0.0f); + PX_ASSERT(BDotB!=0.0f); + const PxReal ADotB = a.dot(b); + const PxReal ADotT = a.dot(T); + const PxReal BDotT = b.dot(T); + + // t parameterizes ray (p, a) + // u parameterizes ray (q, b) + + // Compute t for the closest point on ray (p, a) to ray (q, b) + const PxReal Denom = ADotA*BDotB - ADotB*ADotB; + + PxReal t; + if(Denom!=0.0f) + { + t = (ADotT*BDotB - BDotT*ADotB) / Denom; + + // Clamp result so t is on the segment (p, a) + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + + // find u for point on ray (q, b) closest to point at t + PxReal u; + { + u = (t*ADotB - BDotT) / BDotB; + + // if u is on segment (q, b), t and u correspond to closest points, otherwise, clamp u, recompute and clamp t + if(u<0.0f) + { + u = 0.0f; + t = ADotT / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else if(u > 1.0f) + { + u = 1.0f; + t = (ADotB + ADotT) / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + } + + x = p + a * t; + y = q + b * u; +} + +void Gu::computeEdgeEdgeNormal(PxVec3& normal, const PxVec3& p1, const PxVec3& p2_p1, const PxVec3& p3, const PxVec3& p4_p3, const PxVec3& dir, float d) +{ + // PT: cross-product doesn't produce nice normals so we use an edge-edge distance function itself + + // PT: move the edges "0.1" units from each other before the computation. If the edges are too far + // away, computed normal tend to align itself with the swept direction. If the edges are too close, + // closest points x and y become identical and we can't compute a proper normal. + const PxVec3 p1s = p1 + dir*(d-GU_SAFE_DISTANCE_FOR_NORMAL_COMPUTATION); + + PxVec3 x, y; + edgeEdgeDistNoZeroVector(x, y, p1s, p2_p1, p3, p4_p3); + normal = x - y; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h new file mode 100644 index 000000000..323529351 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h @@ -0,0 +1,338 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_TRIANGLE_UTILS_H +#define GU_SWEEP_TRIANGLE_UTILS_H + +#include "CmPhysXCommon.h" +#include "GuSweepSharedTests.h" +#include "GuInternal.h" +#include "PxTriangle.h" +#include "PxQueryReport.h" + +namespace physx +{ + +namespace Gu +{ + // PT: computes proper impact data for sphere-sweep-vs-tri, after the closest tri has been found. + void computeSphereTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& center, const PxVec3& dir, float t, const PxTriangle& tri); + + // PT: computes proper impact data for box-sweep-vs-tri, after the closest tri has been found. + void computeBoxTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& boxExtents, const PxVec3& localDir, const PxTriangle& triInBoxSpace, PxReal impactDist); + + // PT: computes impact normal between two edges. Produces better normals than just the EE cross product. + // This version properly computes the closest points between two colliding edges and makes a normal from these. + void computeEdgeEdgeNormal(PxVec3& normal, const PxVec3& p1, const PxVec3& p2_p1, const PxVec3& p3, const PxVec3& p4_p3, const PxVec3& dir, float d); + + // PT: small function just to avoid duplicating the code. + // Returns index of first triangle we should process (when processing arrays of input triangles) + PX_FORCE_INLINE PxU32 getInitIndex(const PxU32* PX_RESTRICT cachedIndex, PxU32 nbTris) + { + PxU32 initIndex = 0; // PT: by default the first triangle to process is just the first one in the array + if(cachedIndex) // PT: but if we cached the last closest triangle from a previous call... + { + PX_ASSERT(*cachedIndex < nbTris); + PX_UNUSED(nbTris); + initIndex = *cachedIndex; // PT: ...then we should start with that one, to potentially shrink the ray as early as possible + } + return initIndex; + } + + // PT: quick triangle rejection for sphere-based sweeps. + // Please refer to %SDKRoot%\InternalDocumentation\GU\cullTriangle.png for details & diagram. + PX_FORCE_INLINE bool cullTriangle(const PxVec3* PX_RESTRICT triVerts, const PxVec3& dir, PxReal radius, PxReal t, const PxReal dpc0) + { + // PT: project triangle on axis + const PxReal dp0 = triVerts[0].dot(dir); + const PxReal dp1 = triVerts[1].dot(dir); + const PxReal dp2 = triVerts[2].dot(dir); + + // PT: keep min value = earliest possible impact distance + PxReal dp = dp0; + dp = physx::intrinsics::selectMin(dp, dp1); + dp = physx::intrinsics::selectMin(dp, dp2); + + // PT: make sure we keep triangles that are about as close as best current distance + radius += 0.001f + GU_EPSILON_SAME_DISTANCE; + + // PT: if earliest possible impact distance for this triangle is already larger than + // sphere's current best known impact distance, we can skip the triangle + if(dp>dpc0 + t + radius) + { + //PX_ASSERT(resx == 0.0f); + return false; + } + + // PT: if triangle is fully located before the sphere's initial position, skip it too + const PxReal dpc1 = dpc0 - radius; + if(dp0dpc0 + t + radius) + return false; + + // PT: if quad is fully located before the sphere's initial position, skip it too + const float dpc1 = dpc0 - radius; + if(dp0 + // t (t<=fT) t (t>fT) + // return (A)^2 return (B)^2 + // + // |<-------------->| + // fT + // + PX_FORCE_INLINE PxReal squareDistance(const PxVec3& p0, const PxVec3& dir, PxReal t, const PxVec3& point) + { + PxVec3 diff = point - p0; + PxReal fT = diff.dot(dir); + fT = physx::intrinsics::selectMax(fT, 0.0f); + fT = physx::intrinsics::selectMin(fT, t); + diff -= fT*dir; + return diff.magnitudeSquared(); + } + + // PT: quick triangle culling for sphere-based sweeps + // Please refer to %SDKRoot%\InternalDocumentation\GU\coarseCulling.png for details & diagram. + PX_FORCE_INLINE bool coarseCullingTri(const PxVec3& center, const PxVec3& dir, PxReal t, PxReal radius, const PxVec3* PX_RESTRICT triVerts) + { + // PT: compute center of triangle ### could be precomputed? + const PxVec3 triCenter = (triVerts[0] + triVerts[1] + triVerts[2]) * (1.0f/3.0f); + + // PT: distance between the triangle center and the swept path (an LSS) + // Same as: distancePointSegmentSquared(center, center+dir*t, TriCenter); + PxReal d = PxSqrt(squareDistance(center, dir, t, triCenter)) - radius - 0.0001f; + + if (d < 0.0f) // The triangle center lies inside the swept sphere + return true; + + d*=d; + + // PT: coarse capsule-vs-triangle overlap test ### distances could be precomputed? + if(1) + { + if(d <= (triCenter-triVerts[0]).magnitudeSquared()) + return true; + if(d <= (triCenter-triVerts[1]).magnitudeSquared()) + return true; + if(d <= (triCenter-triVerts[2]).magnitudeSquared()) + return true; + } + else + { + const float d0 = (triCenter-triVerts[0]).magnitudeSquared(); + const float d1 = (triCenter-triVerts[1]).magnitudeSquared(); + const float d2 = (triCenter-triVerts[2]).magnitudeSquared(); + float triRadius = physx::intrinsics::selectMax(d0, d1); + triRadius = physx::intrinsics::selectMax(triRadius, d2); + if(d <= triRadius) + return true; + } + return false; + } + + // PT: quick quad culling for sphere-based sweeps. Same as for triangle, adapted for one more vertex. + PX_FORCE_INLINE bool coarseCullingQuad(const PxVec3& center, const PxVec3& dir, PxReal t, PxReal radius, const PxVec3* PX_RESTRICT quadVerts) + { + // PT: compute center of quad ### could be precomputed? + const PxVec3 quadCenter = (quadVerts[0] + quadVerts[1] + quadVerts[2] + quadVerts[3]) * (1.0f/4.0f); + + // PT: distance between the quad center and the swept path (an LSS) + PxReal d = PxSqrt(squareDistance(center, dir, t, quadCenter)) - radius - 0.0001f; + + if (d < 0.0f) // The quad center lies inside the swept sphere + return true; + + d*=d; + + // PT: coarse capsule-vs-quad overlap test ### distances could be precomputed? + if(1) + { + if(d <= (quadCenter-quadVerts[0]).magnitudeSquared()) + return true; + if(d <= (quadCenter-quadVerts[1]).magnitudeSquared()) + return true; + if(d <= (quadCenter-quadVerts[2]).magnitudeSquared()) + return true; + if(d <= (quadCenter-quadVerts[3]).magnitudeSquared()) + return true; + } + return false; + } + + // PT: combined triangle culling for sphere-based sweeps + PX_FORCE_INLINE bool rejectTriangle(const PxVec3& center, const PxVec3& unitDir, PxReal curT, PxReal radius, const PxVec3* PX_RESTRICT triVerts, const PxReal dpc0) + { + if(!coarseCullingTri(center, unitDir, curT, radius, triVerts)) + return true; + if(!cullTriangle(triVerts, unitDir, radius, curT, dpc0)) + return true; + return false; + } + + // PT: combined quad culling for sphere-based sweeps + PX_FORCE_INLINE bool rejectQuad(const PxVec3& center, const PxVec3& unitDir, PxReal curT, PxReal radius, const PxVec3* PX_RESTRICT quadVerts, const PxReal dpc0) + { + if(!coarseCullingQuad(center, unitDir, curT, radius, quadVerts)) + return true; + if(!cullQuad(quadVerts, unitDir, radius, curT, dpc0)) + return true; + return false; + } + + PX_FORCE_INLINE bool shouldFlipNormal(const PxVec3& normal, bool meshBothSides, bool isDoubleSided, const PxVec3& triangleNormal, const PxVec3& dir) + { + // PT: this function assumes that input normal is opposed to the ray/sweep direction. This is always + // what we want except when we hit a single-sided back face with 'meshBothSides' enabled. + + if(!meshBothSides || isDoubleSided) + return false; + + PX_ASSERT(normal.dot(dir) <= 0.0f); // PT: if this fails, the logic below cannot be applied + PX_UNUSED(normal); + return triangleNormal.dot(dir) > 0.0f; // PT: true for back-facing hits + } + + PX_FORCE_INLINE bool shouldFlipNormal(const PxVec3& normal, bool meshBothSides, bool isDoubleSided, const PxTriangle& triangle, const PxVec3& dir, const PxTransform* pose) + { + // PT: this function assumes that input normal is opposed to the ray/sweep direction. This is always + // what we want except when we hit a single-sided back face with 'meshBothSides' enabled. + + if(!meshBothSides || isDoubleSided) + return false; + + PX_ASSERT(normal.dot(dir) <= 0.0f); // PT: if this fails, the logic below cannot be applied + PX_UNUSED(normal); + + PxVec3 triangleNormal; + triangle.denormalizedNormal(triangleNormal); + + if(pose) + triangleNormal = pose->rotate(triangleNormal); + + return triangleNormal.dot(dir) > 0.0f; // PT: true for back-facing hits + } + + // PT: implements the spec for IO sweeps in a single place (to ensure consistency) + PX_FORCE_INLINE bool setInitialOverlapResults(PxSweepHit& hit, const PxVec3& unitDir, PxU32 faceIndex) + { + // PT: please write these fields in the order they are listed in the struct. + hit.faceIndex = faceIndex; + hit.flags = PxHitFlag::eNORMAL|PxHitFlag::eFACE_INDEX; + hit.normal = -unitDir; + hit.distance = 0.0f; + return true; // PT: true indicates a hit, saves some lines in calling code + } + + PX_FORCE_INLINE void computeBoxLocalImpact( PxVec3& pos, PxVec3& normal, PxHitFlags& outFlags, + const Box& box, const PxVec3& localDir, const PxTriangle& triInBoxSpace, + const PxHitFlags inFlags, bool isDoubleSided, bool meshBothSides, PxReal impactDist) + { + if(inFlags & (PxHitFlag::eNORMAL|PxHitFlag::ePOSITION)) + { + PxVec3 localPos, localNormal; + computeBoxTriImpactData(localPos, localNormal, box.extents, localDir, triInBoxSpace, impactDist); + + if(inFlags & PxHitFlag::eNORMAL) + { + localNormal.normalize(); + + // PT: doing this after the 'rotate' minimizes errors when normal and dir are close to perpendicular + // ....but we must do it before the rotate now, because triangleNormal is in box space (and thus we + // need the normal with the proper orientation, in box space. We can't fix it after it's been rotated + // to box space. + // Technically this one is only here because of the EE cross product in the feature-based sweep. + // PT: TODO: revisit corresponding code in computeImpactData, get rid of ambiguity + // PT: TODO: this may not be needed anymore + if((localNormal.dot(localDir))>0.0f) + localNormal = -localNormal; + + // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention + if(shouldFlipNormal(localNormal, meshBothSides, isDoubleSided, triInBoxSpace, localDir, NULL)) + localNormal = -localNormal; + + normal = box.rotate(localNormal); + outFlags |= PxHitFlag::eNORMAL; + } + + if(inFlags & PxHitFlag::ePOSITION) + { + pos = box.transform(localPos); + outFlags |= PxHitFlag::ePOSITION; + } + } + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp b/src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp new file mode 100644 index 000000000..e0215b60b --- /dev/null +++ b/src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp @@ -0,0 +1,769 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxImmediateMode.h" +#include "CmUtils.h" +#include "PsMathUtils.h" +#include "../../lowleveldynamics/src/DyBodyCoreIntegrator.h" +#include "../../lowleveldynamics/src/DySolverBody.h" +#include "../../lowleveldynamics/src/DyContactPrep.h" +#include "../../lowleveldynamics/src/DyCorrelationBuffer.h" +#include "../../lowleveldynamics/src/DyConstraintPrep.h" +#include "../../lowleveldynamics/src/DySolverControl.h" +#include "../../lowleveldynamics/src/DySolverContext.h" +#include "PxGeometry.h" +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" +#include "../../lowlevel/common/include/collision/PxcContactMethodImpl.h" +#include "GuPersistentContactManifold.h" +#include "NpConstraint.h" + +namespace physx +{ + + namespace + { + +#define MAX_NUM_PARTITIONS 32u + + static PxU32 bitTable[32] = + { + 1u << 0, 1u << 1, 1u << 2, 1u << 3, 1u << 4, 1u << 5, 1u << 6, 1u << 7, 1u << 8, 1u << 9, 1u << 10, 1u << 11, 1u << 12, 1u << 13, 1u << 14, 1u << 15, 1u << 16, 1u << 17, + 1u << 18, 1u << 19, 1u << 20, 1u << 21, 1u << 22, 1u << 23, 1u << 24, 1u << 25, 1u << 26, 1u << 27, 1u << 28, 1u << 29, 1u << 30, 1u << 31 + }; + + PxU32 getBit(const PxU32 index) + { + PX_ASSERT(index < 32); + return bitTable[index]; + } + + + class RigidBodyClassification + { + PxSolverBody* PX_RESTRICT mBodies; + PxU32 mNumBodies; + + public: + RigidBodyClassification(PxSolverBody* PX_RESTRICT bodies, PxU32 numBodies) : mBodies(bodies), mNumBodies(numBodies) + { + } + + //Returns true if it is a dynamic-dynamic constriant; false if it is a dynamic-static or dynamic-kinematic constraint + PX_FORCE_INLINE bool classifyConstraint(const PxSolverConstraintDesc& desc, uintptr_t& indexA, uintptr_t& indexB, bool& activeA, bool& activeB) const + { + indexA = uintptr_t(desc.bodyA - mBodies); + indexB = uintptr_t(desc.bodyB - mBodies); + activeA = indexA < mNumBodies; + activeB = indexB < mNumBodies; + return activeA && activeB; + } + + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(PxU32* numConstraintsPerPartition) + { + for (PxU32 a = 0; a < mNumBodies; ++a) + { + mBodies[a].solverProgress = 0; + + for (PxU32 b = 0; b < mBodies[a].maxSolverFrictionProgress; ++b) + { + PxU32 partId = PxMin(PxU32(mBodies[a].maxSolverNormalProgress + b), MAX_NUM_PARTITIONS); + numConstraintsPerPartition[partId]++; + } + } + } + }; + + template + void classifyConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + PxU32* numConstraintsPerPartition) + { + const PxSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxMemZero(numConstraintsPerPartition, sizeof(PxU32) * 33); + + for (PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB); + + if (notContainsStatic) + { + PxU32 partitionsA = _desc->bodyA->solverProgress; + PxU32 partitionsB = _desc->bodyB->solverProgress; + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + //Write to overflow partition... + numConstraintsPerPartition[availablePartition]++; + _desc->bodyA->maxSolverNormalProgress = MAX_NUM_PARTITIONS; + _desc->bodyB->maxSolverNormalProgress = MAX_NUM_PARTITIONS; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + partitionsA |= partitionBit; + partitionsB |= partitionBit; + } + + _desc->bodyA->solverProgress = partitionsA; + _desc->bodyB->solverProgress = partitionsB; + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + _desc->bodyA->maxSolverNormalProgress = PxMax(_desc->bodyA->maxSolverNormalProgress, PxU16(availablePartition)); + _desc->bodyB->maxSolverNormalProgress = PxMax(_desc->bodyB->maxSolverNormalProgress, PxU16(availablePartition)); + } + else + { + //Just count the number of static constraints and store in maxSolverFrictionProgress... + if (activeA) + _desc->bodyA->maxSolverFrictionProgress++; + else if (activeB) + _desc->bodyB->maxSolverFrictionProgress++; + } + } + + classification.reserveSpaceForStaticConstraints(numConstraintsPerPartition); + + } + + template + void writeConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + PxU32* accumulatedConstraintsPerPartition, PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDesc) + { + const PxSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + for (PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB); + + if (notContainsStatic) + { + PxU32 partitionsA = _desc->bodyA->solverProgress; + PxU32 partitionsB = _desc->bodyB->solverProgress; + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + + partitionsA |= partitionBit; + partitionsB |= partitionBit; + } + + _desc->bodyA->solverProgress = partitionsA; + _desc->bodyB->solverProgress = partitionsB; + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + } + else + { + //Just count the number of static constraints and store in maxSolverFrictionProgress... + PxU32 index = 0; + if (activeA) + index = PxMin(PxU32(_desc->bodyA->maxSolverNormalProgress + _desc->bodyA->maxSolverFrictionProgress++), MAX_NUM_PARTITIONS); + else if (activeB) + index = PxMin(PxU32(_desc->bodyB->maxSolverNormalProgress + _desc->bodyB->maxSolverFrictionProgress++), MAX_NUM_PARTITIONS); + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[index]++] = *_desc; + } + } + } + + } + + + void immediate::PxConstructSolverBodies(const PxRigidBodyData* inRigidData, PxSolverBodyData* outSolverBodyData, const PxU32 nbBodies, const PxVec3& gravity, const PxReal dt) + { + for (PxU32 a = 0; a < nbBodies; ++a) + { + const PxRigidBodyData& rigidData = inRigidData[a]; + PxVec3 lv = rigidData.linearVelocity, av = rigidData.angularVelocity; + Dy::bodyCoreComputeUnconstrainedVelocity(gravity, dt, rigidData.linearDamping, rigidData.angularDamping, 1.f, rigidData.maxLinearVelocitySq, rigidData.maxAngularVelocitySq, lv, av, false); + Dy::copyToSolverBodyData(lv, av, rigidData.invMass, rigidData.invInertia, rigidData.body2World, -rigidData.maxDepenetrationVelocity, rigidData.maxContactImpulse, IG_INVALID_NODE, PX_MAX_F32, outSolverBodyData[a], 0); + } + } + + void immediate::PxConstructStaticSolverBody(const PxTransform& globalPose, PxSolverBodyData& solverBodyData) + { + PxVec3 zero(0.f); + Dy::copyToSolverBodyData(zero, zero, 0.f, zero, globalPose, -PX_MAX_F32, PX_MAX_F32, IG_INVALID_NODE, PX_MAX_F32, solverBodyData, 0); + } + + + void immediate::PxIntegrateSolverBodies(PxSolverBodyData* solverBodyData, PxSolverBody* solverBody, const PxVec3* linearMotionVelocity, const PxVec3* angularMotionState, const PxU32 nbBodiesToIntegrate, PxReal dt) + { + for (PxU32 i = 0; i < nbBodiesToIntegrate; ++i) + { + PxVec3 lmv = linearMotionVelocity[i]; + PxVec3 amv = angularMotionState[i]; + Dy::integrateCore(lmv, amv, solverBody[i], solverBodyData[i], dt); + } + } + + + PxU32 immediate::PxBatchConstraints(PxSolverConstraintDesc* solverConstraintDescs, const PxU32 nbConstraints, PxSolverBody* solverBodies, PxU32 numBodies, PxConstraintBatchHeader* outBatchHeaders, + PxSolverConstraintDesc* outOrderedConstraintDescs) + { + PxU32 constraintsPerPartition[MAX_NUM_PARTITIONS + 1]; + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = solverBodies[a]; + body.solverProgress = 0; + //We re-use maxSolverFrictionProgress and maxSolverNormalProgress to record the + //maximum partition used by dynamic constraints and the number of static constraints affecting + //a body. We use this to make partitioning much cheaper and be able to support + body.maxSolverFrictionProgress = 0; + body.maxSolverNormalProgress = 0; + } + + { + RigidBodyClassification classification(solverBodies, numBodies); + classifyConstraintDesc(solverConstraintDescs, nbConstraints, classification, constraintsPerPartition); + + PxU32 accumulation = 0; + for (PxU32 a = 0; a < MAX_NUM_PARTITIONS+1; ++a) + { + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; + } + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = solverBodies[a]; + body.solverProgress = 0; + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... + body.maxSolverFrictionProgress = 0; + } + + writeConstraintDesc(solverConstraintDescs, nbConstraints, classification, constraintsPerPartition, outOrderedConstraintDescs); + + PxU32 numHeaders = 0; + PxU32 currentPartition = 0; + PxU32 maxJ = nbConstraints == 0 ? 0 : constraintsPerPartition[0]; + + const PxU32 maxBatchPartition = MAX_NUM_PARTITIONS; + for (PxU32 a = 0; a < nbConstraints;) + { + PxConstraintBatchHeader& header = outBatchHeaders[numHeaders++]; + header.mStartIndex = a; + + PxU32 loopMax = PxMin(maxJ - a, 4u); + PxU16 j = 0; + if (loopMax > 0) + { + j = 1; + PxSolverConstraintDesc& desc = outOrderedConstraintDescs[a]; + if (currentPartition < maxBatchPartition) + { + for (; j < loopMax && desc.constraintLengthOver16 == outOrderedConstraintDescs[a + j].constraintLengthOver16; ++j); + } + header.mStride = j; + header.mConstraintType = desc.constraintLengthOver16; + } + if (maxJ == (a + j) && maxJ != nbConstraints) + { + currentPartition++; + maxJ = constraintsPerPartition[currentPartition]; + } + a += j; + } + return numHeaders; + } + } + + + bool immediate::PxCreateContactConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverContactDesc* contactDescs, + PxConstraintAllocator& allocator, PxReal invDt, PxReal bounceThreshold, PxReal frictionOffsetThreshold, PxReal correlationDistance) + { + PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); + PX_ASSERT(bounceThreshold < 0.f); + PX_ASSERT(frictionOffsetThreshold > 0.f); + PX_ASSERT(correlationDistance > 0.f); + + Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; + + Dy::CorrelationBuffer cb; + + PxU32 currentContactDescIdx = 0; + + for (PxU32 i = 0; i < nbHeaders; ++i) + { + PxConstraintBatchHeader& batchHeader = batchHeaders[i]; + if (batchHeader.mStride == 4) + { + PxU32 totalContacts = contactDescs[currentContactDescIdx].numContacts + contactDescs[currentContactDescIdx + 1].numContacts + + contactDescs[currentContactDescIdx + 2].numContacts + contactDescs[currentContactDescIdx + 3].numContacts; + + if (totalContacts <= 64) + { + state = Dy::createFinalizeSolverContacts4(cb, + contactDescs + currentContactDescIdx, + invDt, + bounceThreshold, + frictionOffsetThreshold, + correlationDistance, + 0.f, + allocator); + } + } + + if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) + { + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + Dy::createFinalizeSolverContacts(contactDescs[currentContactDescIdx + a], cb, invDt, bounceThreshold, + frictionOffsetThreshold, correlationDistance, 0.f, allocator, NULL); + } + } + PxU8 type = *contactDescs[currentContactDescIdx].desc->constraint; + + if (type == DY_SC_TYPE_STATIC_CONTACT) + { + //Check if any block of constraints is classified as type static (single) contact constraint. + //If they are, iterate over all constraints grouped with it and switch to "dynamic" contact constraint + //type if there's a dynamic contact constraint in the group. + for (PxU32 c = 1; c < batchHeader.mStride; ++c) + { + if (*contactDescs[currentContactDescIdx + c].desc->constraint == DY_SC_TYPE_RB_CONTACT) + { + type = DY_SC_TYPE_RB_CONTACT; + break; + } + } + } + + batchHeader.mConstraintType = type; + + currentContactDescIdx += batchHeader.mStride; + } + + + + + + return true; + } + + + bool immediate::PxCreateJointConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, PxReal dt, PxReal invDt) + { + PX_ASSERT(dt > 0.f); + PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); + + Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; + + PxU32 currentDescIdx = 0; + for (PxU32 i = 0; i < nbHeaders; ++i) + { + PxConstraintBatchHeader& batchHeader = batchHeaders[i]; + + PxU8 type = DY_SC_TYPE_BLOCK_1D; + if (batchHeader.mStride == 4) + { + PxU32 totalRows = 0; + PxU32 maxRows = 0; + bool batchable = true; + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + if (jointDescs[currentDescIdx + a].numRows == 0) + { + batchable = false; + break; + } + totalRows += jointDescs[currentDescIdx + a].numRows; + maxRows = PxMax(maxRows, jointDescs[currentDescIdx + a].numRows); + } + + if (batchable) + { + state = Dy::setupSolverConstraint4 + (jointDescs + currentDescIdx, + dt, invDt, totalRows, + allocator, maxRows); + } + } + + if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) + { + type = DY_SC_TYPE_RB_1D; + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + Dy::ConstraintHelper::setupSolverConstraint(jointDescs[currentDescIdx + a], + allocator, dt, invDt, NULL); + } + } + + batchHeader.mConstraintType = type; + currentDescIdx += batchHeader.mStride; + } + + return true; + } + + bool immediate::PxCreateJointConstraintsWithShaders(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxConstraint** constraints, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, + PxReal dt, PxReal invDt) + { + Px1DConstraint allRows[Dy::MAX_CONSTRAINT_ROWS * 4]; + + //Runs shaders to fill in rows... + + PxU32 currentDescIdx = 0; + + for (PxU32 i = 0; i < nbHeaders; ++i) + { + + PxU32 numRows = 0; + PxU32 preppedIndex = 0; + PxU32 maxRows = 0; + + PxConstraintBatchHeader& batchHeader = batchHeaders[i]; + + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + Px1DConstraint* rows = allRows + numRows; + PxSolverConstraintPrepDesc& desc = jointDescs[currentDescIdx + a]; + + NpConstraint* npConstraint = static_cast(constraints[currentDescIdx + a]); + + npConstraint->updateConstants(); + + Sc::ConstraintCore& core = npConstraint->getScbConstraint().getScConstraint(); + + PxConstraintSolverPrep prep = core.getPxConnector()->getPrep(); + const void* constantBlock = core.getPxConnector()->getConstantBlock(); + PX_ASSERT(prep); + + PxMemZero(rows + preppedIndex, sizeof(Px1DConstraint)*(Dy::MAX_CONSTRAINT_ROWS)); + for (PxU32 b = preppedIndex; b < Dy::MAX_CONSTRAINT_ROWS; ++b) + { + Px1DConstraint& c = rows[b]; + //Px1DConstraintInit(c); + c.minImpulse = -PX_MAX_REAL; + c.maxImpulse = PX_MAX_REAL; + } + + desc.mInvMassScales.linear0 = desc.mInvMassScales.linear1 = desc.mInvMassScales.angular0 = desc.mInvMassScales.angular1 = 1.f; + + desc.body0WorldOffset = PxVec3(0.f); + + PxVec3 cA2w, cB2w; + PxU32 constraintCount = prep(rows, + desc.body0WorldOffset, + Dy::MAX_CONSTRAINT_ROWS, + desc.mInvMassScales, + constantBlock, + desc.bodyFrame0, desc.bodyFrame1, + !!(core.getFlags() & PxConstraintFlag::eENABLE_EXTENDED_LIMITS), + cA2w, cB2w); + + preppedIndex = Dy::MAX_CONSTRAINT_ROWS - constraintCount; + + maxRows = PxMax(constraintCount, maxRows); + + desc.rows = rows; + desc.numRows = constraintCount; + numRows += constraintCount; + } + + PxCreateJointConstraints(&batchHeader, 1, jointDescs + currentDescIdx, allocator, dt, invDt); + + currentDescIdx += batchHeader.mStride; + } + + return true; //KS - TODO - do some error reporting/management... + } + + PX_FORCE_INLINE bool PxIsZero(PxSolverBody* bodies, PxU32 nbBodies) + { + for (PxU32 i = 0; i < nbBodies; ++i) + { + if (!bodies[i].linearVelocity.isZero() || + !bodies[i].angularState.isZero()) + return false; + } + return true; + } + + + void immediate::PxSolveConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbBatchHeaders, PxSolverConstraintDesc* solverConstraintDescs, PxSolverBody* solverBodies, + PxVec3* linearMotionVelocity, PxVec3* angularMotionVelocity, const PxU32 nbSolverBodies, const PxU32 nbPositionIterations, const PxU32 nbVelocityIterations) + { + PX_ASSERT(nbPositionIterations > 0); + PX_ASSERT(nbVelocityIterations > 0); + PX_ASSERT(PxIsZero(solverBodies, nbSolverBodies)); //Ensure that solver body velocities have been zeroed before solving + + //Stage 1: solve the position iterations... + Dy::SolveBlockMethod* solveTable = Dy::getSolveBlockTable(); + + Dy::SolveBlockMethod* solveConcludeTable = Dy::getSolverConcludeBlockTable(); + + Dy::SolveWriteBackBlockMethod* solveWritebackTable = Dy::getSolveWritebackBlockTable(); + + Dy::SolverContext cache; + cache.mThresholdStreamIndex = 0; + cache.mThresholdStreamLength = 0xFFFFFFF; + + PX_ASSERT(nbPositionIterations > 0); + PX_ASSERT(nbVelocityIterations > 0); + + for (PxU32 i = nbPositionIterations; i > 1; --i) + { + cache.doFriction = i <= 3; + for (PxU32 a = 0; a < nbBatchHeaders; ++a) + { + PxConstraintBatchHeader& batch = batchHeaders[a]; + solveTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + } + } + + cache.doFriction = true; + for (PxU32 a = 0; a < nbBatchHeaders; ++a) + { + PxConstraintBatchHeader& batch = batchHeaders[a]; + solveConcludeTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + } + + //Save motion velocities... + + for (PxU32 a = 0; a < nbSolverBodies; ++a) + { + linearMotionVelocity[a] = solverBodies[a].linearVelocity; + angularMotionVelocity[a] = solverBodies[a].angularState; + } + + for (PxU32 i = nbVelocityIterations; i > 1; --i) + { + for (PxU32 a = 0; a < nbBatchHeaders; ++a) + { + PxConstraintBatchHeader& batch = batchHeaders[a]; + solveTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + } + } + + for (PxU32 a = 0; a < nbBatchHeaders; ++a) + { + PxConstraintBatchHeader& batch = batchHeaders[a]; + solveWritebackTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + } + + } + + + void createCache(Gu::Cache& cache, PxGeometryType::Enum geomType0, PxGeometryType::Enum geomType1, PxCacheAllocator& allocator) + { + + if (gEnablePCMCaching[geomType0][geomType1]) + { + if (geomType0 <= PxGeometryType::eCONVEXMESH && + geomType1 <= PxGeometryType::eCONVEXMESH) + { + if (geomType0 == PxGeometryType::eSPHERE || geomType1 == PxGeometryType::eSPHERE) + { + Gu::PersistentContactManifold* manifold = PX_PLACEMENT_NEW(allocator.allocateCacheData(sizeof(Gu::SpherePersistentContactManifold)), Gu::SpherePersistentContactManifold)(); + cache.setManifold(manifold); + } + else + { + Gu::PersistentContactManifold* manifold = PX_PLACEMENT_NEW(allocator.allocateCacheData(sizeof(Gu::LargePersistentContactManifold)), Gu::LargePersistentContactManifold)(); + cache.setManifold(manifold); + + } + cache.getManifold().clearManifold(); + } + else + { + //ML: raised 1 to indicate the manifold is multiManifold which is for contact gen in mesh/height field + //cache.manifold = 1; + cache.setMultiManifold(NULL); + } + } + else + { + //cache.manifold = 0; + cache.mCachedData = NULL; + cache.mManifoldFlags = 0; + } + } + + + + bool immediate::PxGenerateContacts(const PxGeometry* const * geom0, const PxGeometry* const * geom1, const PxTransform* pose0, const PxTransform* pose1, PxCache* contactCache, const PxU32 nbPairs, PxContactRecorder& contactRecorder, + const PxReal contactDistance, const PxReal meshContactMargin, const PxReal toleranceLength, PxCacheAllocator& allocator) + { + PX_ASSERT(meshContactMargin > 0.f); + PX_ASSERT(toleranceLength > 0.f); + PX_ASSERT(contactDistance > 0.f); + Gu::ContactBuffer contactBuffer; + + for (PxU32 i = 0; i < nbPairs; ++i) + { + + contactBuffer.count = 0; + PxGeometryType::Enum type0 = geom0[i]->getType(); + PxGeometryType::Enum type1 = geom1[i]->getType(); + + const PxGeometry* tempGeom0 = geom0[i]; + const PxGeometry* tempGeom1 = geom1[i]; + + PX_ALIGN(16, PxTransform) transform0 = pose0[i]; + PX_ALIGN(16, PxTransform) transform1 = pose1[i]; + + const bool bSwap = type0 > type1; + if (bSwap) + { + const PxGeometry* temp = tempGeom0; + tempGeom0 = geom1[i]; + tempGeom1 = temp; + PxGeometryType::Enum tempType = type0; + type0 = type1; + type1 = tempType; + transform1 = pose0[i]; + transform0 = pose1[i]; + } + + const Gu::GeometryUnion geomUnion0(*tempGeom0); + const Gu::GeometryUnion geomUnion1(*tempGeom1); + + //Now work out which type of PCM we need... + + Gu::Cache& cache = static_cast(contactCache[i]); + + bool needsMultiManifold = type1 > PxGeometryType::eCONVEXMESH; + + Gu::NarrowPhaseParams params(contactDistance, meshContactMargin, toleranceLength); + + if (needsMultiManifold) + { + Gu::MultiplePersistentContactManifold multiManifold; + + if (cache.isMultiManifold()) + { + multiManifold.fromBuffer(reinterpret_cast(&cache.getMultipleManifold())); + } + else + { + multiManifold.initialize(); + } + cache.setMultiManifold(&multiManifold); + + //Do collision detection, then write manifold out... + g_PCMContactMethodTable[type0][type1](geomUnion0, geomUnion1, transform0, transform1, params, cache, contactBuffer, NULL); + + const PxU32 size = (sizeof(Gu::MultiPersistentManifoldHeader) + + multiManifold.mNumManifolds * sizeof(Gu::SingleManifoldHeader) + + multiManifold.mNumTotalContacts * sizeof(Gu::CachedMeshPersistentContact)); + + PxU8* buffer = allocator.allocateCacheData(size); + + multiManifold.toBuffer(buffer); + + cache.setMultiManifold(buffer); + + } + else + { + //Allocate the type of manifold we need again... + Gu::PersistentContactManifold* oldManifold = NULL; + + if (cache.isManifold()) + oldManifold = &cache.getManifold(); + + //Allocates and creates the PCM... + createCache(cache, type0, type1, allocator); + + //Copy PCM from old to new manifold... + if (oldManifold) + { + Gu::PersistentContactManifold& manifold = cache.getManifold(); + manifold.mRelativeTransform = oldManifold->mRelativeTransform; + manifold.mNumContacts = oldManifold->mNumContacts; + manifold.mNumWarmStartPoints = oldManifold->mNumWarmStartPoints; + manifold.mAIndice[0] = oldManifold->mAIndice[0]; manifold.mAIndice[1] = oldManifold->mAIndice[1]; + manifold.mAIndice[2] = oldManifold->mAIndice[2]; manifold.mAIndice[3] = oldManifold->mAIndice[3]; + manifold.mBIndice[0] = oldManifold->mBIndice[0]; manifold.mBIndice[1] = oldManifold->mBIndice[1]; + manifold.mBIndice[2] = oldManifold->mBIndice[2]; manifold.mBIndice[3] = oldManifold->mBIndice[3]; + PxMemCopy(manifold.mContactPoints, oldManifold->mContactPoints, sizeof(Gu::PersistentContact)*manifold.mNumContacts); + } + + g_PCMContactMethodTable[type0][type1](geomUnion0, geomUnion1, transform0, transform1, params, cache, contactBuffer, NULL); + } + + if (bSwap) + { + for (PxU32 a = 0; a < contactBuffer.count; ++a) + { + contactBuffer.contacts[a].normal = -contactBuffer.contacts[a].normal; + } + } + + + if (contactBuffer.count != 0) + { + //Record this contact pair... + contactRecorder.recordContacts(contactBuffer.contacts, contactBuffer.count, i); + } + } + return true; + } + +} + diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h new file mode 100644 index 000000000..9178d0578 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h @@ -0,0 +1,167 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_MATERIAL_H +#define PXS_MATERIAL_H + +#include "foundation/PxVec3.h" +#include "PxMaterial.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PxMetaData.h" +#include "PsUtilities.h" + +namespace physx +{ + +#define MATERIAL_INVALID_HANDLE 0xffffffff + + //PX_ALIGN_PREFIX(16) struct PxsMaterialData + //{ + // PxReal dynamicFriction; + // PxReal staticFriction; + // PxReal restitution; + // PxReal dynamicFrictionV; + // PxReal staticFrictionV; + // PxVec3 dirOfAnisotropy;//might need to get rid of this + // PxCombineMode::Enum frictionCombineMode; + // PxCombineMode::Enum restitutionCombineMode; + + // PxMaterialFlags flags; + // PxU8 paddingFromFlags[2]; + // PxU32 pads; + + // PxsMaterialData() + // : dynamicFriction(0.0) + // , staticFriction(0.0f) + // , restitution(0.0f) + // , dynamicFrictionV(0.0f) + // , staticFrictionV(0.0f) + // , dirOfAnisotropy(1,0,0) + // , frictionCombineMode(PxCombineMode::eAVERAGE) + // , restitutionCombineMode(PxCombineMode::eAVERAGE) + // {} + + // PX_FORCE_INLINE PxCombineMode::Enum getFrictionCombineMode() const + // { + // return frictionCombineMode; + // } + + // + // PX_FORCE_INLINE PxCombineMode::Enum getRestitutionCombineMode() const + // { + // return restitutionCombineMode; + // } + + // PX_FORCE_INLINE void setFrictionCombineMode(PxCombineMode::Enum frictionFlags) + // { + // frictionCombineMode = frictionFlags; + // } + + // PX_FORCE_INLINE void setRestitutionCombineMode(PxCombineMode::Enum restitutionFlags) + // { + // restitutionCombineMode = restitutionFlags; + // } + + //}PX_ALIGN_SUFFIX(16); + + PX_ALIGN_PREFIX(16) struct PxsMaterialData + { + PxReal dynamicFriction; //4 + PxReal staticFriction; //8 + PxReal restitution; //12 + PxMaterialFlags flags; //14 + PxU8 fricRestCombineMode; //15 + PxU8 padding; //16 + + PxsMaterialData() + : dynamicFriction(0.0f) + , staticFriction(0.0f) + , restitution(0.0f) + , fricRestCombineMode((PxCombineMode::eAVERAGE << 4) | PxCombineMode::eAVERAGE) + , padding(0) + {} + + PxsMaterialData(const PxEMPTY) {} + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxCombineMode::Enum getFrictionCombineMode() const + { + return PxCombineMode::Enum(fricRestCombineMode >> 4); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxCombineMode::Enum getRestitutionCombineMode() const + { + return PxCombineMode::Enum(fricRestCombineMode & 0xf); + } + + PX_FORCE_INLINE void setFrictionCombineMode(PxCombineMode::Enum frictionFlags) + { + fricRestCombineMode = Ps::to8((fricRestCombineMode & 0xf) | (frictionFlags << 4)); + } + + PX_FORCE_INLINE void setRestitutionCombineMode(PxCombineMode::Enum restitutionFlags) + { + fricRestCombineMode = Ps::to8((fricRestCombineMode & 0xf0) | (restitutionFlags)); + } + + }PX_ALIGN_SUFFIX(16); + + + class PxsMaterialCore : public PxsMaterialData, public Ps::UserAllocated + { + public: + + PxsMaterialCore(const PxsMaterialData& desc): PxsMaterialData(desc), mNxMaterial(0), mMaterialIndex(MATERIAL_INVALID_HANDLE) + { + } + + PxsMaterialCore(): mNxMaterial(0), mMaterialIndex(MATERIAL_INVALID_HANDLE) + { + } + + PxsMaterialCore(const PxEMPTY) : PxsMaterialData(PxEmpty) {} + + ~PxsMaterialCore() + { + } + + PX_FORCE_INLINE void setNxMaterial(PxMaterial* m) { mNxMaterial = m; } + PX_FORCE_INLINE PxMaterial* getNxMaterial() const { return mNxMaterial; } + PX_FORCE_INLINE void setMaterialIndex(const PxU32 materialIndex) { mMaterialIndex = materialIndex; } + PX_FORCE_INLINE PxU32 getMaterialIndex() const { return mMaterialIndex; } + + protected: + PxMaterial* mNxMaterial; + PxU32 mMaterialIndex; //handle assign by the handle manager + }; + +} //namespace phyxs + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h new file mode 100644 index 000000000..d81ca4eb6 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_MATERIALMANAGER +#define PXS_MATERIALMANAGER + +#include "PxsMaterialCore.h" +#include "PsAlignedMalloc.h" + +namespace physx +{ + struct PxsMaterialInfo + { + PxU16 mMaterialIndex0; + PxU16 mMaterialIndex1; + }; + + class PxsMaterialManager + { + public: + PxsMaterialManager() + { + const PxU32 matCount = 128; + materials = reinterpret_cast(physx::shdfnd::AlignedAllocator<16>().allocate(sizeof(PxsMaterialCore)*matCount, __FILE__, __LINE__)); + maxMaterials = matCount; + for(PxU32 i=0; i().deallocate(materials); + } + + void setMaterial(PxsMaterialCore* mat) + { + const PxU32 materialIndex = mat->getMaterialIndex(); + resize(materialIndex+1); + materials[materialIndex] = *mat; + } + + void updateMaterial(PxsMaterialCore* mat) + { + materials[mat->getMaterialIndex()] =*mat; + } + + void removeMaterial(PxsMaterialCore* mat) + { + mat->setMaterialIndex(MATERIAL_INVALID_HANDLE); + } + + PX_FORCE_INLINE PxsMaterialCore* getMaterial(const PxU32 index)const + { + PX_ASSERT(index < maxMaterials); + return &materials[index]; + } + + PxU32 getMaxSize()const + { + return maxMaterials; + } + + void resize(PxU32 minValueForMax) + { + if(maxMaterials>=minValueForMax) + return; + + const PxU32 numMaterials = maxMaterials; + + maxMaterials = (minValueForMax+31)&~31; + PxsMaterialCore* mat = reinterpret_cast(physx::shdfnd::AlignedAllocator<16>().allocate(sizeof(PxsMaterialCore)*maxMaterials, __FILE__, __LINE__)); + for(PxU32 i=0; i().deallocate(materials); + + materials = mat; + } + + PxsMaterialCore* materials;//make sure materials's start address is 16 bytes align + PxU32 maxMaterials; + PxU32 mPad[2]; + }; + + class PxsMaterialManagerIterator + { + + public: + PxsMaterialManagerIterator(PxsMaterialManager& manager) : mManager(manager), mIndex(0) + { + } + + bool getNextMaterial(PxsMaterialCore*& materialCore) + { + const PxU32 maxSize = mManager.getMaxSize(); + PxU32 index = mIndex; + while(index < maxSize && mManager.getMaterial(index)->getMaterialIndex() == MATERIAL_INVALID_HANDLE) + index++; + materialCore = NULL; + if(index < maxSize) + materialCore = mManager.getMaterial(index++); + mIndex = index; + return materialCore!=NULL; + } + + private: + PxsMaterialManagerIterator& operator=(const PxsMaterialManagerIterator&); + PxsMaterialManager& mManager; + PxU32 mIndex; + }; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h b/src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h new file mode 100644 index 000000000..524e26a8f --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_CONFIG_H +#define PXD_CONFIG_H + +/*! \file internal top level include file for lowlevel. */ + +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" + +/************************************************************************/ +/* Compiler workarounds */ +/************************************************************************/ +#if PX_VC +#pragma warning(disable: 4355 ) // "this" used in base member initializer list +#pragma warning(disable: 4146 ) // unary minus operator applied to unsigned type. +#endif + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h b/src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h new file mode 100644 index 000000000..edaff42e3 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXV_DYNAMICS_H +#define PXV_DYNAMICS_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "foundation/PxSimpleTypes.h" +#include "PsIntrinsics.h" +#include "PxRigidDynamic.h" + +namespace physx +{ + +/*! +\file +Dynamics interface. +*/ + +/************************************************************************/ +/* Atoms */ +/************************************************************************/ + +class PxsContext; +class PxsRigidBody; +class PxShape; +class PxGeometry; +struct PxsShapeCore; + + +struct PxsRigidCore +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PxsRigidCore() : mFlags(0), mIdtBody2Actor(0), solverIterationCounts(0) {} + PxsRigidCore(const PxEMPTY) : mFlags(PxEmpty) {} + + PX_ALIGN_PREFIX(16) + PxTransform body2World PX_ALIGN_SUFFIX(16); + PxRigidBodyFlags mFlags; // API body flags + PxU8 mIdtBody2Actor; // PT: true if PxsBodyCore::body2Actor is identity + PxU16 solverIterationCounts; //vel iters are in low word and pos iters in high word. + + PX_FORCE_INLINE PxU32 isKinematic() const + { + return mFlags & PxRigidBodyFlag::eKINEMATIC; + } + + PX_FORCE_INLINE PxU32 hasCCD() const + { + return mFlags & PxRigidBodyFlag::eENABLE_CCD; + } + + PX_FORCE_INLINE PxU32 hasCCDFriction() const + { + return mFlags & PxRigidBodyFlag::eENABLE_CCD_FRICTION; + } +}; +PX_COMPILE_TIME_ASSERT(sizeof(PxsRigidCore) == 32); + + +struct PxsBodyCore: public PxsRigidCore +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PxsBodyCore() : PxsRigidCore() {} + PxsBodyCore(const PxEMPTY) : PxsRigidCore(PxEmpty) {} + + PX_FORCE_INLINE const PxTransform& getBody2Actor() const { return body2Actor; } + PX_FORCE_INLINE void setBody2Actor(const PxTransform& t) + { + mIdtBody2Actor = PxU8(t.p.isZero() && t.q.isIdentity()); + + body2Actor = t; + } + protected: + PxTransform body2Actor; + public: + PxReal ccdAdvanceCoefficient; //64 + + PxVec3 linearVelocity; + PxReal maxPenBias; + + PxVec3 angularVelocity; + PxReal contactReportThreshold; //96 + + PxReal maxAngularVelocitySq; + PxReal maxLinearVelocitySq; + PxReal linearDamping; + PxReal angularDamping; //112 + + PxVec3 inverseInertia; + PxReal inverseMass; //128 + + PxReal maxContactImpulse; + PxReal sleepThreshold; + PxReal freezeThreshold; + PxReal wakeCounter; //144 this is authoritative wakeCounter + + PxReal solverWakeCounter; //this is calculated by the solver when it performs sleepCheck. It is committed to wakeCounter in ScAfterIntegrationTask if the body is still awake. + PxU32 numCountedInteractions; + PxU32 numBodyInteractions; //Used by adaptive force to keep track of the total number of body interactions + PxU16 isFastMoving; //This could be a single bit but it's a u16 at the moment for simplicity's sake + PxRigidDynamicLockFlags lockFlags; //160 This could be a u8 but it is a u16 for simplicity's sake. All fits into 16 byte alignment + + PX_FORCE_INLINE bool shouldCreateContactReports() const + { + const PxU32* binary = reinterpret_cast(&contactReportThreshold); + return *binary != 0x7f7fffff; // PX_MAX_REAL + } +}; + +PX_COMPILE_TIME_ASSERT(sizeof(PxsBodyCore) == 160); + + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h b/src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h new file mode 100644 index 000000000..aef2d999b --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXV_GEOMETRY_H +#define PXV_GEOMETRY_H + +#include "foundation/PxTransform.h" +#include "PxvConfig.h" + +namespace physx +{ + +class PxsRigidBody; + +namespace Gu +{ + struct ConvexHullData; + class TriangleMesh; + struct HeightFieldData; +} + +} + +/*! +\file +Geometry interface +*/ + +/************************************************************************/ +/* Shapes */ +/************************************************************************/ + +// moved to +#include "GuGeometryUnion.h" + +namespace physx +{ + +struct PxsShapeCore +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + +// PX_SERIALIZATION + PxsShapeCore() {} + PxsShapeCore(const PxEMPTY) : geometry(PxEmpty) {} +//~PX_SERIALIZATION + + PX_ALIGN_PREFIX(16) + PxTransform transform PX_ALIGN_SUFFIX(16); + PxReal contactOffset; + PxU8 mShapeFlags; // !< API shape flags // PT: TODO: use PxShapeFlags here. Needs to move flags to separate file. + PxU8 mOwnsMaterialIdxMemory; // PT: for de-serialization to avoid deallocating material index list. Moved there from Sc::ShapeCore (since one byte was free). + PxU16 materialIndex; + Gu::GeometryUnion geometry; +}; + +PX_COMPILE_TIME_ASSERT( (sizeof(PxsShapeCore)&0xf) == 0); + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h b/src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h new file mode 100644 index 000000000..de321befc --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h @@ -0,0 +1,116 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_INIT_H +#define PXD_INIT_H + +#include "PxvConfig.h" +#include "PsBasicTemplates.h" + +namespace physx +{ + +/*! +\file +PhysX Low-level, Memory management +*/ + +/************************************************************************/ +/* Error Handling */ +/************************************************************************/ + + +enum PxvErrorCode +{ + PXD_ERROR_NO_ERROR = 0, + PXD_ERROR_INVALID_PARAMETER, + PXD_ERROR_INVALID_PARAMETER_SIZE, + PXD_ERROR_INTERNAL_ERROR, + PXD_ERROR_NOT_IMPLEMENTED, + PXD_ERROR_NO_CONTEXT, + PXD_ERROR_NO_TASK_MANAGER, + PXD_ERROR_WARNING +}; + +class PxShape; +class PxRigidActor; +struct PxsShapeCore; +struct PxsRigidCore; + +struct PxvOffsetTable +{ + PX_FORCE_INLINE PxvOffsetTable() {} + + PX_FORCE_INLINE const PxShape* convertPxsShape2Px(const PxsShapeCore* pxs) const + { + return shdfnd::pointerOffset(pxs, pxsShapeCore2PxShape); + } + + PX_FORCE_INLINE const PxRigidActor* convertPxsRigidCore2PxRigidBody(const PxsRigidCore* pxs) const + { + return shdfnd::pointerOffset(pxs, pxsRigidCore2PxRigidBody); + } + + PX_FORCE_INLINE const PxRigidActor* convertPxsRigidCore2PxRigidStatic(const PxsRigidCore* pxs) const + { + return shdfnd::pointerOffset(pxs, pxsRigidCore2PxRigidStatic); + } + + ptrdiff_t pxsShapeCore2PxShape; + ptrdiff_t pxsRigidCore2PxRigidBody; + ptrdiff_t pxsRigidCore2PxRigidStatic; +}; +extern PxvOffsetTable gPxvOffsetTable; + +/*! +Initialize low-level implementation. +*/ + +void PxvInit(const PxvOffsetTable& offsetTable); + + +/*! +Shut down low-level implementation. +*/ +void PxvTerm(); + +/*! +Initialize low-level implementation. +*/ + +void PxvRegisterHeightFields(); + +#if PX_SUPPORT_GPU_PHYSX +class PxPhysXGpu* PxvGetPhysXGpu(bool createIfNeeded); +#endif + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvManager.h b/src/PhysX/physx/source/lowlevel/api/include/PxvManager.h new file mode 100644 index 000000000..685340ff9 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvManager.h @@ -0,0 +1,239 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXV_MANAGER_H +#define PXV_MANAGER_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMemory.h" +#include "PxvConfig.h" +#include "PxvGeometry.h" + +namespace physx +{ + +class PxvContact; + +/*! +\file +Manager interface +*/ + +/************************************************************************/ +/* Managers */ +/************************************************************************/ + +class PxsContactManager; +class PxsContext; + +struct PxsRigidCore; +struct PxsShapeCore; + + + +/*! +Type of PXD_MANAGER_CCD_MODE property +*/ +enum PxvContactManagerCCDMode +{ + PXD_MANAGER_CCD_NONE, + PXD_MANAGER_CCD_LINEAR +}; + + +/*! +Manager descriptor +*/ +struct PxvManagerDescRigidRigid +{ + /*! + Manager user data + + \sa PXD_MANAGER_USER_DATA + */ + //void* userData; + + /*! + Dominance setting for one way interactions. + A dominance of 0 means the corresp. body will + not be pushable by the other body in the constraint. + \sa PXD_MANAGER_DOMINANCE0 + */ + PxU8 dominance0; + + /*! + Dominance setting for one way interactions. + A dominance of 0 means the corresp. body will + not be pushable by the other body in the constraint. + \sa PXD_MANAGER_DOMINANCE1 + */ + PxU8 dominance1; + + /*! + PxsRigidBodies + */ + PxsRigidBody* rigidBody0; + PxsRigidBody* rigidBody1; + + /*! + Shape Core structures + */ + + const PxsShapeCore* shapeCore0; + const PxsShapeCore* shapeCore1; + + /*! + Body Core structures + */ + + PxsRigidCore* rigidCore0; + PxsRigidCore* rigidCore1; + + /*! + Enable contact information reporting. + + */ + int reportContactInfo; + + /*! + Enable contact impulse threshold reporting. + + */ + int hasForceThreshold; + + /*! + Enable generated contacts to be changeable + + */ + int contactChangeable; + + /*! + Disable strong friction + + */ + //int disableStrongFriction; + + /*! + Contact resolution rest distance. + + */ + PxReal restDistance; + + /*! + Disable contact response + + */ + int disableResponse; + + /*! + Disable discrete contact generation + + */ + int disableDiscreteContact; + + /*! + Disable CCD contact generation + + */ + int disableCCDContact; + + /*! + Is connected to an articulation (1 - first body, 2 - second body) + + */ + int hasArticulations; + + /*! + is connected to a dynamic (1 - first body, 2 - second body) + */ + int hasDynamics; + + /*! + Is the pair touching? Use when re-creating the manager with prior knowledge about touch status. + + positive: pair is touching + 0: touch state unknown (this is a new pair) + negative: pair is not touching + + Default is 0 + */ + int hasTouch; + + /*! + Identifies whether body 1 is kinematic. We can treat kinematics as statics and embed velocity into constraint + because kinematic bodies' velocities will not change + */ + bool body1Kinematic; + + /* + Index entries into the transform cache for shape 0 + */ + + PxU32 transformCache0; + + /* + Index entries into the transform cache for shape 1 + */ + + PxU32 transformCache1; + + + PxvManagerDescRigidRigid() + { + PxMemSet(this, 0, sizeof(PxvManagerDescRigidRigid)); + + dominance0 = 1u; + dominance1 = 1u; + } +}; + + +/*! +Report struct for contact manager touch reports +*/ +struct PxvContactManagerTouchEvent +{ + /*! + Manager handle + */ + PxsContactManager* manager; + + /*! + Manager userdata + */ + void* userData; +}; + +} + +#endif + diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h b/src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h new file mode 100644 index 000000000..530574b3a --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h @@ -0,0 +1,114 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXV_SIM_STATS_H +#define PXV_SIM_STATS_H + +#include "foundation/PxAssert.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "PxGeometry.h" + +namespace physx +{ + +/*! +\file +Context handling +*/ + +/************************************************************************/ +/* Context handling, types */ +/************************************************************************/ + +/*! +Description: contains statistics for the simulation. +*/ +struct PxvSimStats +{ + PxvSimStats() { clearAll(); } + void clearAll() { PxMemZero(this, sizeof(PxvSimStats)); } // set counters to zero + + PX_FORCE_INLINE void incCCDPairs(PxGeometryType::Enum g0, PxGeometryType::Enum g1) + { + PX_ASSERT(g0 <= g1); // That's how they should be sorted + mNbCCDPairs[g0][g1]++; + } + + PX_FORCE_INLINE void decCCDPairs(PxGeometryType::Enum g0, PxGeometryType::Enum g1) + { + PX_ASSERT(g0 <= g1); // That's how they should be sorted + PX_ASSERT(mNbCCDPairs[g0][g1]); + mNbCCDPairs[g0][g1]--; + } + + PX_FORCE_INLINE void incModifiedContactPairs(PxGeometryType::Enum g0, PxGeometryType::Enum g1) + { + PX_ASSERT(g0 <= g1); // That's how they should be sorted + mNbModifiedContactPairs[g0][g1]++; + } + + PX_FORCE_INLINE void decModifiedContactPairs(PxGeometryType::Enum g0, PxGeometryType::Enum g1) + { + PX_ASSERT(g0 <= g1); // That's how they should be sorted + PX_ASSERT(mNbModifiedContactPairs[g0][g1]); + mNbModifiedContactPairs[g0][g1]--; + } + + // PT: those guys are now persistent and shouldn't be cleared each frame + PxU32 mNbDiscreteContactPairs [PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 mNbCCDPairs [PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + + PxU32 mNbModifiedContactPairs [PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + + PxU32 mNbDiscreteContactPairsTotal; // PT: sum of mNbDiscreteContactPairs, i.e. number of pairs reaching narrow phase + PxU32 mNbDiscreteContactPairsWithCacheHits; + PxU32 mNbDiscreteContactPairsWithContacts; + PxU32 mNbActiveConstraints; + PxU32 mNbActiveDynamicBodies; + PxU32 mNbActiveKinematicBodies; + + PxU32 mNbAxisSolverConstraints; + PxU32 mTotalCompressedContactSize; + PxU32 mTotalConstraintSize; + PxU32 mPeakConstraintBlockAllocations; + + PxU32 mNbNewPairs; + PxU32 mNbLostPairs; + + PxU32 mNbNewTouches; + PxU32 mNbLostTouches; + + PxU32 mNbPartitions; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp b/src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp new file mode 100644 index 000000000..69603db69 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxvGlobals.h" +#include "PxsContext.h" +#include "PxcContactMethodImpl.h" +#include "GuContactMethodImpl.h" + + +#if PX_SUPPORT_GPU_PHYSX +#include "PxPhysXGpu.h" + +physx::PxPhysXGpu* gPxPhysXGpu; +#endif + +namespace physx +{ + +PxvOffsetTable gPxvOffsetTable; + +bool PxcPCMContactSphereHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcPCMContactCapsuleHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcPCMContactBoxHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcPCMContactConvexHeightField (GU_CONTACT_METHOD_ARGS); + +bool PxcContactSphereHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcContactCapsuleHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcContactBoxHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcContactConvexHeightField (GU_CONTACT_METHOD_ARGS); + +void PxvRegisterHeightFields() +{ + g_ContactMethodTable[PxGeometryType::eSPHERE][PxGeometryType::eHEIGHTFIELD] = PxcContactSphereHeightField; + g_ContactMethodTable[PxGeometryType::eCAPSULE][PxGeometryType::eHEIGHTFIELD] = PxcContactCapsuleHeightField; + g_ContactMethodTable[PxGeometryType::eBOX][PxGeometryType::eHEIGHTFIELD] = PxcContactBoxHeightField; + g_ContactMethodTable[PxGeometryType::eCONVEXMESH][PxGeometryType::eHEIGHTFIELD] = PxcContactConvexHeightField; + + g_PCMContactMethodTable[PxGeometryType::eSPHERE][PxGeometryType::eHEIGHTFIELD] = PxcPCMContactSphereHeightField; + g_PCMContactMethodTable[PxGeometryType::eCAPSULE][PxGeometryType::eHEIGHTFIELD] = PxcPCMContactCapsuleHeightField; + g_PCMContactMethodTable[PxGeometryType::eBOX][PxGeometryType::eHEIGHTFIELD] = PxcPCMContactBoxHeightField; + g_PCMContactMethodTable[PxGeometryType::eCONVEXMESH][PxGeometryType::eHEIGHTFIELD] = PxcPCMContactConvexHeightField; +} + +void PxvInit(const PxvOffsetTable& offsetTable) +{ +#if PX_SUPPORT_GPU_PHYSX + gPxPhysXGpu = NULL; +#endif + gPxvOffsetTable = offsetTable; +} + +void PxvTerm() +{ +#if PX_SUPPORT_GPU_PHYSX + if (gPxPhysXGpu) + { + gPxPhysXGpu->release(); + gPxPhysXGpu = NULL; + } +#endif +} + +} + +#if PX_SUPPORT_GPU_PHYSX +namespace physx +{ + //forward declare stuff from PxPhysXGpuModuleLoader.cpp + void PxLoadPhysxGPUModule(const char* appGUID); + typedef physx::PxPhysXGpu* (PxCreatePhysXGpu_FUNC)(); + extern PxCreatePhysXGpu_FUNC* g_PxCreatePhysXGpu_Func; + + PxPhysXGpu* PxvGetPhysXGpu(bool createIfNeeded) + { + if (!gPxPhysXGpu && createIfNeeded) + { +#ifdef PX_PHYSX_GPU_STATIC + gPxPhysXGpu = PxCreatePhysXGpu(); +#else + PxLoadPhysxGPUModule(NULL); + if (g_PxCreatePhysXGpu_Func) + { + gPxPhysXGpu = g_PxCreatePhysXGpu_Func(); + } +#endif + } + + return gPxPhysXGpu; + } +} +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h b/src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h new file mode 100644 index 000000000..1ca5dccbd --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXC_CONTACTMETHODIMPL_H +#define PXC_CONTACTMETHODIMPL_H + +#include "GuGeometryUnion.h" +#include "CmPhysXCommon.h" +#include "GuContactMethodImpl.h" + +namespace physx +{ +namespace Cm +{ + class RenderOutput; +} + +namespace Gu +{ + class ContactBuffer; + struct Cache; + struct NarrowPhaseParams; +} + +struct PxcNpCache; +class PxcNpThreadContext; +class PxsContext; +class PxsRigidBody; +struct PxsCCDShape; + +namespace Cm +{ + class FastVertex2ShapeScaling; +} + +/*!\file +This file contains forward declarations of all implemented contact methods. +*/ + + +/*! Parameter list without names to avoid unused parameter warnings +*/ +#define CONTACT_METHOD_ARGS_UNUSED \ + const Gu::GeometryUnion&, \ + const Gu::GeometryUnion&, \ + const PxTransform&, \ + const PxTransform&, \ + const Gu::NarrowPhaseParams&, \ + Gu::Cache&, \ + Gu::ContactBuffer&, \ + Cm::RenderOutput* + + +/*! +Method prototype for contact generation routines +*/ +typedef bool (*PxcContactMethod) (GU_CONTACT_METHOD_ARGS); + + +// Matrix of types +extern PxcContactMethod g_ContactMethodTable[][PxGeometryType::eGEOMETRY_COUNT]; +extern const bool g_CanUseContactCache[][PxGeometryType::eGEOMETRY_COUNT]; +extern PxcContactMethod g_PCMContactMethodTable[][PxGeometryType::eGEOMETRY_COUNT]; + +extern bool gEnablePCMCaching[][PxGeometryType::eGEOMETRY_COUNT]; +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcCCDStateStreamPair.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcCCDStateStreamPair.h new file mode 100644 index 000000000..5504ce468 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcCCDStateStreamPair.h @@ -0,0 +1,29 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h new file mode 100644 index 000000000..53f610bce --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h @@ -0,0 +1,166 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_CONSTRAINTBLOCKPOOL_H +#define PXC_CONSTRAINTBLOCKPOOL_H + +#include "PxvConfig.h" +#include "PsArray.h" +#include "PsMutex.h" +#include "PxcNpMemBlockPool.h" + +namespace physx +{ + +class PxsConstraintBlockManager +{ +public: + PxsConstraintBlockManager(PxcNpMemBlockPool & blockPool): + mBlockPool(blockPool) + { + } + + + PX_FORCE_INLINE void reset() + { + mBlockPool.releaseConstraintBlocks(mTrackingArray); + } + + + PxcNpMemBlockArray mTrackingArray; + PxcNpMemBlockPool& mBlockPool; + +private: + PxsConstraintBlockManager& operator=(const PxsConstraintBlockManager&); +}; + +class PxcConstraintBlockStream +{ + PX_NOCOPY(PxcConstraintBlockStream) +public: + PxcConstraintBlockStream(PxcNpMemBlockPool & blockPool): + mBlockPool(blockPool), + mBlock(NULL), + mUsed(0) + { + } + + PX_FORCE_INLINE PxU8* reserve(PxU32 size, PxsConstraintBlockManager& manager) + { + size = (size+15)&~15; + if(size>PxcNpMemBlock::SIZE) + return mBlockPool.acquireExceptionalConstraintMemory(size); + + if(mBlock == NULL || size+mUsed>PxcNpMemBlock::SIZE) + { + mBlock = mBlockPool.acquireConstraintBlock(manager.mTrackingArray); + PX_ASSERT(0==mBlock || mBlock->data == reinterpret_cast(mBlock)); + mUsed = size; + return reinterpret_cast(mBlock); + } + PX_ASSERT(mBlock && mBlock->data == reinterpret_cast(mBlock)); + PxU8* PX_RESTRICT result = mBlock->data+mUsed; + mUsed += size; + return result; + } + + PX_FORCE_INLINE void reset() + { + mBlock = NULL; + mUsed = 0; + } + + PX_FORCE_INLINE PxcNpMemBlockPool& getMemBlockPool() + { + return mBlockPool; + } + +private: + PxcNpMemBlockPool& mBlockPool; + PxcNpMemBlock* mBlock; // current constraint block + PxU32 mUsed; // number of bytes used in constraint block + //Tracking peak allocations + PxU32 mPeakUsed; +}; + +class PxcContactBlockStream +{ + PX_NOCOPY(PxcContactBlockStream) +public: + PxcContactBlockStream(PxcNpMemBlockPool & blockPool): + mBlockPool(blockPool), + mBlock(NULL), + mUsed(0) + { + } + + PX_FORCE_INLINE PxU8* reserve(PxU32 size) + { + size = (size+15)&~15; + + if(size>PxcNpMemBlock::SIZE) + return mBlockPool.acquireExceptionalConstraintMemory(size); + + PX_ASSERT(size <= PxcNpMemBlock::SIZE); + + if(mBlock == NULL || size+mUsed>PxcNpMemBlock::SIZE) + { + mBlock = mBlockPool.acquireContactBlock(); + PX_ASSERT(0==mBlock || mBlock->data == reinterpret_cast(mBlock)); + mUsed = size; + return reinterpret_cast(mBlock); + } + PX_ASSERT(mBlock && mBlock->data == reinterpret_cast(mBlock)); + PxU8* PX_RESTRICT result = mBlock->data+mUsed; + mUsed += size; + return result; + } + + PX_FORCE_INLINE void reset() + { + mBlock = NULL; + mUsed = 0; + } + + PX_FORCE_INLINE PxcNpMemBlockPool& getMemBlockPool() + { + return mBlockPool; + } + +private: + PxcNpMemBlockPool& mBlockPool; + PxcNpMemBlock* mBlock; // current constraint block + PxU32 mUsed; // number of bytes used in constraint block +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h new file mode 100644 index 000000000..d8deb405d --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXC_CONTACT_CACHE_H +#define PXC_CONTACT_CACHE_H + +#include "foundation/PxTransform.h" +#include "PxvConfig.h" +#include "PxcContactMethodImpl.h" + +namespace physx +{ + bool PxcCacheLocalContacts( PxcNpThreadContext& context, Gu::Cache& pairContactCache, + const PxTransform& tm0, const PxTransform& tm1, + const PxcContactMethod conMethod, + const Gu::GeometryUnion& shape0, const Gu::GeometryUnion& shape1); + + struct PxcLocalContactsCache + { + PxTransform mTransform0; + PxTransform mTransform1; + PxU16 mNbCachedContacts; + bool mUseFaceIndices; + bool mSameNormal; + + PX_FORCE_INLINE void operator = (const PxcLocalContactsCache& other) + { + mTransform0 = other.mTransform0; + mTransform1 = other.mTransform1; + mNbCachedContacts = other.mNbCachedContacts; + mUseFaceIndices = other.mUseFaceIndices; + mSameNormal = other.mSameNormal; + } + }; + +} + +#endif // PXC_CONTACT_CACHE_H diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h new file mode 100644 index 000000000..041ef0711 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXC_MATERIALMETHOD_H +#define PXC_MATERIALMETHOD_H + +#include "CmPhysXCommon.h" +#include "PxGeometry.h" + +namespace physx +{ + +struct PxsShapeCore; +struct PxsMaterialInfo; +class PxcNpThreadContext; + +#define MATERIAL_METHOD_ARGS \ + const PxsShapeCore* shape0, \ + const PxsShapeCore* shape1, \ + PxcNpThreadContext& pairContext, \ + PxsMaterialInfo* materialInfo + + +#define SINGLE_MATERIAL_METHOD_ARGS \ + const PxsShapeCore* shape, \ + const PxU32 index, \ + PxcNpThreadContext& pairContext, \ + PxsMaterialInfo* materialInfo + +/*! +Method prototype for fetch material routines +*/ +typedef bool (*PxcGetMaterialMethod) (MATERIAL_METHOD_ARGS); + +typedef bool (*PxcGetSingleMaterialMethod) (SINGLE_MATERIAL_METHOD_ARGS); + +extern PxcGetMaterialMethod g_GetMaterialMethodTable[][PxGeometryType::eGEOMETRY_COUNT]; + +extern PxcGetSingleMaterialMethod g_GetSingleMaterialMethodTable[PxGeometryType::eGEOMETRY_COUNT]; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h new file mode 100644 index 000000000..1ccb09358 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h @@ -0,0 +1,65 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXC_NP_BATCH_H +#define PXC_NP_BATCH_H + +#include "PxvConfig.h" + +namespace physx +{ + struct PxcNpWorkUnit; + class PxcNpThreadContext; + +struct PxcNpWorkUnit; +class PxsContactManager; +struct PxsContactManagerOutput; + +namespace Gu +{ + struct Cache; +} + +namespace Cm +{ + class FlushPool; +} + +class PxLightCpuTask; + +namespace Gu +{ + class PxgGpuNarrowphaseCoreInterface; +} + +void PxcDiscreteNarrowPhase(PxcNpThreadContext& context, const PxcNpWorkUnit& cmInput, Gu::Cache& cache, PxsContactManagerOutput& output); +void PxcDiscreteNarrowPhasePCM(PxcNpThreadContext& context, const PxcNpWorkUnit& cmInput, Gu::Cache& cache, PxsContactManagerOutput& output); +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h new file mode 100644 index 000000000..ed8048b68 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h @@ -0,0 +1,154 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXC_NPCACHE_H +#define PXC_NPCACHE_H + +#include "foundation/PxMemory.h" + +#include "PsIntrinsics.h" +#include "PxcNpCacheStreamPair.h" + +#include "PsPool.h" +#include "PsFoundation.h" +#include "GuContactMethodImpl.h" +#include "PsUtilities.h" + +namespace physx +{ + +template +void PxcNpCacheWrite(PxcNpCacheStreamPair& streams, + Gu::Cache& cache, + const T& payload, + PxU32 bytes, + const PxU8* data) +{ + const PxU32 payloadSize = (sizeof(payload)+3)&~3; + cache.mCachedSize = Ps::to16((payloadSize + 4 + bytes + 0xF)&~0xF); + + PxU8* ls = streams.reserve(cache.mCachedSize); + cache.mCachedData = ls; + if(ls==NULL || (reinterpret_cast(-1))==ls) + { + if(ls==NULL) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for narrow phase. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + return; + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in narrowphase. " + "Either accept dropped contacts or simplify collision geometry."); + cache.mCachedData = NULL; + ls = NULL; + return; + } + } + + *reinterpret_cast(ls) = payload; + *reinterpret_cast(ls+payloadSize) = bytes; + if(data) + PxMemCopy(ls+payloadSize+sizeof(PxU32), data, bytes); +} + + +template +PxU8* PxcNpCacheWriteInitiate(PxcNpCacheStreamPair& streams, Gu::Cache& cache, const T& payload, PxU32 bytes) +{ + PX_UNUSED(payload); + + const PxU32 payloadSize = (sizeof(payload)+3)&~3; + cache.mCachedSize = Ps::to16((payloadSize + 4 + bytes + 0xF)&~0xF); + + PxU8* ls = streams.reserve(cache.mCachedSize); + cache.mCachedData = ls; + if(NULL==ls || reinterpret_cast(-1)==ls) + { + if(NULL==ls) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for narrow phase. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in narrowphase. " + "Either accept dropped contacts or simplify collision geometry."); + cache.mCachedData = NULL; + ls = NULL; + } + } + return ls; +} + +template +PX_FORCE_INLINE void PxcNpCacheWriteFinalize(PxU8* ls, const T& payload, PxU32 bytes, const PxU8* data) +{ + const PxU32 payloadSize = (sizeof(payload)+3)&~3; + *reinterpret_cast(ls) = payload; + *reinterpret_cast(ls+payloadSize) = bytes; + if(data) + PxMemCopy(ls+payloadSize+sizeof(PxU32), data, bytes); +} + + +template +PX_FORCE_INLINE PxU8* PxcNpCacheRead(Gu::Cache& cache, T*& payload) +{ + PxU8* ls = cache.mCachedData; + payload = reinterpret_cast(ls); + const PxU32 payloadSize = (sizeof(T)+3)&~3; + return reinterpret_cast(ls+payloadSize+sizeof(PxU32)); +} + +template +const PxU8* PxcNpCacheRead2(Gu::Cache& cache, T& payload, PxU32& bytes) +{ + const PxU8* ls = cache.mCachedData; + if(ls==NULL) + { + bytes = 0; + return NULL; + } + + const PxU32 payloadSize = (sizeof(payload)+3)&~3; + payload = *reinterpret_cast(ls); + bytes = *reinterpret_cast(ls+payloadSize); + PX_ASSERT(cache.mCachedSize == ((payloadSize + 4 + bytes+0xF)&~0xF)); + return reinterpret_cast(ls+payloadSize+sizeof(PxU32)); +} + +} + +#endif // #ifndef PXC_NPCACHE_H diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h new file mode 100644 index 000000000..ba7da39d2 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_NPCACHESTREAMPAIR_H +#define PXC_NPCACHESTREAMPAIR_H + +#include "foundation/PxSimpleTypes.h" +#include "PxvConfig.h" +#include "PxcNpMemBlockPool.h" + +namespace physx +{ + +static const PxU32 PXC_NPCACHE_BLOCK_SIZE = 16384; + + +struct PxcNpCacheStreamPair +{ +public: + PxcNpCacheStreamPair(PxcNpMemBlockPool& blockPool); + + // reserve can fail and return null. + PxU8* reserve(PxU32 byteCount); + void reset(); +private: + PxcNpMemBlockPool& mBlockPool; + PxcNpMemBlock* mBlock; + PxU32 mUsed; +private: + PxcNpCacheStreamPair& operator=(const PxcNpCacheStreamPair&); +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h new file mode 100644 index 000000000..5683b0d1b --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXC_NPCONTACTPREPSHARED_H +#define PXC_NPCONTACTPREPSHARED_H + +namespace physx +{ +class PxcNpThreadContext; +struct PxsMaterialInfo; +class PxsMaterialManager; +class PxsConstraintBlockManager; +class PxcConstraintBlockStream; +struct PxsContactManagerOutput; + +namespace Gu +{ + struct ContactPoint; +} + +static const PxReal PXC_SAME_NORMAL = 0.999f; //Around 6 degrees + +PxU32 writeCompressedContact(const Gu::ContactPoint* const PX_RESTRICT contactPoints, const PxU32 numContactPoints, PxcNpThreadContext* threadContext, + PxU8& writtenContactCount, PxU8*& outContactPatches, PxU8*& outContactPoints, PxU16& compressedContactSize, PxReal*& contactForces, PxU32 contactForceByteSize, + const PxsMaterialManager* materialManager, bool hasModifiableContacts, bool forceNoResponse, PxsMaterialInfo* PX_RESTRICT pMaterial, PxU8& numPatches, + PxU32 additionalHeaderSize = 0, PxsConstraintBlockManager* manager = NULL, PxcConstraintBlockStream* blockStream = NULL, bool insertAveragePoint = false, + PxcDataStreamPool* pool = NULL, PxcDataStreamPool* patchStreamPool = NULL, PxcDataStreamPool* forcePool = NULL, const bool isMeshType = false); + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h new file mode 100644 index 000000000..bfe4debf4 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h @@ -0,0 +1,119 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_NP_MEM_BLOCK_POOL_H +#define PXC_NP_MEM_BLOCK_POOL_H + +#include "PxvConfig.h" +#include "PsArray.h" +#include "PxcScratchAllocator.h" + +namespace physx +{ +struct PxcNpMemBlock +{ + enum + { + SIZE = 16384 + }; + PxU8 data[SIZE]; +}; + +typedef Ps::Array PxcNpMemBlockArray; + +class PxcNpMemBlockPool +{ + PX_NOCOPY(PxcNpMemBlockPool) +public: + PxcNpMemBlockPool(PxcScratchAllocator& allocator); + ~PxcNpMemBlockPool(); + + void init(PxU32 initial16KDataBlocks, PxU32 maxBlocks); + void flush(); + void setBlockCount(PxU32 count); + PxU32 getUsedBlockCount() const; + PxU32 getMaxUsedBlockCount() const; + PxU32 getPeakConstraintBlockCount() const; + void releaseUnusedBlocks(); + + PxcNpMemBlock* acquireConstraintBlock(); + PxcNpMemBlock* acquireConstraintBlock(PxcNpMemBlockArray& memBlocks); + PxcNpMemBlock* acquireContactBlock(); + PxcNpMemBlock* acquireFrictionBlock(); + PxcNpMemBlock* acquireNpCacheBlock(); + + PxU8* acquireExceptionalConstraintMemory(PxU32 size); + + void acquireConstraintMemory(); + void releaseConstraintMemory(); + void releaseConstraintBlocks(PxcNpMemBlockArray& memBlocks); + void releaseContacts(); + void swapFrictionStreams(); + void swapNpCacheStreams(); + + void flushUnused(); + +private: + + + Ps::Mutex mLock; + PxcNpMemBlockArray mConstraints; + PxcNpMemBlockArray mContacts[2]; + PxcNpMemBlockArray mFriction[2]; + PxcNpMemBlockArray mNpCache[2]; + PxcNpMemBlockArray mScratchBlocks; + Ps::Array mExceptionalConstraints; + + PxcNpMemBlockArray mUnused; + + PxU32 mNpCacheActiveStream; + PxU32 mFrictionActiveStream; + PxU32 mCCDCacheActiveStream; + PxU32 mContactIndex; + PxU32 mAllocatedBlocks; + PxU32 mMaxBlocks; + PxU32 mInitialBlocks; + PxU32 mUsedBlocks; + PxU32 mMaxUsedBlocks; + PxcNpMemBlock* mScratchBlockAddr; + PxU32 mNbScratchBlocks; + PxcScratchAllocator& mScratchAllocator; + + PxU32 mPeakConstraintAllocations; + PxU32 mConstraintAllocations; + + PxcNpMemBlock* acquire(PxcNpMemBlockArray& trackingArray, PxU32* allocationCount = NULL, PxU32* peakAllocationCount = NULL, bool isScratchAllocation = false); + void release(PxcNpMemBlockArray& deadArray, PxU32* allocationCount = NULL); +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h new file mode 100644 index 000000000..3a3bca94c --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h @@ -0,0 +1,211 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXC_NPTHREADCONTEXT_H +#define PXC_NPTHREADCONTEXT_H + +#include "PxvConfig.h" +#include "CmScaling.h" +#include "CmRenderOutput.h" +#include "PxcNpCacheStreamPair.h" +#include "PxcConstraintBlockStream.h" +#include "GuContactBuffer.h" +#include "PxcThreadCoherentCache.h" +#include "PxGeometry.h" +#include "CmBitMap.h" +#include "../pcm/GuPersistentContactManifold.h" + +namespace physx +{ + +class PxsTransformCache; +class PxsMaterialManager; + +namespace Sc +{ + class BodySim; +} + +/*! +Per-thread context used by contact generation routines. +*/ + +struct PxcDataStreamPool +{ + PxU8* mDataStream; + PxI32 mSharedDataIndex; + PxU32 mDataStreamSize; + PxU32 mSharedDataIndexGPU; + + bool isOverflown() const + { + //FD: my expectaton is that reading those variables is atomic, shared indices are non-decreasing, + //so we can only get a false overflow alert because of concurrency issues, which is not a big deal as it means + //it did overflow a bit later + return mSharedDataIndex + mSharedDataIndexGPU >= mDataStreamSize; + } +}; + +struct PxcNpContext +{ + private: + PX_NOCOPY(PxcNpContext) + public: + + PxcNpContext() : + mNpMemBlockPool (mScratchAllocator), + mMeshContactMargin (0.0f), + mToleranceLength (0.0f), + mCreateContactStream (false), + mContactStreamPool (NULL), + mPatchStreamPool (NULL), + mForceAndIndiceStreamPool(NULL), + mMaterialManager (NULL) + { + } + + PxcScratchAllocator mScratchAllocator; + PxcNpMemBlockPool mNpMemBlockPool; + PxReal mMeshContactMargin; + PxReal mToleranceLength; + Cm::RenderBuffer mRenderBuffer; + bool mCreateContactStream; // flag to enforce that contacts are stored persistently per workunit. Used for PVD. + PxcDataStreamPool* mContactStreamPool; + PxcDataStreamPool* mPatchStreamPool; + PxcDataStreamPool* mForceAndIndiceStreamPool; + PxcDataStreamPool* mConstraintWriteBackStreamPool; + PxsMaterialManager* mMaterialManager; + + PX_FORCE_INLINE PxReal getToleranceLength() const { return mToleranceLength; } + PX_FORCE_INLINE void setToleranceLength(PxReal x) { mToleranceLength = x; } + PX_FORCE_INLINE PxReal getMeshContactMargin() const { return mMeshContactMargin; } + PX_FORCE_INLINE void setMeshContactMargin(PxReal x) { mMeshContactMargin = x; } + PX_FORCE_INLINE bool getCreateContactStream() { return mCreateContactStream; } + + PX_FORCE_INLINE PxcNpMemBlockPool& getNpMemBlockPool() { return mNpMemBlockPool; } + PX_FORCE_INLINE const PxcNpMemBlockPool& getNpMemBlockPool() const { return mNpMemBlockPool; } + PX_FORCE_INLINE void setMaterialManager(PxsMaterialManager* m){ mMaterialManager = m; } + PX_FORCE_INLINE PxsMaterialManager* getMaterialManager() const { return mMaterialManager; } + + Cm::RenderOutput getRenderOutput() { return Cm::RenderOutput(mRenderBuffer); } +}; + +class PxcNpThreadContext : public PxcThreadCoherentCache::EntryBase +{ + PX_NOCOPY(PxcNpThreadContext) +public: + PxcNpThreadContext(PxcNpContext* params); + ~PxcNpThreadContext(); + +#if PX_ENABLE_SIM_STATS + void clearStats(); +#endif + + PX_FORCE_INLINE void setCreateContactStream(bool to) { mCreateContactStream = to; } + + PX_FORCE_INLINE void addLocalNewTouchCount(PxU32 newTouchCMCount) { mLocalNewTouchCount += newTouchCMCount; } + PX_FORCE_INLINE void addLocalLostTouchCount(PxU32 lostTouchCMCount) { mLocalLostTouchCount += lostTouchCMCount; } + PX_FORCE_INLINE PxU32 getLocalNewTouchCount() const { return mLocalNewTouchCount; } + PX_FORCE_INLINE PxU32 getLocalLostTouchCount() const { return mLocalLostTouchCount; } + + PX_FORCE_INLINE void addLocalFoundPatchCount(PxU32 foundPatchCount) { mLocalFoundPatchCount += foundPatchCount; } + PX_FORCE_INLINE void addLocalLostPatchCount(PxU32 lostPatchCount) { mLocalLostPatchCount += lostPatchCount; } + PX_FORCE_INLINE PxU32 getLocalFoundPatchCount() const { return mLocalFoundPatchCount; } + PX_FORCE_INLINE PxU32 getLocalLostPatchCount() const { return mLocalLostPatchCount; } + + PX_FORCE_INLINE Cm::BitMap& getLocalChangeTouch() { return mLocalChangeTouch; } + + PX_FORCE_INLINE Cm::BitMap& getLocalPatchChangeMap() { return mLocalPatchCountChange; } + + void reset(PxU32 cmCount); + // debugging + Cm::RenderOutput mRenderOutput; + + // dsequeira: Need to think about this block pool allocation a bit more. Ideally we'd be + // taking blocks from a single pool, except that we want to be able to selectively reclaim + // blocks if the user needs to defragment, depending on which artifacts they're willing + // to tolerate, such that the blocks we don't reclaim are contiguous. +#if PX_ENABLE_SIM_STATS + PxU32 mDiscreteContactPairs [PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 mModifiedContactPairs [PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; +#endif + PxcContactBlockStream mContactBlockStream; // constraint block pool + PxcNpCacheStreamPair mNpCacheStreamPair; // narrow phase pairwise data cache + + // Everything below here is scratch state. Most of it can even overlap. + + // temporary contact buffer + Gu::ContactBuffer mContactBuffer; + + PX_ALIGN(16, Gu::MultiplePersistentContactManifold mTempManifold); + + Gu::NarrowPhaseParams mNarrowPhaseParams; + + // DS: this stuff got moved here from the PxcNpPairContext. As Pierre says: + ////////// PT: those members shouldn't be there in the end, it's not necessary + Ps::Array mBodySimPool; + PxsTransformCache* mTransformCache; + PxReal* mContactDistance; + bool mPCM; + bool mContactCache; + bool mCreateContactStream; // flag to enforce that contacts are stored persistently per workunit. Used for PVD. + bool mCreateAveragePoint; // flag to enforce whether we create average points +#if PX_ENABLE_SIM_STATS + PxU32 mCompressedCacheSize; + PxU32 mNbDiscreteContactPairsWithCacheHits; + PxU32 mNbDiscreteContactPairsWithContacts; +#endif + PxReal mDt; // AP: still needed for ccd + PxU32 mCCDPass; + PxU32 mCCDFaceIndex; + + PxU32 mMaxPatches; + //PxU32 mTotalContactCount; + PxU32 mTotalCompressedCacheSize; + //PxU32 mTotalPatchCount; + + PxcDataStreamPool* mContactStreamPool; + PxcDataStreamPool* mPatchStreamPool; + PxcDataStreamPool* mForceAndIndiceStreamPool; //this stream is used to store the force buffer and triangle index if we are performing mesh/heightfield contact gen + PxcDataStreamPool* mConstraintWriteBackStreamPool; + PxsMaterialManager* mMaterialManager; +private: + // change touch handling. + Cm::BitMap mLocalChangeTouch; + Cm::BitMap mLocalPatchCountChange; + PxU32 mLocalNewTouchCount; + PxU32 mLocalLostTouchCount; + PxU32 mLocalFoundPatchCount; + PxU32 mLocalLostPatchCount; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h new file mode 100644 index 000000000..0cb68d584 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h @@ -0,0 +1,165 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_NPWORKUNIT_H +#define PXC_NPWORKUNIT_H + +#include "PxcNpThreadContext.h" +#include "PxcMaterialMethodImpl.h" +#include "PxcNpCache.h" + +namespace physx +{ + +class PxvContact; + +struct PxsRigidCore; +struct PxsShapeCore; + +class PxsMaterialManager; + +struct PxcNpWorkUnitFlag +{ + enum Enum + { + eOUTPUT_CONTACTS = 1, + eOUTPUT_CONSTRAINTS = 2, + eDISABLE_STRONG_FRICTION = 4, + eARTICULATION_BODY0 = 8, + eARTICULATION_BODY1 = 16, + eDYNAMIC_BODY0 = 32, + eDYNAMIC_BODY1 = 64, + eMODIFIABLE_CONTACT = 128, + eFORCE_THRESHOLD = 256, + eDETECT_DISCRETE_CONTACT = 512, + eHAS_KINEMATIC_ACTOR = 1024, + eDISABLE_RESPONSE = 2048, + eDETECT_CCD_CONTACTS = 4096 + }; +}; + +struct PxcNpWorkUnitStatusFlag +{ + enum Enum + { + eHAS_NO_TOUCH = (1 << 0), + eHAS_TOUCH = (1 << 1), + //eHAS_SOLVER_CONSTRAINTS = (1 << 2), + eREQUEST_CONSTRAINTS = (1 << 3), + eHAS_CCD_RETOUCH = (1 << 4), // Marks pairs that are touching at a CCD pass and were touching at discrete collision or at a previous CCD pass already + // but we can not tell whether they lost contact in a pass before. We send them as pure eNOTIFY_TOUCH_CCD events to the + // contact report callback if requested. + eDIRTY_MANAGER = (1 << 5), + eREFRESHED_WITH_TOUCH = (1 << 6), + eTOUCH_KNOWN = eHAS_NO_TOUCH | eHAS_TOUCH // The touch status is known (if narrowphase never ran for a pair then no flag will be set) + }; +}; + +/* + * A struct to record the number of work units a particular constraint pointer references. + * This is created at the beginning of the constriant data and is used to bypass constraint preparation when the + * bodies are not moving a lot. In this case, we can recycle the constraints and save ourselves some cycles. +*/ +struct PxcNpWorkUnit; +struct PxcNpWorkUnitBatch +{ + PxcNpWorkUnit* mUnits[4]; + PxU32 mSize; +}; + +struct PxcNpWorkUnit +{ + + const PxsRigidCore* rigidCore0; // INPUT //4 //8 + const PxsRigidCore* rigidCore1; // INPUT //8 //16 + + const PxsShapeCore* shapeCore0; // INPUT //12 //24 + const PxsShapeCore* shapeCore1; // INPUT //16 //32 + + PxU8* ccdContacts; // OUTPUT //20 //40 + + PxU8* frictionDataPtr; // INOUT //24 //48 + + PxU16 flags; // INPUT //26 //50 + PxU8 frictionPatchCount; // INOUT //27 //51 + PxU8 statusFlags; // OUTPUT (see PxcNpWorkUnitStatusFlag) //28 //52 + + PxU8 dominance0; // INPUT //29 //53 + PxU8 dominance1; // INPUT //30 //54 + PxU8 geomType0; // INPUT //31 //55 + PxU8 geomType1; // INPUT //32 //56 + + PxU32 index; // INPUT //36 //60 + + PxReal restDistance; // INPUT //40 //64 + + PxU32 mTransformCache0; // //44 //68 + PxU32 mTransformCache1; // //48 //72 + + PxU32 mEdgeIndex; //inout the island gen edge index //52 //76 + PxU32 mNpIndex; //INPUT //56 //80 + + PxReal mTorsionalPatchRadius; //60 //84 + PxReal mMinTorsionalPatchRadius; //64 //88 + +}; + +//#if !defined(PX_P64) +//PX_COMPILE_TIME_ASSERT(0 == (sizeof(PxcNpWorkUnit) & 0x0f)); +//#endif + +PX_FORCE_INLINE void PxcNpWorkUnitClearContactState(PxcNpWorkUnit& n) +{ + n.ccdContacts = NULL; +} + + +PX_FORCE_INLINE void PxcNpWorkUnitClearCachedState(PxcNpWorkUnit& n) +{ + n.frictionDataPtr = 0; + n.frictionPatchCount = 0; + n.ccdContacts = NULL; +} + +PX_FORCE_INLINE void PxcNpWorkUnitClearFrictionCachedState(PxcNpWorkUnit& n) +{ + n.frictionDataPtr = 0; + n.frictionPatchCount = 0; + n.ccdContacts = NULL; +} + +#if !defined(PX_P64) +//PX_COMPILE_TIME_ASSERT(sizeof(PxcNpWorkUnit)==128); +#endif + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h new file mode 100644 index 000000000..1f698d59e --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h @@ -0,0 +1,118 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXC_RIGIDBODY_H +#define PXC_RIGIDBODY_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "PxvDynamics.h" +#include "CmSpatialVector.h" + +namespace physx +{ + +class PxsContactManager; +struct PxsCCDPair; +struct PxsCCDBody; + +#define PX_INTERNAL_LOCK_FLAG_START 8 + +PX_ALIGN_PREFIX(16) +class PxcRigidBody +{ +public: + + enum PxcRigidBodyFlag + { + eFROZEN = 1 << 0, //This flag indicates that the stabilization is enabled and the body is + //"frozen". By "frozen", we mean that the body's transform is unchanged + //from the previous frame. This permits various optimizations. + eFREEZE_THIS_FRAME = 1 << 1, + eUNFREEZE_THIS_FRAME = 1 << 2, + eACTIVATE_THIS_FRAME = 1 << 3, + eDEACTIVATE_THIS_FRAME = 1 << 4, + eDISABLE_GRAVITY = 1 << 5, + eSPECULATIVE_CCD = 1 << 6, + //KS - copied here for GPU simulation to avoid needing to pass another set of flags around. + eLOCK_LINEAR_X = 1 << (PX_INTERNAL_LOCK_FLAG_START), + eLOCK_LINEAR_Y = 1 << (PX_INTERNAL_LOCK_FLAG_START + 1), + eLOCK_LINEAR_Z = 1 << (PX_INTERNAL_LOCK_FLAG_START + 2), + eLOCK_ANGULAR_X = 1 << (PX_INTERNAL_LOCK_FLAG_START + 3), + eLOCK_ANGULAR_Y = 1 << (PX_INTERNAL_LOCK_FLAG_START + 4), + eLOCK_ANGULAR_Z = 1 << (PX_INTERNAL_LOCK_FLAG_START + 5) + + + }; + + PX_FORCE_INLINE PxcRigidBody(PxsBodyCore* core) + : mLastTransform(core->body2World), + mCCD(NULL), + mCore(core) + { + } + + void adjustCCDLastTransform(); + +protected: + + ~PxcRigidBody() + { + } + +public: + + PxTransform mLastTransform; //28 (28) + + PxU16 mInternalFlags; //30 (30) + PxU16 solverIterationCounts; //32 (32) + + PxsCCDBody* mCCD; //36 (40) // only valid during CCD + + PxsBodyCore* mCore; //40 (48) + +#if !PX_P64_FAMILY + PxU32 alignmentPad[2]; //48 (48) +#endif + + PxVec3 sleepLinVelAcc; //60 (60) + PxReal freezeCount; //64 (64) + + PxVec3 sleepAngVelAcc; //76 (76) + PxReal accelScale; //80 (80) + + +} +PX_ALIGN_SUFFIX(16); +PX_COMPILE_TIME_ASSERT(0 == (sizeof(PxcRigidBody) & 0x0f)); + +} + +#endif //PXC_RIGIDBODY_H diff --git a/src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h new file mode 100644 index 000000000..70d918e88 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h @@ -0,0 +1,138 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXC_SCRATCHALLOCATOR_H +#define PXC_SCRATCHALLOCATOR_H + +#include "foundation/PxAssert.h" +#include "PxvConfig.h" +#include "PsMutex.h" +#include "PsArray.h" +#include "PsAllocator.h" + +namespace physx +{ +class PxcScratchAllocator +{ + PX_NOCOPY(PxcScratchAllocator) +public: + PxcScratchAllocator() : mStack(PX_DEBUG_EXP("PxcScratchAllocator")), mStart(NULL), mSize(0) + { + mStack.reserve(64); + mStack.pushBack(0); + } + + void setBlock(void* addr, PxU32 size) + { + // if the stack is not empty then some scratch memory was not freed on the previous frame. That's + // likely indicative of a problem, because when the scratch block is too small the memory will have + // come from the heap + + PX_ASSERT(mStack.size()==1); + mStack.popBack(); + + mStart = reinterpret_cast(addr); + mSize = size; + mStack.pushBack(mStart + size); + } + + void* allocAll(PxU32& size) + { + Ps::Mutex::ScopedLock lock(mLock); + PX_ASSERT(mStack.size()>0); + size = PxU32(mStack.back()-mStart); + + if(size==0) + return NULL; + + mStack.pushBack(mStart); + return mStart; + } + + + void* alloc(PxU32 requestedSize, bool fallBackToHeap = false) + { + requestedSize = (requestedSize+15)&~15; + + Ps::Mutex::ScopedLock lock(mLock); + PX_ASSERT(mStack.size()>=1); + + PxU8* top = mStack.back(); + + if(top - mStart >= ptrdiff_t(requestedSize)) + { + PxU8* addr = top - requestedSize; + mStack.pushBack(addr); + return addr; + } + + if(!fallBackToHeap) + return NULL; + + return PX_ALLOC(requestedSize, "Scratch Block Fallback"); + } + + void free(void* addr) + { + PX_ASSERT(addr!=NULL); + if(!isScratchAddr(addr)) + { + PX_FREE(addr); + return; + } + + Ps::Mutex::ScopedLock lock(mLock); + PX_ASSERT(mStack.size()>1); + + PxU32 i=mStack.size()-1; + while(mStack[i](addr); + return a>= mStart && a mStack; + PxU8* mStart; + PxU32 mSize; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h new file mode 100644 index 000000000..d375eff46 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h @@ -0,0 +1,150 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXC_THREADCOHERENTCACHE_H +#define PXC_THREADCOHERENTCACHE_H + +#include "PsMutex.h" +#include "PsAllocator.h" +#include "PsSList.h" + +namespace physx +{ + +class PxsContext; +/*! +Controls a pool of large objects which must be thread safe. +Tries to return the object most recently used by the thread(for better cache coherancy). +Assumes the object has a default contructor. + +(Note the semantics are different to a pool because we dont want to construct/destroy each time +an object is requested, which may be expensive). + +TODO: add thread coherancy. +*/ +template +class PxcThreadCoherentCache : public Ps::AlignedAllocator<16, Ps::ReflectionAllocator > +{ + typedef Ps::AlignedAllocator<16, Ps::ReflectionAllocator > Allocator; + PX_NOCOPY(PxcThreadCoherentCache) +public: + + typedef Ps::SListEntry EntryBase; + + PX_INLINE PxcThreadCoherentCache(Params* params, const Allocator& alloc = Allocator()) : Allocator(alloc), mParams(params) + { + } + + PX_INLINE ~PxcThreadCoherentCache() + { + T* np = static_cast(root.pop()); + + while(np!=NULL) + { + np->~T(); + Allocator::deallocate(np); + np = static_cast(root.pop()); + } + } + + PX_INLINE T* get() + { + T* rv = static_cast(root.pop()); + if(rv==NULL) + { + rv = reinterpret_cast(Allocator::allocate(sizeof(T), __FILE__, __LINE__)); + new (rv) T(mParams); + } + + return rv; + } + + PX_INLINE void put(T* item) + { + root.push(*item); + } + + +private: + Ps::SList root; + Params* mParams; + + template + friend class PxcThreadCoherentCacheIterator; +}; + +/*! +Used to iterate over all objects controlled by the cache. + +Note: The iterator flushes the cache(extracts all items on construction and adds them back on +destruction so we can iterate the list in a safe manner). +*/ +template +class PxcThreadCoherentCacheIterator +{ +public: + PxcThreadCoherentCacheIterator(PxcThreadCoherentCache& cache) : mCache(cache) + { + mNext = cache.root.flush(); + mFirst = mNext; + } + ~PxcThreadCoherentCacheIterator() + { + Ps::SListEntry* np = mFirst; + while(np != NULL) + { + Ps::SListEntry* npNext = np->next(); + mCache.root.push(*np); + np = npNext; + } + } + + PX_INLINE T* getNext() + { + if(mNext == NULL) + return NULL; + + T* rv = static_cast(mNext); + mNext = mNext->next(); + + return rv; + } +private: + + PxcThreadCoherentCacheIterator& operator=(const PxcThreadCoherentCacheIterator&); + PxcThreadCoherentCache &mCache; + Ps::SListEntry* mNext; + Ps::SListEntry* mFirst; + +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp b/src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp new file mode 100644 index 000000000..7840341ed --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp @@ -0,0 +1,267 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxcContactMethodImpl.h" + +namespace physx +{ + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +//non-pcm sphere function +bool PxcContactSphereSphere(GU_CONTACT_METHOD_ARGS) +{ + return contactSphereSphere(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSphereCapsule(GU_CONTACT_METHOD_ARGS) +{ + return contactSphereCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSphereBox(GU_CONTACT_METHOD_ARGS) +{ + return contactSphereBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSpherePlane(GU_CONTACT_METHOD_ARGS) +{ + return contactSpherePlane(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSphereConvex(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSphereHeightField(GU_CONTACT_METHOD_ARGS) +{ + return contactSphereHeightfield(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + return contactSphereMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//non-pcm plane functions +bool PxcContactPlaneBox(GU_CONTACT_METHOD_ARGS) +{ + return contactPlaneBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactPlaneCapsule(GU_CONTACT_METHOD_ARGS) +{ + return contactPlaneCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactPlaneConvex(GU_CONTACT_METHOD_ARGS) +{ + return contactPlaneConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//non-pcm capsule funtions +bool PxcContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactCapsuleBox(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactCapsuleConvex(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleHeightfield(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//non-pcm box functions +bool PxcContactBoxBox(GU_CONTACT_METHOD_ARGS) +{ + return contactBoxBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactBoxConvex(GU_CONTACT_METHOD_ARGS) +{ + return contactBoxConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactBoxHeightField(GU_CONTACT_METHOD_ARGS) +{ + return contactBoxHeightfield(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactBoxMesh(GU_CONTACT_METHOD_ARGS) +{ + return contactBoxMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//non-pcm convex functions +bool PxcContactConvexConvex(GU_CONTACT_METHOD_ARGS) +{ + return contactConvexConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactConvexHeightField(GU_CONTACT_METHOD_ARGS) +{ + return contactConvexHeightfield(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactConvexMesh(GU_CONTACT_METHOD_ARGS) +{ + return contactConvexMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//pcm sphere functions +bool PxcPCMContactSphereSphere(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereSphere(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSpherePlane(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSpherePlane(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSphereCapsule(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSphereBox(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSphereConvex(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSphereHeightField(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereHeightField(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//pcm plane functions +bool PxcPCMContactPlaneBox(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactPlaneBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactPlaneCapsule(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactPlaneCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactPlaneConvex(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactPlaneConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//pcm capsule functions +bool PxcPCMContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactCapsuleCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactCapsuleBox(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactCapsuleBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactCapsuleConvex(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactCapsuleConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactCapsuleHeightField(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactCapsuleMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//pcm box functions +bool PxcPCMContactBoxBox(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactBoxBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactBoxConvex(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactBoxConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactBoxHeightField(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactBoxHeightField(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactBoxMesh(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactBoxMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//pcm convex functions +bool PxcPCMContactConvexConvex(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactConvexConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactConvexHeightField(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactConvexHeightField(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactConvexMesh(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactConvexMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +} diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp new file mode 100644 index 000000000..419163225 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp @@ -0,0 +1,393 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxcContactCache.h" +#include "PxsContactManager.h" +#include "PsUtilities.h" +#include "PxcNpCache.h" + +using namespace physx; +using namespace Gu; + +//#define ENABLE_CONTACT_CACHE_STATS + +#ifdef ENABLE_CONTACT_CACHE_STATS + static PxU32 gNbCalls; + static PxU32 gNbHits; +#endif + +void PxcClearContactCacheStats() +{ +#ifdef ENABLE_CONTACT_CACHE_STATS + gNbCalls = 0; + gNbHits = 0; +#endif +} + +void PxcDisplayContactCacheStats() +{ +#ifdef ENABLE_CONTACT_CACHE_STATS + pxPrintf("%d|%d (%f)\n", gNbHits, gNbCalls, gNbCalls ? float(gNbHits)/float(gNbCalls) : 0.0f); +#endif +} + +namespace physx +{ + const bool g_CanUseContactCache[][PxGeometryType::eGEOMETRY_COUNT] = + { + //PxGeometryType::eSPHERE + { + false, //PxcContactSphereSphere + false, //PxcContactSpherePlane + true, //PxcContactSphereCapsule + false, //PxcContactSphereBox + true, //PxcContactSphereConvex + true, //PxcContactSphereMesh + true, //PxcContactSphereHeightField + }, + + //PxGeometryType::ePLANE + { + false, //- + false, //PxcInvalidContactPair + true, //PxcContactPlaneCapsule + true, //PxcContactPlaneBox + true, //PxcContactPlaneConvex + false, //PxcInvalidContactPair + false, //PxcInvalidContactPair + }, + + //PxGeometryType::eCAPSULE + { + false, //- + false, //- + true, //PxcContactCapsuleCapsule + true, //PxcContactCapsuleBox + true, //PxcContactCapsuleConvex + true, //PxcContactCapsuleMesh + true, //PxcContactCapsuleHeightField + }, + + //PxGeometryType::eBOX + { + false, //- + false, //- + false, //- + true, //PxcContactBoxBox + true, //PxcContactBoxConvex + true, //PxcContactBoxMesh + true, //PxcContactBoxHeightField + }, + + //PxGeometryType::eCONVEXMESH + { + false, //- + false, //- + false, //- + false, //- + true, //PxcContactConvexConvex + true, //PxcContactConvexMesh2 + true, //PxcContactConvexHeightField + }, + + //PxGeometryType::eTRIANGLEMESH + { + false, //- + false, //- + false, //- + false, //- + false, //- + false, //PxcInvalidContactPair + false, //PxcInvalidContactPair + }, + + //PxGeometryType::eHEIGHTFIELD + { + false, //- + false, //- + false, //- + false, //- + false, //- + false, //- + false, //PxcInvalidContactPair + }, + }; +} + +static PX_FORCE_INLINE void updateContact( Gu::ContactPoint& dst, const PxcLocalContactsCache& contactsData, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const PxVec3& point, const PxVec3& normal, float separation) +{ + const PxVec3 tmp0 = contactsData.mTransform0.transformInv(point); + const PxVec3 worldpt0 = world0.transform(tmp0); + + const PxVec3 tmp1 = contactsData.mTransform1.transformInv(point); + const PxVec3 worldpt1 = world1.transform(tmp1); + + const PxVec3 motion = worldpt0 - worldpt1; + dst.normal = normal; + dst.point = (worldpt0 + worldpt1)*0.5f; + //dst.point = point; + dst.separation = separation + motion.dot(normal); +} + +static PX_FORCE_INLINE void prefetchData128(PxU8* PX_RESTRICT ptr, PxU32 size) +{ + // PT: always prefetch the cache line containing our address (which unfortunately won't be aligned to 128 most of the time) + Ps::prefetchLine(ptr, 0); + // PT: compute start offset of our data within its cache line + const PxU32 startOffset = PxU32(size_t(ptr)&127); + // PT: prefetch next cache line if needed + if(startOffset+size>128) + Ps::prefetchLine(ptr+128, 0); +} + +static PX_FORCE_INLINE PxU8* outputToCache(PxU8* PX_RESTRICT bytes, const PxVec3& v) +{ + *reinterpret_cast(bytes) = v; + return bytes + sizeof(PxVec3); +} + +static PX_FORCE_INLINE PxU8* outputToCache(PxU8* PX_RESTRICT bytes, PxReal v) +{ + *reinterpret_cast(bytes) = v; + return bytes + sizeof(PxReal); +} + +static PX_FORCE_INLINE PxU8* outputToCache(PxU8* PX_RESTRICT bytes, PxU32 v) +{ + *reinterpret_cast(bytes) = v; + return bytes + sizeof(PxU32); +} + +//PxU32 gContactCache_NbCalls = 0; +//PxU32 gContactCache_NbHits = 0; + +static PX_FORCE_INLINE PxReal maxComponentDeltaPos(const PxTransform& t0, const PxTransform& t1) +{ + PxReal delta = PxAbs(t0.p.x - t1.p.x); + delta = PxMax(delta, PxAbs(t0.p.y - t1.p.y)); + delta = PxMax(delta, PxAbs(t0.p.z - t1.p.z)); + return delta; +} + +static PX_FORCE_INLINE PxReal maxComponentDeltaRot(const PxTransform& t0, const PxTransform& t1) +{ + PxReal delta = PxAbs(t0.q.x - t1.q.x); + delta = PxMax(delta, PxAbs(t0.q.y - t1.q.y)); + delta = PxMax(delta, PxAbs(t0.q.z - t1.q.z)); + delta = PxMax(delta, PxAbs(t0.q.w - t1.q.w)); + return delta; +} + +bool physx::PxcCacheLocalContacts( PxcNpThreadContext& context, Gu::Cache& pairContactCache, + const PxTransform& tm0, const PxTransform& tm1, + const PxcContactMethod conMethod, + const Gu::GeometryUnion& shape0, const Gu::GeometryUnion& shape1) +{ + const Gu::NarrowPhaseParams& params = context.mNarrowPhaseParams; + +// gContactCache_NbCalls++; + + if(pairContactCache.mCachedData) + prefetchData128(pairContactCache.mCachedData, pairContactCache.mCachedSize); + + ContactBuffer& contactBuffer = context.mContactBuffer; + contactBuffer.reset(); + + PxcLocalContactsCache contactsData; + PxU32 nbCachedBytes; + const PxU8* cachedBytes = PxcNpCacheRead2(pairContactCache, contactsData, nbCachedBytes); + + pairContactCache.mCachedData = NULL; + pairContactCache.mCachedSize = 0; + +#ifdef ENABLE_CONTACT_CACHE_STATS + gNbCalls++; +#endif + + const PxU32 payloadSize = (sizeof(PxcLocalContactsCache)+3)&~3; + + if(cachedBytes) + { + // PT: we used to store the relative TM but it's better to save memory and recompute it + const PxTransform t0to1 = tm1.transformInv(tm0); + const PxTransform relTM = contactsData.mTransform1.transformInv(contactsData.mTransform0); + + const PxReal epsilon = 0.01f; + if( maxComponentDeltaPos(t0to1, relTM)(cachedBytes); + const PxVec3* normal0 = NULL; + for(PxU32 i=0;i(contacts); contacts += sizeof(PxVec3); + normal0 = cachedNormal; + } + else + { + cachedNormal = normal0; + } + + const PxVec3* cachedPoint = reinterpret_cast(contacts); contacts += sizeof(PxVec3); + const PxReal* cachedPD = reinterpret_cast(contacts); contacts += sizeof(PxReal); + + updateContact(*dst, contactsData, world0, world1, *cachedPoint, *cachedNormal, *cachedPD); + + if(contactsData.mUseFaceIndices) + { + const PxU32* cachedIndex1 = reinterpret_cast(contacts); contacts += sizeof(PxU32); + + dst->internalFaceIndex1 = *cachedIndex1; + } + else + { + dst->internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + dst++; + } + } + if(ls) + PxcNpCacheWriteFinalize(ls, contactsData, nbCachedBytes, cachedBytes); +#ifdef ENABLE_CONTACT_CACHE_STATS + gNbHits++; +#endif + return true; + } + else + { + // PT: if we reach this point we cached the contacts but we couldn't use them next frame + // => waste of time and memory + } + } + + conMethod(shape0, shape1, tm0, tm1, params, pairContactCache, context.mContactBuffer, &context.mRenderOutput); + + //if(contactBuffer.count) + { + contactsData.mTransform0 = tm0; + contactsData.mTransform1 = tm1; + + PxU32 nbBytes = 0; + const PxU8* bytes = NULL; + const PxU32 count = contactBuffer.count; + if(count) + { + const bool useFaceIndices = contactBuffer.contacts[0].internalFaceIndex1!=PXC_CONTACT_NO_FACE_INDEX; + contactsData.mNbCachedContacts = Ps::to16(count); + contactsData.mUseFaceIndices = useFaceIndices; + + const Gu::ContactPoint* PX_RESTRICT srcContacts = contactBuffer.contacts; + // PT: this loop should not be here. We should output the contacts directly compressed, as we used to. + bool sameNormal = true; + { + const PxVec3 normal0 = srcContacts->normal; + for(PxU32 i=1;i(ls) = contactsData; + *reinterpret_cast(ls+payloadSize) = nbBytes; + bytes = ls+payloadSize+sizeof(PxU32); + PxU8* dest = const_cast(bytes); + for(PxU32 i=0;i> 1; + const bool isFirstTriangle = (triangleIndex & 0x1) == 0; + + //get sample + const PxHeightFieldSample* hf = &hfData->samples[sampleIndex]; + return isFirstTriangle ? hf->materialIndex0 : hf->materialIndex1; +} + +bool physx::PxcGetMaterialHeightField(const PxsShapeCore* shape, const PxU32 index, PxcNpThreadContext& context, PxsMaterialInfo* materialInfo) +{ + PX_ASSERT(index == 1); + PX_UNUSED(index); + const ContactBuffer& contactBuffer = context.mContactBuffer; + const PxHeightFieldGeometryLL& hfGeom = shape->geometry.get(); + if(hfGeom.materials.numIndices <= 1) + { + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + (&materialInfo[i].mMaterialIndex0)[index] = shape->materialIndex; + } + } + else + { + const PxU16* materialIndices = hfGeom.materials.indices; + + const Gu::HeightFieldData* hf = hfGeom.heightFieldData; + + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + const Gu::ContactPoint& contact = contactBuffer.contacts[i]; + const PxU32 localMaterialIndex = GetMaterialIndex(hf, contact.internalFaceIndex1); + (&materialInfo[i].mMaterialIndex0)[index] = materialIndices[localMaterialIndex]; + } + } + return true; +} + +bool physx::PxcGetMaterialShapeHeightField(const PxsShapeCore* shape0, const PxsShapeCore* shape1, PxcNpThreadContext& context, PxsMaterialInfo* materialInfo) +{ + const ContactBuffer& contactBuffer = context.mContactBuffer; + const PxHeightFieldGeometryLL& hfGeom = shape1->geometry.get(); + if(hfGeom.materials.numIndices <= 1) + { + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + materialInfo[i].mMaterialIndex0 = shape0->materialIndex; + materialInfo[i].mMaterialIndex1 = shape1->materialIndex; + } + } + else + { + const PxU16* materialIndices = hfGeom.materials.indices; + + const Gu::HeightFieldData* hf = hfGeom.heightFieldData; + + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + const Gu::ContactPoint& contact = contactBuffer.contacts[i]; + materialInfo[i].mMaterialIndex0 = shape0->materialIndex; + //contact.featureIndex0 = shape0->materialIndex; + const PxU32 localMaterialIndex = GetMaterialIndex(hf, contact.internalFaceIndex1); + //contact.featureIndex1 = materialIndices[localMaterialIndex]; + PX_ASSERT(localMaterialIndexgeometry.get(); + if(shapeMesh.materials.numIndices <= 1) + { + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + (&materialInfo[i].mMaterialIndex0)[index] = shape->materialIndex; + } + } + else + { + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + + Gu::ContactPoint& contact = contactBuffer.contacts[i]; + const PxU16* eaMaterialIndices = shapeMesh.materialIndices; + + const PxU32 localMaterialIndex = eaMaterialIndices[contact.internalFaceIndex1];//shapeMesh.triangleMesh->getTriangleMaterialIndex(contact.featureIndex1); + (&materialInfo[i].mMaterialIndex0)[index] = shapeMesh.materials.indices[localMaterialIndex]; + } + } + return true; +} + +bool physx::PxcGetMaterialShapeMesh(const PxsShapeCore* shape0, const PxsShapeCore* shape1, PxcNpThreadContext& context, PxsMaterialInfo* materialInfo) +{ + + ContactBuffer& contactBuffer = context.mContactBuffer; + const PxTriangleMeshGeometryLL& shapeMesh = shape1->geometry.get(); +// const Gu::TriangleMesh* meshData = shapeMesh.meshData; + if(shapeMesh.materials.numIndices <= 1) + { + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + materialInfo[i].mMaterialIndex0 = shape0->materialIndex; + materialInfo[i].mMaterialIndex1 = shape1->materialIndex; + } + } + else + { + + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + + Gu::ContactPoint& contact = contactBuffer.contacts[i]; + //contact.featureIndex0 = shape0->materialIndex; + materialInfo[i].mMaterialIndex0 = shape0->materialIndex; + const PxU16* eaMaterialIndices = shapeMesh.materialIndices; + + const PxU32 localMaterialIndex = eaMaterialIndices[contact.internalFaceIndex1];//shapeMesh.triangleMesh->getTriangleMaterialIndex(contact.featureIndex1); + //contact.featureIndex1 = shapeMesh.materials.indices[localMaterialIndex]; + materialInfo[i].mMaterialIndex1 = shapeMesh.materials.indices[localMaterialIndex]; + + } + } + + return true; +} diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp new file mode 100644 index 000000000..3a8dfcb6c --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp @@ -0,0 +1,140 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxGeometry.h" +#include "PxcMaterialMethodImpl.h" + +namespace physx +{ +bool PxcGetMaterialShapeShape (MATERIAL_METHOD_ARGS); +bool PxcGetMaterialShapeMesh (MATERIAL_METHOD_ARGS); +bool PxcGetMaterialShapeHeightField (MATERIAL_METHOD_ARGS); +bool PxcGetMaterialShape (SINGLE_MATERIAL_METHOD_ARGS); +bool PxcGetMaterialMesh (SINGLE_MATERIAL_METHOD_ARGS); +bool PxcGetMaterialHeightField (SINGLE_MATERIAL_METHOD_ARGS); + + +PxcGetSingleMaterialMethod g_GetSingleMaterialMethodTable[PxGeometryType::eGEOMETRY_COUNT] = +{ + PxcGetMaterialShape, //PxGeometryType::eSPHERE + PxcGetMaterialShape, //PxGeometryType::ePLANE + PxcGetMaterialShape, //PxGeometryType::eCAPSULE + PxcGetMaterialShape, //PxGeometryType::eBOX + PxcGetMaterialShape, //PxGeometryType::eCONVEXMESH + PxcGetMaterialMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + PxcGetMaterialHeightField, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + +}; + +//Table of contact methods for different shape-type combinations +PxcGetMaterialMethod g_GetMaterialMethodTable[][PxGeometryType::eGEOMETRY_COUNT] = +{ + + //PxGeometryType::eSPHERE + { + PxcGetMaterialShapeShape, //PxGeometryType::eSPHERE + PxcGetMaterialShapeShape, //PxGeometryType::ePLANE + PxcGetMaterialShapeShape, //PxGeometryType::eCAPSULE + PxcGetMaterialShapeShape, //PxGeometryType::eBOX + PxcGetMaterialShapeShape, //PxGeometryType::eCONVEXMESH + PxcGetMaterialShapeMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + PxcGetMaterialShapeHeightField, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + PxcGetMaterialShapeShape, //PxGeometryType::eCAPSULE + PxcGetMaterialShapeShape, //PxGeometryType::eBOX + PxcGetMaterialShapeShape, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + 0, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + PxcGetMaterialShapeShape, //PxGeometryType::eCAPSULE + PxcGetMaterialShapeShape, //PxGeometryType::eBOX + PxcGetMaterialShapeShape, //PxGeometryType::eCONVEXMESH + PxcGetMaterialShapeMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + PxcGetMaterialShapeHeightField, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + PxcGetMaterialShapeShape, //PxGeometryType::eBOX + PxcGetMaterialShapeShape, //PxGeometryType::eCONVEXMESH + PxcGetMaterialShapeMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + PxcGetMaterialShapeHeightField, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + PxcGetMaterialShapeShape, //PxGeometryType::eCONVEXMESH + PxcGetMaterialShapeMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + PxcGetMaterialShapeHeightField, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + 0, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + 0, //PxGeometryType::eHEIGHTFIELD + }, + +}; + +} diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp new file mode 100644 index 000000000..8b948eb5e --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp @@ -0,0 +1,62 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxTriangleMesh.h" +#include "PxvGeometry.h" +#include "PxsMaterialManager.h" +#include "PxcNpThreadContext.h" +#include "GuHeightField.h" + +using namespace physx; +using namespace Gu; + +namespace physx +{ +bool PxcGetMaterialShape(const PxsShapeCore* shape, const PxU32 index, PxcNpThreadContext& context, PxsMaterialInfo* materialInfo) +{ + ContactBuffer& contactBuffer = context.mContactBuffer; + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + (&materialInfo[i].mMaterialIndex0)[index] = shape->materialIndex; + } + return true; +} + +bool PxcGetMaterialShapeShape(const PxsShapeCore* shape0, const PxsShapeCore* shape1, PxcNpThreadContext& context, PxsMaterialInfo* materialInfo) +{ + ContactBuffer& contactBuffer = context.mContactBuffer; + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + materialInfo[i].mMaterialIndex0 = shape0->materialIndex; + materialInfo[i].mMaterialIndex1 = shape1->materialIndex; + } + return true; +} +} + diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp new file mode 100644 index 000000000..0b3441ad9 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp @@ -0,0 +1,445 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxcNpBatch.h" +#include "PxcNpWorkUnit.h" +#include "PxsContactManager.h" +#include "GuGeometryUnion.h" +#include "PxcContactCache.h" +#include "PxcMaterialMethodImpl.h" +#include "PxcNpContactPrepShared.h" +#include "PxvDynamics.h" // for PxsBodyCore +#include "PxvGeometry.h" // for PxsShapeCore +#include "CmFlushPool.h" +#include "CmTask.h" +#include "PxTriangleMesh.h" +#include "PxsMaterialManager.h" +#include "PxsTransformCache.h" +#include "GuPersistentContactManifold.h" +#include "PxsContactManagerState.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + + +static void startContacts(PxsContactManagerOutput& output, PxcNpThreadContext& context) +{ + context.mContactBuffer.reset(); + + output.contactForces = NULL; + output.contactPatches = NULL; + output.contactPoints = NULL; + output.nbContacts = 0; + output.nbPatches = 0; + output.statusFlag = 0; +} + +static void flipContacts(PxcNpThreadContext& threadContext, PxsMaterialInfo* PX_RESTRICT materialInfo) +{ + ContactBuffer& buffer = threadContext.mContactBuffer; + for(PxU32 i=0; imSharedDataIndex, PxI32(contactSize))); + + if(context.mContactStreamPool->isOverflown()) + { + PX_WARN_ONCE("Contact buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + contactPoints = context.mContactStreamPool->mDataStream + context.mContactStreamPool->mDataStreamSize - index; + + const PxU32 patchIndex = PxU32(Ps::atomicAdd(&context.mPatchStreamPool->mSharedDataIndex, PxI32(patchSize))); + + if(context.mPatchStreamPool->isOverflown()) + { + PX_WARN_ONCE("Patch buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + contactPatches = context.mPatchStreamPool->mDataStream + context.mPatchStreamPool->mDataStreamSize - patchIndex; + + if(forceSize) + { + index = PxU32(Ps::atomicAdd(&context.mForceAndIndiceStreamPool->mSharedDataIndex, PxI32(forceSize))); + + if(context.mForceAndIndiceStreamPool->isOverflown()) + { + PX_WARN_ONCE("Force buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + forceBuffer = reinterpret_cast(context.mForceAndIndiceStreamPool->mDataStream + context.mForceAndIndiceStreamPool->mDataStreamSize - index); + } + + if(isOverflown) + { + contactPatches = NULL; + contactPoints = NULL; + forceBuffer = NULL; + cmOutput.nbContacts = cmOutput.nbPatches = 0; + } + else + { + PxMemCopy(contactPatches, oldPatches, patchSize); + PxMemCopy(contactPoints, oldContacts, contactSize); + if (isMeshType) + { + PxMemCopy(forceBuffer + cmOutput.nbContacts, oldForces + cmOutput.nbContacts, sizeof(PxU32) * cmOutput.nbContacts); + } + } + } + else + { + const PxU32 alignedOldSize = (oldSize + 0xf) & 0xfffffff0; + + PxU8* data = context.mContactBlockStream.reserve(alignedOldSize + forceSize); + if(forceSize) + forceBuffer = reinterpret_cast(data + alignedOldSize); + + contactPatches = data; + contactPoints = data + cmOutput.nbPatches * sizeof(PxContactPatch); + + PxMemCopy(data, oldPatches, oldSize); + if (isMeshType) + { + PxMemCopy(forceBuffer + cmOutput.nbContacts, oldForces + cmOutput.nbContacts, sizeof(PxU32) * cmOutput.nbContacts); + } + } + + if(forceSize) + PxMemZero(forceBuffer, forceSize); + + cmOutput.contactPatches= contactPatches; + cmOutput.contactPoints = contactPoints; + cmOutput.contactForces = forceBuffer; + } + + if(cache.mCachedSize) + { + if(cache.isMultiManifold()) + { + PX_ASSERT((cache.mCachedSize & 0xF) == 0); + PxU8* newData = context.mNpCacheStreamPair.reserve(cache.mCachedSize); + PX_ASSERT((reinterpret_cast(newData)& 0xF) == 0); + PxMemCopy(newData, & cache.getMultipleManifold(), cache.mCachedSize); + cache.setMultiManifold(newData); + } + else if(useContactCache) + { + //Copy cache information as well... + const PxU8* cachedData = cache.mCachedData; + PxU8* newData = context.mNpCacheStreamPair.reserve(PxU32(cache.mCachedSize + 0xf) & 0xfff0); + PxMemCopy(newData, cachedData, cache.mCachedSize); + cache.mCachedData = newData; + } + } + return ret; +} + +//ML: isMeshType is used in the GPU codepath. If the collision pair is mesh/heightfield vs primitives, we need to allocate enough memory for the mForceAndIndiceStreamPool in the threadContext. +static bool finishContacts(const PxcNpWorkUnit& input, PxsContactManagerOutput& npOutput, PxcNpThreadContext& threadContext, PxsMaterialInfo* PX_RESTRICT pMaterials, const bool isMeshType) +{ + ContactBuffer& buffer = threadContext.mContactBuffer; + + PX_ASSERT((npOutput.statusFlag & PxsContactManagerStatusFlag::eTOUCH_KNOWN) != PxsContactManagerStatusFlag::eTOUCH_KNOWN); + PxU8 statusFlags = PxU16(npOutput.statusFlag & (~PxsContactManagerStatusFlag::eTOUCH_KNOWN)); + if (buffer.count != 0) + statusFlags |= PxsContactManagerStatusFlag::eHAS_TOUCH; + else + statusFlags |= PxsContactManagerStatusFlag::eHAS_NO_TOUCH; + + npOutput.nbContacts = Ps::to8(buffer.count); + + if(buffer.count==0) + { + npOutput.statusFlag = statusFlags; + npOutput.nbContacts = 0; + npOutput.nbPatches = 0; + return true; + } + +#if PX_ENABLE_SIM_STATS + if(buffer.count) + threadContext.mNbDiscreteContactPairsWithContacts++; +#endif + + npOutput.statusFlag = statusFlags; + + PxU32 contactForceByteSize = buffer.count * sizeof(PxReal); + + //Regardless of the flags, we need to now record the compressed contact stream + + PxU16 compressedContactSize; + + const bool createReports = + input.flags & PxcNpWorkUnitFlag::eOUTPUT_CONTACTS + || threadContext.mCreateContactStream + || (input.flags & PxcNpWorkUnitFlag::eFORCE_THRESHOLD); + + if(!buffer.count || (!isMeshType && !createReports)) + contactForceByteSize = 0; + + bool res = (writeCompressedContact(buffer.contacts, buffer.count, &threadContext, npOutput.nbContacts, npOutput.contactPatches, npOutput.contactPoints, compressedContactSize, + reinterpret_cast(npOutput.contactForces), contactForceByteSize, threadContext.mMaterialManager, ((input.flags & PxcNpWorkUnitFlag::eMODIFIABLE_CONTACT) != 0), + false, pMaterials, npOutput.nbPatches, 0, NULL, NULL, threadContext.mCreateAveragePoint, threadContext.mContactStreamPool, + threadContext.mPatchStreamPool, threadContext.mForceAndIndiceStreamPool, isMeshType) != 0) || (buffer.count == 0); + + //handle buffer overflow + if (buffer.count && !npOutput.nbContacts) + { + PxU8 thisStatusFlags = PxU16(npOutput.statusFlag & (~PxsContactManagerStatusFlag::eTOUCH_KNOWN)); + thisStatusFlags |= PxsContactManagerStatusFlag::eHAS_NO_TOUCH; + + npOutput.statusFlag = thisStatusFlags; + npOutput.nbContacts = 0; + npOutput.nbPatches = 0; +#if PX_ENABLE_SIM_STATS + if(buffer.count) + threadContext.mNbDiscreteContactPairsWithContacts--; +#endif + } + return res; +} + +template +static PX_FORCE_INLINE bool checkContactsMustBeGenerated(PxcNpThreadContext& context, const PxcNpWorkUnit& input, Gu::Cache& cache, PxsContactManagerOutput& output, + const PxsCachedTransform* cachedTransform0, const PxsCachedTransform* cachedTransform1, + const bool flip, PxGeometryType::Enum type0, PxGeometryType::Enum type1) +{ + PX_ASSERT(cachedTransform0->transform.isSane() && cachedTransform1->transform.isSane()); + + //ML : if user doesn't raise the eDETECT_DISCRETE_CONTACT, we should not generate contacts + if(!(input.flags & PxcNpWorkUnitFlag::eDETECT_DISCRETE_CONTACT)) + return false; + + if(!(output.statusFlag & PxcNpWorkUnitStatusFlag::eDIRTY_MANAGER) && !(input.flags & PxcNpWorkUnitFlag::eMODIFIABLE_CONTACT)) + { + const PxU32 body0Dynamic = PxU32(input.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY0); + const PxU32 body1Dynamic = PxU32(input.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1); + + const PxU32 active0 = PxU32(body0Dynamic && !cachedTransform0->isFrozen()); + const PxU32 active1 = PxU32(body1Dynamic && !cachedTransform1->isFrozen()); + + if(!(active0 || active1)) + { + if(flip) + Ps::swap(type0, type1); + + const bool useContactCache = useContactCacheT ? context.mContactCache && g_CanUseContactCache[type0][type1] : false; + +#if PX_ENABLE_SIM_STATS + if(output.nbContacts) + context.mNbDiscreteContactPairsWithContacts++; +#endif + const bool isMeshType = type1 > PxGeometryType::eCONVEXMESH; + copyBuffers(output, cache, context, useContactCache, isMeshType); + return false; + } + } + + output.statusFlag &= (~PxcNpWorkUnitStatusFlag::eDIRTY_MANAGER); + + const PxReal contactDist0 = context.mContactDistance[input.mTransformCache0]; + const PxReal contactDist1 = context.mContactDistance[input.mTransformCache1]; + //context.mNarrowPhaseParams.mContactDistance = shape0->contactOffset + shape1->contactOffset; + context.mNarrowPhaseParams.mContactDistance = contactDist0 + contactDist1; + + return true; +} + +template +static PX_FORCE_INLINE void discreteNarrowPhase(PxcNpThreadContext& context, const PxcNpWorkUnit& input, Gu::Cache& cache, PxsContactManagerOutput& output) +{ + PxGeometryType::Enum type0 = static_cast(input.geomType0); + PxGeometryType::Enum type1 = static_cast(input.geomType1); + + const bool flip = (type1getTransformCache(input.mTransformCache0); + const PxsCachedTransform* cachedTransform1 = &context.mTransformCache->getTransformCache(input.mTransformCache1); + + if(!checkContactsMustBeGenerated(context, input, cache, output, cachedTransform0, cachedTransform1, flip, type0, type1)) + return; + + PxsShapeCore* shape0 = const_cast(input.shapeCore0); + PxsShapeCore* shape1 = const_cast(input.shapeCore1); + + if(flip) + { + Ps::swap(type0, type1); + Ps::swap(shape0, shape1); + Ps::swap(cachedTransform0, cachedTransform1); + } + + PxsMaterialInfo materialInfo[ContactBuffer::MAX_CONTACTS]; + + Gu::MultiplePersistentContactManifold& manifold = context.mTempManifold; + bool isMultiManifold = false; + + if(!useLegacyCodepath) + { + if(cache.isMultiManifold()) + { + //We are using a multi-manifold. This is cached in a reduced npCache... + isMultiManifold = true; + uintptr_t address = uintptr_t(&cache.getMultipleManifold()); + manifold.fromBuffer(reinterpret_cast(address)); + cache.setMultiManifold(&manifold); + } + else if(cache.isManifold()) + { + void* address = reinterpret_cast(&cache.getManifold()); + Ps::prefetch(address); + Ps::prefetch(address, 128); + Ps::prefetch(address, 256); + } + } + + updateDiscreteContactStats(context, type0, type1); + + startContacts(output, context); + + const PxTransform* tm0 = &cachedTransform0->transform; + const PxTransform* tm1 = &cachedTransform1->transform; + PX_ASSERT(tm0->isSane() && tm1->isSane()); + + if(useLegacyCodepath) + { + // PT: many cache misses here... + + Ps::prefetchLine(shape1, 0); // PT: at least get rid of L2s for shape1 + + const PxcContactMethod conMethod = g_ContactMethodTable[type0][type1]; + PX_ASSERT(conMethod); + + const bool useContactCache = context.mContactCache && g_CanUseContactCache[type0][type1]; + if(useContactCache) + { +#if PX_ENABLE_SIM_STATS + if(PxcCacheLocalContacts(context, cache, *tm0, *tm1, conMethod, shape0->geometry, shape1->geometry)) + context.mNbDiscreteContactPairsWithCacheHits++; +#else + PxcCacheLocalContacts(context, n.pairCache, *tm0, *tm1, conMethod, shape0->geometry, shape1->geometry); +#endif + } + else + { + conMethod(shape0->geometry, shape1->geometry, *tm0, *tm1, context.mNarrowPhaseParams, cache, context.mContactBuffer, &context.mRenderOutput); + } + } + else + { + const PxcContactMethod conMethod = g_PCMContactMethodTable[type0][type1]; + PX_ASSERT(conMethod); + + conMethod(shape0->geometry, shape1->geometry, *tm0, *tm1, context.mNarrowPhaseParams, cache, context.mContactBuffer, &context.mRenderOutput); + } + + const PxcGetMaterialMethod materialMethod = g_GetMaterialMethodTable[type0][type1]; + PX_ASSERT(materialMethod); + + materialMethod(shape0, shape1, context, materialInfo); + + if(flip) + flipContacts(context, materialInfo); + + if(!useLegacyCodepath) + { + if(isMultiManifold) + { + //Store the manifold back... + const PxU32 size = (sizeof(MultiPersistentManifoldHeader) + + manifold.mNumManifolds * sizeof(SingleManifoldHeader) + + manifold.mNumTotalContacts * sizeof(Gu::CachedMeshPersistentContact)); + + PxU8* buffer = context.mNpCacheStreamPair.reserve(size); + + PX_ASSERT((reinterpret_cast(buffer)& 0xf) == 0); + manifold.toBuffer(buffer); + cache.setMultiManifold(buffer); + cache.mCachedSize = Ps::to16(size); + } + } + + const bool isMeshType = type1 > PxGeometryType::eCONVEXMESH; + finishContacts(input, output, context, materialInfo, isMeshType); +} + +void physx::PxcDiscreteNarrowPhase(PxcNpThreadContext& context, const PxcNpWorkUnit& input, Gu::Cache& cache, PxsContactManagerOutput& output) +{ + discreteNarrowPhase(context, input, cache, output); +} + +void physx::PxcDiscreteNarrowPhasePCM(PxcNpThreadContext& context, const PxcNpWorkUnit& input, Gu::Cache& cache, PxsContactManagerOutput& output) +{ + discreteNarrowPhase(context, input, cache, output); +} diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp new file mode 100644 index 000000000..a31890e22 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp @@ -0,0 +1,75 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxcNpCacheStreamPair.h" +#include "PsUserAllocated.h" +#include "PxcNpMemBlockPool.h" + +using namespace physx; + +void PxcNpCacheStreamPair::reset() +{ + mBlock = NULL; + mUsed = 0; +} + +PxcNpCacheStreamPair::PxcNpCacheStreamPair(PxcNpMemBlockPool& blockPool): + mBlockPool(blockPool), mBlock(NULL), mUsed(0) +{ +} + +// reserve can fail and return null. Read should never fail +PxU8* PxcNpCacheStreamPair::reserve(PxU32 size) +{ + size = (size+15)&~15; + + if(size>PxcNpMemBlock::SIZE) + { + return reinterpret_cast(-1); + } + + if(mBlock == NULL || mUsed + size > PxcNpMemBlock::SIZE) + { + mBlock = mBlockPool.acquireNpCacheBlock(); + mUsed = 0; + } + + PxU8* ptr; + if(mBlock == NULL) + ptr = 0; + else + { + ptr = mBlock->data+mUsed; + mUsed += size; + } + + return ptr; +} + diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp new file mode 100644 index 000000000..a824d6a93 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp @@ -0,0 +1,554 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsMathUtils.h" +#include "PxcNpWorkUnit.h" +#include "PxvDynamics.h" + +using namespace physx; +using namespace Gu; + +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" + +#include "PxcNpContactPrepShared.h" +#include "PsAtomic.h" +#include "PxsContactManagerState.h" + +#include "PsVecMath.h" +using namespace physx; +using namespace Ps::aos; + +static PX_FORCE_INLINE void copyContactPoint(PxContact* PX_RESTRICT point, const Gu::ContactPoint* PX_RESTRICT cp) +{ + // PT: TODO: consider moving "separation" right after "point" in both structures, to copy both at the same time. +// point->contact = cp->point; + const Vec4V contactV = V4LoadA(&cp->point.x); // PT: V4LoadA safe because 'point' is aligned. + V4StoreU(contactV, &point->contact.x); + + point->separation = cp->separation; +} + +struct StridePatch +{ + PxU8 startIndex; + PxU8 endIndex; + PxU8 nextIndex; + PxU8 totalCount; + bool isRoot; +}; + +PxU32 physx::writeCompressedContact(const Gu::ContactPoint* const PX_RESTRICT contactPoints, const PxU32 numContactPoints, PxcNpThreadContext* threadContext, + PxU8& writtenContactCount, PxU8*& outContactPatches, PxU8*& outContactPoints, PxU16& compressedContactSize, PxReal*& outContactForces, PxU32 contactForceByteSize, + const PxsMaterialManager* materialManager, bool hasModifiableContacts, bool forceNoResponse, PxsMaterialInfo* PX_RESTRICT pMaterial, PxU8& numPatches, + PxU32 additionalHeaderSize, PxsConstraintBlockManager* manager, PxcConstraintBlockStream* blockStream, bool insertAveragePoint, + PxcDataStreamPool* contactStreamPool, PxcDataStreamPool* patchStreamPool, PxcDataStreamPool* forceStreamPool, const bool isMeshType) +{ + if(numContactPoints == 0) + { + writtenContactCount = 0; + outContactPatches = NULL; + outContactPoints = NULL; + outContactForces = NULL; + compressedContactSize = 0; + numPatches = 0; + return 0; + } + + //Calculate the size of the contact buffer... + PX_ALLOCA(strPatches, StridePatch, numContactPoints); + + StridePatch* stridePatches = &strPatches[0]; + + PxU32 numStrideHeaders = 1; + + /*const bool hasInternalFaceIndex = contactPoints[0].internalFaceIndex0 != PXC_CONTACT_NO_FACE_INDEX || + contactPoints[0].internalFaceIndex1 != PXC_CONTACT_NO_FACE_INDEX;*/ + const bool isModifiable = !forceNoResponse && hasModifiableContacts; + + PxU32 totalUniquePatches = 1; + + PxU32 totalContactPoints = numContactPoints; + + PxU32 strideStart = 0; + bool root = true; + StridePatch* parentRootPatch = NULL; + { + const PxReal closeNormalThresh = PXC_SAME_NORMAL; + //Go through and tag how many patches we have... + PxVec3 normal = contactPoints[0].normal; + PxU16 mat0 = pMaterial[0].mMaterialIndex0; + PxU16 mat1 = pMaterial[0].mMaterialIndex1; + + for(PxU32 a = 1; a < numContactPoints; ++a) + { + if(normal.dot(contactPoints[a].normal) < closeNormalThresh || + pMaterial[a].mMaterialIndex0 != mat0 || pMaterial[a].mMaterialIndex1 != mat1) + { + StridePatch& patch = stridePatches[numStrideHeaders-1]; + + patch.startIndex = PxU8(strideStart); + patch.endIndex = PxU8(a); + patch.nextIndex = 0xFF; + patch.totalCount = PxU8(a - strideStart); + patch.isRoot = root; + if(parentRootPatch) + parentRootPatch->totalCount += PxU8(a - strideStart); + + root = true; + parentRootPatch = NULL; + for(PxU32 b = 1; b < numStrideHeaders; ++b) + { + StridePatch& thisPatch = stridePatches[b-1]; + if(thisPatch.isRoot) + { + PxU32 ind = thisPatch.startIndex; + PxReal dp2 = contactPoints[a].normal.dot(contactPoints[ind].normal); + if(dp2 >= closeNormalThresh && pMaterial[a].mMaterialIndex0 == pMaterial[ind].mMaterialIndex0 && + pMaterial[a].mMaterialIndex1 == pMaterial[ind].mMaterialIndex1) + { + PxU32 nextInd = b-1; + while(stridePatches[nextInd].nextIndex != 0xFF) + nextInd = stridePatches[nextInd].nextIndex; + stridePatches[nextInd].nextIndex = PxU8(numStrideHeaders); + root = false; + parentRootPatch = &stridePatches[b-1]; + break; + } + } + } + + normal = contactPoints[a].normal; + + mat0 = pMaterial[a].mMaterialIndex0; + mat1 = pMaterial[a].mMaterialIndex1; + totalContactPoints = insertAveragePoint && (a - strideStart) > 1 ? totalContactPoints + 1 : totalContactPoints; + strideStart = a; + numStrideHeaders++; + if(root) + totalUniquePatches++; + } + } + totalContactPoints = insertAveragePoint &&(numContactPoints - strideStart) > 1 ? totalContactPoints + 1 : totalContactPoints; + contactForceByteSize = insertAveragePoint && contactForceByteSize != 0 ? contactForceByteSize + sizeof(PxF32) * (totalContactPoints - numContactPoints) : contactForceByteSize; + } + { + StridePatch& patch = stridePatches[numStrideHeaders-1]; + patch.startIndex = PxU8(strideStart); + patch.endIndex = PxU8(numContactPoints); + patch.nextIndex = 0xFF; + patch.totalCount = PxU8(numContactPoints - strideStart); + patch.isRoot = root; + if(parentRootPatch) + parentRootPatch->totalCount += PxU8(numContactPoints - strideStart); + } + + numPatches = PxU8(totalUniquePatches); + + //Calculate the number of patches/points required + + const PxU32 patchHeaderSize = sizeof(PxContactPatch) * (isModifiable ? totalContactPoints : totalUniquePatches) + additionalHeaderSize; + const PxU32 pointSize = totalContactPoints * (isModifiable ? sizeof(PxModifiableContact) : sizeof(PxContact)); + + PxU32 requiredContactSize = pointSize; + PxU32 requiredPatchSize = patchHeaderSize; + PxU32 totalRequiredSize; + + PxU8* PX_RESTRICT contactData = NULL; + PxU8* PX_RESTRICT patchData = NULL; + PxReal* PX_RESTRICT forceData = NULL; + PxU32* PX_RESTRICT triangleIndice = NULL; + + if(contactStreamPool && !isModifiable && additionalHeaderSize == 0) //If the contacts are modifiable, we **DON'T** allocate them in GPU pinned memory. This will be handled later when they're modified + { + bool isOverflown = false; + + PxU32 contactIndex = PxU32(Ps::atomicAdd(&contactStreamPool->mSharedDataIndex, PxI32(requiredContactSize))); + + if (contactStreamPool->isOverflown()) + { + PX_WARN_ONCE("Contact buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + + contactData = contactStreamPool->mDataStream + contactStreamPool->mDataStreamSize - contactIndex; + + PxU32 patchIndex = PxU32(Ps::atomicAdd(&patchStreamPool->mSharedDataIndex, PxI32(requiredPatchSize))); + + if (patchStreamPool->isOverflown()) + { + PX_WARN_ONCE("Patch buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + + patchData = patchStreamPool->mDataStream + patchStreamPool->mDataStreamSize - patchIndex; + + if(contactForceByteSize) + { + contactForceByteSize = isMeshType ? contactForceByteSize * 2 : contactForceByteSize; + contactIndex = PxU32(Ps::atomicAdd(&forceStreamPool->mSharedDataIndex, PxI32(contactForceByteSize))); + + if (forceStreamPool->isOverflown()) + { + PX_WARN_ONCE("Force buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + forceData = reinterpret_cast(forceStreamPool->mDataStream + forceStreamPool->mDataStreamSize - contactIndex); + if (isMeshType) + triangleIndice = reinterpret_cast(forceData + numContactPoints); + } + + totalRequiredSize = requiredContactSize + requiredPatchSize; + + if (isOverflown) + { + patchData = NULL; + contactData = NULL; + forceData = NULL; + triangleIndice = NULL; + } + } + else + { + PxU32 alignedRequiredSize = (requiredContactSize + requiredPatchSize + 0xf) & 0xfffffff0; + contactForceByteSize = (isMeshType ? contactForceByteSize * 2 : contactForceByteSize); + PxU32 totalSize = alignedRequiredSize + contactForceByteSize; + PxU8* data = manager ? blockStream->reserve(totalSize, *manager) : threadContext->mContactBlockStream.reserve(totalSize); + + patchData = data; + contactData = patchData + requiredPatchSize; + + if(contactForceByteSize) + { + forceData = reinterpret_cast((data + alignedRequiredSize)); + + if (isMeshType) + triangleIndice = reinterpret_cast(forceData + numContactPoints); + + if(data) + { + PxMemZero(forceData, contactForceByteSize); + } + } + + totalRequiredSize = alignedRequiredSize; + + } + + Ps::prefetchLine(patchData); + Ps::prefetchLine(contactData); + + if(patchData == NULL) + { + writtenContactCount = 0; + outContactPatches = NULL; + outContactPoints = NULL; + outContactForces = NULL; + compressedContactSize = 0; + numPatches = 0; + return 0; + } + +#if PX_ENABLE_SIM_STATS + if(threadContext) + { + threadContext->mCompressedCacheSize += totalRequiredSize; + threadContext->mTotalCompressedCacheSize += totalRequiredSize; + } +#endif + compressedContactSize = Ps::to16(totalRequiredSize); + + + + //PxU32 startIndex = 0; + + //Extract first material + PxU16 origMatIndex0 = pMaterial[0].mMaterialIndex0; + PxU16 origMatIndex1 = pMaterial[0].mMaterialIndex1; + + PxReal staticFriction, dynamicFriction, combinedRestitution; + PxU32 materialFlags; + { + const PxsMaterialData& data0 = *materialManager->getMaterial(origMatIndex0); + const PxsMaterialData& data1 = *materialManager->getMaterial(origMatIndex1); + + combinedRestitution = PxsMaterialCombiner::combineRestitution(data0, data1); + PxsMaterialCombiner combiner(1.0f, 1.0f); + PxsMaterialCombiner::PxsCombinedMaterial combinedMat = combiner.combineIsotropicFriction(data0, data1); + staticFriction = combinedMat.staFriction; + dynamicFriction = combinedMat.dynFriction; + materialFlags = combinedMat.flags; + } + + + PxU8* PX_RESTRICT dataPlusOffset = patchData + additionalHeaderSize; + PxContactPatch* PX_RESTRICT patches = reinterpret_cast(dataPlusOffset); + PxU32* PX_RESTRICT faceIndice = triangleIndice; + + outContactPatches = patchData; + outContactPoints = contactData; + outContactForces = forceData; + + if(isModifiable) + { + + PxU32 flags = PxU32(isModifiable ? PxContactPatch::eMODIFIABLE : 0) | + (forceNoResponse ? PxContactPatch::eFORCE_NO_RESPONSE : 0) | + (isMeshType ? PxContactPatch::eHAS_FACE_INDICES : 0); + + PxU32 currentIndex = 0; + + PxModifiableContact* PX_RESTRICT point = reinterpret_cast(contactData); + + for(PxU32 a = 0; a < numStrideHeaders; ++a) + { + StridePatch& rootPatch = stridePatches[a]; + if(rootPatch.isRoot) + { + PxContactPatch* PX_RESTRICT patch = patches++; + + PxU32 startIndex = rootPatch.startIndex; + + const PxU16 matIndex0 = pMaterial[startIndex].mMaterialIndex0; + const PxU16 matIndex1 = pMaterial[startIndex].mMaterialIndex1; + if(matIndex0 != origMatIndex0 || matIndex1 != origMatIndex1) + { + const PxsMaterialData& data0 = *materialManager->getMaterial(matIndex0); + const PxsMaterialData& data1 = *materialManager->getMaterial(matIndex1); + + combinedRestitution = PxsMaterialCombiner::combineRestitution(data0, data1); + PxsMaterialCombiner combiner(1.0f, 1.0f); + PxsMaterialCombiner::PxsCombinedMaterial combinedMat = combiner.combineIsotropicFriction(data0, data1); + staticFriction = combinedMat.staFriction; + dynamicFriction = combinedMat.dynFriction; + materialFlags = combinedMat.flags; + origMatIndex0 = matIndex0; + origMatIndex1 = matIndex1; + } + + patch->nbContacts = rootPatch.totalCount; + + patch->startContactIndex = Ps::to8(currentIndex); + patch->materialFlags = PxU8(materialFlags); + patch->staticFriction = staticFriction; + patch->dynamicFriction = dynamicFriction; + patch->restitution = combinedRestitution; + patch->materialIndex0 = matIndex0; + patch->materialIndex1 = matIndex1; + patch->normal = contactPoints[0].normal; + patch->mMassModification.mInvMassScale0 = 1.0f; + patch->mMassModification.mInvMassScale1 = 1.0f; + patch->mMassModification.mInvInertiaScale0 = 1.0f; + patch->mMassModification.mInvInertiaScale1 = 1.0f; + patch->internalFlags = PxU8(flags); + + //const PxU32 endIndex = strideHeader[a]; + const PxU32 totalCountThisPatch = rootPatch.totalCount; + if(insertAveragePoint && totalCountThisPatch > 1) + { + PxVec3 avgPt(0.0f); + PxF32 avgPen(0.0f); + PxF32 recipCount = 1.0f/(PxF32(rootPatch.totalCount)); + + PxU32 index = a; + while(index != 0xFF) + { + StridePatch& p = stridePatches[index]; + for(PxU32 b = p.startIndex; b < p.endIndex; ++b) + { + avgPt += contactPoints[b].point; + avgPen += contactPoints[b].separation; + } + index = p.nextIndex; + } + + if (faceIndice) + { + StridePatch& p = stridePatches[index]; + *faceIndice = contactPoints[p.startIndex].internalFaceIndex1; + faceIndice++; + } + + patch->nbContacts++; + point->contact = avgPt * recipCount; + point->separation = avgPen * recipCount; + point->normal = contactPoints[0].normal; + point->maxImpulse = PX_MAX_REAL; + point->targetVelocity = PxVec3(0.0f); + point->staticFriction = staticFriction; + point->dynamicFriction = dynamicFriction; + point->restitution = combinedRestitution; + point->materialFlags = materialFlags; + point->materialIndex0 = matIndex0; + point->materialIndex1 = matIndex1; + point++; + currentIndex++; + Ps::prefetchLine(point, 128); + } + + PxU32 index = a; + while(index != 0xFF) + { + StridePatch& p = stridePatches[index]; + + for(PxU32 b = p.startIndex; b < p.endIndex; ++b) + { + copyContactPoint(point, &contactPoints[b]); + point->normal = contactPoints[b].normal; + point->maxImpulse = PX_MAX_REAL; + point->targetVelocity = PxVec3(0.0f); + point->staticFriction = staticFriction; + point->dynamicFriction = dynamicFriction; + point->restitution = combinedRestitution; + point->materialFlags = materialFlags; + point->materialIndex0 = matIndex0; + point->materialIndex1 = matIndex1; + if (faceIndice) + { + *faceIndice = contactPoints[b].internalFaceIndex1; + faceIndice++; + } + point++; + currentIndex++; + Ps::prefetchLine(point, 128); + } + index = p.nextIndex; + } + } + } + } + else + { + PxU32 flags = PxU32(isMeshType ? PxContactPatch::eHAS_FACE_INDICES : 0); + + PxContact* PX_RESTRICT point = reinterpret_cast(contactData); + + PxU32 currentIndex = 0; + { + for(PxU32 a = 0; a < numStrideHeaders; ++a) + { + StridePatch& rootPatch = stridePatches[a]; + + if(rootPatch.isRoot) + { + const PxU16 matIndex0 = pMaterial[rootPatch.startIndex].mMaterialIndex0; + const PxU16 matIndex1 = pMaterial[rootPatch.startIndex].mMaterialIndex1; + if(matIndex0 != origMatIndex0 || matIndex1 != origMatIndex1) + { + const PxsMaterialData& data0 = *materialManager->getMaterial(matIndex0); + const PxsMaterialData& data1 = *materialManager->getMaterial(matIndex1); + + combinedRestitution = PxsMaterialCombiner::combineRestitution(data0, data1); + PxsMaterialCombiner combiner(1.0f, 1.0f); + PxsMaterialCombiner::PxsCombinedMaterial combinedMat = combiner.combineIsotropicFriction(data0, data1); + staticFriction = combinedMat.staFriction; + dynamicFriction = combinedMat.dynFriction; + materialFlags = combinedMat.flags; + origMatIndex0 = matIndex0; + origMatIndex1 = matIndex1; + } + + PxContactPatch* PX_RESTRICT patch = patches++; + patch->normal = contactPoints[rootPatch.startIndex].normal; + patch->nbContacts = rootPatch.totalCount; + patch->startContactIndex = Ps::to8(currentIndex); + //KS - we could probably compress this further into the header but the complexity might not be worth it + patch->staticFriction = staticFriction; + patch->dynamicFriction = dynamicFriction; + patch->restitution = combinedRestitution; + patch->materialIndex0 = matIndex0; + patch->materialIndex1 = matIndex1; + patch->materialFlags = PxU8(materialFlags); + patch->internalFlags = PxU8(flags); + patch->mMassModification.mInvMassScale0 = 1.0f; + patch->mMassModification.mInvMassScale1 = 1.0f; + patch->mMassModification.mInvInertiaScale0 = 1.0f; + patch->mMassModification.mInvInertiaScale1 = 1.0f; + if(insertAveragePoint && (rootPatch.totalCount) > 1) + { + patch->nbContacts++; + PxVec3 avgPt(0.0f); + PxF32 avgPen(0.0f); + PxF32 recipCount = 1.0f/(PxF32(rootPatch.totalCount)); + PxU32 index = a; + + while(index != 0xFF) + { + StridePatch& p = stridePatches[index]; + for(PxU32 b = p.startIndex; b < p.endIndex; ++b) + { + avgPt += contactPoints[b].point; + avgPen += contactPoints[b].separation; + } + index = stridePatches[index].nextIndex; + } + + if (faceIndice) + { + StridePatch& p = stridePatches[index]; + *faceIndice = contactPoints[p.startIndex].internalFaceIndex1; + faceIndice++; + } + point->contact = avgPt * recipCount; + point->separation = avgPen * recipCount; + + point++; + currentIndex++; + Ps::prefetchLine(point, 128); + } + + PxU32 index = a; + while(index != 0xFF) + { + StridePatch& p = stridePatches[index]; + for(PxU32 b = p.startIndex; b < p.endIndex; ++b) + { + copyContactPoint(point, &contactPoints[b]); + if (faceIndice) + { + *faceIndice = contactPoints[b].internalFaceIndex1; + faceIndice++; + } + point++; + currentIndex++; + Ps::prefetchLine(point, 128); + } + index = stridePatches[index].nextIndex; + } + } + } + } + } + + writtenContactCount = Ps::to8(totalContactPoints); + + return totalRequiredSize; +} diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp new file mode 100644 index 000000000..2c8b0160c --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp @@ -0,0 +1,351 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxMath.h" +#include "PxcNpMemBlockPool.h" +#include "PsUserAllocated.h" +#include "PsInlineArray.h" +#include "PsFoundation.h" + +using namespace physx; + +PxcNpMemBlockPool::PxcNpMemBlockPool(PxcScratchAllocator& allocator): + mConstraints(PX_DEBUG_EXP("PxcNpMemBlockPool::mConstraints")), + mExceptionalConstraints(PX_DEBUG_EXP("PxcNpMemBlockPool::mExceptionalConstraints")), + mNpCacheActiveStream(0), + mFrictionActiveStream(0), + mCCDCacheActiveStream(0), + mContactIndex(0), + mAllocatedBlocks(0), + mMaxBlocks(0), + mUsedBlocks(0), + mMaxUsedBlocks(0), + mScratchBlockAddr(0), + mNbScratchBlocks(0), + mScratchAllocator(allocator), + mPeakConstraintAllocations(0), + mConstraintAllocations(0) +{ +} + +void PxcNpMemBlockPool::init(PxU32 initialBlockCount, PxU32 maxBlocks) +{ + mMaxBlocks = maxBlocks; + mInitialBlocks = initialBlockCount; + + PxU32 reserve = PxMax(initialBlockCount, 64); + + mConstraints.reserve(reserve); + mExceptionalConstraints.reserve(16); + + mFriction[0].reserve(reserve); + mFriction[1].reserve(reserve); + mNpCache[0].reserve(reserve); + mNpCache[1].reserve(reserve); + mUnused.reserve(reserve); + + setBlockCount(initialBlockCount); +} + +PxU32 PxcNpMemBlockPool::getUsedBlockCount() const +{ + return mUsedBlocks; +} + +PxU32 PxcNpMemBlockPool::getMaxUsedBlockCount() const +{ + return mMaxUsedBlocks; +} + +PxU32 PxcNpMemBlockPool::getPeakConstraintBlockCount() const +{ + return mPeakConstraintAllocations; +} + + +void PxcNpMemBlockPool::setBlockCount(PxU32 blockCount) +{ + Ps::Mutex::ScopedLock lock(mLock); + PxU32 current = getUsedBlockCount(); + for(PxU32 i=current;i(PX_ALLOC(PxcNpMemBlock::SIZE, "PxcNpMemBlock"))); + mAllocatedBlocks++; + } +} + +void PxcNpMemBlockPool::releaseUnusedBlocks() +{ + Ps::Mutex::ScopedLock lock(mLock); + while(mUnused.size()) + { + PX_FREE(mUnused.popBack()); + mAllocatedBlocks--; + } +} + + +PxcNpMemBlockPool::~PxcNpMemBlockPool() +{ + // swapping twice guarantees all blocks are released from the stream pairs + swapFrictionStreams(); + swapFrictionStreams(); + + swapNpCacheStreams(); + swapNpCacheStreams(); + + releaseConstraintMemory(); + releaseContacts(); + releaseContacts(); + + PX_ASSERT(mUsedBlocks == 0); + + flushUnused(); +} + +void PxcNpMemBlockPool::acquireConstraintMemory() +{ + PxU32 size; + void* addr = mScratchAllocator.allocAll(size); + size = size&~(PxcNpMemBlock::SIZE-1); + + PX_ASSERT(mScratchBlocks.size()==0); + mScratchBlockAddr = reinterpret_cast(addr); + mNbScratchBlocks = size/PxcNpMemBlock::SIZE; + + mScratchBlocks.resize(mNbScratchBlocks); + for(PxU32 i=0;i0); + mUsedBlocks--; + } + } + + for(PxU32 i=0;i0) + { + PxcNpMemBlock* block = mScratchBlocks.popBack(); + trackingArray.pushBack(block); + return block; + } + + + if(mUnused.size()) + { + PxcNpMemBlock* block = mUnused.popBack(); + trackingArray.pushBack(block); + mMaxUsedBlocks = PxMax(mUsedBlocks+1, mMaxUsedBlocks); + mUsedBlocks++; + return block; + } + + + if(mAllocatedBlocks == mMaxBlocks) + { +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Reached maximum number of allocated blocks so 16k block allocation will fail!"); +#endif + return NULL; + } + +#if PX_CHECKED + if(mInitialBlocks) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Number of required 16k memory blocks has exceeded the initial number of blocks. Allocator is being called. Consider increasing the number of pre-allocated 16k blocks."); + } +#endif + + // increment here so that if we hit the limit in separate threads we won't overallocated + mAllocatedBlocks++; + + PxcNpMemBlock* block = reinterpret_cast(PX_ALLOC(sizeof(PxcNpMemBlock), "PxcNpMemBlock")); + + if(block) + { + trackingArray.pushBack(block); + mMaxUsedBlocks = PxMax(mUsedBlocks+1, mMaxUsedBlocks); + mUsedBlocks++; + } + else + mAllocatedBlocks--; + + return block; +} + +PxU8* PxcNpMemBlockPool::acquireExceptionalConstraintMemory(PxU32 size) +{ + PxU8* memory = reinterpret_cast(PX_ALLOC(size, "PxcNpExceptionalMemory")); + if(memory) + { + Ps::Mutex::ScopedLock lock(mLock); + mExceptionalConstraints.pushBack(memory); + } + return memory; +} + +void PxcNpMemBlockPool::release(PxcNpMemBlockArray& deadArray, PxU32* allocationCount) +{ + Ps::Mutex::ScopedLock lock(mLock); + PX_ASSERT(mUsedBlocks >= deadArray.size()); + mUsedBlocks -= deadArray.size(); + if(allocationCount) + { + *allocationCount -= deadArray.size(); + } + while(deadArray.size()) + { + PxcNpMemBlock* block = deadArray.popBack(); + for(PxU32 a = 0; a < mUnused.size(); ++a) + { + PX_ASSERT(mUnused[a] != block); + } + mUnused.pushBack(block); + } +} + +void PxcNpMemBlockPool::flushUnused() +{ + while(mUnused.size()) + PX_FREE(mUnused.popBack()); +} + + +PxcNpMemBlock* PxcNpMemBlockPool::acquireConstraintBlock() +{ + // we track the scratch blocks in the constraint block array, because the code in acquireMultipleConstraintBlocks + // assumes that acquired blocks are listed there. + + return acquire(mConstraints); +} + +PxcNpMemBlock* PxcNpMemBlockPool::acquireConstraintBlock(PxcNpMemBlockArray& memBlocks) +{ + return acquire(memBlocks, &mConstraintAllocations, &mPeakConstraintAllocations, true); +} + +PxcNpMemBlock* PxcNpMemBlockPool::acquireContactBlock() +{ + return acquire(mContacts[mContactIndex], NULL, NULL, true); +} + + +void PxcNpMemBlockPool::releaseConstraintBlocks(PxcNpMemBlockArray& memBlocks) +{ + Ps::Mutex::ScopedLock lock(mLock); + + while(memBlocks.size()) + { + PxcNpMemBlock* block = memBlocks.popBack(); + if(mScratchAllocator.isScratchAddr(block)) + mScratchBlocks.pushBack(block); + else + { + mUnused.pushBack(block); + PX_ASSERT(mUsedBlocks>0); + mUsedBlocks--; + } + } +} + +void PxcNpMemBlockPool::releaseContacts() +{ + //releaseConstraintBlocks(mContacts); + release(mContacts[1-mContactIndex]); + mContactIndex = 1-mContactIndex; +} + +PxcNpMemBlock* PxcNpMemBlockPool::acquireFrictionBlock() +{ + return acquire(mFriction[mFrictionActiveStream]); +} + +void PxcNpMemBlockPool::swapFrictionStreams() +{ + release(mFriction[1-mFrictionActiveStream]); + mFrictionActiveStream = 1-mFrictionActiveStream; +} + +PxcNpMemBlock* PxcNpMemBlockPool::acquireNpCacheBlock() +{ + return acquire(mNpCache[mNpCacheActiveStream]); +} + +void PxcNpMemBlockPool::swapNpCacheStreams() +{ + release(mNpCache[1-mNpCacheActiveStream]); + mNpCacheActiveStream = 1-mNpCacheActiveStream; +} + diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp new file mode 100644 index 000000000..9a20f7ea7 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxcConstraintBlockStream.h" +#include "PxcNpThreadContext.h" + +using namespace physx; + +PxcNpThreadContext::PxcNpThreadContext(PxcNpContext* params) : + mRenderOutput (params->mRenderBuffer), + mContactBlockStream (params->mNpMemBlockPool), + mNpCacheStreamPair (params->mNpMemBlockPool), + mNarrowPhaseParams (0.0f, params->mMeshContactMargin, params->mToleranceLength), + mPCM (false), + mContactCache (false), + mCreateContactStream (params->mCreateContactStream), + mCreateAveragePoint (false), +#if PX_ENABLE_SIM_STATS + mCompressedCacheSize (0), + mNbDiscreteContactPairsWithCacheHits(0), + mNbDiscreteContactPairsWithContacts (0), +#endif + mMaxPatches (0), + mTotalCompressedCacheSize (0), + mContactStreamPool (params->mContactStreamPool), + mPatchStreamPool (params->mPatchStreamPool), + mForceAndIndiceStreamPool (params->mForceAndIndiceStreamPool), + mMaterialManager(params->mMaterialManager), + mLocalNewTouchCount (0), + mLocalLostTouchCount (0), + mLocalFoundPatchCount (0), + mLocalLostPatchCount (0) +{ +#if PX_ENABLE_SIM_STATS + clearStats(); +#endif +} + +PxcNpThreadContext::~PxcNpThreadContext() +{ +} + +#if PX_ENABLE_SIM_STATS +void PxcNpThreadContext::clearStats() +{ + PxMemSet(mDiscreteContactPairs, 0, sizeof(mDiscreteContactPairs)); + PxMemSet(mModifiedContactPairs, 0, sizeof(mModifiedContactPairs)); + mCompressedCacheSize = 0; + mNbDiscreteContactPairsWithCacheHits = 0; + mNbDiscreteContactPairsWithContacts = 0; +} +#endif + +void PxcNpThreadContext::reset(PxU32 cmCount) +{ + mContactBlockStream.reset(); + mNpCacheStreamPair.reset(); + + mLocalChangeTouch.clear(); + mLocalChangeTouch.resize(cmCount); + mLocalPatchCountChange.clear(); + mLocalPatchCountChange.resize(cmCount); + mLocalNewTouchCount = 0; + mLocalLostTouchCount = 0; + mLocalFoundPatchCount = 0; + mLocalLostPatchCount = 0; +} diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h b/src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h new file mode 100644 index 000000000..12bc731da --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h @@ -0,0 +1,51 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_BODYSIM_H +#define PXS_BODYSIM_H + +#include "PxsRigidBody.h" + +namespace physx +{ + +struct PxsBodySim +{ +public: + PxsBodySim() : mRigidBody(NULL)/*, mBodySimIndex(0xffffffff), mUpdated(0)*/ + { + } + + PxsRigidBody* mRigidBody; //4 or 8 + //PxU32 mUpdated; //12 or 16 +}; + +}//physx + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h b/src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h new file mode 100644 index 000000000..883c49732 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h @@ -0,0 +1,622 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "Ps.h" +#include "PxGeometry.h" +#include "GuCCDSweepConvexMesh.h" +#include "PsHashMap.h" +#include "PxsIslandSim.h" + +#ifndef PXS_CCD_H +#define PXS_CCD_H + +#define CCD_DEBUG_PRINTS 0 +#define CCD_POST_DEPENETRATE_DIST 0.001f +#define CCD_ROTATION_LOCKING 0 +#define CCD_MIN_TIME_LEFT 0.01f +#define CCD_ANGULAR_IMPULSE 0 + +#define DEBUG_RENDER_CCD 0 + +#if CCD_DEBUG_PRINTS +namespace physx { + extern void printCCDDebug(const char* msg, const PxsRigidBody* atom0, PxGeometryType::Enum g0, bool printPtr = true); + extern void printShape(PxsRigidBody* atom0, PxGeometryType::Enum g0, const char* annotation, PxReal dt, PxU32 pass, bool printPtr = true); +} +#define PRINTCCDSHAPE(x) printShape x +#define PRINTCCDDEBUG(x) printCCDDebug x +#else +#define PRINTCCDSHAPE(x) +#define PRINTCCDDEBUG(x) +#endif + +namespace physx +{ + +// ------------------------------------------------------------------------------------------------------------ +// a fraction of objects will be CCD active so this is dynamic, not a member of PsxRigidBody +// CCD code builds a temporary array of PxsCCDPair objects (allocated in blocks) +// this is done to gather scattered data from memory and also to reduce PxsRidigBody permanent memory footprint +// we have to do it every pass since new CMs can become fast moving after each pass (and sometimes cease to be) +// +struct PxsCCDBody; +class PxsRigidBody; +struct PxsShapeCore; +struct PxsRigidCore; +class PxsContactManager; +class PxsContext; +class PxCCDContactModifyCallback; +class PxcNpThreadContext; + +class PxvNphaseImplementationContext; + +namespace Dy +{ + class ThresholdStream; +} + + +/** +\brief structure to represent interactions between a given body and another body. +*/ +struct PxsCCDOverlap +{ + //The body the interaction relates to + PxsCCDBody* mBody; + //The next interaction in the list + PxsCCDOverlap* mNext; +}; + +/** +\brief Temporary CCD representation for a shape. + +Stores data about a shape that may be frequently used in CCD. It also stores update counters per-shape that can be compared with the body's update +counter to determine if the shape needs its transforms re-calculated. This avoids us needing to store a list of shapes in a CCD body. +*/ +struct PxsCCDShape : public Gu::CCDShape +{ +public: + const PxsShapeCore* mShapeCore; //Shape core (can be shared) + const PxsRigidCore* mRigidCore; //Rigid body core + IG::NodeIndex mNodeIndex; + + /** + \brief Returns the world-space pose for this shape + \param[in] atom The rigid body that this CCD shape is associated with + */ + PxTransform getAbsPose(const PxsRigidBody* atom) const; + /** + \brief Returns the world-space previous pose for this shape + \param[in] atom The rigid body that this CCD shape is associated with + */ + PxTransform getLastCCDAbsPose(const PxsRigidBody* atom) const; +}; + +/** +\brief Structure to represent a body in the CCD system. +*/ +struct PxsCCDBody +{ + Cm::SpatialVector mPreSolverVelocity; + PxU16 mIndex; //The CCD body's index + bool mPassDone; //Whether it has been processed in the current CCD pass + bool mHasAnyPassDone; //Whether this body was influenced by any passes + PxReal mTimeLeft; //CCD time left to elapse (normalized in range 0-1) + PxsRigidBody* mBody; //The rigid body + PxsCCDOverlap* mOverlappingObjects; //A list of overlapping bodies for island update + PxU32 mUpdateCount; //How many times this body has eben updated in the CCD. This is correlated with CCD shapes' update counts. + PxU32 mNbInteractionsThisPass; //How many interactions this pass + + + + /** + \brief Returns the CCD body's index. + \return The CCD body's index. + */ + PX_FORCE_INLINE PxU32 getIndex() const { return mIndex; } + + /** + \brief Tests whether this body has already registered an overlap with a given body. + \param[in] body The body to test against. + \return Whether this body has already registered an overlap with a given body. + */ + bool overlaps(PxsCCDBody* body) const + { + PxsCCDOverlap* overlaps = mOverlappingObjects; + + while(overlaps) + { + if(overlaps->mBody == body) + return true; + overlaps = overlaps->mNext; + } + return false; + } + + /** + \brief Registers an overlap with a given body + \param[in] overlap The CCD overlap to register. + */ + void addOverlap(PxsCCDOverlap* overlap) + { + overlap->mNext = mOverlappingObjects; + mOverlappingObjects = overlap; + } + +}; + +/** +\brief a container class used in the CCD that minimizes frequency of hitting the allocator. + +This class stores a set of blocks of memory. It is effectively an array that resizes more efficiently because it doesn't need to +reallocate an entire buffer and copy data. +*/ +template +struct PxsCCDBlockArray +{ + /** + \brief A block of data + */ + struct Block : Ps::UserAllocated { T items[BLOCK_SIZE]; }; + /** + \brief A header for a block of data. + */ + struct BlockInfo + { + Block* block; + PxU32 count; // number of elements in this block + BlockInfo(Block* aBlock, PxU32 aCount) : block(aBlock), count(aCount) {} + }; + /* + \brief An array of block headers + */ + Ps::Array blocks; + /** + \brief The current block. + */ + PxU32 currentBlock; + + /** + \brief Constructor + */ + PxsCCDBlockArray() : currentBlock(0) + { + blocks.pushBack(BlockInfo(PX_NEW(Block), 0)); + } + + /** + \brief Destructor + */ + ~PxsCCDBlockArray() + { + for (PxU32 i = 0; i < blocks.size(); i++) + { + PX_DELETE(blocks[i].block); + } + currentBlock = 0; + } + + /** + \brief Clears this block array. + \note This clear function also deletes all additional blocks + */ + void clear() + { + for (PxU32 i = 0; i < blocks.size(); i++) + { + PX_DELETE(blocks[i].block); + } + blocks.clear(); + blocks.pushBack(BlockInfo(PX_NEW(Block), 0)); // at least one block is expected to always be present in the array + currentBlock = 0; + } + + /** + \brief Clears this block array but does not release the memory. + */ + void clear_NoDelete() + { + currentBlock = 0; + blocks[0].count = 0; + } + + /** + \brief Push a new element onto the back of the block array + \return The new element + */ + T& pushBack() + { + PxU32 numBlocks = blocks.size(); + if (blocks[currentBlock].count == BLOCK_SIZE) + { + if((currentBlock + 1) == numBlocks) + { + blocks.pushBack(BlockInfo(PX_NEW(Block), 0)); + numBlocks ++; + } + currentBlock++; + blocks[currentBlock].count = 0; + } + const PxU32 count = blocks[currentBlock].count ++; + + return blocks[currentBlock].block->items[count]; + } + + /** + \brief Pushes a new element onto the back of this array, intitializing it to match the data + \param data The data to initialize the new element to + \return The new element + */ + T& pushBack(T& data) + { + PxU32 numBlocks = blocks.size(); + if (blocks[currentBlock].count == BLOCK_SIZE) + { + if((currentBlock + 1) == numBlocks) + { + blocks.pushBack(BlockInfo(PX_NEW(Block), 0)); + numBlocks ++; + } + currentBlock++; + blocks[currentBlock].count = 0; + } + const PxU32 count = blocks[currentBlock].count ++; + blocks[currentBlock].block->items[count] = data; + return blocks[currentBlock].block->items[count]; + } + + /** + \brief Pops the last element from the list. + */ + void popBack() + { + PX_ASSERT(blocks[currentBlock].count > 0); + if (blocks[currentBlock].count > 1) + blocks[currentBlock].count --; + else + { + PX_DELETE(blocks[currentBlock].block); + blocks.popBack(); + currentBlock--; + } + } + + /** + \brief Returns the current size of the array. + \return The current size of the array. + */ + PxU32 size() const + { + return (currentBlock)*BLOCK_SIZE + blocks[currentBlock].count; + } + + /** + \brief Returns the element at a given index in the array + \param[in] index The index of the element in the array + \return The element at a given index in the array. + */ + T& operator[] (PxU32 index) const + { + PX_ASSERT(index/BLOCK_SIZE < blocks.size()); + PX_ASSERT(index%BLOCK_SIZE < blocks[index/BLOCK_SIZE].count); + return blocks[index/BLOCK_SIZE].block->items[index%BLOCK_SIZE]; + } +}; + +/** +\brief A structure to represent a potential CCD interaction between a pair of shapes +*/ +struct PxsCCDPair +{ + /** + \brief Defines whether this is an estimated TOI or an accurate TOI. + + We store pairs in a priority queue based on the TOIs. We use cheap estimates to cull away work and lazily evaluate TOIs. This means that an element in the + priority queue may either be an estimate or a precise result. + */ + enum E_TOIType + { + eEstimate, + ePrecise + }; + PxsRigidBody* mBa0; // Body A. Can be NULL for statics + PxsRigidBody* mBa1; // Body B. Can be NULL for statics + PxsCCDShape* mCCDShape0; // Shape A + PxsCCDShape* mCCDShape1; // Shape B + PxVec3 mMinToiNormal; // The contact normal. Only valid for precise results. On the surface of body/shape A + PxReal mMinToi; // Min TOI. Valid for both precise and estimated results but estimates may be too early (i.e. conservative). + PxReal mPenetrationPostStep; // Valid only for precise sweeps. Only used for initial intersections (i.e. at TOI = 0). + PxVec3 mMinToiPoint; // The contact point. Only valid for precise sweep results. + PxReal mPenetration; // The penetration. Only valid for precise sweep results. + PxsContactManager* mCm; // The contact manager. + PxU32 mIslandId; // The index of the island this pair is in + PxGeometryType::Enum mG0, mG1; // The geometry types for shapes 0 and 1 + bool mIsEarliestToiHit; // Indicates this was the earliest hit for one of the bodies in the pair + bool mIsModifiable; // Indicates whether this contact is modifiable + PxU32 mFaceIndex; // The face index. Only valid for precise sweeps involving meshes or heightfields. + PxU16 mMaterialIndex0; // The material index for shape 0 + PxU16 mMaterialIndex1; // The material index for shape 1 + PxReal mDynamicFriction; // The dynamic friction coefficient + PxReal mStaticFriction; // The static friction coefficient + PxReal mRestitution; // The restitution coefficient + PxU32 mEstimatePass; // The current estimation pass. Used after a sweep hit was found to determine if the pair needs re-estimating. + PxReal mAppliedForce; // The applied force for this pair. Only valid if the pair has been responded to. + PxReal mMaxImpulse; // The maximum impulse to be applied + + E_TOIType mToiType; // The TOI type (estimate, precise). + bool mHasFriction; // Whether we want to simulate CCD friction for this pair + + /** + \brief Perform a precise sweep for this pair + \param[in] threadContext The per-thread context + \param[in] dt The time-step + \param[in] pass The current CCD pass + \return The normalized TOI. <=1.0 indicates a hit. Otherwise PX_MAX_REAL. + */ + PxReal sweepFindToi(PxcNpThreadContext& threadContext, PxReal dt, PxU32 pass); + /** + \brief Performs a sweep estimation for this pair + \return The normalized TOI. <= 1.0 indicates a potential hit, otherwise PX_MAX_REAL. + */ + PxReal sweepEstimateToi(); + /** + \brief Advances this pair to the TOI + \param[in] dt The time-step + \param[in] clipTrajectoryToToi Indicates whether we clip the body's trajectory to the end pose. Only done in the final pass + \return Whether the advance was successful. An advance will be unsuccessful if body bodies were already updated. + */ + bool sweepAdvanceToToi(PxReal dt, bool clipTrajectoryToToi); + /** + \brief Updates the transforms of the shapes involved in this pair. + */ + void updateShapes(); + +}; + +/** +\brief Block array of CCD bodies +*/ +typedef PxsCCDBlockArray PxsCCDBodyArray; +/** +\brief Block array of CCD pairs +*/ +typedef PxsCCDBlockArray PxsCCDPairArray; +/** +\brief Block array of CCD overlaps +*/ +typedef PxsCCDBlockArray PxsCCDOverlapArray; +/** +\brief Block array of CCD shapes +*/ +typedef PxsCCDBlockArray PxsCCDShapeArray; + +/** +\brief Pair structure to be able to look-up a rigid body-shape pair in a map +*/ +typedef Ps::Pair PxsRigidShapePair; + + +/** +\brief CCD context object. +*/ +class PxsCCDContext +{ +public: + + /** + \brief Creates this PxsCCDContext + */ + static PxsCCDContext* create(PxsContext* context, Dy::ThresholdStream& dynamicsContext, PxvNphaseImplementationContext& nPhaseContext); + + /** + \brief Destroys this PxsCCDContext + */ + void destroy(); + + /** + \brief Returns the CCD contact modification callback + \return The CCD contact modification callback + */ + PX_FORCE_INLINE PxCCDContactModifyCallback* getCCDContactModifyCallback() const { return mCCDContactModifyCallback; } + /** + \brief Sets the CCD contact modification callback + \param[in] c The CCD contact modification callback + */ + PX_FORCE_INLINE void setCCDContactModifyCallback(PxCCDContactModifyCallback* c) { mCCDContactModifyCallback = c; } + /** + \brief Returns the maximum number of CCD passes + \return The maximum number of CCD passes + */ + PX_FORCE_INLINE PxU32 getCCDMaxPasses() const { return mCCDMaxPasses; } + /** + \brief Sets the maximum number of CCD passes + \param[in] ccdMaxPasses The maximum number of CCD passes + */ + PX_FORCE_INLINE void setCCDMaxPasses(PxU32 ccdMaxPasses) { mCCDMaxPasses = ccdMaxPasses; } + /** + \brief Returns the current CCD pass + \return The current CCD pass + */ + PX_FORCE_INLINE PxU32 getCurrentCCDPass() const { return miCCDPass; } + /** + \brief Returns The number of swept hits reported + \return The number of swept hits reported + */ + PX_FORCE_INLINE PxI32 getNumSweepHits() const { return mSweepTotalHits; } + /** + \brief Returns The number of updated bodies + \return The number of updated bodies in this CCD pass + */ + PX_FORCE_INLINE PxU32 getNumUpdatedBodies() const { return mUpdatedCCDBodies.size(); } + /** + \brief Returns The update bodies array + \return The updated bodies array from this CCD pass + */ + PX_FORCE_INLINE PxsRigidBody*const* getUpdatedBodies() const { return mUpdatedCCDBodies.begin(); } + + /** + \brief Returns Clears the updated bodies array + */ + PX_FORCE_INLINE void clearUpdatedBodies() { mUpdatedCCDBodies.forceSize_Unsafe(0); } + + /** + \brief Runs the CCD contact modification. + \param[in] contacts The list of modifiable contacts + \param[in] contactCount The number of contacts + \param[in] shapeCore0 The first shape core + \param[in] shapeCore1 The second shape core + \param[in] rigidCore0 The first rigid core + \param[in] rigidCore1 The second rigid core + \param[in] rigid0 The first rigid body + \param[in] rigid1 The second rigid body + */ + void runCCDModifiableContact(PxModifiableContact* PX_RESTRICT contacts, PxU32 contactCount, const PxsShapeCore* PX_RESTRICT shapeCore0, + const PxsShapeCore* PX_RESTRICT shapeCore1, const PxsRigidCore* PX_RESTRICT rigidCore0, const PxsRigidCore* PX_RESTRICT rigidCore1, + const PxsRigidBody* PX_RESTRICT rigid0, const PxsRigidBody* PX_RESTRICT rigid1); + + /** + \brief Performs a single CCD update + This occurs after broad phase and is responsible for creating islands, finding the TOI of collisions, filtering contacts, issuing modification callbacks and responding to + collisions. At the end of this phase all bodies will have stepper to their first TOI if they were involved in a CCD collision this frame. + \param[in] dt The timestep to simulate + \param[in] continuation The continuation task + \param[in] islandSim The island manager + \param[in] disableResweep If this is true, we perform a reduced-fidelity CCD approach + */ + void updateCCD(PxReal dt, PxBaseTask* continuation, IG::IslandSim& islandSim, bool disableResweep, PxI32 numFastMovingShapes); + + /** + \brief Signals the beginning of a CCD multi-pass update + */ + void updateCCDBegin(); + + /** + \brief Resets the CCD contact state in any contact managers that previously had a reported CCD touch. This must be called if CCD update is bypassed for a frame + */ + void resetContactManagers(); + + + + +protected: + + /** + \brief Constructor for PxsCCDContext + \param[in] context The PxsContext that is associated with this PxsCCDContext. + */ + PxsCCDContext(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext); + /** + \brief Destructor for PxsCCDContext + */ + ~PxsCCDContext(); + +private: + + + /** + \brief Verifies the consistency of the CCD context at the beginning + */ + void verifyCCDBegin(); + + /** + \brief Cleans up after the CCD update has completed + */ + void updateCCDEnd(); + + /** + \brief Spawns the update island tasks after the initial sweep estimates have been performed + \param[in] continuation The continuation task + */ + void postCCDSweep(PxBaseTask* continuation); + /** + \brief Creates contact buffers for CCD contacts. These will be sent to the user in the contact notification. + \param[in] continuation The continuation task + */ + void postCCDAdvance(PxBaseTask* continuation); + /** + \brief The final phase of the CCD task chain. Cleans up after the parallel update/postCCDAdvance stages. + \param[in] continuation The continuation task + */ + void postCCDDepenetrate(PxBaseTask* continuation); + + typedef Cm::DelegateTask PostCCDSweepTask; + typedef Cm::DelegateTask PostCCDAdvanceTask; + typedef Cm::DelegateTask PostCCDDepenetrateTask; + + PostCCDSweepTask mPostCCDSweepTask; + PostCCDAdvanceTask mPostCCDAdvanceTask; + PostCCDDepenetrateTask mPostCCDDepenetrateTask; + + PxCCDContactModifyCallback* mCCDContactModifyCallback; + + // CCD global data + bool mDisableCCDResweep; + PxU32 miCCDPass; + PxI32 mSweepTotalHits; + + // a fraction of objects will be CCD active so PxsCCDBody is dynamic, not a member of PxsRigidBody + PxsCCDBodyArray mCCDBodies; + PxsCCDOverlapArray mCCDOverlaps; + PxsCCDShapeArray mCCDShapes; + Ps::Array mIslandBodies; + Ps::Array mIslandSizes; + Ps::Array mUpdatedCCDBodies; + Ps::HashMap mMap; + + // temporary array updated during CCD update + //Array mCCDPairs; + PxsCCDPairArray mCCDPairs; + Ps::Array mCCDPtrPairs; + // number of pairs per island + Ps::Array mCCDIslandHistogram; + // thread context valid during CCD update + PxcNpThreadContext* mCCDThreadContext; + // number of pairs to process per thread + PxU32 mCCDPairsPerBatch; + PxU32 mCCDMaxPasses; + + PxsContext* mContext; + Dy::ThresholdStream& mThresholdStream; + + PxvNphaseImplementationContext& mNphaseContext; + + Ps::Mutex mMutex; + +private: + + PX_NOCOPY(PxsCCDContext) +}; + + +} + + + +#endif + diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h new file mode 100644 index 000000000..1293e2aeb --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_CONTACTMANAGER_H +#define PXS_CONTACTMANAGER_H + +#include "PxvConfig.h" +#include "PxcNpWorkUnit.h" + +namespace physx +{ + +class PxsContext; +class PxsRigidBody; +struct PxsCCDBody; +class PxsMaterialManager; +struct PxsCCDShape; + +namespace Dy +{ + class DynamicsContext; +} + +namespace Sc +{ + class ShapeInteraction; +} + +enum PxsPairVisColor +{ + + eVIS_COLOR_SWEPTINTEGRATE_OFF = 0x000000, + eVIS_COLOR_SWEPTINTEGRATE_SLOW = 0x404040, + eVIS_COLOR_SWEPTINTEGRATE_CLEAR = 0x007f00, + eVIS_COLOR_SWEPTINTEGRATE_IMPACT = 0x1680ff, + eVIS_COLOR_SWEPTINTEGRATE_FAIL = 0x0000ff + +}; + + +/** +\brief Additional header structure for CCD contact data stream. +*/ +struct PxsCCDContactHeader +{ + /** + \brief Stream for next collision. The same pair can collide multiple times during multiple CCD passes. + */ + PxsCCDContactHeader* nextStream; //4 //8 + /** + \brief Size (in bytes) of the CCD contact stream (not including force buffer) + */ + PxU16 contactStreamSize; //6 //10 + /** + \brief Defines whether the stream is from a previous pass. + + It could happen that the stream can not get allocated because we run out of memory. In that case the current event should not use the stream + from an event of the previous pass. + */ + PxU16 isFromPreviousPass; //8 //12 + + PxU8 pad[12 - sizeof(PxsCCDContactHeader*)]; //16 +}; + +PX_COMPILE_TIME_ASSERT((sizeof(PxsCCDContactHeader) & 0xF) == 0); + + +class PxsContactManager +{ +public: + PxsContactManager(PxsContext* context, PxU32 index); + ~PxsContactManager(); + + + PX_FORCE_INLINE void setDisableStrongFriction(PxU32 d) { (!d) ? mNpUnit.flags &= ~PxcNpWorkUnitFlag::eDISABLE_STRONG_FRICTION + : mNpUnit.flags |= PxcNpWorkUnitFlag::eDISABLE_STRONG_FRICTION; } + + PX_FORCE_INLINE PxReal getRestDistance() const { return mNpUnit.restDistance; } + PX_FORCE_INLINE void setRestDistance(PxReal v) { mNpUnit.restDistance = v; } + + void destroy(); + + PX_FORCE_INLINE PxU8 getDominance0() const { return mNpUnit.dominance0; } + PX_FORCE_INLINE void setDominance0(PxU8 v) { mNpUnit.dominance0 = v; } + + PX_FORCE_INLINE PxU8 getDominance1() const { return mNpUnit.dominance1; } + PX_FORCE_INLINE void setDominance1(PxU8 v) { mNpUnit.dominance1 = v; } + + PX_FORCE_INLINE PxU16 getTouchStatus() const { return PxU16(mNpUnit.statusFlags & PxcNpWorkUnitStatusFlag::eHAS_TOUCH); } + PX_FORCE_INLINE PxU16 touchStatusKnown() const { return PxU16(mNpUnit.statusFlags & PxcNpWorkUnitStatusFlag::eTOUCH_KNOWN); } + PX_FORCE_INLINE PxI32 getTouchIdx() const { return (mNpUnit.statusFlags& PxcNpWorkUnitStatusFlag::eHAS_TOUCH) ? 1 : (mNpUnit.statusFlags& PxcNpWorkUnitStatusFlag::eHAS_NO_TOUCH ? -1 : 0); } + + PX_FORCE_INLINE PxU32 getIndex() const { return mNpUnit.index; } + + PX_FORCE_INLINE PxU16 getHasCCDRetouch() const { return PxU16(mNpUnit.statusFlags & PxcNpWorkUnitStatusFlag::eHAS_CCD_RETOUCH); } + PX_FORCE_INLINE void clearCCDRetouch() { mNpUnit.statusFlags &= ~PxcNpWorkUnitStatusFlag::eHAS_CCD_RETOUCH; } + PX_FORCE_INLINE void raiseCCDRetouch() { mNpUnit.statusFlags |= PxcNpWorkUnitStatusFlag::eHAS_CCD_RETOUCH; } + + + + // flags stuff - needs to be refactored + + PX_FORCE_INLINE Ps::IntBool isChangeable() const { return Ps::IntBool(mFlags & PXS_CM_CHANGEABLE); } + PX_FORCE_INLINE Ps::IntBool getCCD() const { return Ps::IntBool((mFlags & PXS_CM_CCD_LINEAR) && (mNpUnit.flags & PxcNpWorkUnitFlag::eDETECT_CCD_CONTACTS)); } + PX_FORCE_INLINE Ps::IntBool getHadCCDContact() const { return Ps::IntBool(mFlags & PXS_CM_CCD_CONTACT); } + PX_FORCE_INLINE void setHadCCDContact() { mFlags |= PXS_CM_CCD_CONTACT; } + void setCCD(bool enable); + PX_FORCE_INLINE void clearCCDContactInfo() { mFlags &= ~PXS_CM_CCD_CONTACT; mNpUnit.ccdContacts = NULL; } + + PX_FORCE_INLINE PxcNpWorkUnit& getWorkUnit() { return mNpUnit; } + PX_FORCE_INLINE const PxcNpWorkUnit& getWorkUnit() const { return mNpUnit; } + + PX_FORCE_INLINE void* getUserData() const { return mShapeInteraction; } + + // Setup solver-constraints + void resetCachedState(); + void resetFrictionCachedState(); + + Sc::ShapeInteraction* getShapeInteraction() const { return mShapeInteraction; } + +private: + //KS - moving this up - we want to get at flags + + PxsRigidBody* mRigidBody0; //4 //8 + PxsRigidBody* mRigidBody1; //8 //16 + PxU32 mFlags; //20 //36 + Sc::ShapeInteraction* mShapeInteraction; //16 //32 + + + + friend class PxsContext; + // everything required for narrow phase to run + PxcNpWorkUnit mNpUnit; + + enum + { + PXS_CM_CHANGEABLE = (1<<0), + PXS_CM_CCD_LINEAR = (1<<1), + PXS_CM_CCD_CONTACT = (1 << 2) + }; + + friend class Dy::DynamicsContext; + friend struct PxsCCDPair; + friend class PxsIslandManager; + friend class PxsCCDContext; + friend class Sc::ShapeInteraction; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h new file mode 100644 index 000000000..767eb08f3 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_CONTACT_MANAGER_STATE_H +#define PXS_CONTACT_MANAGER_STATE_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + + struct PxsShapeCore; + + /** + There is an implicit 1:1 mapping between PxgContactManagerInput and PxsContactManagerOutput. The structures are split because PxgNpContactManagerInput contains constant + data that is produced by the CPU code and PxgNpContactManagerOutput contains per-frame contact information produced by the NP. + + There is also a 1:1 mapping between the PxgNpContactManager and PxsContactManager. This mapping is handled within the PxgNPhaseCore. + + The previous contact states are implicitly cached in PxsContactManager and will be propagated to the solver. Friction correlation is also done implicitly using cached + information in PxsContactManager. + The NP will produce a list of pairs that found/lost patches for the solver along with updating the PxgNpContactManagerOutput for all pairs. + */ + + struct PxsContactManagerStatusFlag + { + enum Enum + { + eHAS_NO_TOUCH = (1 << 0), + eHAS_TOUCH = (1 << 1), + //eHAS_SOLVER_CONSTRAINTS = (1 << 2), + eREQUEST_CONSTRAINTS = (1 << 3), + eHAS_CCD_RETOUCH = (1 << 4), // Marks pairs that are touching at a CCD pass and were touching at discrete collision or at a previous CCD pass already + // but we can not tell whether they lost contact in a pass before. We send them as pure eNOTIFY_TOUCH_CCD events to the + // contact report callback if requested. + eDIRTY_MANAGER = (1 << 5), + eTOUCH_KNOWN = eHAS_NO_TOUCH | eHAS_TOUCH // The touch status is known (if narrowphase never ran for a pair then no flag will be set) + }; + }; + + + struct PX_ALIGN_PREFIX(16) PxsContactManagerOutput + { + PxU8* contactPatches; //Start index/ptr for contact patches + PxU8* contactPoints; //Start index/ptr for contact points + PxReal* contactForces; //Start index/ptr for contact forces + PxU8 nbContacts; //Num contacts + PxU8 nbPatches; //Num patches + PxU8 statusFlag; //Status flag (has touch etc.) + PxU8 prevPatches; //Previous number of patches + + PX_FORCE_INLINE PxU32* getInternalFaceIndice() + { + return reinterpret_cast(contactForces + nbContacts); + } + } + PX_ALIGN_SUFFIX(16); + + struct /*PX_ALIGN_PREFIX(16)*/ PxsContactManagerPersistency + { + PxU8 mPrevPatches; + PxU8 mNbFrictionPatches; + PxU8 mNbPrevFrictionPatches; + } + /*PX_ALIGN_SUFFIX(16)*/; + +} + +#endif //PXG_CONTACT_MANAGER_H diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsContext.h b/src/PhysX/physx/source/lowlevel/software/include/PxsContext.h new file mode 100644 index 000000000..7f5fcc5d8 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsContext.h @@ -0,0 +1,333 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_CONTEXT_H +#define PXS_CONTEXT_H + +#include "PxVisualizationParameter.h" +#include "PxSceneDesc.h" + +#include "CmPool.h" + +#include "PxvNphaseImplementationContext.h" +#include "PxvSimStats.h" +#include "PxsContactManager.h" +#include "PxcNpBatch.h" +#include "PxcConstraintBlockStream.h" +#include "PxcNpCacheStreamPair.h" +#include "PxcNpMemBlockPool.h" +#include "CmRenderOutput.h" +#include "CmUtils.h" +#include "CmTask.h" + +#include "PxContactModifyCallback.h" + +#include "PxsTransformCache.h" +#include "GuPersistentContactManifold.h" +#include "DyArticulation.h" + + +#if PX_SUPPORT_GPU_PHYSX +namespace physx +{ + class PxCudaContextManager; +} +#endif + +namespace physx +{ + +class PxsRigidBody; +struct PxcConstraintBlock; +class PxsMaterialManager; +class PxsCCDContext; +struct PxsContactManagerOutput; +struct PxvContactManagerTouchEvent; + +namespace Cm +{ + class FlushPool; +} + +namespace IG +{ + class SimpleIslandManager; + typedef PxU32 EdgeIndex; +} + +enum PxsTouchEventCount +{ + PXS_LOST_TOUCH_COUNT = 0, + PXS_NEW_TOUCH_COUNT = 1, + PXS_CCD_RETOUCH_COUNT = 2, // pairs that are touching at a CCD pass and were touching at discrete collision or at a previous CCD pass already + // (but they could have lost touch in between) + PXS_PATCH_FOUND_COUNT = 3, + PXS_PATCH_LOST_COUNT = 4, + PXS_TOUCH_EVENT_COUNT = 5 +}; + +class PxsContext : public Ps::UserAllocated, public PxcNpContext +{ + PX_NOCOPY(PxsContext) +public: + PxsContext( const PxSceneDesc& desc, PxTaskManager*, Cm::FlushPool&, PxU64 contextID); + ~PxsContext(); + + void removeRigidBody(PxsRigidBody&); + + Dy::Articulation* createArticulation(); + void destroyArticulation(Dy::Articulation&); + + void createTransformCache(Ps::VirtualAllocatorCallback& allocatorCallback); + + PxsContactManager* createContactManager(PxsContactManager* contactManager, const bool useCCD); + void createCache(Gu::Cache& cache, PxsContactManager* cm, PxU8 geomType0, PxU8 geomType1); + void destroyCache(Gu::Cache& cache); + void destroyContactManager(PxsContactManager* cm); + + + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + + // Collision properties + PX_FORCE_INLINE PxContactModifyCallback* getContactModifyCallback() const { return mContactModifyCallback; } + PX_FORCE_INLINE void setContactModifyCallback(PxContactModifyCallback* c) { mContactModifyCallback = c; mNpImplementationContext->setContactModifyCallback(c);} + + + // resource-related + void setScratchBlock(void* addr, PxU32 size); + + void setContactDistance(Ps::Array* contactDistance); + + // Task-related + void updateContactManager(PxReal dt, bool hasBoundsArrayChanged, bool hasContactDistanceChanged, PxBaseTask* continuation, PxBaseTask* firstPassContinuation); + void secondPassUpdateContactManager(PxReal dt, PxBaseTask* continuation); + void fetchUpdateContactManager(); + void swapStreams(); + + void resetThreadContexts(); + + // Manager status change + bool getManagerTouchEventCount(int* newTouch, int* lostTouch, int* ccdTouch) const; + bool fillManagerTouchEvents( + PxvContactManagerTouchEvent* newTouch, PxI32& newTouchCount, + PxvContactManagerTouchEvent* lostTouch, PxI32& lostTouchCount, + PxvContactManagerTouchEvent* ccdTouch, PxI32& ccdTouchCount); + + PX_FORCE_INLINE void getManagerPatchEventCount(PxU32& foundPatch, PxU32& lostPatch) const { foundPatch = mCMTouchEventCount[PXS_PATCH_FOUND_COUNT]; lostPatch = mCMTouchEventCount[PXS_PATCH_LOST_COUNT]; } + bool fillManagerPatchChangedEvents( + PxsContactManager** foundPatch, PxU32& foundPatchCount, + PxsContactManager** lostPatch, PxU32& lostPatchCount); + + void beginUpdate(); + + // PX_ENABLE_SIM_STATS + PX_FORCE_INLINE PxvSimStats& getSimStats() { return mSimStats; } + PX_FORCE_INLINE const PxvSimStats& getSimStats() const { return mSimStats; } + + PX_FORCE_INLINE Cm::FlushPool& getTaskPool() const { return mTaskPool; } + PX_FORCE_INLINE Cm::RenderBuffer& getRenderBuffer() { return mRenderBuffer; } + + PxReal getVisualizationParameter(PxVisualizationParameter::Enum param) const; + void setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value); + + PX_FORCE_INLINE void setVisualizationCullingBox(const PxBounds3& box) { mVisualizationCullingBox = box; } + PX_FORCE_INLINE const PxBounds3& getVisualizationCullingBox()const { return mVisualizationCullingBox; } + + PX_FORCE_INLINE PxReal getRenderScale() const { return mVisualizationParams[PxVisualizationParameter::eSCALE]; } + Cm::RenderOutput getRenderOutput() { return Cm::RenderOutput(mRenderBuffer); } + PX_FORCE_INLINE bool getPCM() const { return mPCM; } + PX_FORCE_INLINE bool getContactCacheFlag() const { return mContactCache; } + PX_FORCE_INLINE bool getCreateAveragePoint() const { return mCreateAveragePoint; } + + // general stuff + void shiftOrigin(const PxVec3& shift); + + void setCreateContactStream(bool to); + PX_FORCE_INLINE void setPCM(bool enabled) { mPCM = enabled; } + PX_FORCE_INLINE void setContactCache(bool enabled) { mContactCache = enabled; } + + PX_FORCE_INLINE PxcScratchAllocator& getScratchAllocator() { return mScratchAllocator; } + PX_FORCE_INLINE PxsTransformCache& getTransformCache() { return *mTransformCache; } + PX_FORCE_INLINE PxReal* getContactDistance() { return mContactDistance->begin(); } + + PX_FORCE_INLINE PxvNphaseImplementationContext* getNphaseImplementationContext() const + { + return mNpImplementationContext; + } + + PX_FORCE_INLINE void setNphaseImplementationContext(PxvNphaseImplementationContext* ctx) + { + mNpImplementationContext = ctx; + } + + PX_FORCE_INLINE PxvNphaseImplementationContext* getNphaseFallbackImplementationContext() const + { + return mNpFallbackImplementationContext; + } + + PX_FORCE_INLINE void setNphaseFallbackImplementationContext(PxvNphaseImplementationContext* ctx) + { + mNpFallbackImplementationContext = ctx; + } + + PxU32 getTotalCompressedContactSize() const { return mTotalCompressedCacheSize; } + PxU32 getMaxPatchCount() const { return mMaxPatches; } + + PX_FORCE_INLINE PxcThreadCoherentCache& getNpThreadContextPool() + { + return mNpThreadContextPool; + } + + PX_FORCE_INLINE PxcNpThreadContext* getNpThreadContext() + { + // We may want to conditional compile to exclude this on single threaded implementations + // if it is determined to be a performance hit. + return mNpThreadContextPool.get(); + } + + PX_FORCE_INLINE void putNpThreadContext(PxcNpThreadContext* threadContext) + { mNpThreadContextPool.put(threadContext); } + PX_FORCE_INLINE Ps::Mutex& getLock() { return mLock; } + + PX_FORCE_INLINE PxTaskManager& getTaskManager() + { + PX_ASSERT(mTaskManager); + return *mTaskManager; + } + + PX_FORCE_INLINE void clearManagerTouchEvents(); + + PX_FORCE_INLINE Cm::PoolList& getContactManagerPool() + { + return this->mContactManagerPool; + } + + PX_FORCE_INLINE void setActiveContactManager(const PxsContactManager* manager) + { + const PxU32 index = manager->getIndex(); + if (index >= mActiveContactManager.size()) + { + PxU32 newSize = (2 * index + 256)&~255; + mActiveContactManager.resize(newSize); + } + mActiveContactManager.set(index); + + //Record any pairs that have CCD enabled! + if (manager->getCCD()) + { + if (index >= mActiveContactManagersWithCCD.size()) + { + PxU32 newSize = (2 * index + 256)&~255; + mActiveContactManagersWithCCD.resize(newSize); + } + mActiveContactManagersWithCCD.set(index); + } + } + + +private: + void mergeCMDiscreteUpdateResults(PxBaseTask* continuation); + + PxU32 mIndex; + + // Threading + PxcThreadCoherentCache + mNpThreadContextPool; + + // Contact managers + Cm::PoolList mContactManagerPool; + Ps::Pool mManifoldPool; + Ps::Pool mSphereManifoldPool; + + Cm::BitMap mActiveContactManager; + Cm::BitMap mActiveContactManagersWithCCD; //KS - adding to filter any pairs that had a touch + Cm::BitMap mContactManagersWithCCDTouch; //KS - adding to filter any pairs that had a touch + Cm::BitMap mContactManagerTouchEvent; + Cm::BitMap mContactManagerPatchChangeEvent; + PxU32 mCMTouchEventCount[PXS_TOUCH_EVENT_COUNT]; + + Ps::Mutex mLock; + + + + PxContactModifyCallback* mContactModifyCallback; + + // narrowphase platform-dependent implementations support + PxvNphaseImplementationContext* mNpImplementationContext; + PxvNphaseImplementationContext* mNpFallbackImplementationContext; + + + // debug rendering (CS TODO: MS would like to have these wrapped into a class) + PxReal mVisualizationParams[PxVisualizationParameter::eNUM_VALUES]; + + PxBounds3 mVisualizationCullingBox; + + PxTaskManager* mTaskManager; + Cm::FlushPool& mTaskPool; + + + // PxU32 mTouchesLost; + // PxU32 mTouchesFound; + + // PX_ENABLE_SIM_STATS + PxvSimStats mSimStats; + bool mPCM; + bool mContactCache; + bool mCreateAveragePoint; + + PxsTransformCache* mTransformCache; + Ps::Array* mContactDistance; + + + PxU32 mMaxPatches; + PxU32 mTotalCompressedCacheSize; + + PxU64 mContextID; + + friend class PxsCCDContext; + friend class PxsNphaseImplementationContext; + friend class PxgNphaseImplementationContext; //FDTODO ideally it shouldn't be here.. +}; + + +PX_FORCE_INLINE void PxsContext::clearManagerTouchEvents() +{ + mContactManagerTouchEvent.clear(); + mContactManagerPatchChangeEvent.clear(); + for(PxU32 i = 0; i < PXS_TOUCH_EVENT_COUNT; ++i) + { + mCMTouchEventCount[i] = 0; + } +} + + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h new file mode 100644 index 000000000..950698b01 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_DEFAULT_MEMORY_MANAGER_H +#define PXS_DEFAULT_MEMORY_MANAGER_H + +#include "PxsMemoryManager.h" +#include "PsAllocator.h" +#include "PsArray.h" + +namespace physx +{ + + class PxsDefaultMemoryAllocator : public Ps::VirtualAllocatorCallback + { + public: + + PxsDefaultMemoryAllocator(const char* name = NULL) + { + PX_UNUSED(name); +#if 0 //PX_USE_NAMED_ALLOCATOR + if (name) + strcpy(mName, name); + else + strcpy(mName, ""); +#endif + } + + virtual ~PxsDefaultMemoryAllocator() + { + } + + virtual void* allocate(const size_t newByteSize, const char* filename, const int line) + { + PX_UNUSED(line); + PX_UNUSED(filename); +#if 0 //PX_USE_NAMED_ALLOCATOR + return PX_ALLOC(newByteSize, mName); +#else + return PX_ALLOC(newByteSize, filename); +#endif + } + + virtual void deallocate(void* ptr) + { + if (ptr) + PX_FREE(ptr); + } + +#if 0 //PX_USE_NAMED_ALLOCATOR + char mName[32]; +#endif + }; + + + class PxsDefaultMemoryManager : public PxsMemoryManager + { + public: + virtual ~PxsDefaultMemoryManager(); + virtual Ps::VirtualAllocatorCallback* createHostMemoryAllocator(const PxU32 gpuComputeVersion = 0); + virtual Ps::VirtualAllocatorCallback* createDeviceMemoryAllocator(const PxU32 gpuComputeVersion = 0); + + virtual void destroyMemoryAllocator(); + + Ps::Array mAllocators; + + }; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h b/src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h new file mode 100644 index 000000000..0646fe2d7 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_HEAP_MEMORY_ALLOCATOR_H +#define PXS_HEAP_MEMORY_ALLOCATOR_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + namespace shdfnd + { + class VirtualAllocatorCallback; + } + + class PxErrorCallback; + class PxsHostMemoryAllocator; + + class PxsHeapMemoryAllocator : public Ps::VirtualAllocatorCallback + { + public: + virtual ~PxsHeapMemoryAllocator(){} + virtual void* allocate(const size_t size, const char* file, const int line) = 0; + virtual void deallocate(void* ptr) = 0; + + }; + + class PxsHeapMemoryAllocatorManager + { + public: + virtual ~PxsHeapMemoryAllocatorManager() + { + + } + PxsHeapMemoryAllocator* mMappedMemoryAllocators; + }; +} + +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h new file mode 100644 index 000000000..0c75adab5 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h @@ -0,0 +1,40 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_INCREMENTAL_CONSTRAINT_PARTITIONING_H +#define PXS_INCREMENTAL_CONSTRAINT_PARTITIONING_H + +#include "PxsSimpleIslandManager.h" + +namespace physx +{ + +} + +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h new file mode 100644 index 000000000..021cd95f0 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h @@ -0,0 +1,363 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_ISLAND_MANAGER_TYPES_H +#define PXS_ISLAND_MANAGER_TYPES_H + +#include "CmPhysXCommon.h" + +namespace physx +{ + +class PxsContactManager; +class PxsRigidBody; +namespace Dy +{ + struct Constraint; + class Articulation; +} + +typedef PxU32 NodeType; +typedef PxU32 EdgeType; +typedef PxU32 IslandType; +#define INVALID_NODE 0xffffffff +#define INVALID_EDGE 0xffffffff +#define INVALID_ISLAND 0xffffffff + +namespace Dy +{ + typedef size_t ArticulationLinkHandle; +} + +//----------------------------------------------------------------------------// + +template class PxsIslandManagerHook +{ + friend class PxsIslandManager; + T index; + +public: + + static const T INVALID = INVLD; + + PX_FORCE_INLINE PxsIslandManagerHook(): index(INVLD) {} + PX_FORCE_INLINE PxsIslandManagerHook(const T id): index(id) {} + PX_FORCE_INLINE PxsIslandManagerHook(const PxsIslandManagerHook& src) : index(src.index) {} + PX_FORCE_INLINE ~PxsIslandManagerHook(){} + + PX_FORCE_INLINE bool isManaged() const { return index!=INVLD; } + +private: +}; + +typedef PxsIslandManagerHook PxsIslandManagerNodeHook; +typedef PxsIslandManagerHook PxsIslandManagerEdgeHook; +typedef PxsIslandManagerHook PxsIslandManagerIslandHook; + +//----------------------------------------------------------------------------// + +/** +\brief PxsIslandObjects contains arrays of all rigid bodies, articulations, contact managers, and constraints that +belong to all awake islands. The per array indices denoting the ownership per island are stored in PxsIslandIndices. + +@see PxsIslandManager::getIslandObjects +*/ +struct PxsIslandObjects +{ + /** + \brief Array of all rigid bodies in all awake islands. + + \note Each rigid body corresponds to the void* passed to PxsIslandManager::addBody. The PxsRigidBody ptr is computed + by adding the rigid body offset value (passed to PxsIslandManager::create) to the void* pointer + ie [(PxsRigidBody*)((PxU8*)owner + rigidBodyOffset)] + + @see PxsIslandManager::addBody, PxsIslandManager::create + */ + PxsRigidBody*const* bodies; + + /** + \brief Array of all articulation roots in all awake islands. + + \note Each Articulation* corresponds to Dy::getArticulation(articLinkHandle) where + articLinkHandle is the handle passed to PxsIslandManager::setArticulationRootLinkHandle. + + @see PxsIslandManager::setArticulationRootLinkHandle, Dy::getArticulation + */ + Dy::Articulation*const* articulations; + + /** + \brief Array of all articulation roots in all awake islands. + + \note Each void* corresponds to the void* passed to PxsIslandManager::setArticulationRootLinkHandle + + @see PxsIslandManager::setArticulationRootLinkHandle + */ + void*const* articulationOwners; + + /** + \brief Array of all contact managers in all awake islands. + + @see PxsIslandManager::setEdgeRigidCM + */ + struct PxsIndexedContactManager* contactManagers; + + /** + \brief Array of all constraints in all awake islands. + + @see PxsIslandManager::setEdgeConstraint + */ + struct PxsIndexedConstraint* constraints; + + + PxsIslandObjects() : bodies(NULL), articulations(NULL), articulationOwners(NULL), contactManagers(NULL), constraints(NULL) + { + } +}; + + +//----------------------------------------------------------------------------// + +/** +\brief An array of PxsIslandIndices describes the rigid bodies, articulations, contacts and constraints that +belong to each island. + +\note Given an array of PxsIslandIndices, the rigid bodies of the ith island span the inclusive range: + (PxsIslandObjects::bodies[PxsIslandIndices[i]], PxsIslandObjects::bodies[PxsIslandIndices[i+1]-1]) + +\note Given an array of PxsIslandIndices, the constraints of the ith island span the inclusive range: + (PxsIslandObjects::constraints[PxsIslandIndices[i]], PxsIslandObjects::constraints[PxsIslandIndices[i+1]-1]) + +@see PxsIslandObjects::getIslandIndices, PxsIslandObjects::getIslandCount +*/ +class PxsIslandIndices +{ +public: + + PxsIslandIndices(){} + ~PxsIslandIndices(){} + + /** + \brief Return true if the corresponding island has a contact with a static rigid body. + */ + PX_FORCE_INLINE bool getHasStaticContact() const + { + return (1 & hasStaticContact) ? true : false; + } + + /** + \brief The starting index of island rigid bodies in the array PxsIslandObjects::bodies + */ + NodeType bodies; + + /** + \brief The starting index of island articulations in the arrays PxsIslandObjects::articulations and PxsIslandObjects::articulationOwners + + \note The total number of articulations is clamped at 32767 on any platform that uses 16-bit handles. + */ + NodeType articulations : 8*sizeof(NodeType)-1; + +private: + + NodeType hasStaticContact : 1; + +public: + + /** + \brief The starting index of island contact managers in the array PxsIslandObjects::contactManagers. + */ + EdgeType contactManagers; + + /** + \brief The starting index of island constraints in the array PxsIslandObjects::constraints. + + \note islandId is for internal use only and is used for tracking islands that need a second pass. + */ + union + { + EdgeType constraints; + IslandType islandId; + }; + +//private: + + /** + \brief Internal use only. + */ + PX_FORCE_INLINE void setHasStaticContact(const bool b) + { + hasStaticContact = NodeType(b ? 1 : 0); + } +}; +PX_COMPILE_TIME_ASSERT(0==(0x07 & sizeof(PxsIslandIndices))); + + +//----------------------------------------------------------------------------// + +typedef Dy::ArticulationLinkHandle PxsNodeType; + + +/** +\brief Each contact manager or constraint references two separate bodies, where +a body can be a dynamic rigid body, a kinematic rigid body, an articulation or a static. +The struct PxsIndexedInteraction describes the bodies that make up the pair. +*/ +struct PxsIndexedInteraction +{ + /** + \brief An enumerated list of all possible body types. + A body type is stored for each body in the pair. + */ + enum Enum + { + eBODY = 0, + eKINEMATIC = 1, + eARTICULATION = 2, + eWORLD = 3 + }; + + /** + \brief An index describing how to access body0 + + \note If body0 is a dynamic (eBODY) rigid body then solverBody0 is an index into PxsIslandObjects::bodies. + \note If body0 is a kinematic (eKINEMATIC) rigid body then solverBody0 is an index into PxsIslandManager::getActiveKinematics. + + \note If body0 is a static (eWORLD) then solverBody0 is PX_MAX_U32 or PX_MAX_U64, depending on the platform being 32- or 64-bit. + + \note If body0 is an articulation then the articulation is found directly from Dy::getArticulation(articulation0) + */ + union + { + PxsNodeType solverBody0; + Dy::ArticulationLinkHandle articulation0; + }; + + /** + \brief An index describing how to access body1 + + \note If body1 is a dynamic (eBODY) rigid body then solverBody1 is an index into PxsIslandObjects::bodies. + \note If body1 is a kinematic (eKINEMATIC) rigid body then solverBody1 is an index into PxsIslandManager::getActiveKinematics. + + \note If body1 is a static (eWORLD) then solverBody1 is PX_MAX_U32 or PX_MAX_U64, depending on the platform being 32- or 64-bit. + + \note If body1 is an articulation then the articulation is found directly from Dy::getArticulation(articulation1) + */ + union + { + PxsNodeType solverBody1; + Dy::ArticulationLinkHandle articulation1; + }; + + /** + \brief The type (eBODY, eKINEMATIC etc) of body0 + */ + PxU8 indexType0; + + /** + \brief The type (eBODY, eKINEMATIC etc) of body1 + */ + PxU8 indexType1; + + PxU8 pad[2]; +}; + +/** +@see PxsIslandObjects, PxsIndexedInteraction +*/ +struct PxsIndexedContactManager : public PxsIndexedInteraction +{ + /** + \brief The contact manager corresponds to the value set in PxsIslandManager::setEdgeRigidCM + */ + PxsContactManager* contactManager; + + PxsIndexedContactManager(PxsContactManager* cm) : contactManager(cm) {} +}; +#if !PX_X64 +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxsIndexedContactManager) & 0x0f)); +#endif + +/** +@see PxsIslandObjects, PxsIndexedInteraction +*/ +struct PxsIndexedConstraint : public PxsIndexedInteraction +{ + /** + \brief The constraint corresponds to the value set in PxsIslandManager::setEdgeConstraint + */ + Dy::Constraint* constraint; + + PxsIndexedConstraint(Dy::Constraint* c) : constraint(c) {} +}; +#if !PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxsIndexedConstraint) & 0x0f)); +#endif + +//----------------------------------------------------------------------------// + +/** +\brief Any sleeping contact pair that finds itself in an awake island after 1st pass island gen +must participate in 2nd pass narrowphase so that contacts can be generated. + +\note Contact managers in sleeping pairs are NULL until PxsIslandManager::setWokenPairContactManagers is complete. + +@see PxsIslandManager::getNarrowPhaseSecondPassContactManagers, PxsIslandManager::getNumNarrowPhaseSecondPassContactManagers, +PxsIslandManager::setWokenPairContactManagers +*/ +struct PxsNarrowPhaseSecondPassContactManager +{ + /** + \brief The contact manager that is to participate in 2nd pass narrowphase. + + \note This pointer is NULL after 1st pass island gen and remains NULL until PxsIslandManager::setWokenPairContactManagers + completes. + */ + PxsContactManager* mCM; + + /** + \brief The corresponding entry in PxsIslandObjects::contactManagers. + + \note All sleeping pairs have a null contact manager during 1st pass island gen. After 1st pass island gen completes, + the bodies to be woken are externally processed. Waking up bodies generates contact managers and passes the pointer to the + corresponding edge. So that the contact manager can be efficiently passed to PxsIslandObjects we store mEdgeId and mSolverCMId. + The contact manager pointers are set in PxsIslandManager::setWokenPairContactManagers + */ + EdgeType mSolverCMId; //Keeps a track of which entries in the solver islands temporarily have a null contact manager + + /** + \brief The internal id of the corresponding edge. + */ + EdgeType mEdgeId; +}; + + +} //namespace physx + + +#endif //PXS_ISLAND_MANAGER_TYPES_H diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h new file mode 100644 index 000000000..713ae169b --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_ISLAND_NODEINDEX_H +#define PXS_ISLAND_NODEINDEX_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ +namespace IG +{ +#define IG_INVALID_NODE 0x1FFFFFFu + + class NodeIndex + { + private: + PxU32 ind; + + public: + + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE NodeIndex(PxU32 id, PxU32 articLinkId) : ind((id << 7) | (articLinkId << 1) | 1) + { + } + + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE NodeIndex(PxU32 id = IG_INVALID_NODE) : ind((id << 7)) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 index() const { return ind >> 7; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 articulationLinkId() const { return ((ind >> 1) & 63); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 isArticulation() const { return ind & 1; } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isStaticBody() const { return (ind >> 7) == IG_INVALID_NODE; } + + PX_CUDA_CALLABLE bool isValid() const { return (ind >> 7) != IG_INVALID_NODE; } + + PX_CUDA_CALLABLE void setIndices(PxU32 index, PxU32 articLinkId) { ind = ((index << 7) | (articLinkId << 1) | 1); } + + PX_CUDA_CALLABLE void setIndices(PxU32 index) { ind = ((index << 7)); } + + PX_CUDA_CALLABLE bool operator < (const IG::NodeIndex& other) const { return ind < other.ind; } + + PX_CUDA_CALLABLE bool operator <= (const IG::NodeIndex& other) const { return ind <= other.ind; } + + PX_CUDA_CALLABLE bool operator == (const IG::NodeIndex& other) const { return ind == other.ind; } + }; + +} +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h new file mode 100644 index 000000000..2390636bf --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h @@ -0,0 +1,935 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_ISLAND_SIM_H +#define PXS_ISLAND_SIM_H + +#include "CmPhysXCommon.h" +#include "foundation/PxAssert.h" +#include "PsArray.h" +#include "CmBitMap.h" +#include "CmPriorityQueue.h" +#include "CmBlockArray.h" +#include "PxsIslandNodeIndex.h" + +namespace physx +{ + +namespace Dy +{ + struct Constraint; + class ArticulationV; +} + +namespace Sc +{ + class ArticulationSim; +} + +class PxsContactManager; +class PxsRigidBody; + +struct PartitionEdge; + +namespace IG +{ + +//This index is +#define IG_INVALID_ISLAND 0xFFFFFFFFu +#define IG_INVALID_EDGE 0xFFFFFFFFu +#define IG_INVALID_LINK 0xFFu + +typedef PxU32 IslandId; +typedef PxU32 EdgeIndex; +typedef PxU32 EdgeInstanceIndex; + +class IslandSim; + +struct Edge +{ + //Edge instances can be implicitly calculated based on this edge index, which is an offset into the array of edges. + //From that, the child edge index is simply the + //The constraint or contact referenced by this edge + + enum EdgeType + { + eCONTACT_MANAGER, + eCONSTRAINT, + eEDGE_TYPE_COUNT + }; + + + enum EdgeState + { + eINSERTED =1<<0, + ePENDING_DESTROYED =1<<1, + eACTIVE =1<<2, + eIN_DIRTY_LIST =1<<3, + eDESTROYED =1<<4, + eREPORT_ONLY_DESTROY=1<<5, + eACTIVATING =1<<6 + }; + + + //NodeIndex mNode1, mNode2; + EdgeType mEdgeType; + PxU16 mEdgeState; + + EdgeIndex mNextIslandEdge, mPrevIslandEdge; + + + + PX_FORCE_INLINE void setInserted() { mEdgeState |= (eINSERTED); } + + PX_FORCE_INLINE void clearInserted() { mEdgeState &= (~eINSERTED); } + + PX_FORCE_INLINE void clearDestroyed() { mEdgeState &=(~eDESTROYED);} + PX_FORCE_INLINE void setPendingDestroyed() { mEdgeState |= ePENDING_DESTROYED; } + PX_FORCE_INLINE void clearPendingDestroyed() { mEdgeState &= (~ePENDING_DESTROYED); } + PX_FORCE_INLINE void activateEdge() { mEdgeState |= eACTIVE; } + PX_FORCE_INLINE void deactivateEdge() { mEdgeState &= (~eACTIVE); } + PX_FORCE_INLINE void markInDirtyList() { mEdgeState |= (eIN_DIRTY_LIST); } + PX_FORCE_INLINE void clearInDirtyList() { mEdgeState &= (~eIN_DIRTY_LIST); } + PX_FORCE_INLINE void setReportOnlyDestroy() { mEdgeState |= (eREPORT_ONLY_DESTROY); } + PX_FORCE_INLINE void clearReportOnlyDestroy() { mEdgeState &= (~eREPORT_ONLY_DESTROY); } +public: + Edge() : mEdgeType(Edge::eCONTACT_MANAGER), mEdgeState(eDESTROYED), + mNextIslandEdge(IG_INVALID_EDGE), mPrevIslandEdge(IG_INVALID_EDGE) + { + } + PX_FORCE_INLINE bool isInserted() const { return !!(mEdgeState & eINSERTED);} + PX_FORCE_INLINE bool isDestroyed() const { return !!(mEdgeState & eDESTROYED); } + PX_FORCE_INLINE bool isPendingDestroyed() const { return !!(mEdgeState & ePENDING_DESTROYED); } + PX_FORCE_INLINE bool isActive() const { return !!(mEdgeState & eACTIVE); } + PX_FORCE_INLINE bool isInDirtyList() const { return !!(mEdgeState & eIN_DIRTY_LIST); } + PX_FORCE_INLINE EdgeType getEdgeType() const { return mEdgeType; } + //PX_FORCE_INLINE const NodeIndex getIndex1() const { return mNode1; } + //PX_FORCE_INLINE const NodeIndex getIndex2() const { return mNode2; } + PX_FORCE_INLINE bool isReportOnlyDestroy() { return !!(mEdgeState & eREPORT_ONLY_DESTROY); } +}; + +struct EdgeInstance +{ + EdgeInstanceIndex mNextEdge, mPrevEdge; //The next edge instance in this node's list of edge instances + + EdgeInstance() : mNextEdge(IG_INVALID_EDGE), mPrevEdge(IG_INVALID_EDGE) + { + } +}; + +template +class HandleManager +{ + Ps::Array mFreeHandles; + Handle mCurrentHandle; + +public: + + HandleManager() : mFreeHandles(PX_DEBUG_EXP("FreeHandles")), mCurrentHandle(0) + { + } + + ~HandleManager(){} + + Handle getHandle() + { + if(mFreeHandles.size()) + { + Handle handle = mFreeHandles.popBack(); + PX_ASSERT(isValidHandle(handle)); + return handle; + } + return mCurrentHandle++; + } + + bool isNotFreeHandle(Handle handle) + { + for(PxU32 a = 0; a < mFreeHandles.size(); ++a) + { + if(mFreeHandles[a] == handle) + return false; + } + return true; + } + + void freeHandle(Handle handle) + { + PX_ASSERT(isValidHandle(handle)); + PX_ASSERT(isNotFreeHandle(handle)); + if(handle == mCurrentHandle) + mCurrentHandle--; + else + mFreeHandles.pushBack(handle); + } + + bool isValidHandle(Handle handle) + { + return handle < mCurrentHandle; + } + + PX_FORCE_INLINE PxU32 getTotalHandles() const { return mCurrentHandle; } +}; + +class Node +{ + +public: + enum NodeType + { + eRIGID_BODY_TYPE, + eARTICULATION_TYPE, + eTYPE_COUNT + }; + enum State + { + eREADY_FOR_SLEEPING = 1u << 0, //! Ready to go to sleep + eACTIVE = 1u << 1, //! Active + eKINEMATIC = 1u << 2, //! Kinematic + eDELETED = 1u << 3, //! Is pending deletion + eDIRTY = 1u << 4, //! Is dirty (i.e. lost a connection) + eACTIVATING = 1u << 5, //! Is in the activating list + eDEACTIVATING = 1u << 6 //! It is being forced to deactivate this frame + }; + EdgeInstanceIndex mFirstEdgeIndex; + + PxU8 mFlags; + PxU8 mType; + PxU16 mStaticTouchCount; + //PxU32 mActiveNodeIndex; //! Look-up for this node in the active nodes list, activating list or deactivating list... + + NodeIndex mNextNode, mPrevNode; + + //A counter for the number of active references to this body. Whenever an edge is activated, this is incremented. + //Whenver an edge is deactivated, this is decremented. This is used for kinematic bodies to determine if they need + //to be in the active kinematics list + PxU32 mActiveRefCount; + + + //A node can correspond with either a rigid body or an articulation or softBody + union + { + PxsRigidBody* mRigidBody; + Dy::ArticulationV* mLLArticulation; + }; + + + + PX_FORCE_INLINE Node() : mFirstEdgeIndex(IG_INVALID_EDGE), mFlags(eDELETED), mType(eRIGID_BODY_TYPE), + mStaticTouchCount(0), mActiveRefCount(0), mRigidBody(NULL) + { + } + + PX_FORCE_INLINE ~Node() {} + + PX_FORCE_INLINE void reset() + { + mFirstEdgeIndex = IG_INVALID_EDGE; + mFlags = eDELETED; + mRigidBody = NULL; + mActiveRefCount = 0; + mStaticTouchCount = 0; + } + + PX_FORCE_INLINE void setRigidBody(PxsRigidBody* body) { mRigidBody = body; } + + PX_FORCE_INLINE PxsRigidBody* getRigidBody() const { return mRigidBody; } + + PX_FORCE_INLINE Dy::ArticulationV* getArticulation() const { return mLLArticulation; } + + + PX_FORCE_INLINE void setActive() { mFlags |= eACTIVE; } + PX_FORCE_INLINE void clearActive() { mFlags &= ~eACTIVE; } + + PX_FORCE_INLINE void setActivating() { mFlags |= eACTIVATING; } + PX_FORCE_INLINE void clearActivating() { mFlags &= ~eACTIVATING; } + + PX_FORCE_INLINE void setDeactivating() { mFlags |= eDEACTIVATING; } + PX_FORCE_INLINE void clearDeactivating() { mFlags &= (~eDEACTIVATING); } + + + //Activates a body/node. + PX_FORCE_INLINE void setIsReadyForSleeping() { mFlags |= eREADY_FOR_SLEEPING; } + + PX_FORCE_INLINE void clearIsReadyForSleeping(){ mFlags &= (~eREADY_FOR_SLEEPING);} + + PX_FORCE_INLINE void setIsDeleted(){mFlags |= eDELETED; } + + PX_FORCE_INLINE void setKinematicFlag() {PX_ASSERT(!isKinematic()); mFlags |= eKINEMATIC;} + + PX_FORCE_INLINE void clearKinematicFlag(){ PX_ASSERT(isKinematic()); mFlags &= (~eKINEMATIC);} + + PX_FORCE_INLINE void markDirty(){mFlags |= eDIRTY;} + + PX_FORCE_INLINE void clearDirty(){mFlags &= (~eDIRTY);} + +public: + + PX_FORCE_INLINE bool isActive() const { return !!(mFlags & eACTIVE); } + + PX_FORCE_INLINE bool isActiveOrActivating() const { return !!(mFlags & (eACTIVE | eACTIVATING)); } + + PX_FORCE_INLINE bool isActivating() const { return !!(mFlags & eACTIVATING); } + + PX_FORCE_INLINE bool isDeactivating() const { return !!(mFlags & eDEACTIVATING); } + + PX_FORCE_INLINE bool isKinematic() const { return !!(mFlags & eKINEMATIC); } + + PX_FORCE_INLINE bool isDeleted() const { return !!(mFlags & eDELETED); } + + PX_FORCE_INLINE bool isDirty() const { return !!(mFlags & eDIRTY); } + + PX_FORCE_INLINE bool isReadyForSleeping() const { return !!(mFlags & eREADY_FOR_SLEEPING); } + + PX_FORCE_INLINE NodeType getNodeType() const { return NodeType(mType); } + + friend class SimpleIslandManager; + +}; + +struct Island +{ + NodeIndex mRootNode; + NodeIndex mLastNode; + PxU32 mSize[Node::eTYPE_COUNT]; + PxU32 mActiveIndex; + + EdgeIndex mFirstEdge[Edge::eEDGE_TYPE_COUNT], mLastEdge[Edge::eEDGE_TYPE_COUNT]; + PxU32 mEdgeCount[Edge::eEDGE_TYPE_COUNT]; + + Island() : mActiveIndex(IG_INVALID_ISLAND) + { + for(PxU32 a = 0; a < Edge::eEDGE_TYPE_COUNT; ++a) + { + mFirstEdge[a] = IG_INVALID_EDGE; + mLastEdge[a] = IG_INVALID_EDGE; + mEdgeCount[a] = 0; + } + + for(PxU32 a = 0; a < Node::eTYPE_COUNT; ++a) + { + mSize[a] = 0; + } + } +}; + +struct TraversalState +{ + NodeIndex mNodeIndex; + PxU32 mCurrentIndex; + PxU32 mPrevIndex; + PxU32 mDepth; + + TraversalState() + { + } + + TraversalState(NodeIndex nodeIndex, PxU32 currentIndex, PxU32 prevIndex, PxU32 depth) : + mNodeIndex(nodeIndex), mCurrentIndex(currentIndex), mPrevIndex(prevIndex), mDepth(depth) + { + } +}; + +struct QueueElement +{ + TraversalState* mState; + PxU32 mHopCount; + + QueueElement() + { + } + + QueueElement(TraversalState* state, PxU32 hopCount) : mState(state), mHopCount(hopCount) + { + } +}; + +struct NodeComparator +{ + NodeComparator() + { + } + + bool operator() (const QueueElement& node0, const QueueElement& node1) const + { + return node0.mHopCount < node1.mHopCount; + } +private: + NodeComparator& operator = (const NodeComparator&); +}; + + +class IslandSim +{ + HandleManager mIslandHandles; //! Handle manager for islands + + Ps::Array mNodes; //! The nodes used in the constraint graph + Ps::Array mActiveNodeIndex; //! The active node index for each node + Cm::BlockArray mEdges; + Cm::BlockArray mEdgeInstances; //! Edges used to connect nodes in the constraint graph + Ps::Array mIslands; //! The array of islands + Ps::Array mIslandStaticTouchCount; //! Array of static touch counts per-island + + + Ps::Array mActiveNodes[Node::eTYPE_COUNT]; //! An array of active nodes + Ps::Array mActiveKinematicNodes; //! An array of active or referenced kinematic nodes + Ps::Array mActivatedEdges[Edge::eEDGE_TYPE_COUNT]; //! An array of active edges + + PxU32 mActiveEdgeCount[Edge::eEDGE_TYPE_COUNT]; + + Ps::Array mHopCounts; //! The observed number of "hops" from a given node to its root node. May be inaccurate but used to accelerate searches. + Ps::Array mFastRoute; //! The observed last route from a given node to the root node. We try the fast route (unless its broken) before trying others. + + Ps::Array mIslandIds; //! The array of per-node island ids + + Cm::BitMap mIslandAwake; //! Indicates whether an island is awake or not + + Cm::BitMap mActiveContactEdges; + + //An array of active islands + Ps::Array mActiveIslands; + + PxU32 mInitialActiveNodeCount[Edge::eEDGE_TYPE_COUNT]; + + Ps::Array mNodesToPutToSleep[Node::eTYPE_COUNT]; + + //Input to this frame's island management (changed nodes/edges) + + //Input list of changes observed this frame. If there no changes, no work to be done. + Ps::Array mDirtyEdges[Edge::eEDGE_TYPE_COUNT]; + //Dirty nodes. These nodes lost at least one connection so we need to recompute islands from these nodes + //Ps::Array mDirtyNodes; + Cm::BitMap mDirtyMap; + PxU32 mLastMapIndex; + + //An array of nodes to activate + Ps::Array mActivatingNodes; + Ps::Array mDestroyedEdges; + Ps::Array mTempIslandIds; + + + //Temporary, transient data used for traversals. TODO - move to PxsSimpleIslandManager. Or if we keep it here, we can + //process multiple island simulations in parallel + Cm::PriorityQueue + mPriorityQueue; //! Priority queue used for graph traversal + Ps::Array mVisitedNodes; //! The list of nodes visited in the current traversal + Cm::BitMap mVisitedState; //! Indicates whether a node has been visited + Ps::Array mIslandSplitEdges[Edge::eEDGE_TYPE_COUNT]; + + Ps::Array mDeactivatingEdges[Edge::eEDGE_TYPE_COUNT]; + + Ps::Array* mFirstPartitionEdges; + Cm::BlockArray& mEdgeNodeIndices; + Ps::Array* mDestroyedPartitionEdges; + + PxU32* mNpIndexPtr; + + PxU64 mContextId; + +public: + + IslandSim(Ps::Array* firstPartitionEdges, Cm::BlockArray& edgeNodeIndices, Ps::Array* destroyedPartitionEdges, PxU64 contextID); + ~IslandSim() {} + + void resize(const PxU32 nbNodes, const PxU32 nbContactManagers, const PxU32 nbConstraints); + + void addRigidBody(PxsRigidBody* body, bool isKinematic, bool isActive, NodeIndex nodeIndex); + + void addArticulation(Sc::ArticulationSim* articulation, Dy::ArticulationV* llArtic, bool isActive, NodeIndex nodeIndex); + + void addContactManager(PxsContactManager* manager, NodeIndex nodeHandle1, NodeIndex nodeHandle2, EdgeIndex handle); + + void addConstraint(Dy::Constraint* constraint, NodeIndex nodeHandle1, NodeIndex nodeHandle2, EdgeIndex handle); + + void activateNode(NodeIndex index); + void deactivateNode(NodeIndex index); + void putNodeToSleep(NodeIndex index); + + void removeConnection(EdgeIndex edgeIndex); + + PX_FORCE_INLINE PxU32 getNbNodes() const { return mNodes.size(); } + + PX_FORCE_INLINE PxU32 getNbActiveNodes(Node::NodeType type) const { return mActiveNodes[type].size(); } + + PX_FORCE_INLINE const NodeIndex* getActiveNodes(Node::NodeType type) const { return mActiveNodes[type].begin(); } + + PX_FORCE_INLINE PxU32 getNbActiveKinematics() const { return mActiveKinematicNodes.size(); } + + PX_FORCE_INLINE const NodeIndex* getActiveKinematics() const { return mActiveKinematicNodes.begin(); } + + PX_FORCE_INLINE PxU32 getNbNodesToActivate(Node::NodeType type) const { return mActiveNodes[type].size() - mInitialActiveNodeCount[type]; } + + PX_FORCE_INLINE const NodeIndex* getNodesToActivate(Node::NodeType type) const { return mActiveNodes[type].begin() + mInitialActiveNodeCount[type]; } + + PX_FORCE_INLINE PxU32 getNbNodesToDeactivate(Node::NodeType type) const { return mNodesToPutToSleep[type].size(); } + + PX_FORCE_INLINE const NodeIndex* getNodesToDeactivate(Node::NodeType type) const { return mNodesToPutToSleep[type].begin(); } + + PX_FORCE_INLINE PxU32 getNbActivatedEdges(Edge::EdgeType type) const { return mActivatedEdges[type].size(); } + + PX_FORCE_INLINE const EdgeIndex* getActivatedEdges(Edge::EdgeType type) const { return mActivatedEdges[type].begin(); } + + PX_FORCE_INLINE PxU32 getNbActiveEdges(Edge::EdgeType type) const { return mActiveEdgeCount[type]; } + + PX_FORCE_INLINE PartitionEdge* getFirstPartitionEdge(IG::EdgeIndex edgeIndex) const { return (*mFirstPartitionEdges)[edgeIndex]; } + PX_FORCE_INLINE void setFirstPartitionEdge(IG::EdgeIndex edgeIndex, PartitionEdge* partitionEdge) { (*mFirstPartitionEdges)[edgeIndex] = partitionEdge; } + + //PX_FORCE_INLINE const EdgeIndex* getActiveEdges(Edge::EdgeType type) const { return mActiveEdges[type].begin(); } + + PX_FORCE_INLINE PxsRigidBody* getRigidBody(NodeIndex nodeIndex) const + { + const Node& node = mNodes[nodeIndex.index()]; + PX_ASSERT(node.mType == Node::eRIGID_BODY_TYPE); + return node.mRigidBody; + } + + PX_FORCE_INLINE Dy::ArticulationV* getLLArticulation(NodeIndex nodeIndex) const + { + const Node& node = mNodes[nodeIndex.index()]; + PX_ASSERT(node.mType == Node::eARTICULATION_TYPE); + return node.mLLArticulation; + } + + PX_FORCE_INLINE void clearDeactivations() + { + mNodesToPutToSleep[0].forceSize_Unsafe(0); + mNodesToPutToSleep[1].forceSize_Unsafe(0); + + mDeactivatingEdges[0].forceSize_Unsafe(0); + mDeactivatingEdges[1].forceSize_Unsafe(0); + } + + PX_FORCE_INLINE const Island& getIsland(IG::IslandId islandIndex) const { return mIslands[islandIndex]; } + + PX_FORCE_INLINE PxU32 getNbActiveIslands() const { return mActiveIslands.size(); } + PX_FORCE_INLINE const IslandId* getActiveIslands() const { return mActiveIslands.begin(); } + + PX_FORCE_INLINE PxU32 getNbDeactivatingEdges(const IG::Edge::EdgeType edgeType) const { return mDeactivatingEdges[edgeType].size(); } + PX_FORCE_INLINE const EdgeIndex* getDeactivatingEdges(const IG::Edge::EdgeType edgeType) const { return mDeactivatingEdges[edgeType].begin(); } + + PX_FORCE_INLINE PxU32 getNbDestroyedEdges() const { return mDestroyedEdges.size(); } + PX_FORCE_INLINE const EdgeIndex* getDestroyedEdges() const { return mDestroyedEdges.begin(); } + + PX_FORCE_INLINE PxU32 getNbDestroyedPartitionEdges() const { return mDestroyedPartitionEdges->size(); } + PX_FORCE_INLINE const PartitionEdge*const * getDestroyedPartitionEdges() const { return mDestroyedPartitionEdges->begin(); } + PX_FORCE_INLINE PartitionEdge** getDestroyedPartitionEdges() { return mDestroyedPartitionEdges->begin(); } + + PX_FORCE_INLINE PxU32 getNbDirtyEdges(IG::Edge::EdgeType type) const { return mDirtyEdges[type].size(); } + PX_FORCE_INLINE const EdgeIndex* getDirtyEdges(IG::Edge::EdgeType type) const { return mDirtyEdges[type].begin(); } + + PX_FORCE_INLINE const Edge& getEdge(const EdgeIndex edgeIndex) const { return mEdges[edgeIndex]; } + + PX_FORCE_INLINE Edge& getEdge(const EdgeIndex edgeIndex) { return mEdges[edgeIndex]; } + + PX_FORCE_INLINE const Node& getNode(const NodeIndex& nodeIndex) const { return mNodes[nodeIndex.index()]; } + + PX_FORCE_INLINE const Island& getIsland(const NodeIndex& nodeIndex) const { PX_ASSERT(mIslandIds[nodeIndex.index()] != IG_INVALID_ISLAND); return mIslands[mIslandIds[nodeIndex.index()]]; } + + PX_FORCE_INLINE PxU32 getIslandStaticTouchCount(const NodeIndex& nodeIndex) const { PX_ASSERT(mIslandIds[nodeIndex.index()] != IG_INVALID_ISLAND); return mIslandStaticTouchCount[mIslandIds[nodeIndex.index()]]; } + + PX_FORCE_INLINE const Cm::BitMap& getActiveContactManagerBitmap() const { return mActiveContactEdges; } + + PX_FORCE_INLINE PxU32 getActiveNodeIndex(const NodeIndex& nodeIndex) const { PxU32 activeNodeIndex = mActiveNodeIndex[nodeIndex.index()]; return activeNodeIndex;} + + PX_FORCE_INLINE const PxU32* getActiveNodeIndex() const { return mActiveNodeIndex.begin(); } + + PX_FORCE_INLINE PxU32 getNbActiveNodeIndex() const { return mActiveNodeIndex.size(); } + + void setKinematic(IG::NodeIndex nodeIndex); + + void setDynamic(IG::NodeIndex nodeIndex); + + PX_FORCE_INLINE void setEdgeNodeIndexPtr(PxU32* ptr) { mNpIndexPtr = ptr; } + + PX_FORCE_INLINE NodeIndex getNodeIndex1(IG::EdgeIndex index) const { return mEdgeNodeIndices[2 * index]; } + PX_FORCE_INLINE NodeIndex getNodeIndex2(IG::EdgeIndex index) const { return mEdgeNodeIndices[2 * index + 1]; } + + PX_FORCE_INLINE PxU32* getEdgeNodeIndexPtr() const { return mNpIndexPtr; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextId; } + + PxU32 getNbIslands() const { return mIslandStaticTouchCount.size(); } + + const PxU32* getIslandStaticTouchCount() const { return mIslandStaticTouchCount.begin(); } + + const PxU32* getIslandIds() const { return mIslandIds.begin(); } + + bool checkInternalConsistency(); + +private: + + void insertNewEdges(); + void removeDestroyedEdges(); + void wakeIslands(); + void wakeIslands2(); + void processNewEdges(); + void processLostEdges(Ps::Array& destroyedNodes, bool allowDeactivation, bool permitKinematicDeactivation, PxU32 dirtyNodeLimit); + + void removeConnectionInternal(EdgeIndex edgeIndex); + + void addConnection(NodeIndex nodeHandle1, NodeIndex nodeHandle2, Edge::EdgeType edgeType, EdgeIndex handle); + + void addConnectionToGraph(EdgeIndex index); + void removeConnectionFromGraph(EdgeIndex edgeIndex); + void connectEdge(EdgeInstance& instance, EdgeInstanceIndex edgeIndex, Node& source, NodeIndex destination); + void disconnectEdge(EdgeInstance& instance, EdgeInstanceIndex edgeIndex, Node& node); + + //Merges 2 islands together. The returned id is the id of the merged island + IslandId mergeIslands(IslandId island0, IslandId island1, NodeIndex node0, NodeIndex node1); + + void mergeIslandsInternal(Island& island0, Island& island1, IslandId islandId0, IslandId islandId1, NodeIndex node0, NodeIndex node1); + + + IslandSim& operator = (const IslandSim&); + IslandSim(const IslandSim&); + + void unwindRoute(PxU32 traversalIndex, NodeIndex lastNode, PxU32 hopCount, IslandId id); + + void activateIsland(IslandId island); + + void deactivateIsland(IslandId island); + + bool canFindRoot(NodeIndex startNode, NodeIndex targetNode, Ps::Array* visitedNodes); + + bool tryFastPath(NodeIndex startNode, NodeIndex targetNode, IslandId islandId); + + bool findRoute(NodeIndex startNode, NodeIndex targetNode, IslandId islandId); + + bool isPathTo(NodeIndex startNode, NodeIndex targetNode); + + void addNode(bool isActive, bool isKinematic, Node::NodeType type, NodeIndex nodeIndex); + + void activateNodeInternal(NodeIndex index); + void deactivateNodeInternal(NodeIndex index); + + PX_FORCE_INLINE void notifyReadyForSleeping(const NodeIndex nodeIndex) + { + Node& node = mNodes[nodeIndex.index()]; + //PX_ASSERT(node.isActive()); + node.setIsReadyForSleeping(); + } + + PX_FORCE_INLINE void notifyNotReadyForSleeping(const NodeIndex nodeIndex) + { + Node& node = mNodes[nodeIndex.index()]; + PX_ASSERT(node.isActive() || node.isActivating()); + node.clearIsReadyForSleeping(); + } + + PX_FORCE_INLINE void markIslandActive(IslandId islandId) + { + Island& island = mIslands[islandId]; + PX_ASSERT(!mIslandAwake.test(islandId)); + PX_ASSERT(island.mActiveIndex == IG_INVALID_ISLAND); + + mIslandAwake.set(islandId); + island.mActiveIndex = mActiveIslands.size(); + mActiveIslands.pushBack(islandId); + } + + PX_FORCE_INLINE void markIslandInactive(IslandId islandId) + { + Island& island = mIslands[islandId]; + PX_ASSERT(mIslandAwake.test(islandId)); + PX_ASSERT(island.mActiveIndex != IG_INVALID_ISLAND); + PX_ASSERT(mActiveIslands[island.mActiveIndex] == islandId); + IslandId replaceId = mActiveIslands[mActiveIslands.size()-1]; + PX_ASSERT(mIslandAwake.test(replaceId)); + Island& replaceIsland = mIslands[replaceId]; + replaceIsland.mActiveIndex = island.mActiveIndex; + mActiveIslands[island.mActiveIndex] = replaceId; + mActiveIslands.forceSize_Unsafe(mActiveIslands.size()-1); + island.mActiveIndex = IG_INVALID_ISLAND; + mIslandAwake.reset(islandId); + } + + PX_FORCE_INLINE void markKinematicActive(NodeIndex index) + { + Node& node = mNodes[index.index()]; + PX_ASSERT(node.isKinematic()); + if(node.mActiveRefCount == 0 && mActiveNodeIndex[index.index()] == IG_INVALID_NODE) + { + //PX_ASSERT(mActiveNodeIndex[index.index()] == IG_INVALID_NODE); + //node.mActiveNodeIndex = mActiveKinematicNodes.size(); + mActiveNodeIndex[index.index()] = mActiveKinematicNodes.size(); + mActiveKinematicNodes.pushBack(index); + } + } + + PX_FORCE_INLINE void markKinematicInactive(NodeIndex index) + { + Node& node = mNodes[index.index()]; + PX_ASSERT(node.isKinematic()); + PX_ASSERT(mActiveNodeIndex[index.index()] != IG_INVALID_NODE); + PX_ASSERT(mActiveKinematicNodes[mActiveNodeIndex[index.index()]].index() == index.index()); + + if(node.mActiveRefCount == 0) + { + //Only remove from active kinematic list if it has no active contacts referencing it *and* it is asleep + if(mActiveNodeIndex[index.index()] != IG_INVALID_NODE) + { + //Need to verify active node index because there is an edge case where a node could be woken, then put to + //sleep in the same frame. This would mean that it would not have an active index at this stage. + NodeIndex replaceIndex = mActiveKinematicNodes.back(); + PX_ASSERT(mActiveNodeIndex[replaceIndex.index()] == mActiveKinematicNodes.size()-1); + mActiveNodeIndex[replaceIndex.index()] = mActiveNodeIndex[index.index()]; + mActiveKinematicNodes[mActiveNodeIndex[index.index()]] = replaceIndex; + mActiveKinematicNodes.forceSize_Unsafe(mActiveKinematicNodes.size()-1); + mActiveNodeIndex[index.index()] = IG_INVALID_NODE; + } + } + } + + PX_FORCE_INLINE void markActive(NodeIndex index) + { + Node& node = mNodes[index.index()]; + PX_ASSERT(!node.isKinematic()); + PX_ASSERT(mActiveNodeIndex[index.index()] == IG_INVALID_NODE); + mActiveNodeIndex[index.index()] = mActiveNodes[node.mType].size(); + mActiveNodes[node.mType].pushBack(index); + } + + PX_FORCE_INLINE void markInactive(NodeIndex index) + { + Node& node = mNodes[index.index()]; + + PX_ASSERT(!node.isKinematic()); + PX_ASSERT(mActiveNodeIndex[index.index()] != IG_INVALID_NODE); + + Ps::Array& activeNodes = mActiveNodes[node.mType]; + + PX_ASSERT(activeNodes[mActiveNodeIndex[index.index()]].index() == index.index()); + const PxU32 initialActiveNodeCount = mInitialActiveNodeCount[node.mType]; + + if(mActiveNodeIndex[index.index()] < initialActiveNodeCount) + { + //It's in the initial active node set. We retain a list of active nodes, where the existing active nodes + //are at the beginning of the array and the newly activated nodes are at the end of the array... + //The solution is to move the node to the end of the initial active node list in this case + PxU32 activeNodeIndex = mActiveNodeIndex[index.index()]; + NodeIndex replaceIndex = activeNodes[initialActiveNodeCount-1]; + PX_ASSERT(mActiveNodeIndex[replaceIndex.index()] == initialActiveNodeCount-1); + mActiveNodeIndex[index.index()] = mActiveNodeIndex[replaceIndex.index()]; + mActiveNodeIndex[replaceIndex.index()] = activeNodeIndex; + activeNodes[activeNodeIndex] = replaceIndex; + activeNodes[mActiveNodeIndex[index.index()]] = index; + mInitialActiveNodeCount[node.mType]--; + } + + PX_ASSERT(!node.isKinematic()); + PX_ASSERT(mActiveNodeIndex[index.index()] != IG_INVALID_NODE); + PX_ASSERT(activeNodes[mActiveNodeIndex[index.index()]].index() == index.index()); + + NodeIndex replaceIndex = activeNodes.back(); + PX_ASSERT(mActiveNodeIndex[replaceIndex.index()] == activeNodes.size()-1); + mActiveNodeIndex[replaceIndex.index()] = mActiveNodeIndex[index.index()]; + activeNodes[mActiveNodeIndex[index.index()]] = replaceIndex; + activeNodes.forceSize_Unsafe(activeNodes.size()-1); + mActiveNodeIndex[index.index()] = IG_INVALID_NODE; + } + + PX_FORCE_INLINE void markEdgeActive(EdgeIndex index) + { + Edge& edge = mEdges[index]; + + PX_ASSERT((edge.mEdgeState & Edge::eACTIVATING) == 0); + + edge.mEdgeState |= Edge::eACTIVATING; + + mActivatedEdges[edge.mEdgeType].pushBack(index); + + mActiveEdgeCount[edge.mEdgeType]++; + + //Set the active bit... + if(edge.mEdgeType == Edge::eCONTACT_MANAGER) + mActiveContactEdges.set(index); + + NodeIndex nodeIndex1 = mEdgeNodeIndices[2 * index]; + NodeIndex nodeIndex2 = mEdgeNodeIndices[2 * index + 1]; + + if (nodeIndex1.index() != IG_INVALID_NODE && nodeIndex2.index() != IG_INVALID_NODE) + { + PX_ASSERT((!mNodes[nodeIndex1.index()].isKinematic()) || (!mNodes[nodeIndex2.index()].isKinematic()) || edge.getEdgeType() == IG::Edge::eCONTACT_MANAGER); + { + Node& node = mNodes[nodeIndex1.index()]; + + if(node.mActiveRefCount == 0 && node.isKinematic() && !(node.isActive() || node.isActivating())) + { + //Add to active kinematic list + markKinematicActive(nodeIndex1); + } + node.mActiveRefCount++; + } + + { + Node& node = mNodes[nodeIndex2.index()]; + if(node.mActiveRefCount == 0 && node.isKinematic() && !(node.isActive() || node.isActivating())) + { + //Add to active kinematic list + markKinematicActive(nodeIndex2); + } + node.mActiveRefCount++; + } + } + + } + + void removeEdgeFromActivatingList(EdgeIndex index); + + PX_FORCE_INLINE void removeEdgeFromIsland(Island& island, EdgeIndex edgeIndex) + { + Edge& edge = mEdges[edgeIndex]; + if(edge.mNextIslandEdge != IG_INVALID_EDGE) + { + PX_ASSERT(mEdges[edge.mNextIslandEdge].mPrevIslandEdge == edgeIndex); + mEdges[edge.mNextIslandEdge].mPrevIslandEdge = edge.mPrevIslandEdge; + } + else + { + PX_ASSERT(island.mLastEdge[edge.mEdgeType] == edgeIndex); + island.mLastEdge[edge.mEdgeType] = edge.mPrevIslandEdge; + } + + if(edge.mPrevIslandEdge != IG_INVALID_EDGE) + { + PX_ASSERT(mEdges[edge.mPrevIslandEdge].mNextIslandEdge == edgeIndex); + mEdges[edge.mPrevIslandEdge].mNextIslandEdge = edge.mNextIslandEdge; + } + else + { + PX_ASSERT(island.mFirstEdge[edge.mEdgeType] == edgeIndex); + island.mFirstEdge[edge.mEdgeType] = edge.mNextIslandEdge; + } + + island.mEdgeCount[edge.mEdgeType]--; + edge.mNextIslandEdge = edge.mPrevIslandEdge = IG_INVALID_EDGE; + } + + PX_FORCE_INLINE void addEdgeToIsland(Island& island, EdgeIndex edgeIndex) + { + Edge& edge = mEdges[edgeIndex]; + PX_ASSERT(edge.mNextIslandEdge == IG_INVALID_EDGE && edge.mPrevIslandEdge == IG_INVALID_EDGE); + + if(island.mLastEdge[edge.mEdgeType] != IG_INVALID_EDGE) + { + PX_ASSERT(mEdges[island.mLastEdge[edge.mEdgeType]].mNextIslandEdge == IG_INVALID_EDGE); + mEdges[island.mLastEdge[edge.mEdgeType]].mNextIslandEdge = edgeIndex; + } + else + { + PX_ASSERT(island.mFirstEdge[edge.mEdgeType] == IG_INVALID_EDGE); + island.mFirstEdge[edge.mEdgeType] = edgeIndex; + } + + edge.mPrevIslandEdge = island.mLastEdge[edge.mEdgeType]; + island.mLastEdge[edge.mEdgeType] = edgeIndex; + island.mEdgeCount[edge.mEdgeType]++; + } + + PX_FORCE_INLINE void removeNodeFromIsland(Island& island, NodeIndex nodeIndex) + { + Node& node = mNodes[nodeIndex.index()]; + if(node.mNextNode.isValid()) + { + PX_ASSERT(mNodes[node.mNextNode.index()].mPrevNode.index() == nodeIndex.index()); + mNodes[node.mNextNode.index()].mPrevNode = node.mPrevNode; + } + else + { + PX_ASSERT(island.mLastNode.index() == nodeIndex.index()); + island.mLastNode = node.mPrevNode; + } + + if(node.mPrevNode.isValid()) + { + PX_ASSERT(mNodes[node.mPrevNode.index()].mNextNode.index() == nodeIndex.index()); + mNodes[node.mPrevNode.index()].mNextNode = node.mNextNode; + } + else + { + PX_ASSERT(island.mRootNode.index() == nodeIndex.index()); + island.mRootNode = node.mNextNode; + } + + island.mSize[node.mType]--; + + node.mNextNode = NodeIndex(); node.mPrevNode = NodeIndex(); + } + + //void setEdgeConnectedInternal(EdgeIndex edgeIndex); + + //void setEdgeDisconnectedInternal(EdgeIndex edgeIndex); + + friend class SimpleIslandManager; + friend class ThirdPassTask; + +}; + + +} + + +struct PartitionIndexData +{ + PxU16 mPartitionIndex; //! The current partition this edge is in. Used to find the edge efficiently. PxU8 is probably too small (256 partitions max) but PxU16 should be more than enough + PxU8 mPatchIndex; //! The patch index for this partition edge. There may be multiple entries for a given edge if there are multiple patches. + PxU8 mCType; //! The type of constraint this is + PxU32 mPartitionEntryIndex; //! index of partition edges for this partition +}; + +struct PartitionNodeData +{ + IG::NodeIndex mNodeIndex0; + IG::NodeIndex mNodeIndex1; + PxU32 mNextIndex0; + PxU32 mNextIndex1; +}; + + +#define INVALID_PARTITION_INDEX 0xFFFF + +struct PartitionEdge +{ + IG::EdgeIndex mEdgeIndex; //! The edge index into the island manager. Used to identify the contact manager/constraint + IG::NodeIndex mNode0; //! The node index for node 0. Can be obtained from the edge index alternatively + IG::NodeIndex mNode1; //! The node idnex for node 1. Can be obtained from the edge index alternatively + bool mInfiniteMass0; //! Whether body 0 is kinematic + bool mInfiniteMass1; //! Whether body 1 is kinematic + + PartitionEdge* mNextPatch; //! for the contact manager has more than 1 patch, we have next patch's edge and previous patch's edge to connect to this edge + + PxU32 mUniqueIndex; //! a unique ID for this edge + + PartitionEdge() : mEdgeIndex(IG_INVALID_EDGE), mInfiniteMass0(false), + mInfiniteMass1(false), mNextPatch(NULL) + { + } +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h b/src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h new file mode 100644 index 000000000..9990dcb77 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h @@ -0,0 +1,49 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_KERNEL_WRANGLER_H +#define PXS_KERNEL_WRANGLER_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + class KernelWrangler; + class PxGpuDispatcher; + class PxErrorCallback; + + class PxsKernelWranglerManager + { + public: + virtual ~PxsKernelWranglerManager(){} + virtual KernelWrangler* getKernelWrangler() = 0; + }; +} +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h b/src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h new file mode 100644 index 000000000..83a5e1337 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h @@ -0,0 +1,143 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_MATERIALCOMBINER_H +#define PXS_MATERIALCOMBINER_H + +#include "PxsMaterialCore.h" + +namespace physx +{ + + class PxsMaterialCombiner + { + public: + + class PxsCombinedMaterial + { + public: + PxReal staFriction; + PxReal dynFriction; + PxU32 flags; //PxMaterialFlag::eDISABLE_FRICTION, PxMaterialFlag::eDISABLE_STRONG_FRICTION. + }; + + static PxReal combineRestitution(const PxsMaterialData& material0, const PxsMaterialData& material1); + + PxsMaterialCombiner(PxReal staticFrictionScaling, PxReal dynamicFrictionScaling); + + PxsCombinedMaterial combineIsotropicFriction(const PxsMaterialData& material0, const PxsMaterialData& material1); + + //ML:: move this function to header file to avoid LHS in Xbox + PX_FORCE_INLINE void combineIsotropicFriction(const PxsMaterialData& mat0, const PxsMaterialData& mat1, PxReal& dynamicFriction, PxReal& staticFriction, PxU32& flags) + { + + const PxU32 combineFlags= (mat0.flags | mat1.flags); //& (PxMaterialFlag::eDISABLE_STRONG_FRICTION|PxMaterialFlag::eDISABLE_FRICTION); //eventually set DisStrongFric flag, lower all others. + + if (!(combineFlags & PxMaterialFlag::eDISABLE_FRICTION)) + { + const PxI32 fictionCombineMode = PxMax(mat0.getFrictionCombineMode(), mat1.getFrictionCombineMode()); + PxReal dynFriction = 0.f; + PxReal staFriction = 0.f; + + + switch (fictionCombineMode) + { + case PxCombineMode::eAVERAGE: + dynFriction = 0.5f * (mat0.dynamicFriction + mat1.dynamicFriction); + staFriction = 0.5f * (mat0.staticFriction + mat1.staticFriction); + break; + case PxCombineMode::eMIN: + dynFriction = PxMin(mat0.dynamicFriction, mat1.dynamicFriction); + staFriction = PxMin(mat0.staticFriction, mat1.staticFriction); + break; + case PxCombineMode::eMULTIPLY: + dynFriction = (mat0.dynamicFriction * mat1.dynamicFriction); + staFriction = (mat0.staticFriction * mat1.staticFriction); + break; + case PxCombineMode::eMAX: + dynFriction = PxMax(mat0.dynamicFriction, mat1.dynamicFriction); + staFriction = PxMax(mat0.staticFriction, mat1.staticFriction); + break; + } + + dynFriction*=mDynamicFrictionScaling; + staFriction*=mStaticFrictionScaling; + //isotropic case + const PxReal fDynFriction = PxMax(dynFriction, 0.f); + + const PxReal fStaFriction = physx::intrinsics::fsel(staFriction - fDynFriction, staFriction*mStaticFrictionScaling, fDynFriction); + /*dest.dynFriction = fDynFriction; + dest.staFriction = fStaFriction;*/ + + dynamicFriction = fDynFriction; + staticFriction = fStaFriction; + flags = combineFlags; + } + else + { + /* dest.flags |= PxMaterialFlag::eDISABLE_STRONG_FRICTION; + dest.staFriction = 0.0f; + dest.dynFriction = 0.0f;*/ + flags = (combineFlags | PxMaterialFlag::eDISABLE_STRONG_FRICTION); + dynamicFriction = 0.f; + staticFriction = 0.f; + } + + } + + + //private: + protected: + static PX_FORCE_INLINE PxReal combineScalars(PxReal a, PxReal b, PxI32 nxCombineMode) + { + switch (nxCombineMode) + { + case PxCombineMode::eAVERAGE: + return 0.5f * (a + b); + case PxCombineMode::eMIN: + return PxMin(a,b); + case PxCombineMode::eMULTIPLY: + return a * b; + case PxCombineMode::eMAX: + return PxMax(a,b); + default: + /* Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Sc::MaterialCombiner::combineScalars(): unknown combine mode");*/ + return PxReal(0); + } + } + + PxReal mStaticFrictionScaling, mDynamicFrictionScaling; + + }; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h new file mode 100644 index 000000000..9cf8d7b78 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h @@ -0,0 +1,61 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_MEMORY_MANAGER_H +#define PXS_MEMORY_MANAGER_H + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxSimpleTypes.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + namespace shdfnd + { + class VirtualAllocatorCallback; + } + + class PxGpuDispatcher; + + class PxsMemoryManager + { + public: + virtual ~PxsMemoryManager(){} + virtual Ps::VirtualAllocatorCallback* createHostMemoryAllocator(const PxU32 gpuComputeVersion = 0) = 0; + virtual Ps::VirtualAllocatorCallback* createDeviceMemoryAllocator(const PxU32 gpuComputeVersion = 0) = 0; + + virtual void destroyMemoryAllocator() = 0; + + }; + + PxsMemoryManager* createMemoryManager(); +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h b/src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h new file mode 100644 index 000000000..d06467212 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h @@ -0,0 +1,143 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_NPHASE_IMPLEMENTATION_CONTEXT_H +#define PXS_NPHASE_IMPLEMENTATION_CONTEXT_H + +#include "PxvNphaseImplementationContext.h" +#include "PxsContactManagerState.h" +#include "PxcNpCache.h" + +namespace physx +{ + +struct PxsContactManagers : PxsContactManagerBase +{ + Ps::Array mOutputContactManagers; + Ps::Array mContactManagerMapping; + Ps::Array mCaches; + + + PxsContactManagers(const PxU32 bucketId) : PxsContactManagerBase(bucketId), + mOutputContactManagers(PX_DEBUG_EXP("mOutputContactManagers")), + mContactManagerMapping(PX_DEBUG_EXP("mContactManagerMapping")), + mCaches(PX_DEBUG_EXP("mCaches")) + { + } + + void clear() + { + mOutputContactManagers.forceSize_Unsafe(0); + mContactManagerMapping.forceSize_Unsafe(0); + mCaches.forceSize_Unsafe(0); + + } +private: + PX_NOCOPY(PxsContactManagers) +}; + + +class PxsNphaseImplementationContext: public PxvNphaseImplementationContextUsableAsFallback +{ +public: + static PxsNphaseImplementationContext* create(PxsContext& context, IG::IslandSim* islandSim); + + PxsNphaseImplementationContext(PxsContext& context, IG::IslandSim* islandSim, PxU32 index = 0): PxvNphaseImplementationContextUsableAsFallback(context), mNarrowPhasePairs(index), mNewNarrowPhasePairs(index), + mModifyCallback(NULL), mIslandSim(islandSim) {} + virtual void destroy(); + virtual void updateContactManager(PxReal dt, bool hasBoundsArrayChanged, bool hasContactDistanceChanged, PxBaseTask* continuation, PxBaseTask* firstPassContinuation); + virtual void postBroadPhaseUpdateContactManager() {} + virtual void secondPassUpdateContactManager(PxReal dt, PxBaseTask* continuation); + + virtual void registerContactManager(PxsContactManager* cm, PxI32 touching, PxU32 numPatches); + virtual void registerContactManagers(PxsContactManager** cm, PxU32 nbContactManagers, PxU32 maxContactManagerId); + virtual void unregisterContactManager(PxsContactManager* cm); + virtual void unregisterContactManagerFallback(PxsContactManager* cm, PxsContactManagerOutput* cmOutputs); + + + virtual void refreshContactManager(PxsContactManager* cm); + virtual void refreshContactManagerFallback(PxsContactManager* cm, PxsContactManagerOutput* cmOutputs); + + virtual void registerShape(const PxsShapeCore& shapeCore); + + virtual void updateShapeMaterial(const PxsShapeCore& shapeCore); + virtual void updateShapeContactOffset(const PxsShapeCore& shapeCore); + + virtual void unregisterShape(const PxsShapeCore& shapeCore); + + virtual void registerMaterial(const PxsMaterialCore& materialCore); + virtual void updateMaterial(const PxsMaterialCore& materialCore); + virtual void unregisterMaterial(const PxsMaterialCore& materialCore); + + virtual void appendContactManagers(); + virtual void appendContactManagersFallback(PxsContactManagerOutput* cmOutputs); + + virtual void removeContactManagersFallback(PxsContactManagerOutput* cmOutputs); + + virtual void setContactModifyCallback(PxContactModifyCallback* callback) { mModifyCallback = callback; } + + virtual PxsContactManagerOutputIterator getContactManagerOutputs(); + + virtual PxsContactManagerOutput& getNewContactManagerOutput(PxU32 npIndex); + + virtual PxsContactManagerOutput* getGPUContactManagerOutputBase() { return NULL; } + + virtual void acquireContext(){} + virtual void releaseContext(){} + virtual void preallocateNewBuffers(PxU32 /*nbNewPairs*/, PxU32 /*maxIndex*/) { /*TODO - implement if it's useful to do so*/} + + void processContactManager(PxReal dt, PxsContactManagerOutput* cmOutputs, PxBaseTask* continuation); + void processContactManagerSecondPass(PxReal dt, PxBaseTask* continuation); + void fetchUpdateContactManager() {} + + + + void startNarrowPhaseTasks() {} + + + + Ps::Array mRemovedContactManagers; + PxsContactManagers mNarrowPhasePairs; + PxsContactManagers mNewNarrowPhasePairs; + + PxContactModifyCallback* mModifyCallback; + + IG::IslandSim* mIslandSim; + +private: + + void unregisterContactManagerInternal(PxU32 npIndex, PxsContactManagers& managers, PxsContactManagerOutput* cmOutputs); + + PX_NOCOPY(PxsNphaseImplementationContext) +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h b/src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h new file mode 100644 index 000000000..571cba466 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h @@ -0,0 +1,169 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_BODYATOM_H +#define PXS_BODYATOM_H + +#include "PxcRigidBody.h" +#include "PxvDynamics.h" + +namespace physx +{ + +class PxsRigidBody : public PxcRigidBody +{ + public: + PX_FORCE_INLINE PxsRigidBody(PxsBodyCore* core) : PxcRigidBody(core) { } + PX_FORCE_INLINE ~PxsRigidBody() {} + + PX_FORCE_INLINE const PxTransform& getPose() const { PX_ASSERT(mCore->body2World.isSane()); return mCore->body2World; } + + //PX_FORCE_INLINE const Cm::SpatialVector& getAccelerationV() const { return mAcceleration; } + //PX_FORCE_INLINE void setAccelerationV(const Cm::SpatialVector& v) { mAcceleration = v; } + + PX_FORCE_INLINE const PxVec3& getLinearVelocity() const { PX_ASSERT(mCore->linearVelocity.isFinite()); return mCore->linearVelocity; } + PX_FORCE_INLINE const PxVec3& getAngularVelocity() const { PX_ASSERT(mCore->angularVelocity.isFinite()); return mCore->angularVelocity; } + + PX_FORCE_INLINE void setVelocity(const PxVec3& linear, + const PxVec3& angular) { PX_ASSERT(linear.isFinite()); PX_ASSERT(angular.isFinite()); + mCore->linearVelocity = linear; + mCore->angularVelocity = angular; } + PX_FORCE_INLINE void setLinearVelocity(const PxVec3& linear) { PX_ASSERT(linear.isFinite()); mCore->linearVelocity = linear; } + PX_FORCE_INLINE void setAngularVelocity(const PxVec3& angular) { PX_ASSERT(angular.isFinite()); mCore->angularVelocity = angular; } + + PX_FORCE_INLINE void constrainLinearVelocity(); + PX_FORCE_INLINE void constrainAngularVelocity(); + + PX_FORCE_INLINE PxU32 getIterationCounts() { return mCore->solverIterationCounts; } + + PX_FORCE_INLINE PxReal getReportThreshold() const { return mCore->contactReportThreshold; } + + // AP newccd todo: merge into get both velocities, compute inverse transform once, precompute mLastTransform.getInverse() + PX_FORCE_INLINE PxVec3 getLinearMotionVelocity(PxReal invDt) const { + // delta(t0(x))=t1(x) + // delta(t0(t0`(x)))=t1(t0`(x)) + // delta(x)=t1(t0`(x)) + PxVec3 deltaP = mCore->body2World.p - getLastCCDTransform().p; + return deltaP * invDt; + } + PX_FORCE_INLINE PxVec3 getAngularMotionVelocity(PxReal invDt) const { + PxQuat deltaQ = mCore->body2World.q * getLastCCDTransform().q.getConjugate(); + PxVec3 axis; + PxReal angle; + deltaQ.toRadiansAndUnitAxis(angle, axis); + return axis * angle * invDt; + } + PX_FORCE_INLINE PxVec3 getLinearMotionVelocity(PxReal dt, const PxsBodyCore* PX_RESTRICT bodyCore) const { + // delta(t0(x))=t1(x) + // delta(t0(t0`(x)))=t1(t0`(x)) + // delta(x)=t1(t0`(x)) + PxVec3 deltaP = bodyCore->body2World.p - getLastCCDTransform().p; + return deltaP * 1.0f / dt; + } + PX_FORCE_INLINE PxVec3 getAngularMotionVelocity(PxReal dt, const PxsBodyCore* PX_RESTRICT bodyCore) const { + PxQuat deltaQ = bodyCore->body2World.q * getLastCCDTransform().q.getConjugate(); + PxVec3 axis; + PxReal angle; + deltaQ.toRadiansAndUnitAxis(angle, axis); + return axis * angle * 1.0f/dt; + } + + PX_FORCE_INLINE const PxTransform& getLastCCDTransform() const { return mLastTransform;} + PX_FORCE_INLINE void saveLastCCDTransform() { mLastTransform = mCore->body2World; } + + PX_FORCE_INLINE bool isKinematic() const { return (mCore->inverseMass == 0.0f); } + + PX_FORCE_INLINE void setPose(const PxTransform& pose) { mCore->body2World = pose; } + PX_FORCE_INLINE void setPosition(const PxVec3& position) { mCore->body2World.p = position; } + PX_FORCE_INLINE PxReal getInvMass() const { return mCore->inverseMass; } + PX_FORCE_INLINE PxVec3 getInvInertia() const { return mCore->inverseInertia; } + PX_FORCE_INLINE PxReal getMass() const { return 1.0f/mCore->inverseMass; } + PX_FORCE_INLINE PxVec3 getInertia() const { return PxVec3(1.0f/mCore->inverseInertia.x, + 1.0f/mCore->inverseInertia.y, + 1.0f/mCore->inverseInertia.z); } + PX_FORCE_INLINE PxsBodyCore& getCore() { return *mCore; } + PX_FORCE_INLINE const PxsBodyCore& getCore() const { return *mCore; } + + PX_FORCE_INLINE PxU32 isActivateThisFrame() const { return PxU32(mInternalFlags & eACTIVATE_THIS_FRAME); } + + PX_FORCE_INLINE PxU32 isDeactivateThisFrame() const { return PxU32(mInternalFlags & eDEACTIVATE_THIS_FRAME); } + + PX_FORCE_INLINE PxU32 isFreezeThisFrame() const { return PxU32(mInternalFlags & eFREEZE_THIS_FRAME); } + + PX_FORCE_INLINE PxU32 isUnfreezeThisFrame() const { return PxU32(mInternalFlags & eUNFREEZE_THIS_FRAME); } + + PX_FORCE_INLINE void clearFreezeFlag() { mInternalFlags &= ~eFREEZE_THIS_FRAME; } + + PX_FORCE_INLINE void clearUnfreezeFlag() { mInternalFlags &= ~eUNFREEZE_THIS_FRAME; } + + PX_FORCE_INLINE void clearAllFrameFlags() { mInternalFlags &= (eFROZEN | eDISABLE_GRAVITY); } + + void advanceToToi(PxReal toi, PxReal dt, bool clip); + void advancePrevPoseToToi(PxReal toi); + PxTransform getAdvancedTransform(PxReal toi) const; + Cm::SpatialVector getPreSolverVelocities() const; + + +}; + + +void PxsRigidBody::constrainLinearVelocity() +{ + const PxU32 lockFlags = mCore->lockFlags; + + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + mCore->linearVelocity.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + mCore->linearVelocity.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + mCore->linearVelocity.z = 0.f; + } +} + +void PxsRigidBody::constrainAngularVelocity() +{ + const PxU32 lockFlags = mCore->lockFlags; + + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + mCore->angularVelocity.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + mCore->angularVelocity.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + mCore->angularVelocity.z = 0.f; + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h b/src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h new file mode 100644 index 000000000..7cadd5c01 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h @@ -0,0 +1,52 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_SHAPESIM_H +#define PXS_SHAPESIM_H + +#include "PxsBodySim.h" +#include "PxsIslandNodeIndex.h" + +namespace physx +{ +//PxsBodySim is 12 or 16 bytes +struct PxsShapeSim// : public PxsBodySim +{ + PxsShapeCore* mShapeCore; // 4 or 8 + IG::NodeIndex mBodySimIndex; // 8 or 12 + PxU32 mElementIndex; // 12 or 16 transform cache and bound index + PxU32 mShapeIndex; // 16 or 20 +#if PX_P64_FAMILY + PxU32 mPad[3]; +#endif +}; + +}//physx + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h new file mode 100644 index 000000000..6c78eaa01 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h @@ -0,0 +1,202 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_SIMPLE_ISLAND_GEN_H +#define PXS_SIMPLE_ISLAND_GEN_H + +#include "PxsIslandSim.h" +#include "CmTask.h" + +namespace physx +{ + +namespace Sc +{ + class Interaction; +} +namespace IG +{ + + class SimpleIslandManager; + +class ThirdPassTask : public Cm::Task +{ + SimpleIslandManager& mIslandManager; + IslandSim& mIslandSim; + +public: + + ThirdPassTask(PxU64 contextID, SimpleIslandManager& islandManager, IslandSim& islandSim); + + virtual void runInternal(); + + virtual const char* getName() const + { + return "ThirdPassIslandGenTask"; + } + +private: + PX_NOCOPY(ThirdPassTask) +}; + +class PostThirdPassTask : public Cm::Task +{ + SimpleIslandManager& mIslandManager; + +public: + + PostThirdPassTask(PxU64 contextID, SimpleIslandManager& islandManager); + + virtual void runInternal(); + + virtual const char* getName() const + { + return "PostThirdPassTask"; + } +private: + PX_NOCOPY(PostThirdPassTask) +}; + +class SimpleIslandManager +{ + + HandleManager mNodeHandles; //! Handle manager for nodes + HandleManager mEdgeHandles; //! Handle manager for edges + + //An array of destroyed nodes + Ps::Array mDestroyedNodes; + Cm::BlockArray mInteractions; + + + //Edges destroyed this frame + Ps::Array mDestroyedEdges; + Ps::Array mFirstPartitionEdges; + Ps::Array mDestroyedPartitionEdges; + //KS - stores node indices for a given edge. Node index 0 is at 2* edgeId and NodeIndex1 is at 2*edgeId + 1 + //can also be used for edgeInstance indexing so there's no need to figure out outboundNode ID either! + Cm::BlockArray mEdgeNodeIndices; + Cm::BlockArray mConstraintOrCm; //! Pointers to either the constraint or Cm for this pair + + Cm::BitMap mConnectedMap; + + IslandSim mIslandManager; + IslandSim mSpeculativeIslandManager; + + ThirdPassTask mSpeculativeThirdPassTask; + ThirdPassTask mAccurateThirdPassTask; + + PostThirdPassTask mPostThirdPassTask; + PxU32 mMaxDirtyNodesPerFrame; + + PxU64 mContextID; +public: + + SimpleIslandManager(bool useEnhancedDeterminism, PxU64 contextID); + + ~SimpleIslandManager(); + + NodeIndex addRigidBody(PxsRigidBody* body, bool isKinematic, bool isActive); + + void removeNode(const NodeIndex index); + + NodeIndex addArticulation(Sc::ArticulationSim* articulation, Dy::ArticulationV* llArtic, bool isActive); + + EdgeIndex addContactManager(PxsContactManager* manager, NodeIndex nodeHandle1, NodeIndex nodeHandle2, Sc::Interaction* interaction); + + EdgeIndex addConstraint(Dy::Constraint* constraint, NodeIndex nodeHandle1, NodeIndex nodeHandle2, Sc::Interaction* interaction); + + bool isConnected(EdgeIndex edgeIndex) const { return !!mConnectedMap.test(edgeIndex); } + + PX_FORCE_INLINE NodeIndex getEdgeIndex(EdgeInstanceIndex edgeIndex) const { return mEdgeNodeIndices[edgeIndex]; } + + void activateNode(NodeIndex index); + void deactivateNode(NodeIndex index); + void putNodeToSleep(NodeIndex index); + + void removeConnection(EdgeIndex edgeIndex); + + void firstPassIslandGen(); + void additionalSpeculativeActivation(); + void secondPassIslandGen(); + void thirdPassIslandGen(PxBaseTask* continuation); + + void clearDestroyedEdges(); + + void setEdgeConnected(EdgeIndex edgeIndex); + void setEdgeDisconnected(EdgeIndex edgeIndex); + + bool getIsEdgeConnected(EdgeIndex edgeIndex); + + void setEdgeRigidCM(const EdgeIndex edgeIndex, PxsContactManager* cm); + + void clearEdgeRigidCM(const EdgeIndex edgeIndex); + + void setKinematic(IG::NodeIndex nodeIndex); + + void setDynamic(IG::NodeIndex nodeIndex); + + const IslandSim& getSpeculativeIslandSim() const { return mSpeculativeIslandManager; } + const IslandSim& getAccurateIslandSim() const { return mIslandManager; } + + IslandSim& getAccurateIslandSim() { return mIslandManager; } + + PX_FORCE_INLINE PxU32 getNbEdgeHandles() const { return mEdgeHandles.getTotalHandles(); } + + PX_FORCE_INLINE PxU32 getNbNodeHandles() const { return mNodeHandles.getTotalHandles(); } + + void deactivateEdge(const EdgeIndex edge); + + PX_FORCE_INLINE PxsContactManager* getContactManager(IG::EdgeIndex edgeId) const { return reinterpret_cast(mConstraintOrCm[edgeId]); } + PX_FORCE_INLINE PxsContactManager* getContactManagerUnsafe(IG::EdgeIndex edgeId) const { return reinterpret_cast(mConstraintOrCm[edgeId]); } + PX_FORCE_INLINE Dy::Constraint* getConstraint(IG::EdgeIndex edgeId) const { return reinterpret_cast(mConstraintOrCm[edgeId]); } + PX_FORCE_INLINE Dy::Constraint* getConstraintUnsafe(IG::EdgeIndex edgeId) const { return reinterpret_cast(mConstraintOrCm[edgeId]); } + + PX_FORCE_INLINE Sc::Interaction* getInteraction(IG::EdgeIndex edgeId) const { return mInteractions[edgeId]; } + + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + + bool checkInternalConsistency(); + + +private: + + friend class ThirdPassTask; + friend class PostThirdPassTask; + + bool validateDeactivations() const; + + PX_NOCOPY(SimpleIslandManager) +}; + + + +} +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h b/src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h new file mode 100644 index 000000000..625352642 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h @@ -0,0 +1,139 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_SIMULATION_CONTROLLER_H +#define PXS_SIMULATION_CONTROLLER_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxPreprocessor.h" +#include "foundation/PxTransform.h" +#include "CmBitMap.h" +#include "PsArray.h" + + +namespace physx +{ + namespace Dy + { + class Context; + struct Constraint; + class ArticulationV; + } + + namespace Cm + { + class EventProfiler; + } + + namespace Bp + { + class BoundsArray; + class BroadPhase; + } + + namespace IG + { + class SimpleIslandManager; + class IslandSim; + class NodeIndex; + } + + class PxGpuDispatcher; + class PxsTransformCache; + class PxvNphaseImplementationContext; + class PxBaseTask; + + struct PxsBodySim; + struct PxsShapeSim; + class PxsRigidBody; + class PxsKernelWranglerManager; + class PxsHeapMemoryAllocatorManager; + + template class PxgIterator; + struct PxgSolverConstraintManagerConstants; + + + class PxsSimulationControllerCallback + { + public: + virtual void updateScBodyAndShapeSim(PxBaseTask* continuation) = 0; + virtual PxU32 getNbCcdBodies() = 0; + + virtual ~PxsSimulationControllerCallback() {} + }; + + + class PxsSimulationController + { + public: + PxsSimulationController(PxsSimulationControllerCallback* callback): mCallback(callback){} + virtual ~PxsSimulationController(){} + + virtual void addJoint(const PxU32 edgeIndex, Dy::Constraint* constraint, IG::IslandSim& islandSim, Ps::Array& jointIndices, + Ps::Array& managerIter, PxU32 uniqueId) = 0; + virtual void removeJoint(const PxU32 edgeIndex, Dy::Constraint* constraint, Ps::Array& jointIndices, IG::IslandSim& islandSim) = 0; + virtual void addShape(PxsShapeSim* shapeSim, const PxU32 index) = 0; + virtual void removeShape(const PxU32 index) = 0; + virtual void addDynamic(PxsRigidBody* rigidBody, const IG::NodeIndex& nodeIndex) = 0; + virtual void addDynamics(PxsRigidBody** rigidBody, const PxU32* nodeIndex, PxU32 nbToProcess) = 0; + virtual void addArticulation(Dy::ArticulationV* articulation, const IG::NodeIndex& nodeIndex) = 0; + virtual void releaseArticulation(Dy::ArticulationV* articulation) = 0; + virtual void releaseDeferredArticulationIds() = 0; + virtual void updateDynamic(const bool isArticulationLink, const IG::NodeIndex&) = 0; + virtual void updateJoint(const PxU32 edgeIndex, Dy::Constraint* constraint) = 0; + virtual void updateBodies(PxsRigidBody** rigidBodies, PxU32* nodeIndices, const PxU32 nbBodies) = 0; + virtual void updateBodiesAndShapes(PxBaseTask* continuation) = 0; + virtual void update(const PxU32 bitMapWordCounts) = 0; + virtual void gpuDmabackData(PxsTransformCache& cache, Bp::BoundsArray& boundArray, Cm::BitMapPinned& changedAABBMgrHandles) = 0; + virtual void udpateScBodyAndShapeSim(PxsTransformCache& cache, Bp::BoundsArray& boundArray, PxBaseTask* continuation) = 0; + virtual PxU32* getActiveBodies() = 0; + virtual PxU32* getDeactiveBodies() = 0; + virtual void** getRigidBodies() = 0; + virtual PxU32 getNbBodies() = 0; + + virtual PxU32* getUnfrozenShapes() = 0; + virtual PxU32* getFrozenShapes() = 0; + virtual PxsShapeSim** getShapeSims() = 0; + virtual PxU32 getNbFrozenShapes() = 0; + virtual PxU32 getNbUnfrozenShapes() = 0; + + virtual void clear() = 0; + virtual void setBounds(Bp::BoundsArray* boundArray) = 0; + virtual void reserve(const PxU32 nbBodies) = 0; + + protected: + PxsSimulationControllerCallback* mCallback; + + }; + + PxsSimulationController* createSimulationController(PxsSimulationControllerCallback* callback); +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h b/src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h new file mode 100644 index 000000000..1970356f1 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h @@ -0,0 +1,144 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_TRANSFORM_CACHE_H +#define PXS_TRANSFORM_CACHE_H + +#include "CmPhysXCommon.h" +#include "CmIDPool.h" +#include "CmBitMap.h" +#include "PsUserAllocated.h" +#include "PsAllocator.h" + +#define PX_DEFAULT_CACHE_SIZE 512 + +namespace physx +{ + struct PxsTransformFlag + { + enum Flags + { + eFROZEN = (1 << 0) + }; + }; + + struct PX_ALIGN_PREFIX(16) PxsCachedTransform + { + PxTransform transform; + PxU32 flags; + + PX_FORCE_INLINE PxU32 isFrozen() const { return flags & PxsTransformFlag::eFROZEN; } + } + PX_ALIGN_SUFFIX(16); + + + class PxsTransformCache : public Ps::UserAllocated + { + typedef PxU32 RefCountType; + + public: + PxsTransformCache(Ps::VirtualAllocatorCallback& allocatorCallback) : mTransformCache(Ps::VirtualAllocator(&allocatorCallback)), mHasAnythingChanged(true) + { + /*mTransformCache.reserve(PX_DEFAULT_CACHE_SIZE); + mTransformCache.forceSize_Unsafe(PX_DEFAULT_CACHE_SIZE);*/ + mUsedSize = 0; + } + + void initEntry(PxU32 index) + { + PxU32 oldCapacity = mTransformCache.capacity(); + if (index >= oldCapacity) + { + PxU32 newCapacity = Ps::nextPowerOfTwo(index); + mTransformCache.reserve(newCapacity); + mTransformCache.forceSize_Unsafe(newCapacity); + } + mUsedSize = PxMax(mUsedSize, index + 1u); + } + + + PX_FORCE_INLINE void setTransformCache(const PxTransform& transform, const PxU32 flags, const PxU32 index) + { + mTransformCache[index].transform = transform; + mTransformCache[index].flags = flags; + mHasAnythingChanged = true; + } + + PX_FORCE_INLINE const PxsCachedTransform& getTransformCache(const PxU32 index) const + { + return mTransformCache[index]; + } + + + PX_FORCE_INLINE PxsCachedTransform& getTransformCache(const PxU32 index) + { + return mTransformCache[index]; + } + + PX_FORCE_INLINE void shiftTransforms(const PxVec3& shift) + { + for (PxU32 i = 0; i < mTransformCache.capacity(); i++) + { + mTransformCache[i].transform.p += shift; + } + mHasAnythingChanged = true; + } + + PX_FORCE_INLINE PxU32 getTotalSize() const + { + return mUsedSize; + } + + PX_FORCE_INLINE const PxsCachedTransform* getTransforms() const + { + return mTransformCache.begin(); + } + + PX_FORCE_INLINE PxsCachedTransform* getTransforms() + { + return mTransformCache.begin(); + } + + PX_FORCE_INLINE Ps::Array* getCachedTransformArray() + { + return &mTransformCache; + } + + PX_FORCE_INLINE void resetChangedState() { mHasAnythingChanged = false; } + PX_FORCE_INLINE void setChangedState() { mHasAnythingChanged = true; } + PX_FORCE_INLINE bool hasChanged() const { return mHasAnythingChanged; } + + private: + Ps::Array mTransformCache; + PxU32 mUsedSize; + bool mHasAnythingChanged; + }; +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h b/src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h new file mode 100644 index 000000000..6e8d84c9f --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h @@ -0,0 +1,217 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXV_NPHASE_IMPLEMENTATION_CONTEXT_H +#define PXV_NPHASE_IMPLEMENTATION_CONTEXT_H + +#include "PxSceneDesc.h" +#include "PxsContactManagerState.h" +#include "PsArray.h" + +namespace physx +{ + +namespace IG +{ + class SimpleIslandManager; + class IslandSim; + typedef PxU32 EdgeIndex; +} + +namespace Dy +{ + class Context; +} + +class PxBaseTask; +class PxsContext; +struct PxsShapeCore; +class PxsMaterialCore; +struct PxgDynamicsMemoryConfig; +class PxsContactManager; +struct PxsContactManagerOutput; +class PxsKernelWranglerManager; +class PxsHeapMemoryAllocatorManager; + + +struct PxsContactManagerBase +{ + static const PxU32 NEW_CONTACT_MANAGER_MASK = 0x80000000; + static const PxU32 GPU_NP_OFFSET = 0x4; + + static const PxU32 MaxBucketBits = 3; + + const PxU32 mBucketId; + + PxsContactManagerBase(const PxU32 bucketId) : mBucketId(bucketId) + { + PX_ASSERT(bucketId < (1<> MaxBucketBits; } + static PX_FORCE_INLINE PxU32 computeBucketIndexFromId(const PxU32 id) { return id & ((1<getPose(); + if (pair.mBa1) + pair.mBa1->getPose(); +#endif +} + +#if CCD_DEBUG_PRINTS +namespace physx { + +static const char* gGeomTypes[PxGeometryType::eGEOMETRY_COUNT+1] = { + "sphere", "plane", "capsule", "box", "convex", "trimesh", "heightfield", "*" +}; + +FILE* gCCDLog = NULL; + +static inline void openCCDLog() +{ + if (gCCDLog) + { + fclose(gCCDLog); + gCCDLog = NULL; + } + gCCDLog = fopen("c:\\ccd.txt", "wt"); + fprintf(gCCDLog, ">>>>>>>>>>>>>>>>>>>>>>>>>>> CCD START FRAME <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); +} + +static inline void printSeparator( + const char* prefix, PxU32 pass, PxsRigidBody* atom0, PxGeometryType::Enum g0, PxsRigidBody* atom1, PxGeometryType::Enum g1) +{ + fprintf(gCCDLog, "------- %s pass %d (%s %x) vs (%s %x)\n", prefix, pass, gGeomTypes[g0], atom0, gGeomTypes[g1], atom1); + fflush(gCCDLog); +} + +static inline void printCCDToi( + const char* header, PxF32 t, const PxVec3& v, + PxsRigidBody* atom0, PxGeometryType::Enum g0, PxsRigidBody* atom1, PxGeometryType::Enum g1 +) +{ + fprintf(gCCDLog, "%s (%s %x vs %s %x): %.5f, (%.2f, %.2f, %.2f)\n", header, gGeomTypes[g0], atom0, gGeomTypes[g1], atom1, t, v.x, v.y, v.z); + fflush(gCCDLog); +} + +static inline void printCCDPair(const char* header, PxsCCDPair& pair) +{ + printCCDToi(header, pair.mMinToi, pair.mMinToiNormal, pair.mBa0, pair.mG0, pair.mBa1, pair.mG1); +} + +// also used in PxcSweepConvexMesh.cpp +void printCCDDebug(const char* msg, const PxsRigidBody* atom0, PxGeometryType::Enum g0, bool printPtr) +{ + fprintf(gCCDLog, " %s (%s %x)\n", msg, gGeomTypes[g0], printPtr ? atom0 : 0); + fflush(gCCDLog); +} + +// also used in PxcSweepConvexMesh.cpp +void printShape( + PxsRigidBody* atom0, PxGeometryType::Enum g0, const char* annotation, PxReal dt, PxU32 pass, bool printPtr) +{ + fprintf(gCCDLog, "%s (%s %x) atom=(%.2f, %.2f, %.2f)>(%.2f, %.2f, %.2f) v=(%.1f, %.1f, %.1f), mv=(%.1f, %.1f, %.1f)\n", + annotation, gGeomTypes[g0], printPtr ? atom0 : 0, + atom0->getLastCCDTransform().p.x, atom0->getLastCCDTransform().p.y, atom0->getLastCCDTransform().p.z, + atom0->getPose().p.x, atom0->getPose().p.y, atom0->getPose().p.z, + atom0->getLinearVelocity().x, atom0->getLinearVelocity().y, atom0->getLinearVelocity().z, + atom0->getLinearMotionVelocity(dt).x, atom0->getLinearMotionVelocity(dt).y, atom0->getLinearMotionVelocity(dt).z ); + fflush(gCCDLog); + #if DEBUG_RENDER_CCD && DEBUG_RENDER_CCD_ATOM_PTR + if (!DEBUG_RENDER_CCD_FIRST_PASS_ONLY || pass == 0) + { + PxScene *s; PxGetPhysics()->getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) + << Cm::DebugText(atom0->getPose().p, 0.05f, "%x", atom0); + } + #endif +} + +static inline void flushCCDLog() +{ + fflush(gCCDLog); +} + +} // namespace physx + +#else + +namespace physx +{ + +void printShape(PxsRigidBody* /*atom0*/, PxGeometryType::Enum /*g0*/, const char* /*annotation*/, PxReal /*dt*/, PxU32 /*pass*/, bool printPtr = true) +{PX_UNUSED(printPtr);} + +static inline void openCCDLog() {} + +static inline void flushCCDLog() {} + +void printCCDDebug(const char* /*msg*/, const PxsRigidBody* /*atom0*/, PxGeometryType::Enum /*g0*/, bool printPtr = true) {PX_UNUSED(printPtr);} + +static inline void printSeparator( + const char* /*prefix*/, PxU32 /*pass*/, PxsRigidBody* /*atom0*/, PxGeometryType::Enum /*g0*/, + PxsRigidBody* /*atom1*/, PxGeometryType::Enum /*g1*/) {} +} // namespace physx +#endif + +namespace +{ + // PT: TODO: refactor with ShapeSim version (SIMD) + PX_INLINE PxTransform getShapeAbsPose(const PxsShapeCore* shapeCore, const PxsRigidCore* rigidCore, PxU32 isDynamic) + { + if(isDynamic) + { + const PxsBodyCore* PX_RESTRICT bodyCore = static_cast(rigidCore); + return bodyCore->body2World * bodyCore->getBody2Actor().getInverse() *shapeCore->transform; + } + else + { + return rigidCore->body2World * shapeCore->transform; + } + } +} + +namespace physx +{ + + PxsCCDContext::PxsCCDContext(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext) : + mPostCCDSweepTask (context->getContextId(), this, "PxsContext.postCCDSweep"), + mPostCCDAdvanceTask (context->getContextId(), this, "PxsContext.postCCDAdvance"), + mPostCCDDepenetrateTask (context->getContextId(), this, "PxsContext.postCCDDepenetrate"), + mDisableCCDResweep (false), + miCCDPass (0), + mSweepTotalHits (0), + mCCDThreadContext (NULL), + mCCDPairsPerBatch (0), + mCCDMaxPasses (1), + mContext (context), + mThresholdStream (thresholdStream), + mNphaseContext(nPhaseContext) +{ +} + +PxsCCDContext::~PxsCCDContext() +{ +} + +PxsCCDContext* PxsCCDContext::create(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext) +{ + PxsCCDContext* dc = reinterpret_cast( + PX_ALLOC(sizeof(PxsCCDContext), "PxsCCDContext")); + + if(dc) + { + new(dc) PxsCCDContext(context, thresholdStream, nPhaseContext); + } + return dc; +} + +void PxsCCDContext::destroy() +{ + this->~PxsCCDContext(); + PX_FREE(this); +} + +PxTransform PxsCCDShape::getAbsPose(const PxsRigidBody* atom) const +{ + // PT: TODO: refactor with ShapeSim version (SIMD) - or with redundant getShapeAbsPose() above in this same file! + if(atom) + return atom->getPose() * atom->getCore().getBody2Actor().getInverse() * mShapeCore->transform; + else + return mRigidCore->body2World * mShapeCore->transform; +} + +PxTransform PxsCCDShape::getLastCCDAbsPose(const PxsRigidBody* atom) const +{ + // PT: TODO: refactor with ShapeSim version (SIMD) + return atom->getLastCCDTransform() * atom->getCore().getBody2Actor().getInverse() * mShapeCore->transform; +} + +PxReal PxsCCDPair::sweepFindToi(PxcNpThreadContext& context, PxReal dt, PxU32 pass) +{ + printSeparator("findToi", pass, mBa0, mG0, NULL, PxGeometryType::eGEOMETRY_COUNT); + //Update shape transforms if necessary + updateShapes(); + + //Extract the bodies + PxsRigidBody* atom0 = mBa0; + PxsRigidBody* atom1 = mBa1; + PxsCCDShape* ccdShape0 = mCCDShape0; + PxsCCDShape* ccdShape1 = mCCDShape1; + PxGeometryType::Enum g0 = mG0, g1 = mG1; + + //If necessary, flip the bodies to make sure that g0 <= g1 + if(mG1 < mG0) + { + g0 = mG1; + g1 = mG0; + ccdShape0 = mCCDShape1; + ccdShape1 = mCCDShape0; + atom0 = mBa1; + atom1 = mBa0; + } + + PX_ALIGN(16, PxTransform tm0); + PX_ALIGN(16, PxTransform tm1); + PX_ALIGN(16, PxTransform lastTm0); + PX_ALIGN(16, PxTransform lastTm1); + + tm0 = ccdShape0->mCurrentTransform; + lastTm0 = ccdShape0->mPrevTransform; + + tm1 = ccdShape1->mCurrentTransform; + lastTm1 = ccdShape1->mPrevTransform; + + const PxVec3 trA = tm0.p - lastTm0.p; + const PxVec3 trB = tm1.p - lastTm1.p; + const PxVec3 relTr = trA - trB; + + // Do the sweep + PxVec3 sweepNormal(0.0f); + PxVec3 sweepPoint(0.0f); + + PxReal restDistance = PxMax(mCm->getWorkUnit().restDistance, 0.f); + + context.mDt = dt; + context.mCCDFaceIndex = PXC_CONTACT_NO_FACE_INDEX; + + //Cull the sweep hit based on the relative velocity along the normal + const PxReal fastMovingThresh0 = ccdShape0->mFastMovingThreshold; + const PxReal fastMovingThresh1 = ccdShape1->mFastMovingThreshold; + + const PxReal sumFastMovingThresh = (fastMovingThresh0 + fastMovingThresh1); + + + PxReal toi = Gu::SweepShapeShape(*ccdShape0, *ccdShape1, tm0, tm1, lastTm0, lastTm1, restDistance, + sweepNormal, sweepPoint, mMinToi, context.mCCDFaceIndex, sumFastMovingThresh); + + //If toi is after the end of TOI, return no hit + if (toi >= 1.0f) + { + mToiType = PxsCCDPair::ePrecise; + mPenetration = 0.f; + mPenetrationPostStep = 0.f; + mMinToi = PX_MAX_REAL; // needs to be reset in case of resweep + return toi; + } + + PX_ASSERT(PxIsFinite(toi)); + PX_ASSERT(sweepNormal.isFinite()); + mFaceIndex = context.mCCDFaceIndex; + + //Work out linear motion (used to cull the sweep hit) + const PxReal linearMotion = relTr.dot(-sweepNormal); + + //If we swapped the shapes, swap them back + if(mG1 >= mG0) + sweepNormal = -sweepNormal; + + + mToiType = PxsCCDPair::ePrecise; + + PxReal penetration = 0.f; + PxReal penetrationPostStep = 0.f; + + //If linear motion along normal < the CCD threshold, set toi to no-hit. + if((linearMotion) < sumFastMovingThresh) + { + mMinToi = PX_MAX_REAL; // needs to be reset in case of resweep + return PX_MAX_REAL; + } + else if(toi <= 0.f) + { + //If the toi <= 0.f, this implies an initial overlap. If the value < 0.f, it implies penetration + PxReal stepRatio0 = atom0 ? atom0->mCCD->mTimeLeft : 1.f; + PxReal stepRatio1 = atom1 ? atom1->mCCD->mTimeLeft : 1.f; + + PxReal stepRatio = PxMin(stepRatio0, stepRatio1); + penetration = -toi; + + toi = 0.f; + + if(stepRatio == 1.f) + { + //If stepRatio == 1.f (i.e. neither body has stepped forwards any time at all) + //we extract the advance coefficients from the bodies and permit the bodies to step forwards a small amount + //to ensure that they won't remain jammed because TOI = 0.0 + const PxReal advance0 = atom0 ? atom0->mCore->ccdAdvanceCoefficient : 1.f; + const PxReal advance1 = atom1 ? atom1->mCore->ccdAdvanceCoefficient : 1.f; + const PxReal advance = PxMin(advance0, advance1); + + PxReal fastMoving = PxMin(fastMovingThresh0, atom1 ? fastMovingThresh1 : PX_MAX_REAL); + PxReal advanceCoeff = advance * fastMoving; + penetrationPostStep = advanceCoeff/linearMotion; + + } + PX_ASSERT(PxIsFinite(toi)); + } + + //Store TOI, penetration and post-step (how much to step forward in initial overlap conditions) + mMinToi = toi; + mPenetration = penetration; + mPenetrationPostStep = penetrationPostStep; + + mMinToiPoint = sweepPoint; + + mMinToiNormal = sweepNormal; + + + //Work out the materials for the contact (restitution, friction etc.) + context.mContactBuffer.count = 0; + context.mContactBuffer.contact(mMinToiPoint, mMinToiNormal, 0.f, g1 == PxGeometryType::eTRIANGLEMESH || g1 == PxGeometryType::eHEIGHTFIELD? mFaceIndex : PXC_CONTACT_NO_FACE_INDEX); + + PxsMaterialInfo materialInfo; + + g_GetSingleMaterialMethodTable[g0](ccdShape0->mShapeCore, 0, context, &materialInfo); + g_GetSingleMaterialMethodTable[g1](ccdShape1->mShapeCore, 1, context, &materialInfo); + + const PxsMaterialData& data0 = *context.mMaterialManager->getMaterial(materialInfo.mMaterialIndex0); + const PxsMaterialData& data1 = *context.mMaterialManager->getMaterial(materialInfo.mMaterialIndex1); + + const PxReal restitution = PxsMaterialCombiner::combineRestitution(data0, data1); + PxsMaterialCombiner combiner(1.0f, 1.0f); + PxsMaterialCombiner::PxsCombinedMaterial combinedMat = combiner.combineIsotropicFriction(data0, data1); + const PxReal sFriction = combinedMat.staFriction; + const PxReal dFriction = combinedMat.dynFriction; + + mMaterialIndex0 = materialInfo.mMaterialIndex0; + mMaterialIndex1 = materialInfo.mMaterialIndex1; + mDynamicFriction = dFriction; + mStaticFriction = sFriction; + mRestitution = restitution; + + return toi; +} + +void PxsCCDPair::updateShapes() +{ + if(mBa0) + { + //If the CCD shape's update count doesn't match the body's update count, this shape needs its transforms and bounds re-calculated + if(mBa0->mCCD->mUpdateCount != mCCDShape0->mUpdateCount) + { + const PxTransform tm0 = mCCDShape0->getAbsPose(mBa0); + const PxTransform lastTm0 = mCCDShape0->getLastCCDAbsPose(mBa0); + + const PxVec3 trA = tm0.p - lastTm0.p; + + Gu::Vec3p origin, extents; + Gu::computeBoundsWithCCDThreshold(origin, extents, mCCDShape0->mShapeCore->geometry.getGeometry(), tm0, NULL); + + mCCDShape0->mCenter = origin - trA; + mCCDShape0->mExtents = extents; + mCCDShape0->mPrevTransform = lastTm0; + mCCDShape0->mCurrentTransform = tm0; + mCCDShape0->mUpdateCount = mBa0->mCCD->mUpdateCount; + } + } + + if(mBa1) + { + //If the CCD shape's update count doesn't match the body's update count, this shape needs its transforms and bounds re-calculated + if(mBa1->mCCD->mUpdateCount != mCCDShape1->mUpdateCount) + { + const PxTransform tm1 = mCCDShape1->getAbsPose(mBa1); + const PxTransform lastTm1 = mCCDShape1->getLastCCDAbsPose(mBa1); + + const PxVec3 trB = tm1.p - lastTm1.p; + + Vec3p origin, extents; + computeBoundsWithCCDThreshold(origin, extents, mCCDShape1->mShapeCore->geometry.getGeometry(), tm1, NULL); + + mCCDShape1->mCenter = origin - trB; + mCCDShape1->mExtents = extents; + mCCDShape1->mPrevTransform = lastTm1; + mCCDShape1->mCurrentTransform = tm1; + mCCDShape1->mUpdateCount = mBa1->mCCD->mUpdateCount; + } + } +} + +PxReal PxsCCDPair::sweepEstimateToi() +{ + //Update shape transforms if necessary + updateShapes(); + + //PxsRigidBody* atom1 = mBa1; + //PxsRigidBody* atom0 = mBa0; + PxsCCDShape* ccdShape0 = mCCDShape0; + PxsCCDShape* ccdShape1 = mCCDShape1; + PxGeometryType::Enum g0 = mG0, g1 = mG1; + PX_UNUSED(g0); + + + //Flip shapes if necessary + if(mG1 < mG0) + { + g0 = mG1; + g1 = mG0; + /*atom0 = mBa1; + atom1 = mBa0;*/ + ccdShape0 = mCCDShape1; + ccdShape1 = mCCDShape0; + } + + //Extract previous/current transforms, translations etc. + PxTransform tm0, lastTm0, tm1, lastTm1; + + PxVec3 trA(0.f); + PxVec3 trB(0.f); + tm0 = ccdShape0->mCurrentTransform; + lastTm0 = ccdShape0->mPrevTransform; + trA = tm0.p - lastTm0.p; + + tm1 = ccdShape1->mCurrentTransform; + lastTm1 = ccdShape1->mPrevTransform; + trB = tm1.p - lastTm1.p; + + PxReal restDistance = PxMax(mCm->getWorkUnit().restDistance, 0.f); + + const PxVec3 relTr = trA - trB; + + //Work out the sum of the fast moving thresholds scaled by the step ratio + const PxReal fastMovingThresh0 = ccdShape0->mFastMovingThreshold; + const PxReal fastMovingThresh1 = ccdShape1->mFastMovingThreshold; + const PxReal sumFastMovingThresh = (fastMovingThresh0 + fastMovingThresh1); + + mToiType = eEstimate; + + //If the objects are not moving fast-enough relative to each-other to warrant CCD, set estimated time as PX_MAX_REAL + if((relTr.magnitudeSquared()) <= (sumFastMovingThresh * sumFastMovingThresh)) + { + mToiType = eEstimate; + mMinToi = PX_MAX_REAL; + return PX_MAX_REAL; + } + + //Otherwise, the objects *are* moving fast-enough so perform estimation pass + if(g1 == PxGeometryType::eTRIANGLEMESH) + { + //Special-case estimation code for meshes + PxF32 toi = Gu::SweepEstimateAnyShapeMesh(*ccdShape0, *ccdShape1, tm0, tm1, lastTm0, lastTm1, restDistance, sumFastMovingThresh); + + mMinToi = toi; + return toi; + } + else if (g1 == PxGeometryType::eHEIGHTFIELD) + { + //Special-case estimation code for heightfields + PxF32 toi = Gu::SweepEstimateAnyShapeHeightfield(*ccdShape0, *ccdShape1, tm0, tm1, lastTm0, lastTm1, restDistance, sumFastMovingThresh); + + mMinToi = toi; + return toi; + } + + //Generic estimation code for prim-prim sweeps + PxVec3 centreA, extentsA; + PxVec3 centreB, extentsB; + centreA = ccdShape0->mCenter; + extentsA = ccdShape0->mExtents + PxVec3(restDistance); + + centreB = ccdShape1->mCenter; + extentsB = ccdShape1->mExtents; + + PxF32 toi = Gu::sweepAABBAABB(centreA, extentsA * 1.1f, centreB, extentsB * 1.1f, trA, trB); + mMinToi = toi; + return toi; +} + +bool PxsCCDPair::sweepAdvanceToToi(PxReal dt, bool clipTrajectoryToToi) +{ + PxsCCDShape* ccds0 = mCCDShape0; + PxsRigidBody* atom0 = mBa0; + PxsCCDShape* ccds1 = mCCDShape1; + PxsRigidBody* atom1 = mBa1; + + const PxsCCDPair* thisPair = this; + + //Both already had a pass so don't do anything + if ((atom0 == NULL || atom0->mCCD->mPassDone) && (atom1 == NULL || atom1->mCCD->mPassDone)) + return false; + + //Test to validate that they're both infinite mass objects. If so, there can be no response so terminate on a notification-only + if((atom0 == NULL || atom0->mCore->inverseMass == 0.f) && (atom1 == NULL || atom1->mCore->inverseMass == 0.f)) + return false; + + //If the TOI < 1.f. If not, this hit happens after this frame or at the very end of the frame. Either way, next frame can handle it + if (thisPair->mMinToi < 1.0f) + { + if(thisPair->mCm->getWorkUnit().flags & PxcNpWorkUnitFlag::eDISABLE_RESPONSE || thisPair->mMaxImpulse == 0.f) + { + //Don't mark pass as done on either body + return true; + } + + + PxReal minToi = thisPair->mMinToi; + + PxF32 penetration = -mPenetration * 10.f; + + PxVec3 minToiNormal = thisPair->mMinToiNormal; + + if (!minToiNormal.isNormalized()) + { + // somehow we got zero normal. This can happen for instance if two identical objects spawn exactly on top of one another + // abort ccd and clip to current toi + if (atom0 && !atom0->mCCD->mPassDone) + { + atom0->advancePrevPoseToToi(minToi); + atom0->advanceToToi(minToi, dt, true); + atom0->mCCD->mUpdateCount++; + } + return true; + } + + + + //Get the material indices... + const PxReal restitution = mRestitution; + const PxReal sFriction = mStaticFriction; + const PxReal dFriction = mDynamicFriction; + + + PxVec3 v0(0.f), v1(0.f); + + PxReal invMass0(0.f), invMass1(0.f); +#if CCD_ANGULAR_IMPULSE + PxMat33 invInertia0(PxVec3(0.f), PxVec3(0.f), PxVec3(0.f)), invInertia1(PxVec3(0.f), PxVec3(0.f), PxVec3(0.f)); + PxVec3 localPoint0(0.f), localPoint1(0.f); +#endif + + PxReal dom0 = mCm->getDominance0(); + PxReal dom1 = mCm->getDominance1(); + + //Work out velocity and invMass for body 0 + if(atom0) + { + //Put contact point in local space, then find how much point is moving using point velocity... + +#if CCD_ANGULAR_IMPULSE + localPoint0 = mMinToiPoint - trA.p; + v0 = atom0->mCore->linearVelocity + atom0->mCore->angularVelocity.cross(localPoint0); + + physx::Cm::transformInertiaTensor(atom0->mCore->inverseInertia, PxMat33(trA.q),invInertia0); + invInertia0 *= dom0; +#else + v0 = atom0->mCore->linearVelocity + atom0->mCore->angularVelocity.cross(ccds0->mCurrentTransform.p - atom0->mCore->body2World.p); +#endif + invMass0 = atom0->getInvMass() * dom0; + + } + + //Work out velocity and invMass for body 1 + if(atom1) + { + + //Put contact point in local space, then find how much point is moving using point velocity... +#if CCD_ANGULAR_IMPULSE + + localPoint1 = mMinToiPoint - trB.p; + v1 = atom1->mCore->linearVelocity + atom1->mCore->angularVelocity.cross(localPoint1); + physx::Cm::transformInertiaTensor(atom1->mCore->inverseInertia, PxMat33(trB.q),invInertia1); + invInertia1 *= dom1; +#else + v1 = atom1->mCore->linearVelocity + atom1->mCore->angularVelocity.cross(ccds1->mCurrentTransform.p - atom1->mCore->body2World.p); +#endif + invMass1 = atom1->getInvMass() * dom1; + } + + PX_ASSERT(v0.isFinite() && v1.isFinite()); + + //Work out relative velocity + PxVec3 vRel = v1 - v0; + + //Project relative velocity onto contact normal and bias with penetration + PxReal relNorVel = vRel.dot(minToiNormal); + PxReal relNorVelPlusPen = relNorVel + penetration; + +#if CCD_ANGULAR_IMPULSE + if(relNorVelPlusPen >= -1e-6f) + { + //we fall back on linear only parts... + localPoint0 = PxVec3(0.f); + localPoint1 = PxVec3(0.f); + v0 = atom0 ? atom0->getLinearVelocity() : PxVec3(0.f); + v1 = atom1 ? atom1->getLinearVelocity() : PxVec3(0.f); + vRel = v1 - v0; + relNorVel = vRel.dot(minToiNormal); + relNorVelPlusPen = relNorVel + penetration; + } +#endif + + + //If the relative motion is moving towards each-other, respond + if(relNorVelPlusPen < -1e-6f) + { + PxReal sumRecipMass = invMass0 + invMass1; + + const PxReal jLin = relNorVelPlusPen; + const PxReal normalResponse = (1.f + restitution) * jLin; + +#if CCD_ANGULAR_IMPULSE + const PxVec3 angularMom0 = invInertia0 * (localPoint0.cross(mMinToiNormal)); + const PxVec3 angularMom1 = invInertia1 * (localPoint1.cross(mMinToiNormal)); + + const PxReal jAng = minToiNormal.dot(angularMom0.cross(localPoint0) + angularMom1.cross(localPoint1)); + const PxReal impulseDivisor = sumRecipMass + jAng; + +#else + const PxReal impulseDivisor = sumRecipMass; +#endif + const PxReal jImp = PxMax(-mMaxImpulse, normalResponse/impulseDivisor); + + PxVec3 j(0.f); + + //If the user requested CCD friction, calculate friction forces. + //Note, CCD is *linear* so friction is also linear. The net result is that CCD friction can stop bodies' lateral motion so its better to have it disabled + //unless there's a real need for it. + if(mHasFriction) + { + + PxVec3 vPerp = vRel - relNorVel * minToiNormal; + PxVec3 tDir = vPerp; + PxReal length = tDir.normalize(); + PxReal vPerpImp = length/impulseDivisor; + PxF32 fricResponse = 0.f; + PxF32 staticResponse = (jImp*sFriction); + PxF32 dynamicResponse = (jImp*dFriction); + + + if (PxAbs(staticResponse) >= vPerpImp) + fricResponse = vPerpImp; + else + { + fricResponse = -dynamicResponse /* times m0 */; + } + + + //const PxVec3 fricJ = -vPerp.getNormalized() * (fricResponse/impulseDivisor); + const PxVec3 fricJ = tDir * (fricResponse); + j = jImp * mMinToiNormal + fricJ; + } + else + { + j = jImp * mMinToiNormal; + } + + verifyCCDPair(*this); + //If we have a negative impulse value, then we need to apply it. If not, the bodies are separating (no impulse to apply). + if(jImp < 0.f) + { + mAppliedForce = -jImp; + + //Adjust velocities + if((atom0 != NULL && atom0->mCCD->mPassDone) || + (atom1 != NULL && atom1->mCCD->mPassDone)) + { + mPenetrationPostStep = 0.f; + } + else + { + if (atom0) + { + //atom0->mAcceleration.linear = atom0->getLinearVelocity(); // to provide pre-"solver" velocity in contact reports + + atom0->setLinearVelocity(atom0->getLinearVelocity() + j * invMass0); + atom0->constrainLinearVelocity(); + #if CCD_ANGULAR_IMPULSE + atom0->mAcceleration.angular = atom0->getAngularVelocity(); // to provide pre-"solver" velocity in contact reports + atom0->setAngularVelocity(atom0->getAngularVelocity() + invInertia0 * localPoint0.cross(j)); + atom0->constrainAngularVelocity(); + #endif + } + if (atom1) + { + //atom1->mAcceleration.linear = atom1->getLinearVelocity(); // to provide pre-"solver" velocity in contact reports + atom1->setLinearVelocity(atom1->getLinearVelocity() - j * invMass1); + atom1->constrainLinearVelocity(); + #if CCD_ANGULAR_IMPULSE + atom1->mAcceleration.angular = atom1->getAngularVelocity(); // to provide pre-"solver" velocity in contact reports + atom1->setAngularVelocity(atom1->getAngularVelocity() - invInertia1 * localPoint1.cross(j)); + atom1->constrainAngularVelocity(); + #endif + } + } + } + } + + //Update poses + if (atom0 && !atom0->mCCD->mPassDone) + { + atom0->advancePrevPoseToToi(minToi); + atom0->advanceToToi(minToi, dt, clipTrajectoryToToi && mPenetrationPostStep == 0.f); + atom0->mCCD->mUpdateCount++; + } + if (atom1 && !atom1->mCCD->mPassDone) + { + atom1->advancePrevPoseToToi(minToi); + atom1->advanceToToi(minToi, dt, clipTrajectoryToToi && mPenetrationPostStep == 0.f); + atom1->mCCD->mUpdateCount++; + } + + //If we had a penetration post-step (i.e. an initial overlap), step forwards slightly after collision response + if(mPenetrationPostStep > 0.f) + { + if (atom0 && !atom0->mCCD->mPassDone) + { + atom0->advancePrevPoseToToi(mPenetrationPostStep); + if(clipTrajectoryToToi) + atom0->advanceToToi(mPenetrationPostStep, dt, clipTrajectoryToToi); + } + if (atom1 && !atom1->mCCD->mPassDone) + { + atom1->advancePrevPoseToToi(mPenetrationPostStep); + if(clipTrajectoryToToi) + atom1->advanceToToi(mPenetrationPostStep, dt, clipTrajectoryToToi); + } + } + //Mark passes as done + if (atom0) + { + atom0->mCCD->mPassDone = true; + atom0->mCCD->mHasAnyPassDone = true; + } + if (atom1) + { + atom1->mCCD->mPassDone = true; + atom1->mCCD->mHasAnyPassDone = true; + } + + return true; + + //return false; + } + else + { + printCCDDebug("advToi: clean sweep", atom0, mG0); + } + + return false; +} + +struct IslandCompare +{ + bool operator()(PxsCCDPair& a, PxsCCDPair& b) const { return a.mIslandId < b.mIslandId; } +}; + +struct IslandPtrCompare +{ + bool operator()(PxsCCDPair*& a, PxsCCDPair*& b) const { return a->mIslandId < b->mIslandId; } +}; + +struct ToiCompare +{ + bool operator()(PxsCCDPair& a, PxsCCDPair& b) const + { + return (a.mMinToi < b.mMinToi) || + ((a.mMinToi == b.mMinToi) && (a.mBa1 != NULL && b.mBa1 == NULL)); + } +}; + +struct ToiPtrCompare +{ + bool operator()(PxsCCDPair*& a, PxsCCDPair*& b) const + { + return (a->mMinToi < b->mMinToi) || + ((a->mMinToi == b->mMinToi) && (a->mBa1 != NULL && b->mBa1 == NULL)); + } +}; + + +// -------------------------------------------------------------- +/** +\brief Class to perform a set of sweep estimate tasks +*/ +class PxsCCDSweepTask : public Cm::Task +{ + PxsCCDPair** mPairs; + PxU32 mNumPairs; +public: + PxsCCDSweepTask(PxU64 contextID, PxsCCDPair** pairs, PxU32 nPairs) + : Cm::Task(contextID), mPairs(pairs), mNumPairs(nPairs) + { + } + + virtual void runInternal() + { + for (PxU32 j = 0; j < mNumPairs; j++) + { + PxsCCDPair& pair = *mPairs[j]; + pair.sweepEstimateToi(); + pair.mEstimatePass = 0; + } + } + + virtual const char *getName() const + { + return "PxsContext.CCDSweep"; + } + +private: + PxsCCDSweepTask& operator=(const PxsCCDSweepTask&); +}; + +#define ENABLE_RESWEEP 1 + +// -------------------------------------------------------------- +/** +\brief Class to advance a set of islands +*/ +class PxsCCDAdvanceTask : public Cm::Task +{ + PxsCCDPair** mCCDPairs; + PxU32 mNumPairs; + PxsContext* mContext; + PxsCCDContext* mCCDContext; + PxReal mDt; + PxU32 mCCDPass; + const PxsCCDBodyArray& mCCDBodies; + + PxU32 mFirstThreadIsland; + PxU32 mIslandsPerThread; + PxU32 mTotalIslandCount; + PxU32 mFirstIslandPair; // pairs are sorted by island + PxsCCDBody** mIslandBodies; + PxU16* mNumIslandBodies; + PxI32* mSweepTotalHits; + bool mClipTrajectory; + bool mDisableResweep; + + PxsCCDAdvanceTask& operator=(const PxsCCDAdvanceTask&); +public: + PxsCCDAdvanceTask(PxsCCDPair** pairs, PxU32 nPairs, const PxsCCDBodyArray& ccdBodies, + PxsContext* context, PxsCCDContext* ccdContext, PxReal dt, PxU32 ccdPass, + PxU32 firstIslandPair, PxU32 firstThreadIsland, PxU32 islandsPerThread, PxU32 totalIslands, + PxsCCDBody** islandBodies, PxU16* numIslandBodies, bool clipTrajectory, bool disableResweep, + PxI32* sweepTotalHits) + : Cm::Task(context->getContextId()), mCCDPairs(pairs), mNumPairs(nPairs), mContext(context), mCCDContext(ccdContext), mDt(dt), + mCCDPass(ccdPass), mCCDBodies(ccdBodies), mFirstThreadIsland(firstThreadIsland), + mIslandsPerThread(islandsPerThread), mTotalIslandCount(totalIslands), mFirstIslandPair(firstIslandPair), + mIslandBodies(islandBodies), mNumIslandBodies(numIslandBodies), mSweepTotalHits(sweepTotalHits), + mClipTrajectory(clipTrajectory), mDisableResweep(disableResweep) + + { + PX_ASSERT(mFirstIslandPair < mNumPairs); + } + + virtual void runInternal() + { + + PxI32 sweepTotalHits = 0; + + PxcNpThreadContext* threadContext = mContext->getNpThreadContext(); + + // -------------------------------------------------------------------------------------- + // loop over island labels assigned to this thread + PxU32 islandStart = mFirstIslandPair; + PxU32 lastIsland = PxMin(mFirstThreadIsland + mIslandsPerThread, mTotalIslandCount); + for (PxU32 iIsland = mFirstThreadIsland; iIsland < lastIsland; iIsland++) + { + if (islandStart >= mNumPairs) + // this is possible when for instance there are two islands with 0 pairs in the second + // since islands are initially segmented using bodies, not pairs, it can happen + break; + + // -------------------------------------------------------------------------------------- + // sort all pairs within current island by toi + PxU32 islandEnd = islandStart+1; + PX_ASSERT(mCCDPairs[islandStart]->mIslandId == iIsland); + while (islandEnd < mNumPairs && mCCDPairs[islandEnd]->mIslandId == iIsland) // find first index past the current island id + islandEnd++; + + if (islandEnd > islandStart+1) + shdfnd::sort(mCCDPairs+islandStart, islandEnd-islandStart, ToiPtrCompare()); + + PX_ASSERT(islandEnd <= mNumPairs); + + // -------------------------------------------------------------------------------------- + // advance all affected pairs within each island to min toi + // for each pair (A,B) in toi order, find any later-toi pairs that collide against A or B + // and resweep against changed trajectories of either A or B (excluding statics and kinematics) + PxReal islandMinToi = PX_MAX_REAL; + PxU32 estimatePass = 1; + + PxReal dt = mDt; + + for (PxU32 iFront = islandStart; iFront < islandEnd; iFront++) + { + PxsCCDPair& pair = *mCCDPairs[iFront]; + + verifyCCDPair(pair); + + //If we have reached a pair with a TOI after 1.0, we can terminate this island + if(pair.mMinToi > 1.f) + break; + + bool needSweep0 = (pair.mBa0 && pair.mBa0->mCCD->mPassDone == false); + bool needSweep1 = (pair.mBa1 && pair.mBa1->mCCD->mPassDone == false); + + //If both bodies have been updated (or one has been updated and the other is static), we can skip to the next pair + if(!(needSweep0 || needSweep1)) + continue; + + { + //If the pair was an estimate, we must perform an accurate sweep now + if(pair.mToiType == PxsCCDPair::eEstimate) + { + pair.sweepFindToi(*threadContext, dt, mCCDPass); + + //Test to see if the pair is still the earliest pair. + if((iFront + 1) < islandEnd && mCCDPairs[iFront+1]->mMinToi < pair.mMinToi) + { + //If there is an earlier pair, we push this pair into its correct place in the list and return to the start + //of this update loop + PxsCCDPair* tmp = &pair; + PxU32 index = iFront; + while((index + 1) < islandEnd && mCCDPairs[index+1]->mMinToi < pair.mMinToi) + { + mCCDPairs[index] = mCCDPairs[index+1]; + ++index; + } + + mCCDPairs[index] = tmp; + + --iFront; + continue; + } + } + + if (pair.mMinToi > 1.f) + break; + + //We now have the earliest contact pair for this island and one/both of the bodies have not been updated. We now perform + //contact modification to find out if the user still wants to respond to the collision + if(pair.mMinToi <= islandMinToi && + pair.mIsModifiable && + mCCDContext->getCCDContactModifyCallback()) + { + + PX_ALIGN(16, PxU8 dataBuffer[sizeof(PxModifiableContact) + sizeof(PxContactPatch)]); + + PxContactPatch* patch = reinterpret_cast(dataBuffer); + PxModifiableContact* point = reinterpret_cast(patch + 1); + + patch->mMassModification.mInvInertiaScale0 = 1.f; + patch->mMassModification.mInvInertiaScale1 = 1.f; + patch->mMassModification.mInvMassScale0 = 1.f; + patch->mMassModification.mInvMassScale1 = 1.f; + + patch->normal = pair.mMinToiNormal; + + patch->dynamicFriction = pair.mDynamicFriction; + patch->staticFriction = pair.mStaticFriction; + patch->materialIndex0 = pair.mMaterialIndex0; + patch->materialIndex1 = pair.mMaterialIndex1; + + patch->startContactIndex = 0; + patch->nbContacts = 1; + + patch->materialFlags = 0; + patch->internalFlags = 0; //44 //Can be a U16 + + + point->contact = pair.mMinToiPoint; + point->normal = pair.mMinToiNormal; + + //KS - todo - reintroduce face indices!!!! + //point.internalFaceIndex0 = PXC_CONTACT_NO_FACE_INDEX; + //point.internalFaceIndex1 = pair.mFaceIndex; + point->materialIndex0 = pair.mMaterialIndex0; + point->materialIndex1 = pair.mMaterialIndex1; + point->dynamicFriction = pair.mDynamicFriction; + point->staticFriction = pair.mStaticFriction; + point->restitution = pair.mRestitution; + point->separation = 0.f; + point->maxImpulse = PX_MAX_REAL; + point->materialFlags = 0; + point->targetVelocity = PxVec3(0.f); + + mCCDContext->runCCDModifiableContact(point, 1, pair.mCCDShape0->mShapeCore, pair.mCCDShape1->mShapeCore, + pair.mCCDShape0->mRigidCore, pair.mCCDShape1->mRigidCore, pair.mBa0, pair.mBa1); + + if ((patch->internalFlags & PxContactPatch::eHAS_MAX_IMPULSE)) + pair.mMaxImpulse = point->maxImpulse; + + pair.mDynamicFriction = point->dynamicFriction; + pair.mStaticFriction = point->staticFriction; + pair.mRestitution = point->restitution; + pair.mMinToiPoint = point->contact; + pair.mMinToiNormal = point->normal; + + } + + } + + // pair.mIsEarliestToiHit is used for contact notification. + // only mark as such if this is the first impact for both atoms of this pair (the impacts are sorted) + // and there was an actual impact for this pair + bool atom0FirstSweep = (pair.mBa0 && pair.mBa0->mCCD->mPassDone == false) || pair.mBa0 == NULL; + bool atom1FirstSweep = (pair.mBa1 && pair.mBa1->mCCD->mPassDone == false) || pair.mBa1 == NULL; + if (pair.mMinToi <= 1.0f && atom0FirstSweep && atom1FirstSweep) + pair.mIsEarliestToiHit = true; + + // sweepAdvanceToToi sets mCCD->mPassDone flags on both atoms, doesn't advance atoms with flag already set + // can advance one atom if the other already has the flag set + bool advanced = pair.sweepAdvanceToToi( dt, mClipTrajectory); + if(pair.mMinToi < 0.f) + pair.mMinToi = 0.f; + verifyCCDPair(pair); + + if (advanced && pair.mMinToi <= 1.0f) + { + sweepTotalHits++; + PxU32 islandStartIndex = iIsland == 0 ? 0 : PxU32(mNumIslandBodies[iIsland - 1]); + PxU32 islandEndIndex = mNumIslandBodies[iIsland]; + + if(pair.mMinToi > 0.f) + { + for(PxU32 a = islandStartIndex; a < islandEndIndex; ++a) + { + if(!mIslandBodies[a]->mPassDone) + { + //If the body has not updated, we advance it to the current time-step that the island has reached. + PxsRigidBody* atom = mIslandBodies[a]->mBody; + atom->advancePrevPoseToToi(pair.mMinToi); + atom->mCCD->mTimeLeft = PxMax(atom->mCCD->mTimeLeft * (1.0f - pair.mMinToi), CCD_MIN_TIME_LEFT); + atom->mCCD->mUpdateCount++; + } + } + + //Adjust remaining dt for the island + dt -= dt * pair.mMinToi; + + const PxReal recipOneMinusToi = 1.f/(1.f - pair.mMinToi); + for(PxU32 k = iFront+1; k < islandEnd; ++k) + { + PxsCCDPair& pair1 = *mCCDPairs[k]; + pair1.mMinToi = (pair1.mMinToi - pair.mMinToi)*recipOneMinusToi; + } + + } + + //If we disabled response, we don't need to resweep at all + if(!mDisableResweep && !(pair.mCm->getWorkUnit().flags & PxcNpWorkUnitFlag::eDISABLE_RESPONSE) && pair.mMaxImpulse != 0.f) + { + void* a0 = pair.mBa0 == NULL ? NULL : reinterpret_cast(pair.mBa0); + void* a1 = pair.mBa1 == NULL ? NULL : reinterpret_cast(pair.mBa1); + + for(PxU32 k = iFront+1; k < islandEnd; ++k) + { + PxsCCDPair& pair1 = *mCCDPairs[k]; + + void* b0 = pair1.mBa0 == NULL ? reinterpret_cast(pair1.mCCDShape0) : reinterpret_cast(pair1.mBa0); + void* b1 = pair1.mBa1 == NULL ? reinterpret_cast(pair1.mCCDShape1) : reinterpret_cast(pair1.mBa1); + + bool containsStatic = pair1.mBa0 == NULL || pair1.mBa1 == NULL; + + PX_ASSERT(b0 != NULL && b1 != NULL); + + if ((!containsStatic) && + ((b0 == a0 && b1 != a1) || (b1 == a0 && b0 != a1) || + (b0 == a1 && b1 != a0) || (b1 == a1 && b0 != a0)) + ) + { + if(estimatePass != pair1.mEstimatePass) + { + pair1.mEstimatePass = estimatePass; + // resweep pair1 since either b0 or b1 trajectory has changed + PxReal oldToi = pair1.mMinToi; + verifyCCDPair(pair1); + PxReal toi1 = pair1.sweepEstimateToi(); + PX_ASSERT(pair1.mBa0); // this is because mMinToiNormal is the impact point here + if (toi1 < oldToi) + { + // if toi decreased, resort the array backwards + PxU32 kk = k; + PX_ASSERT(kk > 0); + + while (kk-1 > iFront && mCCDPairs[kk-1]->mMinToi > toi1) + { + PxsCCDPair* temp = mCCDPairs[kk-1]; + mCCDPairs[kk-1] = mCCDPairs[kk]; + mCCDPairs[kk] = temp; + kk--; + } + + } + else if (toi1 > oldToi) + { + // if toi increased, resort the array forwards + PxU32 kk = k; + PX_ASSERT(kk > 0); + PxU32 stepped = 0; + while (kk+1 < islandEnd && mCCDPairs[kk+1]->mMinToi < toi1) + { + stepped = 1; + PxsCCDPair* temp = mCCDPairs[kk+1]; + mCCDPairs[kk+1] = mCCDPairs[kk]; + mCCDPairs[kk] = temp; + kk++; + } + k -= stepped; + } + } + } + } + } + estimatePass++; + } // if pair.minToi <= 1.0f + } // for iFront + + islandStart = islandEnd; + } // for (PxU32 iIsland = mFirstThreadIsland; iIsland < lastIsland; iIsland++) + + Ps::atomicAdd(mSweepTotalHits, sweepTotalHits); + mContext->putNpThreadContext(threadContext); + } + + virtual const char *getName() const + { + return "PxsContext.CCDAdvance"; + } +}; + +// -------------------------------------------------------------- +// CCD main function +// Overall structure: +/* +for nPasses (passes are now handled in void Sc::Scene::updateCCDMultiPass) + + update CCD broadphase, generate a list of CMs + + foreach CM + create CCDPairs, CCDBodies from CM + add shapes, overlappingShapes to CCDBodies + + foreach CCDBody + assign island labels per body + uses overlappingShapes + + foreach CCDPair + assign island label to pair + + sort all pairs by islandId + + foreach CCDPair + sweep/find toi + compute normal: + + foreach island + sort within island by toi + foreach pair within island + advanceToToi + from curPairInIsland to lastPairInIsland + resweep if needed + +*/ +// -------------------------------------------------------------- +void PxsCCDContext::updateCCDBegin() +{ + openCCDLog(); + + miCCDPass = 0; + mSweepTotalHits = 0; +} + +// -------------------------------------------------------------- +void PxsCCDContext::updateCCDEnd() +{ + if (miCCDPass == mCCDMaxPasses - 1 || mSweepTotalHits == 0) + { + // -------------------------------------------------------------------------------------- + // At last CCD pass we need to reset mBody pointers back to NULL + // so that the next frame we know which ones need to be newly paired with PxsCCDBody objects + // also free the CCDBody memory blocks + + mMutex.lock(); + for (PxU32 j = 0, n = mCCDBodies.size(); j < n; j++) + { + if (mCCDBodies[j].mBody->mCCD && mCCDBodies[j].mBody->mCCD->mHasAnyPassDone) + { + //Record this body in the list of bodies that were updated + mUpdatedCCDBodies.pushBack(mCCDBodies[j].mBody); + } + mCCDBodies[j].mBody->mCCD = NULL; + mCCDBodies[j].mBody->getCore().isFastMoving = false; //Clear the "isFastMoving" bool + } + mMutex.unlock(); + + mCCDBodies.clear_NoDelete(); + + } + + mCCDShapes.clear_NoDelete(); + + mMap.clear(); + + miCCDPass++; +} + +// -------------------------------------------------------------- +void PxsCCDContext::verifyCCDBegin() +{ + #if 0 + // validate that all bodies have a NULL mCCD pointer + if (miCCDPass == 0) + { + Cm::BitMap::Iterator it(mActiveContactManager); + for (PxU32 index = it.getNext(); index != Cm::BitMap::Iterator::DONE; index = it.getNext()) + { + PxsContactManager* cm = mContactManagerPool.findByIndexFast(index); + PxsRigidBody* b0 = cm->mBodyShape0->getBodyAtom(), *b1 = cm->mBodyShape1->getBodyAtom(); + PX_ASSERT(b0 == NULL || b0->mCCD == NULL); + PX_ASSERT(b1 == NULL || b1->mCCD == NULL); + } + } + #endif +} + +void PxsCCDContext::resetContactManagers() +{ + Cm::BitMap::Iterator it(mContext->mContactManagersWithCCDTouch); + + for (PxU32 index = it.getNext(); index != Cm::BitMap::Iterator::DONE; index = it.getNext()) + { + PxsContactManager* cm = mContext->mContactManagerPool.findByIndexFast(index); + cm->clearCCDContactInfo(); + } + + mContext->mContactManagersWithCCDTouch.clear(); +} + +// -------------------------------------------------------------- +void PxsCCDContext::updateCCD(PxReal dt, PxBaseTask* continuation, IG::IslandSim& islandSim, bool disableResweep, PxI32 numFastMovingShapes) +{ + //Flag to run a slightly less-accurate version of CCD that will ensure that objects don't tunnel through the static world but is not as reliable for dynamic-dynamic collisions + mDisableCCDResweep = disableResweep; + mThresholdStream.clear(); // clear force threshold report stream + + mContext->clearManagerTouchEvents(); + + if (miCCDPass == 0) + { + resetContactManagers(); + } + + + // If we're not in the first pass and the previous pass had no sweeps or the BP didn't generate any fast-moving shapes, we can skip CCD entirely + if ((miCCDPass > 0 && mSweepTotalHits == 0) || (numFastMovingShapes == 0)) + { + mSweepTotalHits = 0; + updateCCDEnd(); + return; + } + mSweepTotalHits = 0; + + PX_ASSERT(continuation); + PX_ASSERT(continuation->getReference() > 0); + + //printf("CCD 1\n"); + + mCCDThreadContext = mContext->getNpThreadContext(); + mCCDThreadContext->mDt = dt; // doesn't get set anywhere else since it's only used for CCD now + + verifyCCDBegin(); + + // -------------------------------------------------------------------------------------- + // From a list of active CMs, build a temporary array of PxsCCDPair objects (allocated in blocks) + // this is done to gather scattered data from memory and also to reduce PxsRidigBody permanent memory footprint + // we have to do it every pass since new CMs can become fast moving after each pass (and sometimes cease to be) + mCCDPairs.clear_NoDelete(); + mCCDPtrPairs.forceSize_Unsafe(0); + + mUpdatedCCDBodies.forceSize_Unsafe(0); + + mCCDOverlaps.clear_NoDelete(); + + PxU32 nbKinematicStaticCollisions = 0; + + + bool needsSweep = false; + + { + PX_PROFILE_ZONE("Sim.ccdPair", mContext->mContextID); + + Cm::BitMap::Iterator it(mContext->mActiveContactManagersWithCCD); + for (PxU32 index = it.getNext(); index != Cm::BitMap::Iterator::DONE; index = it.getNext()) + { + PxsContactManager* cm = mContext->mContactManagerPool.findByIndexFast(index); + + // skip disabled pairs + if(!cm->getCCD()) + continue; + + bool isJoint0 = (cm->mNpUnit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) == PxcNpWorkUnitFlag::eARTICULATION_BODY0; + bool isJoint1 = (cm->mNpUnit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) == PxcNpWorkUnitFlag::eARTICULATION_BODY1; + // skip articulation vs articulation ccd + //Actually. This is fundamentally wrong also :(. We only want to skip links in the same articulation - not all articulations!!! + if (isJoint0 && isJoint1) + continue; + + bool isFastMoving0 = static_cast(cm->mNpUnit.rigidCore0)->isFastMoving != 0; + + bool isFastMoving1 = (cm->mNpUnit.flags & (PxcNpWorkUnitFlag::eARTICULATION_BODY1 | PxcNpWorkUnitFlag::eDYNAMIC_BODY1)) ? static_cast(cm->mNpUnit.rigidCore1)->isFastMoving != 0: false; + + if (!(isFastMoving0 || isFastMoving1)) + continue; + + PxcNpWorkUnit& unit = cm->getWorkUnit(); + const PxsRigidCore* rc0 = unit.rigidCore0; + const PxsRigidCore* rc1 = unit.rigidCore1; + + { + const PxsShapeCore* sc0 = unit.shapeCore0; + const PxsShapeCore* sc1 = unit.shapeCore1; + + PxsRigidBody* ba0 = cm->mRigidBody0; + PxsRigidBody* ba1 = cm->mRigidBody1; + + //Look up the body/shape pair in our CCDShape map + const Ps::Pair* ccdShapePair0 = mMap.find(PxsRigidShapePair(rc0, sc0)); + const Ps::Pair* ccdShapePair1 = mMap.find(PxsRigidShapePair(rc1, sc1)); + + //If the CCD shapes exist, extract them from the map + PxsCCDShape* ccdShape0 = ccdShapePair0 ? ccdShapePair0->second : NULL; + PxsCCDShape* ccdShape1 = ccdShapePair1 ? ccdShapePair1->second : NULL; + + PxReal threshold0 = 0.f; + PxReal threshold1 = 0.f; + + PxVec3 trA(0.f); + PxVec3 trB(0.f); + + if(ccdShape0 == NULL) + { + //If we hadn't already created ccdShape, create one + ccdShape0 = &mCCDShapes.pushBack(); + mMap.insert(PxsRigidShapePair(rc0, sc0), ccdShape0); + + ccdShape0->mRigidCore = rc0; + ccdShape0->mShapeCore = sc0; + ccdShape0->mGeometry = &sc0->geometry; + + const PxTransform tm0 = ccdShape0->getAbsPose(ba0); + const PxTransform oldTm0 = ba0 ? ccdShape0->getLastCCDAbsPose(ba0) : tm0; + + trA = tm0.p - oldTm0.p; + + Vec3p origin, extents; + + //Compute the shape's bounds and CCD threshold + threshold0 = computeBoundsWithCCDThreshold(origin, extents, sc0->geometry.getGeometry(), tm0, NULL); + + //Set up the CCD shape + ccdShape0->mCenter = origin - trA; + ccdShape0->mExtents = extents; + ccdShape0->mFastMovingThreshold = threshold0; + ccdShape0->mPrevTransform = oldTm0; + ccdShape0->mCurrentTransform = tm0; + ccdShape0->mUpdateCount = 0; + ccdShape0->mNodeIndex = islandSim.getNodeIndex1(cm->getWorkUnit().mEdgeIndex); + } + else + { + //We had already created the shape, so extract the threshold and translation components + threshold0 = ccdShape0->mFastMovingThreshold; + trA = ccdShape0->mCurrentTransform.p - ccdShape0->mPrevTransform.p; + } + + if(ccdShape1 == NULL) + { + //If the CCD shape was not already constructed, create it + ccdShape1 = &mCCDShapes.pushBack(); + ccdShape1->mRigidCore = rc1; + ccdShape1->mShapeCore = sc1; + ccdShape1->mGeometry = &sc1->geometry; + + mMap.insert(PxsRigidShapePair(rc1, sc1), ccdShape1); + + const PxTransform tm1 = ccdShape1->getAbsPose(ba1); + const PxTransform oldTm1 = ba1 ? ccdShape1->getLastCCDAbsPose(ba1) : tm1; + + trB = tm1.p - oldTm1.p; + + Vec3p origin, extents; + //Compute the shape's bounds and CCD threshold + threshold1 = computeBoundsWithCCDThreshold(origin, extents, sc1->geometry.getGeometry(), tm1, NULL); + + //Set up the CCD shape + ccdShape1->mCenter = origin - trB; + ccdShape1->mExtents = extents; + ccdShape1->mFastMovingThreshold = threshold1; + ccdShape1->mPrevTransform = oldTm1; + ccdShape1->mCurrentTransform = tm1; + ccdShape1->mUpdateCount = 0; + ccdShape1->mNodeIndex = islandSim.getNodeIndex2(cm->getWorkUnit().mEdgeIndex); + } + else + { + //CCD shape already constructed so just extract thresholds and trB components + threshold1 = ccdShape1->mFastMovingThreshold; + trB = ccdShape1->mCurrentTransform.p - ccdShape1->mPrevTransform.p; + } + + { + //Initialize the CCD bodies + PxsRigidBody* atoms[2] = {ba0, ba1}; + for (int k = 0; k < 2; k++) + { + PxsRigidBody* b = atoms[k]; + //If there isn't a body (i.e. it's a static), no need to create a CCD body + if (!b) + continue; + if (b->mCCD == NULL) + { + // this rigid body has no CCD body created for it yet. Create and initialize one. + PxsCCDBody& newB = mCCDBodies.pushBack(); + b->mCCD = &newB; + b->mCCD->mIndex = Ps::to16(mCCDBodies.size()-1); + b->mCCD->mBody = b; + b->mCCD->mTimeLeft = 1.0f; + b->mCCD->mOverlappingObjects = NULL; + b->mCCD->mUpdateCount = 0; + b->mCCD->mHasAnyPassDone = false; + b->mCCD->mNbInteractionsThisPass = 0; + } + b->mCCD->mPassDone = 0; + b->mCCD->mNbInteractionsThisPass++; + } + if(ba0 && ba1) + { + //If both bodies exist (i.e. this is dynamic-dynamic collision), we create an + //overlap between the 2 bodies used for island detection. + if(!(ba0->isKinematic() || ba1->isKinematic())) + { + if(!ba0->mCCD->overlaps(ba1->mCCD)) + { + PxsCCDOverlap* overlapA = &mCCDOverlaps.pushBack(); + PxsCCDOverlap* overlapB = &mCCDOverlaps.pushBack(); + + overlapA->mBody = ba1->mCCD; + overlapB->mBody = ba0->mCCD; + + ba0->mCCD->addOverlap(overlapA); + ba1->mCCD->addOverlap(overlapB); + } + } + } + } + + //We now create the CCD pair. These are used in the CCD sweep and update phases + if (ba0->isKinematic() && (ba1 == NULL || ba1->isKinematic())) + nbKinematicStaticCollisions++; + { + PxsCCDPair& p = mCCDPairs.pushBack(); + p.mBa0 = ba0; + p.mBa1 = ba1; + p.mCCDShape0 = ccdShape0; + p.mCCDShape1 = ccdShape1; + p.mHasFriction = rc0->hasCCDFriction() || rc1->hasCCDFriction(); + p.mMinToi = PX_MAX_REAL; + p.mG0 = cm->mNpUnit.shapeCore0->geometry.getType(); + p.mG1 = cm->mNpUnit.shapeCore1->geometry.getType(); + p.mCm = cm; + p.mIslandId = 0xFFFFffff; + p.mIsEarliestToiHit = false; + p.mFaceIndex = PXC_CONTACT_NO_FACE_INDEX; + p.mIsModifiable = cm->isChangeable() != 0; + p.mAppliedForce = 0.f; + p.mMaxImpulse = PxMin((ba0->mCore->mFlags & PxRigidBodyFlag::eENABLE_CCD_MAX_CONTACT_IMPULSE) ? ba0->mCore->maxContactImpulse : PX_MAX_F32, + (ba1 && ba1->mCore->mFlags & PxRigidBodyFlag::eENABLE_CCD_MAX_CONTACT_IMPULSE) ? ba1->mCore->maxContactImpulse : PX_MAX_F32); + +#if PX_ENABLE_SIM_STATS + mContext->mSimStats.mNbCCDPairs[PxMin(p.mG0, p.mG1)][PxMax(p.mG0, p.mG1)] ++; +#endif + + //Calculate the sum of the thresholds and work out if we need to perform a sweep. + + const PxReal thresh = threshold0 + threshold1; + //If no shape pairs in the entire scene are fast-moving, we can bypass the entire of the CCD. + needsSweep = needsSweep || (trA - trB).magnitudeSquared() >= (thresh * thresh); + } + + } + } + //There are no fast-moving pairs in this scene, so we can terminate right now without proceeding any further + if(!needsSweep) + { + updateCCDEnd(); + mContext->putNpThreadContext(mCCDThreadContext); + return; + } + } + + //Create the pair pointer buffer. This is a flattened array of pointers to pairs. It is used to sort the pairs + //into islands and is also used to prioritize the pairs into their TOIs + { + const PxU32 size = mCCDPairs.size(); + mCCDPtrPairs.reserve(size); + for(PxU32 a = 0; a < size; ++a) + { + mCCDPtrPairs.pushBack(&mCCDPairs[a]); + } + + mThresholdStream.reserve(Ps::nextPowerOfTwo(size)); + + for (PxU32 a = 0; a < mCCDBodies.size(); ++a) + { + mCCDBodies[a].mPreSolverVelocity.linear = mCCDBodies[a].mBody->getLinearVelocity(); + mCCDBodies[a].mPreSolverVelocity.angular = mCCDBodies[a].mBody->getAngularVelocity(); + } + } + + + PxU32 ccdBodyCount = mCCDBodies.size(); + + // -------------------------------------------------------------------------------------- + // assign island labels + const PxU16 noLabelYet = 0xFFFF; + + //Temporary array allocations. Ideally, we should use the scratch pad for there + Array islandLabels; + islandLabels.resize(ccdBodyCount); + Array stack; + stack.reserve(ccdBodyCount); + stack.forceSize_Unsafe(ccdBodyCount); + + + //Initialize all islands labels (for each body) to be unitialized + mIslandSizes.forceSize_Unsafe(0); + mIslandSizes.reserve(ccdBodyCount + 1); + mIslandSizes.forceSize_Unsafe(ccdBodyCount + 1); + for (PxU32 j = 0; j < ccdBodyCount; j++) + islandLabels[j] = noLabelYet; + + PxU32 islandCount = 0; + PxU32 stackSize = 0; + const PxsCCDBody* top = NULL; + + for (PxU32 j = 0; j < ccdBodyCount; j++) + { + //If the body has already been labelled or if it is kinematic, continue + //Also, if the body has no interactions this pass, continue. In single-pass CCD, only bodies with interactions would be part of the CCD. However, + //with multi-pass CCD, we keep all bodies that interacted in previous passes. If the body now has no interactions, we skip it to ensure that island grouping doesn't fail in + //later stages by assigning an island ID to a body with no interactions + if (islandLabels[j] != noLabelYet || mCCDBodies[j].mBody->isKinematic() || mCCDBodies[j].mNbInteractionsThisPass == 0) + continue; + + top = &mCCDBodies[j]; + //Otherwise push it back into the queue and proceed + islandLabels[j] = islandCount; + + stack[stackSize++] = top; + // assign new label to unlabeled atom + // assign the same label to all connected nodes using stack traversal + PxU16 islandSize = 0; + while (stackSize > 0) + { + --stackSize; + const PxsCCDBody* ccdb = top; + top = stack[PxMax(1u, stackSize)-1]; + + PxsCCDOverlap* overlaps = ccdb->mOverlappingObjects; + while(overlaps) + { + if (islandLabels[overlaps->mBody->mIndex] == noLabelYet) // non-static & unlabeled? + { + islandLabels[overlaps->mBody->mIndex] = islandCount; + stack[stackSize++] = overlaps->mBody; // push adjacent node to the top of the stack + top = overlaps->mBody; + islandSize++; + } + overlaps = overlaps->mNext; + } + } + //Record island size + mIslandSizes[islandCount] = PxU16(islandSize + 1); + islandCount++; + } + + PxU32 kinematicIslandId = islandCount; + + islandCount += nbKinematicStaticCollisions; + + for (PxU32 i = kinematicIslandId; i < islandCount; ++i) + mIslandSizes[i] = 1; + + + + // -------------------------------------------------------------------------------------- + // label pairs with island ids + // (need an extra loop since we don't maintain a mapping from atom to all of it's pairs) + mCCDIslandHistogram.clear(); // number of pairs per island + mCCDIslandHistogram.resize(islandCount); + + PxU32 totalActivePairs = 0; + for (PxU32 j = 0, n = mCCDPtrPairs.size(); j < n; j++) + { + const PxU32 staticLabel = 0xFFFFffff; + PxsCCDPair& p = *mCCDPtrPairs[j]; + PxU32 id0 = p.mBa0 && !p.mBa0->isKinematic()? islandLabels[p.mBa0->mCCD->getIndex()] : staticLabel; + PxU32 id1 = p.mBa1 && !p.mBa1->isKinematic()? islandLabels[p.mBa1->mCCD->getIndex()] : staticLabel; + + PxU32 islandId = PxMin(id0, id1); + if (islandId == staticLabel) + islandId = kinematicIslandId++; + + p.mIslandId = islandId; + mCCDIslandHistogram[p.mIslandId] ++; + PX_ASSERT(p.mIslandId != staticLabel); + totalActivePairs++; + } + + PxU16 count = 0; + for(PxU16 a = 0; a < islandCount+1; ++a) + { + PxU16 islandSize = mIslandSizes[a]; + mIslandSizes[a] = count; + count += islandSize; + } + + mIslandBodies.forceSize_Unsafe(0); + mIslandBodies.reserve(ccdBodyCount); + mIslandBodies.forceSize_Unsafe(ccdBodyCount); + for(PxU32 a = 0; a < mCCDBodies.size(); ++a) + { + const PxU32 island = islandLabels[mCCDBodies[a].mIndex]; + if (island != 0xFFFF) + { + PxU16 writeIndex = mIslandSizes[island]; + mIslandSizes[island] = PxU16(writeIndex + 1); + mIslandBodies[writeIndex] = &mCCDBodies[a]; + } + } + + // -------------------------------------------------------------------------------------- + // setup tasks + mPostCCDDepenetrateTask.setContinuation(continuation); + mPostCCDAdvanceTask.setContinuation(&mPostCCDDepenetrateTask); + mPostCCDSweepTask.setContinuation(&mPostCCDAdvanceTask); + + // -------------------------------------------------------------------------------------- + // sort all pairs by islands + shdfnd::sort(mCCDPtrPairs.begin(), mCCDPtrPairs.size(), IslandPtrCompare()); + + // -------------------------------------------------------------------------------------- + // sweep all CCD pairs + const PxU32 nPairs = mCCDPtrPairs.size(); + const PxU32 numThreads = PxMax(1u, mContext->mTaskManager->getCpuDispatcher()->getWorkerCount()); PX_ASSERT(numThreads > 0); + mCCDPairsPerBatch = PxMax((nPairs)/numThreads, 1); + + for (PxU32 batchBegin = 0; batchBegin < nPairs; batchBegin += mCCDPairsPerBatch) + { + void* ptr = mContext->mTaskPool.allocate(sizeof(PxsCCDSweepTask)); + PX_ASSERT_WITH_MESSAGE(ptr, "Failed to allocate PxsCCDSweepTask"); + const PxU32 batchEnd = PxMin(nPairs, batchBegin + mCCDPairsPerBatch); + PX_ASSERT(batchEnd >= batchBegin); + PxsCCDSweepTask* task = PX_PLACEMENT_NEW(ptr, PxsCCDSweepTask)(mContext->getContextId(), mCCDPtrPairs.begin() + batchBegin, batchEnd - batchBegin); + task->setContinuation(*mContext->mTaskManager, &mPostCCDSweepTask); + task->removeReference(); + } + + mPostCCDSweepTask.removeReference(); + mPostCCDAdvanceTask.removeReference(); + mPostCCDDepenetrateTask.removeReference(); +} + +void PxsCCDContext::postCCDSweep(PxBaseTask* continuation) +{ + // -------------------------------------------------------------------------------------- + // batch up the islands and send them over to worker threads + PxU32 firstIslandPair = 0; + PxU32 islandCount = mCCDIslandHistogram.size(); + for (PxU32 firstIslandInBatch = 0; firstIslandInBatch < islandCount;) + { + PxU32 pairSum = 0; + PxU32 lastIslandInBatch = firstIslandInBatch+1; + PxU32 j; + // add up the numbers in the histogram until we reach target pairsPerBatch + for (j = firstIslandInBatch; j < islandCount; j++) + { + pairSum += mCCDIslandHistogram[j]; + if (pairSum > mCCDPairsPerBatch) + { + lastIslandInBatch = j+1; + break; + } + } + if (j == islandCount) // j is islandCount if not enough pairs were left to fill up to pairsPerBatch + { + if (pairSum == 0) + break; // we are done and there are no islands in this batch + lastIslandInBatch = islandCount; + } + + void* ptr = mContext->mTaskPool.allocate(sizeof(PxsCCDAdvanceTask)); + PX_ASSERT_WITH_MESSAGE(ptr , "Failed to allocate PxsCCDSweepTask"); + bool clipTrajectory = (miCCDPass == mCCDMaxPasses-1); + PxsCCDAdvanceTask* task = PX_PLACEMENT_NEW(ptr, PxsCCDAdvanceTask) ( + mCCDPtrPairs.begin(), mCCDPtrPairs.size(), mCCDBodies, mContext, this, mCCDThreadContext->mDt, miCCDPass, + firstIslandPair, firstIslandInBatch, lastIslandInBatch-firstIslandInBatch, islandCount, + mIslandBodies.begin(), mIslandSizes.begin(), clipTrajectory, mDisableCCDResweep, + &mSweepTotalHits); + firstIslandInBatch = lastIslandInBatch; + firstIslandPair += pairSum; + task->setContinuation(*mContext->mTaskManager, continuation); + task->removeReference(); + } // for iIsland +} + +void PxsCCDContext::postCCDAdvance(PxBaseTask* /*continuation*/) +{ + // -------------------------------------------------------------------------------------- + // contact notifications: update touch status (multi-threading this section would probably slow it down but might be worth a try) + PxU32 countLost = 0, countFound = 0, countRetouch = 0; + + PxU32 islandCount = mCCDIslandHistogram.size(); + PxU32 index = 0; + + for (PxU32 island = 0; island < islandCount; ++island) + { + PxU32 islandEnd = mCCDIslandHistogram[island] + index; + for(PxU32 j = index; j < islandEnd; ++j) + { + PxsCCDPair& p = *mCCDPtrPairs[j]; + //The CCD pairs are ordered by TOI. If we reach a TOI > 1, we can terminate + if(p.mMinToi > 1.f) + break; + + //If this was the earliest touch for the pair of bodies, we can notify the user about it. If not, it's a future collision that we haven't stepped to yet + if(p.mIsEarliestToiHit) + { + //Flag that we had a CCD contact + p.mCm->setHadCCDContact(); + + //Test/set the changed touch map + PxU16 oldTouch = p.mCm->getTouchStatus(); + if (!oldTouch) + { + mContext->mContactManagerTouchEvent.growAndSet(p.mCm->getIndex()); + p.mCm->mNpUnit.statusFlags = PxU16((p.mCm->mNpUnit.statusFlags & (~PxcNpWorkUnitStatusFlag::eHAS_NO_TOUCH)) | PxcNpWorkUnitStatusFlag::eHAS_TOUCH); + //Also need to write it in the CmOutput structure!!!!! + + //The achieve this, we need to unregister the CM from the Nphase, then re-register it with the status set. This is the only way to force a push to the GPU + mNphaseContext.unregisterContactManager(p.mCm); + mNphaseContext.registerContactManager(p.mCm, 1, 0); + countFound++; + } + else + { + mContext->mContactManagerTouchEvent.growAndSet(p.mCm->getIndex()); + p.mCm->raiseCCDRetouch(); + countRetouch++; + } + + //Do we want to create reports? + const bool createReports = + p.mCm->mNpUnit.flags & PxcNpWorkUnitFlag::eOUTPUT_CONTACTS + || (p.mCm->mNpUnit.flags & PxcNpWorkUnitFlag::eFORCE_THRESHOLD + && ((p.mCm->mNpUnit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY0 && static_cast(p.mCm->mNpUnit.rigidCore0)->shouldCreateContactReports()) + || (p.mCm->mNpUnit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1 && static_cast(p.mCm->mNpUnit.rigidCore1)->shouldCreateContactReports()))); + + if(createReports) + { + mContext->mContactManagersWithCCDTouch.growAndSet(p.mCm->getIndex()); + + const PxU32 numContacts = 1; + PxsMaterialInfo matInfo; + Gu::ContactBuffer& buffer = mCCDThreadContext->mContactBuffer; + + Gu::ContactPoint& cp = buffer.contacts[0]; + cp.point = p.mMinToiPoint; + cp.normal = -p.mMinToiNormal; //KS - discrete contact gen produces contacts pointing in the opposite direction to CCD sweeps + cp.internalFaceIndex1 = p.mFaceIndex; + cp.separation = 0.0f; + cp.restitution = p.mRestitution; + cp.dynamicFriction = p.mDynamicFriction; + cp.staticFriction = p.mStaticFriction; + cp.targetVel = PxVec3(0.f); + cp.maxImpulse = PX_MAX_REAL; + + matInfo.mMaterialIndex0 = p.mMaterialIndex0; + matInfo.mMaterialIndex1 = p.mMaterialIndex1; + + //Write contact stream for the contact. This will allocate memory for the contacts and forces + PxReal* contactForces; + //PxU8* contactStream; + PxU8* contactPatches; + PxU8* contactPoints; + PxU16 contactStreamSize; + PxU8 contactCount; + PxU8 nbPatches; + PxsCCDContactHeader* ccdHeader = reinterpret_cast(p.mCm->mNpUnit.ccdContacts); + if (writeCompressedContact(buffer.contacts, numContacts, mCCDThreadContext, contactCount, contactPatches, + contactPoints, contactStreamSize, contactForces, numContacts*sizeof(PxReal), mCCDThreadContext->mMaterialManager, + ((p.mCm->mNpUnit.flags & PxcNpWorkUnitFlag::eMODIFIABLE_CONTACT) != 0), true, &matInfo, nbPatches, sizeof(PxsCCDContactHeader),NULL, NULL, + false, NULL, NULL, NULL, p.mFaceIndex != PXC_CONTACT_NO_FACE_INDEX)) + { + PxsCCDContactHeader* newCCDHeader = reinterpret_cast(contactPatches); + newCCDHeader->contactStreamSize = Ps::to16(contactStreamSize); + newCCDHeader->isFromPreviousPass = 0; + + p.mCm->mNpUnit.ccdContacts = contactPatches; // put the latest stream at the head of the linked list since it needs to get accessed every CCD pass + // to prepare the reports + + if (!ccdHeader) + newCCDHeader->nextStream = NULL; + else + { + newCCDHeader->nextStream = ccdHeader; + ccdHeader->isFromPreviousPass = 1; + } + + //And write the force and contact count + PX_ASSERT(contactForces != NULL); + contactForces[0] = p.mAppliedForce; + } + else if (!ccdHeader) + { + p.mCm->mNpUnit.ccdContacts = NULL; + // we do not set the status flag on failure because the pair might have written + // a contact stream sucessfully during discrete collision this frame. + } + else + ccdHeader->isFromPreviousPass = 1; + + //If the touch event already existed, the solver would have already configured the threshold stream + if((p.mCm->mNpUnit.flags & (PxcNpWorkUnitFlag::eARTICULATION_BODY0 | PxcNpWorkUnitFlag::eARTICULATION_BODY1)) == 0 && p.mAppliedForce) + { +#if 1 + ThresholdStreamElement elt; + elt.normalForce = p.mAppliedForce; + elt.accumulatedForce = 0.f; + elt.threshold = PxMin(p.mBa0 == NULL ? PX_MAX_REAL : p.mBa0->mCore->contactReportThreshold, p.mBa1 == NULL ? PX_MAX_REAL : + p.mBa1->mCore->contactReportThreshold); + elt.nodeIndexA = p.mCCDShape0->mNodeIndex; + elt.nodeIndexB =p.mCCDShape1->mNodeIndex; + Ps::order(elt.nodeIndexA,elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA.index() < elt.nodeIndexB.index()); + mThresholdStream.pushBack(elt); +#endif + } + } + } + } + index = islandEnd; + } + + mContext->mCMTouchEventCount[PXS_LOST_TOUCH_COUNT] += countLost; + mContext->mCMTouchEventCount[PXS_NEW_TOUCH_COUNT] += countFound; + mContext->mCMTouchEventCount[PXS_CCD_RETOUCH_COUNT] += countRetouch; +} + +void PxsCCDContext::postCCDDepenetrate(PxBaseTask* /*continuation*/) +{ + // -------------------------------------------------------------------------------------- + // reset mOverlappingShapes array for all bodies + // we do it each pass because this set can change due to movement as well as new objects + // becoming fast moving due to intra-frame impacts + + for (PxU32 j = 0; j < mCCDBodies.size(); j ++) + { + mCCDBodies[j].mOverlappingObjects = NULL; + mCCDBodies[j].mNbInteractionsThisPass = 0; + } + + mCCDOverlaps.clear_NoDelete(); + + updateCCDEnd(); + + mContext->putNpThreadContext(mCCDThreadContext); + + flushCCDLog(); +} + +Cm::SpatialVector PxsRigidBody::getPreSolverVelocities() const +{ + if (mCCD) + return mCCD->mPreSolverVelocity; + return Cm::SpatialVector(PxVec3(0.f), PxVec3(0.f)); +} + + +PxTransform PxsRigidBody::getAdvancedTransform(PxReal toi) const +{ + //If it is kinematic, just return identity. We don't fully support kinematics yet + if (isKinematic()) + return PxTransform(PxIdentity); + + //Otherwise we interpolate the pose between the current and previous pose and return that pose + PxVec3 newLastP = mLastTransform.p*(1.0f-toi) + mCore->body2World.p*toi; // advance mLastTransform position to toi + PxQuat newLastQ = slerp(toi, getLastCCDTransform().q, mCore->body2World.q); // advance mLastTransform rotation to toi + return PxTransform(newLastP, newLastQ); +} + +void PxsRigidBody::advancePrevPoseToToi(PxReal toi) +{ + //If this is kinematic, just return + if (isKinematic()) + return; + + //update latest pose + PxVec3 newLastP = mLastTransform.p*(1.0f-toi) + mCore->body2World.p*toi; // advance mLastTransform position to toi + mLastTransform.p = newLastP; +#if CCD_ROTATION_LOCKING + mCore->body2World.q = getLastCCDTransform().q; +#else + // slerp from last transform to current transform with ratio of toi + PxQuat newLastQ = slerp(toi, getLastCCDTransform().q, mCore->body2World.q); // advance mLastTransform rotation to toi + mLastTransform.q = newLastQ; +#endif + + + +} + + +void PxsRigidBody::advanceToToi(PxReal toi, PxReal dt, bool clip) +{ + if (isKinematic()) + return; + + + if (clip) + { + //If clip is true, we set the previous and current pose to be the same. This basically makes the object appear stationary in the CCD + mCore->body2World.p = getLastCCDTransform().p; +#if !CCD_ROTATION_LOCKING + mCore->body2World.q = getLastCCDTransform().q; +#endif + } + else + { + // advance new CCD target after impact to remaining toi using post-impact velocities + mCore->body2World.p = getLastCCDTransform().p + getLinearVelocity() * dt * (1.0f - toi); +#if !CCD_ROTATION_LOCKING + PxVec3 angularDelta = getAngularVelocity() * dt * (1.0f - toi); + PxReal deltaMag = angularDelta.magnitude(); + PxVec3 deltaAng = deltaMag > 1e-20f ? angularDelta / deltaMag : PxVec3(1.0f, 0.0f, 0.0f); + PxQuat angularQuat(deltaMag, deltaAng); + mCore->body2World.q = getLastCCDTransform().q * angularQuat; +#endif + PX_ASSERT(mCore->body2World.isSane()); + } + + // rescale total time left to elapse this frame + mCCD->mTimeLeft = PxMax(mCCD->mTimeLeft * (1.0f - toi), CCD_MIN_TIME_LEFT); +} + + +void PxsCCDContext::runCCDModifiableContact(PxModifiableContact* PX_RESTRICT contacts, PxU32 contactCount, const PxsShapeCore* PX_RESTRICT shapeCore0, + const PxsShapeCore* PX_RESTRICT shapeCore1, const PxsRigidCore* PX_RESTRICT rigidCore0, const PxsRigidCore* PX_RESTRICT rigidCore1, + const PxsRigidBody* PX_RESTRICT rigid0, const PxsRigidBody* PX_RESTRICT rigid1) +{ + if(!mCCDContactModifyCallback) + return; + + class PxcContactSet: public PxContactSet + { + public: + PxcContactSet(PxU32 count, PxModifiableContact* contacts_) + { + mContacts = contacts_; + mCount = count; + } + }; + { + PxContactModifyPair p; + + p.shape[0] = gPxvOffsetTable.convertPxsShape2Px(shapeCore0); + p.shape[1] = gPxvOffsetTable.convertPxsShape2Px(shapeCore1); + + p.actor[0] = rigid0 != NULL ? gPxvOffsetTable.convertPxsRigidCore2PxRigidBody(rigidCore0) + : gPxvOffsetTable.convertPxsRigidCore2PxRigidStatic(rigidCore0); + + p.actor[1] = rigid1 != NULL ? gPxvOffsetTable.convertPxsRigidCore2PxRigidBody(rigidCore1) + : gPxvOffsetTable.convertPxsRigidCore2PxRigidStatic(rigidCore1); + + p.transform[0] = getShapeAbsPose(shapeCore0, rigidCore0, PxU32(rigid0 != NULL)); + p.transform[1] = getShapeAbsPose(shapeCore1, rigidCore1, PxU32(rigid1 != NULL)); + + static_cast(p.contacts) = + PxcContactSet(contactCount, contacts); + + mCCDContactModifyCallback->onCCDContactModify(&p, 1); + } +} + + +} //namespace physx + + diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp new file mode 100644 index 000000000..a9c84dd27 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxsContactManager.h" +#include "PxsRigidBody.h" +#include "PxcContactMethodImpl.h" +#include "PxvManager.h" +#include "PxsIslandSim.h" + +using namespace physx; + +PxsContactManager::PxsContactManager(PxsContext*, PxU32 index) /*: + mUserData (NULL)*/ +{ + mFlags = 0; + + // PT: TODO: any reason why we don't initialize all members here, e.g. shapeCore pointers? + mNpUnit.index = index; + mNpUnit.rigidCore0 = NULL; + mNpUnit.rigidCore1 = NULL; + mNpUnit.restDistance = 0; + mNpUnit.dominance0 = 1u; + mNpUnit.dominance1 = 1u; + mNpUnit.frictionDataPtr = NULL; + mNpUnit.frictionPatchCount = 0; +} + +PxsContactManager::~PxsContactManager() +{ +} + + +void PxsContactManager::setCCD(bool enable) +{ + PxU32 flags = mFlags & (~PXS_CM_CCD_CONTACT); + if (enable) + flags |= PXS_CM_CCD_LINEAR; + else + flags &= ~PXS_CM_CCD_LINEAR; + + mFlags = flags; +} + + + +void PxsContactManager::resetCachedState() +{ + // happens when the body transform or shape relative transform changes. + + PxcNpWorkUnitClearCachedState(mNpUnit); +} + +void PxsContactManager::resetFrictionCachedState() +{ + // happens when the body transform or shape relative transform changes. + + PxcNpWorkUnitClearFrictionCachedState(mNpUnit); +} + + diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp new file mode 100644 index 000000000..56412e13c --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp @@ -0,0 +1,623 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "PxvConfig.h" +#include "PxcContactCache.h" +#include "PxsRigidBody.h" +#include "PxsContactManager.h" +#include "PxsContext.h" +#include "PxPhysXConfig.h" + +#include "CmBitMap.h" +#include "CmFlushPool.h" + +#include "PxsMaterialManager.h" +#include "PxSceneDesc.h" +#include "PxsCCD.h" +#include "PxvGeometry.h" +#include "PxvManager.h" +#include "PxsSimpleIslandManager.h" + +#if PX_SUPPORT_GPU_PHYSX +#include "task/PxGpuDispatcher.h" +#include "PxPhysXGpu.h" +#endif + +#include "PxcNpContactPrepShared.h" +#include "PxcNpCache.h" + + +using namespace physx; +using namespace physx::shdfnd; + +#define PXS_CONTACTMANAGER_SLABSIZE 1024 +#define PXS_MAX_CONTACTMANAGER_SLABS 64 + +#define PXS_BODYSHAPE_SLABSIZE 1024 +#define PXS_MAX_BODYSHAPE_SLABS 16 + +PxsContext::PxsContext(const PxSceneDesc& desc, PxTaskManager* taskManager, Cm::FlushPool& taskPool, PxU64 contextID) : + mNpThreadContextPool (this), + mContactManagerPool ("mContactManagerPool", this, 256, 8192), + mManifoldPool ("mManifoldPool", 256), + mSphereManifoldPool ("mSphereManifoldPool", 256), + mContactModifyCallback (NULL), + mNpImplementationContext (NULL), + mNpFallbackImplementationContext(NULL), + mTaskManager (taskManager), + mTaskPool (taskPool), + mPCM (desc.flags & PxSceneFlag::eENABLE_PCM), + mContactCache (false), + mCreateAveragePoint (desc.flags & PxSceneFlag::eENABLE_AVERAGE_POINT), + mContextID (contextID) +{ + clearManagerTouchEvents(); + mVisualizationCullingBox.setMaximal(); + + PxMemZero(mVisualizationParams, sizeof(PxReal) * PxVisualizationParameter::eNUM_VALUES); + + mNpMemBlockPool.init(desc.nbContactDataBlocks, desc.maxNbContactDataBlocks); +} + +PxsContext::~PxsContext() +{ + if(mTransformCache) + { + mTransformCache->~PxsTransformCache(); + PX_FREE(mTransformCache); + } + mTransformCache = NULL; + + mContactManagerPool.destroy(); //manually destroy the contact manager pool, otherwise pool deletion order is random and we can get into trouble with references into other pools needed during destruction. +} + +// =========================== Create methods +namespace physx +{ + bool gEnablePCMCaching[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT] = + { + //eSPHERE, + { + false, //eSPHERE + false, //ePLANE + false, //eCAPSULE + false, //eBOX + true, //eCONVEXMESH + true, //eTRIANGLEMESH + true //eHEIGHTFIELD + }, + + //ePLANE + { + false, //eSPHERE + false, //ePLANE + true, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + false, //eTRIANGLEMESH + false //eHEIGHTFIELD + }, + + //eCAPSULE, + { + false, //eSPHERE + true, //ePLANE + false, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + true, //eTRIANGLEMESH + true //eHEIGHTFIELD + }, + + //eBOX, + { + false, //eSPHERE + true, //ePLANE + true, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + true, //eTRIANGLEMESH + true //eHEIGHTFIELD + }, + + //eCONVEXMESH, + { + true, //eSPHERE + true, //ePLANE + true, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + true, //eTRIANGLEMESH + true //eHEIGHTFIELD + }, + //eTRIANGLEMESH, + { + true, //eSPHERE + false, //ePLANE + true, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + false, //eTRIANGLEMESH + false //eHEIGHTFIELD + }, + + //eHEIGHTFIELD, + { + true, //eSPHERE + false, //ePLANE + true, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + false, //eTRIANGLEMESH + false //eHEIGHTFIELD + } + }; +} + +void PxsContext::createTransformCache(Ps::VirtualAllocatorCallback& allocatorCallback) +{ + mTransformCache = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxsTransformCache), PX_DEBUG_EXP("PxsTransformCache")), PxsTransformCache(allocatorCallback)); +} + +PxsContactManager* PxsContext::createContactManager(PxsContactManager* contactManager, const bool useCCD) +{ + PxsContactManager* cm = contactManager? contactManager : mContactManagerPool.get(); + + if(cm) + { + PxcNpWorkUnitClearContactState(cm->getWorkUnit()); + PxcNpWorkUnitClearCachedState(cm->getWorkUnit()); + + if (contactManager == NULL) + { + if (cm->getIndex() >= mActiveContactManager.size()) + { + PxU32 newSize = (2 * cm->getIndex() + 256)&~255; + mActiveContactManager.resize(newSize); + } + mActiveContactManager.set(cm->getIndex()); + + if (useCCD) + { + if (cm->getIndex() >= mActiveContactManagersWithCCD.size()) + { + PxU32 newSize = (2 * cm->getIndex() + 256)&~255; + mActiveContactManagersWithCCD.resize(newSize); + } + mActiveContactManagersWithCCD.set(cm->getIndex()); + } + } + } + else + { + PX_WARN_ONCE("Reached limit of contact pairs."); + } + + return cm; +} + +void PxsContext::createCache(Gu::Cache& cache, PxsContactManager* cm, PxU8 geomType0, PxU8 geomType1) +{ + if(cm) + { + if(mPCM) + { + if(gEnablePCMCaching[geomType0][geomType1]) + { + if(geomType0 <= PxGeometryType::eCONVEXMESH && + geomType1 <= PxGeometryType::eCONVEXMESH) + { + if(geomType0 == PxGeometryType::eSPHERE || geomType1 == PxGeometryType::eSPHERE) + { + Gu::PersistentContactManifold* manifold = mSphereManifoldPool.allocate(); + new(manifold) Gu::SpherePersistentContactManifold(); + cache.setManifold(manifold); + } + else + { + Gu::PersistentContactManifold* manifold = mManifoldPool.allocate(); + new(manifold) Gu::LargePersistentContactManifold(); + cache.setManifold(manifold); + + } + cache.getManifold().clearManifold(); + + } + else + { + //ML: raised 1 to indicate the manifold is multiManifold which is for contact gen in mesh/height field + //cache.manifold = 1; + cache.setMultiManifold(NULL); + } + } + else + { + //cache.manifold = 0; + cache.mCachedData = NULL; + cache.mManifoldFlags = 0; + } + } + } +} + +void PxsContext::destroyContactManager(PxsContactManager* cm) +{ + const PxU32 idx = cm->getIndex(); + if (cm->getCCD()) + mActiveContactManagersWithCCD.growAndReset(idx); + mActiveContactManager.growAndReset(idx); + mContactManagerTouchEvent.growAndReset(idx); + mContactManagerPatchChangeEvent.growAndReset(idx); + mContactManagerPool.put(cm); +} + +void PxsContext::destroyCache(Gu::Cache& cache) +{ + if(cache.isManifold()) + { + if(!cache.isMultiManifold()) + { + Gu::PersistentContactManifold& manifold = cache.getManifold(); + if (manifold.mCapacity == GU_SPHERE_MANIFOLD_CACHE_SIZE) + { + mSphereManifoldPool.deallocate(static_cast(&manifold)); + } + else + { + mManifoldPool.deallocate(static_cast(&manifold)); + } + } + cache.mCachedData = NULL; + cache.mManifoldFlags = 0; + } +} + +void PxsContext::setScratchBlock(void* addr, PxU32 size) +{ + mScratchAllocator.setBlock(addr, size); +} + +void PxsContext::setContactDistance(Ps::Array* contactDistance) +{ + mContactDistance = contactDistance; +} + + +void PxsContext::shiftOrigin(const PxVec3& shift) +{ + // transform cache + mTransformCache->shiftTransforms(-shift); + +#if 0 + if (getContactCacheFlag()) + { + //Iterate all active contact managers + Cm::BitMap::Iterator it(mActiveContactManager); + PxU32 index = it.getNext(); + while(index != Cm::BitMap::Iterator::DONE) + { + PxsContactManager* cm = mContactManagerPool.findByIndexFast(index); + + PxcNpWorkUnit& npwUnit = cm->getWorkUnit(); + + // contact cache + if(!npwUnit.pairCache.isManifold()) + { + PxU8* contactCachePtr = npwUnit.pairCache.mCachedData; + if (contactCachePtr) + { + PxcLocalContactsCache* lcc; + PxU8* contacts = PxcNpCacheRead(npwUnit.pairCache, lcc); +#ifdef _DEBUG + PxcLocalContactsCache testCache; + PxU32 testBytes; + const PxU8* testPtr = PxcNpCacheRead2(npwUnit.pairCache, testCache, testBytes); +#endif + lcc->mTransform0.p -= shift; + lcc->mTransform1.p -= shift; + + const PxU32 nbContacts = lcc->mNbCachedContacts; + const bool sameNormal = lcc->mSameNormal; + const bool useFaceIndices = lcc->mUseFaceIndices; + + for(PxU32 i=0; i < nbContacts; i++) + { + if (i != nbContacts-1) + Ps::prefetchLine(contacts, 128); + + if(!i || !sameNormal) + contacts += sizeof(PxVec3); + + PxVec3* cachedPoint = reinterpret_cast(contacts); + *cachedPoint -= shift; + contacts += sizeof(PxVec3); + contacts += sizeof(PxReal); + + if(useFaceIndices) + contacts += 2 * sizeof(PxU32); + } +#ifdef _DEBUG + PX_ASSERT(contacts == (testPtr + testBytes)); +#endif + } + } + + index = it.getNext(); + } + + } +#endif + + // + // adjust visualization culling box + // + PxBounds3 maximalBounds; + maximalBounds.setMaximal(); + if ((mVisualizationCullingBox.minimum != maximalBounds.minimum) || (mVisualizationCullingBox.maximum != maximalBounds.maximum)) + { + mVisualizationCullingBox.minimum -= shift; + mVisualizationCullingBox.maximum -= shift; + } +} + +void PxsContext::swapStreams() +{ + mNpMemBlockPool.swapNpCacheStreams(); +} + +void PxsContext::mergeCMDiscreteUpdateResults(PxBaseTask* /*continuation*/) +{ + PX_PROFILE_ZONE("Sim.narrowPhaseMerge", mContextID); + + this->mNpImplementationContext->appendContactManagers(); + + //Note: the iterator extracts all the items and returns them to the cache on destruction(for thread safety). + PxcThreadCoherentCacheIterator threadContextIt(mNpThreadContextPool); + + for(PxcNpThreadContext* threadContext = threadContextIt.getNext(); threadContext; threadContext = threadContextIt.getNext()) + { + mCMTouchEventCount[PXS_LOST_TOUCH_COUNT] += threadContext->getLocalLostTouchCount(); + mCMTouchEventCount[PXS_NEW_TOUCH_COUNT] += threadContext->getLocalNewTouchCount(); + mCMTouchEventCount[PXS_PATCH_FOUND_COUNT] += threadContext->getLocalFoundPatchCount(); + mCMTouchEventCount[PXS_PATCH_LOST_COUNT] += threadContext->getLocalLostPatchCount(); + +#if PX_ENABLE_SIM_STATS + for(PxU32 i=0;imDiscreteContactPairs[i][j]); + #endif + for(PxU32 j=i; jmDiscreteContactPairs[i][j]; + const PxU32 nbModified = threadContext->mModifiedContactPairs[i][j]; + mSimStats.mNbDiscreteContactPairs[i][j] += nb; + mSimStats.mNbModifiedContactPairs[i][j] += nbModified; + mSimStats.mNbDiscreteContactPairsTotal += nb; + } + } + + mSimStats.mNbDiscreteContactPairsWithCacheHits += threadContext->mNbDiscreteContactPairsWithCacheHits; + mSimStats.mNbDiscreteContactPairsWithContacts += threadContext->mNbDiscreteContactPairsWithContacts; + + mSimStats.mTotalCompressedContactSize += threadContext->mCompressedCacheSize; + //KS - this data is not available yet + //mSimStats.mTotalConstraintSize += threadContext->mConstraintSize; + threadContext->clearStats(); +#endif + mContactManagerTouchEvent.combineInPlace(threadContext->getLocalChangeTouch()); + mContactManagerPatchChangeEvent.combineInPlace(threadContext->getLocalPatchChangeMap()); + mTotalCompressedCacheSize += threadContext->mTotalCompressedCacheSize; + mMaxPatches = PxMax(mMaxPatches, threadContext->mMaxPatches); + + threadContext->mTotalCompressedCacheSize = threadContext->mMaxPatches = 0; + } +} + +void PxsContext::setCreateContactStream(bool to) +{ + mCreateContactStream = to; + PxcThreadCoherentCacheIterator threadContextIt(mNpThreadContextPool); + for(PxcNpThreadContext* threadContext = threadContextIt.getNext(); threadContext; threadContext = threadContextIt.getNext()) + { + threadContext->setCreateContactStream(to); + } +} + +void PxsContext::updateContactManager(PxReal dt, bool hasBoundsArrayChanged, bool hasContactDistanceChanged, PxBaseTask* continuation, PxBaseTask* firstPassContinuation) +{ + PX_ASSERT(mNpImplementationContext); + mNpImplementationContext->updateContactManager(dt, hasBoundsArrayChanged, hasContactDistanceChanged, continuation, firstPassContinuation); +} + +void PxsContext::secondPassUpdateContactManager(PxReal dt, PxBaseTask* continuation) +{ + PX_ASSERT(mNpImplementationContext); + mNpImplementationContext->secondPassUpdateContactManager(dt, continuation); +} + +void PxsContext::fetchUpdateContactManager() +{ + PX_ASSERT(mNpImplementationContext); + mNpImplementationContext->fetchUpdateContactManager(); + mergeCMDiscreteUpdateResults(NULL); +} + +void PxsContext::resetThreadContexts() +{ + //Note: the iterator extracts all the items and returns them to the cache on destruction(for thread safety). + PxcThreadCoherentCacheIterator threadContextIt(mNpThreadContextPool); + PxcNpThreadContext* threadContext = threadContextIt.getNext(); + + while(threadContext != NULL) + { + threadContext->reset(mContactManagerTouchEvent.size()); + threadContext = threadContextIt.getNext(); + } +} + +bool PxsContext::getManagerTouchEventCount(int* newTouch, int* lostTouch, int* ccdTouch) const +{ + if(newTouch) + *newTouch = int(mCMTouchEventCount[PXS_NEW_TOUCH_COUNT]); + + if(lostTouch) + *lostTouch = int(mCMTouchEventCount[PXS_LOST_TOUCH_COUNT]); + + if(ccdTouch) + *ccdTouch = int(mCMTouchEventCount[PXS_CCD_RETOUCH_COUNT]); + + return true; +} + +bool PxsContext::fillManagerTouchEvents(PxvContactManagerTouchEvent* newTouch, PxI32& newTouchCount, PxvContactManagerTouchEvent* lostTouch, PxI32& lostTouchCount, + PxvContactManagerTouchEvent* ccdTouch, PxI32& ccdTouchCount) +{ + PxU32 index; + + const PxvContactManagerTouchEvent* newTouchStart = newTouch; + const PxvContactManagerTouchEvent* lostTouchStart = lostTouch; + const PxvContactManagerTouchEvent* ccdTouchStart = ccdTouch; + + const PxvContactManagerTouchEvent* newTouchEnd = newTouch + newTouchCount; + const PxvContactManagerTouchEvent* lostTouchEnd = lostTouch + lostTouchCount; + const PxvContactManagerTouchEvent* ccdTouchEnd = ccdTouch + ccdTouchCount; + + PX_UNUSED(newTouchEnd); + PX_UNUSED(lostTouchEnd); + PX_UNUSED(ccdTouchEnd); + + Cm::BitMap::Iterator it(mContactManagerTouchEvent); + + while((index = it.getNext()) != Cm::BitMap::Iterator::DONE) + { + PxsContactManager* cm = mContactManagerPool.findByIndexFast(index); + + if(cm->getTouchStatus()) + { + if (!cm->getHasCCDRetouch()) + { + PX_ASSERT(newTouch < newTouchEnd); + newTouch->manager = cm; + newTouch->userData = cm->getUserData(); + newTouch++; + } + else + { + PX_ASSERT(ccdTouch); + PX_ASSERT(ccdTouch < ccdTouchEnd); + ccdTouch->manager = cm; + ccdTouch->userData = cm->getUserData(); + cm->clearCCDRetouch(); + ccdTouch++; + } + } + else + { + PX_ASSERT(lostTouch < lostTouchEnd); + lostTouch->manager = cm; + lostTouch->userData = cm->getUserData(); + lostTouch++; + } + } + newTouchCount = PxI32(newTouch - newTouchStart); + lostTouchCount = PxI32(lostTouch - lostTouchStart); + ccdTouchCount = PxI32(ccdTouch - ccdTouchStart); + return true; +} + + + +bool PxsContext::fillManagerPatchChangedEvents(PxsContactManager** foundPatch, PxU32& foundPatchCount, + PxsContactManager** lostPatch, PxU32& lostPatchCount) +{ + Cm::BitMap::Iterator it(mContactManagerPatchChangeEvent); + + PxsContactManagerOutputIterator outputs = mNpImplementationContext->getContactManagerOutputs(); + + PxU32 index; + PxsContactManager** currFoundPatch = foundPatch; + PxsContactManager** currLostPatch = lostPatch; + while((index = it.getNext()) != Cm::BitMap::Iterator::DONE) + { + PxsContactManager* cm = mContactManagerPool.findByIndexFast(index); + PxcNpWorkUnit& workUnit = cm->getWorkUnit(); + PxsContactManagerOutput& output = outputs.getContactManager(workUnit.mNpIndex); + if(output.nbPatches > output.prevPatches) + { + PX_ASSERT(PxU32(currFoundPatch - foundPatch) < foundPatchCount); + *currFoundPatch = cm; + currFoundPatch++; + } + else if(output.nbPatches < output.prevPatches) + { + PX_ASSERT(PxU32(currLostPatch - lostPatch) < lostPatchCount); + *currLostPatch = cm; + currLostPatch++; + } + } + + foundPatchCount = PxU32(currFoundPatch - foundPatch); + lostPatchCount = PxU32(currLostPatch - lostPatch); + return true; +} + + +void PxsContext::beginUpdate() +{ +#if PX_ENABLE_SIM_STATS + mSimStats.clearAll(); +#endif +} + + +// Contact manager related + +PxReal PxsContext::getVisualizationParameter(PxVisualizationParameter::Enum param) const +{ + PX_ASSERT(param < PxVisualizationParameter::eNUM_VALUES); + + return mVisualizationParams[param]; +} + +void PxsContext::setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) +{ + PX_ASSERT(param < PxVisualizationParameter::eNUM_VALUES); + PX_ASSERT(value >= 0.0f); + + mVisualizationParams[param] = value; +} + + + + + + diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp new file mode 100644 index 000000000..381830ed7 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxsDefaultMemoryManager.h" + +namespace physx +{ + + PxsDefaultMemoryManager::~PxsDefaultMemoryManager() + { + for (PxU32 i = 0; i < mAllocators.size(); ++i) + { + mAllocators[i]->~VirtualAllocatorCallback(); + PX_FREE(mAllocators[i]); + } + } + + Ps::VirtualAllocatorCallback* PxsDefaultMemoryManager::createHostMemoryAllocator(const PxU32 gpuComputeVersion) + { + PX_UNUSED(gpuComputeVersion); + Ps::VirtualAllocatorCallback* allocator = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxsDefaultMemoryAllocator), "PxsDefaultMemoryAllocator"), PxsDefaultMemoryAllocator()); + mAllocators.pushBack(allocator); + return allocator; + } + + //this is an empty stub + Ps::VirtualAllocatorCallback* PxsDefaultMemoryManager::createDeviceMemoryAllocator(const PxU32 gpuComputeVersion) + { + PX_UNUSED(gpuComputeVersion); + return NULL; + } + + void PxsDefaultMemoryManager::destroyMemoryAllocator() + { + for (PxU32 i = 0; i < mAllocators.size(); ++i) + { + mAllocators[i]->~VirtualAllocatorCallback(); + PX_FREE(mAllocators[i]); + } + } + + + PxsMemoryManager* createMemoryManager() + { + return PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxsDefaultMemoryManager), PX_DEBUG_EXP("PxsDefaultMemoryManager")), PxsDefaultMemoryManager()); + } + +} diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp new file mode 100644 index 000000000..2dd427ee4 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp @@ -0,0 +1,2323 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxsIslandSim.h" +#include "PsSort.h" +#include "PsUtilities.h" +#include "common/PxProfileZone.h" + +#define IG_SANITY_CHECKS 0 + +namespace physx +{ +namespace IG +{ + + IslandSim::IslandSim(Ps::Array* firstPartitionEdges, Cm::BlockArray& edgeNodeIndices, + Ps::Array* destroyedPartitionEdges, PxU64 contextID) : + mNodes(PX_DEBUG_EXP("IslandSim::mNodes")), + mActiveNodeIndex(PX_DEBUG_EXP("IslandSim::mActiveNodeIndex")), + mIslands(PX_DEBUG_EXP("IslandSim::mIslands")), + mIslandStaticTouchCount(PX_DEBUG_EXP("IslandSim.activeStaticTouchCount")), + mActiveKinematicNodes(PX_DEBUG_EXP("IslandSim::mActiveKinematicNodes")), + mHopCounts(PX_DEBUG_EXP("IslandSim::mHopCounts")), + mFastRoute(PX_DEBUG_EXP("IslandSim::,FastRoute")), + mIslandIds(PX_DEBUG_EXP("IslandSim::mIslandIds")), + mActiveIslands(PX_DEBUG_EXP("IslandSim::mActiveIslands")), + mLastMapIndex(0), + mActivatingNodes(PX_DEBUG_EXP("IslandSim::mActivatingNodes")), + mDestroyedEdges(PX_DEBUG_EXP("IslandSim::mDestroyedEdges")), + mTempIslandIds(PX_DEBUG_EXP("IslandSim::mTempIslandIds")), + mVisitedNodes(PX_DEBUG_EXP("IslandSim::mVisitedNodes")), + mFirstPartitionEdges(firstPartitionEdges), + mEdgeNodeIndices(edgeNodeIndices), + mDestroyedPartitionEdges(destroyedPartitionEdges), + mContextId(contextID) + { + mInitialActiveNodeCount[0] = 0; mInitialActiveNodeCount[1] = 0; mNpIndexPtr = NULL; + mActiveEdgeCount[0] = mActiveEdgeCount[1] = 0; + } + +#if PX_ENABLE_ASSERTS +template +static bool contains(Ps::Array& arr, const Thing& thing) +{ + for(PxU32 a = 0; a < arr.size(); ++a) + { + if(thing == arr[a]) + return true; + } + return false; +} +#endif + +void IslandSim::resize(const PxU32 nbNodes, const PxU32 nbContactManagers, const PxU32 nbConstraints) +{ + PxU32 totalEdges = nbContactManagers + nbConstraints; + mNodes.reserve(nbNodes); + mIslandIds.reserve(nbNodes); + mEdges.reserve(totalEdges); + mActiveContactEdges.resize(totalEdges); + mEdgeInstances.reserve(totalEdges*2); +} + +void IslandSim::addNode(bool isActive, bool isKinematic, Node::NodeType type, NodeIndex nodeIndex) +{ + PxU32 handle = nodeIndex.index(); + if(handle == mNodes.capacity()) + { + const PxU32 newCapacity = PxMax(2*mNodes.capacity(), 256u); + mNodes.reserve(newCapacity); + mIslandIds.reserve(newCapacity); + mFastRoute.reserve(newCapacity); + mHopCounts.reserve(newCapacity); + mActiveNodeIndex.reserve(newCapacity); + } + + const PxU32 newSize = PxMax(handle+1, mNodes.size()); + mNodes.resize(newSize); + mIslandIds.resize(newSize); + mFastRoute.resize(newSize); + mHopCounts.resize(newSize); + mActiveNodeIndex.resize(newSize); + + mActiveNodeIndex[handle] = IG_INVALID_NODE; + + + Node& node = mNodes[handle]; + node.mType = Ps::to8(type); + //Ensure that the node is not currently being used. + PX_ASSERT(node.isDeleted()); + + PxU8 flags = PxU16(isActive ? 0 : Node::eREADY_FOR_SLEEPING); + if(isKinematic) + flags |= Node::eKINEMATIC; + node.mFlags = flags; + mIslandIds[handle] = IG_INVALID_ISLAND; + mFastRoute[handle].setIndices(IG_INVALID_NODE); + mHopCounts[handle] = 0; + + if(!isKinematic) + { + IslandId islandHandle = mIslandHandles.getHandle(); + + if(islandHandle == mIslands.capacity()) + { + const PxU32 newCapacity = PxMax(2*mIslands.capacity(), 256u); + mIslands.reserve(newCapacity); + mIslandAwake.resize(newCapacity); + mIslandStaticTouchCount.reserve(newCapacity); + } + mIslands.resize(PxMax(islandHandle+1, mIslands.size())); + mIslandStaticTouchCount.resize(PxMax(islandHandle+1, mIslands.size())); + mIslandAwake.growAndReset(PxMax(islandHandle+1, mIslands.size())); + Island& island = mIslands[islandHandle]; + island.mLastNode = island.mRootNode = nodeIndex; + island.mSize[type] = 1; + mIslandIds[handle] = islandHandle; + mIslandStaticTouchCount[islandHandle] = 0; + } + + if(isActive) + { + activateNode(nodeIndex); + } +} + +void IslandSim::addRigidBody(PxsRigidBody* body, bool isKinematic, bool isActive, NodeIndex nodeIndex) +{ + addNode(isActive, isKinematic, Node::eRIGID_BODY_TYPE, nodeIndex); + Node& node = mNodes[nodeIndex.index()]; + node.mRigidBody = body; +} + +void IslandSim::addArticulation(Sc::ArticulationSim* /*articulation*/, Dy::ArticulationV* llArtic, bool isActive, NodeIndex nodeIndex) +{ + addNode(isActive, false, Node::eARTICULATION_TYPE, nodeIndex); + Node& node = mNodes[nodeIndex.index()]; + node.mLLArticulation = llArtic; +} + +void IslandSim::connectEdge(EdgeInstance& instance, EdgeInstanceIndex edgeIndex, Node& source, NodeIndex /*destination*/) +{ + PX_ASSERT(instance.mNextEdge == IG_INVALID_EDGE); + PX_ASSERT(instance.mPrevEdge == IG_INVALID_EDGE); + + instance.mNextEdge = source.mFirstEdgeIndex; + if(source.mFirstEdgeIndex != IG_INVALID_EDGE) + { + EdgeInstance& firstEdge = mEdgeInstances[source.mFirstEdgeIndex]; + firstEdge.mPrevEdge = edgeIndex; + } + + source.mFirstEdgeIndex = edgeIndex; + instance.mPrevEdge = IG_INVALID_EDGE; +} + +void IslandSim::addConnection(NodeIndex nodeHandle1, NodeIndex nodeHandle2, Edge::EdgeType edgeType, EdgeIndex handle) +{ + PX_UNUSED(nodeHandle1); + PX_UNUSED(nodeHandle2); + if(handle >= mEdges.capacity()) + { + PX_PROFILE_ZONE("ReserveIslandEdges", getContextId()); + const PxU32 newSize = handle + 2048; + mEdges.reserve(newSize); + mActiveContactEdges.resize(mEdges.capacity()); + } + mEdges.resize(PxMax(mEdges.size(), handle+1)); + mActiveContactEdges.reset(handle); + + Edge& edge = mEdges[handle]; + + if(edge.isPendingDestroyed()) + { + //If it's in this state, then the edge has been tagged for destruction but actually is now not needed to be destroyed + edge.clearPendingDestroyed(); + return; + } + + if(edge.isInDirtyList()) + { + PX_ASSERT(mEdgeNodeIndices[handle * 2].index() == nodeHandle1.index()); + PX_ASSERT(mEdgeNodeIndices[handle * 2 + 1].index() == nodeHandle2.index()); + PX_ASSERT(edge.mEdgeType == edgeType); + return; + } + + PX_ASSERT(!edge.isInserted()); + + PX_ASSERT(edge.isDestroyed()); + edge.clearDestroyed(); + + PX_ASSERT(edge.mNextIslandEdge == IG_INVALID_ISLAND); + PX_ASSERT(edge.mPrevIslandEdge == IG_INVALID_ISLAND); + + PX_ASSERT(mEdgeInstances.size() <= 2*handle || mEdgeInstances[2*handle].mNextEdge == IG_INVALID_EDGE); + PX_ASSERT(mEdgeInstances.size() <= 2*handle || mEdgeInstances[2*handle+1].mNextEdge == IG_INVALID_EDGE); + PX_ASSERT(mEdgeInstances.size() <= 2*handle || mEdgeInstances[2*handle].mPrevEdge == IG_INVALID_EDGE); + PX_ASSERT(mEdgeInstances.size() <= 2*handle || mEdgeInstances[2*handle+1].mPrevEdge == IG_INVALID_EDGE); + + edge.mEdgeType = edgeType; + + PX_ASSERT(handle*2 >= mEdgeInstances.size() || mEdgeInstances[handle*2].mNextEdge == IG_INVALID_EDGE); + PX_ASSERT(handle*2+1 >= mEdgeInstances.size() || mEdgeInstances[handle*2+1].mNextEdge == IG_INVALID_EDGE); + PX_ASSERT(handle*2 >= mEdgeInstances.size() || mEdgeInstances[handle*2].mPrevEdge == IG_INVALID_EDGE); + PX_ASSERT(handle*2+1 >= mEdgeInstances.size() || mEdgeInstances[handle*2+1].mPrevEdge == IG_INVALID_EDGE); + + //Add the new handle + if(!edge.isInDirtyList()) + { + PX_ASSERT(!contains(mDirtyEdges[edgeType], handle)); + mDirtyEdges[edgeType].pushBack(handle); + edge.markInDirtyList(); + } + edge.mEdgeState &= ~(Edge::eACTIVATING); +} + +void IslandSim::addConnectionToGraph(EdgeIndex handle) +{ + EdgeInstanceIndex instanceHandle = 2*handle; + PX_ASSERT(instanceHandle < mEdgeInstances.capacity()); + /*if(instanceHandle == mEdgeInstances.capacity()) + { + mEdgeInstances.reserve(2*mEdgeInstances.capacity() + 2); + }*/ + mEdgeInstances.resize(PxMax(instanceHandle+2, mEdgeInstances.size())); + + Edge& edge = mEdges[handle]; + + bool activeEdge = false; + bool kinematicKinematicEdge = true; + + NodeIndex nodeIndex1 = mEdgeNodeIndices[instanceHandle]; + NodeIndex nodeIndex2 = mEdgeNodeIndices[instanceHandle+1]; + + if(nodeIndex1.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex1.index()]; + connectEdge(mEdgeInstances[instanceHandle], instanceHandle, node, nodeIndex2); + activeEdge = node.isActive() || node.isActivating(); + kinematicKinematicEdge = node.isKinematic(); + } + + if (nodeIndex1.index() != nodeIndex2.index() && nodeIndex2.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex2.index()]; + connectEdge(mEdgeInstances[instanceHandle + 1], instanceHandle + 1, node, nodeIndex1); + activeEdge = activeEdge || node.isActive() || node.isActivating(); + kinematicKinematicEdge = kinematicKinematicEdge && node.isKinematic(); + } + + if(activeEdge && (!kinematicKinematicEdge || edge.getEdgeType() == IG::Edge::eCONTACT_MANAGER)) + { + markEdgeActive(handle); + edge.activateEdge(); + } +} + +void IslandSim::removeConnectionFromGraph(EdgeIndex edgeIndex) +{ + NodeIndex nodeIndex1 = mEdgeNodeIndices[2 * edgeIndex]; + NodeIndex nodeIndex2 = mEdgeNodeIndices[2 * edgeIndex+1]; + if (nodeIndex1.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex1.index()]; + if (nodeIndex2.index() == mFastRoute[nodeIndex1.index()].index()) + mFastRoute[nodeIndex1.index()].setIndices(IG_INVALID_NODE); + if(!node.isDirty()) + { + //mDirtyNodes.pushBack(nodeIndex1); + mDirtyMap.growAndSet(nodeIndex1.index()); + node.markDirty(); + } + } + + if (nodeIndex2.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex2.index()]; + if (nodeIndex1.index() == mFastRoute[nodeIndex2.index()].index()) + mFastRoute[nodeIndex2.index()].setIndices(IG_INVALID_NODE); + if(!node.isDirty()) + { + mDirtyMap.growAndSet(nodeIndex2.index()); + node.markDirty(); + } + } +} + +void IslandSim::disconnectEdge(EdgeInstance& instance, EdgeInstanceIndex edgeIndex, Node& node) +{ + + PX_ASSERT(instance.mNextEdge == IG_INVALID_EDGE || mEdgeInstances[instance.mNextEdge].mPrevEdge == edgeIndex); + PX_ASSERT(instance.mPrevEdge == IG_INVALID_EDGE || mEdgeInstances[instance.mPrevEdge].mNextEdge == edgeIndex); + + if(node.mFirstEdgeIndex == edgeIndex) + { + PX_ASSERT(instance.mPrevEdge == IG_INVALID_EDGE); + node.mFirstEdgeIndex = instance.mNextEdge; + } + else + { + EdgeInstance& prev = mEdgeInstances[instance.mPrevEdge]; + PX_ASSERT(prev.mNextEdge == edgeIndex); + prev.mNextEdge = instance.mNextEdge; + } + + if(instance.mNextEdge != IG_INVALID_EDGE) + { + EdgeInstance& next = mEdgeInstances[instance.mNextEdge]; + PX_ASSERT(next.mPrevEdge == edgeIndex); + next.mPrevEdge = instance.mPrevEdge; + } + + PX_ASSERT(instance.mNextEdge == IG_INVALID_EDGE || mEdgeInstances[instance.mNextEdge].mPrevEdge == instance.mPrevEdge); + PX_ASSERT(instance.mPrevEdge == IG_INVALID_EDGE || mEdgeInstances[instance.mPrevEdge].mNextEdge == instance.mNextEdge); + + instance.mNextEdge = IG_INVALID_EDGE; + instance.mPrevEdge = IG_INVALID_EDGE; +} + +void IslandSim::removeConnection(EdgeIndex edgeIndex) +{ + Edge& edge = mEdges[edgeIndex]; + if(!edge.isPendingDestroyed())// && edge.isInserted()) + { + mDestroyedEdges.pushBack(edgeIndex); + /*if(!edge.isInserted()) + edge.setReportOnlyDestroy();*/ + } + edge.setPendingDestroyed(); +} + +void IslandSim::removeConnectionInternal(EdgeIndex edgeIndex) +{ + PX_ASSERT(edgeIndex != IG_INVALID_EDGE); + EdgeInstanceIndex edgeInstanceBase = edgeIndex*2; + + + NodeIndex nodeIndex1 = mEdgeNodeIndices[edgeIndex * 2]; + + if (nodeIndex1.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex1.index()]; + disconnectEdge(mEdgeInstances[edgeInstanceBase], edgeInstanceBase, node); + } + + NodeIndex nodeIndex2 = mEdgeNodeIndices[edgeIndex * 2 + 1]; + + if (nodeIndex2.index() != IG_INVALID_NODE && nodeIndex1.index() != nodeIndex2.index()) + { + Node& node = mNodes[nodeIndex2.index()]; + disconnectEdge(mEdgeInstances[edgeInstanceBase+1], edgeInstanceBase+1, node); + } +} + + +void IslandSim::addContactManager(PxsContactManager* /*manager*/, NodeIndex nodeHandle1, NodeIndex nodeHandle2, EdgeIndex handle) +{ + addConnection(nodeHandle1, nodeHandle2, Edge::eCONTACT_MANAGER, handle); +} + +void IslandSim::addConstraint(Dy::Constraint* /*constraint*/, NodeIndex nodeHandle1, NodeIndex nodeHandle2, EdgeIndex handle) +{ + addConnection(nodeHandle1, nodeHandle2, Edge::eCONSTRAINT, handle); +} + +void IslandSim::activateNode(NodeIndex nodeIndex) +{ + if(nodeIndex.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex.index()]; + + if(!(node.isActive() || + node.isActivating())) + { + + //If the node is kinematic and already in the active node list, then we need to remove it + //from the active kinematic node list, then re-add it after the wake-up. It's a bit stupid + //but it means that we don't need another index + + if(node.isKinematic() && mActiveNodeIndex[nodeIndex.index()] != IG_INVALID_NODE) + { + //node.setActive(); + //node.clearIsReadyForSleeping(); //Clear the "isReadyForSleeping" flag. Just in case it was set + //return; + + PxU32 activeRefCount = node.mActiveRefCount; + node.mActiveRefCount = 0; + node.clearActive(); + markKinematicInactive(nodeIndex); + node.mActiveRefCount = activeRefCount; + } + + node.setActivating(); //Tag it as activating + PX_ASSERT(mActiveNodeIndex[nodeIndex.index()] == IG_INVALID_NODE); + mActiveNodeIndex[nodeIndex.index()] = mActivatingNodes.size(); + //Add to waking list + mActivatingNodes.pushBack(nodeIndex); + } + node.clearIsReadyForSleeping(); //Clear the "isReadyForSleeping" flag. Just in case it was set + node.clearDeactivating(); + } +} + +void IslandSim::deactivateNode(NodeIndex nodeIndex) +{ + if(nodeIndex.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex.index()]; + + //If the node is activating, clear its activating state and remove it from the activating list. + //If it wasn't already activating, then it's probably already in the active list + + bool wasActivating = node.isActivating(); + + if(wasActivating) + { + //Already activating, so remove it from the activating list + node.clearActivating(); + PX_ASSERT(mActivatingNodes[mActiveNodeIndex[nodeIndex.index()]].index() == nodeIndex.index()); + NodeIndex replaceIndex = mActivatingNodes[mActivatingNodes.size()-1]; + mActiveNodeIndex[replaceIndex.index()] = mActiveNodeIndex[nodeIndex.index()]; + mActivatingNodes[mActiveNodeIndex[nodeIndex.index()]] = replaceIndex; + mActivatingNodes.forceSize_Unsafe(mActivatingNodes.size()-1); + mActiveNodeIndex[nodeIndex.index()] = IG_INVALID_NODE; + + if(node.isKinematic()) + { + //If we were temporarily removed from the active kinematic list to be put in the waking kinematic list + //then add the node back in before deactivating the node. This is a bit counter-intuitive but the active + //kinematic list contains all active kinematics and all kinematics that are referenced by an active constraint + PX_ASSERT(mActiveNodeIndex[nodeIndex.index()] == IG_INVALID_NODE); + mActiveNodeIndex[nodeIndex.index()] = mActiveKinematicNodes.size(); + mActiveKinematicNodes.pushBack(nodeIndex); + } + } + + //Raise the "ready for sleeping" flag so that island gen can put this node to sleep + node.setIsReadyForSleeping(); + } +} + +void IslandSim::putNodeToSleep(NodeIndex nodeIndex) +{ + if(nodeIndex.index() != IG_INVALID_NODE) + { + deactivateNode(nodeIndex); + } +} + + +void IslandSim::activateNodeInternal(NodeIndex nodeIndex) +{ + //This method should activate the node, then activate all the connections involving this node + Node& node = mNodes[nodeIndex.index()]; + + if(!node.isActive()) + { + PX_ASSERT(mActiveNodeIndex[nodeIndex.index()] == IG_INVALID_NODE); + + //Activate all the edges + nodes... + + EdgeInstanceIndex index = node.mFirstEdgeIndex; + + while(index != IG_INVALID_EDGE) + { + EdgeIndex idx = index/2; + Edge& edge = mEdges[idx]; //InstanceIndex/2 = edgeIndex + if(!edge.isActive()) + { + //Make the edge active... + PX_ASSERT(mEdgeNodeIndices[idx * 2].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2].index()].isKinematic()); + PX_ASSERT(mEdgeNodeIndices[idx * 2 + 1].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isKinematic()); + + markEdgeActive(idx); + edge.activateEdge(); + + } + index = mEdgeInstances[index].mNextEdge; + } + + if(node.isKinematic()) + { + markKinematicActive(nodeIndex); + } + else + { + markActive(nodeIndex); + } + node.setActive(); + } + +} + +void IslandSim::deactivateNodeInternal(NodeIndex nodeIndex) +{ + //We deactivate a node, we need to loop through all the edges and deactivate them *if* both bodies are asleep + + Node& node = mNodes[nodeIndex.index()]; + + if(node.isActive()) + { + if(node.isKinematic()) + { + markKinematicInactive(nodeIndex); + } + else + { + markInactive(nodeIndex); + } + + //Clear the active status flag + node.clearActive(); + node.clearActivating(); + + EdgeInstanceIndex index = node.mFirstEdgeIndex; + + while(index != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[index]; + + + NodeIndex outboundNode = mEdgeNodeIndices[index ^ 1]; + if(outboundNode.index() == IG_INVALID_NODE || + !mNodes[outboundNode.index()].isActive()) + { + EdgeIndex idx = index/2; + Edge& edge = mEdges[idx]; //InstanceIndex/2 = edgeIndex + //PX_ASSERT(edge.isActive()); //The edge must currently be inactive because the node was active + //Deactivate the edge if both nodes connected are inactive OR if one node is static/kinematic and the other is inactive... + PX_ASSERT(mEdgeNodeIndices[index & (~1)].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[index & (~1)].index()].isActive()); + PX_ASSERT(mEdgeNodeIndices[index | 1].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[index | 1].index()].isActive()); + if(edge.isActive()) + { + edge.deactivateEdge(); + mActiveEdgeCount[edge.mEdgeType]--; + removeEdgeFromActivatingList(idx); + mDeactivatingEdges[edge.mEdgeType].pushBack(idx); + } + } + index = instance.mNextEdge; + } + } + +} + +bool IslandSim::canFindRoot(NodeIndex startNode, NodeIndex targetNode, Ps::Array* visitedNodes) +{ + if(visitedNodes) + visitedNodes->pushBack(startNode); + if(startNode.index() == targetNode.index()) + return true; + Cm::BitMap visitedState; + visitedState.resizeAndClear(mNodes.size()); + + Ps::Array stack; + + stack.pushBack(startNode); + + visitedState.set(startNode.index()); + + do + { + NodeIndex currentIndex = stack.popBack(); + Node& currentNode = mNodes[currentIndex.index()]; + + EdgeInstanceIndex currentEdge = currentNode.mFirstEdgeIndex; + + while(currentEdge != IG_INVALID_EDGE) + { + EdgeInstance& edge = mEdgeInstances[currentEdge]; + NodeIndex outboundNode = mEdgeNodeIndices[currentEdge ^ 1]; + if(outboundNode.index() != IG_INVALID_NODE && !mNodes[outboundNode.index()].isKinematic() && !visitedState.test(outboundNode.index())) + { + if(outboundNode.index() == targetNode.index()) + { + return true; + } + + visitedState.set(outboundNode.index()); + stack.pushBack(outboundNode); + if(visitedNodes) + visitedNodes->pushBack(outboundNode); + } + + currentEdge = edge.mNextEdge; + } + + } + while(stack.size()); + + return false; + +} + + + +void IslandSim::unwindRoute(PxU32 traversalIndex, NodeIndex lastNode, PxU32 hopCount, IslandId id) +{ + //We have found either a witness *or* the root node with this traversal. In the event of finding the root node, hopCount will be 0. In the event of finding + //a witness, hopCount will be the hopCount that witness reported as being the distance to the root. + + PxU32 currIndex = traversalIndex; + PxU32 hc = hopCount+1; //Add on 1 for the hop to the witness/root node. + do + { + TraversalState& state = mVisitedNodes[currIndex]; + mHopCounts[state.mNodeIndex.index()] = hc++; + mIslandIds[state.mNodeIndex.index()] = id; + mFastRoute[state.mNodeIndex.index()] = lastNode; + currIndex = state.mPrevIndex; + lastNode = state.mNodeIndex; + } + while(currIndex != IG_INVALID_NODE); +} + +void IslandSim::activateIsland(IslandId islandId) +{ + Island& island = mIslands[islandId]; + PX_ASSERT(!mIslandAwake.test(islandId)); + PX_ASSERT(island.mActiveIndex == IG_INVALID_ISLAND); + + NodeIndex currentNode = island.mRootNode; + while(currentNode.index() != IG_INVALID_NODE) + { + activateNodeInternal(currentNode); + currentNode = mNodes[currentNode.index()].mNextNode; + } + markIslandActive(islandId); +} + +void IslandSim::deactivateIsland(IslandId islandId) +{ + PX_ASSERT(mIslandAwake.test(islandId)); + Island& island = mIslands[islandId]; + + NodeIndex currentNode = island.mRootNode; + while(currentNode.index() != IG_INVALID_NODE) + { + Node& node = mNodes[currentNode.index()]; + //if(mActiveNodeIndex[currentNode.index()] < mInitialActiveNodeCount[node.mType]) + mNodesToPutToSleep[node.mType].pushBack(currentNode); //If this node was previously active, then push it to the list of nodes to deactivate + deactivateNodeInternal(currentNode); + currentNode = node.mNextNode; + } + markIslandInactive(islandId); +} + + +void IslandSim::wakeIslands() +{ + PX_PROFILE_ZONE("Basic.wakeIslands", getContextId()); + + //(1) Iterate over activating nodes and activate them + + + PxU32 originalActiveIslands = mActiveIslands.size(); + + for (PxU32 a = 0; a < Edge::eEDGE_TYPE_COUNT; ++a) + { + for (PxU32 i = 0, count = mActivatedEdges[a].size(); i < count; ++i) + { + IG::Edge& edge = mEdges[mActivatedEdges[a][i]]; + edge.mEdgeState &= (~Edge::eACTIVATING); + } + + } + + mActivatedEdges[0].forceSize_Unsafe(0); + mActivatedEdges[1].forceSize_Unsafe(0); + /*mInitialActiveEdgeCount[0] = mActiveEdges[0].size(); + mInitialActiveEdgeCount[1] = mActiveEdges[1].size();*/ + + for(PxU32 a = 0; a < mActivatingNodes.size(); ++a) + { + NodeIndex wakeNode = mActivatingNodes[a]; + + IslandId islandId = mIslandIds[wakeNode.index()]; + + Node& node = mNodes[wakeNode.index()]; + node.clearActivating(); + if(islandId != IG_INVALID_ISLAND) + { + if(!mIslandAwake.test(islandId)) + { + markIslandActive(islandId); + } + mActiveNodeIndex[wakeNode.index()] = IG_INVALID_NODE; //Mark active node as invalid. + activateNodeInternal(wakeNode); + } + else + { + PX_ASSERT(node.isKinematic()); + node.setActive(); + PX_ASSERT(mActiveNodeIndex[wakeNode.index()] == a); + mActiveNodeIndex[wakeNode.index()] = mActiveKinematicNodes.size(); + mActiveKinematicNodes.pushBack(wakeNode); + + //Wake up the islands connected to this waking kinematic! + EdgeInstanceIndex index = node.mFirstEdgeIndex; + while(index != IG_INVALID_EDGE) + { + EdgeInstance& edgeInstance = mEdgeInstances[index]; + + NodeIndex outboundNode = mEdgeNodeIndices[index ^ 1]; + //Edge& edge = mEdges[index/2]; + //if(edge.isConnected()) //Only wake up if the edge is not connected... + NodeIndex nodeIndex = outboundNode; + + if (nodeIndex.isStaticBody() || mIslandIds[nodeIndex.index()] == IG_INVALID_ISLAND) + { + //If the edge connects to a static body *or* it connects to a node which is not part of an island (i.e. a kinematic), then activate the edge + EdgeIndex idx = index / 2; + Edge& edge = mEdges[idx]; + if (!edge.isActive() && edge.getEdgeType() != IG::Edge::eCONSTRAINT) + { + //Make the edge active... + PX_ASSERT(mEdgeNodeIndices[idx * 2].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2].index()].isKinematic()); + PX_ASSERT(mEdgeNodeIndices[idx * 2 + 1].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isKinematic()); + + markEdgeActive(idx); + edge.activateEdge(); + + } + } + else + { + IslandId connectedIslandId = mIslandIds[nodeIndex.index()]; + if(!mIslandAwake.test(connectedIslandId)) + { + //Wake up that island + markIslandActive(connectedIslandId); + } + } + + index = edgeInstance.mNextEdge; + } + } + } + + mInitialActiveNodeCount[0] = mActiveNodes[0].size(); + mInitialActiveNodeCount[1] = mActiveNodes[1].size(); + + mActivatingNodes.forceSize_Unsafe(0); + + for(PxU32 a = originalActiveIslands; a < mActiveIslands.size(); ++a) + { + Island& island = mIslands[mActiveIslands[a]]; + + NodeIndex currentNode = island.mRootNode; + while(currentNode.index() != IG_INVALID_NODE) + { + activateNodeInternal(currentNode); + currentNode = mNodes[currentNode.index()].mNextNode; + } + } +} + +void IslandSim::wakeIslands2() +{ + PxU32 originalActiveIslands = mActiveIslands.size(); + + for (PxU32 a = 0; a < mActivatingNodes.size(); ++a) + { + NodeIndex wakeNode = mActivatingNodes[a]; + + IslandId islandId = mIslandIds[wakeNode.index()]; + + Node& node = mNodes[wakeNode.index()]; + node.clearActivating(); + if (islandId != IG_INVALID_ISLAND) + { + if (!mIslandAwake.test(islandId)) + { + markIslandActive(islandId); + } + mActiveNodeIndex[wakeNode.index()] = IG_INVALID_NODE; //Mark active node as invalid. + activateNodeInternal(wakeNode); + } + else + { + PX_ASSERT(node.isKinematic()); + node.setActive(); + PX_ASSERT(mActiveNodeIndex[wakeNode.index()] == a); + mActiveNodeIndex[wakeNode.index()] = mActiveKinematicNodes.size(); + mActiveKinematicNodes.pushBack(wakeNode); + + //Wake up the islands connected to this waking kinematic! + EdgeInstanceIndex index = node.mFirstEdgeIndex; + while (index != IG_INVALID_EDGE) + { + EdgeInstance& edgeInstance = mEdgeInstances[index]; + + NodeIndex outboundNode = mEdgeNodeIndices[index ^ 1]; + //Edge& edge = mEdges[index/2]; + //if(edge.isConnected()) //Only wake up if the edge is not connected... + NodeIndex nodeIndex = outboundNode; + + if (nodeIndex.isStaticBody() || mIslandIds[nodeIndex.index()] == IG_INVALID_ISLAND) + { + //If the edge connects to a static body *or* it connects to a node which is not part of an island (i.e. a kinematic), then activate the edge + EdgeIndex idx = index / 2; + Edge& edge = mEdges[idx]; + if (!edge.isActive() && edge.getEdgeType() != IG::Edge::eCONSTRAINT) + { + //Make the edge active... + PX_ASSERT(mEdgeNodeIndices[idx * 2].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2].index()].isKinematic()); + PX_ASSERT(mEdgeNodeIndices[idx * 2 + 1].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isKinematic()); + + markEdgeActive(idx); + edge.activateEdge(); + + } + } + else + { + IslandId connectedIslandId = mIslandIds[nodeIndex.index()]; + if (!mIslandAwake.test(connectedIslandId)) + { + //Wake up that island + markIslandActive(connectedIslandId); + } + } + + index = edgeInstance.mNextEdge; + } + } + } + + mActivatingNodes.forceSize_Unsafe(0); + + for (PxU32 a = originalActiveIslands; a < mActiveIslands.size(); ++a) + { + Island& island = mIslands[mActiveIslands[a]]; + + NodeIndex currentNode = island.mRootNode; + while (currentNode.index() != IG_INVALID_NODE) + { + activateNodeInternal(currentNode); + currentNode = mNodes[currentNode.index()].mNextNode; + } + } +} + +void IslandSim::insertNewEdges() +{ + PX_PROFILE_ZONE("Basic.insertNewEdges", getContextId()); + + mEdgeInstances.reserve(mEdges.capacity()*2); + + for(PxU32 i = 0; i < Edge::eEDGE_TYPE_COUNT; ++i) + { + for(PxU32 a = 0; a < mDirtyEdges[i].size(); ++a) + { + EdgeIndex edgeIndex = mDirtyEdges[i][a]; + + Edge& edge = mEdges[edgeIndex]; + + if(!edge.isPendingDestroyed()) + { + //PX_ASSERT(!edge.isInserted()); + if(!edge.isInserted()) + { + addConnectionToGraph(edgeIndex); + edge.setInserted(); + } + } + } + } +} + +void IslandSim::removeDestroyedEdges() +{ + PX_PROFILE_ZONE("Basic.removeDestroyedEdges", getContextId()); + + for(PxU32 a = 0; a < mDestroyedEdges.size(); ++a) + { + EdgeIndex edgeIndex = mDestroyedEdges[a]; + + Edge& edge = mEdges[edgeIndex]; + + if(edge.isPendingDestroyed()) + { + if(!edge.isInDirtyList() && edge.isInserted()) + { + PX_ASSERT(edge.isInserted()); + removeConnectionInternal(edgeIndex); + removeConnectionFromGraph(edgeIndex); + //edge.clearInserted(); + } + //edge.clearDestroyed(); + } + } +} + +void IslandSim::processNewEdges() +{ + PX_PROFILE_ZONE("Basic.processNewEdges", getContextId()); + //Stage 1: we process the list of new pairs. To do this, we need to first sort them based on a predicate... + + insertNewEdges(); + + mHopCounts.resize(mNodes.size()); //Make sure we have enough space for hop counts for all nodes + mFastRoute.resize(mNodes.size()); + + + for(PxU32 i = 0; i < Edge::eEDGE_TYPE_COUNT; ++i) + { + for(PxU32 a = 0; a < mDirtyEdges[i].size(); ++a) + { + EdgeIndex edgeIndex = mDirtyEdges[i][a]; + Edge& edge = mEdges[edgeIndex]; + + /*PX_ASSERT(edge.mState != Edge::eDESTROYED || ((edge.mNode1.index() == IG_INVALID_NODE || mNodes[edge.mNode1.index()].isKinematic() || mNodes[edge.mNode1.index()].isActive() == false) && + (edge.mNode2.index() == IG_INVALID_NODE || mNodes[edge.mNode2.index()].isKinematic() || mNodes[edge.mNode2.index()].isActive() == false)));*/ + + //edge.clearInDirtyList(); + + + //We do not process either destroyed or disconnected edges + if(/*edge.isConnected() && */!edge.isPendingDestroyed()) + { + //Conditions: + //(1) Neither body is in an island (static/kinematics are never in islands) so we need to create a new island containing these bodies + // or just 1 body if the other is kinematic/static + //(2) Both bodies are already in the same island. Update root node hop count estimates for the bodies if a route through the new connection + // is shorter for either body + //(3) One body is already in an island and the other isn't, so we just add the new body to the existing island. + //(4) Both bodies are in different islands. In that case, we merge the islands + + NodeIndex nodeIndex1 = mEdgeNodeIndices[2 * edgeIndex]; + NodeIndex nodeIndex2 = mEdgeNodeIndices[2 * edgeIndex+1]; + + IslandId islandId1 = nodeIndex1.index() == IG_INVALID_NODE ? IG_INVALID_ISLAND : mIslandIds[nodeIndex1.index()]; + IslandId islandId2 = nodeIndex2.index() == IG_INVALID_NODE ? IG_INVALID_ISLAND : mIslandIds[nodeIndex2.index()]; + + //TODO - wake ups!!!! + //If one of the nodes is awake and the other is asleep, we need to wake 'em up + + //When a node is activated, the island must also be activated... + + bool active1 = nodeIndex1.index() != IG_INVALID_NODE && mNodes[nodeIndex1.index()].isActive(); + bool active2 = nodeIndex2.index() != IG_INVALID_NODE && mNodes[nodeIndex2.index()].isActive(); + + IslandId islandId = IG_INVALID_ISLAND; + + if(islandId1 == IG_INVALID_ISLAND && islandId2 == IG_INVALID_ISLAND) + { + //All nodes should be introduced in an island now unless they are static or kinematic. Therefore, if we get here, we have an edge + //between 2 kinematic nodes or a kinematic and static node. These should not influence island management so we should just ignore + //these edges. + } + else if(islandId1 == islandId2) + { + islandId = islandId1; + if(active1 || active2) + { + PX_ASSERT(mIslandAwake.test(islandId1)); //If we got here, where the 2 were already in an island, if 1 node is awake, the whole island must be awake + } + //Both bodies in the same island. Nothing major to do already but we should see if this creates a shorter path to root for either node + PxU32 hopCount1 = mHopCounts[nodeIndex1.index()]; + PxU32 hopCount2 = mHopCounts[nodeIndex2.index()]; + if((hopCount1+1) < hopCount2) + { + //It would be faster for node 2 to go through node 1 + mHopCounts[nodeIndex2.index()] = hopCount1 + 1; + mFastRoute[nodeIndex2.index()] = nodeIndex1; + } + else if((hopCount2+1) < hopCount1) + { + //It would be faster for node 1 to go through node 2 + mHopCounts[nodeIndex1.index()] = hopCount2 + 1; + mFastRoute[nodeIndex1.index()] = nodeIndex2; + } + + //No need to activate/deactivate the island. Its state won't have changed + + } + else if(islandId1 == IG_INVALID_ISLAND) + { + islandId = islandId2; + if (nodeIndex1.index() != IG_INVALID_NODE) + { + if (!mNodes[nodeIndex1.index()].isKinematic()) + { + PX_ASSERT(islandId2 != IG_INVALID_ISLAND); + //We need to add node 1 to island2 + PX_ASSERT(mNodes[nodeIndex1.index()].mNextNode.index() == IG_INVALID_NODE); //Ensure that this node is not in any other island + PX_ASSERT(mNodes[nodeIndex1.index()].mPrevNode.index() == IG_INVALID_NODE); //Ensure that this node is not in any other island + + Island& island = mIslands[islandId2]; + + Node& lastNode = mNodes[island.mLastNode.index()]; + + PX_ASSERT(lastNode.mNextNode.index() == IG_INVALID_NODE); + + Node& node = mNodes[nodeIndex1.index()]; + lastNode.mNextNode = nodeIndex1; + node.mPrevNode = island.mLastNode; + island.mLastNode = nodeIndex1; + island.mSize[node.mType]++; + mIslandIds[nodeIndex1.index()] = islandId2; + mHopCounts[nodeIndex1.index()] = mHopCounts[nodeIndex2.index()] + 1; + mFastRoute[nodeIndex1.index()] = nodeIndex2; + + if(active1 || active2) + { + if(!mIslandAwake.test(islandId2)) + { + //This island wasn't already awake, so need to wake the whole island up + activateIsland(islandId2); + } + if(!active1) + { + //Wake up this node... + activateNodeInternal(nodeIndex1); + } + } + } + else if(active1 && !active2) + { + //Active kinematic object -> wake island! + activateIsland(islandId2); + } + } + else + { + //A new touch with a static body... + Node& node = mNodes[nodeIndex2.index()]; + node.mStaticTouchCount++; //Increment static touch counter on the body + //Island& island = mIslands[islandId2]; + //island.mStaticTouchCount++; //Increment static touch counter on the island + mIslandStaticTouchCount[islandId2]++; + + } + } + else if (islandId2 == IG_INVALID_ISLAND) + { + islandId = islandId1; + if (nodeIndex2.index() != IG_INVALID_NODE) + { + if (!mNodes[nodeIndex2.index()].isKinematic()) + { + PX_ASSERT(islandId1 != IG_INVALID_NODE); + //We need to add node 1 to island2 + PX_ASSERT(mNodes[nodeIndex2.index()].mNextNode.index() == IG_INVALID_NODE); //Ensure that this node is not in any other island + PX_ASSERT(mNodes[nodeIndex2.index()].mPrevNode.index() == IG_INVALID_NODE); //Ensure that this node is not in any other island + + Island& island = mIslands[islandId1]; + + Node& lastNode = mNodes[island.mLastNode.index()]; + PX_ASSERT(lastNode.mNextNode.index() == IG_INVALID_NODE); + Node& node = mNodes[nodeIndex2.index()]; + lastNode.mNextNode = nodeIndex2; + node.mPrevNode = island.mLastNode; + island.mLastNode = nodeIndex2; + island.mSize[node.mType]++; + mIslandIds[nodeIndex2.index()] = islandId1; + mHopCounts[nodeIndex2.index()] = mHopCounts[nodeIndex1.index()] + 1; + mFastRoute[nodeIndex2.index()] = nodeIndex1; + + if(active1 || active2) + { + if(!mIslandAwake.test(islandId1)) + { + //This island wasn't already awake, so need to wake the whole island up + activateIsland(islandId1); + } + if(!active1) + { + //Wake up this node... + activateNodeInternal(nodeIndex2); + } + } + } + else if(active2 && !active1) + { + //Active kinematic object -> wake island! + activateIsland(islandId1); + } + } + else + { + //New static touch + //A new touch with a static body... + Node& node = mNodes[nodeIndex1.index()]; + node.mStaticTouchCount++; //Increment static touch counter on the body + //Island& island = mIslands[islandId1]; + mIslandStaticTouchCount[islandId1]++; + //island.mStaticTouchCount++; //Increment static touch counter on the island + } + + } + else + { + PX_ASSERT(islandId1 != islandId2); + PX_ASSERT(islandId1 != IG_INVALID_ISLAND && islandId2 != IG_INVALID_ISLAND); + + if(active1 || active2) + { + //One of the 2 islands was awake, so need to wake the other one! We do this now, before we merge the islands, to ensure that all + //the bodies are activated + if(!mIslandAwake.test(islandId1)) + { + //This island wasn't already awake, so need to wake the whole island up + activateIsland(islandId1); + } + if(!mIslandAwake.test(islandId2)) + { + //This island wasn't already awake, so need to wake the whole island up + activateIsland(islandId2); + } + } + + //OK. We need to merge these islands together... + islandId = mergeIslands(islandId1, islandId2, nodeIndex1, nodeIndex2); + } + + if(islandId != IG_INVALID_ISLAND) + { + //Add new edge to existing island + Island& island = mIslands[islandId]; + addEdgeToIsland(island, edgeIndex); + } + } + } + + } + +} + +bool IslandSim::isPathTo(NodeIndex startNode, NodeIndex targetNode) +{ + Node& node = mNodes[startNode.index()]; + + EdgeInstanceIndex index = node.mFirstEdgeIndex; + while(index != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[index]; + if(/*mEdges[index/2].isConnected() &&*/ mEdgeNodeIndices[index^1].index() == targetNode.index()) + return true; + index = instance.mNextEdge; + } + return false; +} + +bool IslandSim::tryFastPath(NodeIndex startNode, NodeIndex targetNode, IslandId islandId) +{ + PX_UNUSED(startNode); + PX_UNUSED(targetNode); + + NodeIndex currentNode = startNode; + + PxU32 currentVisitedNodes = mVisitedNodes.size(); + + PxU32 depth = 0; + + bool found = false; + do + { + //Get the fast path from this node... + + if(mVisitedState.test(currentNode.index())) + { + found = mIslandIds[currentNode.index()] != IG_INVALID_ISLAND; //Already visited and not tagged with invalid island == a witness! + break; + } + if( currentNode.index() == targetNode.index()) + { + found = true; + break; + } + + mVisitedNodes.pushBack(TraversalState(currentNode, mVisitedNodes.size(), mVisitedNodes.size()-1, depth++)); + + PX_ASSERT(mFastRoute[currentNode.index()].index() == IG_INVALID_NODE || isPathTo(currentNode, mFastRoute[currentNode.index()])); + + mIslandIds[currentNode.index()] = IG_INVALID_ISLAND; + mVisitedState.set(currentNode.index()); + + currentNode = mFastRoute[currentNode.index()]; + } + while(currentNode.index() != IG_INVALID_NODE); + + for(PxU32 a = currentVisitedNodes; a < mVisitedNodes.size(); ++a) + { + TraversalState& state = mVisitedNodes[a]; + mIslandIds[state.mNodeIndex.index()] = islandId; + } + + if(!found) + { + for(PxU32 a = currentVisitedNodes; a < mVisitedNodes.size(); ++a) + { + TraversalState& state = mVisitedNodes[a]; + mVisitedState.reset(state.mNodeIndex.index()); + } + + mVisitedNodes.forceSize_Unsafe(currentVisitedNodes); + } + return found; + +} + +bool IslandSim::findRoute(NodeIndex startNode, NodeIndex targetNode, IslandId islandId) +{ + + //Firstly, traverse the fast path and tag up witnesses. TryFastPath can fail. In that case, no witnesses are left but this node is permitted to report + //that it is still part of the island. Whichever node lost its fast path will be tagged as dirty and will be responsible for recovering the fast path + //and tagging up the visited nodes + if(mFastRoute[startNode.index()].index() != IG_INVALID_NODE) + { + if(tryFastPath(startNode, targetNode, islandId)) + return true; + + //Try fast path can either be successful or not. If it was successful, then we had a valid fast path cached and all nodes on that fast path were tagged + //as witness nodes (visited and with a valid island ID). If the fast path was not successful, then no nodes were tagged as witnesses. + //Technically, we need to find a route to the root node but, as an optimization, we can simply return true from here with no witnesses added. + //Whichever node actually broke the "fast path" will also be on the list of dirty nodes and will be processed later. + //If that broken edge triggered an island separation, this node will be re-visited and added to that island, otherwise + //the path to the root node will be re-established. The end result is the same - the island state is computed - this just saves us some work. + //return true; + } + + { + //If we got here, there was no fast path. Therefore, we need to fall back on searching for the root node. This is optimized by using "hop counts". + //These are per-node counts that indicate the expected number of hops from this node to the root node. These are lazily evaluated and updated + //as new edges are formed or when traversals occur to re-establish islands. As a result, they may be inaccurate but they still serve the purpose + //of guiding our search to minimize the chances of us doing an exhaustive search to find the root node. + mIslandIds[startNode.index()] = IG_INVALID_ISLAND; + TraversalState* startTraversal = &mVisitedNodes.pushBack(TraversalState(startNode, mVisitedNodes.size(), IG_INVALID_NODE, 0)); + mVisitedState.set(startNode.index()); + QueueElement element(startTraversal, mHopCounts[startNode.index()]); + mPriorityQueue.push(element); + + do + { + QueueElement currentQE = mPriorityQueue.pop(); + + TraversalState& currentState = *currentQE.mState; + + Node& currentNode = mNodes[currentState.mNodeIndex.index()]; + + EdgeInstanceIndex edge = currentNode.mFirstEdgeIndex; + + while(edge != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[edge]; + { + NodeIndex nextIndex = mEdgeNodeIndices[edge ^ 1]; + + //Static or kinematic nodes don't connect islands. + if(nextIndex.index() != IG_INVALID_NODE && !mNodes[nextIndex.index()].isKinematic()) + { + if(nextIndex.index() == targetNode.index()) + { + unwindRoute(currentState.mCurrentIndex, nextIndex, 0, islandId); + return true; + } + + if(mVisitedState.test(nextIndex.index())) + { + //We already visited this node. This means that it's either in the priority queue already or we + //visited in on a previous pass. If it was visited on a previous pass, then it already knows what island it's in. + //We now need to test the island id to find out if this node knows the root. + //If it has a valid root id, that id *is* our new root. We can guesstimate our hop count based on the node's properties + + IslandId visitedIslandId = mIslandIds[nextIndex.index()]; + if(visitedIslandId != IG_INVALID_ISLAND) + { + //If we get here, we must have found a node that knows a route to our root node. It must not be a different island + //because that would caused me to have been visited already because totally separate islands trigger a full traversal on + //the orphaned side. + PX_ASSERT(visitedIslandId == islandId); + unwindRoute(currentState.mCurrentIndex, nextIndex, mHopCounts[nextIndex.index()], islandId); + return true; + } + } + else + { + //This node has not been visited yet, so we need to push it into the stack and continue traversing + TraversalState* state = &mVisitedNodes.pushBack(TraversalState(nextIndex, mVisitedNodes.size(), currentState.mCurrentIndex, currentState.mDepth+1)); + QueueElement qe(state, mHopCounts[nextIndex.index()]); + mPriorityQueue.push(qe); + mVisitedState.set(nextIndex.index()); + PX_ASSERT(mIslandIds[nextIndex.index()] == islandId); + mIslandIds[nextIndex.index()] = IG_INVALID_ISLAND; //Flag as invalid island until we know whether we can find root or an island id. + } + } + } + + edge = instance.mNextEdge; + } + } + while(mPriorityQueue.size()); + + return false; + } +} + +#define IG_LIMIT_DIRTY_NODES 0 + + +void IslandSim::processLostEdges(Ps::Array& destroyedNodes, bool allowDeactivation, bool permitKinematicDeactivation, + PxU32 dirtyNodeLimit) +{ + PX_UNUSED(dirtyNodeLimit); + PX_PROFILE_ZONE("Basic.processLostEdges", getContextId()); + //At this point, all nodes and edges are activated. + + //Bit map for visited + mVisitedState.resizeAndClear(mNodes.size()); + + //Reserve space on priority queue for at least 1024 nodes. It will resize if more memory is required during traversal. + mPriorityQueue.reserve(1024); + + mIslandSplitEdges[0].reserve(1024); + mIslandSplitEdges[1].reserve(1024); + + mVisitedNodes.reserve(mNodes.size()); //Make sure we have enough space for all nodes! + + const PxU32 nbDestroyedEdges = mDestroyedEdges.size(); + PX_UNUSED(nbDestroyedEdges); + { + PX_PROFILE_ZONE("Basic.removeEdgesFromIslands", getContextId()); + for (PxU32 a = 0; a < mDestroyedEdges.size(); ++a) + { + EdgeIndex lostIndex = mDestroyedEdges[a]; + Edge& lostEdge = mEdges[lostIndex]; + + if (lostEdge.isPendingDestroyed() && !lostEdge.isInDirtyList()) + { + //Process this edge... + if (!lostEdge.isReportOnlyDestroy() && lostEdge.isInserted()) + { + PxU32 index1 = mEdgeNodeIndices[mDestroyedEdges[a] * 2].index(); + PxU32 index2 = mEdgeNodeIndices[mDestroyedEdges[a] * 2 + 1].index(); + + IslandId islandId = IG_INVALID_ISLAND; + if (index1 != IG_INVALID_NODE && index2 != IG_INVALID_NODE) + { + PX_ASSERT(mIslandIds[index1] == IG_INVALID_ISLAND || mIslandIds[index2] == IG_INVALID_ISLAND || + mIslandIds[index1] == mIslandIds[index2]); + islandId = mIslandIds[index1] != IG_INVALID_ISLAND ? mIslandIds[index1] : mIslandIds[index2]; + } + else if (index1 != IG_INVALID_NODE) + { + PX_ASSERT(index2 == IG_INVALID_NODE); + Node& node = mNodes[index1]; + if (!node.isKinematic()) + { + islandId = mIslandIds[index1]; + node.mStaticTouchCount--; + //Island& island = mIslands[islandId]; + mIslandStaticTouchCount[islandId]--; + //island.mStaticTouchCount--; + } + } + else if (index2 != IG_INVALID_NODE) + { + PX_ASSERT(index1 == IG_INVALID_NODE); + Node& node = mNodes[index2]; + if (!node.isKinematic()) + { + islandId = mIslandIds[index2]; + node.mStaticTouchCount--; + //Island& island = mIslands[islandId]; + mIslandStaticTouchCount[islandId]--; + //island.mStaticTouchCount--; + } + } + + if (islandId != IG_INVALID_ISLAND) + { + //We need to remove this edge from the island + Island& island = mIslands[islandId]; + removeEdgeFromIsland(island, lostIndex); + } + } + + lostEdge.clearInserted(); + + } + } + } + + if (allowDeactivation) + { + PX_PROFILE_ZONE("Basic.findPathsAndBreakIslands", getContextId()); + + + //KS - process only this many dirty nodes, deferring future dirty nodes to subsequent frames. + //This means that it may take several frames for broken edges to trigger islands to completely break but this is better + //than triggering large performance spikes. +#if IG_LIMIT_DIRTY_NODES + Cm::BitMap::CircularIterator iter(mDirtyMap, mLastMapIndex); + const PxU32 MaxCount = dirtyNodeLimit;// +10000000; + PxU32 lastMapIndex = mLastMapIndex; + PxU32 count = 0; +#else + Cm::BitMap::Iterator iter(mDirtyMap); +#endif + + + PxU32 dirtyIdx; + +#if IG_LIMIT_DIRTY_NODES + while ((dirtyIdx = iter.getNext()) != Cm::BitMap::CircularIterator::DONE + && (count++ < MaxCount) +#else + while ((dirtyIdx = iter.getNext()) != Cm::BitMap::Iterator::DONE +#endif + ) + { +#if IG_LIMIT_DIRTY_NODES + lastMapIndex = dirtyIdx + 1; +#endif + //Process dirty nodes. Figure out if we can make our way from the dirty node to the root. + + mPriorityQueue.clear(); //Clear the queue used for traversal + mVisitedNodes.forceSize_Unsafe(0); //Clear the list of nodes in this island + NodeIndex dirtyNodeIndex(dirtyIdx); + Node& dirtyNode = mNodes[dirtyNodeIndex.index()]; + + //Check whether this node has already been touched. If it has been touched this frame, then its island state is reliable + //and we can just unclear the dirty flag on the body. If we were already visited, then the state should have already been confirmed in a + //previous pass. + if (!dirtyNode.isKinematic() && !dirtyNode.isDeleted() && !mVisitedState.test(dirtyNodeIndex.index())) + { + //We haven't visited this node in our island repair passes yet, so we still need to process until we've hit a visited node or found + //our root node. Note that, as soon as we hit a visited node that has already been processed in a previous pass, we know that we can rely + //on its island information although the hop counts may not be optimal. It also indicates that this island was not broken immediately because + //otherwise, the entire new sub-island would already have been visited and this node would have already had its new island state assigned. + + //Indicate that I've been visited + + IslandId islandId = mIslandIds[dirtyNodeIndex.index()]; + Island& findIsland = mIslands[islandId]; + + NodeIndex searchNode = findIsland.mRootNode;//The node that we're searching for! + + if (searchNode.index() != dirtyNodeIndex.index()) //If we are the root node, we don't need to do anything! + { + if (findRoute(dirtyNodeIndex, searchNode, islandId)) + { + //We found the root node so let's let every visited node know that we found its root + //and we can also update our hop counts because we recorded how many hops it took to reach this + //node + + //We already filled in the path to the root/witness with accurate hop counts. Now we just need to fill in the estimates + //for the remaining nodes and re-define their islandIds. We approximate their path to the root by just routing them through + //the route we already found. + + //This loop works because mVisitedNodes are recorded in the order they were visited and we already filled in the critical path + //so the remainder of the paths will just fork from that path. + + //Verify state (that we can see the root from this node)... + +#if IG_SANITY_CHECKS + PX_ASSERT(canFindRoot(dirtyNode, searchNode, NULL)); //Verify that we found the connection +#endif + + for (PxU32 b = 0; b < mVisitedNodes.size(); ++b) + { + TraversalState& state = mVisitedNodes[b]; + if (mIslandIds[state.mNodeIndex.index()] == IG_INVALID_ISLAND) + { + mHopCounts[state.mNodeIndex.index()] = mHopCounts[mVisitedNodes[state.mPrevIndex].mNodeIndex.index()] + 1; + mFastRoute[state.mNodeIndex.index()] = mVisitedNodes[state.mPrevIndex].mNodeIndex; + mIslandIds[state.mNodeIndex.index()] = islandId; + } + } + } + else + { + //If I traversed and could not find the root node, then I have established a new island. In this island, I am the root node + //and I will point all my nodes towards me. Furthermore, I have established how many steps it took to reach all nodes in my island + + //OK. We need to separate the islands. We have a list of nodes that are part of the new island (mVisitedNodes) and we know that the + //first node in that list is the root node. + + + //OK, we need to remove all these actors from their current island, then add them to the new island... + + Island& oldIsland = mIslands[islandId]; + //We can just unpick these nodes from the island because they do not contain the root node (if they did, then we wouldn't be + //removing this node from the island at all). The only challenge is if we need to remove the last node. In that case + //we need to re-establish the new last node in the island but perhaps the simplest way to do that would be to traverse + //the island to establish the last node again + +#if IG_SANITY_CHECKS + PX_ASSERT(!canFindRoot(dirtyNode, searchNode, NULL)); +#endif + + PxU32 totalStaticTouchCount = 0; + mIslandSplitEdges[0].forceSize_Unsafe(0); + mIslandSplitEdges[1].forceSize_Unsafe(0); + PxU32 size[2] = { 0,0 }; + + //NodeIndex lastIndex = oldIsland.mLastNode; + + //size[node.mType] = 1; + + for (PxU32 a = 0; a < mVisitedNodes.size(); ++a) + { + NodeIndex index = mVisitedNodes[a].mNodeIndex; + Node& node = mNodes[index.index()]; + + if (node.mNextNode.index() != IG_INVALID_NODE) + mNodes[node.mNextNode.index()].mPrevNode = node.mPrevNode; + else + oldIsland.mLastNode = node.mPrevNode; + if (node.mPrevNode.index() != IG_INVALID_NODE) + mNodes[node.mPrevNode.index()].mNextNode = node.mNextNode; + + size[node.mType]++; + + node.mNextNode.setIndices(IG_INVALID_NODE); + node.mPrevNode.setIndices(IG_INVALID_NODE); + + PX_ASSERT(mNodes[oldIsland.mLastNode.index()].mNextNode.index() == IG_INVALID_NODE); + + totalStaticTouchCount += node.mStaticTouchCount; + + EdgeInstanceIndex idx = node.mFirstEdgeIndex; + + while (idx != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[idx]; + const EdgeIndex edgeIndex = idx / 2; + Edge& edge = mEdges[edgeIndex]; + + //Only split the island if we're processing the first node or if the first node is infinte-mass + if (!(idx & 1) || (mEdgeNodeIndices[idx & (~1)].index() == IG_INVALID_NODE || mNodes[mEdgeNodeIndices[idx & (~1)].index()].isKinematic())) + { + //We will remove this edge from the island... + mIslandSplitEdges[edge.mEdgeType].pushBack(edgeIndex); + + removeEdgeFromIsland(oldIsland, edgeIndex); + + } + idx = instance.mNextEdge; + } + + } + + //oldIsland.mStaticTouchCount -= totalStaticTouchCount; + mIslandStaticTouchCount[islandId] -= totalStaticTouchCount; + + oldIsland.mSize[0] -= size[0]; + oldIsland.mSize[1] -= size[1]; + + //Now add all these nodes to the new island + + //(1) Create the new island... + IslandId newIslandHandle = mIslandHandles.getHandle(); + /*if(newIslandHandle == mIslands.capacity()) + { + mIslands.reserve(2*mIslands.capacity() + 1); + }*/ + mIslands.resize(PxMax(newIslandHandle + 1, mIslands.size())); + mIslandStaticTouchCount.resize(PxMax(newIslandHandle + 1, mIslandStaticTouchCount.size())); + Island& newIsland = mIslands[newIslandHandle]; + + if (mIslandAwake.test(islandId)) + { + newIsland.mActiveIndex = mActiveIslands.size(); + mActiveIslands.pushBack(newIslandHandle); + mIslandAwake.growAndSet(newIslandHandle); //Separated island, so it should be awake + } + else + { + mIslandAwake.growAndReset(newIslandHandle); + } + + newIsland.mRootNode = dirtyNodeIndex; + mHopCounts[dirtyNodeIndex.index()] = 0; + mIslandIds[dirtyNodeIndex.index()] = newIslandHandle; + //newIsland.mTotalSize = mVisitedNodes.size(); + + mNodes[dirtyNodeIndex.index()].mPrevNode.setIndices(IG_INVALID_NODE); //First node so doesn't have a preceding node + mFastRoute[dirtyNodeIndex.index()].setIndices(IG_INVALID_NODE); + + size[0] = 0; size[1] = 0; + + size[dirtyNode.mType] = 1; + + for (PxU32 a = 1; a < mVisitedNodes.size(); ++a) + { + NodeIndex index = mVisitedNodes[a].mNodeIndex; + Node& thisNode = mNodes[index.index()]; + NodeIndex prevNodeIndex = mVisitedNodes[a - 1].mNodeIndex; + thisNode.mPrevNode = prevNodeIndex; + mNodes[prevNodeIndex.index()].mNextNode = index; + size[thisNode.mType]++; + mIslandIds[index.index()] = newIslandHandle; + mHopCounts[index.index()] = mVisitedNodes[a].mDepth; //How many hops to root + mFastRoute[index.index()] = mVisitedNodes[mVisitedNodes[a].mPrevIndex].mNodeIndex; + } + + newIsland.mSize[0] = size[0]; + newIsland.mSize[1] = size[1]; + //Last node in the island + NodeIndex lastIndex = mVisitedNodes[mVisitedNodes.size() - 1].mNodeIndex; + mNodes[lastIndex.index()].mNextNode.setIndices(IG_INVALID_NODE); + newIsland.mLastNode = lastIndex; + //newIsland.mStaticTouchCount = totalStaticTouchCount; + mIslandStaticTouchCount[newIslandHandle] = totalStaticTouchCount; + newIsland.mSize[0] = size[0]; + newIsland.mSize[1] = size[1]; + + PX_ASSERT(mNodes[newIsland.mLastNode.index()].mNextNode.index() == IG_INVALID_NODE); + + for (PxU32 j = 0; j < 2; ++j) + { + Ps::Array& splitEdges = mIslandSplitEdges[j]; + const PxU32 splitEdgeSize = splitEdges.size(); + if (splitEdgeSize) + { + splitEdges.pushBack(IG_INVALID_EDGE); //Push in a dummy invalid edge to complete the connectivity + mEdges[splitEdges[0]].mNextIslandEdge = splitEdges[1]; + for (PxU32 a = 1; a < splitEdgeSize; ++a) + { + EdgeIndex edgeIndex = splitEdges[a]; + Edge& edge = mEdges[edgeIndex]; + edge.mNextIslandEdge = splitEdges[a + 1]; + edge.mPrevIslandEdge = splitEdges[a - 1]; + } + + newIsland.mFirstEdge[j] = splitEdges[0]; + newIsland.mLastEdge[j] = splitEdges[splitEdgeSize - 1]; + newIsland.mEdgeCount[j] = splitEdgeSize; + } + } + + } + } + + } + + dirtyNode.clearDirty(); +#if IG_LIMIT_DIRTY_NODES + mDirtyMap.reset(dirtyIdx); +#endif +} + + + +#if IG_LIMIT_DIRTY_NODES + mLastMapIndex = lastMapIndex; + if (count < MaxCount) + mLastMapIndex = 0; +#else + mDirtyMap.clear(); +#endif + + //mDirtyNodes.forceSize_Unsafe(0); + } + + + { + PX_PROFILE_ZONE("Basic.clearDestroyedEdges", getContextId()); + //Now process the lost edges... + for (PxU32 a = 0; a < mDestroyedEdges.size(); ++a) + { + //Process these destroyed edges. Recompute island information. Update the islands and hop counters accordingly + EdgeIndex index = mDestroyedEdges[a]; + + Edge& edge = mEdges[index]; + if (edge.isPendingDestroyed()) + { + //if(edge.mFirstPartitionEdge) + PartitionEdge* pEdge = mFirstPartitionEdges ? (*mFirstPartitionEdges)[index] : NULL; + if (pEdge) + { + mDestroyedPartitionEdges->pushBack(pEdge); + (*mFirstPartitionEdges)[index] = NULL; //Force first partition edge to NULL to ensure we don't have a clash + } + if (edge.isActive()) + { + removeEdgeFromActivatingList(index); //TODO - can we remove this call? Can we handle this elsewhere, e.g. when destroying the nodes... + mActiveEdgeCount[edge.mEdgeType]--; + } + + edge = Edge(); //Reset edge + mActiveContactEdges.growAndReset(index); + } + } + + mDestroyedEdges.forceSize_Unsafe(0); + } + + { + PX_PROFILE_ZONE("Basic.clearDestroyedNodes", getContextId()); + + for (PxU32 a = 0; a < destroyedNodes.size(); ++a) + { + NodeIndex nodeIndex = destroyedNodes[a]; + IslandId islandId = mIslandIds[nodeIndex.index()]; + Node& node = mNodes[nodeIndex.index()]; + if (islandId != IG_INVALID_ISLAND) + { + Island& island = mIslands[islandId]; + + removeNodeFromIsland(island, nodeIndex); + + mIslandIds[nodeIndex.index()] = IG_INVALID_ISLAND; + + if ((island.mSize[0] + island.mSize[1]) == 0) + { + mIslandHandles.freeHandle(islandId); + if (island.mActiveIndex != IG_INVALID_ISLAND) + { + IslandId replaceId = mActiveIslands[mActiveIslands.size() - 1]; + Island& replaceIsland = mIslands[replaceId]; + replaceIsland.mActiveIndex = island.mActiveIndex; + mActiveIslands[island.mActiveIndex] = replaceId; + mActiveIslands.forceSize_Unsafe(mActiveIslands.size() - 1); + island.mActiveIndex = IG_INVALID_ISLAND; + //island.mStaticTouchCount -= node.mStaticTouchCount; //Remove the static touch count from the island + mIslandStaticTouchCount[islandId] -= node.mStaticTouchCount; + } + mIslandAwake.reset(islandId); + island.mLastNode.setIndices(IG_INVALID_NODE); + island.mRootNode.setIndices(IG_INVALID_NODE); + island.mActiveIndex = IG_INVALID_ISLAND; + } + } + + if (node.isKinematic()) + { + if (mActiveNodeIndex[nodeIndex.index()] != IG_INVALID_NODE) + { + //Remove from the active kinematics list... + markKinematicInactive(nodeIndex); + } + } + else + { + if (mActiveNodeIndex[nodeIndex.index()] != IG_INVALID_NODE) + { + markInactive(nodeIndex); + } + } + + node.reset(); + } + } + //Now we need to produce the list of active edges and nodes!!! + + //If we get here, we have a list of active islands. From this, we need to iterate over all active islands and establish if that island + //can, in fact, go to sleep. In order to become deactivated, all nodes in the island must be ready for sleeping... + + if (allowDeactivation) + { + PX_PROFILE_ZONE("Basic.deactivation", getContextId()); + for (PxU32 a = 0; a < mActiveIslands.size(); a++) + { + IslandId islandId = mActiveIslands[a]; + + mIslandAwake.reset(islandId); + } + + //Loop over the active kinematic nodes and tag all islands touched by active kinematics as awake + for (PxU32 a = mActiveKinematicNodes.size(); a > 0; --a) + { + NodeIndex kinematicIndex = mActiveKinematicNodes[a - 1]; + + Node& kinematicNode = mNodes[kinematicIndex.index()]; + + if (kinematicNode.isReadyForSleeping()) + { + if (permitKinematicDeactivation) + { + kinematicNode.clearActive(); + markKinematicInactive(kinematicIndex); + } + } + else //if(!kinematicNode.isReadyForSleeping()) + { + //KS - if kinematic is active, then wake up all islands the kinematic is touching + EdgeInstanceIndex edgeId = kinematicNode.mFirstEdgeIndex; + while (edgeId != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[edgeId]; + //Edge& edge = mEdges[edgeId/2]; + //Only wake up islands if a connection was present + //if(edge.isConnected()) + { + NodeIndex outNode = mEdgeNodeIndices[edgeId ^ 1]; + if (outNode.index() != IG_INVALID_NODE) + { + IslandId islandId = mIslandIds[outNode.index()]; + if (islandId != IG_INVALID_ISLAND) + { + mIslandAwake.set(islandId); + PX_ASSERT(mIslands[islandId].mActiveIndex != IG_INVALID_ISLAND); + } + } + } + edgeId = instance.mNextEdge; + } + } + } + + for (PxU32 a = mActiveIslands.size(); a > 0; --a) + { + IslandId islandId = mActiveIslands[a - 1]; + + Island& island = mIslands[islandId]; + + bool canDeactivate = !mIslandAwake.test(islandId); + mIslandAwake.set(islandId); + + //If it was touched by an active kinematic in the above loop, we can't deactivate it. + //Therefore, no point in testing the nodes in the island. They must remain awake + if (canDeactivate) + { + NodeIndex nodeId = island.mRootNode; + while (nodeId.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeId.index()]; + if (!node.isReadyForSleeping()) + { + canDeactivate = false; + break; + } + nodeId = node.mNextNode; + } + if (canDeactivate) + { + //If all nodes in this island are ready for sleeping and there were no active + //kinematics interacting with the any bodies in the island, we can deactivate the island. + deactivateIsland(islandId); + } + } + } + } + + { + PX_PROFILE_ZONE("Basic.resetDirtyEdges", getContextId()); + for (PxU32 i = 0; i < Edge::eEDGE_TYPE_COUNT; ++i) + { + for (PxU32 a = 0; a < mDirtyEdges[i].size(); ++a) + { + Edge& edge = mEdges[mDirtyEdges[i][a]]; + edge.clearInDirtyList(); + } + mDirtyEdges[i].clear(); //All new edges processed + } + } + +} + +IslandId IslandSim::mergeIslands(IslandId island0, IslandId island1, NodeIndex node0, NodeIndex node1) +{ + Island& is0 = mIslands[island0]; + Island& is1 = mIslands[island1]; + + //We defer this process and do it later instead. That way, if we have some pathalogical + //case where multiple islands get merged repeatedly, we don't end up repeatedly remapping all the nodes in those islands + //to their new island. Instead, we just choose the largest island and remap the smaller island to that. + + PxU32 totalSize0 = is0.mSize[0] + is0.mSize[1]; + PxU32 totalSize1 = is1.mSize[0] + is1.mSize[1]; + if(totalSize0 > totalSize1) + { + mergeIslandsInternal(is0, is1, island0, island1, node0, node1); + mIslandAwake.reset(island1); + mIslandHandles.freeHandle(island1); + mFastRoute[node1.index()] = node0; + return island0; + } + else + { + mergeIslandsInternal(is1, is0, island1, island0, node1, node0); + mIslandAwake.reset(island0); + mIslandHandles.freeHandle(island0); + mFastRoute[node0.index()] = node1; + return island1; + } +} + +bool IslandSim::checkInternalConsistency() +{ + //Loop over islands, confirming that the island data is consistent... + //Really expensive. Turn off unless investigating some random issue... +#if 0 + for (PxU32 a = 0; a < mIslands.size(); ++a) + { + Island& island = mIslands[a]; + + PxU32 expectedSize = island.mSize[0] + island.mSize[1]; + bool metLastNode = expectedSize == 0; + + NodeIndex nodeId = island.mRootNode; + + while (nodeId.index() != IG_INVALID_NODE) + { + + PX_ASSERT(mIslandIds[nodeId.index()] == a); + + if (nodeId.index() == island.mLastNode.index()) + { + metLastNode = true; + PX_ASSERT(mNodes[nodeId.index()].mNextNode.index() == IG_INVALID_NODE); + } + + --expectedSize; + + nodeId = mNodes[nodeId.index()].mNextNode; + } + + PX_ASSERT(expectedSize == 0); + PX_ASSERT(metLastNode); + } +#endif + + return true; + +} + +void IslandSim::mergeIslandsInternal(Island& island0, Island& island1, IslandId islandId0, IslandId islandId1, NodeIndex nodeIndex0, NodeIndex nodeIndex1) +{ + PX_ASSERT((island0.mSize[0] + island0.mSize[1]) >= (island1.mSize[0] + island1.mSize[1])); //We only ever merge the smaller island to the larger island + //Stage 1 - we need to move all the nodes across to the new island ID (i.e. write all their new island indices, move them to the + //island and then also update their estimated hop counts to the root. As we don't want to do a full traversal at this point, + //instead, we estimate based on the route from the node to their previous root and then from that root to the new connection + //between the 2 islands. This is probably a very indirect route but it will be refined later. + + //In this case, island1 is subsumed by island0 + + //It takes mHopCounts[nodeIndex1] to get from node1 to its old root. It takes mHopCounts[nodeIndex0] to get from nodeIndex0 to the new root + //and it takes 1 extra hop to go from node1 to node0. Therefore, a sub-optimal route can be planned going via the old root node that should take + //mHopCounts[nodeIndex0] + mHopCounts[nodeIndex1] + 1 + mHopCounts[nodeIndex] to travel from any arbitrary node (nodeIndex) in island1 to the root + //of island2. + + + PxU32 extraPath = mHopCounts[nodeIndex0.index()] + mHopCounts[nodeIndex1.index()] + 1; + + NodeIndex islandNode = island1.mRootNode; + while(islandNode.index() != IG_INVALID_NODE) + { + mHopCounts[islandNode.index()] += extraPath; + mIslandIds[islandNode.index()] = islandId0; + + //mFastRoute[islandNode] = IG_INVALID_NODE; + + Node& node = mNodes[islandNode.index()]; + islandNode = node.mNextNode; + } + + //Now fill in the hop count for node1, which is directly connected to node0. + mHopCounts[nodeIndex1.index()] = mHopCounts[nodeIndex0.index()] + 1; + Node& lastNode = mNodes[island0.mLastNode.index()]; + Node& firstNode = mNodes[island1.mRootNode.index()]; + PX_ASSERT(lastNode.mNextNode.index() == IG_INVALID_NODE); + PX_ASSERT(firstNode.mPrevNode.index() == IG_INVALID_NODE); + PX_ASSERT(island1.mRootNode.index() != island0.mLastNode.index()); + + PX_ASSERT(mNodes[island0.mLastNode.index()].mNextNode.index() == IG_INVALID_NODE); + PX_ASSERT(mNodes[island1.mLastNode.index()].mNextNode.index() == IG_INVALID_NODE); + + PX_ASSERT(mIslandIds[island0.mLastNode.index()] == islandId0); + + + + lastNode.mNextNode = island1.mRootNode; + firstNode.mPrevNode = island0.mLastNode; + + + + island0.mLastNode = island1.mLastNode; + island0.mSize[0] += island1.mSize[0]; + island0.mSize[1] += island1.mSize[1]; + //island0.mStaticTouchCount += island1.mStaticTouchCount; + mIslandStaticTouchCount[islandId0] += mIslandStaticTouchCount[islandId1]; + + //Merge the edge list for the islands... + for(PxU32 a = 0; a < 2; ++a) + { + if(island0.mLastEdge[a] != IG_INVALID_EDGE) + { + PX_ASSERT(mEdges[island0.mLastEdge[a]].mNextIslandEdge == IG_INVALID_EDGE); + mEdges[island0.mLastEdge[a]].mNextIslandEdge = island1.mFirstEdge[a]; + } + else + { + PX_ASSERT(island0.mFirstEdge[a] == IG_INVALID_EDGE); + island0.mFirstEdge[a] = island1.mFirstEdge[a]; + } + if(island1.mFirstEdge[a] != IG_INVALID_EDGE) + { + PX_ASSERT(mEdges[island1.mFirstEdge[a]].mPrevIslandEdge == IG_INVALID_EDGE); + mEdges[island1.mFirstEdge[a]].mPrevIslandEdge = island0.mLastEdge[a]; + island0.mLastEdge[a] = island1.mLastEdge[a]; + } + + island0.mEdgeCount[a] += island1.mEdgeCount[a]; + island1.mFirstEdge[a] = IG_INVALID_EDGE; + island1.mLastEdge[a] = IG_INVALID_EDGE; + island1.mEdgeCount[a] = 0; + } + + + island1.mLastNode.setIndices(IG_INVALID_NODE); + island1.mRootNode.setIndices(IG_INVALID_NODE); + island1.mSize[0] = 0; + island1.mSize[1] = 0; + mIslandStaticTouchCount[islandId1] = 0; + //island1.mStaticTouchCount = 0; + + //Remove from active island list + if(island1.mActiveIndex != IG_INVALID_ISLAND) + { + markIslandInactive(islandId1); + } + +} + +void IslandSim::removeEdgeFromActivatingList(EdgeIndex index) +{ + Edge& edge = mEdges[index]; + + if (edge.mEdgeState & Edge::eACTIVATING) + { + for (PxU32 a = 0, count = mActivatedEdges[edge.mEdgeType].size(); a < count; a++) + { + if (mActivatedEdges[edge.mEdgeType][a] == index) + { + mActivatedEdges[edge.mEdgeType].replaceWithLast(a); + break; + } + } + + edge.mEdgeState &= (~Edge::eACTIVATING); + } + + + NodeIndex nodeIndex1 = mEdgeNodeIndices[index * 2]; + NodeIndex nodeIndex2 = mEdgeNodeIndices[index * 2 + 1]; + + if (nodeIndex1.isValid() && nodeIndex2.isValid()) + { + { + Node& node = mNodes[nodeIndex1.index()]; + node.mActiveRefCount--; + } + { + Node& node = mNodes[nodeIndex2.index()]; + node.mActiveRefCount--; + } + } + + if(edge.mEdgeType == Edge::eCONTACT_MANAGER) + mActiveContactEdges.reset(index); + +} + +void IslandSim::setKinematic(IG::NodeIndex nodeIndex) +{ + + Node& node = mNodes[nodeIndex.index()]; + + if(!node.isKinematic()) + { + + //Transition from dynamic to kinematic: + //(1) Remove this node from the island + //(2) Remove this node from the active node list + //(3) If active or referenced, add it to the active kinematic list + //(4) Tag the node as kinematic + //External code will re-filter interactions and lost touches will be reported + + IslandId islandId = mIslandIds[nodeIndex.index()]; + PX_ASSERT(islandId != IG_INVALID_ISLAND); + + Island& island = mIslands[islandId]; + + mIslandIds[nodeIndex.index()] = IG_INVALID_ISLAND; + + removeNodeFromIsland(island, nodeIndex); + + bool isActive = node.isActive(); + + if (isActive) + { + //Remove from active list... + markInactive(nodeIndex); + } + else if (node.isActivating()) + { + //Remove from activating list... + node.clearActivating(); + PX_ASSERT(mActivatingNodes[mActiveNodeIndex[nodeIndex.index()]].index() == nodeIndex.index()); + + NodeIndex replaceIndex = mActivatingNodes[mActivatingNodes.size() - 1]; + mActiveNodeIndex[replaceIndex.index()] = mActiveNodeIndex[nodeIndex.index()]; + mActivatingNodes[mActiveNodeIndex[nodeIndex.index()]] = replaceIndex; + mActivatingNodes.forceSize_Unsafe(mActivatingNodes.size() - 1); + mActiveNodeIndex[nodeIndex.index()] = IG_INVALID_NODE; + } + + node.setKinematicFlag(); + + node.clearActive(); + + if (/*isActive || */node.mActiveRefCount != 0) + { + //Add to active kinematic list... + PX_ASSERT(mActiveNodeIndex[nodeIndex.index()] == IG_INVALID_NODE); + + mActiveNodeIndex[nodeIndex.index()] = mActivatingNodes.size(); + mActivatingNodes.pushBack(nodeIndex); + node.setActivating(); + } + + PxU32 newSize = island.mSize[0] + island.mSize[1]; + + { + //This node was in an island with other bodies. We need to force an island recomputation in case the + //islands became broken due to losing this connection. Same rules as losing a contact, we just + //tag the nodes directly connected to the lost edge as "dirty" and force an island recomputation if + //it resulted in lost connections + EdgeInstanceIndex edgeId = node.mFirstEdgeIndex; + while(edgeId != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[edgeId]; + EdgeInstanceIndex nextId = instance.mNextEdge; + + PxU32 idx = edgeId/2; + IG::Edge& edge = mEdges[edgeId/2]; + + removeEdgeFromIsland(island, idx); + + removeConnectionInternal(idx); + removeConnectionFromGraph(idx); + + edge.clearInserted(); + + if (edge.isActive()) + { + removeEdgeFromActivatingList(idx); + edge.deactivateEdge(); + mActiveEdgeCount[edge.mEdgeType]--; + } + + if(!edge.isPendingDestroyed()) + { + if(!edge.isInDirtyList()) + { + PX_ASSERT(!contains(mDirtyEdges[edge.mEdgeType], idx)); + mDirtyEdges[edge.mEdgeType].pushBack(idx); + edge.markInDirtyList(); + } + } + else + { + edge.setReportOnlyDestroy(); + } + + edgeId = nextId; + } + } + + if(newSize == 0) + { + //This node was in an island by itself, so no need to mess with any connections + for(PxU32 a = 0; a < Edge::eEDGE_TYPE_COUNT; ++a) + { + island.mFirstEdge[a] = island.mLastEdge[a] = IG_INVALID_EDGE; + island.mEdgeCount[a] = 0; + mIslandStaticTouchCount[islandId] = 0; + //island.mStaticTouchCount = 0; + } + + if(island.mActiveIndex != IG_INVALID_ISLAND) + { + markIslandInactive(islandId); + } + + mIslandAwake.reset(islandId); + mIslandHandles.freeHandle(islandId); + } + } +} + +void IslandSim::setDynamic(IG::NodeIndex nodeIndex) +{ + //(1) Remove all edges involving this node from all islands they may be in + //(2) Mark all edges as "new" edges - let island gen re-process them! + //(3) Remove this node from the active kinematic list + //(4) Add this node to the active dynamic list (if it is active) + //(5) Mark node as dynamic + + + Node& node = mNodes[nodeIndex.index()]; + + if(node.isKinematic()) + { + //EdgeInstanceIndex edgeIndex = node.mFirstEdgeIndex; + + EdgeInstanceIndex edgeId = node.mFirstEdgeIndex; + while(edgeId != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[edgeId]; + EdgeInstanceIndex nextId = instance.mNextEdge; + + NodeIndex otherNode = mEdgeNodeIndices[edgeId^1]; + + PxU32 idx = edgeId/2; + IG::Edge& edge = mEdges[edgeId/2]; + + if(!otherNode.isStaticBody()) + { + IslandId islandId = mIslandIds[otherNode.index()]; + if(islandId != IG_INVALID_ISLAND) + removeEdgeFromIsland(mIslands[islandId], idx); + } + + removeConnectionInternal(idx); + removeConnectionFromGraph(idx); + + edge.clearInserted(); + if (edge.isActive()) + { + edge.deactivateEdge(); + removeEdgeFromActivatingList(idx); + mActiveEdgeCount[edge.mEdgeType]--; + } + + if(!edge.isPendingDestroyed()) + { + if(!edge.isInDirtyList()) + { + PX_ASSERT(!contains(mDirtyEdges[edge.mEdgeType], idx)); + mDirtyEdges[edge.mEdgeType].pushBack(idx); + edge.markInDirtyList(); + } + } + else + { + edge.setReportOnlyDestroy(); + } + + edgeId = nextId; + } + + + if(!node.isActivating() && mActiveNodeIndex[nodeIndex.index()] != IG_INVALID_NODE) + { + //Remove from active kinematic list, add to active dynamic list + PxU32 oldRefCount = node.mActiveRefCount; + node.mActiveRefCount = 0; + markKinematicInactive(nodeIndex); + node.mActiveRefCount = oldRefCount; + } + + node.clearKinematicFlag(); + + + + //Create an island for this node. If there are any edges affecting this node, they will have been marked as + //"new" and will be processed next island update. + { + IslandId islandHandle = mIslandHandles.getHandle(); + + if(islandHandle == mIslands.capacity()) + { + const PxU32 newCapacity = 2*mIslands.capacity()+1; + mIslands.reserve(newCapacity); + mIslandAwake.resize(newCapacity); + mIslandStaticTouchCount.resize(newCapacity); + } + mIslandAwake.reset(islandHandle); + mIslands.resize(PxMax(islandHandle+1, mIslands.size())); + mIslandStaticTouchCount.resize(PxMax(islandHandle + 1, mIslands.size())); + Island& island = mIslands[islandHandle]; + island.mLastNode = island.mRootNode = nodeIndex; + PX_ASSERT(mNodes[nodeIndex.index()].mNextNode.index() == IG_INVALID_NODE); + island.mSize[node.mType] = 1; + mIslandIds[nodeIndex.index()] = islandHandle; + mIslandStaticTouchCount[islandHandle] = 0; + + + if(node.isActive()) + { + node.clearActive(); + + activateNode(nodeIndex); + } + } + } +} + +} +} diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp new file mode 100644 index 000000000..476d3bc71 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp @@ -0,0 +1,102 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxsMaterialCombiner.h" +#include "PsMathUtils.h" +#include "CmPhysXCommon.h" +#include "PsFoundation.h" + +namespace physx +{ + + +PxsMaterialCombiner::PxsMaterialCombiner(PxReal staticFrictionScaling, PxReal dynamicFrictionScaling) +: mStaticFrictionScaling(staticFrictionScaling), mDynamicFrictionScaling(dynamicFrictionScaling) +{} + + +PxReal PxsMaterialCombiner::combineRestitution(const PxsMaterialData& mat0, const PxsMaterialData& mat1) +{ + /*return combineScalars(mat0.restitution, mat1.restitution, PxMax(mat0.restitutionCombineMode, mat1.restitutionCombineMode));*/ + return combineScalars(mat0.restitution, mat1.restitution, PxMax(mat0.getRestitutionCombineMode(), mat1.getRestitutionCombineMode())); +} + +PxsMaterialCombiner::PxsCombinedMaterial PxsMaterialCombiner::combineIsotropicFriction(const PxsMaterialData& mat0, const PxsMaterialData& mat1) +{ + PxsCombinedMaterial dest; + + dest.flags = (mat0.flags | mat1.flags); //& (PxMaterialFlag::eDISABLE_STRONG_FRICTION|PxMaterialFlag::eDISABLE_FRICTION); //eventually set DisStrongFric flag, lower all others. + + if (!(dest.flags & PxMaterialFlag::eDISABLE_FRICTION)) + { + const PxI32 fictionCombineMode = PxMax(mat0.getFrictionCombineMode(), mat1.getFrictionCombineMode()); + PxReal dynFriction = 0.f; + PxReal staFriction = 0.f; + + + switch (fictionCombineMode) + { + case PxCombineMode::eAVERAGE: + dynFriction = 0.5f * (mat0.dynamicFriction + mat1.dynamicFriction); + staFriction = 0.5f * (mat0.staticFriction + mat1.staticFriction); + break; + case PxCombineMode::eMIN: + dynFriction = PxMin(mat0.dynamicFriction, mat1.dynamicFriction); + staFriction = PxMin(mat0.staticFriction, mat1.staticFriction); + break; + case PxCombineMode::eMULTIPLY: + dynFriction = (mat0.dynamicFriction * mat1.dynamicFriction); + staFriction = (mat0.staticFriction * mat1.staticFriction); + break; + case PxCombineMode::eMAX: + dynFriction = PxMax(mat0.dynamicFriction, mat1.dynamicFriction); + staFriction = PxMax(mat0.staticFriction, mat1.staticFriction); + break; + } + + dynFriction*=mDynamicFrictionScaling; + staFriction*=mStaticFrictionScaling; + //isotropic case + const PxReal fDynFriction = PxMax(dynFriction, 0.f); + + const PxReal fStaFriction = physx::intrinsics::fsel(staFriction - fDynFriction, staFriction, fDynFriction); + dest.dynFriction = fDynFriction; + dest.staFriction = fStaFriction; + } + else + { + dest.flags |= PxMaterialFlag::eDISABLE_STRONG_FRICTION; + dest.staFriction = 0.0f; + dest.dynFriction = 0.0f; + } + + return dest; +} +} diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp new file mode 100644 index 000000000..4dbd52df2 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp @@ -0,0 +1,999 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxsContext.h" +#include "CmFlushPool.h" +#include "PxsSimpleIslandManager.h" +#include "common/PxProfileZone.h" + +#if PX_SUPPORT_GPU_PHYSX +#include "PxPhysXGpu.h" +#include "task/PxGpuDispatcher.h" +#endif + +#include "PxsContactManagerState.h" + +#include "PxsNphaseImplementationContext.h" +#include "PxvGeometry.h" +#include "PxvDynamics.h" +#include "PxvGlobals.h" + +#include "PxcNpContactPrepShared.h" + +using namespace physx; +using namespace physx::shdfnd; + + +class PxsCMUpdateTask : public Cm::Task +{ +public: + + static const PxU32 BATCH_SIZE = 128; + + PxsCMUpdateTask(PxsContext* context, PxReal dt, PxsContactManager** cmArray, PxsContactManagerOutput* cmOutputs, Gu::Cache* caches, PxU32 cmCount, PxContactModifyCallback* callback) : + Cm::Task (context->getContextId()), + mCmArray (cmArray), + mCmOutputs (cmOutputs), + mCaches (caches), + mCmCount (cmCount), + mDt (dt), + mContext (context), + mCallback (callback) + { + } + + virtual void release(); + + /*PX_FORCE_INLINE void insert(PxsContactManager* cm) + { + PX_ASSERT(mCmCount < BATCH_SIZE); + mCmArray[mCmCount++]=cm; + }*/ + +protected: + //PxsContactManager* mCmArray[BATCH_SIZE]; + PxsContactManager** mCmArray; + PxsContactManagerOutput* mCmOutputs; + Gu::Cache* mCaches; + PxU32 mCmCount; + PxReal mDt; //we could probably retrieve from context to save space? + PxsContext* mContext; + PxContactModifyCallback* mCallback; +}; + +void PxsCMUpdateTask::release() +{ + // We used to do Task::release(); here before fixing DE1106 (xbox pure virtual crash) + // Release in turn causes the dependent tasks to start running + // The problem was that between the time release was called and by the time we got to the destructor + // The task chain would get all the way to scene finalization code which would reset the allocation pool + // And a new task would get allocated at the same address, then we would invoke the destructor on that freshly created task + // This could potentially cause any number of other problems, it is suprising that it only manifested itself + // as a pure virtual crash + PxBaseTask* saveContinuation = mCont; + this->~PxsCMUpdateTask(); + if (saveContinuation) + saveContinuation->removeReference(); +} + +class PxsCMDiscreteUpdateTask : public PxsCMUpdateTask +{ +public: + PxsCMDiscreteUpdateTask(PxsContext* context, PxReal dt, PxsContactManager** cms, PxsContactManagerOutput* cmOutputs, Gu::Cache* caches, PxU32 nbCms, + PxContactModifyCallback* callback): + PxsCMUpdateTask(context, dt, cms, cmOutputs, caches, nbCms, callback) + {} + + virtual ~PxsCMDiscreteUpdateTask() + {} + + void runModifiableContactManagers(PxU32* modifiableIndices, PxU32 nbModifiableManagers, PxcNpThreadContext& threadContext, PxU32& foundPatchCount_, PxU32& lostPatchCount_, + PxU32& maxPatches_) + { + PX_ASSERT(nbModifiableManagers != 0); + + PxU32 foundPatchCount = foundPatchCount_; + PxU32 lostPatchCount = lostPatchCount_; + PxU32 maxPatches = maxPatches_; + + Cm::BitMap& localPatchChangedMap = threadContext.getLocalPatchChangeMap(); + + class PxcContactSet: public PxContactSet + { + public: + PxcContactSet(PxU32 count, PxModifiableContact *contacts) + { + mContacts = contacts; + mCount = count; + } + PxModifiableContact* getContacts() { return mContacts; } + PxU32 getCount() { return mCount; } + + }; + + + + if(mCallback) + { + PX_ALLOCA(mModifiablePairArray, PxContactModifyPair, nbModifiableManagers); + + + PxsTransformCache& transformCache = mContext->getTransformCache(); + + for(PxU32 i = 0; i < nbModifiableManagers; ++i) + { + PxU32 index = modifiableIndices[i]; + PxsContactManager& cm = *mCmArray[index]; + + PxsContactManagerOutput& output = mCmOutputs[index]; + + PxU32 count = output.nbContacts; + + if(count) + { + PxContactModifyPair& p = mModifiablePairArray[i]; + PxcNpWorkUnit &unit = cm.getWorkUnit(); + + p.shape[0] = gPxvOffsetTable.convertPxsShape2Px(unit.shapeCore0); + p.shape[1] = gPxvOffsetTable.convertPxsShape2Px(unit.shapeCore1); + + p.actor[0] = unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY0 ? gPxvOffsetTable.convertPxsRigidCore2PxRigidBody(unit.rigidCore0) + : gPxvOffsetTable.convertPxsRigidCore2PxRigidStatic(unit.rigidCore0); + + p.actor[1] = unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1 ? gPxvOffsetTable.convertPxsRigidCore2PxRigidBody(unit.rigidCore1) + : gPxvOffsetTable.convertPxsRigidCore2PxRigidStatic(unit.rigidCore1); + + p.transform[0] = transformCache.getTransformCache(unit.mTransformCache0).transform; + p.transform[1] = transformCache.getTransformCache(unit.mTransformCache1).transform; + + PxModifiableContact* contacts = reinterpret_cast(output.contactPoints); + static_cast(p.contacts) = PxcContactSet(count, contacts); + + PxReal mi0 = unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY0 ? static_cast(unit.rigidCore0)->maxContactImpulse : PX_MAX_F32; + PxReal mi1 = unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1 ? static_cast(unit.rigidCore1)->maxContactImpulse : PX_MAX_F32; + PxReal maxImpulse = PxMin(mi0, mi1); + for (PxU32 j = 0; j < count; j++) + contacts[j].maxImpulse = maxImpulse; + + #if PX_ENABLE_SIM_STATS + PxU8 gt0 = Ps::to8(unit.geomType0), gt1 = Ps::to8(unit.geomType1); + threadContext.mModifiedContactPairs[PxMin(gt0, gt1)][PxMax(gt0, gt1)]++; + #endif + } + } + + mCallback->onContactModify(mModifiablePairArray, nbModifiableManagers); + } + + for(PxU32 i = 0; i < nbModifiableManagers; ++i) + { + PxU32 index = modifiableIndices[i]; + PxsContactManager& cm = *mCmArray[index]; + + //Loop through the contacts in the contact stream and update contact count! + + PxU32 numContacts = 0; + PxcNpWorkUnit& unit = cm.getWorkUnit(); + PxsContactManagerOutput& output = mCmOutputs[index]; + + + PxU32 numPatches = output.nbPatches; + + if (output.nbContacts) + { + //PxU8* compressedContacts = cm.getWorkUnit().compressedContacts; + //PxModifyContactHeader* header = reinterpret_cast(compressedContacts); + PxContactPatch* patches = reinterpret_cast(output.contactPatches); + PxModifiableContact* points = reinterpret_cast(output.contactPoints); + + if (patches->internalFlags & PxContactPatch::eREGENERATE_PATCHES) + { + //Some data was modified that must trigger patch re-generation... + for (PxU8 k = 0; k < numPatches; ++k) + { + PxU32 startIndex = patches[k].startContactIndex; + + patches[k].normal = points[startIndex].normal; + patches[k].dynamicFriction = points[startIndex].dynamicFriction; + patches[k].staticFriction = points[startIndex].staticFriction; + patches[k].restitution = points[startIndex].restitution; + + for (PxU32 j = 1; j < patches[k].nbContacts; ++j) + { + if (points[startIndex].normal.dot(points[j + startIndex].normal) < PXC_SAME_NORMAL + && points[startIndex].maxImpulse > 0.f) //TODO - this needs extending for material indices but we don't support modifying those yet + { + //The points are now in a separate friction patch... + for (PxU32 c = numPatches - 1; c > k; --c) + { + patches[c + 1] = patches[c]; + } + numPatches++; + patches[k + 1].materialFlags = patches[k].materialFlags; + patches[k + 1].internalFlags = patches[k].internalFlags; + patches[k + 1].startContactIndex = Ps::to8(j + startIndex); + patches[k + 1].nbContacts = Ps::to8(patches[k].nbContacts - j); + //Fill in patch information now that final patches are available + patches[k].nbContacts = PxU8(j); + break; + } + } + } + } + + if (output.prevPatches < numPatches) + { + foundPatchCount++; + localPatchChangedMap.growAndSet(unit.index); + } + + maxPatches = PxMax(maxPatches, PxU32(numPatches)); + + output.nbPatches = PxU8(numPatches); + + for (PxU32 a = 0; a < output.nbContacts; ++a) + { + numContacts += points[a].maxImpulse != 0.f; + } + } + + if(output.nbPatches < output.prevPatches) + { + lostPatchCount++; + //Trigger a lost patch event...required to let the solver + localPatchChangedMap.growAndSet(unit.index); + } + + if(!numContacts) + { + //KS - we still need to retain the patch count from the previous frame to detect found/lost events... + PxcNpWorkUnitClearFrictionCachedState(unit); + output.nbPatches = 0; + output.nbContacts = 0; + + if(output.prevPatches) + { + lostPatchCount++; + //Trigger a lost patch event...required to let the solver + localPatchChangedMap.growAndSet(unit.index); + } + + continue; + } + + if(threadContext.mContactStreamPool) + { + //We need to allocate a new structure inside the contact stream pool + + PxU32 patchSize = output.nbPatches * sizeof(PxContactPatch); + PxU32 contactSize = output.nbContacts * sizeof(PxExtendedContact); + + /*PxI32 increment = (PxI32)(patchSize + contactSize); + PxI32 index = Ps::atomicAdd(&mContactStreamPool->mSharedContactIndex, increment) - increment; + PxU8* address = mContactStreamPool->mContactStream + index;*/ + bool isOverflown = false; + + PxI32 contactIncrement = PxI32(contactSize); + PxI32 contactIndex = Ps::atomicAdd(&threadContext.mContactStreamPool->mSharedDataIndex, contactIncrement); + + if (threadContext.mContactStreamPool->isOverflown()) + { + PX_WARN_ONCE("Contact buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + + PxU8* contactAddress = threadContext.mContactStreamPool->mDataStream + threadContext.mContactStreamPool->mDataStreamSize - contactIndex; + + PxI32 patchIncrement = PxI32(patchSize); + PxI32 patchIndex = Ps::atomicAdd(&threadContext.mPatchStreamPool->mSharedDataIndex, patchIncrement); + + if (threadContext.mPatchStreamPool->isOverflown()) + { + PX_WARN_ONCE("Patch buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + + PxU8* patchAddress = threadContext.mPatchStreamPool->mDataStream + threadContext.mPatchStreamPool->mDataStreamSize - patchIndex; + + + PxU32 internalFlags = reinterpret_cast(output.contactPatches)->internalFlags; + + PxI32 increment2 = PxI32(output.nbContacts * sizeof(PxReal)); + PxI32 index2 = Ps::atomicAdd(&threadContext.mForceAndIndiceStreamPool->mSharedDataIndex, increment2); + + if (threadContext.mForceAndIndiceStreamPool->isOverflown()) + { + PX_WARN_ONCE("Force buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + + if (isOverflown) + { + output.contactPoints = NULL; + output.contactPatches = NULL; + output.contactForces = NULL; + + output.nbContacts = output.nbPatches = 0; + } + else + { + output.contactForces = reinterpret_cast(threadContext.mForceAndIndiceStreamPool->mDataStream + threadContext.mForceAndIndiceStreamPool->mDataStreamSize - index2); + + PxMemZero(output.contactForces, sizeof(PxReal) * output.nbContacts); + + PxExtendedContact* contacts = reinterpret_cast(contactAddress); + PxMemCopy(patchAddress, output.contactPatches, sizeof(PxContactPatch) * output.nbPatches); + + PxContactPatch* newPatches = reinterpret_cast(patchAddress); + + internalFlags |= PxContactPatch::eCOMPRESSED_MODIFIED_CONTACT; + + for(PxU32 a = 0; a < output.nbPatches; ++a) + { + newPatches[a].internalFlags = PxU8(internalFlags); + } + + //KS - only the first patch will have mass modification properties set. For the GPU solver, this must be propagated to the remaining patches + for(PxU32 a = 1; a < output.nbPatches; ++a) + { + newPatches[a].mMassModification = newPatches->mMassModification; + } + + PxModifiableContact* sourceContacts = reinterpret_cast(output.contactPoints); + + for(PxU32 a = 0; a < output.nbContacts; ++a) + { + PxExtendedContact& contact = contacts[a]; + PxModifiableContact& srcContact = sourceContacts[a]; + contact.contact = srcContact.contact; + contact.separation = srcContact.separation; + contact.targetVelocity = srcContact.targetVelocity; + contact.maxImpulse = srcContact.maxImpulse; + } + + output.contactPatches = patchAddress; + output.contactPoints = reinterpret_cast(contacts); + } + } + } + + foundPatchCount_ = foundPatchCount; + lostPatchCount_ = lostPatchCount; + maxPatches_ = maxPatches; + } + + + template < void (*NarrowPhase)(PxcNpThreadContext&, const PxcNpWorkUnit&, Gu::Cache&, PxsContactManagerOutput&)> + void processCms(PxcNpThreadContext* threadContext) + { + // PT: use local variables to avoid reading class members N times, if possible + const PxU32 nb = mCmCount; + PxsContactManager** PX_RESTRICT cmArray = mCmArray; + + PxU32 lostPatchCount = 0, foundPatchCount = 0; + + PxU32 maxPatches = threadContext->mMaxPatches; + + PxU32 newTouchCMCount = 0, lostTouchCMCount = 0; + Cm::BitMap& localChangeTouchCM = threadContext->getLocalChangeTouch(); + Cm::BitMap& localPatchChangedMap = threadContext->getLocalPatchChangeMap(); + + PX_ALLOCA(modifiableIndices, PxU32, nb); + PxU32 modifiableCount = 0; + + for(PxU32 i=0;igetWorkUnit().shapeCore0); + Ps::prefetchLine(cmArray[prefetch1]->getWorkUnit().shapeCore1); + Ps::prefetchLine(&threadContext->mTransformCache->getTransformCache(cmArray[prefetch1]->getWorkUnit().mTransformCache0)); + Ps::prefetchLine(&threadContext->mTransformCache->getTransformCache(cmArray[prefetch1]->getWorkUnit().mTransformCache1)); + + PxsContactManager* cm = cmArray[i]; + + if(cm) + { + PxsContactManagerOutput& output = mCmOutputs[i]; + PxcNpWorkUnit& unit = cm->getWorkUnit(); + + output.prevPatches = output.nbPatches; + + + PxU8 oldStatusFlag = output.statusFlag; + + PxU8 oldTouch = Ps::to8(oldStatusFlag & PxsContactManagerStatusFlag::eHAS_TOUCH); + + Gu::Cache& cache = mCaches[i]; + + NarrowPhase(*threadContext, unit, cache, output); + + PxU16 newTouch = Ps::to8(output.statusFlag & PxsContactManagerStatusFlag::eHAS_TOUCH); + + + bool modifiable = output.nbPatches != 0 && unit.flags & PxcNpWorkUnitFlag::eMODIFIABLE_CONTACT; + + if(modifiable) + { + modifiableIndices[modifiableCount++] = i; + } + else + { + maxPatches = PxMax(maxPatches, Ps::to32(output.nbPatches)); + + if(output.prevPatches != output.nbPatches) + { + localPatchChangedMap.growAndSet(cmArray[i]->getIndex()); + if(output.prevPatches < output.nbPatches) + foundPatchCount++; + else + lostPatchCount++; + } + } + + if (newTouch ^ oldTouch) + { + cm->getWorkUnit().statusFlags = PxU8(output.statusFlag | (unit.statusFlags & PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH)); //KS - todo - remove the need to access the work unit at all! + localChangeTouchCM.growAndSet(cmArray[i]->getIndex()); + if(newTouch) + newTouchCMCount++; + else + lostTouchCMCount++; + } + else if (!(oldStatusFlag&PxsContactManagerStatusFlag::eTOUCH_KNOWN)) + { + cm->getWorkUnit().statusFlags = PxU8(output.statusFlag | (unit.statusFlags & PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH)); //KS - todo - remove the need to access the work unit at all! + } + } + } + + if(modifiableCount) + { + runModifiableContactManagers(modifiableIndices, modifiableCount, *threadContext, foundPatchCount, lostPatchCount, maxPatches); + } + + + threadContext->addLocalNewTouchCount(newTouchCMCount); + threadContext->addLocalLostTouchCount(lostTouchCMCount); + + threadContext->addLocalFoundPatchCount(foundPatchCount); + threadContext->addLocalLostPatchCount(lostPatchCount); + + threadContext->mMaxPatches = maxPatches; + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("Sim.narrowPhase", mContext->getContextId()); + + PxcNpThreadContext* PX_RESTRICT threadContext = mContext->getNpThreadContext(); + + threadContext->mDt = mDt; + + const bool pcm = mContext->getPCM(); + threadContext->mPCM = pcm; + threadContext->mCreateAveragePoint = mContext->getCreateAveragePoint(); + threadContext->mContactCache = mContext->getContactCacheFlag(); + threadContext->mTransformCache = &mContext->getTransformCache(); + threadContext->mContactDistance = mContext->getContactDistance(); + + if(pcm) + { + processCms(threadContext); + } + else + { + processCms(threadContext); + } + + mContext->putNpThreadContext(threadContext); + } + + virtual const char* getName() const + { + return "PxsContext.contactManagerDiscreteUpdate"; + } +}; + +void PxsNphaseImplementationContext::processContactManager(PxReal dt, PxsContactManagerOutput* cmOutputs, PxBaseTask* continuation) +{ + //Iterate all active contact managers + mContext.mTaskPool.lock(); + const PxU32 nbCmsToProcess = mNarrowPhasePairs.mContactManagerMapping.size(); + + for(PxU32 a = 0; a < nbCmsToProcess;) + { + void* ptr = mContext.mTaskPool.allocateNotThreadSafe(sizeof(PxsCMDiscreteUpdateTask)); + PxU32 nbToProcess = PxMin(nbCmsToProcess - a, PxsCMUpdateTask::BATCH_SIZE); + PxsCMDiscreteUpdateTask* task = PX_PLACEMENT_NEW(ptr, PxsCMDiscreteUpdateTask)(&mContext, dt, mNarrowPhasePairs.mContactManagerMapping.begin() + a, + cmOutputs + a, mNarrowPhasePairs.mCaches.begin() + a, nbToProcess, mModifyCallback); + + a += nbToProcess; + + task->setContinuation(continuation); + task->removeReference(); + } + mContext.mTaskPool.unlock(); +} + +void PxsNphaseImplementationContext::processContactManagerSecondPass(PxReal dt, PxBaseTask* continuation) +{ + //Iterate all active contact managers + mContext.mTaskPool.lock(); + + const PxU32 nbCmsToProcess = mNewNarrowPhasePairs.mContactManagerMapping.size(); + + for(PxU32 a = 0; a < nbCmsToProcess;) + { + void* ptr = mContext.mTaskPool.allocateNotThreadSafe(sizeof(PxsCMDiscreteUpdateTask)); + PxU32 nbToProcess = PxMin(nbCmsToProcess - a, PxsCMUpdateTask::BATCH_SIZE); + PxsCMDiscreteUpdateTask* task = PX_PLACEMENT_NEW(ptr, PxsCMDiscreteUpdateTask)(&mContext, dt, mNewNarrowPhasePairs.mContactManagerMapping.begin() + a, + mNewNarrowPhasePairs.mOutputContactManagers.begin() + a, mNewNarrowPhasePairs.mCaches.begin() + a, nbToProcess, + mModifyCallback); + + a += nbToProcess; + + task->setContinuation(continuation); + task->removeReference(); + } + mContext.mTaskPool.unlock(); +} + +void PxsNphaseImplementationContext::updateContactManager(PxReal dt, bool /*hasBoundsArrayChanged*/, bool /*hasContactDistanceChanged*/, PxBaseTask* continuation, PxBaseTask* firstPassNpContinuation) +{ + PX_PROFILE_ZONE("Sim.queueNarrowPhase", mContext.mContextID); + + firstPassNpContinuation->removeReference(); + + mContext.clearManagerTouchEvents(); + +#if PX_ENABLE_SIM_STATS + mContext.mSimStats.mNbDiscreteContactPairsTotal = 0; + mContext.mSimStats.mNbDiscreteContactPairsWithCacheHits = 0; + mContext.mSimStats.mNbDiscreteContactPairsWithContacts = 0; +#endif + + + //KS - temporarily put this here. TODO - move somewhere better + mContext.mTotalCompressedCacheSize = 0; + mContext.mMaxPatches = 0; + + processContactManager(dt, mNarrowPhasePairs.mOutputContactManagers.begin(), continuation); + +} + +void PxsNphaseImplementationContext::secondPassUpdateContactManager(PxReal dt, PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.queueNarrowPhase", mContext.mContextID); + + processContactManagerSecondPass(dt, continuation); +} + +PxsNphaseImplementationContext* PxsNphaseImplementationContext::create(PxsContext& context, IG::IslandSim* islandSim) +{ + PxsNphaseImplementationContext* npImplContext = reinterpret_cast( + PX_ALLOC(sizeof(PxsNphaseImplementationContext), "PxsNphaseImplementationContext")); + + if (npImplContext) + { + new(npImplContext) PxsNphaseImplementationContext(context, islandSim); + } + + return npImplContext; +} + +void PxsNphaseImplementationContext::destroy() +{ + this->~PxsNphaseImplementationContext(); + PX_FREE(this); +} + +void PxsNphaseImplementationContext::registerContactManagers(PxsContactManager** cms, PxU32 nbContactManagers, PxU32 maxContactManagerId) +{ + PX_UNUSED(maxContactManagerId); + for (PxU32 a = 0; a < nbContactManagers; ++a) + { + registerContactManager(cms[a], 0, 0); + } +} + +void PxsNphaseImplementationContext::registerContactManager(PxsContactManager* cm, PxI32 touching, PxU32 patchCount) +{ + PxcNpWorkUnit& workUnit = cm->getWorkUnit(); + PxsContactManagerOutput output; + + PxU8 geomType0 = PxU8(workUnit.geomType0); + PxU8 geomType1 = PxU8(workUnit.geomType1); + + Gu::Cache cache; + + mContext.createCache(cache, cm, geomType0, geomType1); + + PxMemZero(&output, sizeof(output)); + output.nbPatches = Ps::to8(patchCount); + + if(workUnit.flags & PxcNpWorkUnitFlag::eOUTPUT_CONSTRAINTS) + output.statusFlag |= PxsContactManagerStatusFlag::eREQUEST_CONSTRAINTS; + + if (touching > 0) + { + output.statusFlag |= PxsContactManagerStatusFlag::eHAS_TOUCH; + } + else if (touching < 0) + { + output.statusFlag |= PxsContactManagerStatusFlag::eHAS_NO_TOUCH; + } + + output.statusFlag |= PxsContactManagerStatusFlag::eDIRTY_MANAGER; + + if (cm->getWorkUnit().statusFlags & PxcNpWorkUnitStatusFlag::eHAS_TOUCH) + cm->getWorkUnit().statusFlags |= PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH; + + mNewNarrowPhasePairs.mOutputContactManagers.pushBack(output); + mNewNarrowPhasePairs.mCaches.pushBack(cache); + mNewNarrowPhasePairs.mContactManagerMapping.pushBack(cm); + PxU32 newSz = mNewNarrowPhasePairs.mOutputContactManagers.size(); + cm->getWorkUnit().mNpIndex = mNewNarrowPhasePairs.computeId(newSz - 1) | PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK; +} + +void PxsNphaseImplementationContext::removeContactManagersFallback(PxsContactManagerOutput* cmOutputs) +{ + if (mRemovedContactManagers.size()) + { + Ps::sort(mRemovedContactManagers.begin(), mRemovedContactManagers.size(), Ps::Greater()); + + for (PxU32 a = 0; a < mRemovedContactManagers.size(); ++a) + { +#if PX_DEBUG + if (a > 0) + PX_ASSERT(mRemovedContactManagers[a] < mRemovedContactManagers[a - 1]); +#endif + unregisterContactManagerInternal(mRemovedContactManagers[a], mNarrowPhasePairs, cmOutputs); + } + + mRemovedContactManagers.forceSize_Unsafe(0); + } +} + +void PxsNphaseImplementationContext::unregisterContactManager(PxsContactManager* cm) +{ + PxcNpWorkUnit& unit = cm->getWorkUnit(); + PxU32 index = unit.mNpIndex; + PX_ASSERT(index != 0xFFffFFff); + + if (!(index & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK)) + { + unregisterContactManagerInternal(index, mNarrowPhasePairs, mNarrowPhasePairs.mOutputContactManagers.begin()); + mNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNarrowPhasePairs.mOutputContactManagers.size()-1); + } + else + { + //KS - the index in the "new" list will be the index + unregisterContactManagerInternal(index, mNewNarrowPhasePairs, mNewNarrowPhasePairs.mOutputContactManagers.begin()); + mNewNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNewNarrowPhasePairs.mOutputContactManagers.size()-1); + } +} + +void PxsNphaseImplementationContext::refreshContactManager(PxsContactManager* cm) +{ + PxcNpWorkUnit& unit = cm->getWorkUnit(); + PxU32 index = unit.mNpIndex; + PX_ASSERT(index != 0xFFffFFff); + PxsContactManagerOutput output; + if (!(index & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK)) + { + output = mNarrowPhasePairs.mOutputContactManagers[PxsContactManagerBase::computeIndexFromId(index)]; + unregisterContactManagerInternal(index, mNarrowPhasePairs, mNarrowPhasePairs.mOutputContactManagers.begin()); + mNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNarrowPhasePairs.mOutputContactManagers.size()-1); + } + else + { + output = mNewNarrowPhasePairs.mOutputContactManagers[PxsContactManagerBase::computeIndexFromId(index & (~PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK))]; + //KS - the index in the "new" list will be the index + unregisterContactManagerInternal(index, mNewNarrowPhasePairs, mNewNarrowPhasePairs.mOutputContactManagers.begin()); + mNewNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNewNarrowPhasePairs.mOutputContactManagers.size()-1); + } + PxI32 touching = 0; + if(output.statusFlag & PxsContactManagerStatusFlag::eHAS_TOUCH) + touching = 1; + else if (output.statusFlag & PxsContactManagerStatusFlag::eHAS_NO_TOUCH) + touching = -1; + registerContactManager(cm, touching, output.nbPatches); +} + +void PxsNphaseImplementationContext::unregisterContactManagerFallback(PxsContactManager* cm, PxsContactManagerOutput* /*cmOutputs*/) +{ + PxcNpWorkUnit& unit = cm->getWorkUnit(); + PxU32 index = unit.mNpIndex; + PX_ASSERT(index != 0xFFffFFff); + + if (!(index & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK)) + { + mRemovedContactManagers.pushBack(index); + } + else + { + //KS - the index in the "new" list will be the index + unregisterContactManagerInternal(index, mNewNarrowPhasePairs, mNewNarrowPhasePairs.mOutputContactManagers.begin()); + mNewNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNewNarrowPhasePairs.mOutputContactManagers.size()-1); + } +} + +void PxsNphaseImplementationContext::refreshContactManagerFallback(PxsContactManager* cm, PxsContactManagerOutput* cmOutputs) +{ + PxcNpWorkUnit& unit = cm->getWorkUnit(); + PxU32 index = unit.mNpIndex; + PX_ASSERT(index != 0xFFffFFff); + + PxsContactManagerOutput output; + if (!(index & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK)) + { + output = cmOutputs[PxsContactManagerBase::computeIndexFromId(index)]; + //unregisterContactManagerInternal(index, mNarrowPhasePairs, cmOutputs); + unregisterContactManagerFallback(cm, cmOutputs); + } + else + { + //KS - the index in the "new" list will be the index + output = mNewNarrowPhasePairs.mOutputContactManagers[PxsContactManagerBase::computeIndexFromId(index & (~PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK))]; + unregisterContactManagerInternal(index, mNewNarrowPhasePairs, mNewNarrowPhasePairs.mOutputContactManagers.begin()); + mNewNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNewNarrowPhasePairs.mOutputContactManagers.size()-1); + } + + PxI32 touching = 0; + if(output.statusFlag & PxsContactManagerStatusFlag::eHAS_TOUCH) + { + touching = 1; + cm->getWorkUnit().statusFlags |= PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH; + } + else if (output.statusFlag & PxsContactManagerStatusFlag::eHAS_NO_TOUCH) + touching = -1; + registerContactManager(cm, touching, output.nbPatches); + + +} + +void PxsNphaseImplementationContext::appendContactManagers() +{ + //Copy new pairs to end of old pairs. Clear new flag, update npIndex on CM and clear the new pair buffer + const PxU32 existingSize = mNarrowPhasePairs.mContactManagerMapping.size(); + const PxU32 nbToAdd = mNewNarrowPhasePairs.mContactManagerMapping.size(); + const PxU32 newSize =existingSize + nbToAdd; + + if(newSize > mNarrowPhasePairs.mContactManagerMapping.capacity()) + { + PxU32 newSz = PxMax(256u, PxMax(mNarrowPhasePairs.mContactManagerMapping.capacity()*2, newSize)); + + mNarrowPhasePairs.mContactManagerMapping.reserve(newSz); + mNarrowPhasePairs.mOutputContactManagers.reserve(newSz); + mNarrowPhasePairs.mCaches.reserve(newSz); + } + + mNarrowPhasePairs.mContactManagerMapping.forceSize_Unsafe(newSize); + mNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(newSize); + mNarrowPhasePairs.mCaches.forceSize_Unsafe(newSize); + + PxMemCopy(mNarrowPhasePairs.mContactManagerMapping.begin() + existingSize, mNewNarrowPhasePairs.mContactManagerMapping.begin(), sizeof(PxsContactManager*)*nbToAdd); + PxMemCopy(mNarrowPhasePairs.mOutputContactManagers.begin() + existingSize, mNewNarrowPhasePairs.mOutputContactManagers.begin(), sizeof(PxsContactManagerOutput)*nbToAdd); + PxMemCopy(mNarrowPhasePairs.mCaches.begin() + existingSize, mNewNarrowPhasePairs.mCaches.begin(), sizeof(Gu::Cache)*nbToAdd); + + PxU32* edgeNodeIndices = mIslandSim->getEdgeNodeIndexPtr(); + + for(PxU32 a = 0; a < mNewNarrowPhasePairs.mContactManagerMapping.size(); ++a) + { + PxsContactManager* cm = mNewNarrowPhasePairs.mContactManagerMapping[a]; + PxcNpWorkUnit& unit = cm->getWorkUnit(); + unit.mNpIndex = mNarrowPhasePairs.computeId(existingSize + a); + + if(unit.statusFlags & PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH) + { + unit.statusFlags &= (~PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH); + if(!(unit.flags & PxcNpWorkUnitFlag::eDISABLE_RESPONSE)) + { + PartitionEdge* partitionEdge = mIslandSim->getFirstPartitionEdge(unit.mEdgeIndex); + + while(partitionEdge) + { + edgeNodeIndices[partitionEdge->mUniqueIndex] = unit.mNpIndex; + partitionEdge = partitionEdge->mNextPatch; + } + } + } + } + + mNewNarrowPhasePairs.clear(); +} + +void PxsNphaseImplementationContext::appendContactManagersFallback(PxsContactManagerOutput* cmOutputs) +{ + PX_PROFILE_ZONE("PxsNphaseImplementationContext.appendContactManagersFallback", mContext.mContextID); + + //Copy new pairs to end of old pairs. Clear new flag, update npIndex on CM and clear the new pair buffer + const PxU32 existingSize = mNarrowPhasePairs.mContactManagerMapping.size(); + const PxU32 nbToAdd = mNewNarrowPhasePairs.mContactManagerMapping.size(); + const PxU32 newSize =existingSize + nbToAdd; + + if(newSize > mNarrowPhasePairs.mContactManagerMapping.capacity()) + { + PxU32 newSz = PxMax(mNarrowPhasePairs.mContactManagerMapping.capacity()*2, newSize); + + mNarrowPhasePairs.mContactManagerMapping.reserve(newSz); + mNarrowPhasePairs.mCaches.reserve(newSz); + /*mNarrowPhasePairs.mLostFoundPairsCms.reserve(2 * newSz); + mNarrowPhasePairs.mLostFoundPairsOutputData.reserve(2*newSz);*/ + } + + mNarrowPhasePairs.mContactManagerMapping.forceSize_Unsafe(newSize); + mNarrowPhasePairs.mCaches.forceSize_Unsafe(newSize); + + PxMemCopy(mNarrowPhasePairs.mContactManagerMapping.begin() + existingSize, mNewNarrowPhasePairs.mContactManagerMapping.begin(), sizeof(PxsContactManager*)*nbToAdd); + PxMemCopy(cmOutputs + existingSize, mNewNarrowPhasePairs.mOutputContactManagers.begin(), sizeof(PxsContactManagerOutput)*nbToAdd); + PxMemCopy(mNarrowPhasePairs.mCaches.begin() + existingSize, mNewNarrowPhasePairs.mCaches.begin(), sizeof(Gu::Cache)*nbToAdd); + + PxU32* edgeNodeIndices = mIslandSim->getEdgeNodeIndexPtr(); + + for(PxU32 a = 0; a < mNewNarrowPhasePairs.mContactManagerMapping.size(); ++a) + { + PxsContactManager* cm = mNewNarrowPhasePairs.mContactManagerMapping[a]; + PxcNpWorkUnit& unit = cm->getWorkUnit(); + unit.mNpIndex = mNarrowPhasePairs.computeId(existingSize + a); + + if(unit.statusFlags & PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH) + { + unit.statusFlags &= (~PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH); + if(!(unit.flags & PxcNpWorkUnitFlag::eDISABLE_RESPONSE)) + { + PartitionEdge* partitionEdge = mIslandSim->getFirstPartitionEdge(unit.mEdgeIndex); + + while(partitionEdge) + { + edgeNodeIndices[partitionEdge->mUniqueIndex] = unit.mNpIndex; + partitionEdge = partitionEdge->mNextPatch; + } + } + } + } + + //const PxU32 existingLostFoundPairs = mNarrowPhasePairs.mLostFoundPairsCms.size(); + //const PxU32 newLostFoundPairs = mNewNarrowPhasePairs.mLostFoundPairsCms.size(); + //const PxU32 newLostFoundSize = existingLostFoundPairs + newLostFoundPairs; + + //if (mNarrowPhasePairs.mLostFoundPairsCms.capacity() < newLostFoundSize) + //{ + // const PxU32 newSz = PxMax(newLostFoundSize, 2 * mNarrowPhasePairs.mLostFoundPairsCms.capacity()); + // mNarrowPhasePairs.mLostFoundPairsCms.reserve(newSz); + // mNarrowPhasePairs.mLostFoundPairsOutputData.reserve(newSz); + //} + + //mNarrowPhasePairs.mLostFoundPairsCms.forceSize_Unsafe(newLostFoundSize); + //mNarrowPhasePairs.mLostFoundPairsOutputData.forceSize_Unsafe(newLostFoundSize); + + //PxMemCopy(mNarrowPhasePairs.mLostFoundPairsCms.begin() + existingLostFoundPairs, mNewNarrowPhasePairs.mLostFoundPairsCms.begin(), sizeof(PxsContactManager*)* newLostFoundPairs); + //PxMemCopy(mNarrowPhasePairs.mLostFoundPairsOutputData.begin() + existingLostFoundPairs, mNewNarrowPhasePairs.mLostFoundPairsOutputData.begin(), sizeof(PxsContactManagerOutput) * newLostFoundPairs); + + mNewNarrowPhasePairs.clear(); +} + + + +void PxsNphaseImplementationContext::unregisterContactManagerInternal(PxU32 npIndex, PxsContactManagers& managers, PxsContactManagerOutput* cmOutputs) +{ + //TODO - remove this element from the list. + PxU32 index = PxsContactManagerBase::computeIndexFromId((npIndex & (~PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK))); + + //Now we replace-with-last and remove the elements... + + PxU32 replaceIndex = managers.mContactManagerMapping.size()-1; + + PxsContactManager* replaceManager = managers.mContactManagerMapping[replaceIndex]; + + mContext.destroyCache(managers.mCaches[index]); + + managers.mContactManagerMapping[index] = replaceManager; + managers.mCaches[index] = managers.mCaches[replaceIndex]; + cmOutputs[index] = cmOutputs[replaceIndex]; + + PxU32* edgeNodeIndices = mIslandSim->getEdgeNodeIndexPtr(); + + PxcNpWorkUnit& replaceUnit = replaceManager->getWorkUnit(); + replaceUnit.mNpIndex = npIndex; + if(replaceUnit.statusFlags & PxcNpWorkUnitStatusFlag::eHAS_TOUCH) + { + if(!(replaceUnit.flags & PxcNpWorkUnitFlag::eDISABLE_RESPONSE)) + { + PartitionEdge* partitionEdge = mIslandSim->getFirstPartitionEdge(replaceUnit.mEdgeIndex); + while(partitionEdge) + { + edgeNodeIndices[partitionEdge->mUniqueIndex] = replaceUnit.mNpIndex; + partitionEdge = partitionEdge->mNextPatch; + } + } + } + + managers.mContactManagerMapping.forceSize_Unsafe(replaceIndex); + managers.mCaches.forceSize_Unsafe(replaceIndex); +} + +PxsContactManagerOutput& PxsNphaseImplementationContext::getNewContactManagerOutput(PxU32 npId) +{ + PX_ASSERT(npId & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK); + return this->mNewNarrowPhasePairs.mOutputContactManagers[PxsContactManagerBase::computeIndexFromId(npId & (~PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK))]; +} + +void PxsNphaseImplementationContext::registerShape(const PxsShapeCore& shapeCore) +{ + PX_UNUSED(shapeCore); +} + +void PxsNphaseImplementationContext::updateShapeMaterial(const PxsShapeCore& shapeCore) +{ + PX_UNUSED(shapeCore); +} + +void PxsNphaseImplementationContext::updateShapeContactOffset(const PxsShapeCore& shapeCore) +{ + PX_UNUSED(shapeCore); +} + +void PxsNphaseImplementationContext::unregisterShape(const PxsShapeCore& shapeCore) +{ + PX_UNUSED(shapeCore); +} + +void PxsNphaseImplementationContext::registerMaterial(const PxsMaterialCore& materialCore) +{ + PX_UNUSED(materialCore); +} + +void PxsNphaseImplementationContext::updateMaterial(const PxsMaterialCore& materialCore) +{ + PX_UNUSED(materialCore); +} + +void PxsNphaseImplementationContext::unregisterMaterial(const PxsMaterialCore& materialCore) +{ + PX_UNUSED(materialCore); +} + +PxsContactManagerOutputIterator PxsNphaseImplementationContext::getContactManagerOutputs() +{ + PxU32 offsets[1] = {0}; + return PxsContactManagerOutputIterator(offsets, 1, this->mNarrowPhasePairs.mOutputContactManagers.begin()); +} + + +PxvNphaseImplementationContextUsableAsFallback* physx::createNphaseImplementationContext(PxsContext& context, IG::IslandSim* islandSim) +{ + return PxsNphaseImplementationContext::create(context, islandSim); +} + diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp new file mode 100644 index 000000000..728e6910b --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp @@ -0,0 +1,377 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "PxsSimpleIslandManager.h" +#include "PsSort.h" +#include "PxsContactManager.h" +#include "CmTask.h" +#include "DyVArticulation.h" + + +#define IG_SANITY_CHECKS 0 + +namespace physx +{ +namespace IG +{ + ThirdPassTask::ThirdPassTask(PxU64 contextID, SimpleIslandManager& islandManager, IslandSim& islandSim) : Cm::Task(contextID), mIslandManager(islandManager), mIslandSim(islandSim) + { + } + + PostThirdPassTask::PostThirdPassTask(PxU64 contextID, SimpleIslandManager& islandManager) : Cm::Task(contextID), mIslandManager(islandManager) + { + } + + SimpleIslandManager::SimpleIslandManager(bool useEnhancedDeterminism, PxU64 contextID) : + mDestroyedNodes (PX_DEBUG_EXP("mDestroyedNodes")), + mDestroyedEdges (PX_DEBUG_EXP("mDestroyedEdges")), + mFirstPartitionEdges (PX_DEBUG_EXP("mFirstPartitionEdges")), + mDestroyedPartitionEdges (PX_DEBUG_EXP("IslandSim::mDestroyedPartitionEdges")), + mIslandManager (&mFirstPartitionEdges, mEdgeNodeIndices, &mDestroyedPartitionEdges, contextID), + mSpeculativeIslandManager (NULL, mEdgeNodeIndices, NULL, contextID), + mSpeculativeThirdPassTask (contextID, *this, mSpeculativeIslandManager), + mAccurateThirdPassTask (contextID, *this, mIslandManager), + mPostThirdPassTask (contextID, *this), + mContextID (contextID) +{ + mFirstPartitionEdges.resize(1024); + mMaxDirtyNodesPerFrame = useEnhancedDeterminism ? 0xFFFFFFFF : 1000u; +} + +SimpleIslandManager::~SimpleIslandManager() +{ +} + +NodeIndex SimpleIslandManager::addRigidBody(PxsRigidBody* body, bool isKinematic, bool isActive) +{ + PxU32 handle = mNodeHandles.getHandle(); + NodeIndex nodeIndex(handle); + mIslandManager.addRigidBody(body, isKinematic, isActive, nodeIndex); + mSpeculativeIslandManager.addRigidBody(body, isKinematic, isActive, nodeIndex); + return nodeIndex; +} + +void SimpleIslandManager::removeNode(const NodeIndex index) +{ + PX_ASSERT(mNodeHandles.isValidHandle(index.index())); + mDestroyedNodes.pushBack(index); +} + +NodeIndex SimpleIslandManager::addArticulation(Sc::ArticulationSim* articulation, Dy::ArticulationV* llArtic, bool isActive) +{ + PxU32 handle = mNodeHandles.getHandle(); + NodeIndex nodeIndex(handle); + mIslandManager.addArticulation(articulation, llArtic, isActive, nodeIndex); + mSpeculativeIslandManager.addArticulation(articulation, llArtic, isActive, nodeIndex); + return nodeIndex; +} + +EdgeIndex SimpleIslandManager::addContactManager(PxsContactManager* manager, NodeIndex nodeHandle1, NodeIndex nodeHandle2, Sc::Interaction* interaction) +{ + EdgeIndex handle = mEdgeHandles.getHandle(); + + PxU32 nodeIds = 2 * handle; + if (mEdgeNodeIndices.size() == nodeIds) + { + PX_PROFILE_ZONE("ReserveEdges", getContextId()); + const PxU32 newSize = nodeIds + 2048; + mEdgeNodeIndices.resize(newSize); + mConstraintOrCm.resize(newSize); + mInteractions.resize(newSize); + } + + mEdgeNodeIndices[nodeIds] = nodeHandle1; + mEdgeNodeIndices[nodeIds+1] = nodeHandle2; + mConstraintOrCm[handle] = manager; + mInteractions[handle] = interaction; + + mSpeculativeIslandManager.addContactManager(manager, nodeHandle1, nodeHandle2, handle); + + if (manager) + manager->getWorkUnit().mEdgeIndex = handle; + + if(mConnectedMap.size() == handle) + { + mConnectedMap.resize(2 * (handle + 1)); + } + if (mFirstPartitionEdges.capacity() == handle) + { + mFirstPartitionEdges.resize(2 * (handle + 1)); + } + mConnectedMap.reset(handle); + return handle; +} + +EdgeIndex SimpleIslandManager::addConstraint(Dy::Constraint* constraint, NodeIndex nodeHandle1, NodeIndex nodeHandle2, Sc::Interaction* interaction) +{ + EdgeIndex handle = mEdgeHandles.getHandle(); + + PxU32 nodeIds = 2 * handle; + if (mEdgeNodeIndices.size() == nodeIds) + { + mEdgeNodeIndices.resize(2 * (mEdgeNodeIndices.size() + 2)); + mConstraintOrCm.resize(2 * (handle + 1)); + mInteractions.resize(2 * (handle + 1)); + } + + mEdgeNodeIndices[nodeIds] = nodeHandle1; + mEdgeNodeIndices[nodeIds + 1] = nodeHandle2; + + mConstraintOrCm[handle] = constraint; + + mInteractions[handle] = interaction; + + mIslandManager.addConstraint(constraint, nodeHandle1, nodeHandle2, handle); + mSpeculativeIslandManager.addConstraint(constraint, nodeHandle1, nodeHandle2, handle); + if(mConnectedMap.size() == handle) + { + mConnectedMap.resize(2*(mConnectedMap.size()+1)); + } + + if (mFirstPartitionEdges.capacity() == handle) + { + mFirstPartitionEdges.resize(2 * (mFirstPartitionEdges.capacity() + 1)); + } + mConnectedMap.set(handle); + return handle; +} + +void SimpleIslandManager::activateNode(NodeIndex index) +{ + mIslandManager.activateNode(index); + mSpeculativeIslandManager.activateNode(index); +} + +void SimpleIslandManager::deactivateNode(NodeIndex index) +{ + mIslandManager.deactivateNode(index); + mSpeculativeIslandManager.deactivateNode(index); +} + +void SimpleIslandManager::putNodeToSleep(NodeIndex index) +{ + mIslandManager.putNodeToSleep(index); + mSpeculativeIslandManager.putNodeToSleep(index); +} + +void SimpleIslandManager::removeConnection(EdgeIndex edgeIndex) +{ + if(edgeIndex == IG_INVALID_EDGE) + return; + mDestroyedEdges.pushBack(edgeIndex); + mSpeculativeIslandManager.removeConnection(edgeIndex); + if(mConnectedMap.test(edgeIndex)) + { + mIslandManager.removeConnection(edgeIndex); + mConnectedMap.reset(edgeIndex); + } + + mConstraintOrCm[edgeIndex] = NULL; + mInteractions[edgeIndex] = NULL; +} + +void SimpleIslandManager::firstPassIslandGen() +{ + PX_PROFILE_ZONE("Basic.firstPassIslandGen", getContextId()); + mSpeculativeIslandManager.clearDeactivations(); + mSpeculativeIslandManager.wakeIslands(); + mSpeculativeIslandManager.processNewEdges(); + mSpeculativeIslandManager.removeDestroyedEdges(); + mSpeculativeIslandManager.processLostEdges(mDestroyedNodes, false, false, mMaxDirtyNodesPerFrame); +} + +void SimpleIslandManager::additionalSpeculativeActivation() +{ + mSpeculativeIslandManager.wakeIslands2(); +} + +void SimpleIslandManager::secondPassIslandGen() +{ + PX_PROFILE_ZONE("Basic.secondPassIslandGen", getContextId()); + + mIslandManager.wakeIslands(); + mIslandManager.processNewEdges(); + + mIslandManager.removeDestroyedEdges(); + mIslandManager.processLostEdges(mDestroyedNodes, false, false, mMaxDirtyNodesPerFrame); + + for(PxU32 a = 0; a < mDestroyedNodes.size(); ++a) + { + mNodeHandles.freeHandle(mDestroyedNodes[a].index()); + } + mDestroyedNodes.clear(); + //mDestroyedEdges.clear(); +} + +bool SimpleIslandManager::validateDeactivations() const +{ + //This method sanity checks the deactivations produced by third-pass island gen. Specifically, it ensures that any bodies that + //the speculative IG wants to deactivate are also candidates for deactivation in the accurate island gen. In practice, both should be the case. If this fails, something went wrong... + + const NodeIndex* const nodeIndices = mSpeculativeIslandManager.getNodesToDeactivate(Node::eRIGID_BODY_TYPE); + const PxU32 nbNodesToDeactivate = mSpeculativeIslandManager.getNbNodesToDeactivate(Node::eRIGID_BODY_TYPE); + + for(PxU32 i = 0; i < nbNodesToDeactivate; ++i) + { + //Node is active in accurate sim => mismatch between accurate and inaccurate sim! + const Node& node = mIslandManager.getNode(nodeIndices[i]); + const Node& speculativeNode = mSpeculativeIslandManager.getNode(nodeIndices[i]); + //KS - we need to verify that the bodies in the "deactivating" list are still candidates for deactivation. There are cases where they may not no longer be candidates, e.g. if the application + //put bodies to sleep and activated them + if(node.isActive() && !speculativeNode.isActive()) + return false; + } + return true; +} + +void ThirdPassTask::runInternal() +{ + PX_PROFILE_ZONE("Basic.thirdPassIslandGen", mIslandSim.getContextId()); + mIslandSim.removeDestroyedEdges(); + mIslandSim.processLostEdges(mIslandManager.mDestroyedNodes, true, true, mIslandManager.mMaxDirtyNodesPerFrame); +} + +void PostThirdPassTask::runInternal() +{ + for (PxU32 a = 0; a < mIslandManager.mDestroyedNodes.size(); ++a) + { + mIslandManager.mNodeHandles.freeHandle(mIslandManager.mDestroyedNodes[a].index()); + } + mIslandManager.mDestroyedNodes.clear(); + + for (PxU32 a = 0; a < mIslandManager.mDestroyedEdges.size(); ++a) + { + mIslandManager.mEdgeHandles.freeHandle(mIslandManager.mDestroyedEdges[a]); + } + mIslandManager.mDestroyedEdges.clear(); + + PX_ASSERT(mIslandManager.validateDeactivations()); +} + +void SimpleIslandManager::thirdPassIslandGen(PxBaseTask* continuation) +{ + + mIslandManager.clearDeactivations(); + + mPostThirdPassTask.setContinuation(continuation); + + mSpeculativeThirdPassTask.setContinuation(&mPostThirdPassTask); + mAccurateThirdPassTask.setContinuation(&mPostThirdPassTask); + + mSpeculativeThirdPassTask.removeReference(); + mAccurateThirdPassTask.removeReference(); + + mPostThirdPassTask.removeReference(); + + //PX_PROFILE_ZONE("Basic.thirdPassIslandGen", getContextId()); + //mSpeculativeIslandManager.removeDestroyedEdges(); + //mSpeculativeIslandManager.processLostEdges(mDestroyedNodes, true, true); + + //mIslandManager.removeDestroyedEdges(); + //mIslandManager.processLostEdges(mDestroyedNodes, true, true); + + +} + +bool SimpleIslandManager::checkInternalConsistency() +{ + return mIslandManager.checkInternalConsistency() && mSpeculativeIslandManager.checkInternalConsistency(); +} + +void SimpleIslandManager::clearDestroyedEdges() +{ + mDestroyedPartitionEdges.forceSize_Unsafe(0); +} + +void SimpleIslandManager::setEdgeConnected(EdgeIndex edgeIndex) +{ + if(!mConnectedMap.test(edgeIndex)) + { + mIslandManager.addContactManager(reinterpret_cast(mConstraintOrCm[edgeIndex]), mEdgeNodeIndices[edgeIndex * 2], mEdgeNodeIndices[edgeIndex * 2 + 1], edgeIndex); + mConnectedMap.set(edgeIndex); + } +} + +bool SimpleIslandManager::getIsEdgeConnected(EdgeIndex edgeIndex) +{ + return !!mConnectedMap.test(edgeIndex); +} + +void SimpleIslandManager::deactivateEdge(const EdgeIndex edgeIndex) +{ + if (mFirstPartitionEdges[edgeIndex]) + { + mDestroyedPartitionEdges.pushBack(mFirstPartitionEdges[edgeIndex]); + mFirstPartitionEdges[edgeIndex] = NULL; + } +} + +void SimpleIslandManager::setEdgeDisconnected(EdgeIndex edgeIndex) +{ + if(mConnectedMap.test(edgeIndex)) + { + //PX_ASSERT(!mIslandManager.getEdge(edgeIndex).isInDirtyList()); + mIslandManager.removeConnection(edgeIndex); + mConnectedMap.reset(edgeIndex); + } +} + +void SimpleIslandManager::setEdgeRigidCM(const EdgeIndex edgeIndex, PxsContactManager* cm) +{ + mConstraintOrCm[edgeIndex] = cm; + cm->getWorkUnit().mEdgeIndex = edgeIndex; +} + +void SimpleIslandManager::clearEdgeRigidCM(const EdgeIndex edgeIndex) +{ + mConstraintOrCm[edgeIndex] = NULL; + if (mFirstPartitionEdges[edgeIndex]) + { + //this is the partition edges created/updated by the gpu solver + mDestroyedPartitionEdges.pushBack(mFirstPartitionEdges[edgeIndex]); + mFirstPartitionEdges[edgeIndex] = NULL; + } +} + +void SimpleIslandManager::setKinematic(IG::NodeIndex nodeIndex) +{ + mIslandManager.setKinematic(nodeIndex); + mSpeculativeIslandManager.setKinematic(nodeIndex); +} + +void SimpleIslandManager::setDynamic(IG::NodeIndex nodeIndex) +{ + mIslandManager.setDynamic(nodeIndex); + mSpeculativeIslandManager.setDynamic(nodeIndex); +} + +} +} + diff --git a/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h b/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h new file mode 100644 index 000000000..3c54c0f3d --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h @@ -0,0 +1,610 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_AABBMANAGER_H +#define BP_AABBMANAGER_H + + #include "PxvConfig.h" + #include "CmPhysXCommon.h" + #include "BpBroadPhaseUpdate.h" + #include "GuGeometryUnion.h" + #include "CmBitMap.h" + #include "CmTask.h" + #include "PsAllocator.h" + #include "GuBounds.h" + #include "PsHashMap.h" + #include "CmRadixSortBuffered.h" + #include "PsFoundation.h" + #include "BpAABBManagerTasks.h" + #include "PsHashSet.h" + #include "PxFiltering.h" + #include "PsSList.h" + + namespace physx + { + class PxcScratchAllocator; + struct PxBroadPhaseType; + + namespace Cm + { + class RenderOutput; + class EventProfiler; + class FlushPool; + } + + namespace Bp + { + typedef PxU32 BoundsIndex; + typedef PxU32 AggregateHandle; // PT: currently an index in mAggregates array + typedef PxU32 ActorHandle; + + struct BroadPhasePair; + + struct ElementType + { + enum Enum + { + eSHAPE = 0, + eTRIGGER, + + eCOUNT + }; + }; + PX_COMPILE_TIME_ASSERT(ElementType::eCOUNT <= 4); // 2 bits reserved for type + + /** + \brief Changes to the configuration of overlap pairs are reported as void* pairs. + \note Each void* in the pair corresponds to the void* passed to AABBManager::createVolume. + @see AABBManager::createVolume, AABBManager::getCreatedOverlaps, AABBManager::getDestroyedOverlaps + */ + struct AABBOverlap + { + PX_FORCE_INLINE AABBOverlap() {} + PX_FORCE_INLINE AABBOverlap(void* userData0, void* userData1/*, ActorHandle pairHandle*/) : mUserData0(userData0), mUserData1(userData1)/*, mPairHandle(pairHandle)*/ {} + + void* mUserData0; + void* mUserData1; + /* union + { + ActorHandle mPairHandle; //For created pairs, this is the index into the pair in the pair manager + void* mUserData; //For deleted pairs, this is the user data written by the application to the pair + };*/ + void* mPairUserData; //For deleted pairs, this is the user data written by the application to the pair + }; + + struct BpCacheData : public Ps::SListEntry + { + Ps::Array mCreatedPairs[2]; + Ps::Array mDeletedPairs[2]; + + void reset() + { + mCreatedPairs[0].resizeUninitialized(0); + mCreatedPairs[1].resizeUninitialized(0); + mDeletedPairs[0].resizeUninitialized(0); + mDeletedPairs[1].resizeUninitialized(0); + } + }; + + class BoundsArray : public Ps::UserAllocated + { + PX_NOCOPY(BoundsArray) + + public: + BoundsArray(Ps::VirtualAllocator& allocator) : mBounds(allocator) + { + } + + PX_FORCE_INLINE void initEntry(PxU32 index) + { + index++; // PT: always pretend we need one more entry, to make sure reading the last used entry will be SIMD-safe. + const PxU32 oldCapacity = mBounds.capacity(); + if(index>=oldCapacity) + { + const PxU32 newCapacity = Ps::nextPowerOfTwo(index); + mBounds.reserve(newCapacity); + mBounds.forceSize_Unsafe(newCapacity); + } + } + + PX_FORCE_INLINE void updateBounds(const PxTransform& transform, const Gu::GeometryUnion& geom, PxU32 index) + { + Gu::computeBounds(mBounds[index], geom.getGeometry(), transform, 0.0f, NULL, 1.0f); + mHasAnythingChanged = true; + } + + PX_FORCE_INLINE const PxBounds3& getBounds(PxU32 index) const + { + return mBounds[index]; + } + + PX_FORCE_INLINE void setBounds(const PxBounds3& bounds, PxU32 index) + { + // PX_CHECK_AND_RETURN(bounds.isValid() && !bounds.isEmpty(), "BoundsArray::setBounds - illegal bounds\n"); + mBounds[index] = bounds; + mHasAnythingChanged = true; + } + + PX_FORCE_INLINE const PxBounds3* begin() const + { + return mBounds.begin(); + } + + PX_FORCE_INLINE PxBounds3* begin() + { + return mBounds.begin(); + } + + PX_FORCE_INLINE Ps::Array& getBounds() + { + return mBounds; + } + + PX_FORCE_INLINE PxU32 getCapacity() const + { + return mBounds.size(); + } + + void shiftOrigin(const PxVec3& shift) + { + // we shift some potential NaNs here because we don't know what's active, but should be harmless + for(PxU32 i=0;i mBounds; + bool mHasAnythingChanged; + }; + + struct VolumeData + { + PX_FORCE_INLINE void reset() + { + mAggregate = PX_INVALID_U32; + mUserData = NULL; + } + + PX_FORCE_INLINE void setSingleActor() { mAggregate = PX_INVALID_U32; } + PX_FORCE_INLINE bool isSingleActor() const { return mAggregate == PX_INVALID_U32; } + + PX_FORCE_INLINE void setUserData(void* userData) + { + // PX_ASSERT(!(reinterpret_cast(userData) & 3)); + mUserData = userData; + } + + PX_FORCE_INLINE void* getUserData() const + { + return reinterpret_cast(reinterpret_cast(mUserData)& (~size_t(3))); + } + + PX_FORCE_INLINE void setVolumeType(ElementType::Enum volumeType) + { + PX_ASSERT(volumeType < 2); + mUserData = reinterpret_cast(reinterpret_cast(getUserData()) | static_cast(volumeType)); + } + + PX_FORCE_INLINE ElementType::Enum getVolumeType() const + { + return ElementType::Enum(reinterpret_cast(mUserData) & 3); + } + + PX_FORCE_INLINE void setAggregate(AggregateHandle handle) + { + PX_ASSERT(handle!=PX_INVALID_U32); + mAggregate = (handle<<1)|1; + } + PX_FORCE_INLINE bool isAggregate() const { return !isSingleActor() && ((mAggregate&1)!=0); } + + PX_FORCE_INLINE void setAggregated(AggregateHandle handle) + { + PX_ASSERT(handle!=PX_INVALID_U32); + mAggregate = (handle<<1)|0; + } + + PX_FORCE_INLINE bool isAggregated() const + { + return !isSingleActor() && ((mAggregate&1)==0); + } + + PX_FORCE_INLINE AggregateHandle getAggregateOwner() const { return mAggregate>>1; } + PX_FORCE_INLINE AggregateHandle getAggregate() const { return mAggregate>>1; } + + private: + void* mUserData; + // PT: TODO: consider moving this to a separate array, which wouldn't be allocated at all for people not using aggregates. + // PT: current encoding: + // aggregate == PX_INVALID_U32 => single actor + // aggregate != PX_INVALID_U32 => aggregate index<<1|LSB. LSB==1 for aggregates, LSB==0 for aggregated actors. + AggregateHandle mAggregate; + }; + + // PT: TODO: revisit this..... + class Aggregate; + class PersistentPairs; + class PersistentActorAggregatePair; + class PersistentAggregateAggregatePair; + class PersistentSelfCollisionPairs; + struct AggPair + { + PX_FORCE_INLINE AggPair() {} + PX_FORCE_INLINE AggPair(ShapeHandle index0, ShapeHandle index1) : mIndex0(index0), mIndex1(index1) {} + ShapeHandle mIndex0; + ShapeHandle mIndex1; + + PX_FORCE_INLINE bool operator==(const AggPair& p) const + { + return (p.mIndex0 == mIndex0) && (p.mIndex1 == mIndex1); + } + }; + typedef Ps::CoalescedHashMap AggPairMap; + + // PT: TODO: isn't there a generic pair structure somewhere? refactor with AggPair anyway + struct Pair + { + PX_FORCE_INLINE Pair(PxU32 id0, PxU32 id1) : mID0(id0), mID1(id1) {} + PX_FORCE_INLINE Pair(){} + + PX_FORCE_INLINE bool operator<(const Pair& p) const + { + const PxU64 value0 = *reinterpret_cast(this); + const PxU64 value1 = *reinterpret_cast(&p); + return value0 < value1; + } + + PX_FORCE_INLINE bool operator==(const Pair& p) const + { + return (p.mID0 == mID0) && (p.mID1 == mID1); + } + + PX_FORCE_INLINE bool operator!=(const Pair& p) const + { + return (p.mID0 != mID0) || (p.mID1 != mID1); + } + + PxU32 mID0; + PxU32 mID1; + }; + + class AABBManager; + + class PostBroadPhaseStage2Task : public Cm::Task + { + Cm::FlushPool* mFlushPool; + AABBManager& mManager; + + PX_NOCOPY(PostBroadPhaseStage2Task) + public: + + PostBroadPhaseStage2Task(PxU64 contextID, AABBManager& manager) : Cm::Task(contextID), mFlushPool(NULL), mManager(manager) + { + } + + virtual const char* getName() const { return "PostBroadPhaseStage2Task"; } + + void setFlushPool(Cm::FlushPool* pool) { mFlushPool = pool; } + + virtual void runInternal(); + + }; + + class ProcessAggPairsBase; + + /** + \brief A structure responsible for: + * storing an aabb representation for each active shape in the related scene + * managing the creation/removal of aabb representations when their related shapes are created/removed + * updating all aabbs that require an update due to modification of shape geometry or transform + * updating the aabb of all aggregates from the union of the aabbs of all shapes that make up each aggregate + * computing and reporting the incremental changes to the set of overlapping aabb pairs + */ + class AABBManager : public Ps::UserAllocated + { + PX_NOCOPY(AABBManager) + public: + + AABBManager(BroadPhase& bp, BoundsArray& boundsArray, Ps::Array& contactDistance, + PxU32 maxNbAggregates, PxU32 maxNbShapes, Ps::VirtualAllocator& allocator, PxU64 contextID, + PxPairFilteringMode::Enum kineKineFilteringMode, PxPairFilteringMode::Enum staticKineFilteringMode); + + void destroy(); + + AggregateHandle createAggregate(BoundsIndex index, Bp::FilterGroup::Enum group, void* userData, const bool selfCollisions); + bool destroyAggregate(BoundsIndex& index, Bp::FilterGroup::Enum& group, AggregateHandle aggregateHandle); + + bool addBounds(BoundsIndex index, PxReal contactDistance, Bp::FilterGroup::Enum group, void* userdata, AggregateHandle aggregateHandle, ElementType::Enum volumeType); + void reserveSpaceForBounds(BoundsIndex index); + void removeBounds(BoundsIndex index); + PX_FORCE_INLINE Ps::IntBool isMarkedForRemove(BoundsIndex index) const { return mRemovedHandleMap.boundedTest(index); } + + void setContactOffset(BoundsIndex handle, PxReal offset) + { + // PT: this works even for aggregated shapes, since the corresponding bit will also be set in the 'updated' map. + mContactDistance.begin()[handle] = offset; + mPersistentStateChanged = true; + mChangedHandleMap.growAndSet(handle); + } + + void setVolumeType(BoundsIndex handle, ElementType::Enum volumeType) + { + mVolumeData[handle].setVolumeType(volumeType); + } + + void setBPGroup(BoundsIndex index, Bp::FilterGroup::Enum group) + { + PX_ASSERT((index + 1) < mVolumeData.size()); + PX_ASSERT(group != Bp::FilterGroup::eINVALID); // PT: we use group == Bp::FilterGroup::eINVALID to mark removed/invalid entries + mGroups[index] = group; + } + + // PT: TODO: revisit name: we don't "update AABBs" here anymore + void updateAABBsAndBP( PxU32 numCpuTasks, + Cm::FlushPool& flushPool, + PxcScratchAllocator* scratchAllocator, + bool hasContactDistanceUpdated, + PxBaseTask* continuation, + PxBaseTask* narrowPhaseUnlockTask); + + void finalizeUpdate( PxU32 numCpuTasks, + PxcScratchAllocator* scratchAllocator, + PxBaseTask* continuation, + PxBaseTask* narrowPhaseUnlockTask); + + AABBOverlap* getCreatedOverlaps(ElementType::Enum type, PxU32& count) + { + PX_ASSERT(type < ElementType::eCOUNT); + count = mCreatedOverlaps[type].size(); + return mCreatedOverlaps[type].begin(); + } + + AABBOverlap* getDestroyedOverlaps(ElementType::Enum type, PxU32& count) + { + PX_ASSERT(type < ElementType::eCOUNT); + count = mDestroyedOverlaps[type].size(); + return mDestroyedOverlaps[type].begin(); + } + + void freeBuffers(); + + void** getOutOfBoundsObjects(PxU32& nbOutOfBoundsObjects) + { + nbOutOfBoundsObjects = mOutOfBoundsObjects.size(); + return mOutOfBoundsObjects.begin(); + } + + void clearOutOfBoundsObjects() + { + mOutOfBoundsObjects.clear(); + } + + void** getOutOfBoundsAggregates(PxU32& nbOutOfBoundsAggregates) + { + nbOutOfBoundsAggregates = mOutOfBoundsAggregates.size(); + return mOutOfBoundsAggregates.begin(); + } + + void clearOutOfBoundsAggregates() + { + mOutOfBoundsAggregates.clear(); + } + + void shiftOrigin(const PxVec3& shift); + + void visualize(Cm::RenderOutput& out); + + PX_FORCE_INLINE BroadPhase* getBroadPhase() const { return &mBroadPhase; } + PX_FORCE_INLINE BoundsArray& getBoundsArray() { return mBoundsArray; } + PX_FORCE_INLINE PxU32 getNbActiveAggregates() const { return mNbAggregates; } + PX_FORCE_INLINE const float* getContactDistances() const { return mContactDistance.begin(); } + PX_FORCE_INLINE Cm::BitMapPinned& getChangedAABBMgActorHandleMap() { return mChangedHandleMap; } + + PX_FORCE_INLINE void* getUserData(const BoundsIndex index) const { if (index < mVolumeData.size()) return mVolumeData[index].getUserData(); return NULL; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + + void postBroadPhase(PxBaseTask*, PxBaseTask* narrowPhaseUnlockTask, Cm::FlushPool& flushPool); + + + + BpCacheData* getBpCacheData(); + void putBpCacheData(BpCacheData*); + void resetBpCacheData(); + + Ps::Mutex mMapLock; + + private: + void reserveShapeSpace(PxU32 nbShapes); + + void postBpStage2(PxBaseTask*, Cm::FlushPool&); + + void postBpStage3(PxBaseTask*); + + PostBroadPhaseStage2Task mPostBroadPhase2; + Cm::DelegateTask mPostBroadPhase3; + + //Cm::DelegateTask mPostBroadPhase; + + FinalizeUpdateTask mFinalizeUpdateTask; + + // PT: we have bitmaps here probably to quickly handle added/removed objects during same frame. + // PT: TODO: consider replacing with plain arrays (easier to parse, already existing below, etc) + Cm::BitMap mAddedHandleMap; // PT: indexed by BoundsIndex + Cm::BitMap mRemovedHandleMap; // PT: indexed by BoundsIndex + Cm::BitMapPinned mChangedHandleMap; + + PX_FORCE_INLINE void removeBPEntry(BoundsIndex index) // PT: only for objects passed to the BP + { + if(mAddedHandleMap.test(index)) // PT: if object had been added this frame... + mAddedHandleMap.reset(index); // PT: ...then simply revert the previous operation locally (it hasn't been passed to the BP yet). + else + mRemovedHandleMap.set(index); // PT: else we need to remove it from the BP + } + + PX_FORCE_INLINE void addBPEntry(BoundsIndex index) + { + if(mRemovedHandleMap.test(index)) + mRemovedHandleMap.reset(index); + else + mAddedHandleMap.set(index); + } + + // PT: TODO: when do we need 'Ps::VirtualAllocator' and when don't we? When memory is passed to GPU BP? + //ML: we create mGroups and mContactDistance in the AABBManager constructor. Ps::Array will take Ps::VirtualAllocator as a parameter. Therefore, if GPU BP is using, + //we will passed a pinned host memory allocator, otherwise, we will just pass a normal allocator. + Ps::Array mGroups; // NOTE: we stick Bp::FilterGroup::eINVALID in this slot to indicate that the entry is invalid (removed or never inserted.) + Ps::Array& mContactDistance; + Ps::Array mVolumeData; + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + bool mLUT[Bp::FilterType::COUNT][Bp::FilterType::COUNT]; + #endif + PX_FORCE_INLINE void initEntry(BoundsIndex index, PxReal contactDistance, Bp::FilterGroup::Enum group, void* userData) + { + if ((index + 1) >= mVolumeData.size()) + reserveShapeSpace(index + 1); + + // PT: TODO: why is this needed at all? Why aren't size() and capacity() enough? + mUsedSize = PxMax(index+1, mUsedSize); + + PX_ASSERT(group != Bp::FilterGroup::eINVALID); // PT: we use group == Bp::FilterGroup::eINVALID to mark removed/invalid entries + mGroups[index] = group; + mContactDistance.begin()[index] = contactDistance; + mVolumeData[index].setUserData(userData); + } + + PX_FORCE_INLINE void resetEntry(BoundsIndex index) + { + mGroups[index] = Bp::FilterGroup::eINVALID; + mContactDistance.begin()[index] = 0.0f; + mVolumeData[index].reset(); + } + + // PT: TODO: remove confusion between BoundsIndex and ShapeHandle here! + Ps::Array mAddedHandles; + Ps::Array mUpdatedHandles; + Ps::Array mRemovedHandles; + + BroadPhase& mBroadPhase; + BoundsArray& mBoundsArray; + + Ps::Array mOutOfBoundsObjects; + Ps::Array mOutOfBoundsAggregates; + Ps::Array mCreatedOverlaps[ElementType::eCOUNT]; + Ps::Array mDestroyedOverlaps[ElementType::eCOUNT]; + + PxcScratchAllocator* mScratchAllocator; + + PxBaseTask* mNarrowPhaseUnblockTask; + PxU32 mUsedSize; // highest used value + 1 + bool mOriginShifted; + bool mPersistentStateChanged; + + PxU32 mNbAggregates; + PxU32 mFirstFreeAggregate; + Ps::Array mAggregates; // PT: indexed by AggregateHandle + Ps::Array mDirtyAggregates; + + PxU32 mTimestamp; + + AggPairMap mActorAggregatePairs; + AggPairMap mAggregateAggregatePairs; + + Ps::Array mAggPairTasks; + + #ifdef BP_USE_AGGREGATE_GROUP_TAIL + // PT: TODO: even in the 3.4 trunk this stuff is a clumsy mess: groups are "BpHandle" suddenly passed + // to BroadPhaseUpdateData as "ShapeHandle". + //Free aggregate group ids. + PxU32 mAggregateGroupTide; + Ps::Array mFreeAggregateGroups; // PT: TODO: remove this useless array + #endif + Ps::HashSet mCreatedPairs; + + PxU64 mContextID; + + Ps::SList mBpThreadContextPool; + + PX_FORCE_INLINE Aggregate* getAggregateFromHandle(AggregateHandle handle) + { + PX_ASSERT(handle minB and maxB > minA for all three axes. + The rule that minima(maxima) are even(odd) (see BroadPhaseUpdateData) removes the ambiguity of touching bounds. + + */ + virtual BroadPhasePair* getCreatedPairs() = 0; + + /** + \brief Return the number of deleted overlap pairs computed in the execution of update() that has just completed. + */ + virtual PxU32 getNbDeletedPairs() const = 0; + + /** + \brief Return the number of deleted overlap pairs computed in the execution of update() that has just completed. + Note that a deleted pair can only be reported if that pair has already appeared in the list of created pairs in an earlier update. + A lost overlap occurs when a pair of bounds previously overlapped on all three axes but have now separated on at least one axis. + A lost overlap must involve at least one of the bounds of the lost overlap pair appearing in the updated list. + Lost overlaps arising from removal of bounds from the broadphase do not appear in the list of deleted pairs. + It is impossible for the same pair to appear simultaneously in the list of created and deleted pairs. + The test for overlap is conservative throughout, meaning that deleted pairs do not include touching pairs. + */ + virtual BroadPhasePair* getDeletedPairs() = 0; + + /** + \brief After the broadphase has completed its update() function and the created/deleted pairs have been queried + with getCreatedPairs/getDeletedPairs it is desirable to free any memory that was temporarily acquired for the update but is + is no longer required post-update. This can be achieved with the function freeBuffers(). + */ + virtual void freeBuffers() = 0; + + /** + \brief Adjust internal structures after all bounds have been adjusted due to a scene origin shift. + */ + virtual void shiftOrigin(const PxVec3& shift) = 0; + + /** + \brief Test that the created/updated/removed lists obey the rules that + 1. object ids can only feature in the created list if they have never been previously added or if they were previously removed. + 2. object ids can only be added to the updated list if they have been previously added without being removed. + 3. objects ids can only be added to the removed list if they have been previously added without being removed. + */ +#if PX_CHECKED + virtual bool isValid(const BroadPhaseUpdateData& updateData) const = 0; +#endif + + virtual BroadPhasePair* getBroadPhasePairs() const = 0; + + virtual void deletePairs() = 0; + + // PT: for unit-testing the non-GPU versions + virtual void singleThreadedUpdate(PxcScratchAllocator* /*scratchAllocator*/, const BroadPhaseUpdateData& /*updateData*/){} +}; + +} //namespace Bp + +} //namespace physx + +#endif //BP_BROADPHASE_H diff --git a/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h b/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h new file mode 100644 index 000000000..6fe718204 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h @@ -0,0 +1,523 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_UPDATE_H +#define BP_BROADPHASE_UPDATE_H + +#include "foundation/PxAssert.h" +#include "foundation/PxUnionCast.h" +#include "CmPhysXCommon.h" +#include "PxBroadPhase.h" +#include "Ps.h" + +namespace physx +{ +namespace Bp +{ +typedef PxU32 ShapeHandle; +typedef PxU32 BpHandle; +#define BP_INVALID_BP_HANDLE 0x3fffffff + +#define ALIGN_SIZE_16(size) ((unsigned(size)+15)&(unsigned(~15))) + +#define BP_USE_AGGREGATE_GROUP_TAIL +#define BP_FILTERING_USES_TYPE_IN_GROUP + + /* + \brief AABBManager volumes with the same filter group value are guaranteed never to generate an overlap pair. + \note To ensure that static pairs never overlap, add static shapes with eSTATICS. + The value eDYNAMICS_BASE provides a minimum recommended group value for dynamic shapes. + If dynamics shapes are assigned group values greater than or equal to eDYNAMICS_BASE then + they are allowed to generate broadphase overlaps with statics, and other dynamic shapes provided + they have different group values. + @see AABBManager::createVolume + */ + struct FilterGroup + { + enum Enum + { + eSTATICS = 0, + eDYNAMICS_BASE = 1, +#ifdef BP_USE_AGGREGATE_GROUP_TAIL + eAGGREGATE_BASE = 0xfffffffe, +#endif + eINVALID = 0xffffffff + }; + }; + +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + struct FilterType + { + enum Enum + { + STATIC = 0, + KINEMATIC = 1, + DYNAMIC = 2, + AGGREGATE = 3, + + COUNT = 4 + }; + }; +#endif + + PX_FORCE_INLINE Bp::FilterGroup::Enum getFilterGroup_Statics() + { + return Bp::FilterGroup::eSTATICS; + } + + PX_FORCE_INLINE Bp::FilterGroup::Enum getFilterGroup_Dynamics(PxU32 rigidId, bool isKinematic) + { + const PxU32 group = rigidId + Bp::FilterGroup::eDYNAMICS_BASE; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const PxU32 type = isKinematic ? FilterType::KINEMATIC : FilterType::DYNAMIC; + return Bp::FilterGroup::Enum((group<<2)|type); +#else + PX_UNUSED(isKinematic); + return Bp::FilterGroup::Enum(group); +#endif + } + + PX_FORCE_INLINE Bp::FilterGroup::Enum getFilterGroup(bool isStatic, PxU32 rigidId, bool isKinematic) + { + return isStatic ? getFilterGroup_Statics() : getFilterGroup_Dynamics(rigidId, isKinematic); + } + +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + PX_FORCE_INLINE bool groupFiltering(const Bp::FilterGroup::Enum group0, const Bp::FilterGroup::Enum group1, const bool* PX_RESTRICT lut) + { +/* const int g0 = group0 & ~3; + const int g1 = group1 & ~3; + if(g0==g1) + return false;*/ + if(group0==group1) + { + PX_ASSERT((group0 & ~3)==(group1 & ~3)); + return false; + } + + const int type0 = group0 & 3; + const int type1 = group1 & 3; + return lut[type0*4+type1]; + } +#else + PX_FORCE_INLINE bool groupFiltering(const Bp::FilterGroup::Enum group0, const Bp::FilterGroup::Enum group1) + { + return group0!=group1; + } +#endif + + /* + \brief Encode a single float value with lossless encoding to integer + */ + PX_FORCE_INLINE PxU32 encodeFloat(PxU32 ir) + { + //we may need to check on -0 and 0 + //But it should make no practical difference. + if(ir & PX_SIGN_BITMASK) //negative? + return ~ir;//reverse sequence of negative numbers + else + return ir | PX_SIGN_BITMASK; // flip sign + } + + /* + \brief Encode a single float value with lossless encoding to integer + */ + PX_FORCE_INLINE PxU32 decodeFloat(PxU32 ir) + { + if(ir & PX_SIGN_BITMASK) //positive? + return ir & ~PX_SIGN_BITMASK; //flip sign + else + return ~ir; //undo reversal + } + + +/** +\brief Integer representation of PxBounds3 used by BroadPhase +@see BroadPhaseUpdateData +*/ + +typedef PxU32 ValType; + +class IntegerAABB +{ +public: + + enum + { + MIN_X = 0, + MIN_Y, + MIN_Z, + MAX_X, + MAX_Y, + MAX_Z + }; + + IntegerAABB(const PxBounds3& b, PxReal contactDistance) + { + const PxVec3 dist(contactDistance); + encode(PxBounds3(b.minimum - dist, b.maximum + dist)); + } + + /* + \brief Return the minimum along a specified axis + \param[in] i is the axis + */ + PX_FORCE_INLINE ValType getMin(PxU32 i) const { return (mMinMax)[MIN_X+i]; } + + /* + \brief Return the maximum along a specified axis + \param[in] i is the axis + */ + PX_FORCE_INLINE ValType getMax(PxU32 i) const { return (mMinMax)[MAX_X+i]; } + + /* + \brief Return one of the six min/max values of the bound + \param[in] isMax determines whether a min or max value is returned + \param[in] index is the axis + */ + PX_FORCE_INLINE ValType getExtent(PxU32 isMax, PxU32 index) const + { + PX_ASSERT(isMax<=1); + return (mMinMax)[3*isMax+index]; + } + + /* + \brief Return the minimum on the x axis + */ + PX_FORCE_INLINE ValType getMinX() const { return mMinMax[MIN_X]; } + + /* + \brief Return the minimum on the y axis + */ + PX_FORCE_INLINE ValType getMinY() const { return mMinMax[MIN_Y]; } + + /* + \brief Return the minimum on the z axis + */ + PX_FORCE_INLINE ValType getMinZ() const { return mMinMax[MIN_Z]; } + + /* + \brief Return the maximum on the x axis + */ + PX_FORCE_INLINE ValType getMaxX() const { return mMinMax[MAX_X]; } + + /* + \brief Return the maximum on the y axis + */ + PX_FORCE_INLINE ValType getMaxY() const { return mMinMax[MAX_Y]; } + + /* + \brief Return the maximum on the z axis + */ + PX_FORCE_INLINE ValType getMaxZ() const { return mMinMax[MAX_Z]; } + + /* + \brief Encode float bounds so they are stored as integer bounds + \param[in] bounds is the bounds to be encoded + \note The integer values of minima are always even, while the integer values of maxima are always odd + \note The encoding process masks off the last four bits for minima and masks on the last four bits for maxima. + This keeps the bounds constant when its shape is subjected to small global pose perturbations. In turn, this helps + reduce computational effort in the broadphase update by reducing the amount of sorting required on near-stationary + bodies that are aligned along one or more axis. + @see decode + */ + PX_FORCE_INLINE void encode(const PxBounds3& bounds) + { + const PxU32* PX_RESTRICT min = PxUnionCast(&bounds.minimum.x); + const PxU32* PX_RESTRICT max = PxUnionCast(&bounds.maximum.x); + //Avoid min=max by enforcing the rule that mins are even and maxs are odd. + mMinMax[MIN_X] = encodeFloatMin(min[0]); + mMinMax[MIN_Y] = encodeFloatMin(min[1]); + mMinMax[MIN_Z] = encodeFloatMin(min[2]); + mMinMax[MAX_X] = encodeFloatMax(max[0]) | (1<<2); + mMinMax[MAX_Y] = encodeFloatMax(max[1]) | (1<<2); + mMinMax[MAX_Z] = encodeFloatMax(max[2]) | (1<<2); + } + + /* + \brief Decode from integer bounds to float bounds + \param[out] bounds is the decoded float bounds + \note Encode followed by decode will produce a float bound larger than the original + due to the masking in encode. + @see encode + */ + PX_FORCE_INLINE void decode(PxBounds3& bounds) const + { + PxU32* PX_RESTRICT min = PxUnionCast(&bounds.minimum.x); + PxU32* PX_RESTRICT max = PxUnionCast(&bounds.maximum.x); + min[0] = decodeFloat(mMinMax[MIN_X]); + min[1] = decodeFloat(mMinMax[MIN_Y]); + min[2] = decodeFloat(mMinMax[MIN_Z]); + max[0] = decodeFloat(mMinMax[MAX_X]); + max[1] = decodeFloat(mMinMax[MAX_Y]); + max[2] = decodeFloat(mMinMax[MAX_Z]); + } + + /* + \brief Encode a single minimum value from integer bounds to float bounds + \note The encoding process masks off the last four bits for minima + @see encode + */ + static PX_FORCE_INLINE ValType encodeFloatMin(PxU32 source) + { + return ((encodeFloat(source) >> eGRID_SNAP_VAL) - 1) << eGRID_SNAP_VAL; + } + + /* + \brief Encode a single maximum value from integer bounds to float bounds + \note The encoding process masks on the last four bits for maxima + @see encode + */ + static PX_FORCE_INLINE ValType encodeFloatMax(PxU32 source) + { + return ((encodeFloat(source) >> eGRID_SNAP_VAL) + 1) << eGRID_SNAP_VAL; + } + + /* + \brief Shift the encoded bounds by a specified vector + \param[in] shift is the vector used to shift the bounds + */ + PX_FORCE_INLINE void shift(const PxVec3& shift) + { + ::physx::PxBounds3 elemBounds; + decode(elemBounds); + elemBounds.minimum -= shift; + elemBounds.maximum -= shift; + encode(elemBounds); + } + + /* + \brief Test if this aabb lies entirely inside another aabb + \param[in] box is the other box + \return True if this aabb lies entirely inside box + */ + PX_INLINE bool isInside(const IntegerAABB& box) const + { + if(box.mMinMax[MIN_X]>mMinMax[MIN_X]) return false; + if(box.mMinMax[MIN_Y]>mMinMax[MIN_Y]) return false; + if(box.mMinMax[MIN_Z]>mMinMax[MIN_Z]) return false; + if(box.mMinMax[MAX_X] mMinMax[MAX_X] || mMinMax[MIN_X] > b.mMinMax[MAX_X] || + b.mMinMax[MIN_Y] > mMinMax[MAX_Y] || mMinMax[MIN_Y] > b.mMinMax[MAX_Y] || + b.mMinMax[MIN_Z] > mMinMax[MAX_Z] || mMinMax[MIN_Z] > b.mMinMax[MAX_Z]); + } + + PX_FORCE_INLINE bool intersects1D(const IntegerAABB& b, const PxU32 axis) const + { + const PxU32 maxAxis = axis + 3; + return !(b.mMinMax[axis] > mMinMax[maxAxis] || mMinMax[axis] > b.mMinMax[maxAxis]); + } + + + /* + \brief Expand bounds to include another + \note This is used to compute the aggregate bounds of multiple shape bounds + \param[in] b is the bounds to be included + */ + PX_FORCE_INLINE void include(const IntegerAABB& b) + { + mMinMax[MIN_X] = PxMin(mMinMax[MIN_X], b.mMinMax[MIN_X]); + mMinMax[MIN_Y] = PxMin(mMinMax[MIN_Y], b.mMinMax[MIN_Y]); + mMinMax[MIN_Z] = PxMin(mMinMax[MIN_Z], b.mMinMax[MIN_Z]); + mMinMax[MAX_X] = PxMax(mMinMax[MAX_X], b.mMinMax[MAX_X]); + mMinMax[MAX_Y] = PxMax(mMinMax[MAX_Y], b.mMinMax[MAX_Y]); + mMinMax[MAX_Z] = PxMax(mMinMax[MAX_Z], b.mMinMax[MAX_Z]); + } + + /* + \brief Set the bounds to (max, max, max), (min, min, min) + */ + PX_INLINE void setEmpty() + { + mMinMax[MIN_X] = mMinMax[MIN_Y] = mMinMax[MIN_Z] = 0xff7fffff; //PX_IR(PX_MAX_F32); + mMinMax[MAX_X] = mMinMax[MAX_Y] = mMinMax[MAX_Z] = 0x00800000; ///PX_IR(0.0f); + } + + ValType mMinMax[6]; + +private: + + enum + { + eGRID_SNAP_VAL = 4 + }; +}; + +PX_FORCE_INLINE ValType encodeMin(const PxBounds3& bounds, PxU32 axis, PxReal contactDistance) +{ + const PxReal val = bounds.minimum[axis] - contactDistance; + const PxU32 min = PxUnionCast(val); + const PxU32 m = IntegerAABB::encodeFloatMin(min); + return m; +} + +PX_FORCE_INLINE ValType encodeMax(const PxBounds3& bounds, PxU32 axis, PxReal contactDistance) +{ + const PxReal val = bounds.maximum[axis] + contactDistance; + const PxU32 max = PxUnionCast(val); + const PxU32 m = IntegerAABB::encodeFloatMax(max) | (1<<2); + return m; +} + +class BroadPhase; + +class BroadPhaseUpdateData +{ +public: + + /** + \brief A structure detailing the changes to the collection of aabbs, whose overlaps are computed in the broadphase. + The structure consists of per-object arrays of object bounds and object groups, and three arrays that index + into the per-object arrays, denoting the bounds which are to be created, updated and removed in the broad phase. + + * each entry in the object arrays represents the same shape or aggregate from frame to frame. + * each entry in an index array must be less than the capacity of the per-object arrays. + * no index value may appear in more than one index array, and may not occur more than once in that array. + + An index value is said to be "in use" if it has appeared in a created list in a previous update, and has not + since occurred in a removed list. + + \param[in] created an array of indices describing the bounds that must be inserted into the broadphase. + Each index in the array must not be in use. + + \param[in] updated an array of indices (referencing the boxBounds and boxGroups arrays) describing the bounds + that have moved since the last broadphase update. Each index in the array must be in use, and each object + whose index is in use and whose AABB has changed must appear in the update list. + + \param[in] removed an array of indices describing the bounds that must be removed from the broad phase. Each index in + the array must be in use. + + \param[in] boxBounds an array of bounds coordinates for the AABBs to be processed by the broadphase. + + An entry is valid if its values are integer bitwise representations of floating point numbers that satisfy max>min in each dimension, + along with a further rule that minima(maxima) must have even(odd) values. + + Each entry whose index is either in use or appears in the created array must be valid. An entry whose index is either not in use or + appears in the removed array need not be valid. + + \param[in] boxGroups an array of group ids, one for each bound, used for pair filtering. Bounds with the same group id will not be + reported as overlap pairs by the broad phase. Zero is reserved for static bounds. + + Entries in this array are immutable: the only way to change the group of an object is to remove it from the broad phase and reinsert + it at a different index (recall that each index must appear at most once in the created/updated/removed lists). + + \param[in] boxesCapacity the length of the boxBounds and boxGroups arrays. + + @see BroadPhase::update + */ + BroadPhaseUpdateData( + const ShapeHandle* created, const PxU32 createdSize, + const ShapeHandle* updated, const PxU32 updatedSize, + const ShapeHandle* removed, const PxU32 removedSize, + const PxBounds3* boxBounds, const Bp::FilterGroup::Enum* boxGroups, +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* lut, +#endif + const PxReal* boxContactDistances, const PxU32 boxesCapacity, + const bool stateChanged) : + mCreated (created), + mCreatedSize (createdSize), + mUpdated (updated), + mUpdatedSize (updatedSize), + mRemoved (removed), + mRemovedSize (removedSize), + mBoxBounds (boxBounds), + mBoxGroups (boxGroups), +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT (lut), +#endif + mContactDistance(boxContactDistances), + mBoxesCapacity (boxesCapacity), + mStateChanged (stateChanged) + { + } + + PX_FORCE_INLINE const ShapeHandle* getCreatedHandles() const { return mCreated; } + PX_FORCE_INLINE PxU32 getNumCreatedHandles() const { return mCreatedSize; } + + PX_FORCE_INLINE const ShapeHandle* getUpdatedHandles() const { return mUpdated; } + PX_FORCE_INLINE PxU32 getNumUpdatedHandles() const { return mUpdatedSize; } + + PX_FORCE_INLINE const ShapeHandle* getRemovedHandles() const { return mRemoved; } + PX_FORCE_INLINE PxU32 getNumRemovedHandles() const { return mRemovedSize; } + + PX_FORCE_INLINE const PxBounds3* getAABBs() const { return mBoxBounds; } + PX_FORCE_INLINE const Bp::FilterGroup::Enum* getGroups() const { return mBoxGroups; } +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + PX_FORCE_INLINE const bool* getLUT() const { return mLUT; } +#endif + PX_FORCE_INLINE PxU32 getCapacity() const { return mBoxesCapacity; } + + PX_FORCE_INLINE const PxReal* getContactDistance() const { return mContactDistance; } + + PX_FORCE_INLINE bool getStateChanged() const { return mStateChanged; } + +#if PX_CHECKED + static bool isValid(const BroadPhaseUpdateData& updateData, const BroadPhase& bp); + bool isValid() const; +#endif + +private: + + const ShapeHandle* mCreated; + PxU32 mCreatedSize; + + const ShapeHandle* mUpdated; + PxU32 mUpdatedSize; + + const ShapeHandle* mRemoved; + PxU32 mRemovedSize; + + const PxBounds3* mBoxBounds; + const Bp::FilterGroup::Enum* mBoxGroups; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* mLUT; +#endif + const PxReal* mContactDistance; + PxU32 mBoxesCapacity; + bool mStateChanged; +}; + +} //namespace Bp + +} //namespace physx + +#endif //BP_BROADPHASE_UPDATE_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp new file mode 100644 index 000000000..85109e5f6 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp @@ -0,0 +1,2532 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "BpAABBManager.h" + +#define NB_SENTINELS 6 + +#include "CmRenderOutput.h" +#include "CmFlushPool.h" +#include "BpBroadPhaseMBPCommon.h" +#include "BpBroadPhase.h" +#include "BpBroadPhaseShared.h" +#include "PsFoundation.h" +#include "PsSort.h" +#include "PsHashSet.h" +#include "PsVecMath.h" +#include "GuInternal.h" +#include "common/PxProfileZone.h" +//#include + +using namespace physx; +using namespace Bp; +using namespace Cm; +using namespace Ps::aos; + +static const bool gSingleThreaded = false; +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + #define ABP_SIMD_OVERLAP +#endif +#ifdef ABP_SIMD_OVERLAP + typedef AABB_YZn AABB_YZ; +#else + typedef AABB_YZr AABB_YZ; +#endif + +#ifdef ABP_SIMD_OVERLAP + static const bool gUseRegularBPKernel = false; // false to use "version 13" in box pruning series + static const bool gUnrollLoop = true; // true to use "version 14" in box pruning series +#else + // PT: tested on Switch, for some reason the regular version is fastest there + static const bool gUseRegularBPKernel = true; // false to use "version 13" in box pruning series + static const bool gUnrollLoop = false; // true to use "version 14" in box pruning series +#endif + +namespace physx +{ +namespace Bp +{ +static PX_FORCE_INLINE uint32_t hash(const Pair& p) +{ + return PxU32(Ps::hash( (p.mID0&0xffff)|(p.mID1<<16)) ); +} + +static PX_FORCE_INLINE uint32_t hash(const AggPair& p) +{ + return PxU32(Ps::hash( (p.mIndex0&0xffff)|(p.mIndex1<<16)) ); +} + +static PX_FORCE_INLINE bool shouldPairBeDeleted(const Ps::Array& groups, ShapeHandle h0, ShapeHandle h1) +{ + PX_ASSERT(h0 +static void resetOrClear(T& a) +{ + const PxU32 c = a.capacity(); + const PxU32 s = a.size(); + if(s>=c/2) + a.clear(); + else + a.reset(); +} + +/// + + typedef PxU32 InflatedType; + + // PT: TODO: revisit/optimize all that stuff once it works + class Aggregate : public Ps::UserAllocated + { + public: + Aggregate(BoundsIndex index, bool selfCollisions); + ~Aggregate(); + + BoundsIndex mIndex; + private: + Ps::Array mAggregated; // PT: TODO: replace with linked list? + public: + PersistentSelfCollisionPairs* mSelfCollisionPairs; + PxU32 mDirtyIndex; // PT: index in mDirtyAggregates + private: + AABB_Xi* mInflatedBoundsX; + AABB_YZ* mInflatedBoundsYZ; + PxU32 mAllocatedSize; + public: + PX_FORCE_INLINE PxU32 getNbAggregated() const { return mAggregated.size(); } + PX_FORCE_INLINE BoundsIndex getAggregated(PxU32 i) const { return mAggregated[i]; } + PX_FORCE_INLINE const BoundsIndex* getIndices() const { return mAggregated.begin(); } + PX_FORCE_INLINE void addAggregated(BoundsIndex i) { mAggregated.pushBack(i); } + PX_FORCE_INLINE bool removeAggregated(BoundsIndex i) { return mAggregated.findAndReplaceWithLast(i); } // PT: TODO: optimize? + PX_FORCE_INLINE const PxBounds3& getMergedBounds() const { return mBounds; } + + PX_FORCE_INLINE void resetDirtyState() { mDirtyIndex = PX_INVALID_U32; } + PX_FORCE_INLINE bool isDirty() const { return mDirtyIndex != PX_INVALID_U32; } + PX_FORCE_INLINE void markAsDirty(Ps::Array& dirtyAggregates) + { + if(!isDirty()) + { + mDirtyIndex = dirtyAggregates.size(); + dirtyAggregates.pushBack(this); + } + } + + void allocateBounds(); + void computeBounds(const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances) /*PX_RESTRICT*/; + + PX_FORCE_INLINE const AABB_Xi* getBoundsX() const { return mInflatedBoundsX; } + PX_FORCE_INLINE const AABB_YZ* getBoundsYZ() const { return mInflatedBoundsYZ; } + PX_FORCE_INLINE void getSortedMinBounds() + { + if(mDirtySort) + sortBounds(); + } + private: + PxBounds3 mBounds; + bool mDirtySort; + + void sortBounds(); + PX_NOCOPY(Aggregate) + }; + +/// + +namespace +{ +#define MBP_ALLOC(x) PX_ALLOC(x, "MBP") +#define MBP_ALLOC_TMP(x) PX_ALLOC_TEMP(x, "MBP_TMP") +#define MBP_FREE(x) if(x) PX_FREE_AND_RESET(x) + + struct MBPEntry; + struct RegionHandle; + struct MBP_Object; + + class MBP_PairManager : public PairManagerData + { + public: + MBP_PairManager() {} + ~MBP_PairManager() {} + + PX_FORCE_INLINE InternalPair* addPair(PxU32 id0, PxU32 id1); + }; + +/////////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE InternalPair* MBP_PairManager::addPair(PxU32 id0, PxU32 id1) +{ + PX_ASSERT(id0!=INVALID_ID); + PX_ASSERT(id1!=INVALID_ID); + return addPairInternal(id0, id1); +} + +/////////////////////////////////////////////////////////////////////////////// + +} + + typedef MBP_PairManager PairArray; + +/// + +class PersistentPairs : public Ps::UserAllocated +{ + public: + PersistentPairs() : mTimestamp(PX_INVALID_U32), mShouldBeDeleted(false) {} + virtual ~PersistentPairs() {} + + virtual bool update(AABBManager& /*manager*/, BpCacheData* /*data*/ = NULL) { return false; } + + + PX_FORCE_INLINE void updatePairs(PxU32 timestamp, const PxBounds3* bounds, const float* contactDistances, const Bp::FilterGroup::Enum* groups, +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* lut, +#endif + Ps::Array& volumeData, Ps::Array* createdOverlaps, Ps::Array* destroyedOverlaps); + void outputDeletedOverlaps(Ps::Array* overlaps, const Ps::Array& volumeData); + private: + virtual void findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) = 0; + protected: + PxU32 mTimestamp; + MBP_PairManager mPM; + public: + bool mShouldBeDeleted; +}; + +///// + #define PosXType2 PxU32 + +#if PX_INTEL_FAMILY + #define SIMD_OVERLAP_TEST_14a(box) _mm_movemask_ps(_mm_cmpngt_ps(b, _mm_load_ps(box)))==15 + + #define SIMD_OVERLAP_INIT_9c(box) \ + __m128 b = _mm_shuffle_ps(_mm_load_ps(&box.mMinY), _mm_load_ps(&box.mMinY), 78);\ + const float Coeff = -1.0f;\ + b = _mm_mul_ps(b, _mm_load1_ps(&Coeff)); + + #define SIMD_OVERLAP_TEST_9c(box) \ + const __m128 a = _mm_load_ps(&box.mMinY); \ + const __m128 d = _mm_cmpge_ps(a, b); \ + if(_mm_movemask_ps(d)==15) +#else + #define SIMD_OVERLAP_TEST_14a(box) BAllEqFFFF(V4IsGrtr(b, V4LoadA(box))) + + #define SIMD_OVERLAP_INIT_9c(box) \ + Vec4V b = V4PermZWXY(V4LoadA(&box.mMinY)); \ + b = V4Mul(b, V4Load(-1.0f)); + + #define SIMD_OVERLAP_TEST_9c(box) \ + const Vec4V a = V4LoadA(&box.mMinY); \ + const Vec4V d = V4IsGrtrOrEq(a, b); \ + if(BAllEqTTTT(d)) +#endif + + #define CODEALIGN16 //_asm align 16 +#ifdef ABP_SIMD_OVERLAP + #define SIMD_OVERLAP_PRELOAD_BOX0 SIMD_OVERLAP_INIT_9c(box0) + #define SIMD_OVERLAP_TEST(x) SIMD_OVERLAP_TEST_9c(x) +#else + #define SIMD_OVERLAP_PRELOAD_BOX0 +#endif + +#ifndef ABP_SIMD_OVERLAP +static PX_FORCE_INLINE int intersect2D(const AABB_YZ& a, const AABB_YZ& b) +{ + const bool b0 = b.mMaxY < a.mMinY; + const bool b1 = a.mMaxY < b.mMinY; + const bool b2 = b.mMaxZ < a.mMinZ; + const bool b3 = a.mMaxZ < b.mMinZ; +// const bool b4 = b0 || b1 || b2 || b3; + const bool b4 = b0 | b1 | b2 | b3; + return !b4; +} +#endif + +#ifdef ABP_SIMD_OVERLAP + #define ABP_OVERLAP_TEST(x) SIMD_OVERLAP_TEST(x) +#else + #define ABP_OVERLAP_TEST(x) if(intersect2D(box0, x)) +#endif + +//#define BIP_VERSION_1 + + struct outputPair_Bipartite + { +#ifdef BIP_VERSION_1 + outputPair_Bipartite(PairArray* pairManager, Aggregate* aggregate0, Aggregate* aggregate1, const Bp::FilterGroup::Enum* groups, const bool* lut) : +#else + outputPair_Bipartite(PairArray* pairManager, const BoundsIndex* remap0, const BoundsIndex* remap1, const Bp::FilterGroup::Enum* groups, const bool* lut) : +#endif + mPairManager (pairManager), +#ifdef BIP_VERSION_1 + mAggregate0 (aggregate0), + mAggregate1 (aggregate1), +#else + mRemap0 (remap0), + mRemap1 (remap1), +#endif + mGroups (groups), + mLUT (lut) + { + } + + PX_FORCE_INLINE void outputPair(PxU32 index0, PxU32 index1) + { +#ifdef BIP_VERSION_1 + const PxU32 aggIndex0 = mAggregate0->getAggregated(index0); + const PxU32 aggIndex1 = mAggregate1->getAggregated(index1); +#else + const PxU32 aggIndex0 = mRemap0[index0]; + const PxU32 aggIndex1 = mRemap1[index1]; +#endif + if(groupFiltering(mGroups[aggIndex0], mGroups[aggIndex1], mLUT)) + mPairManager->addPair(aggIndex0, aggIndex1); + } + + PairArray* mPairManager; +#ifdef BIP_VERSION_1 + Aggregate* mAggregate0; + Aggregate* mAggregate1; +#else + const BoundsIndex* mRemap0; + const BoundsIndex* mRemap1; +#endif + const Bp::FilterGroup::Enum* mGroups; + const bool* mLUT; + }; + +template +static void boxPruningKernel( PairArray* PX_RESTRICT pairManager, const bool* PX_RESTRICT lut, +#ifdef BIP_VERSION_1 + Aggregate* PX_RESTRICT aggregate0, Aggregate* PX_RESTRICT aggregate1, +#else + PxU32 nb0, const BoundsIndex* PX_RESTRICT remap0, const AABB_Xi* PX_RESTRICT boxes0_X, const AABB_YZ* PX_RESTRICT boxes0_YZ, + PxU32 nb1, const BoundsIndex* PX_RESTRICT remap1, const AABB_Xi* PX_RESTRICT boxes1_X, const AABB_YZ* PX_RESTRICT boxes1_YZ, +#endif + const Bp::FilterGroup::Enum* PX_RESTRICT groups) +{ +#ifdef BIP_VERSION_1 + outputPair_Bipartite pm(pairManager, aggregate0, aggregate1, groups, lut); + + const PxU32 nb0 = aggregate0->getNbAggregated(); + const PxU32 nb1 = aggregate1->getNbAggregated(); + const AABB_Xi* PX_RESTRICT boxes0_X = aggregate0->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes0_YZ = aggregate0->getBoundsYZ(); + const AABB_Xi* PX_RESTRICT boxes1_X = aggregate1->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes1_YZ = aggregate1->getBoundsYZ(); +#else +// outputPair_Bipartite pm(pairManager, aggregate0->getIndices(), aggregate1->getIndices(), groups, lut); + outputPair_Bipartite pm(pairManager, remap0, remap1, groups, lut); +#endif + + PxU32 index0 = 0; + PxU32 runningIndex1 = 0; + + while(runningIndex1(&boxes1_YZ[runningIndex1]); + const char* const CurrentBoxListX = reinterpret_cast(&boxes1_X[runningIndex1]); + + if(!gUnrollLoop) + { + while(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) + { + const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2); +#ifdef ABP_SIMD_OVERLAP + if(SIMD_OVERLAP_TEST_14a(box)) +#else + if(intersect2D(box0, *reinterpret_cast(box))) +#endif + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes1_X))>>3; + pm.outputPair(index0, Index1); + } + Offset += 8; + } + } + else + { + +#define BIP_VERSION4 +#ifdef BIP_VERSION4 +#ifdef ABP_SIMD_OVERLAP + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(SIMD_OVERLAP_TEST_14a(box)) \ + goto label; } +#else + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(intersect2D(box0, *reinterpret_cast(box))) \ + goto label; } +#endif + goto StartLoop4; + CODEALIGN16 +FoundOverlap3: + Offset += 8; + CODEALIGN16 +FoundOverlap2: + Offset += 8; + CODEALIGN16 +FoundOverlap1: + Offset += 8; + CODEALIGN16 +FoundOverlap0: + Offset += 8; + CODEALIGN16 +FoundOverlap: + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - 8 - reinterpret_cast(boxes1_X))>>3; + pm.outputPair(index0, Index1); + } + CODEALIGN16 +StartLoop4: + while(*reinterpret_cast(CurrentBoxListX + Offset + 8*5)<=maxLimit) + { + BLOCK4(0, FoundOverlap0) + BLOCK4(8, FoundOverlap1) + BLOCK4(16, FoundOverlap2) + BLOCK4(24, FoundOverlap3) + Offset += 40; + BLOCK4(-8, FoundOverlap) + } +#undef BLOCK4 +#endif + +#ifdef ABP_SIMD_OVERLAP + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(SIMD_OVERLAP_TEST_14a(reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto OverlapFound; \ + Offset += 8; +#else + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(intersect2D(box0, *reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto OverlapFound; \ + Offset += 8; +#endif + + goto LoopStart; + CODEALIGN16 +OverlapFound: + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes1_X))>>3; + pm.outputPair(index0, Index1); + } + Offset += 8; + CODEALIGN16 +LoopStart: + BLOCK + BLOCK + BLOCK + } + } + goto LoopStart; + } +#undef BLOCK + } + } + + index0++; + } +} + +static PX_FORCE_INLINE void doBipartiteBoxPruning_Leaf( + PairArray* PX_RESTRICT pairManager, const bool* PX_RESTRICT lut, + Aggregate* PX_RESTRICT aggregate0, Aggregate* PX_RESTRICT aggregate1, const Bp::FilterGroup::Enum* PX_RESTRICT groups) +{ +#ifdef BIP_VERSION_1 + boxPruningKernel<0>(pairManager, lut, aggregate0, aggregate1, groups); + boxPruningKernel<1>(pairManager, lut, aggregate1, aggregate0, groups); +#else + const PxU32 nb0 = aggregate0->getNbAggregated(); + const PxU32 nb1 = aggregate1->getNbAggregated(); + const BoundsIndex* PX_RESTRICT remap0 = aggregate0->getIndices(); + const BoundsIndex* PX_RESTRICT remap1 = aggregate1->getIndices(); + const AABB_Xi* PX_RESTRICT boxes0_X = aggregate0->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes0_YZ = aggregate0->getBoundsYZ(); + const AABB_Xi* PX_RESTRICT boxes1_X = aggregate1->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes1_YZ = aggregate1->getBoundsYZ(); + boxPruningKernel<0>(pairManager, lut, nb0, remap0, boxes0_X, boxes0_YZ, nb1, remap1, boxes1_X, boxes1_YZ, groups); + boxPruningKernel<1>(pairManager, lut, nb1, remap1, boxes1_X, boxes1_YZ, nb0, remap0, boxes0_X, boxes0_YZ, groups); +#endif +} + + struct outputPair_Complete + { + outputPair_Complete(PairArray* pairManager, Aggregate* aggregate, const Bp::FilterGroup::Enum* groups, const bool* lut) : + mPairManager (pairManager), + mAggregate (aggregate), + mGroups (groups), + mLUT (lut) + { + } + + PX_FORCE_INLINE void outputPair(PxU32 index0, PxU32 index1) + { + const PxU32 aggIndex0 = mAggregate->getAggregated(index0); + const PxU32 aggIndex1 = mAggregate->getAggregated(index1); + + if(groupFiltering(mGroups[aggIndex0], mGroups[aggIndex1], mLUT)) + mPairManager->addPair(aggIndex0, aggIndex1); + } + + PairArray* mPairManager; + Aggregate* mAggregate; + const Bp::FilterGroup::Enum* mGroups; + const bool* mLUT; + }; + +static void doCompleteBoxPruning_Leaf( PairArray* PX_RESTRICT pairManager, const bool* PX_RESTRICT lut, + Aggregate* PX_RESTRICT aggregate, const Bp::FilterGroup::Enum* PX_RESTRICT groups) +{ + outputPair_Complete pm(pairManager, aggregate, groups, lut); + const PxU32 nb = aggregate->getNbAggregated(); + const AABB_Xi* PX_RESTRICT boxes_X = aggregate->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes_YZ = aggregate->getBoundsYZ(); + + PxU32 index0 = 0; + PxU32 runningIndex = 0; + while(runningIndex(&boxes_YZ[runningIndex]); + const char* const CurrentBoxListX = reinterpret_cast(&boxes_X[runningIndex]); + + if(!gUnrollLoop) + { + while(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) + { + const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2); +#ifdef ABP_SIMD_OVERLAP + if(SIMD_OVERLAP_TEST_14a(box)) +#else + if(intersect2D(box0, *reinterpret_cast(box))) +#endif + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes_X))>>3; + pm.outputPair(index0, Index); + } + Offset += 8; + } + } + else + { + +#define VERSION4c +#ifdef VERSION4c +#define VERSION3 // Enable this as our safe loop +#ifdef ABP_SIMD_OVERLAP + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(SIMD_OVERLAP_TEST_14a(box)) \ + goto label; } +#else + #define BLOCK4(x, label) {const AABB_YZ* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(intersect2D(box0, *box)) \ + goto label; } +#endif + goto StartLoop4; + CODEALIGN16 +FoundOverlap3: + Offset += 8; + CODEALIGN16 +FoundOverlap2: + Offset += 8; + CODEALIGN16 +FoundOverlap1: + Offset += 8; + CODEALIGN16 +FoundOverlap0: + Offset += 8; + CODEALIGN16 +FoundOverlap: + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - 8 - reinterpret_cast(boxes_X))>>3; + pm.outputPair(index0, Index); + } + CODEALIGN16 +StartLoop4: + while(*reinterpret_cast(CurrentBoxListX + Offset + 8*5)<=maxLimit) + { + BLOCK4(0, FoundOverlap0) + BLOCK4(8, FoundOverlap1) + BLOCK4(16, FoundOverlap2) + BLOCK4(24, FoundOverlap3) + Offset += 40; + BLOCK4(-8, FoundOverlap) + } +#endif + +#define VERSION3 +#ifdef VERSION3 +#ifdef ABP_SIMD_OVERLAP + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(SIMD_OVERLAP_TEST_14a(reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto BeforeLoop; \ + Offset += 8; +#else + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(intersect2D(box0, *reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto BeforeLoop; \ + Offset += 8; +#endif + + goto StartLoop; + CODEALIGN16 +BeforeLoop: + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes_X))>>3; + pm.outputPair(index0, Index); + Offset += 8; + } + CODEALIGN16 +StartLoop: + BLOCK + BLOCK + BLOCK + BLOCK + BLOCK + } + } + } + } + goto StartLoop; + } +#endif + } + } + + index0++; + } +} + +///// + +class PersistentActorAggregatePair : public PersistentPairs +{ + public: + PersistentActorAggregatePair(Aggregate* aggregate, ShapeHandle actorHandle); + virtual ~PersistentActorAggregatePair() {} + + virtual void findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ); + virtual bool update(AABBManager& manager, BpCacheData* data); + + ShapeHandle mAggregateHandle; + ShapeHandle mActorHandle; + Aggregate* mAggregate; +}; + +PersistentActorAggregatePair::PersistentActorAggregatePair(Aggregate* aggregate, ShapeHandle actorHandle) : + mAggregateHandle (aggregate->mIndex), + mActorHandle (actorHandle), + mAggregate (aggregate) +{ +} + +void PersistentActorAggregatePair::findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) +{ + if(0) + { + Aggregate singleActor(INVALID_ID, false); + singleActor.addAggregated(mActorHandle); + singleActor.allocateBounds(); + singleActor.computeBounds(bounds, contactDistances); + singleActor.getSortedMinBounds(); + mAggregate->getSortedMinBounds(); + doBipartiteBoxPruning_Leaf(&pairs, lut, &singleActor, mAggregate, groups); + } + else + { + + mAggregate->getSortedMinBounds(); + const PxU32 nb0 = mAggregate->getNbAggregated(); + const BoundsIndex* PX_RESTRICT remap0 = mAggregate->getIndices(); + const AABB_Xi* PX_RESTRICT boxes0_X = mAggregate->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes0_YZ = mAggregate->getBoundsYZ(); + + PX_ALIGN(16, AABB_Xi inflatedBoundsX[1+NB_SENTINELS]); + PX_ALIGN(16, AABB_YZ inflatedBoundsYZ); + + // Compute bounds + { + PX_ALIGN(16, PxVec4) boxMin; + PX_ALIGN(16, PxVec4) boxMax; + const BoundsIndex actorHandle = mActorHandle; + const PxBounds3& b = bounds[actorHandle]; + const Vec4V offsetV = V4Load(contactDistances[actorHandle]); + const Vec4V minimumV = V4Sub(V4LoadU(&b.minimum.x), offsetV); + const Vec4V maximumV = V4Add(V4LoadU(&b.maximum.x), offsetV); + V4StoreA(minimumV, &boxMin.x); + V4StoreA(maximumV, &boxMax.x); + inflatedBoundsX[0].initFromPxVec4(boxMin, boxMax); + inflatedBoundsYZ.initFromPxVec4(boxMin, boxMax); + + for(PxU32 i=0;i(&pairs, lut, nb0, remap0, boxes0_X, boxes0_YZ, nb1, remap1, boxes1_X, boxes1_YZ, groups); + boxPruningKernel<1>(&pairs, lut, nb1, remap1, boxes1_X, boxes1_YZ, nb0, remap0, boxes0_X, boxes0_YZ, groups); + + } +} + +bool PersistentActorAggregatePair::update(AABBManager& manager, BpCacheData* data) +{ + if(mShouldBeDeleted || shouldPairBeDeleted(manager.mGroups, mAggregateHandle, mActorHandle)) + return true; + + if(!mAggregate->getNbAggregated()) // PT: needed with lazy empty actors + return true; + + if(mAggregate->isDirty() || manager.mChangedHandleMap.boundedTest(mActorHandle)) + manager.updatePairs(*this, data); + + return false; +} + +///// + +class PersistentAggregateAggregatePair : public PersistentPairs +{ + public: + PersistentAggregateAggregatePair(Aggregate* aggregate0, Aggregate* aggregate1); + virtual ~PersistentAggregateAggregatePair() {} + + virtual void findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ); + virtual bool update(AABBManager& manager, BpCacheData*); + + ShapeHandle mAggregateHandle0; + ShapeHandle mAggregateHandle1; + Aggregate* mAggregate0; + Aggregate* mAggregate1; +}; + +PersistentAggregateAggregatePair::PersistentAggregateAggregatePair(Aggregate* aggregate0, Aggregate* aggregate1) : + mAggregateHandle0 (aggregate0->mIndex), + mAggregateHandle1 (aggregate1->mIndex), + mAggregate0 (aggregate0), + mAggregate1 (aggregate1) +{ +} + +void PersistentAggregateAggregatePair::findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT /*bounds*/, const float* PX_RESTRICT /*contactDistances*/, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) +{ + mAggregate0->getSortedMinBounds(); + mAggregate1->getSortedMinBounds(); + doBipartiteBoxPruning_Leaf(&pairs, lut, mAggregate0, mAggregate1, groups); +} + +bool PersistentAggregateAggregatePair::update(AABBManager& manager, BpCacheData* data) +{ + if(mShouldBeDeleted || shouldPairBeDeleted(manager.mGroups, mAggregateHandle0, mAggregateHandle1)) + return true; + + if(!mAggregate0->getNbAggregated() || !mAggregate1->getNbAggregated()) // PT: needed with lazy empty actors + return true; + + if(mAggregate0->isDirty() || mAggregate1->isDirty()) + manager.updatePairs(*this, data); + + return false; +} + +///// + +class PersistentSelfCollisionPairs : public PersistentPairs +{ + public: + PersistentSelfCollisionPairs(Aggregate* aggregate); + virtual ~PersistentSelfCollisionPairs() {} + + virtual void findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ); + + Aggregate* mAggregate; +}; + +PersistentSelfCollisionPairs::PersistentSelfCollisionPairs(Aggregate* aggregate) : + mAggregate (aggregate) +{ +} + +void PersistentSelfCollisionPairs::findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT/*bounds*/, const float* PX_RESTRICT/*contactDistances*/, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) +{ + mAggregate->getSortedMinBounds(); + doCompleteBoxPruning_Leaf(&pairs, lut, mAggregate, groups); +} + +///// + +Aggregate::Aggregate(BoundsIndex index, bool selfCollisions) : + mIndex (index), + mInflatedBoundsX (NULL), + mInflatedBoundsYZ (NULL), + mAllocatedSize (0), + mDirtySort (false) +{ + resetDirtyState(); + mSelfCollisionPairs = selfCollisions ? PX_NEW(PersistentSelfCollisionPairs)(this) : NULL; +} + +Aggregate::~Aggregate() +{ + PX_FREE_AND_RESET(mInflatedBoundsYZ); + PX_FREE_AND_RESET(mInflatedBoundsX); + + if(mSelfCollisionPairs) + PX_DELETE_AND_RESET(mSelfCollisionPairs); +} + +void Aggregate::sortBounds() +{ + mDirtySort = false; + const PxU32 nbObjects = getNbAggregated(); + if(nbObjects<2) + return; + + { + PX_ALLOCA(minPosBounds, InflatedType, nbObjects+1); + bool alreadySorted = true; + InflatedType previousB = mInflatedBoundsX[0].mMinX; + minPosBounds[0] = previousB; + for(PxU32 i=1;i copy = mAggregated; + AABB_Xi* boundsXCopy = reinterpret_cast(PX_ALLOC(sizeof(AABB_Xi)*(nbObjects), "mInflatedBounds")); + AABB_YZ* boundsYZCopy = reinterpret_cast(PX_ALLOC(sizeof(AABB_YZ)*(nbObjects), "mInflatedBounds")); + PxMemCopy(boundsXCopy, mInflatedBoundsX, nbObjects*sizeof(AABB_Xi)); + PxMemCopy(boundsYZCopy, mInflatedBoundsYZ, nbObjects*sizeof(AABB_YZ)); + const PxU32* Sorted = mRS.GetRanks(); + for(PxU32 i=0;i copy = mAggregated; // PT: TODO: revisit this, avoid the copy like we do for the other buffers + AABB_Xi* sortedBoundsX = reinterpret_cast(PX_ALLOC(sizeof(AABB_Xi)*(nbObjects+NB_SENTINELS), "mInflatedBounds")); + AABB_YZ* sortedBoundsYZ = reinterpret_cast(PX_ALLOC(sizeof(AABB_YZ)*(nbObjects), "mInflatedBounds")); + + const PxU32* Sorted = mRS.GetRanks(); + for(PxU32 i=0;i(PX_ALLOC(sizeof(AABB_Xi)*(size+NB_SENTINELS), "mInflatedBounds")); + mInflatedBoundsYZ = reinterpret_cast(PX_ALLOC(sizeof(AABB_YZ)*(size), "mInflatedBounds")); + } +} + +void Aggregate::computeBounds(const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances) /*PX_RESTRICT*/ +{ +// PX_PROFILE_ZONE("Aggregate::computeBounds",0); + + const PxU32 size = getNbAggregated(); + PX_ASSERT(size); + + // PT: TODO: delay the conversion to integers until we sort (i.e. really need) the aggregated bounds? + PX_ALIGN(16, PxVec4) boxMin; + PX_ALIGN(16, PxVec4) boxMax; + + const PxU32 lookAhead = 4; + Vec4V minimumV; + Vec4V maximumV; + { + const BoundsIndex index0 = getAggregated(0); + const PxU32 last = PxMin(lookAhead, size-1); + for(PxU32 i=1;i<=last;i++) + { + const BoundsIndex index = getAggregated(i); + Ps::prefetchLine(bounds + index, 0); + Ps::prefetchLine(contactDistances + index, 0); + } + const PxBounds3& b = bounds[index0]; + const Vec4V offsetV = V4Load(contactDistances[index0]); + minimumV = V4Sub(V4LoadU(&b.minimum.x), offsetV); + maximumV = V4Add(V4LoadU(&b.maximum.x), offsetV); + V4StoreA(minimumV, &boxMin.x); + V4StoreA(maximumV, &boxMax.x); + mInflatedBoundsX[0].initFromPxVec4(boxMin, boxMax); + mInflatedBoundsYZ[0].initFromPxVec4(boxMin, boxMax); + } + + for(PxU32 i=1;i& contactDistance, + PxU32 maxNbAggregates, PxU32 maxNbShapes, Ps::VirtualAllocator& allocator, PxU64 contextID, + PxPairFilteringMode::Enum kineKineFilteringMode, PxPairFilteringMode::Enum staticKineFilteringMode) : + mPostBroadPhase2(contextID, *this), + mPostBroadPhase3(contextID, this, "AABBManager::postBroadPhaseStage3"), + mFinalizeUpdateTask (contextID), + mChangedHandleMap (allocator), + mGroups (allocator), + mContactDistance (contactDistance), + mVolumeData (PX_DEBUG_EXP("AABBManager::mVolumeData")), + mAddedHandles (allocator), + mUpdatedHandles (allocator), + mRemovedHandles (allocator), + mBroadPhase (bp), + mBoundsArray (boundsArray), + mOutOfBoundsObjects (PX_DEBUG_EXP("AABBManager::mOutOfBoundsObjects")), + mOutOfBoundsAggregates (PX_DEBUG_EXP("AABBManager::mOutOfBoundsAggregates")), + //mCreatedOverlaps { Ps::Array(PX_DEBUG_EXP("AABBManager::mCreatedOverlaps")) }, + //mDestroyedOverlaps { Ps::Array(PX_DEBUG_EXP("AABBManager::mDestroyedOverlaps")) }, + mScratchAllocator (NULL), + mNarrowPhaseUnblockTask (NULL), + mUsedSize (0), + mOriginShifted (false), + mPersistentStateChanged (true), + mNbAggregates (0), + mFirstFreeAggregate (PX_INVALID_U32), + mTimestamp (0), +#ifdef BP_USE_AGGREGATE_GROUP_TAIL + mAggregateGroupTide (PxU32(Bp::FilterGroup::eAGGREGATE_BASE)), +#endif + mContextID (contextID) +{ + PX_UNUSED(maxNbAggregates); // PT: TODO: use it or remove it + reserveShapeSpace(PxMax(maxNbShapes, 1u)); + +// mCreatedOverlaps.reserve(16000); +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + { + const bool discardKineKine = kineKineFilteringMode==PxPairFilteringMode::eKILL ? true : false; + const bool discardStaticKine = staticKineFilteringMode==PxPairFilteringMode::eKILL ? true : false; + + for(int j=0;jsecond); +} + +void AABBManager::destroy() +{ + releasePairs(mActorAggregatePairs); + releasePairs(mAggregateAggregatePairs); + + const PxU32 nb = mAggregates.size(); + for(PxU32 i=0;i(mAggregates[currentFree])); + } + + if(!found) + { + Aggregate* a = mAggregates[i]; + PX_DELETE(a); + } + } + + BpCacheData* entry = static_cast(mBpThreadContextPool.pop()); + while (entry) + { + entry->~BpCacheData(); + PX_FREE(entry); + entry = static_cast(mBpThreadContextPool.pop()); + } + + PX_DELETE(this); +} + +/*bool AABBManager::checkID(ShapeHandle id) +{ + for(AggPairMap::Iterator iter = mActorAggregatePairs.getIterator(); !iter.done(); ++iter) + { + PersistentActorAggregatePair* p = static_cast(iter->second); + if(p->mActorHandle==id || p->mAggregateHandle==id) + return false; + } + + for(AggPairMap::Iterator iter = mAggregateAggregatePairs.getIterator(); !iter.done(); ++iter) + { + PersistentAggregateAggregatePair* p = static_cast(iter->second); + if(p->mAggregateHandle0==id || p->mAggregateHandle1==id) + return false; + } + return true; +}*/ + +static void removeAggregateFromDirtyArray(Aggregate* aggregate, Ps::Array& dirtyAggregates) +{ + // PT: TODO: do this lazily like for interactions? + if(aggregate->isDirty()) + { + const PxU32 dirtyIndex = aggregate->mDirtyIndex; + PX_ASSERT(dirtyAggregates[dirtyIndex]==aggregate); + dirtyAggregates.replaceWithLast(dirtyIndex); + if(dirtyIndexmDirtyIndex = dirtyIndex; + aggregate->resetDirtyState(); + } + else + { + PX_ASSERT(!dirtyAggregates.findAndReplaceWithLast(aggregate)); + } +} + +void AABBManager::reserveSpaceForBounds(BoundsIndex index) +{ + if ((index+1) >= mVolumeData.size()) + reserveShapeSpace(index+1); + + resetEntry(index); //KS - make sure this entry is flagged as invalid +} + +// PT: TODO: what is the "userData" here? +bool AABBManager::addBounds(BoundsIndex index, PxReal contactDistance, Bp::FilterGroup::Enum group, void* userData, AggregateHandle aggregateHandle, ElementType::Enum volumeType) +{ +// PX_ASSERT(checkID(index)); + + initEntry(index, contactDistance, group, userData); + mVolumeData[index].setVolumeType(volumeType); + + if(aggregateHandle==PX_INVALID_U32) + { + mVolumeData[index].setSingleActor(); + + addBPEntry(index); + + mPersistentStateChanged = true; + } + else + { +#if PX_CHECKED + if(aggregateHandle>=mAggregates.size()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "AABBManager::addBounds - aggregateId out of bounds\n"); + return false; + } + +/* { + PxU32 firstFreeAggregate = mFirstFreeAggregate; + while(firstFreeAggregate!=PX_INVALID_U32) + { + if(firstFreeAggregate==aggregateHandle) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "AABBManager::destroyAggregate - aggregate has already been removed\n"); + return BP_INVALID_BP_HANDLE; + } + firstFreeAggregate = PxU32(reinterpret_cast(mAggregates[firstFreeAggregate])); + } + }*/ +#endif + mVolumeData[index].setAggregated(aggregateHandle); + + mPersistentStateChanged = true; // PT: TODO: do we need this here? + + Aggregate* aggregate = getAggregateFromHandle(aggregateHandle); + + { + // PT: schedule the aggregate for BP insertion here, if we just added its first shape + if(!aggregate->getNbAggregated()) + addBPEntry(aggregate->mIndex); + + aggregate->addAggregated(index); + + // PT: new actor added to aggregate => mark dirty to recompute bounds later + aggregate->markAsDirty(mDirtyAggregates); + } + } + + // PT: TODO: remove or use this return value. Currently useless since always true. Gives birth to unreachable code in callers. + return true; +} + +void AABBManager::removeBounds(BoundsIndex index) +{ + // PT: TODO: shouldn't it be compared to mUsedSize? + PX_ASSERT(index < mVolumeData.size()); + + if(mVolumeData[index].isSingleActor()) + { + removeBPEntry(index); + + mPersistentStateChanged = true; + } + else + { + PX_ASSERT(mVolumeData[index].isAggregated()); + + const AggregateHandle aggregateHandle = mVolumeData[index].getAggregateOwner(); + Aggregate* aggregate = getAggregateFromHandle(aggregateHandle); + bool status = aggregate->removeAggregated(index); + (void)status; +// PX_ASSERT(status); // PT: can be false when >128 shapes + + // PT: remove empty aggregates, otherwise the BP will crash with empty bounds + if(!aggregate->getNbAggregated()) + { + removeBPEntry(aggregate->mIndex); + removeAggregateFromDirtyArray(aggregate, mDirtyAggregates); + } + else + aggregate->markAsDirty(mDirtyAggregates); // PT: actor removed from aggregate => mark dirty to recompute bounds later + + mPersistentStateChanged = true; // PT: TODO: do we need this here? + } + + resetEntry(index); +} + +// PT: TODO: the userData is actually a PxAggregate pointer. Maybe we could expose/use that. +AggregateHandle AABBManager::createAggregate(BoundsIndex index, Bp::FilterGroup::Enum group, void* userData, const bool selfCollisions) +{ +// PX_ASSERT(checkID(index)); + + Aggregate* aggregate = PX_NEW(Aggregate)(index, selfCollisions); + + AggregateHandle handle; + if(mFirstFreeAggregate==PX_INVALID_U32) + { + handle = mAggregates.size(); + mAggregates.pushBack(aggregate); + } + else + { + handle = mFirstFreeAggregate; + mFirstFreeAggregate = PxU32(reinterpret_cast(mAggregates[mFirstFreeAggregate])); + mAggregates[handle] = aggregate; + } + +#ifdef BP_USE_AGGREGATE_GROUP_TAIL +/* #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + PxU32 id = index; + id<<=2; + id|=FilterType::AGGREGATE; + initEntry(index, 0.0f, Bp::FilterGroup::Enum(id), userData); + #endif*/ + initEntry(index, 0.0f, getAggregateGroup(), userData); + PX_UNUSED(group); +#else + initEntry(index, 0.0f, group, userData); +#endif + + mVolumeData[index].setAggregate(handle); + + mBoundsArray.setBounds(PxBounds3::empty(), index); // PT: no need to set mPersistentStateChanged since "setBounds" already does something similar + + mNbAggregates++; + + // PT: we don't add empty aggregates to mAddedHandleMap yet, since they make the BP crash. + return handle; +} + +bool AABBManager::destroyAggregate(BoundsIndex& index_, Bp::FilterGroup::Enum& group_, AggregateHandle aggregateHandle) +{ +#if PX_CHECKED + if(aggregateHandle>=mAggregates.size()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "AABBManager::destroyAggregate - aggregateId out of bounds\n"); + return false; + } + + { + PxU32 firstFreeAggregate = mFirstFreeAggregate; + while(firstFreeAggregate!=PX_INVALID_U32) + { + if(firstFreeAggregate==aggregateHandle) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "AABBManager::destroyAggregate - aggregate has already been removed\n"); + return false; + } + firstFreeAggregate = PxU32(reinterpret_cast(mAggregates[firstFreeAggregate])); + } + } +#endif + + Aggregate* aggregate = getAggregateFromHandle(aggregateHandle); + +#if PX_CHECKED + if(aggregate->getNbAggregated()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "AABBManager::destroyAggregate - aggregate still has bounds that needs removed\n"); + return false; + } +#endif + + const BoundsIndex index = aggregate->mIndex; + removeAggregateFromDirtyArray(aggregate, mDirtyAggregates); + + if(mAddedHandleMap.test(index)) // PT: if object had been added this frame... + mAddedHandleMap.reset(index); // PT: ...then simply revert the previous operation locally (it hasn't been passed to the BP yet). + else if(aggregate->getNbAggregated()) // PT: else we need to remove it from the BP if it has been added there. If there's no aggregated + mRemovedHandleMap.set(index); // PT: shapes then the aggregate has never been added, or already removed. + + PX_DELETE(aggregate); + mAggregates[aggregateHandle] = reinterpret_cast(size_t(mFirstFreeAggregate)); + mFirstFreeAggregate = aggregateHandle; + + // PT: TODO: shouldn't it be compared to mUsedSize? + PX_ASSERT(index < mVolumeData.size()); + + index_ = index; + group_ = mGroups[index]; + +#ifdef BP_USE_AGGREGATE_GROUP_TAIL + releaseAggregateGroup(mGroups[index]); +#endif + resetEntry(index); + + mPersistentStateChanged = true; + + PX_ASSERT(mNbAggregates); + mNbAggregates--; + + return true; +} + +void AABBManager::handleOriginShift() +{ + mOriginShifted = false; + mPersistentStateChanged = true; + // PT: TODO: isn't the following loop potentially updating removed objects? + // PT: TODO: check that aggregates code is correct here + for(PxU32 i=0; igetNbAggregated()) + { + aggregate->markAsDirty(mDirtyAggregates); + aggregate->allocateBounds(); + aggregate->computeBounds(mBoundsArray.begin(), mContactDistance.begin()); + mBoundsArray.begin()[aggregate->mIndex] = aggregate->getMergedBounds(); + if(!mAddedHandleMap.test(i)) + mUpdatedHandles.pushBack(i); // PT: TODO: BoundsIndex-to-ShapeHandle confusion here + } + } + } + } +} + +void AggregateBoundsComputationTask::runInternal() +{ + const BoundsArray& boundArray = mManager->getBoundsArray(); + const float* contactDistances = mManager->getContactDistances(); + + PxU32 size = mNbToGo; + Aggregate** currentAggregate = mAggregates + mStart; + while(size--) + { + if(size) + { + Aggregate* nextAggregate = *(currentAggregate+1); + Ps::prefetchLine(nextAggregate, 0); + Ps::prefetchLine(nextAggregate, 64); + } + + (*currentAggregate)->computeBounds(boundArray.begin(), contactDistances); + currentAggregate++; + } +} + +void FinalizeUpdateTask::runInternal() +{ + mManager->finalizeUpdate(mNumCpuTasks, mScratchAllocator, getContinuation(), mNarrowPhaseUnlockTask); +} + +void AABBManager::startAggregateBoundsComputationTasks(PxU32 nbToGo, PxU32 numCpuTasks, Cm::FlushPool& flushPool) +{ + const PxU32 nbAggregatesPerTask = nbToGo > numCpuTasks ? nbToGo / numCpuTasks : nbToGo; + + // PT: TODO: better load balancing + + PxU32 start = 0; + while(nbToGo) + { + AggregateBoundsComputationTask* T = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(AggregateBoundsComputationTask)), AggregateBoundsComputationTask(mContextID)); + + const PxU32 nb = nbToGo < nbAggregatesPerTask ? nbToGo : nbAggregatesPerTask; + T->Init(this, start, nb, mDirtyAggregates.begin()); + start += nb; + nbToGo -= nb; + + T->setContinuation(&mFinalizeUpdateTask); + T->removeReference(); + } +} + +void AABBManager::updateAABBsAndBP(PxU32 numCpuTasks, Cm::FlushPool& flushPool, PxcScratchAllocator* scratchAllocator, bool hasContactDistanceUpdated, PxBaseTask* continuation, PxBaseTask* narrowPhaseUnlockTask) +{ + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP", getContextId()); + + mPersistentStateChanged = mPersistentStateChanged || hasContactDistanceUpdated; + + mScratchAllocator = scratchAllocator; + mNarrowPhaseUnblockTask = narrowPhaseUnlockTask; + + const bool singleThreaded = gSingleThreaded || numCpuTasks<2; + if(!singleThreaded) + { + PX_ASSERT(numCpuTasks); + mFinalizeUpdateTask.Init(this, numCpuTasks, scratchAllocator, narrowPhaseUnlockTask); + mFinalizeUpdateTask.setContinuation(continuation); + } + + // Add + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - add", getContextId()); + + resetOrClear(mAddedHandles); + + const PxU32* bits = mAddedHandleMap.getWords(); + if(bits) + { + const PxU32 lastSetBit = mAddedHandleMap.findLast(); + for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) + { + for(PxU32 b = bits[w]; b; b &= b-1) + { + const BoundsIndex handle = PxU32(w<<5|Ps::lowestSetBit(b)); + PX_ASSERT(!mVolumeData[handle].isAggregated()); + mAddedHandles.pushBack(handle); // PT: TODO: BoundsIndex-to-ShapeHandle confusion here + } + } + } + } + + // Update + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - update", getContextId()); + + resetOrClear(mUpdatedHandles); + if(!mOriginShifted) + { + // PT: TODO: + // - intercept calls marking aggregateD shapes dirty, in order to mark their aggregates dirty at the same time. That way we don't discover + // them while parsing the map, i.e. the map is already fully complete when the parsing begins (no need to parse twice etc). + // - once this is done, aggregateD shapes can be ignored during parsing (since we only needed them to go to their aggregates) + // - we can then handle aggregates while parsing the map, i.e. there's no need for sorting anymore. + // - there will be some thoughts to do about the dirty aggregates coming from the added map parsing: we still need to compute their bounds, + // but we don't want to add these to mUpdatedHandles (since they're already in mAddedHandles) + // - we still need the set of dirty aggregates post broadphase, but we don't want to re-parse the full map for self-collisions. So we may still + // need to put dirty aggregates in an array, but that might be simplified now + // - the 'isDirty' checks to updatePairs can use the update map though - but the boundedTest is probably more expensive than the current test + + // PT: TODO: another idea: just output all aggregate handles by default then have a pass on mUpdatedHandles to remove them if that wasn't actually necessary + // ...or just drop the artificial requirement for aggregates... + + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - update - bitmap iteration", getContextId()); + + const PxU32* bits = mChangedHandleMap.getWords(); + if(bits) + { + const PxU32 lastSetBit = mChangedHandleMap.findLast(); + for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) + { + for(PxU32 b = bits[w]; b; b &= b-1) + { + const BoundsIndex handle = PxU32(w<<5|Ps::lowestSetBit(b)); + PX_ASSERT(!mRemovedHandleMap.test(handle)); // a handle may only be updated and deleted if it was just added. + PX_ASSERT(!mVolumeData[handle].isAggregate()); // PT: make sure changedShapes doesn't contain aggregates + + if(mAddedHandleMap.test(handle)) // just-inserted handles may also be marked updated, so skip them + continue; + + if(mVolumeData[handle].isSingleActor()) + { + PX_ASSERT(mGroups[handle] != Bp::FilterGroup::eINVALID); + mUpdatedHandles.pushBack(handle); // PT: TODO: BoundsIndex-to-ShapeHandle confusion here + } + else + { + PX_ASSERT(mVolumeData[handle].isAggregated()); + const AggregateHandle aggregateHandle = mVolumeData[handle].getAggregateOwner(); + Aggregate* aggregate = getAggregateFromHandle(aggregateHandle); + // PT: an actor from the aggregate has been updated => mark dirty to recompute bounds later + // PT: we don't recompute the bounds right away since multiple actors from the same aggregate may have changed. + aggregate->markAsDirty(mDirtyAggregates); + } + } + } + } + } + + const PxU32 size = mDirtyAggregates.size(); + if(size) + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - update - dirty iteration", getContextId()); + for(PxU32 i=0;iallocateBounds(); + if(singleThreaded) + { + aggregate->computeBounds(mBoundsArray.begin(), mContactDistance.begin()); + mBoundsArray.begin()[aggregate->mIndex] = aggregate->getMergedBounds(); + } + + // PT: Can happen when an aggregate has been created and then its actors have been changed (with e.g. setLocalPose) + // before a BP call. + if(!mAddedHandleMap.test(aggregate->mIndex)) + mUpdatedHandles.pushBack(aggregate->mIndex); // PT: TODO: BoundsIndex-to-ShapeHandle confusion here + } + + if(!singleThreaded) + startAggregateBoundsComputationTasks(size, numCpuTasks, flushPool); + + // PT: we're already sorted if no dirty-aggregates are involved + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - update - sort", getContextId()); + + mPersistentStateChanged = true; // PT: was previously set multiple times within 'computeBounds' + // PT: TODO: remove this + Ps::sort(mUpdatedHandles.begin(), mUpdatedHandles.size()); + } + } + } + else + { + handleOriginShift(); + } + } + + // Remove + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - remove", getContextId()); + + resetOrClear(mRemovedHandles); + + const PxU32* bits = mRemovedHandleMap.getWords(); + if(bits) + { + const PxU32 lastSetBit = mRemovedHandleMap.findLast(); + for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) + { + for(PxU32 b = bits[w]; b; b &= b-1) + { + const BoundsIndex handle = PxU32(w<<5|Ps::lowestSetBit(b)); + PX_ASSERT(!mVolumeData[handle].isAggregated()); + mRemovedHandles.pushBack(handle); // PT: TODO: BoundsIndex-to-ShapeHandle confusion here + } + } + } + } + + ///// + + // PT: TODO: do we need to run these threads when we origin-shifted everything before? + if(singleThreaded) + finalizeUpdate(numCpuTasks, scratchAllocator, continuation, narrowPhaseUnlockTask); + else + mFinalizeUpdateTask.removeReference(); +} + +void AABBManager::finalizeUpdate(PxU32 numCpuTasks, PxcScratchAllocator* scratchAllocator, PxBaseTask* continuation, PxBaseTask* narrowPhaseUnlockTask) +{ + PX_PROFILE_ZONE("AABBManager::finalizeUpdate", getContextId()); + + const bool singleThreaded = gSingleThreaded || numCpuTasks<2; + if(!singleThreaded) + { + const PxU32 size = mDirtyAggregates.size(); + for(PxU32 i=0;imIndex] = aggregate->getMergedBounds(); + } + } + + const BroadPhaseUpdateData updateData( mAddedHandles.begin(), mAddedHandles.size(), + mUpdatedHandles.begin(), mUpdatedHandles.size(), + mRemovedHandles.begin(), mRemovedHandles.size(), + mBoundsArray.begin(), mGroups.begin(), +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + &mLUT[0][0], +#endif + mContactDistance.begin(), mBoundsArray.getCapacity(), + mPersistentStateChanged || mBoundsArray.hasChanged()); + mPersistentStateChanged = false; + + PX_ASSERT(updateData.isValid()); + + //KS - skip broad phase if there are no updated shapes. + if (updateData.getNumCreatedHandles() != 0 || updateData.getNumRemovedHandles() != 0 || updateData.getNumUpdatedHandles() != 0) + mBroadPhase.update(numCpuTasks, scratchAllocator, updateData, continuation, narrowPhaseUnlockTask); + else + narrowPhaseUnlockTask->removeReference(); +} + +static PX_FORCE_INLINE void createOverlap(Ps::Array* overlaps, const Ps::Array& volumeData, PxU32 id0, PxU32 id1) +{ +// overlaps.pushBack(AABBOverlap(volumeData[id0].userData, volumeData[id1].userData, handle)); + const ElementType::Enum volumeType = PxMax(volumeData[id0].getVolumeType(), volumeData[id1].getVolumeType()); + overlaps[volumeType].pushBack(AABBOverlap(reinterpret_cast(size_t(id0)), reinterpret_cast(size_t(id1)))); +} + +static PX_FORCE_INLINE void deleteOverlap(Ps::Array* overlaps, const Ps::Array& volumeData, PxU32 id0, PxU32 id1) +{ +// PX_ASSERT(volumeData[id0].userData); +// PX_ASSERT(volumeData[id1].userData); + if (volumeData[id0].getUserData() && volumeData[id1].getUserData()) // PT: TODO: no idea if this is the right thing to do or if it's normal to get null ptrs here + { + const ElementType::Enum volumeType = PxMax(volumeData[id0].getVolumeType(), volumeData[id1].getVolumeType()); +// overlaps.pushBack(AABBOverlap(volumeData[id0].userData, volumeData[id1].userData, handle)); + overlaps[volumeType].pushBack(AABBOverlap(reinterpret_cast(size_t(id0)), reinterpret_cast(size_t(id1)))); + } +} + +void PersistentPairs::outputDeletedOverlaps(Ps::Array* overlaps, const Ps::Array& volumeData) +{ + const PxU32 nbActivePairs = mPM.mNbActivePairs; + for(PxU32 i=0;i& volumeData, Ps::Array* createdOverlaps, Ps::Array* destroyedOverlaps) +{ + if(mTimestamp==timestamp) + return; + + mTimestamp = timestamp; + +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + findOverlaps(mPM, bounds, contactDistances, groups, lut); +#else + findOverlaps(mPM, bounds, contactDistances, groups); +#endif + + PxU32 i=0; + PxU32 nbActivePairs = mPM.mNbActivePairs; + while(imIndex==aggregateHandle); + return PX_NEW(PersistentActorAggregatePair)(aggregate, actorHandle); // PT: TODO: use a pool or something +} + +PersistentAggregateAggregatePair* AABBManager::createPersistentAggregateAggregatePair(ShapeHandle volA, ShapeHandle volB) +{ + PX_ASSERT(mVolumeData[volA].isAggregate()); + PX_ASSERT(mVolumeData[volB].isAggregate()); + const AggregateHandle h0 = mVolumeData[volA].getAggregate(); + const AggregateHandle h1 = mVolumeData[volB].getAggregate(); + Aggregate* aggregate0 = getAggregateFromHandle(h0); + Aggregate* aggregate1 = getAggregateFromHandle(h1); + PX_ASSERT(aggregate0->mIndex==volA); + PX_ASSERT(aggregate1->mIndex==volB); + return PX_NEW(PersistentAggregateAggregatePair)(aggregate0, aggregate1); // PT: TODO: use a pool or something +} + +void AABBManager::updatePairs(PersistentPairs& p, BpCacheData* data) +{ + if (data) + { +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + p.updatePairs(mTimestamp, mBoundsArray.begin(), mContactDistance.begin(), mGroups.begin(), &mLUT[0][0], mVolumeData, data->mCreatedPairs, data->mDeletedPairs); +#else + p.updatePairs(mTimestamp, mBoundsArray.begin(), mContactDistance.begin(), mGroups.begin(), mVolumeData, data->mCreatedPairs, data->mDeletedPairs); +#endif + } + else + { +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + p.updatePairs(mTimestamp, mBoundsArray.begin(), mContactDistance.begin(), mGroups.begin(), &mLUT[0][0], mVolumeData, mCreatedOverlaps, mDestroyedOverlaps); +#else + p.updatePairs(mTimestamp, mBoundsArray.begin(), mContactDistance.begin(), mGroups.begin(), mVolumeData, mCreatedOverlaps, mDestroyedOverlaps); +#endif + } +} + +void AABBManager::processBPCreatedPair(const BroadPhasePair& pair) +{ + PX_ASSERT(!mVolumeData[pair.mVolA].isAggregated()); + PX_ASSERT(!mVolumeData[pair.mVolB].isAggregated()); + const bool isSingleActorA = mVolumeData[pair.mVolA].isSingleActor(); + const bool isSingleActorB = mVolumeData[pair.mVolB].isSingleActor(); + + if(isSingleActorA && isSingleActorB) + { + createOverlap(mCreatedOverlaps, mVolumeData, pair.mVolA, pair.mVolB); // PT: regular actor-actor pair + return; + } + + // PT: TODO: check if this is needed + ShapeHandle volA = pair.mVolA; + ShapeHandle volB = pair.mVolB; + if(volBinsert(AggPair(volA, volB), newPair); + PX_UNUSED(status); + PX_ASSERT(status); + updatePairs(*newPair); +} + +void AABBManager::processBPDeletedPair(const BroadPhasePair& pair) +{ + PX_ASSERT(!mVolumeData[pair.mVolA].isAggregated()); + PX_ASSERT(!mVolumeData[pair.mVolB].isAggregated()); + const bool isSingleActorA = mVolumeData[pair.mVolA].isSingleActor(); + const bool isSingleActorB = mVolumeData[pair.mVolB].isSingleActor(); + + if(isSingleActorA && isSingleActorB) + { + deleteOverlap(mDestroyedOverlaps, mVolumeData, pair.mVolA, pair.mVolB); // PT: regular actor-actor pair + return; + } + + // PT: TODO: check if this is needed + ShapeHandle volA = pair.mVolA; + ShapeHandle volB = pair.mVolB; + if(volBfind(AggPair(volA, volB)); + PX_ASSERT(e); + p = e->second; + } + + p->outputDeletedOverlaps(mDestroyedOverlaps, mVolumeData); + p->mShouldBeDeleted = true; +} + +struct CreatedPairHandler +{ + static PX_FORCE_INLINE void processPair(AABBManager& manager, const BroadPhasePair& pair) { manager.processBPCreatedPair(pair); } +}; + +struct DeletedPairHandler +{ + static PX_FORCE_INLINE void processPair(AABBManager& manager, const BroadPhasePair& pair) { manager.processBPDeletedPair(pair); } +}; + +template +static void processBPPairs(PxU32 nbPairs, const BroadPhasePair* pairs, AABBManager& manager) +{ + // PT: TODO: figure out this ShapeHandle/BpHandle thing. Is it ok to use "BP_INVALID_BP_HANDLE" for a "ShapeHandle"? + ShapeHandle previousA = BP_INVALID_BP_HANDLE; + ShapeHandle previousB = BP_INVALID_BP_HANDLE; + + while(nbPairs--) + { + PX_ASSERT(pairs->mVolA!=BP_INVALID_BP_HANDLE); + PX_ASSERT(pairs->mVolB!=BP_INVALID_BP_HANDLE); + // PT: TODO: why is that test needed now? GPU broadphase? + if(pairs->mVolA != previousA || pairs->mVolB != previousB) + { + previousA = pairs->mVolA; + previousB = pairs->mVolB; + FunctionT::processPair(manager, *pairs); + } + pairs++; + } +} + +static void processAggregatePairs(AggPairMap& map, AABBManager& manager) +{ + // PT: TODO: hmmm we have a list of dirty aggregates but we don't have a list of dirty pairs. + // PT: not sure how the 3.4 trunk solves this but let's just iterate all pairs for now + // PT: atm we reuse this loop to delete removed interactions + // PT: TODO: in fact we could handle all the "lost pairs" stuff right there with extra aabb-abb tests + + // PT: TODO: replace with decent hash map - or remove the hashmap entirely and use a linear array + Ps::Array removedEntries; + for(AggPairMap::Iterator iter = map.getIterator(); !iter.done(); ++iter) + { + PersistentPairs* p = iter->second; + if(p->update(manager)) + { + removedEntries.pushBack(iter->first); + PX_DELETE(p); + } + } + for(PxU32 i=0;i* mArray; + PxU32 mStartIdx; + PxU32 mCount; + + PairData() : mArray(NULL), mStartIdx(0), mCount(0) + { + } +}; + +class ProcessAggPairsBase : public Cm::Task +{ +public: + static const PxU32 MaxPairs = 16; + + PairData mCreatedPairs[2]; + PairData mDestroyedPairs[2]; + + ProcessAggPairsBase(PxU64 contextID) : Cm::Task(contextID) + { + } + + void setCache(BpCacheData& data) + { + for (PxU32 i = 0; i < 2; ++i) + { + mCreatedPairs[i].mArray = &data.mCreatedPairs[i]; + mCreatedPairs[i].mStartIdx = data.mCreatedPairs[i].size(); + mDestroyedPairs[i].mArray = &data.mDeletedPairs[i]; + mDestroyedPairs[i].mStartIdx = data.mDeletedPairs[i].size(); + } + } + + void updateCounters() + { + for (PxU32 i = 0; i < 2; ++i) + { + mCreatedPairs[i].mCount = mCreatedPairs[i].mArray->size() - mCreatedPairs[i].mStartIdx; + mDestroyedPairs[i].mCount = mDestroyedPairs[i].mArray->size() - mDestroyedPairs[i].mStartIdx; + } + } +}; + + +class ProcessAggPairsParallelTask : public ProcessAggPairsBase +{ +public: + PersistentPairs* mPersistentPairs[MaxPairs]; + Bp::AggPair mAggPairs[MaxPairs]; + PxU32 mNbPairs; + AABBManager* mManager; + AggPairMap* mMap; + Ps::Mutex* mMutex; + const char* mName; + + ProcessAggPairsParallelTask(PxU64 contextID, Ps::Mutex* mutex, AABBManager* manager, AggPairMap* map, const char* name) : ProcessAggPairsBase(contextID), + mNbPairs(0), mManager(manager), mMap(map), mMutex(mutex), mName(name) + { + } + + + void runInternal() + { + BpCacheData* data = mManager->getBpCacheData(); + + setCache(*data); + + + Ps::InlineArray removedEntries; + for (PxU32 i = 0; i < mNbPairs; ++i) + { + if (mPersistentPairs[i]->update(*mManager, data)) + { + removedEntries.pushBack(mAggPairs[i]); + PX_DELETE(mPersistentPairs[i]); + } + } + + updateCounters(); + + mManager->putBpCacheData(data); + + + if (removedEntries.size()) + { + Ps::Mutex::ScopedLock lock(*mMutex); + for (PxU32 i = 0; i < removedEntries.size(); i++) + { + bool status = mMap->erase(removedEntries[i]); + PX_ASSERT(status); + PX_UNUSED(status); + } + } + + + } + + virtual const char* getName() const { return mName; } + +}; + +class SortAggregateBoundsParallel : public Cm::Task +{ +public: + static const PxU32 MaxPairs = 16; + Aggregate** mAggregates; + PxU32 mNbAggs; + + SortAggregateBoundsParallel(PxU64 contextID, Aggregate** aggs, PxU32 nbAggs) : Cm::Task(contextID), + mAggregates(aggs), mNbAggs(nbAggs) + { + } + + + void runInternal() + { + PX_PROFILE_ZONE("SortBounds", mContextID); + for (PxU32 i = 0; i < mNbAggs; i++) + { + Aggregate* aggregate = mAggregates[i]; + + aggregate->getSortedMinBounds(); + } + } + + virtual const char* getName() const { return "SortAggregateBoundsParallel"; } + +}; + + +class ProcessSelfCollisionPairsParallel : public ProcessAggPairsBase +{ +public: + Aggregate** mAggregates; + PxU32 mNbAggs; + AABBManager* mManager; + + ProcessSelfCollisionPairsParallel(PxU64 contextID, Aggregate** aggs, PxU32 nbAggs, AABBManager* manager) : ProcessAggPairsBase(contextID), + mAggregates(aggs), mNbAggs(nbAggs), mManager(manager) + { + } + + + void runInternal() + { + BpCacheData* data = mManager->getBpCacheData(); + setCache(*data); + PX_PROFILE_ZONE("ProcessSelfCollisionPairs", mContextID); + for (PxU32 i = 0; i < mNbAggs; i++) + { + Aggregate* aggregate = mAggregates[i]; + + if (aggregate->mSelfCollisionPairs) + mManager->updatePairs(*aggregate->mSelfCollisionPairs, data); + } + updateCounters(); + mManager->putBpCacheData(data); + } + + virtual const char* getName() const { return "ProcessSelfCollisionPairsParallel"; } + +}; + +static void processAggregatePairsParallel(AggPairMap& map, AABBManager& manager, Cm::FlushPool& flushPool, + PxBaseTask* continuation, const char* taskName, Ps::Array& pairTasks) +{ + // PT: TODO: hmmm we have a list of dirty aggregates but we don't have a list of dirty pairs. + // PT: not sure how the 3.4 trunk solves this but let's just iterate all pairs for now + // PT: atm we reuse this loop to delete removed interactions + // PT: TODO: in fact we could handle all the "lost pairs" stuff right there with extra aabb-abb tests + + // PT: TODO: replace with decent hash map - or remove the hashmap entirely and use a linear array + + manager.mMapLock.lock(); + + ProcessAggPairsParallelTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ProcessAggPairsParallelTask)), ProcessAggPairsParallelTask)(0, &manager.mMapLock, &manager, &map, taskName); + + PxU32 startIdx = pairTasks.size(); + + for (AggPairMap::Iterator iter = map.getIterator(); !iter.done(); ++iter) + { + task->mAggPairs[task->mNbPairs] = iter->first; + task->mPersistentPairs[task->mNbPairs++] = iter->second; + if (task->mNbPairs == ProcessAggPairsParallelTask::MaxPairs) + { + pairTasks.pushBack(task); + task->setContinuation(continuation); + task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ProcessAggPairsParallelTask)), ProcessAggPairsParallelTask)(0, &manager.mMapLock, &manager, &map, taskName); + } + } + + manager.mMapLock.unlock(); + + for (PxU32 i = startIdx; i < pairTasks.size(); ++i) + { + pairTasks[i]->removeReference(); + } + + if (task->mNbPairs) + { + pairTasks.pushBack(task); + task->setContinuation(continuation); + task->removeReference(); + //task->runInternal(); + } +} + +void AABBManager::postBroadPhase(PxBaseTask* continuation, PxBaseTask* narrowPhaseUnlockTask, Cm::FlushPool& flushPool) +{ + PX_PROFILE_ZONE("AABBManager::postBroadPhase", getContextId()); + + //KS - There is a continuation task for discrete broad phase, but not for CCD broad phase. PostBroadPhase for CCD broad phase runs in-line. + //This probably should be revisited but it means that we can't use the parallel codepath within CCD. + if (continuation) + { + mPostBroadPhase3.setContinuation(continuation); + mPostBroadPhase2.setContinuation(&mPostBroadPhase3); + } + + mTimestamp++; + + // PT: TODO: consider merging mCreatedOverlaps & mDestroyedOverlaps + // PT: TODO: revisit memory management of mCreatedOverlaps & mDestroyedOverlaps + + //KS - if we ran broad phase, fetch the results now + if(mAddedHandles.size() != 0 || mUpdatedHandles.size() != 0 || mRemovedHandles.size() != 0) + mBroadPhase.fetchBroadPhaseResults(narrowPhaseUnlockTask); + + for(PxU32 i=0; i(mBroadPhase.getNbCreatedPairs(), mBroadPhase.getCreatedPairs(), *this); + processBPPairs(mBroadPhase.getNbDeletedPairs(), mBroadPhase.getDeletedPairs(), *this); + } + + { + //If there is a continuation task, then this is not part of CCD, so we can trigger bounds to be recomputed in parallel before pair generation runs during + //stage 2. + if (continuation) + { + const PxU32 size = mDirtyAggregates.size(); + for (PxU32 i = 0; i < size; i += SortAggregateBoundsParallel::MaxPairs) + { + const PxU32 nbToProcess = PxMin(size - i, SortAggregateBoundsParallel::MaxPairs); + + SortAggregateBoundsParallel* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(SortAggregateBoundsParallel)), SortAggregateBoundsParallel) + (mContextID, &mDirtyAggregates[i], nbToProcess); + + task->setContinuation(&mPostBroadPhase2); + task->removeReference(); + } + } + } + + + + if (continuation) + { + mPostBroadPhase2.setFlushPool(&flushPool); + mPostBroadPhase3.removeReference(); + mPostBroadPhase2.removeReference(); + } + else + { + postBpStage2(NULL, flushPool); + postBpStage3(NULL); + } + +} + +void PostBroadPhaseStage2Task::runInternal() +{ + mManager.postBpStage2(mCont, *mFlushPool); +} + +void AABBManager::postBpStage2(PxBaseTask* continuation, Cm::FlushPool& flushPool) +{ + { + const PxU32 size = mDirtyAggregates.size(); + for (PxU32 i = 0; i < size; i += ProcessSelfCollisionPairsParallel::MaxPairs) + { + const PxU32 nbToProcess = PxMin(size - i, ProcessSelfCollisionPairsParallel::MaxPairs); + + ProcessSelfCollisionPairsParallel* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ProcessSelfCollisionPairsParallel)), ProcessSelfCollisionPairsParallel) + (mContextID, &mDirtyAggregates[i], nbToProcess, this); + if (continuation) + { + task->setContinuation(continuation); + task->removeReference(); + } + else + task->runInternal(); + mAggPairTasks.pushBack(task); + } + } + + { + if (continuation) + processAggregatePairsParallel(mAggregateAggregatePairs, *this, flushPool, continuation, "AggAggPairs", mAggPairTasks); + else + processAggregatePairs(mAggregateAggregatePairs, *this); + } + + + + { + if (continuation) + processAggregatePairsParallel(mActorAggregatePairs, *this, flushPool, continuation, "AggActorPairs", mAggPairTasks); + else + processAggregatePairs(mActorAggregatePairs, *this); + } + + +} + +void AABBManager::postBpStage3(PxBaseTask*) +{ + + { + { + PX_PROFILE_ZONE("SimpleAABBManager::postBroadPhase - aggregate self-collisions", getContextId()); + const PxU32 size = mDirtyAggregates.size(); + for (PxU32 i = 0; i < size; i++) + { + Aggregate* aggregate = mDirtyAggregates[i]; + aggregate->resetDirtyState(); + + } + resetOrClear(mDirtyAggregates); + } + + { + PX_PROFILE_ZONE("SimpleAABBManager::postBroadPhase - append pairs", getContextId()); + + for (PxU32 a = 0; a < mAggPairTasks.size(); ++a) + { + ProcessAggPairsBase* task = mAggPairTasks[a]; + for (PxU32 t = 0; t < 2; t++) + { + for (PxU32 i = 0, startIdx = task->mCreatedPairs[t].mStartIdx; i < task->mCreatedPairs[t].mCount; ++i) + { + mCreatedOverlaps[t].pushBack((*task->mCreatedPairs[t].mArray)[i + startIdx]); + } + for (PxU32 i = 0, startIdx = task->mDestroyedPairs[t].mStartIdx; i < task->mDestroyedPairs[t].mCount; ++i) + { + mDestroyedOverlaps[t].pushBack((*task->mDestroyedPairs[t].mArray)[i + startIdx]); + } + } + } + + mAggPairTasks.forceSize_Unsafe(0); + + + Ps::InlineArray bpCache; + BpCacheData* entry = static_cast(mBpThreadContextPool.pop()); + + while (entry) + { + entry->reset(); + bpCache.pushBack(entry); + entry = static_cast(mBpThreadContextPool.pop()); + } + + //Now reinsert back into queue... + for (PxU32 i = 0; i < bpCache.size(); ++i) + { + mBpThreadContextPool.push(*bpCache[i]); + } + } + } + + { + PX_PROFILE_ZONE("AABBManager::postBroadPhase - process created pairs", getContextId()); + processBPPairs(mBroadPhase.getNbCreatedPairs(), mBroadPhase.getCreatedPairs(), *this); +// processBPPairs(mBroadPhase.getNbDeletedPairs(), mBroadPhase.getDeletedPairs(), *this); + } + + // PT: TODO: revisit this + // Filter out pairs in mDestroyedOverlaps that already exist in mCreatedOverlaps. This should be done better using bitmaps + // and some imposed ordering on previous operations. Later. + // We could also have a dedicated function "reinsertBroadPhase()", which would preserve the existing interactions at Sc-level. + if(1) + { + PX_PROFILE_ZONE("AABBManager::postBroadPhase - post-process", getContextId()); + + PxU32 totalCreatedOverlaps = 0; + for (PxU32 idx=0; idx(mBpThreadContextPool.pop()); + if (rv == NULL) + { + rv = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(BpCacheData), PX_DEBUG_EXP("BpCacheData")), BpCacheData)(); + } + return rv; +} +void AABBManager::putBpCacheData(BpCacheData* data) +{ + mBpThreadContextPool.push(*data); +} +void AABBManager::resetBpCacheData() +{ + Ps::InlineArray bpCache; + BpCacheData* entry = static_cast(mBpThreadContextPool.pop()); + while (entry) + { + entry->reset(); + bpCache.pushBack(entry); + entry = static_cast(mBpThreadContextPool.pop()); + } + + //Now reinsert back into queue... + for (PxU32 i = 0; i < bpCache.size(); ++i) + { + mBpThreadContextPool.push(*bpCache[i]); + } +} + +// PT: disabled this by default, since it bypasses all per-shape/per-actor visualization flags +//static const bool gVisualizeAggregateElems = false; + +void AABBManager::visualize(Cm::RenderOutput& out) +{ + const PxTransform idt = PxTransform(PxIdentity); + out << idt; + + const PxU32 N = mAggregates.size(); + for(PxU32 i=0;igetNbAggregated()) + { + out << PxU32(PxDebugColor::eARGB_GREEN); + const PxBounds3& b = mBoundsArray.getBounds(aggregate->mIndex); + out << Cm::DebugBox(b, true); + } + } + +/* const PxU32 N = mAggregateManager.getAggregatesCapacity(); + for(PxU32 i=0;inbElems) + { + if(!mAggregatesUpdated.isInList(BpHandle(i))) + out << PxU32(PxDebugColor::eARGB_GREEN); + else + out << PxU32(PxDebugColor::eARGB_RED); + + PxBounds3 decoded; + const IntegerAABB& iaabb = mBPElems.getAABB(aggregate->bpElemId); + iaabb.decode(decoded); + + out << Cm::DebugBox(decoded, true); + + if(gVisualizeAggregateElems) + { + PxU32 elem = aggregate->elemHeadID; + while(BP_INVALID_BP_HANDLE!=elem) + { + out << PxU32(PxDebugColor::eARGB_CYAN); + const IntegerAABB elemBounds = mAggregateElems.getAABB(elem); + elemBounds.decode(decoded); + out << Cm::DebugBox(decoded, true); + elem = mAggregateElems.getNextId(elem); + } + } + } + }*/ +} + +} //namespace Bp + +} //namespace physx + diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp new file mode 100644 index 000000000..0df71d8b9 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp @@ -0,0 +1,175 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "BpBroadPhase.h" +#include "BpBroadPhaseSap.h" +#include "BpBroadPhaseMBP.h" +#include "PxSceneDesc.h" +#include "CmBitMap.h" + +using namespace physx; +using namespace Bp; +using namespace Cm; + +BroadPhase* createABP( + const PxU32 maxNbBroadPhaseOverlaps, + const PxU32 maxNbStaticShapes, + const PxU32 maxNbDynamicShapes, + PxU64 contextID); + +BroadPhase* createMBP4( + const PxU32 maxNbBroadPhaseOverlaps, + const PxU32 maxNbStaticShapes, + const PxU32 maxNbDynamicShapes, + PxU64 contextID); + +BroadPhase* BroadPhase::create( + const PxBroadPhaseType::Enum bpType, + const PxU32 maxNbRegions, + const PxU32 maxNbBroadPhaseOverlaps, + const PxU32 maxNbStaticShapes, + const PxU32 maxNbDynamicShapes, + PxU64 contextID) +{ + PX_ASSERT(bpType==PxBroadPhaseType::eMBP || bpType == PxBroadPhaseType::eSAP || bpType == PxBroadPhaseType::eABP); + + if(bpType==PxBroadPhaseType::eABP) + return createABP(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); +// return createMBP4(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); + else if(bpType==PxBroadPhaseType::eMBP) + return PX_NEW(BroadPhaseMBP)(maxNbRegions, maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); + else + return PX_NEW(BroadPhaseSap)(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); +// return createABP(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); +} + + +#if PX_CHECKED +bool BroadPhaseUpdateData::isValid(const BroadPhaseUpdateData& updateData, const BroadPhase& bp) +{ + return (updateData.isValid() && bp.isValid(updateData)); +} + +static bool testHandles(PxU32 size, const BpHandle* handles, const PxU32 capacity, const Bp::FilterGroup::Enum* groups, const PxBounds3* bounds, BitMap& bitmap) +{ + if(!handles && size) + return false; + +/* ValType minVal=0; + ValType maxVal=0xffffffff;*/ + + for(PxU32 i=0;i=capacity) + return false; + + // Array in ascending order of id. + if(i>0 && (h < handles[i-1])) + return false; + + if(groups && groups[h]==FilterGroup::eINVALID) + return false; + + bitmap.set(h); + + if(bounds) + { + for(PxU32 j=0;j<3;j++) + { + //Max must be greater than min. + if(bounds[h].minimum[j]>bounds[h].maximum[j]) + return false; +#if 0 + //Bounds have an upper limit. + if(bounds[created[i]].getMax(j)>=maxVal) + return false; + + //Bounds have a lower limit. + if(bounds[created[i]].getMin(j)<=minVal) + return false; + + //Max must be odd. + if(4 != (bounds[created[i]].getMax(j) & 4)) + return false; + + //Min must be even. + if(0 != (bounds[created[i]].getMin(j) & 4)) + return false; +#endif + } + } + } + return true; +} + +static bool testBitmap(const BitMap& bitmap, PxU32 size, const BpHandle* handles) +{ + while(size--) + { + const BpHandle h = *handles++; + if(bitmap.test(h)) + return false; + } + return true; +} + +bool BroadPhaseUpdateData::isValid() const +{ + const PxBounds3* bounds = getAABBs(); + const PxU32 boxesCapacity = getCapacity(); + const Bp::FilterGroup::Enum* groups = getGroups(); + + BitMap createdBitmap; createdBitmap.resizeAndClear(boxesCapacity); + BitMap updatedBitmap; updatedBitmap.resizeAndClear(boxesCapacity); + BitMap removedBitmap; removedBitmap.resizeAndClear(boxesCapacity); + + if(!testHandles(getNumCreatedHandles(), getCreatedHandles(), boxesCapacity, groups, bounds, createdBitmap)) + return false; + if(!testHandles(getNumUpdatedHandles(), getUpdatedHandles(), boxesCapacity, groups, bounds, updatedBitmap)) + return false; + if(!testHandles(getNumRemovedHandles(), getRemovedHandles(), boxesCapacity, NULL, NULL, removedBitmap)) + return false; + + if(1) + { + // Created/updated + if(!testBitmap(createdBitmap, getNumUpdatedHandles(), getUpdatedHandles())) + return false; + // Created/removed + if(!testBitmap(createdBitmap, getNumRemovedHandles(), getRemovedHandles())) + return false; + // Updated/removed + if(!testBitmap(updatedBitmap, getNumRemovedHandles(), getRemovedHandles())) + return false; + } + return true; +} +#endif diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp new file mode 100644 index 000000000..53362dfaa --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp @@ -0,0 +1,3402 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxProfiler.h" +#include "BpBroadPhaseABP.h" +#include "BpBroadPhaseShared.h" +#include "CmRadixSortBuffered.h" +#include "PsFoundation.h" +#include "PsVecMath.h" +#include "PxcScratchAllocator.h" + +using namespace physx::shdfnd::aos; +using namespace physx; +using namespace Bp; +using namespace Cm; + +#define CHECKPOINT(x) +//#include +//#define CHECKPOINT(x) printf(x); + +//#pragma warning (disable : 4702) +#define CODEALIGN16 //_asm align 16 +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + #define ABP_SIMD_OVERLAP +#endif + +#define ABP_BATCHING 128 +#define USE_ABP_BUCKETS 5000 // PT: don't use buckets below that number... +#ifdef USE_ABP_BUCKETS + #define NB_BUCKETS 5 + // Regular version: 5 buckets a la bucket pruner (4 + cross bucket) + // Alternative version: 4 buckets + dup objects a la MBP regions +// #define USE_ALTERNATIVE_VERSION + #define ABP_USE_INTEGER_XS2 // Works but questionable speedups +#else + #define ABP_USE_INTEGER_XS +#endif +#define NB_SENTINELS 6 + +//#define RECURSE_LIMIT 20000 + + typedef PxU32 ABP_Index; + + static const bool gPrepareOverlapsFlag = true; +#ifdef ABP_SIMD_OVERLAP + static const bool gUseRegularBPKernel = false; // false to use "version 13" in box pruning series + static const bool gUnrollLoop = true; // true to use "version 14" in box pruning series +#else + // PT: tested on Switch, for some reason the regular version is fastest there + static const bool gUseRegularBPKernel = true; // false to use "version 13" in box pruning series + static const bool gUnrollLoop = false; // true to use "version 14" in box pruning series + + //ABP_SIMD_OVERLAP + //MBP.Add64KObjects 13982 ( +0.0%) 4757795 ( +0.0%) FAIL + //MBP.AddBroadPhaseRegion 0 ( +0.0%) 3213795 ( +0.0%) FAIL + //MBP.FinalizeOverlaps64KObjects 507 ( +0.0%) 5650723 ( +0.0%) FAIL + //MBP.FindOverlaps64KMixedObjects 59258 ( +0.0%) 5170179 ( +0.0%) FAIL + //MBP.FindOverlaps64KObjects 31351 ( +0.0%) 7122019 ( +0.0%) FAIL + //MBP.Remove64KObjects 4993 ( +0.0%) 5281683 ( +0.0%) FAIL + //MBP.Update64KObjects 13711 ( +0.0%) 5521699 ( +0.0%) FAIL + + //gUseRegularBPKernel: + //MBP.Add64KObjects 14406 ( +0.0%) 4757795 ( +0.0%) FAIL + //MBP.AddBroadPhaseRegion 0 ( +0.0%) 3213795 ( +0.0%) FAIL + //MBP.FinalizeOverlaps64KObjects 504 ( +0.0%) 5650723 ( +0.0%) FAIL + //MBP.FindOverlaps64KMixedObjects 48929 ( +0.0%) 5170179 ( +0.0%) FAIL + //MBP.FindOverlaps64KObjects 25636 ( +0.0%) 7122019 ( +0.0%) FAIL + //MBP.Remove64KObjects 4878 ( +0.0%) 5281683 ( +0.0%) FAIL + //MBP.Update64KObjects 13932 ( +0.0%) 5521699 ( +0.0%) FAIL + + // false/true + //MBP.Add64KObjects 14278 ( +0.0%) 4757795 ( +0.0%) FAIL + //MBP.AddBroadPhaseRegion 0 ( +0.0%) 3213795 ( +0.0%) FAIL + //MBP.FinalizeOverlaps64KObjects 504 ( +0.0%) 5650723 ( +0.0%) FAIL + //MBP.FindOverlaps64KMixedObjects 60331 ( +0.0%) 5170179 ( +0.0%) FAIL + //MBP.FindOverlaps64KObjects 32064 ( +0.0%) 7122019 ( +0.0%) FAIL + //MBP.Remove64KObjects 4930 ( +0.0%) 5281683 ( +0.0%) FAIL + //MBP.Update64KObjects 13673 ( +0.0%) 5521699 ( +0.0%) FAIL + + // false/false + //MBP.Add64KObjects 13960 ( +0.0%) 4757795 ( +0.0%) FAIL + //MBP.AddBroadPhaseRegion 0 ( +0.0%) 3213795 ( +0.0%) FAIL + //MBP.FinalizeOverlaps64KObjects 503 ( +0.0%) 5650723 ( +0.0%) FAIL + //MBP.FindOverlaps64KMixedObjects 48549 ( +0.0%) 5170179 ( +0.0%) FAIL + //MBP.FindOverlaps64KObjects 25598 ( +0.0%) 7122019 ( +0.0%) FAIL + //MBP.Remove64KObjects 4883 ( +0.0%) 5281683 ( +0.0%) FAIL + //MBP.Update64KObjects 13667 ( +0.0%) 5521699 ( +0.0%) FAIL +#endif + +#ifdef ABP_USE_INTEGER_XS + typedef PxU32 PosXType; + #define SentinelValue 0xffffffff +#else + typedef float PosXType; + #define SentinelValue FLT_MAX +#endif + +#ifdef ABP_USE_INTEGER_XS2 + typedef PxU32 PosXType2; + #define SentinelValue2 0xffffffff +#else + #ifdef ABP_USE_INTEGER_XS + typedef PxU32 PosXType2; + #define SentinelValue2 0xffffffff + #else + typedef float PosXType2; + #define SentinelValue2 FLT_MAX + #endif +#endif + +namespace internalABP{ + + struct SIMD_AABB4 : public Ps::UserAllocated + { + PX_FORCE_INLINE void initFrom2(const PxBounds3& box) + { +#ifdef ABP_USE_INTEGER_XS + mMinX = encodeFloat(PX_IR(box.minimum.x)); + mMaxX = encodeFloat(PX_IR(box.maximum.x)); + mMinY = box.minimum.y; + mMinZ = box.minimum.z; + mMaxY = box.maximum.y; + mMaxZ = box.maximum.z; +#else + mMinX = box.minimum.x; + mMinY = box.minimum.y; + mMinZ = box.minimum.z; + mMaxX = box.maximum.x; + mMaxY = box.maximum.y; + mMaxZ = box.maximum.z; +#endif + } + + PX_FORCE_INLINE void operator = (const SIMD_AABB4& box) + { + mMinX = box.mMinX; + mMinY = box.mMinY; + mMinZ = box.mMinZ; + mMaxX = box.mMaxX; + mMaxY = box.mMaxY; + mMaxZ = box.mMaxZ; + } + + PX_FORCE_INLINE void initSentinel() + { + mMinX = SentinelValue; + } + + PX_FORCE_INLINE bool isSentinel() const + { + return mMinX == SentinelValue; + } + +#ifdef USE_ABP_BUCKETS + // PT: to be able to compute bounds easily + PosXType mMinX; + float mMinY; + float mMinZ; + PosXType mMaxX; + float mMaxY; + float mMaxZ; +#else + PosXType mMinX; + PosXType mMaxX; + float mMinY; + float mMinZ; + float mMaxY; + float mMaxZ; +#endif + }; + +#define USE_SHARED_CLASSES +#ifdef USE_SHARED_CLASSES + struct SIMD_AABB_X4 : public AABB_Xi + { + PX_FORCE_INLINE void initFrom(const SIMD_AABB4& box) + { + #ifdef ABP_USE_INTEGER_XS2 + initFromFloats(&box.mMinX, &box.mMaxX); + #else + mMinX = box.mMinX; + mMaxX = box.mMaxX; + #endif + } + }; + + PX_ALIGN_PREFIX(16) + #ifdef ABP_SIMD_OVERLAP + struct SIMD_AABB_YZ4 : AABB_YZn + { + PX_FORCE_INLINE void initFrom(const SIMD_AABB4& box) + { + #ifdef ABP_SIMD_OVERLAP + mMinY = -box.mMinY; + mMinZ = -box.mMinZ; + #else + mMinY = box.mMinY; + mMinZ = box.mMinZ; + #endif + mMaxY = box.mMaxY; + mMaxZ = box.mMaxZ; + } + } + #else + struct SIMD_AABB_YZ4 : AABB_YZr + { + PX_FORCE_INLINE void initFrom(const SIMD_AABB4& box) + { + mMinY = box.mMinY; + mMinZ = box.mMinZ; + mMaxY = box.mMaxY; + mMaxZ = box.mMaxZ; + } + } + #endif + PX_ALIGN_SUFFIX(16); +#else + struct SIMD_AABB_X4 : public Ps::UserAllocated + { + PX_FORCE_INLINE void initFromFloats(const void* PX_RESTRICT minX, const void* PX_RESTRICT maxX) + { + mMinX = encodeFloat(*reinterpret_cast(minX)); + mMaxX = encodeFloat(*reinterpret_cast(maxX)); + } + + PX_FORCE_INLINE void initFrom(const SIMD_AABB4& box) + { + #ifdef ABP_USE_INTEGER_XS2 + initFromFloats(&box.mMinX, &box.mMaxX); + #else + mMinX = box.mMinX; + mMaxX = box.mMaxX; + #endif + } + + PX_FORCE_INLINE void initFromPxVec4(const PxVec4& min, const PxVec4& max) + { + #ifdef ABP_USE_INTEGER_XS2 + initFromFloats(&min.x, &max.x); + #else + #ifdef ABP_USE_INTEGER_XS + initFromFloats(&min.x, &max.x); + #else + mMinX = min.x; + mMaxX = max.x; + #endif + #endif + } + + PX_FORCE_INLINE void operator = (const SIMD_AABB_X4& box) + { + mMinX = box.mMinX; + mMaxX = box.mMaxX; + } + + PX_FORCE_INLINE void initSentinel() + { + mMinX = SentinelValue2; + } + + PX_FORCE_INLINE bool isSentinel() const + { + return mMinX == SentinelValue2; + } + + PosXType2 mMinX; + PosXType2 mMaxX; + }; + + struct SIMD_AABB_YZ4 : public Ps::UserAllocated + { + PX_FORCE_INLINE void initFrom(const SIMD_AABB4& box) + { + #ifdef ABP_SIMD_OVERLAP + mMinY = -box.mMinY; + mMinZ = -box.mMinZ; + #else + mMinY = box.mMinY; + mMinZ = box.mMinZ; + #endif + mMaxY = box.mMaxY; + mMaxZ = box.mMaxZ; + } + + PX_FORCE_INLINE void initFromPxVec4(const PxVec4& min, const PxVec4& max) + { + #ifdef ABP_SIMD_OVERLAP + mMinY = -min.y; + mMinZ = -min.z; + #else + mMinY = min.y; + mMinZ = min.z; + #endif + mMaxY = max.y; + mMaxZ = max.z; + } + + PX_FORCE_INLINE void operator = (const SIMD_AABB_YZ4& box) + { + V4StoreA(V4LoadA(&box.mMinY), &mMinY); + } + + float mMinY; + float mMinZ; + float mMaxY; + float mMaxZ; + }; +#endif + +#define MBP_ALLOC(x) PX_ALLOC(x, "MBP") +#define MBP_ALLOC_TMP(x) PX_ALLOC_TEMP(x, "MBP_TMP") +#define MBP_FREE(x) if(x) PX_FREE_AND_RESET(x) +#define DELETESINGLE(x) if (x) { delete x; x = NULL; } +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +#define INVALID_ID 0xffffffff + +/////////////////////////////////////////////////////////////////////////////// + +#define DEFAULT_NB_ENTRIES 128 + + class ABP_MM + { + public: + ABP_MM(); + ~ABP_MM(); + + void* frameAlloc(PxU32 size); + void frameFree(void* address); + + PxcScratchAllocator* mScratchAllocator; + }; + +ABP_MM::ABP_MM() : + mScratchAllocator (NULL) +{ +} + +ABP_MM::~ABP_MM() +{ +} + +void* ABP_MM::frameAlloc(PxU32 size) +{ + if(mScratchAllocator) + return mScratchAllocator->alloc(size, true); + return PX_ALLOC_TEMP(size, PX_DEBUG_EXP("frameAlloc")); +} + +void ABP_MM::frameFree(void* address) +{ + if(mScratchAllocator) + mScratchAllocator->free(address); + else + PX_FREE(address); +} + +template +static T* resizeBoxesT(PxU32 oldNbBoxes, PxU32 newNbBoxes, const T* boxes) +{ + T* newBoxes = PX_NEW(T)[newNbBoxes]; + if(oldNbBoxes) + PxMemCopy(newBoxes, boxes, oldNbBoxes*sizeof(T)); + DELETEARRAY(boxes); + return newBoxes; +} + + class Boxes + { + public: + Boxes(); + ~Boxes(); + + PX_FORCE_INLINE void init(const Boxes& boxes){ mSize = boxes.mSize; mCapacity = boxes.mCapacity; } + PX_FORCE_INLINE PxU32 getSize() const { return mSize; } + PX_FORCE_INLINE PxU32 getCapacity() const { return mCapacity; } + PX_FORCE_INLINE bool isFull() const { return mSize==mCapacity; } + PX_FORCE_INLINE void reset() { mSize = mCapacity = 0; } + PX_FORCE_INLINE PxU32 popBack() { return --mSize; } + +// protected: + PxU32 mSize; + PxU32 mCapacity; + }; + +Boxes::Boxes() : + mSize (0), + mCapacity (0) +{ +} + +Boxes::~Boxes() +{ + reset(); +} + + class StraightBoxes : public Boxes + { + public: + StraightBoxes(); + ~StraightBoxes(); + + void init(PxU32 size, PxU32 capacity, SIMD_AABB4* boxes); + void reset(); + PxU32 resize(); + PxU32 resize(PxU32 incoming); + bool allocate(PxU32 nb); + + PX_FORCE_INLINE const SIMD_AABB4* getBoxes() const { return mBoxes; } + PX_FORCE_INLINE SIMD_AABB4* getBoxes() { return mBoxes; } + + PX_FORCE_INLINE void setBounds(PxU32 index, const SIMD_AABB4& box) + { + PX_ASSERT(index(boxes.getBoxes_X()); + mBoxes_YZ = const_cast(boxes.getBoxes_YZ()); +} + +PxU32 SplitBoxes::resize() +{ + const PxU32 capacity = mCapacity; + const PxU32 size = mSize; + +// const PxU32 newCapacity = capacity ? capacity + DEFAULT_NB_ENTRIES : DEFAULT_NB_ENTRIES; +// const PxU32 newCapacity = capacity ? capacity*2 : DEFAULT_NB_ENTRIES; + const PxU32 newCapacity = capacity ? capacity*2 : DEFAULT_NB_ENTRIES; + mBoxes_X = resizeBoxesT(size, newCapacity, mBoxes_X); + mBoxes_YZ = resizeBoxesT(size, newCapacity, mBoxes_YZ); + mCapacity = newCapacity; + return newCapacity; +} + +PxU32 SplitBoxes::resize(PxU32 incoming) +{ + const PxU32 capacity = mCapacity; + const PxU32 size = mSize; + const PxU32 minCapacity = size + incoming; + if(minCapacity(mBoxes_YZ) & 15)); + mSize = mCapacity = nb; + return true; +} + + typedef SplitBoxes StaticBoxes; + typedef SplitBoxes DynamicBoxes; + +/////////////////////////////////////////////////////////////////////////////// + + struct ABP_Object : public Ps::UserAllocated + { + PX_FORCE_INLINE ABP_Object() : mIndex(INVALID_ID) + { +#if PX_DEBUG + mUpdated = false; +#endif + } + + private: + PxU32 mIndex; // Out-to-in, maps user handle to internal array. mIndex indexes either the static or dynamic array. + // PT: the type won't be available for removed objects so we have to store it there. That uses 2 bits. + // Then the "data" will need one more bit for marking sleeping objects so that leaves 28bits for the actual index. + PX_FORCE_INLINE void setData(PxU32 index, FilterType::Enum type) + { +// mIndex = index; + index <<= 2; + index |= type; + mIndex = index; + } + public: + // PT: TODO: rename "index" to data everywhere + PX_FORCE_INLINE void setActiveIndex(PxU32 index, FilterType::Enum type) + { + const PxU32 boxData = (index+index); + setData(boxData, type); + } + + PX_FORCE_INLINE void setSleepingIndex(PxU32 index, FilterType::Enum type) + { + const PxU32 boxData = (index+index)|1; + PX_ASSERT(getType()==type); + setData(boxData, type); + } + + PX_FORCE_INLINE FilterType::Enum getType() const + { + return FilterType::Enum(mIndex&3); + } + + PX_FORCE_INLINE PxU32 getData() const + { + return mIndex>>2; + } + + PX_FORCE_INLINE void invalidateIndex() + { + mIndex = INVALID_ID; + } + PX_FORCE_INLINE bool isValid() const + { + return mIndex != INVALID_ID; + } + +#if PX_DEBUG + bool mUpdated; +#endif + }; + + typedef ABP_Object ABPEntry; + +/////////////////////////////////////////////////////////////////////////////// + +//#define BIT_ARRAY_STACK 512 + + static PX_FORCE_INLINE PxU32 bitsToDwords(PxU32 nbBits) + { + return (nbBits>>5) + ((nbBits&31) ? 1 : 0); + } + + // Use that one instead of an array of bools. Takes less ram, nearly as fast [no bounds checkings and so on]. + class BitArray + { + public: + BitArray(); + BitArray(PxU32 nbBits); + ~BitArray(); + + bool init(PxU32 nbBits); + void empty(); + void resize(PxU32 nbBits); + + PX_FORCE_INLINE void checkResize(PxU32 bitNumber) + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + resize(bitNumber); + } + + PX_FORCE_INLINE void setBitChecked(PxU32 bitNumber) + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + resize(bitNumber); + mBits[index] |= 1<<(bitNumber&31); + } + + PX_FORCE_INLINE void clearBitChecked(PxU32 bitNumber) + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + resize(bitNumber); + mBits[index] &= ~(1<<(bitNumber&31)); + } + // Data management + PX_FORCE_INLINE void setBit(PxU32 bitNumber) { mBits[bitNumber>>5] |= 1<<(bitNumber&31); } + PX_FORCE_INLINE void clearBit(PxU32 bitNumber) { mBits[bitNumber>>5] &= ~(1<<(bitNumber&31)); } + PX_FORCE_INLINE void toggleBit(PxU32 bitNumber) { mBits[bitNumber>>5] ^= 1<<(bitNumber&31); } + + PX_FORCE_INLINE void clearAll() { PxMemZero(mBits, mSize*4); } + PX_FORCE_INLINE void setAll() { PxMemSet(mBits, 0xff, mSize*4); } + + // Data access + PX_FORCE_INLINE Ps::IntBool isSet(PxU32 bitNumber) const { return Ps::IntBool(mBits[bitNumber>>5] & (1<<(bitNumber&31))); } + PX_FORCE_INLINE Ps::IntBool isSetChecked(PxU32 bitNumber) const + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + return 0; + return Ps::IntBool(mBits[index] & (1<<(bitNumber&31))); + } + + PX_FORCE_INLINE const PxU32* getBits() const { return mBits; } + PX_FORCE_INLINE PxU32 getSize() const { return mSize; } + + // PT: replicate Cm::BitMap stuff for temp testing + PxU32 findLast() const + { + for(PxU32 i = mSize; i-- > 0;) + { + if(mBits[i]) + return (i<<5)+Ps::highestSetBit(mBits[i]); + } + return PxU32(0); + } + protected: + PxU32* mBits; //!< Array of bits + PxU32 mSize; //!< Size of the array in dwords +#ifdef BIT_ARRAY_STACK + PxU32 mStack[BIT_ARRAY_STACK]; +#endif + }; + +/////////////////////////////////////////////////////////////////////////////// + +BitArray::BitArray() : mBits(NULL), mSize(0) +{ +} + +BitArray::BitArray(PxU32 nbBits) : mBits(NULL), mSize(0) +{ + init(nbBits); +} + +BitArray::~BitArray() +{ + empty(); +} + +void BitArray::empty() +{ +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); + mBits = NULL; + mSize = 0; +} + +bool BitArray::init(PxU32 nbBits) +{ + mSize = bitsToDwords(nbBits); + // Get ram for n bits +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); +#ifdef BIT_ARRAY_STACK + if(mSize>BIT_ARRAY_STACK) +#endif + mBits = reinterpret_cast(MBP_ALLOC(sizeof(PxU32)*mSize)); +#ifdef BIT_ARRAY_STACK + else + mBits = mStack; +#endif + + // Set all bits to 0 + clearAll(); + return true; +} + +void BitArray::resize(PxU32 nbBits) +{ + const PxU32 newSize = bitsToDwords(nbBits+128); + PxU32* newBits = NULL; +#ifdef BIT_ARRAY_STACK + if(newSize>BIT_ARRAY_STACK) +#endif + { + // Old buffer was stack or allocated, new buffer is allocated + newBits = reinterpret_cast(MBP_ALLOC(sizeof(PxU32)*newSize)); + if(mSize) + PxMemCopy(newBits, mBits, sizeof(PxU32)*mSize); + } +#ifdef BIT_ARRAY_STACK + else + { + newBits = mStack; + if(mSize>BIT_ARRAY_STACK) + { + // Old buffer was allocated, new buffer is stack => copy to stack, shrink + CopyMemory(newBits, mBits, sizeof(PxU32)*BIT_ARRAY_STACK); + } + else + { + // Old buffer was stack, new buffer is stack => keep working on the same stack buffer, nothing to do + } + } +#endif + const PxU32 remain = newSize - mSize; + if(remain) + PxMemZero(newBits + mSize, remain*sizeof(PxU32)); + +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); + mBits = newBits; + mSize = newSize; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ABP_Index* resizeMapping(PxU32 oldNbBoxes, PxU32 newNbBoxes, ABP_Index* mapping) +{ + ABP_Index* newMapping = reinterpret_cast(MBP_ALLOC(sizeof(ABP_Index)*newNbBoxes)); + if(oldNbBoxes) + PxMemCopy(newMapping, mapping, oldNbBoxes*sizeof(ABP_Index)); + MBP_FREE(mapping); + return newMapping; +} + + struct ABP_Object; + + class ABP_PairManager : public PairManagerData + { + public: + ABP_PairManager(); + ~ABP_PairManager(); + + InternalPair* addPair (PxU32 id0, PxU32 id1); + void computeCreatedDeletedPairs (BroadPhaseABP* mbp, const BitArray& updated, const BitArray& removed); + + const Bp::FilterGroup::Enum* mGroups; + const ABP_Index* mInToOut0; + const ABP_Index* mInToOut1; + const ABPEntry* mObjects; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* mLUT; +#endif + }; + + /////////////////////////////////////////////////////////////////////////// + + struct ABP_SharedData + { + PX_FORCE_INLINE ABP_SharedData() : + mABP_Objects (NULL), + mABP_Objects_Capacity (0) + { + } + + void resize(BpHandle userID); + + PX_FORCE_INLINE void checkResize(PxU32 maxID) + { + if(mABP_Objects_CapacitycurrentCapacity) + { + const PxU32 minCapacity = PxMax(newSize, 1024u); + const PxU32 newCapacity = PxMax(minCapacity, currentCapacity*2); + PX_ASSERT(newCapacity>=newSize); + mMaxNbUpdated = newCapacity; + remap = resizeMapping(currentSize, newCapacity, mInToOut_Updated); + } + else + { + remap = mInToOut_Updated; + } + mInToOut_Updated = remap; + mNbUpdated = newSize; + + // PT: we only copy the new handles for now. The bounds will be computed later in "prepareData". + // PT: TODO: do we even need to copy them? Can't we just reuse the source ptr directly? + { + PX_ASSERT(currentSize+nb<=mMaxNbUpdated); + remap += currentSize; + PxU32 nbToGo = nb; + while(nbToGo--) + { + const BpHandle userID = *userIDs++; + + PX_ASSERT(!isNewOrUpdated(userID)); + *remap++ = markAsNewOrUpdated(userID); + + if(sharedData) + sharedData->mUpdatedObjects.setBit(userID); + } + } +} + +// PT: TODO: inline this again +void BoxManager::removeObject(ABPEntry& object, BpHandle userID) +{ + PX_UNUSED(userID); + const PxU32 boxData = object.getData(); + const PxU32 boxIndex = boxData>>1; + if(boxData&1) + { + // Sleeping object. + PX_ASSERT(boxIndex>1; + if(boxData&1) + { + // PT: benchmark for this codepath: MBP.UpdateSleeping + + // Sleeping object. We must reactivate it, i.e: + // - remove it from the array of sleeping objects + // - add it to the array of active/updated objects + + // First we remove: + { + PX_ASSERT(boxIndex also tweaking the sleeping boxes might break the "merge sleeping" array code + PX_ASSERT(mNbRemovedSleeping<=mNbSleeping); + + if(mNbRemovedSleeping==mNbSleeping) + { + // PT: remove everything + mSleepingBoxes.reset(); + PX_FREE_AND_RESET(mInToOut_Sleeping); + mNbSleeping = mNbRemovedSleeping = 0; + return; + } + + const PxU32 expectedTotal = mNbSleeping - mNbRemovedSleeping; + PxU32 nbRemovedFound = 0; + PxU32 nbSleepingLeft = 0; + const PxU32 sleepCapacity = mSleepingBoxes.getCapacity(); + if(expectedTotal>=sleepCapacity/2) + { + // PT: remove holes, keep same data buffers + SIMD_AABB_X4* boxesX = mSleepingBoxes.getBoxes_X(); + SIMD_AABB_YZ4* boxesYZ = mSleepingBoxes.getBoxes_YZ(); + ABP_Index* remap = mInToOut_Sleeping; + + for(PxU32 i=0;i(PX_ALLOC(expectedTotal*sizeof(BpHandle), PX_DEBUG_EXP("tmp"))); + + const SIMD_AABB_X4* PX_RESTRICT srcDataX = mSleepingBoxes.getBoxes_X(); + const SIMD_AABB_YZ4* PX_RESTRICT srcDataYZ = mSleepingBoxes.getBoxes_YZ(); + const ABP_Index* PX_RESTRICT srcRemap = mInToOut_Sleeping; + + for(PxU32 i=0;i(PX_ALLOC(size*sizeof(float), PX_DEBUG_EXP("tmp"))); + + // PT: in this version we compute the key on-the-fly, i.e. it will be computed twice overall. We could make this + // faster by merging bounds and distances inside the AABB manager. + const BpHandle userID = removeNewOrUpdatedMark(index); + + keys[nbUpdated] = bounds[userID].minimum.x - distances[userID]; + + if(!tempBuffer) + { + tempBuffer = reinterpret_cast(memoryManager.frameAlloc(size*sizeof(PxU32))); + newOrUpdatedIDs = tempBuffer; + sleepingIndices = tempBuffer; + } + + newOrUpdatedIDs[size - 1 - nbUpdated] = userID; +#if PX_DEBUG + SIMD_AABB4 aabb; + computeMBPBounds_Check(aabb, bounds, distances, userID); + PX_ASSERT(aabb.mMinX==keys[nbUpdated]); +#endif + nbUpdated++; + } + else + { + if(!tempBuffer) + { + tempBuffer = reinterpret_cast(memoryManager.frameAlloc(size*sizeof(PxU32))); + newOrUpdatedIDs = tempBuffer; + sleepingIndices = tempBuffer; + } + + // PT: sleeping object + sleepingIndices[nbSleeping++] = i; + } + } + PX_ASSERT(nbRemoved + nbUpdated + nbSleeping == size); + + // PT: we must process the sleeping objects first, because the bounds of new sleeping objects are located in the existing updated buffers. + // PT: TODO: *HOWEVER* we could sort things right now and then reuse the "keys" buffer? + + if(nbSleeping) + { + // PT: must merge these guys to current sleeping array + // They should already be in sorted order and we should already have the boxes. +#if PX_DEBUG + const SIMD_AABB_YZ4* boxesYZ = mUpdatedBoxes.getBoxes_YZ(); + float prevKey = -FLT_MAX; + for(PxU32 ii=0;ii=prevKey); + prevKey = key; + + SIMD_AABB4 aabb; + computeMBPBounds_Check(aabb, bounds, distances, userID); + PX_ASSERT(aabb.mMinX==key); + + #ifdef ABP_SIMD_OVERLAP + PX_ASSERT(boxesYZ[i].mMinY==-aabb.mMinY); + PX_ASSERT(boxesYZ[i].mMinZ==-aabb.mMinZ); + #else + PX_ASSERT(boxesYZ[i].mMinY==aabb.mMinY); + PX_ASSERT(boxesYZ[i].mMinZ==aabb.mMinZ); + #endif + PX_ASSERT(boxesYZ[i].mMaxY==aabb.mMaxY); + PX_ASSERT(boxesYZ[i].mMaxZ==aabb.mMaxZ); + } +#endif + + if(mNbSleeping) + { + // PT: benchmark for this codepath: MBP.MergeSleeping + CHECKPOINT("Merging sleeping objects\n"); + + // PT: here, we need to merge two arrays of sleeping objects together: + // - the ones already contained inside mSleepingBoxes + // - the new sleeping objects currently contained in mUpdatedBoxes + // Both of them should already be sorted. + // PT: TODO: super subtle stuff going on there, to revisit + + // PT: TODO: revisit names + PxU32 offsetSorted = 0; + const PxU32 nbSorted = nbSleeping; + const SIMD_AABB_X4* PX_RESTRICT sortedDataX = mUpdatedBoxes.getBoxes_X(); + const SIMD_AABB_YZ4* PX_RESTRICT sortedDataYZ = mUpdatedBoxes.getBoxes_YZ(); + const ABP_Index* PX_RESTRICT sortedRemap = mInToOut_Updated; + + PxU32 offsetNonSorted = 0; + const PxU32 nbToSort = mNbSleeping; + const SIMD_AABB_X4* PX_RESTRICT toSortDataX = mSleepingBoxes.getBoxes_X(); + const SIMD_AABB_YZ4* PX_RESTRICT toSortDataYZ = mSleepingBoxes.getBoxes_YZ(); + const ABP_Index* PX_RESTRICT toSortRemap = mInToOut_Sleeping; + + PX_ASSERT(mNbRemovedSleeping<=mNbSleeping); +#if PX_DEBUG + { + PxU32 nbRemovedFound=0; + for(PxU32 i=0;i(PX_ALLOC(nbTotal*sizeof(BpHandle), PX_DEBUG_EXP("tmp"))); + + PxU32 i=0; + PxU32 nbRemovedFound=0; + PxU32 nbToGo = nbSorted + nbToSort; + while(nbToGo--) + { + PxU32 boxIndex; + { + if(nextCandidateNonSorted(PX_ALLOC(nbSleeping*sizeof(BpHandle), PX_DEBUG_EXP("tmp"))); + PX_FREE(mInToOut_Sleeping); + mInToOut_Sleeping = inToOut_Sleeping; + } + else + { + inToOut_Sleeping = mInToOut_Sleeping; + } + + const SIMD_AABB_X4* srcBoxesX = mUpdatedBoxes.getBoxes_X(); + const SIMD_AABB_YZ4* srcBoxesYZ = mUpdatedBoxes.getBoxes_YZ(); + SIMD_AABB_X4* dstBoxesX = mSleepingBoxes.getBoxes_X(); + SIMD_AABB_YZ4* dstBoxesYZ = mSleepingBoxes.getBoxes_YZ(); + initSentinels(dstBoxesX, nbSleeping); + for(PxU32 ii=0;ii(memoryManager.frameAlloc(sizeof(PxU32)*nbUpdated)); + PxU32* ranks1 = reinterpret_cast(memoryManager.frameAlloc(sizeof(PxU32)*nbUpdated)); + StackRadixSort(rs, ranks0, ranks1); + const PxU32* sorted = rs.Sort(keys, nbUpdated).GetRanks(); + + // PT: + // - shuffle the remap table, store it in sorted order (we can probably use the "recyclable" array here again) + // - compute bounds on-the-fly, store them in sorted order + + // PT: TODO: the "keys" array can be much bigger than stricly necessary here + BpHandle* inToOut_Updated_Sorted; + if(mUpdatedBoxes.allocate(nbUpdated)) + { + inToOut_Updated_Sorted = reinterpret_cast(keys); + PX_FREE(mInToOut_Updated); + mInToOut_Updated = inToOut_Updated_Sorted; + } + else + { + PX_FREE(keys); + inToOut_Updated_Sorted = mInToOut_Updated; + } + SIMD_AABB_X4* PX_RESTRICT dstBoxesX = mUpdatedBoxes.getBoxes_X(); + initSentinels(dstBoxesX, nbUpdated); + +#ifdef USE_ABP_BUCKETS + Vec4V minV = V4Load(FLT_MAX); + Vec4V maxV = V4Load(-FLT_MAX); +#endif + for(PxU32 i=0;i +static void boxPruningKernel( PxU32 nb0, PxU32 nb1, + const SIMD_AABB_X4* PX_RESTRICT boxes0_X, const SIMD_AABB_X4* PX_RESTRICT boxes1_X, + const SIMD_AABB_YZ4* PX_RESTRICT boxes0_YZ, const SIMD_AABB_YZ4* PX_RESTRICT boxes1_YZ, + const ABP_Index* PX_RESTRICT inToOut0, const ABP_Index* PX_RESTRICT inToOut1, + ABP_PairManager* PX_RESTRICT pairManager, const ABPEntry* PX_RESTRICT objects) +{ + pairManager->mInToOut0 = inToOut0; + pairManager->mInToOut1 = inToOut1; + pairManager->mObjects = objects; + + PxU32 index0 = 0; + PxU32 runningIndex1 = 0; + + while(runningIndex1(&boxes1_YZ[runningIndex1]); + const char* const CurrentBoxListX = reinterpret_cast(&boxes1_X[runningIndex1]); + + if(!gUnrollLoop) + { + while(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) + { + const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2); +#ifdef ABP_SIMD_OVERLAP + if(SIMD_OVERLAP_TEST_14a(box)) +#else + if(intersect2D(box0, *reinterpret_cast(box))) +#endif + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes1_X))>>3; + outputPair(*pairManager, index0, Index1); + } + Offset += 8; + } + } + else + { + +#define BIP_VERSION4 +#ifdef BIP_VERSION4 +#ifdef ABP_SIMD_OVERLAP + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(SIMD_OVERLAP_TEST_14a(box)) \ + goto label; } +#else + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(intersect2D(box0, *reinterpret_cast(box))) \ + goto label; } +#endif + goto StartLoop4; + CODEALIGN16 +FoundOverlap3: + Offset += 8; + CODEALIGN16 +FoundOverlap2: + Offset += 8; + CODEALIGN16 +FoundOverlap1: + Offset += 8; + CODEALIGN16 +FoundOverlap0: + Offset += 8; + CODEALIGN16 +FoundOverlap: + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - 8 - reinterpret_cast(boxes1_X))>>3; + outputPair(*pairManager, index0, Index1); + } + CODEALIGN16 +StartLoop4: + while(*reinterpret_cast(CurrentBoxListX + Offset + 8*5)<=maxLimit) + { + BLOCK4(0, FoundOverlap0) + BLOCK4(8, FoundOverlap1) + BLOCK4(16, FoundOverlap2) + BLOCK4(24, FoundOverlap3) + Offset += 40; + BLOCK4(-8, FoundOverlap) + } +#undef BLOCK4 +#endif + +#ifdef ABP_SIMD_OVERLAP + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(SIMD_OVERLAP_TEST_14a(reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto OverlapFound; \ + Offset += 8; +#else + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(intersect2D(box0, *reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto OverlapFound; \ + Offset += 8; +#endif + + goto LoopStart; + CODEALIGN16 +OverlapFound: + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes1_X))>>3; + outputPair(*pairManager, index0, Index1); + } + Offset += 8; + CODEALIGN16 +LoopStart: + BLOCK + BLOCK + BLOCK + } + } + goto LoopStart; + } +#undef BLOCK + } + } + + index0++; + } +} + +static /*PX_FORCE_INLINE*/ void doBipartiteBoxPruning_Leaf( + ABP_PairManager* PX_RESTRICT pairManager, + const ABPEntry* PX_RESTRICT objects, + PxU32 nb0, + PxU32 nb1, + const SIMD_AABB_X4* PX_RESTRICT boxes0_X, + const SIMD_AABB_X4* PX_RESTRICT boxes1_X, + const SIMD_AABB_YZ4* PX_RESTRICT boxes0_YZ, + const SIMD_AABB_YZ4* PX_RESTRICT boxes1_YZ, + const ABP_Index* PX_RESTRICT remap0, + const ABP_Index* PX_RESTRICT remap1 + ) +{ + PX_ASSERT(boxes0_X[nb0].isSentinel()); + PX_ASSERT(boxes1_X[nb1].isSentinel()); + boxPruningKernel<0>(nb0, nb1, boxes0_X, boxes1_X, boxes0_YZ, boxes1_YZ, remap0, remap1, pairManager, objects); + boxPruningKernel<1>(nb1, nb0, boxes1_X, boxes0_X, boxes1_YZ, boxes0_YZ, remap1, remap0, pairManager, objects); +} + +static PX_FORCE_INLINE void doBipartiteBoxPruning_Leaf(ABP_PairManager* PX_RESTRICT pairManager, const ABPEntry* PX_RESTRICT objects, + PxU32 nb0, PxU32 nb1, const SplitBoxes& boxes0, const SplitBoxes& boxes1, const ABP_Index* PX_RESTRICT remap0, const ABP_Index* PX_RESTRICT remap1) +{ + doBipartiteBoxPruning_Leaf(pairManager, objects, nb0, nb1, boxes0.getBoxes_X(), boxes1.getBoxes_X(), boxes0.getBoxes_YZ(), boxes1.getBoxes_YZ(), remap0, remap1); +} + +static void doCompleteBoxPruning_Leaf( ABP_PairManager* PX_RESTRICT pairManager, PxU32 nb, + const SIMD_AABB_X4* PX_RESTRICT boxes_X, + const SIMD_AABB_YZ4* PX_RESTRICT boxes_YZ, + const ABP_Index* PX_RESTRICT remap, + const ABPEntry* PX_RESTRICT objects) +{ + pairManager->mInToOut0 = remap; + pairManager->mInToOut1 = remap; + pairManager->mObjects = objects; + + PxU32 index0 = 0; + PxU32 runningIndex = 0; + while(runningIndex(&boxes_YZ[runningIndex]); + const char* const CurrentBoxListX = reinterpret_cast(&boxes_X[runningIndex]); + + if(!gUnrollLoop) + { + while(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) + { + const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2); +#ifdef ABP_SIMD_OVERLAP + if(SIMD_OVERLAP_TEST_14a(box)) +#else + if(intersect2D(box0, *reinterpret_cast(box))) +#endif + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes_X))>>3; + outputPair(*pairManager, index0, Index); + } + Offset += 8; + } + } + else + { + +#define VERSION4c +#ifdef VERSION4c +#define VERSION3 // Enable this as our safe loop +#ifdef ABP_SIMD_OVERLAP + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(SIMD_OVERLAP_TEST_14a(box)) \ + goto label; } +#else + #define BLOCK4(x, label) {const SIMD_AABB_YZ4* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(intersect2D(box0, *box)) \ + goto label; } +#endif + goto StartLoop4; + CODEALIGN16 +FoundOverlap3: + Offset += 8; + CODEALIGN16 +FoundOverlap2: + Offset += 8; + CODEALIGN16 +FoundOverlap1: + Offset += 8; + CODEALIGN16 +FoundOverlap0: + Offset += 8; + CODEALIGN16 +FoundOverlap: + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - 8 - reinterpret_cast(boxes_X))>>3; + outputPair(*pairManager, index0, Index); + } + CODEALIGN16 +StartLoop4: + while(*reinterpret_cast(CurrentBoxListX + Offset + 8*5)<=maxLimit) + { + BLOCK4(0, FoundOverlap0) + BLOCK4(8, FoundOverlap1) + BLOCK4(16, FoundOverlap2) + BLOCK4(24, FoundOverlap3) + Offset += 40; + BLOCK4(-8, FoundOverlap) + } +#endif + +#define VERSION3 +#ifdef VERSION3 +#ifdef ABP_SIMD_OVERLAP + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(SIMD_OVERLAP_TEST_14a(reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto BeforeLoop; \ + Offset += 8; +#else + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(intersect2D(box0, *reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto BeforeLoop; \ + Offset += 8; +#endif + + goto StartLoop; + CODEALIGN16 +BeforeLoop: + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes_X))>>3; + outputPair(*pairManager, index0, Index); + Offset += 8; + } + CODEALIGN16 +StartLoop: + BLOCK + BLOCK + BLOCK + BLOCK + BLOCK + } + } + } + } + goto StartLoop; + } +#endif + } + } + + index0++; + } +} + +#ifdef USE_ABP_BUCKETS +static const PxU8 gCodes[] = { 4, 4, 4, 255, 4, 3, 2, 255, + 4, 1, 0, 255, 255, 255, 255, 255 }; + +static PX_FORCE_INLINE PxU8 classifyBoxNew(const SIMD_AABB_YZ4& boxYZ, const float limitY, const float limitZ) +{ +#ifdef ABP_SIMD_OVERLAP + // PT: mins have been negated for SIMD tests + const bool upperPart = (-boxYZ.mMinZ) > limitZ; + const bool rightPart = (-boxYZ.mMinY) > limitY; +#else + const bool upperPart = boxYZ.mMinZ > limitZ; + const bool rightPart = boxYZ.mMinY > limitY; +#endif + const bool lowerPart = boxYZ.mMaxZ < limitZ; + const bool leftPart = boxYZ.mMaxY < limitY; + + // Table-based box classification avoids many branches + const PxU32 Code = PxU32(rightPart)|(PxU32(leftPart)<<1)|(PxU32(upperPart)<<2)|(PxU32(lowerPart)<<3); + PX_ASSERT(gCodes[Code]!=255); + return gCodes[Code]; +} + +//#include +#ifdef RECURSE_LIMIT +static void CompleteBoxPruning_Recursive( + ABP_MM& memoryManager, + ABP_PairManager* PX_RESTRICT pairManager, + PxU32 nb, + const SIMD_AABB_X4* PX_RESTRICT listX, + const SIMD_AABB_YZ4* PX_RESTRICT listYZ, + const ABP_Index* PX_RESTRICT remap, + const ABPEntry* PX_RESTRICT objects) +{ +// printf("CompleteBoxPruning_Recursive %d\n", nb); + if(!nb) + return; + + /*__declspec(align(16))*/ float mergedMin[4]; + /*__declspec(align(16))*/ float mergedMax[4]; + { +//#ifdef SAFE_VERSION + Vec4V maxV = V4LoadA(&listYZ[0].mMinY); + for(PxU32 i=1;i(memoryManager.frameAlloc(sizeof(SIMD_AABB_X4)*(nb+NB_SENTINELS*NB_BUCKETS))); + SIMD_AABB_YZ4* BoxListYZBuffer = reinterpret_cast(memoryManager.frameAlloc(sizeof(SIMD_AABB_YZ4)*nb)); + + PxU32 Counters[NB_BUCKETS]; + for(PxU32 i=0;i(memoryManager.frameAlloc(sizeof(PxU32)*nb)); + PxU8* Indices = reinterpret_cast(memoryManager.frameAlloc(sizeof(PxU8)*nb)); + for(PxU32 i=0;i(memoryManager.frameAlloc(sizeof(SIMD_AABB_X4)*(nb+NB_SENTINELS*NB_BUCKETS))); + SIMD_AABB_YZ4* BoxListYZBuffer = reinterpret_cast(memoryManager.frameAlloc(sizeof(SIMD_AABB_YZ4)*nb)); + + const PxVec3& mergedMin = updatedBounds.minimum; + const PxVec3& mergedMax = updatedBounds.maximum; + const float limitY = (mergedMax[1] + mergedMin[1]) * 0.5f; + const float limitZ = (mergedMax[2] + mergedMin[2]) * 0.5f; + + PxU32 Counters[NB_BUCKETS]; + for(PxU32 i=0;i(memoryManager.frameAlloc(sizeof(PxU32)*nb)); + PxU8* Indices = reinterpret_cast(memoryManager.frameAlloc(sizeof(PxU8)*nb)); + for(PxU32 i=0;iUSE_ABP_BUCKETS) + CompleteBoxPruning_Version16(memoryManager, mDBM.getUpdatedBounds(), pairManager, nbUpdated, + updatedDynamicBoxes_X, + updatedDynamicBoxes_YZ, + mDBM.getRemap_Updated(), objects); + else +#endif + doCompleteBoxPruning_Leaf(pairManager, nbUpdated, + updatedDynamicBoxes_X, + updatedDynamicBoxes_YZ, + mDBM.getRemap_Updated(), objects); + } +} + +void ABP::Region_prepareOverlaps() +{ + if( !mDBM.isThereWorkToDo() + && !mKBM.isThereWorkToDo() + && !mSBM.isThereWorkToDo() + ) + return; + + if(mSBM.isThereWorkToDo()) + { + mSBM.prepareData(mRS, mShared.mABP_Objects, mShared.mABP_Objects_Capacity, mMM); + mDBM.notifyStaticChange(mShared.mABP_Objects); + mKBM.notifyStaticChange(mShared.mABP_Objects); + } + + mDBM.prepareData(mRS, mShared.mABP_Objects, mShared.mABP_Objects_Capacity, mMM); + mKBM.prepareData(mRS, mShared.mABP_Objects, mShared.mABP_Objects_Capacity, mMM); + + mRS.reset(); +} + +// Finds static-vs-dynamic and dynamic-vs-dynamic overlaps +static void findAllOverlaps(ABP_MM& memoryManager, ABP_PairManager& pairManager, const ABP_SharedData& mShared, const StaticManager& mSBM, const DynamicManager& mDBM, bool doComplete, bool doBipartite) +{ + const PxU32 nbUpdatedBoxes = mDBM.getNbUpdatedBoxes(); + + // PT: find dynamics-vs-dynamics overlaps + if(doComplete) + doCompleteBoxPruning_(memoryManager, &pairManager, mShared.mABP_Objects, mDBM); + + // PT: find dynamics-vs-statics overlaps + if(doBipartite) + { + // PT: in previous versions we did active-dynamics-vs-all-statics here. + if(nbUpdatedBoxes) + { + if(mSBM.getNbUpdatedBoxes()) + { + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + nbUpdatedBoxes, mSBM.getNbUpdatedBoxes(), + mDBM.getUpdatedBoxes(), mSBM.getUpdatedBoxes(), + mDBM.getRemap_Updated(), mSBM.getRemap_Updated() + ); + } + if(mSBM.getNbNonUpdatedBoxes()) + { + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + nbUpdatedBoxes, mSBM.getNbNonUpdatedBoxes(), + mDBM.getUpdatedBoxes(), mSBM.getSleepingBoxes(), + mDBM.getRemap_Updated(), mSBM.getRemap_Sleeping() + ); + } + } + + // PT: TODO: refactor this with kinematic stuff, etc.... bit tedious + if(mSBM.getNbUpdatedBoxes() && mDBM.getNbNonUpdatedBoxes()) + { + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + mDBM.getNbNonUpdatedBoxes(), mSBM.getNbUpdatedBoxes(), + mDBM.getSleepingBoxes(), mSBM.getUpdatedBoxes(), + mDBM.getRemap_Sleeping(), mSBM.getRemap_Updated() + ); + } + + } +} + +void ABP::Region_findOverlaps(ABP_PairManager& pairManager) +{ + if(!gPrepareOverlapsFlag) + Region_prepareOverlaps(); + + bool doKineKine = true; + bool doStaticKine = true; + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + { + const bool* lut = mPairManager.mLUT; + doStaticKine = lut[Bp::FilterType::KINEMATIC*4+Bp::FilterType::STATIC]; + doKineKine = lut[Bp::FilterType::KINEMATIC*4+Bp::FilterType::KINEMATIC]; + } + #endif + + findAllOverlaps(mMM, pairManager, mShared, mSBM, mDBM, true, true); + findAllOverlaps(mMM, pairManager, mShared, mSBM, mKBM, doKineKine, doStaticKine); + + const PxU32 nbUpdatedDynamics = mDBM.getNbUpdatedBoxes(); + const PxU32 nbNonUpdatedDynamics = mDBM.getNbNonUpdatedBoxes(); + const PxU32 nbUpdatedKinematics = mKBM.getNbUpdatedBoxes(); + const PxU32 nbNonUpdatedKinematics = mKBM.getNbNonUpdatedBoxes(); + + if(nbUpdatedDynamics) + { + // Active dynamics vs active kinematics + if(nbUpdatedKinematics) + { + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + nbUpdatedDynamics, nbUpdatedKinematics, + mDBM.getUpdatedBoxes(), mKBM.getUpdatedBoxes(), + mDBM.getRemap_Updated(), mKBM.getRemap_Updated() + ); + } + // Active dynamics vs inactive kinematics + if(nbNonUpdatedKinematics) + { + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + nbUpdatedDynamics, nbNonUpdatedKinematics, + mDBM.getUpdatedBoxes(), mKBM.getSleepingBoxes(), + mDBM.getRemap_Updated(), mKBM.getRemap_Sleeping() + ); + } + } + + if(nbUpdatedKinematics && nbNonUpdatedDynamics) + { + // Inactive dynamics vs active kinematics + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + nbNonUpdatedDynamics, nbUpdatedKinematics, + mDBM.getSleepingBoxes(), mKBM.getUpdatedBoxes(), + mDBM.getRemap_Sleeping(), mKBM.getRemap_Updated() + ); + } + + mSBM.finalize(); + mDBM.finalize(); + mKBM.finalize(); +} + +/////////////////////////////////////////////////////////////////////////// + +ABP::ABP() : + mSBM (FilterType::STATIC), + mDBM (FilterType::DYNAMIC), + mKBM (FilterType::KINEMATIC), + mTransientBounds (NULL), + mTransientContactDistance (NULL) +// mTransientGroups (NULL) +{ +} + +ABP::~ABP() +{ + reset(); +} + +void ABP::freeBuffers() +{ + mShared.mRemovedObjects.empty(); +} + +void ABP::preallocate(PxU32 nbObjects, PxU32 maxNbOverlaps) +{ + if(nbObjects) + { + PX_DELETE_ARRAY(mShared.mABP_Objects); + ABP_Object* objects = PX_NEW(ABP_Object)[nbObjects]; + mShared.mABP_Objects = objects; + mShared.mABP_Objects_Capacity = nbObjects; +#if PX_DEBUG + for(PxU32 i=0;iremoveObject(object, userID); + + object.invalidateIndex(); +#if PX_DEBUG + object.mUpdated = false; +#endif +} + +void ABP::updateObject(BpHandle userID) +{ + mShared.mUpdatedObjects.setBitChecked(userID); + + PX_ASSERT(userIDupdateObject(object, userID); +} + +void ABP_PairManager::computeCreatedDeletedPairs(BroadPhaseABP* mbp, const BitArray& updated, const BitArray& removed) +{ + // PT: parse all currently active pairs. The goal here is to generate the found/lost pairs, compared to previous frame. + PxU32 i=0; + PxU32 nbActivePairs = mNbActivePairs; + while(imCreated.pushBack(BroadPhasePair(id0, id1)); + + p.clearNew(); + p.clearUpdated(); + i++; + } + else if(p.isUpdated()) + { + // Persistent pair + + // PT: this pair already existed in the structure, and has been found again this frame. Since + // MBP reports "all pairs" each frame (as opposed to SAP), this happens quite often, for each + // active persistent pair. + p.clearUpdated(); + i++; + } + else + { + // Lost pair + + // PT: if the pair is not new and not 'updated', it might be a lost (separated) pair. But this + // is not always the case since we now handle "sleeping" objects directly within MBP. A pair + // of sleeping objects does not generate an 'addPair' call, so it ends up in this codepath. + // Nonetheless the sleeping pair should not be deleted. We can only delete pairs involving + // objects that have been actually moved during the frame. This is the only case in which + // a pair can indeed become 'lost'. + const PxU32 id0 = p.getId0(); + const PxU32 id1 = p.getId1(); + PX_ASSERT(id0!=INVALID_ID); + PX_ASSERT(id1!=INVALID_ID); + + // PT: if none of the involved objects have been updated, the pair is just sleeping: keep it and skip it. + if(updated.isSetChecked(id0) || updated.isSetChecked(id1)) + { + // PT: by design (for better or worse) we do not report pairs to the client when + // one of the involved objects has been deleted. The pair must still be deleted + // from the MBP structure though. + if(!removed.isSetChecked(id0) && !removed.isSetChecked(id1)) + { + // PT: doing the group-based filtering here is useless. The pair should not have + // been added in the first place. + mbp->mDeleted.pushBack(BroadPhasePair(id0, id1)); + } + + const PxU32 hashValue = hash(id0, id1) & mMask; + removePair(id0, id1, hashValue, i); + nbActivePairs--; + } + else i++; + } + } + + shrinkMemory(); +} + +void ABP::findOverlaps(const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) +{ + mPairManager.mGroups = groups; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mPairManager.mLUT = lut; +#endif + + Region_findOverlaps(mPairManager); +} + +PxU32 ABP::finalize(BroadPhaseABP* mbp) +{ + mPairManager.computeCreatedDeletedPairs(mbp, mShared.mUpdatedObjects, mShared.mRemovedObjects); + + mShared.mUpdatedObjects.clearAll(); + + return mPairManager.mNbActivePairs; +} + +void ABP::reset() +{ + mSBM.reset(); + mDBM.reset(); + mKBM.reset(); + + PX_DELETE_ARRAY(mShared.mABP_Objects); + mShared.mABP_Objects_Capacity = 0; + mPairManager.purge(); + mShared.mUpdatedObjects.empty(); + mShared.mRemovedObjects.empty(); +} + +// PT: TODO: is is really ok to use "transient" data in this function? +void ABP::shiftOrigin(const PxVec3& shift) +{ + PX_UNUSED(shift); // PT: unused because the bounds were pre-shifted before calling this function + + // PT: the AABB manager marks all objects as updated when we shift so the stuff below may not be necessary +} + +void ABP::setTransientData(const PxBounds3* bounds, const PxReal* contactDistance/*, const Bp::FilterGroup::Enum* groups*/) +{ + mTransientBounds = bounds; + mTransientContactDistance = contactDistance; +// mTransientGroups = groups; + + mSBM.setSourceData(bounds, contactDistance); + mDBM.setSourceData(bounds, contactDistance); + mKBM.setSourceData(bounds, contactDistance); +} +/////////////////////////////////////////////////////////////////////////////// + +} + +// Below is the PhysX wrapper = link between AABBManager and ABP + +using namespace internalABP; + +#define DEFAULT_CREATED_DELETED_PAIRS_CAPACITY 1024 + +BroadPhaseABP::BroadPhaseABP( PxU32 maxNbBroadPhaseOverlaps, + PxU32 maxNbStaticShapes, + PxU32 maxNbDynamicShapes, + PxU64 /*contextID*/) : + mGroups (NULL) +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + ,mLUT (NULL) +#endif +{ + mABP = PX_NEW(ABP)(); + + const PxU32 nbObjects = maxNbStaticShapes + maxNbDynamicShapes; + mABP->preallocate(nbObjects, maxNbBroadPhaseOverlaps); + + mCreated.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); + mDeleted.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); +} + +BroadPhaseABP::~BroadPhaseABP() +{ + DELETESINGLE(mABP); +} + +void BroadPhaseABP::update(const PxU32 /*numCpuTasks*/, PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData, physx::PxBaseTask* /*continuation*/, physx::PxBaseTask* narrowPhaseUnblockTask) +{ +#if PX_CHECKED + PX_CHECK_AND_RETURN(scratchAllocator, "BroadPhaseABP::update - scratchAllocator must be non-NULL \n"); +#endif + mABP->mMM.mScratchAllocator = scratchAllocator; + + // PT: TODO: move this out of update function + if(narrowPhaseUnblockTask) + narrowPhaseUnblockTask->removeReference(); + + setUpdateData(updateData); + + update(); + postUpdate(); +} + +void BroadPhaseABP::singleThreadedUpdate(PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData) +{ + mABP->mMM.mScratchAllocator = scratchAllocator; + setUpdateData(updateData); + update(); + postUpdate(); +} + +void BroadPhaseABP::removeObjects(const BroadPhaseUpdateData& updateData) +{ + PxU32 nbToGo = updateData.getNumRemovedHandles(); + if(!nbToGo) + return; + + const BpHandle* PX_RESTRICT removed = updateData.getRemovedHandles(); + PX_ASSERT(removed); + + while(nbToGo--) + { + const BpHandle index = *removed++; + PX_ASSERT(index+1mShared.mABP_Objects_Capacity); // PT: we allocated one more box on purpose + mABP->removeObject(index); + } +} + +void BroadPhaseABP::updateObjects(const BroadPhaseUpdateData& updateData) +{ + const BpHandle* PX_RESTRICT updated = updateData.getUpdatedHandles(); + if(!updated) + return; + + PxU32 nbToGo = updateData.getNumUpdatedHandles(); + while(nbToGo--) + { + const BpHandle index = *updated++; + PX_ASSERT(index+1mShared.mABP_Objects_Capacity); // PT: we allocated one more box on purpose + mABP->updateObject(index); + } +} + +void BroadPhaseABP::addObjects(const BroadPhaseUpdateData& updateData) +{ + PxU32 nbToGo = updateData.getNumCreatedHandles(); + if(!nbToGo) + return; + + const BpHandle* PX_RESTRICT created = updateData.getCreatedHandles(); + PX_ASSERT(created); + + const Bp::FilterGroup::Enum* PX_RESTRICT groups = updateData.getGroups(); + + struct Batch + { + PX_FORCE_INLINE Batch() : mNb(0), mMaxIndex(0) {} + + PxU32 mNb; + PxU32 mMaxIndex; + BpHandle mIndices[ABP_BATCHING]; + + PX_FORCE_INLINE void add(const BpHandle index, internalABP::ABP* PX_RESTRICT abp, FilterType::Enum type) + { + PxU32 nb = mNb; + mMaxIndex = PxMax(mMaxIndex, index); + mIndices[nb++] = index; + if(nb==ABP_BATCHING) + { + mNb = 0; + // PT: TODO: we could use a function ptr here + if(type==FilterType::STATIC) + abp->addStaticObjects(mIndices, ABP_BATCHING, mMaxIndex); + else if(type==FilterType::KINEMATIC) + abp->addKinematicObjects(mIndices, ABP_BATCHING, mMaxIndex); + else + { + PX_ASSERT(type==FilterType::DYNAMIC || type==FilterType::AGGREGATE); + abp->addDynamicObjects(mIndices, ABP_BATCHING, mMaxIndex); + } + + mMaxIndex = 0; + } + else + mNb = nb; + } + }; + Batch statics; + Batch dynamics; + Batch kinematics; + + Batch* batches[FilterType::COUNT]; + batches[FilterType::STATIC] = &statics; + batches[FilterType::DYNAMIC] = &dynamics; + batches[FilterType::AGGREGATE] = &dynamics; + batches[FilterType::KINEMATIC] = &kinematics; + + while(nbToGo--) + { + const BpHandle index = *created++; + PX_ASSERT(index+1mShared.mABP_Objects_Capacity); // PT: we allocated one more box on purpose + const FilterType::Enum type = FilterType::Enum(groups[index]&3); + batches[type]->add(index, mABP, type); + } + + if(statics.mNb) + mABP->addStaticObjects(statics.mIndices, statics.mNb, statics.mMaxIndex); + if(kinematics.mNb) + mABP->addKinematicObjects(kinematics.mIndices, kinematics.mNb, kinematics.mMaxIndex); + if(dynamics.mNb) + mABP->addDynamicObjects(dynamics.mIndices, dynamics.mNb, dynamics.mMaxIndex); +} + +void BroadPhaseABP::setUpdateData(const BroadPhaseUpdateData& updateData) +{ + mABP->setTransientData(updateData.getAABBs(), updateData.getContactDistance()/*, updateData.getGroups()*/); + + const PxU32 newCapacity = updateData.getCapacity(); + mABP->mShared.checkResize(newCapacity); + +#if PX_CHECKED + // PT: WARNING: this must be done after the allocateMappingArray call + if(!BroadPhaseUpdateData::isValid(updateData, *this)) + { + PX_CHECK_MSG(false, "Illegal BroadPhaseUpdateData \n"); + return; + } +#endif + + mGroups = updateData.getGroups(); +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT = updateData.getLUT(); +#endif + + removeObjects(updateData); + addObjects(updateData); + updateObjects(updateData); + + PX_ASSERT(!mCreated.size()); + PX_ASSERT(!mDeleted.size()); + + if(gPrepareOverlapsFlag) + mABP->Region_prepareOverlaps(); +} + +void BroadPhaseABP::update() +{ + mABP->findOverlaps(mGroups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , mLUT +#endif + ); +} + +void BroadPhaseABP::postUpdate() +{ + mABP->finalize(this); +} + +PxU32 BroadPhaseABP::getNbCreatedPairs() const +{ + return mCreated.size(); +} + +BroadPhasePair* BroadPhaseABP::getCreatedPairs() +{ + return mCreated.begin(); +} + +PxU32 BroadPhaseABP::getNbDeletedPairs() const +{ + return mDeleted.size(); +} + +BroadPhasePair* BroadPhaseABP::getDeletedPairs() +{ + return mDeleted.begin(); +} + +static void freeBuffer(Ps::Array& buffer) +{ + const PxU32 size = buffer.size(); + if(size>DEFAULT_CREATED_DELETED_PAIRS_CAPACITY) + { + buffer.reset(); + buffer.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); + } + else + { + buffer.clear(); + } +} + +void BroadPhaseABP::freeBuffers() +{ + mABP->freeBuffers(); + freeBuffer(mCreated); + freeBuffer(mDeleted); +} + +#if PX_CHECKED +bool BroadPhaseABP::isValid(const BroadPhaseUpdateData& updateData) const +{ + const PxU32 nbObjects = mABP->mShared.mABP_Objects_Capacity; + PX_UNUSED(nbObjects); + const ABP_Object* PX_RESTRICT objects = mABP->mShared.mABP_Objects; + const BpHandle* created = updateData.getCreatedHandles(); + if(created) + { + PxU32 nbToGo = updateData.getNumCreatedHandles(); + while(nbToGo--) + { + const BpHandle index = *created++; + PX_ASSERT(indexshiftOrigin(shift); +} + +PxU32 BroadPhaseABP::getCurrentNbPairs() const +{ + return mABP->mPairManager.mNbActivePairs; +} + +void BroadPhaseABP::setScratchAllocator(PxcScratchAllocator* scratchAllocator) +{ + mABP->mMM.mScratchAllocator = scratchAllocator; +} + + + +BroadPhase* createABP( + const PxU32 maxNbBroadPhaseOverlaps, + const PxU32 maxNbStaticShapes, + const PxU32 maxNbDynamicShapes, + PxU64 contextID) +{ + return PX_NEW(BroadPhaseABP)(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); +} + diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h new file mode 100644 index 000000000..0ab400bbb --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h @@ -0,0 +1,101 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_ABP_H +#define BP_BROADPHASE_ABP_H + +#include "CmPhysXCommon.h" +#include "BpBroadPhase.h" +#include "PxPhysXConfig.h" +#include "BpBroadPhaseUpdate.h" +#include "PsUserAllocated.h" + +namespace internalABP{ + class ABP; +} + +namespace physx +{ +namespace Bp +{ + class BroadPhaseABP : public BroadPhase, public Ps::UserAllocated + { + PX_NOCOPY(BroadPhaseABP) + public: + BroadPhaseABP( PxU32 maxNbBroadPhaseOverlaps, + PxU32 maxNbStaticShapes, + PxU32 maxNbDynamicShapes, + PxU64 contextID); + virtual ~BroadPhaseABP(); + + // BroadPhase + virtual PxBroadPhaseType::Enum getType() const { return PxBroadPhaseType::eABP; } + virtual void destroy() { delete this; } + virtual void update(const PxU32 numCpuTasks, PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData, physx::PxBaseTask* continuation, physx::PxBaseTask* narrowPhaseUnblockTask); + virtual void fetchBroadPhaseResults(physx::PxBaseTask*) {} + virtual PxU32 getNbCreatedPairs() const; + virtual BroadPhasePair* getCreatedPairs(); + virtual PxU32 getNbDeletedPairs() const; + virtual BroadPhasePair* getDeletedPairs(); + virtual void freeBuffers(); + virtual void shiftOrigin(const PxVec3& shift); +#if PX_CHECKED + virtual bool isValid(const BroadPhaseUpdateData& updateData) const; +#endif + virtual BroadPhasePair* getBroadPhasePairs() const {return NULL;} //KS - TODO - implement this!!! + virtual void deletePairs(){} //KS - TODO - implement this!!! + virtual void singleThreadedUpdate(PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData); + //~BroadPhase + + internalABP::ABP* mABP; // PT: TODO: aggregate + + Ps::Array mCreated; + Ps::Array mDeleted; + + const Bp::FilterGroup::Enum*mGroups; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* mLUT; +#endif + void setUpdateData(const BroadPhaseUpdateData& updateData); + void addObjects(const BroadPhaseUpdateData& updateData); + void removeObjects(const BroadPhaseUpdateData& updateData); + void updateObjects(const BroadPhaseUpdateData& updateData); + + void update(); + void postUpdate(); + + PxU32 getCurrentNbPairs() const; + void setScratchAllocator(PxcScratchAllocator* scratchAllocator); + }; + +} //namespace Bp + +} //namespace physx + +#endif // BP_BROADPHASE_ABP_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp new file mode 100644 index 000000000..92fe49d1d --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp @@ -0,0 +1,3422 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "BpBroadPhaseMBP.h" +#include "BpBroadPhaseShared.h" +#include "CmRadixSortBuffered.h" +#include "PsUtilities.h" +#include "PsFoundation.h" +#include "PsVecMath.h" + +using namespace physx::shdfnd::aos; + +//#define CHECK_NB_OVERLAPS +#define USE_FULLY_INSIDE_FLAG +//#define MBP_USE_NO_CMP_OVERLAP_3D // Seems slower + +//HWSCAN: reverse bits in fully-inside-flag bitmaps because the code gives us indices for which bits are set (and we want the opposite) +#define HWSCAN + +using namespace physx; +using namespace Bp; +using namespace Cm; + + static PX_FORCE_INLINE MBP_Handle encodeHandle(MBP_ObjectIndex objectIndex, PxU32 flipFlop, bool isStatic) + { + /* objectIndex += objectIndex; + objectIndex |= flipFlop; + return objectIndex;*/ + return (objectIndex<<2)|(flipFlop<<1)|PxU32(isStatic); + } + + static PX_FORCE_INLINE MBP_ObjectIndex decodeHandle_Index(MBP_Handle handle) + { + // return handle>>1; + return handle>>2; + } + + static PX_FORCE_INLINE PxU32 decodeHandle_IsStatic(MBP_Handle handle) + { + return handle&1; + } + +#define MBP_ALLOC(x) PX_ALLOC(x, "MBP") +#define MBP_ALLOC_TMP(x) PX_ALLOC_TEMP(x, "MBP_TMP") +#define MBP_FREE(x) if(x) PX_FREE_AND_RESET(x) +#define DELETESINGLE(x) if (x) { delete x; x = NULL; } +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +#define INVALID_ID 0xffffffff + + typedef MBP_Index* MBP_Mapping; + +/* PX_FORCE_INLINE PxU32 encodeFloat(const float val) + { + // We may need to check on -0 and 0 + // But it should make no practical difference. + PxU32 ir = IR(val); + + if(ir & 0x80000000) //negative? + ir = ~ir;//reverse sequence of negative numbers + else + ir |= 0x80000000; // flip sign + + return ir; + }*/ + +struct RegionHandle : public Ps::UserAllocated +{ + PxU16 mHandle; // Handle from region + PxU16 mInternalBPHandle; // Index of region data within mRegions +}; + +enum MBPFlags +{ + MBP_FLIP_FLOP = (1<<1), + MBP_REMOVED = (1<<2) // ### added for TA24714, not needed otherwise +}; + +// We have one of those for each of the "200K" objects so we should optimize this size as much as possible +struct MBP_Object : public Ps::UserAllocated +{ + BpHandle mUserID; // Handle sent to us by the AABB manager + PxU16 mNbHandles; // Number of regions the object is part of + PxU16 mFlags; // MBPFlags ### only 1 bit used in the end + + PX_FORCE_INLINE bool getFlipFlop() const { return (mFlags & MBP_FLIP_FLOP)==0; } + + union + { + RegionHandle mHandle; + PxU32 mHandlesIndex; + }; +}; + +// This one is used in each Region +struct MBPEntry : public Ps::UserAllocated +{ + PX_FORCE_INLINE MBPEntry() + { + mMBPHandle = INVALID_ID; + } + + // ### mIndex could be PxU16 but beware, we store mFirstFree there + PxU32 mIndex; // Out-to-in, maps user handle to internal array. mIndex indexes either the static or dynamic array. + MBP_Handle mMBPHandle; // MBP-level handle (the one returned to users) +#if PX_DEBUG + bool mUpdated; +#endif + + PX_FORCE_INLINE PxU32 isStatic() const + { + return decodeHandle_IsStatic(mMBPHandle); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +//#define BIT_ARRAY_STACK 512 + + static PX_FORCE_INLINE PxU32 bitsToDwords(PxU32 nbBits) + { + return (nbBits>>5) + ((nbBits&31) ? 1 : 0); + } + + // Use that one instead of an array of bools. Takes less ram, nearly as fast [no bounds checkings and so on]. + class BitArray + { + public: + BitArray(); + BitArray(PxU32 nbBits); + ~BitArray(); + + bool init(PxU32 nbBits); + void empty(); + void resize(PxU32 nbBits); + + PX_FORCE_INLINE void setBitChecked(PxU32 bitNumber) + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + resize(bitNumber); + mBits[index] |= 1<<(bitNumber&31); + } + + PX_FORCE_INLINE void clearBitChecked(PxU32 bitNumber) + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + resize(bitNumber); + mBits[index] &= ~(1<<(bitNumber&31)); + } + // Data management + PX_FORCE_INLINE void setBit(PxU32 bitNumber) { mBits[bitNumber>>5] |= 1<<(bitNumber&31); } + PX_FORCE_INLINE void clearBit(PxU32 bitNumber) { mBits[bitNumber>>5] &= ~(1<<(bitNumber&31)); } + PX_FORCE_INLINE void toggleBit(PxU32 bitNumber) { mBits[bitNumber>>5] ^= 1<<(bitNumber&31); } + + PX_FORCE_INLINE void clearAll() { PxMemZero(mBits, mSize*4); } + PX_FORCE_INLINE void setAll() { PxMemSet(mBits, 0xff, mSize*4); } + + // Data access + PX_FORCE_INLINE Ps::IntBool isSet(PxU32 bitNumber) const { return Ps::IntBool(mBits[bitNumber>>5] & (1<<(bitNumber&31))); } + PX_FORCE_INLINE Ps::IntBool isSetChecked(PxU32 bitNumber) const + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + return 0; + return Ps::IntBool(mBits[index] & (1<<(bitNumber&31))); + } + + PX_FORCE_INLINE const PxU32* getBits() const { return mBits; } + PX_FORCE_INLINE PxU32 getSize() const { return mSize; } + + // PT: replicate Cm::BitMap stuff for temp testing + PxU32 findLast() const + { + for(PxU32 i = mSize; i-- > 0;) + { + if(mBits[i]) + return (i<<5)+Ps::highestSetBit(mBits[i]); + } + return PxU32(0); + } + protected: + PxU32* mBits; //!< Array of bits + PxU32 mSize; //!< Size of the array in dwords +#ifdef BIT_ARRAY_STACK + PxU32 mStack[BIT_ARRAY_STACK]; +#endif + }; + +/////////////////////////////////////////////////////////////////////////////// + +BitArray::BitArray() : mBits(NULL), mSize(0) +{ +} + +BitArray::BitArray(PxU32 nbBits) : mBits(NULL), mSize(0) +{ + init(nbBits); +} + +BitArray::~BitArray() +{ + empty(); +} + +void BitArray::empty() +{ +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); + mBits = NULL; + mSize = 0; +} + +bool BitArray::init(PxU32 nbBits) +{ + mSize = bitsToDwords(nbBits); + // Get ram for n bits +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); +#ifdef BIT_ARRAY_STACK + if(mSize>BIT_ARRAY_STACK) +#endif + mBits = reinterpret_cast(MBP_ALLOC(sizeof(PxU32)*mSize)); +#ifdef BIT_ARRAY_STACK + else + mBits = mStack; +#endif + + // Set all bits to 0 + clearAll(); + return true; +} + +void BitArray::resize(PxU32 nbBits) +{ + const PxU32 newSize = bitsToDwords(nbBits+128); + PxU32* newBits = NULL; +#ifdef BIT_ARRAY_STACK + if(newSize>BIT_ARRAY_STACK) +#endif + { + // Old buffer was stack or allocated, new buffer is allocated + newBits = reinterpret_cast(MBP_ALLOC(sizeof(PxU32)*newSize)); + if(mSize) + PxMemCopy(newBits, mBits, sizeof(PxU32)*mSize); + } +#ifdef BIT_ARRAY_STACK + else + { + newBits = mStack; + if(mSize>BIT_ARRAY_STACK) + { + // Old buffer was allocated, new buffer is stack => copy to stack, shrink + CopyMemory(newBits, mBits, sizeof(PxU32)*BIT_ARRAY_STACK); + } + else + { + // Old buffer was stack, new buffer is stack => keep working on the same stack buffer, nothing to do + } + } +#endif + const PxU32 remain = newSize - mSize; + if(remain) + PxMemZero(newBits + mSize, remain*sizeof(PxU32)); + +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); + mBits = newBits; + mSize = newSize; +} + +#ifdef USE_FULLY_INSIDE_FLAG +static PX_FORCE_INLINE void setBit(BitArray& bitmap, MBP_ObjectIndex objectIndex) +{ + #ifdef HWSCAN + bitmap.clearBitChecked(objectIndex); //HWSCAN + #else + bitmap.setBitChecked(objectIndex); + #endif +} + +static PX_FORCE_INLINE void clearBit(BitArray& bitmap, MBP_ObjectIndex objectIndex) +{ + #ifdef HWSCAN + bitmap.setBitChecked(objectIndex); // HWSCAN + #else + bitmap.clearBitChecked(objectIndex); + #endif +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef MBP_SIMD_OVERLAP + typedef SIMD_AABB MBP_AABB; +#else + typedef IAABB MBP_AABB; +#endif + + struct MBPEntry; + struct RegionHandle; + struct MBP_Object; + + class MBP_PairManager : public PairManagerData + { + public: + MBP_PairManager(); + ~MBP_PairManager(); + + InternalPair* addPair (PxU32 id0, PxU32 id1); +// bool removePair (PxU32 id0, PxU32 id1); + bool computeCreatedDeletedPairs (const MBP_Object* objects, BroadPhaseMBP* mbp, const BitArray& updated, const BitArray& removed); + + const Bp::FilterGroup::Enum* mGroups; + const MBP_Object* mObjects; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* mLUT; +#endif + }; + + /////////////////////////////////////////////////////////////////////////// + + #define STACK_BUFFER_SIZE 256 + struct MBPOS_TmpBuffers + { + MBPOS_TmpBuffers(); + ~MBPOS_TmpBuffers(); + + void allocateSleeping(PxU32 nbSleeping, PxU32 nbSentinels); + void allocateUpdated(PxU32 nbUpdated, PxU32 nbSentinels); + + // PT: wtf, why doesn't the 128 version compile? +// MBP_AABB PX_ALIGN(128, mSleepingDynamicBoxes_Stack[STACK_BUFFER_SIZE]); +// MBP_AABB PX_ALIGN(128, mUpdatedDynamicBoxes_Stack[STACK_BUFFER_SIZE]); + MBP_AABB PX_ALIGN(16, mSleepingDynamicBoxes_Stack[STACK_BUFFER_SIZE]); + MBP_AABB PX_ALIGN(16, mUpdatedDynamicBoxes_Stack[STACK_BUFFER_SIZE]); + MBP_Index mInToOut_Dynamic_Sleeping_Stack[STACK_BUFFER_SIZE]; + + PxU32 mNbSleeping; + PxU32 mNbUpdated; + MBP_Index* mInToOut_Dynamic_Sleeping; + MBP_AABB* mSleepingDynamicBoxes; + MBP_AABB* mUpdatedDynamicBoxes; + }; + + struct BIP_Input + { + BIP_Input() : + mObjects (NULL), + mNbUpdatedBoxes (0), + mNbStaticBoxes (0), + mDynamicBoxes (NULL), + mStaticBoxes (NULL), + mInToOut_Static (NULL), + mInToOut_Dynamic(NULL), + mNeeded (false) + { + } + + const MBPEntry* mObjects; + PxU32 mNbUpdatedBoxes; + PxU32 mNbStaticBoxes; + const MBP_AABB* mDynamicBoxes; + const MBP_AABB* mStaticBoxes; + const MBP_Index* mInToOut_Static; + const MBP_Index* mInToOut_Dynamic; + bool mNeeded; + }; + + struct BoxPruning_Input + { + BoxPruning_Input() : + mObjects (NULL), + mUpdatedDynamicBoxes (NULL), + mSleepingDynamicBoxes (NULL), + mInToOut_Dynamic (NULL), + mInToOut_Dynamic_Sleeping (NULL), + mNbUpdated (0), + mNbNonUpdated (0), + mNeeded (false) + { + } + + const MBPEntry* mObjects; + const MBP_AABB* mUpdatedDynamicBoxes; + const MBP_AABB* mSleepingDynamicBoxes; + const MBP_Index* mInToOut_Dynamic; + const MBP_Index* mInToOut_Dynamic_Sleeping; + PxU32 mNbUpdated; + PxU32 mNbNonUpdated; + bool mNeeded; + + BIP_Input mBIPInput; + }; + + class Region : public Ps::UserAllocated + { + PX_NOCOPY(Region) + public: + Region(); + ~Region(); + + void updateObject(const MBP_AABB& bounds, MBP_Index handle); + MBP_Index addObject(const MBP_AABB& bounds, MBP_Handle mbpHandle, bool isStatic); + void removeObject(MBP_Index handle); + MBP_Handle retrieveBounds(MBP_AABB& bounds, MBP_Index handle) const; + void setBounds(MBP_Index handle, const MBP_AABB& bounds); + void prepareOverlaps(); + void findOverlaps(MBP_PairManager& pairManager); + +// private: + BoxPruning_Input PX_ALIGN(16, mInput); + PxU32 mNbObjects; + PxU32 mMaxNbObjects; + PxU32 mFirstFree; + MBPEntry* mObjects; // All objects, indexed by user handle + PxU32 mMaxNbStaticBoxes; + PxU32 mNbStaticBoxes; + PxU32 mMaxNbDynamicBoxes; + PxU32 mNbDynamicBoxes; + MBP_AABB* mStaticBoxes; + MBP_AABB* mDynamicBoxes; + MBP_Mapping mInToOut_Static; // Maps static boxes to mObjects + MBP_Mapping mInToOut_Dynamic; // Maps dynamic boxes to mObjects + PxU32* mPosList; + PxU32 mNbUpdatedBoxes; + PxU32 mPrevNbUpdatedBoxes; + BitArray mStaticBits; + RadixSortBuffered mRS; + bool mNeedsSorting; + bool mNeedsSortingSleeping; + + MBPOS_TmpBuffers mTmpBuffers; + + void optimizeMemory(); + void resizeObjects(); + void staticSort(); + void preparePruning(MBPOS_TmpBuffers& buffers); + void prepareBIPPruning(const MBPOS_TmpBuffers& buffers); + }; + + /////////////////////////////////////////////////////////////////////////// + +// We have one of those for each Region within the MBP +struct RegionData : public Ps::UserAllocated +{ + MBP_AABB mBox; // Volume of space controlled by this Region + Region* mBP; // Pointer to Region itself + Ps::IntBool mOverlap; // True if overlaps other regions + void* mUserData; // Region identifier, reused to contain "first free ID" +}; + + #define MAX_NB_MBP 256 +// #define MAX_NB_MBP 16 + + class MBP : public Ps::UserAllocated + { + public: + MBP(); + ~MBP(); + + void preallocate(PxU32 nbRegions, PxU32 nbObjects, PxU32 maxNbOverlaps); + void reset(); + void freeBuffers(); + + PxU32 addRegion(const PxBroadPhaseRegion& region, bool populateRegion); + bool removeRegion(PxU32 handle); + const Region* getRegion(PxU32 i) const; + PX_FORCE_INLINE PxU32 getNbRegions() const { return mNbRegions; } + + MBP_Handle addObject(const MBP_AABB& box, BpHandle userID, bool isStatic); + bool removeObject(MBP_Handle handle); + bool updateObject(MBP_Handle handle, const MBP_AABB& box); + bool updateObjectAfterRegionRemoval(MBP_Handle handle, Region* removedRegion); + bool updateObjectAfterNewRegionAdded(MBP_Handle handle, const MBP_AABB& box, Region* addedRegion, PxU32 regionIndex); + void prepareOverlaps(); + void findOverlaps(const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ); + PxU32 finalize(BroadPhaseMBP* mbp); + void shiftOrigin(const PxVec3& shift); + + void setTransientBounds(const PxBounds3* bounds, const PxReal* contactDistance); +// private: + PxU32 mNbRegions; + MBP_ObjectIndex mFirstFreeIndex; // First free recycled index for mMBP_Objects + PxU32 mFirstFreeIndexBP; // First free recycled index for mRegions + Ps::Array mRegions; + Ps::Array mMBP_Objects; + MBP_PairManager mPairManager; + + BitArray mUpdatedObjects; // Indexed by MBP_ObjectIndex + BitArray mRemoved; // Indexed by MBP_ObjectIndex + Ps::Array mHandles[MAX_NB_MBP+1]; + PxU32 mFirstFree[MAX_NB_MBP+1]; + PX_FORCE_INLINE RegionHandle* getHandles(MBP_Object& currentObject, PxU32 nbHandles); + void purgeHandles(MBP_Object* PX_RESTRICT object, PxU32 nbHandles); + void storeHandles(MBP_Object* PX_RESTRICT object, PxU32 nbHandles, const RegionHandle* PX_RESTRICT handles); + + Ps::Array mOutOfBoundsObjects; // These are BpHandle but the BP interface expects PxU32s + void addToOutOfBoundsArray(BpHandle id); + + const PxBounds3* mTransientBounds; + const PxReal* mTransientContactDistance; + +#ifdef USE_FULLY_INSIDE_FLAG + BitArray mFullyInsideBitmap; // Indexed by MBP_ObjectIndex +#endif + void populateNewRegion(const MBP_AABB& box, Region* addedRegion, PxU32 regionIndex); + +#ifdef MBP_REGION_BOX_PRUNING + void buildRegionData(); + MBP_AABB mSortedRegionBoxes[MAX_NB_MBP]; + PxU32 mSortedRegionIndices[MAX_NB_MBP]; + PxU32 mNbActiveRegions; + bool mDirtyRegions; +#endif + }; + +#ifdef MBP_SIMD_OVERLAP + #define MBP_OVERLAP_TEST(x) SIMD_OVERLAP_TEST(x) +#else + #define MBP_OVERLAP_TEST(x) if(intersect2D(box0, x)) +#endif + +#define DEFAULT_NB_ENTRIES 128 + +#ifdef MBP_SIMD_OVERLAP + static PX_FORCE_INLINE void initSentinel(SIMD_AABB& box) + { + // box.mMinX = encodeFloat(FLT_MAX)>>1; + box.mMinX = 0xffffffff; + } + #if PX_DEBUG + static PX_FORCE_INLINE bool isSentinel(const SIMD_AABB& box) + { + return box.mMinX == 0xffffffff; + } + #endif +#else + static PX_FORCE_INLINE void initSentinel(MBP_AABB& box) + { + // box.mMinX = encodeFloat(FLT_MAX)>>1; + box.mMinX = 0xffffffff; + } + #if PX_DEBUG + static PX_FORCE_INLINE bool isSentinel(const MBP_AABB& box) + { + return box.mMinX == 0xffffffff; + } + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// + +MBP_PairManager::MBP_PairManager() : + mGroups (NULL), + mObjects (NULL) +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + ,mLUT (NULL) +#endif +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +MBP_PairManager::~MBP_PairManager() +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +InternalPair* MBP_PairManager::addPair(PxU32 id0, PxU32 id1) +{ + PX_ASSERT(id0!=INVALID_ID); + PX_ASSERT(id1!=INVALID_ID); + PX_ASSERT(mGroups); + PX_ASSERT(mObjects); + + { + const MBP_ObjectIndex index0 = decodeHandle_Index(id0); + const MBP_ObjectIndex index1 = decodeHandle_Index(id1); + + const BpHandle object0 = mObjects[index0].mUserID; + const BpHandle object1 = mObjects[index1].mUserID; + +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + if(!groupFiltering(mGroups[object0], mGroups[object1], mLUT)) +#else + if(!groupFiltering(mGroups[object0], mGroups[object1])) +#endif + return NULL; + } + + return addPairInternal(id0, id1); +} + +/////////////////////////////////////////////////////////////////////////////// + +/*bool MBP_PairManager::removePair(PxU32 id0, PxU32 id1) +{ + // Order the ids + sort(id0, id1); + + const PxU32 hashValue = hash(id0, id1) & mMask; + const InternalPair* p = findPair(id0, id1, hashValue); + if(!p) + return false; + PX_ASSERT(p->getId0()==id0); + PX_ASSERT(p->getId1()==id1); + + PairManagerData::removePair(id0, id1, hashValue, getPairIndex(p)); + + shrinkMemory(); + return true; +}*/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef MBP_SIMD_OVERLAP + #define SIMD_OVERLAP_PRELOAD_BOX0 \ + __m128i b = _mm_loadu_si128(reinterpret_cast(&box0.mMinY)); \ + b = _mm_shuffle_epi32(b, 78); + + // PT: technically we don't need the 16 bits from _mm_movemask_epi8, we only + // need the 4 bits from _mm_movemask_ps. Would it be faster? In any case this + // works thanks to the _mm_cmpgt_epi32 which puts the same values in each byte + // of each separate 32bits components. + #define SIMD_OVERLAP_TEST(x) \ + const __m128i a = _mm_loadu_si128(reinterpret_cast(&x.mMinY)); \ + const __m128i d = _mm_cmpgt_epi32(a, b); \ + const int mask = _mm_movemask_epi8(d); \ + if(mask==0x0000ff00) + +#else + #define SIMD_OVERLAP_PRELOAD_BOX0 +#endif + +#ifdef MBP_USE_NO_CMP_OVERLAP +/*static PX_FORCE_INLINE void initBox(IAABB& box, const PxBounds3& src) +{ + box.initFrom2(src); +}*/ +#else +static PX_FORCE_INLINE void initBox(IAABB& box, const PxBounds3& src) +{ + box.initFrom(src); +} +#endif + +Region::Region() : + mNbObjects (0), + mMaxNbObjects (0), + mFirstFree (INVALID_ID), + mObjects (NULL), + mMaxNbStaticBoxes (0), + mNbStaticBoxes (0), + mMaxNbDynamicBoxes (0), + mNbDynamicBoxes (0), + mStaticBoxes (NULL), + mDynamicBoxes (NULL), + mInToOut_Static (NULL), + mInToOut_Dynamic (NULL), + mPosList (NULL), + mNbUpdatedBoxes (0), + mPrevNbUpdatedBoxes (0), + mNeedsSorting (false), + mNeedsSortingSleeping (true) +{ +} + +Region::~Region() +{ + DELETEARRAY(mObjects); + MBP_FREE(mPosList); + MBP_FREE(mInToOut_Dynamic); + MBP_FREE(mInToOut_Static); + DELETEARRAY(mDynamicBoxes); + DELETEARRAY(mStaticBoxes); +} + +// Pre-sort static boxes +#define STACK_BUFFER_SIZE_STATIC_SORT 8192 + #define DEFAULT_NUM_DYNAMIC_BOXES 1024 + +void Region::staticSort() +{ + // For now this version is only compatible with: + // MBP_USE_WORDS + // MBP_USE_SENTINELS + + mNeedsSorting = false; + + const PxU32 nbStaticBoxes = mNbStaticBoxes; + if(!nbStaticBoxes) + { + mStaticBits.empty(); + return; + } + +// PxU32 Time; +// StartProfile(Time); + + // Roadmap: + // - gather updated/modified static boxes + // - sort those, and those only + // - merge sorted set with previously existing (and previously sorted set) + + // Separate things-to-sort and things-already-sorted + const PxU32 totalSize = sizeof(PxU32)*nbStaticBoxes*4; + PxU8 stackBuffer[STACK_BUFFER_SIZE_STATIC_SORT]; + PxU8* tempMemory = totalSize<=STACK_BUFFER_SIZE_STATIC_SORT ? stackBuffer : reinterpret_cast(MBP_ALLOC_TMP(totalSize)); + PxU32* minPosList_ToSort = reinterpret_cast(tempMemory); + PxU32* minPosList_Sorted = reinterpret_cast(tempMemory + sizeof(PxU32)*nbStaticBoxes); + PxU32* boxIndices_ToSort = reinterpret_cast(tempMemory + sizeof(PxU32)*nbStaticBoxes*2); + PxU32* boxIndices_Sorted = reinterpret_cast(tempMemory + sizeof(PxU32)*nbStaticBoxes*3); + PxU32 nbToSort = 0; + PxU32 nbSorted = 0; + for(PxU32 i=0;i(MBP_ALLOC(sizeof(MBP_Index)*mMaxNbStaticBoxes)); + const PxU32 nbStaticSentinels = 2; + MBP_AABB* sortedBoxes = PX_NEW(MBP_AABB)[mMaxNbStaticBoxes+nbStaticSentinels]; + initSentinel(sortedBoxes[nbStaticBoxes]); + initSentinel(sortedBoxes[nbStaticBoxes+1]); + +// EndProfile(Time); +// printf("Part2b: %d\n", Time); + +// StartProfile(Time); + + // Merge streams to final buffers + PxU32 offsetSorted = 0; + PxU32 offsetNonSorted = 0; + + PxU32 nextCandidateNonSorted = offsetNonSorted(MBP_ALLOC(sizeof(MBP_Index)*newNbBoxes)); + if(oldNbBoxes) + PxMemCopy(newMapping, mapping, oldNbBoxes*sizeof(MBP_Index)); + MBP_FREE(mapping); + return newMapping; +} + +static PX_FORCE_INLINE void MTF(MBP_AABB* PX_RESTRICT dynamicBoxes, MBP_Index* PX_RESTRICT inToOut_Dynamic, MBPEntry* PX_RESTRICT objects, const MBP_AABB& bounds, PxU32 frontIndex, MBPEntry& updatedObject) +{ + const PxU32 updatedIndex = updatedObject.mIndex; + if(frontIndex!=updatedIndex) + { + const MBP_AABB box0 = dynamicBoxes[frontIndex]; + dynamicBoxes[frontIndex] = bounds; + dynamicBoxes[updatedIndex] = box0; + + const MBP_Index index0 = inToOut_Dynamic[frontIndex]; + inToOut_Dynamic[frontIndex] = inToOut_Dynamic[updatedIndex]; + inToOut_Dynamic[updatedIndex] = index0; + + objects[index0].mIndex = updatedIndex; + updatedObject.mIndex = frontIndex; + } + else + { + dynamicBoxes[frontIndex] = bounds; + } +} + +MBP_Index Region::addObject(const MBP_AABB& bounds, MBP_Handle mbpHandle, bool isStatic) +{ +#ifdef MBP_USE_WORDS + PX_ASSERT(mNbObjects<0xffff); +#endif + PX_ASSERT((decodeHandle_IsStatic(mbpHandle) && isStatic) || (!decodeHandle_IsStatic(mbpHandle) && !isStatic)); + + MBP_Index handle; + if(mFirstFree!=INVALID_ID) + { + handle = MBP_Index(mFirstFree); + mFirstFree = mObjects[handle].mIndex; + } + else + { + if(mMaxNbObjects==mNbObjects) + resizeObjects(); + + handle = MBP_Index(mNbObjects); + } + mNbObjects++; + /// + + PxU32 boxIndex; + if(isStatic) + { + if(mMaxNbStaticBoxes==mNbStaticBoxes) + { + const PxU32 newMaxNbBoxes = mMaxNbStaticBoxes ? mMaxNbStaticBoxes + DEFAULT_NB_ENTRIES : DEFAULT_NB_ENTRIES; +// const PxU32 newMaxNbBoxes = mMaxNbStaticBoxes ? mMaxNbStaticBoxes*2 : DEFAULT_NB_ENTRIES; + mStaticBoxes = resizeBoxes(mNbStaticBoxes, newMaxNbBoxes, mStaticBoxes); + mInToOut_Static = resizeMapping(mNbStaticBoxes, newMaxNbBoxes, mInToOut_Static); + mMaxNbStaticBoxes = newMaxNbBoxes; + } + + boxIndex = mNbStaticBoxes++; + mStaticBoxes[boxIndex] = bounds; + mInToOut_Static[boxIndex] = handle; + mNeedsSorting = true; + mStaticBits.setBitChecked(boxIndex); + } + else + { + if(mMaxNbDynamicBoxes==mNbDynamicBoxes) + { + const PxU32 newMaxNbBoxes = mMaxNbDynamicBoxes ? mMaxNbDynamicBoxes + DEFAULT_NB_ENTRIES : DEFAULT_NB_ENTRIES; +// const PxU32 newMaxNbBoxes = mMaxNbDynamicBoxes ? mMaxNbDynamicBoxes*2 : DEFAULT_NB_ENTRIES; + mDynamicBoxes = resizeBoxes(mNbDynamicBoxes, newMaxNbBoxes, mDynamicBoxes); + mInToOut_Dynamic = resizeMapping(mNbDynamicBoxes, newMaxNbBoxes, mInToOut_Dynamic); + mMaxNbDynamicBoxes = newMaxNbBoxes; + + MBP_FREE(mPosList); + mPosList = reinterpret_cast(MBP_ALLOC((newMaxNbBoxes+1)*sizeof(PxU32))); + } + + boxIndex = mNbDynamicBoxes++; + mDynamicBoxes[boxIndex] = bounds; + mInToOut_Dynamic[boxIndex] = handle; + } + + mObjects[handle].mIndex = boxIndex; + mObjects[handle].mMBPHandle = mbpHandle; +#if PX_DEBUG + mObjects[handle].mUpdated = !isStatic; +#endif + + if(!isStatic) + { + MTF(mDynamicBoxes, mInToOut_Dynamic, mObjects, bounds, mNbUpdatedBoxes, mObjects[handle]); + mNbUpdatedBoxes++; + mPrevNbUpdatedBoxes = 0; + mNeedsSortingSleeping = true; + PX_ASSERT(mNbUpdatedBoxes<=mNbDynamicBoxes); + } + return handle; +} + +// Moves box 'lastIndex' to location 'removedBoxIndex' +static PX_FORCE_INLINE void remove(MBPEntry* PX_RESTRICT objects, MBP_Index* PX_RESTRICT mapping, MBP_AABB* PX_RESTRICT boxes, PxU32 removedBoxIndex, PxU32 lastIndex) +{ + const PxU32 movedBoxHandle = mapping[lastIndex]; + boxes[removedBoxIndex] = boxes[lastIndex]; // Relocate box data + mapping[removedBoxIndex] = MBP_Index(movedBoxHandle); // Relocate mapping data + MBPEntry& movedObject = objects[movedBoxHandle]; + PX_ASSERT(movedObject.mIndex==lastIndex); // Checks index of moved box was indeed its old location + movedObject.mIndex = removedBoxIndex; // Adjust index of moved box to reflect its new location +} + +void Region::removeObject(MBP_Index handle) +{ + PX_ASSERT(handle>1)|(bits2>>2)|(bits3>>3); + return !mask; + + /* const PxU32 d0 = (b.mMaxY<<16)|a.mMaxY; + const PxU32 d0b = (b.mMaxZ<<16)|a.mMaxZ; + const PxU32 d1 = (a.mMinY<<16)|b.mMinY; + const PxU32 d1b = (a.mMinZ<<16)|b.mMinZ; + const PxU32 mask = (d0 - d1) | (d0b - d1b); + return !(mask & 0x80008000);*/ +#else + if(//mMaxX < a.mMinX || a.mMaxX < mMinX +// || + b.mMaxY < a.mMinY || a.mMaxY < b.mMinY + || + b.mMaxZ < a.mMinZ || a.mMaxZ < b.mMinZ + ) + return FALSE; + return TRUE; +#endif +} +#endif + +#ifdef MBP_USE_NO_CMP_OVERLAP_3D +static PX_FORCE_INLINE bool intersect3D(const MBP_AABB& a, const MBP_AABB& b) +{ + // PT: warning, only valid with the special encoding in InitFrom2 + const PxU32 bits0 = (b.mMaxY - a.mMinY)&0x80000000; + const PxU32 bits1 = (b.mMaxZ - a.mMinZ)&0x80000000; + const PxU32 bits2 = (a.mMaxY - b.mMinY)&0x80000000; + const PxU32 bits3 = (a.mMaxZ - b.mMinZ)&0x80000000; + const PxU32 bits4 = (b.mMaxX - a.mMinX)&0x80000000; + const PxU32 bits5 = (a.mMaxX - b.mMinX)&0x80000000; + const PxU32 mask = bits0|(bits1>>1)|(bits2>>2)|(bits3>>3)|(bits4>>4)|(bits5>>5); + return !mask; +} +#endif + +#ifdef CHECK_NB_OVERLAPS +static PxU32 gNbOverlaps = 0; +#endif + +static PX_FORCE_INLINE void outputPair( MBP_PairManager& pairManager, + PxU32 index0, PxU32 index1, + const MBP_Index* PX_RESTRICT inToOut0, const MBP_Index* PX_RESTRICT inToOut1, + const MBPEntry* PX_RESTRICT objects) +{ +#ifdef CHECK_NB_OVERLAPS + gNbOverlaps++; +#endif + const MBP_Index objectIndex0 = inToOut0[index0]; + const MBP_Index objectIndex1 = inToOut1[index1]; + PX_ASSERT(objectIndex0!=objectIndex1); + const MBP_Handle id0 = objects[objectIndex0].mMBPHandle; + const MBP_Handle id1 = objects[objectIndex1].mMBPHandle; +// printf("2: %d %d\n", index0, index1); +// printf("3: %d %d\n", objectIndex0, objectIndex1); + pairManager.addPair(id0, id1); +} + +MBPOS_TmpBuffers::MBPOS_TmpBuffers() : + mNbSleeping (0), + mNbUpdated (0), + mInToOut_Dynamic_Sleeping (NULL), + mSleepingDynamicBoxes (NULL), + mUpdatedDynamicBoxes (NULL) +{ +} + +MBPOS_TmpBuffers::~MBPOS_TmpBuffers() +{ +// printf("mNbSleeping: %d\n", mNbSleeping); + if(mInToOut_Dynamic_Sleeping!=mInToOut_Dynamic_Sleeping_Stack) + MBP_FREE(mInToOut_Dynamic_Sleeping); + + if(mSleepingDynamicBoxes!=mSleepingDynamicBoxes_Stack) + DELETEARRAY(mSleepingDynamicBoxes); + + if(mUpdatedDynamicBoxes!=mUpdatedDynamicBoxes_Stack) + DELETEARRAY(mUpdatedDynamicBoxes); + + mNbSleeping = 0; + mNbUpdated = 0; +} + +void MBPOS_TmpBuffers::allocateSleeping(PxU32 nbSleeping, PxU32 nbSentinels) +{ + if(nbSleeping>mNbSleeping) + { + if(mInToOut_Dynamic_Sleeping!=mInToOut_Dynamic_Sleeping_Stack) + MBP_FREE(mInToOut_Dynamic_Sleeping); + if(mSleepingDynamicBoxes!=mSleepingDynamicBoxes_Stack) + DELETEARRAY(mSleepingDynamicBoxes); + + if(nbSleeping+nbSentinels<=STACK_BUFFER_SIZE) + { + mSleepingDynamicBoxes = mSleepingDynamicBoxes_Stack; + mInToOut_Dynamic_Sleeping = mInToOut_Dynamic_Sleeping_Stack; + } + else + { + mSleepingDynamicBoxes = PX_NEW_TEMP(MBP_AABB)[nbSleeping+nbSentinels]; + mInToOut_Dynamic_Sleeping = reinterpret_cast(MBP_ALLOC(sizeof(MBP_Index)*nbSleeping)); + } + mNbSleeping = nbSleeping; + } +} + +void MBPOS_TmpBuffers::allocateUpdated(PxU32 nbUpdated, PxU32 nbSentinels) +{ + if(nbUpdated>mNbUpdated) + { + if(mUpdatedDynamicBoxes!=mUpdatedDynamicBoxes_Stack) + DELETEARRAY(mUpdatedDynamicBoxes); + + if(nbUpdated+nbSentinels<=STACK_BUFFER_SIZE) + mUpdatedDynamicBoxes = mUpdatedDynamicBoxes_Stack; + else + mUpdatedDynamicBoxes = PX_NEW_TEMP(MBP_AABB)[nbUpdated+nbSentinels]; + + mNbUpdated = nbUpdated; + } +} + +void Region::preparePruning(MBPOS_TmpBuffers& buffers) +{ +PxU32 _saved = mNbUpdatedBoxes; +mNbUpdatedBoxes = 0; + + if(mPrevNbUpdatedBoxes!=_saved) + mNeedsSortingSleeping = true; + + PxU32 nb = mNbDynamicBoxes; + if(!nb) + { + mInput.mNeeded = false; + mPrevNbUpdatedBoxes = 0; + mNeedsSortingSleeping = true; + return; + } + const MBP_AABB* PX_RESTRICT dynamicBoxes = mDynamicBoxes; + PxU32* PX_RESTRICT posList = mPosList; + +#if PX_DEBUG + PxU32 verifyNbUpdated = 0; + for(PxU32 i=0;i(mRS.GetRecyclable()); + for(PxU32 i=0;i buffers.mUpdatedDynamicBoxes; + mInput.mSleepingDynamicBoxes = sleepingDynamicBoxes; + mInput.mInToOut_Dynamic = inToOut_Dynamic; // Can be shared (3) => (MBP_Index*)mRS.GetRecyclable(); + mInput.mInToOut_Dynamic_Sleeping = inToOut_Dynamic_Sleeping; + mInput.mNbUpdated = nbUpdated; // Can be shared (4) + mInput.mNbNonUpdated = nbNonUpdated; + mInput.mNeeded = true; +} + +void Region::prepareBIPPruning(const MBPOS_TmpBuffers& buffers) +{ + if(!mNbUpdatedBoxes || !mNbStaticBoxes) + { + mInput.mBIPInput.mNeeded = false; + return; + } + + mInput.mBIPInput.mObjects = mObjects; // Can be shared (1) + mInput.mBIPInput.mNbUpdatedBoxes = mNbUpdatedBoxes; // Can be shared (4) + mInput.mBIPInput.mNbStaticBoxes = mNbStaticBoxes; +// mInput.mBIPInput.mDynamicBoxes = mDynamicBoxes; + mInput.mBIPInput.mDynamicBoxes = buffers.mUpdatedDynamicBoxes; // Can be shared (2) + mInput.mBIPInput.mStaticBoxes = mStaticBoxes; + mInput.mBIPInput.mInToOut_Static = mInToOut_Static; + mInput.mBIPInput.mInToOut_Dynamic = reinterpret_cast(mRS.GetRecyclable()); // Can be shared (3) + mInput.mBIPInput.mNeeded = true; +} + +static void doCompleteBoxPruning(MBP_PairManager* PX_RESTRICT pairManager, const BoxPruning_Input& input) +{ + const MBPEntry* PX_RESTRICT objects = input.mObjects; + const MBP_AABB* PX_RESTRICT updatedDynamicBoxes = input.mUpdatedDynamicBoxes; + const MBP_AABB* PX_RESTRICT sleepingDynamicBoxes = input.mSleepingDynamicBoxes; + const MBP_Index* PX_RESTRICT inToOut_Dynamic = input.mInToOut_Dynamic; + const MBP_Index* PX_RESTRICT inToOut_Dynamic_Sleeping = input.mInToOut_Dynamic_Sleeping; + const PxU32 nbUpdated = input.mNbUpdated; + const PxU32 nbNonUpdated = input.mNbNonUpdated; + + // + + // PT: find sleeping-dynamics-vs-active-dynamics overlaps + if(nbNonUpdated) + { + const PxU32 nb0 = nbUpdated; + const PxU32 nb1 = nbNonUpdated; + + // + PxU32 index0 = 0; + PxU32 runningIndex1 = 0; + + while(runningIndex1 +#endif + +// PT: TODO: +// - We could try to keep bounds around all objects (for each region), and then test the new region's bounds against these instead of +// testing all objects one by one. These new bounds (around all objects of a given region) would be delicate to maintain though. +// - Just store these "fully inside flags" (i.e. objects) in a separate list? Or can we do MTF on objects? (probably not, else we +// wouldn't have holes in the array due to removed objects) + +// PT: automatically populate new region with overlapping objects. +// Brute-force version checking all existing objects, potentially optimized using "fully inside" flags. +//#define FIRST_VERSION +#ifdef FIRST_VERSION +void MBP::populateNewRegion(const MBP_AABB& box, Region* addedRegion, PxU32 regionIndex) +{ + const RegionData* PX_RESTRICT regions = mRegions.begin(); + const PxU32 nbObjects = mMBP_Objects.size(); + MBP_Object* PX_RESTRICT objects = mMBP_Objects.begin(); + +#ifdef PRINT_STATS + PxU32 nbObjectsFound = 0; + PxU32 nbObjectsTested = 0; +#endif + +#ifdef USE_FULLY_INSIDE_FLAG + const PxU32* fullyInsideFlags = mFullyInsideBitmap.getBits(); +#endif + + PxU32 j=0; + while(j>5]; + #ifdef HWSCAN + if(blockFlags==0) //HWSCAN + #else + if(blockFlags==0xffffffff) + #endif + { + j+=32; + continue; + } + PxU32 nbToGo = PxMin(nbObjects - j, PxU32(32)); + PxU32 mask = 1; + while(nbToGo--) + { + + MBP_Object& currentObject = objects[j]; + // PT: if an object A is fully contained inside all the regions S it overlaps, we don't need to test it against the new region R. + // The rationale is that even if R does overlap A, any new object B must touch S to overlap with A. So B would be added to S and + // the (A,B) overlap would be detected in S, even if it's not detected in R. + const PxU32 res = blockFlags & mask; + PX_ASSERT((mFullyInsideBitmap.isSet(j) && res) || (!mFullyInsideBitmap.isSet(j) && !res)); + mask+=mask; + j++; + #ifdef HWSCAN + if(!res) //HWSCAN + #else + if(res) + #endif + continue; + PX_ASSERT(!(currentObject.mFlags & MBP_REMOVED)); +#else + MBP_Object& currentObject = objects[j++]; + if(currentObject.mFlags & MBP_REMOVED) + continue; // PT: object is in the free list +#endif + +#ifdef PRINT_STATS + nbObjectsTested++; +#endif + + MBP_AABB bounds; + MBP_Handle mbpHandle; + + const PxU32 nbHandles = currentObject.mNbHandles; + if(nbHandles) + { + RegionHandle* PX_RESTRICT handles = getHandles(currentObject, nbHandles); + // PT: no need to test all regions since they should contain the same info. Just retrieve bounds from the first one. + PxU32 i=0; // for(PxU32 i=0;iretrieveBounds(bounds, h.mHandle); + } + } + else + { + PX_ASSERT(mManager); + + // PT: if the object is out-of-bounds, we're out-of-luck. We don't have the object bounds, so we need to retrieve them + // from the AABB manager - and then re-encode them. This is not very elegant or efficient, but it should rarely happen + // so this is good enough for now. + const PxBounds3 decodedBounds = mManager->getBPBounds(currentObject.mUserID); + + bounds.initFrom2(decodedBounds); + + mbpHandle = currentObject.mHandlesIndex; + } + + if(bounds.intersect(box)) + { +// updateObject(mbpHandle, bounds); + updateObjectAfterNewRegionAdded(mbpHandle, bounds, addedRegion, regionIndex); +#ifdef PRINT_STATS + nbObjectsFound++; +#endif + } + +#ifdef USE_FULLY_INSIDE_FLAG + } +#endif + } +#ifdef PRINT_STATS + printf("Populating new region with %d objects (tested %d/%d object)\n", nbObjectsFound, nbObjectsTested, nbObjects); +#endif +} +#endif + +// PT: version using lowestSetBit +#define SECOND_VERSION +#ifdef SECOND_VERSION + +/* PX_FORCE_INLINE PxU32 lowestSetBitUnsafe64(PxU64 v) + { + unsigned long retval; + _BitScanForward64(&retval, v); + return retval; + }*/ + +void MBP::populateNewRegion(const MBP_AABB& box, Region* addedRegion, PxU32 regionIndex) +{ + const RegionData* PX_RESTRICT regions = mRegions.begin(); + const PxU32 nbObjects = mMBP_Objects.size(); + PX_UNUSED(nbObjects); + MBP_Object* PX_RESTRICT objects = mMBP_Objects.begin(); + const PxU32* fullyInsideFlags = mFullyInsideBitmap.getBits(); +// const PxU64* fullyInsideFlags = (const PxU64*)mFullyInsideBitmap.getBits(); + if(!fullyInsideFlags) + return; + + const PxU32 lastSetBit = mFullyInsideBitmap.findLast(); +// const PxU64 lastSetBit = mFullyInsideBitmap.findLast(); + +#ifdef PRINT_STATS + PxU32 nbObjectsFound = 0; + PxU32 nbObjectsTested = 0; +#endif + + for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) +// for(PxU64 w = 0; w <= lastSetBit >> 6; ++w) + { + for(PxU32 b = fullyInsideFlags[w]; b; b &= b-1) +// for(PxU64 b = fullyInsideFlags[w]; b; b &= b-1) + { + const PxU32 index = PxU32(w<<5|Ps::lowestSetBit(b)); +// const PxU64 index = (PxU64)(w<<6|::lowestSetBitUnsafe64(b)); + PX_ASSERT(indexretrieveBounds(bounds, h.mHandle); + } + } + else + { + // PT: if the object is out-of-bounds, we're out-of-luck. We don't have the object bounds, so we need to retrieve them + // from the AABB manager - and then re-encode them. This is not very elegant or efficient, but it should rarely happen + // so this is good enough for now. + const PxBounds3 rawBounds = mTransientBounds[currentObject.mUserID]; + PxVec3 c(mTransientContactDistance[currentObject.mUserID]); + const PxBounds3 decodedBounds(rawBounds.minimum - c, rawBounds.maximum + c); + bounds.initFrom2(decodedBounds); + + mbpHandle = currentObject.mHandlesIndex; + } + + if(bounds.intersects(box)) + { +// updateObject(mbpHandle, bounds); + updateObjectAfterNewRegionAdded(mbpHandle, bounds, addedRegion, regionIndex); +#ifdef PRINT_STATS + nbObjectsFound++; +#endif + } + } + } +#ifdef PRINT_STATS + printf("Populating new region with %d objects (tested %d/%d object)\n", nbObjectsFound, nbObjectsTested, nbObjects); +#endif +} +#endif + +//#define THIRD_VERSION +#ifdef THIRD_VERSION +void MBP::populateNewRegion(const MBP_AABB& box, Region* addedRegion, PxU32 regionIndex) +{ + const RegionData* PX_RESTRICT regions = mRegions.begin(); + const PxU32 nbObjects = mMBP_Objects.size(); + PX_UNUSED(nbObjects); + MBP_Object* PX_RESTRICT objects = mMBP_Objects.begin(); + + const PxU32* fullyInsideFlags = mFullyInsideBitmap.getBits(); + if(!fullyInsideFlags) + return; + +#ifdef PRINT_STATS + PxU32 nbObjectsFound = 0; + PxU32 nbObjectsTested = 0; +#endif + + Cm::BitMap bm; + bm.importData(mFullyInsideBitmap.getSize(), (PxU32*)fullyInsideFlags); + + Cm::BitMap::Iterator it(bm); + PxU32 index = it.getNext(); + while(index != Cm::BitMap::Iterator::DONE) + { + PX_ASSERT(indexretrieveBounds(bounds, h.mHandle); + } + } + else + { + PX_ASSERT(mManager); + + // PT: if the object is out-of-bounds, we're out-of-luck. We don't have the object bounds, so we need to retrieve them + // from the AABB manager - and then re-encode them. This is not very elegant or efficient, but it should rarely happen + // so this is good enough for now. + const PxBounds3 decodedBounds = mManager->getBPBounds(currentObject.mUserID); + + bounds.initFrom2(decodedBounds); + + mbpHandle = currentObject.mHandlesIndex; + } + + if(bounds.intersect(box)) + { +// updateObject(mbpHandle, bounds); + updateObjectAfterNewRegionAdded(mbpHandle, bounds, addedRegion, regionIndex); +#ifdef PRINT_STATS + nbObjectsFound++; +#endif + } + index = it.getNext(); + } +#ifdef PRINT_STATS + printf("Populating new region with %d objects (tested %d/%d object)\n", nbObjectsFound, nbObjectsTested, nbObjects); +#endif +} +#endif + +PxU32 MBP::addRegion(const PxBroadPhaseRegion& region, bool populateRegion) +{ + PxU32 regionHandle; + RegionData* PX_RESTRICT buffer; + + if(mFirstFreeIndexBP!=INVALID_ID) + { + regionHandle = mFirstFreeIndexBP; + + buffer = mRegions.begin(); + buffer += regionHandle; + + mFirstFreeIndexBP = PxU32(size_t(buffer->mUserData)); // PT: this is safe, we previously stored a PxU32 in there + } + else + { + if(mNbRegions>=MAX_NB_MBP) + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "MBP::addRegion: max number of regions reached."); + return INVALID_ID; + } + + regionHandle = mNbRegions++; + buffer = reserveContainerMemory(mRegions, 1); + } + + Region* newRegion = PX_NEW(Region); + buffer->mBox.initFrom2(region.bounds); + buffer->mBP = newRegion; + buffer->mUserData = region.userData; + + setupOverlapFlags(mNbRegions, mRegions.begin()); + + // PT: automatically populate new region with overlapping objects + if(populateRegion) + populateNewRegion(buffer->mBox, newRegion, regionHandle); + +#ifdef MBP_REGION_BOX_PRUNING + mDirtyRegions = true; +#endif + + return regionHandle; +} + +// ### TODO: recycle regions, make sure objects are properly deleted/transferred, etc +// ### TODO: what happens if we delete a zone then immediately add it back? Do objects get deleted? +// ### TODO: in fact if we remove a zone but we keep the objects, what happens to their current overlaps? Are they kept or discarded? +bool MBP::removeRegion(PxU32 handle) +{ + if(handle>=mNbRegions) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "MBP::removeRegion: invalid handle."); + return false; + } + + RegionData* PX_RESTRICT region = mRegions.begin(); + region += handle; + + Region* bp = region->mBP; + if(!bp) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "MBP::removeRegion: invalid handle."); + return false; + } + + PxBounds3 empty; + empty.setEmpty(); + region->mBox.initFrom2(empty); + + { + // We are going to remove the region but it can still contain objects. We need to update + // those objects so that their handles and out-of-bounds status are modified. + // + // Unfortunately there is no way to iterate over active objects in a region, so we need + // to iterate over the max amount of objects. ### TODO: optimize this + const PxU32 maxNbObjects = bp->mMaxNbObjects; + MBPEntry* PX_RESTRICT objects = bp->mObjects; + for(PxU32 j=0;jmBP = NULL; + region->mUserData = reinterpret_cast(size_t(mFirstFreeIndexBP)); + mFirstFreeIndexBP = handle; + +#ifdef MBP_REGION_BOX_PRUNING + mDirtyRegions = true; +#endif + + // A region has been removed so we need to update the overlap flags for all remaining regions + // ### TODO: optimize this + setupOverlapFlags(mNbRegions, mRegions.begin()); + return true; +} + +const Region* MBP::getRegion(PxU32 i) const +{ + if(i>=mNbRegions) + return NULL; + + const RegionData* PX_RESTRICT regions = mRegions.begin(); + return regions[i].mBP; +} + +#ifdef MBP_REGION_BOX_PRUNING +void MBP::buildRegionData() +{ + const PxU32 size = mNbRegions; + PxU32 nbValidRegions = 0; + if(size) + { + const RegionData* PX_RESTRICT regions = mRegions.begin(); + + // Gather valid regions + PxU32 minPosList[MAX_NB_MBP]; + for(PxU32 i=0;i& c = mHandles[nbHandles]; + handles = reinterpret_cast(c.begin()+handlesIndex); + } + return handles; +} + +void MBP::purgeHandles(MBP_Object* PX_RESTRICT object, PxU32 nbHandles) +{ + if(nbHandles>1) + { + const PxU32 handlesIndex = object->mHandlesIndex; + Ps::Array& c = mHandles[nbHandles]; + PxU32* recycled = c.begin() + handlesIndex; + *recycled = mFirstFree[nbHandles]; + mFirstFree[nbHandles] = handlesIndex; + } +} + +void MBP::storeHandles(MBP_Object* PX_RESTRICT object, PxU32 nbHandles, const RegionHandle* PX_RESTRICT handles) +{ + if(nbHandles==1) + { + object->mHandle = handles[0]; + } + else if(nbHandles) + { + Ps::Array& c = mHandles[nbHandles]; + const PxU32 firstFree = mFirstFree[nbHandles]; + PxU32* handlesMemory; + if(firstFree!=INVALID_ID) + { + object->mHandlesIndex = firstFree; + handlesMemory = c.begin() + firstFree; + mFirstFree[nbHandles] = *handlesMemory; + } + else + { + const PxU32 handlesIndex = c.size(); + object->mHandlesIndex = handlesIndex; + handlesMemory = reserveContainerMemory(c, sizeof(RegionHandle)*nbHandles/sizeof(PxU32)); + } + PxMemCopy(handlesMemory, handles, sizeof(RegionHandle)*nbHandles); + } +} + +MBP_Handle MBP::addObject(const MBP_AABB& box, BpHandle userID, bool isStatic) +{ + MBP_ObjectIndex objectIndex; + MBP_Object* objectMemory; + PxU32 flipFlop; + if(1) + { + if(mFirstFreeIndex!=INVALID_ID) + { + objectIndex = mFirstFreeIndex; + MBP_Object* objects = mMBP_Objects.begin(); + objectMemory = &objects[objectIndex]; + PX_ASSERT(!objectMemory->mNbHandles); + mFirstFreeIndex = objectMemory->mHandlesIndex; + flipFlop = PxU32(objectMemory->getFlipFlop()); + } + else + { + objectIndex = mMBP_Objects.size(); + objectMemory = reserveContainerMemory(mMBP_Objects, 1); + flipFlop = 0; + } + } + else + { + // PT: must be possible to use the AABB-manager's ID directly. Something like this: + objectIndex = userID; + if(mMBP_Objects.capacity()mNbObjects==0xffff) + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "MBP::addObject: 64K objects in single region reached. Some collisions might be lost."); + else +#endif + { + RegionHandle& h = tmpHandles[nbHandles++]; + h.mHandle = regions[i].mBP->addObject(box, MBPObjectHandle, isStatic); + h.mInternalBPHandle = Ps::to16(i); + } + } + } + storeHandles(objectMemory, nbHandles, tmpHandles); + + objectMemory->mNbHandles = Ps::to16(nbHandles); + PxU16 flags = 0; + if(flipFlop) + flags |= MBP_FLIP_FLOP; +#ifdef USE_FULLY_INSIDE_FLAG + if(nbHandles && newObjectIsFullyInsideRegions) + setBit(mFullyInsideBitmap, objectIndex); + else + clearBit(mFullyInsideBitmap, objectIndex); +#endif + if(!nbHandles) + { + objectMemory->mHandlesIndex = MBPObjectHandle; + addToOutOfBoundsArray(userID); + } + + if(!isStatic) + mUpdatedObjects.setBitChecked(objectIndex); + +// objectMemory->mUpdated = !isStatic; + objectMemory->mFlags = flags; + objectMemory->mUserID = userID; + + return MBPObjectHandle; +} + +bool MBP::removeObject(MBP_Handle handle) +{ + const MBP_ObjectIndex objectIndex = decodeHandle_Index(handle); + + MBP_Object* PX_RESTRICT objects = mMBP_Objects.begin(); + MBP_Object& currentObject = objects[objectIndex]; + const RegionData* PX_RESTRICT regions = mRegions.begin(); + // Parse previously overlapping regions. If still overlapping, update object. Else remove from region. + const PxU32 nbHandles = currentObject.mNbHandles; + if(nbHandles) + { + RegionHandle* handles = getHandles(currentObject, nbHandles); + for(PxU32 i=0;iremoveObject(h.mHandle); + } + + purgeHandles(¤tObject, nbHandles); + } + + currentObject.mNbHandles = 0; + currentObject.mFlags |= MBP_REMOVED; + currentObject.mHandlesIndex = mFirstFreeIndex; +// if(!decodeHandle_IsStatic(handle)) +// if(!currentObject.IsStatic()) + mUpdatedObjects.setBitChecked(objectIndex); + + mFirstFreeIndex = objectIndex; + + mRemoved.setBitChecked(objectIndex); // PT: this is cleared each frame so it's not a replacement for the MBP_REMOVED flag + +#ifdef USE_FULLY_INSIDE_FLAG + // PT: when removing an object we mark it as "fully inside" so that it is automatically + // discarded in the "populateNewRegion" function, without the need for MBP_REMOVED. + setBit(mFullyInsideBitmap, objectIndex); +#endif + return true; +} + +static PX_FORCE_INLINE bool stillIntersects(PxU32 handle, PxU32& _nb, PxU32* PX_RESTRICT currentOverlaps) +{ + const PxU32 nb = _nb; + for(PxU32 i=0;i nbHandles changes from 2 to 1 while MBP_FULLY_INSIDE is not set + setBit(mFullyInsideBitmap, objectIndex); +#endif + currentRegion.mBP->updateObject(box, h.mHandle); + return true; + } + } + + // Find regions overlapping object's new position +#ifdef USE_FULLY_INSIDE_FLAG + bool objectIsFullyInsideRegions = true; +#endif + PxU32 nbCurrentOverlaps = 0; + PxU32 currentOverlaps[MAX_NB_MBP+1]; + // PT: here, we may still parse regions which have been removed. But their boxes have been set to empty, + // so nothing will happen. + for(PxU32 i=0;iUpdateObject(box, h.mHandle); + if(stillIntersects(h.mInternalBPHandle, nbCurrentOverlaps, currentOverlaps)) + { + currentRegion.mBP->updateObject(box, h.mHandle); + // Still collides => keep handle for this frame + newHandles[nbNewHandles++] = h; + } + else + { + PX_ASSERT(!currentRegion.mBox.intersects(box)); +// if(currentRegion.mBP) + PX_ASSERT(currentRegion.mBP); + currentRegion.mBP->removeObject(h.mHandle); + } + } + + // Add to new regions if needed + for(PxU32 i=0;iaddObject(box, handle, isStatic!=0); + newHandles[nbNewHandles].mHandle = Ps::to16(BPHandle); + newHandles[nbNewHandles].mInternalBPHandle = Ps::to16(regionIndex); + nbNewHandles++; + } + + if(nbHandles==nbNewHandles) + { + for(PxU32 i=0;iaddObject(box, handle, isStatic!=0); + newHandles[nbNewHandles].mHandle = Ps::to16(BPHandle); + newHandles[nbNewHandles].mInternalBPHandle = Ps::to16(regionIndex); + nbNewHandles++; + } + + // PT: we know that we have one more handle than before, no need to test + purgeHandles(¤tObject, nbHandles); + storeHandles(¤tObject, nbNewHandles, newHandles); + + currentObject.mNbHandles = Ps::to16(nbNewHandles); + + // PT: we know that we have at least one handle (from the newly added region), so we can't be "out of bounds" here. + PX_ASSERT(nbNewHandles); + +#ifdef USE_FULLY_INSIDE_FLAG + // PT: we know that the object was not "fully inside" before, so even if it is fully inside the new region, it + // will not be fully inside all of them => no need to change its fully inside flag + // TODO: an exception to this would be the case where the object was out-of-bounds, and it's now fully inside the new region + // => we could set the flag in that case. +#endif + return true; +} + +bool MBP_PairManager::computeCreatedDeletedPairs(const MBP_Object* objects, BroadPhaseMBP* mbp, const BitArray& updated, const BitArray& removed) +{ + // PT: parse all currently active pairs. The goal here is to generate the found/lost pairs, compared to previous frame. + PxU32 i=0; + PxU32 nbActivePairs = mNbActivePairs; + while(imCreated.pushBack(BroadPhasePair(object0, object1)); + + p.clearNew(); + p.clearUpdated(); + i++; + } + else if(p.isUpdated()) + { + // Persistent pair + + // PT: this pair already existed in the structure, and has been found again this frame. Since + // MBP reports "all pairs" each frame (as opposed to SAP), this happens quite often, for each + // active persistent pair. + p.clearUpdated(); + i++; + } + else + { + // Lost pair + + // PT: if the pair is not new and not 'updated', it might be a lost (separated) pair. But this + // is not always the case since we now handle "sleeping" objects directly within MBP. A pair + // of sleeping objects does not generate an 'addPair' call, so it ends up in this codepath. + // Nonetheless the sleeping pair should not be deleted. We can only delete pairs involving + // objects that have been actually moved during the frame. This is the only case in which + // a pair can indeed become 'lost'. + const PxU32 id0 = p.getId0(); + const PxU32 id1 = p.getId1(); + PX_ASSERT(id0!=INVALID_ID); + PX_ASSERT(id1!=INVALID_ID); + + const MBP_ObjectIndex index0 = decodeHandle_Index(id0); + const MBP_ObjectIndex index1 = decodeHandle_Index(id1); + // PT: if none of the involved objects have been updated, the pair is just sleeping: keep it and skip it. + if(updated.isSetChecked(index0) || updated.isSetChecked(index1)) + { + // PT: by design (for better or worse) we do not report pairs to the client when + // one of the involved objects has been deleted. The pair must still be deleted + // from the MBP structure though. + if(!removed.isSetChecked(index0) && !removed.isSetChecked(index1)) + { + // PT: doing the group-based filtering here is useless. The pair should not have + // been added in the first place. + const BpHandle object0 = objects[index0].mUserID; + const BpHandle object1 = objects[index1].mUserID; + mbp->mDeleted.pushBack(BroadPhasePair(object0, object1)); + } + + const PxU32 hashValue = hash(id0, id1) & mMask; + PairManagerData::removePair(id0, id1, hashValue, i); + nbActivePairs--; + } + else i++; + } + } + + shrinkMemory(); + return true; +} + +void MBP::prepareOverlaps() +{ + const PxU32 nb = mNbRegions; + const RegionData* PX_RESTRICT regions = mRegions.begin(); + for(PxU32 i=0;iprepareOverlaps(); + } +} + +void MBP::findOverlaps(const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) +{ + PxU32 nb = mNbRegions; + const RegionData* PX_RESTRICT regions = mRegions.begin(); + const MBP_Object* objects = mMBP_Objects.begin(); + + mPairManager.mObjects = objects; + mPairManager.mGroups = groups; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mPairManager.mLUT = lut; +#endif + + for(PxU32 i=0;ifindOverlaps(mPairManager); + } +} + +PxU32 MBP::finalize(BroadPhaseMBP* mbp) +{ + const MBP_Object* objects = mMBP_Objects.begin(); + mPairManager.computeCreatedDeletedPairs(objects, mbp, mUpdatedObjects, mRemoved); + + mUpdatedObjects.clearAll(); + + return mPairManager.mNbActivePairs; +} + +void MBP::reset() +{ + PxU32 nb = mNbRegions; + RegionData* PX_RESTRICT regions = mRegions.begin(); + while(nb--) + { +// printf("%d objects in region\n", regions->mBP->mNbObjects); + DELETESINGLE(regions->mBP); + regions++; + } + + mNbRegions = 0; + mFirstFreeIndex = INVALID_ID; + mFirstFreeIndexBP = INVALID_ID; + for(PxU32 i=0;isetBounds(h.mHandle, bounds); + } + } + } +} + +void MBP::setTransientBounds(const PxBounds3* bounds, const PxReal* contactDistance) +{ + mTransientBounds = bounds; + mTransientContactDistance = contactDistance; +} +/////////////////////////////////////////////////////////////////////////////// + +// Below is the PhysX wrapper = link between AABBManager and MBP + +#define DEFAULT_CREATED_DELETED_PAIRS_CAPACITY 1024 + +BroadPhaseMBP::BroadPhaseMBP( PxU32 maxNbRegions, + PxU32 maxNbBroadPhaseOverlaps, + PxU32 maxNbStaticShapes, + PxU32 maxNbDynamicShapes, + PxU64 contextID) : + mMBPUpdateWorkTask (contextID), + mMBPPostUpdateWorkTask (contextID), + mMapping (NULL), + mCapacity (0), + mGroups (NULL) +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + ,mLUT (NULL) +#endif +{ + mMBP = PX_NEW(MBP)(); + + const PxU32 nbObjects = maxNbStaticShapes + maxNbDynamicShapes; + mMBP->preallocate(maxNbRegions, nbObjects, maxNbBroadPhaseOverlaps); + + if(nbObjects) + allocateMappingArray(nbObjects); + + mCreated.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); + mDeleted.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); +} + +BroadPhaseMBP::~BroadPhaseMBP() +{ + DELETESINGLE(mMBP); + PX_FREE(mMapping); +} + +void BroadPhaseMBP::allocateMappingArray(PxU32 newCapacity) +{ + PX_ASSERT(newCapacity>mCapacity); + MBP_Handle* newMapping = reinterpret_cast(PX_ALLOC(sizeof(MBP_Handle)*newCapacity, "MBP")); + if(mCapacity) + PxMemCopy(newMapping, mMapping, mCapacity*sizeof(MBP_Handle)); + for(PxU32 i=mCapacity;imNbRegions; +/* const RegionData* PX_RESTRICT regions = (const RegionData*)mMBP->mRegions.GetEntries(); + PxU32 nbActiveRegions = 0; + for(PxU32 i=0;imNbRegions; + const RegionData* PX_RESTRICT regions = mMBP->mRegions.begin(); + regions += startIndex; + + const PxU32 writeCount = PxMin(size, bufferSize); + for(PxU32 i=0;imNbStaticBoxes; + userBuffer[i].nbDynamicObjects = regions[i].mBP->mNbDynamicBoxes; + } + else + { + userBuffer[i].region.bounds.setEmpty(); + userBuffer[i].region.userData = NULL; + userBuffer[i].active = false; + userBuffer[i].overlap = false; + userBuffer[i].nbStaticObjects = 0; + userBuffer[i].nbDynamicObjects = 0; + } + } + return writeCount; +} + +PxU32 BroadPhaseMBP::addRegion(const PxBroadPhaseRegion& region, bool populateRegion) +{ + return mMBP->addRegion(region, populateRegion); +} + +bool BroadPhaseMBP::removeRegion(PxU32 handle) +{ + return mMBP->removeRegion(handle); +} + +void BroadPhaseMBP::update(const PxU32 numCpuTasks, PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData, physx::PxBaseTask* continuation, physx::PxBaseTask* narrowPhaseUnblockTask) +{ +#if PX_CHECKED + PX_CHECK_AND_RETURN(scratchAllocator, "BroadPhaseMBP::update - scratchAllocator must be non-NULL \n"); +#endif + + // PT: TODO: move this out of update function + if(narrowPhaseUnblockTask) + narrowPhaseUnblockTask->removeReference(); + + setUpdateData(updateData); + + if(1) + { + update(); + postUpdate(); + } + else + { + mMBPPostUpdateWorkTask.set(this, scratchAllocator, numCpuTasks); + mMBPUpdateWorkTask.set(this, scratchAllocator, numCpuTasks); + + mMBPPostUpdateWorkTask.setContinuation(continuation); + mMBPUpdateWorkTask.setContinuation(&mMBPPostUpdateWorkTask); + + mMBPPostUpdateWorkTask.removeReference(); + mMBPUpdateWorkTask.removeReference(); + } +} + +void BroadPhaseMBP::singleThreadedUpdate(PxcScratchAllocator* /*scratchAllocator*/, const BroadPhaseUpdateData& updateData) +{ + // PT: TODO: the scratchAllocator isn't actually needed, is it? + setUpdateData(updateData); + update(); + postUpdate(); +} + +static PX_FORCE_INLINE void computeMBPBounds(MBP_AABB& aabb, const PxBounds3* PX_RESTRICT boundsXYZ, const PxReal* PX_RESTRICT contactDistances, const BpHandle index) +{ + const PxBounds3& b = boundsXYZ[index]; + const Vec4V contactDistanceV = V4Load(contactDistances[index]); + const Vec4V inflatedMinV = V4Sub(V4LoadU(&b.minimum.x), contactDistanceV); + const Vec4V inflatedMaxV = V4Add(V4LoadU(&b.maximum.x), contactDistanceV); // PT: this one is safe because we allocated one more box in the array (in BoundsArray::initEntry) + + PX_ALIGN(16, PxVec4) boxMin; + PX_ALIGN(16, PxVec4) boxMax; + V4StoreA(inflatedMinV, &boxMin.x); + V4StoreA(inflatedMaxV, &boxMax.x); + + const PxU32* PX_RESTRICT min = PxUnionCast(&boxMin.x); + const PxU32* PX_RESTRICT max = PxUnionCast(&boxMax.x); + //Avoid min=max by enforcing the rule that mins are even and maxs are odd. + aabb.mMinX = IntegerAABB::encodeFloatMin(min[0])>>1; + aabb.mMinY = IntegerAABB::encodeFloatMin(min[1])>>1; + aabb.mMinZ = IntegerAABB::encodeFloatMin(min[2])>>1; + aabb.mMaxX = (IntegerAABB::encodeFloatMax(max[0]) | (1<<2))>>1; + aabb.mMaxY = (IntegerAABB::encodeFloatMax(max[1]) | (1<<2))>>1; + aabb.mMaxZ = (IntegerAABB::encodeFloatMax(max[2]) | (1<<2))>>1; + +/* const IntegerAABB bounds(boundsXYZ[index], contactDistances[index]); + + aabb.mMinX = bounds.mMinMax[IntegerAABB::MIN_X]>>1; + aabb.mMinY = bounds.mMinMax[IntegerAABB::MIN_Y]>>1; + aabb.mMinZ = bounds.mMinMax[IntegerAABB::MIN_Z]>>1; + aabb.mMaxX = bounds.mMinMax[IntegerAABB::MAX_X]>>1; + aabb.mMaxY = bounds.mMinMax[IntegerAABB::MAX_Y]>>1; + aabb.mMaxZ = bounds.mMinMax[IntegerAABB::MAX_Z]>>1;*/ + +/* + aabb.mMinX &= ~1; + aabb.mMinY &= ~1; + aabb.mMinZ &= ~1; + aabb.mMaxX |= 1; + aabb.mMaxY |= 1; + aabb.mMaxZ |= 1; +*/ + +/*#if PX_DEBUG + PxBounds3 decodedBox; + PxU32* bin = reinterpret_cast(&decodedBox.minimum.x); + bin[0] = decodeFloat(bounds.mMinMax[IntegerAABB::MIN_X]); + bin[1] = decodeFloat(bounds.mMinMax[IntegerAABB::MIN_Y]); + bin[2] = decodeFloat(bounds.mMinMax[IntegerAABB::MIN_Z]); + bin[3] = decodeFloat(bounds.mMinMax[IntegerAABB::MAX_X]); + bin[4] = decodeFloat(bounds.mMinMax[IntegerAABB::MAX_Y]); + bin[5] = decodeFloat(bounds.mMinMax[IntegerAABB::MAX_Z]); + + MBP_AABB PrunerBox; + PrunerBox.initFrom2(decodedBox); + PX_ASSERT(PrunerBox.mMinX==aabb.mMinX); + PX_ASSERT(PrunerBox.mMinY==aabb.mMinY); + PX_ASSERT(PrunerBox.mMinZ==aabb.mMinZ); + PX_ASSERT(PrunerBox.mMaxX==aabb.mMaxX); + PX_ASSERT(PrunerBox.mMaxY==aabb.mMaxY); + PX_ASSERT(PrunerBox.mMaxZ==aabb.mMaxZ); +#endif*/ +} + +void MBPUpdateWorkTask::runInternal() +{ + mMBP->update(); +} + +void MBPPostUpdateWorkTask::runInternal() +{ + mMBP->postUpdate(); +} + +void BroadPhaseMBP::removeObjects(const BroadPhaseUpdateData& updateData) +{ + const BpHandle* PX_RESTRICT removed = updateData.getRemovedHandles(); + if(removed) + { + PxU32 nbToGo = updateData.getNumRemovedHandles(); + while(nbToGo--) + { + const BpHandle index = *removed++; + PX_ASSERT(index+1removeObject(mMapping[index]); + PX_ASSERT(status); + PX_UNUSED(status); + + mMapping[index] = PX_INVALID_U32; + } + } +} + +void BroadPhaseMBP::updateObjects(const BroadPhaseUpdateData& updateData) +{ + const BpHandle* PX_RESTRICT updated = updateData.getUpdatedHandles(); + if(updated) + { + const PxBounds3* PX_RESTRICT boundsXYZ = updateData.getAABBs(); + PxU32 nbToGo = updateData.getNumUpdatedHandles(); + while(nbToGo--) + { + const BpHandle index = *updated++; + PX_ASSERT(index+1updateObject(mMapping[index], aabb); + PX_ASSERT(status); + PX_UNUSED(status); + } + } +} + +void BroadPhaseMBP::addObjects(const BroadPhaseUpdateData& updateData) +{ + const BpHandle* PX_RESTRICT created = updateData.getCreatedHandles(); + if(created) + { + const PxBounds3* PX_RESTRICT boundsXYZ = updateData.getAABBs(); + const Bp::FilterGroup::Enum* PX_RESTRICT groups = updateData.getGroups(); + + PxU32 nbToGo = updateData.getNumCreatedHandles(); + while(nbToGo--) + { + const BpHandle index = *created++; + PX_ASSERT(index+1addObject(aabb, index, isStatic); + } + } +} + +void BroadPhaseMBP::setUpdateData(const BroadPhaseUpdateData& updateData) +{ + mMBP->setTransientBounds(updateData.getAABBs(), updateData.getContactDistance()); + + const PxU32 newCapacity = updateData.getCapacity(); + if(newCapacity>mCapacity) + allocateMappingArray(newCapacity); + +#if PX_CHECKED + // PT: WARNING: this must be done after the allocateMappingArray call + if(!BroadPhaseUpdateData::isValid(updateData, *this)) + { + PX_CHECK_MSG(false, "Illegal BroadPhaseUpdateData \n"); + return; + } +#endif + + mGroups = updateData.getGroups(); +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT = updateData.getLUT(); +#endif + + // ### TODO: handle groups inside MBP + // ### TODO: get rid of AABB conversions + + removeObjects(updateData); + addObjects(updateData); + updateObjects(updateData); + + PX_ASSERT(!mCreated.size()); + PX_ASSERT(!mDeleted.size()); + + mMBP->prepareOverlaps(); +} + +void BroadPhaseMBP::update() +{ +#ifdef CHECK_NB_OVERLAPS + gNbOverlaps = 0; +#endif + mMBP->findOverlaps(mGroups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , mLUT +#endif + ); +#ifdef CHECK_NB_OVERLAPS + printf("PPU: %d overlaps\n", gNbOverlaps); +#endif +} + +void BroadPhaseMBP::postUpdate() +{ + { + PxU32 Nb = mMBP->mNbRegions; + const RegionData* PX_RESTRICT regions = mMBP->mRegions.begin(); + for(PxU32 i=0;imNbUpdatedBoxes = 0; + } + } + + mMBP->finalize(this); +} + +PxU32 BroadPhaseMBP::getNbCreatedPairs() const +{ + return mCreated.size(); +} + +BroadPhasePair* BroadPhaseMBP::getCreatedPairs() +{ + return mCreated.begin(); +} + +PxU32 BroadPhaseMBP::getNbDeletedPairs() const +{ + return mDeleted.size(); +} + +BroadPhasePair* BroadPhaseMBP::getDeletedPairs() +{ + return mDeleted.begin(); +} + +PxU32 BroadPhaseMBP::getNbOutOfBoundsObjects() const +{ + return mMBP->mOutOfBoundsObjects.size(); +} + +const PxU32* BroadPhaseMBP::getOutOfBoundsObjects() const +{ + return mMBP->mOutOfBoundsObjects.begin(); +} + +static void freeBuffer(Ps::Array& buffer) +{ + const PxU32 size = buffer.size(); + if(size>DEFAULT_CREATED_DELETED_PAIRS_CAPACITY) + { + buffer.reset(); + buffer.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); + } + else + { + buffer.clear(); + } +} + +void BroadPhaseMBP::freeBuffers() +{ + mMBP->freeBuffers(); + freeBuffer(mCreated); + freeBuffer(mDeleted); +} + +#if PX_CHECKED +bool BroadPhaseMBP::isValid(const BroadPhaseUpdateData& updateData) const +{ + const BpHandle* created = updateData.getCreatedHandles(); + if(created) + { + Ps::HashSet set; + PxU32 nbObjects = mMBP->mMBP_Objects.size(); + const MBP_Object* PX_RESTRICT objects = mMBP->mMBP_Objects.begin(); + while(nbObjects--) + { + if(!(objects->mFlags & MBP_REMOVED)) + set.insert(objects->mUserID); + objects++; + } + + PxU32 nbToGo = updateData.getNumCreatedHandles(); + while(nbToGo--) + { + const BpHandle index = *created++; + PX_ASSERT(indexshiftOrigin(shift); +} + +PxU32 BroadPhaseMBP::getCurrentNbPairs() const +{ + return mMBP->mPairManager.mNbActivePairs; +} diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h new file mode 100644 index 000000000..3ef82b480 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h @@ -0,0 +1,114 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_MBP_H +#define BP_BROADPHASE_MBP_H + +#include "CmPhysXCommon.h" +#include "BpBroadPhase.h" +#include "BpBroadPhaseMBPCommon.h" +#include "BpMBPTasks.h" + + class MBP; + +namespace physx +{ +namespace Bp +{ + class BroadPhaseMBP : public BroadPhase, public Ps::UserAllocated + { + PX_NOCOPY(BroadPhaseMBP) + public: + BroadPhaseMBP( PxU32 maxNbRegions, + PxU32 maxNbBroadPhaseOverlaps, + PxU32 maxNbStaticShapes, + PxU32 maxNbDynamicShapes, + PxU64 contextID); + virtual ~BroadPhaseMBP(); + + // BroadPhaseBase + virtual bool getCaps(PxBroadPhaseCaps& caps) const; + virtual PxU32 getNbRegions() const; + virtual PxU32 getRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + virtual PxU32 addRegion(const PxBroadPhaseRegion& region, bool populateRegion); + virtual bool removeRegion(PxU32 handle); + virtual PxU32 getNbOutOfBoundsObjects() const; + virtual const PxU32* getOutOfBoundsObjects() const; + //~BroadPhaseBase + + // BroadPhase + virtual PxBroadPhaseType::Enum getType() const { return PxBroadPhaseType::eMBP; } + virtual void destroy() { delete this; } + virtual void update(const PxU32 numCpuTasks, PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData, physx::PxBaseTask* continuation, physx::PxBaseTask* narrowPhaseUnblockTask); + virtual void fetchBroadPhaseResults(physx::PxBaseTask*) {} + virtual PxU32 getNbCreatedPairs() const; + virtual BroadPhasePair* getCreatedPairs(); + virtual PxU32 getNbDeletedPairs() const; + virtual BroadPhasePair* getDeletedPairs(); + virtual void freeBuffers(); + virtual void shiftOrigin(const PxVec3& shift); +#if PX_CHECKED + virtual bool isValid(const BroadPhaseUpdateData& updateData) const; +#endif + virtual BroadPhasePair* getBroadPhasePairs() const {return NULL;} //KS - TODO - implement this!!! + virtual void deletePairs(){} //KS - TODO - implement this!!! + virtual void singleThreadedUpdate(PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData); + //~BroadPhase + + MBPUpdateWorkTask mMBPUpdateWorkTask; + MBPPostUpdateWorkTask mMBPPostUpdateWorkTask; + + MBP* mMBP; // PT: TODO: aggregate + + MBP_Handle* mMapping; + PxU32 mCapacity; + Ps::Array mCreated; + Ps::Array mDeleted; + + const Bp::FilterGroup::Enum*mGroups; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* mLUT; +#endif + void setUpdateData(const BroadPhaseUpdateData& updateData); + void addObjects(const BroadPhaseUpdateData& updateData); + void removeObjects(const BroadPhaseUpdateData& updateData); + void updateObjects(const BroadPhaseUpdateData& updateData); + + void update(); + void postUpdate(); + void allocateMappingArray(PxU32 newCapacity); + + PxU32 getCurrentNbPairs() const; + }; + +} //namespace Bp + +} //namespace physx + +#endif // BP_BROADPHASE_MBP_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h new file mode 100644 index 000000000..1cc3f9dc8 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h @@ -0,0 +1,199 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_MBP_COMMON_H +#define BP_BROADPHASE_MBP_COMMON_H + +#include "PxPhysXConfig.h" +#include "BpBroadPhaseUpdate.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Bp +{ + +#define MBP_USE_WORDS +#define MBP_USE_NO_CMP_OVERLAP +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + #define MBP_SIMD_OVERLAP +#endif + +#ifdef MBP_USE_WORDS + typedef PxU16 MBP_Index; +#else + typedef PxU32 MBP_Index; +#endif + typedef PxU32 MBP_ObjectIndex; // PT: index in mMBP_Objects + typedef PxU32 MBP_Handle; // PT: returned to MBP users, combination of index/flip-flop/static-bit + + struct IAABB : public Ps::UserAllocated + { + PX_FORCE_INLINE bool isInside(const IAABB& box) const + { + if(box.mMinX>mMinX) return false; + if(box.mMinY>mMinY) return false; + if(box.mMinZ>mMinZ) return false; + if(box.mMaxX(&box.minimum.x); + mMinX = encodeFloat(binary[0])>>1; + mMinY = encodeFloat(binary[1])>>1; + mMinZ = encodeFloat(binary[2])>>1; + mMaxX = encodeFloat(binary[3])>>1; + mMaxY = encodeFloat(binary[4])>>1; + mMaxZ = encodeFloat(binary[5])>>1; + } + + PX_FORCE_INLINE void decode(PxBounds3& box) const + { + PxU32* PX_RESTRICT binary = reinterpret_cast(&box.minimum.x); + binary[0] = decodeFloat(mMinX<<1); + binary[1] = decodeFloat(mMinY<<1); + binary[2] = decodeFloat(mMinZ<<1); + binary[3] = decodeFloat(mMaxX<<1); + binary[4] = decodeFloat(mMaxY<<1); + binary[5] = decodeFloat(mMaxZ<<1); + } + + PX_FORCE_INLINE PxU32 getMin(PxU32 i) const { return (&mMinX)[i]; } + PX_FORCE_INLINE PxU32 getMax(PxU32 i) const { return (&mMaxX)[i]; } + + PxU32 mMinX; + PxU32 mMinY; + PxU32 mMinZ; + PxU32 mMaxX; + PxU32 mMaxY; + PxU32 mMaxZ; + }; + + struct SIMD_AABB : public Ps::UserAllocated + { + PX_FORCE_INLINE void initFrom(const PxBounds3& box) + { + const PxU32* PX_RESTRICT binary = reinterpret_cast(&box.minimum.x); + mMinX = encodeFloat(binary[0]); + mMinY = encodeFloat(binary[1]); + mMinZ = encodeFloat(binary[2]); + mMaxX = encodeFloat(binary[3]); + mMaxY = encodeFloat(binary[4]); + mMaxZ = encodeFloat(binary[5]); + } + + PX_FORCE_INLINE void initFrom2(const PxBounds3& box) + { + const PxU32* PX_RESTRICT binary = reinterpret_cast(&box.minimum.x); + mMinX = encodeFloat(binary[0])>>1; + mMinY = encodeFloat(binary[1])>>1; + mMinZ = encodeFloat(binary[2])>>1; + mMaxX = encodeFloat(binary[3])>>1; + mMaxY = encodeFloat(binary[4])>>1; + mMaxZ = encodeFloat(binary[5])>>1; + } + + PX_FORCE_INLINE void decode(PxBounds3& box) const + { + PxU32* PX_RESTRICT binary = reinterpret_cast(&box.minimum.x); + binary[0] = decodeFloat(mMinX<<1); + binary[1] = decodeFloat(mMinY<<1); + binary[2] = decodeFloat(mMinZ<<1); + binary[3] = decodeFloat(mMaxX<<1); + binary[4] = decodeFloat(mMaxY<<1); + binary[5] = decodeFloat(mMaxZ<<1); + } + + PX_FORCE_INLINE bool isInside(const SIMD_AABB& box) const + { + if(box.mMinX>mMinX) return false; + if(box.mMinY>mMinY) return false; + if(box.mMinZ>mMinZ) return false; + if(box.mMaxX + +namespace physx +{ +namespace Bp +{ +#define DEFAULT_DATA_ARRAY_CAPACITY 1024 +#define DEFAULT_CREATEDDELETED_PAIR_ARRAY_CAPACITY 64 +#define DEFAULT_CREATEDDELETED1AXIS_CAPACITY 8192 + +BroadPhaseSap::BroadPhaseSap( + const PxU32 maxNbBroadPhaseOverlaps, + const PxU32 maxNbStaticShapes, + const PxU32 maxNbDynamicShapes, + PxU64 contextID) : + mScratchAllocator (NULL), + mSapUpdateWorkTask (contextID), + mSapPostUpdateWorkTask (contextID), + mContextID (contextID) +{ + + for(PxU32 i=0;i<3;i++) + mBatchUpdateTasks[i].setContextId(contextID); + + //Boxes + mBoxesSize=0; + mBoxesSizePrev=0; + mBoxesCapacity = (((maxNbStaticShapes + maxNbDynamicShapes) + 31) & ~31); + mBoxEndPts[0] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*mBoxesCapacity)), "SapBox1D")); + mBoxEndPts[1] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*mBoxesCapacity)), "SapBox1D")); + mBoxEndPts[2] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*mBoxesCapacity)), "SapBox1D")); + for(PxU32 i=0; i(PX_ALLOC(ALIGN_SIZE_16((sizeof(PxU8)*mBoxesCapacity)), "BoxesUpdated")); + mSortedUpdateElements = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*mEndPointsCapacity)), "SortedUpdateElements")); + mActivityPockets = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BroadPhaseActivityPocket)*mEndPointsCapacity)), "BroadPhaseActivityPocket")); + + mEndPointValues[0] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(mEndPointsCapacity))), "ValType")); + mEndPointValues[1] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(mEndPointsCapacity))), "ValType")); + mEndPointValues[2] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(mEndPointsCapacity))), "ValType")); + mEndPointDatas[0] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(mEndPointsCapacity))), "BpHandle")); + mEndPointDatas[1] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(mEndPointsCapacity))), "BpHandle")); + mEndPointDatas[2]= reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(mEndPointsCapacity))), "BpHandle")); + + // Initialize sentinels + setMinSentinel(mEndPointValues[0][0],mEndPointDatas[0][0]); + setMaxSentinel(mEndPointValues[0][1],mEndPointDatas[0][1]); + setMinSentinel(mEndPointValues[1][0],mEndPointDatas[1][0]); + setMaxSentinel(mEndPointValues[1][1],mEndPointDatas[1][1]); + setMinSentinel(mEndPointValues[2][0],mEndPointDatas[2][0]); + setMaxSentinel(mEndPointValues[2][1],mEndPointDatas[2][1]); + + mListNext = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*mEndPointsCapacity)), "NextList")); + mListPrev = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*mEndPointsCapacity)), "PrevList")); + + for(PxU32 a = 1; a < mEndPointsCapacity; ++a) + { + mListNext[a-1] = BpHandle(a); + mListPrev[a] = BpHandle(a-1); + } + mListNext[mEndPointsCapacity-1] = BpHandle(mEndPointsCapacity-1); + mListPrev[0] = 0; + + mDefaultPairsCapacity = PxMax(maxNbBroadPhaseOverlaps, PxU32(DEFAULT_CREATEDDELETED_PAIR_ARRAY_CAPACITY)); + + mPairs.init(mDefaultPairsCapacity); + + mBatchUpdateTasks[2].set(this,2); + mBatchUpdateTasks[1].set(this,1); + mBatchUpdateTasks[0].set(this,0); + mBatchUpdateTasks[2].setPairs(NULL, 0); + mBatchUpdateTasks[1].setPairs(NULL, 0); + mBatchUpdateTasks[0].setPairs(NULL, 0); + + //Initialise data array. + mData = NULL; + mDataSize = 0; + mDataCapacity = 0; + + //Initialise pairs arrays. + mCreatedPairsArray = NULL; + mCreatedPairsCapacity = 0; + mCreatedPairsSize = 0; + mDeletedPairsArray = NULL; + mDeletedPairsCapacity = 0; + mDeletedPairsSize = 0; + mActualDeletedPairSize = 0; + +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT = NULL; +#endif +} + +BroadPhaseSap::~BroadPhaseSap() +{ + PX_FREE(mBoxEndPts[0]); + PX_FREE(mBoxEndPts[1]); + PX_FREE(mBoxEndPts[2]); + + PX_FREE(mEndPointValues[0]); + PX_FREE(mEndPointValues[1]); + PX_FREE(mEndPointValues[2]); + PX_FREE(mEndPointDatas[0]); + PX_FREE(mEndPointDatas[1]); + PX_FREE(mEndPointDatas[2]); + + PX_FREE(mListNext); + PX_FREE(mListPrev); + + PX_FREE(mSortedUpdateElements); + PX_FREE(mActivityPockets); + PX_FREE(mBoxesUpdated); + + mPairs.release(); + + mBatchUpdateTasks[0].setPairs(NULL, 0); + mBatchUpdateTasks[1].setPairs(NULL, 0); + mBatchUpdateTasks[2].setPairs(NULL, 0); + + mData = NULL; + + mCreatedPairsArray = NULL; + mDeletedPairsArray = NULL; + +} + +void BroadPhaseSap::destroy() +{ + this->~BroadPhaseSap(); + PX_FREE(this); +} + +void BroadPhaseSap::resizeBuffers() +{ + const PxU32 defaultPairsCapacity = mDefaultPairsCapacity; + + mCreatedPairsArray = reinterpret_cast(mScratchAllocator->alloc(sizeof(BroadPhasePair)*defaultPairsCapacity, true)); + mCreatedPairsCapacity = defaultPairsCapacity; + mCreatedPairsSize = 0; + + mDeletedPairsArray = reinterpret_cast(mScratchAllocator->alloc(sizeof(BroadPhasePair)*defaultPairsCapacity, true)); + mDeletedPairsCapacity = defaultPairsCapacity; + mDeletedPairsSize = 0; + + mData = reinterpret_cast(mScratchAllocator->alloc(sizeof(BpHandle)*defaultPairsCapacity, true)); + mDataCapacity = defaultPairsCapacity; + mDataSize = 0; + + mBatchUpdateTasks[0].setPairs(reinterpret_cast(mScratchAllocator->alloc(sizeof(BroadPhasePair)*defaultPairsCapacity, true)), defaultPairsCapacity); + mBatchUpdateTasks[0].setNumPairs(0); + mBatchUpdateTasks[1].setPairs(reinterpret_cast(mScratchAllocator->alloc(sizeof(BroadPhasePair)*defaultPairsCapacity, true)), defaultPairsCapacity); + mBatchUpdateTasks[1].setNumPairs(0); + mBatchUpdateTasks[2].setPairs(reinterpret_cast(mScratchAllocator->alloc(sizeof(BroadPhasePair)*defaultPairsCapacity, true)), defaultPairsCapacity); + mBatchUpdateTasks[2].setNumPairs(0); +} + +void BroadPhaseSap::freeBuffers() +{ + if(mCreatedPairsArray) mScratchAllocator->free(mCreatedPairsArray); + mCreatedPairsArray = NULL; + mCreatedPairsSize = 0; + mCreatedPairsCapacity = 0; + + if(mDeletedPairsArray) mScratchAllocator->free(mDeletedPairsArray); + mDeletedPairsArray = NULL; + mDeletedPairsSize = 0; + mDeletedPairsCapacity = 0; + mActualDeletedPairSize = 0; + + if(mData) mScratchAllocator->free(mData); + mData = NULL; + mDataSize = 0; + mDataCapacity = 0; + + if(mBatchUpdateTasks[0].getPairs()) mScratchAllocator->free(mBatchUpdateTasks[0].getPairs()); + mBatchUpdateTasks[0].setPairs(NULL, 0); + mBatchUpdateTasks[0].setNumPairs(0); + if(mBatchUpdateTasks[1].getPairs()) mScratchAllocator->free(mBatchUpdateTasks[1].getPairs()); + mBatchUpdateTasks[1].setPairs(NULL, 0); + mBatchUpdateTasks[1].setNumPairs(0); + if(mBatchUpdateTasks[2].getPairs()) mScratchAllocator->free(mBatchUpdateTasks[2].getPairs()); + mBatchUpdateTasks[2].setPairs(NULL, 0); + mBatchUpdateTasks[2].setNumPairs(0); + + //Shrink pair manager buffers it they are larger than needed but only let them shrink to a minimum size. + mPairs.shrinkMemory(); +} + +PX_FORCE_INLINE static void shiftCoord3(const ValType val0, const BpHandle handle0, + const ValType val1, const BpHandle handle1, + const ValType val2, const BpHandle handle2, + const PxF32* shift, ValType& oVal0, ValType& oVal1, ValType& oVal2) +{ + PX_ASSERT(!isSentinel(handle0)); + PX_ASSERT(!isSentinel(handle1)); + PX_ASSERT(!isSentinel(handle2)); + + PxF32 fl0, fl1, fl2; + ValType* PX_RESTRICT bpVal0 = PxUnionCast(&fl0); + ValType* PX_RESTRICT bpVal1 = PxUnionCast(&fl1); + ValType* PX_RESTRICT bpVal2 = PxUnionCast(&fl2); + *bpVal0 = decodeFloat(val0); + *bpVal1 = decodeFloat(val1); + *bpVal2 = decodeFloat(val2); + fl0 -= shift[0]; + fl1 -= shift[1]; + fl2 -= shift[2]; + oVal0 = (isMax(handle0)) ? (IntegerAABB::encodeFloatMax(*bpVal0) | 1) : ((IntegerAABB::encodeFloatMin(*bpVal0) + 1) & ~1); + oVal1 = (isMax(handle1)) ? (IntegerAABB::encodeFloatMax(*bpVal1) | 1) : ((IntegerAABB::encodeFloatMin(*bpVal1) + 1) & ~1); + oVal2 = (isMax(handle2)) ? (IntegerAABB::encodeFloatMax(*bpVal2) | 1) : ((IntegerAABB::encodeFloatMin(*bpVal2) + 1) & ~1); +} + +PX_FORCE_INLINE static void testPostShiftOrder(const ValType prevVal, ValType& currVal, const BpHandle prevIsMax, const BpHandle currIsMax) +{ + if(currVal < prevVal) + { + //The order has been broken by the lossy shift. + //Correct currVal so that it is greater than prevVal. + //If currVal is a box max then ensure that the box is of finite extent. + const ValType shiftCorrection = (prevIsMax==currIsMax) ? ValType(0) : ValType(1); + currVal = prevVal + shiftCorrection; + } +} + +void BroadPhaseSap::shiftOrigin(const PxVec3& shift) +{ + // + // Note: shifting the bounds does not necessarily preserve the order of the broadphase interval endpoints. The encoding of the float bounds is a lossy + // operation, thus it is not possible to get the original float values back and shift them. The only goal of this method is to shift the endpoints + // such that the order is preserved. The new intervals might no reflect the correct bounds! Since all bounds have been marked dirty, they will get + // recomputed in the next frame anyway. This method makes sure that the next frame update can start from a valid configuration that is close to + // the correct one and does not require too many swaps. + // + + if(0==mBoxesSize) + { + return; + } + + // + // Note: processing all the axis at once improved performance on XBox 360 and PS3 because it allows to compensate for stalls + // + + const PxF32 shiftAxis[3] = { shift.x, shift.y, shift.z }; + const BpHandle* PX_RESTRICT epData0 = mEndPointDatas[0]; + ValType* PX_RESTRICT epValues0 = mEndPointValues[0]; + const BpHandle* PX_RESTRICT epData1 = mEndPointDatas[1]; + ValType* PX_RESTRICT epValues1 = mEndPointValues[1]; + const BpHandle* PX_RESTRICT epData2 = mEndPointDatas[2]; + ValType* PX_RESTRICT epValues2 = mEndPointValues[2]; + + //Shift the first value in the array of sorted values. + { + //Shifted min (first element must be a min by definition). + shiftCoord3(epValues0[1], epData0[1], epValues1[1], epData1[1], epValues2[1], epData2[1], shiftAxis, epValues0[1], epValues1[1], epValues2[1]); + PX_ASSERT(!isMax(epData0[1])); + PX_ASSERT(!isMax(epData1[1])); + PX_ASSERT(!isMax(epData2[1])); + } + + //Shift the remainder. + ValType prevVal0 = epValues0[1]; + BpHandle prevIsMax0 = isMax(epData0[1]); + ValType prevVal1 = epValues1[1]; + BpHandle prevIsMax1 = isMax(epData1[1]); + ValType prevVal2 = epValues2[1]; + BpHandle prevIsMax2 = isMax(epData2[1]); + for(PxU32 i=2; i <= mBoxesSize*2; i++) + { + const BpHandle handle0 = epData0[i]; + const BpHandle handle1 = epData1[i]; + const BpHandle handle2 = epData2[i]; + PX_ASSERT(!isSentinel(handle0)); + PX_ASSERT(!isSentinel(handle1)); + PX_ASSERT(!isSentinel(handle2)); + + //Get the relevant prev and curr values after the shift. + const BpHandle currIsMax0 = isMax(epData0[i]); + const BpHandle currIsMax1 = isMax(epData1[i]); + const BpHandle currIsMax2 = isMax(epData2[i]); + ValType currVal0, currVal1, currVal2; + shiftCoord3(epValues0[i], handle0, epValues1[i], handle1, epValues2[i], handle2, shiftAxis, currVal0, currVal1, currVal2); + + //Test if the order has been preserved by the lossy shift. + testPostShiftOrder(prevVal0, currVal0, prevIsMax0, currIsMax0); + testPostShiftOrder(prevVal1, currVal1, prevIsMax1, currIsMax1); + testPostShiftOrder(prevVal2, currVal2, prevIsMax2, currIsMax2); + + prevIsMax0 = currIsMax0; + prevVal0 = currVal0; + prevIsMax1 = currIsMax1; + prevVal1 = currVal1; + prevIsMax2 = currIsMax2; + prevVal2 = currVal2; + + epValues0[i] = currVal0; + epValues1[i] = currVal1; + epValues2[i] = currVal2; + } + + PX_ASSERT(isSelfOrdered()); +} + +#if PX_CHECKED +bool BroadPhaseSap::isValid(const BroadPhaseUpdateData& updateData) const +{ + //Test that the created bounds haven't been added already (without first being removed). + const BpHandle* created=updateData.getCreatedHandles(); + const PxU32 numCreated=updateData.getNumCreatedHandles(); + for(PxU32 i=0;i=mBoxesCapacity then we need to resize to add this id, meaning that the id must be new. + if(id= mBoxesCapacity) + { + return false; + } + } + + //Test that the updated bounds have been been added without being removed. + for(PxU32 i=0;i= mBoxesCapacity) + { + return false; + } + } + + //Test that the removed bounds have already been added and haven't been removed. + for(PxU32 i=0;iremoveReference(); + + if(setUpdateData(updateData)) + { + mScratchAllocator = scratchAllocator; + + resizeBuffers(); + + mSapPostUpdateWorkTask.setBroadPhase(this); + mSapUpdateWorkTask.setBroadPhase(this); + + mSapPostUpdateWorkTask.set(numCpuTasks); + mSapUpdateWorkTask.set(numCpuTasks); + + mSapPostUpdateWorkTask.setContinuation(continuation); + mSapUpdateWorkTask.setContinuation(&mSapPostUpdateWorkTask); + + mSapPostUpdateWorkTask.removeReference(); + mSapUpdateWorkTask.removeReference(); + } +} + +void BroadPhaseSap::singleThreadedUpdate(PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData) +{ +#if PX_CHECKED + PX_CHECK_AND_RETURN(scratchAllocator, "BroadPhaseSap::singleThreadedUpdate - scratchAllocator must be non-NULL \n"); +#endif + + if(setUpdateData(updateData)) + { + mScratchAllocator = scratchAllocator; + resizeBuffers(); + update(); + postUpdate(); + } +} + +bool BroadPhaseSap::setUpdateData(const BroadPhaseUpdateData& updateData) +{ + PX_ASSERT(0==mCreatedPairsSize); + PX_ASSERT(0==mDeletedPairsSize); + +#if PX_CHECKED + if(!BroadPhaseUpdateData::isValid(updateData, *this)) + { + PX_CHECK_MSG(false, "Illegal BroadPhaseUpdateData \n"); + mCreated = NULL; + mCreatedSize = 0; + mUpdated = NULL; + mUpdatedSize = 0; + mRemoved = NULL; + mRemovedSize = 0; + mBoxBoundsMinMax = updateData.getAABBs(); + mBoxGroups = updateData.getGroups(); + return false; + } +#endif + + //Copy across the data ptrs and sizes. + mCreated = updateData.getCreatedHandles(); + mCreatedSize = updateData.getNumCreatedHandles(); + mUpdated = updateData.getUpdatedHandles(); + mUpdatedSize = updateData.getNumUpdatedHandles(); + mRemoved = updateData.getRemovedHandles(); + mRemovedSize = updateData.getNumRemovedHandles(); + mBoxBoundsMinMax = updateData.getAABBs(); + mBoxGroups = updateData.getGroups(); +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT = updateData.getLUT(); +#endif + mContactDistance = updateData.getContactDistance(); + + //Do we need more memory to store the positions of each box min/max in the arrays of sorted boxes min/max? + if(updateData.getCapacity() > mBoxesCapacity) + { + const PxU32 oldBoxesCapacity=mBoxesCapacity; + const PxU32 newBoxesCapacity=updateData.getCapacity(); + SapBox1D* newBoxEndPts0 = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*newBoxesCapacity)), "SapBox1D")); + SapBox1D* newBoxEndPts1 = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*newBoxesCapacity)), "SapBox1D")); + SapBox1D* newBoxEndPts2 = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*newBoxesCapacity)), "SapBox1D")); + + PxMemCopy(newBoxEndPts0, mBoxEndPts[0], sizeof(SapBox1D)*oldBoxesCapacity); + PxMemCopy(newBoxEndPts1, mBoxEndPts[1], sizeof(SapBox1D)*oldBoxesCapacity); + PxMemCopy(newBoxEndPts2, mBoxEndPts[2], sizeof(SapBox1D)*oldBoxesCapacity); + for(PxU32 i=oldBoxesCapacity;i(PX_ALLOC(ALIGN_SIZE_16((sizeof(PxU8))*newBoxesCapacity), "Updated Boxes")); + } + + //Do we need more memory for the array of sorted boxes? + if(2*(mBoxesSize + mCreatedSize) + NUM_SENTINELS > mEndPointsCapacity) + { + const PxU32 newEndPointsCapacity = 2*(mBoxesSize + mCreatedSize) + NUM_SENTINELS; + + ValType* newEndPointValuesX = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(newEndPointsCapacity))), "BPValType")); + ValType* newEndPointValuesY = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(newEndPointsCapacity))), "BPValType")); + ValType* newEndPointValuesZ = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(newEndPointsCapacity))), "BPValType")); + BpHandle* newEndPointDatasX = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(newEndPointsCapacity))), "BpHandle")); + BpHandle* newEndPointDatasY = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(newEndPointsCapacity))), "BpHandle")); + BpHandle* newEndPointDatasZ = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(newEndPointsCapacity))), "BpHandle")); + + PX_FREE(mListNext); + PX_FREE(mListPrev); + + mListNext = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*newEndPointsCapacity)), "NextList")); + mListPrev = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*newEndPointsCapacity)), "Prev")); + + + for(PxU32 a = 1; a < newEndPointsCapacity; ++a) + { + mListNext[a-1] = BpHandle(a); + mListPrev[a] = BpHandle(a-1); + } + mListNext[newEndPointsCapacity-1] = BpHandle(newEndPointsCapacity-1); + mListPrev[0] = 0; + + PxMemCopy(newEndPointValuesX, mEndPointValues[0], sizeof(ValType)*(mBoxesSize*2+NUM_SENTINELS)); + PxMemCopy(newEndPointValuesY, mEndPointValues[1], sizeof(ValType)*(mBoxesSize*2+NUM_SENTINELS)); + PxMemCopy(newEndPointValuesZ, mEndPointValues[2], sizeof(ValType)*(mBoxesSize*2+NUM_SENTINELS)); + PxMemCopy(newEndPointDatasX, mEndPointDatas[0], sizeof(BpHandle)*(mBoxesSize*2+NUM_SENTINELS)); + PxMemCopy(newEndPointDatasY, mEndPointDatas[1], sizeof(BpHandle)*(mBoxesSize*2+NUM_SENTINELS)); + PxMemCopy(newEndPointDatasZ, mEndPointDatas[2], sizeof(BpHandle)*(mBoxesSize*2+NUM_SENTINELS)); + PX_FREE(mEndPointValues[0]); + PX_FREE(mEndPointValues[1]); + PX_FREE(mEndPointValues[2]); + PX_FREE(mEndPointDatas[0]); + PX_FREE(mEndPointDatas[1]); + PX_FREE(mEndPointDatas[2]); + mEndPointValues[0] = newEndPointValuesX; + mEndPointValues[1] = newEndPointValuesY; + mEndPointValues[2] = newEndPointValuesZ; + mEndPointDatas[0] = newEndPointDatasX; + mEndPointDatas[1] = newEndPointDatasY; + mEndPointDatas[2] = newEndPointDatasZ; + mEndPointsCapacity = newEndPointsCapacity; + + PX_FREE(mSortedUpdateElements); + PX_FREE(mActivityPockets); + mSortedUpdateElements = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*newEndPointsCapacity)), "SortedUpdateElements")); + mActivityPockets = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BroadPhaseActivityPocket)*newEndPointsCapacity)), "BroadPhaseActivityPocket")); + } + + PxMemZero(mBoxesUpdated, sizeof(PxU8) * (mBoxesCapacity)); + + for(PxU32 a=0;a volB) + addPair(volA, volB, mScratchAllocator, mPairs, da); + else + removePair(volA, volB, mScratchAllocator, mPairs, da); + } + } + + mData = da.mData; + mDataSize = da.mSize; + mDataCapacity = da.mCapacity; + + batchCreate(); + + //Compute the lists of created and deleted overlap pairs. + + ComputeCreatedDeletedPairsLists( + mBoxGroups, + mData,mDataSize, + mScratchAllocator, + mCreatedPairsArray,mCreatedPairsSize,mCreatedPairsCapacity, + mDeletedPairsArray,mDeletedPairsSize,mDeletedPairsCapacity, + mActualDeletedPairSize, + mPairs); + + //DeletePairsLists(mActualDeletedPairSize, mDeletedPairsArray, mPairs); + + PX_ASSERT(isSelfConsistent()); + mBoxesSizePrev=mBoxesSize; +} + +void BroadPhaseSap::deletePairs() +{ + DeletePairsLists(mActualDeletedPairSize, mDeletedPairsArray, mPairs); +} + +void BroadPhaseBatchUpdateWorkTask::runInternal() +{ + mPairsSize=0; + mSap->batchUpdate(mAxis, mPairs, mPairsSize, mPairsCapacity); +} + +void BroadPhaseSap::update() +{ + PX_PROFILE_ZONE("BroadPhase.SapUpdate", mContextID); + + batchRemove(); + + //Check that the overlap pairs per axis have been reset. + PX_ASSERT(0==mBatchUpdateTasks[0].getPairsSize()); + PX_ASSERT(0==mBatchUpdateTasks[1].getPairsSize()); + PX_ASSERT(0==mBatchUpdateTasks[2].getPairsSize()); + + mBatchUpdateTasks[0].runInternal(); + mBatchUpdateTasks[1].runInternal(); + mBatchUpdateTasks[2].runInternal(); +} + +/////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE void InsertEndPoints(const ValType* PX_RESTRICT newEndPointValues, const BpHandle* PX_RESTRICT newEndPointDatas, PxU32 numNewEndPoints, + ValType* PX_RESTRICT endPointValues, BpHandle* PX_RESTRICT endPointDatas, const PxU32 numEndPoints, SapBox1D* PX_RESTRICT boxes) +{ + ValType* const BaseEPValue = endPointValues; + BpHandle* const BaseEPData = endPointDatas; + + const PxU32 OldSize = numEndPoints-NUM_SENTINELS; + const PxU32 NewSize = numEndPoints-NUM_SENTINELS+numNewEndPoints; + + BaseEPValue[NewSize + 1] = BaseEPValue[OldSize + 1]; + BaseEPData[NewSize + 1] = BaseEPData[OldSize + 1]; + + PxI32 WriteIdx = PxI32(NewSize); + PxU32 CurrInsIdx = 0; + + //const SapValType* FirstValue = &BaseEPValue[0]; + const BpHandle* FirstData = &BaseEPData[0]; + const ValType* CurrentValue = &BaseEPValue[OldSize]; + const BpHandle* CurrentData = &BaseEPData[OldSize]; + while(CurrentData>=FirstData) + { + const ValType& SrcValue = *CurrentValue; + const BpHandle& SrcData = *CurrentData; + const ValType& InsValue = newEndPointValues[CurrInsIdx]; + const BpHandle& InsData = newEndPointDatas[CurrInsIdx]; + + // We need to make sure we insert maxs before mins to handle exactly equal endpoints correctly + const bool ShouldInsert = isMax(InsData) ? (SrcValue <= InsValue) : (SrcValue < InsValue); + + const ValType& MovedValue = ShouldInsert ? InsValue : SrcValue; + const BpHandle& MovedData = ShouldInsert ? InsData : SrcData; + BaseEPValue[WriteIdx] = MovedValue; + BaseEPData[WriteIdx] = MovedData; + boxes[getOwner(MovedData)].mMinMax[isMax(MovedData)] = BpHandle(WriteIdx--); + + if(ShouldInsert) + { + CurrInsIdx++; + if(CurrInsIdx >= numNewEndPoints) + break;//we just inserted the last endpoint + } + else + { + CurrentValue--; + CurrentData--; + } + } +} + +static PX_FORCE_INLINE bool Intersect3D(const ValType bDir1Min, const ValType bDir1Max, const ValType bDir2Min, const ValType bDir2Max, const ValType bDir3Min, const ValType bDir3Max, + const ValType cDir1Min, const ValType cDir1Max, const ValType cDir2Min, const ValType cDir2Max, const ValType cDir3Min, const ValType cDir3Max) +{ + return (bDir1Max >= cDir1Min && cDir1Max >= bDir1Min && + bDir2Max >= cDir2Min && cDir2Max >= bDir2Min && + bDir3Max >= cDir3Min && cDir3Max >= bDir3Min); +} + +void BroadPhaseSap::ComputeSortedLists( //const PxVec4& globalMin, const PxVec4& /*globalMax*/, + BpHandle* PX_RESTRICT newBoxIndicesSorted, PxU32& newBoxIndicesCount, BpHandle* PX_RESTRICT oldBoxIndicesSorted, PxU32& oldBoxIndicesCount, + bool& allNewBoxesStatics, bool& allOldBoxesStatics) +{ + //To help us gather the two lists of sorted boxes we are going to use a bitmap and our knowledge of the indices of the new boxes + const PxU32 bitmapWordCount = ((mBoxesCapacity*2 + 31) & ~31)/32; + Cm::TmpMem bitMapMem(bitmapWordCount); + PxU32* bitMapWords = bitMapMem.getBase(); + PxMemSet(bitMapWords, 0, sizeof(PxU32)*bitmapWordCount); + Cm::BitMap bitmap; + bitmap.setWords(bitMapWords, bitmapWordCount); + + const PxU32 axis0 = 0; + const PxU32 axis1 = 2; + const PxU32 axis2 = 1; + + const PxU32 insertAABBStart = 0; + const PxU32 insertAABBEnd = mCreatedSize; + const BpHandle* PX_RESTRICT createdAABBs = mCreated; + SapBox1D** PX_RESTRICT asapBoxes = mBoxEndPts; + const Bp::FilterGroup::Enum* PX_RESTRICT asapBoxGroupIds = mBoxGroups; + BpHandle* PX_RESTRICT asapEndPointDatas = mEndPointDatas[axis0]; + const PxU32 numSortedEndPoints = mBoxesSize*2 + NUM_SENTINELS; + + //Set the bitmap for new box ids and compute the aabb (of the sorted handles/indices and not of the values) that bounds all new boxes. + + PxU32 globalAABBMinX = PX_MAX_U32; + PxU32 globalAABBMinY = PX_MAX_U32; + PxU32 globalAABBMinZ = PX_MAX_U32; + PxU32 globalAABBMaxX = 0; + PxU32 globalAABBMaxY = 0; + PxU32 globalAABBMaxZ = 0; + + // PT: TODO: compute the global bounds from the initial data, more cache/SIMD-friendly + // => maybe doesn't work, we're dealing with indices here not actual float values IIRC + for(PxU32 i=insertAABBStart;i(globalMin.x)); + PxU32 _globalAABBMinY = IntegerAABB::encodeFloatMin(PxUnionCast(globalMin.y)); + PxU32 _globalAABBMinZ = IntegerAABB::encodeFloatMin(PxUnionCast(globalMin.z)); + PxU32 _globalAABBMaxX = IntegerAABB::encodeFloatMin(PxUnionCast(globalMax.x)); + PxU32 _globalAABBMaxY = IntegerAABB::encodeFloatMin(PxUnionCast(globalMax.y)); + PxU32 _globalAABBMaxZ = IntegerAABB::encodeFloatMin(PxUnionCast(globalMax.z)); + (void)_globalAABBMinX;*/ + + PxU32 oldStaticCount=0; + PxU32 newStaticCount=0; + + //Assign the sorted end pts to the appropriate arrays. + // PT: TODO: we could just do this loop before inserting the new endpts, i.e. no need for a bitmap etc + // => but we need to insert the pts first to have valid mMinMax data in the above loop. + // => but why do we iterate over endpoints and then skip the mins? Why not iterate directly over boxes? ====> probably to get sorted results + // => we could then just use the regular bounds data etc + for(PxU32 i=1;i nepsv(numEndPoints), bv(numEndPoints); + ValType* newEPSortedValues = nepsv.getBase(); + ValType* bufferValues = bv.getBase(); + + // PT: TODO: use the scratch allocator + Cm::RadixSortBuffered RS; + + for(PxU32 Axis=0;Axis<3;Axis++) + { + for(PxU32 i=0;i>1]); + bufferDatas[i] = setData(boxIndex, (sortedIndex&1)!=0); + } + } + + InsertEndPoints(bufferValues, bufferDatas, numEndPoints, mEndPointValues[Axis], mEndPointDatas[Axis], 2*(mBoxesSize-mCreatedSize)+NUM_SENTINELS, mBoxEndPts[Axis]); + } + } + + //Some debug tests. +#if PX_DEBUG + { + for(PxU32 i=0;i oldBoxesIndicesSortedMem(numOldBoxes); + Cm::TmpMem newBoxesIndicesSortedMem(numNewBoxes); + BpHandle* oldBoxesIndicesSorted = oldBoxesIndicesSortedMem.getBase(); + BpHandle* newBoxesIndicesSorted = newBoxesIndicesSortedMem.getBase(); + PxU32 oldBoxCount = 0; + PxU32 newBoxCount = 0; + + bool allNewBoxesStatics = false; + bool allOldBoxesStatics = false; + // PT: TODO: separate static/dynamic to speed things up, compute "minPosList" etc at the same time + // PT: TODO: isn't "newBoxesIndicesSorted" the same as what we already computed in batchCreate() ? + //Ready to gather the two lists now. + ComputeSortedLists(/*globalMin, globalMax,*/ newBoxesIndicesSorted, newBoxCount, oldBoxesIndicesSorted, oldBoxCount, allNewBoxesStatics, allOldBoxesStatics); + + //Intersect new boxes with new boxes and new boxes with existing boxes. + if(!allNewBoxesStatics || !allOldBoxesStatics) + { + const AuxData data0(newBoxCount, mBoxEndPts, newBoxesIndicesSorted, mBoxGroups); + + if(!allNewBoxesStatics) + { + performBoxPruningNewNew(&data0, mScratchAllocator, +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT, +#endif + mPairs, mData, mDataSize, mDataCapacity); + } + + // the old boxes are not the first ones in the array + if(numOldBoxes) + { + if(oldBoxCount) + { + const AuxData data1(oldBoxCount, mBoxEndPts, oldBoxesIndicesSorted, mBoxGroups); + + performBoxPruningNewOld(&data0, &data1, mScratchAllocator, +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT, +#endif + mPairs, mData, mDataSize, mDataCapacity); + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void BroadPhaseSap::batchRemove() +{ + if(!mRemovedSize) return; // Early-exit if no object has been removed + + //The box count is incremented when boxes are added to the create list but these boxes + //haven't yet been added to the pair manager or the sorted axis lists. We need to + //pretend that the box count is the value it was when the bp was last updated. + //Then, at the end, we need to set the box count to the number that includes the boxes + //in the create list and subtract off the boxes that have been removed. + PxU32 currBoxesSize=mBoxesSize; + mBoxesSize=mBoxesSizePrev; + + for(PxU32 Axis=0;Axis<3;Axis++) + { + ValType* const BaseEPValue = mEndPointValues[Axis]; + BpHandle* const BaseEPData = mEndPointDatas[Axis]; + PxU32 MinMinIndex = PX_MAX_U32; + for(PxU32 i=0;i>5); + Cm::TmpMem bitmapWords(bitmapWordCount); + PxMemZero(bitmapWords.getBase(),sizeof(PxU32)*bitmapWordCount); + Cm::BitMap bitmap; + bitmap.setWords(bitmapWords.getBase(),bitmapWordCount); + for(PxU32 i=0;i oldMaxNb); + PX_ASSERT(newMaxNb > 0); + PX_ASSERT(0==((newMaxNb*sizeof(BroadPhasePair)) & 15)); + BroadPhasePair* newElements = reinterpret_cast(scratchAllocator->alloc(sizeof(BroadPhasePair)*newMaxNb, true)); + PX_ASSERT(0==(uintptr_t(newElements) & 0x0f)); + PxMemCopy(newElements, elements, oldMaxNb*sizeof(BroadPhasePair)); + scratchAllocator->free(elements); + return newElements; +} + +#define PERFORM_COMPARISONS 1 + + +void BroadPhaseSap::batchUpdate +(const PxU32 Axis, BroadPhasePair*& pairs, PxU32& pairsSize, PxU32& pairsCapacity) +{ + //Nothin updated so don't do anything + if(mUpdatedSize == 0) + return; + + //If number updated is sufficiently fewer than number of boxes (say less than 20%) + if((mUpdatedSize*5) < mBoxesSize) + { + batchUpdateFewUpdates(Axis, pairs, pairsSize, pairsCapacity); + return; + } + + PxU32 numPairs=0; + PxU32 maxNumPairs=pairsCapacity; + + const PxBounds3* PX_RESTRICT boxMinMax3D = mBoxBoundsMinMax; + SapBox1D* boxMinMax2D[6]={mBoxEndPts[1],mBoxEndPts[2],mBoxEndPts[2],mBoxEndPts[0],mBoxEndPts[0],mBoxEndPts[1]}; + + const SapBox1D* PX_RESTRICT boxMinMax0=boxMinMax2D[2*Axis+0]; + const SapBox1D* PX_RESTRICT boxMinMax1=boxMinMax2D[2*Axis+1]; + +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + const Bp::FilterGroup::Enum* PX_RESTRICT asapBoxGroupIds=mBoxGroups; +#endif + + SapBox1D* PX_RESTRICT asapBoxes=mBoxEndPts[Axis]; + + ValType* PX_RESTRICT asapEndPointValues=mEndPointValues[Axis]; + BpHandle* PX_RESTRICT asapEndPointDatas=mEndPointDatas[Axis]; + + ValType* const PX_RESTRICT BaseEPValues = asapEndPointValues; + BpHandle* const PX_RESTRICT BaseEPDatas = asapEndPointDatas; + + PxU8* PX_RESTRICT updated = mBoxesUpdated; + + //KS - can we lazy create these inside the loop? Might benefit us + + //There are no extents, jus the sentinels, so exit early. + if(isSentinel(BaseEPDatas[1])) + return; + + //We are going to skip the 1st element in the array (the sublist will be sorted) + //but we must first update its value if it has moved + //const PxU32 startIsMax = isMax(BaseEPDatas[1]); + PX_ASSERT(!isMax(BaseEPDatas[1])); + const BpHandle startHandle = getOwner(BaseEPDatas[1]); + + //KS - in theory, we should just be able to grab the min element but there's some issue where a body's max < min (i.e. an invalid extents) that + //appears in a unit test + // ValType ThisValue_ = boxMinMax3D[startHandle].getMin(Axis); + ValType ThisValue_ = encodeMin(boxMinMax3D[startHandle], Axis, mContactDistance[startHandle]); + + BaseEPValues[1] = ThisValue_; + + PxU32 updateCounter = mUpdatedSize*2; + + updateCounter -= updated[startHandle]; + + //We'll never overlap with this sentinel but it just ensures that we don't need to branch to see if + //there's a pocket that we need to test against + + BroadPhaseActivityPocket* PX_RESTRICT currentPocket = mActivityPockets; + + currentPocket->mEndIndex = 0; + currentPocket->mStartIndex = 0; + + BpHandle ind = 2; + PxU8 wasUpdated = updated[startHandle]; + for(; !isSentinel(BaseEPDatas[ind]); ++ind) + { + BpHandle ThisData = BaseEPDatas[ind]; + + const BpHandle handle = getOwner(ThisData); + + if(updated[handle] || wasUpdated) + { + wasUpdated = updated[handle]; + updateCounter -= wasUpdated; + + BpHandle ThisIndex = ind; + + const BpHandle startIsMax = isMax(ThisData); + + //Access and write back the updated values. TODO - can we avoid this when we're walking through inactive nodes? + //BPValType ThisValue = boxMinMax1D[Axis][twoHandle+startIsMax]; + //BPValType ThisValue = startIsMax ? boxMinMax3D[handle].getMax(Axis) : boxMinMax3D[handle].getMin(Axis); + //ValType ThisValue = boxMinMax3D[handle].getExtent(startIsMax, Axis); + + ValType ThisValue = startIsMax ? encodeMax(boxMinMax3D[handle], Axis, mContactDistance[handle]) + : encodeMin(boxMinMax3D[handle], Axis, mContactDistance[handle]); + + BaseEPValues[ThisIndex] = ThisValue; + + PX_ASSERT(handle!=BP_INVALID_BP_HANDLE); + + //We always iterate back through the list... + + BpHandle CurrentIndex = mListPrev[ThisIndex]; + ValType CurrentValue = BaseEPValues[CurrentIndex]; + //PxBpHandle CurrentData = BaseEPDatas[CurrentIndex]; + + if(CurrentValue > ThisValue) + { + wasUpdated = 1; + //Get the bounds of the curr aabb. + //Get the box1d of the curr aabb. + /*const SapBox1D* PX_RESTRICT Object=&asapBoxes[handle]; + PX_ASSERT(Object->mMinMax[0]!=BP_INVALID_BP_HANDLE); + PX_ASSERT(Object->mMinMax[1]!=BP_INVALID_BP_HANDLE);*/ + + // const ValType boxMax=boxMinMax3D[handle].getMax(Axis); + + const ValType boxMax=encodeMax(boxMinMax3D[handle], Axis, mContactDistance[handle]); + + PxU32 endIndex = ind; + PxU32 startIndex = ind; + +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + const Bp::FilterGroup::Enum group = asapBoxGroupIds[handle]; +#endif + if(!isMax(ThisData)) + { + do + { + BpHandle CurrentData = BaseEPDatas[CurrentIndex]; + const BpHandle IsMax = isMax(CurrentData); + + #if PERFORM_COMPARISONS + if(IsMax) + { + const BpHandle ownerId=getOwner(CurrentData); + SapBox1D* PX_RESTRICT id1 = asapBoxes + ownerId; + // Our min passed a max => start overlap + + if( + BaseEPValues[id1->mMinMax[0]] < boxMax && + //2D intersection test using up-to-date values + Intersect2D_Handle(boxMinMax0[handle].mMinMax[0], boxMinMax0[handle].mMinMax[1], boxMinMax1[handle].mMinMax[0], boxMinMax1[handle].mMinMax[1], + boxMinMax0[ownerId].mMinMax[0],boxMinMax0[ownerId].mMinMax[1],boxMinMax1[ownerId].mMinMax[0],boxMinMax1[ownerId].mMinMax[1]) + + #if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + && groupFiltering(group, asapBoxGroupIds[ownerId], mLUT) + #else + && groupFiltering(group, asapBoxGroupIds[ownerId]) + #endif + #else + && handle!=ownerId + #endif + ) + { + if(numPairs==maxNumPairs) + { + const PxU32 newMaxNumPairs=maxNumPairs*2; + pairs = reinterpret_cast(resizeBroadPhasePairArray(maxNumPairs, newMaxNumPairs, mScratchAllocator, pairs)); + maxNumPairs=newMaxNumPairs; + } + PX_ASSERT(numPairs stop overlap + const BpHandle ownerId=getOwner(CurrentData); + +#if 1 + if( +#if BP_SAP_USE_OVERLAP_TEST_ON_REMOVES + Intersect2D_Handle(boxMinMax0[handle].mMinMax[0], boxMinMax0[handle].mMinMax[1], boxMinMax1[handle].mMinMax[0], boxMinMax1[handle].mMinMax[1], + boxMinMax0[ownerId].mMinMax[0],boxMinMax0[ownerId].mMinMax[1],boxMinMax1[ownerId].mMinMax[0],boxMinMax1[ownerId].mMinMax[1]) +#endif +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + && groupFiltering(group, asapBoxGroupIds[ownerId], mLUT) + #else + && groupFiltering(group, asapBoxGroupIds[ownerId]) + #endif +#else + && handle!=ownerId +#endif + ) +#endif + { + if(numPairs==maxNumPairs) + { + const PxU32 newMaxNumPairs=maxNumPairs*2; + pairs = reinterpret_cast(resizeBroadPhasePairArray(maxNumPairs, newMaxNumPairs, mScratchAllocator, pairs)); + maxNumPairs=newMaxNumPairs; + } + PX_ASSERT(numPairsmStartIndex) + { + currentPocket--; + } + //If our start index > currentPocket->mEndIndex, then we don't overlap so create a new pocket + if(currentPocket == mActivityPockets || startIndex > (currentPocket->mEndIndex+1)) + { + currentPocket++; + currentPocket->mStartIndex = startIndex; + } + currentPocket->mEndIndex = endIndex; + }// update max + //ind++; + } + else if (updateCounter == 0) //We've updated all the bodies and neither this nor the previous body was updated, so we're done + break; + + }// updated aabbs + + pairsSize=numPairs; + pairsCapacity=maxNumPairs; + + + BroadPhaseActivityPocket* pocket = mActivityPockets+1; + + while(pocket <= currentPocket) + { + for(PxU32 a = pocket->mStartIndex; a <= pocket->mEndIndex; ++a) + { + mListPrev[a] = BpHandle(a); + } + + //Now copy all the data to the array, updating the remap table + + PxU32 CurrIndex = pocket->mStartIndex-1; + for(PxU32 a = pocket->mStartIndex; a <= pocket->mEndIndex; ++a) + { + CurrIndex = mListNext[CurrIndex]; + PxU32 origIndex = CurrIndex; + BpHandle remappedIndex = mListPrev[origIndex]; + + if(origIndex != a) + { + const BpHandle ownerId=getOwner(BaseEPDatas[remappedIndex]); + const BpHandle IsMax = isMax(BaseEPDatas[remappedIndex]); + ValType tmp = BaseEPValues[a]; + BpHandle tmpHandle = BaseEPDatas[a]; + + BaseEPValues[a] = BaseEPValues[remappedIndex]; + BaseEPDatas[a] = BaseEPDatas[remappedIndex]; + + BaseEPValues[remappedIndex] = tmp; + BaseEPDatas[remappedIndex] = tmpHandle; + + mListPrev[remappedIndex] = mListPrev[a]; + //Write back remap index (should be an immediate jump to original index) + mListPrev[mListPrev[a]] = remappedIndex; + asapBoxes[ownerId].mMinMax[IsMax] = BpHandle(a); + } + + } + + ////Reset next and prev ptrs back + for(PxU32 a = pocket->mStartIndex-1; a <= pocket->mEndIndex; ++a) + { + mListPrev[a+1] = BpHandle(a); + mListNext[a] = BpHandle(a+1); + } + + pocket++; + } + mListPrev[0] = 0; +} + + +void BroadPhaseSap::batchUpdateFewUpdates +(const PxU32 Axis, BroadPhasePair*& pairs, PxU32& pairsSize, PxU32& pairsCapacity) +{ + PxU32 numPairs=0; + PxU32 maxNumPairs=pairsCapacity; + + const PxBounds3* PX_RESTRICT boxMinMax3D = mBoxBoundsMinMax; + SapBox1D* boxMinMax2D[6]={mBoxEndPts[1],mBoxEndPts[2],mBoxEndPts[2],mBoxEndPts[0],mBoxEndPts[0],mBoxEndPts[1]}; + +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + const Bp::FilterGroup::Enum* PX_RESTRICT asapBoxGroupIds=mBoxGroups; +#endif + + SapBox1D* PX_RESTRICT asapBoxes=mBoxEndPts[Axis]; + + /*const BPValType* PX_RESTRICT boxMinMax0=boxMinMax2D[2*Axis]; + const BPValType* PX_RESTRICT boxMinMax1=boxMinMax2D[2*Axis+1];*/ + + ValType* PX_RESTRICT asapEndPointValues=mEndPointValues[Axis]; + BpHandle* PX_RESTRICT asapEndPointDatas=mEndPointDatas[Axis]; + + ValType* const PX_RESTRICT BaseEPValues = asapEndPointValues; + BpHandle* const PX_RESTRICT BaseEPDatas = asapEndPointDatas; + + const SapBox1D* PX_RESTRICT boxMinMax0=boxMinMax2D[2*Axis+0]; + const SapBox1D* PX_RESTRICT boxMinMax1=boxMinMax2D[2*Axis+1]; + + PxU8* PX_RESTRICT updated = mBoxesUpdated; + + const PxU32 endPointSize = mBoxesSize*2 + 1; + + //There are no extents, just the sentinels, so exit early. + if(isSentinel(BaseEPDatas[1])) + return; + + PxU32 ind_ = 0; + + PxU32 index = 1; + + if(mUpdatedSize < 512) + { + //The array of updated elements is small, so use qsort to sort them + for(PxU32 a = 0; a < mUpdatedSize; ++a) + { + const PxU32 handle=mUpdated[a]; + + const SapBox1D* Object=&asapBoxes[handle]; + + PX_ASSERT(Object->mMinMax[0]!=BP_INVALID_BP_HANDLE); + PX_ASSERT(Object->mMinMax[1]!=BP_INVALID_BP_HANDLE); + + //Get the bounds of the curr aabb. + +// const ValType boxMin=boxMinMax3D[handle].getMin(Axis); +// const ValType boxMax=boxMinMax3D[handle].getMax(Axis); + + const ValType boxMin = encodeMin(boxMinMax3D[handle], Axis, mContactDistance[handle]); + const ValType boxMax = encodeMax(boxMinMax3D[handle], Axis, mContactDistance[handle]); + + BaseEPValues[Object->mMinMax[0]] = boxMin; + BaseEPValues[Object->mMinMax[1]] = boxMax; + + mSortedUpdateElements[ind_++] = Object->mMinMax[0]; + mSortedUpdateElements[ind_++] = Object->mMinMax[1]; + } + Ps::sort(mSortedUpdateElements, ind_); + } + else + { + //The array of updated elements is large so use a bucket sort to sort them + for(; index < endPointSize; ++index) + { + if(isSentinel( BaseEPDatas[index] )) + break; + BpHandle ThisData = BaseEPDatas[index]; + BpHandle owner = BpHandle(getOwner(ThisData)); + if(updated[owner]) + { + //BPValType ThisValue = isMax(ThisData) ? boxMinMax3D[owner].getMax(Axis) : boxMinMax3D[owner].getMin(Axis); + ValType ThisValue = isMax(ThisData) ? encodeMax(boxMinMax3D[owner], Axis, mContactDistance[owner]) + : encodeMin(boxMinMax3D[owner], Axis, mContactDistance[owner]); + BaseEPValues[index] = ThisValue; + mSortedUpdateElements[ind_++] = BpHandle(index); + } + } + } + + const PxU32 updateCounter = ind_; + + //We'll never overlap with this sentinel but it just ensures that we don't need to branch to see if + //there's a pocket that we need to test against + BroadPhaseActivityPocket* PX_RESTRICT currentPocket = mActivityPockets; + currentPocket->mEndIndex = 0; + currentPocket->mStartIndex = 0; + + for(PxU32 a = 0; a < updateCounter; ++a) + { + BpHandle ind = mSortedUpdateElements[a]; + + BpHandle NextData; + BpHandle PrevData; + do + { + BpHandle ThisData = BaseEPDatas[ind]; + + const BpHandle handle = getOwner(ThisData); + + BpHandle ThisIndex = ind; + ValType ThisValue = BaseEPValues[ThisIndex]; + + //Get the box1d of the curr aabb. + const SapBox1D* PX_RESTRICT Object=&asapBoxes[handle]; + + PX_ASSERT(handle!=BP_INVALID_BP_HANDLE); + + PX_ASSERT(Object->mMinMax[0]!=BP_INVALID_BP_HANDLE); + PX_ASSERT(Object->mMinMax[1]!=BP_INVALID_BP_HANDLE); + PX_UNUSED(Object); + + //Get the bounds of the curr aabb. + //const PxU32 twoHandle = 2*handle; + + const ValType boxMax=encodeMax(boxMinMax3D[handle], Axis, mContactDistance[handle]); + + //We always iterate back through the list... + BpHandle CurrentIndex = mListPrev[ThisIndex]; + ValType CurrentValue = BaseEPValues[CurrentIndex]; + + if(CurrentValue > ThisValue) + { + //We're performing some swaps so we need an activity pocket here. This structure allows us to keep track of the range of + //modifications in the sorted lists. Doesn't help when everything's moving but makes a really big difference to reconstituting the + //list when only a small number of things are moving + + PxU32 endIndex = ind; + PxU32 startIndex = ind; + + //const BPValType* PX_RESTRICT box0MinMax0 = &boxMinMax0[twoHandle]; + //const BPValType* PX_RESTRICT box0MinMax1 = &boxMinMax1[twoHandle]; +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + const Bp::FilterGroup::Enum group = asapBoxGroupIds[handle]; +#endif + if(!isMax(ThisData)) + { + do + { + BpHandle CurrentData = BaseEPDatas[CurrentIndex]; + const BpHandle IsMax = isMax(CurrentData); + + #if PERFORM_COMPARISONS + if(IsMax) + { + const BpHandle ownerId=getOwner(CurrentData); + SapBox1D* PX_RESTRICT id1 = asapBoxes + ownerId; + // Our min passed a max => start overlap + + if( + BaseEPValues[id1->mMinMax[0]] < boxMax && + //2D intersection test using up-to-date values + Intersect2D_Handle(boxMinMax0[handle].mMinMax[0], boxMinMax0[handle].mMinMax[1], boxMinMax1[handle].mMinMax[0], boxMinMax1[handle].mMinMax[1], + boxMinMax0[ownerId].mMinMax[0],boxMinMax0[ownerId].mMinMax[1],boxMinMax1[ownerId].mMinMax[0],boxMinMax1[ownerId].mMinMax[1]) + #if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + && groupFiltering(group, asapBoxGroupIds[ownerId], mLUT) + #else + && groupFiltering(group, asapBoxGroupIds[ownerId]) + #endif + #else + && Object!=id1 + #endif + ) + { + if(numPairs==maxNumPairs) + { + const PxU32 newMaxNumPairs=maxNumPairs*2; + pairs = reinterpret_cast(resizeBroadPhasePairArray(maxNumPairs, newMaxNumPairs, mScratchAllocator, pairs)); + maxNumPairs=newMaxNumPairs; + } + PX_ASSERT(numPairs stop overlap + const BpHandle ownerId=getOwner(CurrentData); + +#if 1 + if( +#if BP_SAP_USE_OVERLAP_TEST_ON_REMOVES + Intersect2D_Handle(boxMinMax0[handle].mMinMax[0], boxMinMax0[handle].mMinMax[1], boxMinMax1[handle].mMinMax[0], boxMinMax1[handle].mMinMax[1], + boxMinMax0[ownerId].mMinMax[0],boxMinMax0[ownerId].mMinMax[1],boxMinMax1[ownerId].mMinMax[0],boxMinMax1[ownerId].mMinMax[1]) +#endif +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + && groupFiltering(group, asapBoxGroupIds[ownerId], mLUT) + #else + && groupFiltering(group, asapBoxGroupIds[ownerId]) + #endif +#else + && Object!=id1 +#endif + ) +#endif + { + if(numPairs==maxNumPairs) + { + const PxU32 newMaxNumPairs=maxNumPairs*2; + pairs = reinterpret_cast(resizeBroadPhasePairArray(maxNumPairs, newMaxNumPairs, mScratchAllocator, pairs)); + maxNumPairs=newMaxNumPairs; + } + PX_ASSERT(numPairsmStartIndex) + { + currentPocket--; + } + //If our start index > currentPocket->mEndIndex, then we don't overlap so create a new pocket + if(currentPocket == mActivityPockets || startIndex > (currentPocket->mEndIndex+1)) + { + currentPocket++; + currentPocket->mStartIndex = startIndex; + } + currentPocket->mEndIndex = endIndex; + }// update max + //Get prev and next ptr... + + NextData = BaseEPDatas[++ind]; + PrevData = BaseEPDatas[mListPrev[ind]]; + + }while(!isSentinel(NextData) && !updated[getOwner(NextData)] && updated[getOwner(PrevData)]); + + }// updated aabbs + + pairsSize=numPairs; + pairsCapacity=maxNumPairs; + + + BroadPhaseActivityPocket* pocket = mActivityPockets+1; + + while(pocket <= currentPocket) + { + //PxU32 CurrIndex = mListPrev[pocket->mStartIndex]; + for(PxU32 a = pocket->mStartIndex; a <= pocket->mEndIndex; ++a) + { + mListPrev[a] = BpHandle(a); + } + + //Now copy all the data to the array, updating the remap table + PxU32 CurrIndex = pocket->mStartIndex-1; + for(PxU32 a = pocket->mStartIndex; a <= pocket->mEndIndex; ++a) + { + CurrIndex = mListNext[CurrIndex]; + PxU32 origIndex = CurrIndex; + BpHandle remappedIndex = mListPrev[origIndex]; + + if(origIndex != a) + { + const BpHandle ownerId=getOwner(BaseEPDatas[remappedIndex]); + const BpHandle IsMax = isMax(BaseEPDatas[remappedIndex]); + ValType tmp = BaseEPValues[a]; + BpHandle tmpHandle = BaseEPDatas[a]; + + BaseEPValues[a] = BaseEPValues[remappedIndex]; + BaseEPDatas[a] = BaseEPDatas[remappedIndex]; + + BaseEPValues[remappedIndex] = tmp; + BaseEPDatas[remappedIndex] = tmpHandle; + + mListPrev[remappedIndex] = mListPrev[a]; + //Write back remap index (should be an immediate jump to original index) + mListPrev[mListPrev[a]] = remappedIndex; + asapBoxes[ownerId].mMinMax[IsMax] = BpHandle(a); + } + + } + + for(PxU32 a = pocket->mStartIndex-1; a <= pocket->mEndIndex; ++a) + { + mListPrev[a+1] = BpHandle(a); + mListNext[a] = BpHandle(a+1); + } + pocket++; + } +} + +#if PX_DEBUG + +bool BroadPhaseSap::isSelfOrdered() const +{ + if(0==mBoxesSize) + { + return true; + } + + for(PxU32 Axis=0;Axis<3;Axis++) + { + PxU32 it=1; + PX_ASSERT(mEndPointDatas[Axis]); + while(!isSentinel(mEndPointDatas[Axis][it])) + { + //Test the array is sorted. + const ValType prevVal=mEndPointValues[Axis][it-1]; + const ValType currVal=mEndPointValues[Axis][it]; + if(currValid1) PxBpHandleSwap(id0, id1); +} + +PX_FORCE_INLINE bool DifferentPair(const BroadPhasePair& p, BpHandle id0, BpHandle id1) +{ + return (id0!=p.mVolA) || (id1!=p.mVolB); +} + +PX_FORCE_INLINE int Hash32Bits_1(int key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} + +PX_FORCE_INLINE PxU32 Hash(BpHandle id0, BpHandle id1) +{ + return PxU32(Hash32Bits_1( int(PxU32(id0)|(PxU32(id1)<<16)) )); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +SapPairManager::SapPairManager() : + mHashTable (NULL), + mNext (NULL), + mHashSize (0), + mHashCapacity (0), + mMinAllowedHashCapacity (0), + mActivePairs (NULL), + mActivePairStates (NULL), + mNbActivePairs (0), + mActivePairsCapacity (0), + mMask (0) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +SapPairManager::~SapPairManager() +{ + PX_ASSERT(NULL==mHashTable); + PX_ASSERT(NULL==mNext); + PX_ASSERT(NULL==mActivePairs); + PX_ASSERT(NULL==mActivePairStates); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +void SapPairManager::init(const PxU32 size) +{ + mHashTable=reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16(sizeof(BpHandle)*size), "BpHandle")); + mNext=reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16(sizeof(BpHandle)*size), "BpHandle")); + mActivePairs=reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16(sizeof(BroadPhasePair)*size), "BroadPhasePair")); + mActivePairStates=reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16(sizeof(PxU8)*size), "BroadPhaseContextSap ActivePairStates")); + mHashCapacity=size; + mMinAllowedHashCapacity = size; + mActivePairsCapacity=size; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void SapPairManager::release() +{ + PX_FREE(mHashTable); + PX_FREE(mNext); + PX_FREE(mActivePairs); + PX_FREE(mActivePairStates); + mHashTable = NULL; + mNext = NULL; + mActivePairs = NULL; + mActivePairStates = NULL; + mNext = 0; + mHashSize = 0; + mHashCapacity = 0; + mMinAllowedHashCapacity = 0; + mNbActivePairs = 0; + mActivePairsCapacity = 0; + mMask = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const BroadPhasePair* SapPairManager::FindPair(BpHandle id0, BpHandle id1) const +{ + if(0==mHashSize) return NULL; // Nothing has been allocated yet + + // Order the ids + Sort(id0, id1); + + // Compute hash value for this pair + PxU32 HashValue = Hash(id0, id1) & mMask; + PX_ASSERT(HashValue the pair is persistent + PX_ASSERT(Offset the pair is persistent + PX_ASSERT(Offset= mHashSize) + { + // Get more entries + mHashSize = Ps::nextPowerOfTwo(mNbActivePairs+1); + mMask = mHashSize-1; + + reallocPairs(mHashSize>mHashCapacity); + + // Recompute hash value with new hash size + HashValue = Hash(id0, id1) & mMask; + } + + PX_ASSERT(mNbActivePairsmVolA = id0; // ### CMOVs would be nice here + p->mVolB = id1; + mActivePairStates[mNbActivePairs]=state; + + PX_ASSERT(mNbActivePairsmVolA, Last->mVolB) & mMask; + + // Walk the hash table to fix mNext + PX_ASSERT(LastHashValuemVolA==id0); + PX_ASSERT(P->mVolB==id1); + + RemovePair(id0, id1, HashValue, GetPairIndex(P)); + + shrinkMemory(); + + return true; +} + +bool SapPairManager::RemovePairs(const Cm::BitMap& removedAABBs) +{ + PxU32 i=0; + while(i mMinAllowedHashCapacity) || (mHashSize <= (mHashCapacity >> 2)) || (mHashSize <= (mActivePairsCapacity >> 2))); +} + +void SapPairManager::reallocPairs(const bool allocRequired) +{ + if(allocRequired) + { + PX_FREE(mHashTable); + mHashCapacity=mHashSize; + mActivePairsCapacity=mHashSize; + mHashTable = reinterpret_cast(PX_ALLOC(mHashSize*sizeof(BpHandle), "BpHandle")); + + for(PxU32 i=0;i(PX_ALLOC(mHashSize * sizeof(BroadPhasePair), "BroadPhasePair")); PX_ASSERT(NewPairs); + BpHandle* NewNext = reinterpret_cast(PX_ALLOC(mHashSize * sizeof(BpHandle), "BpHandle")); PX_ASSERT(NewNext); + PxU8* NewPairStates = reinterpret_cast(PX_ALLOC(mHashSize * sizeof(PxU8), "SapPairStates")); PX_ASSERT(NewPairStates); + + // Copy old data if needed + if(mNbActivePairs) + { + PxMemCopy(NewPairs, mActivePairs, mNbActivePairs*sizeof(BroadPhasePair)); + PxMemCopy(NewPairStates, mActivePairStates, mNbActivePairs*sizeof(PxU8)); + } + + // ### check it's actually needed... probably only for pairs whose hash value was cut by the and + // yeah, since Hash(id0, id1) is a constant + // However it might not be needed to recompute them => only less efficient but still ok + for(PxU32 i=0;i only less efficient but still ok + for(PxU32 i=0;i0); + const PxU32 newMaxNumPairs=2*maxNumPairs; + BroadPhasePair* newPairs=reinterpret_cast(PX_ALLOC(sizeof(BroadPhasePair)*newMaxNumPairs, "BroadPhasePair")); + PxMemCopy(newPairs, pairs, sizeof(BroadPhasePair)*maxNumPairs); + PX_FREE(pairs); + pairs=newPairs; + maxNumPairs=newMaxNumPairs; +} + +void ComputeCreatedDeletedPairsLists +(const Bp::FilterGroup::Enum* PX_RESTRICT boxGroups, + const BpHandle* PX_RESTRICT dataArray, const PxU32 dataArraySize, + PxcScratchAllocator* scratchAllocator, + BroadPhasePair*& createdPairsList, PxU32& numCreatedPairs, PxU32& maxNumCreatedPairs, + BroadPhasePair*& deletedPairsList, PxU32& numDeletedPairs, PxU32& maxNumDeletedPairs, + PxU32& numActualDeletedPairs, + SapPairManager& pairManager) +{ +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + PX_UNUSED(boxGroups); +#endif + + for(PxU32 i=0;i(scratchAllocator->alloc(sizeof(BroadPhasePair)*2*maxNumDeletedPairs, true)); + PxMemCopy(newDeletedPairsList, deletedPairsList, sizeof(BroadPhasePair)*maxNumDeletedPairs); + scratchAllocator->free(deletedPairsList); + deletedPairsList = newDeletedPairsList; + maxNumDeletedPairs = 2*maxNumDeletedPairs; + } + + PX_ASSERT(numDeletedPairsmUserData != 0xcdcdcdcd); + deletedPairsList[numDeletedPairs++] = BroadPhasePair(UP->mVolA,UP->mVolB/*, ID*/); + } + } + else + { + pairManager.ClearInArray(UP); + // Add => already there... Might want to create user data, though + if(pairManager.IsNew(UP)) + { +#if !BP_SAP_TEST_GROUP_ID_CREATEUPDATE + if(groupFiltering(boxGroups[UP->mVolA], boxGroups[UP->mVolB])) +#endif + { + if(numCreatedPairs==maxNumCreatedPairs) + { + BroadPhasePair* newCreatedPairsList = reinterpret_cast(scratchAllocator->alloc(sizeof(BroadPhasePair)*2*maxNumCreatedPairs, true)); + PxMemCopy(newCreatedPairsList, createdPairsList, sizeof(BroadPhasePair)*maxNumCreatedPairs); + scratchAllocator->free(createdPairsList); + createdPairsList = newCreatedPairsList; + maxNumCreatedPairs = 2*maxNumCreatedPairs; + } + + PX_ASSERT(numCreatedPairsmVolA,UP->mVolB/*, ID*/); + } + pairManager.ClearNew(UP); + } + } + } + + //Record pairs that are to be deleted because they were simultaneously created and removed + //from different axis sorts. + numActualDeletedPairs=numDeletedPairs; + for(PxU32 i=0;i(scratchAllocator->alloc(sizeof(BroadPhasePair)*2*maxNumDeletedPairs, true)); + PxMemCopy(newDeletedPairsList, deletedPairsList, sizeof(BroadPhasePair)*maxNumDeletedPairs); + scratchAllocator->free(deletedPairsList); + deletedPairsList = newDeletedPairsList; + maxNumDeletedPairs = 2*maxNumDeletedPairs; + } + + PX_ASSERT(numActualDeletedPairs<=maxNumDeletedPairs); + deletedPairsList[numActualDeletedPairs++] = BroadPhasePair(UP->mVolA,UP->mVolB/*, ID*/); //KS - should we even get here???? + } + } + +// // #### try batch removal here +// for(PxU32 i=0;i i && boxGroups[deletedPairsList[numDeletedPairs-1].mVolA] == boxGroups[deletedPairsList[numDeletedPairs-1].mVolB]) + { + numDeletedPairs--; + } + deletedPairsList[i]=deletedPairsList[numDeletedPairs-1]; + numDeletedPairs--; + } + } +#endif +} + +void DeletePairsLists(const PxU32 numActualDeletedPairs, BroadPhasePair* deletedPairsList, SapPairManager& pairManager) +{ + // #### try batch removal here + for(PxU32 i=0;i + static PxU32 gNbIter = 0; + static PxU32 gNbTests = 0; + static PxU32 gNbPairs = 0; + #define START_STATS gNbIter = gNbTests = gNbPairs = 0; + #define INCREASE_STATS_NB_ITER gNbIter++; + #define INCREASE_STATS_NB_TESTS gNbTests++; + #define INCREASE_STATS_NB_PAIRS gNbPairs++; + #define DUMP_STATS printf("%d %d %d\n", gNbIter, gNbTests, gNbPairs); +#else + #define START_STATS + #define INCREASE_STATS_NB_ITER + #define INCREASE_STATS_NB_TESTS + #define INCREASE_STATS_NB_PAIRS + #define DUMP_STATS +#endif + +void DataArray::Resize(PxcScratchAllocator* scratchAllocator) +{ + BpHandle* newDataArray = reinterpret_cast(scratchAllocator->alloc(sizeof(BpHandle)*mCapacity*2, true)); + PxMemCopy(newDataArray, mData, mCapacity*sizeof(BpHandle)); + scratchAllocator->free(mData); + mData = newDataArray; + mCapacity *= 2; +} + +static PX_FORCE_INLINE int intersect2D(const BoxYZ& a, const BoxYZ& b) +{ + const bool b0 = b.mMaxY < a.mMinY; + const bool b1 = a.mMaxY < b.mMinY; + const bool b2 = b.mMaxZ < a.mMinZ; + const bool b3 = a.mMaxZ < b.mMinZ; +// const bool b4 = b0 || b1 || b2 || b3; + const bool b4 = b0 | b1 | b2 | b3; + return !b4; +} + +void addPair(const BpHandle id0, const BpHandle id1, PxcScratchAllocator* scratchAllocator, SapPairManager& pairManager, DataArray& dataArray) +{ + const BroadPhasePair* UP = reinterpret_cast(pairManager.AddPair(id0, id1, SapPairManager::PAIR_UNKNOWN)); + + //If the hash table has reached its limit then we're unable to add a new pair. + if(NULL==UP) + return; + + PX_ASSERT(UP); + if(pairManager.IsUnknown(UP)) + { + pairManager.ClearState(UP); + pairManager.SetInArray(UP); + dataArray.AddData(pairManager.GetPairIndex(UP), scratchAllocator); + pairManager.SetNew(UP); + } + pairManager.ClearRemoved(UP); +} + +void removePair(BpHandle id0, BpHandle id1, PxcScratchAllocator* scratchAllocator, SapPairManager& pairManager, DataArray& dataArray) +{ + const BroadPhasePair* UP = reinterpret_cast(pairManager.FindPair(id0, id1)); + if(UP) + { + if(!pairManager.IsInArray(UP)) + { + pairManager.SetInArray(UP); + dataArray.AddData(pairManager.GetPairIndex(UP), scratchAllocator); + } + pairManager.SetRemoved(UP); + } +} + +struct AddPairParams +{ + AddPairParams(const PxU32* remap0, const PxU32* remap1, PxcScratchAllocator* alloc, SapPairManager* pm, DataArray* da) : + mRemap0 (remap0), + mRemap1 (remap1), + mScratchAllocator (alloc), + mPairManager (pm), + mDataArray (da) + { + } + + const PxU32* mRemap0; + const PxU32* mRemap1; + PxcScratchAllocator* mScratchAllocator; + SapPairManager* mPairManager; + DataArray* mDataArray; +}; + +static void addPair(const AddPairParams* PX_RESTRICT params, const BpHandle id0_, const BpHandle id1_) +{ + SapPairManager& pairManager = *params->mPairManager; + + const BroadPhasePair* UP = reinterpret_cast(pairManager.AddPair(params->mRemap0[id0_], params->mRemap1[id1_], SapPairManager::PAIR_UNKNOWN)); + + //If the hash table has reached its limit then we're unable to add a new pair. + if(NULL==UP) + return; + + PX_ASSERT(UP); + if(pairManager.IsUnknown(UP)) + { + pairManager.ClearState(UP); + pairManager.SetInArray(UP); + params->mDataArray->AddData(pairManager.GetPairIndex(UP), params->mScratchAllocator); + pairManager.SetNew(UP); + } + pairManager.ClearRemoved(UP); +} + +// PT: TODO: use SIMD + +AuxData::AuxData(PxU32 nb, const SapBox1D*const* PX_RESTRICT boxes, const BpHandle* PX_RESTRICT indicesSorted, const Bp::FilterGroup::Enum* PX_RESTRICT groupIds) +{ + // PT: TODO: use scratch allocator / etc + BoxX* PX_RESTRICT boxX = reinterpret_cast(PX_ALLOC(sizeof(BoxX)*(nb+1), PX_DEBUG_EXP("mBoxX"))); + BoxYZ* PX_RESTRICT boxYZ = reinterpret_cast(PX_ALLOC(sizeof(BoxYZ)*nb, PX_DEBUG_EXP("mBoxYZ"))); + Bp::FilterGroup::Enum* PX_RESTRICT groups = reinterpret_cast(PX_ALLOC(sizeof(Bp::FilterGroup::Enum)*nb, PX_DEBUG_EXP("mGroups"))); + PxU32* PX_RESTRICT remap = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nb, PX_DEBUG_EXP("mRemap"))); + + mBoxX = boxX; + mBoxYZ = boxYZ; + mGroups = groups; + mRemap = remap; + mNb = nb; + + const PxU32 axis0 = 0; + const PxU32 axis1 = 2; + const PxU32 axis2 = 1; + + const SapBox1D* PX_RESTRICT boxes0 = boxes[axis0]; + const SapBox1D* PX_RESTRICT boxes1 = boxes[axis1]; + const SapBox1D* PX_RESTRICT boxes2 = boxes[axis2]; + + for(PxU32 i=0;imNb; + if(!nb) + return; + + DataArray da(dataArray, dataArraySize, dataArrayCapacity); + + START_STATS + { + BoxX* boxX = auxData->mBoxX; + BoxYZ* boxYZ = auxData->mBoxYZ; + Bp::FilterGroup::Enum* groups = auxData->mGroups; + PxU32* remap = auxData->mRemap; + + AddPairParams params(remap, remap, scratchAllocator, &pairManager, &da); + + PxU32 runningIndex = 0; + PxU32 index0 = 0; + + while(runningIndex(&boxYZ[index0].mMinY)); + b = _mm_shuffle_epi32(b, 78); + const __m128i a = _mm_loadu_si128(reinterpret_cast(&boxYZ[index1].mMinY)); + const __m128i d = _mm_cmpgt_epi32(a, b); + const int mask = _mm_movemask_epi8(d); + if(mask==0x0000ff00)*/ + { + INCREASE_STATS_NB_PAIRS + addPair(¶ms, index0, index1); + } + } + index1++; + } + index0++; + } + } + DUMP_STATS + + dataArray = da.mData; + dataArraySize = da.mSize; + dataArrayCapacity = da.mCapacity; +} + +template +static void bipartitePruning( + const PxU32 nb0, const BoxX* PX_RESTRICT boxX0, const BoxYZ* PX_RESTRICT boxYZ0, const PxU32* PX_RESTRICT remap0, const Bp::FilterGroup::Enum* PX_RESTRICT groups0, + const PxU32 nb1, const BoxX* PX_RESTRICT boxX1, const BoxYZ* PX_RESTRICT boxYZ1, const PxU32* PX_RESTRICT remap1, const Bp::FilterGroup::Enum* PX_RESTRICT groups1, +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* lut, +#endif + PxcScratchAllocator* scratchAllocator, SapPairManager& pairManager, DataArray& dataArray + ) +{ + AddPairParams params(remap0, remap1, scratchAllocator, &pairManager, &dataArray); + + PxU32 runningIndex = 0; + PxU32 index0 = 0; + + while(runningIndexmNb; + const PxU32 nb1 = auxData1->mNb; + + if(!nb0 || !nb1) + return; + + DataArray da(dataArray, dataArraySize, dataArrayCapacity); + + START_STATS + { + const BoxX* boxX0 = auxData0->mBoxX; + const BoxYZ* boxYZ0 = auxData0->mBoxYZ; + const Bp::FilterGroup::Enum* groups0 = auxData0->mGroups; + const PxU32* remap0 = auxData0->mRemap; + + const BoxX* boxX1 = auxData1->mBoxX; + const BoxYZ* boxYZ1 = auxData1->mBoxYZ; + const Bp::FilterGroup::Enum* groups1 = auxData1->mGroups; + const PxU32* remap1 = auxData1->mRemap; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + bipartitePruning<0>(nb0, boxX0, boxYZ0, remap0, groups0, nb1, boxX1, boxYZ1, remap1, groups1, lut, scratchAllocator, pairManager, da); + bipartitePruning<1>(nb1, boxX1, boxYZ1, remap1, groups1, nb0, boxX0, boxYZ0, remap0, groups0, lut, scratchAllocator, pairManager, da); +#else + bipartitePruning<0>(nb0, boxX0, boxYZ0, remap0, groups0, nb1, boxX1, boxYZ1, remap1, groups1, scratchAllocator, pairManager, da); + bipartitePruning<1>(nb1, boxX1, boxYZ1, remap1, groups1, nb0, boxX0, boxYZ0, remap0, groups0, scratchAllocator, pairManager, da); +#endif + } + DUMP_STATS + + dataArray = da.mData; + dataArraySize = da.mSize; + dataArrayCapacity = da.mCapacity; +} + +} //namespace Bp + +} //namespace physx + diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h new file mode 100644 index 000000000..30c2366fd --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h @@ -0,0 +1,285 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_SAP_AUX_H +#define BP_BROADPHASE_SAP_AUX_H + +#include "foundation/PxAssert.h" +#include "CmPhysXCommon.h" +#include "PsIntrinsics.h" +#include "PsUserAllocated.h" +#include "BpBroadPhase.h" +#include "BpBroadPhaseUpdate.h" +#include "CmBitMap.h" +#include "GuAxes.h" +#include "PxcScratchAllocator.h" + +namespace physx +{ +namespace Bp +{ +#define NUM_SENTINELS 2 + +#define BP_SAP_USE_PREFETCH 1//prefetch in batchUpdate + +#define BP_SAP_USE_OVERLAP_TEST_ON_REMOVES 1// "Useless" but faster overall because seriously reduces number of calls (from ~10000 to ~3 sometimes!) + +//Set 1 to test for group ids in batchCreate/batchUpdate so we can avoid group id test in ComputeCreatedDeletedPairsLists +//Set 0 to neglect group id test in batchCreate/batchUpdate and delay test until ComputeCreatedDeletedPairsLists +#define BP_SAP_TEST_GROUP_ID_CREATEUPDATE 1 + +#define MAX_BP_HANDLE 0x3fffffff +#define PX_REMOVED_BP_HANDLE 0x3ffffffd +#define MAX_BP_PAIRS_MESSAGE "Only 4294967296 broadphase pairs are supported. This limit has been exceeded and some pairs will be dropped \n" + +PX_FORCE_INLINE void setMinSentinel(ValType& v, BpHandle& d) +{ + v = 0x00000000;//0x00800000; //0x00800000 is -FLT_MAX but setting it to 0 means we don't crash when we get a value outside the float range. + d = (BP_INVALID_BP_HANDLE & ~1); +} + +PX_FORCE_INLINE void setMaxSentinel(ValType& v, BpHandle& d) +{ + v = 0xffffffff;//0xff7fffff; //0xff7fffff is +FLT_MAX but setting it to 0xffffffff means we don't crash when we get a value outside the float range. + d = BP_INVALID_BP_HANDLE; +} + +PX_FORCE_INLINE BpHandle setData(PxU32 owner_box_id, const bool is_max) +{ + BpHandle d = BpHandle(owner_box_id<<1); + if(is_max) d |= 1; + return d; +} + +PX_FORCE_INLINE bool isSentinel(const BpHandle& d) +{ + return (d&~1)==(BP_INVALID_BP_HANDLE & ~1); +} + +PX_FORCE_INLINE BpHandle isMax(const BpHandle& d) +{ + return BpHandle(d & 1); +} + +PX_FORCE_INLINE BpHandle getOwner(const BpHandle& d) +{ + return BpHandle(d>>1); +} + +class SapBox1D +{ +public: + + PX_FORCE_INLINE SapBox1D() {} + PX_FORCE_INLINE ~SapBox1D() {} + + BpHandle mMinMax[2];//mMinMax[0]=min, mMinMax[1]=max +}; + +class SapPairManager +{ +public: + SapPairManager(); + ~SapPairManager(); + + void init(const PxU32 size); + void release(); + + void shrinkMemory(); + + const BroadPhasePair* AddPair (BpHandle id0, BpHandle id1, const PxU8 state); + bool RemovePair (BpHandle id0, BpHandle id1); + bool RemovePairs (const Cm::BitMap& removedAABBs); + const BroadPhasePair* FindPair (BpHandle id0, BpHandle id1) const; + + PX_FORCE_INLINE PxU32 GetPairIndex(const BroadPhasePair* PX_RESTRICT pair) const + { + return (PxU32((size_t(pair) - size_t(mActivePairs)))/sizeof(BroadPhasePair)); + } + + BpHandle* mHashTable; + BpHandle* mNext; + PxU32 mHashSize; + PxU32 mHashCapacity; + PxU32 mMinAllowedHashCapacity; + BroadPhasePair* mActivePairs; + PxU8* mActivePairStates; + PxU32 mNbActivePairs; + PxU32 mActivePairsCapacity; + PxU32 mMask; + + BroadPhasePair* FindPair (BpHandle id0, BpHandle id1, PxU32 hash_value) const; + void RemovePair (BpHandle id0, BpHandle id1, PxU32 hash_value, PxU32 pair_index); + void reallocPairs(const bool allocRequired); + + enum + { + PAIR_INARRAY=1, + PAIR_REMOVED=2, + PAIR_NEW=4, + PAIR_UNKNOWN=8 + }; + + PX_FORCE_INLINE bool IsInArray(const BroadPhasePair* PX_RESTRICT pair) const + { + const PxU8 state=mActivePairStates[pair-mActivePairs]; + return state & PAIR_INARRAY ? true : false; + } + PX_FORCE_INLINE bool IsRemoved(const BroadPhasePair* PX_RESTRICT pair) const + { + const PxU8 state=mActivePairStates[pair-mActivePairs]; + return state & PAIR_REMOVED ? true : false; + } + PX_FORCE_INLINE bool IsNew(const BroadPhasePair* PX_RESTRICT pair) const + { + const PxU8 state=mActivePairStates[pair-mActivePairs]; + return state & PAIR_NEW ? true : false; + } + PX_FORCE_INLINE bool IsUnknown(const BroadPhasePair* PX_RESTRICT pair) const + { + const PxU8 state=mActivePairStates[pair-mActivePairs]; + return state & PAIR_UNKNOWN ? true : false; + } + + PX_FORCE_INLINE void ClearState(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs]=0; + } + + PX_FORCE_INLINE void SetInArray(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] |= PAIR_INARRAY; + } + PX_FORCE_INLINE void SetRemoved(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] |= PAIR_REMOVED; + } + PX_FORCE_INLINE void SetNew(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] |= PAIR_NEW; + } + PX_FORCE_INLINE void ClearInArray(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] &= ~PAIR_INARRAY; + } + PX_FORCE_INLINE void ClearRemoved(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] &= ~PAIR_REMOVED; + } + PX_FORCE_INLINE void ClearNew(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] &= ~PAIR_NEW; + } +}; + +struct DataArray +{ + DataArray(BpHandle* data, PxU32 size, PxU32 capacity) : mData(data), mSize(size), mCapacity(capacity) {} + + BpHandle* mData; + PxU32 mSize; + PxU32 mCapacity; + + PX_NOINLINE void Resize(PxcScratchAllocator* scratchAllocator); + + PX_FORCE_INLINE void AddData(const PxU32 data, PxcScratchAllocator* scratchAllocator) + { + if(mSize==mCapacity) + Resize(scratchAllocator); + + PX_ASSERT(mSize cDir1Min && cDir1Max > bDir1Min && + bDir2Max > cDir2Min && cDir2Max > bDir2Min); +} + +} //namespace Bp + +} //namespace physx + +#endif //BP_BROADPHASE_SAP_AUX_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp new file mode 100644 index 000000000..0fef90132 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp @@ -0,0 +1,246 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "BpBroadPhaseShared.h" +#include "foundation/PxMemory.h" +#include "PsBitUtils.h" + +using namespace physx; +using namespace Bp; + +#define MBP_ALLOC(x) PX_ALLOC(x, "MBP") +#define MBP_FREE(x) if(x) PX_FREE_AND_RESET(x) + +static PX_FORCE_INLINE void storeDwords(PxU32* dest, PxU32 nb, PxU32 value) +{ + while(nb--) + *dest++ = value; +} + +/////////////////////////////////////////////////////////////////////////////// + +PairManagerData::PairManagerData() : + mHashSize (0), + mMask (0), + mNbActivePairs (0), + mHashTable (NULL), + mNext (NULL), + mActivePairs (NULL), + mReservedMemory (0) +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +PairManagerData::~PairManagerData() +{ + purge(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PairManagerData::purge() +{ + MBP_FREE(mNext); + MBP_FREE(mActivePairs); + MBP_FREE(mHashTable); + mHashSize = 0; + mMask = 0; + mNbActivePairs = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PairManagerData::reallocPairs() +{ + MBP_FREE(mHashTable); + mHashTable = reinterpret_cast(MBP_ALLOC(mHashSize*sizeof(PxU32))); + storeDwords(mHashTable, mHashSize, INVALID_ID); + + // Get some bytes for new entries + InternalPair* newPairs = reinterpret_cast(MBP_ALLOC(mHashSize * sizeof(InternalPair))); PX_ASSERT(newPairs); + PxU32* newNext = reinterpret_cast(MBP_ALLOC(mHashSize * sizeof(PxU32))); PX_ASSERT(newNext); + + // Copy old data if needed + if(mNbActivePairs) + PxMemCopy(newPairs, mActivePairs, mNbActivePairs*sizeof(InternalPair)); + // ### check it's actually needed... probably only for pairs whose hash value was cut by the and + // yeah, since hash(id0, id1) is a constant + // However it might not be needed to recompute them => only less efficient but still ok + for(PxU32 i=0;igetId0(), last->getId1()) & mMask; + + // Walk the hash table to fix mNext + PxU32 offset = mHashTable[lastHashValue]; + PX_ASSERT(offset!=INVALID_ID); + + PxU32 previous=INVALID_ID; + while(offset!=lastPairIndex) + { + previous = offset; + offset = mNext[offset]; + } + + // Let us go/jump us + if(previous!=INVALID_ID) + { + PX_ASSERT(mNext[previous]==lastPairIndex); + mNext[previous] = mNext[lastPairIndex]; + } + // else we were the first + else mHashTable[lastHashValue] = mNext[lastPairIndex]; + // we're now free to reuse mNext[lastPairIndex] without breaking the list + +#if PX_DEBUG + mNext[lastPairIndex]=INVALID_ID; +#endif + + // Don't invalidate entry since we're going to shrink the array + + // 2) Re-insert in free slot + mActivePairs[pairIndex] = mActivePairs[lastPairIndex]; +#if PX_DEBUG + PX_ASSERT(mNext[pairIndex]==INVALID_ID); +#endif + mNext[pairIndex] = mHashTable[lastHashValue]; + mHashTable[lastHashValue] = pairIndex; + + mNbActivePairs--; + } + } +} + diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h new file mode 100644 index 000000000..013f7c544 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h @@ -0,0 +1,243 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_SHARED_H +#define BP_BROADPHASE_SHARED_H + +#include "BpBroadPhaseUpdate.h" +#include "PsUserAllocated.h" +#include "PsHash.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Bp +{ + #define INVALID_ID 0xffffffff + #define INVALID_USER_ID 0xffffffff + + struct InternalPair : public Ps::UserAllocated + { + PX_FORCE_INLINE PxU32 getId0() const { return id0_isNew & ~PX_SIGN_BITMASK; } + PX_FORCE_INLINE PxU32 getId1() const { return id1_isUpdated & ~PX_SIGN_BITMASK; } + + PX_FORCE_INLINE PxU32 isNew() const { return id0_isNew & PX_SIGN_BITMASK; } + PX_FORCE_INLINE PxU32 isUpdated() const { return id1_isUpdated & PX_SIGN_BITMASK; } + + PX_FORCE_INLINE void setNewPair(PxU32 id0, PxU32 id1) + { + PX_ASSERT(!(id0 & PX_SIGN_BITMASK)); + PX_ASSERT(!(id1 & PX_SIGN_BITMASK)); + id0_isNew = id0 | PX_SIGN_BITMASK; + id1_isUpdated = id1; + } + PX_FORCE_INLINE void setUpdated() { id1_isUpdated |= PX_SIGN_BITMASK; } + PX_FORCE_INLINE void clearUpdated() { id1_isUpdated &= ~PX_SIGN_BITMASK; } + PX_FORCE_INLINE void clearNew() { id0_isNew &= ~PX_SIGN_BITMASK; } + + protected: + PxU32 id0_isNew; + PxU32 id1_isUpdated; + }; + + PX_FORCE_INLINE bool differentPair(const InternalPair& p, PxU32 id0, PxU32 id1) { return (id0!=p.getId0()) || (id1!=p.getId1()); } + PX_FORCE_INLINE PxU32 hash(PxU32 id0, PxU32 id1) { return PxU32(Ps::hash( (id0&0xffff)|(id1<<16)) ); } + PX_FORCE_INLINE void sort(PxU32& id0, PxU32& id1) { if(id0>id1) Ps::swap(id0, id1); } + + class PairManagerData + { + public: + PairManagerData(); + ~PairManagerData(); + + PX_FORCE_INLINE PxU32 getPairIndex(const InternalPair* pair) const + { + return (PxU32((size_t(pair) - size_t(mActivePairs)))/sizeof(InternalPair)); + } + + // Internal version saving hash computation + PX_FORCE_INLINE InternalPair* findPair(PxU32 id0, PxU32 id1, PxU32 hashValue) const + { + if(!mHashTable) + return NULL; // Nothing has been allocated yet + + InternalPair* PX_RESTRICT activePairs = mActivePairs; + const PxU32* PX_RESTRICT next = mNext; + + // Look for it in the table + PxU32 offset = mHashTable[hashValue]; + while(offset!=INVALID_ID && differentPair(activePairs[offset], id0, id1)) + { + PX_ASSERT(activePairs[offset].getId0()!=INVALID_USER_ID); + offset = next[offset]; // Better to have a separate array for this + } + if(offset==INVALID_ID) + return NULL; + PX_ASSERT(offset the pair is persistent + + return &activePairs[offset]; + } + + PX_FORCE_INLINE InternalPair* addPairInternal(PxU32 id0, PxU32 id1) + { + // Order the ids + sort(id0, id1); + + const PxU32 fullHashValue = hash(id0, id1); + PxU32 hashValue = fullHashValue & mMask; + + { + InternalPair* PX_RESTRICT p = findPair(id0, id1, hashValue); + if(p) + { + p->setUpdated(); + return p; // Persistent pair + } + } + + // This is a new pair + if(mNbActivePairs >= mHashSize) + hashValue = growPairs(fullHashValue); + + const PxU32 pairIndex = mNbActivePairs++; + + InternalPair* PX_RESTRICT p = &mActivePairs[pairIndex]; + p->setNewPair(id0, id1); + mNext[pairIndex] = mHashTable[hashValue]; + mHashTable[hashValue] = pairIndex; + return p; + } + + PxU32 mHashSize; + PxU32 mMask; + PxU32 mNbActivePairs; + PxU32* mHashTable; + PxU32* mNext; + InternalPair* mActivePairs; + PxU32 mReservedMemory; + + void purge(); + void reallocPairs(); + void shrinkMemory(); + void reserveMemory(PxU32 memSize); + PX_NOINLINE PxU32 growPairs(PxU32 fullHashValue); + void removePair(PxU32 id0, PxU32 id1, PxU32 hashValue, PxU32 pairIndex); + }; + + struct AABB_Xi : public Ps::UserAllocated + { + PX_FORCE_INLINE AABB_Xi() {} + PX_FORCE_INLINE ~AABB_Xi() {} + + PX_FORCE_INLINE void initFromFloats(const void* PX_RESTRICT minX, const void* PX_RESTRICT maxX) + { + mMinX = encodeFloat(*reinterpret_cast(minX)); + mMaxX = encodeFloat(*reinterpret_cast(maxX)); + } + + PX_FORCE_INLINE void initFromPxVec4(const PxVec4& min, const PxVec4& max) + { + initFromFloats(&min.x, &max.x); + } + + PX_FORCE_INLINE void operator = (const AABB_Xi& box) + { + mMinX = box.mMinX; + mMaxX = box.mMaxX; + } + + PX_FORCE_INLINE void initSentinel() + { + mMinX = 0xffffffff; + } + + PX_FORCE_INLINE bool isSentinel() const + { + return mMinX == 0xffffffff; + } + + PxU32 mMinX; + PxU32 mMaxX; + }; + + struct AABB_YZn : public Ps::UserAllocated + { + PX_FORCE_INLINE AABB_YZn() {} + PX_FORCE_INLINE ~AABB_YZn() {} + + PX_FORCE_INLINE void initFromPxVec4(const PxVec4& min, const PxVec4& max) + { + mMinY = -min.y; + mMinZ = -min.z; + mMaxY = max.y; + mMaxZ = max.z; + } + + PX_FORCE_INLINE void operator = (const AABB_YZn& box) + { + using namespace physx::shdfnd::aos; + V4StoreA(V4LoadA(&box.mMinY), &mMinY); + } + + float mMinY; + float mMinZ; + float mMaxY; + float mMaxZ; + }; + + struct AABB_YZr : public Ps::UserAllocated + { + PX_FORCE_INLINE AABB_YZr() {} + PX_FORCE_INLINE ~AABB_YZr() {} + + PX_FORCE_INLINE void initFromPxVec4(const PxVec4& min, const PxVec4& max) + { + mMinY = min.y; + mMinZ = min.z; + mMaxY = max.y; + mMaxZ = max.z; + } + + PX_FORCE_INLINE void operator = (const AABB_YZr& box) + { + using namespace physx::shdfnd::aos; + V4StoreA(V4LoadA(&box.mMinY), &mMinY); + } + + float mMinY; + float mMinZ; + float mMaxY; + float mMaxZ; + }; + +} //namespace Bp +} //namespace physx + +#endif // BP_BROADPHASE_SHARED_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp new file mode 100644 index 000000000..5504ce468 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp @@ -0,0 +1,29 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h new file mode 100644 index 000000000..21f518516 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h @@ -0,0 +1,105 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_MBP_TASKS_H +#define BP_MBP_TASKS_H + +#include "PsUserAllocated.h" +#include "CmTask.h" + +namespace physx +{ + class PxcScratchAllocator; + + namespace Bp + { + class BroadPhaseMBP; + } + +#define MBP_USE_SCRATCHPAD + + class MBPTask : public Cm::Task, public shdfnd::UserAllocated + { + public: + MBPTask(PxU64 contextId) : Cm::Task(contextId), mMBP(NULL), mNumCpuTasks(0) {} + + PX_FORCE_INLINE void set(Bp::BroadPhaseMBP* mbp, PxcScratchAllocator* sa, PxU32 numCpuTasks) + { + mMBP = mbp; + mScratchAllocator = sa; + mNumCpuTasks = numCpuTasks; + } + protected: + Bp::BroadPhaseMBP* mMBP; + PxU32 mNumCpuTasks; + PxcScratchAllocator* mScratchAllocator; + private: + MBPTask& operator=(const MBPTask&); + }; + + // PT: this is the main 'update' task doing the actual box pruning work. + class MBPUpdateWorkTask : public MBPTask + { + public: + MBPUpdateWorkTask(PxU64 contextId) : MBPTask(contextId) {} + ~MBPUpdateWorkTask() {} + // PxBaseTask + virtual const char* getName() const { return "BpMBP.updateWork"; } + //~PxBaseTask + + // Cm::Task + virtual void runInternal(); + //~Cm::Task + + private: + MBPUpdateWorkTask& operator=(const MBPUpdateWorkTask&); + }; + + // PT: this task runs after MBPUpdateWorkTask. This is where MBP_PairManager::removeMarkedPairs is called, to finalize + // the work and come up with created/removed lists. This is single-threaded. + class MBPPostUpdateWorkTask : public MBPTask + { + public: + MBPPostUpdateWorkTask(PxU64 contextId) : MBPTask(contextId) {} + ~MBPPostUpdateWorkTask() {} + // PxBaseTask + virtual const char* getName() const { return "BpMBP.postUpdateWork"; } + //~PxBaseTask + + // Cm::Task + virtual void runInternal(); + //~Cm::Task + + private: + MBPPostUpdateWorkTask& operator=(const MBPPostUpdateWorkTask&); + }; + +} //namespace physx + +#endif // BP_MBP_TASKS_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp new file mode 100644 index 000000000..2735614b6 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "BpSAPTasks.h" +#include "BpBroadPhaseSap.h" +#include "PsTime.h" + +namespace physx +{ + +namespace Bp +{ + +/////////////////////////////////////////////////////////////////////////////// + + +// #define DUMP_TOTAL_SAP_TIME +// 256 convex stacks: from ~13000 down to ~2000 +// pot pourri box: from ~4000 to ~700 +// boxes: ~3400 to ~4000 + +#ifdef DUMP_TOTAL_SAP_TIME + static PxU64 gStartTime = shdfnd::Time::getCurrentCounterValue(); +#endif + +void SapUpdateWorkTask::runInternal() +{ + mSAP->update(); +} + +void SapPostUpdateWorkTask::runInternal() +{ + mSAP->postUpdate(); +#ifdef DUMP_TOTAL_SAP_TIME + PxU64 endTime = shdfnd::Time::getCurrentCounterValue(); + printf("SAP Time: %" PX_PRIu64 "\n", endTime - gStartTime); +#endif +} + +} //namespace Bp + +} //namespace physx diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h new file mode 100644 index 000000000..f9b0523b4 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_SAP_TASKS_H +#define BP_SAP_TASKS_H + +#include "CmTask.h" + +namespace physx +{ + +namespace Bp +{ + + class BroadPhaseSap; + + class SapUpdateWorkTask: public Cm::Task + { + public: + + SapUpdateWorkTask(PxU64 contextId) : Cm::Task(contextId) + { + } + + void setBroadPhase(BroadPhaseSap* sap) + { + mSAP = sap; + } + + void set(const PxU32 numCpuTasks) + { + mNumCpuTasks = numCpuTasks; + } + + virtual void runInternal(); + + virtual const char* getName() const { return "BpSAP.updateWork"; } + + private: + + BroadPhaseSap* mSAP; + PxU32 mNumCpuTasks; + }; + + class SapPostUpdateWorkTask: public Cm::Task + { + public: + + SapPostUpdateWorkTask(PxU64 contextId) : Cm::Task(contextId) + { + } + + void setBroadPhase(BroadPhaseSap* sap) + { + mSAP = sap; + } + + void set(const PxU32 numCpuTasks) + { + mNumCpuTasks=numCpuTasks; + } + + virtual void runInternal(); + + virtual const char* getName() const { return "BpSAP.postUpdateWork"; } + + private: + + BroadPhaseSap* mSAP; + PxU32 mNumCpuTasks; + }; + +} //namespace Bp + +} //namespace physx + +#endif // BP_SAP_TASKS_H diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h new file mode 100644 index 000000000..e653067f2 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h @@ -0,0 +1,284 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_ARTICULATION_H +#define PXD_ARTICULATION_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsVecMath.h" +#include "CmUtils.h" +#include "CmSpatialVector.h" +#include "PxArticulationJoint.h" +#include "DyVArticulation.h" + +namespace physx +{ + class PxConstraintAllocator; + + +#define DY_DEBUG_ARTICULATION 0 + +namespace Dy +{ + struct FsInertia; + struct FsData; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +PX_ALIGN_PREFIX(64) + +class Articulation : public ArticulationV +{ +public: + // public interface + + Articulation(Sc::ArticulationSim*); + ~Articulation(); + + virtual bool resize(const PxU32 linkCount); + + virtual void onUpdateSolverDesc() + { + PxMemZero(mExternalLoads.begin(), sizeof(Ps::aos::Mat33V) * mExternalLoads.size()); + PxMemZero(mInternalLoads.begin(), sizeof(Ps::aos::Mat33V) * mExternalLoads.size()); + } + + FsData* getFsDataPtr() const { return reinterpret_cast(mFsDataBytes.begin()); } + //void setFsDataPtr(FsData* data) { mFsData = data; } + + // get data sizes for allocation at higher levels + virtual void getDataSizes(PxU32 linkCount, + PxU32 &solverDataSize, + PxU32& totalSize, + PxU32& scratchSize); + + virtual void getImpulseResponse( + PxU32 linkID, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaV) const; + + virtual void getImpulseSelfResponse( + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1) const; + + virtual Cm::SpatialVectorV getLinkVelocity(const PxU32 linkID) const; + + virtual Cm::SpatialVectorV getLinkMotionVector(const PxU32 linkID) const; + + //this is called by island gen to determine whether the articulation should be awake or sleep + virtual Cm::SpatialVector getMotionVelocity(const PxU32 linkID) const; + + virtual PxReal getLinkMaxPenBias(const PxU32 linkID) const; + + + static PxU32 computeUnconstrainedVelocities( + const ArticulationSolverDesc& desc, + PxReal dt, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, PxU64 contextID, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + static void computeUnconstrainedVelocitiesTGS( + const ArticulationSolverDesc& desc, + PxReal dt, const PxVec3& gravity, + PxU64 contextID, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + static PxU32 setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* /*Z*/); + + static void saveVelocity(const ArticulationSolverDesc& d, Cm::SpatialVectorF* deltaV); + + static void saveVelocityTGS(const ArticulationSolverDesc& d, PxReal invDtF32); + + static void updateBodies(const ArticulationSolverDesc& desc, PxReal dt); + + static void recordDeltaMotion(const ArticulationSolverDesc &desc, const PxReal dt, Cm::SpatialVectorF* deltaV); + + static void deltaMotionToMotionVelocity(const ArticulationSolverDesc& desc, PxReal invDt); + + virtual void pxcFsApplyImpulse(PxU32 linkID, Ps::aos::Vec3V linear, + Ps::aos::Vec3V angular, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + virtual void pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, + const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, + const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + virtual void solveInternalConstraints(const PxReal dt, const PxReal invDt, Cm::SpatialVectorF* impulses, Cm::SpatialVectorF* DeltaV, + bool velIteration); + + virtual Cm::SpatialVectorV pxcFsGetVelocity(PxU32 linkID); + virtual void pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1); + + virtual Cm::SpatialVectorV pxcFsGetVelocityTGS(PxU32 linkID) { return Articulation::pxcFsGetVelocity(linkID); } + + virtual const PxTransform& getCurrentTransform(PxU32 linkID)const + { + return mPose[linkID]; + } + + virtual const PxQuat& getDeltaQ(PxU32 linkID) const + { + return mDeltaQ[linkID]; + } + + static void prepareDataBlock(FsData& fsData, + const ArticulationLink* links, + PxU16 linkCount, + PxTransform* poses, + PxQuat* deltaQ, + FsInertia* baseInertia, + ArticulationJointTransforms* jointTransforms, + PxU32 expectedSize); + + static void prepareFsData(FsData& fsData, + const ArticulationLink* links); + + static PxReal getResistance(PxReal compliance); + + static PxU32 getFsDataSize(PxU32 linkCount); + + static PxU32 getLtbDataSize(PxU32 linkCount); + + static void setInertia(FsInertia& inertia, + const PxsBodyCore& body, + const PxTransform& pose); + + static void setJointTransforms(ArticulationJointTransforms& transforms, + const PxTransform& parentPose, + const PxTransform& childPose, + const ArticulationJointCore& joint); + + static void applyImpulses(const FsData& matrix, + Cm::SpatialVectorV* Z, + Cm::SpatialVectorV* V); + +private: + +#if DY_DEBUG_ARTICULATION + // debug quantities + + Cm::SpatialVector computeMomentum(const FsInertia *inertia) const; + void computeResiduals(const Cm::SpatialVector *, + const ArticulationJointTransforms* jointTransforms, + bool dump = false) const; + void checkLimits() const; +#endif + + //PX_FORCE_INLINE Cm::SpatialVectorV* getVelocity(FsData& matrix); + + void computeUnconstrainedVelocitiesInternal(const ArticulationSolverDesc& desc, + PxReal dt, + const PxVec3& gravity, PxU64 contextID, + FsInertia* PX_RESTRICT baseInertia, + ArticulationJointTransforms* PX_RESTRICT jointTransforms, + PxcFsScratchAllocator& allocator); + + void prepareLtbMatrix(FsData& fsData, + const FsInertia* baseInertia, + const PxTransform* poses, + const ArticulationJointTransforms* jointTransforms, + PxReal recipDt); + + void computeJointDrives(FsData& fsData, + Ps::aos::Vec3V* drives, + const ArticulationLink* links, + const PxTransform* poses, + const ArticulationJointTransforms* transforms, + const Ps::aos::Mat33V* loads, + PxReal dt); + + mutable Ps::Array mFsDataBytes; // drive cache creation (which is const) can force a resize + + // persistent state of the articulation for warm-starting joint load computation + Ps::Array mInternalLoads; + Ps::Array mExternalLoads; + Ps::Array mScratchMemory; // drive cache creation (which is const) can force a resize + Ps::Array mPose; + Ps::Array mDeltaQ; + Ps::Array mMotionVelocity; // saved here in solver to communicate back to island management/sleeping + +} PX_ALIGN_SUFFIX(64); + +#if PX_VC + #pragma warning(pop) +#endif + +class PxvArticulationDriveCache +{ +public: + // drive cache stuff + static void initialize( + FsData &cache, + PxU16 linkCount, + const ArticulationLink* links, + PxReal compliance, + PxU32 iterations, + char* scratchMemory, + PxU32 scratchMemorySize); + + static PxU32 getLinkCount(const FsData& cache); + + static void applyImpulses(const FsData& cache, + Cm::SpatialVectorV* Z, + Cm::SpatialVectorV* V); + + static void getImpulseResponse(const FsData& cache, + PxU32 linkID, + const Cm::SpatialVectorV& impulse, + Cm::SpatialVectorV& deltaV); +}; + +void PxvRegisterArticulations(); + +} + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h new file mode 100644 index 000000000..42e9dfdec --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXDV_ARTICULATION_CORE_H +#define PXDV_ARTICULATION_CORE_H + +#include "PxArticulationReducedCoordinate.h" + +namespace physx +{ + namespace Dy + { + struct ArticulationCore + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxU32 internalDriveIterations; + PxU32 externalDriveIterations; + PxU32 maxProjectionIterations; + PxU16 solverIterationCounts; //KS - made a U16 so that it matches PxsRigidCore + PxReal separationTolerance; + PxReal sleepThreshold; + PxReal freezeThreshold; + PxReal wakeCounter; + PxArticulationFlags flags; + }; + + struct ArticulationJointCoreDirtyFlag + { + enum Enum + { + eNONE = 0, + eMOTION = 1 << 0, + ePOSE = 1 << 1, + eTARGETPOSE = 1 << 2, + eTARGETVELOCITY = 1 << 3 + }; + }; + + typedef PxFlags ArticulationJointCoreDirtyFlags; + PX_FLAGS_OPERATORS(ArticulationJointCoreDirtyFlag::Enum, PxU8) + } +} + +#endif + diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h new file mode 100644 index 000000000..724b5ff7d --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h @@ -0,0 +1,144 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DV_ARTICULATION_JOINT_CORE_H +#define DV_ARTICULATION_JOINT_CORE_H + +#include "DyArticulationCore.h" +#include "PxArticulationJoint.h" + +namespace physx +{ + namespace Dy + { + + struct ArticulationLimit + { + PxReal low, high; + }; + + struct ArticulationDrive + { + PxReal stiffness, damping, maxForce; + bool isAcceleration; + }; + + struct ArticulationJointCoreBase + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool setJointPose() + { + if (dirtyFlag & ArticulationJointCoreDirtyFlag::ePOSE) + { + relativeQuat = (childPose.q * (parentPose.q.getConjugate())).getNormalized(); + + //ML: this way work in GPU + PxU8 flag = PxU8(ArticulationJointCoreDirtyFlag::ePOSE); + dirtyFlag &= ArticulationJointCoreDirtyFlags(~flag); + + return true; + } + + return false; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator=(ArticulationJointCoreBase& other) + { + parentPose = other.parentPose; + childPose = other.childPose; + + dirtyFlag = other.dirtyFlag; + + prismaticLimited = other.prismaticLimited; + + //KS - temp place to put reduced coordinate limit and drive values + for (PxU32 i = 0; i < 6; ++i) + { + limits[i] = other.limits[i]; + drives[i] = other.drives[i]; + targetP[i] = other.targetP[i]; + targetV[i] = other.targetV[i]; + + dofIds[i] = other.dofIds[i]; + motion[i] = other.motion[i]; + } + + frictionCoefficient = other.frictionCoefficient; + relativeQuat = other.relativeQuat; + jointType = other.jointType; + jointOffset = other.jointOffset; //this is the dof offset for the joint in the cache + + } + + // attachment points, don't change the order, otherwise it will break GPU code + PxTransform parentPose; //28 28 + PxTransform childPose; //28 56 + + //KS - temp place to put reduced coordinate limit and drive values + ArticulationLimit limits[6]; //48 104 + ArticulationDrive drives[6]; //96 200 + PxReal targetP[6]; //24 224 + PxReal targetV[6]; //24 248 + + // initial parent to child rotation + PxQuat relativeQuat; //16 264 + PxReal frictionCoefficient; //4 268 + //this is the dof offset for the joint in the cache + PxU32 jointOffset; //4 272 + + PxU8 dofIds[6]; //6 278 + PxArticulationMotions motion[6]; //6 284 + + PxReal maxJointVelocity; //4 288 + + ArticulationJointCoreDirtyFlags dirtyFlag; //1 289 + bool prismaticLimited; //1 290 + PxU8 jointType; //1 291 + PxU8 pad[13]; //13 304 + + + + ArticulationJointCoreBase() { maxJointVelocity = 100.f; } + // PX_SERIALIZATION + ArticulationJointCoreBase(const PxEMPTY&) {} + //~PX_SERIALIZATION + + }; + } +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h new file mode 100644 index 000000000..67f05583b --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h @@ -0,0 +1,89 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_SHADER_H +#define PXD_SHADER_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "PxvConfig.h" +#include "PxvDynamics.h" +#include "PxConstraint.h" +#include "DyConstraintWriteBack.h" + +namespace physx +{ + +class PxsRigidBody; + +namespace Dy +{ + + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif +PX_ALIGN_PREFIX(16) +struct Constraint +{ +public: + + PxReal linBreakForce; //0 + PxReal angBreakForce; //4 + PxU16 constantBlockSize; //6 + PxU16 flags; //8 + + PxConstraintSolverPrep solverPrep; //12 + PxConstraintProject project; //16 + void* constantBlock; //20 + + PxsRigidBody* body0; //24 + PxsRigidBody* body1; //28 + + PxsBodyCore* bodyCore0; //32 + PxsBodyCore* bodyCore1; //36 + PxU32 index; //40 //this is also a constraint write back index + PxReal minResponseThreshold; //44 +} +PX_ALIGN_SUFFIX(16); +#if PX_VC + #pragma warning(pop) +#endif + +#if !PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(48==sizeof(Constraint)); +#endif + +} + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h new file mode 100644 index 000000000..ca5d889a3 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h @@ -0,0 +1,65 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_CONSTRAINT_WRITE_BACK_H +#define PXD_CONSTRAINT_WRITE_BACK_H + +#include "foundation/PxVec3.h" +#include "PxvConfig.h" +#include "PxvDynamics.h" + +namespace physx +{ + namespace Dy + { + + PX_ALIGN_PREFIX(16) + struct ConstraintWriteback + { + public: + + void initialize() + { + linearImpulse = PxVec3(0); + angularImpulse = PxVec3(0); + broken = false; + } + + PxVec3 linearImpulse; + PxU32 broken; + PxVec3 angularImpulse; + PxU32 pad; + } + PX_ALIGN_SUFFIX(16); + + } +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyContext.h b/src/PhysX/physx/source/lowleveldynamics/include/DyContext.h new file mode 100644 index 000000000..b16d0feda --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyContext.h @@ -0,0 +1,400 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXV_DYNAMICS_CONTEXT_H +#define PXV_DYNAMICS_CONTEXT_H + +#include "CmPhysXCommon.h" +#include "PxSceneDesc.h" +#include "DyThresholdTable.h" +#include "PxcNpThreadContext.h" +#include "PxsSimulationController.h" +#include "DyConstraintWriteBack.h" +#include "PsAllocator.h" + +#define DY_MAX_VELOCITY_COUNT 4 + +namespace physx +{ + +class PxsIslandManager; +class PxcNpMemBlockPool; + +namespace Cm +{ + class EventProfiler; + class FlushPool; +} + +namespace IG +{ + class SimpleIslandManager; + class IslandSim; +} + +template class PxcThreadCoherentCache; +class PxcScratchAllocator; +struct PxvSimStats; +class PxTaskManager; +class PxcNpMemBlockPool; +struct PxgDynamicsMemoryConfig; +class PxsContactManagerOutputIterator; +struct PxsContactManagerOutput; +class PxsKernelWranglerManager; +class PxsHeapMemoryAllocator; +class PxsMemoryManager; +class PxsContactManager; + + +namespace Dy +{ + + +class Context +{ + PX_NOCOPY(Context) +public: + /** + \brief Returns the bounce threshold + \return The bounce threshold. + */ + PX_FORCE_INLINE PxReal getBounceThreshold() const { return mBounceThreshold; } + /** + \brief Returns the friction offset threshold + \return The friction offset threshold. + */ + PX_FORCE_INLINE PxReal getFrictionOffsetThreshold() const { return mFrictionOffsetThreshold; } + /** + \brief Returns the friction offset threshold + \return The friction offset threshold. + */ + PX_FORCE_INLINE PxReal getSolverOffsetSlop() const { return mSolverOffsetSlop; } + /** + \brief Returns the correlation distance + \return The correlation distance. + */ + PX_FORCE_INLINE PxReal getCorrelationDistance() const { return mCorrelationDistance; } + + /** + \brief Returns the CCD separation threshold + \return The CCD separation threshold. + */ + PX_FORCE_INLINE PxReal getCCDSeparationThreshold() const { return mCCDSeparationThreshold; } + + /** + \brief Sets the bounce threshold + \param[in] f The bounce threshold + */ + PX_FORCE_INLINE void setBounceThreshold(PxReal f) { mBounceThreshold = f; } + /** + \brief Sets the correlation distance + \param[in] f The correlation distance + */ + PX_FORCE_INLINE void setCorrelationDistance(PxReal f) { mCorrelationDistance = f; } + /** + \brief Sets the friction offset threshold + \param[in] offset The friction offset threshold + */ + PX_FORCE_INLINE void setFrictionOffsetThreshold(PxReal offset) { mFrictionOffsetThreshold = offset; } + /** + \brief Sets the solver offset slop + \param[in] offset The solver offset slop + */ + PX_FORCE_INLINE void setSolverOffsetSlop(PxReal offset) { mSolverOffsetSlop = offset; } + /** + \brief Sets the friction offset threshold + \param[in] offset The friction offset threshold + */ + PX_FORCE_INLINE void setCCDSeparationThreshold(PxReal offset) { mCCDSeparationThreshold = offset; } + + + /** + \brief Returns the solver batch size + \return The solver batch size. + */ + PX_FORCE_INLINE PxU32 getSolverBatchSize() const { return mSolverBatchSize; } + /** + \brief Sets the solver batch size + \param[in] f The solver batch size + */ + PX_FORCE_INLINE void setSolverBatchSize(PxU32 f) { mSolverBatchSize = f; } + /** + \brief Returns the maximum solver constraint size + \return The maximum solver constraint size in this island in bytes. + */ + PX_FORCE_INLINE PxU32 getMaxSolverConstraintSize() const { return mMaxSolverConstraintSize; } + + /** + \brief Returns the friction model being used. + \return The friction model being used. + */ + PX_FORCE_INLINE PxFrictionType::Enum getFrictionType() const { return mFrictionType; } + + /** + \brief Returns the threshold stream + \return The threshold stream + */ + PX_FORCE_INLINE ThresholdStream& getThresholdStream() { return *mThresholdStream; } + + PX_FORCE_INLINE ThresholdStream& getForceChangedThresholdStream() { return *mForceChangedThresholdStream; } + + /** + \brief Returns the threshold table + \return The threshold table + */ + PX_FORCE_INLINE ThresholdTable& getThresholdTable() { return mThresholdTable; } + + /** + \brief Sets the friction model to be used. + \param[in] f The friction model to be used. + */ + PX_FORCE_INLINE void setFrictionType(PxFrictionType::Enum f) { mFrictionType = f; } + + /** + \brief Destroys this dynamics context + */ + virtual void destroy() = 0; + + + + PX_FORCE_INLINE PxcDataStreamPool& getContactStreamPool() { return mContactStreamPool; } + + PX_FORCE_INLINE PxcDataStreamPool& getPatchStreamPool() { return mPatchStreamPool; } + + PX_FORCE_INLINE PxcDataStreamPool& getForceStreamPool() { return mForceStreamPool; } + + PX_FORCE_INLINE Ps::Array& getConstraintWriteBackPool() { return mConstraintWriteBackPool; } + + + /** + \brief Returns the current frame's timestep + \return The current frame's timestep. + */ + PX_FORCE_INLINE PxReal getDt() const { return mDt; } + /** + \brief Returns 1/(current frame's timestep) + \return 1/(current frame's timestep). + */ + PX_FORCE_INLINE PxReal getInvDt() const { return mInvDt; } + + PX_FORCE_INLINE PxReal getMaxBiasCoefficient() const { return mMaxBiasCoefficient; } + + PX_FORCE_INLINE PxVec3 getGravity() const { return mGravity; } + + + + /** + \brief The entry point for the constraint solver. + \param[in] dt The simulation time-step + \param[in] continuation The continuation task for the solver + \param[in] processLostTouchTask The task that processes lost touches + + This method is called after the island generation has completed. Its main responsibilities are: + (1) Reserving the solver body pools + (2) Initializing the static and kinematic solver bodies, which are shared resources between islands. + (3) Construct the solver task chains for each island + + Each island is solved as an independent solver task chain. In addition, large islands may be solved using multiple parallel tasks. + Island solving is asynchronous. Once all islands have been solved, the continuation task will be called. + + */ + virtual void update(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* processLostTouchTask, + PxsContactManager** foundPatchManagers, PxU32 nbFoundPatchManagers, PxsContactManager** lostPatchManagers, PxU32 nbLostPatchManagers, PxU32 maxPatchesPerCM, + PxsContactManagerOutputIterator& iterator, PxsContactManagerOutput* gpuOutputs, const PxReal dt, const PxVec3& gravity, const PxU32 bitMapWordCounts) = 0; + + virtual void processLostPatches(IG::SimpleIslandManager& simpleIslandManager, PxsContactManager** lostPatchManagers, PxU32 nbLostPatchManagers, PxsContactManagerOutputIterator& iterator) = 0; + + + /** + \brief This method copy gpu solver body data to cpu body core + */ + virtual void updateBodyCore(PxBaseTask* continuation) = 0; + + /** + \brief Called after update's task chain has completed. This collects the results of the solver together + */ + virtual void mergeResults() = 0; + + virtual void setSimulationController(PxsSimulationController* simulationController) = 0; + + virtual void getDataStreamBase(void*& contactStreamBase, void*& patchStreamBase, void*& forceAndIndiceStreamBase) = 0; + + void createThresholdStream(Ps::VirtualAllocatorCallback& callback) { PX_ASSERT(mThresholdStream == NULL); mThresholdStream = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ThresholdStream")), ThresholdStream(callback));} + + void createForceChangeThresholdStream(Ps::VirtualAllocatorCallback& callback) { PX_ASSERT(mForceChangedThresholdStream == NULL); mForceChangedThresholdStream = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ThresholdStream")), ThresholdStream(callback));} + + + +protected: + + Context(IG::IslandSim* accurateIslandSim, Ps::VirtualAllocatorCallback* allocatorCallback, + PxvSimStats& simStats, bool enableStabilization, bool useEnhancedDeterminism, bool useAdaptiveForce, + const PxReal maxBiasCoefficient) : + mThresholdStream(NULL), + mForceChangedThresholdStream(NULL), + mAccurateIslandSim(accurateIslandSim), + mDt (1.0f), + mInvDt (1.0f), + mMaxBiasCoefficient (maxBiasCoefficient), + mEnableStabilization (enableStabilization), + mUseEnhancedDeterminism (useEnhancedDeterminism), + mUseAdaptiveForce (useAdaptiveForce), + mBounceThreshold(-2.0f), + mSolverBatchSize(32), + mConstraintWriteBackPool(Ps::VirtualAllocator(allocatorCallback)), + mSimStats(simStats) + { + } + + virtual ~Context() + { + if(mThresholdStream) + { + mThresholdStream->~ThresholdStream(); + PX_FREE(mThresholdStream); + } + mThresholdStream = NULL; + if(mForceChangedThresholdStream) + { + mForceChangedThresholdStream->~ThresholdStream(); + PX_FREE(mForceChangedThresholdStream); + } + mForceChangedThresholdStream = NULL; + } + + ThresholdStream* mThresholdStream; + ThresholdStream* mForceChangedThresholdStream; + ThresholdTable mThresholdTable; + + IG::IslandSim* mAccurateIslandSim; + PxsSimulationController* mSimulationController; + /** + \brief Time-step. + */ + PxReal mDt; + /** + \brief 1/time-step. + */ + PxReal mInvDt; + + PxReal mMaxBiasCoefficient; + + const bool mEnableStabilization; + + const bool mUseEnhancedDeterminism; + + const bool mUseAdaptiveForce; + + PxVec3 mGravity; + /** + \brief max solver constraint size + */ + PxU32 mMaxSolverConstraintSize; + + /** + \brief Threshold controlling the relative velocity at which the solver transitions between restitution and bias for solving normal contact constraint. + */ + PxReal mBounceThreshold; + /** + \brief Threshold controlling whether friction anchors are constructed or not. If the separation is above mFrictionOffsetThreshold, the contact will not be considered to become a friction anchor + */ + PxReal mFrictionOffsetThreshold; + + /** + \brief Tolerance used to zero offsets along an axis if it is below this threshold. Used to compensate for small numerical divergence inside contact gen. + */ + PxReal mSolverOffsetSlop; + + /** + \brief Threshold controlling whether distant contacts are processed using bias, restitution or a combination of the two. This only has effect on pairs involving bodies that have enabled speculative CCD simulation mode. + */ + PxReal mCCDSeparationThreshold; + + /** + \brief Threshold for controlling friction correlation + */ + PxReal mCorrelationDistance; + /** + \brief The minimum size of an island to generate a solver task chain. + */ + PxU32 mSolverBatchSize; + + /** + \brief The current friction model being used + */ + PxFrictionType::Enum mFrictionType; + + /** + \brief Structure to encapsulate contact stream allocations. Used by GPU solver to reference pre-allocated pinned host memory + */ + PxcDataStreamPool mContactStreamPool; + + /** + \brief Struct to encapsulate the contact patch stream allocations. Used by GPU solver to reference pre-allocated pinned host memory + */ + + PxcDataStreamPool mPatchStreamPool; + + /** + \brief Structure to encapsulate force stream allocations. Used by GPU solver to reference pre-allocated pinned host memory for force reports. + */ + PxcDataStreamPool mForceStreamPool; + + /** + \brief Structure to encapsulate constraint write back allocations. Used by GPU/CPU solver to reference pre-allocated pinned host memory for breakable joint reports. + */ + Ps::Array mConstraintWriteBackPool; + + PxvSimStats& mSimStats; + + +}; + +Context* createDynamicsContext( PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, Cm::FlushPool& taskPool, + PxvSimStats& simStats, PxTaskManager* taskManager, Ps::VirtualAllocatorCallback* allocatorCallback, PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, PxU64 contextID, + const bool enableStabilization, const bool useEnhancedDeterminism, const bool useAdaptiveForce, const PxReal maxBiasCoefficient, + const bool frictionEveryIteration + ); + +Context* createTGSDynamicsContext(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, Cm::FlushPool& taskPool, + PxvSimStats& simStats, PxTaskManager* taskManager, Ps::VirtualAllocatorCallback* allocatorCallback, PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, PxU64 contextID, + const bool enableStabilization, const bool useEnhancedDeterminism, const bool useAdaptiveForce, const PxReal lengthScale +); + + +} + +} + +#endif + diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h new file mode 100644 index 000000000..44026b435 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h @@ -0,0 +1,857 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_FEATHERSTONE_ARTICULATION_H +#define PXD_FEATHERSTONE_ARTICULATION_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsVecMath.h" +#include "CmUtils.h" +#include "PxArticulationJoint.h" +#include "DyVArticulation.h" +#include "DyFeatherstoneArticulationUtils.h" +#include "DyFeatherstoneArticulationJointData.h" + +#ifndef FEATHERSTONE_DEBUG +#define FEATHERSTONE_DEBUG 0 +#endif + +namespace physx +{ + + class PxContactJoint; + struct PxSolverConstraintDesc; + class PxcConstraintBlockStream; + class PxcScratchAllocator; + class PxsConstraintBlockManager; + struct SolverConstraint1DExtStep; + struct PxSolverConstraintPrepDesc; + struct PxSolverBody; + struct PxSolverBodyData; + class PxConstraintAllocator; + +namespace Dy +{ + +//#if PX_VC +//#pragma warning(push) +//#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +//#endif + + //class ArticulationJointCoreData; + class ArticulationLinkData; + struct SpatialSubspaceMatrix; + struct SolverConstraint1DExt; + struct SolverConstraint1DStep; + + class FeatherstoneArticulation; + struct SpatialMatrix; + struct SpatialTransform; + struct Constraint; + + struct DyScratchAllocator + { + char* base; + size_t size; + size_t taken; + + DyScratchAllocator(char* p, size_t s) : base(p), size(s), taken(0) {} + + PxU32 round16(PxU32 size_) { return (size_ + 15)&(~15); } + + template T* alloc(PxU32 count) + { + const PxU32 sizeReq = round16(sizeof(T)*count); + PX_ASSERT((taken+ sizeReq) < size); + T* result = reinterpret_cast(base + taken); + taken += sizeReq; + return result; + } + }; + + + //This stuct is used in TGS + class ArticulationTempData + { + public: + Cm::SpatialVectorF mBaseLinkMotionVelocite; + Ps::Array mZAForce; + Ps::Array mJointPosition; // joint position + Ps::Array mJointVelocity; // joint velocity + Ps::Array mLinkTransform; // this is the transform list for links + }; + + struct ArticulationInternalConstraint + { + //Common/shared directional info between, frictions and drives + Cm::UnAlignedSpatialVector row0; //24 24 + Cm::UnAlignedSpatialVector row1; //24 48 + Cm::UnAlignedSpatialVector deltaVA; //24 72 + Cm::UnAlignedSpatialVector deltaVB; //24 96 + //Response information + PxReal recipResponse; //4 100 + PxReal response; //4 104 + //Joint limit values + PxReal lowLimit; //4 108 + PxReal highLimit; //4 112 + PxReal lowImpulse; //4 116 changed + PxReal highImpulse; //4 120 changed + PxReal erp; //4 124 + //Joint spring drive info + PxReal driveTargetVel; //4 128 + PxReal driveBiasCoefficient; //4 132 + PxReal driveTarget; //4 136 + PxReal driveVelMultiplier; //4 140 + PxReal driveImpulseMultiplier; //4 144 + PxReal maxDriveForce; //4 148 + PxReal driveForce; //4 152 + + PxReal maxFrictionForce; //4 156 + PxReal frictionForce; //4 160 + PxReal frictionForceCoefficient; //4 164 + + bool isLinearConstraint; //1 165 + PxU8 padding[11]; //15 176 + }; + + struct ArticulationInternalLockedAxis + { + //How much an impulse will effect angular velocity + Cm::UnAlignedSpatialVector deltaVA; //24 24 + Cm::UnAlignedSpatialVector deltaVB; //24 48 + //Jacobian axis that is locked + PxVec3 axis; //12 60 + //Response information + PxReal recipResponse; //4 64 + //Initial error + PxReal error; //4 68 + //Bias scale + PxReal biasScale; //4 72 + + PxU32 pad[2]; //4 80 + }; + + class ArticulationData + { + public: + + ArticulationData() : + mLinksData(NULL), mJointData(NULL), + mDofs(0xffffffff), mLocks(0), mDataDirty(true) + { + + } + + ~ArticulationData(); + + PX_FORCE_INLINE void init(); + PX_FORCE_INLINE void resizeLinkData(const PxU32 linkCount); + PX_FORCE_INLINE void resizeJointData(const PxU32 dofs); + + PX_FORCE_INLINE PxReal* getJointAccelerations() { return mJointAcceleration.begin(); } + PX_FORCE_INLINE PxReal* getJointVelocities() { return mJointVelocity.begin(); } + PX_FORCE_INLINE PxReal* getJointDeltaVelocities() { return mJointDeltaVelocity.begin(); } + PX_FORCE_INLINE PxReal* getJointPositions() { return mJointPosition.begin(); } + PX_FORCE_INLINE PxReal* getJointForces() { return mJointForce.begin(); } + //PX_FORCE_INLINE PxReal* getJointFrictionForces() { return mJointFrictionForce.begin(); } + + PX_FORCE_INLINE ArticulationInternalConstraint& getInternalConstraint(const PxU32 dofId) { return mInternalConstraints[dofId]; } + PX_FORCE_INLINE const ArticulationInternalConstraint& getInternalConstraint(const PxU32 dofId) const { return mInternalConstraints[dofId]; } + + PX_FORCE_INLINE ArticulationInternalLockedAxis& getInternalLocks(const PxU32 dofId) { return mInternalLockedAxes[dofId]; } + PX_FORCE_INLINE const ArticulationInternalLockedAxis& getInternalLocks(const PxU32 dofId) const { return mInternalLockedAxes[dofId]; } + + + PX_FORCE_INLINE Cm::SpatialVectorF* getMotionVelocities() { return mMotionVelocities.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getMotionAccelerations() { return mMotionAccelerations.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getCorioliseVectors() { return mCorioliseVectors.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getSpatialZAVectors() { return mZAForces.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getTransmittedForces() { return mJointTransmittedForce.begin(); } + + PX_FORCE_INLINE Cm::SpatialVectorF* getPosIterMotionVelocities() { return mPosIterMotionVelocities.begin(); } + PX_FORCE_INLINE PxReal* getPosIterJointDeltaVelocities() { return mPosIterJointDeltaVelocities.begin(); } + + + PX_FORCE_INLINE Cm::SpatialVectorF& getPosIterMotionVelocity(const PxU32 index) { return mPosIterMotionVelocities[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getMotionVelocity(const PxU32 index) const { return mMotionVelocities[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getMotionAcceleration(const PxU32 index) const { return mMotionAccelerations[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getCorioliseVector(const PxU32 index) const { return mCorioliseVectors[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getSpatialZAVector(const PxU32 index) const { return mZAForces[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getTransmittedForce(const PxU32 index) const { return mJointTransmittedForce[index]; } + + + PX_FORCE_INLINE Cm::SpatialVectorF& getMotionVelocity(const PxU32 index) { return mMotionVelocities[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getMotionAcceleration(const PxU32 index) { return mMotionAccelerations[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getCorioliseVector(const PxU32 index) { return mCorioliseVectors[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getSpatialZAVector(const PxU32 index) { return mZAForces[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getTransmittedForce(const PxU32 index) { return mJointTransmittedForce[index]; } + + //PX_FORCE_INLINE Dy::SpatialMatrix* getTempSpatialMatrix() { mTempSpatialMatrix.begin(); } + + + PX_FORCE_INLINE PxTransform& getPreTransform(const PxU32 index) { return mPreTransform[index]; } + PX_FORCE_INLINE const PxTransform& getPreTransform(const PxU32 index) const { return mPreTransform[index]; } + PX_FORCE_INLINE void setPreTransform(const PxU32 index, const PxTransform& tra) { mPreTransform[index] = tra; } + PX_FORCE_INLINE PxTransform* getPreTransform() { return mPreTransform.begin(); } + + PX_FORCE_INLINE const Cm::SpatialVectorF& getDeltaMotionVector(const PxU32 index) const { return mDeltaMotionVector[index]; } + PX_FORCE_INLINE void setDeltaMotionVector(const PxU32 index, const Cm::SpatialVectorF& vec) { mDeltaMotionVector[index] = vec; } + PX_FORCE_INLINE Cm::SpatialVectorF* getDeltaMotionVector() { return mDeltaMotionVector.begin(); } + + PX_FORCE_INLINE ArticulationLink* getLinks() const { return mLinks; } + PX_FORCE_INLINE PxU32 getLinkCount() const { return mLinkCount; } + + PX_FORCE_INLINE ArticulationLink& getLink(PxU32 index) const { return mLinks[index]; } + + PX_FORCE_INLINE ArticulationLinkData* getLinkData() const { return mLinksData; } + ArticulationLinkData& getLinkData(PxU32 index) const; + + PX_FORCE_INLINE ArticulationJointCoreData* getJointData() const { return mJointData; } + ArticulationJointCoreData& getJointData(PxU32 index) const { return mJointData[index]; } + + PX_FORCE_INLINE const ArticulationCore* getCore() const { return mCore; } + PX_FORCE_INLINE Cm::SpatialVector* getExternalAccelerations() { return mExternalAcceleration; } + + PX_FORCE_INLINE PxU32 getSolverDataSize() { return mSolverDataSize; } + PX_FORCE_INLINE Cm::SpatialVector& getExternalAcceleration(const PxU32 linkID) { return mExternalAcceleration[linkID]; } + + PX_FORCE_INLINE PxReal getDt() const { return mDt; } + PX_FORCE_INLINE void setDt(const PxReal dt) { mDt = dt; } + + PX_FORCE_INLINE bool getDataDirty() { return mDataDirty; } + PX_FORCE_INLINE void setDataDirty(const bool dirty) { mDataDirty = dirty; } + + PX_FORCE_INLINE PxU32 getDofs() const { return mDofs; } + PX_FORCE_INLINE void setDofs(const PxU32 dof) { mDofs = dof; } + + PX_FORCE_INLINE PxU32 getLocks() const { return mLocks; } + PX_FORCE_INLINE void setLocks(const PxU32 locks) { mLocks = locks; } + + PX_FORCE_INLINE FeatherstoneArticulation* getArticulation() { return mArticulation;} + PX_FORCE_INLINE void setArticulation(FeatherstoneArticulation* articulation) { mArticulation = articulation; } + + PX_FORCE_INLINE const SpatialMatrix& getBaseInvSpatialArticulatedInertia() const { return mBaseInvSpatialArticulatedInertia; } + + PX_FORCE_INLINE PxTransform* getAccumulatedPoses() { return mAccumulatedPoses.begin(); } + + PX_FORCE_INLINE const PxTransform* getAccumulatedPoses() const { return mAccumulatedPoses.begin(); } + + private: + + Ps::Array mJointAcceleration; // joint acceleration + Ps::Array mJointVelocity; // joint velocity + Ps::Array mJointDeltaVelocity; // joint velocity change due to contacts + Ps::Array mJointPosition; // joint position + Ps::Array mJointForce; // joint force + //Ps::Array mJointFrictionForce; // joint friction force + + Ps::Array mPosIterJointDeltaVelocities; //joint delta velocity after postion iternation before velocity iteration + Ps::Array mPosIterMotionVelocities; //link motion velocites after position iteration before velocity iteration + Ps::Array mMotionVelocities; //link motion velocites + Ps::Array mMotionAccelerations; //link motion accelerations + Ps::Array mCorioliseVectors; //link coriolise vector + Ps::Array mZAForces; //link spatial zero acceleration force/ spatial articulated force + Ps::Array mJointTransmittedForce; + Ps::Array mInternalConstraints; + Ps::Array mInternalLockedAxes; + + + Ps::Array mDeltaMotionVector; //this is for TGS solver + Ps::Array mPreTransform; //this is the previous transform list for links + + //Ps::Array mTempSpatialMatrix; + + ArticulationLink* mLinks; + PxU32 mLinkCount; + ArticulationLinkData* mLinksData; + ArticulationJointCoreData* mJointData; + PxReal mDt; + PxU32 mDofs; + PxU32 mLocks; + const ArticulationCore* mCore; + Cm::SpatialVector* mExternalAcceleration; + PxU32 mSolverDataSize; + bool mDataDirty; //this means we need to call commonInit() + bool mJointDirty; //this means joint delta velocity has been changed by contacts so we need to update joint velocity/joint acceleration + FeatherstoneArticulation* mArticulation; + + Ps::Array mAccumulatedPoses; + Ps::Array mDeltaQ; + PxReal mAccumulatedDt; + + //ArticulationTempData mTempData; + SpatialMatrix mBaseInvSpatialArticulatedInertia; + + friend class FeatherstoneArticulation; + + }; + + void ArticulationData::init() + { + //zero delta motion vector for TGS solver + PxMemZero(getDeltaMotionVector(), sizeof(Cm::SpatialVectorF) * mLinkCount); + + //zero joint velocity delta, which will be changed in pxcFsApplyImpulse if there are any contact with links + PxMemZero(getJointDeltaVelocities(), sizeof(PxReal) * mDofs); + mJointDirty = false; + + } + + + struct ScratchData + { + public: + ScratchData() + { + motionVelocities = NULL; + motionAccelerations = NULL; + coriolisVectors = NULL; + spatialZAVectors = NULL; + externalAccels = NULL; + compositeSpatialInertias = NULL; + + jointVelocities = NULL; + jointAccelerations = NULL; + jointForces = NULL; + jointPositions = NULL; + jointFrictionForces = NULL; + } + + Cm::SpatialVectorF* motionVelocities; + Cm::SpatialVectorF* motionAccelerations; + Cm::SpatialVectorF* coriolisVectors; + Cm::SpatialVectorF* spatialZAVectors; + Cm::SpatialVector* externalAccels; + Dy::SpatialMatrix* compositeSpatialInertias; + + PxReal* jointVelocities; + PxReal* jointAccelerations; + PxReal* jointForces; + PxReal* jointPositions; + PxReal* jointFrictionForces; + }; + + +#if PX_VC +#pragma warning(push) +#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + PX_ALIGN_PREFIX(64) + class FeatherstoneArticulation : public ArticulationV + { + public: + // public interface + + FeatherstoneArticulation(Sc::ArticulationSim*); + ~FeatherstoneArticulation(); + + + // get data sizes for allocation at higher levels + virtual void getDataSizes(PxU32 linkCount, + PxU32 &solverDataSize, + PxU32& totalSize, + PxU32& scratchSize); + + virtual bool resize(const PxU32 linkCount); + + virtual void onUpdateSolverDesc(); + + virtual PxU32 getDofs(); + + virtual PxU32 getDof(const PxU32 linkID); + + virtual void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag); + + virtual void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag); + + virtual void packJointData(const PxReal* maximum, PxReal* reduced); + + virtual void unpackJointData(const PxReal* reduced, PxReal* maximum); + + virtual void initializeCommonData(); + + //gravity as input, joint force as output + virtual void getGeneralizedGravityForce(const PxVec3& gravity, PxArticulationCache& cache); + + //joint velocity as input, generalised force(coriolis and centrigugal force) as output + virtual void getCoriolisAndCentrifugalForce(PxArticulationCache& cache); + + //external force as input, joint force as output + virtual void getGeneralizedExternalForce(PxArticulationCache& /*cache*/); + + //joint force as input, joint acceleration as output + virtual void getJointAcceleration(const PxVec3& gravity, PxArticulationCache& cache); + + //joint acceleration as input, joint force as out + virtual void getJointForce(PxArticulationCache& cache); + + virtual void getKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache); + + //These two functions are for closed loop system + void getKMatrix(ArticulationJointCore* loopJoint, const PxU32 parentIndex, const PxU32 childIndex, PxArticulationCache& cache); + + virtual void getCoefficentMatrix(const PxReal dt, const PxU32 linkID, const PxContactJoint* contactJoints, const PxU32 nbContacts, PxArticulationCache& cache); + + virtual void getCoefficentMatrixWithLoopJoints(ArticulationLoopConstraint* lConstraints, const PxU32 nbJoints, PxArticulationCache& cache); + + virtual bool getLambda(ArticulationLoopConstraint* lConstraints, const PxU32 nbJoints, PxArticulationCache& cache, PxArticulationCache& rollBackCache, + const PxReal* jointTorque, const PxVec3& gravity, const PxU32 maxIter); + + virtual void getGeneralizedMassMatrix(PxArticulationCache& cache); + + virtual void getGeneralizedMassMatrixCRB(PxArticulationCache& cache); + + virtual void teleportRootLink(); + + virtual void getImpulseResponse( + PxU32 linkID, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaV) const; + + virtual void getImpulseSelfResponse( + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1) const; + + virtual Cm::SpatialVectorV getLinkVelocity(const PxU32 linkID) const; + + virtual Cm::SpatialVectorV getLinkMotionVector(const PxU32 linkID) const; + + //this is called by island gen to determine whether the articulation should be awake or sleep + virtual Cm::SpatialVector getMotionVelocity(const PxU32 linkID) const; + + virtual PxReal getLinkMaxPenBias(const PxU32 linkID) const; + + + + + static PxU32 computeUnconstrainedVelocities( + const ArticulationSolverDesc& desc, + PxReal dt, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, PxU64 contextID, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + static void computeUnconstrainedVelocitiesTGS( + const ArticulationSolverDesc& desc, + PxReal dt, const PxVec3& gravity, + PxU64 contextID, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + static PxU32 setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* Z); + + static void saveVelocity(const ArticulationSolverDesc& d, Cm::SpatialVectorF* deltaV); + + static void saveVelocityTGS(const ArticulationSolverDesc& d, PxReal invDtF32); + + static void updateBodies(const ArticulationSolverDesc& desc, PxReal dt); + + static void updateBodiesTGS(const ArticulationSolverDesc& desc, PxReal dt); + + static void updateBodies(const ArticulationSolverDesc& desc, PxReal dt, bool integrateJointPosition); + + static void recordDeltaMotion(const ArticulationSolverDesc& desc, const PxReal dt, Cm::SpatialVectorF* deltaV); + + static void deltaMotionToMotionVelocity(const ArticulationSolverDesc& desc, PxReal invDt); + + virtual void pxcFsApplyImpulse(PxU32 linkID, Ps::aos::Vec3V linear, + Ps::aos::Vec3V angular, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + virtual void pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, + const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, + const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + virtual void pxcFsApplyImpulses(Cm::SpatialVectorF* Z); + + virtual Cm::SpatialVectorV pxcFsGetVelocity(PxU32 linkID); + + virtual void pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1); + + virtual Cm::SpatialVectorV pxcFsGetVelocityTGS(PxU32 linkID); + + virtual const PxTransform& getCurrentTransform(PxU32 linkID) const; + + virtual const PxQuat& getDeltaQ(PxU32 linkID) const; + + //Applies a set of N impulses, all in local space and updates the links' motion and joint velocities + void applyImpulses(Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + void getDeltaV(Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + //This method calculate the velocity change due to collision/constraint impulse, record joint velocity and acceleration + static Cm::SpatialVectorF propagateVelocity(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z, + PxReal* jointVelocity, const Cm::SpatialVectorF& hDeltaV); + + //This method calculate the velocity change due to collision/constraint impulse + static Cm::SpatialVectorF propagateVelocityTestImpulse(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, + const Cm::SpatialVectorF& Z, const Cm::SpatialVectorF& hDeltaV); + + //This method calculate zero acceration impulse due to test/actual impluse + static Cm::SpatialVectorF propagateImpulse(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z); + + void applyCacheToDest(ArticulationData& data, PxArticulationCache& cache, + PxReal* jVelocities, PxReal* jAcceleration, PxReal* jPosition, PxReal* jointForce, + const PxArticulationCacheFlags flag); + + ArticulationData& getArticulationData() { return mArticulationData; } + + void setGpuRemapId(const PxU32 id) { mGpuRemapId = id; } + PxU32 getGpuRemapId() { return mGpuRemapId; } + + private: + + + void constraintPrep(ArticulationLoopConstraint* lConstraints, const PxU32 nbJoints, + Cm::SpatialVectorF* Z, PxSolverConstraintPrepDesc& prepDesc, PxSolverBody& sBody, + PxSolverBodyData& sBodyData, PxSolverConstraintDesc* desc, PxConstraintAllocator& allocator); + + void updateArticulation(ScratchData& scratchData, + const PxVec3& gravity, + Cm::SpatialVectorF* Z, + Cm::SpatialVectorF* DeltaV); + + + PxU32 computeUnconstrainedVelocitiesInternal( + const ArticulationSolverDesc& desc, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); + + void computeUnconstrainedVelocitiesTGSInternal(const PxVec3& gravity, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); + + + //copy joint data from fromJointData to toJointData + void copyJointData(ArticulationData& data, PxReal* toJointData, PxReal* fromJointData); + + void computeDofs(); + //this function calculates motion subspace matrix(s) for all tree joint + void jcalc(ArticulationData& data); + + //this function calculates loop joint constraint subspace matrix(s) and active force + //subspace matrix + void jcalcLoopJointSubspace(ArticulationJointCore* joint, ArticulationJointCoreData& jointDatum, SpatialSubspaceMatrix& T); + + void computeSpatialInertia(ArticulationData& data); + + //compute zero acceleration force + void computeZ(ArticulationData& data, const PxVec3& gravity, ScratchData& scratchData); + + //compute drag force + void computeD(ArticulationData& data, ScratchData& scratchData, + Cm::SpatialVectorF* tZ, Cm::SpatialVectorF* tDeltaV); + + void solveInternalConstraints(const PxReal dt, const PxReal invDt, Cm::SpatialVectorF* impulses, Cm::SpatialVectorF* DeltaV, + bool velocityIteration); + + //compute coriolis force + void computeC(ArticulationData& data, ScratchData& scratchData); + + //compute relative transform child to parent + void computeRelativeTransformC2P(ArticulationData& data); + //compute relative transform child to base + void computeRelativeTransformC2B(ArticulationData& data); + + void computeLinkVelocities(ArticulationData& data, ScratchData& scratchData); + + void initLinks(ArticulationData& data, const PxVec3& gravity, + ScratchData& scratchData, Cm::SpatialVectorF* tZ, Cm::SpatialVectorF* tDeltaV); + + void computeIs(ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum); + SpatialMatrix computePropagateSpatialInertia(const PxU8 jointType, ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum); + void transformInertia(const SpatialTransform& sTod, SpatialMatrix& inertia); + + void computeArticulatedSpatialInertia(ArticulationData& data); + + void computeArticulatedSpatialZ(ArticulationData& data, ScratchData& scratchData); + + void computeJointAcceleration(ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum, + const Cm::SpatialVectorF& pMotionAcceleration, PxReal* jointAcceleration); + + //compute joint acceleration, joint velocity and link acceleration, velocity based + //on spatial force and spatial articulated inertia tensor + void computeLinkAcceleration(ArticulationData& data, ScratchData& scratchData); + + //void computeTempLinkAcceleration(ArticulationData& data, ScratchData& scratchData); + void computeJointTransmittedFrictionForce(ArticulationData& data, ScratchData& scratchData, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); + void computeJointFriction(ArticulationData& data, ScratchData& scratchData); + + //void copyFromBodyCore(); + //void copyToBodyCore(); + void updateBodies(); + + + void applyExternalImpulse(ArticulationLink* links, const PxU32 linkCount, const bool fixBase, + ArticulationData& data, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV, const PxReal dt, const PxVec3& gravity, Cm::SpatialVector* acceleration); + + static Cm::SpatialVectorF getDeltaVWithDeltaJV(const bool fixBase, const PxU32 linkID, + const ArticulationData& data, Cm::SpatialVectorF* Z, + PxReal* jointVelocities); + + static Cm::SpatialVectorF getDeltaV(const bool fixBase, const PxU32 linkID, + const ArticulationData& data, Cm::SpatialVectorF* Z); + + //impulse need to be in the linkID space + static void getZ(const PxU32 linkID, const ArticulationData& data, + Cm::SpatialVectorF* Z, const Cm::SpatialVectorF& impulse); + + ////impulse0 and impulse1 are in linkID0 and linkID1 space respectively + //static void getZ( + // const ArticulationData& data, + // Cm::SpatialVectorF* Z, + // PxU32 linkID0_, + // const Cm::SpatialVector& impulse0, + // PxU32 linkID1_, + // const Cm::SpatialVector& impulse1); + + //This method use in impulse self response. The input impulse is in the link space + static Cm::SpatialVectorF getImpulseResponse( + const PxU32 linkID, + const bool fixBase, + const ArticulationData& data, + Cm::SpatialVectorF* Z, + const Cm::SpatialVectorF& impulse); + + //This method use in impulse self response. The input impulse is in the link space + static Cm::SpatialVectorF getImpulseResponseWithJ( + const PxU32 linkID, + const bool fixBase, + const ArticulationData& data, + Cm::SpatialVectorF* Z, + const Cm::SpatialVectorF& impulse, + PxReal* jointVelocities); + + void getImpulseSelfResponseInv(const bool fixBase, + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1, + PxReal* jointVelocities); + + void getImpulseResponseSlowInv(Dy::ArticulationLink* links, + const ArticulationData& data, + PxU32 linkID0_, + const Cm::SpatialVector& impulse0, + Cm::SpatialVector& deltaV0, + PxU32 linkID1_, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV1, + PxReal* jointVelocities); + + + Cm::SpatialVectorF getImpulseResponseInv(const bool fixBase, + const PxU32 linkID, Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + PxReal* jointVelocites); + + void inverseDynamic(ArticulationData& data, const PxVec3& gravity, + ScratchData& scratchData); + + void inverseDynamicFloatingBase(ArticulationData& data, const PxVec3& gravity, + ScratchData& scratchData); + + //compute link body force with motion velocity and acceleration + void computeZAForceInv(ArticulationData& data, ScratchData& scratchData); + void initCompositeSpatialInertia(ArticulationData& data, Dy::SpatialMatrix* compositeSpatialInertia); + void computeCompositeSpatialInertiaAndZAForceInv(ArticulationData& data, ScratchData& scratchData); + + void computeRelativeGeneralizedForceInv(ArticulationData& data, ScratchData& scratchData); + + //provided joint velocity and joint acceleartion, compute link acceleration + void computeLinkAccelerationInv(ArticulationData& data, ScratchData& scratchData); + + + void computeGeneralizedForceInv(ArticulationData& data, ScratchData& scratchData); + + void calculateMassMatrixColInv(ScratchData& scratchData); + + void calculateHFixBase(PxArticulationCache& cache); + + void calculateHFloatingBase(PxArticulationCache& cache); + + //joint limits + void enforcePrismaticLimits(PxReal* jPosition, ArticulationJointCore* joint); + + + public: + static void getImpulseSelfResponse(ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + const ArticulationData& data, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1); + + void createHardLimit( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt); + + void createHardLimits( + SolverConstraint1DExt& s0, + SolverConstraint1DExt& s1, + const PxVec3& axis, + PxReal err0, + PxReal err1, + PxReal recipDt, + const Cm::SpatialVectorV& deltaVA, + const Cm::SpatialVectorV& deltaVB, + PxReal recipResponse); + + void createTangentialSpring( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt); + + PxU32 setupSolverConstraints( + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + ArticulationLink* links, + const PxU32 linkCount, + const bool fixBase, + ArticulationData& data, + Cm::SpatialVectorF* Z, + PxU32& acCount); + + void setupInternalConstraints( + ArticulationLink* links, + const PxU32 linkCount, + const bool fixBase, + ArticulationData& data, + Cm::SpatialVectorF* Z, + PxReal dt, + PxReal invDt, + PxReal erp, + bool isTGSSolver); + + void createHardLimitTGS( + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt, + const Cm::SpatialVectorV& deltaVA, + const Cm::SpatialVectorV& deltaVB, + PxReal recipResponse); + + void createTangentialSpringTGS( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt); + + + //integration + void propagateLinksDown(ArticulationData& data, PxReal* jointVelocities, PxReal* jointPositions, + Cm::SpatialVectorF* motionVelocities); + + void computeAndEnforceJointPositions(ArticulationData& data, PxReal* jointPositions); + + //update link position based on joint position provided by the cache + void teleportLinks(ArticulationData& data); + + PxU8* allocateScratchSpatialData(PxcScratchAllocator* allocator, + const PxU32 linkCount, ScratchData& scratchData, bool fallBackToHeap = false); + + void allocateScratchSpatialData(DyScratchAllocator& allocator, + const PxU32 linkCount, ScratchData& scratchData); + + //This method calculate the velocity change from parent to child using parent current motion velocity + PxTransform propagateTransform(const PxU32 linkID, ArticulationLink* links, ArticulationJointCoreData& jointDatum, + Cm::SpatialVectorF* motionVelocities, const PxReal dt, const PxTransform& pBody2World, const PxTransform& currentTransform, + PxReal* jointVelocity, PxReal* jointDeltaVelocities, PxReal* jointPosition); + + static void updateRootBody(const Cm::SpatialVectorF& motionVelocity, + const PxTransform& preTransform, ArticulationData& data, const PxReal dt); + + ArticulationData mArticulationData; + + Ps::Array mScratchMemory; + bool mHasSphericalJoint; + //this is the id remap pxgbodysim and pxgariculation. if application delete the articulation, we need to + //put back this id to the id pool + PxU32 mGpuRemapId; + + } PX_ALIGN_SUFFIX(64); + +#if PX_VC +#pragma warning(pop) +#endif + + void PxvRegisterArticulationsReducedCoordinate(); + +} //namespace Dy + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h new file mode 100644 index 000000000..023757e6f --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h @@ -0,0 +1,261 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_FEATHERSTONE_ARTICULATION_JOINTCORE_H +#define PXD_FEATHERSTONE_ARTICULATION_JOINTCORE_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsVecMath.h" +#include "CmUtils.h" +#include "CmSpatialVector.h" +#include "DyVArticulation.h" +#include "DyFeatherstoneArticulationUtils.h" +#include + +namespace physx +{ + namespace Dy + { + + + class ArticulationJointCoreData + { + public: + + ArticulationJointCoreData() : jointOffset(0xffffffff), dofInternalConstraintMask(0) + { + for (PxU32 i = 0; i < 6; ++i) + { + targetJointPosition[i] = 0.f; + targetJointVelocity[i] = 0.f; + } + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE void computeMotionMatrix(ArticulationJointCoreBase* joint) + { + const PxVec3 childOffset = -joint->childPose.p; + + //transpose(Tc)*S = 0 + //transpose(Ta)*S = 1 + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + PxReal* jJointAxis = jointAxis[0]; + PxVec3 tJointAxis(jJointAxis[3], jJointAxis[4], jJointAxis[5]); + const PxVec3 u = (joint->childPose.rotate(tJointAxis)).getNormalized(); + + motionMatrix.setNumColumns(1); + motionMatrix.setColumn(0, PxVec3(0.f), u); + + PX_ASSERT(dof == 1); + + break; + } + case PxArticulationJointType::eREVOLUTE: + { + PxReal* jJointAxis = jointAxis[0]; + PxVec3 tJointAxis(jJointAxis[0], jJointAxis[1], jJointAxis[2]); + const PxVec3 u = (joint->childPose.rotate(tJointAxis)).getNormalized(); + const PxVec3 uXd = u.cross(childOffset); + + motionMatrix.setNumColumns(1); + motionMatrix.setColumn(0, u, uXd); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + motionMatrix.setNumColumns(dof); + + for (PxU32 ind = 0; ind childPose.rotate(tJointAxis)).getNormalized(); + + const PxVec3 uXd = u.cross(childOffset); + motionMatrix.setColumn(ind, u, uXd); + } + + break; + } + case PxArticulationJointType::eFIX: + { + motionMatrix.setNumColumns(0); + + PX_ASSERT(dof == 0); + break; + } + default: + break; + } + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU8 computeJointDofs(ArticulationJointCoreBase* joint) const + { + PxU8 tDof = 0; + + for (PxU32 i = 0; i < DY_MAX_DOF; ++i) + { + if (joint->motion[i] != PxArticulationMotion::eLOCKED) + { + tDof++; + } + } + + return tDof; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void computeJointDof(ArticulationJointCoreBase* joint, const bool forceRecompute) + { + if (joint->dirtyFlag & ArticulationJointCoreDirtyFlag::eMOTION || forceRecompute) + { + + dof = 0; + lockedAxes = 0; + limitedAxes = 0; + + joint->prismaticLimited = false; + + memset(jointAxis, 0, sizeof(PxReal) * 6 * 6); + + for (PxU8 i = 0; i < DY_MAX_DOF; ++i) + { + if (joint->motion[i] != PxArticulationMotion::eLOCKED) + { + //axis is in the local space of joint + jointAxis[dof][i] = 1.f; + + if (joint->motion[i] == PxArticulationMotion::eLIMITED) + { + + if(i == PxArticulationAxis::eX + || i == PxArticulationAxis::eY + ||i == PxArticulationAxis::eZ) + joint->prismaticLimited = true; + + limitedAxes++; + } + + joint->dofIds[dof++] = i; + } + } + + lockedAxes = 0; + + //Spherical joints treat locked axes as free axes with a constraint. This produces better + //results for spherical joints with 2 dofs free, where keeping the 3rd axis locked can lead to + //an over-consrtained behaviour that is undesirable. However, the drawback is that there will be + //some drift and error on the joint axes + if (joint->jointType == PxArticulationJointType::eSPHERICAL && dof == 2) + { + for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i) + { + if (joint->motion[i] == PxArticulationMotion::eLOCKED) + { + //axis is in the local space of joint + jointAxis[dof][i] = 1.f; + joint->dofIds[dof++] = PxU8(i); + lockedAxes++; + } + } + } + + joint->dirtyFlag &= (~ArticulationJointCoreDirtyFlag::eMOTION); + } + + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE void setJointVelocityDrive(ArticulationJointCoreBase* joint) + { + if (joint->dirtyFlag & ArticulationJointCoreDirtyFlag::eTARGETVELOCITY) + { + PxU32 count = 0; + for (PxU32 i = 0; i < DY_MAX_DOF; ++i) + { + if (joint->motion[i] != PxArticulationMotion::eLOCKED) + { + targetJointVelocity[count] = joint->targetV[i]; + count++; + } + } + joint->dirtyFlag &= ~ArticulationJointCoreDirtyFlag::eTARGETVELOCITY; + } + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void setJointPoseDrive(ArticulationJointCoreBase* joint) + { + if (joint->dirtyFlag & ArticulationJointCoreDirtyFlag::eTARGETPOSE) + { + PxU32 count = 0; + for (PxU32 i = 0; i < DY_MAX_DOF; ++i) + { + if (joint->motion[i] != PxArticulationMotion::eLOCKED) + { + targetJointPosition[count] = joint->targetP[i]; + count++; + } + } + + joint->dirtyFlag &= ~ArticulationJointCoreDirtyFlag::eTARGETPOSE; + } + } + + //in the joint space + PxReal jointAxis[6][6]; //144 144 + + //this is in child body space(S) + SpatialSubspaceMatrix motionMatrix; //196 340 + PxReal targetJointVelocity[6]; //24 364 + PxReal targetJointPosition[6]; //24 388 + PxReal maxDriveForce[6]; //24 412 + + //this is the dof offset for the joint in the cache + PxU32 jointOffset; //4 416 + //degree of freedom + PxU8 dof; //1 417 + PxU8 limitedAxes; //1 418 + PxU8 dofInternalConstraintMask; //1 419 + PxU8 lockedAxes; //1 420 + PxU8 padding[12]; //12 432 + + + + }; + + }//namespace Dy +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h new file mode 100644 index 000000000..33db87784 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h @@ -0,0 +1,881 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_FEATHERSTONE_ARTICULATION_UTIL_H +#define DY_FEATHERSTONE_ARTICULATION_UTIL_H + +#include "PsVecMath.h" +#include "CmSpatialVector.h" +#include "PsBitUtils.h" +#include "foundation/PxMemory.h" + +namespace physx +{ + +namespace Dy +{ + static const size_t DY_MAX_DOF = 6; + + struct SpatialSubspaceMatrix + { + public: + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialSubspaceMatrix() :numColumns(0) + { + //PxMemZero(columns, sizeof(Cm::SpatialVectorF) * 6); + memset(columns, 0, sizeof(Cm::SpatialVectorF) * 6); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void setNumColumns(const PxU32 nc) + { + numColumns = nc; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNumColumns() const + { + return numColumns; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF transposeMultiply(Cm::SpatialVectorF& v) const + { + PxReal result[6]; + for (PxU32 i = 0; i < numColumns; ++i) + { + const Cm::SpatialVectorF& row = columns[i]; + result[i] = row.dot(v); + } + + Cm::SpatialVectorF res; + res.top.x = result[0]; res.top.y = result[1]; res.top.z = result[2]; + res.bottom.x = result[3]; res.bottom.y = result[4]; res.bottom.z = result[5]; + + return res; + + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void setColumn(const PxU32 index, const PxVec3& top, const PxVec3& bottom) + { + columns[index] = Cm::SpatialVectorF(top, bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF& operator[](unsigned int num) + { + return columns[num]; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const Cm::SpatialVectorF& operator[](unsigned int num) const + { + return columns[num]; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const Cm::SpatialVectorF* getColumns() const + { + return columns; + } + + + private: + Cm::SpatialVectorF columns[6]; //192 192 + PxU32 numColumns; //4 196 + + }; + + //this should be 6x6 matrix + //|R, 0| + //|-R*rX, R| + struct SpatialTransform + { + PxMat33 R; + PxQuat q; + PxMat33 T; + + public: + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform() : R(PxZero), T(PxZero) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform(const PxMat33& R_, const PxMat33& T_) : R(R_), T(T_) + { + q = PxQuat(R_); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform(const PxQuat& q_, const PxMat33& T_) : q(q_), T(T_) + { + R = PxMat33(q_); + } + + //This assume angular is the top vector and linear is the bottom vector + /*PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVector operator *(const Cm::SpatialVector& s) const + { + const PxVec3 angular = R * s.angular; + const PxVec3 linear = T * s.angular + R * s.linear; + return Cm::SpatialVector(linear, angular); + }*/ + + + ////This assume angular is the top vector and linear is the bottom vector + //PX_FORCE_INLINE Cm::SpatialVectorF operator *(Cm::SpatialVectorF& s) const + //{ + // const PxVec3 top = R * s.top; + // const PxVec3 bottom = T * s.top + R * s.bottom; + + // const PxVec3 top1 = q.rotate(s.top); + // const PxVec3 bottom1 = T * s.top + q.rotate(s.bottom); + + ///* const PxVec3 tDif = (top - top1).abs(); + // const PxVec3 bDif = (bottom - bottom1).abs(); + // const PxReal eps = 0.001f; + // PX_ASSERT(tDif.x < eps && tDif.y < eps && tDif.z < eps); + // PX_ASSERT(bDif.x < eps && bDif.y < eps && bDif.z < eps);*/ + // return Cm::SpatialVectorF(top1, bottom1); + //} + + //This assume angular is the top vector and linear is the bottom vector + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF operator *(const Cm::SpatialVectorF& s) const + { + //const PxVec3 top = R * s.top; + //const PxVec3 bottom = T * s.top + R * s.bottom; + + const PxVec3 top1 = q.rotate(s.top); + const PxVec3 bottom1 = T * s.top + q.rotate(s.bottom); + + return Cm::SpatialVectorF(top1, bottom1); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector operator *(const Cm::UnAlignedSpatialVector& s) const + { + //const PxVec3 top = R * s.top; + //const PxVec3 bottom = T * s.top + R * s.bottom; + + const PxVec3 top1 = q.rotate(s.top); + const PxVec3 bottom1 = T * s.top + q.rotate(s.bottom); + + return Cm::UnAlignedSpatialVector(top1, bottom1); + } + + //transpose is the same as inverse, R(inverse) = R(transpose) + //|R(t), 0 | + //|rXR(t), R(t)| + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform getTranspose() const + { + SpatialTransform ret; + ret.q = q.getConjugate(); + ret.R = R.getTranspose(); + ret.T = T.getTranspose(); + return ret; + + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF transposeTransform(const Cm::SpatialVectorF& s) const + { + const PxVec3 top1 = q.rotateInv(s.top); + const PxVec3 bottom1 = T.transformTranspose(s.top) + q.rotateInv(s.bottom); + + return Cm::SpatialVectorF(top1, bottom1); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector transposeTransform(const Cm::UnAlignedSpatialVector& s) const + { + const PxVec3 top1 = q.rotateInv(s.top); + const PxVec3 bottom1 = T.transformTranspose(s.top) + q.rotateInv(s.bottom); + + return Cm::UnAlignedSpatialVector(top1, bottom1); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator =(SpatialTransform& other) + { + R = other.R; + q = other.q; + T = other.T; + } + + }; + + //this should be 6x6 matrix and initialize to + //|0, M| + //|I, 0| + //this should be 6x6 matrix but bottomRight is the transpose of topLeft + //so we can get rid of bottomRight + struct SpatialMatrix + { + PxMat33 topLeft; // intialize to 0 + PxMat33 topRight; // initialize to mass matrix + PxMat33 bottomLeft; // initialize to inertia + PxU32 padding; //4 112 + + public: + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix() + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix(PxZERO r) : topLeft(PxZero), topRight(PxZero), + bottomLeft(PxZero) + { + PX_UNUSED(r); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix(const PxMat33& topLeft_, const PxMat33& topRight_, const PxMat33& bottomLeft_) + { + topLeft = topLeft_; + topRight = topRight_; + bottomLeft = bottomLeft_; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33 getBottomRight() const + { + return topLeft.getTranspose(); + } + + PX_FORCE_INLINE void setZero() + { + topLeft = PxMat33(0.f); + topRight = PxMat33(0.f); + bottomLeft = PxMat33(0.f); + } + + + //This assume angular is the top vector and linear is the bottom vector + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVector operator *(const Cm::SpatialVector& s) const + { + const PxVec3 angular = topLeft * s.angular + topRight * s.linear; + const PxVec3 linear = bottomLeft * s.angular + topLeft.getTranspose() * s.linear; + return Cm::SpatialVector(linear, angular); + } + + //This assume angular is the top vector and linear is the bottom vector + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF operator *(const Cm::SpatialVectorF& s) const + { + const PxVec3 top = topLeft * s.top + topRight * s.bottom; + const PxVec3 bottom = bottomLeft * s.top + topLeft.transformTranspose(s.bottom); + + return Cm::SpatialVectorF(top, bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector operator *(const Cm::UnAlignedSpatialVector& s) const + { + const PxVec3 top = topLeft * s.top + topRight * s.bottom; + const PxVec3 bottom = bottomLeft * s.top + topLeft.transformTranspose(s.bottom); + + return Cm::UnAlignedSpatialVector(top, bottom); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator *(const PxReal& s) const + { + const PxMat33 newTopLeft = topLeft * s; + const PxMat33 newTopRight = topRight * s; + const PxMat33 newBottomLeft = bottomLeft * s; + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator -(const SpatialMatrix& s) const + { + PxMat33 newTopLeft = topLeft - s.topLeft; + PxMat33 newTopRight = topRight - s.topRight; + PxMat33 newBottomLeft = bottomLeft - s.bottomLeft; + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator +(const SpatialMatrix& s) const + { + PxMat33 newTopLeft = topLeft + s.topLeft; + PxMat33 newTopRight = topRight + s.topRight; + PxMat33 newBottomLeft = bottomLeft + s.bottomLeft; + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator-() + { + PxMat33 newTopLeft = -topLeft; + PxMat33 newTopRight = -topRight; + PxMat33 newBottomLeft = -bottomLeft; + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator +=(const SpatialMatrix& s) + { + topLeft += s.topLeft; + topRight += s.topRight; + bottomLeft += s.bottomLeft; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator *(const SpatialMatrix& s) + { + PxMat33 sBottomRight = s.topLeft.getTranspose(); + PxMat33 bottomRight = topLeft.getTranspose(); + + PxMat33 newTopLeft = topLeft * s.topLeft + topRight * s.bottomLeft; + PxMat33 newTopRight = topLeft * s.topRight + topRight * sBottomRight; + PxMat33 newBottomLeft = bottomLeft * s.topLeft + bottomRight * s.bottomLeft; + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + static SpatialMatrix constructSpatialMatrix(const Cm::SpatialVector& Is, const Cm::SpatialVector& stI) + { + //construct top left + PxVec3 tLeftC0 = Is.angular * stI.angular.x; + PxVec3 tLeftC1 = Is.angular * stI.angular.y; + PxVec3 tLeftC2 = Is.angular * stI.angular.z; + + PxMat33 topLeft(tLeftC0, tLeftC1, tLeftC2); + + //construct top right + PxVec3 tRightC0 = Is.angular * stI.linear.x; + PxVec3 tRightC1 = Is.angular * stI.linear.y; + PxVec3 tRightC2 = Is.angular * stI.linear.z; + PxMat33 topRight(tRightC0, tRightC1, tRightC2); + + //construct bottom left + PxVec3 bLeftC0 = Is.linear * stI.angular.x; + PxVec3 bLeftC1 = Is.linear * stI.angular.y; + PxVec3 bLeftC2 = Is.linear * stI.angular.z; + PxMat33 bottomLeft(bLeftC0, bLeftC1, bLeftC2); + + return SpatialMatrix(topLeft, topRight, bottomLeft); + } + + static PX_CUDA_CALLABLE SpatialMatrix constructSpatialMatrix(const Cm::SpatialVectorF& Is, const Cm::SpatialVectorF& stI) + { + //construct top left + PxVec3 tLeftC0 = Is.top * stI.top.x; + PxVec3 tLeftC1 = Is.top * stI.top.y; + PxVec3 tLeftC2 = Is.top * stI.top.z; + + PxMat33 topLeft(tLeftC0, tLeftC1, tLeftC2); + + //construct top right + PxVec3 tRightC0 = Is.top * stI.bottom.x; + PxVec3 tRightC1 = Is.top * stI.bottom.y; + PxVec3 tRightC2 = Is.top * stI.bottom.z; + PxMat33 topRight(tRightC0, tRightC1, tRightC2); + + //construct bottom left + PxVec3 bLeftC0 = Is.bottom * stI.top.x; + PxVec3 bLeftC1 = Is.bottom * stI.top.y; + PxVec3 bLeftC2 = Is.bottom * stI.top.z; + PxMat33 bottomLeft(bLeftC0, bLeftC1, bLeftC2); + + return SpatialMatrix(topLeft, topRight, bottomLeft); + } + + static PX_CUDA_CALLABLE SpatialMatrix constructSpatialMatrix(const Cm::SpatialVectorF* columns) + { + PxMat33 topLeft(columns[0].top, columns[1].top, columns[2].top); + PxMat33 bottomLeft(columns[0].bottom, columns[1].bottom, columns[2].bottom); + PxMat33 topRight(columns[3].top, columns[4].top, columns[5].top); + + return SpatialMatrix(topLeft, topRight, bottomLeft); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix getTranspose() + { + PxMat33 newTopLeft = topLeft.getTranspose(); + PxMat33 newTopRight = bottomLeft.getTranspose(); + PxMat33 newBottomLeft = topRight.getTranspose(); + //PxMat33 newBottomRight = bottomRight.getTranspose(); + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft);// , newBottomRight); + } + + //static bool isTranspose(const PxMat33& a, const PxMat33& b) + //{ + // PxReal eps = 0.01f; + // //test bottomRight is the transpose of topLeft + // for (PxU32 i = 0; i <3; ++i) + // { + // for (PxU32 j = 0; j <3; ++j) + // { + // if (PxAbs(a[i][j] - b[j][i]) > eps) + // return false; + // } + // } + + // return true; + //} + + PX_FORCE_INLINE bool isIdentity(const PxMat33& matrix) + { + PxReal eps = 0.00001f; + float x = PxAbs(1.f - matrix.column0.x); + float y = PxAbs(1.f - matrix.column1.y); + float z = PxAbs(1.f - matrix.column2.z); + bool identity = ((x < eps) && PxAbs(matrix.column0.y - 0.f) < eps && PxAbs(matrix.column0.z - 0.f) < eps) && + (PxAbs(matrix.column1.x - 0.f) < eps && (y < eps) && PxAbs(matrix.column1.z - 0.f) < eps) && + (PxAbs(matrix.column2.x - 0.f) < eps && PxAbs(matrix.column2.y - 0.f) < eps && (z < eps)); + + return identity; + } + + PX_FORCE_INLINE bool isZero(const PxMat33& matrix) + { + PxReal eps = 0.0001f; + for (PxU32 i = 0; i < 3; ++i) + { + for (PxU32 j = 0; j < 3; ++j) + { + if (PxAbs(matrix[i][j]) > eps) + return false; + } + } + + return true; + } + + PX_FORCE_INLINE bool isIdentity() + { + bool topLeftIsIdentity = isIdentity(topLeft); + + bool topRightIsZero = isZero(topRight); + + bool bottomLeftIsZero = isZero(bottomLeft); + + return topLeftIsIdentity && topRightIsZero && bottomLeftIsZero; + } + + static bool isEqual(const PxMat33& s0, const PxMat33& s1) + { + PxReal eps = 0.00001f; + for (PxU32 i = 0; i < 3; ++i) + { + for (PxU32 j = 0; j < 3; ++j) + { + PxReal t = s0[i][j] - s1[i][j]; + if (PxAbs(t) > eps) + return false; + } + } + + return true; + } + + PX_FORCE_INLINE bool isEqual(const SpatialMatrix& s) + { + bool topLeftEqual = isEqual(topLeft, s.topLeft); + bool topRightEqual = isEqual(topRight, s.topRight); + bool bottomLeftEqual = isEqual(bottomLeft, s.bottomLeft); + + return topLeftEqual && topRightEqual && bottomLeftEqual; + } + + static PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33 invertSym33(const PxMat33& in) + { + PxVec3 v0 = in[1].cross(in[2]), + v1 = in[2].cross(in[0]), + v2 = in[0].cross(in[1]); + + PxReal det = v0.dot(in[0]); + + if (det != 0) + { + PxReal recipDet = 1.0f / det; + + return PxMat33(v0 * recipDet, + PxVec3(v0.y, v1.y, v1.z) * recipDet, + PxVec3(v0.z, v1.z, v2.z) * recipDet); + } + else + { + return PxMat33(PxIdentity); + } + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix invertInertia() + { + PxMat33 aa = bottomLeft, ll = topRight, la = topLeft; + + aa = (aa + aa.getTranspose())*0.5f; + ll = (ll + ll.getTranspose())*0.5f; + + PxMat33 AAInv = invertSym33(aa); + + PxMat33 z = -la * AAInv; + PxMat33 S = ll + z * la.getTranspose(); // Schur complement of mAA + + PxMat33 LL = invertSym33(S); + + PxMat33 LA = LL * z; + PxMat33 AA = AAInv + z.getTranspose() * LA; + + SpatialMatrix result(LA.getTranspose(), AA, LL);// , LA); + + return result; + } + + SpatialMatrix getInverse() + { + PxMat33 bottomRight = topLeft.getTranspose(); + + PxMat33 blInverse = bottomLeft.getInverse(); + PxMat33 lComp0 = blInverse * (-bottomRight); + PxMat33 lComp1 = topLeft * lComp0 + topRight; + + //This can be simplified + PxMat33 newBottomLeft = lComp1.getInverse(); + PxMat33 newTopLeft = lComp0 * newBottomLeft; + + PxMat33 trInverse = topRight.getInverse(); + PxMat33 rComp0 = trInverse * (-topLeft); + PxMat33 rComp1 = bottomLeft + bottomRight * rComp0; + + PxMat33 newTopRight = rComp1.getInverse(); + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + void zero() + { + topLeft = PxMat33(PxZero); + topRight = PxMat33(PxZero); + bottomLeft = PxMat33(PxZero); + } + + }; + + struct Temp6x6Matrix; + + struct Temp6x3Matrix + { + PxReal column[3][6]; + public: + + Temp6x3Matrix() + { + + } + + Temp6x3Matrix(const Cm::SpatialVectorF* spatialAxis) + { + constructColumn(column[0], spatialAxis[0]); + constructColumn(column[1], spatialAxis[1]); + constructColumn(column[2], spatialAxis[2]); + } + + void constructColumn(PxReal* dest, const Cm::SpatialVectorF& v) + { + dest[0] = v.top.x; + dest[1] = v.top.y; + dest[2] = v.top.z; + + dest[3] = v.bottom.x; + dest[4] = v.bottom.y; + dest[5] = v.bottom.z; + } + + Temp6x6Matrix operator * (PxReal s[6][3]); + + ////s is 3x6 matrix + //PX_FORCE_INLINE Temp6x6Matrix operator * (PxReal s[6][3]) + //{ + // Temp6x6Matrix temp; + + // for (PxU32 i = 0; i < 6; ++i) + // { + // PxReal* tc = temp.column[i]; + + // for (PxU32 j = 0; j < 6; ++j) + // { + // tc[j] = 0.f; + // for (PxU32 k = 0; k < 3; ++k) + // { + // tc[j] += column[k][j] * s[i][k]; + // } + // } + // } + + // return temp; + //} + + PX_FORCE_INLINE Temp6x3Matrix operator * (const PxMat33& s) + { + Temp6x3Matrix temp; + + for (PxU32 i = 0; i < 3; ++i) + { + PxReal* tc = temp.column[i]; + PxVec3 sc = s[i]; + + for (PxU32 j = 0; j < 6; ++j) + { + tc[j] = 0.f; + for (PxU32 k = 0; k < 3; ++k) + { + tc[j] += column[k][j] * sc[k]; + } + } + } + + return temp; + } + + PX_FORCE_INLINE bool isColumnEqual(const PxU32 ind, const Cm::SpatialVectorF& col) + { + PxReal temp[6]; + constructColumn(temp, col); + const PxReal eps = 0.00001f; + for (PxU32 i = 0; i < 6; ++i) + { + const PxReal dif = column[ind][i] - temp[i]; + if (PxAbs(dif) > eps) + return false; + } + return true; + } + + }; + + + struct Temp6x6Matrix + { + PxReal column[6][6]; + public: + Temp6x6Matrix() + { + + } + + Temp6x6Matrix(const SpatialMatrix& spatialMatrix) + { + constructColumn(column[0], spatialMatrix.topLeft.column0, spatialMatrix.bottomLeft.column0); + constructColumn(column[1], spatialMatrix.topLeft.column1, spatialMatrix.bottomLeft.column1); + constructColumn(column[2], spatialMatrix.topLeft.column2, spatialMatrix.bottomLeft.column2); + + const PxMat33 bottomRight = spatialMatrix.getBottomRight(); + constructColumn(column[3], spatialMatrix.topRight.column0, bottomRight.column0); + constructColumn(column[4], spatialMatrix.topRight.column1, bottomRight.column1); + constructColumn(column[5], spatialMatrix.topRight.column2, bottomRight.column2); + } + + void constructColumn(const PxU32 ind, const PxReal* const values) + { + for (PxU32 i = 0; i < 6; ++i) + { + column[ind][i] = values[i]; + } + } + + void constructColumn(PxReal* dest, const PxVec3& top, const PxVec3& bottom) + { + dest[0] = top.x; + dest[1] = top.y; + dest[2] = top.z; + + dest[3] = bottom.x; + dest[4] = bottom.y; + dest[5] = bottom.z; + } + + Temp6x6Matrix getTranspose() const + { + Temp6x6Matrix temp; + for (PxU32 i = 0; i < 6; ++i) + { + for (PxU32 j = 0; j < 6; ++j) + { + temp.column[i][j] = column[j][i]; + } + } + return temp; + } + + PX_FORCE_INLINE Cm::SpatialVector operator * (const Cm::SpatialVector& s) const + { + Temp6x6Matrix tempMatrix = getTranspose(); + PxReal st[6]; + st[0] = s.angular.x; st[1] = s.angular.y; st[2] = s.angular.z; + st[3] = s.linear.x; st[4] = s.linear.y; st[5] = s.linear.z; + + PxReal result[6]; + for (PxU32 i = 0; i < 6; i++) + { + result[i] = 0; + for (PxU32 j = 0; j < 6; ++j) + { + result[i] += tempMatrix.column[i][j] * st[j]; + } + } + + + Cm::SpatialVector temp; + temp.angular.x = result[0]; temp.angular.y = result[1]; temp.angular.z = result[2]; + temp.linear.x = result[3]; temp.linear.y = result[4]; temp.linear.z = result[5]; + return temp; + } + + + PX_FORCE_INLINE Cm::SpatialVectorF operator * (const Cm::SpatialVectorF& s) const + { + PxReal st[6]; + st[0] = s.top.x; st[1] = s.top.y; st[2] = s.top.z; + st[3] = s.bottom.x; st[4] = s.bottom.y; st[5] = s.bottom.z; + + PxReal result[6]; + for (PxU32 i = 0; i < 6; ++i) + { + result[i] = 0.f; + for (PxU32 j = 0; j < 6; ++j) + { + result[i] += column[j][i] * st[j]; + } + } + + Cm::SpatialVectorF temp; + temp.top.x = result[0]; temp.top.y = result[1]; temp.top.z = result[2]; + temp.bottom.x = result[3]; temp.bottom.y = result[4]; temp.bottom.z = result[5]; + return temp; + } + + PX_FORCE_INLINE Temp6x3Matrix operator * (const Temp6x3Matrix& s) const + { + Temp6x3Matrix temp; + for (PxU32 i = 0; i < 3; ++i) + { + PxReal* result = temp.column[i]; + + const PxReal* input = s.column[i]; + + for (PxU32 j = 0; j < 6; ++j) + { + result[j] = 0.f; + for (PxU32 k = 0; k < 6; ++k) + { + result[j] += column[k][j] * input[k]; + } + } + } + + return temp; + + } + + PX_FORCE_INLINE Cm::SpatialVector spatialVectorMul(const Cm::SpatialVector& s) + { + PxReal st[6]; + st[0] = s.angular.x; st[1] = s.angular.y; st[2] = s.angular.z; + st[3] = s.linear.x; st[4] = s.linear.y; st[5] = s.linear.z; + + PxReal result[6]; + for (PxU32 i = 0; i < 6; ++i) + { + result[i] = 0.f; + for (PxU32 j = 0; j < 6; j++) + { + result[i] += column[i][j] * st[j]; + } + } + + Cm::SpatialVector temp; + temp.angular.x = result[0]; temp.angular.y = result[1]; temp.angular.z = result[2]; + temp.linear.x = result[3]; temp.linear.y = result[4]; temp.linear.z = result[5]; + return temp; + } + + PX_FORCE_INLINE bool isEqual(const Cm::SpatialVectorF* m) + { + PxReal temp[6]; + PxReal eps = 0.00001f; + for (PxU32 i = 0; i < 6; ++i) + { + temp[0] = m[i].top.x; temp[1] = m[i].top.y; temp[2] = m[i].top.z; + temp[3] = m[i].bottom.x; temp[4] = m[i].bottom.y; temp[5] = m[i].bottom.z; + + for (PxU32 j = 0; j < 6; ++j) + { + PxReal dif = column[i][j] - temp[j]; + if (PxAbs(dif) > eps) + return false; + } + } + + return true; + } + }; + + //s is 3x6 matrix + PX_FORCE_INLINE Temp6x6Matrix Temp6x3Matrix::operator * (PxReal s[6][3]) + { + Temp6x6Matrix temp; + + for (PxU32 i = 0; i < 6; ++i) + { + PxReal* tc = temp.column[i]; + + for (PxU32 j = 0; j < 6; ++j) + { + tc[j] = 0.f; + for (PxU32 k = 0; k < 3; ++k) + { + tc[j] += column[k][j] * s[i][k]; + } + } + } + + return temp; + } + + PX_FORCE_INLINE void calculateNewVelocity(const PxTransform& newTransform, const PxTransform& oldTransform, + const PxReal dt, PxVec3& linear, PxVec3& angular) + { + //calculate the new velocity + linear = (newTransform.p - oldTransform.p) / dt; + PxQuat quat = newTransform.q * oldTransform.q.getConjugate(); + + if (quat.w < 0) //shortest angle. + quat = -quat; + + PxReal angle; + PxVec3 axis; + quat.toRadiansAndUnitAxis(angle, axis); + angular = (axis * angle) / dt; + } + + + // generates a pair of quaternions (swing, twist) such that in = swing * twist, with + // swing.x = 0 + // twist.y = twist.z = 0, and twist is a unit quat + PX_CUDA_CALLABLE PX_FORCE_INLINE void separateSwingTwist(const PxQuat& q, PxQuat& twist, PxQuat& swing1, PxQuat& swing2) + { + twist = q.x != 0.0f ? PxQuat(q.x, 0, 0, q.w).getNormalized() : PxQuat(PxIdentity); + PxQuat swing = q * twist.getConjugate(); + swing1 = swing.y != 0.f ? PxQuat(0.f, swing.y, 0.f, swing.w).getNormalized() : PxQuat(PxIdentity); + swing = swing * swing1.getConjugate(); + swing2 = swing.z != 0.f ? PxQuat(0.f, 0.f, swing.z, swing.w).getNormalized() : PxQuat(PxIdentity); + } + + +} //namespace Dy + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h new file mode 100644 index 000000000..70e0511bd --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h @@ -0,0 +1,42 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_SLEEPING_CONFIGULATION_H +#define PXD_SLEEPING_CONFIGULATION_H + +#define PXD_FREEZE_INTERVAL 1.5f +#define PXD_FREE_EXIT_THRESHOLD 4.f +#define PXD_FREEZE_TOLERANCE 0.25f + +#define PXD_SLEEP_DAMPING 0.5f +#define PXD_ACCEL_LOSS 0.9f +#define PXD_FREEZE_SCALE 0.1f + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h b/src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h new file mode 100644 index 000000000..b44a7eb9e --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h @@ -0,0 +1,280 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_THRESHOLDTABLE_H +#define PXD_THRESHOLDTABLE_H +#include "Ps.h" +#include "PsArray.h" +#include "CmPhysXCommon.h" +#include "PsAllocator.h" +#include "PsHash.h" +#include "foundation/PxMemory.h" +#include "PxsIslandNodeIndex.h" + +namespace physx +{ + +class PxsRigidBody; + +namespace Sc +{ + class ShapeInteraction; +} + +namespace Dy +{ + +struct ThresholdStreamElement +{ + Sc::ShapeInteraction* shapeInteraction; //4 8 + PxReal normalForce; //8 12 + PxReal threshold; //12 16 + IG::NodeIndex nodeIndexA; //this is the unique node index in island gen which corresonding to that body and it is persistent 16 20 + IG::NodeIndex nodeIndexB; //This is the unique node index in island gen which corresonding to that body and it is persistent 20 24 + PxReal accumulatedForce; //24 28 + PxU32 pad; //28 32 + +#if !PX_P64_FAMILY + PxU32 pad1; //32 +#endif // !PX_X64 + + PX_CUDA_CALLABLE bool operator <= (const ThresholdStreamElement& otherPair) const + { + return ((nodeIndexA < otherPair.nodeIndexA) ||(nodeIndexA == otherPair.nodeIndexA && nodeIndexB <= otherPair.nodeIndexB)); + } + +}; + +typedef Ps::Array ThresholdArray; + +class ThresholdStream : public ThresholdArray +{ +public: + ThresholdStream(Ps::VirtualAllocatorCallback& allocatorCallback) : ThresholdArray(Ps::VirtualAllocator(&allocatorCallback)) + { + } + +}; + +class ThresholdTable +{ +public: + + ThresholdTable() + : mBuffer(NULL), + mHash(NULL), + mHashSize(0), + mHashCapactiy(0), + mPairs(NULL), + mNexts(NULL), + mPairsSize(0), + mPairsCapacity(0) + { + } + + ~ThresholdTable() + { + if(mBuffer) PX_FREE(mBuffer); + } + + void build(const ThresholdStream& stream); + + bool check(const ThresholdStream& stream, const PxU32 nodexIndexA, const PxU32 nodexIndexB, PxReal dt); + + bool check(const ThresholdStream& stream, const ThresholdStreamElement& elem, PxU32& thresholdIndex); + +//private: + + static const PxU32 NO_INDEX = 0xffffffff; + + struct Pair + { + PxU32 thresholdStreamIndex; + PxReal accumulatedForce; + //PxU32 next; // hash key & next ptr + }; + + PxU8* mBuffer; + + PxU32* mHash; + PxU32 mHashSize; + PxU32 mHashCapactiy; + + Pair* mPairs; + PxU32* mNexts; + PxU32 mPairsSize; + PxU32 mPairsCapacity; +}; + +namespace +{ + static PX_FORCE_INLINE PxU32 computeHashKey(const PxU32 nodeIndexA, const PxU32 nodeIndexB, const PxU32 hashCapacity) + { + return (Ps::hash(PxU64(nodeIndexA)<<32 | PxU64(nodeIndexB)) % hashCapacity); + } +} + +inline bool ThresholdTable::check(const ThresholdStream& stream, const ThresholdStreamElement& elem, PxU32& thresholdIndex) +{ + PxU32* PX_RESTRICT hashes = mHash; + PxU32* PX_RESTRICT nextIndices = mNexts; + Pair* PX_RESTRICT pairs = mPairs; + + PX_ASSERT(elem.nodeIndexA < elem.nodeIndexB); + PxU32 hashKey = computeHashKey(elem.nodeIndexA.index(), elem.nodeIndexB.index(), mHashSize); + + PxU32 pairIndex = hashes[hashKey]; + + while(NO_INDEX != pairIndex) + { + Pair& pair = pairs[pairIndex]; + const PxU32 thresholdStreamIndex = pair.thresholdStreamIndex; + PX_ASSERT(thresholdStreamIndex < stream.size()); + const ThresholdStreamElement& otherElement = stream[thresholdStreamIndex]; + if(otherElement.nodeIndexA==elem.nodeIndexA && otherElement.nodeIndexB==elem.nodeIndexB && otherElement.shapeInteraction == elem.shapeInteraction) + { + thresholdIndex = thresholdStreamIndex; + return true; + } + pairIndex = nextIndices[pairIndex]; + } + + thresholdIndex = NO_INDEX; + return false; +} + + +inline void ThresholdTable::build(const ThresholdStream& stream) +{ + //Handle the case of an empty stream. + if(0==stream.size()) + { + mPairsSize=0; + mPairsCapacity=0; + mHashSize=0; + mHashCapactiy=0; + if(mBuffer) PX_FREE(mBuffer); + mBuffer = NULL; + return; + } + + //Realloc/resize if necessary. + const PxU32 pairsCapacity = stream.size(); + const PxU32 hashCapacity = pairsCapacity*2+1; + if((pairsCapacity > mPairsCapacity) || (pairsCapacity < (mPairsCapacity >> 2))) + { + if(mBuffer) PX_FREE(mBuffer); + const PxU32 pairsByteSize = sizeof(Pair)*pairsCapacity; + const PxU32 nextsByteSize = sizeof(PxU32)*pairsCapacity; + const PxU32 hashByteSize = sizeof(PxU32)*hashCapacity; + const PxU32 totalByteSize = pairsByteSize + nextsByteSize + hashByteSize; + mBuffer = reinterpret_cast(PX_ALLOC(totalByteSize, "PxThresholdStream")); + + PxU32 offset = 0; + mPairs = reinterpret_cast(mBuffer + offset); + offset += pairsByteSize; + mNexts = reinterpret_cast(mBuffer + offset); + offset += nextsByteSize; + mHash = reinterpret_cast(mBuffer + offset); + offset += hashByteSize; + PX_ASSERT(totalByteSize == offset); + + mPairsCapacity = pairsCapacity; + mHashCapactiy = hashCapacity; + } + + + //Set each entry of the hash table to 0xffffffff + PxMemSet(mHash, 0xff, sizeof(PxU32)*hashCapacity); + + //Init the sizes of the pairs array and hash array. + mPairsSize = 0; + mHashSize = hashCapacity; + + PxU32* PX_RESTRICT hashes = mHash; + PxU32* PX_RESTRICT nextIndices = mNexts; + Pair* PX_RESTRICT pairs = mPairs; + + //Add all the pairs from the stream. + PxU32 pairsSize = 0; + for(PxU32 i = 0; i < pairsCapacity; i++) + { + const ThresholdStreamElement& element = stream[i]; + const IG::NodeIndex nodeIndexA = element.nodeIndexA; + const IG::NodeIndex nodeIndexB = element.nodeIndexB; + + const PxF32 force = element.normalForce; + + PX_ASSERT(nodeIndexA < nodeIndexB); + + const PxU32 hashKey = computeHashKey(nodeIndexA.index(), nodeIndexB.index(), hashCapacity); + + //Get the index of the first pair found that resulted in a hash that matched hashKey. + PxU32 prevPairIndex = hashKey; + PxU32 pairIndex = hashes[hashKey]; + + //Search through all pairs found that resulted in a hash that matched hashKey. + //Search until the exact same body pair is found. + //Increment the accumulated force if the exact same body pair is found. + while(NO_INDEX != pairIndex) + { + Pair& pair = pairs[pairIndex]; + const PxU32 thresholdStreamIndex = pair.thresholdStreamIndex; + PX_ASSERT(thresholdStreamIndex < stream.size()); + const ThresholdStreamElement& otherElement = stream[thresholdStreamIndex]; + if(nodeIndexA == otherElement.nodeIndexA && nodeIndexB==otherElement.nodeIndexB) + { + pair.accumulatedForce += force; + prevPairIndex = NO_INDEX; + pairIndex = NO_INDEX; + break; + } + prevPairIndex = pairIndex; + pairIndex = nextIndices[pairIndex]; + } + + if(NO_INDEX != prevPairIndex) + { + nextIndices[pairsSize] = hashes[hashKey]; + hashes[hashKey] = pairsSize; + Pair& newPair = pairs[pairsSize]; + newPair.thresholdStreamIndex = i; + newPair.accumulatedForce = force; + pairsSize++; + } + } + mPairsSize = pairsSize; +} + +} + +} + +#endif //DY_THRESHOLDTABLE_H diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h new file mode 100644 index 000000000..2a9bbe755 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h @@ -0,0 +1,492 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXDV_ARTICULATION_H +#define PXDV_ARTICULATION_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsVecMath.h" +#include "CmUtils.h" +#include "CmSpatialVector.h" +#include "PxArticulationJoint.h" +#include "PxArticulation.h" +#include "foundation/PxMemory.h" +#include "DyArticulationCore.h" +#include "DyArticulationJointCore.h" + + +namespace physx +{ + + class PxcRigidBody; + struct PxsBodyCore; + + class PxcConstraintBlockStream; + class PxcRigidBody; + class PxsConstraintBlockManager; + struct PxSolverConstraintDesc; + + + namespace Sc + { + class ArticulationSim; + } + + + namespace Dy + { + + struct PxcFsScratchAllocator; + struct PxTGSSolverConstraintDesc; + + static const size_t DY_ARTICULATION_MAX_SIZE = 64; + struct ArticulationJointTransforms; + class ArticulationJointCoreData; + struct Constraint; + class Context; + + struct ArticulationJointCore : public ArticulationJointCoreBase + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + // drive model + PxQuat targetPosition; + PxVec3 targetVelocity; + + PxReal spring;//old + PxReal damping;//old + + PxReal internalCompliance;//old + PxReal externalCompliance;//old + + // limit model + + PxReal swingLimitContactDistance;//old + + PxReal tangentialStiffness;//old + PxReal tangentialDamping;//old + + bool swingLimited;//old + bool twistLimited;//old + + PxU8 driveType; //both + + PxReal twistLimitContactDistance; //old + + PxReal tanQSwingY;//old + PxReal tanQSwingZ;//old + PxReal tanQSwingPad;//old + PxReal tanQTwistHigh;//old + PxReal tanQTwistLow;//old + PxReal tanQTwistPad;//old + + ArticulationJointCore() + { + //Cm::markSerializedMem(this, sizeof(ArticulationJointCore)); + parentPose = PxTransform(PxIdentity); + childPose = PxTransform(PxIdentity); + internalCompliance = 0; + externalCompliance = 0; + swingLimitContactDistance = 0.05f; + twistLimitContactDistance = 0.05f; + + driveType = PxArticulationJointDriveType::eTARGET; + + jointType = PxArticulationJointType::eFIX; + + for (PxU32 i = 0; i < 6; ++i) + { + motion[i] = PxArticulationMotion::eLOCKED; + } + + dirtyFlag = ArticulationJointCoreDirtyFlag::eMOTION; + + //initial parent to child + relativeQuat = PxQuat(PxIdentity); + + jointOffset = 0; + + } + + ArticulationJointCore(const PxTransform& parentFrame, + const PxTransform& childFrame) + { + parentPose = parentFrame; + childPose = childFrame; + + //the old articulation is eTARGET + driveType = PxArticulationJointDriveType::eTARGET; + + spring = 0.0f; + damping = 0.0f; + + internalCompliance = 1.0f; + externalCompliance = 1.0f; + + for (PxU32 i = 0; i < 6; ++i) + { + limits[i].low = 0.f; + limits[i].high = 0.f; + drives[i].stiffness = 0.f; + drives[i].damping = 0.f; + drives[i].maxForce = 0.f; + targetP[i] = 0.f; + targetV[i] = 0.f; + } + + + PxReal swingYLimit = PxPi / 4; + PxReal swingZLimit = PxPi / 4; + + limits[PxArticulationAxis::eSWING1].low = swingYLimit; + limits[PxArticulationAxis::eSWING2].low = swingZLimit; + + swingLimitContactDistance = 0.05f; + swingLimited = false; + tangentialStiffness = 0.0f; + tangentialDamping = 0.0f; + + PxReal twistLimitLow = -PxPi / 4; + PxReal twistLimitHigh = PxPi / 4; + limits[PxArticulationAxis::eTWIST].low = twistLimitLow; + limits[PxArticulationAxis::eTWIST].high = twistLimitHigh; + twistLimitContactDistance = 0.05f; + twistLimited = false; + + + tanQSwingY = PxTan(swingYLimit / 4); + tanQSwingZ = PxTan(swingZLimit / 4); + tanQSwingPad = PxTan(swingLimitContactDistance / 4); + + tanQTwistHigh = PxTan(twistLimitHigh / 4); + tanQTwistLow = PxTan(twistLimitLow / 4); + tanQTwistPad = PxTan(twistLimitContactDistance / 4); + + + + prismaticLimited = false; + + frictionCoefficient = 0.f; + + for (PxU32 i = 0; i < 6; ++i) + { + motion[i] = PxArticulationMotion::eLOCKED; + } + + dirtyFlag = ArticulationJointCoreDirtyFlag::eMOTION; + + //initial parent to child + relativeQuat = (childFrame.q * (parentFrame.q.getConjugate())).getNormalized(); + + jointOffset = 0; + + } + + void setJointPose(ArticulationJointCoreData& jointDatum); + + // PX_SERIALIZATION + ArticulationJointCore(const PxEMPTY&) {} + //~PX_SERIALIZATION + + }; + + struct ArticulationLoopConstraint + { + public: + PxU32 linkIndex0; + PxU32 linkIndex1; + Dy::Constraint* constraint; + + }; + +#define DY_ARTICULATION_LINK_NONE 0xffffffff + + typedef PxU64 ArticulationBitField; + + struct ArticulationLink + { + ArticulationBitField children; // child bitmap + ArticulationBitField pathToRoot; // path to root, including link and root + PxcRigidBody* body; + PxsBodyCore* bodyCore; + ArticulationJointCore* inboundJoint; + PxU32 parent; + }; + + typedef size_t ArticulationLinkHandle; + + class ArticulationV; + + struct ArticulationSolverDesc + { + ArticulationV* articulation; + ArticulationLink* links; + Cm::SpatialVectorV* motionVelocity; + Cm::SpatialVector* acceleration; + PxTransform* poses; + PxQuat* deltaQ; + physx::shdfnd::aos::Mat33V* externalLoads; + physx::shdfnd::aos::Mat33V* internalLoads; + const ArticulationCore* core; + char* scratchMemory; + PxU16 totalDataSize; + PxU16 solverDataSize; + PxU8 linkCount; + PxU8 numInternalConstraints; + PxU16 scratchMemorySize; + }; + + struct PxcFsScratchAllocator + { + char* base; + size_t size; + size_t taken; + PxcFsScratchAllocator(char* p, size_t s) : base(p), size(s), taken(0) {} + + template + static size_t sizeof16() + { + return (sizeof(T) + 15)&~15; + } + + template T* alloc(PxU32 count) + { + size_t s = sizeof16(); + PX_ASSERT(taken + s*count <= size); + T* result = reinterpret_cast(base + taken); + taken += s*count; + return result; + } + }; + + + static const size_t DY_ARTICULATION_IDMASK = DY_ARTICULATION_MAX_SIZE - 1; + +#if PX_VC +#pragma warning(push) +#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + PX_ALIGN_PREFIX(64) + class ArticulationV + { + public: + + // public interface + + ArticulationV(Sc::ArticulationSim* sim, PxArticulationBase::Enum type) : mArticulationSim(sim), + mContext(NULL), + mType(type), + mUpdateSolverData(true), + mDirty(false), + mMaxDepth(0) + {} + + virtual ~ArticulationV() {} + + virtual void onUpdateSolverDesc() + { + } + + virtual bool resize(const PxU32 linkCount); + + virtual void addBody() + { + mAcceleration.pushBack(Cm::SpatialVector(PxVec3(0.f), PxVec3(0.f))); + mUpdateSolverData = true; + } + + virtual void removeBody() + { + mUpdateSolverData = true; + } + + bool updateSolverData() {return mUpdateSolverData;} + + void setDirty(const bool dirty) { mDirty = dirty; } + bool getDirty() { return mDirty; } + + PX_FORCE_INLINE PxU32 getMaxDepth() { return mMaxDepth; } + PX_FORCE_INLINE void setMaxDepth(const PxU32 depth) { mMaxDepth = depth; } + + // solver methods + PX_FORCE_INLINE PxU32 getLinkIndex(ArticulationLinkHandle handle) const { return PxU32(handle&DY_ARTICULATION_IDMASK); } + PX_FORCE_INLINE PxU32 getBodyCount() const { return mSolverDesc.linkCount; } + PX_FORCE_INLINE PxU32 getSolverDataSize() const { return mSolverDesc.solverDataSize; } + PX_FORCE_INLINE PxU32 getTotalDataSize() const { return mSolverDesc.totalDataSize; } + PX_FORCE_INLINE void getSolverDesc(ArticulationSolverDesc& d) const { d = mSolverDesc; } + PX_FORCE_INLINE ArticulationSolverDesc& getSolverDesc() { return mSolverDesc; } + + PX_FORCE_INLINE const ArticulationCore* getCore() const { return mSolverDesc.core; } + PX_FORCE_INLINE PxU16 getIterationCounts() const { return mSolverDesc.core->solverIterationCounts; } + + PX_FORCE_INLINE Sc::ArticulationSim* getArticulationSim() const { return mArticulationSim; } + + PX_FORCE_INLINE PxU32 getType() const { return mType; } + + // get data sizes for allocation at higher levels + virtual void getDataSizes(PxU32 linkCount, + PxU32 &solverDataSize, + PxU32& totalSize, + PxU32& scratchSize) = 0; + + virtual PxU32 getDofs() { return 0; } + + virtual PxU32 getDof(const PxU32 /*linkID*/) { return 0; } + + virtual void applyCache(PxArticulationCache& /*cache*/, const PxArticulationCacheFlags /*flag*/) {} + + virtual void copyInternalStateToCache(PxArticulationCache&/* cache*/, const PxArticulationCacheFlags /*flag*/) {} + + virtual void packJointData(const PxReal* /*maximum*/, PxReal* /*reduced*/) {} + + virtual void unpackJointData(const PxReal* /*reduced*/, PxReal* /*maximum*/) {} + + virtual void initializeCommonData() {} + + virtual void getGeneralizedGravityForce(const PxVec3& /*gravity*/, PxArticulationCache& /*cache*/) {} + + virtual void getCoriolisAndCentrifugalForce(PxArticulationCache& /*cache*/) {} + + virtual void getGeneralizedExternalForce(PxArticulationCache& /*cache*/) {} + + virtual void getJointAcceleration(const PxVec3& /*gravity*/, PxArticulationCache& /*cache*/){} + + virtual void getJointForce(PxArticulationCache& /*cache*/){} + + virtual void getKinematicJacobian(const PxU32 /*linkID*/, PxArticulationCache& /*cache*/){} + + virtual void getCoefficentMatrix(const PxReal /*dt*/, const PxU32 /*linkID*/, const PxContactJoint* /*joints*/, const PxU32 /*nbContacts*/, PxArticulationCache& /*cache*/){} + + virtual void getCoefficentMatrixWithLoopJoints(ArticulationLoopConstraint* /*lConstraints*/, const PxU32 /*nbJoints*/, PxArticulationCache& /*cache*/) {} + + virtual bool getLambda(ArticulationLoopConstraint* /*lConstraints*/, const PxU32 /*nbJoints*/, + PxArticulationCache& /*cache*/, PxArticulationCache& /*initialState*/, const PxReal* /*jointTorque*/, + const PxVec3& /*gravity*/, const PxU32 /*maxIter*/) { return false; } + + virtual void getGeneralizedMassMatrix(PxArticulationCache& /*cache*/){} + virtual void getGeneralizedMassMatrixCRB(PxArticulationCache& /*cache*/){} + + virtual void teleportRootLink(){} + + virtual void getImpulseResponse( + PxU32 linkID, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaV) const = 0; + + virtual void getImpulseSelfResponse( + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1) const = 0; + + + virtual Cm::SpatialVectorV getLinkVelocity(const PxU32 linkID) const = 0; + + virtual Cm::SpatialVectorV getLinkMotionVector(const PxU32 linkID) const = 0; + + virtual PxReal getLinkMaxPenBias(const PxU32 linkID) const = 0; + + virtual void pxcFsApplyImpulse(PxU32 linkID, Ps::aos::Vec3V linear, + Ps::aos::Vec3V angular, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) = 0; + + virtual void pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, + const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, + const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) = 0; + + virtual void solveInternalConstraints(const PxReal dt, const PxReal invDt, Cm::SpatialVectorF* impulses, Cm::SpatialVectorF* DeltaV, + bool velIteration) = 0; + + virtual Cm::SpatialVectorV pxcFsGetVelocity(PxU32 linkID) = 0; + + virtual void pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1) = 0; + + virtual Cm::SpatialVectorV pxcFsGetVelocityTGS(PxU32 linkID) = 0; + + virtual const PxTransform& getCurrentTransform(PxU32 linkID) const= 0; + + virtual const PxQuat& getDeltaQ(PxU32 linkID) const = 0; + + //this is called by island gen to determine whether the articulation should be awake or sleep + virtual Cm::SpatialVector getMotionVelocity(const PxU32 linkID) const = 0; + + void setDyContext(Dy::Context* context) { mContext = context; } + //These variables are used in the constraint partition + PxU16 maxSolverFrictionProgress; + PxU16 maxSolverNormalProgress; + PxU32 solverProgress; + PxU8 numTotalConstraints; + + protected: + Sc::ArticulationSim* mArticulationSim; + Dy::Context* mContext; + PxU32 mType; + ArticulationSolverDesc mSolverDesc; + + Ps::Array mAcceleration; // supplied by Sc-layer to feed into articulations + + bool mUpdateSolverData; + bool mDirty; //any of links update configulations, the boolean will be set to true + PxU32 mMaxDepth; + + private: + PX_NOCOPY(ArticulationV) + } PX_ALIGN_SUFFIX(64); + +#if PX_VC +#pragma warning(pop) +#endif + + PX_FORCE_INLINE ArticulationV* getArticulation(ArticulationLinkHandle handle) + { + return reinterpret_cast(handle & ~DY_ARTICULATION_IDMASK); + } + + PX_FORCE_INLINE bool isArticulationRootLink(ArticulationLinkHandle handle) + { + return !(handle & DY_ARTICULATION_IDMASK); + } + + + } + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp new file mode 100644 index 000000000..d3bee470d --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp @@ -0,0 +1,1665 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsMathUtils.h" +#include "CmConeLimitHelper.h" +#include "DySolverConstraint1D.h" +#include "DyArticulation.h" +#include "DyArticulationHelper.h" +#include "PxsRigidBody.h" +#include "PxcConstraintBlockStream.h" +#include "DyArticulationContactPrep.h" +#include "DyDynamics.h" +#include "DyArticulationReference.h" +#include "DyArticulationPImpl.h" +#include +#include "DyArticulationUtils.h" +#include "foundation/PxProfiler.h" +#include "DyArticulationFnsSimd.h" +#include "DyTGSDynamics.h" +#include "common/PxProfileZone.h" + + +using namespace physx; + +// we encode articulation link handles in the lower bits of the pointer, so the +// articulation has to be aligned, which in an aligned pool means we need to size it +// appropriately + +namespace physx +{ + namespace Dy + { + void SolverCoreRegisterArticulationFns(); + + void SolverCoreRegisterArticulationFnsCoulomb(); + + void PxcFsFlushVelocity(FsData& matrix); + + // we pass this around by value so that when we return from a function the size is unaltered. That means we don't preserve state + // across functions - even though that could be handy to preserve baseInertia and jointTransforms across the solver so that if we + // need to run position projection positions they don't get recomputed. + + void PxcLtbFactor(FsData& m) + { + typedef ArticulationFnsSimd Fns; + LtbRow* rows = getLtbRows(m); + + for (PxU32 i = m.linkCount; --i>0;) + { + LtbRow& b = rows[i]; + PxU32 p = m.parent[i]; + const FsInertia inertia = Fns::invertInertia(b.inertia); + const Mat33V jResponse = Fns::invertSym33(M33Neg(Fns::computeSIS(inertia, b.j1, b.j1))); + b.inertia = inertia; + rows[p].inertia = Fns::multiplySubtract(rows[p].inertia, jResponse, b.j0, b.j0); + b.jResponse = jResponse; + + } + rows[0].inertia = Fns::invertInertia(rows[0].inertia); + } + + +PX_COMPILE_TIME_ASSERT((sizeof(Articulation)&(DY_ARTICULATION_MAX_SIZE-1))==0); + +Articulation::Articulation(Sc::ArticulationSim* sim) +: ArticulationV(sim, PxArticulationBase::eMaximumCoordinate) +, mFsDataBytes(PX_DEBUG_EXP("Articulation::fsData")) +, mInternalLoads(PX_DEBUG_EXP("ScArticulationSim::internalLoads")) +, mExternalLoads(PX_DEBUG_EXP("ScArticulationSim::externalLoads")) +, mScratchMemory(PX_DEBUG_EXP("ScArticulationSim::scratchMemory")) +, mPose(PX_DEBUG_EXP("ScArticulationSim::poses")) +, mDeltaQ(PX_DEBUG_EXP("ScArticulationSim::poses")) +, mMotionVelocity(PX_DEBUG_EXP("ScArticulationSim::motion velocity")) +{ + PX_ASSERT((reinterpret_cast(this) & (DY_ARTICULATION_MAX_SIZE-1))==0); +} + +Articulation::~Articulation() +{ +} + + +#if DY_DEBUG_ARTICULATION + +void Articulation::computeResiduals(const Cm::SpatialVector *v, + const ArticulationJointTransforms* jointTransforms, + bool /*dump*/) const +{ + typedef ArticulationFnsScalar Fns; + + PxReal error = 0, energy = 0; + for(PxU32 i=1;ilinkCount;i++) + { + const ArticulationJointTransforms &b = jointTransforms[i]; + PxU32 parent = mSolverDesc->links[i].parent; + const ArticulationJointCore &j = *mSolverDesc->links[i].inboundJoint; + PX_UNUSED(j); + + Cm::SpatialVector residual = Fns::translateMotion(mSolverDesc->poses[i].p - b.cB2w.p, v[i]) + - Fns::translateMotion(mSolverDesc->poses[parent].p - b.cB2w.p, v[parent]); + + error += residual.linear.magnitudeSquared(); + energy += residual.angular.magnitudeSquared(); + + } +// if(dump) + printf("Energy %f, Error %f\n", energy, error); +} + + +Cm::SpatialVector Articulation::computeMomentum(const FsInertia *inertia) const +{ + typedef ArticulationFnsScalar Fns; + + Cm::SpatialVector *velocity = reinterpret_cast(getVelocity(*mSolverDesc->fsData)); + Cm::SpatialVector m = Cm::SpatialVector::zero(); + for(PxU32 i=0;ilinkCount;i++) + m += Fns::translateForce(mSolverDesc->poses[i].p - mSolverDesc->poses[0].p, ArticulationFnsScalar::multiply(inertia[i], velocity[i])); + return m; +} + + + +void Articulation::checkLimits() const +{ + for(PxU32 i=1;ilinkCount;i++) + { + PxTransform cA2w = mSolverDesc->poses[mSolverDesc->links[i].parent].transform(mSolverDesc->links[i].inboundJoint->parentPose); + PxTransform cB2w = mSolverDesc->poses[i].transform(mSolverDesc->links[i].inboundJoint->childPose); + + PxTransform cB2cA = cA2w.transformInv(cB2w); + + // the relative quat must be the short way round for limits to work... + + if(cB2cA.q.w<0) + cB2cA.q = -cB2cA.q; + + const ArticulationJointCore& j = *mSolverDesc->links[i].inboundJoint; + + PxQuat swing, twist; + if(j.twistLimited || j.swingLimited) + Ps::separateSwingTwist(cB2cA.q, swing, twist); + + if(j.swingLimited) + { + PxReal swingLimitContactDistance = PxMin(j.swingYLimit, j.swingZLimit)/4; + + Cm::ConeLimitHelper eh(PxTan(j.swingYLimit/4), + PxTan(j.swingZLimit/4), + PxTan(swingLimitContactDistance/4)); + + PxVec3 axis; + PxReal error = 0.0f; + if(eh.getLimit(swing, axis, error)) + printf("%u, (%f, %f), %f, (%f, %f, %f), %f\n", i, j.swingYLimit, j.swingZLimit, swingLimitContactDistance, axis.x, axis.y, axis.z, error); + } + +// if(j.twistLimited) +// { +// PxReal tqTwistHigh = PxTan(j.twistLimitHigh/4), +// tqTwistLow = PxTan(j.twistLimitLow/4), +// twistPad = (tqTwistHigh - tqTwistLow)*0.25f; +// //twistPad = j.twistLimitContactDistance; +// +// PxVec3 axis = jointTransforms[i].cB2w.rotate(PxVec3(1,0,0)); +// PxReal tqPhi = Ps::tanHalf(twist.x, twist.w); +// +// if(tqPhi < tqTwistLow + twistPad) +// constraintData.pushBack(ConstraintData(-axis, -(tqTwistLow - tqPhi)*4)); +// +// if(tqPhi > tqTwistHigh - twistPad) +// constraintData.pushBack(ConstraintData(axis, (tqTwistHigh - tqPhi)*4)); +// } + } + puts(""); +} + +#endif + +bool Dy::Articulation::resize(const PxU32 linkCount) +{ + if (mUpdateSolverData) + { + if (linkCount != mSolverDesc.linkCount) + { + PxU32 solverDataSize, totalSize, scratchSize; + getDataSizes(linkCount, solverDataSize, totalSize, scratchSize); + + PX_ASSERT(mFsDataBytes.size() != totalSize); + PX_ASSERT(!(totalSize & 15) && !(solverDataSize & 15)); + mFsDataBytes.resize(totalSize); + + mExternalLoads.resize(linkCount, Ps::aos::M33Identity()); + mInternalLoads.resize(linkCount, Ps::aos::M33Identity()); + mPose.resize(linkCount, PxTransform(PxIdentity)); + + mDeltaQ.resize(linkCount, PxQuat(PxIdentity)); + + mSolverDesc.externalLoads = mExternalLoads.begin(); + mSolverDesc.internalLoads = mInternalLoads.begin(); + + mScratchMemory.resize(scratchSize); + mSolverDesc.scratchMemory = mScratchMemory.begin(); + mSolverDesc.scratchMemorySize = Ps::to16(scratchSize); + + mSolverDesc.solverDataSize = Ps::to16(solverDataSize); + mSolverDesc.totalDataSize = Ps::to16(totalSize); + mSolverDesc.poses = mPose.begin(); + mSolverDesc.deltaQ = mDeltaQ.begin(); + + mMotionVelocity.resize(linkCount, Cm::SpatialVector(PxVec3(0.0f), PxVec3(0.0f))); + + mSolverDesc.motionVelocity = mMotionVelocity.begin(); + + + } + Dy::ArticulationV::resize(linkCount); + + return true; + } + + return false; +} + +bool Dy::ArticulationV::resize(const PxU32 linkCount) +{ + if (!mUpdateSolverData) + return false; + + if (linkCount != mSolverDesc.linkCount) + { + mSolverDesc.acceleration = mAcceleration.begin(); + mSolverDesc.articulation = this; + + } + mUpdateSolverData = false; + return true; +} + + +void PxvRegisterArticulations() +{ + const PxU32 type = PxU32(PxArticulationBase::eMaximumCoordinate); + ArticulationPImpl::sComputeUnconstrainedVelocities[type] = &Articulation::computeUnconstrainedVelocities; + ArticulationPImpl::sUpdateBodies[type] = &Articulation::updateBodies; + ArticulationPImpl::sUpdateBodiesTGS[type] = &Articulation::updateBodies; + ArticulationPImpl::sSaveVelocity[type] = &Articulation::saveVelocity; + ArticulationPImpl::sSaveVelocityTGS[type] = &Articulation::saveVelocityTGS; + + ArticulationPImpl::sUpdateDeltaMotion[type] = &Articulation::recordDeltaMotion; + ArticulationPImpl::sDeltaMotionToMotionVel[type] = &Articulation::deltaMotionToMotionVelocity; + ArticulationPImpl::sComputeUnconstrainedVelocitiesTGS[type] = &Articulation::computeUnconstrainedVelocitiesTGS; + ArticulationPImpl::sSetupInternalConstraintsTGS[type] = &Articulation::setupSolverConstraintsTGS; + + SolverCoreRegisterArticulationFns(); + SolverCoreRegisterArticulationFnsCoulomb(); +} + +void Articulation::getDataSizes(PxU32 linkCount, PxU32 &solverDataSize, PxU32& totalSize, PxU32& scratchSize) +{ + solverDataSize = sizeof(FsData) // header + + sizeof(Cm::SpatialVectorV) * linkCount // velocity + + sizeof(Cm::SpatialVectorV) * linkCount // deferredVelocity + + sizeof(Cm::SpatialVectorV) * linkCount // motion size + + sizeof(Vec3V) * linkCount // deferredSZ + + sizeof(PxReal) * ((linkCount + 15) & 0xFFFFFFF0) // The maxPenBias values + + sizeof(FsJointVectors) * linkCount // joint offsets + + sizeof(FsInertia) // featherstone root inverse inertia + + sizeof(FsRow) * linkCount; // featherstone matrix rows + + totalSize = solverDataSize + + sizeof(LtbRow) * linkCount // lagrange matrix rows + + sizeof(Cm::SpatialVectorV) * linkCount // ref velocity + + sizeof(FsRowAux) * linkCount; + + scratchSize = PxU32(sizeof(FsInertia)*linkCount*3 + + ((sizeof(ArticulationJointTransforms)+15)&~15) * linkCount + + sizeof(Mat33V) * linkCount + + ((sizeof(ArticulationJointTransforms)+15)&~15) * linkCount); + +} + +void Articulation::getImpulseResponse( + PxU32 linkID, + Cm::SpatialVectorF* /*Z*/, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaV) const +{ + const FsData& matrix = *getFsDataPtr(); + ArticulationHelper::getImpulseResponse(matrix, linkID, impulse, deltaV); +} + +void Articulation::getImpulseSelfResponse( + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* /*Z*/, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1) const +{ + const FsData& matrix = *getFsDataPtr(); + ArticulationHelper::getImpulseSelfResponse(matrix, linkID0, reinterpret_cast(impulse0), + reinterpret_cast(deltaV0), linkID1, + reinterpret_cast(impulse1), + reinterpret_cast(deltaV1)); +} + + +Cm::SpatialVectorV Articulation::getLinkVelocity(const PxU32 linkID) const +{ + FsData& matrix = *getFsDataPtr(); + Cm::SpatialVectorV* velocites = addAddr(&matrix, sizeof(FsData)); + return velocites[linkID]; +} + +Cm::SpatialVectorV Articulation::getLinkMotionVector(const PxU32 linkID) const +{ + FsData& matrix = *getFsDataPtr(); + Cm::SpatialVectorV* velocites = addAddr(getDeferredVel(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); + return velocites[linkID]; +} + +Cm::SpatialVector Articulation::getMotionVelocity(const PxU32 linkID) const +{ + PxVec3 linear, angular; + const Cm::SpatialVectorV& motionVelocity = mMotionVelocity[linkID]; + V3StoreU(motionVelocity.linear, linear); + V3StoreU(motionVelocity.angular, angular); + + return Cm::SpatialVector(linear, angular); +} + +PxReal Articulation::getLinkMaxPenBias(const PxU32 linkID) const +{ + FsData& matrix = *getFsDataPtr(); + PxReal* maxPenBias = addAddr(getDeferredSZ(matrix), sizeof(Vec3V) * matrix.linkCount); + return maxPenBias[linkID]; +} + + +PxU32 Articulation::getFsDataSize(PxU32 linkCount) +{ + return sizeof(FsInertia) + sizeof(FsRow) * linkCount; +} + +PxU32 Articulation::getLtbDataSize(PxU32 linkCount) +{ + return sizeof(LtbRow) * linkCount; +} + +void Articulation::setInertia(FsInertia& inertia, + const PxsBodyCore& body, + const PxTransform& pose) +{ + // assumes that elements that are supposed to be zero (i.e. la matrix and off diagonal elements of ll) are zero + + const PxMat33 R(pose.q); + const PxVec3& v = body.inverseInertia; + const PxReal m = 1.0f / body.inverseMass; + V3WriteX(inertia.ll.col0, m); + V3WriteY(inertia.ll.col1, m); + V3WriteZ(inertia.ll.col2, m); + + PX_ALIGN_PREFIX(16) PxMat33 PX_ALIGN_SUFFIX(16) alignedInertia = R * PxMat33::createDiagonal(PxVec3(1.0f / v.x, 1.0f / v.y, 1.0f / v.z)) * R.getTranspose(); + alignedInertia = (alignedInertia + alignedInertia.getTranspose())*0.5f; + inertia.aa = Mat33V_From_PxMat33(alignedInertia); +} + +void Articulation::setJointTransforms(ArticulationJointTransforms& transforms, + const PxTransform& parentPose, + const PxTransform& childPose, + const ArticulationJointCore& joint) +{ + transforms.cA2w = parentPose.transform(joint.parentPose); + transforms.cB2w = childPose.transform(joint.childPose); + transforms.cB2cA = transforms.cA2w.transformInv(transforms.cB2w); + if (transforms.cB2cA.q.w<0) // the relative quat must be the short way round for limits to work... + { + transforms.cB2cA.q = -transforms.cB2cA.q; + transforms.cB2w.q = -transforms.cB2w.q; + } +} + +void Articulation::prepareDataBlock(FsData& fsData, + const ArticulationLink* links, + PxU16 linkCount, + PxTransform* poses, + PxQuat* deltaQ, + FsInertia* baseInertia, + ArticulationJointTransforms* jointTransforms, + PxU32 expectedSize) +{ + PxU32 stateSize = sizeof(FsData) + + sizeof(Cm::SpatialVectorV) * linkCount + + sizeof(Cm::SpatialVectorV) * linkCount + + sizeof(Cm::SpatialVectorV) * linkCount + + sizeof(Vec3V) * linkCount + + sizeof(PxReal) * ((linkCount + 15) & 0xfffffff0); + + PxU32 jointVectorSize = sizeof(FsJointVectors) * linkCount; + + PxU32 fsDataSize = getFsDataSize(linkCount); + PxU32 ltbDataSize = getLtbDataSize(linkCount); + + PxU32 totalSize = stateSize + + jointVectorSize + + fsDataSize + + ltbDataSize + + sizeof(Cm::SpatialVectorV) * linkCount + + sizeof(FsRowAux) * linkCount; + + PX_UNUSED(totalSize); + PX_UNUSED(expectedSize); + PX_ASSERT(expectedSize == 0 || totalSize == expectedSize); + + PxMemZero(&fsData, stateSize); + fsData.jointVectorOffset = PxU16(stateSize); + fsData.fsDataOffset = PxU16(stateSize + jointVectorSize); + fsData.ltbDataOffset = PxU16(stateSize + jointVectorSize + fsDataSize); + fsData.linkCount = linkCount; + + for (PxU32 i = 1; i(getVelocity(fsData)); + Cm::SpatialVector* motionVector = reinterpret_cast(getMotionVector(fsData)); + + PxMemZero(baseInertia, sizeof(FsInertia)*linkCount); + + PxReal* maxPenBias = getMaxPenBias(fsData); + + for (PxU32 i = 0; i(links[i].inboundJoint); + setJointTransforms(jointTransforms[i], poses[links[i].parent], core.body2World, *inboundJoint); + } + } + + FsJointVectors* jointVectors = getJointVectors(fsData); + for (PxU32 i = 1; i Fns; + + PxU32 linkCount = fsData.linkCount; + FsRow* rows = getFsRows(fsData); + FsRowAux* aux = getAux(fsData); + const FsJointVectors* jointVectors = getJointVectors(fsData); + + rows[0].children = links[0].children; + rows[0].pathToRoot = 1; + + PX_ALIGN_PREFIX(16) PxVec4 v[] PX_ALIGN_SUFFIX(16) = { PxVec4(1.f,0,0,0), PxVec4(0,1.f,0,0), PxVec4(0,0,1.f,0) }; + const Vec3V* axes = reinterpret_cast(v); + + for (PxU32 i = 1; i0); + return 1.0f / compliance; +} + +void Articulation::prepareLtbMatrix(FsData& fsData, + const FsInertia* baseInertia, + const PxTransform* poses, + const ArticulationJointTransforms* jointTransforms, + PxReal recipDt) +{ + PxU32 linkCount = fsData.linkCount; + LtbRow* rows = getLtbRows(fsData); + + rows[0].inertia = baseInertia[0]; + + const PxVec3 axis[3] = { PxVec3(1.0f,0.0f,0.0f), PxVec3(0.0f,1.0f,0.0f), PxVec3(0.0f,0.0f,1.0f) }; + for (PxU32 i = 1; i Fns; + + const LtbRow* rows = getLtbRows(m); + PxMemZero(y, m.linkCount * sizeof(Cm::SpatialVectorV)); + + for (PxU32 i = m.linkCount; i-->1;) + { + const LtbRow& r = rows[i]; + const PxU32 p = m.parent[i]; + + const Vec3V t = V3Sub(b[i], Fns::axisDot(r.j1, y[i])); + b[i] = t; + y[p] = Fns::subtract(y[p], Fns::axisMultiply(r.j0, t)); + } + + y[0] = Fns::multiply(rows[0].inertia, y[0]); + + for (PxU32 i = 1; i Fns; + + FloatV isf[DY_ARTICULATION_MAX_SIZE]; + + for (PxU32 i = 1; i(linkCount); + FsInertia*PX_RESTRICT contribToParent = allocator.alloc(linkCount); + + const FsRow*PX_RESTRICT row = getFsRows(matrix); + const FsRowAux*PX_RESTRICT aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_UNUSED(row); + + // gets rid of about 200 LHSs, need to change the matrix format to make this part of it + PxU64 parent[DY_ARTICULATION_MAX_SIZE]; + for (PxU32 i = 0; i1;) + { + const Cm::SpatialVectorV*PX_RESTRICT S = aux[i].S; + + Ps::prefetch(&load[i - 1]); + Ps::prefetch(&jointVectors[i - 1]); + const FsInertia tmp = Fns::propagate(inertia[i], S, load[i], isf[i]); + inertia[parent[i]] = Fns::addInertia(inertia[parent[i]], Fns::translateInertia(jointVectors[i].parentOffset, tmp)); + contribToParent[i] = tmp; + } + + for (PxU32 i = 1; i Fns; + + Cm::SpatialVectorV IS[3]; + + FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + FsInertia* inertia = allocator.alloc(matrix.linkCount); + PxMemCopy(inertia, baseInertia, matrix.linkCount * sizeof(FsInertia)); + + for (PxU32 i = matrix.linkCount; --i>0;) + { + FsRow& r = rows[i]; + const FsRowAux& a = aux[i]; + const FsJointVectors& jv = jointVectors[i]; + + const Mat33V m = Fns::computeSIS(inertia[i], a.S, IS); + const FloatV f = FLoad(isf[i]); + + const Mat33V D = Fns::invertSym33(Mat33V(V3ScaleAdd(load[i].col0, f, m.col0), + V3ScaleAdd(load[i].col1, f, m.col1), + V3ScaleAdd(load[i].col2, f, m.col2))); + r.D = D; + + inertia[matrix.parent[i]] = Fns::addInertia(inertia[matrix.parent[i]], + Fns::translateInertia(jv.parentOffset, Fns::multiplySubtract(inertia[i], D, IS, r.DSI))); + } + + getRootInverseInertia(matrix) = Fns::invertInertia(inertia[0]); +} + +PX_FORCE_INLINE Cm::SpatialVectorV propagateDrivenImpulse(const FsRow& row, + const FsJointVectors& jv, + Vec3V& SZMinusQ, + const Cm::SpatialVectorV& Z, + const Vec3V& Q) +{ + typedef ArticulationFnsSimd Fns; + + SZMinusQ = V3Sub(V3Add(Z.angular, V3Cross(Z.linear, jv.jointOffset)), Q); + Cm::SpatialVectorV result = Fns::translateForce(jv.parentOffset, Z - Fns::axisMultiply(row.DSI, SZMinusQ)); + + return result; +} + +void PxcFsApplyJointDrives(FsData& matrix, + const Vec3V* Q) +{ + typedef ArticulationFnsSimd Fns; + + PX_ASSERT(matrix.linkCount <= DY_ARTICULATION_MAX_SIZE); + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + Cm::SpatialVectorV Z[DY_ARTICULATION_MAX_SIZE]; + Cm::SpatialVectorV dV[DY_ARTICULATION_MAX_SIZE]; + Vec3V SZminusQ[DY_ARTICULATION_MAX_SIZE]; + + PxMemZero(Z, matrix.linkCount * sizeof(Cm::SpatialVectorV)); + + for (PxU32 i = matrix.linkCount; i-->1;) + Z[matrix.parent[i]] += propagateDrivenImpulse(rows[i], jointVectors[i], SZminusQ[i], Z[i], Q[i]); + + + dV[0] = Fns::multiply(getRootInverseInertia(matrix), -Z[0]); + + for (PxU32 i = 1; i Fns; + + PX_ASSERT(matrix.linkCount <= DY_ARTICULATION_MAX_SIZE); + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + Cm::SpatialVectorV dV[DY_ARTICULATION_MAX_SIZE]; + Vec3V SZ[DY_ARTICULATION_MAX_SIZE]; + + for (PxU32 i = matrix.linkCount; i-->1;) + Z[matrix.parent[i]] += Fns::propagateImpulse(rows[i], jointVectors[i], SZ[i], Z[i], aux[i]); + + dV[0] = Fns::multiply(getRootInverseInertia(matrix), -Z[0]); + + for (PxU32 i = 1; i(desc.articulation); + FsData& fsData = *articulation->getFsDataPtr(); + PxTransform* poses = desc.poses; + PxQuat* deltaQ = desc.deltaQ; + + { + PX_PROFILE_ZONE("Articulations.prepareDataBlock", contextID); + prepareDataBlock(fsData, links, linkCount, poses, deltaQ, baseInertia, jointTransforms, desc.totalDataSize); + } + + const PxReal recipDt = 1.0f / dt; + + Cm::SpatialVectorV* velocity = getVelocity(fsData); + + { + + PX_PROFILE_ZONE("Articulations.setupProject", contextID); + + PxMemZero(getLtbRows(fsData), getLtbDataSize(linkCount)); + prepareLtbMatrix(fsData, baseInertia, poses, jointTransforms, recipDt); + + PxcLtbFactor(fsData); + + Vec3V b[DY_ARTICULATION_MAX_SIZE]; + PxcLtbComputeJv(b, fsData, velocity); + + LtbRow* rows = getLtbRows(fsData); + for (PxU32 i = 1; i(&fsData, fsData.fsDataOffset), getFsDataSize(linkCount)); + prepareFsData(fsData, links); + } + + { + PX_PROFILE_ZONE("Articulations.setupDrives", contextID); + + if (!(desc.core->externalDriveIterations & 0x80000000)) + PxMemZero(desc.externalLoads, sizeof(Mat33V) * linkCount); + + if (!(desc.core->internalDriveIterations & 0x80000000)) + PxMemZero(desc.internalLoads, sizeof(Mat33V) * linkCount); + + PxReal isf[DY_ARTICULATION_MAX_SIZE], esf[DY_ARTICULATION_MAX_SIZE]; // spring factors + Vec3V drive[DY_ARTICULATION_MAX_SIZE]; + + bool externalEqualsInternalCompliance = (desc.core->internalDriveIterations & 0xffff) == (desc.core->externalDriveIterations & 0xffff); + for (PxU32 i = 1; i(*links[i].inboundJoint); + isf[i] = (1 + j.damping * dt + j.spring * dt * dt) * getResistance(j.internalCompliance); + esf[i] = (1 + j.damping * dt + j.spring * dt * dt) * getResistance(j.externalCompliance); + + externalEqualsInternalCompliance = externalEqualsInternalCompliance && j.internalCompliance == j.externalCompliance; + } + + { + PX_PROFILE_ZONE("Articulations.jointInternalLoads", contextID); + PxcFsComputeJointLoadsSimd(fsData, baseInertia, desc.internalLoads, isf, linkCount, desc.core->internalDriveIterations & 0xffff, allocator); + + } + + { + PX_PROFILE_ZONE("Articulations.propagateDrivenInertia", contextID); + PxcFsPropagateDrivenInertiaSimd(fsData, baseInertia, isf, desc.internalLoads, allocator); + } + + { + PX_PROFILE_ZONE("Articulations.computeJointDrives", contextID); + computeJointDrives(fsData, drive, links, poses, jointTransforms, desc.internalLoads, dt); + } + + { + PX_PROFILE_ZONE("Articulations.applyJointDrives", contextID); + PxcFsApplyJointDrives(fsData, drive); + } + + if (!externalEqualsInternalCompliance) + { + { + PX_PROFILE_ZONE("Articulations.jointExternalLoads", contextID); + PxcFsComputeJointLoadsSimd(fsData, baseInertia, desc.externalLoads, esf, linkCount, desc.core->externalDriveIterations & 0xffff, allocator); + } + + { + PX_PROFILE_ZONE("Articulations.propagateDrivenInertia", contextID); + PxcFsPropagateDrivenInertiaSimd(fsData, baseInertia, esf, desc.externalLoads, allocator); + } + } + } + + { + PX_PROFILE_ZONE("Articulations.applyExternalImpulses", contextID); + Cm::SpatialVectorV Z[DY_ARTICULATION_MAX_SIZE]; + + FloatV h = FLoad(dt); + + Cm::SpatialVector* acceleration = desc.acceleration; + + const Vec3V vGravity = V3LoadU(gravity); + + for (PxU32 i = 0; imInternalFlags & PxcRigidBody::eDISABLE_GRAVITY)) + linearAccel = V3Add(linearAccel, vGravity); + Cm::SpatialVectorV a(linearAccel, V3LoadA(acceleration[i].angular)); + Z[i] = -ArticulationFnsSimd::multiply(baseInertia[i], a) * h; + //KS - zero accelerations to ensure they don't get re-applied next frame if nothing touches them again. + acceleration[i].linear = PxVec3(0.f); acceleration[i].angular = PxVec3(0.f); + } + + applyImpulses(fsData, Z, getVelocity(fsData)); + } + + // save off the motion velocity in case there are no constraints with the articulation + + PxMemCopy(desc.motionVelocity, velocity, linkCount * sizeof(Cm::SpatialVectorV)); + + // set up for deferred-update solve + + fsData.dirty = 0; + + // solver progress counters + maxSolverNormalProgress = 0; + maxSolverFrictionProgress = 0; + solverProgress = 0; + + +#if DY_ARTICULATION_DEBUG_VERIFY + for (PxU32 i = 0; i(*desc.articulation); + + PxcFsScratchAllocator fsAllocator(desc.scratchMemory, desc.scratchMemorySize); + FsInertia* PX_RESTRICT baseInertia = fsAllocator.alloc(desc.linkCount); + ArticulationJointTransforms* PX_RESTRICT jointTransforms = fsAllocator.alloc(desc.linkCount); + + articulation.computeUnconstrainedVelocitiesInternal(desc, dt, gravity, contextID, baseInertia, jointTransforms, fsAllocator); + + { + PX_PROFILE_ZONE("Articulations.setupConstraints", contextID); + return ArticulationHelper::setupSolverConstraints(articulation, desc.solverDataSize, allocator, constraintDesc, links, jointTransforms, dt, acCount); + } + +} + + +void Articulation::computeUnconstrainedVelocitiesTGS( + const ArticulationSolverDesc& desc, + PxReal dt, const PxVec3& gravity, + PxU64 contextID, Cm::SpatialVectorF* /*Z*/, Cm::SpatialVectorF* /*deltaV*/) +{ + Articulation& articulation = static_cast(*desc.articulation); + PxcFsScratchAllocator allocator(desc.scratchMemory, desc.scratchMemorySize); + FsInertia* PX_RESTRICT baseInertia = allocator.alloc(desc.linkCount); + ArticulationJointTransforms* PX_RESTRICT jointTransforms = allocator.alloc(desc.linkCount); + + articulation.computeUnconstrainedVelocitiesInternal(desc, dt, gravity, + contextID, baseInertia, jointTransforms, allocator); +} + + +void Articulation::computeJointDrives(FsData& fsData, + Ps::aos::Vec3V* drives, + const ArticulationLink* links, + const PxTransform* poses, + const ArticulationJointTransforms* transforms, + const Ps::aos::Mat33V* loads, + PxReal dt) +{ + typedef ArticulationFnsScalar Fns; + + const PxU32 linkCount = fsData.linkCount; + const Cm::SpatialVector* velocity = reinterpret_cast(getVelocity(fsData)); + + for (PxU32 i = 1; i(*links[i].inboundJoint); + + const Cm::SpatialVector currentVel = Fns::translateMotion(poses[i].p - b.cA2w.p, velocity[i]) + - Fns::translateMotion(poses[parent].p - b.cA2w.p, velocity[parent]); + + // we want the quat such that q * cB2cA = targetPosition + PxVec3 rotVec; + if (j.driveType == PxU8(PxArticulationJointDriveType::eERROR)) + rotVec = j.targetPosition.getImaginaryPart(); + else + rotVec = Ps::log(j.targetPosition * b.cB2cA.q.getConjugate()); // as a rotation vector + + // NM's Tests indicate behavior is better without the term commented out below, even though + // an implicit spring derivation suggests it should be there. + + const PxVec3 posError = b.cA2w.rotate(rotVec); // - currentVel.angular * 0.5f * dt + const PxVec3 velError = b.cA2w.rotate(j.targetVelocity) - currentVel.angular; + + drives[i] = M33MulV3(loads[i], V3LoadU((j.spring * posError + j.damping * velError) * dt * getResistance(j.internalCompliance))); + } +} + + +void Articulation::saveVelocity(const ArticulationSolverDesc& desc, Cm::SpatialVectorF* /*deltaV*/) +{ + Vec3V b[DY_ARTICULATION_MAX_SIZE]; + + Articulation* articulation = static_cast(desc.articulation); + FsData& m = *articulation->getFsDataPtr(); + + Cm::SpatialVectorV* velocity = getVelocity(m); + PxcFsFlushVelocity(m); + + // save off the motion velocity + + for (PxU32 i = 0; i(desc.articulation); + FsData& m = *articulation->getFsDataPtr(); + + Cm::SpatialVectorV* velocity = getVelocity(m); + PxcFsFlushVelocity(m); + + const FloatV invDt = FLoad(invDtF32); + + Cm::SpatialVectorV* motionVector = getMotionVector(m); + + // save off the motion velocity + + for (PxU32 i = 0; i(desc.articulation); + FsData& m = *articulation->getFsDataPtr(); + + PxQuat* deltaQ = desc.deltaQ; + + Cm::SpatialVectorV* velocity = getVelocity(m); + Cm::SpatialVectorV* deltaMotion = getMotionVector(m); + PxcFsFlushVelocity(m); + + const FloatV fDt = FLoad(dt); + + for (PxU32 i = 0; i(desc.articulation); + FsData& m = *articulation->getFsDataPtr(); + + Cm::SpatialVectorV* deltaMotion = getMotionVector(m); + + Cm::SpatialVectorV* velocity = getVelocity(m); + + const FloatV fInvDt = FLoad(invDt); + + for (PxU32 i = 0; i(getRefVelocity(matrix)), linkID, reinterpret_cast(imp)); + } +#endif + + FsData& matrix = *getFsDataPtr(); + Vec3V linZ = V3Neg(linear); + Vec3V angZ = V3Neg(angular); + + const FsRow *rows = getFsRows(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + +#if DY_ARTICULATION_DEBUG_VERIFY + const FsRowAux *aux = getAux(matrix); +#endif + Vec3V *deferredSZ = getDeferredSZ(matrix); + + for (PxU32 i = linkID; i != 0; i = matrix.parent[i]) + { + const FsRow &row = rows[i]; + const FsJointVectors& jv = jointVectors[i]; + +#if DY_ARTICULATION_DEBUG_VERIFY + PxVec3 SZcheck; + Cm::SpatialVector Zcheck = ArticulationRef::propagateImpulse(row, jv, SZcheck, SpV(linZ, angZ), aux[i]); +#endif + + Vec3V SZ = V3Add(angZ, V3Cross(linZ, jv.jointOffset)); + Vec3V lrLinear = V3Sub(linZ, V3ScaleAdd(row.DSI[0].linear, V3GetX(SZ), + V3ScaleAdd(row.DSI[1].linear, V3GetY(SZ), + V3Scale(row.DSI[2].linear, V3GetZ(SZ))))); + + Vec3V lrAngular = V3Sub(angZ, V3ScaleAdd(row.DSI[0].angular, V3GetX(SZ), + V3ScaleAdd(row.DSI[1].angular, V3GetY(SZ), + V3Scale(row.DSI[2].angular, V3GetZ(SZ))))); + + linZ = lrLinear; + angZ = V3Add(lrAngular, V3Cross(jv.parentOffset, lrLinear)); + deferredSZ[i] = V3Add(deferredSZ[i], SZ); + + PX_ASSERT(Ps::aos::isFiniteVec3V(linZ)); + PX_ASSERT(Ps::aos::isFiniteVec3V(angZ)); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector Z = SpV(linZ, angZ); + PX_ASSERT((Z - Zcheck).magnitude()<1e-4*PxMax(Zcheck.magnitude(), 1.0f)); + PX_ASSERT(((PxVec3&)SZ - SZcheck).magnitude()<1e-4*PxMax(SZcheck.magnitude(), 1.0f)); +#endif + } + + matrix.deferredZ.linear = V3Add(matrix.deferredZ.linear, linZ); + matrix.deferredZ.angular = V3Add(matrix.deferredZ.angular, angZ); + + matrix.dirty |= rows[linkID].pathToRoot; +} + + +void Articulation::pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1) +{ + //Common case - linkID = parent of linkID1... + //In this case, we compute the delta velocity between the 2 and return + + v0 = pxcFsGetVelocity(linkID); + v1 = pxcFsGetVelocity(linkID1); +} + +void Articulation::pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, + const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, + const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) +{ + pxcFsApplyImpulse(linkID, linear, angular, Z, deltaV); + pxcFsApplyImpulse(linkID2, linear2, angular2, Z, deltaV); +} + + +void Articulation::solveInternalConstraints(const PxReal /*dt*/, const PxReal /*invDt*/, Cm::SpatialVectorF* /*impulses*/, + Cm::SpatialVectorF* /*DeltaV*/, bool) +{ +} + + +Cm::SpatialVectorV Articulation::pxcFsGetVelocity(PxU32 linkID) +{ + FsData& matrix = *getFsDataPtr(); + const FsRow *rows = getFsRows(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + +#if DY_ARTICULATION_DEBUG_VERIFY + const FsRowAux *aux = getAux(matrix); +#endif + Cm::SpatialVectorV* PX_RESTRICT V = getVelocity(matrix); + + // find the dirty node on the path (including the root) with the lowest index + ArticulationBitField toUpdate = rows[linkID].pathToRoot & matrix.dirty; + + + if (toUpdate) + { + // store the dV elements densely and use an array map to decode - hopefully cache friendlier + PxU32 indexToStackLoc[DY_ARTICULATION_MAX_SIZE], count = 0; + Cm::SpatialVectorV dVStack[DY_ARTICULATION_MAX_SIZE]; + + ArticulationBitField ignoreNodes = (toUpdate & (0 - toUpdate)) - 1; + //PX_ASSERT(ignoreNodes == 0); + ArticulationBitField path = rows[linkID].pathToRoot & ~ignoreNodes, p = path; + ArticulationBitField newDirty = 0; + + Vec3V ldV = V3Zero(), adV = V3Zero(); + Cm::SpatialVectorV* PX_RESTRICT defV = getDeferredVel(matrix); + Vec3V* PX_RESTRICT SZ = getDeferredSZ(matrix); + + if (p & 1) + { + const FsInertia &m = getRootInverseInertia(matrix); + Vec3V lZ = V3Neg(matrix.deferredZ.linear); + Vec3V aZ = V3Neg(matrix.deferredZ.angular); + + ldV = V3Add(M33MulV3(m.ll, lZ), M33MulV3(m.la, aZ)); + adV = V3Add(M33TrnspsMulV3(m.la, lZ), M33MulV3(m.aa, aZ)); + + V[0].linear = V3Add(V[0].linear, ldV); + V[0].angular = V3Add(V[0].angular, adV); + + matrix.deferredZ.linear = V3Zero(); + matrix.deferredZ.angular = V3Zero(); + + indexToStackLoc[0] = count; + Cm::SpatialVectorV &e = dVStack[count++]; + + e.linear = ldV; + e.angular = adV; + + newDirty = rows[0].children; + p--; + } + + + while (p) // using "for(;p;p &= (p-1))" here generates LHSs from the ArticulationLowestSetBit + { + PxU32 i = ArticulationLowestSetBit(p); + const FsJointVectors& jv = jointVectors[i]; + + p &= (p - 1); + + const FsRow* PX_RESTRICT row = rows + i; + + ldV = V3Add(ldV, defV[i].linear); + adV = V3Add(adV, defV[i].angular); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector dVcheck = ArticulationRef::propagateVelocity(*row, jv, (PxVec3&)SZ[i], SpV(ldV, adV), aux[i]); +#endif + + Vec3V DSZ = M33MulV3(row->D, SZ[i]); + + Vec3V lW = V3Add(ldV, V3Cross(adV, jv.parentOffset)); + Vec3V aW = adV; + + const Cm::SpatialVectorV*PX_RESTRICT DSI = row->DSI; + Vec3V lN = V3Merge(V3Dot(DSI[0].linear, lW), V3Dot(DSI[1].linear, lW), V3Dot(DSI[2].linear, lW)); + Vec3V aN = V3Merge(V3Dot(DSI[0].angular, aW), V3Dot(DSI[1].angular, aW), V3Dot(DSI[2].angular, aW)); + + Vec3V n = V3Add(V3Add(lN, aN), DSZ); + + ldV = V3Sub(lW, V3Cross(jv.jointOffset, n)); + adV = V3Sub(aW, n); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector dV = SpV(ldV, adV); + PX_ASSERT((dV - dVcheck).magnitude()<1e-4*PxMax(dVcheck.magnitude(), 1.0f)); +#endif + + V[i].linear = V3Add(V[i].linear, ldV); + V[i].angular = V3Add(V[i].angular, adV); + + defV[i].linear = V3Zero(); + defV[i].angular = V3Zero(); + SZ[i] = V3Zero(); + + indexToStackLoc[i] = count; + Cm::SpatialVectorV &e = dVStack[count++]; + newDirty |= rows[i].children; + + e.linear = ldV; + e.angular = adV; + } + + for (ArticulationBitField defer = newDirty&~path; defer; defer &= (defer - 1)) + { + PxU32 i = ArticulationLowestSetBit(defer); + PxU32 parent = indexToStackLoc[matrix.parent[i]]; + + defV[i].linear = V3Add(defV[i].linear, dVStack[parent].linear); + defV[i].angular = V3Add(defV[i].angular, dVStack[parent].angular); + } + + matrix.dirty = (matrix.dirty | newDirty)&~path; + } +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector v = reinterpret_cast(V[linkID]); + Cm::SpatialVector rv = reinterpret_cast(getRefVelocity(matrix)[linkID]); + PX_ASSERT((v - rv).magnitude()<1e-4f * PxMax(rv.magnitude(), 1.0f)); +#endif + + return V[linkID]; +} + +//Cm::SpatialVectorV PxcFsGetVelocity(Articulation &articulation, +// PxU32 linkID) +//{ +// FsData& matrix = *articulation.getFsDataPtr(); +// const FsRow *rows = getFsRows(matrix); +// const FsJointVectors* jointVectors = getJointVectors(matrix); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// const FsRowAux *aux = getAux(matrix); +//#endif +// Cm::SpatialVectorV* PX_RESTRICT V = getVelocity(matrix); +// +// // find the dirty node on the path (including the root) with the lowest index +// ArticulationBitField toUpdate = rows[linkID].pathToRoot & matrix.dirty; +// +// +// if (toUpdate) +// { +// // store the dV elements densely and use an array map to decode - hopefully cache friendlier +// PxU32 indexToStackLoc[DY_ARTICULATION_MAX_SIZE], count = 0; +// Cm::SpatialVectorV dVStack[DY_ARTICULATION_MAX_SIZE]; +// +// ArticulationBitField ignoreNodes = (toUpdate & (0 - toUpdate)) - 1; +// ArticulationBitField path = rows[linkID].pathToRoot & ~ignoreNodes, p = path; +// ArticulationBitField newDirty = 0; +// +// Vec3V ldV = V3Zero(), adV = V3Zero(); +// Cm::SpatialVectorV* PX_RESTRICT defV = getDeferredVel(matrix); +// Vec3V* PX_RESTRICT SZ = getDeferredSZ(matrix); +// +// if (p & 1) +// { +// const FsInertia &m = getRootInverseInertia(matrix); +// Vec3V lZ = V3Neg(matrix.deferredZ.linear); +// Vec3V aZ = V3Neg(matrix.deferredZ.angular); +// +// ldV = V3Add(M33MulV3(m.ll, lZ), M33MulV3(m.la, aZ)); +// adV = V3Add(M33TrnspsMulV3(m.la, lZ), M33MulV3(m.aa, aZ)); +// +// V[0].linear = V3Add(V[0].linear, ldV); +// V[0].angular = V3Add(V[0].angular, adV); +// +// matrix.deferredZ.linear = V3Zero(); +// matrix.deferredZ.angular = V3Zero(); +// +// indexToStackLoc[0] = count; +// Cm::SpatialVectorV &e = dVStack[count++]; +// +// e.linear = ldV; +// e.angular = adV; +// +// newDirty = rows[0].children; +// p--; +// } +// +// +// while (p) // using "for(;p;p &= (p-1))" here generates LHSs from the ArticulationLowestSetBit +// { +// PxU32 i = ArticulationLowestSetBit(p); +// const FsJointVectors& jv = jointVectors[i]; +// +// p &= (p - 1); +// +// const FsRow* PX_RESTRICT row = rows + i; +// +// ldV = V3Add(ldV, defV[i].linear); +// adV = V3Add(adV, defV[i].angular); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector dVcheck = ArticulationRef::propagateVelocity(*row, jv, (PxVec3&)SZ[i], SpV(ldV, adV), aux[i]); +//#endif +// +// Vec3V DSZ = M33MulV3(row->D, SZ[i]); +// +// Vec3V lW = V3Add(ldV, V3Cross(adV, jv.parentOffset)); +// Vec3V aW = adV; +// +// const Cm::SpatialVectorV*PX_RESTRICT DSI = row->DSI; +// Vec3V lN = V3Merge(V3Dot(DSI[0].linear, lW), V3Dot(DSI[1].linear, lW), V3Dot(DSI[2].linear, lW)); +// Vec3V aN = V3Merge(V3Dot(DSI[0].angular, aW), V3Dot(DSI[1].angular, aW), V3Dot(DSI[2].angular, aW)); +// +// Vec3V n = V3Add(V3Add(lN, aN), DSZ); +// +// ldV = V3Sub(lW, V3Cross(jv.jointOffset, n)); +// adV = V3Sub(aW, n); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector dV = SpV(ldV, adV); +// PX_ASSERT((dV - dVcheck).magnitude()<1e-4*PxMax(dVcheck.magnitude(), 1.0f)); +//#endif +// +// V[i].linear = V3Add(V[i].linear, ldV); +// V[i].angular = V3Add(V[i].angular, adV); +// +// defV[i].linear = V3Zero(); +// defV[i].angular = V3Zero(); +// SZ[i] = V3Zero(); +// +// indexToStackLoc[i] = count; +// Cm::SpatialVectorV &e = dVStack[count++]; +// newDirty |= rows[i].children; +// +// e.linear = ldV; +// e.angular = adV; +// } +// +// for (ArticulationBitField defer = newDirty&~path; defer; defer &= (defer - 1)) +// { +// PxU32 i = ArticulationLowestSetBit(defer); +// PxU32 parent = indexToStackLoc[matrix.parent[i]]; +// +// defV[i].linear = V3Add(defV[i].linear, dVStack[parent].linear); +// defV[i].angular = V3Add(defV[i].angular, dVStack[parent].angular); +// } +// +// matrix.dirty = (matrix.dirty | newDirty)&~path; +// } +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector v = reinterpret_cast(V[linkID]); +// Cm::SpatialVector rv = reinterpret_cast(getRefVelocity(matrix)[linkID]); +// PX_ASSERT((v - rv).magnitude()<1e-4f * PxMax(rv.magnitude(), 1.0f)); +//#endif +// +// return V[linkID]; +//} + +void Articulation::updateBodies(const ArticulationSolverDesc& desc, PxReal dt) +{ + Articulation* articulation = static_cast(desc.articulation); + FsData& fsData = *articulation->getFsDataPtr(); + const ArticulationCore& core = *desc.core; + const ArticulationLink* links = desc.links; + PxTransform* poses = desc.poses; + Cm::SpatialVectorV* motionVelocity = desc.motionVelocity; + + Vec3V b[DY_ARTICULATION_MAX_SIZE]; + + PxU32 linkCount = fsData.linkCount; + + PxcFsFlushVelocity(fsData); + PxcLtbComputeJv(b, fsData, getVelocity(fsData)); + PxcLtbProject(fsData, getVelocity(fsData), b); + + // update positions + PxcFsScratchAllocator allocator(desc.scratchMemory, desc.scratchMemorySize); + PxTransform* PX_RESTRICT oldPose = allocator.alloc(desc.linkCount); + + for (PxU32 i = 0; i(motionVelocity[i].linear); + const PxVec3& av = reinterpret_cast(motionVelocity[i].angular); + oldPose[i] = poses[i]; + poses[i] = PxTransform(poses[i].p + lv * dt, Ps::exp(av*dt) * poses[i].q); + } + + bool projected = false; + const PxReal recipDt = 1.0f / dt; + + FsInertia* PX_RESTRICT baseInertia = allocator.alloc(desc.linkCount); + ArticulationJointTransforms* PX_RESTRICT jointTransforms = allocator.alloc(desc.linkCount); + + for (PxU32 iterations = 0; iterations < core.maxProjectionIterations; iterations++) + { + PxReal maxSeparation = -PX_MAX_F32; + for (PxU32 i = 1; i(*links[i].inboundJoint); + maxSeparation = PxMax(maxSeparation, + (poses[links[i].parent].transform(j.parentPose).p - + poses[i].transform(j.childPose).p).magnitude()); + } + + if (maxSeparation <= core.separationTolerance) + break; + + projected = true; + + // we go around again, finding velocities which pull us back together - this + // form of projection is momentum-preserving but slow compared to hierarchical + // projection + + PxMemZero(baseInertia, sizeof(FsInertia)*linkCount); + + setInertia(baseInertia[0], *links[0].bodyCore, poses[0]); + for (PxU32 i = 1; i(*links[i].inboundJoint)); + } + + articulation->prepareLtbMatrix(fsData, baseInertia, poses, jointTransforms, recipDt); + PxcLtbFactor(fsData); + + LtbRow* rows = getLtbRows(fsData); + + for (PxU32 i = 1; i(motionVelocity[i].linear); + const PxVec3& av = reinterpret_cast(motionVelocity[i].angular); + poses[i] = PxTransform(poses[i].p + lv * dt, Ps::exp(av*dt) * poses[i].q); + } + } + + if (projected) + { + // recompute motion velocities. + for (PxU32 i = 0; ibody2World = poses[i]; + + V3StoreA(velocity[i].linear, links[i].bodyCore->linearVelocity); + V3StoreA(velocity[i].angular, links[i].bodyCore->angularVelocity); + } +} + + +void PxvArticulationDriveCache::initialize( + FsData &fsData, + PxU16 linkCount, + const ArticulationLink* links, + PxReal compliance, + PxU32 iterations, + char* scratchMemory, + PxU32 scratchMemorySize) +{ + PxcFsScratchAllocator allocator(scratchMemory, scratchMemorySize); + FsInertia* PX_RESTRICT baseInertia = allocator.alloc(linkCount); + ArticulationJointTransforms* PX_RESTRICT jointTransforms = allocator.alloc(linkCount); + PxTransform* PX_RESTRICT poses = allocator.alloc(linkCount); + PxQuat* PX_RESTRICT deltaQ = allocator.alloc(linkCount); + Mat33V* PX_RESTRICT jointLoads = allocator.alloc(linkCount); + + PxReal springFactor[DY_ARTICULATION_MAX_SIZE]; // spring factors + + Articulation::prepareDataBlock(fsData, links, linkCount, poses, deltaQ, baseInertia, jointTransforms, 0); + + PxMemZero(addAddr(&fsData, fsData.fsDataOffset), Articulation::getFsDataSize(linkCount)); + Articulation::prepareFsData(fsData, links); + + springFactor[0] = 0.0f; + for (PxU32 i = 1; i 1e10f) //can set as low as 0. + { + t0 *= PxRecipSqrt(ll); + t1 = unitNormal.cross(t0); + } + else + Ps::normalToTangents(unitNormal, t0, t1); //fallback +} + +PxReal SolverExtBody::projectVelocity(const PxVec3& linear, const PxVec3& angular) const +{ + if(mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + return mBodyData->projectVelocity(linear, angular); + } + else + { + const Cm::SpatialVectorV velocity = mArticulation->getLinkVelocity(mLinkIndex); + + FloatV fv = velocity.dot(Cm::SpatialVector(linear, angular)); + + PxF32 f; + FStore(fv, &f); + return f; + /*PxF32 f; + FStore(getVelocity(*mFsData)[mLinkIndex].dot(Cm::SpatialVector(linear, angular)), &f); + return f;*/ + } +} + +PxVec3 SolverExtBody::getLinVel() const +{ + if(mLinkIndex == PxSolverConstraintDesc::NO_LINK) + return mBodyData->linearVelocity; + else + { + Vec3V linear = mArticulation->getLinkVelocity(mLinkIndex).linear; + + PxVec3 result; + V3StoreU(linear, result); + /*PxVec3 result; + V3StoreU(getVelocity(*mFsData)[mLinkIndex].linear, result);*/ + return result; + } +} + + +PxVec3 SolverExtBody::getAngVel() const +{ + if(mLinkIndex == PxSolverConstraintDesc::NO_LINK) + return mBodyData->angularVelocity; + else + { + + Vec3V angular = mArticulation->getLinkVelocity(mLinkIndex).angular; + + PxVec3 result; + V3StoreU(angular, result); + /*PxVec3 result; + V3StoreU(getVelocity(*mFsData)[mLinkIndex].angular, result);*/ + return result; + } +} + +Cm::SpatialVector createImpulseResponseVector(const PxVec3& linear, const PxVec3& angular, const SolverExtBody& body) +{ + if(body.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + return Cm::SpatialVector(linear, body.mBodyData->sqrtInvInertia * angular); + } + return Cm::SpatialVector(linear, angular); +} + +PxReal getImpulseResponse(const SolverExtBody& b0, const Cm::SpatialVector& impulse0, Cm::SpatialVector& deltaV0, PxReal dom0, PxReal angDom0, + const SolverExtBody& b1, const Cm::SpatialVector& impulse1, Cm::SpatialVector& deltaV1, PxReal dom1, PxReal angDom1, + Cm::SpatialVectorF* Z, bool allowSelfCollision) +{ + PxReal response; + allowSelfCollision = true; + // right now self-collision with contacts crashes the solver + + //KS - knocked this out to save some space on SPU + if(allowSelfCollision && b0.mLinkIndex!=PxSolverConstraintDesc::NO_LINK && b0.mArticulation == b1.mArticulation && 0) + { + /*ArticulationHelper::getImpulseSelfResponse(*b0.mFsData,b0.mLinkIndex, impulse0, deltaV0, + b1.mLinkIndex, impulse1, deltaV1);*/ + + b0.mArticulation->getImpulseSelfResponse(b0.mLinkIndex, b1.mLinkIndex, Z, impulse0.scale(dom0, angDom0), impulse1.scale(dom1, angDom1), + deltaV0, deltaV1); + + response = impulse0.dot(deltaV0) + impulse1.dot(deltaV1); + + } + else + { + + if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + deltaV0.linear = impulse0.linear * b0.mBodyData->invMass * dom0; + deltaV0.angular = impulse0.angular * angDom0; + } + else + { + b0.mArticulation->getImpulseResponse(b0.mLinkIndex, Z, impulse0.scale(dom0, angDom0), deltaV0); + } + + response = impulse0.dot(deltaV0); + if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + deltaV1.linear = impulse1.linear * b1.mBodyData->invMass * dom1; + deltaV1.angular = impulse1.angular * angDom1; + } + else + { + b1.mArticulation->getImpulseResponse( b1.mLinkIndex, Z, impulse1.scale(dom1, angDom1), deltaV1); + } + response += impulse1.dot(deltaV1); + } + + return response; +} + + +void setupFinalizeExtSolverContacts( + const ContactPoint* buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const SolverExtBody& b0, + const SolverExtBody& b1, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + const PxReal restDist, + PxU8* frictionDataPtr, + PxReal ccdMaxContactDist, + Cm::SpatialVectorF* Z) +{ + // NOTE II: the friction patches are sparse (some of them have no contact patches, and + // therefore did not get written back to the cache) but the patch addresses are dense, + // corresponding to valid patches + + /*const bool haveFriction = PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0;*/ + + const FloatV ccdMaxSeparation = FLoad(ccdMaxContactDist); + + PxU8* PX_RESTRICT ptr = workspace; + + const FloatV zero = FZero(); + + //KS - TODO - this should all be done in SIMD to avoid LHS + //const PxF32 maxPenBias0 = b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b0.mBodyData->penBiasClamp : getMaxPenBias(*b0.mFsData)[b0.mLinkIndex]; + //const PxF32 maxPenBias1 = b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b1.mBodyData->penBiasClamp : getMaxPenBias(*b1.mFsData)[b1.mLinkIndex]; + + PxF32 maxPenBias0 = b0.mBodyData->penBiasClamp; + PxF32 maxPenBias1 = b1.mBodyData->penBiasClamp; + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias0 = b0.mArticulation->getLinkMaxPenBias(b0.mLinkIndex); + } + + if (b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias1 = b1.mArticulation->getLinkMaxPenBias(b1.mLinkIndex); + } + + const FloatV maxPenBias = FLoad(PxMax(maxPenBias0, maxPenBias1)); + + + const PxReal d0 = invMassScale0; + const PxReal d1 = invMassScale1; + + const PxReal angD0 = invInertiaScale0; + const PxReal angD1 = invInertiaScale1; + + Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4Zero(); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(d0)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(d1)); + + const FloatV restDistance = FLoad(restDist); + + PxU32 frictionPatchWritebackAddrIndex = 0; + PxU32 contactWritebackCount = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + const FloatV invDt = FLoad(invDtF32); + const FloatV p8 = FLoad(0.8f); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + + const FloatV invDtp8 = FMul(invDt, p8); + + PxU8 flags = 0; + + for(PxU32 i=0;irestitution; + + const PxReal staticFriction = contactBase0->staticFriction; + const PxReal dynamicFriction = contactBase0->dynamicFriction; + const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); + + SolverContactHeader* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeader); + + + Ps::prefetchLine(ptr + 128); + Ps::prefetchLine(ptr + 256); + Ps::prefetchLine(ptr + 384); + + const bool haveFriction = (disableStrongFriction == 0) ;//PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0; + header->numNormalConstr = Ps::to8(contactCount); + header->numFrictionConstr = Ps::to8(haveFriction ? frictionPatch.anchorCount*2 : 0); + + header->type = Ps::to8(DY_SC_TYPE_EXT_CONTACT); + + header->flags = flags; + + const FloatV restitution = FLoad(combinedRestitution); + + header->staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; + + header->angDom0 = angD0; + header->angDom1 = angD1; + + const PxU32 pointStride = sizeof(SolverContactPointExt); + const PxU32 frictionStride = sizeof(SolverContactFrictionExt); + + const Vec3V normal = V3LoadU(buffer[c.contactPatches[c.correlationListHeads[i]].start].normal); + + FloatV accumImpulse = FZero(); + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer + c.contactPatches[patch].start; + + PxU8* p = ptr; + + + + for(PxU32 j=0;j(p); + p += pointStride; + + accumImpulse = FAdd(accumImpulse, setupExtSolverContact(b0, b1, d0, d1, angD0, angD1, bodyFrame0, bodyFrame1, normal, invDt, invDtp8, restDistance, maxPenBias, restitution, + bounceThreshold, contact, *solverContact, ccdMaxSeparation, Z)); + + } + + ptr = p; + } + contactWritebackCount += contactCount; + + accumImpulse = FDiv(accumImpulse, FLoad(PxF32(contactCount))); + + header->normal_minAppliedImpulseForFrictionW = V4SetW(Vec4V_From_Vec3V(normal), accumImpulse); + + PxF32* forceBuffer = reinterpret_cast(ptr); + PxMemZero(forceBuffer, sizeof(PxF32) * contactCount); + ptr += sizeof(PxF32) * ((contactCount + 3) & (~3)); + + header->broken = 0; + + if(haveFriction) + { + //const Vec3V normal = Vec3V_From_PxVec3(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); + PxVec3 normalS = buffer[c.contactPatches[c.correlationListHeads[i]].start].normal; + + PxVec3 t0, t1; + computeFrictionTangents(b0.getLinVel() - b1.getLinVel(), normalS, t0, t1); + + Vec3V vT0 = V3LoadU(t0); + Vec3V vT1 = V3LoadU(t1); + + //We want to set the writeBack ptr to point to the broken flag of the friction patch. + //On spu we have a slight problem here because the friction patch array is + //in local store rather than in main memory. The good news is that the address of the friction + //patch array in main memory is stored in the work unit. These two addresses will be equal + //except on spu where one is local store memory and the other is the effective address in main memory. + //Using the value stored in the work unit guarantees that the main memory address is used on all platforms. + PxU8* PX_RESTRICT writeback = frictionDataPtr + frictionPatchWritebackAddrIndex*sizeof(FrictionPatch); + + header->frictionBrokenWritebackByte = writeback; + + for(PxU32 j = 0; j < frictionPatch.anchorCount; j++) + { + SolverContactFrictionExt* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionStride; + SolverContactFrictionExt* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionStride; + + PxVec3 ra = bodyFrame0.q.rotate(frictionPatch.body0Anchors[j]); + PxVec3 rb = bodyFrame1.q.rotate(frictionPatch.body1Anchors[j]); + PxVec3 error = (ra + bodyFrame0.p) - (rb + bodyFrame1.p); + + { + const PxVec3 raXn = ra.cross(t0); + const PxVec3 rbXn = rb.cross(t0); + + Cm::SpatialVector deltaV0, deltaV1; + + const Cm::SpatialVector resp0 = createImpulseResponseVector(t0, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-t0, -rbXn, b1); + FloatV resp = FLoad(getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, Z)); + + const FloatV velMultiplier = FSel(FIsGrtr(resp, FLoad(DY_ARTICULATION_MIN_RESPONSE)), FDiv(p8, resp), zero); + + PxU32 index = c.contactPatches[c.correlationListHeads[i]].start; + PxF32 targetVel = buffer[index].targetVel.dot(t0); + + if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVel -= b0.projectVelocity(t0, raXn); + else if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVel += b1.projectVelocity(t0, rbXn); + + f0->normalXYZ_appliedForceW = V4SetW(vT0, zero); + f0->raXnXYZ_velMultiplierW = V4SetW(V4LoadA(&resp0.angular.x), velMultiplier); + f0->rbXnXYZ_biasW = V4SetW(V4Neg(V4LoadA(&resp1.angular.x)), FLoad(t0.dot(error) * invDtF32)); + f0->linDeltaVA = V3LoadA(deltaV0.linear); + f0->angDeltaVA = V3LoadA(deltaV0.angular); + f0->linDeltaVB = V3LoadA(deltaV1.linear); + f0->angDeltaVB = V3LoadA(deltaV1.angular); + f0->targetVel = targetVel; + } + + { + + const PxVec3 raXn = ra.cross(t1); + const PxVec3 rbXn = rb.cross(t1); + + Cm::SpatialVector deltaV0, deltaV1; + + + const Cm::SpatialVector resp0 = createImpulseResponseVector(t1, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-t1, -rbXn, b1); + + FloatV resp = FLoad(getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, Z)); + + //const FloatV velMultiplier = FSel(FIsGrtr(resp, FLoad(DY_ARTICULATION_MIN_RESPONSE)), FMul(p8, FRecip(resp)), zero); + + const FloatV velMultiplier = FSel(FIsGrtr(resp, FLoad(DY_ARTICULATION_MIN_RESPONSE)), FDiv(p8, resp), zero); + + PxU32 index = c.contactPatches[c.correlationListHeads[i]].start; + PxF32 targetVel = buffer[index].targetVel.dot(t1); + + if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVel -= b0.projectVelocity(t1, raXn); + else if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVel += b1.projectVelocity(t1, rbXn); + + f1->normalXYZ_appliedForceW = V4SetW(vT1, zero); + f1->raXnXYZ_velMultiplierW = V4SetW(V4LoadA(&resp0.angular.x), velMultiplier); + f1->rbXnXYZ_biasW = V4SetW(V4Neg(V4LoadA(&resp1.angular.x)), FLoad(t1.dot(error) * invDtF32)); + f1->linDeltaVA = V3LoadA(deltaV0.linear); + f1->angDeltaVA = V3LoadA(deltaV0.angular); + f1->linDeltaVB = V3LoadA(deltaV1.linear); + f1->angDeltaVB = V3LoadA(deltaV1.angular); + f1->targetVel = targetVel; + } + } + } + + frictionPatchWritebackAddrIndex++; + } +} + +} + + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h new file mode 100644 index 000000000..3d9fc7abe --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h @@ -0,0 +1,97 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCONSTRAINTEXT_H +#define DY_SOLVERCONSTRAINTEXT_H + +#include "DySolverExt.h" + +namespace physx +{ + +struct PxcNpWorkUnit; + + +namespace Gu +{ + class ContactBuffer; + struct ContactPoint; +} + +namespace Dy +{ + + struct CorrelationBuffer; + + PxReal getImpulseResponse(const SolverExtBody& b0, const Cm::SpatialVector& impulse0, Cm::SpatialVector& deltaV0, PxReal dom0, PxReal angDom0, + const SolverExtBody& b1, const Cm::SpatialVector& impulse1, Cm::SpatialVector& deltaV1, PxReal dom1, PxReal angDom1, + Cm::SpatialVectorF* Z, bool allowSelfCollision = false); + + Cm::SpatialVector createImpulseResponseVector(const PxVec3& linear, const PxVec3& angular, const SolverExtBody& body); + + void setupFinalizeExtSolverContacts( + const Gu::ContactPoint* buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const SolverExtBody& b0, + const SolverExtBody& b1, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + PxReal restDistance, PxU8* frictionDataPtr, + PxReal ccdMaxContactDist, + Cm::SpatialVectorF* Z); + + + bool setupFinalizeExtSolverContactsCoulomb( + const Gu::ContactBuffer& buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + PxReal invDt, + PxReal bounceThreshold, + const SolverExtBody& b0, + const SolverExtBody& b1, + PxU32 frictionCountPerPoint, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + PxReal restDist, + PxReal ccdMaxContactDist, + Cm::SpatialVectorF* Z); + +} //namespace Dy + +} + +#endif //DY_SOLVERCONSTRAINTEXT_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp new file mode 100644 index 000000000..af2c30d54 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp @@ -0,0 +1,316 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "DyArticulationContactPrep.h" +#include "DySolverConstraintDesc.h" +#include "DySolverConstraint1D.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DyArticulationHelper.h" +#include "PxcNpWorkUnit.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DyCorrelationBuffer.h" +#include "DySolverConstraintExtShared.h" + +using namespace physx; +using namespace Gu; + +// constraint-gen only, since these use getVelocityFast methods +// which aren't valid during the solver phase + +namespace physx +{ + +namespace Dy +{ + + +bool setupFinalizeExtSolverContactsCoulomb( + const ContactBuffer& buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + PxReal invDt, + PxReal bounceThresholdF32, + const SolverExtBody& b0, + const SolverExtBody& b1, + PxU32 frictionCountPerPoint, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + PxReal restDist, + PxReal ccdMaxDistance, + Cm::SpatialVectorF* Z) +{ + // NOTE II: the friction patches are sparse (some of them have no contact patches, and + // therefore did not get written back to the cache) but the patch addresses are dense, + // corresponding to valid patches + + const FloatV ccdMaxSeparation = FLoad(ccdMaxDistance); + + PxU8* PX_RESTRICT ptr = workspace; + + //KS - TODO - this should all be done in SIMD to avoid LHS + PxF32 maxPenBias0 = b0.mBodyData->penBiasClamp; + PxF32 maxPenBias1 = b1.mBodyData->penBiasClamp; + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias0 = b0.mArticulation->getLinkMaxPenBias(b0.mLinkIndex); + } + + if (b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias1 = b1.mArticulation->getLinkMaxPenBias(b1.mLinkIndex); + } + + const FloatV maxPenBias = FLoad(PxMax(maxPenBias0, maxPenBias1)/invDt); + + const FloatV restDistance = FLoad(restDist); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + + const FloatV invDtV = FLoad(invDt); + const FloatV pt8 = FLoad(0.8f); + + const FloatV invDtp8 = FMul(invDtV, pt8); + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + const PxU32 frictionPatchCount = c.frictionPatchCount; + + const PxU32 pointStride = sizeof(SolverContactPointExt); + const PxU32 frictionStride = sizeof(SolverContactFrictionExt); + const PxU8 pointHeaderType = DY_SC_TYPE_EXT_CONTACT; + const PxU8 frictionHeaderType = DY_SC_TYPE_EXT_FRICTION; + + PxReal d0 = invMassScale0; + PxReal d1 = invMassScale1; + PxReal angD0 = invInertiaScale0; + PxReal angD1 = invInertiaScale1; + + PxU8 flags = 0; + + for(PxU32 i=0;i< frictionPatchCount;i++) + { + const PxU32 contactCount = c.frictionPatchContactCounts[i]; + if(contactCount == 0) + continue; + + const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start; + + const Vec3V normalV = Ps::aos::V3LoadA(contactBase0->normal); + const Vec3V normal = V3LoadA(contactBase0->normal); + + const PxReal combinedRestitution = contactBase0->restitution; + + + SolverContactCoulombHeader* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactCoulombHeader); + + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + + const FloatV restitution = FLoad(combinedRestitution); + + + header->numNormalConstr = PxU8(contactCount); + header->type = pointHeaderType; + //header->setRestitution(combinedRestitution); + + header->setDominance0(d0); + header->setDominance1(d1); + header->angDom0 = angD0; + header->angDom1 = angD1; + header->flags = flags; + + header->setNormal(normalV); + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer.contacts + c.contactPatches[patch].start; + + PxU8* p = ptr; + for(PxU32 j=0;j(p); + p += pointStride; + + setupExtSolverContact(b0, b1, d0, d1, angD0, angD1, bodyFrame0, bodyFrame1, normal, invDtV, invDtp8, restDistance, maxPenBias, restitution, + bounceThreshold, contact, *solverContact, ccdMaxSeparation, Z); + } + ptr = p; + } + } + + //construct all the frictions + + PxU8* PX_RESTRICT ptr2 = workspace; + + const PxF32 orthoThreshold = 0.70710678f; + const PxF32 eps = 0.00001f; + bool hasFriction = false; + + for(PxU32 i=0;i< frictionPatchCount;i++) + { + const PxU32 contactCount = c.frictionPatchContactCounts[i]; + if(contactCount == 0) + continue; + + SolverContactCoulombHeader* header = reinterpret_cast(ptr2); + header->frictionOffset = PxU16(ptr - ptr2); + ptr2 += sizeof(SolverContactCoulombHeader) + header->numNormalConstr * pointStride; + + const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start; + + PxVec3 normal = contactBase0->normal; + + const PxReal staticFriction = contactBase0->staticFriction; + const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + const bool haveFriction = (disableStrongFriction == 0); + + SolverFrictionHeader* frictionHeader = reinterpret_cast(ptr); + frictionHeader->numNormalConstr = Ps::to8(c.frictionPatchContactCounts[i]); + frictionHeader->numFrictionConstr = Ps::to8(haveFriction ? c.frictionPatchContactCounts[i] * frictionCountPerPoint : 0); + frictionHeader->flags = flags; + ptr += sizeof(SolverFrictionHeader); + PxF32* forceBuffer = reinterpret_cast(ptr); + ptr += frictionHeader->getAppliedForcePaddingSize(c.frictionPatchContactCounts[i]); + PxMemZero(forceBuffer, sizeof(PxF32) * c.frictionPatchContactCounts[i]); + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + + + const PxVec3 t0Fallback1(0.f, -normal.z, normal.y); + const PxVec3 t0Fallback2(-normal.y, normal.x, 0.f) ; + const PxVec3 tFallback1 = orthoThreshold > PxAbs(normal.x) ? t0Fallback1 : t0Fallback2; + const PxVec3 vrel = b0.getLinVel() - b1.getLinVel(); + const PxVec3 t0_ = vrel - normal * (normal.dot(vrel)); + const PxReal sqDist = t0_.dot(t0_); + const PxVec3 tDir0 = (sqDist > eps ? t0_: tFallback1).getNormalized(); + const PxVec3 tDir1 = tDir0.cross(normal); + PxVec3 tFallback[2] = {tDir0, tDir1}; + + PxU32 ind = 0; + + if(haveFriction) + { + hasFriction = true; + frictionHeader->setStaticFriction(staticFriction); + frictionHeader->invMass0D0 = d0; + frictionHeader->invMass1D1 = d1; + frictionHeader->angDom0 = angD0; + frictionHeader->angDom1 = angD1; + frictionHeader->type = frictionHeaderType; + + PxU32 totalPatchContactCount = 0; + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const PxU32 start = c.contactPatches[patch].start; + const Gu::ContactPoint* contactBase = buffer.contacts + start; + + PxU8* p = ptr; + + for(PxU32 j =0; j < count; j++) + { + const Gu::ContactPoint& contact = contactBase[j]; + const PxVec3 ra = contact.point - bodyFrame0.p; + const PxVec3 rb = contact.point - bodyFrame1.p; + + const PxVec3 targetVel = contact.targetVel; + const PxVec3 pVRa = b0.getLinVel() + b0.getAngVel().cross(ra); + const PxVec3 pVRb = b1.getLinVel() + b1.getAngVel().cross(rb); + //const PxVec3 vrel = pVRa - pVRb; + + for(PxU32 k = 0; k < frictionCountPerPoint; ++k) + { + SolverContactFrictionExt* PX_RESTRICT f0 = reinterpret_cast(p); + p += frictionStride; + + PxVec3 t0 = tFallback[ind]; + ind = 1 - ind; + PxVec3 raXn = ra.cross(t0); + PxVec3 rbXn = rb.cross(t0); + Cm::SpatialVector deltaV0, deltaV1; + + const Cm::SpatialVector resp0 = createImpulseResponseVector(t0, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-t0, -rbXn, b1); + + PxReal unitResponse = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, Z); + + PxReal tv = targetVel.dot(t0); + if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + tv += pVRa.dot(t0); + else if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + tv -= pVRb.dot(t0); + + + f0->setVelMultiplier(FLoad(unitResponse>0.0f ? 1.f/unitResponse : 0.0f)); + f0->setRaXn(resp0.angular); + f0->setRbXn(-resp1.angular); + f0->targetVel = tv; + f0->setNormal(t0); + f0->setAppliedForce(0.0f); + f0->linDeltaVA = V3LoadA(deltaV0.linear); + f0->angDeltaVA = V3LoadA(deltaV0.angular); + f0->linDeltaVB = V3LoadA(deltaV1.linear); + f0->angDeltaVB = V3LoadA(deltaV1.angular); + } + } + + totalPatchContactCount += c.contactPatches[patch].count; + + ptr = p; + } + } + } + //PX_ASSERT(ptr - workspace == n.solverConstraintSize); + return hasFriction; +} + + +} + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h new file mode 100644 index 000000000..676eb8a98 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h @@ -0,0 +1,262 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_DEBUG_FNS_H +#define DY_ARTICULATION_DEBUG_FNS_H + +#include "DyArticulationFnsScalar.h" +#include "DyArticulationFnsSimd.h" + +namespace physx +{ +namespace Dy +{ +#if 0 + void printMomentum(const char* id, PxTransform* pose, Cm::SpatialVector* velocity, FsInertia* inertia, PxU32 linkCount) + { + typedef ArticulationFnsScalar Fns; + + Cm::SpatialVector m = Cm::SpatialVector::zero(); + for(PxU32 i=0;i Simd; + typedef ArticulationFnsScalar Scalar; + +public: + + static PX_FORCE_INLINE FsInertia addInertia(const FsInertia& in1, const FsInertia& in2) + { + return FsInertia(M33Add(in1.ll, in2.ll), + M33Add(in1.la, in2.la), + M33Add(in1.aa, in2.aa)); + } + + static PX_FORCE_INLINE FsInertia subtractInertia(const FsInertia& in1, const FsInertia& in2) + { + return FsInertia(M33Sub(in1.ll, in2.ll), + M33Sub(in1.la, in2.la), + M33Sub(in1.aa, in2.aa)); + } + + static Mat33V invertSym33(const Mat33V &m) + { + PxMat33 n_ = Scalar::invertSym33(unsimdify(m)); + Mat33V n = SimdBase::invertSym33(m); + compare33(n_, unsimdify(n)); + + return n; + } + + static Mat33V invSqrt(const Mat33V &m) + { + PxMat33 n_ = Scalar::invSqrt(unsimdify(m)); + Mat33V n = SimdBase::invSqrt(m); + compare33(n_, unsimdify(n)); + + return n; + } + + + + static FsInertia invertInertia(const FsInertia &I) + { + SpInertia J_ = Scalar::invertInertia(unsimdify(I)); + FsInertia J = SimdBase::invertInertia(I); + compareInertias(J_,unsimdify(J)); + + return J; + } + + static Mat33V computeSIS(const FsInertia &I, const Cm::SpatialVectorV S[3], Cm::SpatialVectorV*PX_RESTRICT IS) + { + Cm::SpatialVector IS_[3]; + Scalar::multiply(IS_, unsimdify(I), unsimdify(&S[0])); + PxMat33 D_ = Scalar::multiplySym(IS_, unsimdify(&S[0])); + + Mat33V D = SimdBase::computeSIS(I, S, IS); + + compare33(unsimdify(D), D_); + + return D; + } + + + static FsInertia multiplySubtract(const FsInertia &I, const Mat33V &D, const Cm::SpatialVectorV IS[3], Cm::SpatialVectorV*PX_RESTRICT DSI) + { + Cm::SpatialVector DSI_[3]; + + Scalar::multiply(DSI_, unsimdify(IS), unsimdify(D)); + SpInertia J_ = Scalar::multiplySubtract(unsimdify(I), DSI_, unsimdify(IS)); + + FsInertia J = SimdBase::multiplySubtract(I, D, IS, DSI); + + compareInertias(unsimdify(J), J_); + + return J; + } + + + static FsInertia multiplySubtract(const FsInertia &I, const Cm::SpatialVectorV S[3]) + { + SpInertia J_ = Scalar::multiplySubtract(unsimdify(I), unsimdify(S), unsimdify(S)); + FsInertia J = SimdBase::multiplySubtract(I, S); + compareInertias(unsimdify(J), J_); + return J; + } + + + static FsInertia translateInertia(Vec3V offset, const FsInertia &I) + { + PxVec3 offset_; + V3StoreU(offset, offset_); + SpInertia J_ = Scalar::translate(offset_, unsimdify(I)); + FsInertia J = SimdBase::translateInertia(offset, I); + compareInertias(J_, unsimdify(J)); + + return J; + } + + + static PX_FORCE_INLINE FsInertia propagate(const FsInertia &I, + const Cm::SpatialVectorV S[3], + const Mat33V &load, + const FloatV isf) + { + SpInertia J_ = Scalar::propagate(unsimdify(I), unsimdify(&S[0]), unsimdify(load), unsimdify(isf)); + FsInertia J = Simd::propagate(I, S, load, isf); + + compareInertias(J_, unsimdify(J)); + return J; + } + + + static PX_FORCE_INLINE Mat33V computeDriveInertia(const FsInertia &I0, + const FsInertia &I1, + const Cm::SpatialVectorV S[3]) + { + PxMat33 m_ = Scalar::computeDriveInertia(unsimdify(I0), unsimdify(I1), unsimdify(&S[0])); + Mat33V m = Simd::computeDriveInertia(I0, I1, S); + + compare33(m_, unsimdify(m)); + return m; + } + + static const PxMat33 unsimdify(const Mat33V &m) + { + PX_ALIGN(16, PxMat33) m_; + PxMat33_From_Mat33V(m, m_); + return m_; + } + + static PxReal unsimdify(const FloatV &m) + { + PxF32 f; + FStore(m, &f); + return f; + } + + static SpInertia unsimdify(const FsInertia &I) + { + return SpInertia (unsimdify(I.ll), + unsimdify(I.la), + unsimdify(I.aa)); + } + + static const Cm::SpatialVector* unsimdify(const Cm::SpatialVectorV *S) + { + return reinterpret_cast(S); + } + + +private: + + static PxReal absmax(const PxVec3& n) + { + return PxMax(PxAbs(n.x), PxMax(PxAbs(n.y),PxAbs(n.z))); + } + + static PxReal norm(const PxMat33& n) + { + return PxMax(absmax(n.column0), PxMax(absmax(n.column1), absmax(n.column2))); + } + + static void compare33(const PxMat33& ref, const PxMat33& n) + { + PxReal errNorm = norm(ref-n); + PX_UNUSED(errNorm); + PX_ASSERT(errNorm <= PxMax(norm(ref)*1e-3f, 1e-4f)); + } + + static void compareInertias(const SpInertia& a, const SpInertia& b) + { + compare33(a.mLL, b.mLL); + compare33(a.mLA, b.mLA); + compare33(a.mAA, b.mAA); + } + + +}; + +#if DY_ARTICULATION_DEBUG_VERIFY +static bool isPositiveDefinite(const Mat33V& m) +{ + PX_ALIGN_PREFIX(16) PxMat33 m1 PX_ALIGN_SUFFIX(16); + PxMat33_From_Mat33V(m, m1); + return isPositiveDefinite(m1); +} + + +static bool isPositiveDefinite(const FsInertia& s) +{ + return isPositiveDefinite(ArticulationFnsDebug::unsimdify(s)); +} + +static PxReal magnitude(const Cm::SpatialVectorV &v) +{ + return PxSqrt(FStore(V3Dot(v.linear, v.linear)) + FStore(V3Dot(v.angular, v.angular))); +} + +static bool almostEqual(const Cm::SpatialVectorV &ref, const Cm::SpatialVectorV& test, PxReal tolerance) +{ + return magnitude(ref-test)<=tolerance*magnitude(ref); +} +#endif +} +} + +#endif //DY_ARTICULATION_DEBUG_FNS_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h new file mode 100644 index 000000000..24a8d3f02 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h @@ -0,0 +1,397 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_SCALAR_FNS_H +#define DY_ARTICULATION_SCALAR_FNS_H + +// Scalar helpers for articulations + +#include "DyArticulationUtils.h" +#include "DyArticulationScalar.h" +#include "DySpatial.h" + +namespace physx +{ + +namespace Dy +{ + +/* +namespace +{ + static void print(const PxMat33 &m) + { + printf("(%f, %f, %f)\n(%f, %f, %f)\n(%f, %f, %f)\n\n", + m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); + } + + static void print(const Cm::SpatialVector *v, PxU32 count) + { + for(PxU32 i=0;i(m.col0) * v.x + + reinterpret_cast(m.col1) * v.y + + reinterpret_cast(m.col2) * v.z; + } + + static PX_FORCE_INLINE PxVec3 multiplyTranspose(const Mat33V& m, const PxVec3& v) + { + return PxVec3(v.dot(reinterpret_cast(m.col0)), + v.dot(reinterpret_cast(m.col1)), + v.dot(reinterpret_cast(m.col2))); + } + + static Cm::SpatialVector multiply(const FsInertia& m, const Cm::SpatialVector& v) + { + return Cm::SpatialVector(multiply(m.ll,v.linear) + multiply(m.la,v.angular), + multiplyTranspose(m.la, v.linear) + multiply(m.aa, v.angular)); + } + + static PX_FORCE_INLINE Cm::SpatialVector getRootDeltaV(const FsData& matrix, const Cm::SpatialVector& Z) + { + return multiply(getRootInverseInertia(matrix), Z); + } +}; + +} + +} + +#endif //DY_ARTICULATION_SCALAR_FNS_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h new file mode 100644 index 000000000..cf3bfef99 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h @@ -0,0 +1,438 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_SIMD_FNS_H +#define DY_ARTICULATION_SIMD_FNS_H + +#include "DyArticulationUtils.h" + +namespace physx +{ +namespace Dy +{ + +template +class PodULike +{ + PxU8 space[sizeof(T)*count]; +public: + PX_FORCE_INLINE operator T*() { return reinterpret_cast(space); } +}; + +#define POD_U_LIKE(_T, _count, _alignment) PX_ALIGN_PREFIX(_alignment) PodULike<_T, _count> PX_ALIGN_SUFFIX(_alignment) + +class ArticulationFnsSimdBase +{ +public: + + static PX_FORCE_INLINE FsInertia addInertia(const FsInertia& in1, const FsInertia& in2) + { + return FsInertia(M33Add(in1.ll, in2.ll), + M33Add(in1.la, in2.la), + M33Add(in1.aa, in2.aa)); + } + + static PX_FORCE_INLINE FsInertia subtractInertia(const FsInertia& in1, const FsInertia& in2) + { + return FsInertia(M33Sub(in1.ll, in2.ll), + M33Sub(in1.la, in2.la), + M33Sub(in1.aa, in2.aa)); + } + + static PX_FORCE_INLINE Vec3V axisDot(const Cm::SpatialVectorV S[3], const Cm::SpatialVectorV &v) + { + return V3Merge(FAdd(V3Dot(S[0].linear,v.linear), V3Dot(S[0].angular,v.angular)), + FAdd(V3Dot(S[1].linear,v.linear), V3Dot(S[1].angular,v.angular)), + FAdd(V3Dot(S[2].linear,v.linear), V3Dot(S[2].angular,v.angular))); + } + + static PX_FORCE_INLINE Cm::SpatialVectorV axisMultiply(const Cm::SpatialVectorV S[3], Vec3V v) + { + return Cm::SpatialVectorV(V3ScaleAdd(S[0].linear, V3GetX(v), V3ScaleAdd(S[1].linear, V3GetY(v), V3Scale(S[2].linear, V3GetZ(v)))), + V3ScaleAdd(S[0].angular, V3GetX(v), V3ScaleAdd(S[1].angular, V3GetY(v), V3Scale(S[2].angular, V3GetZ(v))))); + } + + + static PX_FORCE_INLINE Cm::SpatialVectorV subtract(const Cm::SpatialVectorV &a, const Cm::SpatialVectorV &b) + { + return Cm::SpatialVectorV(V3Sub(a.linear, b.linear), V3Sub(a.angular, b.angular)); + } + + static PX_FORCE_INLINE Cm::SpatialVectorV add(const Cm::SpatialVectorV &a, const Cm::SpatialVectorV &b) + { + return Cm::SpatialVectorV(V3Add(a.linear, b.linear), V3Add(a.angular, b.angular)); + } + + + static PX_FORCE_INLINE Cm::SpatialVectorV multiply(const FsInertia &I, const Cm::SpatialVectorV &S) + { + return Cm::SpatialVectorV(V3Add(M33MulV3(I.ll,S.linear), M33MulV3(I.la,S.angular)), + V3Add(M33TrnspsMulV3(I.la,S.linear), M33MulV3(I.aa,S.angular))); + } + + + static PX_FORCE_INLINE Cm::SpatialVectorV translateMotion(const Vec3V& p, const Cm::SpatialVectorV& v) + { + return Cm::SpatialVectorV(V3Add(v.linear, V3Cross(p, v.angular)), v.angular); + } + + // translate a force resolved at position p to the origin + + static PX_FORCE_INLINE Cm::SpatialVectorV translateForce(const Vec3V& p, const Cm::SpatialVectorV& v) + { + return Cm::SpatialVectorV(v.linear, V3Add(v.angular, V3Cross(p, v.linear))); + } + + static PX_FORCE_INLINE Mat33V invertSym33(const Mat33V &m) + { + Vec3V a0 = V3Cross(m.col1, m.col2); + Vec3V a1 = V3Cross(m.col2, m.col0); + Vec3V a2 = V3Cross(m.col0, m.col1); + FloatV det = V3Dot(a0, m.col0); + FloatV recipDet = FRecip(det); + + a1 = V3SetX(a1, V3GetY(a0)); + a2 = V3Merge(V3GetZ(a0), V3GetZ(a1), V3GetZ(a2)); // make sure it's symmetric + + return Mat33V(V3Scale(a0, recipDet), + V3Scale(a1, recipDet), + V3Scale(a2, recipDet)); + } + + + static PX_FORCE_INLINE FloatV safeInvSqrt(FloatV v) + { + return FSqrt(FMax(FZero(), FRecip(v))); + } + static PX_FORCE_INLINE Mat33V invSqrt(const Mat33V& m) + { + // cholesky factor to + // (a 0 0) + // (b c 0) + // (d e f) + // except that a,c,f are the reciprocal sqrts rather than sqrts + + // PxVec3 v0 = m.column0, v1 = m.column1, v2 = m.column2; + Vec3V v0 = m.col0, v1 = m.col1, v2 = m.col2; + + const FloatV x0 = V3GetX(v0), y1 = V3GetY(v1), z2 = V3GetZ(v2); + + FloatV a = safeInvSqrt(x0); // PxReal a = PxRecipSqrt(v0.x); + + Vec3V abd = V3Scale(v0, a); // PxReal b = v0.y*a; + FloatV b = V3GetY(abd); + + FloatV c2 = FNegScaleSub(b, b, y1); // PxReal c = PxRecipSqrt(v1.y - b*b); + FloatV c = safeInvSqrt(c2); + + FloatV d = V3GetZ(abd); // PxReal d = v0.z*a; + + FloatV e = FMul(FNegScaleSub(b, d, V3GetZ(v1)), c); // PxReal e = (v1.z-d*b) * c; + + FloatV f2 = FNegScaleSub(d, d, FNegScaleSub(e, e, z2)); // PxReal f = PxRecipSqrt(v2.z - d*d - e*e); + FloatV f = safeInvSqrt(f2); + + // invert + FloatV x = FMul(FMul(b,a),c), // x = -b*a*c + y = FMul((FNegScaleSub(d,a, FMul(e,x))), f), // y = (-e*x-d*a)*f + z = FMul(e, FMul(c,f)); // z = -e*c*f + + return Mat33V(V3Merge(a, FZero(), FZero()), + V3Merge(FNeg(x), c, FZero()), + V3Merge(y, FNeg(z), f)); + } + + + static PX_FORCE_INLINE FsInertia invertInertia(const FsInertia &I) + { + Mat33V aa = M33Scale(M33Add(I.aa, M33Trnsps(I.aa)), FHalf()); + Mat33V ll = M33Scale(M33Add(I.ll, M33Trnsps(I.ll)), FHalf()); + + Mat33V AAInv = invertSym33(aa); + Mat33V z = M33MulM33(M33Neg(I.la), AAInv); + Mat33V S = M33Add(ll, M33MulM33(z, M33Trnsps(I.la))); + + Mat33V LL = invertSym33(S); + Mat33V LA = M33MulM33(LL, z); + Mat33V AA = M33Add(AAInv, M33MulM33(M33Trnsps(z), LA)); + + return FsInertia(LL, LA, AA); + } + + static PX_NOINLINE Mat33V computeSIS(const FsInertia &I, const Cm::SpatialVectorV S[3], Cm::SpatialVectorV IS[3]) + { + Vec3V S0l = S[0].linear, S0a = S[0].angular; + Vec3V S1l = S[1].linear, S1a = S[1].angular; + Vec3V S2l = S[2].linear, S2a = S[2].angular; + + Vec3V IS0l = V3Add(M33MulV3(I.ll,S0l), M33MulV3(I.la,S0a)); + Vec3V IS0a = V3Add(M33TrnspsMulV3(I.la,S0l), M33MulV3(I.aa,S0a)); + Vec3V IS1l = V3Add(M33MulV3(I.ll,S1l), M33MulV3(I.la,S1a)); + Vec3V IS1a = V3Add(M33TrnspsMulV3(I.la,S1l), M33MulV3(I.aa,S1a)); + Vec3V IS2l = V3Add(M33MulV3(I.ll,S2l), M33MulV3(I.la,S2a)); + Vec3V IS2a = V3Add(M33TrnspsMulV3(I.la,S2l), M33MulV3(I.aa,S2a)); + + // compute SIS + FloatV a00 = FAdd(V3Dot(S0l, IS0l), V3Dot(S0a, IS0a)); + FloatV a01 = FAdd(V3Dot(S0l, IS1l), V3Dot(S0a, IS1a)); + FloatV a02 = FAdd(V3Dot(S0l, IS2l), V3Dot(S0a, IS2a)); + FloatV a11 = FAdd(V3Dot(S1l, IS1l), V3Dot(S1a, IS1a)); + FloatV a12 = FAdd(V3Dot(S1l, IS2l), V3Dot(S1a, IS2a)); + FloatV a22 = FAdd(V3Dot(S2l, IS2l), V3Dot(S2a, IS2a)); + + // write IS, a useful side-effect + IS[0].linear = IS0l; IS[0].angular = IS0a; + IS[1].linear = IS1l; IS[1].angular = IS1a; + IS[2].linear = IS2l; IS[2].angular = IS2a; + + return Mat33V(V3Merge(a00, a01, a02), + V3Merge(a01, a11, a12), + V3Merge(a02, a12, a22)); + } + + + static PX_FORCE_INLINE FsInertia multiplySubtract(const FsInertia &I, const Mat33V &D, const Cm::SpatialVectorV IS[3], Cm::SpatialVectorV DSI[3]) + { + // cut'n'paste, how I love ya, how I love ya + + Vec3V IS0l = IS[0].linear, IS0a = IS[0].angular; + Vec3V IS1l = IS[1].linear, IS1a = IS[1].angular; + Vec3V IS2l = IS[2].linear, IS2a = IS[2].angular; + + Vec3V D0 = D.col0, D1 = D.col1, D2 = D.col2; + + // compute IDS + Vec3V DSI0l = V3ScaleAdd(IS0l, V3GetX(D0), V3ScaleAdd(IS1l, V3GetY(D0), V3Scale(IS2l, V3GetZ(D0)))); + Vec3V DSI1l = V3ScaleAdd(IS0l, V3GetX(D1), V3ScaleAdd(IS1l, V3GetY(D1), V3Scale(IS2l, V3GetZ(D1)))); + Vec3V DSI2l = V3ScaleAdd(IS0l, V3GetX(D2), V3ScaleAdd(IS1l, V3GetY(D2), V3Scale(IS2l, V3GetZ(D2)))); + + Vec3V DSI0a = V3ScaleAdd(IS0a, V3GetX(D0), V3ScaleAdd(IS1a, V3GetY(D0), V3Scale(IS2a, V3GetZ(D0)))); + Vec3V DSI1a = V3ScaleAdd(IS0a, V3GetX(D1), V3ScaleAdd(IS1a, V3GetY(D1), V3Scale(IS2a, V3GetZ(D1)))); + Vec3V DSI2a = V3ScaleAdd(IS0a, V3GetX(D2), V3ScaleAdd(IS1a, V3GetY(D2), V3Scale(IS2a, V3GetZ(D2)))); + + // compute J = I - DSI' IS. Each row of DSI' IS generates an inertia dyad + + Vec3V ll0 = I.ll.col0, ll1 = I.ll.col1, ll2 = I.ll.col2; + Vec3V la0 = I.la.col0, la1 = I.la.col1, la2 = I.la.col2; + Vec3V aa0 = I.aa.col0, aa1 = I.aa.col1, aa2 = I.aa.col2; + +#define SUBTRACT_DYAD(_a, _b) \ + ll0 = V3NegScaleSub(_b##l, V3GetX(_a##l), ll0); la0 = V3NegScaleSub(_b##l, V3GetX(_a##a), la0); aa0 = V3NegScaleSub(_b##a, V3GetX(_a##a), aa0); \ + ll1 = V3NegScaleSub(_b##l, V3GetY(_a##l), ll1); la1 = V3NegScaleSub(_b##l, V3GetY(_a##a), la1); aa1 = V3NegScaleSub(_b##a, V3GetY(_a##a), aa1); \ + ll2 = V3NegScaleSub(_b##l, V3GetZ(_a##l), ll2); la2 = V3NegScaleSub(_b##l, V3GetZ(_a##a), la2); aa2 = V3NegScaleSub(_b##a, V3GetZ(_a##a), aa2); + + SUBTRACT_DYAD(IS0, DSI0); + SUBTRACT_DYAD(IS1, DSI1); + SUBTRACT_DYAD(IS2, DSI2); +#undef SUBTRACT_DYAD + + DSI[0].linear = DSI0l; DSI[0].angular = DSI0a; + DSI[1].linear = DSI1l; DSI[1].angular = DSI1a; + DSI[2].linear = DSI2l; DSI[2].angular = DSI2a; + + return FsInertia(Mat33V(ll0, ll1, ll2), + Mat33V(la0, la1, la2), + Mat33V(aa0, aa1, aa2)); + } + + + static PX_FORCE_INLINE FsInertia multiplySubtract(const FsInertia &I, const Cm::SpatialVectorV S[3]) + { + // cut'n'paste, how I love ya, how I love ya + + const Vec3V S0l = S[0].linear, S0a = S[0].angular; + const Vec3V S1l = S[1].linear, S1a = S[1].angular; + const Vec3V S2l = S[2].linear, S2a = S[2].angular; + + // compute J = I - DSI' IS. Each row of DSI' IS generates an inertia dyad + + Vec3V ll0 = I.ll.col0, ll1 = I.ll.col1, ll2 = I.ll.col2; + Vec3V la0 = I.la.col0, la1 = I.la.col1, la2 = I.la.col2; + Vec3V aa0 = I.aa.col0, aa1 = I.aa.col1, aa2 = I.aa.col2; + +#define SUBTRACT_DYAD(_a, _b) \ + ll0 = V3NegScaleSub(_b##l, V3GetX(_a##l), ll0); la0 = V3NegScaleSub(_b##l, V3GetX(_a##a), la0); aa0 = V3NegScaleSub(_b##a, V3GetX(_a##a), aa0); \ + ll1 = V3NegScaleSub(_b##l, V3GetY(_a##l), ll1); la1 = V3NegScaleSub(_b##l, V3GetY(_a##a), la1); aa1 = V3NegScaleSub(_b##a, V3GetY(_a##a), aa1); \ + ll2 = V3NegScaleSub(_b##l, V3GetZ(_a##l), ll2); la2 = V3NegScaleSub(_b##l, V3GetZ(_a##a), la2); aa2 = V3NegScaleSub(_b##a, V3GetZ(_a##a), aa2); + + SUBTRACT_DYAD(S0, S0); + SUBTRACT_DYAD(S1, S1); + SUBTRACT_DYAD(S2, S2); +#undef SUBTRACT_DYAD + + return FsInertia(Mat33V(ll0, ll1, ll2), + Mat33V(la0, la1, la2), + Mat33V(aa0, aa1, aa2)); + } + + + static PX_FORCE_INLINE FsInertia translateInertia(Vec3V a, const FsInertia &input) + { + Vec3V b = V3Neg(a); + + Vec3V la0 = input.la.col0, la1 = input.la.col1, la2 = input.la.col2; + Vec3V ll0 = input.ll.col0, ll1 = input.ll.col1, ll2 = input.ll.col2; + Vec3V aa0 = input.aa.col0, aa1 = input.aa.col1, aa2 = input.aa.col2; + + FloatV aX = V3GetX(a), aY = V3GetY(a), aZ = V3GetZ(a); + FloatV bX = V3GetX(b), bY = V3GetY(b), bZ = V3GetZ(b); + FloatV Z = FZero(); + + // s - star matrix of a + Vec3V s0 = V3Merge(Z, aZ, bY), + s1 = V3Merge(bZ, Z, aX), + s2 = V3Merge(aY, bX, Z); + + // s * la + Vec3V sla0 = V3ScaleAdd(s0, V3GetX(la0), V3ScaleAdd(s1, V3GetY(la0), V3Scale(s2, V3GetZ(la0)))); + Vec3V sla1 = V3ScaleAdd(s0, V3GetX(la1), V3ScaleAdd(s1, V3GetY(la1), V3Scale(s2, V3GetZ(la1)))); + Vec3V sla2 = V3ScaleAdd(s0, V3GetX(la2), V3ScaleAdd(s1, V3GetY(la2), V3Scale(s2, V3GetZ(la2)))); + + // ll * s.transpose() (ll is symmetric) + Vec3V llst0 = V3ScaleAdd(ll2, aY, V3Scale(ll1, bZ)), + llst1 = V3ScaleAdd(ll0, aZ, V3Scale(ll2, bX)), + llst2 = V3ScaleAdd(ll1, aX, V3Scale(ll0, bY)); + + // t = sla+S*llst*0.5f; + + Vec3V sllst0 = V3ScaleAdd(s2, V3GetZ(llst0), V3ScaleAdd(s1, V3GetY(llst0), V3Scale(s0, V3GetX(llst0)))); + Vec3V sllst1 = V3ScaleAdd(s2, V3GetZ(llst1), V3ScaleAdd(s1, V3GetY(llst1), V3Scale(s0, V3GetX(llst1)))); + Vec3V sllst2 = V3ScaleAdd(s2, V3GetZ(llst2), V3ScaleAdd(s1, V3GetY(llst2), V3Scale(s0, V3GetX(llst2)))); + + Vec3V t0 = V3ScaleAdd(sllst0, FHalf(), sla0); + Vec3V t1 = V3ScaleAdd(sllst1, FHalf(), sla1); + Vec3V t2 = V3ScaleAdd(sllst2, FHalf(), sla2); + + // t+t.transpose() + Vec3V r0 = V3Add(t0, V3Merge(V3GetX(t0), V3GetX(t1), V3GetX(t2))), + r1 = V3Add(t1, V3Merge(V3GetY(t0), V3GetY(t1), V3GetY(t2))), + r2 = V3Add(t2, V3Merge(V3GetZ(t0), V3GetZ(t1), V3GetZ(t2))); + + return FsInertia(Mat33V(ll0, ll1, ll2), + + Mat33V(V3Add(la0, llst0), + V3Add(la1, llst1), + V3Add(la2, llst2)), + + Mat33V(V3Add(aa0, r0), + V3Add(aa1, r1), + V3Add(aa2, r2))); + } + +}; + +template +class ArticulationFnsSimd : public Base +{ + static PX_FORCE_INLINE void axisMultiplyLowerTriangular(Cm::SpatialVectorV ES[3], const Mat33V&E, const Cm::SpatialVectorV S[3]) + { + const Vec3V l0 = S[0].linear, l1 = S[1].linear, l2 = S[2].linear; + const Vec3V a0 = S[0].angular, a1 = S[1].angular, a2 = S[2].angular; + ES[0] = Cm::SpatialVectorV(V3Scale(l0, V3GetX(E.col0)), + V3Scale(a0, V3GetX(E.col0))); + ES[1] = Cm::SpatialVectorV(V3ScaleAdd(l0, V3GetX(E.col1), V3Scale(l1, V3GetY(E.col1))), + V3ScaleAdd(a0, V3GetX(E.col1), V3Scale(a1, V3GetY(E.col1)))); + ES[2] = Cm::SpatialVectorV(V3ScaleAdd(l0, V3GetX(E.col2), V3ScaleAdd(l1, V3GetY(E.col2), V3Scale(l2, V3GetZ(E.col2)))), + V3ScaleAdd(a0, V3GetX(E.col2), V3ScaleAdd(a1, V3GetY(E.col2), V3Scale(a2, V3GetZ(E.col2))))); + } + +public: + static PX_FORCE_INLINE FsInertia propagate(const FsInertia &I, + const Cm::SpatialVectorV S[3], + const Mat33V &load, + const FloatV isf) + { + Cm::SpatialVectorV IS[3], ISE[3]; + Mat33V D = Base::computeSIS(I, S, IS); + + D.col0 = V3ScaleAdd(load.col0, isf, D.col0); + D.col1 = V3ScaleAdd(load.col1, isf, D.col1); + D.col2 = V3ScaleAdd(load.col2, isf, D.col2); + + axisMultiplyLowerTriangular(ISE, Base::invSqrt(D), IS); + return Base::multiplySubtract(I, ISE); + } + + + + static PX_INLINE Cm::SpatialVectorV propagateImpulse(const FsRow& row, + const FsJointVectors& jv, + Vec3V& SZ, + const Cm::SpatialVectorV& Z, + const FsRowAux& aux) + { + PX_UNUSED(aux); + + SZ = V3Add(Z.angular, V3Cross(Z.linear, jv.jointOffset)); + return Base::translateForce(jv.parentOffset, Z - Base::axisMultiply(row.DSI, SZ)); + } + + static PX_INLINE Cm::SpatialVectorV propagateVelocity(const FsRow& row, + const FsJointVectors& jv, + const Vec3V& SZ, + const Cm::SpatialVectorV& v, + const FsRowAux& aux) + { + PX_UNUSED(aux); + + Cm::SpatialVectorV w = Base::translateMotion(V3Neg(jv.parentOffset), v); + Vec3V DSZ = M33MulV3(row.D, SZ); + + Vec3V n = V3Add(Base::axisDot(row.DSI, w), DSZ); + return w - Cm::SpatialVectorV(V3Cross(jv.jointOffset, n), n); + } + + + + + + static PX_FORCE_INLINE Mat33V computeDriveInertia(const FsInertia &I0, + const FsInertia &I1, + const Cm::SpatialVectorV S[3]) + { + POD_U_LIKE(Cm::SpatialVectorV, 3, 16) IS, ISD, dummy; + Mat33V D = Base::computeSIS(I0, S, IS); + Mat33V DInv = Base::invertSym33(D); + + FsInertia tmp = Base::addInertia(I0, I1); + tmp = Base::multiplySubtract(tmp, DInv, IS, ISD); + FsInertia J = Base::invertInertia(tmp); + + Mat33V E = Base::computeSIS(J, ISD, dummy); + return Base::invertSym33(M33Add(DInv,E)); + + } +}; + +} +} + +#endif //DY_ARTICULATION_SIMD_FNS_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp new file mode 100644 index 000000000..4653413c3 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp @@ -0,0 +1,524 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec3.h" +#include "foundation/PxMath.h" +#include "foundation/PxMemory.h" +#include "common/PxProfileZone.h" + +#include "PsUtilities.h" +#include "CmSpatialVector.h" +#include "DyArticulationHelper.h" +#include "DyArticulationReference.h" +#include "DyArticulationFnsSimd.h" +#include "DyArticulationFnsScalar.h" +#include "DyArticulationFnsDebug.h" +#include "DySolverConstraintDesc.h" +#include "PxvDynamics.h" +#include "DyArticulation.h" +#include "PxcRigidBody.h" +#include "CmConeLimitHelper.h" +#include "DySolverConstraint1D.h" +#include "PxcConstraintBlockStream.h" +#include "DySolverConstraint1D.h" +#include "DyArticulationPImpl.h" +#include "PsFoundation.h" + +namespace physx +{ + +namespace Dy +{ + + +void getImpulseResponseSlow(const FsData& matrix, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1) +{ + typedef ArticulationFnsSimd Fns; + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_ASSERT(matrix.linkCount<=DY_ARTICULATION_MAX_SIZE); + PxU32 stack[DY_ARTICULATION_MAX_SIZE]; + Vec3V SZ[DY_ARTICULATION_MAX_SIZE]; + + PxU32 i0, i1, ic; + + for(i0 = linkID0, i1 = linkID1; i0!=i1;) // find common path + { + if(i0i1 ;) + v = Fns::propagateVelocity(rows[stack[index]], jointVectors[stack[index]], SZ[stack[index]], v, aux[stack[index]]); + + deltaV1 = v; + for(PxU32 index = i1; index-->i0 ;) + deltaV1 = Fns::propagateVelocity(rows[stack[index]], jointVectors[stack[index]], SZ[stack[index]], deltaV1, aux[stack[index]]); + + deltaV0 = v; + for(PxU32 index = i0; index-->0;) + deltaV0 = Fns::propagateVelocity(rows[stack[index]], jointVectors[stack[index]], SZ[stack[index]], deltaV0, aux[stack[index]]); +} + +void PxcFsGetImpulseResponse(const FsData& matrix, + PxU32 linkID, + const Cm::SpatialVectorV& impulse, + Cm::SpatialVectorV& deltaV) +{ + typedef ArticulationFnsSimd Fns; + + PX_ASSERT(matrix.linkCount<=DY_ARTICULATION_MAX_SIZE); + Vec3V SZ[DY_ARTICULATION_MAX_SIZE]; + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + Cm::SpatialVectorV Z = -impulse; + + for(PxU32 i = linkID; i; i = matrix.parent[i]) + Z = Fns::propagateImpulse(rows[i], jointVectors[i], SZ[i], Z, aux[i]); + + deltaV = Fns::multiply(getRootInverseInertia(matrix), -Z); + + PX_ASSERT(rows[linkID].pathToRoot&1); + + for(ArticulationBitField i=rows[linkID].pathToRoot-1; i; i &= (i-1)) + { + const PxU32 index = ArticulationLowestSetBit(i); + deltaV = Fns::propagateVelocity(rows[index], jointVectors[index], SZ[index], deltaV, aux[index]); + } +} + +void PxcFsGetImpulseSelfResponse(const FsData& matrix, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1) +{ + typedef ArticulationFnsSimd Fns; + + PX_ASSERT(linkID0 != linkID1); + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + // standard case: parent-child limit + if(matrix.parent[linkID1] == linkID0) + { + Vec3V SZ; + const Cm::SpatialVectorV Z = impulse0 - Fns::propagateImpulse(rows[linkID1], jointVectors[linkID1], SZ, -impulse1, aux[linkID1]); + PxcFsGetImpulseResponse(matrix, linkID0, Z, deltaV0); + deltaV1 = Fns::propagateVelocity(rows[linkID1], jointVectors[linkID1], SZ, deltaV0, aux[linkID1]); + } + else + getImpulseResponseSlow(matrix, linkID0, impulse0, deltaV0, linkID1, impulse1, deltaV1); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector V[DY_ARTICULATION_MAX_SIZE]; + for(PxU32 i=0;i(impulse0)); + ArticulationRef::applyImpulse(matrix,V,linkID1, reinterpret_cast(impulse1)); + + Cm::SpatialVector refV0 = V[linkID0]; + Cm::SpatialVector refV1 = V[linkID1]; +#endif +} + +namespace +{ + + PX_FORCE_INLINE Cm::SpatialVectorV getImpulseResponseSimd(const FsData& matrix, PxU32 linkID, Vec3V lZ, Vec3V aZ) + { + PX_ASSERT(matrix.linkCount<=DY_ARTICULATION_MAX_SIZE); + Vec3V SZ[DY_ARTICULATION_MAX_SIZE]; + PxU32 indices[DY_ARTICULATION_MAX_SIZE], iCount = 0; + + const FsRow*PX_RESTRICT rows = getFsRows(matrix); + const FsRowAux*PX_RESTRICT aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_UNUSED(aux); + PX_ASSERT(rows[linkID].pathToRoot&1); + + lZ = V3Neg(lZ); + aZ = V3Neg(aZ); + + for(PxU32 i = linkID; i; i = matrix.parent[i]) + { + const FsRow& r = rows[i]; + const FsJointVectors& j = jointVectors[i]; + + Vec3V sz = V3Add(aZ, V3Cross(lZ, j.jointOffset)); + SZ[iCount] = sz; + + lZ = V3NegScaleSub(r.DSI[0].linear, V3GetX(sz), V3NegScaleSub(r.DSI[1].linear, V3GetY(sz), V3NegScaleSub(r.DSI[2].linear, V3GetZ(sz), lZ))); + aZ = V3NegScaleSub(r.DSI[0].angular, V3GetX(sz), V3NegScaleSub(r.DSI[1].angular, V3GetY(sz), V3NegScaleSub(r.DSI[2].angular, V3GetZ(sz), aZ))); + + aZ = V3Add(aZ, V3Cross(j.parentOffset, lZ)); + indices[iCount++] = i; + } + + const FsInertia& I = getRootInverseInertia(matrix); + + Vec3V lV = V3Neg(V3Add(M33MulV3(I.ll, lZ), M33MulV3(I.la, aZ))); + Vec3V aV = V3Neg(V3Add(M33TrnspsMulV3(I.la, lZ), M33MulV3(I.aa, aZ))); + + while(iCount) + { + PxU32 i = indices[--iCount]; + const FsRow& r = rows[i]; + const FsJointVectors& j = jointVectors[i]; + + lV = V3Sub(lV, V3Cross(j.parentOffset, aV)); + + Vec3V n = V3Add(V3Merge(V3Dot(r.DSI[0].linear, lV), V3Dot(r.DSI[1].linear, lV), V3Dot(r.DSI[2].linear, lV)), + V3Merge(V3Dot(r.DSI[0].angular, aV), V3Dot(r.DSI[1].angular, aV), V3Dot(r.DSI[2].angular, aV))); + + n = V3Add(n, M33MulV3(r.D, SZ[iCount])); + lV = V3Sub(lV, V3Cross(j.jointOffset, n)); + aV = V3Sub(aV, n); + } + + return Cm::SpatialVectorV(lV, aV); + } +} + +void ArticulationHelper::getImpulseResponse(const FsData& matrix, + PxU32 linkID, + const Cm::SpatialVectorV& impulse, + Cm::SpatialVectorV& deltaV) +{ + PX_ASSERT(matrix.linkCount<=DY_ARTICULATION_MAX_SIZE); + + deltaV = getImpulseResponseSimd(matrix, linkID, impulse.linear, impulse.angular); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVectorV deltaV_; + PxcFsGetImpulseResponse(matrix, linkID, impulse, deltaV_); + PX_ASSERT(almostEqual(deltaV_, deltaV,1e-3f)); +#endif +} + +void ArticulationHelper::getImpulseSelfResponse(const FsData& matrix, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1) +{ + PX_ASSERT(linkID0 != linkID1); + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_UNUSED(aux); + + Cm::SpatialVectorV& dV0 = deltaV0, + & dV1 = deltaV1; + + // standard case: parent-child limit + if(matrix.parent[linkID1] == linkID0) + { + const FsRow& r = rows[linkID1]; + const FsJointVectors& j = jointVectors[linkID1]; + + Vec3V lZ = V3Neg(impulse1.linear), + aZ = V3Neg(impulse1.angular); + + Vec3V sz = V3Add(aZ, V3Cross(lZ, j.jointOffset)); + + lZ = V3Sub(lZ, V3ScaleAdd(r.DSI[0].linear, V3GetX(sz), V3ScaleAdd(r.DSI[1].linear, V3GetY(sz), V3Scale(r.DSI[2].linear, V3GetZ(sz))))); + aZ = V3Sub(aZ, V3ScaleAdd(r.DSI[0].angular, V3GetX(sz), V3ScaleAdd(r.DSI[1].angular, V3GetY(sz), V3Scale(r.DSI[2].angular, V3GetZ(sz))))); + + aZ = V3Add(aZ, V3Cross(j.parentOffset, lZ)); + + lZ = V3Sub(impulse0.linear, lZ); + aZ = V3Sub(impulse0.angular, aZ); + + dV0 = getImpulseResponseSimd(matrix, linkID0, lZ, aZ); + + Vec3V aV = dV0.angular; + Vec3V lV = V3Sub(dV0.linear, V3Cross(j.parentOffset, aV)); + + Vec3V n = V3Add(V3Merge(V3Dot(r.DSI[0].linear, lV), V3Dot(r.DSI[1].linear, lV), V3Dot(r.DSI[2].linear, lV)), + V3Merge(V3Dot(r.DSI[0].angular, aV), V3Dot(r.DSI[1].angular, aV), V3Dot(r.DSI[2].angular, aV))); + + n = V3Add(n, M33MulV3(r.D, sz)); + lV = V3Sub(lV, V3Cross(j.jointOffset, n)); + aV = V3Sub(aV, n); + + dV1 = Cm::SpatialVectorV(lV, aV); + } + else + { +#if 0 + getImpulseResponseSlow(matrix, linkID0, impulse0, deltaV0, linkID1, impulse1, deltaV1); +#else + getImpulseResponse(matrix, linkID0, impulse0, deltaV0); + getImpulseResponse(matrix, linkID1, impulse1, deltaV1); +#endif + } + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVectorV dV0_, dV1_; + PxcFsGetImpulseSelfResponse(matrix, linkID0, impulse0, dV0_, linkID1, impulse1, dV1_); + + PX_ASSERT(almostEqual(dV0_, dV0, 1e-3f)); + PX_ASSERT(almostEqual(dV1_, dV1, 1e-3f)); +#endif +} + +PxU32 ArticulationHelper::getFsDataSize(PxU32 linkCount) +{ + return sizeof(FsInertia) + sizeof(FsRow) * linkCount; +} + +PxU32 ArticulationHelper::getLtbDataSize(PxU32 linkCount) +{ + return sizeof(LtbRow) * linkCount; +} + + +void ArticulationHelper::createHardLimit( const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt) +{ + init(s, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); + + ArticulationHelper::getImpulseSelfResponse(fsData, + links[linkIndex].parent,Cm::SpatialVector(PxVec3(0), axis), s.deltaVA, + linkIndex, Cm::SpatialVector(PxVec3(0), -axis), s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if(unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, joint limit ignored"); + + const PxReal recipResponse = unitResponse>0.0f ? 1.0f/unitResponse : 0.0f; + + s.constant = recipResponse * -err * recipDt; + s.unbiasedConstant = err>0.0f ? s.constant : 0.0f; + s.velMultiplier = -recipResponse; + s.impulseMultiplier = 1.0f; +} + +void ArticulationHelper::createTangentialSpring(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt) +{ + init(s, PxVec3(0), PxVec3(0), axis, axis, -PX_MAX_F32, PX_MAX_F32); + + Cm::SpatialVector axis6(PxVec3(0), axis); + PxU32 parent = links[linkIndex].parent; + getImpulseSelfResponse(fsData, parent, axis6, s.deltaVA, linkIndex, -axis6, s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if(unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, tangential spring ignored"); + const PxReal recipResponse = unitResponse>0.0F ? 1.0f/unitResponse : 0.0f; + + // this is a specialization of the spring code in setSolverConstants() for acceleration springs. + // general case is b = dt * (c.mods.spring.damping * c.velocityTarget - c.mods.spring.stiffness * geomError); + // but geomError and velocityTarget are both zero + + const PxReal a = dt * dt * stiffness + dt * damping; + const PxReal x = 1.0f/(1.0f+a); + s.constant = s.unbiasedConstant = 0.0f; + s.velMultiplier = -x * recipResponse * a; + s.impulseMultiplier = 1.0f - x; +} + +PxU32 ArticulationHelper::setupSolverConstraints( Articulation& articulation, PxU32 solverDataSize, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + const ArticulationLink* links, + const ArticulationJointTransforms* jointTransforms, + PxReal dt, + PxU32& acCount) +{ + acCount = 0; + + FsData& fsData = *articulation.getFsDataPtr(); + const PxU16 linkCount = fsData.linkCount; + PxU32 descCount = 0; + const PxReal recipDt = 1.0f/dt; + + const PxConstraintInvMassScale ims(1.0f, 1.0f, 1.0f, 1.0f); + + for(PxU16 i=1;i(*links[i].inboundJoint); + + if(i+10 || j.tangentialDamping>0); + + const PxVec3 twistAxis = jointTransforms[i].cB2w.rotate(PxVec3(1.0f,0,0)); + const PxReal tqTwistAngle = Ps::tanHalf(twist.x, twist.w); + + const bool twistLowerLimited = j.twistLimited && tqTwistAngle < Cm::tanAdd(j.tanQTwistLow, j.tanQTwistPad); + const bool twistUpperLimited = j.twistLimited && tqTwistAngle > Cm::tanAdd(j.tanQTwistHigh, -j.tanQTwistPad); + + const PxU8 constraintCount = PxU8(swingLimited + tangentialStiffness + twistUpperLimited + twistLowerLimited); + if(!constraintCount) + continue; + + PxSolverConstraintDesc& desc = constraintDesc[descCount++]; + + desc.articulationA = &articulation; + desc.linkIndexA = Ps::to16(links[i].parent); + desc.articulationALength = Ps::to16(solverDataSize); + + desc.articulationB = &articulation; + desc.linkIndexB = i; + desc.articulationBLength = Ps::to16(solverDataSize); + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeader) + + sizeof(SolverConstraint1DExt) * constraintCount; + + PX_ASSERT(0==(constraintLength & 0x0f)); + desc.constraintLengthOver16 = Ps::to16(constraintLength/16); + + //desc.constraint = stream.reserve(constraintLength + 16u, constraintBlockManager); + desc.constraint = allocator.reserveConstraintData(constraintLength + 16u); + + desc.writeBack = NULL; + + SolverConstraint1DHeader* header = reinterpret_cast(desc.constraint); + SolverConstraint1DExt* constraints = reinterpret_cast(desc.constraint + sizeof(SolverConstraint1DHeader)); + + init(*header, constraintCount, true, ims); + + PxU32 cIndex = 0; + + if(swingLimited) + { + const PxVec3 normal = jointTransforms[i].cA2w.rotate(swingLimitAxis); + createHardLimit(fsData, links, i, constraints[cIndex++], normal, swingLimitError, recipDt); + if(tangentialStiffness) + { + const PxVec3 tangent = twistAxis.cross(normal).getNormalized(); + createTangentialSpring(fsData, links, i, constraints[cIndex++], tangent, j.tangentialStiffness, j.tangentialDamping, dt); + } + } + + if(twistUpperLimited) + createHardLimit(fsData, links, i, constraints[cIndex++], twistAxis, (j.tanQTwistHigh - tqTwistAngle)*4, recipDt); + + if(twistLowerLimited) + createHardLimit(fsData, links, i, constraints[cIndex++], -twistAxis, -(j.tanQTwistLow - tqTwistAngle)*4, recipDt); + + *(desc.constraint + getConstraintLength(desc)) = 0; + + PX_ASSERT(cIndex == constraintCount); + acCount += constraintCount; + } + + return descCount; +} + + +ArticulationPImpl::ComputeUnconstrainedVelocitiesFn ArticulationPImpl::sComputeUnconstrainedVelocities[2] = { NULL, NULL }; +ArticulationPImpl::UpdateBodiesFn ArticulationPImpl::sUpdateBodies[2] = { NULL, NULL }; +ArticulationPImpl::UpdateBodiesFn ArticulationPImpl::sUpdateBodiesTGS[2] = { NULL, NULL }; +ArticulationPImpl::SaveVelocityFn ArticulationPImpl::sSaveVelocity[2] = { NULL, NULL }; +ArticulationPImpl::SaveVelocityTGSFn ArticulationPImpl::sSaveVelocityTGS[2] = { NULL, NULL }; + +ArticulationPImpl::UpdateDeltaMotionFn ArticulationPImpl::sUpdateDeltaMotion[2] = { NULL, NULL }; +ArticulationPImpl::DeltaMotionToMotionVelFn ArticulationPImpl::sDeltaMotionToMotionVel[2] = { NULL, NULL }; +ArticulationPImpl::ComputeUnconstrainedVelocitiesTGSFn ArticulationPImpl::sComputeUnconstrainedVelocitiesTGS[2] = { NULL, NULL }; + +ArticulationPImpl::SetupInternalConstraintsTGSFn ArticulationPImpl::sSetupInternalConstraintsTGS[2] = { NULL, NULL }; + +} +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h new file mode 100644 index 000000000..0775d4066 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h @@ -0,0 +1,214 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_ARTICULATION_HELPER_H +#define DY_ARTICULATION_HELPER_H + + +#include "DyArticulation.h" + +namespace physx +{ +struct PxsBodyCore; + +class PxcConstraintBlockStream; +class PxcRigidBody; +class PxsConstraintBlockManager; +struct PxSolverConstraintDesc; + +namespace Dy +{ + struct FsInertia; + struct SolverConstraint1DExt; + struct ArticulationJointCore; + struct ArticulationSolverDesc; + struct SolverConstraint1DExtStep; + struct PxcFsScratchAllocator; + struct PxTGSSolverConstraintDesc; + + +struct ArticulationJointTransforms +{ + PxTransform cA2w; // joint parent frame in world space + PxTransform cB2w; // joint child frame in world space + PxTransform cB2cA; // joint relative pose in world space +}; + +class ArticulationHelper +{ +public: + + static void getImpulseResponse( const FsData& matrix, + PxU32 linkID, + const Cm::SpatialVectorV& impulse, + Cm::SpatialVectorV& deltaV); + + + static PX_FORCE_INLINE + void getImpulseResponse( const FsData& matrix, + PxU32 linkID, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaV) + { + getImpulseResponse(matrix, linkID, reinterpret_cast(impulse), reinterpret_cast(deltaV)); + } + + static void getImpulseSelfResponse(const FsData& matrix, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1); + + //static void flushVelocity(FsData& matrix); + + static void saveVelocity(const ArticulationSolverDesc& m, Cm::SpatialVectorF* deltaV); + + static void saveVelocityTGS(const ArticulationSolverDesc& m, const PxReal invDt); + + //static void recordDeltaMotion(const ArticulationSolverDesc& m, PxReal dt); + + //static void deltaMotionToMotionVelocity(const ArticulationSolverDesc& m, PxReal invDt); + + //static void getDataSizes(PxU32 linkCount, PxU32 &solverDataSize, PxU32& totalSize, PxU32& scratchSize); + + /*static void initializeDriveCache(Articulation& articulation, + FsData &data, + PxU16 linkCount, + const ArticulationLink* links, + PxReal compliance, + PxU32 iterations, + char* scratchMemory, + PxU32 scratchMemorySize);*/ + + //static PxU32 getDriveCacheLinkCount(const FsData& cache); + + /*static void applyImpulses(const FsData& matrix, + Cm::SpatialVectorV* Z, + Cm::SpatialVectorV* V);*/ + +//private: + static PxU32 getLtbDataSize(PxU32 linkCount); + static PxU32 getFsDataSize(PxU32 linkCount); + + //static void prepareDataBlock(ArticulationV& articualtion, + // const ArticulationLink* links, + // PxU16 linkCount, + // PxTransform* poses, + // FsInertia *baseInertia, + // ArticulationJointTransforms* jointTransforms, + // PxU32 expectedSize); + + //static void setInertia(FsInertia& inertia, + // const PxsBodyCore& body, + // const PxTransform& pose); + + //static void setJointTransforms(ArticulationJointTransforms& transforms, + // const PxTransform& parentPose, + // const PxTransform& childPose, + // const ArticulationJointCore& joint); + + /*static void prepareLtbMatrix(FsData& fsData, + const FsInertia* baseInertia, + const PxTransform* poses, + const ArticulationJointTransforms* jointTransforms, + PxReal recipDt);*/ + + //static void prepareFsData(FsData& fsData, + // const ArticulationLink* links); + + //static PX_FORCE_INLINE PxReal getResistance(PxReal compliance); + + + static void createHardLimit(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt); + + static void createTangentialSpring(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt); + + static void createHardLimitTGS(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt); + + static void createTangentialSpringTGS(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt); + + static PxU32 setupSolverConstraints(Articulation& articulation, PxU32 solverDataSize, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + const ArticulationLink* links, + const ArticulationJointTransforms* jointTransforms, + PxReal dt, + PxU32& acCount); + + /*static void computeUnconstrainedVelocitiesInternal(const ArticulationSolverDesc& desc, + PxReal dt, + const PxVec3& gravity, PxU64 contextID, + FsInertia* PX_RESTRICT baseInertia, + ArticulationJointTransforms* PX_RESTRICT jointTransforms, + PxcFsScratchAllocator& allocator);*/ + + /*static void computeJointDrives(FsData& fsData, + Ps::aos::Vec3V* drives, + const ArticulationLink* links, + const PxTransform* poses, + const ArticulationJointTransforms* transforms, + const Ps::aos::Mat33V* loads, + PxReal dt);*/ + +}; + +} + +} + +#endif //DY_ARTICULATION_HELPER_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h new file mode 100644 index 000000000..917c49682 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h @@ -0,0 +1,203 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_INTERFACE_H +#define DY_ARTICULATION_INTERFACE_H + +#include "DyArticulationUtils.h" + +namespace physx +{ + +class PxcConstraintBlockStream; +class PxcScratchAllocator; +class PxsConstraintBlockManager; +struct PxSolverConstraintDesc; + +namespace Dy +{ + + struct ArticulationSolverDesc; + + +class ArticulationPImpl +{ +public: + + typedef PxU32 (*ComputeUnconstrainedVelocitiesFn)(const ArticulationSolverDesc& desc, + PxReal dt, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, PxU64 contextID, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); + + typedef void (*UpdateBodiesFn)(const ArticulationSolverDesc& desc, + PxReal dt); + + typedef void (*SaveVelocityFn)(const ArticulationSolverDesc &m, Cm::SpatialVectorF* deltaV); + + typedef void(*SaveVelocityTGSFn)(const ArticulationSolverDesc& m, PxReal invDtF32); + + typedef PxU32(*SetupInternalConstraintsTGSFn)(const ArticulationSolverDesc& desc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* Z); + + typedef void(*ComputeUnconstrainedVelocitiesTGSFn)(const ArticulationSolverDesc& desc, + PxReal dt, + const PxVec3& gravity, PxU64 contextID, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); + + typedef void(*UpdateDeltaMotionFn)(const ArticulationSolverDesc &m, const PxReal dt, Cm::SpatialVectorF* DeltaV); + + typedef void(*DeltaMotionToMotionVelFn)(const ArticulationSolverDesc &m, const PxReal dt); + + static ComputeUnconstrainedVelocitiesFn sComputeUnconstrainedVelocities[2]; + static UpdateBodiesFn sUpdateBodies[2]; + static UpdateBodiesFn sUpdateBodiesTGS[2]; + static SaveVelocityFn sSaveVelocity[2]; + static SaveVelocityTGSFn sSaveVelocityTGS[2]; + + static UpdateDeltaMotionFn sUpdateDeltaMotion[2]; + static DeltaMotionToMotionVelFn sDeltaMotionToMotionVel[2]; + static ComputeUnconstrainedVelocitiesTGSFn sComputeUnconstrainedVelocitiesTGS[2]; + static SetupInternalConstraintsTGSFn sSetupInternalConstraintsTGS[2]; + + static PxU32 computeUnconstrainedVelocities(const ArticulationSolverDesc& desc, + PxReal dt, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + PxcScratchAllocator&, + const PxVec3& gravity, PxU64 contextID, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sComputeUnconstrainedVelocities[type]); + if (sComputeUnconstrainedVelocities[type]) + return (sComputeUnconstrainedVelocities[type])(desc, dt, allocator, constraintDesc, acCount, + gravity, contextID, Z, deltaV); + else + return 0; + } + + static void updateBodies(const ArticulationSolverDesc& desc, + PxReal dt) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sUpdateBodies[type]); + if (sUpdateBodies[type]) + (*sUpdateBodies[type])(desc, dt); + } + + static void updateBodiesTGS(const ArticulationSolverDesc& desc, + PxReal dt) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sUpdateBodiesTGS[type]); + if (sUpdateBodiesTGS[type]) + (*sUpdateBodiesTGS[type])(desc, dt); + } + + static void saveVelocity(const ArticulationSolverDesc& desc, Cm::SpatialVectorF* deltaV) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sSaveVelocity[type]); + if (sSaveVelocity[type]) + (*sSaveVelocity[type])(desc, deltaV); + } + + + static void saveVelocityTGS(const ArticulationSolverDesc& desc, PxReal invDtF32) + { + PX_UNUSED(desc); + PX_UNUSED(invDtF32); + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sSaveVelocityTGS[type]); + if (sSaveVelocityTGS[type]) + (*sSaveVelocityTGS[type])(desc, invDtF32); + } + + static void computeUnconstrainedVelocitiesTGS(const ArticulationSolverDesc& desc, + PxReal dt, + const PxVec3& gravity, PxU64 contextID, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sComputeUnconstrainedVelocitiesTGS[type]); + if (sComputeUnconstrainedVelocitiesTGS[type]) + (sComputeUnconstrainedVelocitiesTGS[type])(desc, dt, gravity, contextID, Z, DeltaV); + } + + static void updateDeltaMotion(const ArticulationSolverDesc& desc, const PxReal dt, Cm::SpatialVectorF* DeltaV) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sUpdateDeltaMotion[type]); + if (sUpdateDeltaMotion[type]) + (*sUpdateDeltaMotion[type])(desc, dt, DeltaV); + } + + static void deltaMotionToMotionVel(const ArticulationSolverDesc& desc, const PxReal invDt) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sDeltaMotionToMotionVel[type]); + if (sDeltaMotionToMotionVel[type]) + (*sDeltaMotionToMotionVel)(desc, invDt); + } + + static PxU32 setupSolverInternalConstraintsTGS(const ArticulationSolverDesc& desc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* Z) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sSetupInternalConstraintsTGS[type]); + if (sSetupInternalConstraintsTGS[type]) + return sSetupInternalConstraintsTGS[type](desc, stream, constraintDesc, dt, invDt, totalDt, acCount, constraintBlockManager, Z); + return 0; + + } +}; + + +} +} +#endif //DY_ARTICULATION_INTERFACE_H + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h new file mode 100644 index 000000000..cece78469 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_REFERENCE_H +#define DY_ARTICULATION_REFERENCE_H + +// a per-row struct where we put extra data for debug and setup - ultimately this will move to be just +// debug only + + + +#include "DyArticulationUtils.h" +#include "DyArticulationScalar.h" +#include "DyArticulationFnsScalar.h" +#include "DySpatial.h" + +#if DY_ARTICULATION_DEBUG_VERIFY + +namespace physx +{ + +PX_FORCE_INLINE Cm::SpatialVector propagateVelocity(const FsRow& row, + const FsJointVectors& jv, + const PxVec3& SZ, + const Cm::SpatialVector& v, + const FsRowAux& aux) +{ + typedef ArticulationFnsScalar Fns; + + Cm::SpatialVector w = Fns::translateMotion(-getParentOffset(jv), v); + PxVec3 DSZ = Fns::multiply(row.D, SZ); + + PxVec3 n = Fns::axisDot(getDSI(row), w) + DSZ; + Cm::SpatialVector result = w - Cm::SpatialVector(getJointOffset(jv).cross(n),n); +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector check = ArticulationRef::propagateVelocity(row, jv, SZ, v, aux); + PX_ASSERT((result-check).magnitude()<1e-5*PxMax(check.magnitude(), 1.0f)); +#endif + return result; +} + +PX_FORCE_INLINE Cm::SpatialVector propagateImpulse(const FsRow& row, + const FsJointVectors& jv, + PxVec3& SZ, + const Cm::SpatialVector& Z, + const FsRowAux& aux) +{ + typedef ArticulationFnsScalar Fns; + + SZ = Z.angular + Z.linear.cross(getJointOffset(jv)); + Cm::SpatialVector result = Fns::translateForce(getParentOffset(jv), Z - Fns::axisMultiply(getDSI(row), SZ)); +#if DY_ARTICULATION_DEBUG_VERIFY + PxVec3 SZcheck; + Cm::SpatialVector check = ArticulationRef::propagateImpulse(row, jv, SZcheck, Z, aux); + PX_ASSERT((result-check).magnitude()<1e-5*PxMax(check.magnitude(), 1.0f)); + PX_ASSERT((SZ-SZcheck).magnitude()<1e-5*PxMax(SZcheck.magnitude(), 1.0f)); +#endif + return result; +} + +} +#endif + +#endif //DY_ARTICULATION_REFERENCE_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp new file mode 100644 index 000000000..d5b5466c8 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp @@ -0,0 +1,317 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "DySpatial.h" +#include "DyArticulation.h" +#include "DyArticulationScalar.h" +#include "DyArticulationFnsScalar.h" +#include "DyArticulationReference.h" +#include "DyArticulationFnsSimd.h" + + +namespace physx +{ +namespace Dy +{ + +#if DY_ARTICULATION_DEBUG_VERIFY +namespace +{ + Cm::SpatialVector SpV(Vec3V linear, Vec3V angular) + { + return Cm::SpatialVector((PxVec3 &)linear, (PxVec3&)angular); + } +} +#endif + +//void PxcFsApplyImpulse(ArticulationV &articulationV, +// PxU32 linkID, +// Vec3V linear, +// Vec3V angular) +//{ +//#if DY_ARTICULATION_DEBUG_VERIFY +// { +// Cm::SpatialVectorV imp(linear, angular); +// ArticulationRef::applyImpulse(matrix, reinterpret_cast(getRefVelocity(matrix)), linkID, reinterpret_cast(imp)); +// } +//#endif +// Articulation& articulation = static_cast<>(articulationV +// FsData& matrix = *articulation.getFsDataPtr(); +// Vec3V linZ = V3Neg(linear); +// Vec3V angZ = V3Neg(angular); +// +// const FsRow *rows = getFsRows(matrix); +// const FsJointVectors* jointVectors = getJointVectors(matrix); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// const FsRowAux *aux = getAux(matrix); +//#endif +// Vec3V *deferredSZ = getDeferredSZ(matrix); +// +// for(PxU32 i = linkID; i!=0; i = matrix.parent[i]) +// { +// const FsRow &row = rows[i]; +// const FsJointVectors& jv = jointVectors[i]; +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// PxVec3 SZcheck; +// Cm::SpatialVector Zcheck = ArticulationRef::propagateImpulse(row, jv, SZcheck, SpV(linZ, angZ), aux[i]); +//#endif +// +// Vec3V SZ = V3Add(angZ, V3Cross(linZ, jv.jointOffset)); +// Vec3V lrLinear = V3Sub(linZ, V3ScaleAdd(row.DSI[0].linear, V3GetX(SZ), +// V3ScaleAdd(row.DSI[1].linear, V3GetY(SZ), +// V3Scale(row.DSI[2].linear, V3GetZ(SZ))))); +// +// Vec3V lrAngular = V3Sub(angZ, V3ScaleAdd(row.DSI[0].angular, V3GetX(SZ), +// V3ScaleAdd(row.DSI[1].angular, V3GetY(SZ), +// V3Scale(row.DSI[2].angular, V3GetZ(SZ))))); +// +// linZ = lrLinear; +// angZ = V3Add(lrAngular, V3Cross(jv.parentOffset, lrLinear)); +// deferredSZ[i] = V3Add(deferredSZ[i], SZ); +// +// PX_ASSERT(Ps::aos::isFiniteVec3V(linZ)); +// PX_ASSERT(Ps::aos::isFiniteVec3V(angZ)); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector Z = SpV(linZ,angZ); +// PX_ASSERT((Z - Zcheck).magnitude()<1e-4*PxMax(Zcheck.magnitude(), 1.0f)); +// PX_ASSERT(((PxVec3&)SZ-SZcheck).magnitude()<1e-4*PxMax(SZcheck.magnitude(), 1.0f)); +//#endif +// } +// +// matrix.deferredZ.linear = V3Add(matrix.deferredZ.linear, linZ); +// matrix.deferredZ.angular = V3Add(matrix.deferredZ.angular, angZ); +// +// matrix.dirty |= rows[linkID].pathToRoot; +//} + +//Cm::SpatialVectorV PxcFsGetVelocity(Articulation &articulation, +// PxU32 linkID) +//{ +// FsData& matrix = *articulation.getFsDataPtr(); +// const FsRow *rows = getFsRows(matrix); +// const FsJointVectors* jointVectors = getJointVectors(matrix); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// const FsRowAux *aux = getAux(matrix); +//#endif +// Cm::SpatialVectorV* PX_RESTRICT V = getVelocity(matrix); +// +// // find the dirty node on the path (including the root) with the lowest index +// ArticulationBitField toUpdate = rows[linkID].pathToRoot & matrix.dirty; +// +// +// if(toUpdate) +// { +// // store the dV elements densely and use an array map to decode - hopefully cache friendlier +// PxU32 indexToStackLoc[DY_ARTICULATION_MAX_SIZE], count = 0; +// Cm::SpatialVectorV dVStack[DY_ARTICULATION_MAX_SIZE]; +// +// ArticulationBitField ignoreNodes = (toUpdate & (0-toUpdate))-1; +// ArticulationBitField path = rows[linkID].pathToRoot & ~ignoreNodes, p = path; +// ArticulationBitField newDirty = 0; +// +// Vec3V ldV = V3Zero(), adV = V3Zero(); +// Cm::SpatialVectorV* PX_RESTRICT defV = getDeferredVel(matrix); +// Vec3V* PX_RESTRICT SZ = getDeferredSZ(matrix); +// +// if(p & 1) +// { +// const FsInertia &m = getRootInverseInertia(matrix); +// Vec3V lZ = V3Neg(matrix.deferredZ.linear); +// Vec3V aZ = V3Neg(matrix.deferredZ.angular); +// +// ldV = V3Add(M33MulV3(m.ll,lZ), M33MulV3(m.la,aZ)); +// adV = V3Add(M33TrnspsMulV3(m.la,lZ), M33MulV3(m.aa,aZ)); +// +// V[0].linear = V3Add(V[0].linear, ldV); +// V[0].angular = V3Add(V[0].angular, adV); +// +// matrix.deferredZ.linear = V3Zero(); +// matrix.deferredZ.angular = V3Zero(); +// +// indexToStackLoc[0] = count; +// Cm::SpatialVectorV &e = dVStack[count++]; +// +// e.linear = ldV; +// e.angular = adV; +// +// newDirty = rows[0].children; +// p--; +// } +// +// +// while(p) // using "for(;p;p &= (p-1))" here generates LHSs from the ArticulationLowestSetBit +// { +// PxU32 i = ArticulationLowestSetBit(p); +// const FsJointVectors& jv = jointVectors[i]; +// +// p &= (p-1); +// +// const FsRow* PX_RESTRICT row = rows + i; +// +// ldV = V3Add(ldV, defV[i].linear); +// adV = V3Add(adV, defV[i].angular); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector dVcheck = ArticulationRef::propagateVelocity(*row, jv, (PxVec3&)SZ[i], SpV(ldV,adV), aux[i]); +//#endif +// +// Vec3V DSZ = M33MulV3(row->D, SZ[i]); +// +// Vec3V lW = V3Add(ldV, V3Cross(adV,jv.parentOffset)); +// Vec3V aW = adV; +// +// const Cm::SpatialVectorV*PX_RESTRICT DSI = row->DSI; +// Vec3V lN = V3Merge(V3Dot(DSI[0].linear, lW), V3Dot(DSI[1].linear, lW), V3Dot(DSI[2].linear, lW)); +// Vec3V aN = V3Merge(V3Dot(DSI[0].angular, aW), V3Dot(DSI[1].angular, aW), V3Dot(DSI[2].angular, aW)); +// +// Vec3V n = V3Add(V3Add(lN, aN), DSZ); +// +// ldV = V3Sub(lW, V3Cross(jv.jointOffset,n)); +// adV = V3Sub(aW, n); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector dV = SpV(ldV,adV); +// PX_ASSERT((dV-dVcheck).magnitude()<1e-4*PxMax(dVcheck.magnitude(), 1.0f)); +//#endif +// +// V[i].linear = V3Add(V[i].linear, ldV); +// V[i].angular = V3Add(V[i].angular, adV); +// +// defV[i].linear = V3Zero(); +// defV[i].angular = V3Zero(); +// SZ[i] = V3Zero(); +// +// indexToStackLoc[i] = count; +// Cm::SpatialVectorV &e = dVStack[count++]; +// newDirty |= rows[i].children; +// +// e.linear = ldV; +// e.angular = adV; +// } +// +// for(ArticulationBitField defer = newDirty&~path; defer; defer &= (defer-1)) +// { +// PxU32 i = ArticulationLowestSetBit(defer); +// PxU32 parent = indexToStackLoc[matrix.parent[i]]; +// +// defV[i].linear = V3Add(defV[i].linear, dVStack[parent].linear); +// defV[i].angular = V3Add(defV[i].angular, dVStack[parent].angular); +// } +// +// matrix.dirty = (matrix.dirty | newDirty)&~path; +// } +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector v = reinterpret_cast(V[linkID]); +// Cm::SpatialVector rv = reinterpret_cast(getRefVelocity(matrix)[linkID]); +// PX_ASSERT((v-rv).magnitude()<1e-4f * PxMax(rv.magnitude(),1.0f)); +//#endif +// +// return V[linkID]; +//} + +Cm::SpatialVectorV PxcFsGetMotionVector(ArticulationV& articulation, + PxU32 linkID) +{ + + /*const Cm::SpatialVectorV* PX_RESTRICT V = articulation.getMotionVector(); + return V[linkID];*/ + + return articulation.getLinkMotionVector(linkID); +} + +PX_FORCE_INLINE Cm::SpatialVectorV propagateVelocitySIMD(const FsRow& row, + const FsJointVectors& jv, + const Vec3V& SZ, + const Cm::SpatialVectorV& v, + const FsRowAux& aux) +{ + PX_UNUSED(aux); + + typedef ArticulationFnsSimd Fns; + + Cm::SpatialVectorV w(V3Add(v.linear, V3Cross(v.angular, jv.parentOffset)), v.angular); + Vec3V DSZ = M33MulV3(row.D, SZ); + + Vec3V n = V3Add(Fns::axisDot(row.DSI, w), DSZ); + Cm::SpatialVectorV result = w - Cm::SpatialVectorV(V3Cross(jv.jointOffset,n), n); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector check = ArticulationRef::propagateVelocity(row, jv, reinterpret_cast(SZ), reinterpret_cast(v), aux); + PX_ASSERT((reinterpret_cast(result)-check).magnitude()<1e-4*PxMax(check.magnitude(), 1.0f)); +#endif + + return result; +} + +void PxcFsFlushVelocity(FsData& matrix) +{ + typedef ArticulationFnsSimd Fns; + + const FsRow* PX_RESTRICT rows = getFsRows(matrix); + const FsRowAux* PX_RESTRICT aux = getAux(matrix); + const FsJointVectors*PX_RESTRICT jointVectors = getJointVectors(matrix); + + Cm::SpatialVectorV V0 = Fns::multiply(getRootInverseInertia(matrix), -matrix.deferredZ); + matrix.deferredZ = Cm::SpatialVectorV(PxZero); + + getVelocity(matrix)[0] += V0; + for(ArticulationBitField defer = rows[0].children; defer; defer &= (defer-1)) + getDeferredVel(matrix)[ArticulationLowestSetBit(defer)] += V0; + + for(PxU32 i = 1; i(getRefVelocity(matrix)[i]); + Cm::SpatialVector diff = v-rv; + PxReal m = rv.magnitude(); + PX_UNUSED(m); + PX_ASSERT(diff.magnitude()<1e-4*PxMax(1.0f,m)); + } +#endif + + matrix.dirty = 0; +} +} +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp new file mode 100644 index 000000000..5889bb330 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp @@ -0,0 +1,575 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "DyArticulationUtils.h" +#include "DyArticulationScalar.h" +#include "DyArticulationReference.h" +#include "DyArticulationFnsDebug.h" + +namespace physx +{ +namespace Dy +{ +namespace ArticulationRef +{ + Cm::SpatialVector propagateImpulse(const FsRow& row, + const FsJointVectors& j, + PxVec3& SZ, + const Cm::SpatialVector& Z, + const FsRowAux& aux) + { + typedef ArticulationFnsScalar Fns; + + SZ = Fns::axisDot(reinterpret_cast(aux.S), Z); + return Fns::translateForce(getParentOffset(j), Z - Fns::axisMultiply(getDSI(row), SZ)); + } + + Cm::SpatialVector propagateVelocity(const FsRow& row, + const FsJointVectors& j, + const PxVec3& SZ, + const Cm::SpatialVector& v, + const FsRowAux& aux) + { + typedef ArticulationFnsScalar Fns; + + Cm::SpatialVector w = Fns::translateMotion(-getParentOffset(j), v); + PxVec3 DSZ = Fns::multiply(row.D, SZ); + + return w - Fns::axisMultiply(reinterpret_cast(aux.S), DSZ + Fns::axisDot(getDSI(row), w)); + } + + void applyImpulse(const FsData& matrix, + Cm::SpatialVector* velocity, + PxU32 linkID, + const Cm::SpatialVector& impulse) + { + typedef ArticulationFnsScalar Fns; + + PX_ASSERT(matrix.linkCount<=DY_ARTICULATION_MAX_SIZE); + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + Cm::SpatialVector dV[DY_ARTICULATION_MAX_SIZE]; + PxVec3 SZ[DY_ARTICULATION_MAX_SIZE]; + + for(PxU32 i=0;i0;) + { + LtbRow& b = rows[i]; + inertia[i] = Fns::invertInertia(inertia[i]); + PxU32 p = m.parent[i]; + + Cm::SpatialVector* j0 = &reinterpret_cast(*b.j0), + * j1 = &reinterpret_cast(*b.j1); + + Fns::multiply(j, inertia[i], j1); + PxMat33 jResponse = Fns::invertSym33(-Fns::multiplySym(j, j1)); + j1[0] = j[0]; j1[1] = j[1]; j1[2] = j[2]; + + b.jResponse = Mat33V_From_PxMat33(jResponse); + Fns::multiply(j, j0, jResponse); + inertia[p] = Fns::multiplySubtract(inertia[p], j, j0); + j0[0] = j[0]; j0[1] = j[1]; j0[2] = j[2]; + } + + rows[0].inertia = Fns::invertInertia(inertia[0]); + for(PxU32 i=1;i(c); + const LtbRow* rows = getLtbRows(m); + PxMemZero(y, m.linkCount*sizeof(Cm::SpatialVector)); + + for(PxU32 i=m.linkCount;i-->1;) + { + PxU32 p = m.parent[i]; + const LtbRow& r = rows[i]; + b[i] -= PxVec4(Fns::axisDot(&static_cast(*r.j1), y[i]),0); + y[p] -= Fns::axisMultiply(&static_cast(*r.j0), b[i].getXYZ()); + } + + y[0] = Fns::multiply(rows[0].inertia,y[0]); + + for(PxU32 i=1; i(*r.j0), y[p]); + y[i] = Fns::multiply(r.inertia, y[i]) - Fns::axisMultiply(&static_cast(*r.j1), t); + } +} + +void PxcFsPropagateDrivenInertiaScalar(FsData& matrix, + const FsInertia* baseInertia, + const PxReal* isf, + const Mat33V* load) +{ + typedef ArticulationFnsScalar Fns; + + Cm::SpatialVector IS[3], DSI[3]; + PxMat33 D; + + FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + SpInertia inertia[DY_ARTICULATION_MAX_SIZE]; + for(PxU32 i=0;i0;) + { + FsRow& r = rows[i]; + const FsRowAux& a = aux[i]; + const FsJointVectors& jv = jointVectors[i]; + + Fns::multiply(IS, inertia[i], &static_cast(*a.S)); + + PX_ALIGN(16, PxMat33) L; + PxMat33_From_Mat33V(load[i], L); + D = Fns::invertSym33(Fns::multiplySym(&static_cast(*a.S), IS) + L*isf[i]); + + Fns::multiply(DSI, IS, D); + + r.D = Mat33V_From_PxMat33(D); + static_cast(r.DSI[0]) = DSI[0]; + static_cast(r.DSI[1]) = DSI[1]; + static_cast(r.DSI[2]) = DSI[2]; + + inertia[matrix.parent[i]] += Fns::translate(getParentOffset(jv), Fns::multiplySubtract(inertia[i], DSI, IS)); + } + + FsInertia& m = getRootInverseInertia(matrix); + m = FsInertia(Fns::invertInertia(inertia[0])); +} + +// no need to compile this ecxcept for verification, and it consumes huge amounts of stack space +void PxcFsComputeJointLoadsScalar(const FsData& matrix, + const FsInertia*PX_RESTRICT baseInertia, + Mat33V*PX_RESTRICT load, + const PxReal*PX_RESTRICT isf, + PxU32 linkCount, + PxU32 maxIterations) +{ + typedef ArticulationFnsScalar Fns; + + // the childward S + SpInertia leafwardInertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia rootwardInertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia inertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia contribToParent[DY_ARTICULATION_MAX_SIZE]; + + // total articulated inertia assuming the articulation is rooted here + + const FsRow* row = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_UNUSED(row); + + PxMat33 load_[DY_ARTICULATION_MAX_SIZE]; + + for(PxU32 iter=0;iter1;) + { + const FsJointVectors& j = jointVectors[i]; + + leafwardInertia[i] = inertia[i]; + contribToParent[i] = Fns::propagate(inertia[i], &static_cast(*aux[i].S), load_[i], isf[i]); + inertia[matrix.parent[i]] += Fns::translate((PxVec3&)j.parentOffset, contribToParent[i]); + } + + for(PxU32 i=1;i(*aux[i].S), load_[i], isf[i]); + } + + for(PxU32 i=1;i(*aux[i].S)); + PX_ASSERT(load_[i][0].isFinite() && load_[i][1].isFinite() && load_[2][i].isFinite()); + } + } + for(PxU32 i=1;i(state.deferredZ) += Z; + state.dirty |= matrix.row[linkID].pathToRoot; +} + +Cm::SpatialVector PxcFsGetVelocity(const FsData& matrix, + PxU32 linkID) +{ + // find the dirty node on the path (including the root) with the lowest index + ArticulationBitField toUpdate = matrix.row[linkID].pathToRoot & state.dirty; + + if(toUpdate) + { + ArticulationBitField ignoreNodes = (toUpdate & (0-toUpdate))-1; + ArticulationBitField path = matrix.row[linkID].pathToRoot & ~ignoreNodes, p = path; + ArticulationBitField newDirty = 0; + + Cm::SpatialVector dV = Cm::SpatialVector::zero(); + if(p & 1) + { + dV = getRootDeltaV(matrix, -deferredZ(state)); + + velocityRef(state, 0) += dV; + for(ArticulationBitField defer = matrix.row[0].children & ~path; defer; defer &= (defer-1)) + deferredVelRef(state, ArticulationLowestSetBit(defer)) += dV; + + deferredZRef(state) = Cm::SpatialVector::zero(); + newDirty = matrix.row[0].children; + p--; + } + + for(; p; p &= (p-1)) + { + PxU32 i = ArticulationLowestSetBit(p); + + dV = propagateVelocity(matrix.row[i], deferredSZ(state,i), dV + state.deferredVel[i], matrix.aux[i]); + + velocityRef(state,i) += dV; + for(ArticulationBitField defer = matrix.row[i].children & ~path; defer; defer &= (defer-1)) + deferredVelRef(state,ArticulationLowestSetBit(defer)) += dV; + + newDirty |= matrix.row[i].children; + deferredVelRef(state,i) = Cm::SpatialVector::zero(); + deferredSZRef(state,i) = PxVec3(0); + } + + state.dirty = (state.dirty | newDirty)&~path; + } +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector v = state.velocity[linkID]; + Cm::SpatialVector rv = state.refVelocity[linkID]; + PX_ASSERT((v-rv).magnitude()<1e-4f * rv.magnitude()); +#endif + + return state.velocity[linkID]; +} + +void PxcFsFlushVelocity(const FsData& matrix) +{ + Cm::SpatialVector V = getRootDeltaV(matrix, -deferredZ(state)); + deferredZRef(state) = Cm::SpatialVector::zero(); + velocityRef(state,0) += V; + for(ArticulationBitField defer = matrix.row[0].children; defer; defer &= (defer-1)) + deferredVelRef(state,ArticulationLowestSetBit(defer)) += V; + + for(PxU32 i = 1; i Fns; + + Cm::SpatialVectorV IS[3]; + PxMat33 D; + + FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + FsInertia *inertia = allocator.alloc(matrix.linkCount); + PxMemCopy(inertia, baseInertia, matrix.linkCount*sizeof(FsInertia)); + + for(PxU32 i=matrix.linkCount; --i>0;) + { + FsRow& r = rows[i]; + const FsRowAux& a = aux[i]; + const FsJointVectors& jv = jointVectors[i]; + + Mat33V m = Fns::computeSIS(inertia[i], a.S, IS); + FloatV f = FLoad(isf[i]); + + Mat33V D = Fns::invertSym33(Mat33V(V3ScaleAdd(load[i].col0, f, m.col0), + V3ScaleAdd(load[i].col1, f, m.col1), + V3ScaleAdd(load[i].col2, f, m.col2))); + r.D = D; + + inertia[matrix.parent[i]] = Fns::addInertia(inertia[matrix.parent[i]], + Fns::translateInertia(jv.parentOffset, Fns::multiplySubtract(inertia[i], D, IS, r.DSI))); + } + + getRootInverseInertia(matrix) = Fns::invertInertia(inertia[0]); +} + +void PxcLtbSolve(const FsData& m, + Vec3V* c, // rhs error to solve for + Cm::SpatialVector* y) // velocity delta output +{ + typedef ArticulationFnsScalar Fns; + + PxVec4* b = reinterpret_cast(c); + const LtbRow* rows = getLtbRows(m); + PxMemZero(y, m.linkCount*sizeof(Cm::SpatialVector)); + + for(PxU32 i=m.linkCount;i-->1;) + { + PxU32 p = m.parent[i]; + const LtbRow& r = rows[i]; + b[i] -= PxVec4(Fns::axisDot(&static_cast(*r.j1), y[i]),0); + y[p] -= Fns::axisMultiply(&static_cast(*r.j0), b[i].getXYZ()); + } + + y[0] = Fns::multiply(rows[0].inertia,y[0]); + + for(PxU32 i=1; i(*r.j0), y[p]); + y[i] = Fns::multiply(r.inertia, y[i]) - Fns::axisMultiply(&static_cast(*r.j1), t); + } +} + + +#endif + + +#if DY_ARTICULATION_DEBUG_VERIFY +void PxcLtbFactorScalar(FsData& m) +{ + typedef ArticulationFnsScalar Fns; + LtbRow* rows = getLtbRows(m); + + SpInertia inertia[DY_ARTICULATION_MAX_SIZE]; + for(PxU32 i=0;i0;) + { + LtbRow& b = rows[i]; + inertia[i] = Fns::invertInertia(inertia[i]); + PxU32 p = m.parent[i]; + + Cm::SpatialVector* j0 = &reinterpret_cast(*b.j0), + * j1 = &reinterpret_cast(*b.j1); + + Fns::multiply(j, inertia[i], j1); + PxMat33 jResponse = Fns::invertSym33(-Fns::multiplySym(j, j1)); + j1[0] = j[0]; j1[1] = j[1]; j1[2] = j[2]; + + b.jResponse = Mat33V_From_PxMat33(jResponse); + Fns::multiply(j, j0, jResponse); + inertia[p] = Fns::multiplySubtract(inertia[p], j, j0); + j0[0] = j[0]; j0[1] = j[1]; j0[2] = j[2]; + } + + rows[0].inertia = Fns::invertInertia(inertia[0]); + for(PxU32 i=1;i0;) + { + FsRow& r = rows[i]; + const FsRowAux& a = aux[i]; + const FsJointVectors& jv = jointVectors[i]; + + Fns::multiply(IS, inertia[i], &reinterpret_cast(*a.S)); + + PX_ALIGN(16, PxMat33) L; + PxMat33_From_Mat33V(load[i], L); + D = Fns::invertSym33(Fns::multiplySym(&reinterpret_cast(*a.S), IS) + L*isf[i]); + + Fns::multiply(DSI, IS, D); + + r.D = Mat33V_From_PxMat33(D); + reinterpret_cast(r.DSI[0]) = DSI[0]; + reinterpret_cast(r.DSI[1]) = DSI[1]; + reinterpret_cast(r.DSI[2]) = DSI[2]; + + inertia[matrix.parent[i]] += Fns::translate(getParentOffset(jv), Fns::multiplySubtract(inertia[i], DSI, IS)); + } + + FsInertia& m = getRootInverseInertia(matrix); + m = FsInertia(Fns::invertInertia(inertia[0])); +} + +// no need to compile this ecxcept for verification, and it consumes huge amounts of stack space +void PxcFsComputeJointLoadsScalar(const FsData& matrix, + const FsInertia*PX_RESTRICT baseInertia, + Mat33V*PX_RESTRICT load, + const PxReal*PX_RESTRICT isf, + PxU32 linkCount, + PxU32 maxIterations) +{ + typedef ArticulationFnsScalar Fns; + + // the childward S + SpInertia leafwardInertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia rootwardInertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia inertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia contribToParent[DY_ARTICULATION_MAX_SIZE]; + + // total articulated inertia assuming the articulation is rooted here + + const FsRow* row = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_UNUSED(row); + + PxMat33 load_[DY_ARTICULATION_MAX_SIZE]; + + for(PxU32 iter=0;iter1;) + { + const FsJointVectors& j = jointVectors[i]; + + leafwardInertia[i] = inertia[i]; + contribToParent[i] = Fns::propagate(inertia[i], &reinterpret_cast(*aux[i].S), load_[i], isf[i]); + inertia[matrix.parent[i]] += Fns::translate((PxVec3&)j.parentOffset, contribToParent[i]); + } + + for(PxU32 i=1;i(*aux[i].S), load_[i], isf[i]); + } + + for(PxU32 i=1;i(*aux[i].S)); + PX_ASSERT(load_[i][0].isFinite() && load_[i][1].isFinite() && load_[2][i].isFinite()); + } + } + for(PxU32 i=1;i(getVelocity(m)[i]); +} + +PX_FORCE_INLINE Cm::SpatialVector& deferredVelRef(FsData &m, PxU32 i) +{ + return reinterpret_cast(getDeferredVel(m)[i]); +} + +PX_FORCE_INLINE PxVec3& deferredSZRef(FsData &m, PxU32 i) +{ + return reinterpret_cast(getDeferredSZ(m)[i]); +} + +PX_FORCE_INLINE const PxVec3& deferredSZ(const FsData &s, PxU32 i) +{ + return reinterpret_cast(getDeferredSZ(s)[i]); +} + +PX_FORCE_INLINE Cm::SpatialVector& deferredZRef(FsData &s) +{ + return unsimdRef(s.deferredZ); +} + + +PX_FORCE_INLINE const Cm::SpatialVector& deferredZ(const FsData &s) +{ + return unsimdRef(s.deferredZ); +} + +PX_FORCE_INLINE const PxVec3& getJointOffset(const FsJointVectors& j) +{ + return reinterpret_cast(j.jointOffset); +} + +PX_FORCE_INLINE const PxVec3& getParentOffset(const FsJointVectors& j) +{ + return reinterpret_cast(j.parentOffset); +} + + + + +PX_FORCE_INLINE const Cm::SpatialVector* getDSI(const FsRow& row) +{ + return PxUnionCast(row.DSI); //reinterpret_cast(row.DSI); +} + +} + +} + +#endif //DY_ARTICULATION_SCALAR_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h new file mode 100644 index 000000000..7ac2ee46c --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h @@ -0,0 +1,336 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_H +#define DY_ARTICULATION_H + +#include "PsVecMath.h" +#include "CmSpatialVector.h" +#include "DySpatial.h" +#include "PsBitUtils.h" +#include "DyArticulation.h" +#include "DyArticulationHelper.h" + +namespace physx +{ + +namespace Dy +{ + struct ArticulationCore; + struct ArticulationLink; + typedef size_t ArticulationLinkHandle; + class Articulation; + +#define DY_ARTICULATION_DEBUG_VERIFY 0 + +PX_FORCE_INLINE PxU32 ArticulationLowestSetBit(ArticulationBitField val) +{ + PxU32 low = PxU32(val&0xffffffff), high = PxU32(val>>32); + PxU32 mask = PxU32((!low)-1); + PxU32 result = (mask&Ps::lowestSetBitUnsafe(low)) | ((~mask)&(Ps::lowestSetBitUnsafe(high)+32)); + PX_ASSERT(val & (PxU64(1)<> 32); + PxU32 mask = PxU32((!high) - 1); + PxU32 result = ((~mask)&Ps::highestSetBitUnsafe(low)) | ((mask)&(Ps::highestSetBitUnsafe(high) + 32)); + PX_ASSERT(val & (PxU64(1) << result)); + return result; +} + +using namespace Ps::aos; + + + +PX_FORCE_INLINE Cm::SpatialVector& unsimdRef(Cm::SpatialVectorV& v) { return reinterpret_cast(v); } +PX_FORCE_INLINE const Cm::SpatialVector& unsimdRef(const Cm::SpatialVectorV& v) { return reinterpret_cast(v); } + + +PX_ALIGN_PREFIX(16) +struct FsJointVectors +{ + Vec3V parentOffset; // 16 bytes world-space offset from parent to child + Vec3V jointOffset; // 16 bytes world-space offset from child to joint +} +PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct FsRow +{ + Cm::SpatialVectorV DSI[3]; // 96 bytes + Mat33V D; // 48 bytes + ArticulationBitField children; // 8 bytes bitmap of children + ArticulationBitField pathToRoot; // 8 bytes bitmap of nodes to root, including self and root +} +PX_ALIGN_SUFFIX(16); + +PX_COMPILE_TIME_ASSERT(sizeof(FsRow)==160); + + + +PX_ALIGN_PREFIX(16) +struct FsInertia +{ + Mat33V ll, la, aa; + PX_FORCE_INLINE FsInertia(const Mat33V& _ll, const Mat33V& _la, const Mat33V& _aa): ll(_ll), la(_la), aa(_aa) {} + PX_FORCE_INLINE FsInertia(const SpInertia& I) + : ll(Mat33V_From_PxMat33(I.mLL)), la(Mat33V_From_PxMat33(I.mLA)), aa(Mat33V_From_PxMat33(I.mAA)) {} + PX_FORCE_INLINE FsInertia() {} + + PX_FORCE_INLINE void operator=(const FsInertia& other) + { + ll.col0 = other.ll.col0; ll.col1 = other.ll.col1; ll.col2 = other.ll.col2; + la.col0 = other.la.col0; la.col1 = other.la.col1; la.col2 = other.la.col2; + aa.col0 = other.aa.col0; aa.col1 = other.aa.col1; aa.col2 = other.aa.col2; + } + + PX_FORCE_INLINE FsInertia(const FsInertia& other) + { + ll.col0 = other.ll.col0; ll.col1 = other.ll.col1; ll.col2 = other.ll.col2; + la.col0 = other.la.col0; la.col1 = other.la.col1; la.col2 = other.la.col2; + aa.col0 = other.aa.col0; aa.col1 = other.aa.col1; aa.col2 = other.aa.col2; + } + +}PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct LtbRow +{ + FsInertia inertia; // body inertia in world space + Cm::SpatialVectorV j0[3], j1[3]; // jacobians + Mat33V jResponse; // inverse response matrix of joint + Vec3V jC; +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct FsRowAux +{ + Cm::SpatialVectorV S[3]; // motion subspace +}PX_ALIGN_SUFFIX(16); + + +struct FsData +{ + //Articulation* articulationX; //4 + +#if !PX_P64_FAMILY + PxU32 pad0; //8 +#endif + PxU16 linkCount; // number of links //10 + PxU16 jointVectorOffset; // offset of read-only data //12 +// PxU16 maxSolverNormalProgress; //14 +// PxU16 maxSolverFrictionProgress; //16 + + PxU64 dirty; //24 + PxU16 ltbDataOffset; // offset of save-velocity data //26 + PxU16 fsDataOffset; // offset of joint references //28 + //PxU32 solverProgress; //32 + + + Cm::SpatialVectorV deferredZ; //64 + PxU8 parent[DY_ARTICULATION_MAX_SIZE]; //128 +}; + +PX_COMPILE_TIME_ASSERT(0 == (sizeof(FsData) & 0x0f)); + +#define SOLVER_BODY_SOLVER_PROGRESS_OFFSET 28 +#define SOLVER_BODY_MAX_SOLVER_PROGRESS_OFFSET 12 + +namespace +{ + template PX_FORCE_INLINE T addAddr(void* addr, PxU32 increment) + { + return reinterpret_cast(reinterpret_cast(addr)+increment); + } + + template PX_FORCE_INLINE T addAddr(const void* addr, PxU32 increment) + { + return reinterpret_cast(reinterpret_cast(addr)+increment); + } +} + +PX_FORCE_INLINE Cm::SpatialVectorV* getVelocity(FsData& matrix) +{ + return addAddr(&matrix, sizeof(FsData)); +} + +PX_FORCE_INLINE const Cm::SpatialVectorV* getVelocity(const FsData& matrix) +{ + return addAddr(&matrix, sizeof(FsData)); +} + +PX_FORCE_INLINE Cm::SpatialVectorV* getDeferredVel(FsData& matrix) +{ + return addAddr(getVelocity(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE const Cm::SpatialVectorV* getDeferredVel(const FsData& matrix) +{ + return addAddr(getVelocity(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE const Cm::SpatialVectorV* getMotionVector(const FsData& matrix) +{ + return addAddr(getDeferredVel(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE Cm::SpatialVectorV* getMotionVector(FsData& matrix) +{ + return addAddr(getDeferredVel(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE Vec3V* getDeferredSZ(FsData& matrix) +{ + return addAddr(getMotionVector(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE const Vec3V* getDeferredSZ(const FsData& matrix) +{ + return addAddr(getMotionVector(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE const PxReal* getMaxPenBias(const FsData& matrix) +{ + return addAddr(getDeferredSZ(matrix), sizeof(Vec3V) * matrix.linkCount); +} + +PX_FORCE_INLINE PxReal* getMaxPenBias(FsData& matrix) +{ + return addAddr(getDeferredSZ(matrix), sizeof(Vec3V) * matrix.linkCount); +} + + +PX_FORCE_INLINE FsJointVectors* getJointVectors(FsData& matrix) +{ + return addAddr(&matrix,matrix.jointVectorOffset); +} + +PX_FORCE_INLINE const FsJointVectors* getJointVectors(const FsData& matrix) +{ + return addAddr(&matrix,matrix.jointVectorOffset); +} + +PX_FORCE_INLINE FsInertia& getRootInverseInertia(FsData& matrix) +{ + return *addAddr(&matrix,matrix.fsDataOffset); +} + +PX_FORCE_INLINE const FsInertia& getRootInverseInertia(const FsData& matrix) +{ + return *addAddr(&matrix,matrix.fsDataOffset); + +} + +PX_FORCE_INLINE FsRow* getFsRows(FsData& matrix) +{ + return addAddr(&getRootInverseInertia(matrix),sizeof(FsInertia)); +} + +PX_FORCE_INLINE const FsRow* getFsRows(const FsData& matrix) +{ + return addAddr(&getRootInverseInertia(matrix),sizeof(FsInertia)); +} + + +PX_FORCE_INLINE LtbRow* getLtbRows(FsData& matrix) +{ + return addAddr(&matrix,matrix.ltbDataOffset); +} + +PX_FORCE_INLINE const LtbRow* getLtbRows(const FsData& matrix) +{ + return addAddr(&matrix,matrix.ltbDataOffset); +} + + +PX_FORCE_INLINE Cm::SpatialVectorV* getRefVelocity(FsData& matrix) +{ + return addAddr(getLtbRows(matrix), sizeof(LtbRow)*matrix.linkCount); +} + +PX_FORCE_INLINE const Cm::SpatialVectorV* getRefVelocity(const FsData& matrix) +{ + return addAddr(getLtbRows(matrix), sizeof(LtbRow)*matrix.linkCount); +} + +PX_FORCE_INLINE FsRowAux* getAux(FsData& matrix) +{ + return addAddr(getRefVelocity(matrix),sizeof(Cm::SpatialVectorV)*matrix.linkCount); +} + +PX_FORCE_INLINE const FsRowAux* getAux(const FsData& matrix) +{ + return addAddr(getRefVelocity(matrix),sizeof(Cm::SpatialVectorV)*matrix.linkCount); +} + +//void PxcFsApplyImpulse(ArticulationV& articulation, +// PxU32 linkID, +// Vec3V linear, +// Vec3V angular); + +//Cm::SpatialVectorV PxcFsGetVelocity(ArticulationV& articulation, +// PxU32 linkID); + +Cm::SpatialVectorV PxcFsGetMotionVector(ArticulationV& articulation, + PxU32 linkID); + + +#if DY_ARTICULATION_DEBUG_VERIFY +namespace ArticulationRef +{ + Cm::SpatialVector propagateVelocity(const FsRow& row, + const FsJointVectors& jv, + const PxVec3& SZ, + const Cm::SpatialVector& v, + const FsRowAux& aux); + + Cm::SpatialVector propagateImpulse(const FsRow& row, + const FsJointVectors& jv, + PxVec3& SZ, + const Cm::SpatialVector& Z, + const FsRowAux& aux); + + void applyImpulse(const FsData& matrix, + Cm::SpatialVector* velocity, + PxU32 linkID, + const Cm::SpatialVector& impulse); + +} +#endif + +} +} + +#endif //DY_ARTICULATION_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h b/src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h new file mode 100644 index 000000000..e8cf685e4 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h @@ -0,0 +1,409 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_BODYCORE_INTEGRATOR_H +#define DY_BODYCORE_INTEGRATOR_H + +#include "CmPhysXCommon.h" +#include "PxvDynamics.h" +#include "PsMathUtils.h" +#include "PxsRigidBody.h" +#include "DySolverBody.h" +#include "DySleepingConfigulation.h" +#include "PxsIslandSim.h" + +namespace physx +{ + +namespace Dy +{ + +PX_FORCE_INLINE void bodyCoreComputeUnconstrainedVelocity +(const PxVec3& gravity, const PxReal dt, const PxReal linearDamping, const PxReal angularDamping, const PxReal accelScale, +const PxReal maxLinearVelocitySq, const PxReal maxAngularVelocitySq, PxVec3& inOutLinearVelocity, PxVec3& inOutAngularVelocity, +bool disableGravity) +{ + + //Multiply everything that needs multiplied by dt to improve code generation. + + PxVec3 linearVelocity = inOutLinearVelocity; + PxVec3 angularVelocity = inOutAngularVelocity; + + const PxReal linearDampingTimesDT=linearDamping*dt; + const PxReal angularDampingTimesDT=angularDamping*dt; + const PxReal oneMinusLinearDampingTimesDT=1.0f-linearDampingTimesDT; + const PxReal oneMinusAngularDampingTimesDT=1.0f-angularDampingTimesDT; + + //TODO context-global gravity + if (!disableGravity) + { + const PxVec3 linearAccelTimesDT = gravity*dt *accelScale; + linearVelocity += linearAccelTimesDT; + } + + //Apply damping. + const PxReal linVelMultiplier = physx::intrinsics::fsel(oneMinusLinearDampingTimesDT, oneMinusLinearDampingTimesDT, 0.0f); + const PxReal angVelMultiplier = physx::intrinsics::fsel(oneMinusAngularDampingTimesDT, oneMinusAngularDampingTimesDT, 0.0f); + linearVelocity*=linVelMultiplier; + angularVelocity*=angVelMultiplier; + + // Clamp velocity + const PxReal linVelSq = linearVelocity.magnitudeSquared(); + if(linVelSq > maxLinearVelocitySq) + { + linearVelocity *= PxSqrt(maxLinearVelocitySq / linVelSq); + } + const PxReal angVelSq = angularVelocity.magnitudeSquared(); + if(angVelSq > maxAngularVelocitySq) + { + angularVelocity *= PxSqrt(maxAngularVelocitySq / angVelSq); + } + + inOutLinearVelocity = linearVelocity; + inOutAngularVelocity = angularVelocity; +} + + +PX_FORCE_INLINE void integrateCore(PxVec3& motionLinearVelocity, PxVec3& motionAngularVelocity, PxSolverBody& solverBody, PxSolverBodyData& solverBodyData, const PxF32 dt) +{ + PxU32 lockFlags = solverBodyData.lockFlags; + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + { + motionLinearVelocity.x = 0.f; + solverBody.linearVelocity.x = 0.f; + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + { + motionLinearVelocity.y = 0.f; + solverBody.linearVelocity.y = 0.f; + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + { + motionLinearVelocity.z = 0.f; + solverBody.linearVelocity.z = 0.f; + } + + //The angular velocity should be 0 because it is now impossible to make it rotate around that axis! + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + { + motionAngularVelocity.x = 0.f; + solverBody.angularState.x = 0.f; + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + { + motionAngularVelocity.y = 0.f; + solverBody.angularState.y = 0.f; + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + { + motionAngularVelocity.z = 0.f; + solverBody.angularState.z = 0.f; + } + } + + // Integrate linear part + PxVec3 linearMotionVel = solverBodyData.linearVelocity + motionLinearVelocity; + PxVec3 delta = linearMotionVel * dt; + PxVec3 angularMotionVel = solverBodyData.angularVelocity + solverBodyData.sqrtInvInertia * motionAngularVelocity; + PxReal w = angularMotionVel.magnitudeSquared(); + solverBodyData.body2World.p += delta; + PX_ASSERT(solverBodyData.body2World.p.isFinite()); + + //Store back the linear and angular velocities + //core.linearVelocity += solverBody.linearVelocity * solverBodyData.sqrtInvMass; + solverBodyData.linearVelocity += solverBody.linearVelocity; + solverBodyData.angularVelocity += solverBodyData.sqrtInvInertia * solverBody.angularState; + + // Integrate the rotation using closed form quaternion integrator + if (w != 0.0f) + { + w = PxSqrt(w); + // Perform a post-solver clamping + // TODO(dsequeira): ignore this for the moment + //just clamp motionVel to half float-range + const PxReal maxW = 1e+7f; //Should be about sqrt(PX_MAX_REAL/2) or smaller + if (w > maxW) + { + angularMotionVel = angularMotionVel.getNormalized() * maxW; + w = maxW; + } + const PxReal v = dt * w * 0.5f; + PxReal s, q; + Ps::sincos(v, s, q); + s /= w; + + const PxVec3 pqr = angularMotionVel * s; + const PxQuat quatVel(pqr.x, pqr.y, pqr.z, 0); + PxQuat result = quatVel * solverBodyData.body2World.q; + + result += solverBodyData.body2World.q * q; + + solverBodyData.body2World.q = result.getNormalized(); + PX_ASSERT(solverBodyData.body2World.q.isSane()); + PX_ASSERT(solverBodyData.body2World.q.isFinite()); + } + + motionLinearVelocity = linearMotionVel; + motionAngularVelocity = angularMotionVel; +} + + +PX_FORCE_INLINE PxReal updateWakeCounter(PxsRigidBody* originalBody, PxReal dt, PxReal /*invDt*/, const bool enableStabilization, const bool useAdaptiveForce, Cm::SpatialVector& motionVelocity, + bool hasStaticTouch) +{ + //KS - at most one of these features can be enabled at any time + PX_ASSERT(!useAdaptiveForce || !enableStabilization); + PxsBodyCore& bodyCore = originalBody->getCore(); + + // update the body's sleep state and + PxReal wakeCounterResetTime = 20.0f*0.02f; + + PxReal wc = bodyCore.wakeCounter; + + { + if (enableStabilization) + { + bool freeze = false; + const PxTransform& body2World = bodyCore.body2World; + + // calculate normalized energy: kinetic energy divided by mass + + const PxVec3 t = bodyCore.inverseInertia; + const PxVec3 inertia(t.x > 0.f ? 1.0f / t.x : 1.f, t.y > 0.f ? 1.0f / t.y : 1.f, t.z > 0.f ? 1.0f / t.z : 1.f); + + + PxVec3 sleepLinVelAcc = motionVelocity.linear; + PxVec3 sleepAngVelAcc = body2World.q.rotateInv(motionVelocity.angular); + + // scale threshold by cluster factor (more contacts => higher sleep threshold) + //const PxReal clusterFactor = PxReal(1u + getNumUniqueInteractions()); + + PxReal invMass = bodyCore.inverseMass; + if (invMass == 0.f) + invMass = 1.f; + + const PxReal angular = sleepAngVelAcc.multiply(sleepAngVelAcc).dot(inertia) * invMass; + const PxReal linear = sleepLinVelAcc.magnitudeSquared(); + PxReal frameNormalizedEnergy = 0.5f * (angular + linear); + + const PxReal cf = hasStaticTouch ? PxReal(PxMin(10u, bodyCore.numBodyInteractions)) : 0.f; + const PxReal freezeThresh = cf*bodyCore.freezeThreshold; + + originalBody->freezeCount = PxMax(originalBody->freezeCount - dt, 0.0f); + bool settled = true; + + PxReal accelScale = PxMin(1.f, originalBody->accelScale + dt); + + if (frameNormalizedEnergy >= freezeThresh) + { + settled = false; + originalBody->freezeCount = PXD_FREEZE_INTERVAL; + } + + if (!hasStaticTouch) + { + accelScale = 1.f; + settled = false; + } + + + if (settled) + { + //Dampen bodies that are just about to go to sleep + if (cf > 1.f) + { + const PxReal sleepDamping = PXD_SLEEP_DAMPING; + const PxReal sleepDampingTimesDT = sleepDamping*dt; + const PxReal d = 1.0f - sleepDampingTimesDT; + bodyCore.linearVelocity = bodyCore.linearVelocity * d; + bodyCore.angularVelocity = bodyCore.angularVelocity * d; + accelScale = accelScale * 0.75f + 0.25f*PXD_FREEZE_SCALE; + } + freeze = originalBody->freezeCount == 0.f && frameNormalizedEnergy < (bodyCore.freezeThreshold * PXD_FREEZE_TOLERANCE); + } + + originalBody->accelScale = accelScale; + + if (freeze) + { + //current flag isn't frozen but freeze flag raise so we need to raise the frozen flag in this frame + bool wasNotFrozen = (originalBody->mInternalFlags & PxsRigidBody::eFROZEN) == 0; + PxU16 flags = PxU16((originalBody->mInternalFlags & PxsRigidBody::eDISABLE_GRAVITY) | PxsRigidBody::eFROZEN); + if (wasNotFrozen) + { + flags |= PxsRigidBody::eFREEZE_THIS_FRAME; + } + originalBody->mInternalFlags = flags; + bodyCore.body2World = originalBody->getLastCCDTransform(); + } + else + { + PxU16 flags = PxU16(originalBody->mInternalFlags & PxsRigidBody::eDISABLE_GRAVITY); + bool wasFrozen = (originalBody->mInternalFlags & PxsRigidBody::eFROZEN) != 0; + if (wasFrozen) + { + flags |= PxsRigidBody::eUNFREEZE_THIS_FRAME; + } + originalBody->mInternalFlags = flags; + } + + /*KS: New algorithm for sleeping when using stabilization: + * Energy *this frame* must be higher than sleep threshold and accumulated energy over previous frames + * must be higher than clusterFactor*energyThreshold. + */ + if (wc < wakeCounterResetTime * 0.5f || wc < dt) + { + //Accumulate energy + originalBody->sleepLinVelAcc += sleepLinVelAcc; + originalBody->sleepAngVelAcc += sleepAngVelAcc; + + //If energy this frame is high + if (frameNormalizedEnergy >= bodyCore.sleepThreshold) + { + //Compute energy over sleep preparation time + const PxReal sleepAngular = originalBody->sleepAngVelAcc.multiply(originalBody->sleepAngVelAcc).dot(inertia) * invMass; + const PxReal sleepLinear = originalBody->sleepLinVelAcc.magnitudeSquared(); + PxReal normalizedEnergy = 0.5f * (sleepAngular + sleepLinear); + const PxReal sleepClusterFactor = PxReal(1u + bodyCore.numCountedInteractions); + // scale threshold by cluster factor (more contacts => higher sleep threshold) + const PxReal threshold = sleepClusterFactor*bodyCore.sleepThreshold; + + //If energy over sleep preparation time is high + if (normalizedEnergy >= threshold) + { + //Wake up + //PX_ASSERT(isActive()); + originalBody->sleepAngVelAcc = PxVec3(0); + originalBody->sleepLinVelAcc = PxVec3(0); + + const float factor = bodyCore.sleepThreshold == 0.f ? 2.0f : PxMin(normalizedEnergy / threshold, 2.0f); + PxReal oldWc = wc; + wc = factor * 0.5f * wakeCounterResetTime + dt * (sleepClusterFactor - 1.0f); + bodyCore.solverWakeCounter = wc; + //if (oldWc == 0.0f) // for the case where a sleeping body got activated by the system (not the user) AND got processed by the solver as well + // notifyNotReadyForSleeping(bodyCore.nodeIndex); + + if (oldWc == 0.0f) + originalBody->mInternalFlags |= PxsRigidBody::eACTIVATE_THIS_FRAME; + + return wc; + } + } + } + + } + else + { + if (useAdaptiveForce) + { + if (hasStaticTouch && bodyCore.numBodyInteractions > 1) + originalBody->accelScale = 1.f / PxReal(bodyCore.numBodyInteractions); + else + originalBody->accelScale = 1.f; + } + if (wc < wakeCounterResetTime * 0.5f || wc < dt) + { + const PxTransform& body2World = bodyCore.body2World; + + // calculate normalized energy: kinetic energy divided by mass + const PxVec3 t = bodyCore.inverseInertia; + const PxVec3 inertia(t.x > 0.f ? 1.0f / t.x : 1.f, t.y > 0.f ? 1.0f / t.y : 1.f, t.z > 0.f ? 1.0f / t.z : 1.f); + + PxVec3 sleepLinVelAcc = motionVelocity.linear; + PxVec3 sleepAngVelAcc = body2World.q.rotateInv(motionVelocity.angular); + + originalBody->sleepLinVelAcc += sleepLinVelAcc; + originalBody->sleepAngVelAcc += sleepAngVelAcc; + + PxReal invMass = bodyCore.inverseMass; + if (invMass == 0.f) + invMass = 1.f; + + const PxReal angular = originalBody->sleepAngVelAcc.multiply(originalBody->sleepAngVelAcc).dot(inertia) * invMass; + const PxReal linear = originalBody->sleepLinVelAcc.magnitudeSquared(); + PxReal normalizedEnergy = 0.5f * (angular + linear); + + // scale threshold by cluster factor (more contacts => higher sleep threshold) + const PxReal clusterFactor = PxReal(1 + bodyCore.numCountedInteractions); + const PxReal threshold = clusterFactor*bodyCore.sleepThreshold; + + if (normalizedEnergy >= threshold) + { + //PX_ASSERT(isActive()); + originalBody->sleepLinVelAcc = PxVec3(0); + originalBody->sleepAngVelAcc = PxVec3(0); + const float factor = threshold == 0.f ? 2.0f : PxMin(normalizedEnergy / threshold, 2.0f); + PxReal oldWc = wc; + wc = factor * 0.5f * wakeCounterResetTime + dt * (clusterFactor - 1.0f); + bodyCore.solverWakeCounter = wc; + PxU16 flags = PxU16(originalBody->mInternalFlags & PxsRigidBody::eDISABLE_GRAVITY); + if (oldWc == 0.0f) // for the case where a sleeping body got activated by the system (not the user) AND got processed by the solver as well + { + flags |= PxsRigidBody::eACTIVATE_THIS_FRAME; + //notifyNotReadyForSleeping(bodyCore.nodeIndex); + } + + originalBody->mInternalFlags = flags; + + return wc; + } + } + } + } + + wc = PxMax(wc - dt, 0.0f); + bodyCore.solverWakeCounter = wc; + return wc; +} + +PX_FORCE_INLINE void sleepCheck(PxsRigidBody* originalBody, const PxReal dt, const PxReal intDt, const bool enableStabilization, bool useAdaptiveForce, Cm::SpatialVector& motionVelocity, + bool hasStaticTouch) +{ + + PxReal wc = updateWakeCounter(originalBody, dt, intDt, enableStabilization, useAdaptiveForce, motionVelocity, hasStaticTouch); + bool wakeCounterZero = (wc == 0.0f); + + if (wakeCounterZero) + { + //PxsBodyCore& bodyCore = originalBody->getCore(); + originalBody->mInternalFlags |= PxsRigidBody::eDEACTIVATE_THIS_FRAME; + // notifyReadyForSleeping(bodyCore.nodeIndex); + originalBody->sleepLinVelAcc = PxVec3(0); + originalBody->sleepAngVelAcc = PxVec3(0); + } +} + +} + +} + +#endif //DY_BODYCORE_INTEGRATOR_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp new file mode 100644 index 000000000..5d95b8e69 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp @@ -0,0 +1,917 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "DyConstraintPartition.h" +#include "DyArticulationUtils.h" + +#define INTERLEAVE_SELF_CONSTRAINTS 1 + + +namespace physx +{ +namespace Dy +{ + +namespace +{ + +PX_FORCE_INLINE PxU32 getArticulationIndex(const uintptr_t eaArticulation, const uintptr_t* eaArticulations, const PxU32 numEas) +{ + PxU32 index=0xffffffff; + for(PxU32 i=0;isolverProgress; + bodyBProgress = desc.bodyB->solverProgress; + return activeA && activeB; + } + + PX_FORCE_INLINE void getProgress(const PxSolverConstraintDesc& desc, + PxU32& bodyAProgress, PxU32& bodyBProgress) + { + bodyAProgress = desc.bodyA->solverProgress; + bodyBProgress = desc.bodyB->solverProgress; + } + + PX_FORCE_INLINE void storeProgress(const PxSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress, + const PxU16 availablePartition) + { + desc.bodyA->solverProgress = bodyAProgress; + desc.bodyA->maxSolverNormalProgress = PxMax(desc.bodyA->maxSolverNormalProgress, availablePartition); + desc.bodyB->solverProgress = bodyBProgress; + desc.bodyB->maxSolverNormalProgress = PxMax(desc.bodyB->maxSolverNormalProgress, availablePartition); + } + + PX_FORCE_INLINE void storeProgress(const PxSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress) + { + desc.bodyA->solverProgress = bodyAProgress; + desc.bodyB->solverProgress = bodyBProgress; + } + + PX_FORCE_INLINE PxU32 getStaticContactWriteIndex(const PxSolverConstraintDesc& desc, + bool activeA, bool activeB) + { + if (activeA) + return PxU32(desc.bodyA->maxSolverNormalProgress + desc.bodyA->maxSolverFrictionProgress++); + else if (activeB) + return PxU32(desc.bodyB->maxSolverNormalProgress + desc.bodyB->maxSolverFrictionProgress++); + + return 0xffffffff; + + } + + PX_FORCE_INLINE void recordStaticConstraint(const PxSolverConstraintDesc& desc, bool& activeA, bool& activeB) const + { + if (activeA) + { + desc.bodyA->maxSolverFrictionProgress++; + } + + if (activeB) + { + desc.bodyB->maxSolverFrictionProgress++; + } + } + + PX_FORCE_INLINE void clearState() + { + for(PxU32 a = 0; a < mNumBodies; ++a) + mBodies[a].solverProgress = 0; + } + + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(Ps::Array& numConstraintsPerPartition) + { + for(PxU32 a = 0; a < mNumBodies; ++a) + { + mBodies[a].solverProgress = 0; + + PxU32 requiredSize = PxU32(mBodies[a].maxSolverNormalProgress + mBodies[a].maxSolverFrictionProgress); + if(requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for(PxU32 b = 0; b < mBodies[a].maxSolverFrictionProgress; ++b) + { + numConstraintsPerPartition[mBodies[a].maxSolverNormalProgress + b]++; + } + } + } +}; + +class ExtendedRigidBodyClassification +{ + + PxSolverBody* PX_RESTRICT mBodies; + PxU32 mNumBodies; + //uintptr_t* PX_RESTRICT mFsDatas; + uintptr_t* PX_RESTRICT mArticulations; + PxU32 mNumArticulations; + +public: + + ExtendedRigidBodyClassification(PxSolverBody* PX_RESTRICT bodies, PxU32 numBodies, uintptr_t* articulations, PxU32 numArticulations) + : mBodies(bodies), mNumBodies(numBodies), mArticulations(articulations), mNumArticulations(numArticulations) + { + } + + //Returns true if it is a dynamic-dynamic constriant; false if it is a dynamic-static or dynamic-kinematic constraint + PX_FORCE_INLINE bool classifyConstraint(const PxSolverConstraintDesc& desc, uintptr_t& indexA, uintptr_t& indexB, + bool& activeA, bool& activeB, PxU32& bodyAProgress, PxU32& bodyBProgress) const + { + bool hasStatic = false; + if(PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + indexA=uintptr_t(desc.bodyA - mBodies); + activeA = indexA < mNumBodies; + hasStatic = desc.bodyADataIndex == 0; + bodyAProgress = activeA ? desc.bodyA->solverProgress: 0; + } + else + { + + ArticulationV* articulationA = desc.articulationA; + indexA=mNumBodies+getArticulationIndex(uintptr_t(articulationA), mArticulations ,mNumArticulations); + //bodyAProgress = articulationA->getFsDataPtr()->solverProgress; + bodyAProgress = articulationA->solverProgress; + activeA = true; + } + if(PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + indexB=uintptr_t(desc.bodyB - mBodies); + activeB = indexB < mNumBodies; + hasStatic = hasStatic || desc.bodyBDataIndex == 0; + bodyBProgress = activeB ? desc.bodyB->solverProgress : 0; + } + else + { + Articulation* articulationB = static_cast(desc.articulationB); + indexB=mNumBodies+getArticulationIndex(uintptr_t(articulationB), mArticulations, mNumArticulations); + activeB = true; + bodyBProgress = articulationB->solverProgress; + } + return !hasStatic; + } + + PX_FORCE_INLINE void getProgress(const PxSolverConstraintDesc& desc, + PxU32& bodyAProgress, PxU32& bodyBProgress) const + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + bodyAProgress = desc.bodyA->solverProgress; + } + else + { + ArticulationV* articulationA = desc.articulationA; + bodyAProgress = articulationA->solverProgress; + } + + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + + bodyBProgress = desc.bodyB->solverProgress; + } + else + { + ArticulationV* articulationB = desc.articulationB; + bodyBProgress = articulationB->solverProgress; + } + } + + + PX_FORCE_INLINE void storeProgress(const PxSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress, + const PxU16 availablePartition) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + desc.bodyA->solverProgress = bodyAProgress; + desc.bodyA->maxSolverNormalProgress = PxMax(desc.bodyA->maxSolverNormalProgress, availablePartition); + } + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->solverProgress = bodyAProgress; + articulationA->maxSolverNormalProgress = PxMax(articulationA->maxSolverNormalProgress, availablePartition); + } + + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + desc.bodyB->solverProgress = bodyBProgress; + desc.bodyB->maxSolverNormalProgress = PxMax(desc.bodyB->maxSolverNormalProgress, availablePartition); + } + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->solverProgress = bodyBProgress; + articulationB->maxSolverNormalProgress = PxMax(articulationB->maxSolverNormalProgress, availablePartition); + } + } + + PX_FORCE_INLINE void storeProgress(const PxSolverConstraintDesc& desc, const PxU32 bodyAProgress, + const PxU32 bodyBProgress) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + desc.bodyA->solverProgress = bodyAProgress; + } + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->solverProgress = bodyAProgress; + } + + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + desc.bodyB->solverProgress = bodyBProgress; + } + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->solverProgress = bodyBProgress; + } + } + + PX_FORCE_INLINE void recordStaticConstraint(const PxSolverConstraintDesc& desc, bool& activeA, bool& activeB) + { + if (activeA) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + desc.bodyA->maxSolverFrictionProgress++; + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->maxSolverFrictionProgress++; + } + } + + if (activeB) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + desc.bodyB->maxSolverFrictionProgress++; + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->maxSolverFrictionProgress++; + } + } + } + + PX_FORCE_INLINE PxU32 getStaticContactWriteIndex(const PxSolverConstraintDesc& desc, + bool activeA, bool activeB) + { + if (activeA) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + return PxU32(desc.bodyA->maxSolverNormalProgress + desc.bodyA->maxSolverFrictionProgress++); + } + else + { + ArticulationV* articulationA = desc.articulationA; + return PxU32(articulationA->maxSolverNormalProgress + articulationA->maxSolverFrictionProgress++); + } + } + else if (activeB) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + return PxU32(desc.bodyB->maxSolverNormalProgress + desc.bodyB->maxSolverFrictionProgress++); + } + else + { + ArticulationV* articulationB = desc.articulationB; + return PxU32(articulationB->maxSolverNormalProgress + articulationB->maxSolverFrictionProgress++); + } + } + + return 0xffffffff; + } + + PX_FORCE_INLINE void clearState() + { + for(PxU32 a = 0; a < mNumBodies; ++a) + mBodies[a].solverProgress = 0; + + for(PxU32 a = 0; a < mNumArticulations; ++a) + (reinterpret_cast(mArticulations[a]))->solverProgress = 0; + } + + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(Ps::Array& numConstraintsPerPartition) + { + for(PxU32 a = 0; a < mNumBodies; ++a) + { + mBodies[a].solverProgress = 0; + + PxU32 requiredSize = PxU32(mBodies[a].maxSolverNormalProgress + mBodies[a].maxSolverFrictionProgress); + if(requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for(PxU32 b = 0; b < mBodies[a].maxSolverFrictionProgress; ++b) + { + numConstraintsPerPartition[mBodies[a].maxSolverNormalProgress + b]++; + } + } + + for(PxU32 a = 0; a < mNumArticulations; ++a) + { + ArticulationV* articulation = reinterpret_cast(mArticulations[a]); + articulation->solverProgress = 0; + + PxU32 requiredSize = PxU32(articulation->maxSolverNormalProgress + articulation->maxSolverFrictionProgress); + if(requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for(PxU32 b = 0; b < articulation->maxSolverFrictionProgress; ++b) + { + numConstraintsPerPartition[articulation->maxSolverNormalProgress + b]++; + } + } + } + +}; + +template +void classifyConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + Ps::Array& numConstraintsPerPartition, PxSolverConstraintDesc* PX_RESTRICT eaTempConstraintDescriptors) +{ + const PxSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxU32 numUnpartitionedConstraints = 0; + + numConstraintsPerPartition.forceSize_Unsafe(32); + + PxMemZero(numConstraintsPerPartition.begin(), sizeof(PxU32) * 32); + + for(PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + + PxU32 partitionsA, partitionsB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB, + partitionsA, partitionsB); + + if(notContainsStatic) + { + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if(availablePartition == MAX_NUM_PARTITIONS) + { + eaTempConstraintDescriptors[numUnpartitionedConstraints++] = *_desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + if (activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + + classification.storeProgress(*_desc, partitionsA, partitionsB, PxU16(availablePartition)); + } + else + { + classification.recordStaticConstraint(*_desc, activeA, activeB); + } + } + + PxU32 partitionStartIndex = 0; + + while(numUnpartitionedConstraints > 0) + { + classification.clearState(); + + partitionStartIndex += 32; + //Keep partitioning the un-partitioned constraints and blat the whole thing to 0! + numConstraintsPerPartition.resize(32 + numConstraintsPerPartition.size()); + PxMemZero(numConstraintsPerPartition.begin() + partitionStartIndex, sizeof(PxU32) * 32); + + PxU32 newNumUnpartitionedConstraints = 0; + PxU32 partitionsA, partitionsB; + bool activeA, activeB; + uintptr_t indexA, indexB; + for(PxU32 i = 0; i < numUnpartitionedConstraints; ++i) + { + const PxSolverConstraintDesc& desc = eaTempConstraintDescriptors[i]; + + classification.classifyConstraint(desc, indexA, indexB, activeA, activeB, + partitionsA, partitionsB); + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if(availablePartition == MAX_NUM_PARTITIONS) + { + //Need to shuffle around unpartitioned constraints... + eaTempConstraintDescriptors[newNumUnpartitionedConstraints++] = desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + + /*desc.bodyA->solverProgress = partitionsA; + desc.bodyB->solverProgress = partitionsB;*/ + availablePartition += partitionStartIndex; + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + + classification.storeProgress(desc, partitionsA, partitionsB, PxU16(availablePartition) ); + + /* desc.bodyA->maxSolverNormalProgress = PxMax(desc.bodyA->maxSolverNormalProgress, PxU16(availablePartition)); + desc.bodyB->maxSolverNormalProgress = PxMax(desc.bodyB->maxSolverNormalProgress, PxU16(availablePartition));*/ + } + + numUnpartitionedConstraints = newNumUnpartitionedConstraints; + } + + classification.reserveSpaceForStaticConstraints(numConstraintsPerPartition); + +} + +template +void writeConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + Ps::Array& accumulatedConstraintsPerPartition, PxSolverConstraintDesc* eaTempConstraintDescriptors, + PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDesc) +{ + PX_UNUSED(eaTempConstraintDescriptors); + const PxSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxU32 numUnpartitionedConstraints = 0; + + for(PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB, partitionsA, partitionsB); + + if(notContainsStatic) + { + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if(availablePartition == MAX_NUM_PARTITIONS) + { + eaTempConstraintDescriptors[numUnpartitionedConstraints++] = *_desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + classification.storeProgress(*_desc, partitionsA, partitionsB, PxU16(availablePartition + 1)); + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + } + else + { + //Just count the number of static constraints and store in maxSolverFrictionProgress... + PxU32 index = classification.getStaticContactWriteIndex(*_desc, activeA, activeB); + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[index]++] = *_desc; + } + } + + PxU32 partitionStartIndex = 0; + + while(numUnpartitionedConstraints > 0) + { + classification.clearState(); + + partitionStartIndex += 32; + PxU32 newNumUnpartitionedConstraints = 0; + + PxU32 partitionsA, partitionsB; + bool activeA, activeB; + uintptr_t indexA, indexB; + for(PxU32 i = 0; i < numUnpartitionedConstraints; ++i) + { + const PxSolverConstraintDesc& desc = eaTempConstraintDescriptors[i]; + + /* PxU32 partitionsA=desc.bodyA->solverProgress; + PxU32 partitionsB=desc.bodyB->solverProgress;*/ + + classification.classifyConstraint(desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if(availablePartition == MAX_NUM_PARTITIONS) + { + //Need to shuffle around unpartitioned constraints... + eaTempConstraintDescriptors[newNumUnpartitionedConstraints++] = desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + /*desc.bodyA->solverProgress = partitionsA; + desc.bodyB->solverProgress = partitionsB; +*/ + classification.storeProgress(desc, partitionsA, partitionsB); + availablePartition += partitionStartIndex; + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = desc; + } + + numUnpartitionedConstraints = newNumUnpartitionedConstraints; + } +} + +} + +#define PX_NORMALIZE_PARTITIONS 1 + +#if PX_NORMALIZE_PARTITIONS + +template +PxU32 normalizePartitions(Ps::Array& accumulatedConstraintsPerPartition, PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDescriptors, + const PxU32 numConstraintDescriptors, Ps::Array& bitField, const Classification& classification, const PxU32 numBodies, const PxU32 numArticulations) +{ + PxU32 numPartitions = 0; + + PxU32 prevAccumulation = 0; + for(; numPartitions < accumulatedConstraintsPerPartition.size() && accumulatedConstraintsPerPartition[numPartitions] > prevAccumulation; + prevAccumulation = accumulatedConstraintsPerPartition[numPartitions++]); + + PxU32 targetSize = (numPartitions == 0 ? 0 : (numConstraintDescriptors)/numPartitions); + + bitField.reserve((numBodies + numArticulations + 31)/32); + bitField.forceSize_Unsafe((numBodies + numArticulations + 31)/32); + + for(PxU32 i = numPartitions; i > 0; i--) + { + PxU32 partitionIndex = i-1; + + //Build the partition mask... + + PxU32 startIndex = partitionIndex == 0 ? 0 : accumulatedConstraintsPerPartition[partitionIndex-1]; + PxU32 endIndex = accumulatedConstraintsPerPartition[partitionIndex]; + + //If its greater than target size, there's nothing that will be pulled into it from earlier partitions + if((endIndex - startIndex) >= targetSize) + continue; + + + PxMemZero(bitField.begin(), sizeof(PxU32)*bitField.size()); + + for(PxU32 a = startIndex; a < endIndex; ++a) + { + PxSolverConstraintDesc& desc = eaOrderedConstraintDescriptors[a]; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + + classification.classifyConstraint(desc, indexA, indexB, activeA, activeB, partitionsA, partitionsB); + + if(activeA) + bitField[PxU32(indexA)/32] |= getBit(indexA & 31); + if(activeB) + bitField[PxU32(indexB)/32] |= getBit(indexB & 31); + } + + bool bTerm = false; + for(PxU32 a = partitionIndex; a > 0 && !bTerm; --a) + { + PxU32 pInd = a-1; + + PxU32 si = pInd == 0 ? 0 : accumulatedConstraintsPerPartition[pInd-1]; + PxU32 ei = accumulatedConstraintsPerPartition[pInd]; + + for(PxU32 b = ei; b > si && !bTerm; --b) + { + PxU32 ind = b-1; + PxSolverConstraintDesc& desc = eaOrderedConstraintDescriptors[ind]; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + + classification.classifyConstraint(desc, indexA, indexB, activeA, activeB, partitionsA, partitionsB); + + bool canAdd = true; + + if(activeA && (bitField[PxU32(indexA)/32] & (getBit(indexA & 31)))) + canAdd = false; + if(activeB && (bitField[PxU32(indexB)/32] & (getBit(indexB & 31)))) + canAdd = false; + + if(canAdd) + { + PxSolverConstraintDesc tmp = eaOrderedConstraintDescriptors[ind]; + + if(activeA) + bitField[PxU32(indexA)/32] |= (getBit(indexA & 31)); + if(activeB) + bitField[PxU32(indexB)/32] |= (getBit(indexB & 31)); + + PxU32 index = ind; + for(PxU32 c = pInd; c < partitionIndex; ++c) + { + PxU32 newIndex = --accumulatedConstraintsPerPartition[c]; + if(index != newIndex) + eaOrderedConstraintDescriptors[index] = eaOrderedConstraintDescriptors[newIndex]; + index = newIndex; + } + + if(index != ind) + eaOrderedConstraintDescriptors[index] = tmp; + + if((accumulatedConstraintsPerPartition[partitionIndex] - accumulatedConstraintsPerPartition[partitionIndex-1]) >= targetSize) + { + bTerm = true; + break; + } + } + } + } + } + + PxU32 partitionCount = 0; + PxU32 lastPartitionCount = 0; + for (PxU32 a = 0; a < numPartitions; ++a) + { + const PxU32 constraintCount = accumulatedConstraintsPerPartition[a]; + accumulatedConstraintsPerPartition[partitionCount] = constraintCount; + if (constraintCount != lastPartitionCount) + { + lastPartitionCount = constraintCount; + partitionCount++; + } + } + + accumulatedConstraintsPerPartition.forceSize_Unsafe(partitionCount); + + return partitionCount; +} + +#endif + +PxU32 partitionContactConstraints(ConstraintPartitionArgs& args) +{ + PxU32 maxPartition = 0; + //Unpack the input data. + const PxU32 numBodies=args.mNumBodies; + PxSolverBody* PX_RESTRICT eaAtoms=args.mBodies; + const PxU32 numArticulations=args.mNumArticulationPtrs; + + const PxU32 numConstraintDescriptors=args.mNumContactConstraintDescriptors; + + PxSolverConstraintDesc* PX_RESTRICT eaConstraintDescriptors=args.mContactConstraintDescriptors; + PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDescriptors=args.mOrderedContactConstraintDescriptors; + PxSolverConstraintDesc* PX_RESTRICT eaTempConstraintDescriptors=args.mTempContactConstraintDescriptors; + + Ps::Array& constraintsPerPartition = *args.mConstraintsPerPartition; + constraintsPerPartition.forceSize_Unsafe(0); + + for(PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.solverProgress = 0; + //We re-use maxSolverFrictionProgress and maxSolverNormalProgress to record the + //maximum partition used by dynamic constraints and the number of static constraints affecting + //a body. We use this to make partitioning much cheaper and be able to support + body.maxSolverFrictionProgress = 0; + body.maxSolverNormalProgress = 0; + } + + PxU32 numOrderedConstraints=0; + + PxU32 numSelfConstraintBlocks=0; + + if(numArticulations == 0) + { + RigidBodyClassification classification(eaAtoms, numBodies); + classifyConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors); + + PxU32 accumulation = 0; + for(PxU32 a = 0; a < constraintsPerPartition.size(); ++a) + { + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; + } + + for(PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.solverProgress = 0; + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... + body.maxSolverFrictionProgress = 0; + } + + writeConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors, eaOrderedConstraintDescriptors); + + numOrderedConstraints = numConstraintDescriptors; + + if(!args.enhancedDeterminism) + maxPartition = normalizePartitions(constraintsPerPartition, eaOrderedConstraintDescriptors, numConstraintDescriptors, *args.mBitField, + classification, numBodies, 0); + + } + else + { + + const ArticulationSolverDesc* articulationDescs=args.mArticulationPtrs; + PX_ALLOCA(_eaArticulations, uintptr_t, numArticulations); + uintptr_t* eaArticulations = _eaArticulations; + for(PxU32 i=0;isolverProgress = 0; + articulation->maxSolverFrictionProgress = 0; + articulation->maxSolverNormalProgress = 0; + } + ExtendedRigidBodyClassification classification(eaAtoms, numBodies, eaArticulations, numArticulations); + + classifyConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, + constraintsPerPartition, eaTempConstraintDescriptors); + + PxU32 accumulation = 0; + for(PxU32 a = 0; a < constraintsPerPartition.size(); ++a) + { + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; + } + + for(PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.solverProgress = 0; + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... + body.maxSolverFrictionProgress = 0; + } + + for(PxU32 a = 0; a < numArticulations; ++a) + { + ArticulationV* articulation = reinterpret_cast(eaArticulations[a]); + articulation->solverProgress = 0; + articulation->maxSolverFrictionProgress = 0; + } + + writeConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors, eaOrderedConstraintDescriptors); + + numOrderedConstraints = numConstraintDescriptors; + + if (!args.enhancedDeterminism) + maxPartition = normalizePartitions(constraintsPerPartition, eaOrderedConstraintDescriptors, + numConstraintDescriptors, *args.mBitField, classification, numBodies, numArticulations); + + } + + + + const PxU32 numConstraintsDifferentBodies=numOrderedConstraints; + + PX_ASSERT(numConstraintsDifferentBodies == numConstraintDescriptors); + + //Now handle the articulated self-constraints. + PxU32 totalConstraintCount = numConstraintsDifferentBodies; + + args.mNumSelfConstraintBlocks=numSelfConstraintBlocks; + + args.mNumDifferentBodyConstraints=numConstraintsDifferentBodies; + args.mNumSelfConstraints=totalConstraintCount-numConstraintsDifferentBodies; + + if (args.enhancedDeterminism) + { + PxU32 prevPartitionSize = 0; + maxPartition = 0; + for (PxU32 a = 0; a < constraintsPerPartition.size(); ++a, maxPartition++) + { + if (constraintsPerPartition[a] == prevPartitionSize) + break; + prevPartitionSize = constraintsPerPartition[a]; + } + } + + return maxPartition; +} + +} + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h new file mode 100644 index 000000000..a66cd6993 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_CONSTRAINTPARTITION_H +#define DY_CONSTRAINTPARTITION_H + +#include "DyDynamics.h" + + + +namespace physx +{ + +namespace Dy +{ +struct ConstraintPartitionArgs +{ + enum + { + eMAX_NUM_BODIES = 8192 + }; + + //Input + PxSolverBody* mBodies; + PxU32 mNumBodies; + ArticulationSolverDesc* mArticulationPtrs; + PxU32 mNumArticulationPtrs; + PxSolverConstraintDesc* mContactConstraintDescriptors; + PxU32 mNumContactConstraintDescriptors; + //output + PxSolverConstraintDesc* mOrderedContactConstraintDescriptors; + PxSolverConstraintDesc* mTempContactConstraintDescriptors; + PxU32 mNumSelfConstraintBlocks; + PxU32 mNumDifferentBodyConstraints; + PxU32 mNumSelfConstraints; + Ps::Array* mConstraintsPerPartition; + //Ps::Array* mStartIndices; + Ps::Array* mBitField; + + bool enhancedDeterminism; +}; + +PxU32 partitionContactConstraints(ConstraintPartitionArgs& args); + +} // namespace physx + +} + + + +#endif // DY_CONSTRAINTPARTITION_H + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h new file mode 100644 index 000000000..27d6190bd --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h @@ -0,0 +1,101 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_CONSTRAINTSHADER_H +#define DY_CONSTRAINTSHADER_H + +#include "DyConstraint.h" + +#include "DySolverConstraintDesc.h" +#include "PsArray.h" + +#define DY_ARTICULATION_MIN_RESPONSE 1e-5f + +#define DY_ARTICULATION_BAD_RESPONSE 0.02f + +namespace physx +{ + +class PxcConstraintBlockStream; +class PxsConstraintBlockManager; +struct PxSolverBody; +struct PxSolverBodyData; +struct PxSolverConstraintDesc; + +namespace Cm +{ + struct SpatialVectorF; +} + +namespace Dy +{ + + static const PxU32 MAX_CONSTRAINT_ROWS = 12; + +struct SolverConstraintShaderPrepDesc +{ + const Constraint* constraint; + PxConstraintSolverPrep solverPrep; + const void* constantBlock; + PxU32 constantBlockByteSize; +}; + +SolverConstraintPrepState::Enum setupSolverConstraint4 + (SolverConstraintShaderPrepDesc* PX_RESTRICT constraintShaderDescs, + PxSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal recipdt, PxU32& totalRows, + PxConstraintAllocator& allocator); + +SolverConstraintPrepState::Enum setupSolverConstraint4 + (PxSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal recipdt, PxU32& totalRows, + PxConstraintAllocator& allocator, PxU32 maxRows); + +PxU32 SetupSolverConstraint(SolverConstraintShaderPrepDesc& shaderDesc, + PxSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + PxReal dt, PxReal invdt, Cm::SpatialVectorF* Z); + + +class ConstraintHelper +{ +public: + + static PxU32 setupSolverConstraint( + PxSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + PxReal dt, PxReal invdt, Cm::SpatialVectorF* Z); +}; + +} + +} + +#endif //DY_CONSTRAINTSHADER_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp new file mode 100644 index 000000000..911f76e58 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp @@ -0,0 +1,605 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxMemory.h" +#include "DyConstraintPrep.h" +#include "PxsRigidBody.h" +#include "DySolverConstraint1D.h" +#include "PsSort.h" +#include "DySolverConstraintDesc.h" +#include "PxcConstraintBlockStream.h" +#include "DyArticulationContactPrep.h" +#include "PsFoundation.h" + +namespace physx +{ +namespace Dy +{ + // dsequeira: + // + // we can choose any linear combination of equality constraints and get the same solution + // Hence we can orthogonalize the constraints using the inner product given by the + // inverse mass matrix, so that when we use PGS, solving a constraint row for a joint + // don't disturb the solution of prior rows. + // + // We also eliminate the equality constraints from the hard inequality constraints - + // (essentially projecting the direction corresponding to the lagrange multiplier + // onto the equality constraint subspace) but 'til I've verified this generates + // exactly the same KKT/complementarity conditions, status is 'experimental'. + // + // since for equality constraints the resulting rows have the property that applying + // an impulse along one row doesn't alter the projected velocity along another row, + // all equality constraints (plus one inequality constraint) can be processed in parallel + // using SIMD + // + // Eliminating the inequality constraints from each other would require a solver change + // and not give us any more parallelism, although we might get better convergence. + +namespace +{ + PX_FORCE_INLINE Vec3V V3FromV4(Vec4V x) { return Vec3V_From_Vec4V(x); } + PX_FORCE_INLINE Vec3V V3FromV4Unsafe(Vec4V x) { return Vec3V_From_Vec4V_WUndefined(x); } + PX_FORCE_INLINE Vec4V V4FromV3(Vec3V x) { return Vec4V_From_Vec3V(x); } + //PX_FORCE_INLINE Vec4V V4ClearW(Vec4V x) { return V4SetW(x, FZero()); } + +struct MassProps +{ + FloatV invMass0; + FloatV invMass1; + FloatV invInertiaScale0; + FloatV invInertiaScale1; + + PX_FORCE_INLINE MassProps(const PxReal imass0, const PxReal imass1, const PxConstraintInvMassScale& ims) + : + invMass0(FLoad(imass0 * ims.linear0)) + , invMass1(FLoad(imass1 * ims.linear1)) + , invInertiaScale0(FLoad(ims.angular0)) + , invInertiaScale1(FLoad(ims.angular1)) + {} +}; + + +PX_FORCE_INLINE PxReal innerProduct(const Px1DConstraint& row0, Px1DConstraint& row1, + PxVec4& row0AngSqrtInvInertia0, PxVec4& row0AngSqrtInvInertia1, + PxVec4& row1AngSqrtInvInertia0, PxVec4& row1AngSqrtInvInertia1, const MassProps& m) +{ + const Vec3V l0 = V3Mul(V3Scale(V3LoadA(row0.linear0), m.invMass0), V3LoadA(row1.linear0)); + const Vec3V l1 = V3Mul(V3Scale(V3LoadA(row0.linear1), m.invMass1), V3LoadA(row1.linear1)); + Vec4V r0ang0 = V4LoadA(&row0AngSqrtInvInertia0.x); + Vec4V r1ang0 = V4LoadA(&row1AngSqrtInvInertia0.x); + Vec4V r0ang1 = V4LoadA(&row0AngSqrtInvInertia1.x); + Vec4V r1ang1 = V4LoadA(&row1AngSqrtInvInertia1.x); + + const Vec3V i0 = V3ScaleAdd(V3Mul(Vec3V_From_Vec4V(r0ang0), Vec3V_From_Vec4V(r1ang0)), m.invInertiaScale0, l0); + const Vec3V i1 = V3ScaleAdd(V3MulAdd(Vec3V_From_Vec4V(r0ang1), Vec3V_From_Vec4V(r1ang1), i0), m.invInertiaScale1, l1); + PxF32 f; + FStore(V3SumElems(i1), &f); + return f; +} + + +// indexed rotation around axis, with sine and cosine of half-angle +PX_FORCE_INLINE PxQuat indexedRotation(PxU32 axis, PxReal s, PxReal c) +{ + PxQuat q(0,0,0,c); + reinterpret_cast(&q)[axis] = s; + return q; +} + +PxQuat diagonalize(const PxMat33& m) // jacobi rotation using quaternions +{ + const PxU32 MAX_ITERS = 5; + + PxQuat q = PxQuat(PxIdentity); + + PxMat33 d; + for(PxU32 i=0; i < MAX_ITERS;i++) + { + const PxMat33 axes(q); + d = axes.getTranspose() * m * axes; + + const PxReal d0 = PxAbs(d[1][2]), d1 = PxAbs(d[0][2]), d2 = PxAbs(d[0][1]); + const PxU32 a = PxU32(d0 > d1 && d0 > d2 ? 0 : d1 > d2 ? 1 : 2); // rotation axis index, from largest off-diagonal element + + const PxU32 a1 = Ps::getNextIndex3(a), a2 = Ps::getNextIndex3(a1); + if(d[a1][a2] == 0.0f || PxAbs(d[a1][a1]-d[a2][a2]) > 2e6f*PxAbs(2.0f*d[a1][a2])) + break; + + const PxReal w = (d[a1][a1]-d[a2][a2]) / (2.0f*d[a1][a2]); // cot(2 * phi), where phi is the rotation angle + const PxReal absw = PxAbs(w); + + PxQuat r; + if(absw>1000) + r = indexedRotation(a, 1.0f/(4.0f*w), 1.f); // h will be very close to 1, so use small angle approx instead + else + { + const PxReal t = 1 / (absw + PxSqrt(w*w+1)); // absolute value of tan phi + const PxReal h = 1 / PxSqrt(t*t+1); // absolute value of cos phi + + PX_ASSERT(h!=1); // |w|<1000 guarantees this with typical IEEE754 machine eps (approx 6e-8) + r = indexedRotation(a, PxSqrt((1-h)/2) * PxSign(w), PxSqrt((1+h)/2)); + } + + q = (q*r).getNormalized(); + } + + return q; +} + + +PX_FORCE_INLINE void rescale(const Mat33V& m, PxVec3& a0, PxVec3& a1, PxVec3& a2) +{ + const Vec3V va0 = V3LoadU(a0); + const Vec3V va1 = V3LoadU(a1); + const Vec3V va2 = V3LoadU(a2); + + const Vec3V b0 = V3ScaleAdd(va0, V3GetX(m.col0), V3ScaleAdd(va1, V3GetY(m.col0), V3Scale(va2, V3GetZ(m.col0)))); + const Vec3V b1 = V3ScaleAdd(va0, V3GetX(m.col1), V3ScaleAdd(va1, V3GetY(m.col1), V3Scale(va2, V3GetZ(m.col1)))); + const Vec3V b2 = V3ScaleAdd(va0, V3GetX(m.col2), V3ScaleAdd(va1, V3GetY(m.col2), V3Scale(va2, V3GetZ(m.col2)))); + + V3StoreU(b0, a0); + V3StoreU(b1, a1); + V3StoreU(b2, a2); +} + +PX_FORCE_INLINE void rescale4(const Mat33V& m, PxReal* a0, PxReal* a1, PxReal* a2) +{ + const Vec4V va0 = V4LoadA(a0); + const Vec4V va1 = V4LoadA(a1); + const Vec4V va2 = V4LoadA(a2); + + const Vec4V b0 = V4ScaleAdd(va0, V3GetX(m.col0), V4ScaleAdd(va1, V3GetY(m.col0), V4Scale(va2, V3GetZ(m.col0)))); + const Vec4V b1 = V4ScaleAdd(va0, V3GetX(m.col1), V4ScaleAdd(va1, V3GetY(m.col1), V4Scale(va2, V3GetZ(m.col1)))); + const Vec4V b2 = V4ScaleAdd(va0, V3GetX(m.col2), V4ScaleAdd(va1, V3GetY(m.col2), V4Scale(va2, V3GetZ(m.col2)))); + + V4StoreA(b0, a0); + V4StoreA(b1, a1); + V4StoreA(b2, a2); +} + +void diagonalize(Px1DConstraint** row, + PxVec4* angSqrtInvInertia0, + PxVec4* angSqrtInvInertia1, + const MassProps &m) +{ + PxReal a00 = innerProduct(*row[0], *row[0], angSqrtInvInertia0[0], angSqrtInvInertia1[0], angSqrtInvInertia0[0], angSqrtInvInertia1[0], m); + PxReal a01 = innerProduct(*row[0], *row[1], angSqrtInvInertia0[0], angSqrtInvInertia1[0], angSqrtInvInertia0[1], angSqrtInvInertia1[1], m); + PxReal a02 = innerProduct(*row[0], *row[2], angSqrtInvInertia0[0], angSqrtInvInertia1[0], angSqrtInvInertia0[2], angSqrtInvInertia1[2], m); + PxReal a11 = innerProduct(*row[1], *row[1], angSqrtInvInertia0[1], angSqrtInvInertia1[1], angSqrtInvInertia0[1], angSqrtInvInertia1[1], m); + PxReal a12 = innerProduct(*row[1], *row[2], angSqrtInvInertia0[1], angSqrtInvInertia1[1], angSqrtInvInertia0[2], angSqrtInvInertia1[2], m); + PxReal a22 = innerProduct(*row[2], *row[2], angSqrtInvInertia0[2], angSqrtInvInertia1[2], angSqrtInvInertia0[2], angSqrtInvInertia1[2], m); + + PxMat33 a(PxVec3(a00, a01, a02), + PxVec3(a01, a11, a12), + PxVec3(a02, a12, a22)); + + PxQuat q = diagonalize(a); + + PxMat33 n(-q); + + Mat33V mn(V3LoadU(n.column0), V3LoadU(n.column1), V3LoadU(n.column2)); + + //KS - We treat as a Vec4V so that we get geometricError rescaled for free along with linear0 + rescale4(mn, &row[0]->linear0.x, &row[1]->linear0.x, &row[2]->linear0.x); + rescale(mn, row[0]->linear1, row[1]->linear1, row[2]->linear1); + //KS - We treat as a PxVec4 so that we get velocityTarget rescaled for free + rescale4(mn, &row[0]->angular0.x, &row[1]->angular0.x, &row[2]->angular0.x); + rescale(mn, row[0]->angular1, row[1]->angular1, row[2]->angular1); + rescale4(mn, &angSqrtInvInertia0[0].x, &angSqrtInvInertia0[1].x, &angSqrtInvInertia0[2].x); + rescale4(mn, &angSqrtInvInertia1[0].x, &angSqrtInvInertia1[1].x, &angSqrtInvInertia1[2].x); + +} + +void orthogonalize(Px1DConstraint** row, + PxVec4* angSqrtInvInertia0, + PxVec4* angSqrtInvInertia1, + PxU32 rowCount, + PxU32 eqRowCount, + const MassProps &m) +{ + PX_ASSERT(eqRowCount<=6); + + const FloatV zero = FZero(); + + Vec3V lin1m[6], ang1m[6], lin1[6], ang1[6]; + Vec4V lin0m[6], ang0m[6]; // must have 0 in the W-field + Vec4V lin0AndG[6], ang0AndT[6]; + + for(PxU32 i=0;ilinear0.x); // linear0 and geometric error + Vec4V a0AndT = V4LoadA(&row[i]->angular0.x); // angular0 and velocity target + + Vec3V l1 = V3FromV4(V4LoadA(&row[i]->linear1.x)); + Vec3V a1 = V3FromV4(V4LoadA(&row[i]->angular1.x)); + + Vec4V angSqrtL0 = V4LoadA(&angSqrtInvInertia0[i].x); + Vec4V angSqrtL1 = V4LoadA(&angSqrtInvInertia1[i].x); + + PxU32 eliminationRows = PxMin(i, eqRowCount); + for(PxU32 j=0;jlinear0.x); + V4StoreA(a0AndT, &row[i]->angular0.x); + V3StoreA(l1, row[i]->linear1); + V3StoreA(a1, row[i]->angular1); + V4StoreA(angSqrtL0, &angSqrtInvInertia0[i].x); + V4StoreA(angSqrtL1, &angSqrtInvInertia1[i].x); + + if(i0 && r->solveHint < sorted[j-1]->solveHint; j--) + sorted[j] = sorted[j-1]; + + sorted[j] = r; + } + + for(PxU32 i=0;isolveHint <= sorted[i+1]->solveHint); + + for (PxU32 i = 0; iangular0)); + const Vec3V angDelta1 = M33MulV3(sqrtInvInertia1, V3LoadU(sorted[i]->angular1)); + V4StoreA(Vec4V_From_Vec3V(angDelta0), &angSqrtInvInertia0[i].x); + V4StoreA(Vec4V_From_Vec3V(angDelta1), &angSqrtInvInertia1[i].x); + } + + if(disablePreprocessing) + return; + + MassProps m(invMass0, invMass1, ims); + for(PxU32 i=0;isolveHint>>8), start = i++; + while(isolveHint>>8) == groupMajorId) + i++; + + if(groupMajorId == 4 || (groupMajorId == 8 && preprocessLinear)) + { + PxU32 bCount = start; // count of bilateral constraints + for(; bCountsolveHint&255)==0; bCount++) + ; + orthogonalize(sorted+start, angSqrtInvInertia0+start, angSqrtInvInertia1+start, i-start, bCount-start, m); + } + + if(groupMajorId == 1 && diagonalizeDrive) + { + PxU32 slerp = start; // count of bilateral constraints + for(; slerpsolveHint&255)!=2; slerp++) + ; + if(slerp+3 == i) + diagonalize(sorted+slerp, angSqrtInvInertia0+slerp, angSqrtInvInertia1+slerp, m); + + PX_ASSERT(i-start==3); + diagonalize(sorted+start, angSqrtInvInertia0+start, angSqrtInvInertia1+start, m); + } + } +} + + + + + +PxU32 ConstraintHelper::setupSolverConstraint( +PxSolverConstraintPrepDesc& prepDesc, +PxConstraintAllocator& allocator, +PxReal dt, PxReal invdt, +Cm::SpatialVectorF* Z) +{ + if (prepDesc.numRows == 0) + { + prepDesc.desc->constraint = NULL; + prepDesc.desc->writeBack = NULL; + prepDesc.desc->constraintLengthOver16 = 0; + prepDesc.desc->writeBackLengthOver4 = 0; + return 0; + } + + PxSolverConstraintDesc& desc = *prepDesc.desc; + + bool isExtended = desc.linkIndexA != PxSolverConstraintDesc::NO_LINK + || desc.linkIndexB != PxSolverConstraintDesc::NO_LINK; + + PxU32 stride = isExtended ? sizeof(SolverConstraint1DExt) : sizeof(SolverConstraint1D); + const PxU32 constraintLength = sizeof(SolverConstraint1DHeader) + stride * prepDesc.numRows; + + //KS - +16 is for the constraint progress counter, which needs to be the last element in the constraint (so that we + //know SPU DMAs have completed) + PxU8* ptr = allocator.reserveConstraintData(constraintLength + 16u); + if(NULL == ptr || (reinterpret_cast(-1))==ptr) + { + if(NULL==ptr) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept joints detaching/exploding or increase buffer size allocated for constraint prep by increasing PxSceneDesc::maxNbContactDataBlocks."); + return 0; + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of constraint data. " + "Either accept joints detaching/exploding or simplify constraints."); + ptr=NULL; + return 0; + } + } + desc.constraint = ptr; + + setConstraintLength(desc,constraintLength); + + desc.writeBack = prepDesc.writeback; + setWritebackLength(desc, sizeof(ConstraintWriteback)); + + memset(desc.constraint, 0, constraintLength); + + SolverConstraint1DHeader* header = reinterpret_cast(desc.constraint); + PxU8* constraints = desc.constraint + sizeof(SolverConstraint1DHeader); + init(*header, Ps::to8(prepDesc.numRows), isExtended, prepDesc.mInvMassScales); + header->body0WorldOffset = prepDesc.body0WorldOffset; + header->linBreakImpulse = prepDesc.linBreakForce * dt; + header->angBreakImpulse = prepDesc.angBreakForce * dt; + header->breakable = PxU8((prepDesc.linBreakForce != PX_MAX_F32) || (prepDesc.angBreakForce != PX_MAX_F32)); + header->invMass0D0 = prepDesc.data0->invMass * prepDesc.mInvMassScales.linear0; + header->invMass1D1 = prepDesc.data1->invMass * prepDesc.mInvMassScales.linear1; + + + PX_ALIGN(16, PxVec4) angSqrtInvInertia0[MAX_CONSTRAINT_ROWS]; + PX_ALIGN(16, PxVec4) angSqrtInvInertia1[MAX_CONSTRAINT_ROWS]; + + Px1DConstraint* sorted[MAX_CONSTRAINT_ROWS]; + + preprocessRows(sorted, prepDesc.rows, angSqrtInvInertia0, angSqrtInvInertia1, prepDesc.numRows, + prepDesc.data0->sqrtInvInertia, prepDesc.data1->sqrtInvInertia, prepDesc.data0->invMass, prepDesc.data1->invMass, + prepDesc.mInvMassScales, isExtended || prepDesc.disablePreprocessing, prepDesc.improvedSlerp, true); + + const PxReal erp = 1.0f; + for (PxU32 i = 0; i(constraints); + Px1DConstraint& c = *sorted[i]; + + PxReal driveScale = c.flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && prepDesc.driveLimitsAreForces ? PxMin(dt, 1.0f) : 1.0f; + + PxReal unitResponse; + PxReal normalVel = 0.0f; + PxReal initVel = 0.f; + + PxReal minResponseThreshold = prepDesc.minResponseThreshold; + + if(!isExtended) + { + init(s, c.linear0, c.linear1, PxVec3(angSqrtInvInertia0[i].x, angSqrtInvInertia0[i].y, angSqrtInvInertia0[i].z), + PxVec3(angSqrtInvInertia1[i].x, angSqrtInvInertia1[i].y, angSqrtInvInertia1[i].z), c.minImpulse * driveScale, c.maxImpulse * driveScale); + s.ang0Writeback = c.angular0; + PxReal resp0 = s.lin0.magnitudeSquared() * prepDesc.data0->invMass * prepDesc.mInvMassScales.linear0 + s.ang0.magnitudeSquared() * prepDesc.mInvMassScales.angular0; + PxReal resp1 = s.lin1.magnitudeSquared() * prepDesc.data1->invMass * prepDesc.mInvMassScales.linear1 + s.ang1.magnitudeSquared() * prepDesc.mInvMassScales.angular1; + unitResponse = resp0 + resp1; + initVel = normalVel = prepDesc.data0->projectVelocity(c.linear0, c.angular0) - prepDesc.data1->projectVelocity(c.linear1, c.angular1); + } + else + { + init(s, c.linear0, c.linear1, c.angular0, c.angular1, c.minImpulse * driveScale, c.maxImpulse * driveScale); + SolverConstraint1DExt& e = static_cast(s); + + const SolverExtBody eb0(reinterpret_cast(prepDesc.body0), prepDesc.data0, desc.linkIndexA); + const SolverExtBody eb1(reinterpret_cast(prepDesc.body1), prepDesc.data1, desc.linkIndexB); + + const Cm::SpatialVector resp0 = createImpulseResponseVector(e.lin0, e.ang0, eb0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-e.lin1, -e.ang1, eb1); + unitResponse = getImpulseResponse(eb0, resp0, unsimdRef(e.deltaVA), prepDesc.mInvMassScales.linear0, prepDesc.mInvMassScales.angular0, + eb1, resp1, unsimdRef(e.deltaVB), prepDesc.mInvMassScales.linear1, prepDesc.mInvMassScales.angular1, Z, false); + + if(!(c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT)) + { + PxReal totalMag = (unsimdRef(e.deltaVA) - unsimdRef(e.deltaVB)).magnitude(); + + PxReal ratio = unitResponse / totalMag; + if (ratio < 0.05f) + unitResponse = 0.f; + } + + + s.ang0Writeback = c.angular0; + s.lin0 = resp0.linear; + s.ang0 = resp0.angular; + s.lin1 = -resp1.linear; + s.ang1 = -resp1.angular; + PxReal vel0, vel1; + if(needsNormalVel(c) || eb0.mLinkIndex == PxSolverConstraintDesc::NO_LINK || eb1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + vel0 = eb0.projectVelocity(c.linear0, c.angular0); + vel1 = eb1.projectVelocity(c.linear1, c.angular1); + + normalVel = vel0 - vel1; + + //normalVel = eb0.projectVelocity(s.lin0, s.ang0) - eb1.projectVelocity(s.lin1, s.ang1); + if(eb0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + initVel = vel0; + else if(eb1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + initVel = -vel1; + + } + + minResponseThreshold = PxMax(minResponseThreshold, DY_ARTICULATION_MIN_RESPONSE); + } + + setSolverConstants(s.constant, s.unbiasedConstant, s.velMultiplier, s.impulseMultiplier, + c, normalVel, unitResponse, minResponseThreshold, erp, dt, invdt); + + //s.targetVelocity = initVel; + const PxReal velBias = initVel * s.velMultiplier; + s.constant += velBias; + s.unbiasedConstant += velBias; + + if(c.flags & Px1DConstraintFlag::eOUTPUT_FORCE) + s.flags |= DY_SC_FLAG_OUTPUT_FORCE; + + constraints += stride; + } + + //KS - Set the solve count at the end to 0 + *(reinterpret_cast(constraints)) = 0; + *(reinterpret_cast(constraints + 4)) = 0; + PX_ASSERT(desc.constraint + getConstraintLength(desc) == constraints); + return prepDesc.numRows; +} + +PxU32 SetupSolverConstraint(SolverConstraintShaderPrepDesc& shaderDesc, + PxSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + PxReal dt, PxReal invdt, Cm::SpatialVectorF* Z) +{ + // LL shouldn't see broken constraints + + PX_ASSERT(!(reinterpret_cast(prepDesc.writeback)->broken)); + + setConstraintLength(*prepDesc.desc, 0); + + if (!shaderDesc.solverPrep) + return 0; + + //PxU32 numAxisConstraints = 0; + + Px1DConstraint rows[MAX_CONSTRAINT_ROWS]; + + // This is necessary so that there will be sensible defaults and shaders will + // continue to work (albeit with a recompile) if the row format changes. + // It's a bit inefficient because it fills in all constraint rows even if there + // is only going to be one generated. A way around this would be for the shader to + // specify the maximum number of rows it needs, or it could call a subroutine to + // prep the row before it starts filling it it. + + PxMemZero(rows, sizeof(Px1DConstraint)*MAX_CONSTRAINT_ROWS); + + for (PxU32 i = 0; isqrtInvInertia, desc.data1->sqrtInvInertia, desc.data0->invMass, desc.data1->invMass, + desc.mInvMassScales, desc.disablePreprocessing, desc.improvedSlerp); + + numRows += desc.numRows; + } + + + PxU32 stride = sizeof(SolverConstraint1DDynamic4); + + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeader4) + stride * maxRows; + + //KS - +16 is for the constraint progress counter, which needs to be the last element in the constraint (so that we + //know SPU DMAs have completed) + PxU8* ptr = allocator.reserveConstraintData(constraintLength + 16u); + if(NULL == ptr || (reinterpret_cast(-1))==ptr) + { + for(PxU32 a = 0; a < 4; ++a) + { + PxSolverConstraintPrepDesc& desc = constraintDescs[a]; + desc.desc->constraint = NULL; + setConstraintLength(*desc.desc, 0); + desc.desc->writeBack = desc.writeback; + } + + if(NULL==ptr) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept joints detaching/exploding or increase buffer size allocated for constraint prep by increasing PxSceneDesc::maxNbContactDataBlocks."); + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of constraint data. " + "Either accept joints detaching/exploding or simplify constraints."); + ptr=NULL; + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + } + //desc.constraint = ptr; + + totalRows = numRows; + + for(PxU32 a = 0; a < 4; ++a) + { + PxSolverConstraintPrepDesc& desc = constraintDescs[a]; + desc.desc->constraint = ptr; + setConstraintLength(*desc.desc, constraintLength); + desc.desc->writeBack = desc.writeback; + } + + const PxReal erp[4] = { 1.0f, 1.0f, 1.0f, 1.0f}; + //OK, now we build all 4 constraints into a single set of rows + + { + PxU8* currPtr = ptr; + SolverConstraint1DHeader4* header = reinterpret_cast(currPtr); + currPtr += sizeof(SolverConstraint1DHeader4); + + const PxSolverBodyData& bd00 = *constraintDescs[0].data0; + const PxSolverBodyData& bd01 = *constraintDescs[1].data0; + const PxSolverBodyData& bd02 = *constraintDescs[2].data0; + const PxSolverBodyData& bd03 = *constraintDescs[3].data0; + + const PxSolverBodyData& bd10 = *constraintDescs[0].data1; + const PxSolverBodyData& bd11 = *constraintDescs[1].data1; + const PxSolverBodyData& bd12 = *constraintDescs[2].data1; + const PxSolverBodyData& bd13 = *constraintDescs[3].data1; + + //Load up masses, invInertia, velocity etc. + + const Vec4V invMassScale0 = V4LoadXYZW(constraintDescs[0].mInvMassScales.linear0, constraintDescs[1].mInvMassScales.linear0, + constraintDescs[2].mInvMassScales.linear0, constraintDescs[3].mInvMassScales.linear0); + const Vec4V invMassScale1 = V4LoadXYZW(constraintDescs[0].mInvMassScales.linear1, constraintDescs[1].mInvMassScales.linear1, + constraintDescs[2].mInvMassScales.linear1, constraintDescs[3].mInvMassScales.linear1); + + + const Vec4V iMass0 = V4LoadXYZW(bd00.invMass, bd01.invMass, bd02.invMass, bd03.invMass); + + const Vec4V iMass1 = V4LoadXYZW(bd10.invMass, bd11.invMass, bd12.invMass, bd13.invMass); + + const Vec4V invMass0 = V4Mul(iMass0, invMassScale0); + const Vec4V invMass1 = V4Mul(iMass1, invMassScale1); + + + const Vec4V invInertiaScale0 = V4LoadXYZW(constraintDescs[0].mInvMassScales.angular0, constraintDescs[1].mInvMassScales.angular0, + constraintDescs[2].mInvMassScales.angular0, constraintDescs[3].mInvMassScales.angular0); + const Vec4V invInertiaScale1 = V4LoadXYZW(constraintDescs[0].mInvMassScales.angular1, constraintDescs[1].mInvMassScales.angular1, + constraintDescs[2].mInvMassScales.angular1, constraintDescs[3].mInvMassScales.angular1); + + //Velocities + Vec4V linVel00 = V4LoadA(&bd00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&bd10.linearVelocity.x); + Vec4V angVel00 = V4LoadA(&bd00.angularVelocity.x); + Vec4V angVel01 = V4LoadA(&bd10.angularVelocity.x); + + Vec4V linVel10 = V4LoadA(&bd01.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&bd11.linearVelocity.x); + Vec4V angVel10 = V4LoadA(&bd01.angularVelocity.x); + Vec4V angVel11 = V4LoadA(&bd11.angularVelocity.x); + + Vec4V linVel20 = V4LoadA(&bd02.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&bd12.linearVelocity.x); + Vec4V angVel20 = V4LoadA(&bd02.angularVelocity.x); + Vec4V angVel21 = V4LoadA(&bd12.angularVelocity.x); + + Vec4V linVel30 = V4LoadA(&bd03.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&bd13.linearVelocity.x); + Vec4V angVel30 = V4LoadA(&bd03.angularVelocity.x); + Vec4V angVel31 = V4LoadA(&bd13.angularVelocity.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2; + Vec4V linVel1T0, linVel1T1, linVel1T2; + Vec4V angVel0T0, angVel0T1, angVel0T2; + Vec4V angVel1T0, angVel1T1, angVel1T2; + + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2); + PX_TRANSPOSE_44_34(angVel00, angVel10, angVel20, angVel30, angVel0T0, angVel0T1, angVel0T2); + PX_TRANSPOSE_44_34(angVel01, angVel11, angVel21, angVel31, angVel1T0, angVel1T1, angVel1T2); + + + + //body world offsets + Vec4V workOffset0 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[0].body0WorldOffset)); + Vec4V workOffset1 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[1].body0WorldOffset)); + Vec4V workOffset2 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[2].body0WorldOffset)); + Vec4V workOffset3 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[3].body0WorldOffset)); + + Vec4V workOffsetX, workOffsetY, workOffsetZ; + + PX_TRANSPOSE_44_34(workOffset0, workOffset1, workOffset2, workOffset3, workOffsetX, workOffsetY, workOffsetZ); + + const FloatV dtV = FLoad(dt); + Vec4V linBreakForce = V4LoadXYZW(constraintDescs[0].linBreakForce, constraintDescs[1].linBreakForce, + constraintDescs[2].linBreakForce, constraintDescs[3].linBreakForce); + Vec4V angBreakForce = V4LoadXYZW(constraintDescs[0].angBreakForce, constraintDescs[1].angBreakForce, + constraintDescs[2].angBreakForce, constraintDescs[3].angBreakForce); + + + header->break0 = PxU8((constraintDescs[0].linBreakForce != PX_MAX_F32) || (constraintDescs[0].angBreakForce != PX_MAX_F32)); + header->break1 = PxU8((constraintDescs[1].linBreakForce != PX_MAX_F32) || (constraintDescs[1].angBreakForce != PX_MAX_F32)); + header->break2 = PxU8((constraintDescs[2].linBreakForce != PX_MAX_F32) || (constraintDescs[2].angBreakForce != PX_MAX_F32)); + header->break3 = PxU8((constraintDescs[3].linBreakForce != PX_MAX_F32) || (constraintDescs[3].angBreakForce != PX_MAX_F32)); + + + //OK, I think that's everything loaded in + + header->invMass0D0 = invMass0; + header->invMass1D1 = invMass1; + header->angD0 = invInertiaScale0; + header->angD1 = invInertiaScale1; + header->body0WorkOffsetX = workOffsetX; + header->body0WorkOffsetY = workOffsetY; + header->body0WorkOffsetZ = workOffsetZ; + + header->count = maxRows; + header->type = DY_SC_TYPE_BLOCK_1D; + header->linBreakImpulse = V4Scale(linBreakForce, dtV); + header->angBreakImpulse = V4Scale(angBreakForce, dtV); + header->count0 = Ps::to8(constraintDescs[0].numRows); + header->count1 = Ps::to8(constraintDescs[1].numRows); + header->count2 = Ps::to8(constraintDescs[2].numRows); + header->count3 = Ps::to8(constraintDescs[3].numRows); + + //Now we loop over the constraints and build the results... + + PxU32 index0 = 0; + PxU32 endIndex0 = constraintDescs[0].numRows - 1; + PxU32 index1 = startIndex[1]; + PxU32 endIndex1 = index1 + constraintDescs[1].numRows - 1; + PxU32 index2 = startIndex[2]; + PxU32 endIndex2 = index2 + constraintDescs[2].numRows - 1; + PxU32 index3 = startIndex[3]; + PxU32 endIndex3 = index3 + constraintDescs[3].numRows - 1; + + const FloatV one = FOne(); + + for(PxU32 a = 0; a < maxRows; ++a) + { + SolverConstraint1DDynamic4* c = reinterpret_cast(currPtr); + currPtr += stride; + + Px1DConstraint* con0 = allSorted[index0]; + Px1DConstraint* con1 = allSorted[index1]; + Px1DConstraint* con2 = allSorted[index2]; + Px1DConstraint* con3 = allSorted[index3]; + + Vec4V cangDelta00 = V4LoadA(&angSqrtInvInertia0[index0].x); + Vec4V cangDelta01 = V4LoadA(&angSqrtInvInertia0[index1].x); + Vec4V cangDelta02 = V4LoadA(&angSqrtInvInertia0[index2].x); + Vec4V cangDelta03 = V4LoadA(&angSqrtInvInertia0[index3].x); + + Vec4V cangDelta10 = V4LoadA(&angSqrtInvInertia1[index0].x); + Vec4V cangDelta11 = V4LoadA(&angSqrtInvInertia1[index1].x); + Vec4V cangDelta12 = V4LoadA(&angSqrtInvInertia1[index2].x); + Vec4V cangDelta13 = V4LoadA(&angSqrtInvInertia1[index3].x); + + index0 = index0 == endIndex0 ? index0 : index0 + 1; + index1 = index1 == endIndex1 ? index1 : index1 + 1; + index2 = index2 == endIndex2 ? index2 : index2 + 1; + index3 = index3 == endIndex3 ? index3 : index3 + 1; + + Vec4V driveScale = V4Splat(one); + if (con0->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[0].driveLimitsAreForces) + driveScale = V4SetX(driveScale, FMin(one, dtV)); + if (con1->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[1].driveLimitsAreForces) + driveScale = V4SetY(driveScale, FMin(one, dtV)); + if (con2->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[2].driveLimitsAreForces) + driveScale = V4SetZ(driveScale, FMin(one, dtV)); + if (con3->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[3].driveLimitsAreForces) + driveScale = V4SetW(driveScale, FMin(one, dtV)); + + + Vec4V clin00 = V4LoadA(&con0->linear0.x); + Vec4V clin01 = V4LoadA(&con1->linear0.x); + Vec4V clin02 = V4LoadA(&con2->linear0.x); + Vec4V clin03 = V4LoadA(&con3->linear0.x); + + Vec4V cang00 = V4LoadA(&con0->angular0.x); + Vec4V cang01 = V4LoadA(&con1->angular0.x); + Vec4V cang02 = V4LoadA(&con2->angular0.x); + Vec4V cang03 = V4LoadA(&con3->angular0.x); + + Vec4V clin0X, clin0Y, clin0Z; + Vec4V cang0X, cang0Y, cang0Z; + + PX_TRANSPOSE_44_34(clin00, clin01, clin02, clin03, clin0X, clin0Y, clin0Z); + PX_TRANSPOSE_44_34(cang00, cang01, cang02, cang03, cang0X, cang0Y, cang0Z); + + const Vec4V maxImpulse = V4LoadXYZW(con0->maxImpulse, con1->maxImpulse, con2->maxImpulse, con3->maxImpulse); + const Vec4V minImpulse = V4LoadXYZW(con0->minImpulse, con1->minImpulse, con2->minImpulse, con3->minImpulse); + + Vec4V angDelta0X, angDelta0Y, angDelta0Z; + + PX_TRANSPOSE_44_34(cangDelta00, cangDelta01, cangDelta02, cangDelta03, angDelta0X, angDelta0Y, angDelta0Z); + + c->flags[0] = 0; + c->flags[1] = 0; + c->flags[2] = 0; + c->flags[3] = 0; + + c->lin0X = clin0X; + c->lin0Y = clin0Y; + c->lin0Z = clin0Z; + c->ang0X = angDelta0X; + c->ang0Y = angDelta0Y; + c->ang0Z = angDelta0Z; + c->ang0WritebackX = cang0X; + c->ang0WritebackY = cang0Y; + c->ang0WritebackZ = cang0Z; + + c->minImpulse = V4Mul(minImpulse, driveScale); + c->maxImpulse = V4Mul(maxImpulse, driveScale); + c->appliedForce = zero; + + const Vec4V lin0MagSq = V4MulAdd(clin0Z, clin0Z, V4MulAdd(clin0Y, clin0Y, V4Mul(clin0X, clin0X))); + const Vec4V cang0DotAngDelta = V4MulAdd(angDelta0Z, angDelta0Z, V4MulAdd(angDelta0Y, angDelta0Y, V4Mul(angDelta0X, angDelta0X))); + c->flags[0] = 0; + c->flags[1] = 0; + c->flags[2] = 0; + c->flags[3] = 0; + + Vec4V unitResponse = V4MulAdd(lin0MagSq, invMass0, V4Mul(cang0DotAngDelta, invInertiaScale0)); + + Vec4V clin10 = V4LoadA(&con0->linear1.x); + Vec4V clin11 = V4LoadA(&con1->linear1.x); + Vec4V clin12 = V4LoadA(&con2->linear1.x); + Vec4V clin13 = V4LoadA(&con3->linear1.x); + + Vec4V cang10 = V4LoadA(&con0->angular1.x); + Vec4V cang11 = V4LoadA(&con1->angular1.x); + Vec4V cang12 = V4LoadA(&con2->angular1.x); + Vec4V cang13 = V4LoadA(&con3->angular1.x); + + Vec4V clin1X, clin1Y, clin1Z; + Vec4V cang1X, cang1Y, cang1Z; + PX_TRANSPOSE_44_34(clin10, clin11, clin12, clin13, clin1X, clin1Y, clin1Z); + PX_TRANSPOSE_44_34(cang10, cang11, cang12, cang13, cang1X, cang1Y, cang1Z); + + Vec4V angDelta1X, angDelta1Y, angDelta1Z; + + PX_TRANSPOSE_44_34(cangDelta10, cangDelta11, cangDelta12, cangDelta13, angDelta1X, angDelta1Y, angDelta1Z); + + const Vec4V lin1MagSq = V4MulAdd(clin1Z, clin1Z, V4MulAdd(clin1Y, clin1Y, V4Mul(clin1X, clin1X))); + const Vec4V cang1DotAngDelta = V4MulAdd(angDelta1Z, angDelta1Z, V4MulAdd(angDelta1Y, angDelta1Y, V4Mul(angDelta1X, angDelta1X))); + + c->lin1X = clin1X; + c->lin1Y = clin1Y; + c->lin1Z = clin1Z; + + c->ang1X = angDelta1X; + c->ang1Y = angDelta1Y; + c->ang1Z = angDelta1Z; + + unitResponse = V4Add(unitResponse, V4MulAdd(lin1MagSq, invMass1, V4Mul(cang1DotAngDelta, invInertiaScale1))); + + Vec4V linProj0(V4Mul(clin0X, linVel0T0)); + Vec4V linProj1(V4Mul(clin1X, linVel1T0)); + Vec4V angProj0(V4Mul(cang0X, angVel0T0)); + Vec4V angProj1(V4Mul(cang1X, angVel1T0)); + + linProj0 = V4MulAdd(clin0Y, linVel0T1, linProj0); + linProj1 = V4MulAdd(clin1Y, linVel1T1, linProj1); + angProj0 = V4MulAdd(cang0Y, angVel0T1, angProj0); + angProj1 = V4MulAdd(cang1Y, angVel1T1, angProj1); + + linProj0 = V4MulAdd(clin0Z, linVel0T2, linProj0); + linProj1 = V4MulAdd(clin1Z, linVel1T2, linProj1); + angProj0 = V4MulAdd(cang0Z, angVel0T2, angProj0); + angProj1 = V4MulAdd(cang1Z, angVel1T2, angProj1); + + const Vec4V projectVel0 = V4Add(linProj0, angProj0); + const Vec4V projectVel1 = V4Add(linProj1, angProj1); + + const Vec4V normalVel = V4Sub(projectVel0, projectVel1); + + + { + const PxVec4& ur = reinterpret_cast(unitResponse); + PxVec4& cConstant = reinterpret_cast(c->constant); + PxVec4& cUnbiasedConstant = reinterpret_cast(c->unbiasedConstant); + PxVec4& cVelMultiplier = reinterpret_cast(c->velMultiplier); + PxVec4& cImpulseMultiplier = reinterpret_cast(c->impulseMultiplier); + + setConstants(cConstant.x, cUnbiasedConstant.x, cVelMultiplier.x, cImpulseMultiplier.x, + *con0, ur.x, constraintDescs[0].minResponseThreshold, erp[0], dt, recipdt, + *constraintDescs[0].data0, *constraintDescs[0].data1, a >= constraintDescs[0].numRows); + + setConstants(cConstant.y, cUnbiasedConstant.y, cVelMultiplier.y, cImpulseMultiplier.y, + *con1, ur.y, constraintDescs[1].minResponseThreshold, erp[1], dt, recipdt, + *constraintDescs[1].data0, *constraintDescs[1].data1, a >= constraintDescs[1].numRows); + + setConstants(cConstant.z, cUnbiasedConstant.z, cVelMultiplier.z, cImpulseMultiplier.z, + *con2, ur.z, constraintDescs[2].minResponseThreshold, erp[2], dt, recipdt, + *constraintDescs[2].data0, *constraintDescs[2].data1, a >= constraintDescs[2].numRows); + + setConstants(cConstant.w, cUnbiasedConstant.w, cVelMultiplier.w, cImpulseMultiplier.w, + *con3, ur.w, constraintDescs[3].minResponseThreshold, erp[3], dt, recipdt, + *constraintDescs[3].data0, *constraintDescs[3].data1, a >= constraintDescs[3].numRows); + } + + const Vec4V velBias = V4Mul(c->velMultiplier, normalVel); + c->constant = V4Add(c->constant, velBias); + c->unbiasedConstant = V4Add(c->unbiasedConstant, velBias); + + if(con0->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[0] |= DY_SC_FLAG_OUTPUT_FORCE; + if(con1->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[1] |= DY_SC_FLAG_OUTPUT_FORCE; + if(con2->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[2] |= DY_SC_FLAG_OUTPUT_FORCE; + if(con3->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[3] |= DY_SC_FLAG_OUTPUT_FORCE; + } + *(reinterpret_cast(currPtr)) = 0; + *(reinterpret_cast(currPtr + 4)) = 0; + } + + //OK, we're ready to allocate and solve prep these constraints now :-) + return SolverConstraintPrepState::eSUCCESS; +} + +} + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp new file mode 100644 index 000000000..0b02e9eed --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp @@ -0,0 +1,820 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "PxSceneDesc.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContact4.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DyDynamics.h" +#include "DyArticulationContactPrep.h" +#include "PxsContactManager.h" +#include "PsFoundation.h" +#include "DyConstraintPrep.h" + +using namespace physx; +using namespace Gu; + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DyContactPrepShared.h" + +using namespace Ps::aos; + +namespace physx +{ +namespace Dy +{ + +PxcCreateFinalizeSolverContactMethod createFinalizeMethods[3] = +{ + createFinalizeSolverContacts, + createFinalizeSolverContactsCoulomb1D, + createFinalizeSolverContactsCoulomb2D +}; + + + +static void setupFinalizeSolverConstraints(Sc::ShapeInteraction* shapeInteraction, + const ContactPoint* buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const PxSolverBodyData& data0, + const PxSolverBodyData& data1, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + bool hasForceThreshold, bool staticOrKinematicBody, + const PxReal restDist, PxU8* frictionDataPtr, + const PxReal maxCCDSeparation, + const PxReal solverOffsetSlopF32) +{ + // NOTE II: the friction patches are sparse (some of them have no contact patches, and + // therefore did not get written back to the cache) but the patch addresses are dense, + // corresponding to valid patches + + const Vec3V solverOffsetSlop = V3Load(solverOffsetSlopF32); + + const FloatV ccdMaxSeparation = FLoad(maxCCDSeparation); + + PxU8 flags = PxU8(hasForceThreshold ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0); + + PxU8* PX_RESTRICT ptr = workspace; + + PxU8 type = Ps::to8(staticOrKinematicBody ? DY_SC_TYPE_STATIC_CONTACT + : DY_SC_TYPE_RB_CONTACT); + + const FloatV zero=FZero(); + const Vec3V v3Zero = V3Zero(); + + const FloatV d0 = FLoad(invMassScale0); + const FloatV d1 = FLoad(invMassScale1); + const FloatV angD0 = FLoad(invInertiaScale0); + const FloatV angD1 = FLoad(invInertiaScale1); + + const FloatV nDom1fV = FNeg(d1); + + const FloatV invMass0 = FLoad(data0.invMass); + const FloatV invMass1 = FLoad(data1.invMass); + + const FloatV invMass0_dom0fV = FMul(d0, invMass0); + const FloatV invMass1_dom1fV = FMul(nDom1fV, invMass1); + + + Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4Zero(); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, invMass0_dom0fV); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, invMass1_dom1fV); + + const FloatV restDistance = FLoad(restDist); + + const FloatV maxPenBias = FMax(FLoad(data0.penBiasClamp), FLoad(data1.penBiasClamp)); + + const QuatV bodyFrame0q = QuatVLoadU(&bodyFrame0.q.x); + const Vec3V bodyFrame0p = V3LoadU(bodyFrame0.p); + + const QuatV bodyFrame1q = QuatVLoadU(&bodyFrame1.q.x); + const Vec3V bodyFrame1p = V3LoadU(bodyFrame1.p); + + PxU32 frictionPatchWritebackAddrIndex = 0; + PxU32 contactWritebackCount = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + const Vec3V linVel0 = V3LoadU_SafeReadW(data0.linearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + const Vec3V linVel1 = V3LoadU_SafeReadW(data1.linearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + const Vec3V angVel0 = V3LoadU_SafeReadW(data0.angularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + const Vec3V angVel1 = V3LoadU_SafeReadW(data1.angularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + + PX_ALIGN(16, const Mat33V invSqrtInertia0) + ( + V3LoadU_SafeReadW(data0.sqrtInvInertia.column0), // PT: safe because 'column1' follows 'column0' in PxMat33 + V3LoadU_SafeReadW(data0.sqrtInvInertia.column1), // PT: safe because 'column2' follows 'column1' in PxMat33 + V3LoadU(data0.sqrtInvInertia.column2) + ); + + PX_ALIGN(16, const Mat33V invSqrtInertia1) + ( + V3LoadU_SafeReadW(data1.sqrtInvInertia.column0), // PT: safe because 'column1' follows 'column0' in PxMat33 + V3LoadU_SafeReadW(data1.sqrtInvInertia.column1), // PT: safe because 'column2' follows 'column1' in PxMat33 + V3LoadU(data1.sqrtInvInertia.column2) + ); + + const FloatV invDt = FLoad(invDtF32); + const FloatV p8 = FLoad(0.8f); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + + const FloatV invDtp8 = FMul(invDt, p8); + + + for(PxU32 i=0;irestitution; + + SolverContactHeader* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeader); + + + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + + header->shapeInteraction = shapeInteraction; + header->flags = flags; + FStore(invMass0_dom0fV, &header->invMass0); + FStore(FNeg(invMass1_dom1fV), &header->invMass1); + const FloatV restitution = FLoad(combinedRestitution); + + PxU32 pointStride = sizeof(SolverContactPoint); + PxU32 frictionStride = sizeof(SolverContactFriction); + + const Vec3V normal = V3LoadA(buffer[c.contactPatches[c.correlationListHeads[i]].start].normal); + const FloatV normalLenSq = V3LengthSq(normal); + const VecCrossV norCross = V3PrepareCross(normal); + const FloatV norVel = V3SumElems(V3NegMulSub(normal, linVel1, V3Mul(normal, linVel0))); + + const FloatV invMassNorLenSq0 = FMul(invMass0_dom0fV, normalLenSq); + const FloatV invMassNorLenSq1 = FMul(invMass1_dom1fV, normalLenSq); + + header->normal_minAppliedImpulseForFrictionW = Vec4V_From_Vec3V(normal); + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer + c.contactPatches[patch].start; + + PxU8* p = ptr; + + for(PxU32 j=0;j(p); + p += pointStride; + + constructContactConstraint(invSqrtInertia0, invSqrtInertia1, invMassNorLenSq0, + invMassNorLenSq1, angD0, angD1, bodyFrame0p, bodyFrame1p, + normal, norVel, norCross, angVel0, angVel1, + invDt, invDtp8, restDistance, maxPenBias, restitution, + bounceThreshold, contact, *solverContact, + ccdMaxSeparation, solverOffsetSlop); + } + + ptr = p; + } + contactWritebackCount += contactCount; + + PxF32* forceBuffers = reinterpret_cast(ptr); + PxMemZero(forceBuffers, sizeof(PxF32) * contactCount); + ptr += ((contactCount + 3) & (~3)) * sizeof(PxF32); // jump to next 16-byte boundary + + const PxReal staticFriction = contactBase0->staticFriction; + const PxReal dynamicFriction = contactBase0->dynamicFriction; + const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); + + const bool haveFriction = (disableStrongFriction == 0 && frictionPatch.anchorCount != 0) ;//PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0; + header->numNormalConstr = Ps::to8(contactCount); + header->numFrictionConstr = Ps::to8(haveFriction ? frictionPatch.anchorCount*2 : 0); + + header->type = type; + + header->staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; + FStore(angD0, &header->angDom0); + FStore(angD1, &header->angDom1); + + header->broken = 0; + + if(haveFriction) + { + const Vec3V linVrel = V3Sub(linVel0, linVel1); + //const Vec3V normal = Vec3V_From_PxVec3_Aligned(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); + + const FloatV orthoThreshold = FLoad(0.70710678f); + const FloatV p1 = FLoad(0.1f); + // fallback: normal.cross((1,0,0)) or normal.cross((0,0,1)) + const FloatV normalX = V3GetX(normal); + const FloatV normalY = V3GetY(normal); + const FloatV normalZ = V3GetZ(normal); + + Vec3V t0Fallback1 = V3Merge(zero, FNeg(normalZ), normalY); + Vec3V t0Fallback2 = V3Merge(FNeg(normalY), normalX, zero) ; + Vec3V t0Fallback = V3Sel(FIsGrtr(orthoThreshold, FAbs(normalX)), t0Fallback1, t0Fallback2); + + Vec3V t0 = V3Sub(linVrel, V3Scale(normal, V3Dot(normal, linVrel))); + t0 = V3Sel(FIsGrtr(V3LengthSq(t0), p1), t0, t0Fallback); + t0 = V3Normalize(t0); + + const VecCrossV t0Cross = V3PrepareCross(t0); + + const Vec3V t1 = V3Cross(norCross, t0Cross); + const VecCrossV t1Cross = V3PrepareCross(t1); + + + // since we don't even have the body velocities we can't compute the tangent dirs, so + // the only thing we can do right now is to write the geometric information (which is the + // same for both axis constraints of an anchor) We put ra in the raXn field, rb in the rbXn + // field, and the error in the normal field. See corresponding comments in + // completeContactFriction() + + //We want to set the writeBack ptr to point to the broken flag of the friction patch. + //On spu we have a slight problem here because the friction patch array is + //in local store rather than in main memory. The good news is that the address of the friction + //patch array in main memory is stored in the work unit. These two addresses will be equal + //except on spu where one is local store memory and the other is the effective address in main memory. + //Using the value stored in the work unit guarantees that the main memory address is used on all platforms. + PxU8* PX_RESTRICT writeback = frictionDataPtr + frictionPatchWritebackAddrIndex*sizeof(FrictionPatch); + + header->frictionBrokenWritebackByte = writeback; + + for(PxU32 j = 0; j < frictionPatch.anchorCount; j++) + { + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + SolverContactFriction* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionStride; + SolverContactFriction* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionStride; + + Vec3V body0Anchor = V3LoadU(frictionPatch.body0Anchors[j]); + Vec3V body1Anchor = V3LoadU(frictionPatch.body1Anchors[j]); + + const Vec3V ra = QuatRotate(bodyFrame0q, body0Anchor); + const Vec3V rb = QuatRotate(bodyFrame1q, body1Anchor); + + /*ra = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(ra)), v3Zero, ra); + rb = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rb)), v3Zero, rb);*/ + + PxU32 index = c.contactID[i][j]; + Vec3V error = V3Sub(V3Add(ra, bodyFrame0p), V3Add(rb, bodyFrame1p)); + error = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(error)), v3Zero, error); + + index = index == 0xFFFF ? c.contactPatches[c.correlationListHeads[i]].start : index; + + const Vec3V tvel = V3LoadA(buffer[index].targetVel); + + { + Vec3V raXn = V3Cross(ra, t0Cross); + Vec3V rbXn = V3Cross(rb, t0Cross); + + raXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(raXn)), V3Zero(), raXn); + rbXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rbXn)), V3Zero(), rbXn); + + const Vec3V raXnSqrtInertia = M33MulV3(invSqrtInertia0, raXn); + const Vec3V rbXnSqrtInertia = M33MulV3(invSqrtInertia1, rbXn); + + + const FloatV resp0 = FAdd(invMass0_dom0fV, FMul(angD0, V3Dot(raXnSqrtInertia, raXnSqrtInertia))); + const FloatV resp1 = FSub(FMul(angD1, V3Dot(rbXnSqrtInertia, rbXnSqrtInertia)), invMass1_dom1fV); + const FloatV resp = FAdd(resp0, resp1); + + const FloatV velMultiplier = FSel(FIsGrtr(resp, zero), FDiv(p8, resp), zero); + + FloatV targetVel = V3Dot(tvel, t0); + + const FloatV vrel1 = FAdd(V3Dot(t0, linVel0), V3Dot(raXn, angVel0)); + const FloatV vrel2 = FAdd(V3Dot(t0, linVel1), V3Dot(rbXn, angVel1)); + const FloatV vrel = FSub(vrel1, vrel2); + + targetVel = FSub(targetVel, vrel); + + f0->normalXYZ_appliedForceW = V4SetW(t0, zero); + f0->raXnXYZ_velMultiplierW = V4SetW(raXnSqrtInertia, velMultiplier); + f0->rbXnXYZ_biasW = V4SetW(rbXnSqrtInertia, FMul(V3Dot(t0, error), invDt)); + FStore(targetVel, &f0->targetVel); + } + + { + + Vec3V raXn = V3Cross(ra, t1Cross); + Vec3V rbXn = V3Cross(rb, t1Cross); + + raXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(raXn)), V3Zero(), raXn); + rbXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rbXn)), V3Zero(), rbXn); + + const Vec3V raXnSqrtInertia = M33MulV3(invSqrtInertia0, raXn); + const Vec3V rbXnSqrtInertia = M33MulV3(invSqrtInertia1, rbXn); + + const FloatV resp0 = FAdd(invMass0_dom0fV, FMul(angD0, V3Dot(raXnSqrtInertia, raXnSqrtInertia))); + const FloatV resp1 = FSub(FMul(angD1, V3Dot(rbXnSqrtInertia, rbXnSqrtInertia)), invMass1_dom1fV); + const FloatV resp = FAdd(resp0, resp1); + + const FloatV velMultiplier = FSel(FIsGrtr(resp, zero), FDiv(p8, resp), zero); + + FloatV targetVel = V3Dot(tvel, t1); + + const FloatV vrel1 = FAdd(V3Dot(t1, linVel0), V3Dot(raXn, angVel0)); + const FloatV vrel2 = FAdd(V3Dot(t1, linVel1), V3Dot(rbXn, angVel1)); + const FloatV vrel = FSub(vrel1, vrel2); + + targetVel = FSub(targetVel, vrel); + + f1->normalXYZ_appliedForceW = V4SetW(t1, zero); + f1->raXnXYZ_velMultiplierW = V4SetW(raXnSqrtInertia, velMultiplier); + f1->rbXnXYZ_biasW = V4SetW(rbXnSqrtInertia, FMul(V3Dot(t1, error), invDt)); + FStore(targetVel, &f1->targetVel); + } + } + } + + frictionPatchWritebackAddrIndex++; + } +} + + +PX_FORCE_INLINE void computeBlockStreamByteSizes(const bool useExtContacts, const CorrelationBuffer& c, + PxU32& _solverConstraintByteSize, PxU32& _frictionPatchByteSize, PxU32& _numFrictionPatches, + PxU32& _axisConstraintCount) +{ + PX_ASSERT(0 == _solverConstraintByteSize); + PX_ASSERT(0 == _frictionPatchByteSize); + PX_ASSERT(0 == _numFrictionPatches); + PX_ASSERT(0 == _axisConstraintCount); + + // PT: use local vars to remove LHS + PxU32 solverConstraintByteSize = 0; + PxU32 numFrictionPatches = 0; + PxU32 axisConstraintCount = 0; + + + for(PxU32 i = 0; i < c.frictionPatchCount; i++) + { + //Friction patches. + if(c.correlationListHeads[i] != CorrelationBuffer::LIST_END) + numFrictionPatches++; + + const FrictionPatch& frictionPatch = c.frictionPatches[i]; + + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0; + + //Solver constraint data. + if(c.frictionPatchContactCounts[i]!=0) + { + solverConstraintByteSize += sizeof(SolverContactHeader); + solverConstraintByteSize += useExtContacts ? c.frictionPatchContactCounts[i] * sizeof(SolverContactPointExt) + : c.frictionPatchContactCounts[i] * sizeof(SolverContactPoint); + solverConstraintByteSize += sizeof(PxF32) * ((c.frictionPatchContactCounts[i] + 3)&(~3)); //Add on space for applied impulses + + axisConstraintCount += c.frictionPatchContactCounts[i]; + + if(haveFriction) + { + solverConstraintByteSize += useExtContacts ? c.frictionPatches[i].anchorCount * 2 * sizeof(SolverContactFrictionExt) + : c.frictionPatches[i].anchorCount * 2 * sizeof(SolverContactFriction); + axisConstraintCount += c.frictionPatches[i].anchorCount * 2; + + } + } + } + PxU32 frictionPatchByteSize = numFrictionPatches*sizeof(FrictionPatch); + + _numFrictionPatches = numFrictionPatches; + _axisConstraintCount = axisConstraintCount; + + //16-byte alignment. + _frictionPatchByteSize = ((frictionPatchByteSize + 0x0f) & ~0x0f); + _solverConstraintByteSize = ((solverConstraintByteSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); + PX_ASSERT(0 == (_frictionPatchByteSize & 0x0f)); +} + +static bool reserveBlockStreams(const bool useExtContacts, Dy::CorrelationBuffer& cBuffer, + PxU8*& solverConstraint, + FrictionPatch*& _frictionPatches, + PxU32& numFrictionPatches, PxU32& solverConstraintByteSize, + PxU32& axisConstraintCount, PxConstraintAllocator& constraintAllocator) +{ + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(NULL == _frictionPatches); + PX_ASSERT(0 == numFrictionPatches); + PX_ASSERT(0 == solverConstraintByteSize); + PX_ASSERT(0 == axisConstraintCount); + + //From frictionPatchStream we just need to reserve a single buffer. + PxU32 frictionPatchByteSize = 0; + //Compute the sizes of all the buffers. + computeBlockStreamByteSizes( + useExtContacts, cBuffer, + solverConstraintByteSize, frictionPatchByteSize, numFrictionPatches, + axisConstraintCount); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if(constraintBlockByteSize > 0) + { + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if(0==constraintBlock || (reinterpret_cast(-1))==constraintBlock) + { + if(0==constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock=NULL; + } + } + PX_ASSERT((size_t(constraintBlock) & 0xF) == 0); + } + + FrictionPatch* frictionPatches = NULL; + //If the constraint block reservation didn't fail then reserve the friction buffer too. + if(frictionPatchByteSize >0 && (0==constraintBlockByteSize || constraintBlock)) + { + frictionPatches = reinterpret_cast(constraintAllocator.reserveFrictionData(frictionPatchByteSize)); + + if(0==frictionPatches || (reinterpret_cast(-1))==frictionPatches) + { + if(0==frictionPatches) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of friction data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + frictionPatches=NULL; + } + } + } + + _frictionPatches = frictionPatches; + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if(0==constraintBlockByteSize || constraintBlock) + { + if(solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0==(uintptr_t(solverConstraint) & 0x0f)); + } + } + + //Return true if neither of the two block reservations failed. + return ((0==constraintBlockByteSize || constraintBlock) && (0==frictionPatchByteSize || frictionPatches)); +} + + +bool createFinalizeSolverContacts( + PxSolverContactDesc& contactDesc, + CorrelationBuffer& c, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z) +{ + Ps::prefetchLine(contactDesc.body0); + Ps::prefetchLine(contactDesc.body1); + Ps::prefetchLine(contactDesc.data0); + Ps::prefetchLine(contactDesc.data1); + + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + const bool hasForceThreshold = contactDesc.hasForceThresholds; + const bool staticOrKinematicBody = contactDesc.bodyState1 == PxSolverContactDesc::eKINEMATIC_BODY || contactDesc.bodyState1 == PxSolverContactDesc::eSTATIC_BODY; + + const bool disableStrongFriction = contactDesc.disableStrongFriction; + const bool useExtContacts = ((contactDesc.bodyState0 | contactDesc.bodyState1) & PxSolverContactDesc::eARTICULATION) != 0; + + PxSolverConstraintDesc& desc = *contactDesc.desc; + + desc.constraintLengthOver16 = 0; + + + if (contactDesc.numContacts == 0) + { + contactDesc.frictionPtr = NULL; + contactDesc.frictionCount = 0; + desc.constraint = NULL; + return true; + } + + if (!disableStrongFriction) + { + getFrictionPatches(c, contactDesc.frictionPtr, contactDesc.frictionCount, contactDesc.bodyFrame0, contactDesc.bodyFrame1, correlationDistance); + } + + bool overflow = !createContactPatches(c, contactDesc.contacts, contactDesc.numContacts, PXC_SAME_NORMAL); + overflow = correlatePatches(c, contactDesc.contacts, contactDesc.bodyFrame0, contactDesc.bodyFrame1, PXC_SAME_NORMAL, 0, 0) || overflow; + PX_UNUSED(overflow); + +#if PX_CHECKED + if (overflow) + { + Ps::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Dropping contacts in solver because we exceeded limit of 32 friction patches."); + } +#endif + + growPatches(c, contactDesc.contacts, contactDesc.bodyFrame0, contactDesc.bodyFrame1, correlationDistance, 0, frictionOffsetThreshold + contactDesc.restDistance); + + //PX_ASSERT(patchCount == c.frictionPatchCount); + + FrictionPatch* frictionPatches = NULL; + PxU8* solverConstraint = NULL; + PxU32 numFrictionPatches = 0; + PxU32 solverConstraintByteSize = 0; + PxU32 axisConstraintCount = 0; + + const bool successfulReserve = reserveBlockStreams( + useExtContacts, c, + solverConstraint, frictionPatches, + numFrictionPatches, + solverConstraintByteSize, + axisConstraintCount, + constraintAllocator); + // initialise the work unit's ptrs to the various buffers. + + contactDesc.frictionPtr = NULL; + contactDesc.frictionCount = 0; + desc.constraint = NULL; + desc.constraintLengthOver16 = 0; + // patch up the work unit with the reserved buffers and set the reserved buffer data as appropriate. + + if (successfulReserve) + { + PxU8* frictionDataPtr = reinterpret_cast(frictionPatches); + contactDesc.frictionPtr = frictionDataPtr; + desc.constraint = solverConstraint; + //output.nbContacts = Ps::to8(numContacts); + contactDesc.frictionCount = Ps::to8(numFrictionPatches); + desc.constraintLengthOver16 = Ps::to16(solverConstraintByteSize / 16); + desc.writeBack = contactDesc.contactForces; + desc.writeBackLengthOver4 = PxU16(contactDesc.contactForces ? contactDesc.numContacts : 0); + + //Initialise friction buffer. + if (frictionPatches) + { + // PT: TODO: revisit this... not very satisfying + //const PxU32 maxSize = numFrictionPatches*sizeof(FrictionPatch); + Ps::prefetchLine(frictionPatches); + Ps::prefetchLine(frictionPatches, 128); + Ps::prefetchLine(frictionPatches, 256); + + for (PxU32 i = 0; i(contactDesc.body0), reinterpret_cast(&data0), desc.linkIndexA); + const SolverExtBody b1(reinterpret_cast(contactDesc.body1), reinterpret_cast(&data1), desc.linkIndexB); + + setupFinalizeExtSolverContacts(contactDesc.contacts, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + b0, b1, invDtF32, bounceThresholdF32, + contactDesc.mInvMassScales.linear0, contactDesc.mInvMassScales.angular0, contactDesc.mInvMassScales.linear1, contactDesc.mInvMassScales.angular1, + contactDesc.restDistance, frictionDataPtr, contactDesc.maxCCDSeparation, Z); + } + else + { + const PxSolverBodyData& data0 = *contactDesc.data0; + const PxSolverBodyData& data1 = *contactDesc.data1; + setupFinalizeSolverConstraints(contactDesc.shapeInteraction, contactDesc.contacts, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + data0, data1, invDtF32, bounceThresholdF32, + contactDesc.mInvMassScales.linear0, contactDesc.mInvMassScales.angular0, contactDesc.mInvMassScales.linear1, contactDesc.mInvMassScales.angular1, + hasForceThreshold, staticOrKinematicBody, contactDesc.restDistance, frictionDataPtr, contactDesc.maxCCDSeparation, solverOffsetSlop); + } + //KS - set to 0 so we have a counter for the number of times we solved the constraint + //only going to be used on SPU but might as well set on all platforms because this code is shared + *(reinterpret_cast(solverConstraint + solverConstraintByteSize)) = 0; + } + } + + return successfulReserve; +} + +FloatV setupExtSolverContact(const SolverExtBody& b0, const SolverExtBody& b1, + const PxF32 d0, const PxF32 d1, const PxF32 angD0, const PxF32 angD1, const PxTransform& bodyFrame0, const PxTransform& bodyFrame1, + const Vec3VArg normal, const FloatVArg invDt, const FloatVArg invDtp8, const FloatVArg restDistance, const FloatVArg maxPenBias, const FloatVArg restitution, + const FloatVArg bounceThreshold, const Gu::ContactPoint& contact, SolverContactPointExt& solverContact, const FloatVArg ccdMaxSeparation, Cm::SpatialVectorF* zVector) +{ + const FloatV zero = FZero(); + const FloatV separation = FLoad(contact.separation); + + const FloatV penetration = FSub(separation, restDistance); + + const PxVec3 ra = contact.point - bodyFrame0.p; + const PxVec3 rb = contact.point - bodyFrame1.p; + + const PxVec3 raXn = ra.cross(contact.normal); + const PxVec3 rbXn = rb.cross(contact.normal); + + Cm::SpatialVector deltaV0, deltaV1; + + const Cm::SpatialVector resp0 = createImpulseResponseVector(contact.normal, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-contact.normal, -rbXn, b1); + + const FloatV unitResponse = FLoad(getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, zVector)); + + + const FloatV vel0 = FLoad(b0.projectVelocity(contact.normal, raXn)); + const FloatV vel1 = FLoad(b1.projectVelocity(contact.normal, rbXn)); + + const FloatV vrel = FSub(vel0, vel1); + + FloatV velMultiplier = FSel(FIsGrtr(FLoad(DY_ARTICULATION_MIN_RESPONSE), unitResponse), zero, FRecip(unitResponse)); + //FloatV velMultiplier = FSel(FIsGrtr(FEps(), unitResponse), zero, FRecip(unitResponse)); + FloatV scaledBias = FMul(velMultiplier, FMax(maxPenBias, FMul(penetration, invDtp8))); + const FloatV penetrationInvDt = FMul(penetration, invDt); + + const BoolV isGreater2 = BAnd(BAnd(FIsGrtr(restitution, zero), FIsGrtr(bounceThreshold, vrel)), FIsGrtr(FNeg(vrel), penetrationInvDt)); + + const BoolV ccdSeparationCondition = FIsGrtrOrEq(ccdMaxSeparation, penetration); + + scaledBias = FSel(BAnd(ccdSeparationCondition, isGreater2), zero, scaledBias); + + FloatV targetVelocity = FSel(isGreater2, FMul(FNeg(vrel), restitution), zero); + + //Get the rigid body's current velocity and embed into the constraint target velocities + if (b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVelocity = FSub(targetVelocity, vel0); + else if (b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVelocity = FAdd(targetVelocity, vel1); + + targetVelocity = FAdd(targetVelocity, V3Dot(V3LoadA(contact.targetVel), normal)); + + const FloatV biasedErr = FScaleAdd(targetVelocity, velMultiplier, FNeg(scaledBias)); + const FloatV unbiasedErr = FScaleAdd(targetVelocity, velMultiplier, FSel(isGreater2, zero, FNeg(FMax(scaledBias, zero)))); + + const FloatV deltaF = FMax(FNegScaleSub(vrel, velMultiplier, biasedErr), zero); + + + FStore(velMultiplier, &solverContact.velMultiplier); + FStore(biasedErr, &solverContact.biasedErr); + FStore(unbiasedErr, &solverContact.unbiasedErr); + solverContact.maxImpulse = contact.maxImpulse; + + solverContact.raXn = V3LoadA(resp0.angular); + solverContact.rbXn = V3Neg(V3LoadA(resp1.angular)); + solverContact.linDeltaVA = V3LoadA(deltaV0.linear); + solverContact.angDeltaVA = V3LoadA(deltaV0.angular); + solverContact.linDeltaVB = V3LoadA(deltaV1.linear); + solverContact.angDeltaVB = V3LoadA(deltaV1.angular); + + return deltaF; +} + + +bool createFinalizeSolverContacts(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z) +{ + ContactBuffer& buffer = threadContext.mContactBuffer; + + + + buffer.count = 0; + + // We pull the friction patches out of the cache to remove the dependency on how + // the cache is organized. Remember original addrs so we can write them back + // efficiently. + + PxU32 numContacts = 0; + { + PxReal invMassScale0 = 1.f; + PxReal invMassScale1 = 1.f; + PxReal invInertiaScale0 = 1.f; + PxReal invInertiaScale1 = 1.f; + + bool hasMaxImpulse = false, hasTargetVelocity = false; + + numContacts = extractContacts(buffer, output, hasMaxImpulse, hasTargetVelocity, invMassScale0, invMassScale1, + invInertiaScale0, invInertiaScale1, PxMin(contactDesc.data0->maxContactImpulse, contactDesc.data1->maxContactImpulse)); + + contactDesc.contacts = buffer.contacts; + contactDesc.numContacts = numContacts; + contactDesc.disableStrongFriction = contactDesc.disableStrongFriction || hasTargetVelocity; + contactDesc.hasMaxImpulse = hasMaxImpulse; + contactDesc.mInvMassScales.linear0 *= invMassScale0; + contactDesc.mInvMassScales.linear1 *= invMassScale1; + contactDesc.mInvMassScales.angular0 *= invInertiaScale0; + contactDesc.mInvMassScales.angular1 *= invInertiaScale1; + } + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + + return createFinalizeSolverContacts(contactDesc, c, invDtF32, bounceThresholdF32, frictionOffsetThreshold, + correlationDistance, solverOffsetSlop, constraintAllocator, Z); +} + +PxU32 getContactManagerConstraintDesc(const PxsContactManagerOutput& cmOutput, const PxsContactManager& /*cm*/, PxSolverConstraintDesc& desc) +{ + desc.writeBackLengthOver4 = cmOutput.nbContacts; + desc.writeBack = cmOutput.contactForces; + return cmOutput.nbContacts;// cm.getWorkUnit().axisConstraintCount; +} + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h new file mode 100644 index 000000000..9edef9ee7 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h @@ -0,0 +1,183 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_CONTACTPREP_H +#define DY_CONTACTPREP_H + +#include "DySolverConstraintDesc.h" +#include "PxSceneDesc.h" +#include "DySolverContact4.h" + +namespace physx +{ + +struct PxcNpWorkUnit; +class PxsConstraintBlockManager; +struct PxsContactManagerOutput; +struct PxSolverBody; +struct PxSolverBodyData; +struct PxSolverConstraintDesc; +class PxsContactManager; + +namespace Dy +{ + class ThreadContext; + struct CorrelationBuffer; + +#define CREATE_FINALIZE_SOLVER_CONTACT_METHOD_ARGS \ + PxSolverContactDesc& contactDesc, \ + PxsContactManagerOutput& output, \ + ThreadContext& threadContext, \ + const PxReal invDtF32, \ + PxReal bounceThresholdF32, \ + PxReal frictionOffsetThreshold, \ + PxReal correlationDistance, \ + PxReal solverOffsetSlop, \ + PxConstraintAllocator& constraintAllocator, \ + Cm::SpatialVectorF* Z + +#define CREATE_FINALIZE_SOVLER_CONTACT_METHOD_ARGS_4 \ + PxsContactManagerOutput** outputs, \ + ThreadContext& threadContext, \ + PxSolverContactDesc* blockDescs, \ + const PxReal invDtF32, \ + PxReal bounceThresholdF32, \ + PxReal frictionThresholdF32, \ + PxReal correlationDistanceF32, \ + PxReal solverOffsetSlopF32, \ + PxConstraintAllocator& constraintAllocator + + +/*! +Method prototype for create finalize solver contact +*/ + +typedef bool (*PxcCreateFinalizeSolverContactMethod)(CREATE_FINALIZE_SOLVER_CONTACT_METHOD_ARGS); + +extern PxcCreateFinalizeSolverContactMethod createFinalizeMethods[3]; + +typedef SolverConstraintPrepState::Enum (*PxcCreateFinalizeSolverContactMethod4)(CREATE_FINALIZE_SOVLER_CONTACT_METHOD_ARGS_4); + +extern PxcCreateFinalizeSolverContactMethod4 createFinalizeMethods4[3]; + + +bool createFinalizeSolverContacts( PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z); + +bool createFinalizeSolverContacts( PxSolverContactDesc& contactDesc, + CorrelationBuffer& c, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z); + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4( PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator); + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4( Dy::CorrelationBuffer& c, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator); + + + +bool createFinalizeSolverContactsCoulomb1D(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z); + +bool createFinalizeSolverContactsCoulomb2D(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z); + + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb1D( PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator); + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb2D(PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator); + + +PxU32 getContactManagerConstraintDesc(const PxsContactManagerOutput& cmOutput, const PxsContactManager& cm, PxSolverConstraintDesc& desc); + +} + +} + +#endif //DY_CONTACTPREP_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp new file mode 100644 index 000000000..d8fea61a1 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp @@ -0,0 +1,1535 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "PxSceneDesc.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContact4.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DyDynamics.h" +#include "DyArticulationContactPrep.h" +#include "PxsContactManager.h" + +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DyContactPrepShared.h" + +using namespace Ps::aos; + +namespace physx +{ +namespace Dy +{ + +PxcCreateFinalizeSolverContactMethod4 createFinalizeMethods4[3] = +{ + createFinalizeSolverContacts4, + createFinalizeSolverContacts4Coulomb1D, + createFinalizeSolverContacts4Coulomb2D +}; + +inline bool ValidateVec4(const Vec4V v) +{ + PX_ALIGN(16, PxVec4 vF); + Ps::aos::V4StoreA(v, &vF.x); + return vF.isFinite(); +} + +static void setupFinalizeSolverConstraints4(PxSolverContactDesc* PX_RESTRICT descs, CorrelationBuffer& c, PxU8* PX_RESTRICT workspace, + const PxReal invDtF32, PxReal bounceThresholdF32, const PxReal solverOffsetSlopF32, + const Ps::aos::Vec4VArg invMassScale0, const Ps::aos::Vec4VArg invInertiaScale0, + const Ps::aos::Vec4VArg invMassScale1, const Ps::aos::Vec4VArg invInertiaScale1) +{ + + //OK, we have a workspace of pre-allocated space to store all 4 descs in. We now need to create the constraints in it + + const Vec4V ccdMaxSeparation = Ps::aos::V4LoadXYZW(descs[0].maxCCDSeparation, descs[1].maxCCDSeparation, descs[2].maxCCDSeparation, descs[3].maxCCDSeparation); + const Vec4V solverOffsetSlop = V4Load(solverOffsetSlopF32); + + const Vec4V zero = V4Zero(); + const BoolV bFalse = BFFFF(); + const FloatV fZero = FZero(); + + PxU8 flags[4] = { PxU8(descs[0].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[1].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[2].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[3].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0) }; + + bool hasMaxImpulse = descs[0].hasMaxImpulse || descs[1].hasMaxImpulse || descs[2].hasMaxImpulse || descs[3].hasMaxImpulse; + + //The block is dynamic if **any** of the constraints have a non-static body B. This allows us to batch static and non-static constraints but we only get a memory/perf + //saving if all 4 are static. This simplifies the constraint partitioning such that it only needs to care about separating contacts and 1D constraints (which it already does) + bool isDynamic = false; + bool hasKinematic = false; + for(PxU32 a = 0; a < 4; ++a) + { + isDynamic = isDynamic || (descs[a].bodyState1 == PxSolverContactDesc::eDYNAMIC_BODY); + hasKinematic = hasKinematic || descs[a].bodyState1 == PxSolverContactDesc::eKINEMATIC_BODY; + } + + const PxU32 constraintSize = isDynamic ? sizeof(SolverContactBatchPointDynamic4) : sizeof(SolverContactBatchPointBase4); + const PxU32 frictionSize = isDynamic ? sizeof(SolverContactFrictionDynamic4) : sizeof(SolverContactFrictionBase4); + + PxU8* PX_RESTRICT ptr = workspace; + + const Vec4V dom0 = invMassScale0; + const Vec4V dom1 = invMassScale1; + const Vec4V angDom0 = invInertiaScale0; + const Vec4V angDom1 = invInertiaScale1; + + const Vec4V maxPenBias = V4Max(V4LoadXYZW(descs[0].data0->penBiasClamp, descs[1].data0->penBiasClamp, + descs[2].data0->penBiasClamp, descs[3].data0->penBiasClamp), + V4LoadXYZW(descs[0].data1->penBiasClamp, descs[1].data1->penBiasClamp, + descs[2].data1->penBiasClamp, descs[3].data1->penBiasClamp)); + + const Vec4V restDistance = V4LoadXYZW(descs[0].restDistance, descs[1].restDistance, descs[2].restDistance, + descs[3].restDistance); + + + //load up velocities + Vec4V linVel00 = V4LoadA(&descs[0].data0->linearVelocity.x); + Vec4V linVel10 = V4LoadA(&descs[1].data0->linearVelocity.x); + Vec4V linVel20 = V4LoadA(&descs[2].data0->linearVelocity.x); + Vec4V linVel30 = V4LoadA(&descs[3].data0->linearVelocity.x); + + Vec4V linVel01 = V4LoadA(&descs[0].data1->linearVelocity.x); + Vec4V linVel11 = V4LoadA(&descs[1].data1->linearVelocity.x); + Vec4V linVel21 = V4LoadA(&descs[2].data1->linearVelocity.x); + Vec4V linVel31 = V4LoadA(&descs[3].data1->linearVelocity.x); + + Vec4V angVel00 = V4LoadA(&descs[0].data0->angularVelocity.x); + Vec4V angVel10 = V4LoadA(&descs[1].data0->angularVelocity.x); + Vec4V angVel20 = V4LoadA(&descs[2].data0->angularVelocity.x); + Vec4V angVel30 = V4LoadA(&descs[3].data0->angularVelocity.x); + + Vec4V angVel01 = V4LoadA(&descs[0].data1->angularVelocity.x); + Vec4V angVel11 = V4LoadA(&descs[1].data1->angularVelocity.x); + Vec4V angVel21 = V4LoadA(&descs[2].data1->angularVelocity.x); + Vec4V angVel31 = V4LoadA(&descs[3].data1->angularVelocity.x); + + Vec4V linVelT00, linVelT10, linVelT20; + Vec4V linVelT01, linVelT11, linVelT21; + Vec4V angVelT00, angVelT10, angVelT20; + Vec4V angVelT01, angVelT11, angVelT21; + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVelT00, linVelT10, linVelT20); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVelT01, linVelT11, linVelT21); + PX_TRANSPOSE_44_34(angVel00, angVel10, angVel20, angVel30, angVelT00, angVelT10, angVelT20); + PX_TRANSPOSE_44_34(angVel01, angVel11, angVel21, angVel31, angVelT01, angVelT11, angVelT21); + + const Vec4V vrelX = V4Sub(linVelT00, linVelT01); + const Vec4V vrelY = V4Sub(linVelT10, linVelT11); + const Vec4V vrelZ = V4Sub(linVelT20, linVelT21); + + //Load up masses and invInertia + + /*const Vec4V sqrtInvMass0 = V4Merge(FLoad(descs[0].data0->sqrtInvMass), FLoad(descs[1].data0->sqrtInvMass), FLoad(descs[2].data0->sqrtInvMass), + FLoad(descs[3].data0->sqrtInvMass)); + + const Vec4V sqrtInvMass1 = V4Merge(FLoad(descs[0].data1->sqrtInvMass), FLoad(descs[1].data1->sqrtInvMass), FLoad(descs[2].data1->sqrtInvMass), + FLoad(descs[3].data1->sqrtInvMass));*/ + + const Vec4V invMass0 = V4LoadXYZW(descs[0].data0->invMass, descs[1].data0->invMass, descs[2].data0->invMass, descs[3].data0->invMass); + const Vec4V invMass1 = V4LoadXYZW(descs[0].data1->invMass, descs[1].data1->invMass, descs[2].data1->invMass, descs[3].data1->invMass); + + const Vec4V invMass0D0 = V4Mul(dom0, invMass0); + const Vec4V invMass1D1 = V4Mul(dom1, invMass1); + + Vec4V invInertia00X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].data0->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia00Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].data0->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia00Z = Vec4V_From_Vec3V(V3LoadU(descs[0].data0->sqrtInvInertia.column2)); + + Vec4V invInertia10X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].data0->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia10Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].data0->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia10Z = Vec4V_From_Vec3V(V3LoadU(descs[1].data0->sqrtInvInertia.column2)); + + Vec4V invInertia20X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].data0->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia20Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].data0->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia20Z = Vec4V_From_Vec3V(V3LoadU(descs[2].data0->sqrtInvInertia.column2)); + + Vec4V invInertia30X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].data0->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia30Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].data0->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia30Z = Vec4V_From_Vec3V(V3LoadU(descs[3].data0->sqrtInvInertia.column2)); + + Vec4V invInertia01X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].data1->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia01Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].data1->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia01Z = Vec4V_From_Vec3V(V3LoadU(descs[0].data1->sqrtInvInertia.column2)); + + Vec4V invInertia11X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].data1->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia11Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].data1->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia11Z = Vec4V_From_Vec3V(V3LoadU(descs[1].data1->sqrtInvInertia.column2)); + + Vec4V invInertia21X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].data1->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia21Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].data1->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia21Z = Vec4V_From_Vec3V(V3LoadU(descs[2].data1->sqrtInvInertia.column2)); + + Vec4V invInertia31X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].data1->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia31Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].data1->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia31Z = Vec4V_From_Vec3V(V3LoadU(descs[3].data1->sqrtInvInertia.column2)); + + Vec4V invInertia0X0, invInertia0X1, invInertia0X2; + Vec4V invInertia0Y0, invInertia0Y1, invInertia0Y2; + Vec4V invInertia0Z0, invInertia0Z1, invInertia0Z2; + + Vec4V invInertia1X0, invInertia1X1, invInertia1X2; + Vec4V invInertia1Y0, invInertia1Y1, invInertia1Y2; + Vec4V invInertia1Z0, invInertia1Z1, invInertia1Z2; + + PX_TRANSPOSE_44_34(invInertia00X, invInertia10X, invInertia20X, invInertia30X, invInertia0X0, invInertia0Y0, invInertia0Z0); + PX_TRANSPOSE_44_34(invInertia00Y, invInertia10Y, invInertia20Y, invInertia30Y, invInertia0X1, invInertia0Y1, invInertia0Z1); + PX_TRANSPOSE_44_34(invInertia00Z, invInertia10Z, invInertia20Z, invInertia30Z, invInertia0X2, invInertia0Y2, invInertia0Z2); + + PX_TRANSPOSE_44_34(invInertia01X, invInertia11X, invInertia21X, invInertia31X, invInertia1X0, invInertia1Y0, invInertia1Z0); + PX_TRANSPOSE_44_34(invInertia01Y, invInertia11Y, invInertia21Y, invInertia31Y, invInertia1X1, invInertia1Y1, invInertia1Z1); + PX_TRANSPOSE_44_34(invInertia01Z, invInertia11Z, invInertia21Z, invInertia31Z, invInertia1X2, invInertia1Y2, invInertia1Z2); + + + const FloatV invDt = FLoad(invDtF32); + const FloatV p8 = FLoad(0.8f); + const Vec4V p84 = V4Splat(p8); + const Vec4V bounceThreshold = V4Splat(FLoad(bounceThresholdF32)); + + const FloatV invDtp8 = FMul(invDt, p8); + + const Vec3V bodyFrame00p = V3LoadU(descs[0].bodyFrame0.p); + const Vec3V bodyFrame01p = V3LoadU(descs[1].bodyFrame0.p); + const Vec3V bodyFrame02p = V3LoadU(descs[2].bodyFrame0.p); + const Vec3V bodyFrame03p = V3LoadU(descs[3].bodyFrame0.p); + + Vec4V bodyFrame00p4 = Vec4V_From_Vec3V(bodyFrame00p); + Vec4V bodyFrame01p4 = Vec4V_From_Vec3V(bodyFrame01p); + Vec4V bodyFrame02p4 = Vec4V_From_Vec3V(bodyFrame02p); + Vec4V bodyFrame03p4 = Vec4V_From_Vec3V(bodyFrame03p); + + Vec4V bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ; + PX_TRANSPOSE_44_34(bodyFrame00p4, bodyFrame01p4, bodyFrame02p4, bodyFrame03p4, bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ); + + + const Vec3V bodyFrame10p = V3LoadU(descs[0].bodyFrame1.p); + const Vec3V bodyFrame11p = V3LoadU(descs[1].bodyFrame1.p); + const Vec3V bodyFrame12p = V3LoadU(descs[2].bodyFrame1.p); + const Vec3V bodyFrame13p = V3LoadU(descs[3].bodyFrame1.p); + + Vec4V bodyFrame10p4 = Vec4V_From_Vec3V(bodyFrame10p); + Vec4V bodyFrame11p4 = Vec4V_From_Vec3V(bodyFrame11p); + Vec4V bodyFrame12p4 = Vec4V_From_Vec3V(bodyFrame12p); + Vec4V bodyFrame13p4 = Vec4V_From_Vec3V(bodyFrame13p); + + Vec4V bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ; + PX_TRANSPOSE_44_34(bodyFrame10p4, bodyFrame11p4, bodyFrame12p4, bodyFrame13p4, bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ); + + + const QuatV bodyFrame00q = QuatVLoadU(&descs[0].bodyFrame0.q.x); + const QuatV bodyFrame01q = QuatVLoadU(&descs[1].bodyFrame0.q.x); + const QuatV bodyFrame02q = QuatVLoadU(&descs[2].bodyFrame0.q.x); + const QuatV bodyFrame03q = QuatVLoadU(&descs[3].bodyFrame0.q.x); + + const QuatV bodyFrame10q = QuatVLoadU(&descs[0].bodyFrame1.q.x); + const QuatV bodyFrame11q = QuatVLoadU(&descs[1].bodyFrame1.q.x); + const QuatV bodyFrame12q = QuatVLoadU(&descs[2].bodyFrame1.q.x); + const QuatV bodyFrame13q = QuatVLoadU(&descs[3].bodyFrame1.q.x); + + PxU32 frictionPatchWritebackAddrIndex0 = 0; + PxU32 frictionPatchWritebackAddrIndex1 = 0; + PxU32 frictionPatchWritebackAddrIndex2 = 0; + PxU32 frictionPatchWritebackAddrIndex3 = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + PxU32 frictionIndex0 = 0, frictionIndex1 = 0, frictionIndex2 = 0, frictionIndex3 = 0; + //PxU32 contactIndex0 = 0, contactIndex1 = 0, contactIndex2 = 0, contactIndex3 = 0; + + + //OK, we iterate through all friction patch counts in the constraint patch, building up the constraint list etc. + + PxU32 maxPatches = PxMax(descs[0].numFrictionPatches, PxMax(descs[1].numFrictionPatches, PxMax(descs[2].numFrictionPatches, descs[3].numFrictionPatches))); + + const Vec4V p1 = V4Splat(FLoad(0.1f)); + const Vec4V orthoThreshold = V4Splat(FLoad(0.70710678f)); + + + PxU32 contact0 = 0, contact1 = 0, contact2 = 0, contact3 = 0; + PxU32 patch0 = 0, patch1 = 0, patch2 = 0, patch3 = 0; + + PxU8 flag = 0; + if(hasMaxImpulse) + flag |= SolverContactHeader4::eHAS_MAX_IMPULSE; + + for(PxU32 i=0;i= descs[0].numFrictionPatches; + const bool hasFinished1 = i >= descs[1].numFrictionPatches; + const bool hasFinished2 = i >= descs[2].numFrictionPatches; + const bool hasFinished3 = i >= descs[3].numFrictionPatches; + + + frictionIndex0 = hasFinished0 ? frictionIndex0 : descs[0].startFrictionPatchIndex + i; + frictionIndex1 = hasFinished1 ? frictionIndex1 : descs[1].startFrictionPatchIndex + i; + frictionIndex2 = hasFinished2 ? frictionIndex2 : descs[2].startFrictionPatchIndex + i; + frictionIndex3 = hasFinished3 ? frictionIndex3 : descs[3].startFrictionPatchIndex + i; + + PxU32 clampedContacts0 = hasFinished0 ? 0 : c.frictionPatchContactCounts[frictionIndex0]; + PxU32 clampedContacts1 = hasFinished1 ? 0 : c.frictionPatchContactCounts[frictionIndex1]; + PxU32 clampedContacts2 = hasFinished2 ? 0 : c.frictionPatchContactCounts[frictionIndex2]; + PxU32 clampedContacts3 = hasFinished3 ? 0 : c.frictionPatchContactCounts[frictionIndex3]; + + PxU32 firstPatch0 = c.correlationListHeads[frictionIndex0]; + PxU32 firstPatch1 = c.correlationListHeads[frictionIndex1]; + PxU32 firstPatch2 = c.correlationListHeads[frictionIndex2]; + PxU32 firstPatch3 = c.correlationListHeads[frictionIndex3]; + + const Gu::ContactPoint* contactBase0 = descs[0].contacts + c.contactPatches[firstPatch0].start; + const Gu::ContactPoint* contactBase1 = descs[1].contacts + c.contactPatches[firstPatch1].start; + const Gu::ContactPoint* contactBase2 = descs[2].contacts + c.contactPatches[firstPatch2].start; + const Gu::ContactPoint* contactBase3 = descs[3].contacts + c.contactPatches[firstPatch3].start; + + const Vec4V restitution = V4Neg(V4LoadXYZW(contactBase0->restitution, contactBase1->restitution, contactBase2->restitution, + contactBase3->restitution)); + + SolverContactHeader4* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeader4); + + + header->flags[0] = flags[0]; + header->flags[1] = flags[1]; + header->flags[2] = flags[2]; + header->flags[3] = flags[3]; + + header->flag = flag; + + PxU32 totalContacts = PxMax(clampedContacts0, PxMax(clampedContacts1, PxMax(clampedContacts2, clampedContacts3))); + + Vec4V* PX_RESTRICT appliedNormalForces = reinterpret_cast(ptr); + ptr += sizeof(Vec4V)*totalContacts; + + PxMemZero(appliedNormalForces, sizeof(Vec4V) * totalContacts); + + header->numNormalConstr = Ps::to8(totalContacts); + header->numNormalConstr0 = Ps::to8(clampedContacts0); + header->numNormalConstr1 = Ps::to8(clampedContacts1); + header->numNormalConstr2 = Ps::to8(clampedContacts2); + header->numNormalConstr3 = Ps::to8(clampedContacts3); + //header->sqrtInvMassA = sqrtInvMass0; + //header->sqrtInvMassB = sqrtInvMass1; + header->invMass0D0 = invMass0D0; + header->invMass1D1 = invMass1D1; + header->angDom0 = angDom0; + header->angDom1 = angDom1; + header->shapeInteraction[0] = descs[0].shapeInteraction; header->shapeInteraction[1] = descs[1].shapeInteraction; + header->shapeInteraction[2] = descs[2].shapeInteraction; header->shapeInteraction[3] = descs[3].shapeInteraction; + + Vec4V* maxImpulse = reinterpret_cast(ptr + constraintSize * totalContacts); + + header->restitution = restitution; + + Vec4V normal0 = V4LoadA(&contactBase0->normal.x); + Vec4V normal1 = V4LoadA(&contactBase1->normal.x); + Vec4V normal2 = V4LoadA(&contactBase2->normal.x); + Vec4V normal3 = V4LoadA(&contactBase3->normal.x); + + Vec4V normalX, normalY, normalZ; + PX_TRANSPOSE_44_34(normal0, normal1, normal2, normal3, normalX, normalY, normalZ); + + PX_ASSERT(ValidateVec4(normalX)); + PX_ASSERT(ValidateVec4(normalY)); + PX_ASSERT(ValidateVec4(normalZ)); + + header->normalX = normalX; + header->normalY = normalY; + header->normalZ = normalZ; + + const Vec4V norVel0 = V4MulAdd(normalZ, linVelT20, V4MulAdd(normalY, linVelT10, V4Mul(normalX, linVelT00))); + const Vec4V norVel1 = V4MulAdd(normalZ, linVelT21, V4MulAdd(normalY, linVelT11, V4Mul(normalX, linVelT01))); + const Vec4V relNorVel = V4Sub(norVel0, norVel1); + + //For all correlation heads - need to pull this out I think + + //OK, we have a counter for all our patches... + PxU32 finished = (PxU32(hasFinished0)) | + ((PxU32(hasFinished1)) << 1) | + ((PxU32(hasFinished2)) << 2) | + ((PxU32(hasFinished3)) << 3); + + CorrelationListIterator iter0(c, firstPatch0); + CorrelationListIterator iter1(c, firstPatch1); + CorrelationListIterator iter2(c, firstPatch2); + CorrelationListIterator iter3(c, firstPatch3); + + //PxU32 contact0, contact1, contact2, contact3; + //PxU32 patch0, patch1, patch2, patch3; + + if(!hasFinished0) + iter0.nextContact(patch0, contact0); + if(!hasFinished1) + iter1.nextContact(patch1, contact1); + if(!hasFinished2) + iter2.nextContact(patch2, contact2); + if(!hasFinished3) + iter3.nextContact(patch3, contact3); + + PxU8* p = ptr; + + PxU32 contactCount = 0; + PxU32 newFinished = + (PxU32(hasFinished0 || !iter0.hasNextContact())) | + ((PxU32(hasFinished1 || !iter1.hasNextContact())) << 1) | + ((PxU32(hasFinished2 || !iter2.hasNextContact())) << 2) | + ((PxU32(hasFinished3 || !iter3.hasNextContact())) << 3); + + while(finished != 0xf) + { + finished = newFinished; + ++contactCount; + Ps::prefetchLine(p, 384); + Ps::prefetchLine(p, 512); + Ps::prefetchLine(p, 640); + + SolverContactBatchPointBase4* PX_RESTRICT solverContact = reinterpret_cast(p); + p += constraintSize; + + const Gu::ContactPoint& con0 = descs[0].contacts[c.contactPatches[patch0].start + contact0]; + const Gu::ContactPoint& con1 = descs[1].contacts[c.contactPatches[patch1].start + contact1]; + const Gu::ContactPoint& con2 = descs[2].contacts[c.contactPatches[patch2].start + contact2]; + const Gu::ContactPoint& con3 = descs[3].contacts[c.contactPatches[patch3].start + contact3]; + + //Now we need to splice these 4 contacts into a single structure + + { + Vec4V point0 = V4LoadA(&con0.point.x); + Vec4V point1 = V4LoadA(&con1.point.x); + Vec4V point2 = V4LoadA(&con2.point.x); + Vec4V point3 = V4LoadA(&con3.point.x); + + Vec4V pointX, pointY, pointZ; + PX_TRANSPOSE_44_34(point0, point1, point2, point3, pointX, pointY, pointZ); + + PX_ASSERT(ValidateVec4(pointX)); + PX_ASSERT(ValidateVec4(pointY)); + PX_ASSERT(ValidateVec4(pointZ)); + + Vec4V cTargetVel0 = V4LoadA(&con0.targetVel.x); + Vec4V cTargetVel1 = V4LoadA(&con1.targetVel.x); + Vec4V cTargetVel2 = V4LoadA(&con2.targetVel.x); + Vec4V cTargetVel3 = V4LoadA(&con3.targetVel.x); + + Vec4V cTargetVelX, cTargetVelY, cTargetVelZ; + PX_TRANSPOSE_44_34(cTargetVel0, cTargetVel1, cTargetVel2, cTargetVel3, cTargetVelX, cTargetVelY, cTargetVelZ); + + const Vec4V separation = V4LoadXYZW(con0.separation, con1.separation, con2.separation, con3.separation); + + const Vec4V cTargetNorVel = V4MulAdd(cTargetVelX, normalX, V4MulAdd(cTargetVelY, normalY, V4Mul(cTargetVelZ, normalZ))); + + const Vec4V raX = V4Sub(pointX, bodyFrame0pX); + const Vec4V raY = V4Sub(pointY, bodyFrame0pY); + const Vec4V raZ = V4Sub(pointZ, bodyFrame0pZ); + + const Vec4V rbX = V4Sub(pointX, bodyFrame1pX); + const Vec4V rbY = V4Sub(pointY, bodyFrame1pY); + const Vec4V rbZ = V4Sub(pointZ, bodyFrame1pZ); + + /*raX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raX)), zero, raX); + raY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raY)), zero, raY); + raZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raZ)), zero, raZ); + + rbX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbX)), zero, rbX); + rbY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbY)), zero, rbY); + rbZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbZ)), zero, rbZ);*/ + + PX_ASSERT(ValidateVec4(raX)); + PX_ASSERT(ValidateVec4(raY)); + PX_ASSERT(ValidateVec4(raZ)); + + PX_ASSERT(ValidateVec4(rbX)); + PX_ASSERT(ValidateVec4(rbY)); + PX_ASSERT(ValidateVec4(rbZ)); + + + //raXn = cross(ra, normal) which = Vec3V( a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x); + + Vec4V raXnX = V4NegMulSub(raZ, normalY, V4Mul(raY, normalZ)); + Vec4V raXnY = V4NegMulSub(raX, normalZ, V4Mul(raZ, normalX)); + Vec4V raXnZ = V4NegMulSub(raY, normalX, V4Mul(raX, normalY)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + + PX_ASSERT(ValidateVec4(delAngVel0X)); + PX_ASSERT(ValidateVec4(delAngVel0Y)); + PX_ASSERT(ValidateVec4(delAngVel0Z)); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0X, delAngVel0X, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0Z, delAngVel0Z))); + const Vec4V dotRaXnAngVel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4Mul(raXnX, angVelT00))); + + Vec4V unitResponse = V4MulAdd(invMass0D0, angDom0, dotDelAngVel0); + Vec4V vrel = V4Add(relNorVel, dotRaXnAngVel0); + + + //The dynamic-only parts - need to if-statement these up. A branch here shouldn't cost us too much + if(isDynamic) + { + SolverContactBatchPointDynamic4* PX_RESTRICT dynamicContact = static_cast(solverContact); + Vec4V rbXnX = V4NegMulSub(rbZ, normalY, V4Mul(rbY, normalZ)); + Vec4V rbXnY = V4NegMulSub(rbX, normalZ, V4Mul(rbZ, normalX)); + Vec4V rbXnZ = V4NegMulSub(rbY, normalX, V4Mul(rbX, normalY)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + Vec4V delAngVel1X = V4Mul(invInertia1X0, rbXnX); + Vec4V delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + Vec4V delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + PX_ASSERT(ValidateVec4(delAngVel1X)); + PX_ASSERT(ValidateVec4(delAngVel1Y)); + PX_ASSERT(ValidateVec4(delAngVel1Z)); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1X, delAngVel1X, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1Z, delAngVel1Z))); + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + unitResponse = V4Add(unitResponse, resp1); + + vrel = V4Sub(vrel, dotRbXnAngVel1); + + //These are for dynamic-only contacts. + dynamicContact->rbXnX = delAngVel1X; + dynamicContact->rbXnY = delAngVel1Y; + dynamicContact->rbXnZ = delAngVel1Z; + + } + else if(hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, normalY, V4Mul(rbY, normalZ)); + const Vec4V rbXnY = V4NegMulSub(rbX, normalZ, V4Mul(rbZ, normalX)); + const Vec4V rbXnZ = V4NegMulSub(rbY, normalX, V4Mul(rbX, normalY)); + + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + + vrel = V4Sub(vrel, dotRbXnAngVel1); + } + + const Vec4V velMultiplier = V4Sel(V4IsGrtr(unitResponse, zero), V4Recip(unitResponse), zero); + + const Vec4V penetration = V4Sub(separation, restDistance); + const Vec4V penInvDtPt8 = V4Max(maxPenBias, V4Scale(penetration, invDtp8)); + Vec4V scaledBias = V4Mul(penInvDtPt8, velMultiplier); + + const Vec4V penetrationInvDt = V4Scale(penetration, invDt); + + const BoolV isGreater2 = BAnd(BAnd(V4IsGrtr(zero, restitution), V4IsGrtr(bounceThreshold, vrel)), + V4IsGrtr(V4Neg(vrel), penetrationInvDt)); + + const BoolV ccdSeparationCondition = V4IsGrtrOrEq(ccdMaxSeparation, penetration); + + scaledBias = V4Sel(BAnd(ccdSeparationCondition, isGreater2), zero, V4Neg(scaledBias)); + + const Vec4V targetVelocity = V4Sel(isGreater2, V4Mul(velMultiplier, V4Mul(vrel, restitution)), zero); + + //Vec4V biasedErr = V4Sel(isGreater2, targetVelocity, scaledBias); + Vec4V biasedErr = V4Add(targetVelocity, scaledBias); + + biasedErr = V4NegMulSub(V4Sub(vrel, cTargetNorVel), velMultiplier, biasedErr); + + //These values are present for static and dynamic contacts + solverContact->raXnX = delAngVel0X; + solverContact->raXnY = delAngVel0Y; + solverContact->raXnZ = delAngVel0Z; + solverContact->velMultiplier = velMultiplier; + solverContact->biasedErr = biasedErr; + + //solverContact->scaledBias = V4Max(zero, scaledBias); + solverContact->scaledBias = V4Sel(isGreater2, scaledBias, V4Max(zero, scaledBias)); + + if(hasMaxImpulse) + { + maxImpulse[contactCount-1] = V4Merge(FLoad(con0.maxImpulse), FLoad(con1.maxImpulse), FLoad(con2.maxImpulse), + FLoad(con3.maxImpulse)); + } + } + if(!(finished & 0x1)) + { + iter0.nextContact(patch0, contact0); + newFinished |= PxU32(!iter0.hasNextContact()); + } + + if(!(finished & 0x2)) + { + iter1.nextContact(patch1, contact1); + newFinished |= (PxU32(!iter1.hasNextContact()) << 1); + } + + if(!(finished & 0x4)) + { + iter2.nextContact(patch2, contact2); + newFinished |= (PxU32(!iter2.hasNextContact()) << 2); + } + + if(!(finished & 0x8)) + { + iter3.nextContact(patch3, contact3); + newFinished |= (PxU32(!iter3.hasNextContact()) << 3); + } + } + ptr = p; + if(hasMaxImpulse) + { + ptr += sizeof(Vec4V) * totalContacts; + } + + //OK...friction time :-) + + Vec4V maxImpulseScale = V4One(); + { + const Vec4V staticFriction = V4LoadXYZW(contactBase0->staticFriction, contactBase1->staticFriction, + contactBase2->staticFriction, contactBase3->staticFriction); + + const Vec4V dynamicFriction = V4LoadXYZW(contactBase0->dynamicFriction, contactBase1->dynamicFriction, + contactBase2->dynamicFriction, contactBase3->dynamicFriction); + + PX_ASSERT(totalContacts == contactCount); + header->dynamicFriction = dynamicFriction; + header->staticFriction = staticFriction; + + const FrictionPatch& frictionPatch0 = c.frictionPatches[frictionIndex0]; + const FrictionPatch& frictionPatch1 = c.frictionPatches[frictionIndex1]; + const FrictionPatch& frictionPatch2 = c.frictionPatches[frictionIndex2]; + const FrictionPatch& frictionPatch3 = c.frictionPatches[frictionIndex3]; + + PxU32 anchorCount0 = frictionPatch0.anchorCount; + PxU32 anchorCount1 = frictionPatch1.anchorCount; + PxU32 anchorCount2 = frictionPatch2.anchorCount; + PxU32 anchorCount3 = frictionPatch3.anchorCount; + + PxU32 clampedAnchorCount0 = hasFinished0 || (contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount0; + PxU32 clampedAnchorCount1 = hasFinished1 || (contactBase1->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount1; + PxU32 clampedAnchorCount2 = hasFinished2 || (contactBase2->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount2; + PxU32 clampedAnchorCount3 = hasFinished3 || (contactBase3->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount3; + + const PxU32 maxAnchorCount = PxMax(clampedAnchorCount0, PxMax(clampedAnchorCount1, PxMax(clampedAnchorCount2, clampedAnchorCount3))); + + //if(clampedAnchorCount0 != clampedAnchorCount1 || clampedAnchorCount0 != clampedAnchorCount2 || clampedAnchorCount0 != clampedAnchorCount3) + // Ps::debugBreak(); + + + //const bool haveFriction = maxAnchorCount != 0; + header->numFrictionConstr = Ps::to8(maxAnchorCount*2); + header->numFrictionConstr0 = Ps::to8(clampedAnchorCount0*2); + header->numFrictionConstr1 = Ps::to8(clampedAnchorCount1*2); + header->numFrictionConstr2 = Ps::to8(clampedAnchorCount2*2); + header->numFrictionConstr3 = Ps::to8(clampedAnchorCount3*2); + + //KS - TODO - extend this if needed + header->type = Ps::to8(isDynamic ? DY_SC_TYPE_BLOCK_RB_CONTACT : DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT); + + if(maxAnchorCount) + { + + //Allocate the shared friction data... + + SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(ptr); + ptr += sizeof(SolverFrictionSharedData4); + PX_UNUSED(fd); + + const BoolV cond =V4IsGrtr(orthoThreshold, V4Abs(normalX)); + + const Vec4V t0FallbackX = V4Sel(cond, zero, V4Neg(normalY)); + const Vec4V t0FallbackY = V4Sel(cond, V4Neg(normalZ), normalX); + const Vec4V t0FallbackZ = V4Sel(cond, normalY, zero); + + //const Vec4V dotNormalVrel = V4MulAdd(normalZ, vrelZ, V4MulAdd(normalY, vrelY, V4Mul(normalX, vrelX))); + const Vec4V vrelSubNorVelX = V4NegMulSub(normalX, relNorVel, vrelX); + const Vec4V vrelSubNorVelY = V4NegMulSub(normalY, relNorVel, vrelY); + const Vec4V vrelSubNorVelZ = V4NegMulSub(normalZ, relNorVel, vrelZ); + + const Vec4V lenSqvrelSubNorVelZ = V4MulAdd(vrelSubNorVelX, vrelSubNorVelX, V4MulAdd(vrelSubNorVelY, vrelSubNorVelY, V4Mul(vrelSubNorVelZ, vrelSubNorVelZ))); + + const BoolV bcon2 = V4IsGrtr(lenSqvrelSubNorVelZ, p1); + + Vec4V t0X = V4Sel(bcon2, vrelSubNorVelX, t0FallbackX); + Vec4V t0Y = V4Sel(bcon2, vrelSubNorVelY, t0FallbackY); + Vec4V t0Z = V4Sel(bcon2, vrelSubNorVelZ, t0FallbackZ); + + + //Now normalize this... + const Vec4V recipLen = V4Rsqrt(V4MulAdd(t0Z, t0Z, V4MulAdd(t0Y, t0Y, V4Mul(t0X, t0X)))); + + t0X = V4Mul(t0X, recipLen); + t0Y = V4Mul(t0Y, recipLen); + t0Z = V4Mul(t0Z, recipLen); + + Vec4V t1X = V4NegMulSub(normalZ, t0Y, V4Mul(normalY, t0Z)); + Vec4V t1Y = V4NegMulSub(normalX, t0Z, V4Mul(normalZ, t0X)); + Vec4V t1Z = V4NegMulSub(normalY, t0X, V4Mul(normalX, t0Y)); + + PX_ASSERT((uintptr_t(descs[0].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[1].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[2].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[3].frictionPtr) & 0xF) == 0); + + + PxU8* PX_RESTRICT writeback0 = descs[0].frictionPtr + frictionPatchWritebackAddrIndex0*sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback1 = descs[1].frictionPtr + frictionPatchWritebackAddrIndex1*sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback2 = descs[2].frictionPtr + frictionPatchWritebackAddrIndex2*sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback3 = descs[3].frictionPtr + frictionPatchWritebackAddrIndex3*sizeof(FrictionPatch); + + PxU32 index0 = 0, index1 = 0, index2 = 0, index3 = 0; + + fd->broken = bFalse; + fd->frictionBrokenWritebackByte[0] = writeback0; + fd->frictionBrokenWritebackByte[1] = writeback1; + fd->frictionBrokenWritebackByte[2] = writeback2; + fd->frictionBrokenWritebackByte[3] = writeback3; + + + fd->normalX[0] = t0X; + fd->normalY[0] = t0Y; + fd->normalZ[0] = t0Z; + + fd->normalX[1] = t1X; + fd->normalY[1] = t1Y; + fd->normalZ[1] = t1Z; + + Vec4V* PX_RESTRICT appliedForces = reinterpret_cast(ptr); + ptr += sizeof(Vec4V)*header->numFrictionConstr; + + PxMemZero(appliedForces, sizeof(Vec4V) * header->numFrictionConstr); + + for(PxU32 j = 0; j < maxAnchorCount; j++) + { + Ps::prefetchLine(ptr, 384); + Ps::prefetchLine(ptr, 512); + Ps::prefetchLine(ptr, 640); + SolverContactFrictionBase4* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionSize; + SolverContactFrictionBase4* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionSize; + + index0 = j < clampedAnchorCount0 ? j : index0; + index1 = j < clampedAnchorCount1 ? j : index1; + index2 = j < clampedAnchorCount2 ? j : index2; + index3 = j < clampedAnchorCount3 ? j : index3; + + if(j >= clampedAnchorCount0) + maxImpulseScale = V4SetX(maxImpulseScale, fZero); + if(j >= clampedAnchorCount1) + maxImpulseScale = V4SetY(maxImpulseScale, fZero); + if(j >= clampedAnchorCount2) + maxImpulseScale = V4SetZ(maxImpulseScale, fZero); + if(j >= clampedAnchorCount3) + maxImpulseScale = V4SetW(maxImpulseScale, fZero); + + t0X = V4Mul(maxImpulseScale, t0X); + t0Y = V4Mul(maxImpulseScale, t0Y); + t0Z = V4Mul(maxImpulseScale, t0Z); + + t1X = V4Mul(maxImpulseScale, t1X); + t1Y = V4Mul(maxImpulseScale, t1Y); + t1Z = V4Mul(maxImpulseScale, t1Z); + + + Vec3V body0Anchor0 = V3LoadU(frictionPatch0.body0Anchors[index0]); + Vec3V body0Anchor1 = V3LoadU(frictionPatch1.body0Anchors[index1]); + Vec3V body0Anchor2 = V3LoadU(frictionPatch2.body0Anchors[index2]); + Vec3V body0Anchor3 = V3LoadU(frictionPatch3.body0Anchors[index3]); + + Vec4V ra0 = Vec4V_From_Vec3V(QuatRotate(bodyFrame00q, body0Anchor0)); + Vec4V ra1 = Vec4V_From_Vec3V(QuatRotate(bodyFrame01q, body0Anchor1)); + Vec4V ra2 = Vec4V_From_Vec3V(QuatRotate(bodyFrame02q, body0Anchor2)); + Vec4V ra3 = Vec4V_From_Vec3V(QuatRotate(bodyFrame03q, body0Anchor3)); + + Vec4V raX, raY, raZ; + PX_TRANSPOSE_44_34(ra0, ra1, ra2, ra3, raX, raY, raZ); + + /*raX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raX)), zero, raX); + raY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raY)), zero, raY); + raZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raZ)), zero, raZ);*/ + + const Vec4V raWorldX = V4Add(raX, bodyFrame0pX); + const Vec4V raWorldY = V4Add(raY, bodyFrame0pY); + const Vec4V raWorldZ = V4Add(raZ, bodyFrame0pZ); + + Vec3V body1Anchor0 = V3LoadU(frictionPatch0.body1Anchors[index0]); + Vec3V body1Anchor1 = V3LoadU(frictionPatch1.body1Anchors[index1]); + Vec3V body1Anchor2 = V3LoadU(frictionPatch2.body1Anchors[index2]); + Vec3V body1Anchor3 = V3LoadU(frictionPatch3.body1Anchors[index3]); + + Vec4V rb0 = Vec4V_From_Vec3V(QuatRotate(bodyFrame10q, body1Anchor0)); + Vec4V rb1 = Vec4V_From_Vec3V(QuatRotate(bodyFrame11q, body1Anchor1)); + Vec4V rb2 = Vec4V_From_Vec3V(QuatRotate(bodyFrame12q, body1Anchor2)); + Vec4V rb3 = Vec4V_From_Vec3V(QuatRotate(bodyFrame13q, body1Anchor3)); + + Vec4V rbX, rbY, rbZ; + PX_TRANSPOSE_44_34(rb0, rb1, rb2, rb3, rbX, rbY, rbZ); + + /*rbX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbX)), zero, rbX); + rbY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbY)), zero, rbY); + rbZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbZ)), zero, rbZ);*/ + + const Vec4V rbWorldX = V4Add(rbX, bodyFrame1pX); + const Vec4V rbWorldY = V4Add(rbY, bodyFrame1pY); + const Vec4V rbWorldZ = V4Add(rbZ, bodyFrame1pZ); + + Vec4V errorX = V4Sub(raWorldX, rbWorldX); + Vec4V errorY = V4Sub(raWorldY, rbWorldY); + Vec4V errorZ = V4Sub(raWorldZ, rbWorldZ); + + /*errorX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorX)), zero, errorX); + errorY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorY)), zero, errorY); + errorZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorZ)), zero, errorZ);*/ + + //KS - todo - get this working with per-point friction + PxU32 contactIndex0 = c.contactID[frictionIndex0][index0]; + PxU32 contactIndex1 = c.contactID[frictionIndex1][index1]; + PxU32 contactIndex2 = c.contactID[frictionIndex2][index2]; + PxU32 contactIndex3 = c.contactID[frictionIndex3][index3]; + + //Ensure that the ocntact indices are valid + PX_ASSERT(contactIndex0 == 0xffff || contactIndex0 < descs[0].numContacts); + PX_ASSERT(contactIndex1 == 0xffff || contactIndex1 < descs[1].numContacts); + PX_ASSERT(contactIndex2 == 0xffff || contactIndex2 < descs[2].numContacts); + PX_ASSERT(contactIndex3 == 0xffff || contactIndex3 < descs[3].numContacts); + + Vec4V targetVel0 = V4LoadA(contactIndex0 == 0xFFFF ? &contactBase0->targetVel.x : &descs[0].contacts[contactIndex0].targetVel.x); + Vec4V targetVel1 = V4LoadA(contactIndex1 == 0xFFFF ? &contactBase0->targetVel.x : &descs[1].contacts[contactIndex1].targetVel.x); + Vec4V targetVel2 = V4LoadA(contactIndex2 == 0xFFFF ? &contactBase0->targetVel.x : &descs[2].contacts[contactIndex2].targetVel.x); + Vec4V targetVel3 = V4LoadA(contactIndex3 == 0xFFFF ? &contactBase0->targetVel.x : &descs[3].contacts[contactIndex3].targetVel.x); + + Vec4V targetVelX, targetVelY, targetVelZ; + PX_TRANSPOSE_44_34(targetVel0, targetVel1, targetVel2, targetVel3, targetVelX, targetVelY, targetVelZ); + + + { + Vec4V raXnX = V4NegMulSub(raZ, t0Y, V4Mul(raY, t0Z)); + Vec4V raXnY = V4NegMulSub(raX, t0Z, V4Mul(raZ, t0X)); + Vec4V raXnZ = V4NegMulSub(raY, t0X, V4Mul(raX, t0Y)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + + Vec4V resp = V4MulAdd(dotDelAngVel0, angDom0, invMass0D0); + + const Vec4V tVel0 = V4MulAdd(t0Z, linVelT20, V4MulAdd(t0Y, linVelT10, V4Mul(t0X, linVelT00))); + Vec4V vrel = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4MulAdd(raXnX, angVelT00, tVel0))); + + if(isDynamic) + { + SolverContactFrictionDynamic4* PX_RESTRICT dynamicF0 = static_cast(f0); + + Vec4V rbXnX = V4NegMulSub(rbZ, t0Y, V4Mul(rbY, t0Z)); + Vec4V rbXnY = V4NegMulSub(rbX, t0Z, V4Mul(rbZ, t0X)); + Vec4V rbXnZ = V4NegMulSub(rbY, t0X, V4Mul(rbX, t0Y)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + Vec4V delAngVel1X = V4Mul(invInertia1X0, rbXnX); + Vec4V delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + Vec4V delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + resp = V4Add(resp, resp1); + + dynamicF0->rbXnX = delAngVel1X; + dynamicF0->rbXnY = delAngVel1Y; + dynamicF0->rbXnZ = delAngVel1Z; + + const Vec4V tVel1 = V4MulAdd(t0Z, linVelT21, V4MulAdd(t0Y, linVelT11, V4Mul(t0X, linVelT01))); + const Vec4V vel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + + vrel = V4Sub(vrel, vel1); + } + else if(hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, t0Y, V4Mul(rbY, t0Z)); + const Vec4V rbXnY = V4NegMulSub(rbX, t0Z, V4Mul(rbZ, t0X)); + const Vec4V rbXnZ = V4NegMulSub(rbY, t0X, V4Mul(rbX, t0Y)); + + const Vec4V tVel1 = V4MulAdd(t0Z, linVelT21, V4MulAdd(t0Y, linVelT11, V4Mul(t0X, linVelT01))); + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + + vrel = V4Sub(vrel, dotRbXnAngVel1); + } + + + const Vec4V velMultiplier = V4Mul(maxImpulseScale, V4Sel(V4IsGrtr(resp, zero), V4Div(p84, resp), zero)); + + Vec4V bias = V4Scale(V4MulAdd(t0Z, errorZ, V4MulAdd(t0Y, errorY, V4Mul(t0X, errorX))), invDt); + + Vec4V targetVel = V4MulAdd(t0Z, targetVelZ,V4MulAdd(t0Y, targetVelY, V4Mul(t0X, targetVelX))); + targetVel = V4Sub(targetVel, vrel); + f0->targetVelocity = V4Neg(V4Mul(targetVel, velMultiplier)); + bias = V4Sub(bias, targetVel); + + f0->raXnX = delAngVel0X; + f0->raXnY = delAngVel0Y; + f0->raXnZ = delAngVel0Z; + f0->scaledBias = V4Mul(bias, velMultiplier); + f0->velMultiplier = velMultiplier; + } + + { + Vec4V raXnX = V4NegMulSub(raZ, t1Y, V4Mul(raY, t1Z)); + Vec4V raXnY = V4NegMulSub(raX, t1Z, V4Mul(raZ, t1X)); + Vec4V raXnZ = V4NegMulSub(raY, t1X, V4Mul(raX, t1Y)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + + Vec4V resp = V4MulAdd(dotDelAngVel0, angDom0, invMass0D0); + + const Vec4V tVel0 = V4MulAdd(t1Z, linVelT20, V4MulAdd(t1Y, linVelT10, V4Mul(t1X, linVelT00))); + Vec4V vrel = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4MulAdd(raXnX, angVelT00, tVel0))); + + if(isDynamic) + { + SolverContactFrictionDynamic4* PX_RESTRICT dynamicF1 = static_cast(f1); + + Vec4V rbXnX = V4NegMulSub(rbZ, t1Y, V4Mul(rbY, t1Z)); + Vec4V rbXnY = V4NegMulSub(rbX, t1Z, V4Mul(rbZ, t1X)); + Vec4V rbXnZ = V4NegMulSub(rbY, t1X, V4Mul(rbX, t1Y)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + Vec4V delAngVel1X = V4Mul(invInertia1X0, rbXnX); + Vec4V delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + Vec4V delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + resp = V4Add(resp, resp1); + + dynamicF1->rbXnX = delAngVel1X; + dynamicF1->rbXnY = delAngVel1Y; + dynamicF1->rbXnZ = delAngVel1Z; + + const Vec4V tVel1 = V4MulAdd(t1Z, linVelT21, V4MulAdd(t1Y, linVelT11, V4Mul(t1X, linVelT01))); + const Vec4V vel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + + vrel = V4Sub(vrel, vel1); + + } + else if(hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, t1Y, V4Mul(rbY, t1Z)); + const Vec4V rbXnY = V4NegMulSub(rbX, t1Z, V4Mul(rbZ, t1X)); + const Vec4V rbXnZ = V4NegMulSub(rbY, t1X, V4Mul(rbX, t1Y)); + + const Vec4V tVel1 = V4MulAdd(t1Z, linVelT21, V4MulAdd(t1Y, linVelT11, V4Mul(t1X, linVelT01))); + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + + vrel = V4Sub(vrel, dotRbXnAngVel1); + } + + + const Vec4V velMultiplier = V4Mul(maxImpulseScale, V4Sel(V4IsGrtr(resp, zero), V4Div(p84, resp), zero)); + + Vec4V bias = V4Scale(V4MulAdd(t1Z, errorZ, V4MulAdd(t1Y, errorY, V4Mul(t1X, errorX))), invDt); + + Vec4V targetVel = V4MulAdd(t1Z, targetVelZ,V4MulAdd(t1Y, targetVelY, V4Mul(t1X, targetVelX))); + targetVel = V4Sub(targetVel, vrel); + f1->targetVelocity = V4Neg(V4Mul(targetVel, velMultiplier)); + bias = V4Sub(bias, targetVel); + f1->raXnX = delAngVel0X; + f1->raXnY = delAngVel0Y; + f1->raXnZ = delAngVel0Z; + f1->scaledBias = V4Mul(bias, velMultiplier); + f1->velMultiplier = velMultiplier; + } + } + + frictionPatchWritebackAddrIndex0++; + frictionPatchWritebackAddrIndex1++; + frictionPatchWritebackAddrIndex2++; + frictionPatchWritebackAddrIndex3++; + } + } + } +} + + + +PX_FORCE_INLINE void computeBlockStreamFrictionByteSizes(const CorrelationBuffer& c, + PxU32& _frictionPatchByteSize, PxU32& _numFrictionPatches, + PxU32 frictionPatchStartIndex, PxU32 frictionPatchEndIndex) +{ + // PT: use local vars to remove LHS + PxU32 numFrictionPatches = 0; + + for(PxU32 i = frictionPatchStartIndex; i < frictionPatchEndIndex; i++) + { + //Friction patches. + if(c.correlationListHeads[i] != CorrelationBuffer::LIST_END) + numFrictionPatches++; + } + PxU32 frictionPatchByteSize = numFrictionPatches*sizeof(FrictionPatch); + + _numFrictionPatches = numFrictionPatches; + + //16-byte alignment. + _frictionPatchByteSize = ((frictionPatchByteSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_frictionPatchByteSize & 0x0f)); +} + +static bool reserveFrictionBlockStreams(const CorrelationBuffer& c, PxConstraintAllocator& constraintAllocator, PxU32 frictionPatchStartIndex, PxU32 frictionPatchEndIndex, + FrictionPatch*& _frictionPatches, + PxU32& numFrictionPatches) +{ + + //From frictionPatchStream we just need to reserve a single buffer. + PxU32 frictionPatchByteSize = 0; + //Compute the sizes of all the buffers. + + computeBlockStreamFrictionByteSizes(c, frictionPatchByteSize, numFrictionPatches, frictionPatchStartIndex, frictionPatchEndIndex); + + FrictionPatch* frictionPatches = NULL; + //If the constraint block reservation didn't fail then reserve the friction buffer too. + if(frictionPatchByteSize > 0) + { + frictionPatches = reinterpret_cast(constraintAllocator.reserveFrictionData(frictionPatchByteSize)); + + if(0==frictionPatches || (reinterpret_cast(-1))==frictionPatches) + { + if(0==frictionPatches) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of friction data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + frictionPatches=NULL; + } + } + } + + _frictionPatches = frictionPatches; + + //Return true if neither of the two block reservations failed. + return (0==frictionPatchByteSize || frictionPatches); +} + +//The persistent friction patch correlation/allocation will already have happenned as this is per-pair. +//This function just computes the size of the combined solve data. +void computeBlockStreamByteSizes4(PxSolverContactDesc* descs, + PxU32& _solverConstraintByteSize, PxU32* _axisConstraintCount, + const CorrelationBuffer& c) +{ + PX_ASSERT(0 == _solverConstraintByteSize); + + PxU32 maxPatches = 0; + PxU32 maxFrictionPatches = 0; + PxU32 maxContactCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxU32 maxFrictionCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxMemZero(maxContactCount, sizeof(maxContactCount)); + PxMemZero(maxFrictionCount, sizeof(maxFrictionCount)); + bool hasMaxImpulse = false; + + for(PxU32 a = 0; a < 4; ++a) + { + PxU32 axisConstraintCount = 0; + hasMaxImpulse = hasMaxImpulse || descs[a].hasMaxImpulse; + for(PxU32 i = 0; i < descs[a].numFrictionPatches; i++) + { + PxU32 ind = i + descs[a].startFrictionPatchIndex; + + const FrictionPatch& frictionPatch = c.frictionPatches[ind]; + + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0 + && frictionPatch.anchorCount != 0; + //Solver constraint data. + if(c.frictionPatchContactCounts[ind]!=0) + { + maxContactCount[i] = PxMax(c.frictionPatchContactCounts[ind], maxContactCount[i]); + axisConstraintCount += c.frictionPatchContactCounts[ind]; + + if(haveFriction) + { + const PxU32 fricCount = PxU32(c.frictionPatches[ind].anchorCount) * 2; + maxFrictionCount[i] = PxMax(fricCount, maxFrictionCount[i]); + axisConstraintCount += fricCount; + } + } + } + maxPatches = PxMax(descs[a].numFrictionPatches, maxPatches); + _axisConstraintCount[a] = axisConstraintCount; + } + + for(PxU32 a = 0; a < maxPatches; ++a) + { + if(maxFrictionCount[a] > 0) + maxFrictionPatches++; + } + + + PxU32 totalContacts = 0, totalFriction = 0; + for(PxU32 a = 0; a < maxPatches; ++a) + { + totalContacts += maxContactCount[a]; + totalFriction += maxFrictionCount[a]; + } + + //OK, we have a given number of friction patches, contact points and friction constraints so we can calculate how much memory we need + + //Body 2 is considered static if it is either *not dynamic* or *kinematic* + + bool hasDynamicBody = false; + for(PxU32 a = 0; a < 4; ++a) + { + hasDynamicBody = hasDynamicBody || ((descs[a].bodyState1 == PxSolverContactDesc::eDYNAMIC_BODY)); + } + + + const bool isStatic = !hasDynamicBody; + + const PxU32 headerSize = sizeof(SolverContactHeader4) * maxPatches + sizeof(SolverFrictionSharedData4) * maxFrictionPatches; + PxU32 constraintSize = isStatic ? (sizeof(SolverContactBatchPointBase4) * totalContacts) + ( sizeof(SolverContactFrictionBase4) * totalFriction) : + (sizeof(SolverContactBatchPointDynamic4) * totalContacts) + (sizeof(SolverContactFrictionDynamic4) * totalFriction); + + //Space for the appliedForce buffer + constraintSize += sizeof(Vec4V)*(totalContacts+totalFriction); + + //If we have max impulse, reserve a buffer for it + if(hasMaxImpulse) + constraintSize += sizeof(Ps::aos::Vec4V) * totalContacts; + + _solverConstraintByteSize = ((constraintSize + headerSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); +} + +static SolverConstraintPrepState::Enum reserveBlockStreams4(PxSolverContactDesc* descs, Dy::CorrelationBuffer& c, + PxU8*& solverConstraint, PxU32* axisConstraintCount, + PxU32& solverConstraintByteSize, + PxConstraintAllocator& constraintAllocator) +{ + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(0 == solverConstraintByteSize); + + //Compute the sizes of all the buffers. + computeBlockStreamByteSizes4(descs, + solverConstraintByteSize, axisConstraintCount, + c); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if(constraintBlockByteSize > 0) + { + if((constraintBlockByteSize + 16u) > 16384) + return SolverConstraintPrepState::eUNBATCHABLE; + + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if(0==constraintBlock || (reinterpret_cast(-1))==constraintBlock) + { + if(0==constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock=NULL; + } + } + } + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if(0==constraintBlockByteSize || constraintBlock) + { + if(solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0==(uintptr_t(solverConstraint) & 0x0f)); + } + } + + return ((0==constraintBlockByteSize || constraintBlock)) ? SolverConstraintPrepState::eSUCCESS : SolverConstraintPrepState::eOUT_OF_MEMORY; +} + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4( + Dy::CorrelationBuffer& c, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + PX_ALIGN(16, PxReal invMassScale0[4]); + PX_ALIGN(16, PxReal invMassScale1[4]); + PX_ALIGN(16, PxReal invInertiaScale0[4]); + PX_ALIGN(16, PxReal invInertiaScale1[4]); + + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + for (PxU32 a = 0; a < 4; ++a) + { + PxSolverContactDesc& blockDesc = blockDescs[a]; + + invMassScale0[a] = blockDesc.mInvMassScales.linear0; + invMassScale1[a] = blockDesc.mInvMassScales.linear1; + invInertiaScale0[a] = blockDesc.mInvMassScales.angular0; + invInertiaScale1[a] = blockDesc.mInvMassScales.angular1; + + blockDesc.startFrictionPatchIndex = c.frictionPatchCount; + if (!(blockDesc.disableStrongFriction)) + { + bool valid = getFrictionPatches(c, blockDesc.frictionPtr, blockDesc.frictionCount, + blockDesc.bodyFrame0, blockDesc.bodyFrame1, correlationDistance); + if (!valid) + return SolverConstraintPrepState::eUNBATCHABLE; + } + //Create the contact patches + blockDesc.startContactPatchIndex = c.contactPatchCount; + if (!createContactPatches(c, blockDesc.contacts, blockDesc.numContacts, PXC_SAME_NORMAL)) + return SolverConstraintPrepState::eUNBATCHABLE; + blockDesc.numContactPatches = PxU16(c.contactPatchCount - blockDesc.startContactPatchIndex); + + bool overflow = correlatePatches(c, blockDesc.contacts, blockDesc.bodyFrame0, blockDesc.bodyFrame1, PXC_SAME_NORMAL, + blockDesc.startContactPatchIndex, blockDesc.startFrictionPatchIndex); + + if (overflow) + return SolverConstraintPrepState::eUNBATCHABLE; + + growPatches(c, blockDesc.contacts, blockDesc.bodyFrame0, blockDesc.bodyFrame1, correlationDistance, blockDesc.startFrictionPatchIndex, + frictionOffsetThreshold + blockDescs[a].restDistance); + + //Remove the empty friction patches - do we actually need to do this? + for (PxU32 p = c.frictionPatchCount; p > blockDesc.startFrictionPatchIndex; --p) + { + if (c.correlationListHeads[p - 1] == 0xffff) + { + //We have an empty patch...need to bin this one... + for (PxU32 p2 = p; p2 < c.frictionPatchCount; ++p2) + { + c.correlationListHeads[p2 - 1] = c.correlationListHeads[p2]; + c.frictionPatchContactCounts[p2 - 1] = c.frictionPatchContactCounts[p2]; + } + c.frictionPatchCount--; + } + } + + PxU32 numFricPatches = c.frictionPatchCount - blockDesc.startFrictionPatchIndex; + blockDesc.numFrictionPatches = numFricPatches; + } + + FrictionPatch* frictionPatchArray[4]; + PxU32 frictionPatchCounts[4]; + + for (PxU32 a = 0; a < 4; ++a) + { + PxSolverContactDesc& blockDesc = blockDescs[a]; + + const bool successfulReserve = reserveFrictionBlockStreams(c, constraintAllocator, blockDesc.startFrictionPatchIndex, blockDesc.numFrictionPatches + blockDesc.startFrictionPatchIndex, + frictionPatchArray[a], + frictionPatchCounts[a]); + + //KS - TODO - how can we recover if we failed to allocate this memory? + if (!successfulReserve) + { + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + } + //At this point, all the friction data has been calculated, the correlation has been done. Provided this was all successful, + //we are ready to create the batched constraints + + PxU8* solverConstraint = NULL; + PxU32 solverConstraintByteSize = 0; + + + + { + PxU32 axisConstraintCount[4]; + SolverConstraintPrepState::Enum state = reserveBlockStreams4(blockDescs, c, + solverConstraint, axisConstraintCount, + solverConstraintByteSize, + constraintAllocator); + + if (state != SolverConstraintPrepState::eSUCCESS) + return state; + + + for (PxU32 a = 0; a < 4; ++a) + { + + FrictionPatch* frictionPatches = frictionPatchArray[a]; + + PxSolverContactDesc& blockDesc = blockDescs[a]; + PxSolverConstraintDesc& desc = *blockDesc.desc; + blockDesc.frictionPtr = reinterpret_cast(frictionPatches); + blockDesc.frictionCount = Ps::to8(frictionPatchCounts[a]); + + //Initialise friction buffer. + if (frictionPatches) + { + // PT: TODO: revisit this... not very satisfying + //const PxU32 maxSize = numFrictionPatches*sizeof(FrictionPatch); + Ps::prefetchLine(frictionPatches); + Ps::prefetchLine(frictionPatches, 128); + Ps::prefetchLine(frictionPatches, 256); + + for (PxU32 i = 0; i(solverConstraint + solverConstraintByteSize)) = 0; + } + return SolverConstraintPrepState::eSUCCESS; +} + + +//This returns 1 of 3 states: success, unbatchable or out-of-memory. If the constraint is unbatchable, we must fall back on 4 separate constraint +//prep calls +SolverConstraintPrepState::Enum createFinalizeSolverContacts4( + PxsContactManagerOutput** cmOutputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + + for (PxU32 a = 0; a < 4; ++a) + { + blockDescs[a].desc->constraintLengthOver16 = 0; + } + + PX_ASSERT(cmOutputs[0]->nbContacts && cmOutputs[1]->nbContacts && cmOutputs[2]->nbContacts && cmOutputs[3]->nbContacts); + + + Gu::ContactBuffer& buffer = threadContext.mContactBuffer; + + buffer.count = 0; + + //PxTransform idt = PxTransform(PxIdentity); + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + + for (PxU32 a = 0; a < 4; ++a) + { + PxSolverContactDesc& blockDesc = blockDescs[a]; + PxSolverConstraintDesc& desc = *blockDesc.desc; + + //blockDesc.startContactIndex = buffer.count; + blockDesc.contacts = buffer.contacts + buffer.count; + + Ps::prefetchLine(desc.bodyA); + Ps::prefetchLine(desc.bodyB); + + + if ((buffer.count + cmOutputs[a]->nbContacts) > 64) + { + return SolverConstraintPrepState::eUNBATCHABLE; + } + + bool hasMaxImpulse = false; + bool hasTargetVelocity = false; + + //OK...do the correlation here as well... + Ps::prefetchLine(blockDescs[a].frictionPtr); + Ps::prefetchLine(blockDescs[a].frictionPtr, 64); + Ps::prefetchLine(blockDescs[a].frictionPtr, 128); + + if (a < 3) + { + Ps::prefetchLine(cmOutputs[a]->contactPatches); + Ps::prefetchLine(cmOutputs[a]->contactPoints); + } + + PxReal invMassScale0, invMassScale1, invInertiaScale0, invInertiaScale1; + + const PxReal defaultMaxImpulse = PxMin(blockDesc.data0->maxContactImpulse, blockDesc.data1->maxContactImpulse); + + PxU32 contactCount = extractContacts(buffer, *cmOutputs[a], hasMaxImpulse, hasTargetVelocity, invMassScale0, invMassScale1, + invInertiaScale0, invInertiaScale1, defaultMaxImpulse); + + if (contactCount == 0) + return SolverConstraintPrepState::eUNBATCHABLE; + + blockDesc.numContacts = contactCount; + blockDesc.hasMaxImpulse = hasMaxImpulse; + blockDesc.disableStrongFriction = blockDesc.disableStrongFriction || hasTargetVelocity; + + blockDesc.mInvMassScales.linear0 *= invMassScale0; + blockDesc.mInvMassScales.linear1 *= invMassScale1; + blockDesc.mInvMassScales.angular0 *= invInertiaScale0; + blockDesc.mInvMassScales.angular1 *= invInertiaScale1; + + //blockDesc.frictionPtr = &blockDescs[a].frictionPtr; + //blockDesc.frictionCount = blockDescs[a].frictionCount; + + } + return createFinalizeSolverContacts4(c, blockDescs, + invDtF32, bounceThresholdF32, frictionOffsetThreshold, + correlationDistance, solverOffsetSlop, constraintAllocator); +} + + + + +} + +} + + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp new file mode 100644 index 000000000..0965f438c --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp @@ -0,0 +1,1030 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +//#include "PxvGeometry.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DySolverConstraintDesc.h" +#include "DySolverBody.h" +#include "DySolverContact4.h" +#include "DySolverContactPF4.h" + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DySolverExt.h" +#include "DyArticulationContactPrep.h" +#include "DyContactPrepShared.h" +#include "PsFoundation.h" + +using namespace physx::Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ +namespace Dy +{ + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb( + PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + PxFrictionType::Enum frictionType); + +static bool setupFinalizeSolverConstraintsCoulomb4(PxSolverContactDesc* PX_RESTRICT descs, PxU8* PX_RESTRICT workspace, + const PxReal invDtF32, PxReal bounceThresholdF32, PxReal solverOffsetSlopF32, CorrelationBuffer& c, const PxU32 numFrictionPerPoint, + const PxU32 numContactPoints4, const PxU32 /*solverConstraintByteSize*/, + const Ps::aos::Vec4VArg invMassScale0, const Ps::aos::Vec4VArg invInertiaScale0, + const Ps::aos::Vec4VArg invMassScale1, const Ps::aos::Vec4VArg invInertiaScale1) +{ + //KS - final step. Create the constraints in the place we pre-allocated... + + const Vec4V ccdMaxSeparation = Ps::aos::V4LoadXYZW(descs[0].maxCCDSeparation, descs[1].maxCCDSeparation, descs[2].maxCCDSeparation, descs[3].maxCCDSeparation); + const Vec4V solverOffsetSlop = V4Load(solverOffsetSlopF32); + + const Vec4V zero = V4Zero(); + + PxU8 flags[4] = { PxU8(descs[0].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[1].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[2].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[3].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0) }; + + + //The block is dynamic if **any** of the constraints have a non-static body B. This allows us to batch static and non-static constraints but we only get a memory/perf + //saving if all 4 are static. This simplifies the constraint partitioning such that it only needs to care about separating contacts and 1D constraints (which it already does) + const bool isDynamic = ((descs[0].bodyState1 | descs[1].bodyState1 | descs[2].bodyState1 | descs[3].bodyState1) & PxSolverContactDesc::eDYNAMIC_BODY) != 0; + + const PxU32 constraintSize = isDynamic ? sizeof(SolverContact4Dynamic) : sizeof(SolverContact4Base); + const PxU32 frictionSize = isDynamic ? sizeof(SolverFriction4Dynamic) : sizeof(SolverFriction4Base); + + PxU8* PX_RESTRICT ptr = workspace; + + const Vec4V dom0 = invMassScale0; + const Vec4V dom1 = invMassScale1; + const Vec4V angDom0 = invInertiaScale0; + const Vec4V angDom1 = invInertiaScale1; + + const Vec4V maxPenBias = V4Max(V4Merge(FLoad(descs[0].data0->penBiasClamp), FLoad(descs[1].data0->penBiasClamp), + FLoad(descs[2].data0->penBiasClamp), FLoad(descs[3].data0->penBiasClamp)), + V4Merge(FLoad(descs[0].data1->penBiasClamp), FLoad(descs[1].data1->penBiasClamp), + FLoad(descs[2].data1->penBiasClamp), FLoad(descs[3].data1->penBiasClamp))); + + const Vec4V restDistance = V4Merge(FLoad(descs[0].restDistance), FLoad(descs[1].restDistance), FLoad(descs[2].restDistance), + FLoad(descs[3].restDistance)); + + //load up velocities + Vec4V linVel00 = V4LoadA(&descs[0].data0->linearVelocity.x); + Vec4V linVel10 = V4LoadA(&descs[1].data0->linearVelocity.x); + Vec4V linVel20 = V4LoadA(&descs[2].data0->linearVelocity.x); + Vec4V linVel30 = V4LoadA(&descs[3].data0->linearVelocity.x); + + Vec4V linVel01 = V4LoadA(&descs[0].data1->linearVelocity.x); + Vec4V linVel11 = V4LoadA(&descs[1].data1->linearVelocity.x); + Vec4V linVel21 = V4LoadA(&descs[2].data1->linearVelocity.x); + Vec4V linVel31 = V4LoadA(&descs[3].data1->linearVelocity.x); + + Vec4V angVel00 = V4LoadA(&descs[0].data0->angularVelocity.x); + Vec4V angVel10 = V4LoadA(&descs[1].data0->angularVelocity.x); + Vec4V angVel20 = V4LoadA(&descs[2].data0->angularVelocity.x); + Vec4V angVel30 = V4LoadA(&descs[3].data0->angularVelocity.x); + + Vec4V angVel01 = V4LoadA(&descs[0].data1->angularVelocity.x); + Vec4V angVel11 = V4LoadA(&descs[1].data1->angularVelocity.x); + Vec4V angVel21 = V4LoadA(&descs[2].data1->angularVelocity.x); + Vec4V angVel31 = V4LoadA(&descs[3].data1->angularVelocity.x); + + Vec4V linVelT00, linVelT10, linVelT20; + Vec4V linVelT01, linVelT11, linVelT21; + Vec4V angVelT00, angVelT10, angVelT20; + Vec4V angVelT01, angVelT11, angVelT21; + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVelT00, linVelT10, linVelT20); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVelT01, linVelT11, linVelT21); + PX_TRANSPOSE_44_34(angVel00, angVel10, angVel20, angVel30, angVelT00, angVelT10, angVelT20); + PX_TRANSPOSE_44_34(angVel01, angVel11, angVel21, angVel31, angVelT01, angVelT11, angVelT21); + + const Vec4V vrelX = V4Sub(linVelT00, linVelT01); + const Vec4V vrelY = V4Sub(linVelT10, linVelT11); + const Vec4V vrelZ = V4Sub(linVelT20, linVelT21); + + + + //Load up masses and invInertia + + const Vec4V invMass0 = V4Merge(FLoad(descs[0].data0->invMass), FLoad(descs[1].data0->invMass), FLoad(descs[2].data0->invMass), + FLoad(descs[3].data0->invMass)); + + const Vec4V invMass1 = V4Merge(FLoad(descs[0].data1->invMass), FLoad(descs[1].data1->invMass), FLoad(descs[2].data1->invMass), + FLoad(descs[3].data1->invMass)); + + const Vec4V invMass0_dom0fV = V4Mul(dom0, invMass0); + const Vec4V invMass1_dom1fV = V4Mul(dom1, invMass1); + + Vec4V invInertia00X = Vec4V_From_Vec3V(V3LoadU(descs[0].data0->sqrtInvInertia.column0)); + Vec4V invInertia00Y = Vec4V_From_Vec3V(V3LoadU(descs[0].data0->sqrtInvInertia.column1)); + Vec4V invInertia00Z = Vec4V_From_Vec3V(V3LoadU(descs[0].data0->sqrtInvInertia.column2)); + + Vec4V invInertia10X = Vec4V_From_Vec3V(V3LoadU(descs[1].data0->sqrtInvInertia.column0)); + Vec4V invInertia10Y = Vec4V_From_Vec3V(V3LoadU(descs[1].data0->sqrtInvInertia.column1)); + Vec4V invInertia10Z = Vec4V_From_Vec3V(V3LoadU(descs[1].data0->sqrtInvInertia.column2)); + + Vec4V invInertia20X = Vec4V_From_Vec3V(V3LoadU(descs[2].data0->sqrtInvInertia.column0)); + Vec4V invInertia20Y = Vec4V_From_Vec3V(V3LoadU(descs[2].data0->sqrtInvInertia.column1)); + Vec4V invInertia20Z = Vec4V_From_Vec3V(V3LoadU(descs[2].data0->sqrtInvInertia.column2)); + + Vec4V invInertia30X = Vec4V_From_Vec3V(V3LoadU(descs[3].data0->sqrtInvInertia.column0)); + Vec4V invInertia30Y = Vec4V_From_Vec3V(V3LoadU(descs[3].data0->sqrtInvInertia.column1)); + Vec4V invInertia30Z = Vec4V_From_Vec3V(V3LoadU(descs[3].data0->sqrtInvInertia.column2)); + + Vec4V invInertia01X = Vec4V_From_Vec3V(V3LoadU(descs[0].data1->sqrtInvInertia.column0)); + Vec4V invInertia01Y = Vec4V_From_Vec3V(V3LoadU(descs[0].data1->sqrtInvInertia.column1)); + Vec4V invInertia01Z = Vec4V_From_Vec3V(V3LoadU(descs[0].data1->sqrtInvInertia.column2)); + + Vec4V invInertia11X = Vec4V_From_Vec3V(V3LoadU(descs[1].data1->sqrtInvInertia.column0)); + Vec4V invInertia11Y = Vec4V_From_Vec3V(V3LoadU(descs[1].data1->sqrtInvInertia.column1)); + Vec4V invInertia11Z = Vec4V_From_Vec3V(V3LoadU(descs[1].data1->sqrtInvInertia.column2)); + + Vec4V invInertia21X = Vec4V_From_Vec3V(V3LoadU(descs[2].data1->sqrtInvInertia.column0)); + Vec4V invInertia21Y = Vec4V_From_Vec3V(V3LoadU(descs[2].data1->sqrtInvInertia.column1)); + Vec4V invInertia21Z = Vec4V_From_Vec3V(V3LoadU(descs[2].data1->sqrtInvInertia.column2)); + + Vec4V invInertia31X = Vec4V_From_Vec3V(V3LoadU(descs[3].data1->sqrtInvInertia.column0)); + Vec4V invInertia31Y = Vec4V_From_Vec3V(V3LoadU(descs[3].data1->sqrtInvInertia.column1)); + Vec4V invInertia31Z = Vec4V_From_Vec3V(V3LoadU(descs[3].data1->sqrtInvInertia.column2)); + + Vec4V invInertia0X0, invInertia0X1, invInertia0X2; + Vec4V invInertia0Y0, invInertia0Y1, invInertia0Y2; + Vec4V invInertia0Z0, invInertia0Z1, invInertia0Z2; + + Vec4V invInertia1X0, invInertia1X1, invInertia1X2; + Vec4V invInertia1Y0, invInertia1Y1, invInertia1Y2; + Vec4V invInertia1Z0, invInertia1Z1, invInertia1Z2; + + PX_TRANSPOSE_44_34(invInertia00X, invInertia10X, invInertia20X, invInertia30X, invInertia0X0, invInertia0Y0, invInertia0Z0); + PX_TRANSPOSE_44_34(invInertia00Y, invInertia10Y, invInertia20Y, invInertia30Y, invInertia0X1, invInertia0Y1, invInertia0Z1); + PX_TRANSPOSE_44_34(invInertia00Z, invInertia10Z, invInertia20Z, invInertia30Z, invInertia0X2, invInertia0Y2, invInertia0Z2); + + PX_TRANSPOSE_44_34(invInertia01X, invInertia11X, invInertia21X, invInertia31X, invInertia1X0, invInertia1Y0, invInertia1Z0); + PX_TRANSPOSE_44_34(invInertia01Y, invInertia11Y, invInertia21Y, invInertia31Y, invInertia1X1, invInertia1Y1, invInertia1Z1); + PX_TRANSPOSE_44_34(invInertia01Z, invInertia11Z, invInertia21Z, invInertia31Z, invInertia1X2, invInertia1Y2, invInertia1Z2); + + const FloatV invDt = FLoad(invDtF32); + const FloatV p8 = FLoad(0.8f); + //const Vec4V p84 = V4Splat(p8); + const Vec4V p1 = V4Splat(FLoad(0.1f)); + const Vec4V bounceThreshold = V4Splat(FLoad(bounceThresholdF32)); + const Vec4V orthoThreshold = V4Splat(FLoad(0.70710678f)); + + const FloatV invDtp8 = FMul(invDt, p8); + + const Vec3V bodyFrame00p = V3LoadU(descs[0].bodyFrame0.p); + const Vec3V bodyFrame01p = V3LoadU(descs[1].bodyFrame0.p); + const Vec3V bodyFrame02p = V3LoadU(descs[2].bodyFrame0.p); + const Vec3V bodyFrame03p = V3LoadU(descs[3].bodyFrame0.p); + + Vec4V bodyFrame00p4 = Vec4V_From_Vec3V(bodyFrame00p); + Vec4V bodyFrame01p4 = Vec4V_From_Vec3V(bodyFrame01p); + Vec4V bodyFrame02p4 = Vec4V_From_Vec3V(bodyFrame02p); + Vec4V bodyFrame03p4 = Vec4V_From_Vec3V(bodyFrame03p); + + Vec4V bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ; + PX_TRANSPOSE_44_34(bodyFrame00p4, bodyFrame01p4, bodyFrame02p4, bodyFrame03p4, bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ); + + + const Vec3V bodyFrame10p = V3LoadU(descs[0].bodyFrame1.p); + const Vec3V bodyFrame11p = V3LoadU(descs[1].bodyFrame1.p); + const Vec3V bodyFrame12p = V3LoadU(descs[2].bodyFrame1.p); + const Vec3V bodyFrame13p = V3LoadU(descs[3].bodyFrame1.p); + + Vec4V bodyFrame10p4 = Vec4V_From_Vec3V(bodyFrame10p); + Vec4V bodyFrame11p4 = Vec4V_From_Vec3V(bodyFrame11p); + Vec4V bodyFrame12p4 = Vec4V_From_Vec3V(bodyFrame12p); + Vec4V bodyFrame13p4 = Vec4V_From_Vec3V(bodyFrame13p); + + Vec4V bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ; + PX_TRANSPOSE_44_34(bodyFrame10p4, bodyFrame11p4, bodyFrame12p4, bodyFrame13p4, bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ); + + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + PxU32 frictionIndex0 = 0, frictionIndex1 = 0, frictionIndex2 = 0, frictionIndex3 = 0; + + + PxU32 maxPatches = PxMax(descs[0].numFrictionPatches, PxMax(descs[1].numFrictionPatches, PxMax(descs[2].numFrictionPatches, descs[3].numFrictionPatches))); + PxU32 maxContacts = numContactPoints4; + + //This is the address at which the first friction patch exists + PxU8* ptr2 = ptr + ((sizeof(SolverContactCoulombHeader4) * maxPatches) + constraintSize * maxContacts); + + //PxU32 contactId = 0; + + for(PxU32 i=0;i= descs[0].numFrictionPatches; + const bool hasFinished1 = i >= descs[1].numFrictionPatches; + const bool hasFinished2 = i >= descs[2].numFrictionPatches; + const bool hasFinished3 = i >= descs[3].numFrictionPatches; + + + frictionIndex0 = hasFinished0 ? frictionIndex0 : descs[0].startFrictionPatchIndex + i; + frictionIndex1 = hasFinished1 ? frictionIndex1 : descs[1].startFrictionPatchIndex + i; + frictionIndex2 = hasFinished2 ? frictionIndex2 : descs[2].startFrictionPatchIndex + i; + frictionIndex3 = hasFinished3 ? frictionIndex3 : descs[3].startFrictionPatchIndex + i; + + PxU32 clampedContacts0 = hasFinished0 ? 0 : c.frictionPatchContactCounts[frictionIndex0]; + PxU32 clampedContacts1 = hasFinished1 ? 0 : c.frictionPatchContactCounts[frictionIndex1]; + PxU32 clampedContacts2 = hasFinished2 ? 0 : c.frictionPatchContactCounts[frictionIndex2]; + PxU32 clampedContacts3 = hasFinished3 ? 0 : c.frictionPatchContactCounts[frictionIndex3]; + + PxU32 clampedFric0 = clampedContacts0 * numFrictionPerPoint; + PxU32 clampedFric1 = clampedContacts1 * numFrictionPerPoint; + PxU32 clampedFric2 = clampedContacts2 * numFrictionPerPoint; + PxU32 clampedFric3 = clampedContacts3 * numFrictionPerPoint; + + + const PxU32 numContacts = PxMax(clampedContacts0, PxMax(clampedContacts1, PxMax(clampedContacts2, clampedContacts3))); + const PxU32 numFrictions = PxMax(clampedFric0, PxMax(clampedFric1, PxMax(clampedFric2, clampedFric3))); + + PxU32 firstPatch0 = c.correlationListHeads[frictionIndex0]; + PxU32 firstPatch1 = c.correlationListHeads[frictionIndex1]; + PxU32 firstPatch2 = c.correlationListHeads[frictionIndex2]; + PxU32 firstPatch3 = c.correlationListHeads[frictionIndex3]; + + const Gu::ContactPoint* contactBase0 = descs[0].contacts + c.contactPatches[firstPatch0].start; + const Gu::ContactPoint* contactBase1 = descs[1].contacts + c.contactPatches[firstPatch1].start; + const Gu::ContactPoint* contactBase2 = descs[2].contacts + c.contactPatches[firstPatch2].start; + const Gu::ContactPoint* contactBase3 = descs[3].contacts + c.contactPatches[firstPatch3].start; + + const Vec4V restitution = V4Merge(FLoad(contactBase0->restitution), FLoad(contactBase1->restitution), FLoad(contactBase2->restitution), + FLoad(contactBase3->restitution)); + + const Vec4V staticFriction = V4Merge(FLoad(contactBase0->staticFriction), FLoad(contactBase1->staticFriction), FLoad(contactBase2->staticFriction), + FLoad(contactBase3->staticFriction)); + + SolverContactCoulombHeader4* PX_RESTRICT header = reinterpret_cast(ptr); + + header->frictionOffset = PxU16(ptr2 - ptr); + + ptr += sizeof(SolverContactCoulombHeader4); + + SolverFrictionHeader4* PX_RESTRICT fricHeader = reinterpret_cast(ptr2); + ptr2 += sizeof(SolverFrictionHeader4) + sizeof(Vec4V) * numContacts; + + + header->numNormalConstr0 = Ps::to8(clampedContacts0); + header->numNormalConstr1 = Ps::to8(clampedContacts1); + header->numNormalConstr2 = Ps::to8(clampedContacts2); + header->numNormalConstr3 = Ps::to8(clampedContacts3); + header->numNormalConstr = Ps::to8(numContacts); + header->invMassADom = invMass0_dom0fV; + header->invMassBDom = invMass1_dom1fV; + header->angD0 = angDom0; + header->angD1 = angDom1; + header->restitution = restitution; + + header->flags[0] = flags[0]; header->flags[1] = flags[1]; header->flags[2] = flags[2]; header->flags[3] = flags[3]; + + header->type = Ps::to8(isDynamic ? DY_SC_TYPE_BLOCK_RB_CONTACT : DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT); + header->shapeInteraction[0] = descs[0].shapeInteraction; header->shapeInteraction[1] = descs[1].shapeInteraction; + header->shapeInteraction[2] = descs[2].shapeInteraction; header->shapeInteraction[3] = descs[3].shapeInteraction; + + + fricHeader->invMassADom = invMass0_dom0fV; + fricHeader->invMassBDom = invMass1_dom1fV; + fricHeader->angD0 = angDom0; + fricHeader->angD1 = angDom1; + fricHeader->numFrictionConstr0 = Ps::to8(clampedFric0); + fricHeader->numFrictionConstr1 = Ps::to8(clampedFric1); + fricHeader->numFrictionConstr2 = Ps::to8(clampedFric2); + fricHeader->numFrictionConstr3 = Ps::to8(clampedFric3); + fricHeader->numNormalConstr = Ps::to8(numContacts); + fricHeader->numNormalConstr0 = Ps::to8(clampedContacts0); + fricHeader->numNormalConstr1 = Ps::to8(clampedContacts1); + fricHeader->numNormalConstr2 = Ps::to8(clampedContacts2); + fricHeader->numNormalConstr3 = Ps::to8(clampedContacts3); + fricHeader->type = Ps::to8(isDynamic ? DY_SC_TYPE_BLOCK_FRICTION : DY_SC_TYPE_BLOCK_STATIC_FRICTION); + fricHeader->staticFriction = staticFriction; + fricHeader->frictionPerContact = PxU32(numFrictionPerPoint == 2 ? 1 : 0); + + fricHeader->numFrictionConstr = Ps::to8(numFrictions); + + Vec4V normal0 = V4LoadA(&contactBase0->normal.x); + Vec4V normal1 = V4LoadA(&contactBase1->normal.x); + Vec4V normal2 = V4LoadA(&contactBase2->normal.x); + Vec4V normal3 = V4LoadA(&contactBase3->normal.x); + + Vec4V normalX, normalY, normalZ; + PX_TRANSPOSE_44_34(normal0, normal1, normal2, normal3, normalX, normalY, normalZ); + header->normalX = normalX; + header->normalY = normalY; + header->normalZ = normalZ; + + const Vec4V normalLenSq = V4MulAdd(normalZ, normalZ, V4MulAdd(normalY, normalY, V4Mul(normalX, normalX))); + + const Vec4V linNorVel0 = V4MulAdd(normalZ, linVelT20, V4MulAdd(normalY, linVelT10, V4Mul(normalX, linVelT00))); + const Vec4V linNorVel1 = V4MulAdd(normalZ, linVelT21, V4MulAdd(normalY, linVelT11, V4Mul(normalX, linVelT01))); + + const Vec4V invMassNorLenSq0 = V4Mul(invMass0_dom0fV, normalLenSq); + const Vec4V invMassNorLenSq1 = V4Mul(invMass1_dom1fV, normalLenSq); + + + //Calculate friction directions + const BoolV cond =V4IsGrtr(orthoThreshold, V4Abs(normalX)); + + const Vec4V t0FallbackX = V4Sel(cond, zero, V4Neg(normalY)); + const Vec4V t0FallbackY = V4Sel(cond, V4Neg(normalZ), normalX); + const Vec4V t0FallbackZ = V4Sel(cond, normalY, zero); + + const Vec4V dotNormalVrel = V4MulAdd(normalZ, vrelZ, V4MulAdd(normalY, vrelY, V4Mul(normalX, vrelX))); + const Vec4V vrelSubNorVelX = V4NegMulSub(normalX, dotNormalVrel, vrelX); + const Vec4V vrelSubNorVelY = V4NegMulSub(normalY, dotNormalVrel, vrelY); + const Vec4V vrelSubNorVelZ = V4NegMulSub(normalZ, dotNormalVrel, vrelZ); + + const Vec4V lenSqvrelSubNorVelZ = V4MulAdd(vrelSubNorVelX, vrelSubNorVelX, V4MulAdd(vrelSubNorVelY, vrelSubNorVelY, V4Mul(vrelSubNorVelZ, vrelSubNorVelZ))); + + const BoolV bcon2 = V4IsGrtr(lenSqvrelSubNorVelZ, p1); + + Vec4V t0X = V4Sel(bcon2, vrelSubNorVelX, t0FallbackX); + Vec4V t0Y = V4Sel(bcon2, vrelSubNorVelY, t0FallbackY); + Vec4V t0Z = V4Sel(bcon2, vrelSubNorVelZ, t0FallbackZ); + + //Now normalize this... + const Vec4V recipLen = V4Rsqrt(V4MulAdd(t0X, t0X, V4MulAdd(t0Y, t0Y, V4Mul(t0Z, t0Z)))); + + t0X = V4Mul(t0X, recipLen); + t0Y = V4Mul(t0Y, recipLen); + t0Z = V4Mul(t0Z, recipLen); + + const Vec4V t1X = V4NegMulSub(normalZ, t0Y, V4Mul(normalY, t0Z)); + const Vec4V t1Y = V4NegMulSub(normalX, t0Z, V4Mul(normalZ, t0X)); + const Vec4V t1Z = V4NegMulSub(normalY, t0X, V4Mul(normalX, t0Y)); + + const Vec4V tFallbackX[2] = {t0X, t1X}; + const Vec4V tFallbackY[2] = {t0Y, t1Y}; + const Vec4V tFallbackZ[2] = {t0Z, t1Z}; + + + //For all correlation heads - need to pull this out I think + + //OK, we have a counter for all our patches... + PxU32 finished = (PxU32(hasFinished0)) | + ((PxU32(hasFinished1)) << 1) | + ((PxU32(hasFinished2)) << 2) | + ((PxU32(hasFinished3)) << 3); + + CorrelationListIterator iter0(c, firstPatch0); + CorrelationListIterator iter1(c, firstPatch1); + CorrelationListIterator iter2(c, firstPatch2); + CorrelationListIterator iter3(c, firstPatch3); + + PxU32 contact0, contact1, contact2, contact3; + PxU32 patch0, patch1, patch2, patch3; + + iter0.nextContact(patch0, contact0); + iter1.nextContact(patch1, contact1); + iter2.nextContact(patch2, contact2); + iter3.nextContact(patch3, contact3); + + PxU8* p = ptr; + + PxU32 contactCount = 0; + PxU32 newFinished = + (PxU32(hasFinished0 || !iter0.hasNextContact())) | + ((PxU32(hasFinished1 || !iter1.hasNextContact())) << 1) | + ((PxU32(hasFinished2 || !iter2.hasNextContact())) << 2) | + ((PxU32(hasFinished3 || !iter3.hasNextContact())) << 3); + + PxU32 fricIndex = 0; + + while(finished != 0xf) + { + finished = newFinished; + ++contactCount; + Ps::prefetchLine(p, 384); + Ps::prefetchLine(p, 512); + Ps::prefetchLine(p, 640); + + SolverContact4Base* PX_RESTRICT solverContact = reinterpret_cast(p); + p += constraintSize; + + const Gu::ContactPoint& con0 = descs[0].contacts[c.contactPatches[patch0].start + contact0]; + const Gu::ContactPoint& con1 = descs[1].contacts[c.contactPatches[patch1].start + contact1]; + const Gu::ContactPoint& con2 = descs[2].contacts[c.contactPatches[patch2].start + contact2]; + const Gu::ContactPoint& con3 = descs[3].contacts[c.contactPatches[patch3].start + contact3]; + + //Now we need to splice these 4 contacts into a single structure + + { + Vec4V point0 = V4LoadA(&con0.point.x); + Vec4V point1 = V4LoadA(&con1.point.x); + Vec4V point2 = V4LoadA(&con2.point.x); + Vec4V point3 = V4LoadA(&con3.point.x); + + Vec4V pointX, pointY, pointZ; + PX_TRANSPOSE_44_34(point0, point1, point2, point3, pointX, pointY, pointZ); + + Vec4V targetVel0 = V4LoadA(&con0.targetVel.x); + Vec4V targetVel1 = V4LoadA(&con1.targetVel.x); + Vec4V targetVel2 = V4LoadA(&con2.targetVel.x); + Vec4V targetVel3 = V4LoadA(&con3.targetVel.x); + + Vec4V targetVelX, targetVelY, targetVelZ; + PX_TRANSPOSE_44_34(targetVel0, targetVel1, targetVel2, targetVel3, targetVelX, targetVelY, targetVelZ); + + Vec4V raX = V4Sub(pointX, bodyFrame0pX); + Vec4V raY = V4Sub(pointY, bodyFrame0pY); + Vec4V raZ = V4Sub(pointZ, bodyFrame0pZ); + + Vec4V rbX = V4Sub(pointX, bodyFrame1pX); + Vec4V rbY = V4Sub(pointY, bodyFrame1pY); + Vec4V rbZ = V4Sub(pointZ, bodyFrame1pZ); + + raX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raX)), zero, raX); + raY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raY)), zero, raY); + raZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raZ)), zero, raZ); + + rbX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbX)), zero, rbX); + rbY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbY)), zero, rbY); + rbZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbZ)), zero, rbZ); + + { + const Vec4V separation = V4Merge(FLoad(con0.separation), FLoad(con1.separation), FLoad(con2.separation), + FLoad(con3.separation)); + const Vec4V maxImpulse = V4Merge(FLoad(con0.maxImpulse), FLoad(con1.maxImpulse), FLoad(con2.maxImpulse), + FLoad(con3.maxImpulse)); + + const Vec4V cTargetVel = V4MulAdd(normalX, targetVelX, V4MulAdd(normalY, targetVelY, V4Mul(normalZ, targetVelZ))); + + //raXn = cross(ra, normal) which = Vec3V( a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x); + const Vec4V raXnX = V4NegMulSub(raZ, normalY, V4Mul(raY, normalZ)); + const Vec4V raXnY = V4NegMulSub(raX, normalZ, V4Mul(raZ, normalX)); + const Vec4V raXnZ = V4NegMulSub(raY, normalX, V4Mul(raX, normalY)); + + const Vec4V v0a0 = V4Mul(invInertia0X0, raXnX); + const Vec4V v0a1 = V4Mul(invInertia0X1, raXnX); + const Vec4V v0a2 = V4Mul(invInertia0X2, raXnX); + + const Vec4V v0PlusV1a0 = V4MulAdd(invInertia0Y0, raXnY, v0a0); + const Vec4V v0PlusV1a1 = V4MulAdd(invInertia0Y1, raXnY, v0a1); + const Vec4V v0PlusV1a2 = V4MulAdd(invInertia0Y2, raXnY, v0a2); + + const Vec4V delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, v0PlusV1a0); + const Vec4V delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, v0PlusV1a1); + const Vec4V delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, v0PlusV1a2); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + const Vec4V dotRaXnAngVel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4Mul(raXnX, angVelT00))); + + Vec4V unitResponse = V4Add(invMassNorLenSq0, dotDelAngVel0); + Vec4V vrel = V4Add(linNorVel0, dotRaXnAngVel0); + + + //The dynamic-only parts - need to if-statement these up. A branch here shouldn't cost us too much + if(isDynamic) + { + SolverContact4Dynamic* PX_RESTRICT dynamicContact = static_cast(solverContact); + const Vec4V rbXnX = V4NegMulSub(rbZ, normalY, V4Mul(rbY, normalZ)); + const Vec4V rbXnY = V4NegMulSub(rbX, normalZ, V4Mul(rbZ, normalX)); + const Vec4V rbXnZ = V4NegMulSub(rbY, normalX, V4Mul(rbX, normalY)); + + const Vec4V v0b0 = V4Mul(invInertia1X0, rbXnX); + const Vec4V v0b1 = V4Mul(invInertia1X1, rbXnX); + const Vec4V v0b2 = V4Mul(invInertia1X2, rbXnX); + + const Vec4V v0PlusV1b0 = V4MulAdd(invInertia1Y0, rbXnY, v0b0); + const Vec4V v0PlusV1b1 = V4MulAdd(invInertia1Y1, rbXnY, v0b1); + const Vec4V v0PlusV1b2 = V4MulAdd(invInertia1Y2, rbXnY, v0b2); + + const Vec4V delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, v0PlusV1b0); + const Vec4V delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, v0PlusV1b1); + const Vec4V delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, v0PlusV1b2); + + + //V3Dot(raXn, delAngVel0) + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + + const Vec4V resp1 = V4Add(dotDelAngVel1, invMassNorLenSq1); + + unitResponse = V4Add(unitResponse, resp1); + + const Vec4V vrel2 = V4Add(linNorVel1, dotRbXnAngVel1); + vrel = V4Sub(vrel, vrel2); + + //These are for dynamic-only contacts. + dynamicContact->rbXnX = delAngVel1X; + dynamicContact->rbXnY = delAngVel1Y; + dynamicContact->rbXnZ = delAngVel1Z; + + } + + const Vec4V velMultiplier = V4Sel(V4IsGrtr(unitResponse, zero), V4Recip(unitResponse), zero); + + const Vec4V penetration = V4Sub(separation, restDistance); + + const Vec4V penInvDtp8 = V4Max(maxPenBias, V4Scale(penetration, invDtp8)); + + Vec4V scaledBias = V4Mul(velMultiplier, penInvDtp8); + + const Vec4V penetrationInvDt = V4Scale(penetration, invDt); + + const BoolV isGreater2 = BAnd(BAnd(V4IsGrtr(restitution, zero), V4IsGrtr(bounceThreshold, vrel)), + V4IsGrtr(V4Neg(vrel), penetrationInvDt)); + + const BoolV ccdSeparationCondition = V4IsGrtrOrEq(ccdMaxSeparation, penetration); + + scaledBias = V4Sel(BAnd(ccdSeparationCondition, isGreater2), zero, scaledBias); + + const Vec4V sumVRel(vrel); + + const Vec4V targetVelocity = V4Sub(V4Add(V4Sel(isGreater2, V4Mul(V4Neg(sumVRel), restitution), zero), cTargetVel), vrel); + + //These values are present for static and dynamic contacts + solverContact->raXnX = delAngVel0X; + solverContact->raXnY = delAngVel0Y; + solverContact->raXnZ = delAngVel0Z; + solverContact->velMultiplier = velMultiplier; + solverContact->appliedForce = zero; + solverContact->scaledBias = scaledBias; + solverContact->targetVelocity = targetVelocity; + solverContact->maxImpulse = maxImpulse; + } + + //PxU32 conId = contactId++; + + /*Vec4V targetVel0 = V4LoadA(&con0.targetVel.x); + Vec4V targetVel1 = V4LoadA(&con1.targetVel.x); + Vec4V targetVel2 = V4LoadA(&con2.targetVel.x); + Vec4V targetVel3 = V4LoadA(&con3.targetVel.x); + + Vec4V targetVelX, targetVelY, targetVelZ; + PX_TRANSPOSE_44_34(targetVel0, targetVel1, targetVel2, targetVel3, targetVelX, targetVelY, targetVelZ);*/ + + for(PxU32 a = 0; a < numFrictionPerPoint; ++a) + { + SolverFriction4Base* PX_RESTRICT friction = reinterpret_cast(ptr2); + + ptr2 += frictionSize; + + const Vec4V tX = tFallbackX[fricIndex]; + const Vec4V tY = tFallbackY[fricIndex]; + const Vec4V tZ = tFallbackZ[fricIndex]; + + fricIndex = 1 - fricIndex; + + const Vec4V raXnX = V4NegMulSub(raZ, tY, V4Mul(raY, tZ)); + const Vec4V raXnY = V4NegMulSub(raX, tZ, V4Mul(raZ, tX)); + const Vec4V raXnZ = V4NegMulSub(raY, tX, V4Mul(raX, tY)); + + const Vec4V v0a0 = V4Mul(invInertia0X0, raXnX); + const Vec4V v0a1 = V4Mul(invInertia0X1, raXnX); + const Vec4V v0a2 = V4Mul(invInertia0X2, raXnX); + + const Vec4V v0PlusV1a0 = V4MulAdd(invInertia0Y0, raXnY, v0a0); + const Vec4V v0PlusV1a1 = V4MulAdd(invInertia0Y1, raXnY, v0a1); + const Vec4V v0PlusV1a2 = V4MulAdd(invInertia0Y2, raXnY, v0a2); + + const Vec4V delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, v0PlusV1a0); + const Vec4V delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, v0PlusV1a1); + const Vec4V delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, v0PlusV1a2); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + + const Vec4V norVel0 = V4MulAdd(tX, linVelT00, V4MulAdd(tY, linVelT10, V4Mul(tZ, linVelT20))); + const Vec4V dotRaXnAngVel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4Mul(raXnX, angVelT00))); + Vec4V vrel = V4Add(norVel0, dotRaXnAngVel0); + + Vec4V unitResponse = V4Add(invMass0_dom0fV, dotDelAngVel0); + + if(isDynamic) + { + SolverFriction4Dynamic* PX_RESTRICT dFric = static_cast(friction); + + const Vec4V rbXnX = V4NegMulSub(rbZ, tY, V4Mul(rbY, tZ)); + const Vec4V rbXnY = V4NegMulSub(rbX, tZ, V4Mul(rbZ, tX)); + const Vec4V rbXnZ = V4NegMulSub(rbY, tX, V4Mul(rbX, tY)); + + const Vec4V v0b0 = V4Mul(invInertia1X0, rbXnX); + const Vec4V v0b1 = V4Mul(invInertia1X1, rbXnX); + const Vec4V v0b2 = V4Mul(invInertia1X2, rbXnX); + + const Vec4V v0PlusV1b0 = V4MulAdd(invInertia1Y0, rbXnY, v0b0); + const Vec4V v0PlusV1b1 = V4MulAdd(invInertia1Y1, rbXnY, v0b1); + const Vec4V v0PlusV1b2 = V4MulAdd(invInertia1Y2, rbXnY, v0b2); + + const Vec4V delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, v0PlusV1b0); + const Vec4V delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, v0PlusV1b1); + const Vec4V delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, v0PlusV1b2); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V norVel1 = V4MulAdd(tX, linVelT01, V4MulAdd(tY, linVelT11, V4Mul(tZ, linVelT21))); + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + vrel = V4Sub(vrel, V4Add(norVel1, dotRbXnAngVel1)); + + const Vec4V resp1 = V4Add(dotDelAngVel1, invMassNorLenSq1); + + unitResponse = V4Add(unitResponse, resp1); + + dFric->rbXnX = delAngVel1X; + dFric->rbXnY = delAngVel1Y; + dFric->rbXnZ = delAngVel1Z; + } + + const Vec4V velMultiplier = V4Neg(V4Sel(V4IsGrtr(unitResponse, zero), V4Recip(unitResponse), zero)); + + friction->appliedForce = zero; + friction->raXnX = delAngVel0X; + friction->raXnY = delAngVel0Y; + friction->raXnZ = delAngVel0Z; + friction->velMultiplier = velMultiplier; + friction->targetVelocity = V4Sub(V4MulAdd(targetVelZ, tZ, V4MulAdd(targetVelY, tY, V4Mul(targetVelX, tX))), vrel); + friction->normalX = tX; + friction->normalY = tY; + friction->normalZ = tZ; + } + } + if(!(finished & 0x1)) + { + iter0.nextContact(patch0, contact0); + newFinished |= PxU32(!iter0.hasNextContact()); + } + + if(!(finished & 0x2)) + { + iter1.nextContact(patch1, contact1); + newFinished |= (PxU32(!iter1.hasNextContact()) << 1); + } + + if(!(finished & 0x4)) + { + iter2.nextContact(patch2, contact2); + newFinished |= (PxU32(!iter2.hasNextContact()) << 2); + } + + if(!(finished & 0x8)) + { + iter3.nextContact(patch3, contact3); + newFinished |= (PxU32(!iter3.hasNextContact()) << 3); + } + } + ptr = p; + } + return true; +} + + + +//The persistent friction patch correlation/allocation will already have happenned as this is per-pair. +//This function just computes the size of the combined solve data. +void computeBlockStreamByteSizesCoulomb4(PxSolverContactDesc* descs, + ThreadContext& threadContext, const CorrelationBuffer& c, + const PxU32 numFrictionPerPoint, + PxU32& _solverConstraintByteSize, PxU32* _axisConstraintCount, PxU32& _numContactPoints4) +{ + PX_ASSERT(0 == _solverConstraintByteSize); + PX_UNUSED(threadContext); + + PxU32 maxPatches = 0; + PxU32 maxContactCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxU32 maxFrictionCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxMemZero(maxContactCount, sizeof(maxContactCount)); + PxMemZero(maxFrictionCount, sizeof(maxFrictionCount)); + for(PxU32 a = 0; a < 4; ++a) + { + PxU32 axisConstraintCount = 0; + + for(PxU32 i = 0; i < descs[a].numFrictionPatches; i++) + { + PxU32 ind = i + descs[a].startFrictionPatchIndex; + + const FrictionPatch& frictionPatch = c.frictionPatches[ind]; + + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0; + //Solver constraint data. + if(c.frictionPatchContactCounts[ind]!=0) + { + maxContactCount[i] = PxMax(c.frictionPatchContactCounts[ind], maxContactCount[i]); + axisConstraintCount += c.frictionPatchContactCounts[ind]; + + if(haveFriction) + { + //const PxU32 fricCount = c.frictionPatches[ind].numConstraints; + const PxU32 fricCount = c.frictionPatchContactCounts[ind] * numFrictionPerPoint; + maxFrictionCount[i] = PxMax(fricCount, maxFrictionCount[i]); + axisConstraintCount += fricCount; + } + } + } + maxPatches = PxMax(descs[a].numFrictionPatches, maxPatches); + _axisConstraintCount[a] = axisConstraintCount; + } + + PxU32 totalContacts = 0, totalFriction = 0; + for(PxU32 a = 0; a < maxPatches; ++a) + { + totalContacts += maxContactCount[a]; + totalFriction += maxFrictionCount[a]; + } + + _numContactPoints4 = totalContacts; + + + //OK, we have a given number of friction patches, contact points and friction constraints so we can calculate how much memory we need + + const bool isStatic = (((descs[0].bodyState1 | descs[1].bodyState1 | descs[2].bodyState1 | descs[3].bodyState1) & PxSolverContactDesc::eDYNAMIC_BODY) == 0); + + const PxU32 headerSize = (sizeof(SolverContactCoulombHeader4) + sizeof(SolverFrictionHeader4)) * maxPatches; + //Add on 1 Vec4V per contact for the applied force buffer + const PxU32 constraintSize = isStatic ? ((sizeof(SolverContact4Base) + sizeof(Vec4V)) * totalContacts) + ( sizeof(SolverFriction4Base) * totalFriction) : + ((sizeof(SolverContact4Dynamic) + sizeof(Vec4V)) * totalContacts) + (sizeof(SolverFriction4Dynamic) * totalFriction); + + _solverConstraintByteSize = ((constraintSize + headerSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); +} + + +static SolverConstraintPrepState::Enum reserveBlockStreamsCoulomb4(PxSolverContactDesc* descs, ThreadContext& threadContext, const CorrelationBuffer& c, + PxU8*& solverConstraint, const PxU32 numFrictionPerContactPoint, + PxU32& solverConstraintByteSize, + PxU32* axisConstraintCount, PxU32& numContactPoints4, PxConstraintAllocator& constraintAllocator) +{ + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(0 == solverConstraintByteSize); + + //From constraintBlockStream we need to reserve contact points, contact forces, and a char buffer for the solver constraint data (already have a variable for this). + //From frictionPatchStream we just need to reserve a single buffer. + + //Compute the sizes of all the buffers. + computeBlockStreamByteSizesCoulomb4( + descs, threadContext, c, numFrictionPerContactPoint, solverConstraintByteSize, + axisConstraintCount, numContactPoints4); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if(constraintBlockByteSize > 0) + { + if((constraintBlockByteSize + 16u) > 16384) + return SolverConstraintPrepState::eUNBATCHABLE; + + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if(0==constraintBlock || (reinterpret_cast(-1))==constraintBlock) + { + if(0==constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock=NULL; + } + } + } + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if(0==constraintBlockByteSize || constraintBlock) + { + if(solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0==(uintptr_t(solverConstraint) & 0x0f)); + } + } + + //Return true if neither of the two block reservations failed. + return ((0==constraintBlockByteSize || constraintBlock)) ? SolverConstraintPrepState::eSUCCESS : SolverConstraintPrepState::eOUT_OF_MEMORY; +} + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb1D( + PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + return createFinalizeSolverContacts4Coulomb(outputs, threadContext, blockDescs, invDtF32, bounceThresholdF32, + frictionOffsetThreshold, correlationDistance, solverOffsetSlop, constraintAllocator, PxFrictionType::eONE_DIRECTIONAL); +} + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb2D( + PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + return createFinalizeSolverContacts4Coulomb(outputs, threadContext, blockDescs, invDtF32, bounceThresholdF32, + frictionOffsetThreshold, correlationDistance, solverOffsetSlop, constraintAllocator, PxFrictionType::eTWO_DIRECTIONAL); +} + + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb( + PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + PxFrictionType::Enum frictionType) +{ + PX_UNUSED(frictionOffsetThreshold); + PX_UNUSED(correlationDistance); + + for(PxU32 i = 0; i < 4; ++i) + { + blockDescs[i].desc->constraintLengthOver16 = 0; + } + + PX_ASSERT(outputs[0]->nbContacts && outputs[1]->nbContacts && outputs[2]->nbContacts && outputs[3]->nbContacts); + + Gu::ContactBuffer& buffer = threadContext.mContactBuffer; + + buffer.count = 0; + + PxU32 numContacts = 0; + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + PxU32 numFrictionPerPoint = PxU32(frictionType == PxFrictionType::eONE_DIRECTIONAL ? 1 : 2); + + PX_ALIGN(16, PxReal invMassScale0[4]); + PX_ALIGN(16, PxReal invMassScale1[4]); + PX_ALIGN(16, PxReal invInertiaScale0[4]); + PX_ALIGN(16, PxReal invInertiaScale1[4]); + + for(PxU32 a = 0; a < 4; ++a) + { + PxSolverContactDesc& blockDesc = blockDescs[a]; + PxSolverConstraintDesc& desc = *blockDesc.desc; + + //blockDesc.startContactIndex = numContacts; + blockDesc.contacts = &buffer.contacts[numContacts]; + + Ps::prefetchLine(desc.bodyA); + Ps::prefetchLine(desc.bodyB); + + if((numContacts + outputs[a]->nbContacts) > 64) + { + return SolverConstraintPrepState::eUNBATCHABLE; + } + bool hasMaxImpulse, hasTargetVelocity; + + const PxReal defaultMaxImpulse = PxMin(blockDesc.data0->maxContactImpulse, blockDesc.data1->maxContactImpulse); + + PxU32 contactCount = extractContacts(buffer, *outputs[a], hasMaxImpulse, hasTargetVelocity, invMassScale0[a], invMassScale1[a], + invInertiaScale0[a], invInertiaScale1[a], defaultMaxImpulse); + + if(contactCount == 0) + return SolverConstraintPrepState::eUNBATCHABLE; + + numContacts+=contactCount; + + blockDesc.numContacts = contactCount; + blockDesc.hasMaxImpulse = hasMaxImpulse; + + blockDesc.startFrictionPatchIndex = c.frictionPatchCount; + blockDesc.startContactPatchIndex = c.contactPatchCount; + + createContactPatches(c, blockDesc.contacts, contactCount, PXC_SAME_NORMAL); + + bool overflow = correlatePatches(c, blockDesc.contacts, blockDesc.bodyFrame0, blockDesc.bodyFrame1, PXC_SAME_NORMAL, blockDesc.startContactPatchIndex, + blockDesc.startFrictionPatchIndex); + if(overflow) + return SolverConstraintPrepState::eUNBATCHABLE; + + blockDesc.numContactPatches = PxU16(c.contactPatchCount - blockDesc.startContactPatchIndex); + blockDesc.numFrictionPatches = c.frictionPatchCount - blockDesc.startFrictionPatchIndex; + + invMassScale0[a] *= blockDesc.mInvMassScales.linear0; + invMassScale1[a] *= blockDesc.mInvMassScales.linear1; + invInertiaScale0[a] *= blockDesc.mInvMassScales.angular0; + invInertiaScale1[a] *= blockDesc.mInvMassScales.angular1; + + } + + //OK, now we need to work out how much memory to allocate, allocate it and then block-create the constraints... + + PxU8* solverConstraint = NULL; + PxU32 solverConstraintByteSize = 0; + PxU32 axisConstraintCount[4]; + PxU32 numContactPoints4 = 0; + + SolverConstraintPrepState::Enum state = reserveBlockStreamsCoulomb4(blockDescs, threadContext, c, + solverConstraint, numFrictionPerPoint, + solverConstraintByteSize, + axisConstraintCount, numContactPoints4, constraintAllocator); + + if(state != SolverConstraintPrepState::eSUCCESS) + return state; + + //OK, we allocated the memory, now let's create the constraints + + for(PxU32 a = 0; a < 4; ++a) + { + PxSolverConstraintDesc& desc = *blockDescs[a].desc; + //n[a]->solverConstraintPointer = solverConstraint; + desc.constraint = solverConstraint; + + //KS - TODO - add back in counters for axisConstraintCount somewhere... + blockDescs[a].axisConstraintCount += Ps::to16(axisConstraintCount[a]); + + desc.constraintLengthOver16 = Ps::to16(solverConstraintByteSize/16); + + PxU32 writeBackLength = outputs[a]->nbContacts * sizeof(PxReal); + void* writeBack = outputs[a]->contactForces; + desc.writeBack = writeBack; + setWritebackLength(desc, writeBackLength); + } + + const Vec4V iMassScale0 = V4LoadA(invMassScale0); + const Vec4V iInertiaScale0 = V4LoadA(invInertiaScale0); + const Vec4V iMassScale1 = V4LoadA(invMassScale1); + const Vec4V iInertiaScale1 = V4LoadA(invInertiaScale1); + + + bool hasFriction = setupFinalizeSolverConstraintsCoulomb4(blockDescs, solverConstraint, + invDtF32, bounceThresholdF32, solverOffsetSlop, c, numFrictionPerPoint, numContactPoints4, solverConstraintByteSize, + iMassScale0, iInertiaScale0, iMassScale1, iInertiaScale1); + + *(reinterpret_cast(solverConstraint + solverConstraintByteSize)) = 0; + *(reinterpret_cast(solverConstraint + solverConstraintByteSize + 4)) = hasFriction ? 0xFFFFFFFF : 0; + + + return SolverConstraintPrepState::eSUCCESS; +} + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp new file mode 100644 index 000000000..1b0803c9d --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp @@ -0,0 +1,664 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +//#include "PxvGeometry.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DySolverConstraintDesc.h" +#include "DySolverBody.h" +#include "DySolverContact4.h" +#include "DySolverContactPF4.h" + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DySolverExt.h" +#include "DyArticulationContactPrep.h" +#include "DyContactPrepShared.h" + +#include "PsFoundation.h" + +using namespace physx::Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ +namespace Dy +{ + +bool createFinalizeSolverContactsCoulomb(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + PxFrictionType::Enum frictionType, + Cm::SpatialVectorF* Z); + +static bool setupFinalizeSolverConstraintsCoulomb( + Sc::ShapeInteraction* shapeInteraction, + const ContactBuffer& buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const PxSolverBodyData& data0, + const PxSolverBodyData& data1, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxU32 frictionPerPointCount, + const bool hasForceThresholds, + const bool staticBody, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + PxReal restDist, + const PxReal maxCCDSeparation, + const PxReal solverOffsetSlopF32) +{ + const FloatV ccdMaxSeparation = FLoad(maxCCDSeparation); + const Vec3V solverOffsetSlop = V3Load(solverOffsetSlopF32); + PxU8* PX_RESTRICT ptr = workspace; + const FloatV zero=FZero(); + + PxU8 flags = PxU8(hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0); + + const FloatV restDistance = FLoad(restDist); + + const Vec3V bodyFrame0p = V3LoadU(bodyFrame0.p); + const Vec3V bodyFrame1p = V3LoadU(bodyFrame1.p); + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + const PxU32 frictionPatchCount = c.frictionPatchCount; + + const PxU32 pointStride = sizeof(SolverContactPoint); + const PxU32 frictionStride = sizeof(SolverContactFriction); + const PxU8 pointHeaderType = Ps::to8(staticBody ? DY_SC_TYPE_STATIC_CONTACT : DY_SC_TYPE_RB_CONTACT); + const PxU8 frictionHeaderType = Ps::to8(staticBody ? DY_SC_TYPE_STATIC_FRICTION : DY_SC_TYPE_FRICTION); + + + const Vec3V linVel0 = V3LoadU(data0.linearVelocity); + const Vec3V linVel1 = V3LoadU(data1.linearVelocity); + const Vec3V angVel0 = V3LoadU(data0.angularVelocity); + const Vec3V angVel1 = V3LoadU(data1.angularVelocity); + + + const FloatV invMass0 = FLoad(data0.invMass); + const FloatV invMass1 = FLoad(data1.invMass); + + const FloatV maxPenBias = FMax(FLoad(data0.penBiasClamp), FLoad(data1.penBiasClamp)); + + // PT: the matrix is symmetric so we can read it as a PxMat33! Gets rid of 25000+ LHS. + const PxMat33& invIn0 = reinterpret_cast(data0.sqrtInvInertia); + PX_ALIGN(16, const Mat33V invSqrtInertia0) + ( + V3LoadU(invIn0.column0), + V3LoadU(invIn0.column1), + V3LoadU(invIn0.column2) + ); + const PxMat33& invIn1 = reinterpret_cast(data1.sqrtInvInertia); + PX_ALIGN(16, const Mat33V invSqrtInertia1) + ( + V3LoadU(invIn1.column0), + V3LoadU(invIn1.column1), + V3LoadU(invIn1.column2) + ); + + const FloatV invDt = FLoad(invDtF32); + const FloatV p8 = FLoad(0.8f); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + const FloatV orthoThreshold = FLoad(0.70710678f); + const FloatV eps = FLoad(0.00001f); + + const FloatV invDtp8 = FMul(invDt, p8); + + const FloatV d0 = FLoad(invMassScale0); + const FloatV d1 = FLoad(invMassScale1); + const FloatV nDom1fV = FNeg(d1); + const FloatV angD0 = FLoad(invInertiaScale0); + const FloatV angD1 = FLoad(invInertiaScale1); + + const FloatV invMass0_dom0fV = FMul(d0, invMass0); + const FloatV invMass1_dom1fV = FMul(nDom1fV, invMass1); + + + for(PxU32 i=0;i< frictionPatchCount;i++) + { + const PxU32 contactCount = c.frictionPatchContactCounts[i]; + if(contactCount == 0) + continue; + + const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start; + + const Vec3V normal = Ps::aos::V3LoadA(contactBase0->normal); + + const FloatV normalLenSq = V3LengthSq(normal); + const VecCrossV norCross = V3PrepareCross(normal); + + const FloatV restitution = FLoad(contactBase0->restitution); + + const FloatV norVel = V3SumElems(V3NegMulSub(normal, linVel1, V3Mul(normal, linVel0))); + /*const FloatV norVel0 = V3Dot(normal, linVel0); + const FloatV norVel1 = V3Dot(normal, linVel1); + const FloatV norVel = FSub(norVel0, norVel1);*/ + + const FloatV invMassNorLenSq0 = FMul(invMass0_dom0fV, normalLenSq); + const FloatV invMassNorLenSq1 = FMul(invMass1_dom1fV, normalLenSq); + + + SolverContactCoulombHeader* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactCoulombHeader); + + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + + + header->numNormalConstr = PxU8(contactCount); + header->type = pointHeaderType; + //header->setRestitution(n.restitution); + //header->setRestitution(contactBase0->restitution); + + header->setDominance0(invMass0_dom0fV); + header->setDominance1(FNeg(invMass1_dom1fV)); + FStore(angD0, &header->angDom0); + FStore(angD1, &header->angDom1); + header->setNormal(normal); + header->flags = flags; + header->shapeInteraction = shapeInteraction; + + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer.contacts + c.contactPatches[patch].start; + + + PxU8* p = ptr; + for(PxU32 j=0;j(p); + p += pointStride; + + constructContactConstraint(invSqrtInertia0, invSqrtInertia1, invMassNorLenSq0, + invMassNorLenSq1, angD0, angD1, bodyFrame0p, bodyFrame1p, + normal, norVel, norCross, angVel0, angVel1, + invDt, invDtp8, restDistance, maxPenBias, restitution, + bounceThreshold, contact, *solverContact, ccdMaxSeparation, solverOffsetSlop); + } + ptr = p; + } + } + + //construct all the frictions + + PxU8* PX_RESTRICT ptr2 = workspace; + + bool hasFriction = false; + for(PxU32 i=0;i< frictionPatchCount;i++) + { + const PxU32 contactCount = c.frictionPatchContactCounts[i]; + if(contactCount == 0) + continue; + + const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start; + + SolverContactCoulombHeader* header = reinterpret_cast(ptr2); + header->frictionOffset = PxU16(ptr - ptr2);// + sizeof(SolverFrictionHeader); + ptr2 += sizeof(SolverContactCoulombHeader) + header->numNormalConstr * pointStride; + + const PxReal staticFriction = contactBase0->staticFriction; + const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + const bool haveFriction = (disableStrongFriction == 0); + + SolverFrictionHeader* frictionHeader = reinterpret_cast(ptr); + frictionHeader->numNormalConstr = Ps::to8(c.frictionPatchContactCounts[i]); + frictionHeader->numFrictionConstr = Ps::to8(haveFriction ? c.frictionPatchContactCounts[i] * frictionPerPointCount : 0); + ptr += sizeof(SolverFrictionHeader); + PxF32* appliedForceBuffer = reinterpret_cast(ptr); + ptr += frictionHeader->getAppliedForcePaddingSize(c.frictionPatchContactCounts[i]); + PxMemZero(appliedForceBuffer, sizeof(PxF32)*contactCount*frictionPerPointCount); + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + + const Vec3V normal = V3LoadU(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); + + const FloatV normalX = V3GetX(normal); + const FloatV normalY = V3GetY(normal); + const FloatV normalZ = V3GetZ(normal); + + const Vec3V t0Fallback1 = V3Merge(zero, FNeg(normalZ), normalY); + const Vec3V t0Fallback2 = V3Merge(FNeg(normalY), normalX, zero) ; + + const BoolV con = FIsGrtr(orthoThreshold, FAbs(normalX)); + const Vec3V tFallback1 = V3Sel(con, t0Fallback1, t0Fallback2); + + const Vec3V linVrel = V3Sub(linVel0, linVel1); + const Vec3V t0_ = V3Sub(linVrel, V3Scale(normal, V3Dot(normal, linVrel))); + const FloatV sqDist = V3Dot(t0_,t0_); + const BoolV con1 = FIsGrtr(sqDist, eps); + const Vec3V tDir0 =V3Normalize(V3Sel(con1, t0_, tFallback1)); + const Vec3V tDir1 = V3Cross(tDir0, normal); + + Vec3V tFallback = tDir0; + Vec3V tFallbackAlt = tDir1; + + if(haveFriction) + { + //frictionHeader->setStaticFriction(n.staticFriction); + frictionHeader->setStaticFriction(staticFriction); + FStore(invMass0_dom0fV, &frictionHeader->invMass0D0); + FStore(FNeg(invMass1_dom1fV), &frictionHeader->invMass1D1); + FStore(angD0, &frictionHeader->angDom0); + FStore(angD1, &frictionHeader->angDom1); + frictionHeader->type = frictionHeaderType; + + PxU32 totalPatchContactCount = 0; + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const PxU32 start = c.contactPatches[patch].start; + const Gu::ContactPoint* contactBase = buffer.contacts + start; + + PxU8* p = ptr; + for(PxU32 j =0; j < count; j++) + { + hasFriction = true; + const Gu::ContactPoint& contact = contactBase[j]; + const Vec3V point = V3LoadU(contact.point); + Vec3V ra = V3Sub(point, bodyFrame0p); + Vec3V rb = V3Sub(point, bodyFrame1p); + ra = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(ra)), V3Zero(), ra); + rb = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rb)), V3Zero(), rb); + const Vec3V targetVel = V3LoadU(contact.targetVel); + + for(PxU32 k = 0; k < frictionPerPointCount; ++k) + { + const Vec3V t0 = tFallback; + tFallback = tFallbackAlt; + tFallbackAlt = t0; + + SolverContactFriction* PX_RESTRICT f0 = reinterpret_cast(p); + p += frictionStride; + //f0->brokenOrContactIndex = contactId; + + const Vec3V raXn = V3Cross(ra, t0); + const Vec3V rbXn = V3Cross(rb, t0); + + const Vec3V delAngVel0 = M33MulV3(invSqrtInertia0, raXn); + const Vec3V delAngVel1 = M33MulV3(invSqrtInertia1, rbXn); + + const FloatV resp0 = FAdd(invMass0_dom0fV, FMul(angD0, V3Dot(delAngVel0, delAngVel0))); + const FloatV resp1 = FSub(FMul(angD1, V3Dot(delAngVel1, delAngVel1)), invMass1_dom1fV); + const FloatV resp = FAdd(resp0, resp1); + + const FloatV velMultiplier = FNeg(FSel(FIsGrtr(resp, zero), FRecip(resp), zero)); + + const FloatV vrel1 = FAdd(V3Dot(t0, linVel0), V3Dot(raXn, angVel0)); + const FloatV vrel2 = FAdd(V3Dot(t0, linVel1), V3Dot(rbXn, angVel1)); + const FloatV vrel = FSub(vrel1, vrel2); + + + f0->normalXYZ_appliedForceW = V4SetW(Vec4V_From_Vec3V(t0), zero); + f0->raXnXYZ_velMultiplierW = V4SetW(Vec4V_From_Vec3V(delAngVel0), velMultiplier); + //f0->rbXnXYZ_targetVelocityW = V4SetW(Vec4V_From_Vec3V(delAngVel1), FSub(V3Dot(targetVel, t0), vrel)); + f0->rbXnXYZ_biasW = Vec4V_From_Vec3V(delAngVel1); + FStore(FSub(V3Dot(targetVel, t0), vrel), &f0->targetVel); + } + } + + totalPatchContactCount += c.contactPatches[patch].count; + + ptr = p; + } + } + } + *ptr = 0; + return hasFriction; +} + + + +static void computeBlockStreamByteSizesCoulomb(const CorrelationBuffer& c, + const PxU32 frictionCountPerPoint, PxU32& _solverConstraintByteSize, + PxU32& _axisConstraintCount, + bool useExtContacts) +{ + PX_ASSERT(0 == _solverConstraintByteSize); + PX_ASSERT(0 == _axisConstraintCount); + + // PT: use local vars to remove LHS + PxU32 solverConstraintByteSize = 0; + PxU32 numFrictionPatches = 0; + PxU32 axisConstraintCount = 0; + + for(PxU32 i = 0; i < c.frictionPatchCount; i++) + { + //Friction patches. + if(c.correlationListHeads[i] != CorrelationBuffer::LIST_END) + numFrictionPatches++; + + + const FrictionPatch& frictionPatch = c.frictionPatches[i]; + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0; + + //Solver constraint data. + if(c.frictionPatchContactCounts[i]!=0) + { + solverConstraintByteSize += sizeof(SolverContactCoulombHeader); + + solverConstraintByteSize += useExtContacts ? c.frictionPatchContactCounts[i] * sizeof(SolverContactPointExt) + : c.frictionPatchContactCounts[i] * sizeof(SolverContactPoint); + + axisConstraintCount += c.frictionPatchContactCounts[i]; + + //We always need the friction headers to write the accumulated + if(haveFriction) + { + //4 bytes + solverConstraintByteSize += sizeof(SolverFrictionHeader); + //buffer to store applied forces in + solverConstraintByteSize += SolverFrictionHeader::getAppliedForcePaddingSize(c.frictionPatchContactCounts[i]); + + const PxU32 nbFrictionConstraints = c.frictionPatchContactCounts[i] * frictionCountPerPoint; + + solverConstraintByteSize += useExtContacts ? nbFrictionConstraints * sizeof(SolverContactFrictionExt) + : nbFrictionConstraints * sizeof(SolverContactFriction); + axisConstraintCount += c.frictionPatchContactCounts[i]; + } + else + { + //reserve buffers for storing accumulated impulses + solverConstraintByteSize += sizeof(SolverFrictionHeader); + solverConstraintByteSize += SolverFrictionHeader::getAppliedForcePaddingSize(c.frictionPatchContactCounts[i]); + } + } + } + _axisConstraintCount = axisConstraintCount; + + //16-byte alignment. + _solverConstraintByteSize = ((solverConstraintByteSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); +} + +static bool reserveBlockStreamsCoulomb(const CorrelationBuffer& c, + PxU8*& solverConstraint, PxU32 frictionCountPerPoint, + PxU32& solverConstraintByteSize, + PxU32& axisConstraintCount, PxConstraintAllocator& constraintAllocator, + bool useExtContacts) +{ + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(0 == solverConstraintByteSize); + PX_ASSERT(0 == axisConstraintCount); + + + //From constraintBlockStream we need to reserve contact points, contact forces, and a char buffer for the solver constraint data (already have a variable for this). + //From frictionPatchStream we just need to reserve a single buffer. + + //Compute the sizes of all the buffers. + computeBlockStreamByteSizesCoulomb( + c, + frictionCountPerPoint, solverConstraintByteSize, + axisConstraintCount, useExtContacts); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if(constraintBlockByteSize > 0) + { + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if(0==constraintBlock || (reinterpret_cast(-1))==constraintBlock) + { + if(0==constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock=NULL; + } + } + } + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if(0==constraintBlockByteSize || constraintBlock) + { + if(solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0==(uintptr_t(solverConstraint) & 0x0f)); + } + } + + //Return true if neither of the two block reservations failed. + return ((0==constraintBlockByteSize || constraintBlock)); +} + +bool createFinalizeSolverContactsCoulomb1D(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z) +{ + return createFinalizeSolverContactsCoulomb(contactDesc, output, threadContext, invDtF32, bounceThresholdF32, frictionOffsetThreshold, correlationDistance, solverOffsetSlop, + constraintAllocator, PxFrictionType::eONE_DIRECTIONAL, Z); +} + +bool createFinalizeSolverContactsCoulomb2D(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z) + +{ + return createFinalizeSolverContactsCoulomb(contactDesc, output, threadContext, invDtF32, bounceThresholdF32, frictionOffsetThreshold, correlationDistance, solverOffsetSlop, + constraintAllocator, PxFrictionType::eTWO_DIRECTIONAL, Z); +} + +bool createFinalizeSolverContactsCoulomb(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + PxFrictionType::Enum frictionType, + Cm::SpatialVectorF* Z) +{ + PX_UNUSED(frictionOffsetThreshold); + PX_UNUSED(correlationDistance); + + PxSolverConstraintDesc& desc = *contactDesc.desc; + + desc.constraintLengthOver16 = 0; + + ContactBuffer& buffer = threadContext.mContactBuffer; + + buffer.count = 0; + + // We pull the friction patches out of the cache to remove the dependency on how + // the cache is organized. Remember original addrs so we can write them back + // efficiently. + + Ps::prefetchLine(contactDesc.frictionPtr); + + PxReal invMassScale0 = 1.f; + PxReal invMassScale1 = 1.f; + PxReal invInertiaScale0 = 1.f; + PxReal invInertiaScale1 = 1.f; + + bool hasMaxImpulse = false, hasTargetVelocity = false; + + PxU32 numContacts = extractContacts(buffer, output, hasMaxImpulse, hasTargetVelocity, invMassScale0, invMassScale1, + invInertiaScale0, invInertiaScale1, PxMin(contactDesc.data0->maxContactImpulse, contactDesc.data1->maxContactImpulse)); + + if(numContacts == 0) + { + contactDesc.frictionPtr = NULL; + contactDesc.frictionCount = 0; + return true; + } + + Ps::prefetchLine(contactDesc.body0); + Ps::prefetchLine(contactDesc.body1); + Ps::prefetchLine(contactDesc.data0); + Ps::prefetchLine(contactDesc.data1); + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + createContactPatches(c, buffer.contacts, buffer.count, PXC_SAME_NORMAL); + + PxU32 numFrictionPerPatch = PxU32(frictionType == PxFrictionType::eONE_DIRECTIONAL ? 1 : 2); + + bool overflow = correlatePatches(c, buffer.contacts, contactDesc.bodyFrame0, contactDesc.bodyFrame1, PXC_SAME_NORMAL, 0, 0); + PX_UNUSED(overflow); +#if PX_CHECKED + if(overflow) + { + Ps::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Dropping contacts in solver because we exceeded limit of 32 friction patches."); + } +#endif + + + //PX_ASSERT(patchCount == c.frictionPatchCount); + + PxU8* solverConstraint = NULL; + PxU32 solverConstraintByteSize = 0; + PxU32 axisConstraintCount = 0; + + bool useExtContacts = !!((contactDesc.bodyState0 | contactDesc.bodyState1) & PxSolverContactDesc::eARTICULATION); + + const bool successfulReserve = reserveBlockStreamsCoulomb( + c, + solverConstraint, numFrictionPerPatch, + solverConstraintByteSize, + axisConstraintCount, + constraintAllocator, + useExtContacts); + + // initialise the work unit's ptrs to the various buffers. + + contactDesc.frictionPtr = NULL; + desc.constraint = NULL; + desc.constraintLengthOver16 = 0; + contactDesc.frictionCount = 0; + + // patch up the work unit with the reserved buffers and set the reserved buffer data as appropriate. + + if(successfulReserve) + { + desc.constraint = solverConstraint; + output.nbContacts = Ps::to8(numContacts); + desc.constraintLengthOver16 = Ps::to16(solverConstraintByteSize/16); + + //Initialise solverConstraint buffer. + if(solverConstraint) + { + bool hasFriction = false; + if(useExtContacts) + { + const PxSolverBodyData& data0 = *contactDesc.data0; + const PxSolverBodyData& data1 = *contactDesc.data1; + + const SolverExtBody b0(reinterpret_cast(contactDesc.body0), reinterpret_cast(&data0), desc.linkIndexA); + const SolverExtBody b1(reinterpret_cast(contactDesc.body1), reinterpret_cast(&data1), desc.linkIndexB); + + hasFriction = setupFinalizeExtSolverContactsCoulomb(buffer, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + invDtF32, bounceThresholdF32, b0, b1, numFrictionPerPatch, + invMassScale0, invInertiaScale0, invMassScale1, invInertiaScale1, contactDesc.restDistance, contactDesc.maxCCDSeparation, Z); + } + else + { + const PxSolverBodyData& data0 = *contactDesc.data0; + const PxSolverBodyData& data1 = *contactDesc.data1; + + hasFriction = setupFinalizeSolverConstraintsCoulomb(contactDesc.shapeInteraction, buffer, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + data0, data1, invDtF32, bounceThresholdF32, numFrictionPerPatch, contactDesc.hasForceThresholds, contactDesc.bodyState1 == PxSolverContactDesc::eSTATIC_BODY, + invMassScale0, invInertiaScale0, invMassScale1, invInertiaScale1, contactDesc.restDistance, contactDesc.maxCCDSeparation, solverOffsetSlop); + } + *(reinterpret_cast(solverConstraint + solverConstraintByteSize)) = 0; + *(reinterpret_cast(solverConstraint + solverConstraintByteSize + 4)) = hasFriction ? 0xFFFFFFFF : 0; + } + } + + return successfulReserve; +} + +} +} + + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h new file mode 100644 index 000000000..e9684ba38 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h @@ -0,0 +1,308 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_CONTACT_PREP_SHARED_H +#define DY_CONTACT_PREP_SHARED_H + +#include "foundation/PxPreprocessor.h" +#include "PxSceneDesc.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DyContactPrep.h" +#include "DyCorrelationBuffer.h" +#include "DyArticulationContactPrep.h" +#include "PxsContactManager.h" +#include "PxsContactManagerState.h" + +namespace physx +{ +namespace Dy +{ + + +PX_FORCE_INLINE bool pointsAreClose(const PxTransform& body1ToBody0, + const PxVec3& localAnchor0, const PxVec3& localAnchor1, + const PxVec3& axis, float correlDist) +{ + const PxVec3 body0PatchPoint1 = body1ToBody0.transform(localAnchor1); + + return PxAbs((localAnchor0 - body0PatchPoint1).dot(axis))(frictionCookie); + + //Try working out relative transforms! TODO - can we compute this lazily for the first friction patch + bool evaluated = false; + PxTransform body1ToBody0; + + while(frictionPatchCount--) + { + Ps::prefetchLine(patches,128); + const FrictionPatch& patch = *patches++; + PX_ASSERT (patch.broken == 0 || patch.broken == 1); + if(!patch.broken) + { + // if the eDISABLE_STRONG_FRICTION flag is there we need to blow away the previous frame's friction correlation, so + // that we can associate each friction anchor with a target velocity. So we lose strong friction. + if(patch.anchorCount != 0 && !(patch.materialFlags & PxMaterialFlag::eDISABLE_STRONG_FRICTION)) + { + PX_ASSERT(patch.anchorCount <= 2); + + + if(!evaluated) + { + body1ToBody0 = bodyFrame0.transformInv(bodyFrame1); + evaluated = true; + } + + + if(patch.body0Normal.dot(body1ToBody0.rotate(patch.body1Normal)) > PXC_SAME_NORMAL) + { + if(!isSeparated(patch, body1ToBody0, correlationDistance)) + { + if(c.frictionPatchCount == CorrelationBuffer::MAX_FRICTION_PATCHES) + return false; + { + c.contactID[c.frictionPatchCount][0] = 0xffff; + c.contactID[c.frictionPatchCount][1] = 0xffff; + //Rotate the contact normal into world space + c.frictionPatchWorldNormal[c.frictionPatchCount] = bodyFrame0.rotate(patch.body0Normal); + c.frictionPatchContactCounts[c.frictionPatchCount] = 0; + c.patchBounds[c.frictionPatchCount].setEmpty(); + c.correlationListHeads[c.frictionPatchCount] = CorrelationBuffer::LIST_END; + PxMemCopy(&c.frictionPatches[c.frictionPatchCount++], &patch, sizeof(FrictionPatch)); + } + } + } + } + } + } + return true; +} + +PX_FORCE_INLINE PxU32 extractContacts(Gu::ContactBuffer& buffer, PxsContactManagerOutput& npOutput, bool& hasMaxImpulse, bool& hasTargetVelocity, + PxReal& invMassScale0, PxReal& invMassScale1, PxReal& invInertiaScale0, PxReal& invInertiaScale1, PxReal defaultMaxImpulse) +{ + PxContactStreamIterator iter(npOutput.contactPatches, npOutput.contactPoints, npOutput.getInternalFaceIndice(), npOutput.nbPatches, npOutput.nbContacts); + + PxU32 numContacts = buffer.count, origContactCount = buffer.count; + if(!iter.forceNoResponse) + { + invMassScale0 = iter.getInvMassScale0(); + invMassScale1 = iter.getInvMassScale1(); + invInertiaScale0 = iter.getInvInertiaScale0(); + invInertiaScale1 = iter.getInvInertiaScale1(); + hasMaxImpulse = (iter.patch->internalFlags & PxContactPatch::eHAS_MAX_IMPULSE) != 0; + hasTargetVelocity = (iter.patch->internalFlags & PxContactPatch::eHAS_TARGET_VELOCITY) != 0; + + while(iter.hasNextPatch()) + { + iter.nextPatch(); + while(iter.hasNextContact()) + { + iter.nextContact(); + Ps::prefetchLine(iter.contact, 128); + Ps::prefetchLine(&buffer.contacts[numContacts], 128); + PxReal maxImpulse = hasMaxImpulse ? iter.getMaxImpulse() : defaultMaxImpulse; + if(maxImpulse != 0.f) + { + PX_ASSERT(numContacts < Gu::ContactBuffer::MAX_CONTACTS); + buffer.contacts[numContacts].normal = iter.getContactNormal(); + buffer.contacts[numContacts].point = iter.getContactPoint(); + buffer.contacts[numContacts].separation = iter.getSeparation(); + //KS - we use the face indices to cache the material indices and flags - avoids bloating the PxContact structure + buffer.contacts[numContacts].materialFlags = PxU8(iter.getMaterialFlags()); + buffer.contacts[numContacts].maxImpulse = maxImpulse; + buffer.contacts[numContacts].staticFriction = iter.getStaticFriction(); + buffer.contacts[numContacts].dynamicFriction = iter.getDynamicFriction(); + buffer.contacts[numContacts].restitution = iter.getRestitution(); + const PxVec3& targetVel = iter.getTargetVel(); + buffer.contacts[numContacts].targetVel = targetVel; + ++numContacts; + } + } + } + } + const PxU32 contactCount = numContacts - origContactCount; + buffer.count = numContacts; + return contactCount; +} + +struct CorrelationListIterator +{ + CorrelationBuffer& buffer; + PxU32 currPatch; + PxU32 currContact; + + CorrelationListIterator(CorrelationBuffer& correlationBuffer, PxU32 startPatch) : buffer(correlationBuffer) + { + //We need to force us to advance the correlation buffer to the first available contact (if one exists) + PxU32 newPatch = startPatch, newContact = 0; + + while(newPatch != CorrelationBuffer::LIST_END && newContact == buffer.contactPatches[newPatch].count) + { + newPatch = buffer.contactPatches[newPatch].next; + newContact = 0; + } + + currPatch = newPatch; + currContact = newContact; + } + + //Returns true if it has another contact pre-loaded. Returns false otherwise + PX_FORCE_INLINE bool hasNextContact() + { + return (currPatch != CorrelationBuffer::LIST_END && currContact < buffer.contactPatches[currPatch].count); + } + + inline void nextContact(PxU32& patch, PxU32& contact) + { + PX_ASSERT(currPatch != CorrelationBuffer::LIST_END); + PX_ASSERT(currContact < buffer.contactPatches[currPatch].count); + + patch = currPatch; + contact = currContact; + PxU32 newPatch = currPatch, newContact = currContact + 1; + + while(newPatch != CorrelationBuffer::LIST_END && newContact == buffer.contactPatches[newPatch].count) + { + newPatch = buffer.contactPatches[newPatch].next; + newContact = 0; + } + + currPatch = newPatch; + currContact = newContact; + } + +private: + CorrelationListIterator& operator=(const CorrelationListIterator&); + +}; + + + PX_FORCE_INLINE void constructContactConstraint(const Mat33V& invSqrtInertia0, const Mat33V& invSqrtInertia1, const FloatVArg invMassNorLenSq0, + const FloatVArg invMassNorLenSq1, const FloatVArg angD0, const FloatVArg angD1, const Vec3VArg bodyFrame0p, const Vec3VArg bodyFrame1p, + const Vec3VArg normal, const FloatVArg norVel, const VecCrossV& norCross, const Vec3VArg angVel0, const Vec3VArg angVel1, + const FloatVArg invDt, const FloatVArg invDtp8, const FloatVArg restDistance, const FloatVArg maxPenBias, const FloatVArg restitution, + const FloatVArg bounceThreshold, const Gu::ContactPoint& contact, SolverContactPoint& solverContact, + const FloatVArg ccdMaxSeparation, const Vec3VArg solverOffsetSlop) + { + const FloatV zero = FZero(); + const Vec3V point = V3LoadA(contact.point); + const FloatV separation = FLoad(contact.separation); + + const FloatV cTargetVel = V3Dot(normal, V3LoadA(contact.targetVel)); + + const Vec3V ra = V3Sub(point, bodyFrame0p); + const Vec3V rb = V3Sub(point, bodyFrame1p); + + /*ra = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(ra)), V3Zero(), ra); + rb = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rb)), V3Zero(), rb);*/ + + Vec3V raXn = V3Cross(ra, norCross); + Vec3V rbXn = V3Cross(rb, norCross); + + raXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(raXn)), V3Zero(), raXn); + rbXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rbXn)), V3Zero(), rbXn); + + const Vec3V raXnSqrtInertia = M33MulV3(invSqrtInertia0, raXn); + const Vec3V rbXnSqrtInertia = M33MulV3(invSqrtInertia1, rbXn); + + const FloatV resp0 = FAdd(invMassNorLenSq0, FMul(V3Dot(raXnSqrtInertia, raXnSqrtInertia), angD0)); + const FloatV resp1 = FSub(FMul(V3Dot(rbXnSqrtInertia, rbXnSqrtInertia), angD1), invMassNorLenSq1); + + const FloatV unitResponse = FAdd(resp0, resp1); + + const FloatV vrel1 = FAdd(norVel, V3Dot(raXn, angVel0)); + const FloatV vrel2 = V3Dot(rbXn, angVel1); + const FloatV vrel = FSub(vrel1, vrel2); + + const FloatV velMultiplier = FSel(FIsGrtr(unitResponse, zero), FRecip(unitResponse), zero); + + const FloatV penetration = FSub(separation, restDistance); + + const FloatV penetrationInvDt = FMul(penetration, invDt); + + const FloatV penetrationInvDtPt8 = FMax(maxPenBias, FMul(penetration, invDtp8)); + + FloatV scaledBias = FMul(velMultiplier, penetrationInvDtPt8); + + const BoolV isGreater2 = BAnd(BAnd(FIsGrtr(restitution, zero), FIsGrtr(bounceThreshold, vrel)), FIsGrtr(FNeg(vrel), penetrationInvDt)); + + const BoolV ccdSeparationCondition = FIsGrtrOrEq(ccdMaxSeparation, penetration); + + scaledBias = FSel(BAnd(ccdSeparationCondition, isGreater2), zero, scaledBias); + + const FloatV sumVRel(vrel); + + FloatV targetVelocity = FAdd(cTargetVel, FSel(isGreater2, FMul(FNeg(sumVRel), restitution), zero)); + + //Note - we add on the initial target velocity + targetVelocity = FSub(targetVelocity, vrel); + + const FloatV biasedErr = FScaleAdd(targetVelocity, velMultiplier, FNeg(scaledBias)); + const FloatV unbiasedErr = FScaleAdd(targetVelocity, velMultiplier, FSel(isGreater2, zero, FNeg(FMax(scaledBias, zero)))); + //const FloatV unbiasedErr = FScaleAdd(targetVelocity, velMultiplier, FNeg(FMax(scaledBias, zero))); + + FStore(velMultiplier, &solverContact.velMultiplier); + FStore(biasedErr, &solverContact.biasedErr); + FStore(unbiasedErr, &solverContact.unbiasedErr); + solverContact.maxImpulse = contact.maxImpulse; + + solverContact.raXn = raXnSqrtInertia; + solverContact.rbXn = rbXnSqrtInertia; + } +} +} + +#endif //DY_CONTACT_PREP_SHARED_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h b/src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h new file mode 100644 index 000000000..8ac9d62d4 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h @@ -0,0 +1,409 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_CONTACT_REDUCTION_H +#define DY_CONTACT_REDUCTION_H + +#include "GuContactPoint.h" +#include "PxsMaterialManager.h" + +namespace physx +{ + + +namespace Dy +{ + +//KS - might be OK with 4 but 5 guarantees the deepest + 4 contacts that contribute to largest surface area +#define CONTACT_REDUCTION_MAX_CONTACTS 6 +#define CONTACT_REDUCTION_MAX_PATCHES 32 +#define PXS_NORMAL_TOLERANCE 0.995f +#define PXS_SEPARATION_TOLERANCE 0.001f + + + //A patch contains a normal, pair of material indices and a list of indices. These indices are + //used to index into the PxContact array that's passed by the user + struct ReducedContactPatch + { + PxU32 numContactPoints; + PxU32 contactPoints[CONTACT_REDUCTION_MAX_CONTACTS]; + }; + + struct ContactPatch + { + PxVec3 rootNormal; + ContactPatch* mNextPatch; + PxReal maxPenetration; + PxU16 startIndex; + PxU16 stride; + PxU16 rootIndex; + PxU16 index; + }; + + struct SortBoundsPredicateManifold + { + bool operator()(const ContactPatch* idx1, const ContactPatch* idx2) const + { + return idx1->maxPenetration < idx2->maxPenetration; + } + }; + + + + template + class ContactReduction + { + public: + ReducedContactPatch mPatches[MaxPatches]; + PxU32 mNumPatches; + ContactPatch mIntermediatePatches[CONTACT_REDUCTION_MAX_PATCHES]; + ContactPatch* mIntermediatePatchesPtrs[CONTACT_REDUCTION_MAX_PATCHES]; + PxU32 mNumIntermediatePatches; + Gu::ContactPoint* PX_RESTRICT mOriginalContacts; + PxsMaterialInfo* PX_RESTRICT mMaterialInfo; + PxU32 mNumOriginalContacts; + + ContactReduction(Gu::ContactPoint* PX_RESTRICT originalContacts, PxsMaterialInfo* PX_RESTRICT materialInfo, PxU32 numContacts) : + mNumPatches(0), mNumIntermediatePatches(0), mOriginalContacts(originalContacts), mMaterialInfo(materialInfo), mNumOriginalContacts(numContacts) + { + } + + void reduceContacts() + { + //First pass, break up into contact patches, storing the start and stride of the patches + //We will need to have contact patches and then coallesce them + mIntermediatePatches[0].rootNormal = mOriginalContacts[0].normal; + mIntermediatePatches[0].mNextPatch = NULL; + mIntermediatePatches[0].startIndex = 0; + mIntermediatePatches[0].rootIndex = 0; + mIntermediatePatches[0].maxPenetration = mOriginalContacts[0].separation; + mIntermediatePatches[0].index = 0; + PxU16 numPatches = 1; + //PxU32 startIndex = 0; + PxU32 numUniquePatches = 1; + PxU16 m = 1; + for(; m < mNumOriginalContacts; ++m) + { + PxI32 index = -1; + for(PxU32 b = numPatches; b > 0; --b) + { + ContactPatch& patch = mIntermediatePatches[b-1]; + if(mMaterialInfo[patch.startIndex].mMaterialIndex0 == mMaterialInfo[m].mMaterialIndex0 && mMaterialInfo[patch.startIndex].mMaterialIndex1 == mMaterialInfo[m].mMaterialIndex1 && + patch.rootNormal.dot(mOriginalContacts[m].normal) >= PXS_NORMAL_TOLERANCE) + { + index = PxI32(b-1); + break; + } + } + + if(index != numPatches - 1) + { + mIntermediatePatches[numPatches-1].stride = PxU16(m - mIntermediatePatches[numPatches - 1].startIndex); + //Create a new patch... + if(numPatches == CONTACT_REDUCTION_MAX_PATCHES) + { + break; + } + mIntermediatePatches[numPatches].startIndex = m; + mIntermediatePatches[numPatches].mNextPatch = NULL; + if(index == -1) + { + mIntermediatePatches[numPatches].rootIndex = numPatches; + mIntermediatePatches[numPatches].rootNormal = mOriginalContacts[m].normal; + mIntermediatePatches[numPatches].maxPenetration = mOriginalContacts[m].separation; + mIntermediatePatches[numPatches].index = numPatches; + ++numUniquePatches; + } + else + { + //Find last element in the link + PxU16 rootIndex = mIntermediatePatches[index].rootIndex; + mIntermediatePatches[index].mNextPatch = &mIntermediatePatches[numPatches]; + mIntermediatePatches[numPatches].rootNormal = mIntermediatePatches[index].rootNormal; + mIntermediatePatches[rootIndex].maxPenetration = mIntermediatePatches[numPatches].maxPenetration = PxMin(mIntermediatePatches[rootIndex].maxPenetration, mOriginalContacts[m].separation); + mIntermediatePatches[numPatches].rootIndex = rootIndex; + mIntermediatePatches[numPatches].index = numPatches; + } + ++numPatches; + } + } + mIntermediatePatches[numPatches-1].stride = PxU16(m - mIntermediatePatches[numPatches-1].startIndex); + + //OK, we have a list of contact patches so that we can start contact reduction per-patch + + //OK, now we can go and reduce the contacts on a per-patch basis... + + for(PxU32 a = 0; a < numPatches; ++a) + { + mIntermediatePatchesPtrs[a] = &mIntermediatePatches[a]; + } + + + SortBoundsPredicateManifold predicate; + Ps::sort(mIntermediatePatchesPtrs, numPatches, predicate); + + PxU32 numReducedPatches = 0; + for(PxU32 a = 0; a < numPatches; ++a) + { + if(mIntermediatePatchesPtrs[a]->rootIndex == mIntermediatePatchesPtrs[a]->index) + { + //Reduce this patch... + if(numReducedPatches == MaxPatches) + break; + + ReducedContactPatch& reducedPatch = mPatches[numReducedPatches++]; + //OK, now we need to work out if we have to reduce patches... + PxU32 contactCount = 0; + { + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + + while(tmpPatch) + { + contactCount += tmpPatch->stride; + tmpPatch = tmpPatch->mNextPatch; + } + } + + if(contactCount <= CONTACT_REDUCTION_MAX_CONTACTS) + { + //Just add the contacts... + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + + PxU32 ind = 0; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + reducedPatch.contactPoints[ind++] = tmpPatch->startIndex + b; + } + tmpPatch = tmpPatch->mNextPatch; + } + reducedPatch.numContactPoints = contactCount; + } + else + { + //Iterate through and find the most extreme point + + + PxU32 ind = 0; + + { + PxReal dist = 0.f; + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + PxReal magSq = mOriginalContacts[tmpPatch->startIndex + b].point.magnitudeSquared(); + if(dist < magSq) + { + ind = tmpPatch->startIndex + b; + dist = magSq; + } + } + tmpPatch = tmpPatch->mNextPatch; + } + } + reducedPatch.contactPoints[0] = ind; + const PxVec3 p0 = mOriginalContacts[ind].point; + + //Now find the point farthest from this point... + { + PxReal maxDist = 0.f; + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + PxReal magSq = (p0 - mOriginalContacts[tmpPatch->startIndex + b].point).magnitudeSquared(); + if(magSq > maxDist) + { + ind = tmpPatch->startIndex + b; + maxDist = magSq; + } + } + tmpPatch = tmpPatch->mNextPatch; + } + } + reducedPatch.contactPoints[1] = ind; + const PxVec3 p1 = mOriginalContacts[ind].point; + + //Now find the point farthest from the segment + + PxVec3 n = (p0 - p1).cross(mIntermediatePatchesPtrs[a]->rootNormal); + + //PxReal tVal = 0.f; + { + PxReal maxDist = 0.f; + //PxReal tmpTVal; + + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + + //PxReal magSq = tmpDistancePointSegmentSquared(p0, p1, mOriginalContacts[tmpPatch->startIndex + b].point, tmpTVal); + PxReal magSq = (mOriginalContacts[tmpPatch->startIndex + b].point - p0).dot(n); + if(magSq > maxDist) + { + ind = tmpPatch->startIndex + b; + //tVal = tmpTVal; + maxDist = magSq; + } + } + tmpPatch = tmpPatch->mNextPatch; + } + } + reducedPatch.contactPoints[2] = ind; + + //const PxVec3 closest = (p0 + (p1 - p0) * tVal); + + const PxVec3 dir = -n;//closest - p3; + + { + PxReal maxDist = 0.f; + //PxReal tVal = 0.f; + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + PxReal magSq = (mOriginalContacts[tmpPatch->startIndex + b].point - p0).dot(dir); + if(magSq > maxDist) + { + ind = tmpPatch->startIndex + b; + maxDist = magSq; + } + } + tmpPatch = tmpPatch->mNextPatch; + } + } + reducedPatch.contactPoints[3] = ind; + + //Now, we iterate through all the points, and cluster the points. From this, we establish the deepest point that's within a + //tolerance of this point and keep that point + + PxReal separation[CONTACT_REDUCTION_MAX_CONTACTS]; + PxU32 deepestInd[CONTACT_REDUCTION_MAX_CONTACTS]; + for(PxU32 i = 0; i < 4; ++i) + { + PxU32 index = reducedPatch.contactPoints[i]; + separation[i] = mOriginalContacts[index].separation - PXS_SEPARATION_TOLERANCE; + deepestInd[i] = index; + } + + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + Gu::ContactPoint& point = mOriginalContacts[tmpPatch->startIndex + b]; + + PxReal distance = PX_MAX_REAL; + PxU32 index = 0; + for(PxU32 c = 0; c < 4; ++c) + { + PxVec3 dif = mOriginalContacts[reducedPatch.contactPoints[c]].point - point.point; + PxReal d = dif.magnitudeSquared(); + if(distance > d) + { + distance = d; + index = c; + } + } + if(separation[index] > point.separation) + { + deepestInd[index] = tmpPatch->startIndex+b; + separation[index] = point.separation; + } + + } + tmpPatch = tmpPatch->mNextPatch; + } + + bool chosen[64]; + PxMemZero(chosen, sizeof(chosen)); + for(PxU32 i = 0; i < 4; ++i) + { + reducedPatch.contactPoints[i] = deepestInd[i]; + chosen[deepestInd[i]] = true; + } + + for(PxU32 i = 4; i < CONTACT_REDUCTION_MAX_CONTACTS; ++i) + { + separation[i] = PX_MAX_REAL; + deepestInd[i] = 0; + } + tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + if(!chosen[tmpPatch->startIndex+b]) + { + Gu::ContactPoint& point = mOriginalContacts[tmpPatch->startIndex + b]; + for(PxU32 j = 4; j < CONTACT_REDUCTION_MAX_CONTACTS; ++j) + { + if(point.separation < separation[j]) + { + for(PxU32 k = CONTACT_REDUCTION_MAX_CONTACTS-1; k > j; --k) + { + separation[k] = separation[k-1]; + deepestInd[k] = deepestInd[k-1]; + } + separation[j] = point.separation; + deepestInd[j] = tmpPatch->startIndex+b; + break; + } + } + } + } + tmpPatch = tmpPatch->mNextPatch; + } + + for(PxU32 i = 4; i < CONTACT_REDUCTION_MAX_CONTACTS; ++i) + { + reducedPatch.contactPoints[i] = deepestInd[i]; + } + + reducedPatch.numContactPoints = CONTACT_REDUCTION_MAX_CONTACTS; + } + } + } + mNumPatches = numReducedPatches; + } + + }; +} + +} + + +#endif //DY_CONTACT_REDUCTION_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h b/src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h new file mode 100644 index 000000000..4a091de52 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h @@ -0,0 +1,107 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_CORRELATIONBUFFER_H +#define DY_CORRELATIONBUFFER_H + +#include "PxvConfig.h" +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "DyFrictionPatch.h" +#include "GuContactBuffer.h" +#include "foundation/PxBounds3.h" + +namespace physx +{ + +struct PxcNpWorkUnit; +struct PxsMaterialInfo; + +namespace Dy +{ + +struct CorrelationBuffer +{ + static const PxU32 MAX_FRICTION_PATCHES = 32; + static const PxU16 LIST_END = 0xffff; + + struct ContactPatchData + { + PxU16 start; + PxU16 next; + PxU8 flags; + PxU8 count; + PxReal staticFriction, dynamicFriction, restitution; + PxBounds3 patchBounds; + }; + + // we can have as many contact patches as contacts, unfortunately + ContactPatchData contactPatches[Gu::ContactBuffer::MAX_CONTACTS]; + + FrictionPatch PX_ALIGN(16, frictionPatches[MAX_FRICTION_PATCHES]); + PxVec3 PX_ALIGN(16, frictionPatchWorldNormal[MAX_FRICTION_PATCHES]); + PxBounds3 patchBounds[MAX_FRICTION_PATCHES]; + + PxU32 frictionPatchContactCounts[MAX_FRICTION_PATCHES]; + PxU32 correlationListHeads[MAX_FRICTION_PATCHES+1]; + + // contact IDs are only used to identify auxiliary contact data when velocity + // targets have been set. + PxU16 contactID[MAX_FRICTION_PATCHES][2]; + + PxU32 contactPatchCount, frictionPatchCount; + +}; + +bool createContactPatches(CorrelationBuffer& fb, const Gu::ContactPoint* cb, PxU32 contactCount, PxReal normalTolerance); + +bool correlatePatches(CorrelationBuffer& fb, + const Gu::ContactPoint* cb, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxReal normalTolerance, + PxU32 startContactPatchIndex, + PxU32 startFrictionPatchIndex); + +void growPatches(CorrelationBuffer& fb, + const Gu::ContactPoint* buffer, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxReal normalTolerance, + PxU32 frictionPatchStartIndex, + PxReal frictionOffsetThreshold); + +} + +} + +#endif //DY_CORRELATIONBUFFER_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp new file mode 100644 index 000000000..46df6a084 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp @@ -0,0 +1,3145 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsTime.h" +#include "PsAtomic.h" +#include "PxvDynamics.h" + +#include "common/PxProfileZone.h" +#include "PxsRigidBody.h" +#include "PxsContactManager.h" +#include "DyDynamics.h" +#include "DyBodyCoreIntegrator.h" +#include "DySolverCore.h" +#include "DySolverControl.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DyArticulationContactPrep.h" +#include "DySolverBody.h" + +#include "DyConstraintPrep.h" +#include "DyConstraintPartition.h" +#include "DyArticulation.h" + +#include "CmFlushPool.h" +#include "DyArticulationPImpl.h" +#include "PxsMaterialManager.h" +#include "DySolverContactPF4.h" +#include "DyContactReduction.h" +#include "PxcNpContactPrepShared.h" +#include "DyContactPrep.h" +#include "DySolverControlPF.h" +#include "PxSceneDesc.h" +#include "PxsSimpleIslandManager.h" +#include "PxvNphaseImplementationContext.h" +#include "PxvSimStats.h" +#include "PxsContactManagerState.h" +#include "PxsDefaultMemoryManager.h" +#include "DyContactPrepShared.h" + +//KS - used to turn on/off batched SIMD constraints. +#define DY_BATCH_CONSTRAINTS 1 +//KS - used to specifically turn on/off batches 1D SIMD constraints. +#define DY_BATCH_1D 1 + +namespace physx +{ +namespace Dy +{ + +struct SolverIslandObjects +{ + PxsRigidBody** bodies; + ArticulationV** articulations; + Dy::ArticulationV** articulationOwners; + PxsIndexedContactManager* contactManagers; + //PxsIndexedConstraint* constraints; + + const IG::IslandId* islandIds; + PxU32 numIslands; + PxU32* bodyRemapTable; + PxU32* nodeIndexArray; + + PxSolverConstraintDesc* constraintDescs; + PxSolverConstraintDesc* orderedConstraintDescs; + PxSolverConstraintDesc* tempConstraintDescs; + PxConstraintBatchHeader* constraintBatchHeaders; + Cm::SpatialVector* motionVelocities; + PxsBodyCore** bodyCoreArray; + + SolverIslandObjects() : bodies(NULL), articulations(NULL), articulationOwners(NULL), + contactManagers(NULL), islandIds(NULL), numIslands(0), nodeIndexArray(NULL), constraintDescs(NULL), orderedConstraintDescs(NULL), + tempConstraintDescs(NULL), constraintBatchHeaders(NULL), motionVelocities(NULL), bodyCoreArray(NULL) + { + } +}; + +Context* createDynamicsContext( PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, Cm::FlushPool& taskPool, + PxvSimStats& simStats, PxTaskManager* taskManager, Ps::VirtualAllocatorCallback* allocatorCallback, + PxsMaterialManager* materialManager, IG::IslandSim* accurateIslandSim, PxU64 contextID, + const bool enableStabilization, const bool useEnhancedDeterminism, const bool useAdaptiveForce, + const PxReal maxBiasCoefficient, const bool frictionEveryIteration + ) +{ + return DynamicsContext::create( memBlockPool, scratchAllocator, taskPool, simStats, taskManager, allocatorCallback, materialManager, accurateIslandSim, + contextID, enableStabilization, useEnhancedDeterminism, useAdaptiveForce, maxBiasCoefficient, frictionEveryIteration); +} + +// PT: TODO: consider removing this function. We already have "createDynamicsContext". +DynamicsContext* DynamicsContext::create( PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocatorCallback, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal maxBiasCoefficient, + const bool frictionEveryIteration + ) +{ + // PT: TODO: inherit from UserAllocated, remove placement new + DynamicsContext* dc = reinterpret_cast(PX_ALLOC(sizeof(DynamicsContext), "DynamicsContext")); + if(dc) + { + new(dc)DynamicsContext(memBlockPool, scratchAllocator, taskPool, simStats, taskManager, allocatorCallback, materialManager, accurateIslandSim, contextID, + enableStabilization, useEnhancedDeterminism, useAdaptiveForce, maxBiasCoefficient, frictionEveryIteration); + } + return dc; +} + + +void DynamicsContext::destroy() +{ + this->~DynamicsContext(); + PX_FREE(this); +} + +void DynamicsContext::resetThreadContexts() +{ + PxcThreadCoherentCacheIterator threadContextIt(mThreadContextPool); + ThreadContext* threadContext = threadContextIt.getNext(); + + while(threadContext != NULL) + { + threadContext->reset(); + threadContext = threadContextIt.getNext(); + } +} + + +// =========================== Basic methods + + +DynamicsContext::DynamicsContext( PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocatorCallback, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal maxBiasCoefficient, + const bool frictionEveryIteration + ) : + Dy::Context (accurateIslandSim, allocatorCallback, simStats, enableStabilization, useEnhancedDeterminism, useAdaptiveForce, maxBiasCoefficient), + mThreadContextPool (memBlockPool), + mMaterialManager (materialManager), + mScratchAllocator (scratchAllocator), + mTaskPool (taskPool), + mTaskManager (taskManager), + mContextID (contextID) +{ + createThresholdStream(*allocatorCallback); + createForceChangeThresholdStream(*allocatorCallback); + mExceededForceThresholdStream[0] = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ExceededForceThresholdStream[0]")), ThresholdStream(*allocatorCallback)); + mExceededForceThresholdStream[1] = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ExceededForceThresholdStream[1]")), ThresholdStream(*allocatorCallback)); + mThresholdStreamOut = 0; + mCurrentIndex = 0; + mWorldSolverBody.linearVelocity = PxVec3(0); + mWorldSolverBody.angularState = PxVec3(0); + mWorldSolverBodyData.invMass = 0; + mWorldSolverBodyData.sqrtInvInertia = PxMat33(PxZero); + mWorldSolverBodyData.nodeIndex = IG_INVALID_NODE; + mWorldSolverBodyData.reportThreshold = PX_MAX_REAL; + mWorldSolverBodyData.penBiasClamp = -PX_MAX_REAL; + mWorldSolverBodyData.maxContactImpulse = PX_MAX_REAL; + mWorldSolverBody.solverProgress=MAX_PERMITTED_SOLVER_PROGRESS; + mWorldSolverBody.maxSolverNormalProgress=MAX_PERMITTED_SOLVER_PROGRESS; + mWorldSolverBody.maxSolverFrictionProgress=MAX_PERMITTED_SOLVER_PROGRESS; + mWorldSolverBodyData.linearVelocity = mWorldSolverBodyData.angularVelocity = PxVec3(0.f); + mWorldSolverBodyData.body2World = PxTransform(PxIdentity); + mWorldSolverBodyData.lockFlags = 0; + mSolverCore[PxFrictionType::ePATCH] = SolverCoreGeneral::create(frictionEveryIteration); + mSolverCore[PxFrictionType::eONE_DIRECTIONAL] = SolverCoreGeneralPF::create(); + mSolverCore[PxFrictionType::eTWO_DIRECTIONAL] = SolverCoreGeneralPF::create(); +} + +DynamicsContext::~DynamicsContext() +{ + for(PxU32 i = 0; i < PxFrictionType::eFRICTION_COUNT; ++i) + { + mSolverCore[i]->destroyV(); + } + + if(mExceededForceThresholdStream[0]) + { + mExceededForceThresholdStream[0]->~ThresholdStream(); + PX_FREE(mExceededForceThresholdStream[0]); + } + mExceededForceThresholdStream[0] = NULL; + + if(mExceededForceThresholdStream[1]) + { + mExceededForceThresholdStream[1]->~ThresholdStream(); + PX_FREE(mExceededForceThresholdStream[1]); + } + mExceededForceThresholdStream[1] = NULL; + +} + +#if PX_ENABLE_SIM_STATS +void DynamicsContext::addThreadStats(const ThreadContext::ThreadSimStats& stats) +{ + mSimStats.mNbActiveConstraints += stats.numActiveConstraints; + mSimStats.mNbActiveDynamicBodies += stats.numActiveDynamicBodies; + mSimStats.mNbActiveKinematicBodies += stats.numActiveKinematicBodies; + mSimStats.mNbAxisSolverConstraints += stats.numAxisSolverConstraints; +} +#endif + +// =========================== Solve methods! + +void DynamicsContext::setDescFromIndices(PxSolverConstraintDesc& desc, const PxsIndexedInteraction& constraint, const PxU32 solverBodyOffset) +{ + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eBODY == 0); + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eKINEMATIC == 1); + const PxU32 offsetMap[] = {solverBodyOffset, 0}; + //const PxU32 offsetMap[] = {mKinematicCount, 0}; + + if(constraint.indexType0 == PxsIndexedInteraction::eARTICULATION) + { + ArticulationV* a = getArticulation(constraint.articulation0); + desc.articulationA = a; + desc.articulationALength = Ps::to16(a->getSolverDataSize()); + PX_ASSERT(0==(desc.articulationALength & 0x0f)); + desc.linkIndexA = Ps::to16(a->getLinkIndex(constraint.articulation0)); + } + else + { + desc.linkIndexA = PxSolverConstraintDesc::NO_LINK; + //desc.articulationALength = 0; //this is unioned with bodyADataIndex + /*desc.bodyA = constraint.indexType0 == PxsIndexedInteraction::eWORLD ? &mWorldSolverBody + : &mSolverBodyPool[(PxU32)constraint.solverBody0 + offsetMap[constraint.indexType0]]; + desc.bodyADataIndex = PxU16(constraint.indexType0 == PxsIndexedInteraction::eWORLD ? 0 + : (PxU16)constraint.solverBody0 + 1 + offsetMap[constraint.indexType0]);*/ + + desc.bodyA = constraint.indexType0 == PxsIndexedInteraction::eWORLD ? &mWorldSolverBody + : &mSolverBodyPool[PxU32(constraint.solverBody0) + offsetMap[constraint.indexType0]]; + desc.bodyADataIndex = constraint.indexType0 == PxsIndexedInteraction::eWORLD ? 0 + : PxU32(constraint.solverBody0) + 1 + offsetMap[constraint.indexType0]; + } + + if(constraint.indexType1 == PxsIndexedInteraction::eARTICULATION) + { + ArticulationV* b = getArticulation(constraint.articulation1); + desc.articulationB = b; + desc.articulationBLength = Ps::to16(b->getSolverDataSize()); + PX_ASSERT(0==(desc.articulationBLength & 0x0f)); + desc.linkIndexB = Ps::to16(b->getLinkIndex(constraint.articulation1)); + } + else + { + desc.linkIndexB = PxSolverConstraintDesc::NO_LINK; + //desc.articulationBLength = 0; //this is unioned with bodyBDataIndex + desc.bodyB = constraint.indexType1 == PxsIndexedInteraction::eWORLD ? &mWorldSolverBody + : &mSolverBodyPool[PxU32(constraint.solverBody1) + offsetMap[constraint.indexType1]]; + desc.bodyBDataIndex = constraint.indexType1 == PxsIndexedInteraction::eWORLD ? 0 + : PxU32(constraint.solverBody1) + 1 + offsetMap[constraint.indexType1]; + } +} + +void DynamicsContext::setDescFromIndices(PxSolverConstraintDesc& desc, IG::EdgeIndex edgeIndex, const IG::SimpleIslandManager& islandManager, + PxU32* bodyRemap, const PxU32 solverBodyOffset) +{ + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eBODY == 0); + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eKINEMATIC == 1); + + const IG::IslandSim& islandSim = islandManager.getAccurateIslandSim(); + + IG::NodeIndex node1 = islandSim.getNodeIndex1(edgeIndex); + if (node1.isStaticBody()) + { + desc.bodyA = &mWorldSolverBody; + desc.bodyADataIndex = 0; + desc.linkIndexA = PxSolverConstraintDesc::NO_LINK; + } + else + { + const IG::Node& node = islandSim.getNode(node1); + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + Dy::ArticulationV* a = islandSim.getLLArticulation(node1); + desc.articulationA = a; + desc.articulationALength = Ps::to16(a->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationALength & 0x0f)); + desc.linkIndexA = Ps::to16(node1.articulationLinkId()); + } + else + { + PxU32 activeIndex = islandSim.getActiveNodeIndex(node1); + PxU32 index = node.isKinematic() ? activeIndex : bodyRemap[activeIndex] + solverBodyOffset; + desc.bodyA = &mSolverBodyPool[index]; + desc.bodyADataIndex = Ps::to16(index + 1); + desc.linkIndexA = PxSolverConstraintDesc::NO_LINK; + } + } + + IG::NodeIndex node2 = islandSim.getNodeIndex2(edgeIndex); + if (node2.isStaticBody()) + { + desc.bodyB = &mWorldSolverBody; + desc.bodyBDataIndex = 0; + desc.linkIndexB = PxSolverConstraintDesc::NO_LINK; + } + else + { + const IG::Node& node = islandSim.getNode(node2); + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + Dy::ArticulationV* b = islandSim.getLLArticulation(node2); + desc.articulationB = b; + desc.articulationBLength = Ps::to16(b->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationBLength & 0x0f)); + desc.linkIndexB = Ps::to16(node2.articulationLinkId()); + } + else + { + PxU32 activeIndex = islandSim.getActiveNodeIndex(node2); + PxU32 index = node.isKinematic() ? activeIndex : bodyRemap[activeIndex] + solverBodyOffset; + desc.bodyB = &mSolverBodyPool[index]; + desc.bodyBDataIndex = Ps::to16(index + 1); + desc.linkIndexB = PxSolverConstraintDesc::NO_LINK; + } + } +} + + +class PxsPreIntegrateTask : public Cm::Task +{ + PxsPreIntegrateTask& operator=(const PxsPreIntegrateTask&); +public: + PxsPreIntegrateTask( DynamicsContext& context, + PxsBodyCore*const* bodyArray, + PxsRigidBody*const* originalBodyArray, + PxU32 const* nodeIndexArray, + PxSolverBody* solverBodies, + PxSolverBodyData* solverBodyDataPool, + PxF32 dt, + PxU32 numBodies, + volatile PxU32* maxSolverPositionIterations, + volatile PxU32* maxSolverVelocityIterations, + const PxU32 startIndex, + const PxU32 numToIntegrate, + const PxVec3& gravity) : + Cm::Task (context.getContextId()), + mContext (context), + mBodyArray (bodyArray), + mOriginalBodyArray (originalBodyArray), + mNodeIndexArray (nodeIndexArray), + mSolverBodies (solverBodies), + mSolverBodyDataPool (solverBodyDataPool), + mDt (dt), + mNumBodies (numBodies), + mMaxSolverPositionIterations(maxSolverPositionIterations), + mMaxSolverVelocityIterations(maxSolverVelocityIterations), + mStartIndex (startIndex), + mNumToIntegrate (numToIntegrate), + mGravity (gravity) + {} + + virtual void runInternal(); + + virtual const char* getName() const + { + return "PxsDynamics.preIntegrate"; + } + +public: + DynamicsContext& mContext; + PxsBodyCore*const* mBodyArray; + PxsRigidBody*const* mOriginalBodyArray; + PxU32 const* mNodeIndexArray; + PxSolverBody* mSolverBodies; + PxSolverBodyData* mSolverBodyDataPool; + PxF32 mDt; + PxU32 mNumBodies; + volatile PxU32* mMaxSolverPositionIterations; + volatile PxU32* mMaxSolverVelocityIterations; + PxU32 mStartIndex; + PxU32 mNumToIntegrate; + PxVec3 mGravity; + +}; + + + +class PxsParallelSolverTask : public Cm::Task +{ + PxsParallelSolverTask& operator=(PxsParallelSolverTask&); +public: + + PxsParallelSolverTask(SolverIslandParams& params, DynamicsContext& context, PxFrictionType::Enum frictionType, IG::IslandSim& islandSim) + : Cm::Task(context.getContextId()), mParams(params), mContext(context), mFrictionType(frictionType), mIslandSim(islandSim) + { + } + + virtual void runInternal() + { + solveParallel(mContext, mParams, mIslandSim); + } + + virtual const char* getName() const + { + return "PxsDynamics.parallelSolver"; + } + + SolverIslandParams& mParams; + DynamicsContext& mContext; + PxFrictionType::Enum mFrictionType; + IG::IslandSim& mIslandSim; +}; + + +#define PX_CONTACT_REDUCTION 1 + +class PxsSolverConstraintPostProcessTask : public Cm::Task +{ + PxsSolverConstraintPostProcessTask& operator=(const PxsSolverConstraintPostProcessTask&); +public: + + PxsSolverConstraintPostProcessTask(DynamicsContext& context, + ThreadContext& threadContext, + const SolverIslandObjects& objects, + const PxU32 solverBodyOffset, + PxU32 startIndex, + PxU32 stride, + PxsMaterialManager* materialManager, + PxsContactManagerOutputIterator& iterator) : + Cm::Task (context.getContextId()), + mContext (context), + mThreadContext (threadContext), + mObjects (objects), + mSolverBodyOffset (solverBodyOffset), + mStartIndex (startIndex), + mStride (stride), + mMaterialManager (materialManager), + mOutputs (iterator) + {} + + void mergeContacts(CompoundContactManager& header, ThreadContext& threadContext) + { + Gu::ContactBuffer& buffer = threadContext.mContactBuffer; + PxsMaterialInfo materialInfo[Gu::ContactBuffer::MAX_CONTACTS]; + PxU32 size = 0; + + for(PxU32 a = 0; a < header.mStride; ++a) + { + PxsContactManager* manager = mThreadContext.orderedContactList[a+header.mStartIndex]->contactManager; + PxcNpWorkUnit& unit = manager->getWorkUnit(); + PxsContactManagerOutput& output = mOutputs.getContactManager(unit.mNpIndex); + PxContactStreamIterator iter(output.contactPatches, output.contactPoints, output.getInternalFaceIndice(), output.nbPatches, output.nbContacts); + + PxU32 origSize = size; + PX_UNUSED(origSize); + if(!iter.forceNoResponse) + { + while(iter.hasNextPatch()) + { + iter.nextPatch(); + while(iter.hasNextContact()) + { + PX_ASSERT(size < Gu::ContactBuffer::MAX_CONTACTS); + iter.nextContact(); + PxsMaterialInfo& info = materialInfo[size]; + Gu::ContactPoint& point = buffer.contacts[size++]; + point.dynamicFriction = iter.getDynamicFriction(); + point.staticFriction = iter.getStaticFriction(); + point.restitution = iter.getRestitution(); + point.internalFaceIndex1 = iter.getFaceIndex1(); + point.materialFlags = PxU8(iter.getMaterialFlags()); + point.maxImpulse = iter.getMaxImpulse(); + point.targetVel = iter.getTargetVel(); + point.normal = iter.getContactNormal(); + point.point = iter.getContactPoint(); + point.separation = iter.getSeparation(); + info.mMaterialIndex0 = iter.getMaterialIndex0(); + info.mMaterialIndex1 = iter.getMaterialIndex1(); + } + } + PX_ASSERT(output.nbContacts == (size - origSize)); + } + } + + PxU32 origSize = size; +#if PX_CONTACT_REDUCTION + ContactReduction<6> reduction(buffer.contacts, materialInfo, size); + reduction.reduceContacts(); + //OK, now we write back the contacts... + + PxU8 histo[Gu::ContactBuffer::MAX_CONTACTS]; + PxMemZero(histo, sizeof(histo)); + + size = 0; + for(PxU32 a = 0; a < reduction.mNumPatches; ++a) + { + ReducedContactPatch& patch = reduction.mPatches[a]; + for(PxU32 b = 0; b < patch.numContactPoints; ++b) + { + histo[patch.contactPoints[b]] = 1; + ++size; + } + } +#endif + + PxU16* PX_RESTRICT data = reinterpret_cast(threadContext.mConstraintBlockStream.reserve(size * sizeof(PxU16), mThreadContext.mConstraintBlockManager)); + header.forceBufferList = data; + + +#if PX_CONTACT_REDUCTION + const PxU32 reservedSize = size; + PX_UNUSED(reservedSize); + size = 0; + for(PxU32 a = 0; a < origSize; ++a) + { + if(histo[a]) + { + if(size != a) + { + buffer.contacts[size] = buffer.contacts[a]; + materialInfo[size] = materialInfo[a]; + } + data[size] = Ps::to16(a); + size++; + } + } + PX_ASSERT(reservedSize >= size); +#else + for(PxU32 a = 0; a < size; ++a) + data[a] = a; +#endif + + + PxU32 contactForceByteSize = size * sizeof(PxReal); + + + PxsContactManagerOutput& output = mOutputs.getContactManager(header.unit->mNpIndex); + + PxU16 compressedContactSize; + + physx::writeCompressedContact(buffer.contacts, size, NULL, output.nbContacts, output.contactPatches, output.contactPoints, compressedContactSize, + reinterpret_cast(output.contactForces), contactForceByteSize, mMaterialManager, false, + false, materialInfo, output.nbPatches, 0, &mThreadContext.mConstraintBlockManager, &threadContext.mConstraintBlockStream, false); + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("ConstraintPostProcess", 0); + PxU32 endIndex = mStartIndex + mStride; + + ThreadContext* threadContext = mContext.getThreadContext(); + //TODO - we need to do this somewhere else + //threadContext->mContactBlockStream.reset(); + threadContext->mConstraintBlockStream.reset(); + + for(PxU32 a = mStartIndex; a < endIndex; ++a) + { + mergeContacts(mThreadContext.compoundConstraints[a], *threadContext); + } + mContext.putThreadContext(threadContext); + } + + virtual const char* getName() const { return "PxsDynamics.solverConstraintPostProcess"; } + + + DynamicsContext& mContext; + ThreadContext& mThreadContext; + const SolverIslandObjects mObjects; + PxU32 mSolverBodyOffset; + PxU32 mStartIndex; + PxU32 mStride; + PxsMaterialManager* mMaterialManager; + PxsContactManagerOutputIterator& mOutputs; +}; + +class PxsForceThresholdTask : public Cm::Task +{ + DynamicsContext& mDynamicsContext; + + PxsForceThresholdTask& operator=(const PxsForceThresholdTask&); +public: + + PxsForceThresholdTask(DynamicsContext& context): Cm::Task(context.getContextId()), mDynamicsContext(context) + { + } + + void createForceChangeThresholdStream() + { + ThresholdStream& thresholdStream = mDynamicsContext.getThresholdStream(); + //bool haveThresholding = thresholdStream.size()!=0; + + ThresholdTable& thresholdTable = mDynamicsContext.getThresholdTable(); + thresholdTable.build(thresholdStream); + + //generate current force exceeded threshold stream + ThresholdStream& curExceededForceThresholdStream = *mDynamicsContext.mExceededForceThresholdStream[mDynamicsContext.mCurrentIndex]; + ThresholdStream& preExceededForceThresholdStream = *mDynamicsContext.mExceededForceThresholdStream[1 - mDynamicsContext.mCurrentIndex]; + curExceededForceThresholdStream.forceSize_Unsafe(0); + + //fill in the currrent exceeded force threshold stream + for(PxU32 i=0; i elem.threshold * mDynamicsContext.mDt) + { + elem.accumulatedForce = pair.accumulatedForce; + curExceededForceThresholdStream.pushBack(elem); + } + } + + ThresholdStream& forceChangeThresholdStream = mDynamicsContext.getForceChangedThresholdStream(); + forceChangeThresholdStream.forceSize_Unsafe(0); + Ps::Array& forceChangeMask = mDynamicsContext.mExceededForceThresholdStreamMask; + + const PxU32 nbPreExceededForce = preExceededForceThresholdStream.size(); + const PxU32 nbCurExceededForce = curExceededForceThresholdStream.size(); + + //generate force change thresholdStream + if(nbPreExceededForce) + { + thresholdTable.build(preExceededForceThresholdStream); + + //set force change mask + const PxU32 nbTotalExceededForce = nbPreExceededForce + nbCurExceededForce; + forceChangeMask.reserve(nbTotalExceededForce); + forceChangeMask.forceSize_Unsafe(nbTotalExceededForce); + + //initialize the forceChangeMask + for (PxU32 i = 0; i < nbTotalExceededForce; ++i) + forceChangeMask[i] = 1; + + for(PxU32 i=0; i< nbCurExceededForce; ++i) + { + ThresholdStreamElement& curElem = curExceededForceThresholdStream[i]; + + PxU32 pos; + if(thresholdTable.check(preExceededForceThresholdStream, curElem, pos)) + { + forceChangeMask[pos] = 0; + forceChangeMask[i + nbPreExceededForce] = 0; + } + } + + //create force change threshold stream + for(PxU32 i=0; i(left.constraint)->index > reinterpret_cast(right.constraint)->index; + } +}; + +struct ArticulationSortPredicate +{ + bool operator()(const PxsIndexedContactManager*& left, const PxsIndexedContactManager*& right) const + { + return left->contactManager->getWorkUnit().index < right->contactManager->getWorkUnit().index; + } +}; + +class BlockAllocator : public PxConstraintAllocator +{ + PxsConstraintBlockManager& mConstraintBlockManager; + PxcConstraintBlockStream& mConstraintBlockStream; + FrictionPatchStreamPair& mFrictionPatchStreamPair; + PxU32& mTotalConstraintByteSize; +public: + + BlockAllocator(PxsConstraintBlockManager& constraintBlockManager, PxcConstraintBlockStream& constraintBlockStream, FrictionPatchStreamPair& frictionPatchStreamPair, + PxU32& totalConstraintByteSize) : + mConstraintBlockManager(constraintBlockManager), mConstraintBlockStream(constraintBlockStream), mFrictionPatchStreamPair(frictionPatchStreamPair), + mTotalConstraintByteSize(totalConstraintByteSize) + { + } + + virtual PxU8* reserveConstraintData(const PxU32 size) + { + mTotalConstraintByteSize += size; + return mConstraintBlockStream.reserve(size, mConstraintBlockManager); + } + + virtual PxU8* reserveFrictionData(const PxU32 size) + { + return mFrictionPatchStreamPair.reserve(size); + } + + virtual PxU8* findInputPatches(PxU8* frictionCookie) + { + return frictionCookie; + } + + PX_NOCOPY(BlockAllocator) + +}; + + +class SolverArticulationUpdateTask : public Cm::Task +{ + + + ThreadContext& mIslandThreadContext; + + ArticulationV** mArticulations; + ArticulationSolverDesc* mArticulationDescArray; + PxU32 mNbToProcess; + + Dy::DynamicsContext& mContext; + PxU32 mStartIdx; + +public: + + static const PxU32 NbArticulationsPerTask = 8; + + SolverArticulationUpdateTask(ThreadContext& islandThreadContext, ArticulationV** articulations, ArticulationSolverDesc* articulationDescArray, PxU32 nbToProcess, Dy::DynamicsContext& context, + PxU32 startIdx) : + Cm::Task(context.getContextId()), mIslandThreadContext(islandThreadContext), mArticulations(articulations), mArticulationDescArray(articulationDescArray), mNbToProcess(nbToProcess), mContext(context), mStartIdx(startIdx) + { + } + + virtual const char* getName() const { return "SolverArticulationUpdateTask"; } + + virtual void runInternal() + { + ThreadContext& threadContext = *mContext.getThreadContext(); + + threadContext.mConstraintBlockStream.reset(); //Clear in case there's some left-over memory in this context, for which the block has already been freed + PxU32 maxVelIters = 0; + PxU32 maxPosIters = 0; + PxU32 maxArticulationLength = 0; + PxU32 maxSolverArticLength = 0; + PxU32 maxLinks = 0; + + for (PxU32 i = 0; i < mNbToProcess; i++) + { + ArticulationV& a = *(mArticulations[i]); + a.getSolverDesc(mArticulationDescArray[i]); + + maxLinks = PxMax(maxLinks, PxU32(mArticulationDescArray[i].linkCount)); + } + + threadContext.mZVector.forceSize_Unsafe(0); + threadContext.mZVector.reserve(maxLinks); + threadContext.mZVector.forceSize_Unsafe(maxLinks); + + threadContext.mDeltaV.forceSize_Unsafe(0); + threadContext.mDeltaV.reserve(maxLinks); + threadContext.mDeltaV.forceSize_Unsafe(maxLinks); + + PxU32 startIdx = mStartIdx; + + BlockAllocator blockAllocator(mIslandThreadContext.mConstraintBlockManager, threadContext.mConstraintBlockStream, threadContext.mFrictionPatchStreamPair, threadContext.mConstraintSize); + + for(PxU32 i=0;i(PxU32(iterWord >> 8), maxVelIters); + maxPosIters = PxMax(PxU32(iterWord & 0xff), maxPosIters); + startIdx += DY_ARTICULATION_MAX_SIZE; + } + + Ps::atomicMax(reinterpret_cast(&mIslandThreadContext.mMaxSolverPositionIterations), PxI32(maxPosIters)); + Ps::atomicMax(reinterpret_cast(&mIslandThreadContext.mMaxSolverVelocityIterations), PxI32(maxVelIters)); + Ps::atomicMax(reinterpret_cast(&mIslandThreadContext.mMaxArticulationLength), PxI32(maxArticulationLength)); + Ps::atomicMax(reinterpret_cast(&mIslandThreadContext.mMaxArticulationSolverLength), PxI32(maxSolverArticLength)); + Ps::atomicMax(reinterpret_cast(&mIslandThreadContext.mMaxArticulationLinks), PxI32(maxLinks)); + + mContext.putThreadContext(&threadContext); + } + +private: + PX_NOCOPY(SolverArticulationUpdateTask) +}; + + +struct EnhancedSortPredicate +{ + bool operator()(const PxsIndexedContactManager& left, const PxsIndexedContactManager& right) const + { + PxcNpWorkUnit& unit0 = left.contactManager->getWorkUnit(); + PxcNpWorkUnit& unit1 = right.contactManager->getWorkUnit(); + return (unit0.mTransformCache0 < unit1.mTransformCache0) || + ((unit0.mTransformCache0 == unit1.mTransformCache0) && (unit0.mTransformCache1 < unit1.mTransformCache1)); + } +}; + + +class PxsSolverStartTask : public Cm::Task +{ + PxsSolverStartTask& operator=(const PxsSolverStartTask&); +public: + + PxsSolverStartTask(DynamicsContext& context, + IslandContext& islandContext, + const SolverIslandObjects& objects, + const PxU32 solverBodyOffset, + const PxU32 kinematicCount, + IG::SimpleIslandManager& islandManager, + PxU32* bodyRemapTable, + PxsMaterialManager* materialManager, + PxsContactManagerOutputIterator& iterator, + bool enhancedDeterminism + ) : + Cm::Task (context.getContextId()), + mContext (context), + mIslandContext (islandContext), + mObjects (objects), + mSolverBodyOffset (solverBodyOffset), + mKinematicCount (kinematicCount), + mIslandManager (islandManager), + mBodyRemapTable (bodyRemapTable), + mMaterialManager (materialManager), + mOutputs (iterator), + mEnhancedDeterminism (enhancedDeterminism) + {} + + void startTasks() + { + PX_PROFILE_ZONE("Dynamics.solveGroup", mContext.getContextId()); + { + ThreadContext& mThreadContext = *mContext.getThreadContext(); + + mIslandContext.mThreadContext = &mThreadContext; + + mThreadContext.mMaxSolverPositionIterations = 0; + mThreadContext.mMaxSolverVelocityIterations = 0; + mThreadContext.mAxisConstraintCount = 0; + mThreadContext.mContactDescPtr = mThreadContext.contactConstraintDescArray; + mThreadContext.mFrictionDescPtr = mThreadContext.frictionConstraintDescArray.begin(); + mThreadContext.mNumDifferentBodyConstraints = 0; + mThreadContext.mNumSelfConstraintBlocks = 0; + mThreadContext.mNumSelfConstraints = 0; + mThreadContext.mNumDifferentBodyFrictionConstraints = 0; + mThreadContext.mNumSelfConstraintFrictionBlocks = 0; + mThreadContext.mNumSelfFrictionConstraints = 0; + mThreadContext.numContactConstraintBatches = 0; + mThreadContext.contactDescArraySize = 0; + mThreadContext.mMaxArticulationLinks = 0; + + + mThreadContext.contactConstraintDescArray = mObjects.constraintDescs; + mThreadContext.orderedContactConstraints = mObjects.orderedConstraintDescs; + mThreadContext.mContactDescPtr = mObjects.constraintDescs; + mThreadContext.tempConstraintDescArray = mObjects.tempConstraintDescs; + mThreadContext.contactConstraintBatchHeaders = mObjects.constraintBatchHeaders; + mThreadContext.motionVelocityArray = mObjects.motionVelocities; + mThreadContext.mBodyCoreArray = mObjects.bodyCoreArray; + mThreadContext.mRigidBodyArray = mObjects.bodies; + mThreadContext.mArticulationArray = mObjects.articulations; + mThreadContext.bodyRemapTable = mObjects.bodyRemapTable; + mThreadContext.mNodeIndexArray = mObjects.nodeIndexArray; + + const PxU32 frictionConstraintCount = mContext.getFrictionType() == PxFrictionType::ePATCH ? 0 : PxU32(mIslandContext.mCounts.contactManagers); + mThreadContext.resizeArrays(frictionConstraintCount, mIslandContext.mCounts.articulations); + + PxsBodyCore** PX_RESTRICT bodyArrayPtr = mThreadContext.mBodyCoreArray; + PxsRigidBody** PX_RESTRICT rigidBodyPtr = mThreadContext.mRigidBodyArray; + ArticulationV** PX_RESTRICT articulationPtr = mThreadContext.mArticulationArray; + PxU32* PX_RESTRICT bodyRemapTable = mThreadContext.bodyRemapTable; + PxU32* PX_RESTRICT nodeIndexArray = mThreadContext.mNodeIndexArray; + + PxU32 nbIslands = mObjects.numIslands; + const IG::IslandId* const islandIds = mObjects.islandIds; + + const IG::IslandSim& islandSim = mIslandManager.getAccurateIslandSim(); + + PxU32 bodyIndex = 0, articIndex = 0; + for (PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::NodeIndex currentIndex = island.mRootNode; + + while (currentIndex.isValid()) + { + const IG::Node& node = islandSim.getNode(currentIndex); + + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + articulationPtr[articIndex++] = node.getArticulation(); + } + else + { + PX_ASSERT(bodyIndex < (mIslandContext.mCounts.bodies + mContext.mKinematicCount + 1)); + nodeIndexArray[bodyIndex++] = currentIndex.index(); + } + + currentIndex = node.mNextNode; + } + } + + //Bodies can come in a slightly jumbled order from islandGen. It's deterministic if the scene is + //identical but can vary if there are additional bodies in the scene in a different island. + if (mEnhancedDeterminism) + { + Ps::sort(nodeIndexArray, bodyIndex); + } + + for (PxU32 a = 0; a < bodyIndex; ++a) + { + IG::NodeIndex currentIndex(nodeIndexArray[a]); + const IG::Node& node = islandSim.getNode(currentIndex); + PxsRigidBody* rigid = node.getRigidBody(); + rigidBodyPtr[a] = rigid; + bodyArrayPtr[a] = &rigid->getCore(); + bodyRemapTable[islandSim.getActiveNodeIndex(currentIndex)] = a; + } + + + PxsIndexedContactManager* indexedManagers = mObjects.contactManagers; + + PxU32 currentContactIndex = 0; + for(PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::EdgeIndex contactEdgeIndex = island.mFirstEdge[IG::Edge::eCONTACT_MANAGER]; + + while(contactEdgeIndex != IG_INVALID_EDGE) + { + const IG::Edge& edge = islandSim.getEdge(contactEdgeIndex); + + PxsContactManager* contactManager = mIslandManager.getContactManager(contactEdgeIndex); + + if(contactManager) + { + const IG::NodeIndex nodeIndex1 = islandSim.getNodeIndex1(contactEdgeIndex); + const IG::NodeIndex nodeIndex2 = islandSim.getNodeIndex2(contactEdgeIndex); + + PxsIndexedContactManager& indexedManager = indexedManagers[currentContactIndex++]; + indexedManager.contactManager = contactManager; + + PX_ASSERT(!nodeIndex1.isStaticBody()); + { + const IG::Node& node1 = islandSim.getNode(nodeIndex1); + + //Is it an articulation or not??? + if(node1.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + indexedManager.indexType0 = PxsIndexedInteraction::eARTICULATION; + indexedManager.solverBody0 = size_t(node1.getArticulation()) | nodeIndex1.articulationLinkId(); + } + else + { + if(node1.isKinematic()) + { + indexedManager.indexType0 = PxsIndexedInteraction::eKINEMATIC; + indexedManager.solverBody0 = islandSim.getActiveNodeIndex(nodeIndex1); + } + else + { + indexedManager.indexType0 = PxsIndexedInteraction::eBODY; + indexedManager.solverBody0 = bodyRemapTable[islandSim.getActiveNodeIndex(nodeIndex1)]; + } + PX_ASSERT(indexedManager.solverBody0 < (mIslandContext.mCounts.bodies + mContext.mKinematicCount + 1)); + } + + } + + if(nodeIndex2.isStaticBody()) + { + indexedManager.indexType1 = PxsIndexedInteraction::eWORLD; + } + else + { + const IG::Node& node2 = islandSim.getNode(nodeIndex2); + + //Is it an articulation or not??? + if(node2.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + indexedManager.indexType1 = PxsIndexedInteraction::eARTICULATION; + indexedManager.solverBody1 = size_t(node2.getArticulation()) | nodeIndex2.articulationLinkId(); + } + else + { + if(node2.isKinematic()) + { + indexedManager.indexType1 = PxsIndexedInteraction::eKINEMATIC; + indexedManager.solverBody1 = islandSim.getActiveNodeIndex(nodeIndex2); + } + else + { + indexedManager.indexType1 = PxsIndexedInteraction::eBODY; + indexedManager.solverBody1 = bodyRemapTable[islandSim.getActiveNodeIndex(nodeIndex2)]; + } + PX_ASSERT(indexedManager.solverBody1 < (mIslandContext.mCounts.bodies + mContext.mKinematicCount + 1)); + } + } + + } + contactEdgeIndex = edge.mNextIslandEdge; + } + } + + if (mEnhancedDeterminism) + { + Ps::sort(indexedManagers, currentContactIndex, EnhancedSortPredicate()); + } + + mIslandContext.mCounts.contactManagers = currentContactIndex; + } + } + + void integrate() + { + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + PxSolverBody* solverBodies = mContext.mSolverBodyPool.begin() + mSolverBodyOffset; + PxSolverBodyData* solverBodyData = mContext.mSolverBodyDataPool.begin() + mSolverBodyOffset; + + { + PX_PROFILE_ZONE("Dynamics.updateVelocities", mContext.getContextId()); + + mContext.preIntegrationParallel( + mContext.mDt, + mThreadContext.mBodyCoreArray, + mObjects.bodies, + mThreadContext.mNodeIndexArray, + mIslandContext.mCounts.bodies, + solverBodies, + solverBodyData, + mThreadContext.motionVelocityArray, + mThreadContext.mMaxSolverPositionIterations, + mThreadContext.mMaxSolverVelocityIterations, + *mCont + ); + } + } + + void articulationTask() + { + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + ArticulationSolverDesc* articulationDescArray = mThreadContext.getArticulations().begin(); + + for(PxU32 i=0;isetContinuation(mCont); + task->removeReference(); + + } + } + + void setupDescTask() + { + PX_PROFILE_ZONE("SetupDescs", 0); + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + PxSolverConstraintDesc* contactDescPtr = mThreadContext.mContactDescPtr; + + //PxU32 constraintCount = mCounts.constraints + mCounts.contactManagers; + + PxU32 nbIslands = mObjects.numIslands; + const IG::IslandId* const islandIds = mObjects.islandIds; + + const IG::IslandSim& islandSim = mIslandManager.getAccurateIslandSim(); + + for(PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::EdgeIndex edgeId = island.mFirstEdge[IG::Edge::eCONSTRAINT]; + + while(edgeId != IG_INVALID_EDGE) + { + PxSolverConstraintDesc& desc = *contactDescPtr; + + const IG::Edge& edge = islandSim.getEdge(edgeId); + Dy::Constraint* constraint = mIslandManager.getConstraint(edgeId); + mContext.setDescFromIndices(desc, edgeId, mIslandManager, mBodyRemapTable, mSolverBodyOffset); + desc.constraint = reinterpret_cast(constraint); + desc.constraintLengthOver16 = DY_SC_TYPE_RB_1D; + contactDescPtr++; + edgeId = edge.mNextIslandEdge; + } + + } + +#if 1 + Ps::sort(mThreadContext.mContactDescPtr, PxU32(contactDescPtr - mThreadContext.mContactDescPtr), ConstraintLess()); +#endif + + + mThreadContext.orderedContactList.forceSize_Unsafe(0); + mThreadContext.orderedContactList.reserve(mIslandContext.mCounts.contactManagers); + mThreadContext.orderedContactList.forceSize_Unsafe(mIslandContext.mCounts.contactManagers); + mThreadContext.tempContactList.forceSize_Unsafe(0); + mThreadContext.tempContactList.reserve(mIslandContext.mCounts.contactManagers); + mThreadContext.tempContactList.forceSize_Unsafe(mIslandContext.mCounts.contactManagers); + + const PxsIndexedContactManager** constraints = mThreadContext.orderedContactList.begin(); + + + //OK, we sort the orderedContactList + + mThreadContext.compoundConstraints.forceSize_Unsafe(0); + if(mIslandContext.mCounts.contactManagers) + { + { + mThreadContext.sortIndexArray.forceSize_Unsafe(0); + + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eBODY == 0); + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eKINEMATIC == 1); + + const PxI32 offsetMap[] = {PxI32(mContext.mKinematicCount), 0}; + + const PxU32 totalBodies = mContext.mKinematicCount + mIslandContext.mCounts.bodies+1; + + mThreadContext.sortIndexArray.reserve(totalBodies); + mThreadContext.sortIndexArray.forceSize_Unsafe(totalBodies); + PxMemZero(mThreadContext.sortIndexArray.begin(), totalBodies * 4); + + //Iterate over the array based on solverBodyDatapool, creating a list of sorted constraints (in order of body pair) + //We only do this with contacts. It's important that this is done this way because we don't want to break our rules that all joints + //appear before all contacts in the constraint list otherwise we will lose all guarantees about sorting joints. + + for(PxU32 a = 0; a < mIslandContext.mCounts.contactManagers; ++a) + { + PX_ASSERT(mObjects.contactManagers[a].indexType0 != PxsIndexedInteraction::eWORLD); + //Index first body... + PxU8 indexType = mObjects.contactManagers[a].indexType0; + if(indexType != PxsIndexedInteraction::eARTICULATION && mObjects.contactManagers[a].indexType1 != PxsIndexedInteraction::eARTICULATION) + { + PX_ASSERT((indexType == PxsIndexedInteraction::eBODY) || (indexType == PxsIndexedInteraction::eKINEMATIC)); + + PxI32 index = PxI32(mObjects.contactManagers[a].solverBody0 + offsetMap[indexType]); + PX_ASSERT(index >= 0); + mThreadContext.sortIndexArray[PxU32(index)]++; + } + } + + PxU32 accumulatedCount = 0; + + for(PxU32 a = mThreadContext.sortIndexArray.size(); a > 0; --a) + { + PxU32 ind = a - 1; + PxU32 val = mThreadContext.sortIndexArray[ind]; + mThreadContext.sortIndexArray[ind] = accumulatedCount; + accumulatedCount += val; + } + + //OK, now copy across data to orderedConstraintDescs, pushing articulations to the end... + for(PxU32 a = 0; a < mIslandContext.mCounts.contactManagers; ++a) + { + //Index first body... + PxU8 indexType = mObjects.contactManagers[a].indexType0; + if(indexType != PxsIndexedInteraction::eARTICULATION && mObjects.contactManagers[a].indexType1 != PxsIndexedInteraction::eARTICULATION) + { + PX_ASSERT((indexType == PxsIndexedInteraction::eBODY) || (indexType == PxsIndexedInteraction::eKINEMATIC)); + + PxI32 index = PxI32(mObjects.contactManagers[a].solverBody0 + offsetMap[indexType]); + PX_ASSERT(index >= 0); + mThreadContext.tempContactList[mThreadContext.sortIndexArray[PxU32(index)]++] = &mObjects.contactManagers[a]; + } + else + { + mThreadContext.tempContactList[accumulatedCount++] = &mObjects.contactManagers[a]; + } + } + + //Now do the same again with bodyB, being careful not to overwrite the joints + PxMemZero(mThreadContext.sortIndexArray.begin(), totalBodies * 4); + + + for(PxU32 a = 0; a < mIslandContext.mCounts.contactManagers; ++a) + { + //Index first body... + PxU8 indexType = mThreadContext.tempContactList[a]->indexType1; + if(indexType != PxsIndexedInteraction::eARTICULATION && mObjects.contactManagers[a].indexType0 != PxsIndexedInteraction::eARTICULATION) + { + PX_ASSERT((indexType == PxsIndexedInteraction::eBODY) || (indexType == PxsIndexedInteraction::eKINEMATIC) || (indexType == PxsIndexedInteraction::eWORLD)); + + PxI32 index = (indexType == PxsIndexedInteraction::eWORLD) ? 0 : PxI32(mThreadContext.tempContactList[a]->solverBody1 + offsetMap[indexType]); + PX_ASSERT(index >= 0); + mThreadContext.sortIndexArray[PxU32(index)]++; + } + } + + accumulatedCount = 0; + for(PxU32 a = mThreadContext.sortIndexArray.size(); a > 0; --a) + { + PxU32 ind = a - 1; + PxU32 val = mThreadContext.sortIndexArray[ind]; + mThreadContext.sortIndexArray[ind] = accumulatedCount; + accumulatedCount += val; + } + + PxU32 articulationStartIndex = accumulatedCount; + + //OK, now copy across data to orderedConstraintDescs, pushing articulations to the end... + for(PxU32 a = 0; a < mIslandContext.mCounts.contactManagers; ++a) + { + //Index first body... + PxU8 indexType = mThreadContext.tempContactList[a]->indexType1; + if(indexType != PxsIndexedInteraction::eARTICULATION && mObjects.contactManagers[a].indexType0 != PxsIndexedInteraction::eARTICULATION) + { + PX_ASSERT((indexType == PxsIndexedInteraction::eBODY) || (indexType == PxsIndexedInteraction::eKINEMATIC) || (indexType == PxsIndexedInteraction::eWORLD)); + + PxI32 index = (indexType == PxsIndexedInteraction::eWORLD) ? 0 : PxI32(mThreadContext.tempContactList[a]->solverBody1 + offsetMap[indexType]); + PX_ASSERT(index >= 0); + constraints[mThreadContext.sortIndexArray[PxU32(index)]++] = mThreadContext.tempContactList[a]; + } + else + { + constraints[accumulatedCount++] = mThreadContext.tempContactList[a]; + } + } + +#if 1 + Ps::sort(constraints + articulationStartIndex, accumulatedCount - articulationStartIndex, ArticulationSortPredicate()); +#endif + } + + mThreadContext.mStartContactDescPtr = contactDescPtr; + + mThreadContext.compoundConstraints.reserve(1024); + mThreadContext.compoundConstraints.forceSize_Unsafe(0); + //mThreadContext.compoundConstraints.forceSize_Unsafe(mCounts.contactManagers); + + PxSolverConstraintDesc* startDesc = contactDescPtr; + mContext.setDescFromIndices(*startDesc, *constraints[0], mSolverBodyOffset); + startDesc->constraint = reinterpret_cast(constraints[0]->contactManager); + startDesc->constraintLengthOver16 = DY_SC_TYPE_RB_CONTACT; + + PxsContactManagerOutput* startManagerOutput = &mOutputs.getContactManager(constraints[0]->contactManager->getWorkUnit().mNpIndex); + PxU32 contactCount = startManagerOutput->nbContacts; + PxU32 startIndex = 0; + PxU32 numHeaders = 0; + + const bool gDisableConstraintWelding = false; + + for(PxU32 a = 1; a < mIslandContext.mCounts.contactManagers; ++a) + { + PxSolverConstraintDesc& desc = *(contactDescPtr+1); + mContext.setDescFromIndices(desc, *constraints[a], mSolverBodyOffset); + + PxsContactManager* manager = constraints[a]->contactManager; + PxsContactManagerOutput& output = mOutputs.getContactManager(manager->getWorkUnit().mNpIndex); + + desc.constraint = reinterpret_cast(constraints[a]->contactManager); + desc.constraintLengthOver16 = DY_SC_TYPE_RB_CONTACT; + + if (contactCount == 0) + { + //This is the first object in the pair + *startDesc = *(contactDescPtr + 1); + startIndex = a; + startManagerOutput = &output; + } + + if(startDesc->bodyA != desc.bodyA || startDesc->bodyB != desc.bodyB + || startDesc->linkIndexA != PxSolverConstraintDesc::NO_LINK || startDesc->linkIndexB != PxSolverConstraintDesc::NO_LINK + || contactCount + output.nbContacts > Gu::ContactBuffer::MAX_CONTACTS + || manager->isChangeable() + || gDisableConstraintWelding + ) //If this is the first thing and no contacts...then we skip + { + PxU32 stride = a - startIndex; + if(contactCount > 0) + { + if(stride > 1) + { + ++numHeaders; + CompoundContactManager& header = mThreadContext.compoundConstraints.insert(); + header.mStartIndex = startIndex; + header.mStride = Ps::to16(stride); + header.mReducedContactCount = Ps::to16(contactCount); + PxsContactManager* manager1 = constraints[startIndex]->contactManager; + PxcNpWorkUnit& unit = manager1->getWorkUnit(); + + PX_ASSERT(startManagerOutput == &mOutputs.getContactManager(unit.mNpIndex)); + + header.unit = &unit; + header.cmOutput = startManagerOutput; + header.originalContactPatches = startManagerOutput->contactPatches; + header.originalContactPoints = startManagerOutput->contactPoints; + header.originalContactCount = startManagerOutput->nbContacts; + header.originalPatchCount = startManagerOutput->nbPatches; + header.originalForceBuffer = reinterpret_cast(startManagerOutput->contactForces); + header.originalStatusFlags = startManagerOutput->statusFlag; + } + startDesc = ++contactDescPtr; + } + else + { + //Copy back next contactDescPtr + *startDesc = *(contactDescPtr+1); + } + contactCount = 0; + startIndex = a; + startManagerOutput = &output; + } + contactCount += output.nbContacts; + + } + PxU32 stride = mIslandContext.mCounts.contactManagers - startIndex; + if(contactCount > 0) + { + if(stride > 1) + { + ++numHeaders; + CompoundContactManager& header = mThreadContext.compoundConstraints.insert(); + header.mStartIndex = startIndex; + header.mStride = Ps::to16(stride); + header.mReducedContactCount = Ps::to16(contactCount); + PxsContactManager* manager = constraints[startIndex]->contactManager; + PxcNpWorkUnit& unit = manager->getWorkUnit(); + header.unit = &unit; + header.cmOutput = startManagerOutput; + header.originalContactPatches = startManagerOutput->contactPatches; + header.originalContactPoints = startManagerOutput->contactPoints; + header.originalContactCount = startManagerOutput->nbContacts; + header.originalPatchCount = startManagerOutput->nbPatches; + header.originalForceBuffer = reinterpret_cast(startManagerOutput->contactForces); + header.originalStatusFlags = startManagerOutput->statusFlag; + } + contactDescPtr++; + } + + if(numHeaders) + { + const PxU32 unrollSize = 8; + for(PxU32 a = 0; a < numHeaders; a+= unrollSize) + { + PxsSolverConstraintPostProcessTask* postProcessTask = PX_PLACEMENT_NEW( mContext.getTaskPool().allocate(sizeof(PxsSolverConstraintPostProcessTask)), + PxsSolverConstraintPostProcessTask)(mContext, mThreadContext, mObjects, mSolverBodyOffset, a, PxMin(unrollSize, numHeaders - a), mMaterialManager, + mOutputs); + postProcessTask->setContinuation(mCont); + postProcessTask->removeReference(); + } + } + } + mThreadContext.contactDescArraySize = PxU32(contactDescPtr - mThreadContext.contactConstraintDescArray); + mThreadContext.mContactDescPtr = contactDescPtr; + } + + virtual void runInternal() + { + startTasks(); + integrate(); + setupDescTask(); + articulationTask(); + } + + virtual const char* getName() const + { + return "PxsDynamics.solverStart"; + } + +private: + DynamicsContext& mContext; + IslandContext& mIslandContext; + const SolverIslandObjects mObjects; + const PxU32 mSolverBodyOffset; + const PxU32 mKinematicCount; + IG::SimpleIslandManager& mIslandManager; + PxU32* mBodyRemapTable; + PxsMaterialManager* mMaterialManager; + PxsContactManagerOutputIterator& mOutputs; + bool mEnhancedDeterminism; +}; + +class PxsSolverConstraintPartitionTask : public Cm::Task +{ + PxsSolverConstraintPartitionTask& operator=(const PxsSolverConstraintPartitionTask&); +public: + + PxsSolverConstraintPartitionTask(DynamicsContext& context, + IslandContext& islandContext, + const SolverIslandObjects& objects, + const PxU32 solverBodyOffset, bool enhancedDeterminism) : + Cm::Task(context.getContextId()), + mContext(context), + mIslandContext(islandContext), + mObjects(objects), + mSolverBodyOffset(solverBodyOffset), + mEnhancedDeterminism(enhancedDeterminism) + {} + + virtual void runInternal() + { + + PX_PROFILE_ZONE("PartitionConstraints", 0); + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + + //Compact articulation pairs... + ArticulationSolverDesc* artics = mThreadContext.getArticulations().begin(); + + if(mIslandContext.mCounts.articulations) + { + PxU32 nbArticConstraints = artics[0].numInternalConstraints; + + PxSolverConstraintDesc* currDesc = mThreadContext.mContactDescPtr; + for(PxU32 a = 1; a < mIslandContext.mCounts.articulations; ++a) + { + //Compact pairs... + const PxU32 nbInternalConstraints = artics[a].numInternalConstraints; + const PxU32 startIdx = a * DY_ARTICULATION_MAX_SIZE; + const PxU32 endIdx = startIdx + nbInternalConstraints; + + for(PxU32 b = startIdx; b < endIdx; ++b) + { + currDesc[nbArticConstraints++] = currDesc[b]; + } + } + + mThreadContext.contactDescArraySize += nbArticConstraints; + } + + PxSolverConstraintDesc* descBegin = mThreadContext.contactConstraintDescArray; + PxU32 descCount = mThreadContext.contactDescArraySize; + + PxSolverBody* solverBodies = mContext.mSolverBodyPool.begin() + mSolverBodyOffset; + + mThreadContext.mNumDifferentBodyConstraints = descCount; + + { + mThreadContext.mNumDifferentBodyConstraints = 0; + mThreadContext.mNumSelfConstraints = 0; + mThreadContext.mNumSelfConstraintBlocks = 0; + mThreadContext.mNumDifferentBodyFrictionConstraints = 0; + mThreadContext.mNumSelfConstraintFrictionBlocks = 0; + mThreadContext.mNumSelfFrictionConstraints = 0; + + if(descCount > 0) + { + ConstraintPartitionArgs args; + args.mBodies = solverBodies; + args.mArticulationPtrs = artics; + args.mContactConstraintDescriptors = descBegin; + args.mNumArticulationPtrs = mThreadContext.getArticulations().size(); + args.mNumBodies = mIslandContext.mCounts.bodies; + args.mNumContactConstraintDescriptors = descCount; + args.mOrderedContactConstraintDescriptors = mThreadContext.orderedContactConstraints; + args.mTempContactConstraintDescriptors = mThreadContext.tempConstraintDescArray; + args.mNumDifferentBodyConstraints = args.mNumSelfConstraints = args.mNumSelfConstraintBlocks = 0; + args.mConstraintsPerPartition = &mThreadContext.mConstraintsPerPartition; + args.mBitField = &mThreadContext.mPartitionNormalizationBitmap; + args.enhancedDeterminism = mEnhancedDeterminism; + + mThreadContext.mMaxPartitions = partitionContactConstraints(args); + mThreadContext.mNumDifferentBodyConstraints = args.mNumDifferentBodyConstraints; + mThreadContext.mNumSelfConstraints = args.mNumSelfConstraints; + mThreadContext.mNumSelfConstraintBlocks = args.mNumSelfConstraintBlocks; + } + else + { + PxMemZero(mThreadContext.mConstraintsPerPartition.begin(), sizeof(PxU32)*mThreadContext.mConstraintsPerPartition.capacity()); + } + + PX_ASSERT((mThreadContext.mNumDifferentBodyConstraints + mThreadContext.mNumSelfConstraints) == descCount); + } + + } + + virtual const char* getName() const { return "PxsDynamics.solverConstraintPartition"; } + + DynamicsContext& mContext; + IslandContext& mIslandContext; + const SolverIslandObjects mObjects; + PxU32 mSolverBodyOffset; + bool mEnhancedDeterminism; +}; + + +class PxsSolverSetupSolveTask : public Cm::Task +{ + PxsSolverSetupSolveTask& operator=(const PxsSolverSetupSolveTask&); +public: + + PxsSolverSetupSolveTask( + DynamicsContext& context, + IslandContext& islandContext, + const SolverIslandObjects& objects, + const PxU32 solverBodyOffset, + IG::IslandSim& islandSim) : + Cm::Task(context.getContextId()), + mContext(context), + mIslandContext(islandContext), + mObjects(objects), + mSolverBodyOffset(solverBodyOffset), + mIslandSim(islandSim) + {} + + + virtual void runInternal() + { + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + + PxSolverConstraintDesc* contactDescBegin = mThreadContext.orderedContactConstraints; + PxSolverConstraintDesc* contactDescPtr = mThreadContext.orderedContactConstraints; + + PxSolverBody* solverBodies = mContext.mSolverBodyPool.begin() + mSolverBodyOffset; + PxSolverBodyData* solverBodyDatas = mContext.mSolverBodyDataPool.begin(); + + PxU32 frictionDescCount = mThreadContext.mNumDifferentBodyFrictionConstraints; + + PxU32 j = 0, i = 0; + + //On PS3, self-constraints will be bumped to the end of the constraint list + //and processed separately. On PC/360, they will be mixed in the array and + //classed as "different body" constraints regardless of the fact that they're self-constraints. + //PxU32 numBatches = mThreadContext.numDifferentBodyBatchHeaders; + // TODO: maybe replace with non-null joints from end of the array + + PxU32 numBatches = 0; + + PxU32 currIndex = 0; + for(PxU32 a = 0; a < mThreadContext.mConstraintsPerPartition.size(); ++a) + { + PxU32 endIndex = currIndex + mThreadContext.mConstraintsPerPartition[a]; + + PxU32 numBatchesInPartition = 0; + for(PxU32 b = currIndex; b < endIndex; ++b) + { + PxConstraintBatchHeader& _header = mThreadContext.contactConstraintBatchHeaders[b]; + PxU16 stride = _header.mStride, newStride = _header.mStride; + PxU32 startIndex = j; + for(PxU16 c = 0; c < stride; ++c) + { + if(getConstraintLength(contactDescBegin[i]) == 0) + { + newStride--; + i++; + } + else + { + if(i!=j) + contactDescBegin[j] = contactDescBegin[i]; + i++; + j++; + contactDescPtr++; + } + } + + if(newStride != 0) + { + mThreadContext.contactConstraintBatchHeaders[numBatches].mStartIndex = startIndex; + mThreadContext.contactConstraintBatchHeaders[numBatches].mStride = newStride; + PxU8 type = *contactDescBegin[startIndex].constraint; + if(type == DY_SC_TYPE_STATIC_CONTACT) + { + //Check if any block of constraints is classified as type static (single) contact constraint. + //If they are, iterate over all constraints grouped with it and switch to "dynamic" contact constraint + //type if there's a dynamic contact constraint in the group. + for(PxU32 c = 1; c < newStride; ++c) + { + if(*contactDescBegin[startIndex+c].constraint == DY_SC_TYPE_RB_CONTACT) + { + type = DY_SC_TYPE_RB_CONTACT; + } + } + } + + mThreadContext.contactConstraintBatchHeaders[numBatches].mConstraintType = type; + numBatches++; + numBatchesInPartition++; + } + } + PxU32 numHeaders = numBatchesInPartition; + currIndex += mThreadContext.mConstraintsPerPartition[a]; + mThreadContext.mConstraintsPerPartition[a] = numHeaders; + } + + PxU32 contactDescCount = PxU32(contactDescPtr - contactDescBegin); + + mThreadContext.mNumDifferentBodyConstraints = contactDescCount; + + PxU32 numSelfConstraintBlocks = mThreadContext.mNumSelfConstraintBlocks; + + //Remap self constraint array. Self-constraint blocks exists on PS3 as an optimization for SPU solver. + for(PxU32 a = 0; a < numSelfConstraintBlocks; ++a) + { + PX_ASSERT(mThreadContext.mSelfConstraintBlocks[a].startId == i); + PxU32 origNumSelfConstraints = mThreadContext.mSelfConstraintBlocks[a].numSelfConstraints; + PxU32 startId = j; + + for(PxU32 b = 0; b < origNumSelfConstraints; ++b) + { + PxSolverConstraintDesc& desc = contactDescBegin[i]; + + if(getConstraintLength(desc)) + { + PxConstraintBatchHeader& header = mThreadContext.contactConstraintBatchHeaders[numBatches++]; + header.mStride = 1; + header.mStartIndex = j; + header.mConstraintType = *desc.constraint; + if(i != j) + contactDescBegin[j] = contactDescBegin[i]; + j++; + } + i++; + } + mThreadContext.mSelfConstraintBlocks[a].startId = startId; + mThreadContext.mSelfConstraintBlocks[a].numSelfConstraints = j - startId; + } + + mThreadContext.numContactConstraintBatches = numBatches; + mThreadContext.mNumSelfConstraints = j - contactDescCount; //self constraint count + contactDescCount = j; + mThreadContext.mOrderedContactDescCount = j; + + //Now do the friction constraints if we're not using the sticky model + if(mContext.getFrictionType() != PxFrictionType::ePATCH) + { + PxSolverConstraintDesc* frictionDescBegin = mThreadContext.frictionConstraintDescArray.begin(); + PxSolverConstraintDesc* frictionDescPtr = frictionDescBegin; + + Ps::Array& frictionHeaderArray = mThreadContext.frictionConstraintBatchHeaders; + frictionHeaderArray.forceSize_Unsafe(0); + frictionHeaderArray.reserve(mThreadContext.numContactConstraintBatches); + PxConstraintBatchHeader* headers = frictionHeaderArray.begin(); + + Ps::Array& constraintsPerPartition = mThreadContext.mConstraintsPerPartition; + Ps::Array& frictionConstraintsPerPartition = mThreadContext.mFrictionConstraintsPerPartition; + frictionConstraintsPerPartition.forceSize_Unsafe(0); + frictionConstraintsPerPartition.reserve(constraintsPerPartition.capacity()); + + + PxU32 fricI = 0; + PxU32 startIndex = 0; + PxU32 fricHeaders = 0; + for(PxU32 k = 0; k < constraintsPerPartition.size(); ++k) + { + PxU32 numBatchesInK = constraintsPerPartition[k]; + PxU32 endIndex = startIndex + numBatchesInK; + + PxU32 startFricH = fricHeaders; + + for(PxU32 a = startIndex; a < endIndex; ++a) + { + PxConstraintBatchHeader& _header = mThreadContext.contactConstraintBatchHeaders[a]; + PxU16 stride = _header.mStride; + if(_header.mConstraintType == DY_SC_TYPE_RB_CONTACT || _header.mConstraintType == DY_SC_TYPE_EXT_CONTACT || + _header.mConstraintType == DY_SC_TYPE_STATIC_CONTACT) + { + PxU8 type = 0; + //Extract friction from this constraint + for(PxU16 b = 0; b < stride; ++b) + { + //create the headers... + PxSolverConstraintDesc& desc = contactDescBegin[_header.mStartIndex + b]; + PX_ASSERT(desc.constraint); + SolverContactCoulombHeader* header = reinterpret_cast(desc.constraint); + PxU32 frictionOffset = header->frictionOffset; + PxU8* PX_RESTRICT constraint = reinterpret_cast(header) + frictionOffset; + const PxU32 origLength = getConstraintLength(desc); + const PxU32 length = (origLength - frictionOffset); + + setConstraintLength(*frictionDescPtr, length); + frictionDescPtr->constraint = constraint; + frictionDescPtr->bodyA = desc.bodyA; + frictionDescPtr->bodyB = desc.bodyB; + frictionDescPtr->bodyADataIndex = desc.bodyADataIndex; + frictionDescPtr->bodyBDataIndex = desc.bodyBDataIndex; + frictionDescPtr->linkIndexA = desc.linkIndexA; + frictionDescPtr->linkIndexB = desc.linkIndexB; + frictionDescPtr->writeBack = NULL; + frictionDescPtr->writeBackLengthOver4 = 0; + type = *constraint; + frictionDescPtr++; + } + headers->mStartIndex = fricI; + headers->mStride = stride; + headers->mConstraintType = type; + headers++; + fricHeaders++; + fricI += stride; + } + else if(_header.mConstraintType == DY_SC_TYPE_BLOCK_RB_CONTACT || _header.mConstraintType == DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT) + { + //KS - TODO - Extract block of 4 contacts from this constraint. This isn't implemented yet for coulomb friction model + PX_ASSERT(contactDescBegin[_header.mStartIndex].constraint); + SolverContactCoulombHeader4* head = reinterpret_cast(contactDescBegin[_header.mStartIndex].constraint); + PxU32 frictionOffset = head->frictionOffset; + PxU8* PX_RESTRICT constraint = reinterpret_cast(head) + frictionOffset; + const PxU32 origLength = getConstraintLength(contactDescBegin[_header.mStartIndex]); + const PxU32 length = (origLength - frictionOffset); + PxU8 type = *constraint; + PX_ASSERT(type == DY_SC_TYPE_BLOCK_FRICTION || type == DY_SC_TYPE_BLOCK_STATIC_FRICTION); + for(PxU32 b = 0; b < 4; ++b) + { + PxSolverConstraintDesc& desc = contactDescBegin[_header.mStartIndex+b]; + setConstraintLength(*frictionDescPtr, length); + frictionDescPtr->constraint = constraint; + frictionDescPtr->bodyA = desc.bodyA; + frictionDescPtr->bodyB = desc.bodyB; + frictionDescPtr->bodyADataIndex = desc.bodyADataIndex; + frictionDescPtr->bodyBDataIndex = desc.bodyBDataIndex; + frictionDescPtr->linkIndexA = desc.linkIndexA; + frictionDescPtr->linkIndexB = desc.linkIndexB; + frictionDescPtr->writeBack = NULL; + frictionDescPtr->writeBackLengthOver4 = 0; + frictionDescPtr++; + } + headers->mStartIndex = fricI; + headers->mStride = stride; + headers->mConstraintType = type; + headers++; + fricHeaders++; + fricI += stride; + } + } + startIndex += numBatchesInK; + if(startFricH < fricHeaders) + { + frictionConstraintsPerPartition.pushBack(fricHeaders - startFricH); + } + } + + + frictionDescCount = PxU32(frictionDescPtr - frictionDescBegin); + + mThreadContext.mNumDifferentBodyFrictionConstraints = frictionDescCount; + + frictionHeaderArray.forceSize_Unsafe(PxU32(headers - frictionHeaderArray.begin())); + + mThreadContext.mNumSelfFrictionConstraints = fricI - frictionDescCount; //self constraint count + mThreadContext.mNumDifferentBodyFrictionConstraints = frictionDescCount; + frictionDescCount = fricI; + mThreadContext.mOrderedFrictionDescCount = frictionDescCount; + + + } + + { + { + PX_PROFILE_ZONE("Dynamics.solver", mContext.getContextId()); + + PxSolverConstraintDesc* contactDescs = mThreadContext.orderedContactConstraints; + PxSolverConstraintDesc* frictionDescs = mThreadContext.frictionConstraintDescArray.begin(); + + PxI32* thresholdPairsOut = &mContext.mThresholdStreamOut; + + SolverIslandParams& params = *reinterpret_cast(mContext.getTaskPool().allocate(sizeof(SolverIslandParams))); + params.positionIterations = mThreadContext.mMaxSolverPositionIterations; + params.velocityIterations = mThreadContext.mMaxSolverVelocityIterations; + params.bodyListStart = solverBodies; + params.bodyDataList = solverBodyDatas; + params.solverBodyOffset = mSolverBodyOffset; + params.bodyListSize = mIslandContext.mCounts.bodies; + params.articulationListStart = mThreadContext.getArticulations().begin(); + params.articulationListSize = mThreadContext.getArticulations().size(); + params.constraintList = contactDescs; + params.constraintIndex = 0; + params.constraintIndex2 = 0; + params.bodyListIndex = 0; + params.bodyListIndex2 = 0; + params.articSolveIndex = 0; + params.articSolveIndex2 = 0; + params.bodyIntegrationListIndex = 0; + params.thresholdStream = mContext.getThresholdStream().begin(); + params.thresholdStreamLength = mContext.getThresholdStream().size(); + params.outThresholdPairs = thresholdPairsOut; + params.motionVelocityArray = mThreadContext.motionVelocityArray; + params.bodyArray = mThreadContext.mBodyCoreArray; + params.numObjectsIntegrated = 0; + params.constraintBatchHeaders = mThreadContext.contactConstraintBatchHeaders; + params.numConstraintHeaders = mThreadContext.numContactConstraintBatches; + params.headersPerPartition = mThreadContext.mConstraintsPerPartition.begin(); + params.nbPartitions = mThreadContext.mConstraintsPerPartition.size(); + params.rigidBodies = const_cast(mObjects.bodies); + params.frictionHeadersPerPartition = mThreadContext.mFrictionConstraintsPerPartition.begin(); + params.nbFrictionPartitions = mThreadContext.mFrictionConstraintsPerPartition.size(); + params.frictionConstraintBatches = mThreadContext.frictionConstraintBatchHeaders.begin(); + params.numFrictionConstraintHeaders = mThreadContext.frictionConstraintBatchHeaders.size(); + params.frictionConstraintIndex = 0; + params.frictionConstraintList = frictionDescs; + params.mMaxArticulationLinks = mThreadContext.mMaxArticulationLinks; + params.dt = mContext.mDt; + params.invDt = mContext.mInvDt; + + const PxU32 unrollSize = 8; + const PxU32 denom = PxMax(1u, (mThreadContext.mMaxPartitions*unrollSize)); + const PxU32 MaxTasks = getTaskManager()->getCpuDispatcher()->getWorkerCount(); + const PxU32 idealThreads = (mThreadContext.numContactConstraintBatches+denom-1)/denom; + const PxU32 numTasks = PxMax(1u, PxMin(idealThreads, MaxTasks)); + + if(numTasks > 1) + { + const PxU32 idealBatchSize = PxMax(unrollSize, idealThreads*unrollSize/(numTasks*2)); + + params.batchSize = idealBatchSize; //assigning ideal batch size for the solver to grab work at. Only needed by the multi-threaded island solver. + + for(PxU32 a = 1; a < numTasks; ++a) + { + void* tsk = mContext.getTaskPool().allocate(sizeof(PxsParallelSolverTask)); + PxsParallelSolverTask* pTask = PX_PLACEMENT_NEW(tsk, PxsParallelSolverTask)( + params, mContext, mContext.getFrictionType(), mIslandSim); + + //Force to complete before merge task! + pTask->setContinuation(mCont); + + pTask->removeReference(); + } + + //Avoid kicking off one parallel task when we can do the work inline in this function + { + PX_PROFILE_ZONE("Dynamics.parallelSolve", mContext.getContextId()); + + solveParallel(mContext, params, mIslandSim); + } + const PxI32 numBodiesPlusArtics = PxI32( mIslandContext.mCounts.bodies + mIslandContext.mCounts.articulations ); + + PxI32* numObjectsIntegrated = ¶ms.numObjectsIntegrated; + + WAIT_FOR_PROGRESS_NO_TIMER(numObjectsIntegrated, numBodiesPlusArtics); + + } + else + { + + mThreadContext.mZVector.forceSize_Unsafe(0); + mThreadContext.mZVector.reserve(mThreadContext.mMaxArticulationLinks); + mThreadContext.mZVector.forceSize_Unsafe(mThreadContext.mMaxArticulationLinks); + + mThreadContext.mDeltaV.forceSize_Unsafe(0); + mThreadContext.mDeltaV.reserve(mThreadContext.mMaxArticulationLinks); + mThreadContext.mDeltaV.forceSize_Unsafe(mThreadContext.mMaxArticulationLinks); + + params.Z = mThreadContext.mZVector.begin(); + params.deltaV = mThreadContext.mDeltaV.begin(); + + //Only one task - a small island so do a sequential solve (avoid the atomic overheads) + solveVBlock(mContext.mSolverCore[mContext.getFrictionType()], params); + + const PxU32 bodyCountMin1 = mIslandContext.mCounts.bodies - 1u; + PxSolverBodyData* solverBodyData2 = solverBodyDatas + mSolverBodyOffset + 1; + for(PxU32 k=0; k < mIslandContext.mCounts.bodies; k++) + { + const PxU32 prefetchAddress = PxMin(k+4, bodyCountMin1); + Ps::prefetchLine(mThreadContext.mBodyCoreArray[prefetchAddress]); + Ps::prefetchLine(&mThreadContext.motionVelocityArray[k], 128); + Ps::prefetchLine(&mThreadContext.mBodyCoreArray[prefetchAddress], 128); + Ps::prefetchLine(&mObjects.bodies[prefetchAddress]); + + PxSolverBodyData& solverBodyData = solverBodyData2[k]; + + integrateCore(mThreadContext.motionVelocityArray[k].linear, mThreadContext.motionVelocityArray[k].angular, + solverBodies[k], solverBodyData, mContext.mDt); + + PxsRigidBody& rBody = *mObjects.bodies[k]; + PxsBodyCore& core = rBody.getCore(); + rBody.mLastTransform = core.body2World; + core.body2World = solverBodyData.body2World; + core.linearVelocity = solverBodyData.linearVelocity; + core.angularVelocity = solverBodyData.angularVelocity; + + + bool hasStaticTouch = mIslandSim.getIslandStaticTouchCount(IG::NodeIndex(solverBodyData.nodeIndex)) != 0; + sleepCheck(const_cast(mObjects.bodies[k]), mContext.mDt, mContext.mInvDt, mContext.mEnableStabilization, mContext.mUseAdaptiveForce, mThreadContext.motionVelocityArray[k], + hasStaticTouch); + } + + for(PxU32 cnt=0;cnt(cmOutput->contactForces); + PxU32 contactCount = cmOutput->nbContacts; + + cmOutput->contactPatches = manager.originalContactPatches; + cmOutput->contactPoints = manager.originalContactPoints; + cmOutput->nbContacts = manager.originalContactCount; + cmOutput->nbPatches = manager.originalPatchCount; + cmOutput->statusFlag = manager.originalStatusFlags; + cmOutput->contactForces = manager.originalForceBuffer; + + for(PxU32 a = 1; a < manager.mStride; ++a) + { + PxsContactManager* pManager = mThreadContext.orderedContactList[manager.mStartIndex + a]->contactManager; + pManager->getWorkUnit().frictionDataPtr = manager.unit->frictionDataPtr; + pManager->getWorkUnit().frictionPatchCount = manager.unit->frictionPatchCount; + //pManager->getWorkUnit().prevFrictionPatchCount = manager.unit->prevFrictionPatchCount; + } + + //This is a stride-based contact force writer. The assumption is that we may have skipped certain unimportant contacts reported by the + //discrete narrow phase + if(contactForces) + { + PxU32 currentContactIndex = 0; + PxU32 currentManagerIndex = manager.mStartIndex; + PxU32 currentManagerContactIndex = 0; + + for(PxU32 a = 0; a < contactCount; ++a) + { + PxU32 index = manager.forceBufferList[a]; + PxsContactManager* pManager = mThreadContext.orderedContactList[currentManagerIndex]->contactManager; + PxsContactManagerOutput* output = &mOutputs.getContactManager(pManager->getWorkUnit().mNpIndex); + while(currentContactIndex < index || output->nbContacts == 0) + { + //Step forwards...first in this manager... + + PxU32 numToStep = PxMin(index - currentContactIndex, PxU32(output->nbContacts) - currentManagerContactIndex); + currentContactIndex += numToStep; + currentManagerContactIndex += numToStep; + if(currentManagerContactIndex == output->nbContacts) + { + currentManagerIndex++; + currentManagerContactIndex = 0; + pManager = mThreadContext.orderedContactList[currentManagerIndex]->contactManager; + output = &mOutputs.getContactManager(pManager->getWorkUnit().mNpIndex); + } + } + if(output->nbContacts > 0 && output->contactForces) + output->contactForces[currentManagerContactIndex] = contactForces[a]; + } + } + } + + mThreadContext.compoundConstraints.forceSize_Unsafe(0); + + mThreadContext.mConstraintBlockManager.reset(); + + mContext.putThreadContext(&mThreadContext); + } + + + virtual const char* getName() const + { + return "PxsDynamics.solverEnd"; + } + + DynamicsContext& mContext; + IslandContext& mIslandContext; + const SolverIslandObjects mObjects; + const PxU32 mSolverBodyOffset; + PxsContactManagerOutputIterator& mOutputs; +}; + +class PxsSolverCreateFinalizeConstraintsTask : public Cm::Task +{ + PxsSolverCreateFinalizeConstraintsTask& operator=(const PxsSolverCreateFinalizeConstraintsTask&); +public: + + PxsSolverCreateFinalizeConstraintsTask( + DynamicsContext& context, + IslandContext& islandContext, + PxU32 solverDataOffset, + PxsContactManagerOutputIterator& outputs, + bool enhancedDeterminism) : + Cm::Task (context.getContextId()), + mContext (context), + mIslandContext (islandContext), + mSolverDataOffset (solverDataOffset), + mOutputs (outputs), + mEnhancedDeterminism (enhancedDeterminism) + { + } + + virtual void runInternal(); + + virtual const char* getName() const { return "PxsDynamics.solverCreateFinalizeConstraints"; } + + DynamicsContext& mContext; + IslandContext& mIslandContext; + PxU32 mSolverDataOffset; + PxsContactManagerOutputIterator& mOutputs; + bool mEnhancedDeterminism; +}; + + +// helper function to join two tasks together and ensure ref counts are correct +void chainTasks(PxLightCpuTask* first, PxLightCpuTask* next) +{ + first->setContinuation(next); + next->removeReference(); +} + +PxBaseTask* createSolverTaskChain(DynamicsContext& dynamicContext, + const SolverIslandObjects& objects, + const PxsIslandIndices& counts, + const PxU32 solverBodyOffset, + IG::SimpleIslandManager& islandManager, + PxU32* bodyRemapTable, PxsMaterialManager* materialManager, PxBaseTask* continuation, + PxsContactManagerOutputIterator& iterator, bool useEnhancedDeterminism) +{ + Cm::FlushPool& taskPool = dynamicContext.getTaskPool(); + + taskPool.lock(); + + + IslandContext* islandContext = reinterpret_cast(taskPool.allocate(sizeof(IslandContext))); + islandContext->mThreadContext = NULL; + islandContext->mCounts = counts; + + + // create lead task + PxsSolverStartTask* startTask = PX_PLACEMENT_NEW(taskPool.allocateNotThreadSafe(sizeof(PxsSolverStartTask)), PxsSolverStartTask)(dynamicContext, *islandContext, objects, solverBodyOffset, dynamicContext.getKinematicCount(), + islandManager, bodyRemapTable, materialManager, iterator, useEnhancedDeterminism); + PxsSolverEndTask* endTask = PX_PLACEMENT_NEW(taskPool.allocateNotThreadSafe(sizeof(PxsSolverEndTask)), PxsSolverEndTask)(dynamicContext, *islandContext, objects, solverBodyOffset, iterator); + + + PxsSolverCreateFinalizeConstraintsTask* createFinalizeConstraintsTask = PX_PLACEMENT_NEW(taskPool.allocateNotThreadSafe(sizeof(PxsSolverCreateFinalizeConstraintsTask)), PxsSolverCreateFinalizeConstraintsTask)(dynamicContext, *islandContext, solverBodyOffset, iterator, useEnhancedDeterminism); + PxsSolverSetupSolveTask* setupSolveTask = PX_PLACEMENT_NEW(taskPool.allocateNotThreadSafe(sizeof(PxsSolverSetupSolveTask)), PxsSolverSetupSolveTask)(dynamicContext, *islandContext, objects, solverBodyOffset, islandManager.getAccurateIslandSim()); + + PxsSolverConstraintPartitionTask* partitionConstraintsTask = PX_PLACEMENT_NEW(taskPool.allocateNotThreadSafe(sizeof(PxsSolverConstraintPartitionTask)), PxsSolverConstraintPartitionTask)(dynamicContext, *islandContext, objects, solverBodyOffset, useEnhancedDeterminism); + + endTask->setContinuation(continuation); + + // set up task chain in reverse order + chainTasks(setupSolveTask, endTask); + chainTasks(createFinalizeConstraintsTask, setupSolveTask); + chainTasks(partitionConstraintsTask, createFinalizeConstraintsTask); + chainTasks(startTask, partitionConstraintsTask); + + taskPool.unlock(); + + return startTask; +} + +class UpdateContinuationTask : public Cm::Task +{ + DynamicsContext& mContext; + IG::SimpleIslandManager& mSimpleIslandManager; + PxBaseTask* mLostTouchTask; + + PX_NOCOPY(UpdateContinuationTask) +public: + + UpdateContinuationTask(DynamicsContext& context, + IG::SimpleIslandManager& simpleIslandManager, + PxBaseTask* lostTouchTask, + PxU64 contextID) : Cm::Task(contextID), mContext(context), mSimpleIslandManager(simpleIslandManager), + mLostTouchTask(lostTouchTask) + { + } + + virtual const char* getName() const { return "UpdateContinuationTask"; } + + virtual void runInternal() + { + mContext.updatePostKinematic(mSimpleIslandManager, mCont, mLostTouchTask); + //Allow lost touch task to run once all tasks have be scheduled + mLostTouchTask->removeReference(); + } + +}; + + +class KinematicCopyTask : public Cm::Task +{ + const IG::NodeIndex* const mKinematicIndices; + const PxU32 mNbKinematics; + const IG::IslandSim& mIslandSim; + PxSolverBodyData* mBodyData; + + PX_NOCOPY(KinematicCopyTask) + +public: + + static const PxU32 NbKinematicsPerTask = 1024; + + KinematicCopyTask(const IG::NodeIndex* const kinematicIndices, + const PxU32 nbKinematics, const IG::IslandSim& islandSim, + PxSolverBodyData* datas, PxU64 contextID) : Cm::Task(contextID), + mKinematicIndices(kinematicIndices), mNbKinematics(nbKinematics), + mIslandSim(islandSim), mBodyData(datas) + { + } + + virtual const char* getName() const { return "KinematicCopyTask"; } + + virtual void runInternal() + { + for (PxU32 i = 0; igetCore(); + copyToSolverBodyData(core.linearVelocity, core.angularVelocity, core.inverseMass, core.inverseInertia, core.body2World, core.maxPenBias, + core.maxContactImpulse, mKinematicIndices[i].index(), core.contactReportThreshold, mBodyData[i + 1], core.lockFlags); + rigidBody->saveLastCCDTransform(); + } + } +}; + + + +void DynamicsContext::update(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask, + PxsContactManager** /*foundPatchManagers*/, PxU32 /*nbFoundPatchManagers*/, + PxsContactManager** /*lostPatchManagers*/, PxU32 /*nbLostPatchManagers*/, + PxU32 /*maxPatchesPerCM*/, + PxsContactManagerOutputIterator& iterator, + PxsContactManagerOutput*, + const PxReal dt, const PxVec3& gravity, const PxU32 /*bitMapWordCounts*/) +{ + PX_PROFILE_ZONE("Dynamics.solverQueueTasks", mContextID); + + PX_UNUSED(simpleIslandManager); + + mOutputIterator = iterator; + + mDt = dt; + mInvDt = dt == 0.0f ? 0.0f : 1.0f / dt; + mGravity = gravity; + + const IG::IslandSim& islandSim = simpleIslandManager.getAccurateIslandSim(); + + const PxU32 islandCount = islandSim.getNbActiveIslands(); + + const PxU32 activatedContactCount = islandSim.getNbActivatedEdges(IG::Edge::eCONTACT_MANAGER); + const IG::EdgeIndex* const activatingEdges = islandSim.getActivatedEdges(IG::Edge::eCONTACT_MANAGER); + + for (PxU32 a = 0; a < activatedContactCount; ++a) + { + PxsContactManager* cm = simpleIslandManager.getContactManager(activatingEdges[a]); + if (cm) + { + cm->getWorkUnit().frictionPatchCount = 0; //KS - zero the friction patch count on any activating edges + } + } + +#if PX_ENABLE_SIM_STATS + if (islandCount > 0) + { + mSimStats.mNbActiveKinematicBodies = islandSim.getNbActiveKinematics(); + mSimStats.mNbActiveDynamicBodies = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + mSimStats.mNbActiveConstraints = islandSim.getNbActiveEdges(IG::Edge::eCONSTRAINT); + } + else + { + mSimStats.mNbActiveKinematicBodies = islandSim.getNbActiveKinematics(); + mSimStats.mNbActiveDynamicBodies = 0; + mSimStats.mNbActiveConstraints = 0; + } +#endif + + mThresholdStreamOut = 0; + + resetThreadContexts(); + + //If there is no work to do then we can do nothing at all. + if (0 == islandCount) + { + return; + } + + //Block to make sure it doesn't run before stage2 of update! + lostTouchTask->addReference(); + + UpdateContinuationTask* task = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(UpdateContinuationTask)), UpdateContinuationTask) + (*this, simpleIslandManager, lostTouchTask, mContextID); + + task->setContinuation(continuation); + + //KS - test that world solver body's velocities are finite and 0, then set it to 0. + //Technically, the velocity should always be 0 but can be stomped if a NAN creeps into the simulation. + PX_ASSERT(mWorldSolverBody.linearVelocity == PxVec3(0.f)); + PX_ASSERT(mWorldSolverBody.angularState == PxVec3(0.f)); + PX_ASSERT(mWorldSolverBody.linearVelocity.isFinite()); + PX_ASSERT(mWorldSolverBody.angularState.isFinite()); + + mWorldSolverBody.linearVelocity = mWorldSolverBody.angularState = PxVec3(0.f); + + const PxU32 kinematicCount = islandSim.getNbActiveKinematics(); + const IG::NodeIndex* const kinematicIndices = islandSim.getActiveKinematics(); + mKinematicCount = kinematicCount; + + const PxU32 bodyCount = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + PxU32 numArtics = islandSim.getNbActiveNodes(IG::Node::eARTICULATION_TYPE); + + { + if (kinematicCount + bodyCount > mSolverBodyPool.capacity()) + { + mSolverBodyPool.reserve((kinematicCount + bodyCount + 31) & ~31); // pad out to 32 * 128 = 4k to prevent alloc churn + mSolverBodyDataPool.reserve((kinematicCount + bodyCount + 31 + 1) & ~31); // pad out to 32 * 128 = 4k to prevent alloc churn + mSolverBodyRemapTable.reserve((kinematicCount + bodyCount + 31 + 1) & ~31); + } + + { + PxSolverBody emptySolverBody; + PxMemZero(&emptySolverBody, sizeof(PxSolverBody)); + mSolverBodyPool.resize(kinematicCount + bodyCount, emptySolverBody); + PxSolverBodyData emptySolverBodyData; + PxMemZero(&emptySolverBodyData, sizeof(PxSolverBodyData)); + mSolverBodyDataPool.resize(kinematicCount + bodyCount + 1, emptySolverBodyData); + mSolverBodyRemapTable.resize(bodyCount); + } + + // integrate and copy all the kinematics - overkill, since not all kinematics + // need solver bodies + + mSolverBodyDataPool[0] = mWorldSolverBodyData; + + + { + PX_PROFILE_ZONE("Dynamics.updateKinematics", mContextID); + PxMemZero(mSolverBodyPool.begin(), kinematicCount * sizeof(PxSolverBody)); + for (PxU32 i = 0; i < kinematicCount; i+= KinematicCopyTask::NbKinematicsPerTask) + { + const PxU32 nbToProcess = PxMin(KinematicCopyTask::NbKinematicsPerTask, kinematicCount - i); + + KinematicCopyTask* copyTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(KinematicCopyTask)), KinematicCopyTask) + (&kinematicIndices[i], nbToProcess, islandSim, &mSolverBodyDataPool[i], mContextID); + + copyTask->setContinuation(task); + + copyTask->removeReference(); + } + } + } + + //Resize arrays of solver constraints... + + PxU32 numArticulationConstraints = numArtics*Dy::DY_ARTICULATION_MAX_SIZE; //Just allocate enough memory to fit worst-case maximum size articulations... + + const PxU32 nbActiveContactManagers = islandSim.getNbActiveEdges(IG::Edge::eCONTACT_MANAGER); + const PxU32 nbActiveConstraints = islandSim.getNbActiveEdges(IG::Edge::eCONSTRAINT); + + PxU32 totalConstraintCount = nbActiveConstraints + nbActiveContactManagers + numArticulationConstraints; + + mSolverConstraintDescPool.forceSize_Unsafe(0); + mSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mOrderedSolverConstraintDescPool.forceSize_Unsafe(0); + mOrderedSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mOrderedSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mTempSolverConstraintDescPool.forceSize_Unsafe(0); + mTempSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mTempSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mContactConstraintBatchHeaders.forceSize_Unsafe(0); + mContactConstraintBatchHeaders.reserve((totalConstraintCount + 63) & (~63)); + mContactConstraintBatchHeaders.forceSize_Unsafe(totalConstraintCount); + + mContactList.forceSize_Unsafe(0); + mContactList.reserve((nbActiveContactManagers + 63u) & (~63u)); + mContactList.forceSize_Unsafe(nbActiveContactManagers); + + mMotionVelocityArray.forceSize_Unsafe(0); + mMotionVelocityArray.reserve((bodyCount + 63u) & (~63u)); + mMotionVelocityArray.forceSize_Unsafe(bodyCount); + + mBodyCoreArray.forceSize_Unsafe(0); + mBodyCoreArray.reserve((bodyCount + 63u) & (~63u)); + mBodyCoreArray.forceSize_Unsafe(bodyCount); + + mRigidBodyArray.forceSize_Unsafe(0); + mRigidBodyArray.reserve((bodyCount + 63u) & (~63u)); + mRigidBodyArray.forceSize_Unsafe(bodyCount); + + mArticulationArray.forceSize_Unsafe(0); + mArticulationArray.reserve((numArtics + 63u) & (~63u)); + mArticulationArray.forceSize_Unsafe(numArtics); + + mNodeIndexArray.forceSize_Unsafe(0); + mNodeIndexArray.reserve((bodyCount + 63u) & (~63u)); + mNodeIndexArray.forceSize_Unsafe(bodyCount); + + + ThresholdStream& stream = getThresholdStream(); + stream.forceSize_Unsafe(0); + stream.reserve(Ps::nextPowerOfTwo(nbActiveContactManagers != 0 ? nbActiveContactManagers - 1 : nbActiveContactManagers)); + + //flip exceeded force threshold buffer + mCurrentIndex = 1 - mCurrentIndex; + + + task->removeReference(); + +} + +void DynamicsContext::updatePostKinematic(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* /*continuation*/, PxBaseTask* lostTouchTask) +{ + + const IG::IslandSim& islandSim = simpleIslandManager.getAccurateIslandSim(); + + const PxU32 islandCount = islandSim.getNbActiveIslands(); + + PxU32 constraintIndex = 0; + + PxU32 solverBatchMax = mSolverBatchSize; + PxU32 articulationBatchMax = 2; + PxU32 minimumConstraintCount = 1; + + + + //create force threshold tasks to produce force change events + PxsForceThresholdTask* forceThresholdTask = PX_PLACEMENT_NEW(getTaskPool().allocate(sizeof(PxsForceThresholdTask)), PxsForceThresholdTask)(*this); + forceThresholdTask->setContinuation(lostTouchTask); + + const IG::IslandId*const islandIds = islandSim.getActiveIslands(); + + PxU32 currentIsland = 0; + PxU32 currentBodyIndex = 0; + PxU32 currentArticulation = 0; + PxU32 currentContact = 0; + //while(start 0) + { + PxBaseTask* task = createSolverTaskChain(*this, objectStarts, counts, + mKinematicCount + currentBodyIndex, simpleIslandManager, mSolverBodyRemapTable.begin(), mMaterialManager, forceThresholdTask, mOutputIterator, mUseEnhancedDeterminism); + task->removeReference(); + } + + currentBodyIndex += nbBodies; + currentArticulation += nbArticulations; + currentContact += nbContactManagers; + + constraintIndex += constraintCount; + } + + //kick off forceThresholdTask + forceThresholdTask->removeReference(); +} + +void DynamicsContext::updateBodyCore(PxBaseTask* continuation) +{ + PX_UNUSED(continuation); +} + +void DynamicsContext::mergeResults() +{ + PX_PROFILE_ZONE("Dynamics.solverMergeResults", mContextID); + //OK. Sum up sim stats here... + +#if PX_ENABLE_SIM_STATS + PxcThreadCoherentCacheIterator threadContextIt(mThreadContextPool); + ThreadContext* threadContext = threadContextIt.getNext(); + + while(threadContext != NULL) + { + ThreadContext::ThreadSimStats& threadStats = threadContext->getSimStats(); + addThreadStats(threadStats); + threadStats.clear(); + threadContext = threadContextIt.getNext(); + } +#endif +} + + +static void preIntegrationParallel( + const PxF32 dt, + PxsBodyCore*const* bodyArray, // INOUT: core body attributes + PxsRigidBody*const* originalBodyArray, // IN: original bodies (LEGACY - DON'T deref the ptrs!!) + PxU32 const* nodeIndexArray, // IN: island node index + PxU32 bodyCount, // IN: body count + PxSolverBody* solverBodyPool, // IN: solver body pool (space preallocated) + PxSolverBodyData* solverBodyDataPool, // IN: solver body data pool (space preallocated) + volatile PxU32* maxSolverPositionIterations, + volatile PxU32* maxSolverVelocityIterations, + const PxVec3& gravity) +{ + PxU32 localMaxPosIter = 0; + PxU32 localMaxVelIter = 0; + + + for(PxU32 a = 1; a < bodyCount; ++a) + { + PxU32 i = a-1; + Ps::prefetchLine(bodyArray[a]); + Ps::prefetchLine(bodyArray[a],128); + Ps::prefetchLine(&solverBodyDataPool[a]); + Ps::prefetchLine(&solverBodyDataPool[a],128); + + PxsBodyCore& core = *bodyArray[i]; + const PxsRigidBody& rBody = *originalBodyArray[i]; + + PxU16 iterWord = core.solverIterationCounts; + localMaxPosIter = PxMax(PxU32(iterWord & 0xff), localMaxPosIter); + localMaxVelIter = PxMax(PxU32(iterWord >> 8), localMaxVelIter); + + //const Cm::SpatialVector& accel = originalBodyArray[i]->getAccelerationV(); + bodyCoreComputeUnconstrainedVelocity(gravity, dt, core.linearDamping, core.angularDamping, rBody.accelScale, core.maxLinearVelocitySq, core.maxAngularVelocitySq, + core.linearVelocity, core.angularVelocity, !!(rBody.mInternalFlags & PxcRigidBody::eDISABLE_GRAVITY)); + + copyToSolverBodyData(core.linearVelocity, core.angularVelocity, core.inverseMass, core.inverseInertia, core.body2World, core.maxPenBias, core.maxContactImpulse, nodeIndexArray[i], + core.contactReportThreshold, solverBodyDataPool[i + 1], core.lockFlags); + solverBodyPool[i].solverProgress = 0; + solverBodyPool[i].maxSolverNormalProgress = 0; + solverBodyPool[i].maxSolverFrictionProgress = 0; + } + const PxU32 i = bodyCount - 1; + PxsBodyCore& core = *bodyArray[i]; + const PxsRigidBody& rBody = *originalBodyArray[i]; + + PxU16 iterWord = core.solverIterationCounts; + localMaxPosIter = PxMax(PxU32(iterWord & 0xff), localMaxPosIter); + localMaxVelIter = PxMax(PxU32(iterWord >> 8), localMaxVelIter); + + bodyCoreComputeUnconstrainedVelocity(gravity, dt, core.linearDamping, core.angularDamping, rBody.accelScale, core.maxLinearVelocitySq, core.maxAngularVelocitySq, + core.linearVelocity, core.angularVelocity, !!(rBody.mInternalFlags & PxcRigidBody::eDISABLE_GRAVITY)); + + copyToSolverBodyData(core.linearVelocity, core.angularVelocity, core.inverseMass, core.inverseInertia, core.body2World, core.maxPenBias, core.maxContactImpulse, nodeIndexArray[i], + core.contactReportThreshold, solverBodyDataPool[i + 1], core.lockFlags); + solverBodyPool[i].solverProgress = 0; + solverBodyPool[i].maxSolverNormalProgress = 0; + solverBodyPool[i].maxSolverFrictionProgress = 0; + + physx::shdfnd::atomicMax(reinterpret_cast(maxSolverPositionIterations), PxI32(localMaxPosIter)); + physx::shdfnd::atomicMax(reinterpret_cast(maxSolverVelocityIterations), PxI32(localMaxVelIter)); +} + + +void PxsPreIntegrateTask::runInternal() +{ + { + PX_PROFILE_ZONE("PreIntegration", 0); + preIntegrationParallel(mDt, mBodyArray + mStartIndex, mOriginalBodyArray + mStartIndex, mNodeIndexArray + mStartIndex, mNumToIntegrate, + mSolverBodies + mStartIndex, mSolverBodyDataPool + mStartIndex, + mMaxSolverPositionIterations, mMaxSolverVelocityIterations, mGravity); + } +} + +void DynamicsContext::preIntegrationParallel( + const PxF32 dt, + PxsBodyCore*const* bodyArray, // INOUT: core body attributes + PxsRigidBody*const* originalBodyArray, // IN: original bodies (LEGACY - DON'T deref the ptrs!!) + PxU32 const* nodeIndexArray, // IN: island node index + PxU32 bodyCount, // IN: body count + PxSolverBody* solverBodyPool, // IN: solver body pool (space preallocated) + PxSolverBodyData* solverBodyDataPool, // IN: solver body data pool (space preallocated) + Cm::SpatialVector* /*motionVelocityArray*/, // OUT: motion velocities + PxU32& maxSolverPositionIterations, + PxU32& maxSolverVelocityIterations, + PxBaseTask& task + ) +{ + //TODO - make this based on some variables so we can try different configurations + const PxU32 IntegrationPerThread = 256; + + const PxU32 numTasks = ((bodyCount + IntegrationPerThread-1)/IntegrationPerThread); + const PxU32 taskBatchSize = 64; + + for(PxU32 i = 0; i < numTasks; i+=taskBatchSize) + { + const PxU32 nbTasks = PxMin(numTasks - i, taskBatchSize); + PxsPreIntegrateTask* tasks = reinterpret_cast(getTaskPool().allocate(sizeof(PxsPreIntegrateTask)*nbTasks)); + for(PxU32 a = 0; a < nbTasks; ++a) + { + PxU32 startIndex = (i+a)*IntegrationPerThread; + PxU32 nbToIntegrate = PxMin((bodyCount-startIndex), IntegrationPerThread); + PxsPreIntegrateTask* pTask = PX_PLACEMENT_NEW(&tasks[a], PxsPreIntegrateTask)(*this, bodyArray, + originalBodyArray, nodeIndexArray, solverBodyPool, solverBodyDataPool, dt, bodyCount, + &maxSolverPositionIterations, &maxSolverVelocityIterations, startIndex, + nbToIntegrate, mGravity); + + pTask->setContinuation(&task); + pTask->removeReference(); + } + } + + PxMemZero(solverBodyPool, bodyCount * sizeof(PxSolverBody)); +} + +inline void WaitBodyRequiredState(volatile PxU32* state, PxU32 requiredState) +{ + while(requiredState != *state ); +} + +void solveParallel(SOLVER_PARALLEL_METHOD_ARGS) +{ + Dy::ThreadContext& threadContext = *context.getThreadContext(); + threadContext.mZVector.forceSize_Unsafe(0); + threadContext.mZVector.reserve(params.mMaxArticulationLinks); + threadContext.mZVector.forceSize_Unsafe(params.mMaxArticulationLinks); + + threadContext.mDeltaV.forceSize_Unsafe(0); + threadContext.mDeltaV.reserve(params.mMaxArticulationLinks); + threadContext.mDeltaV.forceSize_Unsafe(params.mMaxArticulationLinks); + + context.solveParallel(params, islandSim, threadContext.mZVector.begin(), threadContext.mDeltaV.begin()); + + context.putThreadContext(&threadContext); +} + + +void DynamicsContext::solveParallel(SolverIslandParams& params, IG::IslandSim& islandSim, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) +{ + PxI32 targetCount = mSolverCore[mFrictionType]->solveVParallelAndWriteBack(params, Z, deltaV); + + PxI32* solveCount = ¶ms.constraintIndex2; + + //PxI32 targetCount = (PxI32)(params.numConstraintHeaders * (params.velocityIterations + params.positionIterations)); + + WAIT_FOR_PROGRESS_NO_TIMER(solveCount, targetCount); + + integrateCoreParallel(params, islandSim); +} + +void DynamicsContext::integrateCoreParallel(SolverIslandParams& params, IG::IslandSim& islandSim) +{ + const PxI32 unrollCount = 128; + + PxI32* bodyIntegrationListIndex = ¶ms.bodyIntegrationListIndex; + + PxI32 index = physx::shdfnd::atomicAdd(bodyIntegrationListIndex, unrollCount) - unrollCount; + + const PxI32 numBodies = PxI32(params.bodyListSize); + const PxI32 numArtics = PxI32(params.articulationListSize); + + Cm::SpatialVector* PX_RESTRICT motionVelocityArray = params.motionVelocityArray; + PxsBodyCore*const* bodyArray = params.bodyArray; + PxsRigidBody** PX_RESTRICT rigidBodies = params.rigidBodies; + ArticulationSolverDesc* PX_RESTRICT articulationListStart = params.articulationListStart; + + + PxI32 numIntegrated = 0; + + PxI32 bodyRemainder = unrollCount; + + while(index < numArtics) + { + const PxI32 remainder = PxMin(numArtics - index, unrollCount); + bodyRemainder -= remainder; + + for(PxI32 a = 0; a < remainder; ++a, index++) + { + const PxI32 i = index; + { + PX_PROFILE_ZONE("Articulations.integrate", mContextID); + + ArticulationPImpl::updateBodies(articulationListStart[i], mDt); + } + + ++numIntegrated; + } + if(bodyRemainder == 0) + { + index = physx::shdfnd::atomicAdd(bodyIntegrationListIndex, unrollCount) - unrollCount; + bodyRemainder = unrollCount; + } + } + + index -= numArtics; + + const PxI32 unrollPlusArtics = unrollCount + numArtics; + + PxSolverBody* PX_RESTRICT solverBodies = params.bodyListStart; + PxSolverBodyData* PX_RESTRICT solverBodyData = params.bodyDataList + params.solverBodyOffset+1; + + while(index < numBodies) + { + const PxI32 remainder = PxMin(numBodies - index, bodyRemainder); + bodyRemainder -= remainder; + for(PxI32 a = 0; a < remainder; ++a, index++) + { + const PxI32 prefetch = PxMin(index+4, numBodies - 1); + Ps::prefetchLine(bodyArray[prefetch]); + Ps::prefetchLine(bodyArray[prefetch],128); + Ps::prefetchLine(&solverBodies[index],128); + Ps::prefetchLine(&motionVelocityArray[index],128); + Ps::prefetchLine(&bodyArray[index+32]); + Ps::prefetchLine(&rigidBodies[prefetch]); + + PxSolverBodyData& data = solverBodyData[index]; + + integrateCore(motionVelocityArray[index].linear, motionVelocityArray[index].angular, + solverBodies[index], data, mDt); + + PxsRigidBody& rBody = *rigidBodies[index]; + PxsBodyCore& core = rBody.getCore(); + rBody.mLastTransform = core.body2World; + core.body2World = data.body2World; + core.linearVelocity = data.linearVelocity; + core.angularVelocity = data.angularVelocity; + + bool hasStaticTouch = islandSim.getIslandStaticTouchCount(IG::NodeIndex(data.nodeIndex)) != 0; + sleepCheck(rigidBodies[index], mDt, mInvDt, mEnableStabilization, mUseAdaptiveForce, motionVelocityArray[index], hasStaticTouch); + + ++numIntegrated; + } + + { + index = physx::shdfnd::atomicAdd(bodyIntegrationListIndex, unrollCount) - unrollPlusArtics; + bodyRemainder = unrollCount; + } + } + + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(¶ms.numObjectsIntegrated, numIntegrated); +} + +static PxU32 createFinalizeContacts_Parallel(PxSolverBodyData* solverBodyData, ThreadContext& mThreadContext, DynamicsContext& context, + PxU32 startIndex, PxU32 endIndex, PxsContactManagerOutputIterator& outputs) +{ + PX_PROFILE_ZONE("createFinalizeContacts_Parallel", 0); + const PxFrictionType::Enum frictionType = context.getFrictionType(); + const PxReal correlationDist = context.getCorrelationDistance(); + const PxReal bounceThreshold = context.getBounceThreshold(); + const PxReal frictionOffsetThreshold = context.getFrictionOffsetThreshold(); + const PxReal dt = context.getDt(); + const PxReal invDt = PxMin(context.getMaxBiasCoefficient(), context.getInvDt()); + const PxReal solverOffsetSlop = context.getSolverOffsetSlop(); + + PxSolverConstraintDesc* contactDescPtr = mThreadContext.orderedContactConstraints; + + PxConstraintBatchHeader* headers = mThreadContext.contactConstraintBatchHeaders; + + PxI32 axisConstraintCount = 0; + ThreadContext* threadContext = context.getThreadContext(); + threadContext->mConstraintBlockStream.reset(); //ensure there's no left-over memory that belonged to another island + + threadContext->mZVector.forceSize_Unsafe(0); + threadContext->mZVector.reserve(mThreadContext.mMaxArticulationLinks); + threadContext->mZVector.forceSize_Unsafe(mThreadContext.mMaxArticulationLinks); + + //threadContext->mDeltaV.forceSize_Unsafe(0); + //threadContext->mDeltaV.reserve(mThreadContext.mMaxArticulationLinks); + //threadContext->mDeltaV.forceSize_Unsafe(mThreadContext.mMaxArticulationLinks); + + Cm::SpatialVectorF* Z = threadContext->mZVector.begin(); + + PxTransform idt(PxIdentity); + + BlockAllocator blockAllocator(mThreadContext.mConstraintBlockManager, threadContext->mConstraintBlockStream, threadContext->mFrictionPatchStreamPair, threadContext->mConstraintSize); + + const PxReal ccdMaxSeparation = context.getCCDSeparationThreshold(); + + for(PxU32 a = startIndex; a < endIndex; ++a) + { + + PxConstraintBatchHeader& header = headers[a]; + + if(contactDescPtr[header.mStartIndex].constraintLengthOver16 == DY_SC_TYPE_RB_CONTACT) + { + + + PxSolverContactDesc blockDescs[4]; + PxsContactManagerOutput* cmOutputs[4]; + PxsContactManager* cms[4]; + for (PxU32 i = 0; i < header.mStride; ++i) + { + PxSolverConstraintDesc& desc = contactDescPtr[header.mStartIndex + i]; + PxSolverContactDesc& blockDesc = blockDescs[i]; + PxsContactManager* cm = reinterpret_cast(desc.constraint); + + cms[i] = cm; + + PxcNpWorkUnit& unit = cm->getWorkUnit(); + + cmOutputs[i] = &outputs.getContactManager(unit.mNpIndex); + + PxSolverBodyData& data0 = desc.linkIndexA != 0xffff ? solverBodyData[0] : solverBodyData[desc.bodyADataIndex]; + PxSolverBodyData& data1 = desc.linkIndexB != 0xffff ? solverBodyData[0] : solverBodyData[desc.bodyBDataIndex]; + + blockDesc.data0 = &data0; + blockDesc.data1 = &data1; + + PxU8 flags = unit.rigidCore0->mFlags; + if (unit.rigidCore1) + flags |= PxU8(unit.rigidCore1->mFlags); + + blockDesc.bodyFrame0 = unit.rigidCore0->body2World; + blockDesc.bodyFrame1 = unit.rigidCore1 ? unit.rigidCore1->body2World : idt; + blockDesc.shapeInteraction = cm->getShapeInteraction(); + blockDesc.contactForces = cmOutputs[i]->contactForces; + blockDesc.desc = &desc; + blockDesc.body0 = desc.bodyA; + blockDesc.body1 = desc.bodyB; + blockDesc.hasForceThresholds = !!(unit.flags & PxcNpWorkUnitFlag::eFORCE_THRESHOLD); + blockDesc.disableStrongFriction = !!(unit.flags & PxcNpWorkUnitFlag::eDISABLE_STRONG_FRICTION); + blockDesc.bodyState0 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) ? PxSolverContactDesc::eARTICULATION : PxSolverContactDesc::eDYNAMIC_BODY; + blockDesc.bodyState1 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) ? PxSolverContactDesc::eARTICULATION : (unit.flags & PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR) ? PxSolverContactDesc::eKINEMATIC_BODY : + ((unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1) ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eSTATIC_BODY); + //blockDesc.flags = unit.flags; + + PxReal dominance0 = unit.dominance0 ? 1.f : 0.f; + PxReal dominance1 = unit.dominance1 ? 1.f : 0.f; + + blockDesc.mInvMassScales.linear0 = blockDesc.mInvMassScales.angular0 = dominance0; + blockDesc.mInvMassScales.linear1 = blockDesc.mInvMassScales.angular1 = dominance1; + blockDesc.restDistance = unit.restDistance; + blockDesc.frictionPtr = unit.frictionDataPtr; + blockDesc.frictionCount = unit.frictionPatchCount; + blockDesc.maxCCDSeparation = (flags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) ? ccdMaxSeparation : PX_MAX_F32; + + } + +#if DY_BATCH_CONSTRAINTS + SolverConstraintPrepState::Enum state = SolverConstraintPrepState::eUNBATCHABLE; + if(header.mStride == 4) + { + //KS - todo - plumb in axisConstraintCount into this method to keep track of the number of axes + state = createFinalizeMethods4[frictionType](cmOutputs, *threadContext, + blockDescs, + invDt, + bounceThreshold, + frictionOffsetThreshold, + correlationDist, + solverOffsetSlop, + blockAllocator); + + } + if(SolverConstraintPrepState::eSUCCESS != state) +#endif + { + for(PxU32 i = 0; i < header.mStride; ++i) + { + PxSolverConstraintDesc& desc = contactDescPtr[header.mStartIndex+i]; + PxsContactManager* cm = reinterpret_cast(desc.constraint); + PxcNpWorkUnit& n = cm->getWorkUnit(); + + PxsContactManagerOutput& output = outputs.getContactManager(n.mNpIndex); + + createFinalizeMethods[frictionType](blockDescs[i], output, *threadContext, + invDt, bounceThreshold, frictionOffsetThreshold, correlationDist, solverOffsetSlop, + blockAllocator, Z); + + getContactManagerConstraintDesc(output,*cm,desc); + } + } + + for (PxU32 i = 0; i < header.mStride; ++i) + { + PxsContactManager* cm = cms[i]; + + PxcNpWorkUnit& unit = cm->getWorkUnit(); + unit.frictionDataPtr = blockDescs[i].frictionPtr; + unit.frictionPatchCount = blockDescs[i].frictionCount; + axisConstraintCount += blockDescs[i].axisConstraintCount; + + } + } + else if(contactDescPtr[header.mStartIndex].constraintLengthOver16 == DY_SC_TYPE_RB_1D) + { + + SolverConstraintShaderPrepDesc shaderDescs[4]; + PxSolverConstraintPrepDesc descs[4]; + + PxTransform id(PxIdentity); + + for (PxU32 i = 0; i < header.mStride; ++i) + { + PxSolverConstraintDesc& desc = contactDescPtr[header.mStartIndex + i]; + const Constraint* constraint = reinterpret_cast(desc.constraint); + + SolverConstraintShaderPrepDesc& shaderPrepDesc = shaderDescs[i]; + PxSolverConstraintPrepDesc& prepDesc = descs[i]; + + const PxConstraintSolverPrep solverPrep = constraint->solverPrep; + const void* constantBlock = constraint->constantBlock; + const PxU32 constantBlockByteSize = constraint->constantBlockSize; + const PxTransform& pose0 = (constraint->body0 ? constraint->body0->getPose() : id); + const PxTransform& pose1 = (constraint->body1 ? constraint->body1->getPose() : id); + const PxSolverBody* sbody0 = desc.bodyA; + const PxSolverBody* sbody1 = desc.bodyB; + PxSolverBodyData* sbodyData0 = &solverBodyData[desc.linkIndexA != PxSolverConstraintDesc::NO_LINK ? 0 : desc.bodyADataIndex]; + PxSolverBodyData* sbodyData1 = &solverBodyData[desc.linkIndexB != PxSolverConstraintDesc::NO_LINK ? 0 : desc.bodyBDataIndex]; + + shaderPrepDesc.constantBlock = constantBlock; + shaderPrepDesc.constantBlockByteSize = constantBlockByteSize; + shaderPrepDesc.constraint = constraint; + shaderPrepDesc.solverPrep = solverPrep; + + prepDesc.desc = &desc; + prepDesc.bodyFrame0 = pose0; + prepDesc.bodyFrame1 = pose1; + prepDesc.data0 = sbodyData0; + prepDesc.data1 = sbodyData1; + prepDesc.body0 = sbody0; + prepDesc.body1 = sbody1; + prepDesc.linBreakForce = constraint->linBreakForce; + prepDesc.angBreakForce = constraint->angBreakForce; + prepDesc.writeback = &context.getConstraintWriteBackPool()[constraint->index]; + prepDesc.disablePreprocessing = !!(constraint->flags & PxConstraintFlag::eDISABLE_PREPROCESSING); + prepDesc.improvedSlerp = !!(constraint->flags & PxConstraintFlag::eIMPROVED_SLERP); + prepDesc.driveLimitsAreForces = !!(constraint->flags & PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES); + prepDesc.extendedLimits = !!(constraint->flags & PxConstraintFlag::eENABLE_EXTENDED_LIMITS); + prepDesc.minResponseThreshold = constraint->minResponseThreshold; + } + +#if DY_BATCH_CONSTRAINTS && DY_BATCH_1D + SolverConstraintPrepState::Enum state = SolverConstraintPrepState::eUNBATCHABLE; + if(header.mStride == 4) + { + PxU32 totalRows; + state = setupSolverConstraint4 + (shaderDescs, descs, dt, invDt, totalRows, + blockAllocator); + + axisConstraintCount += totalRows; + } + if(state != SolverConstraintPrepState::eSUCCESS) +#endif + { + for(PxU32 i = 0; i < header.mStride; ++i) + { + axisConstraintCount += SetupSolverConstraint(shaderDescs[i], descs[i], blockAllocator, dt, invDt, Z); + } + } + } + } + + threadContext->getSimStats().numAxisSolverConstraints += axisConstraintCount; + + context.putThreadContext(threadContext); + return PxU32(axisConstraintCount); //Can't write to mThreadContext as it's shared!!!! +} + +class PxsCreateFinalizeContactsTask : public Cm::Task +{ + PxsCreateFinalizeContactsTask& operator=(const PxsCreateFinalizeContactsTask&); +public: + PxsCreateFinalizeContactsTask( const PxU32 numConstraints, PxSolverConstraintDesc* descArray, PxSolverBodyData* solverBodyData, + ThreadContext& threadContext, DynamicsContext& context, PxU32 startIndex, PxU32 endIndex, PxsContactManagerOutputIterator& outputs) : + Cm::Task(context.getContextId()), + mNumConstraints(numConstraints), mDescArray(descArray), mSolverBodyData(solverBodyData), + mThreadContext(threadContext), mDynamicsContext(context), + mOutputs(outputs), + mStartIndex(startIndex), mEndIndex(endIndex) + {} + + virtual void runInternal() + { + createFinalizeContacts_Parallel(mSolverBodyData, mThreadContext, mDynamicsContext, mStartIndex, mEndIndex, mOutputs); + } + + virtual const char* getName() const + { + return "PxsDynamics.createFinalizeContacts"; + } + +public: + const PxU32 mNumConstraints; + PxSolverConstraintDesc* mDescArray; + PxSolverBodyData* mSolverBodyData; + ThreadContext& mThreadContext; + DynamicsContext& mDynamicsContext; + PxsContactManagerOutputIterator& mOutputs; + PxU32 mStartIndex; + PxU32 mEndIndex; +}; + +void PxsSolverCreateFinalizeConstraintsTask::runInternal() +{ + PX_PROFILE_ZONE("CreateConstraints", 0); + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + + + + PxU32 descCount = mThreadContext.mNumDifferentBodyConstraints; + PxU32 selfConstraintDescCount = mThreadContext.contactDescArraySize - mThreadContext.mNumDifferentBodyConstraints; + + Ps::Array& accumulatedConstraintsPerPartition = mThreadContext.mConstraintsPerPartition; + + PxU32 numHeaders = 0; + PxU32 currentPartition = 0; + PxU32 maxJ = descCount == 0 ? 0 : accumulatedConstraintsPerPartition[0]; + + const PxU32 maxBatchPartition = 0xFFFFFFFF; + + const PxU32 maxBatchSize = mEnhancedDeterminism ? 1u : 4u; + + PxU32 headersPerPartition = 0; + for(PxU32 a = 0; a < descCount;) + { + + + PxU32 loopMax = PxMin(maxJ - a, maxBatchSize); + PxU16 j = 0; + if(loopMax > 0) + { + PxConstraintBatchHeader& header = mThreadContext.contactConstraintBatchHeaders[numHeaders++]; + + j=1; + PxSolverConstraintDesc& desc = mThreadContext.orderedContactConstraints[a]; + if(!isArticulationConstraint(desc) && (desc.constraintLengthOver16 == DY_SC_TYPE_RB_CONTACT || + desc.constraintLengthOver16 == DY_SC_TYPE_RB_1D) && currentPartition < maxBatchPartition) + { + for(; j < loopMax && desc.constraintLengthOver16 == mThreadContext.orderedContactConstraints[a+j].constraintLengthOver16 && + !isArticulationConstraint(mThreadContext.orderedContactConstraints[a+j]); ++j); + } + header.mStartIndex = a; + header.mStride = j; + headersPerPartition++; + } + if(maxJ == (a + j) && maxJ != descCount) + { + //Go to next partition! + accumulatedConstraintsPerPartition[currentPartition] = headersPerPartition; + headersPerPartition = 0; + currentPartition++; + maxJ = accumulatedConstraintsPerPartition[currentPartition]; + } + a+= j; + } + if(descCount) + accumulatedConstraintsPerPartition[currentPartition] = headersPerPartition; + + + + accumulatedConstraintsPerPartition.forceSize_Unsafe(mThreadContext.mMaxPartitions); + + PxU32 numDifferentBodyBatchHeaders = numHeaders; + + for(PxU32 a = 0; a < selfConstraintDescCount; ++a) + { + PxConstraintBatchHeader& header = mThreadContext.contactConstraintBatchHeaders[numHeaders++]; + header.mStartIndex = a + descCount; + header.mStride = 1; + } + + PxU32 numSelfConstraintBatchHeaders = numHeaders - numDifferentBodyBatchHeaders; + + mThreadContext.numDifferentBodyBatchHeaders = numDifferentBodyBatchHeaders; + mThreadContext.numSelfConstraintBatchHeaders = numSelfConstraintBatchHeaders; + mThreadContext.numContactConstraintBatches = numHeaders; + + PX_UNUSED(descCount); + + { + PxSolverConstraintDesc* descBegin = mThreadContext.orderedContactConstraints; + + const PxU32 numThreads = getTaskManager()->getCpuDispatcher()->getWorkerCount(); + + //Choose an appropriate number of constraint prep tasks. This must be proportionate to the number of constraints to prep and the number + //of worker threads available. + const PxU32 TaskBlockSize = 16; + const PxU32 TaskBlockLargeSize = 64; + const PxU32 BlockAllocationSize = 64; + + PxU32 numTasks = (numHeaders+TaskBlockLargeSize-1)/TaskBlockLargeSize; + + if(numTasks) + { + + if(numTasks < numThreads) + numTasks = PxMax(1u, (numHeaders+TaskBlockSize-1)/TaskBlockSize); + + const PxU32 constraintsPerTask = (numHeaders + numTasks-1)/numTasks; + + for(PxU32 i = 0; i < numTasks; i+=BlockAllocationSize) + { + PxU32 blockSize = PxMin(numTasks - i, BlockAllocationSize); + + PxsCreateFinalizeContactsTask* tasks = reinterpret_cast(mContext.getTaskPool().allocate(sizeof(PxsCreateFinalizeContactsTask)*blockSize)); + + for(PxU32 a = 0; a < blockSize; ++a) + { + PxU32 startIndex = (a + i) * constraintsPerTask; + PxU32 endIndex = PxMin(startIndex + constraintsPerTask, numHeaders); + PxsCreateFinalizeContactsTask* pTask = PX_PLACEMENT_NEW(&tasks[a], PxsCreateFinalizeContactsTask( descCount, descBegin, mContext.mSolverBodyDataPool.begin(), mThreadContext, mContext, startIndex, endIndex, mOutputs)); + + pTask->setContinuation(mCont); + pTask->removeReference(); + } + } + } + } +} + +} +} + + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h new file mode 100644 index 000000000..22bddcbab --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h @@ -0,0 +1,490 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_DYNAMICS_H +#define DY_DYNAMICS_H + +#include "PxvConfig.h" +#include "CmSpatialVector.h" +#include "CmTask.h" +#include "CmPool.h" +#include "PxcThreadCoherentCache.h" +#include "DyThreadContext.h" +#include "PxcConstraintBlockStream.h" +#include "DySolverBody.h" +#include "DyContext.h" +#include "PxsIslandManagerTypes.h" +#include "PxvNphaseImplementationContext.h" +#include "solver/PxSolverDefs.h" + +namespace physx +{ + +namespace Cm +{ + class FlushPool; +} + +namespace IG +{ + class SimpleIslandManager; + struct Edge; +} + +class PxsRigidBody; + +class PxsStreamedThresholdTable; + +struct PxsBodyCore; +struct PxsIslandObjects; +class PxsIslandIndices; +struct PxsIndexedInteraction; +class PxsIslandManager; +struct PxsIndexedConstraint; +struct PxsIndexedContactManager; +class PxsHeapMemoryAllocator; +class PxsMemoryManager; +class PxsDefaultMemoryManager; +struct PxSolverConstraintDesc; + +namespace Cm +{ + class Bitmap; + class SpatialVector; +} + +namespace Dy +{ + class SolverCore; + struct SolverIslandParams; + struct ArticulationSolverDesc; + class Articulation; + class DynamicsContext; + + + + +#define SOLVER_PARALLEL_METHOD_ARGS \ + DynamicsContext& context, \ + SolverIslandParams& params, \ + IG::IslandSim& islandSim + +//typedef void (*PxsSolveParallelMethod)(SOLVER_PARALLEL_METHOD_ARGS); +//extern PxsSolveParallelMethod solveParallel[3]; + +void solveParallel(SOLVER_PARALLEL_METHOD_ARGS); +void solveParallelCouloumFriction(SOLVER_PARALLEL_METHOD_ARGS); + + +struct SolverIslandObjects; + +/** +\brief Solver body pool (array) that enforces 128-byte alignment for base address of array. +\note This reduces cache misses on platforms with 128-byte-size cache lines by aligning the start of the array to the beginning of a cache line. +*/ +class SolverBodyPool : public Ps::Array > > +{ + PX_NOCOPY(SolverBodyPool) +public: + SolverBodyPool() {} +}; + +/** +\brief Solver body data pool (array) that enforces 128-byte alignment for base address of array. +\note This reduces cache misses on platforms with 128-byte-size cache lines by aligning the start of the array to the beginning of a cache line. +*/ +class SolverBodyDataPool : public Ps::Array > > +{ + PX_NOCOPY(SolverBodyDataPool) +public: + SolverBodyDataPool() {} +}; + +class SolverConstraintDescPool : public Ps::Array > > +{ + PX_NOCOPY(SolverConstraintDescPool) +public: + SolverConstraintDescPool() { } +}; + +/** +\brief Encapsulates an island's context +*/ + +struct IslandContext +{ + //The thread context for this island (set in in the island start task, released in the island end task) + ThreadContext* mThreadContext; + PxsIslandIndices mCounts; +}; + + +/** +\brief Encapsules the data used by the constraint solver. +*/ + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + +class DynamicsContext : public Context +{ + PX_NOCOPY(DynamicsContext) +public: + + /** + \brief Creates a DynamicsContext associated with a PxsContext + \return A pointer to the newly-created DynamicsContext. + */ + static DynamicsContext* create( PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocator, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal maxBiasCoefficient, + const bool frictionEveryIteration + ); + + /** + \brief Destroys this DynamicsContext + */ + void destroy(); + + /** + \brief Returns the static world solver body + \return The static world solver body. + */ + PX_FORCE_INLINE PxSolverBody& getWorldSolverBody() { return mWorldSolverBody; } + + PX_FORCE_INLINE Cm::FlushPool& getTaskPool() { return mTaskPool; } + + PX_FORCE_INLINE ThresholdStream& getThresholdStream() { return *mThresholdStream; } + + PX_FORCE_INLINE PxvSimStats& getSimStats() { return mSimStats; } + +#if PX_ENABLE_SIM_STATS + void addThreadStats(const ThreadContext::ThreadSimStats& stats); +#endif + + /** + \brief The entry point for the constraint solver. + \param[in] dt The simulation time-step + \param[in] continuation The continuation task for the solver + + This method is called after the island generation has completed. Its main responsibilities are: + (1) Reserving the solver body pools + (2) Initializing the static and kinematic solver bodies, which are shared resources between islands. + (3) Construct the solver task chains for each island + + Each island is solved as an independent solver task chain in parallel. + + */ + + virtual void update(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask, + PxsContactManager** foundPatchManagers, PxU32 nbFoundPatchManagers, PxsContactManager** lostPatchManagers, PxU32 nbLostPatchManagers, + PxU32 maxPatchesPerCM, PxsContactManagerOutputIterator& iter, PxsContactManagerOutput* gpuOutputs, const PxReal dt, const PxVec3& gravity, const PxU32 bitMapWordCounts); + + + void updatePostKinematic(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask); + + virtual void processLostPatches(IG::SimpleIslandManager& /*simpleIslandManager*/, PxsContactManager** /*lostPatchManagers*/, PxU32 /*nbLostPatchManagers*/, PxsContactManagerOutputIterator& /*iterator*/){} + + virtual void updateBodyCore(PxBaseTask* continuation); + + virtual void setSimulationController(PxsSimulationController* simulationController ){ mSimulationController = simulationController; } + /** + \brief This method combines the results of several islands, e.g. constructing scene-level simulation statistics and merging together threshold streams for contact notification. + */ + virtual void mergeResults(); + + virtual void getDataStreamBase(void*& /*contactStreamBase*/, void*& /*patchStreamBase*/, void*& /*forceAndIndicesStreamBase*/){} + + /** + \brief Allocates and returns a thread context object. + \return A thread context. + */ + PX_FORCE_INLINE ThreadContext* getThreadContext() + { + return mThreadContextPool.get(); + } + + /** + \brief Returns a thread context to the thread context pool. + \param[in] context The thread context to return to the thread context pool. + */ + void putThreadContext(ThreadContext* context) + { + mThreadContextPool.put(context); + } + + + PX_FORCE_INLINE PxU32 getKinematicCount() const { return mKinematicCount; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + +protected: + + /** + \brief Constructor for DynamicsContext + */ + DynamicsContext(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocator, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal maxBiasCoefficient, + const bool frictionEveryIteration + ); + /** + \brief Destructor for DynamicsContext + */ + virtual ~DynamicsContext(); + + + // Solver helper-methods + /** + \brief Computes the unconstrained velocity for a given PxsRigidBody + \param[in] atom The PxsRigidBody + */ + void computeUnconstrainedVelocity(PxsRigidBody* atom) const; + + /** + \brief fills in a PxSolverConstraintDesc from an indexed interaction + \param[in,out] desc The PxSolverConstraintDesc + \param[in] constraint The PxsIndexedInteraction + */ + void setDescFromIndices(PxSolverConstraintDesc& desc, + const PxsIndexedInteraction& constraint, const PxU32 solverBodyOffset); + + + void setDescFromIndices(PxSolverConstraintDesc& desc, IG::EdgeIndex edgeIndex, + const IG::SimpleIslandManager& islandManager, PxU32* bodyRemapTable, const PxU32 solverBodyOffset); + + /** + \brief Compute the unconstrained velocity for set of bodies in parallel. This function may spawn additional tasks. + \param[in] dt The timestep + \param[in] bodyArray The array of body cores + \param[in] originalBodyArray The array of PxsRigidBody + \param[in] nodeIndexArray The array of island node index + \param[in] bodyCount The number of bodies + \param[out] solverBodyPool The pool of solver bodies. These are synced with the corresponding body in bodyArray. + \param[out] solverBodyDataPool The pool of solver body data. These are synced with the corresponding body in bodyArray + \param[out] motionVelocityArray The motion velocities for the bodies + \param[out] maxSolverPositionIterations The maximum number of position iterations requested by any body in the island + \param[out] maxSolverVelocityIterations The maximum number of velocity iterations requested by any body in the island + \param[out] integrateTask The continuation task for any tasks spawned by this function. + */ + void preIntegrationParallel( + const PxF32 dt, + PxsBodyCore*const* bodyArray, // INOUT: core body attributes + PxsRigidBody*const* originalBodyArray, // IN: original body atom names (LEGACY - DON'T deref the ptrs!!) + PxU32 const* nodeIndexArray, // IN: island node index + PxU32 bodyCount, // IN: body count + PxSolverBody* solverBodyPool, // IN: solver atom pool (space preallocated) + PxSolverBodyData* solverBodyDataPool, + Cm::SpatialVector* motionVelocityArray, // OUT: motion velocities + PxU32& maxSolverPositionIterations, + PxU32& maxSolverVelocityIterations, + PxBaseTask& integrateTask + ); + + /** + \brief Solves an island in parallel. + + \param[in] params Solver parameter structure + */ + + void solveParallel(SolverIslandParams& params, IG::IslandSim& islandSim, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + + + void integrateCoreParallel(SolverIslandParams& params, IG::IslandSim& islandSim); + + + + + /** + \brief Resets the thread contexts + */ + void resetThreadContexts(); + + /** + \brief Returns the scratch memory allocator. + \return The scratch memory allocator. + */ + PX_FORCE_INLINE PxcScratchAllocator& getScratchAllocator() { return mScratchAllocator; } + + //Data + + /** + \brief Body to represent the world static body. + */ + PX_ALIGN(16, PxSolverBody mWorldSolverBody); + /** + \brief Body data to represent the world static body. + */ + PX_ALIGN(16, PxSolverBodyData mWorldSolverBodyData); + + /** + \brief A thread context pool + */ + PxcThreadCoherentCache mThreadContextPool; + + /** + \brief Solver constraint desc array + */ + SolverConstraintDescPool mSolverConstraintDescPool; + + /** + \brief Ordered sover constraint desc array (after partitioning) + */ + SolverConstraintDescPool mOrderedSolverConstraintDescPool; + + /** + \brief A temporary array of constraint descs used for partitioning + */ + SolverConstraintDescPool mTempSolverConstraintDescPool; + + /** + \brief An array of contact constraint batch headers + */ + Ps::Array mContactConstraintBatchHeaders; + + /** + \brief Array of motion velocities for all bodies in the scene. + */ + Ps::Array mMotionVelocityArray; + + /** + \brief Array of body core pointers for all bodies in the scene. + */ + Ps::Array mBodyCoreArray; + + /** + \brief Array of rigid body pointers for all bodies in the scene. + */ + Ps::Array mRigidBodyArray; + + /** + \brief Array of articulationpointers for all articulations in the scene. + */ + Ps::Array mArticulationArray; + + /** + \brief Global pool for solver bodies. Kinematic bodies are at the start, and then dynamic bodies + */ + SolverBodyPool mSolverBodyPool; + /** + \brief Global pool for solver body data. Kinematic bodies are at the start, and then dynamic bodies + */ + SolverBodyDataPool mSolverBodyDataPool; + + + ThresholdStream* mExceededForceThresholdStream[2]; //this store previous and current exceeded force thresholdStream + + Ps::Array mExceededForceThresholdStreamMask; + + /** + \brief Interface to the solver core. + \note We currently only support PxsSolverCoreSIMD. Other cores may be added in future releases. + */ + SolverCore* mSolverCore[PxFrictionType::eFRICTION_COUNT]; + + Ps::Array mSolverBodyRemapTable; //Remaps from the "active island" index to the index within a solver island + + Ps::Array mNodeIndexArray; //island node index + + Ps::Array mContactList; + + /** + \brief The total number of kinematic bodies in the scene + */ + PxU32 mKinematicCount; + + /** + \brief Atomic counter for the number of threshold stream elements. + */ + PxI32 mThresholdStreamOut; + + + + PxsMaterialManager* mMaterialManager; + + PxsContactManagerOutputIterator mOutputIterator; + +private: + //private: + PxcScratchAllocator& mScratchAllocator; + Cm::FlushPool& mTaskPool; + PxTaskManager* mTaskManager; + PxU32 mCurrentIndex; // this is the index point to the current exceeded force threshold stream + + PxU64 mContextID; + + protected: + + friend class PxsSolverStartTask; + friend class PxsSolverAticulationsTask; + friend class PxsSolverSetupConstraintsTask; + friend class PxsSolverCreateFinalizeConstraintsTask; + friend class PxsSolverConstraintPartitionTask; + friend class PxsSolverSetupSolveTask; + friend class PxsSolverIntegrateTask; + friend class PxsSolverEndTask; + friend class PxsSolverConstraintPostProcessTask; + friend class PxsForceThresholdTask; + friend class SolverArticulationUpdateTask; + + friend void solveParallel(SOLVER_PARALLEL_METHOD_ARGS); +}; + +#if PX_VC + #pragma warning(pop) +#endif + +} +} + +#endif //DY_DYNAMICS_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp new file mode 100644 index 000000000..c32da63c6 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp @@ -0,0 +1,4218 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsMathUtils.h" +#include "CmConeLimitHelper.h" +#include "DySolverConstraint1D.h" +#include "DyFeatherstoneArticulation.h" +#include "PxsRigidBody.h" +#include "PxcConstraintBlockStream.h" +#include "DyArticulationContactPrep.h" +#include "DyDynamics.h" +#include "DyArticulationReference.h" +#include "DyArticulationPImpl.h" +#include "DyArticulationFnsSimd.h" +#include "PxArticulation.h" +#include "PsFoundation.h" +#include "DyFeatherstoneArticulationLink.h" +#include "DyFeatherstoneArticulationJointData.h" +#include "DySolverConstraint1DStep.h" +#include "DyTGSDynamics.h" +#include "DyConstraintPrep.h" +#include "common/PxProfileZone.h" + +#ifndef FEATURESTONE_DEBUG +#define FEATURESTONE_DEBUG 0 +#endif + + +// we encode articulation link handles in the lower bits of the pointer, so the +// articulation has to be aligned, which in an aligned pool means we need to size it +// appropriately + +namespace physx +{ + +namespace Dy +{ + void SolverCoreRegisterArticulationFns(); + + void SolverCoreRegisterArticulationFnsCoulomb(); + + void PxvRegisterArticulationsReducedCoordinate() + { + PxArticulationBase::Enum type = PxArticulationBase::eReducedCoordinate; + ArticulationPImpl::sComputeUnconstrainedVelocities[type] = &FeatherstoneArticulation::computeUnconstrainedVelocities; + ArticulationPImpl::sUpdateBodies[type] = &FeatherstoneArticulation::updateBodies; + ArticulationPImpl::sUpdateBodiesTGS[type] = &FeatherstoneArticulation::updateBodiesTGS; + ArticulationPImpl::sSaveVelocity[type] = &FeatherstoneArticulation::saveVelocity; + ArticulationPImpl::sSaveVelocityTGS[type] = &FeatherstoneArticulation::saveVelocityTGS; + + ArticulationPImpl::sUpdateDeltaMotion[type] = &FeatherstoneArticulation::recordDeltaMotion; + ArticulationPImpl::sDeltaMotionToMotionVel[type] = &FeatherstoneArticulation::deltaMotionToMotionVelocity; + ArticulationPImpl::sComputeUnconstrainedVelocitiesTGS[type] = &FeatherstoneArticulation::computeUnconstrainedVelocitiesTGS; + ArticulationPImpl::sSetupInternalConstraintsTGS[type] = &FeatherstoneArticulation::setupSolverConstraintsTGS; + + SolverCoreRegisterArticulationFns(); + SolverCoreRegisterArticulationFnsCoulomb(); + } + + + ArticulationData::~ArticulationData() + { + + if (mLinksData) + PX_FREE_AND_RESET(mLinksData); + + if (mJointData) + PX_FREE_AND_RESET(mJointData); + } + + void ArticulationData::resizeLinkData(const PxU32 linkCount) + { + mMotionVelocities.reserve(linkCount); + mMotionVelocities.forceSize_Unsafe(linkCount); + + mMotionAccelerations.reserve(linkCount); + mMotionAccelerations.forceSize_Unsafe(linkCount); + + mCorioliseVectors.reserve(linkCount); + mCorioliseVectors.forceSize_Unsafe(linkCount); + + mZAForces.reserve(linkCount); + mZAForces.forceSize_Unsafe(linkCount); + + mDeltaMotionVector.reserve(linkCount); + mDeltaMotionVector.forceSize_Unsafe(linkCount); + + mPreTransform.reserve(linkCount); + mPreTransform.forceSize_Unsafe(linkCount); + + //mTempSpatialMatrix.reserve(linkCount); + //mTempSpatialMatrix.forceSize_Unsafe(linkCount); + + mAccumulatedPoses.reserve(linkCount); + mAccumulatedPoses.forceSize_Unsafe(linkCount); + + mDeltaQ.reserve(linkCount); + mDeltaQ.forceSize_Unsafe(linkCount); + + mPosIterMotionVelocities.reserve(linkCount); + mPosIterMotionVelocities.forceSize_Unsafe(linkCount); + + mJointTransmittedForce.reserve(linkCount); + mJointTransmittedForce.forceSize_Unsafe(linkCount); + + + if (mLinksData) + PX_FREE_AND_RESET(mLinksData); + + if (mJointData) + PX_FREE_AND_RESET(mJointData); + + mLinksData = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ArticulationLinkData) * linkCount, PX_DEBUG_EXP("ArticulationLinkData")), ArticulationLinkData)(); + mJointData = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ArticulationJointCoreData) * linkCount, PX_DEBUG_EXP("ArticulationJointCoreData")), ArticulationJointCoreData)(); + + + const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount; + + PxMemZero(mMotionVelocities.begin(), size); + PxMemZero(mMotionAccelerations.begin(), size); + PxMemZero(mCorioliseVectors.begin(), size); + PxMemZero(mZAForces.begin(), size); + PxMemZero(mDeltaMotionVector.begin(), size); + + PxMemZero(mPreTransform.begin(), sizeof(PxTransform) * linkCount); + + PxMemZero(mLinksData, sizeof(ArticulationLinkData) * linkCount); + PxMemZero(mJointData, sizeof(ArticulationJointCoreData) * linkCount); + + + } + + void ArticulationData::resizeJointData(const PxU32 dofs) + { + mJointAcceleration.reserve(dofs); + mJointAcceleration.forceSize_Unsafe(dofs); + + mJointVelocity.reserve(dofs); + mJointVelocity.forceSize_Unsafe(dofs); + + mJointDeltaVelocity.reserve(dofs); + mJointDeltaVelocity.forceSize_Unsafe(dofs); + + mJointPosition.reserve(dofs); + mJointPosition.forceSize_Unsafe(dofs); + + mJointForce.reserve(dofs); + mJointForce.forceSize_Unsafe(dofs); + + /* mJointFrictionForce.reserve(dofs); + mJointFrictionForce.forceSize_Unsafe(dofs);*/ + + mPosIterJointDeltaVelocities.reserve(dofs); + mPosIterJointDeltaVelocities.forceSize_Unsafe(dofs); + + + /*mTempData.mJointForce.reserve(dofs); + mTempData.mJointForce.forceSize_Unsafe(dofs);*/ + + PxMemZero(mJointAcceleration.begin(), sizeof(PxReal) * dofs); + PxMemZero(mJointVelocity.begin(), sizeof(PxReal) * dofs); + PxMemZero(mJointDeltaVelocity.begin(), sizeof(PxReal) * dofs); + PxMemZero(mPosIterJointDeltaVelocities.begin(), sizeof(PxReal)*dofs); + PxMemZero(mJointPosition.begin(), sizeof(PxReal) * dofs); + PxMemZero(mJointForce.begin(), sizeof(PxReal) * dofs); + //PxMemZero(mJointFrictionForce.begin(), sizeof(PxReal) * dofs); + } + + ArticulationLinkData& ArticulationData::getLinkData(PxU32 index) const + { + PX_ASSERT(index < mLinkCount); + return mLinksData[index]; + } + + //ArticulationJointCoreData& ArticulationData::getJointData(PxU32 index) const + //{ + // PX_ASSERT(index < mLinkCount); + // return mJointData[index]; + //} + + void ArticulationJointCore::setJointPose(ArticulationJointCoreData& jointDatum) + { + if (dirtyFlag & ArticulationJointCoreDirtyFlag::ePOSE) + { + relativeQuat = (childPose.q * (parentPose.q.getConjugate())).getNormalized(); + + jointDatum.computeMotionMatrix(static_cast(this)); + + dirtyFlag &= ~ArticulationJointCoreDirtyFlag::ePOSE; + } + } + + PX_COMPILE_TIME_ASSERT((sizeof(Articulation)&(DY_ARTICULATION_MAX_SIZE - 1)) == 0); + + FeatherstoneArticulation::FeatherstoneArticulation(Sc::ArticulationSim* sim) + : ArticulationV(sim, PxArticulationBase::eReducedCoordinate), mHasSphericalJoint(false), + mGpuRemapId(0xffffffff) + { + PX_ASSERT((reinterpret_cast(this) & (DY_ARTICULATION_MAX_SIZE - 1)) == 0); + } + + FeatherstoneArticulation::~FeatherstoneArticulation() + { + } + + void FeatherstoneArticulation::copyJointData(ArticulationData& data, PxReal* toJointData, PxReal* fromJointData) + { + const PxU32 linkCount = data.getLinkCount(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + + PxReal* dest = &toJointData[jointDatum.jointOffset]; + + PxReal* source = &fromJointData[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + dest[ind] = source[ind]; + } + } + } + + void FeatherstoneArticulation::computeDofs() + { + const PxU32 linkCount = mArticulationData.getLinkCount(); + PxU32 totalDofs = 0; + PxU32 totalLocks = 0; + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = mArticulationData.getLink(linkID); + + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + + jointDatum.computeJointDof(link.inboundJoint, true); + jointDatum.jointOffset = totalDofs; + totalDofs += jointDatum.dof; + totalLocks += jointDatum.lockedAxes; + } + + if (totalDofs != mArticulationData.getDofs()) + { + mArticulationData.resizeJointData(totalDofs); + } + + mArticulationData.setDofs(totalDofs); + mArticulationData.setLocks(totalLocks); + } + + bool FeatherstoneArticulation::resize(const PxU32 linkCount) + { + if (Dy::ArticulationV::resize(linkCount)) + { + if (linkCount != mSolverDesc.linkCount) + { + //compute scratchSize + const PxU32 linkCount4 = (linkCount + 3)&(~3); + PxU32 scratchSize = sizeof(Cm::SpatialVectorF) * linkCount4 * 4 + + sizeof(Cm::SpatialVector) * linkCount4 + + sizeof(Dy::SpatialMatrix) * linkCount4 + + sizeof(PxReal) * linkCount4 * 4; + + mScratchMemory.resize(scratchSize); + mSolverDesc.scratchMemory = mScratchMemory.begin(); + mSolverDesc.scratchMemorySize = Ps::to16(scratchSize); + + mArticulationData.resizeLinkData(linkCount); + + } + return true; + } + return false; + } + + + void FeatherstoneArticulation::getDataSizes(PxU32 linkCount, PxU32 &solverDataSize, PxU32& totalSize, PxU32& scratchSize) + { + PX_UNUSED(linkCount); + solverDataSize = 0; + totalSize = 0; + scratchSize = 0; + } + + void FeatherstoneArticulation::onUpdateSolverDesc() + { + Dy::ArticulationV::onUpdateSolverDesc(); + mArticulationData.mLinks = mSolverDesc.links; + mArticulationData.mLinkCount = mSolverDesc.linkCount; + mArticulationData.mCore = mSolverDesc.core; + mArticulationData.mExternalAcceleration = mSolverDesc.acceleration; + mArticulationData.mSolverDataSize = mSolverDesc.solverDataSize; + mArticulationData.mArticulation = this; + + computeDofs(); + } + + PxU32 FeatherstoneArticulation::getDofs() + { + PxU32 dofs = mArticulationData.getDofs(); + + if (dofs == 0xffffffff) + { + computeDofs(); + } + + return mArticulationData.getDofs(); + } + + PxU32 FeatherstoneArticulation::getDof(const PxU32 linkID) + { + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + return jointDatum.dof; + } + + void FeatherstoneArticulation::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) + { + applyCacheToDest(mArticulationData, cache, mArticulationData.getJointVelocities(), mArticulationData.getJointAccelerations(), + mArticulationData.getJointPositions(), mArticulationData.getJointForces(), flag); + } + + void FeatherstoneArticulation::copyInternalStateToCache(PxArticulationCache& cache, + const PxArticulationCacheFlags flag) + { + if (flag & PxArticulationCache::eVELOCITY) + { + copyJointData(mArticulationData, cache.jointVelocity, mArticulationData.getJointVelocities()); + } + + if (flag & PxArticulationCache::eACCELERATION) + { + copyJointData(mArticulationData, cache.jointAcceleration, mArticulationData.getJointAccelerations()); + } + + if (flag & PxArticulationCache::ePOSITION) + { + copyJointData(mArticulationData, cache.jointPosition, mArticulationData.getJointPositions()); + } + + if (flag & PxArticulationCache::eFORCE) + { + copyJointData(mArticulationData, cache.jointForce, mArticulationData.getJointForces()); + } + + if (flag & PxArticulationCache::eROOT) + { + ArticulationLink& rLink = mArticulationData.getLink(0); + Cm::SpatialVectorF& vel = mArticulationData.getMotionVelocity(0); + Cm::SpatialVectorF& acel = mArticulationData.getMotionAcceleration(0); + PxsBodyCore& rBodyCore = *rLink.bodyCore; + const PxTransform& body2World = rBodyCore.body2World; + cache.rootLinkData.transform = body2World; + cache.rootLinkData.linVel = vel.bottom; + cache.rootLinkData.angVel = vel.top; + cache.rootLinkData.linAcel = body2World.rotate(acel.bottom); + cache.rootLinkData.angAcel = body2World.rotate(acel.top); + } + } + + void FeatherstoneArticulation::transformInertia(const SpatialTransform& sTod, SpatialMatrix& spatialInertia) + { + const SpatialTransform dTos = sTod.getTranspose(); + + PxMat33 tl = sTod.R * spatialInertia.topLeft; + PxMat33 tr = sTod.R * spatialInertia.topRight; + PxMat33 bl = sTod.T * spatialInertia.topLeft + sTod.R * spatialInertia.bottomLeft; + PxMat33 br = sTod.T * spatialInertia.topRight + sTod.R * spatialInertia.getBottomRight(); + + spatialInertia.topLeft = tl * dTos.R + tr * dTos.T; + spatialInertia.topRight = tr * dTos.R; + spatialInertia.bottomLeft = bl * dTos.R + br * dTos.T; + + //aligned inertia + spatialInertia.bottomLeft = (spatialInertia.bottomLeft + spatialInertia.bottomLeft.getTranspose()) * 0.5f; + } + + void FeatherstoneArticulation::getImpulseResponse( + PxU32 linkID, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaVV) const + { + PX_ASSERT(impulse.pad0 == 0.f && impulse.pad1 == 0.f); + + //impulse lin is contact normal, and ang is raxn. R is body2World, R(t) is world2Body + //| R(t), 0 | + //| R(t)*r, R(t)| + //r is the vector from center of mass to contact point + //p(impluse) = |n| + // |0| + + //ArticulationLink* links = mArticulationData.getLinks(); + + //ArticulationLink& link = links[linkID]; + //PxTransform& body2World = link.bodyCore->body2World; + const PxTransform& body2World = mArticulationData.getPreTransform(linkID); + + //transform p(impluse) from world space to the local space of linkId + Cm::SpatialVectorF impl = Cm::SpatialVectorF(body2World.rotateInv(impulse.linear), body2World.rotateInv(impulse.angular)); + + getZ(linkID, mArticulationData, Z, impl); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + Cm::SpatialVectorF deltaV = getDeltaV(fixBase, linkID, mArticulationData, Z); + PX_ASSERT(deltaV.pad0 == 0.f && deltaV.pad1 == 0.f); + + //this is in world space + deltaVV.linear = body2World.rotate(deltaV.bottom); + deltaVV.angular = body2World.rotate(deltaV.top); + } + + //This will return world space SpatialVectorV + Cm::SpatialVectorV FeatherstoneArticulation::getLinkVelocity(const PxU32 linkID) const + { + //This is in the world space + const Cm::SpatialVectorF& motionVelocity = mArticulationData.getMotionVelocity(linkID); + + Cm::SpatialVectorV velocity; + velocity.linear = V3LoadU(motionVelocity.bottom); + velocity.angular = V3LoadU(motionVelocity.top); + + return velocity; + } + + Cm::SpatialVectorV FeatherstoneArticulation::getLinkMotionVector(const PxU32 linkID) const + { + const Cm::SpatialVectorF& motionVector = mArticulationData.getDeltaMotionVector(linkID); + + //ArticulationLink& link = mArticulationData.getLink(linkID); + //PxTransform& body2World = link.bodyCore->body2World; + + /*const PxVec3 mLinear = body2World.rotate(motionVector.bottom); + const PxVec3 mAngular = body2World.rotate(motionVector.top);*/ + + Cm::SpatialVectorV velocity; + velocity.linear = V3LoadU(motionVector.bottom); + velocity.angular = V3LoadU(motionVector.top); + + return velocity; + } + + //this is called by island gen to determine whether the articulation should be awake or sleep + Cm::SpatialVector FeatherstoneArticulation::getMotionVelocity(const PxU32 linkID) const + { + //This is in the body space + const Cm::SpatialVectorF& motionVelocity = mArticulationData.getMotionVelocity(linkID); + + // ArticulationLink& link = mArticulationData.getLink(linkID); + // PxTransform& body2World = link.bodyCore->body2World; + + /*const PxVec3 linear = body2World.rotate(motionVelocity.bottom); + const PxVec3 angular = body2World.rotate(motionVelocity.top);*/ + + return Cm::SpatialVector(motionVelocity.bottom, motionVelocity.top); + } + + PxReal FeatherstoneArticulation::getLinkMaxPenBias(const PxU32 linkID) const + { + return mArticulationData.getLinkData(linkID).maxPenBias; + } + + void PxcFsFlushVelocity(FeatherstoneArticulation& articulation, Cm::SpatialVectorF* deltaV) + { + ArticulationData& data = articulation.mArticulationData; + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities(); + Cm::SpatialVectorF* deferredZ = data.getSpatialZAVectors(); + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + //PxTransform* poses = data.getAccumulatedPoses(); + PxTransform* poses = data.getPreTransform(); + + //This will be zero at the begining of the frame + PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + + if (fixBase) + { + deltaV[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + else + { + //ArticulationLink& link = links[0]; + + deltaV[0] = data.getBaseInvSpatialArticulatedInertia() * (-deferredZ[0]); + //const PxTransform& body2World0 = link.bodyCore->body2World; + const PxTransform& body2World0 = poses[0]; + + motionVelocities[0] += deltaV[0].rotate(body2World0); + + PX_ASSERT(motionVelocities[0].isFinite()); + } + + const PxU32 linkCount = data.getLinkCount(); + + for (PxU32 i = 1; i < linkCount; i++) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& tJointDatum = jointData[i]; + + Cm::SpatialVectorF dV = FeatherstoneArticulation::propagateVelocity(tLinkDatum, tJointDatum, deferredZ[i], + &jointDeltaVelocities[tJointDatum.jointOffset], deltaV[tLink.parent]); + + deltaV[i] = dV; + + const PxTransform& tBody2World = poses[i]; + motionVelocities[i] += dV.rotate(tBody2World); + + PX_ASSERT(motionVelocities[i].isFinite()); + } + + PxMemZero(deferredZ, sizeof(Cm::SpatialVectorF)*linkCount); + } + + + + + void FeatherstoneArticulation::recordDeltaMotion(const ArticulationSolverDesc& desc, + const PxReal dt, Cm::SpatialVectorF* deltaV) + { + FeatherstoneArticulation* articulation = static_cast(desc.articulation); + ArticulationData& data = articulation->mArticulationData; + const PxU32 linkCount = data.getLinkCount(); + + if(data.mJointDirty) + PxcFsFlushVelocity(*articulation, deltaV); + + Cm::SpatialVectorF* deltaMotion = data.getDeltaMotionVector(); + Cm::SpatialVectorF* motionVeloties = data.getMotionVelocities(); + PX_UNUSED(motionVeloties); + + PxReal* jointPosition = data.getJointPositions(); + PxReal* jointVelocities = data.getJointVelocities(); + PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + + + data.mAccumulatedDt += dt; + data.setDt(dt); + + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + if (!fixBase) + { + const Cm::SpatialVectorF& motionVelocity = data.getMotionVelocity(0); + PX_ASSERT(motionVelocity.top.isFinite()); + PX_ASSERT(motionVelocity.bottom.isFinite()); + + const PxTransform preTrans = data.mAccumulatedPoses[0]; + + PxVec3 lin = motionVelocity.bottom; + PxVec3 ang = motionVelocity.top; + + PxVec3 newP = preTrans.p + lin * dt; + + PxTransform newPose = PxTransform(newP, Ps::exp(ang*dt) * preTrans.q); + + //PxVec3 lin, ang; + /*calculateNewVelocity(newPose, data.mPreTransform[0], + 1.f, lin, ang); */ + + data.mAccumulatedPoses[0] = newPose; + + PxQuat dq = newPose.q * data.mPreTransform[0].q.getConjugate(); + + if (dq.w < 0.f) + dq = -dq; + + data.mDeltaQ[0] = dq; + + deltaMotion[0] += motionVelocity * dt; + /*deltaMotion[0].top = ang; + deltaMotion[0].bottom = lin;*/ + + + } + + for (PxU32 linkID = 1; linkID < linkCount; linkID++) + { + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + + PxTransform newPose = articulation->propagateTransform(linkID, data.getLinks(), jointDatum, data.getMotionVelocities(), + dt, data.mAccumulatedPoses[data.getLink(linkID).parent], data.mAccumulatedPoses[linkID], + jointVelocities, jointDeltaVelocities, jointPosition); + + //data.mDeltaQ[linkID] = data.mPreTransform[linkID].q.getConjugate() * newPose.q; + PxQuat dq = newPose.q * data.mPreTransform[linkID].q.getConjugate(); + + if(dq.w < 0.f) + dq = -dq; + + data.mDeltaQ[linkID] = dq; + + for (PxU32 i = 0, idx = jointDatum.jointOffset; i < jointDatum.dof; ++i, idx++) + { + jointDeltaVelocities[idx] = 0.f; + } + + + PxVec3 lin, ang; + calculateNewVelocity(newPose, data.mPreTransform[linkID], + 1.f, lin, ang); + + + deltaMotion[linkID].top = ang;// motionVeloties[linkID].top * dt; + deltaMotion[linkID].bottom = lin;// motionVeloties[linkID].top * dt; + + //Record the new current pose + data.mAccumulatedPoses[linkID] = newPose; + +#if 0 + + + ArticulationLink& link = data.getLink(linkID); + + if (link.inboundJoint->jointType != PxArticulationJointType::eSPHERICAL) + { + const PxTransform& parentPose = data.mAccumulatedPoses[link.parent]; + const PxTransform& body2World = newPose; + + + PxVec3 rw = body2World.p - parentPose.p; + + PxVec3 linVel = data.getMotionVelocity(link.parent).bottom + data.getMotionVelocity(link.parent).top.cross(rw); + PxVec3 angVel = data.getMotionVelocity(link.parent).top; + + const PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + + PxVec3 tDeltaV(0.f); + PxVec3 tDeltaA(0.f); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + tDeltaV += jointDatum.motionMatrix[ind].bottom * jVelocity[ind]; + tDeltaA += jointDatum.motionMatrix[ind].top * jVelocity[ind]; + } + + linVel += body2World.rotate(tDeltaV); + angVel += body2World.rotate(tDeltaA); + + PX_UNUSED(linVel); + + motionVeloties[linkID].bottom = linVel; + //motionVeloties[linkID].bottom = deltaChange/dt; + motionVeloties[linkID].top = angVel; + } +#endif + } + } + + void FeatherstoneArticulation::deltaMotionToMotionVelocity(const ArticulationSolverDesc& desc, PxReal invDt) + { + + FeatherstoneArticulation* articulation = static_cast(desc.articulation); + ArticulationData& data = articulation->mArticulationData; + const PxU32 linkCount = data.getLinkCount(); + Cm::SpatialVectorF* deltaMotion = data.getDeltaMotionVector(); + + for (PxU32 linkID = 0; linkID(delta); + + } + } + + + //This is used in the solveExt1D, solveExtContact + Cm::SpatialVectorV FeatherstoneArticulation::pxcFsGetVelocity(PxU32 linkID) + { + + Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors(); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + + if (!fixBase) + { + deltaV = mArticulationData.mBaseInvSpatialArticulatedInertia * (-deferredZ[0]); + } + + for (ArticulationBitField i = links[linkID].pathToRoot - 1; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = linkData[index]; + ArticulationJointCoreData& tJointDatum = jointData[index]; + PX_ASSERT(links[index].parent < index); + deltaV = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV); + } + + //ArticulationLink& link = mArticulationData.getLink(linkID); + //PxTransform& body2World = link.bodyCore->body2World; + //PxTransform& body2World = mArticulationData.getAccumulatedPoses()[linkID]; + PxTransform& body2World = mArticulationData.getPreTransform(linkID); + Cm::SpatialVectorF vel = mArticulationData.getMotionVelocity(linkID) + deltaV.rotate(body2World); + + return Cm::SpatialVector(vel.bottom, vel.top); + } + + void FeatherstoneArticulation::pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1) + { + //if (mArticulationData.getLink(linkID1).parent == linkID) + //{ + + // Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors(); + + // const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + // ArticulationLink* links = mArticulationData.getLinks(); + // ArticulationLinkData* linkData = mArticulationData.getLinkData(); + // ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + // Cm::SpatialVectorF deltaV = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + + // if (!fixBase) + // { + // deltaV = mArticulationData.mBaseInvSpatialArticulatedInertia * (-deferredZ[0]); + // } + + // for (ArticulationBitField i = links[linkID].pathToRoot - 1; i; i &= (i - 1)) + // { + // //index of child of link h on path to link linkID + // const PxU32 index = ArticulationLowestSetBit(i); + // ArticulationLinkData& tLinkDatum = linkData[index]; + // ArticulationJointCoreData& tJointDatum = jointData[index]; + // PX_ASSERT(links[index].parent < index); + // deltaV = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV); + // } + + // ArticulationLink& link = mArticulationData.getLink(linkID); + // PxTransform& body2World = link.bodyCore->body2World; + // Cm::SpatialVectorF vel = mArticulationData.getMotionVelocity(linkID) + deltaV.rotate(body2World); + + // v0 = Cm::SpatialVector(vel.bottom, vel.top); + + // { + // ArticulationLinkData& tLinkDatum = linkData[linkID1]; + // ArticulationJointCoreData& tJointDatum = jointData[linkID1]; + // //Now do the final step for the child link... + // deltaV = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[linkID1], deltaV); + + // ArticulationLink& link1 = mArticulationData.getLink(linkID1); + // PxTransform& body2World1 = link1.bodyCore->body2World; + // Cm::SpatialVectorF vel1 = mArticulationData.getMotionVelocity(linkID1) + deltaV.rotate(body2World1); + // v1 = Cm::SpatialVector(vel1.bottom, vel1.top); + // } + + //} + //else + { + Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors(); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + + if (!fixBase) + { + deltaV = mArticulationData.mBaseInvSpatialArticulatedInertia * (-deferredZ[0]); + } + + PxU64 common = links[linkID].pathToRoot & links[linkID1].pathToRoot; + PxU64 exclusive0 = links[linkID].pathToRoot ^ common; + PxU64 exclusive1 = links[linkID1].pathToRoot ^ common; + + for (ArticulationBitField i = common - 1; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = linkData[index]; + ArticulationJointCoreData& tJointDatum = jointData[index]; + PX_ASSERT(links[index].parent < index); + deltaV = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV); + } + + Cm::SpatialVectorF deltaV1 = deltaV; + + for (ArticulationBitField i = exclusive0; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = linkData[index]; + ArticulationJointCoreData& tJointDatum = jointData[index]; + PX_ASSERT(links[index].parent < index); + deltaV = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV); + } + + for (ArticulationBitField i = exclusive1; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = linkData[index]; + ArticulationJointCoreData& tJointDatum = jointData[index]; + PX_ASSERT(links[index].parent < index); + deltaV1 = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV1); + } + + + //ArticulationLink& link = mArticulationData.getLink(linkID); + //PxTransform& body2World = link.bodyCore->body2World; + //PxTransform& body2World = mArticulationData.getAccumulatedPoses()[linkID]; + PxTransform& body2World = mArticulationData.getPreTransform(linkID); + Cm::SpatialVectorF vel = mArticulationData.getMotionVelocity(linkID) + deltaV.rotate(body2World); + + v0 = Cm::SpatialVector(vel.bottom, vel.top); + + //ArticulationLink& link1 = mArticulationData.getLink(linkID1); + //PxTransform& body2World1 = link1.bodyCore->body2World; + //PxTransform& body2World1 = mArticulationData.getAccumulatedPoses()[linkID1]; + PxTransform& body2World1 = mArticulationData.getPreTransform(linkID1); + Cm::SpatialVectorF vel1 = mArticulationData.getMotionVelocity(linkID1) + deltaV1.rotate(body2World1); + + v1 = Cm::SpatialVector(vel1.bottom, vel1.top); + } + } + + /*Cm::SpatialVectorV FeatherstoneArticulation::pxcFsGetVelocity(PxU32 linkID) + { + + Cm::SpatialVectorF& vel = mArticulationData.getMotionVelocity(linkID); + + return Cm::SpatialVector(vel.bottom, vel.top); + }*/ + + Cm::SpatialVectorV FeatherstoneArticulation::pxcFsGetVelocityTGS(PxU32 linkID) + { + return getLinkVelocity(linkID); + } + + //This is used in the solveExt1D, solveExtContact + void FeatherstoneArticulation::pxcFsApplyImpulse(PxU32 linkID, + Ps::aos::Vec3V linear, Ps::aos::Vec3V angular, + Cm::SpatialVectorF* /*Z*/, Cm::SpatialVectorF* /*deltaV*/) + { + const ArticulationSolverDesc* desc = &mSolverDesc; + + ArticulationLink* links = static_cast(desc->links); + + //initialize all zero acceration impulse to be zero + ArticulationData& data = mArticulationData; + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + Cm::SpatialVectorF* deferredZ = data.getSpatialZAVectors(); + + //This will be zero at the begining of the frame + //PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + + //Cm::SpatialVectorF* motionVelocity = data.getMotionVelocities(); + + data.mJointDirty = true; + + //set all zero acceleration forces to be zero + //PxMemZero(Z, sizeof(Cm::SpatialVectorF) * desc->linkCount); + + //impulse is in world space + Cm::SpatialVector impulse; + V3StoreU(angular, impulse.angular); + V3StoreU(linear, impulse.linear); + + //transform p(impluse) from world space to the local space of link + //ArticulationLink& collidedlink = links[linkID]; + //PxTransform& body2World = collidedlink.bodyCore->body2World; + //PxTransform& body2World = data.getAccumulatedPoses()[linkID]; + PxTransform& body2World = data.getPreTransform(linkID); + + Cm::SpatialVectorF Z0(-body2World.rotateInv(impulse.linear), -body2World.rotateInv(impulse.angular)); + deferredZ[linkID] += Z0; + + for (PxU32 i = linkID; i; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& jointDatum = jointData[i]; + Z0 = propagateImpulse(tLinkDatum, jointDatum, Z0); + deferredZ[tLink.parent] += Z0; + } + + } + + void FeatherstoneArticulation::pxcFsApplyImpulses(Cm::SpatialVectorF* Z) + { + ArticulationLink* links = mArticulationData.getLinks(); + + //initialize all zero acceration impulse to be zero + ArticulationData& data = mArticulationData; + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + Cm::SpatialVectorF* deferredZ = data.getSpatialZAVectors(); + + const PxU32 startIndex = PxU32(linkCount - 1); + + data.mJointDirty = true; + + for (PxU32 linkID = startIndex; linkID > 0; --linkID) + { + ArticulationLink& tLink = links[linkID]; + ArticulationLinkData& tLinkDatum = linkData[linkID]; + ArticulationJointCoreData& jointDatum = jointData[linkID]; + + Z[tLink.parent] += propagateImpulse(tLinkDatum, jointDatum, Z[linkID]); + deferredZ[linkID] += Z[linkID]; + } + + deferredZ[0] += Z[0]; + + } + + void FeatherstoneArticulation::pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, + const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, + const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* /*deltaV*/) + { + if (0) + { + pxcFsApplyImpulse(linkID, linear, angular, Z, NULL); + pxcFsApplyImpulse(linkID2, linear2, angular2, Z, NULL); + } + else + { + const ArticulationSolverDesc* desc = &mSolverDesc; + ArticulationData& data = mArticulationData; + data.mJointDirty = true; + Cm::SpatialVectorF* deferredZ = data.getSpatialZAVectors(); + ArticulationLink* links = static_cast(desc->links); + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + //impulse is in world space + Cm::SpatialVector impulse0; + V3StoreU(angular, impulse0.angular); + V3StoreU(linear, impulse0.linear); + + Cm::SpatialVector impulse1; + V3StoreU(angular2, impulse1.angular); + V3StoreU(linear2, impulse1.linear); + + //transform p(impluse) from world space to the local space of link + //ArticulationLink& collidedlink = links[linkID]; + //ArticulationLink& collidedlink2 = links[linkID2]; + /*PxTransform& body2World = collidedlink.bodyCore->body2World; + PxTransform& body2World2 = collidedlink2.bodyCore->body2World;*/ + + /*PxTransform& body2World = data.getAccumulatedPoses()[linkID]; + PxTransform& body2World2 = data.getAccumulatedPoses()[linkID2];*/ + + PxTransform& body2World = data.getPreTransform(linkID); + PxTransform& body2World2 = data.getPreTransform(linkID2); + + + + PxU64 commonId = links[linkID].pathToRoot & links[linkID2].pathToRoot; + PxU32 commonLink = Dy::ArticulationHighestSetBit(commonId); //Now, work from one to that common, then the other to that common, then go from there upwards... + + Cm::SpatialVectorF Z1 = Cm::SpatialVectorF(-body2World.rotateInv(impulse0.linear), -body2World.rotateInv(impulse0.angular)); + Cm::SpatialVectorF Z2 = Cm::SpatialVectorF(-body2World2.rotateInv(impulse1.linear), -body2World2.rotateInv(impulse1.angular)); + + Z[linkID2] = Z2; + deferredZ[linkID2] += Z2; + + for (PxU32 i = linkID2; i != commonLink; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& jointDatum = jointData[i]; + Z2 = propagateImpulse(tLinkDatum, jointDatum, Z2); + Z[tLink.parent] = Z2; + deferredZ[tLink.parent] += Z2; + } + + Z[linkID] = Z1; + deferredZ[linkID] += Z1; + + for (PxU32 i = linkID; i != commonLink; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& jointDatum = jointData[i]; + Z1 = propagateImpulse(tLinkDatum, jointDatum, Z1); + Z[tLink.parent] = Z1; + deferredZ[tLink.parent] += Z1; + } + + Z[commonLink] = Z1 + Z2; + + for (PxU32 i = commonLink; i; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& jointDatum = jointData[i]; + Z[tLink.parent] = propagateImpulse(tLinkDatum, jointDatum, Z[i]); + deferredZ[tLink.parent] += Z[tLink.parent]; + } + } + } + + //Z is the link space(drag force) + void FeatherstoneArticulation::applyImpulses(Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) + { + ArticulationLink* links = mArticulationData.getLinks(); + + //initialize all zero acceration impulse to be zero + ArticulationData& data = mArticulationData; + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + const PxU32 startIndex = PxU32(linkCount - 1); + + for (PxU32 linkID = startIndex; linkID > 0; --linkID) + { + ArticulationLink& tLink = links[linkID]; + ArticulationLinkData& tLinkDatum = linkData[linkID]; + ArticulationJointCoreData& jointDatum = jointData[linkID]; + + Z[tLink.parent] += propagateImpulse(tLinkDatum, jointDatum, Z[linkID]); + } + + getDeltaV(Z, deltaV); + } + + void FeatherstoneArticulation::getDeltaV(Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) + { + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + Cm::SpatialVectorF* motionVelocities = mArticulationData.getMotionVelocities(); + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + //This will be zero at the begining of the frame + PxReal* jointDeltaVelocities = mArticulationData.getJointDeltaVelocities(); + + if (fixBase) + { + deltaV[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + else + { + ArticulationLink& link = links[0]; + + deltaV[0] = mArticulationData.mBaseInvSpatialArticulatedInertia * (-Z[0]); + const PxTransform& body2World0 = link.bodyCore->body2World; + motionVelocities[0] += deltaV[0].rotate(body2World0); + + PX_ASSERT(motionVelocities[0].isFinite()); + } + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + for (PxU32 i = 1; i < linkCount; i++) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& tJointDatum = jointData[i]; + Cm::SpatialVectorF dV = propagateVelocity(tLinkDatum, tJointDatum, Z[i], + &jointDeltaVelocities[tJointDatum.jointOffset], deltaV[tLink.parent]); + + deltaV[i] = dV; + const PxTransform& tBody2World = tLink.bodyCore->body2World; + + motionVelocities[i] += dV.rotate(tBody2World); + + PX_ASSERT(motionVelocities[i].isFinite()); + } + } + + PxQuat computeSphericalJointPositions(ArticulationJointCore* joint, + const PxQuat newRot, const PxQuat pBody2WorldRot, + PxReal* jPositions); + + PxTransform FeatherstoneArticulation::propagateTransform(const PxU32 linkID, ArticulationLink* links, + ArticulationJointCoreData& jointDatum, Cm::SpatialVectorF* motionVelocities, const PxReal dt, const PxTransform& pBody2World, + const PxTransform& currentTransform, PxReal* jointVelocities, PxReal* jointDeltaVelocities, PxReal* jointPositions) + { + ArticulationLink& link = links[linkID]; + + ArticulationJointCore* joint = link.inboundJoint; + + PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + PxReal* jDeltaVelocity = &jointDeltaVelocities[jointDatum.jointOffset]; + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + + PxQuat newParentToChild; + PxQuat newWorldQ; + PxVec3 r; + + const PxVec3 childOffset = -joint->childPose.p; + const PxVec3 parentOffset = joint->parentPose.p; + + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + PxReal tJointPosition = jPosition[0] + (jVelocity[0] + jDeltaVelocity[0]) * dt; + + if (link.inboundJoint->prismaticLimited) + { + if (tJointPosition < (link.inboundJoint->limits[link.inboundJoint->dofIds[0]].low)) + tJointPosition = link.inboundJoint->limits[link.inboundJoint->dofIds[0]].low; + if (tJointPosition >(link.inboundJoint->limits[link.inboundJoint->dofIds[0]].high)) + tJointPosition = link.inboundJoint->limits[link.inboundJoint->dofIds[0]].high; + } + + jPosition[0] = tJointPosition; + jVelocity[0] += jDeltaVelocity[0]; + jDeltaVelocity[0] = 0.f; + + + + + newParentToChild = joint->relativeQuat; + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + r = e + d + jointDatum.motionMatrix[0].bottom * tJointPosition; + break; + } + case PxArticulationJointType::eREVOLUTE: + { + PxReal tJointPosition = jPosition[0] + (jVelocity[0] + jDeltaVelocity[0]) * dt; + + if (link.inboundJoint->twistLimited) + { + if (tJointPosition < (link.inboundJoint->limits[link.inboundJoint->dofIds[0]].low)) + tJointPosition = link.inboundJoint->limits[link.inboundJoint->dofIds[0]].low; + if (tJointPosition >(link.inboundJoint->limits[link.inboundJoint->dofIds[0]].high)) + tJointPosition = link.inboundJoint->limits[link.inboundJoint->dofIds[0]].high; + } + else + { + if (tJointPosition > PxTwoPi) + tJointPosition -= 2.f*PxTwoPi; + else if (tJointPosition < -PxTwoPi) + tJointPosition += 2.f*PxTwoPi; + + tJointPosition = PxClamp(tJointPosition, -2.f*PxTwoPi, 2.f*PxTwoPi); + } + + jPosition[0] = tJointPosition; + jVelocity[0] += jDeltaVelocity[0]; + jDeltaVelocity[0] = 0.f; + + const PxVec3& u = jointDatum.motionMatrix[0].top; + + PxQuat jointRotation = PxQuat(-tJointPosition, u); + if (jointRotation.w < 0) //shortest angle. + jointRotation = -jointRotation; + + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + if (jointDatum.dof < 3) + { + PxQuat jointRotation(PxIdentity); + for (PxU32 i = 0; i < jointDatum.dof; ++i) + { + const PxReal delta = (jVelocity[i] + jDeltaVelocity[i]) * dt; + + jVelocity[i] += jDeltaVelocity[i]; + + PxReal jPos = jPosition[i] + delta; + //jPosition[i] += delta; + if (jPos > PxTwoPi) + jPos -= 2.f*PxTwoPi; + else if (jPos < -PxTwoPi) + jPos += 2.f*PxTwoPi; + + jPos = PxClamp(jPos, -2.f*PxTwoPi, 2.f*PxTwoPi); + + jPosition[i] = jPos; + + jDeltaVelocity[i] = 0.f; + + const PxVec3& u = jointDatum.motionMatrix[i].top; + + PxQuat jRotation = PxQuat(-jPosition[i], u); + if (jRotation.w < 0) //shortest angle. + jRotation = -jRotation; + + jointRotation = jointRotation * jRotation; + + + } + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + + } + else + { + + + PxVec3 worldAngVel = motionVelocities[linkID].top; + + newWorldQ = Ps::exp(worldAngVel*dt) * currentTransform.q; + + newParentToChild = computeSphericalJointPositions(joint, newWorldQ, + pBody2World.q, jPosition); + + PxQuat newQ = (pBody2World.q * newParentToChild.getConjugate()).getNormalized(); + + const PxQuat cB2w = newQ * joint->childPose.q; + + const PxMat33 cB2w_m(cB2w); + + PxVec3 axis0 = cB2w_m.column0; + PxVec3 axis1 = cB2w_m.column1; + PxVec3 axis2 = cB2w_m.column2; + + PxVec3 relAngVel = worldAngVel - motionVelocities[link.parent].top; + + PxU32 dofIdx = 0; + if (joint->motion[PxArticulationAxis::eTWIST] != PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis0.dot(relAngVel); + if (joint->motion[PxArticulationAxis::eSWING1] != PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis1.dot(relAngVel); + if (joint->motion[PxArticulationAxis::eSWING2] != PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis2.dot(relAngVel); + if (joint->motion[PxArticulationAxis::eTWIST] == PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis0.dot(relAngVel); + if (joint->motion[PxArticulationAxis::eSWING1] == PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis1.dot(relAngVel); + if (joint->motion[PxArticulationAxis::eSWING2] == PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis2.dot(relAngVel); + } + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + break; + } + case PxArticulationJointType::eFIX: + { + //this is fix joint so joint don't have velocity + newParentToChild = joint->relativeQuat; + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + r = e + d; + break; + } + default: + break; + } + + PxTransform cBody2World; + cBody2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized(); + cBody2World.p = pBody2World.p + cBody2World.q.rotate(r); + + PX_ASSERT(cBody2World.isSane()); + + return cBody2World; + } + + + const PxTransform& FeatherstoneArticulation::getCurrentTransform(PxU32 linkID) const + { + return mArticulationData.mAccumulatedPoses[linkID]; + } + + const PxQuat& FeatherstoneArticulation::getDeltaQ(PxU32 linkID) const + { + return mArticulationData.mDeltaQ[linkID]; + } + + //Z is the spatial acceleration impulse of links[linkID] + Cm::SpatialVectorF FeatherstoneArticulation::propagateVelocity(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, + const Cm::SpatialVectorF& Z, PxReal* jointVelocity, const Cm::SpatialVectorF& hDeltaV) + { + + Cm::SpatialVectorF pDeltaV = linkDatum.childToParent.transposeTransform(hDeltaV); //parent velocity change + + Cm::SpatialVectorF temp = linkDatum.spatialArticulatedInertia * pDeltaV + Z; + + PxReal tJointDelta[6]; + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + tJointDelta[ind] = -sa.innerProduct(temp); + } + + Cm::SpatialVectorF jointSpatialDeltaV(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + PxReal jDelta = 0.f; + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + jDelta += linkDatum.invStIs[ind2][ind] * tJointDelta[ind2]; + } + + jointVelocity[ind] += jDelta; + //PX_ASSERT(PxAbs(jointVelocity[ind]) < 500.f); + + + const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + jointSpatialDeltaV += sa * jDelta; + } + + return pDeltaV + jointSpatialDeltaV; + } + + + //This method calculate the velocity change due to collision/constraint impulse + Cm::SpatialVectorF FeatherstoneArticulation::propagateVelocityTestImpulse(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z, + const Cm::SpatialVectorF& hDeltaV) + { + + const SpatialTransform& c2p = linkDatum.childToParent; + Cm::SpatialVectorF pDeltaV = c2p.transposeTransform(hDeltaV); //parent velocity change + + Cm::SpatialVectorF temp = linkDatum.spatialArticulatedInertia * pDeltaV + Z; + + PxReal tJointDelta[6]; + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + tJointDelta[ind] = -sa.innerProduct(temp); + } + + Cm::SpatialVectorF jointSpatialDeltaV(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + PxReal jDelta = 0.f; + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + jDelta += linkDatum.invStIs[ind2][ind] * tJointDelta[ind2]; + } + + const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + jointSpatialDeltaV += sa * jDelta; + } + + return pDeltaV + jointSpatialDeltaV; + } + + //PX_FORCE_INLINE Cm::SpatialVectorF ComputeDeltaVelocity(const ArticulationLinkData& linkDatum, + // const ArticulationJointCoreData& jointDatum, Cm::SpatialVectorF& hDeltaV) + //{ + // const SpatialTransform& c2p = linkDatum.childToParent; + // Cm::SpatialVectorF pDeltaV = c2p.transposeTransform(hDeltaV); //parent velocity change + // Cm::SpatialVectorF temp = linkDatum.spatialArticulatedInertia * pDeltaV; + // PxReal tJointDelta[6]; + // for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + // { + // const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + // tJointDelta[ind] = -sa.innerProduct(temp); + // } + + // Cm::SpatialVectorF jointSpatialDeltaV(PxVec3(0.f), PxVec3(0.f)); + + // for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + // { + // PxReal jDelta = 0.f; + // for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + // { + // jDelta += linkDatum.invStIs[ind2][ind] * tJointDelta[ind2]; + // } + + // const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + // jointSpatialDeltaV += sa * jDelta; + // } + + // return pDeltaV + jointSpatialDeltaV; + //} + + Cm::SpatialVectorF FeatherstoneArticulation::propagateImpulse(const ArticulationLinkData& linkDatum, + const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z) + { + + Cm::SpatialVectorF temp(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + const PxReal stZ = sa.innerProduct(Z); + temp += linkDatum.IsInvD[ind] * stZ; + } + + //parent space's spatial zero acceleration impulse + return linkDatum.childToParent * (Z - temp); + } + + Cm::SpatialVectorF FeatherstoneArticulation::getDeltaVWithDeltaJV(const bool fixBase, const PxU32 linkID, + const ArticulationData& data, Cm::SpatialVectorF* Z, + PxReal* jointVelocities) + { + + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero(); + if (!fixBase) + { + //velocity change + //SpatialMatrix inverseArticulatedInertia = hLinkDatum.spatialArticulatedInertia.getInverse(); + const SpatialMatrix& inverseArticulatedInertia = data.mBaseInvSpatialArticulatedInertia; + deltaV = inverseArticulatedInertia * (-Z[0]); + } + + for (ArticulationBitField i = data.getLink(linkID).pathToRoot - 1; i; i &= (i - 1)) + { + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = data.getLinkData(index); + ArticulationJointCoreData& tJointDatum = data.getJointData(index); + PxReal* jVelocity = &jointVelocities[tJointDatum.jointOffset]; + deltaV = FeatherstoneArticulation::propagateVelocity(tLinkDatum, tJointDatum, Z[index], jVelocity, deltaV); + } + + return deltaV; + } + + Cm::SpatialVectorF FeatherstoneArticulation::getDeltaV(const bool fixBase, const PxU32 linkID, + const ArticulationData& data, Cm::SpatialVectorF* Z) + { + + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero(); + if (!fixBase) + { + //velocity change + //SpatialMatrix inverseArticulatedInertia = hLinkDatum.spatialArticulatedInertia.getInverse(); + const SpatialMatrix& inverseArticulatedInertia = data.mBaseInvSpatialArticulatedInertia; + deltaV = inverseArticulatedInertia * (-Z[0]); + } + + for (ArticulationBitField i = data.getLink(linkID).pathToRoot - 1; i; i &= (i - 1)) + { + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = data.getLinkData(index); + ArticulationJointCoreData& tJointDatum = data.getJointData(index); + deltaV = FeatherstoneArticulation::propagateVelocityTestImpulse(tLinkDatum, tJointDatum, Z[index], deltaV); + } + + return deltaV; + } + + void FeatherstoneArticulation::getZ(const PxU32 linkID, + const ArticulationData& data, Cm::SpatialVectorF* Z, + const Cm::SpatialVectorF& impulse) + { + ArticulationLink* links = data.getLinks(); + + //impulse need to be in linkID space!!! + Z[linkID] = -impulse; + + for (PxU32 i = linkID; i; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + const ArticulationLinkData& tLinkDatum = data.getLinkData(i); + const ArticulationJointCoreData& tJointDatum = data.getJointData(i); + Z[tLink.parent] = FeatherstoneArticulation::propagateImpulse(tLinkDatum, tJointDatum, Z[i]); + } + } + + //This method use in impulse self response. The input impulse is in the link space + Cm::SpatialVectorF FeatherstoneArticulation::getImpulseResponse( + const PxU32 linkID, + const bool fixBase, + const ArticulationData& data, + Cm::SpatialVectorF* Z, + const Cm::SpatialVectorF& impulse) + { + getZ(linkID, data, Z, impulse); + + return getDeltaV(fixBase, linkID, data, Z); + } + + + //This method use in impulse self response. The input impulse is in the link space + Cm::SpatialVectorF FeatherstoneArticulation::getImpulseResponseWithJ( + const PxU32 linkID, + const bool fixBase, + const ArticulationData& data, + Cm::SpatialVectorF* Z, + const Cm::SpatialVectorF& impulse, + PxReal* jointVelocites) + { + getZ(linkID, data, Z, impulse); + + return getDeltaVWithDeltaJV(fixBase, linkID, data, Z, jointVelocites); + } + + void FeatherstoneArticulation::saveVelocity(const ArticulationSolverDesc& d, Cm::SpatialVectorF* deltaV) + { + + FeatherstoneArticulation* arti = static_cast(d.articulation); + ArticulationData& data = arti->mArticulationData; + + //update all links' motion velocity, joint delta velocity if there are contacts/constraints + if (data.mJointDirty) + PxcFsFlushVelocity(*arti, deltaV); + + const PxU32 linkCount = data.getLinkCount(); + //copy motion velocites + Cm::SpatialVectorF* vels = data.getMotionVelocities(); + Cm::SpatialVectorF* posVels = data.getPosIterMotionVelocities(); + PxMemCopy(posVels, vels, sizeof(Cm::SpatialVectorF) * linkCount); + + //copy joint velocities + const PxU32 dofs = data.getDofs(); + + PxReal* jPosDeltaVels = data.getPosIterJointDeltaVelocities(); + + PxReal* jDeltaVels = data.getJointDeltaVelocities(); + + PxMemCopy(jPosDeltaVels, jDeltaVels, sizeof(PxReal) * dofs); + + /* for (PxU32 i = 0; i < dofs; ++i) + { + PX_ASSERT(PxAbs(jPosDeltaVels[i]) < 30.f); + }*/ + + + } + + void FeatherstoneArticulation::saveVelocityTGS(const ArticulationSolverDesc& d, PxReal invDtF32) + { + PX_UNUSED(d); + PX_UNUSED(invDtF32); + } + + + void FeatherstoneArticulation::getImpulseSelfResponse( + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1) const + { + FeatherstoneArticulation::getImpulseSelfResponse(mArticulationData.getLinks(), !!(mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE), + Z, mArticulationData, linkID0, reinterpret_cast(impulse0), + reinterpret_cast(deltaV0), linkID1, reinterpret_cast(impulse1), + reinterpret_cast(deltaV1)); + } + + + void getImpulseResponseSlow(Dy::ArticulationLink* links, + const ArticulationData& data, + PxU32 linkID0_, + const Cm::SpatialVector& impulse0, + Cm::SpatialVector& deltaV0, + PxU32 linkID1_, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV1) + { + + PxU32 stack[DY_ARTICULATION_MAX_SIZE]; + Cm::SpatialVectorF Z[DY_ARTICULATION_MAX_SIZE]; + //Cm::SpatialVectorF ZZV[DY_ARTICULATION_MAX_SIZE]; + + PxU32 i0, i1, ic; + + PxU32 linkID0 = linkID0_; + PxU32 linkID1 = linkID1_; + + + const PxTransform& transform0 = data.getLink(linkID0_).bodyCore->body2World; + const PxTransform& transform1 = data.getLink(linkID1_).bodyCore->body2World; + + for (i0 = linkID0, i1 = linkID1; i0 != i1;) // find common path + { + if (i0flags & PxArticulationFlag::eFIX_BASE) + { + Z[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + + //SpatialMatrix inverseArticulatedInertia = data.getLinkData(0).spatialArticulatedInertia.getInverse(); + const SpatialMatrix& inverseArticulatedInertia = data.getBaseInvSpatialArticulatedInertia(); + Cm::SpatialVectorF v = inverseArticulatedInertia * (-Z[0]); + + for (PxU32 index = ic; (index--) > i1;) + v = FeatherstoneArticulation::propagateVelocityTestImpulse(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], v); + + + Cm::SpatialVectorF dv1 = v; + for (PxU32 index = i1; (index--) > i0;) + dv1 = FeatherstoneArticulation::propagateVelocityTestImpulse(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], dv1); + + Cm::SpatialVectorF dv0= v; + for (PxU32 index = i0; (index--) > 0;) + dv0 = FeatherstoneArticulation::propagateVelocityTestImpulse(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], dv0); + + deltaV0.linear = transform0.rotate(dv0.bottom); + deltaV0.angular = transform0.rotate(dv0.top); + + deltaV1.linear = transform1.rotate(dv1.bottom); + deltaV1.angular = transform1.rotate(dv1.top); + + } + + + void FeatherstoneArticulation::getImpulseSelfResponse(ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + const ArticulationData& data, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1) + { +#if 0 + ArticulationLink& pLink = links[linkID0]; + ArticulationLink& cLink = links[linkID1]; + + PxTransform& pBody2World = pLink.bodyCore->body2World; + PxTransform& cBody2World = cLink.bodyCore->body2World; + + Cm::SpatialVectorF pImpulse = Cm::SpatialVectorF(pBody2World.rotateInv(reinterpret_cast(impulse0.linear)), pBody2World.rotateInv(reinterpret_cast(impulse0.angular))); + Cm::SpatialVectorF cImpulse = Cm::SpatialVectorF(cBody2World.rotateInv(reinterpret_cast(impulse1.linear)), cBody2World.rotateInv(reinterpret_cast(impulse1.angular))); + + Cm::SpatialVectorF resp0 = FeatherstoneArticulation::getImpulseResponse(linkID0, fixBase, data, Z, pImpulse).rotate(pBody2World); + Cm::SpatialVectorF resp1 = FeatherstoneArticulation::getImpulseResponse(linkID1, fixBase, data, Z, cImpulse).rotate(cBody2World); + + deltaV0 = Cm::SpatialVector(resp0.bottom, resp0.top); + deltaV1 = Cm::SpatialVector(resp1.bottom, resp1.top); +#else + + ArticulationLink& link = links[linkID1]; + ArticulationLinkData& linkDatum = data.getLinkData(linkID1); + ArticulationJointCoreData& jointDatum = data.getJointData(linkID1); + if (link.parent == linkID0) + { + PX_ASSERT(linkID0 == link.parent); + PX_ASSERT(linkID0 < linkID1); + + ArticulationLink& pLink = links[linkID0]; + //impulse is in world space + Cm::SpatialVector imp1; + V3StoreU(impulse1.angular, imp1.angular); + V3StoreU(impulse1.linear, imp1.linear); + + Cm::SpatialVector imp0; + V3StoreU(impulse0.angular, imp0.angular); + V3StoreU(impulse0.linear, imp0.linear); + + PxTransform& pBody2World = pLink.bodyCore->body2World; + + Cm::SpatialVectorF pImpulse = Cm::SpatialVectorF(pBody2World.rotateInv(imp0.linear), pBody2World.rotateInv(imp0.angular)); + + PX_ASSERT(linkID0 == link.parent); + + PxTransform& body2World = link.bodyCore->body2World; + + //initialize child link spatial zero acceleration impulse + Cm::SpatialVectorF Z1 = Cm::SpatialVectorF(-body2World.rotateInv(imp1.linear), -body2World.rotateInv(imp1.angular)); + //this calculate parent link spatial zero acceleration impulse + Cm::SpatialVectorF Z0 = FeatherstoneArticulation::propagateImpulse(linkDatum, jointDatum, Z1); + + //in parent space + const Cm::SpatialVectorF impulseDif = pImpulse - Z0; + + Cm::SpatialVectorF delV0(PxVec3(0.f), PxVec3(0.f)); + Cm::SpatialVectorF delV1(PxVec3(0.f), PxVec3(0.f)); + + //calculate velocity change start from the parent link to the root + delV0 = FeatherstoneArticulation::getImpulseResponse(linkID0, fixBase, data, Z, impulseDif); + + //calculate velocity change for child link + delV1 = FeatherstoneArticulation::propagateVelocityTestImpulse(linkDatum, jointDatum, Z1, delV0); + + //translate delV0 and delV1 into world space again + const PxVec3 lin0 = pBody2World.rotate(delV0.bottom); + const PxVec3 ang0 = pBody2World.rotate(delV0.top); + const PxVec3 lin1 = body2World.rotate(delV1.bottom); + const PxVec3 ang1 = body2World.rotate(delV1.top); + + deltaV0.linear = V3LoadU(lin0); + deltaV0.angular = V3LoadU(ang0); + deltaV1.linear = V3LoadU(lin1); + deltaV1.angular = V3LoadU(ang1); + } + else + { + getImpulseResponseSlow(links, data, linkID0, reinterpret_cast(impulse0), + reinterpret_cast(deltaV0), linkID1, + reinterpret_cast(impulse1), reinterpret_cast(deltaV1)); + } +#endif + } + + void FeatherstoneArticulation::createHardLimit( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt) + { + init(s, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); + + + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkIndex].parent, Cm::SpatialVector(PxVec3(0), axis), s.deltaVA, + linkIndex, Cm::SpatialVector(PxVec3(0), -axis), s.deltaVB); + + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if (unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, joint limit ignored"); + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + s.constant = recipResponse * -err * recipDt; + s.unbiasedConstant = err>0.0f ? s.constant : 0.0f; + s.velMultiplier = -recipResponse; + s.impulseMultiplier = 1.0f; + } + + + void FeatherstoneArticulation::createHardLimits( + SolverConstraint1DExt& s0, + SolverConstraint1DExt& s1, + const PxVec3& axis, + PxReal err0, + PxReal err1, + PxReal recipDt, + const Cm::SpatialVectorV& deltaVA, + const Cm::SpatialVectorV& deltaVB, + PxReal recipResponse) + { + init(s0, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); + init(s1, PxVec3(0), PxVec3(0), -axis, -axis, 0, PX_MAX_F32); + + s0.deltaVA = deltaVA; + s0.deltaVB = deltaVB; + s1.deltaVA = -deltaVA; + s1.deltaVB = -deltaVB; + + s0.constant = recipResponse * -err0 * recipDt; + s0.unbiasedConstant = err0>0.0f ? s0.constant : 0.0f; + s0.velMultiplier = -recipResponse; + s0.impulseMultiplier = 1.0f; + + s1.constant = recipResponse * -err1 * recipDt; + s1.unbiasedConstant = err1>0.0f ? s1.constant : 0.0f; + s1.velMultiplier = -recipResponse; + s1.impulseMultiplier = 1.0f; + } + + + void FeatherstoneArticulation::createTangentialSpring( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt) + { + init(s, PxVec3(0), PxVec3(0), axis, axis, -PX_MAX_F32, PX_MAX_F32); + + Cm::SpatialVector axis6(PxVec3(0), axis); + PxU32 parent = links[linkIndex].parent; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, parent, axis6, s.deltaVA, linkIndex, -axis6, s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if (unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, tangential spring ignored"); + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + // this is a specialization of the spring code in setSolverConstants() for acceleration springs. + // general case is b = dt * (c.mods.spring.damping * c.velocityTarget - c.mods.spring.stiffness * geomError); + // but geomError and velocityTarget are both zero + + const PxReal a = dt * dt * stiffness + dt * damping; + const PxReal x = 1.0f / (1.0f + a); + s.constant = s.unbiasedConstant = 0.0f; + s.velMultiplier = -x * recipResponse * a; + s.impulseMultiplier = 1.0f - x; + } + + void createDriveOrLimit(Dy::SolverConstraint1DExt& c, PxReal bias, const PxReal minImpulse, const PxReal maxImpulse, + bool keepBias, bool isLimit, const Cm::SpatialVectorV& deltaVA, const Cm::SpatialVectorV& deltaVB, const PxReal recipResponse) + { + c.deltaVA = deltaVA; + c.deltaVB = deltaVB; + + c.minImpulse = minImpulse; + c.maxImpulse = maxImpulse; + + c.velMultiplier = -recipResponse; + c.impulseMultiplier = 1.0f; + + { + // see usage of 'for internal use' in preprocessRows() + c.constant = recipResponse * (-bias); + if (!keepBias && (!isLimit || bias < 0.f)) + c.unbiasedConstant = 0.f; + else + c.unbiasedConstant = c.constant; + } + c.appliedForce = 0.f; + c.flags = 0; + c.ang0Writeback = c.ang0; + } + + + void createSpringDrive(Dy::SolverConstraint1DExt& c, PxReal error, PxReal targetVelocity, PxReal maxImpulse, + PxReal stiffness, PxReal damping, PxReal dt, const Cm::SpatialVectorV& deltaVA, const Cm::SpatialVectorV& deltaVB, + const PxReal unitResponse) + { + c.deltaVA = deltaVA; + c.deltaVB = deltaVB; + + PxReal a = dt * dt * stiffness + dt * damping; + PxReal b = dt * (damping * targetVelocity - stiffness * error); + + PxReal x = 1.0f / (1.0f + a*unitResponse); + c.constant = c.unbiasedConstant = x * b; + c.velMultiplier = -x*a; + c.impulseMultiplier = 1.0f - x; + + c.minImpulse = -maxImpulse; + c.maxImpulse = maxImpulse; + + c.appliedForce = 0.f; + c.flags = 0; + c.ang0Writeback = c.ang0; + } + + void createSpringDrive(Dy::SolverConstraint1DExtStep& c, PxReal error, PxReal targetVelocity, PxReal maxImpulse, + PxReal stiffness, PxReal damping, PxReal dt, PxReal totalDt, const Cm::SpatialVectorV& deltaVA, const Cm::SpatialVectorV& deltaVB, + PxReal unitResponse) + { + PX_UNUSED(dt); + + c.deltaVA = deltaVA; + c.deltaVB = deltaVB; + + PxReal a2 = totalDt * (totalDt*stiffness + damping); + PxReal b = totalDt * (damping * targetVelocity - stiffness * error); + + PxReal x = 1.0f / (1.0f + a2*unitResponse); + + c.velTarget = x * b * unitResponse; + c.velMultiplier = -x*a2 * unitResponse; + c.biasScale = 0.f;//c.velMultiplier / dt; + c.impulseMultiplier = 1.0f -x; //KS - add back in -x term? + + c.minImpulse = -maxImpulse; + c.maxImpulse = maxImpulse; + c.recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.f / unitResponse : 0.f; + c.error = 0.f; + c.maxBias = 100.f; + c.angularErrorScale = 1.f; + + c.appliedForce = 0.f; + c.flags = 0; + } + + + void createDriveOrLimit(Dy::SolverConstraint1DExtStep& c, PxReal error, const PxReal minImpulse, const PxReal maxImpulse, + bool keepBias, PxReal maxBias, const PxReal velTarget, const PxReal biasCoefficient, bool isLimit, + const Cm::SpatialVectorV& deltaVA, const Cm::SpatialVectorV& deltaVB, PxReal recipResponse) + { + c.deltaVA = deltaVA; + c.deltaVB = deltaVB; + + c.minImpulse = minImpulse; + c.maxImpulse = maxImpulse; + + c.velMultiplier = -1.f; + c.impulseMultiplier = 1.0f; + + c.error = error; + c.velTarget = velTarget; + c.biasScale = -biasCoefficient; + c.recipResponse = recipResponse; + c.maxBias = maxBias; + c.appliedForce = 0.f; + + c.flags = PxU32(keepBias || (isLimit && error > 0.f) ? DY_SC_FLAG_KEEP_BIAS | DY_SC_FLAG_INEQUALITY : 0); + c.angularErrorScale = 0.f; + } + + PX_INLINE void computeJacobianAxes(PxVec3 row[3], const PxQuat& qa, const PxQuat& qb) + { + // Compute jacobian matrix for (qa* qb) [[* means conjugate in this expr]] + // d/dt (qa* qb) = 1/2 L(qa*) R(qb) (omega_b - omega_a) + // result is L(qa*) R(qb), where L(q) and R(q) are left/right q multiply matrix + + const PxReal wa = qa.w, wb = qb.w; + const PxVec3 va(qa.x, qa.y, qa.z), vb(qb.x, qb.y, qb.z); + + const PxVec3 c = vb*wa + va*wb; + const PxReal d0 = wa*wb; + const PxReal d1 = va.dot(vb); + const PxReal d = d0 - d1; + + row[0] = (va * vb.x + vb * va.x + PxVec3(d, c.z, -c.y)) * 0.5f; + row[1] = (va * vb.y + vb * va.y + PxVec3(-c.z, d, c.x)) * 0.5f; + row[2] = (va * vb.z + vb * va.z + PxVec3(c.y, -c.x, d)) * 0.5f; + + if ((d0 + d1) != 0.0f) // check if relative rotation is 180 degrees which can lead to singular matrix + return; + else + { + row[0].x += PX_EPS_F32; + row[1].y += PX_EPS_F32; + row[2].z += PX_EPS_F32; + } + } + + void FeatherstoneArticulation::setupInternalConstraints( + ArticulationLink* links, + const PxU32 linkCount, + const bool fixBase, + ArticulationData& data, + Cm::SpatialVectorF* Z, + PxReal dt, + PxReal invDt, + PxReal erp, + bool isTGSSolver) + { + PX_PROFILE_ZONE("Articulations:setupSolverConstraintsInternal", 0); + + const PxConstraintInvMassScale ims(1.0f, 1.0f, 1.0f, 1.0f); + + Cm::SpatialVectorF* jointTransmittedForce = data.getTransmittedForces(); + PxReal* jointPositions = data.getJointPositions(); + + data.mInternalConstraints.forceSize_Unsafe(0); + data.mInternalConstraints.resizeUninitialized(data.getDofs()); + + data.mInternalLockedAxes.forceSize_Unsafe(0); + data.mInternalLockedAxes.resizeUninitialized(data.getLocks()); + + ArticulationInternalConstraint* constraints = data.mInternalConstraints.begin(); + ArticulationInternalLockedAxis* locks = data.mInternalLockedAxes.begin(); + + + for (PxU16 linkID = 1; linkID < linkCount; linkID++) + { + const ArticulationLink& link = links[linkID]; + + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + PX_UNUSED(jPosition); + + const ArticulationLink& pLink = links[link.parent]; + + const ArticulationJointCore& j = *link.inboundJoint; + + //const bool jointDrive = (j.driveType != PxArticulationJointDriveType::eNONE); + + bool hasFriction = j.frictionCoefficient > 0.f; + + const PxReal fCoefficient = j.frictionCoefficient * dt; + const PxReal transmissionForce = jointTransmittedForce[linkID].magnitude() * fCoefficient; + PX_UNUSED(transmissionForce); + + const PxU32 limitedRows = 2u * jointDatum.limitedAxes; + const PxU32 lockedRows = jointDatum.lockedAxes; + + + PxU8 driveRows = 0; + + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)) + driveRows++; + } + + const PxU8 frictionRows = hasFriction ? jointDatum.dof : PxU8(0); + + const PxU8 constraintCount = PxU8(limitedRows + driveRows + frictionRows + lockedRows); + if (!constraintCount) + { + //Skip these constraints... + //constraints += jointDatum.dof; + jointDatum.dofInternalConstraintMask = 0; + continue; + } + + const PxTransform cA2w = pLink.bodyCore->body2World.transform(j.parentPose); + const PxTransform cB2w = link.bodyCore->body2World.transform(j.childPose); + + PxU32 dofId = 0; + + PxU8 dofMask = 0; + for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + dofMask |= (1 << dofId); + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].top); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + /*PxReal unitResponse = unsimdRef(deltaVA).angular.dot(axis) - unsimdRef(deltaVB).angular.dot(axis); + PxReal totalMag = (unsimdRef(deltaVA) - unsimdRef(deltaVB)).magnitude(); + + PxReal ratio = unitResponse / totalMag; + if (ratio < 1e-4f) + unitResponse = 0.f;*/ + + Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); + Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); + + PxReal r0 = deltaV0.angular.dot(axis); + PxReal r1 = deltaV1.angular.dot(axis); + + PxReal unitResponse = r0 - r1; + + //Cm::SpatialVector rem0(deltaV0.linear, deltaV0.angular - (axis * r0)); + //Cm::SpatialVector rem1(deltaV1.linear, deltaV1.angular - (axis*r1)); + + //PxReal rem = (rem0 - rem1).magnitude(); + + ///*if ((unitResponse / rem) < DY_ARTICULATION_BAD_RESPONSE) + // unitResponse = 0.f;*/ + + //unitResponse += rem; + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + //constraints->unitResponse = unitResponse; + constraints->recipResponse = recipResponse; + constraints->response = unitResponse; + constraints->row0 = Cm::SpatialVectorF(PxVec3(0), axis); + constraints->row1 = Cm::SpatialVectorF(PxVec3(0), axis); + constraints->deltaVA.top = unsimdRef(deltaVA).angular; + constraints->deltaVA.bottom = unsimdRef(deltaVA).linear; + constraints->deltaVB.top = unsimdRef(deltaVB).angular; + constraints->deltaVB.bottom = unsimdRef(deltaVB).linear; + constraints->erp = erp; + constraints->isLinearConstraint = false; + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + constraints->lowLimit = j.limits[i].low; + constraints->highLimit = j.limits[i].high; + } + else + { + constraints->lowLimit = -PX_MAX_F32; + constraints->highLimit = PX_MAX_F32; + } + + constraints->lowImpulse = 0.f; + constraints->highImpulse = 0.f; + + constraints->frictionForce = 0.f; + constraints->maxFrictionForce = hasFriction ? transmissionForce/dt : 0.f; + constraints->frictionForceCoefficient = isTGSSolver ? 0.f : 1.f; + + + if (hasDrive) + { + PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + + //KS - clamp drive target within limits - no point in having 2 parts fight against each-other + if (j.motion[i] == PxArticulationMotion::eLIMITED) + targetPos = PxClamp(targetPos, j.limits[i].low, j.limits[i].high); + + PxReal a = dt * dt * j.drives[i].stiffness + dt * j.drives[i].damping; + PxReal b = dt * (j.drives[i].damping * targetVelocity /*+ j.drives[i].stiffness * (targetPos - jointPos)*/); + PxReal x = 0.f; + + if (!j.drives[i].isAcceleration) + { + x = unitResponse > 0.f ? 1.0f / (1.0f + a*unitResponse) : 0.f; + constraints->driveTargetVel = x * b; + constraints->driveBiasCoefficient = x*j.drives[i].stiffness*dt; + constraints->driveVelMultiplier = -x*a; + } + else + { + x = 1.0f / (1.0f + a); + constraints->driveTargetVel = x * b*recipResponse; + constraints->driveBiasCoefficient = x*j.drives[i].stiffness*dt*recipResponse; + constraints->driveVelMultiplier = -x*a*recipResponse; + } + + constraints->driveTarget = targetPos; + constraints->driveImpulseMultiplier = isTGSSolver ? 1.f : 1.0f - x; + constraints->maxDriveForce = j.drives[i].maxForce;// *dt; + constraints->driveForce = 0.f; + } + else + { + constraints->driveTargetVel = 0.f; + constraints->driveTarget = 0.f; + constraints->driveBiasCoefficient = 0.f; + constraints->driveVelMultiplier = 0.f; + constraints->driveImpulseMultiplier = 0.f; + constraints->maxDriveForce = 0.f; + constraints->driveForce = 0.f; + } + constraints++; + } + dofId++; + } + } + + for (PxU32 i = PxArticulationAxis::eX; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + + dofMask |= (1 << dofId); + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].bottom); + const PxVec3 ang0 = (cA2w.p - pLink.bodyCore->body2World.p).cross(axis); + const PxVec3 ang1 = (cB2w.p - link.bodyCore->body2World.p).cross(axis); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(axis, ang0), deltaVA, + linkID, Cm::SpatialVector(-axis, -ang1), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); + Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); + + PxReal r0 = deltaV0.linear.dot(axis) + deltaV0.angular.dot(ang0); + PxReal r1 = deltaV1.linear.dot(axis) + + deltaV1.angular.dot(ang1); + + PxReal unitResponse = r0 - r1; + + //Cm::SpatialVector rem0(deltaV0.linear - axis * r0, deltaV0.angular - (ang0 * r0)); + //Cm::SpatialVector rem1(deltaV1.linear - axis * r1, deltaV1.angular - (ang1*r1)); + + //PxReal rem = (rem0 - rem1).magnitude(); + + ////unitResponse += rem; + + ///*if ((unitResponse / rem) < DY_ARTICULATION_BAD_RESPONSE) + // unitResponse = 0.f;*/ + + //unitResponse += rem; + + /*PxReal unitResponse = unsimdRef(deltaVA).linear.dot(axis) + unsimdRef(deltaVA).angular.dot(ang0) - unsimdRef(deltaVB).linear.dot(axis) - + unsimdRef(deltaVB).angular.dot(ang1); + + PxReal totalMag = (unsimdRef(deltaVA) - unsimdRef(deltaVB)).magnitude(); + + PxReal ratio = unitResponse / totalMag; + if (ratio < 1e-4f) + unitResponse = 0.f;*/ + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + constraints->response = unitResponse; + constraints->recipResponse = recipResponse; + constraints->row0 = Cm::SpatialVectorF(axis, ang0); + constraints->row1 = Cm::SpatialVectorF(axis, ang1); + constraints->deltaVA.top = unsimdRef(deltaVA).angular; + constraints->deltaVA.bottom = unsimdRef(deltaVA).linear; + constraints->deltaVB.top = unsimdRef(deltaVB).angular; + constraints->deltaVB.bottom = unsimdRef(deltaVB).linear; + constraints->erp = erp; + constraints->isLinearConstraint = true; + + constraints->lowImpulse = 0.f; + constraints->highImpulse = 0.f; + + constraints->frictionForce = 0.f; + constraints->maxFrictionForce = hasFriction ? transmissionForce/dt : 0.f; + + constraints->frictionForceCoefficient = isTGSSolver ? 0.f : 1.f; + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + constraints->lowLimit = j.limits[i].low; + constraints->highLimit = j.limits[i].high; + } + else + { + constraints->lowLimit = -PX_MAX_F32; + constraints->highLimit = PX_MAX_F32; + } + + if (hasDrive) + { + PxReal targetVelocity = -jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + + //KS - clamp drive target within limits - no point in having 2 parts fight against each-other + if (j.motion[i] == PxArticulationMotion::eLIMITED) + targetPos = PxClamp(targetPos, j.limits[i].low, j.limits[i].high); + + PxReal a = dt * dt * j.drives[i].stiffness + dt * j.drives[i].damping; + PxReal b = dt * (j.drives[i].damping * targetVelocity /*+ j.drives[i].stiffness * (targetPos - jointPos)*/); + PxReal x = 0.f; + + if (!j.drives[i].isAcceleration) + { + x = unitResponse > 0.f ? 1.0f / (1.0f + a*unitResponse) : 0.f; + constraints->driveTargetVel = x * b; + constraints->driveBiasCoefficient = x*j.drives[i].stiffness*dt; + constraints->driveVelMultiplier = -x*a; + } + else + { + x = 1.0f / (1.0f + a); + constraints->driveTargetVel = x * b*recipResponse; + constraints->driveBiasCoefficient = x*j.drives[i].stiffness*dt*recipResponse; + constraints->driveVelMultiplier = -x*a*recipResponse; + } + + constraints->driveTarget = targetPos; + constraints->driveImpulseMultiplier = isTGSSolver ? 1.f : 1.0f - x; + constraints->maxDriveForce = j.drives[i].maxForce;// *dt; + constraints->driveForce = 0.f; + } + else + { + constraints->driveTargetVel = 0.f; + constraints->driveTarget = 0.f; + constraints->driveBiasCoefficient = 0.f; + constraints->driveVelMultiplier = 0.f; + constraints->driveImpulseMultiplier = 0.f; + constraints->maxDriveForce = 0.f; + constraints->driveForce = 0.f; + } + constraints++; + } + dofId++; + } + } + + if (jointDatum.lockedAxes) + { + const PxQuat qB2qA = cA2w.q.getConjugate() * cB2w.q; + PxVec3 row[3]; + computeJacobianAxes(row, cA2w.q, cB2w.q); + + PxReal err[] = { -qB2qA.x, -qB2qA.y, -qB2qA.z }; + + for (PxU32 i = PxArticulationAxis::eTWIST; i <= PxArticulationAxis::eSWING2; ++i) + { + //Get axis, then add lock constraint... + if (j.motion[i] == PxArticulationMotion::eLOCKED) + { + const PxVec3 axis = row[i]; + PxReal error = err[i]; + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + /*PxReal unitResponse = unsimdRef(deltaVA).angular.dot(axis) - unsimdRef(deltaVB).angular.dot(axis); + + PxReal totalMag = (unsimdRef(deltaVA) - unsimdRef(deltaVB)).magnitude(); + + PxReal ratio = unitResponse / totalMag; + if (ratio < 1e-4f) + unitResponse = 0.f;*/ + + Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); + Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); + + PxReal r0 = deltaV0.angular.dot(axis); + PxReal r1 = deltaV1.angular.dot(axis); + + PxReal unitResponse = r0 - r1; + + //Cm::SpatialVector rem0(deltaV0.linear, deltaV0.angular - (axis * r0)); + //Cm::SpatialVector rem1(deltaV1.linear, deltaV1.angular - (axis * r1)); + + //PxReal rem = (rem0 - rem1).magnitude(); + + //// + ///*if ((unitResponse / rem) < DY_ARTICULATION_BAD_RESPONSE) + // unitResponse = 0.f;*/ + + //unitResponse += rem; + + const PxReal recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + locks->axis = axis; + locks->deltaVA.top = unsimdRef(deltaVA).angular; + locks->deltaVA.bottom = unsimdRef(deltaVA).linear; + locks->deltaVB.top = unsimdRef(deltaVB).angular; + locks->deltaVB.bottom = unsimdRef(deltaVB).linear; + locks->recipResponse = recipResponse; + locks->error = error; + locks->biasScale = invDt*erp;//*0.7f; + locks++; + } + } + } + + jointDatum.dofInternalConstraintMask = dofMask; + } + } + + +#if 1 + + PxU32 FeatherstoneArticulation::setupSolverConstraints( + PxConstraintAllocator& /*allocator*/, + PxSolverConstraintDesc* /*constraintDesc*/, + ArticulationLink* links, + const PxU32 linkCount, + const bool fixBase, + ArticulationData& data, + Cm::SpatialVectorF* Z, + PxU32& acCount) + { + acCount = 0; + + setupInternalConstraints(links, linkCount, fixBase, data, Z, data.getDt(), 1.f / data.getDt(), 1.f, false); + + return 0; + } + +#else + + + PxU32 FeatherstoneArticulation::setupSolverConstraints( + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + ArticulationLink* links, + const PxU32 linkCount, + const bool fixBase, + ArticulationData& data, + Cm::SpatialVectorF* Z, + PxU32& acCount) + { + PX_PROFILE_ZONE("Articulations:setupSolverConstraints", 0); + acCount = 0; + + const PxReal dt = data.getDt(); + PxU32 descCount = 0; + const PxReal recipDt = 1.0f / dt; + + const PxConstraintInvMassScale ims(1.0f, 1.0f, 1.0f, 1.0f); + + Cm::SpatialVectorF* jointTransmittedForce = data.getTransmittedForces(); + PxReal* jointPositions = data.getJointPositions(); + + for (PxU16 linkID = 1; linkID < linkCount; linkID++) + { + const ArticulationLink& link = links[linkID]; + + const ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + PX_UNUSED(jPosition); + + const ArticulationLink& pLink = links[link.parent]; + + const ArticulationJointCore& j = *link.inboundJoint; + + //const bool jointDrive = (j.driveType != PxArticulationJointDriveType::eNONE); + + bool hasFriction = j.frictionCoefficient > 0.f; + + const PxReal fCoefficient = j.frictionCoefficient * dt; + const PxReal transmissionForce = jointTransmittedForce[linkID].magnitude() * fCoefficient; + + const PxU32 limitedRows = 2u * jointDatum.limitedAxes; + + PxU8 lockedAxes = jointDatum.lockedAxes; + + + PxU8 driveRows = 0; + + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)) + driveRows++; + } + + PxU8 frictionRows = hasFriction ? jointDatum.dof : PxU8(0); + + const PxU8 constraintCount = PxU8(limitedRows + driveRows + frictionRows + lockedAxes); + if (!constraintCount) + continue; + + const PxTransform cA2w = pLink.bodyCore->body2World.transform(j.parentPose); + const PxTransform cB2w = link.bodyCore->body2World.transform(j.childPose); + + PxSolverConstraintDesc& desc = constraintDesc[descCount++]; + + desc.articulationA = data.getArticulation(); + desc.linkIndexA = Ps::to16(links[linkID].parent); + desc.articulationALength = Ps::to16(data.getSolverDataSize()); + + desc.articulationB = data.getArticulation(); + desc.linkIndexB = linkID; + desc.articulationBLength = Ps::to16(data.getSolverDataSize()); + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeader) + + sizeof(SolverConstraint1DExt) * constraintCount; + + PX_ASSERT(0 == (constraintLength & 0x0f)); + desc.constraintLengthOver16 = Ps::to16(constraintLength / 16); + + desc.constraint = allocator.reserveConstraintData(constraintLength + 16u); + + desc.writeBack = NULL; + + SolverConstraint1DHeader* header = reinterpret_cast(desc.constraint); + SolverConstraint1DExt* constraints = reinterpret_cast(desc.constraint + sizeof(SolverConstraint1DHeader)); + + init(*header, constraintCount, true, ims); + + PxU32 cIndex = 0; + + + PxU32 dofId = 0; + + for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].top); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + PxReal unitResponse = unsimdRef(deltaVA).angular.dot(axis) - unsimdRef(deltaVB).angular.dot(axis); + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + + if (frictionRows) + { + Dy::SolverConstraint1DExt& frictionRow = constraints[cIndex]; + cIndex++; + + frictionRow.lin0 = PxVec3(0.f); + frictionRow.lin1 = PxVec3(0.f); + frictionRow.ang0 = axis; + frictionRow.ang1 = axis; + + createDriveOrLimit(frictionRow, 0.f, -transmissionForce, transmissionForce, false, false, + deltaVA, deltaVB, recipResponse); + } + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + const PxReal lowLimit = j.limits[i].low; + const PxReal highLimit = j.limits[i].high; + + createHardLimits(constraints[cIndex], constraints[cIndex + 1], -axis, jointPos - lowLimit, + highLimit - jointPos, recipDt, -deltaVA, -deltaVB, recipResponse); + cIndex += 2; + } + + if (hasDrive) + { + PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + const PxReal jointPos = -data.mJointPosition[jointDatum.jointOffset + dofId]; + + Dy::SolverConstraint1DExt& c = constraints[cIndex++]; + + c.lin0 = PxVec3(0); + c.lin1 = PxVec3(0); + c.ang0 = axis; + c.ang1 = axis; + + createSpringDrive(c, jointPos + targetPos, -targetVelocity, j.drives[i].maxForce, + j.drives[i].stiffness, j.drives[i].damping, dt, deltaVA, deltaVB, unitResponse); + } + + + + } + dofId++; + } + + } + + for (PxU32 i = PxArticulationAxis::eX; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + + + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].bottom); + const PxVec3 ang0 = (cA2w.p - pLink.bodyCore->body2World.p).cross(axis); + const PxVec3 ang1 = (cB2w.p - pLink.bodyCore->body2World.p).cross(axis); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(axis, ang0), deltaVA, + linkID, Cm::SpatialVector(-axis, -ang1), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + PxReal unitResponse = unsimdRef(deltaVA).linear.dot(axis) + unsimdRef(deltaVA).angular.dot(ang0) - unsimdRef(deltaVB).linear.dot(axis) - + unsimdRef(deltaVB).angular.dot(ang1); + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + + if (frictionRows) + { + Dy::SolverConstraint1DExt& frictionRow = constraints[cIndex]; + cIndex++; + + frictionRow.lin0 = axis; + frictionRow.lin1 = axis; + frictionRow.ang0 = ang0; + frictionRow.ang1 = ang1; + + createDriveOrLimit(frictionRow, 0.f, -transmissionForce, transmissionForce, false, false, + deltaVA, deltaVB, recipResponse); + } + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + //Do linear limits... + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + const PxReal lowLimit = j.limits[i].low; + const PxReal highLimit = j.limits[i].high; + + Dy::SolverConstraint1DExt* prismaticRows = &constraints[cIndex]; + cIndex += 2; + + prismaticRows[0].lin0 = -axis; + prismaticRows[0].lin1 = -axis; + prismaticRows[0].ang0 = -ang0; + prismaticRows[0].ang1 = -ang1; + prismaticRows[1].lin0 = axis; + prismaticRows[1].lin1 = axis; + prismaticRows[1].ang0 = ang0; + prismaticRows[1].ang1 = ang1; + + createDriveOrLimit(prismaticRows[0], (jointPos - lowLimit)*recipDt, 0.f, PX_MAX_F32, false, true, -deltaVA, -deltaVB, recipResponse); + createDriveOrLimit(prismaticRows[1], (highLimit - jointPos)*recipDt, 0.f, PX_MAX_F32, false, true, deltaVA, deltaVB, recipResponse); + + } + + if (hasDrive) + { + PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + const PxReal jointPos = -data.mJointPosition[jointDatum.jointOffset + dofId]; + + Dy::SolverConstraint1DExt& c = constraints[cIndex++]; + + c.lin0 = axis; + c.lin1 = axis; + c.ang0 = ang0; + c.ang1 = ang1; + + createSpringDrive(c, jointPos + targetPos, -targetVelocity, j.drives[i].maxForce, + j.drives[i].stiffness, j.drives[i].damping, dt, -deltaVA, -deltaVB, unitResponse); + } + + + } + + dofId++; + } + } + + if (lockedAxes) + { + const PxQuat qB2qA = cA2w.q.getConjugate() * cB2w.q; + PxVec3 row[3]; + computeJacobianAxes(row, cA2w.q, cB2w.q); + + PxReal err[] = { -qB2qA.x, -qB2qA.y, -qB2qA.z }; + + for (PxU32 i = PxArticulationAxis::eTWIST; i <= PxArticulationAxis::eSWING2; ++i) + { + //Get axis, then add lock constraint... + if (j.motion[i] == PxArticulationMotion::eLOCKED) + { + const PxVec3 axis = row[i]; + PxReal error = err[i]; + Dy::SolverConstraint1DExt& lockRow = constraints[cIndex]; + cIndex++; + + //const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[i].top); + + lockRow.lin0 = PxVec3(0.f); + lockRow.lin1 = PxVec3(0.f); + lockRow.ang0 = axis; + lockRow.ang1 = axis; + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + PxReal unitResponse = unsimdRef(deltaVA).angular.dot(axis) - unsimdRef(deltaVB).angular.dot(axis); + + const PxReal recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + + createDriveOrLimit(lockRow, error*recipDt, -PX_MAX_F32, PX_MAX_F32, false, false, deltaVA, deltaVB, recipResponse); + } + } + } + + + *(desc.constraint + getConstraintLength(desc)) = 0; + + PX_ASSERT(cIndex == constraintCount); + acCount += constraintCount; + } + + return descCount; + } + +#endif + +#if 0 + + static PxU32 getConstraintLength(const PxTGSSolverConstraintDesc& desc) + { + return PxU32(desc.constraintLengthOver16 << 4); + } + + + PxU32 FeatherstoneArticulation::setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* Z) + { + + PX_PROFILE_ZONE("Articulations:setupSolverConstraintsTGS", 0); + FeatherstoneArticulation* articulation = static_cast(articDesc.articulation); + ArticulationData& data = articulation->mArticulationData; + + const PxU32 solverDataSize = articDesc.solverDataSize; + + PX_UNUSED(dt); + acCount = 0; + + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + const PxU32 linkCount = data.getLinkCount(); + ArticulationLink* links = data.getLinks(); + //ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + PxU32 descCount = 0; + const PxReal recipDt = invDt; + + const PxConstraintInvMassScale ims(1.0f, 1.0f, 1.0f, 1.0f); + + + Cm::SpatialVectorF* jointTransmittedForce = data.getTransmittedForces(); + + //for (PxU16 linkID = PxU16(linkCount-1); linkID>=1; linkID--) + for (PxU16 linkID = 1; linkID < linkCount; linkID++) + { + const ArticulationLink& link = links[linkID]; + //const ArticulationLinkData& linkDatum = linkData[linkID]; + const ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + const ArticulationLink& pLink = links[link.parent]; + + const ArticulationJointCore& j = static_cast(*links[linkID].inboundJoint); + + //const bool jointDrive = (j.driveType != PxArticulationJointDriveType::eNONE); + + bool hasFriction = j.frictionCoefficient > 0.f; + + const PxReal fCoefficient = j.frictionCoefficient * dt; + const PxReal transmissionForce = jointTransmittedForce[linkID].magnitude() * fCoefficient; + + PxU32 limitedRows = jointDatum.limitedAxes * 2u; + + PxU8 driveRows = 0; + + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)) + driveRows++; + } + + PxU8 frictionRows = hasFriction ? PxU8(jointData[linkID].dof) : PxU8(0); + + PxU32 lockedAxes = jointDatum.lockedAxes; + + + const PxU8 constraintCount = PxU8(limitedRows + driveRows + frictionRows + lockedAxes); + if (!constraintCount) + continue; + + const PxTransform cA2w = pLink.bodyCore->body2World.transform(j.parentPose); + const PxTransform cB2w = link.bodyCore->body2World.transform(j.childPose); + + //const PxTransform cB2cA = cA2w.transformInv(cB2w); + + PxTGSSolverConstraintDesc& desc = constraintDesc[descCount++]; + + desc.articulationA = articulation; + desc.linkIndexA = Ps::to16(links[linkID].parent); + desc.articulationALength = Ps::to16(solverDataSize); + + desc.articulationB = articulation; + desc.linkIndexB = linkID; + desc.articulationBLength = Ps::to16(solverDataSize); + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeaderStep) + + sizeof(SolverConstraint1DExtStep) * constraintCount; + + PX_ASSERT(0 == (constraintLength & 0x0f)); + desc.constraintLengthOver16 = Ps::to16(constraintLength / 16); + + desc.constraint = stream.reserve(constraintLength + 16u, constraintBlockManager); + + desc.writeBack = NULL; + + SolverConstraint1DHeaderStep* header = reinterpret_cast(desc.constraint); + SolverConstraint1DExtStep* constraints = reinterpret_cast(desc.constraint + sizeof(SolverConstraint1DHeaderStep)); + + init(*header, constraintCount, true, 0.f, ims); + + header->rAWorld = cA2w.p - pLink.bodyCore->body2World.p; + header->rBWorld = cB2w.p - link.bodyCore->body2World.p; + header->rALocal = j.parentPose.p; + header->rBLocal = j.childPose.p; + + PxU32 cIndex = 0; + + PxU32 dofId = 0; + + for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].top); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + PxReal unitResponse = unsimdRef(deltaVA).angular.dot(axis) - unsimdRef(deltaVB).angular.dot(axis); + + const PxReal recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + + if (frictionRows) + { + Dy::SolverConstraint1DExtStep& frictionRow = constraints[cIndex]; + cIndex++; + + frictionRow.lin0 = PxVec3(0.f); + frictionRow.lin1 = PxVec3(0.f); + frictionRow.ang0 = axis; + frictionRow.ang1 = axis; + + + createDriveOrLimit(frictionRow, 0.f, -transmissionForce, transmissionForce, false, 0.f, 0.f, 0.f, false, + deltaVA, deltaVB, recipResponse); + } + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + const PxReal lowLimit = j.limits[i].low; + const PxReal highLimit = j.limits[i].high; + + articulation->createHardLimitTGS(constraints[cIndex++], -axis, jointPos - lowLimit, recipDt, -deltaVA, -deltaVB, recipResponse); + articulation->createHardLimitTGS(constraints[cIndex++], axis, highLimit - jointPos, recipDt, deltaVA, deltaVB, recipResponse); + } + + if (hasDrive) + { + PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + + Dy::SolverConstraint1DExtStep& c = constraints[cIndex++]; + + c.lin0 = PxVec3(0); + c.lin1 = PxVec3(0); + c.ang0 = axis; + c.ang1 = axis; + + createSpringDrive(c, targetPos - jointPos, -targetVelocity, j.drives[i].maxForce, + j.drives[i].stiffness, j.drives[i].damping, dt, totalDt, deltaVA, deltaVB, unitResponse); + } + + + + } + dofId++; + } + } + + for (PxU32 i = PxArticulationAxis::eX; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + + + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].bottom); + const PxVec3 ang0 = (cA2w.p - pLink.bodyCore->body2World.p).cross(axis); + const PxVec3 ang1 = (cB2w.p - pLink.bodyCore->body2World.p).cross(axis); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(axis, ang0), deltaVA, + linkID, Cm::SpatialVector(-axis, -ang1), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + PxReal unitResponse = unsimdRef(deltaVA).linear.dot(axis) + unsimdRef(deltaVA).angular.dot(ang0) - unsimdRef(deltaVB).linear.dot(axis) - + unsimdRef(deltaVB).angular.dot(ang1); + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + + if (frictionRows) + { + Dy::SolverConstraint1DExtStep& frictionRow = constraints[cIndex]; + cIndex++; + + frictionRow.lin0 = axis; + frictionRow.lin1 = axis; + frictionRow.ang0 = ang0; + frictionRow.ang1 = ang1; + + createDriveOrLimit(frictionRow, 0.f, -transmissionForce, transmissionForce, false, 0.f, 0.f, 0.f, false, + deltaVA, deltaVB, recipResponse); + } + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + //Do linear limits... + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + const PxReal lowLimit = j.limits[i].low; + const PxReal highLimit = j.limits[i].high; + + Dy::SolverConstraint1DExtStep* prismaticRows = &constraints[cIndex]; + cIndex += 2; + + prismaticRows[0].lin0 = -axis; + prismaticRows[0].lin1 = -axis; + prismaticRows[0].ang0 = -ang0; + prismaticRows[0].ang1 = -ang1; + prismaticRows[1].lin0 = axis; + prismaticRows[1].lin1 = axis; + prismaticRows[1].ang0 = ang0; + prismaticRows[1].ang1 = ang1; + + createDriveOrLimit(prismaticRows[0], (jointPos - lowLimit), 0.f, PX_MAX_F32, false, 100.f, 0.f, 0.7f*recipDt, true, -deltaVA, -deltaVB, recipResponse); + createDriveOrLimit(prismaticRows[1], (highLimit - jointPos), 0.f, PX_MAX_F32, false, 100.f, 0.f, 0.7f*recipDt, true, deltaVA, deltaVB, recipResponse); + } + + if (hasDrive) + { + PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + + Dy::SolverConstraint1DExtStep& c = constraints[cIndex++]; + + c.lin0 = axis; + c.lin1 = axis; + c.ang0 = ang0; + c.ang1 = ang1; + + createSpringDrive(c, targetPos - jointPos, -targetVelocity, j.drives[i].maxForce, + j.drives[i].stiffness, j.drives[i].damping, dt, totalDt, -deltaVA, -deltaVB, unitResponse); + } + + + } + dofId++; + } + } + + if (lockedAxes) + { + const PxQuat qB2qA = cA2w.q.getConjugate() * cB2w.q; + PxVec3 row[3]; + computeJacobianAxes(row, cA2w.q, cB2w.q); + + PxReal err[] = { -qB2qA.x, -qB2qA.y, -qB2qA.z }; + + for (PxU32 i = PxArticulationAxis::eTWIST; i <= PxArticulationAxis::eSWING2; ++i) + { + //Get axis, then add lock constraint... + if (j.motion[i] == PxArticulationMotion::eLOCKED) + { + Dy::SolverConstraint1DExtStep& lockRow = constraints[cIndex]; + cIndex++; + + const PxVec3 axis = row[i]; + PxReal error = err[i]; + + lockRow.lin0 = PxVec3(0.f); + lockRow.lin1 = PxVec3(0.f); + lockRow.ang0 = row[i]; + lockRow.ang1 = row[i]; + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), row[i]), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -row[i]), deltaVB); + + PxReal unitResponse = unsimdRef(deltaVA).angular.dot(row[i]) - unsimdRef(deltaVB).angular.dot(row[i]); + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + createDriveOrLimit(lockRow, error, -PX_MAX_F32, PX_MAX_F32, false, 30.f, 0.f, 0.7f*invDt, false, deltaVA, deltaVB, recipResponse); + lockRow.angularErrorScale = 1.f; + } + } + } + + + + *(desc.constraint + getConstraintLength(desc)) = 0; + + PX_ASSERT(cIndex == constraintCount); + acCount += constraintCount; + } + + return descCount; + } + +#else + + PxU32 FeatherstoneArticulation::setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc, + PxcConstraintBlockStream& /*stream*/, + PxTGSSolverConstraintDesc* /*constraintDesc*/, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& /*constraintBlockManager*/, + Cm::SpatialVectorF* Z) + { + PX_UNUSED(dt); + PX_UNUSED(totalDt); + acCount = 0; + + FeatherstoneArticulation* thisArtic = static_cast(articDesc.articulation); + + ArticulationLink* links = thisArtic->mArticulationData.getLinks(); + const PxU32 linkCount = thisArtic->mArticulationData.getLinkCount(); + const bool fixBase = thisArtic->mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + thisArtic->setupInternalConstraints(links, linkCount, fixBase, thisArtic->mArticulationData, Z, totalDt, invDt, 0.7f, true); + + return 0; + } + +#endif + + + void FeatherstoneArticulation::createHardLimitTGS( + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt, + const Cm::SpatialVectorV& deltaVA, + const Cm::SpatialVectorV& deltaVB, + PxReal recipResponse) + { + PxReal error = err; + init(s, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); + + s.deltaVA = deltaVA; + s.deltaVB = deltaVB; + + + s.error = error; + s.biasScale = -recipDt*0.7f; + s.maxBias = 30.f; + s.velMultiplier = -1.f; + s.recipResponse = recipResponse; + s.impulseMultiplier = 1.0f; + s.velTarget = 0.f; + s.angularErrorScale = 1.f; + s.flags |= DY_SC_FLAG_INEQUALITY; + if (error > 0.f) + s.flags |= DY_SC_FLAG_KEEP_BIAS; + } + + void FeatherstoneArticulation::createTangentialSpringTGS( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt) + { + init(s, PxVec3(0), PxVec3(0), axis, axis, -PX_MAX_F32, PX_MAX_F32); + + Cm::SpatialVector axis6(PxVec3(0), axis); + PxU32 parent = links[linkIndex].parent; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, parent, axis6, s.deltaVA, linkIndex, -axis6, s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if (unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, tangential spring ignored"); + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + // this is a specialization of the spring code in setSolverConstants() for acceleration springs. + // general case is b = dt * (c.mods.spring.damping * c.velocityTarget - c.mods.spring.stiffness * geomError); + // but geomError and velocityTarget are both zero + + const PxReal a = dt * dt * stiffness + dt * damping; + const PxReal x = 1.0f / (1.0f + a); + s.error = 0.f; + s.biasScale = 0.f; + s.maxBias = 0.f; + s.velMultiplier = -x * a; + s.impulseMultiplier = 1.0f - x; + s.velTarget = 0.f; + s.recipResponse = recipResponse; + s.angularErrorScale = 1.f; + } + + void FeatherstoneArticulation::teleportLinks(ArticulationData& data) + { + jcalc(data); + + ArticulationLink* links = mArticulationData.getLinks(); + + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + PxReal* jointPositions = data.getJointPositions(); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + + ArticulationJointCoreData& jointDatum = jointData[linkID]; + + ArticulationLink& pLink = links[link.parent]; + const PxTransform pBody2World = pLink.bodyCore->body2World; + + ArticulationJointCore* joint = link.inboundJoint; + + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + + PxQuat newParentToChild; + PxQuat newWorldQ; + PxVec3 r; + + + const PxVec3 childOffset = -joint->childPose.p; + const PxVec3 parentOffset = joint->parentPose.p; + + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + newParentToChild = joint->relativeQuat; + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + const PxVec3& u = jointDatum.motionMatrix[0].bottom; + + r = e + d + u * jPosition[0]; + break; + } + case PxArticulationJointType::eREVOLUTE: + { + const PxVec3& u = jointDatum.motionMatrix[0].top; + + PxQuat jointRotation = PxQuat(-jPosition[0], u); + if (jointRotation.w < 0) //shortest angle. + jointRotation = -jointRotation; + + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + PxQuat jointRotation(PxIdentity); + for (PxU32 d = jointDatum.dof; d > 0; --d) + { + PxQuat deltaRot(jPosition[d - 1], jointDatum.motionMatrix[d - 1].top); + jointRotation = jointRotation * deltaRot; + } + + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + break; + } + case PxArticulationJointType::eFIX: + { + //this is fix joint so joint don't have velocity + newParentToChild = joint->relativeQuat; + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + r = e + d; + break; + } + default: + break; + } + + PxTransform& body2World = link.bodyCore->body2World; + body2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized(); + body2World.p = pBody2World.p + body2World.q.rotate(r); + + PX_ASSERT(body2World.isSane()); + } + } + + void FeatherstoneArticulation::jcalc(ArticulationData& data) + { + + if (getDirty()) + { + ArticulationLink* links = data.getLinks(); + ArticulationJointCoreData* jointData = data.getJointData(); + const PxU32 linkCount = data.getLinkCount(); + + PxU32 totalDof = 0; + bool hasSphericalJoint = false; + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + + ArticulationLink& link = links[linkID]; + ArticulationJointCore* joint = link.inboundJoint; + ArticulationJointCoreData& jointDatum = jointData[linkID]; + + PX_CHECK_AND_RETURN(joint->jointType != PxArticulationJointType::eUNDEFINED, "FeatherstoneArticulation::jcalc application need to define valid joint type and motion"); + jointDatum.computeJointDof(joint, false); + joint->setJointPose(jointDatum); + jointDatum.setJointVelocityDrive(joint); + jointDatum.setJointPoseDrive(joint); + + if (joint->jointType == PxArticulationJointType::eSPHERICAL) + hasSphericalJoint = true; + + jointDatum.jointOffset = totalDof; + joint->jointOffset = totalDof; + totalDof += jointDatum.dof; + } + + if (totalDof != mArticulationData.getDofs()) + { + mArticulationData.resizeJointData(totalDof); + mArticulationData.setDofs(totalDof); + } + + mHasSphericalJoint = hasSphericalJoint; + + setDirty(false); + } + } + + //compute link's spatial inertia tensor + void FeatherstoneArticulation::computeSpatialInertia(ArticulationData& data) + { + for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + PxsBodyCore& core = *link.bodyCore; + + const PxVec3& ii = core.inverseInertia; + + const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass; + + //construct mass matric + linkDatum.spatialArticulatedInertia.topLeft = PxMat33(PxZero); + //linkDatum.spatialArticulatedInertia.bottomRight = PxMat33(PxZero); + linkDatum.spatialArticulatedInertia.topRight = PxMat33::createDiagonal(PxVec3(m)); + + //construct inertia matrix + PxMat33& I = linkDatum.spatialArticulatedInertia.bottomLeft; + const PxVec3 inertiaTensor = PxVec3(ii.x == 0.f ? 0.f : (1.f / ii.x), ii.y == 0.f ? 0.f : (1.f / ii.y), ii.z == 0.f ? 0.f : (1.f / ii.z)); + I = PxMat33::createDiagonal(inertiaTensor); + + linkDatum.spatialInertia = linkDatum.spatialArticulatedInertia; + } + } + + void FeatherstoneArticulation::computeZ(ArticulationData& data, + const PxVec3& gravity, ScratchData& scratchData) + { + + Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; + Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; + Cm::SpatialVector* externalAccels = scratchData.externalAccels; + + for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + PxsBodyCore& core = *link.bodyCore; + const PxTransform& body2World = core.body2World; + + //PxMat33& I = linkDatum.spatialArticulatedInertia.bottomLeft; + + //construct spatial zero acceleration + Cm::SpatialVectorF& z = spatialZAForces[linkID]; + + Cm::SpatialVectorF v; + v.top = body2World.rotateInv(motionVelocities[linkID].top); + v.bottom = body2World.rotateInv(motionVelocities[linkID].bottom); + + const PxVec3 exLinAccel = -body2World.rotateInv(gravity); + + /*Cm::SpatialVectorF Iv = linkDatum.spatialArticulatedInertia*v; + z.top = -v.bottom.cross(Iv.top) + (exLinAccel * m); + + z.bottom = v.top.cross(Iv.bottom);*/ + const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass; + const PxMat33& I = linkDatum.spatialInertia.bottomLeft; + + const PxVec3 inertiaTensor(I.column0.x, I.column1.y, I.column2.z); + z.top = (exLinAccel * m); + z.bottom = v.top.cross(inertiaTensor.multiply(v.top)); + + PX_ASSERT(z.top.isFinite()); + PX_ASSERT(z.bottom.isFinite()); + } + + if (externalAccels) + { + for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID) + { + Cm::SpatialVector& externalAccel = externalAccels[linkID]; + + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + PxsBodyCore& core = *link.bodyCore; + const PxTransform& body2World = core.body2World; + + const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass; + + PxMat33& I = linkDatum.spatialInertia.bottomLeft; + + const PxVec3 inertiaTensor(I.column0.x, I.column1.y, I.column2.z); + + //construct spatial zero acceleration + Cm::SpatialVectorF& z = spatialZAForces[linkID]; + + const PxVec3 exLinAccel = -body2World.rotateInv(externalAccel.linear); + const PxVec3 exAngAccel = -body2World.rotateInv(externalAccel.angular); + + z.top += (exLinAccel * m); + z.bottom += inertiaTensor.multiply(exAngAccel); + + PX_ASSERT(z.top.isFinite()); + PX_ASSERT(z.bottom.isFinite()); + + //ML: we can't clear the external accelerations in here because it will break + //TGS and when we are using inverse dynamic as well + //externalAccel = Cm::SpatialVector(PxVec3(0), PxVec3(0)); + } + } + } + + //This can be called by forward dynamic + void FeatherstoneArticulation::computeD(ArticulationData& data, ScratchData& scratchData, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; + + Cm::SpatialVectorF* dragForces = Z; + + PxMemZero(dragForces, sizeof(Cm::SpatialVectorF)*data.getLinkCount()); + + const PxReal dt = data.getDt(); + + bool bAppliedImpusle = false; + + for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + + PxsBodyCore& core = *link.bodyCore; + const PxTransform& body2World = core.body2World; + + const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass; + + //PxMat33& I = linkDatum.spatialArticulatedInertia.bottomLeft; + PxVec3 I = PxVec3(1.f / core.inverseInertia.x, 1.f / core.inverseInertia.y, 1.f / core.inverseInertia.z); + + Cm::SpatialVectorF& d = dragForces[linkID]; + + Cm::SpatialVectorF v; + v.top = body2World.rotateInv(motionVelocities[linkID].top); + v.bottom = body2World.rotateInv(motionVelocities[linkID].bottom); + + if (core.linearDamping > 0.f || core.angularDamping > 0.f) + { + d.top += (v.bottom * core.linearDamping*m*dt); + d.bottom += I.multiply(v.top* core.angularDamping*dt); + bAppliedImpusle = true; + } + + const PxReal maxAng = core.maxAngularVelocitySq; + const PxReal maxLin = core.maxLinearVelocitySq; + + const PxReal angMag = v.top.magnitudeSquared(); + const PxReal linMag = v.bottom.magnitudeSquared(); + + if (angMag > maxAng || linMag > maxLin) + { + + if (angMag > maxAng) + { + PxReal scale = 1.f - PxSqrt(maxAng) / PxSqrt(angMag); + PxVec3 tmpaccelerationAng = (I.multiply(v.top)*scale); + PX_UNUSED(tmpaccelerationAng); + d.bottom = tmpaccelerationAng; + + bAppliedImpusle = true; + } + + if (linMag > maxLin) + { + PxReal scale = 1.f - (PxSqrt(maxLin) / PxSqrt(linMag))*0.8f; + PxVec3 tmpaccelerationLin = (v.bottom*m*scale); + PX_UNUSED(tmpaccelerationLin); + d.top = tmpaccelerationLin; + + bAppliedImpusle = true; + + } + } + } + + if (bAppliedImpusle) + { + applyImpulses(dragForces, DeltaV); + + PxReal* deltaV = data.getJointDeltaVelocities(); + PxReal* jointV = data.getJointVelocities(); + + for (PxU32 linkID = 1; linkID < data.getLinkCount(); ++linkID) + { + ArticulationJointCoreData& tJointDatum = data.getJointData()[linkID]; + for (PxU32 i = 0; i < tJointDatum.dof; ++i) + { + jointV[i + tJointDatum.jointOffset] += deltaV[i + tJointDatum.jointOffset]; + deltaV[i + tJointDatum.jointOffset] = 0.f; + } + } + } + } + + //compute coriolis and centrifugal term + void FeatherstoneArticulation::computeC(ArticulationData& data, ScratchData& scratchData) + { + const PxReal* jointVelocities = scratchData.jointVelocities; + Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors; + + const PxU32 linkCount = data.getLinkCount(); + + if (jointVelocities) + { + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + + const PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + PX_UNUSED(jVelocity); + Cm::SpatialVectorF& coriolis = coriolisVectors[linkID]; + + if (jointDatum.dof > 0) + { + //transform parent link's angular velocity into current link's body space + const PxVec3 parentAngular = scratchData.motionVelocities[link.parent].top; + const PxTransform& body2World = link.bodyCore->body2World; + const PxVec3 pAngular = body2World.q.rotateInv(parentAngular); + const PxVec3 temp0 = pAngular.cross(pAngular.cross(linkDatum.r)); + +#if 1 + Cm::SpatialVectorF relVel(PxVec3(0.f), PxVec3(0.f)); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const PxReal jV = jVelocity[ind]; + relVel += jointDatum.motionMatrix[ind] * jV; + } + + + const PxVec3 aVec = relVel.top; + const PxVec3 force = pAngular.cross(aVec); + + //compute linear part + const PxVec3 lVel = relVel.bottom; + + const PxVec3 temp1 = 2.f * pAngular.cross(lVel); + const PxVec3 temp2 = aVec.cross(lVel); + const PxVec3 torque = temp0 + temp1 + temp2; + coriolis = Cm::SpatialVectorF(force, torque); + +#else + coriolis = Cm::SpatialVectorF(PxVec3(0.f), temp0); + + //motionMatrix is in link space + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const PxReal jV = jVelocity[ind]; + + //compute angular part + const PxVec3 aVec = jointDatum.motionMatrix[ind].top * jV; + const PxVec3 angular = pAngular.cross(aVec); + + //compute linear part + const PxVec3 lVel = jointDatum.motionMatrix[ind].bottom * jV; + + const PxVec3 temp1 = 2 * pAngular.cross(lVel); + const PxVec3 temp2 = aVec.cross(lVel); + const PxVec3 linear = temp1 + temp2; + + Cm::SpatialVectorF tCoriolis(angular, linear); + + coriolis += tCoriolis; + PX_ASSERT(coriolis.isFinite()); + } +#endif + } + else + { + //fix joint + coriolis = Cm::SpatialVectorF(PxVec3(0), PxVec3(0)); + } + } + } + else + { + PxMemZero(coriolisVectors, sizeof(Cm::SpatialVectorF)*linkCount); + } + + } + + static PxMat33 constructSkewSymmetricMatrix(const PxVec3 r) + { + PxMat33 temp; + temp.column0 = PxVec3(0.f, r.z, -r.y); + temp.column1 = PxVec3(-r.z, 0.f, r.x); + temp.column2 = PxVec3(r.y, -r.x, 0.f); + return temp; + } + + void FeatherstoneArticulation::computeRelativeTransformC2P(ArticulationData& data) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + const PxU32 linkCount = data.getLinkCount(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + PxsBodyCore& bodyCore = *link.bodyCore; + + const PxTransform& body2World = bodyCore.body2World; + + ArticulationLink& pLink = links[link.parent]; + PxsBodyCore& pBodyCore = *pLink.bodyCore; + const PxTransform& pBody2World = pBodyCore.body2World; + + PxTransform tC2P = pBody2World.transformInv(body2World).getNormalized(); + + linkDatum.childToParent.R = PxMat33(tC2P.q); + linkDatum.childToParent.q = tC2P.q; + linkDatum.r = body2World.rotateInv(body2World.p - pBody2World.p);//body space of link i + linkDatum.rw =body2World.p - pBody2World.p; + + //child to parent rotation matrix + const PxMat33& c2p = linkDatum.childToParent.R; + //r is in link body space + PxMat33 skewMatrixPR = -constructSkewSymmetricMatrix(linkDatum.r); + //rotation matrix cToP's inverse is rotation matrix pToC + linkDatum.childToParent.T = c2p * (-skewMatrixPR); + +#if FEATURESTONE_DEBUG + { + //debug + PxMat33 pToC = c2p.getTranspose(); + //parentToChild -rR + PxMat33 T2 = skewMatrixPR * pToC; + + PX_ASSERT(SpatialMatrix::isTranspose(linkDatum.childToParent.T, T2)); + } +#endif + } + } + + void FeatherstoneArticulation::computeRelativeTransformC2B(ArticulationData& data) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + const PxU32 linkCount = data.getLinkCount(); + + ArticulationLink& bLink = links[0]; + const PxTransform& bBody2World = bLink.bodyCore->body2World; + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + PxsBodyCore& bodyCore = *link.bodyCore; + + const PxTransform& body2World = bodyCore.body2World; + + PxTransform tC2B = bBody2World.transformInv(body2World).getNormalized(); + linkDatum.childToBase.R= PxMat33(tC2B.q); + linkDatum.childToBase.q = tC2B.q; + const PxVec3 r = body2World.rotateInv(body2World.p - bBody2World.p);//body space of link i + + //child to parent rotation matrix + const PxMat33& c2b = linkDatum.childToParent.R; + //r is in link body space + PxMat33 skewMatrixPR = -constructSkewSymmetricMatrix(r); + //rotation matrix cToP's inverse is rotation matrix pToC + linkDatum.childToBase.T = c2b * (-skewMatrixPR); + } + } + + + //compute all links velocities + void FeatherstoneArticulation::computeLinkVelocities(ArticulationData& data, + ScratchData& scratchData) + { + + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + const PxU32 linkCount = data.getLinkCount(); + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + //motion velocities has to be in world space to avoid numerical errors caused by space + Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + + PxReal* jointVelocities = scratchData.jointVelocities; + + ArticulationLink& baseLink = links[0]; + ArticulationLinkData& baseLinkDatum = linkData[0]; + + PxsBodyCore& core0 = *baseLink.bodyCore; + + baseLinkDatum.maxPenBias = core0.maxPenBias; + + if (fixBase) + { + motionVelocities[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + motionAccelerations[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + else + { + motionVelocities[0] = Cm::SpatialVectorF(core0.angularVelocity, core0.linearVelocity); + } + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + PxsBodyCore& bodyCore = *link.bodyCore; + + linkDatum.maxPenBias = bodyCore.maxPenBias; + + //SpatialTransform p2c = linkDatum.childToParent.getTranspose(); + + //motionVelocites[linkID] = p2c * motionVelocites[link.parent]; + //motionVelocites[linkID] = motionVelocites[link.parent]; + /*motionVelocities[linkID].top = motionVelocities[link.parent].top; + motionVelocities[linkID].bottom = motionVelocities[link.parent].bottom + motionVelocities[link.parent].top.cross(linkDatum.rw);*/ + + PxVec3 ang = motionVelocities[link.parent].top; + PxVec3 lin = motionVelocities[link.parent].bottom + motionVelocities[link.parent].top.cross(linkDatum.rw); + const PxTransform& body2World = bodyCore.body2World; + + if (jointVelocities) + { + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + //const PxReal* const jV = &jointVelocity[linkID * 6]; + PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero(); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + //KS - TODO - only hack for rotational constraints!!! + //jVelocity[ind] = PxClamp(jVelocity[ind], -100.f, 100.f); + deltaV += jointDatum.motionMatrix[ind] * jVelocity[ind]; + } + + ang += body2World.rotate(deltaV.top); + lin += body2World.rotate(deltaV.bottom); + } + + motionVelocities[linkID] = Cm::SpatialVectorF(ang, lin); + } + } + + void FeatherstoneArticulation::solveInternalConstraints(const PxReal dt, const PxReal invDt, + Cm::SpatialVectorF* impulses, Cm::SpatialVectorF* DeltaV, bool velocityIteration) + { + PX_UNUSED(dt); + PX_UNUSED(invDt); + PX_UNUSED(velocityIteration); + const PxU32 count = mArticulationData.getLinkCount(); + + if (count <= 1) + return; + + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + ////(1) Flush velocities first...TODO - incorporate into code below properly...done! + //if(mArticulationData.mJointDirty) + // PxcFsFlushVelocity(*this, DeltaV); + + ArticulationLink* links = mArticulationData.getLinks(); + PxReal* jointPositions = mArticulationData.getJointPositions(); + Cm::SpatialVectorF* baseVelocities = mArticulationData.getMotionVelocities(); + //PxTransform* transforms = mArticulationData.mAccumulatedPoses.begin(); + PxTransform* transforms = mArticulationData.mPreTransform.begin(); + Cm::SpatialVectorF* deltaP = mArticulationData.getDeltaMotionVector(); + + PX_UNUSED(baseVelocities); + PX_UNUSED(transforms); + + + //PxMemZero(DeltaV, sizeof(Cm::SpatialVector)*count); + + Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors(); + + bool impulse = false; + { + + impulses[0].top = PxVec3(0.f); + impulses[0].bottom = PxVec3(0.f); + + + + if (!fixBase) + { + //ArticulationLink& link = links[0]; + + Cm::SpatialVectorF temp = mArticulationData.getBaseInvSpatialArticulatedInertia() * (-deferredZ[0]); + + const PxTransform& body2World0 = transforms[0]; + DeltaV[0] = temp.rotate(body2World0); + } + else + { + DeltaV[0].top = PxVec3(0.f); + DeltaV[0].bottom = PxVec3(0.f); + } + + + + PxU32 dofId = 0; + PxU32 lockId = 0; + for (PxU32 linkID = 1; linkID < count; ++linkID) + { + const ArticulationLink& link = links[linkID]; + //const ArticulationLink& plink = links[link.parent]; + ArticulationLinkData& linkDatum = mArticulationData.getLinkData(linkID); + + PX_UNUSED(linkDatum); + + const ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + PX_UNUSED(jPosition); + //PxReal* jVelocity = &jointVelocity[jointDatum.jointOffset]; + //PX_UNUSED(jVelocity); + + Cm::UnAlignedSpatialVector i1(PxVec3(0.f), PxVec3(0.f)); + + + Cm::SpatialVectorF parentV = DeltaV[link.parent] + baseVelocities[link.parent]; + + + Cm::SpatialVectorF localParentAccel = DeltaV[link.parent].rotateInv(transforms[link.parent]); + + Cm::SpatialVectorF parentVelContrib = propagateVelocityTestImpulse(linkDatum, jointDatum, deferredZ[linkID], localParentAccel).rotate(transforms[linkID]); + //Cm::SpatialVectorF parentVelContrib = ComputeDeltaVelocity(linkDatum, jointDatum, localParentAccel).rotate(transforms[linkID]); + + //DeltaV[linkID] will always be zero so doesn't need to be used in here... + Cm::SpatialVectorF childV = baseVelocities[linkID] + parentVelContrib; + + //KS - we need to record this in dv1. Even if the constraint doesn't apply any impulses, a previous + //constraint could have done, and this could lead to something not being accumulated. + Cm::UnAlignedSpatialVector dv1(parentVelContrib.top, parentVelContrib.bottom); + + + if (jointDatum.dofInternalConstraintMask || jointDatum.lockedAxes) + { + Cm::UnAlignedSpatialVector i0(PxVec3(0.f), PxVec3(0.f)); + Cm::UnAlignedSpatialVector dv0(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 dof = 0; dof < jointDatum.dof; ++dof) + { + if (jointDatum.dofInternalConstraintMask & (1 << dof)) + { + ArticulationInternalConstraint& constraint = mArticulationData.mInternalConstraints[dofId++]; + + PxReal jointP = jPosition[dof]; + + if (!constraint.isLinearConstraint) + { + //Clamp jointP within +/- PxPi + if (jointP > PxTwoPi) + jointP -= 2.f*PxTwoPi; + else if (jointP < -PxTwoPi) + jointP += 2.f*PxTwoPi; + } + + PxReal error = (constraint.driveTarget - jointP); + + //if (!constraint.isLinearConstraint) + //{ + // if (PxAbs(error) > PxPi) + // { + // //drive target and current angle are in different hemispheres, so we + // //should see if we can legally reach the target going the "shorter" route, + // //otherwise we stick with the straight line route... + // PxReal targetInPHemisphere = constraint.driveTarget; + // if(error < 0.f) + // targetInPHemisphere += PxTwoPi; + // else + // targetInPHemisphere -= PxTwoPi; + + // if (constraint.lowLimit <= targetInPHemisphere && targetInPHemisphere <= constraint.highLimit) + // { + // error = targetInPHemisphere - jointP; + // } + // } + //} + + PxReal jointV = constraint.row1.innerProduct(childV) - constraint.row0.innerProduct(parentV); + + const PxReal appliedFriction = constraint.frictionForce*constraint.frictionForceCoefficient; + + PxReal frictionForce = PxClamp(-jointV *constraint.recipResponse + appliedFriction, + -constraint.maxFrictionForce*dt, constraint.maxFrictionForce*dt); + + PxReal frictionDeltaF = frictionForce - appliedFriction; + + constraint.frictionForce += frictionDeltaF; + + jointV += frictionDeltaF * constraint.response; + + + + PxReal unclampedForce = constraint.driveImpulseMultiplier * constraint.driveForce + + jointV * constraint.driveVelMultiplier + constraint.driveTargetVel + error * constraint.driveBiasCoefficient; + + PxReal clampedForce = PxClamp(unclampedForce, -constraint.maxDriveForce, constraint.maxDriveForce); + PxReal driveDeltaF = (clampedForce - constraint.driveForce); + + //Where we will be next frame - we use this to compute error bias terms to correct limits and drives... + + jointV += driveDeltaF * constraint.response; + + driveDeltaF += frictionDeltaF; + + + PxReal deltaF = 0.f; + + if (velocityIteration) + { + deltaF = PxClamp(-jointV*constraint.recipResponse, -constraint.lowImpulse, -constraint.highImpulse); + } + else + { + PxReal futureP = jointP + jointV * dt; + if (futureP > constraint.highLimit) + { + PxReal erp = jointP > constraint.highLimit ? constraint.erp : 1.f; + //PxReal deltaV = (constraint.highLimit - jointP)*invDt*erp - jointV; + PxReal deltaV = (constraint.highLimit - futureP)*invDt*erp; + deltaF = PxMin(constraint.highImpulse + deltaV * constraint.recipResponse, 0.f) - constraint.highImpulse; + + constraint.highImpulse += deltaF; + + } + else if (futureP < constraint.lowLimit) + { + PxReal erp = jointP < constraint.lowLimit ? constraint.erp : 1.f; + //PxReal deltaV = (constraint.lowLimit - jointP)*invDt*erp - jointV; + PxReal deltaV = (constraint.lowLimit - futureP)*invDt*erp; + deltaF = PxMax(constraint.lowImpulse + deltaV * constraint.recipResponse, 0.f) - constraint.lowImpulse; + constraint.lowImpulse += deltaF; + } + } + + deltaF += driveDeltaF; + + if (deltaF != 0.f) + { + impulse = true; + constraint.driveForce = clampedForce; + + i0 += constraint.row0 * deltaF; + i1 -= constraint.row1 * deltaF; + + const Cm::UnAlignedSpatialVector deltaVP = -constraint.deltaVA * deltaF; + const Cm::UnAlignedSpatialVector deltaVC = -constraint.deltaVB * deltaF; + + dv0 += deltaVP; + dv1 += deltaVC; + + parentV += Cm::SpatialVectorF(deltaVP.top, deltaVP.bottom); + childV += Cm::SpatialVectorF(deltaVC.top, deltaVC.bottom); + } + + } + } + + PxU32 startIdx = PxU32(jointDatum.dof - jointDatum.lockedAxes); + + for (PxU32 i = startIdx; i < jointDatum.dof; ++i) + { + ArticulationInternalLockedAxis& lockAxis = mArticulationData.mInternalLockedAxes[lockId++]; + + PxReal jointV = lockAxis.axis.dot(childV.top) - lockAxis.axis.dot(parentV.top); + + PxReal deltaJointP = lockAxis.axis.dot(deltaP[linkID].top) - lockAxis.axis.dot(deltaP[link.parent].top); + PX_UNUSED(deltaJointP); + + PxReal deltaV = -jointV; + //We aim to zero velocity but also correct bias... + if(!velocityIteration) + deltaV += PxClamp((lockAxis.error - deltaJointP) * lockAxis.biasScale, -30.f, 30.f); + //deltaV +=PxClamp((lockAxis.error - deltaJointP) * lockAxis.biasScale, -10.f, 10.f); + + //deltaV = PxClamp(deltaV, -5.f, 5.f); + + PxReal deltaF = deltaV * lockAxis.recipResponse; + + if (deltaF != 0.f) + { + impulse = true; + + i0.bottom += lockAxis.axis * deltaF; + i1.bottom -= lockAxis.axis * deltaF; + + const Cm::UnAlignedSpatialVector tDeltaVP = -lockAxis.deltaVA * deltaF; + const Cm::UnAlignedSpatialVector tDeltaVC = -lockAxis.deltaVB * deltaF; + + const Cm::SpatialVectorF deltaVP = Cm::SpatialVectorF(tDeltaVP.top, tDeltaVP.bottom); + const Cm::SpatialVectorF deltaVC = Cm::SpatialVectorF(tDeltaVC.top, tDeltaVC.bottom); + + dv0 += deltaVP; + dv1 += deltaVC; + + parentV += deltaVP; + childV += deltaVC; + } + + } + + DeltaV[link.parent] += Cm::SpatialVectorF(dv0.top, dv0.bottom); + impulses[link.parent] += Cm::SpatialVectorF(i0.top, i0.bottom); + } + + DeltaV[linkID] = Cm::SpatialVectorF(dv1.top, dv1.bottom); + impulses[linkID] = Cm::SpatialVectorF(i1.top, i1.bottom); + } + + if (1) + { + //Now propagate impulses... + if (impulse) + { + for (PxU32 i = 0; i < count; ++i) + { + //impulses[i] = impulses[i].rotateInv(/*transforms[i]*/links[i].bodyCore->body2World); + impulses[i] = impulses[i].rotateInv(transforms[i]); + } + pxcFsApplyImpulses(impulses); + //applyImpulses(impulses, DeltaV); + } + } + } + } + + //This method is for user update the root link transform so we need to + //fix up other link's position. In this case, we should assume all joint + //velocity/pose is to be zero + void FeatherstoneArticulation::teleportRootLink() + { + //make sure motionMatrix has been set + jcalc(mArticulationData); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + ArticulationLink* links = mArticulationData.getLinks(); + PxReal* jointPositions = mArticulationData.getJointPositions(); + Cm::SpatialVectorF* motionVelocities = mArticulationData.getMotionVelocities(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + const PxTransform oldTransform = link.bodyCore->body2World; + + ArticulationLink& pLink = links[link.parent]; + const PxTransform pBody2World = pLink.bodyCore->body2World; + + ArticulationJointCore* joint = link.inboundJoint; + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + + PxQuat newParentToChild; + PxQuat newWorldQ; + PxVec3 r; + + const PxVec3 childOffset = -joint->childPose.p; + const PxVec3 parentOffset = joint->parentPose.p; + + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + newParentToChild = joint->relativeQuat; + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + const PxVec3& u = jointDatum.motionMatrix[0].bottom; + + r = e + d + u * jPosition[0]; + break; + } + case PxArticulationJointType::eREVOLUTE: + { + const PxVec3& u = jointDatum.motionMatrix[0].top; + + PxQuat jointRotation = PxQuat(-jPosition[0], u); + if (jointRotation.w < 0) //shortest angle. + jointRotation = -jointRotation; + + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + /*PxVec3 worldAngVel = oldTransform.rotate(link.motionVelocity.top); + + newWorldQ = Ps::exp(worldAngVel*dt) * oldTransform.q; + + PxQuat newParentToChild2 = (newWorldQ.getConjugate() * joint->relativeQuat * pBody2World.q).getNormalized(); + + const PxVec3 e2 = newParentToChild2.rotate(parentOffset); + const PxVec3 d2 = childOffset; + r = e2 + d2;*/ + + PX_ASSERT(r.isFinite()); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + + //PxVec3 angVel(joint->jointVelocity[0], joint->jointVelocity[1], joint->jointVelocity[2]); + //PxVec3 worldAngVel = pLink.bodyCore->angularVelocity + oldTransform.rotate(angVel); + + PxVec3 worldAngVel = motionVelocities[linkID].top; + + /*const PxReal eps = 0.001f; + const PxVec3 dif = worldAngVel - worldAngVel2; + PX_ASSERT(PxAbs(dif.x) < eps && PxAbs(dif.y) < eps && PxAbs(dif.z) < eps);*/ + + newWorldQ = Ps::exp(worldAngVel) * oldTransform.q; + + newParentToChild = (newWorldQ.getConjugate() * joint->relativeQuat * pBody2World.q).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + break; + } + case PxArticulationJointType::eFIX: + { + //this is fix joint so joint don't have velocity + newParentToChild = joint->relativeQuat; + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + r = e + d; + break; + } + default: + break; + } + + PxTransform& body2World = link.bodyCore->body2World; + body2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized(); + body2World.p = pBody2World.p + body2World.q.rotate(r); + + PX_ASSERT(body2World.isSane()); + } + } + + + PxU8* FeatherstoneArticulation::allocateScratchSpatialData(PxcScratchAllocator* allocator, + const PxU32 linkCount, ScratchData& scratchData, bool fallBackToHeap) + { + + const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount; + const PxU32 totalSize = size * 4 + sizeof(Dy::SpatialMatrix) * linkCount; + + PxU8* tempMemory = reinterpret_cast(allocator->alloc(totalSize, fallBackToHeap)); + + scratchData.motionVelocities = reinterpret_cast(tempMemory); + PxU32 offset = size; + scratchData.motionAccelerations = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.coriolisVectors = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.spatialZAVectors = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.compositeSpatialInertias = reinterpret_cast(tempMemory + offset); + + return tempMemory; + } + + void FeatherstoneArticulation::allocateScratchSpatialData(DyScratchAllocator& allocator, + const PxU32 linkCount, ScratchData& scratchData) + { + const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount; + const PxU32 totalSize = size * 5 + sizeof(Dy::SpatialMatrix) * linkCount; + + PxU8* tempMemory = allocator.alloc(totalSize); + + scratchData.motionVelocities = reinterpret_cast(tempMemory); + PxU32 offset = size; + scratchData.motionAccelerations = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.coriolisVectors = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.spatialZAVectors = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.externalAccels = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.compositeSpatialInertias = reinterpret_cast(tempMemory + offset); + + } + + +}//namespace Dy +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h new file mode 100644 index 000000000..7eecc4d42 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_FEATHERSTONE_ARTICULATION_LINK_H +#define PXD_FEATHERSTONE_ARTICULATION_LINK_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsVecMath.h" +#include "CmUtils.h" +#include "CmSpatialVector.h" +#include "DyVArticulation.h" +#include "DyFeatherstoneArticulationUtils.h" + +namespace physx +{ + namespace Dy + { + + class ArticulationLinkData + { + public: + ArticulationLinkData() + { + maxPenBias = 0.f; + } + + Cm::SpatialVectorF Is[6];//stI is the transpose of Is + Cm::SpatialVectorF IsInvD[6]; + SpatialMatrix spatialInertia; + SpatialMatrix spatialArticulatedInertia; + SpatialTransform childToParent; + SpatialTransform childToBase; + PxVec3 r; //vector from parent com to child com + PxVec3 rw; //vector from parent com to child com + PxReal qstZIc[6];//jointForce - stZIc + PxReal invStIs[6][6]; + PxReal maxPenBias; + + }; + + struct ArticSolverData + { + SpatialTransform childToParent; + PxReal invStIs[6][6]; + }; + + }//namespace Dy +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp new file mode 100644 index 000000000..b6d38acd8 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp @@ -0,0 +1,1677 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsMathUtils.h" +#include "CmConeLimitHelper.h" +#include "DySolverConstraint1D.h" +#include "DyFeatherstoneArticulation.h" +#include "PxsRigidBody.h" +#include "PxcConstraintBlockStream.h" +#include "DyArticulationContactPrep.h" +#include "DyDynamics.h" +#include "DyArticulationReference.h" +#include "DyArticulationPImpl.h" +#include "DyArticulationFnsSimd.h" +#include "DyFeatherstoneArticulationLink.h" +#include "DyFeatherstoneArticulationJointData.h" +#include "PsFoundation.h" +#include "PxsIslandSim.h" +#include "common/PxProfileZone.h" +#include + + +#ifdef _MSC_VER +#pragma warning(disable:4505) +#endif + +namespace physx +{ +namespace Dy +{ + void PxcFsFlushVelocity(FeatherstoneArticulation& articulation, Cm::SpatialVectorF* deltaV); + + //initialize spatial articualted matrix and coriolis spatial force + void FeatherstoneArticulation::initLinks(ArticulationData& data, + const PxVec3& gravity, ScratchData& scratchData, Cm::SpatialVectorF* Z, + Cm::SpatialVectorF* DeltaV) + { + PX_UNUSED(Z); + PX_UNUSED(DeltaV); + //compute individual link's spatial inertia tensor + //[0, M] + //[I, 0] + computeSpatialInertia(data); + + //compute inidividual zero acceleration force + computeZ(data, gravity, scratchData); + + Cm::SpatialVectorF* za = mArticulationData.getTransmittedForces(); + //copy individual zero acceleration force to mTempData zaForce buffer + PxMemCopy(za, mArticulationData.getSpatialZAVectors(), sizeof(Cm::SpatialVectorF) * mArticulationData.getLinkCount()); + + computeArticulatedSpatialInertia(data); + + computeD(data, scratchData, Z, DeltaV); + + //compute corolis and centrifugal force + computeC(data, scratchData); + + computeArticulatedSpatialZ(mArticulationData, scratchData); + } + + +#if (FEATHERSTONE_DEBUG && (PX_DEBUG || PX_CHECKED)) + static bool isSpatialVectorEqual(Cm::SpatialVectorF& t0, Cm::SpatialVectorF& t1) + { + float eps = 0.0001f; + bool e0 = PxAbs(t0.top.x - t1.top.x) < eps && + PxAbs(t0.top.y - t1.top.y) < eps && + PxAbs(t0.top.z - t1.top.z) < eps; + + bool e1 = PxAbs(t0.bottom.x - t1.bottom.x) < eps && + PxAbs(t0.bottom.y - t1.bottom.y) < eps && + PxAbs(t0.bottom.z - t1.bottom.z) < eps; + + return e0 && e1; + } + + static bool isSpatialVectorZero(Cm::SpatialVectorF& t0) + { + float eps = 0.000001f; + + const bool c0 = PxAbs(t0.top.x) < eps && PxAbs(t0.top.y) < eps && PxAbs(t0.top.z) < eps; + const bool c1 = PxAbs(t0.bottom.x) < eps && PxAbs(t0.bottom.y) < eps && PxAbs(t0.bottom.z) < eps; + + return c0 && c1; + } +#endif + + //calculate Is + void FeatherstoneArticulation::computeIs(ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum) + { + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + linkDatum.Is[ind] = linkDatum.spatialArticulatedInertia * sa; + } + } + + + //compute inertia contribution part + SpatialMatrix FeatherstoneArticulation::computePropagateSpatialInertia(const PxU8 jointType, ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum) + { + SpatialMatrix spatialInertia; + + switch (jointType) + { + case PxArticulationJointType::ePRISMATIC: + case PxArticulationJointType::eREVOLUTE: + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[0]; + + Cm::SpatialVectorF& Is = linkDatum.Is[0]; + + const PxReal stIs = sa.innerProduct(linkDatum.Is[0]); + + linkDatum.invStIs[0][0] = (stIs > /*PX_EPS_REAL*/1e-5f) ? (1.f / stIs) : 0.f; + + linkDatum.IsInvD[0] = Is * linkDatum.invStIs[0][0]; + + + //(6x1)Is = [v0, v1]; (1x6)stI = [v1, v0] + //Cm::SpatialVector stI1(Is1.angular, Is1.linear); + Cm::SpatialVectorF stI(Is.bottom, Is.top); + + spatialInertia = SpatialMatrix::constructSpatialMatrix(linkDatum.IsInvD[0], stI); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + + +#if FEATHERSTONE_DEBUG + //This is for debugging + Temp6x6Matrix bigInertia(linkDatum.spatialArticulatedInertia); + Temp6x3Matrix bigS(jointDatum.motionMatrix.getColumns()); + + Temp6x3Matrix bigIs = bigInertia * bigS; + + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + Cm::SpatialVectorF tempIs = bigInertia * jointDatum.motionMatrix[ind]; + + PX_ASSERT(isSpatialVectorEqual(tempIs, linkDatum.Is[ind])); + + PX_ASSERT(bigIs.isColumnEqual(ind, tempIs)); + + } +#endif + + PxMat33 D(PxIdentity); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind2]; + D[ind][ind2] = sa.innerProduct(linkDatum.Is[ind]); + } + } + + PxMat33 invD = SpatialMatrix::invertSym33(D); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + linkDatum.invStIs[ind][ind2] = invD[ind][ind2]; + + } + } + +#if FEATHERSTONE_DEBUG + //debugging + Temp6x3Matrix bigIsInvD = bigIs * invD; +#endif + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + linkDatum.IsInvD[ind] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + Cm::SpatialVectorF& Is = linkDatum.Is[ind2]; + linkDatum.IsInvD[ind] += Is * linkDatum.invStIs[ind][ind2]; + } + +#if FEATHERSTONE_DEBUG + + const bool equal = bigIsInvD.isColumnEqual(ind, linkDatum.IsInvD[ind]); + PX_ASSERT(equal); +#endif + } + + +#if FEATHERSTONE_DEBUG + Temp6x6Matrix transpose6x6 = bigInertia.getTranspose(); +#endif + PxReal stI[6][3]; //[column][row] + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { +#if FEATHERSTONE_DEBUG + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + + Cm::SpatialVectorF sat = Cm::SpatialVectorF(sa.bottom, sa.top); + Cm::SpatialVectorF tstI = transpose6x6 * sat; +#endif + + Cm::SpatialVectorF& Is = linkDatum.Is[ind]; + +#if FEATHERSTONE_DEBUG + Cm::SpatialVectorF temp(Is.bottom, Is.top); + const bool equal = isSpatialVectorEqual(temp, tstI); + PX_ASSERT(equal); +#endif + + //(6x1)Is = [v0, v1]; (1x6)stI = [v1, v0] + stI[0][ind] = Is.bottom.x; + stI[1][ind] = Is.bottom.y; + stI[2][ind] = Is.bottom.z; + stI[3][ind] = Is.top.x; + stI[4][ind] = Is.top.y; + stI[5][ind] = Is.top.z; + } + + Cm::SpatialVectorF columns[6]; + for (PxU32 ind = 0; ind < 6; ++ind) + { + columns[ind] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + columns[ind] += linkDatum.IsInvD[ind2] * stI[ind][ind2]; + } + } + + spatialInertia = SpatialMatrix::constructSpatialMatrix(columns); +#if FEATHERSTONE_DEBUG + Temp6x6Matrix result = bigIsInvD * stI; + PX_ASSERT(result.isEqual(columns)); +#endif + + break; + } + default: + spatialInertia.setZero(); + break; + } + + //(I - Is*Inv(sIs)*sI) + spatialInertia = linkDatum.spatialArticulatedInertia - spatialInertia; + + return spatialInertia; + } + + void FeatherstoneArticulation::computeArticulatedSpatialInertia(ArticulationData& data) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + const PxU32 linkCount = data.getLinkCount(); + const PxU32 startIndex = PxU32(linkCount - 1); + + for (PxU32 linkID = startIndex; linkID > 0; --linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + + ArticulationJointCoreData& jointDatum = jointData[linkID]; + computeIs(linkDatum, jointDatum); + + //(I - Is*Inv(sIs)*sI) + SpatialMatrix spatialInertia = computePropagateSpatialInertia(link.inboundJoint->jointType, + linkDatum, jointDatum); + + //transform spatial inertia into parent space + transformInertia(linkDatum.childToParent, spatialInertia); + //PX_ASSERT(spatialInertia.isTranspose(spatialInertia.topLeft, spatialInertia.bottomRight)); + + //accumulate child's articulated spatial inertia to the parent's articulated spatial inertia + ArticulationLinkData& pLinkDatum = linkData[link.parent]; + pLinkDatum.spatialArticulatedInertia += spatialInertia; + } + + //cache base link inverse spatial inertia + ArticulationLinkData& bLinkDatum = linkData[0]; + data.mBaseInvSpatialArticulatedInertia = bLinkDatum.spatialArticulatedInertia.invertInertia(); + } + + void FeatherstoneArticulation::computeArticulatedSpatialZ(ArticulationData& data, + ScratchData& scratchData) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + const PxU32 linkCount = data.getLinkCount(); + const PxU32 startIndex = PxU32(linkCount - 1); + + Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors; + Cm::SpatialVectorF* articulatedZA = scratchData.spatialZAVectors; + + PxReal* jointForces = scratchData.jointForces; + + for (PxU32 linkID = startIndex; linkID > 0; --linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + + ArticulationJointCoreData& jointDatum = jointData[linkID]; + + //calculate spatial zero acceleration force, this can move out of the loop + Cm::SpatialVectorF Ic = linkDatum.spatialArticulatedInertia * coriolisVectors[linkID]; + Cm::SpatialVectorF ZIc = articulatedZA[linkID] + Ic; + + const PxReal* jF = &jointForces[jointDatum.jointOffset]; + + Cm::SpatialVectorF ZA(PxVec3(0.f), PxVec3(0.f)); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + const PxReal stZ = sa.innerProduct(ZIc); + + //link.qstZIc[ind] = jF[ind] - stZ; + linkDatum.qstZIc[ind] = jF[ind] - stZ; + PX_ASSERT(PxIsFinite(linkDatum.qstZIc[ind])); + + ZA += linkDatum.IsInvD[ind] * linkDatum.qstZIc[ind]; + + } + //accumulate childen's articulated zero acceleration force to parent's articulated zero acceleration + ZA += ZIc; + articulatedZA[link.parent] += linkDatum.childToParent * ZA; + } + } + + void FeatherstoneArticulation::computeJointAcceleration(ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum, + const Cm::SpatialVectorF& pMotionAcceleration, PxReal* jointAcceleration) + { + + PxReal tJAccel[6]; + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + //stI * pAcceleration + const PxReal temp = linkDatum.Is[ind].innerProduct(pMotionAcceleration); + + tJAccel[ind] = (linkDatum.qstZIc[ind] - temp); + } + + //calculate jointAcceleration + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + jointAcceleration[ind] = 0.f; + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + jointAcceleration[ind] += linkDatum.invStIs[ind2][ind] * tJAccel[ind2]; + } + //PX_ASSERT(PxAbs(jointAcceleration[ind]) < 5000); + } + + } + + void FeatherstoneArticulation::computeLinkAcceleration(ArticulationData& data, + ScratchData& scratchData) + { + const PxU32 linkCount = data.getLinkCount(); + const PxReal dt = data.getDt(); + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + //we have initialized motionVelocity and motionAcceleration to be zero in the root link if + //fix based flag is raised + Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors; + Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; + + if (!fixBase) + { + //ArticulationLinkData& baseLinkDatum = data.getLinkData(0); + SpatialMatrix invInertia = data.mBaseInvSpatialArticulatedInertia;//baseLinkDatum.spatialArticulatedInertia.invertInertia(); + +#if FEATHERSTONE_DEBUG + SpatialMatrix result = invInertia * baseLinkDatum.spatialArticulatedInertia; + + bool isIdentity = result.isIdentity(); + + PX_ASSERT(isIdentity); + + PX_UNUSED(isIdentity); +#endif + + + ArticulationLink& baseLink = data.getLink(0); + const PxTransform& body2World = baseLink.bodyCore->body2World; + + motionAccelerations[0] = -(invInertia * spatialZAForces[0]); + Cm::SpatialVectorF deltaV = motionAccelerations[0] * dt; + + //Cm::SpatialVectorF oldMotionVel = motionVelocities[0]; + Cm::SpatialVectorF oldVel = motionVelocities[0]; + + motionVelocities[0].top += body2World.rotate(deltaV.top); + motionVelocities[0].bottom += body2World.rotate(deltaV.bottom); + + } +#if FEATHERSTONE_DEBUG + else + { + PX_ASSERT(isSpatialVectorZero(motionAccelerations[0])); + PX_ASSERT(isSpatialVectorZero(motionVelocities[0])); + } +#endif + + /*PxReal* jointAccelerations = data.getJointAccelerations(); + PxReal* jointVelocities = data.getJointVelocities(); + PxReal* jointPositions = data.getJointPositions();*/ + + PxReal* jointAccelerations = scratchData.jointAccelerations; + PxReal* jointVelocities = scratchData.jointVelocities; + + //printf("===========================\n"); + + //calculate acceleration + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + ArticulationJointCore& joint = *link.inboundJoint; + PX_UNUSED(joint); + + SpatialTransform p2C = linkDatum.childToParent.getTranspose(); + Cm::SpatialVectorF pMotionAcceleration = p2C * motionAccelerations[link.parent]; + + + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + + //calculate jointAcceleration + PxReal* jA = &jointAccelerations[jointDatum.jointOffset]; + computeJointAcceleration(linkDatum, jointDatum, pMotionAcceleration, jA); + //printf("jA %f\n", jA[0]); + + Cm::SpatialVectorF motionAcceleration(PxVec3(0.f), PxVec3(0.f)); + PxReal* jointVelocity = &jointVelocities[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + PxReal jVel = jointVelocity[ind] + jA[ind] * dt; + if (PxAbs(jVel) > joint.maxJointVelocity) + { + jVel = jVel < 0.f ? -joint.maxJointVelocity : joint.maxJointVelocity; + jA[ind] = (jVel - jointVelocity[ind]) / dt; + } + + jointVelocity[ind] = jVel; + motionAcceleration += jointDatum.motionMatrix[ind] * jA[ind]; + } + + //KS - can we just work out velocities by projecting out the joint velocities instead of accumulating all this? + motionAccelerations[linkID] = pMotionAcceleration + coriolisVectors[linkID] + motionAcceleration; + PX_ASSERT(motionAccelerations[linkID].isFinite()); + + const PxTransform body2World = link.bodyCore->body2World; + //motionVelocities[linkID] += motionAccelerations[linkID] * dt; + + const Cm::SpatialVectorF deltaV = motionAccelerations[linkID] * dt; + motionVelocities[linkID].top += body2World.rotate(deltaV.top); + motionVelocities[linkID].bottom += body2World.rotate(deltaV.bottom); + } + + + } + + + void FeatherstoneArticulation::computeJointTransmittedFrictionForce( + ArticulationData& data, ScratchData& scratchData, Cm::SpatialVectorF* /*Z*/, Cm::SpatialVectorF* /*DeltaV*/) + { + //const PxU32 linkCount = data.getLinkCount(); + const PxU32 startIndex = data.getLinkCount() - 1; + + //const PxReal frictionCoefficent =30.5f; + Cm::SpatialVectorF* transmittedForce = scratchData.spatialZAVectors; + + for (PxU32 linkID = startIndex; linkID > 1; --linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + //joint force transmitted from parent to child + transmittedForce[link.parent] += linkDatum.childToParent * transmittedForce[linkID]; + } + + transmittedForce[0] = Cm::SpatialVectorF::Zero(); + + //const PxReal dt = data.getDt(); + //for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + //{ + // //ArticulationLink& link = data.getLink(linkID); + // //ArticulationLinkData& linkDatum = data.getLinkData(linkID); + // transmittedForce[linkID] = transmittedForce[linkID] * (frictionCoefficent) * dt; + // //transmittedForce[link.parent] -= linkDatum.childToParent * transmittedForce[linkID]; + //} + + // + + //applyImpulses(transmittedForce, Z, DeltaV); + + //PxReal* deltaV = data.getJointDeltaVelocities(); + //PxReal* jointV = data.getJointVelocities(); + + //for (PxU32 linkID = 1; linkID < data.getLinkCount(); ++linkID) + //{ + // ArticulationJointCoreData& tJointDatum = data.getJointData()[linkID]; + // for (PxU32 i = 0; i < tJointDatum.dof; ++i) + // { + // jointV[i + tJointDatum.jointOffset] += deltaV[i + tJointDatum.jointOffset]; + // deltaV[i + tJointDatum.jointOffset] = 0.f; + // } + //} + } + + //void FeatherstoneArticulation::computeJointFriction(ArticulationData& data, + // ScratchData& scratchData) + //{ + // PX_UNUSED(scratchData); + // const PxU32 linkCount = data.getLinkCount(); + // PxReal* jointForces = scratchData.jointForces; + // PxReal* jointFrictionForces = data.getJointFrictionForces(); + // PxReal* jointVelocities = data.getJointVelocities(); + + // const PxReal coefficient = 0.5f; + + // for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + // { + // ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + // //compute generalized force + // PxReal* jFs = &jointForces[jointDatum.jointOffset]; + // PxReal* jVs = &jointVelocities[jointDatum.jointOffset]; + // PxReal* jFFs = &jointFrictionForces[jointDatum.jointOffset]; + + // for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + // { + // PxReal sign = jVs[ind] > 0 ? -1.f : 1.f; + // jFFs[ind] = coefficient * PxAbs(jFs[ind]) *sign; + + // //jFFs[ind] = coefficient * jVs[ind]; + // } + // } + //} + + + void FeatherstoneArticulation::applyExternalImpulse(ArticulationLink* links, const PxU32 linkCount, + const bool fixBase, ArticulationData& data, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV, + const PxReal dt, const PxVec3& gravity, Cm::SpatialVector* acceleration) + { + + PxReal* jointVelocities = data.getJointVelocities(); + PxReal* jointAccelerations = data.getJointAccelerations(); + PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + + const PxU32 totalDofs = data.getDofs(); + PxMemZero(jointDeltaVelocities, sizeof(PxReal)*totalDofs); + + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + Cm::SpatialVectorF* motionVelcities = data.getMotionVelocities(); + + //compute external impulse + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + PxsBodyCore& core = *link.bodyCore; + + const PxTransform& body2World = core.body2World; + + Cm::SpatialVector& externalAccel = acceleration[linkID]; + const PxVec3 linkGravity = body2World.rotateInv(gravity); + PxVec3 linearAccel = body2World.rotateInv(externalAccel.linear); + if (!(link.body->mInternalFlags & PxsRigidBody::eDISABLE_GRAVITY)) + linearAccel += linkGravity; + + PxVec3 angularAccel = body2World.rotateInv(externalAccel.angular); + + Cm::SpatialVectorF a(angularAccel, linearAccel); + + Z[linkID] = -linkData[linkID].spatialArticulatedInertia * a * dt; + + externalAccel.linear = PxVec3(0.f); externalAccel.angular = PxVec3(0.f); + } + + + for (PxU32 linkID = PxU32(linkCount - 1); linkID > 0; --linkID) + { + ArticulationLink& tLink = links[linkID]; + ArticulationLinkData& tLinkDatum = linkData[linkID]; + ArticulationJointCoreData& tJointDatum = jointData[linkID]; + Z[tLink.parent] += FeatherstoneArticulation::propagateImpulse(tLinkDatum, tJointDatum, Z[linkID]); + } + + + if (fixBase) + { + deltaV[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + else + { + ArticulationLinkData& hLinkDatum = linkData[0]; + + SpatialMatrix inverseArticulatedInertia = hLinkDatum.spatialArticulatedInertia.getInverse(); + + deltaV[0] = inverseArticulatedInertia * (-Z[0]); + motionVelcities[0] += deltaV[0]; + + PX_ASSERT(motionVelcities[0].isFinite()); + } + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& tLink = links[linkID]; + ArticulationLinkData& tLinkDatum = linkData[linkID]; + ArticulationJointCoreData& tJointDatum = jointData[linkID]; + PxReal* jV = &jointDeltaVelocities[tJointDatum.jointOffset]; + deltaV[linkID] = FeatherstoneArticulation::propagateVelocity(tLinkDatum, tJointDatum, Z[linkID], + jV, deltaV[tLink.parent]); + motionVelcities[linkID] += deltaV[linkID]; + PX_ASSERT(motionVelcities[linkID].isFinite()); + } + + const PxReal invDt = 1 / dt; + //update joint acceleration + for (PxU32 i = 0; i < data.getDofs(); ++i) + { + jointVelocities[i] += jointDeltaVelocities[i]; + jointAccelerations[i] = jointDeltaVelocities[i] * invDt; + } + } + + + PxU32 FeatherstoneArticulation::computeUnconstrainedVelocities( + const ArticulationSolverDesc& desc, + PxReal dt, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, PxU64 contextID, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) + { + PX_UNUSED(contextID); + PX_UNUSED(desc); + FeatherstoneArticulation* articulation = static_cast(desc.articulation); + ArticulationData& data = articulation->mArticulationData; + data.setDt(dt); + + return articulation->computeUnconstrainedVelocitiesInternal(desc, allocator, constraintDesc, + acCount, gravity, Z, deltaV); + } + + + void FeatherstoneArticulation::computeUnconstrainedVelocitiesTGS( + const ArticulationSolverDesc& desc, + PxReal dt, const PxVec3& gravity, + PxU64 contextID, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + PX_UNUSED(contextID); + + FeatherstoneArticulation* articulation = static_cast(desc.articulation); + ArticulationData& data = articulation->mArticulationData; + data.setDt(dt); + + return articulation->computeUnconstrainedVelocitiesTGSInternal(gravity, Z, DeltaV); + } + + + //void FeatherstoneArticulation::computeCounteractJointForce(const ArticulationSolverDesc& desc, ScratchData& /*scratchData*/, const PxVec3& gravity) + //{ + // const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + // const PxU32 linkCount = mArticulationData.getLinkCount(); + // const PxU32 totalDofs = mArticulationData.getDofs(); + // //common data + // computeRelativeTransform(mArticulationData); + + // jcalc(mArticulationData); + + // computeSpatialInertia(mArticulationData); + + // DyScratchAllocator allocator(desc.scratchMemory, desc.scratchMemorySize); + + // ScratchData tempScratchData; + // allocateScratchSpatialData(allocator, linkCount, tempScratchData); + + // //PxReal* gravityJointForce = allocator.alloc(totalDofs); + // //{ + // // PxMemZero(gravityJointForce, sizeof(PxReal) * totalDofs); + + // // //compute joint force due to gravity + // // tempScratchData.jointVelocities = NULL; + // // tempScratchData.jointAccelerations = NULL; + // // tempScratchData.jointForces = gravityJointForce; + // // tempScratchData.externalAccels = NULL; + + // // if (fixBase) + // // inverseDynamic(mArticulationData, gravity,tempScratchData); + // // else + // // inverseDynamicFloatingLink(mArticulationData, gravity, tempScratchData); + // //} + + // ////PxReal* jointForce = mArticulationData.getJointForces(); + // //PxReal* tempJointForce = mArticulationData.getTempJointForces(); + // //{ + // // PxMemZero(tempJointForce, sizeof(PxReal) * totalDofs); + + // // //compute joint force due to coriolis force + // // tempScratchData.jointVelocities = mArticulationData.getJointVelocities(); + // // tempScratchData.jointAccelerations = NULL; + // // tempScratchData.jointForces = tempJointForce; + // // tempScratchData.externalAccels = NULL; + + // // if (fixBase) + // // inverseDynamic(mArticulationData, PxVec3(0.f), tempScratchData); + // // else + // // inverseDynamicFloatingLink(mArticulationData, PxVec3(0.f), tempScratchData); + // //} + + // //PxReal* jointForce = mArticulationData.getJointForces(); + // //for (PxU32 i = 0; i < mArticulationData.getDofs(); ++i) + // //{ + // // jointForce[i] = tempJointForce[i] - gravityJointForce[i]; + // //} + + // //PxReal* jointForce = mArticulationData.getJointForces(); + // PxReal* tempJointForce = mArticulationData.getTempJointForces(); + // { + // PxMemZero(tempJointForce, sizeof(PxReal) * totalDofs); + + // //compute joint force due to coriolis force + // tempScratchData.jointVelocities = mArticulationData.getJointVelocities(); + // tempScratchData.jointAccelerations = NULL; + // tempScratchData.jointForces = tempJointForce; + // tempScratchData.externalAccels = mArticulationData.getExternalAccelerations(); + + // if (fixBase) + // inverseDynamic(mArticulationData, gravity, tempScratchData); + // else + // inverseDynamicFloatingLink(mArticulationData, gravity, tempScratchData); + // } + + // PxReal* jointForce = mArticulationData.getJointForces(); + // for (PxU32 i = 0; i < mArticulationData.getDofs(); ++i) + // { + // jointForce[i] = tempJointForce[i]; + // } + //} + + void FeatherstoneArticulation::updateArticulation(ScratchData& scratchData, + const PxVec3& gravity, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + computeRelativeTransformC2P(mArticulationData); + + computeLinkVelocities(mArticulationData, scratchData); + + initLinks(mArticulationData, gravity, scratchData, Z, DeltaV); + + computeLinkAcceleration(mArticulationData, scratchData); + + //KS - force a recompute of link velocities + ////computeLinkVelocities(mArticulationData, scratchData); + //{ + // ArticulationData& data = mArticulationData; + // ArticulationLink* links = data.getLinks(); + // ArticulationLinkData* linkData = data.getLinkData(); + // const PxU32 linkCount = data.getLinkCount(); + // const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + // //motion velocities has to be in world space to avoid numerical errors caused by space + // Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; + // //Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + + // PxReal* jointVelocities = scratchData.jointVelocities; + + // ArticulationLink& baseLink = links[0]; + // ArticulationLinkData& baseLinkDatum = linkData[0]; + + // PxsBodyCore& core0 = *baseLink.bodyCore; + + // baseLinkDatum.maxPenBias = core0.maxPenBias; + + // for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + // { + // ArticulationLink& link = links[linkID]; + // ArticulationLinkData& linkDatum = linkData[linkID]; + // PxsBodyCore& bodyCore = *link.bodyCore; + + // linkDatum.maxPenBias = bodyCore.maxPenBias; + + // //SpatialTransform p2c = linkDatum.childToParent.getTranspose(); + + // //motionVelocites[linkID] = p2c * motionVelocites[link.parent]; + // //motionVelocites[linkID] = motionVelocites[link.parent]; + // /*motionVelocities[linkID].top = motionVelocities[link.parent].top; + // motionVelocities[linkID].bottom = motionVelocities[link.parent].bottom + motionVelocities[link.parent].top.cross(linkDatum.rw);*/ + + // const PxTransform& body2World = bodyCore.body2World; + // Cm::SpatialVectorF deltaVel; + // deltaVel.top = body2World.rotateInv(motionVelocities[linkID].top - motionVelocities[link.parent].top); + // deltaVel.bottom = body2World.rotateInv(motionVelocities[linkID].bottom - (motionVelocities[link.parent].bottom + motionVelocities[link.parent].top.cross(linkDatum.rw))); + + // PxVec3 ang = motionVelocities[link.parent].top; + // PxVec3 lin = motionVelocities[link.parent].bottom + motionVelocities[link.parent].top.cross(linkDatum.rw); + // + + // if (jointVelocities) + // { + // ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + // //const PxReal* const jV = &jointVelocity[linkID * 6]; + // //PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + + // Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero(); + // for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + // { + // /*if (jVelocity[ind] < -100.f) + // jVelocity[ind] = -100.f; + // else if (jVelocity[ind] > 100.f) + // jVelocity[ind] = 100.f;*/ + // + // PxReal jointVel = Cm::SpatialVectorF(PxVec3(jointDatum.jointAxis[ind][0], jointDatum.jointAxis[ind][1], jointDatum.jointAxis[ind][2]), + // PxVec3(jointDatum.jointAxis[ind][3], jointDatum.jointAxis[ind][4], jointDatum.jointAxis[ind][5])).dot(deltaVel); + + // //PxReal diff = jointVel - jVelocity[ind]; + // //PX_ASSERT(PxAbs(diff) < 1e-3f); + // + // deltaV += jointDatum.motionMatrix[ind] * jointVel; + // //jVelocity[ind] = jointVel; + // } + + // ang += body2World.rotate(deltaV.top); + // lin += body2World.rotate(deltaV.bottom); + // } + + // PxVec3 angDiff = ang - motionVelocities[linkID].top; + // PxVec3 linDiff = lin - motionVelocities[linkID].bottom; + + // motionVelocities[linkID] = Cm::SpatialVectorF(ang, lin); + // } + //} + } + + + PxU32 FeatherstoneArticulation::computeUnconstrainedVelocitiesInternal( + const ArticulationSolverDesc& desc, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + PX_PROFILE_ZONE("Articulations:computeUnconstrainedVelocities", 0); + + PX_UNUSED(desc); + ArticulationLink* links = mArticulationData.getLinks(); + const PxU32 linkCount = mArticulationData.getLinkCount(); + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + mArticulationData.init(); + + jcalc(mArticulationData); + + ScratchData scratchData; + scratchData.motionVelocities = mArticulationData.getMotionVelocities(); + scratchData.motionAccelerations = mArticulationData.getMotionAccelerations(); + scratchData.coriolisVectors = mArticulationData.getCorioliseVectors(); + scratchData.spatialZAVectors = mArticulationData.getSpatialZAVectors(); + scratchData.jointAccelerations = mArticulationData.getJointAccelerations(); + scratchData.jointVelocities = mArticulationData.getJointVelocities(); + scratchData.jointPositions = mArticulationData.getJointPositions(); + scratchData.jointForces = mArticulationData.getJointForces(); + scratchData.externalAccels = mArticulationData.getExternalAccelerations(); + + updateArticulation(scratchData, gravity, Z, DeltaV); + + //use individual zero acceleration force(we copy the initial Z value to the transmitted force buffers in initLink()) + scratchData.spatialZAVectors = mArticulationData.getTransmittedForces(); + computeZAForceInv(mArticulationData, scratchData); + computeJointTransmittedFrictionForce(mArticulationData, scratchData, Z, DeltaV); + + //the dirty flag is used in inverse dynamic + mArticulationData.setDataDirty(true); + + //zero zero acceleration vector in the articulation data so that we can use this buffer to accumulated + //impulse for the contacts/constraints in the PGS/TGS solvers + PxMemZero(mArticulationData.getSpatialZAVectors(), sizeof(Cm::SpatialVectorF) * linkCount); + + // solver progress counters + maxSolverNormalProgress = 0; + maxSolverFrictionProgress = 0; + solverProgress = 0; + numTotalConstraints = 0; + + for (PxU32 a = 0; a < mArticulationData.getLinkCount(); ++a) + { + mArticulationData.mAccumulatedPoses[a] = mArticulationData.getLink(a).bodyCore->body2World; + mArticulationData.mPreTransform[a] = mArticulationData.getLink(a).bodyCore->body2World; + mArticulationData.mDeltaQ[a] = PxQuat(PxIdentity); + } + + return setupSolverConstraints(allocator, constraintDesc, links, linkCount, + fixBase, mArticulationData, Z, acCount); + } + + + void FeatherstoneArticulation::computeUnconstrainedVelocitiesTGSInternal(const PxVec3& gravity, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + PX_PROFILE_ZONE("Articulations:computeUnconstrainedVelocitiesSS", 0); + const PxU32 linkCount = mArticulationData.getLinkCount(); + + + mArticulationData.init(); + + jcalc(mArticulationData); + + ScratchData scratchData; + scratchData.motionVelocities = mArticulationData.getMotionVelocities(); + scratchData.motionAccelerations = mArticulationData.getMotionAccelerations(); + scratchData.coriolisVectors = mArticulationData.getCorioliseVectors(); + scratchData.spatialZAVectors = mArticulationData.getSpatialZAVectors(); + scratchData.jointAccelerations = mArticulationData.getJointAccelerations(); + scratchData.jointVelocities = mArticulationData.getJointVelocities(); + scratchData.jointPositions = mArticulationData.getJointPositions(); + scratchData.jointForces = mArticulationData.getJointForces(); + scratchData.externalAccels = mArticulationData.getExternalAccelerations(); + + updateArticulation(scratchData, gravity, Z, DeltaV); + + + scratchData.spatialZAVectors = mArticulationData.getTransmittedForces(); + computeZAForceInv(mArticulationData, scratchData); + computeJointTransmittedFrictionForce(mArticulationData, scratchData, Z, DeltaV); + + + //zero zero acceleration vector in the articulation data so that we can use this buffer to accumulated + //impulse for the contacts/constraints in the PGS/TGS solvers + PxMemZero(mArticulationData.getSpatialZAVectors(), sizeof(Cm::SpatialVectorF) * linkCount); + + //the dirty flag is used in inverse dynamic + mArticulationData.setDataDirty(true); + + for (PxU32 a = 0; a < mArticulationData.getLinkCount(); ++a) + { + mArticulationData.mAccumulatedPoses[a] = mArticulationData.getLink(a).bodyCore->body2World; + mArticulationData.mPreTransform[a] = mArticulationData.getLink(a).bodyCore->body2World; + mArticulationData.mDeltaQ[a] = PxQuat(PxIdentity); + } + mArticulationData.mAccumulatedDt = 0.f; + } + + void FeatherstoneArticulation::enforcePrismaticLimits(PxReal* jPosition, ArticulationJointCore* joint) + { + if (joint->prismaticLimited) + { + if (jPosition[0] < (joint->limits[joint->dofIds[0]].low)) + jPosition[0] = joint->limits[joint->dofIds[0]].low; + + if (jPosition[0] > (joint->limits[joint->dofIds[0]].high)) + jPosition[0] = joint->limits[joint->dofIds[0]].high; + } + } + + PxQuat computeSphericalJointPositions(ArticulationJointCore* joint, + const PxQuat newRot, const PxQuat pBody2WorldRot, + PxReal* jPositions) + { + + PxQuat newParentToChild = (newRot.getConjugate() * pBody2WorldRot).getNormalized(); + + //PxQuat newQ = (pBody2WorldRot * newParentToChild.getConjugate()).getNormalized(); + + const PxQuat cA2w = pBody2WorldRot * joint->parentPose.q; + PxQuat cB2w = newRot * joint->childPose.q; + + if (cA2w.dot(cB2w)<0.0f) // minimum dist quat (equiv to flipping cB2bB.q, which we don't use anywhere) + cB2w = -cB2w; + + const PxQuat cB2cA = cA2w.getConjugate() * cB2w; + + PxQuat twist; //x + PxQuat swing1;//y + PxQuat swing2;//z + separateSwingTwist(cB2cA, twist, swing1, swing2); + //tan(t / 2) = sin(t) / (1 + cos t), so this is the quarter angle + + const PxReal theta0 = PxAtan2(twist.x, (1.f + twist.w)) * 4.f; + const PxReal theta1 = PxAtan2(swing1.y, (1.f + swing1.w)) * 4.f; + const PxReal theta2 = PxAtan2(swing2.z, (1.f + swing2.w)) * 4.f; + + + PxU32 dofIndex = 0; + if (joint->motion[PxArticulationAxis::eTWIST] != PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta0; + if (joint->motion[PxArticulationAxis::eSWING1] != PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta1; + if (joint->motion[PxArticulationAxis::eSWING2] != PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta2; + if (joint->motion[PxArticulationAxis::eTWIST] == PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta0; + if (joint->motion[PxArticulationAxis::eSWING1] == PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta1; + if (joint->motion[PxArticulationAxis::eSWING2] == PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta2; + + return newParentToChild; + } + + void FeatherstoneArticulation::computeAndEnforceJointPositions(ArticulationData& data, PxReal* jointPositions) + { + ArticulationLink* links = data.getLinks(); + const PxU32 linkCount = data.getLinkCount(); + + ArticulationJointCoreData* jointData = data.getJointData(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationJointCore* joint = link.inboundJoint; + ArticulationJointCoreData& jointDatum = jointData[linkID]; + PxReal* jPositions = &jointPositions[jointDatum.jointOffset]; + + if (joint->jointType == PxArticulationJointType::eSPHERICAL) + { + ArticulationLink& pLink = links[link.parent]; + //const PxTransform pBody2World = pLink.bodyCore->body2World; + + computeSphericalJointPositions(joint, link.bodyCore->body2World.q, + pLink.bodyCore->body2World.q, jPositions); + } + else if (joint->jointType == PxArticulationJointType::eREVOLUTE) + { + /*if (jPositions[0] < -PxPi) + jPositions[0] += PxTwoPi; + else if (jPositions[0] > PxPi) + jPositions[0] -= PxTwoPi;*/ + + PxReal jPos = jPositions[0]; + + if (jPos > PxTwoPi) + jPos -= PxTwoPi*2.f; + else if (jPos < -PxTwoPi) + jPos += PxTwoPi*2.f; + + jPos = PxClamp(jPos, -PxTwoPi*2.f, PxTwoPi*2.f); + + jPositions[0] = jPos; + } + else if(joint->jointType == PxArticulationJointType::ePRISMATIC) + { + enforcePrismaticLimits(jPositions, joint); + } + } + } + + void FeatherstoneArticulation::propagateLinksDown(ArticulationData& data, PxReal* jointDeltaVelocities, PxReal* jointPositions, + Cm::SpatialVectorF* motionVelocities) + { + ArticulationLink* links = mArticulationData.getLinks(); + + PxTransform* preTransforms = mArticulationData.getPreTransform(); + PX_UNUSED(preTransforms); + PX_UNUSED(motionVelocities); + + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + const PxReal dt = data.getDt(); + + PxReal* jointVelocities = data.getJointVelocities(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + + ArticulationJointCoreData& jointDatum = jointData[linkID]; + //ArticulationLinkData& linkDatum = mArticulationData.getLinkData(linkID); + //const PxTransform oldTransform = preTransforms[linkID]; + + ArticulationLink& pLink = links[link.parent]; + const PxTransform pBody2World = pLink.bodyCore->body2World; + + ArticulationJointCore* joint = link.inboundJoint; + + PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + PxReal* jDeltaVelocity = &jointDeltaVelocities[jointDatum.jointOffset]; + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + + PxQuat newParentToChild; + PxQuat newWorldQ; + PxVec3 r; + + + const PxVec3 childOffset = -joint->childPose.p; + const PxVec3 parentOffset = joint->parentPose.p; + + PxTransform& body2World = link.bodyCore->body2World; + + + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + const PxReal delta = (jVelocity[0] + jDeltaVelocity[0]) * dt; + + jPosition[0] += delta; + + enforcePrismaticLimits(jPosition, joint); + + newParentToChild = joint->relativeQuat; + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + const PxVec3& u = jointDatum.motionMatrix[0].bottom; + + r = e + d + u * jPosition[0]; + break; + } + case PxArticulationJointType::eREVOLUTE: + { + //use positional iteration JointVelociy to integrate + const PxReal delta = (jVelocity[0] + jDeltaVelocity[0]) * dt; + + PxReal jPos = jPosition[0] + delta; + + if (jPos > PxTwoPi) + jPos -= PxTwoPi*2.f; + else if (jPos < -PxTwoPi) + jPos += PxTwoPi*2.f; + + jPos = PxClamp(jPos, -PxTwoPi*2.f, PxTwoPi*2.f); + jPosition[0] = jPos; + + const PxVec3& u = jointDatum.motionMatrix[0].top; + + PxQuat jointRotation = PxQuat(-jPosition[0], u); + if (jointRotation.w < 0) //shortest angle. + jointRotation = -jointRotation; + + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + if (jointDatum.dof < 3) + { + newParentToChild = PxQuat(PxIdentity); + //We are simulating a revolute or 2d joint, so just integrate quaternions and joint positions as above... + for (PxU32 i = 0; i < jointDatum.dof; ++i) + { + const PxReal delta = (jVelocity[i] + jDeltaVelocity[i]) * dt; + + PxReal jPos = jPosition[i] + delta; + + if (jPos > PxTwoPi) + jPos -= PxTwoPi*2.f; + else if (jPos < -PxTwoPi) + jPos += PxTwoPi*2.f; + + jPos = PxClamp(jPos, -PxTwoPi*2.f, PxTwoPi*2.f); + jPosition[i] = jPos; + + const PxVec3& u = jointDatum.motionMatrix[i].top; + + PxQuat jointRotation = PxQuat(-jPosition[i], u); + if (jointRotation.w < 0) //shortest angle. + jointRotation = -jointRotation; + + newParentToChild = newParentToChild * (jointRotation * joint->relativeQuat).getNormalized(); + } + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + + } + else + { + + const PxTransform oldTransform = preTransforms[linkID]; + + PxVec3 worldAngVel = motionVelocities[linkID].top; + + newWorldQ = Ps::exp(worldAngVel*dt) * oldTransform.q; + + newParentToChild = computeSphericalJointPositions(joint, newWorldQ, + pBody2World.q, jPosition); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + } + + break; + } + case PxArticulationJointType::eFIX: + { + //this is fix joint so joint don't have velocity + newParentToChild = joint->relativeQuat; + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + r = e + d; + break; + } + default: + break; + } + + + body2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized(); + body2World.p = pBody2World.p + body2World.q.rotate(r); + + + PX_ASSERT(body2World.isSane()); + PX_ASSERT(body2World.isValid()); + + } + } + + void FeatherstoneArticulation::updateBodies(const ArticulationSolverDesc& desc, PxReal dt) + { + updateBodies(desc, dt, true); + } + + void FeatherstoneArticulation::updateBodiesTGS(const ArticulationSolverDesc& desc, PxReal dt) + { + updateBodies(desc, dt, false); + } + + void FeatherstoneArticulation::updateBodies(const ArticulationSolverDesc& desc, PxReal dt, bool integrateJointPositions) + { + FeatherstoneArticulation* articulation = static_cast(desc.articulation); + ArticulationData& data = articulation->mArticulationData; + ArticulationLink* links = data.getLinks(); + const PxU32 linkCount = data.getLinkCount(); + + Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities(); + + Cm::SpatialVector* externalAccels = data.getExternalAccelerations(); + Cm::SpatialVector zero = Cm::SpatialVector::zero(); + + if (integrateJointPositions) + data.setDt(dt); + else + data.setDt(0.f); + + + + PxTransform* preTransforms = data.getPreTransform(); + + if (articulation->mHasSphericalJoint) + { + for (PxU32 i = 0; i < linkCount; ++i) + { + ArticulationLink& link = links[i]; + + PxsBodyCore* bodyCore = link.bodyCore; + + //record link's previous transform + preTransforms[i] = bodyCore->body2World; + } + } + + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + ArticulationLink& baseLink = links[0]; + + PxsBodyCore* baseBodyCore = baseLink.bodyCore; + + if (!integrateJointPositions) + { + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + links[linkID].bodyCore->body2World = data.mAccumulatedPoses[linkID].getNormalized(); + } + + articulation->computeAndEnforceJointPositions(data, data.getJointPositions()); + } + else + { + + if (!fixBase) + { + + //body2World store new body transform integrated from solver linear/angular velocity + + ArticulationLink& link = links[0]; + + const PxTransform& preTrans = link.bodyCore->body2World; + + Cm::SpatialVectorF& posVel = data.getPosIterMotionVelocity(0); + + updateRootBody(posVel, preTrans, data, dt); + } + else if(!integrateJointPositions) + { + baseBodyCore->body2World = data.mAccumulatedPoses[0]; + } + + //using the original joint velocities and delta velocities changed in the positional iter to update joint position/body transform + articulation->propagateLinksDown(data, data.getPosIterJointDeltaVelocities(), data.getJointPositions(), data.getPosIterMotionVelocities()); + } + + //update joint velocities/accelerations due to contacts/constraints + if (data.mJointDirty && integrateJointPositions) + { + //update delta joint velocity and motion velocity due to velocity iteration changes + Cm::SpatialVectorF deltaV[64]; + + PxcFsFlushVelocity(*articulation, deltaV); + + //update joint velocity/accelerations + PxReal* jointVelocities = data.getJointVelocities(); + PxReal* jointAccelerations = data.getJointAccelerations(); + PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + const PxU32 totalDofs = data.getDofs(); + const PxReal invDt = 1.f / dt; + for (PxU32 i = 0; i < totalDofs; ++i) + { + jointVelocities[i] += jointDeltaVelocities[i]; + jointAccelerations[i] += jointDeltaVelocities[i] * invDt; + } + } + + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + PxsBodyCore* bodyCore = link.bodyCore; + + bodyCore->linearVelocity = motionVelocities[linkID].bottom; + bodyCore->angularVelocity = motionVelocities[linkID].top; + //zero external accelerations + externalAccels[linkID] = zero; + } + } + + + //void FeatherstoneArticulation::updateBodies(const ArticulationSolverDesc& desc, PxReal dt, bool integrateJointPositions) + //{ + // PX_PROFILE_ZONE("Articulations:updateBodies", 0); + // FeatherstoneArticulation* articulation = static_cast(desc.articulation); + // ArticulationData& data = articulation->mArticulationData; + // ArticulationLink* links = data.getLinks(); + // const PxU32 linkCount = data.getLinkCount(); + + // Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities(); + // Cm::SpatialVector* externalAccels = data.getExternalAccelerations(); + // Cm::SpatialVector zero = Cm::SpatialVector::zero(); + + // if (data.mJointDirty || (!integrateJointPositions)) + // { + // //printf("============collision change joint velocity============\n"); + // + // if (data.mJointDirty) + // { + // Cm::SpatialVectorF deltaV[64]; + + // PxcFsFlushVelocity(*articulation, deltaV); + // } + + // PxTransform* preTransforms = data.getPreTransform(); + + // if (integrateJointPositions) + // data.setDt(dt); + // else + // data.setDt(0.f); + + // //update joint velocity/accelerations due to contact + // PxReal* jointVelocities = data.getJointVelocities(); + // PxReal* jointAccelerations = data.getJointAccelerations(); + // PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + // const PxU32 totalDofs = data.getDofs(); + // const PxReal invDt = 1.f / dt; + // for (PxU32 i = 0; i < totalDofs; ++i) + // { + // jointVelocities[i] += jointDeltaVelocities[i]; + // jointAccelerations[i] += jointDeltaVelocities[i] * invDt; + // //printf("jV %f\n", jointVelocities[i]); + // } + + // if (articulation->mHasSphericalJoint) + // { + // for (PxU32 i = 0; i < linkCount; ++i) + // { + // ArticulationLink& link = links[i]; + + // PxsBodyCore* bodyCore = link.bodyCore; + + // //record link's previous transform + // preTransforms[i] = bodyCore->body2World; + // } + // } + + // const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + // ArticulationLink& baseLink = links[0]; + + // PxsBodyCore* baseBodyCore = baseLink.bodyCore; + + // if (!fixBase) + // { + + // + + // //body2World store new body transform integrated from solver linear/angular velocity + // if (integrateJointPositions) + // { + // ArticulationLink& link = links[0]; + + // const PxTransform& preTrans = link.bodyCore->body2World; + + // Cm::SpatialVectorF& posVel = data.getPosIterMotionVelocity(0); + + // updateRootBody(posVel, preTrans, data, dt); + // } + // else + // { + // baseBodyCore->body2World = data.mAccumulatedPoses[0]; + // } + + // Cm::SpatialVectorF& motionVelocity = motionVelocities[0]; + // PX_ASSERT(motionVelocity.top.isFinite()); + // PX_ASSERT(motionVelocity.bottom.isFinite()); + + // baseBodyCore->linearVelocity = motionVelocity.bottom; + // baseBodyCore->angularVelocity = motionVelocity.top; + // } + + + // articulation->propagateLinksDown(data, data.getPosIterJointDeltaVelocities(), data.getJointPositions(), data.getPosIterMotionVelocities()); + + // //zero all the external accelerations + // externalAccels[0] = zero; + + // for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + // { + // ArticulationLink& link = links[linkID]; + // PxsBodyCore* bodyCore = link.bodyCore; + + // bodyCore->linearVelocity = motionVelocities[linkID].bottom; + // bodyCore->angularVelocity = motionVelocities[linkID].top; + + // externalAccels[linkID] = zero; + // } + // } + // else + // { + // for (PxU32 i = 0; i < linkCount; ++i) + // { + // ArticulationLink& link = links[i]; + + // PxsBodyCore* bodyCore = link.bodyCore; + + // //record link's unconstrainted integrated transform + // bodyCore->body2World = data.mTempData.mLinkTransform[i]; + + // } + + // for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + // { + // ArticulationLink& link = links[linkID]; + // PxsBodyCore* bodyCore = link.bodyCore; + + // bodyCore->linearVelocity = motionVelocities[linkID].bottom; + // bodyCore->angularVelocity = motionVelocities[linkID].top; + // + // //zero all the external accelerations + // externalAccels[linkID] = zero; + // + // } + // } + + //} + + void FeatherstoneArticulation::updateRootBody(const Cm::SpatialVectorF& motionVelocity, + const PxTransform& preTransform, ArticulationData& data, const PxReal dt) + { + ArticulationLink* links = data.getLinks(); + //body2World store new body transform integrated from solver linear/angular velocity + + PX_ASSERT(motionVelocity.top.isFinite()); + PX_ASSERT(motionVelocity.bottom.isFinite()); + + ArticulationLink& baseLink = links[0]; + + PxsBodyCore* baseBodyCore = baseLink.bodyCore; + + //(1) project the current body's velocity (based on its pre-pose) to the geometric COM that we're integrating around... + + PxVec3 comLinVel = motionVelocity.bottom; + + //using the position iteration motion velocity to compute the body2World + PxVec3 newP = (preTransform.p) + comLinVel * dt; + + PxQuat deltaQ = Ps::exp(motionVelocity.top*dt); + + baseBodyCore->body2World = PxTransform(newP, (deltaQ* preTransform.q).getNormalized()); + + //ML: we are going to store the motionVelocity back later + /* baseBodyCore->linearVelocity = motionVelocity.bottom; + baseBodyCore->angularVelocity = motionVelocity.top;*/ + + PX_ASSERT(baseBodyCore->body2World.isFinite() && baseBodyCore->body2World.isValid()); + } + + void FeatherstoneArticulation::updateBodies() + { + ArticulationData& data = mArticulationData; + const PxReal dt = data.getDt(); + + ArticulationLink* links = data.getLinks(); + + Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities(); + PxTransform* preTransforms = data.getPreTransform(); + + const PxU32 linkCount = data.getLinkCount(); + + if (mHasSphericalJoint) + { + for (PxU32 i = 0; i < linkCount; ++i) + { + ArticulationLink& link = links[i]; + + PxsBodyCore* bodyCore = link.bodyCore; + + //record link's previous transform + PX_ASSERT(bodyCore->body2World.isFinite() && bodyCore->body2World.isValid()); + preTransforms[i] = bodyCore->body2World; + } + } + + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + if (!fixBase) + { + ArticulationLink& link = links[0]; + + const PxTransform& preTrans = link.bodyCore->body2World; + + Cm::SpatialVectorF& motionVelocity = motionVelocities[0]; + + updateRootBody(motionVelocity, preTrans, data, dt); + } + + propagateLinksDown(data, data.getJointVelocities(), data.getJointPositions(), data.getMotionVelocities()); + } + + void FeatherstoneArticulation::getJointAcceleration(const PxVec3& gravity, PxArticulationCache& cache) + { + + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Articulation::getJointAcceleration() commonInit need to be called first to initialize data!"); + return; + } + + const PxU32 linkCount = mArticulationData.getLinkCount(); + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + scratchData.jointVelocities = cache.jointVelocity; + scratchData.jointForces = cache.jointForce; + + computeLinkVelocities(mArticulationData, scratchData); + + //compute individual link's spatial inertia tensor + //[0, M] + //[I, 0] + computeSpatialInertia(mArticulationData); + + //compute inidividual zero acceleration force + computeZ(mArticulationData, gravity, scratchData); + + computeArticulatedSpatialInertia(mArticulationData); + + //compute corolis and centrifugal force + computeC(mArticulationData, scratchData); + + computeArticulatedSpatialZ(mArticulationData, scratchData); + + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + //we have initialized motionVelocity and motionAcceleration to be zero in the root link if + //fix based flag is raised + ArticulationLinkData& baseLinkDatum = mArticulationData.getLinkData(0); + + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; + Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors; + + if (!fixBase) + { + SpatialMatrix inverseArticulatedInertia = baseLinkDatum.spatialArticulatedInertia.getInverse(); + motionAccelerations[0] = -(inverseArticulatedInertia * spatialZAForces[0]); + } +#if FEATHERSTONE_DEBUG + else + { + PX_ASSERT(isSpatialVectorZero(motionAccelerations[0])); + } +#endif + + PxReal* jointAccelerations = cache.jointAcceleration; + //calculate acceleration + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = mArticulationData.getLink(linkID); + + ArticulationLinkData& linkDatum = mArticulationData.getLinkData(linkID); + + //SpatialTransform p2C = linkDatum.childToParent.getTranspose(); + Cm::SpatialVectorF pMotionAcceleration = linkDatum.childToParent.transposeTransform(motionAccelerations[link.parent]); + + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + //calculate jointAcceleration + PxReal* jA = &jointAccelerations[jointDatum.jointOffset]; + computeJointAcceleration(linkDatum, jointDatum, pMotionAcceleration, jA); + + Cm::SpatialVectorF motionAcceleration(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + motionAcceleration += jointDatum.motionMatrix[ind] * jA[ind]; + } + + motionAccelerations[linkID] = pMotionAcceleration + coriolisVectors[linkID] + motionAcceleration; + PX_ASSERT(motionAccelerations[linkID].isFinite()); + } + + allocator->free(tempMemory); + } + +}//namespace Dy +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp new file mode 100644 index 000000000..5d45c0b82 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp @@ -0,0 +1,2120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsMathUtils.h" +#include "CmConeLimitHelper.h" +#include "DySolverConstraint1D.h" +#include "DyFeatherstoneArticulation.h" +#include "PxsRigidBody.h" +#include "PxcConstraintBlockStream.h" +#include "DyArticulationContactPrep.h" +#include "DyDynamics.h" +#include "DyArticulationReference.h" +#include "DyArticulationPImpl.h" +#include "foundation/PxProfiler.h" +#include "DyArticulationFnsSimd.h" +#include "PsFoundation.h" +#include "extensions/PxContactJoint.h" +#include "DyFeatherstoneArticulationLink.h" +#include "DyFeatherstoneArticulationJointData.h" +#include "DyConstraint.h" +#include "DyConstraintPrep.h" +#include "DySolverContext.h" + +namespace physx +{ + +namespace Dy +{ + void PxcFsFlushVelocity(FeatherstoneArticulation& articulation, Cm::SpatialVectorF* deltaV); + + void FeatherstoneArticulation::computeLinkAccelerationInv(ArticulationData& data, ScratchData& scratchData) + { + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + + Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors; + + PxReal* jointAccelerations = scratchData.jointAccelerations; + + motionAccelerations[0] = Cm::SpatialVectorF::Zero(); + + for (PxU32 linkID = 1; linkID < data.getLinkCount(); ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + SpatialTransform p2c = linkDatum.childToParent.getTranspose(); + + //parent's motion acceleration into child space + Cm::SpatialVectorF pMotionAcceleration = p2c * motionAccelerations[link.parent]; + + Cm::SpatialVectorF motionAcceleration(PxVec3(0.f), PxVec3(0.f)); + + if (jointAccelerations) + { + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + const PxReal* jAcceleration = &jointAccelerations[jointDatum.jointOffset]; + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + motionAcceleration += jointDatum.motionMatrix[ind] * jAcceleration[ind]; + } + } + + motionAccelerations[linkID] = pMotionAcceleration + coriolisVectors[linkID] + motionAcceleration; + } + } + + //generalized force + void FeatherstoneArticulation::computeGeneralizedForceInv(ArticulationData& data, ScratchData& scratchData) + { + const PxU32 linkCount = data.getLinkCount(); + + Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; + PxReal* jointForces = scratchData.jointForces; + + for (PxU32 linkID = (linkCount - 1); linkID > 0; --linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + //joint force + //pLink.spatialZAForce += link.childToParent * link.spatialZAForce; + spatialZAForces[link.parent] += linkDatum.childToParent * spatialZAForces[linkID]; + + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + //compute generalized force + PxReal* force = &jointForces[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + force[ind] = jointDatum.motionMatrix[ind].innerProduct(spatialZAForces[linkID]); + } + } + } + + void FeatherstoneArticulation::computeZAForceInv(ArticulationData& data, ScratchData& scratchData) + { + const PxU32 linkCount = data.getLinkCount(); + + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + + Cm::SpatialVectorF* biasForce = scratchData.spatialZAVectors; + + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + Cm::SpatialVectorF Ia = linkDatum.spatialInertia * motionAccelerations[linkID]; + + biasForce[linkID] +=Ia; + } + } + + void FeatherstoneArticulation::initCompositeSpatialInertia(ArticulationData& data, Dy::SpatialMatrix* compositeSpatialInertia) + { + ArticulationLinkData* linkData = data.getLinkData(); + const PxU32 linkCount = data.getLinkCount(); + + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + ArticulationLinkData& linkDatum = linkData[linkID]; + compositeSpatialInertia[linkID] = linkDatum.spatialInertia; + } + } + + void FeatherstoneArticulation::computeCompositeSpatialInertiaAndZAForceInv(ArticulationData& data, ScratchData& scratchData) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + const PxU32 linkCount = data.getLinkCount(); + const PxU32 startIndex = PxU32(linkCount - 1); + + Dy::SpatialMatrix* compositeSpatialInertia = scratchData.compositeSpatialInertias; + Cm::SpatialVectorF* zaForce = scratchData.spatialZAVectors; + + initCompositeSpatialInertia(data, compositeSpatialInertia); + + for (PxU32 linkID = startIndex; linkID > 0; --linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + + const SpatialTransform& c2p = linkDatum.childToParent; + const SpatialTransform p2c = linkDatum.childToParent.getTranspose(); + + Dy::SpatialMatrix cSpatialInertia = compositeSpatialInertia[linkID]; + + PxMat33 tl = c2p.R * cSpatialInertia.topLeft; + PxMat33 tr = c2p.R * cSpatialInertia.topRight; + PxMat33 bl = c2p.T * cSpatialInertia.topLeft + c2p.R * cSpatialInertia.bottomLeft; + PxMat33 br = c2p.T * cSpatialInertia.topRight + c2p.R * cSpatialInertia.getBottomRight(); + + cSpatialInertia.topLeft = tl * p2c.R + tr * p2c.T; + cSpatialInertia.topRight = tr * p2c.R; + cSpatialInertia.bottomLeft = bl * p2c.R + br * p2c.T; + + //aligned inertia + cSpatialInertia.bottomLeft = (cSpatialInertia.bottomLeft + cSpatialInertia.bottomLeft.getTranspose()) * 0.5f; + + //compute parent's composite spatial inertia + compositeSpatialInertia[link.parent] += cSpatialInertia; + + //compute zero acceleration force. This is the force that would be required to support the + //motion of all the bodies in childen set if root node acceleration happened to be zero + zaForce[link.parent] += c2p * zaForce[linkID]; + } + } + + void FeatherstoneArticulation::computeRelativeGeneralizedForceInv(ArticulationData& data, ScratchData& scratchData) + { + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + Dy::SpatialMatrix* compositeSpatialInertia = scratchData.compositeSpatialInertias; + Cm::SpatialVectorF* zaForce = scratchData.spatialZAVectors; + PxReal* jointForces = scratchData.jointForces; + + Dy::SpatialMatrix invInertia = compositeSpatialInertia[0].invertInertia(); + motionAccelerations[0] = -(invInertia * zaForce[0]); + + const PxU32 linkCount = data.getLinkCount(); + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + + const SpatialTransform& p2c = linkDatum.childToParent.getTranspose(); + + motionAccelerations[linkID] = p2c * motionAccelerations[link.parent]; + + + zaForce[linkID] = compositeSpatialInertia[linkID] * motionAccelerations[linkID] + zaForce[linkID]; + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + //compute generalized force + PxReal* jForce = &jointForces[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + jForce[ind] = jointDatum.motionMatrix[ind].innerProduct(zaForce[linkID]); + } + } + } + + void FeatherstoneArticulation::inverseDynamic(ArticulationData& data, const PxVec3& gravity, + ScratchData& scratchData) + { + //pass 1 + computeLinkVelocities(data, scratchData); + + computeC(data, scratchData); + + computeZ(data, gravity, scratchData); + + computeLinkAccelerationInv(data, scratchData); + + computeZAForceInv(data, scratchData); + + //pass 2 + computeGeneralizedForceInv(data, scratchData); + } + + void FeatherstoneArticulation::inverseDynamicFloatingBase(ArticulationData& data, const PxVec3& gravity, + ScratchData& scratchData) + { + //pass 1 + computeLinkVelocities(data, scratchData); + + computeC(data, scratchData); + + computeZ(data, gravity, scratchData); + //no gravity, no external accelerations because we have turned those in force in + //computeZ + computeLinkAccelerationInv(data, scratchData); + + computeZAForceInv(data, scratchData); + + //pass 2 + computeCompositeSpatialInertiaAndZAForceInv(data, scratchData); + + //pass 3 + computeRelativeGeneralizedForceInv(data, scratchData); + } + + + void FeatherstoneArticulation::applyCacheToDest(ArticulationData& data, PxArticulationCache& cache, + PxReal* jVelocities, PxReal* jAccelerations, PxReal* jPositions, PxReal* jointForces, + const PxArticulationCacheFlags flag) + { + if (flag & PxArticulationCache::eVELOCITY) + { + copyJointData(data, jVelocities, cache.jointVelocity); + } + + if (flag & PxArticulationCache::eACCELERATION) + { + copyJointData(data, jAccelerations, cache.jointAcceleration); + } + + if (flag & PxArticulationCache::eROOT) + { + ArticulationLink& rLink = mArticulationData.getLink(0); + rLink.bodyCore->body2World = cache.rootLinkData.transform; + rLink.bodyCore->linearVelocity = cache.rootLinkData.linVel; + rLink.bodyCore->angularVelocity = cache.rootLinkData.angVel; + } + + if (flag & PxArticulationCache::ePOSITION) + { + copyJointData(data, jPositions, cache.jointPosition); + //update link's position based on the joint position + teleportLinks(data); + } + + if (flag & PxArticulationCache::eFORCE) + { + copyJointData(data, jointForces, cache.jointForce); + } + } + + void FeatherstoneArticulation::packJointData(const PxReal* maximum, PxReal* reduced) + { + const PxU32 linkCount = mArticulationData.getLinkCount(); + + for (PxU32 linkID = 1; linkID < linkCount; linkID++) + { + + ArticulationLink& linkDatum = mArticulationData.getLink(linkID); + ArticulationJointCore* joint = linkDatum.inboundJoint; + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + + const PxReal* maxJointData = &maximum[(linkID - 1) * DY_MAX_DOF]; + PxReal* reducedJointData = &reduced[jointDatum.jointOffset]; + + PxU32 count = 0; + for (PxU32 j = 0; j < DY_MAX_DOF; ++j) + { + PxArticulationMotions motion = joint->motion[j]; + if (motion != PxArticulationMotion::eLOCKED) + { + reducedJointData[count] = maxJointData[j]; + count++; + } + } + + PX_ASSERT(count == jointDatum.dof); + } + + } + + void FeatherstoneArticulation::unpackJointData(const PxReal* reduced, PxReal* maximum) + { + const PxU32 linkCount = mArticulationData.getLinkCount(); + + for (PxU32 linkID = 1; linkID < linkCount; linkID++) + { + ArticulationLink& linkDatum = mArticulationData.getLink(linkID); + ArticulationJointCore* joint = linkDatum.inboundJoint; + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + + PxReal* maxJointData = &maximum[(linkID - 1) * DY_MAX_DOF]; + const PxReal* reducedJointData = &reduced[jointDatum.jointOffset]; + + PxU32 count = 0; + for (PxU32 j = 0; j < DY_MAX_DOF; ++j) + { + PxArticulationMotions motion = joint->motion[j]; + if (motion != PxArticulationMotion::eLOCKED) + { + maxJointData[j] = reducedJointData[count]; + count++; + } + else + { + maxJointData[j] = 0.f; + } + } + + PX_ASSERT(count == jointDatum.dof); + } + } + + void FeatherstoneArticulation::initializeCommonData() + { + computeRelativeTransformC2P(mArticulationData); + + computeRelativeTransformC2B(mArticulationData); + + jcalc(mArticulationData); + + computeSpatialInertia(mArticulationData); + + mArticulationData.setDataDirty(false); + } + + void FeatherstoneArticulation::getGeneralizedGravityForce(const PxVec3& gravity, PxArticulationCache& cache) + { + + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Articulation::getGeneralisedGravityForce() commonInit need to be called first to initialize data!"); + return; + } + +#if FEATHERSTONE_DEBUG + PxReal* jointForce = reinterpret_cast(PX_ALLOC(sizeof(PxReal) * mArticulationData.getDofs(), "jointForce")); + { + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + scratchData.jointVelocities = NULL; + scratchData.jointAccelerations = NULL; + scratchData.jointForces = jointForce; + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + if (fixBase) + inverseDynamic(mArticulationData, gravity, NULL, scratchData); + else + inverseDynamicFloatingLink(mArticulationData, gravity, NULL, scratchData); + + allocator->free(tempMemory); + } +#endif + + const PxVec3 tGravity = -gravity; + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + const PxU32 linkCount = mArticulationData.getLinkCount(); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + if (fixBase) + { + Cm::SpatialVectorF* spatialZAForces = reinterpret_cast(allocator->alloc(sizeof(Cm::SpatialVectorF) * linkCount)); + + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + ArticulationLink& link = mArticulationData.getLink(linkID); + + PxsBodyCore& core = *link.bodyCore; + + const PxTransform& body2World = core.body2World; + + const PxReal m = 1.0f / core.inverseMass; + + const PxVec3 linkGravity = body2World.rotateInv(tGravity); + + spatialZAForces[linkID].top = m*linkGravity; + spatialZAForces[linkID].bottom = PxVec3(0.f); + } + + ScratchData scratchData; + scratchData.spatialZAVectors = spatialZAForces; + scratchData.jointForces = cache.jointForce; + + computeGeneralizedForceInv(mArticulationData, scratchData); + + //release spatialZA vectors + allocator->free(spatialZAForces); + } + else + { + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + scratchData.jointVelocities = NULL; + scratchData.jointAccelerations = NULL; + scratchData.jointForces = cache.jointForce; + scratchData.externalAccels = NULL; + + inverseDynamicFloatingBase(mArticulationData, tGravity, scratchData); + + allocator->free(tempMemory); + } + +#if FEATHERSTONE_DEBUG + //compare joint force + const PxU32 totalDofs = mArticulationData.getDofs(); + for (PxU32 i = 0; i < totalDofs; ++i) + { + const PxReal dif = jointForce[i] - cache.jointForce[i]; + PX_ASSERT(PxAbs(dif) < 5e-3f); + } + + PX_FREE(jointForce); +#endif + + } + + //gravity, acceleration and external force(external acceleration) are zero + void FeatherstoneArticulation::getCoriolisAndCentrifugalForce(PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Articulation::getCoriolisAndCentrifugalForce() commonInit need to be called first to initialize data!"); + return; + } + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + scratchData.jointVelocities = cache.jointVelocity; + scratchData.jointAccelerations = NULL; + scratchData.jointForces = cache.jointForce; + scratchData.externalAccels = NULL; + + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + if (fixBase) + inverseDynamic(mArticulationData, PxVec3(0.f), scratchData); + else + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + + allocator->free(tempMemory); + + } + + //gravity, joint acceleration and joint velocity are zero + void FeatherstoneArticulation::getGeneralizedExternalForce(PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Articulation::getCoriolisAndCentrifugalForce() commonInit need to be called first to initialize data!"); + return; + } + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + scratchData.jointVelocities = NULL; + scratchData.jointAccelerations = NULL; + scratchData.jointForces = cache.jointForce; + + Cm::SpatialVector* accels = reinterpret_cast(allocator->alloc(sizeof(Cm::SpatialVector) * linkCount)); + + //turn external forces to external accels + for (PxU32 i = 0; i < linkCount; ++i) + { + ArticulationLink& link = mArticulationData.getLink(i); + PxsBodyCore& core = *link.bodyCore; + + Cm::SpatialVector& force = cache.externalForces[i]; + Cm::SpatialVector& accel = accels[i]; + + accel.linear = force.linear * core.inverseMass; + + PxMat33 inverseInertiaWorldSpace; + Cm::transformInertiaTensor(core.inverseInertia, PxMat33(core.body2World.q), inverseInertiaWorldSpace); + + accel.angular = inverseInertiaWorldSpace * force.angular; + } + + scratchData.externalAccels = accels; + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + if (fixBase) + inverseDynamic(mArticulationData, PxVec3(0.f), scratchData); + else + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + + allocator->free(tempMemory); + allocator->free(accels); + + } + + //provided joint acceleration, calculate joint force + void FeatherstoneArticulation::getJointForce(PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "ArticulationHelper::getJointForce() commonInit need to be called first to initialize data!"); + return; + } + + //const PxU32 size = sizeof(PxReal) * mArticulationData.getDofs(); + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + //PxReal* jointVelocities = reinterpret_cast(allocator->alloc(size)); + + + ScratchData scratchData; + scratchData.jointVelocities = NULL;//jont velocity will be zero + scratchData.jointAccelerations = cache.jointAcceleration; //input + scratchData.jointForces = cache.jointForce; //output + scratchData.externalAccels = NULL; + + PxU8* tempMemory = allocateScratchSpatialData(allocator, mArticulationData.getLinkCount(), scratchData); + + //make sure joint velocity be zero + //PxMemZero(jointVelocities, sizeof(PxReal) * mArticulationData.getDofs()); + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + if (fixBase) + inverseDynamic(mArticulationData, PxVec3(0.f), scratchData); + else + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + + //allocator->free(jointVelocities); + allocator->free(tempMemory); + } + + static void computeJacobian(PxKinematicJacobian& jacobian, ArticulationJointCoreData& jointDatum, + const PxTransform& body2World) + { + for (PxU32 ind = 0; ind < jointDatum.motionMatrix.getNumColumns(); ++ind) + { + PxReal* jacoColumn = jacobian.j[ind]; + + Cm::SpatialVectorF& column = jointDatum.motionMatrix[0]; + + const PxVec3 wAngular = body2World.rotate(column.top); + const PxVec3 wLinear = body2World.rotate(column.bottom); + + jacoColumn[0] = wAngular.x; jacoColumn[1] = wAngular.y; jacoColumn[2] = wAngular.z; + jacoColumn[3] = wLinear.x; jacoColumn[4] = wLinear.y; jacoColumn[5] = wLinear.z; + } + } + + void FeatherstoneArticulation::getKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) + { + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxMemZero(cache.jacobian, sizeof(PxKinematicJacobian) * linkCount); + + ArticulationLink& endEffectorLink = mArticulationData.getLink(linkID); + + jcalc(mArticulationData); + + for (ArticulationBitField i = endEffectorLink.pathToRoot - 1; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + + ArticulationLink& tLink = mArticulationData.getLink(linkID); + ArticulationJointCoreData& tJointData = mArticulationData.getJointData(linkID); + + PxTransform& body2World = tLink.bodyCore->body2World; + PxKinematicJacobian& jacobian = cache.jacobian[index]; + computeJacobian(jacobian, tJointData, body2World); + } + } + + void FeatherstoneArticulation::jcalcLoopJointSubspace(ArticulationJointCore* joint, + ArticulationJointCoreData& jointDatum, SpatialSubspaceMatrix& T) + { + const PxVec3 childOffset = -joint->childPose.p; + const PxVec3 zero(0.f); + + //if the column is free, we put zero for it, this is for computing K(coefficent matrix) + T.setNumColumns(6); + + //transpose(Tc)*S = 0 + //transpose(Ta)*S = 1 + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + PX_ASSERT(jointDatum.dof == 1); + + const PxVec3 rx = (joint->childPose.rotate(PxVec3(1.f, 0.f, 0.f))).getNormalized(); + const PxVec3 ry = (joint->childPose.rotate(PxVec3(0.f, 1.f, 0.f))).getNormalized(); + const PxVec3 rz = (joint->childPose.rotate(PxVec3(0.f, 0.f, 1.f))).getNormalized(); + + + //joint->activeForceSubspace.setNumColumns(1); + + if (jointDatum.jointAxis[0][3] == 1.f) + { + //x is the free translation axis + T.setColumn(0, rx, zero); + T.setColumn(1, ry, zero); + T.setColumn(2, rz, zero); + T.setColumn(3, zero, zero); + T.setColumn(4, zero, ry); + T.setColumn(5, zero, rz); + + //joint->activeForceSubspace.setColumn(0, PxVec3(0.f), rx); + } + else if (jointDatum.jointAxis[0][4] == 1.f) + { + //y is the free translation axis + T.setColumn(0, rx, zero); + T.setColumn(1, ry, zero); + T.setColumn(2, rz, zero); + T.setColumn(3, zero, rx); + T.setColumn(4, zero, zero); + T.setColumn(5, zero, rz); + + //joint->activeForceSubspace.setColumn(0, PxVec3(0.f), ry); + } + else if (jointDatum.jointAxis[0][5] == 1.f) + { + //z is the free translation axis + T.setColumn(0, rx, zero); + T.setColumn(1, ry, zero); + T.setColumn(2, rx, zero); + T.setColumn(3, zero, rx); + T.setColumn(4, zero, ry); + T.setColumn(5, zero, zero); + + //joint->activeForceSubspace.setColumn(0, PxVec3(0.f), rz); + } + + break; + } + case PxArticulationJointType::eREVOLUTE: + { + //joint->activeForceSubspace.setNumColumns(1); + + const PxVec3 rx = (joint->childPose.rotate(PxVec3(1.f, 0.f, 0.f))).getNormalized(); + const PxVec3 ry = (joint->childPose.rotate(PxVec3(0.f, 1.f, 0.f))).getNormalized(); + const PxVec3 rz = (joint->childPose.rotate(PxVec3(0.f, 0.f, 1.f))).getNormalized(); + + const PxVec3 rxXd = rx.cross(childOffset); + const PxVec3 ryXd = ry.cross(childOffset); + const PxVec3 rzXd = rz.cross(childOffset); + + if (jointDatum.jointAxis[0][0] == 1.f) + { + //x is the free rotation axis + + T.setColumn(0, zero, zero); + T.setColumn(1, ry, zero); + T.setColumn(2, rz, zero); + + //joint->activeForceSubspace.setColumn(0, rx, PxVec3(0.f)); + + } + else if (jointDatum.jointAxis[0][1] == 1.f) + { + //y is the free rotation axis + T.setColumn(0, rx, zero); + T.setColumn(1, zero, zero); + T.setColumn(2, rz, zero); + + //joint->activeForceSubspace.setColumn(0, ry, PxVec3(0.f)); + } + else if (jointDatum.jointAxis[0][2] == 1.f) + { + //z is the rotation axis + T.setColumn(0, rx, zero); + T.setColumn(1, ry, zero); + T.setColumn(2, zero, zero); + + //joint->activeForceSubspace.setColumn(0, rz, PxVec3(0.f)); + } + + T.setColumn(3, rxXd, rx); + T.setColumn(4, ryXd, ry); + T.setColumn(5, rzXd, rz); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + //joint->activeForceSubspace.setNumColumns(3); + + const PxVec3 rx = (joint->childPose.rotate(PxVec3(1.f, 0.f, 0.f))).getNormalized(); + const PxVec3 ry = (joint->childPose.rotate(PxVec3(0.f, 1.f, 0.f))).getNormalized(); + const PxVec3 rz = (joint->childPose.rotate(PxVec3(0.f, 0.f, 1.f))).getNormalized(); + + const PxVec3 rxXd = rx.cross(childOffset); + const PxVec3 ryXd = ry.cross(childOffset); + const PxVec3 rzXd = rz.cross(childOffset); + + T.setColumn(0, zero, zero); + T.setColumn(1, zero, zero); + T.setColumn(2, zero, zero); + + T.setColumn(3, rxXd, rx); + T.setColumn(4, ryXd, ry); + T.setColumn(5, rzXd, rz); + + //need to implement constraint force subspace matrix and active force subspace matrix + + break; + } + case PxArticulationJointType::eFIX: + { + //joint->activeForceSubspace.setNumColumns(0); + //T.setNumColumns(6); + + /* const PxVec3 rx = (joint->childPose.rotate(PxVec3(1.f, 0.f, 0.f))).getNormalized(); + const PxVec3 ry = (joint->childPose.rotate(PxVec3(0.f, 1.f, 0.f))).getNormalized(); + const PxVec3 rz = (joint->childPose.rotate(PxVec3(0.f, 0.f, 1.f))).getNormalized(); + + T.setColumn(0, rx, PxVec3(0.f)); + T.setColumn(1, ry, PxVec3(0.f)); + T.setColumn(2, rz, PxVec3(0.f)); + T.setColumn(3, PxVec3(0.f), rx); + T.setColumn(4, PxVec3(0.f), ry); + T.setColumn(5, PxVec3(0.f), rz); + */ + + T.setColumn(0, PxVec3(1.f, 0.f, 0.f), zero); + T.setColumn(1, PxVec3(0.f, 1.f, 0.f), zero); + T.setColumn(2, PxVec3(0.f, 0.f, 1.f), zero); + T.setColumn(3, zero, PxVec3(1.f, 0.f, 0.f)); + T.setColumn(4, zero, PxVec3(0.f, 1.f, 0.f)); + T.setColumn(5, zero, PxVec3(0.f, 0.f, 1.f)); + + PX_ASSERT(jointDatum.dof == 0); + break; + } + default: + break; + + } + } + + //This method supports just one loopJoint + void FeatherstoneArticulation::getKMatrix(ArticulationJointCore* loopJoint, const PxU32 parentIndex, const PxU32 childIndex, PxArticulationCache& cache) + { + PX_UNUSED(loopJoint); + PX_UNUSED(parentIndex); + PX_UNUSED(childIndex); + PX_UNUSED(cache); + + ////initialize all tree links motion subspace matrix + //jcalc(mArticulationData); + + ////linkID is the parent link, ground is the child link so child link is the fix base + //ArticulationLinkData& pLinkDatum = mArticulationData.getLinkData(parentIndex); + + //ArticulationLink& cLink = mArticulationData.getLink(childIndex); + //ArticulationLinkData& cLinkDatum = mArticulationData.getLinkData(childIndex); + // + //ArticulationJointCoreData loopJointDatum; + //loopJointDatum.computeJointDof(loopJoint); + + ////this is constraintForceSubspace in child body space(T) + //SpatialSubspaceMatrix T; + + ////loop joint constraint subspace matrix(T) + //jcalcLoopJointSubspace(loopJoint, loopJointDatum, T); + + //const PxU32 linkCount = mArticulationData.getLinkCount(); + ////set Jacobian matrix to be zero + //PxMemZero(cache.jacobian, sizeof(PxKinematicJacobian) * linkCount); + + ////transform T to world space + //PxTransform& body2World = cLink.bodyCore->body2World; + + //for (PxU32 ind = 0; ind < T.getNumColumns(); ++ind) + //{ + // Cm::SpatialVectorF& column = T[ind]; + // T.setColumn(ind, body2World.rotate(column.top), body2World.rotate(column.bottom)); + //} + + //const Cm::SpatialVectorF& pAccel = pLinkDatum.motionAcceleration; + //const Cm::SpatialVectorF& cAccel = cLinkDatum.motionAcceleration; + + //const Cm::SpatialVectorF& pVel = pLinkDatum.motionVelocity; + //const Cm::SpatialVectorF& cVel = cLinkDatum.motionVelocity; + + //Cm::SpatialVectorF k = (pAccel - cAccel) + pVel.cross(cVel); + //k = T.transposeMultiply(k); + //k = -k; + + //PxU32 i = childIndex; + //PxU32 j = parentIndex; + + //PxU32* index = NULL; + + //while (i != j) + //{ + // if (i > j) + // index = &i; + // else + // index = &j; + + // const PxU32 linkIndex = *index; + + // PxKinematicJacobian* K = cache.jacobian + linkIndex; + + // ArticulationLink& link = mArticulationData.getLink(linkIndex); + + // ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkIndex); + + // SpatialSubspaceMatrix& S = jointDatum.motionMatrix; + + // PxTransform& tBody2World = link.bodyCore->body2World; + + // Cm::SpatialVectorF res; + // for (PxU32 ind = 0; ind < S.getNumColumns(); ++ind) + // { + // Cm::SpatialVectorF& sCol = S[ind]; + + // //transform spatial axis into world space + // sCol.top = tBody2World.rotate(sCol.top); + // sCol.bottom = tBody2World.rotate(sCol.bottom); + + // res = T.transposeMultiply(sCol); + // res = -res; + + // PxReal* kSubMatrix = K->j[ind]; + + // kSubMatrix[0] = res.top.x; kSubMatrix[1] = res.top.y; kSubMatrix[2] = res.top.z; + // kSubMatrix[3] = res.bottom.x; kSubMatrix[4] = res.bottom.y; kSubMatrix[5] = res.bottom.z; + // } + + // //overwrite either i or j to its parent index + // *index = link.parent; + //} + } + + + void FeatherstoneArticulation::getCoefficentMatrix(const PxReal dt, const PxU32 linkID, const PxContactJoint* contactJoints, const PxU32 nbContacts, PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "ArticulationHelper::getCoefficentMatrix() commonInit need to be called first to initialize data!"); + return; + } + + computeArticulatedSpatialInertia(mArticulationData); + + ArticulationLink* links = mArticulationData.getLinks(); + + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxReal* coefficentMatrix = cache.coefficentMatrix; + + const PxU32 elementCount = mArticulationData.getDofs(); + + //zero coefficent matrix + PxMemZero(coefficentMatrix, sizeof(PxReal) * elementCount * nbContacts); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + + for (PxU32 a = 0; a < nbContacts; ++a) + { + + PxJacobianRow row; + contactJoints[a].computeJacobians(&row); + + //impulse lin is contact normal, and ang is raxn. R is body2World, R(t) is world2Body + //| R(t), 0 | + //| R(t)*r, R(t)| + //r is the vector from center of mass to contact point + //p(impluse) = |n| + // |0| + + //transform p(impluse) from work space to the local space of link + ArticulationLink& link = links[linkID]; + PxTransform& body2World = link.bodyCore->body2World; + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + Cm::SpatialVectorF* Z = scratchData.spatialZAVectors; + + //make sure all links' spatial zero acceleration impulse are zero + PxMemZero(Z, sizeof(Cm::SpatialVectorF) * linkCount); + + Cm::SpatialVectorF impl = Cm::SpatialVectorF(body2World.rotateInv(row.linear0), body2World.rotateInv(row.angular0)); + + getZ(linkID, mArticulationData, Z, impl); + + const PxU32 totalDofs = mArticulationData.getDofs(); + + const PxU32 size = sizeof(PxReal) * totalDofs; + + PxU8* tData = reinterpret_cast(allocator->alloc(size * 2)); + + PxReal* jointVelocities = reinterpret_cast(tData); + PxReal* jointAccelerations = reinterpret_cast(tData + size); + //zero joint Velocites + PxMemZero(jointVelocities, size); + + getDeltaVWithDeltaJV(fixBase, linkID, mArticulationData, Z, jointVelocities); + + const PxReal invDt = 1.f / dt; + //calculate joint acceleration due to velocity change + for (PxU32 i = 0; i < totalDofs; ++i) + { + jointAccelerations[i] = jointVelocities[i] * invDt; + } + + //compute individual link's spatial inertia tensor. This is very important + computeSpatialInertia(mArticulationData); + + PxReal* coeCol = &coefficentMatrix[elementCount * a]; + + //this means the joint force calculated by the inverse dynamic + //will be just influenced by joint acceleration change + scratchData.jointVelocities = NULL; + scratchData.externalAccels = NULL; + + //Input + scratchData.jointAccelerations = jointAccelerations; + + //a column of the coefficent matrix is the joint force + scratchData.jointForces = coeCol; + + if (fixBase) + { + inverseDynamic(mArticulationData, PxVec3(0.f), scratchData); + } + else + { + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + } + + allocator->free(tData); + allocator->free(tempMemory); + } + } + + void FeatherstoneArticulation::getImpulseResponseSlowInv(Dy::ArticulationLink* links, + const ArticulationData& data, + PxU32 linkID0_, + const Cm::SpatialVector& impulse0, + Cm::SpatialVector& deltaV0, + PxU32 linkID1_, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV1, + PxReal* jointVelocities) + { + PX_UNUSED(jointVelocities); + PxU32 stack[DY_ARTICULATION_MAX_SIZE]; + Cm::SpatialVectorF Z[DY_ARTICULATION_MAX_SIZE]; + //Cm::SpatialVectorF ZZV[DY_ARTICULATION_MAX_SIZE]; + + PxU32 i0, i1, ic; + + PxU32 linkID0 = linkID0_; + PxU32 linkID1 = linkID1_; + + + const PxTransform& transform0 = data.getLink(linkID0_).bodyCore->body2World; + const PxTransform& transform1 = data.getLink(linkID1_).bodyCore->body2World; + + for (i0 = linkID0, i1 = linkID1; i0 != i1;) // find common path + { + if (i0flags & PxArticulationFlag::eFIX_BASE) + { + Z[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + + //SpatialMatrix inverseArticulatedInertia = data.getLinkData(0).spatialArticulatedInertia.getInverse(); + const SpatialMatrix& inverseArticulatedInertia = data.getBaseInvSpatialArticulatedInertia(); + Cm::SpatialVectorF v = inverseArticulatedInertia * (-Z[0]); + + for (PxU32 index = ic; (index--) > i1;) + v = FeatherstoneArticulation::propagateVelocity(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], jointVelocities, v); + + Cm::SpatialVectorF dv1 = v; + for (PxU32 index = i1; (index--) > i0;) + dv1 = FeatherstoneArticulation::propagateVelocity(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], jointVelocities, dv1); + + Cm::SpatialVectorF dv0 = v; + for (PxU32 index = i0; (index--) > 0;) + dv0 = FeatherstoneArticulation::propagateVelocity(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], jointVelocities, dv0); + + deltaV0.linear = transform0.rotate(dv0.bottom); + deltaV0.angular = transform0.rotate(dv0.top); + + deltaV1.linear = transform1.rotate(dv1.bottom); + deltaV1.angular = transform1.rotate(dv1.top); + } + + void FeatherstoneArticulation::getImpulseSelfResponseInv(const bool fixBase, + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1, + PxReal* jointVelocities) + { + ArticulationLink* links = mArticulationData.getLinks(); + + //transform p(impluse) from work space to the local space of link + ArticulationLink& link = links[linkID1]; + ArticulationLinkData& linkDatum = mArticulationData.getLinkData(linkID1); + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID1); + + + if (link.parent == linkID0) + { + PX_ASSERT(linkID0 == link.parent); + PX_ASSERT(linkID0 < linkID1); + + ArticulationLink& pLink = links[linkID0]; + + //impulse is in world space + const Cm::SpatialVector& imp1 = impulse1; + const Cm::SpatialVector& imp0 = impulse0; + + PxTransform& pBody2World = pLink.bodyCore->body2World; + + Cm::SpatialVectorF pImpulse = Cm::SpatialVectorF(pBody2World.rotateInv(imp0.linear), pBody2World.rotateInv(imp0.angular)); + + PX_ASSERT(linkID0 == link.parent); + + PxTransform& body2World = link.bodyCore->body2World; + + //initialize child link spatial zero acceleration impulse + Cm::SpatialVectorF Z1 = Cm::SpatialVectorF(-body2World.rotateInv(imp1.linear), -body2World.rotateInv(imp1.angular)); + //this calculate parent link spatial zero acceleration impulse + Cm::SpatialVectorF Z0 = FeatherstoneArticulation::propagateImpulse(linkDatum, jointDatum, Z1); + + //in parent space + const Cm::SpatialVectorF impulseDif = pImpulse - Z0; + + Cm::SpatialVectorF delV0(PxVec3(0.f), PxVec3(0.f)); + Cm::SpatialVectorF delV1(PxVec3(0.f), PxVec3(0.f)); + + //calculate velocity change start from the parent link to the root + delV0 = FeatherstoneArticulation::getImpulseResponseWithJ(linkID0, fixBase, mArticulationData, Z, impulseDif, jointVelocities); + + //calculate velocity change for child link + delV1 = FeatherstoneArticulation::propagateVelocity(linkDatum, jointDatum, Z1, jointVelocities, delV0); + + //translate delV0 and delV1 into world space again + deltaV0.linear = pBody2World.rotate(delV0.bottom); + deltaV0.angular = pBody2World.rotate(delV0.top); + deltaV1.linear = body2World.rotate(delV1.bottom); + deltaV1.angular = body2World.rotate(delV1.top); + } + else + { + getImpulseResponseSlowInv(links, mArticulationData, linkID0, impulse0, deltaV0, linkID1,impulse1, deltaV1, jointVelocities ); + } + } + + Cm::SpatialVectorF FeatherstoneArticulation::getImpulseResponseInv( + const bool fixBase, const PxU32 linkID, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + PxReal* jointVelocities) + { + + //impulse lin is contact normal, and ang is raxn. R is body2World, R(t) is world2Body + //| R(t), 0 | + //| R(t)*r, R(t)| + //r is the vector from center of mass to contact point + //p(impluse) = |n| + // |0| + + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + const PxU32 linkCount = mArticulationData.getLinkCount(); + + //transform p(impluse) from work space to the local space of link + ArticulationLink& link = links[linkID]; + PxTransform& body2World = link.bodyCore->body2World; + + //make sure all links' spatial zero acceleration impulse are zero + PxMemZero(Z, sizeof(Cm::SpatialVectorF) * linkCount); + + Z[linkID] = Cm::SpatialVectorF(-body2World.rotateInv(impulse.linear), -body2World.rotateInv(impulse.angular)); + + for (PxU32 i = linkID; i; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& tJointDatum = jointData[i]; + Z[tLink.parent] = propagateImpulse(tLinkDatum, tJointDatum, Z[i]); + } + + //set velocity change of the root link to be zero + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + if (!fixBase) + deltaV = mArticulationData.mBaseInvSpatialArticulatedInertia * (-Z[0]); + + for (ArticulationBitField i = links[linkID].pathToRoot - 1; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = linkData[index]; + ArticulationJointCoreData& tJointDatum = jointData[index]; + + PxReal* jVelocity = &jointVelocities[tJointDatum.jointOffset]; + + PX_ASSERT(links[index].parent < index); + deltaV = propagateVelocity(tLinkDatum, tJointDatum, Z[index], jVelocity, deltaV); + } + + return deltaV; + + } + + + void FeatherstoneArticulation::getCoefficentMatrixWithLoopJoints(ArticulationLoopConstraint* lConstraints, const PxU32 nbConstraints, PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "ArticulationHelper::getCoefficentMatrix() commonInit need to be called first to initialize data!"); + return; + } + + computeArticulatedSpatialInertia(mArticulationData); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxReal* coefficentMatrix = cache.coefficentMatrix; + + const PxU32 elementCount = mArticulationData.getDofs(); + + //zero coefficent matrix + PxMemZero(coefficentMatrix, sizeof(PxReal) * elementCount * nbConstraints); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + Cm::SpatialVectorF* Z = scratchData.spatialZAVectors; + const PxU32 totalDofs = mArticulationData.getDofs(); + + const PxU32 size = sizeof(PxReal) * totalDofs; + + PxU8* tData = reinterpret_cast(allocator->alloc(size * 2)); + + const PxReal invDt = 1.f / mArticulationData.getDt(); + PxReal* jointVelocities = reinterpret_cast(tData); + PxReal* jointAccelerations = reinterpret_cast(tData + size); + + for (PxU32 a = 0; a < nbConstraints; ++a) + { + ArticulationLoopConstraint& lConstraint = lConstraints[a]; + Constraint* aConstraint = lConstraint.constraint; + + Px1DConstraint rows[MAX_CONSTRAINT_ROWS]; + + PxMemZero(rows, sizeof(Px1DConstraint)*MAX_CONSTRAINT_ROWS); + + for (PxU32 i = 0; ibody0) + body2World0 = aConstraint->bodyCore0->body2World; + + if (aConstraint->body1) + body2World1 = aConstraint->bodyCore1->body2World; + + PxVec3 body0WorldOffset(0.f); + PxVec3 ra, rb; + PxConstraintInvMassScale invMassScales; + PxU32 constraintCount = (*aConstraint->solverPrep)(rows, + body0WorldOffset, + MAX_CONSTRAINT_ROWS, + invMassScales, + aConstraint->constantBlock, + body2World0, body2World1, !!(aConstraint->flags & PxConstraintFlag::eENABLE_EXTENDED_LIMITS), ra, rb); + + const PxU32 linkIndex0 = lConstraint.linkIndex0; + const PxU32 linkIndex1 = lConstraint.linkIndex1; + + //zero joint Velocites + PxMemZero(jointVelocities, size); + + for (PxU32 j = 0; j < constraintCount; ++j) + { + Px1DConstraint& row = rows[j]; + + if (linkIndex0 != 0x80000000 && linkIndex1 != 0x80000000) + { + const bool flip = linkIndex0 > linkIndex1; + + Cm::SpatialVector impulse0(row.linear0, row.angular0); + Cm::SpatialVector impulse1(row.linear1, row.angular1); + + Cm::SpatialVector deltaV0, deltaV1; + + if (flip) + { + getImpulseSelfResponseInv(fixBase, linkIndex1, linkIndex0, Z, impulse1, impulse0, + deltaV1, deltaV0, jointVelocities); + } + else + { + getImpulseSelfResponseInv(fixBase, linkIndex0, linkIndex1, Z, impulse0, impulse1, + deltaV0, deltaV1, jointVelocities); + } + } + else + { + if (linkIndex0 == 0x80000000) + { + Cm::SpatialVector impulse1(row.linear1, row.angular1); + getImpulseResponseInv(fixBase, linkIndex1, Z, impulse1, jointVelocities); + } + else + { + Cm::SpatialVector impulse0(row.linear0, row.angular0); + getImpulseResponseInv(fixBase, linkIndex0, Z, impulse0, jointVelocities); + } + } + } + + + //calculate joint acceleration due to velocity change + for (PxU32 i = 0; i < totalDofs; ++i) + { + jointAccelerations[i] = jointVelocities[i] * invDt; + } + + //reset spatial inertia + computeSpatialInertia(mArticulationData); + + PxReal* coeCol = &coefficentMatrix[elementCount * a]; + + //this means the joint force calculated by the inverse dynamic + //will be just influenced by joint acceleration change + scratchData.jointVelocities = NULL; + scratchData.externalAccels = NULL; + + //Input + scratchData.jointAccelerations = jointAccelerations; + + //a column of the coefficent matrix is the joint force + scratchData.jointForces = coeCol; + + if (fixBase) + { + inverseDynamic(mArticulationData, PxVec3(0.f), scratchData); + } + else + { + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + } + + allocator->free(tData); + allocator->free(tempMemory); + } + } + + void FeatherstoneArticulation::constraintPrep(ArticulationLoopConstraint* lConstraints, + const PxU32 nbJoints, Cm::SpatialVectorF* Z, PxSolverConstraintPrepDesc& prepDesc, + PxSolverBody& sBody, PxSolverBodyData& sBodyData, PxSolverConstraintDesc* descs, + PxConstraintAllocator& allocator) + { + const PxReal dt = mArticulationData.getDt(); + const PxReal invDt = 1.f / dt; + //constraint prep + for (PxU32 a = 0; a < nbJoints; ++a) + { + ArticulationLoopConstraint& lConstraint = lConstraints[a]; + Constraint* aConstraint = lConstraint.constraint; + + PxSolverConstraintDesc& desc = descs[a]; + prepDesc.desc = &desc; + prepDesc.linBreakForce = aConstraint->linBreakForce; + prepDesc.angBreakForce = aConstraint->angBreakForce; + prepDesc.writeback = &mContext->getConstraintWriteBackPool()[aConstraint->index]; + prepDesc.disablePreprocessing = !!(aConstraint->flags & PxConstraintFlag::eDISABLE_PREPROCESSING); + prepDesc.improvedSlerp = !!(aConstraint->flags & PxConstraintFlag::eIMPROVED_SLERP); + prepDesc.driveLimitsAreForces = !!(aConstraint->flags & PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES); + prepDesc.extendedLimits = !!(aConstraint->flags & PxConstraintFlag::eENABLE_EXTENDED_LIMITS); + prepDesc.minResponseThreshold = aConstraint->minResponseThreshold; + + Px1DConstraint rows[MAX_CONSTRAINT_ROWS]; + + PxMemZero(rows, sizeof(Px1DConstraint)*MAX_CONSTRAINT_ROWS); + + for (PxU32 i = 0; i < MAX_CONSTRAINT_ROWS; i++) + { + Px1DConstraint& c = rows[i]; + //Px1DConstraintInit(c); + c.minImpulse = -PX_MAX_REAL; + c.maxImpulse = PX_MAX_REAL; + } + + prepDesc.mInvMassScales.linear0 = prepDesc.mInvMassScales.linear1 = prepDesc.mInvMassScales.angular0 = prepDesc.mInvMassScales.angular1 = 1.f; + + PxTransform body2World0 = PxTransform(PxIdentity); + PxTransform body2World1 = PxTransform(PxIdentity); + + if (aConstraint->body0) + body2World0 = aConstraint->bodyCore0->body2World; + + if (aConstraint->body1) + body2World1 = aConstraint->bodyCore1->body2World; + + + PxVec3 body0WorldOffset(0.f); + PxVec3 ra, rb; + PxConstraintInvMassScale invMassScales; + PxU32 constraintCount = (*aConstraint->solverPrep)(rows, + body0WorldOffset, + MAX_CONSTRAINT_ROWS, + invMassScales, + aConstraint->constantBlock, + body2World0, body2World1, !!(aConstraint->flags & PxConstraintFlag::eENABLE_EXTENDED_LIMITS), + ra, rb); + + + prepDesc.body0WorldOffset = body0WorldOffset; + prepDesc.bodyFrame0 = body2World0; + prepDesc.bodyFrame1 = body2World1; + prepDesc.numRows = constraintCount; + prepDesc.rows = rows; + + const PxU32 linkIndex0 = lConstraint.linkIndex0; + const PxU32 linkIndex1 = lConstraint.linkIndex1; + + + if (linkIndex0 != 0x80000000 && linkIndex1 != 0x80000000) + { + desc.articulationA = this; + desc.articulationB = this; + desc.linkIndexA = PxU16(linkIndex0); + desc.linkIndexB = PxU16(linkIndex1); + + desc.bodyA = reinterpret_cast(this); + desc.bodyB = reinterpret_cast(this); + + prepDesc.bodyState0 = PxSolverConstraintPrepDescBase::eARTICULATION; + prepDesc.bodyState1 = PxSolverConstraintPrepDescBase::eARTICULATION; + + } + else if (linkIndex0 == 0x80000000) + { + desc.articulationA = NULL; + desc.articulationB = this; + + desc.linkIndexA = 0xffff; + desc.linkIndexB = PxU16(linkIndex1); + + desc.bodyA = &sBody; + desc.bodyB = reinterpret_cast(this); + + prepDesc.bodyState0 = PxSolverConstraintPrepDescBase::eSTATIC_BODY; + prepDesc.bodyState1 = PxSolverConstraintPrepDescBase::eARTICULATION; + } + else if (linkIndex1 == 0x80000000) + { + desc.articulationA = this; + desc.articulationB = NULL; + + desc.linkIndexA = PxU16(linkIndex0); + desc.linkIndexB = 0xffff; + + desc.bodyA = reinterpret_cast(this); + desc.bodyB = &sBody; + + prepDesc.bodyState0 = PxSolverConstraintPrepDescBase::eARTICULATION; + prepDesc.bodyState1 = PxSolverConstraintPrepDescBase::eSTATIC_BODY; + + } + + prepDesc.body0 = desc.bodyA; + prepDesc.body1 = desc.bodyB; + prepDesc.data0 = &sBodyData; + prepDesc.data1 = &sBodyData; + + ConstraintHelper::setupSolverConstraint(prepDesc, allocator, dt, invDt, Z); + } + + } + + class BlockBasedAllocator + { + struct AllocationPage + { + static const PxU32 PageSize = 32 * 1024; + PxU8 mPage[PageSize]; + + PxU32 currentIndex; + + AllocationPage() : currentIndex(0) {} + + PxU8* allocate(const PxU32 size) + { + PxU32 alignedSize = (size + 15)&(~15); + if ((currentIndex + alignedSize) < PageSize) + { + PxU8* ret = &mPage[currentIndex]; + currentIndex += alignedSize; + return ret; + } + return NULL; + } + }; + + AllocationPage* currentPage; + + physx::shdfnd::Array mAllocatedBlocks; + PxU32 mCurrentIndex; + + public: + BlockBasedAllocator() : currentPage(NULL), mCurrentIndex(0) + { + } + + virtual PxU8* allocate(const PxU32 byteSize) + { + if (currentPage) + { + PxU8* data = currentPage->allocate(byteSize); + if (data) + return data; + } + + if (mCurrentIndex < mAllocatedBlocks.size()) + { + currentPage = mAllocatedBlocks[mCurrentIndex++]; + currentPage->currentIndex = 0; + return currentPage->allocate(byteSize); + } + currentPage = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(AllocationPage), PX_DEBUG_EXP("AllocationPage")), AllocationPage)(); + mAllocatedBlocks.pushBack(currentPage); + mCurrentIndex = mAllocatedBlocks.size(); + + return currentPage->allocate(byteSize); + } + + void release() { for (PxU32 a = 0; a < mAllocatedBlocks.size(); ++a) PX_FREE(mAllocatedBlocks[a]); mAllocatedBlocks.clear(); currentPage = NULL; mCurrentIndex = 0; } + + void reset() { currentPage = NULL; mCurrentIndex = 0; } + + virtual ~BlockBasedAllocator() + { + release(); + } + }; + + class ArticulationBlockAllocator : public PxConstraintAllocator + { + BlockBasedAllocator mConstraintAllocator; + BlockBasedAllocator mFrictionAllocator[2]; + + PxU32 currIdx; + + public: + + ArticulationBlockAllocator() : currIdx(0) + { + } + + virtual ~ArticulationBlockAllocator() {} + + virtual PxU8* reserveConstraintData(const PxU32 size) + { + return reinterpret_cast(mConstraintAllocator.allocate(size)); + } + + virtual PxU8* reserveFrictionData(const PxU32 byteSize) + { + return reinterpret_cast(mFrictionAllocator[currIdx].allocate(byteSize)); + } + + void release() { currIdx = 1 - currIdx; mConstraintAllocator.release(); mFrictionAllocator[currIdx].release(); } + + PX_NOCOPY(ArticulationBlockAllocator) + + }; + + void solveExt1D(const PxSolverConstraintDesc& desc, SolverContext& cache); + void writeBack1D(const PxSolverConstraintDesc& desc, SolverContext&, PxSolverBodyData&, PxSolverBodyData&); + void conclude1D(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/); + void clearExt1D(const PxSolverConstraintDesc& desc, SolverContext& cache); + + bool FeatherstoneArticulation::getLambda(ArticulationLoopConstraint* lConstraints, const PxU32 nbJoints, + PxArticulationCache& cache, PxArticulationCache& initialState, + const PxReal* jointTorque, const PxVec3& gravity, const PxU32 maxIter) + { + const PxReal dt = mArticulationData.getDt(); + const PxReal invDt = 1.f / dt; + const PxU32 totalDofs = mArticulationData.getDofs(); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + ArticulationBlockAllocator bAlloc; + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + Cm::SpatialVectorF* Z = reinterpret_cast(allocator->alloc(sizeof(Cm::SpatialVectorF) * linkCount, true)); + Cm::SpatialVectorF* deltaV = reinterpret_cast(allocator->alloc(sizeof(Cm::SpatialVectorF) * linkCount, true)); + + PxReal* prevoiusLambdas =reinterpret_cast(allocator->alloc(sizeof(PxReal)*nbJoints * 2, true)); + PxReal* lambdas = cache.lambda; + + //this is the joint force changed caused by contact force based on impulse strength is 1 + PxReal* J = cache.coefficentMatrix; + + PxSolverBody staticSolverBody; + PxMemZero(&staticSolverBody, sizeof(PxSolverBody)); + PxSolverBodyData staticSolverBodyData; + PxMemZero(&staticSolverBodyData, sizeof(PxSolverBodyData)); + staticSolverBodyData.maxContactImpulse = PX_MAX_F32; + staticSolverBodyData.penBiasClamp = -PX_MAX_F32; + staticSolverBodyData.body2World = PxTransform(PxIdentity); + + Dy::SolverContext context; + context.Z = Z; + context.deltaV = deltaV; + context.doFriction = false; + + + PxSolverConstraintDesc* desc = reinterpret_cast(allocator->alloc(sizeof(PxSolverConstraintDesc) * nbJoints, true)); + ArticulationSolverDesc artiDesc; + + PxSolverConstraintDesc* constraintDescs = reinterpret_cast(allocator->alloc(sizeof(PxSolverConstraintDesc) * mArticulationData.getLinkCount()-1, true)); + + //run forward dynamic to calculate the lamba + + artiDesc.articulation = this; + PxU32 acCount = 0; + computeUnconstrainedVelocities(artiDesc, dt, bAlloc, constraintDescs, acCount, + gravity, 0, Z, deltaV); + + ScratchData scratchData; + scratchData.motionVelocities = mArticulationData.getMotionVelocities(); + scratchData.motionAccelerations = mArticulationData.getMotionAccelerations(); + scratchData.coriolisVectors = mArticulationData.getCorioliseVectors(); + scratchData.spatialZAVectors = mArticulationData.getSpatialZAVectors(); + scratchData.jointAccelerations = mArticulationData.getJointAccelerations(); + scratchData.jointVelocities = mArticulationData.getJointVelocities(); + scratchData.jointPositions = mArticulationData.getJointPositions(); + scratchData.jointForces = mArticulationData.getJointForces(); + scratchData.externalAccels = mArticulationData.getExternalAccelerations(); + + //prepare constraint data + PxSolverConstraintPrepDesc prepDesc; + constraintPrep(lConstraints, nbJoints, Z, prepDesc, staticSolverBody, + staticSolverBodyData, desc, bAlloc); + + for (PxU32 i = 0; i < nbJoints; ++i) + { + prevoiusLambdas[i] = PX_MAX_F32; + } + + bool found = true; + + for (PxU32 iter = 0; iter < maxIter; ++iter) + { + found = true; + for (PxU32 i = 0; i < nbJoints; ++i) + { + clearExt1D(desc[i], context); + } + + //solve + for (PxU32 itr = 0; itr < 4; itr++) + { + for (PxU32 i = 0; i < nbJoints; ++i) + { + solveExt1D(desc[i], context); + } + } + for (PxU32 i = 0; i < nbJoints; ++i) + { + conclude1D(desc[i], context); + } + + PxcFsFlushVelocity(*this, deltaV); + + for (PxU32 i = 0; i < nbJoints; ++i) + { + solveExt1D(desc[i], context); + writeBack1D(desc[i], context, staticSolverBodyData, staticSolverBodyData); + } + + PxReal eps = 1e-5f; + for (PxU32 i = 0; i < nbJoints; ++i) + { + Dy::Constraint* constraint = lConstraints->constraint; + + Dy::ConstraintWriteback& solverOutput = mContext->getConstraintWriteBackPool()[constraint->index]; + PxVec3 linearForce = solverOutput.linearImpulse * invDt; + + //linear force is normalize so lambda is the magnitude of linear force + lambdas[i] = linearForce.magnitude() * dt; + + const PxReal dif = PxAbs(prevoiusLambdas[i] - lambdas[i]); + if (dif > eps) + found = false; + + prevoiusLambdas[i] = lambdas[i]; + } + + if (found) + break; + + //joint force + PxReal* jf3 = cache.jointForce; + + //zero the joint force buffer + PxMemZero(jf3, sizeof(PxReal)*totalDofs); + + for (PxU32 colInd = 0; colInd < nbJoints; ++colInd) + { + PxReal* col = &J[colInd * totalDofs]; + + for (PxU32 j = 0; j < totalDofs; ++j) + { + jf3[j] += col[j] * lambdas[colInd]; + } + } + + //jointTorque is M(q)*qddot + C(q,qdot)t - g(q) + //jointTorque - J*lambda. + for (PxU32 j = 0; j < totalDofs; ++j) + { + jf3[j] = jointTorque[j] - jf3[j]; + } + + //reset all joint velocities/ + applyCache(initialState, PxArticulationCache::eALL); + + //copy constraint torque to internal data + applyCache(cache, PxArticulationCache::eFORCE); + + mArticulationData.init(); + + computeLinkVelocities(mArticulationData, scratchData); + computeZ(mArticulationData, gravity, scratchData); + computeArticulatedSpatialZ(mArticulationData, scratchData); + computeLinkAcceleration(mArticulationData, scratchData); + + //zero zero acceleration vector in the articulation data so that we can use this buffer to accumulated + //impulse for the contacts/constraints in the PGS/TGS solvers + PxMemZero(mArticulationData.getSpatialZAVectors(), sizeof(Cm::SpatialVectorF) * linkCount); + } + + allocator->free(constraintDescs); + allocator->free(prevoiusLambdas); + allocator->free(Z); + allocator->free(deltaV); + allocator->free(desc); + bAlloc.release(); + + //roll back to the current stage + applyCache(initialState, PxArticulationCache::eALL); + + return found; + + } + + //i is the current link ID, we need to compute the row/column related to the joint i with all the other joints + PxU32 computeHi(ArticulationData& data, const PxU32 i, PxReal* massMatrix, Cm::SpatialVectorF* f) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + + ArticulationJointCoreData& jointDatum = data.getJointData(i); + + const PxU32 totalDofs = data.getDofs(); + + //Hii + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const PxU32 row = (jointDatum.jointOffset + ind)* totalDofs; + const Cm::SpatialVectorF& tf = f[ind]; + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + const PxU32 col = jointDatum.jointOffset + ind2; + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind2]; + massMatrix[row + col] = sa.innerProduct(tf); + } + } + + PxU32 j = i; + + ArticulationLink* jLink = &links[j]; + ArticulationLinkData* jLinkDatum = &linkData[j]; + while (jLink->parent != 0) + { + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + f[ind] = jLinkDatum->childToParent * f[ind]; + } + + //assign j to the parent link + j = jLink->parent; + jLink = &links[j]; + jLinkDatum = &linkData[j]; + + //Hij + ArticulationJointCoreData& pJointDatum = data.getJointData(j); + + for (PxU32 ind = 0; ind < pJointDatum.dof; ++ind) + { + Cm::SpatialVectorF& sa = pJointDatum.motionMatrix[ind]; + const PxU32 col = pJointDatum.jointOffset + ind; + + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + const PxU32 row = (jointDatum.jointOffset + ind2)* totalDofs; + + Cm::SpatialVectorF& fcol = f[ind2]; + + massMatrix[row + col] = fcol.innerProduct(sa); + } + } + + //Hji = transpose(Hij) + { + for (PxU32 ind = 0; ind < pJointDatum.dof; ++ind) + { + const PxU32 pRow = (pJointDatum.jointOffset + ind)* totalDofs; + const PxU32 col = pJointDatum.jointOffset + ind; + + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + const PxU32 pCol = jointDatum.jointOffset + ind2; + const PxU32 row = (jointDatum.jointOffset + ind2) * totalDofs; + + massMatrix[pRow + pCol] = massMatrix[row + col]; + } + } + } + + } + return j; + } + + void FeatherstoneArticulation::calculateHFixBase(PxArticulationCache& cache) + { + const PxU32 elementCount = mArticulationData.getDofs(); + + PxReal* massMatrix = cache.massMatrix; + + PxMemZero(massMatrix, sizeof(PxReal) * elementCount * elementCount); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + + const PxU32 startIndex = PxU32(linkCount - 1); + + Dy::SpatialMatrix* compositeSpatialInertia = reinterpret_cast(allocator->alloc(sizeof(Dy::SpatialMatrix) * linkCount)); + + //initialize composite spatial inertial + initCompositeSpatialInertia(mArticulationData, compositeSpatialInertia); + + Cm::SpatialVectorF F[6]; + for (PxU32 i = startIndex; i > 0; --i) + { + ArticulationLink& link = links[i]; + ArticulationLinkData& linkDatum = linkData[i]; + + Dy::SpatialMatrix cSpatialInertia = compositeSpatialInertia[i]; + //transform current link's spatial inertia to parent's space + transformInertia(linkDatum.childToParent, cSpatialInertia); + + //compute parent's composite spatial inertia + compositeSpatialInertia[link.parent] += cSpatialInertia; + + Dy::SpatialMatrix& tSpatialInertia = compositeSpatialInertia[i]; + + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(i); + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + F[ind] = tSpatialInertia* sa; + } + + //Hii, Hij, Hji + computeHi(mArticulationData, i, massMatrix, F); + } + + allocator->free(compositeSpatialInertia); + } + + + void FeatherstoneArticulation::calculateHFloatingBase(PxArticulationCache& cache) + { + const PxU32 elementCount = mArticulationData.getDofs(); + + PxReal* massMatrix = cache.massMatrix; + + PxMemZero(massMatrix, sizeof(PxReal) * elementCount * elementCount); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + + const PxU32 startIndex = PxU32(linkCount - 1); + + Dy::SpatialMatrix* compositeSpatialInertia = reinterpret_cast(allocator->alloc(sizeof(Dy::SpatialMatrix) * linkCount)); + Cm::SpatialVectorF* F = reinterpret_cast(allocator->alloc(sizeof(Cm::SpatialVectorF) * elementCount)); + + //initialize composite spatial inertial + initCompositeSpatialInertia(mArticulationData, compositeSpatialInertia); + + for (PxU32 i = startIndex; i > 0; --i) + { + ArticulationLink& link = links[i]; + ArticulationLinkData& linkDatum = linkData[i]; + + Dy::SpatialMatrix cSpatialInertia = compositeSpatialInertia[i]; + //transform current link's spatial inertia to parent's space + transformInertia(linkDatum.childToParent, cSpatialInertia); + + //compute parent's composite spatial inertia + compositeSpatialInertia[link.parent] += cSpatialInertia; + + Dy::SpatialMatrix& tSpatialInertia = compositeSpatialInertia[i]; + + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(i); + + Cm::SpatialVectorF* f = &F[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + f[ind] = tSpatialInertia* sa; + } + + //Hii, Hij, Hji + const PxU32 j = computeHi(mArticulationData, i, massMatrix, f); + + //transform F to the base link space + ArticulationLinkData& fDatum = linkData[j]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + f[ind] = fDatum.childToBase * f[ind]; + } + } + + //Ib = base link composite inertia tensor + //compute transpose(F) * inv(Ib) *F + Dy::SpatialMatrix invI0 = compositeSpatialInertia[0].invertInertia(); + + //H - transpose(F) * inv(Ib) * F; + for (PxU32 row = 0; row < elementCount; ++row) + { + const Cm::SpatialVectorF& f = F[row]; + for (PxU32 col = 0; col < elementCount; ++col) + { + const Cm::SpatialVectorF invIf = invI0 * F[col]; + const PxReal v = f.innerProduct(invIf); + const PxU32 index = row * elementCount + col; + massMatrix[index] = massMatrix[index] - v; + } + } + + allocator->free(compositeSpatialInertia); + allocator->free(F); + + } + + //calcualte a single column of H, jointForce is equal to a single column of H + void FeatherstoneArticulation::calculateMassMatrixColInv(ScratchData& scratchData) + { + const PxU32 linkCount = mArticulationData.getLinkCount(); + + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; + + //Input + PxReal* jointAccelerations = scratchData.jointAccelerations; + + //set base link motion acceleration to be zero because H should + //be just affected by joint position/link position + motionAccelerations[0] = Cm::SpatialVectorF::Zero(); + spatialZAForces[0] = Cm::SpatialVectorF::Zero(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = mArticulationData.getLink(linkID); + ArticulationLinkData& linkDatum = mArticulationData.getLinkData(linkID); + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + + SpatialTransform p2c = linkDatum.childToParent.getTranspose(); + + //parent motion accelerations into child space + Cm::SpatialVectorF accel = p2c * motionAccelerations[link.parent]; + + const PxReal* jAcceleration = &jointAccelerations[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + accel += jointDatum.motionMatrix[ind] * jAcceleration[ind]; + } + + motionAccelerations[linkID] = accel; + + spatialZAForces[linkID] = linkDatum.spatialArticulatedInertia * accel; + } + + computeGeneralizedForceInv(mArticulationData, scratchData); + + } + + void FeatherstoneArticulation::getGeneralizedMassMatrixCRB(PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "ArticulationHelper::getGeneralizedMassMatrix() commonInit need to be called first to initialize data!"); + return; + } + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + if (fixBase) + { + calculateHFixBase(cache); + } + else + { + calculateHFloatingBase(cache); + } + + } + + void FeatherstoneArticulation::getGeneralizedMassMatrix( PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "ArticulationHelper::getGeneralizedMassMatrix() commonInit need to be called first to initialize data!"); + return; + } + + + //calculate each column for mass matrix + PxReal* massMatrix = cache.massMatrix; + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + const PxU32 elementCount = mArticulationData.getDofs(); + + const PxU32 size = sizeof(PxReal) * elementCount; + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + PxReal* jointAccelerations = reinterpret_cast(allocator->alloc(size)); + + scratchData.jointAccelerations = jointAccelerations; + scratchData.jointVelocities = NULL; + scratchData.externalAccels = NULL; + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + //initialize jointAcceleration to be zero + PxMemZero(jointAccelerations, size); + + for (PxU32 colInd = 0; colInd < elementCount; ++colInd) + { + PxReal* col = &massMatrix[colInd * elementCount]; + + scratchData.jointForces = col; + + //set joint acceleration 1 in the col + 1 and zero elsewhere + jointAccelerations[colInd] = 1; + + if (fixBase) + { + //jointAcceleration is Q, HQ = ID(model, qdot, Q). + calculateMassMatrixColInv(scratchData); + } + else + { + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + } + + //reset joint acceleration to be zero + jointAccelerations[colInd] = 0; + } + + allocator->free(jointAccelerations); + allocator->free(tempMemory); + } + + + +} //namespace Dy + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp new file mode 100644 index 000000000..35bbfeac5 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp @@ -0,0 +1,298 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxvConfig.h" +#include "DyCorrelationBuffer.h" +#include "PxsMaterialManager.h" +#include "PsUtilities.h" +#include "foundation/PxBounds3.h" + +using namespace physx; +using namespace Gu; + +namespace physx +{ + +namespace Dy +{ + +namespace +{ +PX_FORCE_INLINE void initContactPatch(CorrelationBuffer::ContactPatchData& patch, PxU16 index, PxReal restitution, PxReal staticFriction, PxReal dynamicFriction, + PxU8 flags) +{ + patch.start = index; + patch.count = 1; + patch.next = 0; + patch.flags = flags; + patch.restitution = restitution; + patch.staticFriction = staticFriction; + patch.dynamicFriction = dynamicFriction; +} + +PX_FORCE_INLINE void initFrictionPatch(FrictionPatch& p, const PxVec3& worldNormal, const PxTransform& body0Pose, const PxTransform& body1Pose, + PxReal restitution, PxReal staticFriction, PxReal dynamicFriction, PxU8 materialFlags) +{ + p.body0Normal = body0Pose.rotateInv(worldNormal); + p.body1Normal = body1Pose.rotateInv(worldNormal); + p.relativeQuat = body0Pose.q.getConjugate() * body1Pose.q; + p.anchorCount = 0; + p.broken = 0; + p.staticFriction = staticFriction; + p.dynamicFriction = dynamicFriction; + p.restitution = restitution; + p.materialFlags = materialFlags; +} +} + + +bool createContactPatches(CorrelationBuffer& fb, const Gu::ContactPoint* cb, PxU32 contactCount, PxReal normalTolerance) +{ + + // PT: this rewritten version below doesn't have LHS + + PxU32 contactPatchCount = fb.contactPatchCount; + if(contactPatchCount == Gu::ContactBuffer::MAX_CONTACTS) + return false; + if(contactCount>0) + { + CorrelationBuffer::ContactPatchData* currentPatchData = fb.contactPatches + contactPatchCount; + const Gu::ContactPoint* PX_RESTRICT contacts = cb; + + PxU8 count=1; + + initContactPatch(fb.contactPatches[contactPatchCount++], Ps::to16(0), contacts[0].restitution, + contacts[0].staticFriction, contacts[0].dynamicFriction, PxU8(contacts[0].materialFlags)); + + PxBounds3 bounds(contacts[0].point, contacts[0].point); + + PxU32 patchIndex = 0; + + for (PxU32 i = 1; i=normalTolerance) + { + bounds.include(curContact.point); + count++; + } + else + { + if(contactPatchCount == Gu::ContactBuffer::MAX_CONTACTS) + return false; + patchIndex = i; + currentPatchData->count = count; + count = 1; + currentPatchData->patchBounds = bounds; + currentPatchData = fb.contactPatches + contactPatchCount; + + initContactPatch(fb.contactPatches[contactPatchCount++], Ps::to16(i), curContact.restitution, + curContact.staticFriction, curContact.dynamicFriction, PxU8(curContact.materialFlags)); + + bounds = PxBounds3(curContact.point, curContact.point); + } + } + if(count!=1) + currentPatchData->count = count; + + currentPatchData->patchBounds = bounds; + } + fb.contactPatchCount = contactPatchCount; + return true; +} + +bool correlatePatches(CorrelationBuffer& fb, + const Gu::ContactPoint* cb, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxReal normalTolerance, + PxU32 startContactPatchIndex, + PxU32 startFrictionPatchIndex) +{ + bool overflow = false; + PxU32 frictionPatchCount = fb.frictionPatchCount; + + for(PxU32 i=startContactPatchIndex;i= frictionPatchDiagonalSq) + continue; + + fp.anchorCount = 0; + } + + PxVec3 worldAnchors[2]; + PxU16 anchorCount = 0; + PxReal pointDistSq = 0.0f, dist0, dist1; + + // if we have an anchor already, keep it + if(fp.anchorCount == 1) + { + worldAnchors[anchorCount++] = bodyFrame0.transform(fp.body0Anchors[0]); + } + + for(PxU32 patch = fb.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = fb.contactPatches[patch].next) + { + CorrelationBuffer::ContactPatchData& cp = fb.contactPatches[patch]; + for(PxU16 j=0;j (correlationDistance * correlationDistance)) + { + fb.contactID[i][1] = PxU16(cp.start+j); + worldAnchors[1] = worldPoint; + anchorCount++; + } + break; + default: //case 2 + dist0 = (worldPoint-worldAnchors[0]).magnitudeSquared(); + dist1 = (worldPoint-worldAnchors[1]).magnitudeSquared(); + if (dist0 > dist1) + { + if(dist0 > pointDistSq) + { + fb.contactID[i][1] = PxU16(cp.start+j); + worldAnchors[1] = worldPoint; + pointDistSq = dist0; + } + } + else if (dist1 > pointDistSq) + { + fb.contactID[i][0] = PxU16(cp.start+j); + worldAnchors[0] = worldPoint; + pointDistSq = dist1; + } + } + } + } + } + + //PX_ASSERT(anchorCount > 0); + + // add the new anchor(s) to the patch + for(PxU32 j = fp.anchorCount; j < anchorCount; j++) + { + fp.body0Anchors[j] = bodyFrame0.transformInv(worldAnchors[j]); + fp.body1Anchors[j] = bodyFrame1.transformInv(worldAnchors[j]); + } + + // the block contact solver always reads at least one anchor per patch for performance reasons even if there are no valid patches, + // so we need to initialize this in the unexpected case that we have no anchors + + if(anchorCount==0) + fp.body0Anchors[0] = fp.body1Anchors[0] = PxVec3(0); + + fp.anchorCount = anchorCount; + } +} + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h new file mode 100644 index 000000000..5bf28a293 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h @@ -0,0 +1,83 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_FRICTIONPATCH_H +#define PXC_FRICTIONPATCH_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec3.h" +#include "PxvConfig.h" + +namespace physx +{ + +namespace Dy +{ + +struct FrictionPatch +{ + PxU8 broken; // PT: must be first byte of struct, see "frictionBrokenWritebackByte" + PxU8 materialFlags; + PxU16 anchorCount; + PxReal restitution; + PxReal staticFriction; + PxReal dynamicFriction; + PxVec3 body0Normal; + PxVec3 body1Normal; + PxVec3 body0Anchors[2]; + PxVec3 body1Anchors[2]; + PxQuat relativeQuat; + + PX_FORCE_INLINE void operator = (const FrictionPatch& other) + { + broken = other.broken; + materialFlags = other.materialFlags; + anchorCount = other.anchorCount; + body0Normal = other.body0Normal; + body1Normal = other.body1Normal; + body0Anchors[0] = other.body0Anchors[0]; + body0Anchors[1] = other.body0Anchors[1]; + body1Anchors[0] = other.body1Anchors[0]; + body1Anchors[1] = other.body1Anchors[1]; + relativeQuat = other.relativeQuat; + restitution = other.restitution; + staticFriction = other.staticFriction; + dynamicFriction = other.dynamicFriction; + } +}; + +//PX_COMPILE_TIME_ASSERT(sizeof(FrictionPatch)==80); + +} + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h new file mode 100644 index 000000000..461e9b9bb --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h @@ -0,0 +1,128 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_FRICTIONPATCHPOOL_H +#define PXC_FRICTIONPATCHPOOL_H + +#include "foundation/PxSimpleTypes.h" +#include "PxvConfig.h" +#include "PsMutex.h" +#include "PsArray.h" + +// Each narrow phase thread has an input stream of friction patches from the +// previous frame and an output stream of friction patches which will be +// saved for next frame. The patches persist for exactly one frame at which +// point they get thrown away. + + +// There is a stream pair per thread. A contact callback reserves space +// for its friction patches and gets a cookie in return that can stash +// for next frame. Cookies are valid for one frame only. +// +// note that all friction patches reserved are guaranteed to be contiguous; +// this might turn out to be a bit inefficient if we often have a large +// number of friction patches + +#include "PxcNpMemBlockPool.h" + +namespace physx +{ + +class FrictionPatchStreamPair +{ +public: + FrictionPatchStreamPair(PxcNpMemBlockPool& blockPool); + + // reserve can fail and return null. Read should never fail + template + FrictionPatch* reserve(const PxU32 size); + + template + const FrictionPatch* findInputPatches(const PxU8* ptr) const; + void reset(); + + PxcNpMemBlockPool& getBlockPool() { return mBlockPool;} +private: + PxcNpMemBlockPool& mBlockPool; + PxcNpMemBlock* mBlock; + PxU32 mUsed; + + FrictionPatchStreamPair& operator=(const FrictionPatchStreamPair&); +}; + +PX_FORCE_INLINE FrictionPatchStreamPair::FrictionPatchStreamPair(PxcNpMemBlockPool& blockPool): + mBlockPool(blockPool), mBlock(NULL), mUsed(0) +{ +} + +PX_FORCE_INLINE void FrictionPatchStreamPair::reset() +{ + mBlock = NULL; + mUsed = 0; +} + +// reserve can fail and return null. Read should never fail +template +FrictionPatch* FrictionPatchStreamPair::reserve(const PxU32 size) +{ + if(size>PxcNpMemBlock::SIZE) + { + return reinterpret_cast(-1); + } + + PX_ASSERT(size <= PxcNpMemBlock::SIZE); + + FrictionPatch* ptr = NULL; + + if(mBlock == NULL || mUsed + size > PxcNpMemBlock::SIZE) + { + mBlock = mBlockPool.acquireFrictionBlock(); + mUsed = 0; + } + + if(mBlock) + { + ptr = reinterpret_cast(mBlock->data+mUsed); + mUsed += size; + } + + return ptr; +} + +template +const FrictionPatch* FrictionPatchStreamPair::findInputPatches(const PxU8* ptr) const +{ + return reinterpret_cast(ptr); +} + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp new file mode 100644 index 000000000..8efc7e170 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CmUtils.h" +#include "DySolverBody.h" +#include "PxsRigidBody.h" +#include "PxvDynamics.h" + +using namespace physx; + +// PT: TODO: SIMDify all this... So far I only took care of the quat-to-matrix transform +void Dy::copyToSolverBodyData(const PxVec3& linearVelocity, const PxVec3& angularVelocity, const PxReal invMass, const PxVec3& invInertia, const PxTransform& globalPose, + const PxReal maxDepenetrationVelocity, const PxReal maxContactImpulse, const PxU32 nodeIndex, const PxReal reportThreshold, PxSolverBodyData& data, PxU32 lockFlags) +{ + data.nodeIndex = nodeIndex; + + const PxVec3 safeSqrtInvInertia = computeSafeSqrtInertia(invInertia); + + const PxMat33 rotation(globalPose.q); + + Cm::transformInertiaTensor(safeSqrtInvInertia, rotation, data.sqrtInvInertia); + + // Copy simple properties + data.linearVelocity = linearVelocity; + data.angularVelocity = angularVelocity; + + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + data.linearVelocity.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + data.linearVelocity.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + data.linearVelocity.z = 0.f; + + //KS - technically, we can zero the inertia columns and produce stiffer constraints. However, this can cause numerical issues with the + //joint solver, which is fixed by disabling joint preprocessing and setting minResponseThreshold to some reasonable value > 0. However, until + //this is handled automatically, it's probably better not to zero these inertia rows + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + { + data.angularVelocity.x = 0.f; + //data.sqrtInvInertia.column0 = PxVec3(0.f); + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + { + data.angularVelocity.y = 0.f; + //data.sqrtInvInertia.column1 = PxVec3(0.f); + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + { + data.angularVelocity.z = 0.f; + //data.sqrtInvInertia.column2 = PxVec3(0.f); + } + } + + PX_ASSERT(linearVelocity.isFinite()); + PX_ASSERT(angularVelocity.isFinite()); + + data.invMass = invMass; + data.penBiasClamp = maxDepenetrationVelocity; + data.maxContactImpulse = maxContactImpulse; + data.body2World = globalPose; + + data.lockFlags = PxU16(lockFlags); + + data.reportThreshold = reportThreshold; +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h new file mode 100644 index 000000000..148f23fc5 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h @@ -0,0 +1,72 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERBODY_H +#define DY_SOLVERBODY_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" +#include "CmPhysXCommon.h" +#include "CmSpatialVector.h" +#include "solver/PxSolverDefs.h" + +namespace physx +{ + +class PxsRigidBody; +struct PxsBodyCore; + +namespace Dy +{ + +// PT: TODO: make sure this is still needed / replace with V4sqrt +//This method returns values of 0 when the inertia is 0. This is a bit of a hack but allows us to +//represent kinematic objects' velocities in our new format +PX_FORCE_INLINE PxVec3 computeSafeSqrtInertia(const PxVec3& v) +{ + return PxVec3( v.x == 0.0f ? 0.0f : PxSqrt(v.x), + v.y == 0.0f ? 0.0f : PxSqrt(v.y), + v.z == 0.0f ? 0.0f : PxSqrt(v.z)); +} + +void copyToSolverBodyData(const PxVec3& linearVelocity, const PxVec3& angularVelocity, const PxReal invMass, const PxVec3& invInertia, const PxTransform& globalPose, + const PxReal maxDepenetrationVelocity, const PxReal maxContactImpulse, const PxU32 nodeIndex, const PxReal reportThreshold, PxSolverBodyData& solverBodyData, PxU32 lockFlags); + +// PT: TODO: using PxsBodyCore in the interface makes us write less data to the stack for passing arguments, and we can take advantage of the class layout +// (we know what is aligned or not, we know if it is safe to V4Load vectors, etc). Note that this is what we previously had, which is why PxsBodyCore was still +// forward-referenced above. +//void copyToSolverBodyData(PxSolverBodyData& solverBodyData, const PxsBodyCore& core, const PxU32 nodeIndex); + +} + +} + +#endif //DY_SOLVERBODY_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h new file mode 100644 index 000000000..303b4d0d0 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h @@ -0,0 +1,204 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVER_CONSTRAINT_1D_H +#define DY_SOLVER_CONSTRAINT_1D_H + +#include "foundation/PxVec3.h" +#include "PxvConfig.h" +#include "DyArticulationUtils.h" +#include "DySolverConstraintTypes.h" +#include "DySolverBody.h" +#include "PxConstraintDesc.h" +#include "DySolverConstraintDesc.h" + +namespace physx +{ + +namespace Dy +{ + +// dsequeira: we should probably fork these structures for constraints and extended constraints, +// since there's a few things that are used for one but not the other + +struct SolverConstraint1DHeader +{ + PxU8 type; // enum SolverConstraintType - must be first byte + PxU8 count; // count of following 1D constraints + PxU8 dominance; + PxU8 breakable; // indicate whether this constraint is breakable or not + + PxReal linBreakImpulse; + PxReal angBreakImpulse; + PxReal invMass0D0; + PxVec3 body0WorldOffset; + PxReal invMass1D1; + PxReal linearInvMassScale0; // only used by articulations + PxReal angularInvMassScale0; // only used by articulations + PxReal linearInvMassScale1; // only used by articulations + PxReal angularInvMassScale1; // only used by articulations +}; + +PX_COMPILE_TIME_ASSERT(sizeof(SolverConstraint1DHeader) == 48); + +PX_ALIGN_PREFIX(16) +struct SolverConstraint1D +{ +public: + PxVec3 lin0; //!< linear velocity projection (body 0) + PxReal constant; //!< constraint constant term + + PxVec3 lin1; //!< linear velocity projection (body 1) + PxReal unbiasedConstant; //!< constraint constant term without bias + + PxVec3 ang0; //!< angular velocity projection (body 0) + PxReal velMultiplier; //!< constraint velocity multiplier + + PxVec3 ang1; //!< angular velocity projection (body 1) + PxReal impulseMultiplier; //!< constraint impulse multiplier + + PxVec3 ang0Writeback; //!< unscaled angular velocity projection (body 0) + PxU32 pad; + + PxReal minImpulse; //!< Lower bound on impulse magnitude + PxReal maxImpulse; //!< Upper bound on impulse magnitude + PxReal appliedForce; //!< applied force to correct velocity+bias + PxU32 flags; +} PX_ALIGN_SUFFIX(16); + +PX_COMPILE_TIME_ASSERT(sizeof(SolverConstraint1D) == 96); + + +struct SolverConstraint1DExt : public SolverConstraint1D +{ +public: + Cm::SpatialVectorV deltaVA; + Cm::SpatialVectorV deltaVB; +}; + +PX_COMPILE_TIME_ASSERT(sizeof(SolverConstraint1DExt) == 160); + + +PX_FORCE_INLINE void init(SolverConstraint1DHeader& h, + PxU8 count, + bool isExtended, + const PxConstraintInvMassScale& ims) +{ + h.type = PxU8(isExtended ? DY_SC_TYPE_EXT_1D : DY_SC_TYPE_RB_1D); + h.count = count; + h.dominance = 0; + h.linearInvMassScale0 = ims.linear0; + h.angularInvMassScale0 = ims.angular0; + h.linearInvMassScale1 = -ims.linear1; + h.angularInvMassScale1 = -ims.angular1; +} + +PX_FORCE_INLINE void init(SolverConstraint1D& c, + const PxVec3& _linear0, const PxVec3& _linear1, + const PxVec3& _angular0, const PxVec3& _angular1, + PxReal _minImpulse, PxReal _maxImpulse) +{ + PX_ASSERT(_linear0.isFinite()); + PX_ASSERT(_linear1.isFinite()); + c.lin0 = _linear0; + c.lin1 = _linear1; + c.ang0 = _angular0; + c.ang1 = _angular1; + c.minImpulse = _minImpulse; + c.maxImpulse = _maxImpulse; + c.flags = 0; + c.appliedForce = 0; +} + +PX_FORCE_INLINE bool needsNormalVel(const Px1DConstraint &c) +{ + return c.flags & Px1DConstraintFlag::eRESTITUTION + || (c.flags & Px1DConstraintFlag::eSPRING && c.flags & Px1DConstraintFlag::eACCELERATION_SPRING); +} + +PX_FORCE_INLINE void setSolverConstants(PxReal& constant, + PxReal& unbiasedConstant, + PxReal& velMultiplier, + PxReal& impulseMultiplier, + const Px1DConstraint& c, + PxReal normalVel, + PxReal unitResponse, + PxReal minRowResponse, + PxReal erp, + PxReal dt, + PxReal recipdt) +{ + PX_ASSERT(PxIsFinite(unitResponse)); + PxReal recipResponse = unitResponse <= minRowResponse ? 0 : 1.0f/unitResponse; + + PxReal geomError = c.geometricError * erp; + + if(c.flags & Px1DConstraintFlag::eSPRING) + { + PxReal a = dt * dt * c.mods.spring.stiffness + dt * c.mods.spring.damping; + PxReal b = dt * (c.mods.spring.damping * c.velocityTarget - c.mods.spring.stiffness * geomError); + + if(c.flags & Px1DConstraintFlag::eACCELERATION_SPRING) + { + PxReal x = 1.0f/(1.0f+a); + constant = unbiasedConstant = x * recipResponse * b; + velMultiplier = -x * recipResponse * a; + impulseMultiplier = 1.0f-x; + } + else + { + PxReal x = unitResponse == 0.f ? 0.f : 1.0f/(1.0f+a*unitResponse); + constant = unbiasedConstant = x * b; + velMultiplier = -x*a; + impulseMultiplier = 1.0f-x; + } + } + else + { + velMultiplier = -recipResponse; + impulseMultiplier = 1.0f; + + if(c.flags & Px1DConstraintFlag::eRESTITUTION && -normalVel>c.mods.bounce.velocityThreshold) + { + unbiasedConstant = constant = recipResponse * c.mods.bounce.restitution*-normalVel; + } + else + { + // see usage of 'for internal use' in preprocessRows() + constant = recipResponse * (c.velocityTarget - geomError*recipdt); + unbiasedConstant = recipResponse * (c.velocityTarget - c.forInternalUse*recipdt); + } + } +} + +} +} + +#endif //DY_SOLVER_CONSTRAINT_1D_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h new file mode 100644 index 000000000..0da65a22a --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h @@ -0,0 +1,106 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_SOLVERCONSTRAINT1D4_H +#define DY_SOLVERCONSTRAINT1D4_H + +#include "foundation/PxVec3.h" +#include "PxvConfig.h" +#include "DyArticulationUtils.h" +#include "DySolverConstraint1D.h" + +namespace physx +{ + +namespace Dy +{ + +struct SolverConstraint1DHeader4 +{ + PxU8 type; // enum SolverConstraintType - must be first byte + PxU8 pad0[3]; + //These counts are the max of the 4 sets of data. + //When certain pairs have fewer constraints than others, they are padded with 0s so that no work is performed but + //calculations are still shared (afterall, they're computationally free because we're doing 4 things at a time in SIMD) + PxU32 count; + PxU8 count0, count1, count2, count3; + PxU8 break0, break1, break2, break3; + + Vec4V linBreakImpulse; + Vec4V angBreakImpulse; + Vec4V invMass0D0; + Vec4V invMass1D1; + Vec4V angD0; + Vec4V angD1; + + Vec4V body0WorkOffsetX; + Vec4V body0WorkOffsetY; + Vec4V body0WorkOffsetZ; +}; + +struct SolverConstraint1DBase4 +{ +public: + Vec4V lin0X; + Vec4V lin0Y; + Vec4V lin0Z; + Vec4V ang0X; + Vec4V ang0Y; + Vec4V ang0Z; + Vec4V ang0WritebackX; + Vec4V ang0WritebackY; + Vec4V ang0WritebackZ; + Vec4V constant; + Vec4V unbiasedConstant; + Vec4V velMultiplier; + Vec4V impulseMultiplier; + Vec4V minImpulse; + Vec4V maxImpulse; + Vec4V appliedForce; + PxU32 flags[4]; +}; + +PX_COMPILE_TIME_ASSERT(sizeof(SolverConstraint1DBase4) == 272); + +struct SolverConstraint1DDynamic4 : public SolverConstraint1DBase4 +{ + Vec4V lin1X; + Vec4V lin1Y; + Vec4V lin1Z; + Vec4V ang1X; + Vec4V ang1Y; + Vec4V ang1Z; +}; +PX_COMPILE_TIME_ASSERT(sizeof(SolverConstraint1DDynamic4) == 368); + +} + +} + +#endif //DY_SOLVERCONSTRAINT1D4_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h new file mode 100644 index 000000000..b2f7e25e4 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h @@ -0,0 +1,222 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVER_CONSTRAINT_1D_STEP_H +#define DY_SOLVER_CONSTRAINT_1D_STEP_H + +#include "foundation/PxVec3.h" +#include "PxvConfig.h" +#include "DyArticulationUtils.h" +#include "DySolverConstraintTypes.h" +#include "DySolverBody.h" +#include "PxConstraintDesc.h" +#include "DySolverConstraintDesc.h" + + +namespace physx +{ + namespace Dy + { + struct SolverContactHeaderStep + { + enum DySolverContactFlags + { + eHAS_FORCE_THRESHOLDS = 0x1 + }; + + PxU8 type; //Note: mType should be first as the solver expects a type in the first byte. + PxU8 flags; + PxU8 numNormalConstr; + PxU8 numFrictionConstr; //4 + + PxReal angDom0; //8 + PxReal angDom1; //12 + PxReal invMass0; //16 + + Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; //32 + PxVec3 normal; //48 + + PxReal maxPenBias; //52 + PxReal invMass1; //56 + PxReal minNormalForce; //60 + PxU32 broken; //64 + PxU8* frictionBrokenWritebackByte; //68 72 + Sc::ShapeInteraction* shapeInteraction; //72 80 + + + PX_FORCE_INLINE FloatV getStaticFriction() const { return V4GetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W); } + PX_FORCE_INLINE FloatV getDynamicFriction() const { return V4GetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W); } + PX_FORCE_INLINE FloatV getDominance0() const { return V4GetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W); } + PX_FORCE_INLINE FloatV getDominance1() const { return V4GetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W); } + }; + + struct SolverContactPointStep + { + PxVec3 raXnI; + PxF32 separation; + PxVec3 rbXnI; + PxF32 velMultiplier; + PxF32 targetVelocity; + PxF32 biasCoefficient; + //2 more slots here for extra data + PxU32 pad[2]; + }; + + struct SolverContactPointStepExt : public SolverContactPointStep + { + Vec3V linDeltaVA; + Vec3V linDeltaVB; + Vec3V angDeltaVA; + Vec3V angDeltaVB; + }; + + struct SolverContactFrictionStep + { + Vec4V normalXYZ_ErrorW; //16 + Vec4V raXnI_targetVelW; + Vec4V rbXnI_velMultiplierW; + PxReal biasScale; + PxReal appliedForce; + PxReal frictionScale; + PxU32 pad[1]; + + PX_FORCE_INLINE void setAppliedForce(const FloatV f) { FStore(f, &appliedForce); } + }; + + struct SolverContactFrictionStepExt : public SolverContactFrictionStep + { + Vec3V linDeltaVA; + Vec3V linDeltaVB; + Vec3V angDeltaVA; + Vec3V angDeltaVB; + }; + + struct SolverConstraint1DHeaderStep + { + PxU8 type; // enum SolverConstraintType - must be first byte + PxU8 count; // count of following 1D constraints + PxU8 dominance; + PxU8 breakable; // indicate whether this constraint is breakable or not + PxReal linBreakImpulse; + PxReal angBreakImpulse; + PxReal invMass0D0; + + PxVec3 body0WorldOffset; + PxReal invMass1D1; + + PxVec3 rAWorld; + PxReal linearInvMassScale0; // only used by articulations + + PxVec3 rBWorld; + PxReal angularInvMassScale0; + + PxReal linearInvMassScale1; // only used by articulations + PxReal angularInvMassScale1; + PxU32 pad[2]; + + //Ortho axes for body 0, recipResponse in W component + PxVec4 angOrthoAxis0_recipResponseW[3]; + //Ortho axes for body 1, error of body in W component + PxVec4 angOrthoAxis1_Error[3]; + }; + + + PX_FORCE_INLINE void init(SolverConstraint1DHeaderStep& h, + PxU8 count, + bool isExtended, + const PxConstraintInvMassScale& ims) + { + h.type = PxU8(isExtended ? DY_SC_TYPE_EXT_1D : DY_SC_TYPE_RB_1D); + h.count = count; + h.dominance = 0; + h.linearInvMassScale0 = ims.linear0; + h.angularInvMassScale0 = ims.angular0; + h.linearInvMassScale1 = -ims.linear1; + h.angularInvMassScale1 = -ims.angular1; + } + + + PX_ALIGN_PREFIX(16) + struct SolverConstraint1DStep + { + public: + PxVec3 lin0; //!< linear velocity projection (body 0) + PxReal error; //!< constraint error term - must be scaled by biasScale. Can be adjusted at run-time + + PxVec3 lin1; //!< linear velocity projection (body 1) + PxReal biasScale; //!< constraint constant bias scale. Constant + + PxVec3 ang0; //!< angular velocity projection (body 0) + PxReal velMultiplier; //!< constraint velocity multiplier + + PxVec3 ang1; //!< angular velocity projection (body 1) + PxReal impulseMultiplier; //!< constraint impulse multiplier + + PxReal velTarget; //!< Scaled target velocity of the constraint drive + + PxReal minImpulse; //!< Lower bound on impulse magnitude + PxReal maxImpulse; //!< Upper bound on impulse magnitude + PxReal appliedForce; //!< applied force to correct velocity+bias + + PxReal maxBias; + PxU32 flags; + PxReal recipResponse; //Constant. Only used for articulations; + PxReal angularErrorScale; //Constant + } PX_ALIGN_SUFFIX(16); + + struct SolverConstraint1DExtStep : public SolverConstraint1DStep + { + public: + Cm::SpatialVectorV deltaVA; + Cm::SpatialVectorV deltaVB; + }; + + PX_FORCE_INLINE void init(SolverConstraint1DStep& c, + const PxVec3& _linear0, const PxVec3& _linear1, + const PxVec3& _angular0, const PxVec3& _angular1, + PxReal _minImpulse, PxReal _maxImpulse) + { + PX_ASSERT(_linear0.isFinite()); + PX_ASSERT(_linear1.isFinite()); + c.lin0 = _linear0; + c.lin1 = _linear1; + c.ang0 = _angular0; + c.ang1 = _angular1; + c.minImpulse = _minImpulse; + c.maxImpulse = _maxImpulse; + c.flags = 0; + c.appliedForce = 0; + c.angularErrorScale = 1.f; + } + + }//namespace Dy +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h new file mode 100644 index 000000000..6f6408260 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCONSTRAINTDESC_H +#define DY_SOLVERCONSTRAINTDESC_H + +#include "PxvConfig.h" +#include "DySolverConstraintTypes.h" +#include "PsUtilities.h" +#include "PxConstraintDesc.h" +#include "solver/PxSolverDefs.h" + +namespace physx +{ + +struct PxcNpWorkUnit; + +struct PxsContactManagerOutput; + +namespace Cm +{ + class SpatialVector; +} + +struct PxSolverBody; +struct PxSolverBodyData; + +namespace Dy +{ + +struct FsData; + + + + +// dsequeira: moved this articulation stuff here to sever a build dep on Articulation.h through DyThreadContext.h and onward + +struct SelfConstraintBlock +{ + PxU32 startId; + PxU32 numSelfConstraints; + PxU16 fsDataLength; + PxU16 requiredSolverProgress; + uintptr_t eaFsData; +}; + +//This class rolls together multiple contact managers into a single contact manager. +struct CompoundContactManager +{ + PxU32 mStartIndex; + PxU16 mStride; + PxU16 mReducedContactCount; + + PxcNpWorkUnit* unit; //This is a work unit but the contact buffer has been adjusted to contain all the contacts for all the subsequent pairs + PxsContactManagerOutput* cmOutput; + PxU8* originalContactPatches; //This is the original contact buffer that we replaced with a combined buffer + PxU8* originalContactPoints; + PxU8 originalContactCount; + PxU8 originalPatchCount; + PxU8 originalStatusFlags; + PxReal* originalForceBuffer; //This is the original force buffer that we replaced with a combined force buffer + PxU16* forceBufferList; //This is a list of indices from the reduced force buffer to the original force buffers - we need this to fix up the write-backs from the solver +}; + +struct SolverConstraintPrepState +{ +enum Enum +{ + eOUT_OF_MEMORY, + eUNBATCHABLE, + eSUCCESS +}; +}; + +PX_FORCE_INLINE bool isArticulationConstraint(const PxSolverConstraintDesc& desc) +{ + return desc.linkIndexA != PxSolverConstraintDesc::NO_LINK || + desc.linkIndexB != PxSolverConstraintDesc::NO_LINK; +} + + +PX_FORCE_INLINE void setConstraintLength(PxSolverConstraintDesc& desc, const PxU32 constraintLength) +{ + PX_ASSERT(0==(constraintLength & 0x0f)); + PX_ASSERT(constraintLength <= PX_MAX_U16 * 16); + desc.constraintLengthOver16 = Ps::to16(constraintLength >> 4); +} + +PX_FORCE_INLINE void setWritebackLength(PxSolverConstraintDesc& desc, const PxU32 writeBackLength) +{ + PX_ASSERT(0==(writeBackLength & 0x03)); + PX_ASSERT(writeBackLength <= PX_MAX_U16 * 4); + desc.writeBackLengthOver4 = Ps::to16(writeBackLength >> 2); +} + +PX_FORCE_INLINE PxU32 getConstraintLength(const PxSolverConstraintDesc& desc) +{ + return PxU32(desc.constraintLengthOver16 << 4); +} + + +PX_FORCE_INLINE PxU32 getWritebackLength(const PxSolverConstraintDesc& desc) +{ + return PxU32(desc.writeBackLengthOver4 << 2); +} + +PX_COMPILE_TIME_ASSERT(0 == (0x0f & sizeof(PxSolverConstraintDesc))); + +#define MAX_PERMITTED_SOLVER_PROGRESS 0xFFFF + +} + +} + +#endif //DY_SOLVERCONSTRAINTDESC_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h new file mode 100644 index 000000000..007bd5972 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_SOLVER_CONSTRAINT_EXT_SHARED_H +#define DY_SOLVER_CONSTRAINT_EXT_SHARED_H + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "DyArticulationContactPrep.h" +#include "DySolverConstraintDesc.h" +#include "DySolverConstraint1D.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DyArticulationHelper.h" +#include "PxcNpWorkUnit.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" + +namespace physx +{ + namespace Dy + { + FloatV setupExtSolverContact(const SolverExtBody& b0, const SolverExtBody& b1, + const PxF32 d0, const PxF32 d1, const PxF32 angD0, const PxF32 angD1, const PxTransform& bodyFrame0, const PxTransform& bodyFrame1, + const Vec3VArg normal, const FloatVArg invDt, const FloatVArg invDtp8, const FloatVArg restDistance, const FloatVArg maxPenBias, const FloatVArg restitution, + const FloatVArg bounceThreshold, const Gu::ContactPoint& contact, SolverContactPointExt& solverContact, const FloatVArg ccdMaxSeparation, + Cm::SpatialVectorF* Z); + } //namespace Dy +} + +#endif //DY_SOLVER_CONSTRAINT_EXT_SHARED_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h new file mode 100644 index 000000000..6b857c781 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h @@ -0,0 +1,72 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCONSTRAINTTYPES_H +#define DY_SOLVERCONSTRAINTTYPES_H + +#include "foundation/PxSimpleTypes.h" +#include "PxvConfig.h" + +namespace physx +{ + +enum SolverConstraintType +{ + DY_SC_TYPE_NONE = 0, + DY_SC_TYPE_RB_CONTACT, // RB-only contact + DY_SC_TYPE_RB_1D, // RB-only 1D constraint + DY_SC_TYPE_EXT_CONTACT, // contact involving articulations + DY_SC_TYPE_EXT_1D, // 1D constraint involving articulations + DY_SC_TYPE_STATIC_CONTACT, // RB-only contact where body b is static + DY_SC_TYPE_NOFRICTION_RB_CONTACT, //RB-only contact with no friction patch + DY_SC_TYPE_BLOCK_RB_CONTACT, + DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT, + DY_SC_TYPE_BLOCK_1D, + DY_SC_TYPE_FRICTION, + DY_SC_TYPE_STATIC_FRICTION, + DY_SC_TYPE_EXT_FRICTION, + DY_SC_TYPE_BLOCK_FRICTION, + DY_SC_TYPE_BLOCK_STATIC_FRICTION, + DY_SC_CONSTRAINT_TYPE_COUNT //Count of the number of different constraint types in the solver +}; + +enum SolverConstraintFlags +{ + DY_SC_FLAG_OUTPUT_FORCE = (1<<1), + DY_SC_FLAG_KEEP_BIAS = (1<<2), + DY_SC_FLAG_ROT_EQ = (1<<3), + DY_SC_FLAG_ORTHO_TARGET = (1<<4), + DY_SC_FLAG_SPRING = (1<<5), + DY_SC_FLAG_INEQUALITY = (1<<6) +}; + +} + +#endif //DY_SOLVERCONSTRAINTTYPES_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp new file mode 100644 index 000000000..ca0027eba --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp @@ -0,0 +1,1261 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" + +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverContact.h" +#include "DySolverConstraint1D.h" +#include "DySolverConstraintDesc.h" +#include "DyThresholdTable.h" +#include "DySolverContext.h" +#include "PsUtilities.h" +#include "DyConstraint.h" +#include "PsAtomic.h" +#include "DySolverConstraintsShared.h" + +namespace physx +{ + +namespace Dy +{ + +//Port of scalar implementation to SIMD maths with some interleaving of instructions +void solve1D(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + PX_UNUSED(cache); + PxSolverBody& b0 = *desc.bodyA; + PxSolverBody& b1 = *desc.bodyB; + + PxU8* PX_RESTRICT bPtr = desc.constraint; + if (bPtr == NULL) + return; + //PxU32 length = desc.constraintLength; + + const SolverConstraint1DHeader* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1D* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeader)); + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V linVel1 = V3LoadA(b1.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + Vec3V angState1 = V3LoadA(b1.angularState); + + const FloatV invMass0 = FLoad(header->invMass0D0); + const FloatV invMass1 = FLoad(header->invMass1D1); + const FloatV invInertiaScale0 = FLoad(header->angularInvMassScale0); + const FloatV invInertiaScale1 = FLoad(header->angularInvMassScale1); + + + for(PxU32 i=0; icount;++i, base++) + { + Ps::prefetchLine(base+1); + SolverConstraint1D& c = *base; + + const Vec3V clinVel0 = V3LoadA(c.lin0); + const Vec3V clinVel1 = V3LoadA(c.lin1); + const Vec3V cangVel0 = V3LoadA(c.ang0); + const Vec3V cangVel1 = V3LoadA(c.ang1); + + const FloatV constant = FLoad(c.constant); + const FloatV vMul = FLoad(c.velMultiplier); + const FloatV iMul = FLoad(c.impulseMultiplier); + const FloatV appliedForce = FLoad(c.appliedForce); + //const FloatV targetVel = FLoad(c.targetVelocity); + + const FloatV maxImpulse = FLoad(c.maxImpulse); + const FloatV minImpulse = FLoad(c.minImpulse); + + const Vec3V v0 = V3MulAdd(linVel0, clinVel0, V3Mul(angState0, cangVel0)); + const Vec3V v1 = V3MulAdd(linVel1, clinVel1, V3Mul(angState1, cangVel1)); + + const FloatV normalVel = V3SumElems(V3Sub(v0, v1)); + const FloatV unclampedForce = FScaleAdd(iMul, appliedForce, FScaleAdd(vMul, normalVel, constant)); + const FloatV clampedForce = FMin(maxImpulse, (FMax(minImpulse, unclampedForce))); + const FloatV deltaF = FSub(clampedForce, appliedForce); + + FStore(clampedForce, &c.appliedForce); + linVel0 = V3ScaleAdd(clinVel0, FMul(deltaF, invMass0), linVel0); + linVel1 = V3NegScaleSub(clinVel1, FMul(deltaF, invMass1), linVel1); + angState0 = V3ScaleAdd(cangVel0, FMul(deltaF, invInertiaScale0), angState0); + //This should be negScaleSub but invInertiaScale1 is negated already + angState1 = V3ScaleAdd(cangVel1, FMul(deltaF, invInertiaScale1), angState1); + + } + + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(angState0, b0.angularState); + V3StoreA(linVel1, b1.linearVelocity); + V3StoreA(angState1, b1.angularState); + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularState.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularState.isFinite()); +} + +void conclude1D(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + SolverConstraint1DHeader* header = reinterpret_cast(desc.constraint); + if (header == NULL) + return; + PxU8* base = desc.constraint + sizeof(SolverConstraint1DHeader); + PxU32 stride = header->type == DY_SC_TYPE_EXT_1D ? sizeof(SolverConstraint1DExt) : sizeof(SolverConstraint1D); + + for(PxU32 i=0; icount; i++) + { + SolverConstraint1D& c = *reinterpret_cast(base); + + c.constant = c.unbiasedConstant; + + base += stride; + } + PX_ASSERT(desc.constraint + getConstraintLength(desc) == base); +} + +// ============================================================== + +void solveContact(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + PxSolverBody& b0 = *desc.bodyA; + PxSolverBody& b1 = *desc.bodyB; + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V linVel1 = V3LoadA(b1.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + Vec3V angState1 = V3LoadA(b1.angularState); + + const PxU8* PX_RESTRICT last = desc.constraint + getConstraintLength(desc); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + while(currPtr < last) + { + SolverContactHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverContactPoint* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPoint); + + PxF32* forceBuffer = reinterpret_cast(currPtr); + currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + SolverContactFriction* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFriction); + + const FloatV invMassA = FLoad(hdr->invMass0); + const FloatV invMassB = FLoad(hdr->invMass1); + + const FloatV angDom0 = FLoad(hdr->angDom0); + const FloatV angDom1 = FLoad(hdr->angDom1); + + const Vec3V contactNormal = Vec3V_From_Vec4V_WUndefined(hdr->normal_minAppliedImpulseForFrictionW); + + const FloatV accumulatedNormalImpulse = solveDynamicContacts(contacts, numNormalConstr, contactNormal, invMassA, invMassB, + angDom0, angDom1, linVel0, angState0, linVel1, angState1, forceBuffer); + + if(cache.doFriction && numFrictionConstr) + { + const FloatV staticFrictionCof = hdr->getStaticFriction(); + const FloatV dynamicFrictionCof = hdr->getDynamicFriction(); + const FloatV maxFrictionImpulse = FMul(staticFrictionCof, accumulatedNormalImpulse); + const FloatV maxDynFrictionImpulse = FMul(dynamicFrictionCof, accumulatedNormalImpulse); + const FloatV negMaxDynFrictionImpulse = FNeg(maxDynFrictionImpulse); + + BoolV broken = BFFFF(); + + if(cache.writeBackIteration) + Ps::prefetchLine(hdr->frictionBrokenWritebackByte); + + for(PxU32 i=0;i maxFrictionImpulse + // clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse] + // (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce] + // set broken flag to true || broken flag + + // FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier); + // FloatV potentialSumF = FAdd(appliedForce, deltaF); + + const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1); + + // On XBox this clamping code uses the vector simple pipe rather than vector float, + // which eliminates a lot of stall cycles + + const BoolV clamp = FIsGrtr(FAbs(totalImpulse), maxFrictionImpulse); + + const FloatV totalClamped = FMin(maxDynFrictionImpulse, FMax(negMaxDynFrictionImpulse, totalImpulse)); + + const FloatV newAppliedForce = FSel(clamp, totalClamped,totalImpulse); + + broken = BOr(broken, clamp); + + FloatV deltaF = FSub(newAppliedForce, appliedForce); + + // we could get rid of the stall here by calculating and clamping delta separately, but + // the complexity isn't really worth it. + + linVel0 = V3ScaleAdd(delLinVel0, deltaF, linVel0); + linVel1 = V3NegScaleSub(delLinVel1, deltaF, linVel1); + angState0 = V3ScaleAdd(raXn, FMul(deltaF, angDom0), angState0); + angState1 = V3NegScaleSub(rbXn, FMul(deltaF, angDom1), angState1); + + f.setAppliedForce(newAppliedForce); + + + } + Store_From_BoolV(broken, &hdr->broken); + } + + } + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularState.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularState.isFinite()); + + // Write back + V3StoreU(linVel0, b0.linearVelocity); + V3StoreU(linVel1, b1.linearVelocity); + V3StoreU(angState0, b0.angularState); + V3StoreU(angState1, b1.angularState); + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularState.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularState.isFinite()); + + PX_ASSERT(currPtr == last); +} + +void solveContact_BStatic(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + PxSolverBody& b0 = *desc.bodyA; + //PxSolverBody& b1 = *desc.bodyB; + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + + const PxU8* PX_RESTRICT last = desc.constraint + getConstraintLength(desc); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + while(currPtr < last) + { + SolverContactHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverContactPoint* PX_RESTRICT contacts = reinterpret_cast(currPtr); + //Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPoint); + + PxF32* forceBuffer = reinterpret_cast(currPtr); + currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + SolverContactFriction* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFriction); + + + + const FloatV invMassA = FLoad(hdr->invMass0); + + const Vec3V contactNormal = Vec3V_From_Vec4V_WUndefined(hdr->normal_minAppliedImpulseForFrictionW); + const FloatV angDom0 = FLoad(hdr->angDom0); + + + const FloatV accumulatedNormalImpulse = solveStaticContacts(contacts, numNormalConstr, contactNormal, + invMassA, angDom0, linVel0, angState0, forceBuffer); + + if(cache.doFriction && numFrictionConstr) + { + const FloatV maxFrictionImpulse = FMul(hdr->getStaticFriction(), accumulatedNormalImpulse); + const FloatV maxDynFrictionImpulse = FMul(hdr->getDynamicFriction(), accumulatedNormalImpulse); + + BoolV broken = BFFFF(); + if(cache.writeBackIteration) + Ps::prefetchLine(hdr->frictionBrokenWritebackByte); + + for(PxU32 i=0;i maxFrictionImpulse + // clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse] + // (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce] + // set broken flag to true || broken flag + + // FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier); + // FloatV potentialSumF = FAdd(appliedForce, deltaF); + + const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1); + + // On XBox this clamping code uses the vector simple pipe rather than vector float, + // which eliminates a lot of stall cycles + + const BoolV clamp = FIsGrtr(FAbs(totalImpulse), maxFrictionImpulse); + + const FloatV totalClamped = FMin(maxDynFrictionImpulse, FMax(negMaxDynFrictionImpulse, totalImpulse)); + + broken = BOr(broken, clamp); + + const FloatV newAppliedForce = FSel(clamp, totalClamped,totalImpulse); + + FloatV deltaF = FSub(newAppliedForce, appliedForce); + + // we could get rid of the stall here by calculating and clamping delta separately, but + // the complexity isn't really worth it. + + linVel0 = V3ScaleAdd(delLinVel0, deltaF, linVel0); + angState0 = V3ScaleAdd(raXn, FMul(deltaF, angDom0), angState0); + + f.setAppliedForce(newAppliedForce); + + } + Store_From_BoolV(broken, &hdr->broken); + } + + } + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularState.isFinite()); + + // Write back + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(angState0, b0.angularState); + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularState.isFinite()); + + PX_ASSERT(currPtr == last); +} + + +void concludeContact(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + PxU8* PX_RESTRICT cPtr = desc.constraint; + + const FloatV zero = FZero(); + + PxU8* PX_RESTRICT last = desc.constraint + getConstraintLength(desc); + while(cPtr < last) + { + const SolverContactHeader* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + //if(cPtr < last) + //Ps::prefetchLine(cPtr, 512); + Ps::prefetchLine(cPtr,128); + Ps::prefetchLine(cPtr,256); + Ps::prefetchLine(cPtr,384); + + const PxU32 pointStride = hdr->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactPointExt) + : sizeof(SolverContactPoint); + for(PxU32 i=0;i(cPtr); + cPtr += pointStride; + //c->scaledBias = PxMin(c->scaledBias, 0.f); + c->biasedErr = c->unbiasedErr; + } + + cPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); //Jump over force buffers + + const PxU32 frictionStride = hdr->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactFrictionExt) + : sizeof(SolverContactFriction); + for(PxU32 i=0;i(cPtr); + cPtr += frictionStride; + f->setBias(zero); + } + } + PX_ASSERT(cPtr == last); +} + +void writeBackContact(const PxSolverConstraintDesc& desc, SolverContext& cache, + PxSolverBodyData& bd0, PxSolverBodyData& bd1) +{ + + PxReal normalForce = 0; + + PxU8* PX_RESTRICT cPtr = desc.constraint; + PxReal* PX_RESTRICT vForceWriteback = reinterpret_cast(desc.writeBack); + PxU8* PX_RESTRICT last = desc.constraint + getConstraintLength(desc); + + bool forceThreshold = false; + + while(cPtr < last) + { + const SolverContactHeader* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactHeader); + + forceThreshold = hdr->flags & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + //if(cPtr < last) + Ps::prefetchLine(cPtr, 256); + Ps::prefetchLine(cPtr, 384); + + const PxU32 pointStride = hdr->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactPointExt) + : sizeof(SolverContactPoint); + + cPtr += pointStride * numNormalConstr; + PxF32* forceBuffer = reinterpret_cast(cPtr); + cPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + if(vForceWriteback!=NULL) + { + for(PxU32 i=0; itype == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactFrictionExt) + : sizeof(SolverContactFriction); + + if(hdr->broken && hdr->frictionBrokenWritebackByte != NULL) + { + *hdr->frictionBrokenWritebackByte = 1; + } + + cPtr += frictionStride * numFrictionConstr; + + } + PX_ASSERT(cPtr == last); + + + + if(forceThreshold && desc.linkIndexA == PxSolverConstraintDesc::NO_LINK && desc.linkIndexB == PxSolverConstraintDesc::NO_LINK && + normalForce !=0 && (bd0.reportThreshold < PX_MAX_REAL || bd1.reportThreshold < PX_MAX_REAL)) + { + ThresholdStreamElement elt; + elt.normalForce = normalForce; + elt.threshold = PxMin(bd0.reportThreshold, bd1.reportThreshold); + elt.nodeIndexA = IG::NodeIndex(bd0.nodeIndex); + elt.nodeIndexB = IG::NodeIndex(bd1.nodeIndex); + elt.shapeInteraction = reinterpret_cast(desc.constraint)->shapeInteraction; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + PX_ASSERT(cache.mThresholdStreamIndex(desc.writeBack); + if(writeback) + { + SolverConstraint1DHeader* header = reinterpret_cast(desc.constraint); + PxU8* base = desc.constraint + sizeof(SolverConstraint1DHeader); + PxU32 stride = header->type == DY_SC_TYPE_EXT_1D ? sizeof(SolverConstraint1DExt) : sizeof(SolverConstraint1D); + + PxVec3 lin(0), ang(0); + for(PxU32 i=0; icount; i++) + { + const SolverConstraint1D* c = reinterpret_cast(base); + if(c->flags & DY_SC_FLAG_OUTPUT_FORCE) + { + lin += c->lin0 * c->appliedForce; + ang += c->ang0Writeback * c->appliedForce; + } + base += stride; + } + + ang -= header->body0WorldOffset.cross(lin); + writeback->linearImpulse = lin; + writeback->angularImpulse = ang; + writeback->broken = header->breakable ? PxU32(lin.magnitude()>header->linBreakImpulse || ang.magnitude()>header->angBreakImpulse) : 0; + + PX_ASSERT(desc.constraint + getConstraintLength(desc) == base); + } +} + + +void solve1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solve1D(desc[a-1], cache); + } + solve1D(desc[constraintCount-1], cache); +} + +void solve1DConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solve1D(desc[a-1], cache); + conclude1D(desc[a-1], cache); + } + solve1D(desc[constraintCount-1], cache); + conclude1D(desc[constraintCount-1], cache); +} + +void solve1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a-1].bodyBDataIndex]; + solve1D(desc[a-1], cache); + writeBack1D(desc[a-1], cache, bd0, bd1); + } + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[constraintCount-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[constraintCount-1].bodyBDataIndex]; + solve1D(desc[constraintCount-1], cache); + writeBack1D(desc[constraintCount-1], cache, bd0, bd1); +} + +void writeBack1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a-1].bodyBDataIndex]; + writeBack1D(desc[a-1], cache, bd0, bd1); + } + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[constraintCount-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[constraintCount-1].bodyBDataIndex]; + writeBack1D(desc[constraintCount-1], cache, bd0, bd1); +} + +void solveContactBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solveContact(desc[a-1], cache); + } + solveContact(desc[constraintCount-1], cache); +} + +void solveContactConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solveContact(desc[a-1], cache); + concludeContact(desc[a-1], cache); + } + solveContact(desc[constraintCount-1], cache); + concludeContact(desc[constraintCount-1], cache); +} + +void solveContactBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a-1].bodyBDataIndex]; + solveContact(desc[a-1], cache); + writeBackContact(desc[a-1], cache, bd0, bd1); + } + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[constraintCount-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[constraintCount-1].bodyBDataIndex]; + solveContact(desc[constraintCount-1], cache); + writeBackContact(desc[constraintCount-1], cache, bd0, bd1); + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveContact_BStaticBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solveContact_BStatic(desc[a-1], cache); + } + solveContact_BStatic(desc[constraintCount-1], cache); +} + +void solveContact_BStaticConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solveContact_BStatic(desc[a-1], cache); + concludeContact(desc[a-1], cache); + } + solveContact_BStatic(desc[constraintCount-1], cache); + concludeContact(desc[constraintCount-1], cache); +} + +void solveContact_BStaticBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a-1].bodyBDataIndex]; + solveContact_BStatic(desc[a-1], cache); + writeBackContact(desc[a-1], cache, bd0, bd1); + } + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[constraintCount-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[constraintCount-1].bodyBDataIndex]; + solveContact_BStatic(desc[constraintCount-1], cache); + writeBackContact(desc[constraintCount-1], cache, bd0, bd1); + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Not enough space to write 4 more thresholds back! + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void clearExt1D(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + PxU8* PX_RESTRICT bPtr = desc.constraint; + const SolverConstraint1DHeader* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DExt* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeader)); + + for (PxU32 i = 0; i < header->count; ++i, base++) + { + base->appliedForce = 0.f; + } +} + +//Port of scalar implementation to SIMD maths with some interleaving of instructions +void solveExt1D(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + PxU8* PX_RESTRICT bPtr = desc.constraint; + //PxU32 length = desc.constraintLength; + + const SolverConstraint1DHeader* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DExt* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeader)); + + Vec3V linVel0, angVel0, linVel1, angVel1; + + if (desc.articulationA == desc.articulationB) + { + Cm::SpatialVectorV v0, v1; + desc.articulationA->pxcFsGetVelocities(desc.linkIndexA, desc.linkIndexB, v0, v1); + linVel0 = v0.linear; + angVel0 = v0.angular; + linVel1 = v1.linear; + angVel1 = v1.angular; + } + else + { + + if (desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + linVel0 = V3LoadA(desc.bodyA->linearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + } + } + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + for(PxU32 i=0; icount;++i, base++) + { + Ps::prefetchLine(base+1); + + const Vec4V lin0XYZ_constantW = V4LoadA(&base->lin0.x); + const Vec4V lin1XYZ_unbiasedConstantW = V4LoadA(&base->lin1.x); + const Vec4V ang0XYZ_velMultiplierW = V4LoadA(&base->ang0.x); + const Vec4V ang1XYZ_impulseMultiplierW = V4LoadA(&base->ang1.x); + const Vec4V minImpulseX_maxImpulseY_appliedForceZ = V4LoadA(&base->minImpulse); + + const Vec3V lin0 = Vec3V_From_Vec4V(lin0XYZ_constantW); FloatV constant = V4GetW(lin0XYZ_constantW); + const Vec3V lin1 = Vec3V_From_Vec4V(lin1XYZ_unbiasedConstantW); + const Vec3V ang0 = Vec3V_From_Vec4V(ang0XYZ_velMultiplierW); FloatV vMul = V4GetW(ang0XYZ_velMultiplierW); + const Vec3V ang1 = Vec3V_From_Vec4V(ang1XYZ_impulseMultiplierW); FloatV iMul = V4GetW(ang1XYZ_impulseMultiplierW); + + const FloatV minImpulse = V4GetX(minImpulseX_maxImpulseY_appliedForceZ); + const FloatV maxImpulse = V4GetY(minImpulseX_maxImpulseY_appliedForceZ); + const FloatV appliedForce = V4GetZ(minImpulseX_maxImpulseY_appliedForceZ); + + const Vec3V v0 = V3MulAdd(linVel0, lin0, V3Mul(angVel0, ang0)); + const Vec3V v1 = V3MulAdd(linVel1, lin1, V3Mul(angVel1, ang1)); + const FloatV normalVel = V3SumElems(V3Sub(v0, v1)); + + const FloatV unclampedForce = FScaleAdd(iMul, appliedForce, FScaleAdd(vMul, normalVel, constant)); + const FloatV clampedForce = FMin(maxImpulse, (FMax(minImpulse, unclampedForce))); + const FloatV deltaF = FSub(clampedForce, appliedForce); + + FStore(clampedForce, &base->appliedForce); + li0 = V3ScaleAdd(lin0, deltaF, li0); ai0 = V3ScaleAdd(ang0, deltaF, ai0); + li1 = V3ScaleAdd(lin1, deltaF, li1); ai1 = V3ScaleAdd(ang1, deltaF, ai1); + + linVel0 = V3ScaleAdd(base->deltaVA.linear, deltaF, linVel0); angVel0 = V3ScaleAdd(base->deltaVA.angular, deltaF, angVel0); + linVel1 = V3ScaleAdd(base->deltaVB.linear, deltaF, linVel1); angVel1 = V3ScaleAdd(base->deltaVB.angular, deltaF, angVel1); + +#if 0 + PxVec3 lv0, lv1, av0, av1; + V3StoreU(linVel0, lv0); V3StoreU(linVel1, lv1); + V3StoreU(angVel0, av0); V3StoreU(angVel1, av1); + + PX_ASSERT(lv0.magnitude() < 30.f); + PX_ASSERT(lv1.magnitude() < 30.f); + PX_ASSERT(av0.magnitude() < 30.f); + PX_ASSERT(av1.magnitude() < 30.f); +#endif + + } + + if (desc.articulationA == desc.articulationB) + { + desc.articulationA->pxcFsApplyImpulses(desc.linkIndexA, V3Scale(li0, FLoad(header->linearInvMassScale0)), V3Scale(ai0, FLoad(header->angularInvMassScale0)), + desc.linkIndexB, V3Scale(li1, FLoad(header->linearInvMassScale1)), V3Scale(ai1, FLoad(header->angularInvMassScale1)), cache.Z, cache.deltaV); + } + else + { + + if (desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularState); + } + else + { + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, V3Scale(li0, FLoad(header->linearInvMassScale0)), + V3Scale(ai0, FLoad(header->angularInvMassScale0)), cache.Z, cache.deltaV); + + + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularState); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, V3Scale(li1, FLoad(header->linearInvMassScale1)), + V3Scale(ai1, FLoad(header->angularInvMassScale1)), cache.Z, cache.deltaV); + } + } + +} + +FloatV solveExtContacts(SolverContactPointExt* contacts, const PxU32 nbContactPoints, const Vec3VArg contactNormal, + Vec3V& linVel0, Vec3V& angVel0, + Vec3V& linVel1, Vec3V& angVel1, + Vec3V& li0, Vec3V& ai0, + Vec3V& li1, Vec3V& ai1, + PxF32* PX_RESTRICT appliedForceBuffer) +{ + + FloatV accumulatedNormalImpulse = FZero(); + for (PxU32 i = 0; ilinearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + } + + //Vec3V origLin0 = linVel0, origAng0 = angVel0, origLin1 = linVel1, origAng1 = angVel1; + + const PxU8* PX_RESTRICT last = desc.constraint + desc.constraintLengthOver16*16; + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + Vec3V linImpulse0 = V3Zero(), linImpulse1 = V3Zero(), angImpulse0 = V3Zero(), angImpulse1 = V3Zero(); + + while(currPtr < last) + { + SolverContactHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverContactPointExt* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPointExt); + + PxF32* appliedForceBuffer = reinterpret_cast(currPtr); + currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + SolverContactFrictionExt* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionExt); + + + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + const Vec3V contactNormal = Vec3V_From_Vec4V(hdr->normal_minAppliedImpulseForFrictionW); + const FloatV minNorImpulse = V4GetW(hdr->normal_minAppliedImpulseForFrictionW); + + const FloatV accumulatedNormalImpulse = FMax(solveExtContacts(contacts, numNormalConstr, contactNormal, linVel0, angVel0, linVel1, + angVel1, li0, ai0, li1, ai1, appliedForceBuffer), minNorImpulse); + + + if(cache.doFriction && numFrictionConstr) + { + Ps::prefetchLine(frictions); + const FloatV maxFrictionImpulse = FMul(hdr->getStaticFriction(), accumulatedNormalImpulse); + const FloatV maxDynFrictionImpulse = FMul(hdr->getDynamicFriction(), accumulatedNormalImpulse); + + BoolV broken = BFFFF(); + + for(PxU32 i=0;i maxFrictionImpulse + // clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse] + // (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce] + // set broken flag to true || broken flag + + // FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier); + // FloatV potentialSumF = FAdd(appliedForce, deltaF); + + const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1); + + // On XBox this clamping code uses the vector simple pipe rather than vector float, + // which eliminates a lot of stall cycles + + const BoolV clampLow = FIsGrtr(negMaxFrictionImpulse, totalImpulse); + const BoolV clampHigh = FIsGrtr(totalImpulse, maxFrictionImpulse); + + const FloatV totalClampedLow = FMax(negMaxDynFrictionImpulse, totalImpulse); + const FloatV totalClampedHigh = FMin(maxDynFrictionImpulse, totalImpulse); + + const FloatV newAppliedForce = FSel(clampLow, totalClampedLow, + FSel(clampHigh, totalClampedHigh, totalImpulse)); + + broken = BOr(broken, BOr(clampLow, clampHigh)); + + FloatV deltaF = FSub(newAppliedForce, appliedForce); + + linVel0 = V3ScaleAdd(f.linDeltaVA, deltaF, linVel0); + angVel0 = V3ScaleAdd(f.angDeltaVA, deltaF, angVel0); + linVel1 = V3ScaleAdd(f.linDeltaVB, deltaF, linVel1); + angVel1 = V3ScaleAdd(f.angDeltaVB, deltaF, angVel1); + + li0 = V3ScaleAdd(normal, deltaF, li0); ai0 = V3ScaleAdd(raXn, deltaF, ai0); + li1 = V3ScaleAdd(normal, deltaF, li1); ai1 = V3ScaleAdd(rbXn, deltaF, ai1); + +#if 0 + PxVec3 lv0, lv1, av0, av1; + V3StoreU(linVel0, lv0); V3StoreU(linVel1, lv1); + V3StoreU(angVel0, av0); V3StoreU(angVel1, av1); + + PX_ASSERT(lv0.magnitude() < 30.f); + PX_ASSERT(lv1.magnitude() < 30.f); + PX_ASSERT(av0.magnitude() < 30.f); + PX_ASSERT(av1.magnitude() < 30.f); +#endif + + f.setAppliedForce(newAppliedForce); + } + Store_From_BoolV(broken, &hdr->broken); + } + + linImpulse0 = V3ScaleAdd(li0, hdr->getDominance0(), linImpulse0); + angImpulse0 = V3ScaleAdd(ai0, FLoad(hdr->angDom0), angImpulse0); + linImpulse1 = V3NegScaleSub(li1, hdr->getDominance1(), linImpulse1); + angImpulse1 = V3NegScaleSub(ai1, FLoad(hdr->angDom1), angImpulse1); + } + + if(desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularState); + } + else + { + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, + linImpulse0, angImpulse0, cache.Z, cache.deltaV); + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularState); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, + linImpulse1, angImpulse1, cache.Z, cache.deltaV); + } + + PX_ASSERT(currPtr == last); +} + + +void solveExtContactBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtContact(desc[a], cache); + } +} + +void solveExtContactConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtContact(desc[a], cache); + concludeContact(desc[a], cache); + } +} + +void solveExtContactBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a].linkIndexA != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a].linkIndexB != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyBDataIndex]; + + solveExtContact(desc[a], cache); + writeBackContact(desc[a], cache, bd0, bd1); + } + if(cache.mThresholdStreamIndex > 0) + { + //Not enough space to write 4 more thresholds back! + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveExt1DBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExt1D(desc[a], cache); + } +} + +void solveExt1DConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExt1D(desc[a], cache); + conclude1D(desc[a], cache); + } +} + +void solveExt1DBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a].linkIndexA != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a].linkIndexB != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyBDataIndex]; + solveExt1D(desc[a], cache); + writeBack1D(desc[a], cache, bd0, bd1); + } +} + +void ext1DBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a].linkIndexA != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a].linkIndexB != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyBDataIndex]; + writeBack1D(desc[a], cache, bd0, bd1); + } +} + +void solveConcludeExtContact (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveExtContact(desc, cache); + concludeContact(desc, cache); +} + +void solveConcludeExt1D (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveExt1D(desc, cache); + conclude1D(desc, cache); +} + + +void solveConclude1D(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solve1D(desc, cache); + conclude1D(desc, cache); +} + +void solveConcludeContact (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveContact(desc, cache); + concludeContact(desc, cache); +} + +void solveConcludeContact_BStatic (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveContact_BStatic(desc, cache); + concludeContact(desc, cache); +} + + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp new file mode 100644 index 000000000..fb196cb62 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp @@ -0,0 +1,1227 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "PsFPU.h" + +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverContact.h" +#include "DySolverConstraint1D.h" +#include "DySolverConstraintDesc.h" +#include "DyThresholdTable.h" +#include "DySolverContext.h" +#include "PsUtilities.h" +#include "DyConstraint.h" +#include "PsAtomic.h" +#include "DySolverContact4.h" +#include "DySolverConstraint1D4.h" + +namespace physx +{ + +namespace Dy +{ + +static void solveContact4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& cache) +{ + PxSolverBody& b00 = *desc[0].bodyA; + PxSolverBody& b01 = *desc[0].bodyB; + PxSolverBody& b10 = *desc[1].bodyA; + PxSolverBody& b11 = *desc[1].bodyB; + PxSolverBody& b20 = *desc[2].bodyA; + PxSolverBody& b21 = *desc[2].bodyB; + PxSolverBody& b30 = *desc[3].bodyA; + PxSolverBody& b31 = *desc[3].bodyB; + + //We'll need this. + const Vec4V vZero = V4Zero(); + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&b01.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularState.x); + Vec4V angState01 = V4LoadA(&b01.angularState.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&b11.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularState.x); + Vec4V angState11 = V4LoadA(&b11.angularState.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&b21.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularState.x); + Vec4V angState21 = V4LoadA(&b21.angularState.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&b31.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularState.x); + Vec4V angState31 = V4LoadA(&b31.angularState.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2, linVel0T3; + Vec4V linVel1T0, linVel1T1, linVel1T2, linVel1T3; + Vec4V angState0T0, angState0T1, angState0T2, angState0T3; + Vec4V angState1T0, angState1T1, angState1T2, angState1T3; + + + PX_TRANSPOSE_44(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2, linVel0T3); + PX_TRANSPOSE_44(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2, linVel1T3); + PX_TRANSPOSE_44(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2, angState0T3); + PX_TRANSPOSE_44(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2, angState1T3); + + + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + Vec4V vMax = V4Splat(FMax()); + + const PxU8* PX_RESTRICT prefetchAddress = currPtr + sizeof(SolverContactHeader4) + sizeof(SolverContactBatchPointDynamic4); + + const SolverContactHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + const Vec4V invMassA = hdr->invMass0D0; + const Vec4V invMassB = hdr->invMass1D1; + + const Vec4V sumInvMass = V4Add(invMassA, invMassB); + + + while(currPtr < last) + { + + hdr = reinterpret_cast(currPtr); + + PX_ASSERT(hdr->type == DY_SC_TYPE_BLOCK_RB_CONTACT); + + currPtr = reinterpret_cast(const_cast(hdr) + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + Vec4V* appliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numNormalConstr; + + SolverContactBatchPointDynamic4* PX_RESTRICT contacts = reinterpret_cast(currPtr); + + Vec4V* maxImpulses; + currPtr = reinterpret_cast(contacts + numNormalConstr); + PxU32 maxImpulseMask = 0; + if(hasMaxImpulse) + { + maxImpulseMask = 0xFFFFFFFF; + maxImpulses = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V) * numNormalConstr; + } + else + { + maxImpulses = &vMax; + } + + + SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(currPtr); + if(numFrictionConstr) + currPtr += sizeof(SolverFrictionSharedData4); + + Vec4V* frictionAppliedForce = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numFrictionConstr; + + const SolverContactFrictionDynamic4* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionDynamic4); + + Vec4V accumulatedNormalImpulse = vZero; + + const Vec4V angD0 = hdr->angDom0; + const Vec4V angD1 = hdr->angDom1; + + const Vec4V _normalT0 = hdr->normalX; + const Vec4V _normalT1 = hdr->normalY; + const Vec4V _normalT2 = hdr->normalZ; + + Vec4V contactNormalVel1 = V4Mul(linVel0T0, _normalT0); + Vec4V contactNormalVel3 = V4Mul(linVel1T0, _normalT0); + contactNormalVel1 = V4MulAdd(linVel0T1, _normalT1, contactNormalVel1); + contactNormalVel3 = V4MulAdd(linVel1T1, _normalT1, contactNormalVel3); + contactNormalVel1 = V4MulAdd(linVel0T2, _normalT2, contactNormalVel1); + contactNormalVel3 = V4MulAdd(linVel1T2, _normalT2, contactNormalVel3); + + Vec4V relVel1 = V4Sub(contactNormalVel1, contactNormalVel3); + + Vec4V accumDeltaF = vZero; + + for(PxU32 i=0;istaticFriction; + const Vec4V dynamicFric = hdr->dynamicFriction; + + const Vec4V maxFrictionImpulse = V4Mul(staticFric, accumulatedNormalImpulse); + const Vec4V maxDynFrictionImpulse = V4Mul(dynamicFric, accumulatedNormalImpulse); + const Vec4V negMaxDynFrictionImpulse = V4Neg(maxDynFrictionImpulse); + //const Vec4V negMaxFrictionImpulse = V4Neg(maxFrictionImpulse); + BoolV broken = BFFFF(); + + if(cache.writeBackIteration) + { + Ps::prefetchLine(fd->frictionBrokenWritebackByte[0]); + Ps::prefetchLine(fd->frictionBrokenWritebackByte[1]); + Ps::prefetchLine(fd->frictionBrokenWritebackByte[2]); + } + + + for(PxU32 i=0;inormalX[i&1]; + const Vec4V normalT1 = fd->normalY[i&1]; + const Vec4V normalT2 = fd->normalZ[i&1]; + + Vec4V normalVel1 = V4Mul(linVel0T0, normalT0); + Vec4V normalVel2 = V4Mul(f.raXnX, angState0T0); + Vec4V normalVel3 = V4Mul(linVel1T0, normalT0); + Vec4V normalVel4 = V4Mul(f.rbXnX, angState1T0); + + normalVel1 = V4MulAdd(linVel0T1, normalT1, normalVel1); + normalVel2 = V4MulAdd(f.raXnY, angState0T1, normalVel2); + normalVel3 = V4MulAdd(linVel1T1, normalT1, normalVel3); + normalVel4 = V4MulAdd(f.rbXnY, angState1T1, normalVel4); + + normalVel1 = V4MulAdd(linVel0T2, normalT2, normalVel1); + normalVel2 = V4MulAdd(f.raXnZ, angState0T2, normalVel2); + normalVel3 = V4MulAdd(linVel1T2, normalT2, normalVel3); + normalVel4 = V4MulAdd(f.rbXnZ, angState1T2, normalVel4); + + const Vec4V _normalVel = V4Add(normalVel1, normalVel2); + const Vec4V __normalVel = V4Add(normalVel3, normalVel4); + + // appliedForce -bias * velMultiplier - a hoisted part of the total impulse computation + + const Vec4V normalVel = V4Sub(_normalVel, __normalVel ); + + const Vec4V tmp1 = V4Sub(appliedForce, f.scaledBias); + + const Vec4V totalImpulse = V4NegMulSub(normalVel, f.velMultiplier, tmp1); + + broken = BOr(broken, V4IsGrtr(V4Abs(totalImpulse), maxFrictionImpulse)); + + const Vec4V newAppliedForce = V4Sel(broken, V4Min(maxDynFrictionImpulse, V4Max(negMaxDynFrictionImpulse, totalImpulse)), totalImpulse); + + const Vec4V deltaF =V4Sub(newAppliedForce, appliedForce); + + frictionAppliedForce[i] = newAppliedForce; + + const Vec4V deltaFIM0 = V4Mul(deltaF, invMassA); + const Vec4V deltaFIM1 = V4Mul(deltaF, invMassB); + + const Vec4V angDetaF0 = V4Mul(deltaF, angD0); + const Vec4V angDetaF1 = V4Mul(deltaF, angD1); + + linVel0T0 = V4MulAdd(normalT0, deltaFIM0, linVel0T0); + linVel1T0 = V4NegMulSub(normalT0, deltaFIM1, linVel1T0); + angState0T0 = V4MulAdd(f.raXnX, angDetaF0, angState0T0); + angState1T0 = V4NegMulSub(f.rbXnX, angDetaF1, angState1T0); + + linVel0T1 = V4MulAdd(normalT1, deltaFIM0, linVel0T1); + linVel1T1 = V4NegMulSub(normalT1, deltaFIM1, linVel1T1); + angState0T1 = V4MulAdd(f.raXnY, angDetaF0, angState0T1); + angState1T1 = V4NegMulSub(f.rbXnY, angDetaF1, angState1T1); + + linVel0T2 = V4MulAdd(normalT2, deltaFIM0, linVel0T2); + linVel1T2 = V4NegMulSub(normalT2, deltaFIM1, linVel1T2); + angState0T2 = V4MulAdd(f.raXnZ, angDetaF0, angState0T2); + angState1T2 = V4NegMulSub(f.rbXnZ, angDetaF1, angState1T2); + } + fd->broken = broken; + } + } + + PX_TRANSPOSE_44(linVel0T0, linVel0T1, linVel0T2, linVel0T3, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_44(linVel1T0, linVel1T1, linVel1T2, linVel1T3, linVel01, linVel11, linVel21, linVel31); + PX_TRANSPOSE_44(angState0T0, angState0T1, angState0T2, angState0T3, angState00, angState10, angState20, angState30); + PX_TRANSPOSE_44(angState1T0, angState1T1, angState1T2, angState1T3, angState01, angState11, angState21, angState31); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularState.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularState.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularState.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularState.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularState.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularState.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularState.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularState.isFinite()); + + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(angState00, &b00.angularState.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(angState10, &b10.angularState.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(angState20, &b20.angularState.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + V4StoreA(angState30, &b30.angularState.x); + + if(desc[0].bodyBDataIndex != 0) + { + V4StoreA(linVel01, &b01.linearVelocity.x); + V4StoreA(angState01, &b01.angularState.x); + } + if(desc[1].bodyBDataIndex != 0) + { + V4StoreA(linVel11, &b11.linearVelocity.x); + V4StoreA(angState11, &b11.angularState.x); + } + if(desc[2].bodyBDataIndex != 0) + { + V4StoreA(linVel21, &b21.linearVelocity.x); + V4StoreA(angState21, &b21.angularState.x); + } + if(desc[3].bodyBDataIndex != 0) + { + V4StoreA(linVel31, &b31.linearVelocity.x); + V4StoreA(angState31, &b31.angularState.x); + } + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularState.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularState.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularState.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularState.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularState.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularState.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularState.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularState.isFinite()); +} + +static void solveContact4_StaticBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& cache) +{ + PxSolverBody& b00 = *desc[0].bodyA; + PxSolverBody& b10 = *desc[1].bodyA; + PxSolverBody& b20 = *desc[2].bodyA; + PxSolverBody& b30 = *desc[3].bodyA; + + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + + //We'll need this. + const Vec4V vZero = V4Zero(); + Vec4V vMax = V4Splat(FMax()); + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularState.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularState.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularState.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularState.x); + + Vec4V linVel0T0, linVel0T1, linVel0T2, linVel0T3; + Vec4V angState0T0, angState0T1, angState0T2, angState0T3; + + + PX_TRANSPOSE_44(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2, linVel0T3); + PX_TRANSPOSE_44(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2, angState0T3); + + const PxU8* PX_RESTRICT prefetchAddress = currPtr + sizeof(SolverContactHeader4) + sizeof(SolverContactBatchPointBase4); + + const SolverContactHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + const Vec4V invMass0 = hdr->invMass0D0; + + while((currPtr < last)) + { + hdr = reinterpret_cast(currPtr); + + PX_ASSERT(hdr->type == DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT); + + currPtr = const_cast(reinterpret_cast(hdr + 1)); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + Vec4V* appliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numNormalConstr; + + SolverContactBatchPointBase4* PX_RESTRICT contacts = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(contacts + numNormalConstr); + + Vec4V* maxImpulses; + PxU32 maxImpulseMask; + if(hasMaxImpulse) + { + maxImpulseMask = 0xFFFFFFFF; + maxImpulses = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V) * numNormalConstr; + } + else + { + maxImpulseMask = 0; + maxImpulses = &vMax; + } + + SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(currPtr); + if(numFrictionConstr) + currPtr += sizeof(SolverFrictionSharedData4); + + Vec4V* frictionAppliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numFrictionConstr; + + const SolverContactFrictionBase4* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionBase4); + + + Vec4V accumulatedNormalImpulse = vZero; + + const Vec4V angD0 = hdr->angDom0; + const Vec4V _normalT0 = hdr->normalX; + const Vec4V _normalT1 = hdr->normalY; + const Vec4V _normalT2 = hdr->normalZ; + + Vec4V contactNormalVel1 = V4Mul(linVel0T0, _normalT0); + contactNormalVel1 = V4MulAdd(linVel0T1, _normalT1, contactNormalVel1); + + contactNormalVel1 = V4MulAdd(linVel0T2, _normalT2, contactNormalVel1); + + Vec4V accumDeltaF = vZero; + + + for(PxU32 i=0;istaticFriction; + + const Vec4V dynamicFric = hdr->dynamicFriction; + + const Vec4V maxFrictionImpulse = V4Mul(staticFric, accumulatedNormalImpulse); + const Vec4V maxDynFrictionImpulse = V4Mul(dynamicFric, accumulatedNormalImpulse); + const Vec4V negMaxDynFrictionImpulse = V4Neg(maxDynFrictionImpulse); + + BoolV broken = BFFFF(); + + if(cache.writeBackIteration) + { + Ps::prefetchLine(fd->frictionBrokenWritebackByte[0]); + Ps::prefetchLine(fd->frictionBrokenWritebackByte[1]); + Ps::prefetchLine(fd->frictionBrokenWritebackByte[2]); + Ps::prefetchLine(fd->frictionBrokenWritebackByte[3]); + } + + for(PxU32 i=0;inormalX[i&1]; + const Vec4V normalT1 = fd->normalY[i&1]; + const Vec4V normalT2 = fd->normalZ[i&1]; + + Vec4V normalVel1 = V4Mul(linVel0T0, normalT0); + Vec4V normalVel2 = V4Mul(f.raXnX, angState0T0); + + normalVel1 = V4MulAdd(linVel0T1, normalT1, normalVel1); + normalVel2 = V4MulAdd(f.raXnY, angState0T1, normalVel2); + + normalVel1 = V4MulAdd(linVel0T2, normalT2, normalVel1); + normalVel2 = V4MulAdd(f.raXnZ, angState0T2, normalVel2); + + //relative normal velocity for all 4 constraints + const Vec4V normalVel = V4Add(normalVel1, normalVel2); + + // appliedForce -bias * velMultiplier - a hoisted part of the total impulse computation + const Vec4V tmp1 = V4Sub(appliedForce, f.scaledBias); + + const Vec4V totalImpulse = V4NegMulSub(normalVel, f.velMultiplier, tmp1); + + broken = BOr(broken, V4IsGrtr(V4Abs(totalImpulse), maxFrictionImpulse)); + + const Vec4V newAppliedForce = V4Sel(broken, V4Min(maxDynFrictionImpulse, V4Max(negMaxDynFrictionImpulse, totalImpulse)), totalImpulse); + + const Vec4V deltaF =V4Sub(newAppliedForce, appliedForce); + + const Vec4V deltaFInvMass = V4Mul(invMass0, deltaF); + const Vec4V angDeltaF = V4Mul(angD0, deltaF); + + linVel0T0 = V4MulAdd(normalT0, deltaFInvMass, linVel0T0); + angState0T0 = V4MulAdd(f.raXnX, angDeltaF, angState0T0); + + linVel0T1 = V4MulAdd(normalT1, deltaFInvMass, linVel0T1); + angState0T1 = V4MulAdd(f.raXnY, angDeltaF, angState0T1); + + linVel0T2 = V4MulAdd(normalT2, deltaFInvMass, linVel0T2); + angState0T2 = V4MulAdd(f.raXnZ, angDeltaF, angState0T2); + +#if 1 + frictionAppliedForces[i] = newAppliedForce; +#endif + + } + + fd->broken = broken; + } + } + + PX_TRANSPOSE_44(linVel0T0, linVel0T1, linVel0T2, linVel0T3, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_44(angState0T0, angState0T1, angState0T2, angState0T3, angState00, angState10, angState20, angState30); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularState.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularState.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularState.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularState.isFinite()); + + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + + V4StoreA(angState00, &b00.angularState.x); + V4StoreA(angState10, &b10.angularState.x); + V4StoreA(angState20, &b20.angularState.x); + V4StoreA(angState30, &b30.angularState.x); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularState.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularState.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularState.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularState.isFinite()); +} + +static void concludeContact4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& /*cache*/, PxU32 contactSize, PxU32 frictionSize) +{ + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + while((currPtr < last)) + { + const SolverContactHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + currPtr = const_cast(reinterpret_cast(hdr + 1)); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + currPtr += sizeof(Vec4V)*numNormalConstr; + + SolverContactBatchPointBase4* PX_RESTRICT contacts = reinterpret_cast(currPtr); + currPtr += (numNormalConstr * contactSize); + bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + if(hasMaxImpulse) + currPtr += sizeof(Vec4V) * numNormalConstr; + + currPtr += sizeof(Vec4V)*numFrictionConstr; + + SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(currPtr); + if(numFrictionConstr) + currPtr += sizeof(SolverFrictionSharedData4); + PX_UNUSED(fd); + + SolverContactFrictionBase4* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += (numFrictionConstr * frictionSize); + + for(PxU32 i=0;i((reinterpret_cast(contacts)) + contactSize); + c.biasedErr = V4Sub(c.biasedErr, c.scaledBias); + } + + for(PxU32 i=0;i((reinterpret_cast(frictions)) + frictionSize); + f.scaledBias = f.targetVelocity; + } + } +} + +void writeBackContact4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& cache, + const PxSolverBodyData** PX_RESTRICT bd0, const PxSolverBodyData** PX_RESTRICT bd1) +{ + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + PxReal* PX_RESTRICT vForceWriteback0 = reinterpret_cast(desc[0].writeBack); + PxReal* PX_RESTRICT vForceWriteback1 = reinterpret_cast(desc[1].writeBack); + PxReal* PX_RESTRICT vForceWriteback2 = reinterpret_cast(desc[2].writeBack); + PxReal* PX_RESTRICT vForceWriteback3 = reinterpret_cast(desc[3].writeBack); + + const PxU8 type = *desc[0].constraint; + const PxU32 contactSize = type == DY_SC_TYPE_BLOCK_RB_CONTACT ? sizeof(SolverContactBatchPointDynamic4) : sizeof(SolverContactBatchPointBase4); + const PxU32 frictionSize = type == DY_SC_TYPE_BLOCK_RB_CONTACT ? sizeof(SolverContactFrictionDynamic4) : sizeof(SolverContactFrictionBase4); + + + Vec4V normalForce = V4Zero(); + + + //We'll need this. + //const Vec4V vZero = V4Zero(); + + bool writeBackThresholds[4] = {false, false, false, false}; + + while((currPtr < last)) + { + SolverContactHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(hdr + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + Vec4V* PX_RESTRICT appliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numNormalConstr; + + //SolverContactBatchPointBase4* PX_RESTRICT contacts = (SolverContactBatchPointBase4*)currPtr; + currPtr += (numNormalConstr * contactSize); + + bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + if(hasMaxImpulse) + currPtr += sizeof(Vec4V) * numNormalConstr; + + SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(currPtr); + if(numFrictionConstr) + currPtr += sizeof(SolverFrictionSharedData4); + + currPtr += sizeof(Vec4V)*numFrictionConstr; + + //SolverContactFrictionBase4* PX_RESTRICT frictions = (SolverContactFrictionBase4*)currPtr; + currPtr += (numFrictionConstr * frictionSize); + + writeBackThresholds[0] = hdr->flags[0] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[1] = hdr->flags[1] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[2] = hdr->flags[2] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[3] = hdr->flags[3] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + + + for(PxU32 i=0;inumNormalConstr0) + FStore(appliedForce0, vForceWriteback0++); + if(vForceWriteback1 && i < hdr->numNormalConstr1) + FStore(appliedForce1, vForceWriteback1++); + if(vForceWriteback2 && i < hdr->numNormalConstr2) + FStore(appliedForce2, vForceWriteback2++); + if(vForceWriteback3 && i < hdr->numNormalConstr3) + FStore(appliedForce3, vForceWriteback3++); + } + + if(numFrictionConstr) + { + PX_ALIGN(16, PxU32 broken[4]); + BStoreA(fd->broken, broken); + + PxU8* frictionCounts = &hdr->numFrictionConstr0; + + for(PxU32 a = 0; a < 4; ++a) + { + if(frictionCounts[a] && broken[a]) + *fd->frictionBrokenWritebackByte[a] = 1; // PT: bad L2 miss here + } + } + } + + PX_ALIGN(16, PxReal nf[4]); + V4StoreA(normalForce, nf); + + Sc::ShapeInteraction** shapeInteractions = reinterpret_cast(desc[0].constraint)->shapeInteraction; + + for(PxU32 a = 0; a < 4; ++a) + { + if(writeBackThresholds[a] && desc[a].linkIndexA == PxSolverConstraintDesc::NO_LINK && desc[a].linkIndexB == PxSolverConstraintDesc::NO_LINK && + nf[a] !=0.f && (bd0[a]->reportThreshold < PX_MAX_REAL || bd1[a]->reportThreshold < PX_MAX_REAL)) + { + ThresholdStreamElement elt; + elt.normalForce = nf[a]; + elt.threshold = PxMin(bd0[a]->reportThreshold, bd1[a]->reportThreshold); + elt.nodeIndexA = IG::NodeIndex(bd0[a]->nodeIndex); + elt.nodeIndexB = IG::NodeIndex(bd1[a]->nodeIndex); + elt.shapeInteraction = shapeInteractions[a]; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + PX_ASSERT(cache.mThresholdStreamIndex(bPtr); + SolverConstraint1DDynamic4* PX_RESTRICT base = reinterpret_cast(header+1); + + //const FloatV fZero = FZero(); + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&b01.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularState.x); + Vec4V angState01 = V4LoadA(&b01.angularState.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&b11.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularState.x); + Vec4V angState11 = V4LoadA(&b11.angularState.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&b21.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularState.x); + Vec4V angState21 = V4LoadA(&b21.angularState.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&b31.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularState.x); + Vec4V angState31 = V4LoadA(&b31.angularState.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2, linVel0T3; + Vec4V linVel1T0, linVel1T1, linVel1T2, linVel1T3; + Vec4V angState0T0, angState0T1, angState0T2, angState0T3; + Vec4V angState1T0, angState1T1, angState1T2, angState1T3; + + + PX_TRANSPOSE_44(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2, linVel0T3); + PX_TRANSPOSE_44(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2, linVel1T3); + PX_TRANSPOSE_44(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2, angState0T3); + PX_TRANSPOSE_44(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2, angState1T3); + + const Vec4V invMass0D0 = header->invMass0D0; + const Vec4V invMass1D1 = header->invMass1D1; + + const Vec4V angD0 = header->angD0; + const Vec4V angD1 = header->angD1; + + PxU32 maxConstraints = header->count; + + for(PxU32 a = 0; a < maxConstraints; ++a) + { + SolverConstraint1DDynamic4& c = *base; + base++; + + Ps::prefetchLine(base); + Ps::prefetchLine(base, 64); + Ps::prefetchLine(base, 128); + Ps::prefetchLine(base, 192); + Ps::prefetchLine(base, 256); + + const Vec4V appliedForce = c.appliedForce; + + Vec4V linProj0(V4Mul(c.lin0X, linVel0T0)); + Vec4V linProj1(V4Mul(c.lin1X, linVel1T0)); + Vec4V angProj0(V4Mul(c.ang0X, angState0T0)); + Vec4V angProj1(V4Mul(c.ang1X, angState1T0)); + + linProj0 = V4MulAdd(c.lin0Y, linVel0T1, linProj0); + linProj1 = V4MulAdd(c.lin1Y, linVel1T1, linProj1); + angProj0 = V4MulAdd(c.ang0Y, angState0T1, angProj0); + angProj1 = V4MulAdd(c.ang1Y, angState1T1, angProj1); + + linProj0 = V4MulAdd(c.lin0Z, linVel0T2, linProj0); + linProj1 = V4MulAdd(c.lin1Z, linVel1T2, linProj1); + angProj0 = V4MulAdd(c.ang0Z, angState0T2, angProj0); + angProj1 = V4MulAdd(c.ang1Z, angState1T2, angProj1); + + const Vec4V projectVel0 = V4Add(linProj0, angProj0); + const Vec4V projectVel1 = V4Add(linProj1, angProj1); + + const Vec4V normalVel = V4Sub(projectVel0, projectVel1); + + const Vec4V unclampedForce = V4MulAdd(appliedForce, c.impulseMultiplier, V4MulAdd(normalVel, c.velMultiplier, c.constant)); + const Vec4V clampedForce = V4Max(c.minImpulse, V4Min(c.maxImpulse, unclampedForce)); + const Vec4V deltaF = V4Sub(clampedForce, appliedForce); + c.appliedForce = clampedForce; + + const Vec4V deltaFInvMass0 = V4Mul(deltaF, invMass0D0); + const Vec4V deltaFInvMass1 = V4Mul(deltaF, invMass1D1); + + const Vec4V angDeltaFInvMass0 = V4Mul(deltaF, angD0); + const Vec4V angDeltaFInvMass1 = V4Mul(deltaF, angD1); + + linVel0T0 = V4MulAdd(c.lin0X, deltaFInvMass0, linVel0T0); + linVel1T0 = V4NegMulSub(c.lin1X, deltaFInvMass1, linVel1T0); + angState0T0 = V4MulAdd(c.ang0X, angDeltaFInvMass0, angState0T0); + angState1T0 = V4NegMulSub(c.ang1X, angDeltaFInvMass1, angState1T0); + + linVel0T1 = V4MulAdd(c.lin0Y, deltaFInvMass0, linVel0T1); + linVel1T1 = V4NegMulSub(c.lin1Y, deltaFInvMass1, linVel1T1); + angState0T1 = V4MulAdd(c.ang0Y, angDeltaFInvMass0, angState0T1); + angState1T1 = V4NegMulSub(c.ang1Y, angDeltaFInvMass1, angState1T1); + + linVel0T2 = V4MulAdd(c.lin0Z, deltaFInvMass0, linVel0T2); + linVel1T2 = V4NegMulSub(c.lin1Z, deltaFInvMass1, linVel1T2); + angState0T2 = V4MulAdd(c.ang0Z, angDeltaFInvMass0, angState0T2); + angState1T2 = V4NegMulSub(c.ang1Z, angDeltaFInvMass1, angState1T2); + } + + PX_TRANSPOSE_44(linVel0T0, linVel0T1, linVel0T2, linVel0T3, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_44(linVel1T0, linVel1T1, linVel1T2, linVel1T3, linVel01, linVel11, linVel21, linVel31); + PX_TRANSPOSE_44(angState0T0, angState0T1, angState0T2, angState0T3, angState00, angState10, angState20, angState30); + PX_TRANSPOSE_44(angState1T0, angState1T1, angState1T2, angState1T3, angState01, angState11, angState21, angState31); + + + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + + V4StoreA(linVel01, &b01.linearVelocity.x); + V4StoreA(linVel11, &b11.linearVelocity.x); + V4StoreA(linVel21, &b21.linearVelocity.x); + V4StoreA(linVel31, &b31.linearVelocity.x); + + V4StoreA(angState00, &b00.angularState.x); + V4StoreA(angState10, &b10.angularState.x); + V4StoreA(angState20, &b20.angularState.x); + V4StoreA(angState30, &b30.angularState.x); + + V4StoreA(angState01, &b01.angularState.x); + V4StoreA(angState11, &b11.angularState.x); + V4StoreA(angState21, &b21.angularState.x); + V4StoreA(angState31, &b31.angularState.x); + +} + +static void conclude1D4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& /*cache*/) +{ + SolverConstraint1DHeader4* header = reinterpret_cast(desc[0].constraint); + PxU8* base = desc[0].constraint + sizeof(SolverConstraint1DHeader4); + PxU32 stride = header->type == DY_SC_TYPE_BLOCK_1D ? sizeof(SolverConstraint1DDynamic4) : sizeof(SolverConstraint1DBase4); + + for(PxU32 i=0; icount; i++) + { + SolverConstraint1DBase4& c = *reinterpret_cast(base); + c.constant = c.unbiasedConstant; + base += stride; + } + PX_ASSERT(desc[0].constraint + getConstraintLength(desc[0]) == base); +} + +void writeBack1D4(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& /*cache*/, + const PxSolverBodyData** PX_RESTRICT /*bd0*/, const PxSolverBodyData** PX_RESTRICT /*bd1*/) +{ + ConstraintWriteback* writeback0 = reinterpret_cast(desc[0].writeBack); + ConstraintWriteback* writeback1 = reinterpret_cast(desc[1].writeBack); + ConstraintWriteback* writeback2 = reinterpret_cast(desc[2].writeBack); + ConstraintWriteback* writeback3 = reinterpret_cast(desc[3].writeBack); + + if(writeback0 || writeback1 || writeback2 || writeback3) + { + SolverConstraint1DHeader4* header = reinterpret_cast(desc[0].constraint); + PxU8* base = desc[0].constraint + sizeof(SolverConstraint1DHeader4); + PxU32 stride = header->type == DY_SC_TYPE_BLOCK_1D ? sizeof(SolverConstraint1DDynamic4) : sizeof(SolverConstraint1DBase4); + + const Vec4V zero = V4Zero(); + Vec4V linX(zero), linY(zero), linZ(zero); + Vec4V angX(zero), angY(zero), angZ(zero); + + for(PxU32 i=0; icount; i++) + { + const SolverConstraint1DBase4* c = reinterpret_cast(base); + + //Load in flags + const VecI32V flags = I4LoadU(reinterpret_cast(&c->flags[0])); + //Work out masks + const VecI32V mask = I4Load(DY_SC_FLAG_OUTPUT_FORCE); + + const VecI32V masked = VecI32V_And(flags, mask); + const BoolV isEq = VecI32V_IsEq(masked, mask); + + const Vec4V appliedForce = V4Sel(isEq, c->appliedForce, zero); + + linX = V4MulAdd(c->lin0X, appliedForce, linX); + linY = V4MulAdd(c->lin0Y, appliedForce, linY); + linZ = V4MulAdd(c->lin0Z, appliedForce, linZ); + + angX = V4MulAdd(c->ang0WritebackX, appliedForce, angX); + angY = V4MulAdd(c->ang0WritebackY, appliedForce, angY); + angZ = V4MulAdd(c->ang0WritebackZ, appliedForce, angZ); + + base += stride; + } + + //We need to do the cross product now + + angX = V4Sub(angX, V4NegMulSub(header->body0WorkOffsetZ, linY, V4Mul(header->body0WorkOffsetY, linZ))); + angY = V4Sub(angY, V4NegMulSub(header->body0WorkOffsetX, linZ, V4Mul(header->body0WorkOffsetZ, linX))); + angZ = V4Sub(angZ, V4NegMulSub(header->body0WorkOffsetY, linX, V4Mul(header->body0WorkOffsetX, linY))); + + const Vec4V linLenSq = V4MulAdd(linZ, linZ, V4MulAdd(linY, linY, V4Mul(linX, linX))); + const Vec4V angLenSq = V4MulAdd(angZ, angZ, V4MulAdd(angY, angY, V4Mul(angX, angX))); + + const Vec4V linLen = V4Sqrt(linLenSq); + const Vec4V angLen = V4Sqrt(angLenSq); + + const BoolV broken = BOr(V4IsGrtr(linLen, header->linBreakImpulse), V4IsGrtr(angLen, header->angBreakImpulse)); + + PX_ALIGN(16, PxU32 iBroken[4]); + BStoreA(broken, iBroken); + + Vec4V lin0, lin1, lin2, lin3; + Vec4V ang0, ang1, ang2, ang3; + + PX_TRANSPOSE_34_44(linX, linY, linZ, lin0, lin1, lin2, lin3); + PX_TRANSPOSE_34_44(angX, angY, angZ, ang0, ang1, ang2, ang3); + + if(writeback0) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin0), writeback0->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang0), writeback0->angularImpulse); + writeback0->broken = header->break0 ? PxU32(iBroken[0] != 0) : 0; + } + if(writeback1) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin1), writeback1->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang1), writeback1->angularImpulse); + writeback1->broken = header->break1 ? PxU32(iBroken[1] != 0) : 0; + } + if(writeback2) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin2), writeback2->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang2), writeback2->angularImpulse); + writeback2->broken = header->break2 ? PxU32(iBroken[2] != 0) : 0; + } + if(writeback3) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin3), writeback3->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang3), writeback3->angularImpulse); + writeback3->broken = header->break3 ? PxU32(iBroken[3] != 0) : 0; + } + + PX_ASSERT(desc[0].constraint + getConstraintLength(desc[0]) == base); + } +} + + +void solveContactPreBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_Block(desc, cache); +} + +void solveContactPreBlock_Static(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_StaticBlock(desc, cache); +} + +void solveContactPreBlock_Conclude(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_Block(desc, cache); + concludeContact4_Block(desc, cache, sizeof(SolverContactBatchPointDynamic4), sizeof(SolverContactFrictionDynamic4)); +} + +void solveContactPreBlock_ConcludeStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_StaticBlock(desc, cache); + concludeContact4_Block(desc, cache, sizeof(SolverContactBatchPointBase4), sizeof(SolverContactFrictionBase4)); +} + +void solveContactPreBlock_WriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_Block(desc, cache); + + const PxSolverBodyData* bd0[4] = { &cache.solverBodyArray[desc[0].bodyADataIndex], + &cache.solverBodyArray[desc[1].bodyADataIndex], + &cache.solverBodyArray[desc[2].bodyADataIndex], + &cache.solverBodyArray[desc[3].bodyADataIndex]}; + + const PxSolverBodyData* bd1[4] = { &cache.solverBodyArray[desc[0].bodyBDataIndex], + &cache.solverBodyArray[desc[1].bodyBDataIndex], + &cache.solverBodyArray[desc[2].bodyBDataIndex], + &cache.solverBodyArray[desc[3].bodyBDataIndex]}; + + writeBackContact4_Block(desc, cache, bd0, bd1); + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveContactPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_StaticBlock(desc, cache); + const PxSolverBodyData* bd0[4] = { &cache.solverBodyArray[desc[0].bodyADataIndex], + &cache.solverBodyArray[desc[1].bodyADataIndex], + &cache.solverBodyArray[desc[2].bodyADataIndex], + &cache.solverBodyArray[desc[3].bodyADataIndex]}; + + const PxSolverBodyData* bd1[4] = { &cache.solverBodyArray[desc[0].bodyBDataIndex], + &cache.solverBodyArray[desc[1].bodyBDataIndex], + &cache.solverBodyArray[desc[2].bodyBDataIndex], + &cache.solverBodyArray[desc[3].bodyBDataIndex]}; + + writeBackContact4_Block(desc, cache, bd0, bd1); + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solve1D4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solve1D4_Block(desc, cache); +} + + +void solve1D4Block_Conclude(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solve1D4_Block(desc, cache); + conclude1D4_Block(desc, cache); +} + + +void solve1D4Block_WriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solve1D4_Block(desc, cache); + + const PxSolverBodyData* bd0[4] = { &cache.solverBodyArray[desc[0].bodyADataIndex], + &cache.solverBodyArray[desc[1].bodyADataIndex], + &cache.solverBodyArray[desc[2].bodyADataIndex], + &cache.solverBodyArray[desc[3].bodyADataIndex]}; + + const PxSolverBodyData* bd1[4] = { &cache.solverBodyArray[desc[0].bodyBDataIndex], + &cache.solverBodyArray[desc[1].bodyBDataIndex], + &cache.solverBodyArray[desc[2].bodyBDataIndex], + &cache.solverBodyArray[desc[3].bodyBDataIndex]}; + + writeBack1D4(desc, cache, bd0, bd1); +} + +void writeBack1D4Block(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + const PxSolverBodyData* bd0[4] = { &cache.solverBodyArray[desc[0].bodyADataIndex], + &cache.solverBodyArray[desc[1].bodyADataIndex], + &cache.solverBodyArray[desc[2].bodyADataIndex], + &cache.solverBodyArray[desc[3].bodyADataIndex]}; + + const PxSolverBodyData* bd1[4] = { &cache.solverBodyArray[desc[0].bodyBDataIndex], + &cache.solverBodyArray[desc[1].bodyBDataIndex], + &cache.solverBodyArray[desc[2].bodyBDataIndex], + &cache.solverBodyArray[desc[3].bodyBDataIndex]}; + + writeBack1D4(desc, cache, bd0, bd1); +} + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h new file mode 100644 index 000000000..ed3d9e838 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h @@ -0,0 +1,171 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_SOLVER_CORE_SHARED_H +#define DY_SOLVER_CORE_SHARED_H + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" + +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverContact.h" +#include "DySolverConstraint1D.h" +#include "DySolverConstraintDesc.h" +#include "PsUtilities.h" +#include "DyConstraint.h" +#include "PsAtomic.h" + + +namespace physx +{ + +namespace Dy +{ + PX_FORCE_INLINE static FloatV solveDynamicContacts(SolverContactPoint* contacts, const PxU32 nbContactPoints, const Vec3VArg contactNormal, + const FloatVArg invMassA, const FloatVArg invMassB, const FloatVArg angDom0, const FloatVArg angDom1, Vec3V& linVel0_, Vec3V& angState0_, + Vec3V& linVel1_, Vec3V& angState1_, PxF32* PX_RESTRICT forceBuffer) +{ + Vec3V linVel0 = linVel0_; + Vec3V angState0 = angState0_; + Vec3V linVel1 = linVel1_; + Vec3V angState1 = angState1_; + FloatV accumulatedNormalImpulse = FZero(); + + const Vec3V delLinVel0 = V3Scale(contactNormal, invMassA); + const Vec3V delLinVel1 = V3Scale(contactNormal, invMassB); + + for(PxU32 i=0;i +#include +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverConstraint1D.h" +#include "DySolverContact.h" +#include "DyThresholdTable.h" +#include "DySolverControl.h" +#include "DyArticulationHelper.h" +#include "PsAtomic.h" +#include "PsIntrinsics.h" +#include "DyArticulationPImpl.h" +#include "PsThread.h" +#include "DySolverConstraintDesc.h" +#include "DySolverContext.h" + + +namespace physx +{ + +namespace Dy +{ + +//----------------------------------- + +void solve1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContact_BStaticBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock_Static (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4_Block (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + + +void solve1DConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContact_BStaticConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock_Conclude (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock_ConcludeStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4Block_Conclude (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solve1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContact_BStaticBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4Block_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void writeBack1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void contactBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void extContactBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void ext1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void contactPreBlock_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void writeBack1D4Block (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +// could move this to PxPreprocessor.h but +// no implementation available for MSVC +#if PX_GCC_FAMILY +#define PX_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define PX_UNUSED_ATTRIBUTE +#endif + +#define DYNAMIC_ARTICULATION_REGISTRATION(x) 0 + +static SolveBlockMethod gVTableSolveBlock[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactBlock, // DY_SC_TYPE_RB_CONTACT + solve1DBlock, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactBlock), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DBlock), // DY_SC_TYPE_EXT_1D + solveContact_BStaticBlock, // DY_SC_TYPE_STATIC_CONTACT + solveContactBlock, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactPreBlock, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactPreBlock_Static, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4_Block, // DY_SC_TYPE_BLOCK_1D, +}; + +static SolveWriteBackBlockMethod gVTableSolveWriteBackBlock[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactBlockWriteBack, // DY_SC_TYPE_RB_CONTACT + solve1DBlockWriteBack, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactBlockWriteBack), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DBlockWriteBack), // DY_SC_TYPE_EXT_1D + solveContact_BStaticBlockWriteBack, // DY_SC_TYPE_STATIC_CONTACT + solveContactBlockWriteBack, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactPreBlock_WriteBack, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactPreBlock_WriteBackStatic, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4Block_WriteBack, // DY_SC_TYPE_BLOCK_1D, +}; + +static SolveBlockMethod gVTableSolveConcludeBlock[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactConcludeBlock, // DY_SC_TYPE_RB_CONTACT + solve1DConcludeBlock, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactConcludeBlock), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DConcludeBlock), // DY_SC_TYPE_EXT_1D + solveContact_BStaticConcludeBlock, // DY_SC_TYPE_STATIC_CONTACT + solveContactConcludeBlock, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactPreBlock_Conclude, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactPreBlock_ConcludeStatic, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4Block_Conclude, // DY_SC_TYPE_BLOCK_1D, +}; + +void SolverCoreRegisterArticulationFns() +{ + gVTableSolveBlock[DY_SC_TYPE_EXT_CONTACT] = solveExtContactBlock; + gVTableSolveBlock[DY_SC_TYPE_EXT_1D] = solveExt1DBlock; + + gVTableSolveWriteBackBlock[DY_SC_TYPE_EXT_CONTACT] = solveExtContactBlockWriteBack; + gVTableSolveWriteBackBlock[DY_SC_TYPE_EXT_1D] = solveExt1DBlockWriteBack; + gVTableSolveConcludeBlock[DY_SC_TYPE_EXT_CONTACT] = solveExtContactConcludeBlock; + gVTableSolveConcludeBlock[DY_SC_TYPE_EXT_1D] = solveExt1DConcludeBlock; +} + + +SolveBlockMethod* getSolveBlockTable() +{ + return gVTableSolveBlock; +} + +SolveBlockMethod* getSolverConcludeBlockTable() +{ + return gVTableSolveConcludeBlock; +} + +SolveWriteBackBlockMethod* getSolveWritebackBlockTable() +{ + return gVTableSolveWriteBackBlock; +} + + + + +SolverCoreGeneral* SolverCoreGeneral::create(bool fricEveryIteration) +{ + SolverCoreGeneral* scg = reinterpret_cast( + PX_ALLOC(sizeof(SolverCoreGeneral), "SolverCoreGeneral")); + + if (scg) + { + new (scg) SolverCoreGeneral; + scg->frictionEveryIteration = fricEveryIteration; + } + + return scg; +} + +void SolverCoreGeneral::destroyV() +{ + this->~SolverCoreGeneral(); + PX_FREE(this); +} + +void SolverCoreGeneral::solveV_Blocks(SolverIslandParams& params) const +{ + + const PxI32 TempThresholdStreamSize = 32; + ThresholdStreamElement tempThresholdStream[TempThresholdStreamSize]; + + SolverContext cache; + cache.solverBodyArray = params.bodyDataList; + cache.mThresholdStream = tempThresholdStream; + cache.mThresholdStreamLength = TempThresholdStreamSize; + cache.mThresholdStreamIndex = 0; + cache.writeBackIteration = false; + cache.Z = params.Z; + cache.deltaV = params.deltaV; + + PxI32 batchCount = PxI32(params.numConstraintHeaders); + + PxSolverBody* PX_RESTRICT bodyListStart = params.bodyListStart; + const PxU32 bodyListSize = params.bodyListSize; + + Cm::SpatialVector* PX_RESTRICT motionVelocityArray = params.motionVelocityArray; + + const PxU32 velocityIterations = params.velocityIterations; + const PxU32 positionIterations = params.positionIterations; + + const PxU32 numConstraintHeaders = params.numConstraintHeaders; + const PxU32 articulationListSize = params.articulationListSize; + + ArticulationSolverDesc* PX_RESTRICT articulationListStart = params.articulationListStart; + + PX_ASSERT(velocityIterations >= 1); + PX_ASSERT(positionIterations >= 1); + + if(numConstraintHeaders == 0) + { + for (PxU32 baIdx = 0; baIdx < bodyListSize; baIdx++) + { + Cm::SpatialVector& motionVel = motionVelocityArray[baIdx]; + PxSolverBody& atom = bodyListStart[baIdx]; + + motionVel.linear = atom.linearVelocity; + motionVel.angular = atom.angularState; + } + + //Even thought there are no external constraints, there may still be internal constraints in the articulations... + for(PxU32 i = 0; i < positionIterations; ++i) + for (PxU32 j = 0; j < articulationListSize; ++j) + articulationListStart[j].articulation->solveInternalConstraints(params.dt, params.invDt, cache.Z, cache.deltaV, false); + + for (PxU32 i = 0; i < articulationListSize; i++) + ArticulationPImpl::saveVelocity(articulationListStart[i], cache.deltaV); + + for (PxU32 i = 0; i < velocityIterations; ++i) + for (PxU32 j = 0; j < articulationListSize; ++j) + articulationListStart[j].articulation->solveInternalConstraints(params.dt, params.invDt, cache.Z, cache.deltaV, true); + + return; + } + + BatchIterator contactIterator(params.constraintBatchHeaders, params.numConstraintHeaders); + + PxSolverConstraintDesc* PX_RESTRICT constraintList = params.constraintList; + + //0-(n-1) iterations + PxI32 normalIter = 0; + + for (PxU32 iteration = positionIterations; iteration > 0; iteration--) //decreasing positive numbers == position iters + { + cache.doFriction = this->frictionEveryIteration ? true : iteration <= 3; + + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, iteration == 1 ? gVTableSolveConcludeBlock : gVTableSolveBlock, normalIter); + + for (PxU32 i = 0; i < articulationListSize; ++i) + articulationListStart[i].articulation->solveInternalConstraints(params.dt, params.invDt, cache.Z, cache.deltaV, false); + + ++normalIter; + } + + for (PxU32 baIdx = 0; baIdx < bodyListSize; baIdx++) + { + const PxSolverBody& atom = bodyListStart[baIdx]; + Cm::SpatialVector& motionVel = motionVelocityArray[baIdx]; + motionVel.linear = atom.linearVelocity; + motionVel.angular = atom.angularState; + } + + + for (PxU32 i = 0; i < articulationListSize; i++) + ArticulationPImpl::saveVelocity(articulationListStart[i], cache.deltaV); + + + const PxI32 velItersMinOne = (PxI32(velocityIterations)) - 1; + + PxI32 iteration = 0; + + for(; iteration < velItersMinOne; ++iteration) + { + + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, gVTableSolveBlock, normalIter); + + for (PxU32 i = 0; i < articulationListSize; ++i) + articulationListStart[i].articulation->solveInternalConstraints(params.dt, params.invDt, cache.Z, cache.deltaV, true); + ++normalIter; + + } + + PxI32* outThresholdPairs = params.outThresholdPairs; + ThresholdStreamElement* PX_RESTRICT thresholdStream = params.thresholdStream; + PxU32 thresholdStreamLength = params.thresholdStreamLength; + + cache.writeBackIteration = true; + cache.mSharedThresholdStream = thresholdStream; + cache.mSharedThresholdStreamLength = thresholdStreamLength; + cache.mSharedOutThresholdPairs = outThresholdPairs; + for(; iteration < PxI32(velocityIterations); ++iteration) + { + + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, gVTableSolveWriteBackBlock, normalIter); + + for (PxU32 i = 0; i < articulationListSize; ++i) + articulationListStart[i].articulation->solveInternalConstraints(params.dt, params.invDt, cache.Z, cache.deltaV, true); + + ++normalIter; + + } + + //Write back remaining threshold streams + if(cache.mThresholdStreamIndex > 0) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(outThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 b = 0; b < cache.mThresholdStreamIndex; ++b) + { + thresholdStream[b + threshIndex] = cache.mThresholdStream[b]; + } + cache.mThresholdStreamIndex = 0; + } +} + +PxI32 SolverCoreGeneral::solveVParallelAndWriteBack +(SolverIslandParams& params, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) const +{ +#if PX_PROFILE_SOLVE_STALLS + PxU64 startTime = readTimer(); + + PxU64 stallCount = 0; +#endif + + SolverContext cache; + cache.solverBodyArray = params.bodyDataList; + const PxU32 batchSize = params.batchSize; + + const PxI32 UnrollCount = PxI32(batchSize); + const PxI32 ArticCount = 2; + const PxI32 SaveUnrollCount = 32; + + const PxI32 TempThresholdStreamSize = 32; + ThresholdStreamElement tempThresholdStream[TempThresholdStreamSize]; + + const PxI32 bodyListSize = PxI32(params.bodyListSize); + const PxI32 articulationListSize = PxI32(params.articulationListSize); + + + const PxI32 batchCount = PxI32(params.numConstraintHeaders); + cache.mThresholdStream = tempThresholdStream; + cache.mThresholdStreamLength = TempThresholdStreamSize; + cache.mThresholdStreamIndex = 0; + cache.writeBackIteration = false; + cache.Z = Z; + cache.deltaV = deltaV; + + const PxReal dt = params.dt; + const PxReal invDt = params.invDt; + + const PxI32 positionIterations = PxI32(params.positionIterations); + const PxI32 velocityIterations = PxI32(params.velocityIterations); + + PxI32* constraintIndex = ¶ms.constraintIndex; + PxI32* constraintIndex2 = ¶ms.constraintIndex2; + + PxI32* articIndex = ¶ms.articSolveIndex; + PxI32* articIndex2 = ¶ms.articSolveIndex2; + + PxSolverConstraintDesc* PX_RESTRICT constraintList = params.constraintList; + + ArticulationSolverDesc* PX_RESTRICT articulationListStart = params.articulationListStart; + + const PxU32 nbPartitions = params.nbPartitions; + + PxU32* headersPerPartition = params.headersPerPartition; + + PX_UNUSED(velocityIterations); + + PX_ASSERT(velocityIterations >= 1); + PX_ASSERT(positionIterations >= 1); + + PxI32 endIndexCount = UnrollCount; + PxI32 index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + + PxI32 articSolveStart = 0; + PxI32 articSolveEnd = 0; + PxI32 maxArticIndex = 0; + PxI32 articIndexCounter = 0; + + BatchIterator contactIter(params.constraintBatchHeaders, params.numConstraintHeaders); + + PxI32 maxNormalIndex = 0; + PxI32 normalIteration = 0; + PxU32 a = 0; + PxI32 targetConstraintIndex = 0; + PxI32 targetArticIndex = 0; + + for(PxU32 i = 0; i < 2; ++i) + { + SolveBlockMethod* solveTable = i == 0 ? gVTableSolveBlock : gVTableSolveConcludeBlock; + for(; a < positionIterations - 1 + i; ++a) + { + WAIT_FOR_PROGRESS(articIndex2, targetArticIndex); + + cache.doFriction = this->frictionEveryIteration ? true : (positionIterations - a) <= 3; + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxNormalIndex += headersPerPartition[b]; + + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, solveTable, + normalIteration); + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(constraintIndex2, nbSolved); + } + targetConstraintIndex += headersPerPartition[b]; //Increment target constraint index by batch count + } + + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxArticIndex += articulationListSize; + targetArticIndex += articulationListSize; + + while (articSolveStart < maxArticIndex) + { + + PxI32 endIdx = PxMin(articSolveEnd, maxArticIndex); + + PxI32 nbSolved = 0; + while (articSolveStart < endIdx) + { + articulationListStart[articSolveStart - articIndexCounter].articulation->solveInternalConstraints(dt, invDt, cache.Z, cache.deltaV, false); + articSolveStart++; + nbSolved++; + } + + if (nbSolved) + { + physx::shdfnd::atomicAdd(articIndex2, nbSolved); + } + + PxI32 remaining = articSolveEnd - articSolveStart; + + if (remaining == 0) + { + articSolveStart = physx::shdfnd::atomicAdd(articIndex, ArticCount) - ArticCount; + articSolveEnd = articSolveStart + ArticCount; + } + + } + + articIndexCounter += articulationListSize; + + ++normalIteration; + } + } + + PxI32* bodyListIndex = ¶ms.bodyListIndex; + PxI32* bodyListIndex2 = ¶ms.bodyListIndex2; + + PxSolverBody* PX_RESTRICT bodyListStart = params.bodyListStart; + Cm::SpatialVector* PX_RESTRICT motionVelocityArray = params.motionVelocityArray; + + + //Save velocity - articulated + PxI32 endIndexCount2 = SaveUnrollCount; + PxI32 index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount; + { + WAIT_FOR_PROGRESS(articIndex2, targetArticIndex); + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + PxI32 nbConcluded = 0; + while(index2 < articulationListSize) + { + const PxI32 remainder = PxMin(SaveUnrollCount, (articulationListSize - index2)); + endIndexCount2 -= remainder; + for(PxI32 b = 0; b < remainder; ++b, ++index2) + { + ArticulationPImpl::saveVelocity(articulationListStart[index2], cache.deltaV); + } + if(endIndexCount2 == 0) + { + index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount; + endIndexCount2 = SaveUnrollCount; + } + nbConcluded += remainder; + } + + index2 -= articulationListSize; + + //save velocity + + + while(index2 < bodyListSize) + { + const PxI32 remainder = PxMin(endIndexCount2, (bodyListSize - index2)); + endIndexCount2 -= remainder; + for(PxI32 b = 0; b < remainder; ++b, ++index2) + { + Ps::prefetchLine(&bodyListStart[index2 + 8]); + Ps::prefetchLine(&motionVelocityArray[index2 + 8]); + PxSolverBody& body = bodyListStart[index2]; + Cm::SpatialVector& motionVel = motionVelocityArray[index2]; + motionVel.linear = body.linearVelocity; + motionVel.angular = body.angularState; + PX_ASSERT(motionVel.linear.isFinite()); + PX_ASSERT(motionVel.angular.isFinite()); + } + + nbConcluded += remainder; + + //Branch not required because this is the last time we use this atomic variable + //if(index2 < articulationListSizePlusbodyListSize) + { + index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount - articulationListSize; + endIndexCount2 = SaveUnrollCount; + } + } + + if(nbConcluded) + { + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(bodyListIndex2, nbConcluded); + } + } + + + WAIT_FOR_PROGRESS(bodyListIndex2, (bodyListSize + articulationListSize)); + + a = 1; + for(; a < params.velocityIterations; ++a) + { + WAIT_FOR_PROGRESS(articIndex2, targetArticIndex); + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxNormalIndex += headersPerPartition[b]; + + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, gVTableSolveBlock, + normalIteration); + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(constraintIndex2, nbSolved); + } + targetConstraintIndex += headersPerPartition[b]; //Increment target constraint index by batch count + } + + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxArticIndex += articulationListSize; + targetArticIndex += articulationListSize; + + while (articSolveStart < maxArticIndex) + { + + PxI32 endIdx = PxMin(articSolveEnd, maxArticIndex); + + PxI32 nbSolved = 0; + while (articSolveStart < endIdx) + { + articulationListStart[articSolveStart - articIndexCounter].articulation->solveInternalConstraints(dt, invDt, cache.Z, cache.deltaV, true); + articSolveStart++; + nbSolved++; + } + + if (nbSolved) + { + physx::shdfnd::atomicAdd(articIndex2, nbSolved); + } + + PxI32 remaining = articSolveEnd - articSolveStart; + + if (remaining == 0) + { + articSolveStart = physx::shdfnd::atomicAdd(articIndex, ArticCount) - ArticCount; + articSolveEnd = articSolveStart + ArticCount; + } + + } + ++normalIteration; + articIndexCounter += articulationListSize; + } + + ThresholdStreamElement* PX_RESTRICT thresholdStream = params.thresholdStream; + PxU32 thresholdStreamLength = params.thresholdStreamLength; + PxI32* outThresholdPairs = params.outThresholdPairs; + + cache.mSharedOutThresholdPairs = outThresholdPairs; + cache.mSharedThresholdStream = thresholdStream; + cache.mSharedThresholdStreamLength = thresholdStreamLength; + + //Last iteration - do writeback as well! + cache.writeBackIteration = true; + { + WAIT_FOR_PROGRESS(articIndex2, targetArticIndex); + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxNormalIndex += headersPerPartition[b]; + + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, gVTableSolveWriteBackBlock, + normalIteration); + + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(constraintIndex2, nbSolved); + } + targetConstraintIndex += headersPerPartition[b]; //Increment target constraint index by batch count + } + { + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxArticIndex += articulationListSize; + targetArticIndex += articulationListSize; + + while (articSolveStart < maxArticIndex) + { + + PxI32 endIdx = PxMin(articSolveEnd, maxArticIndex); + + PxI32 nbSolved = 0; + while (articSolveStart < endIdx) + { + articulationListStart[articSolveStart - articIndexCounter].articulation->solveInternalConstraints(dt, invDt, cache.Z, cache.deltaV, false); + articSolveStart++; + nbSolved++; + } + + if (nbSolved) + { + physx::shdfnd::atomicAdd(articIndex2, nbSolved); + } + + PxI32 remaining = articSolveEnd - articSolveStart; + + if (remaining == 0) + { + articSolveStart = physx::shdfnd::atomicAdd(articIndex, ArticCount) - ArticCount; + articSolveEnd = articSolveStart + ArticCount; + } + + } + } + + if(cache.mThresholdStreamIndex > 0) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(outThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 b = 0; b < cache.mThresholdStreamIndex; ++b) + { + thresholdStream[b + threshIndex] = cache.mThresholdStream[b]; + } + cache.mThresholdStreamIndex = 0; + } + + ++normalIteration; + + } + +#if PX_PROFILE_SOLVE_STALLS + + + PxU64 endTime = readTimer(); + PxReal totalTime = (PxReal)(endTime - startTime); + PxReal stallTime = (PxReal)stallCount; + PxReal stallRatio = stallTime/totalTime; + if(0)//stallRatio > 0.2f) + { + LARGE_INTEGER frequency; + QueryPerformanceFrequency( &frequency ); + printf("Warning -- percentage time stalled = %f; stalled for %f seconds; total Time took %f seconds\n", + stallRatio * 100.f, stallTime/(PxReal)frequency.QuadPart, totalTime/(PxReal)frequency.QuadPart); + } +#endif + + return normalIteration * batchCount; + +} + + +void SolverCoreGeneral::writeBackV +(const PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxU32 /*constraintListSize*/, PxConstraintBatchHeader* batchHeaders, const PxU32 numBatches, + ThresholdStreamElement* PX_RESTRICT thresholdStream, const PxU32 thresholdStreamLength, PxU32& outThresholdPairs, + PxSolverBodyData* atomListData, WriteBackBlockMethod writeBackTable[]) const +{ + SolverContext cache; + cache.solverBodyArray = atomListData; + cache.mThresholdStream = thresholdStream; + cache.mThresholdStreamLength = thresholdStreamLength; + cache.mThresholdStreamIndex = 0; + + PxI32 outThreshIndex = 0; + for(PxU32 j = 0; j < numBatches; ++j) + { + PxU8 type = *constraintList[batchHeaders[j].mStartIndex].constraint; + writeBackTable[type](constraintList + batchHeaders[j].mStartIndex, + batchHeaders[j].mStride, cache); + } + + outThresholdPairs = PxU32(outThreshIndex); +} + +void solveVBlock(SOLVEV_BLOCK_METHOD_ARGS) +{ + solverCore->solveV_Blocks(params); +} + +} +} + + +//#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h new file mode 100644 index 000000000..f9315a33c --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h @@ -0,0 +1,167 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCOREGENERAL_H +#define DY_SOLVERCOREGENERAL_H + +#include "DySolverCore.h" +#include "DySolverConstraintDesc.h" + +namespace physx +{ + +namespace Dy +{ + +struct FsData; + +inline void BusyWaitState(volatile PxU32* state, const PxU32 requiredState) +{ + while(requiredState != *state ); +} + +inline void WaitBodyRequiredState(PxU32* state, const PxU32 requiredState) +{ + if(*state != requiredState) + { + BusyWaitState(state, requiredState); + } +} + +inline void BusyWaitStates(volatile PxU32* stateA, volatile PxU32* stateB, const PxU32 requiredStateA, const PxU32 requiredStateB) +{ + while(*stateA != requiredStateA); + while(*stateB != requiredStateB); +} + + +class BatchIterator +{ +public: + PxConstraintBatchHeader* constraintBatchHeaders; + PxU32 mSize; + PxU32 mCurrentIndex; + + BatchIterator(PxConstraintBatchHeader* _constraintBatchHeaders, PxU32 size) : constraintBatchHeaders(_constraintBatchHeaders), + mSize(size), mCurrentIndex(0) + { + } + + PX_FORCE_INLINE const PxConstraintBatchHeader& GetCurrentHeader(const PxU32 constraintIndex) + { + PxU32 currentIndex = mCurrentIndex; + while((constraintIndex - constraintBatchHeaders[currentIndex].mStartIndex) >= constraintBatchHeaders[currentIndex].mStride) + currentIndex = (currentIndex + 1)%mSize; + Ps::prefetchLine(&constraintBatchHeaders[currentIndex], 128); + mCurrentIndex = currentIndex; + return constraintBatchHeaders[currentIndex]; + } +private: + BatchIterator& operator=(const BatchIterator&); +}; + + +inline void SolveBlockParallel (PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxI32 batchCount, const PxI32 index, + const PxI32 headerCount, SolverContext& cache, BatchIterator& iterator, + SolveBlockMethod solveTable[], + const PxI32 iteration + ) +{ + const PxI32 indA = index - (iteration * headerCount); + + const PxConstraintBatchHeader* PX_RESTRICT headers = iterator.constraintBatchHeaders; + + const PxI32 endIndex = indA + batchCount; + for(PxI32 i = indA; i < endIndex; ++i) + { + const PxConstraintBatchHeader& header = headers[i]; + + const PxI32 numToGrab = header.mStride; + PxSolverConstraintDesc* PX_RESTRICT block = &constraintList[header.mStartIndex]; + + Ps::prefetch(block[0].constraint, 384); + + for(PxI32 b = 0; b < numToGrab; ++b) + { + Ps::prefetchLine(block[b].bodyA); + Ps::prefetchLine(block[b].bodyB); + } + + //OK. We have a number of constraints to run... + solveTable[header.mConstraintType](block, PxU32(numToGrab), cache); + } +} + + + + +class SolverCoreGeneral : public SolverCore +{ +public: + bool frictionEveryIteration; + static SolverCoreGeneral* create(bool fricEveryIteration); + + // Implements SolverCore + virtual void destroyV(); + + virtual PxI32 solveVParallelAndWriteBack + (SolverIslandParams& params, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) const; + + virtual void solveV_Blocks + (SolverIslandParams& params) const; + + virtual void writeBackV + (const PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxU32 constraintListSize, PxConstraintBatchHeader* contactConstraintBatches, const PxU32 numBatches, + ThresholdStreamElement* PX_RESTRICT thresholdStream, const PxU32 thresholdStreamLength, PxU32& outThresholdPairs, + PxSolverBodyData* atomListData, WriteBackBlockMethod writeBackTable[]) const; + +private: + + //~Implements SolverCore +}; + +#define SOLVEV_BLOCK_METHOD_ARGS \ + SolverCore* solverCore, \ + SolverIslandParams& params + +void solveVBlock(SOLVEV_BLOCK_METHOD_ARGS); + +SolveBlockMethod* getSolveBlockTable(); + +SolveBlockMethod* getSolverConcludeBlockTable(); + +SolveWriteBackBlockMethod* getSolveWritebackBlockTable(); + + +} + +} + +#endif //DY_SOLVERCOREGENERAL_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp new file mode 100644 index 000000000..853ce9e15 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp @@ -0,0 +1,760 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "PsAllocator.h" +#include +#include +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverConstraint1D.h" +#include "DySolverContact.h" +#include "DyThresholdTable.h" +#include "DySolverControl.h" +#include "DyArticulationHelper.h" +#include "PsAtomic.h" +#include "PsIntrinsics.h" +#include "DyArticulationPImpl.h" +#include "PsThread.h" +#include "DySolverConstraintDesc.h" +#include "DySolverContext.h" +#include "DySolverControlPF.h" + +namespace physx +{ + +namespace Dy +{ +//----------------------------------- + +void solve1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4_Block (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + + +void solve1DConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4Block_Conclude (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solve1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4Block_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void writeBack1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void ext1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void writeBack1D4Block (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + + +void solveFrictionBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFriction_BStaticBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtFrictionBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactCoulombBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulomb_BStaticBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + + +void solveContactCoulombConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactCoulombConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulomb_BStaticConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solveContactCoulombBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactCoulombBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulomb_BStaticBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFrictionBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFriction_BStaticBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtFrictionBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +//Pre-block 1d/2d friction stuff... + +void solveContactCoulombPreBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombPreBlock_Static (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombPreBlock_Conclude (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombPreBlock_ConcludeStatic (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombPreBlock_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFrictionCoulombPreBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solveFrictionCoulombPreBlock_Static (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFrictionCoulombPreBlock_Conclude (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFrictionCoulombPreBlock_ConcludeStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solveFrictionCoulombPreBlock_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solveFrictionCoulombPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + + +// could move this to PxPreprocessor.h but +// no implementation available for MSVC +#if PX_GCC_FAMILY +#define PX_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define PX_UNUSED_ATTRIBUTE +#endif + +#define DYNAMIC_ARTICULATION_REGISTRATION(x) 0 + + +static SolveBlockMethod gVTableSolveBlockCoulomb[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactCoulombBlock, // DY_SC_TYPE_RB_CONTACT + solve1DBlock, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactCoulombBlock), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DBlock), // DY_SC_TYPE_EXT_1D + solveContactCoulomb_BStaticBlock, // DY_SC_TYPE_STATIC_CONTACT + solveContactCoulombBlock, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactCoulombPreBlock, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactCoulombPreBlock_Static, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4_Block, // DY_SC_TYPE_BLOCK_1D, + solveFrictionBlock, // DY_SC_TYPE_FRICTION_CONSTRAINT + solveFriction_BStaticBlock, // DY_SC_TYPE_STATIC_FRICTION_CONSTRAINT + DYNAMIC_ARTICULATION_REGISTRATION(solveExtFrictionBlock), // DY_SC_TYPE_EXT_FRICTION_CONSTRAINT + solveFrictionCoulombPreBlock, // DY_SC_TYPE_BLOCK_FRICTION + solveFrictionCoulombPreBlock_Static // DY_SC_TYPE_BLOCK_STATIC_FRICTION +}; + +static SolveWriteBackBlockMethod gVTableSolveWriteBackBlockCoulomb[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactCoulombBlockWriteBack, // DY_SC_TYPE_RB_CONTACT + solve1DBlockWriteBack, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactCoulombBlockWriteBack), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DBlockWriteBack), // DY_SC_TYPE_EXT_1D + solveContactCoulomb_BStaticBlockWriteBack, // DY_SC_TYPE_STATIC_CONTACT + solveContactCoulombBlockWriteBack, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactCoulombPreBlock_WriteBack, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactCoulombPreBlock_WriteBackStatic, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4Block_WriteBack, // DY_SC_TYPE_BLOCK_1D, + solveFrictionBlockWriteBack, // DY_SC_TYPE_FRICTION_CONSTRAINT + solveFriction_BStaticBlockWriteBack, // DY_SC_TYPE_STATIC_FRICTION_CONSTRAINT + DYNAMIC_ARTICULATION_REGISTRATION(solveExtFrictionBlockWriteBack), // DY_SC_TYPE_EXT_FRICTION_CONSTRAINT + solveFrictionCoulombPreBlock_WriteBack, // DY_SC_TYPE_BLOCK_FRICTION + solveFrictionCoulombPreBlock_WriteBackStatic // DY_SC_TYPE_BLOCK_STATIC_FRICTION +}; + + +static SolveBlockMethod gVTableSolveConcludeBlockCoulomb[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactCoulombConcludeBlock, // DY_SC_TYPE_RB_CONTACT + solve1DConcludeBlock, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactCoulombConcludeBlock), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DConcludeBlock), // DY_SC_TYPE_EXT_1D + solveContactCoulomb_BStaticConcludeBlock, // DY_SC_TYPE_STATIC_CONTACT + solveContactCoulombConcludeBlock, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactCoulombPreBlock_Conclude, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactCoulombPreBlock_ConcludeStatic, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4Block_Conclude, // DY_SC_TYPE_BLOCK_1D, + solveFrictionBlock, // DY_SC_TYPE_FRICTION_CONSTRAINT + solveFriction_BStaticBlock, // DY_SC_TYPE_STATIC_FRICTION_CONSTRAINT + DYNAMIC_ARTICULATION_REGISTRATION(solveExtFrictionBlock), // DY_SC_TYPE_EXT_FRICTION_CONSTRAINT + solveFrictionCoulombPreBlock_Conclude, // DY_SC_TYPE_BLOCK_FRICTION + solveFrictionCoulombPreBlock_ConcludeStatic // DY_SC_TYPE_BLOCK_STATIC_FRICTION +}; + + +void SolverCoreRegisterArticulationFnsCoulomb() +{ + gVTableSolveBlockCoulomb[DY_SC_TYPE_EXT_CONTACT] = solveExtContactCoulombBlock; + gVTableSolveBlockCoulomb[DY_SC_TYPE_EXT_1D] = solveExt1DBlock; + + gVTableSolveWriteBackBlockCoulomb[DY_SC_TYPE_EXT_CONTACT] = solveExtContactCoulombBlockWriteBack; + gVTableSolveWriteBackBlockCoulomb[DY_SC_TYPE_EXT_1D] = solveExt1DBlockWriteBack; + gVTableSolveConcludeBlockCoulomb[DY_SC_TYPE_EXT_CONTACT] = solveExtContactCoulombConcludeBlock; + gVTableSolveConcludeBlockCoulomb[DY_SC_TYPE_EXT_1D] = solveExt1DConcludeBlock; + + gVTableSolveBlockCoulomb[DY_SC_TYPE_EXT_FRICTION] = solveExtFrictionBlock; + gVTableSolveWriteBackBlockCoulomb[DY_SC_TYPE_EXT_FRICTION] = solveExtFrictionBlockWriteBack; + gVTableSolveConcludeBlockCoulomb[DY_SC_TYPE_EXT_FRICTION] = solveExtFrictionBlock; +} + +SolverCoreGeneralPF* SolverCoreGeneralPF::create() +{ + SolverCoreGeneralPF* scg = reinterpret_cast( + PX_ALLOC(sizeof(SolverCoreGeneralPF), "SolverCoreGeneral")); + + if(scg) + new (scg) SolverCoreGeneralPF; + + return scg; +} + +void SolverCoreGeneralPF::destroyV() +{ + this->~SolverCoreGeneralPF(); + PX_FREE(this); +} + +void SolverCoreGeneralPF::solveV_Blocks(SolverIslandParams& params) const +{ + const PxI32 TempThresholdStreamSize = 32; + ThresholdStreamElement tempThresholdStream[TempThresholdStreamSize]; + + SolverContext cache; + cache.solverBodyArray = params.bodyDataList; + cache.mThresholdStream = tempThresholdStream; + cache.mThresholdStreamLength = TempThresholdStreamSize; + cache.mThresholdStreamIndex = 0; + cache.writeBackIteration = false; + cache.deltaV = params.deltaV; + cache.Z = params.Z; + + PxI32 batchCount = PxI32(params.numConstraintHeaders); + + PxSolverBody* PX_RESTRICT bodyListStart = params.bodyListStart; + const PxU32 bodyListSize = params.bodyListSize; + + Cm::SpatialVector* PX_RESTRICT motionVelocityArray = params.motionVelocityArray; + + const PxU32 velocityIterations = params.velocityIterations; + const PxU32 positionIterations = params.positionIterations; + + const PxU32 numConstraintHeaders = params.numConstraintHeaders; + const PxU32 articulationListSize = params.articulationListSize; + + ArticulationSolverDesc* PX_RESTRICT articulationListStart = params.articulationListStart; + + + PX_ASSERT(velocityIterations >= 1); + PX_ASSERT(positionIterations >= 1); + + if(numConstraintHeaders == 0) + { + for (PxU32 baIdx = 0; baIdx < bodyListSize; baIdx++) + { + Cm::SpatialVector& motionVel = motionVelocityArray[baIdx]; + PxSolverBody& atom = bodyListStart[baIdx]; + motionVel.linear = atom.linearVelocity; + motionVel.angular = atom.angularState; + } + + for (PxU32 i = 0; i < articulationListSize; i++) + ArticulationPImpl::saveVelocity(articulationListStart[i], cache.deltaV); + + return; + } + + BatchIterator contactIterator(params.constraintBatchHeaders, params.numConstraintHeaders); + BatchIterator frictionIterator(params.frictionConstraintBatches, params.numFrictionConstraintHeaders); + + + PxI32 frictionBatchCount = PxI32(params.numFrictionConstraintHeaders); + + PxSolverConstraintDesc* PX_RESTRICT constraintList = params.constraintList; + + PxSolverConstraintDesc* PX_RESTRICT frictionConstraintList = params.frictionConstraintList; + + + //0-(n-1) iterations + PxI32 normalIter = 0; + PxI32 frictionIter = 0; + for (PxU32 iteration = positionIterations; iteration > 0; iteration--) //decreasing positive numbers == position iters + { + + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, iteration == 1 ? gVTableSolveConcludeBlockCoulomb : gVTableSolveBlockCoulomb, normalIter); + ++normalIter; + + } + + if(frictionBatchCount>0) + { + const PxU32 numIterations = positionIterations * 2; + for (PxU32 iteration = numIterations; iteration > 0; iteration--) //decreasing positive numbers == position iters + { + SolveBlockParallel(frictionConstraintList, frictionBatchCount, frictionIter * frictionBatchCount, frictionBatchCount, + cache, frictionIterator, iteration == 1 ? gVTableSolveConcludeBlockCoulomb : gVTableSolveBlockCoulomb, frictionIter); + ++frictionIter; + } + } + + for (PxU32 baIdx = 0; baIdx < bodyListSize; baIdx++) + { + const PxSolverBody& atom = bodyListStart[baIdx]; + Cm::SpatialVector& motionVel = motionVelocityArray[baIdx]; + motionVel.linear = atom.linearVelocity; + motionVel.angular = atom.angularState; + } + + + for (PxU32 i = 0; i < articulationListSize; i++) + ArticulationPImpl::saveVelocity(articulationListStart[i], cache.deltaV); + + + const PxU32 velItersMinOne = velocityIterations - 1; + + PxU32 iteration = 0; + + for(; iteration < velItersMinOne; ++iteration) + { + + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, gVTableSolveBlockCoulomb, normalIter); + ++normalIter; + + if(frictionBatchCount > 0) + { + SolveBlockParallel(frictionConstraintList, frictionBatchCount, frictionIter * frictionBatchCount, frictionBatchCount, + cache, frictionIterator, gVTableSolveBlockCoulomb, frictionIter); + ++frictionIter; + } + } + + PxI32* outThresholdPairs = params.outThresholdPairs; + ThresholdStreamElement* PX_RESTRICT thresholdStream = params.thresholdStream; + PxU32 thresholdStreamLength = params.thresholdStreamLength; + + cache.writeBackIteration = true; + + cache.mSharedOutThresholdPairs = outThresholdPairs; + cache.mSharedThresholdStreamLength = thresholdStreamLength; + cache.mSharedThresholdStream = thresholdStream; + + for(; iteration < velocityIterations; ++iteration) + { + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, gVTableSolveWriteBackBlockCoulomb, normalIter); + ++normalIter; + + if(frictionBatchCount > 0) + { + SolveBlockParallel(frictionConstraintList, frictionBatchCount, frictionIter * frictionBatchCount, frictionBatchCount, + cache, frictionIterator, gVTableSolveWriteBackBlockCoulomb, frictionIter); + ++frictionIter; + } + } + + //Write back remaining threshold streams + if(cache.mThresholdStreamIndex > 0) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(reinterpret_cast(&outThresholdPairs), PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 b = 0; b < cache.mThresholdStreamIndex; ++b) + { + thresholdStream[b + threshIndex] = cache.mThresholdStream[b]; + } + cache.mThresholdStreamIndex = 0; + } + +} + +PxI32 SolverCoreGeneralPF::solveVParallelAndWriteBack(SolverIslandParams& params, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) const +{ + SolverContext cache; + cache.solverBodyArray = params.bodyDataList; + + const PxI32 UnrollCount = PxI32(params.batchSize); + const PxI32 SaveUnrollCount = 64; + + const PxI32 TempThresholdStreamSize = 32; + ThresholdStreamElement tempThresholdStream[TempThresholdStreamSize]; + + + const PxI32 batchCount = PxI32(params.numConstraintHeaders); + const PxI32 frictionBatchCount = PxI32(params.numFrictionConstraintHeaders);//frictionConstraintBatches.size(); + cache.mThresholdStream = tempThresholdStream; + cache.mThresholdStreamLength = TempThresholdStreamSize; + cache.mThresholdStreamIndex = 0; + cache.Z = Z; + cache.deltaV = deltaV; + + const PxI32 positionIterations = PxI32(params.positionIterations); + const PxU32 velocityIterations = params.velocityIterations; + + const PxI32 bodyListSize = PxI32(params.bodyListSize); + const PxI32 articulationListSize = PxI32(params.articulationListSize); + + PX_ASSERT(velocityIterations >= 1); + PX_ASSERT(positionIterations >= 1); + + PxI32* constraintIndex = ¶ms.constraintIndex; + PxI32* constraintIndex2 = ¶ms.constraintIndex2; + PxI32* frictionConstraintIndex = ¶ms.frictionConstraintIndex; + + PxI32 endIndexCount = UnrollCount; + PxI32 index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + PxI32 frictionIndex = physx::shdfnd::atomicAdd(frictionConstraintIndex, UnrollCount) - UnrollCount; + + + BatchIterator contactIter(params.constraintBatchHeaders, params.numConstraintHeaders); + BatchIterator frictionIter(params.frictionConstraintBatches, params.numFrictionConstraintHeaders); + + PxU32* headersPerPartition = params.headersPerPartition; + PxU32 nbPartitions = params.nbPartitions; + + PxU32* frictionHeadersPerPartition = params.frictionHeadersPerPartition; + PxU32 nbFrictionPartitions = params.nbFrictionPartitions; + + PxSolverConstraintDesc* PX_RESTRICT constraintList = params.constraintList; + PxSolverConstraintDesc* PX_RESTRICT frictionConstraintList = params.frictionConstraintList; + + + PxI32 maxNormalIndex = 0; + PxI32 maxProgress = 0; + PxI32 frictionEndIndexCount = UnrollCount; + PxI32 maxFrictionIndex = 0; + + PxI32 normalIteration = 0; + PxI32 frictionIteration = 0; + PxU32 a = 0; + for(PxU32 i = 0; i < 2; ++i) + { + SolveBlockMethod* solveTable = i == 0 ? gVTableSolveBlockCoulomb : gVTableSolveConcludeBlockCoulomb; + for(; a < positionIterations - 1 + i; ++a) + { + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxNormalIndex += headersPerPartition[b]; + maxProgress += headersPerPartition[b]; + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, solveTable, + normalIteration); + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + ++normalIteration; + } + + } + + + for(PxU32 i = 0; i < 2; ++i) + { + SolveBlockMethod* solveTable = i == 0 ? gVTableSolveBlockCoulomb : gVTableSolveConcludeBlockCoulomb; + const PxI32 numIterations = positionIterations *2; + for(; a < numIterations - 1 + i; ++a) + { + for(PxU32 b = 0; b < nbFrictionPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxProgress += frictionHeadersPerPartition[b]; + maxFrictionIndex += frictionHeadersPerPartition[b]; + PxI32 nbSolved = 0; + while(frictionIndex < maxFrictionIndex) + { + const PxI32 remainder = PxMin(maxFrictionIndex - frictionIndex, frictionEndIndexCount); + SolveBlockParallel(frictionConstraintList, remainder, frictionIndex, frictionBatchCount, cache, frictionIter, + solveTable, frictionIteration); + frictionIndex += remainder; + frictionEndIndexCount -= remainder; + nbSolved += remainder; + if(frictionEndIndexCount == 0) + { + frictionEndIndexCount = UnrollCount; + frictionIndex = physx::shdfnd::atomicAdd(frictionConstraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + ++frictionIteration; + + } + + } + + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + + + PxI32* bodyListIndex = ¶ms.bodyListIndex; + + ArticulationSolverDesc* PX_RESTRICT articulationListStart = params.articulationListStart; + + PxSolverBody* PX_RESTRICT bodyListStart = params.bodyListStart; + + Cm::SpatialVector* PX_RESTRICT motionVelocityArray = params.motionVelocityArray; + + PxI32* bodyListIndex2 = ¶ms.bodyListIndex2; + + PxI32 endIndexCount2 = SaveUnrollCount; + PxI32 index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount; + { + PxI32 nbConcluded = 0; + while(index2 < articulationListSize) + { + const PxI32 remainder = PxMin(SaveUnrollCount, (articulationListSize - index2)); + endIndexCount2 -= remainder; + for(PxI32 b = 0; b < remainder; ++b, ++index2) + { + ArticulationPImpl::saveVelocity(articulationListStart[index2], cache.deltaV); + } + nbConcluded += remainder; + if(endIndexCount2 == 0) + { + index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount; + endIndexCount2 = SaveUnrollCount; + } + nbConcluded += remainder; + } + + index2 -= articulationListSize; + + //save velocity + + + while(index2 < bodyListSize) + { + const PxI32 remainder = PxMin(endIndexCount2, (bodyListSize - index2)); + endIndexCount2 -= remainder; + for(PxI32 b = 0; b < remainder; ++b, ++index2) + { + Ps::prefetchLine(&bodyListStart[index2 + 8]); + Ps::prefetchLine(&motionVelocityArray[index2 + 8]); + PxSolverBody& body = bodyListStart[index2]; + Cm::SpatialVector& motionVel = motionVelocityArray[index2]; + motionVel.linear = body.linearVelocity; + motionVel.angular = body.angularState; + PX_ASSERT(motionVel.linear.isFinite()); + PX_ASSERT(motionVel.angular.isFinite()); + } + + nbConcluded += remainder; + + //Branch not required because this is the last time we use this atomic variable + //if(index2 < articulationListSizePlusbodyListSize) + { + index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount - articulationListSize; + endIndexCount2 = SaveUnrollCount; + } + } + + if(nbConcluded) + { + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(bodyListIndex2, nbConcluded); + } + } + + + WAIT_FOR_PROGRESS(bodyListIndex2, (bodyListSize + articulationListSize)); + + a = 0; + for(; a < velocityIterations-1; ++a) + { + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxNormalIndex += headersPerPartition[b]; + maxProgress += headersPerPartition[b]; + + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, gVTableSolveBlockCoulomb, normalIteration); + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + ++normalIteration; + + for(PxU32 b = 0; b < nbFrictionPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxFrictionIndex += frictionHeadersPerPartition[b]; + maxProgress += frictionHeadersPerPartition[b]; + + PxI32 nbSolved = 0; + while(frictionIndex < maxFrictionIndex) + { + const PxI32 remainder = PxMin(maxFrictionIndex - frictionIndex, frictionEndIndexCount); + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, gVTableSolveBlockCoulomb, + normalIteration); + + frictionIndex += remainder; + frictionEndIndexCount -= remainder; + nbSolved += remainder; + if(frictionEndIndexCount == 0) + { + frictionEndIndexCount = UnrollCount; + frictionIndex = physx::shdfnd::atomicAdd(frictionConstraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + + ++frictionIteration; + } + + ThresholdStreamElement* PX_RESTRICT thresholdStream = params.thresholdStream; + const PxU32 thresholdStreamLength = params.thresholdStreamLength; + PxI32* outThresholdPairs = params.outThresholdPairs; + + cache.mSharedThresholdStream = thresholdStream; + cache.mSharedOutThresholdPairs = outThresholdPairs; + cache.mSharedThresholdStreamLength = thresholdStreamLength; + + { + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxNormalIndex += headersPerPartition[b]; + maxProgress += headersPerPartition[b]; + + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + + SolveBlockParallel(constraintList, remainder, normalIteration * batchCount, batchCount, + cache, contactIter, gVTableSolveWriteBackBlockCoulomb, normalIteration); + + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + + ++normalIteration; + + cache.mSharedOutThresholdPairs = outThresholdPairs; + cache.mSharedThresholdStream = thresholdStream; + cache.mSharedThresholdStreamLength = thresholdStreamLength; + + for(PxU32 b = 0; b < nbFrictionPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxFrictionIndex += frictionHeadersPerPartition[b]; + maxProgress += frictionHeadersPerPartition[b]; + + PxI32 nbSolved = 0; + while(frictionIndex < maxFrictionIndex) + { + const PxI32 remainder = PxMin(maxFrictionIndex - frictionIndex, frictionEndIndexCount); + + SolveBlockParallel(frictionConstraintList, remainder, frictionIndex, frictionBatchCount, cache, frictionIter, + gVTableSolveWriteBackBlockCoulomb, frictionIteration); + + frictionIndex += remainder; + frictionEndIndexCount -= remainder; + nbSolved += remainder; + if(frictionEndIndexCount == 0) + { + frictionEndIndexCount = UnrollCount; + frictionIndex = physx::shdfnd::atomicAdd(frictionConstraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + + if(cache.mThresholdStreamIndex > 0) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(outThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 b = 0; b < cache.mThresholdStreamIndex; ++b) + { + thresholdStream[b + threshIndex] = cache.mThresholdStream[b]; + } + cache.mThresholdStreamIndex = 0; + } + + ++frictionIteration; + } + + return normalIteration * batchCount + frictionIteration * frictionBatchCount; +} + + +void SolverCoreGeneralPF::writeBackV +(const PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxU32 /*constraintListSize*/, PxConstraintBatchHeader* batchHeaders, const PxU32 numBatches, + ThresholdStreamElement* PX_RESTRICT thresholdStream, const PxU32 thresholdStreamLength, PxU32& outThresholdPairs, + PxSolverBodyData* atomListData, WriteBackBlockMethod writeBackTable[]) const +{ + SolverContext cache; + cache.solverBodyArray = atomListData; + cache.mThresholdStream = thresholdStream; + cache.mThresholdStreamLength = thresholdStreamLength; + cache.mThresholdStreamIndex = 0; + + PxI32 outThreshIndex = 0; + for(PxU32 j = 0; j < numBatches; ++j) + { + PxU8 type = *constraintList[batchHeaders[j].mStartIndex].constraint; + writeBackTable[type](constraintList + batchHeaders[j].mStartIndex, + batchHeaders[j].mStride, cache); + } + + outThresholdPairs = PxU32(outThreshIndex); +} + +} + +} + + +//#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h new file mode 100644 index 000000000..7ab760bef --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h @@ -0,0 +1,71 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCONTROLPF_H +#define DY_SOLVERCONTROLPF_H + +#include "DySolverCore.h" +#include "DySolverConstraintDesc.h" + +namespace physx +{ + +namespace Dy +{ + +class SolverCoreGeneralPF : public SolverCore +{ +public: + static SolverCoreGeneralPF* create(); + + // Implements SolverCore + virtual void destroyV(); + + virtual PxI32 solveVParallelAndWriteBack + (SolverIslandParams& params, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) const; + + virtual void solveV_Blocks + (SolverIslandParams& params) const; + + virtual void writeBackV + (const PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxU32 constraintListSize, PxConstraintBatchHeader* contactConstraintBatches, const PxU32 numBatches, + ThresholdStreamElement* PX_RESTRICT thresholdStream, const PxU32 thresholdStreamLength, PxU32& outThresholdPairs, + PxSolverBodyData* atomListData, WriteBackBlockMethod writeBackTable[]) const; + +private: + + //~Implements SolverCore +}; + +} + +} + +#endif //DY_SOLVERCOREGENERALPF_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h new file mode 100644 index 000000000..dbe1da918 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h @@ -0,0 +1,251 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCORE_H +#define DY_SOLVERCORE_H + +#include "PxvConfig.h" +#include "PsArray.h" +#include "PsThread.h" + + +namespace physx +{ + +struct PxSolverBody; +struct PxSolverBodyData; +struct PxSolverConstraintDesc; +struct PxConstraintBatchHeader; + +namespace Dy +{ +struct ThresholdStreamElement; + + +struct ArticulationSolverDesc; +class Articulation; +struct SolverContext; + +typedef void (*WriteBackMethod)(const PxSolverConstraintDesc& desc, SolverContext& cache, PxSolverBodyData& sbd0, PxSolverBodyData& sbd1); +typedef void (*SolveMethod)(const PxSolverConstraintDesc& desc, SolverContext& cache); +typedef void (*SolveBlockMethod)(const PxSolverConstraintDesc* desc, const PxU32 constraintCount, SolverContext& cache); +typedef void (*SolveWriteBackBlockMethod)(const PxSolverConstraintDesc* desc, const PxU32 constraintCount, SolverContext& cache); +typedef void (*WriteBackBlockMethod)(const PxSolverConstraintDesc* desc, const PxU32 constraintCount, SolverContext& cache); + +#define PX_PROFILE_SOLVE_STALLS 0 +#if PX_PROFILE_SOLVE_STALLS +#if PX_WINDOWS +#include + + +PX_FORCE_INLINE PxU64 readTimer() +{ + //return __rdtsc(); + + LARGE_INTEGER i; + QueryPerformanceCounter(&i); + return i.QuadPart; +} + +#endif +#endif + + +#define YIELD_THREADS 1 + +#if YIELD_THREADS + +#define ATTEMPTS_BEFORE_BACKOFF 30000 +#define ATTEMPTS_BEFORE_RETEST 10000 + +#endif + +PX_INLINE void WaitForProgressCount(volatile PxI32* pGlobalIndex, const PxI32 targetIndex) +{ +#if YIELD_THREADS + if(*pGlobalIndex < targetIndex) + { + bool satisfied = false; + PxU32 count = ATTEMPTS_BEFORE_BACKOFF; + do + { + satisfied = true; + while(*pGlobalIndex < targetIndex) + { + if(--count == 0) + { + satisfied = false; + break; + } + } + if(!satisfied) + Ps::Thread::yield(); + count = ATTEMPTS_BEFORE_RETEST; + } + while(!satisfied); + } +#else + while(*pGlobalIndex < targetIndex); +#endif +} + + +#if PX_PROFILE_SOLVE_STALLS +PX_INLINE void WaitForProgressCount(volatile PxI32* pGlobalIndex, const PxI32 targetIndex, PxU64& stallTime) +{ + if(*pGlobalIndex < targetIndex) + { + bool satisfied = false; + PxU32 count = ATTEMPTS_BEFORE_BACKOFF; + do + { + satisfied = true; + PxU64 startTime = readTimer(); + while(*pGlobalIndex < targetIndex) + { + if(--count == 0) + { + satisfied = false; + break; + } + } + PxU64 endTime = readTimer(); + stallTime += (endTime - startTime); + if(!satisfied) + Ps::Thread::yield(); + count = ATTEMPTS_BEFORE_BACKOFF; + } + while(!satisfied); + } +} + +#define WAIT_FOR_PROGRESS(pGlobalIndex, targetIndex) if(*pGlobalIndex < targetIndex) WaitForProgressCount(pGlobalIndex, targetIndex, stallCount) +#else +#define WAIT_FOR_PROGRESS(pGlobalIndex, targetIndex) if(*pGlobalIndex < targetIndex) WaitForProgressCount(pGlobalIndex, targetIndex) +#endif +#define WAIT_FOR_PROGRESS_NO_TIMER(pGlobalIndex, targetIndex) if(*pGlobalIndex < targetIndex) WaitForProgressCount(pGlobalIndex, targetIndex) + + +struct SolverIslandParams +{ + //Default friction model params + PxU32 positionIterations; + PxU32 velocityIterations; + PxSolverBody* PX_RESTRICT bodyListStart; + PxSolverBodyData* PX_RESTRICT bodyDataList; + PxU32 bodyListSize; + PxU32 solverBodyOffset; + ArticulationSolverDesc* PX_RESTRICT articulationListStart; + PxU32 articulationListSize; + PxSolverConstraintDesc* PX_RESTRICT constraintList; + PxConstraintBatchHeader* constraintBatchHeaders; + PxU32 numConstraintHeaders; + PxU32* headersPerPartition; + PxU32 nbPartitions; + Cm::SpatialVector* PX_RESTRICT motionVelocityArray; + PxU32 batchSize; + PxsBodyCore*const* bodyArray; + PxsRigidBody** PX_RESTRICT rigidBodies; + + //Shared state progress counters + PxI32 constraintIndex; + PxI32 constraintIndex2; + PxI32 bodyListIndex; + PxI32 bodyListIndex2; + PxI32 articSolveIndex; + PxI32 articSolveIndex2; + PxI32 bodyIntegrationListIndex; + PxI32 numObjectsIntegrated; + + PxReal dt; + PxReal invDt; + + + //Additional 1d/2d friction model params + PxSolverConstraintDesc* PX_RESTRICT frictionConstraintList; + + PxConstraintBatchHeader* frictionConstraintBatches; + PxU32 numFrictionConstraintHeaders; + PxU32* frictionHeadersPerPartition; + PxU32 nbFrictionPartitions; + + //Additional Shared state progress counters + PxI32 frictionConstraintIndex; + + //Write-back threshold information + ThresholdStreamElement* PX_RESTRICT thresholdStream; + PxU32 thresholdStreamLength; + + PxI32* outThresholdPairs; + + PxU32 mMaxArticulationLinks; + Cm::SpatialVectorF* Z; + Cm::SpatialVectorF* deltaV; +}; + + +/*! +Interface to constraint solver cores + +*/ +class SolverCore +{ +public: + virtual void destroyV() = 0; + virtual ~SolverCore() {} + /* + solves dual problem exactly by GS-iterating until convergence stops + only uses regular velocity vector for storing results, and backs up initial state, which is restored. + the solution forces are saved in a vector. + + state should not be stored, this function is safe to call from multiple threads. + + Returns the total number of constraints that should be solved across all threads. Used for synchronization outside of this method + */ + + virtual PxI32 solveVParallelAndWriteBack + (SolverIslandParams& params, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) const = 0; + + + virtual void solveV_Blocks + (SolverIslandParams& params) const = 0; + + + virtual void writeBackV + (const PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxU32 constraintListSize, PxConstraintBatchHeader* contactConstraintBatches, const PxU32 numConstraintBatches, + ThresholdStreamElement* PX_RESTRICT thresholdStream, const PxU32 thresholdStreamLength, PxU32& outThresholdPairs, + PxSolverBodyData* atomListData, WriteBackBlockMethod writeBackTable[]) const = 0; +}; + +} + +} + +#endif //DY_SOLVERCORE_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h new file mode 100644 index 000000000..ae5703433 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h @@ -0,0 +1,85 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVEREXTBODY_H +#define DY_SOLVEREXTBODY_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "CmPhysXCommon.h" +#include "CmSpatialVector.h" + +namespace physx +{ + +class PxsRigidBody; +struct PxsBodyCore; +struct PxSolverBody; +struct PxSolverBodyData; + + +namespace Dy +{ + + +class ArticulationV; +struct SolverConstraint1D; + +class SolverExtBody +{ +public: + union + { + const ArticulationV* mArticulation; + const PxSolverBody* mBody; + }; + const PxSolverBodyData* mBodyData; + + PxU16 mLinkIndex; + + SolverExtBody(const void* bodyOrArticulation, const void* bodyData, PxU16 linkIndex): + mBody(reinterpret_cast(bodyOrArticulation)), + mBodyData(reinterpret_cast(bodyData)), + mLinkIndex(linkIndex) + {} + + void getResponse(const PxVec3& linImpulse, const PxVec3& angImpulse, + PxVec3& linDeltaV, PxVec3& angDeltaV, PxReal dominance) const; + + PxReal projectVelocity(const PxVec3& linear, const PxVec3& angular) const; + PxVec3 getLinVel() const; + PxVec3 getAngVel() const; +}; + +} + +} + +#endif //DY_SOLVEREXTBODY_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp new file mode 100644 index 000000000..a7e6c018d --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp @@ -0,0 +1,879 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" + +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DySolverConstraint1D.h" +#include "DySolverConstraintDesc.h" +#include "DyThresholdTable.h" +#include "DySolverContext.h" +#include "PsUtilities.h" +#include "DyConstraint.h" +#include "PsAtomic.h" +#include "DyThresholdTable.h" +#include "DySolverConstraintsShared.h" + +namespace physx +{ + +namespace Dy +{ + +void solveContactCoulomb(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + PxSolverBody& b0 = *desc.bodyA; + PxSolverBody& b1 = *desc.bodyB; + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V linVel1 = V3LoadA(b1.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + Vec3V angState1 = V3LoadA(b1.angularState); + + SolverContactCoulombHeader* PX_RESTRICT firstHeader = reinterpret_cast(desc.constraint); + const PxU8* PX_RESTRICT last = desc.constraint + firstHeader->frictionOffset;//getConstraintLength(desc); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + + //const FloatV zero = FZero(); + + while(currPtr < last) + { + SolverContactCoulombHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactCoulombHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + const Vec3V normal = hdr->getNormal(); + const FloatV invMassDom0 = FLoad(hdr->dominance0); + const FloatV invMassDom1 = FLoad(hdr->dominance1); + const FloatV angD0 = FLoad(hdr->angDom0); + const FloatV angD1 = FLoad(hdr->angDom1); + + + + SolverContactPoint* PX_RESTRICT contacts = reinterpret_cast(currPtr); + currPtr += numNormalConstr * sizeof(SolverContactPoint); + + PxF32* appliedImpulse = reinterpret_cast ((reinterpret_cast(hdr)) + hdr->frictionOffset + sizeof(SolverFrictionHeader)); + Ps::prefetchLine(appliedImpulse); + + solveDynamicContacts(contacts, numNormalConstr, normal, invMassDom0, invMassDom1, + angD0, angD1, linVel0, angState0, linVel1, angState1, appliedImpulse); + } + + // Write back + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(linVel1, b1.linearVelocity); + V3StoreA(angState0, b0.angularState); + V3StoreA(angState1, b1.angularState); + + PX_ASSERT(currPtr == last); +} + +void solveFriction(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + PxSolverBody& b0 = *desc.bodyA; + PxSolverBody& b1 = *desc.bodyB; + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V linVel1 = V3LoadA(b1.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + Vec3V angState1 = V3LoadA(b1.angularState); + + PxU8* PX_RESTRICT ptr = desc.constraint; + PxU8* PX_RESTRICT currPtr = ptr; + + const PxU8* PX_RESTRICT last = ptr + getConstraintLength(desc); + + + while(currPtr < last) + { + const SolverFrictionHeader* PX_RESTRICT frictionHeader = reinterpret_cast(currPtr); + currPtr += sizeof(SolverFrictionHeader); + PxF32* appliedImpulse = reinterpret_cast(currPtr); + currPtr += frictionHeader->getAppliedForcePaddingSize(); + + SolverContactFriction* PX_RESTRICT frictions = reinterpret_cast(currPtr); + const PxU32 numFrictionConstr = frictionHeader->numFrictionConstr; + const PxU32 numNormalConstr = frictionHeader->numNormalConstr; + + const PxU32 numFrictionPerPoint = numFrictionConstr/numNormalConstr; + + currPtr += numFrictionConstr * sizeof(SolverContactFriction); + const FloatV staticFriction = frictionHeader->getStaticFriction(); + + const FloatV invMass0D0 = FLoad(frictionHeader->invMass0D0); + const FloatV invMass1D1 = FLoad(frictionHeader->invMass1D1); + + + const FloatV angD0 = FLoad(frictionHeader->angDom0); + const FloatV angD1 = FLoad(frictionHeader->angDom1); + + for(PxU32 i=0, j = 0;i(desc.constraint); + const PxU8* PX_RESTRICT last = desc.constraint + firstHeader->frictionOffset;//getConstraintLength(desc); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + //const FloatV zero = FZero(); + + while(currPtr < last) + { + SolverContactCoulombHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactCoulombHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + SolverContactPoint* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPoint); + + PxF32* appliedImpulse = reinterpret_cast ((reinterpret_cast(hdr)) + hdr->frictionOffset + sizeof(SolverFrictionHeader)); + Ps::prefetchLine(appliedImpulse); + + const Vec3V normal = hdr->getNormal(); + + const FloatV invMassDom0 = FLoad(hdr->dominance0); + + const FloatV angD0 = FLoad(hdr->angDom0); + + solveStaticContacts(contacts, numNormalConstr, normal, invMassDom0, + angD0, linVel0, angState0, appliedImpulse); + } + + // Write back + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(angState0, b0.angularState); + + PX_ASSERT(currPtr == last); +} + +void solveFriction_BStatic(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + PxSolverBody& b0 = *desc.bodyA; + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + + PxU8* PX_RESTRICT currPtr = desc.constraint; + + const PxU8* PX_RESTRICT last = currPtr + getConstraintLength(desc); + + while(currPtr < last) + { + + const SolverFrictionHeader* PX_RESTRICT frictionHeader = reinterpret_cast(currPtr); + const PxU32 numFrictionConstr = frictionHeader->numFrictionConstr; + const PxU32 numNormalConstr = frictionHeader->numNormalConstr; + const PxU32 numFrictionPerPoint = numFrictionConstr/numNormalConstr; + currPtr +=sizeof(SolverFrictionHeader); + PxF32* appliedImpulse = reinterpret_cast(currPtr); + currPtr +=frictionHeader->getAppliedForcePaddingSize(); + + SolverContactFriction* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFriction); + + const FloatV invMass0 = FLoad(frictionHeader->invMass0D0); + const FloatV angD0 = FLoad(frictionHeader->angDom0); + //const FloatV angD1 = FLoad(frictionHeader->angDom1); + + + const FloatV staticFriction = frictionHeader->getStaticFriction(); + + for(PxU32 i=0, j = 0;i(cPtr); + PxU8* PX_RESTRICT last = desc.constraint + firstHeader->frictionOffset;//getConstraintLength(desc); + while(cPtr < last) + { + const SolverContactCoulombHeader* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactCoulombHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + //if(cPtr < last) + //Ps::prefetchLine(cPtr, 512); + Ps::prefetchLine(cPtr,128); + Ps::prefetchLine(cPtr,256); + Ps::prefetchLine(cPtr,384); + + const PxU32 pointStride = hdr->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactPointExt) + : sizeof(SolverContactPoint); + for(PxU32 i=0;i(cPtr); + cPtr += pointStride; + //c->scaledBias = PxMin(c->scaledBias, 0.f); + c->biasedErr = c->unbiasedErr; + } + } + PX_ASSERT(cPtr == last); +} + +void writeBackContactCoulomb(const PxSolverConstraintDesc& desc, SolverContext& cache, + PxSolverBodyData& bd0, PxSolverBodyData& bd1) +{ + + PxReal normalForce = 0.f; + + PxU8* PX_RESTRICT cPtr = desc.constraint; + PxReal* PX_RESTRICT vForceWriteback = reinterpret_cast(desc.writeBack); + const SolverContactCoulombHeader* PX_RESTRICT firstHeader = reinterpret_cast(cPtr); + PxU8* PX_RESTRICT last = desc.constraint + firstHeader->frictionOffset; + + const PxU32 pointStride = firstHeader->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactPointExt) + : sizeof(SolverContactPoint); + + bool hasForceThresholds = false; + while(cPtr < last) + { + const SolverContactCoulombHeader* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactCoulombHeader); + + PxF32* appliedImpulse = reinterpret_cast (const_cast((reinterpret_cast(hdr)) + hdr->frictionOffset + sizeof(SolverFrictionHeader))); + + hasForceThresholds = hdr->flags & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + Ps::prefetchLine(cPtr, 256); + Ps::prefetchLine(cPtr, 384); + + if(vForceWriteback!=NULL) + { + for(PxU32 i=0; i(bd0.reportThreshold, bd1.reportThreshold); + elt.nodeIndexA = IG::NodeIndex(bd0.nodeIndex); + elt.nodeIndexB = IG::NodeIndex(bd1.nodeIndex); + elt.shapeInteraction = (reinterpret_cast(desc.constraint))->shapeInteraction; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + + PX_ASSERT(cache.mThresholdStreamIndex (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveContactCoulomb_BStaticBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveContactCoulomb_BStatic(desc[a], cache); + } +} + +void solveContactCoulomb_BStaticConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveContactCoulomb_BStatic(desc[a], cache); + concludeContactCoulomb(desc[a], cache); + } +} + +void solveContactCoulomb_BStaticBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a].bodyBDataIndex]; + solveContactCoulomb_BStatic(desc[a], cache); + writeBackContactCoulomb(desc[a], cache, bd0, bd1); + } + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Not enough space to write 4 more thresholds back! + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveExtContactCoulomb(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + //We'll need this. +// const FloatV zero = FZero(); +// const FloatV one = FOne(); + + Vec3V linVel0, angVel0, linVel1, angVel1; + + if(desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + linVel0 = V3LoadA(desc.bodyA->linearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + } + + //const PxU8* PX_RESTRICT last = desc.constraint + desc.constraintLengthOver16*16; + + PxU8* PX_RESTRICT currPtr = desc.constraint; + + const SolverContactCoulombHeader* PX_RESTRICT firstHeader = reinterpret_cast(currPtr); + + const PxU8* PX_RESTRICT last = desc.constraint + firstHeader->frictionOffset; + + //hopefully pointer aliasing doesn't bite. + + Vec3V linImpulse0 = V3Zero(), linImpulse1 = V3Zero(), angImpulse0 = V3Zero(), angImpulse1 = V3Zero(); + + while(currPtr < last) + { + const SolverContactCoulombHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactCoulombHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + PxF32* appliedImpulse = reinterpret_cast(const_cast(((reinterpret_cast(hdr)) + hdr->frictionOffset + sizeof(SolverFrictionHeader)))); + Ps::prefetchLine(appliedImpulse); + + SolverContactPointExt* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPointExt); + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + const Vec3V normal = hdr->getNormal(); + + solveExtContacts(contacts, numNormalConstr, normal, linVel0, angVel0, linVel1, angVel1, li0, ai0, li1, ai1, appliedImpulse); + + linImpulse0 = V3ScaleAdd(li0, FLoad(hdr->dominance0), linImpulse0); + angImpulse0 = V3ScaleAdd(ai0, FLoad(hdr->angDom0), angImpulse0); + linImpulse1 = V3NegScaleSub(li1, FLoad(hdr->dominance1), linImpulse1); + angImpulse1 = V3NegScaleSub(ai1, FLoad(hdr->angDom1), angImpulse1); + } + + if(desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularState); + } + else + { + Cm::SpatialVectorF Z[64]; + Cm::SpatialVectorF deltaV[64]; + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, linImpulse0, + angImpulse0, Z, deltaV); + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularState); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, linImpulse1, + angImpulse1, cache.Z, cache.deltaV); + } + + PX_ASSERT(currPtr == last); +} + +void solveExtFriction(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + Vec3V linVel0, angVel0, linVel1, angVel1; + + if(desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + linVel0 = V3LoadA(desc.bodyA->linearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + } + + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + const PxU8* PX_RESTRICT last = currPtr + desc.constraintLengthOver16*16; + + Vec3V linImpulse0 = V3Zero(), linImpulse1 = V3Zero(), angImpulse0 = V3Zero(), angImpulse1 = V3Zero(); + + while(currPtr < last) + { + + const SolverFrictionHeader* PX_RESTRICT frictionHeader = reinterpret_cast(currPtr); + currPtr += sizeof(SolverFrictionHeader); + PxF32* appliedImpulse = reinterpret_cast(currPtr); + currPtr += frictionHeader->getAppliedForcePaddingSize(); + + SolverContactFrictionExt* PX_RESTRICT frictions = reinterpret_cast(currPtr); + const PxU32 numFrictionConstr = frictionHeader->numFrictionConstr; + + currPtr += numFrictionConstr * sizeof(SolverContactFrictionExt); + const FloatV staticFriction = frictionHeader->getStaticFriction(); + + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + PxU32 numNormalConstr = frictionHeader->numNormalConstr; + PxU32 nbFrictionsPerPoint = numFrictionConstr/numNormalConstr; + + + + + for(PxU32 i = 0, j = 0; i < numFrictionConstr; j++) + { + for(PxU32 p=0;pinvMass0D0), linImpulse0); + angImpulse0 = V3ScaleAdd(ai0, FLoad(frictionHeader->angDom0), angImpulse0); + linImpulse1 = V3NegScaleSub(li1, FLoad(frictionHeader->invMass1D1), linImpulse1); + angImpulse1 = V3NegScaleSub(ai1, FLoad(frictionHeader->angDom1), angImpulse1); + } + + if(desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularState); + } + else + { + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, + linImpulse0, angImpulse0, cache.Z, cache.deltaV); + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularState); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, + linImpulse1, angImpulse1, cache.Z, cache.deltaV); + } + + PX_ASSERT(currPtr == last); + +} + +void solveExtFrictionBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtFriction(desc[a], cache); + } +} + +void solveExtFrictionConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtFriction(desc[a], cache); + } +} + +void solveExtFrictionBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtFriction(desc[a], cache); + } +} + + +void solveConcludeExtContactCoulomb (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveExtContactCoulomb(desc, cache); + concludeContactCoulomb(desc, cache); +} + +void solveExtContactCoulombBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtContactCoulomb(desc[a], cache); + } +} + +void solveExtContactCoulombConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtContactCoulomb(desc[a], cache); + concludeContactCoulomb(desc[a], cache); + } +} + +void solveExtContactCoulombBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a].linkIndexA != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a].linkIndexB != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyBDataIndex]; + + solveExtContactCoulomb(desc[a], cache); + writeBackContactCoulomb(desc[a], cache, bd0, bd1); + } + if(cache.mThresholdStreamIndex > 0) + { + //Not enough space to write 4 more thresholds back! + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + + +void solveConcludeContactCoulomb (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveContactCoulomb(desc, cache); + concludeContactCoulomb(desc, cache); +} + + +void solveConcludeContactCoulomb_BStatic (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveContactCoulomb_BStatic(desc, cache); + concludeContactCoulomb(desc, cache); +} + + + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp new file mode 100644 index 000000000..65b28464e --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp @@ -0,0 +1,985 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "PsFPU.h" +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverContactPF4.h" +#include "DySolverConstraint1D.h" +#include "DySolverConstraintDesc.h" +#include "DyThresholdTable.h" +#include "DySolverContext.h" +#include "PsUtilities.h" +#include "DyConstraint.h" +#include "PsAtomic.h" +#include "DySolverContact.h" + +namespace physx +{ + +namespace Dy +{ + +static void solveContactCoulomb4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& /*cache*/) +{ + PxSolverBody& b00 = *desc[0].bodyA; + PxSolverBody& b01 = *desc[0].bodyB; + PxSolverBody& b10 = *desc[1].bodyA; + PxSolverBody& b11 = *desc[1].bodyB; + PxSolverBody& b20 = *desc[2].bodyA; + PxSolverBody& b21 = *desc[2].bodyB; + PxSolverBody& b30 = *desc[3].bodyA; + PxSolverBody& b31 = *desc[3].bodyB; + + //We'll need this. + const Vec4V vZero = V4Zero(); + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&b01.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularState.x); + Vec4V angState01 = V4LoadA(&b01.angularState.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&b11.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularState.x); + Vec4V angState11 = V4LoadA(&b11.angularState.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&b21.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularState.x); + Vec4V angState21 = V4LoadA(&b21.angularState.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&b31.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularState.x); + Vec4V angState31 = V4LoadA(&b31.angularState.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2, linVel0T3; + Vec4V linVel1T0, linVel1T1, linVel1T2, linVel1T3; + Vec4V angState0T0, angState0T1, angState0T2, angState0T3; + Vec4V angState1T0, angState1T1, angState1T2, angState1T3; + + + PX_TRANSPOSE_44(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2, linVel0T3); + PX_TRANSPOSE_44(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2, linVel1T3); + PX_TRANSPOSE_44(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2, angState0T3); + PX_TRANSPOSE_44(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2, angState1T3); + + + + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + SolverContactCoulombHeader4* PX_RESTRICT firstHeader = reinterpret_cast(currPtr); + + const PxU8* PX_RESTRICT last = desc[0].constraint + firstHeader->frictionOffset; + + //const PxU8* PX_RESTRICT endPtr = desc[0].constraint + getConstraintLength(desc[0]); + + + //TODO - can I avoid this many tests??? + while(currPtr < last) + { + + SolverContactCoulombHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + Vec4V* appliedForceBuffer = reinterpret_cast(currPtr + hdr->frictionOffset + sizeof(SolverFrictionHeader4)); + + //PX_ASSERT((PxU8*)appliedForceBuffer < endPtr); + + currPtr = reinterpret_cast(hdr + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + SolverContact4Dynamic* PX_RESTRICT contacts = reinterpret_cast(currPtr); + //const Vec4V dominance1 = V4Neg(__dominance1); + + currPtr = reinterpret_cast(contacts + numNormalConstr); + + const Vec4V invMass0D0 = hdr->invMassADom; + const Vec4V invMass1D1 = hdr->invMassBDom; + const Vec4V angD0 = hdr->angD0; + const Vec4V angD1 = hdr->angD1; + + const Vec4V normalT0 = hdr->normalX; + const Vec4V normalT1 = hdr->normalY; + const Vec4V normalT2 = hdr->normalZ; + + const Vec4V __normalVel1 = V4Mul(linVel0T0, normalT0); + const Vec4V __normalVel3 = V4Mul(linVel1T0, normalT0); + const Vec4V _normalVel1 = V4MulAdd(linVel0T1, normalT1, __normalVel1); + const Vec4V _normalVel3 = V4MulAdd(linVel1T1, normalT1, __normalVel3); + + Vec4V normalVel1 = V4MulAdd(linVel0T2, normalT2, _normalVel1); + Vec4V normalVel3 = V4MulAdd(linVel1T2, normalT2, _normalVel3); + + Vec4V accumDeltaF = vZero; + + for(PxU32 i=0;i(currPtr); + + const PxU8* PX_RESTRICT last = desc[0].constraint + firstHeader->frictionOffset; + + + //TODO - can I avoid this many tests??? + while(currPtr < last) + { + + SolverContactCoulombHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + Vec4V* appliedForceBuffer = reinterpret_cast(currPtr + hdr->frictionOffset + sizeof(SolverFrictionHeader4)); + + currPtr = reinterpret_cast(hdr + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + SolverContact4Base* PX_RESTRICT contacts = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(contacts + numNormalConstr); + + const Vec4V invMass0D0 = hdr->invMassADom; + const Vec4V angD0 = hdr->angD0; + + const Vec4V normalT0 = hdr->normalX; + const Vec4V normalT1 = hdr->normalY; + const Vec4V normalT2 = hdr->normalZ; + + const Vec4V __normalVel1 = V4Mul(linVel0T0, normalT0); + const Vec4V _normalVel1 = V4MulAdd(linVel0T1, normalT1, __normalVel1); + + Vec4V normalVel1 = V4MulAdd(linVel0T2, normalT2, _normalVel1); + + Vec4V accumDeltaF = vZero; + + for(PxU32 i=0;i(currPtr); + + currPtr = reinterpret_cast(hdr + 1); + + Vec4V* appliedImpulses = reinterpret_cast(currPtr); + + currPtr += hdr->numNormalConstr * sizeof(Vec4V); + + Ps::prefetchLine(currPtr, 128); + Ps::prefetchLine(currPtr,256); + Ps::prefetchLine(currPtr,384); + + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverFriction4Dynamic* PX_RESTRICT frictions = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(frictions + hdr->numFrictionConstr); + + const PxU32 maxFrictionConstr = numFrictionConstr; + + const Vec4V staticFric = hdr->staticFriction; + + const Vec4V invMass0D0 = hdr->invMassADom; + const Vec4V invMass1D1 = hdr->invMassBDom; + + const Vec4V angD0 = hdr->angD0; + const Vec4V angD1 = hdr->angD1; + + for(PxU32 i=0;i>hdr->frictionPerContact]; + + const Vec4V maxFriction = V4Mul(staticFric, appliedImpulse); + + const Vec4V nMaxFriction = V4Neg(maxFriction); + + const Vec4V normalX = f.normalX; + const Vec4V normalY = f.normalY; + const Vec4V normalZ = f.normalZ; + + const Vec4V raXnX = f.raXnX; + const Vec4V raXnY = f.raXnY; + const Vec4V raXnZ = f.raXnZ; + + const Vec4V rbXnX = f.rbXnX; + const Vec4V rbXnY = f.rbXnY; + const Vec4V rbXnZ = f.rbXnZ; + + const Vec4V appliedForce(f.appliedForce); + const Vec4V velMultiplier(f.velMultiplier); + const Vec4V targetVel(f.targetVelocity); + + //4 x 4 Dot3 products encoded as 8 M44 transposes, 4 MulV and 8 MulAdd ops + + const Vec4V __normalVel1 = V4Mul(linVel0T0, normalX); + const Vec4V __normalVel2 = V4Mul(raXnX, angState0T0); + const Vec4V __normalVel3 = V4Mul(linVel1T0, normalX); + const Vec4V __normalVel4 = V4Mul(rbXnX, angState1T0); + + const Vec4V _normalVel1 = V4MulAdd(linVel0T1, normalY, __normalVel1); + const Vec4V _normalVel2 = V4MulAdd(raXnY, angState0T1, __normalVel2); + const Vec4V _normalVel3 = V4MulAdd(linVel1T1, normalY, __normalVel3); + const Vec4V _normalVel4 = V4MulAdd(rbXnY, angState1T1, __normalVel4); + + const Vec4V normalVel1 = V4MulAdd(linVel0T2, normalZ, _normalVel1); + const Vec4V normalVel2 = V4MulAdd(raXnZ, angState0T2, _normalVel2); + const Vec4V normalVel3 = V4MulAdd(linVel1T2, normalZ, _normalVel3); + const Vec4V normalVel4 = V4MulAdd(rbXnZ, angState1T2, _normalVel4); + + + const Vec4V _normalVel = V4Add(normalVel1, normalVel2); + const Vec4V __normalVel = V4Add(normalVel3, normalVel4); + + const Vec4V normalVel = V4Sub(_normalVel, __normalVel ); + + const Vec4V tmp = V4NegMulSub(targetVel, velMultiplier, appliedForce); + Vec4V newAppliedForce = V4MulAdd(normalVel, velMultiplier, tmp); + newAppliedForce = V4Clamp(newAppliedForce,nMaxFriction, maxFriction); + const Vec4V deltaF = V4Sub(newAppliedForce, appliedForce); + + const Vec4V deltaLinF0 = V4Mul(invMass0D0, deltaF); + const Vec4V deltaLinF1 = V4Mul(invMass1D1, deltaF); + + const Vec4V deltaAngF0 = V4Mul(angD0, deltaF); + const Vec4V deltaAngF1 = V4Mul(angD1, deltaF); + + + linVel0T0 = V4MulAdd(normalX, deltaLinF0, linVel0T0); + linVel1T0 = V4NegMulSub(normalX, deltaLinF1, linVel1T0); + angState0T0 = V4MulAdd(raXnX, deltaAngF0, angState0T0); + angState1T0 = V4NegMulSub(rbXnX, deltaAngF1, angState1T0); + + linVel0T1 = V4MulAdd(normalY, deltaLinF0, linVel0T1); + linVel1T1 = V4NegMulSub(normalY, deltaLinF1, linVel1T1); + angState0T1 = V4MulAdd(raXnY, deltaAngF0, angState0T1); + angState1T1 = V4NegMulSub(rbXnY, deltaAngF1, angState1T1); + + linVel0T2 = V4MulAdd(normalZ, deltaLinF0, linVel0T2); + linVel1T2 = V4NegMulSub(normalZ, deltaLinF1, linVel1T2); + angState0T2 = V4MulAdd(raXnZ, deltaAngF0, angState0T2); + angState1T2 = V4NegMulSub(rbXnZ, deltaAngF1, angState1T2); + + f.appliedForce = newAppliedForce; + } + } + + PX_ASSERT(currPtr == endPtr); + + //KS - we need to use PX_TRANSPOSE_44 here instead of the 34_43 variants because the W components are being used to + //store the bodies' progress counters. + + PX_TRANSPOSE_44(linVel0T0, linVel0T1, linVel0T2, linVel0T3, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_44(linVel1T0, linVel1T1, linVel1T2, linVel1T3, linVel01, linVel11, linVel21, linVel31); + PX_TRANSPOSE_44(angState0T0, angState0T1, angState0T2, angState0T3, angState00, angState10, angState20, angState30); + PX_TRANSPOSE_44(angState1T0, angState1T1, angState1T2, angState1T3, angState01, angState11, angState21, angState31); + + + // Write back + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + + V4StoreA(linVel01, &b01.linearVelocity.x); + V4StoreA(linVel11, &b11.linearVelocity.x); + V4StoreA(linVel21, &b21.linearVelocity.x); + V4StoreA(linVel31, &b31.linearVelocity.x); + + V4StoreA(angState00, &b00.angularState.x); + V4StoreA(angState10, &b10.angularState.x); + V4StoreA(angState20, &b20.angularState.x); + V4StoreA(angState30, &b30.angularState.x); + + V4StoreA(angState01, &b01.angularState.x); + V4StoreA(angState11, &b11.angularState.x); + V4StoreA(angState21, &b21.angularState.x); + V4StoreA(angState31, &b31.angularState.x); + +} + + +static void solveFriction4_StaticBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& /*cache*/) +{ + + PxSolverBody& b00 = *desc[0].bodyA; + PxSolverBody& b10 = *desc[1].bodyA; + PxSolverBody& b20 = *desc[2].bodyA; + PxSolverBody& b30 = *desc[3].bodyA; + + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularState.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularState.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularState.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularState.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2, linVel0T3; + Vec4V angState0T0, angState0T1, angState0T2, angState0T3; + + + PX_TRANSPOSE_44(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2, linVel0T3); + PX_TRANSPOSE_44(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2, angState0T3); + + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + PxU8* PX_RESTRICT endPtr = desc[0].constraint + getConstraintLength(desc[0]); + + + while(currPtr < endPtr) + { + SolverFrictionHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(hdr + 1); + + Vec4V* appliedImpulses = reinterpret_cast(currPtr); + + currPtr += hdr->numNormalConstr * sizeof(Vec4V); + + Ps::prefetchLine(currPtr, 128); + Ps::prefetchLine(currPtr,256); + Ps::prefetchLine(currPtr,384); + + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverFriction4Base* PX_RESTRICT frictions = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(frictions + hdr->numFrictionConstr); + + const PxU32 maxFrictionConstr = numFrictionConstr; + + const Vec4V staticFric = hdr->staticFriction; + + const Vec4V invMass0D0 = hdr->invMassADom; + const Vec4V angD0 = hdr->angD0; + + for(PxU32 i=0;i>hdr->frictionPerContact]; + + const Vec4V maxFriction = V4Mul(staticFric, appliedImpulse); + + const Vec4V nMaxFriction = V4Neg(maxFriction); + + const Vec4V normalX = f.normalX; + const Vec4V normalY = f.normalY; + const Vec4V normalZ = f.normalZ; + + const Vec4V raXnX = f.raXnX; + const Vec4V raXnY = f.raXnY; + const Vec4V raXnZ = f.raXnZ; + + const Vec4V appliedForce(f.appliedForce); + const Vec4V velMultiplier(f.velMultiplier); + const Vec4V targetVel(f.targetVelocity); + + //4 x 4 Dot3 products encoded as 8 M44 transposes, 4 MulV and 8 MulAdd ops + + const Vec4V __normalVel1 = V4Mul(linVel0T0, normalX); + const Vec4V __normalVel2 = V4Mul(raXnX, angState0T0); + + const Vec4V _normalVel1 = V4MulAdd(linVel0T1, normalY, __normalVel1); + const Vec4V _normalVel2 = V4MulAdd(raXnY, angState0T1, __normalVel2); + + const Vec4V normalVel1 = V4MulAdd(linVel0T2, normalZ, _normalVel1); + const Vec4V normalVel2 = V4MulAdd(raXnZ, angState0T2, _normalVel2); + + const Vec4V delLinVel00 = V4Mul(normalX, invMass0D0); + + const Vec4V delLinVel10 = V4Mul(normalY, invMass0D0); + + const Vec4V normalVel = V4Add(normalVel1, normalVel2); + + const Vec4V delLinVel20 = V4Mul(normalZ, invMass0D0); + + const Vec4V tmp = V4NegMulSub(targetVel, velMultiplier, appliedForce); + + Vec4V newAppliedForce = V4MulAdd(normalVel, velMultiplier, tmp); + newAppliedForce = V4Clamp(newAppliedForce,nMaxFriction, maxFriction); + const Vec4V deltaF = V4Sub(newAppliedForce, appliedForce); + + const Vec4V deltaAngF0 = V4Mul(angD0, deltaF); + + linVel0T0 = V4MulAdd(delLinVel00, deltaF, linVel0T0); + angState0T0 = V4MulAdd(raXnX, deltaAngF0, angState0T0); + + linVel0T1 = V4MulAdd(delLinVel10, deltaF, linVel0T1); + angState0T1 = V4MulAdd(raXnY, deltaAngF0, angState0T1); + + linVel0T2 = V4MulAdd(delLinVel20, deltaF, linVel0T2); + angState0T2 = V4MulAdd(raXnZ, deltaAngF0, angState0T2); + + f.appliedForce = newAppliedForce; + } + } + + PX_ASSERT(currPtr == endPtr); + + //KS - we need to use PX_TRANSPOSE_44 here instead of the 34_43 variants because the W components are being used to + //store the bodies' progress counters. + + PX_TRANSPOSE_44(linVel0T0, linVel0T1, linVel0T2, linVel0T3, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_44(angState0T0, angState0T1, angState0T2, angState0T3, angState00, angState10, angState20, angState30); + + // Write back + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + + V4StoreA(angState00, &b00.angularState.x); + V4StoreA(angState10, &b10.angularState.x); + V4StoreA(angState20, &b20.angularState.x); + V4StoreA(angState30, &b30.angularState.x); +} + +static void concludeContactCoulomb4(const PxSolverConstraintDesc* desc, SolverContext& /*cache*/) +{ + PxU8* PX_RESTRICT cPtr = desc[0].constraint; + + const Vec4V zero = V4Zero(); + + const SolverContactCoulombHeader4* PX_RESTRICT firstHeader = reinterpret_cast(cPtr); + PxU8* PX_RESTRICT last = desc[0].constraint + firstHeader->frictionOffset; + + PxU32 pointStride = firstHeader->type == DY_SC_TYPE_BLOCK_RB_CONTACT ? sizeof(SolverContact4Dynamic) : sizeof(SolverContact4Base); + + while(cPtr < last) + { + const SolverContactCoulombHeader4* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactCoulombHeader4); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + //if(cPtr < last) + //Ps::prefetchLine(cPtr, 512); + Ps::prefetchLine(cPtr,128); + Ps::prefetchLine(cPtr,256); + Ps::prefetchLine(cPtr,384); + + for(PxU32 i=0;i(cPtr); + cPtr += pointStride; + c->scaledBias = V4Max(c->scaledBias, zero); + } + } + PX_ASSERT(cPtr == last); +} + +void writeBackContactCoulomb4(const PxSolverConstraintDesc* desc, SolverContext& cache, + const PxSolverBodyData** PX_RESTRICT bd0, const PxSolverBodyData** PX_RESTRICT bd1) +{ + Vec4V normalForceV = V4Zero(); + PxU8* PX_RESTRICT cPtr = desc[0].constraint; + PxReal* PX_RESTRICT vForceWriteback0 = reinterpret_cast(desc[0].writeBack); + PxReal* PX_RESTRICT vForceWriteback1 = reinterpret_cast(desc[1].writeBack); + PxReal* PX_RESTRICT vForceWriteback2 = reinterpret_cast(desc[2].writeBack); + PxReal* PX_RESTRICT vForceWriteback3 = reinterpret_cast(desc[3].writeBack); + + const SolverContactCoulombHeader4* PX_RESTRICT firstHeader = reinterpret_cast(cPtr); + PxU8* PX_RESTRICT last = desc[0].constraint + firstHeader->frictionOffset; + + const PxU32 pointStride = firstHeader->type == DY_SC_TYPE_BLOCK_RB_CONTACT ? sizeof(SolverContact4Dynamic) + : sizeof(SolverContact4Base); + + bool writeBackThresholds[4] = {false, false, false, false}; + + + while(cPtr < last) + { + const SolverContactCoulombHeader4* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactCoulombHeader4); + + writeBackThresholds[0] = hdr->flags[0] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[1] = hdr->flags[1] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[2] = hdr->flags[2] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[3] = hdr->flags[3] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + Ps::prefetchLine(cPtr, 256); + Ps::prefetchLine(cPtr, 384); + + + for(PxU32 i=0; i(cPtr); + cPtr += pointStride; + + const Vec4V appliedForce = c->appliedForce; + if(vForceWriteback0 && i < hdr->numNormalConstr0) + FStore(V4GetX(appliedForce), vForceWriteback0++); + if(vForceWriteback1 && i < hdr->numNormalConstr1) + FStore(V4GetY(appliedForce), vForceWriteback1++); + if(vForceWriteback2 && i < hdr->numNormalConstr2) + FStore(V4GetZ(appliedForce), vForceWriteback2++); + if(vForceWriteback3 && i < hdr->numNormalConstr3) + FStore(V4GetW(appliedForce), vForceWriteback3++); + + normalForceV = V4Add(normalForceV, appliedForce); + } + } + PX_ASSERT(cPtr == last); + + PX_ALIGN(16, PxReal nf[4]); + V4StoreA(normalForceV, nf); + + //all constraint pointer in descs are the same constraint + Sc::ShapeInteraction** shapeInteractions = reinterpret_cast(desc[0].constraint)->shapeInteraction; + + for(PxU32 a = 0; a < 4; ++a) + { + if(writeBackThresholds[a] && desc[a].linkIndexA == PxSolverConstraintDesc::NO_LINK && desc[a].linkIndexB == PxSolverConstraintDesc::NO_LINK && + nf[a] !=0.f && (bd0[a]->reportThreshold < PX_MAX_REAL || bd1[a]->reportThreshold < PX_MAX_REAL)) + { + ThresholdStreamElement elt; + elt.normalForce = nf[a]; + elt.threshold = PxMin(bd0[a]->reportThreshold, bd1[a]->reportThreshold); + elt.nodeIndexA = IG::NodeIndex(bd0[a]->nodeIndex); + elt.nodeIndexB = IG::NodeIndex(bd1[a]->nodeIndex); + elt.shapeInteraction = shapeInteractions[a]; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + PX_ASSERT(cache.mThresholdStreamIndex (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveContactCoulombPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContactCoulomb4_StaticBlock(desc, cache); + const PxSolverBodyData* bd0[4] = { &cache.solverBodyArray[desc[0].bodyADataIndex], + &cache.solverBodyArray[desc[1].bodyADataIndex], + &cache.solverBodyArray[desc[2].bodyADataIndex], + &cache.solverBodyArray[desc[3].bodyADataIndex]}; + + const PxSolverBodyData* bd1[4] = { &cache.solverBodyArray[desc[0].bodyBDataIndex], + &cache.solverBodyArray[desc[1].bodyBDataIndex], + &cache.solverBodyArray[desc[2].bodyBDataIndex], + &cache.solverBodyArray[desc[3].bodyBDataIndex]}; + + writeBackContactCoulomb4(desc, cache, bd0, bd1); + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveFrictionCoulombPreBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_Block(desc, cache); +} + +void solveFrictionCoulombPreBlock_Static(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_StaticBlock(desc, cache); +} + +void solveFrictionCoulombPreBlock_Conclude(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_Block(desc, cache); +} + +void solveFrictionCoulombPreBlock_ConcludeStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_StaticBlock(desc, cache); +} + +void solveFrictionCoulombPreBlock_WriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_Block(desc, cache); +} + +void solveFrictionCoulombPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_StaticBlock(desc, cache); +} + + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h b/src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h new file mode 100644 index 000000000..0196bdb97 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SPATIAL_H +#define DY_SPATIAL_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "PsMathUtils.h" +#include "CmSpatialVector.h" + +namespace physx +{ +namespace Dy +{ +// translate a motion resolved at position p to the origin + + +// should have a 'from' frame and a 'to' frame +class SpInertia +{ +public: + SpInertia() {} + + SpInertia(const PxMat33& ll, const PxMat33& la, const PxMat33& aa): mLL(ll), mLA(la), mAA(aa) + { + } + + static SpInertia getZero() + { + return SpInertia(PxMat33(PxZero), PxMat33(PxZero), + PxMat33(PxZero)); + } + + static SpInertia dyad(const Cm::SpatialVector& column, const Cm::SpatialVector& row) + { + return SpInertia(dyad(column.linear, row.linear), + dyad(column.linear, row.angular), + dyad(column.angular, row.angular)); + } + + + static SpInertia inertia(PxReal mass, const PxVec3& inertia) + { + return SpInertia(PxMat33::createDiagonal(PxVec3(mass,mass,mass)), PxMat33(PxZero), + PxMat33::createDiagonal(inertia)); + } + + + SpInertia operator+(const SpInertia& m) const + { + return SpInertia(mLL+m.mLL, mLA+m.mLA, mAA+m.mAA); + } + + SpInertia operator-(const SpInertia& m) const + { + return SpInertia(mLL-m.mLL, mLA-m.mLA, mAA-m.mAA); + } + + SpInertia operator*(PxReal r) const + { + return SpInertia(mLL*r, mLA*r, mAA*r); + } + + void operator+=(const SpInertia& m) + { + mLL+=m.mLL; + mLA+=m.mLA; + mAA+=m.mAA; + } + + void operator-=(const SpInertia& m) + { + mLL-=m.mLL; + mLA-=m.mLA; + mAA-=m.mAA; + } + + + PX_FORCE_INLINE Cm::SpatialVector operator *(const Cm::SpatialVector& v) const + { + return Cm::SpatialVector(mLL*v.linear +mLA*v.angular, + mLA.transformTranspose(v.linear)+mAA*v.angular); + } + + SpInertia operator *(const SpInertia& v) const + { + return SpInertia(mLL*v.mLL + mLA * v.mLA.getTranspose(), + mLL*v.mLA + mLA * v.mAA, + mLA.getTranspose()*v.mLA + mAA * v.mAA); + } + + + bool isFinite() const + { + return true; +// return mLL.isFinite() && mLA.isFinite() && mAA.isFinite(); + } + + PxMat33 mLL, mLA; // linear force from angular motion, linear force from linear motion + PxMat33 mAA; // angular force from angular motion, mAL = mLA.transpose() + +private: + static PxMat33 dyad(PxVec3 col, PxVec3 row) + { + return PxMat33(col*row.x, col*row.y, col*row.z); + } + + +}; + +} +} + +#endif //DY_SPATIAL_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp new file mode 100644 index 000000000..ee854ae3b --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp @@ -0,0 +1,3381 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "PxSceneDesc.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContact4.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DyDynamics.h" +#include "DyArticulationContactPrep.h" +#include "PxsContactManager.h" +#include "PsFoundation.h" +#include "DyFeatherstoneArticulation.h" +#include "DyFeatherstoneArticulationLink.h" +#include "DyFeatherstoneArticulationJointData.h" + +using namespace physx; +using namespace Gu; + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DyContactPrepShared.h" +#include "DySolverConstraint1D.h" +#include "DyConstraintPrep.h" +#include "DyTGSDynamics.h" + +#include "CmConeLimitHelper.h" + +#include "DySolverContext.h" +#include "DySolverConstraint1DStep.h" + +using namespace Ps::aos; + + +namespace physx +{ +namespace Dy +{ + PX_FORCE_INLINE PxReal safeRecip(const PxReal x) + { + return x > PX_EPS_F32 ? 1.f/x : 0.f; + } + + PX_FORCE_INLINE void computeBlockStreamByteSizesStep(const bool useExtContacts, const CorrelationBuffer& c, + PxU32& _solverConstraintByteSize, PxU32& _frictionPatchByteSize, PxU32& _numFrictionPatches, + PxU32& _axisConstraintCount, PxReal torsionalPatchRadius) + { + PX_ASSERT(0 == _solverConstraintByteSize); + PX_ASSERT(0 == _frictionPatchByteSize); + PX_ASSERT(0 == _numFrictionPatches); + PX_ASSERT(0 == _axisConstraintCount); + + // PT: use local vars to remove LHS + PxU32 solverConstraintByteSize = 0; + PxU32 numFrictionPatches = 0; + PxU32 axisConstraintCount = 0; + + + for (PxU32 i = 0; i < c.frictionPatchCount; i++) + { + //Friction patches. + if (c.correlationListHeads[i] != CorrelationBuffer::LIST_END) + numFrictionPatches++; + + const FrictionPatch& frictionPatch = c.frictionPatches[i]; + + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0; + + //Solver constraint data. + if (c.frictionPatchContactCounts[i] != 0) + { + solverConstraintByteSize += sizeof(SolverContactHeaderStep); + solverConstraintByteSize += useExtContacts ? c.frictionPatchContactCounts[i] * sizeof(SolverContactPointStepExt) + : c.frictionPatchContactCounts[i] * sizeof(SolverContactPointStep); + solverConstraintByteSize += sizeof(PxF32) * ((c.frictionPatchContactCounts[i] + 3)&(~3)); //Add on space for applied impulses + + axisConstraintCount += c.frictionPatchContactCounts[i]; + + if (haveFriction) + { + PxU32 nbAnchors = PxU32(c.frictionPatches[i].anchorCount * 2); + if (torsionalPatchRadius > 0.f && c.frictionPatches[i].anchorCount == 1) + nbAnchors++; + solverConstraintByteSize += useExtContacts ? nbAnchors * sizeof(SolverContactFrictionStepExt) + : nbAnchors * sizeof(SolverContactFrictionStep); + axisConstraintCount += nbAnchors; + + } + } + } + PxU32 frictionPatchByteSize = numFrictionPatches*sizeof(FrictionPatch); + + _numFrictionPatches = numFrictionPatches; + _axisConstraintCount = axisConstraintCount; + + //16-byte alignment. + _frictionPatchByteSize = ((frictionPatchByteSize + 0x0f) & ~0x0f); + _solverConstraintByteSize = ((solverConstraintByteSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); + PX_ASSERT(0 == (_frictionPatchByteSize & 0x0f)); + } + + static bool reserveBlockStreams(const bool useExtContacts, Dy::CorrelationBuffer& cBuffer, + PxU8*& solverConstraint, + FrictionPatch*& _frictionPatches, + PxU32& numFrictionPatches, PxU32& solverConstraintByteSize, + PxU32& axisConstraintCount, PxConstraintAllocator& constraintAllocator, + PxReal torsionalPatchRadius) + { + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(NULL == _frictionPatches); + PX_ASSERT(0 == numFrictionPatches); + PX_ASSERT(0 == solverConstraintByteSize); + PX_ASSERT(0 == axisConstraintCount); + + //From frictionPatchStream we just need to reserve a single buffer. + PxU32 frictionPatchByteSize = 0; + //Compute the sizes of all the buffers. + computeBlockStreamByteSizesStep( + useExtContacts, cBuffer, + solverConstraintByteSize, frictionPatchByteSize, numFrictionPatches, + axisConstraintCount, torsionalPatchRadius); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if (constraintBlockByteSize > 0) + { + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if (0 == constraintBlock || (reinterpret_cast(-1)) == constraintBlock) + { + if (0 == constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock = NULL; + } + } + PX_ASSERT((size_t(constraintBlock) & 0xF) == 0); + } + + FrictionPatch* frictionPatches = NULL; + //If the constraint block reservation didn't fail then reserve the friction buffer too. + if (frictionPatchByteSize > 0 && (0 == constraintBlockByteSize || constraintBlock)) + { + frictionPatches = reinterpret_cast(constraintAllocator.reserveFrictionData(frictionPatchByteSize)); + + if (0 == frictionPatches || (reinterpret_cast(-1)) == frictionPatches) + { + if (0 == frictionPatches) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of friction data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + frictionPatches = NULL; + } + } + } + + _frictionPatches = frictionPatches; + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if (0 == constraintBlockByteSize || constraintBlock) + { + if (solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0 == (uintptr_t(solverConstraint) & 0x0f)); + } + } + + //Return true if neither of the two block reservations failed. + return ((0 == constraintBlockByteSize || constraintBlock) && (0 == frictionPatchByteSize || frictionPatches)); + } + + class SolverExtBodyStep + { + public: + union + { + const ArticulationV* mArticulation; + const PxTGSSolverBodyVel* mBody; + }; + + const PxTGSSolverBodyTxInertia* mTxI; + const PxStepSolverBodyData* mData; + + PxU16 mLinkIndex; + + SolverExtBodyStep(const void* bodyOrArticulation, const PxTGSSolverBodyTxInertia* txI, + const PxStepSolverBodyData* data, PxU16 linkIndex) : + mBody(reinterpret_cast(bodyOrArticulation)), + mTxI(txI), + mData(data), + mLinkIndex(linkIndex) + {} + + PxReal projectVelocity(const PxVec3& linear, const PxVec3& angular) const; + PxVec3 getLinVel() const; + PxVec3 getAngVel() const; + bool isKinematic() const + { + return (mLinkIndex == PxSolverConstraintDesc::NO_LINK) && + mBody->isKinematic; + } + }; + + Cm::SpatialVector createImpulseResponseVector(const PxVec3& linear, const PxVec3& angular, const SolverExtBodyStep& body) + { + if (body.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + return Cm::SpatialVector(linear, body.mTxI->sqrtInvInertia * angular); + } + return Cm::SpatialVector(linear, angular); + } + + + PxReal getImpulseResponse(const SolverExtBodyStep& b0, const Cm::SpatialVector& impulse0, Cm::SpatialVector& deltaV0, PxReal dom0, PxReal angDom0, + const SolverExtBodyStep& b1, const Cm::SpatialVector& impulse1, Cm::SpatialVector& deltaV1, PxReal dom1, PxReal angDom1, + bool allowSelfCollision) + { + PxReal response; + if (allowSelfCollision && b0.mArticulation == b1.mArticulation) + { + Cm::SpatialVectorF Z[64]; + b0.mArticulation->getImpulseSelfResponse(b0.mLinkIndex, b1.mLinkIndex, Z, + impulse0.scale(dom0, angDom0), impulse1.scale(dom1, angDom1), deltaV0, deltaV1); + + response = impulse0.dot(deltaV0) + impulse1.dot(deltaV1); + } + else + { + + if (b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + deltaV0.linear = impulse0.linear * b0.mData->invMass * dom0; + deltaV0.angular =/* b0.mBody->sqrtInvInertia * */impulse0.angular * angDom0; + } + else + { + //ArticulationHelper::getImpulseResponse(*b0.mFsData, b0.mLinkIndex, impulse0.scale(dom0, angDom0), deltaV0); + const ArticulationV* articulation = b0.mArticulation; + Cm::SpatialVectorF Z[64]; + articulation->getImpulseResponse(b0.mLinkIndex, Z, impulse0.scale(dom0, angDom0), deltaV0); + } + + response = impulse0.dot(deltaV0); + if (b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + deltaV1.linear = impulse1.linear * b1.mData->invMass * dom1; + deltaV1.angular = /*b1.mBody->sqrtInvInertia * */impulse1.angular * angDom1; + } + else + { + const ArticulationV* articulation = b1.mArticulation; + Cm::SpatialVectorF Z[64]; + articulation->getImpulseResponse(b1.mLinkIndex, Z, impulse1.scale(dom1, angDom1), deltaV1); + //ArticulationHelper::getImpulseResponse(*b1.mFsData, b1.mLinkIndex, impulse1.scale(dom1, angDom1), deltaV1); + + } + response += impulse1.dot(deltaV1); + } + + return response; + } + + + PxReal SolverExtBodyStep::projectVelocity(const PxVec3& linear, const PxVec3& angular) const + { + if (mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + return mData->projectVelocity(linear, angular); + } + else + { + Cm::SpatialVectorV velocities = mArticulation->getLinkVelocity(mLinkIndex); + FloatV fv = velocities.dot(Cm::SpatialVector(linear, angular)); + PxF32 f; + FStore(fv, &f); + /*PxF32 f; + FStore(getVelocity(*mFsData)[mLinkIndex].dot(Cm::SpatialVector(linear, angular)), &f); + return f;*/ + return f; + } + } + + + + static FloatV constructContactConstraintStep(const Mat33V& sqrtInvInertia0, const Mat33V& sqrtInvInertia1, const FloatVArg invMassNorLenSq0, + const FloatVArg invMassNorLenSq1, const FloatVArg angD0, const FloatVArg angD1, const Vec3VArg bodyFrame0p, const Vec3VArg bodyFrame1p, + const QuatV& /*bodyFrame0q*/, const QuatV& /*bodyFrame1q*/, + const Vec3VArg normal, const FloatVArg norVel0, const FloatVArg norVel1, const VecCrossV& norCross, const Vec3VArg angVel0, const Vec3VArg angVel1, + const FloatVArg invDt, const FloatVArg invDtp8, const FloatVArg restDistance, const FloatVArg restitution, + const FloatVArg bounceThreshold, const Gu::ContactPoint& contact, SolverContactPointStep& solverContact, + const FloatVArg /*ccdMaxSeparation*/, const bool isKinematic0, const bool isKinematic1) + { + const FloatV zero = FZero(); + const Vec3V point = V3LoadA(contact.point); + const FloatV separation = FLoad(contact.separation); + + const FloatV cTargetVel = V3Dot(normal, V3LoadA(contact.targetVel)); + + const Vec3V ra = V3Sub(point, bodyFrame0p); + const Vec3V rb = V3Sub(point, bodyFrame1p); + + const Vec3V raXn = V3Cross(ra, norCross); + const Vec3V rbXn = V3Cross(rb, norCross); + + const Vec3V raXnInertia = M33MulV3(sqrtInvInertia0, raXn); + const Vec3V rbXnInertia = M33MulV3(sqrtInvInertia1, rbXn); + + const FloatV resp0 = FAdd(invMassNorLenSq0, FMul(V3Dot(raXnInertia, raXnInertia), angD0)); + const FloatV resp1 = FSub(FMul(V3Dot(rbXnInertia, rbXnInertia), angD1), invMassNorLenSq1); + + const FloatV unitResponse = FAdd(resp0, resp1); + + const FloatV vrel1 = FAdd(norVel0, V3Dot(raXn, angVel0)); + const FloatV vrel2 = FAdd(norVel1, V3Dot(rbXn, angVel1)); + const FloatV vrel = FSub(vrel1, vrel2); + + const FloatV penetration = FSub(separation, restDistance); + + const FloatV penetrationInvDt = FMul(penetration, invDt); + + FloatV scaledBias = FNeg(invDtp8); + + const BoolV isGreater2 = BAnd(BAnd(FIsGrtr(restitution, zero), FIsGrtr(bounceThreshold, vrel)), FIsGrtr(FNeg(vrel), penetrationInvDt)); + + const FloatV totalError = penetration; + + const FloatV sumVRel(vrel); + + const FloatV velMultiplier = FSel(FIsGrtr(unitResponse, FZero()), FRecip(unitResponse), FZero()); + + FloatV targetVelocity = FAdd(cTargetVel, FSel(isGreater2, FMul(FNeg(sumVRel), restitution), zero)); + + if(isKinematic0) + targetVelocity = FSub(targetVelocity, vrel1); + if(isKinematic1) + targetVelocity = FAdd(targetVelocity, vrel2); + + //KS - don't store scaled by angD0/angD1 here! It'll corrupt the velocity projection... + V3StoreA(raXnInertia, solverContact.raXnI); + V3StoreA(rbXnInertia, solverContact.rbXnI); + + FStore(velMultiplier, &solverContact.velMultiplier); + + FStore(totalError, &solverContact.separation); + FStore(scaledBias, &solverContact.biasCoefficient); + FStore(targetVelocity, &solverContact.targetVelocity); + + return penetration; + + } + + + static void setupFinalizeSolverConstraints(Sc::ShapeInteraction* shapeInteraction, + const ContactPoint* buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const PxTGSSolverBodyVel& b0, + const PxTGSSolverBodyVel& b1, + const PxTGSSolverBodyTxInertia& txI0, + const PxTGSSolverBodyTxInertia& txI1, + const PxStepSolverBodyData& data0, + const PxStepSolverBodyData& data1, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + bool hasForceThreshold, bool staticOrKinematicBody, + const PxReal restDist, PxU8* frictionDataPtr, + const PxReal maxCCDSeparation, + const bool disableStrongFriction, + const PxReal torsionalPatchRadiusF32, + const PxReal minTorsionalPatchRadiusF32) + { + bool hasTorsionalFriction = torsionalPatchRadiusF32 > 0.f || minTorsionalPatchRadiusF32 > 0.f; + + // NOTE II: the friction patches are sparse (some of them have no contact patches, and + // therefore did not get written back to the cache) but the patch addresses are dense, + // corresponding to valid patches + + const bool isKinematic0 = b0.isKinematic; + const bool isKinematic1 = b1.isKinematic; + + const FloatV ccdMaxSeparation = FLoad(maxCCDSeparation); + + PxU8 flags = PxU8(hasForceThreshold ? SolverContactHeaderStep::eHAS_FORCE_THRESHOLDS : 0); + + PxU8* PX_RESTRICT ptr = workspace; + + PxU8 type = Ps::to8(staticOrKinematicBody ? DY_SC_TYPE_STATIC_CONTACT + : DY_SC_TYPE_RB_CONTACT); + + const FloatV zero = FZero(); + + const FloatV d0 = FLoad(invMassScale0); + const FloatV d1 = FLoad(invMassScale1); + const FloatV angD0 = FLoad(invInertiaScale0); + const FloatV angD1 = FLoad(invInertiaScale1); + + const FloatV nDom1fV = FNeg(d1); + + const FloatV invMass0 = FLoad(data0.invMass); + const FloatV invMass1 = FLoad(data1.invMass); + + const FloatV invMass0_dom0fV = FMul(d0, invMass0); + const FloatV invMass1_dom1fV = FMul(nDom1fV, invMass1); + + Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4Zero(); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, invMass0_dom0fV); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, invMass1_dom1fV); + + const FloatV restDistance = FLoad(restDist); + + const PxReal maxPenBias = PxMax(data0.penBiasClamp, data1.penBiasClamp); + + const QuatV bodyFrame0q = QuatVLoadU(&bodyFrame0.q.x); + const Vec3V bodyFrame0p = V3LoadU(bodyFrame0.p); + + const QuatV bodyFrame1q = QuatVLoadU(&bodyFrame1.q.x); + const Vec3V bodyFrame1p = V3LoadU(bodyFrame1.p); + + + + PxU32 frictionPatchWritebackAddrIndex = 0; + PxU32 contactWritebackCount = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + //const Vec3V linVel0 = V3LoadU_SafeReadW(b0.linearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + //const Vec3V linVel1 = V3LoadU_SafeReadW(b1.linearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + //const Vec3V angVel0 = V3LoadU_SafeReadW(b0.angularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + //const Vec3V angVel1 = V3LoadU_SafeReadW(b1.angularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + + + const Vec3V linVel0 = V3LoadU_SafeReadW(data0.originalLinearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + const Vec3V linVel1 = V3LoadU_SafeReadW(data1.originalLinearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + const Vec3V angVel0 = V3LoadU_SafeReadW(data0.originalAngularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + const Vec3V angVel1 = V3LoadU_SafeReadW(data1.originalAngularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + + + PX_ALIGN(16, const Mat33V sqrtInvInertia0) + ( + V3LoadU_SafeReadW(txI0.sqrtInvInertia.column0), // PT: safe because 'column1' follows 'column0' in PxMat33 + V3LoadU_SafeReadW(txI0.sqrtInvInertia.column1), // PT: safe because 'column2' follows 'column1' in PxMat33 + V3LoadU(txI0.sqrtInvInertia.column2) + ); + + PX_ALIGN(16, const Mat33V sqrtInvInertia1) + ( + V3LoadU_SafeReadW(txI1.sqrtInvInertia.column0), // PT: safe because 'column1' follows 'column0' in PxMat33 + V3LoadU_SafeReadW(txI1.sqrtInvInertia.column1), // PT: safe because 'column2' follows 'column1' in PxMat33 + V3LoadU(txI1.sqrtInvInertia.column2) + ); + + const FloatV invDt = FLoad(invDtF32); + const FloatV invTotalDt = FLoad(invTotalDtF32); + const FloatV p8 = FLoad(0.8f); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + + const FloatV invDtp8 = FMul(invDt, p8); + + const PxReal frictionBiasScale = disableStrongFriction ? 0.f : invDtF32; + + + for (PxU32 i = 0; irestitution; + + SolverContactHeaderStep* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeaderStep); + + + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + + header->shapeInteraction = shapeInteraction; + header->flags = flags; + FStore(invMass0_dom0fV, &header->invMass0); + FStore(FNeg(invMass1_dom1fV), &header->invMass1); + const FloatV restitution = FLoad(combinedRestitution); + + PxU32 pointStride = sizeof(SolverContactPointStep); + PxU32 frictionStride = sizeof(SolverContactFrictionStep); + + const Vec3V normal = V3LoadA(buffer[c.contactPatches[c.correlationListHeads[i]].start].normal); + const FloatV normalLenSq = V3LengthSq(normal); + const VecCrossV norCross = V3PrepareCross(normal); + //const FloatV norVel = V3SumElems(V3NegMulSub(normal, linVel1, V3Mul(normal, linVel0))); + const FloatV norVel0 = V3Dot(linVel0, normal); + const FloatV norVel1 = V3Dot(linVel1, normal); + + const FloatV invMassNorLenSq0 = FMul(invMass0_dom0fV, normalLenSq); + const FloatV invMassNorLenSq1 = FMul(invMass1_dom1fV, normalLenSq); + + V3StoreA(normal, header->normal); + header->maxPenBias = maxPenBias; + + FloatV maxPenetration = FMax(); + + for (PxU32 patch = c.correlationListHeads[i]; + patch != CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer + c.contactPatches[patch].start; + + PxU8* p = ptr; + + for (PxU32 j = 0; j(p); + p += pointStride; + + maxPenetration = FMin(maxPenetration, constructContactConstraintStep(sqrtInvInertia0, sqrtInvInertia1, invMassNorLenSq0, + invMassNorLenSq1, angD0, angD1, bodyFrame0p, bodyFrame1p, bodyFrame0q, bodyFrame1q, + normal, norVel0, norVel1, norCross, angVel0, angVel1, + invTotalDt, invDtp8, restDistance, restitution, + bounceThreshold, contact, *solverContact, + ccdMaxSeparation, isKinematic0, isKinematic1)); + } + + ptr = p; + } + + contactWritebackCount += contactCount; + + PxF32* forceBuffers = reinterpret_cast(ptr); + PxMemZero(forceBuffers, sizeof(PxF32) * contactCount); + ptr += ((contactCount + 3) & (~3)) * sizeof(PxF32); // jump to next 16-byte boundary + + const PxReal staticFriction = contactBase0->staticFriction; + const PxReal dynamicFriction = contactBase0->dynamicFriction; + const bool disableFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); + + const bool haveFriction = (disableFriction == 0 && frictionPatch.anchorCount != 0);//PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0; + header->numNormalConstr = Ps::to8(contactCount); + header->numFrictionConstr = Ps::to8(haveFriction ? frictionPatch.anchorCount * 2 : 0); + + header->type = type; + + header->staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; + FStore(angD0, &header->angDom0); + FStore(angD1, &header->angDom1); + + header->broken = 0; + + if (haveFriction) + { + + + const Vec3V linVrel = V3Sub(linVel0, linVel1); + //const Vec3V normal = Vec3V_From_PxVec3_Aligned(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); + + const FloatV orthoThreshold = FLoad(0.70710678f); + const FloatV p1 = FLoad(0.1f); + // fallback: normal.cross((1,0,0)) or normal.cross((0,0,1)) + const FloatV normalX = V3GetX(normal); + const FloatV normalY = V3GetY(normal); + const FloatV normalZ = V3GetZ(normal); + + Vec3V t0Fallback1 = V3Merge(zero, FNeg(normalZ), normalY); + Vec3V t0Fallback2 = V3Merge(FNeg(normalY), normalX, zero); + Vec3V t0Fallback = V3Sel(FIsGrtr(orthoThreshold, FAbs(normalX)), t0Fallback1, t0Fallback2); + + Vec3V t0 = V3Sub(linVrel, V3Scale(normal, V3Dot(normal, linVrel))); + t0 = V3Sel(FIsGrtr(V3LengthSq(t0), p1), t0, t0Fallback); + t0 = V3Normalize(t0); + + const VecCrossV t0Cross = V3PrepareCross(t0); + + const Vec3V t1 = V3Cross(norCross, t0Cross); + //const VecCrossV t1Cross = V3PrepareCross(t1); + + + // since we don't even have the body velocities we can't compute the tangent dirs, so + // the only thing we can do right now is to write the geometric information (which is the + // same for both axis constraints of an anchor) We put ra in the raXn field, rb in the rbXn + // field, and the error in the normal field. See corresponding comments in + // completeContactFriction() + + //We want to set the writeBack ptr to point to the broken flag of the friction patch. + //On spu we have a slight problem here because the friction patch array is + //in local store rather than in main memory. The good news is that the address of the friction + //patch array in main memory is stored in the work unit. These two addresses will be equal + //except on spu where one is local store memory and the other is the effective address in main memory. + //Using the value stored in the work unit guarantees that the main memory address is used on all platforms. + PxU8* PX_RESTRICT writeback = frictionDataPtr + frictionPatchWritebackAddrIndex*sizeof(FrictionPatch); + + + + const FloatV norVel00 = V3Dot(linVel0, t0); + const FloatV norVel01 = V3Dot(linVel1, t0); + const FloatV norVel10 = V3Dot(linVel0, t1); + const FloatV norVel11 = V3Dot(linVel1, t1); + + const Vec3V relTr = V3Sub(bodyFrame0p, bodyFrame1p); + + header->frictionBrokenWritebackByte = writeback; + + for (PxU32 j = 0; j < frictionPatch.anchorCount; j++) + { + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + SolverContactFrictionStep* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionStride; + SolverContactFrictionStep* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionStride; + + Vec3V body0Anchor = V3LoadU(frictionPatch.body0Anchors[j]); + Vec3V body1Anchor = V3LoadU(frictionPatch.body1Anchors[j]); + + Vec3V ra = QuatRotate(bodyFrame0q, body0Anchor); + Vec3V rb = QuatRotate(bodyFrame1q, body1Anchor); + + PxU32 index = c.contactID[i][j]; + + index = index == 0xFFFF ? c.contactPatches[c.correlationListHeads[i]].start : index; + + const Vec3V tvel = V3LoadA(buffer[index].targetVel); + + const Vec3V error = V3Add(V3Sub(ra, rb), relTr); + + + + { + const Vec3V raXn = V3Cross(ra, t0); + const Vec3V rbXn = V3Cross(rb, t0); + const Vec3V raXnInertia = M33MulV3(sqrtInvInertia0, raXn); + const Vec3V rbXnInertia = M33MulV3(sqrtInvInertia1, rbXn); + + const FloatV resp0 = FAdd(invMassNorLenSq0, FMul(V3Dot(raXnInertia, raXnInertia), angD0)); + const FloatV resp1 = FSub(FMul(V3Dot(rbXnInertia, rbXnInertia), angD1), invMassNorLenSq1); + + const FloatV unitResponse = FAdd(resp0, resp1); + + const FloatV velMultiplier = FSel(FIsGrtr(unitResponse, zero), FDiv(p8, unitResponse), zero); + + FloatV targetVel = V3Dot(tvel, t0); + + if(isKinematic0) + targetVel = FSub(targetVel, FAdd(norVel00, V3Dot(raXn, angVel0))); + if (isKinematic1) + targetVel = FAdd(targetVel, FAdd(norVel01, V3Dot(rbXn, angVel1))); + + f0->normalXYZ_ErrorW = V4SetW(t0, V3Dot(error, t0)); + //f0->raXnXYZ_targetVelW = V4SetW(body0Anchor, targetVel); + //f0->rbXnXYZ_biasW = V4SetW(body1Anchor, FZero()); + /*f0->raXn_biasScaleW = V4SetW(Vec4V_From_Vec3V(raXn), frictionBiasScale); + f0->rbXn_errorW = V4SetW(rbXn, V3Dot(error, t0));*/ + f0->raXnI_targetVelW = V4SetW(raXnInertia, targetVel); + f0->rbXnI_velMultiplierW = V4SetW(rbXnInertia, velMultiplier); + f0->appliedForce = 0.f; + f0->frictionScale = 1.f; + f0->biasScale = frictionBiasScale; + } + + { + FloatV targetVel = V3Dot(tvel, t1); + + + const Vec3V raXn = V3Cross(ra, t1); + const Vec3V rbXn = V3Cross(rb, t1); + const Vec3V raXnInertia = M33MulV3(sqrtInvInertia0, raXn); + const Vec3V rbXnInertia = M33MulV3(sqrtInvInertia1, rbXn); + + const FloatV resp0 = FAdd(invMassNorLenSq0, FMul(V3Dot(raXnInertia, raXnInertia), angD0)); + const FloatV resp1 = FSub(FMul(V3Dot(rbXnInertia, rbXnInertia), angD1), invMassNorLenSq1); + + const FloatV unitResponse = FAdd(resp0, resp1); + + const FloatV velMultiplier = FSel(FIsGrtr(unitResponse, zero), FDiv(p8, unitResponse), zero); + + if (isKinematic0) + targetVel = FSub(targetVel, FAdd(norVel10, V3Dot(raXn, angVel0))); + if (isKinematic1) + targetVel = FAdd(targetVel, FAdd(norVel11, V3Dot(rbXn, angVel1))); + + + f1->normalXYZ_ErrorW = V4SetW(t1, V3Dot(error, t1)); + //f1->raXnXYZ_targetVelW = V4SetW(body0Anchor, targetVel); + //f1->rbXnXYZ_biasW = V4SetW(body1Anchor, FZero()); + //f1->raXn_biasScaleW = V4SetW(Vec4V_From_Vec3V(V3Cross(ra, t1)), frictionBiasScale); + //f1->rbXn_errorW = V4SetW(V3Cross(rb, t1), V3Dot(error, t1)); + f1->raXnI_targetVelW = V4SetW(raXnInertia, targetVel); + f1->rbXnI_velMultiplierW = V4SetW(rbXnInertia, velMultiplier); + f1->appliedForce = 0.f; + f1->frictionScale = 1.f; + f1->biasScale = frictionBiasScale; + } + } + + if (hasTorsionalFriction && frictionPatch.anchorCount == 1) + { + const FloatV torsionalPatchRadius = FLoad(torsionalPatchRadiusF32); + const FloatV minTorsionalPatchRadius = FLoad(minTorsionalPatchRadiusF32); + const FloatV torsionalFriction = FMax(minTorsionalPatchRadius, FSqrt(FMul(FMax(zero, FNeg(maxPenetration)), torsionalPatchRadius))); + header->numFrictionConstr++; + SolverContactFrictionStep* PX_RESTRICT f = reinterpret_cast(ptr); + ptr += frictionStride; + + //Rotate the oldRelativeQuat into world space to get the new target relative quat + const PxQuat newTargetQ1 = txI0.deltaBody2World.q * frictionPatch.relativeQuat; + //Now we need to find the rotation around "normal" from Q1 to newTargetQ1. This is the error... + const PxQuat deltaQ = newTargetQ1.getConjugate() * txI1.deltaBody2World.q; + + const PxVec3 nTemp = buffer[c.contactPatches[c.correlationListHeads[i]].start].normal; + + PxQuat temp(deltaQ.x*nTemp.x, deltaQ.y*nTemp.y, deltaQ.z*nTemp.z, deltaQ.w); + + const PxReal magnitude = temp.normalize(); + + PxReal angle = PxAtan(physx::intrinsics::fsel(magnitude - 1e-6f, temp.dot(PxQuat(nTemp.x, nTemp.y, nTemp.z, 0.f)) / temp.w, 0.f)); + + //OK. We have old relative quat and new relative quat. Now find difference + + + const Vec3V raXnInertia = M33MulV3(sqrtInvInertia0, normal); + const Vec3V rbXnInertia = M33MulV3(sqrtInvInertia1, normal); + + const FloatV resp0 = FMul(V3Dot(raXnInertia, raXnInertia), angD0); + const FloatV resp1 = FMul(V3Dot(rbXnInertia, rbXnInertia), angD1); + + const FloatV unitResponse = FAdd(resp0, resp1); + + const FloatV velMultiplier = FSel(FIsGrtr(unitResponse, zero), FDiv(p8, unitResponse), zero); + + FloatV targetVel = zero; + + if (isKinematic0) + targetVel = V3Dot(normal, angVel0); + if (isKinematic1) + targetVel = V3Dot(normal, angVel1); + + f->normalXYZ_ErrorW = V4SetW(V3Zero(), FLoad(-angle)); + f->raXnI_targetVelW = V4SetW(raXnInertia, targetVel); + f->rbXnI_velMultiplierW = V4SetW(rbXnInertia, velMultiplier); + f->biasScale = frictionBiasScale; + f->appliedForce = 0.f; + FStore(torsionalFriction, &f->frictionScale); + } + } + + frictionPatchWritebackAddrIndex++; + } + } + + + static FloatV setupExtSolverContactStep(const SolverExtBodyStep& b0, const SolverExtBodyStep& b1, + const PxF32 d0, const PxF32 d1, const PxF32 angD0, const PxF32 angD1, const PxTransform& bodyFrame0, const PxTransform& bodyFrame1, + const Vec3VArg normal, const FloatVArg invDt, const FloatVArg invDtp8, const FloatVArg restDistance, const FloatVArg restitution, + const FloatVArg bounceThreshold, const Gu::ContactPoint& contact, SolverContactPointStepExt& solverContact, const FloatVArg /*ccdMaxSeparation*/, + const bool isKinematic0, const bool isKinematic1) + { + const FloatV zero = FZero(); + const FloatV separation = FLoad(contact.separation); + + const FloatV penetration = FSub(separation, restDistance); + + const PxVec3 ra = contact.point - bodyFrame0.p; + const PxVec3 rb = contact.point - bodyFrame1.p; + + const PxVec3 raXn = ra.cross(contact.normal); + const PxVec3 rbXn = rb.cross(contact.normal); + + Cm::SpatialVector deltaV0, deltaV1; + + const Cm::SpatialVector resp0 = createImpulseResponseVector(contact.normal, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-contact.normal, -rbXn, b1); + + PxReal resp = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, false); + + FloatV unitResponse = FLoad(resp); + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK && + b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + PxReal lr0 = deltaV0.linear.dot(resp0.linear); + PxReal ar0 = deltaV0.angular.dot(resp0.angular) * safeRecip(resp0.angular.magnitude()); + PxReal lr1 = deltaV1.linear.dot(resp1.linear); + PxReal ar1 = deltaV1.angular.dot(resp1.angular)* safeRecip(resp1.angular.magnitude()); + + Cm::SpatialVector rem0(deltaV0.linear - resp0.linear*lr0, deltaV0.angular - resp0.angular*ar0); + Cm::SpatialVector rem1(deltaV1.linear - resp1.linear*lr1, deltaV1.angular - resp1.angular*ar1); + + + PxReal rem = (rem0 - rem1).magnitude(); + + unitResponse = FAdd(unitResponse, FLoad(rem)); + } + + const FloatV vel0 = FLoad(b0.projectVelocity(contact.normal, raXn)); + const FloatV vel1 = FLoad(b1.projectVelocity(contact.normal, rbXn)); + const FloatV vrel = FSub(vel0, vel1); + + + + + FloatV velMultiplier = FSel(FIsGrtr(unitResponse, FEps()), FRecip(unitResponse), zero); + FloatV scaledBias = invDtp8; + const FloatV penetrationInvDt = FMul(penetration, invDt); + + const BoolV isGreater2 = BAnd(BAnd(FIsGrtr(restitution, zero), FIsGrtr(bounceThreshold, vrel)), FIsGrtr(FNeg(vrel), penetrationInvDt)); + + //scaledBias = FSel(isGreater2, FZero(), FNeg(scaledBias)); + scaledBias = FNeg(scaledBias); + + FloatV targetVelocity = FSel(isGreater2, FMul(FNeg(vrel), restitution), zero); + + targetVelocity = FAdd(targetVelocity, V3Dot(V3LoadA(contact.targetVel), normal)); + + if (isKinematic0) + targetVelocity = FSub(targetVelocity, vel0); + if(isKinematic1) + targetVelocity = FAdd(targetVelocity, vel1); + + const FloatV biasedErr = FMul(FAdd(targetVelocity, FMul(penetration, scaledBias)),velMultiplier); + + const FloatV deltaF = FMax(FNegScaleSub(vrel, velMultiplier, biasedErr), zero); + + + FStore(velMultiplier, &solverContact.velMultiplier); + FStore(scaledBias, &solverContact.biasCoefficient); + FStore(penetration, &solverContact.separation); + FStore(targetVelocity, &solverContact.targetVelocity); + + solverContact.raXnI = resp0.angular; + solverContact.rbXnI = -resp1.angular; + solverContact.linDeltaVA = V3LoadA(deltaV0.linear); + solverContact.angDeltaVA = V3LoadA(deltaV0.angular); + solverContact.linDeltaVB = V3LoadA(deltaV1.linear); + solverContact.angDeltaVB = V3LoadA(deltaV1.angular); + + return deltaF; + } + + PX_INLINE void computeFrictionTangents(const PxVec3& vrel, const PxVec3& unitNormal, PxVec3& t0, PxVec3& t1) + { + PX_ASSERT(PxAbs(unitNormal.magnitude() - 1)<1e-3f); + + t0 = vrel - unitNormal * unitNormal.dot(vrel); + PxReal ll = t0.magnitudeSquared(); + + if (ll > 0.1f) //can set as low as 0. + { + t0 *= PxRecipSqrt(ll); + t1 = unitNormal.cross(t0); + } + else + Ps::normalToTangents(unitNormal, t0, t1); //fallback + } + + PxVec3 SolverExtBodyStep::getLinVel() const + { + if (mLinkIndex == PxSolverConstraintDesc::NO_LINK) + return mBody->linearVelocity; + else + { + Cm::SpatialVectorV velocity = mArticulation->getLinkVelocity(mLinkIndex); + PxVec3 result; + V3StoreU(velocity.linear, result); + return result; + } + } + + + + void setupFinalizeExtSolverContactsStep( + const ContactPoint* buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const SolverExtBodyStep& b0, + const SolverExtBodyStep& b1, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + const PxReal restDist, + PxU8* frictionDataPtr, + PxReal ccdMaxContactDist, + const PxReal torsionalPatchRadiusF32, + const PxReal minTorsionalPatchRadiusF32) + { + // NOTE II: the friction patches are sparse (some of them have no contact patches, and + // therefore did not get written back to the cache) but the patch addresses are dense, + // corresponding to valid patches + + /*const bool haveFriction = PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0;*/ + + const bool isKinematic0 = b0.isKinematic(); + const bool isKinematic1 = b1.isKinematic(); + + const FloatV ccdMaxSeparation = FLoad(ccdMaxContactDist); + + bool hasTorsionalFriction = torsionalPatchRadiusF32 > 0.f || minTorsionalPatchRadiusF32 > 0.f; + + PxU8* PX_RESTRICT ptr = workspace; + + const FloatV zero = FZero(); + + //KS - TODO - this should all be done in SIMD to avoid LHS + /*const PxF32 maxPenBias0 = b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b0.mData->penBiasClamp : getMaxPenBias(*b0.mFsData)[b0.mLinkIndex]; + const PxF32 maxPenBias1 = b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b1.mData->penBiasClamp : getMaxPenBias(*b1.mFsData)[b1.mLinkIndex];*/ + + PxF32 maxPenBias0 = b0.mData->penBiasClamp; + PxF32 maxPenBias1 = b1.mData->penBiasClamp; + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias0 = b0.mArticulation->getLinkMaxPenBias(b0.mLinkIndex); + } + + if (b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias1 = b1.mArticulation->getLinkMaxPenBias(b1.mLinkIndex); + } + + const PxReal maxPenBias = PxMax(maxPenBias0, maxPenBias1); + + + const PxReal d0 = invMassScale0; + const PxReal d1 = invMassScale1; + + const PxReal angD0 = invInertiaScale0; + const PxReal angD1 = invInertiaScale1; + + Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4Zero(); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(d0)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(d1)); + + const FloatV restDistance = FLoad(restDist); + + PxU32 frictionPatchWritebackAddrIndex = 0; + PxU32 contactWritebackCount = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + const FloatV invDt = FLoad(invDtF32); + const FloatV invTotalDt = FLoad(invTotalDtF32); + const FloatV p8 = FLoad(0.8f); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + + const FloatV invDtp8 = FMul(invDt, p8); + + PxU8 flags = 0; + + for (PxU32 i = 0; irestitution; + + const PxReal staticFriction = contactBase0->staticFriction; + const PxReal dynamicFriction = contactBase0->dynamicFriction; + const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); + + const PxReal frictionBiasScale = disableStrongFriction ? 0.f : invDtF32; + + SolverContactHeaderStep* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeaderStep); + + + Ps::prefetchLine(ptr + 128); + Ps::prefetchLine(ptr + 256); + Ps::prefetchLine(ptr + 384); + + const bool haveFriction = (disableStrongFriction == 0);//PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0; + header->numNormalConstr = Ps::to8(contactCount); + header->numFrictionConstr = Ps::to8(haveFriction ? frictionPatch.anchorCount * 2 : 0); + + header->type = Ps::to8(DY_SC_TYPE_EXT_CONTACT); + + header->flags = flags; + + const FloatV restitution = FLoad(combinedRestitution); + + header->staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; + + header->angDom0 = angD0; + header->angDom1 = angD1; + + const PxU32 pointStride = sizeof(SolverContactPointStepExt); + const PxU32 frictionStride = sizeof(SolverContactFrictionStepExt); + + const Vec3V normal = V3LoadU(buffer[c.contactPatches[c.correlationListHeads[i]].start].normal); + + V3StoreA(normal, header->normal); + header->maxPenBias = maxPenBias; + + FloatV maxPenetration = FZero(); + + FloatV accumulatedImpulse = FZero(); + + for (PxU32 patch = c.correlationListHeads[i]; + patch != CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer + c.contactPatches[patch].start; + + PxU8* p = ptr; + + for (PxU32 j = 0; j(p); + p += pointStride; + + accumulatedImpulse = FAdd(accumulatedImpulse, setupExtSolverContactStep(b0, b1, d0, d1, angD0, angD1, bodyFrame0, bodyFrame1, normal, invTotalDt, invDtp8, restDistance, restitution, + bounceThreshold, contact, *solverContact, ccdMaxSeparation, isKinematic0, isKinematic1)); + + maxPenetration = FMin(FLoad(contact.separation), maxPenetration); + + } + + ptr = p; + } + contactWritebackCount += contactCount; + + accumulatedImpulse = FDiv(accumulatedImpulse, FLoad(PxF32(contactCount))); + + FStore(accumulatedImpulse, &header->minNormalForce); + + PxF32* forceBuffer = reinterpret_cast(ptr); + PxMemZero(forceBuffer, sizeof(PxF32) * contactCount); + ptr += sizeof(PxF32) * ((contactCount + 3) & (~3)); + + header->broken = 0; + + if (haveFriction) + { + //const Vec3V normal = Vec3V_From_PxVec3(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); + PxVec3 normalS = buffer[c.contactPatches[c.correlationListHeads[i]].start].normal; + + PxVec3 t0, t1; + computeFrictionTangents(b0.getLinVel() - b1.getLinVel(), normalS, t0, t1); + + Vec3V vT0 = V3LoadU(t0); + Vec3V vT1 = V3LoadU(t1); + + //We want to set the writeBack ptr to point to the broken flag of the friction patch. + //On spu we have a slight problem here because the friction patch array is + //in local store rather than in main memory. The good news is that the address of the friction + //patch array in main memory is stored in the work unit. These two addresses will be equal + //except on spu where one is local store memory and the other is the effective address in main memory. + //Using the value stored in the work unit guarantees that the main memory address is used on all platforms. + PxU8* PX_RESTRICT writeback = frictionDataPtr + frictionPatchWritebackAddrIndex * sizeof(FrictionPatch); + + header->frictionBrokenWritebackByte = writeback; + + for (PxU32 j = 0; j < frictionPatch.anchorCount; j++) + { + SolverContactFrictionStepExt* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionStride; + SolverContactFrictionStepExt* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionStride; + + PxVec3 ra = bodyFrame0.q.rotate(frictionPatch.body0Anchors[j]); + PxVec3 rb = bodyFrame1.q.rotate(frictionPatch.body1Anchors[j]); + PxVec3 error = (ra + bodyFrame0.p) - (rb + bodyFrame1.p); + + { + const PxVec3 raXn = ra.cross(t0); + const PxVec3 rbXn = rb.cross(t0); + + Cm::SpatialVector deltaV0, deltaV1; + + const Cm::SpatialVector resp0 = createImpulseResponseVector(t0, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-t0, -rbXn, b1); + PxReal ur = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, false); + FloatV resp = FLoad(ur); + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK && + b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + PxReal lr0 = deltaV0.linear.dot(resp0.linear); + PxReal ar0 = deltaV0.angular.dot(resp0.angular) * safeRecip(resp0.angular.magnitude()); + PxReal lr1 = deltaV1.linear.dot(resp1.linear); + PxReal ar1 = deltaV1.angular.dot(resp1.angular)* safeRecip(resp1.angular.magnitude()); + + Cm::SpatialVector rem0(deltaV0.linear - resp0.linear*lr0, deltaV0.angular - resp0.angular*ar0); + Cm::SpatialVector rem1(deltaV1.linear - resp1.linear*lr1, deltaV1.angular - resp1.angular*ar1); + + + PxReal rem = (rem0 - rem1).magnitude(); + + resp = FAdd(resp, FLoad(rem)); + } + + const FloatV velMultiplier = FSel(FIsGrtr(resp, FEps()), FDiv(p8, resp), zero); + + PxU32 index = c.contactPatches[c.correlationListHeads[i]].start; + PxF32 targetVel = buffer[index].targetVel.dot(t0); + + if(isKinematic0) + targetVel -= b0.projectVelocity(t0, raXn); + if(isKinematic1) + targetVel += b1.projectVelocity(t0, rbXn); + + f0->normalXYZ_ErrorW = V4SetW(vT0, FLoad(error.dot(t0))); + f0->raXnI_targetVelW = V4SetW(V4LoadA(&resp0.angular.x), FLoad(targetVel)); + f0->rbXnI_velMultiplierW = V4SetW(V4Neg(V4LoadA(&resp1.angular.x)), velMultiplier); + f0->appliedForce = 0.f; + f0->biasScale = frictionBiasScale; + f0->linDeltaVA = V3LoadA(deltaV0.linear); + f0->linDeltaVB = V3LoadA(deltaV1.linear); + f0->angDeltaVA = V3LoadA(deltaV0.angular); + f0->angDeltaVB = V3LoadA(deltaV1.angular); + } + + { + + const PxVec3 raXn = ra.cross(t1); + const PxVec3 rbXn = rb.cross(t1); + + Cm::SpatialVector deltaV0, deltaV1; + + + const Cm::SpatialVector resp0 = createImpulseResponseVector(t1, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-t1, -rbXn, b1); + + PxReal ur = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, false); + FloatV resp = FLoad(ur); + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK && + b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + PxReal lr0 = deltaV0.linear.dot(resp0.linear); + PxReal ar0 = deltaV0.angular.dot(resp0.angular) * safeRecip(resp0.angular.magnitude()); + PxReal lr1 = deltaV1.linear.dot(resp1.linear); + PxReal ar1 = deltaV1.angular.dot(resp1.angular)* safeRecip(resp1.angular.magnitude()); + + Cm::SpatialVector rem0(deltaV0.linear - resp0.linear*lr0, deltaV0.angular - resp0.angular*ar0); + Cm::SpatialVector rem1(deltaV1.linear - resp1.linear*lr1, deltaV1.angular - resp1.angular*ar1); + + + PxReal rem = (rem0 - rem1).magnitude(); + + resp = FAdd(resp, FLoad(rem)); + } + + const FloatV velMultiplier = FSel(FIsGrtr(resp, FEps()), FDiv(p8, resp), zero); + + PxU32 index = c.contactPatches[c.correlationListHeads[i]].start; + PxF32 targetVel = buffer[index].targetVel.dot(t1); + + if (isKinematic0) + targetVel -= b0.projectVelocity(t1, raXn); + if (isKinematic1) + targetVel += b1.projectVelocity(t1, rbXn); + + f1->normalXYZ_ErrorW = V4SetW(vT1, FLoad(error.dot(t1))); + f1->raXnI_targetVelW = V4SetW(V4LoadA(&resp0.angular.x), FLoad(targetVel)); + f1->rbXnI_velMultiplierW = V4SetW(V4Neg(V4LoadA(&resp1.angular.x)), velMultiplier); + f1->appliedForce = 0.f; + f1->biasScale = frictionBiasScale; + f1->linDeltaVA = V3LoadA(deltaV0.linear); + f1->linDeltaVB = V3LoadA(deltaV1.linear); + f1->angDeltaVA = V3LoadA(deltaV0.angular); + f1->angDeltaVB = V3LoadA(deltaV1.angular); + } + } + + if (hasTorsionalFriction && frictionPatch.anchorCount == 1) + { + header->numFrictionConstr++; + + const FloatV torsionalPatchRadius = FLoad(torsionalPatchRadiusF32); + const FloatV minTorsionalPatchRadius = FLoad(minTorsionalPatchRadiusF32); + const FloatV torsionalFriction = FMax(minTorsionalPatchRadius, FSqrt(FMul(FMax(zero, FNeg(maxPenetration)), torsionalPatchRadius))); + header->numFrictionConstr++; + SolverContactFrictionStepExt* PX_RESTRICT f = reinterpret_cast(ptr); + ptr += frictionStride; + + //Rotate the oldRelativeQuat into world space to get the new target relative quat + const PxQuat newTargetQ1 = bodyFrame0.q * frictionPatch.relativeQuat; + //Now we need to find the rotation around "normal" from Q1 to newTargetQ1. This is the error... + const PxQuat deltaQ = newTargetQ1.getConjugate() * bodyFrame1.q; + + const PxVec3 nTemp = buffer[c.contactPatches[c.correlationListHeads[i]].start].normal; + + PxQuat temp(deltaQ.x*nTemp.x, deltaQ.y*nTemp.y, deltaQ.z*nTemp.z, deltaQ.w); + + const PxReal magnitude = temp.normalize(); + + PxReal angle = PxAtan(physx::intrinsics::fsel(magnitude - 1e-6f, temp.dot(PxQuat(nTemp.x, nTemp.y, nTemp.z, 0.f)) / temp.w, 0.f)); + + //OK. We have old relative quat and new relative quat. Now find difference + + const Cm::SpatialVector resp0 = createImpulseResponseVector(PxVec3(0.f), header->normal, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(PxVec3(0.f), -header->normal, b1); + + Cm::SpatialVector deltaV0, deltaV1; + + PxReal ur = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, false); + + FloatV resp = FLoad(ur); + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK && + b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + PxReal lr0 = deltaV0.linear.dot(resp0.linear); + PxReal ar0 = deltaV0.angular.dot(resp0.angular) * safeRecip(resp0.angular.magnitude()); + PxReal lr1 = deltaV1.linear.dot(resp1.linear); + PxReal ar1 = deltaV1.angular.dot(resp1.angular)* safeRecip(resp1.angular.magnitude()); + + Cm::SpatialVector rem0(deltaV0.linear - resp0.linear*lr0, deltaV0.angular - resp0.angular*ar0); + Cm::SpatialVector rem1(deltaV1.linear - resp1.linear*lr1, deltaV1.angular - resp1.angular*ar1); + + + PxReal rem = (rem0 - rem1).magnitude(); + + resp = FAdd(resp, FLoad(rem)); + } + + + const FloatV velMultiplier = FSel(FIsGrtr(resp, FEps()), FDiv(p8, resp), zero); + + f->normalXYZ_ErrorW = V4SetW(V3Zero(), FLoad(-angle)); + f->raXnI_targetVelW = V4SetW(V3LoadA(resp0.angular), zero); + f->rbXnI_velMultiplierW = V4SetW(V3LoadA(resp1.angular), velMultiplier); + f->biasScale = frictionBiasScale; + f->appliedForce = 0.f; + FStore(torsionalFriction, &f->frictionScale); + f->linDeltaVA = V3LoadA(deltaV0.linear); + f->linDeltaVB = V3LoadA(deltaV1.linear); + f->angDeltaVA = V3LoadA(deltaV0.angular); + f->angDeltaVB = V3LoadA(deltaV1.angular); + + } + + } + + frictionPatchWritebackAddrIndex++; + } + } + + + + + bool createFinalizeSolverContactsStep( + PxTGSSolverContactDesc& contactDesc, + CorrelationBuffer& c, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxConstraintAllocator& constraintAllocator) + { + Ps::prefetchLine(contactDesc.body0); + Ps::prefetchLine(contactDesc.body1); + + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + const bool hasForceThreshold = contactDesc.hasForceThresholds; + const bool staticOrKinematicBody = contactDesc.bodyState1 == PxSolverContactDesc::eKINEMATIC_BODY || contactDesc.bodyState1 == PxSolverContactDesc::eSTATIC_BODY; + + const bool disableStrongFriction = contactDesc.disableStrongFriction; + const bool useExtContacts = ((contactDesc.bodyState0 | contactDesc.bodyState1) & PxSolverContactDesc::eARTICULATION) != 0; + + PxTGSSolverConstraintDesc& desc = *contactDesc.desc; + + desc.constraintLengthOver16 = 0; + + + if (contactDesc.numContacts == 0) + { + contactDesc.frictionPtr = NULL; + contactDesc.frictionCount = 0; + desc.constraint = NULL; + return true; + } + + if (!disableStrongFriction) + { + getFrictionPatches(c, contactDesc.frictionPtr, contactDesc.frictionCount, contactDesc.bodyFrame0, contactDesc.bodyFrame1, correlationDistance); + } + + bool overflow = !createContactPatches(c, contactDesc.contacts, contactDesc.numContacts, PXC_SAME_NORMAL); + overflow = correlatePatches(c, contactDesc.contacts, contactDesc.bodyFrame0, contactDesc.bodyFrame1, PXC_SAME_NORMAL, 0, 0) || overflow; + PX_UNUSED(overflow); + +#if PX_CHECKED + if (overflow) + { + Ps::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Dropping contacts in solver because we exceeded limit of 32 friction patches."); + } +#endif + + growPatches(c, contactDesc.contacts, contactDesc.bodyFrame0, contactDesc.bodyFrame1, correlationDistance, 0, frictionOffsetThreshold + contactDesc.restDistance); + + //PX_ASSERT(patchCount == c.frictionPatchCount); + + FrictionPatch* frictionPatches = NULL; + PxU8* solverConstraint = NULL; + PxU32 numFrictionPatches = 0; + PxU32 solverConstraintByteSize = 0; + PxU32 axisConstraintCount = 0; + + const bool successfulReserve = reserveBlockStreams( + useExtContacts, c, + solverConstraint, frictionPatches, + numFrictionPatches, + solverConstraintByteSize, + axisConstraintCount, + constraintAllocator, + PxMax(contactDesc.torsionalPatchRadius, contactDesc.minTorsionalPatchRadius)); + // initialise the work unit's ptrs to the various buffers. + + contactDesc.frictionPtr = NULL; + contactDesc.frictionCount = 0; + desc.constraint = NULL; + desc.constraintLengthOver16 = 0; + // patch up the work unit with the reserved buffers and set the reserved buffer data as appropriate. + + if (successfulReserve) + { + PxU8* frictionDataPtr = reinterpret_cast(frictionPatches); + contactDesc.frictionPtr = frictionDataPtr; + desc.constraint = solverConstraint; + //output.nbContacts = Ps::to8(numContacts); + contactDesc.frictionCount = Ps::to8(numFrictionPatches); + PX_ASSERT((solverConstraintByteSize & 0xf) == 0); + desc.constraintLengthOver16 = Ps::to16(solverConstraintByteSize / 16); + desc.writeBack = contactDesc.contactForces; + desc.writeBackLengthOver4 = PxU16(contactDesc.contactForces ? contactDesc.numContacts : 0); + + //Initialise friction buffer. + if (frictionPatches) + { + // PT: TODO: revisit this... not very satisfying + //const PxU32 maxSize = numFrictionPatches*sizeof(FrictionPatch); + Ps::prefetchLine(frictionPatches); + Ps::prefetchLine(frictionPatches, 128); + Ps::prefetchLine(frictionPatches, 256); + + for (PxU32 i = 0; i(contactDesc.body0), contactDesc.body0TxI, contactDesc.bodyData0, desc.linkIndexA); + const SolverExtBodyStep b1(reinterpret_cast(contactDesc.body1), contactDesc.body1TxI, contactDesc.bodyData1, desc.linkIndexB); + + setupFinalizeExtSolverContactsStep(contactDesc.contacts, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + b0, b1, invDtF32, invTotalDtF32, bounceThresholdF32, + contactDesc.mInvMassScales.linear0, contactDesc.mInvMassScales.angular0, contactDesc.mInvMassScales.linear1, contactDesc.mInvMassScales.angular1, + contactDesc.restDistance, frictionDataPtr, contactDesc.maxCCDSeparation, contactDesc.torsionalPatchRadius, contactDesc.minTorsionalPatchRadius); + } + else + { + + const PxTGSSolverBodyVel& b0 = *contactDesc.body0; + const PxTGSSolverBodyVel& b1 = *contactDesc.body1; + + setupFinalizeSolverConstraints(contactDesc.shapeInteraction, contactDesc.contacts, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + b0, b1, *contactDesc.body0TxI, *contactDesc.body1TxI, *contactDesc.bodyData0, *contactDesc.bodyData1, invDtF32, invTotalDtF32, bounceThresholdF32, + contactDesc.mInvMassScales.linear0, contactDesc.mInvMassScales.angular0, contactDesc.mInvMassScales.linear1, contactDesc.mInvMassScales.angular1, + hasForceThreshold, staticOrKinematicBody, contactDesc.restDistance, frictionDataPtr, contactDesc.maxCCDSeparation, disableStrongFriction, + contactDesc.torsionalPatchRadius, contactDesc.minTorsionalPatchRadius); + } + //KS - set to 0 so we have a counter for the number of times we solved the constraint + //only going to be used on SPU but might as well set on all platforms because this code is shared + *(reinterpret_cast(solverConstraint + solverConstraintByteSize)) = 0; + } + } + + return successfulReserve; + } + + + + + bool createFinalizeSolverContactsStep(PxTGSSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + const PxReal invTotalDt, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxConstraintAllocator& constraintAllocator) + { + using namespace Gu; + ContactBuffer& buffer = threadContext.mContactBuffer; + + + + buffer.count = 0; + + // We pull the friction patches out of the cache to remove the dependency on how + // the cache is organized. Remember original addrs so we can write them back + // efficiently. + + PxU32 numContacts = 0; + { + PxReal invMassScale0 = 1.f; + PxReal invMassScale1 = 1.f; + PxReal invInertiaScale0 = 1.f; + PxReal invInertiaScale1 = 1.f; + contactDesc.mInvMassScales.angular0 = (contactDesc.bodyState0 != PxSolverContactDesc::eARTICULATION && contactDesc.body0->isKinematic) ? 0.f : contactDesc.mInvMassScales.angular0; + contactDesc.mInvMassScales.angular1 = (contactDesc.bodyState1 != PxSolverContactDesc::eARTICULATION && contactDesc.body1->isKinematic) ? 0.f : contactDesc.mInvMassScales.angular1; + + bool hasMaxImpulse = false, hasTargetVelocity = false; + + numContacts = extractContacts(buffer, output, hasMaxImpulse, hasTargetVelocity, invMassScale0, invMassScale1, + invInertiaScale0, invInertiaScale1, contactDesc.maxImpulse); + + contactDesc.contacts = buffer.contacts; + contactDesc.numContacts = numContacts; + contactDesc.disableStrongFriction = contactDesc.disableStrongFriction || hasTargetVelocity; + contactDesc.hasMaxImpulse = hasMaxImpulse; + contactDesc.mInvMassScales.linear0 *= invMassScale0; + contactDesc.mInvMassScales.linear1 *= invMassScale1; + contactDesc.mInvMassScales.angular0 *= invInertiaScale0; + contactDesc.mInvMassScales.angular1 *= invInertiaScale1; + } + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + + return createFinalizeSolverContactsStep(contactDesc, c, invDtF32, invTotalDt, bounceThresholdF32, frictionOffsetThreshold, correlationDistance, constraintAllocator); + } + + PX_FORCE_INLINE PxU32 getConstraintLength(const PxTGSSolverConstraintDesc& desc) + { + return PxU32(desc.constraintLengthOver16 << 4); + } + + PX_FORCE_INLINE PxU32 getWritebackLength(const PxTGSSolverConstraintDesc& desc) + { + return PxU32(desc.writeBackLengthOver4 << 2); + } + + + + static FloatV solveDynamicContactsStep(SolverContactPointStep* contacts, const PxU32 nbContactPoints, const Vec3VArg contactNormal, + const FloatVArg invMassA, const FloatVArg invMassB, Vec3V& linVel0_, Vec3V& angState0_, + Vec3V& linVel1_, Vec3V& angState1_, PxF32* PX_RESTRICT forceBuffer, + const Vec3V& angMotion0, const Vec3V& angMotion1, + const Vec3V& linRelMotion, const FloatVArg maxPenBias, + const FloatVArg angD0, const FloatVArg angD1, const FloatVArg minPen, + const FloatVArg elapsedTime) + { + Vec3V linVel0 = linVel0_; + Vec3V angState0 = angState0_; + Vec3V linVel1 = linVel1_; + Vec3V angState1 = angState1_; + FloatV accumulatedNormalImpulse = FZero(); + + const Vec3V delLinVel0 = V3Scale(contactNormal, invMassA); + const Vec3V delLinVel1 = V3Scale(contactNormal, invMassB); + + const FloatV deltaV = V3Dot(linRelMotion, contactNormal); + + for (PxU32 i = 0; i(currPtr); + currPtr += sizeof(SolverContactHeaderStep); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverContactPointStep* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPointStep); + + PxF32* forceBuffer = reinterpret_cast(currPtr); + currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + SolverContactFrictionStep* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionStep); + + const FloatV invMassA = FLoad(hdr->invMass0); + const FloatV invMassB = FLoad(hdr->invMass1); + + const FloatV angDom0 = FLoad(hdr->angDom0); + const FloatV angDom1 = FLoad(hdr->angDom1); + + //const FloatV sumMass = FAdd(invMassA, invMassB); + + const Vec3V contactNormal = V3LoadA(hdr->normal); + + const FloatV maxPenBias = FLoad(hdr->maxPenBias); + + const FloatV accumulatedNormalImpulse = solveDynamicContactsStep(contacts, numNormalConstr, contactNormal, invMassA, invMassB, + linVel0, angState0, linVel1, angState1, forceBuffer,angMotion0, angMotion1, relMotion, maxPenBias, angDom0, angDom1, minPen, + elapsedTime); + + if (numFrictionConstr && doFriction) + { + const FloatV staticFrictionCof = hdr->getStaticFriction(); + const FloatV dynamicFrictionCof = hdr->getDynamicFriction(); + const FloatV maxFrictionImpulse = FMul(staticFrictionCof, accumulatedNormalImpulse); + const FloatV maxDynFrictionImpulse = FMul(dynamicFrictionCof, accumulatedNormalImpulse); + const FloatV negMaxDynFrictionImpulse = FNeg(maxDynFrictionImpulse); + + BoolV broken = BFFFF(); + + for (PxU32 i = 0; i maxFrictionImpulse + // clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse] + // (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce] + // set broken flag to true || broken flag + + // FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier); + // FloatV potentialSumF = FAdd(appliedForce, deltaF); + + const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1); + + // On XBox this clamping code uses the vector simple pipe rather than vector float, + // which eliminates a lot of stall cycles + + const BoolV clamp = FIsGrtr(FAbs(totalImpulse), FMul(frictionScale, maxFrictionImpulse)); + + const FloatV totalClamped = FMin(FMul(frictionScale, maxDynFrictionImpulse), FMax(FMul(frictionScale, negMaxDynFrictionImpulse), totalImpulse)); + + const FloatV newAppliedForce = FSel(clamp, totalClamped, totalImpulse); + + broken = BOr(broken, clamp); + + FloatV deltaF = FSub(newAppliedForce, appliedForce); + + // we could get rid of the stall here by calculating and clamping delta separately, but + // the complexity isn't really worth it. + + linVel0 = V3ScaleAdd(delLinVel0, deltaF, linVel0); + linVel1 = V3NegScaleSub(delLinVel1, deltaF, linVel1); + angState0 = V3ScaleAdd(raXnI, FMul(deltaF, angDom0), angState0); + angState1 = V3NegScaleSub(rbXnI, FMul(deltaF, angDom1), angState1); + + f.setAppliedForce(newAppliedForce); + + + } + Store_From_BoolV(broken, &hdr->broken); + } + + } + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularVelocity.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularVelocity.isFinite()); + + // Write back + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(linVel1, b1.linearVelocity); + V3StoreA(angState0, b0.angularVelocity); + V3StoreA(angState1, b1.angularVelocity); + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularVelocity.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularVelocity.isFinite()); + + PX_ASSERT(currPtr == last); + } + + void writeBackContact(const PxTGSSolverConstraintDesc& desc, SolverContext* cache) + { + PX_UNUSED(cache); + PxReal normalForce = 0; + + PxU8* PX_RESTRICT cPtr = desc.constraint; + PxReal* PX_RESTRICT vForceWriteback = reinterpret_cast(desc.writeBack); + PxU8* PX_RESTRICT last = desc.constraint + desc.constraintLengthOver16 * 16; + + bool forceThreshold = false; + + while (cPtr < last) + { + const SolverContactHeaderStep* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactHeaderStep); + + forceThreshold = hdr->flags & SolverContactHeaderStep::eHAS_FORCE_THRESHOLDS; + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + //if(cPtr < last) + Ps::prefetchLine(cPtr, 256); + Ps::prefetchLine(cPtr, 384); + + const PxU32 pointStride = hdr->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactPointStepExt) + : sizeof(SolverContactPointStep); + + cPtr += pointStride * numNormalConstr; + PxF32* forceBuffer = reinterpret_cast(cPtr); + cPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + if (vForceWriteback != NULL) + { + for (PxU32 i = 0; itype == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactFrictionStepExt) + : sizeof(SolverContactFrictionStep); + + if (hdr->broken && hdr->frictionBrokenWritebackByte != NULL) + { + *hdr->frictionBrokenWritebackByte = 1; + } + + cPtr += frictionStride * numFrictionConstr; + + } + PX_ASSERT(cPtr == last); + + PX_UNUSED(forceThreshold); + +#if 0 + if (cache && forceThreshold && desc.linkIndexA == PxSolverConstraintDesc::NO_LINK && desc.linkIndexB == PxSolverConstraintDesc::NO_LINK && + normalForce != 0 && (desc.bodyA->reportThreshold < PX_MAX_REAL || desc.bodyB->reportThreshold < PX_MAX_REAL)) + { + ThresholdStreamElement elt; + elt.normalForce = normalForce; + elt.threshold = PxMin(desc.bodyA->reportThreshold, desc.bodyB->reportThreshold); + elt.nodeIndexA = desc.bodyA->nodeIndex; + elt.nodeIndexB = desc.bodyB->nodeIndex; + elt.shapeInteraction = reinterpret_cast(desc.constraint)->shapeInteraction; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + PX_ASSERT(cache->mThresholdStreamIndexmThresholdStreamLength); + cache->mThresholdStream[cache->mThresholdStreamIndex++] = elt; + } +#endif + } + + + + + + PX_FORCE_INLINE Vec3V V3FromV4(Vec4V x) { return Vec3V_From_Vec4V(x); } + PX_FORCE_INLINE Vec3V V3FromV4Unsafe(Vec4V x) { return Vec3V_From_Vec4V_WUndefined(x); } + PX_FORCE_INLINE Vec4V V4FromV3(Vec3V x) { return Vec4V_From_Vec3V(x); } + //PX_FORCE_INLINE Vec4V V4ClearW(Vec4V x) { return V4SetW(x, FZero()); } + + + void preprocessRows(Px1DConstraint** sorted, + Px1DConstraint* rows, + PxVec4* angSqrtInvInertia0, + PxVec4* angSqrtInvInertia1, + PxU32 rowCount, + const PxMat33& sqrtInvInertia0F32, + const PxMat33& sqrtInvInertia1F32, + const PxReal invMass0, + const PxReal invMass1, + const PxConstraintInvMassScale& ims, + bool disablePreprocessing, + bool diagonalizeDrive, + bool preprocessLinear = true); + + + +void setSolverConstantsStep(PxReal& error, + PxReal& biasScale, + PxReal& targetVel, + PxReal& maxBias, + PxReal& velMultiplier, + PxReal& impulseMultiplier, + PxReal& rcpResponse, + const Px1DConstraint& c, + PxReal normalVel, + PxReal unitResponse, + PxReal minRowResponse, + PxReal erp, + PxReal dt, + PxReal totalDt, + PxReal biasClamp, + PxReal recipdt, + PxReal recipTotalDt) +{ + PX_UNUSED(dt); + PX_UNUSED(totalDt); + PX_ASSERT(PxIsFinite(unitResponse)); + PxReal recipResponse = unitResponse <= minRowResponse ? 0 : 1.0f / unitResponse; + PX_ASSERT(recipResponse < 1e5f); + PxReal geomError = c.geometricError; + + rcpResponse = recipResponse; + + + + if (c.flags & Px1DConstraintFlag::eSPRING) + { + error = geomError; + + PxReal a = dt * (dt*c.mods.spring.stiffness + c.mods.spring.damping); + PxReal b = dt*(c.mods.spring.damping * c.velocityTarget /*- c.mods.spring.stiffness * geomError*/); + maxBias = biasClamp; + + if (c.flags & Px1DConstraintFlag::eACCELERATION_SPRING) + { + PxReal x = 1.0f / (1.0f + a); + targetVel = x * b; + velMultiplier = -x * a; + biasScale = -x * c.mods.spring.stiffness*dt; + //KS - impulse multiplier for TGS solver is 1 because we consider each pass to be a separate step, with the solver computing the impulse proportional to the step dt. + //Alternatively, we could consider the spring over totalDt and then use an impulseMultiplier of 1-x, but this would require the bias to be constant to allow the constraint + //to produce stiff results if the number of iterations was large + impulseMultiplier = 1.0f; + } + else + { + PxReal x = 1.0f / (1.0f + a*unitResponse); + targetVel = x * b*unitResponse; + velMultiplier = -x*a*unitResponse; + //KS - impulse multiplier for TGS solver is 1 because we consider each pass to be a separate step, with the solver computing the impulse proportional to the step dt. + //Alternatively, we could consider the spring over totalDt and then use an impulseMultiplier of 1-x, but this would require the bias to be constant to allow the constraint + //to produce stiff results if the number of iterations was large + biasScale = -x * c.mods.spring.stiffness*unitResponse*dt; + impulseMultiplier = 1.0f; + } + + } + else + { + velMultiplier = -1.f;// *recipResponse; + impulseMultiplier = 1.0f; + + if (c.flags & Px1DConstraintFlag::eRESTITUTION && -normalVel>c.mods.bounce.velocityThreshold) + { + error = 0.f; + biasScale = 0.f; + + targetVel = c.mods.bounce.restitution*-normalVel; + maxBias = 0.f; + } + else + { + + biasScale = -recipdt*erp;// *recipResponse; + if (c.flags & Px1DConstraintFlag::eDRIVE_ROW) + { + error = 0.f; + targetVel = c.velocityTarget - geomError *recipTotalDt; + } + else + { + error = geomError; + //KS - if there is a velocity target, then we cannot also have bias otherwise the two compete against each-other. + //Therefore, we set the velocity target + targetVel = c.velocityTarget;// *recipResponse; + } + + maxBias = biasClamp;// *recipResponse; + + /*PxReal errorBias = PxClamp(geomError*erp*recipdt, -biasClamp, biasClamp); + + constant = (c.velocityTarget - errorBias) * recipResponse;*/ + + } + } +} + + + + + +PxU32 setupSolverConstraintStep( + const PxTGSSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + const PxReal dt, const PxReal totalDt, const PxReal invdt, const PxReal invTotalDt, + const PxU32 nbSubsteps, + const PxReal lengthScale) +{ + PX_UNUSED(nbSubsteps); + //static const PxU32 MAX_CONSTRAINT_ROWS = 12; + + if (prepDesc.numRows == 0) + { + prepDesc.desc->constraint = NULL; + prepDesc.desc->writeBack = NULL; + prepDesc.desc->constraintLengthOver16 = 0; + prepDesc.desc->writeBackLengthOver4 = 0; + return 0; + } + + PxTGSSolverConstraintDesc& desc = *prepDesc.desc; + + const bool isExtended = desc.linkIndexA != PxTGSSolverConstraintDesc::NO_LINK + || desc.linkIndexB != PxTGSSolverConstraintDesc::NO_LINK; + + const bool isKinematic0 = desc.linkIndexA == PxTGSSolverConstraintDesc::NO_LINK && + desc.bodyA->isKinematic; + + const bool isKinematic1 = desc.linkIndexB == PxTGSSolverConstraintDesc::NO_LINK && + desc.bodyB->isKinematic; + + PxU32 stride = isExtended ? sizeof(SolverConstraint1DExtStep) : sizeof(SolverConstraint1DStep); + const PxU32 constraintLength = sizeof(SolverConstraint1DHeaderStep) + stride * prepDesc.numRows; + + //KS - +16 is for the constraint progress counter, which needs to be the last element in the constraint (so that we + //know SPU DMAs have completed) + PxU8* ptr = allocator.reserveConstraintData(constraintLength + 16u); + if (NULL == ptr || (reinterpret_cast(-1)) == ptr) + { + if (NULL == ptr) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept joints detaching/exploding or increase buffer size allocated for constraint prep by increasing PxSceneDesc::maxNbContactDataBlocks."); + return 0; + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of constraint data. " + "Either accept joints detaching/exploding or simplify constraints."); + ptr = NULL; + return 0; + } + } + desc.constraint = ptr; + + //setConstraintLength(desc, constraintLength); + PX_ASSERT((constraintLength & 0xf) == 0); + desc.constraintLengthOver16 = Ps::to16(constraintLength / 16); + + desc.writeBack = prepDesc.writeback; + desc.writeBackLengthOver4 = Ps::to16(sizeof(ConstraintWriteback)/4); + + memset(desc.constraint, 0, constraintLength); + + SolverConstraint1DHeaderStep* header = reinterpret_cast(desc.constraint); + PxU8* constraints = desc.constraint + sizeof(SolverConstraint1DHeaderStep); + init(*header, Ps::to8(prepDesc.numRows), isExtended, prepDesc.mInvMassScales); + header->body0WorldOffset = prepDesc.body0WorldOffset; + header->linBreakImpulse = prepDesc.linBreakForce * totalDt; + header->angBreakImpulse = prepDesc.angBreakForce * totalDt; + header->breakable = PxU8((prepDesc.linBreakForce != PX_MAX_F32) || (prepDesc.angBreakForce != PX_MAX_F32)); + header->invMass0D0 = prepDesc.bodyData0->invMass * prepDesc.mInvMassScales.linear0; + header->invMass1D1 = prepDesc.bodyData1->invMass * prepDesc.mInvMassScales.linear1; + + header->rAWorld = prepDesc.cA2w - prepDesc.bodyFrame0.p; + header->rBWorld = prepDesc.cB2w - prepDesc.bodyFrame1.p; + + Px1DConstraint* sorted[MAX_CONSTRAINT_ROWS]; + + PX_ALIGN(16, PxVec4) angSqrtInvInertia0[MAX_CONSTRAINT_ROWS]; + PX_ALIGN(16, PxVec4) angSqrtInvInertia1[MAX_CONSTRAINT_ROWS]; + + for (PxU32 i = 0; i < prepDesc.numRows; ++i) + { + if (prepDesc.rows[i].flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT) + { + if (prepDesc.rows[i].solveHint == PxConstraintSolveHint::eEQUALITY) + prepDesc.rows[i].solveHint = PxConstraintSolveHint::eROTATIONAL_EQUALITY; + else if (prepDesc.rows[i].solveHint == PxConstraintSolveHint::eINEQUALITY) + prepDesc.rows[i].solveHint = PxConstraintSolveHint::eROTATIONAL_INEQUALITY; + } + } + + preprocessRows(sorted, prepDesc.rows, angSqrtInvInertia0, angSqrtInvInertia1, prepDesc.numRows, + prepDesc.body0TxI->sqrtInvInertia, prepDesc.body1TxI->sqrtInvInertia, prepDesc.bodyData0->invMass, prepDesc.bodyData1->invMass, + prepDesc.mInvMassScales, isExtended || prepDesc.disablePreprocessing, prepDesc.improvedSlerp, false); + + PxReal erp = 1.f; + PxReal linearErp = 1.f; + + const PxReal recipDt = invdt; + + PxU32 orthoCount = 0; + for (PxU32 i = 0; i(constraints); + Px1DConstraint& c = *sorted[i]; + + PxReal driveScale = c.flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && prepDesc.driveLimitsAreForces ? PxMin(totalDt, 1.0f) : 1.0f; + + PxReal unitResponse; + PxReal normalVel = 0.0f; + + PxReal angSpeedLimit = 100.f; + PxReal linSpeedLimit = 1000.f; + + PxReal vel0, vel1; + + if (!isExtended) + { + const PxVec3 angSqrtInvInertia0V3(angSqrtInvInertia0[i].x, angSqrtInvInertia0[i].y, angSqrtInvInertia0[i].z); + const PxVec3 angSqrtInvInertia1V3(angSqrtInvInertia1[i].x, angSqrtInvInertia1[i].y, angSqrtInvInertia1[i].z); + init(s, c.linear0, c.linear1, c.angular0, c.angular1, c.minImpulse * driveScale, c.maxImpulse * driveScale); + + /*unitResponse = prepDesc.body0->getResponse(c.linear0, c.angular0, s.ang0, prepDesc.mInvMassScales.linear0, prepDesc.mInvMassScales.angular0) + + prepDesc.body1->getResponse(-c.linear1, -c.angular1, s.ang1, prepDesc.mInvMassScales.linear1, prepDesc.mInvMassScales.angular1);*/ + + const PxReal linSumMass = s.lin0.magnitudeSquared() * prepDesc.bodyData0->invMass * prepDesc.mInvMassScales.linear0 + s.lin1.magnitudeSquared() * prepDesc.bodyData1->invMass * prepDesc.mInvMassScales.linear1; + + PxReal resp0 = angSqrtInvInertia0V3.magnitudeSquared() * prepDesc.mInvMassScales.angular0; + PxReal resp1 = angSqrtInvInertia1V3.magnitudeSquared() * prepDesc.mInvMassScales.angular1; + unitResponse = resp0 + resp1 + linSumMass; + + //s.recipResponseOrLinearSumMass = linSumMass; + + vel0 = prepDesc.bodyData0->projectVelocity(s.lin0, s.ang0); + vel1 = prepDesc.bodyData1->projectVelocity(s.lin1, s.ang1); + + normalVel = vel0 - vel1; + + //if (c.solveHint & PxConstraintSolveHint::eEQUALITY) + if(!(c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT)) + { + s.ang0 = PxVec3(0.f); + s.ang1 = PxVec3(0.f); + s.angularErrorScale = 0.f; + } + + } + else + { + linearErp = 0.7f; + erp = 0.7f; + //angSpeedLimit = 25.f; + linSpeedLimit = 200.f; + const SolverExtBodyStep eb0(reinterpret_cast(prepDesc.body0), prepDesc.body0TxI, prepDesc.bodyData0, desc.linkIndexA); + const SolverExtBodyStep eb1(reinterpret_cast(prepDesc.body1), prepDesc.body1TxI, prepDesc.bodyData1, desc.linkIndexB); + const Cm::SpatialVector resp0 = createImpulseResponseVector(c.linear0, c.angular0, eb0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-c.linear1, -c.angular1, eb1); + + init(s, resp0.linear, -resp1.linear, resp0.angular, -resp1.angular, c.minImpulse * driveScale, c.maxImpulse * driveScale); + SolverConstraint1DExtStep& e = static_cast(s); + + Cm::SpatialVector& delta0 = unsimdRef(e.deltaVA); + Cm::SpatialVector& delta1 = unsimdRef(e.deltaVB); + + unitResponse = getImpulseResponse(eb0, resp0, delta0, prepDesc.mInvMassScales.linear0, prepDesc.mInvMassScales.angular0, + eb1, resp1, delta1, prepDesc.mInvMassScales.linear1, prepDesc.mInvMassScales.angular1, false); + + if (eb0.mLinkIndex != PxSolverConstraintDesc::NO_LINK && + eb1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + PxReal lr0 = delta0.linear.dot(resp0.linear); + PxReal ar0 = delta0.angular.dot(resp0.angular) * safeRecip(resp0.angular.magnitude()); + PxReal lr1 = delta1.linear.dot(resp1.linear); + PxReal ar1 = delta1.angular.dot(resp1.angular)* safeRecip(resp1.angular.magnitude()); + + Cm::SpatialVector rem0(delta0.linear - resp0.linear*lr0, delta0.angular - resp0.angular*ar0); + Cm::SpatialVector rem1(delta1.linear - resp1.linear*lr1, delta1.angular - resp1.angular*ar1); + + + PxReal rem = (rem0 - rem1).magnitude(); + + unitResponse += rem; + } + + + + { + vel0 = eb0.projectVelocity(s.lin0, s.ang0); + vel1 = eb1.projectVelocity(s.lin1, s.ang1); + + normalVel = vel0 - vel1; + } + + if (!(c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT)) + { + s.angularErrorScale = 0.f; + } + } + + PxReal recipResponse = 0.f; + + setSolverConstantsStep(s.error, s.biasScale, s.velTarget, s.maxBias, s.velMultiplier, s.impulseMultiplier, recipResponse, c, + normalVel, unitResponse, isExtended ? 1e-5f : prepDesc.minResponseThreshold, (c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT ? erp : linearErp), dt, totalDt, + c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT ? angSpeedLimit : linSpeedLimit*lengthScale, recipDt, invTotalDt); + + s.recipResponse = recipResponse; + + if(isKinematic0) + s.velTarget -= vel0; + if(isKinematic1) + s.velMultiplier += vel1; + + + + if (c.flags & Px1DConstraintFlag::eOUTPUT_FORCE) + s.flags |= DY_SC_FLAG_OUTPUT_FORCE; + + if ((c.flags & Px1DConstraintFlag::eKEEPBIAS)) + s.flags |= DY_SC_FLAG_KEEP_BIAS; + if (c.solveHint & 1) + s.flags |= DY_SC_FLAG_INEQUALITY; + + if (!(isExtended || prepDesc.disablePreprocessing)) + { + //KS - the code that orthogonalizes constraints on-the-fly only works if the linear and angular constraints have already been pre-orthogonalized + if (c.solveHint == PxConstraintSolveHint::eROTATIONAL_EQUALITY) + { + s.flags |= DY_SC_FLAG_ROT_EQ; + + PX_ASSERT(orthoCount < 3); + + /*angOrtho0[orthoCount] = PxVec3(angSqrtInvInertia0[i].x, angSqrtInvInertia0[i].y, angSqrtInvInertia0[i].z); + angOrtho1[orthoCount] = PxVec3(angSqrtInvInertia1[i].x, angSqrtInvInertia1[i].y, angSqrtInvInertia1[i].z); + recipResponses[orthoCount] = recipResponse;*/ + + header->angOrthoAxis0_recipResponseW[orthoCount] = + PxVec4(angSqrtInvInertia0[i].x*prepDesc.mInvMassScales.angular0, angSqrtInvInertia0[i].y*prepDesc.mInvMassScales.angular0, angSqrtInvInertia0[i].z*prepDesc.mInvMassScales.angular0, recipResponse); + header->angOrthoAxis1_Error[orthoCount].x = angSqrtInvInertia1[i].x*prepDesc.mInvMassScales.angular1; + header->angOrthoAxis1_Error[orthoCount].y = angSqrtInvInertia1[i].y*prepDesc.mInvMassScales.angular1; + header->angOrthoAxis1_Error[orthoCount].z = angSqrtInvInertia1[i].z*prepDesc.mInvMassScales.angular1; + header->angOrthoAxis1_Error[orthoCount].w = c.geometricError; + + orthoCount++; + } + + else if (c.solveHint & PxConstraintSolveHint::eEQUALITY) + s.flags |= DY_SC_FLAG_ORTHO_TARGET; + } + + + + + constraints += stride; + } + + + //KS - Set the solve count at the end to 0 + *(reinterpret_cast(constraints)) = 0; + *(reinterpret_cast(constraints + 4)) = 0; + PX_ASSERT(desc.constraint + getConstraintLength(desc) == constraints); + return prepDesc.numRows; +} + +PxU32 SetupSolverConstraintStep(SolverConstraintShaderPrepDesc& shaderDesc, + PxTGSSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + const PxReal dt, const PxReal totalDt, const PxReal invdt, const PxReal invTotalDt, const PxU32 nbSubsteps, + const PxReal lengthScale) +{ + // LL shouldn't see broken constraints + + PX_ASSERT(!(reinterpret_cast(prepDesc.writeback)->broken)); + + prepDesc.desc->constraintLengthOver16 = 0; + //setConstraintLength(*prepDesc.desc, 0); + + if (!shaderDesc.solverPrep) + return 0; + + //PxU32 numAxisConstraints = 0; + + Px1DConstraint rows[MAX_CONSTRAINT_ROWS]; + + // This is necessary so that there will be sensible defaults and shaders will + // continue to work (albeit with a recompile) if the row format changes. + // It's a bit inefficient because it fills in all constraint rows even if there + // is only going to be one generated. A way around this would be for the shader to + // specify the maximum number of rows it needs, or it could call a subroutine to + // prep the row before it starts filling it it. + + PxMemZero(rows, sizeof(Px1DConstraint)*MAX_CONSTRAINT_ROWS); + + for (PxU32 i = 0; iisKinematic) + prepDesc.mInvMassScales.angular0 = 0.f; + if (prepDesc.bodyState1 != PxSolverContactDesc::eARTICULATION && prepDesc.body1->isKinematic) + prepDesc.mInvMassScales.angular1 = 0.f; + + + return setupSolverConstraintStep(prepDesc, allocator, dt, totalDt, invdt, invTotalDt, nbSubsteps, lengthScale); +} + +//Port of scalar implementation to SIMD maths with some interleaving of instructions +void solveExt1DStep(const PxTGSSolverConstraintDesc& desc, const PxReal elapsedTimeF32, SolverContext& cache, + const PxTGSSolverBodyTxInertia* const txInertias) +{ + PxU8* PX_RESTRICT bPtr = desc.constraint; + //PxU32 length = desc.constraintLength; + + const SolverConstraint1DHeaderStep* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DExtStep* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeaderStep)); + + const FloatV elapsedTime = FLoad(elapsedTimeF32); + + Vec3V linVel0, angVel0, linVel1, angVel1; + Vec3V linMotion0, angMotion0, linMotion1, angMotion1; + + QuatV rotA, rotB; + + if (desc.articulationA == desc.articulationB) + { + Cm::SpatialVectorV v0, v1; + desc.articulationA->pxcFsGetVelocities(desc.linkIndexA, desc.linkIndexB, v0, v1); + linVel0 = v0.linear; + angVel0 = v0.angular; + linVel1 = v1.linear; + angVel1 = v1.angular; + + Cm::SpatialVectorV motionV0 = PxcFsGetMotionVector(*desc.articulationA, desc.linkIndexA); + Cm::SpatialVectorV motionV1 = PxcFsGetMotionVector(*desc.articulationB, desc.linkIndexB); + + linMotion0 = motionV0.linear; + angMotion0 = motionV0.angular; + linMotion1 = motionV1.linear; + angMotion1 = motionV1.angular; + + rotA = Ps::aos::QuatVLoadU(&desc.articulationA->getDeltaQ(desc.linkIndexA).x); + rotB = Ps::aos::QuatVLoadU(&desc.articulationB->getDeltaQ(desc.linkIndexB).x); + } + else + { + + if (desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + linVel0 = V3LoadA(desc.bodyA->linearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularVelocity); + linMotion0 = V3LoadA(desc.bodyA->deltaLinDt); + angMotion0 = V3LoadA(desc.bodyA->deltaAngDt); + rotA = Ps::aos::QuatVLoadA(&txInertias[desc.bodyAIdx].deltaBody2World.q.x); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + rotA = Ps::aos::QuatVLoadU(&desc.articulationA->getDeltaQ(desc.linkIndexA).x); + Cm::SpatialVectorV motionV = PxcFsGetMotionVector(*desc.articulationA, desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + + linMotion0 = motionV.linear; + angMotion0 = motionV.angular; + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularVelocity); + linMotion1 = V3LoadA(desc.bodyB->deltaLinDt); + angMotion1 = V3LoadA(desc.bodyB->deltaAngDt); + rotB = Ps::aos::QuatVLoadA(&txInertias[desc.bodyBIdx].deltaBody2World.q.x); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + rotB = Ps::aos::QuatVLoadU(&desc.articulationB->getDeltaQ(desc.linkIndexB).x); + Cm::SpatialVectorV motionV = PxcFsGetMotionVector(*desc.articulationB, desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + + linMotion1 = motionV.linear; + angMotion1 = motionV.angular; + } + } + + /*PX_ASSERT(FAllGrtr(FLoad(40.f * 40.f), V3Dot(linVel0, linVel0))); + PX_ASSERT(FAllGrtr(FLoad(40.f * 40.f), V3Dot(linVel1, linVel1))); + PX_ASSERT(FAllGrtr(FLoad(20.f * 20.f), V3Dot(angVel0, angVel0))); + PX_ASSERT(FAllGrtr(FLoad(20.f * 20.f), V3Dot(angVel1, angVel1)));*/ + + const Vec3V raPrev = V3LoadA(header->rAWorld); + const Vec3V rbPrev = V3LoadA(header->rBWorld); + + const Vec3V ra = QuatRotate(rotA, V3LoadA(header->rAWorld)); + const Vec3V rb = QuatRotate(rotB, V3LoadA(header->rBWorld)); + + const Vec3V raMotion = V3Sub(V3Add(ra, linMotion0), raPrev); + const Vec3V rbMotion = V3Sub(V3Add(rb, linMotion1), rbPrev); + + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + for (PxU32 i = 0; icount; ++i, base++) + { + Ps::prefetchLine(base + 1); + + SolverConstraint1DExtStep& c = *base; + + const Vec3V clinVel0 = V3LoadA(c.lin0); + const Vec3V clinVel1 = V3LoadA(c.lin1); + + const Vec3V cangVel0 = V3LoadA(c.ang0); + const Vec3V cangVel1 = V3LoadA(c.ang1); + + const FloatV recipResponse = FLoad(c.recipResponse); + + const FloatV targetVel = FLoad(c.velTarget); + + const FloatV deltaAng = FMul(FSub(V3Dot(cangVel0, angMotion0), V3Dot(cangVel1, angMotion1)), FLoad(c.angularErrorScale)); + const FloatV error = FNegScaleSub(targetVel, elapsedTime, FAdd(FAdd(FLoad(c.error), FSub(V3Dot(raMotion, clinVel0), V3Dot(rbMotion, clinVel1))), deltaAng)); + + const FloatV biasScale = FLoad(c.biasScale); + const FloatV maxBias = FLoad(c.maxBias); + + const FloatV vMul = FMul(recipResponse, FLoad(c.velMultiplier)); + const FloatV iMul = FLoad(c.impulseMultiplier); + const FloatV appliedForce = FLoad(c.appliedForce); + + const FloatV unclampedBias = FMul(error, biasScale); + const FloatV minBias = c.flags & DY_SC_FLAG_INEQUALITY ? FNeg(FMax()) : FNeg(maxBias); + const FloatV bias = FClamp(unclampedBias, minBias, maxBias); + + const FloatV constant = FMul(recipResponse, FAdd(bias, targetVel)); + + const FloatV maxImpulse = FLoad(c.maxImpulse); + const FloatV minImpulse = FLoad(c.minImpulse); + + const Vec3V v0 = V3MulAdd(linVel0, clinVel0, V3Mul(angVel0, cangVel0)); + const Vec3V v1 = V3MulAdd(linVel1, clinVel1, V3Mul(angVel1, cangVel1)); + const FloatV normalVel = V3SumElems(V3Sub(v0, v1)); + + const FloatV iM = FMul(iMul, appliedForce); + + const FloatV unclampedForce = FAdd(iM, FScaleAdd(vMul, normalVel, constant)); + const FloatV clampedForce = FMin(maxImpulse, (FMax(minImpulse, unclampedForce))); + const FloatV deltaF = FSub(clampedForce, appliedForce); + + FStore(clampedForce, &c.appliedForce); + + //PX_ASSERT(FAllGrtr(FLoad(1000.f), FAbs(deltaF))); + + + FStore(clampedForce, &base->appliedForce); + li0 = V3ScaleAdd(clinVel0, deltaF, li0); ai0 = V3ScaleAdd(cangVel0, deltaF, ai0); + li1 = V3ScaleAdd(clinVel1, deltaF, li1); ai1 = V3ScaleAdd(cangVel1, deltaF, ai1); + + linVel0 = V3ScaleAdd(base->deltaVA.linear, deltaF, linVel0); angVel0 = V3ScaleAdd(base->deltaVA.angular, deltaF, angVel0); + linVel1 = V3ScaleAdd(base->deltaVB.linear, deltaF, linVel1); angVel1 = V3ScaleAdd(base->deltaVB.angular, deltaF, angVel1); + + /*PX_ASSERT(FAllGrtr(FLoad(40.f * 40.f), V3Dot(linVel0, linVel0))); + PX_ASSERT(FAllGrtr(FLoad(40.f * 40.f), V3Dot(linVel1, linVel1))); + PX_ASSERT(FAllGrtr(FLoad(20.f * 20.f), V3Dot(angVel0, angVel0))); + PX_ASSERT(FAllGrtr(FLoad(20.f * 20.f), V3Dot(angVel1, angVel1)));*/ + } + + if (desc.articulationA == desc.articulationB) + { + desc.articulationA->pxcFsApplyImpulses(desc.linkIndexA, V3Scale(li0, FLoad(header->linearInvMassScale0)), + V3Scale(ai0, FLoad(header->angularInvMassScale0)), desc.linkIndexB, V3Scale(li1, FLoad(header->linearInvMassScale1)), + V3Scale(ai1, FLoad(header->angularInvMassScale1)), cache.Z, cache.deltaV); + } + else + { + + if (desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularVelocity); + } + else + { + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, V3Scale(li0, FLoad(header->linearInvMassScale0)), + V3Scale(ai0, FLoad(header->angularInvMassScale0)), cache.Z, cache.deltaV); + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularVelocity); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, V3Scale(li1, FLoad(header->linearInvMassScale1)), + V3Scale(ai1, FLoad(header->angularInvMassScale1)), cache.Z, cache.deltaV); + } + } +} + + + + +//Port of scalar implementation to SIMD maths with some interleaving of instructions +void solve1DStep(const PxTGSSolverConstraintDesc& desc, const PxTGSSolverBodyTxInertia* const txInertias, const PxReal elapsedTime) +{ + PxU8* PX_RESTRICT bPtr = desc.constraint; + if (bPtr == NULL) + return; + + PxTGSSolverBodyVel& b0 = *desc.bodyA; + PxTGSSolverBodyVel& b1 = *desc.bodyB; + + const FloatV elapsed = FLoad(elapsedTime); + + + const PxTGSSolverBodyTxInertia& txI0 = txInertias[desc.bodyAIdx]; + const PxTGSSolverBodyTxInertia& txI1 = txInertias[desc.bodyBIdx]; + + const SolverConstraint1DHeaderStep* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DStep* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeaderStep)); + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V linVel1 = V3LoadA(b1.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularVelocity); + Vec3V angState1 = V3LoadA(b1.angularVelocity); + + Mat33V sqrtInvInertia0 = Mat33V(V3LoadU(txI0.sqrtInvInertia.column0), V3LoadU(txI0.sqrtInvInertia.column1), V3LoadU(txI0.sqrtInvInertia.column2)); + Mat33V sqrtInvInertia1 = Mat33V(V3LoadU(txI1.sqrtInvInertia.column0), V3LoadU(txI1.sqrtInvInertia.column1), V3LoadU(txI1.sqrtInvInertia.column2)); + + + const FloatV invMass0 = FLoad(header->invMass0D0); + const FloatV invMass1 = FLoad(header->invMass1D1); + const FloatV invInertiaScale0 = FLoad(header->angularInvMassScale0); + const FloatV invInertiaScale1 = FLoad(header->angularInvMassScale1); + + + const QuatV deltaRotA = Ps::aos::QuatVLoadA(&txI0.deltaBody2World.q.x); + const QuatV deltaRotB = Ps::aos::QuatVLoadA(&txI1.deltaBody2World.q.x); + + const Vec3V raPrev = V3LoadA(header->rAWorld); + const Vec3V rbPrev = V3LoadA(header->rBWorld); + + const Vec3V ra = QuatRotate(deltaRotA, raPrev); + const Vec3V rb = QuatRotate(deltaRotB, rbPrev); + + + const Vec3V ang0 = V3LoadA(b0.deltaAngDt); + const Vec3V ang1 = V3LoadA(b1.deltaAngDt); + + const Vec3V lin0 = V3LoadA(b0.deltaLinDt); + const Vec3V lin1 = V3LoadA(b1.deltaLinDt); + + const Vec3V raMotion = V3Sub(V3Add(ra, lin0), raPrev); + const Vec3V rbMotion = V3Sub(V3Add(rb, lin1), rbPrev); + + const VecCrossV raCross = V3PrepareCross(ra); + const VecCrossV rbCross = V3PrepareCross(rb); + + const Vec4V ang0Ortho0_recipResponseW = V4LoadA(&header->angOrthoAxis0_recipResponseW[0].x); + const Vec4V ang0Ortho1_recipResponseW = V4LoadA(&header->angOrthoAxis0_recipResponseW[1].x); + const Vec4V ang0Ortho2_recipResponseW = V4LoadA(&header->angOrthoAxis0_recipResponseW[2].x); + + const Vec4V ang1Ortho0_Error0 = V4LoadA(&header->angOrthoAxis1_Error[0].x); + const Vec4V ang1Ortho1_Error1 = V4LoadA(&header->angOrthoAxis1_Error[1].x); + const Vec4V ang1Ortho2_Error2 = V4LoadA(&header->angOrthoAxis1_Error[2].x); + + const FloatV recipResponse0 = V4GetW(ang0Ortho0_recipResponseW); + const FloatV recipResponse1 = V4GetW(ang0Ortho1_recipResponseW); + const FloatV recipResponse2 = V4GetW(ang0Ortho2_recipResponseW); + + const Vec3V ang0Ortho0 = Vec3V_From_Vec4V(ang0Ortho0_recipResponseW); + const Vec3V ang0Ortho1 = Vec3V_From_Vec4V(ang0Ortho1_recipResponseW); + const Vec3V ang0Ortho2 = Vec3V_From_Vec4V(ang0Ortho2_recipResponseW); + + const Vec3V ang1Ortho0 = Vec3V_From_Vec4V(ang1Ortho0_Error0); + const Vec3V ang1Ortho1 = Vec3V_From_Vec4V(ang1Ortho1_Error1); + const Vec3V ang1Ortho2 = Vec3V_From_Vec4V(ang1Ortho2_Error2); + + FloatV error0 = FAdd(V4GetW(ang1Ortho0_Error0), FSub(V3Dot(ang0Ortho0, ang0), V3Dot(ang1Ortho0, ang1))); + FloatV error1 = FAdd(V4GetW(ang1Ortho1_Error1), FSub(V3Dot(ang0Ortho1, ang0), V3Dot(ang1Ortho1, ang1))); + FloatV error2 = FAdd(V4GetW(ang1Ortho2_Error2), FSub(V3Dot(ang0Ortho2, ang0), V3Dot(ang1Ortho2, ang1))); + + for (PxU32 i = 0; icount; ++i, base++) + { + Ps::prefetchLine(base + 1); + SolverConstraint1DStep& c = *base; + + const Vec3V clinVel0 = V3LoadA(c.lin0); + const Vec3V clinVel1 = V3LoadA(c.lin1); + + const Vec3V cangVel0_ = V3LoadA(c.ang0); + const Vec3V cangVel1_ = V3LoadA(c.ang1); + + const FloatV angularErrorScale = FLoad(c.angularErrorScale); + + const FloatV biasScale = FLoad(c.biasScale); + const FloatV maxBias = FLoad(c.maxBias); + const FloatV targetVel = FLoad(c.velTarget); + const FloatV iMul = FLoad(c.impulseMultiplier); + const FloatV appliedForce = FLoad(c.appliedForce); + const FloatV velMultiplier = FLoad(c.velMultiplier); + + const FloatV maxImpulse = FLoad(c.maxImpulse); + const FloatV minImpulse = FLoad(c.minImpulse); + + Vec3V cangVel0 = V3Add(cangVel0_, V3Cross(raCross, clinVel0)); + Vec3V cangVel1 = V3Add(cangVel1_, V3Cross(rbCross, clinVel1)); + + + FloatV error = FLoad(c.error); + + const FloatV minBias = (c.flags & DY_SC_FLAG_INEQUALITY) ? FNeg(FMax()) : FNeg(maxBias); + + + Vec3V raXnI = M33MulV3(sqrtInvInertia0, cangVel0); + Vec3V rbXnI = M33MulV3(sqrtInvInertia1, cangVel1); + + if (c.flags & DY_SC_FLAG_ORTHO_TARGET) + { + //Re-orthogonalize the constraints before velocity projection and impulse response calculation + //Can be done in using instruction parallelism because angular locked axes are orthogonal to linear axes! + + const FloatV proj0 = FMul(V3SumElems(V3MulAdd(raXnI, ang0Ortho0, + V3Mul(rbXnI, ang1Ortho0))), recipResponse0); + + const FloatV proj1 = FMul(V3SumElems(V3MulAdd(raXnI, ang0Ortho1, + V3Mul(rbXnI, ang1Ortho1))), recipResponse1); + const FloatV proj2 = FMul(V3SumElems(V3MulAdd(raXnI, ang0Ortho2, + V3Mul(rbXnI, ang1Ortho2))), recipResponse2); + + const Vec3V delta0 = V3ScaleAdd(ang0Ortho0, proj0, V3ScaleAdd(ang0Ortho1, proj1, V3Scale(ang0Ortho2, proj2))); + const Vec3V delta1 = V3ScaleAdd(ang1Ortho0, proj0, V3ScaleAdd(ang1Ortho1, proj1, V3Scale(ang1Ortho2, proj2))); + + raXnI = V3Sub(raXnI, delta0); + rbXnI = V3Sub(rbXnI, delta1); + + error = FSub(error, FScaleAdd(error0, proj0, FScaleAdd(error1, proj1, FMul(error2, proj2)))); + } + + const FloatV deltaAng = FMul(angularErrorScale, FSub(V3Dot(raXnI, ang0), V3Dot(rbXnI, ang1))); + + error = FNegScaleSub(targetVel, elapsed, FAdd(FAdd(error, FSub(V3Dot(raMotion, clinVel0), V3Dot(rbMotion, clinVel1))), deltaAng)); + + const FloatV resp0 = FScaleAdd(invMass0, V3Dot(clinVel0, clinVel0), V3SumElems(V3Mul(V3Scale(raXnI, invInertiaScale0), raXnI))); + const FloatV resp1 = FSub(FMul(invMass1, V3Dot(clinVel1, clinVel1)), V3SumElems(V3Mul(V3Scale(rbXnI, invInertiaScale1), rbXnI))); + + const FloatV response = FAdd(resp0, resp1); + const FloatV recipResponse = FSel(FIsGrtr(response, FZero()), FRecip(response), FZero()); + + const FloatV vMul = FMul(recipResponse, velMultiplier); + + const FloatV unclampedBias = FMul(error, biasScale); + const FloatV bias = FClamp(unclampedBias, minBias, maxBias); + + const FloatV constant = FMul(recipResponse, FAdd(bias, targetVel)); + + const Vec3V v0 = V3MulAdd(linVel0, clinVel0, V3Mul(angState0, raXnI)); + const Vec3V v1 = V3MulAdd(linVel1, clinVel1, V3Mul(angState1, rbXnI)); + const FloatV normalVel = V3SumElems(V3Sub(v0, v1)); + + const FloatV af = FMul(iMul, appliedForce); + + const FloatV unclampedForce = FAdd(af, FScaleAdd(vMul, normalVel, constant)); + const FloatV clampedForce = FClamp(unclampedForce, minImpulse, maxImpulse); + const FloatV deltaF = FSub(clampedForce, appliedForce); + + FStore(clampedForce, &c.appliedForce); + linVel0 = V3ScaleAdd(clinVel0, FMul(deltaF, invMass0), linVel0); + linVel1 = V3NegScaleSub(clinVel1, FMul(deltaF, invMass1), linVel1); + angState0 = V3ScaleAdd(raXnI, FMul(deltaF, invInertiaScale0), angState0); + angState1 = V3ScaleAdd(rbXnI, FMul(deltaF, invInertiaScale1), angState1); + + } + + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(angState0, b0.angularVelocity); + V3StoreA(linVel1, b1.linearVelocity); + V3StoreA(angState1, b1.angularVelocity); + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularVelocity.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularVelocity.isFinite()); +} + + +//Port of scalar implementation to SIMD maths with some interleaving of instructions +void conclude1DStep(const PxTGSSolverConstraintDesc& desc) +{ + PxU8* PX_RESTRICT bPtr = desc.constraint; + if (bPtr == NULL) + return; + + const SolverConstraint1DHeaderStep* PX_RESTRICT header = reinterpret_cast(bPtr); + PxU8* PX_RESTRICT base = bPtr + sizeof(SolverConstraint1DHeaderStep); + const PxU32 stride = header->type == DY_SC_TYPE_RB_1D ? sizeof(SolverConstraint1DStep) : sizeof(SolverConstraint1DExtStep); + + for (PxU32 i = 0; icount; ++i, base+=stride) + { + SolverConstraint1DStep& c = *reinterpret_cast(base); + Ps::prefetchLine(&c + 1); + if (!(c.flags & DY_SC_FLAG_KEEP_BIAS)) + c.biasScale = 0.f; + } +} + +void concludeContact(const PxTGSSolverConstraintDesc& desc) +{ + PX_UNUSED(desc); + //const PxU8* PX_RESTRICT last = desc.constraint + getConstraintLength(desc); + + ////hopefully pointer aliasing doesn't bite. + //PxU8* PX_RESTRICT currPtr = desc.constraint; + + //SolverContactHeaderStep* PX_RESTRICT firstHdr = reinterpret_cast(currPtr); + + //bool isExtended = firstHdr->type == DY_SC_TYPE_EXT_CONTACT; + + //const PxU32 contactStride = isExtended ? sizeof(SolverContactPointStepExt) : sizeof(SolverContactPointStep); + //const PxU32 frictionStride = isExtended ? sizeof(SolverContactFrictionStepExt) : sizeof(SolverContactFrictionStep); + + + //while (currPtr < last) + //{ + // SolverContactHeaderStep* PX_RESTRICT hdr = reinterpret_cast(currPtr); + // currPtr += sizeof(SolverContactHeaderStep); + + // const PxU32 numNormalConstr = hdr->numNormalConstr; + // const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + // /*PxU8* PX_RESTRICT contacts = currPtr; + // Ps::prefetchLine(contacts);*/ + // currPtr += numNormalConstr * contactStride; + + // //PxF32* forceBuffer = reinterpret_cast(currPtr); + // currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + // PxU8* PX_RESTRICT frictions = currPtr; + // currPtr += numFrictionConstr * frictionStride; + + // /*for (PxU32 i = 0; i < numNormalConstr; ++i) + // { + // SolverContactPointStep& c = *reinterpret_cast(contacts); + // contacts += contactStride; + // if(c.separation <= 0.f) + // c.biasCoefficient = 0.f; + // }*/ + + // for (PxU32 i = 0; i < numFrictionConstr; ++i) + // { + // SolverContactFrictionStep& f = *reinterpret_cast(frictions); + // frictions += frictionStride; + // f.biasScale = 0.f; + // } + //} + + //PX_ASSERT(currPtr == last); +} + + + + +void writeBack1D(const PxTGSSolverConstraintDesc& desc) +{ + ConstraintWriteback* writeback = reinterpret_cast(desc.writeBack); + if (writeback) + { + SolverConstraint1DHeaderStep* header = reinterpret_cast(desc.constraint); + PxU8* base = desc.constraint + sizeof(SolverConstraint1DHeaderStep); + PxU32 stride = header->type == DY_SC_TYPE_EXT_1D ? sizeof(SolverConstraint1DExtStep) : sizeof(SolverConstraint1DStep); + + PxVec3 lin(0), ang(0); + for (PxU32 i = 0; icount; i++) + { + const SolverConstraint1DStep* c = reinterpret_cast(base); + if (c->flags & DY_SC_FLAG_OUTPUT_FORCE) + { + lin += c->lin0 * c->appliedForce; + ang += (c->ang0 + c->lin0.cross(header->rAWorld)) * c->appliedForce; + } + base += stride; + } + + ang -= header->body0WorldOffset.cross(lin); + writeback->linearImpulse = lin; + writeback->angularImpulse = ang; + writeback->broken = header->breakable ? PxU32(lin.magnitude()>header->linBreakImpulse || ang.magnitude()>header->angBreakImpulse) : 0; + + PX_ASSERT(desc.constraint + (desc.constraintLengthOver16 * 16) == base); + } +} + + +PxU32 Articulation::setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* /*Z*/) +{ + Articulation* articulation = static_cast(articDesc.articulation); + FsData& fsData = *articulation->getFsDataPtr(); + const PxU32 solverDataSize = articDesc.solverDataSize; + const ArticulationLink* links = articDesc.links; + + PxcFsScratchAllocator allocator(articDesc.scratchMemory, articDesc.scratchMemorySize); + FsInertia* PX_RESTRICT baseInertia = allocator.alloc(articDesc.linkCount); + PX_UNUSED(baseInertia); + ArticulationJointTransforms* PX_RESTRICT jointTransforms = allocator.alloc(articDesc.linkCount); + + PX_UNUSED(dt); + acCount = 0; + + const PxU16 linkCount = fsData.linkCount; + PxU32 descCount = 0; + const PxReal recipDt = invDt; + + const PxConstraintInvMassScale ims(1.0f, 1.0f, 1.0f, 1.0f); + + for (PxU16 i = 1; i(*links[i].inboundJoint); + + if (i + 10 || j.tangentialDamping>0); + + const PxVec3 twistAxis = jointTransforms[i].cB2w.rotate(PxVec3(1.0f, 0, 0)); + const PxReal tqTwistAngle = Ps::tanHalf(twist.x, twist.w); + + const bool twistLowerLimited = j.twistLimited && tqTwistAngle < Cm::tanAdd(j.tanQTwistLow, j.tanQTwistPad); + const bool twistUpperLimited = j.twistLimited && tqTwistAngle > Cm::tanAdd(j.tanQTwistHigh, -j.tanQTwistPad); + + const PxU8 constraintCount = PxU8(swingLimited + tangentialStiffness + twistUpperLimited + twistLowerLimited); + if (!constraintCount) + continue; + + PxTGSSolverConstraintDesc& desc = constraintDesc[descCount++]; + + desc.articulationA = articulation; + desc.linkIndexA = Ps::to16(links[i].parent); + desc.articulationALength = Ps::to16(solverDataSize); + + desc.articulationB = articulation; + desc.linkIndexB = i; + desc.articulationBLength = Ps::to16(solverDataSize); + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeaderStep) + + sizeof(SolverConstraint1DExtStep) * constraintCount; + + PX_ASSERT(0 == (constraintLength & 0x0f)); + desc.constraintLengthOver16 = Ps::to16(constraintLength / 16); + + desc.constraint = stream.reserve(constraintLength + 16u, constraintBlockManager); + + desc.writeBack = NULL; + + SolverConstraint1DHeaderStep* header = reinterpret_cast(desc.constraint); + SolverConstraint1DExtStep* constraints = reinterpret_cast(desc.constraint + sizeof(SolverConstraint1DHeaderStep)); + + init(*header, constraintCount, true, ims); + + header->rAWorld = PxVec3(0.f); + header->rBWorld = PxVec3(0.f); + + PxU32 cIndex = 0; + + if (swingLimited) + { + const PxVec3 normal = jointTransforms[i].cA2w.rotate(swingLimitAxis); + ArticulationHelper::createHardLimitTGS(fsData, links, i, constraints[cIndex++], normal, swingLimitError, recipDt); + if (tangentialStiffness) + { + const PxVec3 tangent = twistAxis.cross(normal).getNormalized(); + ArticulationHelper::createTangentialSpringTGS(fsData, links, i, constraints[cIndex++], tangent, j.tangentialStiffness, j.tangentialDamping, 1.f/recipDt); + } + } + + if (twistUpperLimited) + ArticulationHelper::createHardLimitTGS(fsData, links, i, constraints[cIndex++], twistAxis, (j.tanQTwistHigh - tqTwistAngle) * 4, recipDt); + + if (twistLowerLimited) + ArticulationHelper::createHardLimitTGS(fsData, links, i, constraints[cIndex++], -twistAxis, -(j.tanQTwistLow - tqTwistAngle) * 4, recipDt); + + *(desc.constraint + getConstraintLength(desc)) = 0; + + PX_ASSERT(cIndex == constraintCount); + acCount += constraintCount; + } + + return descCount; + +} + + +void ArticulationHelper::createHardLimitTGS(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt) +{ + PxReal error = err; + init(s, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); + + ArticulationHelper::getImpulseSelfResponse(fsData, + links[linkIndex].parent, Cm::SpatialVector(PxVec3(0), axis), s.deltaVA, + linkIndex, Cm::SpatialVector(PxVec3(0), -axis), s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if (unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, joint limit ignored"); + + const PxReal recipResponse = unitResponse>0.0f ? 1.0f / unitResponse : 0.0f; + + s.error = error; + s.biasScale = -recipDt*0.7f; + s.maxBias = PX_MAX_F32; + s.velMultiplier = -1.f; + s.recipResponse = recipResponse; + s.impulseMultiplier = 1.0f; + s.velTarget = 0.f; +} + +void ArticulationHelper::createTangentialSpringTGS(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt) +{ + init(s, PxVec3(0), PxVec3(0), axis, axis, -PX_MAX_F32, PX_MAX_F32); + + Cm::SpatialVector axis6(PxVec3(0), axis); + PxU32 parent = links[linkIndex].parent; + getImpulseSelfResponse(fsData, parent, axis6, s.deltaVA, linkIndex, -axis6, s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if (unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, tangential spring ignored"); + const PxReal recipResponse = unitResponse>0.0F ? 1.0f / unitResponse : 0.0f; + + // this is a specialization of the spring code in setSolverConstants() for acceleration springs. + // general case is b = dt * (c.mods.spring.damping * c.velocityTarget - c.mods.spring.stiffness * geomError); + // but geomError and velocityTarget are both zero + + const PxReal a = dt * dt * stiffness + dt * damping; + const PxReal x = 1.0f / (1.0f + a); + s.error = 0.f; + s.biasScale = 0.f; + s.maxBias = 0.f; + s.velMultiplier = -x * a; + s.impulseMultiplier = 1.0f - x; + s.velTarget = 0.f; + s.recipResponse = recipResponse; +} + +static FloatV solveExtContactsStep(SolverContactPointStepExt* contacts, const PxU32 nbContactPoints, const Vec3VArg contactNormal, + Vec3V& linVel0, Vec3V& angVel0, + Vec3V& linVel1, Vec3V& angVel1, + Vec3V& li0, Vec3V& ai0, + Vec3V& li1, Vec3V& ai1, + const Vec3V& linDeltaA, const Vec3V& linDeltaB, const Vec3V& angDeltaA, const Vec3V angDeltaB, const FloatV& maxPenBias, + PxF32* PX_RESTRICT appliedForceBuffer, + const FloatV& minPen, + const FloatV& elapsedTime) +{ + + const FloatV deltaV = V3Dot(contactNormal, V3Sub(linDeltaA, linDeltaB)); + + FloatV accumulatedNormalImpulse = FZero(); + for (PxU32 i = 0; ilinearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularVelocity); + linDelta0 = V3LoadA(desc.bodyA->deltaLinDt); + angDelta0 = V3LoadA(desc.bodyA->deltaAngDt); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + Cm::SpatialVectorV deltaV = PxcFsGetMotionVector(*desc.articulationA, desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + linDelta0 = deltaV.linear; + angDelta0 = deltaV.angular; + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularVelocity); + linDelta1 = V3LoadA(desc.bodyB->deltaLinDt); + angDelta1 = V3LoadA(desc.bodyB->deltaAngDt); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + Cm::SpatialVectorV deltaV = PxcFsGetMotionVector(*desc.articulationB, desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + linDelta1 = deltaV.linear; + angDelta1 = deltaV.angular; + } + + /*PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(linVel0, linVel0))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(linVel1, linVel1))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(angVel0, angVel0))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(angVel1, angVel1)));*/ + + + const PxU8* PX_RESTRICT last = desc.constraint + desc.constraintLengthOver16 * 16; + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + Vec3V linImpulse0 = V3Zero(), linImpulse1 = V3Zero(), angImpulse0 = V3Zero(), angImpulse1 = V3Zero(); + + const Vec3V relMotion = V3Sub(linDelta0, linDelta1); + + while (currPtr < last) + { + SolverContactHeaderStep* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactHeaderStep); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverContactPointStepExt* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPointStepExt); + + PxF32* appliedForceBuffer = reinterpret_cast(currPtr); + currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + SolverContactFrictionStepExt* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionStepExt); + + + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + const Vec3V contactNormal = V3LoadA(hdr->normal); + + const FloatV accumulatedNormalImpulse = FMax(solveExtContactsStep(contacts, numNormalConstr, contactNormal, linVel0, angVel0, linVel1, + angVel1, li0, ai0, li1, ai1, linDelta0, linDelta1, angDelta0, angDelta1, FLoad(hdr->maxPenBias), appliedForceBuffer, minPen, elapsedTime), + FLoad(hdr->minNormalForce)); + + if(doFriction && numFrictionConstr) + { + Ps::prefetchLine(frictions); + const FloatV maxFrictionImpulse = FMul(hdr->getStaticFriction(), accumulatedNormalImpulse); + const FloatV maxDynFrictionImpulse = FMul(hdr->getDynamicFriction(), accumulatedNormalImpulse); + + BoolV broken = BFFFF(); + + for (PxU32 i = 0; i maxFrictionImpulse + // clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse] + // (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce] + // set broken flag to true || broken flag + + // FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier); + // FloatV potentialSumF = FAdd(appliedForce, deltaF); + + const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1); + + // On XBox this clamping code uses the vector simple pipe rather than vector float, + // which eliminates a lot of stall cycles + + const BoolV clamp = FIsGrtr(FAbs(totalImpulse), maxFrictionImpulse); + + const FloatV totalClamped = FMin(maxDynFrictionImpulse, FMax(negMaxDynFrictionImpulse, totalImpulse)); + + const FloatV newAppliedForce = FSel(clamp, totalClamped, totalImpulse); + + broken = BOr(broken, clamp); + + FloatV deltaF = FSub(newAppliedForce, appliedForce); + + linVel0 = V3ScaleAdd(f.linDeltaVA, deltaF, linVel0); + angVel0 = V3ScaleAdd(raXnI, deltaF, angVel0); + linVel1 = V3ScaleAdd(f.linDeltaVB, deltaF, linVel1); + angVel1 = V3ScaleAdd(rbXnI, deltaF, angVel1); + + /*PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(linVel0, linVel0))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(linVel1, linVel1))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(angVel0, angVel0))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(angVel1, angVel1)));*/ + + li0 = V3ScaleAdd(normal, deltaF, li0); ai0 = V3ScaleAdd(raXn, deltaF, ai0); + li1 = V3ScaleAdd(normal, deltaF, li1); ai1 = V3ScaleAdd(rbXn, deltaF, ai1); + + f.setAppliedForce(newAppliedForce); + } + Store_From_BoolV(broken, &hdr->broken); + } + + linImpulse0 = V3ScaleAdd(li0, hdr->getDominance0(), linImpulse0); + angImpulse0 = V3ScaleAdd(ai0, FLoad(hdr->angDom0), angImpulse0); + linImpulse1 = V3NegScaleSub(li1, hdr->getDominance1(), linImpulse1); + angImpulse1 = V3NegScaleSub(ai1, FLoad(hdr->angDom1), angImpulse1); + } + + if (desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularVelocity); + } + else + { + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, + linImpulse0, angImpulse0, cache.Z, cache.deltaV); + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularVelocity); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, linImpulse1, + angImpulse1, cache.Z, cache.deltaV); + } + + PX_ASSERT(currPtr == last); +} + +void solveContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveContact(desc[i], doFriction, minPenetration, elapsedTime); + } +} + +void solve1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxTGSSolverBodyTxInertia* const txInertias, const PxReal elapsedTime) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solve1DStep(desc[i], txInertias, elapsedTime); + } +} + +void solveExtContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime, SolverContext& cache) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveExtContactStep(desc[i], doFriction, minPenetration, elapsedTime, cache); + } +} + +void solveExt1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime, SolverContext& cache, + const PxTGSSolverBodyTxInertia* const txInertias) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveExt1DStep(desc[i], elapsedTime, cache, txInertias); + } +} + +void writeBackContact(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, SolverContext* cache) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + writeBackContact(desc[i], cache); + } +} + +void writeBack1D(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + writeBack1D(desc[i]); + } +} + +void solveConclude1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, + const PxTGSSolverBodyTxInertia* const txInertias, const PxReal elapsedTime) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solve1DStep(desc[i], txInertias, elapsedTime); + conclude1DStep(desc[i]); + } +} + +void solveConclude1DBlockExt(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, + const PxReal elapsedTime, SolverContext& cache, const PxTGSSolverBodyTxInertia* const txInertias) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveExt1DStep(desc[i], elapsedTime, cache, txInertias); + conclude1DStep(desc[i]); + } +} + + +void solveConcludeContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveContact(desc[i], true, -PX_MAX_F32, elapsedTime); + concludeContact(desc[i]); + } +} + +void solveConcludeContactExtBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime, SolverContext& cache) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveExtContactStep(desc[i], true, -PX_MAX_F32, elapsedTime, cache); + concludeContact(desc[i]); + } +} + + +} +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp new file mode 100644 index 000000000..9263d1244 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp @@ -0,0 +1,3658 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "PxSceneDesc.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContact4.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DyDynamics.h" +#include "DyArticulationContactPrep.h" +#include "PxsContactManager.h" +#include "PsFoundation.h" +#include "DyTGSDynamics.h" + +using namespace physx; +using namespace Gu; + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DyContactPrepShared.h" +#include "DySolverConstraint1D.h" +#include "DyConstraintPrep.h" + +#include "CmConeLimitHelper.h" + +#include "DySolverContext.h" + +namespace physx +{ +namespace Dy +{ + + inline bool ValidateVec4(const Vec4V v) + { + PX_ALIGN(16, PxVec4 vF); + Ps::aos::V4StoreA(v, &vF.x); + return vF.isFinite(); + } + + PX_FORCE_INLINE void QuatRotate4(const Vec4VArg qx, const Vec4VArg qy, const Vec4VArg qz, const Vec4VArg qw, const Vec4VArg vx, const Vec4VArg vy, const Vec4VArg vz, + Vec4V& rX, Vec4V& rY, Vec4V& rZ) + { + /* + const PxVec3 qv(x,y,z); + return (v*(w*w-0.5f) + (qv.cross(v))*w + qv*(qv.dot(v)))*2; + */ + + const Vec4V two = V4Splat(FLoad(2.f)); + const Vec4V nhalf = V4Splat(FLoad(-0.5f)); + const Vec4V w2 = V4MulAdd(qw, qw, nhalf); + const Vec4V ax = V4Mul(vx, w2); + const Vec4V ay = V4Mul(vy, w2); + const Vec4V az = V4Mul(vz, w2); + + const Vec4V crX = V4NegMulSub(qz, vy, V4Mul(qy, vz)); + const Vec4V crY = V4NegMulSub(qx, vz, V4Mul(qz, vx)); + const Vec4V crZ = V4NegMulSub(qy, vx, V4Mul(qx, vy)); + + const Vec4V tempX = V4MulAdd(crX, qw, ax); + const Vec4V tempY = V4MulAdd(crY, qw, ay); + const Vec4V tempZ = V4MulAdd(crZ, qw, az); + + Vec4V dotuv = V4Mul(qx, vx); + dotuv = V4MulAdd(qy, vy, dotuv); + dotuv = V4MulAdd(qz, vz, dotuv); + + rX = V4Mul(V4MulAdd(qx, dotuv, tempX), two); + rY = V4Mul(V4MulAdd(qy, dotuv, tempY), two); + rZ = V4Mul(V4MulAdd(qz, dotuv, tempZ), two); + } + + +struct SolverContactHeaderStepBlock +{ + enum + { + eHAS_MAX_IMPULSE = 1 << 0, + eHAS_TARGET_VELOCITY = 1 << 1 + }; + + PxU8 type; //Note: mType should be first as the solver expects a type in the first byte. + PxU8 numNormalConstr; + PxU8 numFrictionConstr; + PxU8 flag; + + PxU8 flags[4]; + + //KS - used for write-back only + PxU8 numNormalConstrs[4]; + PxU8 numFrictionConstrs[4]; + + Vec4V restitution; + Vec4V staticFriction; + Vec4V dynamicFriction; + //Technically, these mass properties could be pulled out into a new structure and shared. For multi-manifold contacts, + //this would save 64 bytes per-manifold after the cost of the first manifold + Vec4V invMass0D0; + Vec4V invMass1D1; + Vec4V angDom0; + Vec4V angDom1; + //Normal is shared between all contacts in the batch. This will save some memory! + Vec4V normalX; + Vec4V normalY; + Vec4V normalZ; + + Vec4V maxPenBias; + + Sc::ShapeInteraction* shapeInteraction[4]; //192 or 208 + + BoolV broken; + PxU8* frictionBrokenWritebackByte[4]; +}; + +struct SolverContactPointStepBlock +{ + Vec4V raXnI[3]; + Vec4V rbXnI[3]; + Vec4V separation; + Vec4V velMultiplier; + Vec4V targetVelocity; + Vec4V biasCoefficient; +}; + +//KS - technically, this friction constraint has identical data to the above contact constraint. +//We make them separate structs for clarity +struct SolverContactFrictionStepBlock +{ + Vec4V normal[3]; + Vec4V raXnI[3]; + Vec4V rbXnI[3]; + Vec4V error; + Vec4V velMultiplier; + Vec4V targetVel; + Vec4V biasCoefficient; +}; + +struct SolverConstraint1DHeaderStep4 +{ + PxU8 type; // enum SolverConstraintType - must be first byte + PxU8 pad0[3]; + //These counts are the max of the 4 sets of data. + //When certain pairs have fewer constraints than others, they are padded with 0s so that no work is performed but + //calculations are still shared (afterall, they're computationally free because we're doing 4 things at a time in SIMD) + PxU32 count; + PxU8 counts[4]; + PxU8 breakable[4]; + + Vec4V linBreakImpulse; + Vec4V angBreakImpulse; + Vec4V invMass0D0; + Vec4V invMass1D1; + Vec4V angD0; + Vec4V angD1; + + Vec4V body0WorkOffset[3]; + Vec4V rAWorld[3]; + Vec4V rBWorld[3]; + + Vec4V angOrthoAxis0X[3]; + Vec4V angOrthoAxis0Y[3]; + Vec4V angOrthoAxis0Z[3]; + Vec4V angOrthoAxis1X[3]; + Vec4V angOrthoAxis1Y[3]; + Vec4V angOrthoAxis1Z[3]; + Vec4V angOrthoRecipResponse[3]; + Vec4V angOrthoError[3]; +}; + + +PX_ALIGN_PREFIX(16) +struct SolverConstraint1DStep4 +{ +public: + Vec4V lin0[3]; //!< linear velocity projection (body 0) + Vec4V error; //!< constraint error term - must be scaled by biasScale. Can be adjusted at run-time + + Vec4V lin1[3]; //!< linear velocity projection (body 1) + Vec4V biasScale; //!< constraint constant bias scale. Constant + + Vec4V ang0[3]; //!< angular velocity projection (body 0) + Vec4V velMultiplier; //!< constraint velocity multiplier + + Vec4V ang1[3]; //!< angular velocity projection (body 1) + Vec4V impulseMultiplier; //!< constraint impulse multiplier + + Vec4V velTarget; //!< Scaled target velocity of the constraint drive + + Vec4V minImpulse; //!< Lower bound on impulse magnitude + Vec4V maxImpulse; //!< Upper bound on impulse magnitude + Vec4V appliedForce; //!< applied force to correct velocity+bias + + Vec4V maxBias; + Vec4V angularErrorScale; //Constant + PxU32 flags[4]; +} PX_ALIGN_SUFFIX(16); + + + + +static void setupFinalizeSolverConstraints4Step(PxTGSSolverContactDesc* PX_RESTRICT descs, CorrelationBuffer& c, + PxU8* PX_RESTRICT workspace, const PxReal invDtF32, const PxReal invTotalDtF32, PxReal bounceThresholdF32, const PxReal solverOffsetSlopF32, + const Ps::aos::Vec4VArg invMassScale0, const Ps::aos::Vec4VArg invInertiaScale0, + const Ps::aos::Vec4VArg invMassScale1, const Ps::aos::Vec4VArg invInertiaScale1) +{ + + //OK, we have a workspace of pre-allocated space to store all 4 descs in. We now need to create the constraints in it + + //const Vec4V ccdMaxSeparation = Ps::aos::V4LoadXYZW(descs[0].maxCCDSeparation, descs[1].maxCCDSeparation, descs[2].maxCCDSeparation, descs[3].maxCCDSeparation); + const Vec4V solverOffsetSlop = V4Load(solverOffsetSlopF32); + + const Vec4V zero = V4Zero(); + const BoolV bFalse = BFFFF(); + const FloatV fZero = FZero(); + + PxU8 flags[4] = { PxU8(descs[0].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[1].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[2].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[3].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0) }; + + bool hasMaxImpulse = descs[0].hasMaxImpulse || descs[1].hasMaxImpulse || descs[2].hasMaxImpulse || descs[3].hasMaxImpulse; + + //The block is dynamic if **any** of the constraints have a non-static body B. This allows us to batch static and non-static constraints but we only get a memory/perf + //saving if all 4 are static. This simplifies the constraint partitioning such that it only needs to care about separating contacts and 1D constraints (which it already does) + bool isDynamic = false; + bool hasKinematic = false; + + PxReal kinematicScale0F32[4]; + PxReal kinematicScale1F32[4]; + + for (PxU32 a = 0; a < 4; ++a) + { + isDynamic = isDynamic || (descs[a].bodyState1 == PxSolverContactDesc::eDYNAMIC_BODY); + hasKinematic = hasKinematic || descs[a].bodyState1 == PxSolverContactDesc::eKINEMATIC_BODY; + kinematicScale0F32[a] = descs[a].body0->isKinematic ? 1.f : 0.f; + kinematicScale1F32[a] = descs[a].body1->isKinematic ? 1.f : 0.f; + } + + /*BoolV kinematic0 = BLoad(isKinematic0); + BoolV kinematic1 = BLoad(isKinematic1);*/ + + const Vec4V kinematicScale0 = V4LoadU(kinematicScale0F32); + const Vec4V kinematicScale1 = V4LoadU(kinematicScale1F32); + + const PxU32 constraintSize = sizeof(SolverContactPointStepBlock); + const PxU32 frictionSize = sizeof(SolverContactFrictionStepBlock); + + PxU8* PX_RESTRICT ptr = workspace; + + const Vec4V dom0 = invMassScale0; + const Vec4V dom1 = invMassScale1; + const Vec4V angDom0 = invInertiaScale0; + const Vec4V angDom1 = invInertiaScale1; + + const Vec4V maxPenBias = V4Max(V4LoadXYZW(descs[0].bodyData0->penBiasClamp, descs[1].bodyData0->penBiasClamp, + descs[2].bodyData0->penBiasClamp, descs[3].bodyData0->penBiasClamp), + V4LoadXYZW(descs[0].bodyData1->penBiasClamp, descs[1].bodyData1->penBiasClamp, + descs[2].bodyData1->penBiasClamp, descs[3].bodyData1->penBiasClamp)); + + const Vec4V restDistance = V4LoadXYZW(descs[0].restDistance, descs[1].restDistance, descs[2].restDistance, + descs[3].restDistance); + + + //load up velocities + Vec4V linVel00 = V4LoadA(&descs[0].bodyData0->originalLinearVelocity.x); + Vec4V linVel10 = V4LoadA(&descs[1].bodyData0->originalLinearVelocity.x); + Vec4V linVel20 = V4LoadA(&descs[2].bodyData0->originalLinearVelocity.x); + Vec4V linVel30 = V4LoadA(&descs[3].bodyData0->originalLinearVelocity.x); + + Vec4V linVel01 = V4LoadA(&descs[0].bodyData1->originalLinearVelocity.x); + Vec4V linVel11 = V4LoadA(&descs[1].bodyData1->originalLinearVelocity.x); + Vec4V linVel21 = V4LoadA(&descs[2].bodyData1->originalLinearVelocity.x); + Vec4V linVel31 = V4LoadA(&descs[3].bodyData1->originalLinearVelocity.x); + + Vec4V angVel00 = V4LoadA(&descs[0].bodyData0->originalAngularVelocity.x); + Vec4V angVel10 = V4LoadA(&descs[1].bodyData0->originalAngularVelocity.x); + Vec4V angVel20 = V4LoadA(&descs[2].bodyData0->originalAngularVelocity.x); + Vec4V angVel30 = V4LoadA(&descs[3].bodyData0->originalAngularVelocity.x); + + Vec4V angVel01 = V4LoadA(&descs[0].bodyData1->originalAngularVelocity.x); + Vec4V angVel11 = V4LoadA(&descs[1].bodyData1->originalAngularVelocity.x); + Vec4V angVel21 = V4LoadA(&descs[2].bodyData1->originalAngularVelocity.x); + Vec4V angVel31 = V4LoadA(&descs[3].bodyData1->originalAngularVelocity.x); + + Vec4V linVelT00, linVelT10, linVelT20; + Vec4V linVelT01, linVelT11, linVelT21; + Vec4V angVelT00, angVelT10, angVelT20; + Vec4V angVelT01, angVelT11, angVelT21; + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVelT00, linVelT10, linVelT20); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVelT01, linVelT11, linVelT21); + PX_TRANSPOSE_44_34(angVel00, angVel10, angVel20, angVel30, angVelT00, angVelT10, angVelT20); + PX_TRANSPOSE_44_34(angVel01, angVel11, angVel21, angVel31, angVelT01, angVelT11, angVelT21); + + const Vec4V vrelX = V4Sub(linVelT00, linVelT01); + const Vec4V vrelY = V4Sub(linVelT10, linVelT11); + const Vec4V vrelZ = V4Sub(linVelT20, linVelT21); + + //Load up masses and invInertia + + const Vec4V invMass0 = V4LoadXYZW(descs[0].bodyData0->invMass, descs[1].bodyData0->invMass, descs[2].bodyData0->invMass, descs[3].bodyData0->invMass); + const Vec4V invMass1 = V4LoadXYZW(descs[0].bodyData1->invMass, descs[1].bodyData1->invMass, descs[2].bodyData1->invMass, descs[3].bodyData1->invMass); + + const Vec4V invMass0D0 = V4Mul(dom0, invMass0); + const Vec4V invMass1D1 = V4Mul(dom1, invMass1); + + Vec4V invInertia00X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].body0TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia00Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].body0TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia00Z = Vec4V_From_Vec3V(V3LoadU(descs[0].body0TxI->sqrtInvInertia.column2)); + + Vec4V invInertia10X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].body0TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia10Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].body0TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia10Z = Vec4V_From_Vec3V(V3LoadU(descs[1].body0TxI->sqrtInvInertia.column2)); + + Vec4V invInertia20X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].body0TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia20Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].body0TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia20Z = Vec4V_From_Vec3V(V3LoadU(descs[2].body0TxI->sqrtInvInertia.column2)); + + Vec4V invInertia30X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].body0TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia30Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].body0TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia30Z = Vec4V_From_Vec3V(V3LoadU(descs[3].body0TxI->sqrtInvInertia.column2)); + + Vec4V invInertia01X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].body1TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia01Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].body1TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia01Z = Vec4V_From_Vec3V(V3LoadU(descs[0].body1TxI->sqrtInvInertia.column2)); + + Vec4V invInertia11X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].body1TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia11Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].body1TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia11Z = Vec4V_From_Vec3V(V3LoadU(descs[1].body1TxI->sqrtInvInertia.column2)); + + Vec4V invInertia21X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].body1TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia21Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].body1TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia21Z = Vec4V_From_Vec3V(V3LoadU(descs[2].body1TxI->sqrtInvInertia.column2)); + + Vec4V invInertia31X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].body1TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia31Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].body1TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia31Z = Vec4V_From_Vec3V(V3LoadU(descs[3].body1TxI->sqrtInvInertia.column2)); + + Vec4V invInertia0X0, invInertia0X1, invInertia0X2; + Vec4V invInertia0Y0, invInertia0Y1, invInertia0Y2; + Vec4V invInertia0Z0, invInertia0Z1, invInertia0Z2; + + Vec4V invInertia1X0, invInertia1X1, invInertia1X2; + Vec4V invInertia1Y0, invInertia1Y1, invInertia1Y2; + Vec4V invInertia1Z0, invInertia1Z1, invInertia1Z2; + + PX_TRANSPOSE_44_34(invInertia00X, invInertia10X, invInertia20X, invInertia30X, invInertia0X0, invInertia0Y0, invInertia0Z0); + PX_TRANSPOSE_44_34(invInertia00Y, invInertia10Y, invInertia20Y, invInertia30Y, invInertia0X1, invInertia0Y1, invInertia0Z1); + PX_TRANSPOSE_44_34(invInertia00Z, invInertia10Z, invInertia20Z, invInertia30Z, invInertia0X2, invInertia0Y2, invInertia0Z2); + + PX_TRANSPOSE_44_34(invInertia01X, invInertia11X, invInertia21X, invInertia31X, invInertia1X0, invInertia1Y0, invInertia1Z0); + PX_TRANSPOSE_44_34(invInertia01Y, invInertia11Y, invInertia21Y, invInertia31Y, invInertia1X1, invInertia1Y1, invInertia1Z1); + PX_TRANSPOSE_44_34(invInertia01Z, invInertia11Z, invInertia21Z, invInertia31Z, invInertia1X2, invInertia1Y2, invInertia1Z2); + + + const FloatV invDt = FLoad(invDtF32); + const FloatV invTotalDt = FLoad(invTotalDtF32); + const FloatV p8 = FLoad(0.8f); + const Vec4V p84 = V4Splat(p8); + const Vec4V bounceThreshold = V4Splat(FLoad(bounceThresholdF32)); + + const Vec4V invDtp8 = V4Splat(FMul(invDt, p8)); + + const Vec3V bodyFrame00p = V3LoadU(descs[0].bodyFrame0.p); + const Vec3V bodyFrame01p = V3LoadU(descs[1].bodyFrame0.p); + const Vec3V bodyFrame02p = V3LoadU(descs[2].bodyFrame0.p); + const Vec3V bodyFrame03p = V3LoadU(descs[3].bodyFrame0.p); + + Vec4V bodyFrame00p4 = Vec4V_From_Vec3V(bodyFrame00p); + Vec4V bodyFrame01p4 = Vec4V_From_Vec3V(bodyFrame01p); + Vec4V bodyFrame02p4 = Vec4V_From_Vec3V(bodyFrame02p); + Vec4V bodyFrame03p4 = Vec4V_From_Vec3V(bodyFrame03p); + + Vec4V bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ; + PX_TRANSPOSE_44_34(bodyFrame00p4, bodyFrame01p4, bodyFrame02p4, bodyFrame03p4, bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ); + + + const Vec3V bodyFrame10p = V3LoadU(descs[0].bodyFrame1.p); + const Vec3V bodyFrame11p = V3LoadU(descs[1].bodyFrame1.p); + const Vec3V bodyFrame12p = V3LoadU(descs[2].bodyFrame1.p); + const Vec3V bodyFrame13p = V3LoadU(descs[3].bodyFrame1.p); + + Vec4V bodyFrame10p4 = Vec4V_From_Vec3V(bodyFrame10p); + Vec4V bodyFrame11p4 = Vec4V_From_Vec3V(bodyFrame11p); + Vec4V bodyFrame12p4 = Vec4V_From_Vec3V(bodyFrame12p); + Vec4V bodyFrame13p4 = Vec4V_From_Vec3V(bodyFrame13p); + + Vec4V bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ; + PX_TRANSPOSE_44_34(bodyFrame10p4, bodyFrame11p4, bodyFrame12p4, bodyFrame13p4, bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ); + + + const QuatV bodyFrame00q = QuatVLoadU(&descs[0].bodyFrame0.q.x); + const QuatV bodyFrame01q = QuatVLoadU(&descs[1].bodyFrame0.q.x); + const QuatV bodyFrame02q = QuatVLoadU(&descs[2].bodyFrame0.q.x); + const QuatV bodyFrame03q = QuatVLoadU(&descs[3].bodyFrame0.q.x); + + const QuatV bodyFrame10q = QuatVLoadU(&descs[0].bodyFrame1.q.x); + const QuatV bodyFrame11q = QuatVLoadU(&descs[1].bodyFrame1.q.x); + const QuatV bodyFrame12q = QuatVLoadU(&descs[2].bodyFrame1.q.x); + const QuatV bodyFrame13q = QuatVLoadU(&descs[3].bodyFrame1.q.x); + + PxU32 frictionPatchWritebackAddrIndex0 = 0; + PxU32 frictionPatchWritebackAddrIndex1 = 0; + PxU32 frictionPatchWritebackAddrIndex2 = 0; + PxU32 frictionPatchWritebackAddrIndex3 = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + PxU32 frictionIndex0 = 0, frictionIndex1 = 0, frictionIndex2 = 0, frictionIndex3 = 0; + //PxU32 contactIndex0 = 0, contactIndex1 = 0, contactIndex2 = 0, contactIndex3 = 0; + + + //OK, we iterate through all friction patch counts in the constraint patch, building up the constraint list etc. + + PxU32 maxPatches = PxMax(descs[0].numFrictionPatches, PxMax(descs[1].numFrictionPatches, PxMax(descs[2].numFrictionPatches, descs[3].numFrictionPatches))); + + const Vec4V p1 = V4Splat(FLoad(0.1f)); + const Vec4V orthoThreshold = V4Splat(FLoad(0.70710678f)); + + + PxU32 contact0 = 0, contact1 = 0, contact2 = 0, contact3 = 0; + PxU32 patch0 = 0, patch1 = 0, patch2 = 0, patch3 = 0; + + PxU8 flag = 0; + if (hasMaxImpulse) + flag |= SolverContactHeader4::eHAS_MAX_IMPULSE; + + for (PxU32 i = 0; i= descs[0].numFrictionPatches; + const bool hasFinished1 = i >= descs[1].numFrictionPatches; + const bool hasFinished2 = i >= descs[2].numFrictionPatches; + const bool hasFinished3 = i >= descs[3].numFrictionPatches; + + + frictionIndex0 = hasFinished0 ? frictionIndex0 : descs[0].startFrictionPatchIndex + i; + frictionIndex1 = hasFinished1 ? frictionIndex1 : descs[1].startFrictionPatchIndex + i; + frictionIndex2 = hasFinished2 ? frictionIndex2 : descs[2].startFrictionPatchIndex + i; + frictionIndex3 = hasFinished3 ? frictionIndex3 : descs[3].startFrictionPatchIndex + i; + + PxU32 clampedContacts0 = hasFinished0 ? 0 : c.frictionPatchContactCounts[frictionIndex0]; + PxU32 clampedContacts1 = hasFinished1 ? 0 : c.frictionPatchContactCounts[frictionIndex1]; + PxU32 clampedContacts2 = hasFinished2 ? 0 : c.frictionPatchContactCounts[frictionIndex2]; + PxU32 clampedContacts3 = hasFinished3 ? 0 : c.frictionPatchContactCounts[frictionIndex3]; + + PxU32 firstPatch0 = c.correlationListHeads[frictionIndex0]; + PxU32 firstPatch1 = c.correlationListHeads[frictionIndex1]; + PxU32 firstPatch2 = c.correlationListHeads[frictionIndex2]; + PxU32 firstPatch3 = c.correlationListHeads[frictionIndex3]; + + const Gu::ContactPoint* contactBase0 = descs[0].contacts + c.contactPatches[firstPatch0].start; + const Gu::ContactPoint* contactBase1 = descs[1].contacts + c.contactPatches[firstPatch1].start; + const Gu::ContactPoint* contactBase2 = descs[2].contacts + c.contactPatches[firstPatch2].start; + const Gu::ContactPoint* contactBase3 = descs[3].contacts + c.contactPatches[firstPatch3].start; + + const Vec4V restitution = V4Neg(V4LoadXYZW(contactBase0->restitution, contactBase1->restitution, contactBase2->restitution, + contactBase3->restitution)); + + SolverContactHeaderStepBlock* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeaderStepBlock); + + + header->flags[0] = flags[0]; + header->flags[1] = flags[1]; + header->flags[2] = flags[2]; + header->flags[3] = flags[3]; + + header->flag = flag; + + PxU32 totalContacts = PxMax(clampedContacts0, PxMax(clampedContacts1, PxMax(clampedContacts2, clampedContacts3))); + + Vec4V* PX_RESTRICT appliedNormalForces = reinterpret_cast(ptr); + ptr += sizeof(Vec4V)*totalContacts; + + PxMemZero(appliedNormalForces, sizeof(Vec4V) * totalContacts); + + header->numNormalConstr = Ps::to8(totalContacts); + header->numNormalConstrs[0] = Ps::to8(clampedContacts0); + header->numNormalConstrs[1] = Ps::to8(clampedContacts1); + header->numNormalConstrs[2] = Ps::to8(clampedContacts2); + header->numNormalConstrs[3] = Ps::to8(clampedContacts3); + //header->sqrtInvMassA = sqrtInvMass0; + //header->sqrtInvMassB = sqrtInvMass1; + header->invMass0D0 = invMass0D0; + header->invMass1D1 = invMass1D1; + header->angDom0 = angDom0; + header->angDom1 = angDom1; + header->shapeInteraction[0] = descs[0].shapeInteraction; header->shapeInteraction[1] = descs[1].shapeInteraction; + header->shapeInteraction[2] = descs[2].shapeInteraction; header->shapeInteraction[3] = descs[3].shapeInteraction; + + Vec4V* maxImpulse = reinterpret_cast(ptr + constraintSize * totalContacts); + + header->restitution = restitution; + + Vec4V normal0 = V4LoadA(&contactBase0->normal.x); + Vec4V normal1 = V4LoadA(&contactBase1->normal.x); + Vec4V normal2 = V4LoadA(&contactBase2->normal.x); + Vec4V normal3 = V4LoadA(&contactBase3->normal.x); + + Vec4V normalX, normalY, normalZ; + PX_TRANSPOSE_44_34(normal0, normal1, normal2, normal3, normalX, normalY, normalZ); + + PX_ASSERT(ValidateVec4(normalX)); + PX_ASSERT(ValidateVec4(normalY)); + PX_ASSERT(ValidateVec4(normalZ)); + + header->normalX = normalX; + header->normalY = normalY; + header->normalZ = normalZ; + + header->maxPenBias = maxPenBias; + + const Vec4V norVel0 = V4MulAdd(normalZ, linVelT20, V4MulAdd(normalY, linVelT10, V4Mul(normalX, linVelT00))); + const Vec4V norVel1 = V4MulAdd(normalZ, linVelT21, V4MulAdd(normalY, linVelT11, V4Mul(normalX, linVelT01))); + const Vec4V relNorVel = V4Sub(norVel0, norVel1); + + //For all correlation heads - need to pull this out I think + + //OK, we have a counter for all our patches... + PxU32 finished = (PxU32(hasFinished0)) | + ((PxU32(hasFinished1)) << 1) | + ((PxU32(hasFinished2)) << 2) | + ((PxU32(hasFinished3)) << 3); + + CorrelationListIterator iter0(c, firstPatch0); + CorrelationListIterator iter1(c, firstPatch1); + CorrelationListIterator iter2(c, firstPatch2); + CorrelationListIterator iter3(c, firstPatch3); + + //PxU32 contact0, contact1, contact2, contact3; + //PxU32 patch0, patch1, patch2, patch3; + + if (!hasFinished0) + iter0.nextContact(patch0, contact0); + if (!hasFinished1) + iter1.nextContact(patch1, contact1); + if (!hasFinished2) + iter2.nextContact(patch2, contact2); + if (!hasFinished3) + iter3.nextContact(patch3, contact3); + + PxU8* p = ptr; + + PxU32 contactCount = 0; + PxU32 newFinished = + (PxU32(hasFinished0 || !iter0.hasNextContact())) | + ((PxU32(hasFinished1 || !iter1.hasNextContact())) << 1) | + ((PxU32(hasFinished2 || !iter2.hasNextContact())) << 2) | + ((PxU32(hasFinished3 || !iter3.hasNextContact())) << 3); + + while (finished != 0xf) + { + finished = newFinished; + ++contactCount; + Ps::prefetchLine(p, 384); + Ps::prefetchLine(p, 512); + Ps::prefetchLine(p, 640); + + SolverContactPointStepBlock* PX_RESTRICT solverContact = reinterpret_cast(p); + p += constraintSize; + + const Gu::ContactPoint& con0 = descs[0].contacts[c.contactPatches[patch0].start + contact0]; + const Gu::ContactPoint& con1 = descs[1].contacts[c.contactPatches[patch1].start + contact1]; + const Gu::ContactPoint& con2 = descs[2].contacts[c.contactPatches[patch2].start + contact2]; + const Gu::ContactPoint& con3 = descs[3].contacts[c.contactPatches[patch3].start + contact3]; + + //Now we need to splice these 4 contacts into a single structure + + { + Vec4V point0 = V4LoadA(&con0.point.x); + Vec4V point1 = V4LoadA(&con1.point.x); + Vec4V point2 = V4LoadA(&con2.point.x); + Vec4V point3 = V4LoadA(&con3.point.x); + + Vec4V pointX, pointY, pointZ; + PX_TRANSPOSE_44_34(point0, point1, point2, point3, pointX, pointY, pointZ); + + Vec4V cTargetVel0 = V4LoadA(&con0.targetVel.x); + Vec4V cTargetVel1 = V4LoadA(&con1.targetVel.x); + Vec4V cTargetVel2 = V4LoadA(&con2.targetVel.x); + Vec4V cTargetVel3 = V4LoadA(&con3.targetVel.x); + + Vec4V cTargetVelX, cTargetVelY, cTargetVelZ; + PX_TRANSPOSE_44_34(cTargetVel0, cTargetVel1, cTargetVel2, cTargetVel3, cTargetVelX, cTargetVelY, cTargetVelZ); + + const Vec4V separation = V4LoadXYZW(con0.separation, con1.separation, con2.separation, con3.separation); + + const Vec4V cTargetNorVel = V4MulAdd(cTargetVelX, normalX, V4MulAdd(cTargetVelY, normalY, V4Mul(cTargetVelZ, normalZ))); + + const Vec4V raX = V4Sub(pointX, bodyFrame0pX); + const Vec4V raY = V4Sub(pointY, bodyFrame0pY); + const Vec4V raZ = V4Sub(pointZ, bodyFrame0pZ); + + const Vec4V rbX = V4Sub(pointX, bodyFrame1pX); + const Vec4V rbY = V4Sub(pointY, bodyFrame1pY); + const Vec4V rbZ = V4Sub(pointZ, bodyFrame1pZ); + + /*raX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raX)), zero, raX); + raY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raY)), zero, raY); + raZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raZ)), zero, raZ); + + rbX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbX)), zero, rbX); + rbY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbY)), zero, rbY); + rbZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbZ)), zero, rbZ);*/ + + PX_ASSERT(ValidateVec4(raX)); + PX_ASSERT(ValidateVec4(raY)); + PX_ASSERT(ValidateVec4(raZ)); + + PX_ASSERT(ValidateVec4(rbX)); + PX_ASSERT(ValidateVec4(rbY)); + PX_ASSERT(ValidateVec4(rbZ)); + + + //raXn = cross(ra, normal) which = Vec3V( a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x); + + Vec4V raXnX = V4NegMulSub(raZ, normalY, V4Mul(raY, normalZ)); + Vec4V raXnY = V4NegMulSub(raX, normalZ, V4Mul(raZ, normalX)); + Vec4V raXnZ = V4NegMulSub(raY, normalX, V4Mul(raX, normalY)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + + PX_ASSERT(ValidateVec4(delAngVel0X)); + PX_ASSERT(ValidateVec4(delAngVel0Y)); + PX_ASSERT(ValidateVec4(delAngVel0Z)); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0X, delAngVel0X, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0Z, delAngVel0Z))); + const Vec4V relAngVel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4Mul(raXnX, angVelT00))); + + Vec4V unitResponse = V4MulAdd(invMass0D0, angDom0, dotDelAngVel0); + Vec4V vrel0 = V4Add(norVel0, relAngVel0); + Vec4V vrel1 = norVel1; + + Vec4V delAngVel1X = zero; + Vec4V delAngVel1Y = zero; + Vec4V delAngVel1Z = zero; + + //The dynamic-only parts - need to if-statement these up. A branch here shouldn't cost us too much + if (isDynamic) + { + Vec4V rbXnX = V4NegMulSub(rbZ, normalY, V4Mul(rbY, normalZ)); + Vec4V rbXnY = V4NegMulSub(rbX, normalZ, V4Mul(rbZ, normalX)); + Vec4V rbXnZ = V4NegMulSub(rbY, normalX, V4Mul(rbX, normalY)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + delAngVel1X = V4Mul(invInertia1X0, rbXnX); + delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + PX_ASSERT(ValidateVec4(delAngVel1X)); + PX_ASSERT(ValidateVec4(delAngVel1Y)); + PX_ASSERT(ValidateVec4(delAngVel1Z)); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1X, delAngVel1X, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1Z, delAngVel1Z))); + const Vec4V relAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + unitResponse = V4Add(unitResponse, resp1); + + vrel1 = V4Add(vrel1, relAngVel1); + + //These are for dynamic-only contacts. + + + } + else if (hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, normalY, V4Mul(rbY, normalZ)); + const Vec4V rbXnY = V4NegMulSub(rbX, normalZ, V4Mul(rbZ, normalX)); + const Vec4V rbXnZ = V4NegMulSub(rbY, normalX, V4Mul(rbX, normalY)); + + const Vec4V relAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + + vrel1 = V4Add(vrel1, relAngVel1); + } + + Vec4V vrel = V4Sub(vrel0, vrel1); + + solverContact->rbXnI[0] = delAngVel1X; + solverContact->rbXnI[1] = delAngVel1Y; + solverContact->rbXnI[2] = delAngVel1Z; + + const Vec4V velMultiplier = V4Sel(V4IsGrtr(unitResponse, zero), V4Recip(unitResponse), zero); + + const Vec4V penetration = V4Sub(separation, restDistance); + //const Vec4V penInvDtPt8 = V4Max(maxPenBias, V4Scale(penetration, invDtp8)); + //Vec4V scaledBias = invDtPt8; + + const Vec4V penetrationInvDt = V4Scale(penetration, invTotalDt); + + const BoolV isGreater2 = BAnd(BAnd(V4IsGrtr(zero, restitution), V4IsGrtr(bounceThreshold, vrel)), + V4IsGrtr(V4Neg(vrel), penetrationInvDt)); + + + const Vec4V scaledBias = V4Neg(invDtp8); + + Vec4V targetVelocity = V4NegMulSub(vrel0, kinematicScale0, V4MulAdd(vrel1, kinematicScale1, V4Sel(isGreater2, V4Mul(vrel, restitution), zero))); + + + + //Vec4V biasedErr = V4Sel(isGreater2, targetVelocity, scaledBias); + //Vec4V biasedErr = V4Add(targetVelocity, scaledBias); + + //biasedErr = V4NegMulSub(V4Sub(vrel, cTargetNorVel), velMultiplier, biasedErr); + + //These values are present for static and dynamic contacts + solverContact->raXnI[0] = delAngVel0X; + solverContact->raXnI[1] = delAngVel0Y; + solverContact->raXnI[2] = delAngVel0Z; + solverContact->velMultiplier = velMultiplier; + solverContact->targetVelocity = V4Add(cTargetNorVel, targetVelocity); + solverContact->separation = penetration; + solverContact->biasCoefficient = scaledBias; + + if (hasMaxImpulse) + { + maxImpulse[contactCount - 1] = V4Merge(FLoad(con0.maxImpulse), FLoad(con1.maxImpulse), FLoad(con2.maxImpulse), + FLoad(con3.maxImpulse)); + } + } + if (!(finished & 0x1)) + { + iter0.nextContact(patch0, contact0); + newFinished |= PxU32(!iter0.hasNextContact()); + } + + if (!(finished & 0x2)) + { + iter1.nextContact(patch1, contact1); + newFinished |= (PxU32(!iter1.hasNextContact()) << 1); + } + + if (!(finished & 0x4)) + { + iter2.nextContact(patch2, contact2); + newFinished |= (PxU32(!iter2.hasNextContact()) << 2); + } + + if (!(finished & 0x8)) + { + iter3.nextContact(patch3, contact3); + newFinished |= (PxU32(!iter3.hasNextContact()) << 3); + } + } + ptr = p; + if (hasMaxImpulse) + { + ptr += sizeof(Vec4V) * totalContacts; + } + + //OK...friction time :-) + + Vec4V maxImpulseScale = V4One(); + { + const Vec4V staticFriction = V4LoadXYZW(contactBase0->staticFriction, contactBase1->staticFriction, + contactBase2->staticFriction, contactBase3->staticFriction); + + const Vec4V dynamicFriction = V4LoadXYZW(contactBase0->dynamicFriction, contactBase1->dynamicFriction, + contactBase2->dynamicFriction, contactBase3->dynamicFriction); + + PX_ASSERT(totalContacts == contactCount); + header->dynamicFriction = dynamicFriction; + header->staticFriction = staticFriction; + + const FrictionPatch& frictionPatch0 = c.frictionPatches[frictionIndex0]; + const FrictionPatch& frictionPatch1 = c.frictionPatches[frictionIndex1]; + const FrictionPatch& frictionPatch2 = c.frictionPatches[frictionIndex2]; + const FrictionPatch& frictionPatch3 = c.frictionPatches[frictionIndex3]; + + PxU32 anchorCount0 = frictionPatch0.anchorCount; + PxU32 anchorCount1 = frictionPatch1.anchorCount; + PxU32 anchorCount2 = frictionPatch2.anchorCount; + PxU32 anchorCount3 = frictionPatch3.anchorCount; + + PxU32 clampedAnchorCount0 = hasFinished0 || (contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount0; + PxU32 clampedAnchorCount1 = hasFinished1 || (contactBase1->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount1; + PxU32 clampedAnchorCount2 = hasFinished2 || (contactBase2->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount2; + PxU32 clampedAnchorCount3 = hasFinished3 || (contactBase3->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount3; + + const PxU32 maxAnchorCount = PxMax(clampedAnchorCount0, PxMax(clampedAnchorCount1, PxMax(clampedAnchorCount2, clampedAnchorCount3))); + + //if(clampedAnchorCount0 != clampedAnchorCount1 || clampedAnchorCount0 != clampedAnchorCount2 || clampedAnchorCount0 != clampedAnchorCount3) + // Ps::debugBreak(); + + + //const bool haveFriction = maxAnchorCount != 0; + header->numFrictionConstr = Ps::to8(maxAnchorCount * 2); + header->numFrictionConstrs[0] = Ps::to8(clampedAnchorCount0 * 2); + header->numFrictionConstrs[1] = Ps::to8(clampedAnchorCount1 * 2); + header->numFrictionConstrs[2] = Ps::to8(clampedAnchorCount2 * 2); + header->numFrictionConstrs[3] = Ps::to8(clampedAnchorCount3 * 2); + + //KS - TODO - extend this if needed + header->type = Ps::to8(DY_SC_TYPE_BLOCK_RB_CONTACT); + + if (maxAnchorCount) + { + const BoolV cond = V4IsGrtr(orthoThreshold, V4Abs(normalX)); + + const Vec4V t0FallbackX = V4Sel(cond, zero, V4Neg(normalY)); + const Vec4V t0FallbackY = V4Sel(cond, V4Neg(normalZ), normalX); + const Vec4V t0FallbackZ = V4Sel(cond, normalY, zero); + + //const Vec4V dotNormalVrel = V4MulAdd(normalZ, vrelZ, V4MulAdd(normalY, vrelY, V4Mul(normalX, vrelX))); + const Vec4V vrelSubNorVelX = V4NegMulSub(normalX, relNorVel, vrelX); + const Vec4V vrelSubNorVelY = V4NegMulSub(normalY, relNorVel, vrelY); + const Vec4V vrelSubNorVelZ = V4NegMulSub(normalZ, relNorVel, vrelZ); + + const Vec4V lenSqvrelSubNorVelZ = V4MulAdd(vrelSubNorVelX, vrelSubNorVelX, V4MulAdd(vrelSubNorVelY, vrelSubNorVelY, V4Mul(vrelSubNorVelZ, vrelSubNorVelZ))); + + const BoolV bcon2 = V4IsGrtr(lenSqvrelSubNorVelZ, p1); + + Vec4V t0X = V4Sel(bcon2, vrelSubNorVelX, t0FallbackX); + Vec4V t0Y = V4Sel(bcon2, vrelSubNorVelY, t0FallbackY); + Vec4V t0Z = V4Sel(bcon2, vrelSubNorVelZ, t0FallbackZ); + + + //Now normalize this... + const Vec4V recipLen = V4Rsqrt(V4MulAdd(t0Z, t0Z, V4MulAdd(t0Y, t0Y, V4Mul(t0X, t0X)))); + + t0X = V4Mul(t0X, recipLen); + t0Y = V4Mul(t0Y, recipLen); + t0Z = V4Mul(t0Z, recipLen); + + Vec4V t1X = V4NegMulSub(normalZ, t0Y, V4Mul(normalY, t0Z)); + Vec4V t1Y = V4NegMulSub(normalX, t0Z, V4Mul(normalZ, t0X)); + Vec4V t1Z = V4NegMulSub(normalY, t0X, V4Mul(normalX, t0Y)); + + PX_ASSERT((uintptr_t(descs[0].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[1].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[2].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[3].frictionPtr) & 0xF) == 0); + + + PxU8* PX_RESTRICT writeback0 = descs[0].frictionPtr + frictionPatchWritebackAddrIndex0 * sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback1 = descs[1].frictionPtr + frictionPatchWritebackAddrIndex1 * sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback2 = descs[2].frictionPtr + frictionPatchWritebackAddrIndex2 * sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback3 = descs[3].frictionPtr + frictionPatchWritebackAddrIndex3 * sizeof(FrictionPatch); + + PxU32 index0 = 0, index1 = 0, index2 = 0, index3 = 0; + + header->broken = bFalse; + header->frictionBrokenWritebackByte[0] = writeback0; + header->frictionBrokenWritebackByte[1] = writeback1; + header->frictionBrokenWritebackByte[2] = writeback2; + header->frictionBrokenWritebackByte[3] = writeback3; + + + /*header->frictionNormal[0][0] = t0X; + header->frictionNormal[0][1] = t0Y; + header->frictionNormal[0][2] = t0Z; + + header->frictionNormal[1][0] = t1X; + header->frictionNormal[1][1] = t1Y; + header->frictionNormal[1][2] = t1Z;*/ + + Vec4V* PX_RESTRICT appliedForces = reinterpret_cast(ptr); + ptr += sizeof(Vec4V)*header->numFrictionConstr; + + PxMemZero(appliedForces, sizeof(Vec4V) * header->numFrictionConstr); + + for (PxU32 j = 0; j < maxAnchorCount; j++) + { + Ps::prefetchLine(ptr, 384); + Ps::prefetchLine(ptr, 512); + Ps::prefetchLine(ptr, 640); + SolverContactFrictionStepBlock* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionSize; + SolverContactFrictionStepBlock* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionSize; + + index0 = j < clampedAnchorCount0 ? j : index0; + index1 = j < clampedAnchorCount1 ? j : index1; + index2 = j < clampedAnchorCount2 ? j : index2; + index3 = j < clampedAnchorCount3 ? j : index3; + + if (j >= clampedAnchorCount0) + maxImpulseScale = V4SetX(maxImpulseScale, fZero); + if (j >= clampedAnchorCount1) + maxImpulseScale = V4SetY(maxImpulseScale, fZero); + if (j >= clampedAnchorCount2) + maxImpulseScale = V4SetZ(maxImpulseScale, fZero); + if (j >= clampedAnchorCount3) + maxImpulseScale = V4SetW(maxImpulseScale, fZero); + + t0X = V4Mul(maxImpulseScale, t0X); + t0Y = V4Mul(maxImpulseScale, t0Y); + t0Z = V4Mul(maxImpulseScale, t0Z); + + t1X = V4Mul(maxImpulseScale, t1X); + t1Y = V4Mul(maxImpulseScale, t1Y); + t1Z = V4Mul(maxImpulseScale, t1Z); + + + Vec3V body0Anchor0 = V3LoadU(frictionPatch0.body0Anchors[index0]); + Vec3V body0Anchor1 = V3LoadU(frictionPatch1.body0Anchors[index1]); + Vec3V body0Anchor2 = V3LoadU(frictionPatch2.body0Anchors[index2]); + Vec3V body0Anchor3 = V3LoadU(frictionPatch3.body0Anchors[index3]); + + Vec4V ra0 = Vec4V_From_Vec3V(QuatRotate(bodyFrame00q, body0Anchor0)); + Vec4V ra1 = Vec4V_From_Vec3V(QuatRotate(bodyFrame01q, body0Anchor1)); + Vec4V ra2 = Vec4V_From_Vec3V(QuatRotate(bodyFrame02q, body0Anchor2)); + Vec4V ra3 = Vec4V_From_Vec3V(QuatRotate(bodyFrame03q, body0Anchor3)); + + Vec4V raX, raY, raZ; + PX_TRANSPOSE_44_34(ra0, ra1, ra2, ra3, raX, raY, raZ); + + /*raX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raX)), zero, raX); + raY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raY)), zero, raY); + raZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raZ)), zero, raZ);*/ + + const Vec4V raWorldX = V4Add(raX, bodyFrame0pX); + const Vec4V raWorldY = V4Add(raY, bodyFrame0pY); + const Vec4V raWorldZ = V4Add(raZ, bodyFrame0pZ); + + Vec3V body1Anchor0 = V3LoadU(frictionPatch0.body1Anchors[index0]); + Vec3V body1Anchor1 = V3LoadU(frictionPatch1.body1Anchors[index1]); + Vec3V body1Anchor2 = V3LoadU(frictionPatch2.body1Anchors[index2]); + Vec3V body1Anchor3 = V3LoadU(frictionPatch3.body1Anchors[index3]); + + Vec4V rb0 = Vec4V_From_Vec3V(QuatRotate(bodyFrame10q, body1Anchor0)); + Vec4V rb1 = Vec4V_From_Vec3V(QuatRotate(bodyFrame11q, body1Anchor1)); + Vec4V rb2 = Vec4V_From_Vec3V(QuatRotate(bodyFrame12q, body1Anchor2)); + Vec4V rb3 = Vec4V_From_Vec3V(QuatRotate(bodyFrame13q, body1Anchor3)); + + Vec4V rbX, rbY, rbZ; + PX_TRANSPOSE_44_34(rb0, rb1, rb2, rb3, rbX, rbY, rbZ); + + /*rbX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbX)), zero, rbX); + rbY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbY)), zero, rbY); + rbZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbZ)), zero, rbZ);*/ + + const Vec4V rbWorldX = V4Add(rbX, bodyFrame1pX); + const Vec4V rbWorldY = V4Add(rbY, bodyFrame1pY); + const Vec4V rbWorldZ = V4Add(rbZ, bodyFrame1pZ); + + Vec4V errorX = V4Sub(raWorldX, rbWorldX); + Vec4V errorY = V4Sub(raWorldY, rbWorldY); + Vec4V errorZ = V4Sub(raWorldZ, rbWorldZ); + + /*errorX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorX)), zero, errorX); + errorY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorY)), zero, errorY); + errorZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorZ)), zero, errorZ);*/ + + //KS - todo - get this working with per-point friction + PxU32 contactIndex0 = c.contactID[frictionIndex0][index0]; + PxU32 contactIndex1 = c.contactID[frictionIndex1][index1]; + PxU32 contactIndex2 = c.contactID[frictionIndex2][index2]; + PxU32 contactIndex3 = c.contactID[frictionIndex3][index3]; + + //Ensure that the ocntact indices are valid + PX_ASSERT(contactIndex0 == 0xffff || contactIndex0 < descs[0].numContacts); + PX_ASSERT(contactIndex1 == 0xffff || contactIndex1 < descs[1].numContacts); + PX_ASSERT(contactIndex2 == 0xffff || contactIndex2 < descs[2].numContacts); + PX_ASSERT(contactIndex3 == 0xffff || contactIndex3 < descs[3].numContacts); + + Vec4V targetVel0 = V4LoadA(contactIndex0 == 0xFFFF ? &contactBase0->targetVel.x : &descs[0].contacts[contactIndex0].targetVel.x); + Vec4V targetVel1 = V4LoadA(contactIndex1 == 0xFFFF ? &contactBase0->targetVel.x : &descs[1].contacts[contactIndex1].targetVel.x); + Vec4V targetVel2 = V4LoadA(contactIndex2 == 0xFFFF ? &contactBase0->targetVel.x : &descs[2].contacts[contactIndex2].targetVel.x); + Vec4V targetVel3 = V4LoadA(contactIndex3 == 0xFFFF ? &contactBase0->targetVel.x : &descs[3].contacts[contactIndex3].targetVel.x); + + Vec4V targetVelX, targetVelY, targetVelZ; + PX_TRANSPOSE_44_34(targetVel0, targetVel1, targetVel2, targetVel3, targetVelX, targetVelY, targetVelZ); + + + { + Vec4V raXnX = V4NegMulSub(raZ, t0Y, V4Mul(raY, t0Z)); + Vec4V raXnY = V4NegMulSub(raX, t0Z, V4Mul(raZ, t0X)); + Vec4V raXnZ = V4NegMulSub(raY, t0X, V4Mul(raX, t0Y)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + + Vec4V resp = V4MulAdd(dotDelAngVel0, angDom0, invMass0D0); + + const Vec4V tVel0 = V4MulAdd(t0Z, linVelT20, V4MulAdd(t0Y, linVelT10, V4Mul(t0X, linVelT00))); + Vec4V vrel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4MulAdd(raXnX, angVelT00, tVel0))); + + Vec4V delAngVel1X = zero; + Vec4V delAngVel1Y = zero; + Vec4V delAngVel1Z = zero; + + Vec4V vrel1 = zero; + + if (isDynamic) + { + Vec4V rbXnX = V4NegMulSub(rbZ, t0Y, V4Mul(rbY, t0Z)); + Vec4V rbXnY = V4NegMulSub(rbX, t0Z, V4Mul(rbZ, t0X)); + Vec4V rbXnZ = V4NegMulSub(rbY, t0X, V4Mul(rbX, t0Y)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + delAngVel1X = V4Mul(invInertia1X0, rbXnX); + delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + resp = V4Add(resp, resp1); + + const Vec4V tVel1 = V4MulAdd(t0Z, linVelT21, V4MulAdd(t0Y, linVelT11, V4Mul(t0X, linVelT01))); + vrel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + } + else if (hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, t0Y, V4Mul(rbY, t0Z)); + const Vec4V rbXnY = V4NegMulSub(rbX, t0Z, V4Mul(rbZ, t0X)); + const Vec4V rbXnZ = V4NegMulSub(rbY, t0X, V4Mul(rbX, t0Y)); + + const Vec4V tVel1 = V4MulAdd(t0Z, linVelT21, V4MulAdd(t0Y, linVelT11, V4Mul(t0X, linVelT01))); + vrel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + } + + f0->rbXnI[0] = delAngVel1X; + f0->rbXnI[1] = delAngVel1Y; + f0->rbXnI[2] = delAngVel1Z; + + const Vec4V velMultiplier = V4Mul(maxImpulseScale, V4Sel(V4IsGrtr(resp, zero), V4Div(p84, resp), zero)); + + Vec4V error = V4MulAdd(t0Z, errorZ, V4MulAdd(t0Y, errorY, V4Mul(t0X, errorX))); + + Vec4V targetVel = V4NegMulSub(vrel0, kinematicScale0, V4MulAdd(vrel1, kinematicScale1, V4MulAdd(t0Z, targetVelZ, V4MulAdd(t0Y, targetVelY, V4Mul(t0X, targetVelX))))); + + f0->normal[0] = t0X; + f0->normal[1] = t0Y; + f0->normal[2] = t0Z; + f0->raXnI[0] = delAngVel0X; + f0->raXnI[1] = delAngVel0Y; + f0->raXnI[2] = delAngVel0Z; + f0->error = error; + f0->velMultiplier = velMultiplier; + f0->biasCoefficient = V4Splat(invDt); + f0->targetVel = targetVel; + } + + { + Vec4V raXnX = V4NegMulSub(raZ, t1Y, V4Mul(raY, t1Z)); + Vec4V raXnY = V4NegMulSub(raX, t1Z, V4Mul(raZ, t1X)); + Vec4V raXnZ = V4NegMulSub(raY, t1X, V4Mul(raX, t1Y)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + + Vec4V resp = V4MulAdd(dotDelAngVel0, angDom0, invMass0D0); + + const Vec4V tVel0 = V4MulAdd(t1Z, linVelT20, V4MulAdd(t1Y, linVelT10, V4Mul(t1X, linVelT00))); + Vec4V vrel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4MulAdd(raXnX, angVelT00, tVel0))); + + Vec4V delAngVel1X = zero; + Vec4V delAngVel1Y = zero; + Vec4V delAngVel1Z = zero; + + Vec4V vrel1 = zero; + + if (isDynamic) + { + + Vec4V rbXnX = V4NegMulSub(rbZ, t1Y, V4Mul(rbY, t1Z)); + Vec4V rbXnY = V4NegMulSub(rbX, t1Z, V4Mul(rbZ, t1X)); + Vec4V rbXnZ = V4NegMulSub(rbY, t1X, V4Mul(rbX, t1Y)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + delAngVel1X = V4Mul(invInertia1X0, rbXnX); + delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + resp = V4Add(resp, resp1); + + + + const Vec4V tVel1 = V4MulAdd(t1Z, linVelT21, V4MulAdd(t1Y, linVelT11, V4Mul(t1X, linVelT01))); + vrel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + } + else if (hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, t1Y, V4Mul(rbY, t1Z)); + const Vec4V rbXnY = V4NegMulSub(rbX, t1Z, V4Mul(rbZ, t1X)); + const Vec4V rbXnZ = V4NegMulSub(rbY, t1X, V4Mul(rbX, t1Y)); + + const Vec4V tVel1 = V4MulAdd(t1Z, linVelT21, V4MulAdd(t1Y, linVelT11, V4Mul(t1X, linVelT01))); + vrel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + } + + f1->rbXnI[0] = delAngVel1X; + f1->rbXnI[1] = delAngVel1Y; + f1->rbXnI[2] = delAngVel1Z; + + + const Vec4V velMultiplier = V4Mul(maxImpulseScale, V4Sel(V4IsGrtr(resp, zero), V4Div(p84, resp), zero)); + + Vec4V error = V4MulAdd(t1Z, errorZ, V4MulAdd(t1Y, errorY, V4Mul(t1X, errorX))); + + Vec4V targetVel = V4NegMulSub(vrel0, kinematicScale0, V4MulAdd(vrel1, kinematicScale1, V4MulAdd(t1Z, targetVelZ, V4MulAdd(t1Y, targetVelY, V4Mul(t1X, targetVelX))))); + + f1->normal[0] = t1X; + f1->normal[1] = t1Y; + f1->normal[2] = t1Z; + f1->raXnI[0] = delAngVel0X; + f1->raXnI[1] = delAngVel0Y; + f1->raXnI[2] = delAngVel0Z; + f1->error = error; + f1->velMultiplier = velMultiplier; + f1->targetVel = targetVel; + f1->biasCoefficient = V4Splat(invDt); + } + } + + frictionPatchWritebackAddrIndex0++; + frictionPatchWritebackAddrIndex1++; + frictionPatchWritebackAddrIndex2++; + frictionPatchWritebackAddrIndex3++; + } + } + } +} + + + +PX_FORCE_INLINE void computeBlockStreamFrictionByteSizes(const CorrelationBuffer& c, + PxU32& _frictionPatchByteSize, PxU32& _numFrictionPatches, + PxU32 frictionPatchStartIndex, PxU32 frictionPatchEndIndex) +{ + // PT: use local vars to remove LHS + PxU32 numFrictionPatches = 0; + + for (PxU32 i = frictionPatchStartIndex; i < frictionPatchEndIndex; i++) + { + //Friction patches. + if (c.correlationListHeads[i] != CorrelationBuffer::LIST_END) + numFrictionPatches++; + } + PxU32 frictionPatchByteSize = numFrictionPatches * sizeof(FrictionPatch); + + _numFrictionPatches = numFrictionPatches; + + //16-byte alignment. + _frictionPatchByteSize = ((frictionPatchByteSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_frictionPatchByteSize & 0x0f)); +} + + +static bool reserveFrictionBlockStreams(const CorrelationBuffer& c, PxConstraintAllocator& constraintAllocator, PxU32 frictionPatchStartIndex, PxU32 frictionPatchEndIndex, + FrictionPatch*& _frictionPatches, + PxU32& numFrictionPatches) +{ + + //From frictionPatchStream we just need to reserve a single buffer. + PxU32 frictionPatchByteSize = 0; + //Compute the sizes of all the buffers. + + computeBlockStreamFrictionByteSizes(c, frictionPatchByteSize, numFrictionPatches, frictionPatchStartIndex, frictionPatchEndIndex); + + FrictionPatch* frictionPatches = NULL; + //If the constraint block reservation didn't fail then reserve the friction buffer too. + if (frictionPatchByteSize > 0) + { + frictionPatches = reinterpret_cast(constraintAllocator.reserveFrictionData(frictionPatchByteSize)); + + if (0 == frictionPatches || (reinterpret_cast(-1)) == frictionPatches) + { + if (0 == frictionPatches) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of friction data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + frictionPatches = NULL; + } + } + } + + _frictionPatches = frictionPatches; + + //Return true if neither of the two block reservations failed. + return (0 == frictionPatchByteSize || frictionPatches); +} + +//The persistent friction patch correlation/allocation will already have happenned as this is per-pair. +//This function just computes the size of the combined solve data. +void computeBlockStreamByteSizes4(PxTGSSolverContactDesc* descs, + PxU32& _solverConstraintByteSize, PxU32* _axisConstraintCount, + const CorrelationBuffer& c) +{ + PX_ASSERT(0 == _solverConstraintByteSize); + + PxU32 maxPatches = 0; + PxU32 maxFrictionPatches = 0; + PxU32 maxContactCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxU32 maxFrictionCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxMemZero(maxContactCount, sizeof(maxContactCount)); + PxMemZero(maxFrictionCount, sizeof(maxFrictionCount)); + bool hasMaxImpulse = false; + + for (PxU32 a = 0; a < 4; ++a) + { + PxU32 axisConstraintCount = 0; + hasMaxImpulse = hasMaxImpulse || descs[a].hasMaxImpulse; + for (PxU32 i = 0; i < descs[a].numFrictionPatches; i++) + { + PxU32 ind = i + descs[a].startFrictionPatchIndex; + + const FrictionPatch& frictionPatch = c.frictionPatches[ind]; + + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0 + && frictionPatch.anchorCount != 0; + //Solver constraint data. + if (c.frictionPatchContactCounts[ind] != 0) + { + maxContactCount[i] = PxMax(c.frictionPatchContactCounts[ind], maxContactCount[i]); + axisConstraintCount += c.frictionPatchContactCounts[ind]; + + if (haveFriction) + { + const PxU32 fricCount = PxU32(c.frictionPatches[ind].anchorCount) * 2; + maxFrictionCount[i] = PxMax(fricCount, maxFrictionCount[i]); + axisConstraintCount += fricCount; + } + } + } + maxPatches = PxMax(descs[a].numFrictionPatches, maxPatches); + _axisConstraintCount[a] = axisConstraintCount; + } + + for (PxU32 a = 0; a < maxPatches; ++a) + { + if (maxFrictionCount[a] > 0) + maxFrictionPatches++; + } + + + PxU32 totalContacts = 0, totalFriction = 0; + for (PxU32 a = 0; a < maxPatches; ++a) + { + totalContacts += maxContactCount[a]; + totalFriction += maxFrictionCount[a]; + } + + //OK, we have a given number of friction patches, contact points and friction constraints so we can calculate how much memory we need + + //Body 2 is considered static if it is either *not dynamic* or *kinematic* + + /*bool hasDynamicBody = false; + for (PxU32 a = 0; a < 4; ++a) + { + hasDynamicBody = hasDynamicBody || ((descs[a].bodyState1 == PxSolverContactDesc::eDYNAMIC_BODY)); + } + + + const bool isStatic = !hasDynamicBody;*/ + + const PxU32 headerSize = sizeof(SolverContactHeaderStepBlock) * maxPatches; + //PxU32 constraintSize = isStatic ? (sizeof(SolverContactBatchPointBase4) * totalContacts) + (sizeof(SolverContactFrictionBase4) * totalFriction) : + // (sizeof(SolverContactBatchPointDynamic4) * totalContacts) + (sizeof(SolverContactFrictionDynamic4) * totalFriction); + + PxU32 constraintSize = (sizeof(SolverContactPointStepBlock) * totalContacts) + (sizeof(SolverContactFrictionStepBlock) * totalFriction); + + //Space for the appliedForce buffer + constraintSize += sizeof(Vec4V)*(totalContacts + totalFriction); + + //If we have max impulse, reserve a buffer for it + if (hasMaxImpulse) + constraintSize += sizeof(Ps::aos::Vec4V) * totalContacts; + + _solverConstraintByteSize = ((constraintSize + headerSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); +} + +static SolverConstraintPrepState::Enum reserveBlockStreams4(PxTGSSolverContactDesc* descs, Dy::CorrelationBuffer& c, + PxU8*& solverConstraint, PxU32* axisConstraintCount, + PxU32& solverConstraintByteSize, + PxConstraintAllocator& constraintAllocator) +{ + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(0 == solverConstraintByteSize); + + //Compute the sizes of all the buffers. + computeBlockStreamByteSizes4(descs, + solverConstraintByteSize, axisConstraintCount, + c); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if (constraintBlockByteSize > 0) + { + if ((constraintBlockByteSize + 16u) > 16384) + return SolverConstraintPrepState::eUNBATCHABLE; + + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if (0 == constraintBlock || (reinterpret_cast(-1)) == constraintBlock) + { + if (0 == constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock = NULL; + } + } + } + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if (0 == constraintBlockByteSize || constraintBlock) + { + if (solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0 == (uintptr_t(solverConstraint) & 0x0f)); + } + } + + return ((0 == constraintBlockByteSize || constraintBlock)) ? SolverConstraintPrepState::eSUCCESS : SolverConstraintPrepState::eOUT_OF_MEMORY; +} + + + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Step( + Dy::CorrelationBuffer& c, + PxTGSSolverContactDesc* blockDescs, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + PX_ALIGN(16, PxReal invMassScale0[4]); + PX_ALIGN(16, PxReal invMassScale1[4]); + PX_ALIGN(16, PxReal invInertiaScale0[4]); + PX_ALIGN(16, PxReal invInertiaScale1[4]); + + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + for (PxU32 a = 0; a < 4; ++a) + { + PxTGSSolverContactDesc& blockDesc = blockDescs[a]; + + invMassScale0[a] = blockDesc.mInvMassScales.linear0; + invMassScale1[a] = blockDesc.mInvMassScales.linear1; + invInertiaScale0[a] = blockDesc.mInvMassScales.angular0; + invInertiaScale1[a] = blockDesc.mInvMassScales.angular1; + + blockDesc.startFrictionPatchIndex = c.frictionPatchCount; + if (!(blockDesc.disableStrongFriction)) + { + bool valid = getFrictionPatches(c, blockDesc.frictionPtr, blockDesc.frictionCount, + blockDesc.bodyFrame0, blockDesc.bodyFrame1, correlationDistance); + if (!valid) + return SolverConstraintPrepState::eUNBATCHABLE; + } + //Create the contact patches + blockDesc.startContactPatchIndex = c.contactPatchCount; + if (!createContactPatches(c, blockDesc.contacts, blockDesc.numContacts, PXC_SAME_NORMAL)) + return SolverConstraintPrepState::eUNBATCHABLE; + blockDesc.numContactPatches = PxU16(c.contactPatchCount - blockDesc.startContactPatchIndex); + + bool overflow = correlatePatches(c, blockDesc.contacts, blockDesc.bodyFrame0, blockDesc.bodyFrame1, PXC_SAME_NORMAL, + blockDesc.startContactPatchIndex, blockDesc.startFrictionPatchIndex); + + if (overflow) + return SolverConstraintPrepState::eUNBATCHABLE; + + growPatches(c, blockDesc.contacts, blockDesc.bodyFrame0, blockDesc.bodyFrame1, correlationDistance, blockDesc.startFrictionPatchIndex, + frictionOffsetThreshold + blockDescs[a].restDistance); + + //Remove the empty friction patches - do we actually need to do this? + for (PxU32 p = c.frictionPatchCount; p > blockDesc.startFrictionPatchIndex; --p) + { + if (c.correlationListHeads[p - 1] == 0xffff) + { + //We have an empty patch...need to bin this one... + for (PxU32 p2 = p; p2 < c.frictionPatchCount; ++p2) + { + c.correlationListHeads[p2 - 1] = c.correlationListHeads[p2]; + c.frictionPatchContactCounts[p2 - 1] = c.frictionPatchContactCounts[p2]; + } + c.frictionPatchCount--; + } + } + + PxU32 numFricPatches = c.frictionPatchCount - blockDesc.startFrictionPatchIndex; + blockDesc.numFrictionPatches = numFricPatches; + } + + FrictionPatch* frictionPatchArray[4]; + PxU32 frictionPatchCounts[4]; + + for (PxU32 a = 0; a < 4; ++a) + { + PxTGSSolverContactDesc& blockDesc = blockDescs[a]; + + const bool successfulReserve = reserveFrictionBlockStreams(c, constraintAllocator, blockDesc.startFrictionPatchIndex, blockDesc.numFrictionPatches + blockDesc.startFrictionPatchIndex, + frictionPatchArray[a], + frictionPatchCounts[a]); + + //KS - TODO - how can we recover if we failed to allocate this memory? + if (!successfulReserve) + { + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + } + //At this point, all the friction data has been calculated, the correlation has been done. Provided this was all successful, + //we are ready to create the batched constraints + + PxU8* solverConstraint = NULL; + PxU32 solverConstraintByteSize = 0; + + + + { + PxU32 axisConstraintCount[4]; + SolverConstraintPrepState::Enum state = reserveBlockStreams4(blockDescs, c, + solverConstraint, axisConstraintCount, + solverConstraintByteSize, + constraintAllocator); + + if (state != SolverConstraintPrepState::eSUCCESS) + return state; + + + for (PxU32 a = 0; a < 4; ++a) + { + + FrictionPatch* frictionPatches = frictionPatchArray[a]; + + PxTGSSolverContactDesc& blockDesc = blockDescs[a]; + PxTGSSolverConstraintDesc& desc = *blockDesc.desc; + blockDesc.frictionPtr = reinterpret_cast(frictionPatches); + blockDesc.frictionCount = Ps::to8(frictionPatchCounts[a]); + + //Initialise friction buffer. + if (frictionPatches) + { + // PT: TODO: revisit this... not very satisfying + //const PxU32 maxSize = numFrictionPatches*sizeof(FrictionPatch); + Ps::prefetchLine(frictionPatches); + Ps::prefetchLine(frictionPatches, 128); + Ps::prefetchLine(frictionPatches, 256); + + for (PxU32 i = 0; i(solverConstraint + solverConstraintByteSize)) = 0; + } + return SolverConstraintPrepState::eSUCCESS; +} + + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Step( + PxsContactManagerOutput** cmOutputs, + ThreadContext& threadContext, + PxTGSSolverContactDesc* blockDescs, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + + for (PxU32 a = 0; a < 4; ++a) + { + blockDescs[a].desc->constraintLengthOver16 = 0; + } + + //PX_ASSERT(cmOutputs[0]->nbContacts && cmOutputs[1]->nbContacts && cmOutputs[2]->nbContacts && cmOutputs[3]->nbContacts); + + + Gu::ContactBuffer& buffer = threadContext.mContactBuffer; + + buffer.count = 0; + + //PxTransform idt = PxTransform(PxIdentity); + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + + for (PxU32 a = 0; a < 4; ++a) + { + PxTGSSolverContactDesc& blockDesc = blockDescs[a]; + PxTGSSolverConstraintDesc& desc = *blockDesc.desc; + + //blockDesc.startContactIndex = buffer.count; + blockDesc.contacts = buffer.contacts + buffer.count; + + Ps::prefetchLine(desc.bodyA); + Ps::prefetchLine(desc.bodyB); + + + //Unbatchable if we have (a) too many contacts or (b) torsional friction enabled - it just seems easier to handle this on an individual contact basis because it is expected to + //be used relatively rarely + if ((buffer.count + cmOutputs[a]->nbContacts) > 64 || (blockDesc.torsionalPatchRadius != 0.f || blockDesc.minTorsionalPatchRadius != 0.f) ) + { + return SolverConstraintPrepState::eUNBATCHABLE; + } + + bool hasMaxImpulse = false; + bool hasTargetVelocity = false; + + //OK...do the correlation here as well... + Ps::prefetchLine(blockDescs[a].frictionPtr); + Ps::prefetchLine(blockDescs[a].frictionPtr, 64); + Ps::prefetchLine(blockDescs[a].frictionPtr, 128); + + if (a < 3) + { + Ps::prefetchLine(cmOutputs[a]->contactPatches); + Ps::prefetchLine(cmOutputs[a]->contactPoints); + } + + PxReal invMassScale0, invMassScale1, invInertiaScale0, invInertiaScale1; + + const PxReal defaultMaxImpulse = PxMin(blockDesc.bodyData0->maxContactImpulse, blockDesc.bodyData1->maxContactImpulse); + + PxU32 contactCount = extractContacts(buffer, *cmOutputs[a], hasMaxImpulse, hasTargetVelocity, invMassScale0, invMassScale1, + invInertiaScale0, invInertiaScale1, defaultMaxImpulse); + + if (contactCount == 0 || hasTargetVelocity) + return SolverConstraintPrepState::eUNBATCHABLE; + + blockDesc.numContacts = contactCount; + blockDesc.hasMaxImpulse = hasMaxImpulse; + blockDesc.disableStrongFriction = blockDesc.disableStrongFriction || hasTargetVelocity; + + blockDesc.mInvMassScales.linear0 *= invMassScale0; + blockDesc.mInvMassScales.linear1 *= invMassScale1; + blockDesc.mInvMassScales.angular0 *= blockDesc.body0->isKinematic ? 0.f : invInertiaScale0; + blockDesc.mInvMassScales.angular1 *= blockDesc.body1->isKinematic ? 0.f : invInertiaScale1; + } + + return createFinalizeSolverContacts4Step(c, blockDescs, + invDtF32, invTotalDtF32, bounceThresholdF32, frictionOffsetThreshold, + correlationDistance, solverOffsetSlop, constraintAllocator); +} + +PX_FORCE_INLINE PxU32 getConstraintLength(const PxTGSSolverConstraintDesc& desc) +{ + return PxU32(desc.constraintLengthOver16 << 4); +} + +PX_FORCE_INLINE PxU32 getWritebackLength(const PxTGSSolverConstraintDesc& desc) +{ + return PxU32(desc.writeBackLengthOver4 << 2); +} + + + +void preprocessRows(Px1DConstraint** sorted, + Px1DConstraint* rows, + PxVec4* angSqrtInvInertia0, + PxVec4* angSqrtInvInertia1, + PxU32 rowCount, + const PxMat33& sqrtInvInertia0F32, + const PxMat33& sqrtInvInertia1F32, + const PxReal invMass0, + const PxReal invMass1, + const PxConstraintInvMassScale& ims, + bool disablePreprocessing, + bool diagonalizeDrive, + bool preprocessLinear = true); + +void setSolverConstantsStep(PxReal& error, + PxReal& biasScale, + PxReal& targetVel, + PxReal& maxBias, + PxReal& velMultiplier, + PxReal& impulseMultiplier, + PxReal& rcpResponse, + const Px1DConstraint& c, + PxReal normalVel, + PxReal unitResponse, + PxReal minRowResponse, + PxReal erp, + PxReal dt, + PxReal totalDt, + PxReal biasClamp, + PxReal recipdt, + PxReal recipTotalDt); + + +namespace +{ + void setConstants(PxReal& error, PxReal& biasScale, PxReal& targetVel, PxReal& maxBias, PxReal& velMultiplier, PxReal& impulseMultiplier, + PxReal& rcpResponse, const Px1DConstraint& c, PxReal unitResponse, PxReal minRowResponse, PxReal dt, PxReal totalDt, + PxReal recipdt, PxReal recipTotalDt, const bool finished, + const PxReal lengthScale, const PxReal nv, const PxReal nv0, const PxReal nv1, const bool isKinematic0, const bool isKinematic1) + { + PX_UNUSED(dt); + if (finished) + { + error = 0.f; + biasScale = 0.f; + maxBias = 0.f; + velMultiplier = 0.f; + impulseMultiplier = 0.f; + rcpResponse = 0.f; + targetVel = 0.f; + return; + } + + PxReal erp = 1.f; + PxReal linearErp = 1.f; + + //PxReal biasClamp = c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT ? 50.f : 200.f*lengthScale; + PxReal biasClamp = c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT ? 100.f : 1000.f*lengthScale; + + setSolverConstantsStep(error, biasScale, targetVel, maxBias, velMultiplier, impulseMultiplier, rcpResponse, c, nv, unitResponse, + minRowResponse, c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT ? erp : linearErp, dt, totalDt, biasClamp, recipdt, recipTotalDt); + + if(isKinematic0) + targetVel -= nv0; + if(isKinematic1) + targetVel += nv1; + } + + void setOrthoData(const PxReal& ang0X, const PxReal& ang0Y, const PxReal& ang0Z, const PxReal& ang1X, const PxReal& ang1Y, const PxReal& ang1Z, + const PxReal& recipResponse, const PxReal& error, PxReal& orthoAng0X, PxReal& orthoAng0Y, PxReal& orthoAng0Z, PxReal& orthoAng1X, PxReal& orthoAng1Y, PxReal& orthoAng1Z, + PxReal& orthoRecipResponse, PxReal& orthoError, const bool disableProcessing, const PxU32 solveHint, PxU32& flags, PxU32& orthoCount, const bool finished) + { + if (!finished && !disableProcessing) + { + if (solveHint == PxConstraintSolveHint::eROTATIONAL_EQUALITY) + { + flags |= DY_SC_FLAG_ROT_EQ; + orthoAng0X = ang0X; orthoAng0Y = ang0Y; orthoAng0Z = ang0Z; + orthoAng1X = ang1X; orthoAng1Y = ang1Y; orthoAng1Z = ang1Z; + orthoRecipResponse = recipResponse; + orthoError = error; + orthoCount++; + } + else if (solveHint & PxConstraintSolveHint::eEQUALITY) + flags |= DY_SC_FLAG_ORTHO_TARGET; + } + } +} + +SolverConstraintPrepState::Enum setupSolverConstraintStep4 +(PxTGSSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal totalDt, const PxReal recipdt, const PxReal recipTotalDt, PxU32& totalRows, + PxConstraintAllocator& allocator, PxU32 maxRows, const PxReal lengthScale); + +SolverConstraintPrepState::Enum setupSolverConstraintStep4 +(SolverConstraintShaderPrepDesc* PX_RESTRICT constraintShaderDescs, + PxTGSSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal totalDt, const PxReal recipdt, const PxReal recipTotalDt, PxU32& totalRows, + PxConstraintAllocator& allocator, const PxReal lengthScale) + +{ + //KS - we will never get here with constraints involving articulations so we don't need to stress about those in here + + totalRows = 0; + + Px1DConstraint allRows[MAX_CONSTRAINT_ROWS * 4]; + + PxU32 numRows = 0; + + PxU32 maxRows = 0; + PxU32 preppedIndex = 0; + + for (PxU32 a = 0; a < 4; ++a) + { + Px1DConstraint* rows = allRows + numRows; + SolverConstraintShaderPrepDesc& shaderDesc = constraintShaderDescs[a]; + PxTGSSolverConstraintPrepDesc& desc = constraintDescs[a]; + + if (!shaderDesc.solverPrep) + return SolverConstraintPrepState::eUNBATCHABLE; + + PxMemZero(rows + preppedIndex, sizeof(Px1DConstraint)*(MAX_CONSTRAINT_ROWS)); + for (PxU32 b = preppedIndex; b < MAX_CONSTRAINT_ROWS; ++b) + { + Px1DConstraint& c = rows[b]; + //Px1DConstraintInit(c); + c.minImpulse = -PX_MAX_REAL; + c.maxImpulse = PX_MAX_REAL; + } + + desc.mInvMassScales.linear0 = desc.mInvMassScales.linear1 = desc.mInvMassScales.angular0 = desc.mInvMassScales.angular1 = 1.f; + + desc.body0WorldOffset = PxVec3(0.f); + + PxU32 constraintCount = (*shaderDesc.solverPrep)(rows, + desc.body0WorldOffset, + MAX_CONSTRAINT_ROWS, + desc.mInvMassScales, + shaderDesc.constantBlock, + desc.bodyFrame0, desc.bodyFrame1, desc.extendedLimits, desc.cA2w, desc.cB2w); + + preppedIndex = MAX_CONSTRAINT_ROWS - constraintCount; + + maxRows = PxMax(constraintCount, maxRows); + + if (constraintCount == 0) + return SolverConstraintPrepState::eUNBATCHABLE; + + desc.rows = rows; + desc.numRows = constraintCount; + numRows += constraintCount; + + if (desc.body0->isKinematic) + desc.mInvMassScales.angular0 = 0.f; + if (desc.body1->isKinematic) + desc.mInvMassScales.angular1 = 0.f; + + } + + return setupSolverConstraintStep4(constraintDescs, dt, totalDt, recipdt, recipTotalDt, totalRows, allocator, maxRows, lengthScale); +} + +SolverConstraintPrepState::Enum setupSolverConstraintStep4 +(PxTGSSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal totalDt, const PxReal recipdt, const PxReal recipTotalDt, PxU32& totalRows, + PxConstraintAllocator& allocator, PxU32 maxRows, + const PxReal lengthScale) +{ + const Vec4V zero = V4Zero(); + Px1DConstraint* allSorted[MAX_CONSTRAINT_ROWS * 4]; + PxU32 startIndex[4]; + PX_ALIGN(16, PxVec4) angSqrtInvInertia0[MAX_CONSTRAINT_ROWS * 4]; + PX_ALIGN(16, PxVec4) angSqrtInvInertia1[MAX_CONSTRAINT_ROWS * 4]; + + PxU32 numRows = 0; + + for (PxU32 a = 0; a < 4; ++a) + { + startIndex[a] = numRows; + PxTGSSolverConstraintPrepDesc& desc = constraintDescs[a]; + Px1DConstraint** sorted = allSorted + numRows; + + for (PxU32 i = 0; i < desc.numRows; ++i) + { + if (desc.rows[i].flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT) + { + if (desc.rows[i].solveHint == PxConstraintSolveHint::eEQUALITY) + desc.rows[i].solveHint = PxConstraintSolveHint::eROTATIONAL_EQUALITY; + else if (desc.rows[i].solveHint == PxConstraintSolveHint::eINEQUALITY) + desc.rows[i].solveHint = PxConstraintSolveHint::eROTATIONAL_INEQUALITY; + } + } + + + preprocessRows(sorted, desc.rows, angSqrtInvInertia0 + numRows, angSqrtInvInertia1 + numRows, desc.numRows, + desc.body0TxI->sqrtInvInertia, desc.body1TxI->sqrtInvInertia, desc.bodyData0->invMass, desc.bodyData1->invMass, + desc.mInvMassScales, desc.disablePreprocessing, desc.improvedSlerp, false); + + numRows += desc.numRows; + } + + + PxU32 stride = sizeof(SolverConstraint1DStep4); + + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeaderStep4) + stride * maxRows; + + //KS - +16 is for the constraint progress counter, which needs to be the last element in the constraint (so that we + //know SPU DMAs have completed) + PxU8* ptr = allocator.reserveConstraintData(constraintLength + 16u); + if (NULL == ptr || (reinterpret_cast(-1)) == ptr) + { + for (PxU32 a = 0; a < 4; ++a) + { + PxTGSSolverConstraintPrepDesc& desc = constraintDescs[a]; + desc.desc->constraint = NULL; + desc.desc->constraintLengthOver16 = 0; + desc.desc->writeBack = desc.writeback; + } + + if (NULL == ptr) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept joints detaching/exploding or increase buffer size allocated for constraint prep by increasing PxSceneDesc::maxNbContactDataBlocks."); + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of constraint data. " + "Either accept joints detaching/exploding or simplify constraints."); + ptr = NULL; + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + } + //desc.constraint = ptr; + + totalRows = numRows; + + const bool isKinematic00 = constraintDescs[0].body0->isKinematic; + const bool isKinematic01 = constraintDescs[0].body1->isKinematic; + const bool isKinematic10 = constraintDescs[1].body0->isKinematic; + const bool isKinematic11 = constraintDescs[1].body1->isKinematic; + const bool isKinematic20 = constraintDescs[2].body0->isKinematic; + const bool isKinematic21 = constraintDescs[2].body1->isKinematic; + const bool isKinematic30 = constraintDescs[3].body0->isKinematic; + const bool isKinematic31 = constraintDescs[3].body1->isKinematic; + + for (PxU32 a = 0; a < 4; ++a) + { + PxTGSSolverConstraintPrepDesc& desc = constraintDescs[a]; + desc.desc->constraint = ptr; + desc.desc->constraintLengthOver16 = PxU16(constraintLength/16); + desc.desc->writeBack = desc.writeback; + } + + { + PxU8* currPtr = ptr; + SolverConstraint1DHeaderStep4* header = reinterpret_cast(currPtr); + currPtr += sizeof(SolverConstraint1DHeaderStep4); + + const PxStepSolverBodyData& bd00 = *constraintDescs[0].bodyData0; + const PxStepSolverBodyData& bd01 = *constraintDescs[1].bodyData0; + const PxStepSolverBodyData& bd02 = *constraintDescs[2].bodyData0; + const PxStepSolverBodyData& bd03 = *constraintDescs[3].bodyData0; + + const PxStepSolverBodyData& bd10 = *constraintDescs[0].bodyData1; + const PxStepSolverBodyData& bd11 = *constraintDescs[1].bodyData1; + const PxStepSolverBodyData& bd12 = *constraintDescs[2].bodyData1; + const PxStepSolverBodyData& bd13 = *constraintDescs[3].bodyData1; + + //Load up masses, invInertia, velocity etc. + + const Vec4V invMassScale0 = V4LoadXYZW(constraintDescs[0].mInvMassScales.linear0, constraintDescs[1].mInvMassScales.linear0, + constraintDescs[2].mInvMassScales.linear0, constraintDescs[3].mInvMassScales.linear0); + const Vec4V invMassScale1 = V4LoadXYZW(constraintDescs[0].mInvMassScales.linear1, constraintDescs[1].mInvMassScales.linear1, + constraintDescs[2].mInvMassScales.linear1, constraintDescs[3].mInvMassScales.linear1); + + + const Vec4V iMass0 = V4LoadXYZW(bd00.invMass, bd01.invMass, bd02.invMass, bd03.invMass); + + const Vec4V iMass1 = V4LoadXYZW(bd10.invMass, bd11.invMass, bd12.invMass, bd13.invMass); + + const Vec4V invMass0 = V4Mul(iMass0, invMassScale0); + const Vec4V invMass1 = V4Mul(iMass1, invMassScale1); + + + const Vec4V invInertiaScale0 = V4LoadXYZW(constraintDescs[0].mInvMassScales.angular0, constraintDescs[1].mInvMassScales.angular0, + constraintDescs[2].mInvMassScales.angular0, constraintDescs[3].mInvMassScales.angular0); + const Vec4V invInertiaScale1 = V4LoadXYZW(constraintDescs[0].mInvMassScales.angular1, constraintDescs[1].mInvMassScales.angular1, + constraintDescs[2].mInvMassScales.angular1, constraintDescs[3].mInvMassScales.angular1); + + + //body world offsets + Vec4V workOffset0 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[0].body0WorldOffset)); + Vec4V workOffset1 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[1].body0WorldOffset)); + Vec4V workOffset2 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[2].body0WorldOffset)); + Vec4V workOffset3 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[3].body0WorldOffset)); + + Vec4V workOffsetX, workOffsetY, workOffsetZ; + + PX_TRANSPOSE_44_34(workOffset0, workOffset1, workOffset2, workOffset3, workOffsetX, workOffsetY, workOffsetZ); + + const FloatV dtV = FLoad(totalDt); + Vec4V linBreakForce = V4LoadXYZW(constraintDescs[0].linBreakForce, constraintDescs[1].linBreakForce, + constraintDescs[2].linBreakForce, constraintDescs[3].linBreakForce); + Vec4V angBreakForce = V4LoadXYZW(constraintDescs[0].angBreakForce, constraintDescs[1].angBreakForce, + constraintDescs[2].angBreakForce, constraintDescs[3].angBreakForce); + + + header->breakable[0] = PxU8((constraintDescs[0].linBreakForce != PX_MAX_F32) || (constraintDescs[0].angBreakForce != PX_MAX_F32)); + header->breakable[1] = PxU8((constraintDescs[1].linBreakForce != PX_MAX_F32) || (constraintDescs[1].angBreakForce != PX_MAX_F32)); + header->breakable[2] = PxU8((constraintDescs[2].linBreakForce != PX_MAX_F32) || (constraintDescs[2].angBreakForce != PX_MAX_F32)); + header->breakable[3] = PxU8((constraintDescs[3].linBreakForce != PX_MAX_F32) || (constraintDescs[3].angBreakForce != PX_MAX_F32)); + + //OK, I think that's everything loaded in + + header->invMass0D0 = invMass0; + header->invMass1D1 = invMass1; + header->angD0 = invInertiaScale0; + header->angD1 = invInertiaScale1; + header->body0WorkOffset[0] = workOffsetX; + header->body0WorkOffset[1] = workOffsetY; + header->body0WorkOffset[2] = workOffsetZ; + + header->count = maxRows; + header->type = DY_SC_TYPE_BLOCK_1D; + header->linBreakImpulse = V4Scale(linBreakForce, dtV); + header->angBreakImpulse = V4Scale(angBreakForce, dtV); + header->counts[0] = Ps::to8(constraintDescs[0].numRows); + header->counts[1] = Ps::to8(constraintDescs[1].numRows); + header->counts[2] = Ps::to8(constraintDescs[2].numRows); + header->counts[3] = Ps::to8(constraintDescs[3].numRows); + + + Vec4V ca2WX, ca2WY, ca2WZ; + Vec4V cb2WX, cb2WY, cb2WZ; + + Vec4V ca2W0 = V4LoadU(&constraintDescs[0].cA2w.x); + Vec4V ca2W1 = V4LoadU(&constraintDescs[1].cA2w.x); + Vec4V ca2W2 = V4LoadU(&constraintDescs[2].cA2w.x); + Vec4V ca2W3 = V4LoadU(&constraintDescs[3].cA2w.x); + + Vec4V cb2W0 = V4LoadU(&constraintDescs[0].cB2w.x); + Vec4V cb2W1 = V4LoadU(&constraintDescs[1].cB2w.x); + Vec4V cb2W2 = V4LoadU(&constraintDescs[2].cB2w.x); + Vec4V cb2W3 = V4LoadU(&constraintDescs[3].cB2w.x); + + PX_TRANSPOSE_44_34(ca2W0, ca2W1, ca2W2, ca2W3, ca2WX, ca2WY, ca2WZ); + PX_TRANSPOSE_44_34(cb2W0, cb2W1, cb2W2, cb2W3, cb2WX, cb2WY, cb2WZ); + + Vec4V pos00 = V4LoadA(&constraintDescs[0].body0TxI->deltaBody2World.p.x); + Vec4V pos01 = V4LoadA(&constraintDescs[0].body1TxI->deltaBody2World.p.x); + Vec4V pos10 = V4LoadA(&constraintDescs[1].body0TxI->deltaBody2World.p.x); + Vec4V pos11 = V4LoadA(&constraintDescs[1].body1TxI->deltaBody2World.p.x); + Vec4V pos20 = V4LoadA(&constraintDescs[2].body0TxI->deltaBody2World.p.x); + Vec4V pos21 = V4LoadA(&constraintDescs[2].body1TxI->deltaBody2World.p.x); + Vec4V pos30 = V4LoadA(&constraintDescs[3].body0TxI->deltaBody2World.p.x); + Vec4V pos31 = V4LoadA(&constraintDescs[3].body1TxI->deltaBody2World.p.x); + + Vec4V pos0X, pos0Y, pos0Z; + Vec4V pos1X, pos1Y, pos1Z; + + PX_TRANSPOSE_44_34(pos00, pos10, pos20, pos30, pos0X, pos0Y, pos0Z); + PX_TRANSPOSE_44_34(pos01, pos11, pos21, pos31, pos1X, pos1Y, pos1Z); + + Vec4V linVel00 = V4LoadA(&constraintDescs[0].bodyData0->originalLinearVelocity.x); + Vec4V linVel01 = V4LoadA(&constraintDescs[0].bodyData1->originalLinearVelocity.x); + Vec4V angState00 = V4LoadA(&constraintDescs[0].bodyData0->originalAngularVelocity.x); + Vec4V angState01 = V4LoadA(&constraintDescs[0].bodyData1->originalAngularVelocity.x); + + Vec4V linVel10 = V4LoadA(&constraintDescs[1].bodyData0->originalLinearVelocity.x); + Vec4V linVel11 = V4LoadA(&constraintDescs[1].bodyData1->originalLinearVelocity.x); + Vec4V angState10 = V4LoadA(&constraintDescs[1].bodyData0->originalAngularVelocity.x); + Vec4V angState11 = V4LoadA(&constraintDescs[1].bodyData1->originalAngularVelocity.x); + + Vec4V linVel20 = V4LoadA(&constraintDescs[2].bodyData0->originalLinearVelocity.x); + Vec4V linVel21 = V4LoadA(&constraintDescs[2].bodyData1->originalLinearVelocity.x); + Vec4V angState20 = V4LoadA(&constraintDescs[2].bodyData0->originalAngularVelocity.x); + Vec4V angState21 = V4LoadA(&constraintDescs[2].bodyData1->originalAngularVelocity.x); + + Vec4V linVel30 = V4LoadA(&constraintDescs[3].bodyData0->originalLinearVelocity.x); + Vec4V linVel31 = V4LoadA(&constraintDescs[3].bodyData1->originalLinearVelocity.x); + Vec4V angState30 = V4LoadA(&constraintDescs[3].bodyData0->originalAngularVelocity.x); + Vec4V angState31 = V4LoadA(&constraintDescs[3].bodyData1->originalAngularVelocity.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2; + Vec4V linVel1T0, linVel1T1, linVel1T2; + Vec4V angState0T0, angState0T1, angState0T2; + Vec4V angState1T0, angState1T1, angState1T2; + + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2); + PX_TRANSPOSE_44_34(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2); + PX_TRANSPOSE_44_34(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2); + + + + const Vec4V raWorldX = V4Sub(ca2WX, pos0X); + const Vec4V raWorldY = V4Sub(ca2WY, pos0Y); + const Vec4V raWorldZ = V4Sub(ca2WZ, pos0Z); + + const Vec4V rbWorldX = V4Sub(cb2WX, pos1X); + const Vec4V rbWorldY = V4Sub(cb2WY, pos1Y); + const Vec4V rbWorldZ = V4Sub(cb2WZ, pos1Z); + + header->rAWorld[0] = raWorldX; + header->rAWorld[1] = raWorldY; + header->rAWorld[2] = raWorldZ; + + header->rBWorld[0] = rbWorldX; + header->rBWorld[1] = rbWorldY; + header->rBWorld[2] = rbWorldZ; + + //Now we loop over the constraints and build the results... + + PxU32 index0 = 0; + PxU32 endIndex0 = constraintDescs[0].numRows - 1; + PxU32 index1 = startIndex[1]; + PxU32 endIndex1 = index1 + constraintDescs[1].numRows - 1; + PxU32 index2 = startIndex[2]; + PxU32 endIndex2 = index2 + constraintDescs[2].numRows - 1; + PxU32 index3 = startIndex[3]; + PxU32 endIndex3 = index3 + constraintDescs[3].numRows - 1; + + const Vec4V one = V4One(); + const FloatV fOne = FOne(); + + PxU32 orthoCount0 = 0, orthoCount1 = 0, orthoCount2 = 0, orthoCount3 = 0; + + for (PxU32 a = 0; a < 3; ++a) + { + header->angOrthoAxis0X[a] = V4Zero(); + header->angOrthoAxis0Y[a] = V4Zero(); + header->angOrthoAxis0Z[a] = V4Zero(); + + header->angOrthoAxis1X[a] = V4Zero(); + header->angOrthoAxis1Y[a] = V4Zero(); + header->angOrthoAxis1Z[a] = V4Zero(); + + header->angOrthoRecipResponse[a] = V4Zero(); + header->angOrthoError[a] = V4Zero(); + } + + for (PxU32 a = 0; a < maxRows; ++a) + { + bool finished[] = { a >= constraintDescs[0].numRows, a >= constraintDescs[1].numRows, a >= constraintDescs[2].numRows, a >= constraintDescs[3].numRows }; + BoolV bFinished = BLoad(finished); + SolverConstraint1DStep4* c = reinterpret_cast(currPtr); + currPtr += stride; + + Px1DConstraint* con0 = allSorted[index0]; + Px1DConstraint* con1 = allSorted[index1]; + Px1DConstraint* con2 = allSorted[index2]; + Px1DConstraint* con3 = allSorted[index3]; + + bool angularConstraint[4] = + { + !!(con0->flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT), + !!(con1->flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT), + !!(con2->flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT), + !!(con3->flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT), + }; + + BoolV bAngularConstraint = BLoad(angularConstraint); + + Vec4V cangDelta00 = V4LoadA(&angSqrtInvInertia0[index0].x); + Vec4V cangDelta01 = V4LoadA(&angSqrtInvInertia0[index1].x); + Vec4V cangDelta02 = V4LoadA(&angSqrtInvInertia0[index2].x); + Vec4V cangDelta03 = V4LoadA(&angSqrtInvInertia0[index3].x); + + Vec4V cangDelta10 = V4LoadA(&angSqrtInvInertia1[index0].x); + Vec4V cangDelta11 = V4LoadA(&angSqrtInvInertia1[index1].x); + Vec4V cangDelta12 = V4LoadA(&angSqrtInvInertia1[index2].x); + Vec4V cangDelta13 = V4LoadA(&angSqrtInvInertia1[index3].x); + + index0 = index0 == endIndex0 ? index0 : index0 + 1; + index1 = index1 == endIndex1 ? index1 : index1 + 1; + index2 = index2 == endIndex2 ? index2 : index2 + 1; + index3 = index3 == endIndex3 ? index3 : index3 + 1; + + Vec4V driveScale = one; + if (con0->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[0].driveLimitsAreForces) + driveScale = V4SetX(driveScale, FMin(fOne, dtV)); + if (con1->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[1].driveLimitsAreForces) + driveScale = V4SetY(driveScale, FMin(fOne, dtV)); + if (con2->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[2].driveLimitsAreForces) + driveScale = V4SetZ(driveScale, FMin(fOne, dtV)); + if (con3->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[3].driveLimitsAreForces) + driveScale = V4SetW(driveScale, FMin(fOne, dtV)); + + + Vec4V clin00 = V4LoadA(&con0->linear0.x); + Vec4V clin01 = V4LoadA(&con1->linear0.x); + Vec4V clin02 = V4LoadA(&con2->linear0.x); + Vec4V clin03 = V4LoadA(&con3->linear0.x); + + Vec4V clin0X, clin0Y, clin0Z; + + PX_TRANSPOSE_44_34(clin00, clin01, clin02, clin03, clin0X, clin0Y, clin0Z); + + Vec4V cang00 = V4LoadA(&con0->angular0.x); + Vec4V cang01 = V4LoadA(&con1->angular0.x); + Vec4V cang02 = V4LoadA(&con2->angular0.x); + Vec4V cang03 = V4LoadA(&con3->angular0.x); + + Vec4V cang0X, cang0Y, cang0Z; + + PX_TRANSPOSE_44_34(cang00, cang01, cang02, cang03, cang0X, cang0Y, cang0Z); + + Vec4V cang10 = V4LoadA(&con0->angular1.x); + Vec4V cang11 = V4LoadA(&con1->angular1.x); + Vec4V cang12 = V4LoadA(&con2->angular1.x); + Vec4V cang13 = V4LoadA(&con3->angular1.x); + + Vec4V cang1X, cang1Y, cang1Z; + + PX_TRANSPOSE_44_34(cang10, cang11, cang12, cang13, cang1X, cang1Y, cang1Z); + + const Vec4V maxImpulse = V4LoadXYZW(con0->maxImpulse, con1->maxImpulse, con2->maxImpulse, con3->maxImpulse); + const Vec4V minImpulse = V4LoadXYZW(con0->minImpulse, con1->minImpulse, con2->minImpulse, con3->minImpulse); + + Vec4V angDelta0X, angDelta0Y, angDelta0Z; + + PX_TRANSPOSE_44_34(cangDelta00, cangDelta01, cangDelta02, cangDelta03, angDelta0X, angDelta0Y, angDelta0Z); + + c->flags[0] = 0; + c->flags[1] = 0; + c->flags[2] = 0; + c->flags[3] = 0; + + c->lin0[0] = V4Sel(bFinished, zero, clin0X); + c->lin0[1] = V4Sel(bFinished, zero, clin0Y); + c->lin0[2] = V4Sel(bFinished, zero, clin0Z); + c->ang0[0] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang0X, zero); + c->ang0[1] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang0Y, zero); + c->ang0[2] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang0Z, zero); + c->angularErrorScale = V4Sel(bAngularConstraint, one, zero); + + c->minImpulse = V4Mul(minImpulse, driveScale); + c->maxImpulse = V4Mul(maxImpulse, driveScale); + c->appliedForce = zero; + + const Vec4V lin0MagSq = V4MulAdd(clin0Z, clin0Z, V4MulAdd(clin0Y, clin0Y, V4Mul(clin0X, clin0X))); + const Vec4V cang0DotAngDelta = V4MulAdd(angDelta0Z, angDelta0Z, V4MulAdd(angDelta0Y, angDelta0Y, V4Mul(angDelta0X, angDelta0X))); + + Vec4V unitResponse = V4MulAdd(lin0MagSq, invMass0, V4Mul(cang0DotAngDelta, invInertiaScale0)); + + Vec4V clin10 = V4LoadA(&con0->linear1.x); + Vec4V clin11 = V4LoadA(&con1->linear1.x); + Vec4V clin12 = V4LoadA(&con2->linear1.x); + Vec4V clin13 = V4LoadA(&con3->linear1.x); + + Vec4V clin1X, clin1Y, clin1Z; + PX_TRANSPOSE_44_34(clin10, clin11, clin12, clin13, clin1X, clin1Y, clin1Z); + + Vec4V angDelta1X, angDelta1Y, angDelta1Z; + + PX_TRANSPOSE_44_34(cangDelta10, cangDelta11, cangDelta12, cangDelta13, angDelta1X, angDelta1Y, angDelta1Z); + + const Vec4V lin1MagSq = V4MulAdd(clin1Z, clin1Z, V4MulAdd(clin1Y, clin1Y, V4Mul(clin1X, clin1X))); + const Vec4V cang1DotAngDelta = V4MulAdd(angDelta1Z, angDelta1Z, V4MulAdd(angDelta1Y, angDelta1Y, V4Mul(angDelta1X, angDelta1X))); + + c->lin1[0] = V4Sel(bFinished, zero, clin1X); + c->lin1[1] = V4Sel(bFinished, zero, clin1Y); + c->lin1[2] = V4Sel(bFinished, zero, clin1Z); + + c->ang1[0] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang1X, zero); + c->ang1[1] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang1Y, zero); + c->ang1[2] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang1Z, zero); + + unitResponse = V4Add(unitResponse, V4MulAdd(lin1MagSq, invMass1, V4Mul(cang1DotAngDelta, invInertiaScale1))); + + const Vec4V lnormalVel0 = V4MulAdd(clin0X, linVel0T0, V4MulAdd(clin0Y, linVel0T1, V4Mul(clin0Z, linVel0T2))); + const Vec4V lnormalVel1 = V4MulAdd(clin1X, linVel1T0, V4MulAdd(clin1Y, linVel1T1, V4Mul(clin1Z, linVel1T2))); + + const Vec4V angVel0 = V4MulAdd(cang0X, angState0T0, V4MulAdd(cang0Y, angState0T1, V4Mul(cang0Z, angState0T2))); + const Vec4V angVel1 = V4MulAdd(angDelta1X, angState1T0, V4MulAdd(angDelta1Y, angState1T1, V4Mul(angDelta1Z, angState1T2))); + + const Vec4V normalVel0 = V4Add(lnormalVel0, angVel0); + const Vec4V normalVel1 = V4Add(lnormalVel1, angVel1); + + const Vec4V normalVel = V4Sub(normalVel0, normalVel1); + + angDelta0X = V4Mul(angDelta0X, invInertiaScale0); + angDelta0Y = V4Mul(angDelta0Y, invInertiaScale0); + angDelta0Z = V4Mul(angDelta0Z, invInertiaScale0); + + angDelta1X = V4Mul(angDelta1X, invInertiaScale1); + angDelta1Y = V4Mul(angDelta1Y, invInertiaScale1); + angDelta1Z = V4Mul(angDelta1Z, invInertiaScale1); + + + + { + PxReal recipResponse[4]; + const PxVec4& ur = reinterpret_cast(unitResponse); + PxVec4& cBiasScale = reinterpret_cast(c->biasScale); + PxVec4& cError = reinterpret_cast(c->error); + PxVec4& cMaxBias = reinterpret_cast(c->maxBias); + PxVec4& cTargetVel = reinterpret_cast(c->velTarget); + PxVec4& cVelMultiplier = reinterpret_cast(c->velMultiplier); + PxVec4& cImpulseMultiplier = reinterpret_cast(c->impulseMultiplier); + const PxVec4& nVel = reinterpret_cast(normalVel); + const PxVec4& nVel0 = reinterpret_cast(normalVel0); + const PxVec4& nVel1 = reinterpret_cast(normalVel1); + + setConstants(cError.x, cBiasScale.x, cTargetVel.x, cMaxBias.x, cVelMultiplier.x, cImpulseMultiplier.x, + recipResponse[0], *con0, ur.x, constraintDescs[0].minResponseThreshold, dt, totalDt, recipdt, recipTotalDt, + a >= constraintDescs[0].numRows, lengthScale, nVel.x, nVel0.x, nVel1.x, isKinematic00, isKinematic01); + + setConstants(cError.y, cBiasScale.y, cTargetVel.y, cMaxBias.y, cVelMultiplier.y, cImpulseMultiplier.y, + recipResponse[1], *con1, ur.y, constraintDescs[1].minResponseThreshold, dt, totalDt, recipdt, recipTotalDt, + a >= constraintDescs[1].numRows, lengthScale, nVel.y, nVel0.y, nVel1.y, isKinematic10, isKinematic11); + + setConstants(cError.z, cBiasScale.z, cTargetVel.z, cMaxBias.z, cVelMultiplier.z, cImpulseMultiplier.z, + recipResponse[2], *con2, ur.z, constraintDescs[2].minResponseThreshold, dt, totalDt, recipdt, recipTotalDt, + a >= constraintDescs[2].numRows, lengthScale, nVel.z, nVel0.z, nVel1.z, isKinematic20, isKinematic21); + + setConstants(cError.w, cBiasScale.w, cTargetVel.w, cMaxBias.w, cVelMultiplier.w, cImpulseMultiplier.w, + recipResponse[3], *con3, ur.w, constraintDescs[3].minResponseThreshold, dt, totalDt, recipdt, recipTotalDt, + a >= constraintDescs[3].numRows, lengthScale, nVel.w, nVel0.w, nVel1.w, isKinematic30, isKinematic31); + + PxVec4* angOrthoAxes0X = reinterpret_cast(header->angOrthoAxis0X); + PxVec4* angOrthoAxes0Y = reinterpret_cast(header->angOrthoAxis0Y); + PxVec4* angOrthoAxes0Z = reinterpret_cast(header->angOrthoAxis0Z); + PxVec4* angOrthoAxes1X = reinterpret_cast(header->angOrthoAxis1X); + PxVec4* angOrthoAxes1Y = reinterpret_cast(header->angOrthoAxis1Y); + PxVec4* angOrthoAxes1Z = reinterpret_cast(header->angOrthoAxis1Z); + PxVec4* orthoRecipResponse = reinterpret_cast(header->angOrthoRecipResponse); + PxVec4* orthoError = reinterpret_cast(header->angOrthoError); + + const PxVec4& ang0X = reinterpret_cast(angDelta0X); + const PxVec4& ang0Y = reinterpret_cast(angDelta0Y); + const PxVec4& ang0Z = reinterpret_cast(angDelta0Z); + + const PxVec4& ang1X = reinterpret_cast(angDelta1X); + const PxVec4& ang1Y = reinterpret_cast(angDelta1Y); + const PxVec4& ang1Z = reinterpret_cast(angDelta1Z); + + setOrthoData(ang0X.x, ang0Y.x, ang0Z.x, ang1X.x, ang1Y.x, ang1Z.x, recipResponse[0], cError.x, angOrthoAxes0X[orthoCount0].x, + angOrthoAxes0Y[orthoCount0].x, angOrthoAxes0Z[orthoCount0].x, angOrthoAxes1X[orthoCount0].x, angOrthoAxes1Y[orthoCount0].x, + angOrthoAxes1Z[orthoCount0].x, orthoRecipResponse[orthoCount0].x, orthoError[orthoCount0].x, constraintDescs[0].disablePreprocessing, con0->solveHint, + c->flags[0], orthoCount0, a >= constraintDescs[0].numRows); + + setOrthoData(ang0X.y, ang0Y.y, ang0Z.y, ang1X.y, ang1Y.y, ang1Z.y, recipResponse[1], cError.y, angOrthoAxes0X[orthoCount1].y, + angOrthoAxes0Y[orthoCount1].y, angOrthoAxes0Z[orthoCount1].y, angOrthoAxes1X[orthoCount1].y, angOrthoAxes1Y[orthoCount1].y, + angOrthoAxes1Z[orthoCount1].y, orthoRecipResponse[orthoCount1].y, orthoError[orthoCount1].y, constraintDescs[1].disablePreprocessing, con1->solveHint, + c->flags[1], orthoCount1, a >= constraintDescs[1].numRows); + + setOrthoData(ang0X.z, ang0Y.z, ang0Z.z, ang1X.z, ang1Y.z, ang1Z.z, recipResponse[2], cError.z, angOrthoAxes0X[orthoCount2].z, + angOrthoAxes0Y[orthoCount2].z, angOrthoAxes0Z[orthoCount2].z, angOrthoAxes1X[orthoCount2].z, angOrthoAxes1Y[orthoCount2].z, + angOrthoAxes1Z[orthoCount2].z, orthoRecipResponse[orthoCount2].z, orthoError[orthoCount2].z, constraintDescs[2].disablePreprocessing, con2->solveHint, + c->flags[2], orthoCount2, a >= constraintDescs[2].numRows); + + setOrthoData(ang0X.w, ang0Y.w, ang0Z.w, ang1X.w, ang1Y.w, ang1Z.w, recipResponse[3], cError.w, angOrthoAxes0X[orthoCount3].w, + angOrthoAxes0Y[orthoCount3].w, angOrthoAxes0Z[orthoCount3].w, angOrthoAxes1X[orthoCount3].w, angOrthoAxes1Y[orthoCount3].w, + angOrthoAxes1Z[orthoCount3].w, orthoRecipResponse[orthoCount3].w, orthoError[orthoCount3].w, constraintDescs[3].disablePreprocessing, con3->solveHint, + c->flags[3], orthoCount3, a >= constraintDescs[3].numRows); + + } + + if (con0->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[0] |= DY_SC_FLAG_OUTPUT_FORCE; + if (con1->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[1] |= DY_SC_FLAG_OUTPUT_FORCE; + if (con2->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[2] |= DY_SC_FLAG_OUTPUT_FORCE; + if (con3->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[3] |= DY_SC_FLAG_OUTPUT_FORCE; + + if ((con0->flags & Px1DConstraintFlag::eKEEPBIAS)) + c->flags[0] |= DY_SC_FLAG_KEEP_BIAS; + if ((con1->flags & Px1DConstraintFlag::eKEEPBIAS)) + c->flags[1] |= DY_SC_FLAG_KEEP_BIAS; + if ((con2->flags & Px1DConstraintFlag::eKEEPBIAS)) + c->flags[2] |= DY_SC_FLAG_KEEP_BIAS; + if ((con3->flags & Px1DConstraintFlag::eKEEPBIAS)) + c->flags[3] |= DY_SC_FLAG_KEEP_BIAS; + + //Flag inequality constraints... + if (con0->solveHint & 1) + c->flags[0] |= DY_SC_FLAG_INEQUALITY; + if (con1->solveHint & 1) + c->flags[1] |= DY_SC_FLAG_INEQUALITY; + if (con2->solveHint & 1) + c->flags[2] |= DY_SC_FLAG_INEQUALITY; + if (con3->solveHint & 1) + c->flags[3] |= DY_SC_FLAG_INEQUALITY; + + } + *(reinterpret_cast(currPtr)) = 0; + *(reinterpret_cast(currPtr + 4)) = 0; + } + + //OK, we're ready to allocate and solve prep these constraints now :-) + return SolverConstraintPrepState::eSUCCESS; +} + + + + + +void solveContact4_Block(const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const bool doFriction, const PxReal minPenetration, + const PxReal elapsedTimeF32) +{ + PxTGSSolverBodyVel& b00 = *desc[0].bodyA; + PxTGSSolverBodyVel& b01 = *desc[0].bodyB; + PxTGSSolverBodyVel& b10 = *desc[1].bodyA; + PxTGSSolverBodyVel& b11 = *desc[1].bodyB; + PxTGSSolverBodyVel& b20 = *desc[2].bodyA; + PxTGSSolverBodyVel& b21 = *desc[2].bodyB; + PxTGSSolverBodyVel& b30 = *desc[3].bodyA; + PxTGSSolverBodyVel& b31 = *desc[3].bodyB; + + const Vec4V minPen = V4Load(minPenetration); + + const Vec4V elapsedTime = V4Load(elapsedTimeF32); + + //We'll need this. + const Vec4V vZero = V4Zero(); + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&b01.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularVelocity.x); + Vec4V angState01 = V4LoadA(&b01.angularVelocity.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&b11.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularVelocity.x); + Vec4V angState11 = V4LoadA(&b11.angularVelocity.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&b21.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularVelocity.x); + Vec4V angState21 = V4LoadA(&b21.angularVelocity.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&b31.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularVelocity.x); + Vec4V angState31 = V4LoadA(&b31.angularVelocity.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2; + Vec4V linVel1T0, linVel1T1, linVel1T2; + Vec4V angState0T0, angState0T1, angState0T2; + Vec4V angState1T0, angState1T1, angState1T2; + + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2); + PX_TRANSPOSE_44_34(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2); + PX_TRANSPOSE_44_34(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2); + + + Vec4V linDelta00 = V4LoadA(&b00.deltaLinDt.x); + Vec4V linDelta01 = V4LoadA(&b01.deltaLinDt.x); + Vec4V angDelta00 = V4LoadA(&b00.deltaAngDt.x); + Vec4V angDelta01 = V4LoadA(&b01.deltaAngDt.x); + + Vec4V linDelta10 = V4LoadA(&b10.deltaLinDt.x); + Vec4V linDelta11 = V4LoadA(&b11.deltaLinDt.x); + Vec4V angDelta10 = V4LoadA(&b10.deltaAngDt.x); + Vec4V angDelta11 = V4LoadA(&b11.deltaAngDt.x); + + Vec4V linDelta20 = V4LoadA(&b20.deltaLinDt.x); + Vec4V linDelta21 = V4LoadA(&b21.deltaLinDt.x); + Vec4V angDelta20 = V4LoadA(&b20.deltaAngDt.x); + Vec4V angDelta21 = V4LoadA(&b21.deltaAngDt.x); + + Vec4V linDelta30 = V4LoadA(&b30.deltaLinDt.x); + Vec4V linDelta31 = V4LoadA(&b31.deltaLinDt.x); + Vec4V angDelta30 = V4LoadA(&b30.deltaAngDt.x); + Vec4V angDelta31 = V4LoadA(&b31.deltaAngDt.x); + + Vec4V linDelta0T0, linDelta0T1, linDelta0T2; + Vec4V linDelta1T0, linDelta1T1, linDelta1T2; + Vec4V angDelta0T0, angDelta0T1, angDelta0T2; + Vec4V angDelta1T0, angDelta1T1, angDelta1T2; + + + PX_TRANSPOSE_44_34(linDelta00, linDelta10, linDelta20, linDelta30, linDelta0T0, linDelta0T1, linDelta0T2); + PX_TRANSPOSE_44_34(linDelta01, linDelta11, linDelta21, linDelta31, linDelta1T0, linDelta1T1, linDelta1T2); + PX_TRANSPOSE_44_34(angDelta00, angDelta10, angDelta20, angDelta30, angDelta0T0, angDelta0T1, angDelta0T2); + PX_TRANSPOSE_44_34(angDelta01, angDelta11, angDelta21, angDelta31, angDelta1T0, angDelta1T1, angDelta1T2); + + + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + Vec4V vMax = V4Splat(FMax()); + + const PxU8* PX_RESTRICT prefetchAddress = currPtr + sizeof(SolverContactHeaderStepBlock) + sizeof(SolverContactPointStepBlock); + + SolverContactHeaderStepBlock* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + const Vec4V invMassA = hdr->invMass0D0; + const Vec4V invMassB = hdr->invMass1D1; + + const Vec4V sumInvMass = V4Add(invMassA, invMassB); + + Vec4V linDeltaX = V4Sub(linDelta0T0, linDelta1T0); + Vec4V linDeltaY = V4Sub(linDelta0T1, linDelta1T1); + Vec4V linDeltaZ = V4Sub(linDelta0T2, linDelta1T2); + + + while (currPtr < last) + { + + hdr = reinterpret_cast(currPtr); + + PX_ASSERT(hdr->type == DY_SC_TYPE_BLOCK_RB_CONTACT); + + currPtr = reinterpret_cast(const_cast(hdr) + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + bool hasMaxImpulse = (hdr->flag & SolverContactHeaderStepBlock::eHAS_MAX_IMPULSE) != 0; + + Vec4V* appliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numNormalConstr; + + SolverContactPointStepBlock* PX_RESTRICT contacts = reinterpret_cast(currPtr); + + Vec4V* maxImpulses; + currPtr = reinterpret_cast(contacts + numNormalConstr); + PxU32 maxImpulseMask = 0; + if (hasMaxImpulse) + { + maxImpulseMask = 0xFFFFFFFF; + maxImpulses = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V) * numNormalConstr; + } + else + { + maxImpulses = &vMax; + } + + + /*SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(currPtr); + if (numFrictionConstr) + currPtr += sizeof(SolverFrictionSharedData4);*/ + + Vec4V* frictionAppliedForce = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numFrictionConstr; + + const SolverContactFrictionStepBlock* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionStepBlock); + + Vec4V accumulatedNormalImpulse = vZero; + + const Vec4V angD0 = hdr->angDom0; + const Vec4V angD1 = hdr->angDom1; + + const Vec4V _normalT0 = hdr->normalX; + const Vec4V _normalT1 = hdr->normalY; + const Vec4V _normalT2 = hdr->normalZ; + + Vec4V contactNormalVel1 = V4Mul(linVel0T0, _normalT0); + Vec4V contactNormalVel3 = V4Mul(linVel1T0, _normalT0); + contactNormalVel1 = V4MulAdd(linVel0T1, _normalT1, contactNormalVel1); + contactNormalVel3 = V4MulAdd(linVel1T1, _normalT1, contactNormalVel3); + contactNormalVel1 = V4MulAdd(linVel0T2, _normalT2, contactNormalVel1); + contactNormalVel3 = V4MulAdd(linVel1T2, _normalT2, contactNormalVel3); + + const Vec4V maxPenBias = hdr->maxPenBias; + + Vec4V relVel1 = V4Sub(contactNormalVel1, contactNormalVel3); + + Vec4V deltaNormalV = V4Mul(linDeltaX, _normalT0); + deltaNormalV = V4MulAdd(linDeltaY, _normalT1, deltaNormalV); + deltaNormalV = V4MulAdd(linDeltaZ, _normalT2, deltaNormalV); + + Vec4V accumDeltaF = vZero; + + for (PxU32 i = 0; istaticFriction; + const Vec4V dynamicFric = hdr->dynamicFriction; + + const Vec4V maxFrictionImpulse = V4Mul(staticFric, accumulatedNormalImpulse); + const Vec4V maxDynFrictionImpulse = V4Mul(dynamicFric, accumulatedNormalImpulse); + const Vec4V negMaxDynFrictionImpulse = V4Neg(maxDynFrictionImpulse); + //const Vec4V negMaxFrictionImpulse = V4Neg(maxFrictionImpulse); + BoolV broken = BFFFF(); + + + for (PxU32 i = 0; ibroken = broken; + } + } + + PX_TRANSPOSE_34_44(linVel0T0, linVel0T1, linVel0T2, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_34_44(linVel1T0, linVel1T1, linVel1T2, linVel01, linVel11, linVel21, linVel31); + PX_TRANSPOSE_34_44(angState0T0, angState0T1, angState0T2, angState00, angState10, angState20, angState30); + PX_TRANSPOSE_34_44(angState1T0, angState1T1, angState1T2, angState01, angState11, angState21, angState31); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularVelocity.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularVelocity.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularVelocity.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularVelocity.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularVelocity.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularVelocity.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularVelocity.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularVelocity.isFinite()); + + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(angState00, &b00.angularVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(angState10, &b10.angularVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(angState20, &b20.angularVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + V4StoreA(angState30, &b30.angularVelocity.x); + + if (desc[0].bodyBIdx != 0) + { + V4StoreA(linVel01, &b01.linearVelocity.x); + V4StoreA(angState01, &b01.angularVelocity.x); + } + if (desc[1].bodyBIdx != 0) + { + V4StoreA(linVel11, &b11.linearVelocity.x); + V4StoreA(angState11, &b11.angularVelocity.x); + } + if (desc[2].bodyBIdx != 0) + { + V4StoreA(linVel21, &b21.linearVelocity.x); + V4StoreA(angState21, &b21.angularVelocity.x); + } + if (desc[3].bodyBIdx != 0) + { + V4StoreA(linVel31, &b31.linearVelocity.x); + V4StoreA(angState31, &b31.angularVelocity.x); + } + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularVelocity.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularVelocity.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularVelocity.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularVelocity.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularVelocity.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularVelocity.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularVelocity.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularVelocity.isFinite()); +} + + +void writeBackContact4_Block(const PxTGSSolverConstraintDesc* PX_RESTRICT desc, SolverContext* cache) +{ + PX_UNUSED(cache); + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + PxReal* PX_RESTRICT vForceWriteback0 = reinterpret_cast(desc[0].writeBack); + PxReal* PX_RESTRICT vForceWriteback1 = reinterpret_cast(desc[1].writeBack); + PxReal* PX_RESTRICT vForceWriteback2 = reinterpret_cast(desc[2].writeBack); + PxReal* PX_RESTRICT vForceWriteback3 = reinterpret_cast(desc[3].writeBack); + + //const PxU8 type = *desc[0].constraint; + const PxU32 contactSize = sizeof(SolverContactPointStepBlock); + const PxU32 frictionSize = sizeof(SolverContactFrictionStepBlock); + + + Vec4V normalForce = V4Zero(); + + + //We'll need this. + //const Vec4V vZero = V4Zero(); + + bool writeBackThresholds[4] = { false, false, false, false }; + + while ((currPtr < last)) + { + SolverContactHeaderStepBlock* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(hdr + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + Vec4V* PX_RESTRICT appliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numNormalConstr; + + //SolverContactBatchPointBase4* PX_RESTRICT contacts = (SolverContactBatchPointBase4*)currPtr; + currPtr += (numNormalConstr * contactSize); + + bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + if (hasMaxImpulse) + currPtr += sizeof(Vec4V) * numNormalConstr; + + currPtr += sizeof(Vec4V)*numFrictionConstr; + + //SolverContactFrictionBase4* PX_RESTRICT frictions = (SolverContactFrictionBase4*)currPtr; + currPtr += (numFrictionConstr * frictionSize); + + writeBackThresholds[0] = hdr->flags[0] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[1] = hdr->flags[1] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[2] = hdr->flags[2] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[3] = hdr->flags[3] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + + + for (PxU32 i = 0; inumNormalConstrs[0]) + FStore(appliedForce0, vForceWriteback0++); + if (vForceWriteback1 && i < hdr->numNormalConstrs[1]) + FStore(appliedForce1, vForceWriteback1++); + if (vForceWriteback2 && i < hdr->numNormalConstrs[2]) + FStore(appliedForce2, vForceWriteback2++); + if (vForceWriteback3 && i < hdr->numNormalConstrs[3]) + FStore(appliedForce3, vForceWriteback3++); + } + + if (numFrictionConstr) + { + PX_ALIGN(16, PxU32 broken[4]); + BStoreA(hdr->broken, broken); + + PxU8* frictionCounts = hdr->numNormalConstrs; + + for (PxU32 a = 0; a < 4; ++a) + { + if (frictionCounts[a] && broken[a]) + *hdr->frictionBrokenWritebackByte[a] = 1; // PT: bad L2 miss here + } + } + } + + PX_UNUSED(writeBackThresholds); + +#if 0 + if (cache) + { + + PX_ALIGN(16, PxReal nf[4]); + V4StoreA(normalForce, nf); + + Sc::ShapeInteraction** shapeInteractions = reinterpret_cast(desc[0].constraint)->shapeInteraction; + + for (PxU32 a = 0; a < 4; ++a) + { + if (writeBackThresholds[a] && desc[a].linkIndexA == PxSolverConstraintDesc::NO_LINK && desc[a].linkIndexB == PxSolverConstraintDesc::NO_LINK && + nf[a] != 0.f && (bd0[a]->reportThreshold < PX_MAX_REAL || bd1[a]->reportThreshold < PX_MAX_REAL)) + { + ThresholdStreamElement elt; + elt.normalForce = nf[a]; + elt.threshold = PxMin(bd0[a]->reportThreshold, bd1[a]->reportThreshold); + elt.nodeIndexA = bd0[a]->nodeIndex; + elt.nodeIndexB = bd1[a]->nodeIndex; + elt.shapeInteraction = shapeInteractions[a]; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + PX_ASSERT(cache.mThresholdStreamIndex < cache.mThresholdStreamLength); + cache.mThresholdStream[cache.mThresholdStreamIndex++] = elt; + } + } + } +#endif +} + +void solveContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime) +{ + solveContact4_Block(desc + hdr.mStartIndex, doFriction, minPenetration, elapsedTime); +} + +void writeBackContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, SolverContext* cache) +{ + writeBackContact4_Block(desc + hdr.mStartIndex, cache); +} + +PX_FORCE_INLINE Vec4V V4Dot3(const Vec4V& x0, const Vec4V& y0, const Vec4V& z0, const Vec4V& x1, const Vec4V& y1, const Vec4V& z1) +{ + return V4MulAdd(x0, x1, V4MulAdd(y0, y1, V4Mul(z0, z1))); +} + + +void solve1DStep4(const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxTGSSolverBodyTxInertia* const txInertias, + const PxReal elapsedTimeF32) +{ + PxU8* PX_RESTRICT bPtr = desc->constraint; + if (bPtr == NULL) + return; + + const FloatV elapsedTime = FLoad(elapsedTimeF32); + + PxTGSSolverBodyVel& b00 = *desc[0].bodyA; + PxTGSSolverBodyVel& b01 = *desc[0].bodyB; + PxTGSSolverBodyVel& b10 = *desc[1].bodyA; + PxTGSSolverBodyVel& b11 = *desc[1].bodyB; + PxTGSSolverBodyVel& b20 = *desc[2].bodyA; + PxTGSSolverBodyVel& b21 = *desc[2].bodyB; + PxTGSSolverBodyVel& b30 = *desc[3].bodyA; + PxTGSSolverBodyVel& b31 = *desc[3].bodyB; + + const PxTGSSolverBodyTxInertia& txI00 = txInertias[desc[0].bodyAIdx]; + const PxTGSSolverBodyTxInertia& txI01 = txInertias[desc[0].bodyBIdx]; + const PxTGSSolverBodyTxInertia& txI10 = txInertias[desc[1].bodyAIdx]; + const PxTGSSolverBodyTxInertia& txI11 = txInertias[desc[1].bodyBIdx]; + const PxTGSSolverBodyTxInertia& txI20 = txInertias[desc[2].bodyAIdx]; + const PxTGSSolverBodyTxInertia& txI21 = txInertias[desc[2].bodyBIdx]; + const PxTGSSolverBodyTxInertia& txI30 = txInertias[desc[3].bodyAIdx]; + const PxTGSSolverBodyTxInertia& txI31 = txInertias[desc[3].bodyBIdx]; + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&b01.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularVelocity.x); + Vec4V angState01 = V4LoadA(&b01.angularVelocity.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&b11.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularVelocity.x); + Vec4V angState11 = V4LoadA(&b11.angularVelocity.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&b21.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularVelocity.x); + Vec4V angState21 = V4LoadA(&b21.angularVelocity.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&b31.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularVelocity.x); + Vec4V angState31 = V4LoadA(&b31.angularVelocity.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2; + Vec4V linVel1T0, linVel1T1, linVel1T2; + Vec4V angState0T0, angState0T1, angState0T2; + Vec4V angState1T0, angState1T1, angState1T2; + + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2); + PX_TRANSPOSE_44_34(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2); + PX_TRANSPOSE_44_34(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2); + + + Vec4V linDelta00 = V4LoadA(&b00.deltaLinDt.x); + Vec4V linDelta01 = V4LoadA(&b01.deltaLinDt.x); + Vec4V angDelta00 = V4LoadA(&b00.deltaAngDt.x); + Vec4V angDelta01 = V4LoadA(&b01.deltaAngDt.x); + + Vec4V linDelta10 = V4LoadA(&b10.deltaLinDt.x); + Vec4V linDelta11 = V4LoadA(&b11.deltaLinDt.x); + Vec4V angDelta10 = V4LoadA(&b10.deltaAngDt.x); + Vec4V angDelta11 = V4LoadA(&b11.deltaAngDt.x); + + Vec4V linDelta20 = V4LoadA(&b20.deltaLinDt.x); + Vec4V linDelta21 = V4LoadA(&b21.deltaLinDt.x); + Vec4V angDelta20 = V4LoadA(&b20.deltaAngDt.x); + Vec4V angDelta21 = V4LoadA(&b21.deltaAngDt.x); + + Vec4V linDelta30 = V4LoadA(&b30.deltaLinDt.x); + Vec4V linDelta31 = V4LoadA(&b31.deltaLinDt.x); + Vec4V angDelta30 = V4LoadA(&b30.deltaAngDt.x); + Vec4V angDelta31 = V4LoadA(&b31.deltaAngDt.x); + + Vec4V linDelta0T0, linDelta0T1, linDelta0T2; + Vec4V linDelta1T0, linDelta1T1, linDelta1T2; + Vec4V angDelta0T0, angDelta0T1, angDelta0T2; + Vec4V angDelta1T0, angDelta1T1, angDelta1T2; + + + PX_TRANSPOSE_44_34(linDelta00, linDelta10, linDelta20, linDelta30, linDelta0T0, linDelta0T1, linDelta0T2); + PX_TRANSPOSE_44_34(linDelta01, linDelta11, linDelta21, linDelta31, linDelta1T0, linDelta1T1, linDelta1T2); + PX_TRANSPOSE_44_34(angDelta00, angDelta10, angDelta20, angDelta30, angDelta0T0, angDelta0T1, angDelta0T2); + PX_TRANSPOSE_44_34(angDelta01, angDelta11, angDelta21, angDelta31, angDelta1T0, angDelta1T1, angDelta1T2); + + + + + const SolverConstraint1DHeaderStep4* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DStep4* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeaderStep4)); + + + Vec4V invInertia00X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI00.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia00Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI00.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia00Z = Vec4V_From_Vec3V(V3LoadU(txI00.sqrtInvInertia.column2)); + + Vec4V invInertia10X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI10.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia10Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI10.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia10Z = Vec4V_From_Vec3V(V3LoadU(txI10.sqrtInvInertia.column2)); + + Vec4V invInertia20X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI20.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia20Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI20.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia20Z = Vec4V_From_Vec3V(V3LoadU(txI20.sqrtInvInertia.column2)); + + Vec4V invInertia30X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI30.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia30Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI30.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia30Z = Vec4V_From_Vec3V(V3LoadU(txI30.sqrtInvInertia.column2)); + + Vec4V invInertia01X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI01.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia01Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI01.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia01Z = Vec4V_From_Vec3V(V3LoadU(txI01.sqrtInvInertia.column2)); + + Vec4V invInertia11X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI11.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia11Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI11.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia11Z = Vec4V_From_Vec3V(V3LoadU(txI11.sqrtInvInertia.column2)); + + Vec4V invInertia21X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI21.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia21Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI21.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia21Z = Vec4V_From_Vec3V(V3LoadU(txI21.sqrtInvInertia.column2)); + + Vec4V invInertia31X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI31.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia31Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI31.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia31Z = Vec4V_From_Vec3V(V3LoadU(txI31.sqrtInvInertia.column2)); + + Vec4V invInertia0X0, invInertia0X1, invInertia0X2; + Vec4V invInertia0Y0, invInertia0Y1, invInertia0Y2; + Vec4V invInertia0Z0, invInertia0Z1, invInertia0Z2; + + Vec4V invInertia1X0, invInertia1X1, invInertia1X2; + Vec4V invInertia1Y0, invInertia1Y1, invInertia1Y2; + Vec4V invInertia1Z0, invInertia1Z1, invInertia1Z2; + + PX_TRANSPOSE_44_34(invInertia00X, invInertia10X, invInertia20X, invInertia30X, invInertia0X0, invInertia0Y0, invInertia0Z0); + PX_TRANSPOSE_44_34(invInertia00Y, invInertia10Y, invInertia20Y, invInertia30Y, invInertia0X1, invInertia0Y1, invInertia0Z1); + PX_TRANSPOSE_44_34(invInertia00Z, invInertia10Z, invInertia20Z, invInertia30Z, invInertia0X2, invInertia0Y2, invInertia0Z2); + + PX_TRANSPOSE_44_34(invInertia01X, invInertia11X, invInertia21X, invInertia31X, invInertia1X0, invInertia1Y0, invInertia1Z0); + PX_TRANSPOSE_44_34(invInertia01Y, invInertia11Y, invInertia21Y, invInertia31Y, invInertia1X1, invInertia1Y1, invInertia1Z1); + PX_TRANSPOSE_44_34(invInertia01Z, invInertia11Z, invInertia21Z, invInertia31Z, invInertia1X2, invInertia1Y2, invInertia1Z2); + + const Vec4V invInertiaScale0 = header->angD0; + const Vec4V invInertiaScale1 = header->angD1; + + //KS - todo - load this a bit quicker... + Vec4V rot00 = V4LoadA(&txI00.deltaBody2World.q.x); + Vec4V rot01 = V4LoadA(&txI01.deltaBody2World.q.x); + Vec4V rot10 = V4LoadA(&txI10.deltaBody2World.q.x); + Vec4V rot11 = V4LoadA(&txI11.deltaBody2World.q.x); + Vec4V rot20 = V4LoadA(&txI20.deltaBody2World.q.x); + Vec4V rot21 = V4LoadA(&txI21.deltaBody2World.q.x); + Vec4V rot30 = V4LoadA(&txI30.deltaBody2World.q.x); + Vec4V rot31 = V4LoadA(&txI31.deltaBody2World.q.x); + + Vec4V rot0X, rot0Y, rot0Z, rot0W; + Vec4V rot1X, rot1Y, rot1Z, rot1W; + + PX_TRANSPOSE_44(rot00, rot10, rot20, rot30, rot0X, rot0Y, rot0Z, rot0W); + PX_TRANSPOSE_44(rot01, rot11, rot21, rot31, rot1X, rot1Y, rot1Z, rot1W); + + Vec4V raX, raY, raZ; + Vec4V rbX, rbY, rbZ; + + QuatRotate4(rot0X, rot0Y, rot0Z, rot0W, header->rAWorld[0], header->rAWorld[1], header->rAWorld[2], raX, raY, raZ); + QuatRotate4(rot1X, rot1Y, rot1Z, rot1W, header->rBWorld[0], header->rBWorld[1], header->rBWorld[2], rbX, rbY, rbZ); + + + const Vec4V raMotionX = V4Sub(V4Add(raX, linDelta0T0), header->rAWorld[0]); + const Vec4V raMotionY = V4Sub(V4Add(raY, linDelta0T1), header->rAWorld[1]); + const Vec4V raMotionZ = V4Sub(V4Add(raZ, linDelta0T2), header->rAWorld[2]); + const Vec4V rbMotionX = V4Sub(V4Add(rbX, linDelta1T0), header->rBWorld[0]); + const Vec4V rbMotionY = V4Sub(V4Add(rbY, linDelta1T1), header->rBWorld[1]); + const Vec4V rbMotionZ = V4Sub(V4Add(rbZ, linDelta1T2), header->rBWorld[2]); + + const Vec4V mass0 = header->invMass0D0; + const Vec4V mass1 = header->invMass1D1; + + const VecI32V orthoMask = I4Load(DY_SC_FLAG_ORTHO_TARGET); + const VecI32V limitMask = I4Load(DY_SC_FLAG_INEQUALITY); + const Vec4V zero = V4Zero(); + const Vec4V one = V4One(); + + Vec4V error0 = V4Add(header->angOrthoError[0], + V4Sub(V4Dot3(header->angOrthoAxis0X[0], header->angOrthoAxis0Y[0], header->angOrthoAxis0Z[0], angDelta0T0, angDelta0T1, angDelta0T2), + V4Dot3(header->angOrthoAxis1X[0], header->angOrthoAxis1Y[0], header->angOrthoAxis1Z[0], angDelta1T0, angDelta1T1, angDelta1T2))); + + Vec4V error1 = V4Add(header->angOrthoError[1], + V4Sub(V4Dot3(header->angOrthoAxis0X[1], header->angOrthoAxis0Y[1], header->angOrthoAxis0Z[1], angDelta0T0, angDelta0T1, angDelta0T2), + V4Dot3(header->angOrthoAxis1X[1], header->angOrthoAxis1Y[1], header->angOrthoAxis1Z[1], angDelta1T0, angDelta1T1, angDelta1T2))); + + Vec4V error2 = V4Add(header->angOrthoError[2], + V4Sub(V4Dot3(header->angOrthoAxis0X[2], header->angOrthoAxis0Y[2], header->angOrthoAxis0Z[2], angDelta0T0, angDelta0T1, angDelta0T2), + V4Dot3(header->angOrthoAxis1X[2], header->angOrthoAxis1Y[2], header->angOrthoAxis1Z[2], angDelta1T0, angDelta1T1, angDelta1T2))); + + for (PxU32 i = 0; icount; ++i, base++) + { + Ps::prefetchLine(base + 1); + SolverConstraint1DStep4& c = *base; + + const Vec4V cangVel0X = V4Add(c.ang0[0], V4NegMulSub(raZ, c.lin0[1], V4Mul(raY, c.lin0[2]))); + const Vec4V cangVel0Y = V4Add(c.ang0[1], V4NegMulSub(raX, c.lin0[2], V4Mul(raZ, c.lin0[0]))); + const Vec4V cangVel0Z = V4Add(c.ang0[2], V4NegMulSub(raY, c.lin0[0], V4Mul(raX, c.lin0[1]))); + + const Vec4V cangVel1X = V4Add(c.ang1[0], V4NegMulSub(rbZ, c.lin1[1], V4Mul(rbY, c.lin1[2]))); + const Vec4V cangVel1Y = V4Add(c.ang1[1], V4NegMulSub(rbX, c.lin1[2], V4Mul(rbZ, c.lin1[0]))); + const Vec4V cangVel1Z = V4Add(c.ang1[2], V4NegMulSub(rbY, c.lin1[0], V4Mul(rbX, c.lin1[1]))); + + const VecI32V flags = I4LoadA(reinterpret_cast(c.flags)); + + const BoolV useOrtho = VecI32V_IsEq(VecI32V_And(flags, orthoMask), orthoMask); + + + const Vec4V angOrthoCoefficient = V4Sel(useOrtho, one, zero); + + + Vec4V delAngVel0X = V4Mul(invInertia0X0, cangVel0X); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, cangVel0X); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, cangVel0X); + + delAngVel0X = V4MulAdd(invInertia0Y0, cangVel0Y, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, cangVel0Y, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, cangVel0Y, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, cangVel0Z, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, cangVel0Z, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, cangVel0Z, delAngVel0Z); + + Vec4V delAngVel1X = V4Mul(invInertia1X0, cangVel1X); + Vec4V delAngVel1Y = V4Mul(invInertia1X1, cangVel1X); + Vec4V delAngVel1Z = V4Mul(invInertia1X2, cangVel1X); + + delAngVel1X = V4MulAdd(invInertia1Y0, cangVel1Y, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, cangVel1Y, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, cangVel1Y, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, cangVel1Z, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, cangVel1Z, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, cangVel1Z, delAngVel1Z); + + Vec4V err = c.error; + { + const Vec4V proj0 = V4Mul(V4MulAdd(header->angOrthoAxis0X[0], delAngVel0X, V4MulAdd(header->angOrthoAxis0Y[0], delAngVel0Y, + V4MulAdd(header->angOrthoAxis0Z[0], delAngVel0Z, V4MulAdd(header->angOrthoAxis1X[0], delAngVel1X, + V4MulAdd(header->angOrthoAxis1Y[0], delAngVel1Y, V4Mul(header->angOrthoAxis1Z[0], delAngVel1Z)))))), header->angOrthoRecipResponse[0]); + + const Vec4V proj1 = V4Mul(V4MulAdd(header->angOrthoAxis0X[1], delAngVel0X, V4MulAdd(header->angOrthoAxis0Y[1], delAngVel0Y, + V4MulAdd(header->angOrthoAxis0Z[1], delAngVel0Z, V4MulAdd(header->angOrthoAxis1X[1], delAngVel1X, + V4MulAdd(header->angOrthoAxis1Y[1], delAngVel1Y, V4Mul(header->angOrthoAxis1Z[1], delAngVel1Z)))))), header->angOrthoRecipResponse[1]); + + const Vec4V proj2 = V4Mul(V4MulAdd(header->angOrthoAxis0X[2], delAngVel0X, V4MulAdd(header->angOrthoAxis0Y[2], delAngVel0Y, + V4MulAdd(header->angOrthoAxis0Z[2], delAngVel0Z, V4MulAdd(header->angOrthoAxis1X[2], delAngVel1X, + V4MulAdd(header->angOrthoAxis1Y[2], delAngVel1Y, V4Mul(header->angOrthoAxis1Z[2], delAngVel1Z)))))), header->angOrthoRecipResponse[2]); + + const Vec4V delta0X = V4MulAdd(header->angOrthoAxis0X[0], proj0, V4MulAdd(header->angOrthoAxis0X[1], proj1, V4Mul(header->angOrthoAxis0X[2], proj2))); + const Vec4V delta0Y = V4MulAdd(header->angOrthoAxis0Y[0], proj0, V4MulAdd(header->angOrthoAxis0Y[1], proj1, V4Mul(header->angOrthoAxis0Y[2], proj2))); + const Vec4V delta0Z = V4MulAdd(header->angOrthoAxis0Z[0], proj0, V4MulAdd(header->angOrthoAxis0Z[1], proj1, V4Mul(header->angOrthoAxis0Z[2], proj2))); + + const Vec4V delta1X = V4MulAdd(header->angOrthoAxis1X[0], proj0, V4MulAdd(header->angOrthoAxis1X[1], proj1, V4Mul(header->angOrthoAxis1X[2], proj2))); + const Vec4V delta1Y = V4MulAdd(header->angOrthoAxis1Y[0], proj0, V4MulAdd(header->angOrthoAxis1Y[1], proj1, V4Mul(header->angOrthoAxis1Y[2], proj2))); + const Vec4V delta1Z = V4MulAdd(header->angOrthoAxis1Z[0], proj0, V4MulAdd(header->angOrthoAxis1Z[1], proj1, V4Mul(header->angOrthoAxis1Z[2], proj2))); + + + delAngVel0X = V4NegMulSub(delta0X, angOrthoCoefficient, delAngVel0X); + delAngVel0Y = V4NegMulSub(delta0Y, angOrthoCoefficient, delAngVel0Y); + delAngVel0Z = V4NegMulSub(delta0Z, angOrthoCoefficient, delAngVel0Z); + + delAngVel1X = V4NegMulSub(delta1X, angOrthoCoefficient, delAngVel1X); + delAngVel1Y = V4NegMulSub(delta1Y, angOrthoCoefficient, delAngVel1Y); + delAngVel1Z = V4NegMulSub(delta1Z, angOrthoCoefficient, delAngVel1Z); + + err = V4Sub(err, V4Mul(V4MulAdd(error0, proj0, V4MulAdd(error1, proj1, V4Mul(error2, proj2))), angOrthoCoefficient)); + } + + Vec4V ang0IX = V4Mul(invInertia0X0, delAngVel0X); + Vec4V ang0IY = V4Mul(invInertia0X1, delAngVel0X); + Vec4V ang0IZ = V4Mul(invInertia0X2, delAngVel0X); + + ang0IX = V4MulAdd(invInertia0Y0, delAngVel0Y, ang0IX); + ang0IY = V4MulAdd(invInertia0Y1, delAngVel0Y, ang0IY); + ang0IZ = V4MulAdd(invInertia0Y2, delAngVel0Y, ang0IZ); + + ang0IX = V4MulAdd(invInertia0Z0, delAngVel0Z, ang0IX); + ang0IY = V4MulAdd(invInertia0Z1, delAngVel0Z, ang0IY); + ang0IZ = V4MulAdd(invInertia0Z2, delAngVel0Z, ang0IZ); + + Vec4V ang1IX = V4Mul(invInertia1X0, delAngVel1X); + Vec4V ang1IY = V4Mul(invInertia1X1, delAngVel1X); + Vec4V ang1IZ = V4Mul(invInertia1X2, delAngVel1X); + + ang1IX = V4MulAdd(invInertia1Y0, delAngVel1Y, ang1IX); + ang1IY = V4MulAdd(invInertia1Y1, delAngVel1Y, ang1IY); + ang1IZ = V4MulAdd(invInertia1Y2, delAngVel1Y, ang1IZ); + + ang1IX = V4MulAdd(invInertia1Z0, delAngVel1Z, ang1IX); + ang1IY = V4MulAdd(invInertia1Z1, delAngVel1Z, ang1IY); + ang1IZ = V4MulAdd(invInertia1Z2, delAngVel1Z, ang1IZ); + + + const Vec4V clinVel0X = c.lin0[0]; + const Vec4V clinVel0Y = c.lin0[1]; + const Vec4V clinVel0Z = c.lin0[2]; + + const Vec4V clinVel1X = c.lin1[0]; + const Vec4V clinVel1Y = c.lin1[1]; + const Vec4V clinVel1Z = c.lin1[2]; + + + const Vec4V clinVel0X_ = c.lin0[0]; + const Vec4V clinVel0Y_ = c.lin0[1]; + const Vec4V clinVel0Z_ = c.lin0[2]; + + const Vec4V clinVel1X_ = c.lin1[0]; + const Vec4V clinVel1Y_ = c.lin1[1]; + const Vec4V clinVel1Z_ = c.lin1[2]; + + + //KS - compute raXnI and effective mass. Unfortunately, the joints are noticeably less stable if we don't do this each + //iteration. It's skippable. If we do that, there's no need for the invInertiaTensors + + + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0X, delAngVel0X, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0Z, delAngVel0Z))); + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1X, delAngVel1X, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1Z, delAngVel1Z))); + + const Vec4V dotRbXnAngDelta0 = V4MulAdd(delAngVel0X, angDelta0T0, V4MulAdd(delAngVel0Y, angDelta0T1, V4Mul(delAngVel0Z, angDelta0T2))); + const Vec4V dotRbXnAngDelta1 = V4MulAdd(delAngVel1X, angDelta1T0, V4MulAdd(delAngVel1Y, angDelta1T1, V4Mul(delAngVel1Z, angDelta1T2))); + + const Vec4V dotRaMotion = V4MulAdd(clinVel0X_, raMotionX, V4MulAdd(clinVel0Y_, raMotionY, V4Mul(clinVel0Z_, raMotionZ))); + const Vec4V dotRbMotion = V4MulAdd(clinVel1X_, rbMotionX, V4MulAdd(clinVel1Y_, rbMotionY, V4Mul(clinVel1Z_, rbMotionZ))); + + const Vec4V deltaAng = V4Mul(c.angularErrorScale, V4Sub(dotRbXnAngDelta0, dotRbXnAngDelta1)); + const Vec4V error = V4NegScaleSub(c.velTarget, elapsedTime, V4Add(V4Add(err, V4Sub(dotRaMotion, dotRbMotion)), deltaAng)); + + const Vec4V dotClinVel0 = V4MulAdd(clinVel0X, clinVel0X, V4MulAdd(clinVel0Y, clinVel0Y, V4Mul(clinVel0Z, clinVel0Z))); + const Vec4V dotClinVel1 = V4MulAdd(clinVel1X, clinVel1X, V4MulAdd(clinVel1Y, clinVel1Y, V4Mul(clinVel1Z, clinVel1Z))); + + const Vec4V resp0 = V4MulAdd(mass0, dotClinVel0, V4Mul(invInertiaScale0, dotDelAngVel0)); + const Vec4V resp1 = V4MulAdd(mass1, dotClinVel1, V4Mul(invInertiaScale1, dotDelAngVel1)); + const Vec4V response = V4Add(resp0, resp1); + const Vec4V recipResponse = V4Sel(V4IsGrtr(response, V4Zero()), V4Recip(response), V4Zero()); + + + const Vec4V vMul = V4Mul(recipResponse, c.velMultiplier); + + const BoolV isLimitConstraint = VecI32V_IsEq(VecI32V_And(flags, limitMask), limitMask); + + const Vec4V minBias = V4Sel(isLimitConstraint, V4Neg(Vec4V_From_FloatV(FMax())), V4Neg(c.maxBias)); + const Vec4V unclampedBias = V4Mul(error, c.biasScale); + const Vec4V bias = V4Clamp(unclampedBias, minBias, c.maxBias); + + const Vec4V constant = V4Mul(recipResponse, V4Add(bias, c.velTarget)); + + const Vec4V normalVel0 = V4MulAdd(clinVel0X_, linVel0T0, V4MulAdd(clinVel0Y_, linVel0T1, V4Mul(clinVel0Z_, linVel0T2))); + const Vec4V normalVel1 = V4MulAdd(clinVel1X_, linVel1T0, V4MulAdd(clinVel1Y_, linVel1T1, V4Mul(clinVel1Z_, linVel1T2))); + + const Vec4V angVel0 = V4MulAdd(delAngVel0X, angState0T0, V4MulAdd(delAngVel0Y, angState0T1, V4Mul(delAngVel0Z, angState0T2))); + const Vec4V angVel1 = V4MulAdd(delAngVel1X, angState1T0, V4MulAdd(delAngVel1Y, angState1T1, V4Mul(delAngVel1Z, angState1T2))); + + const Vec4V normalVel = V4Add(V4Sub(normalVel0, normalVel1), V4Sub(angVel0, angVel1)); + + + const Vec4V unclampedForce = V4MulAdd(c.impulseMultiplier, c.appliedForce, V4MulAdd(vMul, normalVel, constant)); + const Vec4V clampedForce = V4Clamp(unclampedForce, c.minImpulse, c.maxImpulse); + const Vec4V deltaF = V4Sub(clampedForce, c.appliedForce); + + c.appliedForce = clampedForce; + + const Vec4V deltaFIM0 = V4Mul(deltaF, mass0); + const Vec4V deltaFIM1 = V4Mul(deltaF, mass1); + const Vec4V angDetaF0 = V4Mul(deltaF, invInertiaScale0); + const Vec4V angDetaF1 = V4Mul(deltaF, invInertiaScale1); + + + linVel0T0 = V4MulAdd(clinVel0X_, deltaFIM0, linVel0T0); + linVel1T0 = V4NegMulSub(clinVel1X_, deltaFIM1, linVel1T0); + angState0T0 = V4MulAdd(delAngVel0X, angDetaF0, angState0T0); + angState1T0 = V4NegMulSub(delAngVel1X, angDetaF1, angState1T0); + + linVel0T1 = V4MulAdd(clinVel0Y_, deltaFIM0, linVel0T1); + linVel1T1 = V4NegMulSub(clinVel1Y_, deltaFIM1, linVel1T1); + angState0T1 = V4MulAdd(delAngVel0Y, angDetaF0, angState0T1); + angState1T1 = V4NegMulSub(delAngVel1Y, angDetaF1, angState1T1); + + linVel0T2 = V4MulAdd(clinVel0Z_, deltaFIM0, linVel0T2); + linVel1T2 = V4NegMulSub(clinVel1Z_, deltaFIM1, linVel1T2); + angState0T2 = V4MulAdd(delAngVel0Z, angDetaF0, angState0T2); + angState1T2 = V4NegMulSub(delAngVel1Z, angDetaF1, angState1T2); + + } + + PX_TRANSPOSE_34_44(linVel0T0, linVel0T1, linVel0T2, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_34_44(linVel1T0, linVel1T1, linVel1T2, linVel01, linVel11, linVel21, linVel31); + PX_TRANSPOSE_34_44(angState0T0, angState0T1, angState0T2, angState00, angState10, angState20, angState30); + PX_TRANSPOSE_34_44(angState1T0, angState1T1, angState1T2, angState01, angState11, angState21, angState31); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularVelocity.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularVelocity.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularVelocity.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularVelocity.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularVelocity.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularVelocity.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularVelocity.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularVelocity.isFinite()); + + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(angState00, &b00.angularVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(angState10, &b10.angularVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(angState20, &b20.angularVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + V4StoreA(angState30, &b30.angularVelocity.x); + + V4StoreA(linVel01, &b01.linearVelocity.x); + V4StoreA(angState01, &b01.angularVelocity.x); + V4StoreA(linVel11, &b11.linearVelocity.x); + V4StoreA(angState11, &b11.angularVelocity.x); + V4StoreA(linVel21, &b21.linearVelocity.x); + V4StoreA(angState21, &b21.angularVelocity.x); + V4StoreA(linVel31, &b31.linearVelocity.x); + V4StoreA(angState31, &b31.angularVelocity.x); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularVelocity.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularVelocity.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularVelocity.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularVelocity.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularVelocity.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularVelocity.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularVelocity.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularVelocity.isFinite()); +} + + +void solve1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxTGSSolverBodyTxInertia* const txInertias, + const PxReal elapsedTime) +{ + solve1DStep4(desc + hdr.mStartIndex, txInertias, elapsedTime); +} + +void writeBack1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc) +{ + PX_UNUSED(hdr); + ConstraintWriteback* writeback0 = reinterpret_cast(desc[hdr.mStartIndex].writeBack); + ConstraintWriteback* writeback1 = reinterpret_cast(desc[hdr.mStartIndex + 1].writeBack); + ConstraintWriteback* writeback2 = reinterpret_cast(desc[hdr.mStartIndex + 2].writeBack); + ConstraintWriteback* writeback3 = reinterpret_cast(desc[hdr.mStartIndex + 3].writeBack); + + if (writeback0 || writeback1 || writeback2 || writeback3) + { + SolverConstraint1DHeaderStep4* header = reinterpret_cast(desc[hdr.mStartIndex].constraint); + SolverConstraint1DStep4* base = reinterpret_cast(desc[hdr.mStartIndex].constraint + sizeof(SolverConstraint1DHeaderStep4)); + + const Vec4V zero = V4Zero(); + Vec4V linX(zero), linY(zero), linZ(zero); + Vec4V angX(zero), angY(zero), angZ(zero); + + for (PxU32 i = 0; icount; i++) + { + const SolverConstraint1DStep4* c = base; + + //Load in flags + const VecI32V flags = I4LoadU(reinterpret_cast(&c->flags[0])); + //Work out masks + const VecI32V mask = I4Load(DY_SC_FLAG_OUTPUT_FORCE); + + const VecI32V masked = VecI32V_And(flags, mask); + const BoolV isEq = VecI32V_IsEq(masked, mask); + + const Vec4V appliedForce = V4Sel(isEq, c->appliedForce, zero); + + linX = V4MulAdd(c->lin0[0], appliedForce, linX); + linY = V4MulAdd(c->lin0[1], appliedForce, linY); + linZ = V4MulAdd(c->lin0[2], appliedForce, linZ); + + angX = V4MulAdd(c->ang0[0], appliedForce, angX); + angY = V4MulAdd(c->ang0[1], appliedForce, angY); + angZ = V4MulAdd(c->ang0[2], appliedForce, angZ); + + base++; + } + + //We need to do the cross product now + + angX = V4Sub(angX, V4NegMulSub(header->body0WorkOffset[0], linY, V4Mul(header->body0WorkOffset[1], linZ))); + angY = V4Sub(angY, V4NegMulSub(header->body0WorkOffset[1], linZ, V4Mul(header->body0WorkOffset[2], linX))); + angZ = V4Sub(angZ, V4NegMulSub(header->body0WorkOffset[2], linX, V4Mul(header->body0WorkOffset[0], linY))); + + const Vec4V linLenSq = V4MulAdd(linZ, linZ, V4MulAdd(linY, linY, V4Mul(linX, linX))); + const Vec4V angLenSq = V4MulAdd(angZ, angZ, V4MulAdd(angY, angY, V4Mul(angX, angX))); + + const Vec4V linLen = V4Sqrt(linLenSq); + const Vec4V angLen = V4Sqrt(angLenSq); + + const BoolV broken = BOr(V4IsGrtr(linLen, header->linBreakImpulse), V4IsGrtr(angLen, header->angBreakImpulse)); + + PX_ALIGN(16, PxU32 iBroken[4]); + BStoreA(broken, iBroken); + + Vec4V lin0, lin1, lin2, lin3; + Vec4V ang0, ang1, ang2, ang3; + + PX_TRANSPOSE_34_44(linX, linY, linZ, lin0, lin1, lin2, lin3); + PX_TRANSPOSE_34_44(angX, angY, angZ, ang0, ang1, ang2, ang3); + + if (writeback0) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin0), writeback0->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang0), writeback0->angularImpulse); + writeback0->broken = header->breakable[0] ? PxU32(iBroken[0] != 0) : 0; + } + if (writeback1) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin1), writeback1->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang1), writeback1->angularImpulse); + writeback1->broken = header->breakable[1] ? PxU32(iBroken[1] != 0) : 0; + } + if (writeback2) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin2), writeback2->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang2), writeback2->angularImpulse); + writeback2->broken = header->breakable[2] ? PxU32(iBroken[2] != 0) : 0; + } + if (writeback3) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin3), writeback3->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang3), writeback3->angularImpulse); + writeback3->broken = header->breakable[3] ? PxU32(iBroken[3] != 0) : 0; + } + } +} + + +void concludeContact4_Block(const PxTGSSolverConstraintDesc* PX_RESTRICT desc) +{ + PX_UNUSED(desc); + //const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + ////hopefully pointer aliasing doesn't bite. + //PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + //const Vec4V zero = V4Zero(); + + ////const PxU8 type = *desc[0].constraint; + //const PxU32 contactSize = sizeof(SolverContactPointStepBlock); + //const PxU32 frictionSize = sizeof(SolverContactFrictionStepBlock); + + //while ((currPtr < last)) + //{ + // SolverContactHeaderStepBlock* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + // currPtr = reinterpret_cast(hdr + 1); + + // const PxU32 numNormalConstr = hdr->numNormalConstr; + // const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + // //Applied forces + // currPtr += sizeof(Vec4V)*numNormalConstr; + + // //SolverContactPointStepBlock* PX_RESTRICT contacts = reinterpret_cast(currPtr); + // currPtr += (numNormalConstr * contactSize); + + // bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + // if (hasMaxImpulse) + // currPtr += sizeof(Vec4V) * numNormalConstr; + + // currPtr += sizeof(Vec4V)*numFrictionConstr; + + // SolverContactFrictionStepBlock* PX_RESTRICT frictions = reinterpret_cast(currPtr); + // currPtr += (numFrictionConstr * frictionSize); + + // /*for (PxU32 i = 0; iconstraint; + if (bPtr == NULL) + return; + + + const SolverConstraint1DHeaderStep4* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DStep4* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeaderStep4)); + + const VecI32V mask = I4Load(DY_SC_FLAG_KEEP_BIAS); + const Vec4V zero = V4Zero(); + + for (PxU32 i = 0; icount; ++i, base++) + { + Ps::prefetchLine(base + 1); + SolverConstraint1DStep4& c = *base; + + const VecI32V flags = I4LoadA(reinterpret_cast(c.flags)); + + const BoolV keepBias = VecI32V_IsEq(VecI32V_And(flags, mask), mask); + + c.biasScale = V4Sel(keepBias, c.biasScale, zero); + } +} + + +void solveConcludeContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, + const PxReal elapsedTime) +{ + solveContact4_Block(desc + hdr.mStartIndex, true, -PX_MAX_F32, elapsedTime); + concludeContact4_Block(desc + hdr.mStartIndex); +} + +void solveConclude1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxTGSSolverBodyTxInertia* const txInertias, + const PxReal elapsedTime) +{ + solve1DStep4(desc + hdr.mStartIndex, txInertias, elapsedTime); + conclude1DStep4(desc + hdr.mStartIndex); +} + + + + + + +} +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp new file mode 100644 index 000000000..2c779c754 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp @@ -0,0 +1,3307 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsTime.h" +#include "PsAtomic.h" +#include "PxvDynamics.h" + +#include "common/PxProfileZone.h" +#include "PxsRigidBody.h" +#include "PxsContactManager.h" +#include "DyTGSDynamics.h" +#include "DyBodyCoreIntegrator.h" +#include "DySolverCore.h" +#include "DySolverControl.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DyArticulationContactPrep.h" +#include "DySolverBody.h" + +#include "DyConstraintPrep.h" +#include "DyConstraintPartition.h" +#include "DyArticulation.h" + +#include "CmFlushPool.h" +#include "DyArticulationPImpl.h" +#include "PxsMaterialManager.h" +#include "DySolverContactPF4.h" +#include "DyContactReduction.h" +#include "PxcNpContactPrepShared.h" +#include "DyContactPrep.h" +#include "DySolverControlPF.h" +#include "PxSceneDesc.h" +#include "PxsSimpleIslandManager.h" +#include "PxvNphaseImplementationContext.h" +#include "PxsContactManagerState.h" +#include "PxsDefaultMemoryManager.h" +#include "DyContactPrepShared.h" +#include "DySolverContext.h" +#include "DyDynamics.h" +#include "DySolverConstraint1D.h" +#include "DyTGSPartition.h" +#include "PxvSimStats.h" + +#define PX_USE_BLOCK_SOLVER 1 +#define PX_USE_BLOCK_1D 1 + + +namespace physx +{ +namespace Dy +{ + +Context* createTGSDynamicsContext(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, Cm::FlushPool& taskPool, + PxvSimStats& simStats, PxTaskManager* taskManager, Ps::VirtualAllocatorCallback* allocatorCallback, PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, PxU64 contextID, + const bool enableStabilization, const bool useEnhancedDeterminism, const bool useAdaptiveForce, + const PxReal lengthScale + ) +{ + return DynamicsTGSContext::create(memBlockPool, scratchAllocator, taskPool, simStats, taskManager, allocatorCallback, materialManager, accurateIslandSim, + contextID, enableStabilization, useEnhancedDeterminism, useAdaptiveForce, lengthScale); +} + +// PT: TODO: consider removing this function. We already have "createDynamicsContext". +DynamicsTGSContext* DynamicsTGSContext::create(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocatorCallback, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal lengthScale + ) +{ + // PT: TODO: inherit from UserAllocated, remove placement new + DynamicsTGSContext* dc = reinterpret_cast(PX_ALLOC(sizeof(DynamicsTGSContext), "DynamicsTGSContext")); + if (dc) + { + new(dc)DynamicsTGSContext(memBlockPool, scratchAllocator, taskPool, simStats, taskManager, allocatorCallback, materialManager, accurateIslandSim, contextID, enableStabilization, useEnhancedDeterminism, useAdaptiveForce, lengthScale); + } + return dc; +} + +void DynamicsTGSContext::destroy() +{ + this->~DynamicsTGSContext(); + PX_FREE(this); +} + +void DynamicsTGSContext::resetThreadContexts() +{ + PxcThreadCoherentCacheIterator threadContextIt(mThreadContextPool); + ThreadContext* threadContext = threadContextIt.getNext(); + + while (threadContext != NULL) + { + threadContext->reset(); + threadContext = threadContextIt.getNext(); + } +} + +PX_FORCE_INLINE PxVec3 safeRecip(const PxVec3& v) +{ + return PxVec3(v.x == 0.f ? 0.f : 1.f/v.x, v.y == 0.f ? 0.f : 1.f/v.y, v.z == 0.f ? 0.f : 1.f/v.z); +} + +void copyToSolverBodyDataStep(const PxVec3& linearVelocity, const PxVec3& angularVelocity, const PxReal invMass, const PxVec3& invInertia, const PxTransform& globalPose, + const PxReal maxDepenetrationVelocity, const PxReal maxContactImpulse, const PxU32 nodeIndex, const PxReal reportThreshold, + const PxReal maxAngVelSq, PxU32 lockFlags, bool isKinematic, + PxTGSSolverBodyVel& solverVel, PxTGSSolverBodyTxInertia& solverBodyTxInertia, PxStepSolverBodyData& solverBodyData) +{ + const PxMat33 rotation(globalPose.q); + + const PxVec3 sqrtInvInertia = computeSafeSqrtInertia(invInertia); + + const PxVec3 sqrtBodySpaceInertia = safeRecip(sqrtInvInertia); + + Cm::transformInertiaTensor(sqrtInvInertia, rotation, solverBodyTxInertia.sqrtInvInertia); + + solverBodyTxInertia.deltaBody2World.p = globalPose.p; + solverBodyTxInertia.deltaBody2World.q = PxQuat(PxIdentity); + + PxMat33 sqrtInertia; + Cm::transformInertiaTensor(sqrtBodySpaceInertia, rotation, sqrtInertia); + + PxVec3 lv = linearVelocity; + PxVec3 av = angularVelocity; + + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + lv.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + lv.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + lv.z = 0.f; + + //KS - technically, we can zero the inertia columns and produce stiffer constraints. However, this can cause numerical issues with the + //joint solver, which is fixed by disabling joint preprocessing and setting minResponseThreshold to some reasonable value > 0. However, until + //this is handled automatically, it's probably better not to zero these inertia rows + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + { + av.x = 0.f; + //data.sqrtInvInertia.column0 = PxVec3(0.f); + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + { + av.y = 0.f; + //data.sqrtInvInertia.column1 = PxVec3(0.f); + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + { + av.z = 0.f; + //data.sqrtInvInertia.column2 = PxVec3(0.f); + } + } + + solverVel.linearVelocity = lv; + solverVel.angularVelocity = sqrtInertia * av; + solverVel.deltaLinDt = PxVec3(0.f); + solverVel.deltaAngDt = PxVec3(0.f); + solverVel.lockFlags = PxU16(lockFlags); + solverVel.isKinematic = isKinematic; + solverVel.maxAngVel = PxSqrt(maxAngVelSq); + solverVel.partitionMask = 0; + + + + solverBodyData.nodeIndex = nodeIndex; + solverBodyData.invMass = invMass; + solverBodyData.penBiasClamp = maxDepenetrationVelocity; + solverBodyData.maxContactImpulse = maxContactImpulse; + solverBodyData.reportThreshold = reportThreshold; + solverBodyData.originalLinearVelocity = lv; + solverBodyData.originalAngularVelocity = av; + + PX_ASSERT(lv.isFinite()); + PX_ASSERT(av.isFinite()); +} + + + +void copyToSolverBodyDataStepKinematic(const PxVec3& linearVelocity, const PxVec3& angularVelocity, const PxTransform& globalPose, + const PxReal maxDepenetrationVelocity, const PxReal maxContactImpulse, const PxU32 nodeIndex, const PxReal reportThreshold, + const PxReal maxAngVelSq, + PxTGSSolverBodyVel& solverVel, PxTGSSolverBodyTxInertia& solverBodyTxInertia, PxStepSolverBodyData& solverBodyData) +{ + const PxMat33 rotation(globalPose.q); + + solverBodyTxInertia.deltaBody2World.p = globalPose.p; + solverBodyTxInertia.deltaBody2World.q = PxQuat(PxIdentity); + + solverBodyTxInertia.sqrtInvInertia = PxMat33(PxVec3(0.f), PxVec3(0.f), PxVec3(0.f)); + + solverVel.linearVelocity = PxVec3(0.f); + solverVel.angularVelocity = PxVec3(0.f); + solverVel.deltaLinDt = PxVec3(0.f); + solverVel.deltaAngDt = PxVec3(0.f); + solverVel.lockFlags = 0; + solverVel.isKinematic = true; + solverVel.maxAngVel = PxSqrt(maxAngVelSq); + solverVel.partitionMask = 0; + + solverBodyData.nodeIndex = nodeIndex; + solverBodyData.invMass = 0.f; + solverBodyData.penBiasClamp = maxDepenetrationVelocity; + solverBodyData.maxContactImpulse = maxContactImpulse; + solverBodyData.reportThreshold = reportThreshold; + solverBodyData.originalLinearVelocity = linearVelocity; + solverBodyData.originalAngularVelocity = angularVelocity; +} + + +// =========================== Basic methods + + +DynamicsTGSContext::DynamicsTGSContext(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocatorCallback, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal lengthScale + ) : + Dy::Context(accurateIslandSim, allocatorCallback, simStats, enableStabilization, useEnhancedDeterminism, useAdaptiveForce, PX_MAX_F32), + mThreadContextPool(memBlockPool), + mMaterialManager(materialManager), + mLengthScale(lengthScale), + mScratchAllocator(scratchAllocator), + mTaskPool(taskPool), + mTaskManager(taskManager), + mContextID(contextID) +{ + createThresholdStream(*allocatorCallback); + createForceChangeThresholdStream(*allocatorCallback); + mExceededForceThresholdStream[0] = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ExceededForceThresholdStream[0]")), ThresholdStream(*allocatorCallback)); + mExceededForceThresholdStream[1] = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ExceededForceThresholdStream[1]")), ThresholdStream(*allocatorCallback)); + mThresholdStreamOut = 0; + mCurrentIndex = 0; + + PxMemZero(&mWorldSolverBodyVel, sizeof(mWorldSolverBodyVel)); + + mWorldSolverBodyVel.lockFlags = 0; + mWorldSolverBodyVel.isKinematic = false; + mWorldSolverBodyTxInertia.sqrtInvInertia = PxMat33(PxZero); + mWorldSolverBodyTxInertia.deltaBody2World = PxTransform(PxIdentity); + + mWorldSolverBodyData2.penBiasClamp = -PX_MAX_REAL; + mWorldSolverBodyData2.maxContactImpulse = PX_MAX_REAL; + mWorldSolverBodyData2.nodeIndex = IG_INVALID_NODE; + mWorldSolverBodyData2.invMass = 0; + mWorldSolverBodyData2.reportThreshold = PX_MAX_REAL; + mWorldSolverBodyData2.originalLinearVelocity = PxVec3(0.f); + mWorldSolverBodyData2.originalAngularVelocity = PxVec3(0.f); + + +} + +DynamicsTGSContext::~DynamicsTGSContext() +{ + if (mExceededForceThresholdStream[0]) + { + mExceededForceThresholdStream[0]->~ThresholdStream(); + PX_FREE(mExceededForceThresholdStream[0]); + } + mExceededForceThresholdStream[0] = NULL; + + if (mExceededForceThresholdStream[1]) + { + mExceededForceThresholdStream[1]->~ThresholdStream(); + PX_FREE(mExceededForceThresholdStream[1]); + } + mExceededForceThresholdStream[1] = NULL; + +} + + + +void DynamicsTGSContext::setDescFromIndices(PxTGSSolverConstraintDesc& desc, + const PxsIndexedInteraction& constraint, const PxU32 solverBodyOffset, PxTGSSolverBodyVel* solverBodies) +{ + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eBODY == 0); + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eKINEMATIC == 1); + const PxU32 offsetMap[] = { solverBodyOffset, 0 }; + //const PxU32 offsetMap[] = {mKinematicCount, 0}; + + if (constraint.indexType0 == PxsIndexedInteraction::eARTICULATION) + { + ArticulationV* a = getArticulation(constraint.articulation0); + desc.articulationA = a; + desc.articulationALength = Ps::to16(a->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationALength & 0x0f)); + desc.linkIndexA = Ps::to16(a->getLinkIndex(constraint.articulation0)); + desc.bodyAIdx = 0; + } + else + { + desc.bodyA = constraint.indexType0 == PxsIndexedInteraction::eWORLD ? &mWorldSolverBodyVel + : &solverBodies[PxU32(constraint.solverBody0) + offsetMap[constraint.indexType0] + 1]; + + desc.bodyAIdx = constraint.indexType0 == PxsIndexedInteraction::eWORLD ? 0 + : PxU32(constraint.solverBody0) + offsetMap[constraint.indexType0] + 1; + + desc.linkIndexA = PxTGSSolverConstraintDesc::NO_LINK; + } + + if (constraint.indexType1 == PxsIndexedInteraction::eARTICULATION) + { + ArticulationV* b = getArticulation(constraint.articulation1); + desc.articulationB = b; + desc.articulationBLength = Ps::to16(b->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationBLength & 0x0f)); + desc.linkIndexB = Ps::to16(b->getLinkIndex(constraint.articulation1)); + desc.bodyBIdx = 0; + } + else + { + desc.bodyB = constraint.indexType1 == PxsIndexedInteraction::eWORLD ? &mWorldSolverBodyVel + : &solverBodies[PxU32(constraint.solverBody1) + offsetMap[constraint.indexType1] + 1]; + + desc.bodyBIdx = constraint.indexType1 == PxsIndexedInteraction::eWORLD ? 0 + : PxU32(constraint.solverBody1) + offsetMap[constraint.indexType1] + 1; + + desc.linkIndexB = PxTGSSolverConstraintDesc::NO_LINK; + } +} + +void DynamicsTGSContext::setDescFromIndices(PxTGSSolverConstraintDesc& desc, IG::EdgeIndex edgeIndex, const IG::SimpleIslandManager& islandManager, + PxU32* bodyRemap, const PxU32 solverBodyOffset, PxTGSSolverBodyVel* solverBodies) +{ + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eBODY == 0); + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eKINEMATIC == 1); + + const IG::IslandSim& islandSim = islandManager.getAccurateIslandSim(); + + IG::NodeIndex node1 = islandSim.getNodeIndex1(edgeIndex); + if (node1.isStaticBody()) + { + desc.bodyA = &mWorldSolverBodyVel; + desc.bodyAIdx = 0; + desc.linkIndexA = PxTGSSolverConstraintDesc::NO_LINK; + } + else + { + const IG::Node& node = islandSim.getNode(node1); + + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + PX_ASSERT(node1.isArticulation()); + Dy::ArticulationV* a = islandSim.getLLArticulation(node1); + desc.articulationA = a; + desc.articulationALength = Ps::to16(a->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationALength & 0x0f)); + desc.linkIndexA = Ps::to16(node1.articulationLinkId()); + desc.bodyAIdx = 0; + } + else + { + PX_ASSERT(!node1.isArticulation()); + PxU32 activeIndex = islandSim.getActiveNodeIndex(node1); + PxU32 index = node.isKinematic() ? activeIndex : bodyRemap[activeIndex] + solverBodyOffset; + desc.bodyA = &solverBodies[index + 1]; + desc.bodyAIdx = index + 1; + desc.linkIndexA = PxTGSSolverConstraintDesc::NO_LINK; + } + } + + IG::NodeIndex node2 = islandSim.getNodeIndex2(edgeIndex); + if (node2.isStaticBody()) + { + desc.bodyB = &mWorldSolverBodyVel; + desc.bodyBIdx = 0; + desc.linkIndexB = PxTGSSolverConstraintDesc::NO_LINK; + } + else + { + const IG::Node& node = islandSim.getNode(node2); + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + PX_ASSERT(node2.isArticulation()); + Dy::ArticulationV* b = islandSim.getLLArticulation(node2); + desc.articulationB = b; + desc.articulationBLength = Ps::to16(b->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationBLength & 0x0f)); + desc.linkIndexB = Ps::to16(node2.articulationLinkId()); + desc.bodyBIdx = 0; + } + else + { + PX_ASSERT(!node2.isArticulation()); + PxU32 activeIndex = islandSim.getActiveNodeIndex(node2); + PxU32 index = node.isKinematic() ? activeIndex : bodyRemap[activeIndex] + solverBodyOffset; + desc.bodyB = &solverBodies[index + 1]; + desc.bodyBIdx = index + 1; + desc.linkIndexB = PxTGSSolverConstraintDesc::NO_LINK; + } + } +} + +class DynamicsMergeTask : public Cm::Task +{ + PxBaseTask* mSecondContinuation; + +public: + DynamicsMergeTask(PxU64 contextId) : Cm::Task(contextId), mSecondContinuation(NULL) + { + } + + void setSecondContinuation(PxBaseTask* task) { task->addReference(); mSecondContinuation = task; } + + virtual const char* getName() const { return "MergeTask"; } + + virtual void runInternal() + { + } + + virtual void release() + { + mSecondContinuation->removeReference(); + Cm::Task::release(); + } + +}; + +class KinematicCopyTGSTask : public Cm::Task +{ + const IG::NodeIndex* const mKinematicIndices; + const PxU32 mNbKinematics; + const IG::IslandSim& mIslandSim; + PxTGSSolverBodyVel* mVels; + PxTGSSolverBodyTxInertia* mInertia; + PxStepSolverBodyData* mBodyData; + + PX_NOCOPY(KinematicCopyTGSTask) + +public: + + static const PxU32 NbKinematicsPerTask = 1024; + + KinematicCopyTGSTask(const IG::NodeIndex* const kinematicIndices, + const PxU32 nbKinematics, const IG::IslandSim& islandSim, PxTGSSolverBodyVel* vels, + PxTGSSolverBodyTxInertia* inertias, PxStepSolverBodyData* datas, PxU64 contextID) : Cm::Task(contextID), + mKinematicIndices(kinematicIndices), mNbKinematics(nbKinematics), + mIslandSim(islandSim), mVels(vels), mInertia(inertias), mBodyData(datas) + { + } + + virtual const char* getName() const { return "KinematicCopyTask"; } + + virtual void runInternal() + { + for (PxU32 i = 0; igetCore(); + copyToSolverBodyDataStepKinematic(core.linearVelocity, core.angularVelocity, core.body2World, core.maxPenBias, + core.maxContactImpulse, mKinematicIndices[i].index(), core.contactReportThreshold, core.maxAngularVelocitySq, + mVels[i], mInertia[i], mBodyData[i]); + + rigidBody->saveLastCCDTransform(); + } + } +}; + +class UpdateContinuationTGSTask : public Cm::Task +{ + DynamicsTGSContext& mContext; + IG::SimpleIslandManager& mSimpleIslandManager; + PxBaseTask* mLostTouchTask; + + PX_NOCOPY(UpdateContinuationTGSTask) +public: + + UpdateContinuationTGSTask(DynamicsTGSContext& context, + IG::SimpleIslandManager& simpleIslandManager, + PxBaseTask* lostTouchTask, + PxU64 contextID) : Cm::Task(contextID), mContext(context), mSimpleIslandManager(simpleIslandManager), + mLostTouchTask(lostTouchTask) + { + } + + virtual const char* getName() const { return "UpdateContinuationTask";} + + virtual void runInternal() + { + mContext.updatePostKinematic(mSimpleIslandManager, mCont, mLostTouchTask); + //Allow lost touch task to run once all tasks have be scheduled + mLostTouchTask->removeReference(); + } + +}; + + +void DynamicsTGSContext::update(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask, + PxsContactManager** /*foundPatchManagers*/, PxU32 /*nbFoundPatchManagers*/, + PxsContactManager** /*lostPatchManagers*/, PxU32 /*nbLostPatchManagers*/, + PxU32 /*maxPatchesPerCM*/, + PxsContactManagerOutputIterator& iterator, + PxsContactManagerOutput*, + const PxReal dt, const PxVec3& gravity, const PxU32 /*bitMapWordCounts*/) +{ + PX_PROFILE_ZONE("Dynamics.solverQueueTasks", mContextID); + + PX_UNUSED(simpleIslandManager); + + mOutputIterator = iterator; + + //const PxU32 nbSubsteps = 16; + + mDt = dt; + mInvDt = 1.f / dt; + + mGravity = gravity; + + const IG::IslandSim& islandSim = simpleIslandManager.getAccurateIslandSim(); + + const PxU32 islandCount = islandSim.getNbActiveIslands(); + + const PxU32 activatedContactCount = islandSim.getNbActivatedEdges(IG::Edge::eCONTACT_MANAGER); + const IG::EdgeIndex* const activatingEdges = islandSim.getActivatedEdges(IG::Edge::eCONTACT_MANAGER); + + for (PxU32 a = 0; a < activatedContactCount; ++a) + { + PxsContactManager* cm = simpleIslandManager.getContactManager(activatingEdges[a]); + if (cm) + { + cm->getWorkUnit().frictionPatchCount = 0; //KS - zero the friction patch count on any activating edges + } + } + +#if PX_ENABLE_SIM_STATS + if (islandCount > 0) + { + mSimStats.mNbActiveKinematicBodies = islandSim.getNbActiveKinematics(); + mSimStats.mNbActiveDynamicBodies = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + mSimStats.mNbActiveConstraints = islandSim.getNbActiveEdges(IG::Edge::eCONSTRAINT); + } + else + { + mSimStats.mNbActiveKinematicBodies = islandSim.getNbActiveKinematics(); + mSimStats.mNbActiveDynamicBodies = 0; + mSimStats.mNbActiveConstraints = 0; + } +#endif + + mThresholdStreamOut = 0; + + resetThreadContexts(); + + //If there is no work to do then we can do nothing at all. + if (0 == islandCount) + { + return; + } + + //Block to make sure it doesn't run before stage2 of update! + lostTouchTask->addReference(); + + UpdateContinuationTGSTask* task = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(UpdateContinuationTGSTask)), UpdateContinuationTGSTask) + (*this, simpleIslandManager, lostTouchTask, mContextID); + + task->setContinuation(continuation); + + //KS - test that world solver body's velocities are finite and 0, then set it to 0. + //Technically, the velocity should always be 0 but can be stomped if a NAN creeps into the simulation. + PX_ASSERT(mWorldSolverBodyVel.linearVelocity == PxVec3(0.f)); + PX_ASSERT(mWorldSolverBodyVel.angularVelocity == PxVec3(0.f)); + PX_ASSERT(mWorldSolverBodyVel.linearVelocity.isFinite()); + PX_ASSERT(mWorldSolverBodyVel.angularVelocity.isFinite()); + + mWorldSolverBodyVel.linearVelocity = mWorldSolverBodyVel.angularVelocity = PxVec3(0.f); + + const PxU32 kinematicCount = islandSim.getNbActiveKinematics(); + const IG::NodeIndex* const kinematicIndices = islandSim.getActiveKinematics(); + mKinematicCount = kinematicCount; + + const PxU32 bodyCount = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + PxU32 numArtics = islandSim.getNbActiveNodes(IG::Node::eARTICULATION_TYPE); + + { + if (kinematicCount + bodyCount > mSolverBodyVelPool.capacity()) + { + mSolverBodyRemapTable.reserve((kinematicCount + bodyCount + 31 + 1) & ~31); + mSolverBodyVelPool.reserve((kinematicCount + bodyCount +31 + 1) & ~31); + mSolverBodyTxInertiaPool.reserve((kinematicCount + bodyCount +31 + 1) & ~31); + mSolverBodyDataPool2.reserve((kinematicCount + bodyCount +31 + 1) & ~31); + } + + { + mSolverBodyVelPool.resize(kinematicCount + bodyCount +1); + mSolverBodyTxInertiaPool.resize(kinematicCount + bodyCount +1); + mSolverBodyDataPool2.resize(kinematicCount + bodyCount +1); + mSolverBodyRemapTable.resize(kinematicCount + bodyCount + 1); + } + + // integrate and copy all the kinematics - overkill, since not all kinematics + // need solver bodies + + mSolverBodyVelPool[0] = mWorldSolverBodyVel; + mSolverBodyTxInertiaPool[0] = mWorldSolverBodyTxInertia; + mSolverBodyDataPool2[0] = mWorldSolverBodyData2; + + + { + PX_PROFILE_ZONE("Dynamics.updateKinematics", mContextID); + for (PxU32 i = 0; i < kinematicCount; i+= KinematicCopyTGSTask::NbKinematicsPerTask) + { + const PxU32 nbToProcess = PxMin(kinematicCount - i, KinematicCopyTGSTask::NbKinematicsPerTask); + + KinematicCopyTGSTask* kinematicTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(KinematicCopyTGSTask)), KinematicCopyTGSTask) + (kinematicIndices + i, nbToProcess, islandSim, &mSolverBodyVelPool[i+1], + &mSolverBodyTxInertiaPool[i + 1], &mSolverBodyDataPool2[i + 1], mContextID); + + kinematicTask->setContinuation(task); + kinematicTask->removeReference(); + } + } + } + + PxU32 numArticulationConstraints = numArtics*Dy::DY_ARTICULATION_MAX_SIZE; //Just allocate enough memory to fit worst-case maximum size articulations... + + const PxU32 nbActiveContactManagers = islandSim.getNbActiveEdges(IG::Edge::eCONTACT_MANAGER); + const PxU32 nbActiveConstraints = islandSim.getNbActiveEdges(IG::Edge::eCONSTRAINT); + + PxU32 totalConstraintCount = nbActiveConstraints + nbActiveContactManagers + numArticulationConstraints; + + mSolverConstraintDescPool.forceSize_Unsafe(0); + mSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mOrderedSolverConstraintDescPool.forceSize_Unsafe(0); + mOrderedSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mOrderedSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mContactConstraintBatchHeaders.forceSize_Unsafe(0); + mContactConstraintBatchHeaders.reserve((totalConstraintCount + 63) & (~63)); + mContactConstraintBatchHeaders.forceSize_Unsafe(totalConstraintCount); + + mTempSolverConstraintDescPool.forceSize_Unsafe(0); + mTempSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mTempSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mContactList.forceSize_Unsafe(0); + mContactList.reserve((nbActiveContactManagers + 63u) & (~63u)); + mContactList.forceSize_Unsafe(nbActiveContactManagers); + + mMotionVelocityArray.forceSize_Unsafe(0); + mMotionVelocityArray.reserve((bodyCount + 63u) & (~63u)); + mMotionVelocityArray.forceSize_Unsafe(bodyCount); + + mBodyCoreArray.forceSize_Unsafe(0); + mBodyCoreArray.reserve((bodyCount + 63u) & (~63u)); + mBodyCoreArray.forceSize_Unsafe(bodyCount); + + mRigidBodyArray.forceSize_Unsafe(0); + mRigidBodyArray.reserve((bodyCount + 63u) & (~63u)); + mRigidBodyArray.forceSize_Unsafe(bodyCount); + + mArticulationArray.forceSize_Unsafe(0); + mArticulationArray.reserve((numArtics + 63u) & (~63u)); + mArticulationArray.forceSize_Unsafe(numArtics); + + mNodeIndexArray.forceSize_Unsafe(0); + mNodeIndexArray.reserve((bodyCount + 63u) & (~63u)); + mNodeIndexArray.forceSize_Unsafe(bodyCount); + + + + + ThresholdStream& stream = getThresholdStream(); + stream.forceSize_Unsafe(0); + stream.reserve(Ps::nextPowerOfTwo(nbActiveContactManagers != 0 ? nbActiveContactManagers - 1 : nbActiveContactManagers)); + + + + //flip exceeded force threshold buffer + mCurrentIndex = 1 - mCurrentIndex; + + task->removeReference(); + +} + +void DynamicsTGSContext::updatePostKinematic(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask) +{ + const IG::IslandSim& islandSim = simpleIslandManager.getAccurateIslandSim(); + + const IG::IslandId*const islandIds = islandSim.getActiveIslands(); + + DynamicsMergeTask* mergeTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(DynamicsMergeTask)), DynamicsMergeTask)(mContextID); + + mergeTask->setContinuation(continuation); + mergeTask->setSecondContinuation(lostTouchTask); + + //PxReal dt = mDt; + + PxU32 currentIsland = 0; + PxU32 currentBodyIndex = 0; + PxU32 currentArticulation = 0; + PxU32 currentContact = 0; + + PxU32 constraintIndex = 0; + + const PxU32 minIslandSize = mSolverBatchSize; + + const PxU32 islandCount = islandSim.getNbActiveIslands(); + + //while(startremoveReference(); +} + +void DynamicsTGSContext::prepareBodiesAndConstraints(const SolverIslandObjectsStep& objects, + IG::SimpleIslandManager& islandManager, + IslandContextStep& islandContext) +{ + Dy::ThreadContext& mThreadContext = *islandContext.mThreadContext; + + mThreadContext.mMaxSolverPositionIterations = 0; + mThreadContext.mMaxSolverVelocityIterations = 0; + mThreadContext.mAxisConstraintCount = 0; + mThreadContext.mContactDescPtr = mThreadContext.contactConstraintDescArray; + mThreadContext.mFrictionDescPtr = mThreadContext.frictionConstraintDescArray.begin(); + mThreadContext.mNumDifferentBodyConstraints = 0; + mThreadContext.mNumSelfConstraintBlocks = 0; + mThreadContext.mNumSelfConstraints = 0; + mThreadContext.mNumDifferentBodyFrictionConstraints = 0; + mThreadContext.mNumSelfConstraintFrictionBlocks = 0; + mThreadContext.mNumSelfFrictionConstraints = 0; + mThreadContext.numContactConstraintBatches = 0; + mThreadContext.contactDescArraySize = 0; + + mThreadContext.motionVelocityArray = objects.motionVelocities; + mThreadContext.mBodyCoreArray = objects.bodyCoreArray; + mThreadContext.mRigidBodyArray = objects.bodies; + mThreadContext.mArticulationArray = objects.articulations; + mThreadContext.bodyRemapTable = objects.bodyRemapTable; + mThreadContext.mNodeIndexArray = objects.nodeIndexArray; + + const PxU32 frictionConstraintCount = 0; + mThreadContext.resizeArrays(frictionConstraintCount, islandContext.mCounts.articulations); + + PxsBodyCore** PX_RESTRICT bodyArrayPtr = mThreadContext.mBodyCoreArray; + PxsRigidBody** PX_RESTRICT rigidBodyPtr = mThreadContext.mRigidBodyArray; + ArticulationV** PX_RESTRICT articulationPtr = mThreadContext.mArticulationArray; + PxU32* PX_RESTRICT bodyRemapTable = mThreadContext.bodyRemapTable; + PxU32* PX_RESTRICT nodeIndexArray = mThreadContext.mNodeIndexArray; + + PxU32 nbIslands = objects.numIslands; + const IG::IslandId* const islandIds = objects.islandIds; + + const IG::IslandSim& islandSim = islandManager.getAccurateIslandSim(); + + PxU32 bodyIndex = 0, articIndex = 0; + for (PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::NodeIndex currentIndex = island.mRootNode; + + while (currentIndex.isValid()) + { + const IG::Node& node = islandSim.getNode(currentIndex); + + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + articulationPtr[articIndex++] = node.getArticulation(); + } + else + { + PxsRigidBody* rigid = node.getRigidBody(); + PX_ASSERT(bodyIndex < (islandContext.mCounts.bodies + mKinematicCount + 1)); + rigidBodyPtr[bodyIndex] = rigid; + bodyArrayPtr[bodyIndex] = &rigid->getCore(); + nodeIndexArray[bodyIndex] = currentIndex.index(); + bodyRemapTable[islandSim.getActiveNodeIndex(currentIndex)] = bodyIndex++; + } + + currentIndex = node.mNextNode; + } + } + + + PxsIndexedContactManager* indexedManagers = objects.contactManagers; + + PxU32 currentContactIndex = 0; + for (PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::EdgeIndex contactEdgeIndex = island.mFirstEdge[IG::Edge::eCONTACT_MANAGER]; + + while (contactEdgeIndex != IG_INVALID_EDGE) + { + const IG::Edge& edge = islandSim.getEdge(contactEdgeIndex); + + PxsContactManager* contactManager = islandManager.getContactManager(contactEdgeIndex); + + if (contactManager) + { + const IG::NodeIndex nodeIndex1 = islandSim.getNodeIndex1(contactEdgeIndex); + const IG::NodeIndex nodeIndex2 = islandSim.getNodeIndex2(contactEdgeIndex); + + PxsIndexedContactManager& indexedManager = indexedManagers[currentContactIndex++]; + indexedManager.contactManager = contactManager; + + PX_ASSERT(!nodeIndex1.isStaticBody()); + { + const IG::Node& node1 = islandSim.getNode(nodeIndex1); + + //Is it an articulation or not??? + if (node1.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + indexedManager.indexType0 = PxsIndexedInteraction::eARTICULATION; + indexedManager.solverBody0 = size_t(node1.getArticulation()) | nodeIndex1.articulationLinkId(); + } + else + { + if (node1.isKinematic()) + { + indexedManager.indexType0 = PxsIndexedInteraction::eKINEMATIC; + indexedManager.solverBody0 = islandSim.getActiveNodeIndex(nodeIndex1); + } + else + { + indexedManager.indexType0 = PxsIndexedInteraction::eBODY; + indexedManager.solverBody0 = bodyRemapTable[islandSim.getActiveNodeIndex(nodeIndex1)]; + } + PX_ASSERT(indexedManager.solverBody0 < (islandContext.mCounts.bodies + mKinematicCount + 1)); + } + + } + + if (nodeIndex2.isStaticBody()) + { + indexedManager.indexType1 = PxsIndexedInteraction::eWORLD; + } + else + { + const IG::Node& node2 = islandSim.getNode(nodeIndex2); + + //Is it an articulation or not??? + if (node2.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + indexedManager.indexType1 = PxsIndexedInteraction::eARTICULATION; + indexedManager.solverBody1 = size_t(node2.getArticulation()) | nodeIndex2.articulationLinkId(); + } + else + { + if (node2.isKinematic()) + { + indexedManager.indexType1 = PxsIndexedInteraction::eKINEMATIC; + indexedManager.solverBody1 = islandSim.getActiveNodeIndex(nodeIndex2); + } + else + { + indexedManager.indexType1 = PxsIndexedInteraction::eBODY; + indexedManager.solverBody1 = bodyRemapTable[islandSim.getActiveNodeIndex(nodeIndex2)]; + } + PX_ASSERT(indexedManager.solverBody1 < (islandContext.mCounts.bodies + mKinematicCount + 1)); + } + } + + } + contactEdgeIndex = edge.mNextIslandEdge; + } + } + + islandContext.mCounts.contactManagers = currentContactIndex; +} + +struct ConstraintLess +{ + bool operator()(const PxTGSSolverConstraintDesc& left, const PxTGSSolverConstraintDesc& right) const + { + return reinterpret_cast(left.constraint)->index > reinterpret_cast(right.constraint)->index; + } +}; + +void DynamicsTGSContext::setupDescs(IslandContextStep& mIslandContext, const SolverIslandObjectsStep& mObjects, IG::SimpleIslandManager& mIslandManager, + PxU32* mBodyRemapTable, PxU32 mSolverBodyOffset, PxsContactManagerOutputIterator& outputs) +{ + PX_UNUSED(outputs); + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + PxTGSSolverConstraintDesc* contactDescPtr = mObjects.constraintDescs; + + //PxU32 constraintCount = mCounts.constraints + mCounts.contactManagers; + + PxU32 nbIslands = mObjects.numIslands; + const IG::IslandId* const islandIds = mObjects.islandIds; + + const IG::IslandSim& islandSim = mIslandManager.getAccurateIslandSim(); + + for (PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::EdgeIndex edgeId = island.mFirstEdge[IG::Edge::eCONSTRAINT]; + + while (edgeId != IG_INVALID_EDGE) + { + PxTGSSolverConstraintDesc& desc = *contactDescPtr; + + const IG::Edge& edge = islandSim.getEdge(edgeId); + Dy::Constraint* constraint = mIslandManager.getConstraint(edgeId); + setDescFromIndices(desc, edgeId, mIslandManager, mBodyRemapTable, mSolverBodyOffset, + mSolverBodyVelPool.begin()); + desc.constraint = reinterpret_cast(constraint); + desc.constraintLengthOver16 = DY_SC_TYPE_RB_1D; + contactDescPtr++; + edgeId = edge.mNextIslandEdge; + } + + } + + Ps::sort(mObjects.constraintDescs, PxU32(contactDescPtr - mObjects.constraintDescs), ConstraintLess()); + + if (mIslandContext.mCounts.contactManagers) + { + { + for (PxU32 a = 0; a < mIslandContext.mCounts.contactManagers; ++a) + { + //PxsContactManagerOutput& output = outputs.getContactManager(mObjects.contactManagers[a].contactManager->getWorkUnit().mNpIndex); + //if (output.nbContacts > 0) + { + PxTGSSolverConstraintDesc& desc = *contactDescPtr; + setDescFromIndices(desc, mObjects.contactManagers[a], mSolverBodyOffset, mSolverBodyVelPool.begin()); + desc.constraint = reinterpret_cast(mObjects.contactManagers[a].contactManager); + desc.constraintLengthOver16 = DY_SC_TYPE_RB_CONTACT; + contactDescPtr++; + } + //else + //{ + // //Clear friction state! + // mObjects.contactManagers[a].contactManager->getWorkUnit().frictionDataPtr = NULL; + // mObjects.contactManagers[a].contactManager->getWorkUnit().frictionPatchCount = 0; + //} + } + + } + } + mThreadContext.contactDescArraySize = PxU32(contactDescPtr - mObjects.constraintDescs); +} + +void DynamicsTGSContext::preIntegrateBodies(PxsBodyCore** bodyArray, PxsRigidBody** originalBodyArray, + PxTGSSolverBodyVel* solverBodyVelPool, PxTGSSolverBodyTxInertia* solverBodyTxInertia, PxStepSolverBodyData* solverBodyDataPool2, PxU32* nodeIndexArray, const PxU32 bodyCount, const PxVec3& gravity, const PxReal dt, PxU32& posIters, PxU32& velIters, PxU32 /*iteration*/) +{ + PX_PROFILE_ZONE("PreIntegrate", 0); + PxU32 localMaxPosIter = 0; + PxU32 localMaxVelIter = 0; + for (PxU32 i = 0; i < bodyCount; ++i) + { + PxsBodyCore& core = *bodyArray[i]; + const PxsRigidBody& rBody = *originalBodyArray[i]; + + PxU16 iterWord = core.solverIterationCounts; + localMaxPosIter = PxMax(PxU32(iterWord & 0xff), localMaxPosIter); + localMaxVelIter = PxMax(PxU32(iterWord >> 8), localMaxVelIter); + + //const Cm::SpatialVector& accel = originalBodyArray[i]->getAccelerationV(); + bodyCoreComputeUnconstrainedVelocity(gravity, dt, core.linearDamping, core.angularDamping, rBody.accelScale, core.maxLinearVelocitySq, core.maxAngularVelocitySq, + core.linearVelocity, core.angularVelocity, !!(rBody.mInternalFlags & PxcRigidBody::eDISABLE_GRAVITY)); + + copyToSolverBodyDataStep(core.linearVelocity, core.angularVelocity, core.inverseMass, core.inverseInertia, core.body2World, core.maxPenBias, core.maxContactImpulse, nodeIndexArray[i], + core.contactReportThreshold, core.maxAngularVelocitySq, core.lockFlags, false, + solverBodyVelPool[i+1], solverBodyTxInertia[i+1], solverBodyDataPool2[i+1]); + } + + posIters = localMaxPosIter; + velIters = localMaxVelIter; +} + +class BlockAllocator : public PxConstraintAllocator +{ + PxsConstraintBlockManager& mConstraintBlockManager; + PxcConstraintBlockStream& mConstraintBlockStream; + FrictionPatchStreamPair& mFrictionPatchStreamPair; + PxU32& mTotalConstraintByteSize; +public: + + BlockAllocator(PxsConstraintBlockManager& constraintBlockManager, PxcConstraintBlockStream& constraintBlockStream, FrictionPatchStreamPair& frictionPatchStreamPair, + PxU32& totalConstraintByteSize) : + mConstraintBlockManager(constraintBlockManager), mConstraintBlockStream(constraintBlockStream), mFrictionPatchStreamPair(frictionPatchStreamPair), + mTotalConstraintByteSize(totalConstraintByteSize) + { + } + + virtual PxU8* reserveConstraintData(const PxU32 size) + { + mTotalConstraintByteSize += size; + return mConstraintBlockStream.reserve(size, mConstraintBlockManager); + } + + virtual PxU8* reserveFrictionData(const PxU32 size) + { + return mFrictionPatchStreamPair.reserve(size); + } + + virtual PxU8* findInputPatches(PxU8* frictionCookie) + { + return frictionCookie; + } + + PX_NOCOPY(BlockAllocator) + +}; + +PxU32 getContactManagerConstraintDesc(const PxsContactManagerOutput& cmOutput, const PxsContactManager& /*cm*/, PxTGSSolverConstraintDesc& desc) +{ + desc.writeBackLengthOver4 = cmOutput.nbContacts; + desc.writeBack = cmOutput.contactForces; + return cmOutput.nbContacts;// cm.getWorkUnit().axisConstraintCount; +} + +bool createFinalizeSolverContactsStep(PxTGSSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxConstraintAllocator& constraintAllocator); + +PxU32 SetupSolverConstraintStep(SolverConstraintShaderPrepDesc& shaderDesc, + PxTGSSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + const PxReal dt, const PxReal totalDt, const PxReal invdt, const PxReal invTotalDt, const PxU32 nbSubsteps, + const PxReal lengthScale); + +SolverConstraintPrepState::Enum setupSolverConstraintStep4 +(SolverConstraintShaderPrepDesc* PX_RESTRICT constraintShaderDescs, + PxTGSSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal totalDt, const PxReal recipdt, const PxReal recipTotalDt, PxU32& totalRows, + PxConstraintAllocator& allocator, const PxReal lengthScale); + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Step( + PxsContactManagerOutput** cmOutputs, + ThreadContext& threadContext, + PxTGSSolverContactDesc* blockDescs, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator); + + +void DynamicsTGSContext::createSolverConstraints(PxTGSSolverConstraintDesc* contactDescPtr, PxConstraintBatchHeader* headers, const PxU32 nbHeaders, + PxsContactManagerOutputIterator& outputs, Dy::ThreadContext& islandThreadContext, Dy::ThreadContext& threadContext, PxReal stepDt, PxReal totalDt, PxReal invStepDt, + PxU32 nbSubsteps) +{ + PX_UNUSED(totalDt); + PX_PROFILE_ZONE("CreateConstraints", 0); + PxTransform idt(PxIdentity); + + BlockAllocator blockAllocator(islandThreadContext.mConstraintBlockManager, threadContext.mConstraintBlockStream, threadContext.mFrictionPatchStreamPair, threadContext.mConstraintSize); + + PxTGSSolverBodyTxInertia* txInertias = mSolverBodyTxInertiaPool.begin(); + PxStepSolverBodyData* solverBodyDatas = mSolverBodyDataPool2.begin(); + + const PxReal invTotalDt = 1.f / totalDt; + + for (PxU32 h = 0; h < nbHeaders; ++h) + { + PxConstraintBatchHeader& hdr = headers[h]; + PxU32 startIdx = hdr.mStartIndex; + PxU32 endIdx = startIdx + hdr.mStride; + + if (contactDescPtr[startIdx].constraintLengthOver16 == DY_SC_TYPE_RB_CONTACT) + { + PxTGSSolverContactDesc blockDescs[4]; + PxsContactManagerOutput* cmOutputs[4]; + PxsContactManager* cms[4]; + + for (PxU32 a = startIdx, i = 0; a < endIdx; ++a, i++) + { + PxTGSSolverConstraintDesc& desc = contactDescPtr[a]; + PxsContactManager* cm = reinterpret_cast(desc.constraint); + + PxTGSSolverContactDesc& blockDesc = blockDescs[i]; + + cms[i] = cm; + + PxcNpWorkUnit& unit = cm->getWorkUnit(); + + PxsContactManagerOutput* cmOutput = &outputs.getContactManager(unit.mNpIndex); + + cmOutputs[i] = cmOutput; + + PxTGSSolverBodyVel& b0 = *desc.bodyA; + PxTGSSolverBodyVel& b1 = *desc.bodyB; + + PxTGSSolverBodyTxInertia& txI0 = txInertias[desc.bodyAIdx]; + PxTGSSolverBodyTxInertia& txI1 = txInertias[desc.bodyBIdx]; + + PxStepSolverBodyData& data0 = solverBodyDatas[desc.bodyAIdx]; + PxStepSolverBodyData& data1 = solverBodyDatas[desc.bodyBIdx]; + + blockDesc.body0 = &b0; + blockDesc.body1 = &b1; + + PxU8 flags = unit.rigidCore0->mFlags; + if (unit.rigidCore1) + flags |= PxU8(unit.rigidCore1->mFlags); + + blockDesc.bodyFrame0 = unit.rigidCore0->body2World; + blockDesc.bodyFrame1 = unit.rigidCore1->body2World; + blockDesc.shapeInteraction = cm->getShapeInteraction(); + blockDesc.contactForces = cmOutput->contactForces; + blockDesc.desc = &desc; + blockDesc.body0 = &b0; + blockDesc.body1 = &b1; + blockDesc.body0TxI = &txI0; + blockDesc.body1TxI = &txI1; + blockDesc.bodyData0 = &data0; + blockDesc.bodyData1 = &data1; + blockDesc.hasForceThresholds = !!(unit.flags & PxcNpWorkUnitFlag::eFORCE_THRESHOLD); + blockDesc.disableStrongFriction = !!(unit.flags & PxcNpWorkUnitFlag::eDISABLE_STRONG_FRICTION); + blockDesc.bodyState0 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) ? PxSolverContactDesc::eARTICULATION : PxSolverContactDesc::eDYNAMIC_BODY; + blockDesc.bodyState1 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) ? PxSolverContactDesc::eARTICULATION : (unit.flags & PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR) ? PxSolverContactDesc::eKINEMATIC_BODY : + ((unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1) ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eSTATIC_BODY); + //blockDesc.flags = unit.flags; + + PxReal maxImpulse0 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) ? static_cast(unit.rigidCore0)->maxContactImpulse : data0.maxContactImpulse; + PxReal maxImpulse1 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) ? static_cast(unit.rigidCore1)->maxContactImpulse : data1.maxContactImpulse; + + PxReal dominance0 = unit.dominance0 ? 1.f : 0.f; + PxReal dominance1 = unit.dominance1 ? 1.f : 0.f; + + blockDesc.mInvMassScales.linear0 = blockDesc.mInvMassScales.angular0 = dominance0; + blockDesc.mInvMassScales.linear1 = blockDesc.mInvMassScales.angular1 = dominance1; + blockDesc.restDistance = unit.restDistance; + blockDesc.frictionPtr = unit.frictionDataPtr; + blockDesc.frictionCount = unit.frictionPatchCount; + blockDesc.maxCCDSeparation = PX_MAX_F32; + blockDesc.maxImpulse = PxMin(maxImpulse0, maxImpulse1); + blockDesc.torsionalPatchRadius = unit.mTorsionalPatchRadius; + blockDesc.minTorsionalPatchRadius = unit.mMinTorsionalPatchRadius; + } + + SolverConstraintPrepState::Enum buildState = SolverConstraintPrepState::eUNBATCHABLE; + +#if PX_USE_BLOCK_SOLVER + if (hdr.mStride == 4) + { + buildState = createFinalizeSolverContacts4Step( + cmOutputs, + threadContext, + blockDescs, + invStepDt, + invTotalDt, + mBounceThreshold, + mFrictionOffsetThreshold, + mCorrelationDistance, + mSolverOffsetSlop, + blockAllocator); + } +#endif + + if (buildState != SolverConstraintPrepState::eSUCCESS) + { + + for (PxU32 a = startIdx, i = 0; a < endIdx; ++a, i++) + { + PxTGSSolverConstraintDesc& desc = contactDescPtr[a]; + PxsContactManager* cm = reinterpret_cast(desc.constraint); + //PxcNpWorkUnit& n = cm->getWorkUnit(); + + PxsContactManagerOutput& output = *cmOutputs[i]; + //PX_ASSERT(output.nbContacts != 0); + + createFinalizeSolverContactsStep(blockDescs[i], output, threadContext, + invStepDt, invTotalDt, mBounceThreshold, mFrictionOffsetThreshold, mCorrelationDistance, blockAllocator); + + getContactManagerConstraintDesc(output, *cm, desc); + + //PX_ASSERT(desc.constraint != NULL); + } + } + + for (PxU32 i = 0; i < hdr.mStride; ++i) + { + PxsContactManager* cm = cms[i]; + + PxcNpWorkUnit& unit = cm->getWorkUnit(); + unit.frictionDataPtr = blockDescs[i].frictionPtr; + unit.frictionPatchCount = blockDescs[i].frictionCount; + + } + + } + else if (contactDescPtr[startIdx].constraintLengthOver16 == DY_SC_TYPE_RB_1D) + { + SolverConstraintShaderPrepDesc shaderPrepDescs[4]; + PxTGSSolverConstraintPrepDesc prepDescs[4]; + + for (PxU32 a = startIdx, i = 0; a < endIdx; ++a, i++) + { + SolverConstraintShaderPrepDesc& shaderPrepDesc = shaderPrepDescs[i]; + PxTGSSolverConstraintPrepDesc& prepDesc = prepDescs[i]; + + PxTransform id(PxIdentity); + + { + PxTGSSolverConstraintDesc& desc = contactDescPtr[a]; + const Constraint* constraint = reinterpret_cast(desc.constraint); + + const PxConstraintSolverPrep solverPrep = constraint->solverPrep; + const void* constantBlock = constraint->constantBlock; + const PxU32 constantBlockByteSize = constraint->constantBlockSize; + const PxTransform& pose0 = (constraint->body0 ? constraint->body0->getPose() : id); + const PxTransform& pose1 = (constraint->body1 ? constraint->body1->getPose() : id); + const PxTGSSolverBodyVel* sbody0 = desc.bodyA; + const PxTGSSolverBodyVel* sbody1 = desc.bodyB; + + PxTGSSolverBodyTxInertia& txI0 = txInertias[desc.bodyAIdx]; + PxTGSSolverBodyTxInertia& txI1 = txInertias[desc.bodyBIdx]; + + PxStepSolverBodyData& data0 = solverBodyDatas[desc.bodyAIdx]; + PxStepSolverBodyData& data1 = solverBodyDatas[desc.bodyBIdx]; + + shaderPrepDesc.constantBlock = constantBlock; + shaderPrepDesc.constantBlockByteSize = constantBlockByteSize; + shaderPrepDesc.constraint = constraint; + shaderPrepDesc.solverPrep = solverPrep; + + prepDesc.desc = &desc; + prepDesc.bodyFrame0 = pose0; + prepDesc.bodyFrame1 = pose1; + prepDesc.body0 = sbody0; + prepDesc.body1 = sbody1; + prepDesc.body0TxI = &txI0; + prepDesc.body1TxI = &txI1; + prepDesc.bodyData0 = &data0; + prepDesc.bodyData1 = &data1; + prepDesc.linBreakForce = constraint->linBreakForce; + prepDesc.angBreakForce = constraint->angBreakForce; + prepDesc.writeback = &getConstraintWriteBackPool()[constraint->index]; + prepDesc.disablePreprocessing = !!(constraint->flags & PxConstraintFlag::eDISABLE_PREPROCESSING); + prepDesc.improvedSlerp = !!(constraint->flags & PxConstraintFlag::eIMPROVED_SLERP); + prepDesc.driveLimitsAreForces = !!(constraint->flags & PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES); + prepDesc.extendedLimits = !!(constraint->flags & PxConstraintFlag::eENABLE_EXTENDED_LIMITS); + prepDesc.minResponseThreshold = constraint->minResponseThreshold; + + prepDesc.bodyState0 = desc.linkIndexA == PxSolverConstraintDesc::NO_LINK ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eARTICULATION; + prepDesc.bodyState1 = desc.linkIndexB == PxSolverConstraintDesc::NO_LINK ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eARTICULATION; + } + } + + SolverConstraintPrepState::Enum buildState = SolverConstraintPrepState::eUNBATCHABLE; + +#if PX_USE_BLOCK_SOLVER +#if PX_USE_BLOCK_1D + if (hdr.mStride == 4) + { + PxU32 totalRows; + buildState = setupSolverConstraintStep4 + (shaderPrepDescs, prepDescs, stepDt, totalDt, invStepDt, invTotalDt, totalRows, blockAllocator, mLengthScale); + } +#endif +#endif + + if (buildState != SolverConstraintPrepState::eSUCCESS) + { + for (PxU32 a = startIdx, i = 0; a < endIdx; ++a, i++) + { + PxReal clampedInvDt = invStepDt; + SetupSolverConstraintStep(shaderPrepDescs[i], prepDescs[i], blockAllocator, stepDt, totalDt, clampedInvDt, invTotalDt, nbSubsteps, mLengthScale); + } + } + } + } +} + +void solveContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime); + +void solve1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxTGSSolverBodyTxInertia* const txInertias, const PxReal elapsedTime); + +void solveExtContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime, SolverContext& cache); + +void solveExt1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime, SolverContext& cache, + const PxTGSSolverBodyTxInertia* const txInertias); + +void solveContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime); + +void solve1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxTGSSolverBodyTxInertia* const txInertias, + const PxReal elapsedTime); + + +void DynamicsTGSContext::solveConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, bool doFriction, + PxReal invStepDt, const PxTGSSolverBodyTxInertia* const solverTxInertia, const PxReal elapsedTime, const PxReal minPenetration, SolverContext& cache) +{ + PX_UNUSED(invStepDt); + PX_UNUSED(solverTxInertia); + + for (PxU32 h = 0; h < nbHeaders; ++h) + { + const PxConstraintBatchHeader& hdr = batchHeaders[h]; + + switch (hdr.mConstraintType) + { + case DY_SC_TYPE_RB_CONTACT: + case DY_SC_TYPE_STATIC_CONTACT: + solveContactBlock(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime); + break; + case DY_SC_TYPE_EXT_CONTACT: + solveExtContactBlock(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime, cache); + break; + case DY_SC_TYPE_RB_1D: + solve1DBlock(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + case DY_SC_TYPE_EXT_1D: + solveExt1DBlock(hdr, contactDescPtr, elapsedTime, cache, solverTxInertia); + break; + case DY_SC_TYPE_BLOCK_RB_CONTACT: + solveContact4(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime); + break; + case DY_SC_TYPE_BLOCK_1D: + solve1D4(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + default: + break; + } + } + +} + +void DynamicsTGSContext::parallelSolveConstraints(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, bool doFriction, + PxTGSSolverBodyTxInertia* solverTxInertia, const PxReal elapsedTime, const PxReal minPenetration, + SolverContext& cache) +{ + PX_UNUSED(solverTxInertia); + for (PxU32 h = 0; h < nbHeaders; ++h) + { + const PxConstraintBatchHeader& hdr = batchHeaders[h]; + + + switch (hdr.mConstraintType) + { + case DY_SC_TYPE_RB_CONTACT: + case DY_SC_TYPE_STATIC_CONTACT: + solveContactBlock(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime); + break; + case DY_SC_TYPE_EXT_CONTACT: + solveExtContactBlock(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime, cache); + break; + case DY_SC_TYPE_RB_1D: + solve1DBlock(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + case DY_SC_TYPE_EXT_1D: + solveExt1DBlock(hdr, contactDescPtr, elapsedTime, cache, solverTxInertia); + break; + case DY_SC_TYPE_BLOCK_RB_CONTACT: + solveContact4(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime); + break; + case DY_SC_TYPE_BLOCK_1D: + solve1D4(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + default: + break; + } + } + +} + + +void writeBackContact(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, SolverContext* cache); + + +void writeBack1D(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc); + +void writeBackContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, SolverContext* cache); + +void writeBack1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc); + + +void DynamicsTGSContext::writebackConstraintsIteration(const PxConstraintBatchHeader* const hdrs, const PxTGSSolverConstraintDesc* const contactDescPtr, const PxU32 nbHeaders) +{ + PX_PROFILE_ZONE("Writeback", 0); + + for (PxU32 h = 0; h < nbHeaders; ++h) + { + const PxConstraintBatchHeader& hdr = hdrs[h]; + + switch (hdr.mConstraintType) + { + case DY_SC_TYPE_RB_CONTACT: + case DY_SC_TYPE_STATIC_CONTACT: + case DY_SC_TYPE_EXT_CONTACT: + writeBackContact(hdr, contactDescPtr, NULL); + break; + case DY_SC_TYPE_RB_1D: + case DY_SC_TYPE_EXT_1D: + writeBack1D(hdr, contactDescPtr); + break; + case DY_SC_TYPE_BLOCK_RB_CONTACT: + writeBackContact4(hdr, contactDescPtr, NULL); + break; + case DY_SC_TYPE_BLOCK_1D: + writeBack1D4(hdr, contactDescPtr); + break; + default: + break; + }; + } +} + +void DynamicsTGSContext::parallelWritebackConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders) +{ + for (PxU32 h = 0; h < nbHeaders; ++h) + { + const PxConstraintBatchHeader& hdr = batchHeaders[h]; + + switch (hdr.mConstraintType) + { + case DY_SC_TYPE_RB_CONTACT: + case DY_SC_TYPE_STATIC_CONTACT: + case DY_SC_TYPE_EXT_CONTACT: + writeBackContact(hdr, contactDescPtr, NULL); + break; + case DY_SC_TYPE_RB_1D: + case DY_SC_TYPE_EXT_1D: + writeBack1D(hdr, contactDescPtr); + break; + case DY_SC_TYPE_BLOCK_RB_CONTACT: + writeBackContact4(hdr, contactDescPtr, NULL); + break; + default: + break; + }; + } +} + +void solveConclude1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, + const PxTGSSolverBodyTxInertia* const txInertias, const PxReal elapsedTime); + +void solveConcludeContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime); + +void solveConcludeContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxReal elapsedTime); + +void solveConclude1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxTGSSolverBodyTxInertia* const txInertias, + const PxReal elapsedTime); + +void solveConcludeContactExtBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime, SolverContext& cache); + +void solveConclude1DBlockExt(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, + const PxReal elapsedTime, SolverContext& cache, const PxTGSSolverBodyTxInertia* const txInertias); + +void DynamicsTGSContext::solveConcludeConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, + const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, PxTGSSolverBodyTxInertia* solverTxInertia, + const PxReal elapsedTime, SolverContext& cache) +{ + for (PxU32 h = 0; h < nbHeaders; ++h) + { + const PxConstraintBatchHeader& hdr = batchHeaders[h]; + + switch (hdr.mConstraintType) + { + case DY_SC_TYPE_RB_CONTACT: + case DY_SC_TYPE_STATIC_CONTACT: + solveConcludeContactBlock(hdr, contactDescPtr, elapsedTime); + break; + case DY_SC_TYPE_EXT_CONTACT: + solveConcludeContactExtBlock(hdr, contactDescPtr, elapsedTime, cache); + break; + case DY_SC_TYPE_RB_1D: + solveConclude1DBlock(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + case DY_SC_TYPE_EXT_1D: + solveConclude1DBlockExt(hdr, contactDescPtr, elapsedTime, cache, solverTxInertia); + break; + case DY_SC_TYPE_BLOCK_RB_CONTACT: + solveConcludeContact4(hdr, contactDescPtr, elapsedTime); + break; + case DY_SC_TYPE_BLOCK_1D: + solveConclude1D4(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + default: + break; + } + } +} + + +static void integrateCoreStep(PxTGSSolverBodyVel& vel, PxTGSSolverBodyTxInertia& txInertia, const PxStepSolverBodyData& /*bodyData*/, const PxF32 dt) +{ + PxU32 lockFlags = vel.lockFlags; + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + vel.linearVelocity.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + vel.linearVelocity.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + vel.linearVelocity.z = 0.f; + + //The angular velocity should be 0 because it is now impossible to make it rotate around that axis! + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + vel.angularVelocity.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + vel.angularVelocity.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + vel.angularVelocity.z = 0.f; + } + + PxVec3 linearMotionVel = vel.linearVelocity; + const PxVec3 delta = linearMotionVel * dt; + + + PxVec3 unmolestedAngVel = vel.angularVelocity; + PxVec3 angularMotionVel = txInertia.sqrtInvInertia * vel.angularVelocity; + PxReal w2 = angularMotionVel.magnitudeSquared(); + txInertia.deltaBody2World.p += delta; + PX_ASSERT(txInertia.deltaBody2World.p.isFinite()); + + + // Integrate the rotation using closed form quaternion integrator + if (w2 != 0.0f) + { + PxReal w = PxSqrt(w2); + + //KS - we allow a little bit more angular velocity than the default + //maxAngVel member otherwise the simulation feels a little flimsy + /*const PxReal maxW = PxMax(50.f, vel.maxAngVel); + + if (w > maxW) + { + PxReal ratio = maxW / w; + + vel.angularVelocity *= ratio; + }*/ + + const PxReal v = dt * w * 0.5f; + PxReal s, q; + Ps::sincos(v, s, q); + s /= w; + + const PxVec3 pqr = angularMotionVel * s; + const PxQuat quatVel(pqr.x, pqr.y, pqr.z, 0); + PxQuat result = quatVel * txInertia.deltaBody2World.q; + + result += txInertia.deltaBody2World.q * q; + + txInertia.deltaBody2World.q = result.getNormalized(); + PX_ASSERT(txInertia.deltaBody2World.q.isSane()); + PX_ASSERT(txInertia.deltaBody2World.q.isFinite()); + + //Accumulate the angular rotations in a space we can project the angular constraints to + } + + vel.deltaAngDt += unmolestedAngVel * dt; + vel.deltaLinDt += delta; + + /*solverBodyData.body2World = txInertia.body2World; + solverBodyData.deltaLinDt = vel.deltaLinDt; + solverBodyData.deltaAngDt = vel.deltaAngDt;*/ +} + + +void DynamicsTGSContext::integrateBodies(const SolverIslandObjectsStep& /*objects*/, + const PxU32 count, PxTGSSolverBodyVel* PX_RESTRICT vels, PxTGSSolverBodyTxInertia* PX_RESTRICT txInertias, const PxStepSolverBodyData*const PX_RESTRICT bodyDatas, PxReal dt) +{ + for (PxU32 k = 0; k < count; k++) + { + integrateCoreStep(vels[k+1], txInertias[k+1], bodyDatas[k+1], dt); + } +} + +void DynamicsTGSContext::parallelIntegrateBodies(PxTGSSolverBodyVel* vels, PxTGSSolverBodyTxInertia* txInertias, + const PxStepSolverBodyData* const bodyDatas, const PxU32 count, PxReal dt) +{ + for (PxU32 k = 0; k < count; k++) + { + integrateCoreStep(vels[k+1], txInertias[k+1], bodyDatas[k+1], dt); + } +} + +void DynamicsTGSContext::copyBackBodies(const SolverIslandObjectsStep& objects, + PxTGSSolverBodyVel* vels, PxTGSSolverBodyTxInertia* txInertias, + PxStepSolverBodyData* solverBodyDatas, PxReal invDt, IG::IslandSim& islandSim, + PxU32 startIdx, PxU32 endIdx) +{ + for (PxU32 k = startIdx; k < endIdx; k++) + { + //PxStepSolverBody& solverBodyData = solverBodyData2[k + 1]; + PxTGSSolverBodyVel& solverBodyVel = vels[k + 1]; + PxTGSSolverBodyTxInertia& solverBodyTxI = txInertias[k + 1]; + PxStepSolverBodyData& solverBodyData = solverBodyDatas[k + 1]; + + Cm::SpatialVector motionVel(solverBodyVel.deltaLinDt*invDt, solverBodyTxI.sqrtInvInertia*(solverBodyVel.deltaAngDt*invDt)); + + PxsRigidBody& rBody = *objects.bodies[k]; + PxsBodyCore& core = rBody.getCore(); + rBody.mLastTransform = core.body2World; + core.body2World.q = (solverBodyTxI.deltaBody2World.q * core.body2World.q).getNormalized(); + core.body2World.p = solverBodyTxI.deltaBody2World.p; + core.linearVelocity = (solverBodyVel.linearVelocity); + core.angularVelocity = solverBodyTxI.sqrtInvInertia*(solverBodyVel.angularVelocity); + + + bool hasStaticTouch = islandSim.getIslandStaticTouchCount(IG::NodeIndex(solverBodyData.nodeIndex)) != 0; + sleepCheck(&rBody, mDt, invDt, mEnableStabilization, mUseAdaptiveForce, motionVel, + hasStaticTouch); + } +} + + +void DynamicsTGSContext::stepArticulations(Dy::ThreadContext& threadContext, const PxsIslandIndices& counts, PxReal dt) +{ + for (PxU32 a = 0; a < counts.articulations; ++a) + { + ArticulationSolverDesc& d = threadContext.getArticulations()[a]; + //if(d.articulation->numTotalConstraints > 0) + //d.articulation->solveInternalConstraints(dt, 1.f / dt, threadContext.mZVector.begin(), threadContext.mDeltaV.begin(), false); + ArticulationPImpl::updateDeltaMotion(d, dt, threadContext.mDeltaV.begin()); + + } +} + +void DynamicsTGSContext::updateArticulations(Dy::ThreadContext& threadContext, const PxU32 startIdx, const PxU32 endIdx, PxReal dt) +{ + for (PxU32 a = startIdx; a < endIdx; ++a) + { + ArticulationSolverDesc& d = threadContext.getArticulations()[a]; + ArticulationPImpl::updateBodiesTGS(d, dt); + } +} + +class ArticulationTask : public Cm::Task +{ + Dy::DynamicsTGSContext& mContext; + ArticulationSolverDesc* mDescs; + PxU32 mNbDescs; + PxVec3 mGravity; + PxReal mDt; + + PX_NOCOPY(ArticulationTask) + +public: + static const PxU32 MaxNbPerTask = 4; + + ArticulationTask(Dy::DynamicsTGSContext& context, ArticulationSolverDesc* descs, const PxU32 nbDescs, const PxVec3& gravity, + PxReal dt, PxU64 contextId) : Cm::Task(contextId), mContext(context), + mDescs(descs), mNbDescs(nbDescs), mGravity(gravity), mDt(dt) + { + } + + virtual const char* getName() const { return "ArticulationTask"; } + + virtual void runInternal() + { + PxU32 maxLinks = 0; + for (PxU32 i = 0; i < mNbDescs; i++) + { + maxLinks = PxMax(maxLinks, PxU32(mDescs[i].linkCount)); + } + + Dy::ThreadContext& threadContext = *mContext.getThreadContext(); + + threadContext.mZVector.forceSize_Unsafe(0); + threadContext.mZVector.reserve(maxLinks); + threadContext.mZVector.forceSize_Unsafe(maxLinks); + + threadContext.mDeltaV.forceSize_Unsafe(0); + threadContext.mDeltaV.reserve(maxLinks); + threadContext.mDeltaV.forceSize_Unsafe(maxLinks); + + for (PxU32 a = 0; a < mNbDescs; ++a) + { + + ArticulationPImpl::computeUnconstrainedVelocitiesTGS(mDescs[a], mDt, + mGravity, getContextId(), threadContext.mZVector.begin(), threadContext.mDeltaV.begin()); + } + + mContext.putThreadContext(&threadContext); + } + +}; + + +void DynamicsTGSContext::setupArticulations(IslandContextStep& islandContext, const PxVec3& gravity, const PxReal dt, PxU32& posIters, + PxU32& velIters, PxBaseTask* continuation) +{ + Dy::ArticulationV** articulations = islandContext.mThreadContext->mArticulationArray; + PxU32 nbArticulations = islandContext.mCounts.articulations; + + PxU32 maxVelIters = 0; + PxU32 maxPosIters = 0; + PxU32 maxArticulationLength = 0; + PxU32 maxSolverArticLength = 0; + + //PxU32 startIdx = 0; + for (PxU32 a = 0; a < nbArticulations; a+= ArticulationTask::MaxNbPerTask) + { + const PxU32 endIdx = PxMin(nbArticulations, a + ArticulationTask::MaxNbPerTask); + for (PxU32 b = a; b < endIdx; ++b) + { + ArticulationSolverDesc& desc = islandContext.mThreadContext->getArticulations()[b]; + articulations[b]->getSolverDesc(desc); + maxArticulationLength = PxMax(maxArticulationLength, PxU32(desc.totalDataSize)); + maxSolverArticLength = PxMax(maxSolverArticLength, PxU32(desc.solverDataSize)); + + const PxU16 iterWord = articulations[b]->getIterationCounts(); + maxVelIters = PxMax(PxU32(iterWord >> 8), maxVelIters); + maxPosIters = PxMax(PxU32(iterWord & 0xff), maxPosIters); + } + + Dy::ThreadContext* threadContext = islandContext.mThreadContext; + ArticulationTask* task = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(ArticulationTask)), ArticulationTask)(*this, threadContext->getArticulations().begin() + a, + endIdx - a, gravity, dt, getContextId()); + + task->setContinuation(continuation); + task->removeReference(); + + + + //startIdx += descCount; + + } + + velIters = PxMax(maxVelIters, velIters); + posIters = PxMax(maxPosIters, posIters); + +} + +PxU32 DynamicsTGSContext::setupArticulationInternalConstraints(IslandContextStep& islandContext, PxReal dt, PxReal invStepDt, PxTGSSolverConstraintDesc* constraintDescs) +{ + Dy::ArticulationV** articulations = islandContext.mThreadContext->mArticulationArray; + PxU32 nbArticulations = islandContext.mCounts.articulations; + + ThreadContext* threadContext = getThreadContext(); + threadContext->mConstraintBlockStream.reset(); + + PxU32 startIdx = 0; + PxU32 totalDescCount = 0; + Cm::SpatialVectorF Z[64]; + for (PxU32 a = 0; a < nbArticulations; ++a) + { + ArticulationSolverDesc& desc = islandContext.mThreadContext->getArticulations()[a]; + articulations[a]->getSolverDesc(desc); + + PxU32 acCount; + PxU32 descCount = ArticulationPImpl::setupSolverInternalConstraintsTGS(desc, threadContext->mConstraintBlockStream, constraintDescs+startIdx, + islandContext.mStepDt, invStepDt, dt, acCount, islandContext.mThreadContext->mConstraintBlockManager, Z); + + desc.numInternalConstraints = Ps::to8(descCount); + + totalDescCount += descCount; + + startIdx += DY_ARTICULATION_MAX_SIZE; + } + + putThreadContext(threadContext); + + islandContext.mThreadContext->contactDescArraySize += totalDescCount; + + return totalDescCount; +} + +class SetupDescsTask : public Cm::Task +{ + Dy::IslandContextStep& mIslandContext; + const SolverIslandObjectsStep& mObjects; + IG::SimpleIslandManager& mIslandManager; + PxU32* mBodyRemapTable; + PxU32 mSolverBodyOffset; + PxsContactManagerOutputIterator& mOutputs; + DynamicsTGSContext& mContext; + + PX_NOCOPY(SetupDescsTask) + +public: + + SetupDescsTask(IslandContextStep& islandContext, const SolverIslandObjectsStep& objects, IG::SimpleIslandManager& islandManager, + PxU32* bodyRemapTable, PxU32 solverBodyOffset, PxsContactManagerOutputIterator& outputs, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mObjects(objects), mIslandManager(islandManager), mBodyRemapTable(bodyRemapTable), mSolverBodyOffset(solverBodyOffset), + mOutputs(outputs), mContext(context) + { + } + + virtual const char* getName() const { return "SetupDescsTask"; } + + virtual void runInternal() + { + mContext.setupDescs(mIslandContext, mObjects, mIslandManager, mBodyRemapTable, mSolverBodyOffset, mOutputs); + mIslandContext.mArticulationOffset = mIslandContext.mThreadContext->contactDescArraySize; + } +}; + + +class PreIntegrateParallelTask : public Cm::Task +{ + PxsBodyCore** mBodyArray; + PxsRigidBody** mOriginalBodyArray; + PxTGSSolverBodyVel* mSolverBodyVelPool; + PxTGSSolverBodyTxInertia* mSolverBodyTxInertia; + PxStepSolverBodyData* mSolverBodyDataPool2; + PxU32* mNodeIndexArray; + const PxU32 mBodyCount; + const PxVec3& mGravity; + const PxReal mDt; + PxU32& mPosIters; + PxU32& mVelIters; + DynamicsTGSContext& mContext; + + PX_NOCOPY(PreIntegrateParallelTask) + +public: + + PreIntegrateParallelTask(PxsBodyCore** bodyArray, PxsRigidBody** originalBodyArray, + PxTGSSolverBodyVel* solverBodyVelPool, PxTGSSolverBodyTxInertia* solverBodyTxInertia, PxStepSolverBodyData* solverBodyDataPool2, + PxU32* nodeIndexArray, const PxU32 bodyCount, const PxVec3& gravity, const PxReal dt, PxU32& posIters, PxU32& velIters, + DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mBodyArray(bodyArray), mOriginalBodyArray(originalBodyArray), mSolverBodyVelPool(solverBodyVelPool), + mSolverBodyTxInertia(solverBodyTxInertia), mSolverBodyDataPool2(solverBodyDataPool2), mNodeIndexArray(nodeIndexArray), + mBodyCount(bodyCount), mGravity(gravity), mDt(dt), mPosIters(posIters), mVelIters(velIters), mContext(context) + { + } + + virtual const char* getName() const { return "PreIntegrateParallelTask"; } + + virtual void runInternal() + { + PxU32 posIters = 0; + PxU32 velIters = 0; + mContext.preIntegrateBodies(mBodyArray, mOriginalBodyArray, mSolverBodyVelPool, mSolverBodyTxInertia, mSolverBodyDataPool2, mNodeIndexArray, mBodyCount, mGravity, mDt, posIters, velIters, 0); + + Ps::atomicMax(reinterpret_cast(&mPosIters), PxI32(posIters)); + Ps::atomicMax(reinterpret_cast(&mVelIters), PxI32(velIters)); + + } +}; + + +class PreIntegrateTask : public Cm::Task +{ + PxsBodyCore** mBodyArray; + PxsRigidBody** mOriginalBodyArray; + PxTGSSolverBodyVel* mSolverBodyVelPool; + PxTGSSolverBodyTxInertia* mSolverBodyTxInertia; + PxStepSolverBodyData* mSolverBodyDataPool2; + PxU32* mNodeIndexArray; + const PxU32 mBodyCount; + const PxVec3& mGravity; + const PxReal mDt; + PxU32& mPosIters; + PxU32& mVelIters; + DynamicsTGSContext& mContext; + + PX_NOCOPY(PreIntegrateTask) + +public: + + PreIntegrateTask(PxsBodyCore** bodyArray, PxsRigidBody** originalBodyArray, + PxTGSSolverBodyVel* solverBodyVelPool, PxTGSSolverBodyTxInertia* solverBodyTxInertia, PxStepSolverBodyData* solverBodyDataPool2, + PxU32* nodeIndexArray, const PxU32 bodyCount, const PxVec3& gravity, const PxReal dt, PxU32& posIters, PxU32& velIters, + DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mBodyArray(bodyArray), mOriginalBodyArray(originalBodyArray), mSolverBodyVelPool(solverBodyVelPool), + mSolverBodyTxInertia(solverBodyTxInertia), mSolverBodyDataPool2(solverBodyDataPool2), mNodeIndexArray(nodeIndexArray), + mBodyCount(bodyCount), mGravity(gravity), mDt(dt), mPosIters(posIters), mVelIters(velIters), mContext(context) + { + } + + virtual const char* getName() const { return "PreIntegrateTask"; } + + virtual void runInternal() + { + const PxU32 BodiesPerTask = 512; + + if (mBodyCount <= BodiesPerTask) + { + PxU32 posIters = 0; + PxU32 velIters = 0; + mContext.preIntegrateBodies(mBodyArray, mOriginalBodyArray, mSolverBodyVelPool, mSolverBodyTxInertia, mSolverBodyDataPool2, mNodeIndexArray, mBodyCount, mGravity, mDt, posIters, velIters, 0); + + Ps::atomicMax(reinterpret_cast(&mPosIters), PxI32(posIters)); + Ps::atomicMax(reinterpret_cast(&mVelIters), PxI32(velIters)); + } + else + { + for (PxU32 i = 0; i < mBodyCount; i += BodiesPerTask) + { + const PxU32 nbToProcess = PxMin(mBodyCount - i, BodiesPerTask); + PreIntegrateParallelTask* task = PX_PLACEMENT_NEW(mContext.getTaskPool().allocate(sizeof(PreIntegrateParallelTask)), PreIntegrateParallelTask) + (mBodyArray+i, mOriginalBodyArray+i, mSolverBodyVelPool+i, mSolverBodyTxInertia+i, mSolverBodyDataPool2+i,mNodeIndexArray+i, + nbToProcess, mGravity, mDt, mPosIters, mVelIters, mContext); + + task->setContinuation(mCont); + task->removeReference(); + } + } + + + } +}; + +class SetStepperTask : public Cm::Task +{ + Dy::IslandContextStep& mIslandContext; + Dy::DynamicsTGSContext& mContext; + + PxBaseTask* mAdditionalContinuation; + + PX_NOCOPY(SetStepperTask) + +public: + + SetStepperTask(Dy::IslandContextStep& islandContext, + DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mContext(context), + mAdditionalContinuation(NULL) + { + } + + virtual const char* getName() const { return "SetStepperTask"; } + + void setAdditionalContinuation(PxBaseTask* cont) + { + mAdditionalContinuation = cont; + cont->addReference(); + } + + virtual void runInternal() + { + PxReal dt = mContext.getDt(); + + //ML: TGS can't work well with high velocity iteration counts, so we should limit the velocity iteration counts to be DY_MAX_ITERATION_COUNT. However, + //we should put the extra iterations to the position iteration count so the users will see some behaviour improvements + + const PxU32 newVelIters = PxMin(mIslandContext.mVelIters, PxU32(DY_MAX_VELOCITY_COUNT)); + const PxU32 remainVelIters = mIslandContext.mVelIters - newVelIters; + + mIslandContext.mVelIters = newVelIters; + mIslandContext.mPosIters = mIslandContext.mPosIters + remainVelIters; + + mIslandContext.mStepDt = dt / PxReal(mIslandContext.mPosIters); + mIslandContext.mInvStepDt = 1.f/mIslandContext.mStepDt;//PxMin(1000.f, 1.f / mIslandContext.mStepDt); + } + + virtual void release() + { + Cm::Task::release(); + mAdditionalContinuation->removeReference(); + } +}; + +class SetupArticulationTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + const PxVec3& mGravity; + const PxReal mDt; + PxU32& mPosIters; + PxU32& mVelIters; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(SetupArticulationTask) + +public: + + SetupArticulationTask(IslandContextStep& islandContext, const PxVec3& gravity, const PxReal dt, PxU32& posIters, + PxU32& velIters, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mGravity(gravity), mDt(dt), mPosIters(posIters), mVelIters(velIters), mContext(context) + { + } + + virtual const char* getName() const { return "SetupArticulationTask"; } + + virtual void runInternal() + { + PxU32 posIters = 0, velIters = 0; + mContext.setupArticulations(mIslandContext, mGravity, mDt, posIters, velIters, mCont); + + Ps::atomicMax(reinterpret_cast(&mPosIters), PxI32(posIters)); + Ps::atomicMax(reinterpret_cast(&mVelIters), PxI32(velIters)); + } +}; + +class SetupArticulationInternalConstraintsTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + const PxReal mDt; + const PxReal mInvDt; + PxTGSSolverConstraintDesc* mConstraintDescs; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(SetupArticulationInternalConstraintsTask) + +public: + + SetupArticulationInternalConstraintsTask(IslandContextStep& islandContext, PxReal dt, PxReal invDt, PxTGSSolverConstraintDesc* constraintDescs, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mDt(dt), mInvDt(invDt), mConstraintDescs(constraintDescs), mContext(context) + { + } + + virtual const char* getName() const { return "SetupArticulationInternalConstraintsTask"; } + + virtual void runInternal() + { + mContext.setupArticulationInternalConstraints(mIslandContext, mDt, mIslandContext.mInvStepDt, mConstraintDescs + mIslandContext.mArticulationOffset); + } +}; + +class SetupSolverConstraintsSubTask : public Cm::Task +{ + PxTGSSolverConstraintDesc* mContactDescPtr; + PxConstraintBatchHeader* mHeaders; + const PxU32 mNbHeaders; + PxsContactManagerOutputIterator& mOutputs; + PxReal mStepDt; + PxReal mTotalDt; + PxReal mInvStepDt; + PxReal mInvDtTotal; + PxU32 mNbSubsteps; + DynamicsTGSContext& mContext; + ThreadContext& mIslandThreadContext; + + PX_NOCOPY(SetupSolverConstraintsSubTask) + +public: + + static const PxU32 MaxPerTask = 64; + + SetupSolverConstraintsSubTask(PxTGSSolverConstraintDesc* contactDescPtr, PxConstraintBatchHeader* headers, const PxU32 nbHeaders, + PxsContactManagerOutputIterator& outputs, PxReal stepDt, PxReal totalDt, PxReal invStepDt, PxReal invDtTotal, PxU32 nbSubsteps, ThreadContext& islandThreadContext, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mContactDescPtr(contactDescPtr), mHeaders(headers), mNbHeaders(nbHeaders), mOutputs(outputs), mStepDt(stepDt), mTotalDt(totalDt), mInvStepDt(invStepDt), + mInvDtTotal(invDtTotal), mNbSubsteps(nbSubsteps), mContext(context), mIslandThreadContext(islandThreadContext) + { + } + + virtual const char* getName() const { return "SetupSolverConstraintsSubTask"; } + + virtual void runInternal() + { + ThreadContext* tempContext = mContext.getThreadContext(); + tempContext->mConstraintBlockStream.reset(); + mContext.createSolverConstraints(mContactDescPtr, mHeaders, mNbHeaders, mOutputs, mIslandThreadContext, *tempContext, mStepDt, mTotalDt, mInvStepDt, + mNbSubsteps); + mContext.putThreadContext(tempContext); + } + +}; + +class SetupSolverConstraintsTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + PxTGSSolverConstraintDesc* mContactDescPtr; + PxsContactManagerOutputIterator& mOutputs; + Dy::ThreadContext& mThreadContext; + PxReal mTotalDt; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(SetupSolverConstraintsTask) + +public: + + + + SetupSolverConstraintsTask(IslandContextStep& islandContext, PxTGSSolverConstraintDesc* contactDescPtr, + PxsContactManagerOutputIterator& outputs, Dy::ThreadContext& threadContext, PxReal totalDt, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mContactDescPtr(contactDescPtr), mOutputs(outputs), mThreadContext(threadContext), + mTotalDt(totalDt), mContext(context) + { + } + + virtual const char* getName() const { return "SetupSolverConstraintsTask"; } + + virtual void runInternal() + { + Dy::ThreadContext& threadContext = *mIslandContext.mThreadContext; + const PxU32 nbBatches = threadContext.numContactConstraintBatches; + PxConstraintBatchHeader* hdr = mIslandContext.mObjects.constraintBatchHeaders; + //for (PxU32 a = 0; a < mIslandContext.mArticulationOffset; a += SetupSolverConstraintsSubTask::MaxPerTask) + for (PxU32 a = 0; a < nbBatches; a += SetupSolverConstraintsSubTask::MaxPerTask) + { + const PxU32 nbConstraints = PxMin(nbBatches - a, SetupSolverConstraintsSubTask::MaxPerTask); + SetupSolverConstraintsSubTask* task = PX_PLACEMENT_NEW(mContext.mTaskPool.allocate(sizeof(SetupSolverConstraintsSubTask)), SetupSolverConstraintsSubTask) + (mContactDescPtr, hdr + a, nbConstraints, mOutputs, mIslandContext.mStepDt, mTotalDt, mIslandContext.mInvStepDt, mContext.mInvDt, mIslandContext.mPosIters, mThreadContext, mContext); + + task->setContinuation(mCont); + task->removeReference(); + } + } +}; + +static bool isArticulationConstraint(PxTGSSolverConstraintDesc& desc) +{ + return desc.linkIndexA != PxTGSSolverConstraintDesc::NO_LINK || + desc.linkIndexB != PxTGSSolverConstraintDesc::NO_LINK; +} + + +class PartitionTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + PxTGSSolverConstraintDesc* mContactDescPtr; + PxTGSSolverBodyVel* mSolverBodyData; + Dy::ThreadContext& mThreadContext; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(PartitionTask) + +public: + + + + PartitionTask(IslandContextStep& islandContext, PxTGSSolverConstraintDesc* contactDescPtr, PxTGSSolverBodyVel* solverBodyData, + Dy::ThreadContext& threadContext, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mContactDescPtr(contactDescPtr), mSolverBodyData(solverBodyData), mThreadContext(threadContext), + mContext(context) + { + } + + virtual const char* getName() const { return "PartitionTask"; } + + virtual void runInternal() + { + + ArticulationSolverDesc* artics = mThreadContext.getArticulations().begin(); + if (mIslandContext.mCounts.articulations) + { + PxU32 nbArticConstraints = artics[0].numInternalConstraints + mIslandContext.mArticulationOffset; + + PxTGSSolverConstraintDesc* currDesc = mContactDescPtr; + for (PxU32 a = 1, startIdx = mIslandContext.mArticulationOffset+DY_ARTICULATION_MAX_SIZE; a < mIslandContext.mCounts.articulations; ++a, startIdx += DY_ARTICULATION_MAX_SIZE) + { + //Compact pairs... + const PxU32 nbInternalConstraints = artics[a].numInternalConstraints; + const PxU32 endIdx = startIdx + nbInternalConstraints; + + for (PxU32 b = startIdx; b < endIdx; ++b) + { + currDesc[nbArticConstraints++] = currDesc[b]; + } + } + } + + PxU32 totalDescCount = mThreadContext.contactDescArraySize; + + mThreadContext.mConstraintsPerPartition.forceSize_Unsafe(0); + mThreadContext.mConstraintsPerPartition.resize(1); + mThreadContext.mConstraintsPerPartition[0] = 0; + + TGSConstraintPartitionArgs args; + args.enhancedDeterminism = false; + args.mBodies = mSolverBodyData; + args.mArticulationPtrs = artics; + args.mContactConstraintDescriptors = mContactDescPtr; + args.mNumArticulationPtrs = mThreadContext.getArticulations().size(); + args.mNumBodies = mIslandContext.mCounts.bodies; + args.mNumContactConstraintDescriptors = totalDescCount; + args.mOrderedContactConstraintDescriptors = mIslandContext.mObjects.orderedConstraintDescs; + args.mTempContactConstraintDescriptors = mIslandContext.mObjects.tempConstraintDescs; + args.mNumDifferentBodyConstraints = args.mNumSelfConstraints = args.mNumSelfConstraintBlocks = 0; + args.mConstraintsPerPartition = &mThreadContext.mConstraintsPerPartition; + args.mBitField = &mThreadContext.mPartitionNormalizationBitmap; + + //Partition constraints + mThreadContext.mMaxPartitions = partitionContactConstraintsStep(args); + mThreadContext.mNumDifferentBodyConstraints = args.mNumDifferentBodyConstraints; + mThreadContext.mNumSelfConstraints = args.mNumSelfConstraints; + mThreadContext.mNumSelfConstraintBlocks = args.mNumSelfConstraintBlocks; + + + { + PxU32 descCount = mThreadContext.mNumDifferentBodyConstraints; + PxU32 selfConstraintDescCount = mThreadContext.contactDescArraySize - mThreadContext.mNumDifferentBodyConstraints; + + Ps::Array& accumulatedConstraintsPerPartition = mThreadContext.mConstraintsPerPartition; + + PxU32 numHeaders = 0; + PxU32 currentPartition = 0; + PxU32 maxJ = descCount == 0 ? 0 : accumulatedConstraintsPerPartition[0]; + + const PxU32 maxBatchPartition = 0xFFFFFFFF; + + const PxU32 maxBatchSize = args.enhancedDeterminism ? 1u : 4u; + + PxConstraintBatchHeader* batchHeaders = mIslandContext.mObjects.constraintBatchHeaders; + + PxTGSSolverConstraintDesc* orderedConstraints = mIslandContext.mObjects.orderedConstraintDescs; + + PxU32 headersPerPartition = 0; + for (PxU32 a = 0; a < descCount;) + { + + + PxU32 loopMax = PxMin(maxJ - a, maxBatchSize); + PxU16 j = 0; + if (loopMax > 0) + { + PxConstraintBatchHeader& header = batchHeaders[numHeaders++]; + + j = 1; + PxTGSSolverConstraintDesc& desc = orderedConstraints[a]; + if (!isArticulationConstraint(desc) && (desc.constraintLengthOver16 == DY_SC_TYPE_RB_CONTACT || + desc.constraintLengthOver16 == DY_SC_TYPE_RB_1D) && currentPartition < maxBatchPartition) + { + for (; j < loopMax && desc.constraintLengthOver16 == orderedConstraints[a + j].constraintLengthOver16 && + !isArticulationConstraint(orderedConstraints[a + j]); ++j); + } + header.mStartIndex = a; + header.mStride = j; + header.mConstraintType = desc.constraintLengthOver16; + headersPerPartition++; + } + if (maxJ == (a + j) && maxJ != descCount) + { + //Go to next partition! + accumulatedConstraintsPerPartition[currentPartition] = headersPerPartition; + headersPerPartition = 0; + currentPartition++; + maxJ = accumulatedConstraintsPerPartition[currentPartition]; + } + a += j; + } + if (descCount) + accumulatedConstraintsPerPartition[currentPartition] = headersPerPartition; + + + + accumulatedConstraintsPerPartition.forceSize_Unsafe(mThreadContext.mMaxPartitions); + + PxU32 numDifferentBodyBatchHeaders = numHeaders; + + for (PxU32 a = 0; a < selfConstraintDescCount; ++a) + { + PxConstraintBatchHeader& header = batchHeaders[numHeaders++]; + header.mStartIndex = a + descCount; + header.mStride = 1; + header.mConstraintType = DY_SC_TYPE_EXT_1D; + } + + PxU32 numSelfConstraintBatchHeaders = numHeaders - numDifferentBodyBatchHeaders; + + mThreadContext.numDifferentBodyBatchHeaders = numDifferentBodyBatchHeaders; + mThreadContext.numSelfConstraintBatchHeaders = numSelfConstraintBatchHeaders; + mThreadContext.numContactConstraintBatches = numHeaders; + } + + } +}; + +PX_FORCE_INLINE PxU32 getConstraintLength(const PxTGSSolverConstraintDesc& desc) +{ + return PxU32(desc.constraintLengthOver16 << 4); +} + +class ParallelSolveTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + const SolverIslandObjectsStep& mObjects; + const PxsIslandIndices& mCounts; + ThreadContext& mThreadContext; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(ParallelSolveTask) + +public: + + ParallelSolveTask(IslandContextStep& islandContext, const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& threadContext, + DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mObjects(objects), mCounts(counts), mThreadContext(threadContext), mContext(context) + { + } + + virtual const char* getName() const { return "ParallelSolveTask"; } + + virtual void runInternal() + { + mContext.iterativeSolveIslandParallel(mObjects, mCounts, mThreadContext, mIslandContext.mStepDt, mIslandContext.mPosIters, mIslandContext.mVelIters, + &mIslandContext.mSharedSolverIndex, &mIslandContext.mSharedRigidBodyIndex, &mIslandContext.mSharedArticulationIndex, + &mIslandContext.mSolvedCount, &mIslandContext.mRigidBodyIntegratedCount, &mIslandContext.mArticulationIntegratedCount, + 4, 128); + } +}; + + +class SolveIslandTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + const SolverIslandObjectsStep& mObjects; + const PxsIslandIndices& mCounts; + ThreadContext& mThreadContext; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(SolveIslandTask) + +public: + + SolveIslandTask(IslandContextStep& islandContext, const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& threadContext, + DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mObjects(objects), mCounts(counts), mThreadContext(threadContext), mContext(context) + { + } + + virtual const char* getName() const { return "SolveIslandTask"; } + + virtual void runInternal() + { + + PxU32 j = 0, i = 0; + + PxU32 numBatches = 0; + + PxU32 currIndex = 0; + PxU32 totalCount = 0; + + PxTGSSolverConstraintDesc* contactDescBegin = mObjects.orderedConstraintDescs; + PxTGSSolverConstraintDesc* contactDescPtr = contactDescBegin; + PxConstraintBatchHeader* headers = mObjects.constraintBatchHeaders; + + PxU32 totalPartitions = 0; + for (PxU32 a = 0; a < mThreadContext.mConstraintsPerPartition.size(); ++a) + { + PxU32 endIndex = currIndex + mThreadContext.mConstraintsPerPartition[a]; + + PxU32 numBatchesInPartition = 0; + for (PxU32 b = currIndex; b < endIndex; ++b) + { + PxConstraintBatchHeader& _header = headers[b]; + PxU16 stride = _header.mStride, newStride = _header.mStride; + PxU32 startIndex = j; + for (PxU16 c = 0; c < stride; ++c) + { + if (getConstraintLength(contactDescBegin[i]) == 0) + { + newStride--; + i++; + } + else + { + if (i != j) + contactDescBegin[j] = contactDescBegin[i]; + i++; + j++; + contactDescPtr++; + } + } + + if (newStride != 0) + { + headers[numBatches].mStartIndex = startIndex; + headers[numBatches].mStride = newStride; + PxU8 type = *contactDescBegin[startIndex].constraint; + if (type == DY_SC_TYPE_STATIC_CONTACT) + { + //Check if any block of constraints is classified as type static (single) contact constraint. + //If they are, iterate over all constraints grouped with it and switch to "dynamic" contact constraint + //type if there's a dynamic contact constraint in the group. + for (PxU32 c = 1; c < newStride; ++c) + { + if (*contactDescBegin[startIndex + c].constraint == DY_SC_TYPE_RB_CONTACT) + { + type = DY_SC_TYPE_RB_CONTACT; + } + } + } + + headers[numBatches].mConstraintType = type; + numBatches++; + numBatchesInPartition++; + } + } + currIndex += mThreadContext.mConstraintsPerPartition[a]; + mThreadContext.mConstraintsPerPartition[totalPartitions] = numBatchesInPartition; + if (numBatchesInPartition) + totalPartitions++; + totalCount += numBatchesInPartition; + } + + currIndex = totalCount; + //Decision whether to spawn multi-threaded solver or single-threaded solver... + + mThreadContext.mConstraintsPerPartition.forceSize_Unsafe(totalPartitions); + mThreadContext.numContactConstraintBatches = totalCount; + + PxU32 maxLinks = 0; + for (PxU32 a = 0; a < mCounts.articulations; ++a) + { + ArticulationSolverDesc& desc = mThreadContext.getArticulations()[a]; + maxLinks = PxMax(maxLinks, PxU32(desc.linkCount)); + } + + mThreadContext.mZVector.forceSize_Unsafe(0); + mThreadContext.mZVector.reserve(maxLinks); + mThreadContext.mZVector.forceSize_Unsafe(maxLinks); + + mThreadContext.mDeltaV.forceSize_Unsafe(0); + mThreadContext.mDeltaV.reserve(maxLinks); + mThreadContext.mDeltaV.forceSize_Unsafe(maxLinks); + + SolverContext cache; + cache.Z = mThreadContext.mZVector.begin(); + cache.deltaV = mThreadContext.mDeltaV.begin(); + + if (mThreadContext.mConstraintsPerPartition.size()) + { + const PxU32 threadCount = this->getTaskManager()->getCpuDispatcher()->getWorkerCount(); + + const PxU32 nbHeadersPerPartition = ((currIndex + mThreadContext.mConstraintsPerPartition.size() - 1) / mThreadContext.mConstraintsPerPartition.size()); + + const PxU32 NbBatchesPerThread = 8; + + const PxU32 nbIdealThreads = (nbHeadersPerPartition + NbBatchesPerThread-1) / NbBatchesPerThread; + + if (threadCount < 2 || nbIdealThreads < 2) + mContext.iterativeSolveIsland(mObjects, mCounts, mThreadContext, mIslandContext.mStepDt, mIslandContext.mInvStepDt, + mIslandContext.mPosIters, mIslandContext.mVelIters, cache); + else + { + + mIslandContext.mSharedSolverIndex = 0; + mIslandContext.mSolvedCount = 0; + mIslandContext.mSharedRigidBodyIndex = 0; + mIslandContext.mRigidBodyIntegratedCount = 0; + mIslandContext.mSharedArticulationIndex = 0; + mIslandContext.mArticulationIntegratedCount = 0; + + PxU32 nbThreads = PxMin(threadCount, nbIdealThreads); + + ParallelSolveTask* tasks = reinterpret_cast(mContext.getTaskPool().allocate(sizeof(ParallelSolveTask)*nbThreads)); + + for (PxU32 a = 0; a < nbThreads; ++a) + { + PX_PLACEMENT_NEW(&tasks[a], ParallelSolveTask)(mIslandContext, mObjects, mCounts, mThreadContext, mContext); + tasks[a].setContinuation(mCont); + tasks[a].removeReference(); + } + } + } + else + { + mContext.iterativeSolveIsland(mObjects, mCounts, mThreadContext, mIslandContext.mStepDt, + mIslandContext.mInvStepDt, mIslandContext.mPosIters, mIslandContext.mVelIters, cache); + } + } +}; + +class EndIslandTask : public Cm::Task +{ + ThreadContext& mThreadContext; + DynamicsTGSContext& mContext; + + PX_NOCOPY(EndIslandTask) + +public: + + EndIslandTask(ThreadContext& threadContext, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mThreadContext(threadContext), mContext(context) + { + } + + virtual const char* getName() const { return "EndIslandTask"; } + + virtual void runInternal() + { + mContext.endIsland(mThreadContext); + } +}; + +class FinishSolveIslandTask : public Cm::Task +{ + ThreadContext& mThreadContext; + const SolverIslandObjectsStep& mObjects; + const PxsIslandIndices& mCounts; + IG::SimpleIslandManager& mIslandManager; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(FinishSolveIslandTask) + +public: + + FinishSolveIslandTask(ThreadContext& threadContext, const SolverIslandObjectsStep& objects, + const PxsIslandIndices& counts, IG::SimpleIslandManager& islandManager, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mThreadContext(threadContext), mObjects(objects), mCounts(counts), mIslandManager(islandManager), mContext(context) + { + } + + virtual const char* getName() const { return "FinishSolveIslandTask"; } + + virtual void runInternal() + { + mContext.finishSolveIsland(mThreadContext, mObjects, mCounts, mIslandManager, mCont); + } +}; + + + +void DynamicsTGSContext::iterativeSolveIsland(const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& mThreadContext, + const PxReal stepDt, const PxReal invStepDt, const PxU32 posIters, const PxU32 velIters, SolverContext& cache) +{ + PX_PROFILE_ZONE("Dynamics:solveIsland", 0); + PxReal elapsedTime = 0.f; + PxReal recipStepDt = 1.f/stepDt; + + const PxU32 bodyOffset = objects.solverBodyOffset; + + if (mThreadContext.numContactConstraintBatches == 0) + { + for (PxU32 i = 0; i < counts.articulations; ++i) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[i]; + for (PxU32 a = 1; a < posIters; a++) + { + d.articulation->solveInternalConstraints(stepDt, recipStepDt, mThreadContext.mZVector.begin(), mThreadContext.mDeltaV.begin(), false); + stepArticulations(mThreadContext, counts, stepDt); + } + + ArticulationPImpl::saveVelocityTGS(d, mInvDt); + + for (PxU32 a = 0; a < velIters; ++a) + { + d.articulation->solveInternalConstraints(stepDt, recipStepDt, mThreadContext.mZVector.begin(), mThreadContext.mDeltaV.begin(), true); + } + } + + integrateBodies(objects, counts.bodies, mSolverBodyVelPool.begin() + bodyOffset, mSolverBodyTxInertiaPool.begin() + bodyOffset, mSolverBodyDataPool2.begin() + bodyOffset, mDt); + return; + } + + + for (PxU32 a = 1; a < posIters; a++) + { + bool doFriction = true; + + for (PxU32 i = 0; i < counts.articulations; ++i) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[i]; + d.articulation->solveInternalConstraints(stepDt, recipStepDt, mThreadContext.mZVector.begin(), mThreadContext.mDeltaV.begin(), false); + } + + solveConstraintsIteration(objects.orderedConstraintDescs, objects.constraintBatchHeaders, mThreadContext.numContactConstraintBatches, doFriction, invStepDt, + mSolverBodyTxInertiaPool.begin(), elapsedTime, -PX_MAX_F32, cache); + integrateBodies(objects, counts.bodies, mSolverBodyVelPool.begin() + bodyOffset, mSolverBodyTxInertiaPool.begin() + bodyOffset, mSolverBodyDataPool2.begin() + bodyOffset, stepDt); + + stepArticulations(mThreadContext, counts, stepDt); + elapsedTime += stepDt; + } + + for (PxU32 i = 0; i < counts.articulations; ++i) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[i]; + d.articulation->solveInternalConstraints(stepDt, recipStepDt, mThreadContext.mZVector.begin(), mThreadContext.mDeltaV.begin(), false); + } + + solveConcludeConstraintsIteration(objects.orderedConstraintDescs, objects.constraintBatchHeaders, mThreadContext.numContactConstraintBatches, + mSolverBodyTxInertiaPool.begin(), elapsedTime, cache); + elapsedTime += stepDt; + + const PxReal invDt = mInvDt; + + integrateBodies(objects, counts.bodies, mSolverBodyVelPool.begin() + bodyOffset, mSolverBodyTxInertiaPool.begin() + bodyOffset, mSolverBodyDataPool2.begin() + bodyOffset, stepDt); + + stepArticulations(mThreadContext, counts, stepDt); + + for(PxU32 a = 0; a < counts.articulations; ++a) + { + Dy::ArticulationSolverDesc& desc = mThreadContext.getArticulations()[a]; + + //ArticulationPImpl::updateDeltaMotion(desc, stepDt, mThreadContext.mDeltaV.begin()); + ArticulationPImpl::saveVelocityTGS(desc, invDt); + } + + for (PxU32 a = 0; a < velIters; ++a) + { + for (PxU32 i = 0; i < counts.articulations; ++i) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[i]; + d.articulation->solveInternalConstraints(stepDt, recipStepDt, mThreadContext.mZVector.begin(), mThreadContext.mDeltaV.begin(), false); + } + bool doFriction = true; + solveConstraintsIteration(objects.orderedConstraintDescs, objects.constraintBatchHeaders, mThreadContext.numContactConstraintBatches, doFriction, invStepDt, + mSolverBodyTxInertiaPool.begin(), elapsedTime, 0.f, cache); + } + + writebackConstraintsIteration(objects.constraintBatchHeaders, objects.orderedConstraintDescs, mThreadContext.numContactConstraintBatches); +} + + +void DynamicsTGSContext::iterativeSolveIslandParallel(const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& mThreadContext, + const PxReal stepDt, const PxU32 posIters, const PxU32 velIters, PxI32* solverCounts, PxI32* integrationCounts, PxI32* articulationIntegrationCounts, + PxI32* solverProgressCount, PxI32* integrationProgressCount, PxI32* articulationProgressCount, PxU32 solverUnrollSize, PxU32 integrationUnrollSize) +{ + PX_PROFILE_ZONE("Dynamics:solveIslandParallel", 0); + Dy::ThreadContext& threadContext = *getThreadContext(); + PxU32 startSolveIdx = PxU32(Ps::atomicAdd(solverCounts, PxI32(solverUnrollSize))) - solverUnrollSize; + PxU32 nbSolveRemaining = solverUnrollSize; + + PxU32 startIntegrateIdx = PxU32(Ps::atomicAdd(integrationCounts, PxI32(integrationUnrollSize))) - integrationUnrollSize; + PxU32 nbIntegrateRemaining = integrationUnrollSize; + + //For now, just do articulations 1 at a time. Might need to tweak this later depending on performance + PxU32 startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + + PxU32 targetSolverProgressCount = 0, targetIntegrationProgressCount = 0, targetArticulationProgressCount = 0; + + const PxU32 nbSolverBatches = mThreadContext.numContactConstraintBatches; + const PxU32 nbBodies = counts.bodies;// + mKinematicCount; + const PxU32 nbArticulations = counts.articulations; + + PxTGSSolverConstraintDesc* contactDescs = objects.orderedConstraintDescs; + PxConstraintBatchHeader* batchHeaders = objects.constraintBatchHeaders; + + PxTGSSolverBodyVel* solverVels = mSolverBodyVelPool.begin(); + PxTGSSolverBodyTxInertia* solverTxInertias = mSolverBodyTxInertiaPool.begin(); + const PxStepSolverBodyData*const solverBodyData = mSolverBodyDataPool2.begin(); + + PxU32* constraintsPerPartitions = mThreadContext.mConstraintsPerPartition.begin(); + const PxU32 nbPartitions = mThreadContext.mConstraintsPerPartition.size(); + + const PxU32 bodyOffset = objects.solverBodyOffset; + + SolverContext cache; + cache.Z = threadContext.mZVector.begin(); + cache.deltaV = threadContext.mDeltaV.begin(); + + PxReal elapsedTime = 0.f; + + PxReal invStepDt = 1.f/ stepDt; + + for (PxU32 a = 1; a < posIters; ++a, targetIntegrationProgressCount += nbBodies, targetArticulationProgressCount += nbArticulations) + { + WAIT_FOR_PROGRESS(integrationProgressCount, PxI32(targetIntegrationProgressCount)); + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + PxU32 artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + + PxU32 nbArticsProcessed = 0; + while (artIcStartIdx < nbArticulations) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[artIcStartIdx]; + + d.articulation->solveInternalConstraints(stepDt, invStepDt, threadContext.mZVector.begin(), + threadContext.mDeltaV.begin(), false); + ArticulationPImpl::updateDeltaMotion(d, stepDt, cache.deltaV); + + nbArticsProcessed++; + + startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + } + + if (nbArticsProcessed) + Ps::atomicAdd(articulationProgressCount, PxI32(nbArticsProcessed)); + + targetArticulationProgressCount += nbArticulations; + + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + + PxU32 offset = 0; + for (PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + //Find the startIdx in the partition to process + PxU32 startIdx = startSolveIdx - targetSolverProgressCount; + + const PxU32 nbBatches = constraintsPerPartitions[b]; + + PxU32 nbSolved = 0; + while (startIdx < nbBatches) + { + PxU32 nbToSolve = PxMin(nbBatches - startIdx, nbSolveRemaining); + parallelSolveConstraints(contactDescs, batchHeaders + startIdx + offset, nbToSolve, true, + solverTxInertias, elapsedTime, -PX_MAX_F32, cache); + nbSolveRemaining -= nbToSolve; + startSolveIdx += nbToSolve; + startIdx += nbToSolve; + + nbSolved += nbToSolve; + + if (nbSolveRemaining == 0) + { + startSolveIdx = PxU32(Ps::atomicAdd(solverCounts, PxI32(solverUnrollSize))) - solverUnrollSize; + nbSolveRemaining = solverUnrollSize; + startIdx = startSolveIdx - targetSolverProgressCount; + } + } + + if (nbSolved) + Ps::atomicAdd(solverProgressCount, PxI32(nbSolved)); + + targetSolverProgressCount += nbBatches; + offset += nbBatches; + } + + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + + + PxU32 integStartIdx = startIntegrateIdx - targetIntegrationProgressCount; + + PxU32 nbIntegrated = 0; + while (integStartIdx < nbBodies) + { + PxU32 nbToIntegrate = PxMin(nbBodies - integStartIdx, nbIntegrateRemaining); + + parallelIntegrateBodies(solverVels + integStartIdx + bodyOffset, solverTxInertias + integStartIdx + bodyOffset, + solverBodyData + integStartIdx + bodyOffset, nbToIntegrate, stepDt); + + nbIntegrateRemaining -= nbToIntegrate; + startIntegrateIdx += nbToIntegrate; + integStartIdx += nbToIntegrate; + + nbIntegrated += nbToIntegrate; + + if (nbIntegrateRemaining == 0) + { + startIntegrateIdx = PxU32(Ps::atomicAdd(integrationCounts, PxI32(integrationUnrollSize))) - integrationUnrollSize; + nbIntegrateRemaining = integrationUnrollSize; + integStartIdx = startIntegrateIdx - targetIntegrationProgressCount; + } + } + + if (nbIntegrated) + Ps::atomicAdd(integrationProgressCount, PxI32(nbIntegrated)); + + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + + nbArticsProcessed = 0; + while (artIcStartIdx < nbArticulations) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[artIcStartIdx]; + ArticulationPImpl::updateDeltaMotion(d, stepDt, cache.deltaV); + + nbArticsProcessed++; + + startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + } + + if (nbArticsProcessed) + Ps::atomicAdd(articulationProgressCount, PxI32(nbArticsProcessed)); + + elapsedTime += stepDt; + + } + + { + WAIT_FOR_PROGRESS(integrationProgressCount, PxI32(targetIntegrationProgressCount)); + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + PxU32 artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + + PxU32 nbArticsProcessed = 0; + while (artIcStartIdx < nbArticulations) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[artIcStartIdx]; + + d.articulation->solveInternalConstraints(stepDt, invStepDt, threadContext.mZVector.begin(), + threadContext.mDeltaV.begin(), false); + ArticulationPImpl::updateDeltaMotion(d, stepDt, cache.deltaV); + + nbArticsProcessed++; + + startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + } + + if (nbArticsProcessed) + Ps::atomicAdd(articulationProgressCount, PxI32(nbArticsProcessed)); + + targetArticulationProgressCount += nbArticulations; + + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + + PxU32 offset = 0; + for (PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + //Find the startIdx in the partition to process + PxU32 startIdx = startSolveIdx - targetSolverProgressCount; + + const PxU32 nbBatches = constraintsPerPartitions[b]; + + PxU32 nbSolved = 0; + while (startIdx < nbBatches) + { + PxU32 nbToSolve = PxMin(nbBatches - startIdx, nbSolveRemaining); + solveConcludeConstraintsIteration(contactDescs, batchHeaders + startIdx + offset, nbToSolve, + solverTxInertias, elapsedTime, cache); + nbSolveRemaining -= nbToSolve; + startSolveIdx += nbToSolve; + startIdx += nbToSolve; + + nbSolved += nbToSolve; + + if (nbSolveRemaining == 0) + { + startSolveIdx = PxU32(Ps::atomicAdd(solverCounts, PxI32(solverUnrollSize))) - solverUnrollSize; + nbSolveRemaining = solverUnrollSize; + startIdx = startSolveIdx - targetSolverProgressCount; + } + } + + if (nbSolved) + Ps::atomicAdd(solverProgressCount, PxI32(nbSolved)); + + targetSolverProgressCount += nbBatches; + offset += nbBatches; + + + } + + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + + + const PxReal invDt = mInvDt; + //const PxReal invDtPt25 = mInvDt * 0.25f; + PxU32 integStartIdx = startIntegrateIdx - targetIntegrationProgressCount; + + PxU32 nbIntegrated = 0; + while (integStartIdx < nbBodies) + { + PxU32 nbToIntegrate = PxMin(nbBodies - integStartIdx, nbIntegrateRemaining); + + parallelIntegrateBodies(solverVels + integStartIdx + bodyOffset, solverTxInertias + integStartIdx + bodyOffset, + solverBodyData + integStartIdx + bodyOffset, nbToIntegrate, stepDt); + + nbIntegrateRemaining -= nbToIntegrate; + startIntegrateIdx += nbToIntegrate; + integStartIdx += nbToIntegrate; + + nbIntegrated += nbToIntegrate; + + if (nbIntegrateRemaining == 0) + { + startIntegrateIdx = PxU32(Ps::atomicAdd(integrationCounts, PxI32(integrationUnrollSize))) - integrationUnrollSize; + nbIntegrateRemaining = integrationUnrollSize; + integStartIdx = startIntegrateIdx - targetIntegrationProgressCount; + } + } + + if (nbIntegrated) + Ps::atomicAdd(integrationProgressCount, PxI32(nbIntegrated)); + + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + + nbArticsProcessed = 0; + while (artIcStartIdx < nbArticulations) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[artIcStartIdx]; + ArticulationPImpl::updateDeltaMotion(d, stepDt, cache.deltaV); + ArticulationPImpl::saveVelocityTGS(d, invDt); + + nbArticsProcessed++; + + startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + } + + if (nbArticsProcessed) + Ps::atomicAdd(articulationProgressCount, PxI32(nbArticsProcessed)); + + elapsedTime += stepDt; + + targetIntegrationProgressCount += nbBodies; + targetArticulationProgressCount += nbArticulations; + + putThreadContext(&threadContext); + } + + //Write back constraints... + + WAIT_FOR_PROGRESS(integrationProgressCount, PxI32(targetIntegrationProgressCount)); + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + + for (PxU32 a = 0; a < velIters; ++a) + { + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + + PxU32 artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + + PxU32 nbArticsProcessed = 0; + while (artIcStartIdx < nbArticulations) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[artIcStartIdx]; + + d.articulation->solveInternalConstraints(stepDt, invStepDt, threadContext.mZVector.begin(), + threadContext.mDeltaV.begin(), false); + ArticulationPImpl::updateDeltaMotion(d, stepDt, cache.deltaV); + + nbArticsProcessed++; + + startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + } + + if (nbArticsProcessed) + Ps::atomicAdd(articulationProgressCount, PxI32(nbArticsProcessed)); + + targetArticulationProgressCount += nbArticulations; + + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + PxU32 offset = 0; + for (PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + + //Find the startIdx in the partition to process + PxU32 startIdx = startSolveIdx - targetSolverProgressCount; + + const PxU32 nbBatches = constraintsPerPartitions[b]; + + PxU32 nbSolved = 0; + while (startIdx < nbBatches) + { + PxU32 nbToSolve = PxMin(nbBatches - startIdx, nbSolveRemaining); + parallelSolveConstraints(contactDescs, batchHeaders + startIdx + offset, nbToSolve, true, + solverTxInertias, elapsedTime, 0.f, cache); + nbSolveRemaining -= nbToSolve; + startSolveIdx += nbToSolve; + startIdx += nbToSolve; + + nbSolved += nbToSolve; + + + if (nbSolveRemaining == 0) + { + startSolveIdx = PxU32(Ps::atomicAdd(solverCounts, PxI32(solverUnrollSize))) - solverUnrollSize; + nbSolveRemaining = solverUnrollSize; + + startIdx = startSolveIdx - targetSolverProgressCount; + } + } + + if (nbSolved) + Ps::atomicAdd(solverProgressCount, PxI32(nbSolved)); + + targetSolverProgressCount += nbBatches; + offset += nbBatches; + } + } + + { + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + { + //Find the startIdx in the partition to process + PxU32 startIdx = startSolveIdx - targetSolverProgressCount; + + const PxU32 nbBatches = nbSolverBatches; + + PxU32 nbSolved = 0; + while (startIdx < nbBatches) + { + PxU32 nbToSolve = PxMin(nbBatches - startIdx, nbSolveRemaining); + parallelWritebackConstraintsIteration(contactDescs, batchHeaders + startIdx, nbToSolve); + nbSolveRemaining -= nbToSolve; + startSolveIdx += nbToSolve; + startIdx += nbToSolve; + + nbSolved += nbToSolve; + + if (nbSolveRemaining == 0) + { + startSolveIdx = PxU32(Ps::atomicAdd(solverCounts, PxI32(solverUnrollSize))) - solverUnrollSize; + nbSolveRemaining = solverUnrollSize; + startIdx = startSolveIdx - targetSolverProgressCount; + } + } + + if (nbSolved) + Ps::atomicAdd(solverProgressCount, PxI32(nbSolved)); + + targetSolverProgressCount += nbBatches; + } + + } + +} + +class CopyBackTask : public Cm::Task +{ + const SolverIslandObjectsStep& mObjects; + PxTGSSolverBodyVel* mVels; + PxTGSSolverBodyTxInertia* mTxInertias; + PxStepSolverBodyData* mSolverBodyDatas; + const PxReal mInvDt; + IG::IslandSim& mIslandSim; + const PxU32 mStartIdx; + const PxU32 mEndIdx; + DynamicsTGSContext& mContext; + + PX_NOCOPY(CopyBackTask) + +public: + + CopyBackTask(const SolverIslandObjectsStep& objects, + PxTGSSolverBodyVel* vels, PxTGSSolverBodyTxInertia* txInertias, + PxStepSolverBodyData* solverBodyDatas, PxReal invDt, IG::IslandSim& islandSim, + PxU32 startIdx, PxU32 endIdx, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mObjects(objects), mVels(vels), mTxInertias(txInertias), mSolverBodyDatas(solverBodyDatas), mInvDt(invDt), + mIslandSim(islandSim), mStartIdx(startIdx), mEndIdx(endIdx), mContext(context) + { + } + + virtual const char* getName() const { return "CopyBackTask"; } + + virtual void runInternal() + { + mContext.copyBackBodies(mObjects, mVels, mTxInertias, mSolverBodyDatas, mInvDt, mIslandSim, mStartIdx, mEndIdx); + } +}; + +class UpdateArticTask : public Cm::Task +{ + Dy::ThreadContext& mThreadContext; + PxU32 mStartIdx; + PxU32 mEndIdx; + PxReal mDt; + DynamicsTGSContext& mContext; + + PX_NOCOPY(UpdateArticTask) + +public: + + UpdateArticTask(Dy::ThreadContext& threadContext, PxU32 startIdx, PxU32 endIdx, PxReal dt, DynamicsTGSContext& context) : + Cm::Task(context.getContextId()), + mThreadContext(threadContext), mStartIdx(startIdx), mEndIdx(endIdx), mDt(dt), mContext(context) + { + } + + virtual const char* getName() const { return "UpdateArticTask"; } + + virtual void runInternal() + { + mContext.updateArticulations(mThreadContext, mStartIdx, mEndIdx, mDt); + } +}; + + +void DynamicsTGSContext::finishSolveIsland(ThreadContext& mThreadContext, const SolverIslandObjectsStep& objects, + const PxsIslandIndices& counts, IG::SimpleIslandManager& islandManager, PxBaseTask* continuation) +{ + mThreadContext.mConstraintBlockManager.reset(); + mThreadContext.mConstraintBlockStream.reset(); + + const PxU32 NbBodiesPerTask = 512; + + for (PxU32 a = 0; a < counts.bodies; a += NbBodiesPerTask) + { + CopyBackTask* task = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(CopyBackTask)), CopyBackTask) + (objects, mSolverBodyVelPool.begin() + objects.solverBodyOffset, + mSolverBodyTxInertiaPool.begin() + objects.solverBodyOffset, mSolverBodyDataPool2.begin() + objects.solverBodyOffset, + mInvDt, islandManager.getAccurateIslandSim(), a, PxMin(a + NbBodiesPerTask, counts.bodies),*this); + + task->setContinuation(continuation); + task->removeReference(); + } + + const PxU32 NbArticsPerTask = 2; + + for (PxU32 a = 0; a < counts.articulations; a += NbArticsPerTask) + { + UpdateArticTask* task = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(UpdateArticTask)), UpdateArticTask) + (mThreadContext, a, PxMin(counts.articulations, a+NbArticsPerTask), mDt, *this); + + task->setContinuation(continuation); + task->removeReference(); + } +} + + +void DynamicsTGSContext::endIsland(ThreadContext& mThreadContext) +{ + putThreadContext(&mThreadContext); +} + + +void DynamicsTGSContext::solveIsland(const SolverIslandObjectsStep& objects, + const PxsIslandIndices& counts, + const PxU32 solverBodyOffset, + IG::SimpleIslandManager& islandManager, + PxU32* bodyRemapTable, PxsMaterialManager* materialManager, + PxsContactManagerOutputIterator& iterator, + PxBaseTask* continuation) +{ + PX_UNUSED(continuation); + + PX_UNUSED(iterator); + PX_UNUSED(materialManager); + PX_UNUSED(bodyRemapTable); + PX_UNUSED(islandManager); + + ThreadContext& mThreadContext = *getThreadContext(); + + IslandContextStep& islandContext = *reinterpret_cast(mTaskPool.allocate(sizeof(IslandContextStep))); + islandContext.mThreadContext = &mThreadContext; + islandContext.mCounts = counts; + islandContext.mObjects = objects; + islandContext.mPosIters = 0; + islandContext.mVelIters = 0; + islandContext.mObjects.solverBodyOffset = solverBodyOffset; + + + prepareBodiesAndConstraints(islandContext.mObjects, islandManager, islandContext); + + ////Create task chain... + SetupDescsTask* descTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SetupDescsTask)), SetupDescsTask)(islandContext, islandContext.mObjects, islandManager, + bodyRemapTable, solverBodyOffset, iterator, *this); + PreIntegrateTask* intTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(PreIntegrateTask)), PreIntegrateTask)(islandContext.mObjects.bodyCoreArray, islandContext.mObjects.bodies, + mSolverBodyVelPool.begin() + solverBodyOffset, + mSolverBodyTxInertiaPool.begin() + solverBodyOffset, mSolverBodyDataPool2.begin() + solverBodyOffset, + mThreadContext.mNodeIndexArray, islandContext.mCounts.bodies, mGravity, mDt, islandContext.mPosIters, + islandContext.mVelIters, *this); + SetupArticulationTask* articTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SetupArticulationTask)), SetupArticulationTask)(islandContext, mGravity, + mDt, islandContext.mPosIters, islandContext.mVelIters, *this); + SetStepperTask* stepperTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SetStepperTask)), SetStepperTask)(islandContext, *this); + SetupArticulationInternalConstraintsTask* articConTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SetupArticulationInternalConstraintsTask)), SetupArticulationInternalConstraintsTask) + (islandContext, mDt, mInvDt, islandContext.mObjects.constraintDescs, *this); + PartitionTask* partitionTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(PartitionTask)), PartitionTask) + (islandContext, islandContext.mObjects.constraintDescs, mSolverBodyVelPool.begin()+solverBodyOffset+1, mThreadContext, + *this); + SetupSolverConstraintsTask* constraintTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SetupSolverConstraintsTask)), SetupSolverConstraintsTask) + (islandContext, islandContext.mObjects.orderedConstraintDescs, iterator, mThreadContext, + mDt, *this); + + SolveIslandTask* solveTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SolveIslandTask)), SolveIslandTask)(islandContext, islandContext.mObjects, islandContext.mCounts, mThreadContext, *this); + + FinishSolveIslandTask* finishTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(FinishSolveIslandTask)), FinishSolveIslandTask)(mThreadContext, islandContext.mObjects, islandContext.mCounts, islandManager, *this); + + EndIslandTask* endTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(EndIslandTask)), EndIslandTask)(mThreadContext, *this); + + endTask->setContinuation(continuation); + finishTask->setContinuation(endTask); + solveTask->setContinuation(finishTask); + constraintTask->setContinuation(solveTask); + partitionTask->setContinuation(constraintTask); + articConTask->setContinuation(partitionTask); + //Stepper triggers both articCon and constraintTask + stepperTask->setContinuation(articConTask); + stepperTask->setAdditionalContinuation(constraintTask); + + articTask->setContinuation(stepperTask); + intTask->setContinuation(stepperTask); + descTask->setContinuation(stepperTask); + + endTask->removeReference(); + finishTask->removeReference(); + solveTask->removeReference(); + constraintTask->removeReference(); + partitionTask->removeReference(); + articConTask->removeReference(); + stepperTask->removeReference(); + articTask->removeReference(); + intTask->removeReference(); + descTask->removeReference(); + + + //Launch a setupDescs task + //setupDescs(islandContext, objects, islandManager, bodyRemapTable, mKinematicCount, iterator); + // + ////Launch a preIntegrateBodies task + //preIntegrateBodies(objects.bodyCoreArray, objects.bodies, mThreadContext.solverBodyData.begin() + mKinematicCount, + // mThreadContext.mNodeIndexArray, counts.bodies, mGravity, mDt, islandContext.mPosIters, islandContext.mVelIters, 0); + + //const PxU32 articConstraintOffset = mThreadContext.contactDescArraySize; + //islandContext.mArticulationOffset = mThreadContext.contactDescArraySize; + + ////Launch an articulation setup task + //setupArticulations(islandContext, mGravity, mDt, islandContext.mPosIters, islandContext.mVelIters); + + //PxU32 substeps = islandContext.mPosIters; + + //PxReal stepDt = mDt / PxReal(substeps); + //PxReal invStepDt = PxMin(1000.f, mInvDt*PxReal(substeps)); + + //islandContext.mStepDt = stepDt; + //islandContext.mInvStepDt = invStepDt; + + ////Setup articulation constraints - needs to be refactored for compaction etc. + //setupArticulationInternalConstraints(islandContext, mDt, invStepDt, objects.constraintDescs + articConstraintOffset); + + //createSolverConstraints(objects.constraintDescs, articConstraintOffset, mThreadContext.solverBodyData.begin(), iterator, mThreadContext, + // mDt, invStepDt, substeps); + + //iterativeSolveIsland(objects, counts, mThreadContext, stepDt, invStepDt, islandContext.mPosIters, islandContext.mVelIters); + + //endIsland(mThreadContext, objects, counts, islandManager); + +} + +void DynamicsTGSContext::updateBodyCore(PxBaseTask* continuation) +{ + PX_UNUSED(continuation); +} + +void DynamicsTGSContext::mergeResults() +{ +} + + + +} +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h new file mode 100644 index 000000000..6d2754074 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h @@ -0,0 +1,637 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_TGS_DYNAMICS_H +#define DY_TGS_DYNAMICS_H + +#include "PxvConfig.h" +#include "CmSpatialVector.h" +#include "CmTask.h" +#include "CmPool.h" +#include "PxcThreadCoherentCache.h" +#include "DyThreadContext.h" +#include "PxcConstraintBlockStream.h" +#include "DySolverBody.h" +#include "DyContext.h" +#include "PxsIslandManagerTypes.h" +#include "PxvNphaseImplementationContext.h" +#include "solver/PxSolverDefs.h" +#include "PxsIslandSim.h" + +namespace physx +{ + + namespace Cm + { + class FlushPool; + } + + namespace IG + { + class SimpleIslandManager; + struct Edge; + } + + class PxsRigidBody; + + class PxsStreamedThresholdTable; + + struct PxsBodyCore; + struct PxsIslandObjects; + class PxsIslandIndices; + struct PxsIndexedInteraction; + class PxsIslandManager; + struct PxsIndexedConstraint; + struct PxsIndexedContactManager; + class PxsHeapMemoryAllocator; + class PxsMemoryManager; + class PxsDefaultMemoryManager; + + + namespace Cm + { + class Bitmap; + class SpatialVector; + } + + namespace Dy + { + class SolverCore; + struct SolverIslandParams; + struct ArticulationSolverDesc; + class ArticulationV; + class DynamicsContext; + struct SolverContext; + + struct PxTGSSolverConstraintDesc + { + static const PxU16 NO_LINK = 0xffff; + + enum ConstraintType + { + eCONTACT_CONSTRAINT, //!< Defines this pair is a contact constraint + eJOINT_CONSTRAINT //!< Defines this pair is a joint constraint + }; + + union + { + PxTGSSolverBodyVel* bodyA; //!< bodyA pointer + Dy::ArticulationV* articulationA; //!< Articulation pointer for body A + }; + + union + { + PxTGSSolverBodyVel* bodyB; //!< BodyB pointer + Dy::ArticulationV* articulationB; //!< Articulation pointer for body B + }; + + PxU16 linkIndexA; + PxU16 linkIndexB; + union + { + PxU16 articulationALength; + PxU32 bodyAIdx; + }; + union + { + PxU16 articulationBLength; + PxU32 bodyBIdx; + }; + + PxU16 writeBackLengthOver4; //!< writeBackLength/4, max writeback length is 256K, allows PxSolverConstraintDesc to fit in 32 bytes + PxU16 constraintLengthOver16; //!< constraintLength/16, max constraint length is 1MB, allows PxSolverConstraintDesc to fit in 32 bytes + + PxU8* constraint; //!< Pointer to the constraint rows to be solved + void* writeBack; //!< Pointer to the writeback structure results for this given constraint are to be written to + + }; + + struct SolverIslandObjectsStep + { + PxsRigidBody** bodies; + ArticulationV** articulations; + ArticulationV** articulationOwners; + PxsIndexedContactManager* contactManagers; + + const IG::IslandId* islandIds; + PxU32 numIslands; + PxU32* bodyRemapTable; + PxU32* nodeIndexArray; + + PxTGSSolverConstraintDesc* constraintDescs; + PxTGSSolverConstraintDesc* orderedConstraintDescs; + PxTGSSolverConstraintDesc* tempConstraintDescs; + PxConstraintBatchHeader* constraintBatchHeaders; + Cm::SpatialVector* motionVelocities; + PxsBodyCore** bodyCoreArray; + + PxU32 solverBodyOffset; + + SolverIslandObjectsStep() : bodies(NULL), articulations(NULL), articulationOwners(NULL), + contactManagers(NULL), islandIds(NULL), numIslands(0), nodeIndexArray(NULL), constraintDescs(NULL), motionVelocities(NULL), bodyCoreArray(NULL), + solverBodyOffset(0) + { + } + }; + + struct IslandContextStep + { + //The thread context for this island (set in in the island start task, released in the island end task) + ThreadContext* mThreadContext; + PxsIslandIndices mCounts; + SolverIslandObjectsStep mObjects; + PxU32 mPosIters; + PxU32 mVelIters; + PxU32 mArticulationOffset; + PxReal mStepDt; + PxReal mInvStepDt; + PxI32 mSharedSolverIndex; + PxI32 mSolvedCount; + PxI32 mSharedRigidBodyIndex; + PxI32 mRigidBodyIntegratedCount; + PxI32 mSharedArticulationIndex; + PxI32 mArticulationIntegratedCount; + }; + + + + + struct PxTGSSolverConstraintPrepDescBase + { + PxConstraintInvMassScale mInvMassScales; //!< In: The local mass scaling for this pair. + + PxTGSSolverConstraintDesc* desc; //!< Output: The PxSolverConstraintDesc filled in by contact prep + + const PxTGSSolverBodyVel* body0; //!< In: The first body. Stores velocity information. Unused unless contact involves articulations. + const PxTGSSolverBodyVel *body1; //!< In: The second body. Stores velocity information. Unused unless contact involves articulations. + + const PxTGSSolverBodyTxInertia* body0TxI; + const PxTGSSolverBodyTxInertia* body1TxI; + + const PxStepSolverBodyData* bodyData0; + const PxStepSolverBodyData* bodyData1; + + PxTransform bodyFrame0; //!< In: The world-space transform of the first body. + PxTransform bodyFrame1; //!< In: The world-space transform of the second body. + + PxSolverContactDesc::BodyState bodyState0; //!< In: Defines what kind of actor the first body is + PxSolverContactDesc::BodyState bodyState1; //!< In: Defines what kind of actor the second body is + + }; + + struct PxTGSSolverConstraintPrepDesc : public PxTGSSolverConstraintPrepDescBase + { + Px1DConstraint* rows; //!< The start of the constraint rows + PxU32 numRows; //!< The number of rows + + PxReal linBreakForce, angBreakForce; //!< Break forces + PxReal minResponseThreshold; //!< The minimum response threshold + void* writeback; //!< Pointer to constraint writeback structure. Reports back joint breaking. If not required, set to NULL. + bool disablePreprocessing; //!< Disable joint pre-processing. Pre-processing can improve stability but under certain circumstances, e.g. when some invInertia rows are zero/almost zero, can cause instabilities. + bool improvedSlerp; //!< Use improved slerp model + bool driveLimitsAreForces; //!< Indicates whether drive limits are forces + bool extendedLimits; //!< Indiciates whether extended limits are used + + PxVec3 body0WorldOffset; //!< Body0 world offset + PxVec3 cA2w; //!< Location of anchor point A in world space + PxVec3 cB2w; //!< Location of anchor point B in world space + }; + + + struct PxTGSSolverContactDesc : public PxTGSSolverConstraintPrepDescBase + { + + Sc::ShapeInteraction* shapeInteraction; //!< Pointer to share interaction. Used for force threshold reports in solver. Set to NULL if using immediate mode. + Gu::ContactPoint* contacts; //!< The start of the contacts for this pair + PxU32 numContacts; //!< The total number of contacs this pair references. + + bool hasMaxImpulse; //!< Defines whether this pairs has maxImpulses clamping enabled + bool disableStrongFriction; //!< Defines whether this pair disables strong friction (sticky friction correlation) + bool hasForceThresholds; //!< Defines whether this pair requires force thresholds + + PxReal restDistance; //!< A distance at which the solver should aim to hold the bodies separated. Default is 0 + PxReal maxCCDSeparation; //!< A distance used to configure speculative CCD behavior. Default is PX_MAX_F32. Set internally in PhysX for bodies with eENABLE_SPECULATIVE_CCD on. Do not set directly! + + PxU8* frictionPtr; //!< InOut: Friction patch correlation data. Set each frame by solver. Can be retained for improved behaviour or discarded each frame. + PxU8 frictionCount; //!< The total number of friction patches in this pair + + PxReal* contactForces; //!< Out: A buffer for the solver to write applied contact forces to. + + PxU32 startFrictionPatchIndex; //!< Start index of friction patch in the correlation buffer. Set by friction correlation + PxU32 numFrictionPatches; //!< Total number of friction patches in this pair. Set by friction correlation + + PxU32 startContactPatchIndex; //!< The start index of this pair's contact patches in the correlation buffer. For internal use only + PxU16 numContactPatches; //!< Total number of contact patches. + PxU16 axisConstraintCount; //!< Axis constraint count. Defines how many constraint rows this pair has produced. Useful for statistical purposes. + + PxReal maxImpulse; + + PxReal torsionalPatchRadius; + PxReal minTorsionalPatchRadius; + }; + + + + struct SolverIslandObjectsStep; + + class SolverBodyVelDataPool : public Ps::Array > > + { + PX_NOCOPY(SolverBodyVelDataPool) + public: + SolverBodyVelDataPool() {} + }; + + class SolverBodyTxInertiaPool : public Ps::Array > > + { + PX_NOCOPY(SolverBodyTxInertiaPool) + public: + SolverBodyTxInertiaPool() {} + }; + + class SolverBodyDataStepPool : public Ps::Array > > + { + PX_NOCOPY(SolverBodyDataStepPool) + public: + SolverBodyDataStepPool() {} + }; + + + + class SolverStepConstraintDescPool : public Ps::Array > > + { + PX_NOCOPY(SolverStepConstraintDescPool) + public: + SolverStepConstraintDescPool() { } + }; + + +#if PX_VC +#pragma warning(push) +#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + + class DynamicsTGSContext : public Context + { + PX_NOCOPY(DynamicsTGSContext) + public: + + /**PxBaseTask* continuation + \brief Creates a DynamicsContext associated with a PxsContext + \return A pointer to the newly-created DynamicsContext. + */ + static DynamicsTGSContext* create(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocator, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal lengthScale + ); + + /** + \brief Destroys this DynamicsContext + */ + void destroy(); + + /** + \brief Returns the static world solver body + \return The static world solver body. + */ + //PX_FORCE_INLINE PxSolverBody& getWorldSolverBody() { return mWorldSolverBody; } + + PX_FORCE_INLINE Cm::FlushPool& getTaskPool() { return mTaskPool; } + + PX_FORCE_INLINE ThresholdStream& getThresholdStream() { return *mThresholdStream; } + + PX_FORCE_INLINE PxvSimStats& getSimStats() { return mSimStats; } + +#if PX_ENABLE_SIM_STATS + void addThreadStats(const ThreadContext::ThreadSimStats& stats); +#endif + + /** + \brief The entry point for the constraint solver. + \param[in] dt The simulation time-step + \param[in] continuation The continuation task for the solver + + This method is called after the island generation has completed. Its main responsibilities are: + (1) Reserving the solver body pools + (2) Initializing the static and kinematic solver bodies, which are shared resources between islands. + (3) Construct the solver task chains for each island + + Each island is solved as an independent solver task chain in parallel. + + */ + + virtual void update(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask, + PxsContactManager** foundPatchManagers, PxU32 nbFoundPatchManagers, PxsContactManager** lostPatchManagers, PxU32 nbLostPatchManagers, + PxU32 maxPatchesPerCM, PxsContactManagerOutputIterator& iter, PxsContactManagerOutput* gpuOutputs, const PxReal dt, const PxVec3& gravity, const PxU32 bitMapWordCounts); + + void updatePostKinematic(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, + PxBaseTask* lostTouchTask); + + virtual void processLostPatches(IG::SimpleIslandManager& /*simpleIslandManager*/, PxsContactManager** /*lostPatchManagers*/, PxU32 /*nbLostPatchManagers*/, PxsContactManagerOutputIterator& /*iterator*/){} + + virtual void updateBodyCore(PxBaseTask* continuation); + + virtual void setSimulationController(PxsSimulationController* simulationController){ mSimulationController = simulationController; } + /** + \brief This method combines the results of several islands, e.g. constructing scene-level simulation statistics and merging together threshold streams for contact notification. + */ + virtual void mergeResults(); + + virtual void getDataStreamBase(void*& /*contactStreamBase*/, void*& /*patchStreamBase*/, void*& /*forceAndIndicesStreamBase*/){} + + /** + \brief Allocates and returns a thread context object. + \return A thread context. + */ + PX_FORCE_INLINE ThreadContext* getThreadContext() + { + return mThreadContextPool.get(); + } + + /** + \brief Returns a thread context to the thread context pool. + \param[in] context The thread context to return to the thread context pool. + */ + void putThreadContext(ThreadContext* context) + { + mThreadContextPool.put(context); + } + + + PX_FORCE_INLINE PxU32 getKinematicCount() const { return mKinematicCount; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + + protected: + + /** + \brief Constructor for DynamicsContext + */ + DynamicsTGSContext(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocator, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal lengthScale + ); + /** + \brief Destructor for DynamicsContext + */ + virtual ~DynamicsTGSContext(); + + + // Solver helper-methods + /** + \brief Computes the unconstrained velocity for a given PxsRigidBody + \param[in] atom The PxsRigidBody + */ + void computeUnconstrainedVelocity(PxsRigidBody* atom) const; + + /** + \brief fills in a PxSolverConstraintDesc from an indexed interaction + \param[in,out] desc The PxSolverConstraintDesc + \param[in] constraint The PxsIndexedInteraction + */ + void setDescFromIndices(PxTGSSolverConstraintDesc& desc, + const PxsIndexedInteraction& constraint, const PxU32 solverBodyOffset, PxTGSSolverBodyVel* solverBodies); + + + void setDescFromIndices(PxTGSSolverConstraintDesc& desc, IG::EdgeIndex edgeIndex, + const IG::SimpleIslandManager& islandManager, PxU32* bodyRemapTable, const PxU32 solverBodyOffset, PxTGSSolverBodyVel* solverBodies); + + + void solveIsland(const SolverIslandObjectsStep& objects, + const PxsIslandIndices& counts, + const PxU32 solverBodyOffset, + IG::SimpleIslandManager& islandManager, + PxU32* bodyRemapTable, PxsMaterialManager* materialManager, + PxsContactManagerOutputIterator& iterator, + PxBaseTask* continuation); + + void prepareBodiesAndConstraints(const SolverIslandObjectsStep& objects, + IG::SimpleIslandManager& islandManager, + IslandContextStep& islandContext); + + void setupDescs(IslandContextStep& islandContext, const SolverIslandObjectsStep& objects, IG::SimpleIslandManager& mIslandManager, PxU32* mBodyRemapTable, PxU32 mSolverBodyOffset, + PxsContactManagerOutputIterator& outputs); + + void preIntegrateBodies(PxsBodyCore** bodyArray, PxsRigidBody** originalBodyArray, + PxTGSSolverBodyVel* solverBodyVelPool, PxTGSSolverBodyTxInertia* solverBodyTxInertia, PxStepSolverBodyData* solverBodyDataPool2, + PxU32* nodeIndexArray, const PxU32 bodyCount, const PxVec3& gravity, const PxReal dt, PxU32& posIters, PxU32& velIters, PxU32 iteration); + + void setupArticulations(IslandContextStep& islandContext, const PxVec3& gravity, const PxReal dt, PxU32& posIters, PxU32& velIters, PxBaseTask* continuation); + + PxU32 setupArticulationInternalConstraints(IslandContextStep& islandContext, PxReal dt, PxReal invStepDt, PxTGSSolverConstraintDesc* constraintDescs); + + void createSolverConstraints(PxTGSSolverConstraintDesc* contactDescPtr, PxConstraintBatchHeader* headers, const PxU32 nbHeaders, + PxsContactManagerOutputIterator& outputs, Dy::ThreadContext& islandThreadContext, Dy::ThreadContext& threadContext, PxReal stepDt, PxReal totalDt, + PxReal invStepDt, PxU32 nbSubsteps); + + void solveConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, bool doFriction, PxReal invStepDt, + const PxTGSSolverBodyTxInertia* const solverTxInertia, const PxReal elapsedTime, const PxReal minPenetration, SolverContext& cache); + + void solveConcludeConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, + PxTGSSolverBodyTxInertia* solverTxInertia, const PxReal elapsedTime, SolverContext& cache); + + void parallelSolveConstraints(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, bool doFriction, PxTGSSolverBodyTxInertia* solverTxInertia, + const PxReal elapsedTime, const PxReal minPenetration, SolverContext& cache); + + void writebackConstraintsIteration(const PxConstraintBatchHeader* const hdrs, const PxTGSSolverConstraintDesc* const contactDescPtr, const PxU32 nbHeaders); + + void parallelWritebackConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders); + + void integrateBodies(const SolverIslandObjectsStep& objects, + const PxU32 count, PxTGSSolverBodyVel* vels, + PxTGSSolverBodyTxInertia* txInertias, const PxStepSolverBodyData*const bodyDatas, PxReal dt); + + void parallelIntegrateBodies(PxTGSSolverBodyVel* vels, PxTGSSolverBodyTxInertia* txInertias, + const PxStepSolverBodyData* const bodyDatas, const PxU32 count, PxReal dt); + + void copyBackBodies(const SolverIslandObjectsStep& objects, + PxTGSSolverBodyVel* vels, PxTGSSolverBodyTxInertia* txInertias, + PxStepSolverBodyData* solverBodyData, PxReal invDt, IG::IslandSim& islandSim, + PxU32 startIdx, PxU32 endIdx); + + void updateArticulations(Dy::ThreadContext& threadContext, const PxU32 startIdx, const PxU32 endIdx, PxReal dt); + + void stepArticulations(Dy::ThreadContext& threadContext, const PxsIslandIndices& counts, PxReal dt); + + void iterativeSolveIsland(const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& mThreadContext, + const PxReal stepDt, const PxReal invStepDt, const PxU32 posIters, const PxU32 velIters, SolverContext& cache); + + void iterativeSolveIslandParallel(const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& mThreadContext, + const PxReal stepDt, const PxU32 posIters, const PxU32 velIters, PxI32* solverCounts, PxI32* integrationCounts, PxI32* articulationIntegrationCounts, + PxI32* solverProgressCount, PxI32* integrationProgressCount, PxI32* articulationProgressCount, PxU32 solverUnrollSize, PxU32 integrationUnrollSize); + + void endIsland(ThreadContext& mThreadContext); + + void finishSolveIsland(ThreadContext& mThreadContext, const SolverIslandObjectsStep& objects, + const PxsIslandIndices& counts, IG::SimpleIslandManager& islandManager, PxBaseTask* continuation); + + + + + /** + \brief Resets the thread contexts + */ + void resetThreadContexts(); + + /** + \brief Returns the scratch memory allocator. + \return The scratch memory allocator. + */ + PX_FORCE_INLINE PxcScratchAllocator& getScratchAllocator() { return mScratchAllocator; } + + //Data + + PxTGSSolverBodyVel mWorldSolverBodyVel; + PxTGSSolverBodyTxInertia mWorldSolverBodyTxInertia; + PxStepSolverBodyData mWorldSolverBodyData2; + + /** + \brief A thread context pool + */ + PxcThreadCoherentCache mThreadContextPool; + + /** + \brief Solver constraint desc array + */ + SolverStepConstraintDescPool mSolverConstraintDescPool; + + SolverStepConstraintDescPool mOrderedSolverConstraintDescPool; + + SolverStepConstraintDescPool mTempSolverConstraintDescPool; + + Ps::Array mContactConstraintBatchHeaders; + + /** + \brief Array of motion velocities for all bodies in the scene. + */ + Ps::Array mMotionVelocityArray; + + /** + \brief Array of body core pointers for all bodies in the scene. + */ + Ps::Array mBodyCoreArray; + + /** + \brief Array of rigid body pointers for all bodies in the scene. + */ + Ps::Array mRigidBodyArray; + + /** + \brief Array of articulationpointers for all articulations in the scene. + */ + Ps::Array mArticulationArray; + + SolverBodyVelDataPool mSolverBodyVelPool; + + SolverBodyTxInertiaPool mSolverBodyTxInertiaPool; + + SolverBodyDataStepPool mSolverBodyDataPool2; + + + ThresholdStream* mExceededForceThresholdStream[2]; //this store previous and current exceeded force thresholdStream + + Ps::Array mExceededForceThresholdStreamMask; + + Ps::Array mSolverBodyRemapTable; //Remaps from the "active island" index to the index within a solver island + + Ps::Array mNodeIndexArray; //island node index + + Ps::Array mContactList; + + /** + \brief The total number of kinematic bodies in the scene + */ + PxU32 mKinematicCount; + + /** + \brief Atomic counter for the number of threshold stream elements. + */ + PxI32 mThresholdStreamOut; + + + + PxsMaterialManager* mMaterialManager; + + PxsContactManagerOutputIterator mOutputIterator; + + PxReal mLengthScale; + + private: + //private: + PxcScratchAllocator& mScratchAllocator; + Cm::FlushPool& mTaskPool; + PxTaskManager* mTaskManager; + PxU32 mCurrentIndex; // this is the index point to the current exceeded force threshold stream + + PxU64 mContextID; + + friend class SetupDescsTask; + friend class PreIntegrateTask; + friend class SetupArticulationTask; + friend class SetupArticulationInternalConstraintsTask; + friend class SetupSolverConstraintsTask; + friend class SolveIslandTask; + friend class EndIslandTask; + friend class SetupSolverConstraintsSubTask; + friend class ParallelSolveTask; + friend class PreIntegrateParallelTask; + friend class CopyBackTask; + friend class UpdateArticTask; + friend class FinishSolveIslandTask; + }; + +#if PX_VC +#pragma warning(pop) +#endif + + } +} + +#endif + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp new file mode 100644 index 000000000..2b65cafdf --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp @@ -0,0 +1,961 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "DyTGSPartition.h" +#include "DyArticulationUtils.h" +#include "PsAlloca.h" +#include "foundation/PxMemory.h" + +#define INTERLEAVE_SELF_CONSTRAINTS 1 + + +namespace physx +{ + namespace Dy + { + + namespace + { + + PX_FORCE_INLINE PxU32 getArticulationIndex(const uintptr_t eaArticulation, const uintptr_t* eaArticulations, const PxU32 numEas) + { + PxU32 index = 0xffffffff; + for (PxU32 i = 0; ipartitionMask; + bodyBProgress = desc.bodyB->partitionMask; + return desc.bodyAIdx != 0 && desc.bodyBIdx != 0; + } + + PX_FORCE_INLINE void getProgress(const PxTGSSolverConstraintDesc& desc, + PxU32& bodyAProgress, PxU32& bodyBProgress) + { + bodyAProgress = desc.bodyA->partitionMask; + bodyBProgress = desc.bodyB->partitionMask; + } + + PX_FORCE_INLINE void storeProgress(const PxTGSSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress, + const PxU16 availablePartition) + { + desc.bodyA->partitionMask = bodyAProgress; + desc.bodyA->maxDynamicPartition = PxMax(desc.bodyA->maxDynamicPartition, availablePartition); + desc.bodyB->partitionMask = bodyBProgress; + desc.bodyB->maxDynamicPartition = PxMax(desc.bodyB->maxDynamicPartition, availablePartition); + } + + PX_FORCE_INLINE void storeProgress(const PxTGSSolverConstraintDesc& desc, const bool activeA, const bool activeB, + const PxU32 bodyAProgress, const PxU32 bodyBProgress) + { + if(activeA) + desc.bodyA->partitionMask = bodyAProgress; + if(activeB) + desc.bodyB->partitionMask = bodyBProgress; + } + + PX_FORCE_INLINE void recordStaticConstraint(const PxTGSSolverConstraintDesc& desc, bool& activeA, bool& activeB) + { + if (activeA) + desc.bodyA->nbStaticInteractions++; + + if (activeB) + desc.bodyB->nbStaticInteractions++; + + } + + PX_FORCE_INLINE PxU32 getStaticContactWriteIndex(const PxTGSSolverConstraintDesc& desc, + bool activeA, bool activeB) + { + if (activeA) + { + return PxU32(desc.bodyA->maxDynamicPartition + desc.bodyA->nbStaticInteractions++); + + } + else if (activeB) + { + return PxU32(desc.bodyB->maxDynamicPartition + desc.bodyB->nbStaticInteractions++); + } + + return 0xffffffff; + } + + PX_FORCE_INLINE void clearState() + { + for (PxU32 a = 0; a < mNumBodies; ++a) + mBodies[a].partitionMask = 0; + } + + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(Ps::Array& numConstraintsPerPartition) + { + for (PxU32 a = 0; a < mNumBodies; ++a) + { + mBodies[a].partitionMask = 0; + + PxU32 requiredSize = PxU32(mBodies[a].maxDynamicPartition + mBodies[a].nbStaticInteractions); + if (requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for (PxU32 b = 0; b < mBodies[a].nbStaticInteractions; ++b) + { + numConstraintsPerPartition[mBodies[a].maxDynamicPartition + b]++; + } + } + } + }; + + class ExtendedRigidBodyClassification + { + + PxTGSSolverBodyVel* PX_RESTRICT mBodies; + PxU32 mNumBodies; + uintptr_t* PX_RESTRICT mArticulations; + PxU32 mNumArticulations; + + + public: + + ExtendedRigidBodyClassification(PxTGSSolverBodyVel* PX_RESTRICT bodies, PxU32 numBodies, uintptr_t* PX_RESTRICT articulations, + PxU32 numArticulations): mBodies(bodies), mNumBodies(numBodies), + mArticulations(articulations), mNumArticulations(numArticulations) + { + } + + //Returns true if it is a dynamic-dynamic constriant; false if it is a dynamic-static or dynamic-kinematic constraint + PX_FORCE_INLINE bool classifyConstraint(const PxTGSSolverConstraintDesc& desc, uintptr_t& indexA, uintptr_t& indexB, + bool& activeA, bool& activeB, PxU32& bodyAProgress, PxU32& bodyBProgress) const + { + bool staticA = false, staticB = false; + if (PxTGSSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + indexA = uintptr_t(desc.bodyA - mBodies); + activeA = indexA < mNumBodies; + staticA = desc.bodyAIdx == 0; + bodyAProgress = activeA ? desc.bodyA->partitionMask : 0; + } + else + { + ArticulationV* articulationA = desc.articulationA; + indexA = mNumBodies + getArticulationIndex(uintptr_t(articulationA), mArticulations, mNumArticulations); + activeA = true; + bodyAProgress = articulationA->solverProgress; + } + + if (PxTGSSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + indexB = uintptr_t(desc.bodyB - mBodies); + activeB = indexB < mNumBodies; + staticB = desc.bodyBIdx == 0; + bodyBProgress = activeB ? desc.bodyB->partitionMask : 0; + } + else + { + ArticulationV* articulationB = desc.articulationB; + indexB = mNumBodies + getArticulationIndex(uintptr_t(articulationB), mArticulations, mNumArticulations); + activeB = true; + bodyBProgress = articulationB->solverProgress; + } + return !(staticA || staticB); + } + + PX_FORCE_INLINE void getProgress(const PxTGSSolverConstraintDesc& desc, + PxU32& bodyAProgress, PxU32& bodyBProgress) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + bodyAProgress = desc.bodyA->partitionMask; + } + else + { + ArticulationV* articulationA = desc.articulationA; + bodyAProgress = articulationA->solverProgress; + } + + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + bodyBProgress = desc.bodyB->partitionMask; + } + else + { + ArticulationV* articulationB = desc.articulationB; + bodyBProgress = articulationB->solverProgress; + } + } + + //called by classifyConstraintDesc + PX_FORCE_INLINE void storeProgress(const PxTGSSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress, + const PxU16 availablePartition) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + desc.bodyA->partitionMask = bodyAProgress; + desc.bodyA->maxDynamicPartition = PxMax(desc.bodyA->maxDynamicPartition, availablePartition); + } + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->solverProgress = bodyAProgress; + articulationA->maxSolverFrictionProgress = PxMax(articulationA->maxSolverFrictionProgress, availablePartition); + } + + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + desc.bodyB->partitionMask = bodyBProgress; + desc.bodyB->maxDynamicPartition = PxMax(desc.bodyB->maxDynamicPartition, availablePartition); + } + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->solverProgress = bodyBProgress; + articulationB->maxSolverFrictionProgress = PxMax(articulationB->maxSolverFrictionProgress, availablePartition); + } + } + + //called by writeConstraintDesc + PX_FORCE_INLINE void storeProgress(const PxTGSSolverConstraintDesc& desc, + const bool activeA, const bool activeB, const PxU32 bodyAProgress, const PxU32 bodyBProgress) + { + if (activeA) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + desc.bodyA->partitionMask = bodyAProgress; + } + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->solverProgress = bodyAProgress; + articulationA->numTotalConstraints++; + } + } + + if (activeB) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + desc.bodyB->partitionMask = bodyBProgress; + } + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->solverProgress = bodyBProgress; + articulationB->numTotalConstraints++; + } + } + } + + //called by classifyConstraintDesc + PX_FORCE_INLINE void recordStaticConstraint(const PxTGSSolverConstraintDesc& desc, bool& activeA, bool& activeB) + { + if (activeA) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + desc.bodyA->nbStaticInteractions++; + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->maxSolverNormalProgress++; + } + } + + if (activeB) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + desc.bodyB->nbStaticInteractions++; + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->maxSolverNormalProgress++; + } + } + } + + //called by writeConstraintDesc + PX_FORCE_INLINE PxU32 getStaticContactWriteIndex(const PxTGSSolverConstraintDesc& desc, + bool activeA, bool activeB) + { + if (activeA) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + return PxU32(desc.bodyA->maxDynamicPartition + desc.bodyA->nbStaticInteractions++); + } + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->numTotalConstraints++; + return PxU32(articulationA->maxSolverFrictionProgress + articulationA->maxSolverNormalProgress++); + } + + } + else if (activeB) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + return PxU32(desc.bodyB->maxDynamicPartition + desc.bodyB->nbStaticInteractions++); + } + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->numTotalConstraints++; + return PxU32(articulationB->maxSolverFrictionProgress + articulationB->maxSolverNormalProgress++); + } + } + + return 0xffffffff; + } + + PX_FORCE_INLINE void clearState() + { + for (PxU32 a = 0; a < mNumBodies; ++a) + mBodies[a].partitionMask = 0; + + for (PxU32 a = 0; a < mNumArticulations; ++a) + (reinterpret_cast(mArticulations[a]))->solverProgress = 0; + } + + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(Ps::Array& numConstraintsPerPartition) + { + for (PxU32 a = 0; a < mNumBodies; ++a) + { + mBodies[a].partitionMask = 0; + + PxU32 requiredSize = PxU32(mBodies[a].maxDynamicPartition + mBodies[a].nbStaticInteractions); + if (requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for (PxU32 b = 0; b < mBodies[a].nbStaticInteractions; ++b) + { + numConstraintsPerPartition[mBodies[a].maxDynamicPartition + b]++; + } + } + + for (PxU32 a = 0; a < mNumArticulations; ++a) + { + ArticulationV* articulation = reinterpret_cast(mArticulations[a]); + articulation->solverProgress = 0; + + PxU32 requiredSize = PxU32(articulation->maxSolverNormalProgress + articulation->maxSolverFrictionProgress); + if (requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for (PxU32 b = 0; b < articulation->maxSolverNormalProgress; ++b) + { + numConstraintsPerPartition[articulation->maxSolverFrictionProgress + b]++; + } + } + } + + }; + + template + void classifyConstraintDesc(const PxTGSSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + Ps::Array& numConstraintsPerPartition, PxTGSSolverConstraintDesc* PX_RESTRICT eaTempConstraintDescriptors) + { + const PxTGSSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxU32 numUnpartitionedConstraints = 0; + PxU32 nbNormalConstraints = 0; + PxU32 nbStaticConstraints = 0; + + numConstraintsPerPartition.forceSize_Unsafe(32); + + PxMemZero(numConstraintsPerPartition.begin(), sizeof(PxU32) * 32); + + for (PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + if (notContainsStatic) + { + nbNormalConstraints++; + /* PxU32 partitionsA = _desc->bodyA->partitionMask; + PxU32 partitionsB = _desc->bodyB->partitionMask;*/ + + PxU32 availablePartition; + { + const PxU32 combinedMask = ((~partitionsA) & (~partitionsB)); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + eaTempConstraintDescriptors[numUnpartitionedConstraints++] = *_desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + PX_ASSERT((partitionBit & partitionsA) == 0); + PX_ASSERT((partitionBit & partitionsB) == 0); + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + classification.storeProgress(*_desc, partitionsA, partitionsB, PxU16(availablePartition)); + } + else + { + nbStaticConstraints++; + + //Just count the number of static constraints and store in maxSolverFrictionProgress... + PX_ASSERT(activeA || activeB); + classification.recordStaticConstraint(*_desc, activeA, activeB); + } + } + + PxU32 partitionStartIndex = 0; + + while (numUnpartitionedConstraints > 0) + { + classification.clearState(); + + partitionStartIndex += 32; + //Keep partitioning the un-partitioned constraints and blat the whole thing to 0! + numConstraintsPerPartition.resize(32 + numConstraintsPerPartition.size()); + PxMemZero(numConstraintsPerPartition.begin() + partitionStartIndex, sizeof(PxU32) * 32); + + PxU32 newNumUnpartitionedConstraints = 0; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + + for (PxU32 i = 0; i < numUnpartitionedConstraints; ++i) + { + const PxTGSSolverConstraintDesc& desc = eaTempConstraintDescriptors[i]; + + classification.classifyConstraint(desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + /*PxU32 partitionsA = desc.bodyA->partitionMask; + PxU32 partitionsB = desc.bodyB->partitionMask;*/ + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + //Need to shuffle around unpartitioned constraints... + eaTempConstraintDescriptors[newNumUnpartitionedConstraints++] = desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + availablePartition += partitionStartIndex; + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + classification.storeProgress(desc, partitionsA, partitionsB, PxU16(availablePartition)); + + } + + numUnpartitionedConstraints = newNumUnpartitionedConstraints; + } + + classification.reserveSpaceForStaticConstraints(numConstraintsPerPartition); + + } + + template + void writeConstraintDesc(const PxTGSSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + Ps::Array& accumulatedConstraintsPerPartition, PxTGSSolverConstraintDesc* eaTempConstraintDescriptors, + PxTGSSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDesc) + { + PX_UNUSED(eaTempConstraintDescriptors); + const PxTGSSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxU32 numUnpartitionedConstraints = 0; + + for (PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + if (notContainsStatic) + { + /*PxU32 partitionsA = _desc->bodyA->partitionMask; + PxU32 partitionsB = _desc->bodyB->partitionMask;*/ + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + eaTempConstraintDescriptors[numUnpartitionedConstraints++] = *_desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + /*_desc->bodyA->partitionMask = partitionsA; + _desc->bodyB->partitionMask = partitionsB; +*/ + classification.storeProgress(*_desc, activeA, activeB, partitionsA, partitionsB); + + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + } + else + { + //Just count the number of static constraints and store in maxSolverFrictionProgress... + /*PxU32 index = 0; + if (activeA) + index = PxU32(_desc->bodyA->maxDynamicPartition + _desc->bodyA->nbStaticInteractions++); + else if (activeB) + index = PxU32(_desc->bodyB->maxDynamicPartition + _desc->bodyB->nbStaticInteractions++);*/ + + PxU32 index = classification.getStaticContactWriteIndex(*_desc, activeA, activeB); + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[index]++] = *_desc; + } + } + + PxU32 partitionStartIndex = 0; + + while (numUnpartitionedConstraints > 0) + { + classification.clearState(); + + partitionStartIndex += 32; + PxU32 newNumUnpartitionedConstraints = 0; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + + for (PxU32 i = 0; i < numUnpartitionedConstraints; ++i) + { + const PxTGSSolverConstraintDesc& desc = eaTempConstraintDescriptors[i]; + + classification.classifyConstraint(desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + /* PxU32 partitionsA = desc.bodyA->partitionMask; + PxU32 partitionsB = desc.bodyB->partitionMask;*/ + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + //Need to shuffle around unpartitioned constraints... + eaTempConstraintDescriptors[newNumUnpartitionedConstraints++] = desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + + partitionsA |= partitionBit; + partitionsB |= partitionBit; + } + + /*if(activeA) + desc.bodyA->partitionMask = partitionsA; + if(activeB) + desc.bodyB->partitionMask = partitionsB;*/ + classification.storeProgress(desc, activeA, activeB, partitionsA, partitionsB); + availablePartition += partitionStartIndex; + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = desc; + } + + numUnpartitionedConstraints = newNumUnpartitionedConstraints; + } + } + + } + +#define PX_NORMALIZE_PARTITIONS 1 + +#if PX_NORMALIZE_PARTITIONS + + template + PxU32 normalizePartitions(Ps::Array& accumulatedConstraintsPerPartition, PxTGSSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDescriptors, + const PxU32 numConstraintDescriptors, Ps::Array& bitField, const Classification& classification, const PxU32 numBodies, const PxU32 numArticulations) + { + PxU32 numPartitions = 0; + + PxU32 prevAccumulation = 0; + for (; numPartitions < accumulatedConstraintsPerPartition.size() && accumulatedConstraintsPerPartition[numPartitions] > prevAccumulation; + prevAccumulation = accumulatedConstraintsPerPartition[numPartitions++]); + + PxU32 targetSize = (numPartitions == 0 ? 0 : (numConstraintDescriptors) / numPartitions); + + bitField.reserve((numBodies + numArticulations + 31) / 32); + bitField.forceSize_Unsafe((numBodies + numArticulations + 31) / 32); + + for (PxU32 i = numPartitions; i > 0; i--) + { + PxU32 partitionIndex = i - 1; + + //Build the partition mask... + + PxU32 startIndex = partitionIndex == 0 ? 0 : accumulatedConstraintsPerPartition[partitionIndex - 1]; + PxU32 endIndex = accumulatedConstraintsPerPartition[partitionIndex]; + + //If its greater than target size, there's nothing that will be pulled into it from earlier partitions + if ((endIndex - startIndex) >= targetSize) + continue; + + + PxMemZero(bitField.begin(), sizeof(PxU32)*bitField.size()); + + for (PxU32 a = startIndex; a < endIndex; ++a) + { + PxTGSSolverConstraintDesc& desc = eaOrderedConstraintDescriptors[a]; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + classification.classifyConstraint(desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + if (activeA) + bitField[PxU32(indexA) / 32] |= getBit(indexA & 31); + if (activeB) + bitField[PxU32(indexB) / 32] |= getBit(indexB & 31); + } + + bool bTerm = false; + for (PxU32 a = partitionIndex; a > 0 && !bTerm; --a) + { + PxU32 pInd = a - 1; + + PxU32 si = pInd == 0 ? 0 : accumulatedConstraintsPerPartition[pInd - 1]; + PxU32 ei = accumulatedConstraintsPerPartition[pInd]; + + for (PxU32 b = ei; b > si && !bTerm; --b) + { + PxU32 ind = b - 1; + PxTGSSolverConstraintDesc& desc = eaOrderedConstraintDescriptors[ind]; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + classification.classifyConstraint(desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + bool canAdd = true; + + if (activeA && (bitField[PxU32(indexA) / 32] & (getBit(indexA & 31)))) + canAdd = false; + if (activeB && (bitField[PxU32(indexB) / 32] & (getBit(indexB & 31)))) + canAdd = false; + + if (canAdd) + { + PxTGSSolverConstraintDesc tmp = eaOrderedConstraintDescriptors[ind]; + + if (activeA) + bitField[PxU32(indexA) / 32] |= (getBit(indexA & 31)); + if (activeB) + bitField[PxU32(indexB) / 32] |= (getBit(indexB & 31)); + + PxU32 index = ind; + for (PxU32 c = pInd; c < partitionIndex; ++c) + { + PxU32 newIndex = --accumulatedConstraintsPerPartition[c]; + if (index != newIndex) + eaOrderedConstraintDescriptors[index] = eaOrderedConstraintDescriptors[newIndex]; + index = newIndex; + } + + if (index != ind) + eaOrderedConstraintDescriptors[index] = tmp; + + if ((accumulatedConstraintsPerPartition[partitionIndex] - accumulatedConstraintsPerPartition[partitionIndex - 1]) >= targetSize) + { + bTerm = true; + break; + } + } + } + } + } + + PxU32 partitionCount = 0; + PxU32 lastPartitionCount = 0; + for (PxU32 a = 0; a < numPartitions; ++a) + { + const PxU32 constraintCount = accumulatedConstraintsPerPartition[a]; + accumulatedConstraintsPerPartition[partitionCount] = constraintCount; + if (constraintCount != lastPartitionCount) + { + lastPartitionCount = constraintCount; + partitionCount++; + } + } + + accumulatedConstraintsPerPartition.forceSize_Unsafe(partitionCount); + + return partitionCount; + } + +#endif + + PxU32 partitionContactConstraintsStep(TGSConstraintPartitionArgs& args) + { + PxU32 maxPartition = 0; + //Unpack the input data. + const PxU32 numBodies = args.mNumBodies; + PxTGSSolverBodyVel* PX_RESTRICT eaAtoms = args.mBodies; + const PxU32 numArticulations = args.mNumArticulationPtrs; + + const PxU32 numConstraintDescriptors = args.mNumContactConstraintDescriptors; + + PxTGSSolverConstraintDesc* PX_RESTRICT eaConstraintDescriptors = args.mContactConstraintDescriptors; + PxTGSSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDescriptors = args.mOrderedContactConstraintDescriptors; + PxTGSSolverConstraintDesc* PX_RESTRICT eaTempConstraintDescriptors = args.mTempContactConstraintDescriptors; + + Ps::Array& constraintsPerPartition = *args.mConstraintsPerPartition; + constraintsPerPartition.forceSize_Unsafe(0); + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxTGSSolverBodyVel& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.partitionMask = 0; + //We re-use maxSolverFrictionProgress and maxSolverNormalProgress to record the + //maximum partition used by dynamic constraints and the number of static constraints affecting + //a body. We use this to make partitioning much cheaper and be able to support + body.maxDynamicPartition = 0; + body.nbStaticInteractions = 0; + } + + PxU32 numOrderedConstraints = 0; + + PxU32 numSelfConstraintBlocks = 0; + + if (numArticulations == 0) + { + RigidBodyClassification classification(eaAtoms, numBodies); + classifyConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors); + + PxU32 accumulation = 0; + for (PxU32 a = 0; a < constraintsPerPartition.size(); ++a) + { + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; + } + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxTGSSolverBodyVel& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.partitionMask = 0; + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... + body.nbStaticInteractions = 0; + } + + writeConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors, eaOrderedConstraintDescriptors); + + numOrderedConstraints = numConstraintDescriptors; + + if (!args.enhancedDeterminism) + maxPartition = normalizePartitions(constraintsPerPartition, eaOrderedConstraintDescriptors, numConstraintDescriptors, *args.mBitField, + classification, numBodies, 0); + + } + else + { + + ArticulationSolverDesc* articulationDescs = args.mArticulationPtrs; + PX_ALLOCA(_eaArticulations, uintptr_t, numArticulations); + uintptr_t* eaArticulations = _eaArticulations; + for (PxU32 i = 0; isolverProgress = 0; + articulation->maxSolverFrictionProgress = 0; + articulation->maxSolverNormalProgress = 0; + articulation->numTotalConstraints = 0; + } + ExtendedRigidBodyClassification classification(eaAtoms, numBodies, eaArticulations, numArticulations); + + classifyConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, + constraintsPerPartition, eaTempConstraintDescriptors); + + PxU32 accumulation = 0; + for (PxU32 a = 0; a < constraintsPerPartition.size(); ++a) + { + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; + } + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxTGSSolverBodyVel& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.partitionMask = 0; + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... + body.nbStaticInteractions = 0; + } + + for (PxU32 a = 0; a < numArticulations; ++a) + { + ArticulationV* articulation = reinterpret_cast(eaArticulations[a]); + articulation->solverProgress = 0; + articulation->maxSolverNormalProgress = 0; + articulation->numTotalConstraints = 0; + } + + writeConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors, eaOrderedConstraintDescriptors); + + numOrderedConstraints = numConstraintDescriptors; + + if (!args.enhancedDeterminism) + maxPartition = normalizePartitions(constraintsPerPartition, eaOrderedConstraintDescriptors, + numConstraintDescriptors, *args.mBitField, classification, numBodies, numArticulations); + + } + + + + const PxU32 numConstraintsDifferentBodies = numOrderedConstraints; + + PX_ASSERT(numConstraintsDifferentBodies == numConstraintDescriptors); + + //Now handle the articulated self-constraints. + PxU32 totalConstraintCount = numConstraintsDifferentBodies; + + args.mNumSelfConstraintBlocks = numSelfConstraintBlocks; + + args.mNumDifferentBodyConstraints = numConstraintsDifferentBodies; + args.mNumSelfConstraints = totalConstraintCount - numConstraintsDifferentBodies; + + if (args.enhancedDeterminism) + { + PxU32 prevPartitionSize = 0; + maxPartition = 0; + for (PxU32 a = 0; a < constraintsPerPartition.size(); ++a, maxPartition++) + { + if (constraintsPerPartition[a] == prevPartitionSize) + break; + prevPartitionSize = constraintsPerPartition[a]; + } + } + + return maxPartition; + } + + } + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h new file mode 100644 index 000000000..538309d2a --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_TGS_CONSTRAINTPARTITION_H +#define DY_TGS_CONSTRAINTPARTITION_H + +#include "foundation/PxSimpleTypes.h" +#include "DyTGSDynamics.h" +#include "DyArticulation.h" +#include "PsArray.h" + +namespace physx +{ + + namespace Dy + { + struct TGSConstraintPartitionArgs + { + enum + { + eMAX_NUM_BODIES = 8192 + }; + + //Input + PxTGSSolverBodyVel* mBodies; + PxU32 mNumBodies; + ArticulationSolverDesc* mArticulationPtrs; + PxU32 mNumArticulationPtrs; + PxTGSSolverConstraintDesc* mContactConstraintDescriptors; + PxU32 mNumContactConstraintDescriptors; + //output + PxTGSSolverConstraintDesc* mOrderedContactConstraintDescriptors; + PxTGSSolverConstraintDesc* mTempContactConstraintDescriptors; + PxU32 mNumSelfConstraintBlocks; + PxU32 mNumDifferentBodyConstraints; + PxU32 mNumSelfConstraints; + Ps::Array* mConstraintsPerPartition; + Ps::Array* mBitField; + + bool enhancedDeterminism; + }; + + PxU32 partitionContactConstraintsStep(TGSConstraintPartitionArgs& args); + + } // namespace physx + +} + + + +#endif // DY_CONSTRAINTPARTITION_H + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp new file mode 100644 index 000000000..8c9e172c3 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "DyThreadContext.h" +#include "PsBitUtils.h" + +namespace physx +{ +namespace Dy +{ + +ThreadContext::ThreadContext(PxcNpMemBlockPool* memBlockPool): + mFrictionPatchStreamPair(*memBlockPool), + mConstraintBlockManager (*memBlockPool), + mConstraintBlockStream (*memBlockPool), + mNumDifferentBodyConstraints(0), + mNumSelfConstraints(0), + mNumSelfConstraintBlocks(0), + mConstraintsPerPartition(PX_DEBUG_EXP("ThreadContext::mConstraintsPerPartition")), + mFrictionConstraintsPerPartition(PX_DEBUG_EXP("ThreadContext::frictionsConstraintsPerPartition")), + mPartitionNormalizationBitmap(PX_DEBUG_EXP("ThreadContext::mPartitionNormalizationBitmap")), + frictionConstraintDescArray(PX_DEBUG_EXP("ThreadContext::solverFrictionConstraintArray")), + frictionConstraintBatchHeaders(PX_DEBUG_EXP("ThreadContext::frictionConstraintBatchHeaders")), + compoundConstraints(PX_DEBUG_EXP("ThreadContext::compoundConstraints")), + orderedContactList(PX_DEBUG_EXP("ThreadContext::orderedContactList")), + tempContactList(PX_DEBUG_EXP("ThreadContext::tempContactList")), + sortIndexArray(PX_DEBUG_EXP("ThreadContext::sortIndexArray")), + mConstraintSize (0), + mAxisConstraintCount(0), + mSelfConstraintBlocks(NULL), + mMaxPartitions(0), + mMaxSolverPositionIterations(0), + mMaxSolverVelocityIterations(0), + mMaxArticulationLength(0), + mContactDescPtr(NULL), + mFrictionDescPtr(NULL), + mArticulations(PX_DEBUG_EXP("ThreadContext::articulations")) + +{ +#if PX_ENABLE_SIM_STATS + mThreadSimStats.clear(); +#endif + //Defaulted to have space for 16384 bodies + mPartitionNormalizationBitmap.reserve(512); + //Defaulted to have space for 128 partitions (should be more-than-enough) + mConstraintsPerPartition.reserve(128); +} + +void ThreadContext::resizeArrays(PxU32 frictionConstraintDescCount, PxU32 articulationCount) +{ + // resize resizes smaller arrays to the exact target size, which can generate a lot of churn + frictionConstraintDescArray.forceSize_Unsafe(0); + frictionConstraintDescArray.reserve((frictionConstraintDescCount+63)&~63); + + mArticulations.forceSize_Unsafe(0); + mArticulations.reserve(PxMax(Ps::nextPowerOfTwo(articulationCount), 16)); + mArticulations.forceSize_Unsafe(articulationCount); + + mContactDescPtr = contactConstraintDescArray; + mFrictionDescPtr = frictionConstraintDescArray.begin(); +} + +void ThreadContext::reset() +{ + // TODO: move these to the PxcNpThreadContext + mFrictionPatchStreamPair.reset(); + mConstraintBlockStream.reset(); + + mContactDescPtr = contactConstraintDescArray; + mFrictionDescPtr = frictionConstraintDescArray.begin(); + + mAxisConstraintCount = 0; + mMaxSolverPositionIterations = 0; + mMaxSolverVelocityIterations = 0; + mNumDifferentBodyConstraints = 0; + mNumSelfConstraints = 0; + mSelfConstraintBlocks = NULL; + mNumSelfConstraintBlocks = 0; + mConstraintSize = 0; +} + +} +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h new file mode 100644 index 000000000..7a77339fe --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h @@ -0,0 +1,255 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_THREADCONTEXT_H +#define DY_THREADCONTEXT_H + +#include "foundation/PxTransform.h" +#include "PxvConfig.h" +#include "CmBitMap.h" +#include "CmMatrix34.h" +#include "PxcThreadCoherentCache.h" +#include "DyThresholdTable.h" +#include "PsAllocator.h" +#include "PsAllocator.h" +#include "GuContactBuffer.h" +#include "DySolverConstraintDesc.h" +#include "PxvDynamics.h" +#include "DyArticulation.h" +#include "DyFrictionPatchStreamPair.h" +#include "PxcConstraintBlockStream.h" +#include "DyCorrelationBuffer.h" + +namespace physx +{ +struct PxsIndexedContactManager; + +namespace Dy +{ + + //Needed by all constraints + struct PxTGSSolverBodyVel + { + PX_ALIGN(16, PxVec3) linearVelocity; //12 + PxU16 nbStaticInteractions; //14 Used to accumulate the number of static interactions + PxU16 maxDynamicPartition; //16 Used to accumualte the max partition of dynamic interactions + PxVec3 angularVelocity; //28 + PxU32 partitionMask; //32 Used in partitioning as a bit-field + PxVec3 deltaAngDt; //44 + PxReal maxAngVel; //48 + PxVec3 deltaLinDt; //60 + PxU16 lockFlags; //62 + bool isKinematic; //63 + PxU8 pad; //64 + + PX_FORCE_INLINE PxReal projectVelocity(const PxVec3& lin, const PxVec3& ang) const + { + return linearVelocity.dot(lin) + angularVelocity.dot(ang); + } + + }; + + //Needed only by prep, integration and 1D constraints + struct PxTGSSolverBodyTxInertia + { + PxTransform deltaBody2World; + PxMat33 sqrtInvInertia; + }; + + struct PxStepSolverBodyData + { + PX_ALIGN(16, PxVec3) originalLinearVelocity; + PxReal maxContactImpulse; + PxVec3 originalAngularVelocity; + PxReal penBiasClamp; + + PxReal invMass; + PxU32 nodeIndex; + PxReal reportThreshold; + PxU32 pad; + + PxReal projectVelocity(const PxVec3& linear, const PxVec3& angular) const + { + return originalLinearVelocity.dot(linear) + originalAngularVelocity.dot(angular); + } + }; + +/*! +Cache information specific to the software implementation(non common). + +See PxcgetThreadContext. + +Not thread-safe, so remember to have one object per thread! + +TODO! refactor this and rename(it is a general per thread cache). Move transform cache into its own class. +*/ +class ThreadContext : + public PxcThreadCoherentCache::EntryBase +{ + PX_NOCOPY(ThreadContext) +public: + +#if PX_ENABLE_SIM_STATS + struct ThreadSimStats + { + void clear() + { + + numActiveConstraints = 0; + numActiveDynamicBodies = 0; + numActiveKinematicBodies = 0; + numAxisSolverConstraints = 0; + + } + + PxU32 numActiveConstraints; + PxU32 numActiveDynamicBodies; + PxU32 numActiveKinematicBodies; + PxU32 numAxisSolverConstraints; + + }; +#endif + + //TODO: tune cache size based on number of active objects. + ThreadContext(PxcNpMemBlockPool* memBlockPool); + void reset(); + void resizeArrays(PxU32 frictionConstraintDescCount, PxU32 articulationCount); + + PX_FORCE_INLINE Ps::Array& getArticulations() { return mArticulations; } + + +#if PX_ENABLE_SIM_STATS + PX_FORCE_INLINE ThreadSimStats& getSimStats() + { + return mThreadSimStats; + } +#endif + + Gu::ContactBuffer mContactBuffer; + + // temporary buffer for correlation + PX_ALIGN(16, CorrelationBuffer mCorrelationBuffer); + + FrictionPatchStreamPair mFrictionPatchStreamPair; // patch streams + + PxsConstraintBlockManager mConstraintBlockManager; // for when this thread context is "lead" on an island + PxcConstraintBlockStream mConstraintBlockStream; // constraint block pool + + + // this stuff is just used for reformatting the solver data. Hopefully we should have a more + // sane format for this when the dust settles - so it's just temporary. If we keep this around + // here we should move these from public to private + + PxU32 mNumDifferentBodyConstraints; + PxU32 mNumDifferentBodyFrictionConstraints; + PxU32 mNumSelfConstraints; + PxU32 mNumSelfFrictionConstraints; + PxU32 mNumSelfConstraintBlocks; + PxU32 mNumSelfConstraintFrictionBlocks; + + Ps::Array mConstraintsPerPartition; + Ps::Array mFrictionConstraintsPerPartition; + Ps::Array mPartitionNormalizationBitmap; + PxsBodyCore** mBodyCoreArray; + PxsRigidBody** mRigidBodyArray; + ArticulationV** mArticulationArray; + Cm::SpatialVector* motionVelocityArray; + PxU32* bodyRemapTable; + PxU32* mNodeIndexArray; + + //Constraint info for normal constraint sovler + PxSolverConstraintDesc* contactConstraintDescArray; + PxU32 contactDescArraySize; + PxSolverConstraintDesc* orderedContactConstraints; + PxConstraintBatchHeader* contactConstraintBatchHeaders; + PxU32 numContactConstraintBatches; + + //Constraint info for partitioning + PxSolverConstraintDesc* tempConstraintDescArray; + + //Additional constraint info for 1d/2d friction model + Ps::Array frictionConstraintDescArray; + Ps::Array frictionConstraintBatchHeaders; + + //Info for tracking compound contact managers (temporary data - could use scratch memory!) + Ps::Array compoundConstraints; + + //Used for sorting constraints. Temporary, could use scratch memory + Ps::Array orderedContactList; + Ps::Array tempContactList; + Ps::Array sortIndexArray; + + Ps::Array mZVector; + Ps::Array mDeltaV; + + + PxU32 numDifferentBodyBatchHeaders; + PxU32 numSelfConstraintBatchHeaders; + + + PxU32 mOrderedContactDescCount; + PxU32 mOrderedFrictionDescCount; + + PxU32 mConstraintSize; + + PxU32 mAxisConstraintCount; + SelfConstraintBlock* mSelfConstraintBlocks; + + SelfConstraintBlock* mSelfConstraintFrictionBlocks; + + PxU32 mMaxPartitions; + PxU32 mMaxFrictionPartitions; + PxU32 mMaxSolverPositionIterations; + PxU32 mMaxSolverVelocityIterations; + PxU32 mMaxArticulationLength; + PxU32 mMaxArticulationSolverLength; + PxU32 mMaxArticulationLinks; + + PxSolverConstraintDesc* mContactDescPtr; + PxSolverConstraintDesc* mStartContactDescPtr; + PxSolverConstraintDesc* mFrictionDescPtr; + +private: + + Ps::Array mArticulations; + +#if PX_ENABLE_SIM_STATS + ThreadSimStats mThreadSimStats; +#endif + + public: + +}; + +} + +} + +#endif //DY_THREADCONTEXT_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp new file mode 100644 index 000000000..bb4cd3018 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "DyThresholdTable.h" +#include "PsHash.h" +#include "PsUtilities.h" +#include "PsAllocator.h" + +namespace physx +{ + namespace Dy + { + bool ThresholdTable::check(const ThresholdStream& stream, const PxU32 nodeIndexA, const PxU32 nodeIndexB, PxReal dt) + { + PxU32* PX_RESTRICT hashes = mHash; + PxU32* PX_RESTRICT nextIndices = mNexts; + Pair* PX_RESTRICT pairs = mPairs; + + /*const PxsRigidBody* b0 = PxMin(body0, body1); + const PxsRigidBody* b1 = PxMax(body0, body1);*/ + + const PxU32 nA = PxMin(nodeIndexA, nodeIndexB); + const PxU32 nB = PxMax(nodeIndexA, nodeIndexB); + + PxU32 hashKey = computeHashKey(nodeIndexA, nodeIndexB, mHashSize); + + PxU32 pairIndex = hashes[hashKey]; + while(NO_INDEX != pairIndex) + { + Pair& pair = pairs[pairIndex]; + const PxU32 thresholdStreamIndex = pair.thresholdStreamIndex; + PX_ASSERT(thresholdStreamIndex < stream.size()); + const ThresholdStreamElement& otherElement = stream[thresholdStreamIndex]; + if(otherElement.nodeIndexA.index()==nA && otherElement.nodeIndexB.index()==nB) + return (pair.accumulatedForce > (otherElement.threshold * dt)); + pairIndex = nextIndices[pairIndex]; + } + return false; + } + } +} diff --git a/src/PhysX/physx/source/physx/src/NpActor.cpp b/src/PhysX/physx/source/physx/src/NpActor.cpp new file mode 100644 index 000000000..910b7958d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpActor.cpp @@ -0,0 +1,486 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpActor.h" +#include "PxRigidActor.h" +#include "NpConstraint.h" +#include "NpFactory.h" +#include "NpShape.h" +#include "NpPhysics.h" +#include "NpAggregate.h" +#include "NpScene.h" +#include "NpRigidStatic.h" +#include "NpRigidDynamic.h" +#include "NpArticulationLink.h" +#include "CmTransformUtils.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////// + +NpActor::NpActor(const char* name) : + mName(name), + mConnectorArray(NULL) +{ +} + +NpActor::~NpActor() +{ +} + + +typedef Ps::HashMap ConnectorMap; +struct NpActorUserData +{ + PxU32 referenceCount; + ConnectorMap* tmpOriginalConnectors; +}; + +void NpActor::exportExtraData(PxSerializationContext& stream) +{ + const PxCollection& collection = stream.getCollection(); + if(mConnectorArray) + { + PxU32 connectorSize = mConnectorArray->size(); + PxU32 missedCount = 0; + for(PxU32 i = 0; i < connectorSize; ++i) + { + NpConnector& c = (*mConnectorArray)[i]; + PxBase* object = c.mObject; + if(!collection.contains(*object)) + { + ++missedCount; + } + } + + NpConnectorArray* exportConnectorArray = mConnectorArray; + + if(missedCount > 0) + { + exportConnectorArray = NpFactory::getInstance().acquireConnectorArray(); + if(missedCount < connectorSize) + { + exportConnectorArray->reserve(connectorSize - missedCount); + for(PxU32 i = 0; i < connectorSize; ++i) + { + NpConnector& c = (*mConnectorArray)[i]; + PxBase* object = c.mObject; + if(collection.contains(*object)) + { + exportConnectorArray->pushBack(c); + } + } + } + } + + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(exportConnectorArray, sizeof(NpConnectorArray)); + Cm::exportInlineArray(*exportConnectorArray, stream); + + if(missedCount > 0) + NpFactory::getInstance().releaseConnectorArray(exportConnectorArray); + } + stream.writeName(mName); +} + +void NpActor::importExtraData(PxDeserializationContext& context) +{ + if(mConnectorArray) + { + mConnectorArray = context.readExtraData(); + new (mConnectorArray) NpConnectorArray(PxEmpty); + + if(mConnectorArray->size() == 0) + mConnectorArray = NULL; + else + Cm::importInlineArray(*mConnectorArray, context); + } + context.readName(mName); +} + +void NpActor::resolveReferences(PxDeserializationContext& context) +{ + // Resolve connector pointers if needed + if(mConnectorArray) + { + const PxU32 nbConnectors = mConnectorArray->size(); + for(PxU32 i=0; isize(); + PxU32 currentIndex = 0; + while(nbConnectors--) + { + NpConnector& connector = (*mConnectorArray)[currentIndex]; + if (connector.mType == NpConnectorType::eConstraint) + { + NpConstraint* c = static_cast(connector.mObject); + c->actorDeleted(&owner); + + NpScene* s = c->getNpScene(); + if (s) + { + s->getScene().removeConstraint(c->getScbConstraint()); + s->removeFromConstraintList(*c); + } + + removeConnector(owner, currentIndex); + } + else + currentIndex++; + } + } +} + +void NpActor::release(PxActor& owner) +{ + if(mConnectorArray) // Need to test again because the code above might purge the connector array if no element remains + { + PX_ASSERT(mConnectorArray->size() == 1); // At this point only the aggregate should remain + PX_ASSERT((*mConnectorArray)[0].mType == NpConnectorType::eAggregate); + + NpAggregate* a = static_cast((*mConnectorArray)[0].mObject); + bool status = a->removeActorAndReinsert(owner, false); + PX_ASSERT(status); + PX_UNUSED(status); + PX_ASSERT(!mConnectorArray); // Remove should happen in aggregate code + } + + PX_ASSERT(!mConnectorArray); // All the connector objects should have been removed at this point +} + +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 NpActor::findConnector(NpConnectorType::Enum type, PxBase* object) const +{ + if(!mConnectorArray) + return 0xffffffff; + + for(PxU32 i=0; i < mConnectorArray->size(); i++) + { + NpConnector& c = (*mConnectorArray)[i]; + if (c.mType == type && c.mObject == object) + return i; + } + + return 0xffffffff; +} + +void NpActor::addConnector(NpConnectorType::Enum type, PxBase* object, const char* errMsg) +{ + if(!mConnectorArray) + mConnectorArray = NpFactory::getInstance().acquireConnectorArray(); + + PX_CHECK_MSG(findConnector(type, object) == 0xffffffff, errMsg); + PX_UNUSED(errMsg); + + if(mConnectorArray->isInUserMemory() && mConnectorArray->size() == mConnectorArray->capacity()) + { + NpConnectorArray* newConnectorArray = NpFactory::getInstance().acquireConnectorArray(); + newConnectorArray->assign(mConnectorArray->begin(), mConnectorArray->end()); + mConnectorArray->~NpConnectorArray(); + mConnectorArray = newConnectorArray; + } + + NpConnector c(type, object); + mConnectorArray->pushBack(c); +} + +void NpActor::removeConnector(PxActor& /*owner*/, PxU32 index) +{ + PX_ASSERT(mConnectorArray); + PX_ASSERT(index < mConnectorArray->size()); + + mConnectorArray->replaceWithLast(index); + + if(mConnectorArray->size() == 0) + { + if(!mConnectorArray->isInUserMemory()) + NpFactory::getInstance().releaseConnectorArray(mConnectorArray); + else + mConnectorArray->~NpConnectorArray(); + mConnectorArray = NULL; + } +} + +void NpActor::removeConnector(PxActor& owner, NpConnectorType::Enum type, PxBase* object, const char* errorMsg) +{ + PX_CHECK_MSG(mConnectorArray, errorMsg); + PX_UNUSED(errorMsg); + + if(mConnectorArray) + { + PxU32 index = findConnector(type, object); + + PX_CHECK_MSG(index != 0xffffffff, errorMsg); + + removeConnector(owner, index); + } +} + +PxU32 NpActor::getNbConnectors(NpConnectorType::Enum type) const +{ + PxU32 nbConnectors = 0; + + if(mConnectorArray) + { + for(PxU32 i=0; i < mConnectorArray->size(); i++) + { + if ((*mConnectorArray)[i].mType == type) + nbConnectors++; + } + } + + return nbConnectors; +} + +/////////////////////////////////////////////////////////////////////////////// + +NpAggregate* NpActor::getNpAggregate(PxU32& index) const +{ + PX_ASSERT(getNbConnectors(NpConnectorType::eAggregate) <= 1); + + if(mConnectorArray) + { + // PT: TODO: sort by type to optimize this... + for(PxU32 i=0; i < mConnectorArray->size(); i++) + { + NpConnector& c = (*mConnectorArray)[i]; + if (c.mType == NpConnectorType::eAggregate) + { + index = i; + return static_cast(c.mObject); + } + } + } + + return NULL; +} + +void NpActor::setAggregate(NpAggregate* np, PxActor& owner) +{ + PxU32 index = 0xffffffff; + NpAggregate* a = getNpAggregate(index); + + if (!a) + { + PX_ASSERT(np); + addConnector(NpConnectorType::eAggregate, np, "NpActor::setAggregate() failed"); + } + else + { + PX_ASSERT(mConnectorArray); + PX_ASSERT(index != 0xffffffff); + if (!np) + removeConnector(owner, index); + else + (*mConnectorArray)[index].mObject = np; + } +} + +PxAggregate* NpActor::getAggregate() const +{ + PxU32 index = 0xffffffff; + NpAggregate* a = getNpAggregate(index); + return static_cast(a); +} + + +/////////////////////////////////////////////////////////////////////////////// + +void NpActor::removeConstraintsFromScene() +{ + NpConnectorIterator iter = getConnectorIterator(NpConnectorType::eConstraint); + while (PxBase* ser = iter.getNext()) + { + NpConstraint* c = static_cast(ser); + + NpScene* s = c->getNpScene(); + + if (s) + { + s->removeFromConstraintList(*c); + s->getScene().removeConstraint(c->getScbConstraint()); + } + } +} + +void NpActor::addConstraintsToSceneInternal() +{ + if(!mConnectorArray) + return; + + NpConnectorIterator iter = getConnectorIterator(NpConnectorType::eConstraint); + while (PxBase* ser = iter.getNext()) + { + NpConstraint* c = static_cast(ser); + PX_ASSERT(c->getNpScene() == NULL); + + c->markDirty(); // PT: "temp" fix for crash when removing/re-adding jointed actor from/to a scene + + NpScene* s = c->getSceneFromActors(); + if (s) + { + s->addToConstraintList(*c); + s->getScene().addConstraint(c->getScbConstraint()); + } + } +} + + + +/////////////////////////////////////////////////////////////////////////////// + +NpShapeManager* NpActor::getShapeManager(PxRigidActor& actor) +{ + // DS: if the performance here becomes an issue we can use the same kind of offset hack as below + + const PxType actorType = actor.getConcreteType(); + + if (actorType == PxConcreteType::eRIGID_DYNAMIC) + return &static_cast(actor).getShapeManager(); + else if(actorType == PxConcreteType::eRIGID_STATIC) + return &static_cast(actor).getShapeManager(); + else if (actorType == PxConcreteType::eARTICULATION_LINK) + return &static_cast(actor).getShapeManager(); + else + { + PX_ASSERT(0); + return NULL; + } +} + +const NpShapeManager* NpActor::getShapeManager(const PxRigidActor& actor) +{ + // DS: if the performance here becomes an issue we can use the same kind of offset hack as below + + const PxType actorType = actor.getConcreteType(); + + if (actorType == PxConcreteType::eRIGID_DYNAMIC) + return &static_cast(actor).getShapeManager(); + else if(actorType == PxConcreteType::eRIGID_STATIC) + return &static_cast(actor).getShapeManager(); + else if (actorType == PxConcreteType::eARTICULATION_LINK) + return &static_cast(actor).getShapeManager(); + else + { + PX_ASSERT(0); + return NULL; + } +} + +void NpActor::getGlobalPose(PxTransform& globalPose, const NpShape& shape, const PxRigidActor& actor) +{ + const Scb::Actor& scbActor = NpActor::getScbFromPxActor(actor); + const Scb::Shape& scbShape = shape.getScbShape(); + + NpActor::getGlobalPose(globalPose, scbShape, scbActor); +} + +void NpActor::getGlobalPose(PxTransform& globalPose, const Scb::Shape& scbShape, const Scb::Actor& scbActor) +{ + const PxTransform& shape2Actor = scbShape.getShape2Actor(); + + // PT: TODO: duplicated from SqBounds.cpp. Refactor. + const ScbType::Enum actorType = scbActor.getScbType(); + if(actorType==ScbType::eRIGID_STATIC) + { + Cm::getStaticGlobalPoseAligned(static_cast(scbActor).getActor2World(), shape2Actor, globalPose); + } + else + { + PX_ASSERT(actorType==ScbType::eBODY || actorType == ScbType::eBODY_FROM_ARTICULATION_LINK); + + const Scb::Body& body = static_cast(scbActor); + PX_ALIGN(16, PxTransform) kinematicTarget; + const PxU16 sqktFlags = PxRigidBodyFlag::eKINEMATIC | PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES; + const bool useTarget = (PxU16(body.getFlags()) & sqktFlags) == sqktFlags; + const PxTransform& body2World = (useTarget && body.getKinematicTarget(kinematicTarget)) ? kinematicTarget : body.getBody2World(); + Cm::getDynamicGlobalPoseAligned(body2World, shape2Actor, body.getBody2Actor(), globalPose); + } +} + +namespace +{ + template NpActor* pxToNpActor(PxActor *p) + { + return static_cast(static_cast(p)); + } +} + +NpActor::Offsets::Offsets() +{ + for(PxU32 i=0;i(addr); + pxActorToNpActor[PxConcreteType::eRIGID_STATIC] = reinterpret_cast(pxToNpActor(n)) - addr; + pxActorToNpActor[PxConcreteType::eRIGID_DYNAMIC] = reinterpret_cast(pxToNpActor(n)) - addr; + pxActorToNpActor[PxConcreteType::eARTICULATION_LINK] = reinterpret_cast(pxToNpActor(n)) - addr; + pxActorToScbActor[PxConcreteType::eRIGID_STATIC] = PX_OFFSET_OF_RT(NpRigidStatic, getScbActorFast()); + pxActorToScbActor[PxConcreteType::eRIGID_DYNAMIC] = PX_OFFSET_OF_RT(NpRigidDynamic, getScbActorFast()); + pxActorToScbActor[PxConcreteType::eARTICULATION_LINK] = PX_OFFSET_OF_RT(NpArticulationLink, getScbActorFast()); +} + +const NpActor::Offsets NpActor::sOffsets; + + +NpScene* NpActor::getOwnerScene(const PxActor& actor) +{ + const Scb::Actor& scbActor = getScbFromPxActor(actor); + Scb::Scene* scbScene = scbActor.getScbScene(); + return scbScene? static_cast(scbScene->getPxScene()) : NULL; +} + +NpScene* NpActor::getAPIScene(const PxActor& actor) +{ + const Scb::Actor& scbActor = getScbFromPxActor(actor); + Scb::Scene* scbScene = scbActor.getScbSceneForAPI(); + return scbScene? static_cast(scbScene->getPxScene()) : NULL; +} + +void NpActor::onActorRelease(PxActor* actor) +{ + NpFactory::getInstance().onActorRelease(actor); +} diff --git a/src/PhysX/physx/source/physx/src/NpActor.h b/src/PhysX/physx/source/physx/src/NpActor.h new file mode 100644 index 000000000..a4151a4c5 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpActor.h @@ -0,0 +1,163 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ACTOR +#define PX_PHYSICS_NP_ACTOR + +#include "NpConnector.h" +#include "ScbActor.h" // DM: without this inclusion PVD-diabled android build fails, lacking Scb::Actor definition + +namespace physx +{ + class NpShapeManager; + class NpAggregate; + class NpScene; + class NpShape; + +class NpActor +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + // We sometimes pass in the PxActor here even though it's always a base class + // of the objects which inherit from this class too. But passing + // context to functions which need it allows this to be purely a mixin containing shared + // utility code rather than an abstract base class. + +public: +// PX_SERIALIZATION + NpActor(const PxEMPTY) {} + + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + NpActor(const char* name); + + void releaseConstraints(PxRigidActor& owner); + void release(PxActor& owner); + + NpAggregate* getNpAggregate(PxU32& index) const; + void setAggregate(NpAggregate* np, PxActor& owner); + PxAggregate* getAggregate() const; + + void removeConstraintsFromScene(); + PX_FORCE_INLINE void addConstraintsToScene() // inline the fast path for addActors() + { + if(mConnectorArray) + addConstraintsToSceneInternal(); + } + + PX_FORCE_INLINE NpConnectorArray** getConnectorArrayAddress() { return &mConnectorArray;} + PxU32 findConnector(NpConnectorType::Enum type, PxBase* object) const; + void addConnector(NpConnectorType::Enum type, PxBase* object, const char* errMsg); + void removeConnector(PxActor& owner, NpConnectorType::Enum type, PxBase* object, const char* errorMsg); + PxU32 getNbConnectors(NpConnectorType::Enum type) const; + + static NpShapeManager* getShapeManager(PxRigidActor& actor); // bit misplaced here, but we don't want a separate subclass just for this + static const NpShapeManager* getShapeManager(const PxRigidActor& actor); // bit misplaced here, but we don't want a separate subclass just for this + + static void getGlobalPose(PxTransform& globalPose, const NpShape& shape, const PxRigidActor& actor); + static void getGlobalPose(PxTransform& globalPose, const Scb::Shape& scbShape, const Scb::Actor& scbActor); + + static NpActor& getFromPxActor(PxActor& actor) { return *Ps::pointerOffset(&actor, ptrdiff_t(sOffsets.pxActorToNpActor[actor.getConcreteType()])); } + static const NpActor& getFromPxActor(const PxActor& actor) { return *Ps::pointerOffset(&actor, ptrdiff_t(sOffsets.pxActorToNpActor[actor.getConcreteType()])); } + + static Scb::Actor& getScbFromPxActor(PxActor& actor) { return *Ps::pointerOffset(&actor, ptrdiff_t(sOffsets.pxActorToScbActor[actor.getConcreteType()])); } + static const Scb::Actor& getScbFromPxActor(const PxActor& actor) { return *Ps::pointerOffset(&actor, ptrdiff_t(sOffsets.pxActorToScbActor[actor.getConcreteType()])); } + + static NpScene* getAPIScene(const PxActor& actor); // the scene the user thinks the actor is in + static NpScene* getOwnerScene(const PxActor& actor); // the scene the user thinks the actor is in, or from which the actor is pending removal + + PX_FORCE_INLINE NpConnectorIterator getConnectorIterator(NpConnectorType::Enum type) + { + if (mConnectorArray) + return NpConnectorIterator(&mConnectorArray->front(), mConnectorArray->size(), type); + else + return NpConnectorIterator(NULL, 0, type); + } + + // a couple of methods that sever include dependencies in NpActor.h + + static void onActorRelease(PxActor* actor); + + + +template +PxU32 getConnectors(NpConnectorType::Enum type, T** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const +{ + PxU32 nbConnectors = 0; + + if(mConnectorArray) + { + for(PxU32 i=0; i < mConnectorArray->size(); i++) + { + NpConnector& c = (*mConnectorArray)[i]; + if (c.mType == type && nbConnectors < bufferSize && i>=startIndex) + { + userBuffer[nbConnectors] = static_cast(c.mObject); + nbConnectors++; + } + } + } + + return nbConnectors; +} + +protected: + ~NpActor(); + const char* mName; + // Lazy-create array for connector objects like constraints, observers, ... + // Most actors have no such objects, so we bias this class accordingly: + NpConnectorArray* mConnectorArray; + +private: + void addConstraintsToSceneInternal(); + void removeConnector(PxActor& owner, PxU32 index); + struct Offsets + { + size_t pxActorToNpActor[PxConcreteType::ePHYSX_CORE_COUNT]; + size_t pxActorToScbActor[PxConcreteType::ePHYSX_CORE_COUNT]; + Offsets(); + }; +public: + static const Offsets sOffsets; +}; + + + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpActorTemplate.h b/src/PhysX/physx/source/physx/src/NpActorTemplate.h new file mode 100644 index 000000000..9f95ee971 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpActorTemplate.h @@ -0,0 +1,254 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ACTOR_TEMPLATE +#define PX_PHYSICS_NP_ACTOR_TEMPLATE + +#include "PsUserAllocated.h" +#include "NpWriteCheck.h" +#include "NpReadCheck.h" +#include "NpActor.h" +#include "ScbActor.h" +#include "NpScene.h" + +namespace physx +{ + +// PT: only API (virtual) functions should be implemented here. Other shared non-virtual functions should go to NpActor. + +/** +This is an API class. API classes run in a different thread than the simulation. +For the sake of simplicity they have their own methods, and they do not call simulation +methods directly. To set simulation state, they also have their own custom set +methods in the implementation classes. + +Changing the data layout of this class breaks the binary serialization format. +See comments for PX_BINARY_SERIAL_VERSION. +*/ +template +class NpActorTemplate : public APIClass, public NpActor, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + PX_NOCOPY(NpActorTemplate) +public: +// PX_SERIALIZATION + NpActorTemplate(PxBaseFlags baseFlags) : APIClass(baseFlags), NpActor(PxEmpty) {} + virtual void exportExtraData(PxSerializationContext& stream) { NpActor::exportExtraData(stream); } + void importExtraData(PxDeserializationContext& context) { NpActor::importExtraData(context); } + virtual void resolveReferences(PxDeserializationContext& context) { NpActor::resolveReferences(context); } +//~PX_SERIALIZATION + + NpActorTemplate(PxType concreteType, PxBaseFlags baseFlags, const char* name, void* userData); + virtual ~NpActorTemplate(); + + //--------------------------------------------------------------------------------- + // PxActor implementation + //--------------------------------------------------------------------------------- + virtual void release() { NpActor::release(*this); } + + // The rule is: If an API method is used somewhere in here, it has to be redeclared, else GCC whines + virtual PxActorType::Enum getType() const = 0; + + virtual PxScene* getScene() const; + + // Debug name + virtual void setName(const char*); + virtual const char* getName() const; + + virtual PxBounds3 getWorldBounds(float inflation=1.01f) const = 0; + + // Flags + virtual void setActorFlag(PxActorFlag::Enum flag, bool value); + virtual void setActorFlags(PxActorFlags inFlags); + virtual PxActorFlags getActorFlags() const; + + // Dominance + virtual void setDominanceGroup(PxDominanceGroup dominanceGroup); + virtual PxDominanceGroup getDominanceGroup() const; + + // Multiclient + virtual void setOwnerClient( PxClientID inClient ); + virtual PxClientID getOwnerClient() const; + + // Aggregates + virtual PxAggregate* getAggregate() const { return NpActor::getAggregate(); } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- +protected: + PX_FORCE_INLINE void setActorFlagInternal(PxActorFlag::Enum flag, bool value); + PX_FORCE_INLINE void setActorFlagsInternal(PxActorFlags inFlags); +}; + +/////////////////////////////////////////////////////////////////////////////// + +template +NpActorTemplate::NpActorTemplate(PxType concreteType, + PxBaseFlags baseFlags, + const char* name, + void* actorUserData) +:APIClass(concreteType, baseFlags), +NpActor(name) +{ + // don't ref Scb actor here, it hasn't been assigned yet + + APIClass::userData = actorUserData; +} + + +template +NpActorTemplate::~NpActorTemplate() +{ + NpActor::onActorRelease(this); +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: this one is very slow +template +PxScene* NpActorTemplate::getScene() const +{ + return NpActor::getAPIScene(*this); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +void NpActorTemplate::setName(const char* debugName) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + mName = debugName; + +#if PX_SUPPORT_PVD + Scb::Scene* scbScene = getScbFromPxActor(*this).getScbSceneForAPI(); + Scb::Actor& scbActor = NpActor::getScbFromPxActor(*this); + //Name changing is not bufferred + if(scbScene) + scbScene->getScenePvdClient().updatePvdProperties(&scbActor); +#endif +} + +template +const char* NpActorTemplate::getName() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mName; +} + +/////////////////////////////////////////////////////////////////////////////// + +template +void NpActorTemplate::setDominanceGroup(PxDominanceGroup dominanceGroup) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + NpActor::getScbFromPxActor(*this).setDominanceGroup(dominanceGroup); +} + + +template +PxDominanceGroup NpActorTemplate::getDominanceGroup() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return NpActor::getScbFromPxActor(*this).getDominanceGroup(); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +void NpActorTemplate::setOwnerClient( PxClientID inId ) +{ + if ( NpActor::getOwnerScene(*this) != NULL ) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Attempt to set the client id when an actor is already in a scene."); + } + else + NpActor::getScbFromPxActor(*this).setOwnerClient( inId ); +} + +template +PxClientID NpActorTemplate::getOwnerClient() const +{ + return NpActor::getScbFromPxActor(*this).getOwnerClient(); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +PX_FORCE_INLINE void NpActorTemplate::setActorFlagInternal(PxActorFlag::Enum flag, bool value) +{ + Scb::Actor& a = NpActor::getScbFromPxActor(*this); + if (value) + a.setActorFlags( a.getActorFlags() | flag ); + else + a.setActorFlags( a.getActorFlags() & (~PxActorFlags(flag)) ); +} + +template +PX_FORCE_INLINE void NpActorTemplate::setActorFlagsInternal(PxActorFlags inFlags) +{ + Scb::Actor& a = NpActor::getScbFromPxActor(*this); + a.setActorFlags( inFlags ); +} + +template +void NpActorTemplate::setActorFlag(PxActorFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + setActorFlagInternal(flag, value); +} + +template +void NpActorTemplate::setActorFlags(PxActorFlags inFlags) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + setActorFlagsInternal(inFlags); +} + +template +PxActorFlags NpActorTemplate::getActorFlags() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return NpActor::getScbFromPxActor(*this).getActorFlags(); +} + +/////////////////////////////////////////////////////////////////////////////// + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpAggregate.cpp b/src/PhysX/physx/source/physx/src/NpAggregate.cpp new file mode 100644 index 000000000..2154fadd4 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpAggregate.cpp @@ -0,0 +1,421 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpAggregate.h" + +/////////////////////////////////////////////////////////////////////////////// + +#include "PxActor.h" +#include "NpRigidStatic.h" +#include "NpRigidDynamic.h" +#include "NpArticulation.h" +#include "NpActor.h" +#include "GuBVHStructure.h" +#include "CmUtils.h" +#include "NpArticulationTemplate.h" + +using namespace physx; + +PX_FORCE_INLINE void setAggregate(NpAggregate* aggregate, PxActor& actor) +{ + NpActor& np = NpActor::getFromPxActor(actor); + np.setAggregate(aggregate, actor); +} + +/////////////////////////////////////////////////////////////////////////////// + + +NpAggregate::NpAggregate(PxU32 maxActors, bool selfCollisions) +: PxAggregate(PxConcreteType::eAGGREGATE, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mAggregate(this, maxActors, selfCollisions) +, mNbActors(0) +{ + mActors = reinterpret_cast(PX_ALLOC(sizeof(PxActor*)*maxActors, "PxActor*")); +} + +NpAggregate::~NpAggregate() +{ + NpFactory::getInstance().onAggregateRelease(this); + if(getBaseFlags()&PxBaseFlag::eOWNS_MEMORY) + PX_FREE(mActors); +} + +void NpAggregate::removeAndReinsert(PxActor& actor, bool reinsert) +{ + NpActor& np = NpActor::getFromPxActor(actor); + Scb::Actor& scb = NpActor::getScbFromPxActor(actor); + + np.setAggregate(NULL, actor); + + mAggregate.removeActor(scb, reinsert); +} + +void NpAggregate::release() +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_SIMD_GUARD; + + NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, NULL); + + /* + "An aggregate should be empty when it gets released. If it isn't, the behavior should be: remove the actors from + the aggregate, then remove the aggregate from the scene (if any) then delete it. I guess that implies the actors + get individually reinserted into the broad phase if the aggregate is in a scene." + */ + for(PxU32 i=0;igetType() == PxActorType::eARTICULATION_LINK) + { + NpArticulationLink* link = static_cast(mActors[i]); + PxArticulationImpl* impl = reinterpret_cast(link->getRoot().getImpl()); + impl->setAggregate(NULL); + } + + removeAndReinsert(*mActors[i], true); + } + + NpScene* s = getAPIScene(); + if(s) + { + s->getScene().removeAggregate(getScbAggregate()); + s->removeFromAggregateList(*this); + } + + mAggregate.destroy(); +} + +void NpAggregate::addActorInternal(PxActor& actor, NpScene& s, const PxBVHStructure* bvhStructure) +{ + if (actor.getType() != PxActorType::eARTICULATION_LINK) + { + Scb::Actor& scb = NpActor::getScbFromPxActor(actor); + + mAggregate.addActor(scb); + + s.addActorInternal(actor, bvhStructure); + } + else if (!actor.getScene()) // This check makes sure that a link of an articulation gets only added once. + { + NpArticulationLink& al = static_cast(actor); + PxArticulationBase& npArt = al.getRoot(); + for(PxU32 i=0; i < npArt.getNbLinks(); i++) + { + PxArticulationLink* link; + npArt.getLinks(&link, 1, i); + mAggregate.addActor(static_cast(link)->getScbActorFast()); + } + + s.addArticulationInternal(npArt); + } +} + +bool NpAggregate::addActor(PxActor& actor, const PxBVHStructure* bvhStructure) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_SIMD_GUARD; + + if(mNbActors==mAggregate.getMaxActorCount()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add actor to aggregate, max number of actors reached"); + return false; + } + + if(actor.getAggregate()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add actor to aggregate, actor already belongs to an aggregate"); + return false; + } + + if(actor.getScene()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add actor to aggregate, actor already belongs to a scene"); + return false; + } + + if(actor.getType() == PxActorType::eARTICULATION_LINK) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add articulation link to aggregate, only whole articulations can be added"); + return false; + } + + setAggregate(this, actor); + + mActors[mNbActors++] = &actor; + + // PT: when an object is added to a aggregate at runtime, i.e. when the aggregate has already been added to the scene, + // we need to immediately add the newcomer to the scene as well. + NpScene* s = getAPIScene(); + if(s) + { + addActorInternal(actor, *s, bvhStructure); + } + else + { + // A.B. if BVH structure is provided we need to keep it stored till the aggregate is inseted into a scene + if(bvhStructure) + { + PxBVHStructure* bvhStructureMutable = const_cast(bvhStructure); + static_cast(bvhStructureMutable)->incRefCount(); + NpActor::getFromPxActor(actor).addConnector(NpConnectorType::eBvhStructure, bvhStructureMutable, "PxBVHStructure already added to the PxActor!"); + } + } + + return true; +} + +bool NpAggregate::removeActorAndReinsert(PxActor& actor, bool reinsert) +{ + for(PxU32 i=0;i(NpConnectorType::eBvhStructure, &bvhStructure, 1)) + { + np.removeConnector(actor, NpConnectorType::eBvhStructure, bvhStructure, "PxBVHStructure connector could not have been removed!"); + bvhStructure->decRefCount(); + } + } + + // PT: there are really 2 cases here: + // a) the user just wants to remove the actor from the aggregate, but the actor is still alive so if the aggregate has been added to a scene, + // we must reinsert the removed actor to that same scene + // b) this is called by the framework when releasing an actor, in which case we don't want to reinsert it anywhere. + // + // We assume that when called by the user, we always want to reinsert. The framework however will call the internal function + // without reinsertion. + return removeActorAndReinsert(actor, true); +} + +bool NpAggregate::addArticulation(PxArticulationBase& art) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_SIMD_GUARD; + + if((mNbActors+art.getNbLinks()) > mAggregate.getMaxActorCount()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add articulation links, max number of actors reached"); + return false; + } + + if(art.getAggregate()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add articulation to aggregate, articulation already belongs to an aggregate"); + return false; + } + + if(art.getScene()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add articulation to aggregate, articulation already belongs to a scene"); + return false; + } + + PxArticulationImpl* impl = reinterpret_cast(art.getImpl()); + impl->setAggregate(this); + NpArticulationLink* const* links = impl->getLinks(); + for(PxU32 i=0; i < impl->getNbLinks(); i++) + { + NpArticulationLink& l = *links[i]; + + setAggregate(this, l); + + mActors[mNbActors++] = &l; + + mAggregate.addActor(l.getScbActorFast()); + } + + // PT: when an object is added to a aggregate at runtime, i.e. when the aggregate has already been added to the scene, + // we need to immediately add the newcomer to the scene as well. + NpScene* s = getAPIScene(); + if(s) + { + s->addArticulationInternal(art); + } + + return true; +} + +bool NpAggregate::removeArticulationAndReinsert(PxArticulationBase& art, bool reinsert) +{ + bool found = false; + PxU32 idx = 0; + while(idx < mNbActors) + { + if ((mActors[idx]->getType() == PxActorType::eARTICULATION_LINK) && (&static_cast(mActors[idx])->getRoot() == &art)) + { + PxActor* a = mActors[idx]; + mActors[idx] = mActors[--mNbActors]; + removeAndReinsert(*a, reinsert); + found = true; + } + else + idx++; + } + + reinterpret_cast(art.getImpl())->setAggregate(NULL); + + if (!found) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't remove articulation, articulation doesn't belong to aggregate"); + return found; +} + +bool NpAggregate::removeArticulation(PxArticulationBase& art) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_SIMD_GUARD; + + // see comments in removeActor() + return removeArticulationAndReinsert(art, true); +} + +PxU32 NpAggregate::getNbActors() const +{ + NP_READ_CHECK(getOwnerScene()); + return mNbActors; +} + +PxU32 NpAggregate::getMaxNbActors() const +{ + NP_READ_CHECK(getOwnerScene()); + return mAggregate.getMaxActorCount(); +} + +PxU32 NpAggregate::getActors(PxActor** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(getOwnerScene()); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mActors, getCurrentSizeFast()); +} + +PxScene* NpAggregate::getScene() +{ + return getAPIScene(); +} + +NpScene* NpAggregate::getAPIScene() const +{ + return mAggregate.getScbSceneForAPI() ? static_cast(mAggregate.getScbSceneForAPI()->getPxScene()) : NULL; +} + +NpScene* NpAggregate::getOwnerScene() const +{ + return mAggregate.getScbScene() ? static_cast(mAggregate.getScbScene()->getPxScene()) : NULL; +} + +bool NpAggregate::getSelfCollision() const +{ + NP_READ_CHECK(getOwnerScene()); + return mAggregate.getSelfCollide(); +} + +// PX_SERIALIZATION +void NpAggregate::exportExtraData(PxSerializationContext& stream) +{ + if(mActors) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mActors, mNbActors * sizeof(PxActor*)); + } +} + +void NpAggregate::importExtraData(PxDeserializationContext& context) +{ + if(mActors) + mActors = context.readExtraData(mNbActors); +} + +void NpAggregate::resolveReferences(PxDeserializationContext& context) +{ + // Resolve actor pointers if needed + for(PxU32 i=0; i < mNbActors; i++) + { + context.translatePxBase(mActors[i]); + { + //update aggregate if mActors is in external reference + NpActor& np = NpActor::getFromPxActor(*mActors[i]); + if(np.getAggregate() == NULL) + { + np.setAggregate(this, *mActors[i]); + } + if(mActors[i]->getType() == PxActorType::eARTICULATION_LINK) + { + PxArticulationBase& articulation = static_cast(mActors[i])->getRoot(); + if(!articulation.getAggregate()) + reinterpret_cast(articulation.getImpl())->setAggregate(this); + } + } + } +} + +NpAggregate* NpAggregate::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpAggregate* obj = new (address) NpAggregate(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(NpAggregate); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void NpAggregate::requiresObjects(PxProcessPxBaseCallback& c) +{ + for(PxU32 i=0; i < mNbActors; i++) + { + PxArticulationLink* link = mActors[i]->is(); + if(link) + c.process(link->getArticulation()); + else + c.process(*mActors[i]); + } +} +// ~PX_SERIALIZATION diff --git a/src/PhysX/physx/source/physx/src/NpAggregate.h b/src/PhysX/physx/source/physx/src/NpAggregate.h new file mode 100644 index 000000000..11200dc35 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpAggregate.h @@ -0,0 +1,100 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_AGGREGATE +#define PX_PHYSICS_NP_AGGREGATE + +#include "CmPhysXCommon.h" +#include "PxAggregate.h" +#include "ScbAggregate.h" +#include "PsUserAllocated.h" + +namespace physx +{ + +class NpScene; + +class NpAggregate : public PxAggregate, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpAggregate(PxBaseFlags baseFlags) : PxAggregate(baseFlags), mAggregate(PxEmpty) {} + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback& c); + static NpAggregate* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + NpAggregate(PxU32 maxActors, bool selfCollision); + virtual ~NpAggregate(); + + virtual void release(); + virtual bool addActor(PxActor&, const PxBVHStructure* ); + virtual bool removeActor(PxActor&); + virtual bool addArticulation(PxArticulationBase&); + virtual bool removeArticulation(PxArticulationBase&); + + virtual PxU32 getNbActors() const; + virtual PxU32 getMaxNbActors() const; + virtual PxU32 getActors(PxActor** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + virtual PxScene* getScene(); + virtual bool getSelfCollision() const; + + PX_FORCE_INLINE PxU32 getCurrentSizeFast() const { return mNbActors; } + PX_FORCE_INLINE PxActor* getActorFast(PxU32 i) const { return mActors[i]; } + PX_FORCE_INLINE bool getSelfCollideFast() const { return mAggregate.getSelfCollide(); } + + NpScene* getAPIScene() const; + NpScene* getOwnerScene() const; // the scene the user thinks the actor is in, or from which the actor is pending removal + + void addActorInternal(PxActor& actor, NpScene& s, const PxBVHStructure* bvhStructure); + void removeAndReinsert(PxActor& actor, bool reinsert); + bool removeActorAndReinsert(PxActor& actor, bool reinsert); + bool removeArticulationAndReinsert(PxArticulationBase& art, bool reinsert); + + PX_FORCE_INLINE Scb::Aggregate& getScbAggregate() { return mAggregate; } + +private: + Scb::Aggregate mAggregate; + PxU32 mNbActors; + PxActor** mActors; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulation.cpp b/src/PhysX/physx/source/physx/src/NpArticulation.cpp new file mode 100644 index 000000000..4ce735c67 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulation.cpp @@ -0,0 +1,247 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxTransform.h" +#include "NpArticulation.h" +#include "NpArticulationLink.h" +#include "NpWriteCheck.h" +#include "NpReadCheck.h" +#include "NpFactory.h" +#include "ScbArticulation.h" +#include "NpAggregate.h" +#include "CmUtils.h" +#include "NpArticulationJoint.h" + +namespace physx +{ + +// PX_SERIALIZATION +void NpArticulation::requiresObjects(PxProcessPxBaseCallback& c) +{ + // Collect articulation links + const PxU32 nbLinks = mImpl.mArticulationLinks.size(); + for(PxU32 i=0; iimportExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +NpArticulation::NpArticulation() + : NpArticulationTemplate(PxConcreteType::eARTICULATION, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +{ + PxArticulationBase::userData = NULL; + mType = PxArticulationBase::eMaximumCoordinate; + mImpl.mArticulation.setArticulationType(PxArticulationBase::eMaximumCoordinate); +} + + +NpArticulation::~NpArticulation() +{ + NpFactory::getInstance().onArticulationRelease(this); +} + + +PxU32 NpArticulation::getInternalDriveIterations() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getInternalDriveIterations(); +} + +void NpArticulation::setInternalDriveIterations(PxU32 iterations) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setInternalDriveIterations(iterations); +} + +PxU32 NpArticulation::getExternalDriveIterations() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getExternalDriveIterations(); +} + +void NpArticulation::setExternalDriveIterations(PxU32 iterations) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setExternalDriveIterations(iterations); +} + +PxU32 NpArticulation::getMaxProjectionIterations() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getMaxProjectionIterations(); +} + +void NpArticulation::setMaxProjectionIterations(PxU32 iterations) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setMaxProjectionIterations(iterations); +} + +PxReal NpArticulation::getSeparationTolerance() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getSeparationTolerance(); +} + +void NpArticulation::setSeparationTolerance(PxReal tolerance) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setSeparationTolerance(tolerance); +} + + +PxArticulationDriveCache* NpArticulation::createDriveCache(PxReal compliance, PxU32 driveIterations) const +{ + PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::createDriveCache: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + return reinterpret_cast(mImpl.mArticulation.getScArticulation().createDriveCache(compliance, driveIterations)); +} + + +void NpArticulation::updateDriveCache(PxArticulationDriveCache& cache, PxReal compliance, PxU32 driveIterations) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::updateDriveCache: object must be in a scene"); + + Sc::ArticulationDriveCache& c = reinterpret_cast(cache); + PX_CHECK_AND_RETURN(mImpl.mArticulation.getScArticulation().getCacheLinkCount(c) == mImpl.mArticulationLinks.size(), "PxArticulation::updateDriveCache: Articulation size has changed; drive cache is invalid"); + + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + mImpl.mArticulation.getScArticulation().updateDriveCache(c, compliance, driveIterations); +} + +void NpArticulation::releaseDriveCache(PxArticulationDriveCache&cache ) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::releaseDriveCache: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + mImpl.mArticulation.getScArticulation().releaseDriveCache(reinterpret_cast(cache)); +} + +void NpArticulation::applyImpulse(PxArticulationLink* link, + const PxArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::applyImpulse: object must be in a scene"); + PX_CHECK_AND_RETURN(force.isFinite() && torque.isFinite(), "PxArticulation::applyImpulse: invalid force/torque"); + const Sc::ArticulationDriveCache& c = reinterpret_cast(driveCache); + PX_CHECK_AND_RETURN(mImpl.mArticulation.getScArticulation().getCacheLinkCount(c) == mImpl.mArticulationLinks.size(), "PxArticulation::applyImpulse: Articulation size has changed; drive cache is invalid"); + + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + if(mImpl.isSleeping()) + mImpl.wakeUp(); + + mImpl.mArticulation.getScArticulation().applyImpulse(static_cast(link)->getScbBodyFast().getScBody(), c,force, torque); + for(PxU32 i=0;igetScbBodyFast().getScBody().getLinearVelocity(), + av = mImpl.mArticulationLinks[i]->getScbBodyFast().getScBody().getAngularVelocity(); + mImpl.mArticulationLinks[i]->setLinearVelocity(lv); + mImpl.mArticulationLinks[i]->setAngularVelocity(av); + } +} + +void NpArticulation::computeImpulseResponse(PxArticulationLink* link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const PxArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const +{ + + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeImpulseResponse: object must be in a scene"); + PX_CHECK_AND_RETURN(force.isFinite() && torque.isFinite(), "PxArticulation::computeImpulseResponse: invalid force/torque"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + const Sc::ArticulationDriveCache& c = reinterpret_cast(driveCache); + PX_CHECK_AND_RETURN(mImpl.mArticulation.getScArticulation().getCacheLinkCount(c) == mImpl.mArticulationLinks.size(), "PxArticulation::computeImpulseResponse: Articulation size has changed; drive cache is invalid"); + PX_UNUSED(&c); + + mImpl.mArticulation.getScArticulation().computeImpulseResponse(static_cast(link)->getScbBodyFast().getScBody(), + linearResponse, angularResponse, + reinterpret_cast(driveCache), + force, torque); +} + +Scb::Body* NpArticulationGetRootFromScb(Scb::Articulation&c) +{ + const size_t offset = size_t(&(reinterpret_cast(0)->mImpl.getScbArticulation())); + NpArticulation* np = reinterpret_cast(reinterpret_cast(&c) - offset); + + NpArticulationLink* a = np->mImpl.getRoot(); + + return a ? &a->getScbBodyFast() : NULL; +} + +PxArticulationJointBase* NpArticulation::createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame) +{ + return NpFactory::getInstance().createNpArticulationJoint(static_cast(parent), parentFrame, static_cast(child), childFrame); +} +void NpArticulation::releaseArticulationJoint(PxArticulationJointBase* joint) +{ + NpFactory::getInstance().releaseArticulationJointToPool(*static_cast(joint)); +} + +} diff --git a/src/PhysX/physx/source/physx/src/NpArticulation.h b/src/PhysX/physx/source/physx/src/NpArticulation.h new file mode 100644 index 000000000..8192e4373 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulation.h @@ -0,0 +1,143 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ARTICULATION +#define PX_PHYSICS_NP_ARTICULATION + +#include "PxArticulation.h" +#include "CmPhysXCommon.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +#include "ScbArticulation.h" +#include "NpArticulationLink.h" +#include "NpArticulationTemplate.h" +#include "NpArticulationJoint.h" + +namespace physx +{ + +class NpArticulationLink; +class NpScene; +class NpAggregate; +class PxAggregate; + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4250) +#endif + + +class NpArticulation : public NpArticulationTemplate +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + +public: + virtual ~NpArticulation(); + +// PX_SERIALIZATION + NpArticulation(PxBaseFlags baseFlags) : NpArticulationTemplate(baseFlags) + {} + + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback& c); + static NpArticulation* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + //--------------------------------------------------------------------------------- + // PxArticulation implementation + //--------------------------------------------------------------------------------- + + virtual PxU32 getInternalDriveIterations() const; + virtual void setInternalDriveIterations(PxU32 iterations); + + virtual PxU32 getExternalDriveIterations() const; + virtual void setExternalDriveIterations(PxU32 iterations); + + virtual PxU32 getMaxProjectionIterations() const; + virtual void setMaxProjectionIterations(PxU32 iterations); + + virtual PxReal getSeparationTolerance() const; + virtual void setSeparationTolerance(PxReal tolerance); + + + virtual PxArticulationDriveCache* + createDriveCache(PxReal compliance, PxU32 driveIterations) const; + + virtual void updateDriveCache(PxArticulationDriveCache& cache, PxReal compliance, PxU32 driveIterations) const; + + virtual void releaseDriveCache(PxArticulationDriveCache&) const; + + virtual void applyImpulse(PxArticulationLink*, + const PxArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque); + + virtual void computeImpulseResponse(PxArticulationLink*, + PxVec3& linearResponse, + PxVec3& angularResponse, + const PxArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const; + + virtual const char* getConcreteTypeName() const { return "PxArticulation"; } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpArticulation(); + + + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulation", name) || PxBase::isKindOf(name); } + + virtual PxArticulationJointBase* createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame); + virtual void releaseArticulationJoint(PxArticulationJointBase* joint); +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp b/src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp new file mode 100644 index 000000000..eea8fe4cb --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp @@ -0,0 +1,381 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpCast.h" +#include "NpWriteCheck.h" +#include "NpReadCheck.h" + +namespace physx +{ +// PX_SERIALIZATION +void NpArticulationJoint::resolveReferences(PxDeserializationContext& context) +{ + mImpl.resolveReferences(context); +} + +NpArticulationJoint* NpArticulationJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpArticulationJoint* obj = new (address) NpArticulationJoint(PxBaseFlags(0)); + address += sizeof(NpArticulationJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} +// ~PX_SERIALIZATION + +NpArticulationJoint::NpArticulationJoint(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame) +: NpArticulationJointTemplate(parent, parentFrame, child, childFrame) +{ +} + + +NpArticulationJoint::~NpArticulationJoint() +{ +} + + +void NpArticulationJoint::setTargetOrientation(const PxQuat& p) +{ + PX_CHECK_AND_RETURN(mImpl.getScbArticulationJoint().getDriveType() != PxArticulationJointDriveType::eTARGET || p.isUnit(), "NpArticulationJoint::setTargetOrientation, quat orientation is not valid."); + PX_CHECK_AND_RETURN(mImpl.getScbArticulationJoint().getDriveType() != PxArticulationJointDriveType::eERROR || (p.getImaginaryPart().isFinite() && p.w == 0), "NpArticulationJoint::setTargetOrientation rotation vector orientation is not valid."); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTargetOrientation(p); +} + + +PxQuat NpArticulationJoint::getTargetOrientation() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTargetOrientation(); +} + + +void NpArticulationJoint::setTargetVelocity(const PxVec3& v) +{ + PX_CHECK_AND_RETURN(v.isFinite(), "NpArticulationJoint::setTargetVelocity v is not valid."); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTargetVelocity(v); +} + + +PxArticulationJointDriveType::Enum NpArticulationJoint::getDriveType() const +{ + NP_READ_CHECK(getOwnerScene()); + return mImpl.getScbArticulationJoint().getDriveType(); +} + +void NpArticulationJoint::setDriveType(PxArticulationJointDriveType::Enum driveType) +{ + NP_WRITE_CHECK(getOwnerScene()); + mImpl.getScbArticulationJoint().setDriveType(driveType); +} + + +PxVec3 NpArticulationJoint::getTargetVelocity() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTargetVelocity(); +} + + +void NpArticulationJoint::setStiffness(PxReal s) +{ + PX_CHECK_AND_RETURN(PxIsFinite(s) && s >= 0.0f, "PxArticulationJoint::setStiffness: spring coefficient must be >= 0!"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setStiffness(s); +} + + +PxReal NpArticulationJoint::getStiffness() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getStiffness(); +} + + +void NpArticulationJoint::setDamping(PxReal d) +{ + PX_CHECK_AND_RETURN(PxIsFinite(d) && d >= 0.0f, "PxArticulationJoint::setDamping: damping coefficient must be >= 0!"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setDamping(d); +} + + +PxReal NpArticulationJoint::getDamping() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getDamping(); +} + + +void NpArticulationJoint::setSwingLimitContactDistance(PxReal d) +{ + PX_CHECK_AND_RETURN(PxIsFinite(d) && d >= 0.0f, "PxArticulationJoint::setSwingLimitContactDistance: padding coefficient must be > 0!"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setSwingLimitContactDistance(d); +} + + +PxReal NpArticulationJoint::getSwingLimitContactDistance() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getSwingLimitContactDistance(); +} + + +void NpArticulationJoint::setTwistLimitContactDistance(PxReal d) +{ + PX_CHECK_AND_RETURN(PxIsFinite(d) && d >= 0.0f, "PxArticulationJoint::setTwistLimitContactDistance: padding coefficient must be > 0!"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTwistLimitContactDistance(d); +} + + +PxReal NpArticulationJoint::getTwistLimitContactDistance() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTwistLimitContactDistance(); +} + +PxArticulationJointType::Enum NpArticulationJoint::getJointType() const +{ + NP_READ_CHECK(getOwnerScene()); + return mImpl.getScbArticulationJoint().getJointType(); +} + +void NpArticulationJoint::setJointType(PxArticulationJointType::Enum jointType) +{ + NP_WRITE_CHECK(getOwnerScene()); + mImpl.getScbArticulationJoint().setJointType(jointType); +} + +void NpArticulationJoint::setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) +{ + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setMotion(axis, motion); + + reinterpret_cast(getChild().getArticulation().getImpl())->increaseCacheVersion(); +} + +PxArticulationMotion::Enum NpArticulationJoint::getMotion(PxArticulationAxis::Enum axis) const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getMotion(axis); +} + +void NpArticulationJoint::setFrictionCoefficient(const PxReal coefficient) +{ + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setFrictionCoefficient(coefficient); + +} + +PxReal NpArticulationJoint::getFrictionCoefficient() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getFrictionCoefficient(); +} + +void NpArticulationJoint::setInternalCompliance(PxReal r) +{ + PX_CHECK_AND_RETURN(PxIsFinite(r) && r>0, "PxArticulationJoint::setInternalCompliance: compliance must be > 0"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setInternalCompliance(r); +} + + + +PxReal NpArticulationJoint::getInternalCompliance() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getInternalCompliance(); +} + + +void NpArticulationJoint::setExternalCompliance(PxReal r) +{ + PX_CHECK_AND_RETURN(PxIsFinite(r) && r>0, "PxArticulationJoint::setExternalCompliance: compliance must be > 0"); + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setExternalCompliance(r); +} + + +PxReal NpArticulationJoint::getExternalCompliance() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getExternalCompliance(); +} + + +void NpArticulationJoint::setSwingLimit(PxReal yLimit, PxReal zLimit) +{ + PX_CHECK_AND_RETURN(PxIsFinite(yLimit) && PxIsFinite(zLimit) && yLimit > 0 && zLimit > 0 && yLimit < PxPi && zLimit < PxPi, + "PxArticulationJoint::setSwingLimit: values must be >0 and < Pi"); + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setSwingLimit(yLimit, zLimit); +} + + +void NpArticulationJoint::getSwingLimit(PxReal &yLimit, PxReal &zLimit) const +{ + NP_READ_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().getSwingLimit(yLimit, zLimit); +} + + +void NpArticulationJoint::setTangentialStiffness(PxReal stiffness) +{ + PX_CHECK_AND_RETURN(PxIsFinite(stiffness) && stiffness >= 0, "PxArticulationJoint::setTangentialStiffness: stiffness must be > 0"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTangentialStiffness(stiffness); +} + + +PxReal NpArticulationJoint::getTangentialStiffness() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTangentialStiffness(); +} + + +void NpArticulationJoint::setTangentialDamping(PxReal damping) +{ + PX_CHECK_AND_RETURN(PxIsFinite(damping) && damping >= 0, "PxArticulationJoint::setTangentialDamping: damping must be > 0"); + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTangentialDamping(damping); +} + + +PxReal NpArticulationJoint::getTangentialDamping() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTangentialDamping(); +} + + + +void NpArticulationJoint::setSwingLimitEnabled(bool e) +{ + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setSwingLimitEnabled(e); +} + + +bool NpArticulationJoint::getSwingLimitEnabled() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getSwingLimitEnabled(); +} + + +void NpArticulationJoint::setTwistLimit(PxReal lower, PxReal upper) +{ + PX_CHECK_AND_RETURN(PxIsFinite(lower) && PxIsFinite(upper) && lower-PxPi && upper < PxPi, "PxArticulationJoint::setTwistLimit: illegal parameters"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTwistLimit(lower, upper); +} + + +void NpArticulationJoint::getTwistLimit(PxReal &lower, PxReal &upper) const +{ + NP_READ_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().getTwistLimit(lower, upper); +} + + +void NpArticulationJoint::setTwistLimitEnabled(bool e) +{ + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTwistLimitEnabled(e); +} + + +bool NpArticulationJoint::getTwistLimitEnabled() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTwistLimitEnabled(); +} + + +void NpArticulationJointGetBodiesFromScb(Scb::ArticulationJoint&c, Scb::Body*&b0, Scb::Body*&b1) +{ + NpArticulationJoint* np = const_cast(getNpArticulationJoint(&c)); + + NpArticulationLink& a0 = np->getParent(), & a1 = np->getChild(); + b0 = &a0.getScbBodyFast(); + b1 = &a1.getScbBodyFast(); +} + + +} diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJoint.h b/src/PhysX/physx/source/physx/src/NpArticulationJoint.h new file mode 100644 index 000000000..1c4189771 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationJoint.h @@ -0,0 +1,357 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ARTICULATION_JOINT +#define PX_PHYSICS_NP_ARTICULATION_JOINT + +#include "PxArticulationJoint.h" +#include "ScbArticulationJoint.h" +#include "NpArticulationTemplate.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +namespace physx +{ + +class NpScene; +class NpArticulationLink; + +class PxArticulationJointImpl +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + Scb::ArticulationJoint mJoint; + NpArticulationLink* mParent; + NpArticulationLink* mChild; + PxArticulationBase::Enum mType; + + PX_INLINE PxArticulationJointImpl(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame, + PxArticulationBase::Enum type); + +// PX_SERIALIZATION + PX_INLINE PxArticulationJointImpl(const PxEMPTY) : mJoint(PxEmpty) {} +//~PX_SERIALIZATION + + PX_INLINE NpScene* getOwnerScene() const; // the scene the user thinks the actor is in, or from which the actor is pending removal + + PX_INLINE void release(); + + PX_INLINE const Scb::ArticulationJoint& getScbArticulationJoint() const { return mJoint; } + PX_INLINE Scb::ArticulationJoint& getScbArticulationJoint() { return mJoint; } + + PX_INLINE PxArticulationLink& getParentArticulationLink() const; + PX_INLINE PxArticulationLink& getChildArticulationLink() const; + + PX_INLINE PxTransform getParentPose() const; + PX_INLINE void setParentPose(const PxTransform&); + + PX_INLINE PxTransform getChildPose() const; + PX_INLINE void setChildPose(const PxTransform&); + + PX_INLINE const NpArticulationLink& getParent() const { return *mParent; } + PX_INLINE NpArticulationLink& getParent() { return *mParent; } + + PX_INLINE const NpArticulationLink& getChild() const { return *mChild; } + PX_INLINE NpArticulationLink& getChild() { return *mChild; } + + PX_INLINE void resolveReferences(PxDeserializationContext& context); +}; + +template +class NpArticulationJointTemplate : public APIClass, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + +// PX_SERIALIZATION + NpArticulationJointTemplate(PxBaseFlags baseFlags) : APIClass(baseFlags), mImpl(PxEmpty) + { + mImpl.getScbArticulationJoint().getScArticulationJoint().setRoot(this); + } +//~PX_SERIALIZATION + + NpArticulationJointTemplate(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame, + PxArticulationBase::Enum type = PxArticulationBase::eMaximumCoordinate); + + virtual ~NpArticulationJointTemplate() {} + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- +public: + void release(); + + PX_INLINE const Scb::ArticulationJoint& getScbArticulationJoint() const { return mImpl.getScbArticulationJoint(); } + PX_INLINE Scb::ArticulationJoint& getScbArticulationJoint() { return mImpl.getScbArticulationJoint(); } + + virtual PxArticulationLink& getParentArticulationLink() const { return mImpl.getParentArticulationLink(); } + virtual PxArticulationLink& getChildArticulationLink() const { return mImpl.getChildArticulationLink(); } + + virtual PxTransform getParentPose() const { return mImpl.getParentPose(); } + virtual void setParentPose(const PxTransform& t) { mImpl.setParentPose(t); } + + virtual PxTransform getChildPose() const { return mImpl.getChildPose(); } + virtual void setChildPose(const PxTransform& t) { mImpl.setChildPose(t); } + + PX_INLINE const NpArticulationLink& getParent() const { return mImpl.getParent(); } + PX_INLINE NpArticulationLink& getParent() { return mImpl.getParent(); } + + PX_INLINE const NpArticulationLink& getChild() const { return mImpl.getChild(); } + PX_INLINE NpArticulationLink& getChild() { return mImpl.getChild(); } + + virtual PxArticulationJointImpl* getImpl() { return &mImpl; } + virtual const PxArticulationJointImpl* getImpl() const { return &mImpl; } + +protected: + + NpScene* getOwnerScene() const { return mImpl.getOwnerScene(); } + + PxArticulationJointImpl mImpl; +}; + +class NpArticulationJoint : public NpArticulationJointTemplate +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpArticulationJoint(PxBaseFlags baseFlags) : NpArticulationJointTemplate(baseFlags) {} + virtual void resolveReferences(PxDeserializationContext& context); + static NpArticulationJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void exportExtraData(PxSerializationContext&) {} + void importExtraData(PxDeserializationContext&) {} + virtual void requiresObjects(PxProcessPxBaseCallback&){} + virtual bool isSubordinate() const { return true; } +//~PX_SERIALIZATION + NpArticulationJoint(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame); + + virtual ~NpArticulationJoint(); + + //--------------------------------------------------------------------------------- + // PxArticulationJoint implementation + //--------------------------------------------------------------------------------- + // Save + + + virtual void setTargetOrientation(const PxQuat&); + virtual PxQuat getTargetOrientation() const; + + + virtual void setTargetVelocity(const PxVec3&); + virtual PxVec3 getTargetVelocity() const; + + virtual void setDriveType(PxArticulationJointDriveType::Enum driveType); + virtual PxArticulationJointDriveType::Enum + getDriveType() const; + + + virtual void setStiffness(PxReal); + virtual PxReal getStiffness() const; + + virtual void setDamping(PxReal); + virtual PxReal getDamping() const; + + virtual void setInternalCompliance(PxReal); + virtual PxReal getInternalCompliance() const; + + virtual void setExternalCompliance(PxReal); + virtual PxReal getExternalCompliance() const; + + virtual void setSwingLimit(PxReal yLimit, PxReal zLimit); + virtual void getSwingLimit(PxReal &yLimit, PxReal &zLimit) const; + + virtual void setTangentialStiffness(PxReal spring); + virtual PxReal getTangentialStiffness() const; + + virtual void setTangentialDamping(PxReal damping); + virtual PxReal getTangentialDamping() const; + + virtual void setSwingLimitEnabled(bool); + virtual bool getSwingLimitEnabled() const; + + virtual void setSwingLimitContactDistance(PxReal contactDistance); + virtual PxReal getSwingLimitContactDistance() const; + + virtual void setTwistLimit(PxReal lower, PxReal upper); + virtual void getTwistLimit(PxReal &lower, PxReal &upper) const; + + virtual void setTwistLimitEnabled(bool); + virtual bool getTwistLimitEnabled() const; + + virtual void setTwistLimitContactDistance(PxReal contactDistance); + virtual PxReal getTwistLimitContactDistance() const; + + virtual void setJointType(PxArticulationJointType::Enum jointType); + virtual PxArticulationJointType::Enum + getJointType() const; + + virtual void setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion); + virtual PxArticulationMotion::Enum + getMotion(PxArticulationAxis::Enum axis) const; + + virtual void setFrictionCoefficient(const PxReal coefficient); + + virtual PxReal getFrictionCoefficient() const ; +}; + +PX_INLINE PxArticulationLink& PxArticulationJointImpl::getParentArticulationLink() const +{ + return *mParent; +} + + +PX_INLINE PxArticulationLink& PxArticulationJointImpl::getChildArticulationLink() const +{ + return *mChild; +} + + +PX_INLINE PxTransform PxArticulationJointImpl::getParentPose() const +{ + NP_READ_CHECK(getOwnerScene()); + return mJoint.getParentPose(); +} + + +PX_INLINE void PxArticulationJointImpl::setParentPose(const PxTransform& t) +{ + PX_CHECK_AND_RETURN(t.isSane(), "NpArticulationJoint::setParentPose t is not valid."); + + NP_WRITE_CHECK(getOwnerScene()); + if (mParent == NULL) + return; + + mJoint.setParentPose(mParent->getCMassLocalPose().transformInv(t.getNormalized())); +} + + +PX_INLINE PxTransform PxArticulationJointImpl::getChildPose() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mChild->getCMassLocalPose().transform(mJoint.getChildPose()); +} + + +PX_INLINE void PxArticulationJointImpl::setChildPose(const PxTransform& t) +{ + PX_CHECK_AND_RETURN(t.isSane(), "NpArticulationJoint::setChildPose t is not valid."); + + NP_WRITE_CHECK(getOwnerScene()); + + mJoint.setChildPose(mChild->getCMassLocalPose().transformInv(t.getNormalized())); +} + + +PX_INLINE NpScene* PxArticulationJointImpl::getOwnerScene() const +{ + return mJoint.getScbScene() ? static_cast(mJoint.getScbScene()->getPxScene()) : NULL; +} + + +PX_INLINE PxArticulationJointImpl::PxArticulationJointImpl(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame, + PxArticulationBase::Enum type) + : mJoint(parentFrame, childFrame, type) + , mParent(&parent) + , mChild(&child) + , mType(type) +{ + PxArticulationImpl* impl = parent.getRoot().getImpl(); + Scb::Articulation& scbArti = impl->getArticulation(); + mJoint.setScArticulation(&scbArti); +} + +PX_INLINE void PxArticulationJointImpl::release() +{ + if (mJoint.getScbSceneForAPI()) + mJoint.getScbSceneForAPI()->removeArticulationJoint(mJoint); + + mJoint.destroy(); +} + +PX_INLINE void PxArticulationJointImpl::resolveReferences(PxDeserializationContext& context) +{ + context.translatePxBase(mParent); + context.translatePxBase(mChild); +} + +template +void NpArticulationJointTemplate::release() +{ + NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, NULL); + mImpl.release(); +} + + +template +NpArticulationJointTemplate::NpArticulationJointTemplate(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame, + PxArticulationBase::Enum type) + : APIClass(PxConcreteType::eARTICULATION_JOINT, PxBaseFlag::eOWNS_MEMORY), + mImpl(parent, parentFrame, child, childFrame, type) +{ + PX_UNUSED(type); + mImpl.getScbArticulationJoint().getScArticulationJoint().setRoot(this); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp new file mode 100644 index 000000000..41345a0b3 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp @@ -0,0 +1,242 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpCast.h" +#include "NpWriteCheck.h" +#include "NpReadCheck.h" +#include "NpArticulationJointReducedCoordinate.h" + +namespace physx +{ + NpArticulationJointReducedCoordinate* NpArticulationJointReducedCoordinate::createObject(PxU8*& address, PxDeserializationContext& context) + { + NpArticulationJointReducedCoordinate* obj = new (address) NpArticulationJointReducedCoordinate(PxBaseFlags(0)); + address += sizeof(NpArticulationJointReducedCoordinate); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; + } + + void NpArticulationJointReducedCoordinate::getBinaryMetaData(PxOutputStream& stream) + { + // 184 => 200 => 192 => 224 => 208 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpArticulationJointReducedCoordinate) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpArticulationJointReducedCoordinate, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationJointReducedCoordinate, PxArticulationJointImpl, mImpl, 0) + + } + //~PX_SERIALIZATION + NpArticulationJointReducedCoordinate::NpArticulationJointReducedCoordinate(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame) : + NpArticulationJointTemplate(parent, parentFrame, child, childFrame, PxArticulationBase::eReducedCoordinate) + { + + } + + NpArticulationJointReducedCoordinate::~NpArticulationJointReducedCoordinate() + { + } + + + void NpArticulationJointReducedCoordinate::setJointType(PxArticulationJointType::Enum jointType) + { + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(jointType != PxArticulationJointType::eUNDEFINED, "PxArticulationJointReducedCoordinate::setJointType valid joint type(ePRISMATIC, eREVOLUTE, eSPHERICAL, eFIX) need to be set"); + mImpl.getScbArticulationJoint().setJointType(jointType); + } + PxArticulationJointType::Enum NpArticulationJointReducedCoordinate::getJointType() const + { + NP_READ_CHECK(getOwnerScene()); + return mImpl.getScbArticulationJoint().getJointType(); + } + +#if PX_CHECKED + bool NpArticulationJointReducedCoordinate::isValidMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) + { + + PxArticulationJointType::Enum type = getJointType(); + + bool valid = true; + switch (type) + { + case PxArticulationJointType::ePRISMATIC: + { + if (axis < PxArticulationAxis::eX && motion != PxArticulationMotion::eLOCKED) + valid = false; + else if(motion != PxArticulationMotion::eLOCKED) + { + //Check to ensure that we only have zero DOFs already active... + for (PxU32 i = PxArticulationAxis::eX; i <= PxArticulationAxis::eZ; i++) + { + if(i != PxU32(axis) && mImpl.mJoint.getMotion(PxArticulationAxis::Enum(i)) != PxArticulationMotion::eLOCKED) + valid = false; + } + } + break; + } + case PxArticulationJointType::eREVOLUTE: + { + if (axis >= PxArticulationAxis::eX && motion != PxArticulationMotion::eLOCKED) + valid = false; + else if (motion != PxArticulationMotion::eLOCKED) + { + + for (PxU32 i = PxArticulationAxis::eTWIST; i < PxArticulationAxis::eX; i++) + { + if (i != PxU32(axis) && this->mImpl.mJoint.getMotion(PxArticulationAxis::Enum(i)) != PxArticulationMotion::eLOCKED) + valid = false; + } + } + break; + } + case PxArticulationJointType::eSPHERICAL: + { + if (axis >= PxArticulationAxis::eX && motion != PxArticulationMotion::eLOCKED) + valid = false; + break; + } + case PxArticulationJointType::eFIX: + { + if (motion != PxArticulationMotion::eLOCKED) + valid = false; + break; + } + case PxArticulationJointType::eUNDEFINED: + { + valid = false; + break; + } + default: + break; + } + + return valid; + } + + /*bool NpArticulationJointReducedCoordinate::isValidType(PxArticulationJointType::Enum type) + { + bool hasPrismatic = (getMotion(PxArticulationAxis::eX) != PxArticulationMotion::eLOCKED) || + (getMotion(PxArticulationAxis::eY) != PxArticulationMotion::eLOCKED) || + (getMotion(PxArticulationAxis::eZ) != PxArticulationMotion::eLOCKED); + + bool hasRotation = (getMotion(PxArticulationAxis::eTWIST) != PxArticulationMotion::eLOCKED) || + (getMotion(PxArticulationAxis::eSWING1) != PxArticulationMotion::eLOCKED) || + (getMotion(PxArticulationAxis::eSWING2) != PxArticulationMotion::eLOCKED); + + if (type == PxArticulationJointType::eFIX) + return !hasPrismatic && !hasRotation; + if (type == PxArticulationJointType::eREVOLUTE || type == PxArticulationJointType::eSPHERICAL) + return !hasPrismatic; + if (type == PxArticulationJointType::ePRISMATIC) + return !hasRotation; + + return true; + }*/ +#endif + + void NpArticulationJointReducedCoordinate::setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) + { + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(getJointType() != PxArticulationJointType::eUNDEFINED, "PxArticulationJointReducedCoordinate::setMotion valid joint type(ePRISMATIC, eREVOLUTE, eSPHERICAL or eFIX) has to be set before setMotion"); + PX_CHECK_AND_RETURN(isValidMotion(axis, motion), "PxArticulationJointReducedCoordinate::setMotion illegal motion state requested."); + mImpl.getScbArticulationJoint().setMotion(axis, motion); + reinterpret_cast(getChild().getArticulation().getImpl())->increaseCacheVersion(); + } + + PxArticulationMotion::Enum NpArticulationJointReducedCoordinate::getMotion(PxArticulationAxis::Enum axis) const + { + NP_READ_CHECK(getOwnerScene()); + return mImpl.getScbArticulationJoint().getMotion(axis); + } + + void NpArticulationJointReducedCoordinate::setFrictionCoefficient(const PxReal coefficient) + { + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setFrictionCoefficient(coefficient); + } + + PxReal NpArticulationJointReducedCoordinate::getFrictionCoefficient() const + { + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getFrictionCoefficient(); + } + + void NpArticulationJointReducedCoordinate::setMaxJointVelocity(const PxReal maxJointV) + { + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setMaxJointVelocity(maxJointV); + } + + PxReal NpArticulationJointReducedCoordinate::getMaxJointVelocity() const + { + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getMaxJointVelocity(); + } + + void NpArticulationJointReducedCoordinate::setLimit(PxArticulationAxis::Enum axis, const PxReal lowLimit, const PxReal highLimit) + { + mImpl.getScbArticulationJoint().setLimit(axis, lowLimit, highLimit); + } + void NpArticulationJointReducedCoordinate::getLimit(PxArticulationAxis::Enum axis, PxReal& lowLimit, PxReal& highLimit) + { + mImpl.getScbArticulationJoint().getLimit(axis, lowLimit, highLimit); + } + void NpArticulationJointReducedCoordinate::setDrive(PxArticulationAxis::Enum axis, const PxReal stiffness, const PxReal damping, const PxReal maxForce, bool isAccelerationDrive) + { + mImpl.getScbArticulationJoint().setDrive(axis, stiffness, damping, maxForce, isAccelerationDrive); + } + void NpArticulationJointReducedCoordinate::getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration) + { + mImpl.getScbArticulationJoint().getDrive(axis, stiffness, damping, maxForce, isAcceleration); + } + void NpArticulationJointReducedCoordinate::setDriveTarget(PxArticulationAxis::Enum axis, const PxReal target) + { + mImpl.getScbArticulationJoint().setDriveTarget(axis, target); + } + void NpArticulationJointReducedCoordinate::setDriveVelocity(PxArticulationAxis::Enum axis, const PxReal targetVel) + { + mImpl.getScbArticulationJoint().setDriveVelocity(axis, targetVel); + } + PxReal NpArticulationJointReducedCoordinate::getDriveTarget(PxArticulationAxis::Enum axis) + { + return mImpl.getScbArticulationJoint().getDriveTarget(axis); + } + PxReal NpArticulationJointReducedCoordinate::getDriveVelocity(PxArticulationAxis::Enum axis) + { + return mImpl.getScbArticulationJoint().getDriveVelocity(axis); + } +} diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h new file mode 100644 index 000000000..f2b1d19cc --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h @@ -0,0 +1,108 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ARTICULATION_JOINT_RC +#define PX_PHYSICS_NP_ARTICULATION_JOINT_RC + +#include "PxArticulationJointReducedCoordinate.h" +#include "ScbArticulationJoint.h" +#include "NpArticulationTemplate.h" +#include "NpArticulationJoint.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +namespace physx +{ + + class NpArticulationJointReducedCoordinate : public NpArticulationJointTemplate + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + // PX_SERIALIZATION + NpArticulationJointReducedCoordinate(PxBaseFlags baseFlags) : NpArticulationJointTemplate(baseFlags) {} + virtual void resolveReferences(PxDeserializationContext& context) { mImpl.resolveReferences(context); } + static NpArticulationJointReducedCoordinate* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void exportExtraData(PxSerializationContext&) {} + void importExtraData(PxDeserializationContext&) {} + virtual void requiresObjects(PxProcessPxBaseCallback&) {} + virtual bool isSubordinate() const { return true; } + //~PX_SERIALIZATION + NpArticulationJointReducedCoordinate(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame); + + virtual ~NpArticulationJointReducedCoordinate(); + + //--------------------------------------------------------------------------------- + // PxArticulationJoint implementation + //--------------------------------------------------------------------------------- + // Save + + + virtual void setJointType(PxArticulationJointType::Enum jointType); + virtual PxArticulationJointType::Enum + getJointType() const; + + virtual void setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion); + virtual PxArticulationMotion::Enum + getMotion(PxArticulationAxis::Enum axis) const; + + virtual void setFrictionCoefficient(const PxReal coefficient); + virtual PxReal getFrictionCoefficient() const; + + virtual void setMaxJointVelocity(const PxReal maxJointV); + virtual PxReal getMaxJointVelocity() const; + + virtual void setLimit(PxArticulationAxis::Enum axis, const PxReal lowLimit, const PxReal highLimit); + virtual void getLimit(PxArticulationAxis::Enum axis, PxReal& lowLimit, PxReal& highLimit); + virtual void setDrive(PxArticulationAxis::Enum axis, const PxReal stiffness, const PxReal damping, const PxReal maxForce, bool isAccelerationDrive); + virtual void getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration); + virtual void setDriveTarget(PxArticulationAxis::Enum axis, const PxReal target); + virtual void setDriveVelocity(PxArticulationAxis::Enum axis, const PxReal targetVel); + virtual PxReal getDriveTarget(PxArticulationAxis::Enum axis); + virtual PxReal getDriveVelocity(PxArticulationAxis::Enum axis); +#if PX_CHECKED + private: + bool isValidMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion); +#endif + }; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulationLink.cpp b/src/PhysX/physx/source/physx/src/NpArticulationLink.cpp new file mode 100644 index 000000000..3d215a7a0 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationLink.cpp @@ -0,0 +1,620 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpArticulationLink.h" +#include "NpArticulationJoint.h" +#include "NpWriteCheck.h" +#include "NpReadCheck.h" +#include "ScbArticulation.h" +#include "CmVisualization.h" +#include "CmConeLimitHelper.h" +#include "CmUtils.h" +#include "NpArticulation.h" + +using namespace physx; + +// PX_SERIALIZATION +void NpArticulationLink::requiresObjects(PxProcessPxBaseCallback& c) +{ + NpArticulationLinkT::requiresObjects(c); + + if(mInboundJoint) + c.process(*mInboundJoint); +} + +void NpArticulationLink::exportExtraData(PxSerializationContext& stream) +{ + NpArticulationLinkT::exportExtraData(stream); + Cm::exportInlineArray(mChildLinks, stream); +} + +void NpArticulationLink::importExtraData(PxDeserializationContext& context) +{ + NpArticulationLinkT::importExtraData(context); + Cm::importInlineArray(mChildLinks, context); +} + +void NpArticulationLink::resolveReferences(PxDeserializationContext& context) +{ + context.translatePxBase(mRoot); + context.translatePxBase(mInboundJoint); + context.translatePxBase(mParent); + + NpArticulationLinkT::resolveReferences(context); + + const PxU32 nbLinks = mChildLinks.size(); + for(PxU32 i=0;iimportExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +NpArticulationLink::NpArticulationLink(const PxTransform& bodyPose, PxArticulationBase& root, NpArticulationLink* parent) +: NpArticulationLinkT(PxConcreteType::eARTICULATION_LINK, PxBaseFlag::eOWNS_MEMORY, PxActorType::eARTICULATION_LINK, bodyPose) +, mRoot(&root) +, mInboundJoint(NULL) +, mParent(parent) +, mLLIndex(0xffffffff) +, mInboundJointDof(0xffffffff) +{ + PX_ASSERT(mBody.getScbType() == ScbType::eBODY); + mBody.setScbType(ScbType::eBODY_FROM_ARTICULATION_LINK); + + if (parent) + parent->addToChildList(*this); +} + + +NpArticulationLink::~NpArticulationLink() +{ +} + +void NpArticulationLink::releaseInternal() +{ + NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, userData); + + NpArticulationLinkT::release(); + + PxArticulationImpl* impl = reinterpret_cast(mRoot->getImpl()); + impl->removeLinkFromList(*this); + + if (mParent) + mParent->removeFromChildList(*this); + + if (mInboundJoint) + mInboundJoint->release(); + + NpScene* npScene = NpActor::getAPIScene(*this); + if (npScene) + npScene->getScene().removeActor(mBody, true, false); + + mBody.destroy(); +} + + +void NpArticulationLink::release() +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + PxArticulationImpl* impl = reinterpret_cast(mRoot->getImpl()); + + PX_UNUSED(impl); + + if (impl->getRoot() == this && NpActor::getOwnerScene(*this) != NULL) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxArticulationLink::release(): root link may not be released while articulation is in a scene"); + return; + } + + //! this function doesn't get called when the articulation root is released + // therefore, put deregistration code etc. into dtor, not here + + if (mChildLinks.empty()) + { + releaseInternal(); + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxArticulationLink::release(): Only leaf articulation links can be released. Release call failed"); + } +} + + + +PxTransform NpArticulationLink::getGlobalPose() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + //!!!AL TODO: Need to start from root and compute along the branch to reflect double buffered state of root link + return getScbBodyFast().getBody2World() * getScbBodyFast().getBody2Actor().getInverse(); +} + +void NpArticulationLink::setLinearDamping(PxReal linearDamping) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(linearDamping), "NpArticulationLink::setLinearDamping: invalid float"); + PX_CHECK_AND_RETURN(linearDamping >= 0, "NpArticulationLink::setLinearDamping: The linear damping must be nonnegative!"); + + getScbBodyFast().setLinearDamping(linearDamping); +} + +PxReal NpArticulationLink::getLinearDamping() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getLinearDamping(); +} + +void NpArticulationLink::setAngularDamping(PxReal angularDamping) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(angularDamping), "NpArticulationLink::setAngularDamping: invalid float"); + PX_CHECK_AND_RETURN(angularDamping >= 0, "NpArticulationLink::setAngularDamping: The angular damping must be nonnegative!") + + getScbBodyFast().setAngularDamping(angularDamping); +} + +PxReal NpArticulationLink::getAngularDamping() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getAngularDamping(); +} + +PxArticulationBase& NpArticulationLink::getArticulation() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return *mRoot; +} + +PxArticulationReducedCoordinate& NpArticulationLink::getArticulationReducedCoordinate() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + //return *mRoot; + PxArticulationReducedCoordinate* ret = NULL; + return *ret; +} + + +PxArticulationJointBase* NpArticulationLink::getInboundJoint() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mInboundJoint; +} + +PxU32 NpArticulationLink::getInboundJointDof() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return mInboundJointDof; +} + +PxU32 NpArticulationLink::getNbChildren() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mChildLinks.size(); +} + + +PxU32 NpArticulationLink::getChildren(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mChildLinks.begin(), mChildLinks.size()); +} + +PxU32 NpArticulationLink::getLinkIndex() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mLLIndex; +} + + +void NpArticulationLink::setCMassLocalPose(const PxTransform& pose) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(pose.isSane(), "PxArticulationLink::setCMassLocalPose: invalid parameter"); + + PxTransform p = pose.getNormalized(); + PxTransform oldpose = getScbBodyFast().getBody2Actor(); + PxTransform comShift = p.transformInv(oldpose); + + NpArticulationLinkT::setCMassLocalPoseInternal(p); + + if(mInboundJoint) + { + Scb::ArticulationJoint &j = mInboundJoint->getImpl()->getScbArticulationJoint(); + j.setChildPose(comShift.transform(j.getChildPose())); + } + + for(PxU32 i=0; i(mChildLinks[i]->getInboundJoint())->getScbArticulationJoint(); + j.setParentPose(comShift.transform(j.getParentPose())); + } +} + + +void NpArticulationLink::addForce(const PxVec3& force, PxForceMode::Enum mode, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_UNUSED(scene); + + PX_CHECK_AND_RETURN(force.isFinite(), "NpArticulationLink::addForce: force is not valid."); + NP_WRITE_CHECK(scene); + PX_CHECK_AND_RETURN(scene, "NpArticulationLink::addForce: articulation link must be in a scene!"); + + addSpatialForce(&force, 0, mode); + + reinterpret_cast(mRoot->getImpl())->wakeUpInternal((!force.isZero()), autowake); +} + + +void NpArticulationLink::addTorque(const PxVec3& torque, PxForceMode::Enum mode, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_UNUSED(scene); + + PX_CHECK_AND_RETURN(torque.isFinite(), "NpArticulationLink::addTorque: force is not valid."); + NP_WRITE_CHECK(scene); + PX_CHECK_AND_RETURN(scene, "NpArticulationLink::addTorque: articulation link must be in a scene!"); + + addSpatialForce(0, &torque, mode); + + reinterpret_cast(mRoot->getImpl())->wakeUpInternal((!torque.isZero()), autowake); +} + +void NpArticulationLink::setForceAndTorque(const PxVec3& force, const PxVec3& torque, PxForceMode::Enum mode) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_UNUSED(scene); + + PX_CHECK_AND_RETURN(torque.isFinite(), "NpArticulationLink::setForceAndTorque: torque is not valid."); + PX_CHECK_AND_RETURN(force.isFinite(), "NpArticulationLink::setForceAndTorque: force is not valid."); + NP_WRITE_CHECK(scene); + PX_CHECK_AND_RETURN(scene, "NpArticulationLink::addTorque: articulation link must be in a scene!"); + + setSpatialForce(&force, &torque, mode); + + reinterpret_cast(mRoot->getImpl())->wakeUpInternal((!torque.isZero()), true); + +} + +void NpArticulationLink::clearForce(PxForceMode::Enum mode) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_UNUSED(scene); + NP_WRITE_CHECK(scene); + PX_CHECK_AND_RETURN(scene, "NpArticulationLink::clearForce: articulation link must be in a scene!"); + + clearSpatialForce(mode, true, false); +} + +void NpArticulationLink::clearTorque(PxForceMode::Enum mode) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_UNUSED(scene); + NP_WRITE_CHECK(scene); + PX_CHECK_AND_RETURN(scene, "NpArticulationLink::clearTorque: articulation link must be in a scene!"); + + clearSpatialForce(mode, false, true); +} + +void NpArticulationLink::setGlobalPoseInternal(const PxTransform& pose, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + + PX_CHECK_AND_RETURN(pose.isValid(), "NpArticulationLink::setGlobalPose pose is not valid."); + + NP_WRITE_CHECK(scene); + +#if PX_CHECKED + if (scene) + scene->checkPositionSanity(*this, pose, "PxArticulationLink::setGlobalPose"); +#endif + + PxTransform body2World = pose * getScbBodyFast().getBody2Actor(); + getScbBodyFast().setBody2World(body2World, false); + + if (scene && autowake) + reinterpret_cast(mRoot->getImpl())->wakeUpInternal(false, true); + + if (scene) + reinterpret_cast(mRoot->getImpl())->setGlobalPose(); +} + +void NpArticulationLink::setGlobalPose(const PxTransform& pose) +{ + // clow: no need to test inputs here, it's done in the setGlobalPose function already + setGlobalPose(pose, true); +} + +void NpArticulationLink::setGlobalPose(const PxTransform& pose, bool autowake) +{ + PX_CHECK_AND_RETURN(mRoot->getType() == PxArticulationBase::eMaximumCoordinate, "NpArticulationLink::setGlobalPose teleport isn't allowed in the reduced coordinate system."); + + setGlobalPoseInternal(pose, autowake); +} + + +void NpArticulationLink::setLinearVelocity(const PxVec3& velocity, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + + PX_CHECK_AND_RETURN(velocity.isFinite(), "NpArticulationLink::setLinearVelocity velocity is not valid."); + + NP_WRITE_CHECK(scene); + + getScbBodyFast().setLinearVelocity(velocity); + + if (scene) + reinterpret_cast(mRoot->getImpl())->wakeUpInternal((!velocity.isZero()), autowake); +} + + +void NpArticulationLink::setAngularVelocity(const PxVec3& velocity, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + + PX_CHECK_AND_RETURN(velocity.isFinite(), "NpArticulationLink::setAngularVelocity velocity is not valid."); + + NP_WRITE_CHECK(scene); + + getScbBodyFast().setAngularVelocity(velocity); + + if (scene) + reinterpret_cast(mRoot->getImpl())->wakeUpInternal((!velocity.isZero()), autowake); +} + +void NpArticulationLink::setMaxAngularVelocity(PxReal maxAngularVelocity) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(maxAngularVelocity), "NpArticulationLink::setMaxAngularVelocity: invalid float"); + PX_CHECK_AND_RETURN(maxAngularVelocity >= 0.0f, "NpArticulationLink::setMaxAngularVelocity: threshold must be non-negative!"); + + getScbBodyFast().setMaxAngVelSq(maxAngularVelocity * maxAngularVelocity); +} + +PxReal NpArticulationLink::getMaxAngularVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return PxSqrt(getScbBodyFast().getMaxAngVelSq()); +} + +void NpArticulationLink::setMaxLinearVelocity(PxReal maxLinearVelocity) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(maxLinearVelocity), "NpArticulationLink::setMaxAngularVelocity: invalid float"); + PX_CHECK_AND_RETURN(maxLinearVelocity >= 0.0f, "NpArticulationLink::setMaxAngularVelocity: threshold must be non-negative!"); + + getScbBodyFast().setMaxLinVelSq(maxLinearVelocity * maxLinearVelocity); +} + +PxReal NpArticulationLink::getMaxLinearVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return PxSqrt(getScbBodyFast().getMaxLinVelSq()); +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +void NpArticulationLink::visualize(Cm::RenderOutput& out, NpScene* scene) +{ + NpArticulationLinkT::visualize(out, scene); + + if (getScbBodyFast().getActorFlags() & PxActorFlag::eVISUALIZATION) + { + PX_ASSERT(getScene()); + PxReal scale = getScene()->getVisualizationParameter(PxVisualizationParameter::eSCALE); + + PxReal massAxes = scale * getScene()->getVisualizationParameter(PxVisualizationParameter::eBODY_MASS_AXES); + if (massAxes != 0) + { + PxU32 color = 0xff; + color = (color<<16 | color<<8 | color); + PxVec3 dims = invertDiagInertia(getScbBodyFast().getInverseInertia()); + dims = getDimsFromBodyInertia(dims, 1.0f / getScbBodyFast().getInverseMass()); + + out << color << getScbBodyFast().getBody2World() << Cm::DebugBox(dims * 0.5f); + } + PxReal frameScale = scale * getScene()->getVisualizationParameter(PxVisualizationParameter::eJOINT_LOCAL_FRAMES); + PxReal limitScale = scale * getScene()->getVisualizationParameter(PxVisualizationParameter::eJOINT_LIMITS); + if ( frameScale != 0.0f || limitScale != 0.0f ) + { + Cm::ConstraintImmediateVisualizer viz( frameScale, limitScale, out ); + visualizeJoint( viz ); + } + } +} + +static PX_FORCE_INLINE PxReal computePhi(const PxQuat& q) +{ + PxQuat twist = q; + twist.normalize(); + + PxReal angle = twist.getAngle(); + if (twist.x<0.0f) + angle = -angle; + return angle; +} + +static PX_FORCE_INLINE float computeSwingAngle(float swingYZ, float swingW) +{ + return 4.0f * PxAtan2(swingYZ, 1.0f + swingW); // tan (t/2) = sin(t)/(1+cos t), so this is the quarter angle +} + +static PX_FORCE_INLINE void separateSwingTwist(const PxQuat& q, PxQuat& twist, PxQuat& swing1, PxQuat& swing2) +{ + twist = q.x != 0.0f ? PxQuat(q.x, 0, 0, q.w).getNormalized() : PxQuat(PxIdentity); + PxQuat swing = q * twist.getConjugate(); + swing1 = swing.y != 0.f ? PxQuat(0.f, swing.y, 0.f, swing.w).getNormalized() : PxQuat(PxIdentity); + swing = swing * swing1.getConjugate(); + swing2 = swing.z != 0.f ? PxQuat(0.f, 0.f, swing.z, swing.w).getNormalized() : PxQuat(PxIdentity); +} + + +void NpArticulationLink::visualizeJoint(PxConstraintVisualizer& jointViz) +{ + NpArticulationLink* parent = getParent(); + if(parent) + { + PxTransform cA2w = getGlobalPose().transform(mInboundJoint->getChildPose()); + PxTransform cB2w = parent->getGlobalPose().transform(mInboundJoint->getParentPose()); + + jointViz.visualizeJointFrames(cA2w, cB2w); + + PxArticulationJointImpl* impl = mInboundJoint->getImpl(); + + if (impl->mType == PxArticulationBase::eMaximumCoordinate) + { + + PxTransform parentFrame = cB2w; + + if (cA2w.q.dot(cB2w.q) < 0) + cB2w.q = -cB2w.q; + + PxTransform cB2cA = cA2w.transformInv(cB2w); + + PxQuat swing, twist; + Ps::separateSwingTwist(cB2cA.q, swing, twist); + + PxMat33 cA2w_m(cA2w.q), cB2w_m(cB2w.q); + + PxReal tqPhi = Ps::tanHalf(twist.x, twist.w); // always support (-pi, +pi) + + PxReal lower, upper, yLimit, zLimit; + + impl->getScbArticulationJoint().getTwistLimit(lower, upper); + impl->getScbArticulationJoint().getSwingLimit(yLimit, zLimit); + PxReal swingPad = impl->getScbArticulationJoint().getSwingLimitContactDistance(), twistPad = impl->getScbArticulationJoint().getTwistLimitContactDistance(); + jointViz.visualizeAngularLimit(parentFrame, lower, upper, PxAbs(tqPhi) > PxTan(upper - twistPad)); + + PxVec3 tanQSwing = PxVec3(0, Ps::tanHalf(swing.z, swing.w), -Ps::tanHalf(swing.y, swing.w)); + Cm::ConeLimitHelper coneHelper(PxTan(yLimit / 4), PxTan(zLimit / 4), PxTan(swingPad / 4)); + jointViz.visualizeLimitCone(parentFrame, PxTan(yLimit / 4), PxTan(zLimit / 4), !coneHelper.contains(tanQSwing)); + } + else + { + //(1) visualize any angular dofs/limits... + + const PxMat33 cA2w_m(cA2w.q), cB2w_m(cB2w.q); + + PxTransform parentFrame = cB2w; + + if (cA2w.q.dot(cB2w.q) < 0) + cB2w.q = -cB2w.q; + + //const PxTransform cB2cA = cA2w.transformInv(cB2w); + + const PxTransform cA2cB = cB2w.transformInv(cA2w); + + Sc::ArticulationJointCore& joint = impl->getScbArticulationJoint().getScArticulationJoint(); + + PxQuat swing1, swing2, twist; + separateSwingTwist(cA2cB.q, twist, swing1, swing2); + + const PxReal pad = 0.01f; + + if(joint.getMotion(PxArticulationAxis::eTWIST)) + { + PxReal lowLimit, highLimit; + + const PxReal angle = computePhi(twist); + joint.getLimit(PxArticulationAxis::Enum(PxArticulationAxis::eTWIST), lowLimit, highLimit); + + bool active = (angle-pad) < lowLimit || (angle+pad) > highLimit; + + PxTransform tmp = parentFrame; + + jointViz.visualizeAngularLimit(tmp, lowLimit, highLimit, active); + } + + if (joint.getMotion(PxArticulationAxis::eSWING1)) + { + PxReal lowLimit, highLimit; + + joint.getLimit(PxArticulationAxis::Enum(PxArticulationAxis::eSWING1), lowLimit, highLimit); + + const PxReal angle = computeSwingAngle(swing1.y, swing1.w); + + bool active = (angle - pad) < lowLimit || (angle + pad) > highLimit; + + PxTransform tmp = parentFrame; + tmp.q = tmp.q * PxQuat(-PxPiDivTwo, PxVec3(0.f, 0.f, 1.f)); + + + jointViz.visualizeAngularLimit(tmp, -highLimit, -lowLimit, active); + } + + if (joint.getMotion(PxArticulationAxis::eSWING2)) + { + PxReal lowLimit, highLimit; + + joint.getLimit(PxArticulationAxis::Enum(PxArticulationAxis::eSWING2), lowLimit, highLimit); + + const PxReal angle = computeSwingAngle(swing2.z, swing2.w); + + bool active = (angle - pad) < lowLimit || (angle + pad) > highLimit; + + PxTransform tmp = parentFrame; + tmp.q = tmp.q * PxQuat(PxPiDivTwo, PxVec3(0.f, 1.f, 0.f)); + + jointViz.visualizeAngularLimit(tmp, -highLimit, -lowLimit, active); + } + + for (PxU32 i = PxArticulationAxis::eX; i <= PxArticulationAxis::eZ; ++i) + { + if (joint.getMotion(PxArticulationAxis::Enum(i)) == PxArticulationMotion::eLIMITED) + { + PxU32 index = i - PxArticulationAxis::eX; + PxReal lowLimit, highLimit; + joint.getLimit(PxArticulationAxis::Enum(i), lowLimit, highLimit); + PxReal ordinate = cA2cB.p[index]; + PxVec3 origin = cB2w.p; + PxVec3 axis = cA2w_m[index]; + const bool active = ordinate < lowLimit || ordinate > highLimit; + const PxVec3 p0 = origin + axis * lowLimit; + const PxVec3 p1 = origin + axis * highLimit; + jointViz.visualizeLine(p0, p1, active ? 0xff0000u : 0xffffffu); + } + } + } + } +} +#endif // PX_ENABLE_DEBUG_VISUALIZATION diff --git a/src/PhysX/physx/source/physx/src/NpArticulationLink.h b/src/PhysX/physx/source/physx/src/NpArticulationLink.h new file mode 100644 index 000000000..d525cb0c6 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationLink.h @@ -0,0 +1,177 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ARTICULATION_LINK +#define PX_PHYSICS_NP_ARTICULATION_LINK + +#include "NpRigidBodyTemplate.h" +#include "PxArticulationLink.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +namespace physx +{ + +class NpArticulation; +class NpArticulationLink; +class NpArticulationJoint; +class PxConstraintVisualizer; + +typedef NpRigidBodyTemplate NpArticulationLinkT; + +class NpArticulationLinkArray : public Ps::InlineArray //!!!AL TODO: check if default of 4 elements makes sense +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpArticulationLinkArray(const PxEMPTY) : Ps::InlineArray (PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + NpArticulationLinkArray() : Ps::InlineArray(PX_DEBUG_EXP("articulationLinkArray")) {} +}; + + + +class NpArticulationLink : public NpArticulationLinkT +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpArticulationLink(PxBaseFlags baseFlags) : NpArticulationLinkT(baseFlags), mChildLinks(PxEmpty) {} + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void registerReferences(PxSerializationContext& stream); + void resolveReferences(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback& c); + virtual bool isSubordinate() const { return true; } + static NpArticulationLink* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + virtual ~NpArticulationLink(); + + //--------------------------------------------------------------------------------- + // PxArticulationLink implementation + //--------------------------------------------------------------------------------- + virtual void release(); + + + virtual PxActorType::Enum getType() const { return PxActorType::eARTICULATION_LINK; } + + // Pose + virtual void setGlobalPose(const PxTransform& pose); + virtual void setGlobalPose(const PxTransform& pose, bool autowake); + virtual PxTransform getGlobalPose() const; + + //damping + virtual void setLinearDamping(PxReal linDamp); + virtual PxReal getLinearDamping() const; + + virtual void setAngularDamping(PxReal angDamp); + virtual PxReal getAngularDamping() const; + + // Velocity + virtual void setLinearVelocity(const PxVec3&, bool autowake = true); + virtual void setAngularVelocity(const PxVec3&, bool autowake = true); + virtual void setMaxAngularVelocity(PxReal); + virtual PxReal getMaxAngularVelocity() const; + virtual void setMaxLinearVelocity(PxReal); + virtual PxReal getMaxLinearVelocity() const; + + virtual PxArticulationBase& getArticulation() const; + + virtual PxArticulationReducedCoordinate& getArticulationReducedCoordinate() const; + + virtual PxArticulationJointBase* getInboundJoint() const; + virtual PxU32 getInboundJointDof() const; + + virtual PxU32 getNbChildren() const; + virtual PxU32 getChildren(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + virtual PxU32 getLinkIndex() const; + virtual void setCMassLocalPose(const PxTransform& pose); + + virtual void addForce(const PxVec3& force, PxForceMode::Enum mode = PxForceMode::eFORCE, bool autowake = true); + virtual void addTorque(const PxVec3& torque, PxForceMode::Enum mode = PxForceMode::eFORCE, bool autowake = true); + virtual void setForceAndTorque(const PxVec3& force, const PxVec3& torque, PxForceMode::Enum mode = PxForceMode::eFORCE); + virtual void clearForce(PxForceMode::Enum mode = PxForceMode::eFORCE); + virtual void clearTorque(PxForceMode::Enum mode = PxForceMode::eFORCE); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpArticulationLink(const PxTransform& bodyPose, PxArticulationBase& root, NpArticulationLink* parent); + + void releaseInternal(); + + PX_INLINE PxArticulationBase& getRoot() { return *mRoot; } + PX_INLINE NpArticulationLink* getParent() { return mParent; } + + PX_INLINE void setInboundJoint(PxArticulationJointBase& joint) { mInboundJoint = &joint; } + + void setGlobalPoseInternal(const PxTransform& pose, bool autowake); + void setLLIndex(const PxU32 index) { mLLIndex = index; } + void setInboundJointDof(const PxU32 index) { mInboundJointDof = index; } +private: + PX_INLINE void addToChildList(NpArticulationLink& link) { mChildLinks.pushBack(&link); } + PX_INLINE void removeFromChildList(NpArticulationLink& link) { PX_ASSERT(mChildLinks.find(&link) != mChildLinks.end()); mChildLinks.findAndReplaceWithLast(&link); } + +public: + PX_INLINE NpArticulationLink* const* getChildren() { return mChildLinks.empty() ? NULL : &mChildLinks.front(); } + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene); + void visualizeJoint(PxConstraintVisualizer& jointViz); +#endif + + +private: + PxArticulationBase* mRoot; //!!!AL TODO: Revisit: Could probably be avoided if registration and deregistration in root is handled differently + PxArticulationJointBase* mInboundJoint; + NpArticulationLink* mParent; //!!!AL TODO: Revisit: Some memory waste but makes things faster + NpArticulationLinkArray mChildLinks; + PxU32 mLLIndex; + PxU32 mInboundJointDof; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp new file mode 100644 index 000000000..765f89c0d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp @@ -0,0 +1,424 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpArticulationReducedCoordinate.h" + +#include "CmPhysXCommon.h" +#include "DyFeatherstoneArticulation.h" +#include "ScArticulationSim.h" +#include "ScConstraintSim.h" + +#include "PsAlignedMalloc.h" +#include "PsFoundation.h" +#include "PsPool.h" + +#include "PxPvdDataStream.h" +#include "extensions/PxJoint.h" + +namespace physx +{ + + class LLReducedArticulationPool : public Ps::Pool > + { + public: + LLReducedArticulationPool() {} + }; + + + NpArticulationReducedCoordinate::NpArticulationReducedCoordinate() + : NpArticulationTemplate(PxConcreteType::eARTICULATION, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) + { + PxArticulationBase::userData = NULL; + mType = PxArticulationBase::eReducedCoordinate; + mImpl.mArticulation.setArticulationType(PxArticulationBase::eReducedCoordinate); + } + + + void NpArticulationReducedCoordinate::setArticulationFlags(PxArticulationFlags flags) + { + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setArticulationFlags(flags); + } + + void NpArticulationReducedCoordinate::setArticulationFlag(PxArticulationFlag::Enum flag, bool value) + { + NP_WRITE_CHECK(mImpl.getOwnerScene()); + PxArticulationFlags flags = mImpl.getArticulation().getArticulationFlags(); + + if(value) + flags |= flag; + else + flags &= (~flag); + + mImpl.getArticulation().setArticulationFlags(flags); + } + + PxArticulationFlags NpArticulationReducedCoordinate::getArticulationFlags() const + { + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getArticulationFlags(); + } + + PxU32 NpArticulationReducedCoordinate::getDofs() const + { + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.mArticulation.getScArticulation().getDofs(); + } + + PxArticulationCache* NpArticulationReducedCoordinate::createCache() const + { + PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::createCache: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + PxArticulationCache* cache = mImpl.mArticulation.getScArticulation().createCache(); + cache->version = mImpl.mCacheVersion; + + return cache; + } + + PxU32 NpArticulationReducedCoordinate::getCacheDataSize() const + { + PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::getCacheDataSize: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + return mImpl.mArticulation.getScArticulation().getCacheDataSize(); + } + + void NpArticulationReducedCoordinate::zeroCache(PxArticulationCache& cache) + { + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + return mImpl.mArticulation.getScArticulation().zeroCache(cache); + } + + void NpArticulationReducedCoordinate::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake) + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::applyCache: object must be in a scene"); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::applyCache : cache is invalid, articulation configuration has changed! ") + + //if we try to do a bulk op when sim is running, return with error + if (static_cast(getScene())->getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "NpArticulation::applyCache() not allowed while simulation is running."); + return; + } + + mImpl.mArticulation.getScArticulation().applyCache(cache, flag); + + if (flag & PxArticulationCache::ePOSITION) + { + const PxU32 linkCount = mImpl.mArticulationLinks.size(); + + for (PxU32 i = 0; i < linkCount; ++i) + { + NpArticulationLink* link = mImpl.mArticulationLinks[i]; + //in the lowlevel articulation, we have already updated bodyCore's body2World + const PxTransform internalPose = link->getScbBodyFast().getScBody().getBody2World(); + link->getScbBodyFast().setBody2World(internalPose, false); + } + } + + mImpl.wakeUpInternal(false, autowake); + + } + + void NpArticulationReducedCoordinate::copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::copyInternalStateToCache: object must be in a scene"); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::applyCache : cache is invalid, articulation configuration has changed! ") + + mImpl.mArticulation.getScArticulation().copyInternalStateToCache(cache, flag); + } + + void NpArticulationReducedCoordinate::releaseCache(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::releaseCache: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + mImpl.mArticulation.getScArticulation().releaseCache(cache); + } + + void NpArticulationReducedCoordinate::packJointData(const PxReal* maximum, PxReal* reduced) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::packJointData: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + mImpl.mArticulation.getScArticulation().packJointData(maximum, reduced); + } + + void NpArticulationReducedCoordinate::unpackJointData(const PxReal* reduced, PxReal* maximum) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::unpackJointData: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + mImpl.mArticulation.getScArticulation().unpackJointData(reduced, maximum); + } + + void NpArticulationReducedCoordinate::commonInit() const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::commonInit: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + mImpl.mArticulation.getScArticulation().commonInit(); + } + + void NpArticulationReducedCoordinate::computeGeneralizedGravityForce(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralisedGravityForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralisedGravityForce : cache is invalid, articulation configuration has changed! "); + + + mImpl.mArticulation.getScArticulation().computeGeneralizedGravityForce(cache); + } + + void NpArticulationReducedCoordinate::computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeCoriolisAndCentrifugalForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeCoriolisAndCentrifugalForce : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeCoriolisAndCentrifugalForce(cache); + } + + void NpArticulationReducedCoordinate::computeGeneralizedExternalForce(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralizedExternalForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralizedExternalForce : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeGeneralizedExternalForce(cache); + } + + void NpArticulationReducedCoordinate::computeJointAcceleration(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeJointAcceleration: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeJointAcceleration : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeJointAcceleration(cache); + } + + void NpArticulationReducedCoordinate::computeJointForce(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeJointForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeJointForce : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeJointForce(cache); + } + + void NpArticulationReducedCoordinate::computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeKenematicJacobian: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeKenematicJacobian : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeKinematicJacobian(linkID, cache); + } + + void NpArticulationReducedCoordinate::computeCoefficentMatrix(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeCoefficentMatrix: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeCoefficentMatrix : cache is invalid, articulation configuration has changed! "); + + for (PxU32 i = 0; i < mLoopJoints.size(); ++i) + { + static_cast(mLoopJoints[i]->getConstraint())->updateConstants(); + } + + mImpl.mArticulation.getScArticulation().computeCoefficentMatrix(cache); + } + + bool NpArticulationReducedCoordinate::computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxU32 maxIter) const + { + + if (!mImpl.getAPIScene()) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxArticulation::computeLambda : object must be in a scened!"); + return false; + } + + NP_READ_CHECK(mImpl.getOwnerScene()); + + if (cache.version != mImpl.mCacheVersion) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxArticulation::computeLambda : cache is invalid, articulation configuration has changed!"); + return false; + } + + return mImpl.mArticulation.getScArticulation().computeLambda(cache, initialState, jointTorque, getScene()->getGravity(), maxIter); + } + + void NpArticulationReducedCoordinate::computeGeneralizedMassMatrix(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralizedMassMatrix: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralizedMassMatrix : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeGeneralizedMassMatrix(cache); + } + + void NpArticulationReducedCoordinate::addLoopJoint(PxJoint* joint) + { + + NP_WRITE_CHECK(mImpl.getOwnerScene()); + +#if PX_CHECKED + + PxRigidActor* actor0; + PxRigidActor* actor1; + + joint->getActors(actor0, actor1); + + PxArticulationLink* link0 = NULL; + PxArticulationLink* link1 = NULL; + + if(actor0) + link0 = actor0->is(); + + if(actor1) + link1 = actor1->is(); + + PX_CHECK_AND_RETURN((link0 || link1), "PxArticulation::addLoopJoint : at least one of the PxRigidActors need to be PxArticulationLink! "); + + + PxArticulationBase* base0 = NULL; + PxArticulationBase* base1 = NULL; + if (link0) + base0 = &link0->getArticulation(); + + if (link1) + base1 = &link1->getArticulation(); + + PX_CHECK_AND_RETURN((base0 == this || base1 == this), "PxArticulation::addLoopJoint : at least one of the PxArticulationLink belongs to this articulation! "); + + +#endif + + + const PxU32 size = mLoopJoints.size(); + if (size >= mLoopJoints.capacity()) + { + mLoopJoints.reserve(size * 2 + 1); + } + + mLoopJoints.pushBack(joint); + + Scb::Articulation& scbArt = mImpl.getArticulation(); + Sc::ArticulationSim* scArtSim = scbArt.getScArticulation().getSim(); + + NpConstraint* constraint = static_cast(joint->getConstraint()); + Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); + if(scArtSim) + scArtSim->addLoopConstraint(cSim); + } + + void NpArticulationReducedCoordinate::removeLoopJoint(PxJoint* joint) + { + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + mLoopJoints.findAndReplaceWithLast(joint); + + Scb::Articulation& scbArt = mImpl.getArticulation(); + Sc::ArticulationSim* scArtSim = scbArt.getScArticulation().getSim(); + + NpConstraint* constraint = static_cast(joint->getConstraint()); + Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); + scArtSim->removeLoopConstraint(cSim); + } + + + PxU32 NpArticulationReducedCoordinate::getNbLoopJoints() const + { + NP_READ_CHECK(mImpl.getOwnerScene()); + + return mLoopJoints.size(); + } + + PxU32 NpArticulationReducedCoordinate::getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex) const + { + NP_READ_CHECK(mImpl.getOwnerScene()); + + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mLoopJoints.begin(), mLoopJoints.size()); + } + + PxU32 NpArticulationReducedCoordinate::getCoefficentMatrixSize() const + { + NP_READ_CHECK(mImpl.getOwnerScene()); + + return mImpl.mArticulation.getScArticulation().getCoefficentMatrixSize(); + } + + void NpArticulationReducedCoordinate::teleportRootLink(const PxTransform& pose, bool autowake) + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulationReducedCoordinate::teleportRootLink: object must be in a scene"); + + PX_CHECK_AND_RETURN(pose.isValid(), "PxArticulationReducedCoordinate::teleportRootLink pose is not valid."); + + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + NpArticulationLink* root = mImpl.mArticulationLinks[0]; + + root->setGlobalPoseInternal(pose, autowake); + } + + + NpArticulationReducedCoordinate::~NpArticulationReducedCoordinate() + { + NpFactory::getInstance().onArticulationRelease(this); + } + + PxArticulationJointBase* NpArticulationReducedCoordinate::createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame) + { + + return NpFactory::getInstance().createNpArticulationJointRC(static_cast(parent), parentFrame, static_cast(child), childFrame); + } + + void NpArticulationReducedCoordinate::releaseArticulationJoint(PxArticulationJointBase* joint) + { + NpFactory::getInstance().releaseArticulationJointRCToPool(*static_cast(joint)); + } + +} + diff --git a/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h new file mode 100644 index 000000000..718e31fa1 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h @@ -0,0 +1,169 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ARTICULATION_RC +#define PX_PHYSICS_NP_ARTICULATION_RC + +#include "PxArticulationReducedCoordinate.h" +#include "CmPhysXCommon.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +#include "ScbArticulation.h" +#include "NpArticulationLink.h" +#include "NpArticulationTemplate.h" +#include "NpArticulationJointReducedCoordinate.h" + +namespace physx +{ + + class NpArticulationLink; + class NpScene; + class NpAggregate; + class PxAggregate; + class PxJoint; + + class NpArticulationReducedCoordinate : public NpArticulationTemplate + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + virtual ~NpArticulationReducedCoordinate(); + + //KS - todo - re-enable serialization later... + // PX_SERIALIZATION + NpArticulationReducedCoordinate(PxBaseFlags baseFlags) : NpArticulationTemplate(baseFlags) + { + mType = PxArticulationBase::eReducedCoordinate; + } + + /*virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + virtual void requires(PxProcessPxBaseCallback& c); + static NpArticulation* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream);*/ + //~PX_SERIALIZATION + + //--------------------------------------------------------------------------------- + // PxArticulation implementation + //--------------------------------------------------------------------------------- + + //--------------------------------------------------------------------------------- + // PxArticulationReducedCoordinate implementation + //--------------------------------------------------------------------------------- + virtual void setArticulationFlags(PxArticulationFlags flags); + + virtual void setArticulationFlag(PxArticulationFlag::Enum flag, bool value); + + virtual PxArticulationFlags getArticulationFlags() const; + + virtual PxU32 getDofs() const; + + virtual PxArticulationCache* createCache() const; + + virtual PxU32 getCacheDataSize() const; + + virtual void zeroCache(PxArticulationCache& cache); + + virtual void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake); + + virtual void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const; + + virtual void releaseCache(PxArticulationCache& cache) const; + + virtual void packJointData(const PxReal* maximum, PxReal* reduced) const; + + virtual void unpackJointData(const PxReal* reduced, PxReal* maximum) const; + + virtual void commonInit() const; + + virtual void computeGeneralizedGravityForce(PxArticulationCache& cache) const; + + virtual void computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const; + + virtual void computeGeneralizedExternalForce(PxArticulationCache& cache) const; + + virtual void computeJointAcceleration(PxArticulationCache& cache) const; + + virtual void computeJointForce(PxArticulationCache& cache) const; + + virtual void computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const; + + virtual void computeCoefficentMatrix(PxArticulationCache& cache) const; + + virtual bool computeLambda(PxArticulationCache& cache, PxArticulationCache& rollBackCache, const PxReal* const jointTorque, const PxU32 maxIter) const; + + virtual void computeGeneralizedMassMatrix(PxArticulationCache& cache) const; + + virtual void addLoopJoint(PxJoint* joint); + + virtual void removeLoopJoint(PxJoint* constraint); + + virtual PxU32 getNbLoopJoints() const; + + virtual PxU32 getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex = 0) const; + + virtual PxU32 getCoefficentMatrixSize() const; + + virtual void teleportRootLink(const PxTransform& pose, bool autowake); + + virtual const char* getConcreteTypeName() const { return "PxArticulation"; } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpArticulationReducedCoordinate(); + + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulation", name) || PxBase::isKindOf(name); } + + virtual PxArticulationJointBase* createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame); + virtual void releaseArticulationJoint(PxArticulationJointBase* joint); + + private: + + Ps::Array mLoopJoints; + + friend class NpScene; + }; + + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulationTemplate.h b/src/PhysX/physx/source/physx/src/NpArticulationTemplate.h new file mode 100644 index 000000000..fbe4dc68b --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationTemplate.h @@ -0,0 +1,568 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_ARTICULATION_TEMPLATE +#define NP_ARTICULATION_TEMPLATE + +#include "CmPhysXCommon.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +#include "ScbArticulation.h" +#include "NpArticulationLink.h" +#include "NpAggregate.h" + +namespace physx +{ + +class NpArticulationLink; +class NpScene; +class NpAggregate; +class PxAggregate; + +class PxArticulationImpl +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + ~PxArticulationImpl() + { + } + + PxArticulationImpl() : + mAggregate(NULL), mName(NULL), mCacheVersion(0) {} + + // PX_SERIALIZATION + PxArticulationImpl(const PxEMPTY) : mArticulation(PxEmpty), mArticulationLinks(PxEmpty) {} + //~PX_SERIALIZATION + + + PX_INLINE PxScene* getScene() const; + + PX_INLINE void setSleepThreshold(PxReal threshold); + PX_INLINE PxReal getSleepThreshold() const; + + PX_INLINE void setStabilizationThreshold(PxReal threshold); + PX_INLINE PxReal getStabilizationThreshold() const; + + PX_INLINE void setWakeCounter(PxReal wakeCounterValue); + PX_INLINE PxReal getWakeCounter() const; + + PX_INLINE void setSolverIterationCounts(PxU32 positionIters, PxU32 velocityIters); + PX_INLINE void getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const; + + PX_INLINE bool isSleeping() const; + PX_INLINE void wakeUp(); + PX_INLINE void putToSleep(); + + PX_INLINE PxU32 getNbLinks() const; + PX_INLINE PxU32 getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + PX_INLINE PxBounds3 getWorldBounds(float inflation = 1.01f) const; + + PX_INLINE PxAggregate* getAggregate() const; + + // Debug name + PX_INLINE void setName(const char*); + PX_INLINE const char* getName() const; + + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + + PX_INLINE void addToLinkList(NpArticulationLink& link) { mArticulationLinks.pushBack(&link); } + PX_INLINE bool removeLinkFromList(NpArticulationLink& link) { PX_ASSERT(mArticulationLinks.find(&link) != mArticulationLinks.end()); return mArticulationLinks.findAndReplaceWithLast(&link); } + PX_FORCE_INLINE NpArticulationLink* const* getLinks() { return &mArticulationLinks.front(); } + + PX_INLINE NpArticulationLink* getRoot(); + + PX_FORCE_INLINE const Scb::Articulation& getArticulation() const { return mArticulation; } + PX_FORCE_INLINE Scb::Articulation& getArticulation() { return mArticulation; } + + PX_INLINE NpScene* getAPIScene() const; + PX_INLINE NpScene* getOwnerScene() const; // the scene the user thinks the actor is in, or from which the actor is pending removal + + PX_FORCE_INLINE void setAggregate(PxAggregate* a) { mAggregate = static_cast(a); } + + PX_INLINE void wakeUpInternal(bool forceWakeUp, bool autowake); + + PX_INLINE void setGlobalPose(); + + PX_FORCE_INLINE Scb::Articulation& getScbArticulation() { return mArticulation; } + PX_FORCE_INLINE const Scb::Articulation& getScbArticulation() const { return mArticulation; } + + void increaseCacheVersion() { mCacheVersion++; } + + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + PX_INLINE void visualize(Cm::RenderOutput& out, NpScene* scene); +#endif + + + Scb::Articulation mArticulation; + NpArticulationLinkArray mArticulationLinks; + NpAggregate* mAggregate; + const char* mName; + PxU32 mCacheVersion; +}; + +template +class NpArticulationTemplate : public APIClass, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + virtual ~NpArticulationTemplate(); + NpArticulationTemplate(PxType concreteType, PxBaseFlags baseFlags) : APIClass(concreteType, baseFlags) {} + + // PX_SERIALIZATION + NpArticulationTemplate(PxBaseFlags baseFlags) : APIClass(baseFlags), mImpl(PxEmpty) {} + //~PX_SERIALIZATION + + + virtual void release(); + + virtual PxScene* getScene() const { return mImpl.getScene(); } + + virtual void setSleepThreshold(PxReal threshold) { mImpl.setSleepThreshold(threshold); } + virtual PxReal getSleepThreshold() const { return mImpl.getSleepThreshold();} + + virtual void setStabilizationThreshold(PxReal threshold) { mImpl.setStabilizationThreshold(threshold); } + virtual PxReal getStabilizationThreshold() const { return mImpl.getStabilizationThreshold(); } + + virtual void setWakeCounter(PxReal wakeCounterValue) { mImpl.setWakeCounter(wakeCounterValue); } + virtual PxReal getWakeCounter() const {return mImpl.getWakeCounter(); } + + virtual void setSolverIterationCounts(PxU32 positionIters, PxU32 velocityIters) { mImpl.setSolverIterationCounts(positionIters, velocityIters); } + virtual void getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const { mImpl.getSolverIterationCounts(positionIters, velocityIters); } + + virtual bool isSleeping() const { return mImpl.isSleeping(); } + virtual void wakeUp() { mImpl.wakeUp(); } + virtual void putToSleep() { mImpl.putToSleep(); } + + virtual PxU32 getNbLinks() const { return mImpl.getNbLinks(); } + virtual PxU32 getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const + { + return mImpl.getLinks(userBuffer, bufferSize, startIndex); + } + + virtual PxBounds3 getWorldBounds(float inflation = 1.01f) const { return mImpl.getWorldBounds(inflation); } + + virtual PxAggregate* getAggregate() const { return mImpl.getAggregate(); } + + // Debug name + virtual void setName(const char* name) { return mImpl.setName(name); } + virtual const char* getName() const { return mImpl.getName(); } + + virtual PxArticulationLink* createLink(PxArticulationLink* parent, const PxTransform& pose); + + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpArticulationTemplate(); + + virtual const PxArticulationImpl* getImpl() const { return &mImpl; } + + virtual PxArticulationImpl* getImpl() { return &mImpl; } + + virtual PxArticulationBase::Enum getType() const { return PxArticulationBase::Enum(mType); } + + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene) { mImpl.visualize(out, scene); } +#endif + +public: + + PxU32 mType; + PxArticulationImpl mImpl; + + +}; + + +template +NpArticulationTemplate::NpArticulationTemplate() + : APIClass(PxConcreteType::eARTICULATION, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +{ + PxArticulationBase::userData = NULL; + mType = PxArticulationBase::eMaximumCoordinate; +} + +template +NpArticulationTemplate::~NpArticulationTemplate() +{ + NpFactory::getInstance().onArticulationRelease(this); +} + +template +void NpArticulationTemplate::release() +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, PxArticulationBase::userData); + + //!!!AL TODO: Order should not matter in this case. Optimize by having a path which does not restrict release to leaf links or + // by using a more advanced data structure + PxU32 idx = 0; + while (mImpl.mArticulationLinks.size()) + { + idx = idx % mImpl.mArticulationLinks.size(); + + if (mImpl.mArticulationLinks[idx]->getNbChildren() == 0) + { + mImpl.mArticulationLinks[idx]->releaseInternal(); // deletes joint, link and removes link from list + } + else + { + idx++; + } + } + + NpScene* npScene = mImpl.getAPIScene(); + if (npScene) + { + npScene->getScene().removeArticulation(mImpl.getArticulation()); + + npScene->removeFromArticulationList(*this); + } + + mImpl.mArticulationLinks.clear(); + + mImpl.mArticulation.destroy(); +} + +template +PxArticulationLink* NpArticulationTemplate::createLink(PxArticulationLink* parent, const PxTransform& pose) +{ + PX_CHECK_AND_RETURN_NULL(pose.isSane(), "NpArticulation::createLink pose is not valid."); + PX_CHECK_AND_RETURN_NULL(mImpl.mArticulationLinks.size()<64, "NpArticulation::createLink: at most 64 links allowed in an articulation"); + + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + if (parent && mImpl.mArticulationLinks.empty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Root articulation link must have NULL parent pointer!"); + return NULL; + } + + if (!parent && !mImpl.mArticulationLinks.empty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Non-root articulation link must have valid parent pointer!"); + return NULL; + } + + //increase cache version + mImpl.mCacheVersion++; + + NpArticulationLink* parentLink = static_cast(parent); + + NpArticulationLink* link = static_cast(NpFactory::getInstance().createArticulationLink(*this, parentLink, pose.getNormalized())); + + if (link) + { + NpScene* scene = mImpl.getAPIScene(); + if (scene) + scene->addArticulationLink(*link); + + mImpl.addToLinkList(*link); + } + return link; +} + +PxScene* PxArticulationImpl::getScene() const +{ + return getAPIScene(); +} + +void PxArticulationImpl::setSolverIterationCounts(PxU32 positionIters, PxU32 velocityIters) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(positionIters > 0, "Articulation::setSolverIterationCount: positionIters must be more than zero!"); + PX_CHECK_AND_RETURN(positionIters <= 255, "Articulation::setSolverIterationCount: positionIters must be no greater than 255!"); + PX_CHECK_AND_RETURN(velocityIters > 0, "Articulation::setSolverIterationCount: velocityIters must be more than zero!"); + PX_CHECK_AND_RETURN(velocityIters <= 255, "Articulation::setSolverIterationCount: velocityIters must be no greater than 255!"); + + getArticulation().setSolverIterationCounts((velocityIters & 0xff) << 8 | (positionIters & 0xff)); +} + + +void PxArticulationImpl::getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const +{ + NP_READ_CHECK(getOwnerScene()); + PxU16 x = getArticulation().getSolverIterationCounts(); + velocityIters = PxU32(x >> 8); + positionIters = PxU32(x & 0xff); +} + + +void PxArticulationImpl::setGlobalPose() +{ + PX_CHECK_AND_RETURN(getAPIScene(), "PxArticulation::setGlobalPose: object must be in a scene"); + NP_READ_CHECK(getOwnerScene()); + + mArticulation.setGlobalPose(); + + //This code is force PVD to update other links position + if (!mArticulation.isBuffering()) + { + physx::NpArticulationLink*const* links = getLinks(); + + const PxU32 nbLinks = getNbLinks(); + for (PxU32 i = 1; i < nbLinks; ++i) + { + physx::NpArticulationLink* link = links[i]; + //in the lowlevel articulation, we have already updated bodyCore's body2World + const PxTransform internalPose = link->getScbBodyFast().getScBody().getBody2World(); + link->getScbBodyFast().setBody2World(internalPose, false); + } + } +} + + +bool PxArticulationImpl::isSleeping() const +{ + NP_READ_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN_VAL(getAPIScene(), "Articulation::isSleeping: articulation must be in a scene.", true); + + return getArticulation().isSleeping(); +} + + +void PxArticulationImpl::setSleepThreshold(PxReal threshold) +{ + NP_WRITE_CHECK(getOwnerScene()); + getArticulation().setSleepThreshold(threshold); +} + + +PxReal PxArticulationImpl::getSleepThreshold() const +{ + NP_READ_CHECK(getOwnerScene()); + return getArticulation().getSleepThreshold(); +} + + +void PxArticulationImpl::setStabilizationThreshold(PxReal threshold) +{ + NP_WRITE_CHECK(getOwnerScene()); + getArticulation().setFreezeThreshold(threshold); +} + + +PxReal PxArticulationImpl::getStabilizationThreshold() const +{ + NP_READ_CHECK(getOwnerScene()); + return getArticulation().getFreezeThreshold(); +} + + + +void PxArticulationImpl::setWakeCounter(PxReal wakeCounterValue) +{ + NP_WRITE_CHECK(getOwnerScene()); + + for (PxU32 i = 0; i < mArticulationLinks.size(); i++) + { + mArticulationLinks[i]->getScbBodyFast().setWakeCounter(wakeCounterValue); + } + + getArticulation().setWakeCounter(wakeCounterValue); +} + + +PxReal PxArticulationImpl::getWakeCounter() const +{ + NP_READ_CHECK(getOwnerScene()); + return getArticulation().getWakeCounter(); +} + + +void PxArticulationImpl::wakeUpInternal(bool forceWakeUp, bool autowake) +{ + NpScene* scene = getAPIScene(); + PX_ASSERT(scene); + PxReal wakeCounterResetValue = scene->getWakeCounterResetValueInteral(); + + Scb::Articulation& a = getArticulation(); + PxReal wakeCounter = a.getWakeCounter(); + + bool needsWakingUp = isSleeping() && (autowake || forceWakeUp); + if (autowake && (wakeCounter < wakeCounterResetValue)) + { + wakeCounter = wakeCounterResetValue; + needsWakingUp = true; + } + + if (needsWakingUp) + { + for (PxU32 i = 0; i < mArticulationLinks.size(); i++) + { + mArticulationLinks[i]->getScbBodyFast().wakeUpInternal(wakeCounter); + } + + a.wakeUpInternal(wakeCounter); + } +} + + +void PxArticulationImpl::wakeUp() +{ + NpScene* scene = getAPIScene(); + + NP_WRITE_CHECK(getOwnerScene()); // don't use the API scene, since it will be NULL on pending removal + PX_CHECK_AND_RETURN(scene, "Articulation::wakeUp: articulation must be in a scene."); + + for (PxU32 i = 0; i < mArticulationLinks.size(); i++) + { + mArticulationLinks[i]->getScbBodyFast().wakeUpInternal(scene->getWakeCounterResetValueInteral()); + } + + getArticulation().wakeUp(); +} + + +void PxArticulationImpl::putToSleep() +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(getAPIScene(), "Articulation::putToSleep: articulation must be in a scene."); + + for (PxU32 i = 0; i < mArticulationLinks.size(); i++) + { + mArticulationLinks[i]->getScbBodyFast().putToSleepInternal(); + } + + getArticulation().putToSleep(); +} + + +PxU32 PxArticulationImpl::getNbLinks() const +{ + NP_READ_CHECK(getOwnerScene()); + return mArticulationLinks.size(); +} + + +PxU32 PxArticulationImpl::getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(getOwnerScene()); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mArticulationLinks.begin(), mArticulationLinks.size()); +} + + +PxBounds3 PxArticulationImpl::getWorldBounds(float inflation) const +{ + NP_READ_CHECK(getOwnerScene()); + PxBounds3 bounds = PxBounds3::empty(); + + for (PxU32 i = 0; i < mArticulationLinks.size(); i++) + { + bounds.include(mArticulationLinks[i]->getWorldBounds()); + } + PX_ASSERT(bounds.isValid()); + + // PT: unfortunately we can't just scale the min/max vectors, we need to go through center/extents. + const PxVec3 center = bounds.getCenter(); + const PxVec3 inflatedExtents = bounds.getExtents() * inflation; + return PxBounds3::centerExtents(center, inflatedExtents); +} + + +PxAggregate* PxArticulationImpl::getAggregate() const +{ + NP_READ_CHECK(getOwnerScene()); + return mAggregate; +} + + +void PxArticulationImpl::setName(const char* debugName) +{ + NP_WRITE_CHECK(getOwnerScene()); + mName = debugName; +} + + +const char* PxArticulationImpl::getName() const +{ + NP_READ_CHECK(getOwnerScene()); + return mName; +} + + +NpScene* PxArticulationImpl::getAPIScene() const +{ + return static_cast(mArticulation.getScbSceneForAPI() ? mArticulation.getScbSceneForAPI()->getPxScene() : NULL); +} + + +NpScene* PxArticulationImpl::getOwnerScene() const +{ + return static_cast(mArticulation.getScbScene() ? mArticulation.getScbScene()->getPxScene() : NULL); +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION + +void PxArticulationImpl::visualize(Cm::RenderOutput& out, NpScene* scene) +{ + for (PxU32 i = 0; ivisualize(out, scene); +} +#endif // PX_ENABLE_DEBUG_VISUALIZATION + + +NpArticulationLink* PxArticulationImpl::getRoot() +{ + if (!mArticulationLinks.size()) + return NULL; + + PX_ASSERT(mArticulationLinks[0]->getInboundJoint() == NULL); + return mArticulationLinks[0]; +} + +} + +#endif //NP_ARTICULATION_TEMPLATE + diff --git a/src/PhysX/physx/source/physx/src/NpBatchQuery.cpp b/src/PhysX/physx/source/physx/src/NpBatchQuery.cpp new file mode 100644 index 000000000..b31ea85a9 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpBatchQuery.cpp @@ -0,0 +1,603 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpBatchQuery.h" +#include "NpReadCheck.h" +#include "NpActor.h" +#include "NpShapeManager.h" +#include "PsAtomic.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "NpScene.h" +#include "PxGeometryQuery.h" +#include "common/PxProfileZone.h" + +using namespace physx; +using namespace Sq; +using namespace Cm; + +#if !PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxRaycastHit)& 0x0f)); +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxSweepHit)& 0x0f)); +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxOverlapHit)& 0x0f)); +#endif + +#define CHECK_RUNNING(QueryMessage) \ + if(Ps::atomicCompareExchange(&mBatchQueryIsRunning, -1, 0) == 1)\ + {\ + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, QueryMessage); return;\ + } + +NpBatchQuery::NpBatchQuery(NpScene& owner, const PxBatchQueryDesc& d) + : mNpScene(&owner), mNbRaycasts(0), mNbOverlaps(0), mNbSweeps(0), mBatchQueryIsRunning(0), mDesc(d), mPrevOffset(PxU32(eTERMINAL)) +{ + mHasMtdSweep = false; +} + +NpBatchQuery::~NpBatchQuery() +{ +} + +void NpBatchQuery::setUserMemory(const PxBatchQueryMemory& userMem) +{ + if(Ps::atomicCompareExchange(&mBatchQueryIsRunning, 0, 0) != 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxBatchQuery::setUserMemory: This batch is still executing, skipping setUserMemory"); + return; + } + + PxBatchQueryDesc& desc = getDesc(); + + desc.queryMemory = userMem; +} + +const PxBatchQueryMemory& NpBatchQuery::getUserMemory() +{ + return getDesc().queryMemory; +} + +// ROS abbreviates Raycast/Overlap/Sweep +struct QTypeROS { enum Enum { eRAYCAST = 0, eOVERLAP = 1, eSWEEP = 2 }; }; // AP: perhaps can be shared with some other code + +namespace physx +{ +struct BatchStreamHeader +{ + BatchStreamHeader( + PxHitFlags aHitFlags, const PxQueryCache* aCache, const PxQueryFilterData& aFd, + void* aUserData, PxU16 aMaxTouchHits, QTypeROS::Enum aHitTypeId) : + hitFlags(aHitFlags), fd(aFd), userData(aUserData), cache(aCache), + maxTouchHits(aMaxTouchHits), hitTypeId(char(aHitTypeId)) + { + nextQueryOffset = PxU32(NpBatchQuery::eTERMINAL); + } + + BatchStreamHeader() {} + + // TODO: possibly maintain 3 separate offset lists in the same array for raycasts/overlaps/sweeps + // offset of a 4-byte ptr to offset in the previously stored batch query in the stream, this is not a ptr because of reallocs + PxU32 nextQueryOffset; + PxHitFlags hitFlags; + PxQueryFilterData fd; + void* userData; + const PxQueryCache* cache; + PxU16 maxTouchHits; + char hitTypeId; // Sq::QTypeROS +}; +} // namespace physx + +static void writeGeom(BatchQueryStream& stream, const PxGeometry& geom) +{ + PxGeometryType::Enum geomType = geom.getType(); + stream.write(PxU32(geomType)); + switch (geomType) + { + case PxGeometryType::eCAPSULE: + stream.write(static_cast(geom)); + break; + case PxGeometryType::eSPHERE: + stream.write(static_cast(geom)); + break; + case PxGeometryType::eCONVEXMESH: + stream.write(static_cast(geom)); + break; + case PxGeometryType::eBOX: + stream.write(static_cast(geom)); + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("Unsupported geometry type in writeGeom"); + } +} + +static PxGeometry* readGeom(BatchQueryStreamReader& reader) +{ + PxU32 geomType = *reader.read(); + PxGeometry* anyGeom = NULL; + switch (geomType) + { + case PxGeometryType::eCAPSULE: + anyGeom = reader.read(); + break; + case PxGeometryType::eSPHERE: + anyGeom = reader.read(); + break; + case PxGeometryType::eCONVEXMESH: + anyGeom = reader.read(); + break; + case PxGeometryType::eBOX: + anyGeom = reader.read(); + break; + default: + PX_ALWAYS_ASSERT_MESSAGE("Unsupported geometry type in readGeom"); + } + + return anyGeom; +} + +static void writeQueryInput(BatchQueryStream& stream, const MultiQueryInput& input) +{ + stream.write(input); + if (input.rayOrigin) + stream.write(input.rayOrigin); + if (input.unitDir) + stream.write(input.unitDir); + if (input.pose) + stream.write(input.pose); + if (input.geometry) + writeGeom(stream, *input.geometry); +} + +static MultiQueryInput* readQueryInput(BatchQueryStreamReader& stream) +{ + MultiQueryInput* input = stream.read(); + if (input->rayOrigin) + input->rayOrigin = stream.read(); + if (input->unitDir) + input->unitDir = stream.read(); + if (input->pose) + input->pose = stream.read(); + if (input->geometry) + input->geometry = readGeom(stream); + + return input; +} + +template +void writeStatus(ResultType* aRes, const PxHitBuffer& hits, void* userData, bool overflow) +{ + ResultType* res = aRes; + res->userData = userData; + res->block = hits.block; + res->hasBlock = hits.hasBlock; + res->nbTouches = hits.nbTouches; + res->queryStatus = PxU8(overflow ? PxBatchQueryStatus::eOVERFLOW : PxBatchQueryStatus::eSUCCESS); + res->touches = (overflow && res->nbTouches == 0) ? NULL : hits.touches; +} + +void NpBatchQuery::resetResultBuffers() +{ + for (PxU32 i = 0; i +struct PxOverflowBuffer : PxHitBuffer +{ + bool overflow; + PxU32 saveNbTouches; + HitType extraHit; + HitType* saveTouches; + bool processCalled; + PxOverflowBuffer(HitType* hits, PxU32 count) : PxHitBuffer(hits, count), overflow(false), processCalled(false) + { + } + + virtual PxAgain processTouches(const HitType* /*hits*/, PxU32 /*count*/) + { + if (processCalled) + return false; + saveTouches = this->touches; + saveNbTouches = this->nbTouches; + processCalled = true; + this->touches = &extraHit; + this->maxNbTouches = 1; + return true; + } + + virtual void finalizeQuery() + { + if (processCalled) + { + overflow = (this->nbTouches > 0); + this->nbTouches = saveNbTouches; + this->touches = saveTouches; + } + } +}; + +void NpBatchQuery::execute() +{ + NP_READ_CHECK(mNpScene); + + if(mNbRaycasts) + { + PX_CHECK_AND_RETURN(mDesc.queryMemory.userRaycastResultBuffer!=NULL, "PxBatchQuery execute: userRaycastResultBuffer is NULL"); + PX_CHECK_AND_RETURN(mDesc.queryMemory.raycastTouchBufferSize > 0 ? + (mDesc.queryMemory.userRaycastTouchBuffer != NULL) : true, "PxBatchQuery execute: userRaycastTouchBuffer is NULL"); + } + if(mNbOverlaps) + { + PX_CHECK_AND_RETURN(mDesc.queryMemory.userOverlapResultBuffer!=NULL, "PxBatchQuery execute: userOverlapResultBuffer is NULL"); + PX_CHECK_AND_RETURN(mDesc.queryMemory.overlapTouchBufferSize > 0 ? + (mDesc.queryMemory.userOverlapTouchBuffer != NULL) : true, "PxBatchQuery execute: userOverlapTouchBuffer is NULL"); + } + if(mNbSweeps) + { + PX_CHECK_AND_RETURN(mDesc.queryMemory.userSweepResultBuffer!=NULL, "PxBatchQuery execute: userSweepResultBuffer is NULL"); + PX_CHECK_AND_RETURN(mDesc.queryMemory.sweepTouchBufferSize > 0 ? + (mDesc.queryMemory.userSweepTouchBuffer != NULL) : true, "PxBatchQuery execute: userSweepTouchBuffer is NULL"); + } + + PX_SIMD_GUARD; + + PX_PROFILE_ZONE("BatchedSceneQuery.execute", mNpScene->getContextId()); + PxI32 ret = Ps::atomicCompareExchange(&mBatchQueryIsRunning, 1, 0); + if(ret == 1) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxBatchQuery::execute: This batch is already executing"); + return; + } + else if(ret == -1) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxBatchQuery::execute: Another thread is still adding queries to this batch"); + return; + } + + resetResultBuffers(); + + // If PVD is connected and IS_PVD_SQ_ENABLED, record the offsets for queries in pvd buffers and run the queries on PPU + bool isSqCollectorLocked = false; + PX_UNUSED(isSqCollectorLocked); + +#if PX_SUPPORT_PVD + PxU32 pvdRayQstartIdx = 0; + PxU32 pvdOverlapQstartIdx = 0; + PxU32 pvdSweepQstartIdx = 0; + + Vd::ScbScenePvdClient& pvdClient = mNpScene->mScene.getScenePvdClient(); + const bool needUpdatePvd = pvdClient.checkPvdDebugFlag() && (pvdClient.getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES); + + if(needUpdatePvd) + { + mNpScene->getBatchedSqCollector().getLock().lock(); + isSqCollectorLocked = true; + + pvdRayQstartIdx = mNpScene->getBatchedSqCollector().mAccumulatedRaycastQueries.size(); + pvdOverlapQstartIdx = mNpScene->getBatchedSqCollector().mAccumulatedOverlapQueries.size(); + pvdSweepQstartIdx = mNpScene->getBatchedSqCollector().mAccumulatedSweepQueries.size(); + } +#endif + + // setup local pointers to user provided output buffers + PxRaycastHit* raycastHits = mDesc.queryMemory.userRaycastTouchBuffer; PX_UNUSED(raycastHits); + PxRaycastQueryResult* raycastResults = mDesc.queryMemory.userRaycastResultBuffer; PX_UNUSED(raycastResults); + PxU32 raycastHitsSize = mDesc.queryMemory.raycastTouchBufferSize; PX_UNUSED(raycastHitsSize); + + PxOverlapHit* overlapHits = mDesc.queryMemory.userOverlapTouchBuffer; PX_UNUSED(overlapHits); + PxOverlapQueryResult* overlapResults = mDesc.queryMemory.userOverlapResultBuffer; PX_UNUSED(overlapResults); + PxU32 overlapHitsSize = mDesc.queryMemory.overlapTouchBufferSize; PX_UNUSED(overlapHitsSize); + + PxSweepHit* sweepHits = mDesc.queryMemory.userSweepTouchBuffer; PX_UNUSED(sweepHits); + PxSweepQueryResult* sweepResults = mDesc.queryMemory.userSweepResultBuffer; PX_UNUSED(sweepResults); + PxU32 sweepHitsSize = mDesc.queryMemory.sweepTouchBufferSize; PX_UNUSED(sweepHitsSize); + + BatchQueryFilterData bfd(mDesc.filterShaderData, mDesc.filterShaderDataSize, mDesc.preFilterShader, mDesc.postFilterShader); + + // data declarations for double buffering the input stream + PxU32 curQueryOffset = 0; // first query starts at 0 + if (mPrevOffset == eTERMINAL) // except if zero queries were queued + { + finalizeExecute(); + return; + } + + PxU32 hitsSpaceLeft; PX_UNUSED(hitsSpaceLeft); + + // ====================== parse and execute the batch query memory stream ====================== + PxU32 queryCount = 0; + do { + // parse a query from the input stream, create a stream reader at current double buffer + BatchQueryStreamReader reader(mStream.begin()+curQueryOffset); + BatchStreamHeader& h = *reader.read(); + + curQueryOffset = h.nextQueryOffset; + Ps::prefetchLine(mStream.begin() + curQueryOffset); + + MultiQueryInput& input = *readQueryInput(reader); + + // ====================== switch over query type - QTypeROS::eRAYCAST, eOVERLAP, eSWEEP ===================== + switch (h.hitTypeId) + { + // =============== Current query is a raycast ===================== + case QTypeROS::eRAYCAST: + { + PxU32 nbRaycastHits = PxU32(raycastHits - mDesc.queryMemory.userRaycastTouchBuffer); + PX_ASSERT(nbRaycastHits <= raycastHitsSize); + hitsSpaceLeft = raycastHitsSize - nbRaycastHits; + PxOverflowBuffer hits(raycastHits, PxMin(h.maxTouchHits, hitsSpaceLeft)); + mNpScene->NpScene::multiQuery(input, hits, h.hitFlags, h.cache, h.fd, NULL, &bfd); + hits.overflow |= (hitsSpaceLeft == 0 && h.maxTouchHits > 0); // report overflow if 0 space left and maxTouchHits>0 + writeStatus(raycastResults++, hits, h.userData, hits.overflow); + raycastHits += hits.nbTouches; + } break; + + // ================ Current query is an overlap ==================== + case QTypeROS::eOVERLAP: + { + PxU32 nbOverlapHits = PxU32(overlapHits - mDesc.queryMemory.userOverlapTouchBuffer); + PX_ASSERT(nbOverlapHits <= overlapHitsSize); + hitsSpaceLeft = overlapHitsSize - nbOverlapHits; + PxOverflowBuffer hits(overlapHits, PxMin(h.maxTouchHits, hitsSpaceLeft)); + mNpScene->NpScene::multiQuery(input, hits, h.hitFlags, h.cache, h.fd, NULL, &bfd); + hits.overflow |= (hitsSpaceLeft == 0 && h.maxTouchHits > 0); // report overflow if 0 space left and maxTouchHits>0 + writeStatus(overlapResults++, hits, h.userData, hits.overflow); + overlapHits += hits.nbTouches; + } break; + + // ================== Current query is a sweep ========================= + case QTypeROS::eSWEEP: + { + + PxU32 nbSweepHits = PxU32(sweepHits - mDesc.queryMemory.userSweepTouchBuffer); + PX_ASSERT(nbSweepHits <= sweepHitsSize); + hitsSpaceLeft = sweepHitsSize - nbSweepHits; + PxOverflowBuffer hits(sweepHits, PxMin(h.maxTouchHits, hitsSpaceLeft)); + mNpScene->NpScene::multiQuery(input, hits, h.hitFlags, h.cache, h.fd, NULL, &bfd); + hits.overflow |= (hitsSpaceLeft == 0 && h.maxTouchHits > 0); // report overflow if 0 space left and maxTouchHits>0 + writeStatus(sweepResults++, hits, h.userData, hits.overflow); + sweepHits += hits.nbTouches; + } break; + default: + PX_ALWAYS_ASSERT_MESSAGE("Unexpected batch query type (raycast/overlap/sweep)."); + } + + if (h.nextQueryOffset == eTERMINAL) // end of stream + // AP: previously also had a break on hitCount==-1 which is aborted due to out of space + // abort stream parsing if we ran into an aborted query (hitCount==-1).. but it was easier to just continue + // the perf implications for aborted queries are not a significant consideration and this allows to avoid + // writing special case code for filling the query buffers after aborted query + break; + #undef MULTIQ + queryCount++; + } while (queryCount < 1000000); + +#if PX_SUPPORT_PVD + if( isSqCollectorLocked && needUpdatePvd) + { + mNpScene->getBatchedSqCollector().collectAllBatchedHits( mDesc.queryMemory.userRaycastResultBuffer, mNbRaycasts, pvdRayQstartIdx, + mDesc.queryMemory.userOverlapResultBuffer, mNbOverlaps, pvdOverlapQstartIdx, + mDesc.queryMemory.userSweepResultBuffer, mNbSweeps, pvdSweepQstartIdx); + } + + // Maybe connection is disconnected after the lock + if( isSqCollectorLocked ) + { + mNpScene->getBatchedSqCollector().getLock().unlock(); + isSqCollectorLocked = false; + } +#endif + + finalizeExecute(); +} + +/////////////////////////////////////////////////////////////////////////////// +void NpBatchQuery::writeBatchHeader(const BatchStreamHeader& h) +{ + PxU32 streamPos = PxU32(mStream.getPos()); // save the stream pos before we write the header + mStream.write(h); + // link into a list as offset + PxU32* prevPtr = (mPrevOffset == eTERMINAL) ? &mPrevOffset : reinterpret_cast(mStream.begin()+mPrevOffset); + PxU32 headerPtr = streamPos+PX_OFFSET_OF(BatchStreamHeader, nextQueryOffset); + *prevPtr = headerPtr; + mPrevOffset = headerPtr; +} + +/////////////////////////////////////////////////////////////////////////////// +void NpBatchQuery::raycast( + const PxVec3& origin, const PxVec3& unitDir, PxReal distance, PxU16 maxTouchHits, + PxHitFlags hitFlags, const PxQueryFilterData& fd, void* userData, const PxQueryCache* cache) +{ + PX_CHECK_AND_RETURN(distance>0, "PxBatchQuery::raycast: The maximum distance must be greater than zero!"); + PX_CHECK_AND_RETURN(unitDir.isNormalized(), "PxBatchQuery::raycast: Direction must be normalized"); + PX_CHECK_AND_RETURN(origin.isFinite(), "PxBatchQuery::raycast: origin is not valid"); + if (mNbRaycasts >= mDesc.queryMemory.getMaxRaycastsPerExecute()) + { + PX_CHECK_AND_RETURN(mNbRaycasts < mDesc.queryMemory.getMaxRaycastsPerExecute(), + "PxBatchQuery: number of raycast() calls exceeds PxBatchQueryMemory::raycastResultBufferSize, query discarded"); + return; + } + CHECK_RUNNING("PxBatchQuery::raycast: This batch is still executing, skipping query."); + mNbRaycasts++; + + writeBatchHeader(BatchStreamHeader(hitFlags, cache, fd, userData, maxTouchHits, QTypeROS::eRAYCAST)); + writeQueryInput(mStream, MultiQueryInput(origin, unitDir, distance)); + + Ps::atomicExchange(&mBatchQueryIsRunning, 0); +} + +/////////////////////////////////////////////////////////////////////////////// +void NpBatchQuery::overlap( + const PxGeometry& geometry, const PxTransform& pose, PxU16 maxTouchHits, + const PxQueryFilterData& fd, void* userData, const PxQueryCache* cache) +{ + PX_CHECK_AND_RETURN(pose.isValid(), "NpBatchQuery::overlapMultiple pose is not valid."); + if (mNbOverlaps >= mDesc.queryMemory.getMaxOverlapsPerExecute()) + { + PX_CHECK_AND_RETURN(mNbOverlaps < mDesc.queryMemory.getMaxOverlapsPerExecute(), + "PxBatchQuery: number of overlap() calls exceeds PxBatchQueryMemory::overlapResultBufferSize, query discarded"); + return; + } + CHECK_RUNNING("PxBatchQuery::overlap: This batch is still executing, skipping query.") + mNbOverlaps++; + + writeBatchHeader(BatchStreamHeader(PxHitFlags(), cache, fd, userData, maxTouchHits, QTypeROS::eOVERLAP)); + writeQueryInput(mStream, MultiQueryInput(&geometry, &pose)); + + Ps::atomicExchange(&mBatchQueryIsRunning, 0); +} + +void NpBatchQuery::sweep( + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, PxU16 maxTouchHits, + PxHitFlags hitFlags, const PxQueryFilterData& fd, void* userData, const PxQueryCache* cache, const PxReal inflation) +{ + PX_CHECK_AND_RETURN(pose.isValid(), "Batch sweep input check: pose is not valid."); + PX_CHECK_AND_RETURN(unitDir.isFinite(), "Batch sweep input check: unitDir is not valid."); + PX_CHECK_AND_RETURN(unitDir.isNormalized(), "Batch sweep input check: direction must be normalized"); + PX_CHECK_AND_RETURN(distance >= 0.0f, "Batch sweep input check: distance cannot be negative"); + PX_CHECK_AND_RETURN(distance != 0.0f || !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP), + "Batch sweep input check: zero-length sweep only valid without the PxHitFlag::eASSUME_NO_INITIAL_OVERLAP flag"); + +#if PX_CHECKED + if(!PxGeometryQuery::isValid(geometry)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry is not valid"); + return; + } +#endif // PX_CHECKED + + if (mNbSweeps >= mDesc.queryMemory.getMaxSweepsPerExecute()) + { + PX_CHECK_AND_RETURN(mNbSweeps < mDesc.queryMemory.getMaxSweepsPerExecute(), + "PxBatchQuery: number of sweep() calls exceeds PxBatchQueryMemory::sweepResultBufferSize, query discarded"); + return; + } + + + CHECK_RUNNING("PxBatchQuery::sweep: This batch is still executing, skipping query.") + mNbSweeps++; + + writeBatchHeader(BatchStreamHeader(hitFlags, cache, fd, userData, maxTouchHits, QTypeROS::eSWEEP)); + + //set the MTD flag + mHasMtdSweep |= !!(hitFlags & PxHitFlag::eMTD); + + if((hitFlags & PxHitFlag::ePRECISE_SWEEP) && (hitFlags & PxHitFlag::eMTD)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support MTD. Perform MTD with default sweep"); + hitFlags &= ~PxHitFlag::ePRECISE_SWEEP; + } + + if((hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP) && (hitFlags & PxHitFlag::eMTD)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " eMTD cannot be used in conjunction with eASSUME_NO_INITIAL_OVERLAP. eASSUME_NO_INITIAL_OVERLAP will be ignored"); + hitFlags &= ~PxHitFlag::eASSUME_NO_INITIAL_OVERLAP; + } + + PxReal realInflation = inflation; + if((hitFlags & PxHitFlag::ePRECISE_SWEEP)&& inflation > 0.0f) + { + realInflation = 0.0f; + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support inflation, inflation will be overwritten to be zero"); + } + + writeQueryInput(mStream, MultiQueryInput(&geometry, &pose, unitDir, distance, realInflation)); + + Ps::atomicExchange(&mBatchQueryIsRunning, 0); +} + +void NpBatchQuery::release() +{ + if(Ps::atomicCompareExchange(&mBatchQueryIsRunning, 0, 0) != 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxBatchQuery::release: This batch is still executing, skipping release"); + return; + } + + mNpScene->releaseBatchQuery(this); +} + +PxBatchQueryPreFilterShader NpBatchQuery::getPreFilterShader() const +{ + return mDesc.preFilterShader; +} + +PxBatchQueryPostFilterShader NpBatchQuery::getPostFilterShader() const +{ + return mDesc.postFilterShader; +} + +const void* NpBatchQuery::getFilterShaderData() const +{ + return mDesc.filterShaderData; +} + +PxU32 NpBatchQuery::getFilterShaderDataSize() const +{ + return mDesc.filterShaderDataSize; +} + + diff --git a/src/PhysX/physx/source/physx/src/NpBatchQuery.h b/src/PhysX/physx/source/physx/src/NpBatchQuery.h new file mode 100644 index 000000000..4bc3c6f9c --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpBatchQuery.h @@ -0,0 +1,166 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_SCENEQUERY +#define PX_PHYSICS_NP_SCENEQUERY +/** \addtogroup physics +@{ */ + +#include "PxBatchQuery.h" +#include "PsArray.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsSync.h" + +namespace physx +{ + +class NpSceneQueryManager; +struct BatchStreamHeader; +class NpScene; + +namespace Sq +{ +class SceneQueryManager; +} + +struct BatchQueryStream : Ps::Array +{ + BatchQueryStream() { rewind(); } + + void rewind() { mPosition = 0; } + + PX_FORCE_INLINE PxI32 getPos() { return PxI32(mPosition); } // signed to avoid casts elsewhere + + // write an object of type T to the stream, copying by value + template + PX_FORCE_INLINE void write(const T* val, PxU32 count = 1) + { + PX_COMPILE_TIME_ASSERT(sizeof(T) > 0); + PxU32 newPosition = mPosition + sizeof(T)*count; + if (newPosition > capacity()) + reserve(newPosition+(newPosition<<1)); + resizeUninitialized(newPosition); + T* dest = reinterpret_cast(begin() + mPosition); + for (PxU32 i = 0; i < count; i++) + { + *dest = *(val+i); + dest++; + } + mPosition = newPosition; + } + + template + PX_FORCE_INLINE void write(const T& val) + { + write(&val, 1); + } + + PX_FORCE_INLINE bool atEnd() const { return mPosition >= size(); } + +protected: + mutable PxU32 mPosition; +}; + +struct BatchQueryStreamReader +{ + BatchQueryStreamReader(char* buffer) : mBuffer(buffer), mReadPos(0) {} + + // read an object of type T from the stream (simply returns a pointer without copying) + template + PX_FORCE_INLINE T* read(PxU32 count = 1) + { + //PX_ASSERT(mPosition+sizeof(T)*count <= size()); + T* result = reinterpret_cast(mBuffer+mReadPos); + mReadPos += sizeof(T)*count; + return result; + } + + char* mBuffer; + PxU32 mReadPos; +}; + +class NpBatchQuery : public PxBatchQuery, public Ps::UserAllocated +{ +public: + NpBatchQuery(NpScene& owner, const PxBatchQueryDesc& d); + virtual ~NpBatchQuery(); + + // PxBatchQuery interface + virtual void execute(); + virtual void release(); + virtual PxBatchQueryPreFilterShader getPreFilterShader() const; + virtual PxBatchQueryPostFilterShader getPostFilterShader() const; + virtual const void* getFilterShaderData() const; + virtual PxU32 getFilterShaderDataSize() const; + virtual void setUserMemory(const PxBatchQueryMemory& ); + virtual const PxBatchQueryMemory& getUserMemory(); + + virtual void raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal distance, PxU16 maxTouchHits, + PxHitFlags hitFlags, const PxQueryFilterData& filterData, + void* userData, const PxQueryCache* cache); + + virtual void overlap(const PxGeometry& geometry, const PxTransform& pose, PxU16 maxTouchHits, + const PxQueryFilterData& filterData, void* userData, + const PxQueryCache* cache); + + virtual void sweep(const PxGeometry& geometry, const PxTransform& pose, + const PxVec3& unitDir, const PxReal distance, PxU16 maxTouchHits, + PxHitFlags hitFlags, const PxQueryFilterData& filterData, + void* userData, const PxQueryCache* cache, const PxReal inflation); + + PxBatchQueryDesc& getDesc() { return mDesc; } + virtual const PxBatchQueryDesc& getDesc() const { return mDesc; } + + enum { eTERMINAL = PxU32(-16) }; // -16 so it's aligned to avoid SPU checks + + // sync object for batch query completion wait + shdfnd::Sync mSync; +private: + void resetResultBuffers(); + void finalizeExecute(); + void writeBatchHeader(const BatchStreamHeader& h); + + NpScene* mNpScene; + BatchQueryStream mStream; + PxU32 mNbRaycasts, mNbOverlaps, mNbSweeps; + volatile PxI32 mBatchQueryIsRunning; + PxBatchQueryDesc mDesc; + // offset in mStream of the offset to the next query for the last header written by BQ query functions + PxU32 mPrevOffset; + bool mHasMtdSweep; + + friend class physx::Sq::SceneQueryManager; +}; + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/physx/src/NpCast.h b/src/PhysX/physx/source/physx/src/NpCast.h new file mode 100644 index 000000000..4ae9476ec --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpCast.h @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NP_CAST +#define PX_PHYSICS_NP_CAST + +#include "PxPhysXConfig.h" +#include "NpScene.h" +#include "NpRigidDynamic.h" +#include "NpRigidStatic.h" +#include "NpArticulation.h" +#include "NpArticulationReducedCoordinate.h" +#include "NpArticulationLink.h" +#include "NpArticulationJoint.h" +#include "NpAggregate.h" + +namespace physx +{ + // PT: Scb-to-Np casts + + PX_FORCE_INLINE const NpScene* getNpScene(const Scb::Scene* scene) + { + const size_t scbOffset = reinterpret_cast(&(reinterpret_cast(0)->getScene())); + return reinterpret_cast(reinterpret_cast(scene) - scbOffset); + } + + PX_FORCE_INLINE const NpRigidDynamic* getNpRigidDynamic(const Scb::Body* scbBody) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbActorFast())); + return reinterpret_cast(reinterpret_cast(scbBody) - offset); + } + + PX_FORCE_INLINE const NpRigidStatic* getNpRigidStatic(const Scb::RigidStatic* scbRigidStatic) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbActorFast())); + return reinterpret_cast(reinterpret_cast(scbRigidStatic) - offset); + } + + PX_FORCE_INLINE const NpShape* getNpShape(const Scb::Shape* scbShape) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbShape())); + return reinterpret_cast(reinterpret_cast(scbShape) - offset); + } + + PX_FORCE_INLINE const NpArticulationLink* getNpArticulationLink(const Scb::Body* scbArticulationLink) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbActorFast())); + return reinterpret_cast(reinterpret_cast(scbArticulationLink) - offset); + } + + PX_FORCE_INLINE const NpArticulation* getNpArticulation(const Scb::Articulation* scbArticulation) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->mImpl.getArticulation())); + return reinterpret_cast(reinterpret_cast(scbArticulation) - offset); + } + + PX_FORCE_INLINE const NpArticulationReducedCoordinate* getNpArticulationRC(const Scb::Articulation* scbArticulation) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->mImpl.getArticulation())); + return reinterpret_cast(reinterpret_cast(scbArticulation) - offset); + } + + PX_FORCE_INLINE const NpArticulationJoint* getNpArticulationJoint(const Scb::ArticulationJoint* scbArticulationJoint) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbArticulationJoint())); + return reinterpret_cast(reinterpret_cast(scbArticulationJoint) - offset); + } + + PX_FORCE_INLINE const NpAggregate* getNpAggregate(const Scb::Aggregate* aggregate) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbAggregate())); + return reinterpret_cast(reinterpret_cast(aggregate) - offset); + } + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpConnector.h b/src/PhysX/physx/source/physx/src/NpConnector.h new file mode 100644 index 000000000..d404153d8 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpConnector.h @@ -0,0 +1,136 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_CONNECTOR +#define PX_PHYSICS_NP_CONNECTOR + +#include "CmPhysXCommon.h" +#include "PsInlineArray.h" +#include "PxSerialFramework.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +namespace physx +{ + +struct NpConnectorType +{ + enum Enum + { + eConstraint, + eAggregate, + eObserver, + eBvhStructure, + eInvalid + }; +}; + + +class NpConnector +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + NpConnector() : mType(NpConnectorType::eInvalid), mObject(NULL) {} + NpConnector(NpConnectorType::Enum type, PxBase* object) : mType(Ps::to8(type)), mObject(object) {} +// PX_SERIALIZATION + NpConnector(const NpConnector& c) + { + //special copy constructor that initializes padding bytes for meta data verification (PX_CHECKED only) + Cm::markSerializedMem(this, sizeof(NpConnector)); + mType = c.mType; + mObject = c.mObject; + } + + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PxU8 mType; // Revisit whether the type is really necessary or whether the serializable type is enough. + // Since joints might gonna inherit from observers to register for constraint release events, the type + // is necessary because a joint has its own serializable type and could not be detected as observer anymore. + PxU8 mPadding[3]; // PT: padding from prev byte + PxBase* mObject; // So far the serialization framework only supports ptr resolve for PxBase objects. + // However, so far the observers all are PxBase, hence this choice of type. +}; + + +class NpConnectorIterator +{ +public: + PX_FORCE_INLINE NpConnectorIterator(NpConnector* c, PxU32 size, NpConnectorType::Enum type) : mConnectors(c), mSize(size), mIndex(0), mType(type) {} + + PX_FORCE_INLINE PxBase* getNext() + { + PxBase* s = NULL; + while(mIndex < mSize) + { + NpConnector& c = mConnectors[mIndex]; + mIndex++; + if (c.mType == mType) + return c.mObject; + } + return s; + } + +private: + NpConnector* mConnectors; + PxU32 mSize; + PxU32 mIndex; + NpConnectorType::Enum mType; +}; + + +class NpConnectorArray: public Ps::InlineArray +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpConnectorArray(const PxEMPTY) : Ps::InlineArray (PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + NpConnectorArray() : Ps::InlineArray(PX_DEBUG_EXP("connectorArray")) + { + //special default constructor that initializes padding bytes for meta data verification (PX_CHECKED only) + Cm::markSerializedMem(this->mData, 4*sizeof(NpConnector)); + } +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpConstraint.cpp b/src/PhysX/physx/source/physx/src/NpConstraint.cpp new file mode 100644 index 000000000..7e7d3a1e8 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpConstraint.cpp @@ -0,0 +1,407 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxConstraint.h" +#include "NpConstraint.h" +#include "NpPhysics.h" +#include "NpRigidDynamic.h" +#include "NpRigidStatic.h" +#include "NpArticulationLink.h" +#include "ScbConstraint.h" +#include "ScbNpDeps.h" + +using namespace physx; + +void NpConstraint::setConstraintFunctions(PxConstraintConnector& n, const PxConstraintShaderTable& shaders) +{ + mConstraint.getScConstraint().setConstraintFunctions(n, shaders); + + //update mConnectorArray, since mActor0 or mActor1 should be in external reference + bool bNeedUpdate = false; + if(mActor0) + { + NpActor& npActor = NpActor::getFromPxActor(*mActor0); + if(npActor.findConnector(NpConnectorType::eConstraint, this) == 0xffffffff) + { + bNeedUpdate = true; + npActor.addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); + } + } + + if(mActor1) + { + NpActor& npActor = NpActor::getFromPxActor(*mActor1); + if(npActor.findConnector(NpConnectorType::eConstraint, this) == 0xffffffff) + { + bNeedUpdate = true; + npActor.addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); + } + } + + if(bNeedUpdate) + { + NpScene* newScene = getSceneFromActors(mActor0, mActor1); + NpScene* oldScene = getNpScene(); + + if (oldScene != newScene) + { + if (oldScene) + { + oldScene->removeFromConstraintList(*this); + oldScene->getScene().removeConstraint(getScbConstraint()); + } + if (newScene) + { + newScene->addToConstraintList(*this); + newScene->getScene().addConstraint(getScbConstraint()); + } + } + } +} + +NpConstraint::NpConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) +: PxConstraint(PxConcreteType::eCONSTRAINT, PxBaseFlag::eOWNS_MEMORY) +, mActor0 (actor0) +, mActor1 (actor1) +, mConstraint (connector, shaders, dataSize) +, mIsDirty (true) +{ + + mConstraint.setFlags(shaders.flag); + if(actor0) + NpActor::getFromPxActor(*actor0).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); + if(actor1) + NpActor::getFromPxActor(*actor1).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); + + NpScene* s = getSceneFromActors(actor0, actor1); + if (s) + { + s->addToConstraintList(*this); + s->getScene().addConstraint(mConstraint); + } +} + + +NpConstraint::~NpConstraint() +{ + if(getBaseFlags()&PxBaseFlag::eOWNS_MEMORY) + mConstraint.getPxConnector()->onConstraintRelease(); + + NpFactory::getInstance().onConstraintRelease(this); +} + +void NpConstraint::release() +{ + NpScene* npScene = getNpScene(); + NP_WRITE_CHECK(npScene); + + NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, NULL); + + if(mActor0) + NpActor::getFromPxActor(*mActor0).removeConnector(*mActor0, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); + if(mActor1) + NpActor::getFromPxActor(*mActor1).removeConnector(*mActor1, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); + + if (npScene) + { + npScene->removeFromConstraintList(*this); + npScene->getScene().removeConstraint(getScbConstraint()); + } + + mConstraint.destroy(); +} + +// PX_SERIALIZATION +void NpConstraint::resolveReferences(PxDeserializationContext& context) +{ + context.translatePxBase(mActor0); + context.translatePxBase(mActor1); +} + +NpConstraint* NpConstraint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpConstraint* obj = new (address) NpConstraint(PxBaseFlags(0)); + address += sizeof(NpConstraint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// ~PX_SERIALIZATION + +PxScene* NpConstraint::getScene() const +{ + return getNpScene(); +} + +void NpConstraint::getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const +{ + NP_READ_CHECK(getNpScene()); + actor0 = mActor0; + actor1 = mActor1; +} + +void NpConstraint::setActors(PxRigidActor* actor0, PxRigidActor* actor1) +{ + NP_WRITE_CHECK(getNpScene()); + + PX_CHECK_AND_RETURN((actor0 && !actor0->is()) || (actor1 && !actor1->is()), "PxConstraint: at least one actor must be non-static"); + PX_SIMD_GUARD; + + if(mActor0) + NpActor::getFromPxActor(*mActor0).removeConnector(*mActor0, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); + if(mActor1) + NpActor::getFromPxActor(*mActor1).removeConnector(*mActor1, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); + + if(actor0) + NpActor::getFromPxActor(*actor0).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); + if(actor1) + NpActor::getFromPxActor(*actor1).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); + + mActor0 = actor0; + mActor1 = actor1; + + NpScene* newScene = getSceneFromActors(actor0, actor1); + NpScene* oldScene = getNpScene(); + + if (oldScene != newScene) + { + if (oldScene) + { + oldScene->removeFromConstraintList(*this); + oldScene->getScene().removeConstraint(getScbConstraint()); + } + + getScbConstraint().setBodies(getScbRigidObject(actor0), getScbRigidObject(actor1)); + + if (newScene) + { + newScene->addToConstraintList(*this); + newScene->getScene().addConstraint(getScbConstraint()); + } + } + else + getScbConstraint().setBodies(getScbRigidObject(actor0), getScbRigidObject(actor1)); +} + +void NpConstraint::markDirty() +{ + mIsDirty = true; +} + +void NpConstraint::setFlags(PxConstraintFlags flags) +{ + NP_WRITE_CHECK(getNpScene()); + // check for eBROKEN which is a read only flag + PX_CHECK_AND_RETURN(!(flags & PxConstraintFlag::eBROKEN), "PxConstraintFlag::eBROKEN is a read only flag"); + + PX_CHECK_AND_RETURN(!(flags & PxConstraintFlag::eGPU_COMPATIBLE), "PxConstraintFlag::eGPU_COMPATIBLE is an internal flag and is illegal to set via the API"); + + PX_SIMD_GUARD; + + mConstraint.setFlags(flags); +} + +PxConstraintFlags NpConstraint::getFlags() const +{ + NP_READ_CHECK(getNpScene()); + return mConstraint.getFlags(); +} + + +void NpConstraint::setFlag(PxConstraintFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(getNpScene()); + + // check for eBROKEN which is a read only flag + PX_CHECK_AND_RETURN(flag != PxConstraintFlag::eBROKEN, "PxConstraintFlag::eBROKEN is a read only flag"); + + PX_CHECK_AND_RETURN(flag != PxConstraintFlag::eGPU_COMPATIBLE, "PxConstraintFlag::eGPU_COMPATIBLE is an internal flag and is illegal to set via the API"); + + PX_SIMD_GUARD; + + PxConstraintFlags f = mConstraint.getFlags(); + + mConstraint.setFlags(value ? f|flag : f&~flag); +} + + +void NpConstraint::getForce(PxVec3& linear, PxVec3& angular) const +{ + NP_READ_CHECK(getNpScene()); + mConstraint.getForce(linear, angular); +} + +void NpConstraint::updateConstants() +{ + if(!mIsDirty) + return; + + //ML - we can't just set dirty to false because this fails with constraints that were created while the scene was buffering! + if(mConstraint.updateConstants(mConstraint.getPxConnector()->prepareData())) + mIsDirty = false; +#if PX_SUPPORT_PVD + Scb::Scene* scbScene = mConstraint.getScbSceneForAPI(); + //Changed to use the visual scenes update system which respects + //the debugger's connection type flag. + if( scbScene && !scbScene->isPhysicsBuffering() ) + scbScene->getScenePvdClient().updatePvdProperties( &mConstraint ); +#endif +} + +void NpConstraint::setBreakForce(PxReal linear, PxReal angular) +{ + NP_WRITE_CHECK(getNpScene()); + PX_SIMD_GUARD; + mConstraint.setBreakForce(linear, angular); +} + +void NpConstraint::getBreakForce(PxReal& linear, PxReal& angular) const +{ + NP_READ_CHECK(getNpScene()); + PX_SIMD_GUARD; + mConstraint.getBreakForce(linear, angular); +} + + +void NpConstraint::setMinResponseThreshold(PxReal threshold) +{ + PX_CHECK_AND_RETURN(PxIsFinite(threshold) && threshold>=0, "PxConstraint::setMinResponseThreshold: threshold must be non-negative"); + NP_WRITE_CHECK(getNpScene()); + PX_SIMD_GUARD; + mConstraint.setMinResponseThreshold(threshold); +} + +PxReal NpConstraint::getMinResponseThreshold() const +{ + NP_READ_CHECK(getNpScene()); + PX_SIMD_GUARD; + return mConstraint.getMinResponseThreshold(); +} + +bool NpConstraint::isValid() const +{ + NP_READ_CHECK(getNpScene()); + bool isValid0 = mActor0 && !mActor0->is(); + bool isValid1 = mActor1 && !mActor1->is(); + return isValid0 || isValid1; +} + +void* NpConstraint::getExternalReference(PxU32& typeID) +{ + NP_READ_CHECK(getNpScene()); + PxConstraintConnector* connector = mConstraint.getPxConnector(); + return connector->getExternalReference(typeID); +} + +void NpConstraint::comShift(PxRigidActor* actor) +{ + PX_ASSERT(actor == mActor0 || actor == mActor1); + PxConstraintConnector* connector = mConstraint.getPxConnector(); + if(actor == mActor0) + connector->onComShift(0); + if(actor == mActor1) + connector->onComShift(1); +} + +void NpConstraint::actorDeleted(PxRigidActor* actor) +{ + // the actor cannot be deleted without also removing it from the scene, + // which means that the joint will also have been removed from the scene, + // so we can just reset the actor here. + PX_ASSERT(actor == mActor0 || actor == mActor1); + + if(actor == mActor0) + mActor0 = 0; + else + mActor1 = 0; +} + + + +NpScene* NpConstraint::getNpScene() const +{ + return mConstraint.getScbSceneForAPI() ? static_cast(mConstraint.getScbSceneForAPI()->getPxScene()) : NULL; +} + +Scb::RigidObject* NpConstraint::getScbRigidObject(PxRigidActor* a) +{ + if(!a) + return NULL; + + PxType type = a->getConcreteType(); + + if (type == PxConcreteType::eRIGID_DYNAMIC) + return &(static_cast(a)->getScbBodyFast()); + else if (type == PxConcreteType::eARTICULATION_LINK) + return &(static_cast(a)->getScbBodyFast()); + else + { + PX_ASSERT(type == PxConcreteType::eRIGID_STATIC); + return &(static_cast(a)->getScbRigidStaticFast()); + } +} + +void physx::NpConstraintGetRigidObjectsFromScb(const Scb::Constraint&c, Scb::RigidObject*&b0, Scb::RigidObject*&b1) +{ + const size_t offset = size_t(&(reinterpret_cast(0)->getScbConstraint())); + const NpConstraint* np = reinterpret_cast(reinterpret_cast(&c)-offset); + + PxRigidActor* a0, * a1; + np->getActors(a0, a1); + b0 = NpConstraint::getScbRigidObject(a0); + b1 = NpConstraint::getScbRigidObject(a1); +} + +NpScene* NpConstraint::getSceneFromActors() const +{ + return getSceneFromActors(mActor0, mActor1); +} + +PX_FORCE_INLINE NpScene* NpConstraint::getSceneFromActors(const PxRigidActor* actor0, const PxRigidActor* actor1) +{ + NpScene* s0 = NULL; + NpScene* s1 = NULL; + + if (actor0 && (!(actor0->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) + s0 = static_cast(actor0->getScene()); + if (actor1 && (!(actor1->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) + s1 = static_cast(actor1->getScene()); + +#if PX_CHECKED + if ((s0 && s1) && (s0 != s1)) + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Adding constraint to scene: Actors belong to different scenes, undefined behavior expected!"); +#endif + + if ((!actor0 || s0) && (!actor1 || s1)) + return s0 ? s0 : s1; + else + return NULL; +} diff --git a/src/PhysX/physx/source/physx/src/NpConstraint.h b/src/PhysX/physx/source/physx/src/NpConstraint.h new file mode 100644 index 000000000..4738b4cb4 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpConstraint.h @@ -0,0 +1,131 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_CONSTRAINT +#define PX_PHYSICS_NP_CONSTRAINT + +#include "PsUserAllocated.h" +#include "PxConstraint.h" +#include "ScbConstraint.h" + +namespace physx +{ + +class NpScene; +class NpRigidDynamic; + +namespace Scb +{ + class RigidObject; +} + +class NpConstraint : public PxConstraint, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpConstraint(PxBaseFlags baseFlags) : PxConstraint(baseFlags), mConstraint(PxEmpty) {} + virtual void setConstraintFunctions(PxConstraintConnector& n, + const PxConstraintShaderTable &t); + static NpConstraint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void exportExtraData(PxSerializationContext&) {} + void importExtraData(PxDeserializationContext&) { } + void resolveReferences(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback&){} + virtual bool isSubordinate() const { return true; } +//~PX_SERIALIZATION + NpConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize); + ~NpConstraint(); + + virtual void release(); + + virtual PxScene* getScene() const; + + virtual void getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const; + virtual void setActors(PxRigidActor* actor0, PxRigidActor* actor1); + + virtual PxConstraintFlags getFlags() const; + virtual void setFlags(PxConstraintFlags flags); + virtual void setFlag(PxConstraintFlag::Enum flag, bool value); + + virtual void getForce(PxVec3& linear, PxVec3& angular) const; + + virtual void markDirty(); + + virtual void setBreakForce(PxReal linear, PxReal angular); + virtual void getBreakForce(PxReal& linear, PxReal& angular) const; + + virtual void setMinResponseThreshold(PxReal threshold); + virtual PxReal getMinResponseThreshold() const; + + virtual bool isValid() const; + + virtual void* getExternalReference(PxU32& typeID); + + void initialize(const PxConstraintDesc&, Scb::Constraint*); + void updateConstants(); + void comShift(PxRigidActor*); + void actorDeleted(PxRigidActor*); + + + + NpScene* getNpScene() const; + + NpScene* getSceneFromActors() const; + PX_FORCE_INLINE Scb::Constraint& getScbConstraint() { return mConstraint; } + PX_FORCE_INLINE const Scb::Constraint& getScbConstraint() const { return mConstraint; } + PX_FORCE_INLINE bool isDirty() const { return mIsDirty; } + + + static Scb::RigidObject* getScbRigidObject(PxRigidActor*); +private: + PX_FORCE_INLINE static NpScene* getSceneFromActors(const PxRigidActor* actor0, const PxRigidActor* actor1); // Returns the scene if both actors are in the scene, else NULL + + PxRigidActor* mActor0; + PxRigidActor* mActor1; + Scb::Constraint mConstraint; + + // this used to be stored in Scb, but that doesn't really work since Scb::Constraint's + // flags all get cleared on fetchResults. In any case, in order to support O(1) + // insert/remove this really wants to be an index into NpScene's dirty joint array + + bool mIsDirty; + bool mPaddingFromBool[3]; // PT: because of mIsDirty +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpFactory.cpp b/src/PhysX/physx/source/physx/src/NpFactory.cpp new file mode 100644 index 000000000..416bcd125 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpFactory.cpp @@ -0,0 +1,850 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpCast.h" +#include "NpFactory.h" +#include "NpPhysics.h" +#include "ScPhysics.h" +#include "ScbScene.h" +#include "ScbActor.h" +#include "GuHeightField.h" +#include "GuTriangleMesh.h" +#include "GuConvexMesh.h" + +#include "NpConnector.h" +#include "NpPtrTableStorageManager.h" +#include "CmCollection.h" +#include "NpArticulationReducedCoordinate.h" +#include "NpArticulationJointReducedCoordinate.h" + +using namespace physx; + +NpFactory::NpFactory() +: GuMeshFactory() +, mConnectorArrayPool(PX_DEBUG_EXP("connectorArrayPool")) +, mPtrTableStorageManager(PX_NEW(NpPtrTableStorageManager)) +, mMaterialPool(PX_DEBUG_EXP("MaterialPool")) +#if PX_SUPPORT_PVD + , mNpFactoryListener(NULL) +#endif +{ +} + +namespace +{ + template void releaseAll(Ps::HashSet& container) + { + // a bit tricky: release will call the factory back to remove the object from + // the tracking array, immediately invalidating the iterator. Reconstructing the + // iterator per delete can be expensive. So, we use a temporary object. + // + // a coalesced hash would be efficient too, but we only ever iterate over it + // here so it's not worth the 2x remove penalty over the normal hash. + + Ps::Array > tmp; + tmp.reserve(container.size()); + for(typename Ps::HashSet::Iterator iter = container.getIterator(); !iter.done(); ++iter) + tmp.pushBack(*iter); + + PX_ASSERT(tmp.size() == container.size()); + for(PxU32 i=0;irelease(); + } +} + +NpFactory::~NpFactory() +{ + PX_DELETE(mPtrTableStorageManager); +} + +void NpFactory::release() +{ + releaseAll(mAggregateTracking); + releaseAll(mConstraintTracking); + releaseAll(mArticulationTracking); + releaseAll(mActorTracking); + while(mShapeTracking.size()) + static_cast(mShapeTracking.getEntries()[0])->releaseInternal(); + + GuMeshFactory::release(); // deletes the class +} + +void NpFactory::createInstance() +{ + PX_ASSERT(!mInstance); + mInstance = PX_NEW(NpFactory)(); +} + +void NpFactory::destroyInstance() +{ + PX_ASSERT(mInstance); + mInstance->release(); + mInstance = NULL; +} + +NpFactory* NpFactory::mInstance = NULL; + +/////////////////////////////////////////////////////////////////////////////// + +template +static void addToTracking(T1& set, T0* element, Ps::Mutex& mutex, bool lock) +{ + if(!element) + return; + + if(lock) + mutex.lock(); + + set.insert(element); + + if(lock) + mutex.unlock(); +} + +/////////////////////////////////////////////////////////////////////////////// Actors + +void NpFactory::addRigidStatic(PxRigidStatic* npActor, bool lock) +{ + addToTracking(mActorTracking, npActor, mTrackingMutex, lock); +} + +void NpFactory::addRigidDynamic(PxRigidDynamic* npBody, bool lock) +{ + addToTracking(mActorTracking, npBody, mTrackingMutex, lock); +} + +void NpFactory::addShape(PxShape* shape, bool lock) +{ + addToTracking(mShapeTracking, shape, mTrackingMutex, lock); +} + +void NpFactory::onActorRelease(PxActor* a) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mActorTracking.erase(a); +} + +void NpFactory::onShapeRelease(PxShape* a) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mShapeTracking.erase(a); +} + +void NpFactory::addArticulation(PxArticulationBase* npArticulation, bool lock) +{ + addToTracking(mArticulationTracking, npArticulation, mTrackingMutex, lock); +} + +namespace +{ + PxArticulationBase* createArticulation() + { + NpArticulation* npArticulation = NpFactory::getInstance().createNpArticulation(); + if (!npArticulation) + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Articulation initialization failed: returned NULL."); + + return npArticulation; + } + + PxArticulationBase* createArticulationRC() + { + NpArticulationReducedCoordinate* npArticulation = NpFactory::getInstance().createNpArticulationRC(); + if (!npArticulation) + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Articulation initialization failed: returned NULL."); + + return npArticulation; + } + + NpArticulationLink* createArticulationLink(PxArticulationBase&root, NpArticulationLink* parent, const PxTransform& pose) + { + PX_CHECK_AND_RETURN_NULL(pose.isValid(),"Supplied PxArticulation pose is not valid. Articulation link creation method returns NULL."); + PX_CHECK_AND_RETURN_NULL((!parent || (&parent->getRoot() == &root)), "specified parent link is not part of the destination articulation. Articulation link creation method returns NULL."); + + NpArticulationLink* npArticulationLink = NpFactory::getInstance().createNpArticulationLink(root, parent, pose); + if (!npArticulationLink) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Articulation link initialization failed: returned NULL."); + return NULL; + } + + if (parent) + { + PxTransform parentPose = parent->getCMassLocalPose().transformInv(pose); + PxTransform childPose = PxTransform(PxIdentity); + + PxArticulationJointBase* npArticulationJoint = root.createArticulationJoint(*parent, parentPose, *npArticulationLink, childPose); + if (!npArticulationJoint) + { + PX_DELETE(npArticulationLink); + + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Articulation link initialization failed due to joint creation failure: returned NULL."); + return NULL; + } + + npArticulationLink->setInboundJoint(*npArticulationJoint); + } + + return npArticulationLink; + } + + // pointers to functions above, initialized during subsystem registration + static PxArticulationBase* (*sCreateArticulationFn)() = 0; + static PxArticulationBase* (*sCreateArticulationRCFn)() = 0; + static NpArticulationLink* (*sCreateArticulationLinkFn)(PxArticulationBase&, NpArticulationLink* parent, const PxTransform& pose) = 0; +} + +void NpFactory::registerArticulations() +{ + sCreateArticulationFn = &::createArticulation; + sCreateArticulationLinkFn = &::createArticulationLink; +} + +void NpFactory::registerArticulationRCs() +{ + sCreateArticulationRCFn = &::createArticulationRC; + sCreateArticulationLinkFn = &::createArticulationLink; +} + +void NpFactory::releaseArticulationToPool(PxArticulationBase& articulation) +{ + + PX_ASSERT(articulation.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + if (articulation.getType() == PxArticulationBase::eMaximumCoordinate) + { + Ps::Mutex::ScopedLock lock(mArticulationPoolLock); + mArticulationPool.destroy(static_cast(&articulation)); + } + else + { + Ps::Mutex::ScopedLock lock(mArticulationRCPoolLock); + mArticulationRCPool.destroy(static_cast(&articulation)); + } +} + +NpArticulation* NpFactory::createNpArticulation() +{ + Ps::Mutex::ScopedLock lock(mArticulationPoolLock); + return mArticulationPool.construct(); +} + +PxArticulation* NpFactory::createArticulation() +{ + if(!sCreateArticulationFn) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Articulations not registered: returned NULL."); + return NULL; + } + + PxArticulationBase* npArticulation = (*sCreateArticulationFn)(); + if(npArticulation) + addArticulation(npArticulation); + + return static_cast(npArticulation); +} + +PxArticulationReducedCoordinate* NpFactory::createArticulationRC() +{ + if (!sCreateArticulationRCFn) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Articulations not registered: returned NULL."); + return NULL; + } + + PxArticulationBase* npArticulation = (*sCreateArticulationRCFn)(); + if (npArticulation) + addArticulation(npArticulation); + + return static_cast(npArticulation); +} + +NpArticulationReducedCoordinate* NpFactory::createNpArticulationRC() +{ + Ps::Mutex::ScopedLock lock(mArticulationRCPoolLock); + return mArticulationRCPool.construct(); +} + +void NpFactory::onArticulationRelease(PxArticulationBase* a) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mArticulationTracking.erase(a); +} + +NpArticulationLink* NpFactory::createNpArticulationLink(PxArticulationBase&root, NpArticulationLink* parent, const PxTransform& pose) +{ + NpArticulationLink* npArticulationLink; + { + Ps::Mutex::ScopedLock lock(mArticulationLinkPoolLock); + npArticulationLink = mArticulationLinkPool.construct(pose, root, parent); + } + return npArticulationLink; +} + +void NpFactory::releaseArticulationLinkToPool(NpArticulationLink& articulationLink) +{ + PX_ASSERT(articulationLink.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mArticulationLinkPoolLock); + mArticulationLinkPool.destroy(&articulationLink); +} + +PxArticulationLink* NpFactory::createArticulationLink(PxArticulationBase& root, NpArticulationLink* parent, const PxTransform& pose) +{ + if(!sCreateArticulationLinkFn) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Articulations not registered: returned NULL."); + return NULL; + } + + return (*sCreateArticulationLinkFn)(root, parent, pose); +} + +NpArticulationJoint* NpFactory::createNpArticulationJoint(NpArticulationLink& parent, const PxTransform& parentFrame, NpArticulationLink& child, const PxTransform& childFrame) +{ + NpArticulationJoint* npArticulationJoint; + { + Ps::Mutex::ScopedLock lock(mArticulationJointPoolLock); + npArticulationJoint = mArticulationJointPool.construct(parent, parentFrame, child, childFrame); + } + return npArticulationJoint; +} + +void NpFactory::releaseArticulationJointToPool(NpArticulationJoint& articulationJoint) +{ + PX_ASSERT(articulationJoint.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mArticulationJointPoolLock); + mArticulationJointPool.destroy(&articulationJoint); +} + +NpArticulationJointReducedCoordinate* NpFactory::createNpArticulationJointRC(NpArticulationLink& parent, const PxTransform& parentFrame, NpArticulationLink& child, const PxTransform& childFrame) +{ + NpArticulationJointReducedCoordinate* npArticulationJoint; + { + Ps::Mutex::ScopedLock lock(mArticulationJointRCPoolLock); + npArticulationJoint = mArticulationRCJointPool.construct(parent, parentFrame, child, childFrame); + } + return npArticulationJoint; +} + +void NpFactory::releaseArticulationJointRCToPool(NpArticulationJointReducedCoordinate& articulationJoint) +{ + PX_ASSERT(articulationJoint.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mArticulationJointRCPoolLock); + mArticulationRCJointPool.destroy(&articulationJoint); +} + +/////////////////////////////////////////////////////////////////////////////// constraint + +void NpFactory::addConstraint(PxConstraint* npConstraint, bool lock) +{ + addToTracking(mConstraintTracking, npConstraint, mTrackingMutex, lock); +} + +PxConstraint* NpFactory::createConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) +{ + PX_CHECK_AND_RETURN_NULL((actor0 && !actor0->is()) || (actor1 && !actor1->is()), "createConstraint: At least one actor must be dynamic or an articulation link"); + + NpConstraint* npConstraint; + { + Ps::Mutex::ScopedLock lock(mConstraintPoolLock); + npConstraint = mConstraintPool.construct(actor0, actor1, connector, shaders, dataSize); + } + addConstraint(npConstraint); + return npConstraint; +} + +void NpFactory::releaseConstraintToPool(NpConstraint& constraint) +{ + PX_ASSERT(constraint.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mConstraintPoolLock); + mConstraintPool.destroy(&constraint); +} + +void NpFactory::onConstraintRelease(PxConstraint* c) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mConstraintTracking.erase(c); +} + +/////////////////////////////////////////////////////////////////////////////// aggregate + +void NpFactory::addAggregate(PxAggregate* npAggregate, bool lock) +{ + addToTracking(mAggregateTracking, npAggregate, mTrackingMutex, lock); +} + +PxAggregate* NpFactory::createAggregate(PxU32 maxActors, bool selfCollisions) +{ + NpAggregate* npAggregate; + { + Ps::Mutex::ScopedLock lock(mAggregatePoolLock); + npAggregate = mAggregatePool.construct(maxActors, selfCollisions); + } + + addAggregate(npAggregate); + return npAggregate; +} + +void NpFactory::releaseAggregateToPool(NpAggregate& aggregate) +{ + PX_ASSERT(aggregate.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mAggregatePoolLock); + mAggregatePool.destroy(&aggregate); +} + +void NpFactory::onAggregateRelease(PxAggregate* a) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mAggregateTracking.erase(a); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxMaterial* NpFactory::createMaterial(PxReal staticFriction, PxReal dynamicFriction, PxReal restitution) +{ + PX_CHECK_AND_RETURN_NULL(dynamicFriction >= 0.0f, "createMaterial: dynamicFriction must be >= 0."); + PX_CHECK_AND_RETURN_NULL(staticFriction >= 0.0f, "createMaterial: staticFriction must be >= 0."); + PX_CHECK_AND_RETURN_NULL(restitution >= 0.0f || restitution <= 1.0f, "createMaterial: restitution must be between 0 and 1."); + + Sc::MaterialData data; + data.staticFriction = staticFriction; + data.dynamicFriction = dynamicFriction; + data.restitution = restitution; + + NpMaterial* npMaterial; + { + Ps::Mutex::ScopedLock lock(mMaterialPoolLock); + npMaterial = mMaterialPool.construct(data); + } + return npMaterial; +} + +void NpFactory::releaseMaterialToPool(NpMaterial& material) +{ + PX_ASSERT(material.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mMaterialPoolLock); + mMaterialPool.destroy(&material); +} + +/////////////////////////////////////////////////////////////////////////////// + +NpConnectorArray* NpFactory::acquireConnectorArray() +{ + Ps::MutexT<>::ScopedLock l(mConnectorArrayPoolLock); + return mConnectorArrayPool.construct(); +} + +void NpFactory::releaseConnectorArray(NpConnectorArray* array) +{ + Ps::MutexT<>::ScopedLock l(mConnectorArrayPoolLock); + mConnectorArrayPool.destroy(array); +} + +/////////////////////////////////////////////////////////////////////////////// + +NpShape* NpFactory::createShape(const PxGeometry& geometry, + PxShapeFlags shapeFlags, + PxMaterial*const* materials, + PxU16 materialCount, + bool isExclusive) +{ + switch(geometry.getType()) + { + case PxGeometryType::eBOX: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eSPHERE: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eCAPSULE: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eCONVEXMESH: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::ePLANE: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eHEIGHTFIELD: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eTRIANGLEMESH: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ASSERT(0); + } + + // + // Check for invalid material table setups + // + +#if PX_CHECKED + if (!NpShape::checkMaterialSetup(geometry, "Shape creation", materials, materialCount)) + return NULL; +#endif + + Ps::InlineArray materialIndices("NpFactory::TmpMaterialIndexBuffer"); + materialIndices.resize(materialCount); + if(materialCount == 1) + materialIndices[0] = Ps::to16((static_cast(materials[0]))->getHandle()); + else + NpMaterial::getMaterialIndices(materials, materialIndices.begin(), materialCount); + + NpShape* npShape; + { + Ps::Mutex::ScopedLock lock(mShapePoolLock); + PxU16* mi = materialIndices.begin(); // required to placate pool constructor arg passing + npShape = mShapePool.construct(geometry, shapeFlags, mi, materialCount, isExclusive); + } + + if(!npShape) + return NULL; + + for(PxU32 i=0; i < materialCount; i++) + static_cast(npShape->getMaterial(i))->incRefCount(); + + addShape(npShape); + + return npShape; +} + +void NpFactory::releaseShapeToPool(NpShape& shape) +{ + PX_ASSERT(shape.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mShapePoolLock); + mShapePool.destroy(&shape); +} + +PxU32 NpFactory::getNbShapes() const +{ + // PT: TODO: isn't there a lock missing here? See usage in MeshFactory + return mShapeTracking.size(); +} + +PxU32 NpFactory::getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + // PT: TODO: isn't there a lock missing here? See usage in MeshFactory + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mShapeTracking.getEntries(), mShapeTracking.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxRigidStatic* NpFactory::createRigidStatic(const PxTransform& pose) +{ + PX_CHECK_AND_RETURN_NULL(pose.isValid(), "pose is not valid. createRigidStatic returns NULL."); + + NpRigidStatic* npActor; + + { + Ps::Mutex::ScopedLock lock(mRigidStaticPoolLock); + npActor = mRigidStaticPool.construct(pose); + } + + addRigidStatic(npActor); + return npActor; +} + +void NpFactory::releaseRigidStaticToPool(NpRigidStatic& rigidStatic) +{ + PX_ASSERT(rigidStatic.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mRigidStaticPoolLock); + mRigidStaticPool.destroy(&rigidStatic); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxRigidDynamic* NpFactory::createRigidDynamic(const PxTransform& pose) +{ + PX_CHECK_AND_RETURN_NULL(pose.isValid(), "pose is not valid. createRigidDynamic returns NULL."); + + NpRigidDynamic* npBody; + { + Ps::Mutex::ScopedLock lock(mRigidDynamicPoolLock); + npBody = mRigidDynamicPool.construct(pose); + } + addRigidDynamic(npBody); + return npBody; +} + +void NpFactory::releaseRigidDynamicToPool(NpRigidDynamic& rigidDynamic) +{ + PX_ASSERT(rigidDynamic.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mRigidDynamicPoolLock); + mRigidDynamicPool.destroy(&rigidDynamic); +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: this function is here to minimize the amount of locks when deserializing a collection +void NpFactory::addCollection(const Cm::Collection& collection) +{ + + PxU32 nb = collection.getNbObjects(); + const Ps::Pair* entries = collection.internalGetObjects(); + // PT: we take the lock only once, here + Ps::Mutex::ScopedLock lock(mTrackingMutex); + + for(PxU32 i=0;igetConcreteType(); +////////////////////////// + if(serialType==PxConcreteType::eHEIGHTFIELD) + { + Gu::HeightField* gu = static_cast(s); + gu->setMeshFactory(this); + addHeightField(gu, false); + } + else if(serialType==PxConcreteType::eCONVEX_MESH) + { + Gu::ConvexMesh* gu = static_cast(s); + gu->setMeshFactory(this); + addConvexMesh(gu, false); + } + else if(serialType==PxConcreteType::eTRIANGLE_MESH_BVH33 || serialType==PxConcreteType::eTRIANGLE_MESH_BVH34) + { + Gu::TriangleMesh* gu = static_cast(s); + gu->setMeshFactory(this); + addTriangleMesh(gu, false); + } + else if(serialType==PxConcreteType::eRIGID_DYNAMIC) + { + NpRigidDynamic* np = static_cast(s); + addRigidDynamic(np, false); + } + else if(serialType==PxConcreteType::eRIGID_STATIC) + { + NpRigidStatic* np = static_cast(s); + addRigidStatic(np, false); + } + else if(serialType==PxConcreteType::eSHAPE) + { + NpShape* np = static_cast(s); + addShape(np, false); + } + else if(serialType==PxConcreteType::eMATERIAL) + { + } + else if(serialType==PxConcreteType::eCONSTRAINT) + { + NpConstraint* np = static_cast(s); + addConstraint(np, false); + } + else if(serialType==PxConcreteType::eAGGREGATE) + { + NpAggregate* np = static_cast(s); + addAggregate(np, false); + + // PT: TODO: double-check this.... is it correct? + for(PxU32 j=0;jgetCurrentSizeFast();j++) + { + PxBase* actor = np->getActorFast(j); + const PxType serialType1 = actor->getConcreteType(); + + if(serialType1==PxConcreteType::eRIGID_STATIC) + addRigidStatic(static_cast(actor), false); + else if(serialType1==PxConcreteType::eRIGID_DYNAMIC) + addRigidDynamic(static_cast(actor), false); + else if(serialType1==PxConcreteType::eARTICULATION_LINK) + {} + else PX_ASSERT(0); + } + } + else if(serialType==PxConcreteType::eARTICULATION) + { + NpArticulation* np = static_cast(s); + addArticulation(np, false); + } + else if(serialType==PxConcreteType::eARTICULATION_LINK) + { +// NpArticulationLink* np = static_cast(s); + } + else if(serialType==PxConcreteType::eARTICULATION_JOINT) + { +// NpArticulationJoint* np = static_cast(s); + } + else + { +// assert(0); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#if PX_SUPPORT_PVD +void NpFactory::setNpFactoryListener( NpFactoryListener& inListener) +{ + mNpFactoryListener = &inListener; + addFactoryListener(inListener); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// these calls are issued from the Scb layer when buffered deletes are issued. +// TODO: we should really push these down as a virtual interface that is part of Scb's reqs +// to eliminate this link-time dep. + +static void NpDestroyRigidActor(Scb::RigidStatic& scb) +{ + NpRigidStatic* np = const_cast(getNpRigidStatic(&scb)); + + void* ud = np->userData; + + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseRigidStaticToPool(*np); + else + np->~NpRigidStatic(); + + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, ud); +} + +static void NpDestroyRigidDynamic(Scb::Body& scb) +{ + NpRigidDynamic* np = const_cast(getNpRigidDynamic(&scb)); + + void* ud = np->userData; + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseRigidDynamicToPool(*np); + else + np->~NpRigidDynamic(); + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, ud); +} + +static void NpDestroyArticulationLink(Scb::Body& scb) +{ + NpArticulationLink* np = const_cast(getNpArticulationLink(&scb)); + + void* ud = np->userData; + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseArticulationLinkToPool(*np); + else + np->~NpArticulationLink(); + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, ud); +} + +static void NpDestroyArticulationJoint(Scb::ArticulationJoint& scb) +{ + PxArticulationJointBase* np = scb.getScArticulationJoint().getRoot(); + + if (np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + { + PxArticulationJointImpl* impl = np->getImpl(); + if (impl->mType == PxArticulationBase::eMaximumCoordinate) + { + NpFactory::getInstance().releaseArticulationJointToPool(*static_cast(np)); + } + else + { + NpFactory::getInstance().releaseArticulationJointRCToPool(*static_cast(np)); + } + } + else + np->~PxArticulationJointBase(); + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, NULL); +} + +static void NpDestroyArticulation(Scb::Articulation& scb) +{ + PxArticulationBase* artic = const_cast(static_cast(getNpArticulation(&scb))); + + void* ud = artic->PxArticulationBase::userData; + if (artic->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseArticulationToPool(*artic); + else + artic->~PxArticulationBase(); + NpPhysics::getInstance().notifyDeletionListenersMemRelease(artic, ud); +} + +static void NpDestroyAggregate(Scb::Aggregate& scb) +{ + NpAggregate* np = const_cast(getNpAggregate(&scb)); + + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseAggregateToPool(*np); + else + np->~NpAggregate(); + + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, NULL); +} + +static void NpDestroyShape(Scb::Shape& scb) +{ + NpShape* np = const_cast(getNpShape(&scb)); + + void* ud = np->userData; + + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseShapeToPool(*np); + else + np->~NpShape(); + + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, ud); +} + +static void NpDestroyConstraint(Scb::Constraint& scb) +{ + const size_t offset = size_t(&(reinterpret_cast(0)->getScbConstraint())); + NpConstraint* np = reinterpret_cast(reinterpret_cast(&scb)-offset); + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseConstraintToPool(*np); + else + np->~NpConstraint(); + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, NULL); +} + +namespace physx +{ + void NpDestroy(Scb::Base& base) + { + switch(base.getScbType()) + { + case ScbType::eSHAPE_EXCLUSIVE: + case ScbType::eSHAPE_SHARED: { NpDestroyShape(static_cast(base)); }break; + case ScbType::eBODY: { NpDestroyRigidDynamic(static_cast(base)); }break; + case ScbType::eBODY_FROM_ARTICULATION_LINK: { NpDestroyArticulationLink(static_cast(base)); }break; + case ScbType::eRIGID_STATIC: { NpDestroyRigidActor(static_cast(base)); }break; + case ScbType::eCONSTRAINT: { NpDestroyConstraint(static_cast(base)); }break; + case ScbType::eARTICULATION: { NpDestroyArticulation(static_cast(base)); }break; + case ScbType::eARTICULATION_JOINT: { NpDestroyArticulationJoint(static_cast(base)); }break; + case ScbType::eAGGREGATE: { NpDestroyAggregate(static_cast(base)); }break; + case ScbType::eUNDEFINED: + case ScbType::eTYPE_COUNT: + PX_ALWAYS_ASSERT_MESSAGE("NpDestroy: missing type!"); + break; + } + } +} + diff --git a/src/PhysX/physx/source/physx/src/NpFactory.h b/src/PhysX/physx/source/physx/src/NpFactory.h new file mode 100644 index 000000000..d36249995 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpFactory.h @@ -0,0 +1,266 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NP_FACTORY +#define PX_PHYSICS_NP_FACTORY + +#include "PsPool.h" +#include "PsMutex.h" +#include "PsHashSet.h" + +#include "GuMeshFactory.h" +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" +#include "PxShape.h" +#include "PxArticulationBase.h" + +namespace physx +{ + +class PxActor; + +class PxRigidActor; + +class PxRigidStatic; +class NpRigidStatic; + +class PxRigidDynamic; +class NpRigidDynamic; + +class NpConnectorArray; + +struct PxConstraintShaderTable; +class PxConstraintConnector; +class PxConstraint; +class NpConstraint; + +class PxArticulation; +class PxArticulationReducedCoordinate; +class NpArticulation; +class NpArticulationReducedCoordinate; +class PxArticulationLink; +class NpArticulationLink; +class NpArticulationJoint; +class NpArticulationJointReducedCoordinate; + +class PxMaterial; +class NpMaterial; + +class PxGeometry; + +class NpShape; + +class NpScene; + +class PxAggregate; +class NpAggregate; + +class NpConnectorArray; +class NpPtrTableStorageManager; + +namespace Cm +{ + class Collection; +} + +namespace Scb +{ + class RigidObject; + class Articulation; +} + +namespace pvdsdk +{ + class PvdDataStream; + class PsPvd; +} + +class NpFactoryListener : public GuMeshFactoryListener +{ +protected: + virtual ~NpFactoryListener(){} +}; + +class NpFactory : public GuMeshFactory +{ + PX_NOCOPY(NpFactory) +public: + NpFactory(); +private: + ~NpFactory(); +public: + static void createInstance(); + static void destroyInstance(); + static void registerArticulations(); + static void registerArticulationRCs(); + + void release(); + + void addCollection(const Cm::Collection& collection); + + PX_INLINE static NpFactory& getInstance() { return *mInstance; } + + // Rigid dynamic + PxRigidDynamic* createRigidDynamic(const PxTransform& pose); + void addRigidDynamic(PxRigidDynamic*, bool lock=true); + void releaseRigidDynamicToPool(NpRigidDynamic&); +// PT: TODO: add missing functions +// PxU32 getNbRigidDynamics() const; +// PxU32 getRigidDynamics(PxRigidDynamic** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Rigid static + PxRigidStatic* createRigidStatic(const PxTransform& pose); + void addRigidStatic(PxRigidStatic*, bool lock=true); + void releaseRigidStaticToPool(NpRigidStatic&); +// PT: TODO: add missing functions +// PxU32 getNbRigidStatics() const; +// PxU32 getRigidStatics(PxRigidStatic** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Shapes + NpShape* createShape(const PxGeometry& geometry, PxShapeFlags shapeFlags, PxMaterial*const* materials, PxU16 materialCount, bool isExclusive); + void addShape(PxShape*, bool lock=true); + void releaseShapeToPool(NpShape&); + PxU32 getNbShapes() const; + PxU32 getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Constraints + PxConstraint* createConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize); + void addConstraint(PxConstraint*, bool lock=true); + void releaseConstraintToPool(NpConstraint&); +// PT: TODO: add missing functions +// PxU32 getNbConstraints() const; +// PxU32 getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Articulations + PxArticulation* createArticulation(); + void addArticulation(PxArticulationBase*, bool lock=true); + void releaseArticulationToPool(PxArticulationBase& articulation); + PxArticulationReducedCoordinate* createArticulationRC(); + NpArticulation* createNpArticulation(); + NpArticulationReducedCoordinate* createNpArticulationRC(); +// PT: TODO: add missing functions +// PxU32 getNbArticulations() const; +// PxU32 getArticulations(PxArticulation** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Articulation links + NpArticulationLink* createNpArticulationLink(PxArticulationBase&root, NpArticulationLink* parent, const PxTransform& pose); + void releaseArticulationLinkToPool(NpArticulationLink& articulation); + PxArticulationLink* createArticulationLink(PxArticulationBase&, NpArticulationLink* parent, const PxTransform& pose); + + // Articulation joints + NpArticulationJoint* createNpArticulationJoint(NpArticulationLink& parent, const PxTransform& parentFrame, NpArticulationLink& child, const PxTransform& childFrame); + void releaseArticulationJointToPool(NpArticulationJoint& articulationJoint); + NpArticulationJointReducedCoordinate* createNpArticulationJointRC(NpArticulationLink& parent, const PxTransform& parentFrame, NpArticulationLink& child, const PxTransform& childFrame); + void releaseArticulationJointRCToPool(NpArticulationJointReducedCoordinate& articulationJoint); + + // Aggregates + PxAggregate* createAggregate(PxU32 maxActors, bool selfCollisions); + void addAggregate(PxAggregate*, bool lock=true); + void releaseAggregateToPool(NpAggregate&); +// PT: TODO: add missing functions +// PxU32 getNbAggregates() const; +// PxU32 getAggregates(PxAggregate** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Materials + PxMaterial* createMaterial(PxReal staticFriction, PxReal dynamicFriction, PxReal restitution); + void releaseMaterialToPool(NpMaterial& material); + + // It's easiest to track these uninvasively, so it's OK to use the Px pointers + void onActorRelease(PxActor*); + void onConstraintRelease(PxConstraint*); + void onAggregateRelease(PxAggregate*); + void onArticulationRelease(PxArticulationBase*); + void onShapeRelease(PxShape*); + + NpConnectorArray* acquireConnectorArray(); + void releaseConnectorArray(NpConnectorArray*); + + NpPtrTableStorageManager& getPtrTableStorageManager() { return *mPtrTableStorageManager; } + +#if PX_SUPPORT_PVD + void setNpFactoryListener( NpFactoryListener& ); +#endif + +private: + void releaseExclusiveShapeUserReferences(); + + Ps::Pool mConnectorArrayPool; + Ps::Mutex mConnectorArrayPoolLock; + + NpPtrTableStorageManager* mPtrTableStorageManager; + + Ps::HashSet mAggregateTracking; + Ps::HashSet mArticulationTracking; + Ps::HashSet mConstraintTracking; + Ps::HashSet mActorTracking; + Ps::CoalescedHashSet mShapeTracking; + + Ps::Pool2 mRigidDynamicPool; + Ps::Mutex mRigidDynamicPoolLock; + + Ps::Pool2 mRigidStaticPool; + Ps::Mutex mRigidStaticPoolLock; + + Ps::Pool2 mShapePool; + Ps::Mutex mShapePoolLock; + + Ps::Pool2 mAggregatePool; + Ps::Mutex mAggregatePoolLock; + + Ps::Pool2 mConstraintPool; + Ps::Mutex mConstraintPoolLock; + + Ps::Pool2 mMaterialPool; + Ps::Mutex mMaterialPoolLock; + + Ps::Pool2 mArticulationPool; + Ps::Mutex mArticulationPoolLock; + + Ps::Pool2 mArticulationRCPool; + Ps::Mutex mArticulationRCPoolLock; + + Ps::Pool2 mArticulationLinkPool; + Ps::Mutex mArticulationLinkPoolLock; + + Ps::Pool2 mArticulationJointPool; + Ps::Mutex mArticulationJointPoolLock; + + Ps::Pool2 mArticulationRCJointPool; + Ps::Mutex mArticulationJointRCPoolLock; + + static NpFactory* mInstance; + +#if PX_SUPPORT_PVD + NpFactoryListener* mNpFactoryListener; +#endif +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpMaterial.cpp b/src/PhysX/physx/source/physx/src/NpMaterial.cpp new file mode 100644 index 000000000..b743c15cb --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpMaterial.cpp @@ -0,0 +1,205 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpMaterial.h" +#include "NpPhysics.h" +#include "CmUtils.h" + +using namespace physx; + +NpMaterial::NpMaterial(const Sc::MaterialCore& desc) +: PxMaterial(PxConcreteType::eMATERIAL, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mMaterial(desc) +{ + mMaterial.setNxMaterial(this); // back-reference +} + +NpMaterial::~NpMaterial() +{ + NpPhysics::getInstance().removeMaterialFromTable(*this); +} + +// PX_SERIALIZATION +void NpMaterial::resolveReferences(PxDeserializationContext&) +{ + // ### this one could be automated if NpMaterial would inherit from MaterialCore + // ### well actually in that case the pointer would not even be needed.... + mMaterial.setNxMaterial(this); // Resolve MaterialCore::mNxMaterial + + // Maybe not the best place to do it but it has to be done before the shapes resolve material indices + // since the material index translation table is needed there. This requires that the materials have + // been added to the table already. + NpPhysics::getInstance().addMaterial(this); +} + +void NpMaterial::onRefCountZero() +{ + void* ud = userData; + + if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseMaterialToPool(*this); + else + this->~NpMaterial(); + + NpPhysics::getInstance().notifyDeletionListenersMemRelease(this, ud); +} + +NpMaterial* NpMaterial::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpMaterial* obj = new (address) NpMaterial(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(NpMaterial); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +void NpMaterial::release() +{ + decRefCount(); +} + +void NpMaterial::acquireReference() +{ + incRefCount(); +} + +PxU32 NpMaterial::getReferenceCount() const +{ + return getRefCount(); +} + +PX_INLINE void NpMaterial::updateMaterial() +{ + NpPhysics::getInstance().updateMaterial(*this); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setDynamicFriction(PxReal x) +{ + PX_CHECK_AND_RETURN(PxIsFinite(x), "PxMaterial::setDynamicFriction: invalid float"); + mMaterial.dynamicFriction = x; + + updateMaterial(); +} + +PxReal NpMaterial::getDynamicFriction() const +{ + return mMaterial.dynamicFriction; +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setStaticFriction(PxReal x) +{ + PX_CHECK_AND_RETURN(PxIsFinite(x), "PxMaterial::setStaticFriction: invalid float"); + mMaterial.staticFriction = x; + + updateMaterial(); +} + +PxReal NpMaterial::getStaticFriction() const +{ + return mMaterial.staticFriction; +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setRestitution(PxReal x) +{ + PX_CHECK_AND_RETURN(PxIsFinite(x), "PxMaterial::setRestitution: invalid float"); + PX_CHECK_MSG(((x >= 0.0f) && (x <= 1.0f)), "PxMaterial::setRestitution: Restitution value has to be in [0,1]!"); + if ((x < 0.0f) || (x > 1.0f)) + { + PxClamp(x, 0.0f, 1.0f); + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxMaterial::setRestitution: Invalid value %f was clamped to [0,1]!", PxF64(x)); + } + mMaterial.restitution = x; + + updateMaterial(); +} + +PxReal NpMaterial::getRestitution() const +{ + return mMaterial.restitution; +} + +///////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setFlag(PxMaterialFlag::Enum flag, bool value) +{ + if (value) + mMaterial.flags |= flag; + else + mMaterial.flags &= ~PxMaterialFlags(flag); + + updateMaterial(); +} + +void NpMaterial::setFlags(PxMaterialFlags inFlags) +{ + mMaterial.flags = inFlags; + updateMaterial(); +} + +PxMaterialFlags NpMaterial::getFlags() const +{ + return mMaterial.flags; +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setFrictionCombineMode(PxCombineMode::Enum x) +{ + mMaterial.setFrictionCombineMode(x); + + updateMaterial(); +} + +PxCombineMode::Enum NpMaterial::getFrictionCombineMode() const +{ + return mMaterial.getFrictionCombineMode(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setRestitutionCombineMode(PxCombineMode::Enum x) +{ + mMaterial.setRestitutionCombineMode(x); + updateMaterial(); +} + +PxCombineMode::Enum NpMaterial::getRestitutionCombineMode() const +{ + return mMaterial.getRestitutionCombineMode(); +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/physx/src/NpMaterial.h b/src/PhysX/physx/source/physx/src/NpMaterial.h new file mode 100644 index 000000000..722b9b379 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpMaterial.h @@ -0,0 +1,122 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_MATERIAL +#define PX_PHYSICS_NP_MATERIAL + +#include "PxMaterial.h" +#include "ScMaterialCore.h" +#include "PsUserAllocated.h" +#include "CmRefCountable.h" +#include "PsUtilities.h" + +// PX_SERIALIZATION +#include "PxSerialFramework.h" +//~PX_SERIALIZATION + +namespace physx +{ + +// Compared to other objects, materials are special since they belong to the SDK and not to scenes +// (similar to meshes). That's why the NpMaterial does have direct access to the core material instead +// of having a buffered interface for it. Scenes will have copies of the SDK material table and there +// the materials will be buffered. + + +class NpMaterial : public PxMaterial, public Ps::UserAllocated, public Cm::RefCountable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpMaterial(PxBaseFlags baseFlags) : PxMaterial(baseFlags), Cm::RefCountable(PxEmpty), mMaterial(PxEmpty) {} + virtual void onRefCountZero(); + virtual void resolveReferences(PxDeserializationContext& context); + static NpMaterial* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void exportExtraData(PxSerializationContext&) {} + void importExtraData(PxDeserializationContext&) {} + virtual void requiresObjects(PxProcessPxBaseCallback&){} +//~PX_SERIALIZATION + NpMaterial(const Sc::MaterialCore& desc); + ~NpMaterial(); + + virtual void release(); + + virtual void acquireReference(); + virtual PxU32 getReferenceCount() const; + + virtual void setDynamicFriction(PxReal); + virtual PxReal getDynamicFriction() const; + virtual void setStaticFriction(PxReal); + virtual PxReal getStaticFriction() const; + virtual void setRestitution(PxReal); + virtual PxReal getRestitution() const; + virtual void setFlag(PxMaterialFlag::Enum flag, bool value); + virtual void setFlags(PxMaterialFlags inFlags); + virtual PxMaterialFlags getFlags() const; + virtual void setFrictionCombineMode(PxCombineMode::Enum); + virtual PxCombineMode::Enum getFrictionCombineMode() const; + virtual void setRestitutionCombineMode(PxCombineMode::Enum); + virtual PxCombineMode::Enum getRestitutionCombineMode() const; + + PX_INLINE const Sc::MaterialCore& getScMaterial() const { return mMaterial; } + PX_INLINE Sc::MaterialCore& getScMaterial() { return mMaterial; } + PX_INLINE PxU32 getHandle() const { return mMaterial.getMaterialIndex();} + PX_INLINE void setHandle(PxU32 handle) { return mMaterial.setMaterialIndex(handle);} + + PX_FORCE_INLINE static void getMaterialIndices(PxMaterial*const* materials, PxU16* materialIndices, PxU32 materialCount); + +private: + PX_INLINE void updateMaterial(); + +// PX_SERIALIZATION +public: +//~PX_SERIALIZATION + Sc::MaterialCore mMaterial; +}; + + +PX_FORCE_INLINE void NpMaterial::getMaterialIndices(PxMaterial*const* materials, PxU16* materialIndices, PxU32 materialCount) +{ + for(PxU32 i=0; i < materialCount; i++) + { + materialIndices[i] = Ps::to16((static_cast(materials[i]))->getHandle()); + } +} + + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpMaterialManager.h b/src/PhysX/physx/source/physx/src/NpMaterialManager.h new file mode 100644 index 000000000..d3c38a842 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpMaterialManager.h @@ -0,0 +1,164 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef NP_MATERIALMANAGER +#define NP_MATERIALMANAGER + +#include "foundation/PxMemory.h" +#include "NpMaterial.h" +#include "CmIDPool.h" + +namespace physx +{ + class NpMaterialManager + { + public: + NpMaterialManager() + { + const PxU32 matCount = 128; + mMaterials = reinterpret_cast(PX_ALLOC(sizeof(NpMaterial*) * matCount, "NpMaterialManager::initialise")); + mMaxMaterials = matCount; + PxMemZero(mMaterials, sizeof(NpMaterial*)*mMaxMaterials); + } + + ~NpMaterialManager() {} + + void releaseMaterials() + { + for(PxU32 i=0; igetHandle(); + mHandleManager.freeID(handle); + mMaterials[i]->release(); + mMaterials[i] = NULL; + } + } + PX_FREE(mMaterials); + } + + bool setMaterial(NpMaterial& mat) + { + const PxU32 materialIndex = mHandleManager.getNewID(); + + if(materialIndex >= mMaxMaterials) + resize(); + + mMaterials[materialIndex] = &mat; + mat.setHandle(materialIndex); + return true; + } + + void updateMaterial(NpMaterial& mat) + { + mMaterials[mat.getHandle()] = &mat; + } + + PX_FORCE_INLINE PxU32 getNumMaterials() const + { + return mHandleManager.getNumUsedID(); + } + + void removeMaterial(NpMaterial& mat) + { + const PxU32 handle = mat.getHandle(); + if(handle != MATERIAL_INVALID_HANDLE) + { + mMaterials[handle] = NULL; + mHandleManager.freeID(handle); + } + } + + PX_FORCE_INLINE NpMaterial* getMaterial(const PxU32 index) const + { + PX_ASSERT(index < mMaxMaterials); + return mMaterials[index]; + } + + PX_FORCE_INLINE PxU32 getMaxSize() const + { + return mMaxMaterials; + } + + PX_FORCE_INLINE NpMaterial** getMaterials() const + { + return mMaterials; + } + + private: + void resize() + { + const PxU32 numMaterials = mMaxMaterials; + mMaxMaterials = mMaxMaterials*2; + + NpMaterial** mat = reinterpret_cast(PX_ALLOC(sizeof(NpMaterial*)*mMaxMaterials, "NpMaterialManager::resize")); + PxMemZero(mat, sizeof(NpMaterial*)*mMaxMaterials); + for(PxU32 i=0; i 72 => 64 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpMaterial) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpMaterial, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpMaterial, RefCountable) + + PX_DEF_BIN_METADATA_ITEM(stream, NpMaterial, void, userData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpMaterial, MaterialCore, mMaterial, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpConstraint::getBinaryMetaData(PxOutputStream& stream) +{ +// 136 => 140 => 144 => 128 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpConstraint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpConstraint, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, NpConstraint, PxRigidActor, mActor0, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpConstraint, PxRigidActor, mActor1, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpConstraint, Scb::Constraint, mConstraint, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpConstraint, bool, mIsDirty, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, NpConstraint, bool, mPaddingFromBool, PxMetaDataFlag::ePADDING) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpShapeManager::getBinaryMetaData(PxOutputStream& stream) +{ +// 8 bytes + PX_DEF_BIN_METADATA_CLASS(stream, NpShapeManager) + PX_DEF_BIN_METADATA_ITEM(stream, NpShapeManager, PtrTable, mShapes, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpShapeManager, PtrTable, mSceneQueryData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpShapeManager, PxU32, mSqCompoundId, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpShapeManager, Sq::PruningStructure, mPruningStructure, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpShape::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, NpInternalShapeFlags, PxU8) + +// 208 => 224 => 208 => 192 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpShape) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpShape, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpShape, RefCountable) + + // PxShape + PX_DEF_BIN_METADATA_ITEM(stream, NpShape, void, userData, PxMetaDataFlag::ePTR) + + // NpShape + PX_DEF_BIN_METADATA_ITEM(stream, NpShape, PxRigidActor, mActor, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpShape, Scb::Shape, mShape, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpShape, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpShape, PxI32, mExclusiveAndActorCount, 0) + + + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, NpShape, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpRigidStatic::getBinaryMetaData(PxOutputStream& stream) +{ +// 124 => 128 => 124 => 108 => 96 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpRigidStatic) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpRigidStatic, PxBase) +// PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpRigidStatic, NpRigidStaticT) // ### ??? + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpRigidStatic, NpActor) + + DefineMetaData_PxActor(NpRigidStatic) + DefineMetaData_NpRigidActorTemplate(NpRigidStatic) + + // NpRigidStatic + PX_DEF_BIN_METADATA_ITEM(stream, NpRigidStatic, Scb::RigidStatic, mRigidStatic, 0) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, NpRigidStatic, NpConnectorArray, mConnectorArray, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, NpRigidStatic, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpConnector::getBinaryMetaData(PxOutputStream& stream) +{ +// 8 bytes + PX_DEF_BIN_METADATA_CLASS(stream, NpConnector) + PX_DEF_BIN_METADATA_ITEM(stream, NpConnector, PxU8, mType, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, NpConnector, PxU8, mPadding, PxMetaDataFlag::ePADDING) + PX_DEF_BIN_METADATA_ITEM(stream, NpConnector, PxBase, mObject, PxMetaDataFlag::ePTR) +} + +void NpConnectorArray::getBinaryMetaData(PxOutputStream& stream) +{ +// 48 bytes + PX_DEF_BIN_METADATA_CLASS(stream, NpConnectorArray) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, NpConnectorArray, NpConnector, mBuffer, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpConnectorArray, bool, mBufferUsed, 0) + // PT: OMG this is so painful... I can't put the padding explicitly in the template + { PxMetaDataEntry tmp = {"char", "mPadding", 1 + PxU32(PX_OFFSET_OF_RT(NpConnectorArray, mBufferUsed)), 3, 3, 0, PxMetaDataFlag::ePADDING, 0}; PX_STORE_METADATA(stream, tmp); } + PX_DEF_BIN_METADATA_ITEM(stream, NpConnectorArray, NpConnector, mData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpConnectorArray, PxU32, mSize, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpConnectorArray, PxU32, mCapacity, 0) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, NpConnectorArray, NpConnector, mBufferUsed, mCapacity, PxMetaDataFlag::eCONTROL_FLIP|PxMetaDataFlag::eCOUNT_MASK_MSB, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpRigidDynamic::getBinaryMetaData(PxOutputStream& stream) +{ +// 368 => 352 => 304 => 288 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpRigidDynamic) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpRigidDynamic, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpRigidDynamic, NpActor) + + DefineMetaData_PxActor(NpRigidDynamic) + DefineMetaData_NpRigidActorTemplate(NpRigidDynamic) + DefineMetaData_NpRigidBodyTemplate(NpRigidDynamic) + + // NpRigidDynamic + + //------ Extra-data ------ + +// Extra data: +// - inline array from shape manager +// - optional constraint array + +// PX_DEF_BIN_METADATA_ITEM(stream,NpRigidDynamic, NpShapeManager, mShapeManager.mShapes, 0) + +/* + virtual void exportExtraData(PxOutputStream& stream) + { + mShapeManager.exportExtraData(stream); + ActorTemplateClass::exportExtraData(stream); + } +void NpActorTemplate::exportExtraData(PxOutputStream& stream) +{ + if(mConnectorArray) + { + stream.storeBuffer(mConnectorArray, sizeof(NpConnectorArray) + mConnectorArray->exportExtraData(stream); + } +} +*/ + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, NpRigidDynamic, NpConnectorArray, mConnectorArray, PX_SERIAL_ALIGN) +//### missing inline array data here... only works for "buffered" arrays so far +/* + Big issue: we can't output the "offset of" the inline array within the class, since the inline array itself is extra-data. But we need to read + the array itself to know if it's inline or not (the "is buffered" bool). So we need to read from the extra data! +*/ + +/* +[17:41:39] Gordon Yeoman nvidia: PxsBodyCore need to be 16-byte aligned for spu. If it is 128-byte aligned then that is a mistake. Feel free to change it to 16. +*/ + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, NpRigidDynamic, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpArticulationLinkArray::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, NpArticulationLinkArray) + PX_DEF_BIN_METADATA_ITEMS(stream, NpArticulationLinkArray, NpArticulationLink, mBuffer, PxMetaDataFlag::ePTR, 4) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLinkArray, bool, mBufferUsed, 0) + // PT: OMG this is so painful... I can't put the padding explicitely in the template + { PxMetaDataEntry tmp = {"char", "mPadding", 1 + PxU32(PX_OFFSET_OF_RT(NpArticulationLinkArray, mBufferUsed)), 3, 3, 0, PxMetaDataFlag::ePADDING, 0}; PX_STORE_METADATA(stream, tmp); } + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLinkArray, NpArticulationLink, mData, PxMetaDataFlag::ePTR) // ### + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLinkArray, PxU32, mSize, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLinkArray, PxU32, mCapacity, 0) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, NpArticulationLinkArray, NpArticulationLink, mBufferUsed, mCapacity, PxMetaDataFlag::eCONTROL_FLIP|PxMetaDataFlag::eCOUNT_MASK_MSB|PxMetaDataFlag::ePTR, 0) +} + +void NpArticulation::getBinaryMetaData(PxOutputStream& stream) +{ +// 92 => 108 => 104 => 116 => 120 => 104 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpArticulation) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpArticulation, PxBase) + + // PxArticulation + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxArticulationBase::Enum, PxU32) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, void, userData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, PxArticulationBase::Enum, mType, 0) + + // NpArticulation + //PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, PxArticulationImpl, mImpl, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, Scb::Articulation, mImpl.mArticulation, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, NpArticulationLinkArray, mImpl.mArticulationLinks, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, NpAggregate, mImpl.mAggregate, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, char, mImpl.mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, NpArticulation, mImpl.mName, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, PxU32, mImpl.mCacheVersion, 0) + +} + +void NpArticulationLink::getBinaryMetaData(PxOutputStream& stream) +{ +// 400 (!) => 352 => 336 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpArticulationLink) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpArticulationLink, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpArticulationLink, NpActor) + + DefineMetaData_PxActor(NpArticulationLink) + DefineMetaData_NpRigidActorTemplate(NpArticulationLink) + DefineMetaData_NpRigidBodyTemplate(NpArticulationLink) + + // NpArticulationLink + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, NpArticulation, mRoot, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, NpArticulationJoint, mInboundJoint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, NpArticulationLink, mParent, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, NpArticulationLinkArray, mChildLinks, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, PxU32, mLLIndex, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, PxU32, mInboundJointDof, 0); + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, NpArticulationLink, NpConnectorArray, mConnectorArray, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, NpArticulationLink, mName, 0) +} + +void NpArticulationJoint::getBinaryMetaData(PxOutputStream& stream) +{ +// 184 => 200 => 192 => 224 => 208 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpArticulationJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpArticulationJoint, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationJoint, Scb::ArticulationJoint, mImpl.mJoint, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationJoint, NpArticulationLink, mImpl.mParent, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationJoint, NpArticulationLink, mImpl.mChild, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationJoint, PxArticulationBase::Enum, mImpl.mType, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpAggregate::getBinaryMetaData(PxOutputStream& stream) +{ +// 36 => 56 => 40 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpAggregate) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpAggregate, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, NpAggregate, Scb::Aggregate, mAggregate, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpAggregate, PxU32, mNbActors, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpAggregate, PxActor, mActors, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mActors + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, NpAggregate, PxActor, mActors, mNbActors, PxMetaDataFlag::ePTR, PX_SERIAL_ALIGN) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PxMeshScale(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxMeshScale) + PX_DEF_BIN_METADATA_ITEM(stream, PxMeshScale, PxVec3, scale, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxMeshScale, PxQuat, rotation, 0) +} + +/////////////////////////////////////////////////////////////////////////////// +namespace physx +{ +void getBinaryMetaData_PxBase(PxOutputStream& stream) +{ + // 8 bytes + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxBaseFlags, PxU16) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxType, PxU16) + PX_DEF_BIN_METADATA_VCLASS(stream, PxBase) + PX_DEF_BIN_METADATA_ITEM(stream, PxBase, PxType, mConcreteType, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxBase, PxBaseFlags, mBaseFlags, 0) +} +} +void RefCountable::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_VCLASS(stream, RefCountable) + PX_DEF_BIN_METADATA_ITEM(stream, RefCountable, PxI32, mRefCount, 0) +} + +static void getFoundationMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxU8, char) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxI8, char) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxU16, short) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxI16, short) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxU32, int) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxI32, int) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxReal, float) + + getBinaryMetaData_PxVec3(stream); + getBinaryMetaData_PxVec4(stream); + getBinaryMetaData_PxQuat(stream); + getBinaryMetaData_PxBounds3(stream); + getBinaryMetaData_PxTransform(stream); + getBinaryMetaData_PxMat33(stream); + getBinaryMetaData_SpatialVectorF(stream); + getBinaryMetaData_BitMap(stream); + Cm::PtrTable::getBinaryMetaData(stream); + getBinaryMetaData_PxPlane(stream); + getBinaryMetaData_PxConstraintInvMassScale(stream); + + getBinaryMetaData_PxBase(stream); + RefCountable::getBinaryMetaData(stream); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PxGetPhysicsBinaryMetaData(PxOutputStream& stream) +{ + getFoundationMetaData(stream); + + getBinaryMetaData_PxMeshScale(stream); + + MaterialIndicesStruct::getBinaryMetaData(stream); + Gu::GeometryUnion::getBinaryMetaData(stream); + Gu::ConvexMesh::getBinaryMetaData(stream); + Gu::TriangleMesh::getBinaryMetaData(stream); + Gu::RTreeTriangleMesh::getBinaryMetaData(stream); + Gu::BV4TriangleMesh::getBinaryMetaData(stream); + Gu::HeightField::getBinaryMetaData(stream); + + Sc::ActorCore::getBinaryMetaData(stream); + Sc::RigidCore::getBinaryMetaData(stream); + Sc::StaticCore::getBinaryMetaData(stream); + Sc::BodyCore::getBinaryMetaData(stream); + Sc::MaterialCore::getBinaryMetaData(stream); + Sc::ShapeCore::getBinaryMetaData(stream); + Sc::ConstraintCore::getBinaryMetaData(stream); + Sc::ArticulationCore::getBinaryMetaData(stream); + Sc::ArticulationJointCore::getBinaryMetaData(stream); + + Scb::Base::getBinaryMetaData(stream); + Scb::Actor::getBinaryMetaData(stream); + Scb::RigidObject::getBinaryMetaData(stream); + Scb::RigidStatic::getBinaryMetaData(stream); + Scb::Body::getBinaryMetaData(stream); + Scb::Shape::getBinaryMetaData(stream); + Scb::Constraint::getBinaryMetaData(stream); + Scb::Articulation::getBinaryMetaData(stream); + Scb::ArticulationJoint::getBinaryMetaData(stream); + Scb::Aggregate::getBinaryMetaData(stream); + + NpConnector::getBinaryMetaData(stream); + NpConnectorArray::getBinaryMetaData(stream); + NpActor::getBinaryMetaData(stream); + NpMaterial::getBinaryMetaData(stream); // NP_MATERIAL + NpRigidDynamic::getBinaryMetaData(stream); // NP_RIGID_DYNAMIC + NpRigidStatic::getBinaryMetaData(stream); // NP_RIGID_STATIC + NpShape::getBinaryMetaData(stream); // NP_SHAPE + NpConstraint::getBinaryMetaData(stream); // NP_CONSTRAINT + NpArticulation::getBinaryMetaData(stream); // NP_ARTICULATION + NpArticulationLink::getBinaryMetaData(stream); // NP_ARTICULATION_LINK + NpArticulationJoint::getBinaryMetaData(stream); // NP_ARTICULATION_JOINT + NpArticulationLinkArray::getBinaryMetaData(stream); + NpShapeManager::getBinaryMetaData(stream); + NpAggregate::getBinaryMetaData(stream); // NP_AGGREGATE +} diff --git a/src/PhysX/physx/source/physx/src/NpPhysics.cpp b/src/PhysX/physx/source/physx/src/NpPhysics.cpp new file mode 100644 index 000000000..8db81f7f5 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPhysics.cpp @@ -0,0 +1,747 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpPhysics.h" + +// PX_SERIALIZATION +#include "foundation/PxProfiler.h" +#include "foundation/PxIO.h" +#include "foundation/PxErrorCallback.h" +#include "PxPhysicsVersion.h" +#include "CmCollection.h" +#include "CmUtils.h" +#include "NpRigidStatic.h" +#include "NpRigidDynamic.h" +#include "NpArticulation.h" +#include "NpArticulationLink.h" +#include "NpArticulationJoint.h" +#include "NpMaterial.h" +#include "GuHeightFieldData.h" +#include "GuHeightField.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "PsIntrinsics.h" +#include "PxTolerancesScale.h" +#include "PxvGlobals.h" // dynamic registration of HFs & articulations in LL +#include "GuOverlapTests.h" // dynamic registration of HFs in Gu +#include "PxDeletionListener.h" +#include "PxPhysicsSerialization.h" +#include "PsString.h" +#include "PvdPhysicsClient.h" +#include "SqPruningStructure.h" + +//~PX_SERIALIZATION + +#include "NpFactory.h" + +#if PX_SWITCH +#include "switch/NpMiddlewareInfo.h" +#endif + +using namespace physx; +using namespace Cm; + +bool NpPhysics::apiReentryLock = false; +NpPhysics* NpPhysics::mInstance = NULL; +PxU32 NpPhysics::mRefCount = 0; + +#if PX_CHECKED +bool NpPhysics::mHeightFieldsRegistered = false; //just for error checking +#endif + +NpPhysics::NpPhysics(const PxTolerancesScale& scale, const PxvOffsetTable& pxvOffsetTable, bool trackOutstandingAllocations, pvdsdk::PsPvd* pvd) : + mSceneArray(PX_DEBUG_EXP("physicsSceneArray")) + , mPhysics(scale, pxvOffsetTable) + , mDeletionListenersExist(false) +#if PX_SUPPORT_GPU_PHYSX + , mNbRegisteredGpuClients(0) +#endif +{ + PX_UNUSED(trackOutstandingAllocations); + + //mMasterMaterialTable.reserve(10); + +#if PX_SUPPORT_PVD + mPvd = pvd; + if(pvd) + { + mPvdPhysicsClient = PX_NEW(Vd::PvdPhysicsClient)(mPvd); + shdfnd::getFoundation().registerErrorCallback(*mPvdPhysicsClient); + shdfnd::getFoundation().registerAllocationListener(*mPvd); + } + else + { + mPvdPhysicsClient = NULL; + } +#else + PX_UNUSED(pvd); +#endif +} + +NpPhysics::~NpPhysics() +{ + // Release all scenes in case the user didn't do it + PxU32 nbScenes = mSceneArray.size(); + NpScene** scenes = mSceneArray.begin(); + for(PxU32 i=0;i 0) + //{ + // // It's done this way since the material destructor removes the material from the table and adjusts indices + + // PX_ASSERT(mMasterMaterialTable[0]->getRefCount() == 1); + // mMasterMaterialTable[0]->decRefCount(); + //} + //mMasterMaterialTable.clear(); + + mMasterMaterialManager.releaseMaterials(); + +#if PX_SUPPORT_PVD + if(mPvd) + { + mPvdPhysicsClient->destroyPvdInstance(this); + mPvd->removeClient(mPvdPhysicsClient); + shdfnd::getFoundation().deregisterErrorCallback(*mPvdPhysicsClient); + PX_DELETE_AND_RESET(mPvdPhysicsClient); + shdfnd::getFoundation().deregisterAllocationListener(*mPvd); + } +#endif + + const DeletionListenerMap::Entry* delListenerEntries = mDeletionListenerMap.getEntries(); + const PxU32 delListenerEntryCount = mDeletionListenerMap.size(); + for(PxU32 i=0; i < delListenerEntryCount; i++) + { + PX_DELETE(delListenerEntries[i].second); + } + mDeletionListenerMap.clear(); +} + +void NpPhysics::initOffsetTables(PxvOffsetTable& pxvOffsetTable) +{ + // init offset tables for Pxs/Sc/Scb/Px conversions + { + Sc::OffsetTable& offsetTable = Sc::gOffsetTable; + offsetTable.scRigidStatic2PxActor = -reinterpret_cast(&(reinterpret_cast(0)->getScbRigidStaticFast())) - static_cast(Scb::RigidStatic::getScOffset()); + offsetTable.scRigidDynamic2PxActor = -reinterpret_cast(&(reinterpret_cast(0)->getScbBodyFast())) - static_cast(Scb::Body::getScOffset()); + offsetTable.scArticulationLink2PxActor = -reinterpret_cast(&(reinterpret_cast(0)->getScbBodyFast())) - static_cast(Scb::Body::getScOffset()); + offsetTable.scArticulation2Px = -reinterpret_cast(&(reinterpret_cast(0)->mImpl.getScbArticulation())) - static_cast(Scb::Articulation::getScOffset()); + offsetTable.scConstraint2Px = -reinterpret_cast(&(reinterpret_cast(0)->getScbConstraint())) - static_cast(Scb::Constraint::getScOffset()); + offsetTable.scShape2Px = -reinterpret_cast(&(reinterpret_cast(0)->getScbShape())) - static_cast(Scb::Shape::getScOffset()); + + for(PxU32 i=0;i(&static_cast(0)->getCore()); + pxvOffsetTable.pxsRigidCore2PxRigidBody = scOffsetTable.scRigidDynamic2PxActor - reinterpret_cast(&static_cast(0)->getCore()); + pxvOffsetTable.pxsRigidCore2PxRigidStatic = scOffsetTable.scRigidStatic2PxActor - reinterpret_cast(&static_cast(0)->getCore()); + } +} + +NpPhysics* NpPhysics::createInstance(PxU32 version, PxFoundation& foundation, const PxTolerancesScale& scale, bool trackOutstandingAllocations, pvdsdk::PsPvd* pvd) +{ + PX_UNUSED(foundation); + +#if PX_SWITCH + NpSetMiddlewareInfo(); // register middleware info such that PhysX usage can be tracked +#endif + + if (version!=PX_PHYSICS_VERSION) + { + char buffer[256]; + Ps::snprintf(buffer, 256, "Wrong version: PhysX version is 0x%08x, tried to create 0x%08x", PX_PHYSICS_VERSION, version); + foundation.getErrorCallback().reportError(PxErrorCode::eINVALID_PARAMETER, buffer, __FILE__, __LINE__); + return NULL; + } + + if (!scale.isValid()) + { + foundation.getErrorCallback().reportError(PxErrorCode::eINVALID_PARAMETER, "Scale invalid.\n", __FILE__, __LINE__); + return NULL; + } + + if(0 == mRefCount) + { + PX_ASSERT(static_cast(&foundation) == &Ps::Foundation::getInstance()); + + Ps::Foundation::incRefCount(); + + // init offset tables for Pxs/Sc/Scb/Px conversions + PxvOffsetTable pxvOffsetTable; + initOffsetTables(pxvOffsetTable); + + //SerialFactory::createInstance(); + mInstance = PX_NEW (NpPhysics)(scale, pxvOffsetTable, trackOutstandingAllocations, pvd); + NpFactory::createInstance(); + +#if PX_SUPPORT_PVD + if(pvd) + { + NpFactory::getInstance().setNpFactoryListener( *mInstance->mPvdPhysicsClient ); + pvd->addClient(mInstance->mPvdPhysicsClient); + } +#endif + + NpFactory::getInstance().addFactoryListener(mInstance->mDeletionMeshListener); + } + ++mRefCount; + + return mInstance; +} + +PxU32 NpPhysics::releaseInstance() +{ + PX_ASSERT(mRefCount > 0); + if (--mRefCount) + return mRefCount; + +#if PX_SUPPORT_PVD + if(mInstance->mPvd) + { + NpFactory::getInstance().removeFactoryListener( *mInstance->mPvdPhysicsClient ); + } +#endif + + NpFactory::destroyInstance(); + + PX_ASSERT(mInstance); + PX_DELETE_AND_RESET(mInstance); + + Ps::Foundation::decRefCount(); + + return mRefCount; +} + +void NpPhysics::release() +{ + NpPhysics::releaseInstance(); +} + +PxScene* NpPhysics::createScene(const PxSceneDesc& desc) +{ + PX_CHECK_AND_RETURN_NULL(desc.isValid(), "Physics::createScene: desc.isValid() is false!"); + + const PxTolerancesScale& scale = mPhysics.getTolerancesScale(); + const PxTolerancesScale& descScale = desc.getTolerancesScale(); + PX_UNUSED(scale); + PX_UNUSED(descScale); + PX_CHECK_AND_RETURN_NULL((descScale.length == scale.length) && (descScale.speed == scale.speed), "Physics::createScene: PxTolerancesScale must be the same as used for creation of PxPhysics!"); + + Ps::Mutex::ScopedLock lock(mSceneAndMaterialMutex); // done here because scene constructor accesses profiling manager of the SDK + + NpScene* npScene = PX_NEW (NpScene)(desc); + if(!npScene) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Unable to create scene."); + return NULL; + } + if(!npScene->getTaskManager()) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Unable to create scene. Task manager creation failed."); + return NULL; + } + + npScene->loadFromDesc(desc); + +#if PX_SUPPORT_PVD + if(mPvd) + { + npScene->mScene.getScenePvdClient().setPsPvd(mPvd); + mPvd->addClient(&npScene->mScene.getScenePvdClient()); + } +#endif + + if (!sendMaterialTable(*npScene) || !npScene->getScene().isValid()) + { + PX_DELETE(npScene); + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Unable to create scene."); + return NULL; + } + + mSceneArray.pushBack(npScene); + return npScene; +} + +void NpPhysics::releaseSceneInternal(PxScene& scene) +{ + NpScene* pScene = static_cast(&scene); + + Ps::Mutex::ScopedLock lock(mSceneAndMaterialMutex); + for(PxU32 i=0;i(mSceneAndMaterialMutex)); + return mSceneArray.size(); +} + +PxU32 NpPhysics::getScenes(PxScene** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(const_cast(mSceneAndMaterialMutex)); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mSceneArray.begin(), mSceneArray.size()); +} + +PxRigidStatic* NpPhysics::createRigidStatic(const PxTransform& globalPose) +{ + PX_CHECK_AND_RETURN_NULL(globalPose.isSane(), "PxPhysics::createRigidStatic: invalid transform"); + return NpFactory::getInstance().createRigidStatic(globalPose.getNormalized()); +} + +PxShape* NpPhysics::createShape(const PxGeometry& geometry, PxMaterial*const * materials, PxU16 materialCount, bool isExclusive, PxShapeFlags shapeFlags) +{ + PX_CHECK_AND_RETURN_NULL(materials, "createShape: material pointer is NULL"); + PX_CHECK_AND_RETURN_NULL(materialCount>0, "createShape: material count is zero"); + +#if PX_CHECKED + const bool isHeightfield = geometry.getType() == PxGeometryType::eHEIGHTFIELD; + if (isHeightfield) + { + PX_CHECK_AND_RETURN_NULL(mHeightFieldsRegistered, "NpPhysics::createShape: Creating Heightfield shape without having called PxRegister[Unified]HeightFields()!"); + } + const bool hasMeshTypeGeom = isHeightfield || (geometry.getType() == PxGeometryType::eTRIANGLEMESH); + PX_CHECK_AND_RETURN_NULL(!(hasMeshTypeGeom && (shapeFlags & PxShapeFlag::eTRIGGER_SHAPE)), "NpPhysics::createShape: triangle mesh and heightfield triggers are not supported!"); + PX_CHECK_AND_RETURN_NULL(!((shapeFlags & PxShapeFlag::eSIMULATION_SHAPE) && (shapeFlags & PxShapeFlag::eTRIGGER_SHAPE)), "NpPhysics::createShape: shapes cannot simultaneously be trigger shapes and simulation shapes."); +#endif + + return NpFactory::getInstance().createShape(geometry, shapeFlags, materials, materialCount, isExclusive); +} + +PxU32 NpPhysics::getNbShapes() const +{ + return NpFactory::getInstance().getNbShapes(); +} + +PxU32 NpPhysics::getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return NpFactory::getInstance().getShapes(userBuffer, bufferSize, startIndex); +} + +PxRigidDynamic* NpPhysics::createRigidDynamic(const PxTransform& globalPose) +{ + PX_CHECK_AND_RETURN_NULL(globalPose.isSane(), "PxPhysics::createRigidDynamic: invalid transform"); + return NpFactory::getInstance().createRigidDynamic(globalPose.getNormalized()); +} + +PxConstraint* NpPhysics::createConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) +{ + return NpFactory::getInstance().createConstraint(actor0, actor1, connector, shaders, dataSize); +} + +PxArticulation* NpPhysics::createArticulation() +{ + return NpFactory::getInstance().createArticulation(); +} + +PxArticulationReducedCoordinate* NpPhysics::createArticulationReducedCoordinate() +{ + return NpFactory::getInstance().createArticulationRC(); +} + +PxAggregate* NpPhysics::createAggregate(PxU32 maxSize, bool selfCollisionEnabled) +{ + return NpFactory::getInstance().createAggregate(maxSize, selfCollisionEnabled); +} + +/////////////////////////////////////////////////////////////////////////////// + +NpMaterial* NpPhysics::addMaterial(NpMaterial* m) +{ + if(!m) + return NULL; + + Ps::Mutex::ScopedLock lock(mSceneAndMaterialMutex); + + //the handle is set inside the setMaterial method + if(mMasterMaterialManager.setMaterial(*m)) + { + // Let all scenes know of the new material + for(PxU32 i=0; i < mSceneArray.size(); i++) + { + NpScene* s = getScene(i); + s->addMaterial(*m); + } + return m; + } + else + { + m->release(); + return NULL; + } +} + +PxMaterial* NpPhysics::createMaterial(PxReal staticFriction, PxReal dynamicFriction, PxReal restitution) +{ + PxMaterial* m = NpFactory::getInstance().createMaterial(staticFriction, dynamicFriction, restitution); + return addMaterial(static_cast(m)); +} + +PxU32 NpPhysics::getNbMaterials() const +{ + Ps::Mutex::ScopedLock lock(const_cast(mSceneAndMaterialMutex)); + return mMasterMaterialManager.getNumMaterials(); +} + +PxU32 NpPhysics::getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(const_cast(mSceneAndMaterialMutex)); + NpMaterialManagerIterator iter(mMasterMaterialManager); + PxU32 writeCount =0; + PxU32 index = 0; + NpMaterial* mat; + while(iter.getNextMaterial(mat)) + { + if(index++ < startIndex) + continue; + if(writeCount == bufferSize) + break; + userBuffer[writeCount++] = mat; + } + return writeCount; +} + +void NpPhysics::removeMaterialFromTable(NpMaterial& m) +{ + Ps::Mutex::ScopedLock lock(mSceneAndMaterialMutex); + + // Let all scenes know of the deleted material + for(PxU32 i=0; i < mSceneArray.size(); i++) + { + NpScene* s = getScene(i); + s->removeMaterial(m); + } + + mMasterMaterialManager.removeMaterial(m); +} + +void NpPhysics::updateMaterial(NpMaterial& m) +{ + Ps::Mutex::ScopedLock lock(mSceneAndMaterialMutex); + + // Let all scenes know of the updated material + for(PxU32 i=0; i < mSceneArray.size(); i++) + { + NpScene* s = getScene(i); + s->updateMaterial(m); + } + mMasterMaterialManager.updateMaterial(m); +} + +bool NpPhysics::sendMaterialTable(NpScene& scene) +{ + // note: no lock here because this method gets only called at scene creation and there we do lock + + NpMaterialManagerIterator iter(mMasterMaterialManager); + NpMaterial* mat; + while(iter.getNextMaterial(mat)) + scene.addMaterial(*mat); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +PxTriangleMesh* NpPhysics::createTriangleMesh(PxInputStream& stream) +{ + return NpFactory::getInstance().createTriangleMesh(stream); +} + +PxU32 NpPhysics::getNbTriangleMeshes() const +{ + return NpFactory::getInstance().getNbTriangleMeshes(); +} + +PxU32 NpPhysics::getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return NpFactory::getInstance().getTriangleMeshes(userBuffer, bufferSize, startIndex); +} + +PxHeightField* NpPhysics::createHeightField(PxInputStream& stream) +{ + return NpFactory::getInstance().createHeightField(stream); +} + +PxU32 NpPhysics::getNbHeightFields() const +{ + return NpFactory::getInstance().getNbHeightFields(); +} + +PxU32 NpPhysics::getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return NpFactory::getInstance().getHeightFields(userBuffer, bufferSize, startIndex); +} + +/////////////////////////////////////////////////////////////////////////////// +PxConvexMesh* NpPhysics::createConvexMesh(PxInputStream& stream) +{ + return NpFactory::getInstance().createConvexMesh(stream); +} + +PxU32 NpPhysics::getNbConvexMeshes() const +{ + return NpFactory::getInstance().getNbConvexMeshes(); +} + +PxU32 NpPhysics::getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return NpFactory::getInstance().getConvexMeshes(userBuffer, bufferSize, startIndex); +} + +/////////////////////////////////////////////////////////////////////////////// +PxBVHStructure* NpPhysics::createBVHStructure(PxInputStream& stream) +{ + return NpFactory::getInstance().createBVHStructure(stream); +} + +PxU32 NpPhysics::getNbBVHStructures() const +{ + return NpFactory::getInstance().getNbBVHStructures(); +} + +PxU32 NpPhysics::getBVHStructures(PxBVHStructure** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return NpFactory::getInstance().getBVHStructures(userBuffer, bufferSize, startIndex); +} +/////////////////////////////////////////////////////////////////////////////// + +PxPruningStructure* NpPhysics::createPruningStructure(PxRigidActor*const* actors, PxU32 nbActors) +{ + PX_SIMD_GUARD; + + PX_ASSERT(actors); + PX_ASSERT(nbActors > 0); + + Sq::PruningStructure* ps = PX_NEW(Sq::PruningStructure)(); + if(!ps->build(actors, nbActors)) + { + PX_DELETE_AND_RESET(ps); + } + return ps; +} + +#if PX_SUPPORT_GPU_PHYSX +void NpPhysics::registerPhysXIndicatorGpuClient() +{ + Ps::Mutex::ScopedLock lock(mPhysXIndicatorMutex); + + ++mNbRegisteredGpuClients; + + mPhysXIndicator.setIsGpu(mNbRegisteredGpuClients>0); +} + +void NpPhysics::unregisterPhysXIndicatorGpuClient() +{ + Ps::Mutex::ScopedLock lock(mPhysXIndicatorMutex); + + if (mNbRegisteredGpuClients) + --mNbRegisteredGpuClients; + + mPhysXIndicator.setIsGpu(mNbRegisteredGpuClients>0); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +void NpPhysics::registerDeletionListener(PxDeletionListener& observer, const PxDeletionEventFlags& deletionEvents, bool restrictedObjectSet) +{ + Ps::Mutex::ScopedLock lock(mDeletionListenerMutex); + + const DeletionListenerMap::Entry* entry = mDeletionListenerMap.find(&observer); + if(!entry) + { + NpDelListenerEntry* e = PX_NEW(NpDelListenerEntry)(deletionEvents, restrictedObjectSet); + if (e) + { + if (mDeletionListenerMap.insert(&observer, e)) + mDeletionListenersExist = true; + else + { + PX_DELETE(e); + PX_ALWAYS_ASSERT(); + } + } + } + else + PX_ASSERT(mDeletionListenersExist); +} + +void NpPhysics::unregisterDeletionListener(PxDeletionListener& observer) +{ + Ps::Mutex::ScopedLock lock(mDeletionListenerMutex); + + const DeletionListenerMap::Entry* entry = mDeletionListenerMap.find(&observer); + if(entry) + { + NpDelListenerEntry* e = entry->second; + mDeletionListenerMap.erase(&observer); + PX_DELETE(e); + } + mDeletionListenersExist = mDeletionListenerMap.size()>0; +} + +void NpPhysics::registerDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount) +{ + Ps::Mutex::ScopedLock lock(mDeletionListenerMutex); + + const DeletionListenerMap::Entry* entry = mDeletionListenerMap.find(&observer); + if(entry) + { + NpDelListenerEntry* e = entry->second; + PX_CHECK_AND_RETURN(e->restrictedObjectSet, "PxPhysics::registerDeletionListenerObjects: deletion listener is not configured to receive events from specific objects."); + + e->registeredObjects.reserve(e->registeredObjects.size() + observableCount); + for(PxU32 i=0; i < observableCount; i++) + e->registeredObjects.insert(observables[i]); + } + else + { + PX_CHECK_AND_RETURN(false, "PxPhysics::registerDeletionListenerObjects: deletion listener has to be registered in PxPhysics first."); + } +} + +void NpPhysics::unregisterDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount) +{ + Ps::Mutex::ScopedLock lock(mDeletionListenerMutex); + + const DeletionListenerMap::Entry* entry = mDeletionListenerMap.find(&observer); + if(entry) + { + NpDelListenerEntry* e = entry->second; + if (e->restrictedObjectSet) + { + for(PxU32 i=0; i < observableCount; i++) + e->registeredObjects.erase(observables[i]); + } + else + { + PX_CHECK_AND_RETURN(false, "PxPhysics::unregisterDeletionListenerObjects: deletion listener is not configured to receive events from specific objects."); + } + } + else + { + PX_CHECK_AND_RETURN(false, "PxPhysics::unregisterDeletionListenerObjects: deletion listener has to be registered in PxPhysics first."); + } +} + +void NpPhysics::notifyDeletionListeners(const PxBase* base, void* userData, PxDeletionEventFlag::Enum deletionEvent) +{ + // we don't protect the check for whether there are any listeners, because we don't want to take a hit in the + // common case where there are no listeners. Note the API comments here, that users should not register or + // unregister deletion listeners while deletions are occurring + + if(mDeletionListenersExist) + { + Ps::Mutex::ScopedLock lock(mDeletionListenerMutex); + + const DeletionListenerMap::Entry* delListenerEntries = mDeletionListenerMap.getEntries(); + const PxU32 delListenerEntryCount = mDeletionListenerMap.size(); + for(PxU32 i=0; i < delListenerEntryCount; i++) + { + const NpDelListenerEntry* entry = delListenerEntries[i].second; + + if (entry->flags & deletionEvent) + { + if (entry->restrictedObjectSet) + { + if (entry->registeredObjects.contains(base)) + delListenerEntries[i].first->onRelease(base, userData, deletionEvent); + } + else + delListenerEntries[i].first->onRelease(base, userData, deletionEvent); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +const PxTolerancesScale& NpPhysics::getTolerancesScale() const +{ + return mPhysics.getTolerancesScale(); +} + +PxFoundation& NpPhysics::getFoundation() +{ + return Ps::Foundation::getInstance(); +} + +PxPhysics& PxGetPhysics() +{ + return NpPhysics::getInstance(); +} + +PxPhysics* PxCreateBasePhysics(PxU32 version, PxFoundation& foundation, const PxTolerancesScale& scale, bool trackOutstandingAllocations, PxPvd* pvd) +{ + return NpPhysics::createInstance(version, foundation, scale, trackOutstandingAllocations, static_cast(pvd)); +} + +void PxRegisterArticulations(PxPhysics& physics) +{ + PX_UNUSED(&physics); // for the moment + Dy::PxvRegisterArticulations(); + NpFactory::registerArticulations(); +} + +void PxRegisterArticulationsReducedCoordinate(PxPhysics& physics) +{ + PX_UNUSED(&physics); // for the moment + Dy::PxvRegisterArticulationsReducedCoordinate(); + NpFactory::registerArticulationRCs(); +} + +void PxRegisterHeightFields(PxPhysics& physics) +{ + PX_UNUSED(&physics); // for the moment + PX_CHECK_AND_RETURN(NpPhysics::getInstance().getNumScenes() == 0, "PxRegisterHeightFields: it is illegal to call a heightfield registration function after you have a scene."); + + PxvRegisterHeightFields(); + Gu::registerHeightFields(); +#if PX_CHECKED + NpPhysics::heightfieldsAreRegistered(); +#endif +} + +void PxAddCollectionToPhysics(const PxCollection& collection) +{ + NpFactory& factory = NpFactory::getInstance(); + const Cm::Collection& c = static_cast(collection); + factory.addCollection(c); +} diff --git a/src/PhysX/physx/source/physx/src/NpPhysics.h b/src/PhysX/physx/source/physx/src/NpPhysics.h new file mode 100644 index 000000000..084f3ce0d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPhysics.h @@ -0,0 +1,247 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_PHYSICS +#define PX_PHYSICS_NP_PHYSICS + +#include "PxPhysics.h" +#include "PsUserAllocated.h" +#include "GuMeshFactory.h" +#include "NpMaterial.h" +#include "NpPhysicsInsertionCallback.h" +#include "NpMaterialManager.h" +#include "ScPhysics.h" +#include "PsHashSet.h" +#include "PsHashMap.h" + +#ifdef LINUX +#include +#endif + +#if PX_SUPPORT_GPU_PHYSX +#include "device/PhysXIndicator.h" +#endif + +#include "PsPvd.h" + +namespace physx +{ +#if PX_SUPPORT_PVD +namespace Vd +{ + class PvdPhysicsClient; +} +#endif + struct NpMaterialIndexTranslator + { + NpMaterialIndexTranslator() : indicesNeedTranslation(false) {} + + Ps::HashMap map; + bool indicesNeedTranslation; + }; + + class NpScene; + struct PxvOffsetTable; + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4996) // We have to implement deprecated member functions, do not warn. +#endif + +class NpPhysics : public PxPhysics, public Ps::UserAllocated +{ + NpPhysics& operator=(const NpPhysics&); + NpPhysics(const NpPhysics &); + + struct NpDelListenerEntry : public UserAllocated + { + NpDelListenerEntry(const PxDeletionEventFlags& de, bool restrictedObjSet) + : flags(de) + , restrictedObjectSet(restrictedObjSet) + { + } + + Ps::HashSet registeredObjects; // specifically registered objects for deletion events + PxDeletionEventFlags flags; + bool restrictedObjectSet; + }; + + + NpPhysics( const PxTolerancesScale& scale, + const PxvOffsetTable& pxvOffsetTable, + bool trackOutstandingAllocations, + physx::pvdsdk::PsPvd* pvd); + virtual ~NpPhysics(); + +public: + + static NpPhysics* createInstance( PxU32 version, + PxFoundation& foundation, + const PxTolerancesScale& scale, + bool trackOutstandingAllocations, + physx::pvdsdk::PsPvd* pvd); + + static PxU32 releaseInstance(); + + static NpPhysics& getInstance() { return *mInstance; } + + virtual void release(); + + virtual PxScene* createScene(const PxSceneDesc&); + void releaseSceneInternal(PxScene&); + virtual PxU32 getNbScenes() const; + virtual PxU32 getScenes(PxScene** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxRigidStatic* createRigidStatic(const PxTransform&); + virtual PxRigidDynamic* createRigidDynamic(const PxTransform&); + virtual PxArticulation* createArticulation(); + virtual PxArticulationReducedCoordinate* createArticulationReducedCoordinate(); + virtual PxConstraint* createConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize); + virtual PxAggregate* createAggregate(PxU32 maxSize, bool selfCollision); + + virtual PxShape* createShape(const PxGeometry&, PxMaterial*const *, PxU16, bool, PxShapeFlags shapeFlags); + virtual PxU32 getNbShapes() const; + virtual PxU32 getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + virtual PxMaterial* createMaterial(PxReal staticFriction, PxReal dynamicFriction, PxReal restitution); + virtual PxU32 getNbMaterials() const; + virtual PxU32 getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxTriangleMesh* createTriangleMesh(PxInputStream&); + virtual PxU32 getNbTriangleMeshes() const; + virtual PxU32 getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxHeightField* createHeightField(PxInputStream& stream); + virtual PxU32 getNbHeightFields() const; + virtual PxU32 getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxConvexMesh* createConvexMesh(PxInputStream&); + virtual PxU32 getNbConvexMeshes() const; + virtual PxU32 getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxBVHStructure* createBVHStructure(PxInputStream&); + virtual PxU32 getNbBVHStructures() const; + virtual PxU32 getBVHStructures(PxBVHStructure** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + +#if PX_SUPPORT_GPU_PHYSX + void registerPhysXIndicatorGpuClient(); + void unregisterPhysXIndicatorGpuClient(); +#else + PX_FORCE_INLINE void registerPhysXIndicatorGpuClient() {} + PX_FORCE_INLINE void unregisterPhysXIndicatorGpuClient() {} +#endif + + virtual PxPruningStructure* createPruningStructure(PxRigidActor*const* actors, PxU32 nbActors); + + virtual const PxTolerancesScale& getTolerancesScale() const; + + virtual PxFoundation& getFoundation(); + + PX_INLINE NpScene* getScene(PxU32 i) const { return mSceneArray[i]; } + PX_INLINE PxU32 getNumScenes() const { return mSceneArray.size(); } +#if PX_CHECKED + static PX_INLINE void heightfieldsAreRegistered() { mHeightFieldsRegistered = true; } +#endif + + virtual void registerDeletionListener(PxDeletionListener& observer, const PxDeletionEventFlags& deletionEvents, bool restrictedObjectSet); + virtual void unregisterDeletionListener(PxDeletionListener& observer); + virtual void registerDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount); + virtual void unregisterDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount); + + void notifyDeletionListeners(const PxBase*, void* userData, PxDeletionEventFlag::Enum deletionEvent); + PX_FORCE_INLINE void notifyDeletionListenersUserRelease(const PxBase* b, void* userData) { notifyDeletionListeners(b, userData, PxDeletionEventFlag::eUSER_RELEASE); } + PX_FORCE_INLINE void notifyDeletionListenersMemRelease(const PxBase* b, void* userData) { notifyDeletionListeners(b, userData, PxDeletionEventFlag::eMEMORY_RELEASE); } + + virtual PxPhysicsInsertionCallback& getPhysicsInsertionCallback() { return mObjectInsertion; } + + void removeMaterialFromTable(NpMaterial&); + void updateMaterial(NpMaterial&); + bool sendMaterialTable(NpScene&); + + NpMaterialManager& getMaterialManager() { return mMasterMaterialManager; } + + NpMaterial* addMaterial(NpMaterial* np); + + static void initOffsetTables(PxvOffsetTable& pxvOffsetTable); + + static bool apiReentryLock; + +private: + typedef Ps::CoalescedHashMap DeletionListenerMap; + + Ps::Array mSceneArray; + + Sc::Physics mPhysics; + NpMaterialManager mMasterMaterialManager; + + NpPhysicsInsertionCallback mObjectInsertion; + + struct MeshDeletionListener: public GuMeshFactoryListener + { + void onGuMeshFactoryBufferRelease(const PxBase* object, PxType type) + { + PX_UNUSED(type); + NpPhysics::getInstance().notifyDeletionListeners(object, NULL, PxDeletionEventFlag::eMEMORY_RELEASE); + } + }; + + Ps::Mutex mDeletionListenerMutex; + DeletionListenerMap mDeletionListenerMap; + MeshDeletionListener mDeletionMeshListener; + bool mDeletionListenersExist; + + Ps::Mutex mSceneAndMaterialMutex; // guarantees thread safety for API calls related to scene and material containers + +#if PX_SUPPORT_GPU_PHYSX + PhysXIndicator mPhysXIndicator; + PxU32 mNbRegisteredGpuClients; + Ps::Mutex mPhysXIndicatorMutex; +#endif +#if PX_SUPPORT_PVD + physx::pvdsdk::PsPvd* mPvd; + Vd::PvdPhysicsClient* mPvdPhysicsClient; +#endif + + static PxU32 mRefCount; + static NpPhysics* mInstance; + +#if PX_CHECKED + static bool mHeightFieldsRegistered; //just for error checking +#endif + + friend class NpCollection; +}; + +#if PX_VC +#pragma warning(pop) +#endif +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h b/src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h new file mode 100644 index 000000000..aba7297f1 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h @@ -0,0 +1,72 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_PHYSICS_INSERTION_CALLBACK +#define PX_PHYSICS_NP_PHYSICS_INSERTION_CALLBACK + +#include "common/PxPhysicsInsertionCallback.h" +#include "GuTriangleMesh.h" +#include "GuHeightField.h" +#include "GuConvexMesh.h" +#include "NpFactory.h" +#include "PsFoundation.h" + +namespace physx +{ + class NpPhysicsInsertionCallback: public PxPhysicsInsertionCallback + { + public: + NpPhysicsInsertionCallback() {} + + virtual PxBase* buildObjectFromData(PxConcreteType::Enum type, void* data) + { + if(type == PxConcreteType::eTRIANGLE_MESH_BVH33 || type == PxConcreteType::eTRIANGLE_MESH_BVH34) + return NpFactory::getInstance().createTriangleMesh(data); + + if (type == PxConcreteType::eCONVEX_MESH) + return NpFactory::getInstance().createConvexMesh(data); + + if (type == PxConcreteType::eHEIGHTFIELD) + return NpFactory::getInstance().createHeightField(data); + + if (type == PxConcreteType::eBVH_STRUCTURE) + return NpFactory::getInstance().createBVHStructure(data); + + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Inserting object failed: " + "Object type not supported for buildObjectFromData."); + + return NULL; + } + + }; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h b/src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h new file mode 100644 index 000000000..91e66adc0 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h @@ -0,0 +1,105 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_PTRTABLESTORAGEMANAGER_H +#define PX_PHYSICS_NP_PTRTABLESTORAGEMANAGER_H + +#include "CmPhysXCommon.h" +#include "PsMutex.h" +#include "PsUserAllocated.h" +#include "CmPtrTable.h" +#include "PsBitUtils.h" + +namespace physx +{ +class NpPtrTableStorageManager : public Cm::PtrTableStorageManager, public Ps::UserAllocated +{ + PX_NOCOPY(NpPtrTableStorageManager) + +public: + + NpPtrTableStorageManager() {} + ~NpPtrTableStorageManager() {} + + void** allocate(PxU32 capacity) + { + PX_ASSERT(Ps::isPowerOfTwo(capacity)); + + Ps::Mutex::ScopedLock lock(mMutex); + + return capacity<=4*sizeof(void*) ? reinterpret_cast(mPool4.construct()) + : capacity<=16*sizeof(void*) ? reinterpret_cast(mPool16.construct()) + : capacity<=64*sizeof(void*) ? reinterpret_cast(mPool64.construct()) + : reinterpret_cast(PX_ALLOC(capacity*sizeof(void*), "CmPtrTable pointer array")); + } + + void deallocate(void** addr, PxU32 capacity) + { + PX_ASSERT(Ps::isPowerOfTwo(capacity)); + + Ps::Mutex::ScopedLock lock(mMutex); + + if(capacity<=4*sizeof(void*)) mPool4.destroy(reinterpret_cast< PtrBlock<4>*>(addr)); + else if(capacity<=16*sizeof(void*)) mPool16.destroy(reinterpret_cast< PtrBlock<16>*>(addr)); + else if(capacity<=64*sizeof(void*)) mPool64.destroy(reinterpret_cast< PtrBlock<64>*>(addr)); + else PX_FREE(addr); + } + + // originalCapacity is the only way we know which pool the alloc request belongs to, + // so if those are no longer going to match, we need to realloc. + + bool canReuse(PxU32 originalCapacity, PxU32 newCapacity) + { + PX_ASSERT(Ps::isPowerOfTwo(originalCapacity)); + PX_ASSERT(Ps::isPowerOfTwo(newCapacity)); + + return poolId(originalCapacity) == poolId(newCapacity) && newCapacity<=64; + } + +private: + Ps::Mutex mMutex; + + int poolId(PxU32 size) + { + return size<=4 ? 0 + : size<=16 ? 1 + : size<=64 ? 2 + : 3; + } + + template class PtrBlock { void* ptr[N]; }; + + Ps::Pool2, 4096 > mPool4; + Ps::Pool2, 4096 > mPool16; + Ps::Pool2, 4096 > mPool64; +}; + +} +#endif diff --git a/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp new file mode 100644 index 000000000..64de49aae --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp @@ -0,0 +1,182 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpScene.h" + +#if PX_SUPPORT_PVD +using namespace physx; +using namespace Sq; +using namespace Vd; + +static const char* gName_PvdRaycast[2] = { "SceneQueries.Raycasts", "BatchedQueries.Raycasts" }; +static const char* gName_PvdSweep[2] = { "SceneQueries.Sweeps", "BatchedQueries.Sweeps" }; +static const char* gName_PvdOverlap[2] = { "SceneQueries.Overlaps", "BatchedQueries.Overlaps" }; +static const char* gName_PvdSqHit[2] = { "SceneQueries.Hits", "BatchedQueries.Hits" }; +static const char* gName_PxTransform[2] = { "SceneQueries.PoseList", "BatchedQueries.PoseList" }; +static const char* gName_PxFilterData[2] = { "SceneQueries.FilterDataList", "BatchedQueries.FilterDataList" }; +static const char* gName_PxGeometryHolder[2] = { "SceneQueries.GeometryList", "BatchedQueries.GeometryList" }; + +PvdSceneQueryCollector::PvdSceneQueryCollector(Scb::Scene& scene, bool isBatched) : + mAccumulatedRaycastQueries (gName_PvdRaycast), + mAccumulatedSweepQueries (gName_PvdSweep), + mAccumulatedOverlapQueries (gName_PvdOverlap), + mPvdSqHits (gName_PvdSqHit), + mPoses (gName_PxTransform), + mFilterData (gName_PxFilterData), + mScene (scene), + mGeometries0 (gName_PxGeometryHolder), + mGeometries1 (gName_PxGeometryHolder), + mInUse (0), + mIsBatched (isBatched) +{ +} + +void PvdSceneQueryCollector::release() +{ + physx::pvdsdk::PvdDataStream* stream = mScene.getScenePvdClient().getDataStream(); + if(stream && stream->isConnected()) + { + const Ps::Array& geoms = getPrevFrameGeometries(); + for(PxU32 k=0; kdestroyInstance(&geoms[k]); + + clearGeometryArrays(); + } +} + +template +static void collectBatchedHits(const QueryResultT* results, Ps::Array& accumulated, Ps::Array& pvdSqHits, PxU32 nb, PxU32 startIdx, const char* arrayName) +{ + for(PxU32 i=0; i +static void accumulate(PvdHitType& query, Ps::Array& accumulated, const char* arrayName, Ps::Array& dst, const SDKHitType* src, PxU32 nb, const PxQueryFilterData& fd) +{ + query.mFilterFlags = fd.flags; + query.mHits = PvdReference(arrayName, dst.size(), nb); + + PX_ASSERT(PxU32(-1) != nb); + for(PxU32 i=0; i 0 ? 1u : 0; +} + +template static void pushBackT(Ps::Array& array, const Type& item, PvdReference& ref, const char* arrayName) +{ + ref = PvdReference(arrayName, array.size(), 1); + array.pushBack(item); +} + +void PvdSceneQueryCollector::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal distance, const PxRaycastHit* hit, PxU32 hitsNum, const PxQueryFilterData& fd, bool multipleHits) +{ + Ps::Mutex::ScopedLock lock(mMutex); + + PvdRaycast raycastQuery; + raycastQuery.mOrigin = origin; + raycastQuery.mUnitDir = unitDir; + raycastQuery.mDistance = distance; + raycastQuery.mFilterData = fd.data; + if(fd.flags & PxQueryFlag::eANY_HIT) raycastQuery.mType = QueryID::QUERY_RAYCAST_ANY_OBJECT; + else if(multipleHits) raycastQuery.mType = QueryID::QUERY_RAYCAST_ALL_OBJECTS; + else raycastQuery.mType = QueryID::QUERY_RAYCAST_CLOSEST_OBJECT; + clampNbHits(hitsNum, fd, multipleHits); + + accumulate(raycastQuery, mAccumulatedRaycastQueries, getArrayName(mPvdSqHits), mPvdSqHits, hit, hitsNum, fd); +} + +void PvdSceneQueryCollector::sweep(const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, PxReal distance, const PxSweepHit* hit, PxU32 hitsNum, const PxQueryFilterData& fd, bool multipleHits) +{ + Ps::Mutex::ScopedLock lock(mMutex); + + PvdSweep sweepQuery; + pushBackT(getGeometries(mInUse), PxGeometryHolder(geometry), sweepQuery.mGeometries, getArrayName(getGeometries(mInUse))); // PT: TODO: optimize this. We memcopy once to the stack, then again to the array.... + pushBackT(mPoses, pose, sweepQuery.mPoses, getArrayName(mPoses)); + pushBackT(mFilterData, fd.data, sweepQuery.mFilterData, getArrayName(mFilterData)); + + const PxGeometryType::Enum type = geometry.getType(); // PT: TODO: QueryID::QUERY_LINEAR_xxx_SWEEP_ALL_OBJECTS are never used! + if(type==PxGeometryType::eBOX) sweepQuery.mType = QueryID::QUERY_LINEAR_OBB_SWEEP_CLOSEST_OBJECT; + else if(type==PxGeometryType::eSPHERE || type==PxGeometryType::eCAPSULE) sweepQuery.mType = QueryID::QUERY_LINEAR_CAPSULE_SWEEP_CLOSEST_OBJECT; + else if(type==PxGeometryType::eCONVEXMESH) sweepQuery.mType = QueryID::QUERY_LINEAR_CONVEX_SWEEP_CLOSEST_OBJECT; + else PX_ASSERT(0); + sweepQuery.mUnitDir = unitDir; + sweepQuery.mDistance = distance; + clampNbHits(hitsNum, fd, multipleHits); + + accumulate(sweepQuery, mAccumulatedSweepQueries, getArrayName(mPvdSqHits), mPvdSqHits, hit, hitsNum, fd); +} + +void PvdSceneQueryCollector::overlapMultiple(const PxGeometry& geometry, const PxTransform& pose, const PxOverlapHit* hit, PxU32 hitsNum, const PxQueryFilterData& fd) +{ + Ps::Mutex::ScopedLock lock(mMutex); + + PvdOverlap overlapQuery; + pushBackT(getGeometries(mInUse), PxGeometryHolder(geometry), overlapQuery.mGeometries, getArrayName(getGeometries(mInUse))); // PT: TODO: optimize this. We memcopy once to the stack, then again to the array.... + + const PxGeometryType::Enum type = geometry.getType(); + if(type==PxGeometryType::eBOX) overlapQuery.mType = pose.q.isIdentity() ? QueryID::QUERY_OVERLAP_AABB_ALL_OBJECTS : QueryID::QUERY_OVERLAP_OBB_ALL_OBJECTS; + else if(type==PxGeometryType::eSPHERE) overlapQuery.mType = QueryID::QUERY_OVERLAP_SPHERE_ALL_OBJECTS; + else if(type==PxGeometryType::eCAPSULE) overlapQuery.mType = QueryID::QUERY_OVERLAP_CAPSULE_ALL_OBJECTS; + else if(type==PxGeometryType::eCONVEXMESH) overlapQuery.mType = QueryID::QUERY_OVERLAP_CONVEX_ALL_OBJECTS; + else PX_ASSERT(0); + overlapQuery.mPose = pose; + overlapQuery.mFilterData = fd.data; + + accumulate(overlapQuery, mAccumulatedOverlapQueries, getArrayName(mPvdSqHits), mPvdSqHits, hit, hitsNum, fd); +} +#endif diff --git a/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h new file mode 100644 index 000000000..3290c884d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h @@ -0,0 +1,231 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_PVD_SCENEQUERYCOLLECTOR_H +#define NP_PVD_SCENEQUERYCOLLECTOR_H + +#include "CmPhysXCommon.h" +#include "PsArray.h" +#include "PxFiltering.h" +#include "PxGeometryHelpers.h" +#include "PxQueryReport.h" +#include "PxBatchQueryDesc.h" + +#if PX_SUPPORT_PVD + +namespace physx +{ +namespace Scb +{ +class Scene; +} + +namespace Vd +{ +struct PvdReference +{ + PX_FORCE_INLINE PvdReference() {} + PX_FORCE_INLINE PvdReference(const char* arrayName, PxU32 baseIndex, PxU32 count) : mArrayName(arrayName), mBaseIndex(baseIndex), mCount(count) {} + + const char* mArrayName; + PxU32 mBaseIndex; + PxU32 mCount; +}; + +struct PvdRaycast +{ + PxU32 mType; + PxFilterData mFilterData; + PxU32 mFilterFlags; + PxVec3 mOrigin; + PxVec3 mUnitDir; + PxReal mDistance; + PvdReference mHits; +}; + +struct PvdOverlap +{ + PxU32 mType; + PxFilterData mFilterData; + PxU32 mFilterFlags; + PxTransform mPose; + PvdReference mGeometries; + PvdReference mHits; +}; + +struct PvdSweep +{ + PxU32 mType; + PxU32 mFilterFlags; + PxVec3 mUnitDir; + PxReal mDistance; + PvdReference mGeometries; + PvdReference mPoses; + PvdReference mFilterData; + PvdReference mHits; +}; + +struct PvdSqHit +{ + const void* mShape; + const void* mActor; + PxU32 mFaceIndex; + PxU32 mFlags; + PxVec3 mImpact; + PxVec3 mNormal; + PxF32 mDistance; + PxF32 mU; + PxF32 mV; + + PvdSqHit() + { + setDefaults(PxQueryHit()); + } + + explicit PvdSqHit(const PxOverlapHit& hit) + { + setDefaults(hit); + } + + explicit PvdSqHit(const PxRaycastHit& hit) + { + setDefaults(hit); + + mImpact = hit.position; + mNormal = hit.normal; + mDistance = hit.distance; + mFlags = hit.flags; + mU = hit.u; + mV = hit.v; + } + + explicit PvdSqHit(const PxSweepHit& hit) + { + setDefaults(hit); + + mImpact = hit.position; + mNormal = hit.normal; + mDistance = hit.distance; + mFlags = hit.flags; + } + + private: + void setDefaults(const PxQueryHit& hit) + { + mShape = hit.shape; + mActor = hit.actor; + mFaceIndex = hit.faceIndex; + mFlags = 0; + mImpact = mNormal = PxVec3(0.0f); + mDistance = mU = mV = 0.0f; + } +}; + +template +class NamedArray : public Ps::Array +{ + public: + NamedArray(const char* names[2]) { mNames[0] = names[0]; mNames[1] = names[1]; } + + const char* mNames[2]; +}; + +class PvdSceneQueryCollector +{ + PX_NOCOPY(PvdSceneQueryCollector) +public: + PvdSceneQueryCollector(Scb::Scene& scene, bool isBatched); + ~PvdSceneQueryCollector() {} + + void clear() + { + Ps::Mutex::ScopedLock lock(mMutex); + + mAccumulatedRaycastQueries.clear(); + mAccumulatedOverlapQueries.clear(); + mAccumulatedSweepQueries.clear(); + mPvdSqHits.clear(); + mPoses.clear(); + mFilterData.clear(); + } + + void clearGeometryArrays() + { + mGeometries0.clear(); + mGeometries1.clear(); + } + + void release(); + + void raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal distance, const PxRaycastHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData, bool multipleHits); + void sweep(const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, PxReal distance, const PxSweepHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData, bool multipleHits); + void overlapMultiple(const PxGeometry& geometry, const PxTransform& pose, const PxOverlapHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData); + + void collectAllBatchedHits (const PxRaycastQueryResult* raycastResults, PxU32 nbRaycastResults, PxU32 batchedRayQstartIdx, + const PxOverlapQueryResult* overlapResults, PxU32 nbOverlapResults, PxU32 batchedOverlapQstartIdx, + const PxSweepQueryResult* sweepResults, PxU32 nbSweepResults, PxU32 batchedSweepQstartIdx); + + PX_FORCE_INLINE Ps::Mutex& getLock() { return mMutex; } + + template + PX_FORCE_INLINE const char* getArrayName(const NamedArray& namedArray) const { return namedArray.mNames[mIsBatched]; } + + PX_FORCE_INLINE const NamedArray& getGeometries(PxU32 index) const { return index ? mGeometries1 : mGeometries0; } + PX_FORCE_INLINE NamedArray& getGeometries(PxU32 index) { return index ? mGeometries1 : mGeometries0; } + + PX_FORCE_INLINE const NamedArray& getCurrentFrameGeometries() const { return getGeometries(mInUse); } + PX_FORCE_INLINE const NamedArray& getPrevFrameGeometries() const { return getGeometries(mInUse ^ 1); } + + void prepareNextFrameGeometries() + { + mInUse ^= 1; + getGeometries(mInUse).clear(); + } + + NamedArray mAccumulatedRaycastQueries; + NamedArray mAccumulatedSweepQueries; + NamedArray mAccumulatedOverlapQueries; + NamedArray mPvdSqHits; + NamedArray mPoses; + NamedArray mFilterData; + +private: + Scb::Scene& mScene; + Ps::Mutex mMutex; + NamedArraymGeometries0; + NamedArraymGeometries1; + PxU32 mInUse; + const bool mIsBatched; +}; +} +} + +#endif // PX_SUPPORT_PVD + +#endif // NP_PVD_SCENEQUERYCOLLECTOR_H diff --git a/src/PhysX/physx/source/physx/src/NpQueryShared.h b/src/PhysX/physx/source/physx/src/NpQueryShared.h new file mode 100644 index 000000000..531cf17a7 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpQueryShared.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_QUERYSHARED +#define PX_PHYSICS_NP_QUERYSHARED + +#include "foundation/PxMemory.h" + +namespace physx +{ + +using namespace Cm; + +PX_FORCE_INLINE bool applyFilterEquation(const Scb::Shape& scbShape, const PxFilterData& queryFd) +{ + // if the filterData field is non-zero, and the bitwise-AND value of filterData AND the shape's + // queryFilterData is zero, the shape is skipped. + if(queryFd.word0 | queryFd.word1 | queryFd.word2 | queryFd.word3) + { + const PxFilterData& objFd = scbShape.getScShape().getQueryFilterData(); + const PxU32 keep = (queryFd.word0 & objFd.word0) | (queryFd.word1 & objFd.word1) | (queryFd.word2 & objFd.word2) | (queryFd.word3 & objFd.word3); + if(!keep) + return false; + } + return true; +} + +//======================================================================================================================== +// these partial template specializations are used to generalize the query code to be reused for all permutations of +// hit type=(raycast, overlap, sweep) x query type=(ANY, SINGLE, MULTIPLE) +template struct HitTypeSupport { enum { IsRaycast = 0, IsSweep = 0, IsOverlap = 0 }; }; +template <> struct HitTypeSupport +{ + enum { IsRaycast = 1, IsSweep = 0, IsOverlap = 0 }; + static PX_FORCE_INLINE PxReal getDistance(const PxQueryHit& hit) { return static_cast(hit).distance; } +}; +template <> struct HitTypeSupport +{ + enum { IsRaycast = 0, IsSweep = 1, IsOverlap = 0 }; + static PX_FORCE_INLINE PxReal getDistance(const PxQueryHit& hit) { return static_cast(hit).distance; } +}; +template <> struct HitTypeSupport +{ + enum { IsRaycast = 0, IsSweep = 0, IsOverlap = 1 }; + static PX_FORCE_INLINE PxReal getDistance(const PxQueryHit&) { return -1.0f; } +}; + +#define HITDIST(hit) HitTypeSupport::getDistance(hit) + +template +static PxU32 clipHitsToNewMaxDist(HitType* ppuHits, PxU32 count, PxReal newMaxDist) +{ + PxU32 i=0; + while(i!=count) + { + if(HITDIST(ppuHits[i]) > newMaxDist) + ppuHits[i] = ppuHits[--count]; + else + i++; + } + return count; +} + +} // namespace physx + +#endif // PX_PHYSICS_NP_QUERYSHARED diff --git a/src/PhysX/physx/source/physx/src/NpReadCheck.cpp b/src/PhysX/physx/source/physx/src/NpReadCheck.cpp new file mode 100644 index 000000000..3610823cf --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpReadCheck.cpp @@ -0,0 +1,83 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpReadCheck.h" + +#include "NpScene.h" + +using namespace physx; + +NpReadCheck::NpReadCheck(const NpScene* scene, const char* functionName) + : mScene(scene), mName(functionName), mErrorCount(0) +{ + if (mScene) + { + if (!mScene->startRead()) + { + if (mScene->getScene().getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "An API read call (%s) was made from thread %d but PxScene::lockRead() was not called first, note that " + "when PxSceneFlag::eREQUIRE_RW_LOCK is enabled all API reads and writes must be " + "wrapped in the appropriate locks.", mName, PxU32(Ps::Thread::getId())); + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Overlapping API read and write call detected during %s from thread %d! Note that read operations to " + "the SDK must not be overlapped with write calls, else the resulting behavior is undefined.", mName, PxU32(Ps::Thread::getId())); + } + } + + // Record the NpScene read/write error counter which is + // incremented any time a NpScene::startWrite/startRead fails + // (see destructor for additional error checking based on this count) + mErrorCount = mScene->getReadWriteErrorCount(); + } +} + + +NpReadCheck::~NpReadCheck() +{ + if (mScene) + { + // By checking if the NpScene::mConcurrentErrorCount has been incremented + // we can detect if an erroneous read/write was performed during + // this objects lifetime. In this case we also print this function's + // details so that the user can see which two API calls overlapped + if (mScene->getReadWriteErrorCount() != mErrorCount && !(mScene->getScene().getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Leaving %s on thread %d, an API overlapping write on another thread was detected.", mName, PxU32(Ps::Thread::getId())); + } + + mScene->stopRead(); + } +} diff --git a/src/PhysX/physx/source/physx/src/NpReadCheck.h b/src/PhysX/physx/source/physx/src/NpReadCheck.h new file mode 100644 index 000000000..a3303b905 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpReadCheck.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef NP_READ_CHECK_H +#define NP_READ_CHECK_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + +class NpScene; + +// RAII wrapper around the PxScene::startRead() method, note that this +// object does not acquire any scene locks, it is an error checking only mechanism +class NpReadCheck +{ +public: + NpReadCheck(const NpScene* scene, const char* functionName); + ~NpReadCheck(); +private: + const NpScene* mScene; + const char* mName; + PxU32 mErrorCount; +}; + +#if (PX_DEBUG || PX_CHECKED) + // Creates a scoped read check object that detects whether appropriate scene locks + // have been acquired and checks if reads/writes overlap, this macro should typically + // be placed at the beginning of any const API methods that are not multi-thread safe, + // the error conditions checked can be summarized as: + + // 1. PxSceneFlag::eREQUIRE_RW_LOCK was specified but PxScene::lockRead() was not yet called + // 2. Other threads were already writing, or began writing during the object lifetime + #define NP_READ_CHECK(npScenePtr) NpReadCheck npReadCheck(static_cast(npScenePtr), __FUNCTION__); +#else + #define NP_READ_CHECK(npScenePtr) +#endif + +} + +#endif // NP_Read_CHECK_H diff --git a/src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h b/src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h new file mode 100644 index 000000000..6cacd67cf --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h @@ -0,0 +1,452 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_RIGIDACTOR_TEMPLATE +#define PX_PHYSICS_NP_RIGIDACTOR_TEMPLATE + +#include "NpActorTemplate.h" +#include "NpShapeManager.h" +#include "NpConstraint.h" +#include "NpFactory.h" + +// PX_SERIALIZATION +#include "foundation/PxErrors.h" +//~PX_SERIALIZATION + +namespace physx +{ + +template +class NpRigidActorTemplate : public NpActorTemplate +{ +private: + typedef NpActorTemplate ActorTemplateClass; + +public: +// PX_SERIALIZATION + NpRigidActorTemplate(PxBaseFlags baseFlags) : ActorTemplateClass(baseFlags), mShapeManager(PxEmpty), mIndex(0xFFFFFFFF) {} + virtual void requiresObjects(PxProcessPxBaseCallback& c); + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); +//~PX_SERIALIZATION + virtual ~NpRigidActorTemplate(); + + //--------------------------------------------------------------------------------- + // PxActor implementation + //--------------------------------------------------------------------------------- + virtual void release(); + + // The rule is: If an API method is used somewhere in here, it has to be redeclared, else GCC whines + virtual PxActorType::Enum getType() const = 0; + + virtual PxBounds3 getWorldBounds(float inflation=1.01f) const; + + virtual void setActorFlag(PxActorFlag::Enum flag, bool value); + virtual void setActorFlags(PxActorFlags inFlags); + + //--------------------------------------------------------------------------------- + // PxRigidActor implementation + //--------------------------------------------------------------------------------- + + // Shapes + virtual PxU32 getNbShapes() const; + virtual PxU32 getShapes(PxShape** buffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + // Constraint shaders + virtual PxU32 getNbConstraints() const; + virtual PxU32 getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + // shared shapes + virtual bool attachShape(PxShape& s); + virtual void detachShape(PxShape& s, bool wakeOnLostTouch); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpRigidActorTemplate(PxType concreteType, PxBaseFlags baseFlags); + + // not optimal but the template alternative is hardly more readable and perf is not that critical here + virtual void switchToNoSim() { PX_ASSERT(false); } + virtual void switchFromNoSim() { PX_ASSERT(false); } + + void releaseShape(NpShape& s); + + PX_FORCE_INLINE NpShapeManager& getShapeManager() { return mShapeManager; } + PX_FORCE_INLINE const NpShapeManager& getShapeManager() const { return mShapeManager; } + + void updateShaderComs(); + + PX_FORCE_INLINE PxU32 getRigidActorArrayIndex() const { return mIndex; } + PX_FORCE_INLINE void setRigidActorArrayIndex(const PxU32& index) { mIndex = index; } + + bool resetFiltering(Scb::RigidObject& ro, PxShape*const* shapes, PxU32 shapeCount); + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene); +#endif +protected: + PX_FORCE_INLINE void setActorSimFlag(bool value); + + NpShapeManager mShapeManager; + PxU32 mIndex; // index for the NpScene rigid actor array +}; + +// PX_SERIALIZATION + +template +void NpRigidActorTemplate::requiresObjects(PxProcessPxBaseCallback& c) +{ + // export shapes + PxU32 nbShapes = mShapeManager.getNbShapes(); + for(PxU32 i=0;i +void NpRigidActorTemplate::exportExtraData(PxSerializationContext& stream) +{ + mShapeManager.exportExtraData(stream); + ActorTemplateClass::exportExtraData(stream); +} + +template +void NpRigidActorTemplate::importExtraData(PxDeserializationContext& context) +{ + mShapeManager.importExtraData(context); + ActorTemplateClass::importExtraData(context); +} + +template +void NpRigidActorTemplate::resolveReferences(PxDeserializationContext& context) +{ + const PxU32 nbShapes = mShapeManager.getNbShapes(); + NpShape** shapes = const_cast(mShapeManager.getShapes()); + for(PxU32 j=0;jonActorAttach(*this); + } + + ActorTemplateClass::resolveReferences(context); +} + +//~PX_SERIALIZATION + +template +NpRigidActorTemplate::NpRigidActorTemplate(PxType concreteType, PxBaseFlags baseFlags) +: ActorTemplateClass(concreteType, baseFlags, NULL, NULL) +{ +} + +template +NpRigidActorTemplate::~NpRigidActorTemplate() +{ + // TODO: no mechanism for notifying shaders of actor destruction yet +} + + +template +void NpRigidActorTemplate::release() +{ + NpActor::releaseConstraints(*this); + NpScene* scene = NpActor::getAPIScene(*this); + if(mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidActor::release: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + + mShapeManager.detachAll(scene, *this); + + ActorTemplateClass::release(); // PT: added for PxAggregate +} + + +template +void NpRigidActorTemplate::releaseShape(NpShape& s) +{ + // invalidate the pruning structure if the actor bounds changed + if (mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidActor::releaseShape: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + mShapeManager.detachShape(s, *this); +} + + +template +bool NpRigidActorTemplate::attachShape(PxShape& shape) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN_VAL(!static_cast(shape).isExclusive() || shape.getActor()==NULL, "PxRigidActor::attachShape: shape must be shared or unowned", false); + + PX_SIMD_GUARD + // invalidate the pruning structure if the actor bounds changed + if (mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidActor::attachShape: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + + mShapeManager.attachShape(static_cast(shape), *this); + return true; +} + +template +void NpRigidActorTemplate::detachShape(PxShape& shape, bool wakeOnLostTouch) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + if (mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidActor::detachShape: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + + if(!mShapeManager.detachShape(static_cast(shape), *this, wakeOnLostTouch)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidActor::detachShape: shape is not attached to this actor!"); + } +} + + +template +PxU32 NpRigidActorTemplate::getNbShapes() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mShapeManager.getNbShapes(); +} + + +template +PxU32 NpRigidActorTemplate::getShapes(PxShape** buffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mShapeManager.getShapes(buffer, bufferSize, startIndex); +} + +template +PxU32 NpRigidActorTemplate::getNbConstraints() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return ActorTemplateClass::getNbConnectors(NpConnectorType::eConstraint); +} + + +template +PxU32 NpRigidActorTemplate::getConstraints(PxConstraint** buffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return ActorTemplateClass::template getConnectors(NpConnectorType::eConstraint, buffer, bufferSize, startIndex); // Some people will love me for this one... The syntax is to be standard compliant and + // picky gcc won't compile without it. It is needed if you call a templated member function + // of a templated class +} + +template +PxBounds3 NpRigidActorTemplate::getWorldBounds(float inflation) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + PX_SIMD_GUARD; + + const PxBounds3 bounds = mShapeManager.getWorldBounds(*this); + PX_ASSERT(bounds.isValid()); + + // PT: unfortunately we can't just scale the min/max vectors, we need to go through center/extents. + const PxVec3 center = bounds.getCenter(); + const PxVec3 inflatedExtents = bounds.getExtents() * inflation; + return PxBounds3::centerExtents(center, inflatedExtents); +} + + +template +PX_FORCE_INLINE void NpRigidActorTemplate::setActorSimFlag(bool value) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + + PxActorFlags oldFlags = ActorTemplateClass::getActorFlags(); + bool hadNoSimFlag = oldFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + + PX_CHECK_AND_RETURN((getType() != PxActorType::eARTICULATION_LINK) || (!value && !hadNoSimFlag), "PxActor::setActorFlag: PxActorFlag::eDISABLE_SIMULATION is only supported by PxRigidDynamic and PxRigidStatic objects."); + + if (hadNoSimFlag && (!value)) + { + switchFromNoSim(); + ActorTemplateClass::setActorFlagsInternal(oldFlags & (~PxActorFlag::eDISABLE_SIMULATION)); // needs to be done before the code below to make sure the latest flags get picked up + if (scene) + NpActor::addConstraintsToScene(); + } + else if ((!hadNoSimFlag) && value) + { + if (scene) + NpActor::removeConstraintsFromScene(); + ActorTemplateClass::setActorFlagsInternal(oldFlags | PxActorFlag::eDISABLE_SIMULATION); + switchToNoSim(); + } +} + + +template +void NpRigidActorTemplate::setActorFlag(PxActorFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + if (flag == PxActorFlag::eDISABLE_SIMULATION) + setActorSimFlag(value); + + ActorTemplateClass::setActorFlagInternal(flag, value); +} + + +template +void NpRigidActorTemplate::setActorFlags(PxActorFlags inFlags) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + bool noSim = inFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + setActorSimFlag(noSim); + + ActorTemplateClass::setActorFlagsInternal(inFlags); +} + + +template +void NpRigidActorTemplate::updateShaderComs() +{ + NpConnectorIterator iter = ActorTemplateClass::getConnectorIterator(NpConnectorType::eConstraint); + while (PxBase* ser = iter.getNext()) + { + NpConstraint* c = static_cast(ser); + c->comShift(this); + } +} + + +PX_FORCE_INLINE static void fillResetFilteringShapeList(Scb::RigidObject& ro, Scb::Shape& scb, Scb::Shape** shapes, PxU32& shapeCount) +{ + // It is important to filter out shapes that have been added while the simulation was running because for those there is nothing to do. + + if (!ro.isBuffered(Scb::RigidObjectBuffer::BF_Shapes) || !ro.isAddedShape(scb)) + { + shapes[shapeCount] = &scb; + shapeCount++; + } +} + + +template +bool NpRigidActorTemplate::resetFiltering(Scb::RigidObject& ro, PxShape*const* shapes, PxU32 shapeCount) +{ + PX_CHECK_AND_RETURN_VAL(!(ro.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxScene::resetFiltering(): Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!", false); + for(PxU32 i=0; i < shapeCount; i++) + { +#if PX_CHECKED + PxRigidActor* ra = shapes[i]->getActor(); + if (ra != this) + { + bool found = false; + if (ra == NULL) + { + NpShape*const* sh = mShapeManager.getShapes(); + for(PxU32 j=0; j < mShapeManager.getNbShapes(); j++) + { + if (sh[j] == shapes[i]) + { + found = true; + break; + } + } + } + + PX_CHECK_AND_RETURN_VAL(found, "PxScene::resetFiltering(): specified shape not in actor!", false); + } +#endif + PX_CHECK_AND_RETURN_VAL(static_cast(shapes[i])->getScbShape().getFlags() & (PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE), "PxScene::resetFiltering(): specified shapes not of type eSIMULATION_SHAPE or eTRIGGER_SHAPE!", false); + } + + PxU32 sCount; + if (shapes) + sCount = shapeCount; + else + sCount = mShapeManager.getNbShapes(); + + PX_ALLOCA(scbShapes, Scb::Shape*, sCount); + if (scbShapes) + { + if (shapes) // the user specified the shapes + { + PxU32 sAccepted = 0; + for(PxU32 i=0; i < sCount; i++) + { + Scb::Shape& scb = static_cast(shapes[i])->getScbShape(); + fillResetFilteringShapeList(ro, scb, scbShapes, sAccepted); + } + sCount = sAccepted; + } + else // the user just specified the actor and the shapes are taken from the actor + { + NpShape* const* sh = mShapeManager.getShapes(); + PxU32 sAccepted = 0; + for(PxU32 i=0; i < sCount; i++) + { + Scb::Shape& scb = sh[i]->getScbShape(); + if (scb.getFlags() & (PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) + { + fillResetFilteringShapeList(ro, scb, scbShapes, sAccepted); + } + } + sCount = sAccepted; + } + + if (sCount) + { + ro.resetFiltering(scbShapes, sCount); + } + } + + return true; +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +template +void NpRigidActorTemplate::visualize(Cm::RenderOutput& out, NpScene* scene) +{ + mShapeManager.visualize(out, scene, *this); +} +#endif // PX_ENABLE_DEBUG_VISUALIZATION + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h b/src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h new file mode 100644 index 000000000..f53f334b5 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NP_RIGIDACTOR_TEMPLATE_INTERNAL +#define PX_PHYSICS_NP_RIGIDACTOR_TEMPLATE_INTERNAL + +namespace physx +{ + +template +static PX_FORCE_INLINE void releaseActorT(NpRigidActorTemplate* actor, T2& scbActor) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*actor)); + + NpPhysics::getInstance().notifyDeletionListenersUserRelease(actor, actor->userData); + + Scb::Scene* s = scbActor.getScbSceneForAPI(); + + const bool noSim = scbActor.isSimDisabledInternally(); + // important to check the non-buffered flag because it tells what the current internal state of the object is + // (someone might switch to non-simulation and release all while the sim is running). Reading is fine even if + // the sim is running because actor flags are read-only internally. + if(s && noSim) + { + // need to do it here because the Np-shape buffer will not be valid anymore after the release below + // and unlike simulation objects, there is no shape buffer in the simulation controller + actor->getShapeManager().clearShapesOnRelease(*s, *actor); + } + + actor->NpRigidActorTemplate::release(); + + if(s) + { + s->removeActor(scbActor, true, noSim); + static_cast(s->getPxScene())->removeFromRigidActorList(actor->getRigidActorArrayIndex()); + } + + scbActor.destroy(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h b/src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h new file mode 100644 index 000000000..dea694f13 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h @@ -0,0 +1,631 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_RIGIDBODY_TEMPLATE +#define PX_PHYSICS_NP_RIGIDBODY_TEMPLATE + +#include "NpRigidActorTemplate.h" +#include "ScbBody.h" +#include "NpPhysics.h" +#include "NpShape.h" +#include "NpScene.h" + +namespace physx +{ + +PX_INLINE PxVec3 invertDiagInertia(const PxVec3& m) +{ + return PxVec3( m.x == 0.0f ? 0.0f : 1.0f/m.x, + m.y == 0.0f ? 0.0f : 1.0f/m.y, + m.z == 0.0f ? 0.0f : 1.0f/m.z); +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +/* +given the diagonal of the body space inertia tensor, and the total mass +this returns the body space AABB width, height and depth of an equivalent box +*/ +PX_INLINE PxVec3 getDimsFromBodyInertia(const PxVec3& inertiaMoments, PxReal mass) +{ + const PxVec3 inertia = inertiaMoments * (6.0f/mass); + return PxVec3( PxSqrt(PxAbs(- inertia.x + inertia.y + inertia.z)), + PxSqrt(PxAbs(+ inertia.x - inertia.y + inertia.z)), + PxSqrt(PxAbs(+ inertia.x + inertia.y - inertia.z))); +} +#endif + + +template +class NpRigidBodyTemplate : public NpRigidActorTemplate +{ +private: + typedef NpRigidActorTemplate RigidActorTemplateClass; +public: +// PX_SERIALIZATION + NpRigidBodyTemplate(PxBaseFlags baseFlags) : RigidActorTemplateClass(baseFlags), mBody(PxEmpty) {} +//~PX_SERIALIZATION + virtual ~NpRigidBodyTemplate(); + + //--------------------------------------------------------------------------------- + // PxRigidActor implementation + //--------------------------------------------------------------------------------- + // The rule is: If an API method is used somewhere in here, it has to be redeclared, else GCC whines + virtual PxTransform getGlobalPose() const = 0; + + //--------------------------------------------------------------------------------- + // PxRigidBody implementation + //--------------------------------------------------------------------------------- + + // Center of mass pose + virtual PxTransform getCMassLocalPose() const; + + // Mass + virtual void setMass(PxReal mass); + virtual PxReal getMass() const; + virtual PxReal getInvMass() const; + + virtual void setMassSpaceInertiaTensor(const PxVec3& m); + virtual PxVec3 getMassSpaceInertiaTensor() const; + virtual PxVec3 getMassSpaceInvInertiaTensor() const; + + // Velocity + virtual PxVec3 getLinearVelocity() const; + virtual PxVec3 getAngularVelocity() const; + + virtual bool attachShape(PxShape& shape); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpRigidBodyTemplate(PxType concreteType, PxBaseFlags baseFlags, const PxActorType::Enum type, const PxTransform& bodyPose); + + PX_FORCE_INLINE const Scb::Body& getScbBodyFast() const { return mBody; } // PT: important: keep returning an address here (else update prefetch in SceneQueryManager::addShapes) + PX_FORCE_INLINE Scb::Body& getScbBodyFast() { return mBody; } // PT: important: keep returning an address here (else update prefetch in SceneQueryManager::addShapes) + + PX_FORCE_INLINE Scb::Actor& getScbActorFast() { return mBody; } + PX_FORCE_INLINE const Scb::Actor& getScbActorFast() const { return mBody; } + + // Flags + virtual void setRigidBodyFlag(PxRigidBodyFlag::Enum, bool value); + virtual void setRigidBodyFlags(PxRigidBodyFlags inFlags); + PX_FORCE_INLINE PxRigidBodyFlags getRigidBodyFlagsFast() const + { + return getScbBodyFast().getFlags(); + } + virtual PxRigidBodyFlags getRigidBodyFlags() const + { + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return getRigidBodyFlagsFast(); + } + + virtual void setMinCCDAdvanceCoefficient(PxReal advanceCoefficient); + + virtual PxReal getMinCCDAdvanceCoefficient() const; + + virtual void setMaxDepenetrationVelocity(PxReal maxDepenVel); + + virtual PxReal getMaxDepenetrationVelocity() const; + + virtual void setMaxContactImpulse(PxReal maxDepenVel); + + virtual PxReal getMaxContactImpulse() const; + + virtual PxU32 getInternalIslandNodeIndex() const; + +protected: + void setCMassLocalPoseInternal(const PxTransform&); + + void addSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode); + void clearSpatialForce(PxForceMode::Enum mode, bool force, bool torque); + void setSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode); + + PX_INLINE void updateBody2Actor(const PxTransform& newBody2Actor); + + PX_FORCE_INLINE void setRigidBodyFlagsInternal(const PxRigidBodyFlags& currentFlags, const PxRigidBodyFlags& newFlags); + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene); +#endif + + PX_FORCE_INLINE bool isKinematic() + { + return (APIClass::getConcreteType() == PxConcreteType::eRIGID_DYNAMIC) && (getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC); + } + +protected: + Scb::Body mBody; +}; + + +template +NpRigidBodyTemplate::NpRigidBodyTemplate(PxType concreteType, PxBaseFlags baseFlags, PxActorType::Enum type, const PxTransform& bodyPose) +: RigidActorTemplateClass(concreteType, baseFlags) +, mBody(type, bodyPose) +{ +} + + +template +NpRigidBodyTemplate::~NpRigidBodyTemplate() +{ +} + +namespace +{ + PX_FORCE_INLINE static bool isSimGeom(PxGeometryType::Enum t) + { + return t != PxGeometryType::eTRIANGLEMESH && t != PxGeometryType::ePLANE && t != PxGeometryType::eHEIGHTFIELD; + } +} + +template +bool NpRigidBodyTemplate::attachShape(PxShape& shape) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN_VAL(!(shape.getFlags() & PxShapeFlag::eSIMULATION_SHAPE) + || isSimGeom(shape.getGeometryType()) + || isKinematic(), + "attachShape: Triangle mesh, heightfield or plane geometry shapes configured as eSIMULATION_SHAPE are not supported for non-kinematic PxRigidDynamic instances.", false); + return RigidActorTemplateClass::attachShape(shape); +} + + +template +void NpRigidBodyTemplate::setCMassLocalPoseInternal(const PxTransform& body2Actor) +{ + //the point here is to change the mass distribution w/o changing the actors' pose in the world + + PxTransform newBody2World = getGlobalPose() * body2Actor; + + mBody.setBody2World(newBody2World, true); + mBody.setBody2Actor(body2Actor); + + RigidActorTemplateClass::updateShaderComs(); +} + + +template +PxTransform NpRigidBodyTemplate::getCMassLocalPose() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getBody2Actor(); +} + + +template +void NpRigidBodyTemplate::setMass(PxReal mass) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(mass), "PxRigidDynamic::setMass: invalid float"); + PX_CHECK_AND_RETURN(mass>=0, "PxRigidDynamic::setMass: mass must be non-negative!"); + PX_CHECK_AND_RETURN(this->getType() != PxActorType::eARTICULATION_LINK || mass > 0.0f, "PxRigidDynamic::setMassSpaceInertiaTensor: components must be > 0 for articualtions"); + + getScbBodyFast().setInverseMass(mass > 0.0f ? 1.0f/mass : 0.0f); +} + + +template +PxReal NpRigidBodyTemplate::getMass() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + const PxReal invMass = mBody.getInverseMass(); + + return invMass > 0.0f ? 1.0f/invMass : 0.0f; +} + +template +PxReal NpRigidBodyTemplate::getInvMass() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return mBody.getInverseMass(); +} + + +template +void NpRigidBodyTemplate::setMassSpaceInertiaTensor(const PxVec3& m) +{ + PX_CHECK_AND_RETURN(m.isFinite(), "PxRigidDynamic::setMassSpaceInertiaTensor: invalid inertia"); + PX_CHECK_AND_RETURN(m.x>=0.0f && m.y>=0.0f && m.z>=0.0f, "PxRigidDynamic::setMassSpaceInertiaTensor: components must be non-negative"); + PX_CHECK_AND_RETURN(this->getType() != PxActorType::eARTICULATION_LINK || (m.x > 0.0f && m.y > 0.0f && m.z > 0.0f), "PxRigidDynamic::setMassSpaceInertiaTensor: components must be > 0 for articualtions"); + + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + mBody.setInverseInertia(invertDiagInertia(m)); +} + + +template +PxVec3 NpRigidBodyTemplate::getMassSpaceInertiaTensor() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return invertDiagInertia(mBody.getInverseInertia()); +} + +template +PxVec3 NpRigidBodyTemplate::getMassSpaceInvInertiaTensor() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return mBody.getInverseInertia(); +} + + +template +PxVec3 NpRigidBodyTemplate::getLinearVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return mBody.getLinearVelocity(); +} + + +template +PxVec3 NpRigidBodyTemplate::getAngularVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return mBody.getAngularVelocity(); +} + + +template +void NpRigidBodyTemplate::addSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode) +{ + PX_ASSERT(!(mBody.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + switch (mode) + { + case PxForceMode::eFORCE: + { + PxVec3 linAcc, angAcc; + if (force) + { + linAcc = (*force) * mBody.getInverseMass(); + force = &linAcc; + } + if (torque) + { + angAcc = mBody.getGlobalInertiaTensorInverse() * (*torque); + torque = &angAcc; + } + mBody.addSpatialAcceleration(force, torque); + } + break; + + case PxForceMode::eACCELERATION: + mBody.addSpatialAcceleration(force, torque); + break; + + case PxForceMode::eIMPULSE: + { + PxVec3 linVelDelta, angVelDelta; + if (force) + { + linVelDelta = ((*force) * mBody.getInverseMass()); + force = &linVelDelta; + } + if (torque) + { + angVelDelta = (mBody.getGlobalInertiaTensorInverse() * (*torque)); + torque = &angVelDelta; + } + mBody.addSpatialVelocity(force, torque); + } + break; + + case PxForceMode::eVELOCITY_CHANGE: + mBody.addSpatialVelocity(force, torque); + break; + } +} + +template +void NpRigidBodyTemplate::setSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode) +{ + PX_ASSERT(!(mBody.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + switch (mode) + { + case PxForceMode::eFORCE: + { + PxVec3 linAcc, angAcc; + if (force) + { + linAcc = (*force) * mBody.getInverseMass(); + force = &linAcc; + } + if (torque) + { + angAcc = mBody.getGlobalInertiaTensorInverse() * (*torque); + torque = &angAcc; + } + mBody.setSpatialAcceleration(force, torque); + } + break; + + case PxForceMode::eACCELERATION: + mBody.setSpatialAcceleration(force, torque); + break; + + case PxForceMode::eIMPULSE: + { + PxVec3 linVelDelta, angVelDelta; + if (force) + { + linVelDelta = ((*force) * mBody.getInverseMass()); + force = &linVelDelta; + } + if (torque) + { + angVelDelta = (mBody.getGlobalInertiaTensorInverse() * (*torque)); + torque = &angVelDelta; + } + mBody.addSpatialVelocity(force, torque); + } + break; + + case PxForceMode::eVELOCITY_CHANGE: + mBody.addSpatialVelocity(force, torque); + break; + } +} + +template +void NpRigidBodyTemplate::clearSpatialForce(PxForceMode::Enum mode, bool force, bool torque) +{ + PX_ASSERT(!(mBody.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + switch (mode) + { + case PxForceMode::eFORCE: + case PxForceMode::eACCELERATION: + mBody.clearSpatialAcceleration(force, torque); + break; + case PxForceMode::eIMPULSE: + case PxForceMode::eVELOCITY_CHANGE: + mBody.clearSpatialVelocity(force, torque); + break; + } +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +template +void NpRigidBodyTemplate::visualize(Cm::RenderOutput& out, NpScene* scene) +{ + RigidActorTemplateClass::visualize(out, scene); + + if (mBody.getActorFlags() & PxActorFlag::eVISUALIZATION) + { + Scb::Scene& scbScene = scene->getScene(); + const PxReal scale = scbScene.getVisualizationParameter(PxVisualizationParameter::eSCALE); + + //visualize actor frames + const PxReal actorAxes = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eACTOR_AXES); + if (actorAxes != 0.0f) + out << getGlobalPose() << Cm::DebugBasis(PxVec3(actorAxes)); + + const PxReal bodyAxes = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eBODY_AXES); + if (bodyAxes != 0.0f) + out << mBody.getBody2World() << Cm::DebugBasis(PxVec3(bodyAxes)); + + const PxReal linVelocity = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eBODY_LIN_VELOCITY); + if (linVelocity != 0.0f) + { + out << 0xffffff << PxMat44(PxIdentity) << Cm::DebugArrow(mBody.getBody2World().p, + mBody.getLinearVelocity() * linVelocity, 0.2f * linVelocity); + } + + const PxReal angVelocity = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eBODY_ANG_VELOCITY); + if (angVelocity != 0.0f) + { + out << 0x000000 << PxMat44(PxIdentity) << Cm::DebugArrow(mBody.getBody2World().p, + mBody.getAngularVelocity() * angVelocity, 0.2f * angVelocity); + } + } +} +#endif + + +PX_FORCE_INLINE void updateDynamicSceneQueryShapes(NpShapeManager& shapeManager, Sq::SceneQueryManager& sqManager, const PxRigidActor& actor) +{ + shapeManager.markAllSceneQueryForUpdate(sqManager, actor); + sqManager.get(Sq::PruningIndex::eDYNAMIC).invalidateTimestamp(); +} + + +template +PX_FORCE_INLINE void NpRigidBodyTemplate::setRigidBodyFlagsInternal(const PxRigidBodyFlags& currentFlags, const PxRigidBodyFlags& newFlags) +{ + PxRigidBodyFlags filteredNewFlags = newFlags; + //Test to ensure we are not enabling both CCD and kinematic state on a body. This is unsupported + if((filteredNewFlags & PxRigidBodyFlag::eENABLE_CCD) && (filteredNewFlags & PxRigidBodyFlag::eKINEMATIC)) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "RigidBody::setRigidBodyFlag: kinematic bodies with CCD enabled are not supported! CCD will be ignored."); + filteredNewFlags &= PxRigidBodyFlags(~PxRigidBodyFlag::eENABLE_CCD); + } + + if ((filteredNewFlags & PxRigidBodyFlag::eENABLE_CCD) && (filteredNewFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD)) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "RigidBody::setRigidBodyFlag: eENABLE_CCD can't be raised as the same time as eENABLE_SPECULATIVE_CCD! eENABLE_SPECULATIVE_CCD will be ignored."); + filteredNewFlags &= PxRigidBodyFlags(~PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD); + } + + Scb::Body& body = getScbBodyFast(); + NpScene* scene = NpActor::getAPIScene(*this); + + const bool isKinematic = currentFlags & PxRigidBodyFlag::eKINEMATIC; + const bool willBeKinematic = filteredNewFlags & PxRigidBodyFlag::eKINEMATIC; + const bool kinematicSwitchingToDynamic = isKinematic && (!willBeKinematic); + const bool dynamicSwitchingToKinematic = (!isKinematic) && willBeKinematic; + + if(kinematicSwitchingToDynamic) + { + NpShapeManager& shapeManager = this->getShapeManager(); + PxU32 nbShapes = shapeManager.getNbShapes(); + NpShape*const* shapes = shapeManager.getShapes(); + bool hasTriangleMesh = false; + for(PxU32 i=0;igetFlags() & PxShapeFlag::eSIMULATION_SHAPE) && (shapes[i]->getGeometryTypeFast()==PxGeometryType::eTRIANGLEMESH || shapes[i]->getGeometryTypeFast()==PxGeometryType::ePLANE || shapes[i]->getGeometryTypeFast()==PxGeometryType::eHEIGHTFIELD)) + { + hasTriangleMesh = true; + break; + } + } + if(hasTriangleMesh) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "RigidBody::setRigidBodyFlag: dynamic meshes/planes/heightfields are not supported!"); + return; + } + + PxTransform bodyTarget; + if ((currentFlags & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES) && body.getKinematicTarget(bodyTarget) && scene) + { + updateDynamicSceneQueryShapes(shapeManager, scene->getSceneQueryManagerFast(), *this); + } + + body.clearSimStateDataForPendingInsert(); + } + else if (dynamicSwitchingToKinematic) + { + if (this->getType() != PxActorType::eARTICULATION_LINK) + { + body.transitionSimStateDataForPendingInsert(); + } + else + { + //We're an articulation, raise an issue + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "RigidBody::setRigidBodyFlag: kinematic articulation links are not supported!"); + return; + } + } + + const bool kinematicSwitchingUseTargetForSceneQuery = isKinematic && willBeKinematic && + ((currentFlags & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES) != (filteredNewFlags & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES)); + if (kinematicSwitchingUseTargetForSceneQuery) + { + PxTransform bodyTarget; + if (body.getKinematicTarget(bodyTarget) && scene) + { + updateDynamicSceneQueryShapes(this->getShapeManager(), scene->getSceneQueryManagerFast(), *this); + } + } + + body.setFlags(filteredNewFlags); +} + + +template +void NpRigidBodyTemplate::setRigidBodyFlag(PxRigidBodyFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + Scb::Body& body = getScbBodyFast(); + const PxRigidBodyFlags currentFlags = body.getFlags(); + const PxRigidBodyFlags newFlags = value ? currentFlags | flag : currentFlags & (~PxRigidBodyFlags(flag)); + + setRigidBodyFlagsInternal(currentFlags, newFlags); +} + + +template +void NpRigidBodyTemplate::setRigidBodyFlags(PxRigidBodyFlags inFlags) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + Scb::Body& body = getScbBodyFast(); + const PxRigidBodyFlags currentFlags = body.getFlags(); + + setRigidBodyFlagsInternal(currentFlags, inFlags); +} + + +template +void NpRigidBodyTemplate::setMinCCDAdvanceCoefficient(PxReal minCCDAdvanceCoefficient) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + mBody.setMinCCDAdvanceCoefficient(minCCDAdvanceCoefficient); +} + +template +PxReal NpRigidBodyTemplate::getMinCCDAdvanceCoefficient() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mBody.getMinCCDAdvanceCoefficient(); +} + +template +void NpRigidBodyTemplate::setMaxDepenetrationVelocity(PxReal maxDepenVel) +{ + PX_CHECK_AND_RETURN(maxDepenVel > 0.0f, "PxRigidDynamic::setMaxDepenetrationVelocity: maxDepenVel must be greater than zero."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + mBody.setMaxPenetrationBias(-maxDepenVel); +} + +template +PxReal NpRigidBodyTemplate::getMaxDepenetrationVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return -mBody.getMaxPenetrationBias(); +} + +template +void NpRigidBodyTemplate::setMaxContactImpulse(const PxReal maxImpulse) +{ + PX_CHECK_AND_RETURN(maxImpulse >= 0.f, "NpRigidBody::setMaxImpulse: impulse limit must be greater than or equal to zero."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + mBody.setMaxContactImpulse(maxImpulse); +} + +template +PxReal NpRigidBodyTemplate::getMaxContactImpulse() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mBody.getMaxContactImpulse(); +} + +template +PxU32 NpRigidBodyTemplate::getInternalIslandNodeIndex() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mBody.getInternalIslandNodeIndex(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp b/src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp new file mode 100644 index 000000000..f0c4573b9 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp @@ -0,0 +1,566 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpRigidDynamic.h" +#include "NpRigidActorTemplateInternal.h" + +using namespace physx; + +NpRigidDynamic::NpRigidDynamic(const PxTransform& bodyPose) +: NpRigidDynamicT(PxConcreteType::eRIGID_DYNAMIC, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, PxActorType::eRIGID_DYNAMIC, bodyPose) +{} + +NpRigidDynamic::~NpRigidDynamic() +{ +} + +// PX_SERIALIZATION +void NpRigidDynamic::requiresObjects(PxProcessPxBaseCallback& c) +{ + NpRigidDynamicT::requiresObjects(c); +} + +NpRigidDynamic* NpRigidDynamic::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpRigidDynamic* obj = new (address) NpRigidDynamic(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(NpRigidDynamic); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +void NpRigidDynamic::release() +{ + releaseActorT(this, mBody); +} + + +void NpRigidDynamic::setGlobalPose(const PxTransform& pose, bool autowake) +{ + NpScene* scene = NpActor::getAPIScene(*this); + +#if PX_CHECKED + if(scene) + scene->checkPositionSanity(*this, pose, "PxRigidDynamic::setGlobalPose"); +#endif + + PX_CHECK_AND_RETURN(pose.isSane(), "PxRigidDynamic::setGlobalPose: pose is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + const PxTransform newPose = pose.getNormalized(); //AM: added to fix 1461 where users read and write orientations for no reason. + + Scb::Body& b = getScbBodyFast(); + const PxTransform body2World = newPose * b.getBody2Actor(); + b.setBody2World(body2World, false); + + if(scene) + updateDynamicSceneQueryShapes(mShapeManager, scene->getSceneQueryManagerFast(), *this); + + // invalidate the pruning structure if the actor bounds changed + if(mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidDynamic::setGlobalPose: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + + if(scene && autowake && !(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + wakeUpInternal(); +} + + +PX_FORCE_INLINE void NpRigidDynamic::setKinematicTargetInternal(const PxTransform& targetPose) +{ + Scb::Body& b = getScbBodyFast(); + + // The target is actor related. Transform to body related target + const PxTransform bodyTarget = targetPose * b.getBody2Actor(); + + b.setKinematicTarget(bodyTarget); + + NpScene* scene = NpActor::getAPIScene(*this); + if ((b.getFlags() & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES) && scene) + { + updateDynamicSceneQueryShapes(mShapeManager, scene->getSceneQueryManagerFast(), *this); + } +} + + +void NpRigidDynamic::setKinematicTarget(const PxTransform& destination) +{ + PX_CHECK_AND_RETURN(destination.isSane(), "PxRigidDynamic::setKinematicTarget: destination is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + +#if PX_CHECKED + NpScene* scene = NpActor::getAPIScene(*this); + if(scene) + scene->checkPositionSanity(*this, destination, "PxRigidDynamic::setKinematicTarget"); + + Scb::Body& b = getScbBodyFast(); + PX_CHECK_AND_RETURN((b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::setKinematicTarget: Body must be kinematic!"); + PX_CHECK_AND_RETURN(scene, "PxRigidDynamic::setKinematicTarget: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::setKinematicTarget: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); +#endif + + setKinematicTargetInternal(destination.getNormalized()); +} + + +bool NpRigidDynamic::getKinematicTarget(PxTransform& target) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + const Scb::Body& b = getScbBodyFast(); + if(b.getFlags() & PxRigidBodyFlag::eKINEMATIC) + { + PxTransform bodyTarget; + if(b.getKinematicTarget(bodyTarget)) + { + // The internal target is body related. Transform to actor related target + target = bodyTarget * b.getBody2Actor().getInverse(); + return true; + } + } + return false; +} + +void NpRigidDynamic::setCMassLocalPose(const PxTransform& pose) +{ + PX_CHECK_AND_RETURN(pose.isSane(), "PxRigidDynamic::setCMassLocalPose pose is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + const PxTransform p = pose.getNormalized(); + + const PxTransform oldBody2Actor = getScbBodyFast().getBody2Actor(); + + NpRigidDynamicT::setCMassLocalPoseInternal(p); + + Scb::Body& b = getScbBodyFast(); + if(b.getFlags() & PxRigidBodyFlag::eKINEMATIC) + { + PxTransform bodyTarget; + if(b.getKinematicTarget(bodyTarget)) + { + PxTransform actorTarget = bodyTarget * oldBody2Actor.getInverse(); // get old target pose for the actor from the body target + setKinematicTargetInternal(actorTarget); + } + } +} + + +void NpRigidDynamic::setLinearDamping(PxReal linearDamping) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(linearDamping), "PxRigidDynamic::setLinearDamping: invalid float"); + PX_CHECK_AND_RETURN(linearDamping >=0, "PxRigidDynamic::setLinearDamping: The linear damping must be nonnegative!"); + + getScbBodyFast().setLinearDamping(linearDamping); +} + + +PxReal NpRigidDynamic::getLinearDamping() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getLinearDamping(); +} + + +void NpRigidDynamic::setAngularDamping(PxReal angularDamping) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(angularDamping), "PxRigidDynamic::setAngularDamping: invalid float"); + PX_CHECK_AND_RETURN(angularDamping>=0, "PxRigidDynamic::setAngularDamping: The angular damping must be nonnegative!") + + getScbBodyFast().setAngularDamping(angularDamping); +} + + +PxReal NpRigidDynamic::getAngularDamping() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getAngularDamping(); +} + + +void NpRigidDynamic::setLinearVelocity(const PxVec3& velocity, bool autowake) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(velocity.isFinite(), "PxRigidDynamic::setLinearVelocity: velocity is not valid."); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::setLinearVelocity: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::setLinearVelocity: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + Scb::Body& b = getScbBodyFast(); + b.setLinearVelocity(velocity); + + NpScene* scene = NpActor::getAPIScene(*this); + if(scene) + wakeUpInternalNoKinematicTest(b, (!velocity.isZero()), autowake); +} + + +void NpRigidDynamic::setAngularVelocity(const PxVec3& velocity, bool autowake) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(velocity.isFinite(), "PxRigidDynamic::setAngularVelocity: velocity is not valid."); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::setAngularVelocity: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::setAngularVelocity: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + Scb::Body& b = getScbBodyFast(); + b.setAngularVelocity(velocity); + + NpScene* scene = NpActor::getAPIScene(*this); + if(scene) + wakeUpInternalNoKinematicTest(b, (!velocity.isZero()), autowake); +} + + +void NpRigidDynamic::setMaxAngularVelocity(PxReal maxAngularVelocity) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(maxAngularVelocity), "PxRigidDynamic::setMaxAngularVelocity: invalid float"); + PX_CHECK_AND_RETURN(maxAngularVelocity>=0.0f, "PxRigidDynamic::setMaxAngularVelocity: threshold must be non-negative!"); + + getScbBodyFast().setMaxAngVelSq(maxAngularVelocity * maxAngularVelocity); +} + + +PxReal NpRigidDynamic::getMaxAngularVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return PxSqrt(getScbBodyFast().getMaxAngVelSq()); +} + +void NpRigidDynamic::setMaxLinearVelocity(PxReal maxLinearVelocity) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(maxLinearVelocity), "PxRigidDynamic::setMaxAngularVelocity: invalid float"); + PX_CHECK_AND_RETURN(maxLinearVelocity >= 0.0f, "PxRigidDynamic::setMaxAngularVelocity: threshold must be non-negative!"); + + getScbBodyFast().setMaxLinVelSq(maxLinearVelocity * maxLinearVelocity); +} + +PxReal NpRigidDynamic::getMaxLinearVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return PxSqrt(getScbBodyFast().getMaxLinVelSq()); +} + + +void NpRigidDynamic::addForce(const PxVec3& force, PxForceMode::Enum mode, bool autowake) +{ + Scb::Body& b = getScbBodyFast(); + + PX_CHECK_AND_RETURN(force.isFinite(), "PxRigidDynamic::addForce: force is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::addForce: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::addForce: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::addForce: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + addSpatialForce(&force, NULL, mode); + + wakeUpInternalNoKinematicTest(b, (!force.isZero()), autowake); +} + +void NpRigidDynamic::setForceAndTorque(const PxVec3& force, const PxVec3& torque, PxForceMode::Enum mode) +{ + Scb::Body& b = getScbBodyFast(); + + PX_CHECK_AND_RETURN(force.isFinite(), "PxRigidDynamic::setForce: force is not valid."); + PX_CHECK_AND_RETURN(torque.isFinite(), "PxRigidDynamic::setForce: force is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::addForce: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::addForce: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::addForce: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + setSpatialForce(&force, &torque, mode); + + wakeUpInternalNoKinematicTest(b, (!force.isZero()), true); +} + + +void NpRigidDynamic::addTorque(const PxVec3& torque, PxForceMode::Enum mode, bool autowake) +{ + Scb::Body& b = getScbBodyFast(); + + PX_CHECK_AND_RETURN(torque.isFinite(), "PxRigidDynamic::addTorque: torque is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::addTorque: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::addTorque: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::addTorque: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + addSpatialForce(NULL, &torque, mode); + + wakeUpInternalNoKinematicTest(b, (!torque.isZero()), autowake); +} + +void NpRigidDynamic::clearForce(PxForceMode::Enum mode) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::clearForce: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::clearForce: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::clearForce: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + clearSpatialForce(mode, true, false); +} + + +void NpRigidDynamic::clearTorque(PxForceMode::Enum mode) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::clearTorque: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::clearTorque: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::clearTorque: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + clearSpatialForce(mode, false, true); +} + + +bool NpRigidDynamic::isSleeping() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN_VAL(NpActor::getAPIScene(*this), "PxRigidDynamic::isSleeping: Body must be in a scene.", true); + + return getScbBodyFast().isSleeping(); +} + + +void NpRigidDynamic::setSleepThreshold(PxReal threshold) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(threshold), "PxRigidDynamic::setSleepThreshold: invalid float."); + PX_CHECK_AND_RETURN(threshold>=0.0f, "PxRigidDynamic::setSleepThreshold: threshold must be non-negative!"); + + getScbBodyFast().setSleepThreshold(threshold); +} + + +PxReal NpRigidDynamic::getSleepThreshold() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getSleepThreshold(); +} + +void NpRigidDynamic::setStabilizationThreshold(PxReal threshold) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(threshold), "PxRigidDynamic::setSleepThreshold: invalid float."); + PX_CHECK_AND_RETURN(threshold>=0.0f, "PxRigidDynamic::setSleepThreshold: threshold must be non-negative!"); + + getScbBodyFast().setFreezeThreshold(threshold); +} + + +PxReal NpRigidDynamic::getStabilizationThreshold() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getFreezeThreshold(); +} + + +void NpRigidDynamic::setWakeCounter(PxReal wakeCounterValue) +{ + Scb::Body& b = getScbBodyFast(); + + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(wakeCounterValue), "PxRigidDynamic::setWakeCounter: invalid float."); + PX_CHECK_AND_RETURN(wakeCounterValue>=0.0f, "PxRigidDynamic::setWakeCounter: wakeCounterValue must be non-negative!"); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::setWakeCounter: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::setWakeCounter: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + b.setWakeCounter(wakeCounterValue); +} + + +PxReal NpRigidDynamic::getWakeCounter() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getWakeCounter(); +} + + +void NpRigidDynamic::wakeUp() +{ + Scb::Body& b = getScbBodyFast(); + + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::wakeUp: Body must be in a scene."); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::wakeUp: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::wakeUp: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + b.wakeUp(); +} + + +void NpRigidDynamic::putToSleep() +{ + Scb::Body& b = getScbBodyFast(); + + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::putToSleep: Body must be in a scene."); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::putToSleep: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::putToSleep: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + b.putToSleep(); +} + + +void NpRigidDynamic::setSolverIterationCounts(PxU32 positionIters, PxU32 velocityIters) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(positionIters > 0, "PxRigidDynamic::setSolverIterationCount: positionIters must be more than zero!"); + PX_CHECK_AND_RETURN(positionIters <= 255, "PxRigidDynamic::setSolverIterationCount: positionIters must be no greater than 255!"); + PX_CHECK_AND_RETURN(velocityIters > 0, "PxRigidDynamic::setSolverIterationCount: velocityIters must be more than zero!"); + PX_CHECK_AND_RETURN(velocityIters <= 255, "PxRigidDynamic::setSolverIterationCount: velocityIters must be no greater than 255!"); + + getScbBodyFast().setSolverIterationCounts((velocityIters & 0xff) << 8 | (positionIters & 0xff)); +} + + +void NpRigidDynamic::getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + PxU16 x = getScbBodyFast().getSolverIterationCounts(); + velocityIters = PxU32(x >> 8); + positionIters = PxU32(x & 0xff); +} + + +void NpRigidDynamic::setContactReportThreshold(PxReal threshold) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(threshold), "PxRigidDynamic::setContactReportThreshold: invalid float."); + PX_CHECK_AND_RETURN(threshold >= 0.0f, "PxRigidDynamic::setContactReportThreshold: Force threshold must be greater than zero!"); + + getScbBodyFast().setContactReportThreshold(threshold<0 ? 0 : threshold); +} + + +PxReal NpRigidDynamic::getContactReportThreshold() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getContactReportThreshold(); +} + + +PxU32 physx::NpRigidDynamicGetShapes(Scb::Body& body, void* const*& shapes, bool* isCompound) +{ + NpRigidDynamic* a = static_cast(body.getScBody().getPxActor()); + NpShapeManager& sm = a->getShapeManager(); + shapes = reinterpret_cast(sm.getShapes()); + if(isCompound) + *isCompound = sm.isSqCompound(); + return sm.getNbShapes(); +} + + +void NpRigidDynamic::switchToNoSim() +{ + getScbBodyFast().switchBodyToNoSim(); +} + + +void NpRigidDynamic::switchFromNoSim() +{ + getScbBodyFast().switchFromNoSim(true); +} + + +void NpRigidDynamic::wakeUpInternalNoKinematicTest(Scb::Body& body, bool forceWakeUp, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_ASSERT(scene); + PxReal wakeCounterResetValue = scene->getWakeCounterResetValueInteral(); + + PxReal wakeCounter = body.getWakeCounter(); + + bool needsWakingUp = body.isSleeping() && (autowake || forceWakeUp); + if (autowake && (wakeCounter < wakeCounterResetValue)) + { + wakeCounter = wakeCounterResetValue; + needsWakingUp = true; + } + + if (needsWakingUp) + body.wakeUpInternal(wakeCounter); +} + +PxRigidDynamicLockFlags NpRigidDynamic::getRigidDynamicLockFlags() const +{ + return mBody.getLockFlags(); +} +void NpRigidDynamic::setRigidDynamicLockFlags(PxRigidDynamicLockFlags flags) +{ + mBody.setLockFlags(flags); +} +void NpRigidDynamic::setRigidDynamicLockFlag(PxRigidDynamicLockFlag::Enum flag, bool value) +{ + PxRigidDynamicLockFlags flags = mBody.getLockFlags(); + if (value) + flags = flags | flag; + else + flags = flags & (~flag); + + mBody.setLockFlags(flags); +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +void NpRigidDynamic::visualize(Cm::RenderOutput& out, NpScene* npScene) +{ + NpRigidDynamicT::visualize(out, npScene); + + if (getScbBodyFast().getActorFlags() & PxActorFlag::eVISUALIZATION) + { + PX_ASSERT(npScene); + const PxReal scale = npScene->getVisualizationParameter(PxVisualizationParameter::eSCALE); + + const PxReal massAxes = scale * npScene->getVisualizationParameter(PxVisualizationParameter::eBODY_MASS_AXES); + if (massAxes != 0.0f) + { + PxReal sleepTime = getScbBodyFast().getWakeCounter() / npScene->getWakeCounterResetValueInteral(); + PxU32 color = PxU32(0xff * (sleepTime>1.0f ? 1.0f : sleepTime)); + color = getScbBodyFast().isSleeping() ? 0xff0000 : (color<<16 | color<<8 | color); + PxVec3 dims = invertDiagInertia(getScbBodyFast().getInverseInertia()); + dims = getDimsFromBodyInertia(dims, 1.0f / getScbBodyFast().getInverseMass()); + + out << color << getScbBodyFast().getBody2World() << Cm::DebugBox(dims * 0.5f); + } + } +} +#endif diff --git a/src/PhysX/physx/source/physx/src/NpRigidDynamic.h b/src/PhysX/physx/source/physx/src/NpRigidDynamic.h new file mode 100644 index 000000000..ea9aaa914 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidDynamic.h @@ -0,0 +1,176 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_RIGIDDYNAMIC +#define PX_PHYSICS_NP_RIGIDDYNAMIC + +#include "NpRigidBodyTemplate.h" +#include "PxRigidDynamic.h" +#include "ScbBody.h" +#include "PxMetaData.h" + +namespace physx +{ + +class NpRigidDynamic; +typedef NpRigidBodyTemplate NpRigidDynamicT; + +class NpRigidDynamic : public NpRigidDynamicT +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpRigidDynamic(PxBaseFlags baseFlags) : NpRigidDynamicT(baseFlags) {} + + virtual void requiresObjects(PxProcessPxBaseCallback& c); + + static NpRigidDynamic* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + virtual ~NpRigidDynamic(); + + //--------------------------------------------------------------------------------- + // PxActor implementation + //--------------------------------------------------------------------------------- + + virtual void release(); + + //--------------------------------------------------------------------------------- + // PxRigidDynamic implementation + //--------------------------------------------------------------------------------- + + virtual PxActorType::Enum getType() const { return PxActorType::eRIGID_DYNAMIC; } + + // Pose + virtual void setGlobalPose(const PxTransform& pose, bool autowake); + PX_FORCE_INLINE PxTransform getGlobalPoseFast() const + { + const Scb::Body& body=getScbBodyFast(); + return body.getBody2World() * body.getBody2Actor().getInverse(); + } + virtual PxTransform getGlobalPose() const + { + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return getGlobalPoseFast(); + } + + virtual void setKinematicTarget(const PxTransform& destination); + virtual bool getKinematicTarget(PxTransform& target) const; + + // Center of mass pose + virtual void setCMassLocalPose(const PxTransform&); + + // Damping + virtual void setLinearDamping(PxReal); + virtual PxReal getLinearDamping() const; + virtual void setAngularDamping(PxReal); + virtual PxReal getAngularDamping() const; + + // Velocity + virtual void setLinearVelocity(const PxVec3&, bool autowake); + virtual void setAngularVelocity(const PxVec3&, bool autowake); + virtual void setMaxAngularVelocity(PxReal); + virtual PxReal getMaxAngularVelocity() const; + virtual void setMaxLinearVelocity(PxReal); + virtual PxReal getMaxLinearVelocity() const; + + // Force/Torque modifiers + virtual void addForce(const PxVec3&, PxForceMode::Enum mode, bool autowake); + virtual void clearForce(PxForceMode::Enum mode); + virtual void addTorque(const PxVec3&, PxForceMode::Enum mode, bool autowake); + virtual void clearTorque(PxForceMode::Enum mode); + virtual void setForceAndTorque(const PxVec3& force, const PxVec3& torque, PxForceMode::Enum mode = PxForceMode::eFORCE); + + + + // Sleeping + virtual bool isSleeping() const; + virtual PxReal getSleepThreshold() const; + virtual void setSleepThreshold(PxReal threshold); + virtual PxReal getStabilizationThreshold() const; + virtual void setStabilizationThreshold(PxReal threshold); + virtual void setWakeCounter(PxReal wakeCounterValue); + virtual PxReal getWakeCounter() const; + virtual void wakeUp(); + virtual void putToSleep(); + + virtual void setSolverIterationCounts(PxU32 positionIters, PxU32 velocityIters); + virtual void getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const; + + virtual void setContactReportThreshold(PxReal threshold); + virtual PxReal getContactReportThreshold() const; + + virtual PxRigidDynamicLockFlags getRigidDynamicLockFlags() const; + virtual void setRigidDynamicLockFlags(PxRigidDynamicLockFlags flags); + virtual void setRigidDynamicLockFlag(PxRigidDynamicLockFlag::Enum flag, bool value); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpRigidDynamic(const PxTransform& bodyPose); + + virtual void switchToNoSim(); + virtual void switchFromNoSim(); + + PX_FORCE_INLINE void wakeUpInternal(); + void wakeUpInternalNoKinematicTest(Scb::Body& body, bool forceWakeUp, bool autowake); + +private: + PX_FORCE_INLINE void setKinematicTargetInternal(const PxTransform& destination); + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene); +#endif +}; + + + + +PX_FORCE_INLINE void NpRigidDynamic::wakeUpInternal() +{ + PX_ASSERT(NpActor::getOwnerScene(*this)); + + Scb::Body& body = getScbBodyFast(); + const PxRigidBodyFlags currentFlags = body.getFlags(); + + if (!(currentFlags & PxRigidBodyFlag::eKINEMATIC)) // kinematics are only awake when a target is set, else they are asleep + wakeUpInternalNoKinematicTest(body, false, true); +} + + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpRigidStatic.cpp b/src/PhysX/physx/source/physx/src/NpRigidStatic.cpp new file mode 100644 index 000000000..34af3d241 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidStatic.cpp @@ -0,0 +1,163 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpRigidStatic.h" +#include "NpPhysics.h" +#include "ScbNpDeps.h" +#include "NpScene.h" +#include "NpRigidActorTemplateInternal.h" + +using namespace physx; + +NpRigidStatic::NpRigidStatic(const PxTransform& pose) +: NpRigidStaticT(PxConcreteType::eRIGID_STATIC, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mRigidStatic(pose) +{ +} + +NpRigidStatic::~NpRigidStatic() +{ +} + +// PX_SERIALIZATION +void NpRigidStatic::requiresObjects(PxProcessPxBaseCallback& c) +{ + NpRigidStaticT::requiresObjects(c); +} + +NpRigidStatic* NpRigidStatic::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpRigidStatic* obj = new (address) NpRigidStatic(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(NpRigidStatic); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +void NpRigidStatic::release() +{ + releaseActorT(this, mRigidStatic); +} + +void NpRigidStatic::setGlobalPose(const PxTransform& pose, bool /*wake*/) +{ + PX_CHECK_AND_RETURN(pose.isSane(), "PxRigidStatic::setGlobalPose: pose is not valid."); + + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + NpScene* npScene = NpActor::getAPIScene(*this); +#if PX_CHECKED + if(npScene) + npScene->checkPositionSanity(*this, pose, "PxRigidStatic::setGlobalPose"); +#endif + + mRigidStatic.setActor2World(pose.getNormalized()); + + if(npScene) + { + mShapeManager.markAllSceneQueryForUpdate(npScene->getSceneQueryManagerFast(), *this); + npScene->getSceneQueryManagerFast().get(Sq::PruningIndex::eSTATIC).invalidateTimestamp(); + } + +#if PX_SUPPORT_PVD + // have to do this here since this call gets not forwarded to Scb::RigidStatic + Scb::Scene* scbScene = NpActor::getScbFromPxActor(*this).getScbSceneForAPI(); + if(scbScene) + scbScene->getScenePvdClient().updatePvdProperties(&mRigidStatic); +#endif + + // invalidate the pruning structure if the actor bounds changed + if (mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidStatic::setGlobalPose: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + + updateShaderComs(); +} + +PxTransform NpRigidStatic::getGlobalPose() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mRigidStatic.getActor2World(); +} + +PxU32 physx::NpRigidStaticGetShapes(Scb::RigidStatic& rigid, void* const *&shapes) +{ + NpRigidStatic* a = static_cast(rigid.getScRigidCore().getPxActor()); + NpShapeManager& sm = a->getShapeManager(); + shapes = reinterpret_cast(sm.getShapes()); + return sm.getNbShapes(); +} + +void NpRigidStatic::switchToNoSim() +{ + getScbRigidStaticFast().switchToNoSim(false); +} + +void NpRigidStatic::switchFromNoSim() +{ + getScbRigidStaticFast().switchFromNoSim(false); +} + +#if PX_CHECKED +bool NpRigidStatic::checkConstraintValidity() const +{ + // Perhaps NpConnectorConstIterator would be worth it... + NpConnectorIterator iter = (const_cast(this))->getConnectorIterator(NpConnectorType::eConstraint); + while (PxBase* ser = iter.getNext()) + { + NpConstraint* c = static_cast(ser); + if(!c->NpConstraint::isValid()) + return false; + } + return true; +} +#endif + +#if PX_ENABLE_DEBUG_VISUALIZATION +void NpRigidStatic::visualize(Cm::RenderOutput& out, NpScene* scene) +{ + NpRigidStaticT::visualize(out, scene); + + if (getScbRigidStaticFast().getActorFlags() & PxActorFlag::eVISUALIZATION) + { + Scb::Scene& scbScene = scene->getScene(); + PxReal scale = scbScene.getVisualizationParameter(PxVisualizationParameter::eSCALE); + + //visualize actor frames + PxReal actorAxes = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eACTOR_AXES); + if (actorAxes != 0) + out << getGlobalPose() << Cm::DebugBasis(PxVec3(actorAxes)); + } +} +#endif + diff --git a/src/PhysX/physx/source/physx/src/NpRigidStatic.h b/src/PhysX/physx/source/physx/src/NpRigidStatic.h new file mode 100644 index 000000000..7da078970 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidStatic.h @@ -0,0 +1,115 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_RIGIDSTATIC +#define PX_PHYSICS_NP_RIGIDSTATIC + +#include "NpRigidActorTemplate.h" +#include "PxRigidStatic.h" +#include "ScbRigidStatic.h" + +#include "PxMetaData.h" + +namespace physx +{ + +namespace Scb +{ + class RigidObject; +} + +class NpRigidStatic; +typedef NpRigidActorTemplate NpRigidStaticT; + +class NpRigidStatic : public NpRigidStaticT +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpRigidStatic(PxBaseFlags baseFlags) : NpRigidStaticT(baseFlags), mRigidStatic(PxEmpty) {} + virtual void requiresObjects(PxProcessPxBaseCallback& c); + static NpRigidStatic* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + virtual ~NpRigidStatic(); + + //--------------------------------------------------------------------------------- + // PxActor implementation + //--------------------------------------------------------------------------------- + virtual void release(); + + virtual PxActorType::Enum getType() const { return PxActorType::eRIGID_STATIC; } + + //--------------------------------------------------------------------------------- + // PxRigidActor implementation + //--------------------------------------------------------------------------------- + + // Pose + virtual void setGlobalPose(const PxTransform& pose, bool wake); + virtual PxTransform getGlobalPose() const; + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpRigidStatic(const PxTransform& pose); + + virtual void switchToNoSim(); + virtual void switchFromNoSim(); + +#if PX_CHECKED + bool checkConstraintValidity() const; +#endif + + PX_FORCE_INLINE const Scb::Actor& getScbActorFast() const { return mRigidStatic; } + PX_FORCE_INLINE Scb::Actor& getScbActorFast() { return mRigidStatic; } + + PX_FORCE_INLINE const Scb::RigidStatic& getScbRigidStaticFast() const { return mRigidStatic; } + PX_FORCE_INLINE Scb::RigidStatic& getScbRigidStaticFast() { return mRigidStatic; } + + PX_FORCE_INLINE const PxTransform& getGlobalPoseFast() const { return mRigidStatic.getActor2World(); } + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene); +#endif + +private: + Scb::RigidStatic mRigidStatic; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpScene.cpp b/src/PhysX/physx/source/physx/src/NpScene.cpp new file mode 100644 index 000000000..d6f6d9f35 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpScene.cpp @@ -0,0 +1,3022 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxSimulationEventCallback.h" + +#include "NpScene.h" +#include "NpRigidStatic.h" +#include "NpRigidDynamic.h" +#include "NpArticulation.h" +#include "NpArticulationReducedCoordinate.h" +#include "NpArticulationLink.h" +#include "NpArticulationJoint.h" +#include "NpAggregate.h" +#include "NpBatchQuery.h" +#include "SqPruner.h" +#include "SqPruningStructure.h" +#include "SqSceneQueryManager.h" +#include "GuBVHStructure.h" + +#include "ScbNpDeps.h" +#include "ScArticulationSim.h" +#include "ScConstraintSim.h" +#include "CmCollection.h" +#include "CmUtils.h" + + +#if PX_SUPPORT_GPU_PHYSX +#include "task/PxGpuDispatcher.h" +#endif + +#include "extensions/PxJoint.h" + +#include "PxsIslandSim.h" +#include "common/PxProfileZone.h" + +using namespace physx; + +// enable thread checks in all debug builds +#if PX_DEBUG || PX_CHECKED +#define NP_ENABLE_THREAD_CHECKS 1 +#else +#define NP_ENABLE_THREAD_CHECKS 0 +#endif + +using namespace shdfnd; +using namespace Sq; + +/////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE bool removeFromSceneCheck(NpScene* npScene, PxScene* scene, const char* name) +{ + if (scene == static_cast(npScene)) + { + return true; + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "%s not assigned to scene or assigned to another scene. Call will be ignored!", name); + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + + +NpSceneQueries::NpSceneQueries(const PxSceneDesc& desc) : + mScene (desc, getContextId()), + mSQManager (mScene, desc.staticStructure, desc.dynamicStructure, desc.dynamicTreeRebuildRateHint, desc.limits), + mCachedRaycastFuncs (Gu::getRaycastFuncTable()), + mCachedSweepFuncs (Gu::getSweepFuncTable()), + mCachedOverlapFuncs (Gu::getOverlapFuncTable()), + mSceneQueriesStaticPrunerUpdate (getContextId(), 0, "NpSceneQueries.sceneQueriesStaticPrunerUpdate"), + mSceneQueriesDynamicPrunerUpdate(getContextId(), 0, "NpSceneQueries.sceneQueriesDynamicPrunerUpdate"), + mSceneQueryUpdateMode (desc.sceneQueryUpdateMode) +#if PX_SUPPORT_PVD + , mSingleSqCollector (mScene, false), + mBatchedSqCollector (mScene, true) +#endif +{ + mSceneQueriesStaticPrunerUpdate.setObject(this); + mSceneQueriesDynamicPrunerUpdate.setObject(this); +} + +NpScene::NpScene(const PxSceneDesc& desc) : + NpSceneQueries (desc), + mConstraints (PX_DEBUG_EXP("sceneConstraints")), + mRigidActors (PX_DEBUG_EXP("sceneRigidActors")), + mArticulations (PX_DEBUG_EXP("sceneArticulations")), + mAggregates (PX_DEBUG_EXP("sceneAggregates")), + mSanityBounds (desc.sanityBounds), + mNbClients (1), //we always have the default client. + mClientBehaviorFlags (PX_DEBUG_EXP("sceneBehaviorFlags")), + mSceneCompletion (getContextId(), mPhysicsDone), + mCollisionCompletion (getContextId(), mCollisionDone), + mSceneQueriesCompletion (getContextId(), mSceneQueriesDone), + mSceneExecution (getContextId(), 0, "NpScene.execution"), + mSceneCollide (getContextId(), 0, "NpScene.collide"), + mSceneAdvance (getContextId(), 0, "NpScene.solve"), + mControllingSimulation (false), + mSimThreadStackSize (0), + mConcurrentWriteCount (0), + mConcurrentReadCount (0), + mConcurrentErrorCount (0), + mCurrentWriter (0), + mSceneQueriesUpdateRunning (false), + mHasSimulatedOnce (false), + mBetweenFetchResults (false), + mBuildFrozenActors (false) +{ + mSceneExecution.setObject(this); + mSceneCollide.setObject(this); + mSceneAdvance.setObject(this); + + mTaskManager = mScene.getScScene().getTaskManagerPtr(); + mThreadReadWriteDepth = Ps::TlsAlloc(); + + updatePhysXIndicator(); + +} + +NpSceneQueries::~NpSceneQueries() +{ +} + +NpScene::~NpScene() +{ + // PT: we need to do that one first, now that we don't release the objects anymore. Otherwise we end up with a sequence like: + // - actor is part of an aggregate, and part of a scene + // - actor gets removed from the scene. This does *not* remove it from the aggregate. + // - aggregate gets removed from the scene, sees that one contained actor ain't in the scene => we get a warning message + PxU32 aggregateCount = mAggregates.size(); + while(aggregateCount--) + removeAggregate(*mAggregates.getEntries()[aggregateCount], false); + + PxU32 rigidActorCount = mRigidActors.size(); + while(rigidActorCount--) + removeActor(*mRigidActors[rigidActorCount], false); + + PxU32 articCount = mArticulations.size(); + while(articCount--) + removeArticulation(*mArticulations.getEntries()[articCount], false); + + bool unlock = mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK; + +#if PX_SUPPORT_PVD + getSingleSqCollector().release(); + getBatchedSqCollector().release(); +#endif + + // release batch queries + PxU32 numSq = mBatchQueries.size(); + while(numSq--) + PX_DELETE(mBatchQueries[numSq]); + mBatchQueries.clear(); + + mScene.release(); + + // unlock the lock taken in release(), must unlock before + // mRWLock is destroyed otherwise behavior is undefined + if (unlock) + unlockWrite(); + + TlsFree(mThreadReadWriteDepth); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::release() +{ + // need to acquire lock for release, note this is unlocked in the destructor + if (mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) + lockWrite(__FILE__, __LINE__); + + // It will be hard to do a write check here since all object release calls in the scene destructor do it and would mess + // up the test. If we really want it on scene destruction as well, we need to either have internal and external release + // calls or come up with a different approach (for example using thread ID as detector variable). + + if(getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::release(): Scene is still being simulated! PxScene::fetchResults() is called implicitly."); + + if(getSimulationStage() == Sc::SimulationStage::eCOLLIDE) + { + fetchCollision(true); + } + + if(getSimulationStage() == Sc::SimulationStage::eFETCHCOLLIDE) // need to call getSimulationStage() again beacause fetchCollision() might change the value. + { + // this is for split sim + advance(NULL); + } + + fetchResults(true, NULL); + } + NpPhysics::getInstance().releaseSceneInternal(*this); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NpScene::loadFromDesc(const PxSceneDesc& desc) +{ + { + if(desc.limits.maxNbActors) + mRigidActors.reserve(desc.limits.maxNbActors); + + //const PxU32 totalNbShapes = desc.limits.maxNbStaticShapes + desc.limits.maxNbDynamicShapes; + mScene.getScScene().preAllocate(desc.limits.maxNbActors, desc.limits.maxNbBodies, desc.limits.maxNbStaticShapes, desc.limits.maxNbDynamicShapes); + } + + userData = desc.userData; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setGravity(const PxVec3& g) +{ + NP_WRITE_CHECK(this); + mScene.setGravity(g); +} + +PxVec3 NpScene::getGravity() const +{ + NP_READ_CHECK(this); + return mScene.getGravity(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setBounceThresholdVelocity(const PxReal t) +{ + NP_WRITE_CHECK(this); + mScene.setBounceThresholdVelocity(t); +} + +PxReal NpScene::getBounceThresholdVelocity() const +{ + NP_READ_CHECK(this) + return mScene.getBounceThresholdVelocity(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setLimits(const PxSceneLimits& limits) +{ + NP_WRITE_CHECK(this); + + if(limits.maxNbActors) + mRigidActors.reserve(limits.maxNbActors); + + mScene.getScScene().preAllocate(limits.maxNbActors, limits.maxNbBodies, limits.maxNbStaticShapes, limits.maxNbDynamicShapes); + mScene.setLimits(limits); + + mSQManager.preallocate(limits.maxNbStaticShapes, limits.maxNbDynamicShapes); +} + +////////////////////////////////////////////////////////////////////////// + +PxSceneLimits NpScene::getLimits() const +{ + NP_READ_CHECK(this); + + return mScene.getLimits(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setFlag(PxSceneFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(this); + + // this call supports mutable flags only + PX_CHECK_AND_RETURN(PxSceneFlags(flag) & PxSceneFlags(PxSceneFlag::eMUTABLE_FLAGS), + "PxScene::setFlag: This flag is not mutable - you can only set it once in PxSceneDesc at startup!"); + + PxSceneFlags currentFlags = mScene.getFlags(); + + if(value) + currentFlags |= flag; + else + currentFlags &= ~PxSceneFlags(flag); + + mScene.setFlags(currentFlags); +} + +PxSceneFlags NpScene::getFlags() const +{ + NP_READ_CHECK(this); + return mScene.getFlags(); +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: make sure we always add to array and set the array index properly / at the same time +template +static PX_FORCE_INLINE void addRigidActorToArray(T& a, Ps::Array& rigidActors) +{ + a.setRigidActorArrayIndex(rigidActors.size()); + rigidActors.pushBack(&a); +} + +void NpScene::addActor(PxActor& actor, const PxBVHStructure* bvhStructure) +{ + PX_PROFILE_ZONE("API.addActor", getContextId()); + NP_WRITE_CHECK(this); + PX_SIMD_GUARD; + + PxRigidStatic* a = actor.is(); + if(a) + { +#if PX_CHECKED + if(!static_cast(a)->checkConstraintValidity()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): actor has invalid constraint and may not be added to scene"); + return; + } +#endif + if(static_cast(a)->getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); + return; + } + } + + PxRigidDynamic* aD = actor.is(); + if(aD && static_cast(aD)->getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); + return; + } + + const Scb::ControlState::Enum cs = NpActor::getScbFromPxActor(actor).getControlState(); + if((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING) && (NpActor::getOwnerScene(actor) == this))) + addActorInternal(actor, bvhStructure); + else + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): Actor already assigned to a scene. Call will be ignored!"); +} + +void NpScene::addActorInternal(PxActor& actor, const PxBVHStructure* bvhStructure) +{ + // BvhStructure check + if(bvhStructure) + { + const PxRigidActor* rigidActor = actor.is(); + if(!rigidActor || bvhStructure->getNbBounds() == 0 || bvhStructure->getNbBounds() > rigidActor->getNbShapes()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxRigidActor::setBVHStructure structure is empty or does not match shapes in the actor."); + return; + } + } + + switch(actor.getConcreteType()) + { + case PxConcreteType::eRIGID_STATIC: + { + NpRigidStatic& npStatic = static_cast(actor); +#if PX_CHECKED + checkPositionSanity(npStatic, npStatic.getGlobalPose(), "PxScene::addActor or PxScene::addAggregate"); +#endif + addRigidStatic(npStatic, static_cast(bvhStructure)); + } + break; + + case PxConcreteType::eRIGID_DYNAMIC: + { + NpRigidDynamic& npDynamic = static_cast(actor); +#if PX_CHECKED + checkPositionSanity(npDynamic, npDynamic.getGlobalPose(), "PxScene::addActor or PxScene::addAggregate"); +#endif + addRigidDynamic(npDynamic, static_cast(bvhStructure)); + } + break; + + case PxConcreteType::eARTICULATION_LINK: + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addActor(): Individual articulation links can not be added to the scene"); + } + break; + + default: + PX_ASSERT(0); + } +} + +void NpScene::updateScbStateAndSetupSq(const PxRigidActor& rigidActor, Scb::Actor& scbActor, NpShapeManager& shapeManager, bool actorDynamic, const PxBounds3* bounds, bool hasPrunerStructure) +{ + // all the things Scb does in non-buffered insertion + SceneQueryManager& sqManager = getSceneQueryManagerFast(); + + scbActor.setScbScene(&mScene); + scbActor.setControlState(Scb::ControlState::eIN_SCENE); + NpShape*const * shapes = shapeManager.getShapes(); + PxU32 nbShapes = shapeManager.getNbShapes(); + + for(PxU32 i=0;i(body), shapeManager, actorDynamic, bounds, hasPrunerStructure); +} + +void NpScene::addActors(PxActor*const* actors, PxU32 nbActors) +{ + addActorsInternal(actors, nbActors, NULL); +} + +void NpScene::addActors(const PxPruningStructure& ps) +{ + const Sq::PruningStructure& prunerStructure = static_cast(ps); + if(!prunerStructure.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxScene::addActors(): Provided pruning structure is not valid."); + return; + } + addActorsInternal(prunerStructure.getActors(), prunerStructure.getNbActors(), &prunerStructure); +} + +void NpScene::addActorsInternal(PxActor*const* PX_RESTRICT actors, PxU32 nbActors, const Sq::PruningStructure* pS) +{ + PX_PROFILE_ZONE("API.addActors", getContextId()); + NP_WRITE_CHECK(this); + PX_SIMD_GUARD; + + if(getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxScene::addActors() not allowed while simulation is running."); + return; + } + + const bool hasPrunerStructure = pS ? true : false; + Sc::Scene& scScene = mScene.getScScene(); + PxU32 actorsDone; + + Sc::BatchInsertionState scState; + scScene.startBatchInsertion(scState); + + scState.staticActorOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getScbRigidStaticFast().getScStatic()))); + scState.staticShapeTableOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getShapeManager().getShapeTable()))); + scState.dynamicActorOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getScbBodyFast().getScBody()))); + scState.dynamicShapeTableOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getShapeManager().getShapeTable()))); + scState.shapeOffset = ptrdiff_t(NpShapeGetScPtrOffset()); + + Ps::InlineArray shapeBounds; + for(actorsDone=0; actorsDonegetConcreteType(); + if(type == PxConcreteType::eRIGID_STATIC) + { + NpRigidStatic& a = *static_cast(actors[actorsDone]); +#if PX_CHECKED + if(!a.checkConstraintValidity()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActors(): actor has invalid constraint and may not be added to scene"); + break; + } + checkPositionSanity(a, a.getGlobalPose(), "PxScene::addActors"); +#endif + if(!hasPrunerStructure && a.getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActors(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); + break; + } + + if(!(a.getScbRigidStaticFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + { + shapeBounds.resizeUninitialized(a.NpRigidStatic::getNbShapes()+1); // PT: +1 for safe reads in addPrunerData/inflateBounds + scScene.addStatic(&a, scState, shapeBounds.begin()); + updateScbStateAndSetupSq(a, a.getScbActorFast(), a.getShapeManager(), false, shapeBounds.begin(), hasPrunerStructure); + addRigidActorToArray(a, mRigidActors); + a.addConstraintsToScene(); + } + else + addRigidStatic(a, NULL, hasPrunerStructure); + } + else if(type == PxConcreteType::eRIGID_DYNAMIC) + { + NpRigidDynamic& a = *static_cast(actors[actorsDone]); +#if PX_CHECKED + checkPositionSanity(a, a.getGlobalPose(), "PxScene::addActors"); +#endif + if(!hasPrunerStructure && a.getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActors(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); + break; + } + + if(!(a.getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + { + shapeBounds.resizeUninitialized(a.NpRigidDynamic::getNbShapes()+1); // PT: +1 for safe reads in addPrunerData/inflateBounds + scScene.addBody(&a, scState, shapeBounds.begin(), false); + updateScbStateAndSetupSq(a, a.getScbBodyFast(), a.getShapeManager(), true, shapeBounds.begin(), hasPrunerStructure); + addRigidActorToArray(a, mRigidActors); + a.addConstraintsToScene(); + } + else + addRigidDynamic(a, NULL, hasPrunerStructure); + } + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addRigidActors(): articulation link not permitted"); + break; + } + } + // merge sq PrunerStructure + if(pS) + { + mSQManager.addPruningStructure(*pS); + } + scScene.finishBatchInsertion(scState); + + // if we failed, still complete everything for the successful inserted actors before backing out +#if PX_SUPPORT_PVD + for(PxU32 i=0;igetConcreteType()==PxConcreteType::eRIGID_STATIC) && (!(static_cast(actors[i])->getScbRigidStaticFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) + mScene.getScenePvdClient().addStaticAndShapesToPvd(static_cast(actors[i])->getScbRigidStaticFast()); + else if ((actors[i]->getConcreteType() == PxConcreteType::eRIGID_DYNAMIC) && (!(static_cast(actors[i])->getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) + mScene.getScenePvdClient().addBodyAndShapesToPvd(static_cast(actors[i])->getScbBodyFast()); + } +#endif + + if(actorsDonegetConcreteType(); + if (!removeFromSceneCheck(this, actors[actorsDone]->getScene(), "PxScene::removeActors(): Actor")) + { + break; + } + + removeState.bufferedShapes.clear(); + removeState.removedShapes.clear(); + + if(type == PxConcreteType::eRIGID_STATIC) + { + NpRigidStatic& actor = *static_cast(actors[actorsDone]); + const PxActorFlags actorFlags = actor.getScbRigidStaticFast().getActorFlags(); + + if(actor.getShapeManager().getNbShapes()) + Ps::prefetch(actor.getShapeManager().getShapes()[0],sizeof(NpShape)); + scScene.prefetchForRemove(actor.getScbRigidStaticFast().getScStatic()); + Ps::prefetch(mRigidActors[mRigidActors.size()-1],sizeof(NpRigidDynamic)); + + const bool noSimBuffered = actorFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + if (!noSimBuffered) + actor.removeConstraintsFromScene(); + + actor.getShapeManager().teardownAllSceneQuery(getSceneQueryManagerFast(), actor); + + Scb::RigidStatic& rs = actor.getScbRigidStaticFast(); + mScene.removeActor(rs, wakeOnLostTouch, rs.isSimDisabledInternally()); + removeFromRigidActorList(actor.getRigidActorArrayIndex()); + } + else if(type == PxConcreteType::eRIGID_DYNAMIC) + { + NpRigidDynamic& actor = *static_cast(actors[actorsDone]); + const PxActorFlags actorFlags = actor.getScbBodyFast().getActorFlags(); + + if(actor.getShapeManager().getNbShapes()) + Ps::prefetch(actor.getShapeManager().getShapes()[0],sizeof(NpShape)); + scScene.prefetchForRemove(actor.getScbBodyFast().getScBody()); + Ps::prefetch(mRigidActors[mRigidActors.size()-1],sizeof(NpRigidDynamic)); + + const bool noSimBuffered = actorFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + if (!noSimBuffered) + actor.removeConstraintsFromScene(); + + actor.getShapeManager().teardownAllSceneQuery(getSceneQueryManagerFast(), actor); + + Scb::Body& b = actor.getScbBodyFast(); + mScene.removeActor(b, wakeOnLostTouch, b.isSimDisabledInternally()); + removeFromRigidActorList(actor.getRigidActorArrayIndex()); + } + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::removeActor(): Individual articulation links can not be removed from the scene"); + break; + } + } + + scScene.setBatchRemove(NULL); +} + +void NpScene::removeActor(PxActor& actor, bool wakeOnLostTouch) +{ + PX_PROFILE_ZONE("API.removeActor", getContextId()); + NP_WRITE_CHECK(this); + if (removeFromSceneCheck(this, actor.getScene(), "PxScene::removeActor(): Actor")) + { + removeActorInternal(actor, wakeOnLostTouch, true); + } +} + +void NpScene::removeActorInternal(PxActor& actor, bool wakeOnLostTouch, bool removeFromAggregate) +{ + switch(actor.getType()) + { + case PxActorType::eRIGID_STATIC: + { + NpRigidStatic& npStatic = static_cast(actor); + removeRigidStatic(npStatic, wakeOnLostTouch, removeFromAggregate); + } + break; + + case PxActorType::eRIGID_DYNAMIC: + { + NpRigidDynamic& npDynamic = static_cast(actor); + removeRigidDynamic(npDynamic, wakeOnLostTouch, removeFromAggregate); + } + break; + + case PxActorType::eARTICULATION_LINK: + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::removeActor(): Individual articulation links can not be removed from the scene"); + } + break; + + case PxActorType::eACTOR_COUNT: + case PxActorType::eACTOR_FORCE_DWORD: + PX_ASSERT(0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: TODO: inline this one in the header for consistency +void NpScene::removeFromRigidActorList(const PxU32& index) +{ + PX_ASSERT(index != 0xFFFFFFFF); + PX_ASSERT(index < mRigidActors.size()); + + { + const PxU32 size = mRigidActors.size() - 1; + mRigidActors.replaceWithLast(index); + if(size && size != index) + { + PxRigidActor& rigidActor = *mRigidActors[index]; + switch(rigidActor.getType()) + { + case PxActorType::eRIGID_STATIC: + { + NpRigidStatic& npStatic = static_cast(rigidActor); + npStatic.setRigidActorArrayIndex(index); + } + break; + case PxActorType::eRIGID_DYNAMIC: + { + NpRigidDynamic& npDynamic = static_cast(rigidActor); + npDynamic.setRigidActorArrayIndex(index); + } + break; + case PxActorType::eARTICULATION_LINK: + case PxActorType::eACTOR_COUNT: + case PxActorType::eACTOR_FORCE_DWORD: + PX_ASSERT(0); + break; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +template +static PX_FORCE_INLINE void addActorT(T& actor, T2& scbActor, Ps::Array& actors, NpScene* scene, const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure) +{ + const bool noSimBuffered = scbActor.getActorFlags().isSet(PxActorFlag::eDISABLE_SIMULATION); + + PxBounds3 bounds[8+1]; // PT: +1 for safe reads in addPrunerData/inflateBounds + const bool canReuseBounds = !noSimBuffered && !scene->getScene().isPhysicsBuffering() && actor.getShapeManager().getNbShapes()<=8; + PxBounds3* uninflatedBounds = canReuseBounds ? bounds : NULL; + + scene->getScene().addActor(scbActor, noSimBuffered, uninflatedBounds, bvhStructure); + + actor.getShapeManager().setupAllSceneQuery(scene, actor, hasPrunerStructure, uninflatedBounds, bvhStructure); + if(!noSimBuffered) + actor.addConstraintsToScene(); + addRigidActorToArray(actor, actors); +} + +void NpScene::addRigidStatic(NpRigidStatic& actor, const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure) +{ + addActorT(actor, actor.getScbRigidStaticFast(), mRigidActors, this, bvhStructure, hasPrunerStructure); +} + +void NpScene::addRigidDynamic(NpRigidDynamic& body, const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure) +{ + addActorT(body, body.getScbBodyFast(), mRigidActors, this, bvhStructure, hasPrunerStructure); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +static PX_FORCE_INLINE void removeActorT(T& actor, T2& scbActor, NpScene* scene, bool wakeOnLostTouch, bool removeFromAggregate) +{ + PX_ASSERT(NpActor::getAPIScene(actor) == scene); + const bool noSimBuffered = scbActor.getActorFlags().isSet(PxActorFlag::eDISABLE_SIMULATION); + + if(removeFromAggregate) + { + PxU32 index = 0xffffffff; + NpAggregate* aggregate = actor.getNpAggregate(index); + if(aggregate) + { + aggregate->removeActorAndReinsert(actor, false); + PX_ASSERT(!actor.getAggregate()); + } + } + + actor.getShapeManager().teardownAllSceneQuery(scene->getSceneQueryManagerFast(), actor); + if(!noSimBuffered) + actor.removeConstraintsFromScene(); + + scene->getScene().removeActor(scbActor, wakeOnLostTouch, scbActor.isSimDisabledInternally()); + scene->removeFromRigidActorList(actor.getRigidActorArrayIndex()); +} + +void NpScene::removeRigidStatic(NpRigidStatic& actor, bool wakeOnLostTouch, bool removeFromAggregate) +{ + removeActorT(actor, actor.getScbRigidStaticFast(), this, wakeOnLostTouch, removeFromAggregate); +} + +void NpScene::removeRigidDynamic(NpRigidDynamic& body, bool wakeOnLostTouch, bool removeFromAggregate) +{ + removeActorT(body, body.getScbBodyFast(), this, wakeOnLostTouch, removeFromAggregate); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::addArticulation(PxArticulationBase& articulation) +{ + PX_PROFILE_ZONE("API.addArticulation", getContextId()); + NP_WRITE_CHECK(this); + + PX_CHECK_AND_RETURN(articulation.getNbLinks()>0, "PxScene::addArticulation: empty articulations may not be added to simulation."); + PX_SIMD_GUARD; + + if (this->getFlags() & PxSceneFlag::eENABLE_GPU_DYNAMICS) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): Articulations are not currently supported when PxSceneFlag::eENABLE_GPU_DYNAMICS is set!"); + return; + } + + if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE && articulation.getType() == PxArticulationBase::eReducedCoordinate) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): this call is not allowed while the simulation is running. Call will be ignored!"); + return; + } + + PxArticulationImpl& npa = *reinterpret_cast(articulation.getImpl()); + + Scb::Articulation& art = npa.getArticulation(); + const Scb::ControlState::Enum cs = art.getControlState(); + if ((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING) && (art.getScbScene()->getPxScene() == this))) + addArticulationInternal(articulation); + else + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation already assigned to a scene. Call will be ignored!"); +} + +void NpScene::addArticulationInternal(PxArticulationBase& npa) +{ + + // Add root link first + PxU32 nbLinks = npa.getNbLinks(); + PX_ASSERT(nbLinks > 0); + PxArticulationImpl* impl = reinterpret_cast(npa.getImpl()); + NpArticulationLink* rootLink = static_cast(impl->getRoot()); + +#if PX_CHECKED + checkPositionSanity(*rootLink, rootLink->getGlobalPose(), "PxScene::addArticulation or PxScene::addAggregate"); +#endif + if(rootLink->getMass()==0) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero mass added to scene; defaulting mass to 1"); + rootLink->setMass(1.0f); + } + + PxVec3 inertia0 = rootLink->getMassSpaceInertiaTensor(); + if(inertia0.x == 0.0f || inertia0.y == 0.0f || inertia0.z == 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero moment of inertia added to scene; defaulting inertia to (1,1,1)"); + rootLink->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); + } + + bool linkTriggersWakeUp = !rootLink->getScbBodyFast().checkSleepReadinessBesidesWakeCounter(); + + addArticulationLinkBody(*rootLink); + + // Add articulation + PxArticulationImpl* npaImpl = reinterpret_cast(npa.getImpl()); + Scb::Articulation& scbArt = npaImpl->getArticulation(); + scbArt.setArticulationType(npa.getType()); + mScene.addArticulation(scbArt); + + Sc::ArticulationCore& scArtCore = scbArt.getScArticulation(); + Sc::ArticulationSim* scArtSim = scArtCore.getSim(); + + if (scArtSim) + { + PxU32 handle = scArtSim->findBodyIndex(*rootLink->getScbBodyFast().getScBody().getSim()); + rootLink->setLLIndex(handle); + } + rootLink->setInboundJointDof(0); + + addArticulationLinkConstraint(*rootLink); + + // Add links & joints + PX_ALLOCA(linkStack, NpArticulationLink*, nbLinks); + linkStack[0] = rootLink; + PxU32 curLink = 0; + PxU32 stackSize = 1; + while(curLink < (nbLinks-1)) + { + PX_ASSERT(curLink < stackSize); + NpArticulationLink* l = linkStack[curLink]; + NpArticulationLink*const* children = l->getChildren(); + + for(PxU32 i=0; i < l->getNbChildren(); i++) + { + NpArticulationLink* child = children[i]; + +#if PX_CHECKED + checkPositionSanity(*child, child->getGlobalPose(), "PxScene::addArticulation"); +#endif + if(child->getMass()==0) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero mass added to scene; defaulting mass to 1"); + child->setMass(1.0f); + } + + PxVec3 inertia = child->getMassSpaceInertiaTensor(); + if(inertia.x == 0.0f || inertia.y == 0.0f || inertia.z == 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero moment of inertia added to scene; defaulting inertia to (1,1,1)"); + child->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); + } + + linkTriggersWakeUp = linkTriggersWakeUp || (!child->getScbBodyFast().checkSleepReadinessBesidesWakeCounter()); + + addArticulationLink(*child); // Adds joint too + if (scArtSim) + { + PxU32 cHandle = scArtSim->findBodyIndex(*child->getScbBodyFast().getScBody().getSim()); + child->setLLIndex(cHandle); + } + //child->setInboundJointDof(scArtSim->getDof(cHandle)); + + linkStack[stackSize] = child; + stackSize++; + } + + curLink++; + } + + if ((scbArt.getWakeCounter() == 0.0f) && linkTriggersWakeUp) + { + // this is for the buffered insert case, where the articulation needs to wake up, if one of the links triggers activation. + npaImpl->wakeUpInternal(true, false); + } + + mArticulations.insert(&npa); + + if (scArtSim) + { + scArtSim->checkResize(); + + linkStack[0] = rootLink; + curLink = 0; + stackSize = 1; + + while (curLink < (nbLinks - 1)) + { + PX_ASSERT(curLink < stackSize); + NpArticulationLink* l = linkStack[curLink]; + NpArticulationLink*const* children = l->getChildren(); + + for (PxU32 i = 0; i < l->getNbChildren(); i++) + { + NpArticulationLink* child = children[i]; + child->setInboundJointDof(scArtSim->getDof(child->getLinkIndex())); + + linkStack[stackSize] = child; + stackSize++; + } + + curLink++; + } + } + + //add loop joints + if (npa.getType() == PxArticulationBase::eReducedCoordinate) + { + //This method will prepare link data for the gpu + mScene.getScScene().addArticulationSimControl(scbArt.getScArticulation()); + + NpArticulationReducedCoordinate* npaRC = static_cast(&npa); + + for (PxU32 i = 0; i < npaRC->mLoopJoints.size(); ++i) + { + PxJoint* joint = npaRC->mLoopJoints[i]; + NpConstraint* constraint = static_cast(joint->getConstraint()); + Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); + scArtSim->addLoopConstraint(cSim); + } + } +} + +void NpScene::removeArticulation(PxArticulationBase& articulation, bool wakeOnLostTouch) +{ + PX_PROFILE_ZONE("API.removeArticulation", getContextId()); + NP_WRITE_CHECK(this); + + if (removeFromSceneCheck(this, articulation.getScene(), "PxScene::removeArticulation(): Articulation")) + { + NpArticulation& npa = static_cast(articulation); + removeArticulationInternal(npa, wakeOnLostTouch, true); + } +} + +void NpScene::removeArticulationInternal(PxArticulationBase& npa, bool wakeOnLostTouch, bool removeFromAggregate) +{ + PxU32 nbLinks = npa.getNbLinks(); + PX_ASSERT(nbLinks > 0); + + if(removeFromAggregate && npa.getAggregate()) + { + static_cast(npa.getAggregate())->removeArticulationAndReinsert(npa, false); + PX_ASSERT(!npa.getAggregate()); + } + + //!!!AL + // Inefficient. We might want to introduce a LL method to kill the whole LL articulation together with all joints in one go, then + // the order of removing the links/joints does not matter anymore. + + // Remove links & joints + PX_ALLOCA(linkStack, NpArticulationLink*, nbLinks); + linkStack[0] = reinterpret_cast(npa.getImpl())->getLinks()[0]; + PxU32 curLink = 0, stackSize = 1; + + while(curLink < (nbLinks-1)) + { + PX_ASSERT(curLink < stackSize); + NpArticulationLink* l = linkStack[curLink]; + NpArticulationLink*const* children = l->getChildren(); + + for(PxU32 i=0; i < l->getNbChildren(); i++) + { + linkStack[stackSize] = children[i]; + stackSize++; + } + + curLink++; + } + + PxRigidBodyFlags flag; + for(PxI32 j=PxI32(nbLinks); j-- > 0; ) + { + flag |=linkStack[j]->getScbBodyFast().getScBody().getCore().mFlags; + removeArticulationLink(*linkStack[j], wakeOnLostTouch); + } + + if (flag & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + { + PxArticulationImpl* impl = reinterpret_cast(npa.getImpl()); + IG::NodeIndex index = impl->getScbArticulation().getScArticulation().getIslandNodeIndex(); + if (index.isValid()) + mScene.getScScene().resetSpeculativeCCDArticulationLink(index.index()); + } + // Remove articulation + mScene.removeArticulation(reinterpret_cast(npa.getImpl())->getArticulation()); + + + removeFromArticulationList(npa); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::addArticulationLinkBody(NpArticulationLink& link) +{ + mScene.addActor(link.getScbBodyFast(), false, NULL, NULL); + link.getShapeManager().setupAllSceneQuery(this, link, false); +} + +void NpScene::addArticulationLinkConstraint(NpArticulationLink& link) +{ + PxArticulationJointBase* j = link.getInboundJoint(); + if (j) + { + PxArticulationJointImpl* impl = j->getImpl(); + mScene.addArticulationJoint(impl->getScbArticulationJoint()); + } + + link.addConstraintsToScene(); +} + +void NpScene::addArticulationLink(NpArticulationLink& link) +{ + addArticulationLinkBody(link); + addArticulationLinkConstraint(link); +} + +void NpScene::removeArticulationLink(NpArticulationLink& link, bool wakeOnLostTouch) +{ + PxArticulationJointBase* j =link.getInboundJoint(); + + link.removeConstraintsFromScene(); + link.getShapeManager().teardownAllSceneQuery(getSceneQueryManagerFast(), link); + + if (j) + mScene.removeArticulationJoint(j->getImpl()->getScbArticulationJoint()); + + mScene.removeActor(link.getScbBodyFast(), wakeOnLostTouch, false); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::addAggregate(PxAggregate& aggregate) +{ + PX_PROFILE_ZONE("API.addAggregate", getContextId()); + NP_WRITE_CHECK(this); + PX_SIMD_GUARD; + + NpAggregate& np = static_cast(aggregate); + + const PxU32 nb = np.getCurrentSizeFast(); +#if PX_CHECKED + for(PxU32 i=0;iis(); + if(a && !static_cast(a)->checkConstraintValidity()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addAggregate(): Aggregate contains an actor with an invalid constraint!"); + return; + } + } +#endif + + Scb::Aggregate& agg = np.getScbAggregate(); + const Scb::ControlState::Enum cs = agg.getControlState(); + if ((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING) && (agg.getScbScene()->getPxScene() == this))) + { + mScene.addAggregate(agg); + + for(PxU32 i=0;i(NpConnectorType::eBvhStructure, &bvhStructure, 1)) + { + npActor.removeConnector(actor, NpConnectorType::eBvhStructure, bvhStructure, "PxBVHStructure connector could not have been removed!"); + } + + np.addActorInternal(actor, *this, bvhStructure); + + // if a bvh structure was used dec ref count, we increased the ref count when adding the actor connection + if(bvhStructure) + { + bvhStructure->decRefCount(); + } + } + + mAggregates.insert(&aggregate); + } + else + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addAggregate(): Aggregate already assigned to a scene. Call will be ignored!"); +} + +void NpScene::removeAggregate(PxAggregate& aggregate, bool wakeOnLostTouch) +{ + PX_PROFILE_ZONE("API.removeAggregate", getContextId()); + NP_WRITE_CHECK(this); + if(!removeFromSceneCheck(this, aggregate.getScene(), "PxScene::removeAggregate(): Aggregate")) + return; + + NpAggregate& np = static_cast(aggregate); + if(np.getScene()!=this) + return; + + const PxU32 nb = np.getCurrentSizeFast(); + for(PxU32 j=0;jgetType() != PxActorType::eARTICULATION_LINK) + { + Scb::Actor& scb = NpActor::getScbFromPxActor(*a); + + np.getScbAggregate().removeActor(scb, false); // This is only here to make sure the aggregateID gets set to invalid on sync + + removeActorInternal(*a, wakeOnLostTouch, false); + } + else if (a->getScene()) + { + NpArticulationLink& al = static_cast(*a); + PxArticulationBase& npArt = al.getRoot(); + PxArticulationImpl* impl = reinterpret_cast(npArt.getImpl()); + NpArticulationLink* const* links = impl->getLinks(); + for(PxU32 i=0; i < npArt.getNbLinks(); i++) + { + np.getScbAggregate().removeActor(links[i]->getScbActorFast(), false); // This is only here to make sure the aggregateID gets set to invalid on sync + } + + removeArticulationInternal(npArt, wakeOnLostTouch, false); + } + } + + mScene.removeAggregate(np.getScbAggregate()); + + removeFromAggregateList(aggregate); +} + +PxU32 NpScene::getNbAggregates() const +{ + NP_READ_CHECK(this); + return mAggregates.size(); +} + +PxU32 NpScene::getAggregates(PxAggregate** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(this); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mAggregates.getEntries(), mAggregates.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::addCollection(const PxCollection& collection) +{ + PX_PROFILE_ZONE("API.addCollection", getContextId()); + const Cm::Collection& col = static_cast(collection); + + PxU32 nb = col.internalGetNbObjects(); +#if PX_CHECKED + for(PxU32 i=0;iis(); + if(a && !static_cast(a)->checkConstraintValidity()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addCollection(): collection contains an actor with an invalid constraint!"); + return; + } + } +#endif + + Ps::Array actorsToInsert; + actorsToInsert.reserve(nb); + + struct Local + { + static void addActorIfNeeded(PxActor* actor, Ps::Array& actorArray) + { + if(actor->getAggregate()) + return; // The actor will be added when the aggregate is added + actorArray.pushBack(actor); + } + }; + + for(PxU32 i=0;igetConcreteType(); + + //NpArticulationLink, NpArticulationJoint are added with the NpArticulation + //Actors and Articulations that are members of an Aggregate are added with the NpAggregate + + if(serialType==PxConcreteType::eRIGID_DYNAMIC) + { + NpRigidDynamic* np = static_cast(s); + // if pruner structure exists for the actor, actor will be added with the pruner structure + if(!np->getShapeManager().getPruningStructure()) + Local::addActorIfNeeded(np, actorsToInsert); + } + else if(serialType==PxConcreteType::eRIGID_STATIC) + { + NpRigidStatic* np = static_cast(s); + // if pruner structure exists for the actor, actor will be added with the pruner structure + if(!np->getShapeManager().getPruningStructure()) + Local::addActorIfNeeded(np, actorsToInsert); + } + else if(serialType==PxConcreteType::eSHAPE) + { + } + else if(serialType==PxConcreteType::eARTICULATION) + { + NpArticulation* np = static_cast(s); + if (!np->getAggregate()) // The actor will be added when the aggregate is added + { + if (np->mType == PxArticulationBase::eMaximumCoordinate) + addArticulation(static_cast(*np)); + /*else + addArticulation(static_cast(*np));*/ + } + } + else if(serialType==PxConcreteType::eAGGREGATE) + { + NpAggregate* np = static_cast(s); + addAggregate(*np); + } + else if(serialType == PxConcreteType::ePRUNING_STRUCTURE) + { + PxPruningStructure* ps = static_cast(s); + addActors(*ps); + } + } + + if(!actorsToInsert.empty()) + addActorsInternal(&actorsToInsert[0], actorsToInsert.size(), NULL); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 NpScene::getNbActors(PxActorTypeFlags types) const +{ + NP_READ_CHECK(this); + PxU32 nbActors = 0; + + if (types & PxActorTypeFlag::eRIGID_STATIC) + { + for(PxU32 i=mRigidActors.size(); i--;) + { + if (mRigidActors[i]->is()) + nbActors++; + } + } + + if (types & PxActorTypeFlag::eRIGID_DYNAMIC) + { + for(PxU32 i=mRigidActors.size(); i--;) + { + if (mRigidActors[i]->is()) + nbActors++; + } + } + + return nbActors; +} + +PxU32 NpScene::getActors(PxActorTypeFlags types, PxActor** buffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(this); + + PxU32 writeCount = 0; + PxU32 virtualIndex = 0; // PT: virtual index of actor, continuous across different actor containers. + + if(types & (PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC)) + { + const PxU32 size = mRigidActors.size(); + for(PxU32 i=0; (i < size) && (writeCount < bufferSize); i++) + { + if ((types & PxActorTypeFlag::eRIGID_STATIC ) && mRigidActors[i]->is()) + { + if (virtualIndex >= startIndex) + buffer[writeCount++] = mRigidActors[i]; + virtualIndex++; + } + else if ((types & PxActorTypeFlag::eRIGID_DYNAMIC) && mRigidActors[i]->is()) + { + if (virtualIndex >= startIndex) + buffer[writeCount++] = mRigidActors[i]; + virtualIndex++; + } + } + } + + return writeCount; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxActor** NpScene::getActiveActors(PxU32& nbActorsOut) +{ + NP_READ_CHECK(this); + return mScene.getActiveActors(nbActorsOut); +} + +PxActor** NpScene::getFrozenActors(PxU32& nbActorsOut) +{ + NP_READ_CHECK(this); + return mScene.getFrozenActors(nbActorsOut); +} + +void NpScene::setFrozenActorFlag(const bool buildFrozenActors) +{ +#if PX_CHECKED + PxSceneFlags combinedFlag(PxSceneFlag::eENABLE_ACTIVE_ACTORS | PxSceneFlag::eENABLE_STABILIZATION); + + PX_CHECK_AND_RETURN((getFlags() & combinedFlag)== combinedFlag, + "NpScene::setFrozenActorFlag: Cannot raise BuildFrozenActors if PxSceneFlag::eENABLE_STABILIZATION and PxSceneFlag::eENABLE_ACTIVE_ACTORS is not raised!"); +#endif + mBuildFrozenActors = buildFrozenActors; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 NpScene::getNbArticulations() const +{ + NP_READ_CHECK(this); + return mArticulations.size(); +} + +PxU32 NpScene::getArticulations(PxArticulationBase** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(this); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mArticulations.getEntries(), mArticulations.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 NpScene::getNbConstraints() const +{ + NP_READ_CHECK(this); + return mConstraints.size(); +} + +PxU32 NpScene::getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(this); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mConstraints.getEntries(), mConstraints.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +const PxRenderBuffer& NpScene::getRenderBuffer() +{ + if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + // will be reading the Sc::Scene renderable which is getting written + // during the sim, hence, avoid call while simulation is running. + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxScene::getRenderBuffer() not allowed while simulation is running."); + } + + return mRenderBuffer; +} + +void NpScene::visualize() +{ + NP_READ_CHECK(this); + + PX_PROFILE_ZONE("NpScene::visualize", getContextId()); + + mRenderBuffer.clear(); // clear last frame visualizations + +#if PX_ENABLE_DEBUG_VISUALIZATION + if(getVisualizationParameter(PxVisualizationParameter::eSCALE) == 0.0f) + return; + + Cm::RenderOutput out(mRenderBuffer); + + // Visualize scene axis + const PxReal worldAxes = getVisualizationParameter(PxVisualizationParameter::eWORLD_AXES); + if (worldAxes != 0) + out << Cm::DebugBasis(PxVec3(worldAxes)); + + // Visualize articulations + for(PxU32 i=0;i(mArticulations.getEntries()[i])->visualize(out, this); + + // Visualize rigid actors and rigid bodies + PxRigidActor*const* rigidActors = mRigidActors.begin(); + const PxU32 rigidActorCount = mRigidActors.size(); + + for(PxU32 i=0; i < rigidActorCount; i++) + { + PxRigidActor* a = rigidActors[i]; + if (a->getType() == PxActorType::eRIGID_DYNAMIC) + static_cast(a)->visualize(out, this); + else + static_cast(a)->visualize(out, this); + } + + // Visualize pruning structures + const bool visStatic = getVisualizationParameter(PxVisualizationParameter::eCOLLISION_STATIC) != 0.0f; + const bool visDynamic = getVisualizationParameter(PxVisualizationParameter::eCOLLISION_DYNAMIC) != 0.0f; + //flushQueryUpdates(); // DE7834 + if(visStatic && mSQManager.get(PruningIndex::eSTATIC).pruner()) + mSQManager.get(PruningIndex::eSTATIC).pruner()->visualize(out, PxU32(PxDebugColor::eARGB_BLUE)); + if(visDynamic && mSQManager.get(PruningIndex::eDYNAMIC).pruner()) + mSQManager.get(PruningIndex::eDYNAMIC).pruner()->visualize(out, PxU32(PxDebugColor::eARGB_RED)); + + if(getVisualizationParameter(PxVisualizationParameter::eMBP_REGIONS) != 0.0f) + { + out << PxTransform(PxIdentity); + + const PxU32 nbRegions = mScene.getNbBroadPhaseRegions(); + for(PxU32 i=0;i 0) && (data != NULL)) ), "PxScene::setFilterShaderData(): data pointer must not be NULL unless the specified data size is 0 too and vice versa."); + + mScene.setFilterShaderData(data, dataSize); +} + +const void* NpScene::getFilterShaderData() const +{ + NP_READ_CHECK(this); + return mScene.getFilterShaderData(); +} + +PxU32 NpScene::getFilterShaderDataSize() const +{ + NP_READ_CHECK(this); + return mScene.getFilterShaderDataSize(); +} + +PxSimulationFilterShader NpScene::getFilterShader() const +{ + NP_READ_CHECK(this); + return mScene.getFilterShader(); +} + +PxSimulationFilterCallback* NpScene::getFilterCallback() const +{ + NP_READ_CHECK(this); + return mScene.getFilterCallback(); +} + +void NpScene::resetFiltering(PxActor& actor) +{ + NP_WRITE_CHECK(this); + + PX_CHECK_AND_RETURN(NpActor::getAPIScene(actor) && (NpActor::getAPIScene(actor) == this), "PxScene::resetFiltering(): actor not in scene!"); + + switch(actor.getConcreteType()) + { + case PxConcreteType::eRIGID_STATIC: + { + NpRigidStatic& npStatic = static_cast(actor); + npStatic.resetFiltering(npStatic.getScbRigidStaticFast(), NULL, 0); + } + break; + + case PxConcreteType::eRIGID_DYNAMIC: + { + NpRigidDynamic& npDynamic = static_cast(actor); + if (npDynamic.resetFiltering(npDynamic.getScbBodyFast(), NULL, 0)) + npDynamic.wakeUpInternal(); + } + break; + + case PxConcreteType::eARTICULATION_LINK: + { + NpArticulationLink& npLink = static_cast(actor); + if (npLink.resetFiltering(npLink.getScbBodyFast(), NULL, 0)) + { + PxArticulationImpl* impl = reinterpret_cast(npLink.getRoot().getImpl()); + impl->wakeUpInternal(false, true); + } + } + break; + + default: + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxScene::resetFiltering(): only PxRigidActor supports this operation!"); + } +} + +void NpScene::resetFiltering(PxRigidActor& actor, PxShape*const* shapes, PxU32 shapeCount) +{ + NP_WRITE_CHECK(this); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(actor) && (NpActor::getAPIScene(actor) == this), "PxScene::resetFiltering(): actor not in scene!"); + PX_SIMD_GUARD; + + switch(actor.getConcreteType()) + { + case PxConcreteType::eRIGID_STATIC: + { + NpRigidStatic& npStatic = static_cast(actor); + npStatic.resetFiltering(npStatic.getScbRigidStaticFast(), shapes, shapeCount); + } + break; + + case PxConcreteType::eRIGID_DYNAMIC: + { + NpRigidDynamic& npDynamic = static_cast(actor); + if (npDynamic.resetFiltering(npDynamic.getScbBodyFast(), shapes, shapeCount)) + npDynamic.wakeUpInternal(); + } + break; + + case PxConcreteType::eARTICULATION_LINK: + { + NpArticulationLink& npLink = static_cast(actor); + if (npLink.resetFiltering(npLink.getScbBodyFast(), shapes, shapeCount)) + { + PxArticulationImpl* impl = reinterpret_cast(npLink.getRoot().getImpl()); + impl->wakeUpInternal(false, true); + } + } + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +PxPhysics& NpScene::getPhysics() +{ + return NpPhysics::getInstance(); +} + +void NpScene::updateDirtyShaders() +{ + PX_PROFILE_ZONE("Sim.updateDirtyShaders", getContextId()); + // this should continue to be done in the Np layer even after SC has taken over + // all vital simulation functions, because it needs to complete before simulate() + // returns to the application + + // However, the implementation needs fixing so that it does work proportional to + // the number of dirty shaders + + PxConstraint*const* constraints = mConstraints.getEntries(); + for(PxU32 i=0;i(constraints[i])->updateConstants(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +void NpScene::simulateOrCollide(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation, const char* invalidCallMsg, Sc::SimulationStage::Enum simStage) +{ + PX_SIMD_GUARD; + + { + // write guard must end before simulation kicks off worker threads + // otherwise the simulation callbacks could overlap with this function + // and perform API reads,triggering an error + NP_WRITE_CHECK(this); + + PX_PROFILE_START_CROSSTHREAD("Basic.simulate", getContextId()); + + if(getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + //fetchResult doesn't get called + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, invalidCallMsg); + return; + } + + PX_CHECK_AND_RETURN(elapsedTime > 0, "PxScene::collide/simulate: The elapsed time must be positive!"); + + PX_CHECK_AND_RETURN((reinterpret_cast(scratchBlock)&15) == 0, "PxScene::simulate: scratch block must be 16-byte aligned!"); + + PX_CHECK_AND_RETURN((scratchBlockSize&16383) == 0, "PxScene::simulate: scratch block size must be a multiple of 16K"); + +#if PX_SUPPORT_PVD + //signal the frame is starting. + mScene.getScenePvdClient().frameStart(elapsedTime); +#endif + +#if PX_ENABLE_DEBUG_VISUALIZATION + visualize(); +#endif + + updateDirtyShaders(); + +#if PX_SUPPORT_PVD + mScene.getScenePvdClient().updateJoints(); +#endif + + mScene.getScScene().setScratchBlock(scratchBlock, scratchBlockSize); + + mElapsedTime = elapsedTime; + if (simStage == Sc::SimulationStage::eCOLLIDE) + mScene.getScScene().setElapsedTime(elapsedTime); + + mControllingSimulation = controlSimulation; + + //sync all the material events + NpPhysics& physics = static_cast(this->getPhysics()); + NpMaterialManager& manager = physics.getMaterialManager(); + NpMaterial** materials = manager.getMaterials(); + mScene.updateLowLevelMaterial(materials); + + setSimulationStage(simStage); + mScene.setPhysicsBuffering(true); + mHasSimulatedOnce = true; + } + + { + PX_PROFILE_ZONE("Sim.taskFrameworkSetup", getContextId()); + + if (controlSimulation) + { + { + PX_PROFILE_ZONE("Sim.resetDependencies", getContextId()); + // Only reset dependencies, etc if we own the TaskManager. Will be false + // when an NpScene is controlled by an APEX scene. + mTaskManager->resetDependencies(); + } + mTaskManager->startSimulation(); + } + + if (simStage == Sc::SimulationStage::eCOLLIDE) + { + mCollisionCompletion.setContinuation(*mTaskManager, completionTask); + mSceneCollide.setContinuation(&mCollisionCompletion); + //Initialize scene completion task + mSceneCompletion.setContinuation(*mTaskManager, NULL); + } + else + { + mSceneCompletion.setContinuation(*mTaskManager, completionTask); + mSceneExecution.setContinuation(*mTaskManager, &mSceneCompletion); + } + + if (simStage == Sc::SimulationStage::eCOLLIDE) + { + mCollisionCompletion.removeReference(); + mSceneCollide.removeReference(); + } + else + { + mSceneCompletion.removeReference(); + mSceneExecution.removeReference(); + } + } +} + +void NpScene::simulate(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation) +{ + simulateOrCollide( elapsedTime, completionTask, scratchBlock, scratchBlockSize, controlSimulation, + "PxScene::simulate: Simulation is still processing last simulate call, you should call fetchResults()!", Sc::SimulationStage::eADVANCE); +} + +void NpScene::advance( physx::PxBaseTask* completionTask) +{ + NP_WRITE_CHECK(this); + //issue error if advance() doesn't get called between fetchCollision() and fetchResult() + if(getSimulationStage() != Sc::SimulationStage::eFETCHCOLLIDE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::advance: advance() called illegally! advance() needed to be called after fetchCollision() and before fetchResult()!!"); + return; + } + + //apply buffering for forces, velocities, kinematic targets and wake-up events + mScene.syncWriteThroughProperties(); + + //if mSimulateStage == eFETCHCOLLIDE, which means collide() has been kicked off and finished running, we can run advance() safely + { + //change the mSimulateStaget to eADVANCE to indicate the next stage to run is fetchResult() + setSimulationStage(Sc::SimulationStage::eADVANCE); + + { + PX_PROFILE_ZONE("Sim.taskFrameworkSetup", getContextId()); + + mSceneCompletion.setDependent(completionTask); + mSceneAdvance.setContinuation(*mTaskManager, &mSceneCompletion); + mSceneCompletion.removeReference(); + mSceneAdvance.removeReference(); + } + } +} + +void NpScene::collide(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation) +{ + simulateOrCollide( elapsedTime, + completionTask, + scratchBlock, + scratchBlockSize, + controlSimulation, + "PxScene::collide: collide() called illegally! If it isn't the first frame, collide() needed to be called between fetchResults() and fetchCollision(). Otherwise, collide() needed to be called before fetchCollision()", + Sc::SimulationStage::eCOLLIDE); +} + +bool NpScene::checkResultsInternal(bool block) +{ + PX_PROFILE_ZONE("Basic.checkResults", getContextId()); + return mPhysicsDone.wait(block ? Ps::Sync::waitForever : 0); +} + +bool NpScene::checkCollisionInternal(bool block) +{ + PX_PROFILE_ZONE("Basic.checkCollision", getContextId()); + return mCollisionDone.wait(block ? Ps::Sync::waitForever : 0); +} + +bool NpScene::checkResults(bool block) +{ + return checkResultsInternal(block); +} + +bool NpScene::checkCollision(bool block) +{ + return checkCollisionInternal(block); +} + +void NpScene::fireOutOfBoundsCallbacks() +{ + PX_PROFILE_ZONE("Sim.fireOutOfBoundsCallbacks", getContextId()); + + // Fire broad-phase callbacks + { + Sc::Scene& scene = mScene.getScScene(); + using namespace physx::Sc; + + bool outputWarning = scene.fireOutOfBoundsCallbacks(); + + // Aggregates + { + void** outAgg = scene.getOutOfBoundsAggregates(); + const PxU32 nbOut1 = scene.getNbOutOfBoundsAggregates(); + + PxBroadPhaseCallback* cb = scene.getBroadPhaseCallback(); + + for(PxU32 i=0;i(outAgg[i]); + NpAggregate* np = static_cast(px); + if(np->getScbAggregate().getControlState()==Scb::ControlState::eREMOVE_PENDING) + continue; + + if(cb) + cb->onObjectOutOfBounds(*px); + else + outputWarning = true; + } + scene.clearOutOfBoundsAggregates(); + } + + if(outputWarning) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "At least one object is out of the broadphase bounds. To manage those objects, define a PxBroadPhaseCallback for each used client."); + } +} + +bool NpScene::fetchCollision(bool block) +{ + if(getSimulationStage() != Sc::SimulationStage::eCOLLIDE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::fetchCollision: fetchCollision() should be called after collide() and before advance()!"); + return false; + } + + //if collision isn't finish running (and block is false), then return false + if(!checkCollisionInternal(block)) + return false; + + // take write check *after* collision() finished, otherwise + // we will block fetchCollision() from using the API + NP_WRITE_CHECK_NOREENTRY(this); + + setSimulationStage(Sc::SimulationStage::eFETCHCOLLIDE); + + return true; +} + +class SqRefFinder: public Sc::SqRefFinder +{ +public: + virtual Sq::PrunerHandle find(const PxRigidBody* body, const PxShape* shape) + { + const Sq::PrunerData prunerdata = NpActor::getShapeManager(*body)->findSceneQueryData(*static_cast(shape)); + return Sq::getPrunerHandle(prunerdata); + } +private: +}; + +// The order of the following operations is important! +// 1. Process object deletions which were carried out while the simulation was running (since these effect contact and trigger reports) +// 2. Write contact reports to global stream (taking pending deletions into account), clear some simulation buffers (deleted objects etc.), ... +// 3. Send reports which have to be done before the data is synced (contact & trigger reports etc.) such that the user gets the old state. +// 4. Mark the simulation as not running internally to allow reading data which should not be read otherwise +// 5. Synchronize the simulation and user state +// 6. Fire callbacks which need to reflect the synchronized object state + +void NpScene::fetchResultsPreContactCallbacks() +{ +#if PX_SUPPORT_PVD + mScene.getScenePvdClient().updateContacts(); +#endif + + mScene.prepareOutOfBoundsCallbacks(); + mScene.processPendingRemove(); + mScene.endSimulation(); + + { + PX_PROFILE_ZONE("Sim.fireCallbacksPreSync", getContextId()); + fireOutOfBoundsCallbacks(); // fire out-of-bounds callbacks + mScene.fireBrokenConstraintCallbacks(); + mScene.fireTriggerCallbacks(); + } +} + +void NpScene::fetchResultsPostContactCallbacks() +{ + mScene.postCallbacksPreSync(); + mScene.syncEntireScene(); // double buffering + + SqRefFinder sqRefFinder; + mScene.getScScene().syncSceneQueryBounds(mSQManager.getDynamicBoundsSync(), sqRefFinder); + + mSQManager.updateCompoundActors(mScene.getScScene().getActiveCompoundBodiesArray(), mScene.getScScene().getNumActiveCompoundBodies()); + mSQManager.afterSync(getSceneQueryUpdateModeFast()); + +#if PX_SUPPORT_PVD + mScene.getScenePvdClient().updateSceneQueries(); + + getSingleSqCollector().clear(); + getBatchedSqCollector().clear(); +#endif + + // fire sleep and wake-up events + // we do this after buffer-swapping so that the events have the new state + { + PX_PROFILE_ZONE("Sim.fireCallbacksPostSync", getContextId()); + mScene.fireCallBacksPostSync(); + } + + mScene.postReportsCleanup(); + + // build the list of active actors + { + PX_PROFILE_ZONE("Sim.buildActiveActors", getContextId()); + + const bool buildActiveActors = mScene.getFlags() & PxSceneFlag::eENABLE_ACTIVE_ACTORS; + + if (buildActiveActors && mBuildFrozenActors) + mScene.buildActiveAndFrozenActors(); + else if (buildActiveActors) + mScene.buildActiveActors(); + } + + mRenderBuffer.append(mScene.getScScene().getRenderBuffer()); + + PX_ASSERT(getSimulationStage() != Sc::SimulationStage::eCOMPLETE); + if (mControllingSimulation) + { + mTaskManager->stopSimulation(); + } + + setSimulationStage(Sc::SimulationStage::eCOMPLETE); + + mPhysicsDone.reset(); // allow Physics to run again + mCollisionDone.reset(); +} + +bool NpScene::fetchResults(bool block, PxU32* errorState) +{ + if(getSimulationStage() != Sc::SimulationStage::eADVANCE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::fetchResults: fetchResults() called illegally! It must be called after advance() or simulate()"); + return false; + } + + if(!checkResultsInternal(block)) + return false; + + { + PX_SIMD_GUARD; + + // take write check *after* simulation has finished, otherwise + // we will block simulation callbacks from using the API + // disallow re-entry to detect callbacks making write calls + NP_WRITE_CHECK_NOREENTRY(this); + + // we use cross thread profile here, to show the event in cross thread view + // PT: TODO: why do we want to show it in the cross thread view? + PX_PROFILE_START_CROSSTHREAD("Basic.fetchResults", getContextId()); + PX_PROFILE_ZONE("Sim.fetchResults", getContextId()); + + fetchResultsPreContactCallbacks(); + + { + // PT: TODO: why a cross-thread event here? + PX_PROFILE_START_CROSSTHREAD("Basic.processCallbacks", getContextId()); + mScene.fireQueuedContactCallbacks(); + PX_PROFILE_STOP_CROSSTHREAD("Basic.processCallbacks", getContextId()); + } + + fetchResultsPostContactCallbacks(); + + PX_PROFILE_STOP_CROSSTHREAD("Basic.fetchResults", getContextId()); + PX_PROFILE_STOP_CROSSTHREAD("Basic.simulate", getContextId()); + + if(errorState) + *errorState = 0; + } + +#if PX_SUPPORT_PVD + { + PX_SIMD_GUARD; + mScene.getScenePvdClient().frameEnd(); + } +#endif + return true; +} + +bool NpScene::fetchResultsStart(const PxContactPairHeader*& contactPairs, PxU32& nbContactPairs, bool block) +{ + if (getSimulationStage() != Sc::SimulationStage::eADVANCE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PXScene::fetchResultsStart: fetchResultsStart() called illegally! It must be called after advance() or simulate()"); + return false; + } + + if (!checkResultsInternal(block)) + return false; + + PX_SIMD_GUARD; + NP_WRITE_CHECK(this); + + // we use cross thread profile here, to show the event in cross thread view + PX_PROFILE_START_CROSSTHREAD("Basic.fetchResults", getContextId()); + PX_PROFILE_ZONE("Sim.fetchResultsStart", getContextId()); + + fetchResultsPreContactCallbacks(); + const Ps::Array& pairs = mScene.getQueuedContactPairHeaders(); + nbContactPairs = pairs.size(); + contactPairs = pairs.begin(); + + mBetweenFetchResults = true; + return true; +} + +void NpContactCallbackTask::setData(NpScene* scene, const PxContactPairHeader* contactPairHeaders, const uint32_t nbContactPairHeaders) +{ + mScene = scene; + mContactPairHeaders = contactPairHeaders; + mNbContactPairHeaders = nbContactPairHeaders; +} + +void NpContactCallbackTask::run() +{ + physx::PxSimulationEventCallback* callback = mScene->getSimulationEventCallback(); + if(!callback) + return; + + mScene->lockRead(); + for(uint32_t i=0; ionContact(pairHeader, pairHeader.pairs, pairHeader.nbPairs); + } + mScene->unlockRead(); +} + +void NpScene::processCallbacks(physx::PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Basic.processCallbacks", getContextId()); + PX_PROFILE_ZONE("Sim.processCallbacks", getContextId()); + //ML: because Apex destruction callback isn't thread safe so that we make this run single thread first + const Ps::Array& pairs = mScene.getQueuedContactPairHeaders(); + const PxU32 nbPairs = pairs.size(); + const PxContactPairHeader* contactPairs = pairs.begin(); + const PxU32 nbToProcess = 256; + + Cm::FlushPool* flushPool = mScene.getScScene().getFlushPool(); + + for (PxU32 i = 0; i < nbPairs; i += nbToProcess) + { + NpContactCallbackTask* task = PX_PLACEMENT_NEW(flushPool->allocate(sizeof(NpContactCallbackTask)), NpContactCallbackTask)(); + task->setData(this, contactPairs+i, PxMin(nbToProcess, nbPairs - i)); + task->setContinuation(continuation); + task->removeReference(); + } +} + +void NpScene::fetchResultsFinish(PxU32* errorState) +{ + { + PX_SIMD_GUARD; + PX_PROFILE_STOP_CROSSTHREAD("Basic.processCallbacks", getContextId()); + PX_PROFILE_ZONE("Basic.fetchResultsFinish", getContextId()); + + mBetweenFetchResults = false; + NP_WRITE_CHECK(this); + + fetchResultsPostContactCallbacks(); + + if (errorState) + *errorState = 0; + + PX_PROFILE_STOP_CROSSTHREAD("Basic.fetchResults", getContextId()); + PX_PROFILE_STOP_CROSSTHREAD("Basic.simulate", getContextId()); + } + +#if PX_SUPPORT_PVD + mScene.getScenePvdClient().frameEnd(); +#endif +} + +void NpScene::flushSimulation(bool sendPendingReports) +{ + PX_PROFILE_ZONE("API.flushSimulation", getContextId()); + NP_WRITE_CHECK_NOREENTRY(this); + PX_SIMD_GUARD; + + if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxScene::flushSimulation(): This call is not allowed while the simulation is running. Call will be ignored"); + return; + } + + mScene.flush(sendPendingReports); + mSQManager.flushMemory(); + + //!!! TODO: Shrink all NpObject lists? +} + +void NpScene::flushQueryUpdates() +{ + // DS: how do we profile const methods?????? + PX_PROFILE_ZONE("API.flushQueryUpdates", getContextId()); + NP_READ_CHECK(this); + PX_SIMD_GUARD; + + if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxScene::flushQueryUpdates(): This call is not allowed while the simulation is running. Call will be ignored"); + return; + } + + mSQManager.flushUpdates(); +} + +/* +Replaces finishRun() with the addition of appropriate thread sync(pulled out of PhysicsThread()) + +Note: this function can be called from the application thread or the physics thread, depending on the +scene flags. +*/ +void NpScene::executeScene(PxBaseTask* continuation) +{ + mScene.simulate(mElapsedTime, continuation); +} + +void NpScene::executeCollide(PxBaseTask* continuation) +{ + mScene.collide(mElapsedTime, continuation); +} + +void NpScene::executeAdvance(PxBaseTask* continuation) +{ + mScene.advance(mElapsedTime, continuation); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::addMaterial(const NpMaterial& mat) +{ + mScene.addMaterial(mat.getScMaterial()); +} + +void NpScene::updateMaterial(const NpMaterial& mat) +{ + //PxU32 index = mat.getTableIndex(); + mScene.updateMaterial(mat.getScMaterial()); +} + +void NpScene::removeMaterial(const NpMaterial& mat) +{ + //PxU32 index = mat.getTableIndex(); + mScene.removeMaterial(mat.getScMaterial()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance) +{ + NP_WRITE_CHECK(this); + PX_CHECK_AND_RETURN((group1 < PX_MAX_DOMINANCE_GROUP && group2 < PX_MAX_DOMINANCE_GROUP), + "PxScene::setDominanceGroupPair: invalid params! Groups must be <= 31!"); + //can't change matrix diagonal + PX_CHECK_AND_RETURN(group1 != group2, "PxScene::setDominanceGroupPair: invalid params! Groups must be unequal! Can't change matrix diagonal!"); + PX_CHECK_AND_RETURN( + ((dominance.dominance0) == 1.0f && (dominance.dominance1 == 1.0f)) + || ((dominance.dominance0) == 1.0f && (dominance.dominance1 == 0.0f)) + || ((dominance.dominance0) == 0.0f && (dominance.dominance1 == 1.0f)) + , "PxScene::setDominanceGroupPair: invalid params! dominance must be one of (1,1), (1,0), or (0,1)!"); + + mScene.setDominanceGroupPair(group1, group2, dominance); +} + +PxDominanceGroupPair NpScene::getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const +{ + NP_READ_CHECK(this); + PX_CHECK_AND_RETURN_VAL((group1 < PX_MAX_DOMINANCE_GROUP && group2 < PX_MAX_DOMINANCE_GROUP), + "PxScene::getDominanceGroupPair: invalid params! Groups must be <= 31!", PxDominanceGroupPair(PxU8(1u), PxU8(1u))); + return mScene.getDominanceGroupPair(group1, group2); +} + +/////////////////////////////////////////////////////////////////////////////// + +#if PX_SUPPORT_GPU_PHYSX + +void NpScene::updatePhysXIndicator() +{ + Ps::IntBool isGpu = mScene.getScScene().isUsingGpuRigidBodies(); + + mPhysXIndicator.setIsGpu(isGpu != 0); +} +#endif //PX_SUPPORT_GPU_PHYSX + + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setSceneQueryUpdateMode(PxSceneQueryUpdateMode::Enum updateMode) +{ + NP_WRITE_CHECK(this); + mSceneQueryUpdateMode = updateMode; +} + +PxSceneQueryUpdateMode::Enum NpScene::getSceneQueryUpdateMode() const +{ + NP_READ_CHECK(this); + return mSceneQueryUpdateMode; +} + +void NpScene::setDynamicTreeRebuildRateHint(PxU32 dynamicTreeRebuildRateHint) +{ + PX_CHECK_AND_RETURN((dynamicTreeRebuildRateHint >= 4), "PxScene::setDynamicTreeRebuildRateHint(): Param has to be >= 4!"); + mSQManager.setDynamicTreeRebuildRateHint(dynamicTreeRebuildRateHint); +} + +PxU32 NpScene::getDynamicTreeRebuildRateHint() const +{ + NP_READ_CHECK(this); + return mSQManager.getDynamicTreeRebuildRateHint(); +} + +void NpScene::forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure) +{ + PX_PROFILE_ZONE("API.forceDynamicTreeRebuild", getContextId()); + NP_WRITE_CHECK(this); + PX_SIMD_GUARD; + mSQManager.forceDynamicTreeRebuild(rebuildStaticStructure, rebuildDynamicStructure); +} + +void NpScene::setSolverBatchSize(PxU32 solverBatchSize) +{ + NP_WRITE_CHECK(this); + mScene.setSolverBatchSize(solverBatchSize); +} + +PxU32 NpScene::getSolverBatchSize(void) const +{ + NP_READ_CHECK(this); + // get from our local copy + return mScene.getSolverBatchSize(); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NpScene::setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) +{ + NP_WRITE_CHECK(this); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(value), "PxScene::setVisualizationParameter: value is not valid.", false); + + if (param >= PxVisualizationParameter::eNUM_VALUES) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "setVisualizationParameter: parameter out of range."); + return false; + } + else if (value < 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "setVisualizationParameter: value must be larger or equal to 0."); + return false; + } + else + { + mScene.setVisualizationParameter(param, value); + return true; + } +} + +PxReal NpScene::getVisualizationParameter(PxVisualizationParameter::Enum param) const +{ + if (param < PxVisualizationParameter::eNUM_VALUES) + return mScene.getVisualizationParameter(param); + else + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "getVisualizationParameter: param is not an enum."); + + return 0.0f; +} + +void NpScene::setVisualizationCullingBox(const PxBounds3& box) +{ + NP_WRITE_CHECK(this); + PX_CHECK_MSG(box.isValid(), "PxScene::setVisualizationCullingBox(): invalid bounds provided!"); + mScene.setVisualizationCullingBox(box); +} + +PxBounds3 NpScene::getVisualizationCullingBox() const +{ + NP_READ_CHECK(this); + const PxBounds3& bounds = mScene.getVisualizationCullingBox(); + PX_ASSERT(bounds.isValid()); + return bounds; +} + +void NpScene::setNbContactDataBlocks(PxU32 numBlocks) +{ + PX_CHECK_AND_RETURN((getSimulationStage() == Sc::SimulationStage::eCOMPLETE), + "PxScene::setNbContactDataBlock: This call is not allowed while the simulation is running. Call will be ignored!"); + + mScene.getScScene().setNbContactDataBlocks(numBlocks); +} + +PxU32 NpScene::getNbContactDataBlocksUsed() const +{ + PX_CHECK_AND_RETURN_VAL((getSimulationStage() == Sc::SimulationStage::eCOMPLETE), + "PxScene::getNbContactDataBlocksUsed: This call is not allowed while the simulation is running. Returning 0.", 0); + + return mScene.getScScene().getNbContactDataBlocksUsed(); +} + +PxU32 NpScene::getMaxNbContactDataBlocksUsed() const +{ + PX_CHECK_AND_RETURN_VAL((getSimulationStage() == Sc::SimulationStage::eCOMPLETE), + "PxScene::getMaxNbContactDataBlocksUsed: This call is not allowed while the simulation is running. Returning 0.", 0); + + return mScene.getScScene().getMaxNbContactDataBlocksUsed(); +} + +PxU32 NpScene::getTimestamp() const +{ + return mScene.getScScene().getTimeStamp(); +} + +PxU32 NpScene::getSceneQueryStaticTimestamp() const +{ + return mSQManager.get(PruningIndex::eSTATIC).timestamp(); +} + +PxCpuDispatcher* NpScene::getCpuDispatcher() const +{ + return getTaskManager()->getCpuDispatcher(); +} + +PxGpuDispatcher* NpScene::getGpuDispatcher() const +{ + return getTaskManager()->getGpuDispatcher(); +} + +PxPruningStructureType::Enum NpScene::getStaticStructure() const +{ + return mSQManager.get(PruningIndex::eSTATIC).type(); +} + +PxPruningStructureType::Enum NpScene::getDynamicStructure() const +{ + return mSQManager.get(PruningIndex::eDYNAMIC).type(); +} + +PxReal NpScene::getFrictionOffsetThreshold() const +{ + return mScene.getScScene().getFrictionOffsetThreshold(); +} + +PxU32 NpScene::getContactReportStreamBufferSize() const +{ + return mScene.getScScene().getDefaultContactReportStreamBufferSize(); +} + +#if PX_CHECKED +void NpScene::checkPositionSanity(const PxRigidActor& a, const PxTransform& pose, const char* fnName) const +{ + if(!mSanityBounds.contains(pose.p)) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "%s: actor pose for %lp is outside sanity bounds\n", fnName, &a); +} +#endif + +namespace +{ + struct ThreadReadWriteCount + { + ThreadReadWriteCount(const size_t data) + : readDepth(data & 0xFF), + writeDepth((data >> 8) & 0xFF), + readLockDepth((data >> 16) & 0xFF), + writeLockDepth((data >> 24) & 0xFF) + { + + } + + size_t getData() const { return size_t(writeLockDepth) << 24 | size_t(readLockDepth) << 16 | size_t(writeDepth) << 8 | size_t(readDepth); } + + PxU8 readDepth; // depth of re-entrant reads + PxU8 writeDepth; // depth of re-entrant writes + + PxU8 readLockDepth; // depth of read-locks + PxU8 writeLockDepth; // depth of write-locks + }; +} + +#if NP_ENABLE_THREAD_CHECKS + +NpScene::StartWriteResult::Enum NpScene::startWrite(bool allowReentry) +{ + PX_COMPILE_TIME_ASSERT(sizeof(ThreadReadWriteCount) == 4); + + if (mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) + { + ThreadReadWriteCount localCounts(TlsGetValue(mThreadReadWriteDepth)); + + if (mBetweenFetchResults) + return StartWriteResult::eIN_FETCHRESULTS; + + // ensure we already have the write lock + return localCounts.writeLockDepth > 0 ? StartWriteResult::eOK : StartWriteResult::eNO_LOCK; + } + + { + ThreadReadWriteCount localCounts(TlsGetValue(mThreadReadWriteDepth)); + StartWriteResult::Enum result; + + if (mBetweenFetchResults) + result = StartWriteResult::eIN_FETCHRESULTS; + + // check we are the only thread reading (allows read->write order on a single thread) and no other threads are writing + else if (mConcurrentReadCount != localCounts.readDepth || mConcurrentWriteCount != localCounts.writeDepth) + result = StartWriteResult::eRACE_DETECTED; + + else + result = StartWriteResult::eOK; + + // increment shared write counter + Ps::atomicIncrement(&mConcurrentWriteCount); + + // in the normal case (re-entry is allowed) then we simply increment + // the writeDepth by 1, otherwise (re-entry is not allowed) increment + // by 2 to force subsequent writes to fail by creating a mismatch between + // the concurrent write counter and the local counter, any value > 1 will do + localCounts.writeDepth += allowReentry ? 1 : 2; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + if (result != StartWriteResult::eOK) + Ps::atomicIncrement(&mConcurrentErrorCount); + + return result; + } +} + +void NpScene::stopWrite(bool allowReentry) +{ + if (!(mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK)) + { + Ps::atomicDecrement(&mConcurrentWriteCount); + + // decrement depth of writes for this thread + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + + // see comment in startWrite() + if (allowReentry) + localCounts.writeDepth--; + else + localCounts.writeDepth-=2; + + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + } +} + +bool NpScene::startRead() const +{ + if (mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) + { + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + + // ensure we already have the write or read lock + return localCounts.writeLockDepth > 0 || localCounts.readLockDepth > 0; + } + else + { + Ps::atomicIncrement(&mConcurrentReadCount); + + // update current threads read depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + localCounts.readDepth++; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + // success if the current thread is already performing a write (API re-entry) or no writes are in progress + bool success = (localCounts.writeDepth > 0 || mConcurrentWriteCount == 0); + + if (!success) + Ps::atomicIncrement(&mConcurrentErrorCount); + + return success; + } +} + +void NpScene::stopRead() const +{ + if (!(mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK)) + { + Ps::atomicDecrement(&mConcurrentReadCount); + + // update local threads read depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + localCounts.readDepth--; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + } +} + +#else + +NpScene::StartWriteResult::Enum NpScene::startWrite(bool) { PX_ASSERT(0); return NpScene::StartWriteResult::eOK; } +void NpScene::stopWrite(bool) {} + +bool NpScene::startRead() const { PX_ASSERT(0); return false; } +void NpScene::stopRead() const {} + +#endif // NP_ENABLE_THREAD_CHECKS + +void NpScene::lockRead(const char* /*file*/, PxU32 /*line*/) +{ + // increment this threads read depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + localCounts.readLockDepth++; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + // if we are the current writer then increment the reader count but don't actually lock (allow reading from threads with write ownership) + if(localCounts.readLockDepth == 1) + mRWLock.lockReader(mCurrentWriter != Thread::getId()); +} + +void NpScene::unlockRead() +{ + // increment this threads read depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + if(localCounts.readLockDepth < 1) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::unlockRead() called without matching call to PxScene::lockRead(), behaviour will be undefined."); + return; + } + localCounts.readLockDepth--; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + // only unlock on last read + if(localCounts.readLockDepth == 0) + mRWLock.unlockReader(); +} + +void NpScene::lockWrite(const char* file, PxU32 line) +{ + // increment this threads write depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + if (localCounts.writeLockDepth == 0 && localCounts.readLockDepth > 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, file?file:__FILE__, file?int(line):__LINE__, "PxScene::lockWrite() detected after a PxScene::lockRead(), lock upgrading is not supported, behaviour will be undefined."); + return; + } + localCounts.writeLockDepth++; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + // only lock on first call + if (localCounts.writeLockDepth == 1) + mRWLock.lockWriter(); + + PX_ASSERT(mCurrentWriter == 0 || mCurrentWriter == Thread::getId()); + + // set ourselves as the current writer + mCurrentWriter = Thread::getId(); +} + +void NpScene::unlockWrite() +{ + // increment this thread's write depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + if (localCounts.writeLockDepth < 1) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::unlockWrite() called without matching call to PxScene::lockWrite(), behaviour will be undefined."); + return; + } + localCounts.writeLockDepth--; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + PX_ASSERT(mCurrentWriter == Thread::getId()); + + if (localCounts.writeLockDepth == 0) + { + mCurrentWriter = 0; + mRWLock.unlockWriter(); + } +} + +PxReal NpScene::getWakeCounterResetValue() const +{ + NP_READ_CHECK(this); + + return getWakeCounterResetValueInteral(); +} + +static PX_FORCE_INLINE void shiftRigidActor(PxRigidActor* a, const PxVec3& shift) +{ + PxActorType::Enum t = a->getType(); + if (t == PxActorType::eRIGID_DYNAMIC) + { + NpRigidDynamic* rd = static_cast(a); + rd->getScbBodyFast().onOriginShift(shift); + } + else if (t == PxActorType::eRIGID_STATIC) + { + NpRigidStatic* rs = static_cast(a); + rs->getScbRigidStaticFast().onOriginShift(shift); + } + else + { + PX_ASSERT(t == PxActorType::eARTICULATION_LINK); + NpArticulationLink* al = static_cast(a); + al->getScbBodyFast().onOriginShift(shift); + } +} + +void NpScene::shiftOrigin(const PxVec3& shift) +{ + PX_PROFILE_ZONE("API.shiftOrigin", getContextId()); + NP_WRITE_CHECK(this); + + if(mScene.isPhysicsBuffering()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::shiftOrigin() not allowed while simulation is running. Call will be ignored."); + return; + } + + PX_SIMD_GUARD; + + const PxU32 prefetchLookAhead = 4; + PxU32 rigidCount = mRigidActors.size(); + PxRigidActor*const* rigidActors = mRigidActors.begin(); + PxU32 batchIterCount = rigidCount / prefetchLookAhead; + + PxU32 idx = 0; + for(PxU32 i=0; i < batchIterCount; i++) + { + // prefetch elements for next batch + if (i < (batchIterCount-1)) + { + Ps::prefetchLine(rigidActors[idx + prefetchLookAhead]); + Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead]) + 128); // for the buffered pose + Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 1]); + Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead + 1]) + 128); + Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 2]); + Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead + 2]) + 128); + Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 3]); + Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead + 3]) + 128); + } + else + { + for(PxU32 k=(idx + prefetchLookAhead); k < rigidCount; k++) + { + Ps::prefetchLine(rigidActors[k]); + Ps::prefetchLine(reinterpret_cast(rigidActors[k]) + 128); + } + } + + for(PxU32 j=idx; j < (idx + prefetchLookAhead); j++) + { + shiftRigidActor(rigidActors[j], shift); + } + + idx += prefetchLookAhead; + } + // process remaining objects + for(PxU32 i=idx; i < rigidCount; i++) + { + shiftRigidActor(rigidActors[i], shift); + } + + PxArticulationBase*const* articulations = mArticulations.getEntries(); + for(PxU32 i=0; i < mArticulations.size(); i++) + { + PxArticulationBase* np = (articulations[i]); + + NpArticulationLink*const* links = reinterpret_cast(np->getImpl())->getLinks(); + + for(PxU32 j=0; j < np->getNbLinks(); j++) + { + shiftRigidActor(links[j], shift); + } + } + + + mScene.shiftOrigin(shift); + + + // + // shift scene query related data structures + // + mSQManager.shiftOrigin(shift); + +#if PX_ENABLE_DEBUG_VISUALIZATION + // + // debug visualization + // + mRenderBuffer.shift(-shift); +#endif +} + +#if PX_SUPPORT_PVD +PxPvdSceneClient* NpScene::getScenePvdClient() +{ + return &mScene.getScenePvdClient(); +} +#else +PxPvdSceneClient* NpScene::getScenePvdClient() +{ + return NULL; +} +#endif + +PxsSimulationController* NpScene::getSimulationController() +{ + return mScene.getScScene().getSimulationController(); +} + +void NpScene::setActiveActors(PxActor** actors, PxU32 nbActors) +{ + NP_WRITE_CHECK(this); + mScene.setActiveActors(actors, nbActors); +} + +void NpScene::forceSceneQueryRebuild() +{ + SqRefFinder sqRefFinder; + mScene.getScScene().syncSceneQueryBounds(mSQManager.getDynamicBoundsSync(), sqRefFinder); + + mSQManager.afterSync(getSceneQueryUpdateModeFast()); +} + +void NpScene::sceneQueriesUpdate(physx::PxBaseTask* completionTask, bool controlSimulation) +{ + PX_SIMD_GUARD; + + bool runUpdateTasks[PruningIndex::eCOUNT] = {true, true}; + { + // write guard must end before scene queries tasks kicks off worker threads + NP_WRITE_CHECK(this); + + PX_PROFILE_START_CROSSTHREAD("Basic.sceneQueriesUpdate", getContextId()); + + if(mSceneQueriesUpdateRunning) + { + //fetchSceneQueries doesn't get called + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::fetchSceneQueries was not called!"); + return; + } + + // flush scene queries updates + mSQManager.flushUpdates(); + + // prepare scene queries for build - copy bounds + runUpdateTasks[PruningIndex::eSTATIC] = mSQManager.prepareSceneQueriesUpdate(PruningIndex::eSTATIC); + runUpdateTasks[PruningIndex::eDYNAMIC] = mSQManager.prepareSceneQueriesUpdate(PruningIndex::eDYNAMIC); + + mSceneQueriesUpdateRunning = true; + } + + { + PX_PROFILE_ZONE("Sim.sceneQueriesTaskSetup", getContextId()); + + if (controlSimulation) + { + { + PX_PROFILE_ZONE("Sim.resetDependencies", getContextId()); + // Only reset dependencies, etc if we own the TaskManager. Will be false + // when an NpScene is controlled by an APEX scene. + mTaskManager->resetDependencies(); + } + mTaskManager->startSimulation(); + } + + mSceneQueriesCompletion.setContinuation(*mTaskManager, completionTask); + if(runUpdateTasks[PruningIndex::eSTATIC]) + mSceneQueriesStaticPrunerUpdate.setContinuation(&mSceneQueriesCompletion); + if(runUpdateTasks[PruningIndex::eDYNAMIC]) + mSceneQueriesDynamicPrunerUpdate.setContinuation(&mSceneQueriesCompletion); + + mSceneQueriesCompletion.removeReference(); + if(runUpdateTasks[PruningIndex::eSTATIC]) + mSceneQueriesStaticPrunerUpdate.removeReference(); + if(runUpdateTasks[PruningIndex::eDYNAMIC]) + mSceneQueriesDynamicPrunerUpdate.removeReference(); + } +} + +bool NpScene::checkSceneQueriesInternal(bool block) +{ + PX_PROFILE_ZONE("Basic.checkSceneQueries", getContextId()); + return mSceneQueriesDone.wait(block ? Ps::Sync::waitForever : 0); +} + +bool NpScene::checkQueries(bool block) +{ + return checkSceneQueriesInternal(block); +} + +bool NpScene::fetchQueries(bool block) +{ + if(!mSceneQueriesUpdateRunning) + { + //fetchSceneQueries doesn't get called + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "PxScene::fetchQueries: fetchQueries() called illegally! It must be called after sceneQueriesUpdate()"); + return false; + } + + if(!checkSceneQueriesInternal(block)) + return false; + + { + PX_SIMD_GUARD; + + NP_WRITE_CHECK(this); + + // we use cross thread profile here, to show the event in cross thread view + // PT: TODO: why do we want to show it in the cross thread view? + PX_PROFILE_START_CROSSTHREAD("Basic.fetchQueries", getContextId()); + + // flush updates and commit if work is done + mSQManager.flushUpdates(); + + PX_PROFILE_STOP_CROSSTHREAD("Basic.fetchQueries", getContextId()); + PX_PROFILE_STOP_CROSSTHREAD("Basic.sceneQueriesUpdate", getContextId()); + + mSceneQueriesDone.reset(); + mSceneQueriesUpdateRunning = false; + } + return true; +} + +void NpScene::frameEnd() +{ +#if PX_SUPPORT_PVD + mScene.getScenePvdClient().frameEnd(); +#endif +} + +PxBatchQuery* NpScene::createBatchQuery(const PxBatchQueryDesc& desc) +{ + PX_PROFILE_ZONE("API.createBatchQuery", getContextId()); + PX_CHECK_AND_RETURN_NULL(desc.isValid(),"Supplied PxBatchQueryDesc is not valid. createBatchQuery returns NULL."); + + NpBatchQuery* bq = PX_NEW(NpBatchQuery)(*this, desc); + mBatchQueries.pushBack(bq); + return bq; +} + +void NpScene::releaseBatchQuery(PxBatchQuery* sq) +{ + PX_PROFILE_ZONE("API.releaseBatchQuery", getContextId()); + NpBatchQuery* npsq = static_cast(sq); + bool found = mBatchQueries.findAndReplaceWithLast(npsq); + PX_UNUSED(found); PX_ASSERT(found); + PX_DELETE_AND_RESET(npsq); +} diff --git a/src/PhysX/physx/source/physx/src/NpScene.h b/src/PhysX/physx/source/physx/src/NpScene.h new file mode 100644 index 000000000..eca152c6e --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpScene.h @@ -0,0 +1,496 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_SCENE +#define PX_PHYSICS_NP_SCENE + +#include "PsUserAllocated.h" +#include "PsSync.h" +#include "PsArray.h" +#include "PsThread.h" +#include "PsHashSet.h" +#include "PxPhysXConfig.h" + +#if PX_SUPPORT_GPU_PHYSX +#include "device/PhysXIndicator.h" +#endif + +#include "NpSceneQueries.h" +#include "NpSceneAccessor.h" + +namespace physx +{ + +class PhysicsThread; +class PxBatchQueryDesc; +class NpMaterial; +class NpScene; +class NpArticulation; + +namespace Sc +{ + class Joint; + class ConstraintBreakEvent; +} + +namespace Sq +{ + class SceneQueryManager; +} + +class NpObjectFactory; +class NpRigidStatic; +class NpRigidDynamic; +class NpConstraint; +class NpArticulationLink; +class NpShapeManager; +class NpBatchQuery; + +class PxBatchQuery; + +enum NpProfileZones +{ + NpScene_checkResults, + NpScene_reportContacts, + NpScene_reportProfiling, + NpScene_reportTriggers, + NpScene_stats, + + NpPrNumZones +}; + + +class NpContactCallbackTask : public physx::PxLightCpuTask +{ + NpScene* mScene; + const PxContactPairHeader* mContactPairHeaders; + uint32_t mNbContactPairHeaders; + +public: + + void setData(NpScene* scene, const PxContactPairHeader* contactPairHeaders, const uint32_t nbContactPairHeaders); + + virtual void run(); + + virtual const char* getName() const + { + return "NpContactCallbackTask"; + } +}; + +class NpScene : public NpSceneQueries, public Ps::UserAllocated +{ + //virtual interfaces: + + PX_NOCOPY(NpScene) + public: + + virtual void release(); + + virtual void setFlag(PxSceneFlag::Enum flag, bool value); + virtual PxSceneFlags getFlags() const; + + // implement PxScene: + + virtual void setGravity(const PxVec3&); + virtual PxVec3 getGravity() const; + + virtual void setBounceThresholdVelocity(const PxReal t); + virtual PxReal getBounceThresholdVelocity() const; + + virtual PxReal getFrictionOffsetThreshold() const; + + virtual void setLimits(const PxSceneLimits& limits); + virtual PxSceneLimits getLimits() const; + + virtual void addActor(PxActor& actor, const PxBVHStructure* bvhStructure); + virtual void removeActor(PxActor& actor, bool wakeOnLostTouch); + + virtual PxU32 getNbConstraints() const; + virtual PxU32 getConstraints(PxConstraint** buffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual void addArticulation(PxArticulationBase&); + virtual void removeArticulation(PxArticulationBase&, bool wakeOnLostTouch); + + virtual PxU32 getNbArticulations() const; + virtual PxU32 getArticulations(PxArticulationBase** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + // Aggregates + virtual void addAggregate(PxAggregate&); + virtual void removeAggregate(PxAggregate&, bool wakeOnLostTouch); + virtual PxU32 getNbAggregates() const; + virtual PxU32 getAggregates(PxAggregate** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual void addCollection(const PxCollection& collection); + + // Groups + virtual void setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance); + virtual PxDominanceGroupPair getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const; + + // Actors + virtual PxU32 getNbActors(PxActorTypeFlags types) const; + virtual PxU32 getActors(PxActorTypeFlags types, PxActor** buffer, PxU32 bufferSize, PxU32 startIndex=0) const; + virtual PxActor** getActiveActors(PxU32& nbActorsOut); + + // Run + virtual void getSimulationStatistics(PxSimulationStatistics& s) const; + + // Multiclient + virtual PxClientID createClient(); + + // FrictionModel + virtual void setFrictionType(PxFrictionType::Enum frictionType); + virtual PxFrictionType::Enum getFrictionType() const; + + // Callbacks + virtual void setSimulationEventCallback(PxSimulationEventCallback* callback); + virtual PxSimulationEventCallback* getSimulationEventCallback() const; + virtual void setContactModifyCallback(PxContactModifyCallback* callback); + virtual PxContactModifyCallback* getContactModifyCallback() const; + virtual void setCCDContactModifyCallback(PxCCDContactModifyCallback* callback); + virtual PxCCDContactModifyCallback* getCCDContactModifyCallback() const; + virtual void setBroadPhaseCallback(PxBroadPhaseCallback* callback); + virtual PxBroadPhaseCallback* getBroadPhaseCallback() const; + + //CCD passes + virtual void setCCDMaxPasses(PxU32 ccdMaxPasses); + virtual PxU32 getCCDMaxPasses() const; + + // Collision filtering + virtual void setFilterShaderData(const void* data, PxU32 dataSize); + virtual const void* getFilterShaderData() const; + virtual PxU32 getFilterShaderDataSize() const; + virtual PxSimulationFilterShader getFilterShader() const; + virtual PxSimulationFilterCallback* getFilterCallback() const; + virtual void resetFiltering(PxActor& actor); + virtual void resetFiltering(PxRigidActor& actor, PxShape*const* shapes, PxU32 shapeCount); + + // Get Physics SDK + virtual PxPhysics& getPhysics(); + + // new API methods + virtual void simulate(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation); + virtual void advance(physx::PxBaseTask* completionTask); + virtual void collide(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation = true); + virtual bool checkResults(bool block); + virtual bool checkCollision(bool block); + virtual bool fetchCollision(bool block); + virtual bool fetchResults(bool block, PxU32* errorState); + virtual bool fetchResultsStart(const PxContactPairHeader*& contactPairs, PxU32& nbContactPairs, bool block = false); + virtual void processCallbacks(physx::PxBaseTask* continuation); + virtual void fetchResultsFinish(PxU32* errorState = 0); + + + + virtual void flush(bool sendPendingReports) { flushSimulation(sendPendingReports); } + virtual void flushSimulation(bool sendPendingReports); + virtual void flushQueryUpdates(); + virtual const PxRenderBuffer& getRenderBuffer(); + + virtual PxBatchQuery* createBatchQuery(const PxBatchQueryDesc& desc); + void releaseBatchQuery(PxBatchQuery* bq); + virtual void setDynamicTreeRebuildRateHint(PxU32 dynamicTreeRebuildRateHint); + virtual PxU32 getDynamicTreeRebuildRateHint() const; + virtual void forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure); + virtual void sceneQueriesUpdate(physx::PxBaseTask* completionTask, bool controlSimulation); + virtual bool checkQueries(bool block); + virtual bool fetchQueries(bool block); + virtual void setSceneQueryUpdateMode(PxSceneQueryUpdateMode::Enum updateMode); + virtual PxSceneQueryUpdateMode::Enum getSceneQueryUpdateMode() const; + + virtual void setSolverBatchSize(PxU32 solverBatchSize); + virtual PxU32 getSolverBatchSize(void) const; + + virtual bool setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value); + virtual PxReal getVisualizationParameter(PxVisualizationParameter::Enum param) const; + + virtual void setVisualizationCullingBox(const PxBounds3& box); + virtual PxBounds3 getVisualizationCullingBox() const; + + virtual PxTaskManager* getTaskManager() { return mTaskManager; } + void checkBeginWrite() const {} + + virtual void setNbContactDataBlocks(PxU32 numBlocks); + virtual PxU32 getNbContactDataBlocksUsed() const; + virtual PxU32 getMaxNbContactDataBlocksUsed() const; + + virtual PxU32 getContactReportStreamBufferSize() const; + + virtual PxU32 getTimestamp() const; + virtual PxU32 getSceneQueryStaticTimestamp() const; + + virtual PxCpuDispatcher* getCpuDispatcher() const; + virtual PxGpuDispatcher* getGpuDispatcher() const; + + virtual PxPruningStructureType::Enum getStaticStructure() const; + virtual PxPruningStructureType::Enum getDynamicStructure() const; + + virtual PxBroadPhaseType::Enum getBroadPhaseType() const; + virtual bool getBroadPhaseCaps(PxBroadPhaseCaps& caps) const; + virtual PxU32 getNbBroadPhaseRegions() const; + virtual PxU32 getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + virtual PxU32 addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion); + virtual bool removeBroadPhaseRegion(PxU32 handle); + + virtual void addActors(PxActor*const* actors, PxU32 nbActors); + virtual void addActors(const PxPruningStructure& prunerStructure); + virtual void removeActors(PxActor*const* actors, PxU32 nbActors, bool wakeOnLostTouch); + + virtual void lockRead(const char* file=NULL, PxU32 line=0); + virtual void unlockRead(); + + virtual void lockWrite(const char* file=NULL, PxU32 line=0); + virtual void unlockWrite(); + + virtual PxReal getWakeCounterResetValue() const; + + virtual void shiftOrigin(const PxVec3& shift); + + virtual PxPvdSceneClient* getScenePvdClient(); + + //Implementations for NpSceneAccessor interface! + virtual PxsSimulationController* getSimulationController(); + virtual void setActiveActors(PxActor** actors, PxU32 nbActors); + virtual PxActor** getFrozenActors(PxU32& nbActorsOut); + virtual void setFrozenActorFlag(const bool buildFrozenActors); + virtual void forceSceneQueryRebuild(); + virtual void frameEnd(); + + //internal public methods: + public: + NpScene(const PxSceneDesc& desc); + ~NpScene(); + + PX_FORCE_INLINE PxTaskManager* getTaskManager() const { return mTaskManager; } + + PX_FORCE_INLINE Sc::SimulationStage::Enum getSimulationStage() const { return mScene.getSimulationStage(); } + PX_FORCE_INLINE void setSimulationStage(Sc::SimulationStage::Enum stage) { mScene.setSimulationStage(stage); } + + void addActorInternal(PxActor& actor, const PxBVHStructure* bvhStructure); + void removeActorInternal(PxActor& actor, bool wakeOnLostTouch, bool removeFromAggregate); + void addActorsInternal(PxActor*const* PX_RESTRICT actors, PxU32 nbActors, const Sq::PruningStructure* ps = NULL); + + void addArticulationInternal(PxArticulationBase&); + void removeArticulationInternal(PxArticulationBase&, bool wakeOnLostTouch, bool removeFromAggregate); + // materials + void addMaterial(const NpMaterial& mat); + void updateMaterial(const NpMaterial& mat); + void removeMaterial(const NpMaterial& mat); + + void executeScene(PxBaseTask* continuation); + void executeCollide(PxBaseTask* continuation); + void executeAdvance(PxBaseTask* continuation); + void constraintBreakEventNotify(PxConstraint *const *constraints, PxU32 count); + + bool loadFromDesc(const PxSceneDesc&); + + void removeFromRigidActorList(const PxU32&); + PX_FORCE_INLINE void removeFromArticulationList(PxArticulationBase&); + PX_FORCE_INLINE void removeFromAggregateList(PxAggregate&); + + PX_FORCE_INLINE void addToConstraintList(PxConstraint&); + PX_FORCE_INLINE void removeFromConstraintList(PxConstraint&); + + void addArticulationLink(NpArticulationLink& link); + void addArticulationLinkBody(NpArticulationLink& link); + void addArticulationLinkConstraint(NpArticulationLink& link); + void removeArticulationLink(NpArticulationLink& link, bool wakeOnLostTouch); + + struct StartWriteResult + { + enum Enum { eOK, eNO_LOCK, eIN_FETCHRESULTS, eRACE_DETECTED }; + }; + + StartWriteResult::Enum startWrite(bool allowReentry); + void stopWrite(bool allowReentry); + + bool startRead() const; + void stopRead() const; + + PxU32 getReadWriteErrorCount() const { return PxU32(mConcurrentErrorCount); } + +#if PX_CHECKED + void checkPositionSanity(const PxRigidActor& a, const PxTransform& pose, const char* fnName) const; +#endif + +#if PX_SUPPORT_GPU_PHYSX + void updatePhysXIndicator(); +#else + PX_FORCE_INLINE void updatePhysXIndicator() {} +#endif + + PX_FORCE_INLINE PxReal getWakeCounterResetValueInteral() const { return mScene.getWakeCounterResetValue(); } + +private: + bool checkResultsInternal(bool block); + bool checkCollisionInternal(bool block); + bool checkSceneQueriesInternal(bool block); + void simulateOrCollide(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation, const char* invalidCallMsg, Sc::SimulationStage::Enum simStage); + + void addRigidStatic(NpRigidStatic& , const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure = false); + void removeRigidStatic(NpRigidStatic&, bool wakeOnLostTouch, bool removeFromAggregate); + void addRigidDynamic(NpRigidDynamic& , const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure = false); + void removeRigidDynamic(NpRigidDynamic&, bool wakeOnLostTouch, bool removeFromAggregate); + + bool addRigidActorsInternal(PxU32 nbActors, PxActor** PX_RESTRICT actors); + + void visualize(); + + void updateDirtyShaders(); + + void fireOutOfBoundsCallbacks(); + void fetchResultsPreContactCallbacks(); + void fetchResultsPostContactCallbacks(); + + + + void updateScbStateAndSetupSq(const PxRigidActor& rigidActor, Scb::Actor& actor, NpShapeManager& shapeManager, bool actorDynamic, const PxBounds3* bounds, bool hasPrunerStructure); + PX_FORCE_INLINE void updateScbStateAndSetupSq(const PxRigidActor& rigidActor, Scb::Body& body, NpShapeManager& shapeManager, bool actorDynamic, const PxBounds3* bounds, bool hasPrunerStructure); + + Cm::RenderBuffer mRenderBuffer; + + Ps::CoalescedHashSet mConstraints; + Ps::Array mRigidActors; // no hash set used because it would be quite a bit slower when adding a large number of actors + Ps::CoalescedHashSet mArticulations; + Ps::CoalescedHashSet mAggregates; + Ps::Array mBatchQueries; + + PxBounds3 mSanityBounds; +#if PX_SUPPORT_GPU_PHYSX + PhysXIndicator mPhysXIndicator; +#endif + + Ps::Sync mPhysicsDone; // physics thread signals this when update ready + Ps::Sync mCollisionDone; // physics thread signals this when all collisions ready + Ps::Sync mSceneQueriesDone; // physics thread signals this when all scene queries update ready + + + //legacy timing settings: + PxReal mElapsedTime; //needed to transfer the elapsed time param from the user to the sim thread. + + PxU32 mNbClients; // Tracks reserved clients for multiclient support. + Ps::Array mClientBehaviorFlags;// Tracks behavior bits for clients. + + + struct SceneCompletion : public Cm::Task + { + SceneCompletion(PxU64 contextId, Ps::Sync& sync) : Cm::Task(contextId), mSync(sync){} + virtual void runInternal() {} + //ML: As soon as mSync.set is called, and the scene is shutting down, + //the scene may be deleted. That means this running task may also be deleted. + //As such, we call mSync.set() inside release() to avoid a crash because the v-table on this + //task might be deleted between the call to runInternal() and release() in the worker thread. + virtual void release() + { + //We cache the continuation pointer because this class may be deleted + //as soon as mSync.set() is called if the application releases the scene. + PxBaseTask* c = mCont; + //once mSync.set(), fetchResults() will be allowed to run. + mSync.set(); + //Call the continuation task that we cached above. If we use mCont or + //any other member variable of this class, there is a small chance + //that the variables might have become corrupted if the class + //was deleted. + if(c) c->removeReference(); + } + virtual const char* getName() const { return "NpScene.completion"; } + + // //This method just is called in the split sim approach as a way to set continuation after the task has been initialized + void setDependent(PxBaseTask* task){PX_ASSERT(mCont == NULL); mCont = task; if(task)task->addReference();} + Ps::Sync& mSync; + private: + SceneCompletion& operator=(const SceneCompletion&); + }; + + typedef Cm::DelegateTask SceneExecution; + typedef Cm::DelegateTask SceneCollide; + typedef Cm::DelegateTask SceneAdvance; + + + PxTaskManager* mTaskManager; + SceneCompletion mSceneCompletion; + SceneCompletion mCollisionCompletion; + SceneCompletion mSceneQueriesCompletion; + SceneExecution mSceneExecution; + SceneCollide mSceneCollide; + SceneAdvance mSceneAdvance; + bool mControllingSimulation; + + PxU32 mSimThreadStackSize; + + volatile PxI32 mConcurrentWriteCount; + mutable volatile PxI32 mConcurrentReadCount; + mutable volatile PxI32 mConcurrentErrorCount; + + // TLS slot index, keeps track of re-entry depth for this thread + PxU32 mThreadReadWriteDepth; + Ps::Thread::Id mCurrentWriter; + Ps::ReadWriteLock mRWLock; + + bool mSceneQueriesUpdateRunning; + + bool mHasSimulatedOnce; + bool mBetweenFetchResults; + bool mBuildFrozenActors; +}; + + +PX_FORCE_INLINE void NpScene::addToConstraintList(PxConstraint& constraint) +{ + mConstraints.insert(&constraint); +} + + +PX_FORCE_INLINE void NpScene::removeFromConstraintList(PxConstraint& constraint) +{ + const bool exists = mConstraints.erase(&constraint); + PX_ASSERT(exists); + PX_UNUSED(exists); +} + + +PX_FORCE_INLINE void NpScene::removeFromArticulationList(PxArticulationBase& articulation) +{ + const bool exists = mArticulations.erase(&articulation); + PX_ASSERT(exists); + PX_UNUSED(exists); +} + + +PX_FORCE_INLINE void NpScene::removeFromAggregateList(PxAggregate& aggregate) +{ + const bool exists = mAggregates.erase(&aggregate); + PX_ASSERT(exists); + PX_UNUSED(exists); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpSceneAccessor.h b/src/PhysX/physx/source/physx/src/NpSceneAccessor.h new file mode 100644 index 000000000..b729b8c6d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpSceneAccessor.h @@ -0,0 +1,60 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NP_SCENEACCESSOR +#define PX_PHYSICS_NP_SCENEACCESSOR + +#include "PxScene.h" + +namespace physx +{ + class PxsSimulationController; + + class NpSceneAccessor : public PxScene + { + + PX_NOCOPY(NpSceneAccessor) + + public: + NpSceneAccessor(){} + virtual ~NpSceneAccessor(){} + + virtual PxsSimulationController* getSimulationController() = 0; + virtual void setActiveActors(PxActor** actors, PxU32 nbActors) = 0; + virtual PxActor** getFrozenActors(PxU32& nbActorsOut) = 0; + virtual void setFrozenActorFlag(const bool buildFrozenActors) = 0; + + virtual void forceSceneQueryRebuild() = 0; + virtual void frameEnd() = 0; + + }; +} + +#endif + diff --git a/src/PhysX/physx/source/physx/src/NpSceneQueries.cpp b/src/PhysX/physx/source/physx/src/NpSceneQueries.cpp new file mode 100644 index 000000000..9cb098993 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpSceneQueries.cpp @@ -0,0 +1,850 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionRayBox.h" +#include "PxGeometryQuery.h" +#include "NpRigidDynamic.h" +#include "NpQueryShared.h" +#include "SqPruner.h" +#include "GuBounds.h" +#include "GuIntersectionRay.h" +#include "common/PxProfileZone.h" + +// Synchronous scene queries + +using namespace physx; +using namespace Sq; +using namespace Gu; + +#if PX_SUPPORT_PVD +#include "NpPvdSceneQueryCollector.h" +#endif + +namespace local +{ + // helper class to encapsulate Scb::Actor and Shape together with PxActorShape + struct ActorShape : PxActorShape + { + const Scb::Shape* scbShape; + const Scb::Actor* scbActor; + + ActorShape() : PxActorShape() {} + + ActorShape(PxRigidActor* eaActor, PxShape* eaShape, Scb::Shape* sShape, Scb::Actor* sActor) : PxActorShape(eaActor, eaShape) + { + scbShape = sShape; + scbActor = sActor; + } + }; + + // fill the helper actor shape + static PX_FORCE_INLINE void populate(const PrunerPayload& payload, ActorShape& as) + { + Scb::Shape* localShape = reinterpret_cast(payload.data[0]); + Scb::Actor* localActor = reinterpret_cast(payload.data[1]); + + as.scbShape = localShape; + as.scbActor = localActor; + + as.actor = static_cast(static_cast(localActor->getActorCore()).getPxActor()); + as.shape = localShape->getScShape().getPxShape(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +bool NpSceneQueries::raycast( + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxHitCallback& hits, PxHitFlags hitFlags, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache) const +{ + PX_PROFILE_ZONE("SceneQuery.raycast", getContextId()); + NP_READ_CHECK(this); + PX_SIMD_GUARD; + + MultiQueryInput input(origin, unitDir, distance); + return multiQuery(input, hits, hitFlags, cache, filterData, filterCall, NULL); +} + +////////////////////////////////////////////////////////////////////////// +bool NpSceneQueries::overlap( + const PxGeometry& geometry, const PxTransform& pose, PxOverlapCallback& hits, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall) const +{ + PX_PROFILE_ZONE("SceneQuery.overlap", getContextId()); + NP_READ_CHECK(this); + PX_SIMD_GUARD; + + MultiQueryInput input(&geometry, &pose); + // we are not supporting cache for overlaps for some reason + return multiQuery(input, hits, PxHitFlags(), NULL, filterData, filterCall, NULL); +} + +/////////////////////////////////////////////////////////////////////////////// +bool NpSceneQueries::sweep( + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxHitCallback& hits, PxHitFlags hitFlags, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache, const PxReal inflation) const +{ + PX_PROFILE_ZONE("SceneQuery.sweep", getContextId()); + NP_READ_CHECK(this); + PX_SIMD_GUARD; + +#if PX_CHECKED + if(!PxGeometryQuery::isValid(geometry)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry is not valid"); + return false; + } +#endif // PX_CHECKED + + + if((hitFlags & PxHitFlag::ePRECISE_SWEEP) && (hitFlags & PxHitFlag::eMTD)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support MTD. Perform MTD with default sweep"); + hitFlags &= ~PxHitFlag::ePRECISE_SWEEP; + } + + if((hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP) && (hitFlags & PxHitFlag::eMTD)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " eMTD cannot be used in conjunction with eASSUME_NO_INITIAL_OVERLAP. eASSUME_NO_INITIAL_OVERLAP will be ignored"); + hitFlags &= ~PxHitFlag::eASSUME_NO_INITIAL_OVERLAP; + } + + PxReal realInflation = inflation; + if((hitFlags & PxHitFlag::ePRECISE_SWEEP)&& inflation > 0.f) + { + realInflation = 0.f; + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support inflation, inflation will be overwritten to be zero"); + } + MultiQueryInput input(&geometry, &pose, unitDir, distance, realInflation); + return multiQuery(input, hits, hitFlags, cache, filterData, filterCall, NULL); +} + +/////////////////////////////////////////////////////////////////////////////// +//======================================================================================================================== + +static PX_FORCE_INLINE bool applyAllPreFiltersSQ( + const local::ActorShape* as, PxQueryHitType::Enum& hitType, const PxQueryFlags& inFilterFlags, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + BatchQueryFilterData* bfd, PxHitFlags& queryFlags/*, PxU32 maxNbTouches*/) +{ + // AP: the !bfd clause is here because there's no other way to pass data to BQ pre/post filter shaders + // For normal query the data can be passed with inherited callback instance + // So if for BQ SPU filter shader the user tries to pass data via FD, the equation will always cut it out + // AP scaffold TODO: once SPU is officially phased out we can remove the !bfd clause, fix broken UTs (that are wrong) + // and also remove support for filter shaders + if(!bfd && !applyFilterEquation(*as->scbShape, filterData.data)) + return false; + + if((inFilterFlags & PxQueryFlag::ePREFILTER) && (filterCall || bfd)) + { + PxHitFlags outQueryFlags = queryFlags; + + if(filterCall) + hitType = filterCall->preFilter(filterData.data, as->shape, as->actor, outQueryFlags); + else if(bfd->preFilterShader) + hitType = bfd->preFilterShader( + filterData.data, as->scbShape->getScShape().getQueryFilterData(), + bfd->filterShaderData, bfd->filterShaderDataSize, outQueryFlags); + + // AP: at this point the callback might return eTOUCH but the touch buffer can be empty, the hit will be discarded + //PX_CHECK_MSG(hitType == PxQueryHitType::eTOUCH ? maxNbTouches > 0 : true, + // "SceneQuery: preFilter returned eTOUCH but empty touch buffer was provided, hit discarded."); + + queryFlags = (queryFlags & ~PxHitFlag::eMODIFIABLE_FLAGS) | (outQueryFlags & PxHitFlag::eMODIFIABLE_FLAGS); + } + // test passed, continue to return as; + return true; +} + +//======================================================================================================================== +// performs a single geometry query for any HitType (PxSweepHit, PxOverlapHit, PxRaycastHit) +template +struct GeomQueryAny +{ + static PX_FORCE_INLINE PxU32 geomHit( + const NpSceneQueries& sceneQueries, const MultiQueryInput& input, const ShapeData& sd, + const PxGeometry& sceneGeom, const PxTransform& pose, PxHitFlags hitFlags, + PxU32 maxHits, HitType* hits, const PxReal shrunkMaxDistance, PxBounds3* precomputedBounds) + { + const PxGeometry& geom0 = *input.geometry; + const PxTransform& pose0 = *input.pose; + const PxGeometry& geom1 = sceneGeom; + const PxTransform& pose1 = pose; + + // Handle raycasts + if(HitTypeSupport::IsRaycast) + { + // the test for mesh AABB is archived in //sw/physx/dev/apokrovsky/graveyard/sqMeshAABBTest.cpp + // TODO: investigate performance impact (see US12801) + PX_CHECK_AND_RETURN_VAL(input.getDir().isFinite(), "PxScene::raycast(): rayDir is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(input.getOrigin().isFinite(), "PxScene::raycast(): rayOrigin is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "PxScene::raycast(): pose is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(shrunkMaxDistance >= 0.0f, "PxScene::raycast(): maxDist is negative.", 0); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(shrunkMaxDistance), "PxScene::raycast(): maxDist is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(PxAbs(input.getDir().magnitudeSquared()-1)<1e-4f, + "PxScene::raycast(): ray direction must be unit vector.", 0); + + // PT: TODO: investigate perf difference + const RaycastFunc func = sceneQueries.mCachedRaycastFuncs[geom1.getType()]; + return func(geom1, pose1, input.getOrigin(), input.getDir(), shrunkMaxDistance, + hitFlags, maxHits, reinterpret_cast(hits)); + } + // Handle sweeps + else if(HitTypeSupport::IsSweep) + { + PX_ASSERT(precomputedBounds != NULL); + // b0 = query shape bounds + // b1 = scene shape bounds + // AP: Here we clip the sweep to bounds with sum of extents. This is needed for GJK stability. + // because sweep is equivalent to a raycast vs a scene shape with inflated bounds. + // This also may (or may not) provide an optimization for meshes because top level of rtree has multiple boxes + // and there is no bounds test for the whole mesh elsewhere + PxBounds3 b0 = *precomputedBounds, b1; + // compute the scene geometry bounds + // PT: TODO: avoid recomputing the bounds here + Gu::computeBounds(b1, sceneGeom, pose, 0.0f, NULL, 1.0f); + const PxVec3 combExt = (b0.getExtents() + b1.getExtents())*1.01f; + + PxF32 tnear, tfar; + if(!intersectRayAABB2(-combExt, combExt, b0.getCenter() - b1.getCenter(), input.getDir(), shrunkMaxDistance, tnear, tfar)) // returns (tneartfar) // this second test is needed because shrunkMaxDistance can be 0 for 0 length sweep + return 0; + PX_ASSERT(input.getDir().isNormalized()); + // tfar is now the t where the ray exits the AABB. input.getDir() is normalized + + const PxVec3& unitDir = input.getDir(); + PxSweepHit& sweepHit = reinterpret_cast(hits[0]); + + // if we don't start inside the AABB box, offset the start pos, because of precision issues with large maxDist + const bool offsetPos = (tnear > GU_RAY_SURFACE_OFFSET); + const PxReal offset = offsetPos ? (tnear - GU_RAY_SURFACE_OFFSET) : 0.0f; + const PxVec3 offsetVec(offsetPos ? (unitDir*offset) : PxVec3(0.0f)); + // we move the geometry we sweep against, so that we avoid the Gu::Capsule/Box recomputation + const PxTransform pose1Offset(pose1.p - offsetVec, pose1.q); + + const PxReal distance = PxMin(tfar, shrunkMaxDistance) - offset; + const PxReal inflation = input.inflation; + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "PxScene::sweep(): pose0 is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(pose1Offset.isValid(), "PxScene::sweep(): pose1 is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "PxScene::sweep(): unitDir is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(distance), "PxScene::sweep(): distance is not valid.", 0); + PX_CHECK_AND_RETURN_VAL((distance >= 0.0f && !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) || distance > 0.0f, + "PxScene::sweep(): sweep distance must be >=0 or >0 with eASSUME_NO_INITIAL_OVERLAP.", 0); + + PxU32 retVal = 0; + const GeomSweepFuncs& sf = sceneQueries.mCachedSweepFuncs; + switch(geom0.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast(geom0); + // PT: TODO: technically this capsule with 0.0 half-height is invalid ("isValid" returns false) + const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f); + const Capsule worldCapsule(pose0.p, pose0.p, sphereGeom.radius); // AP: precompute? + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()]; + retVal = PxU32(func(geom1, pose1Offset, capsuleGeom, pose0, worldCapsule, unitDir, distance, sweepHit, hitFlags, inflation)); + } + break; + + case PxGeometryType::eCAPSULE: + { + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()]; + retVal = PxU32(func(geom1, pose1Offset, static_cast(geom0), pose0, sd.getGuCapsule(), unitDir, distance, sweepHit, hitFlags, inflation)); + } + break; + + case PxGeometryType::eBOX: + { + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepBoxFunc func = precise ? sf.preciseBoxMap[geom1.getType()] : sf.boxMap[geom1.getType()]; + retVal = PxU32(func(geom1, pose1Offset, static_cast(geom0), pose0, sd.getGuBox(), unitDir, distance, sweepHit, hitFlags, inflation)); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + const SweepConvexFunc func = sf.convexMap[geom1.getType()]; + retVal = PxU32(func(geom1, pose1Offset, convexGeom, pose0, unitDir, distance, sweepHit, hitFlags, inflation)); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxScene::sweep(): first geometry object parameter must be sphere, capsule, box or convex geometry."); + break; + } + if (retVal) + { + // we need to offset the distance back + sweepHit.distance += offset; + // we need to offset the hit position back as we moved the geometry we sweep against + sweepHit.position += offsetVec; + } + return retVal; + } + // Handle overlaps + else if(HitTypeSupport::IsOverlap) + { + const GeomOverlapTable* overlapFuncs = sceneQueries.mCachedOverlapFuncs; + return PxU32(Gu::overlap(geom0, pose0, geom1, pose1, overlapFuncs)); + } + else + { + PX_ALWAYS_ASSERT_MESSAGE("Unexpected template expansion in GeomQueryAny::geomHit"); + return 0; + } + } +}; + +// struct to access protected data members in the public PxHitCallback API +template +struct MultiQueryCallback : public PrunerCallback +{ + const NpSceneQueries& mScene; + const MultiQueryInput& mInput; + PxHitCallback& mHitCall; + const PxHitFlags mHitFlags; + const PxQueryFilterData& mFilterData; + PxQueryFilterCallback* mFilterCall; + PxReal mShrunkDistance; + BatchQueryFilterData* mBfd; // only not NULL for batch queries + const PxHitFlags mMeshAnyHitFlags; + bool mReportTouchesAgain; + bool mFarBlockFound; // this is to prevent repeated searches for far block + bool mNoBlock; + const bool mAnyHit; + bool mIsCached; // is this call coming as a callback from the pruner or a single item cached callback? + + // The reason we need these bounds is because we need to know combined(inflated shape) bounds to clip the sweep path + // to be tolerable by GJK precision issues. This test is done for (queryShape vs touchedShapes) + // So it makes sense to cache the bounds for sweep query shape, otherwise we'd have to recompute them every time + // Currently only used for sweeps. + PxBounds3 mQueryShapeBounds; + bool mQueryShapeBoundsValid; + const ShapeData* mShapeData; + + MultiQueryCallback( + const NpSceneQueries& scene, const MultiQueryInput& input, bool anyHit, PxHitCallback& hitCall, PxHitFlags hitFlags, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, PxReal shrunkDistance, BatchQueryFilterData* aBfd) : + mScene (scene), + mInput (input), + mHitCall (hitCall), + mHitFlags (hitFlags), + mFilterData (filterData), + mFilterCall (filterCall), + mShrunkDistance (shrunkDistance), + mBfd (aBfd), + mMeshAnyHitFlags ((hitFlags.isSet(PxHitFlag::eMESH_ANY) || anyHit) ? PxHitFlag::eMESH_ANY : PxHitFlag::Enum(0)), + mReportTouchesAgain (true), + mFarBlockFound (filterData.flags & PxQueryFlag::eNO_BLOCK), + mNoBlock (filterData.flags & PxQueryFlag::eNO_BLOCK), + mAnyHit (anyHit), + mIsCached (false), + mQueryShapeBoundsValid (false), + mShapeData (NULL) + { + } + + virtual PxAgain invoke(PxReal& aDist, const PrunerPayload& aPayload) + { + const PxU32 tempCount = 1; + HitType tempBuf[tempCount]; + + // PT: TODO: do we need actorShape.actor/actorShape.shape immediately? + local::ActorShape actorShape; + local::populate(aPayload, actorShape); + + const PxQueryFlags filterFlags = mFilterData.flags; + + // for no filter callback, default to eTOUCH for MULTIPLE, eBLOCK otherwise + // also always treat as eBLOCK if currently tested shape is cached + // Using eRESERVED flag as a special condition to default to eTOUCH hits while only looking for a single blocking hit + // from a nested query (see other comments containing #LABEL1) + PxQueryHitType::Enum shapeHitType = + ((mHitCall.maxNbTouches || (mFilterData.flags & PxQueryFlag::eRESERVED)) && !mIsCached) + ? PxQueryHitType::eTOUCH + : PxQueryHitType::eBLOCK; + + // apply pre-filter + PxHitFlags filteredHitFlags = mHitFlags; + if(!mIsCached) // don't run filters on single item cache + if(!applyAllPreFiltersSQ(&actorShape, shapeHitType/*in&out*/, filterFlags, mFilterData, mFilterCall, + mBfd, filteredHitFlags/*, mHitCall.maxNbTouches*/)) + return true; // skip this shape from reporting if prefilter said to do so + if(shapeHitType == PxQueryHitType::eNONE) + return true; + + PX_ASSERT(actorShape.actor && actorShape.shape); + const Scb::Shape* shape = actorShape.scbShape; + const Scb::Actor* actor = actorShape.scbActor; + + // compute the global pose for the cached shape and actor + PX_ALIGN(16, PxTransform) globalPose; + NpActor::getGlobalPose(globalPose, *shape, *actor); + + const PxGeometry& shapeGeom = shape->getGeometry(); + + // Here we decide whether to use the user provided buffer in place or a local stack buffer + // see if we have more room left in the callback results buffer than in the parent stack buffer + // if so get subHits in-place in the hit buffer instead of the parent stack buffer + // nbTouches is the number of accumulated touch hits so far + // maxNbTouches is the size of the user buffer + PxU32 maxSubHits1 = mHitCall.maxNbTouches - mHitCall.nbTouches; // how much room is left in the user buffer + HitType* subHits1 = mHitCall.touches + mHitCall.nbTouches; // pointer to the first free hit in the user buffer + if(mHitCall.nbTouches >= mHitCall.maxNbTouches) + // if there's no room left in the user buffer, use a stack buffer + { + // tried using 64 here - causes check stack code to get generated on xbox, perhaps because of guard page + // need this buffer in case the input buffer is full but we still want to correctly merge results from later hits + maxSubHits1 = tempCount; + subHits1 = reinterpret_cast(tempBuf); + } + + // limit number of hits to 1 for meshes if eMESH_MULTIPLE wasn't specified. this tells geomQuery to only look for a closest hit + if(shapeGeom.getType() == PxGeometryType::eTRIANGLEMESH && !(filteredHitFlags & PxHitFlag::eMESH_MULTIPLE)) + maxSubHits1 = 1; // required to only receive 1 hit to pass UTs + // call the geometry specific intersection template + PxU32 nbSubHits = GeomQueryAny::geomHit( + mScene, mInput, *mShapeData, shapeGeom, globalPose, + filteredHitFlags | mMeshAnyHitFlags, + maxSubHits1, subHits1, mShrunkDistance, mQueryShapeBoundsValid ? &mQueryShapeBounds : NULL); + + // ------------------------- iterate over geometry subhits ----------------------------------- + for (PxU32 iSubHit = 0; iSubHit < nbSubHits; iSubHit++) + { + HitType& hit = subHits1[iSubHit]; + hit.actor = actorShape.actor; + hit.shape = actorShape.shape; + + // some additional processing only for sweep hits with initial overlap + if(HitTypeSupport::IsSweep && HITDIST(hit) == 0.0f && !(filteredHitFlags & PxHitFlag::eMTD)) + // PT: necessary as some leaf routines are called with reversed params, thus writing +unitDir there. + // AP: apparently still necessary to also do in Gu because Gu can be used standalone (without SQ) + reinterpret_cast(hit).normal = -mInput.getDir(); + + // start out with hitType for this cached shape set to a pre-filtered hit type + PxQueryHitType::Enum hitType = shapeHitType; + + // run the post-filter if specified in filterFlags and filterCall is non-NULL + if(!mIsCached && (mFilterCall || mBfd) && (filterFlags & PxQueryFlag::ePOSTFILTER)) + { + if(mFilterCall) + hitType = mFilterCall->postFilter(mFilterData.data, hit); + else if(mBfd->postFilterShader) + hitType = mBfd->postFilterShader( + mFilterData.data, actorShape.scbShape->getScShape().getQueryFilterData(), + mBfd->filterShaderData, mBfd->filterShaderDataSize, hit); + } + + // early out on any hit if eANY_HIT was specified, regardless of hit type + if(mAnyHit && hitType != PxQueryHitType::eNONE) + { + // block or touch qualifies for qType=ANY type hit => return it as blocking according to spec. Ignore eNONE. + mHitCall.block = hit; + mHitCall.hasBlock = true; + return false; // found a hit for ANY qType, can early exit now + } + + if(mNoBlock) + hitType = PxQueryHitType::eTOUCH; + + PX_WARN_ONCE_IF(HitTypeSupport::IsOverlap && hitType == PxQueryHitType::eBLOCK, + "eBLOCK returned from user filter for overlap() query. This may cause undesired behavior. " + "Consider using PxQueryFlag::eNO_BLOCK for overlap queries."); + + if(hitType == PxQueryHitType::eTOUCH) + { + // -------------------------- handle eTOUCH hits --------------------------------- + // for qType=multiple, store the hit. For other qTypes ignore it. + // <= is important for initially overlapping sweeps + #if PX_CHECKED + if(mHitCall.maxNbTouches == 0 && !mBfd && !mFilterData.flags.isSet(PxQueryFlag::eRESERVED)) + // issue a warning if eTOUCH was returned by the prefilter, we have 0 touch buffer and not a batch query + // not doing for BQ because the touches buffer can be overflown and thats ok by spec + // eRESERVED to avoid a warning from nested callback (closest blocking hit recursive search) + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "User filter returned PxQueryHitType::eTOUCH but the touches buffer was empty. Hit was discarded."); + #endif + + if(mHitCall.maxNbTouches && mReportTouchesAgain && HITDIST(hit) <= mShrunkDistance) + { + // Buffer full: need to find the closest blocking hit, clip touch hits and flush the buffer + if(mHitCall.nbTouches == mHitCall.maxNbTouches) + { + // issue a second nested query just looking for the closest blocking hit + // could do better perf-wise by saving traversal state (start looking for blocking from this point) + // but this is not a perf critical case because users can provide a bigger buffer + // that covers non-degenerate cases + // far block search doesn't apply to overlaps because overlaps don't work with blocking hits + if(HitTypeSupport::IsOverlap == 0) + { + // AP: the use of eRESERVED is a bit tricky, see other comments containing #LABEL1 + PxQueryFilterData fd1 = mFilterData; fd1.flags |= PxQueryFlag::eRESERVED; + PxHitBuffer buf1; // create a temp callback buffer for a single blocking hit + if(!mFarBlockFound && mHitCall.maxNbTouches > 0 && mScene.NpSceneQueries::multiQuery( + mInput, buf1, mHitFlags, NULL, fd1, mFilterCall, mBfd)) + { + mHitCall.block = buf1.block; + mHitCall.hasBlock = true; + mHitCall.nbTouches = + clipHitsToNewMaxDist(mHitCall.touches, mHitCall.nbTouches, HITDIST(buf1.block)); + mShrunkDistance = HITDIST(buf1.block); + aDist = mShrunkDistance; + } + mFarBlockFound = true; + } + if(mHitCall.nbTouches == mHitCall.maxNbTouches) + { + mReportTouchesAgain = mHitCall.processTouches(mHitCall.touches, mHitCall.nbTouches); + if(!mReportTouchesAgain) + return false; // optimization - buffer is full + else + mHitCall.nbTouches = 0; // reset nbTouches so we can continue accumulating again + } + } + + //if(hitCall.nbTouches < hitCall.maxNbTouches) // can be true if maxNbTouches is 0 + mHitCall.touches[mHitCall.nbTouches++] = hit; + } // if(hitCall.maxNbTouches && reportTouchesAgain && HITDIST(hit) <= shrunkDistance) + } // if(hitType == PxQueryHitType::eTOUCH) + else if(hitType == PxQueryHitType::eBLOCK) + { + // -------------------------- handle eBLOCK hits ---------------------------------- + // only eBLOCK qualifies as a closest hit candidate => compare against best distance and store + // <= is needed for eTOUCH hits to be recorded correctly vs same eBLOCK distance for overlaps + if(HITDIST(hit) <= mShrunkDistance) + { + if(HitTypeSupport::IsOverlap == 0) + { + mShrunkDistance = HITDIST(hit); + aDist = mShrunkDistance; + } + mHitCall.block = hit; + mHitCall.hasBlock = true; + } + } // if(hitType == eBLOCK) + else { + PX_ASSERT(hitType == PxQueryHitType::eNONE); + } + } // for iSubHit + return true; + } + +private: + MultiQueryCallback& operator=(const MultiQueryCallback&); +}; + +//======================================================================================================================== +#if PX_SUPPORT_PVD +template +struct CapturePvdOnReturn : public PxHitCallback +{ + // copy the arguments of multiQuery into a struct, this is strictly for PVD recording + const NpSceneQueries* mSQ; + const MultiQueryInput& mInput; + PxHitFlags mHitFlags; // PT: TODO: this is not used! + const PxQueryCache* mCache; // PT: TODO: this is not used! + const PxQueryFilterData& mFilterData; + PxQueryFilterCallback* mFilterCall; // PT: TODO: this is not used! + BatchQueryFilterData* mBFD; // PT: TODO: check if this is sometimes not NULL + Ps::Array mAllHits; + PxHitCallback& mParentCallback; + + CapturePvdOnReturn( + const NpSceneQueries* sq, const MultiQueryInput& input, PxHitFlags hitFlags, + const PxQueryCache* cache, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + BatchQueryFilterData* bfd, PxHitCallback& parentCallback) : + PxHitCallback (parentCallback.touches, parentCallback.maxNbTouches), + mSQ (sq), + mInput (input), + mHitFlags (hitFlags), + mCache (cache), + mFilterData (filterData), + mFilterCall (filterCall), + mBFD (bfd), + mParentCallback (parentCallback) + {} + + virtual PxAgain processTouches(const HitType* hits, PxU32 nbHits) + { + const PxAgain again = mParentCallback.processTouches(hits, nbHits); + for(PxU32 i=0; igetScene().getScenePvdClient(); + if(!(pvdClient.checkPvdDebugFlag() && (pvdClient.getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES))) + return; + + physx::Vd::PvdSceneQueryCollector& collector = mBFD ? mSQ->getBatchedSqCollector() : mSQ->getSingleSqCollector(); + + if(mParentCallback.nbTouches) + { + for(PxU32 i = 0; i < mParentCallback.nbTouches; i++) + mAllHits.pushBack(mParentCallback.touches[i]); + } + + if(mParentCallback.hasBlock) + mAllHits.pushBack(mParentCallback.block); + + // PT: TODO: why do we need reinterpret_casts below? + if(HitTypeSupport::IsRaycast) + collector.raycast (mInput.getOrigin(), mInput.getDir(), mInput.maxDistance, reinterpret_cast(mAllHits.begin()), mAllHits.size(), mFilterData, this->maxNbTouches!=0); + else if(HitTypeSupport::IsOverlap) + collector.overlapMultiple (*mInput.geometry, *mInput.pose, reinterpret_cast(mAllHits.begin()), mAllHits.size(), mFilterData); + else if(HitTypeSupport::IsSweep) + collector.sweep (*mInput.geometry, *mInput.pose, mInput.getDir(), mInput.maxDistance, reinterpret_cast(mAllHits.begin()), mAllHits.size(), mFilterData, this->maxNbTouches!=0); + } + +private: + CapturePvdOnReturn& operator=(const CapturePvdOnReturn&); +}; +#endif // PX_SUPPORT_PVD + +//======================================================================================================================== +template +struct IssueCallbacksOnReturn +{ + PxHitCallback& hits; + PxAgain again; // query was stopped by previous processTouches. This means that nbTouches is still non-zero + // but we don't need to issue processTouches again + PX_FORCE_INLINE IssueCallbacksOnReturn(PxHitCallback& aHits) : hits(aHits) + { + again = true; + } + + ~IssueCallbacksOnReturn() + { + if(again) + // only issue processTouches if query wasn't stopped + // this is because nbTouches doesn't get reset to 0 in this case (according to spec) + // and the touches in touches array were already processed by the callback + { + if(hits.hasBlock && hits.nbTouches) + hits.nbTouches = clipHitsToNewMaxDist(hits.touches, hits.nbTouches, HITDIST(hits.block)); + if(hits.nbTouches) + { + bool again_ = hits.processTouches(hits.touches, hits.nbTouches); + if(again_) + hits.nbTouches = 0; + } + } + hits.finalizeQuery(); + } + +private: + IssueCallbacksOnReturn& operator=(const IssueCallbacksOnReturn&); +}; + +#undef HITDIST + +//======================================================================================================================== +template +bool NpSceneQueries::multiQuery( + const MultiQueryInput& input, PxHitCallback& hits, PxHitFlags hitFlags, const PxQueryCache* cache, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, BatchQueryFilterData* bfd) const +{ + const bool anyHit = (filterData.flags & PxQueryFlag::eANY_HIT) == PxQueryFlag::eANY_HIT; + + PxI32 retval = 0; PX_UNUSED(retval); + + if(HitTypeSupport::IsRaycast == 0) + { + PX_CHECK_AND_RETURN_VAL(input.pose != NULL, "NpSceneQueries::overlap/sweep pose is NULL.", 0); + PX_CHECK_AND_RETURN_VAL(input.pose->isValid(), "NpSceneQueries::overlap/sweep pose is not valid.", 0); + } + else + { + PX_CHECK_AND_RETURN_VAL(input.getOrigin().isFinite(), "NpSceneQueries::raycast pose is not valid.", 0); + } + + if(HitTypeSupport::IsOverlap == 0) + { + PX_CHECK_AND_RETURN_VAL(input.getDir().isFinite(), "NpSceneQueries multiQuery input check: unitDir is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(input.getDir().isNormalized(), "NpSceneQueries multiQuery input check: direction must be normalized", 0); + } + + if(HitTypeSupport::IsRaycast) + { + PX_CHECK_AND_RETURN_VAL(input.maxDistance > 0.0f, "NpSceneQueries::multiQuery input check: distance cannot be negative or zero", 0); + } + + if(HitTypeSupport::IsOverlap && !anyHit) + { + PX_CHECK_AND_RETURN_VAL(hits.maxNbTouches > 0, "PxScene::overlap() and PxBatchQuery::overlap() calls without eANY_HIT flag require a touch hit buffer for return results.", 0); + } + + if(HitTypeSupport::IsSweep) + { + PX_CHECK_AND_RETURN_VAL(input.maxDistance >= 0.0f, "NpSceneQueries multiQuery input check: distance cannot be negative", 0); + PX_CHECK_AND_RETURN_VAL(input.maxDistance != 0.0f || !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP), + "NpSceneQueries multiQuery input check: zero-length sweep only valid without the PxHitFlag::eASSUME_NO_INITIAL_OVERLAP flag", 0); + } + + PX_CHECK_MSG(!cache || (cache && cache->shape && cache->actor), "Raycast cache specified but shape or actor pointer is NULL!"); + PxU32 cachedCompoundId = INVALID_PRUNERHANDLE; + const PrunerData cacheData = cache ? NpActor::getShapeManager(*cache->actor)->findSceneQueryData(*static_cast(cache->shape), cachedCompoundId) : SQ_INVALID_PRUNER_DATA; + + // this function is logically const for the SDK user, as flushUpdates() will not have an API-visible effect on this object + // internally however, flushUpdates() changes the states of the Pruners in mSQManager + // because here is the only place we need this, const_cast instead of making SQM mutable + const_cast(this)->mSQManager.flushUpdates(); + +#if PX_SUPPORT_PVD + CapturePvdOnReturn pvdCapture(this, input, hitFlags, cache, filterData, filterCall, bfd, hits); +#endif + + IssueCallbacksOnReturn cbr(hits); // destructor will execute callbacks on return from this function + hits.hasBlock = false; + hits.nbTouches = 0; + + PxReal shrunkDistance = HitTypeSupport::IsOverlap ? PX_MAX_REAL : input.maxDistance; // can be progressively shrunk as we go over the list of shapes + if(HitTypeSupport::IsSweep) + shrunkDistance = PxMin(shrunkDistance, PX_MAX_SWEEP_DISTANCE); + MultiQueryCallback pcb(*this, input, anyHit, hits, hitFlags, filterData, filterCall, shrunkDistance, bfd); + + if(cacheData!=SQ_INVALID_PRUNER_DATA && hits.maxNbTouches == 0) // don't use cache for queries that can return touch hits + { + // this block is only executed for single shape cache + const PrunerPayload& cachedPayload = mSQManager.getPayload(cachedCompoundId, cacheData); + pcb.mIsCached = true; + PxReal dummyDist; + + PxAgain againAfterCache; + if(HitTypeSupport::IsSweep) + { + // AP: for sweeps we cache the bounds because we need to know them for the test to clip the sweep to bounds + // otherwise GJK becomes unstable. The bounds can be used multiple times so this is an optimization. + const ShapeData sd(*input.geometry, *input.pose, input.inflation); + pcb.mQueryShapeBounds = sd.getPrunerInflatedWorldAABB(); + pcb.mQueryShapeBoundsValid = true; + pcb.mShapeData = &sd; + againAfterCache = pcb.invoke(dummyDist, cachedPayload); + pcb.mShapeData = NULL; + } else + againAfterCache = pcb.invoke(dummyDist, cachedPayload); + pcb.mIsCached = false; + if(!againAfterCache) // if PxAgain result for cached shape was false (abort query), return here + return hits.hasAnyHits(); + } + + const Pruner* staticPruner = mSQManager.get(PruningIndex::eSTATIC).pruner(); + const Pruner* dynamicPruner = mSQManager.get(PruningIndex::eDYNAMIC).pruner(); + const CompoundPruner* compoundPruner = mSQManager.getCompoundPruner().pruner(); + + const PxU32 doStatics = filterData.flags & PxQueryFlag::eSTATIC; + const PxU32 doDynamics = filterData.flags & PxQueryFlag::eDYNAMIC; + + if(HitTypeSupport::IsRaycast) + { + bool again = doStatics ? staticPruner->raycast(input.getOrigin(), input.getDir(), pcb.mShrunkDistance, pcb) : true; + if(!again) + return hits.hasAnyHits(); + + if(doDynamics) + again = dynamicPruner->raycast(input.getOrigin(), input.getDir(), pcb.mShrunkDistance, pcb); + + if(again) + again = compoundPruner->raycast(input.getOrigin(), input.getDir(), pcb.mShrunkDistance, pcb, filterData.flags); + + cbr.again = again; // update the status to avoid duplicate processTouches() + return hits.hasAnyHits(); + } + else if(HitTypeSupport::IsOverlap) + { + PX_ASSERT(input.geometry); + + const ShapeData sd(*input.geometry, *input.pose, input.inflation); + pcb.mShapeData = &sd; + PxAgain again = doStatics ? staticPruner->overlap(sd, pcb) : true; + if(!again) // && (filterData.flags & PxQueryFlag::eANY_HIT)) + return hits.hasAnyHits(); + + if(doDynamics) + again = dynamicPruner->overlap(sd, pcb); + + if(again) + again = compoundPruner->overlap(sd, pcb, filterData.flags); + + cbr.again = again; // update the status to avoid duplicate processTouches() + return hits.hasAnyHits(); + } + else + { + PX_ASSERT(HitTypeSupport::IsSweep); + PX_ASSERT(input.geometry); + + const ShapeData sd(*input.geometry, *input.pose, input.inflation); + pcb.mQueryShapeBounds = sd.getPrunerInflatedWorldAABB(); + pcb.mQueryShapeBoundsValid = true; + pcb.mShapeData = &sd; + PxAgain again = doStatics ? staticPruner->sweep(sd, input.getDir(), pcb.mShrunkDistance, pcb) : true; + if(!again) + return hits.hasAnyHits(); + + if(doDynamics) + again = dynamicPruner->sweep(sd, input.getDir(), pcb.mShrunkDistance, pcb); + + if(again) + again = compoundPruner->sweep(sd, input.getDir(), pcb.mShrunkDistance, pcb, filterData.flags); + + cbr.again = again; // update the status to avoid duplicate processTouches() + return hits.hasAnyHits(); + } +} + +void NpSceneQueries::sceneQueriesStaticPrunerUpdate(PxBaseTask* ) +{ + PX_PROFILE_ZONE("SceneQuery.sceneQueriesStaticPrunerUpdate", getContextId()); + // run pruner build only, this will build the new tree only, no commit happens + mSQManager.sceneQueryBuildStep(PruningIndex::eSTATIC); +} + +void NpSceneQueries::sceneQueriesDynamicPrunerUpdate(PxBaseTask*) +{ + PX_PROFILE_ZONE("SceneQuery.sceneQueriesDynamicPrunerUpdate", getContextId()); + // run pruner build only, this will build the new tree only, no commit happens + mSQManager.sceneQueryBuildStep(PruningIndex::eDYNAMIC); +} + +//explicit template instantiation +template bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; +template bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; +template bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; + diff --git a/src/PhysX/physx/source/physx/src/NpSceneQueries.h b/src/PhysX/physx/source/physx/src/NpSceneQueries.h new file mode 100644 index 000000000..e07fcfd73 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpSceneQueries.h @@ -0,0 +1,236 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NP_SCENEQUERIES +#define PX_PHYSICS_NP_SCENEQUERIES + + +#include "PxQueryReport.h" +#include "PsIntrinsics.h" +#include "CmPhysXCommon.h" +#include "SqSceneQueryManager.h" +#include "GuTriangleMesh.h" +#include "GuRaycastTests.h" +#include "GuSweepTests.h" +#include "GuOverlapTests.h" +#include "NpSceneAccessor.h" +#include "ScbScene.h" + +#if PX_SUPPORT_PVD +#include "NpPvdSceneQueryCollector.h" +#endif + +namespace physx { namespace Sq { + + struct QueryID { enum Enum { + QUERY_RAYCAST_ANY_OBJECT, + QUERY_RAYCAST_CLOSEST_OBJECT, + QUERY_RAYCAST_ALL_OBJECTS, + + QUERY_OVERLAP_SPHERE_ALL_OBJECTS, + QUERY_OVERLAP_AABB_ALL_OBJECTS, + QUERY_OVERLAP_OBB_ALL_OBJECTS, + QUERY_OVERLAP_CAPSULE_ALL_OBJECTS, + QUERY_OVERLAP_CONVEX_ALL_OBJECTS, + + QUERY_LINEAR_OBB_SWEEP_CLOSEST_OBJECT, + QUERY_LINEAR_CAPSULE_SWEEP_CLOSEST_OBJECT, + QUERY_LINEAR_CONVEX_SWEEP_CLOSEST_OBJECT + }; }; +} + +struct MultiQueryInput +{ + const PxVec3* rayOrigin; // only valid for raycasts + const PxVec3* unitDir; // only valid for raycasts and sweeps + PxReal maxDistance; // only valid for raycasts and sweeps + const PxGeometry* geometry; // only valid for overlaps and sweeps + const PxTransform* pose; // only valid for overlaps and sweeps + PxReal inflation; // only valid for sweeps + + // Raycast constructor + MultiQueryInput(const PxVec3& aRayOrigin, const PxVec3& aUnitDir, PxReal aMaxDist) + { + Ps::prefetchLine(&aRayOrigin); + Ps::prefetchLine(&aUnitDir); + rayOrigin = &aRayOrigin; + unitDir = &aUnitDir; + maxDistance = aMaxDist; + geometry = NULL; + pose = NULL; + inflation = 0.0f; + } + + // Overlap constructor + MultiQueryInput(const PxGeometry* aGeometry, const PxTransform* aPose) + { + Ps::prefetchLine(aGeometry); + Ps::prefetchLine(aPose); + geometry = aGeometry; + pose = aPose; + inflation = 0.0f; + rayOrigin = unitDir = NULL; + } + + // Sweep constructor + MultiQueryInput( + const PxGeometry* aGeometry, const PxTransform* aPose, + const PxVec3& aUnitDir, const PxReal aMaxDist, const PxReal aInflation) + { + Ps::prefetchLine(aGeometry); + Ps::prefetchLine(aPose); + Ps::prefetchLine(&aUnitDir); + rayOrigin = NULL; + maxDistance = aMaxDist; + unitDir = &aUnitDir; + geometry = aGeometry; + pose = aPose; + inflation = aInflation; + } + + PX_FORCE_INLINE const PxVec3& getDir() const { PX_ASSERT(unitDir); return *unitDir; } + PX_FORCE_INLINE const PxVec3& getOrigin() const { PX_ASSERT(rayOrigin); return *rayOrigin; } +}; + +struct BatchQueryFilterData +{ + void* filterShaderData; + PxU32 filterShaderDataSize; + PxBatchQueryPreFilterShader preFilterShader; + PxBatchQueryPostFilterShader postFilterShader; + #if PX_SUPPORT_PVD + Vd::PvdSceneQueryCollector* collector; // gets set to bq collector + #endif + BatchQueryFilterData(void* fsData, PxU32 fsSize, PxBatchQueryPreFilterShader preFs, PxBatchQueryPostFilterShader postFs) + : filterShaderData(fsData), filterShaderDataSize(fsSize), preFilterShader(preFs), postFilterShader(postFs) + { + #if PX_SUPPORT_PVD + collector = NULL; + #endif + } +}; + +class PxGeometry; + +class NpSceneQueries : public NpSceneAccessor +{ + PX_NOCOPY(NpSceneQueries) + +public: + NpSceneQueries(const PxSceneDesc& desc); + ~NpSceneQueries(); + + template + bool multiQuery( + const MultiQueryInput& in, + PxHitCallback& hits, PxHitFlags hitFlags, const PxQueryCache* cache, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + BatchQueryFilterData* bqFd) const; + + // Synchronous scene queries + virtual bool raycast( + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, // Ray data + PxRaycastCallback& hitCall, PxHitFlags hitFlags, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache) const; + + virtual bool sweep( + const PxGeometry& geometry, const PxTransform& pose, // GeomObject data + const PxVec3& unitDir, const PxReal distance, // Ray data + PxSweepCallback& hitCall, PxHitFlags hitFlags, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache, const PxReal inflation) const; + + virtual bool overlap( + const PxGeometry& geometry, const PxTransform& transform, // GeomObject data + PxOverlapCallback& hitCall, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall) const; + + PX_FORCE_INLINE PxU64 getContextId() const { return PxU64(reinterpret_cast(this)); } + PX_FORCE_INLINE Scb::Scene& getScene() { return mScene; } + PX_FORCE_INLINE const Scb::Scene& getScene() const { return mScene; } + PX_FORCE_INLINE PxU32 getFlagsFast() const { return mScene.getFlags(); } + PX_FORCE_INLINE Sq::SceneQueryManager& getSceneQueryManagerFast() { return mSQManager; } + PX_FORCE_INLINE PxSceneQueryUpdateMode::Enum getSceneQueryUpdateModeFast() const { return mSceneQueryUpdateMode; } + + void sceneQueriesStaticPrunerUpdate(PxBaseTask* continuation); + void sceneQueriesDynamicPrunerUpdate(PxBaseTask* continuation); + + Scb::Scene mScene; + + Sq::SceneQueryManager mSQManager; + + const Gu::GeomRaycastTable& mCachedRaycastFuncs; + const Gu::GeomSweepFuncs& mCachedSweepFuncs; + const Gu::GeomOverlapTable* mCachedOverlapFuncs; + + typedef Cm::DelegateTask SceneQueriesStaticPrunerUpdate; + typedef Cm::DelegateTask SceneQueriesDynamicPrunerUpdate; + SceneQueriesStaticPrunerUpdate mSceneQueriesStaticPrunerUpdate; + SceneQueriesDynamicPrunerUpdate mSceneQueriesDynamicPrunerUpdate; + + PxSceneQueryUpdateMode::Enum mSceneQueryUpdateMode; + +#if PX_SUPPORT_PVD +public: + //Scene query and hits for pvd, collected in current frame + mutable Vd::PvdSceneQueryCollector mSingleSqCollector; + mutable Vd::PvdSceneQueryCollector mBatchedSqCollector; + +PX_FORCE_INLINE Vd::PvdSceneQueryCollector& getSingleSqCollector() const {return mSingleSqCollector;} +PX_FORCE_INLINE Vd::PvdSceneQueryCollector& getBatchedSqCollector() const {return mBatchedSqCollector;} +#endif // PX_SUPPORT_PVD +}; + +#if PX_SUPPORT_EXTERN_TEMPLATE +//explicit template instantiation declaration +extern template +bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; + +extern template +bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; + +extern template +bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; +#endif + +namespace Sq { class AABBPruner; class AABBTreeRuntimeNode; class AABBTree; } + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +#if PX_VC + #pragma warning(pop) +#endif + +} // namespace physx, sq + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp b/src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp new file mode 100644 index 000000000..eb48292b2 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp @@ -0,0 +1,182 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuHeightField.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" +#include "GuTriangleMeshRTree.h" + +#include "NpRigidStatic.h" +#include "NpRigidDynamic.h" +#include "NpArticulation.h" +#include "NpArticulationLink.h" +#include "NpArticulationJoint.h" +#include "NpMaterial.h" +#include "NpAggregate.h" +#include "GuHeightFieldData.h" + +#include "SqPruningStructure.h" + +#include "PxBase.h" +#include "PxSerialFramework.h" +#include "PxSerializer.h" +#include "PxPhysicsSerialization.h" + +namespace physx +{ + using namespace physx::Gu; + + template<> + void PxSerializerDefaultAdapter::exportData(PxBase& obj, PxSerializationContext& s) const + { + PxU32 classSize = sizeof(NpRigidDynamic); + NpRigidDynamic& dynamic = static_cast(obj); + + PxsBodyCore serialCore; + size_t address = dynamic.getScbBodyFast().getScBody().getSerialCore(serialCore); + PxU32 offset = PxU32(address - reinterpret_cast(&dynamic)); + PX_ASSERT(offset + sizeof(serialCore) <= classSize); + s.writeData(&dynamic, offset); + s.writeData(&serialCore, sizeof(serialCore)); + void* tail = reinterpret_cast(&dynamic) + offset + sizeof(serialCore); + s.writeData(tail, classSize - offset - sizeof(serialCore)); + } + + template<> + void PxSerializerDefaultAdapter::registerReferences(PxBase& obj, PxSerializationContext& s) const + { + NpRigidDynamic& dynamic = static_cast(obj); + + s.registerReference(obj, PX_SERIAL_REF_KIND_PXBASE, size_t(&obj)); + + struct RequiresCallback : public PxProcessPxBaseCallback + { + RequiresCallback(physx::PxSerializationContext& c) : context(c) {} + RequiresCallback& operator=(const RequiresCallback&) { PX_ASSERT(0); return *this; } //PX_NOCOPY doesn't work for local classes + void process(PxBase& base) + { + context.registerReference(base, PX_SERIAL_REF_KIND_PXBASE, size_t(&base)); + } + PxSerializationContext& context; + }; + + RequiresCallback callback(s); + dynamic.requiresObjects(callback); + } + + template<> + void PxSerializerDefaultAdapter::registerReferences(PxBase& obj, PxSerializationContext& s) const + { + NpShape& shape = static_cast(obj); + + s.registerReference(obj, PX_SERIAL_REF_KIND_PXBASE, size_t(&obj)); + + struct RequiresCallback : public PxProcessPxBaseCallback + { + RequiresCallback(physx::PxSerializationContext& c) : context(c) {} + RequiresCallback &operator=(const RequiresCallback&) { PX_ASSERT(0); return *this; } //PX_NOCOPY doesn't work for local classes + void process(PxBase& base) + { + PxMaterial* pxMaterial = base.is(); + if (!pxMaterial) + { + context.registerReference(base, PX_SERIAL_REF_KIND_PXBASE, size_t(&base)); + } + else + { + //ideally we would move this part to ScShapeCore but we don't yet have a MaterialManager available there. + PxU32 index = static_cast(pxMaterial)->getHandle(); + context.registerReference(base, PX_SERIAL_REF_KIND_MATERIAL_IDX, size_t(index)); + } + } + PxSerializationContext& context; + }; + + RequiresCallback callback(s); + shape.requiresObjects(callback); + } + + template<> + bool PxSerializerDefaultAdapter::isSubordinate() const + { + return true; + } + + template<> + bool PxSerializerDefaultAdapter::isSubordinate() const + { + return true; + } + + template<> + bool PxSerializerDefaultAdapter::isSubordinate() const + { + return true; + } +} + +using namespace physx; + +void PxRegisterPhysicsSerializers(PxSerializationRegistry& sr) +{ + sr.registerSerializer(PxConcreteType::eCONVEX_MESH, PX_NEW_SERIALIZER_ADAPTER(ConvexMesh)); + sr.registerSerializer(PxConcreteType::eTRIANGLE_MESH_BVH33, PX_NEW_SERIALIZER_ADAPTER(RTreeTriangleMesh)); + sr.registerSerializer(PxConcreteType::eTRIANGLE_MESH_BVH34, PX_NEW_SERIALIZER_ADAPTER(BV4TriangleMesh)); + sr.registerSerializer(PxConcreteType::eHEIGHTFIELD, PX_NEW_SERIALIZER_ADAPTER(HeightField)); + sr.registerSerializer(PxConcreteType::eRIGID_DYNAMIC, PX_NEW_SERIALIZER_ADAPTER(NpRigidDynamic)); + sr.registerSerializer(PxConcreteType::eRIGID_STATIC, PX_NEW_SERIALIZER_ADAPTER(NpRigidStatic)); + sr.registerSerializer(PxConcreteType::eSHAPE, PX_NEW_SERIALIZER_ADAPTER(NpShape)); + sr.registerSerializer(PxConcreteType::eMATERIAL, PX_NEW_SERIALIZER_ADAPTER(NpMaterial)); + sr.registerSerializer(PxConcreteType::eCONSTRAINT, PX_NEW_SERIALIZER_ADAPTER(NpConstraint)); + sr.registerSerializer(PxConcreteType::eAGGREGATE, PX_NEW_SERIALIZER_ADAPTER(NpAggregate)); + sr.registerSerializer(PxConcreteType::eARTICULATION, PX_NEW_SERIALIZER_ADAPTER(NpArticulation)); + sr.registerSerializer(PxConcreteType::eARTICULATION_LINK, PX_NEW_SERIALIZER_ADAPTER(NpArticulationLink)); + sr.registerSerializer(PxConcreteType::eARTICULATION_JOINT, PX_NEW_SERIALIZER_ADAPTER(NpArticulationJoint)); + sr.registerSerializer(PxConcreteType::ePRUNING_STRUCTURE, PX_NEW_SERIALIZER_ADAPTER(Sq::PruningStructure)); +} + + +void PxUnregisterPhysicsSerializers(PxSerializationRegistry& sr) +{ + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eCONVEX_MESH)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eTRIANGLE_MESH_BVH33)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eTRIANGLE_MESH_BVH34)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eHEIGHTFIELD)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eRIGID_DYNAMIC)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eRIGID_STATIC)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eSHAPE)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eMATERIAL)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eCONSTRAINT)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eAGGREGATE)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eARTICULATION)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eARTICULATION_LINK)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eARTICULATION_JOINT)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::ePRUNING_STRUCTURE)); +} diff --git a/src/PhysX/physx/source/physx/src/NpShape.cpp b/src/PhysX/physx/source/physx/src/NpShape.cpp new file mode 100644 index 000000000..fbbe27edc --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpShape.cpp @@ -0,0 +1,823 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpCast.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "ScbNpDeps.h" +#include "GuBounds.h" + +using namespace physx; +using namespace Sq; + +static PX_FORCE_INLINE void updatePvdProperties(const Scb::Shape& shape) +{ +#if PX_SUPPORT_PVD + Scb::Scene* scbScene = shape.getScbSceneForAPI(); + if(scbScene) + scbScene->getScenePvdClient().updatePvdProperties(&shape); +#else + PX_UNUSED(shape); +#endif +} + +NpShape::NpShape(const PxGeometry& geometry, PxShapeFlags shapeFlags, const PxU16* materialIndices, PxU16 materialCount, bool isExclusive) +: PxShape (PxConcreteType::eSHAPE, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mActor (NULL) +, mShape (geometry, shapeFlags, materialIndices, materialCount, isExclusive) +, mName (NULL) +, mExclusiveAndActorCount (isExclusive ? EXCLUSIVE_MASK : 0) +{ + PX_ASSERT(mShape.getScShape().getPxShape() == static_cast(this)); + + PxShape::userData = NULL; + + incMeshRefCount(); +} + +NpShape::~NpShape() +{ + decMeshRefCount(); + + PxU32 nbMaterials = mShape.getNbMaterials(); + for (PxU32 i=0; i < nbMaterials; i++) + { + NpMaterial* mat = static_cast(mShape.getMaterial(i)); + mat->decRefCount(); + } +} + +void NpShape::onRefCountZero() +{ + NpFactory::getInstance().onShapeRelease(this); + // see NpShape.h for ref counting semantics for shapes + NpDestroy(getScbShape()); +} + +// PX_SERIALIZATION + +NpShape::NpShape(PxBaseFlags baseFlags) : PxShape(baseFlags), mShape(PxEmpty) +{ + mExclusiveAndActorCount &= EXCLUSIVE_MASK; +} + +void NpShape::exportExtraData(PxSerializationContext& stream) +{ + getScbShape().getScShape().exportExtraData(stream); + stream.writeName(mName); +} + +void NpShape::importExtraData(PxDeserializationContext& context) +{ + getScbShape().getScShape().importExtraData(context); + context.readName(mName); +} + +void NpShape::requiresObjects(PxProcessPxBaseCallback& c) +{ + //meshes + PxBase* mesh = NULL; + switch(mShape.getGeometryType()) + { + case PxGeometryType::eCONVEXMESH: + mesh = static_cast(mShape.getGeometry()).convexMesh; + break; + case PxGeometryType::eHEIGHTFIELD: + mesh = static_cast(mShape.getGeometry()).heightField; + break; + case PxGeometryType::eTRIANGLEMESH: + mesh = static_cast(mShape.getGeometry()).triangleMesh; + break; + case PxGeometryType::eSPHERE: + case PxGeometryType::ePLANE: + case PxGeometryType::eCAPSULE: + case PxGeometryType::eBOX: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + + if(mesh) + c.process(*mesh); + + //material + PxU32 nbMaterials = mShape.getNbMaterials(); + for (PxU32 i=0; i < nbMaterials; i++) + { + NpMaterial* mat = static_cast(mShape.getMaterial(i)); + c.process(*mat); + } +} + +void NpShape::resolveReferences(PxDeserializationContext& context) +{ + // getMaterials() only works after material indices have been patched. + // in order to get to the new material indices, we need access to the new materials. + // this only leaves us with the option of acquiring the material through the context given an old material index (we do have the mapping) + { + PxU32 nbIndices = mShape.getScShape().getNbMaterialIndices(); + const PxU16* indices = mShape.getScShape().getMaterialIndices(); + + for (PxU32 i=0; i < nbIndices; i++) + { + PxBase* base = context.resolveReference(PX_SERIAL_REF_KIND_MATERIAL_IDX, size_t(indices[i])); + PX_ASSERT(base && base->is()); + + NpMaterial& material = *static_cast(base); + getScbShape().getScShape().resolveMaterialReference(i, PxU16(material.getHandle())); + } + } + + context.translatePxBase(mActor); + + getScbShape().getScShape().resolveReferences(context); + + + incMeshRefCount(); + + // Increment materials' refcounts in a second pass. Works better in case of failure above. + PxU32 nbMaterials = mShape.getNbMaterials(); + for (PxU32 i=0; i < nbMaterials; i++) + { + NpMaterial* mat = static_cast(mShape.getMaterial(i)); + mat->incRefCount(); + } +} + +NpShape* NpShape::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpShape* obj = new (address) NpShape(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(NpShape); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +PxU32 NpShape::getReferenceCount() const +{ + return getRefCount(); +} + +void NpShape::acquireReference() +{ + incRefCount(); +} + +void NpShape::release() +{ + PX_CHECK_AND_RETURN(getRefCount() > 1 || getActorCount() == 0, "PxShape::release: last reference to a shape released while still attached to an actor!"); + NP_WRITE_CHECK(getOwnerScene()); + releaseInternal(); +} + +void NpShape::releaseInternal() +{ + decRefCount(); +} + +Sc::RigidCore& NpShape::getScRigidObjectExclusive() const +{ + const PxType actorType = mActor->getConcreteType(); + + if (actorType == PxConcreteType::eRIGID_DYNAMIC) + return static_cast(*mActor).getScbBodyFast().getScBody(); + else if (actorType == PxConcreteType::eARTICULATION_LINK) + return static_cast(*mActor).getScbBodyFast().getScBody(); + else + return static_cast(*mActor).getScbRigidStaticFast().getScStatic(); +} + +void NpShape::updateSQ(const char* errorMessage) +{ + if(mActor && (mShape.getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE)) + { + NpScene* scene = NpActor::getAPIScene(*mActor); + NpShapeManager* shapeManager = NpActor::getShapeManager(*mActor); + if(scene) + { + PxU32 compoundId; + const PrunerData sqData = shapeManager->findSceneQueryData(*this, compoundId); + scene->getSceneQueryManagerFast().markForUpdate(compoundId, sqData); + } + + // invalidate the pruning structure if the actor bounds changed + if(shapeManager->getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, errorMessage); + shapeManager->getPruningStructure()->invalidate(mActor); + } + } +} + +PxGeometryType::Enum NpShape::getGeometryType() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getGeometryType(); +} + +void NpShape::setGeometry(const PxGeometry& g) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setGeometry: shared shapes attached to actors are not writable."); + PX_SIMD_GUARD; + + // PT: fixes US2117 + if(g.getType() != getGeometryTypeFast()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxShape::setGeometry(): Invalid geometry type. Changing the type of the shape is not supported."); + return; + } + +#if PX_CHECKED + bool isValid = false; + switch(g.getType()) + { + case PxGeometryType::eSPHERE: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::ePLANE: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eCAPSULE: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eBOX: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eCONVEXMESH: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eTRIANGLEMESH: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eHEIGHTFIELD: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + + if(!isValid) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxShape::setGeometry(): Invalid geometry!"); + return; + } +#endif + + decMeshRefCount(); + + mShape.setGeometry(g); + + incMeshRefCount(); + + updateSQ("PxShape::setGeometry: Shape is a part of pruning structure, pruning structure is now invalid!"); +} + +PxGeometryHolder NpShape::getGeometry() const +{ + PX_COMPILE_TIME_ASSERT(sizeof(Gu::GeometryUnion)>=sizeof(PxGeometryHolder)); + return reinterpret_cast(mShape.getGeometry()); +} + +template +static PX_FORCE_INLINE bool getGeometryT(const NpShape* npShape, PxGeometryType::Enum type, T& geom) +{ + NP_READ_CHECK(npShape->getOwnerScene()); + + if(npShape->getGeometryTypeFast() != type) + return false; + + geom = static_cast(npShape->getScbShape().getGeometry()); + return true; +} + +bool NpShape::getBoxGeometry(PxBoxGeometry& g) const { return getGeometryT(this, PxGeometryType::eBOX, g); } +bool NpShape::getSphereGeometry(PxSphereGeometry& g) const { return getGeometryT(this, PxGeometryType::eSPHERE, g); } +bool NpShape::getCapsuleGeometry(PxCapsuleGeometry& g) const { return getGeometryT(this, PxGeometryType::eCAPSULE, g); } +bool NpShape::getPlaneGeometry(PxPlaneGeometry& g) const { return getGeometryT(this, PxGeometryType::ePLANE, g); } +bool NpShape::getConvexMeshGeometry(PxConvexMeshGeometry& g) const { return getGeometryT(this, PxGeometryType::eCONVEXMESH, g); } +bool NpShape::getTriangleMeshGeometry(PxTriangleMeshGeometry& g) const { return getGeometryT(this, PxGeometryType::eTRIANGLEMESH, g); } +bool NpShape::getHeightFieldGeometry(PxHeightFieldGeometry& g) const { return getGeometryT(this, PxGeometryType::eHEIGHTFIELD, g); } + +PxRigidActor* NpShape::getActor() const +{ + NP_READ_CHECK(getOwnerScene()); + return mActor; +} + +void NpShape::setLocalPose(const PxTransform& newShape2Actor) +{ + PX_CHECK_AND_RETURN(newShape2Actor.isSane(), "PxShape::setLocalPose: pose is not valid."); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setLocalPose: shared shapes attached to actors are not writable."); + NP_WRITE_CHECK(getOwnerScene()); + + mShape.setShape2Actor(newShape2Actor.getNormalized()); + + updateSQ("PxShape::setLocalPose: Shape is a part of pruning structure, pruning structure is now invalid!"); +} + +PxTransform NpShape::getLocalPose() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getShape2Actor(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpShape::setSimulationFilterData(const PxFilterData& data) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setSimulationFilterData: shared shapes attached to actors are not writable."); + mShape.setSimulationFilterData(data); +} + +PxFilterData NpShape::getSimulationFilterData() const +{ + NP_READ_CHECK(getOwnerScene()); + return mShape.getSimulationFilterData(); +} + +void NpShape::setQueryFilterData(const PxFilterData& data) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setQueryFilterData: shared shapes attached to actors are not writable."); + + mShape.getScShape().setQueryFilterData(data); // PT: this one doesn't need double-buffering + + updatePvdProperties(mShape); +} + +PxFilterData NpShape::getQueryFilterData() const +{ + NP_READ_CHECK(getOwnerScene()); + + return getQueryFilterDataFast(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpShape::setMaterials(PxMaterial*const* materials, PxU16 materialCount) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setMaterials: shared shapes attached to actors are not writable."); + +#if PX_CHECKED + if (!NpShape::checkMaterialSetup(mShape.getGeometry(), "PxShape::setMaterials()", materials, materialCount)) + return; +#endif + + PxU32 oldMaterialCount = mShape.getNbMaterials(); + PX_ALLOCA(oldMaterials, PxMaterial*, oldMaterialCount); + PxU32 tmp = mShape.getMaterials(oldMaterials, oldMaterialCount); + PX_ASSERT(tmp == oldMaterialCount); + PX_UNUSED(tmp); + + if (mShape.setMaterials(materials, materialCount)) + { + for(PxU32 i=0; i < materialCount; i++) + static_cast(materials[i])->incRefCount(); + + for(PxU32 i=0; i < oldMaterialCount; i++) + static_cast(oldMaterials[i])->decRefCount(); + } +} + +PxU16 NpShape::getNbMaterials() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getNbMaterials(); +} + +PxU32 NpShape::getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getMaterials(userBuffer, bufferSize, startIndex); +} + +PxMaterial* NpShape::getMaterialFromInternalFaceIndex(PxU32 faceIndex) const +{ + NP_READ_CHECK(getOwnerScene()); + + bool isHf = (getGeometryType() == PxGeometryType::eHEIGHTFIELD); + bool isMesh = (getGeometryType() == PxGeometryType::eTRIANGLEMESH); + if( faceIndex == 0xFFFFffff && (isHf || isMesh) ) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxShape::getMaterialFromInternalFaceIndex received 0xFFFFffff as input - returning NULL."); + return NULL; + } + + PxMaterialTableIndex hitMatTableId = 0; + + if(isHf) + { + PxHeightFieldGeometry hfGeom; + getHeightFieldGeometry(hfGeom); + + hitMatTableId = hfGeom.heightField->getTriangleMaterialIndex(faceIndex); + } + else if(isMesh) + { + PxTriangleMeshGeometry triGeo; + getTriangleMeshGeometry(triGeo); + + Gu::TriangleMesh* tm = static_cast(triGeo.triangleMesh); + if(tm->hasPerTriangleMaterials()) + hitMatTableId = triGeo.triangleMesh->getTriangleMaterialIndex(faceIndex); + } + + return getMaterial(hitMatTableId); +} + +void NpShape::setContactOffset(PxReal contactOffset) +{ + NP_WRITE_CHECK(getOwnerScene()); + + PX_CHECK_AND_RETURN(PxIsFinite(contactOffset), "PxShape::setContactOffset: invalid float"); + PX_CHECK_AND_RETURN((contactOffset >= 0.0f && contactOffset > mShape.getRestOffset()), "PxShape::setContactOffset: contactOffset should be positive, and greater than restOffset!"); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setContactOffset: shared shapes attached to actors are not writable."); + + mShape.setContactOffset(contactOffset); +} + +PxReal NpShape::getContactOffset() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getContactOffset(); +} + +void NpShape::setRestOffset(PxReal restOffset) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(PxIsFinite(restOffset), "PxShape::setRestOffset: invalid float"); + PX_CHECK_AND_RETURN((restOffset < mShape.getContactOffset()), "PxShape::setRestOffset: restOffset should be less than contactOffset!"); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setRestOffset: shared shapes attached to actors are not writable."); + + mShape.setRestOffset(restOffset); +} + +PxReal NpShape::getRestOffset() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getRestOffset(); +} + +void NpShape::setTorsionalPatchRadius(PxReal radius) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(PxIsFinite(radius), "PxShape::setTorsionalPatchRadius: invalid float"); + PX_CHECK_AND_RETURN((radius >= 0.f), "PxShape::setTorsionalPatchRadius: must be >= 0.f"); + + mShape.setTorsionalPatchRadius(radius); +} + +PxReal NpShape::getTorsionalPatchRadius() const +{ + NP_READ_CHECK(getOwnerScene()); + return mShape.getTorsionalPatchRadius(); +} + +void NpShape::setMinTorsionalPatchRadius(PxReal radius) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(PxIsFinite(radius), "PxShape::setMinTorsionalPatchRadius: invalid float"); + PX_CHECK_AND_RETURN((radius >= 0.f), "PxShape::setMinTorsionalPatchRadius: must be >= 0.f"); + + mShape.setMinTorsionalPatchRadius(radius); +} + +PxReal NpShape::getMinTorsionalPatchRadius() const +{ + NP_READ_CHECK(getOwnerScene()); + return mShape.getMinTorsionalPatchRadius(); +} + +void NpShape::setFlagsInternal(PxShapeFlags inFlags) +{ + const bool hasMeshTypeGeom = mShape.getGeometryType() == PxGeometryType::eTRIANGLEMESH || mShape.getGeometryType() == PxGeometryType::eHEIGHTFIELD; + + if(hasMeshTypeGeom && (inFlags & PxShapeFlag::eTRIGGER_SHAPE)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxShape::setFlag(s): triangle mesh and heightfield triggers are not supported!"); + return; + } + + if((inFlags & PxShapeFlag::eSIMULATION_SHAPE) && (inFlags & PxShapeFlag::eTRIGGER_SHAPE)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxShape::setFlag(s): shapes cannot simultaneously be trigger shapes and simulation shapes."); + return; + } + + const PxShapeFlags oldFlags = mShape.getFlags(); + + const bool oldIsSimShape = oldFlags & PxShapeFlag::eSIMULATION_SHAPE; + const bool isSimShape = inFlags & PxShapeFlag::eSIMULATION_SHAPE; + + if(mActor) + { + const PxType type = mActor->getConcreteType(); + + // PT: US5732 - support kinematic meshes + bool isKinematic = false; + if(type==PxConcreteType::eRIGID_DYNAMIC) + { + PxRigidDynamic* rigidDynamic = static_cast(mActor); + isKinematic = rigidDynamic->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC; + } + + if((type != PxConcreteType::eRIGID_STATIC) && !isKinematic && isSimShape && !oldIsSimShape && (hasMeshTypeGeom || mShape.getGeometryType() == PxGeometryType::ePLANE)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxShape::setFlag(s): triangle mesh, heightfield and plane shapes can only be simulation shapes if part of a PxRigidStatic!"); + return; + } + } + + const bool oldHasSceneQuery = oldFlags & PxShapeFlag::eSCENE_QUERY_SHAPE; + const bool hasSceneQuery = inFlags & PxShapeFlag::eSCENE_QUERY_SHAPE; + + mShape.setFlags(inFlags); + + if(oldHasSceneQuery != hasSceneQuery && mActor) + { + NpScene* npScene = getAPIScene(); + NpShapeManager* shapeManager = NpActor::getShapeManager(*mActor); + if(npScene) + { + if(hasSceneQuery) + shapeManager->setupSceneQuery(npScene->getSceneQueryManagerFast(), *mActor, *this); + else + shapeManager->teardownSceneQuery(npScene->getSceneQueryManagerFast(), *this); + } + + // invalidate the pruning structure if the actor bounds changed + if(shapeManager->getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxShape::setFlag: Shape is a part of pruning structure, pruning structure is now invalid!"); + shapeManager->getPruningStructure()->invalidate(mActor); + } + } +} + +void NpShape::setFlag(PxShapeFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setFlag: shared shapes attached to actors are not writable."); + PX_SIMD_GUARD; + + PxShapeFlags shapeFlags = mShape.getFlags(); + shapeFlags = value ? shapeFlags | flag : shapeFlags & ~flag; + + setFlagsInternal(shapeFlags); +} + +void NpShape::setFlags(PxShapeFlags inFlags) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setFlags: shared shapes attached to actors are not writable."); + PX_SIMD_GUARD; + + setFlagsInternal(inFlags); +} + +PxShapeFlags NpShape::getFlags() const +{ + NP_READ_CHECK(getOwnerScene()); + return mShape.getFlags(); +} + +bool NpShape::isExclusive() const +{ + NP_READ_CHECK(getOwnerScene()); + return (mExclusiveAndActorCount & EXCLUSIVE_MASK) != 0; +} + +void NpShape::onActorAttach(PxRigidActor& actor) +{ + incRefCount(); + if(isExclusiveFast()) + mActor = &actor; + Ps::atomicIncrement(&mExclusiveAndActorCount); +} + +void NpShape::onActorDetach() +{ + PX_ASSERT(getActorCount() > 0); + Ps::atomicDecrement(&mExclusiveAndActorCount); + if(isExclusiveFast()) + mActor = NULL; + decRefCount(); +} + +void NpShape::setName(const char* debugName) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setName: shared shapes attached to actors are not writable."); + + mName = debugName; + + updatePvdProperties(mShape); +} + +const char* NpShape::getName() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mName; +} + +NpScene* NpShape::getOwnerScene() const +{ + return mActor ? NpActor::getOwnerScene(*mActor) : NULL; +} + +NpScene* NpShape::getAPIScene() const +{ + // gets called when we update SQ structures due to a write - in which case there must be an actor + PX_ASSERT(mActor); + return NpActor::getAPIScene(*mActor); +} + +/////////////////////////////////////////////////////////////////////////////// + +namespace physx +{ +Sc::RigidCore* NpShapeGetScRigidObjectFromScbSLOW(const Scb::Shape& scb) +{ + const NpShape* np = getNpShape(&scb); + return np->NpShape::getActor() ? &np->getScRigidObjectExclusive() : NULL; +} + +size_t NpShapeGetScPtrOffset() +{ + const size_t offset = size_t(&(reinterpret_cast(0)->getScbShape().getScShape())); + return offset; +} + +void NpShapeIncRefCount(Scb::Shape& scb) +{ + NpShape* np = const_cast(getNpShape(&scb)); + np->incRefCount(); +} + +void NpShapeDecRefCount(Scb::Shape& scb) +{ + NpShape* np = const_cast(getNpShape(&scb)); + np->decRefCount(); +} +} + +// see NpConvexMesh.h, NpHeightField.h, NpTriangleMesh.h for details on how ref counting works for meshes +Cm::RefCountable* NpShape::getMeshRefCountable() +{ + switch(mShape.getGeometryType()) + { + case PxGeometryType::eCONVEXMESH: + return static_cast( + static_cast(mShape.getGeometry()).convexMesh); + + case PxGeometryType::eHEIGHTFIELD: + return static_cast( + static_cast(mShape.getGeometry()).heightField); + + case PxGeometryType::eTRIANGLEMESH: + return static_cast( + static_cast(mShape.getGeometry()).triangleMesh); + + case PxGeometryType::eSPHERE: + case PxGeometryType::ePLANE: + case PxGeometryType::eCAPSULE: + case PxGeometryType::eBOX: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + return NULL; +} + +bool NpShape::isWritable() +{ + // a shape is writable if it's exclusive, or it's not connected to any actors (which is true if the ref count is 1 and the user ref is not released.) + return isExclusiveFast() || (getRefCount()==1 && (mBaseFlags & PxBaseFlag::eIS_RELEASABLE)); +} + +void NpShape::incMeshRefCount() +{ + Cm::RefCountable* npMesh = getMeshRefCountable(); + if(npMesh) + npMesh->incRefCount(); +} + +void NpShape::decMeshRefCount() +{ + Cm::RefCountable* npMesh = getMeshRefCountable(); + if(npMesh) + npMesh->decRefCount(); +} + +bool NpShape::checkMaterialSetup(const PxGeometry& geom, const char* errorMsgPrefix, PxMaterial*const* materials, PxU16 materialCount) +{ + for(PxU32 i=0; i 1 && (geom.getType() != PxGeometryType::eHEIGHTFIELD) && (geom.getType() != PxGeometryType::eTRIANGLEMESH)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: multiple materials defined for single material geometry!", errorMsgPrefix); + return false; + } + + // verify we provide all materials required + if (materialCount > 1 && (geom.getType() == PxGeometryType::eTRIANGLEMESH)) + { + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + const PxTriangleMesh& mesh = *meshGeom.triangleMesh; + if(mesh.getTriangleMaterialIndex(0) != 0xffff) + { + for(PxU32 i = 0; i < mesh.getNbTriangles(); i++) + { + const PxMaterialTableIndex meshMaterialIndex = mesh.getTriangleMaterialIndex(i); + if(meshMaterialIndex >= materialCount) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: PxTriangleMesh material indices reference more materials than provided!", errorMsgPrefix); + break; + } + } + } + } + if (materialCount > 1 && (geom.getType() == PxGeometryType::eHEIGHTFIELD)) + { + const PxHeightFieldGeometry& meshGeom = static_cast(geom); + const PxHeightField& mesh = *meshGeom.heightField; + if(mesh.getTriangleMaterialIndex(0) != 0xffff) + { + const PxU32 nbTris = mesh.getNbColumns()*mesh.getNbRows()*2; + for(PxU32 i = 0; i < nbTris; i++) + { + const PxMaterialTableIndex meshMaterialIndex = mesh.getTriangleMaterialIndex(i); + if(meshMaterialIndex != PxHeightFieldMaterial::eHOLE && meshMaterialIndex >= materialCount) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: PxHeightField material indices reference more materials than provided!", errorMsgPrefix); + break; + } + } + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/physx/src/NpShape.h b/src/PhysX/physx/source/physx/src/NpShape.h new file mode 100644 index 000000000..ccc193ddf --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpShape.h @@ -0,0 +1,219 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_SHAPE +#define PX_PHYSICS_NP_SHAPE + +#include "PxShape.h" +#include "buffering/ScbShape.h" +#include "PxMetaData.h" + +namespace physx +{ + +struct NpInternalShapeFlag +{ + enum Enum + { + eEXCLUSIVE = (1<<0) + }; +}; + +/** +\brief collection of set bits defined in PxShapeFlag. + +@see PxShapeFlag +*/ +typedef PxFlags NpInternalShapeFlags; +PX_FLAGS_OPERATORS(NpInternalShapeFlag::Enum,PxU8) + + +class NpScene; +class NpShapeManager; + +namespace Scb +{ + class Scene; + class RigidObject; +} + +namespace Sc +{ + class MaterialCore; +} + +class NpShape : public PxShape, public Ps::UserAllocated, public Cm::RefCountable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpShape(PxBaseFlags baseFlags); + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback& c); + void resolveReferences(PxDeserializationContext& context); + static NpShape* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + NpShape(const PxGeometry& geometry, + PxShapeFlags shapeFlags, + const PxU16* materialIndices, + PxU16 materialCount, + bool isExclusive); + + virtual ~NpShape(); + + //--------------------------------------------------------------------------------- + // PxShape implementation + //--------------------------------------------------------------------------------- + virtual void release(); //!< call to release from actor + virtual PxU32 getReferenceCount() const; + virtual void acquireReference(); + + virtual PxGeometryType::Enum getGeometryType() const; + + virtual void setGeometry(const PxGeometry&); + virtual PxGeometryHolder getGeometry() const; + virtual bool getBoxGeometry(PxBoxGeometry&) const; + virtual bool getSphereGeometry(PxSphereGeometry&) const; + virtual bool getCapsuleGeometry(PxCapsuleGeometry&) const; + virtual bool getPlaneGeometry(PxPlaneGeometry&) const; + virtual bool getConvexMeshGeometry(PxConvexMeshGeometry& g) const; + virtual bool getTriangleMeshGeometry(PxTriangleMeshGeometry& g) const; + virtual bool getHeightFieldGeometry(PxHeightFieldGeometry& g) const; + + virtual PxRigidActor* getActor() const; + + virtual void setLocalPose(const PxTransform& pose); + virtual PxTransform getLocalPose() const; + + virtual void setSimulationFilterData(const PxFilterData& data); + virtual PxFilterData getSimulationFilterData() const; + virtual void setQueryFilterData(const PxFilterData& data); + virtual PxFilterData getQueryFilterData() const; + + virtual void setMaterials(PxMaterial*const* materials, PxU16 materialCount); + virtual PxU16 getNbMaterials() const; + virtual PxU32 getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + virtual PxMaterial* getMaterialFromInternalFaceIndex(PxU32 faceIndex) const; + + virtual void setContactOffset(PxReal); + virtual PxReal getContactOffset() const; + + virtual void setRestOffset(PxReal); + virtual PxReal getRestOffset() const; + + virtual void setTorsionalPatchRadius(PxReal); + virtual PxReal getTorsionalPatchRadius() const; + + virtual void setMinTorsionalPatchRadius(PxReal); + virtual PxReal getMinTorsionalPatchRadius() const; + + virtual void setFlag(PxShapeFlag::Enum flag, bool value); + virtual void setFlags( PxShapeFlags inFlags ); + virtual PxShapeFlags getFlags() const; + + virtual bool isExclusive() const; + + virtual void setName(const char* debugName); + virtual const char* getName() const; + + //--------------------------------------------------------------------------------- + // RefCountable implementation + //--------------------------------------------------------------------------------- + + // Ref counting for shapes works like this: + // * for exclusive shapes the actor has a counted reference + // * for shared shapes, each actor has a counted reference, and the user has a counted reference + // * for either kind, each instance of the shape in a scene (i.e. each shapeSim) causes the reference count to be incremented by 1. + // Because these semantics aren't clear to users, this reference count should not be exposed in the API + + virtual void onRefCountZero(); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + + void setFlagsInternal( PxShapeFlags inFlags ); + + PX_FORCE_INLINE PxShapeFlags getFlagsFast() const { return mShape.getFlags(); } + PX_FORCE_INLINE PxShapeFlags getFlagsUnbuffered() const { return mShape.getScShape().getFlags(); } + PX_FORCE_INLINE PxGeometryType::Enum getGeometryTypeFast() const { return mShape.getGeometryType(); } + PX_FORCE_INLINE const Gu::GeometryUnion& getGeometryFast() const { return mShape.getGeometryUnion(); } + PX_FORCE_INLINE const PxTransform& getLocalPoseFast() const { return mShape.getShape2Actor(); } + PX_FORCE_INLINE PxU32 getActorCount() const { return PxU32(mExclusiveAndActorCount & ACTOR_COUNT_MASK); } + PX_FORCE_INLINE PxI32 isExclusiveFast() const { return mExclusiveAndActorCount & EXCLUSIVE_MASK; } + + PX_FORCE_INLINE const PxFilterData& getQueryFilterDataFast() const + { + return mShape.getScShape().getQueryFilterData(); // PT: this one doesn't need double-buffering + } + + PX_FORCE_INLINE const Scb::Shape& getScbShape() const { return mShape; } + PX_FORCE_INLINE Scb::Shape& getScbShape() { return mShape; } + + PX_INLINE PxMaterial* getMaterial(PxU32 index) const { return mShape.getMaterial(index); } + static bool checkMaterialSetup(const PxGeometry& geom, const char* errorMsgPrefix, PxMaterial*const* materials, PxU16 materialCount); + + void onActorAttach(PxRigidActor& actor); + void onActorDetach(); + + // These methods are used only for sync'ing, and may only be called on exclusive shapes since only exclusive shapes have buffering + Sc::RigidCore& getScRigidObjectExclusive() const; + void releaseInternal(); + + NpScene* getOwnerScene() const; // same distinctions as for NpActor +private: + NpScene* getAPIScene() const; + + void incMeshRefCount(); + void decMeshRefCount(); + Cm::RefCountable* getMeshRefCountable(); + bool isWritable(); + void updateSQ(const char* errorMessage); + + PxRigidActor* mActor; // Auto-resolving refs breaks DLL loading for some reason + Scb::Shape mShape; + const char* mName; + + static const PxI32 EXCLUSIVE_MASK = 0x80000000; + static const PxI32 ACTOR_COUNT_MASK = 0x7fffffff; + + volatile PxI32 mExclusiveAndActorCount; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpShapeManager.cpp b/src/PhysX/physx/source/physx/src/NpShapeManager.cpp new file mode 100644 index 000000000..0a37f728e --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpShapeManager.cpp @@ -0,0 +1,799 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpShapeManager.h" +#include "NpFactory.h" +#include "ScbRigidObject.h" +#include "NpActor.h" +#include "SqPruningStructure.h" +#include "NpScene.h" +#include "NpPtrTableStorageManager.h" +#include "NpRigidDynamic.h" +#include "NpArticulationLink.h" +#include "ScBodySim.h" +#include "GuBounds.h" +#include "CmUtils.h" +#include "PsAlloca.h" + +using namespace physx; +using namespace Sq; +using namespace Gu; +using namespace Cm; + +static PX_FORCE_INLINE bool isSceneQuery(const NpShape& shape) { return shape.getFlagsFast() & PxShapeFlag::eSCENE_QUERY_SHAPE; } + +NpShapeManager::NpShapeManager() + : mSqCompoundId(INVALID_PRUNERHANDLE), mPruningStructure(NULL) +{ +} + +// PX_SERIALIZATION +NpShapeManager::NpShapeManager(const PxEMPTY) : + mShapes (PxEmpty), + mSceneQueryData (PxEmpty) +{ +} + +NpShapeManager::~NpShapeManager() +{ + PX_ASSERT(!mPruningStructure); + PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); + mShapes.clear(sm); + mSceneQueryData.clear(sm); +} + +void NpShapeManager::exportExtraData(PxSerializationContext& stream) +{ + mShapes.exportExtraData(stream); + mSceneQueryData.exportExtraData(stream); +} + +void NpShapeManager::importExtraData(PxDeserializationContext& context) +{ + mShapes.importExtraData(context); + mSceneQueryData.importExtraData(context); +} +//~PX_SERIALIZATION + +void NpShapeManager::attachShape(NpShape& shape, PxRigidActor& actor) +{ + PX_ASSERT(!mPruningStructure); + + PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); + + const PxU32 index = getNbShapes(); + mShapes.add(&shape, sm); + mSceneQueryData.add(reinterpret_cast(size_t(SQ_INVALID_PRUNER_DATA)), sm); + + NpScene* scene = NpActor::getAPIScene(actor); + if(scene && isSceneQuery(shape)) + setupSceneQuery(scene->getSceneQueryManagerFast(), actor, index); + + Scb::RigidObject& ro = static_cast(NpActor::getScbFromPxActor(actor)); + ro.onShapeAttach(shape.getScbShape()); + + PX_ASSERT(!shape.isExclusive() || shape.getActor()==NULL); + shape.onActorAttach(actor); +} + +bool NpShapeManager::detachShape(NpShape& s, PxRigidActor& actor, bool wakeOnLostTouch) +{ + PX_ASSERT(!mPruningStructure); + + const PxU32 index = mShapes.find(&s); + if(index==0xffffffff) + return false; + + NpScene* scene = NpActor::getAPIScene(actor); + if(scene && isSceneQuery(s)) + { + scene->getSceneQueryManagerFast().removePrunerShape(mSqCompoundId, getPrunerData(index)); + // if this is the last shape of a compound shape, we have to remove the compound id + // and in case of a dynamic actor, remove it from the active list + if(isSqCompound() && (mShapes.getCount() == 1)) + { + mSqCompoundId = INVALID_PRUNERHANDLE; + const PxType actorType = actor.getConcreteType(); + const bool isDynamic = actorType == PxConcreteType::eRIGID_DYNAMIC || actorType == PxConcreteType::eARTICULATION_LINK; + if(isDynamic) + { + // for PxRigidDynamic and PxArticulationLink we need to remove the compound rigid flag and remove them from active list + if(actor.is()) + const_cast(static_cast(actor)).getScbBodyFast().getScBody().getSim()->disableCompound(); + else + { + if(actor.is()) + const_cast(static_cast(actor)).getScbBodyFast().getScBody().getSim()->disableCompound(); + } + } + } + } + + Scb::RigidObject& ro = static_cast(NpActor::getScbFromPxActor(actor)); + ro.onShapeDetach(s.getScbShape(), wakeOnLostTouch, (s.getRefCount() == 1)); + PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); + mShapes.replaceWithLast(index, sm); + mSceneQueryData.replaceWithLast(index, sm); + + s.onActorDetach(); + return true; +} + +void NpShapeManager::detachAll(NpScene* scene, const PxRigidActor& actor) +{ + // assumes all SQ data has been released, which is currently the responsbility of the owning actor + const PxU32 nbShapes = getNbShapes(); + NpShape*const *shapes = getShapes(); + + if(scene) + teardownAllSceneQuery(scene->getSceneQueryManagerFast(), actor); + + // actor cleanup in Scb/Sc will remove any outstanding references corresponding to sim objects, so we don't need to do that here. + for(PxU32 i=0;ionActorDetach(); + + PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); + + mShapes.clear(sm); + mSceneQueryData.clear(sm); +} + +PxU32 NpShapeManager::getShapes(PxShape** buffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return getArrayOfPointers(buffer, bufferSize, startIndex, getShapes(), getNbShapes()); +} + +PxBounds3 NpShapeManager::getWorldBounds(const PxRigidActor& actor) const +{ + PxBounds3 bounds(PxBounds3::empty()); + + const PxU32 nbShapes = getNbShapes(); + const PxTransform actorPose = actor.getGlobalPose(); + NpShape*const* PX_RESTRICT shapes = getShapes(); + + for(PxU32 i=0;igetScbShape().getGeometry(), actorPose * shapes[i]->getLocalPoseFast())); + + return bounds; +} + +void NpShapeManager::clearShapesOnRelease(Scb::Scene& s, PxRigidActor& r) +{ + PX_ASSERT(static_cast(NpActor::getScbFromPxActor(r)).isSimDisabledInternally()); + + const PxU32 nbShapes = getNbShapes(); + NpShape*const* PX_RESTRICT shapes = getShapes(); + + for(PxU32 i=0;igetScbShape(); + scbShape.checkUpdateOnRemove(&s); +#if PX_SUPPORT_PVD + s.getScenePvdClient().releasePvdInstance(&scbShape, r); +#else + PX_UNUSED(r); +#endif + } +} + +void NpShapeManager::releaseExclusiveUserReferences() +{ + // when the factory is torn down, release any shape owner refs that are still outstanding + const PxU32 nbShapes = getNbShapes(); + NpShape*const* PX_RESTRICT shapes = getShapes(); + for(PxU32 i=0;iisExclusiveFast() && shapes[i]->getRefCount()>1) + shapes[i]->release(); + } +} + +void NpShapeManager::setupSceneQuery(SceneQueryManager& sqManager, const PxRigidActor& actor, const NpShape& shape) +{ + PX_ASSERT(shape.getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE); + const PxU32 index = mShapes.find(&shape); + PX_ASSERT(index!=0xffffffff); + setupSceneQuery(sqManager, actor, index); +} + +void NpShapeManager::teardownSceneQuery(SceneQueryManager& sqManager, const NpShape& shape) +{ + const PxU32 index = mShapes.find(&shape); + PX_ASSERT(index!=0xffffffff); + teardownSceneQuery(sqManager, index); +} + +void NpShapeManager::setupAllSceneQuery(NpScene* scene, const PxRigidActor& actor, bool hasPrunerStructure, const PxBounds3* bounds, const Gu::BVHStructure* bvhStructure) +{ + PX_ASSERT(scene); // shouldn't get here unless we're in a scene + SceneQueryManager& sqManager = scene->getSceneQueryManagerFast(); + + const PxU32 nbShapes = getNbShapes(); + NpShape*const *shapes = getShapes(); + + // if BVH structure was provided, we add shapes into compound pruner + if(bvhStructure) + { + addBVHStructureShapes(sqManager, actor, bvhStructure); + } + else + { + const PxType actorType = actor.getConcreteType(); + const bool isDynamic = actorType == PxConcreteType::eRIGID_DYNAMIC || actorType == PxConcreteType::eARTICULATION_LINK; + + for(PxU32 i=0;igetNbBounds()); + + mSqCompoundId = static_cast(NpActor::getScbFromPxActor(actor).getActorCore()).getRigidID(); + sqManager.addCompoundShape(*bvhStructure, mSqCompoundId, actor.getGlobalPose(), prunerData, scbShapes, scbActor); + + numSqShapes = 0; + for(PxU32 i = 0; i < nbShapes; i++) + { + const NpShape& shape = *getShapes()[i]; + if(isSceneQuery(shape)) + setPrunerData(i, prunerData[numSqShapes++]); + } +} + +void NpShapeManager::addPrunerShape(SceneQueryManager& sqManager, PxU32 index, const NpShape& shape, const PxRigidActor& actor, bool dynamic, const PxBounds3* bound, bool hasPrunerStructure) +{ + const Scb::Shape& scbShape = shape.getScbShape(); + const Scb::Actor& scbActor = NpActor::getScbFromPxActor(actor); + setPrunerData(index, sqManager.addPrunerShape(scbShape, scbActor, dynamic, mSqCompoundId, bound, hasPrunerStructure)); +} + +void NpShapeManager::setupSceneQuery(SceneQueryManager& sqManager, const PxRigidActor& actor, PxU32 index) +{ + const PxType actorType = actor.getConcreteType(); + const bool isDynamic = actorType == PxConcreteType::eRIGID_DYNAMIC || actorType == PxConcreteType::eARTICULATION_LINK; + addPrunerShape(sqManager, index, *(getShapes()[index]), actor, isDynamic, NULL, false); +} + +void NpShapeManager::teardownSceneQuery(SceneQueryManager& sqManager, PxU32 index) +{ + sqManager.removePrunerShape(mSqCompoundId, getPrunerData(index)); + setPrunerData(index, SQ_INVALID_PRUNER_DATA); +} + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "GuHeightFieldUtil.h" +#include "PxGeometryQuery.h" +#include "PxMeshQuery.h" +#include "GuConvexEdgeFlags.h" +#include "GuMidphaseInterface.h" + +static const PxU32 gCollisionShapeColor = PxU32(PxDebugColor::eARGB_MAGENTA); + +static void visualizeSphere(const PxSphereGeometry& geometry, RenderOutput& out, const PxTransform& absPose) +{ + out << gCollisionShapeColor; // PT: no need to output this for each segment! + + out << absPose << DebugCircle(100, geometry.radius); + + PxMat44 rotPose(absPose); + Ps::swap(rotPose.column1, rotPose.column2); + rotPose.column1 = -rotPose.column1; + out << rotPose << DebugCircle(100, geometry.radius); + + Ps::swap(rotPose.column0, rotPose.column2); + rotPose.column0 = -rotPose.column0; + out << rotPose << DebugCircle(100, geometry.radius); +} + +static void visualizePlane(const PxPlaneGeometry& /*geometry*/, RenderOutput& out, const PxTransform& absPose) +{ + PxMat44 rotPose(absPose); + Ps::swap(rotPose.column1, rotPose.column2); + rotPose.column1 = -rotPose.column1; + + Ps::swap(rotPose.column0, rotPose.column2); + rotPose.column0 = -rotPose.column0; + + out << rotPose << gCollisionShapeColor; // PT: no need to output this for each segment! + for(PxReal radius = 2.0f; radius < 20.0f ; radius += 2.0f) + out << DebugCircle(100, radius*radius); +} + +static void visualizeCapsule(const PxCapsuleGeometry& geometry, RenderOutput& out, const PxTransform& absPose) +{ + out << gCollisionShapeColor; + out.outputCapsule(geometry.radius, geometry.halfHeight, absPose); +} + +static void visualizeBox(const PxBoxGeometry& geometry, RenderOutput& out, const PxTransform& absPose) +{ + out << gCollisionShapeColor; + out << absPose << DebugBox(geometry.halfExtents); +} + +static void visualizeConvexMesh(const PxConvexMeshGeometry& geometry, RenderOutput& out, const PxTransform& absPose) +{ + const ConvexMesh* convexMesh = static_cast(geometry.convexMesh); + const ConvexHullData& hullData = convexMesh->getHull(); + + const PxVec3* vertices = hullData.getHullVertices(); + const PxU8* indexBuffer = hullData.getVertexData8(); + const PxU32 nbPolygons = convexMesh->getNbPolygonsFast(); + + const PxMat44 m44(PxMat33(absPose.q) * geometry.scale.toMat33(), absPose.p); + + out << m44 << gCollisionShapeColor; // PT: no need to output this for each segment! + + for(PxU32 i=0; i(indices); + ref0 = dtriangles[i*3+0]; + ref1 = dtriangles[i*3+1]; + ref2 = dtriangles[i*3+2]; + } + else + { + const PxU16* wtriangles = reinterpret_cast(indices); + ref0 = wtriangles[i*3+0]; + ref1 = wtriangles[i*3+1]; + ref2 = wtriangles[i*3+2]; + } + + wp[0] = vertices[ref0]; + wp[1] = vertices[ref1]; + wp[2] = vertices[ref2]; +} + +static void getTriangle(const Gu::TriangleMesh& mesh, PxU32 i, PxVec3* wp, const PxVec3* vertices, const void* indices, const Matrix34& absPose, bool has16BitIndices) +{ + PxVec3 localVerts[3]; + getTriangle(mesh, i, localVerts, vertices, indices, has16BitIndices); + + wp[0] = absPose.transform(localVerts[0]); + wp[1] = absPose.transform(localVerts[1]); + wp[2] = absPose.transform(localVerts[2]); +} + +static void visualizeActiveEdges(RenderOutput& out, const Gu::TriangleMesh& mesh, PxU32 nbTriangles, const PxU32* results, const Matrix34& absPose) +{ + const PxU8* extraTrigData = mesh.getExtraTrigData(); + PX_ASSERT(extraTrigData); + + const PxVec3* vertices = mesh.getVerticesFast(); + const void* indices = mesh.getTrianglesFast(); + + out << PxU32(PxDebugColor::eARGB_YELLOW); // PT: no need to output this for each segment! + + const bool has16Bit = mesh.has16BitIndices(); + for(PxU32 i=0; i(geometry.triangleMesh); + + const PxMat44 midt(PxIdentity); + const Matrix34 absPose(PxMat33(pose.q) * geometry.scale.toMat33(), pose.p); + + PxU32 nbTriangles = triangleMesh->getNbTrianglesFast(); + const PxU32 nbVertices = triangleMesh->getNbVerticesFast(); + const PxVec3* vertices = triangleMesh->getVerticesFast(); + const void* indices = triangleMesh->getTrianglesFast(); + const bool has16Bit = triangleMesh->has16BitIndices(); + + // PT: TODO: don't render the same edge multiple times + + PxU32* results = NULL; + if(useCullBox) + { + const Gu::Box worldBox( + (cullbox.maximum + cullbox.minimum)*0.5f, + (cullbox.maximum - cullbox.minimum)*0.5f, + PxMat33(PxIdentity)); + + // PT: TODO: use the callback version here to avoid allocating this huge array + results = reinterpret_cast(PX_ALLOC_TEMP(sizeof(PxU32)*nbTriangles, "tmp triangle indices")); + LimitedResults limitedResults(results, nbTriangles, 0); + Midphase::intersectBoxVsMesh(worldBox, *triangleMesh, pose, geometry.scale, &limitedResults); + nbTriangles = limitedResults.mNbResults; + + if(visualizeShapes) + { + const PxU32 scolor = gCollisionShapeColor; + + out << midt << scolor; // PT: no need to output this for each segment! + + PxDebugLine* segments = out.reserveSegments(nbTriangles*3); + for(PxU32 i=0; i(PX_ALLOC(sizeof(PxVec3)*nbVertices, "PxVec3")); + for(PxU32 i=0;igetExtraTrigData()) + visualizeActiveEdges(out, *triangleMesh, nbTriangles, results, absPose); + + if(results) + PX_FREE(results); +} + +static void visualizeHeightField(const PxHeightFieldGeometry& hfGeometry, RenderOutput& out, const PxTransform& absPose, const PxBounds3& cullbox, bool useCullBox) +{ + const HeightField* heightfield = static_cast(hfGeometry.heightField); + + // PT: TODO: the debug viz for HFs is minimal at the moment... + const PxU32 scolor = gCollisionShapeColor; + const PxMat44 midt = PxMat44(PxIdentity); + + HeightFieldUtil hfUtil(hfGeometry); + + const PxU32 nbRows = heightfield->getNbRowsFast(); + const PxU32 nbColumns = heightfield->getNbColumnsFast(); + const PxU32 nbVerts = nbRows * nbColumns; + const PxU32 nbTriangles = 2 * nbVerts; + + out << midt << scolor; // PT: no need to output the same matrix/color for each triangle + + if(useCullBox) + { + const PxTransform pose0((cullbox.maximum + cullbox.minimum)*0.5f); + const PxBoxGeometry boxGeometry((cullbox.maximum - cullbox.minimum)*0.5f); + + PxU32* results = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTriangles, "tmp triangle indices")); + + bool overflow = false; + PxU32 nbTouchedTris = PxMeshQuery::findOverlapHeightField(boxGeometry, pose0, hfGeometry, absPose, results, nbTriangles, 0, overflow); + + PxDebugLine* segments = out.reserveSegments(nbTouchedTris*3); + + for(PxU32 i=0; iisValidTriangle(index) && heightfield->getTriangleMaterial(index) != PxHeightFieldMaterial::eHOLE) + { + outputTriangle(segments, currentTriangle.verts[0], currentTriangle.verts[1], currentTriangle.verts[2], scolor); + segments+=3; + } + } + PX_FREE(results); + } + else + { + // PT: transform vertices only once + PxVec3* tmpVerts = reinterpret_cast(PX_ALLOC(sizeof(PxVec3)*nbVerts, "PxVec3")); + // PT: TODO: optimize the following line + for(PxU32 i=0;igetVertex(i))); + + for(PxU32 i=0; iisValidTriangle(i) && heightfield->getTriangleMaterial(i) != PxHeightFieldMaterial::eHOLE) + { + PxU32 vi0, vi1, vi2; + heightfield->getTriangleVertexIndices(i, vi0, vi1, vi2); + + PxDebugLine* segments = out.reserveSegments(3); + outputTriangle(segments, tmpVerts[vi0], tmpVerts[vi1], tmpVerts[vi2], scolor); + } + } + PX_FREE(tmpVerts); + } +} + +static void visualize(const PxGeometry& geometry, RenderOutput& out, const PxTransform& absPose, const PxBounds3& cullbox, const PxReal fscale, bool visualizeShapes, bool visualizeEdges, bool useCullBox) +{ + // triangle meshes can render active edges or face normals, but for other types we can just early out if there are no collision shapes + if(!visualizeShapes && geometry.getType() != PxGeometryType::eTRIANGLEMESH) + return; + + switch(geometry.getType()) + { + case PxGeometryType::eSPHERE: + visualizeSphere(static_cast(geometry), out, absPose); + break; + case PxGeometryType::eBOX: + visualizeBox(static_cast(geometry), out, absPose); + break; + case PxGeometryType::ePLANE: + visualizePlane(static_cast(geometry), out, absPose); + break; + case PxGeometryType::eCAPSULE: + visualizeCapsule(static_cast(geometry), out, absPose); + break; + case PxGeometryType::eCONVEXMESH: + visualizeConvexMesh(static_cast(geometry), out, absPose); + break; + case PxGeometryType::eTRIANGLEMESH: + visualizeTriangleMesh(static_cast(geometry), out, absPose, cullbox, fscale, visualizeShapes, visualizeEdges, useCullBox); + break; + case PxGeometryType::eHEIGHTFIELD: + visualizeHeightField(static_cast(geometry), out, absPose, cullbox, useCullBox); + break; + case PxGeometryType::eINVALID: + break; + case PxGeometryType::eGEOMETRY_COUNT: + break; + } +} + +void NpShapeManager::visualize(RenderOutput& out, NpScene* scene, const PxRigidActor& actor) +{ + const PxReal scale = scene->getVisualizationParameter(PxVisualizationParameter::eSCALE); + if(!scale) + return; + + const PxU32 nbShapes = getNbShapes(); + NpShape*const* PX_RESTRICT shapes = getShapes(); + + const bool visualizeCompounds = (nbShapes>1) && scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_COMPOUNDS)!=0.0f; + + // PT: moved all these out of the loop, no need to grab them once per shape + const PxBounds3& cullbox = scene->getScene().getVisualizationCullingBox(); + const bool visualizeAABBs = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_AABBS)!=0.0f; + const bool visualizeShapes = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_SHAPES)!=0.0f; + const bool visualizeEdges = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_EDGES)!=0.0f; + const float fNormals = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_FNORMALS); + const bool visualizeFNormals = fNormals!=0.0f; + const bool visualizeCollision = visualizeShapes || visualizeFNormals || visualizeEdges; + const bool useCullBox = !cullbox.isEmpty(); + const bool needsShapeBounds0 = visualizeCompounds || (visualizeCollision && useCullBox); + const PxReal collisionAxes = scale * scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_AXES); + const PxReal fscale = scale * fNormals; + + const PxTransform actorPose = actor.getGlobalPose(); + + PxBounds3 compoundBounds(PxBounds3::empty()); + for(PxU32 i=0;igetScbShape(); + + const PxTransform absPose = actorPose * scbShape.getShape2Actor(); + const PxGeometry& geom = scbShape.getGeometry(); + + const bool shapeDebugVizEnabled = scbShape.getFlags() & PxShapeFlag::eVISUALIZATION; + + const bool needsShapeBounds = needsShapeBounds0 || (visualizeAABBs && shapeDebugVizEnabled); + const PxBounds3 currentShapeBounds = needsShapeBounds ? Gu::computeBounds(geom, absPose) : PxBounds3::empty(); + + if(shapeDebugVizEnabled) + { + if(visualizeAABBs) + out << PxU32(PxDebugColor::eARGB_YELLOW) << PxMat44(PxIdentity) << DebugBox(currentShapeBounds); + + if(collisionAxes != 0.0f) + out << PxMat44(absPose) << DebugBasis(PxVec3(collisionAxes), 0xcf0000, 0x00cf00, 0x0000cf); + + if(visualizeCollision) + { + if(!useCullBox || cullbox.intersects(currentShapeBounds)) + ::visualize(geom, out, absPose, cullbox, fscale, visualizeShapes, visualizeEdges, useCullBox); + } + } + + if(visualizeCompounds) + compoundBounds.include(currentShapeBounds); + } + if(visualizeCompounds && !compoundBounds.isEmpty()) + out << gCollisionShapeColor << PxMat44(PxIdentity) << DebugBox(compoundBounds); +} +#endif // PX_ENABLE_DEBUG_VISUALIZATION diff --git a/src/PhysX/physx/source/physx/src/NpShapeManager.h b/src/PhysX/physx/source/physx/src/NpShapeManager.h new file mode 100644 index 000000000..52a840369 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpShapeManager.h @@ -0,0 +1,134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_SHAPE_MANAGER +#define PX_PHYSICS_NP_SHAPE_MANAGER + +#include "NpShape.h" +#include "CmPtrTable.h" +#include "SqSceneQueryManager.h" +#include "GuBVHStructure.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +namespace physx +{ + +namespace Sq +{ + class SceneQueryManager; + class PruningStructure; +} + +class NpScene; + +class NpShapeManager : public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + static void getBinaryMetaData(PxOutputStream& stream); + NpShapeManager(const PxEMPTY); + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); +//~PX_SERIALIZATION + NpShapeManager(); + ~NpShapeManager(); + + PX_FORCE_INLINE PxU32 getNbShapes() const { return mShapes.getCount(); } + PX_FORCE_INLINE NpShape* const* getShapes() const { return reinterpret_cast(mShapes.getPtrs()); } + PxU32 getShapes(PxShape** buffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + void attachShape(NpShape& shape, PxRigidActor& actor); + bool detachShape(NpShape& s, PxRigidActor &actor, bool wakeOnLostTouch); + void detachAll(NpScene *scene, const PxRigidActor& actor); + + void teardownSceneQuery(Sq::SceneQueryManager& sqManager, const NpShape& shape); + void setupSceneQuery(Sq::SceneQueryManager& sqManager, const PxRigidActor& actor, const NpShape& shape); + + void addPrunerShape(Sq::SceneQueryManager& sqManager, PxU32 index, const NpShape& shape, const PxRigidActor& actor, bool dynamic, const PxBounds3* bound, bool hasPrunerStructure); + + PX_FORCE_INLINE void setPrunerData(PxU32 index, Sq::PrunerData data) + { + PX_ASSERT(index(data); + } + + PX_FORCE_INLINE Sq::PrunerData getPrunerData(PxU32 index) const + { + PX_ASSERT(indexstartWrite(mAllowReentry)) + { + case NpScene::StartWriteResult::eOK: + break; + case NpScene::StartWriteResult::eNO_LOCK: + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "An API write call (%s) was made from thread %d but PxScene::lockWrite() was not called first, note that " + "when PxSceneFlag::eREQUIRE_RW_LOCK is enabled all API reads and writes must be " + "wrapped in the appropriate locks.", mName, PxU32(Ps::Thread::getId())); + break; + case NpScene::StartWriteResult::eRACE_DETECTED: + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Concurrent API write call or overlapping API read and write call detected during %s from thread %d! " + "Note that write operations to the SDK must be sequential, i.e., no overlap with " + "other write or read calls, else the resulting behavior is undefined. " + "Also note that API writes during a callback function are not permitted.", mName, PxU32(Ps::Thread::getId())); + break; + + case NpScene::StartWriteResult::eIN_FETCHRESULTS: + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Illegal write call detected in %s from thread %d during split fetchResults! " + "Note that write operations to the SDK are not permitted between the start of fetchResultsStart() and end of fetchResultsFinish(). " + "Behavior will be undefined. ", mName, PxU32(Ps::Thread::getId())); + break; + } + + // Record the NpScene read/write error counter which is + // incremented any time a NpScene::startWrite/startRead fails + // (see destructor for additional error checking based on this count) + mErrorCount = mScene->getReadWriteErrorCount(); + } +} + + +NpWriteCheck::~NpWriteCheck() +{ + if (mScene) + { + // By checking if the NpScene::mConcurrentErrorCount has been incremented + // we can detect if an erroneous read/write was performed during + // this objects lifetime. In this case we also print this function's + // details so that the user can see which two API calls overlapped + if (mScene->getReadWriteErrorCount() != mErrorCount && !(mScene->getScene().getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Leaving %s on thread %d, an overlapping API read or write by another thread was detected.", mName, PxU32(Ps::Thread::getId())); + } + + mScene->stopWrite(mAllowReentry); + } +} diff --git a/src/PhysX/physx/source/physx/src/NpWriteCheck.h b/src/PhysX/physx/source/physx/src/NpWriteCheck.h new file mode 100644 index 000000000..5095a4c49 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpWriteCheck.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef NP_WRITE_CHECK_H +#define NP_WRITE_CHECK_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + +class NpScene; + +// RAII wrapper around the PxScene::startWrite() method, note that this +// object does not acquire any scene locks, it is an error checking only mechanism +class NpWriteCheck +{ +public: + NpWriteCheck(NpScene* scene, const char* functionName, bool allowReentry=true); + ~NpWriteCheck(); + +private: + + NpScene* mScene; + const char* mName; + bool mAllowReentry; + PxU32 mErrorCount; +}; + +#if PX_DEBUG || PX_CHECKED + // Creates a scoped write check object that detects whether appropriate scene locks + // have been acquired and checks if reads/writes overlap, this macro should typically + // be placed at the beginning of any non-const API methods that are not multi-thread safe. + // By default re-entrant write calls by the same thread are allowed, the error conditions + // checked can be summarized as: + + // 1. PxSceneFlag::eREQUIRE_RW_LOCK was specified but PxScene::lockWrite() was not yet called + // 2. Other threads were already reading, or began reading during the object lifetime + // 3. Other threads were already writing, or began writing during the object lifetime + #define NP_WRITE_CHECK(npScenePtr) NpWriteCheck npWriteCheck(npScenePtr, __FUNCTION__); + + // Creates a scoped write check object that disallows re-entrant writes, this is used by + // the NpScene::simulate method to detect when callbacks make write calls to the API + #define NP_WRITE_CHECK_NOREENTRY(npScenePtr) NpWriteCheck npWriteCheck(npScenePtr, __FUNCTION__, false); +#else + #define NP_WRITE_CHECK(npScenePtr) + #define NP_WRITE_CHECK_NOREENTRY(npScenePtr) +#endif + +} + +#endif // NP_WRITE_CHECK_H diff --git a/src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h b/src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h new file mode 100644 index 000000000..552b95a0b --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h @@ -0,0 +1,82 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_META_DATA_PVD_BINDING_DATA_H +#define PX_META_DATA_PVD_BINDING_DATA_H +#if PX_SUPPORT_PVD +#include "foundation/PxSimpleTypes.h" +#include "PsArray.h" +#include "PsHashSet.h" +#include "PsHashMap.h" + +namespace physx +{ +namespace Vd +{ +using namespace physx::shdfnd; + +typedef HashSet OwnerActorsValueType; +typedef HashMap OwnerActorsMap; + +struct PvdMetaDataBindingData : public UserAllocated +{ + Array mTempU8Array; + Array mActors; + Array mArticulations; + Array mArticulationLinks; + HashSet mSleepingActors; + OwnerActorsMap mOwnerActorsMap; + + PvdMetaDataBindingData() + : mTempU8Array(PX_DEBUG_EXP("TempU8Array")) + , mActors(PX_DEBUG_EXP("PxActor")) + , mArticulations(PX_DEBUG_EXP("Articulations")) + , mArticulationLinks(PX_DEBUG_EXP("ArticulationLinks")) + , mSleepingActors(PX_DEBUG_EXP("SleepingActors")) + { + } + + template + TDataType* allocateTemp(PxU32 numItems) + { + mTempU8Array.resize(numItems * sizeof(TDataType)); + if(numItems) + return reinterpret_cast(mTempU8Array.begin()); + else + return NULL; + } + + DataRef tempToRef() + { + return DataRef(mTempU8Array.begin(), mTempU8Array.size()); + } +}; +} +} +#endif // PX_SUPPORT_PVD +#endif diff --git a/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp new file mode 100644 index 000000000..ee812cd5f --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp @@ -0,0 +1,1635 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// suppress LNK4221 +#include "foundation/PxPreprocessor.h" +PX_DUMMY_SYMBOL + +#if PX_SUPPORT_PVD + +#include "foundation/PxSimpleTypes.h" +#include "foundation/Px.h" + +#include "PxMetaDataObjects.h" +#include "PxPvdDataStream.h" +#include "PxScene.h" +#include "ScBodyCore.h" +#include "PvdMetaDataExtensions.h" +#include "PvdMetaDataPropertyVisitor.h" +#include "PvdMetaDataDefineProperties.h" +#include "PvdMetaDataBindingData.h" +#include "PxRigidDynamic.h" +#include "PxArticulation.h" +#include "PxArticulationLink.h" +#include "NpScene.h" +#include "NpPhysics.h" + +#include "PvdTypeNames.h" +#include "PvdMetaDataPvdBinding.h" + +using namespace physx; +using namespace Sc; +using namespace Vd; +using namespace Sq; + +namespace physx +{ +namespace Vd +{ + +struct NameValuePair +{ + const char* mName; + PxU32 mValue; +}; + +static const NameValuePair g_physx_Sq_SceneQueryID__EnumConversion[] = { + { "QUERY_RAYCAST_ANY_OBJECT", PxU32(QueryID::QUERY_RAYCAST_ANY_OBJECT) }, + { "QUERY_RAYCAST_CLOSEST_OBJECT", PxU32(QueryID::QUERY_RAYCAST_CLOSEST_OBJECT) }, + { "QUERY_RAYCAST_ALL_OBJECTS", PxU32(QueryID::QUERY_RAYCAST_ALL_OBJECTS) }, + { "QUERY_OVERLAP_SPHERE_ALL_OBJECTS", PxU32(QueryID::QUERY_OVERLAP_SPHERE_ALL_OBJECTS) }, + { "QUERY_OVERLAP_AABB_ALL_OBJECTS", PxU32(QueryID::QUERY_OVERLAP_AABB_ALL_OBJECTS) }, + { "QUERY_OVERLAP_OBB_ALL_OBJECTS", PxU32(QueryID::QUERY_OVERLAP_OBB_ALL_OBJECTS) }, + { "QUERY_OVERLAP_CAPSULE_ALL_OBJECTS", PxU32(QueryID::QUERY_OVERLAP_CAPSULE_ALL_OBJECTS) }, + { "QUERY_OVERLAP_CONVEX_ALL_OBJECTS", PxU32(QueryID::QUERY_OVERLAP_CONVEX_ALL_OBJECTS) }, + { "QUERY_LINEAR_OBB_SWEEP_CLOSEST_OBJECT", PxU32(QueryID::QUERY_LINEAR_OBB_SWEEP_CLOSEST_OBJECT) }, + { "QUERY_LINEAR_CAPSULE_SWEEP_CLOSEST_OBJECT", PxU32(QueryID::QUERY_LINEAR_CAPSULE_SWEEP_CLOSEST_OBJECT) }, + { "QUERY_LINEAR_CONVEX_SWEEP_CLOSEST_OBJECT", PxU32(QueryID::QUERY_LINEAR_CONVEX_SWEEP_CLOSEST_OBJECT) }, + { NULL, 0 } +}; + +struct SceneQueryIDConvertor +{ + const NameValuePair* NameConversion; + SceneQueryIDConvertor() : NameConversion(g_physx_Sq_SceneQueryID__EnumConversion) + { + } +}; + +PvdMetaDataBinding::PvdMetaDataBinding() : mBindingData(PX_NEW(PvdMetaDataBindingData)()) +{ +} + +PvdMetaDataBinding::~PvdMetaDataBinding() +{ + for(OwnerActorsMap::Iterator iter = mBindingData->mOwnerActorsMap.getIterator(); !iter.done(); iter++) + { + iter->second->~OwnerActorsValueType(); + PX_FREE(iter->second); + } + + PX_DELETE(mBindingData); + mBindingData = NULL; +} + +template +static inline void definePropertyStruct(PvdDataStream& inStream, const char* pushName = NULL) +{ + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoValueStructDefine definitionObj(helper); + bool doPush = pushName && *pushName; + if(doPush) + definitionObj.pushName(pushName); + visitAllPvdProperties(definitionObj); + if(doPush) + definitionObj.popName(); + helper.addPropertyMessage(getPvdNamespacedNameForType(), getPvdNamespacedNameForType(), sizeof(TValueType)); +} + +template +static inline void createClassAndDefineProperties(PvdDataStream& inStream) +{ + inStream.createClass(); + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoDefine definitionObj(helper, getPvdNamespacedNameForType()); + visitAllPvdProperties(definitionObj); +} + +template +static inline void createClassDeriveAndDefineProperties(PvdDataStream& inStream) +{ + inStream.createClass(); + inStream.deriveClass(); + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoDefine definitionObj(helper, getPvdNamespacedNameForType()); + visitInstancePvdProperties(definitionObj); +} + +template +static inline void defineProperty(PvdDataStream& inStream, const char* inPropertyName, const char* semantic) +{ + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + // PxEnumTraits< TValueType > filterFlagsEnum; + TConvertSrc filterFlagsEnum; + const TConvertData* convertor = filterFlagsEnum.NameConversion; + + for(; convertor->mName != NULL; ++convertor) + helper.addNamedValue(convertor->mName, convertor->mValue); + + inStream.createProperty(inPropertyName, semantic, PropertyType::Scalar, helper.getNamedValues()); + helper.clearNamedValues(); +} + +template +static inline void definePropertyFlags(PvdDataStream& inStream, const char* inPropertyName) +{ + defineProperty(inStream, inPropertyName, "Bitflag"); +} +template +static inline void definePropertyEnums(PvdDataStream& inStream, const char* inPropertyName) +{ + defineProperty(inStream, inPropertyName, "Enumeration Value"); +} + +static PX_FORCE_INLINE void registerPvdRaycast(PvdDataStream& inStream) +{ + inStream.createClass(); + definePropertyEnums(inStream, "type"); + inStream.createProperty("filterData"); + definePropertyFlags, PxU32ToName>(inStream, "filterFlags"); + inStream.createProperty("origin"); + inStream.createProperty("unitDir"); + inStream.createProperty("distance"); + inStream.createProperty("hits_arrayName"); + inStream.createProperty("hits_baseIndex"); + inStream.createProperty("hits_count"); +} + +static PX_FORCE_INLINE void registerPvdSweep(PvdDataStream& inStream) +{ + inStream.createClass(); + definePropertyEnums(inStream, "type"); + definePropertyFlags, PxU32ToName>(inStream, "filterFlags"); + inStream.createProperty("unitDir"); + inStream.createProperty("distance"); + inStream.createProperty("geom_arrayName"); + inStream.createProperty("geom_baseIndex"); + inStream.createProperty("geom_count"); + inStream.createProperty("pose_arrayName"); + inStream.createProperty("pose_baseIndex"); + inStream.createProperty("pose_count"); + inStream.createProperty("filterData_arrayName"); + inStream.createProperty("filterData_baseIndex"); + inStream.createProperty("filterData_count"); + inStream.createProperty("hits_arrayName"); + inStream.createProperty("hits_baseIndex"); + inStream.createProperty("hits_count"); +} + +static PX_FORCE_INLINE void registerPvdOverlap(PvdDataStream& inStream) +{ + inStream.createClass(); + definePropertyEnums(inStream, "type"); + inStream.createProperty("filterData"); + definePropertyFlags, PxU32ToName>(inStream, "filterFlags"); + inStream.createProperty("pose"); + inStream.createProperty("geom_arrayName"); + inStream.createProperty("geom_baseIndex"); + inStream.createProperty("geom_count"); + inStream.createProperty("hits_arrayName"); + inStream.createProperty("hits_baseIndex"); + inStream.createProperty("hits_count"); +} + +static PX_FORCE_INLINE void registerPvdSqHit(PvdDataStream& inStream) +{ + inStream.createClass(); + inStream.createProperty("Shape"); + inStream.createProperty("Actor"); + inStream.createProperty("FaceIndex"); + definePropertyFlags, PxU32ToName>(inStream, "Flags"); + inStream.createProperty("Impact"); + inStream.createProperty("Normal"); + inStream.createProperty("Distance"); + inStream.createProperty("U"); + inStream.createProperty("V"); +} + +void PvdMetaDataBinding::registerSDKProperties(PvdDataStream& inStream) +{ + if (inStream.isClassExist()) + return; + // PxPhysics + { + inStream.createClass(); + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoDefine definitionObj(helper, getPvdNamespacedNameForType()); + helper.pushName("TolerancesScale"); + visitAllPvdProperties(definitionObj); + helper.popName(); + inStream.createProperty("Scenes", "children", PropertyType::Array); + inStream.createProperty("SharedShapes", "children", PropertyType::Array); + inStream.createProperty("Materials", "children", PropertyType::Array); + inStream.createProperty("HeightFields", "children", PropertyType::Array); + inStream.createProperty("ConvexMeshes", "children", PropertyType::Array); + inStream.createProperty("TriangleMeshes", "children", PropertyType::Array); + inStream.createProperty("Version.Major"); + inStream.createProperty("Version.Minor"); + inStream.createProperty("Version.Bugfix"); + inStream.createProperty("Version.Build"); + definePropertyStruct(inStream, "TolerancesScale"); + } + { // PxGeometry + inStream.createClass(); + inStream.createProperty("Shape", "parents", PropertyType::Scalar); + } + { // PxBoxGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxSphereGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxCapsuleGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxPlaneGeometry + createClassDeriveAndDefineProperties(inStream); + } + { // PxConvexMeshGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxTriangleMeshGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxHeightFieldGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + + // PxScene + { + // PT: TODO: why inline this for PvdContact but do PvdRaycast/etc in separate functions? + { // contact information + inStream.createClass(); + inStream.createProperty("Point"); + inStream.createProperty("Axis"); + inStream.createProperty("Shapes[0]"); + inStream.createProperty("Shapes[1]"); + inStream.createProperty("Separation"); + inStream.createProperty("NormalForce"); + inStream.createProperty("InternalFaceIndex[0]"); + inStream.createProperty("InternalFaceIndex[1]"); + inStream.createProperty("NormalForceValid"); + } + + registerPvdSqHit(inStream); + registerPvdRaycast(inStream); + registerPvdSweep(inStream); + registerPvdOverlap(inStream); + + inStream.createClass(); + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoDefine definitionObj(helper, getPvdNamespacedNameForType()); + visitAllPvdProperties(definitionObj); + helper.pushName("SimulationStatistics"); + visitAllPvdProperties(definitionObj); + helper.popName(); + inStream.createProperty("Physics", "parents", PropertyType::Scalar); + inStream.createProperty("Timestamp"); + inStream.createProperty("SimulateElapsedTime"); + definePropertyStruct(inStream); + definePropertyStruct(inStream, "SimulationStatistics"); + inStream.createProperty("Contacts", "", PropertyType::Array); + + inStream.createProperty("SceneQueries.Overlaps", "", PropertyType::Array); + inStream.createProperty("SceneQueries.Sweeps", "", PropertyType::Array); + inStream.createProperty("SceneQueries.Hits", "", PropertyType::Array); + inStream.createProperty("SceneQueries.Raycasts", "", PropertyType::Array); + inStream.createProperty("SceneQueries.PoseList", "", PropertyType::Array); + inStream.createProperty("SceneQueries.FilterDataList", "", PropertyType::Array); + inStream.createProperty("SceneQueries.GeometryList", "", PropertyType::Array); + + inStream.createProperty("BatchedQueries.Overlaps", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.Sweeps", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.Hits", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.Raycasts", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.PoseList", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.FilterDataList", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.GeometryList", "", PropertyType::Array); + + inStream.createProperty("RigidStatics", "children", PropertyType::Array); + inStream.createProperty("RigidDynamics", "children", PropertyType::Array); + inStream.createProperty("Articulations", "children", PropertyType::Array); + inStream.createProperty("Joints", "children", PropertyType::Array); + inStream.createProperty("Aggregates", "children", PropertyType::Array); + } + // PxMaterial + { + createClassAndDefineProperties(inStream); + definePropertyStruct(inStream); + inStream.createProperty("Physics", "parents", PropertyType::Scalar); + } + // PxHeightField + { + { + inStream.createClass(); + inStream.createProperty("Height"); + inStream.createProperty("MaterialIndex[0]"); + inStream.createProperty("MaterialIndex[1]"); + } + + inStream.createClass(); + // It is important the PVD fields match the RepX fields, so this has + // to be hand coded. + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoDefine definitionObj(helper, getPvdNamespacedNameForType()); + visitAllPvdProperties(definitionObj); + inStream.createProperty("Samples", "", PropertyType::Array); + inStream.createProperty("Physics", "parents", PropertyType::Scalar); + definePropertyStruct(inStream); + } + // PxConvexMesh + { + { // hull polygon data. + inStream.createClass(); + inStream.createProperty("NumVertices"); + inStream.createProperty("IndexBase"); + } + inStream.createClass(); + inStream.createProperty("Mass"); + inStream.createProperty("LocalInertia"); + inStream.createProperty("LocalCenterOfMass"); + inStream.createProperty("Points", "", PropertyType::Array); + inStream.createProperty("HullPolygons", "", PropertyType::Array); + inStream.createProperty("PolygonIndexes", "", PropertyType::Array); + inStream.createProperty("Physics", "parents", PropertyType::Scalar); + } + // PxTriangleMesh + { + inStream.createClass(); + inStream.createProperty("Points", "", PropertyType::Array); + inStream.createProperty("NbTriangles", "", PropertyType::Scalar); + inStream.createProperty("Triangles", "", PropertyType::Array); + inStream.createProperty("MaterialIndices", "", PropertyType::Array); + inStream.createProperty("Physics", "parents", PropertyType::Scalar); + } + { // PxShape + createClassAndDefineProperties(inStream); + definePropertyStruct(inStream); + inStream.createProperty("Geometry", "children"); + inStream.createProperty("Materials", "children", PropertyType::Array); + inStream.createProperty("Actor", "parents"); + } + // PxActor + { + createClassAndDefineProperties(inStream); + inStream.createProperty("Scene", "parents"); + } + // PxRigidActor + { + createClassDeriveAndDefineProperties(inStream); + inStream.createProperty("Shapes", "children", PropertyType::Array); + inStream.createProperty("Joints", "children", PropertyType::Array); + } + // PxRigidStatic + { + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxRigidBody + createClassDeriveAndDefineProperties(inStream); + } + // PxRigidDynamic + { + createClassDeriveAndDefineProperties(inStream); + // If anyone adds a 'getKinematicTarget' to PxRigidDynamic you can remove the line + // below (after the code generator has run). + inStream.createProperty("KinematicTarget"); + definePropertyStruct(inStream); + // Manually define the update struct. + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + /*struct PxRigidDynamicUpdateBlock + { + Transform GlobalPose; + Float3 LinearVelocity; + Float3 AngularVelocity; + PxU8 IsSleeping; + PxU8 padding[3]; + }; + */ + helper.pushName("GlobalPose"); + helper.addPropertyMessageArg(PX_OFFSET_OF_RT(PxRigidDynamicUpdateBlock, GlobalPose)); + helper.popName(); + helper.pushName("LinearVelocity"); + helper.addPropertyMessageArg(PX_OFFSET_OF_RT(PxRigidDynamicUpdateBlock, LinearVelocity)); + helper.popName(); + helper.pushName("AngularVelocity"); + helper.addPropertyMessageArg(PX_OFFSET_OF_RT(PxRigidDynamicUpdateBlock, AngularVelocity)); + helper.popName(); + helper.pushName("IsSleeping"); + helper.addPropertyMessageArg(PX_OFFSET_OF_RT(PxRigidDynamicUpdateBlock, IsSleeping)); + helper.popName(); + helper.addPropertyMessage(); + } + + // PxArticulationBase + { + createClassAndDefineProperties(inStream); + inStream.createProperty("Scene", "parents"); + inStream.createProperty("Links", "children", PropertyType::Array); + definePropertyStruct(inStream); + } + + //{ // PxArticulation + // createClassDeriveAndDefineProperties(inStream); + // definePropertyStruct(inStream); + //} + + { // PxArticulationLink + createClassDeriveAndDefineProperties(inStream); + inStream.createProperty("Parent", "parents"); + inStream.createProperty("Links", "children", PropertyType::Array); + inStream.createProperty("InboundJoint", "children"); + definePropertyStruct(inStream); + + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + /*struct PxArticulationLinkUpdateBlock + { + Transform GlobalPose; + Float3 LinearVelocity; + Float3 AngularVelocity; + }; + */ + helper.pushName("GlobalPose"); + helper.addPropertyMessageArg(PX_OFFSET_OF(PxArticulationLinkUpdateBlock, GlobalPose)); + helper.popName(); + helper.pushName("LinearVelocity"); + helper.addPropertyMessageArg(PX_OFFSET_OF(PxArticulationLinkUpdateBlock, LinearVelocity)); + helper.popName(); + helper.pushName("AngularVelocity"); + helper.addPropertyMessageArg(PX_OFFSET_OF(PxArticulationLinkUpdateBlock, AngularVelocity)); + helper.popName(); + helper.addPropertyMessage(); + } + { // PxArticulationJoint + createClassAndDefineProperties(inStream); + inStream.createProperty("Link", "parents"); + definePropertyStruct(inStream); + } + { // PxConstraint + createClassAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { + // PxAggregate + createClassAndDefineProperties(inStream); + inStream.createProperty("Scene", "parents"); + definePropertyStruct(inStream); + inStream.createProperty("Actors", "children", PropertyType::Array); + inStream.createProperty("Articulations", "children", PropertyType::Array); + } +} + +template +static void doSendAllProperties(PvdDataStream& inStream, const TDataType* inDatatype, const void* instanceId) +{ + TValueType theValues(inDatatype); + inStream.setPropertyMessage(instanceId, theValues); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxPhysics& inPhysics) +{ + PxTolerancesScale theScale(inPhysics.getTolerancesScale()); + doSendAllProperties(inStream, &theScale, &inPhysics); + inStream.setPropertyValue(&inPhysics, "Version.Major", PxU32(PX_PHYSICS_VERSION_MAJOR)); + inStream.setPropertyValue(&inPhysics, "Version.Minor", PxU32(PX_PHYSICS_VERSION_MINOR)); + inStream.setPropertyValue(&inPhysics, "Version.Bugfix", PxU32(PX_PHYSICS_VERSION_BUGFIX)); + +#if PX_CHECKED +#if defined(NDEBUG) + // This is a checked build + String buildType = "Checked"; +#elif defined(_DEBUG) + // This is a debug build + String buildType = "Debug"; +#endif +#elif PX_PROFILE + String buildType = "Profile"; +#elif defined(NDEBUG) + // This is a release build + String buildType = "Release"; +#endif + inStream.setPropertyValue(&inPhysics, "Version.Build", buildType); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxScene& inScene) +{ + PxPhysics& physics(const_cast(inScene).getPhysics()); + PxTolerancesScale theScale; + PxSceneDesc theDesc(theScale); + + { + theDesc.gravity = inScene.getGravity(); + + theDesc.simulationEventCallback = inScene.getSimulationEventCallback(); + theDesc.contactModifyCallback = inScene.getContactModifyCallback(); + theDesc.ccdContactModifyCallback = inScene.getCCDContactModifyCallback(); + + theDesc.filterShaderData = inScene.getFilterShaderData(); + theDesc.filterShaderDataSize = inScene.getFilterShaderDataSize(); + theDesc.filterShader = inScene.getFilterShader(); + theDesc.filterCallback = inScene.getFilterCallback(); + + theDesc.broadPhaseType = inScene.getBroadPhaseType(); + theDesc.broadPhaseCallback = inScene.getBroadPhaseCallback(); + + theDesc.limits = inScene.getLimits(); + + theDesc.frictionType = inScene.getFrictionType(); + + theDesc.bounceThresholdVelocity = inScene.getBounceThresholdVelocity(); + + theDesc.frictionOffsetThreshold = inScene.getFrictionOffsetThreshold(); + + theDesc.flags = inScene.getFlags(); + + theDesc.cpuDispatcher = inScene.getCpuDispatcher(); + theDesc.gpuDispatcher = inScene.getGpuDispatcher(); + + theDesc.staticStructure = inScene.getStaticStructure(); + theDesc.dynamicStructure = inScene.getDynamicStructure(); + theDesc.dynamicTreeRebuildRateHint = inScene.getDynamicTreeRebuildRateHint(); + + theDesc.userData = inScene.userData; + + theDesc.solverBatchSize = inScene.getSolverBatchSize(); + + // theDesc.nbContactDataBlocks = inScene.getNbContactDataBlocksUsed(); + // theDesc.maxNbContactDataBlocks = inScene.getMaxNbContactDataBlocksUsed(); + + theDesc.contactReportStreamBufferSize = inScene.getContactReportStreamBufferSize(); + + theDesc.ccdMaxPasses = inScene.getCCDMaxPasses(); + + // theDesc.simulationOrder = inScene.getSimulationOrder(); + + theDesc.wakeCounterResetValue = inScene.getWakeCounterResetValue(); + } + + PxSceneDescGeneratedValues theValues(&theDesc); + inStream.setPropertyMessage(&inScene, theValues); + // Create parent/child relationship. + inStream.setPropertyValue(&inScene, "Physics", reinterpret_cast(&physics)); + inStream.pushBackObjectRef(&physics, "Scenes", &inScene); +} + +void PvdMetaDataBinding::sendBeginFrame(PvdDataStream& inStream, const PxScene* inScene, PxReal simulateElapsedTime) +{ + inStream.beginSection(inScene, "frame"); + inStream.setPropertyValue(inScene, "Timestamp", inScene->getTimestamp()); + inStream.setPropertyValue(inScene, "SimulateElapsedTime", simulateElapsedTime); +} + +template +struct NullConverter +{ + void operator()(TDataType& data, const TDataType& src) + { + data = src; + } +}; + +template > +class ScopedPropertyValueSender +{ + TTargetType mStack[T_NUM_ITEMS]; + TTargetType* mCur; + const TTargetType* mEnd; + PvdDataStream& mStream; + + public: + ScopedPropertyValueSender(PvdDataStream& inStream, const void* inObj, String name) + : mCur(mStack), mEnd(&mStack[T_NUM_ITEMS]), mStream(inStream) + { + mStream.beginSetPropertyValue(inObj, name, getPvdNamespacedNameForType()); + } + + ~ScopedPropertyValueSender() + { + if(mStack != mCur) + { + PxU32 size = sizeof(TTargetType) * PxU32(mCur - mStack); + mStream.appendPropertyValueData(DataRef(reinterpret_cast(mStack), size)); + } + mStream.endSetPropertyValue(); + } + + void append(const TSourceType& data) + { + Converter()(*mCur, data); + if(mCur < mEnd - 1) + ++mCur; + else + { + mStream.appendPropertyValueData(DataRef(reinterpret_cast(mStack), sizeof mStack)); + mCur = mStack; + } + } + + private: + ScopedPropertyValueSender& operator=(const ScopedPropertyValueSender&); +}; + +void PvdMetaDataBinding::sendContacts(PvdDataStream& inStream, const PxScene& inScene) +{ + inStream.setPropertyValue(&inScene, "Contacts", DataRef(), getPvdNamespacedNameForType()); +} + +void PvdMetaDataBinding::sendStats(PvdDataStream& inStream, const PxScene* inScene) +{ + PxSimulationStatistics theStats; + inScene->getSimulationStatistics(theStats); + + PxSimulationStatisticsGeneratedValues values(&theStats); + inStream.setPropertyMessage(inScene, values); +} + +struct PvdContactConverter +{ + void operator()(PvdContact& data, const Sc::Contact& src) + { + data.point = src.point; + data.axis = src.normal; + data.shape0 = src.shape0; + data.shape1 = src.shape1; + data.separation = src.separation; + data.normalForce = src.normalForce; + data.internalFaceIndex0 = src.faceIndex0; + data.internalFaceIndex1 = src.faceIndex1; + data.normalForceAvailable = src.normalForceAvailable; + } +}; + +void PvdMetaDataBinding::sendContacts(PvdDataStream& inStream, const PxScene& inScene, Array& inContacts) +{ + ScopedPropertyValueSender sender(inStream, &inScene, "Contacts"); + + for(PxU32 i = 0; i < inContacts.size(); i++) + { + sender.append(inContacts[i]); + } +} + +void PvdMetaDataBinding::sendEndFrame(PvdDataStream& inStream, const PxScene* inScene) +{ + //flush other client + inStream.endSection(inScene, "frame"); +} + +template +static void addPhysicsGroupProperty(PvdDataStream& inStream, const char* groupName, const TDataType& inData, const PxPhysics& ownerPhysics) +{ + inStream.setPropertyValue(&inData, "Physics", reinterpret_cast(&ownerPhysics)); + inStream.pushBackObjectRef(&ownerPhysics, groupName, &inData); + // Buffer type objects *have* to be flushed directly out once created else scene creation doesn't work. +} + +template +static void removePhysicsGroupProperty(PvdDataStream& inStream, const char* groupName, const TDataType& inData, const PxPhysics& ownerPhysics) +{ + inStream.removeObjectRef(&ownerPhysics, groupName, &inData); + inStream.destroyInstance(&inData); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxMaterial& inMaterial, const PxPhysics& ownerPhysics) +{ + inStream.createInstance(&inMaterial); + sendAllProperties(inStream, inMaterial); + addPhysicsGroupProperty(inStream, "Materials", inMaterial, ownerPhysics); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxMaterial& inMaterial) +{ + PxMaterialGeneratedValues values(&inMaterial); + inStream.setPropertyMessage(&inMaterial, values); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxMaterial& inMaterial, const PxPhysics& ownerPhysics) +{ + removePhysicsGroupProperty(inStream, "Materials", inMaterial, ownerPhysics); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxHeightField& inData) +{ + PxHeightFieldDesc theDesc; + // Save the height field to desc. + theDesc.nbRows = inData.getNbRows(); + theDesc.nbColumns = inData.getNbColumns(); + theDesc.format = inData.getFormat(); + theDesc.samples.stride = inData.getSampleStride(); + theDesc.samples.data = NULL; + theDesc.convexEdgeThreshold = inData.getConvexEdgeThreshold(); + theDesc.flags = inData.getFlags(); + + PxU32 theCellCount = inData.getNbRows() * inData.getNbColumns(); + PxU32 theSampleStride = sizeof(PxHeightFieldSample); + PxU32 theSampleBufSize = theCellCount * theSampleStride; + mBindingData->mTempU8Array.resize(theSampleBufSize); + PxHeightFieldSample* theSamples = reinterpret_cast(mBindingData->mTempU8Array.begin()); + inData.saveCells(theSamples, theSampleBufSize); + theDesc.samples.data = theSamples; + PxHeightFieldDescGeneratedValues values(&theDesc); + inStream.setPropertyMessage(&inData, values); + PxHeightFieldSample* theSampleData = reinterpret_cast(mBindingData->mTempU8Array.begin()); + inStream.setPropertyValue(&inData, "Samples", theSampleData, theCellCount); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxHeightField& inData, const PxPhysics& ownerPhysics) +{ + inStream.createInstance(&inData); + sendAllProperties(inStream, inData); + addPhysicsGroupProperty(inStream, "HeightFields", inData, ownerPhysics); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxHeightField& inData, const PxPhysics& ownerPhysics) +{ + removePhysicsGroupProperty(inStream, "HeightFields", inData, ownerPhysics); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxConvexMesh& inData, const PxPhysics& ownerPhysics) +{ + inStream.createInstance(&inData); + PxReal mass; + PxMat33 localInertia; + PxVec3 localCom; + inData.getMassInformation(mass, reinterpret_cast(localInertia), localCom); + inStream.setPropertyValue(&inData, "Mass", mass); + inStream.setPropertyValue(&inData, "LocalInertia", localInertia); + inStream.setPropertyValue(&inData, "LocalCenterOfMass", localCom); + + // update arrays: + // vertex Array: + { + const PxVec3* vertexPtr = inData.getVertices(); + const PxU32 numVertices = inData.getNbVertices(); + inStream.setPropertyValue(&inData, "Points", vertexPtr, numVertices); + } + + // HullPolyArray: + PxU16 maxIndices = 0; + { + + PxU32 numPolygons = inData.getNbPolygons(); + PvdHullPolygonData* tempData = mBindingData->allocateTemp(numPolygons); + // Get the polygon data stripping the plane equations + for(PxU32 index = 0; index < numPolygons; index++) + { + PxHullPolygon curOut; + inData.getPolygonData(index, curOut); + maxIndices = PxMax(maxIndices, PxU16(curOut.mIndexBase + curOut.mNbVerts)); + tempData[index].mIndexBase = curOut.mIndexBase; + tempData[index].mNumVertices = curOut.mNbVerts; + } + inStream.setPropertyValue(&inData, "HullPolygons", tempData, numPolygons); + } + + // poly index Array: + { + const PxU8* indices = inData.getIndexBuffer(); + inStream.setPropertyValue(&inData, "PolygonIndexes", indices, maxIndices); + } + addPhysicsGroupProperty(inStream, "ConvexMeshes", inData, ownerPhysics); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxConvexMesh& inData, const PxPhysics& ownerPhysics) +{ + removePhysicsGroupProperty(inStream, "ConvexMeshes", inData, ownerPhysics); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxTriangleMesh& inData, const PxPhysics& ownerPhysics) +{ + inStream.createInstance(&inData); + bool hasMatIndex = inData.getTriangleMaterialIndex(0) != 0xffff; + // update arrays: + // vertex Array: + { + const PxVec3* vertexPtr = inData.getVertices(); + const PxU32 numVertices = inData.getNbVertices(); + inStream.setPropertyValue(&inData, "Points", vertexPtr, numVertices); + } + + // index Array: + { + const bool has16BitIndices = inData.getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false; + const PxU32 numTriangles = inData.getNbTriangles(); + + inStream.setPropertyValue(&inData, "NbTriangles", numTriangles); + + const PxU32 numIndexes = numTriangles * 3; + const PxU8* trianglePtr = reinterpret_cast(inData.getTriangles()); + // We declared this type as a 32 bit integer above. + // PVD will automatically unsigned-extend data that is smaller than the target type. + if(has16BitIndices) + inStream.setPropertyValue(&inData, "Triangles", reinterpret_cast(trianglePtr), numIndexes); + else + inStream.setPropertyValue(&inData, "Triangles", reinterpret_cast(trianglePtr), numIndexes); + } + + // material Array: + if(hasMatIndex) + { + PxU32 numMaterials = inData.getNbTriangles(); + PxU16* matIndexData = mBindingData->allocateTemp(numMaterials); + for(PxU32 m = 0; m < numMaterials; m++) + matIndexData[m] = inData.getTriangleMaterialIndex(m); + inStream.setPropertyValue(&inData, "MaterialIndices", matIndexData, numMaterials); + } + addPhysicsGroupProperty(inStream, "TriangleMeshes", inData, ownerPhysics); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxTriangleMesh& inData, const PxPhysics& ownerPhysics) +{ + removePhysicsGroupProperty(inStream, "TriangleMeshes", inData, ownerPhysics); +} + +template +void PvdMetaDataBinding::registrarPhysicsObject(PvdDataStream&, const TDataType&, PsPvd*) +{ +} + +template <> +void PvdMetaDataBinding::registrarPhysicsObject(PvdDataStream& inStream, const PxConvexMeshGeometry& geom, PsPvd* pvd) +{ + if(pvd->registerObject(geom.convexMesh)) + createInstance(inStream, *geom.convexMesh, PxGetPhysics()); +} + +template <> +void PvdMetaDataBinding::registrarPhysicsObject(PvdDataStream& inStream, const PxTriangleMeshGeometry& geom, PsPvd* pvd) +{ + if(pvd->registerObject(geom.triangleMesh)) + createInstance(inStream, *geom.triangleMesh, PxGetPhysics()); +} + +template <> +void PvdMetaDataBinding::registrarPhysicsObject(PvdDataStream& inStream, const PxHeightFieldGeometry& geom, PsPvd* pvd) +{ + if(pvd->registerObject(geom.heightField)) + createInstance(inStream, *geom.heightField, PxGetPhysics()); +} + +template +static void sendGeometry(PvdMetaDataBinding& metaBind, PvdDataStream& inStream, const PxShape& inShape, const TGeomType& geom, PsPvd* pvd) +{ + const void* geomInst = (reinterpret_cast(&inShape)) + 4; + inStream.createInstance(getPvdNamespacedNameForType(), geomInst); + metaBind.registrarPhysicsObject(inStream, geom, pvd); + TGeneratedValuesType values(&geom); + inStream.setPropertyMessage(geomInst, values); + inStream.setPropertyValue(&inShape, "Geometry", geomInst); + inStream.setPropertyValue(geomInst, "Shape", reinterpret_cast(&inShape)); +} + +static void setGeometry(PvdMetaDataBinding& metaBind, PvdDataStream& inStream, const PxShape& inObj, PsPvd* pvd) +{ + switch(inObj.getGeometryType()) + { +#define SEND_PVD_GEOM_TYPE(enumType, geomType, valueType) \ + case PxGeometryType::enumType: \ + { \ + Px##geomType geom; \ + inObj.get##geomType(geom); \ + sendGeometry(metaBind, inStream, inObj, geom, pvd); \ + } \ + break; + SEND_PVD_GEOM_TYPE(eSPHERE, SphereGeometry, PxSphereGeometryGeneratedValues); + // Plane geometries don't have any properties, so this avoids using a property + // struct for them. + case PxGeometryType::ePLANE: + { + PxPlaneGeometry geom; + inObj.getPlaneGeometry(geom); + const void* geomInst = (reinterpret_cast(&inObj)) + 4; + inStream.createInstance(getPvdNamespacedNameForType(), geomInst); + inStream.setPropertyValue(&inObj, "Geometry", geomInst); + inStream.setPropertyValue(geomInst, "Shape", reinterpret_cast(&inObj)); + } + break; + SEND_PVD_GEOM_TYPE(eCAPSULE, CapsuleGeometry, PxCapsuleGeometryGeneratedValues); + SEND_PVD_GEOM_TYPE(eBOX, BoxGeometry, PxBoxGeometryGeneratedValues); + SEND_PVD_GEOM_TYPE(eCONVEXMESH, ConvexMeshGeometry, PxConvexMeshGeometryGeneratedValues); + SEND_PVD_GEOM_TYPE(eTRIANGLEMESH, TriangleMeshGeometry, PxTriangleMeshGeometryGeneratedValues); + SEND_PVD_GEOM_TYPE(eHEIGHTFIELD, HeightFieldGeometry, PxHeightFieldGeometryGeneratedValues); +#undef SEND_PVD_GEOM_TYPE + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ASSERT(false); + break; + } +} + +static void setMaterials(PvdMetaDataBinding& metaBing, PvdDataStream& inStream, const PxShape& inObj, PsPvd* pvd, PvdMetaDataBindingData* mBindingData) +{ + PxU32 numMaterials = inObj.getNbMaterials(); + PxMaterial** materialPtr = mBindingData->allocateTemp(numMaterials); + inObj.getMaterials(materialPtr, numMaterials); + for(PxU32 idx = 0; idx < numMaterials; ++idx) + { + if(pvd->registerObject(materialPtr[idx])) + metaBing.createInstance(inStream, *materialPtr[idx], PxGetPhysics()); + inStream.pushBackObjectRef(&inObj, "Materials", materialPtr[idx]); + } +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxShape& inObj, const PxRigidActor& owner, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + if(!inStream.isInstanceValid(&owner)) + return; + + const OwnerActorsMap::Entry* entry = mBindingData->mOwnerActorsMap.find(&inObj); + if(entry != NULL) + { + if(!mBindingData->mOwnerActorsMap[&inObj]->contains(&owner)) + mBindingData->mOwnerActorsMap[&inObj]->insert(&owner); + } + else + { + OwnerActorsValueType* data = reinterpret_cast( + PX_ALLOC(sizeof(OwnerActorsValueType), "mOwnerActorsMapValue")); //( 1 ); + OwnerActorsValueType* actors = PX_PLACEMENT_NEW(data, OwnerActorsValueType); + actors->insert(&owner); + + mBindingData->mOwnerActorsMap.insert(&inObj, actors); + } + + if(inStream.isInstanceValid(&inObj)) + { + inStream.pushBackObjectRef(&owner, "Shapes", &inObj); + return; + } + + inStream.createInstance(&inObj); + inStream.pushBackObjectRef(&owner, "Shapes", &inObj); + inStream.setPropertyValue(&inObj, "Actor", reinterpret_cast(&owner)); + sendAllProperties(inStream, inObj); + setGeometry(*this, inStream, inObj, pvd); + setMaterials(*this, inStream, inObj, pvd, mBindingData); + if(!inObj.isExclusive()) + inStream.pushBackObjectRef(&ownerPhysics, "SharedShapes", &inObj); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxShape& inObj) +{ + PxShapeGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} + +void PvdMetaDataBinding::releaseAndRecreateGeometry(PvdDataStream& inStream, const PxShape& inObj, PxPhysics& /*ownerPhysics*/, PsPvd* pvd) +{ + const void* geomInst = (reinterpret_cast(&inObj)) + 4; + inStream.destroyInstance(geomInst); + // Quick fix for HF modify, PxConvexMesh and PxTriangleMesh need recook, they should always be new if modified + if(inObj.getGeometryType() == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometry hfGeom; + inObj.getHeightFieldGeometry(hfGeom); + if(inStream.isInstanceValid(hfGeom.heightField)) + sendAllProperties(inStream, *hfGeom.heightField); + } + + setGeometry(*this, inStream, inObj, pvd); + + // Need update actor cause PVD takes actor-shape as a pair. + { + PxRigidActor* actor = inObj.getActor(); + if(actor != NULL) + { + + if(const PxRigidStatic* rgS = actor->is()) + sendAllProperties(inStream, *rgS); + else if(const PxRigidDynamic* rgD = actor->is()) + sendAllProperties(inStream, *rgD); + } + } +} + +void PvdMetaDataBinding::updateMaterials(PvdDataStream& inStream, const PxShape& inObj, PsPvd* pvd) +{ + // Clear the shape's materials array. + inStream.setPropertyValue(&inObj, "Materials", DataRef(), getPvdNamespacedNameForType()); + setMaterials(*this, inStream, inObj, pvd, mBindingData); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxShape& inObj, const PxRigidActor& owner) +{ + if(inStream.isInstanceValid(&inObj)) + { + inStream.removeObjectRef(&owner, "Shapes", &inObj); + + bool bDestroy = true; + const OwnerActorsMap::Entry* entry0 = mBindingData->mOwnerActorsMap.find(&inObj); + if(entry0 != NULL) + { + entry0->second->erase(&owner); + if(entry0->second->size() > 0) + bDestroy = false; + else + { + mBindingData->mOwnerActorsMap[&inObj]->~OwnerActorsValueType(); + PX_FREE(mBindingData->mOwnerActorsMap[&inObj]); + mBindingData->mOwnerActorsMap.erase(&inObj); + } + } + + if(bDestroy) + { + if(!inObj.isExclusive()) + inStream.removeObjectRef(&PxGetPhysics(), "SharedShapes", &inObj); + + const void* geomInst = (reinterpret_cast(&inObj)) + 4; + inStream.destroyInstance(geomInst); + inStream.destroyInstance(&inObj); + + const OwnerActorsMap::Entry* entry = mBindingData->mOwnerActorsMap.find(&inObj); + if(entry != NULL) + { + entry->second->~OwnerActorsValueType(); + PX_FREE(entry->second); + mBindingData->mOwnerActorsMap.erase(&inObj); + } + } + } +} + +template +static void addSceneGroupProperty(PvdDataStream& inStream, const char* groupName, const TDataType& inObj, const PxScene& inScene) +{ + inStream.createInstance(&inObj); + inStream.pushBackObjectRef(&inScene, groupName, &inObj); + inStream.setPropertyValue(&inObj, "Scene", reinterpret_cast(&inScene)); +} + +template +static void removeSceneGroupProperty(PvdDataStream& inStream, const char* groupName, const TDataType& inObj, const PxScene& inScene) +{ + inStream.removeObjectRef(&inScene, groupName, &inObj); + inStream.destroyInstance(&inObj); +} + +static void sendShapes(PvdMetaDataBinding& binding, PvdDataStream& inStream, const PxRigidActor& inObj, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + InlineArray shapeData; + PxU32 nbShapes = inObj.getNbShapes(); + shapeData.resize(nbShapes); + inObj.getShapes(shapeData.begin(), nbShapes); + for(PxU32 idx = 0; idx < nbShapes; ++idx) + binding.createInstance(inStream, *shapeData[idx], inObj, ownerPhysics, pvd); +} + +static void releaseShapes(PvdMetaDataBinding& binding, PvdDataStream& inStream, const PxRigidActor& inObj) +{ + InlineArray shapeData; + PxU32 nbShapes = inObj.getNbShapes(); + shapeData.resize(nbShapes); + inObj.getShapes(shapeData.begin(), nbShapes); + for(PxU32 idx = 0; idx < nbShapes; ++idx) + binding.destroyInstance(inStream, *shapeData[idx], inObj); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxRigidStatic& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + addSceneGroupProperty(inStream, "RigidStatics", inObj, ownerScene); + sendAllProperties(inStream, inObj); + sendShapes(*this, inStream, inObj, ownerPhysics, pvd); +} +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxRigidStatic& inObj) +{ + PxRigidStaticGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxRigidStatic& inObj, const PxScene& ownerScene) +{ + releaseShapes(*this, inStream, inObj); + removeSceneGroupProperty(inStream, "RigidStatics", inObj, ownerScene); +} +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxRigidDynamic& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + addSceneGroupProperty(inStream, "RigidDynamics", inObj, ownerScene); + sendAllProperties(inStream, inObj); + sendShapes(*this, inStream, inObj, ownerPhysics, pvd); +} +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxRigidDynamic& inObj) +{ + PxRigidDynamicGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxRigidDynamic& inObj, const PxScene& ownerScene) +{ + releaseShapes(*this, inStream, inObj); + removeSceneGroupProperty(inStream, "RigidDynamics", inObj, ownerScene); +} + +static void addChild(PvdDataStream& inStream, const void* inParent, const PxArticulationLink& inChild) +{ + inStream.pushBackObjectRef(inParent, "Links", &inChild); + inStream.setPropertyValue(&inChild, "Parent", inParent); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxArticulationBase& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + addSceneGroupProperty(inStream, "Articulations", inObj, ownerScene); + sendAllProperties(inStream, inObj); + PxU32 numLinks = inObj.getNbLinks(); + mBindingData->mArticulationLinks.resize(numLinks); + inObj.getLinks(mBindingData->mArticulationLinks.begin(), numLinks); + // From Dilip Sequiera: + /* + No, there can only be one root, and in all the code I wrote (which is not 100% of the HL code for + articulations), the index of a child is always > the index of the parent. + */ + + // Create all the links + for(PxU32 idx = 0; idx < numLinks; ++idx) + { + if(!inStream.isInstanceValid(mBindingData->mArticulationLinks[idx])) + createInstance(inStream, *mBindingData->mArticulationLinks[idx], ownerPhysics, pvd); + } + + // Setup the link graph + for(PxU32 idx = 0; idx < numLinks; ++idx) + { + PxArticulationLink* link = mBindingData->mArticulationLinks[idx]; + if(idx == 0) + addChild(inStream, &inObj, *link); + + PxU32 numChildren = link->getNbChildren(); + PxArticulationLink** children = mBindingData->allocateTemp(numChildren); + link->getChildren(children, numChildren); + for(PxU32 i = 0; i < numChildren; ++i) + addChild(inStream, link, *children[i]); + } +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxArticulationBase& inObj) +{ + PxArticulationBaseGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxArticulationBase& inObj, const PxScene& ownerScene) +{ + removeSceneGroupProperty(inStream, "Articulations", inObj, ownerScene); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxArticulationLink& inObj, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + inStream.createInstance(&inObj); + PxArticulationJointBase* joint(inObj.getInboundJoint()); + if(joint) + { + inStream.createInstance(joint); + inStream.setPropertyValue(&inObj, "InboundJoint", reinterpret_cast(joint)); + inStream.setPropertyValue(joint, "Link", reinterpret_cast(&inObj)); + sendAllProperties(inStream, *joint); + } + sendAllProperties(inStream, inObj); + sendShapes(*this, inStream, inObj, ownerPhysics, pvd); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxArticulationLink& inObj) +{ + PxArticulationLinkGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxArticulationLink& inObj) +{ + PxArticulationJointBase* joint(inObj.getInboundJoint()); + if(joint) + inStream.destroyInstance(joint); + releaseShapes(*this, inStream, inObj); + inStream.destroyInstance(&inObj); +} +// These are created as part of the articulation link's creation process, so outside entities don't need to +// create them. +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxArticulationJointBase& inObj) +{ + PxArticulationJointBaseGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} + +void PvdMetaDataBinding::originShift(PvdDataStream& inStream, const PxScene* inScene, PxVec3 shift) +{ + inStream.originShift(inScene, shift); +} + +template +static void updateActor(PvdDataStream& inStream, TActorType** actorGroup, PxU32 numActors, TOperator sleepingOp, PvdMetaDataBindingData& bindingData) +{ + TBlockType theBlock; + if(numActors == 0) + return; + for(PxU32 idx = 0; idx < numActors; ++idx) + { + TActorType* theActor(actorGroup[idx]); + bool sleeping = sleepingOp(theActor, theBlock); + bool wasSleeping = bindingData.mSleepingActors.contains(theActor); + + if(sleeping == false || sleeping != wasSleeping) + { + theBlock.GlobalPose = theActor->getGlobalPose(); + theBlock.AngularVelocity = theActor->getAngularVelocity(); + theBlock.LinearVelocity = theActor->getLinearVelocity(); + inStream.sendPropertyMessageFromGroup(theActor, theBlock); + if(sleeping != wasSleeping) + { + if(sleeping) + bindingData.mSleepingActors.insert(theActor); + else + bindingData.mSleepingActors.erase(theActor); + } + } + } +} + +struct RigidDynamicUpdateOp +{ + bool operator()(PxRigidDynamic* actor, PxRigidDynamicUpdateBlock& block) + { + bool sleeping = actor->isSleeping(); + block.IsSleeping = sleeping; + return sleeping; + } +}; + +struct ArticulationLinkUpdateOp +{ + bool sleeping; + ArticulationLinkUpdateOp(bool s) : sleeping(s) + { + } + bool operator()(PxArticulationLink*, PxArticulationLinkUpdateBlock&) + { + return sleeping; + } +}; + +void PvdMetaDataBinding::updateDynamicActorsAndArticulations(PvdDataStream& inStream, const PxScene* inScene, PvdVisualizer* linkJointViz) +{ + PX_COMPILE_TIME_ASSERT(sizeof(PxRigidDynamicUpdateBlock) == 14 * 4); + { + PxU32 actorCount = inScene->getNbActors(PxActorTypeFlag::eRIGID_DYNAMIC); + if(actorCount) + { + inStream.beginPropertyMessageGroup(); + mBindingData->mActors.resize(actorCount); + PxActor** theActors = mBindingData->mActors.begin(); + inScene->getActors(PxActorTypeFlag::eRIGID_DYNAMIC, theActors, actorCount); + updateActor(inStream, reinterpret_cast(theActors), actorCount, RigidDynamicUpdateOp(), *mBindingData); + inStream.endPropertyMessageGroup(); + } + } + { + PxU32 articulationCount = inScene->getNbArticulations(); + if(articulationCount) + { + mBindingData->mArticulations.resize(articulationCount); + PxArticulationBase** firstArticulation = mBindingData->mArticulations.begin(); + PxArticulationBase** lastArticulation = firstArticulation + articulationCount; + inScene->getArticulations(firstArticulation, articulationCount); + inStream.beginPropertyMessageGroup(); + for(; firstArticulation < lastArticulation; ++firstArticulation) + { + PxU32 linkCount = (*firstArticulation)->getNbLinks(); + bool sleeping = (*firstArticulation)->isSleeping(); + if(linkCount) + { + mBindingData->mArticulationLinks.resize(linkCount); + PxArticulationLink** theLink = mBindingData->mArticulationLinks.begin(); + (*firstArticulation)->getLinks(theLink, linkCount); + updateActor(inStream, theLink, linkCount, ArticulationLinkUpdateOp(sleeping), *mBindingData); + if(linkJointViz) + { + for(PxU32 idx = 0; idx < linkCount; ++idx) + linkJointViz->visualize(*theLink[idx]); + } + } + } + inStream.endPropertyMessageGroup(); + firstArticulation = mBindingData->mArticulations.begin(); + for(; firstArticulation < lastArticulation; ++firstArticulation) + inStream.setPropertyValue(*firstArticulation, "IsSleeping", (*firstArticulation)->isSleeping()); + } + } +} + +template +struct CollectionOperator +{ + Array& mTempArray; + const TObjType& mObject; + PvdDataStream& mStream; + + CollectionOperator(Array& ary, const TObjType& obj, PvdDataStream& stream) + : mTempArray(ary), mObject(obj), mStream(stream) + { + } + void pushName(const char*) + { + } + void popName() + { + } + template + void simpleProperty(PxU32 /*key*/, const TAccessor&) + { + } + template + void flagsProperty(PxU32 /*key*/, const TAccessor&, const PxU32ToName*) + { + } + + template + void handleCollection(const TCollectionProp& prop, NamespacedName dtype, PxU32 countMultiplier = 1) + { + PxU32 count = prop.size(&mObject); + mTempArray.resize(count * sizeof(TDataType)); + TColType* start = reinterpret_cast(mTempArray.begin()); + prop.get(&mObject, start, count * countMultiplier); + mStream.setPropertyValue(&mObject, prop.mName, DataRef(mTempArray.begin(), mTempArray.size()), dtype); + } + template + void handleCollection(const PxReadOnlyCollectionPropertyInfo& prop) + { + handleCollection(prop, getPvdNamespacedNameForType()); + } + // Enumerations or bitflags. + template + void handleCollection(const PxReadOnlyCollectionPropertyInfo& prop, const PxU32ToName*) + { + PX_COMPILE_TIME_ASSERT(sizeof(TColType) == sizeof(PxU32)); + handleCollection(prop, getPvdNamespacedNameForType()); + } + + private: + CollectionOperator& operator=(const CollectionOperator&); +}; + +// per frame update + +#define ENABLE_AGGREGATE_PVD_SUPPORT 1 +#ifdef ENABLE_AGGREGATE_PVD_SUPPORT + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxAggregate& inObj, const PxScene& ownerScene) +{ + addSceneGroupProperty(inStream, "Aggregates", inObj, ownerScene); + sendAllProperties(inStream, inObj); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxAggregate& inObj) +{ + PxAggregateGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxAggregate& inObj, const PxScene& ownerScene) +{ + removeSceneGroupProperty(inStream, "Aggregates", inObj, ownerScene); +} + +class ChangeOjectRefCmd : public PvdDataStream::PvdCommand +{ + ChangeOjectRefCmd& operator=(const ChangeOjectRefCmd&) + { + PX_ASSERT(0); + return *this; + } // PX_NOCOPY doesn't work for local classes + public: + const void* mInstance; + String mPropName; + const void* mPropObj; + const bool mPushBack; + + ChangeOjectRefCmd(const void* inInst, String inName, const void* inObj, bool pushBack) + : mInstance(inInst), mPropName(inName), mPropObj(inObj), mPushBack(pushBack) + { + } + + // Assigned is needed for copying + ChangeOjectRefCmd(const ChangeOjectRefCmd& other) + : PvdDataStream::PvdCommand(other), mInstance(other.mInstance), mPropName(other.mPropName), mPropObj(other.mPropObj), mPushBack(other.mPushBack) + { + } + + virtual bool canRun(PvdInstanceDataStream& inStream) + { + PX_ASSERT(inStream.isInstanceValid(mInstance)); + return inStream.isInstanceValid(mPropObj); + } + virtual void run(PvdInstanceDataStream& inStream) + { + if(!inStream.isInstanceValid(mInstance)) + return; + + if(mPushBack) + { + if(inStream.isInstanceValid(mPropObj)) + inStream.pushBackObjectRef(mInstance, mPropName, mPropObj); + } + else + { + // the called function will assert if propobj is already removed + inStream.removeObjectRef(mInstance, mPropName, mPropObj); + } + } +}; + +static void changeAggregateSubActors(PvdDataStream& inStream, const PxAggregate& inObj, const PxActor& inActor, bool pushBack) +{ + const PxArticulationLink* link = inActor.is(); + String propName = NULL; + const void* object = NULL; + if(link == NULL) + { + propName = "Actors"; + object = &inActor; + } + else if(link->getInboundJoint() == NULL) + { + propName = "Articulations"; + object = &link->getArticulation(); + } + else + return; + + ChangeOjectRefCmd* cmd = PX_PLACEMENT_NEW(inStream.allocateMemForCmd(sizeof(ChangeOjectRefCmd)), ChangeOjectRefCmd)(&inObj, propName, object, pushBack); + + if(cmd->canRun(inStream)) + cmd->run(inStream); + else + inStream.pushPvdCommand(*cmd); +} +void PvdMetaDataBinding::detachAggregateActor(PvdDataStream& inStream, const PxAggregate& inObj, const PxActor& inActor) +{ + changeAggregateSubActors(inStream, inObj, inActor, false); +} + +void PvdMetaDataBinding::attachAggregateActor(PvdDataStream& inStream, const PxAggregate& inObj, const PxActor& inActor) +{ + changeAggregateSubActors(inStream, inObj, inActor, true); +} +#else +void PvdMetaDataBinding::createInstance(PvdDataStream&, const PxAggregate&, const PxScene&, ObjectRegistrar&) +{ +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream&, const PxAggregate&) +{ +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream&, const PxAggregate&, const PxScene&) +{ +} + +void PvdMetaDataBinding::detachAggregateActor(PvdDataStream&, const PxAggregate&, const PxActor&) +{ +} + +void PvdMetaDataBinding::attachAggregateActor(PvdDataStream&, const PxAggregate&, const PxActor&) +{ +} +#endif + +template +static void sendSceneArray(PvdDataStream& inStream, const PxScene& inScene, const Ps::Array& inArray, const char* propName) +{ + if(0 == inArray.size()) + inStream.setPropertyValue(&inScene, propName, DataRef(), getPvdNamespacedNameForType()); + else + { + ScopedPropertyValueSender sender(inStream, &inScene, propName); + for(PxU32 i = 0; i < inArray.size(); ++i) + sender.append(inArray[i]); + } +} + +static void sendSceneArray(PvdDataStream& inStream, const PxScene& inScene, const Ps::Array& inArray, const char* propName) +{ + if(0 == inArray.size()) + inStream.setPropertyValue(&inScene, propName, DataRef(), getPvdNamespacedNameForType()); + else + { + ScopedPropertyValueSender sender(inStream, &inScene, propName); + for(PxU32 i = 0; i < inArray.size(); ++i) + { + if(!inStream.isInstanceValid(inArray[i].mShape) || !inStream.isInstanceValid(inArray[i].mActor)) + { + PvdSqHit hit = inArray[i]; + hit.mShape = NULL; + hit.mActor = NULL; + sender.append(hit); + } + else + sender.append(inArray[i]); + } + } +} + +void PvdMetaDataBinding::sendSceneQueries(PvdDataStream& inStream, const PxScene& inScene, PsPvd* pvd) +{ + if(!inStream.isConnected()) + return; + + const physx::NpScene& scene = static_cast(inScene); + + for(PxU32 i = 0; i < 2; i++) + { + PvdSceneQueryCollector& collector = ((i == 0) ? scene.getSingleSqCollector() : scene.getBatchedSqCollector()); + Ps::Mutex::ScopedLock lock(collector.getLock()); + + String propName = collector.getArrayName(collector.mPvdSqHits); + sendSceneArray(inStream, inScene, collector.mPvdSqHits, propName); + + propName = collector.getArrayName(collector.mPoses); + sendSceneArray(inStream, inScene, collector.mPoses, propName); + + propName = collector.getArrayName(collector.mFilterData); + sendSceneArray(inStream, inScene, collector.mFilterData, propName); + + const NamedArray& geometriesToDestroy = collector.getPrevFrameGeometries(); + propName = collector.getArrayName(geometriesToDestroy); + for(PxU32 k = 0; k < geometriesToDestroy.size(); ++k) + { + const PxGeometryHolder& inObj = geometriesToDestroy[k]; + inStream.removeObjectRef(&inScene, propName, &inObj); + inStream.destroyInstance(&inObj); + } + const Ps::Array& geometriesToCreate = collector.getCurrentFrameGeometries(); + for(PxU32 k = 0; k < geometriesToCreate.size(); ++k) + { + const PxGeometry& geometry = geometriesToCreate[k].any(); + switch(geometry.getType()) + { +#define SEND_PVD_GEOM_TYPE(enumType, TGeomType, TValueType) \ + case enumType: \ + { \ + const TGeomType& inObj = static_cast(geometry); \ + inStream.createInstance(getPvdNamespacedNameForType(), &inObj); \ + registrarPhysicsObject(inStream, inObj, pvd); \ + TValueType values(&inObj); \ + inStream.setPropertyMessage(&inObj, values); \ + inStream.pushBackObjectRef(&inScene, propName, &inObj); \ + } \ + break; + SEND_PVD_GEOM_TYPE(PxGeometryType::eBOX, PxBoxGeometry, PxBoxGeometryGeneratedValues) + SEND_PVD_GEOM_TYPE(PxGeometryType::eSPHERE, PxSphereGeometry, PxSphereGeometryGeneratedValues) + SEND_PVD_GEOM_TYPE(PxGeometryType::eCAPSULE, PxCapsuleGeometry, PxCapsuleGeometryGeneratedValues) + SEND_PVD_GEOM_TYPE(PxGeometryType::eCONVEXMESH, PxConvexMeshGeometry, PxConvexMeshGeometryGeneratedValues) +#undef SEND_PVD_GEOM_TYPE + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported scene query geometry type"); + break; + } + } + collector.prepareNextFrameGeometries(); + + propName = collector.getArrayName(collector.mAccumulatedRaycastQueries); + sendSceneArray(inStream, inScene, collector.mAccumulatedRaycastQueries, propName); + + propName = collector.getArrayName(collector.mAccumulatedOverlapQueries); + sendSceneArray(inStream, inScene, collector.mAccumulatedOverlapQueries, propName); + + propName = collector.getArrayName(collector.mAccumulatedSweepQueries); + sendSceneArray(inStream, inScene, collector.mAccumulatedSweepQueries, propName); + } +} +} +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h new file mode 100644 index 000000000..55df602b2 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h @@ -0,0 +1,151 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_META_DATA_PVD_BINDING_H +#define PX_META_DATA_PVD_BINDING_H + +#if PX_SUPPORT_PVD + +#include "PxPhysXConfig.h" +#include "PsArray.h" + +namespace physx +{ + namespace pvdsdk + { + class PsPvd; + class PvdDataStream; + struct PvdMetaDataBindingData; + } +} + +namespace physx +{ + +namespace Sc +{ +struct Contact; +} + +namespace Vd +{ + +using namespace physx::pvdsdk; + +class PvdVisualizer +{ + protected: + virtual ~PvdVisualizer() + { + } + + public: + virtual void visualize(PxArticulationLink& link) = 0; +}; + +class PvdMetaDataBinding +{ + PvdMetaDataBindingData* mBindingData; + + public: + PvdMetaDataBinding(); + ~PvdMetaDataBinding(); + void registerSDKProperties(PvdDataStream& inStream); + + void sendAllProperties(PvdDataStream& inStream, const PxPhysics& inPhysics); + + void sendAllProperties(PvdDataStream& inStream, const PxScene& inScene); + // per frame update + void sendBeginFrame(PvdDataStream& inStream, const PxScene* inScene, PxReal simulateElapsedTime); + void sendContacts(PvdDataStream& inStream, const PxScene& inScene, shdfnd::Array& inContacts); + void sendContacts(PvdDataStream& inStream, const PxScene& inScene); + void sendStats(PvdDataStream& inStream, const PxScene* inScene); + void sendSceneQueries(PvdDataStream& inStream, const PxScene& inScene, PsPvd* pvd); + void sendEndFrame(PvdDataStream& inStream, const PxScene* inScene); + + void createInstance(PvdDataStream& inStream, const PxMaterial& inMaterial, const PxPhysics& ownerPhysics); + void sendAllProperties(PvdDataStream& inStream, const PxMaterial& inMaterial); + void destroyInstance(PvdDataStream& inStream, const PxMaterial& inMaterial, const PxPhysics& ownerPhysics); + + void createInstance(PvdDataStream& inStream, const PxHeightField& inData, const PxPhysics& ownerPhysics); + void sendAllProperties(PvdDataStream& inStream, const PxHeightField& inData); + void destroyInstance(PvdDataStream& inStream, const PxHeightField& inData, const PxPhysics& ownerPhysics); + + void createInstance(PvdDataStream& inStream, const PxConvexMesh& inData, const PxPhysics& ownerPhysics); + void destroyInstance(PvdDataStream& inStream, const PxConvexMesh& inData, const PxPhysics& ownerPhysics); + + void createInstance(PvdDataStream& inStream, const PxTriangleMesh& inData, const PxPhysics& ownerPhysics); + void destroyInstance(PvdDataStream& inStream, const PxTriangleMesh& inData, const PxPhysics& ownerPhysics); + + void createInstance(PvdDataStream& inStream, const PxRigidStatic& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd); + void sendAllProperties(PvdDataStream& inStream, const PxRigidStatic& inObj); + void destroyInstance(PvdDataStream& inStream, const PxRigidStatic& inObj, const PxScene& ownerScene); + + void createInstance(PvdDataStream& inStream, const PxRigidDynamic& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd); + void sendAllProperties(PvdDataStream& inStream, const PxRigidDynamic& inObj); + void destroyInstance(PvdDataStream& inStream, const PxRigidDynamic& inObj, const PxScene& ownerScene); + + void createInstance(PvdDataStream& inStream, const PxArticulationBase& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd); + void sendAllProperties(PvdDataStream& inStream, const PxArticulationBase& inObj); + void destroyInstance(PvdDataStream& inStream, const PxArticulationBase& inObj, const PxScene& ownerScene); + + void createInstance(PvdDataStream& inStream, const PxArticulationLink& inObj, const PxPhysics& ownerPhysics, PsPvd* pvd); + void sendAllProperties(PvdDataStream& inStream, const PxArticulationLink& inObj); + void destroyInstance(PvdDataStream& inStream, const PxArticulationLink& inObj); + + void createInstance(PvdDataStream& inStream, const PxShape& inObj, const PxRigidActor& owner, const PxPhysics& ownerPhysics, PsPvd* pvd); + void sendAllProperties(PvdDataStream& inStream, const PxShape& inObj); + void releaseAndRecreateGeometry(PvdDataStream& inStream, const PxShape& inObj, PxPhysics& ownerPhysics, PsPvd* pvd); + void updateMaterials(PvdDataStream& inStream, const PxShape& inObj, PsPvd* pvd); + void destroyInstance(PvdDataStream& inStream, const PxShape& inObj, const PxRigidActor& owner); + + // These are created as part of the articulation link's creation process, so outside entities don't need to + // create them. + void sendAllProperties(PvdDataStream& inStream, const PxArticulationJointBase& inObj); + + // per frame update + void updateDynamicActorsAndArticulations(PvdDataStream& inStream, const PxScene* inScene, PvdVisualizer* linkJointViz); + + // Origin Shift + void originShift(PvdDataStream& inStream, const PxScene* inScene, PxVec3 shift); + + void createInstance(PvdDataStream& inStream, const PxAggregate& inObj, const PxScene& ownerScene); + void sendAllProperties(PvdDataStream& inStream, const PxAggregate& inObj); + void destroyInstance(PvdDataStream& inStream, const PxAggregate& inObj, const PxScene& ownerScene); + void detachAggregateActor(PvdDataStream& inStream, const PxAggregate& inObj, const PxActor& inActor); + void attachAggregateActor(PvdDataStream& inStream, const PxAggregate& inObj, const PxActor& inActor); + + template + void registrarPhysicsObject(PvdDataStream&, const TDataType&, PsPvd*); +}; +} +} + +#endif +#endif diff --git a/src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp new file mode 100644 index 000000000..537cee7c2 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp @@ -0,0 +1,216 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// PX_DUMMY_SYMBOL + +#if PX_SUPPORT_PVD + +#include "pvd/PxPvdTransport.h" +#include "PxPhysics.h" +#include "PxPvdClient.h" +#include "PxPvdDataStream.h" +#include "PxPvdObjectModelBaseTypes.h" +#include "PvdPhysicsClient.h" +#include "PvdTypeNames.h" + +using namespace physx; +using namespace physx::Vd; + +PvdPhysicsClient::PvdPhysicsClient(PsPvd* pvd) +: mPvd(pvd), mPvdDataStream(NULL), mIsConnected(false) +{ +} + +PvdPhysicsClient::~PvdPhysicsClient() +{ + mPvd->removeClient(this); +} + +PvdDataStream* PvdPhysicsClient::getDataStream() +{ + return mPvdDataStream; +} + +PvdMetaDataBinding* PvdPhysicsClient::getMetaDataBinding() +{ + return &mMetaDataBinding; +} + +PvdUserRenderer* PvdPhysicsClient::getUserRender() +{ + PX_ASSERT(0); + return NULL; +} + +bool PvdPhysicsClient::isConnected() const +{ + return mIsConnected; +} + +void PvdPhysicsClient::onPvdConnected() +{ + if(mIsConnected || !mPvd) + return; + + mIsConnected = true; + mPvdDataStream = PvdDataStream::create(mPvd); + sendEntireSDK(); +} + +void PvdPhysicsClient::onPvdDisconnected() +{ + if(!mIsConnected) + return; + mIsConnected = false; + + mPvdDataStream->release(); + mPvdDataStream = NULL; +} + +void PvdPhysicsClient::flush() +{ +} + +void PvdPhysicsClient::sendEntireSDK() +{ + PxPhysics& physics = PxGetPhysics(); + + mMetaDataBinding.registerSDKProperties(*mPvdDataStream); + mPvdDataStream->createInstance(&physics); + + mPvdDataStream->setIsTopLevelUIElement(&physics, true); + mMetaDataBinding.sendAllProperties(*mPvdDataStream, physics); + +#define SEND_BUFFER_GROUP(type, name) \ + { \ + physx::shdfnd::Array buffers; \ + PxU32 numBuffers = physics.getNb##name(); \ + buffers.resize(numBuffers); \ + physics.get##name(buffers.begin(), numBuffers); \ + for(PxU32 i = 0; i < numBuffers; i++) \ + { \ + if(mPvd->registerObject(buffers[i])) \ + createPvdInstance(buffers[i]); \ + } \ + } + + SEND_BUFFER_GROUP(PxMaterial, Materials); + SEND_BUFFER_GROUP(PxTriangleMesh, TriangleMeshes); + SEND_BUFFER_GROUP(PxConvexMesh, ConvexMeshes); + SEND_BUFFER_GROUP(PxHeightField, HeightFields); +} + +void PvdPhysicsClient::destroyPvdInstance(const PxPhysics* physics) +{ + if(mPvdDataStream) + mPvdDataStream->destroyInstance(physics); +} + +void PvdPhysicsClient::createPvdInstance(const PxTriangleMesh* triMesh) +{ + mMetaDataBinding.createInstance(*mPvdDataStream, *triMesh, PxGetPhysics()); +} + +void PvdPhysicsClient::destroyPvdInstance(const PxTriangleMesh* triMesh) +{ + mMetaDataBinding.destroyInstance(*mPvdDataStream, *triMesh, PxGetPhysics()); +} + +void PvdPhysicsClient::createPvdInstance(const PxConvexMesh* convexMesh) +{ + mMetaDataBinding.createInstance(*mPvdDataStream, *convexMesh, PxGetPhysics()); +} + +void PvdPhysicsClient::destroyPvdInstance(const PxConvexMesh* convexMesh) +{ + mMetaDataBinding.destroyInstance(*mPvdDataStream, *convexMesh, PxGetPhysics()); +} + +void PvdPhysicsClient::createPvdInstance(const PxHeightField* heightField) +{ + mMetaDataBinding.createInstance(*mPvdDataStream, *heightField, PxGetPhysics()); +} + +void PvdPhysicsClient::destroyPvdInstance(const PxHeightField* heightField) +{ + mMetaDataBinding.destroyInstance(*mPvdDataStream, *heightField, PxGetPhysics()); +} + +void PvdPhysicsClient::createPvdInstance(const PxMaterial* mat) +{ + mMetaDataBinding.createInstance(*mPvdDataStream, *mat, PxGetPhysics()); +} + +void PvdPhysicsClient::updatePvdProperties(const PxMaterial* mat) +{ + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *mat); +} + +void PvdPhysicsClient::destroyPvdInstance(const PxMaterial* mat) +{ + mMetaDataBinding.destroyInstance(*mPvdDataStream, *mat, PxGetPhysics()); +} + +void PvdPhysicsClient::onGuMeshFactoryBufferRelease(const PxBase* object, PxType typeID) +{ + if(!mIsConnected || !mPvd) + return; + + if(mPvd->unRegisterObject(object)) + { + switch(typeID) + { + case PxConcreteType::eHEIGHTFIELD: + destroyPvdInstance(static_cast(object)); + break; + + case PxConcreteType::eCONVEX_MESH: + destroyPvdInstance(static_cast(object)); + break; + + case PxConcreteType::eTRIANGLE_MESH_BVH33: + case PxConcreteType::eTRIANGLE_MESH_BVH34: + destroyPvdInstance(static_cast(object)); + break; + + default: + break; + } + } +} + +void PvdPhysicsClient::reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) +{ + if(mIsConnected) + { + mPvdDataStream->sendErrorMessage(code, message, file, PxU32(line)); + } +} + +#endif // PX_SUPPORT_PVD diff --git a/src/PhysX/physx/source/physx/src/PvdPhysicsClient.h b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.h new file mode 100644 index 000000000..f4704b715 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PVD_PHYSICS_CLIENT_H +#define PVD_PHYSICS_CLIENT_H +#if PX_SUPPORT_PVD +#include "foundation/PxErrorCallback.h" +#include "PxPvdClient.h" +#include "PvdMetaDataPvdBinding.h" +#include "NpFactory.h" +#include "PsHashMap.h" +#include "PsMutex.h" +#include "PsPvd.h" + +namespace physx +{ +class PxProfileMemoryEventBuffer; + +namespace Vd +{ + +class PvdPhysicsClient : public PvdClient, public PxErrorCallback, public NpFactoryListener, public shdfnd::UserAllocated +{ + PX_NOCOPY(PvdPhysicsClient) + public: + PvdPhysicsClient(PsPvd* pvd); + virtual ~PvdPhysicsClient(); + + bool isConnected() const; + void onPvdConnected(); + void onPvdDisconnected(); + void flush(); + + physx::pvdsdk::PvdDataStream* getDataStream(); + PvdMetaDataBinding* getMetaDataBinding(); + PvdUserRenderer* getUserRender(); + + void sendEntireSDK(); + void destroyPvdInstance(const PxPhysics* physics); + + // NpFactoryListener + virtual void onGuMeshFactoryBufferRelease(const PxBase* object, PxType typeID); + /// NpFactoryListener + + // PxErrorCallback + void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line); + + private: + void createPvdInstance(const PxTriangleMesh* triMesh); + void destroyPvdInstance(const PxTriangleMesh* triMesh); + void createPvdInstance(const PxConvexMesh* convexMesh); + void destroyPvdInstance(const PxConvexMesh* convexMesh); + void createPvdInstance(const PxHeightField* heightField); + void destroyPvdInstance(const PxHeightField* heightField); + void createPvdInstance(const PxMaterial* mat); + void destroyPvdInstance(const PxMaterial* mat); + void updatePvdProperties(const PxMaterial* mat); + + PsPvd* mPvd; + PvdDataStream* mPvdDataStream; + PvdMetaDataBinding mMetaDataBinding; + bool mIsConnected; +}; + +} // namespace Vd +} // namespace physx + +#endif // PX_SUPPORT_PVD +#endif // PVD_PHYSICS_CLIENT_H diff --git a/src/PhysX/physx/source/physx/src/PvdTypeNames.h b/src/PhysX/physx/source/physx/src/PvdTypeNames.h new file mode 100644 index 000000000..d7a3b82f6 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdTypeNames.h @@ -0,0 +1,174 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PVD_TYPE_NAMES_H +#define PVD_TYPE_NAMES_H +#if PX_SUPPORT_PVD +#include "PxPvdObjectModelBaseTypes.h" +#include "PxMetaDataObjects.h" +#include "PxHeightFieldSample.h" + +namespace physx +{ +namespace Vd +{ +struct PvdSqHit; +struct PvdRaycast; +struct PvdOverlap; +struct PvdSweep; + +struct PvdHullPolygonData +{ + PxU16 mNumVertices; + PxU16 mIndexBase; + PvdHullPolygonData(PxU16 numVert, PxU16 idxBase) : mNumVertices(numVert), mIndexBase(idxBase) + { + } +}; + + +struct PxArticulationLinkUpdateBlock +{ + PxTransform GlobalPose; + PxVec3 LinearVelocity; + PxVec3 AngularVelocity; +}; +struct PxRigidDynamicUpdateBlock : public PxArticulationLinkUpdateBlock +{ + bool IsSleeping; +}; + +struct PvdContact +{ + PxVec3 point; + PxVec3 axis; + const void* shape0; + const void* shape1; + PxF32 separation; + PxF32 normalForce; + PxU32 internalFaceIndex0; + PxU32 internalFaceIndex1; + bool normalForceAvailable; +}; + +struct PvdPositionAndRadius +{ + PxVec3 position; + PxF32 radius; +}; + +} //Vd +} //physx + +namespace physx +{ +namespace pvdsdk +{ + +#define DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(type) DEFINE_PVD_TYPE_NAME_MAP(physx::type, "physx3", #type) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPhysics) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxScene) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxTolerancesScale) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxTolerancesScaleGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSceneDescGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSceneDesc) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSimulationStatistics) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSimulationStatisticsGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxMaterial) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxMaterialGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightField) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightFieldDesc) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightFieldDescGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxTriangleMesh) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxConvexMesh) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxActor) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidActor) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidBody) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidDynamic) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidDynamicGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidStatic) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidStaticGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxShape) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxShapeGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxBoxGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPlaneGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxCapsuleGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSphereGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightFieldGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxTriangleMeshGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxConvexMeshGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxBoxGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPlaneGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxCapsuleGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSphereGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightFieldGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxTriangleMeshGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxConvexMeshGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightFieldSample) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxConstraint) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxConstraintGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationBase) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationBaseGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulation) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationLink) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationLinkGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationJointBase) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationJointBaseGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationJoint) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationJointGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationJointReducedCoordinate) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxAggregate) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxAggregateGeneratedValues) + +#undef DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP + +#define DEFINE_NATIVE_PVD_TYPE_MAP(type) DEFINE_PVD_TYPE_NAME_MAP(physx::Vd::type, "physx3", #type) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdHullPolygonData) +DEFINE_NATIVE_PVD_TYPE_MAP(PxRigidDynamicUpdateBlock) +DEFINE_NATIVE_PVD_TYPE_MAP(PxArticulationLinkUpdateBlock) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdContact) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdRaycast) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdSweep) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdOverlap) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdSqHit) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdPositionAndRadius) + +#undef DEFINE_NATIVE_PVD_TYPE_MAP + + +DEFINE_PVD_TYPE_ALIAS(physx::PxFilterData, U32Array4) + + +} +} + +#endif + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp new file mode 100644 index 000000000..67a921c11 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp @@ -0,0 +1,53 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScbBase.h" + +using namespace physx; +using namespace Scb; + +#include "ScbActor.h" +#include "ScbRigidStatic.h" +#include "ScbBody.h" + +Actor::Offsets::Offsets() +{ + const size_t staticOffset = reinterpret_cast(&(reinterpret_cast(0)->getScStatic())); + const size_t bodyOffset = reinterpret_cast(&(reinterpret_cast(0)->getScBody())); + + scToScb[PxActorType::eRIGID_STATIC] = staticOffset; + scToScb[PxActorType::eRIGID_DYNAMIC] = bodyOffset; + scToScb[PxActorType::eARTICULATION_LINK] = bodyOffset; + + scbToSc[ScbType::eRIGID_STATIC] = staticOffset; + scbToSc[ScbType::eBODY] = bodyOffset; + scbToSc[ScbType::eBODY_FROM_ARTICULATION_LINK] = bodyOffset; +} + +const Actor::Offsets Actor::sOffsets; diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbActor.h b/src/PhysX/physx/source/physx/src/buffering/ScbActor.h new file mode 100644 index 000000000..bba2c17f5 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbActor.h @@ -0,0 +1,207 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_FSACTOR +#define PX_PHYSICS_SCB_FSACTOR + +#include "ScActorCore.h" +#include "PsUtilities.h" +#include "ScbBase.h" +#include "ScbDefs.h" + +#include "PxClient.h" + + +namespace physx +{ +namespace Scb +{ +struct ActorBuffer +{ +#ifdef USE_NEW_SYSTEM + PxActorFlags mActorFlags; + PxDominanceGroup mDominanceGroup; +// PxActorClientBehaviorFlags mClientBehaviorFlags; +#else + template struct Fns {}; + typedef Sc::ActorCore Core; + typedef ActorBuffer Buf; + + SCB_REGULAR_ATTRIBUTE (0, PxActorFlags, ActorFlags) + SCB_REGULAR_ATTRIBUTE (1, PxDominanceGroup, DominanceGroup) +// SCB_REGULAR_ATTRIBUTE (2, PxActorClientBehaviorFlags, ClientBehaviorFlags) +#endif + enum { AttrCount = 3 }; + +protected: + ~ActorBuffer(){} +}; + +class Actor : public Base +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef ActorBuffer Buf; + typedef Sc::ActorCore Core; + +public: +// PX_SERIALIZATION + Actor(const PxEMPTY) : Base(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PX_INLINE Actor() {} + +#ifdef USE_NEW_SYSTEM + SCB_MEMBER(Actor, getActorCore(), ActorFlags, PxActorFlags, 0) + SCB_MEMBER(Actor, getActorCore(), DominanceGroup, PxDominanceGroup, 1) +// SCB_MEMBER(Actor, getActorCore(), ClientBehaviorFlags, PxActorClientBehaviorFlags, 2) +#endif + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::Actor interface + //--------------------------------------------------------------------------------- +#ifndef USE_NEW_SYSTEM + PX_INLINE PxActorFlags getActorFlags() const { return read(); } + PX_INLINE void setActorFlags(PxActorFlags v); + + PX_INLINE PxDominanceGroup getDominanceGroup() const { return read();} + PX_INLINE void setDominanceGroup(PxDominanceGroup v) { write(v); } + +// PX_INLINE PxActorClientBehaviorFlags getClientBehaviorFlags() const { return read(); } +// PX_INLINE void setClientBehaviorFlags(PxActorClientBehaviorFlags v){ write(v); } +#endif + + PX_INLINE void setOwnerClient(PxClientID inId); + PX_INLINE PxClientID getOwnerClient() const { return getActorCore().getOwnerClient(); } //immutable, so this should be fine. + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE const Core& getActorCore() const { return *reinterpret_cast(reinterpret_cast(this) + sOffsets.scbToSc[getScbType()]); } + PX_FORCE_INLINE Core& getActorCore() { return *reinterpret_cast(reinterpret_cast(this) + sOffsets.scbToSc[getScbType()]); } + + PX_FORCE_INLINE static const Actor& fromSc(const Core& a) { return *reinterpret_cast(reinterpret_cast(&a) - sOffsets.scToScb[a.getActorCoreType()]); } + PX_FORCE_INLINE static Actor& fromSc(Core &a) { return *reinterpret_cast(reinterpret_cast(&a) - sOffsets.scToScb[a.getActorCoreType()]); } + + PX_FORCE_INLINE PxActorType::Enum getActorType() const { return getActorCore().getActorCoreType(); } + +protected: + ~Actor() {} + PX_INLINE void syncState(); + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + struct Access: public BufferedAccess {}; + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, getActorCore()); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, getActorCore(), v); } + template PX_FORCE_INLINE void flush(Core& core, const Buf& buf) { Access::flush >(*this, core, buf); } +#endif + + struct Offsets + { + size_t scToScb[PxActorType::eACTOR_COUNT]; + size_t scbToSc[ScbType::eTYPE_COUNT]; + Offsets(); + }; + static const Offsets sOffsets; +}; + +#ifndef USE_NEW_SYSTEM +PX_INLINE void Actor::setActorFlags(PxActorFlags v) +{ + // PT: TODO: move this check out of here, they should be done in Np! +#if PX_CHECKED + const PxActorFlags aFlags = getActorFlags(); + const PxActorType::Enum aType = getActorType(); + if((!aFlags.isSet(PxActorFlag::eDISABLE_SIMULATION)) && v.isSet(PxActorFlag::eDISABLE_SIMULATION) && + (aType != PxActorType::eRIGID_DYNAMIC) && (aType != PxActorType::eRIGID_STATIC)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxActor::setActorFlag: PxActorFlag::eDISABLE_SIMULATION is only supported by PxRigidDynamic and PxRigidStatic objects."); + } +#endif + + write(v); +} +#endif + +PX_INLINE void Actor::setOwnerClient(PxClientID inId) +{ + //This call is only valid if we aren't in a scene. + //Thus we can't be buffering yet + if(!isBuffering()) + { + getActorCore().setOwnerClient(inId); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Attempt to set the client id when an actor is buffering"); + } +} + +PX_INLINE void Actor::syncState() +{ + //this should be called from syncState() of derived classes + + const PxU32 flags = getBufferFlags(); +#ifdef USE_NEW_SYSTEM + if(flags & (BF_ActorFlags|BF_DominanceGroup/*|BF_ClientBehaviorFlags*/)) + { + syncActorFlags(); + syncDominanceGroup(); +// syncClientBehaviorFlags(); + } +#else + if(flags & (Buf::BF_ActorFlags|Buf::BF_DominanceGroup/*|Buf::BF_ClientBehaviorFlags*/)) + { + Core& core = getActorCore(); + const Buf& buffer = *reinterpret_cast(getStream()); + + flush(core, buffer); + flush(core, buffer); +// flush(core, buffer); + } +#endif +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp new file mode 100644 index 000000000..85dd5edf2 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp @@ -0,0 +1,139 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScbAggregate.h" +#include "ScbActor.h" + +using namespace physx; + +void Scb::Aggregate::addActor(Scb::Actor& actor) +{ + const ControlState::Enum state = getControlState(); + + if(!isBufferingSpecial(state)) + { + actor.getActorCore().setAggregateID(mAggregateID); + PvdAttachActorToAggregate( this, &actor ); + PvdUpdateProperties( this ); + } + else if((state != ControlState::eREMOVE_PENDING)) // If the aggregate is pending for deletion, adding/removing an actor should not be double buffered because the aggregateID must not be set for the actors + { + // if available, search in list of removed actors to cover the remove-add case + Scb::AggregateBuffer* PX_RESTRICT bufferedData = getBufferedData(); + if(bufferedData->removeBufferIdx != 0xffffffff) + { + Scb::Actor** removeBuffer = getScbScene()->getActorBuffer(bufferedData->removeBufferIdx); + for(PxU32 i=0; iremoveCount; i++) + { + if(removeBuffer[i] == &actor) + { + removeBuffer[i] = removeBuffer[bufferedData->removeCount - 1]; + PX_ASSERT(bufferedData->removeCount > 0); + bufferedData->removeCount--; + break; + } + } + } + + Scb::Actor** actorBuffer; + if(bufferedData->addBufferIdx == 0xffffffff) + actorBuffer = getScbScene()->allocActorBuffer(mMaxNbActors, bufferedData->addBufferIdx); + else + actorBuffer = getScbScene()->getActorBuffer(bufferedData->addBufferIdx); + + PX_ASSERT(bufferedData->addCount < mMaxNbActors); + actorBuffer[bufferedData->addCount] = &actor; + bufferedData->addCount++; + + if(state != ControlState::eINSERT_PENDING) + markUpdated(BF_ADD_ACTOR); + else + { + // Not a great solution but aggregates are special in the sense that even in the pending insert case, data needs to be double buffered + // (see isBufferingSpecial() for details) + setBufferFlag(BF_ADD_ACTOR); + } + } +} + +void Scb::Aggregate::removeActor(Scb::Actor& actor, bool reinsert) +{ + const ControlState::Enum state = getControlState(); + const ControlState::Enum actorState = actor.getControlState(); + + if(!isBufferingSpecial(state)) + { + Sc::ActorCore& ac = actor.getActorCore(); + ac.setAggregateID(PX_INVALID_U32); + + if(getScbSceneForAPI() && reinsert) + ac.reinsertShapes(); + } + else if((state != ControlState::eREMOVE_PENDING)) + { + // if available, search in list of added actors to cover the add-remove case + Scb::AggregateBuffer* PX_RESTRICT bufferedData = getBufferedData(); + if(bufferedData->addBufferIdx != 0xffffffff) + { + Scb::Actor** addBuffer = getScbScene()->getActorBuffer(bufferedData->addBufferIdx); + for(PxU32 i=0; iaddCount; i++) + { + if(addBuffer[i] == &actor) + { + addBuffer[i] = addBuffer[bufferedData->addCount - 1]; + PX_ASSERT(bufferedData->addCount > 0); + bufferedData->addCount--; + return; // It's fine to abort here because the aggregateID has not been set yet + } + } + } + + Scb::Actor** actorBuffer; + if(bufferedData->removeBufferIdx == 0xffffffff) + actorBuffer = getScbScene()->allocActorBuffer(mMaxNbActors, bufferedData->removeBufferIdx); + else + actorBuffer = getScbScene()->getActorBuffer(bufferedData->removeBufferIdx); + + PX_ASSERT(bufferedData->removeCount < mMaxNbActors); + actorBuffer[bufferedData->removeCount] = &actor; + bufferedData->removeCount++; + + markUpdated(BF_REMOVE_ACTOR); + } + + //Update pvd status if not buffer OR the actor release while aggregate is already in scene + if(!isBufferingSpecial(state) + || ((actorState == ControlState::eIN_SCENE || actorState == ControlState::eREMOVE_PENDING ) + && state != ControlState::eINSERT_PENDING + && !reinsert)) + { + PvdDetachActorFromAggregate( this, &actor ); + PvdUpdateProperties( this ); + } +} diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h new file mode 100644 index 000000000..28cf591cd --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h @@ -0,0 +1,238 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_AGGREGATE +#define PX_PHYSICS_SCB_AGGREGATE + +#include "CmPhysXCommon.h" +#include "PxAggregate.h" +#include "ScbActor.h" +#include "ScbAggregate.h" +#include "ScbBase.h" + +// PX_SERIALIZATION +#include "PxSerialFramework.h" +//~PX_SERIALIZATION + +namespace physx +{ +namespace Scb +{ + +class Actor; + +struct AggregateBuffer +{ + AggregateBuffer() : addBufferIdx(0xffffffff), addCount(0), removeBufferIdx(0xffffffff), removeCount(0) {} + + PxU32 addBufferIdx; + PxU32 addCount; + PxU32 removeBufferIdx; + PxU32 removeCount; +}; + +class Aggregate : public Base +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +private: + enum BufferFlag + { + BF_ADD_ACTOR = (1 << 0), + BF_REMOVE_ACTOR = (1 << 1) + }; + +public: + +// PX_SERIALIZATION + Aggregate(const PxEMPTY) : Base(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + PX_INLINE Aggregate(PxAggregate* px, PxU32 maxActors, bool selfCollision); + PX_INLINE ~Aggregate(); + + void addActor(Scb::Actor&); + void removeActor(Scb::Actor& actor, bool reinsert); + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(Scb::Scene& scene); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE PxU32 getMaxActorCount() const { return mMaxNbActors; } + PX_FORCE_INLINE bool getSelfCollide() const { return mSelfCollide; } + PX_FORCE_INLINE PxU32 getAggregateID() const { return mAggregateID; } + PX_FORCE_INLINE void setAggregateID(PxU32 cid) { mAggregateID = cid; } + + PX_FORCE_INLINE bool isBufferingSpecial(ControlState::Enum state) const; + + PxAggregate* mPxAggregate; // Back pointer +private: + PxU32 mAggregateID; + PxU32 mMaxNbActors; + bool mSelfCollide; + + PX_FORCE_INLINE const Scb::AggregateBuffer* getBufferedData() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Scb::AggregateBuffer* getBufferedData() { return reinterpret_cast(getStream()); } +}; + +PX_INLINE Aggregate::Aggregate(PxAggregate* px, PxU32 maxActors, bool selfCollision) : + mPxAggregate (px), + mAggregateID (PX_INVALID_U32), + mMaxNbActors (maxActors), + mSelfCollide (selfCollision) +{ + setScbType(ScbType::eAGGREGATE); +} + +PX_INLINE Aggregate::~Aggregate() +{ +} + +PX_FORCE_INLINE bool Aggregate::isBufferingSpecial(ControlState::Enum state) const +{ + // A special version of the buffer check is needed for aggregates because it is not fine for adding/removing + // an actor to be not double buffered if the aggregate is pending for insertion. + // For example: Adding an actor can not be processed if the aggregate is pending for insertion because the aggregateID + // is not yet available (an there is no Sc::Aggregate object to store the actors) + + Scb::Scene* scbScene = getScbSceneForAPI(); + return state == ControlState::eREMOVE_PENDING || // pending remove not possible if not buffered + (scbScene && scbScene->isPhysicsBuffering()); +} + +//-------------------------------------------------------------- +// +// PVD Events +// +//-------------------------------------------------------------- + +namespace +{ +#if PX_SUPPORT_PVD + PX_FORCE_INLINE void PvdAttachActorToAggregate(Scb::Aggregate* pAggregate, Scb::Actor* pScbActor) + { + Scb::Scene* scbScene = pAggregate->getScbSceneForAPI(); + if(scbScene/* && scbScene->getScenePvdClient().isInstanceValid(pAggregate)*/) + scbScene->getScenePvdClient().attachAggregateActor( pAggregate, pScbActor ); + } + + PX_FORCE_INLINE void PvdDetachActorFromAggregate(Scb::Aggregate* pAggregate, Scb::Actor* pScbActor) + { + Scb::Scene* scbScene = pAggregate->getScbSceneForAPI(); + if(scbScene/*&& scbScene->getScenePvdClient().isInstanceValid(pAggregate)*/) + scbScene->getScenePvdClient().detachAggregateActor( pAggregate, pScbActor ); + } + + PX_FORCE_INLINE void PvdUpdateProperties(Scb::Aggregate* pAggregate) + { + Scb::Scene* scbScene = pAggregate->getScbSceneForAPI(); + if(scbScene /*&& scbScene->getScenePvdClient().isInstanceValid(pAggregate)*/) + scbScene->getScenePvdClient().updatePvdProperties( pAggregate ); + } +#else +#define PvdAttachActorToAggregate(aggregate, scbActor) {} +#define PvdDetachActorFromAggregate(aggregate, scbActor) {} +#define PvdUpdateProperties(aggregate) {} +#endif +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_INLINE void Aggregate::syncState(Scb::Scene& scene) +{ + const PxU32 flags = getBufferFlags(); + + enum AggregateSyncDirtyType + { + DIRTY_NONE = 0, + DIRTY_ADD_ACTOR = 1<<0, + DIRTY_REMOVE_ACTOR = 1<<1 + }; + + PxU32 dirtyType = DIRTY_NONE; + + if(flags) + { + const Scb::AggregateBuffer* PX_RESTRICT bufferedData = getBufferedData(); + + if(flags & BF_ADD_ACTOR) + { + dirtyType |= DIRTY_ADD_ACTOR; + + Scb::Actor* const* actorBuffer = scene.getActorBuffer(bufferedData->addBufferIdx); + + PX_ASSERT(mAggregateID != PX_INVALID_U32); + for(PxU32 i=0; iaddCount; i++) + { + actorBuffer[i]->getActorCore().setAggregateID(mAggregateID); + PvdAttachActorToAggregate( this, actorBuffer[i] ); + } + } + + if(flags & BF_REMOVE_ACTOR) + { + dirtyType |= DIRTY_REMOVE_ACTOR; + Scb::Actor* const* actorBuffer = scene.getActorBuffer(bufferedData->removeBufferIdx); + + for(PxU32 i=0; iremoveCount; i++) + { + const ControlState::Enum state = actorBuffer[i]->getControlState(); + + Sc::ActorCore& ac = actorBuffer[i]->getActorCore(); + ac.setAggregateID(PX_INVALID_U32); + if(state != ControlState::eREMOVE_PENDING) + PvdDetachActorFromAggregate( this, actorBuffer[i] ); + if(state == ControlState::eINSERT_PENDING || state == ControlState::eIN_SCENE) + ac.reinsertShapes(); + } + } + + if(dirtyType != DIRTY_NONE) + PvdUpdateProperties( this ); + } + + postSyncState(); +} + +}// namespace Scb +}// namespace physx + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h b/src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h new file mode 100644 index 000000000..3d1a13089 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h @@ -0,0 +1,414 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_ARTICULATION +#define PX_PHYSICS_SCB_ARTICULATION + +#include "ScArticulationCore.h" +#include "ScbBase.h" +#include "ScbDefs.h" + +namespace physx +{ +namespace Scb +{ + +struct ArticulationBuffer +{ +#ifdef USE_NEW_SYSTEM + PxU32 mInternalDriveIterations; + PxU32 mExternalDriveIterations; + PxU32 mMaxProjectionIterations; + PxReal mSeparationTolerance; + PxReal mSleepThreshold; + PxU16 mSolverIterationCounts; + PxReal mFreezeThreshold; +#else + template struct Fns {}; + typedef Sc::ArticulationCore Core; + typedef ArticulationBuffer Buf; + + SCB_REGULAR_ATTRIBUTE(0, PxU32, InternalDriveIterations) + SCB_REGULAR_ATTRIBUTE(1, PxU32, ExternalDriveIterations) + SCB_REGULAR_ATTRIBUTE(2, PxU32, MaxProjectionIterations) + SCB_REGULAR_ATTRIBUTE(3, PxReal, SeparationTolerance) + SCB_REGULAR_ATTRIBUTE(4, PxReal, SleepThreshold) + SCB_REGULAR_ATTRIBUTE(5, PxU16, SolverIterationCounts) + SCB_REGULAR_ATTRIBUTE(6, PxReal, FreezeThreshold) +#endif + enum { BF_WakeCounter = 1<<7 }; + enum { BF_PutToSleep = 1<<8 }; + enum { BF_WakeUp = 1<<9 }; + enum { BF_ArticulationFlags = 1 << 19 }; + enum { BF_ArticulationPoseUpdate = 1 << 11 }; + +}; + +class Articulation : public Base +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef ArticulationBuffer Buf; + typedef Sc::ArticulationCore Core; + +// PX_SERIALIZATION +public: + Articulation(const PxEMPTY) : Base(PxEmpty), mArticulation(PxEmpty), mBufferedIsSleeping(1) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + +private: + +public: + PX_INLINE Articulation(); + PX_INLINE ~Articulation() {} + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::Articulation interface + //--------------------------------------------------------------------------------- + +#ifdef USE_NEW_SYSTEM + SCB_MEMBER(Articulation, mArticulation, InternalDriveIterations, PxU32, 0) + SCB_MEMBER(Articulation, mArticulation, ExternalDriveIterations, PxU32, 1) + SCB_MEMBER(Articulation, mArticulation, MaxProjectionIterations, PxU32, 2) + SCB_MEMBER(Articulation, mArticulation, SeparationTolerance, PxReal, 3) + SCB_MEMBER(Articulation, mArticulation, SleepThreshold, PxReal, 4) + SCB_MEMBER(Articulation, mArticulation, SolverIterationCounts, PxU16, 5) + SCB_MEMBER(Articulation, mArticulation, FreezeThreshold, PxReal, 6) +#else + PX_INLINE PxU32 getInternalDriveIterations() const { return read(); } + PX_INLINE void setInternalDriveIterations(const PxU32 v) { write(v); } + + PX_INLINE PxU32 getExternalDriveIterations() const { return read(); } + PX_INLINE void setExternalDriveIterations(const PxU32 v) { write(v); } + + PX_INLINE PxU32 getMaxProjectionIterations() const { return read(); } + PX_INLINE void setMaxProjectionIterations(const PxU32 v) { write(v); } + + PX_INLINE PxU16 getSolverIterationCounts() const { return read(); } + PX_INLINE void setSolverIterationCounts(PxU16 v) { write(v); } + + PX_INLINE PxReal getSeparationTolerance() const { return read(); } + PX_INLINE void setSeparationTolerance(const PxReal v) { write(v); } + + PX_INLINE PxReal getSleepThreshold() const { return read(); } + PX_INLINE void setSleepThreshold(const PxReal v) { write(v); } + + PX_INLINE PxReal getFreezeThreshold() const { return read(); } + PX_INLINE void setFreezeThreshold(const PxReal v) { write(v); } +#endif + PX_INLINE PxReal getWakeCounter() const { return mBufferedWakeCounter; } + PX_INLINE void setWakeCounter(const PxReal v); + + PX_INLINE PxArticulationFlags getArticulationFlags() const { return mBufferedArticulationFlags; } + PX_INLINE void setArticulationFlags(PxArticulationFlags flags); + + PX_INLINE PxArticulationBase::Enum getArticulationType() const { return mArticulation.getArticulationType(); } + PX_INLINE void setArticulationType(PxArticulationBase::Enum type) { mArticulation.setArticulationType(type); } + + PX_FORCE_INLINE void wakeUp(); + PX_FORCE_INLINE void putToSleep(); + PX_FORCE_INLINE bool isSleeping() const { return (mBufferedIsSleeping != 0); } + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(); + + PX_FORCE_INLINE const Sc::ArticulationCore& getScArticulation() const { return mArticulation; } // Only use if you know what you're doing! + PX_FORCE_INLINE Sc::ArticulationCore& getScArticulation() { return mArticulation; } // Only use if you know what you're doing! + + PX_FORCE_INLINE static Articulation& fromSc(Core &a) { return *reinterpret_cast(reinterpret_cast(&a)-getScOffset()); } + PX_FORCE_INLINE static const Articulation& fromSc(const Core &a) { return *reinterpret_cast(reinterpret_cast(&a)-getScOffset()); } + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mArticulation); } + + PX_FORCE_INLINE void wakeUpInternal(PxReal wakeCounter); + + PX_FORCE_INLINE void setGlobalPose(); + + PX_FORCE_INLINE void initBufferedState(); + PX_FORCE_INLINE void clearBufferedState(); + PX_FORCE_INLINE void clearBufferedSleepStateChange(); + +private: + Sc::ArticulationCore mArticulation; + PxReal mBufferedWakeCounter; + PxU8 mBufferedIsSleeping; + PxArticulationFlags mBufferedArticulationFlags; + + PX_FORCE_INLINE const Buf* getArticulationBuffer() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Buf* getArticulationBuffer() { return reinterpret_cast(getStream()); } + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess {}; + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, mArticulation); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, mArticulation, v); } + template PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush >(*this, mArticulation, buf); } +#endif +}; + +Articulation::Articulation() +{ + setScbType(ScbType::eARTICULATION); + mBufferedWakeCounter = mArticulation.getWakeCounter(); + mBufferedIsSleeping = 1; // this is the specified value for free standing objects +} + +PX_INLINE void Articulation::setWakeCounter(PxReal counter) +{ + mBufferedWakeCounter = counter; + + if(!isBuffering()) + { + if(getScbScene() && (counter > 0.0f)) + mBufferedIsSleeping = 0; + + mArticulation.setWakeCounter(counter); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if(counter > 0.0f) + { + mBufferedIsSleeping = 0; + markUpdated(Buf::BF_WakeUp | Buf::BF_WakeCounter); + resetBufferFlag(Buf::BF_PutToSleep); + } + else + markUpdated(Buf::BF_WakeCounter); + } +} + +PX_FORCE_INLINE void Articulation::setArticulationFlags(PxArticulationFlags flags) +{ + mBufferedArticulationFlags = flags; + if (!isBuffering()) + { + mArticulation.setArticulationFlags(flags); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + markUpdated(Buf::BF_ArticulationFlags); + } +} + +PX_FORCE_INLINE void Articulation::wakeUp() +{ + Scene* scene = getScbScene(); + PX_ASSERT(scene); // only allowed for an object in a scene + + wakeUpInternal(scene->getWakeCounterResetValue()); +} + +PX_FORCE_INLINE void Articulation::wakeUpInternal(PxReal wakeCounter) +{ + PX_ASSERT(getScbScene()); + + mBufferedWakeCounter = wakeCounter; + + mBufferedIsSleeping = 0; + if(!isBuffering()) + { + mArticulation.wakeUp(wakeCounter); + } + else + { + markUpdated(Buf::BF_WakeUp | Buf::BF_WakeCounter); + resetBufferFlag(Buf::BF_PutToSleep); + } +} + +PX_INLINE void Articulation::setGlobalPose() +{ + if (!isBuffering()) + { + mArticulation.setGlobalPose(); + } + else + { + markUpdated(Buf::BF_ArticulationPoseUpdate); + } +} + +PX_FORCE_INLINE void Articulation::putToSleep() +{ + mBufferedWakeCounter = 0.0f; + + mBufferedIsSleeping = 1; + if(!isBuffering()) + { + mArticulation.putToSleep(); + } + else + { + markUpdated(Buf::BF_PutToSleep | Buf::BF_WakeCounter); + resetBufferFlag(Buf::BF_WakeUp); + } +} + +PX_FORCE_INLINE void Articulation::initBufferedState() +{ + PX_ASSERT(mBufferedIsSleeping); // this method is only meant to get called when an object is added to the scene + + if(getWakeCounter() == 0.0f) + mBufferedIsSleeping = 1; + else + mBufferedIsSleeping = 0; + // note: to really know whether the articulation is awake/asleep on insertion, we need to go through every link and check whether any of them has + // a parameter setup that keeps it alive. However, the links get added after the articulation, so we do not test those here. After the links + // are added, an additional check will wake the articulation up if necessary. +} + +PX_FORCE_INLINE void Articulation::clearBufferedState() +{ + mBufferedIsSleeping = 1; // the expected state when an object gets removed from the scene +} + +PX_FORCE_INLINE void Articulation::clearBufferedSleepStateChange() +{ + resetBufferFlag(Buf::BF_WakeUp | Buf::BF_PutToSleep); +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_INLINE void Articulation::syncState() +{ + // see comments in Body::syncState + PX_ASSERT( (getControlState() != ControlState::eREMOVE_PENDING) || + (mBufferedIsSleeping && (!isBuffered(Buf::BF_WakeUp | Buf::BF_PutToSleep))) ); + + const PxU32 flags = getBufferFlags(); + + //---- + + if((flags & Buf::BF_WakeCounter) == 0) + mBufferedWakeCounter = mArticulation.getWakeCounter(); + else if (!(flags & (Buf::BF_WakeUp | Buf::BF_PutToSleep))) // if there has been at least one buffered sleep state transition, then there is no use in adjusting the wake counter separately because it will + // get done in the sleep state update. + { + PX_ASSERT(mBufferedWakeCounter == 0.0f); // a wake counter change is always connected to a sleep state change, except one case: if setWakeCounter(0.0f) was called + + mArticulation.setWakeCounter(mBufferedWakeCounter); + } + + //---- + + if((flags & (Buf::BF_WakeUp | Buf::BF_PutToSleep)) == 0) + { + const bool isSimObjectSleeping = mArticulation.isSleeping(); + if(getControlState() != ControlState::eREMOVE_PENDING) // we do not want the simulation sleep state to take effect if the object was removed (free standing objects have buffered state sleeping) + mBufferedIsSleeping = PxU8(isSimObjectSleeping); + else + PX_ASSERT(mBufferedIsSleeping); // this must get set immediately at remove + } + else + { + PX_ASSERT(flags & Buf::BF_WakeCounter); // sleep state transitions always mark the wake counter dirty + PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing an object should clear pending wakeUp/putToSleep operations since the state for a free standing object gets set according to specification. + + if(flags & Buf::BF_PutToSleep) + { + PX_ASSERT(mBufferedIsSleeping); + PX_ASSERT(!(flags & Buf::BF_WakeUp)); + PX_ASSERT(mBufferedWakeCounter == 0.0f); + mArticulation.putToSleep(); + } + else + { + PX_ASSERT(!mBufferedIsSleeping); + PX_ASSERT(flags & Buf::BF_WakeUp); + + mArticulation.wakeUp(mBufferedWakeCounter); + } + } + + //---- + + if(flags&~(Buf::BF_WakeCounter|Buf::BF_WakeUp|Buf::BF_PutToSleep)) // Optimization to avoid all the if-statements below if possible + { +#ifdef USE_NEW_SYSTEM + syncExternalDriveIterations(); + syncInternalDriveIterations(); + syncMaxProjectionIterations(); + syncSeparationTolerance(); + syncSleepThreshold(); + syncSolverIterationCounts(); + syncFreezeThreshold(); +#else + const Buf* PX_RESTRICT buffer = getArticulationBuffer(); + + flush(*buffer); + flush(*buffer); + flush(*buffer); + flush(*buffer); + flush(*buffer); + flush(*buffer); + flush(*buffer); +#endif + } + + //---- + if (flags&(Buf::BF_ArticulationPoseUpdate)) + { + //the root link global pose has been changed. We need to update + //other links's global pose + mArticulation.setGlobalPose(); + } + + // -------------- + // postconditions + // + PX_ASSERT((getControlState() != ControlState::eREMOVE_PENDING) || mBufferedIsSleeping); // nothing in this method should change this + // + // postconditions + // -------------- + + postSyncState(); +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h b/src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h new file mode 100644 index 000000000..67e743665 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h @@ -0,0 +1,578 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_ARTICULATION_JOINT +#define PX_PHYSICS_SCB_ARTICULATION_JOINT + +#include "ScArticulationJointCore.h" +#include "ScbArticulation.h" +#include "ScbBody.h" +#include "ScbBase.h" +#include "ScbDefs.h" + +namespace physx +{ +namespace Scb +{ + +struct ArticulationJointBuffer +{ +#ifdef USE_NEW_SYSTEM + PxTransform mParentPose; + PxTransform mChildPose; + PxQuat mTargetOrientation; + PxVec3 mTargetVelocity; + PxReal mStiffness; + PxReal mDamping; + PxReal mInternalCompliance; + PxReal mExternalCompliance; + PxReal mSwingLimitContactDistance; + bool mSwingLimitEnabled; + PxReal mTangentialStiffness; + PxReal mTangentialDamping; + PxReal mTwistLimitContactDistance; + bool mTwistLimitEnabled; + PxArticulationJointDriveType::Enum mDriveType; +#else + template struct Fns {}; + typedef Sc::ArticulationJointCore Core; + typedef ArticulationJointBuffer Buf; + + SCB_REGULAR_ATTRIBUTE(0, PxTransform, ParentPose) + SCB_REGULAR_ATTRIBUTE(1, PxTransform, ChildPose) + SCB_REGULAR_ATTRIBUTE(2, PxQuat, TargetOrientation) + SCB_REGULAR_ATTRIBUTE(3, PxVec3, TargetVelocity) + SCB_REGULAR_ATTRIBUTE(4, PxReal, Stiffness) + SCB_REGULAR_ATTRIBUTE(5, PxReal, Damping) + SCB_REGULAR_ATTRIBUTE(6, PxReal, FrictionCoefficient) + SCB_REGULAR_ATTRIBUTE(7, PxReal, MaxJointVelocity) + SCB_REGULAR_ATTRIBUTE(8, PxReal, InternalCompliance) + SCB_REGULAR_ATTRIBUTE(9, PxReal, ExternalCompliance) + SCB_REGULAR_ATTRIBUTE(10, PxReal, SwingLimitContactDistance) + SCB_REGULAR_ATTRIBUTE(11, bool, SwingLimitEnabled) + SCB_REGULAR_ATTRIBUTE(12, PxReal, TangentialStiffness) + SCB_REGULAR_ATTRIBUTE(13, PxReal, TangentialDamping) + SCB_REGULAR_ATTRIBUTE(14, PxReal, TwistLimitContactDistance) + SCB_REGULAR_ATTRIBUTE(15, bool, TwistLimitEnabled) + SCB_REGULAR_ATTRIBUTE(16, PxArticulationJointDriveType::Enum, DriveType) + + enum { BF_SwingLimit = 1<<17 }; + enum { BF_TwistLimit = 1<<18 }; +#endif + SCB_REGULAR_ATTRIBUTE(19, PxArticulationJointType::Enum, JointType) + + PxReal mSwingLimitY; + PxReal mSwingLimitZ; + PxReal mTwistLimitLower; + PxReal mTwistLimitUpper; + + Dy::ArticulationLimit mLimits[6]; + Dy::ArticulationDrive mDrives[6]; + PxReal mTargetP[6]; + PxReal mTargetV[6]; + + //Joint limit/drive/whatever flags + enum + { + BF_Limits = 1 << 20, + BF_Drives = 1 << 21, + BF_Targets = 1 << 22, + BF_Motion = 1 << 23 + }; + + PxArticulationMotion::Enum mMotion[PxArticulationAxis::eCOUNT]; + + PxU32 mJointBufferFlags; +}; + +class ArticulationJoint : public Base +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef ArticulationJointBuffer Buf; + typedef Sc::ArticulationJointCore Core; + +public: +// PX_SERIALIZATION + ArticulationJoint(const PxEMPTY) : Base(PxEmpty), mJoint(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PX_INLINE ArticulationJoint( const PxTransform& parentFrame, + const PxTransform& childFrame, + PxArticulationBase::Enum type); + PX_INLINE ~ArticulationJoint(); + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::ArticulationJoint interface + //--------------------------------------------------------------------------------- + +#ifdef USE_NEW_SYSTEM + SCB_MEMBER(ArticulationJoint, mJoint, ParentPose, PxTransform, 0) + SCB_MEMBER(ArticulationJoint, mJoint, ChildPose, PxTransform, 1) + SCB_MEMBER(ArticulationJoint, mJoint, TargetOrientation, PxQuat, 2) + SCB_MEMBER(ArticulationJoint, mJoint, TargetVelocity, PxVec3, 3) + SCB_MEMBER(ArticulationJoint, mJoint, Stiffness, PxReal, 4) + SCB_MEMBER(ArticulationJoint, mJoint, Damping, PxReal, 5) + SCB_MEMBER(ArticulationJoint, mJoint, InternalCompliance, PxReal, 6) + SCB_MEMBER(ArticulationJoint, mJoint, ExternalCompliance, PxReal, 7) + SCB_MEMBER(ArticulationJoint, mJoint, SwingLimitContactDistance, PxReal, 8) + SCB_MEMBER(ArticulationJoint, mJoint, SwingLimitEnabled, bool, 9) + SCB_MEMBER(ArticulationJoint, mJoint, TangentialStiffness, PxReal, 10) + SCB_MEMBER(ArticulationJoint, mJoint, TangentialDamping, PxReal, 11) + SCB_MEMBER(ArticulationJoint, mJoint, TwistLimitContactDistance, PxReal, 12) + SCB_MEMBER(ArticulationJoint, mJoint, TwistLimitEnabled, bool, 13) + SCB_MEMBER(ArticulationJoint, mJoint, DriveType, PxArticulationJointDriveType::Enum, 14) +#else + PX_INLINE PxTransform getParentPose() const { return read(); } + PX_INLINE void setParentPose(const PxTransform& v) { write(v); } + + PX_INLINE PxTransform getChildPose() const { return read(); } + PX_INLINE void setChildPose(const PxTransform& v) { write(v); } + + PX_INLINE PxQuat getTargetOrientation() const { return read(); } + PX_INLINE void setTargetOrientation(const PxQuat& v) { write(v); } + + PX_INLINE PxVec3 getTargetVelocity() const { return read(); } + PX_INLINE void setTargetVelocity(const PxVec3& v) { write(v); } + + PX_INLINE PxReal getStiffness() const { return read(); } + PX_INLINE void setStiffness(PxReal v) { write(v); } + + PX_INLINE PxReal getDamping() const { return read(); } + PX_INLINE void setDamping(PxReal v) { write(v); } + + PX_INLINE PxReal getInternalCompliance() const { return read(); } + PX_INLINE void setInternalCompliance(PxReal v) { write(v); } + + PX_INLINE PxReal getExternalCompliance() const { return read(); } + PX_INLINE void setExternalCompliance(PxReal v) { write(v); } + + PX_INLINE PxReal getTangentialStiffness() const { return read(); } + PX_INLINE void setTangentialStiffness(PxReal v) { write(v); } + + PX_INLINE PxReal getTangentialDamping() const { return read(); } + PX_INLINE void setTangentialDamping(PxReal v) { write(v); } + + PX_INLINE PxReal getSwingLimitContactDistance() const { return read(); } + PX_INLINE void setSwingLimitContactDistance(PxReal v) { write(v); } + + PX_INLINE PxReal getTwistLimitContactDistance() const { return read(); } + PX_INLINE void setTwistLimitContactDistance(PxReal v) { write(v); } + + PX_INLINE PxArticulationJointDriveType::Enum + getDriveType() const { return read(); } + PX_INLINE void setDriveType(PxArticulationJointDriveType::Enum v) + { write(v); } + + PX_INLINE bool getSwingLimitEnabled() const { return read(); } + PX_INLINE void setSwingLimitEnabled(bool v) { write(v); } + + PX_INLINE bool getTwistLimitEnabled() const { return read(); } + PX_INLINE void setTwistLimitEnabled(bool v) { write(v); } +#endif + + PX_INLINE void getSwingLimit(PxReal& yLimit, PxReal& zLimit) const; + PX_INLINE void setSwingLimit(PxReal yLimit, PxReal zLimit); + + PX_INLINE void getTwistLimit(PxReal &lower, PxReal &upper) const; + PX_INLINE void setTwistLimit(PxReal lower, PxReal upper); + + PX_INLINE void setLimit(PxArticulationAxis::Enum axis, PxReal lower, PxReal upper); + PX_INLINE void getLimit(PxArticulationAxis::Enum axis, PxReal& lower, PxReal& upper) const; + + PX_INLINE void setDrive(PxArticulationAxis::Enum axis, PxReal stiffness, PxReal damping, PxReal maxForce, bool isAcceleration); + PX_INLINE void getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration) const; + + PX_INLINE void setDriveTarget(PxArticulationAxis::Enum axis, PxReal targetP); + PX_INLINE PxReal getDriveTarget(PxArticulationAxis::Enum axis) const; + + PX_INLINE void setDriveVelocity(PxArticulationAxis::Enum axis, PxReal targetV); + PX_INLINE PxReal getDriveVelocity(PxArticulationAxis::Enum axis) const; + + PX_INLINE PxArticulationJointType::Enum + getJointType() const { return read(); } + PX_INLINE void setJointType(PxArticulationJointType::Enum v) { write(v); } + + + PX_INLINE PxArticulationMotion::Enum + getMotion(PxArticulationAxis::Enum axis) const; + PX_INLINE void setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion); + + PX_INLINE PxReal getFrictionCoefficient() const { return read(); } + PX_INLINE void setFrictionCoefficient(const PxReal u) { write(u); } + + PX_INLINE PxReal getMaxJointVelocity() const { return read(); } + PX_INLINE void setMaxJointVelocity(const PxReal maxJointV) { write(maxJointV); } + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(); + + PX_FORCE_INLINE const Core& getScArticulationJoint() const { return mJoint; } // Only use if you know what you're doing! + PX_FORCE_INLINE Core& getScArticulationJoint() { return mJoint; } // Only use if you know what you're doing! + + + PX_FORCE_INLINE void setScArticulation(Scb::Articulation* articulation) { mJoint.setArticulation(&articulation->getScArticulation()); } + +private: + Core mJoint; + + PX_FORCE_INLINE const Buf* getBuffer() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Buf* getBuffer() { return reinterpret_cast(getStream()); } + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess {}; + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, mJoint); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, mJoint, v); } + template PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush >(*this, mJoint, buf); } +#endif +}; + +ArticulationJoint::ArticulationJoint(const PxTransform& parentFrame, const PxTransform& childFrame, + PxArticulationBase::Enum type) : + mJoint(parentFrame, childFrame, type) +{ + setScbType(ScbType::eARTICULATION_JOINT); +} + +ArticulationJoint::~ArticulationJoint() +{ +} + +PX_INLINE void ArticulationJoint::getSwingLimit(PxReal &yLimit, PxReal &zLimit) const +{ + if(isBuffered(Buf::BF_SwingLimit)) + { + yLimit = getBuffer()->mSwingLimitY; + zLimit = getBuffer()->mSwingLimitZ; + } + else + mJoint.getSwingLimit(yLimit, zLimit); +} + +PX_INLINE void ArticulationJoint::setSwingLimit(PxReal yLimit, PxReal zLimit) +{ + if(!isBuffering()) + mJoint.setSwingLimit(yLimit, zLimit); + else + { + getBuffer()->mSwingLimitY = yLimit; + getBuffer()->mSwingLimitZ = zLimit; + markUpdated(Buf::BF_SwingLimit); + } +} + +PX_INLINE void ArticulationJoint::getTwistLimit(PxReal &lower, PxReal &upper) const +{ + if(isBuffered(Buf::BF_TwistLimit)) + { + lower = getBuffer()->mTwistLimitLower; + upper = getBuffer()->mTwistLimitUpper; + } + else + mJoint.getTwistLimit(lower, upper); +} + +PX_INLINE void ArticulationJoint::setTwistLimit(PxReal lower, PxReal upper) +{ + if(!isBuffering()) + mJoint.setTwistLimit(lower, upper); + else + { + getBuffer()->mTwistLimitLower = lower; + getBuffer()->mTwistLimitUpper = upper; + markUpdated(Buf::BF_TwistLimit); + } +} + +PX_INLINE void ArticulationJoint::setLimit(PxArticulationAxis::Enum axis, PxReal lower, PxReal upper) +{ + if (!isBuffering()) + mJoint.setLimit(axis, lower, upper); + else + { + if (!isBuffered(Buf::BF_Limits)) + { + Dy::ArticulationLimit* limits = getBuffer()->mLimits; + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.getLimit(PxArticulationAxis::Enum(i), limits[i].low, limits[i].high); + } + } + getBuffer()->mLimits[axis].low = lower; + getBuffer()->mLimits[axis].high = upper; + markUpdated(Buf::BF_Limits); + } +} +PX_INLINE void ArticulationJoint::getLimit(PxArticulationAxis::Enum axis, PxReal& lower, PxReal& upper) const +{ + if (isBuffered(Buf::BF_Limits)) + { + lower = getBuffer()->mLimits[axis].low; + upper = getBuffer()->mLimits[axis].high; + } + else + mJoint.getLimit(axis, lower, upper); +} + +PX_INLINE void ArticulationJoint::setDrive(PxArticulationAxis::Enum axis, PxReal stiffness, PxReal damping, PxReal maxForce, bool isAcceleration) +{ + if (!isBuffering()) + mJoint.setDrive(axis, stiffness, damping, maxForce, isAcceleration); + else + { + if (!isBuffered(Buf::BF_Drives)) + { + Dy::ArticulationDrive* drives = getBuffer()->mDrives; + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.getDrive(PxArticulationAxis::Enum(i), drives[i].stiffness, drives[i].damping, drives[i].maxForce, drives[i].isAcceleration); + } + } + getBuffer()->mDrives[axis].stiffness = stiffness; + getBuffer()->mDrives[axis].damping = damping; + getBuffer()->mDrives[axis].maxForce = maxForce; + getBuffer()->mDrives[axis].isAcceleration = isAcceleration; + markUpdated(Buf::BF_Drives); + } +} +PX_INLINE void ArticulationJoint::getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration) const +{ + if (!isBuffered(Buf::BF_Drives)) + mJoint.getDrive(axis, stiffness, damping, maxForce, isAcceleration); + else + { + stiffness = getBuffer()->mDrives[axis].stiffness; + damping = getBuffer()->mDrives[axis].damping; + maxForce = getBuffer()->mDrives[axis].maxForce; + isAcceleration = getBuffer()->mDrives[axis].isAcceleration; + } +} + +PX_INLINE void ArticulationJoint::setDriveTarget(PxArticulationAxis::Enum axis, PxReal targetP) +{ + if (!isBuffering()) + mJoint.setTargetP(axis, targetP); + else + { + if (!isBuffered(Buf::BF_Targets)) + { + PxReal* targetP_ = getBuffer()->mTargetP; + PxReal* targetV_ = getBuffer()->mTargetV; + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + targetP_[i] = mJoint.getTargetP(PxArticulationAxis::Enum(i)); + targetV_[i] = mJoint.getTargetV(PxArticulationAxis::Enum(i)); + } + } + getBuffer()->mTargetP[axis] = targetP; + markUpdated(Buf::BF_Targets); + } +} +PX_INLINE PxReal ArticulationJoint::getDriveTarget(PxArticulationAxis::Enum axis) const +{ + if (!isBuffered(Buf::BF_Targets)) + return mJoint.getTargetP(axis); + else + { + return getBuffer()->mTargetP[axis]; + } +} + +PX_INLINE void ArticulationJoint::setDriveVelocity(PxArticulationAxis::Enum axis, PxReal targetP) +{ + if (!isBuffering()) + mJoint.setTargetV(axis, targetP); + else + { + if (!isBuffered(Buf::BF_Targets)) + { + PxReal* targetP_ = getBuffer()->mTargetP; + PxReal* targetV_ = getBuffer()->mTargetV; + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + targetP_[i] = mJoint.getTargetP(PxArticulationAxis::Enum(i)); + targetV_[i] = mJoint.getTargetV(PxArticulationAxis::Enum(i)); + } + } + getBuffer()->mTargetV[axis] = targetP; + markUpdated(Buf::BF_Targets); + } +} +PX_INLINE PxReal ArticulationJoint::getDriveVelocity(PxArticulationAxis::Enum axis) const +{ + if (!isBuffered(Buf::BF_Targets)) + return mJoint.getTargetV(axis); + else + { + return getBuffer()->mTargetV[axis]; + } +} + + +PX_INLINE void ArticulationJoint::setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) +{ + if (!isBuffering()) + mJoint.setMotion(axis, motion); + else + { + if (!isBuffered(Buf::BF_Motion)) + { + PxArticulationMotion::Enum* motionAxis = getBuffer()->mMotion; + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + motionAxis[i] = mJoint.getMotion(PxArticulationAxis::Enum(i)); + } + } + + getBuffer()->mMotion[axis] = motion; + markUpdated(PxU32(Buf::BF_Motion)); + + } +} + +PX_INLINE PxArticulationMotion::Enum ArticulationJoint::getMotion(PxArticulationAxis::Enum axis) const +{ + if (isBuffered(PxU32(Buf::BF_Motion))) + { + return getBuffer()->mMotion[axis]; + } + else + return mJoint.getMotion(axis); +} + + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_INLINE void ArticulationJoint::syncState() +{ + const PxU32 flags = getBufferFlags(); + if(flags) // Optimization to avoid all the if-statements below if possible + { + const Buf& buffer = *getBuffer(); + +#ifdef USE_NEW_SYSTEM + syncParentPose(); + syncChildPose(); + syncTargetOrientation(); + syncTargetVelocity(); + syncStiffness(); + syncDamping(); + syncInternalCompliance(); + syncExternalCompliance(); + syncSwingLimitContactDistance(); + syncSwingLimitEnabled(); + syncTwistLimitContactDistance(); + syncTwistLimitEnabled(); + syncTangentialStiffness(); + syncTangentialDamping(); + syncDriveType(); +#else + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); +#endif + + if(isBuffered(Buf::BF_SwingLimit)) + mJoint.setSwingLimit(buffer.mSwingLimitY, buffer.mSwingLimitZ); + + if(isBuffered(Buf::BF_TwistLimit)) + mJoint.setTwistLimit(buffer.mTwistLimitLower, buffer.mTwistLimitUpper); + + if (isBuffered(Buf::BF_Motion)) + { + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.setMotion(PxArticulationAxis::Enum(i), buffer.mMotion[i]); + } + } + + if (isBuffered(Buf::BF_Limits)) + { + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.setLimit(PxArticulationAxis::Enum(i), buffer.mLimits[i].low, buffer.mLimits[i].high); + } + } + + if (isBuffered(Buf::BF_Drives)) + { + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.setDrive(PxArticulationAxis::Enum(i), buffer.mDrives[i].stiffness, buffer.mDrives[i].damping, buffer.mDrives[i].maxForce, buffer.mDrives[i].isAcceleration); + } + } + + if (isBuffered(Buf::BF_Targets)) + { + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.setTargetP(PxArticulationAxis::Enum(i), buffer.mTargetP[i]); + mJoint.setTargetV(PxArticulationAxis::Enum(i), buffer.mTargetV[i]); + } + } + } + + postSyncState(); +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp new file mode 100644 index 000000000..6fc476f57 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScbBase.h" +#include "ScbNpDeps.h" + +using namespace physx; +using namespace Scb; + +void Base::destroy() +{ + if(!isBuffering()) + NpDestroy(*this); + else + { + // Schedule for destroy + PX_ASSERT(!(getControlFlags() & ControlFlag::eIS_RELEASED)); + PX_ASSERT(getControlState() == ControlState::eREMOVE_PENDING); + setControlFlag(ControlFlag::eIS_RELEASED); + } +} diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbBase.h b/src/PhysX/physx/source/physx/src/buffering/ScbBase.h new file mode 100644 index 000000000..c8b160edd --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbBase.h @@ -0,0 +1,318 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_BASE +#define PX_PHYSICS_SCB_BASE + +#include "CmPhysXCommon.h" +#include "ScbScene.h" + +namespace physx +{ +#if PX_SUPPORT_PVD + // PT: updatePvdProperties() is overloaded and the compiler needs to know 'this' type to do the right thing. + // Thus we can't just move this as an inlined Base function. + #define UPDATE_PVD_PROPERTIES_OBJECT() { \ + Scb::Scene* scene_ = getScbSceneForAPI(); \ + if(scene_ && !insertPending() ) \ + scene_->getScenePvdClient().updatePvdProperties(this); } +#else + #define UPDATE_PVD_PROPERTIES_OBJECT() {} +#endif + +namespace Scb +{ + struct ControlState + { + enum Enum + { + /** + \brief The object is not in the scene. + + The object has not been added to a scene yet. This is the default when an object gets created. + + In this state... + \li ...user changes get written to Core directly. + \li ...the object can not be in the list of dirty objects. + \li ...the object can not be marked as eIS_UPDATED or eIS_RELEASED. + */ + eNOT_IN_SCENE = 0, + + /** + \brief The object waits to get inserted into the scene internally. + + The object has been added to the scene while the simulation is running and is waiting to get fully registered in all layers. + + In this state... + \li ...user changes get written to Core directly (since the object has not yet registered in the inner layers, hence, will not get accessed internally). + \li ...the object is in the list of dirty objects. + \li ...the object can not be marked as eIS_UPDATED or eIS_RELEASED. + */ + eINSERT_PENDING = 1, + + /** + \brief The object is registered in the scene internally. + + The object has been added to the scene and is fully registered in all layers. + + In this state... + \li ...user changes get written to a buffer object and get synced with Core at a sync point. + \li ...the object can be in the list of dirty objects. + \li ...the object can be marked as eIS_UPDATED but not eIS_RELEASED. + */ + eIN_SCENE = 2, + + /** + \brief The object waits to get removed from the scene internally. + + The object is in the scene and fully registered in all layers but has been removed while the simulation is running and is now + waiting to get unregistered from all layers. + + In this state... + \li ...user changes get written to a buffer object and get synced with Core at a sync point. + \li ...the object is in the list of dirty objects. + \li ...the object can be marked as eIS_UPDATED or eIS_RELEASED. + */ + eREMOVE_PENDING = 3 + }; + }; + + struct ControlFlag + { + enum Enum + { + /** + \brief An object property/state has been changed. + + A property/state of the object has been changed and needs to get synced to Core. Insertion & removal don't count. + */ + eIS_UPDATED = 1, + + /** + \brief The object has been released. + + The object has not just been removed from the scene it has been released as well. The object will get destroyed after the sync has been completed. + */ + eIS_RELEASED = 2 + }; + }; + + /** + \brief Base class for objects that should support buffering. + + This class has members to track the buffering related object state and mark which properties have been changed and need to get synced at sync points. + */ + class Base + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_NOCOPY(Base) + public: +// PX_SERIALIZATION + Base(const PxEMPTY) : + mScene (NULL), + mStreamPtr (NULL) + { + resetAllBufferFlags(); + resetControl(ControlState::eNOT_IN_SCENE); + } + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + Base() : + mScene (NULL), + mStreamPtr (NULL) + { +#if PX_DEBUG + setScbType(ScbType::eUNDEFINED); + resetControl(ControlState::eNOT_IN_SCENE); + resetAllBufferFlags(); + PX_ASSERT(mControlState==0); +#endif + mControlState = 0; + } + + PX_INLINE bool isBuffering() const + { + const ControlState::Enum state = getControlState(); + return state == ControlState::eREMOVE_PENDING || // pending remove not possible if not buffered + (state == ControlState::eIN_SCENE && mScene->isPhysicsBuffering()); + } + + PX_FORCE_INLINE Ps::IntBool insertPending() const { return getControlState() == ControlState::eINSERT_PENDING; } + PX_FORCE_INLINE ScbType::Enum getScbType() const { return ScbType::Enum((mControlState&eTYPE_MASK)>>eTYPE_SHIFT); } + PX_FORCE_INLINE void setScbType(ScbType::Enum type) { mControlState = (mControlState&~eTYPE_MASK)|(type<>eCONTROLFLAG_SHIFT; } + PX_FORCE_INLINE void setControlFlag(ControlFlag::Enum f) { mControlState |= (f<>eCONTROLSTATE_SHIFT);} + PX_FORCE_INLINE void setControlState(ControlState::Enum s) { mControlState = (mControlState&~eCONTROLSTATE_MASK)|(s<scheduleForUpdate(*this); } + + /** + \brief Destroys the object. + + If the simulation is not running, this will release the object, else it will just mark the object as eIS_RELEASED and thus it will get deleted + when the next sync point is reached. This method expects that the object has been removed from the scene first (or waits for removal). + */ + void destroy(); + + /** + \brief Test if a property has been changed by the user. + + \param[in] flag The flag of the property to test for. Has to be within the bounds of eBUFFERFLAG_MASK. + \return Positive value if the property has been changed by the user. + */ + PX_FORCE_INLINE Ps::IntBool isBuffered(PxU32 flag) const + { + PX_ASSERT((flag & eBUFFERFLAG_MASK) == flag); + return Ps::IntBool(mControlState & flag); + } + + PX_FORCE_INLINE PxU8* getStream() { return mStreamPtr ? mStreamPtr : mStreamPtr = mScene->getStream(getScbType()); } + PX_FORCE_INLINE const PxU8* getStream() const { PX_ASSERT(mStreamPtr); return mStreamPtr; } + + /** + \brief Helper method to trigger object tracking after a property change. + + This method will flag the marked property as changed and will add the object to the list of updated objects if it is not + registered already. + + \param[in] flag The flag of the changed property. Has to be within the bounds of eBUFFERFLAG_MASK. + */ + PX_FORCE_INLINE void markUpdated(PxU32 flag) + { + PX_ASSERT((flag & eBUFFERFLAG_MASK) == flag); + scheduleForUpdate(); + mControlState |= flag; + } + protected: + ~Base(){} + + PX_FORCE_INLINE PxU32 getBufferFlags() const { return mControlState & eBUFFERFLAG_MASK; } + PX_FORCE_INLINE void setBufferFlag(PxU32 flag) { PX_ASSERT((flag & eBUFFERFLAG_MASK) == flag); mControlState |= flag; } + PX_FORCE_INLINE void resetBufferFlag(PxU32 flag) { PX_ASSERT((flag & eBUFFERFLAG_MASK) == flag); mControlState &= ~flag; } + PX_FORCE_INLINE void resetAllBufferFlags() { mControlState &=~eBUFFERFLAG_MASK; } + + /** + \brief Cleanup method after the object has been synced. + + Every buffering object should implement a syncState() method where the buffered user changes get synced with Core. Call this method at the end of the + syncState() method to clear all buffer flags and references. + */ + PX_FORCE_INLINE void postSyncState() + { + // DS: this can get called even when mScene == NULL, by removeAggregate (see AggregateFreeStandingCreateDelete test) + // TODO(dsequeira): investigate that when the dust settles on shape refactoring + PX_ASSERT(getControlState()!=ControlState::eNOT_IN_SCENE || mScene == NULL); + PX_ASSERT(getScbType()!=ScbType::eUNDEFINED); + + mStreamPtr = NULL; + resetAllBufferFlags(); +// resetControlFlag(ControlFlag::eIS_UPDATED); + } + private: + enum { eBUFFERFLAG_MASK = (1<<24) - 1, + eTYPE_MASK = 15<<24, + eCONTROLFLAG_MASK = 3<<28, + eCONTROLSTATE_MASK = 3<<30, + eCONTROL_MASK = 15<<28}; + + enum { eTYPE_SHIFT = 24, + eCONTROLFLAG_SHIFT = 28, + eCONTROLSTATE_SHIFT = 30}; + + /** + \brief Scene pointer. + + The scene pointer get set as soon as the user adds an object to the scene. However, it does not get cleared until the object has been + removed from the scene internally, i.e., removing an object while the simulation is running will not set this pointer to NULL immediately. + */ + Scb::Scene* mScene; + + /** + \brief Mix of buffering related states/flags. + + highest lowest + | 2 | 2 | 4 | 24 | + | ControlState | ControlFlag | ScbType | buffer attribute flags | + + The buffer attribute flags mark which of the properties have been updated. The specific implementation of this class defines those flags. + */ + PxU32 mControlState; + + /** + \brief Data buffer to store property/state changes temporarily. + + Pointer to a temporary struct where user changes made while the simulation is running are buffered. The structure is currently as large as necessary to hold all + properties of a buffered object. Even if only a single property of an object gets changed, the whole structure is assigned. The motivation for this was to keep + the implementation complexity low based on the assumption that users will not change all objects in a scene every frame. The temporary buffer gets assigned on demand + and is returned to pools after the data has been synced to Core. The pointer is then set to NULL again. + This kind of buffer can not be used for properties that get written by the simulation (for example, pose, velocity, sleep state). Those need to be permanently buffered + in the specific implementation of this class, else the user might get an inconsistent picture of the scene object state. + + @see postSyncState() + */ + PxU8* mStreamPtr; + }; + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbBody.h b/src/PhysX/physx/source/physx/src/buffering/ScbBody.h new file mode 100644 index 000000000..33bee2a54 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbBody.h @@ -0,0 +1,1134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_BODY +#define PX_PHYSICS_SCB_BODY + +#include "ScBodyCore.h" + +#include "ScbRigidObject.h" +#include "CmUtils.h" +#include "PsUtilities.h" +#include "PxRigidDynamic.h" +#include "ScbDefs.h" +#include "GuSIMDHelpers.h" + +namespace physx +{ +namespace Scb +{ +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +struct BodyBuffer : public RigidObjectBuffer //once RigidObject has its own buffered elements, derive from that instead +{ +#ifdef USE_NEW_SYSTEM + // PT: I think these start from 0 because they're stored in mBodyBufferFlags instead of the base + // regular attributes + PxReal mInverseMass; + PxVec3 mInverseInertia; + PxReal mLinearDamping; + PxReal mAngularDamping; + PxReal mMaxAngVelSq; + PxReal mMaxLinVelSq; + PxReal mSleepThreshold; + PxReal mCCDAdvanceCoefficient; + PxReal mContactReportThreshold; + PxU16 mSolverIterationCounts; + PX_ALIGN(16, PxTransform) mBody2Actor; + PxReal mMaxPenetrationBias; + PxReal mFreezeThreshold; + PxReal mMaxContactImpulse; + PxRigidDynamicLockFlags mRigidDynamicLockFlags; +#else + template struct Fns {}; // TODO: make the base class traits visible + typedef Sc::BodyCore Core; + typedef BodyBuffer Buf; + + // PT: I think these start from 0 because they're stored in mBodyBufferFlags instead of the base + // regular attributes + SCB_REGULAR_ATTRIBUTE(0, PxReal, InverseMass) + SCB_REGULAR_ATTRIBUTE(1, PxVec3, InverseInertia) + SCB_REGULAR_ATTRIBUTE(2, PxReal, LinearDamping) + SCB_REGULAR_ATTRIBUTE(3, PxReal, AngularDamping) + SCB_REGULAR_ATTRIBUTE(4, PxReal, MaxAngVelSq) + SCB_REGULAR_ATTRIBUTE(5, PxReal, MaxLinVelSq) + SCB_REGULAR_ATTRIBUTE(6, PxReal, SleepThreshold) + SCB_REGULAR_ATTRIBUTE(7, PxReal, CCDAdvanceCoefficient) + SCB_REGULAR_ATTRIBUTE(8, PxReal, ContactReportThreshold) + SCB_REGULAR_ATTRIBUTE(9, PxU16, SolverIterationCounts) + SCB_REGULAR_ATTRIBUTE_ALIGNED(10,PxTransform, Body2Actor, 16) + SCB_REGULAR_ATTRIBUTE(11, PxReal, MaxPenetrationBias) + SCB_REGULAR_ATTRIBUTE(12, PxReal, FreezeThreshold) + SCB_REGULAR_ATTRIBUTE(13, PxReal, MaxContactImpulse) + SCB_REGULAR_ATTRIBUTE(14, PxRigidDynamicLockFlags, RigidDynamicLockFlags) +#endif + // irregular attributes + + PX_ALIGN(16, PxTransform) mKinematicTarget; + PxVec3 mLinAcceleration; + PxVec3 mAngAcceleration; + PxVec3 mLinDeltaVelocity; + PxVec3 mAngDeltaVelocity; + + PxRigidBodyFlags mRigidBodyFlags; + + enum + { + BF_RigidBodyFlags = 1u<<14u, + BF_KinematicTarget = 1u<<15u, + BF_AccelerationLinear = 1u<<16u, + BF_AccelerationAngular = 1u<<17u, + BF_Acceleration = BF_AccelerationLinear|BF_AccelerationAngular, + BF_DeltaVelocityLinear = 1u<<18u, + BF_DeltaVelocityAngular = 1u<<19u, + BF_DeltaVelocity = BF_DeltaVelocityLinear|BF_DeltaVelocityAngular, + BF_Body2World = 1u<<20u, + BF_Body2World_CoM = 1u<<21u, // the body pose was adjusted because of a center of mass change only + BF_LinearVelocity = 1u<<22u, + BF_AngularVelocity = 1u<<23u, + BF_WakeCounter = 1u<<24u, + BF_PutToSleep = 1u<<25u, + BF_WakeUp = 1u<<26u, + BF_ClearAccelerationLinear = 1u<<27u, + BF_ClearAccelerationAngular = 1u<<28u, + BF_ClearAcceleration = BF_ClearAccelerationLinear|BF_ClearAccelerationAngular, + BF_ClearDeltaVelocityLinear = 1u<<29u, + BF_ClearDeltaVelocityAngular= 1u<<30u, + BF_ClearDeltaVelocity = BF_ClearDeltaVelocityLinear|BF_ClearDeltaVelocityAngular + }; + + BodyBuffer(): mLinAcceleration(0), mAngAcceleration(0), mLinDeltaVelocity(0), mAngDeltaVelocity(0) {} +}; + +#if PX_VC + #pragma warning(pop) +#endif + +class Body : public Scb::RigidObject +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef BodyBuffer Buf; + typedef Sc::BodyCore Core; + +public: +// PX_SERIALIZATION + Body(const PxEMPTY) : Scb::RigidObject(PxEmpty), mBodyCore(PxEmpty), mBufferedIsSleeping(1) { PX_ASSERT(mBodyBufferFlags == 0); } + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + PX_INLINE Body(PxActorType::Enum type, const PxTransform& bodyPose); + PX_INLINE ~Body() {} + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::BodyCore interface + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE const PxTransform& getBody2World() const { return mBufferedBody2World; } // PT: important: keep returning an address here (else update prefetch in SceneQueryManager::addShapes) + PX_INLINE void setBody2World(const PxTransform& p, bool asPartOfBody2ActorChange); + + PX_FORCE_INLINE const PxVec3& getLinearVelocity() const { return mBufferedLinVelocity; } + PX_INLINE void setLinearVelocity(const PxVec3& v); + PX_FORCE_INLINE const PxVec3& getAngularVelocity() const { return mBufferedAngVelocity; } + PX_INLINE void setAngularVelocity(const PxVec3& v); + + PX_FORCE_INLINE void wakeUp(); + PX_FORCE_INLINE void putToSleep(); + PX_FORCE_INLINE PxReal getWakeCounter() const { return mBufferedWakeCounter; } + PX_INLINE void setWakeCounter(PxReal w); + PX_FORCE_INLINE bool isSleeping() const { return (mBufferedIsSleeping != 0); } + +#ifdef USE_NEW_SYSTEM + SCB_MEMBER(Body, mBodyCore, InverseMass, PxReal, 0) + SCB_MEMBER(Body, mBodyCore, InverseInertia, PxVec3, 1) + SCB_MEMBER(Body, mBodyCore, LinearDamping, PxReal, 2) + SCB_MEMBER(Body, mBodyCore, AngularDamping, PxReal, 3) + SCB_MEMBER(Body, mBodyCore, MaxAngVelSq, PxReal, 4) + SCB_MEMBER(Body, mBodyCore, MaxLinVelSq, PxReal, 5) + SCB_MEMBER(Body, mBodyCore, SleepThreshold, PxReal, 6) + SCB_MEMBER(Body, mBodyCore, CCDAdvanceCoefficient, PxReal, 7) + SCB_MEMBER(Body, mBodyCore, ContactReportThreshold, PxReal, 8) + SCB_MEMBER(Body, mBodyCore, SolverIterationCounts, PxU16, 9) + SCB_MEMBER(Body, mBodyCore, Body2Actor, const PxTransform&, 10) + SCB_MEMBER(Body, mBodyCore, MaxPenetrationBias, PxReal, 11) + SCB_MEMBER(Body, mBodyCore, FreezeThreshold, PxReal, 12) + SCB_MEMBER(Body, mBodyCore, MaxContactImpulse, PxReal, 13) + SCB_MEMBER(Body, mBodyCore, RigidDynamicLockFlags, PxRigidDynamicLockFlags, 14) + + // PT: TODO: fix the inconsistent naming + PX_FORCE_INLINE PxReal getMinCCDAdvanceCoefficient() const { return getCCDAdvanceCoefficient(); } + PX_FORCE_INLINE void setMinCCDAdvanceCoefficient(PxReal v) { setCCDAdvanceCoefficient(v); } + PX_FORCE_INLINE PxRigidDynamicLockFlags getLockFlags() const { return getRigidDynamicLockFlags(); } + PX_FORCE_INLINE void setLockFlags(PxRigidDynamicLockFlags f) { setRigidDynamicLockFlags(f); } +#else + PX_INLINE const PxTransform& getBody2Actor() const { return read(); } + PX_INLINE void setBody2Actor(const PxTransform& m) { write(m); } + + PX_INLINE PxReal getInverseMass() const { return read(); } + PX_INLINE void setInverseMass(PxReal m) { write(m); } + + PX_INLINE PxVec3 getInverseInertia() const { return read(); } + PX_INLINE void setInverseInertia(const PxVec3& i) { write(i); } + + PX_INLINE PxReal getLinearDamping() const { return read(); } + PX_INLINE void setLinearDamping(PxReal d) { write(d); } + + PX_INLINE PxReal getAngularDamping() const { return read(); } + PX_INLINE void setAngularDamping(PxReal d) { write(d); } + + PX_INLINE PxReal getMaxAngVelSq() const { return read(); } + PX_INLINE void setMaxAngVelSq(PxReal v) { write(v); } + + PX_INLINE PxReal getSleepThreshold() const { return read(); } + PX_INLINE void setSleepThreshold(PxReal t) { write(t); } + + PX_INLINE void setMinCCDAdvanceCoefficient(PxReal minCCDAdvanceCoefficient){write(minCCDAdvanceCoefficient);} + PX_INLINE PxReal getMinCCDAdvanceCoefficient() const { return read();} + + PX_INLINE PxReal getContactReportThreshold() const { return read(); } + PX_INLINE void setContactReportThreshold(PxReal t) { write(t); } + + PX_INLINE PxReal getMaxLinVelSq() const { return read(); } + PX_INLINE void setMaxLinVelSq(PxReal v) { write(v); } + + PX_INLINE PxU16 getSolverIterationCounts() const { return Ps::to16(read()); } + PX_INLINE void setSolverIterationCounts(PxU16 c) { write(c); } + + PX_INLINE PxReal getMaxPenetrationBias() const { return read(); } + PX_INLINE void setMaxPenetrationBias(PxReal t) { write(t); } + + PX_INLINE PxReal getFreezeThreshold() const { return read(); } + PX_INLINE void setFreezeThreshold(PxReal t) { write(t); } + + PX_INLINE PxReal getMaxContactImpulse() const { return read(); } + PX_INLINE void setMaxContactImpulse(PxReal t) { write(t); } + + PX_INLINE PxRigidDynamicLockFlags getLockFlags() const { return read(); } + PX_INLINE void setLockFlags(PxRigidDynamicLockFlags f) { write(f); } +#endif + + PX_INLINE PxRigidBodyFlags getFlags() const { return (isBuffered(Buf::BF_RigidBodyFlags)) ? getBodyBuffer()->mRigidBodyFlags : mBodyCore.getFlags(); } + PX_INLINE void setFlags(PxRigidBodyFlags f); + + PX_INLINE PxU32 getInternalIslandNodeIndex() const { return mBodyCore.getInternalIslandNodeIndex(); } + + PX_INLINE void addSpatialAcceleration(const PxVec3* linAcc, const PxVec3* angAcc); + PX_INLINE void setSpatialAcceleration(const PxVec3* linAcc, const PxVec3* angAcc); + PX_INLINE void clearSpatialAcceleration(bool force, bool torque); + PX_INLINE void addSpatialVelocity(const PxVec3* linVelDelta, const PxVec3* angVelDelta); + PX_INLINE void clearSpatialVelocity(bool force, bool torque); + + PX_INLINE bool getKinematicTarget(PxTransform& p) const; + PX_INLINE void setKinematicTarget(const PxTransform& p); + + PX_FORCE_INLINE void onOriginShift(const PxVec3& shift); + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(); + PX_INLINE void syncCollisionWriteThroughState(); + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mBodyCore); } + + /** + \brief Shadowed method of #Scb::Base::markUpdated() to store the buffered property flags in a separate location (ran out of flag space) + */ + PX_FORCE_INLINE void markUpdated(PxU32 flag); + + /** + \brief Shadowed method of #Scb::Base::isBuffered() to check the buffered property flags (ran out of flag space) + */ + PX_FORCE_INLINE Ps::IntBool isBuffered(PxU32 flag) const; + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- +public: + PX_FORCE_INLINE const Sc::BodyCore& getScBody() const { return mBodyCore; } // Only use if you know what you're doing! + PX_FORCE_INLINE Sc::BodyCore& getScBody() { return mBodyCore; } // Only use if you know what you're doing! + + PX_FORCE_INLINE static const Body& fromSc(const Core& a) { return static_cast(Actor::fromSc(a)); } + PX_FORCE_INLINE static Body& fromSc(Core &a) { return static_cast(Actor::fromSc(a)); } + + PX_FORCE_INLINE bool hasKinematicTarget() const; + PX_FORCE_INLINE void clearSimStateDataForPendingInsert(); + PX_FORCE_INLINE void transitionSimStateDataForPendingInsert(); + + PX_INLINE PxMat33 getGlobalInertiaTensorInverse() const; + + PX_FORCE_INLINE bool checkSleepReadinessBesidesWakeCounter(); + PX_FORCE_INLINE void initBufferedState(); + PX_FORCE_INLINE void clearBufferedState(); + PX_FORCE_INLINE void clearBufferedSleepStateChange(); + + PX_INLINE void wakeUpInternal(PxReal wakeCounter); + PX_INLINE void putToSleepInternal(); + + PX_FORCE_INLINE void switchBodyToNoSim(); + +private: + Sc::BodyCore mBodyCore; + //--------------------------------------------------------------------------------- + // Permanently buffered data (simulation written data) + //--------------------------------------------------------------------------------- + PxTransform mBufferedBody2World; + PxVec3 mBufferedLinVelocity; + PxVec3 mBufferedAngVelocity; + PxReal mBufferedWakeCounter; + PxU32 mBufferedIsSleeping; // Note: If the object is not in a scene this value must be true, i.e., positive + // Don't need 4 bytes but otherwise there is padding here anyway. + PxU32 mBodyBufferFlags; // Stores the buffered property flags since there is not enough space in usual location in Scb::Base. + + PX_FORCE_INLINE const Buf* getBodyBuffer() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Buf* getBodyBuffer() { return reinterpret_cast(getStream()); } + + PX_INLINE void accumulate(PxVec3& linear, PxVec3& angular, PxU32 linearFlag, PxU32 angularFlag, const PxVec3* linIncrement, const PxVec3* angIncrement) + { + PxU32 flag = 0; + if(linIncrement) + { + linear += *linIncrement; + flag |= linearFlag; + } + + if(angIncrement) + { + angular += *angIncrement; + flag |= angularFlag; + } + + markUpdated(flag); + } + + PX_INLINE void resetAccumulator(PxVec3& linear, PxVec3& angular, PxU32 linearFlag, PxU32 angularFlag, PxU32 raisedFlagLinear, PxU32 raisedFlagAngular, bool force, bool torque) + { + PxU32 flags = mBodyBufferFlags; + PxU32 raisedFlags = 0; + if(force) + { + linear = PxVec3(0.0f); + flags &= ~linearFlag; + raisedFlags |= raisedFlagLinear; + } + + if(torque) + { + angular = PxVec3(0.0f); + flags &= ~angularFlag; + raisedFlags |= raisedFlagAngular; + } + + //This is for the split sim logic to support write-through spatial accelerations. It is for the condition where a spatial acceleration has been applied prior to + //collide(). However, a clear spatial acceleration command is raised by the user between collide() and fetchCollision(), we need to raised this + //flag to clear the previous applied spatial acceleration in the unbuffered state so that this spatial acceleration can be cleared correctly in fetchCollision(). + flags |= raisedFlags; + mBodyBufferFlags = flags; + scheduleForUpdate(); + } + + PX_FORCE_INLINE void setBufferedParamsForAsleep() // use this in the non-buffered case to set the buffered properties + { + mBufferedIsSleeping = 1; + mBufferedWakeCounter = 0.0f; + mBufferedLinVelocity = PxVec3(0.0f); + mBufferedAngVelocity = PxVec3(0.0f); + // no need to clear forces since that will be the job of the corresponding core/sim methods + } + + PX_FORCE_INLINE void setBufferedParamsForAwake(PxReal wakeCounter) // use this in the non-buffered case to set the buffered properties + { + mBufferedIsSleeping = 0; + mBufferedWakeCounter = wakeCounter; + } + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess {}; + + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, mBodyCore); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, mBodyCore, v); } + template PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush >(*this, mBodyCore, buf); } +#endif +}; + + +PX_INLINE Body::Body(PxActorType::Enum type, const PxTransform& bodyPose) : mBodyCore(type, bodyPose) +{ + setScbType(ScbType::eBODY); + + mBufferedBody2World = mBodyCore.getBody2World(); + mBufferedLinVelocity = mBodyCore.getLinearVelocity(); + mBufferedAngVelocity = mBodyCore.getAngularVelocity(); + mBufferedWakeCounter = mBodyCore.getWakeCounter(); + mBufferedIsSleeping = 1; // this is the specified value for free standing objects + mBodyBufferFlags = 0; +} + +PX_INLINE void Body::setBody2World(const PxTransform& p, bool asPartOfBody2ActorChange) +{ + mBufferedBody2World = p; + + if(!isBuffering()) + { + mBodyCore.setBody2World(p); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if(!asPartOfBody2ActorChange) + { + // call was triggered by a setGlobalPose(). This means the simulated body pose will get + // overwritten by the user value, so we do not need to adjust it. + + mBodyBufferFlags &= ~Buf::BF_Body2World_CoM; + } + else if(!(mBodyBufferFlags & Buf::BF_Body2World)) + { + // there has been no setGlobalPose() on the body yet and the center of mass changes. + // This case needs special treatment because the simulation results for such a body will be based on + // the old center of mass but need to get translated to the new center of mass. + + mBodyBufferFlags |= Buf::BF_Body2World_CoM; + } + + markUpdated(Buf::BF_Body2World); + } +} + +PX_INLINE void Body::setLinearVelocity(const PxVec3& v) +{ + mBufferedLinVelocity = v; + + if(!isBuffering()) + { + mBodyCore.setLinearVelocity(v); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + markUpdated(Buf::BF_LinearVelocity); +} + +PX_INLINE void Body::setAngularVelocity(const PxVec3& v) +{ + mBufferedAngVelocity = v; + + if(!isBuffering()) + { + mBodyCore.setAngularVelocity(v); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + markUpdated(Buf::BF_AngularVelocity); +} + + +PX_INLINE void Body::wakeUpInternal(PxReal wakeCounter) +{ + PX_ASSERT(getScbScene()); + + if(!isBuffering()) + { + setBufferedParamsForAwake(wakeCounter); + mBodyCore.wakeUp(wakeCounter); + } + else + { + mBufferedIsSleeping = 0; + mBufferedWakeCounter = wakeCounter; + markUpdated(Buf::BF_WakeUp | Buf::BF_WakeCounter); + mBodyBufferFlags &= ~Buf::BF_PutToSleep; + } +} + +PX_FORCE_INLINE void Body::wakeUp() +{ + PX_ASSERT(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)); + Scene* scene = getScbScene(); + PX_ASSERT(scene); // only allowed for an object in a scene + + wakeUpInternal(scene->getWakeCounterResetValue()); +} + +PX_INLINE void Body::putToSleepInternal() +{ + if(!isBuffering()) + { + setBufferedParamsForAsleep(); + mBodyCore.putToSleep(); + } + else + { + mBufferedIsSleeping = 1; + mBufferedWakeCounter = 0.0f; + // it is necessary to set the velocities as a buffered operation (not just adjust the buffered velocities) because + // a putToSleep can be followed by a wakeUp in which case only the wakeUp will get processed on sync, however, the velocities + // still need to get set to 0. + setLinearVelocity(PxVec3(0.0f)); + setAngularVelocity(PxVec3(0.0f)); + mBodyBufferFlags &= ~(Buf::BF_Acceleration | Buf::BF_DeltaVelocity | Buf::BF_KinematicTarget); + + markUpdated(Buf::BF_PutToSleep | Buf::BF_WakeCounter); + mBodyBufferFlags &= ~Buf::BF_WakeUp; + } +} + +PX_FORCE_INLINE void Body::putToSleep() +{ + PX_ASSERT(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + putToSleepInternal(); +} + +PX_INLINE void Body::setWakeCounter(PxReal w) +{ + PX_ASSERT(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + mBufferedWakeCounter = w; + + if(!isBuffering()) + { + if(getScbScene() && (w > 0.0f)) + mBufferedIsSleeping = 0; + + mBodyCore.setWakeCounter(w); + + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if(w > 0.0f) + wakeUpInternal(w); + else + markUpdated(Buf::BF_WakeCounter); + } +} + +PX_INLINE void Body::setFlags(PxRigidBodyFlags f) +{ + const PxU32 wasKinematic = getFlags() & PxRigidBodyFlag::eKINEMATIC; + const PxU32 isKinematic = f & PxRigidBodyFlag::eKINEMATIC; + const bool switchToKinematic = ((!wasKinematic) && isKinematic); + const bool switchToDynamic = (wasKinematic && (!isKinematic)); + + if(!isBuffering()) + { + if(switchToKinematic) + setBufferedParamsForAsleep(); + + mBodyCore.setFlags(getScbScene() ? getScbScene()->getScScene().getSimStateDataPool() : NULL, f); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if(switchToKinematic) + putToSleepInternal(); + else if(switchToDynamic) + mBodyBufferFlags &= ~Buf::BF_KinematicTarget; + + getBodyBuffer()->mRigidBodyFlags = f; + markUpdated(Buf::BF_RigidBodyFlags); + } +} + +PX_INLINE void Body::addSpatialAcceleration(const PxVec3* linAcc, const PxVec3* angAcc) +{ + if(!isBuffering()) + { + mBodyCore.addSpatialAcceleration(getScbScene()->getScScene().getSimStateDataPool(), linAcc, angAcc); + //Spatial acceleration isn't sent to PVD. + } + else + { + Buf* b = getBodyBuffer(); + accumulate(b->mLinAcceleration, b->mAngAcceleration, Buf::BF_AccelerationLinear, Buf::BF_AccelerationAngular, linAcc, angAcc); + } +} + +PX_INLINE void Body::setSpatialAcceleration(const PxVec3* linAcc, const PxVec3* angAcc) +{ + if (!isBuffering()) + { + mBodyCore.setSpatialAcceleration(getScbScene()->getScScene().getSimStateDataPool(), linAcc, angAcc); + //Spatial acceleration isn't sent to PVD. + } + else + { + Buf* b = getBodyBuffer(); + + PxU32 flag = 0; + if (linAcc) + { + b->mLinAcceleration = *linAcc; + flag |= Buf::BF_AccelerationLinear; + } + + if (angAcc) + { + b->mAngAcceleration += *angAcc; + flag |= Buf::BF_AccelerationAngular; + } + + markUpdated(flag); + } +} + +PX_INLINE void Body::clearSpatialAcceleration(bool force, bool torque) +{ + if(!isBuffering()) + { + mBodyCore.clearSpatialAcceleration(force, torque); + //Spatial acceleration isn't sent to PVD. + } + else + { + Buf* b = getBodyBuffer(); + resetAccumulator(b->mLinAcceleration, b->mAngAcceleration, Buf::BF_AccelerationLinear, Buf::BF_AccelerationAngular, Buf::BF_ClearAccelerationLinear, Buf::BF_ClearAccelerationAngular, force, torque); + } +} + +PX_INLINE void Body::addSpatialVelocity(const PxVec3* linVelDelta, const PxVec3* angVelDelta) +{ + if(!isBuffering()) + { + mBodyCore.addSpatialVelocity(getScbScene()->getScScene().getSimStateDataPool(), linVelDelta, angVelDelta); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* b = getBodyBuffer(); + accumulate(b->mLinDeltaVelocity, b->mAngDeltaVelocity, Buf::BF_DeltaVelocityLinear, Buf::BF_DeltaVelocityAngular, linVelDelta, angVelDelta); + } +} + +PX_INLINE void Body::clearSpatialVelocity(bool force, bool torque) +{ + if(!isBuffering()) + { + mBodyCore.clearSpatialVelocity(force, torque); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* b = getBodyBuffer(); + resetAccumulator(b->mLinDeltaVelocity, b->mAngDeltaVelocity, Buf::BF_DeltaVelocityLinear, Buf::BF_DeltaVelocityAngular, Buf::BF_ClearDeltaVelocityLinear, PxU32(Buf::BF_ClearDeltaVelocityAngular), force, torque); + } +} + +PX_INLINE bool Body::getKinematicTarget(PxTransform& p) const +{ + if(isBuffered(Buf::BF_KinematicTarget)) + { + p = getBodyBuffer()->mKinematicTarget; + return true; + } + else if(getControlState() != ControlState::eREMOVE_PENDING) + return mBodyCore.getKinematicTarget(p); + else + return false; +} + +PX_INLINE void Body::setKinematicTarget(const PxTransform& p) +{ + Scene* scene = getScbScene(); + PX_ASSERT(scene); // only allowed for an object in a scene + PxReal wakeCounterResetValue = scene->getWakeCounterResetValue(); + + if(!isBuffering()) + { + mBodyCore.setKinematicTarget(scene->getScScene().getSimStateDataPool(), p, wakeCounterResetValue); + setBufferedParamsForAwake(wakeCounterResetValue); + + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + //Kinematics can now have buffered delta velocities... + //PX_ASSERT((mBodyBufferFlags & (Buf::BF_DeltaVelocity|Buf::BF_Acceleration)) == 0); // switching to kinematic should do that. + getBodyBuffer()->mKinematicTarget = p; + markUpdated(Buf::BF_KinematicTarget); + + wakeUpInternal(wakeCounterResetValue); + } +#if PX_SUPPORT_PVD + if(getControlState() == ControlState::eIN_SCENE) + scene->getScenePvdClient().updateKinematicTarget(this, p); +#endif +} + +PX_FORCE_INLINE void Body::onOriginShift(const PxVec3& shift) +{ + mBufferedBody2World.p -= shift; + mBodyCore.onOriginShift(shift); +} + +//-------------------------------------------------------------- +// +// Miscellaneous +// +//-------------------------------------------------------------- + +PX_FORCE_INLINE bool Body::hasKinematicTarget() const +{ + return (isBuffered(BodyBuffer::BF_KinematicTarget) || mBodyCore.getHasValidKinematicTarget()); +} + +PX_FORCE_INLINE void Body::clearSimStateDataForPendingInsert() +{ + if(insertPending()) + { + // not-so-nice-code to cover the following cases: + // - user adds a kinematic to the scene, sets a target and removes the kinematic from scene again (all while the sim is running) + // - same as above but instead of removing the kinematic it gets switched to dynamic + // - user adds a dynamic to the scene, sets a target and removes the dynamic from scene again (all while the sim is running) + + Sc::BodyCore& core = mBodyCore; + if(core.getSimStateData(true)) + core.tearDownSimStateData(getScbScene()->getScScene().getSimStateDataPool(), true); + else if(core.getSimStateData(false)) + core.tearDownSimStateData(getScbScene()->getScScene().getSimStateDataPool(), false); + } +} + +PX_FORCE_INLINE void Body::transitionSimStateDataForPendingInsert() +{ + if(insertPending()) + { + // not-so-nice-code to cover the following case: + // - user adds a dynamic, adds force, then switches to kinematic (all while the sim is running) + + Sc::BodyCore& core = mBodyCore; + if(core.getSimStateData(false)) + core.setupSimStateData(getScbScene()->getScScene().getSimStateDataPool(), true); // note: this won't allocate the memory twice + } +} + +PX_INLINE PxMat33 Body::getGlobalInertiaTensorInverse() const +{ + PxMat33 inverseInertiaWorldSpace; + Cm::transformInertiaTensor(getInverseInertia(), Gu::PxMat33Padded(getBody2World().q), inverseInertiaWorldSpace); + return inverseInertiaWorldSpace; +} + +PX_FORCE_INLINE bool Body::checkSleepReadinessBesidesWakeCounter() +{ + return (getLinearVelocity().isZero() && getAngularVelocity().isZero()); + // no need to test for pending force updates yet since currently this is not supported on scene insertion +} + +PX_FORCE_INLINE void Body::initBufferedState() +{ + PX_ASSERT(mBufferedIsSleeping); // this method is only meant to get called when an object is added to the scene + + if((getWakeCounter() == 0.0f) && checkSleepReadinessBesidesWakeCounter()) + mBufferedIsSleeping = 1; + else + mBufferedIsSleeping = 0; +} + +PX_FORCE_INLINE void Body::clearBufferedState() +{ + if(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)) + { + mBufferedIsSleeping = 1; // the expected state when an object gets removed from the scene. + mBodyBufferFlags &= ~(Buf::BF_Acceleration | Buf::BF_DeltaVelocity); + } + else + { + // make sure the buffered properties for a kinematic that is not in a scene are set according to the specification + + // necessary to use the putToSleep method because buffered re-insertion case needs to be covered as well. Currently, re-insertion + // just clears the remove operation. This would prevent the core object parameters to get updated. Thus the operations need + // to be buffered and putToSleepInternal takes care of that. + putToSleepInternal(); + } + + RigidObject::clearBufferedState(); +} + +PX_FORCE_INLINE void Body::clearBufferedSleepStateChange() +{ + mBodyBufferFlags &= ~(Buf::BF_WakeUp | Buf::BF_PutToSleep); +} + +PX_FORCE_INLINE void Body::switchBodyToNoSim() +{ + Scb::Scene* scene = getScbScene(); + + switchToNoSim(true); + + if((!scene) || (!getScbScene()->isPhysicsBuffering())) + { + setBufferedParamsForAsleep(); + mBodyCore.putToSleep(); + } + else + putToSleepInternal(); + + if(scene) + clearSimStateDataForPendingInsert(); +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_FORCE_INLINE void Body::markUpdated(PxU32 flag) +{ + scheduleForUpdate(); + mBodyBufferFlags |= flag; +} + +PX_FORCE_INLINE Ps::IntBool Body::isBuffered(PxU32 flag) const +{ + return Ps::IntBool(mBodyBufferFlags & flag); +} + +PX_INLINE void Body::syncCollisionWriteThroughState() +{ + PxU32 bufferFlags = mBodyBufferFlags; + + //---- + if ((bufferFlags & Buf::BF_LinearVelocity) == 0) + mBufferedLinVelocity = mBodyCore.getLinearVelocity(); + else + { + PX_ASSERT((mBufferedIsSleeping && mBufferedLinVelocity.isZero()) || + (!mBufferedIsSleeping) || + (getControlState() == ControlState::eREMOVE_PENDING)); + PX_ASSERT(mBufferedLinVelocity.isZero() || + ((!mBufferedLinVelocity.isZero()) && (!mBufferedIsSleeping)) || + (getControlState() == ControlState::eREMOVE_PENDING)); + + mBodyCore.setLinearVelocity(mBufferedLinVelocity); + //clear the flag + bufferFlags &= ~Buf::BF_LinearVelocity; + } + + //---- + + if ((bufferFlags & Buf::BF_AngularVelocity) == 0) + mBufferedAngVelocity = mBodyCore.getAngularVelocity(); + else + { + PX_ASSERT((mBufferedIsSleeping && mBufferedAngVelocity.isZero()) || + (!mBufferedIsSleeping) || + (getControlState() == ControlState::eREMOVE_PENDING)); + PX_ASSERT(mBufferedAngVelocity.isZero() || + ((!mBufferedAngVelocity.isZero()) && (!mBufferedIsSleeping)) || + (getControlState() == ControlState::eREMOVE_PENDING)); + + mBodyCore.setAngularVelocity(mBufferedAngVelocity); + //clear the flag + bufferFlags &= ~Buf::BF_AngularVelocity; + } + + //---- + + if(bufferFlags & Buf::BF_KinematicTarget) + { + //don't apply kinematic target unless the actor is kinematic already. setKinematicTarget is write-through properties for split sim but setRigidBodyFlag transition from rigid body to kinematic isn't write-through + if(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC) + { + Buf& buffer = *getBodyBuffer(); + PX_ASSERT(mBufferedWakeCounter > 0.0f); // that is the expected behavior + + mBodyCore.setKinematicTarget(getScbScene()->getScScene().getSimStateDataPool(), buffer.mKinematicTarget, mBufferedWakeCounter); + //clear the flag + bufferFlags &= ~Buf::BF_KinematicTarget; + } + } + + //---- + //in case user call addForce(), collide(), clearForce(), which we need to clear the acclearation in the low-level + if(bufferFlags & Buf::BF_ClearAcceleration) + { + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping); + mBodyCore.clearSpatialAcceleration((bufferFlags & Buf::BF_ClearAccelerationLinear)!=0, (bufferFlags & Buf::BF_ClearAccelerationAngular)!=0); + + //clear the flag, we don't clear the buffered acceleration, because the user might call addForce() again after calling clearForce() + bufferFlags &= ~Buf::BF_ClearAcceleration; + } + + //---- + + //apply addForce/clearForce, addTorque/clearTorque + if(bufferFlags & Buf::BF_Acceleration) + { + Buf& buffer = *getBodyBuffer(); + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping || (buffer.mLinAcceleration.isZero() && buffer.mAngAcceleration.isZero())); + mBodyCore.addSpatialAcceleration(getScbScene()->getScScene().getSimStateDataPool(), &buffer.mLinAcceleration, &buffer.mAngAcceleration); + + //clear the flag + bufferFlags &= ~Buf::BF_Acceleration; + buffer.mLinAcceleration = PxVec3(0.0f); + buffer.mAngAcceleration = PxVec3(0.0f); + } + + //---- + + if(bufferFlags & Buf::BF_ClearDeltaVelocity) + { + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping ); + mBodyCore.clearSpatialVelocity((bufferFlags & Buf::BF_ClearDeltaVelocityLinear)!=0, (bufferFlags & Buf::BF_ClearDeltaVelocityAngular)!=0); + + //clear the flag, we don't clear the buffered velocity, because the user might call addForce() again after calling clearForce() + bufferFlags &= ~Buf::BF_ClearDeltaVelocity; + } + + //---- + + if(bufferFlags & Buf::BF_DeltaVelocity) + { + Buf& buffer = *getBodyBuffer(); + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping || (buffer.mLinDeltaVelocity.isZero() && buffer.mAngDeltaVelocity.isZero())); + mBodyCore.addSpatialVelocity(getScbScene()->getScScene().getSimStateDataPool(), &buffer.mLinDeltaVelocity, &buffer.mAngDeltaVelocity); + + //clear the flag + bufferFlags &= ~Buf::BF_DeltaVelocity; + buffer.mLinDeltaVelocity = PxVec3(0.0f); + buffer.mAngDeltaVelocity = PxVec3(0.0f); + } + + //---- + + if((bufferFlags & Buf::BF_WakeCounter) == 0) + mBufferedWakeCounter = mBodyCore.getWakeCounter(); + else if(!(bufferFlags & (Buf::BF_WakeUp | Buf::BF_PutToSleep))) // if there has been at least one buffered sleep state transition, then there is no use in adjusting the wake counter separately because it will + // get done in the sleep state update. + { + PX_ASSERT((getControlState() == ControlState::eREMOVE_PENDING) || (mBufferedWakeCounter == 0.0f)); // a wake counter change is always connected to a sleep state change, except if setWakeCounter(0.0f) was called or an object gets removed from the scene after it was woken up. + + mBodyCore.setWakeCounter(mBufferedWakeCounter); + + bufferFlags &= ~Buf::BF_WakeCounter; + } + else if(bufferFlags & Buf::BF_WakeUp) + { + Buf& buffer = *getBodyBuffer(); + //Because in the split sim, transition from rigid body to kinematic isn't a write through properties. However, when the user call setKinematicTarget, the SDK wake up the actor so we want to avoid waking up the + //actor if the actor is transitioning from rigid body to kinematic or vice versa. + PxRigidBodyFlags changeFlags= mBodyCore.getFlags() ^ buffer.mRigidBodyFlags; + if(!((bufferFlags & Buf::BF_RigidBodyFlags) && (changeFlags & PxRigidBodyFlag::eKINEMATIC))) + { + PX_ASSERT(bufferFlags & Buf::BF_WakeCounter); // sleep state transitions always mark the wake counter dirty + PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing an object should clear pending wakeUp/putToSleep operations since the state for a free standing object gets set according to specification. + + // The sleep state ends up with the proper result that reflects the order of the original buffered operations because... + // - every operation that affects the sleep state makes a buffered call to wakeUp/putToSleep, hence, the buffered sleep transition + // will always reflect the latest change + // - a user triggered sleep state transition (wakeUp/putToSleep) always adjusts the affected properties (velocity, force, wake counter...) through separate + // buffered calls, hence, those properties will get adjusted to the correct values in the end + // - sleep state sync runs after all calls that have side effects on the sleep state. + // + PX_ASSERT(!mBufferedIsSleeping); + PX_ASSERT(bufferFlags & Buf::BF_WakeUp); + + // can not assert for any values here since it is possible, for example, to end up waking something up with a 0 wakeCounter here (as a consequence of a buffered wakeUp() followed by a setWakeCounter(0)) + + mBodyCore.wakeUp(mBufferedWakeCounter); + + bufferFlags &= ~(Buf::BF_WakeUp | Buf::BF_WakeCounter); + } + } + + //---- + + mBodyBufferFlags = bufferFlags; +} + +PX_INLINE void Body::syncState() +{ + // if the body was removed from the scene, we expect... + // ...it to be marked as sleeping (that is the specification) + // ...no pending sleep state change (because wakeUp/putTosleep is illegal for a free standing object and we clear such operations if pending). + // Note: a sleep state change might have happened before the removal but the corresponding wake counter change is then covered through the BF_WakeCounter dirty flag. + PX_ASSERT( (getControlState() != ControlState::eREMOVE_PENDING) || + (mBufferedIsSleeping && (!isBuffered(Buf::BF_WakeUp | Buf::BF_PutToSleep))) ); + + // + // IMPORTANT: Since we ran out of space for buffered property flags, the Scb::Body property related flags are stored in mBodyBufferFlags. + // To get the buffer flags from the base classes, use getBufferFlags() + // + const PxU32 bufferFlags = mBodyBufferFlags; + const PxU32 baseBufferFlags = getBufferFlags(); + + if((bufferFlags & Buf::BF_Body2World) == 0) + mBufferedBody2World = mBodyCore.getBody2World(); + else if((bufferFlags & Buf::BF_Body2World_CoM) == 0) + mBodyCore.setBody2World(mBufferedBody2World); + else + { + // IMPORTANT: Do this before adjusting body2Actor +#ifdef USE_NEW_SYSTEM + PX_ASSERT(bufferFlags & BF_Body2Actor); +#else + PX_ASSERT(bufferFlags & Buf::BF_Body2Actor); +#endif + Buf& buffer = *getBodyBuffer(); + const PxTransform newBody2oldBody = mBodyCore.getBody2Actor().transformInv(buffer.mBody2Actor); + + PxTransform b2w = mBodyCore.getBody2World(); + b2w = b2w.transform(newBody2oldBody); // translate simulation result from old CoM to new CoM + + mBufferedBody2World = b2w; + mBodyCore.setBody2World(b2w); + } + + //---- + +#ifdef USE_NEW_SYSTEM + if(baseBufferFlags & BF_ActorFlags) +#else + if(baseBufferFlags & Buf::BF_ActorFlags) +#endif + syncNoSimSwitch(*getBodyBuffer(), mBodyCore, true); + + //---- + + if(bufferFlags & ~( Buf::BF_WakeCounter|Buf::BF_Body2World|Buf::BF_LinearVelocity|Buf::BF_AngularVelocity + |Buf::BF_WakeUp|Buf::BF_PutToSleep)) // Optimization to avoid all the if-statements below if possible + { + const Buf& buffer = *getBodyBuffer(); +#ifdef USE_NEW_SYSTEM + syncInverseMass(); + syncInverseInertia(); + syncLinearDamping(); + syncAngularDamping(); + syncMaxAngVelSq(); + syncMaxLinVelSq(); + syncSleepThreshold(); + syncSolverIterationCounts(); + syncContactReportThreshold(); + syncBody2Actor(); + syncFreezeThreshold(); + syncMaxPenetrationBias(); + syncMaxContactImpulse(); + syncCCDAdvanceCoefficient(); +#else + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); +#endif + if(bufferFlags & Buf::BF_RigidBodyFlags) + mBodyCore.setFlags(getScbScene()->getScScene().getSimStateDataPool(), buffer.mRigidBodyFlags); + } + + //This method sync all the write through properties in collision and is called in fetchCollision() + syncCollisionWriteThroughState(); + + //---- + + if((bufferFlags & (Buf::BF_PutToSleep)) == 0) + { + const bool isSimObjectSleeping = mBodyCore.isSleeping(); + if(getControlState() != ControlState::eREMOVE_PENDING) // we do not want to sync the simulation sleep state if the object was removed (free standing objects have buffered state sleeping) + mBufferedIsSleeping = PxU32(isSimObjectSleeping); + else + PX_ASSERT(mBufferedIsSleeping); // this must get set immediately at remove + } + else + { + PX_ASSERT(bufferFlags & Buf::BF_WakeCounter); // sleep state transitions always mark the wake counter dirty + PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing an object should clear pending wakeUp/putToSleep operations since the state for a free standing object gets set according to specification. + + // The sleep state ends up with the proper result that reflects the order of the original buffered operations because... + // - every operation that affects the sleep state makes a buffered call to wakeUp/putToSleep, hence, the buffered sleep transition + // will always reflect the latest change + // - a user triggered sleep state transition (wakeUp/putToSleep) always adjusts the affected properties (velocity, force, wake counter...) through separate + // buffered calls, hence, those properties will get adjusted to the correct values in the end + // - sleep state sync runs after all calls that have side effects on the sleep state. + // + + PX_ASSERT(mBufferedIsSleeping); + PX_ASSERT(!(bufferFlags & Buf::BF_WakeUp)); + PX_ASSERT(mBufferedWakeCounter == 0.0f); + PX_ASSERT(mBufferedLinVelocity.isZero()); + PX_ASSERT(mBufferedAngVelocity.isZero()); + PX_ASSERT(!(bufferFlags & Buf::BF_Acceleration)); + PX_ASSERT(!(bufferFlags & Buf::BF_DeltaVelocity)); + + mBodyCore.putToSleep(); + } + + // PT: we must call this even when there's no buffered data + RigidObject::syncState(); + + // -------------- + // postconditions + // + PX_ASSERT((getControlState() != ControlState::eREMOVE_PENDING) || mBufferedIsSleeping); // nothing in this method should change this +#ifdef _DEBUG + // make sure that for a removed kinematic, the buffered params hold the values as defined in our specification + if((mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC) && (getControlState() == ControlState::eREMOVE_PENDING)) + { + PX_ASSERT(mBufferedLinVelocity.isZero()); + PX_ASSERT(mBufferedAngVelocity.isZero()); + PX_ASSERT(mBufferedWakeCounter == 0.0f); + } +#endif + // + // postconditions + // -------------- + + postSyncState(); + mBodyBufferFlags = 0; +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h b/src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h new file mode 100644 index 000000000..129f68f8b --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h @@ -0,0 +1,313 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_CONSTRAINTSHADER +#define PX_PHYSICS_SCB_CONSTRAINTSHADER + +#include "CmPhysXCommon.h" +#include "../../../simulationcontroller/include/ScConstraintCore.h" + +#include "ScbBody.h" + +namespace physx +{ + +namespace Sc +{ + class RigidCore; +} + +namespace Scb +{ + +struct ConstraintBuffer +{ +public: + Sc::RigidCore* rigids[2]; + PxReal linBreakForce; + PxReal angBreakForce; + PxConstraintFlags flags; + PxReal minResponseThreshold; +}; + +enum ConstraintBufferFlag +{ + BF_BODIES = (1 << 0), + BF_BREAK_IMPULSE = (1 << 1), + BF_FLAGS = (1 << 2), + BF_MIN_RESPONSE_THRESHOLD = (1 << 3) +}; + +class Constraint : public Base, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + +public: + + typedef ConstraintBuffer Buf; + typedef Sc::ConstraintCore Core; + +// PX_SERIALIZATION + Constraint(const PxEMPTY) : Base(PxEmpty), mConstraint(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + PX_INLINE Constraint(PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize); + PX_INLINE ~Constraint() {} + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::ConstraintCore interface + //--------------------------------------------------------------------------------- + + PX_INLINE PxConstraint* getPxConstraint() const; + PX_INLINE PxConstraintConnector* getPxConnector() const; + + PX_INLINE void setFlags(PxConstraintFlags f); + PX_INLINE PxConstraintFlags getFlags() const; + + PX_INLINE void setBodies(Scb::RigidObject* r0, Scb::RigidObject* r1); + + PX_INLINE void getForce(PxVec3& force, PxVec3& torque) const; + + PX_INLINE void setBreakForce(PxReal linear, PxReal angular); + PX_INLINE void getBreakForce(PxReal& linear, PxReal& angular) const; + + PX_INLINE void setMinResponseThreshold(PxReal threshold); + PX_INLINE PxReal getMinResponseThreshold() const; + + PX_INLINE bool updateConstants(void* addr); + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void prepareForActorRemoval(); + PX_INLINE void syncState(); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE const Core& getScConstraint() const { return mConstraint; } // Only use if you know what you're doing! + PX_FORCE_INLINE Core& getScConstraint() { return mConstraint; } // Only use if you know what you're doing! + + PX_FORCE_INLINE static Constraint& fromSc(Core &a) { return *reinterpret_cast(reinterpret_cast(&a)-getScOffset()); } + PX_FORCE_INLINE static const Constraint& fromSc(const Core &a) { return *reinterpret_cast(reinterpret_cast(&a)-getScOffset()); } + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mConstraint); } + +private: + Core mConstraint; + + //--------------------------------------------------------------------------------- + // Permanently buffered data (simulation written data) + //--------------------------------------------------------------------------------- + PxVec3 mBufferedForce; + PxVec3 mBufferedTorque; + PxConstraintFlags mBrokenFlag; + + PX_FORCE_INLINE const Buf* getBufferedData() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Buf* getBufferedData() { return reinterpret_cast(getStream()); } +}; + +} // namespace Scb + +PX_INLINE Scb::Constraint::Constraint(PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) : + mConstraint (connector, shaders, dataSize), + mBufferedForce (0.0f), + mBufferedTorque (0.0f), + mBrokenFlag (0) +{ + setScbType(ScbType::eCONSTRAINT); +} + +PX_INLINE PxConstraintConnector* Scb::Constraint::getPxConnector() const +{ + return mConstraint.getPxConnector(); +} + +PX_INLINE void Scb::Constraint::setFlags(PxConstraintFlags f) +{ + if(!isBuffering()) + { + mConstraint.setFlags(f); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + getBufferedData()->flags = f; + markUpdated(BF_FLAGS); + } +} + +PX_INLINE PxConstraintFlags Scb::Constraint::getFlags() const +{ + return isBuffered(BF_FLAGS) ? getBufferedData()->flags & (~(PxConstraintFlag::eBROKEN | PxConstraintFlag::eGPU_COMPATIBLE) | mBrokenFlag) + : mConstraint.getFlags() & (~(PxConstraintFlag::eBROKEN | PxConstraintFlag::eGPU_COMPATIBLE) | mBrokenFlag); +} + +PX_INLINE void Scb::Constraint::setBodies(Scb::RigidObject* r0, Scb::RigidObject* r1) +{ + Sc::RigidCore* scR0 = r0 ? &r0->getScRigidCore() : NULL; + Sc::RigidCore* scR1 = r1 ? &r1->getScRigidCore() : NULL; + + if(!isBuffering()) + { + mConstraint.prepareForSetBodies(); + mConstraint.setBodies(scR0, scR1); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* PX_RESTRICT bufferedData = getBufferedData(); + bufferedData->rigids[0] = scR0; + bufferedData->rigids[1] = scR1; + markUpdated(BF_BODIES); + } + + mBufferedForce = PxVec3(0.0f); + mBufferedTorque = PxVec3(0.0f); +} + +PX_INLINE void Scb::Constraint::getForce(PxVec3& force, PxVec3& torque) const +{ + force = mBufferedForce; + torque = mBufferedTorque; +} + +PX_INLINE void Scb::Constraint::setBreakForce(PxReal linear, PxReal angular) +{ + if(!isBuffering()) + { + mConstraint.setBreakForce(linear, angular); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* PX_RESTRICT bufferedData = getBufferedData(); + bufferedData->linBreakForce = linear; + bufferedData->angBreakForce = angular; + markUpdated(BF_BREAK_IMPULSE); + } +} + +PX_INLINE void Scb::Constraint::getBreakForce(PxReal& linear, PxReal& angular) const +{ + if(isBuffered(BF_BREAK_IMPULSE)) + { + const Buf* PX_RESTRICT bufferedData = getBufferedData(); + linear = bufferedData->linBreakForce; + angular = bufferedData->angBreakForce; + } + else + mConstraint.getBreakForce(linear, angular); +} + +PX_INLINE void Scb::Constraint::setMinResponseThreshold(PxReal threshold) +{ + if(!isBuffering()) + { + mConstraint.setMinResponseThreshold(threshold); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* PX_RESTRICT bufferedData = getBufferedData(); + bufferedData->minResponseThreshold = threshold; + markUpdated(BF_MIN_RESPONSE_THRESHOLD); + } +} + +PX_INLINE PxReal Scb::Constraint::getMinResponseThreshold() const +{ + if(isBuffered(BF_MIN_RESPONSE_THRESHOLD)) + { + const Buf* PX_RESTRICT bufferedData = getBufferedData(); + return bufferedData->minResponseThreshold; + } + else + return mConstraint.getMinResponseThreshold(); +} + +PX_INLINE bool Scb::Constraint::updateConstants(void* addr) +{ + PX_ASSERT(!getScbScene()->isPhysicsBuffering()); + + return mConstraint.updateConstants(addr); +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_INLINE void Scb::Constraint::prepareForActorRemoval() +{ + // when the bodies of a constraint have been changed during buffering, it's possible the + // attached actor is going to get deleted. Sc expects that all interactions with that actor + // will have been removed, so we give the Sc::Constraint a chance to ensure that before + // the actors go away. + if(getBufferFlags() & BF_BODIES) + mConstraint.prepareForSetBodies(); +} + +PX_INLINE void Scb::Constraint::syncState() +{ + //!!! Force has to be synced every frame (might want to have a list of active constraint shaders?) + mConstraint.getForce(mBufferedForce, mBufferedTorque); + + mBrokenFlag = mConstraint.getFlags() & PxConstraintFlag::eBROKEN; + + const PxU32 flags = getBufferFlags(); + if(flags) + { + const Buf* PX_RESTRICT bufferedData = getBufferedData(); + + if(flags & BF_BODIES) + mConstraint.setBodies(bufferedData->rigids[0], bufferedData->rigids[1]); + + if(flags & BF_BREAK_IMPULSE) + mConstraint.setBreakForce(bufferedData->linBreakForce, bufferedData->angBreakForce); + + if(flags & BF_MIN_RESPONSE_THRESHOLD) + mConstraint.setMinResponseThreshold(bufferedData->minResponseThreshold); + + if(flags & BF_FLAGS) + mConstraint.setFlags(bufferedData->flags | mBrokenFlag); + } + + postSyncState(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbDefs.h b/src/PhysX/physx/source/physx/src/buffering/ScbDefs.h new file mode 100644 index 000000000..d8c8c1111 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbDefs.h @@ -0,0 +1,210 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_DEFS +#define PX_PHYSICS_SCB_DEFS + +#include "ScbBase.h" + +//#define USE_NEW_SYSTEM + +#ifdef USE_NEW_SYSTEM +namespace physx +{ +namespace Scb +{ + template + void setValueT(ValueType value, CoreType& core, SCBType& scb) + { + if(!scb.isBuffering()) + { + AccessType::setCore(core, value); +#if PX_SUPPORT_PVD + if(scb.getControlState() == ControlState::eIN_SCENE) + { + Scb::Scene* scene = scb.getScbScene(); + PX_ASSERT(scene); + scene->getScenePvdClient().updatePvdProperties(&scb); + } +#endif + } + else + { + AccessType::setBuffered(scb, value); + scb.markUpdated(1< + ValueType getValueT(const CoreType& core, const SCBType& scb) + { + if(!scb.isBuffered(1< + void syncT(CoreType& core, SCBType& scb) + { + if(scb.isBuffered(1<(getStream())->m##_name = v; } \ + PX_INLINE _type getBuffered##_name() const { return reinterpret_cast(getStream())->m##_name; } \ + struct Fns_##_name \ + { \ + static PX_INLINE void setCore(Core& core, _type v) { core.set##_name(v); } \ + static PX_INLINE void setBuffered(_scb& scb, _type v) { scb.setBuffered##_name(v); } \ + static PX_INLINE _type getCore(const Core& core) { return core.get##_name(); } \ + static PX_INLINE _type getBuffered(const _scb& scb) { return scb.getBuffered##_name(); } \ + }; \ + PX_INLINE void set##_name(_type v) { setValueT<_type, Core, _scb, Fns_##_name, _id>(v, _getCore, *this); } \ + PX_INLINE _type get##_name() const { return getValueT<_type, Core, _scb, Fns_##_name, _id>(_getCore, *this); } \ + PX_INLINE void sync##_name() { syncT(_getCore, *this); } +} +} +#else + +// a Regular attribute of type T is one for which +// * the SC method takes a single argument of type ArgType (defined below) +// * Scb either passes that argument through, or dumps it in a buffer to flush later. +// * PVD is notified when the variable changes +// +// For each such, we can define static methods to read and write the core and buffered variables, +// and capture the buffering logic in the BufferedAccess class. +// +// The dummy arg is necessary here because ISO permits partial specialization of member templates +// but not full specialization. +// +// putting just accessors and mutators here allows us to change the behavior just by varying the +// BufferAccess template (e.g. to compile without buffering), and also to size-reduce that template +// by passing function pointers if necessary + +#define SCB_REGULAR_ATTRIBUTE(_val, _type, _name) \ +enum { BF_##_name = 1<<(_val) }; \ +_type m##_name; \ +template struct Fns<1<<(_val),Dummy> \ +{ \ + typedef typename ArgType<_type>::Type Arg; \ + enum { flag = 1<<(_val) }; \ + static PX_FORCE_INLINE Arg getBuffered(const Buf& buf) { return Arg(buf.m##_name);} \ + static PX_FORCE_INLINE void setBuffered(Buf& buf, Arg v) { buf.m##_name = v;} \ + static PX_FORCE_INLINE Arg getCore(const Core& core) { return Arg(core.get##_name());} \ + static PX_FORCE_INLINE void setCore(Core& core, Arg v) { core.set##_name(v);} \ +}; + +#define SCB_REGULAR_ATTRIBUTE_ALIGNED(_val, _type, _name, _alignment) \ +enum { BF_##_name = 1<<(_val) }; \ +PX_ALIGN(_alignment, _type) m##_name; \ +template struct Fns<1<<(_val),Dummy> \ +{ \ + typedef typename ArgType<_type>::Type Arg; \ + enum { flag = 1<<(_val) }; \ + static PX_FORCE_INLINE Arg getBuffered(const Buf& buf) { return buf.m##_name;} \ + static PX_FORCE_INLINE void setBuffered(Buf& buf, Arg v) { buf.m##_name = v;} \ + static PX_FORCE_INLINE Arg getCore(const Core& core) { return core.get##_name();} \ + static PX_FORCE_INLINE void setCore(Core& core, Arg v) { core.set##_name(v);} \ +}; + +namespace physx +{ +namespace Scb +{ +class Scene; + +template struct ArgType { typedef T Type; }; +template<> struct ArgType { typedef const PxVec3& Type; }; +template<> struct ArgType { typedef const PxTransform& Type; }; +template<> struct ArgType { typedef const PxQuat& Type; }; +template<> struct ArgType { typedef const PxPlane& Type; }; +template<> struct ArgType { typedef const PxFilterData& Type; }; + +// TODO: should be able to size-reduce this if necessary by just generating one set per +// arg type instead of one per arg, by passing function pointers to the accessors/mutators/flag +// instead of instancing per type. + +template // BaseClass: introduced to have Scb::Body use custom location for storing buffered property flags +struct BufferedAccess +{ + template + static PX_FORCE_INLINE typename Fns::Arg read(const BaseClass& base, const Core& core) + { + /*return base.isBuffered(Fns::flag) ? Fns::getBuffered(*reinterpret_cast(base.getStream())) + : Fns::getCore(core);*/ + + if (base.isBuffered(Fns::flag)) + { + return Fns::getBuffered(*reinterpret_cast(base.getStream())); + } + return Fns::getCore(core); + + } + + template + static PX_FORCE_INLINE void write(BaseClass& base, Core& core, typename Fns::Arg v) + { + if(!base.isBuffering()) + { + Fns::setCore(core, v); +#if PX_SUPPORT_PVD + if(base.getControlState() == ControlState::eIN_SCENE) + { + Scb::Scene* scene = base.getScbScene(); + PX_ASSERT(scene); + scene->getScenePvdClient().updatePvdProperties(static_cast(&base)); + } +#endif + } + else + { + Fns::setBuffered(*reinterpret_cast(base.getStream()), v); + base.markUpdated(Fns::flag); + } + } + + template + static PX_FORCE_INLINE void flush(const BaseClass& base, Core& core, const Buf& buf) + { + if(base.isBuffered(Fns::flag)) + Fns::setCore(core, Fns::getBuffered(buf)); + } +}; + +} +} +#endif + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp new file mode 100644 index 000000000..fe498ee04 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp @@ -0,0 +1,169 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "ScbShape.h" +#include "ScbBody.h" +#include "ScbRigidStatic.h" +#include "ScbConstraint.h" +#include "ScbArticulation.h" +#include "ScbArticulationJoint.h" +#include "ScbAggregate.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Base::getBinaryMetaData(PxOutputStream& stream) +{ + // 28 => 12 bytes + PX_DEF_BIN_METADATA_TYPEDEF(stream, ScbType::Enum, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Base) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Base, Scb::Scene, mScene, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Base, PxU32, mControlState, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Base, PxU8*, mStreamPtr, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Shape::getBinaryMetaData(PxOutputStream& stream) +{ + // 176 => 160 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Shape) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Shape, Scb::Base) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Shape, ShapeCore, mShape, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Actor::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Actor) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Actor, Scb::Base) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::RigidObject::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::RigidObject) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::RigidObject, Scb::Actor) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Body::getBinaryMetaData(PxOutputStream& stream) +{ + // 240 => 224 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Body) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Body, Scb::RigidObject) + +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxU32, mPaddingScbBody1, PxMetaDataFlag::ePADDING) +#endif + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, Sc::BodyCore, mBodyCore, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxTransform, mBufferedBody2World, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxVec3, mBufferedLinVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxVec3, mBufferedAngVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxReal, mBufferedWakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxU32, mBufferedIsSleeping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxU32, mBodyBufferFlags, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::RigidStatic::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::RigidStatic) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::RigidStatic, Scb::RigidObject) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::RigidStatic, Sc::StaticCore, mStatic, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Articulation::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Articulation) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Articulation, Scb::Base) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Articulation, ArticulationCore, mArticulation, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Articulation, PxReal, mBufferedWakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Articulation, PxU8, mBufferedIsSleeping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Articulation, PxArticulationFlags, mBufferedArticulationFlags, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::ArticulationJoint::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::ArticulationJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::ArticulationJoint, Scb::Base) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::ArticulationJoint, ArticulationJointCore, mJoint, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Constraint::getBinaryMetaData(PxOutputStream& stream) +{ + // 120 => 108 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Constraint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Constraint, Scb::Base) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Constraint, ConstraintCore, mConstraint, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Constraint, PxVec3, mBufferedForce, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Constraint, PxVec3, mBufferedTorque, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Constraint, PxConstraintFlags, mBrokenFlag, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Constraint, PxU16, mPaddingFromBrokenFlags, PxMetaDataFlag::ePADDING) +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Aggregate::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Aggregate) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Aggregate, Scb::Base) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Aggregate, PxAggregate,mPxAggregate, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Aggregate, PxU32, mAggregateID, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Aggregate, PxU32, mMaxNbActors, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Aggregate, bool, mSelfCollide, 0) + +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Scb::Aggregate, bool, mPaddingFromBool, PxMetaDataFlag::ePADDING) +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h b/src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h new file mode 100644 index 000000000..e0868a82d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCB_NPDEPS +#define PX_PHYSICS_SCB_NPDEPS + +namespace physx +{ + +// The Scb layer needs to delete the owning Np objects, but we don't want to include the Np headers +// necessary to find their addresses. So we use link-level dependencies instead. + +namespace Scb +{ + class Base; + class Shape; + class RigidObject; + class Constraint; + class Scene; + class ArticulationJoint; + class Articulation; + class RigidStatic; + class Body; +} + +namespace Sc +{ + class RigidCore; +} + +class PxScene; + +extern void NpDestroy(Scb::Base&); + +// we want to get the pointer to the rigid object that owns a shape, and the two actor pointers for a constraint, so that we don't +// duplicate the scene graph in Scb + +extern PxU32 NpRigidStaticGetShapes(Scb::RigidStatic& rigid, void* const *&shapes); +extern PxU32 NpRigidDynamicGetShapes(Scb::Body& body, void* const *&shapes, bool* isCompound = NULL); +extern size_t NpShapeGetScPtrOffset(); +extern void NpShapeIncRefCount(Scb::Shape& shape); +extern void NpShapeDecRefCount(Scb::Shape& shape); + +extern Sc::RigidCore* NpShapeGetScRigidObjectFromScbSLOW(const Scb::Shape &); +extern void NpConstraintGetRigidObjectsFromScb(const Scb::Constraint&, Scb::RigidObject*&, Scb::RigidObject*&); +extern void NpArticulationJointGetBodiesFromScb(Scb::ArticulationJoint&, Scb::Body*&, Scb::Body*&); +extern Scb::Body* NpArticulationGetRootFromScb(Scb::Articulation&); +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h b/src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h new file mode 100644 index 000000000..f4cc38003 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h @@ -0,0 +1,511 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_RIGID_OBJECT +#define PX_PHYSICS_SCB_RIGID_OBJECT + +#include "../../simulationcontroller/include/ScRigidCore.h" +#include "ScbScene.h" +#include "ScbActor.h" +#include "ScbShape.h" +#include "PsInlineArray.h" + +namespace physx +{ + +// base class for dynamic and static rigid objects, so that shapes can have something to refer to + +namespace Scb +{ +struct RemovedShape +{ + RemovedShape() : mShape(NULL), mWakeTouching(0) {} + RemovedShape(Scb::Shape* s, PxU8 wakeTouching) : mShape(s), mWakeTouching(wakeTouching) {} + + PX_FORCE_INLINE bool operator == (const RemovedShape& other) const + { + return (mShape == other.mShape); + } + + PX_FORCE_INLINE bool operator != (const RemovedShape& other) const + { + return (mShape != other.mShape); + } + + Scb::Shape* mShape; + PxU8 mWakeTouching; +}; + +struct RigidObjectBuffer : public ActorBuffer //once RigidObject has its own buffered elements, derive from that instead +{ + RigidObjectBuffer(): mResetFilterShape(0), mResetFilterShapeCount(0) {} + + // TODO(dsequeira): ideally we would use an allocator that allocates from the buffered memory stream + Ps::InlineArray mAddedShapes; + Ps::InlineArray mRemovedShapes; + union + { + PxU32 mResetFilterShapesIdx; + Scb::Shape* mResetFilterShape; + }; + PxU32 mResetFilterShapeCount; + + enum { BF_Base = ActorBuffer::AttrCount }; + + enum + { + BF_Shapes = 1<mRemovedShapes.size();i++) + { + RemovedShape& rs = b->mRemovedShapes[i]; + Shape& shape = *rs.mShape; + shape.setControlStateIfExclusive(NULL, Scb::ControlState::eNOT_IN_SCENE); + + Sc::RigidCore& rc = getScRigidCore(); + Scb::Scene* scene = getScbScene(); +#if PX_SUPPORT_PVD + scene->getScenePvdClient().releasePvdInstance(&shape, pxActor); +#endif + if(!isSimDisabledInternally()) + { + rc.removeShapeFromScene(shape.getScShape(), (rs.mWakeTouching != 0)); + + shape.checkUpdateOnRemove(scene); + + NpShapeDecRefCount(shape); + } + } + } + + // The array of removed shapes must be reset to avoid memory leaks. + b->mRemovedShapes.reset(); + } + } + + PX_INLINE void syncState() + { + const PxU32 bufferFlags = getBufferFlags(); + + if(bufferFlags & Buf::BF_ResetFiltering) + { + PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing the actor should have cleared BF_ResetFiltering + + Scb::Scene* scene = getScbScene(); + Sc::RigidCore& scCore = getScRigidCore(); + RigidObjectBuffer* b = getBuffer(); + Scb::Shape* const* shapes = (b->mResetFilterShapeCount == 1) ? &b->mResetFilterShape : scene->getShapeBuffer(b->mResetFilterShapesIdx); + for(PxU32 i=0; imResetFilterShapeCount; i++) + { + Sc::ShapeCore& scShape = shapes[i]->getScShape(); + + // do not process the call if the shape will not be a broadphase shape any longer + if(shapes[i]->getFlags() & (PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) + scCore.onShapeChange(scShape, Sc::ShapeChangeNotifyFlag::eRESET_FILTERING, PxShapeFlags()); + } + } + + if(bufferFlags & Buf::BF_Shapes) + { + RigidObjectBuffer* b = getBuffer(); + ControlState::Enum cs = getControlState(); +#if PX_SUPPORT_PVD + PxActor& pxActor = *getScRigidCore().getPxActor(); +#endif + for(PxU32 i=0;imAddedShapes.size();i++) + { + Shape& shape = *b->mAddedShapes[i]; + + // it can happen that a shape gets attached while the sim is running but then the actor is removed from the scene, + // so we need to distinguish those two cases + if(cs != ControlState::eREMOVE_PENDING) + { + shape.setControlStateIfExclusive(getScbScene(), Scb::ControlState::eIN_SCENE); + + if(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) // important to use the buffered flags since we want the new state. + { + getScRigidCore().addShapeToScene(shape.getScShape()); + NpShapeIncRefCount(shape); + } +#if PX_SUPPORT_PVD + getScbScene()->getScenePvdClient().createPvdInstance(&shape, pxActor); +#endif + } + else + shape.setControlStateIfExclusive(getScbScene(), Scb::ControlState::eNOT_IN_SCENE); + } + + // reset the arrays, because destructors don't run on buffers + b->mAddedShapes.reset(); + } + + Actor::syncState(); + } + + PX_FORCE_INLINE void scheduleForWakeTouching() + { + PX_ASSERT(getScbScene() && getScbScene()->isPhysicsBuffering()); + setBufferFlag(RigidObjectBuffer::BF_WakeTouching); + } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- +public: + PX_INLINE const Sc::RigidCore& getScRigidCore() const { return static_cast(getActorCore()); } // Only use if you know what you're doing! + PX_INLINE Sc::RigidCore& getScRigidCore() { return static_cast(getActorCore()); } // Only use if you know what you're doing! + + PX_INLINE void onShapeAttach(Scb::Shape& shape) + { + // there are two things to do here: add the shape to the sim (if unbuffered) or set it up for + // * if unbuffered, add the shape to the sim and PVD and increment its refcount, else set it up for buffered insertion, + // * if the shape is exclusive, set its Scb control state appropriately. + + ControlState::Enum cs = getControlState(); + if(cs==ControlState::eNOT_IN_SCENE) + return; + + Scene* scbScene = getScbScene(); + if(!scbScene->isPhysicsBuffering()) + { + if(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + { + NpShapeIncRefCount(shape); + getScRigidCore().addShapeToScene(shape.getScShape()); + } + +#if PX_SUPPORT_PVD + scbScene->getScenePvdClient().createPvdInstance(&shape, *getScRigidCore().getPxActor()); +#endif + shape.setControlStateIfExclusive(scbScene, ControlState::eIN_SCENE); + return; + } + else if(cs == ControlState::eINSERT_PENDING) + { + shape.setControlStateIfExclusive(scbScene, ControlState::eINSERT_PENDING); + return; + } + + RigidObjectBuffer* b = getBuffer(); + if(!b->mRemovedShapes.findAndReplaceWithLast(RemovedShape(&shape, 0))) + b->mAddedShapes.pushBack(&shape); + markUpdated(Buf::BF_Shapes); + + shape.setControlStateIfExclusive(scbScene, ControlState::eINSERT_PENDING); + } + + PX_INLINE void onShapeDetach(Scb::Shape& shape, bool wakeOnLostTouch, bool toBeReleased) + { + // see comments in onShapeAttach + ControlState::Enum cs = getControlState(); + if(cs==ControlState::eNOT_IN_SCENE) + return; + + Scene* scbScene = getScbScene(); + if(!scbScene->isPhysicsBuffering()) + { +#if PX_SUPPORT_PVD + scbScene->getScenePvdClient().releasePvdInstance(&shape, *getScRigidCore().getPxActor()); +#endif + if(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + { + getScRigidCore().removeShapeFromScene(shape.getScShape(), wakeOnLostTouch); + NpShapeDecRefCount(shape); + } + + shape.setControlStateIfExclusive(NULL, ControlState::eNOT_IN_SCENE); + return; + } + else if(cs == ControlState::eINSERT_PENDING) + { + shape.setControlStateIfExclusive(NULL, ControlState::eNOT_IN_SCENE); + return; + } + + RigidObjectBuffer* b = getBuffer(); + + // remove from the resetFiltering list + const PxU32 bufferFlags = getBufferFlags(); + if(bufferFlags & Buf::BF_ResetFiltering) + { + if(b->mResetFilterShapeCount == 1) + { + if(b->mResetFilterShape == &shape) + { + b->mResetFilterShapeCount = 0; + b->mResetFilterShape = 0; + resetBufferFlag(Buf::BF_ResetFiltering); + } + } + else + { + Scb::Shape** shapes = scbScene->getShapeBuffer(b->mResetFilterShapesIdx); + PxU32 idx = 0; + PxU32 lastIdx = b->mResetFilterShapeCount; + for(PxU32 k=0; k < b->mResetFilterShapeCount; k++) // need to iterate over whole list, same shape can be in there multiple times + { + if(shapes[idx] != &shape) + idx++; + else + { + lastIdx--; + shapes[idx] = shapes[lastIdx]; + } + } + b->mResetFilterShapeCount = idx; + if(idx == 0) + { + b->mResetFilterShape = 0; + resetBufferFlag(Buf::BF_ResetFiltering); + } + else if(idx == 1) + b->mResetFilterShape = shapes[0]; + } + } + + if(b->mAddedShapes.findAndReplaceWithLast(&shape)) + shape.setControlStateIfExclusive(scbScene, ControlState::eIN_SCENE); + else + { + if(!isSimDisabledInternally()) + { + b->mRemovedShapes.pushBack(RemovedShape(&shape, PxU8(wakeOnLostTouch ? 1 : 0))); + } + else + { + PX_ASSERT(scbScene); + PX_ASSERT(scbScene->isPhysicsBuffering()); + if(toBeReleased) + { + shape.checkUpdateOnRemove(scbScene); +#if PX_SUPPORT_PVD + scbScene->getScenePvdClient().releasePvdInstance(&shape, *getScRigidCore().getPxActor()); +#endif + } + else + b->mRemovedShapes.pushBack(RemovedShape(&shape, 0)); + } + shape.setControlStateIfExclusive(scbScene, ControlState::eREMOVE_PENDING); + } + markUpdated(Buf::BF_Shapes); + } + + PX_INLINE bool isAddedShape(Scb::Shape&); // check whether the specified shape is pending for insertion. Only call this method if you know that there are pending shape adds/removes. + + PX_FORCE_INLINE void switchToNoSim(bool isDynamic); + PX_FORCE_INLINE void switchFromNoSim(bool isDynamic); + PX_FORCE_INLINE void syncNoSimSwitch(const Buf& buf, Sc::RigidCore& rc, bool isDynamic); + + // IMPORTANT: This is the non-buffered state, for the case where it is important to know what the current internal state is. + // Reading is fine even if the sim is running because actor flags are read-only internally. + PX_FORCE_INLINE bool isSimDisabledInternally() const { return getScRigidCore().getActorFlags().isSet(PxActorFlag::eDISABLE_SIMULATION); } + + PX_FORCE_INLINE void clearBufferedState() { resetBufferFlag(Buf::BF_ResetFiltering); } + + PX_FORCE_INLINE static const RigidObject& fromSc(const Sc::RigidCore& a) { return static_cast(Actor::fromSc(a)); } + PX_FORCE_INLINE static RigidObject& fromSc(Sc::RigidCore &a) { return static_cast(Actor::fromSc(a)); } +protected: + ~RigidObject() {} +private: + Buf* getBuffer() { return reinterpret_cast(getStream()); } + + PX_FORCE_INLINE void copyResetFilterShapes(Scb::Shape** shapePtrs, Scb::Shape*const* oldShapes, PxU32 oldShapeCount, Scb::Shape*const* newShapes, PxU32 newShapeCount); +}; + +PX_INLINE void RigidObject::resetFiltering(Scb::Shape*const* shapes, PxU32 shapeCount) +{ + PX_ASSERT(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)); + + if(!isBuffering()) + { + for(PxU32 i=0; i < shapeCount; i++) + getScRigidCore().onShapeChange(shapes[i]->getScShape(), Sc::ShapeChangeNotifyFlag::eRESET_FILTERING, PxShapeFlags()); + } + else + { + RigidObjectBuffer* b = getBuffer(); + + if(b->mResetFilterShapeCount == 0) + { + if(shapeCount == 1) + { + b->mResetFilterShape = shapes[0]; + b->mResetFilterShapeCount = 1; + markUpdated(Buf::BF_ResetFiltering); + } + else + { + PxU32 bufferIdx; + Scb::Shape** shapePtrs = getScbScene()->allocShapeBuffer(shapeCount, bufferIdx); + if(shapePtrs) + { + for(PxU32 i=0; i < shapeCount; i++) + shapePtrs[i] = shapes[i]; + b->mResetFilterShapesIdx = bufferIdx; + b->mResetFilterShapeCount = shapeCount; + markUpdated(Buf::BF_ResetFiltering); + } + } + } + else + { + PxU32 newCount = b->mResetFilterShapeCount + shapeCount; + PxU32 bufferIdx; + Scb::Shape** shapePtrs = getScbScene()->allocShapeBuffer(newCount, bufferIdx); + if(shapePtrs) + { + if(b->mResetFilterShapeCount == 1) + copyResetFilterShapes(shapePtrs, &b->mResetFilterShape, 1, shapes, shapeCount); + else + copyResetFilterShapes(shapePtrs, getScbScene()->getShapeBuffer(b->mResetFilterShapesIdx), b->mResetFilterShapeCount, shapes, shapeCount); + b->mResetFilterShapesIdx = bufferIdx; + b->mResetFilterShapeCount = newCount; + markUpdated(Buf::BF_ResetFiltering); + } + } + } +} + +PX_INLINE bool RigidObject::isAddedShape(Scb::Shape& shape) +{ + PX_ASSERT(isBuffered(Buf::BF_Shapes)); + + if(shape.isExclusive()) + { + return (shape.getControlState() == Scb::ControlState::eINSERT_PENDING); + } + else + { + // For shared shapes it is not clear from the shape alone whether it has been added while the simulation was running. + + RigidObjectBuffer* buf = getBuffer(); + PX_ASSERT(buf); + const PxU32 addedShapeCount = buf->mAddedShapes.size(); + for(PxU32 k=0; k < addedShapeCount; k++) + { + if(&shape == buf->mAddedShapes[k]) + return true; + } + return false; + } +} + +PX_FORCE_INLINE void RigidObject::switchToNoSim(bool isDynamic) +{ + Scb::Scene* scene = getScbScene(); + + if(scene && (!scene->isPhysicsBuffering())) + scene->switchRigidToNoSim(*this, isDynamic); +} + +PX_FORCE_INLINE void RigidObject::switchFromNoSim(bool isDynamic) +{ + Scb::Scene* scene = getScbScene(); + + if(scene && (!scene->isPhysicsBuffering())) + scene->switchRigidFromNoSim(*this, isDynamic); +} + +PX_FORCE_INLINE void RigidObject::syncNoSimSwitch(const Buf& buf, Sc::RigidCore& rc, bool isDynamic) +{ + const PxActorFlags oldFlags = rc.getActorFlags(); + const bool oldNoSim = oldFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + const bool newNoSim = buf.mActorFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + + if(oldNoSim && (!newNoSim)) + getScbScene()->switchRigidFromNoSim(*this, isDynamic); + else if((!oldNoSim) && newNoSim) + getScbScene()->switchRigidToNoSim(*this, isDynamic); +} + +PX_FORCE_INLINE void RigidObject::copyResetFilterShapes(Scb::Shape** shapePtrs, Scb::Shape*const* oldShapes, PxU32 oldShapeCount, Scb::Shape*const* newShapes, PxU32 newShapeCount) +{ + for(PxU32 i=0; i < oldShapeCount; i++) + shapePtrs[i] = oldShapes[i]; + for(PxU32 i=0; i < newShapeCount; i++) + shapePtrs[i+oldShapeCount] = newShapes[i]; +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h b/src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h new file mode 100644 index 000000000..7589faab3 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h @@ -0,0 +1,163 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_RIGID_STATIC +#define PX_PHYSICS_SCB_RIGID_STATIC + +#include "ScStaticCore.h" +#include "ScbScene.h" +#include "ScbActor.h" +#include "ScbRigidObject.h" + +namespace physx +{ +namespace Scb +{ +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +struct RigidStaticBuffer : public RigidObjectBuffer +{ +#ifdef USE_NEW_SYSTEM + PX_ALIGN(16, PxTransform) mActor2World; +#else + template struct Fns {}; // TODO: make the base class traits visible + typedef Sc::StaticCore Core; + typedef RigidStaticBuffer Buf; + + // regular attributes + enum { BF_Base = RigidObjectBuffer::AttrCount }; +// SCB_REGULAR_ATTRIBUTE(BF_Base, PxTransform, Actor2World) + SCB_REGULAR_ATTRIBUTE_ALIGNED(BF_Base, PxTransform, Actor2World, 16) +#endif +}; + +#if PX_VC + #pragma warning(pop) +#endif + +class RigidStatic : public Scb::RigidObject +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef RigidStaticBuffer Buf; + typedef Sc::StaticCore Core; + +public: +// PX_SERIALIZATION + RigidStatic(const PxEMPTY) : Scb::RigidObject(PxEmpty), mStatic(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + PX_INLINE RigidStatic(const PxTransform& actor2World); + PX_INLINE ~RigidStatic() {} + +#ifdef USE_NEW_SYSTEM + SCB_MEMBER(RigidStatic, mStatic, Actor2World, const PxTransform&, RigidObjectBuffer::AttrCount) +#else + PX_INLINE const PxTransform& getActor2World() const { return read(); } + PX_INLINE void setActor2World(const PxTransform& m) { write(m); } +#endif + + PX_FORCE_INLINE void onOriginShift(const PxVec3& shift) { mStatic.onOriginShift(shift); } + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(); + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mStatic); } + + PX_FORCE_INLINE Sc::StaticCore& getScStatic() { return mStatic; } + + PX_FORCE_INLINE void initBufferedState() {} + +private: + Sc::StaticCore mStatic; + + PX_FORCE_INLINE const Buf* getRigidActorBuffer() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Buf* getRigidActorBuffer() { return reinterpret_cast(getStream()); } + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess {}; + + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, mStatic); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, mStatic, v); } + template PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush >(*this, mStatic, buf); } +#endif +}; + +RigidStatic::RigidStatic(const PxTransform& actor2World) : + mStatic(actor2World) +{ + setScbType(ScbType::eRIGID_STATIC); +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- +PX_INLINE void RigidStatic::syncState() +{ + const PxU32 bufferFlags = getBufferFlags(); + +#ifdef USE_NEW_SYSTEM + if(bufferFlags & BF_ActorFlags) +#else + if(bufferFlags & Buf::BF_ActorFlags) +#endif + syncNoSimSwitch(*getRigidActorBuffer(), mStatic, false); + + RigidObject::syncState(); + +#ifdef USE_NEW_SYSTEM + syncActor2World(); +#else + if(bufferFlags & Buf::BF_Actor2World) + flush(*getRigidActorBuffer()); +#endif + postSyncState(); +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp new file mode 100644 index 000000000..751af5837 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp @@ -0,0 +1,1334 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpCast.h" +#include "ScbScene.h" +#include "ScbRigidStatic.h" +#include "ScbBody.h" +#include "ScbShape.h" +#include "ScbConstraint.h" +#include "ScbArticulation.h" +#include "ScbArticulationJoint.h" +#include "ScbNpDeps.h" +#include "ScbAggregate.h" + +#include "PsFoundation.h" +#include "PxArticulation.h" +#include "common/PxProfileZone.h" + +namespace physx +{ + class NpMaterial; +} + +using namespace physx; + +// constants to make boolean template parameters more readable +static const bool tSimRunning = true; +static const bool tAdd = true; +static const bool tDynamic = true; +static const bool tNonSimObject = true; +static const bool tSyncOnRemove = true; +static const bool tWakeOnLostTouchCheck = true; + +void Scb::ObjectTracker::scheduleForInsert(Scb::Base& element) +{ + ControlState::Enum state = element.getControlState(); + PxU32 flags = element.getControlFlags(); + PX_ASSERT(!(flags & ControlFlag::eIS_RELEASED)); + PX_ASSERT(state == ControlState::eNOT_IN_SCENE || state == ControlState::eREMOVE_PENDING); + + if(state == ControlState::eREMOVE_PENDING) + { + element.setControlState(ControlState::eIN_SCENE); + if(!(flags & ControlFlag::eIS_UPDATED)) + remove(element); + } + else + { + PX_ASSERT(!(flags & ControlFlag::eIS_UPDATED)); + element.setControlState(ControlState::eINSERT_PENDING); + insert(element); + } +} + +void Scb::ObjectTracker::scheduleForRemove(Scb::Base& element) +{ + ControlState::Enum state = element.getControlState(); + PxU32 flags = element.getControlFlags(); + + PX_ASSERT(!(flags & ControlFlag::eIS_RELEASED)); + + if(state == ControlState::eINSERT_PENDING) + { + // if it's inserted this frame, just remove it - it can't be dirty + //ML: this assert wont' work because buffered insert raises this flag. We have a unit test which called TEST_F(ObserverTest, OnRelease) to verify it + //PX_ASSERT(!(flags & ControlFlag::eIS_UPDATED)); + element.setControlState(ControlState::eNOT_IN_SCENE); + remove(element); + } + else if(state == ControlState::eIN_SCENE) + { + element.setControlState(ControlState::eREMOVE_PENDING); + if(!(flags & ControlFlag::eIS_UPDATED)) + insert(element); + } + else + { + PX_ALWAYS_ASSERT_MESSAGE("Trying to remove element not in scene."); + } +} + +void Scb::ObjectTracker::scheduleForUpdate(Scb::Base& element) +{ + ControlState::Enum state = element.getControlState(); + PxU32 flags = element.getControlFlags(); + + PX_ASSERT(!(flags & ControlFlag::eIS_RELEASED)); + PX_ASSERT(state == ControlState::eIN_SCENE || state == ControlState::eREMOVE_PENDING || state == ControlState::eINSERT_PENDING); + + if(!(flags & ControlFlag::eIS_UPDATED)) + { + element.setControlFlag(ControlFlag::eIS_UPDATED); + if(state == ControlState::eIN_SCENE) + insert(element); + } +} + +void Scb::ObjectTracker::clear() +{ + Scb::Base *const * elements = mBuffered.getEntries(); + for(PxU32 i=0;igetControlState(); + PxU32 flags = elements[i]->getControlFlags(); + + if(state == ControlState::eIN_SCENE || state == ControlState::eINSERT_PENDING) + elements[i]->resetControl(ControlState::eIN_SCENE); + else + { + elements[i]->resetControl(ControlState::eNOT_IN_SCENE); + elements[i]->setScbScene(NULL); + } + + if(flags & ControlFlag::eIS_RELEASED) + NpDestroy(*elements[i]); + } + mBuffered.clear(); +} + +void Scb::ObjectTracker::insert(Scb::Base& element) +{ + PX_ASSERT(!mBuffered.contains(&element)); + mBuffered.insert(&element); +} + +void Scb::ObjectTracker::remove(Scb::Base& element) +{ + mBuffered.erase(&element); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +PX_FORCE_INLINE static void addOrRemoveRigidObject(Sc::Scene& s, T& rigidObject, bool wakeOnLostTouch, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure); + +template struct ScSceneFns {}; + +template<> struct ScSceneFns +{ + static PX_FORCE_INLINE void insert(Sc::Scene& s, Scb::Articulation& v, PxBounds3*, const Gu::BVHStructure*) + { + Scb::Body* b = NpArticulationGetRootFromScb(v); + s.addArticulation(v.getScArticulation(), b->getScBody()); + } + static PX_FORCE_INLINE void remove(Sc::Scene& s, Scb::Articulation& v, bool wakeOnLostTouch) + { + PX_UNUSED(wakeOnLostTouch); + + v.clearBufferedSleepStateChange(); // see comment in remove code of Scb::Body + + s.removeArticulation(v.getScArticulation()); + } +}; + +template<> struct ScSceneFns +{ + static PX_FORCE_INLINE void insert(Sc::Scene& s, Scb::ArticulationJoint& v, PxBounds3*, const Gu::BVHStructure*) + { + Scb::Body* scb0, * scb1; + NpArticulationJointGetBodiesFromScb(v, scb0, scb1); + s.addArticulationJoint(v.getScArticulationJoint(), scb0->getScBody(), scb1->getScBody()); + } + static PX_FORCE_INLINE void remove(Sc::Scene& s, Scb::ArticulationJoint& v, bool wakeOnLostTouch) + { + PX_UNUSED(wakeOnLostTouch); + s.removeArticulationJoint(v.getScArticulationJoint()); + } +}; + +template<> struct ScSceneFns +{ + static PX_FORCE_INLINE void insert(Sc::Scene& s, Scb::Constraint& v, PxBounds3*, const Gu::BVHStructure*) + { + Scb::RigidObject* scb0, * scb1; + NpConstraintGetRigidObjectsFromScb(v, scb0, scb1); + + PX_ASSERT((!scb0) || (!(scb0->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))); + PX_ASSERT((!scb1) || (!(scb1->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))); + + s.addConstraint(v.getScConstraint(), scb0 ? &scb0->getScRigidCore() : NULL, scb1 ? &scb1->getScRigidCore() : NULL); + } + static PX_FORCE_INLINE void remove(Sc::Scene& s, Scb::Constraint& v, bool wakeOnLostTouch) + { + PX_UNUSED(wakeOnLostTouch); + s.removeConstraint(v.getScConstraint()); + } +}; + +template<> struct ScSceneFns +{ + static PX_FORCE_INLINE void insert(Sc::Scene& s, Scb::RigidStatic& v, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) + { + // important to use the buffered flags because for a pending insert those describe the end state the + // user expects. + + if (!(v.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + addOrRemoveRigidObject(s, v, false, uninflatedBounds, bvhStructure); + else + addOrRemoveRigidObject(s, v, false, uninflatedBounds, bvhStructure); + } + static PX_FORCE_INLINE void remove(Sc::Scene& s, Scb::RigidStatic& v, bool wakeOnLostTouch) + { + // important to use the original flags because for a pending removal those describe the original state that needs + // to get cleaned up. + + if (!v.isSimDisabledInternally()) + addOrRemoveRigidObject(s, v, wakeOnLostTouch, NULL, NULL); + else + addOrRemoveRigidObject(s, v, false, NULL, NULL); + } +}; + +template<> struct ScSceneFns +{ + static PX_FORCE_INLINE void insert(Sc::Scene& s, Scb::Body& v, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) + { + // see comments in rigid static case + if (!(v.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + addOrRemoveRigidObject(s, v, false, uninflatedBounds, bvhStructure); + else + addOrRemoveRigidObject(s, v, false, uninflatedBounds, bvhStructure); + } + static PX_FORCE_INLINE void remove(Sc::Scene& s, Scb::Body& v, bool wakeOnLostTouch) + { + // strictly speaking, the following is only necessary for pending removes but it does not have a + // functional side effect if applied all the time. + // When an object gets removed from the scene, pending wakeUp/putToSleep events should be ignored because + // the internal sleep state for a free standing object is specified as sleeping. All the other parameter changes + // that go along with a sleep state change should still get processed though (zero vel, zero wake counter on + // putToSleep for example). Those are not affected because they are tracked through buffered updates + // of the velocity and wake counter. + // The clearing happens here because only here we are sure that the object does get removed for real. At earlier + // stages someone might remove and then re-insert and object and for such cases it is important to keep the + // sleep state change buffered. + v.clearBufferedSleepStateChange(); + + // see comments in rigid static case + if (!v.isSimDisabledInternally()) + addOrRemoveRigidObject(s, v, wakeOnLostTouch, NULL, NULL); + else + addOrRemoveRigidObject(s, v, false, NULL, NULL); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +#if PX_SUPPORT_PVD +template struct PvdFns +{ + // PT: in the following functions, checkPvdDebugFlag() is done by the callers to save time when functions are called from a loop. + + static void createInstance(Scb::Scene& scene, Vd::ScbScenePvdClient& d, T* v) + { + PX_PROFILE_ZONE("PVD.createPVDInstance", scene.getContextId()); + PX_UNUSED(scene); + d.createPvdInstance(v); + } + + static void updateInstance(Scb::Scene& scene, Vd::ScbScenePvdClient& d, T* v) + { + PX_UNUSED(scene); + if(((v->getControlFlags() & Scb::ControlFlag::eIS_RELEASED) == 0) && (v->getControlState() != Scb::ControlState::eREMOVE_PENDING)) + { + PX_PROFILE_ZONE("PVD.updatePVDProperties", scene.getContextId()); + d.updatePvdProperties(v); + } + } + + static void releaseInstance(Scb::Scene& scene, Vd::ScbScenePvdClient& d, T* v) + { + PX_UNUSED(scene); + PX_PROFILE_ZONE("PVD.releasePVDInstance", scene.getContextId()); + d.releasePvdInstance(v); + } +}; + + #define CREATE_PVD_INSTANCE(obj) \ + { \ + if(mScenePvdClient.checkPvdDebugFlag()) \ + { \ + PX_PROFILE_ZONE("PVD.createPVDInstance", getContextId());\ + mScenePvdClient.createPvdInstance(obj); \ + } \ + } + #define RELEASE_PVD_INSTANCE(obj) \ + { \ + if(mScenePvdClient.checkPvdDebugFlag()) \ + { \ + PX_PROFILE_ZONE("PVD.releasePVDInstance", getContextId());\ + mScenePvdClient.releasePvdInstance(obj); \ + } \ + } + #define UPDATE_PVD_PROPERTIES(obj) \ + { \ + if(mScenePvdClient.checkPvdDebugFlag()) \ + { \ + PX_PROFILE_ZONE("PVD.updatePVDProperties", getContextId());\ + mScenePvdClient.updatePvdProperties(obj); \ + } \ + } + #define PVD_ORIGIN_SHIFT(shift) \ + { \ + if(mScenePvdClient.checkPvdDebugFlag()) \ + { \ + PX_PROFILE_ZONE("PVD.originShift", getContextId());\ + mScenePvdClient.originShift(shift); \ + } \ + } +#else + #define CREATE_PVD_INSTANCE(obj) {} + #define RELEASE_PVD_INSTANCE(obj) {} + #define UPDATE_PVD_PROPERTIES(obj) {} + #define PVD_ORIGIN_SHIFT(shift){} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +Scb::Scene::Scene(const PxSceneDesc& desc, PxU64 contextID) : + mScene (desc, contextID), + mSimulationRunning (false), + mIsBuffering (false), + mStream (16384), + mShapeMaterialBuffer (PX_DEBUG_EXP("shapeMaterialBuffer")), + mShapePtrBuffer (PX_DEBUG_EXP("shapePtrBuffer")), + mActorPtrBuffer (PX_DEBUG_EXP("actorPtrBuffer")), +#if PX_SUPPORT_PVD + mScenePvdClient (*this), +#endif + mWakeCounterResetValue (desc.wakeCounterResetValue), + mBufferFlags (0) +{ +} + +void Scb::Scene::release() +{ +#if PX_SUPPORT_PVD + mScenePvdClient.releasePvdInstance(); +#endif + mScene.release(); + mShapeMaterialBuffer.clear(); + mShapePtrBuffer.clear(); + mActorPtrBuffer.clear(); + mStream.clear(); +} + +// PT: TODO: inline this +PxScene* Scb::Scene::getPxScene() +{ + return const_cast(getNpScene(this)); +} + +/////////////////////////////////////////////////////////////////////////////// +template +void Scb::Scene::add(T& v, ObjectTracker &tracker, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) +{ + v.setScbScene(this); + + if (!mIsBuffering) + { + v.resetControl(ControlState::eIN_SCENE); + ScSceneFns::insert(mScene, v, uninflatedBounds, bvhStructure); +#if PX_SUPPORT_PVD + if(mScenePvdClient.checkPvdDebugFlag()) + PvdFns::createInstance(*this, mScenePvdClient, &v); +#endif + } + else + tracker.scheduleForInsert(v); +} + +template +void Scb::Scene::remove(T& v, ObjectTracker &tracker, bool wakeOnLostTouch) +{ + if (!mIsBuffering) + { + ScSceneFns::remove(mScene, v, wakeOnLostTouch); +#if PX_SUPPORT_PVD + if(mScenePvdClient.checkPvdDebugFlag()) + PvdFns::releaseInstance(*this, mScenePvdClient, &v); +#endif + v.resetControl(ControlState::eNOT_IN_SCENE); + v.setScbScene(NULL); + } + else + { + tracker.scheduleForRemove(v); + } +} + + +/////////////////////////////////////////////////////////////////////////////// + +template +void Scb::Scene::addRigidNoSim(T& v, ObjectTracker &tracker, const Gu::BVHStructure* bvhStructure) +{ + PX_ASSERT(v.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION); + v.setScbScene(this); + + if (!mIsBuffering) + { + v.resetControl(ControlState::eIN_SCENE); +#if PX_SUPPORT_PVD + if(mScenePvdClient.checkPvdDebugFlag()) + PvdFns::createInstance(*this, mScenePvdClient, &v); +#endif + addOrRemoveRigidObject(mScene, v, false, NULL, bvhStructure); + } + else + { + tracker.scheduleForInsert(v); + addOrRemoveRigidObject(mScene, v, false, NULL, bvhStructure); + } +} + + +template +void Scb::Scene::removeRigidNoSim(T& v, ObjectTracker &tracker) +{ + PX_ASSERT(v.isSimDisabledInternally()); + + if (!mIsBuffering) + { + addOrRemoveRigidObject(mScene, v, false, NULL, NULL); +#if PX_SUPPORT_PVD + if(mScenePvdClient.checkPvdDebugFlag()) + PvdFns::releaseInstance(*this, mScenePvdClient, &v); +#endif + v.resetControl(ControlState::eNOT_IN_SCENE); + v.setScbScene(NULL); + } + else + { + tracker.scheduleForRemove(v); + addOrRemoveRigidObject(mScene, v, false, NULL, NULL); + } +} + + +void Scb::Scene::switchRigidToNoSim(Scb::RigidObject& r, bool isDynamic) +{ + PX_ASSERT(!mIsBuffering); + + // when a simulation objects has a pending remove and then gets switched to a non-simulation object, + // we must not process the code below. On sync the object will get removed before this call. + if (r.getControlState() == ControlState::eIN_SCENE) + { + size_t ptrOffset = -Scb::Shape::getScOffset(); + Ps::InlineArray scShapes; + + if (isDynamic) + mScene.removeBody(static_cast(r.getScRigidCore()), scShapes, true); + else + mScene.removeStatic(static_cast(r.getScRigidCore()), scShapes, true); + + // not in simulation anymore -> decrement shape ref-counts + void* const* shapes = reinterpret_cast(const_cast(scShapes.begin())); + for(PxU32 i=0; i < scShapes.size(); i++) + { + Scb::Shape& scbShape = *Ps::pointerOffset(shapes[i], ptrdiff_t(ptrOffset)); + NpShapeDecRefCount(scbShape); + } + } +} + + +void Scb::Scene::switchRigidFromNoSim(Scb::RigidObject& r, bool isDynamic) +{ + PX_ASSERT(!mIsBuffering); + + // when a non-simulation objects has a pending remove and then gets switched to a simulation object, + // we must not process the code below. On sync the object will get removed before this call. + if (r.getControlState() == ControlState::eIN_SCENE) + { + void* const* shapes; + size_t shapePtrOffset = NpShapeGetScPtrOffset(); + size_t ptrOffset = shapePtrOffset - Scb::Shape::getScOffset(); + + PxU32 nbShapes; + if (isDynamic) + { + bool isCompound; + nbShapes = NpRigidDynamicGetShapes(static_cast(r), shapes, &isCompound); + mScene.addBody(static_cast(r.getScRigidCore()), shapes, nbShapes, shapePtrOffset, NULL, isCompound); + } + else + { + nbShapes = NpRigidStaticGetShapes(static_cast(r), shapes); + mScene.addStatic(static_cast(r.getScRigidCore()), shapes, nbShapes, shapePtrOffset, NULL); + } + + // add to simulation -> increment shape ref-counts + for(PxU32 i=0; i < nbShapes; i++) + { + Scb::Shape& scbShape = *Ps::pointerOffset(shapes[i], ptrdiff_t(ptrOffset)); + NpShapeIncRefCount(scbShape); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// + +// PT: TODO: +// - consider making these templates not scene member functions + +template +PX_FORCE_INLINE void Scb::Scene::addActorT(T& actor, Scb::ObjectTracker& tracker, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) +{ + PX_PROFILE_ZONE("API.addActorToSim", getContextId()); + if(!noSim) + { + add(actor, tracker, uninflatedBounds, bvhStructure); + actor.initBufferedState(); + + // copy buffer control state from rigid object to shapes and set scene + if(mIsBuffering) + addOrRemoveRigidObject(mScene, actor, false, NULL, bvhStructure); + } + else + { + addRigidNoSim(actor, tracker, bvhStructure); + actor.initBufferedState(); + } +} + +void Scb::Scene::addActor(Scb::RigidStatic& rigidStatic, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) +{ + addActorT(rigidStatic, mRigidStaticManager, noSim, uninflatedBounds, bvhStructure); +} + +void Scb::Scene::addActor(Scb::Body& body, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) +{ + addActorT(body, mBodyManager, noSim, uninflatedBounds, bvhStructure); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::removeActor(Scb::RigidStatic& rigidStatic, bool wakeOnLostTouch, bool noSim) +{ + PX_PROFILE_ZONE("API.removeActorFromSim", getContextId()); + if (!noSim) + { + remove(rigidStatic, mRigidStaticManager, wakeOnLostTouch); + + // copy buffer control state from rigid object to shapes and set scene + if (mIsBuffering) + { + if (wakeOnLostTouch) + rigidStatic.scheduleForWakeTouching(); + addOrRemoveRigidObject(mScene, rigidStatic, wakeOnLostTouch, NULL, NULL); + } + } + else + { + removeRigidNoSim(rigidStatic, mRigidStaticManager); + } + + rigidStatic.clearBufferedState(); +} + +void Scb::Scene::removeActor(Scb::Body& body, bool wakeOnLostTouch, bool noSim) +{ + PX_PROFILE_ZONE("API.removeActorFromSim", getContextId()); + if (!noSim) + { + body.clearSimStateDataForPendingInsert(); + + remove(body, mBodyManager, wakeOnLostTouch); + body.clearBufferedState(); + + // copy buffer control state from rigid object to shapes and set scene + if (mIsBuffering) + { + if (wakeOnLostTouch) + body.scheduleForWakeTouching(); + addOrRemoveRigidObject(mScene, body, wakeOnLostTouch, NULL, NULL); + } + } + else + { + removeRigidNoSim(body, mBodyManager); + + // note: "noSim" refers to the internal state here. The following asserts only apply if the bufferd state has not switched to "sim". + PX_ASSERT(!(body.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION) || body.isSleeping()); + PX_ASSERT(!(body.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION) || !body.isBuffered(BodyBuffer::BF_KinematicTarget | BodyBuffer::BF_Acceleration | BodyBuffer::BF_DeltaVelocity)); + // What about velocity, wakeCounter, ...? + // Those are not allowed on a no-sim object, however, they might still be necessary due to complex buffering scenarios: + // Imagine the following operation flow (all buffered): + // - dynamic sim object awake with velocities + // - switch to no-sim -> needs to clear velocities, wake counter, put to sleep, ... + // - switch back to sim -> the velocities, wake counter, ... still need to get cleared and it needs to be asleep (that would be the non-buffered behavior of the operations) + + body.clearBufferedState(); // this also covers the buffered case where a noSim object gets switched to a sim object, followed by a wakeUp() call and then a remove. + // If we checked whether the buffered object is still a noSim object then only body.RigidObject::clearBufferedState() would be necessary. + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::addConstraint(Scb::Constraint& constraint) +{ + add(constraint, mConstraintManager, NULL, NULL); +} + +void Scb::Scene::removeConstraint(Scb::Constraint& constraint) +{ + if (!mIsBuffering) + { + mScene.removeConstraint(constraint.getScConstraint()); + + // Release pvd constraint immediately since delayed removal with already released ext::joints does not work, can't call callback. + if(constraint.getControlState() != ControlState::eINSERT_PENDING) + RELEASE_PVD_INSTANCE(&constraint) + + constraint.resetControl(ControlState::eNOT_IN_SCENE); + constraint.setScbScene(NULL); + } + else + { + mConstraintManager.scheduleForRemove(constraint); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::addArticulation(Scb::Articulation& articulation) +{ + add(articulation, mArticulationManager, NULL, NULL); + articulation.initBufferedState(); +} + +void Scb::Scene::removeArticulation(Scb::Articulation& articulation) +{ + remove(articulation, mArticulationManager); + articulation.clearBufferedState(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::addArticulationJoint(Scb::ArticulationJoint& joint) +{ + add(joint, mArticulationJointManager, NULL, NULL); +} + +void Scb::Scene::removeArticulationJoint(Scb::ArticulationJoint& joint) +{ + remove(joint, mArticulationJointManager); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::addAggregate(Scb::Aggregate& agg) +{ + agg.setScbScene(this); + + if (!mIsBuffering) + { + PxU32 aggregateID = mScene.createAggregate(agg.mPxAggregate, agg.getSelfCollide()); + agg.setAggregateID(aggregateID); + agg.resetControl(ControlState::eIN_SCENE); +#if PX_SUPPORT_PVD + //Sending pvd events after all aggregates's actors are inserted into scene + mScenePvdClient.createPvdInstance(&agg); +#endif + } + else + mAggregateManager.scheduleForInsert(agg); +} + + +void Scb::Scene::removeAggregate(Scb::Aggregate& agg) +{ + if (!mIsBuffering) + { + mScene.deleteAggregate(agg.getAggregateID()); + agg.resetControl(ControlState::eNOT_IN_SCENE); + agg.setScbScene(NULL); +#if PX_SUPPORT_PVD + mScenePvdClient.releasePvdInstance(&agg); +#endif + } + else + { + mAggregateManager.scheduleForRemove(agg); + } +} + + +void Scb::Scene::addMaterial(const Sc::MaterialCore& material) +{ + Ps::Mutex::ScopedLock lock(mSceneMaterialBufferLock); + + mSceneMaterialBuffer.pushBack(MaterialEvent(material.getMaterialIndex(), MATERIAL_ADD)); + + CREATE_PVD_INSTANCE(&material) +} + +void Scb::Scene::updateMaterial(const Sc::MaterialCore& material) +{ + Ps::Mutex::ScopedLock lock(mSceneMaterialBufferLock); + + mSceneMaterialBuffer.pushBack(MaterialEvent(material.getMaterialIndex(), MATERIAL_UPDATE)); + + UPDATE_PVD_PROPERTIES(&material) +} + +void Scb::Scene::removeMaterial(const Sc::MaterialCore& material) +{ + if(material.getMaterialIndex() == MATERIAL_INVALID_HANDLE) + return; + + Ps::Mutex::ScopedLock lock(mSceneMaterialBufferLock); + + mSceneMaterialBuffer.pushBack(MaterialEvent(material.getMaterialIndex(), MATERIAL_REMOVE)); + + RELEASE_PVD_INSTANCE(&material); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::updateLowLevelMaterial(NpMaterial** masterMaterial) +{ + Ps::Mutex::ScopedLock lock(mSceneMaterialBufferLock); + + //sync all the material events + PxsMaterialManager& manager = mScene.getMaterialManager(); + for(PxU32 i=0; i< mSceneMaterialBuffer.size(); ++i) + { + const MaterialEvent& event = mSceneMaterialBuffer[i]; + const NpMaterial* masMat = masterMaterial[event.mHandle]; + switch(event.mType) + { + case MATERIAL_ADD: + if(masMat) + { + Sc::MaterialCore* materialCore = &masterMaterial[event.mHandle]->getScMaterial(); + manager.setMaterial(materialCore); + mScene.registerMaterialInNP(*materialCore); + } + break; + case MATERIAL_UPDATE: + if(masMat) + { + Sc::MaterialCore* materialCore = &masterMaterial[event.mHandle]->getScMaterial(); + manager.updateMaterial(materialCore); + mScene.updateMaterialInNP(*materialCore); + } + break; + case MATERIAL_REMOVE: + if (event.mHandle < manager.getMaxSize()) // materials might get added and then removed again immediately. However, the add does not get processed (see case MATERIAL_ADD above), + { // so the remove might end up reading out of bounds memory unless checked. + PxsMaterialCore* materialCore = manager.getMaterial(event.mHandle); + if (materialCore->getMaterialIndex() == event.mHandle) + { + mScene.unregisterMaterialInNP(*materialCore); + manager.removeMaterial(materialCore); + } + } + break; + }; + } + + mSceneMaterialBuffer.resize(0); +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- +void Scb::Scene::syncState() +{ + //process client creation -- must be done before BF_CLIENT_BEHAVIOR_FLAGS processing in the below block: + while(mBufferedData.mNumClientsCreated) + { + mScene.createClient(); + mBufferedData.mNumClientsCreated--; + } + + if(mBufferFlags) + { + if(isBuffered(BF_GRAVITY)) + mScene.setGravity(mBufferedData.mGravity); + + if(isBuffered(BF_BOUNCETHRESHOLDVELOCITY)) + mScene.setBounceThresholdVelocity(mBufferedData.mBounceThresholdVelocity); + + if(isBuffered(BF_FLAGS)) + mScene.setPublicFlags(mBufferedData.mFlags); + + if(isBuffered(BF_DOMINANCE_PAIRS)) + mBufferedData.syncDominancePairs(mScene); + + if(isBuffered(BF_SOLVER_BATCH_SIZE)) + mScene.setSolverBatchSize(mBufferedData.mSolverBatchSize); + + if(isBuffered(BF_VISUALIZATION)) + { + for(PxU32 i=0; i +void Scb::Scene::processUserUpdates(ObjectTracker &tracker) +{ +#if PX_SUPPORT_PVD + bool isPvdValid = mScenePvdClient.checkPvdDebugFlag(); +#endif + Base*const * buffered = tracker.getBuffered(); + for(PxU32 i=0; i < tracker.getBufferedCount(); i++) + { + T& v = *static_cast(buffered[i]); + if (v.getControlState() == ControlState::eINSERT_PENDING) + { + ScSceneFns::insert(mScene, v, NULL, NULL); +#if PX_SUPPORT_PVD + if(isPvdValid) + PvdFns::createInstance(*this, mScenePvdClient, &v); +#endif + } + else if(v.getControlFlags() & ControlFlag::eIS_UPDATED) + { + v.syncState(); +#if PX_SUPPORT_PVD + if(isPvdValid) + PvdFns::updateInstance(*this, mScenePvdClient, &v); +#endif + } + } +} + +template +void Scb::Scene::processSimUpdates(S*const * scObjects, PxU32 nbObjects) +{ +#if PX_SUPPORT_PVD + bool isPvdValid = mScenePvdClient.checkPvdDebugFlag(); +#endif + for(PxU32 i=0;i::updateInstance(*this, mScenePvdClient, &v); +#endif + } + } +} + +#define ENABLE_PVD_ORIGINSHIFT_EVENT +void Scb::Scene::shiftOrigin(const PxVec3& shift) +{ + PX_ASSERT(!isPhysicsBuffering()); + mScene.shiftOrigin(shift); + +#ifdef ENABLE_PVD_ORIGINSHIFT_EVENT + PVD_ORIGIN_SHIFT(shift); +#endif +} + +void Scb::Scene::syncWriteThroughProperties() +{ + mStream.lock(); + + Base*const * buffered = mBodyManager.getBuffered(); + PxU32 count = mBodyManager.getBufferedCount(); + for(PxU32 i=0; i < count; i++) + { + Scb::Body& bufferedBody = *static_cast(buffered[i]); + bufferedBody.syncCollisionWriteThroughState(); + } + + mStream.unlock(); +} + +void Scb::Scene::syncEntireScene() +{ + PX_PROFILE_ZONE("Sim.syncState", getContextId()); + + setPhysicsBuffering(false); // Clear the buffering flag to allow buffered writes to execute immediately. Once collision detection is running, buffering is automatically forced on + + mStream.lock(); + syncState(); + // + // Process aggregates (needs to be done before adding actors because the actor's aggregateID needs to get set) + // + + for(PxU32 i=0; i < mAggregateManager.getBufferedCount(); i++) + { + Aggregate* a = static_cast(mAggregateManager.getBuffered()[i]); + if (a->getControlState() == ControlState::eINSERT_PENDING) + { + PxU32 aggregateID = mScene.createAggregate(a->mPxAggregate, a->getSelfCollide()); + a->setAggregateID(aggregateID); +#if PX_SUPPORT_PVD + mScenePvdClient.createPvdInstance(a); +#endif + a->syncState(*this); // Necessary to set the aggregate ID for all actors of the aggregate + } + else if(a->getControlFlags() & ControlFlag::eIS_UPDATED) + { + a->syncState(*this); + } + } + mAggregateManager.clear(); + mActorPtrBuffer.clear(); + + + // rigid statics + processUserUpdates(mRigidStaticManager); + mRigidStaticManager.clear(); + + + // rigid dynamics and articulation links + // + // 1) Sync simulation changed data + { + PX_PROFILE_ZONE("SyncActiveBodies", getContextId()); + Sc::BodyCore*const* activeBodies = mScene.getActiveBodiesArray(); + PxU32 nbActiveBodies = mScene.getNumActiveBodies(); + while(nbActiveBodies--) + { + Sc::BodyCore* bodyCore = *activeBodies++; + Scb::Body& bufferedBody = Scb::Body::fromSc(*bodyCore); + if (!(bufferedBody.getControlFlags() & ControlFlag::eIS_UPDATED)) // Else the data will be synced further below + bufferedBody.syncState(); + } + } + + // 2) Sync data of rigid dynamics which were put to sleep by the simulation + + PxU32 nbSleepingBodies; + Sc::BodyCore* const* sleepingBodies = mScene.getSleepBodiesArray(nbSleepingBodies); + processSimUpdates(sleepingBodies, nbSleepingBodies); + + // user updates + processUserUpdates(mBodyManager); + mBodyManager.clear(); + mShapePtrBuffer.clear(); + + + // rigid body shapes + // + // IMPORTANT: This has to run after the material update + // + // Sync user changed data. Inserts and removes are handled in actor sync + for(PxU32 i=0; i < mShapeManager.getBufferedCount(); i++) + { + Scb::Shape* s = static_cast(mShapeManager.getBuffered()[i]); + + if(s->getControlFlags() & ControlFlag::eIS_UPDATED) + { + s->syncState(); + UPDATE_PVD_PROPERTIES(s) + } + } + + mShapeManager.clear(); + mShapeMaterialBuffer.clear(); + + // constraints (get break force and broken status from sim) + + processSimUpdates(mScene.getConstraints(), mScene.getNbConstraints()); + processUserUpdates(mConstraintManager); + mConstraintManager.clear(); + + // articulations (get sleep state from sim) + processSimUpdates(mScene.getArticulations(), mScene.getNbArticulations()); + processUserUpdates(mArticulationManager); + mArticulationManager.clear(); + + + // Process articulation joints + processUserUpdates(mArticulationJointManager); + mArticulationJointManager.clear(); + + + mStream.clearNotThreadSafe(); + mStream.unlock(); +} + + + +template +void Scb::Scene::processRemoves(ObjectTracker& tracker) +{ +#if PX_SUPPORT_PVD + bool isPvdValid = mScenePvdClient.checkPvdDebugFlag(); +#endif + typedef ScSceneFns Fns; + for(PxU32 i=0; i < tracker.getBufferedCount(); i++) + { + T* v = static_cast(tracker.getBuffered()[i]); + if(v->getControlState() == ControlState::eREMOVE_PENDING) + { + bool wakeOnLostTouch = false; + if (wakeOnLostTouchCheck) + { + PX_ASSERT( (v->getScbType() == ScbType::eBODY) || + (v->getScbType() == ScbType::eBODY_FROM_ARTICULATION_LINK) || + (v->getScbType() == ScbType::eRIGID_STATIC) ); + wakeOnLostTouch = (v->Base::isBuffered(RigidObjectBuffer::BF_WakeTouching) != 0); // important to use Scb::Base::isBuffered() because Scb::Body, for example, has a shadowed implementation of this method + } + + Fns::remove(mScene, *v, wakeOnLostTouch); + + // if no object param has been updated, the state sync still needs to be processed to write simulation results + // back to the permanently buffered params. + if (syncOnRemove && !(v->getControlFlags() & ControlFlag::eIS_UPDATED)) + v->syncState(); +#if PX_SUPPORT_PVD + if(isPvdValid) + PvdFns::releaseInstance(*this, mScenePvdClient, v); +#endif + } + } +} + +template +void Scb::Scene::processShapeRemoves(ObjectTracker& tracker) +{ + for(PxU32 i=0; i < tracker.getBufferedCount(); i++) + { + T* v = static_cast(tracker.getBuffered()[i]); + v->processShapeRemoves(); + } +} + +void Scb::Scene::processPendingRemove() +{ + processShapeRemoves(mRigidStaticManager); + processShapeRemoves(mBodyManager); + + processRemoves(mConstraintManager); + + Scb::Base *const * buffered = mConstraintManager.getBuffered(); + for(PxU32 i=0; i < mConstraintManager.getBufferedCount(); i++) + { + Scb::Constraint* constraint = static_cast(buffered[i]); + + if(constraint->getControlFlags() & ControlFlag::eIS_UPDATED) + constraint->prepareForActorRemoval(); // see comments in Scb::Constraint + } + + processRemoves (mArticulationJointManager); + processRemoves (mRigidStaticManager); + processRemoves (mBodyManager); + processRemoves (mArticulationManager); + + // Do after actors have been removed (coumpound can only be removed after all its elements are gone) + for(PxU32 i=0; i < mAggregateManager.getBufferedCount(); i++) + { + Aggregate* a = static_cast(mAggregateManager.getBuffered()[i]); + + if(a->getControlState() == ControlState::eREMOVE_PENDING) + { + a->syncState(*this); // Clears the aggregate ID for all actors of the aggregate + mScene.deleteAggregate(a->getAggregateID()); + +#if PX_SUPPORT_PVD + mScenePvdClient.releasePvdInstance(a); +#endif + } + } +} + +void Scb::Scene::scheduleForUpdate(Scb::Base& object) +{ + switch(object.getScbType()) + { + case ScbType::eSHAPE_EXCLUSIVE: + case ScbType::eSHAPE_SHARED: { mShapeManager.scheduleForUpdate(object); }break; + case ScbType::eBODY: { mBodyManager.scheduleForUpdate(object); }break; + case ScbType::eBODY_FROM_ARTICULATION_LINK: { mBodyManager.scheduleForUpdate(object); }break; + case ScbType::eRIGID_STATIC: { mRigidStaticManager.scheduleForUpdate(object); }break; + case ScbType::eCONSTRAINT: { mConstraintManager.scheduleForUpdate(object); }break; + case ScbType::eARTICULATION: { mArticulationManager.scheduleForUpdate(object); }break; + case ScbType::eARTICULATION_JOINT: { mArticulationJointManager.scheduleForUpdate(object); }break; + case ScbType::eAGGREGATE: { mAggregateManager.scheduleForUpdate(object); }break; + case ScbType::eUNDEFINED: + case ScbType::eTYPE_COUNT: + PX_ALWAYS_ASSERT_MESSAGE( "scheduleForUpdate: missing type!"); + break; + } +} + +PxU8* Scb::Scene::getStream(ScbType::Enum type) +{ + PxU8* memory = NULL; + switch(type) + { + case ScbType::eSHAPE_EXCLUSIVE: + case ScbType::eSHAPE_SHARED: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::ShapeBuffer))); new (memory) Scb::ShapeBuffer; }break; + case ScbType::eBODY: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::BodyBuffer))); new (memory) Scb::BodyBuffer; }break; + case ScbType::eBODY_FROM_ARTICULATION_LINK: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::BodyBuffer))); new (memory) Scb::BodyBuffer; }break; + case ScbType::eRIGID_STATIC: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::RigidStaticBuffer))); new (memory) Scb::RigidStaticBuffer; }break; + case ScbType::eCONSTRAINT: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::ConstraintBuffer))); new (memory) Scb::ConstraintBuffer; }break; + case ScbType::eARTICULATION: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::ArticulationBuffer))); new (memory) Scb::ArticulationBuffer; }break; + case ScbType::eARTICULATION_JOINT: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::ArticulationJointBuffer))); new (memory) Scb::ArticulationJointBuffer; }break; + case ScbType::eAGGREGATE: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::AggregateBuffer))); new (memory) Scb::AggregateBuffer; }break; + case ScbType::eUNDEFINED: + case ScbType::eTYPE_COUNT: + PX_ALWAYS_ASSERT_MESSAGE("getStream: missing type!"); + return NULL; + } + return memory; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxBroadPhaseType::Enum Scb::Scene::getBroadPhaseType() const +{ + return mScene.getBroadPhaseType(); +} + +bool Scb::Scene::getBroadPhaseCaps(PxBroadPhaseCaps& caps) const +{ + return mScene.getBroadPhaseCaps(caps); +} + +PxU32 Scb::Scene::getNbBroadPhaseRegions() const +{ + return mScene.getNbBroadPhaseRegions(); +} + +PxU32 Scb::Scene::getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return mScene.getBroadPhaseRegions(userBuffer, bufferSize, startIndex); +} + +PxU32 Scb::Scene::addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion) +{ + if(!isPhysicsBuffering()) + return mScene.addBroadPhaseRegion(region, populateRegion); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addBroadPhaseRegion() not allowed while simulation is running. Call will be ignored."); + return 0xffffffff; +} + +bool Scb::Scene::removeBroadPhaseRegion(PxU32 handle) +{ + if(!isPhysicsBuffering()) + return mScene.removeBroadPhaseRegion(handle); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::removeBroadPhaseRegion() not allowed while simulation is running. Call will be ignored."); + return false; +} + +////////////////////////////////////////////////////////////////////////// + +// +// To avoid duplication of a lot of similar code, the following templated method was introduced. Its main purpose is to +// take care of all the operations related to adding/removing a rigid object to/from the scene. Depending on the type +// of rigid object and the simulation state, there are slight changes to the code flow necessary. Among the operations are: +// +// - Add/remove rigid object to/from scene +// - Add/remove shapes from PVD +// - Adjust buffer control state of shapes +// - Adjust ref-count of shapes +// +template +PX_FORCE_INLINE static void addOrRemoveRigidObject(Sc::Scene& s, T& rigidObject, bool wakeOnLostTouch, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) +{ + PX_ASSERT(TIsDynamic || (rigidObject.getScbType() == ScbType::eRIGID_STATIC)); + if (TSimRunning && TIsNonSimObject && TAdd) + PX_ASSERT(rigidObject.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION); + if (TSimRunning && TIsNonSimObject&& !TAdd) + PX_ASSERT(rigidObject.isSimDisabledInternally()); + if (!TSimRunning && TIsNonSimObject) + PX_ASSERT(rigidObject.isSimDisabledInternally()); // when the simulation flag gets cleared on an object with pending removal, only the core flag knows that internally it is still a non-simulation object. + PX_ASSERT(!uninflatedBounds || (TAdd && !TSimRunning && !TIsNonSimObject)); + + Ps::InlineArray localShapes; + Ps::InlineArray& scShapes = s.getBatchRemove() ? s.getBatchRemove()->removedShapes : localShapes; + + PxActor* pxActor = NULL; + void* const* shapes; + PxU32 nbShapes; + size_t shapePtrOffset = NpShapeGetScPtrOffset(); + + Scb::Body& dynamicObject = reinterpret_cast(rigidObject); + Scb::RigidStatic& staticObject = reinterpret_cast(rigidObject); + + if (!TSimRunning) + { + if (TIsDynamic) + pxActor = dynamicObject.getScBody().getPxActor(); + else + pxActor = staticObject.getScStatic().getPxActor(); + } + PX_UNUSED(pxActor); + PX_UNUSED(bvhStructure); + size_t ptrOffset; + if (TAdd || TSimRunning || TIsNonSimObject) + { + // Np buffers are still valid when the object gets removed while the sim is running. + // Furthermore, for non-simulation objects, there exists no shape buffer in the simulation controller + // and we need to fetch from Np all the time. + + ptrOffset = shapePtrOffset - Scb::Shape::getScOffset(); + + if (TIsDynamic) + nbShapes = NpRigidDynamicGetShapes(dynamicObject, shapes); + else + nbShapes = NpRigidStaticGetShapes(staticObject, shapes); + } + + if ((!TSimRunning) && (!TIsNonSimObject)) + { + if (TAdd) + { + if (TIsDynamic) + { + const bool isCompound = bvhStructure ? true : false; + s.addBody(dynamicObject.getScBody(), shapes, nbShapes, shapePtrOffset, uninflatedBounds, isCompound); + } + else + s.addStatic(staticObject.getScStatic(), shapes, nbShapes, shapePtrOffset, uninflatedBounds); + } + else + { + ptrOffset = -Scb::Shape::getScOffset(); + + if (TIsDynamic) + s.removeBody(dynamicObject.getScBody(), scShapes, wakeOnLostTouch); + else + s.removeStatic(staticObject.getScStatic(), scShapes, wakeOnLostTouch); + + shapes = reinterpret_cast(const_cast(scShapes.begin())); + nbShapes = scShapes.size(); + } + } + + Scb::Scene* scbScene = rigidObject.getScbScene(); + Scb::Scene* shapeScenePtr = scbScene; + Scb::ControlState::Enum controlState = rigidObject.getControlState(); + + if (!TSimRunning) + { + // hacky: in the non-buffered case the rigid objects might not have been updated properly at this point, so it's done explicitly. + + if (TAdd) + { + PX_ASSERT(shapeScenePtr == scbScene); + controlState = Scb::ControlState::eIN_SCENE; + } + else + { + shapeScenePtr = NULL; + controlState = Scb::ControlState::eNOT_IN_SCENE; + } + } + + for(PxU32 i=0; i < nbShapes; i++) + { + Scb::Shape& scbShape = *Ps::pointerOffset(shapes[i], ptrdiff_t(ptrOffset)); + + if (!TSimRunning) + { + PX_ASSERT(pxActor); + PX_ASSERT(scbScene); + + if (TAdd) + { + scbShape.setControlStateIfExclusive(shapeScenePtr, controlState); + + if (!TIsNonSimObject) + NpShapeIncRefCount(scbShape); // simulation increases the refcount to avoid that shapes get destroyed while the sim is running + +#if PX_SUPPORT_PVD + scbScene->getScenePvdClient().createPvdInstance(&scbShape, *pxActor); +#endif + } + else + { + scbShape.checkUpdateOnRemove(scbScene); + +#if PX_SUPPORT_PVD + scbScene->getScenePvdClient().releasePvdInstance(&scbShape, *pxActor); +#endif + scbShape.setControlStateIfExclusive(shapeScenePtr, controlState); + + if (!TIsNonSimObject) + NpShapeDecRefCount(scbShape); // see comment in the "TAdd" section above + } + } + else + scbShape.setControlStateIfExclusive(shapeScenePtr, controlState); + } +} diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbScene.h b/src/PhysX/physx/source/physx/src/buffering/ScbScene.h new file mode 100644 index 000000000..af3934ca7 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbScene.h @@ -0,0 +1,753 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_SCENE +#define PX_PHYSICS_SCB_SCENE + +#include "ScScene.h" + +#include "ScbSceneBuffer.h" +#include "ScbType.h" +#include "PsFoundation.h" +#include "PsMutex.h" +#include "PsHashSet.h" + +#if PX_SUPPORT_PVD +#include "PxPhysics.h" +#include "ScbScenePvdClient.h" +#endif + +namespace physx +{ + +class NpMaterial; + +namespace Sc +{ + class BodyDesc; +} + +namespace Gu +{ + class BVHStructure; +} + +namespace Scb +{ + class Base; + class RigidObject; + class RigidStatic; + class Body; + class Actor; + class Shape; + class Constraint; + class Material; + class Articulation; + class ArticulationJoint; + class Aggregate; + + struct ShapeBuffer; + + /** + \brief Helper class to track inserted/removed/updated buffering objects. + */ + class ObjectTracker + { + public: + ObjectTracker() {} + + /** + \brief An object has been inserted while the simulation was running -> track it for insertion at sync point + */ + void scheduleForInsert(Base& element); + + /** + \brief An object has been removed while the simulation was running -> track it for removal at sync point + */ + void scheduleForRemove(Base& element); + + /** + \brief An object has been changed while the simulation was running -> track it for update at sync point + */ + void scheduleForUpdate(Base& element); + + /** + \brief Get the list of dirty objects that require processing at a sync point. + */ + Base*const * getBuffered() { return mBuffered.getEntries(); } + + /** + \brief Get number of dirty objects that require processing at a sync point. + */ + PxU32 getBufferedCount() const { return mBuffered.size(); } + + /** + \brief Cleanup dirty objects after sync point. + + \li Transition pending insertion objects from eINSERT_PENDING to eIN_SCENE. + \li Transition pending removal objects from eREMOVE_PENDING to eNOT_IN_SCENE. + \li Destroy objects marked as eIS_RELEASED. + \li Clear dirty list. + */ + void clear(); + + void insert(Base& element); + void remove(Base& element); + + private: + Ps::CoalescedHashSet mBuffered; + }; + + typedef ObjectTracker ShapeManager; + typedef ObjectTracker RigidStaticManager; + typedef ObjectTracker BodyManager; + typedef ObjectTracker ArticulationManager; + typedef ObjectTracker ConstraintManager; + typedef ObjectTracker ArticulationJointManager; + typedef ObjectTracker AggregateManager; + + enum MATERIAL_EVENT + { + MATERIAL_ADD, + MATERIAL_UPDATE, + MATERIAL_REMOVE + }; + + class MaterialEvent + { + public: + PX_FORCE_INLINE MaterialEvent(PxU32 handle, MATERIAL_EVENT type) : mHandle(handle), mType(type) {} + PX_FORCE_INLINE MaterialEvent() {} + + PxU32 mHandle;//handle to the master material table + MATERIAL_EVENT mType; + }; + + class Scene : public Ps::UserAllocated + { + PX_NOCOPY(Scene) + public: + enum BufferFlag + { + BF_GRAVITY = (1 << 0), + BF_BOUNCETHRESHOLDVELOCITY = (1 << 1), + BF_FLAGS = (1 << 2), + BF_DOMINANCE_PAIRS = (1 << 3), + BF_SOLVER_BATCH_SIZE = (1 << 4), + BF_VISUALIZATION = (1 << 5), + BF_CULLING_BOX = (1 << 6) + }; + + public: + Scene(const PxSceneDesc& desc, PxU64 contextID); + ~Scene() {} //use release() plz. + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::Scene interface + //--------------------------------------------------------------------------------- + void release(); + + PxScene* getPxScene(); + + PX_INLINE void setGravity(const PxVec3& gravity); + PX_INLINE PxVec3 getGravity() const; + + PX_INLINE void setBounceThresholdVelocity(const PxReal t); + PX_INLINE PxReal getBounceThresholdVelocity() const; + + PX_INLINE void setFlags(PxSceneFlags flags); + PX_INLINE PxSceneFlags getFlags() const; + + PX_INLINE void setFrictionType(PxFrictionType::Enum type) { mScene.setFrictionType(type); } + PX_INLINE PxFrictionType::Enum getFrictionType() const { return mScene.getFrictionType(); } + + void addActor(Scb::RigidStatic&, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure); + void removeActor(Scb::RigidStatic&, bool wakeOnLostTouch, bool noSim); + + void addActor(Scb::Body&, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure); + void removeActor(Scb::Body&, bool wakeOnLostTouch, bool noSim); + + void addConstraint(Scb::Constraint&); + void removeConstraint(Scb::Constraint&); + + void addArticulation(Scb::Articulation&); + void removeArticulation(Scb::Articulation&); + + void addArticulationJoint(Scb::ArticulationJoint&); + void removeArticulationJoint(Scb::ArticulationJoint&); + + void addAggregate(Scb::Aggregate&); + void removeAggregate(Scb::Aggregate&); + + void addMaterial(const Sc::MaterialCore& mat); + void updateMaterial(const Sc::MaterialCore& mat); + void removeMaterial(const Sc::MaterialCore& mat); + void updateLowLevelMaterial(NpMaterial** masterMaterials); + // These methods are only to be called at fetchResults! + PX_INLINE PxU32 getNumActiveBodies() const { return mScene.getNumActiveBodies(); } + PX_INLINE Sc::BodyCore* const* getActiveBodiesArray() const { return mScene.getActiveBodiesArray(); } + + PX_INLINE PxSimulationEventCallback* getSimulationEventCallback() const; + PX_INLINE void setSimulationEventCallback(PxSimulationEventCallback* callback); + PX_INLINE PxContactModifyCallback* getContactModifyCallback() const; + PX_INLINE void setContactModifyCallback(PxContactModifyCallback* callback); + PX_INLINE PxCCDContactModifyCallback* getCCDContactModifyCallback() const; + PX_INLINE void setCCDContactModifyCallback(PxCCDContactModifyCallback* callback); + PX_INLINE PxU32 getCCDMaxPasses() const; + PX_INLINE void setCCDMaxPasses(PxU32 ccdMaxPasses); + PX_INLINE void setBroadPhaseCallback(PxBroadPhaseCallback* callback); + PX_INLINE PxBroadPhaseCallback* getBroadPhaseCallback() const; + PxBroadPhaseType::Enum getBroadPhaseType() const; + bool getBroadPhaseCaps(PxBroadPhaseCaps& caps) const; + PxU32 getNbBroadPhaseRegions() const; + PxU32 getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + PxU32 addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion); + bool removeBroadPhaseRegion(PxU32 handle); + + // Collision filtering + PX_INLINE void setFilterShaderData(const void* data, PxU32 dataSize); + PX_INLINE const void* getFilterShaderData() const; + PX_INLINE PxU32 getFilterShaderDataSize() const; + PX_INLINE PxSimulationFilterShader getFilterShader() const; + PX_INLINE PxSimulationFilterCallback* getFilterCallback() const; + + // Groups + PX_INLINE void setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance); + PX_INLINE PxDominanceGroupPair getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const; + + PX_INLINE void setSolverBatchSize(PxU32 solverBatchSize); + PX_INLINE PxU32 getSolverBatchSize() const; + + PX_INLINE void simulate(PxReal timeStep, PxBaseTask* continuation) { mScene.simulate(timeStep, continuation); } + PX_INLINE void collide(PxReal timeStep, PxBaseTask* continuation) { mScene.collide(timeStep, continuation); } + PX_INLINE void advance(PxReal timeStep, PxBaseTask* continuation) { mScene.advance(timeStep, continuation); } + PX_INLINE void endSimulation() { mScene.endSimulation(); } + + PX_INLINE void flush(bool sendPendingReports); + PX_INLINE void fireBrokenConstraintCallbacks() { mScene.fireBrokenConstraintCallbacks(); } + PX_INLINE void fireTriggerCallbacks() { mScene.fireTriggerCallbacks(); } + PX_INLINE void fireQueuedContactCallbacks() { mScene.fireQueuedContactCallbacks(false); } + PX_INLINE const Ps::Array& + getQueuedContactPairHeaders() { return mScene.getQueuedContactPairHeaders(); } + PX_FORCE_INLINE void postCallbacksPreSync() { mScene.postCallbacksPreSync(); } //cleanup tasks after the pre-sync callbacks have fired + + PX_INLINE void fireCallBacksPostSync() { mScene.fireCallbacksPostSync(); } //callbacks that are fired on the core side, after the buffers get synced + PX_INLINE void postReportsCleanup(); + + PX_INLINE const PxSceneLimits& getLimits() const { return mScene.getLimits(); } + PX_INLINE void setLimits(const PxSceneLimits& limits) { mScene.setLimits(limits); } + + PX_INLINE void getStats(PxSimulationStatistics& stats) const; + + PX_INLINE void buildActiveActors(); //build the list of active actors + PX_INLINE void buildActiveAndFrozenActors(); //build the list of active and frozen actors + + PX_INLINE PxActor** getActiveActors(PxU32& nbActorsOut); + PX_INLINE void setActiveActors(PxActor** actors, PxU32 nbActors); + PX_INLINE PxActor** getFrozenActors(PxU32& nbActorsOut); + + + PX_INLINE PxClientID createClient(); + + PX_INLINE void setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value); + PX_INLINE PxReal getVisualizationParameter(PxVisualizationParameter::Enum param) const; + + PX_INLINE void setVisualizationCullingBox(const PxBounds3& box); + PX_INLINE const PxBounds3& getVisualizationCullingBox() const; + + void shiftOrigin(const PxVec3& shift); + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + public: + void syncWriteThroughProperties(); + void syncEntireScene(); + void processPendingRemove(); + + PX_FORCE_INLINE PxU16* allocShapeMaterialBuffer(PxU32 count, PxU32& startIdx) { return allocArrayBuffer(mShapeMaterialBuffer, count, startIdx); } + PX_FORCE_INLINE const PxU16* getShapeMaterialBuffer(PxU32 startIdx) const { return &mShapeMaterialBuffer[startIdx]; } + PX_FORCE_INLINE Scb::Shape** allocShapeBuffer(PxU32 count, PxU32& startIdx) { return allocArrayBuffer(mShapePtrBuffer, count, startIdx); } + PX_FORCE_INLINE Scb::Shape** getShapeBuffer(PxU32 startIdx) { return &mShapePtrBuffer[startIdx]; } + PX_FORCE_INLINE Scb::Actor** allocActorBuffer(PxU32 count, PxU32& startIdx) { return allocArrayBuffer(mActorPtrBuffer, count, startIdx); } + PX_FORCE_INLINE Scb::Actor** getActorBuffer(PxU32 startIdx) { return &mActorPtrBuffer[startIdx]; } + + void scheduleForUpdate(Scb::Base& object); + PxU8* getStream(ScbType::Enum type); + + PX_FORCE_INLINE void removeShapeFromPendingUpdateList(Scb::Base& shape) { mShapeManager.remove(shape); } + + PX_FORCE_INLINE const Sc::Scene& getScScene() const { return mScene; } + PX_FORCE_INLINE Sc::Scene& getScScene() { return mScene; } + PX_FORCE_INLINE void prepareOutOfBoundsCallbacks() { mScene.prepareOutOfBoundsCallbacks(); } + + private: + void syncState(); + PX_FORCE_INLINE Ps::IntBool isBuffered(BufferFlag f) const { return Ps::IntBool(mBufferFlags& f); } + PX_FORCE_INLINE void markUpdated(BufferFlag f) { mBufferFlags |= f; } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + public: + + PX_FORCE_INLINE bool isPhysicsBuffering() const { return mIsBuffering; } + PX_FORCE_INLINE void setPhysicsBuffering(bool buffering) { mIsBuffering = buffering; } + + PX_FORCE_INLINE Sc::SimulationStage::Enum getSimulationStage() const { return mScene.getSimulationStage(); } + PX_FORCE_INLINE void setSimulationStage(Sc::SimulationStage::Enum stage) { mScene.setSimulationStage(stage); } + + PX_FORCE_INLINE bool isValid() const { return mScene.isValid(); } + + PX_FORCE_INLINE PxReal getWakeCounterResetValue() const { return mWakeCounterResetValue; } + + void switchRigidToNoSim(Scb::RigidObject&, bool isDynamic); + void switchRigidFromNoSim(Scb::RigidObject&, bool isDynamic); + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mScene); } + +#if PX_SUPPORT_PVD + PX_FORCE_INLINE Vd::ScbScenePvdClient& getScenePvdClient() { return mScenePvdClient; } + PX_FORCE_INLINE const Vd::ScbScenePvdClient& getScenePvdClient() const { return mScenePvdClient; } +#endif + PX_FORCE_INLINE PxU64 getContextId() const { return mScene.getContextId(); } + + private: + void addShapeInternal(Scb::Shape&); + void addShapesInternal(PxU32 nbShapes, PxShape** PX_RESTRICT shapes, size_t scbOffset, PxActor** PX_RESTRICT owners, PxU32 offsetNpToCore, bool isDynamic); + + template T* allocArrayBuffer(Ps::Array& buffer, PxU32 count, PxU32& startIdx); + + private: + + template + PX_FORCE_INLINE void addActorT(T& actor, ObjectTracker& tracker, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure); + + template void add(T& v, ObjectTracker& tracker, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure); + template void remove(T& v, ObjectTracker& tracker, bool wakeOnLostTouch = false); + template void addRigidNoSim(T& v, ObjectTracker& tracker, const Gu::BVHStructure* bvhStructure); + template void removeRigidNoSim(T& v, ObjectTracker& tracker); + template void processSimUpdates(S*const * scObjects, PxU32 nbObjects); + template void processUserUpdates(ObjectTracker& tracker); + template void processRemoves(ObjectTracker& tracker); + template void processShapeRemoves(ObjectTracker& tracker); + + Sc::Scene mScene; + + Ps::Array mSceneMaterialBuffer; + Ps::Mutex mSceneMaterialBufferLock; + + bool mSimulationRunning; + bool mIsBuffering; + + Cm::FlushPool mStream; // Pool for temporarily buffering user changes on objects + ShapeManager mShapeManager; + Ps::Array mShapeMaterialBuffer; // Buffered setMaterial() call might need to track list of materials (for multi material shapes) + Ps::Array mShapePtrBuffer; // List of shape pointers to track buffered calls to resetFiltering(), for example + Ps::Array mActorPtrBuffer; + RigidStaticManager mRigidStaticManager; + BodyManager mBodyManager; + ConstraintManager mConstraintManager; + ArticulationManager mArticulationManager; + ArticulationJointManager mArticulationJointManager; + AggregateManager mAggregateManager; +#if PX_SUPPORT_PVD + Vd::ScbScenePvdClient mScenePvdClient; +#endif + + PX_FORCE_INLINE void updatePvdProperties() + { +#if PX_SUPPORT_PVD + // PT: TODO: shouldn't we test PxPvdInstrumentationFlag::eDEBUG here? + if(mScenePvdClient.isConnected()) + mScenePvdClient.updatePvdProperties(); +#endif + } + + PxReal mWakeCounterResetValue; + + // note: If deletion of rigid objects is done before the sync of the simulation data then we + // might wanna consider having a separate list for deleted rigid objects (for performance + // reasons) + + //--------------------------------------------------------------------------------- + // On demand buffered data (simulation read-only data) + //--------------------------------------------------------------------------------- + Scb::SceneBuffer mBufferedData; + PxU32 mBufferFlags; + }; + +} // namespace Scb + +template +T* Scb::Scene::allocArrayBuffer(Ps::Array& buffer, PxU32 count, PxU32& startIdx) +{ + PxU32 oldSize = buffer.size(); + buffer.resize(oldSize + count); + startIdx = oldSize; + return &buffer[oldSize]; +} + +PX_INLINE void Scb::Scene::setGravity(const PxVec3& gravity) +{ + if(!isPhysicsBuffering()) + { + mScene.setGravity(gravity); + updatePvdProperties(); + } + else + { + mBufferedData.mGravity = gravity; + markUpdated(BF_GRAVITY); + } +} + +PX_INLINE PxVec3 Scb::Scene::getGravity() const +{ + if(isBuffered(BF_GRAVITY)) + return mBufferedData.mGravity; + else + return mScene.getGravity(); +} + +void Scb::Scene::setBounceThresholdVelocity(const PxReal t) +{ + if(!isPhysicsBuffering()) + { + mScene.setBounceThresholdVelocity(t); + updatePvdProperties(); + } + else + { + mBufferedData.mBounceThresholdVelocity = t; + markUpdated(BF_BOUNCETHRESHOLDVELOCITY); + } +} + +PxReal Scb::Scene::getBounceThresholdVelocity() const +{ + if(isBuffered(BF_BOUNCETHRESHOLDVELOCITY)) + return mBufferedData.mBounceThresholdVelocity; + else + return mScene.getBounceThresholdVelocity(); +} + +PX_INLINE void Scb::Scene::setFlags(PxSceneFlags flags) +{ + if(!isPhysicsBuffering()) + { + mScene.setPublicFlags(flags); + const bool pcm = (flags & PxSceneFlag::eENABLE_PCM); + mScene.setPCM(pcm); + const bool contactCache = !(flags & PxSceneFlag::eDISABLE_CONTACT_CACHE); + mScene.setContactCache(contactCache); + updatePvdProperties(); + } + else + { + mBufferedData.mFlags = flags; + markUpdated(BF_FLAGS); + } +} + +PX_INLINE PxSceneFlags Scb::Scene::getFlags() const +{ + if(isBuffered(BF_FLAGS)) + return mBufferedData.mFlags; + else + return mScene.getPublicFlags(); +} + +/////////////////////////////////////////////////////////////////////////////// + +PX_INLINE PxSimulationEventCallback* Scb::Scene::getSimulationEventCallback() const +{ + return mScene.getSimulationEventCallback(); +} + +PX_INLINE void Scb::Scene::setSimulationEventCallback(PxSimulationEventCallback* callback) +{ + if(!isPhysicsBuffering()) + mScene.setSimulationEventCallback(callback); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::setSimulationEventCallback() not allowed while simulation is running. Call will be ignored."); +} + +PX_INLINE PxContactModifyCallback* Scb::Scene::getContactModifyCallback() const +{ + return mScene.getContactModifyCallback(); +} + +PX_INLINE void Scb::Scene::setContactModifyCallback(PxContactModifyCallback* callback) +{ + if(!isPhysicsBuffering()) + mScene.setContactModifyCallback(callback); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::setContactModifyCallback() not allowed while simulation is running. Call will be ignored."); +} + +PX_INLINE PxCCDContactModifyCallback* Scb::Scene::getCCDContactModifyCallback() const +{ + return mScene.getCCDContactModifyCallback(); +} + +PX_INLINE void Scb::Scene::setCCDContactModifyCallback(PxCCDContactModifyCallback* callback) +{ + if(!isPhysicsBuffering()) + mScene.setCCDContactModifyCallback(callback); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::setCCDContactModifyCallback() not allowed while simulation is running. Call will be ignored."); +} + +PX_INLINE PxU32 Scb::Scene::getCCDMaxPasses() const +{ + return mScene.getCCDMaxPasses(); +} + +PX_INLINE void Scb::Scene::setCCDMaxPasses(PxU32 ccdMaxPasses) +{ + if(!isPhysicsBuffering()) + mScene.setCCDMaxPasses(ccdMaxPasses); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::setCCDMaxPasses() not allowed while simulation is running. Call will be ignored."); +} + +PX_INLINE PxBroadPhaseCallback* Scb::Scene::getBroadPhaseCallback() const +{ + return mScene.getBroadPhaseCallback(); +} + +PX_INLINE void Scb::Scene::setBroadPhaseCallback(PxBroadPhaseCallback* callback) +{ + if(!isPhysicsBuffering()) + mScene.setBroadPhaseCallback(callback); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::setBroadPhaseCallback() not allowed while simulation is running. Call will be ignored."); +} + +/////////////////////////////////////////////////////////////////////////////// + +PX_INLINE void Scb::Scene::setFilterShaderData(const void* data, PxU32 dataSize) +{ + if(!isPhysicsBuffering()) + mScene.setFilterShaderData(data, dataSize); + else + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::setFilterShaderData() not allowed while simulation is running. Call will be ignored."); +} + +PX_INLINE const void* Scb::Scene::getFilterShaderData() const +{ + return mScene.getFilterShaderDataFast(); +} + +PX_INLINE PxU32 Scb::Scene::getFilterShaderDataSize() const +{ + return mScene.getFilterShaderDataSizeFast(); +} + +PX_INLINE PxSimulationFilterShader Scb::Scene::getFilterShader() const +{ + return mScene.getFilterShaderFast(); +} + +PX_INLINE PxSimulationFilterCallback* Scb::Scene::getFilterCallback() const +{ + return mScene.getFilterCallbackFast(); +} + +PX_INLINE void Scb::Scene::flush(bool sendPendingReports) +{ + PX_ASSERT(!isPhysicsBuffering()); + + mShapeMaterialBuffer.reset(); + mShapePtrBuffer.reset(); + mActorPtrBuffer.reset(); + + //!!! TODO: Clear all buffers used for double buffering changes (see ObjectTracker::mBufferPool) + + mScene.flush(sendPendingReports); +} + +PX_INLINE void Scb::Scene::postReportsCleanup() +{ + PX_ASSERT(!isPhysicsBuffering()); + mScene.postReportsCleanup(); +} + +PX_INLINE void Scb::Scene::setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance) +{ + if(!isPhysicsBuffering()) + { + mScene.setDominanceGroupPair(group1, group2, dominance); + updatePvdProperties(); + } + else + { + mBufferedData.setDominancePair(group1, group2, dominance); + markUpdated(BF_DOMINANCE_PAIRS); + } +} + +PX_INLINE PxDominanceGroupPair Scb::Scene::getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const +{ + if(isBuffered(BF_DOMINANCE_PAIRS)) + { + PxDominanceGroupPair dominance(0, 0); + if(mBufferedData.getDominancePair(group1, group2, dominance)) + return dominance; + } + + return mScene.getDominanceGroupPair(group1, group2); +} + +PX_INLINE void Scb::Scene::setSolverBatchSize(PxU32 solverBatchSize) +{ + if(!isPhysicsBuffering()) + { + mScene.setSolverBatchSize(solverBatchSize); + updatePvdProperties(); + } + else + { + mBufferedData.mSolverBatchSize = solverBatchSize; + markUpdated(BF_SOLVER_BATCH_SIZE); + } +} + +PX_INLINE PxU32 Scb::Scene::getSolverBatchSize() const +{ + if(isBuffered(BF_SOLVER_BATCH_SIZE)) + return mBufferedData.mSolverBatchSize; + else + return mScene.getSolverBatchSize(); +} + +PX_INLINE void Scb::Scene::getStats(PxSimulationStatistics& stats) const +{ + PX_ASSERT(!isPhysicsBuffering()); + + mScene.getStats(stats); +} + +PX_INLINE void Scb::Scene::buildActiveActors() +{ + PX_ASSERT(!isPhysicsBuffering()); + + mScene.buildActiveActors(); +} + +PX_INLINE void Scb::Scene::buildActiveAndFrozenActors() +{ + PX_ASSERT(!isPhysicsBuffering()); + + mScene.buildActiveAndFrozenActors(); +} + +PX_INLINE PxActor** Scb::Scene::getActiveActors(PxU32& nbActorsOut) +{ + if(!isPhysicsBuffering()) + return mScene.getActiveActors(nbActorsOut); + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::getActiveActors() not allowed while simulation is running. Call will be ignored."); + nbActorsOut = 0; + return NULL; + } +} + +PX_INLINE void Scb::Scene::setActiveActors(PxActor** actors, PxU32 nbActors) +{ + mScene.setActiveActors(actors, nbActors); +} + +PX_INLINE PxActor** Scb::Scene::getFrozenActors(PxU32& nbActorsOut) +{ + if(!isPhysicsBuffering()) + return mScene.getFrozenActors(nbActorsOut); + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::getFrozenActors() not allowed while simulation is running. Call will be ignored."); + nbActorsOut = 0; + return NULL; + } +} + +PX_INLINE PxClientID Scb::Scene::createClient() +{ + if(!isPhysicsBuffering()) + return mScene.createClient(); + else + return PxClientID(mScene.getNbClients() + mBufferedData.mNumClientsCreated++); +} + +PX_INLINE void Scb::Scene::setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) +{ + if(!isPhysicsBuffering()) + mScene.setVisualizationParameter(param, value); + else + { + PX_ASSERT(param < PxVisualizationParameter::eNUM_VALUES); + mBufferedData.mVisualizationParamChanged[param] = 1; + mBufferedData.mVisualizationParam[param] = value; + markUpdated(BF_VISUALIZATION); + } +} + +PX_INLINE PxReal Scb::Scene::getVisualizationParameter(PxVisualizationParameter::Enum param) const +{ + PX_ASSERT(param < PxVisualizationParameter::eNUM_VALUES); + + if(isBuffered(BF_VISUALIZATION) && mBufferedData.mVisualizationParamChanged[param]) + return mBufferedData.mVisualizationParam[param]; + else + return mScene.getVisualizationParameter(param); +} + +PX_INLINE void Scb::Scene::setVisualizationCullingBox(const PxBounds3& box) +{ + if(!isPhysicsBuffering()) + mScene.setVisualizationCullingBox(box); + else + { + mBufferedData.mVisualizationCullingBox = box; + markUpdated(BF_CULLING_BOX); + } +} + +PX_INLINE const PxBounds3& Scb::Scene::getVisualizationCullingBox() const +{ + if(isBuffered(BF_CULLING_BOX)) + return mBufferedData.mVisualizationCullingBox; + else + return mScene.getVisualizationCullingBox(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h b/src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h new file mode 100644 index 000000000..1fa8ad9d0 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h @@ -0,0 +1,153 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCB_SCENE_BUFFER +#define PX_PHYSICS_SCB_SCENE_BUFFER + +#include "CmPhysXCommon.h" + +#include "ScScene.h" + +namespace physx +{ +namespace Scb +{ + +struct SceneBuffer +{ +public: + static const PxU32 sMaxNbDominanceGroups = 32; + + PX_INLINE SceneBuffer(); + + PX_INLINE void clearDominanceBuffer(); + PX_INLINE void setDominancePair(PxU32 group1, PxU32 group2, const PxDominanceGroupPair& dominance); + PX_INLINE bool getDominancePair(PxU32 group1, PxU32 group2, PxDominanceGroupPair& dominance) const; + PX_INLINE void syncDominancePairs(Sc::Scene& scene); + + PX_INLINE void clearVisualizationParams(); + + PxReal mVisualizationParam[PxVisualizationParameter::eNUM_VALUES]; + PxU8 mVisualizationParamChanged[PxVisualizationParameter::eNUM_VALUES]; + PxBounds3 mVisualizationCullingBox; +private: + PxU32 mDominancePairFlag[sMaxNbDominanceGroups - 1]; + PxU32 mDominancePairValues[sMaxNbDominanceGroups]; +public: + PxVec3 mGravity; + PxReal mBounceThresholdVelocity; + PxSceneFlags mFlags; + PxU32 mSolverBatchSize; + PxU32 mNumClientsCreated; +}; + +PX_INLINE SceneBuffer::SceneBuffer() : + mNumClientsCreated (0) +{ + clearDominanceBuffer(); + clearVisualizationParams(); +} + +PX_FORCE_INLINE void SceneBuffer::clearDominanceBuffer() +{ + PxMemZero(&mDominancePairFlag, (sMaxNbDominanceGroups - 1) * sizeof(PxU32)); +} + +PX_FORCE_INLINE void SceneBuffer::clearVisualizationParams() +{ + PxMemZero(mVisualizationParamChanged, PxVisualizationParameter::eNUM_VALUES * sizeof(PxU8)); +} + +PX_INLINE void SceneBuffer::setDominancePair(PxU32 group1, PxU32 group2, const PxDominanceGroupPair& dominance) +{ + PX_ASSERT(group1 != group2); + PX_ASSERT(group1 < sMaxNbDominanceGroups); + PX_ASSERT(group2 < sMaxNbDominanceGroups); + + if(group1 < group2) + mDominancePairFlag[group1] |= (1 << group2); + else + mDominancePairFlag[group2] |= (1 << group1); + + if(dominance.dominance0 != 0.0f) + mDominancePairValues[group1] |= (1 << group2); + else + mDominancePairValues[group1] &= ~(1 << group2); + + if(dominance.dominance1 != 0.0f) + mDominancePairValues[group2] |= (1 << group1); + else + mDominancePairValues[group2] &= ~(1 << group1); +} + +PX_INLINE bool SceneBuffer::getDominancePair(PxU32 group1, PxU32 group2, PxDominanceGroupPair& dominance) const +{ + PX_ASSERT(group1 != group2); + PX_ASSERT(group1 < sMaxNbDominanceGroups); + PX_ASSERT(group2 < sMaxNbDominanceGroups); + + PxU32 isBuffered; + if(group1 < group2) + isBuffered = mDominancePairFlag[group1] & (1 << group2); + else + isBuffered = mDominancePairFlag[group2] & (1 << group1); + + if(!isBuffered) + return false; + + dominance.dominance0 = PxU8((mDominancePairValues[group1] & (1 << group2)) >> group2); + dominance.dominance1 = PxU8((mDominancePairValues[group2] & (1 << group1)) >> group1); + return true; +} + +PX_INLINE void SceneBuffer::syncDominancePairs(Sc::Scene& scene) +{ + for(PxU32 i=0; i<(sMaxNbDominanceGroups - 1); i++) + { + if(mDominancePairFlag[i]) + { + for(PxU32 j=(i+1); j(&(reinterpret_cast(0)->getScConstraint())); + const size_t scbOffset = reinterpret_cast(&(reinterpret_cast(0)->getScbConstraint())); + return reinterpret_cast(reinterpret_cast(scConstraint) - scOffset - scbOffset); + } + + /////////////////////////////////////////////////////////////////////////////// + + PX_FORCE_INLINE static const PxActor* getPxActor(const Scb::Actor* scbActor) + { + const PxActorType::Enum type = scbActor->getActorCore().getActorCoreType(); + if(type == PxActorType::eRIGID_DYNAMIC) + return getNpRigidDynamic(static_cast(scbActor)); + else if(type == PxActorType::eRIGID_STATIC) + return getNpRigidStatic(static_cast(scbActor)); + else if(type == PxActorType::eARTICULATION_LINK) + return getNpArticulationLink(static_cast(scbActor)); + + return NULL; + } + + struct CreateOp + { + CreateOp& operator=(const CreateOp&); + physx::pvdsdk::PvdDataStream& mStream; + PvdMetaDataBinding& mBinding; + PsPvd* mPvd; + PxScene& mScene; + CreateOp(physx::pvdsdk::PvdDataStream& str, PvdMetaDataBinding& bind, PsPvd* pvd, PxScene& scene) + : mStream(str), mBinding(bind), mPvd(pvd), mScene(scene) + { + } + template + void operator()(const TDataType& dtype) + { + mBinding.createInstance(mStream, dtype, mScene, PxGetPhysics(), mPvd); + } + void operator()(const PxArticulationLink&) + { + } + }; + + struct UpdateOp + { + UpdateOp& operator=(const UpdateOp&); + physx::pvdsdk::PvdDataStream& mStream; + PvdMetaDataBinding& mBinding; + UpdateOp(physx::pvdsdk::PvdDataStream& str, PvdMetaDataBinding& bind) : mStream(str), mBinding(bind) + { + } + template + void operator()(const TDataType& dtype) + { + mBinding.sendAllProperties(mStream, dtype); + } + }; + + struct DestroyOp + { + DestroyOp& operator=(const DestroyOp&); + physx::pvdsdk::PvdDataStream& mStream; + PvdMetaDataBinding& mBinding; + PxScene& mScene; + DestroyOp(physx::pvdsdk::PvdDataStream& str, PvdMetaDataBinding& bind, PxScene& scene) + : mStream(str), mBinding(bind), mScene(scene) + { + } + template + void operator()(const TDataType& dtype) + { + mBinding.destroyInstance(mStream, dtype, mScene); + } + void operator()(const PxArticulationLink& dtype) + { + mBinding.destroyInstance(mStream, dtype); + } + }; + + template + inline void BodyTypeOperation(const Scb::Body* scbBody, TOperator op) + { + bool isArticulationLink = scbBody->getActorType() == PxActorType::eARTICULATION_LINK; + if(isArticulationLink) + { + const NpArticulationLink* link = getNpArticulationLink(scbBody); + op(*static_cast(link)); + } + else + { + const NpRigidDynamic* npRigidDynamic = getNpRigidDynamic(scbBody); + op(*static_cast(npRigidDynamic)); + } + } + + template + inline void ActorTypeOperation(const PxActor* actor, TOperator op) + { + switch(actor->getType()) + { + case PxActorType::eRIGID_STATIC: + op(*static_cast(actor)); + break; + case PxActorType::eRIGID_DYNAMIC: + op(*static_cast(actor)); + break; + case PxActorType::eARTICULATION_LINK: + op(*static_cast(actor)); + break; + case PxActorType::eACTOR_COUNT: + case PxActorType::eACTOR_FORCE_DWORD: + PX_ASSERT(false); + break; + }; + } + + namespace + { + struct PvdConstraintVisualizer : public PxConstraintVisualizer + { + PX_NOCOPY(PvdConstraintVisualizer) + public: + physx::pvdsdk::PvdUserRenderer& mRenderer; + PvdConstraintVisualizer(const void* id, physx::pvdsdk::PvdUserRenderer& r) : mRenderer(r) + { + mRenderer.setInstanceId(id); + } + virtual void visualizeJointFrames(const PxTransform& parent, const PxTransform& child) + { + mRenderer.visualizeJointFrames(parent, child); + } + + virtual void visualizeLinearLimit(const PxTransform& t0, const PxTransform& t1, PxReal value, bool active) + { + mRenderer.visualizeLinearLimit(t0, t1, PxF32(value), active); + } + + virtual void visualizeAngularLimit(const PxTransform& t0, PxReal lower, PxReal upper, bool active) + { + mRenderer.visualizeAngularLimit(t0, PxF32(lower), PxF32(upper), active); + } + + virtual void visualizeLimitCone(const PxTransform& t, PxReal tanQSwingY, PxReal tanQSwingZ, bool active) + { + mRenderer.visualizeLimitCone(t, PxF32(tanQSwingY), PxF32(tanQSwingZ), active); + } + + virtual void visualizeDoubleCone(const PxTransform& t, PxReal angle, bool active) + { + mRenderer.visualizeDoubleCone(t, PxF32(angle), active); + } + + virtual void visualizeLine( const PxVec3& p0, const PxVec3& p1, PxU32 color) + { + const PvdDebugLine line(p0, p1, color); + mRenderer.drawLines(&line, 1); + } + }; + } + + class SceneRendererClient : public RendererEventClient, public physx::shdfnd::UserAllocated + { + PX_NOCOPY(SceneRendererClient) + public: + SceneRendererClient(PvdUserRenderer* renderer, PxPvd* pvd):mRenderer(renderer) + { + mStream = PvdDataStream::create(pvd); + mStream->createInstance(renderer); + } + + ~SceneRendererClient() + { + mStream->destroyInstance(mRenderer); + mStream->release(); + } + + virtual void handleBufferFlush(const uint8_t* inData, uint32_t inLength) + { + mStream->setPropertyValue(mRenderer, "events", inData, inLength); + } + + private: + + PvdUserRenderer* mRenderer; + PvdDataStream* mStream; + }; + +} // namespace + +ScbScenePvdClient::ScbScenePvdClient(Scb::Scene& scene) : + mPvd (NULL), + mScbScene (scene), + mPvdDataStream (NULL), + mUserRender (NULL), + mRenderClient (NULL), + mIsConnected (false) +{ +} + +ScbScenePvdClient::~ScbScenePvdClient() +{ + if(mPvd) + mPvd->removeClient(this); +} + +void ScbScenePvdClient::updateCamera(const char* name, const PxVec3& origin, const PxVec3& up, const PxVec3& target) +{ + if(mIsConnected) + mPvdDataStream->updateCamera(name, origin, up, target); +} + +void ScbScenePvdClient::drawPoints(const PvdDebugPoint* points, PxU32 count) +{ + if(mUserRender) + mUserRender->drawPoints(points, count); +} + +void ScbScenePvdClient::drawLines(const PvdDebugLine* lines, PxU32 count) +{ + if(mUserRender) + mUserRender->drawLines(lines, count); +} + +void ScbScenePvdClient::drawTriangles(const PvdDebugTriangle* triangles, PxU32 count) +{ + if(mUserRender) + mUserRender->drawTriangles(triangles, count); +} + +void ScbScenePvdClient::drawText(const PvdDebugText& text) +{ + if(mUserRender) + mUserRender->drawText(text); +} + +void ScbScenePvdClient::setScenePvdFlag(PxPvdSceneFlag::Enum flag, bool value) +{ + if(value) + mFlags |= flag; + else + mFlags &= ~flag; +} + +void ScbScenePvdClient::onPvdConnected() +{ + if(mIsConnected || !mPvd) + return; + + mIsConnected = true; + + mPvdDataStream = PvdDataStream::create(mPvd); + + mUserRender = PvdUserRenderer::create(); + mRenderClient = PX_NEW(SceneRendererClient)(mUserRender, mPvd); + mUserRender->setClient(mRenderClient); + + sendEntireScene(); +} + +void ScbScenePvdClient::onPvdDisconnected() +{ + if(!mIsConnected) + return; + mIsConnected = false; + + PX_DELETE(mRenderClient); + mRenderClient = NULL; + mUserRender->release(); + mUserRender = NULL; + mPvdDataStream->release(); + mPvdDataStream = NULL; +} + +void ScbScenePvdClient::updatePvdProperties() +{ + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *mScbScene.getPxScene()); +} + +void ScbScenePvdClient::releasePvdInstance() +{ + if(mPvdDataStream) + { + PxScene* theScene = mScbScene.getPxScene(); + // remove from parent + mPvdDataStream->removeObjectRef(&PxGetPhysics(), "Scenes", theScene); + mPvdDataStream->destroyInstance(theScene); + } +} + +// PT: this is only called once, from "onPvdConnected" +void ScbScenePvdClient::sendEntireScene() +{ + NpScene* npScene = static_cast(mScbScene.getPxScene()); + + if(npScene->getFlagsFast() & PxSceneFlag::eREQUIRE_RW_LOCK) // getFlagsFast() will trigger a warning of lock check + npScene->lockRead(__FILE__, __LINE__); + + PxPhysics& physics = PxGetPhysics(); + { + PxScene* theScene = mScbScene.getPxScene(); + mPvdDataStream->createInstance(theScene); + updatePvdProperties(); + + // Create parent/child relationship. + mPvdDataStream->setPropertyValue(theScene, "Physics", reinterpret_cast(&physics)); + mPvdDataStream->pushBackObjectRef(&physics, "Scenes", theScene); + } + + // materials: + { + PxsMaterialManager& manager = mScbScene.getScScene().getMaterialManager(); + PxsMaterialManagerIterator iter(manager); + PxsMaterialCore* mat; + while(iter.getNextMaterial(mat)) + { + const PxMaterial* theMaterial = mat->getNxMaterial(); + if(mPvd->registerObject(theMaterial)) + mMetaDataBinding.createInstance(*mPvdDataStream, *theMaterial, physics); + }; + } + + if(mPvd->getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG) + { + Ps::Array actorArray; + + // RBs + // static: + { + PxU32 numActors = npScene->getNbActors(PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC); + actorArray.resize(numActors); + npScene->getActors(PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC, actorArray.begin(), + actorArray.size()); + for(PxU32 i = 0; i < numActors; i++) + { + PxActor* pxActor = actorArray[i]; + if(pxActor->is()) + mMetaDataBinding.createInstance(*mPvdDataStream, *static_cast(pxActor), *npScene, physics, mPvd); + else + mMetaDataBinding.createInstance(*mPvdDataStream, *static_cast(pxActor), *npScene, physics, mPvd); + } + } + // articulations & links + { + Ps::Array articulations; + PxU32 numArticulations = npScene->getNbArticulations(); + articulations.resize(numArticulations); + npScene->getArticulations(articulations.begin(), articulations.size()); + for(PxU32 i = 0; i < numArticulations; i++) + mMetaDataBinding.createInstance(*mPvdDataStream, *articulations[i], *npScene, physics, mPvd); + } + + // joints + { + Sc::ConstraintCore*const * constraints = mScbScene.getScScene().getConstraints(); + PxU32 nbConstraints = mScbScene.getScScene().getNbConstraints(); + for(PxU32 i = 0; i < nbConstraints; i++) + { + updateConstraint(*constraints[i], PxPvdUpdateType::CREATE_INSTANCE); + updateConstraint(*constraints[i], PxPvdUpdateType::UPDATE_ALL_PROPERTIES); + } + } + } + + if(npScene->getFlagsFast() & PxSceneFlag::eREQUIRE_RW_LOCK) + npScene->unlockRead(); +} + +void ScbScenePvdClient::updateConstraint(const Sc::ConstraintCore& scConstraint, PxU32 updateType) +{ + PxConstraintConnector* conn = scConstraint.getPxConnector(); + if(conn && checkPvdDebugFlag()) + conn->updatePvdProperties(*mPvdDataStream, scConstraint.getPxConstraint(), PxPvdUpdateType::Enum(updateType)); +} + +void ScbScenePvdClient::createPvdInstance(const PxActor* actor) +{ + if(checkPvdDebugFlag()) + ActorTypeOperation(actor, CreateOp(*mPvdDataStream, mMetaDataBinding, mPvd, *mScbScene.getPxScene())); +} + +void ScbScenePvdClient::updatePvdProperties(const PxActor* actor) +{ + if(checkPvdDebugFlag()) + ActorTypeOperation(actor, UpdateOp(*mPvdDataStream, mMetaDataBinding)); +} + +void ScbScenePvdClient::releasePvdInstance(const PxActor* actor) +{ + if(checkPvdDebugFlag()) + ActorTypeOperation(actor, DestroyOp(*mPvdDataStream, mMetaDataBinding, *mScbScene.getPxScene())); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Actor* actor) +{ + // PT: why not UPDATE_PVD_PROPERTIES_CHECK() here? + createPvdInstance(getPxActor(actor)); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Actor* actor) +{ + // PT: why not UPDATE_PVD_PROPERTIES_CHECK() here? + updatePvdProperties(getPxActor(actor)); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::Actor* actor) +{ + // PT: why not UPDATE_PVD_PROPERTIES_CHECK() here? + releasePvdInstance(getPxActor(actor)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Body* body) +{ + if(checkPvdDebugFlag() && body->getActorType() != PxActorType::eARTICULATION_LINK) + BodyTypeOperation(body, CreateOp(*mPvdDataStream, mMetaDataBinding, mPvd, *mScbScene.getPxScene())); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Body* body) +{ + if(checkPvdDebugFlag()) + BodyTypeOperation(body, UpdateOp(*mPvdDataStream, mMetaDataBinding)); +} + +void ScbScenePvdClient::updateKinematicTarget(const Scb::Body* body, const PxTransform& p) +{ + if(checkPvdDebugFlag()) + mPvdDataStream->setPropertyValue(getNpRigidDynamic(body), "KinematicTarget", p); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::RigidStatic* rigidStatic) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.createInstance(*mPvdDataStream, *getNpRigidStatic(rigidStatic), *mScbScene.getPxScene(), PxGetPhysics(), mPvd); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::RigidStatic* rigidStatic) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *getNpRigidStatic(rigidStatic)); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::RigidObject* rigidObject) +{ + releasePvdInstance(getPxActor(rigidObject)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Constraint* constraint) +{ + if(checkPvdDebugFlag()) + updateConstraint(constraint->getScConstraint(), PxPvdUpdateType::CREATE_INSTANCE); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Constraint* constraint) +{ + if(checkPvdDebugFlag()) + updateConstraint(constraint->getScConstraint(), PxPvdUpdateType::UPDATE_ALL_PROPERTIES); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::Constraint* constraint) +{ + const Sc::ConstraintCore& scConstraint = constraint->getScConstraint(); + PxConstraintConnector* conn; + if(checkPvdDebugFlag() && (conn = scConstraint.getPxConnector()) != NULL) + conn->updatePvdProperties(*mPvdDataStream, scConstraint.getPxConstraint(), PxPvdUpdateType::RELEASE_INSTANCE); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Articulation* articulation) +{ + if (checkPvdDebugFlag()) + { + if(articulation->getArticulationType() == PxArticulationBase::eMaximumCoordinate) + mMetaDataBinding.createInstance(*mPvdDataStream, *getNpArticulation(articulation), *mScbScene.getPxScene(), PxGetPhysics(), mPvd); + else + mMetaDataBinding.createInstance(*mPvdDataStream, *getNpArticulationRC(articulation), *mScbScene.getPxScene(), PxGetPhysics(), mPvd); + } +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Articulation* articulation) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *getNpArticulation(articulation)); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::Articulation* articulation) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.destroyInstance(*mPvdDataStream, *getNpArticulation(articulation), *mScbScene.getPxScene()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::ArticulationJoint* articulationJoint) +{ + PX_UNUSED(articulationJoint); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::ArticulationJoint* articulationJoint) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *getNpArticulationJoint(articulationJoint)); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::ArticulationJoint* articulationJoint) +{ + PX_UNUSED(articulationJoint); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Sc::MaterialCore* materialCore) +{ + if(checkPvdDebugFlag()) + { + const PxMaterial* theMaterial = materialCore->getNxMaterial(); + if(mPvd->registerObject(theMaterial)) + mMetaDataBinding.createInstance(*mPvdDataStream, *theMaterial, PxGetPhysics()); + } +} + +void ScbScenePvdClient::updatePvdProperties(const Sc::MaterialCore* materialCore) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *materialCore->getNxMaterial()); +} + +void ScbScenePvdClient::releasePvdInstance(const Sc::MaterialCore* materialCore) +{ + if(checkPvdDebugFlag() && mPvd->unRegisterObject(materialCore->getNxMaterial() ) ) + mMetaDataBinding.destroyInstance(*mPvdDataStream, *materialCore->getNxMaterial(), PxGetPhysics()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Shape* shape, PxActor& owner) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.createPVDInstance", getContextId(mScbScene)); + const PxShape* npShape = getNpShape(shape); + mMetaDataBinding.createInstance(*mPvdDataStream, *npShape, static_cast(owner), PxGetPhysics(), mPvd); + } +} + +static void addShapesToPvd(PxU32 nbShapes, void* const* shapes, const size_t offset, PxActor& pxActor, PsPvd* pvd, PvdDataStream& stream, PvdMetaDataBinding& binding) +{ + PxPhysics& physics = PxGetPhysics(); + for(PxU32 i=0;i(reinterpret_cast(shapes[i]) + offset); + const PxShape* npShape = getNpShape(shape); + binding.createInstance(stream, *npShape, static_cast(pxActor), physics, pvd); + } +} + +void ScbScenePvdClient::addBodyAndShapesToPvd(Scb::Body& b) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.createPVDInstance", getContextId(mScbScene)); + createPvdInstance(&b); + + const size_t offset = NpShapeGetScPtrOffset() - Scb::Shape::getScOffset(); + PxActor& pxActor = *b.getScBody().getPxActor(); + + void* const* shapes; + const PxU32 nbShapes = NpRigidDynamicGetShapes(b, shapes); + addShapesToPvd(nbShapes, shapes, offset, pxActor, mPvd, *mPvdDataStream, mMetaDataBinding); + } +} + +void ScbScenePvdClient::addStaticAndShapesToPvd(Scb::RigidStatic& s) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.createPVDInstance", getContextId(mScbScene)); + createPvdInstance(&s); + + const size_t offset = NpShapeGetScPtrOffset() - Scb::Shape::getScOffset(); + PxActor& pxActor = *s.getScStatic().getPxActor(); + + void* const* shapes; + const PxU32 nbShapes = NpRigidStaticGetShapes(s, shapes); + addShapesToPvd(nbShapes, shapes, offset, pxActor, mPvd, *mPvdDataStream, mMetaDataBinding); + } +} + +void ScbScenePvdClient::updateMaterials(const Scb::Shape* shape) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.updateMaterials(*mPvdDataStream, *getNpShape(const_cast(shape)), mPvd); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Shape* shape) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *getNpShape(const_cast(shape))); +} + +void ScbScenePvdClient::releaseAndRecreateGeometry(const Scb::Shape* shape) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.releaseAndRecreateGeometry(*mPvdDataStream, *getNpShape(const_cast(shape)), + NpPhysics::getInstance(), mPvd); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::Shape* shape, PxActor& owner) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.releasePVDInstance", getContextId(mScbScene)); + + const NpShape* npShape = getNpShape(shape); + mMetaDataBinding.destroyInstance(*mPvdDataStream, *npShape, static_cast(owner)); + + const PxU32 numMaterials = npShape->getNbMaterials(); + PX_ALLOCA(materialPtr, PxMaterial*, numMaterials); + npShape->getMaterials(materialPtr, numMaterials); + + for(PxU32 idx = 0; idx < numMaterials; ++idx) + releasePvdInstance(&(static_cast(materialPtr[idx])->getScMaterial())); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::originShift(PxVec3 shift) +{ + mMetaDataBinding.originShift(*mPvdDataStream, mScbScene.getPxScene(), shift); +} + +void ScbScenePvdClient::frameStart(PxReal simulateElapsedTime) +{ + PX_PROFILE_ZONE("Basic.pvdFrameStart", mScbScene.getContextId()); + + if(!mIsConnected) + return; + + mPvdDataStream->flushPvdCommand(); + mMetaDataBinding.sendBeginFrame(*mPvdDataStream, mScbScene.getPxScene(), simulateElapsedTime); +} + +void ScbScenePvdClient::frameEnd() +{ + PX_PROFILE_ZONE("Basic.pvdFrameEnd", mScbScene.getContextId()); + + if(!mIsConnected) + { + if(mPvd) + mPvd->flush(); // Even if we aren't connected, we may need to flush buffered events. + return; + } + + PxScene* theScene = mScbScene.getPxScene(); + + mMetaDataBinding.sendStats(*mPvdDataStream, theScene); + + // flush our data to the main connection + mPvd->flush(); + + // End the frame *before* we send the dynamic object current data. + // This ensures that contacts end up synced with the rest of the system. + // Note that contacts were sent much earler in NpScene::fetchResults. + mMetaDataBinding.sendEndFrame(*mPvdDataStream, mScbScene.getPxScene()); + + if(mPvd->getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG) + { + PX_PROFILE_ZONE("PVD.sceneUpdate", getContextId(mScbScene)); + + PvdVisualizer* vizualizer = NULL; + const bool visualizeJoints = getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS; + if(visualizeJoints) + vizualizer = this; + + mMetaDataBinding.updateDynamicActorsAndArticulations(*mPvdDataStream, theScene, vizualizer); + } + + // frame end moved to update contacts to have them in the previous frame. +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Aggregate* aggregate) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.createPVDInstance", getContextId(mScbScene)); + const NpAggregate* npAggregate = getNpAggregate(aggregate); + mMetaDataBinding.createInstance(*mPvdDataStream, *npAggregate, *mScbScene.getPxScene()); + } +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Aggregate* aggregate) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *getNpAggregate(aggregate)); +} + +void ScbScenePvdClient::attachAggregateActor(const Scb::Aggregate* aggregate, Scb::Actor* actor) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.attachAggregateActor(*mPvdDataStream, *getNpAggregate(aggregate), *getPxActor(actor)); +} + +void ScbScenePvdClient::detachAggregateActor(const Scb::Aggregate* aggregate, Scb::Actor* actor) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.detachAggregateActor(*mPvdDataStream, *getNpAggregate(aggregate), *getPxActor(actor)); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::Aggregate* aggregate) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.releasePVDInstance", getContextId(mScbScene)); + const NpAggregate* npAggregate = getNpAggregate(aggregate); + mMetaDataBinding.destroyInstance(*mPvdDataStream, *npAggregate, *mScbScene.getPxScene()); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::updateJoints() +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.updateJoints", getContextId(mScbScene)); + + const bool visualizeJoints = getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS; + + Sc::ConstraintCore*const * constraints = mScbScene.getScScene().getConstraints(); + const PxU32 nbConstraints = mScbScene.getScScene().getNbConstraints(); + PxI64 constraintCount = 0; + + for(PxU32 i=0; iisDirty() + ? PxPvdUpdateType::UPDATE_ALL_PROPERTIES + : PxPvdUpdateType::UPDATE_SIM_PROPERTIES; + updateConstraint(*constraint, updateType); + PxConstraintConnector* conn = constraint->getPxConnector(); + // visualization is updated here + { + PxU32 typeId = 0; + void* joint = NULL; + if(conn) + joint = conn->getExternalReference(typeId); + // visualize: + Sc::ConstraintSim* sim = constraint->getSim(); + if(visualizeJoints && sim && sim->getConstantsLL() && joint && constraint->getVisualize()) + { + Sc::BodySim* b0 = sim->getBody(0); + Sc::BodySim* b1 = sim->getBody(1); + PxTransform t0 = b0 ? b0->getBody2World() : PxTransform(PxIdentity); + PxTransform t1 = b1 ? b1->getBody2World() : PxTransform(PxIdentity); + PvdConstraintVisualizer viz(joint, *mUserRender); + (*constraint->getVisualize())(viz, sim->getConstantsLL(), t0, t1, 0xffffFFFF); + } + } + ++constraintCount; + } + + mUserRender->flushRenderEvents(); + } +} + +void ScbScenePvdClient::updateContacts() +{ + if(!checkPvdDebugFlag()) + return; + + PX_PROFILE_ZONE("PVD.updateContacts", getContextId(mScbScene)); + + // if contacts are disabled, send empty array and return + const PxScene* theScene(mScbScene.getPxScene()); + if(!(getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_CONTACTS)) + { + mMetaDataBinding.sendContacts(*mPvdDataStream, *theScene); + return; + } + + PxsContactManagerOutputIterator outputIter; + + Sc::ContactIterator contactIter; + mScbScene.getScScene().initContactsIterator(contactIter, outputIter); + Sc::ContactIterator::Pair* pair; + Sc::Contact* contact; + Ps::Array contacts; + while ((pair = contactIter.getNextPair()) != NULL) + { + while ((contact = pair->getNextContact()) != NULL) + contacts.pushBack(*contact); + } + + mMetaDataBinding.sendContacts(*mPvdDataStream, *theScene, contacts); +} + +void ScbScenePvdClient::updateSceneQueries() +{ + if(checkPvdDebugFlag() && (getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES)) + mMetaDataBinding.sendSceneQueries(*mPvdDataStream, *mScbScene.getPxScene(), mPvd); +} + +void ScbScenePvdClient::setCreateContactReports(bool b) +{ + mScbScene.getScScene().setCreateContactReports(b); +} + +void ScbScenePvdClient::visualize(PxArticulationLink& link) +{ + NpArticulationLink& npLink = static_cast(link); + const void* itemId = npLink.getInboundJoint(); + if(itemId && mUserRender) + { + PvdConstraintVisualizer viz(itemId, *mUserRender); + npLink.visualizeJoint(viz); + } +} + +void ScbScenePvdClient::visualize(const PxRenderBuffer& debugRenderable) +{ + if(mUserRender) + { + // PT: I think the mUserRender object can contain extra data (including things coming from the user), because the various + // draw functions are exposed e.g. in PxPvdSceneClient.h. So I suppose we have to keep the render buffer around regardless + // of the connection flags. Thus I only skip the "drawRenderbuffer" call, for minimal intrusion into this file. + if(checkPvdDebugFlag()) + { + mUserRender->drawRenderbuffer( + reinterpret_cast(debugRenderable.getPoints()), debugRenderable.getNbPoints(), + reinterpret_cast(debugRenderable.getLines()), debugRenderable.getNbLines(), + reinterpret_cast(debugRenderable.getTriangles()), debugRenderable.getNbTriangles()); + } + mUserRender->flushRenderEvents(); + } +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h b/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h new file mode 100644 index 000000000..d84414b2b --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h @@ -0,0 +1,202 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SCB_SCENE_PVD_CLIENT_H +#define SCB_SCENE_PVD_CLIENT_H + +#include "PxPhysXConfig.h" + +#if PX_SUPPORT_PVD + +#include "foundation/PxStrideIterator.h" +#include "pvd/PxPvdTransport.h" + +#include "PxPvdSceneClient.h" +#include "PvdMetaDataPvdBinding.h" + +#include "CmBitMap.h" + +#include "PxPvdClient.h" +#include "PxPvdUserRenderer.h" +#include "PsPvd.h" + +namespace physx +{ +class PxScene; +class PxActor; +class PxShape; +class PxGeometryHolder; +class PxArticulationLink; +class PxRenderBuffer; + +namespace Scb +{ +class Scene; +class Actor; +class Body; +class RigidStatic; +class RigidObject; +class Shape; +class Constraint; +class Articulation; +class ArticulationJoint; +class Aggregate; +class SoftBody; +} + +namespace Sc +{ +class MaterialCore; +class ConstraintCore; +} + +namespace Vd +{ +class ScbScenePvdClient : public PxPvdSceneClient, public PvdClient, public PvdVisualizer +{ + PX_NOCOPY(ScbScenePvdClient) + public: + ScbScenePvdClient(Scb::Scene& scene); + virtual ~ScbScenePvdClient(); + + // PxPvdSceneClient + virtual void setScenePvdFlag(PxPvdSceneFlag::Enum flag, bool value); + virtual void setScenePvdFlags(PxPvdSceneFlags flags) { mFlags = flags; } + virtual PxPvdSceneFlags getScenePvdFlags() const { return mFlags; } + virtual void updateCamera(const char* name, const PxVec3& origin, const PxVec3& up, const PxVec3& target); + virtual void drawPoints(const PvdDebugPoint* points, PxU32 count); + virtual void drawLines(const PvdDebugLine* lines, PxU32 count); + virtual void drawTriangles(const PvdDebugTriangle* triangles, PxU32 count); + virtual void drawText(const PvdDebugText& text); + virtual PvdClient* getClientInternal() { return this; } + //~PxPvdSceneClient + + // pvdClient + virtual PvdDataStream* getDataStream() { return mPvdDataStream; } + virtual PvdMetaDataBinding* getMetaDataBinding() { return &mMetaDataBinding; } + virtual PvdUserRenderer* getUserRender() { return mUserRender; } + virtual bool isConnected() const { return mIsConnected; } + virtual void onPvdConnected(); + virtual void onPvdDisconnected(); + virtual void flush() {} + //~pvdClient + + PX_FORCE_INLINE bool checkPvdDebugFlag() const + { + return mIsConnected && (mPvd->getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG); + } + + PX_FORCE_INLINE PxPvdSceneFlags getScenePvdFlagsFast() const { return mFlags; } + PX_FORCE_INLINE void setPsPvd(PsPvd* pvd) { mPvd = pvd; } + + void frameStart(PxReal simulateElapsedTime); + void frameEnd(); + + void updatePvdProperties(); + void releasePvdInstance(); + + void createPvdInstance (const PxActor* actor); // temporary for deformables and particle systems - sschirm: deformables and particles are gone... + void updatePvdProperties(const PxActor* actor); + void releasePvdInstance (const PxActor* actor); // temporary for deformables and particle systems - sschirm: deformables and particles are gone... + + void createPvdInstance (const Scb::Actor* actor); // temporary for deformables and particle systems - sschirm: deformables and particles are gone... + void updatePvdProperties(const Scb::Actor* actor); + void releasePvdInstance (const Scb::Actor* actor); // temporary for deformables and particle systems - sschirm: deformables and particles are gone... + + void createPvdInstance (const Scb::Body* body); + void updatePvdProperties (const Scb::Body* body); + void updateKinematicTarget (const Scb::Body* body, const PxTransform& p); + + void createPvdInstance (const Scb::RigidStatic* rigidStatic); + void updatePvdProperties (const Scb::RigidStatic* rigidStatic); + + void releasePvdInstance (const Scb::RigidObject* rigidObject); + + void createPvdInstance (const Scb::Constraint* constraint); + void updatePvdProperties(const Scb::Constraint* constraint); + void releasePvdInstance (const Scb::Constraint* constraint); + + void createPvdInstance (const Scb::Articulation* articulation); + void updatePvdProperties(const Scb::Articulation* articulation); + void releasePvdInstance (const Scb::Articulation* articulation); + + void createPvdInstance (const Scb::ArticulationJoint* articulationJoint); + void updatePvdProperties(const Scb::ArticulationJoint* articulationJoint); + void releasePvdInstance (const Scb::ArticulationJoint* articulationJoint); + + void createPvdInstance (const Sc::MaterialCore* materialCore); + void updatePvdProperties(const Sc::MaterialCore* materialCore); + void releasePvdInstance (const Sc::MaterialCore* materialCore); + + void createPvdInstance (const Scb::Shape* shape, PxActor& owner); + void updateMaterials (const Scb::Shape* shape); + void updatePvdProperties (const Scb::Shape* shape); + void releaseAndRecreateGeometry (const Scb::Shape* shape); + void releasePvdInstance (const Scb::Shape* shape, PxActor& owner); + void addBodyAndShapesToPvd (Scb::Body& b); + void addStaticAndShapesToPvd (Scb::RigidStatic& s); + + void createPvdInstance (const Scb::Aggregate* aggregate); + void updatePvdProperties (const Scb::Aggregate* aggregate); + void attachAggregateActor (const Scb::Aggregate* aggregate, Scb::Actor* actor); + void detachAggregateActor (const Scb::Aggregate* aggregate, Scb::Actor* actor); + void releasePvdInstance (const Scb::Aggregate* aggregate); + + void originShift(PxVec3 shift); + void updateJoints(); + void updateContacts(); + void updateSceneQueries(); + + // PvdVisualizer + void visualize(PxArticulationLink& link); + void visualize(const PxRenderBuffer& debugRenderable); + + private: + + void sendEntireScene(); + void updateConstraint(const Sc::ConstraintCore& scConstraint, PxU32 updateType); + void setCreateContactReports(bool b); + + PxPvdSceneFlags mFlags; + PsPvd* mPvd; + Scb::Scene& mScbScene; + + PvdDataStream* mPvdDataStream; + PvdMetaDataBinding mMetaDataBinding; + PvdUserRenderer* mUserRender; + RendererEventClient* mRenderClient; + bool mIsConnected; +}; + +} // pvd + +} // physx +#endif // PX_SUPPORT_PVD + +#endif // SCB_SCENE_PVD_CLIENT_H diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp new file mode 100644 index 000000000..ccae62a06 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp @@ -0,0 +1,144 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScbShape.h" + +using namespace physx; + +bool Scb::Shape::setMaterialsHelper(PxMaterial* const* materials, PxU16 materialCount) +{ + PX_ASSERT(!isBuffering()); + + if(materialCount == 1) + { + const PxU16 materialIndex = Ps::to16((static_cast(materials[0]))->getHandle()); + + mShape.setMaterialIndices(&materialIndex, 1); + } + else + { + PX_ASSERT(materialCount > 1); + + PX_ALLOCA(materialIndices, PxU16, materialCount); + + if(materialIndices) + { + NpMaterial::getMaterialIndices(materials, materialIndices, materialCount); + mShape.setMaterialIndices(materialIndices, materialCount); + } + else + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, + "PxShape::setMaterials() failed. Out of memory. Call will be ignored."); + return false; + } + } + + Scb::Scene* sc = getScbScene(); + if(sc) + sc->getScScene().notifyNphaseOnUpdateShapeMaterial(mShape); + + return true; +} + +void Scb::Shape::syncState() +{ + const PxU32 flags = getBufferFlags(); + if(flags) + { + const PxShapeFlags oldShapeFlags = mShape.getFlags(); + + const Scb::ShapeBuffer& buffer = *getBufferedData(); + + Scb::Scene* scbScene = getScbScene(); // PT: can be NULL. See e.g. RbShapeTest.ReleaseShapeWithPendingUpdate UT. + + if(flags & Buf::BF_Geometry) + { + if(scbScene) + scbScene->getScScene().unregisterShapeFromNphase(mShape); + + mShape.setGeometry(buffer.geometry.getGeometry()); + + if(scbScene) + scbScene->getScScene().registerShapeInNphase(mShape); + +#if PX_SUPPORT_PVD + if(getControlState() == ControlState::eIN_SCENE) + { + PX_ASSERT(scbScene); + scbScene->getScenePvdClient().releaseAndRecreateGeometry(this); + } +#endif + } + + if(flags & Buf::BF_Material) + { + // PT: not sure if this is correct. Added the check for PX-800 but "getMaterialBuffer" doesn't always need the scene pointer... + if(scbScene) + { + const PxU16* materialIndices = getMaterialBuffer(*scbScene, buffer); + mShape.setMaterialIndices(materialIndices, buffer.materialCount); + scbScene->getScScene().notifyNphaseOnUpdateShapeMaterial(mShape); + } + UPDATE_PVD_MATERIALS() + // TODO: So far we did not bother to fail gracefully in the case of running out of memory. If that should change then this + // method is somewhat problematic. The material ref counters have been adjusted at the time when the public API was called. + // Could be that one of the old materials was deleted afterwards. The problem now is what to do if this method fails? + // We can't adjust the material ref counts any longer since some of the old materials might have been deleted. + // One solution could be that this class allocates an array of material pointers when the buffered method is called. + // This array is then passed into the core object and is used by the core object, i.e., the core object does not allocate the + // buffer itself. + } + +#ifdef USE_NEW_SYSTEM + syncShape2Actor(); + syncSimulationFilterData(); + syncContactOffset(); + syncRestOffset(); + syncFlags(); + syncTorsionalPatchRadius(); + syncMinTorsionalPatchRadius(); +#else + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); +#endif + + Sc::RigidCore* scRigidCore = NpShapeGetScRigidObjectFromScbSLOW(*this); + + if(scRigidCore) // may be NULL for exclusive shapes because of pending shape updates after buffered release of actor. + scRigidCore->onShapeChange(mShape, Sc::ShapeChangeNotifyFlags(flags), oldShapeFlags, true); + } + + postSyncState(); +} diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbShape.h b/src/PhysX/physx/source/physx/src/buffering/ScbShape.h new file mode 100644 index 000000000..1652c477e --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbShape.h @@ -0,0 +1,481 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_SHAPE +#define PX_PHYSICS_SCB_SHAPE + +#include "NpMaterial.h" +#include "NpPhysics.h" +#include "ScbNpDeps.h" +#include "ScShapeCore.h" +#include "ScRigidCore.h" + +#include "PsUtilities.h" + +// PX_SERIALIZATION +#include "PxSerialFramework.h" +//~PX_SERIALIZATION + +#include "ScbDefs.h" + +namespace physx +{ +#if PX_SUPPORT_PVD + #define UPDATE_PVD_MATERIALS() \ + if(getControlState() == ControlState::eIN_SCENE) \ + getScbScene()->getScenePvdClient().updateMaterials(this); +#else + #define UPDATE_PVD_MATERIALS() {} +#endif + +namespace Scb +{ + +class RigidObject; + +struct ShapeBuffer +{ +#ifndef USE_NEW_SYSTEM + template struct Fns {}; // TODO: make the base class traits visible + typedef Sc::ShapeCore Core; + typedef ShapeBuffer Buf; +#endif + + ShapeBuffer() : materialBufferIndex(0), materialCount(0) {} + +#ifdef USE_NEW_SYSTEM + PX_ALIGN(16, PxTransform) mShape2Actor; + PxFilterData mSimulationFilterData; + PxReal mContactOffset; + PxReal mRestOffset; + PxShapeFlags mFlags; + PxReal mTorsionalPatchRadius; + PxReal mMinTorsionalPatchRadius; +#else + // PT: I think we start with "2" (instead of 0) because the two first bits are reserved + // below, for geometry & materials. + SCB_REGULAR_ATTRIBUTE_ALIGNED(2, PxTransform, Shape2Actor, 16) +// SCB_REGULAR_ATTRIBUTE(2, PxTransform, Shape2Actor) + SCB_REGULAR_ATTRIBUTE(3, PxFilterData, SimulationFilterData) + SCB_REGULAR_ATTRIBUTE(4, PxReal, ContactOffset) + SCB_REGULAR_ATTRIBUTE(5, PxReal, RestOffset) + SCB_REGULAR_ATTRIBUTE(6, PxShapeFlags, Flags) + SCB_REGULAR_ATTRIBUTE(7, PxReal, TorsionalPatchRadius) + SCB_REGULAR_ATTRIBUTE(8, PxReal, MinTorsionalPatchRadius) +#endif + Gu::GeometryUnion geometry; + + union + { + PxU16 materialIndex; // for single material shapes + PxU32 materialBufferIndex; // for multi material shapes + }; + PxU16 materialCount; + + enum + { + BF_Geometry = 1<<0, + BF_Material = 1<<1 + }; +}; + +class Shape : public Base +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef Sc::ShapeCore Core; + typedef ShapeBuffer Buf; +public: +// PX_SERIALIZATION + Shape(const PxEMPTY) : Base(PxEmpty), mShape(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PX_INLINE Shape(const PxGeometry& geometry, + PxShapeFlags shapeFlags, + const PxU16* materialIndices, + PxU16 materialCount, + bool isExclusive); + + PX_INLINE PxGeometryType::Enum getGeometryType() const; + + PX_INLINE const PxGeometry& getGeometry() const; + PX_INLINE const Gu::GeometryUnion&getGeometryUnion() const; + PX_INLINE Scb::ShapeBuffer* setGeometry(const PxGeometry& geom); + + PX_INLINE PxU16 getNbMaterials() const; + PX_INLINE PxMaterial* getMaterial(PxU32 index) const; + PX_INLINE PxU32 getMaterials(PxMaterial** buffer, PxU32 bufferSize, PxU32 startIndex=0) const; + PX_INLINE bool setMaterials(PxMaterial*const* materials, PxU16 materialCount); + +#ifdef USE_NEW_SYSTEM + template + void setValueT(ValueType value, CoreType& core, SCBType& scb) + { + if(!scb.isBuffering()) + { + const PxShapeFlags oldShapeFlags = core.getFlags(); + AccessType::setCore(core, value); + + // shared shapes return NULL. But shared shapes aren't mutable when attached to an actor, so no notification needed. + Sc::RigidCore* rigidCore = NpShapeGetScRigidObjectFromScbSLOW(scb); + if(rigidCore && scb.getControlState() != ControlState::eINSERT_PENDING) + rigidCore->onShapeChange(core, Sc::ShapeChangeNotifyFlags(1<getScenePvdClient().updatePvdProperties(&scb); +#endif + } + else + { + AccessType::setBuffered(scb, value); + scb.markUpdated(1<(); } + PX_INLINE void setShape2Actor(const PxTransform& v) { write(v); } + + PX_INLINE PxFilterData getSimulationFilterData() const { return read(); } + PX_INLINE void setSimulationFilterData(const PxFilterData& v) { write(v); } + + PX_INLINE PxReal getContactOffset() const { return read(); } + PX_INLINE void setContactOffset(PxReal v) { write(v); } + + PX_INLINE PxReal getRestOffset() const { return read(); } + PX_INLINE void setRestOffset(PxReal v) { write(v); } + + PX_INLINE PxReal getTorsionalPatchRadius() const { return read(); } + PX_INLINE void setTorsionalPatchRadius(PxReal v) { write(v); } + + PX_INLINE PxReal getMinTorsionalPatchRadius() const { return read(); } + PX_INLINE void setMinTorsionalPatchRadius(PxReal v) { write(v); } + + PX_INLINE PxShapeFlags getFlags() const { return read(); } + PX_INLINE void setFlags(PxShapeFlags v) { write(v); } +#endif + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + void syncState(); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE const PxU16* getScMaterialIndices() const { return mShape.getMaterialIndices(); } // Only use if you know what you're doing! + + PX_FORCE_INLINE Sc::ShapeCore& getScShape() { return mShape; } // Only use if you know what you're doing! + PX_FORCE_INLINE const Sc::ShapeCore& getScShape() const { return mShape; } + + PX_FORCE_INLINE bool isExclusive() const { return getScbType() == ScbType::eSHAPE_EXCLUSIVE; } + PX_FORCE_INLINE void setControlStateIfExclusive(Scene* s, ControlState::Enum cs); // for exclusive shapes + + template PX_FORCE_INLINE void checkUpdateOnRemove(Scene* s); + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mShape); } + +private: + bool setMaterialsHelper(PxMaterial* const* materials, PxU16 materialCount); + + Sc::ShapeCore mShape; + + PX_FORCE_INLINE const Scb::ShapeBuffer* getBufferedData() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Scb::ShapeBuffer* getBufferedData() { return reinterpret_cast(getStream()); } + + PX_FORCE_INLINE const PxU16* getMaterialBuffer(const Scb::Scene& scene, const Scb::ShapeBuffer& sb) const + { + if(sb.materialCount == 1) + return &sb.materialIndex; + else + return scene.getShapeMaterialBuffer(sb.materialBufferIndex); + } + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess + { + template + static PX_FORCE_INLINE void write(Shape& base, Core& core, typename Fns::Arg v) + { + if(!base.isBuffering()) + { + PxShapeFlags oldShapeFlags = core.getFlags(); + Fns::setCore(core, v); + + // shared shapes return NULL. But shared shapes aren't mutable when attached to an actor, so no notification needed. + Sc::RigidCore* rigidCore = NpShapeGetScRigidObjectFromScbSLOW(base); + if(rigidCore && base.getControlState() != ControlState::eINSERT_PENDING) + rigidCore->onShapeChange(core, Sc::ShapeChangeNotifyFlags(Fns::flag), oldShapeFlags); +#if PX_SUPPORT_PVD + Scb::Scene* scene = base.getScbSceneForAPI(); // shared shapes also return zero here + if(scene && !base.insertPending()) + scene->getScenePvdClient().updatePvdProperties(&base); +#endif + } + else + { + Fns::setBuffered(*reinterpret_cast(base.getStream()), v); + base.markUpdated(Fns::flag); + } + } + }; + + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, mShape); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, mShape, v); } + template PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush >(*this, mShape, buf); } +#endif +}; + +PX_INLINE Shape::Shape(const PxGeometry& geometry, + PxShapeFlags shapeFlags, + const PxU16* materialIndices, + PxU16 materialCount, + bool isExclusive) : + mShape(geometry, shapeFlags, materialIndices, materialCount) +{ + // paranoia: the notify flags in Sc have to match up +#ifdef USE_NEW_SYSTEM + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Geometry) == PxU32(Sc::ShapeChangeNotifyFlag::eGEOMETRY)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Material) == PxU32(Sc::ShapeChangeNotifyFlag::eMATERIAL)); + PX_COMPILE_TIME_ASSERT(PxU32(BF_Shape2Actor) == PxU32(Sc::ShapeChangeNotifyFlag::eSHAPE2BODY)); + PX_COMPILE_TIME_ASSERT(PxU32(BF_SimulationFilterData) == PxU32(Sc::ShapeChangeNotifyFlag::eFILTERDATA)); + PX_COMPILE_TIME_ASSERT(PxU32(BF_ContactOffset) == PxU32(Sc::ShapeChangeNotifyFlag::eCONTACTOFFSET)); + PX_COMPILE_TIME_ASSERT(PxU32(BF_RestOffset) == PxU32(Sc::ShapeChangeNotifyFlag::eRESTOFFSET)); + PX_COMPILE_TIME_ASSERT(PxU32(BF_Flags) == PxU32(Sc::ShapeChangeNotifyFlag::eFLAGS)); +#else + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Geometry) == PxU32(Sc::ShapeChangeNotifyFlag::eGEOMETRY)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Material) == PxU32(Sc::ShapeChangeNotifyFlag::eMATERIAL)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Shape2Actor) == PxU32(Sc::ShapeChangeNotifyFlag::eSHAPE2BODY)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_SimulationFilterData) == PxU32(Sc::ShapeChangeNotifyFlag::eFILTERDATA)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_ContactOffset) == PxU32(Sc::ShapeChangeNotifyFlag::eCONTACTOFFSET)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_RestOffset) == PxU32(Sc::ShapeChangeNotifyFlag::eRESTOFFSET)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Flags) == PxU32(Sc::ShapeChangeNotifyFlag::eFLAGS)); +#endif + + if(isExclusive) + setScbType(ScbType::eSHAPE_EXCLUSIVE); + else + setScbType(ScbType::eSHAPE_SHARED); +} + +PX_INLINE PxGeometryType::Enum Shape::getGeometryType() const +{ + return mShape.getGeometryType(); +} + +PX_INLINE const PxGeometry& Shape::getGeometry() const +{ + if(isBuffered(Buf::BF_Geometry)) + return getBufferedData()->geometry.getGeometry(); + else + return mShape.getGeometry(); +} + +PX_INLINE const Gu::GeometryUnion& Shape::getGeometryUnion() const +{ + if(isBuffered(Buf::BF_Geometry)) + return getBufferedData()->geometry; + else + return mShape.getGeometryUnion(); +} + +PX_INLINE Scb::ShapeBuffer* Shape::setGeometry(const PxGeometry& geom) +{ + Scb::ShapeBuffer* shapeBuffer = NULL; + if (!isBuffering()) + { + Scb::Scene* sc = getScbScene(); + + if(sc) + sc->getScScene().unregisterShapeFromNphase(mShape); + + mShape.setGeometry(geom); + + if(sc) + sc->getScScene().registerShapeInNphase(mShape); + + Sc::RigidCore* rigidCore = NpShapeGetScRigidObjectFromScbSLOW(*this); + if(rigidCore) + rigidCore->onShapeChange(mShape, Sc::ShapeChangeNotifyFlag::eGEOMETRY, PxShapeFlags()); + +#if PX_SUPPORT_PVD + Scb::Scene* scbScene = getScbSceneForAPI(); + if(scbScene) + scbScene->getScenePvdClient().releaseAndRecreateGeometry(this); +#endif + } + else + { + markUpdated(Buf::BF_Geometry); + shapeBuffer = getBufferedData(); + shapeBuffer->geometry.set(geom); + } + + return shapeBuffer; +} + +PX_INLINE PxU16 Shape::getNbMaterials() const +{ + if(isBuffered(Buf::BF_Material)) + return getBufferedData()->materialCount; + else + return mShape.getNbMaterialIndices(); +} + +PX_INLINE PxMaterial* Shape::getMaterial(PxU32 index) const +{ + PX_ASSERT(index < getNbMaterials()); + + NpMaterialManager& matManager = NpPhysics::getInstance().getMaterialManager(); + if(isBuffered(Buf::BF_Material)) + { + const PxU16* materialIndices = getMaterialBuffer(*getScbScene(), *getBufferedData()); + return matManager.getMaterial(materialIndices[index]); + } + else + { + PxU16 matTableIndex = mShape.getMaterialIndices()[index]; + return matManager.getMaterial(matTableIndex); + } +} + +PX_INLINE PxU32 Shape::getMaterials(PxMaterial** buffer, PxU32 bufferSize, PxU32 startIndex) const +{ + const PxU16* materialIndices; + PxU32 matCount; + NpMaterialManager& matManager = NpPhysics::getInstance().getMaterialManager(); + if(isBuffered(Buf::BF_Material)) + { + // IMPORTANT: + // As long as the material pointers get copied to a user buffer, this works fine. + // Never give direct access to the internal material buffer because in the + // double buffered case the pointer changes on resize. + + const Scb::ShapeBuffer* PX_RESTRICT bufferedData = getBufferedData(); + + materialIndices = getMaterialBuffer(*getScbScene(), *bufferedData); + matCount = bufferedData->materialCount; + } + else + { + materialIndices = mShape.getMaterialIndices(); + matCount = mShape.getNbMaterialIndices(); + } + + // PT: this is copied from Cm::getArrayOfPointers(). We cannot use the Cm function here + // because of the extra indirection needed to access the materials. + PxU32 size = matCount; + const PxU32 remainder = PxU32(PxMax(PxI32(size - startIndex), 0)); + const PxU32 writeCount = PxMin(remainder, bufferSize); + materialIndices += startIndex; + for(PxU32 i=0;imaterialIndex; + else + { + PxU32 bufferIdx; + materialIndices = getScbScene()->allocShapeMaterialBuffer(materialCount, bufferIdx); + bufferedData->materialBufferIndex = bufferIdx; + } + bufferedData->materialCount = materialCount; + + NpMaterial::getMaterialIndices(materials, materialIndices, materialCount); + + markUpdated(Buf::BF_Material); + + return true; + } +} + +PX_FORCE_INLINE void Shape::setControlStateIfExclusive(Scene* s, ControlState::Enum cs) +{ + if(isExclusive()) + { + setControlState(cs); + setScbScene(s); + } +} + +template +PX_FORCE_INLINE void Shape::checkUpdateOnRemove(Scene* s) +{ + // special code to cover the case where a shape has a pending update and gets released. The following operations have to be done + // before the ref-counter of the shape gets decremented because that could cause the shape to be deleted in which case it must not + // be in the pending update list any longer. + if(getControlFlags() & Scb::ControlFlag::eIS_UPDATED) + { + if(sync) + syncState(); + s->removeShapeFromPendingUpdateList(*this); + + resetControlFlag(ControlFlag::eIS_UPDATED); + } +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbType.h b/src/PhysX/physx/source/physx/src/buffering/ScbType.h new file mode 100644 index 000000000..f1108ae61 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbType.h @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_TYPE +#define PX_PHYSICS_SCB_TYPE + +namespace physx +{ + struct ScbType + { + enum Enum + { + eUNDEFINED, + eSHAPE_EXCLUSIVE, + eSHAPE_SHARED, + eBODY, + eBODY_FROM_ARTICULATION_LINK, + eRIGID_STATIC, + eCONSTRAINT, + eARTICULATION, + eARTICULATION_JOINT, + eAGGREGATE, + eTYPE_COUNT + }; + }; +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/device/PhysXIndicator.h b/src/PhysX/physx/source/physx/src/device/PhysXIndicator.h new file mode 100644 index 000000000..6bb76f239 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/device/PhysXIndicator.h @@ -0,0 +1,56 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef __PHYSXINDICATOR_H__ +#define __PHYSXINDICATOR_H__ + +#include "foundation/PxPreprocessor.h" + +namespace physx +{ + struct NvPhysXToDrv_Data_V1_; + + class PhysXIndicator + { + public: + PhysXIndicator(bool isGpu = false); + ~PhysXIndicator(); + + void setIsGpu(bool isGpu); + + private: + void updateCounter(int delta); + + NvPhysXToDrv_Data_V1_* mPhysXDataPtr; + void* mFileHandle; + bool mIsGpu; + }; +} + +#endif // __PHYSXINDICATOR_H__ diff --git a/src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp b/src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp new file mode 100644 index 000000000..c21905982 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp @@ -0,0 +1,51 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PhysXIndicator.h" +#include "nvPhysXtoDrv.h" + +physx::PhysXIndicator::PhysXIndicator(bool isGpu) +: mPhysXDataPtr(0), mFileHandle(0), mIsGpu(isGpu) +{ + +} + +physx::PhysXIndicator::~PhysXIndicator() +{ +} + +void physx::PhysXIndicator::setIsGpu(bool isGpu) +{ + PX_UNUSED(isGpu); +} + +PX_INLINE void physx::PhysXIndicator::updateCounter(int delta) +{ + PX_UNUSED(delta); +} diff --git a/src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h b/src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h new file mode 100644 index 000000000..cbffc8292 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef __NVPHYSXTODRV_H__ +#define __NVPHYSXTODRV_H__ + +// The puprose of this interface is to provide graphics drivers with information +// about PhysX state to draw PhysX visual indicator + +// We share information between modules using a memory section object. PhysX creates +// such object, graphics drivers try to open it. The name of the object has +// fixed part (NvPhysXToDrv_SectionName) followed by the process id. This allows +// each process to have its own communication channel. + +namespace physx +{ + +#define NvPhysXToDrv_SectionName "PH71828182845_" + +// Vista apps cannot create stuff in Global\\ namespace when NOT elevated, so use local scope +#define NvPhysXToDrv_Build_SectionName(PID, buf) sprintf(buf, NvPhysXToDrv_SectionName "%x", static_cast(PID)) +#define NvPhysXToDrv_Build_SectionNameXP(PID, buf) sprintf(buf, "Global\\" NvPhysXToDrv_SectionName "%x", static_cast(PID)) + +typedef struct NvPhysXToDrv_Header_ +{ + int signature; // header interface signature + int version; // version of the interface + int size; // size of the structure + int reserved; // reserved, must be zero +} NvPhysXToDrv_Header; + +// this structure describes layout of data in the shared memory section +typedef struct NvPhysXToDrv_Data_V1_ +{ + NvPhysXToDrv_Header header; // keep this member first in all versions of the interface. + + int bCpuPhysicsPresent; // nonzero if cpu physics is initialized + int bGpuPhysicsPresent; // nonzero if gpu physics is initialized + +} NvPhysXToDrv_Data_V1; + +// some random magic number as our interface signature +#define NvPhysXToDrv_Header_Signature 0xA7AB + +// use the macro to setup the header to the latest version of the interface +// update the macro when a new verson of the interface is added +#define NvPhysXToDrv_Header_Init(header) \ +{ \ + header.signature = NvPhysXToDrv_Header_Signature; \ + header.version = 1; \ + header.size = sizeof(NvPhysXToDrv_Data_V1); \ + header.reserved = 0; \ +} + +// validate the header against all known interface versions +// add validation checks when new interfaces are added +#define NvPhysXToDrv_Header_Validate(header, curVersion) \ + ( \ + (header.signature == NvPhysXToDrv_Header_Signature) && \ + (header.version == curVersion) && \ + (curVersion == 1) && \ + (header.size == sizeof(NvPhysXToDrv_Data_V1)) \ + ) + +} + +#endif // __NVPHYSXTODRV_H__ diff --git a/src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp b/src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp new file mode 100644 index 000000000..6357e936f --- /dev/null +++ b/src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp @@ -0,0 +1,131 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PhysXIndicator.h" +#include "nvPhysXtoDrv.h" + +#pragma warning (push) +#pragma warning (disable : 4668) //'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#include +#pragma warning (pop) + +#include + +#if _MSC_VER >= 1800 +#include +#endif + +// Scope-based to indicate to NV driver that CPU PhysX is happening +physx::PhysXIndicator::PhysXIndicator(bool isGpu) +: mPhysXDataPtr(0), mFileHandle(0), mIsGpu(isGpu) +{ + // Get the windows version (we can only create Global\\ namespace objects in XP) + /** + Operating system Version number + ---------------- -------------- + Windows 7 6.1 + Windows Server 2008 R2 6.1 + Windows Server 2008 6.0 + Windows Vista 6.0 + Windows Server 2003 R2 5.2 + Windows Server 2003 5.2 + Windows XP 5.1 + Windows 2000 5.0 + **/ + + char configName[128]; + +#if _MSC_VER >= 1800 + if (!IsWindowsVistaOrGreater()) +#else + OSVERSIONINFOEX windowsVersionInfo; + windowsVersionInfo.dwOSVersionInfoSize = sizeof (windowsVersionInfo); + GetVersionEx((LPOSVERSIONINFO)&windowsVersionInfo); + + if (windowsVersionInfo.dwMajorVersion < 6) +#endif + NvPhysXToDrv_Build_SectionNameXP(GetCurrentProcessId(), configName); + else + NvPhysXToDrv_Build_SectionName(GetCurrentProcessId(), configName); + + mFileHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE, 0, sizeof(NvPhysXToDrv_Data_V1), configName); + + if (!mFileHandle || mFileHandle == INVALID_HANDLE_VALUE) + return; + + bool alreadyExists = ERROR_ALREADY_EXISTS == GetLastError(); + + mPhysXDataPtr = (physx::NvPhysXToDrv_Data_V1*)MapViewOfFile(mFileHandle, + FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(NvPhysXToDrv_Data_V1)); + + if(!mPhysXDataPtr) + return; + + if (!alreadyExists) + { + mPhysXDataPtr->bCpuPhysicsPresent = 0; + mPhysXDataPtr->bGpuPhysicsPresent = 0; + } + + updateCounter(1); + + // init header last to prevent race conditions + // this must be done because the driver may have already created the shared memory block, + // thus alreadyExists may be true, even if PhysX hasn't been initialized + NvPhysXToDrv_Header_Init(mPhysXDataPtr->header); +} + +physx::PhysXIndicator::~PhysXIndicator() +{ + if(mPhysXDataPtr) + { + updateCounter(-1); + UnmapViewOfFile(mPhysXDataPtr); + } + + if(mFileHandle && mFileHandle != INVALID_HANDLE_VALUE) + CloseHandle(mFileHandle); +} + +void physx::PhysXIndicator::setIsGpu(bool isGpu) +{ + if(!mPhysXDataPtr) + return; + + updateCounter(-1); + mIsGpu = isGpu; + updateCounter(1); +} + +PX_INLINE void physx::PhysXIndicator::updateCounter(int delta) +{ + (mIsGpu ? mPhysXDataPtr->bGpuPhysicsPresent + : mPhysXDataPtr->bCpuPhysicsPresent) += delta; +} diff --git a/src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp b/src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp new file mode 100644 index 000000000..6f8e5f3a5 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxPhysXConfig.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "PxGpu.h" + +#ifndef PX_PHYSX_GPU_STATIC +namespace grid +{ + class Server; + class ClientContextPredictionManager; +} + +namespace physx +{ + //forward declare stuff from PxPhysXGpuModuleLoader.cpp + void PxLoadPhysxGPUModule(const char* appGUID); + typedef physx::PxCudaContextManager* (PxCreateCudaContextManager_FUNC)(physx::PxFoundation& foundation, const physx::PxCudaContextManagerDesc& desc); + typedef int (PxGetSuggestedCudaDeviceOrdinal_FUNC)(physx::PxErrorCallback& errc); + typedef grid::ClientContextPredictionManager* (PxCreateClientContextManager_FUNC)(grid::Server* server, physx::PxU32 maxNbSleepMsg); + extern PxCreateCudaContextManager_FUNC* g_PxCreateCudaContextManager_Func; + extern PxGetSuggestedCudaDeviceOrdinal_FUNC* g_PxGetSuggestedCudaDeviceOrdinal_Func; + extern PxCreateClientContextManager_FUNC* g_CreateClientContextManager_Func; + +} // end physx namespace + + + +physx::PxCudaContextManager* PxCreateCudaContextManager(physx::PxFoundation& foundation, const physx::PxCudaContextManagerDesc& desc) +{ + if (!physx::g_PxCreateCudaContextManager_Func) + physx::PxLoadPhysxGPUModule(desc.appGUID); + + if (physx::g_PxCreateCudaContextManager_Func) + return physx::g_PxCreateCudaContextManager_Func(foundation, desc); + else + return NULL; +} + +int PxGetSuggestedCudaDeviceOrdinal(physx::PxErrorCallback& errc) +{ + if (!physx::g_PxGetSuggestedCudaDeviceOrdinal_Func) + physx::PxLoadPhysxGPUModule(NULL); + + if (physx::g_PxGetSuggestedCudaDeviceOrdinal_Func) + return physx::g_PxGetSuggestedCudaDeviceOrdinal_Func(errc); + else + return -1; +} + +PX_C_EXPORT PX_PHYSX_CORE_API grid::ClientContextPredictionManager* PX_CALL_CONV PxCreateCudaClientContextManager(grid::Server* server, physx::PxU32 maxNbSleepMsg); + +grid::ClientContextPredictionManager* PxCreateCudaClientContextManager(grid::Server* server, physx::PxU32 maxNbSleepMsg) +{ + if (!physx::g_CreateClientContextManager_Func) + physx::PxLoadPhysxGPUModule(NULL); + + if (physx::g_CreateClientContextManager_Func) + return physx::g_CreateClientContextManager_Func(server, maxNbSleepMsg); + else + return NULL; +} +#endif + +#endif // PX_SUPPORT_GPU_PHYSX + diff --git a/src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp b/src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp new file mode 100644 index 000000000..288d812da --- /dev/null +++ b/src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp @@ -0,0 +1,194 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxPhysXConfig.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "foundation/Px.h" +#include "PsFoundation.h" +#include "PxPhysics.h" +#include "PxGpu.h" + +#include "cudamanager/PxCudaContextManager.h" + +#if PX_WINDOWS +#include "windows/PsWindowsInclude.h" +#include "windows/PxWindowsDelayLoadHook.h" +#include "windows/CmWindowsModuleUpdateLoader.h" +#elif PX_LINUX +#include +#endif // ~PX_LINUX + +namespace physx +{ + // alias shared foundation to something usable + namespace Ps = shdfnd; +} + +#define STRINGIFY(x) #x +#define GETSTRING(x) STRINGIFY(x) + +#define PHYSX_GPU_SHARED_LIB_NAME GETSTRING(PX_PHYSX_GPU_SHARED_LIB_NAME) +static const char* gPhysXGpuLibraryName = PHYSX_GPU_SHARED_LIB_NAME; + +#undef GETSTRING +#undef STRINGIFY + +#if PX_WINDOWS +namespace physx +{ + const PxDelayLoadHook* PxGetPhysXDelayLoadHook(); +} +#endif + +void PxSetPhysXGpuLoadHook(const PxGpuLoadHook* hook) +{ + gPhysXGpuLibraryName = hook->getPhysXGpuDllName(); +} + +namespace grid +{ + class Server; + class ClientContextPredictionManager; +} + +namespace physx +{ +#if PX_VC +#pragma warning(disable: 4191) //'operator/operation' : unsafe conversion from 'type of expression' to 'type required' +#endif + + class PxFoundation; + class PxPhysXGpu; + + typedef physx::PxPhysXGpu* (PxCreatePhysXGpu_FUNC)(); + typedef physx::PxCudaContextManager* (PxCreateCudaContextManager_FUNC)(physx::PxFoundation& foundation, const physx::PxCudaContextManagerDesc& desc); + typedef int (PxGetSuggestedCudaDeviceOrdinal_FUNC)(physx::PxErrorCallback& errc); + typedef grid::ClientContextPredictionManager* (PxCreateClientContextManager_FUNC)(grid::Server* server, physx::PxU32 maxNbSleepMsg); + + PxCreatePhysXGpu_FUNC* g_PxCreatePhysXGpu_Func = NULL; + PxCreateCudaContextManager_FUNC* g_PxCreateCudaContextManager_Func = NULL; + PxGetSuggestedCudaDeviceOrdinal_FUNC* g_PxGetSuggestedCudaDeviceOrdinal_Func = NULL; + + PxCreateClientContextManager_FUNC* g_CreateClientContextManager_Func = NULL; + +#if PX_WINDOWS + + typedef void (PxSetPhysXGpuDelayLoadHook_FUNC)(const PxDelayLoadHook* delayLoadHook); + PxSetPhysXGpuDelayLoadHook_FUNC* g_PxSetPhysXGpuDelayLoadHook_Func = NULL; + +#define DEFAULT_PHYSX_GPU_GUID "D79FA4BF-177C-4841-8091-4375D311D6A3" + + void PxLoadPhysxGPUModule(const char* appGUID) + { + static HMODULE s_library; + + if (s_library == NULL) + s_library = GetModuleHandle(gPhysXGpuLibraryName); + + if (s_library == NULL) + { + Cm::CmModuleUpdateLoader moduleLoader(UPDATE_LOADER_DLL_NAME); + s_library = moduleLoader.LoadModule(gPhysXGpuLibraryName, appGUID == NULL ? DEFAULT_PHYSX_GPU_GUID : appGUID); + } + + if (s_library) + { + g_PxSetPhysXGpuDelayLoadHook_Func = (PxSetPhysXGpuDelayLoadHook_FUNC*)GetProcAddress(s_library, "PxSetPhysXGpuDelayLoadHook"); + g_PxCreatePhysXGpu_Func = (PxCreatePhysXGpu_FUNC*)GetProcAddress(s_library, "PxCreatePhysXGpu"); + g_PxCreateCudaContextManager_Func = (PxCreateCudaContextManager_FUNC*)GetProcAddress(s_library, "PxCreateCudaContextManager"); + g_PxGetSuggestedCudaDeviceOrdinal_Func = (PxGetSuggestedCudaDeviceOrdinal_FUNC*)GetProcAddress(s_library, "PxGetSuggestedCudaDeviceOrdinal"); + g_CreateClientContextManager_Func = (PxCreateClientContextManager_FUNC*)GetProcAddress(s_library, "PxCreateCudaClientContextManager"); + } + + // Check for errors + if (s_library == NULL) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Failed to load PhysXGpu dll!"); + return; + } + + if (g_PxSetPhysXGpuDelayLoadHook_Func == NULL || g_PxCreatePhysXGpu_Func == NULL || g_PxCreateCudaContextManager_Func == NULL || g_PxGetSuggestedCudaDeviceOrdinal_Func == NULL) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "PhysXGpu dll is incompatible with this version of PhysX!"); + return; + } + + g_PxSetPhysXGpuDelayLoadHook_Func(PxGetPhysXDelayLoadHook()); + + } + +#elif PX_LINUX + + void PxLoadPhysxGPUModule(const char*) + { + static void* s_library; + + if (s_library == NULL) + { + // load libcuda.so here since gcc configured with --as-needed won't link to it + // if there is no call from the binary to it. + void* hLibCuda = dlopen("libcuda.so", RTLD_NOW | RTLD_GLOBAL); + if (hLibCuda) + { + s_library = dlopen(gPhysXGpuLibraryName, RTLD_NOW); + } + else + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "libcuda.so!"); + return; + } + } + + // no UpdateLoader + if (s_library) + { + *reinterpret_cast(&g_PxCreatePhysXGpu_Func) = dlsym(s_library, "PxCreatePhysXGpu"); + *reinterpret_cast(&g_PxCreateCudaContextManager_Func) = dlsym(s_library, "PxCreateCudaContextManager"); + *reinterpret_cast(&g_PxGetSuggestedCudaDeviceOrdinal_Func) = dlsym(s_library, "PxGetSuggestedCudaDeviceOrdinal"); + *reinterpret_cast(&g_CreateClientContextManager_Func ) = dlsym(s_library, "PxCreateCudaClientContextManager"); + } + + // Check for errors + if (s_library == NULL) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Failed to load %s.", gPhysXGpuLibraryName); + return; + } + if (g_PxCreatePhysXGpu_Func == NULL || g_PxCreateCudaContextManager_Func == NULL || g_PxGetSuggestedCudaDeviceOrdinal_Func == NULL) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "%s is incompatible with this version of PhysX!", gPhysXGpuLibraryName); + return; + } + } + +#endif // PX_LINUX + +} // end physx namespace + +#endif // PX_SUPPORT_GPU_PHYSX diff --git a/src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp b/src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp new file mode 100644 index 000000000..203a12cca --- /dev/null +++ b/src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "windows/PxWindowsDelayLoadHook.h" +#include "windows/PsWindowsInclude.h" +#include "windows/CmWindowsLoadLibrary.h" + +// Prior to Visual Studio 2015 Update 3, these hooks were non-const. +#define DELAYIMP_INSECURE_WRITABLE_HOOKS +#include + +namespace physx +{ + static const PxDelayLoadHook* gDelayLoadHook = NULL; + + void PxSetPhysXDelayLoadHook(const PxDelayLoadHook* hook) + { + gDelayLoadHook = hook; + } + + const PxDelayLoadHook* PxGetPhysXDelayLoadHook() + { + return gDelayLoadHook; + } +} + +using namespace physx; + +#pragma comment(lib, "delayimp") + +FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli) +{ + switch (dliNotify) { + case dliStartProcessing : + break; + + case dliNotePreLoadLibrary : + { + return Cm::physXCommonDliNotePreLoadLibrary(pdli->szDll,gDelayLoadHook); + } + break; + + case dliNotePreGetProcAddress : + break; + + case dliFailLoadLib : + break; + + case dliFailGetProc : + break; + + case dliNoteEndProcessing : + break; + + default : + + return NULL; + } + + return NULL; +} + +PfnDliHook __pfnDliNotifyHook2 = delayHook; diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp new file mode 100644 index 000000000..f025526bb --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp @@ -0,0 +1,197 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxController.h" +#include "CctBoxController.h" +#include "CctCharacterControllerManager.h" +#include "PxBoxGeometry.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" + +using namespace physx; +using namespace Cct; + +static PX_FORCE_INLINE PxVec3 CCTtoProxyExtents(PxF32 halfHeight, PxF32 halfSideExtent, PxF32 halfForwardExtent, PxF32 coeff) +{ + // PT: because we now orient the box CCT using the same quat as for capsules... + // i.e. the identity quat corresponds to a up dir = 1,0,0 (which is like the worst choice we could have made, of course) + return PxVec3(halfHeight * coeff, halfSideExtent * coeff, halfForwardExtent * coeff); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +BoxController::BoxController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* s) : Controller(desc, s) +{ + mType = PxControllerShapeType::eBOX; + + const PxBoxControllerDesc& bc = static_cast(desc); + + mHalfHeight = bc.halfHeight; + mHalfSideExtent = bc.halfSideExtent; + mHalfForwardExtent = bc.halfForwardExtent; + + // Create kinematic actor under the hood + PxBoxGeometry boxGeom; + boxGeom.halfExtents = CCTtoProxyExtents(bc.halfHeight, bc.halfSideExtent, bc.halfForwardExtent, mProxyScaleCoeff); + + createProxyActor(sdk, boxGeom, *desc.material); +} + +BoxController::~BoxController() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BoxController::invalidateCache() +{ + if(mManager->mLockingEnabled) + mWriteLock.lock(); + + mCctModule.voidTestCache(); + + if(mManager->mLockingEnabled) + mWriteLock.unlock(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool BoxController::getWorldBox(PxExtendedBounds3& box) const +{ + setCenterExtents(box, mPosition, PxVec3(mHalfHeight, mHalfSideExtent, mHalfForwardExtent)); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxF32 BoxController::getHalfHeight() const +{ + return mHalfHeight; +} + +PxF32 BoxController::getHalfSideExtent() const +{ + return mHalfSideExtent; +} + +PxF32 BoxController::getHalfForwardExtent() const +{ + return mHalfForwardExtent; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool BoxController::updateKinematicProxy() +{ + // Set extents for kinematic proxy + if(mKineActor) + { + PxShape* shape = getKineShape(); + + PX_ASSERT(shape->getGeometryType() == PxGeometryType::eBOX); + PxBoxGeometry bg; + shape->getBoxGeometry(bg); + + bg.halfExtents = CCTtoProxyExtents(mHalfHeight, mHalfSideExtent, mHalfForwardExtent, mProxyScaleCoeff); + shape->setGeometry(bg); + } + return true; +} + +bool BoxController::setHalfHeight(PxF32 halfHeight) +{ + if(halfHeight<=0.0f) + return false; + + mHalfHeight = halfHeight; + return updateKinematicProxy(); +} + +bool BoxController::setHalfSideExtent(PxF32 halfSideExtent) +{ + if(halfSideExtent<=0.0f) + return false; + + mHalfSideExtent = halfSideExtent; + return updateKinematicProxy(); +} + +bool BoxController::setHalfForwardExtent(PxF32 halfForwardExtent) +{ + if(halfForwardExtent<=0.0f) + return false; + + mHalfForwardExtent = halfForwardExtent; + return updateKinematicProxy(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxExtendedVec3 BoxController::getFootPosition() const +{ + PxExtendedVec3 groundPosition = mPosition; // Middle of the CCT + groundPosition -= mUserParams.mUpDirection * (mHalfHeight + mUserParams.mContactOffset); // Ground + return groundPosition; +} + +bool BoxController::setFootPosition(const PxExtendedVec3& position) +{ + PxExtendedVec3 centerPosition = position; + centerPosition += mUserParams.mUpDirection * (mHalfHeight + mUserParams.mContactOffset); + return setPosition(centerPosition); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BoxController::getOBB(PxExtendedBox& obb) const +{ + // PT: TODO: optimize this + PxExtendedBounds3 worldBox; + getWorldBox(worldBox); + + getCenter(worldBox, obb.center); + getExtents(worldBox, obb.extents); + obb.rot = mUserParams.mQuatFromUp; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BoxController::resize(PxReal height) +{ + const float oldHeight = getHalfHeight(); + setHalfHeight(height); + + const float delta = height - oldHeight; + PxExtendedVec3 pos = getPosition(); + pos += mUserParams.mUpDirection * delta; + setPosition(pos); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h new file mode 100644 index 000000000..46f6d238d --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h @@ -0,0 +1,112 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_BOX_CONTROLLER +#define CCT_BOX_CONTROLLER + +/* Exclude from documentation */ +/** \cond */ + +#include "CctController.h" +#include "PxBoxController.h" + +namespace physx +{ + +class PxPhysics; + +namespace Cct +{ + + class BoxController : public PxBoxController, public Controller + { + public: + BoxController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* scene); + virtual ~BoxController(); + + // Controller + virtual PxF32 getHalfHeightInternal() const { return mHalfHeight; } + virtual bool getWorldBox(PxExtendedBounds3& box) const; + virtual PxController* getPxController() { return this; } + //~Controller + + // PxController + virtual PxControllerShapeType::Enum getType() const { return mType; } + virtual void release() { releaseInternal(); } + virtual PxControllerCollisionFlags move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles); + virtual bool setPosition(const PxExtendedVec3& position) { return setPos(position); } + virtual const PxExtendedVec3& getPosition() const { return mPosition; } + virtual bool setFootPosition(const PxExtendedVec3& position); + virtual PxExtendedVec3 getFootPosition() const; + virtual PxRigidDynamic* getActor() const { return mKineActor; } + virtual void setStepOffset(const float offset) { if(offset>0.0f) + mUserParams.mStepOffset = offset; } + virtual PxF32 getStepOffset() const { return mUserParams.mStepOffset; } + virtual void setNonWalkableMode(PxControllerNonWalkableMode::Enum flag) { mUserParams.mNonWalkableMode = flag; } + virtual PxControllerNonWalkableMode::Enum getNonWalkableMode() const { return mUserParams.mNonWalkableMode; } + virtual PxF32 getContactOffset() const { return mUserParams.mContactOffset; } + virtual void setContactOffset(PxF32 offset) { if(offset>0.0f) + mUserParams.mContactOffset = offset;} + virtual PxVec3 getUpDirection() const { return mUserParams.mUpDirection; } + virtual void setUpDirection(const PxVec3& up) { setUpDirectionInternal(up); } + virtual PxF32 getSlopeLimit() const { return mUserParams.mSlopeLimit; } + virtual void setSlopeLimit(PxF32 slopeLimit) { if(slopeLimit>0.0f) + mUserParams.mSlopeLimit = slopeLimit;} + virtual void invalidateCache(); + virtual PxScene* getScene() { return mScene; } + virtual void* getUserData() const { return mUserData; } + virtual void setUserData(void* userData) { mUserData = userData; } + virtual void getState(PxControllerState& state) const { return getInternalState(state); } + virtual void getStats(PxControllerStats& stats) const { return getInternalStats(stats); } + virtual void resize(PxReal height); + //~PxController + + // PxBoxController + virtual PxF32 getHalfHeight() const; + virtual PxF32 getHalfSideExtent() const; + virtual PxF32 getHalfForwardExtent() const; + virtual bool setHalfHeight(PxF32 halfHeight); + virtual bool setHalfSideExtent(PxF32 halfSideExtent); + virtual bool setHalfForwardExtent(PxF32 halfForwardExtent); + //~ PxBoxController + + PxF32 mHalfHeight; + PxF32 mHalfSideExtent; + PxF32 mHalfForwardExtent; + + bool updateKinematicProxy(); + void getOBB(PxExtendedBox& obb) const; + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp new file mode 100644 index 000000000..ea96dfb56 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp @@ -0,0 +1,192 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxController.h" +#include "CctCapsuleController.h" +#include "CctCharacterControllerManager.h" +#include "PxCapsuleGeometry.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" + +using namespace physx; +using namespace Cct; + +static PX_FORCE_INLINE float CCTtoProxyRadius(float r, PxF32 coeff) { return r * coeff; } +static PX_FORCE_INLINE float CCTtoProxyHeight(float h, PxF32 coeff) { return 0.5f * h * coeff; } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +CapsuleController::CapsuleController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* s) : Controller(desc, s) +{ + mType = PxControllerShapeType::eCAPSULE; + + const PxCapsuleControllerDesc& cc = static_cast(desc); + + mRadius = cc.radius; + mHeight = cc.height; + mClimbingMode = cc.climbingMode; + + // Create kinematic actor under the hood + PxCapsuleGeometry capsGeom; + capsGeom.radius = CCTtoProxyRadius(cc.radius, mProxyScaleCoeff); + capsGeom.halfHeight = CCTtoProxyHeight(cc.height, mProxyScaleCoeff); + + createProxyActor(sdk, capsGeom, *desc.material); +} + +CapsuleController::~CapsuleController() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CapsuleController::invalidateCache() +{ + if(mManager->mLockingEnabled) + mWriteLock.lock(); + + mCctModule.voidTestCache(); + + if(mManager->mLockingEnabled) + mWriteLock.unlock(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool CapsuleController::getWorldBox(PxExtendedBounds3& box) const +{ + setCenterExtents(box, mPosition, PxVec3(mRadius, mRadius+mHeight*0.5f, mRadius)); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool CapsuleController::setRadius(PxF32 r) +{ + // Set radius for CCT volume + mRadius = r; + + // Set radius for kinematic proxy + if(mKineActor) + { + PxShape* shape = getKineShape(); + + PX_ASSERT(shape->getGeometryType() == PxGeometryType::eCAPSULE); + PxCapsuleGeometry cg; + shape->getCapsuleGeometry(cg); + + cg.radius = CCTtoProxyRadius(r, mProxyScaleCoeff); + shape->setGeometry(cg); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool CapsuleController::setHeight(PxF32 h) +{ + // Set height for CCT volume + mHeight = h; + + // Set height for kinematic proxy + if(mKineActor) + { + PxShape* shape = getKineShape(); + + PX_ASSERT(shape->getGeometryType() == PxGeometryType::eCAPSULE); + PxCapsuleGeometry cg; + shape->getCapsuleGeometry(cg); + + cg.halfHeight = CCTtoProxyHeight(h, mProxyScaleCoeff); + shape->setGeometry(cg); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxCapsuleClimbingMode::Enum CapsuleController::getClimbingMode() const +{ + return mClimbingMode; +} + +bool CapsuleController::setClimbingMode(PxCapsuleClimbingMode::Enum mode) +{ + if(mode>=PxCapsuleClimbingMode::eLAST) + return false; + mClimbingMode = mode; + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxExtendedVec3 CapsuleController::getFootPosition() const +{ + PxExtendedVec3 groundPosition = mPosition; // Middle of the CCT + groundPosition -= mUserParams.mUpDirection * (mUserParams.mContactOffset+mRadius+mHeight*0.5f); // Ground + return groundPosition; +} + +bool CapsuleController::setFootPosition(const PxExtendedVec3& position) +{ + PxExtendedVec3 centerPosition = position; + centerPosition += mUserParams.mUpDirection * (mUserParams.mContactOffset+mRadius+mHeight*0.5f); + return setPosition(centerPosition); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CapsuleController::getCapsule(PxExtendedCapsule& capsule) const +{ + // PT: TODO: optimize this + PxExtendedVec3 p0 = mPosition; + PxExtendedVec3 p1 = mPosition; + const PxVec3 extents = mUserParams.mUpDirection*mHeight*0.5f; + p0 -= extents; + p1 += extents; + + capsule.p0 = p0; + capsule.p1 = p1; + capsule.radius = mRadius; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CapsuleController::resize(PxReal height) +{ + const float oldHeight = getHeight(); + setHeight(height); + + const float delta = height - oldHeight; + PxExtendedVec3 pos = getPosition(); + pos += mUserParams.mUpDirection * delta * 0.5f; + setPosition(pos); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h new file mode 100644 index 000000000..cefab7c38 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h @@ -0,0 +1,111 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_CAPSULE_CONTROLLER +#define CCT_CAPSULE_CONTROLLER + +/* Exclude from documentation */ +/** \cond */ + +#include "CctController.h" +#include "PxCapsuleController.h" + +namespace physx +{ + +class PxPhysics; + +namespace Cct +{ + + class CapsuleController : public PxCapsuleController, public Controller + { + public: + CapsuleController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* scene); + virtual ~CapsuleController(); + + // Controller + virtual PxF32 getHalfHeightInternal() const { return mRadius+mHeight*0.5f; } + virtual bool getWorldBox(PxExtendedBounds3& box) const; + virtual PxController* getPxController() { return this; } + //~Controller + + // PxController + virtual PxControllerShapeType::Enum getType() const { return mType; } + virtual void release() { releaseInternal(); } + virtual PxControllerCollisionFlags move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles); + virtual bool setPosition(const PxExtendedVec3& position) { return setPos(position); } + virtual const PxExtendedVec3& getPosition() const { return mPosition; } + virtual bool setFootPosition(const PxExtendedVec3& position); + virtual PxExtendedVec3 getFootPosition() const; + virtual PxRigidDynamic* getActor() const { return mKineActor; } + virtual void setStepOffset(const float offset) { if(offset>0.0f) + mUserParams.mStepOffset = offset; } + virtual PxF32 getStepOffset() const { return mUserParams.mStepOffset; } + virtual void setNonWalkableMode(PxControllerNonWalkableMode::Enum flag) { mUserParams.mNonWalkableMode = flag; } + virtual PxControllerNonWalkableMode::Enum getNonWalkableMode() const { return mUserParams.mNonWalkableMode; } + virtual PxF32 getContactOffset() const { return mUserParams.mContactOffset; } + virtual void setContactOffset(PxF32 offset) { if(offset>0.0f) + mUserParams.mContactOffset = offset;} + virtual PxVec3 getUpDirection() const { return mUserParams.mUpDirection; } + virtual void setUpDirection(const PxVec3& up) { setUpDirectionInternal(up); } + virtual PxF32 getSlopeLimit() const { return mUserParams.mSlopeLimit; } + virtual void setSlopeLimit(PxF32 slopeLimit) { if(slopeLimit>0.0f) + mUserParams.mSlopeLimit = slopeLimit;} + virtual void invalidateCache(); + virtual PxScene* getScene() { return mScene; } + virtual void* getUserData() const { return mUserData; } + virtual void setUserData(void* userData) { mUserData = userData; } + virtual void getState(PxControllerState& state) const { return getInternalState(state); } + virtual void getStats(PxControllerStats& stats) const { return getInternalStats(stats); } + virtual void resize(PxReal height); + //~PxController + + // PxCapsuleController + virtual PxF32 getRadius() const { return mRadius; } + virtual PxF32 getHeight() const { return mHeight; } + virtual PxCapsuleClimbingMode::Enum getClimbingMode() const; + virtual bool setRadius(PxF32 radius); + virtual bool setHeight(PxF32 height); + virtual bool setClimbingMode(PxCapsuleClimbingMode::Enum); + //~ PxCapsuleController + + void getCapsule(PxExtendedCapsule& capsule) const; + + PxF32 mRadius; + PxF32 mHeight; + PxCapsuleClimbingMode::Enum mClimbingMode; + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp new file mode 100644 index 000000000..510ad5c0c --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp @@ -0,0 +1,2552 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "CctCharacterController.h" +#include "CctCharacterControllerManager.h" +#include "CctSweptBox.h" +#include "CctSweptCapsule.h" +#include "CctObstacleContext.h" +#include "PxRigidDynamic.h" +#include "CmRenderOutput.h" +#include "PsMathUtils.h" +#include "GuIntersectionBoxBox.h" +#include "GuDistanceSegmentBox.h" +#include "PxMeshQuery.h" +#include "PsFPU.h" + +// PT: TODO: remove those includes.... shouldn't be allowed from here +#include "PxControllerObstacles.h" // (*) +#include "CctInternalStructs.h" // (*) +#include "PxControllerManager.h" // (*) +#include "PxControllerBehavior.h" // (*) + +//#define DEBUG_MTD +#ifdef DEBUG_MTD + #include +#endif + +#define MAX_ITER 10 + +using namespace physx; +using namespace Cct; +using namespace Gu; +using namespace Cm; + +static const PxU32 gObstacleDebugColor = PxU32(PxDebugColor::eARGB_CYAN); +//static const PxU32 gCCTBoxDebugColor = PxU32(PxDebugColor::eARGB_YELLOW); +static const PxU32 gTBVDebugColor = PxU32(PxDebugColor::eARGB_MAGENTA); +static const bool gUsePartialUpdates = true; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxHitFlags getSweepHitFlags(const CCTParams& params) +{ + PxHitFlags sweepHitFlags = PxHitFlag::eDEFAULT/*|PxHitFlag::eMESH_BOTH_SIDES*/; +// sweepHitFlags |= PxHitFlag::eASSUME_NO_INITIAL_OVERLAP; + if(params.mPreciseSweeps) + sweepHitFlags |= PxHitFlag::ePRECISE_SWEEP; + return sweepHitFlags; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool shouldApplyRecoveryModule(const PxRigidActor& rigidActor) +{ + // PT: we must let the dynamic objects go through the CCT for proper 2-way interactions. + // But we should still apply the recovery module for kinematics. + + const PxType type = rigidActor.getConcreteType(); + if(type==PxConcreteType::eRIGID_STATIC) + return true; + + if(type!=PxConcreteType::eRIGID_DYNAMIC) + return false; + + return static_cast(rigidActor).getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static const bool gUseLocalSpace = true; +static PxVec3 worldToLocal(const PxObstacle& obstacle, const PxExtendedVec3& worldPos) +{ + const PxTransform tr(toVec3(obstacle.mPos), obstacle.mRot); + return tr.transformInv(toVec3(worldPos)); +} + +static PxVec3 localToWorld(const PxObstacle& obstacle, const PxVec3& localPos) +{ + const PxTransform tr(toVec3(obstacle.mPos), obstacle.mRot); + return tr.transform(localPos); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef PX_BIG_WORLDS + typedef PxExtendedBounds3 PxCCTBounds3; + typedef PxExtendedVec3 PxCCTVec3; +#else + typedef PxBounds3 PxCCTBounds3; + typedef PxVec3 PxCCTVec3; +#endif + +static PX_INLINE void scale(PxCCTBounds3& b, const PxVec3& scale) +{ + PxCCTVec3 center; getCenter(b, center); + PxVec3 extents; getExtents(b, extents); + extents.x *= scale.x; + extents.y *= scale.y; + extents.z *= scale.z; + setCenterExtents(b, center, extents); +} + +static PX_INLINE void computeReflexionVector(PxVec3& reflected, const PxVec3& incomingDir, const PxVec3& outwardNormal) +{ + reflected = incomingDir - outwardNormal * 2.0f * (incomingDir.dot(outwardNormal)); +} + +static PX_INLINE void collisionResponse(PxExtendedVec3& targetPosition, const PxExtendedVec3& currentPosition, const PxVec3& currentDir, const PxVec3& hitNormal, PxF32 bump, PxF32 friction, bool normalize=false) +{ + // Compute reflect direction + PxVec3 reflectDir; + computeReflexionVector(reflectDir, currentDir, hitNormal); + reflectDir.normalize(); + + // Decompose it + PxVec3 normalCompo, tangentCompo; + Ps::decomposeVector(normalCompo, tangentCompo, reflectDir, hitNormal); + + // Compute new destination position + const PxF32 amplitude = (targetPosition - currentPosition).magnitude(); + + targetPosition = currentPosition; + if(bump!=0.0f) + { + if(normalize) + normalCompo.normalize(); + targetPosition += normalCompo*bump*amplitude; + } + if(friction!=0.0f) + { + if(normalize) + tangentCompo.normalize(); + targetPosition += tangentCompo*friction*amplitude; + } +} + +static PX_INLINE void relocateBox(PxBoxGeometry& boxGeom, PxTransform& pose, const PxExtendedVec3& center, const PxVec3& extents, const PxExtendedVec3& origin, const PxQuat& quatFromUp) +{ + boxGeom.halfExtents = extents; + + pose.p.x = float(center.x - origin.x); + pose.p.y = float(center.y - origin.y); + pose.p.z = float(center.z - origin.z); + + pose.q = quatFromUp; +} + +static PX_INLINE void relocateBox(PxBoxGeometry& boxGeom, PxTransform& pose, const TouchedUserBox& userBox) +{ + relocateBox(boxGeom, pose, userBox.mBox.center, userBox.mBox.extents, userBox.mOffset, userBox.mBox.rot); +} + +static PX_INLINE void relocateBox(PxBoxGeometry& boxGeom, PxTransform& pose, const TouchedBox& box) +{ + boxGeom.halfExtents = box.mExtents; + + pose.p = box.mCenter; + pose.q = box.mRot; +} + +static PX_INLINE void relocateCapsule( + PxCapsuleGeometry& capsuleGeom, PxTransform& pose, const SweptCapsule* sc, + const PxQuat& quatFromUp, + const PxExtendedVec3& center, const PxExtendedVec3& origin) +{ + capsuleGeom.radius = sc->mRadius; + capsuleGeom.halfHeight = 0.5f * sc->mHeight; + + pose.p.x = float(center.x - origin.x); + pose.p.y = float(center.y - origin.y); + pose.p.z = float(center.z - origin.z); + + pose.q = quatFromUp; +} + +static PX_INLINE void relocateCapsule(PxCapsuleGeometry& capsuleGeom, PxTransform& pose, const PxVec3& p0, const PxVec3& p1, PxReal radius) +{ + capsuleGeom.radius = radius; + pose = PxTransformFromSegment(p0, p1, &capsuleGeom.halfHeight); + if(capsuleGeom.halfHeight==0.0f) + capsuleGeom.halfHeight = FLT_EPSILON; +} + +static PX_INLINE void relocateCapsule(PxCapsuleGeometry& capsuleGeom, PxTransform& pose, const TouchedUserCapsule& userCapsule) +{ + PxVec3 p0, p1; + p0.x = float(userCapsule.mCapsule.p0.x - userCapsule.mOffset.x); + p0.y = float(userCapsule.mCapsule.p0.y - userCapsule.mOffset.y); + p0.z = float(userCapsule.mCapsule.p0.z - userCapsule.mOffset.z); + p1.x = float(userCapsule.mCapsule.p1.x - userCapsule.mOffset.x); + p1.y = float(userCapsule.mCapsule.p1.y - userCapsule.mOffset.y); + p1.z = float(userCapsule.mCapsule.p1.z - userCapsule.mOffset.z); + + relocateCapsule(capsuleGeom, pose, p0, p1, userCapsule.mCapsule.radius); +} + +static bool SweepBoxUserBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_BOX); + const SweptBox* SB = static_cast(volume); + const TouchedUserBox* TC = static_cast(geom); + + PxBoxGeometry boxGeom0; + PxTransform boxPose0; + // To precompute + relocateBox(boxGeom0, boxPose0, center, SB->mExtents, TC->mOffset, test->mUserParams.mQuatFromUp); + + PxBoxGeometry boxGeom1; + PxTransform boxPose1; + relocateBox(boxGeom1, boxPose1, *TC); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom0, boxPose0, boxGeom1, boxPose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mWorldNormal = sweepHit.normal; + impact.mDistance = sweepHit.distance; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TC->mOffset); + return true; +} + +static bool SweepBoxUserCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_CAPSULE); + const SweptBox* SB = static_cast(volume); + const TouchedUserCapsule* TC = static_cast(geom); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, center, SB->mExtents, TC->mOffset, test->mUserParams.mQuatFromUp); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, *TC); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom, boxPose, capsuleGeom, capsulePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + // ### check this + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(Capsule, Box0.center, Box0.extents, Box0.rot, &t, &p); + Box0.rot.multiply(p,p); + impact.mWorldPos.x = p.x + Box0.center.x + TC->mOffset.x; + impact.mWorldPos.y = p.y + Box0.center.y + TC->mOffset.y; + impact.mWorldPos.z = p.z + Box0.center.z + TC->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TC->mOffset); + } + return true; +} + +static bool sweepVolumeVsMesh( const SweepTest* sweepTest, const TouchedMesh* touchedMesh, SweptContact& impact, + const PxVec3& unitDir, const PxGeometry& geom, const PxTransform& pose, + PxU32 nbTris, const PxTriangle* triangles, + PxU32 cachedIndex) +{ + PxSweepHit sweepHit; + if(PxMeshQuery::sweep(unitDir, impact.mDistance, geom, pose, nbTris, triangles, sweepHit, getSweepHitFlags(sweepTest->mUserParams), &cachedIndex)) + { + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.setWorldPos(sweepHit.position, touchedMesh->mOffset); + + // Returned index is only between 0 and nbTris, i.e. it indexes the array of cached triangles, not the original mesh. + PX_ASSERT(sweepHit.faceIndex < nbTris); + sweepTest->mCachedTriIndex[sweepTest->mCachedTriIndexIndex] = sweepHit.faceIndex; + + // The CCT loop will use the index from the start of the cache... + impact.mInternalIndex = sweepHit.faceIndex + touchedMesh->mIndexWorldTriangles; + const PxU32* triangleIndices = &sweepTest->mTriangleIndices[touchedMesh->mIndexWorldTriangles]; + impact.mTriangleIndex = triangleIndices[sweepHit.faceIndex]; + return true; + } + return false; +} + +static bool SweepBoxMesh(const SweepTest* sweep_test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eMESH); + const SweptBox* SB = static_cast(volume); + const TouchedMesh* TM = static_cast(geom); + + PxU32 nbTris = TM->mNbTris; + if(!nbTris) + return false; + + // Fetch triangle data for current mesh (the stream may contain triangles from multiple meshes) + const PxTriangle* T = &sweep_test->mWorldTriangles.getTriangle(TM->mIndexWorldTriangles); + + // PT: this only really works when the CCT collides with a single mesh, but that's the most common case. When it doesn't, there's just no speedup but it still works. + PxU32 CachedIndex = sweep_test->mCachedTriIndex[sweep_test->mCachedTriIndexIndex]; + if(CachedIndex>=nbTris) + CachedIndex=0; + + PxBoxGeometry boxGeom; + boxGeom.halfExtents = SB->mExtents; + PxTransform boxPose(PxVec3(float(center.x - TM->mOffset.x), float(center.y - TM->mOffset.y), float(center.z - TM->mOffset.z)), sweep_test->mUserParams.mQuatFromUp); // Precompute + return sweepVolumeVsMesh(sweep_test, TM, impact, dir, boxGeom, boxPose, nbTris, T, CachedIndex); +} + +static bool SweepCapsuleMesh( + const SweepTest* sweep_test, const SweptVolume* volume, const TouchedGeom* geom, + const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eMESH); + const SweptCapsule* SC = static_cast(volume); + const TouchedMesh* TM = static_cast(geom); + + PxU32 nbTris = TM->mNbTris; + if(!nbTris) + return false; + + // Fetch triangle data for current mesh (the stream may contain triangles from multiple meshes) + const PxTriangle* T = &sweep_test->mWorldTriangles.getTriangle(TM->mIndexWorldTriangles); + + // PT: this only really works when the CCT collides with a single mesh, but that's the most common case. + // When it doesn't, there's just no speedup but it still works. + PxU32 CachedIndex = sweep_test->mCachedTriIndex[sweep_test->mCachedTriIndexIndex]; + if(CachedIndex>=nbTris) + CachedIndex=0; + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, sweep_test->mUserParams.mQuatFromUp, center, TM->mOffset); + + return sweepVolumeVsMesh(sweep_test, TM, impact, dir, capsuleGeom, capsulePose, nbTris, T, CachedIndex); +} + +static bool SweepBoxBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eBOX); + const SweptBox* SB = static_cast(volume); + const TouchedBox* TB = static_cast(geom); + + PxBoxGeometry boxGeom0; + PxTransform boxPose0; + // To precompute + relocateBox(boxGeom0, boxPose0, center, SB->mExtents, TB->mOffset, test->mUserParams.mQuatFromUp); + + PxBoxGeometry boxGeom1; + PxTransform boxPose1; + relocateBox(boxGeom1, boxPose1, *TB); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom0, boxPose0, boxGeom1, boxPose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mWorldNormal = sweepHit.normal; + impact.mDistance = sweepHit.distance; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TB->mOffset); + return true; +} + +static bool SweepBoxSphere(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eSPHERE); + const SweptBox* SB = static_cast(volume); + const TouchedSphere* TS = static_cast(geom); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, center, SB->mExtents, TS->mOffset, test->mUserParams.mQuatFromUp); + + PxSphereGeometry sphereGeom; + sphereGeom.radius = TS->mRadius; + PxTransform spherePose; + spherePose.p = TS->mCenter; + spherePose.q = PxQuat(PxIdentity); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom, boxPose, sphereGeom, spherePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /* + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + PxVec3 NewSphereCenter = TS->mSphere.center - d * dir; + PxVec3 Closest; + gUtilLib->PxPointOBBSqrDist(NewSphereCenter, Box0.center, Box0.extents, Box0.rot, &Closest); + // Compute point on the box, after sweep + Box0.rot.multiply(Closest, Closest); + impact.mWorldPos.x = TS->mOffset.x + Closest.x + Box0.center.x + d * dir.x; + impact.mWorldPos.y = TS->mOffset.y + Closest.y + Box0.center.y + d * dir.y; + impact.mWorldPos.z = TS->mOffset.z + Closest.z + Box0.center.z + d * dir.z; + + impact.mWorldNormal = -impact.mWorldNormal; + }*/ + { + impact.setWorldPos(sweepHit.position, TS->mOffset); + } + return true; +} + +static bool SweepBoxCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eCAPSULE); + const SweptBox* SB = static_cast(volume); + const TouchedCapsule* TC = static_cast(geom); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, center, SB->mExtents, TC->mOffset, test->mUserParams.mQuatFromUp); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, TC->mP0, TC->mP1, TC->mRadius); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom, boxPose, capsuleGeom, capsulePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(TC->mCapsule, Box0.center, Box0.extents, Box0.rot, &t, &p); + Box0.rot.multiply(p,p); + impact.mWorldPos.x = p.x + Box0.center.x + TC->mOffset.x; + impact.mWorldPos.y = p.y + Box0.center.y + TC->mOffset.y; + impact.mWorldPos.z = p.z + Box0.center.z + TC->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TC->mOffset); + } + return true; +} + +static bool SweepCapsuleBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eBOX); + const SweptCapsule* SC = static_cast(volume); + const TouchedBox* TB = static_cast(geom); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, test->mUserParams.mQuatFromUp, center, TB->mOffset); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, *TB); + + // The box and capsule coordinates are relative to the center of the cached bounding box + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom, capsulePose, boxGeom, boxPose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(Capsule, TB->mBox.center, TB->mBox.extents, TB->mBox.rot, &t, &p); + TB->mBox.rot.multiply(p,p); + p += TB->mBox.center; + impact.mWorldPos.x = p.x + TB->mOffset.x; + impact.mWorldPos.y = p.y + TB->mOffset.y; + impact.mWorldPos.z = p.z + TB->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TB->mOffset); + } + return true; +} + +static bool SweepCapsuleSphere(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eSPHERE); + const SweptCapsule* SC = static_cast(volume); + const TouchedSphere* TS = static_cast(geom); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, test->mUserParams.mQuatFromUp, center, TS->mOffset); + + PxSphereGeometry sphereGeom; + sphereGeom.radius = TS->mRadius; + PxTransform spherePose; + spherePose.p = TS->mCenter; + spherePose.q = PxQuat(PxIdentity); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom, capsulePose, sphereGeom, spherePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TS->mOffset); + return true; +} + +static bool SweepCapsuleCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eCAPSULE); + const SweptCapsule* SC = static_cast(volume); + const TouchedCapsule* TC = static_cast(geom); + + PxCapsuleGeometry capsuleGeom0; + PxTransform capsulePose0; + relocateCapsule(capsuleGeom0, capsulePose0, SC, test->mUserParams.mQuatFromUp, center, TC->mOffset); + + PxCapsuleGeometry capsuleGeom1; + PxTransform capsulePose1; + relocateCapsule(capsuleGeom1, capsulePose1, TC->mP0, TC->mP1, TC->mRadius); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom0, capsulePose0, capsuleGeom1, capsulePose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TC->mOffset); + return true; +} + +static bool SweepCapsuleUserCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_CAPSULE); + const SweptCapsule* SC = static_cast(volume); + const TouchedUserCapsule* TC = static_cast(geom); + + PxCapsuleGeometry capsuleGeom0; + PxTransform capsulePose0; + relocateCapsule(capsuleGeom0, capsulePose0, SC, test->mUserParams.mQuatFromUp, center, TC->mOffset); + + PxCapsuleGeometry capsuleGeom1; + PxTransform capsulePose1; + relocateCapsule(capsuleGeom1, capsulePose1, *TC); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom0, capsulePose0, capsuleGeom1, capsulePose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TC->mOffset); + return true; +} + +static bool SweepCapsuleUserBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_BOX); + const SweptCapsule* SC = static_cast(volume); + const TouchedUserBox* TB = static_cast(geom); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, test->mUserParams.mQuatFromUp, center, TB->mOffset); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + relocateBox(boxGeom, boxPose, *TB); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom, capsulePose, boxGeom, boxPose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + // ### check this + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(Capsule, Box.center, Box.extents, Box.rot, &t, &p); + p += Box.center; + impact.mWorldPos.x = p.x + TB->mOffset.x; + impact.mWorldPos.y = p.y + TB->mOffset.y; + impact.mWorldPos.z = p.z + TB->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TB->mOffset); + } + return true; +} + +typedef bool (*SweepFunc) (const SweepTest*, const SweptVolume*, const TouchedGeom*, const PxExtendedVec3&, const PxVec3&, SweptContact&); + +static SweepFunc gSweepMap[SweptVolumeType::eLAST][TouchedGeomType::eLAST] = { + // Box funcs + { + SweepBoxUserBox, + SweepBoxUserCapsule, + SweepBoxMesh, + SweepBoxBox, + SweepBoxSphere, + SweepBoxCapsule + }, + + // Capsule funcs + { + SweepCapsuleUserBox, + SweepCapsuleUserCapsule, + SweepCapsuleMesh, + SweepCapsuleBox, + SweepCapsuleSphere, + SweepCapsuleCapsule + } +}; + +PX_COMPILE_TIME_ASSERT(sizeof(gSweepMap)==SweptVolumeType::eLAST*TouchedGeomType::eLAST*sizeof(SweepFunc)); + +static const PxU32 GeomSizes[] = +{ + sizeof(TouchedUserBox), + sizeof(TouchedUserCapsule), + sizeof(TouchedMesh), + sizeof(TouchedBox), + sizeof(TouchedSphere), + sizeof(TouchedCapsule), +}; + +static const TouchedGeom* CollideGeoms( + const SweepTest* sweep_test, const SweptVolume& volume, const IntArray& geom_stream, + const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact, bool discardInitialOverlap) +{ + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.mGeom = NULL; + + const PxU32* Data = geom_stream.begin(); + const PxU32* Last = geom_stream.end(); + while(Data!=Last) + { + const TouchedGeom* CurrentGeom = reinterpret_cast(Data); + + SweepFunc ST = gSweepMap[volume.getType()][CurrentGeom->mType]; + if(ST) + { + SweptContact C; + C.mDistance = impact.mDistance; // Initialize with current best distance + C.mInternalIndex = PX_INVALID_U32; + C.mTriangleIndex = PX_INVALID_U32; + if((ST)(sweep_test, &volume, CurrentGeom, center, dir, C)) + { + if(C.mDistance==0.0f) + { + if(!discardInitialOverlap) + { + if(CurrentGeom->mType==TouchedGeomType::eUSER_BOX || CurrentGeom->mType==TouchedGeomType::eUSER_CAPSULE) + { + } + else + { + const PxRigidActor* touchedActor = CurrentGeom->mActor; + PX_ASSERT(touchedActor); + + if(shouldApplyRecoveryModule(*touchedActor)) + { + impact = C; + impact.mGeom = const_cast(CurrentGeom); + return CurrentGeom; + } + } + } + } +/* else + if(discardInitialOverlap && C.mDistance==0.0f) + { + // PT: we previously used eINITIAL_OVERLAP without eINITIAL_OVERLAP_KEEP, i.e. initially overlapping shapes got ignored. + // So we replicate this behavior here. + }*/ + else if(C.mDistance(CurrentGeom); + if(C.mDistance <= 0.0f) // there is no point testing for closer hits + return CurrentGeom; // since we are touching a shape already + } + } + } + + const PxU8* ptr = reinterpret_cast(Data); + ptr += GeomSizes[CurrentGeom->mType]; + Data = reinterpret_cast(ptr); + } + return impact.mGeom; +} + +static PxVec3 computeMTD(const SweepTest* sweep_test, const SweptVolume& volume, const IntArray& geom_stream, const PxExtendedVec3& center, float contactOffset) +{ + PxVec3 p = toVec3(center); + +// contactOffset += 0.01f; + + const PxU32 maxIter = 4; + PxU32 nbIter = 0; + bool isValid = true; + while(isValid && nbIter(Data); + + if(CurrentGeom->mType==TouchedGeomType::eUSER_BOX || CurrentGeom->mType==TouchedGeomType::eUSER_CAPSULE) + { + } + else + { + const PxRigidActor* touchedActor = CurrentGeom->mActor; + PX_ASSERT(touchedActor); + + if(shouldApplyRecoveryModule(*touchedActor)) + { + const PxShape* touchedShape = reinterpret_cast(CurrentGeom->mTGUserData); + PX_ASSERT(touchedShape); + + const PxGeometryHolder gh = touchedShape->getGeometry(); + const PxTransform globalPose = getShapeGlobalPose(*touchedShape, *touchedActor); + + PxVec3 mtd; + PxF32 depth; + + const PxTransform volumePose(p, sweep_test->mUserParams.mQuatFromUp); + if(volume.getType()==SweptVolumeType::eCAPSULE) + { + const SweptCapsule& sc = static_cast(volume); + const PxCapsuleGeometry capsuleGeom(sc.mRadius+contactOffset, sc.mHeight*0.5f); + isValid = PxGeometryQuery::computePenetration(mtd, depth, capsuleGeom, volumePose, gh.any(), globalPose); + } + else + { + PX_ASSERT(volume.getType()==SweptVolumeType::eBOX); + const SweptBox& sb = static_cast(volume); + const PxBoxGeometry boxGeom(sb.mExtents+PxVec3(contactOffset)); + isValid = PxGeometryQuery::computePenetration(mtd, depth, boxGeom, volumePose, gh.any(), globalPose); + } + + if(isValid) + { + nbIter++; + PX_ASSERT(depth>=0.0f); + PX_ASSERT(mtd.isFinite()); + PX_ASSERT(PxIsFinite(depth)); +#ifdef DEBUG_MTD + PX_ASSERT(depth<=1.0f); + if(depth>1.0f || !mtd.isFinite() || !PxIsFinite(depth)) + { + int stop=1; + (void)stop; + } + printf("Depth: %f\n", depth); + printf("mtd: %f %f %f\n", mtd.x, mtd.y, mtd.z); +#endif + p += mtd * depth; + } + } + } + + const PxU8* ptr = reinterpret_cast(Data); + ptr += GeomSizes[CurrentGeom->mType]; + Data = reinterpret_cast(ptr); + } + } + return p; +} + + + +static bool ParseGeomStream(const void* object, const IntArray& geom_stream) +{ + const PxU32* Data = geom_stream.begin(); + const PxU32* Last = geom_stream.end(); + while(Data!=Last) + { + const TouchedGeom* CurrentGeom = reinterpret_cast(Data); + if(CurrentGeom->mTGUserData==object) + return true; + + const PxU8* ptr = reinterpret_cast(Data); + ptr += GeomSizes[CurrentGeom->mType]; + Data = reinterpret_cast(ptr); + } + return false; +} + +CCTParams::CCTParams() : + mNonWalkableMode (PxControllerNonWalkableMode::ePREVENT_CLIMBING), + mQuatFromUp (PxQuat(PxIdentity)), + mUpDirection (PxVec3(0.0f)), + mSlopeLimit (0.0f), + mContactOffset (0.0f), + mStepOffset (0.0f), + mInvisibleWallHeight (0.0f), + mMaxJumpHeight (0.0f), + mMaxEdgeLength2 (0.0f), + mTessellation (false), + mHandleSlope (false), + mOverlapRecovery (false), + mPreciseSweeps (true), + mPreventVerticalSlidingAgainstCeiling (false) +{ +} + +SweepTest::SweepTest(bool registerDeletionListener) : + mRenderBuffer (NULL), + mRenderFlags (0), + mTriangleIndices (PX_DEBUG_EXP("sweepTestTriangleIndices")), + mGeomStream (PX_DEBUG_EXP("sweepTestStream")), + mTouchedShape (registerDeletionListener), + mTouchedActor (registerDeletionListener), + mSQTimeStamp (0xffffffff), + mNbFullUpdates (0), + mNbPartialUpdates (0), + mNbTessellation (0), + mNbIterations (0), + mFlags (0), + mRegisterDeletionListener(registerDeletionListener), + mCctManager (NULL) +{ + mCacheBounds.setEmpty(); + mCachedTriIndexIndex = 0; + mCachedTriIndex[0] = mCachedTriIndex[1] = mCachedTriIndex[2] = 0; + mNbCachedStatic = 0; + mNbCachedT = 0; + + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + mTouchedPos = PxVec3(0); + mTouchedPosShape_Local = PxVec3(0); + mTouchedPosShape_World = PxVec3(0); + mTouchedPosObstacle_Local = PxVec3(0); + mTouchedPosObstacle_World = PxVec3(0); + +// mVolumeGrowth = 1.2f; // Must be >1.0f and not too big + mVolumeGrowth = 1.5f; // Must be >1.0f and not too big +// mVolumeGrowth = 2.0f; // Must be >1.0f and not too big + + mContactNormalDownPass = PxVec3(0.0f); + mContactNormalSidePass = PxVec3(0.0f); + mTouchedTriMin = 0.0f; + mTouchedTriMax = 0.0f; +} + + +SweepTest::~SweepTest() +{ + // set the TouchedObject to NULL so we unregister the actor/shape + mTouchedShape = NULL; + mTouchedActor = NULL; +} + +void SweepTest::voidTestCache() +{ + mTouchedShape = NULL; + mTouchedActor = NULL; + mCacheBounds.setEmpty(); + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; +} + +void SweepTest::onRelease(const PxBase& observed) +{ + if (mTouchedActor == &observed) + { + mTouchedShape = NULL; + mTouchedActor = NULL; + return; + } + + if(ParseGeomStream(&observed, mGeomStream)) + mCacheBounds.setEmpty(); + + if (mTouchedShape == &observed) + mTouchedShape = NULL; +} + +void SweepTest::updateCachedShapesRegistration(PxU32 startIndex, bool unregister) +{ + if(!mRegisterDeletionListener) + return; + + if(!mGeomStream.size() || startIndex == mGeomStream.size()) + return; + + PX_ASSERT(startIndex <= mGeomStream.size()); + + const PxU32* data = &mGeomStream[startIndex]; + const PxU32* last = mGeomStream.end(); + while (data != last) + { + const TouchedGeom* CurrentGeom = reinterpret_cast(data); + if (CurrentGeom->mActor) + { + if(unregister) + mCctManager->unregisterObservedObject(reinterpret_cast(CurrentGeom->mTGUserData)); + else + mCctManager->registerObservedObject(reinterpret_cast(CurrentGeom->mTGUserData)); + } + else + { + // we can early exit, the rest of the data are user obstacles + return; + } + + const PxU8* ptr = reinterpret_cast(data); + ptr += GeomSizes[CurrentGeom->mType]; + data = reinterpret_cast(ptr); + } +} + +void SweepTest::onObstacleAdded(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance ) +{ + if(mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE) + { + // check if new obstacle is closer + const ObstacleContext* obstContext = static_cast (context); + PxRaycastHit obstacleHit; + const PxObstacle* obst = obstContext->raycastSingle(obstacleHit,index,origin,unitDir,distance); + + if(obst && (obstacleHit.position.dot(unitDir))<(mTouchedPosObstacle_World.dot(unitDir))) + { + PX_ASSERT(obstacleHit.distance<=distance); + mTouchedObstacleHandle = index; + if(!gUseLocalSpace) + { + mTouchedPos = toVec3(obst->mPos); + } + else + { + mTouchedPosObstacle_World = obstacleHit.position; + mTouchedPosObstacle_Local = worldToLocal(*obst, PxExtendedVec3(PxExtended(obstacleHit.position.x),PxExtended(obstacleHit.position.y),PxExtended(obstacleHit.position.z))); + } + } + } +} + +void SweepTest::onObstacleRemoved(ObstacleHandle index) +{ + if(index == mTouchedObstacleHandle) + { + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + } +} + +void SweepTest::onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance) +{ + if(index == mTouchedObstacleHandle) + { + // check if updated obstacle is still closest + const ObstacleContext* obstContext = static_cast (context); + PxRaycastHit obstacleHit; + ObstacleHandle closestHandle = INVALID_OBSTACLE_HANDLE; + const PxObstacle* obst = obstContext->raycastSingle(obstacleHit,origin,unitDir,distance,closestHandle); + + if(mTouchedObstacleHandle == closestHandle) + return; + + if(obst) + { + PX_ASSERT(obstacleHit.distance<=distance); + mTouchedObstacleHandle = closestHandle; + if(!gUseLocalSpace) + { + mTouchedPos = toVec3(obst->mPos); + } + else + { + mTouchedPosObstacle_World = obstacleHit.position; + mTouchedPosObstacle_Local = worldToLocal(*obst, PxExtendedVec3(PxExtended(obstacleHit.position.x),PxExtended(obstacleHit.position.y),PxExtended(obstacleHit.position.z))); + } + } + } +} + +void SweepTest::onOriginShift(const PxVec3& shift) +{ + mCacheBounds.minimum -= shift; + mCacheBounds.maximum -= shift; + + if(mTouchedShape) + { + const PxRigidActor* rigidActor = mTouchedActor.get(); + if(rigidActor->getConcreteType() != PxConcreteType::eRIGID_STATIC) + { + mTouchedPosShape_World -= shift; + } + } + else if (mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE) + { + if(!gUseLocalSpace) + { + mTouchedPos -= shift; + } + else + { + mTouchedPosObstacle_World -= shift; + } + } + + // adjust cache + PxU32* data = mGeomStream.begin(); + PxU32* last = mGeomStream.end(); + while(data != last) + { + TouchedGeom* currentGeom = reinterpret_cast(data); + + currentGeom->mOffset -= shift; + + PxU8* ptr = reinterpret_cast(data); + ptr += GeomSizes[currentGeom->mType]; + data = reinterpret_cast(ptr); + } +} + +static PxBounds3 getBounds3(const PxExtendedBounds3& extended) +{ + return PxBounds3(toVec3(extended.minimum), toVec3(extended.maximum)); // LOSS OF ACCURACY +} + +// PT: finds both touched CCTs and touched user-defined obstacles +void SweepTest::findTouchedObstacles(const UserObstacles& userObstacles, const PxExtendedBounds3& worldBox) +{ + PxExtendedVec3 Origin; // Will be TouchedGeom::mOffset + getCenter(worldBox, Origin); + + { + const PxU32 nbBoxes = userObstacles.mNbBoxes; + const PxExtendedBox* boxes = userObstacles.mBoxes; + const void** boxUserData = userObstacles.mBoxUserData; + + const PxBounds3 singlePrecisionWorldBox = getBounds3(worldBox); + + // Find touched boxes, i.e. other box controllers + for(PxU32 i=0;i(reserveContainerMemory(mGeomStream, sizeof(TouchedUserBox)/sizeof(PxU32))); + UserBox->mType = TouchedGeomType::eUSER_BOX; + UserBox->mTGUserData = boxUserData[i]; + UserBox->mActor = NULL; + UserBox->mOffset = Origin; + UserBox->mBox = boxes[i]; + } + } + + { + // Find touched capsules, i.e. other capsule controllers + const PxU32 nbCapsules = userObstacles.mNbCapsules; + const PxExtendedCapsule* capsules = userObstacles.mCapsules; + const void** capsuleUserData = userObstacles.mCapsuleUserData; + + PxExtendedVec3 Center; + PxVec3 Extents; + getCenter(worldBox, Center); + getExtents(worldBox, Extents); + + for(PxU32 i=0;i worldBox.maximum.x) || (worldBox.minimum.x > capMaxx + PxExtended(r))) continue; + + const PxExtended capMiny = PxMin(capsules[i].p0.y, capsules[i].p1.y); + const PxExtended capMaxy = PxMax(capsules[i].p0.y, capsules[i].p1.y); + if((capMiny - PxExtended(r) > worldBox.maximum.y) || (worldBox.minimum.y > capMaxy + PxExtended(r))) continue; + + const PxExtended capMinz = PxMin(capsules[i].p0.z, capsules[i].p1.z); + const PxExtended capMaxz = PxMax(capsules[i].p0.z, capsules[i].p1.z); + if((capMinz - PxExtended(r) > worldBox.maximum.z) || (worldBox.minimum.z > capMaxz + PxExtended(r))) continue; + + // PT: more accurate capsule-box test. Not strictly necessary but worth doing if available + const PxReal d2 = Gu::distanceSegmentBoxSquared(toVec3(capsules[i].p0), toVec3(capsules[i].p1), toVec3(Center), Extents, PxMat33(PxIdentity)); + if(d2>r*r) + continue; + + TouchedUserCapsule* UserCapsule = reinterpret_cast(reserveContainerMemory(mGeomStream, sizeof(TouchedUserCapsule)/sizeof(PxU32))); + UserCapsule->mType = TouchedGeomType::eUSER_CAPSULE; + UserCapsule->mTGUserData = capsuleUserData[i]; + UserCapsule->mActor = NULL; + UserCapsule->mOffset = Origin; + UserCapsule->mCapsule = capsules[i]; + } + } +} + +void SweepTest::updateTouchedGeoms( const InternalCBData_FindTouchedGeom* userData, const UserObstacles& userObstacles, + const PxExtendedBounds3& worldTemporalBox, const PxControllerFilters& filters, const PxVec3& sideVector) +{ + /* + - if this is the first iteration (new frame) we have to redo the dynamic objects & the CCTs. The static objects can + be cached. + - if this is not, we can cache everything + */ + + // PT: using "worldTemporalBox" instead of "mCacheBounds" seems to produce TTP 6207 +//#define DYNAMIC_BOX worldTemporalBox +#define DYNAMIC_BOX mCacheBounds + + bool newCachedBox = false; + + CCTFilter filter; + filter.mFilterData = filters.mFilterData; + filter.mFilterCallback = filters.mFilterCallback; + filter.mPreFilter = filters.mFilterFlags & PxQueryFlag::ePREFILTER; + filter.mPostFilter = filters.mFilterFlags & PxQueryFlag::ePOSTFILTER; + + // PT: detect changes to the static pruning structure + bool sceneHasChanged = false; + { + const PxU32 currentTimestamp = getSceneTimestamp(userData); + if(currentTimestamp!=mSQTimeStamp) + { + mSQTimeStamp = currentTimestamp; + sceneHasChanged = true; + } + } + + // If the input box is inside the cached box, nothing to do + if(gUsePartialUpdates && !sceneHasChanged && worldTemporalBox.isInside(mCacheBounds)) + { + //printf("CACHEIN%d\n", mFirstUpdate); + if(mFlags & STF_FIRST_UPDATE) + { + mFlags &= ~STF_FIRST_UPDATE; + + // Only redo the dynamic + updateCachedShapesRegistration(mNbCachedStatic, true); + mGeomStream.forceSize_Unsafe(mNbCachedStatic); + mWorldTriangles.forceSize_Unsafe(mNbCachedT); + mTriangleIndices.forceSize_Unsafe(mNbCachedT); + + filter.mStaticShapes = false; + if(filters.mFilterFlags & PxQueryFlag::eDYNAMIC) + filter.mDynamicShapes = true; + findTouchedGeometry(userData, DYNAMIC_BOX, mWorldTriangles, mTriangleIndices, mGeomStream, filter, mUserParams, mNbTessellation); + updateCachedShapesRegistration(mNbCachedStatic, false); + + findTouchedObstacles(userObstacles, DYNAMIC_BOX); + + mNbPartialUpdates++; + } + } + else + { + //printf("CACHEOUTNS=%d\n", mNbCachedStatic); + newCachedBox = true; + + // Cache BV used for the query + mCacheBounds = worldTemporalBox; + + // Grow the volume a bit. The temporal box here doesn't take sliding & collision response into account. + // In bad cases it is possible to eventually touch a portion of space not covered by this volume. Just + // in case, we grow the initial volume slightly. Then, additional tests are performed within the loop + // to make sure the TBV is always correct. There's a tradeoff between the original (artificial) growth + // of the volume, and the number of TBV recomputations performed at runtime... + scale(mCacheBounds, PxVec3(mVolumeGrowth)); +// scale(mCacheBounds, PxVec3(mVolumeGrowth, 1.0f, mVolumeGrowth)); + + if(1 && !sideVector.isZero()) + { + const PxVec3 sn = sideVector.getNormalized(); + float dp0 = PxAbs((worldTemporalBox.maximum - worldTemporalBox.minimum).dot(sn)); + float dp1 = PxAbs((mCacheBounds.maximum - mCacheBounds.minimum).dot(sn)); + dp1 -= dp0; + dp1 *= 0.5f * 0.9f; + const PxVec3 offset = sn * dp1; +// printf("%f %f %f\n", offset.x, offset.y, offset.z); + mCacheBounds.minimum += offset; + mCacheBounds.maximum += offset; + add(mCacheBounds, worldTemporalBox); + PX_ASSERT(worldTemporalBox.isInside(mCacheBounds)); + } + + updateCachedShapesRegistration(0, true); + + // Gather triangles touched by this box. This covers multiple meshes. + mWorldTriangles.clear(); + mTriangleIndices.clear(); + mGeomStream.clear(); +// mWorldTriangles.reset(); +// mTriangleIndices.reset(); +// mGeomStream.reset(); + + mCachedTriIndexIndex = 0; + mCachedTriIndex[0] = mCachedTriIndex[1] = mCachedTriIndex[2] = 0; + + mNbFullUpdates++; + + if(filters.mFilterFlags & PxQueryFlag::eSTATIC) + filter.mStaticShapes = true; + filter.mDynamicShapes = false; + findTouchedGeometry(userData, mCacheBounds, mWorldTriangles, mTriangleIndices, mGeomStream, filter, mUserParams, mNbTessellation); + + mNbCachedStatic = mGeomStream.size(); + mNbCachedT = mWorldTriangles.size(); + PX_ASSERT(mTriangleIndices.size()==mNbCachedT); + + filter.mStaticShapes = false; + if(filters.mFilterFlags & PxQueryFlag::eDYNAMIC) + filter.mDynamicShapes = true; + findTouchedGeometry(userData, DYNAMIC_BOX, mWorldTriangles, mTriangleIndices, mGeomStream, filter, mUserParams, mNbTessellation); + // We can't early exit when no tris are touched since we also have to handle the boxes + updateCachedShapesRegistration(0, false); + + findTouchedObstacles(userObstacles, DYNAMIC_BOX); + + mFlags &= ~STF_FIRST_UPDATE; + //printf("CACHEOUTNSDONE=%d\n", mNbCachedStatic); + } + + if(mRenderBuffer) + { + // PT: worldTemporalBox = temporal BV for this frame + RenderOutput out(*mRenderBuffer); + + if(mRenderFlags & PxControllerDebugRenderFlag::eTEMPORAL_BV) + { + out << gTBVDebugColor; + out << DebugBox(getBounds3(worldTemporalBox)); + } + + if(mRenderFlags & PxControllerDebugRenderFlag::eCACHED_BV) + { + if(newCachedBox) + out << PxU32(PxDebugColor::eARGB_RED); + else + out << PxU32(PxDebugColor::eARGB_GREEN); + out << DebugBox(getBounds3(mCacheBounds)); + } + } +} + +// This is the generic sweep test for all swept volumes, but not character-controller specific +bool SweepTest::doSweepTest(const InternalCBData_FindTouchedGeom* userData, + InternalCBData_OnHit* userHitData, + const UserObstacles& userObstacles, + SweptVolume& swept_volume, + const PxVec3& direction, const PxVec3& sideVector, PxU32 max_iter, PxU32* nb_collisions, + float min_dist, const PxControllerFilters& filters, SweepPass sweepPass, + const PxRigidActor*& touchedActorOut, const PxShape*& touchedShapeOut, PxU64 contextID) +{ + // Early exit when motion is zero. Since the motion is decomposed into several vectors + // and this function is called for each of them, it actually happens quite often. + if(direction.isZero()) + return false; + + PX_PROFILE_ZONE("CharacterController.doSweepTest", contextID); + PX_UNUSED(contextID); + + bool hasMoved = false; + mFlags &= ~(STF_VALIDATE_TRIANGLE_DOWN|STF_TOUCH_OTHER_CCT|STF_TOUCH_OBSTACLE); + touchedShapeOut = NULL; + touchedActorOut = NULL; + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + + PxExtendedVec3 currentPosition = swept_volume.mCenter; + PxExtendedVec3 targetOrientation = swept_volume.mCenter; + targetOrientation += direction; + + PxU32 NbCollisions = 0; + while(max_iter--) + { + mNbIterations++; + // Compute current direction + PxVec3 currentDirection = targetOrientation - currentPosition; + + // Make sure the new TBV is still valid + { + // Compute temporal bounding box. We could use a capsule or an OBB instead: + // - the volume would be smaller + // - but the query would be slower + // Overall it's unclear whether it's worth it or not. + // TODO: optimize this part ? + PxExtendedBounds3 temporalBox; + swept_volume.computeTemporalBox(*this, temporalBox, currentPosition, currentDirection); + + // Gather touched geoms + updateTouchedGeoms(userData, userObstacles, temporalBox, filters, sideVector); + } + + const float Length = currentDirection.magnitude(); + if(Length<=min_dist) //Use <= to handle the case where min_dist is zero. + break; + + currentDirection /= Length; + + // From Quake2: "if velocity is against the original velocity, stop dead to avoid tiny occilations in sloping corners" + if((currentDirection.dot(direction)) <= 0.0f) + break; + + // From this point, we're going to update the position at least once + hasMoved = true; + + // Find closest collision + SweptContact C; + C.mDistance = Length + mUserParams.mContactOffset; + + if(!CollideGeoms(this, swept_volume, mGeomStream, currentPosition, currentDirection, C, !mUserParams.mOverlapRecovery)) + { + // no collision found => move to desired position + currentPosition = targetOrientation; + break; + } + + PX_ASSERT(C.mGeom); // If we reach this point, we must have touched a geom + + if(mUserParams.mOverlapRecovery && C.mDistance==0.0f) + { +/* SweptContact C; + C.mDistance = 10.0f; + CollideGeoms(this, swept_volume, mGeomStream, currentPosition, -currentDirection, C, true); + currentPosition -= currentDirection*C.mDistance; + + C.mDistance = 10.0f; + CollideGeoms(this, swept_volume, mGeomStream, currentPosition, currentDirection, C, true); + const float DynSkin = mUserParams.mContactOffset; + if(C.mDistance>DynSkin) + currentPosition += currentDirection*(C.mDistance-DynSkin);*/ + + const PxVec3 mtd = computeMTD(this, swept_volume, mGeomStream, currentPosition, mUserParams.mContactOffset); + + NbCollisions++; + + if(nb_collisions) + *nb_collisions = NbCollisions; + +#ifdef DEBUG_MTD + printf("MTD FIXUP: %f %f %f\n", mtd.x - swept_volume.mCenter.x, mtd.y - swept_volume.mCenter.y, mtd.z - swept_volume.mCenter.z); +#endif + swept_volume.mCenter.x = PxExtended(mtd.x); + swept_volume.mCenter.y = PxExtended(mtd.y); + swept_volume.mCenter.z = PxExtended(mtd.z); + return hasMoved; +// currentPosition.x = mtd.x; +// currentPosition.y = mtd.y; +// currentPosition.z = mtd.z; +// continue; + } + + bool preventVerticalMotion = false; + bool stopSliding = true; + if(C.mGeom->mType==TouchedGeomType::eUSER_BOX || C.mGeom->mType==TouchedGeomType::eUSER_CAPSULE) + { + if(sweepPass!=SWEEP_PASS_SENSOR) + { + // We touched a user object, typically another CCT, but can also be a user-defined obstacle + + // PT: TODO: technically lines marked with (*) shouldn't be here... revisit later + + const PxObstacle* touchedObstacle = NULL; // (*) + ObstacleHandle touchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + // if(mValidateCallback) + { + PxInternalCBData_OnHit* internalData = static_cast(userHitData); // (*) + internalData->touchedObstacle = NULL; // (*) + internalData->touchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + const PxU32 behaviorFlags = userHitCallback(userHitData, C, currentDirection, Length); + stopSliding = (behaviorFlags & PxControllerBehaviorFlag::eCCT_SLIDE)==0; // (*) + touchedObstacle = internalData->touchedObstacle; // (*) + touchedObstacleHandle = internalData->touchedObstacleHandle; + } + // printf("INTERNAL: %d\n", int(touchedObstacle)); + + if(sweepPass==SWEEP_PASS_DOWN) + { + // (*) + if(touchedObstacle) + { + mFlags |= STF_TOUCH_OBSTACLE; + + mTouchedObstacleHandle = touchedObstacleHandle; + if(!gUseLocalSpace) + { + mTouchedPos = toVec3(touchedObstacle->mPos); + } + else + { + mTouchedPosObstacle_World = toVec3(C.mWorldPos); + mTouchedPosObstacle_Local = worldToLocal(*touchedObstacle, C.mWorldPos); + } + } + else + { + mFlags |= STF_TOUCH_OTHER_CCT; + } + } + } + } + else + { + const PxShape* touchedShape = reinterpret_cast(C.mGeom->mTGUserData); + PX_ASSERT(touchedShape); + const PxRigidActor* touchedActor = C.mGeom->mActor; + PX_ASSERT(touchedActor); + + // We touched a normal object + if(sweepPass==SWEEP_PASS_DOWN) + { + mFlags &= ~(STF_TOUCH_OTHER_CCT|STF_TOUCH_OBSTACLE); + +#ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST + mFlags |= STF_VALIDATE_TRIANGLE_DOWN; + mContactNormalDownPass = C.mWorldNormal; +#else + + // Work out if the shape is attached to a static or dynamic actor. + // The slope limit is currently only considered when walking on static actors. + // It is ignored for shapes attached attached to dynamics and kinematics. + // TODO: 1. should we treat stationary kinematics the same as statics. + // 2. should we treat all kinematics the same as statics. + // 3. should we treat no kinematics the same as statics. + if((touchedActor->getConcreteType() == PxConcreteType::eRIGID_STATIC) && (C.mInternalIndex!=PX_INVALID_U32)) + { + mFlags |= STF_VALIDATE_TRIANGLE_DOWN; + const PxTriangle& touchedTri = mWorldTriangles.getTriangle(C.mInternalIndex); + const PxVec3& upDirection = mUserParams.mUpDirection; + const float dp0 = touchedTri.verts[0].dot(upDirection); + const float dp1 = touchedTri.verts[1].dot(upDirection); + const float dp2 = touchedTri.verts[2].dot(upDirection); + float dpmin = dp0; + dpmin = physx::intrinsics::selectMin(dpmin, dp1); + dpmin = physx::intrinsics::selectMin(dpmin, dp2); + float dpmax = dp0; + dpmax = physx::intrinsics::selectMax(dpmax, dp1); + dpmax = physx::intrinsics::selectMax(dpmax, dp2); + + PxExtendedVec3 cacheCenter; + getCenter(mCacheBounds, cacheCenter); + const float offset = upDirection.dot(toVec3(cacheCenter)); + mTouchedTriMin = dpmin + offset; + mTouchedTriMax = dpmax + offset; + + touchedTri.normal(mContactNormalDownPass); + } +#endif + // Update touched shape in down pass + touchedShapeOut = const_cast(touchedShape); + touchedActorOut = touchedActor; +// mTouchedPos = getShapeGlobalPose(*touchedShape).p; + const PxTransform shapeTransform = getShapeGlobalPose(*touchedShape, *touchedActor); + const PxVec3 worldPos = toVec3(C.mWorldPos); + mTouchedPosShape_World = worldPos; + mTouchedPosShape_Local = shapeTransform.transformInv(worldPos); + } + else if(sweepPass==SWEEP_PASS_SIDE || sweepPass==SWEEP_PASS_SENSOR) + { + if((touchedActor->getConcreteType() == PxConcreteType::eRIGID_STATIC) && (C.mInternalIndex!=PX_INVALID_U32)) + { + mFlags |= STF_VALIDATE_TRIANGLE_SIDE; + const PxTriangle& touchedTri = mWorldTriangles.getTriangle(C.mInternalIndex); + touchedTri.normal(mContactNormalSidePass); +// printf("%f | %f | %f\n", mContactNormalSidePass.x, mContactNormalSidePass.y, mContactNormalSidePass.z); + if(mUserParams.mPreventVerticalSlidingAgainstCeiling && mContactNormalSidePass.dot(mUserParams.mUpDirection)<0.0f) + preventVerticalMotion = true; + } + } + + if(sweepPass!=SWEEP_PASS_SENSOR) +// if(mValidateCallback) + { + const PxU32 behaviorFlags = shapeHitCallback(userHitData, C, currentDirection, Length); + stopSliding = (behaviorFlags & PxControllerBehaviorFlag::eCCT_SLIDE)==0; // (*) + } + } + + if(sweepPass==SWEEP_PASS_DOWN && !stopSliding) + { + // Trying to solve the following problem: + // - by default, the CCT "friction" is infinite, i.e. a CCT will not slide on a slope (this is by design) + // - this produces bad results when a capsule CCT stands on top of another capsule CCT, without sliding. Visually it looks + // like the character is standing on the other character's head, it looks bad. So, here, we would like to let the CCT + // slide away, i.e. we don't want friction. + // So here we simply increase the number of iterations (== let the CCT slide) when the first down collision is with another CCT. + if(!NbCollisions) + max_iter += 9; +// max_iter += 1; + } + + NbCollisions++; +// mContactPointHeight = (float)C.mWorldPos[mUserParams.mUpDirection]; // UBI + mContactPointHeight = toVec3(C.mWorldPos).dot(mUserParams.mUpDirection); // UBI + + const float DynSkin = mUserParams.mContactOffset; + + if(C.mDistance>DynSkin/*+0.01f*/) + currentPosition += currentDirection*(C.mDistance-DynSkin); +// DE6513 +/* else if(sweepPass==SWEEP_PASS_SIDE) + { + // Might be better to do a proper sweep pass here, in the opposite direction + currentPosition += currentDirection*(C.mDistance-DynSkin); + }*/ +//~DE6513 + + PxVec3 WorldNormal = C.mWorldNormal; + if(preventVerticalMotion || ((mFlags & STF_WALK_EXPERIMENT) && (mUserParams.mNonWalkableMode!=PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING))) + { + // Make sure the auto-step doesn't bypass this ! + // PT: cancel out normal compo +// WorldNormal[mUserParams.mUpDirection]=0.0f; +// WorldNormal.normalize(); + PxVec3 normalCompo, tangentCompo; + Ps::decomposeVector(normalCompo, tangentCompo, WorldNormal, mUserParams.mUpDirection); + WorldNormal = tangentCompo; + WorldNormal.normalize(); + } + + const float Bump = 0.0f; // ### doesn't work when !=0 because of Quake2 hack! + const float Friction = 1.0f; + collisionResponse(targetOrientation, currentPosition, currentDirection, WorldNormal, Bump, Friction, (mFlags & STF_NORMALIZE_RESPONSE)!=0); + } + + if(nb_collisions) + *nb_collisions = NbCollisions; + + // Final box position that should be reflected in the graphics engine + swept_volume.mCenter = currentPosition; + + // If we didn't move, don't update the box position at all (keeping possible lazy-evaluated structures valid) + return hasMoved; +} + +// ### have a return code to tell if we really moved or not + +// Using swept code & direct position update (no physics engine) +// This function is the generic character controller logic, valid for all swept volumes +PxControllerCollisionFlags SweepTest::moveCharacter( + const InternalCBData_FindTouchedGeom* userData, + InternalCBData_OnHit* userHitData, + SweptVolume& volume, + const PxVec3& direction, + const UserObstacles& userObstacles, + float min_dist, + const PxControllerFilters& filters, + bool constrainedClimbingMode, + bool standingOnMoving, + const PxRigidActor*& touchedActor, + const PxShape*& touchedShape, + PxU64 contextID) +{ + PX_PROFILE_ZONE("CharacterController.moveCharacter", contextID); + PX_UNUSED(contextID); + + bool standingOnMovingUp = standingOnMoving; + + mFlags &= ~STF_HIT_NON_WALKABLE; + PxControllerCollisionFlags CollisionFlags = PxControllerCollisionFlags(0); + const PxU32 maxIter = MAX_ITER; // 1 for "collide and stop" + const PxU32 maxIterSides = maxIter; + const PxU32 maxIterDown = ((mFlags & STF_WALK_EXPERIMENT) && mUserParams.mNonWalkableMode==PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING) ? maxIter : 1; +// const PxU32 maxIterDown = 1; + + // ### this causes the artificial gap on top of chars + float stepOffset = mUserParams.mStepOffset; // Default step offset can be cancelled in some cases. + + // Save initial height + const PxVec3& upDirection = mUserParams.mUpDirection; + const PxExtended originalHeight = volume.mCenter.dot(upDirection); + const PxExtended originalBottomPoint = originalHeight - PxExtended(volume.mHalfHeight); // UBI + + // TEST! Disable auto-step when flying. Not sure this is really useful. +// if(direction[upDirection]>0.0f) + const float dir_dot_up = direction.dot(upDirection); +//printf("%f\n", dir_dot_up); + if(dir_dot_up>0.0f) + { + mFlags |= STF_IS_MOVING_UP; + + // PT: this makes it fail on a platform moving up when jumping + // However if we don't do that a jump when moving up a slope doesn't work anymore! + // Not doing this also creates jittering when a capsule CCT jumps against another capsule CCT + if(!standingOnMovingUp) // PT: if we're standing on something moving up it's safer to do the up motion anyway, even though this won't work well before we add the flag in TA13542 + { +// static int count=0; printf("Cancelling step offset... %d\n", count++); + stepOffset = 0.0f; + } + } + else + { + mFlags &= ~STF_IS_MOVING_UP; + } + + // Decompose motion into 3 independent motions: up, side, down + // - if the motion is purely down (gravity only), the up part is needed to fight accuracy issues. For example if the + // character is already touching the geometry a bit, the down sweep test might have troubles. If we first move it above + // the geometry, the problems disappear. + // - if the motion is lateral (character moving forward under normal gravity) the decomposition provides the autostep feature + // - if the motion is purely up, the down part can be skipped + + PxVec3 UpVector(0.0f, 0.0f, 0.0f); + PxVec3 DownVector(0.0f, 0.0f, 0.0f); + + PxVec3 normal_compo, tangent_compo; + Ps::decomposeVector(normal_compo, tangent_compo, direction, upDirection); + +// if(direction[upDirection]<0.0f) + if(dir_dot_up<=0.0f) +// DownVector[upDirection] = direction[upDirection]; + DownVector = normal_compo; + else +// UpVector[upDirection] = direction[upDirection]; + UpVector = normal_compo; + +// PxVec3 SideVector = direction; +// SideVector[upDirection] = 0.0f; + PxVec3 SideVector = tangent_compo; + + // If the side motion is zero, i.e. if the character is not really moving, disable auto-step. + // This is important to prevent the CCT from automatically climbing on small objects that move + // against it. We should climb over those only if there's a valid side motion from the player. + const bool sideVectorIsZero = !standingOnMovingUp && Ps::isAlmostZero(SideVector); // We can't use PxVec3::isZero() safely with arbitrary up vectors + // #### however if we do this the up pass is disabled, with bad consequences when the CCT is on a dynamic object!! + // ### this line makes it possible to push other CCTs by jumping on them +// const bool sideVectorIsZero = false; +// printf("sideVectorIsZero: %d\n", sideVectorIsZero); + +// if(!SideVector.isZero()) + if(!sideVectorIsZero) +// UpVector[upDirection] += stepOffset; + UpVector += upDirection*stepOffset; +// printf("stepOffset: %f\n", stepOffset); + + // ==========[ Initial volume query ]=========================== + // PT: the main difference between this initial query and subsequent ones is that we use the + // full direction vector here, not the components along each axis. So there is a good chance + // that this initial query will contain all the motion we need, and thus subsequent queries + // will be skipped. + { + PxExtendedBounds3 temporalBox; + volume.computeTemporalBox(*this, temporalBox, volume.mCenter, direction); + + // Gather touched geoms + updateTouchedGeoms(userData, userObstacles, temporalBox, filters, SideVector); + } + + // ==========[ UP PASS ]=========================== + + mCachedTriIndexIndex = 0; + const bool performUpPass = true; + PxU32 NbCollisions=0; + + PxU32 maxIterUp; + if(mUserParams.mPreventVerticalSlidingAgainstCeiling) + maxIterUp = 1; + else + maxIterUp = Ps::isAlmostZero(SideVector) ? maxIter : 1; + + if(performUpPass) + { +// printf("%f | %f | %f\n", UpVector.x, UpVector.y, UpVector.z); + + // Prevent user callback for up motion. This up displacement is artificial, and only needed for auto-stepping. + // If we call the user for this, we might eventually apply upward forces to objects resting on top of us, even + // if we visually don't move. This produces weird-looking motions. +// mValidateCallback = false; + // PT: actually I think the previous comment is wrong. It's not only needed for auto-stepping: when the character + // jumps there's a legit up motion and the lack of callback in that case could need some object can't be pushed + // by the character's 'head' (for example). So I now think it's better to use the callback all the time, and + // let users figure out what to do using the available state (like "isMovingUp", etc). +// mValidateCallback = true; + + // In the walk-experiment we explicitly want to ban any up motions, to avoid characters climbing slopes they shouldn't climb. + // So let's bypass the whole up pass. + if(!(mFlags & STF_WALK_EXPERIMENT)) + { + // ### maxIter here seems to "solve" the V bug + if(doSweepTest(userData, userHitData, userObstacles, volume, UpVector, SideVector, maxIterUp, &NbCollisions, min_dist, filters, SWEEP_PASS_UP, touchedActor, touchedShape, contextID)) + { + if(NbCollisions) + { + CollisionFlags |= PxControllerCollisionFlag::eCOLLISION_UP; + + // Clamp step offset to make sure we don't undo more than what we did + float Delta = float(volume.mCenter.dot(upDirection) - originalHeight); + + if(Delta(volume).mRadius; + + const float sideM = SideVector.magnitude(); + if(sideM originalBottomPoint + PxExtended(stepOffset)) + { + mFlags |= STF_HIT_NON_WALKABLE; + if(!(mFlags & STF_WALK_EXPERIMENT)) + return CollisionFlags; + // printf("Contrained\n"); + } + } + //~constrainedClimbingMode + } + } + } + //printf("AD:%.2f %.2f %.2f NS=%d\n", volume.mCenter.x, volume.mCenter.y, volume.mCenter.z, mNbCachedStatic); +// printf("%d\n", mTouchOtherCCT); + + // TEST: do another down pass if we're on a non-walkable poly + // ### kind of works but still not perfect + // ### could it be because we zero the Y impulse later? + // ### also check clamped response vectors +// if(mUserParams.mHandleSlope && mValidateTriangle && direction[upDirection]<0.0f) +// if(mUserParams.mHandleSlope && !mTouchOtherCCT && !mTouchObstacle && mValidateTriangle && dir_dot_up<0.0f) + if(mUserParams.mHandleSlope && !(mFlags & (STF_TOUCH_OTHER_CCT|STF_TOUCH_OBSTACLE)) && (mFlags & STF_VALIDATE_TRIANGLE_DOWN) && dir_dot_up<=0.0f) + { + PxVec3 Normal; + #ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST + Normal = mContactNormalDownPass; + #else + //mTouchedTriangle.normal(Normal); + Normal = mContactNormalDownPass; + #endif + + const float touchedTriHeight = float(PxExtended(mTouchedTriMax) - originalBottomPoint); + +/* if(touchedTriHeight>mUserParams.mStepOffset) + { + if(constrainedClimbingMode && mContactPointHeight > originalBottomPoint + stepOffset) + { + mFlags |= STF_HIT_NON_WALKABLE; + if(!(mFlags & STF_WALK_EXPERIMENT)) + return CollisionFlags; + } + }*/ + + if(touchedTriHeight>mUserParams.mStepOffset && testSlope(Normal, upDirection, mUserParams.mSlopeLimit)) + { + mFlags |= STF_HIT_NON_WALKABLE; + // Early exit if we're going to run this again anyway... + if(!(mFlags & STF_WALK_EXPERIMENT)) + return CollisionFlags; + /* CatchScene()->GetRenderer()->AddLine(mTouchedTriangle.mVerts[0], mTouched.mVerts[1], ARGB_YELLOW); + CatchScene()->GetRenderer()->AddLine(mTouchedTriangle.mVerts[0], mTouched.mVerts[2], ARGB_YELLOW); + CatchScene()->GetRenderer()->AddLine(mTouchedTriangle.mVerts[1], mTouched.mVerts[2], ARGB_YELLOW); + */ + + // ==========[ WALK EXPERIMENT ]=========================== + + mFlags |= STF_NORMALIZE_RESPONSE; + + const PxExtended tmp = volume.mCenter.dot(upDirection); + float Delta = tmp > originalHeight ? float(tmp - originalHeight) : 0.0f; + Delta += fabsf(direction.dot(upDirection)); + float Recover = Delta; + + NbCollisions=0; + const float MD = Recover < min_dist ? Recover/float(maxIter) : min_dist; + + PxVec3 RecoverPoint(0,0,0); + RecoverPoint = -upDirection*Recover; + + // PT: we pass "SWEEP_PASS_UP" for compatibility with previous code, but it's technically wrong (this is a 'down' pass) + if(doSweepTest(userData, userHitData, userObstacles, volume, RecoverPoint, SideVector, maxIter, &NbCollisions, MD, filters, SWEEP_PASS_UP, touchedActor, touchedShape, contextID)) + { + // if(NbCollisions) CollisionFlags |= COLLISION_Y_DOWN; + // PT: why did we do this ? Removed for now. It creates a bug (non registered event) when we land on a steep poly. + // However this might have been needed when we were sliding on those polygons, and we didn't want the land anim to + // start while we were sliding. + // if(NbCollisions) CollisionFlags &= ~PxControllerCollisionFlag::eCOLLISION_DOWN; + } + mFlags &= ~STF_NORMALIZE_RESPONSE; + } + } + } + + return CollisionFlags; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// This is an interface between NX users and the internal character controller module. + +#include "CctInternalStructs.h" +#include "CctBoxController.h" +#include "CctCapsuleController.h" +#include "CctCharacterControllerManager.h" +#include "PxActor.h" +#include "PxScene.h" +#include "PxControllerBehavior.h" +#include "CctObstacleContext.h" + + // PT: we use a local class instead of making "Controller" a PxQueryFilterCallback, since it would waste more memory. + // Ideally we'd have a C-style callback and a user-data pointer, instead of being forced to create a class. + class ControllerFilter : public PxQueryFilterCallback + { + public: + PxQueryHitType::Enum preFilter(const PxFilterData& filterData, const PxShape* shape, const PxRigidActor* actor, PxHitFlags& queryFlags) + { + // PT: ignore triggers + if(shape->getFlags() & physx::PxShapeFlag::eTRIGGER_SHAPE) + return PxQueryHitType::eNONE; + + // PT: we want to discard our own internal shapes only + if(mShapeHashSet->contains(const_cast(shape))) + return PxQueryHitType::eNONE; + + // PT: otherwise we revert to the user-callback, if it exists, and if users enabled that call + if(mUserFilterCallback && (mUserFilterFlags | PxQueryFlag::ePREFILTER)) + return mUserFilterCallback->preFilter(filterData, shape, actor, queryFlags); + + return PxQueryHitType::eBLOCK; + } + + PxQueryHitType::Enum postFilter(const PxFilterData& filterData, const PxQueryHit& hit) + { + // PT: we may get called if users have asked for such a callback + if(mUserFilterCallback && (mUserFilterFlags | PxQueryFlag::ePOSTFILTER)) + return mUserFilterCallback->postFilter(filterData, hit); + + PX_ASSERT(0); // PT: otherwise we shouldn't have been called + return PxQueryHitType::eNONE; + } + + Ps::HashSet* mShapeHashSet; + PxQueryFilterCallback* mUserFilterCallback; + PxQueryFlags mUserFilterFlags; + }; + + +bool Controller::filterTouchedShape(const PxControllerFilters& filters) +{ + PxQueryFlags filterFlags = PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER; + PxQueryFilterData filterData(filters.mFilterData ? *filters.mFilterData : PxFilterData(), filterFlags); + PxHitFlags hitFlags = PxHitFlags(0); + + if(filters.mFilterCallback && (filters.mFilterFlags | PxQueryFlag::ePREFILTER)) + { + PxQueryHitType::Enum retVal = filters.mFilterCallback->preFilter(filterData.data, mCctModule.mTouchedShape.get(), mCctModule.mTouchedActor.get(), hitFlags); + if(retVal != PxQueryHitType::eNONE) + return true; + else + return false; + } + + return true; +} + +void Controller::findTouchedObject(const PxControllerFilters& filters, const PxObstacleContext* obstacleContext, const PxVec3& upDirection) +{ + PX_ASSERT(!mCctModule.mTouchedShape && (mCctModule.mTouchedObstacleHandle == INVALID_OBSTACLE_HANDLE)); + + // PT: the CCT works perfectly on statics without this extra mechanism, so we only raycasts against dynamics. + // The pre-filter callback is used to filter out our own proxy actor shapes. We need to make sure our own filter + // doesn't disturb the user-provided filter(s). + + // PT: for starter, if user doesn't want to collide against dynamics, we can skip the whole thing + if(filters.mFilterFlags & PxQueryFlag::eDYNAMIC) + { + ControllerFilter preFilter; + preFilter.mShapeHashSet = &mManager->mCCTShapes; + preFilter.mUserFilterCallback = filters.mFilterCallback; + preFilter.mUserFilterFlags = filters.mFilterFlags; + + // PT: for our own purpose we just want dynamics & pre-filter + PxQueryFlags filterFlags = PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER; + // PT: but we may need the post-filter callback as well if users want it + if(filters.mFilterFlags & PxQueryFlag::ePOSTFILTER) + filterFlags |= PxQueryFlag::ePOSTFILTER; + + PxQueryFilterData filterData(filters.mFilterData ? *filters.mFilterData : PxFilterData(), filterFlags); + + const PxF32 probeLength = getHalfHeightInternal(); // Distance to feet + const PxF32 extra = 0.0f;//probeLength * 0.1f; + + const PxVec3 rayOrigin = toVec3(mPosition); + + PxRaycastBuffer hit; + hit.block.distance = FLT_MAX; + if(mScene->raycast(rayOrigin, -upDirection, probeLength+extra, hit, PxHitFlags(0), filterData, &preFilter)) + { + // copy touching hit to blocking so that the rest of the code works with .block + hit.block = hit.getAnyHit(0); + PX_ASSERT(hit.block.shape); + PX_ASSERT(hit.block.actor); + PX_ASSERT(hit.block.distance<=probeLength+extra); + mCctModule.mTouchedShape = hit.block.shape; + mCctModule.mTouchedActor = hit.block.actor; +// mCctModule.mTouchedPos = getShapeGlobalPose(*hit.shape).p - upDirection*(probeLength-hit.distance); + // PT: we only care about the up delta here + const PxTransform shapeTransform = getShapeGlobalPose(*hit.block.shape, *hit.block.actor); + mCctModule.mTouchedPosShape_World = PxVec3(0) - upDirection*(probeLength-hit.block.distance); + mCctModule.mTouchedPosShape_Local = shapeTransform.transformInv(PxVec3(0)); + + mPreviousSceneTimestamp = mScene->getTimestamp()-1; // PT: just make sure cached timestamp is different + } + + if(obstacleContext) + { + const ObstacleContext* obstacles = static_cast(obstacleContext); + PxRaycastHit obstacleHit; + ObstacleHandle obstacleHandle; + const PxObstacle* touchedObstacle = obstacles->raycastSingle(obstacleHit, rayOrigin, -upDirection, probeLength+extra, obstacleHandle); +// printf("Touched raycast obstacle: %d\n", int(touchedObstacle)); + if(touchedObstacle && obstacleHit.distancemPos) - upDirection*(probeLength-obstacleHit.distance); + } + else + { + // PT: we only care about the up delta here + mCctModule.mTouchedPosObstacle_World = PxVec3(0) - upDirection*(probeLength-obstacleHit.distance); + mCctModule.mTouchedPosObstacle_Local = worldToLocal(*touchedObstacle, PxExtendedVec3(0,0,0)); + } + } + } + } +} + +bool Controller::rideOnTouchedObject(SweptVolume& volume, const PxVec3& upDirection, PxVec3& disp, const PxObstacleContext* obstacleContext) +{ + PX_ASSERT(mCctModule.mTouchedShape || (mCctModule.mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE)); + + bool standingOnMoving = false; + + bool canDoUpdate = true; // Always true on obstacles + PxU32 behaviorFlags = 0; // Default on shapes + PxVec3 delta(0); + float timeCoeff = 1.0f; + + if(mCctModule.mTouchedShape) + { + // PT: riding on a shape + + // PT: it is important to skip this stuff for static meshes, + // otherwise accuracy issues create bugs like TA14007. + const PxRigidActor& rigidActor = *mCctModule.mTouchedActor.get(); + if(rigidActor.getConcreteType()!=PxConcreteType::eRIGID_STATIC) + { + // PT: we only do the update when the timestamp has changed, otherwise "delta" will be zero + // even if the underlying shape is moving. + const PxU32 timestamp = mScene->getTimestamp(); +// printf("TimeStamp: %d\n", timestamp); + canDoUpdate = timestamp!=mPreviousSceneTimestamp; + if(canDoUpdate) + { + mPreviousSceneTimestamp = timestamp; + + timeCoeff = computeTimeCoeff(); + + if(mBehaviorCallback) + behaviorFlags = mBehaviorCallback->getBehaviorFlags(*mCctModule.mTouchedShape.get(), *mCctModule.mTouchedActor.get()); + +// delta = getShapeGlobalPose(*mCctModule.mTouchedShape).p - mCctModule.mTouchedPos; + const PxTransform shapeTransform = getShapeGlobalPose(*mCctModule.mTouchedShape.get(), rigidActor); + const PxVec3 posPreviousFrame = mCctModule.mTouchedPosShape_World; + const PxVec3 posCurrentFrame = shapeTransform.transform(mCctModule.mTouchedPosShape_Local); + delta = posCurrentFrame - posPreviousFrame; + } + } + } + else + { + // PT: riding on an obstacle + behaviorFlags = PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT; // Default on obstacles + + timeCoeff = computeTimeCoeff(); + + const PxObstacle* touchedObstacle = obstacleContext->getObstacleByHandle(mCctModule.mTouchedObstacleHandle); + PX_ASSERT(touchedObstacle); + + if(mBehaviorCallback) + behaviorFlags = mBehaviorCallback->getBehaviorFlags(*touchedObstacle); + + if(!gUseLocalSpace) + { + delta = toVec3(touchedObstacle->mPos) - mCctModule.mTouchedPos; + } + else + { + PxVec3 posPreviousFrame = mCctModule.mTouchedPosObstacle_World; + PxVec3 posCurrentFrame = localToWorld(*touchedObstacle, mCctModule.mTouchedPosObstacle_Local); + delta = posCurrentFrame - posPreviousFrame; + } + } + + if(canDoUpdate && !(behaviorFlags & PxControllerBehaviorFlag::eCCT_USER_DEFINED_RIDE)) + { + // PT: amazingly enough even isAlmostZero doesn't solve this one. + // Moving on a static mesh sometimes produces delta bigger than 1e-6f! + // This may also explain the drift on some rotating platforms. It looks + // like this delta computation is not very accurate. +// standingOnMoving = !delta.isZero(); + standingOnMoving = !Ps::isAlmostZero(delta); + mCachedStandingOnMoving = standingOnMoving; +//printf("%f %f %f\n", delta.x, delta.y, delta.z); + if(standingOnMoving) + { + const float dir_dot_up = delta.dot(upDirection); + const bool deltaMovingUp = dir_dot_up>0.0f; + + PxVec3 deltaUpDisp, deltaSideDisp; + Ps::decomposeVector(deltaUpDisp, deltaSideDisp, delta, upDirection); + + if(deltaMovingUp) + { + volume.mCenter.x += PxExtended(deltaUpDisp.x); + volume.mCenter.y += PxExtended(deltaUpDisp.y); + volume.mCenter.z += PxExtended(deltaUpDisp.z); + } + else + { + disp += deltaUpDisp; + } + + if(behaviorFlags & PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT) + disp += deltaSideDisp; + } +// printf("delta in: %f %f %f (%f)\n", delta.x, delta.y, delta.z, 1.0f/timeCoeff); + mDeltaXP = delta * timeCoeff; + } + else + { + standingOnMoving = mCachedStandingOnMoving; + } +// mDelta = delta; + + return standingOnMoving; +} + +PxControllerCollisionFlags Controller::move(SweptVolume& volume, const PxVec3& originalDisp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacleContext, bool constrainedClimbingMode) +{ + const bool lockWrite = mManager->mLockingEnabled; + if(lockWrite) + mWriteLock.lock(); + + mGlobalTime += PxF64(elapsedTime); + + // Init CCT with per-controller settings + RenderBuffer* renderBuffer = mManager->mRenderBuffer; + const PxU32 debugRenderFlags = mManager->mDebugRenderingFlags; + mCctModule.mRenderBuffer = renderBuffer; + mCctModule.mRenderFlags = debugRenderFlags; + mCctModule.mUserParams = mUserParams; + mCctModule.mFlags |= STF_FIRST_UPDATE; + mCctModule.mUserParams.mMaxEdgeLength2 = mManager->mMaxEdgeLength * mManager->mMaxEdgeLength; + mCctModule.mUserParams.mTessellation = mManager->mTessellation; + mCctModule.mUserParams.mOverlapRecovery = mManager->mOverlapRecovery; + mCctModule.mUserParams.mPreciseSweeps = mManager->mPreciseSweeps; + mCctModule.mUserParams.mPreventVerticalSlidingAgainstCeiling = mManager->mPreventVerticalSlidingAgainstCeiling; + mCctModule.resetStats(); + + const PxVec3& upDirection = mUserParams.mUpDirection; + + /////////// + + PxVec3 disp = originalDisp + mOverlapRecover; + mOverlapRecover = PxVec3(0.0f); + + bool standingOnMoving = false; // PT: whether the CCT is currently standing on a moving object + //printf("Touched shape: %d\n", int(mCctModule.mTouchedShape)); +//standingOnMoving=true; +// printf("Touched obstacle: %d\n", int(mCctModule.mTouchedObstacle)); + + if(mCctModule.mTouchedActor && mCctModule.mTouchedShape) + { + PxU32 nbShapes = mCctModule.mTouchedActor->getNbShapes(); + bool found = false; + for(PxU32 i=0;igetShapes(&shape, 1, i); + if(mCctModule.mTouchedShape==shape) + { + found = true; + break; + } + } + + if(!found) + { + mCctModule.mTouchedActor = NULL; + mCctModule.mTouchedShape = NULL; + } + else + { + // check if we are still in the same scene + if(mCctModule.mTouchedActor->getScene() != mScene) + { + mCctModule.mTouchedShape = NULL; + mCctModule.mTouchedActor = NULL; + } + else + { + // check if the shape still does have the sq flag + if(!(mCctModule.mTouchedShape->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE)) + { + mCctModule.mTouchedShape = NULL; + mCctModule.mTouchedActor = NULL; + } + else + { + // invoke the CCT filtering for the shape + if(!filterTouchedShape(filters)) + { + mCctModule.mTouchedShape = NULL; + mCctModule.mTouchedActor = NULL; + } + } + } + } + } + + if(!mCctModule.mTouchedShape && (mCctModule.mTouchedObstacleHandle == INVALID_OBSTACLE_HANDLE)) + findTouchedObject(filters, obstacleContext, upDirection); + + if(mCctModule.mTouchedShape || (mCctModule.mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE)) + { + standingOnMoving = rideOnTouchedObject(volume, upDirection, disp, obstacleContext); + } + else + { + mCachedStandingOnMoving = false; + mDeltaXP = PxVec3(0.0f); + } +// printf("standingOnMoving: %d\n", standingOnMoving); + + /////////// + Ps::Array& boxUserData = mManager->mBoxUserData; + Ps::Array& boxes = mManager->mBoxes; + Ps::Array& capsuleUserData = mManager->mCapsuleUserData; + Ps::Array& capsules = mManager->mCapsules; + PX_ASSERT(!boxUserData.size()); + PX_ASSERT(!boxes.size()); + PX_ASSERT(!capsuleUserData.size()); + PX_ASSERT(!capsules.size()); + + { + PX_PROFILE_ZONE("CharacterController.filterCandidateControllers", getContextId()); + + // Experiment - to do better + const PxU32 nbControllers = mManager->getNbControllers(); + Controller** controllers = mManager->getControllers(); + + for(PxU32 i=0;ifilter(*getPxController(), *currentController->getPxController()); + + if(keepController) + { + if(currentController->mType==PxControllerShapeType::eBOX) + { + // PT: TODO: optimize this + BoxController* BC = static_cast(currentController); + PxExtendedBox obb; + BC->getOBB(obb); + + boxes.pushBack(obb); + +#ifdef REMOVED + if(renderBuffer /*&& (debugRenderFlags & PxControllerDebugRenderFlag::eOBSTACLES)*/) + { + RenderOutput out(*renderBuffer); + out << gCCTBoxDebugColor; + + out << PxTransform(toVec3(obb.center), obb.rot); + + out << DebugBox(obb.extents, true); + } +#endif + const size_t code = encodeUserObject(i, USER_OBJECT_CCT); + boxUserData.pushBack(reinterpret_cast(code)); + } + else if(currentController->mType==PxControllerShapeType::eCAPSULE) + { + CapsuleController* CC = static_cast(currentController); + + // PT: TODO: optimize this + PxExtendedCapsule worldCapule; + CC->getCapsule(worldCapule); + capsules.pushBack(worldCapule); + + const size_t code = encodeUserObject(i, USER_OBJECT_CCT); + capsuleUserData.pushBack(reinterpret_cast(code)); + } + else PX_ASSERT(0); + } + } + } + + const ObstacleContext* obstacles = NULL; + if(obstacleContext) + { + obstacles = static_cast(obstacleContext); + + // PT: TODO: optimize this + const PxU32 nbExtraBoxes = obstacles->mBoxObstacles.size(); + for(PxU32 i=0;imBoxObstacles[i].mData; + + PxExtendedBox extraBox; + extraBox.center = userBoxObstacle.mPos; + extraBox.extents = userBoxObstacle.mHalfExtents; + extraBox.rot = userBoxObstacle.mRot; + boxes.pushBack(extraBox); + + const size_t code = encodeUserObject(i, USER_OBJECT_BOX_OBSTACLE); + boxUserData.pushBack(reinterpret_cast(code)); + + if(renderBuffer && (debugRenderFlags & PxControllerDebugRenderFlag::eOBSTACLES)) + { + RenderOutput out(*renderBuffer); + out << gObstacleDebugColor; + + out << PxTransform(toVec3(userBoxObstacle.mPos), userBoxObstacle.mRot); + + out << DebugBox(userBoxObstacle.mHalfExtents, true); + } + } + + const PxU32 nbExtraCapsules = obstacles->mCapsuleObstacles.size(); + for(PxU32 i=0;imCapsuleObstacles[i].mData; + + PxExtendedCapsule extraCapsule; + const PxVec3 capsuleAxis = userCapsuleObstacle.mRot.getBasisVector0() * userCapsuleObstacle.mHalfHeight; + extraCapsule.p0 = PxExtendedVec3( userCapsuleObstacle.mPos.x - PxExtended(capsuleAxis.x), + userCapsuleObstacle.mPos.y - PxExtended(capsuleAxis.y), + userCapsuleObstacle.mPos.z - PxExtended(capsuleAxis.z)); + extraCapsule.p1 = PxExtendedVec3( userCapsuleObstacle.mPos.x + PxExtended(capsuleAxis.x), + userCapsuleObstacle.mPos.y + PxExtended(capsuleAxis.y), + userCapsuleObstacle.mPos.z + PxExtended(capsuleAxis.z)); + + extraCapsule.radius = userCapsuleObstacle.mRadius; + capsules.pushBack(extraCapsule); + const size_t code = encodeUserObject(i, USER_OBJECT_CAPSULE_OBSTACLE); + capsuleUserData.pushBack(reinterpret_cast(code)); + + if(renderBuffer && (debugRenderFlags & PxControllerDebugRenderFlag::eOBSTACLES)) + { + RenderOutput out(*renderBuffer); + out << gObstacleDebugColor; + out.outputCapsule(userCapsuleObstacle.mRadius, userCapsuleObstacle.mHalfHeight, PxTransform(toVec3(userCapsuleObstacle.mPos), userCapsuleObstacle.mRot)); + } + } + } + + + UserObstacles userObstacles; + + const PxU32 nbBoxes = boxes.size(); + userObstacles.mNbBoxes = nbBoxes; + userObstacles.mBoxes = nbBoxes ? boxes.begin() : NULL; + userObstacles.mBoxUserData = nbBoxes ? boxUserData.begin() : NULL; + + const PxU32 nbCapsules = capsules.size(); + userObstacles.mNbCapsules = nbCapsules; + userObstacles.mCapsules = nbCapsules ? capsules.begin() : NULL; + userObstacles.mCapsuleUserData = nbCapsules ? capsuleUserData.begin() : NULL; + + PxInternalCBData_OnHit userHitData; + userHitData.controller = this; + userHitData.obstacles = obstacles; + + /////////// + + PxControllerCollisionFlags collisionFlags = PxControllerCollisionFlags(0); + + PxInternalCBData_FindTouchedGeom findGeomData; + findGeomData.scene = mScene; + findGeomData.renderBuffer = renderBuffer; + findGeomData.cctShapeHashSet = &mManager->mCCTShapes; + + mCctModule.mFlags &= ~STF_WALK_EXPERIMENT; + + // store new touched actor/shape. Then set new actor/shape to avoid register/unregister for same objects + const PxRigidActor* touchedActor = NULL; + const PxShape* touchedShape = NULL; + PxExtendedVec3 Backup = volume.mCenter; + collisionFlags = mCctModule.moveCharacter(&findGeomData, &userHitData, volume, disp, userObstacles, minDist, filters, constrainedClimbingMode, standingOnMoving, touchedActor, touchedShape, getContextId()); + + if(mCctModule.mFlags & STF_HIT_NON_WALKABLE) + { + // A bit slow, but everything else I tried was less convincing... + mCctModule.mFlags |= STF_WALK_EXPERIMENT; + volume.mCenter = Backup; + + PxVec3 xpDisp; + if(mUserParams.mNonWalkableMode==PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING) + { + PxVec3 tangent_compo; + Ps::decomposeVector(xpDisp, tangent_compo, disp, upDirection); + } + else xpDisp = disp; + + collisionFlags = mCctModule.moveCharacter(&findGeomData, &userHitData, volume, xpDisp, userObstacles, minDist, filters, constrainedClimbingMode, standingOnMoving, touchedActor, touchedShape, getContextId()); + + mCctModule.mFlags &= ~STF_WALK_EXPERIMENT; + } + mCctModule.mTouchedActor = touchedActor; + mCctModule.mTouchedShape = touchedShape; + + mCollisionFlags = collisionFlags; + + // Copy results back + mPosition = volume.mCenter; + + // Update kinematic actor + if(mKineActor) + { + const PxVec3 delta = Backup - volume.mCenter; + const PxF32 deltaM2 = delta.magnitudeSquared(); + if(deltaM2!=0.0f) + { + PxTransform targetPose = mKineActor->getGlobalPose(); + targetPose.p = toVec3(mPosition); + targetPose.q = mUserParams.mQuatFromUp; + mKineActor->setKinematicTarget(targetPose); + } + } + + mManager->resetObstaclesBuffers(); + + if (lockWrite) + mWriteLock.unlock(); + + return collisionFlags; +} + + +PxControllerCollisionFlags BoxController::move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles) +{ + PX_PROFILE_ZONE("CharacterController.move", getContextId()); + + PX_SIMD_GUARD; + + // Create internal swept box + SweptBox sweptBox; + sweptBox.mCenter = mPosition; + sweptBox.mExtents = PxVec3(mHalfHeight, mHalfSideExtent, mHalfForwardExtent); + sweptBox.mHalfHeight = mHalfHeight; // UBI + return Controller::move(sweptBox, disp, minDist, elapsedTime, filters, obstacles, false); +} + +PxControllerCollisionFlags CapsuleController::move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles) +{ + PX_PROFILE_ZONE("CharacterController.move", getContextId()); + + PX_SIMD_GUARD; + + // Create internal swept capsule + SweptCapsule sweptCapsule; + sweptCapsule.mCenter = mPosition; + sweptCapsule.mRadius = mRadius; + sweptCapsule.mHeight = mHeight; + sweptCapsule.mHalfHeight = mHeight*0.5f + mRadius; // UBI + return Controller::move(sweptCapsule, disp, minDist, elapsedTime, filters, obstacles, mClimbingMode==PxCapsuleClimbingMode::eCONSTRAINED); +} + diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h new file mode 100644 index 000000000..b075a0ca3 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h @@ -0,0 +1,481 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_CHARACTER_CONTROLLER +#define CCT_CHARACTER_CONTROLLER + +//#define USE_CONTACT_NORMAL_FOR_SLOPE_TEST + +#include "PxController.h" +#include "PxControllerObstacles.h" +#include "CctCharacterControllerManager.h" +#include "CctUtils.h" +#include "PxTriangle.h" +#include "PsArray.h" +#include "PsHashSet.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +struct PxFilterData; +class PxQueryFilterCallback; +class PxObstacle; + +namespace Cm +{ + class RenderBuffer; +} + +namespace Cct +{ + struct CCTParams + { + CCTParams(); + + PxControllerNonWalkableMode::Enum mNonWalkableMode; + PxQuat mQuatFromUp; + PxVec3 mUpDirection; + PxF32 mSlopeLimit; + PxF32 mContactOffset; + PxF32 mStepOffset; + PxF32 mInvisibleWallHeight; + PxF32 mMaxJumpHeight; + PxF32 mMaxEdgeLength2; + bool mTessellation; + bool mHandleSlope; // True to handle walkable parts according to slope + bool mOverlapRecovery; + bool mPreciseSweeps; + bool mPreventVerticalSlidingAgainstCeiling; + }; + +// typedef Ps::Array TriArray; + typedef Ps::Array IntArray; + + // PT: using private inheritance to control access, and make sure allocations are SIMD friendly + class TriArray : private Ps::Array + { + public: + + PX_FORCE_INLINE PxTriangle* reserve(PxU32 nbTris) + { + // PT: customized version of "reserveContainerMemory" + + const PxU32 maxNbEntries = Ps::Array::capacity(); + const PxU32 realRequiredSize = Ps::Array::size() + nbTris; + // PT: allocate one more tri to make sure we can safely V4Load the last one... + const PxU32 requiredSize = realRequiredSize + 1; + + if(requiredSize>maxNbEntries) + { + // PT: ok so the commented out growing policy was introduced by PX-837 but it produces + // large memory usage regressions (see PX-881) while not actually making things run + // faster. Our benchmarks show no performance difference, but up to +38% more memory + // used with this "standard" growing policy. So for now we just go back to the initial + // growing policy. It should be fine since PX-837 was not actually reported by a customer, + // it was just a concern that appeared while looking at the code. Ideally we'd use a pool + // with fixed-size slabs to get the best of both worlds but it would make iterating over + // triangles more complicated and would need more refactoring. So for now we don't bother, + // but we'll keep this note here for the next time this problem shows up. + // PT: new August 2018: turns out PX-837 was correct. Not doing this produces very large + // performance problems (like: the app freezes!) in SampleCCT. We didn't see it because + // it's an internal sample that it rarely used these days... + const PxU32 naturalGrowthSize = maxNbEntries ? maxNbEntries*2 : 2; + const PxU32 newSize = PxMax(requiredSize, naturalGrowthSize); +// const PxU32 newSize = requiredSize; + + Ps::Array::reserve(newSize); + } + + PxTriangle* buf = Ps::Array::end(); + // ...but we still want the size to reflect the correct number + Ps::Array::forceSize_Unsafe(realRequiredSize); + return buf; + } + + PX_FORCE_INLINE void pushBack(const PxTriangle& tri) + { + PxTriangle* memory = reserve(1); + memory->verts[0] = tri.verts[0]; + memory->verts[1] = tri.verts[1]; + memory->verts[2] = tri.verts[2]; + } + + PX_FORCE_INLINE PxU32 size() const + { + return Ps::Array::size(); + } + + PX_FORCE_INLINE const PxTriangle* begin() const + { + return Ps::Array::begin(); + } + + PX_FORCE_INLINE void clear() + { + Ps::Array::clear(); + } + + PX_FORCE_INLINE void forceSize_Unsafe(PxU32 size) + { + Ps::Array::forceSize_Unsafe(size); + } + + PX_FORCE_INLINE const PxTriangle& getTriangle(PxU32 index) const + { + return (*this)[index]; + } + + }; + + /* Exclude from documentation */ + /** \cond */ + + struct TouchedGeomType + { + enum Enum + { + eUSER_BOX, + eUSER_CAPSULE, + eMESH, + eBOX, + eSPHERE, + eCAPSULE, + + eLAST, + + eFORCE_DWORD = 0x7fffffff + }; + }; + + class SweptVolume; + + // PT: apparently .Net aligns some of them on 8-bytes boundaries for no good reason. This is bad. + // Whenever a variable points to a field of a specially aligned struct, it has to be declared with __packed (see GHS docu, Structure Packing, page 111). + // Every reference to such a field needs the __packed declaration: all function parameters and assignment operators etc. +#pragma pack(push,4) + + struct TouchedGeom + { + TouchedGeomType::Enum mType; + const void* mTGUserData; // PxController or PxShape pointer + const PxRigidActor* mActor; // PxActor for PxShape pointers (mandatory with shared shapes) + PxExtendedVec3 mOffset; // Local origin, typically the center of the world bounds around the character. We translate both + // touched shapes & the character so that they are nearby this PxVec3, then add the offset back to + // computed "world" impacts. + protected: + ~TouchedGeom(){} + }; + + struct TouchedUserBox : public TouchedGeom + { + PxExtendedBox mBox; + }; + PX_COMPILE_TIME_ASSERT(sizeof(TouchedUserBox)==sizeof(TouchedGeom)+sizeof(PxExtendedBox)); + + struct TouchedUserCapsule : public TouchedGeom + { + PxExtendedCapsule mCapsule; + }; + PX_COMPILE_TIME_ASSERT(sizeof(TouchedUserCapsule)==sizeof(TouchedGeom)+sizeof(PxExtendedCapsule)); + + struct TouchedMesh : public TouchedGeom + { + PxU32 mNbTris; + PxU32 mIndexWorldTriangles; + }; + + struct TouchedBox : public TouchedGeom + { + PxVec3 mCenter; + PxVec3 mExtents; + PxQuat mRot; + }; + + struct TouchedSphere : public TouchedGeom + { + PxVec3 mCenter; //!< Sphere's center + PxF32 mRadius; //!< Sphere's radius + }; + + struct TouchedCapsule : public TouchedGeom + { + PxVec3 mP0; //!< Start of segment + PxVec3 mP1; //!< End of segment + PxF32 mRadius; //!< Capsule's radius + }; + +#pragma pack(pop) + + struct SweptContact + { + PxExtendedVec3 mWorldPos; // Contact position in world space + PxVec3 mWorldNormal; // Contact normal in world space + PxF32 mDistance; // Contact distance + PxU32 mInternalIndex; // Reserved for internal usage + PxU32 mTriangleIndex; // Triangle index for meshes/heightfields + TouchedGeom* mGeom; + + PX_FORCE_INLINE void setWorldPos(const PxVec3& localImpact, const PxExtendedVec3& offset) + { + mWorldPos.x = PxExtended(localImpact.x) + offset.x; + mWorldPos.y = PxExtended(localImpact.y) + offset.y; + mWorldPos.z = PxExtended(localImpact.z) + offset.z; + } + }; + + // PT: user-defined obstacles. Note that "user" is from the SweepTest class' point of view, + // i.e. the PhysX CCT module is the user in this case. This is to limit coupling between the + // core CCT module and the PhysX classes. + struct UserObstacles// : PxObstacleContext + { + PxU32 mNbBoxes; + const PxExtendedBox* mBoxes; + const void** mBoxUserData; + + PxU32 mNbCapsules; + const PxExtendedCapsule* mCapsules; + const void** mCapsuleUserData; + }; + + struct InternalCBData_OnHit{}; + struct InternalCBData_FindTouchedGeom{}; + + enum SweepTestFlag + { + STF_HIT_NON_WALKABLE = (1<<0), + STF_WALK_EXPERIMENT = (1<<1), + STF_VALIDATE_TRIANGLE_DOWN = (1<<2), // Validate touched triangle data (down pass) + STF_VALIDATE_TRIANGLE_SIDE = (1<<3), // Validate touched triangle data (side pass) + STF_TOUCH_OTHER_CCT = (1<<4), // Are we standing on another CCT or not? (only updated for down pass) + STF_TOUCH_OBSTACLE = (1<<5), // Are we standing on an obstacle or not? (only updated for down pass) + STF_NORMALIZE_RESPONSE = (1<<6), + STF_FIRST_UPDATE = (1<<7), + STF_IS_MOVING_UP = (1<<8) + }; + + enum SweepPass + { + SWEEP_PASS_UP, + SWEEP_PASS_SIDE, + SWEEP_PASS_DOWN, + SWEEP_PASS_SENSOR + }; + + class Controller; + + template + struct TouchedObject + { + TouchedObject(bool regDl) + : mTouchedObject(NULL), mRegisterDeletionListener(regDl), mCctManager(NULL) + { + } + + PX_FORCE_INLINE const T* operator->() const { return mTouchedObject; } + PX_FORCE_INLINE bool operator==(const TouchedObject& otherObject) { return mTouchedObject == otherObject.mTouchedObject; } + PX_FORCE_INLINE bool operator==(const T* otherObject) { return mTouchedObject == otherObject; } + PX_FORCE_INLINE bool operator==(const PxBase* otherObject) { return mTouchedObject == otherObject; } + PX_FORCE_INLINE operator bool() const { return mTouchedObject != NULL; } + PX_FORCE_INLINE TouchedObject& operator=(const T* assignedObject) + { + if(mRegisterDeletionListener && (mTouchedObject != assignedObject)) + { + if(mTouchedObject) + mCctManager->unregisterObservedObject(mTouchedObject); + + if(assignedObject) + mCctManager->registerObservedObject(assignedObject); + } + mTouchedObject = assignedObject; + return *this; + } + + const T* get() const { return mTouchedObject; } + + void setCctManager(CharacterControllerManager* cm) { mCctManager = cm; } + + private: + TouchedObject& operator=(const TouchedObject&); + + const T* mTouchedObject; + bool mRegisterDeletionListener; + CharacterControllerManager* mCctManager; + }; + + class SweepTest + { + public: + SweepTest(bool registerDeletionListener); + ~SweepTest(); + + PxControllerCollisionFlags moveCharacter( const InternalCBData_FindTouchedGeom* userData, + InternalCBData_OnHit* user_data2, + SweptVolume& volume, + const PxVec3& direction, + const UserObstacles& userObstacles, + PxF32 min_dist, + const PxControllerFilters& filters, + bool constrainedClimbingMode, + bool standingOnMoving, + const PxRigidActor*& touchedActor, + const PxShape*& touchedShape, + PxU64 contextID); + + bool doSweepTest(const InternalCBData_FindTouchedGeom* userDataTouchedGeom, + InternalCBData_OnHit* userDataOnHit, + const UserObstacles& userObstacles, + SweptVolume& swept_volume, + const PxVec3& direction, const PxVec3& sideVector, PxU32 max_iter, + PxU32* nb_collisions, PxF32 min_dist, const PxControllerFilters& filters, SweepPass sweepPass, + const PxRigidActor*& touchedActor, const PxShape*& touchedShape, PxU64 contextID); + + void findTouchedObstacles(const UserObstacles& userObstacles, const PxExtendedBounds3& world_box); + + void voidTestCache(); + void onRelease(const PxBase& observed); + void updateCachedShapesRegistration(PxU32 startIndex, bool unregister); + + // observer notifications + void onObstacleRemoved(ObstacleHandle index); + void onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance); + void onObstacleAdded(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance); + + void onOriginShift(const PxVec3& shift); + + Cm::RenderBuffer* mRenderBuffer; + PxU32 mRenderFlags; + TriArray mWorldTriangles; + IntArray mTriangleIndices; + IntArray mGeomStream; + PxExtendedBounds3 mCacheBounds; + PxU32 mCachedTriIndexIndex; + mutable PxU32 mCachedTriIndex[3]; + PxU32 mNbCachedStatic; + PxU32 mNbCachedT; + public: +#ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST + PxVec3 mContactNormalDownPass; +#else + PxVec3 mContactNormalDownPass; + PxVec3 mContactNormalSidePass; + float mTouchedTriMin; + float mTouchedTriMax; + //PxTriangle mTouchedTriangle; +#endif + // + TouchedObject mTouchedShape; // Shape on which the CCT is standing + TouchedObject mTouchedActor; // Actor from touched shape + ObstacleHandle mTouchedObstacleHandle; // Obstacle on which the CCT is standing + PxVec3 mTouchedPos; // Last known position of mTouchedShape/mTouchedObstacle + // PT: TODO: union those + PxVec3 mTouchedPosShape_Local; + PxVec3 mTouchedPosShape_World; + PxVec3 mTouchedPosObstacle_Local; + PxVec3 mTouchedPosObstacle_World; + // + CCTParams mUserParams; + PxF32 mVolumeGrowth; // Must be >1.0f and not too big + PxF32 mContactPointHeight; // UBI + PxU32 mSQTimeStamp; + PxU16 mNbFullUpdates; + PxU16 mNbPartialUpdates; + PxU16 mNbTessellation; + PxU16 mNbIterations; + PxU32 mFlags; + bool mRegisterDeletionListener; + + PX_FORCE_INLINE void resetStats() + { + mNbFullUpdates = 0; + mNbPartialUpdates = 0; + mNbTessellation = 0; + mNbIterations = 0; + } + + void setCctManager(CharacterControllerManager* cm) + { + mCctManager = cm; + mTouchedActor.setCctManager(cm); + mTouchedShape.setCctManager(cm); + } + + private: + void updateTouchedGeoms( const InternalCBData_FindTouchedGeom* userData, const UserObstacles& userObstacles, + const PxExtendedBounds3& worldBox, const PxControllerFilters& filters, const PxVec3& sideVector); + + CharacterControllerManager* mCctManager; + SweepTest(const SweepTest&); + SweepTest& operator=(const SweepTest& ); + }; + + class CCTFilter // PT: internal filter data, could be replaced with PxControllerFilters eventually + { + public: + PX_FORCE_INLINE CCTFilter() : + mFilterData (NULL), + mFilterCallback (NULL), + mStaticShapes (false), + mDynamicShapes (false), + mPreFilter (false), + mPostFilter (false), + mCCTShapes (NULL) + { + } + const PxFilterData* mFilterData; + PxQueryFilterCallback* mFilterCallback; + bool mStaticShapes; + bool mDynamicShapes; + bool mPreFilter; + bool mPostFilter; + Ps::HashSet* mCCTShapes; + }; + + PxU32 getSceneTimestamp(const InternalCBData_FindTouchedGeom* userData); + + void findTouchedGeometry(const InternalCBData_FindTouchedGeom* userData, + const PxExtendedBounds3& world_aabb, + + TriArray& world_triangles, + IntArray& triIndicesArray, + IntArray& geomStream, + + const CCTFilter& filter, + const CCTParams& params, + PxU16& nbTessellation); + + PxU32 shapeHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, PxF32 length); + PxU32 userHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, PxF32 length); + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp new file mode 100644 index 000000000..893849a9c --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp @@ -0,0 +1,1106 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "CctInternalStructs.h" +#include "PxScene.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxBoxGeometry.h" +#include "PxConvexMesh.h" +#include "PxMeshQuery.h" +#include "extensions/PxTriangleMeshExt.h" +#include "PxTriangleMeshGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "CmRenderOutput.h" +#include "GuIntersectionTriangleBox.h" +#include "PsMathUtils.h" +#include "GuSIMDHelpers.h" + +static const bool gVisualizeTouchedTris = false; +static const float gDebugVisOffset = 0.01f; + +using namespace physx; +using namespace Cct; +using namespace Gu; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: HINT: disable gUsePartialUpdates for easier visualization +static void visualizeTouchedTriangles(PxU32 nbTrisToRender, PxU32 startIndex, const PxTriangle* triangles, RenderBuffer* renderBuffer, const PxVec3& offset, const PxVec3& upDirection) +{ + if(!renderBuffer) + return; + + PxVec3 yy = -offset; + yy += upDirection * gDebugVisOffset; // PT: move slightly in the up direction + + for(PxU32 i=0; inbTessellation++; + + // PT: this one is safe, v0/v1/v2 can always be V4Loaded + if(!intersectTriangleBox_Unsafe(tp->cullingBoxCenter, tp->cullingBoxExtents, v0, v1, v2)) + return; + + PxU32 code; + { + const PxVec3 edge0 = v0 - v1; + const PxVec3 edge1 = v1 - v2; + const PxVec3 edge2 = v2 - v0; + const float maxEdgeLength2 = tp->maxEdgeLength2; + const bool split0 = edge0.magnitudeSquared()>maxEdgeLength2; + const bool split1 = edge1.magnitudeSquared()>maxEdgeLength2; + const bool split2 = edge2.magnitudeSquared()>maxEdgeLength2; + code = (PxU32(split2)<<2)|(PxU32(split1)<<1)|PxU32(split0); + } + + // PT: using Vec3p to make sure we can safely V4LoadU these vertices + const Vec3p m0 = (v0 + v1)*0.5f; + const Vec3p m1 = (v1 + v2)*0.5f; + const Vec3p m2 = (v2 + v0)*0.5f; + + switch(code) + { + case 0: // 000: no split + { + tp->worldTriangles->pushBack(PxTriangle(v0, v1, v2)); + tp->triIndicesArray->pushBack(tp->index); + tp->nbNewTris++; + } + break; + case 1: // 001: split edge0 + { + tessellateTriangleRecursive(tp, v0, m0, v2); + tessellateTriangleRecursive(tp, m0, v1, v2); + } + break; + case 2: // 010: split edge1 + { + tessellateTriangleRecursive(tp, v0, v1, m1); + tessellateTriangleRecursive(tp, v0, m1, v2); + } + break; + case 3: // 011: split edge0/edge1 + { + tessellateTriangleRecursive(tp, v0, m0, m1); + tessellateTriangleRecursive(tp, v0, m1, v2); + tessellateTriangleRecursive(tp, m0, v1, m1); + } + break; + case 4: // 100: split edge2 + { + tessellateTriangleRecursive(tp, v0, v1, m2); + tessellateTriangleRecursive(tp, v1, v2, m2); + } + break; + case 5: // 101: split edge0/edge2 + { + tessellateTriangleRecursive(tp, v0, m0, m2); + tessellateTriangleRecursive(tp, m0, v1, m2); + tessellateTriangleRecursive(tp, m2, v1, v2); + } + break; + case 6: // 110: split edge1/edge2 + { + tessellateTriangleRecursive(tp, v0, v1, m1); + tessellateTriangleRecursive(tp, v0, m1, m2); + tessellateTriangleRecursive(tp, m2, m1, v2); + } + break; + case 7: // 111: split edge0/edge1/edge2 + { + tessellateTriangleRecursive(tp, v0, m0, m2); + tessellateTriangleRecursive(tp, m0, v1, m1); + tessellateTriangleRecursive(tp, m2, m1, v2); + tessellateTriangleRecursive(tp, m0, m1, m2); + } + break; + }; +} + +static void tessellateTriangle(PxU32& nbNewTris, const TrianglePadded& tr, PxU32 index, TriArray& worldTriangles, IntArray& triIndicesArray, const PxBounds3& cullingBox, const CCTParams& params, PxU16& nbTessellation) +{ + TessParams tp; + tp.nbNewTris = 0; + tp.index = index; + tp.worldTriangles = &worldTriangles; + tp.triIndicesArray = &triIndicesArray; + tp.cullingBoxCenter = cullingBox.getCenter(); + tp.cullingBoxExtents = cullingBox.getExtents(); + tp.maxEdgeLength2 = params.mMaxEdgeLength2; + tp.nbTessellation = 0; + tessellateTriangleRecursive(&tp, tr.verts[0], tr.verts[1], tr.verts[2]); + + nbNewTris += tp.nbNewTris; + nbTessellation += tp.nbTessellation; +// nbTessellation += PxU16(tp.nbNewTris); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputPlaneToStream(PxShape* planeShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer) +{ + PX_ASSERT(planeShape->getGeometryType() == PxGeometryType::ePLANE); + + const PxF32 length = (tmpBounds.maximum - tmpBounds.minimum).magnitude(); + PxVec3 center = toVec3(origin); + + const PxPlane plane = PxPlaneEquationFromTransform(globalPose); + + PxVec3 right, up; + Ps::computeBasis(plane.n, right, up); + right *= length; + up *= length; + + const PxVec3 p = plane.project(center); + const PxVec3 p0 = p - right + up; + const PxVec3 p1 = p - right - up; + const PxVec3 p2 = p + right - up; + const PxVec3 p3 = p + right + up; + + const PxU32 nbTouchedTris = 2; + + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + + TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = planeShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mNbTris = nbTouchedTris; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(nbTouchedTris); + + triIndicesArray.pushBack(0); + triIndicesArray.pushBack(1); + + TouchedTriangles[0].verts[0] = p0 + offset; + TouchedTriangles[0].verts[1] = p1 + offset; + TouchedTriangles[0].verts[2] = p2 + offset; + + TouchedTriangles[1].verts[0] = p0 + offset; + TouchedTriangles[1].verts[1] = p2 + offset; + TouchedTriangles[1].verts[2] = p3 + offset; + + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); +} + +static void outputSphereToStream(PxShape* sphereShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, const PxExtendedVec3& origin) +{ + PX_ASSERT(sphereShape->getGeometryType() == PxGeometryType::eSPHERE); + PxExtendedSphere WorldSphere; + { + PxSphereGeometry sg; + sphereShape->getSphereGeometry(sg); + + WorldSphere.radius = sg.radius; + WorldSphere.center.x = PxExtended(globalPose.p.x); + WorldSphere.center.y = PxExtended(globalPose.p.y); + WorldSphere.center.z = PxExtended(globalPose.p.z); + } + + TouchedSphere* PX_RESTRICT touchedSphere = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedSphere)/sizeof(PxU32))); + touchedSphere->mType = TouchedGeomType::eSPHERE; + touchedSphere->mTGUserData = sphereShape; + touchedSphere->mActor = actor; + touchedSphere->mOffset = origin; + touchedSphere->mRadius = WorldSphere.radius; + touchedSphere->mCenter.x = float(WorldSphere.center.x - origin.x); + touchedSphere->mCenter.y = float(WorldSphere.center.y - origin.y); + touchedSphere->mCenter.z = float(WorldSphere.center.z - origin.z); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputCapsuleToStream(PxShape* capsuleShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, const PxExtendedVec3& origin) +{ + PX_ASSERT(capsuleShape->getGeometryType() == PxGeometryType::eCAPSULE); + PxExtendedCapsule WorldCapsule; + { + PxCapsuleGeometry cg; + capsuleShape->getCapsuleGeometry(cg); + + PxVec3 p0 = cg.halfHeight * globalPose.q.getBasisVector0(); + PxVec3 p1 = -p0; + p0 += globalPose.p; + p1 += globalPose.p; + + WorldCapsule.radius = cg.radius; + WorldCapsule.p0.x = PxExtended(p0.x); + WorldCapsule.p0.y = PxExtended(p0.y); + WorldCapsule.p0.z = PxExtended(p0.z); + WorldCapsule.p1.x = PxExtended(p1.x); + WorldCapsule.p1.y = PxExtended(p1.y); + WorldCapsule.p1.z = PxExtended(p1.z); + } + + TouchedCapsule* PX_RESTRICT touchedCapsule = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedCapsule)/sizeof(PxU32))); + touchedCapsule->mType = TouchedGeomType::eCAPSULE; + touchedCapsule->mTGUserData = capsuleShape; + touchedCapsule->mActor = actor; + touchedCapsule->mOffset = origin; + touchedCapsule->mRadius = WorldCapsule.radius; + touchedCapsule->mP0.x = float(WorldCapsule.p0.x - origin.x); + touchedCapsule->mP0.y = float(WorldCapsule.p0.y - origin.y); + touchedCapsule->mP0.z = float(WorldCapsule.p0.z - origin.z); + touchedCapsule->mP1.x = float(WorldCapsule.p1.x - origin.x); + touchedCapsule->mP1.y = float(WorldCapsule.p1.y - origin.y); + touchedCapsule->mP1.z = float(WorldCapsule.p1.z - origin.z); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputBoxToStream( PxShape* boxShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, PxU16& nbTessellation) +{ + PX_ASSERT(boxShape->getGeometryType() == PxGeometryType::eBOX); + PxBoxGeometry bg; + boxShape->getBoxGeometry(bg); + + //8 verts in local space. + const PxF32 dx = bg.halfExtents.x; + const PxF32 dy = bg.halfExtents.y; + const PxF32 dz = bg.halfExtents.z; + PxVec3 boxVerts[8]= + { + PxVec3(-dx,-dy,-dz), + PxVec3(+dx,-dy,-dz), + PxVec3(+dx,+dy,-dz), + PxVec3(-dx,+dy,-dz), + PxVec3(-dx,-dy,+dz), + PxVec3(+dx,-dy,+dz), + PxVec3(+dx,+dy,+dz), + PxVec3(-dx,+dy,+dz) + }; + //Transform verts into world space. + const PxVec3 pxOrigin = toVec3(origin); + for(PxU32 i = 0; i < 8; i++) + { + boxVerts[i] = globalPose.transform(boxVerts[i]) - pxOrigin; + } + + //Index of triangles. + const PxU32 boxTris[12][3]= + { + {0,2,1}, + {2,0,3}, //0,1,2,3 + + {3,6,2}, + {6,3,7}, //3,2,6,7 + + {7,5,6}, + {5,7,4}, //7,6,5,4 + + {4,1,5}, + {1,4,0}, //4,5,1,0 + + {0,7,3}, + {7,0,4}, //0,3,7,4 + + {2,5,1}, + {5,2,6} //2,1,5,6 + }; + + TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = boxShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + if(params.mTessellation) + { + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + const PxBounds3 cullingBox = PxBounds3::centerExtents(tmpBounds.getCenter() + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i<12; i++) + { + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + currentTriangle.verts[0] = boxVerts[boxTris[i][0]]; + currentTriangle.verts[1] = boxVerts[boxTris[i][1]]; + currentTriangle.verts[2] = boxVerts[boxTris[i][2]]; + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, PX_INVALID_U32, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + touchedMesh->mNbTris = 12; + + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(12); + + for(PxU32 i=0; i<12; i++) + { + PxTriangle& currentTriangle = TouchedTriangles[i]; + currentTriangle.verts[0] = boxVerts[boxTris[i][0]]; + currentTriangle.verts[1] = boxVerts[boxTris[i][1]]; + currentTriangle.verts[2] = boxVerts[boxTris[i][2]]; + + triIndicesArray.pushBack(PX_INVALID_U32); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PxU32 createInvisibleWalls(const CCTParams& params, const PxTriangle& currentTriangle, TriArray& worldTriangles, IntArray& triIndicesArray) +{ + const PxF32 wallHeight = params.mInvisibleWallHeight; + if(wallHeight==0.0f) + return 0; + + PxU32 nbNewTris = 0; // Number of newly created tris + + const PxVec3& upDirection = params.mUpDirection; + + PxVec3 normal; + currentTriangle.normal(normal); + if(testSlope(normal, upDirection, params.mSlopeLimit)) + { + const PxVec3 upWall = upDirection*wallHeight; + PxVec3 v0p = currentTriangle.verts[0] + upWall; + PxVec3 v1p = currentTriangle.verts[1] + upWall; + PxVec3 v2p = currentTriangle.verts[2] + upWall; + + // Extrude edge 0-1 + PxVec3 faceNormal01; + { + // 0-1-0p + const PxTriangle tri0_1_0p(currentTriangle.verts[0], currentTriangle.verts[1], v0p); + worldTriangles.pushBack(tri0_1_0p); + + // 0p-1-1p + const PxTriangle tri0p_1_1p(v0p, currentTriangle.verts[1], v1p); + worldTriangles.pushBack(tri0p_1_1p); + + tri0p_1_1p.normal(faceNormal01); + } + + // Extrude edge 1-2 + PxVec3 faceNormal12; + { + // 1p-1-2p + const PxTriangle tri1p_1_2p(v1p, currentTriangle.verts[1], v2p); + worldTriangles.pushBack(tri1p_1_2p); + + // 2p-1-2 + const PxTriangle tri2p_1_2(v2p, currentTriangle.verts[1], currentTriangle.verts[2]); + worldTriangles.pushBack(tri2p_1_2); + + tri2p_1_2.normal(faceNormal12); + } + + // Extrude edge 2-0 + PxVec3 faceNormal20; + { + // 0p-2-0 + const PxTriangle tri0p_2_0(v0p, currentTriangle.verts[2], currentTriangle.verts[0]); + worldTriangles.pushBack(tri0p_2_0); + + // 0p-2p-2 + const PxTriangle tri0p_2p_2(v0p, v2p, currentTriangle.verts[2]); + worldTriangles.pushBack(tri0p_2p_2); + + tri0p_2p_2.normal(faceNormal20); + } + + const PxU32 triIndex = PX_INVALID_U32; + for(PxU32 i=0;i<6;i++) + triIndicesArray.pushBack(triIndex); + + nbNewTris += 6; + } + return nbNewTris; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputMeshToStream( PxShape* meshShape, const PxRigidActor* actor, const PxTransform& meshPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer, PxU16& nbTessellation) +{ + PX_ASSERT(meshShape->getGeometryType() == PxGeometryType::eTRIANGLEMESH); + // Do AABB-mesh query + + PxTriangleMeshGeometry triGeom; + meshShape->getTriangleMeshGeometry(triGeom); + + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxTransform boxPose(tmpBounds.getCenter(), PxQuat(PxIdentity)); + + // Collide AABB against current mesh + PxMeshOverlapUtil overlapUtil; + const PxU32 nbTouchedTris = overlapUtil.findOverlap(boxGeom, boxPose, triGeom, meshPose); + + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + + TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = meshShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mNbTris = nbTouchedTris; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + const PxU32* PX_RESTRICT indices = overlapUtil.getResults(); + + if(params.mSlopeLimit!=0.0f) + { + if(!params.mTessellation) + { + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + const PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { + worldTriangles.pushBack(currentTriangle); + + triIndicesArray.pushBack(triangleIndex); + nbCreatedTris++; + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { +/* worldTriangles.pushBack(currentTriangle); + triIndicesArray.pushBack(triangleIndex); + nbCreatedTris++;*/ + + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; +// printf("Tesselate: %d new tris\n", nbNewTris); + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + else + { + if(!params.mTessellation) + { + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(nbTouchedTris); + + // Loop through touched triangles + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + PxTriangle& currentTriangle = *TouchedTriangles++; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + triIndicesArray.pushBack(triangleIndex); + } + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); +// printf("Tesselate: %d new tris\n", nbNewTris); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputHeightFieldToStream( PxShape* hfShape, const PxRigidActor* actor, const PxTransform& heightfieldPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer, PxU16& nbTessellation) +{ + PX_ASSERT(hfShape->getGeometryType() == PxGeometryType::eHEIGHTFIELD); + // Do AABB-mesh query + + PxHeightFieldGeometry hfGeom; + hfShape->getHeightFieldGeometry(hfGeom); + + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxTransform boxPose(tmpBounds.getCenter(), PxQuat(PxIdentity)); + + // Collide AABB against current heightfield + PxMeshOverlapUtil overlapUtil; + const PxU32 nbTouchedTris = overlapUtil.findOverlap(boxGeom, boxPose, hfGeom, heightfieldPose); + + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + + TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; // ptchernev: seems to work + touchedMesh->mTGUserData = hfShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mNbTris = nbTouchedTris; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + const PxU32* PX_RESTRICT indices = overlapUtil.getResults(); + + if(params.mSlopeLimit!=0.0f) + { + if(!params.mTessellation) + { + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + const PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { + worldTriangles.pushBack(currentTriangle); + + triIndicesArray.pushBack(triangleIndex); + nbCreatedTris++; + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; +// printf("Tesselate: %d new tris\n", nbNewTris); + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + else + { + if(!params.mTessellation) + { + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(nbTouchedTris); + + // Loop through touched triangles + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + PxTriangle& currentTriangle = *TouchedTriangles++; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + triIndicesArray.pushBack(triangleIndex); + } + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); +// printf("Tesselate: %d new tris\n", nbNewTris); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputConvexToStream(PxShape* convexShape, const PxRigidActor* actor, const PxTransform& absPose_, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer, PxU16& nbTessellation) +{ + PX_ASSERT(convexShape->getGeometryType() == PxGeometryType::eCONVEXMESH); + PxConvexMeshGeometry cg; + convexShape->getConvexMeshGeometry(cg); + PX_ASSERT(cg.convexMesh); + + // Do AABB-mesh query + + PxU32* TF; + + // Collide AABB against current mesh + // The overlap function doesn't exist for convexes so let's just dump all tris + PxConvexMesh& cm = *cg.convexMesh; + + // PT: convex triangles are not exposed anymore so we need to access convex polygons & triangulate them + + // PT: TODO: this is copied from "DrawObjects", move this to a shared place. Actually a helper directly in PxConvexMesh would be useful. + PxU32 Nb = 0; + { + const PxU32 nbPolys = cm.getNbPolygons(); + const PxU8* polygons = cm.getIndexBuffer(); + + for(PxU32 i=0;i(PxAlloca(sizeof(PxU32)*Nb*3)); + PxU32* t = TF; + for(PxU32 i=0;i(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = convexShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + const PxVec3* verts = cm.getVertices(); + // Loop through touched triangles + if(params.mTessellation) + { + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxBounds3 cullingBox = PxBounds3::centerExtents(tmpBounds.getCenter() + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + while(Nb--) + { + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + + const PxU32 vref0 = *TF++; + const PxU32 vref1 = *TF++; + const PxU32 vref2 = *TF++; + + currentTriangle.verts[0] = MeshOffset + absPose.rotate(verts[vref0]); + currentTriangle.verts[1] = MeshOffset + absPose.rotate(verts[vref1]); + currentTriangle.verts[2] = MeshOffset + absPose.rotate(verts[vref2]); + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, PX_INVALID_U32, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(Nb); + + touchedMesh->mNbTris = Nb; + while(Nb--) + { + // Compute triangle in world space, add to array + PxTriangle& currentTriangle = *TouchedTriangles++; + + const PxU32 vref0 = *TF++; + const PxU32 vref1 = *TF++; + const PxU32 vref2 = *TF++; + + currentTriangle.verts[0] = MeshOffset + absPose.rotate(verts[vref0]); + currentTriangle.verts[1] = MeshOffset + absPose.rotate(verts[vref1]); + currentTriangle.verts[2] = MeshOffset + absPose.rotate(verts[vref2]); + + triIndicesArray.pushBack(PX_INVALID_U32); + } + } + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxU32 Cct::getSceneTimestamp(const InternalCBData_FindTouchedGeom* userData) +{ + PX_ASSERT(userData); + const PxInternalCBData_FindTouchedGeom* internalData = static_cast(userData); + PxScene* scene = internalData->scene; + return scene->getSceneQueryStaticTimestamp(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Cct::findTouchedGeometry( + const InternalCBData_FindTouchedGeom* userData, + const PxExtendedBounds3& worldBounds, // ### we should also accept other volumes + + TriArray& worldTriangles, + IntArray& triIndicesArray, + IntArray& geomStream, + + const CCTFilter& filter, + const CCTParams& params, + PxU16& nbTessellation) +{ + PX_ASSERT(userData); + + const PxInternalCBData_FindTouchedGeom* internalData = static_cast(userData); + PxScene* scene = internalData->scene; + + PX_PROFILE_ZONE("CharacterController.findTouchedGeometry", PxU64(reinterpret_cast(scene))); + + RenderBuffer* renderBuffer = internalData->renderBuffer; + + PxExtendedVec3 Origin; // Will be TouchedGeom::mOffset + getCenter(worldBounds, Origin); + + // Find touched *boxes* i.e. touched objects' AABBs in the world + // We collide against dynamic shapes too, to get back dynamic boxes/etc + // TODO: add active groups in interface! + PxQueryFlags sqFilterFlags; + if(filter.mStaticShapes) sqFilterFlags |= PxQueryFlag::eSTATIC; + if(filter.mDynamicShapes) sqFilterFlags |= PxQueryFlag::eDYNAMIC; + if(filter.mFilterCallback) + { + if(filter.mPreFilter) + sqFilterFlags |= PxQueryFlag::ePREFILTER; + if(filter.mPostFilter) + sqFilterFlags |= PxQueryFlag::ePOSTFILTER; + } + + // ### this one is dangerous + const PxBounds3 tmpBounds(toVec3(worldBounds.minimum), toVec3(worldBounds.maximum)); // LOSS OF ACCURACY + + // PT: unfortunate conversion forced by the PxGeometry API + const PxVec3 center = tmpBounds.getCenter(); + const PxVec3 extents = tmpBounds.getExtents(); + + const PxU32 size = 100; + PxOverlapHit hits[size]; + + PxQueryFilterData sceneQueryFilterData = filter.mFilterData ? PxQueryFilterData(*filter.mFilterData, sqFilterFlags) : PxQueryFilterData(sqFilterFlags); + + PxOverlapBuffer hitBuffer(hits, size); + sceneQueryFilterData.flags |= PxQueryFlag::eNO_BLOCK; // fix for DE8255 + scene->overlap(PxBoxGeometry(extents), PxTransform(center), hitBuffer, sceneQueryFilterData, filter.mFilterCallback); + PxU32 numberHits = hitBuffer.getNbAnyHits(); + for(PxU32 i = 0; i < numberHits; i++) + { + const PxOverlapHit& hit = hitBuffer.getAnyHit(i); + PxShape* shape = hit.shape; + PxRigidActor* actor = hit.actor; + if(!shape || !actor) + continue; + + // Filtering + + // Discard all CCT shapes, i.e. kinematic actors we created ourselves. We don't need to collide with them since they're surrounded + // by the real CCT volume - and collisions with those are handled elsewhere. + if(internalData->cctShapeHashSet->contains(shape)) + continue; + + // Ubi (EA) : Discarding Triggers : + if(shape->getFlags() & PxShapeFlag::eTRIGGER_SHAPE) + continue; + + // PT: here you might want to disable kinematic objects. + + // Output shape to stream + const PxTransform globalPose = getShapeGlobalPose(*shape, *actor); + + const PxGeometryType::Enum type = shape->getGeometryType(); // ### VIRTUAL! + if(type==PxGeometryType::eSPHERE) outputSphereToStream (shape, actor, globalPose, geomStream, Origin); + else if(type==PxGeometryType::eCAPSULE) outputCapsuleToStream (shape, actor, globalPose, geomStream, Origin); + else if(type==PxGeometryType::eBOX) outputBoxToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, nbTessellation); + else if(type==PxGeometryType::eTRIANGLEMESH) outputMeshToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); + else if(type==PxGeometryType::eHEIGHTFIELD) outputHeightFieldToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); + else if(type==PxGeometryType::eCONVEXMESH) outputConvexToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); + else if(type==PxGeometryType::ePLANE) outputPlaneToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "CctCharacterControllerManager.h" +#include "CctObstacleContext.h" +#include "PxControllerBehavior.h" + +// #### hmmm, in the down case, isn't reported length too big ? It contains our artificial up component, +// that might confuse the user + +static void fillCCTHit(PxControllerHit& hit, const SweptContact& contact, const PxVec3& dir, float length, Controller* controller) +{ + hit.controller = controller->getPxController(); + hit.worldPos = contact.mWorldPos; + hit.worldNormal = contact.mWorldNormal; + hit.dir = dir; + hit.length = length; +} + +static const PxU32 defaultBehaviorFlags = 0; + +PxU32 Cct::shapeHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, float length) +{ + Controller* controller = static_cast(userData)->controller; + + PxControllerShapeHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + hit.shape = const_cast(reinterpret_cast(contact.mGeom->mTGUserData)); + hit.actor = const_cast(contact.mGeom->mActor); + hit.triangleIndex = contact.mTriangleIndex; + + if(controller->mReportCallback) + controller->mReportCallback->onShapeHit(hit); + + PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; + return behaviorCB ? behaviorCB->getBehaviorFlags(*hit.shape, *hit.actor) : defaultBehaviorFlags; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxU32 handleObstacleHit(const PxObstacle& touchedObstacle, const ObstacleHandle& obstacleHandle, PxControllerObstacleHit& hit, const PxInternalCBData_OnHit* internalData, Controller* controller) +{ + hit.userData = touchedObstacle.mUserData; + const_cast(internalData)->touchedObstacle = &touchedObstacle; // (*) PT: TODO: revisit + const_cast(internalData)->touchedObstacleHandle = obstacleHandle; + + if(controller->mReportCallback) + controller->mReportCallback->onObstacleHit(hit); + + PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; + return behaviorCB ? behaviorCB->getBehaviorFlags(touchedObstacle) : defaultBehaviorFlags; +} + +PxU32 Cct::userHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, float length) +{ + const PxInternalCBData_OnHit* internalData = static_cast(userData); + Controller* controller = internalData->controller; + + const PxU32 objectCode = PxU32(size_t(contact.mGeom->mTGUserData)); + const UserObjectType type = decodeType(objectCode); + const PxU32 index = decodeIndex(objectCode); + + if(type==USER_OBJECT_CCT) + { + PX_ASSERT(indexgetCctManager()->getNbControllers()); + Controller** controllers = controller->getCctManager()->getControllers(); + Controller* other = controllers[index]; + + PxControllersHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + hit.other = other->getPxController(); + + if(controller->mReportCallback) + controller->mReportCallback->onControllerHit(hit); + + PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; + return behaviorCB ? behaviorCB->getBehaviorFlags(*hit.other) : defaultBehaviorFlags; + } + else if(type==USER_OBJECT_BOX_OBSTACLE) + { + PX_ASSERT(internalData->obstacles); + PX_ASSERT(indexobstacles->mBoxObstacles.size()); + + PxControllerObstacleHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + const ObstacleContext::InternalBoxObstacle& obstacle = internalData->obstacles->mBoxObstacles[index]; + const PxBoxObstacle& touchedObstacle = obstacle.mData; + return handleObstacleHit(touchedObstacle, obstacle.mHandle , hit, internalData, controller); + } + else if(type==USER_OBJECT_CAPSULE_OBSTACLE) + { + PX_ASSERT(internalData->obstacles); + PX_ASSERT(indexobstacles->mCapsuleObstacles.size()); + + PxControllerObstacleHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + const ObstacleContext::InternalCapsuleObstacle& obstacle = internalData->obstacles->mCapsuleObstacles[index]; + const PxCapsuleObstacle& touchedObstacle = obstacle.mData; + return handleObstacleHit(touchedObstacle, obstacle.mHandle, hit, internalData, controller); + } + else PX_ASSERT(0); + + return defaultBehaviorFlags; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp new file mode 100644 index 000000000..007b50603 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp @@ -0,0 +1,661 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CctCharacterControllerManager.h" +#include "CctBoxController.h" +#include "CctCapsuleController.h" +#include "CctObstacleContext.h" +#include "CmBoxPruning.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentBox.h" +#include "PsUtilities.h" +#include "PsMathUtils.h" +#include "PxRigidDynamic.h" +#include "PxScene.h" +#include "PxPhysics.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Cct; + +static const PxF32 gMaxOverlapRecover = 4.0f; // PT: TODO: expose this + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +CharacterControllerManager::CharacterControllerManager(PxScene& scene, bool lockingEnabled) : + mScene (scene), + mRenderBuffer (NULL), + mDebugRenderingFlags (0), + mMaxEdgeLength (1.0f), + mTessellation (false), + mOverlapRecovery (true), + mPreciseSweeps (true), + mPreventVerticalSlidingAgainstCeiling (false), + mLockingEnabled (lockingEnabled) +{ + // PT: register ourself as a deletion listener, to be called by the SDK whenever an object is deleted + PxPhysics& physics = scene.getPhysics(); + physics.registerDeletionListener(*this, PxDeletionEventFlag::eUSER_RELEASE); +} + +CharacterControllerManager::~CharacterControllerManager() +{ + if(mRenderBuffer) + { + delete mRenderBuffer; + mRenderBuffer = 0; + } +} + +void CharacterControllerManager::release() +{ + // PT: TODO: use non virtual calls & move to dtor + while(getNbControllers()!= 0) + releaseController(*getController(0)); + + while(getNbObstacleContexts()!= 0) + mObstacleContexts[0]->release(); + + PxPhysics& physics = mScene.getPhysics(); + physics.unregisterDeletionListener(*this); + + delete this; + + Ps::Foundation::decRefCount(); +} + +PxScene& CharacterControllerManager::getScene() const +{ + return mScene; +} + +PxRenderBuffer& CharacterControllerManager::getRenderBuffer() +{ + if(!mRenderBuffer) + mRenderBuffer = PX_NEW(Cm::RenderBuffer); + + return *mRenderBuffer; +} + +void CharacterControllerManager::setDebugRenderingFlags(PxControllerDebugRenderFlags flags) +{ + mDebugRenderingFlags = flags; + + if(!flags) + { + if(mRenderBuffer) + { + delete mRenderBuffer; + mRenderBuffer = 0; + } + } +} + +PxU32 CharacterControllerManager::getNbControllers() const +{ + return mControllers.size(); +} + +Controller** CharacterControllerManager::getControllers() +{ + return mControllers.begin(); +} + +PxController* CharacterControllerManager::getController(PxU32 index) +{ + if(index>=mControllers.size()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxControllerManager::getController(): out-of-range index"); + return NULL; + } + + PX_ASSERT(mControllers[index]); + return mControllers[index]->getPxController(); +} + +PxController* CharacterControllerManager::createController(const PxControllerDesc& desc) +{ + if(!desc.isValid()) + return NULL; + + Controller* newController = NULL; + + PxController* N = NULL; + if(desc.getType()==PxControllerShapeType::eBOX) + { + BoxController* boxController = PX_NEW(BoxController)(desc, mScene.getPhysics(), &mScene); + newController = boxController; + N = boxController; + } + else if(desc.getType()==PxControllerShapeType::eCAPSULE) + { + CapsuleController* capsuleController = PX_NEW(CapsuleController)(desc, mScene.getPhysics(), &mScene); + newController = capsuleController; + N = capsuleController; + } + else PX_ALWAYS_ASSERT_MESSAGE( "INTERNAL ERROR - invalid CCT type, should have been caught by isValid()."); + + if(newController) + { + mControllers.pushBack(newController); + newController->setCctManager(this); + + PxShape* shape = NULL; + PxU32 nb = N->getActor()->getShapes(&shape, 1); + PX_ASSERT(nb==1); + PX_UNUSED(nb); + mCCTShapes.insert(shape); + } + + return N; +} + +void CharacterControllerManager::releaseController(PxController& controller) +{ + for(PxU32 i = 0; igetPxController() == &controller) + { + mControllers.replaceWithLast(i); + break; + } + } + + PxShape* shape = NULL; + PxU32 nb = controller.getActor()->getShapes(&shape, 1); + PX_ASSERT(nb==1); + PX_UNUSED(nb); + mCCTShapes.erase(shape); + + if(controller.getType() == PxControllerShapeType::eCAPSULE) + { + CapsuleController* cc = static_cast(&controller); + PX_DELETE(cc); + } + else if(controller.getType() == PxControllerShapeType::eBOX) + { + BoxController* bc = static_cast(&controller); + PX_DELETE(bc); + } + else PX_ASSERT(0); +} + +void CharacterControllerManager::purgeControllers() +{ + while(mControllers.size()) + releaseController(*mControllers[0]->getPxController()); +} + +void CharacterControllerManager::onRelease(const PxBase* observed, void* , PxDeletionEventFlag::Enum deletionEvent) +{ + PX_ASSERT(deletionEvent == PxDeletionEventFlag::eUSER_RELEASE); // the only type we registered for + PX_UNUSED(deletionEvent); + + if(!(observed->getConcreteType()==PxConcreteType:: eRIGID_DYNAMIC || observed->getConcreteType()==PxConcreteType:: eRIGID_STATIC || + observed->getConcreteType()==PxConcreteType::eSHAPE)) + return; + + // check if object was registered + if(mLockingEnabled) + mWriteLock.lock(); + + const ObservedRefCountMap::Entry* releaseEntry = mObservedRefCountMap.find(observed); + if(mLockingEnabled) + mWriteLock.unlock(); + + if(releaseEntry) + { + for (PxU32 i = 0; i < mControllers.size(); i++) + { + Controller* controller = mControllers[i]; + if(mLockingEnabled) + controller->mWriteLock.lock(); + + controller->onRelease(*observed); + + if(mLockingEnabled) + controller->mWriteLock.unlock(); + } + } +} + +void CharacterControllerManager::registerObservedObject(const PxBase* obj) +{ + if(mLockingEnabled) + mWriteLock.lock(); + + mObservedRefCountMap[obj].refCount++; + + if(mLockingEnabled) + mWriteLock.unlock(); +} + +void CharacterControllerManager::unregisterObservedObject(const PxBase* obj) +{ + if(mLockingEnabled) + mWriteLock.lock(); + + ObservedRefCounter& refCounter = mObservedRefCountMap[obj]; + PX_ASSERT(refCounter.refCount); + refCounter.refCount--; + if(!refCounter.refCount) + mObservedRefCountMap.erase(obj); + + if(mLockingEnabled) + mWriteLock.unlock(); +} + +PxU32 CharacterControllerManager::getNbObstacleContexts() const +{ + return mObstacleContexts.size(); +} + +PxObstacleContext* CharacterControllerManager::getObstacleContext(PxU32 index) +{ + if(index>=mObstacleContexts.size()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxControllerManager::getObstacleContext(): out-of-range index"); + return NULL; + } + + PX_ASSERT(mObstacleContexts[index]); + return mObstacleContexts[index]; +} + +PxObstacleContext* CharacterControllerManager::createObstacleContext() +{ + ObstacleContext* oc = PX_NEW(ObstacleContext)(*this); + + mObstacleContexts.pushBack(oc); + + return oc; +} + +void CharacterControllerManager::releaseObstacleContext(ObstacleContext& oc) +{ + PX_ASSERT(mObstacleContexts.find(&oc) != mObstacleContexts.end()); + mObstacleContexts.findAndReplaceWithLast(&oc); + + PX_DELETE(&oc); +} + +void CharacterControllerManager::onObstacleRemoved(ObstacleHandle index) const +{ + for(PxU32 i = 0; imCctModule.onObstacleRemoved(index); + } +} + +void CharacterControllerManager::onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* context) const +{ + for(PxU32 i = 0; imCctModule.onObstacleUpdated(index,context, toVec3(mControllers[i]->mPosition), -mControllers[i]->mUserParams.mUpDirection, mControllers[i]->getHalfHeightInternal()); + } +} + +void CharacterControllerManager::onObstacleAdded(ObstacleHandle index, const PxObstacleContext* context) const +{ + for(PxU32 i = 0; imCctModule.onObstacleAdded(index,context, toVec3(mControllers[i]->mPosition), -mControllers[i]->mUserParams.mUpDirection, mControllers[i]->getHalfHeightInternal()); + } +} + + +// PT: TODO: move to array class? +template +void resetOrClear(T& a) +{ + const PxU32 c = a.capacity(); + if(!c) + return; + const PxU32 s = a.size(); + if(s>c/2) + a.clear(); + else + a.reset(); +} + +void CharacterControllerManager::resetObstaclesBuffers() +{ + resetOrClear(mBoxUserData); + resetOrClear(mBoxes); + resetOrClear(mCapsuleUserData); + resetOrClear(mCapsules); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CharacterControllerManager::setTessellation(bool flag, float maxEdgeLength) +{ + mTessellation = flag; + mMaxEdgeLength = maxEdgeLength; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CharacterControllerManager::setOverlapRecoveryModule(bool flag) +{ + mOverlapRecovery = flag; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CharacterControllerManager::setPreciseSweeps(bool flag) +{ + mPreciseSweeps = flag; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CharacterControllerManager::setPreventVerticalSlidingAgainstCeiling(bool flag) +{ + mPreventVerticalSlidingAgainstCeiling = flag; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CharacterControllerManager::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i=0; i < mControllers.size(); i++) + { + mControllers[i]->onOriginShift(shift); + } + + for(PxU32 i=0; i < mObstacleContexts.size(); i++) + { + mObstacleContexts[i]->onOriginShift(shift); + } + + if (mRenderBuffer) + mRenderBuffer->shift(-shift); + + // assumption is that these are just used for temporary stuff + PX_ASSERT(!mBoxes.size()); + PX_ASSERT(!mCapsules.size()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD(PxVec3& mtd, PxF32& depth, const PxVec3& e0, const PxVec3& c0, const PxMat33& r0, const PxVec3& e1, const PxVec3& c1, const PxMat33& r1) +{ + // Translation, in parent frame + const PxVec3 v = c1 - c0; + // Translation, in A's frame + const PxVec3 T(v.dot(r0[0]), v.dot(r0[1]), v.dot(r0[2])); + + // B's basis with respect to A's local frame + PxReal R[3][3]; + PxReal FR[3][3]; + PxReal ra, rb, t, d; + + PxReal overlap[15]; + + // Calculate rotation matrix + for(PxU32 i=0;i<3;i++) + { + for(PxU32 k=0;k<3;k++) + { + R[i][k] = r0[i].dot(r1[k]); + FR[i][k] = 1e-6f + PxAbs(R[i][k]); // Precompute fabs matrix + } + } + + // A's basis vectors + for(PxU32 i=0;i<3;i++) + { + ra = e0[i]; + rb = e1[0]*FR[i][0] + e1[1]*FR[i][1] + e1[2]*FR[i][2]; + t = PxAbs(T[i]); + + d = ra + rb - t; + if(d<0.0f) + return false; + + overlap[i] = d; + } + + // B's basis vectors + for(PxU32 k=0;k<3;k++) + { + ra = e0[0]*FR[0][k] + e0[1]*FR[1][k] + e0[2]*FR[2][k]; + rb = e1[k]; + t = PxAbs(T[0]*R[0][k] + T[1]*R[1][k] + T[2]*R[2][k]); + + d = ra + rb - t; + if(d<0.0f) + return false; + + overlap[k+3] = d; + } + + // PT: edge-edge tests are skipped, by design + + PxU32 minIndex=0; + PxReal minD = overlap[0]; + for(PxU32 i=1;i<6;i++) + { + if(overlap[i]mType>entity1->mType) + Ps::swap(entity0, entity1); + + if(entity0->mType==PxControllerShapeType::eCAPSULE && entity1->mType==PxControllerShapeType::eCAPSULE) + { + CapsuleController* cc0 = static_cast(entity0); + CapsuleController* cc1 = static_cast(entity1); + + PxExtendedCapsule capsule0; + cc0->getCapsule(capsule0); + + PxExtendedCapsule capsule1; + cc1->getCapsule(capsule1); + + const PxF32 r = capsule0.radius + capsule1.radius; + + const PxVec3 p00 = toVec3(capsule0.p0); + const PxVec3 p01 = toVec3(capsule0.p1); + const PxVec3 p10 = toVec3(capsule1.p0); + const PxVec3 p11 = toVec3(capsule1.p1); + + PxF32 s,t; + const PxF32 d = sqrtf(Gu::distanceSegmentSegmentSquared(p00, p01 - p00, p10, p11 - p10, &s, &t)); + if(dmCctModule.mUserParams.mUpDirection; + dir = fixDir(center0 - center1, up); + overlap = r - d; + } + } + else if(entity0->mType==PxControllerShapeType::eBOX && entity1->mType==PxControllerShapeType::eCAPSULE) + { + BoxController* cc0 = static_cast(entity0); + CapsuleController* cc1 = static_cast(entity1); + + PxExtendedBox obb; + cc0->getOBB(obb); + + PxExtendedCapsule capsule; + cc1->getCapsule(capsule); + const PxVec3 p0 = toVec3(capsule.p0); + const PxVec3 p1 = toVec3(capsule.p1); + + PxF32 t; + PxVec3 p; + const PxMat33 M(obb.rot); + const PxVec3 boxCenter = toVec3(obb.center); + const PxF32 d = sqrtf(Gu::distanceSegmentBoxSquared(p0, p1, boxCenter, obb.extents, M, &t, &p)); + if(dmCctModule.mUserParams.mUpDirection; + dir = fixDir(center0 - center1, up); + overlap = capsule.radius - d; + } + } + else + { + PX_ASSERT(entity0->mType==PxControllerShapeType::eBOX); + PX_ASSERT(entity1->mType==PxControllerShapeType::eBOX); + BoxController* cc0 = static_cast(entity0); + BoxController* cc1 = static_cast(entity1); + + PxExtendedBox obb0; + cc0->getOBB(obb0); + + PxExtendedBox obb1; + cc1->getOBB(obb1); + + PxVec3 mtd; + PxF32 depth; + if(computeMTD( mtd, depth, + obb0.extents, toVec3(obb0.center), PxMat33(obb0.rot), + obb1.extents, toVec3(obb1.center), PxMat33(obb1.rot))) + { + const PxVec3 center0 = toVec3(obb0.center); + const PxVec3 center1 = toVec3(obb1.center); + const PxVec3 witness = center0 - center1; + if(mtd.dot(witness)<0.0f) + dir = -mtd; + else + dir = mtd; + const PxVec3 up = entity0->mCctModule.mUserParams.mUpDirection; + dir = fixDir(dir, up); + overlap = depth; + } + } + + if(overlap!=0.0f) + { + // We want to limit this to some reasonable amount, to avoid obvious "popping". + const PxF32 maxOverlap = gMaxOverlapRecover * elapsedTime; + if(overlap>maxOverlap) + overlap=maxOverlap; + + const PxVec3 sep = dir * overlap * 0.5f; + entity0->mOverlapRecover += sep; + entity1->mOverlapRecover -= sep; + } +} + +void CharacterControllerManager::computeInteractions(PxF32 elapsedTime, PxControllerFilterCallback* cctFilterCb) +{ + PxU32 nbControllers = mControllers.size(); + Controller** controllers = mControllers.begin(); + + PxBounds3* boxes = reinterpret_cast(PX_ALLOC_TEMP(sizeof(PxBounds3)*nbControllers, "CharacterControllerManager::computeInteractions")); // PT: TODO: get rid of alloc + PxBounds3* runningBoxes = boxes; + + while(nbControllers--) + { + Controller* current = *controllers++; + + PxExtendedBounds3 extBox; + current->getWorldBox(extBox); + + *runningBoxes++ = PxBounds3(toVec3(extBox.minimum), toVec3(extBox.maximum)); // ### LOSS OF ACCURACY + } + + // + + const PxU32 nbEntities = PxU32(runningBoxes - boxes); + + Ps::Array pairs; // PT: TODO: get rid of alloc + Cm::CompleteBoxPruning(boxes, nbEntities, pairs, Gu::Axes(physx::Gu::AXES_XZY)); // PT: TODO: revisit for variable up axis + + PxU32 nbPairs = pairs.size()>>1; + const PxU32* indices = pairs.begin(); + while(nbPairs--) + { + const PxU32 index0 = *indices++; + const PxU32 index1 = *indices++; + Controller* ctrl0 = mControllers[index0]; + Controller* ctrl1 = mControllers[index1]; + + bool keep=true; + if(cctFilterCb) + keep = cctFilterCb->filter(*ctrl0->getPxController(), *ctrl1->getPxController()); + + if(keep) + InteractionCharacterCharacter(ctrl0, ctrl1, elapsedTime); + } + + PX_FREE(boxes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//Public factory methods + +PX_C_EXPORT PX_PHYSX_CHARACTER_API PxControllerManager* PX_CALL_CONV PxCreateControllerManager(PxScene& scene, bool lockingEnabled) +{ + Ps::Foundation::incRefCount(); + return PX_NEW(CharacterControllerManager)(scene, lockingEnabled); +} diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h new file mode 100644 index 000000000..94a64dfeb --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h @@ -0,0 +1,147 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_CHARACTER_CONTROLLER_MANAGER +#define CCT_CHARACTER_CONTROLLER_MANAGER + +//Exclude file from docs +/** \cond */ + +#include "PxControllerManager.h" +#include "PxControllerObstacles.h" +#include "PxMeshQuery.h" +#include "PxDeletionListener.h" +#include "CmRenderOutput.h" +#include "CctUtils.h" +#include "PsHashSet.h" +#include "PsHashMap.h" +#include "PsMutex.h" + +namespace physx +{ +namespace Cct +{ + class Controller; + class ObstacleContext; + + struct ObservedRefCounter + { + ObservedRefCounter(): refCount(0) + { + } + + PxU32 refCount; + }; + + typedef Ps::HashMap ObservedRefCountMap; + + //Implements the PxControllerManager interface, this class used to be called ControllerManager + class CharacterControllerManager : public PxControllerManager , public Ps::UserAllocated, public PxDeletionListener + { + public: + CharacterControllerManager(PxScene& scene, bool lockingEnabled = false); + virtual ~CharacterControllerManager(); + + // PxControllerManager + virtual void release(); + virtual PxScene& getScene() const; + virtual PxU32 getNbControllers() const; + virtual PxController* getController(PxU32 index); + virtual PxController* createController(const PxControllerDesc& desc); + + virtual void purgeControllers(); + virtual PxRenderBuffer& getRenderBuffer(); + virtual void setDebugRenderingFlags(PxControllerDebugRenderFlags flags); + virtual PxU32 getNbObstacleContexts() const; + virtual PxObstacleContext* getObstacleContext(PxU32 index); + virtual PxObstacleContext* createObstacleContext(); + virtual void computeInteractions(PxF32 elapsedTime, PxControllerFilterCallback* cctFilterCb); + virtual void setTessellation(bool flag, float maxEdgeLength); + virtual void setOverlapRecoveryModule(bool flag); + virtual void setPreciseSweeps(bool flag); + virtual void setPreventVerticalSlidingAgainstCeiling(bool flag); + virtual void shiftOrigin(const PxVec3& shift); + //~PxControllerManager + + // PxDeletionListener + virtual void onRelease(const PxBase* observed, void* userData, PxDeletionEventFlag::Enum deletionEvent); + //~PxDeletionListener + void registerObservedObject(const PxBase* obj); + void unregisterObservedObject(const PxBase* obj); + + // ObstacleContextNotifications + void onObstacleRemoved(ObstacleHandle index) const; + void onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* ) const; + void onObstacleAdded(ObstacleHandle index, const PxObstacleContext*) const; + + void releaseController(PxController& controller); + Controller** getControllers(); + void releaseObstacleContext(ObstacleContext& oc); + void resetObstaclesBuffers(); + + PxScene& mScene; + + Cm::RenderBuffer* mRenderBuffer; + PxControllerDebugRenderFlags mDebugRenderingFlags; + // Shared buffers for obstacles + Ps::Array mBoxUserData; + Ps::Array mBoxes; + + Ps::Array mCapsuleUserData; + Ps::Array mCapsules; + + Ps::Array mControllers; + Ps::HashSet mCCTShapes; + + Ps::Array mObstacleContexts; + + float mMaxEdgeLength; + bool mTessellation; + + bool mOverlapRecovery; + bool mPreciseSweeps; + bool mPreventVerticalSlidingAgainstCeiling; + + bool mLockingEnabled; + + protected: + CharacterControllerManager &operator=(const CharacterControllerManager &); + CharacterControllerManager(const CharacterControllerManager& ); + + private: + ObservedRefCountMap mObservedRefCountMap; + mutable Ps::Mutex mWriteLock; // Lock used for guarding pointers in observedrefcountmap + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif //CCT_CHARACTER_CONTROLLER_MANAGER diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp new file mode 100644 index 000000000..96cced109 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp @@ -0,0 +1,235 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxController.h" +#include "CctController.h" +#include "CctBoxController.h" +#include "CctCharacterControllerManager.h" +#include "PxScene.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" +#include "extensions/PxRigidBodyExt.h" +#include "foundation/PxMathUtils.h" +#include "PsUtilities.h" +#include "PxPhysics.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Cct; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Controller::Controller(const PxControllerDesc& desc, PxScene* s) : + mCctModule (desc.registerDeletionListener), + mScene (s), + mPreviousSceneTimestamp (0xffffffff), + mGlobalTime (0.0), + mPreviousGlobalTime (0.0), + mProxyDensity (0.0f), + mProxyScaleCoeff (0.0f), + mCollisionFlags (0), + mCachedStandingOnMoving (false), + mManager (NULL) +{ + mType = PxControllerShapeType::eFORCE_DWORD; + + mUserParams.mNonWalkableMode = desc.nonWalkableMode; + mUserParams.mSlopeLimit = desc.slopeLimit; + mUserParams.mContactOffset = desc.contactOffset; + mUserParams.mStepOffset = desc.stepOffset; + mUserParams.mInvisibleWallHeight = desc.invisibleWallHeight; + mUserParams.mMaxJumpHeight = desc.maxJumpHeight; + mUserParams.mHandleSlope = desc.slopeLimit!=0.0f; + + mReportCallback = desc.reportCallback; + mBehaviorCallback = desc.behaviorCallback; + mUserData = desc.userData; + + mKineActor = NULL; + mPosition = desc.position; + mProxyDensity = desc.density; + mProxyScaleCoeff = desc.scaleCoeff; + + mCctModule.mVolumeGrowth = desc.volumeGrowth; + + mRegisterDeletionListener = desc.registerDeletionListener; + + mDeltaXP = PxVec3(0); + mOverlapRecover = PxVec3(0); + + mUserParams.mUpDirection = PxVec3(0.0f); + setUpDirectionInternal(desc.upDirection); +} + +Controller::~Controller() +{ + if(mScene) + { + if(mKineActor) + mKineActor->release(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::onRelease(const PxBase& observed) +{ + mCctModule.onRelease(observed); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::onOriginShift(const PxVec3& shift) +{ + mPosition -= shift; + + if(mManager && mManager->mLockingEnabled) + mWriteLock.lock(); + + mCctModule.onOriginShift(shift); + + if(mManager && mManager->mLockingEnabled) + mWriteLock.unlock(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::setUpDirectionInternal(const PxVec3& up) +{ + PX_CHECK_MSG(up.isNormalized(), "CCT: up direction must be normalized"); + + if(mUserParams.mUpDirection==up) + return; + + const PxQuat q = PxShortestRotation(PxVec3(1.0f, 0.0f, 0.0f), up); + + mUserParams.mQuatFromUp = q; + mUserParams.mUpDirection = up; + + // Update kinematic actor + /*if(mKineActor) + { + PxTransform pose = mKineActor->getGlobalPose(); + pose.q = q; + mKineActor->setGlobalPose(pose); + }*/ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::releaseInternal() +{ + mManager->releaseController(*getPxController()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::getInternalState(PxControllerState& state) const +{ + if(mManager->mLockingEnabled) + mWriteLock.lock(); + + state.deltaXP = mDeltaXP; + state.touchedShape = const_cast(mCctModule.mTouchedShape.get()); + state.touchedActor = const_cast(mCctModule.mTouchedActor.get()); + state.touchedObstacleHandle = mCctModule.mTouchedObstacleHandle; + state.standOnAnotherCCT = (mCctModule.mFlags & STF_TOUCH_OTHER_CCT)!=0; + state.standOnObstacle = (mCctModule.mFlags & STF_TOUCH_OBSTACLE)!=0; + state.isMovingUp = (mCctModule.mFlags & STF_IS_MOVING_UP)!=0; + state.collisionFlags = mCollisionFlags; + + if(mManager->mLockingEnabled) + mWriteLock.unlock(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::getInternalStats(PxControllerStats& stats) const +{ + stats.nbFullUpdates = mCctModule.mNbFullUpdates; + stats.nbPartialUpdates = mCctModule.mNbPartialUpdates; + stats.nbIterations = mCctModule.mNbIterations; + stats.nbTessellation = mCctModule.mNbTessellation; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Controller::setPos(const PxExtendedVec3& pos) +{ + mPosition = pos; + + // Update kinematic actor + if(mKineActor) + { + PxTransform targetPose = mKineActor->getGlobalPose(); + targetPose.p = toVec3(mPosition); // LOSS OF ACCURACY + targetPose.q = mUserParams.mQuatFromUp; + mKineActor->setKinematicTarget(targetPose); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Controller::createProxyActor(PxPhysics& sdk, const PxGeometry& geometry, const PxMaterial& material) +{ + // PT: we don't disable raycasting or CD because: + // - raycasting is needed for visibility queries (the SDK otherwise doesn't know about the CCTS) + // - collision is needed because the only reason we create actors there is to handle collisions with dynamic shapes + // So it's actually wrong to disable any of those. + + PxTransform globalPose; + globalPose.p = toVec3(mPosition); // LOSS OF ACCURACY + globalPose.q = mUserParams.mQuatFromUp; + + mKineActor = sdk.createRigidDynamic(globalPose); + if(!mKineActor) + return false; + + PxShape* shape = sdk.createShape(geometry, material, true); + mKineActor->attachShape(*shape); + shape->release(); + mKineActor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true); + + PxRigidBodyExt::updateMassAndInertia(*mKineActor, mProxyDensity); + mScene->addActor(*mKineActor); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxShape* Controller::getKineShape() const +{ + // PT: TODO: cache this and avoid the virtual call + PxShape* shape = NULL; + PxU32 nb = mKineActor->getShapes(&shape, 1); + PX_ASSERT(nb==1); + PX_UNUSED(nb); + return shape; +} diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h new file mode 100644 index 000000000..ab6a4c539 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_CONTROLLER +#define CCT_CONTROLLER + +/* Exclude from documentation */ +/** \cond */ + +#include "CctCharacterController.h" +#include "PsUserAllocated.h" +#include "PsMutex.h" + +namespace physx +{ + +class PxPhysics; +class PxScene; +class PxRigidDynamic; +class PxGeometry; +class PxMaterial; + +namespace Cct +{ + class CharacterControllerManager; + + class Controller : public Ps::UserAllocated + { + PX_NOCOPY(Controller) + public: + Controller(const PxControllerDesc& desc, PxScene* scene); + virtual ~Controller(); + + void releaseInternal(); + void getInternalState(PxControllerState& state) const; + void getInternalStats(PxControllerStats& stats) const; + + virtual PxF32 getHalfHeightInternal() const = 0; + virtual bool getWorldBox(PxExtendedBounds3& box) const = 0; + virtual PxController* getPxController() = 0; + + void onOriginShift(const PxVec3& shift); + + void onRelease(const PxBase& observed); + + void setCctManager(CharacterControllerManager* cm) + { + mManager = cm; + mCctModule.setCctManager(cm); + } + + PX_FORCE_INLINE CharacterControllerManager* getCctManager() { return mManager; } + PX_FORCE_INLINE PxU64 getContextId() const { return PxU64(reinterpret_cast(mScene)); } + + PxControllerShapeType::Enum mType; + // User params + CCTParams mUserParams; + PxUserControllerHitReport* mReportCallback; + PxControllerBehaviorCallback* mBehaviorCallback; + void* mUserData; + // Internal data + SweepTest mCctModule; // Internal CCT object. Optim test for Ubi. + PxRigidDynamic* mKineActor; // Associated kinematic actor + PxExtendedVec3 mPosition; // Current position + PxVec3 mDeltaXP; + PxVec3 mOverlapRecover; + PxScene* mScene; // Handy scene owner + PxU32 mPreviousSceneTimestamp; + PxF64 mGlobalTime; + PxF64 mPreviousGlobalTime; + PxF32 mProxyDensity; // Density for proxy actor + PxF32 mProxyScaleCoeff; // Scale coeff for proxy actor + PxControllerCollisionFlags mCollisionFlags; // Last known collision flags (PxControllerCollisionFlag) + bool mCachedStandingOnMoving; + bool mRegisterDeletionListener; + mutable Ps::Mutex mWriteLock; // Lock used for guarding touched pointers and cache data from overwriting + // during onRelease call. + protected: + // Internal methods + void setUpDirectionInternal(const PxVec3& up); + PxShape* getKineShape() const; + bool createProxyActor(PxPhysics& sdk, const PxGeometry& geometry, const PxMaterial& material); + bool setPos(const PxExtendedVec3& pos); + void findTouchedObject(const PxControllerFilters& filters, const PxObstacleContext* obstacleContext, const PxVec3& upDirection); + bool rideOnTouchedObject(SweptVolume& volume, const PxVec3& upDirection, PxVec3& disp, const PxObstacleContext* obstacleContext); + PxControllerCollisionFlags move(SweptVolume& volume, const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles, bool constrainedClimbingMode); + bool filterTouchedShape(const PxControllerFilters& filters); + + PX_FORCE_INLINE float computeTimeCoeff() + { + const float elapsedTime = float(mGlobalTime - mPreviousGlobalTime); + mPreviousGlobalTime = mGlobalTime; + return 1.0f / elapsedTime; + } + + CharacterControllerManager* mManager; // Owner manager + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h new file mode 100644 index 000000000..71452aa23 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h @@ -0,0 +1,86 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CHARACTER_INTERNAL_STRUCTS_H +#define PX_CHARACTER_INTERNAL_STRUCTS_H + +#include "CctController.h" + +namespace physx +{ + class PxObstacle; // (*) + +namespace Cct +{ + class ObstacleContext; + + enum UserObjectType + { + USER_OBJECT_CCT = 0, + USER_OBJECT_BOX_OBSTACLE = 1, + USER_OBJECT_CAPSULE_OBSTACLE = 2 + }; + + PX_FORCE_INLINE PxU32 encodeUserObject(PxU32 index, UserObjectType type) + { + PX_ASSERT(index<=0xffff); + PX_ASSERT(PxU32(type)<=0xffff); + return (PxU16(index)<<16)|PxU32(type); + } + + PX_FORCE_INLINE UserObjectType decodeType(PxU32 code) + { + return UserObjectType(code & 0xffff); + } + + PX_FORCE_INLINE PxU32 decodeIndex(PxU32 code) + { + return code>>16; + } + + struct PxInternalCBData_OnHit : InternalCBData_OnHit + { + Controller* controller; + const ObstacleContext* obstacles; + const PxObstacle* touchedObstacle; + ObstacleHandle touchedObstacleHandle; + }; + + struct PxInternalCBData_FindTouchedGeom : InternalCBData_FindTouchedGeom + { + PxScene* scene; + Cm::RenderBuffer* renderBuffer; // Render buffer from controller manager, not the one from the scene + + Ps::HashSet* cctShapeHashSet; + }; +} +} + +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp new file mode 100644 index 000000000..36575cd79 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp @@ -0,0 +1,538 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "CctObstacleContext.h" +#include "CctCharacterControllerManager.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Cct; + +//! Initial list size +#define DEFAULT_HANDLEMANAGER_SIZE 2 + +HandleManager::HandleManager() : mCurrentNbObjects(0), mNbFreeIndices(0) +{ + mMaxNbObjects = DEFAULT_HANDLEMANAGER_SIZE; + mObjects = reinterpret_cast(PX_ALLOC(sizeof(void*)*mMaxNbObjects, "HandleManager")); + mOutToIn = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); + mInToOut = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); + mStamps = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); + PxMemSet(mOutToIn, 0xff, mMaxNbObjects*sizeof(PxU16)); + PxMemSet(mInToOut, 0xff, mMaxNbObjects*sizeof(PxU16)); + PxMemZero(mStamps, mMaxNbObjects*sizeof(PxU16)); +} + +HandleManager::~HandleManager() +{ + SetupLists(); +} + +bool HandleManager::SetupLists(void** objects, PxU16* oti, PxU16* ito, PxU16* stamps) +{ + // Release old data + PX_FREE_AND_RESET(mStamps); + PX_FREE_AND_RESET(mInToOut); + PX_FREE_AND_RESET(mOutToIn); + PX_FREE_AND_RESET(mObjects); + // Assign new data + mObjects = objects; + mOutToIn = oti; + mInToOut = ito; + mStamps = stamps; + return true; +} + +Handle HandleManager::Add(void* object) +{ + // Are there any free indices I should recycle? + if(mNbFreeIndices) + { + const PxU16 FreeIndex = mInToOut[mCurrentNbObjects];// Get the recycled virtual index + mObjects[mCurrentNbObjects] = object; // The physical location is always at the end of the list (it never has holes). + mOutToIn[FreeIndex] = Ps::to16(mCurrentNbObjects++); // Update virtual-to-physical remapping table. + mNbFreeIndices--; + return Handle((mStamps[FreeIndex]<<16)|FreeIndex); // Return virtual index (handle) to the client app + } + else + { + PX_ASSERT_WITH_MESSAGE(mCurrentNbObjects<0xffff ,"Internal error - 64K objects in HandleManager!"); + + // Is the array large enough for another entry? + if(mCurrentNbObjects==mMaxNbObjects) + { + // Nope! Resize all arrays (could be avoided with linked lists... one day) + mMaxNbObjects<<=1; // The more you eat, the more you're given + if(mMaxNbObjects>0xffff) mMaxNbObjects = 0xffff; // Clamp to 64K + void** NewList = reinterpret_cast(PX_ALLOC(sizeof(void*)*mMaxNbObjects, "HandleManager")); // New physical list + PxU16* NewOTI = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New remapping table + PxU16* NewITO = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New remapping table + PxU16* NewStamps = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New stamps + PxMemCopy(NewList, mObjects, mCurrentNbObjects*sizeof(void*)); // Copy old data + PxMemCopy(NewOTI, mOutToIn, mCurrentNbObjects*sizeof(PxU16)); // Copy old data + PxMemCopy(NewITO, mInToOut, mCurrentNbObjects*sizeof(PxU16)); // Copy old data + PxMemCopy(NewStamps, mStamps, mCurrentNbObjects*sizeof(PxU16)); // Copy old data + PxMemSet(NewOTI+mCurrentNbObjects, 0xff, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16)); + PxMemSet(NewITO+mCurrentNbObjects, 0xff, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16)); + PxMemZero(NewStamps+mCurrentNbObjects, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16)); + SetupLists(NewList, NewOTI, NewITO, NewStamps); + } + + mObjects[mCurrentNbObjects] = object; // Store object at mCurrentNbObjects = physical index = virtual index + mOutToIn[mCurrentNbObjects] = Ps::to16(mCurrentNbObjects); // Update virtual-to-physical remapping table. + mInToOut[mCurrentNbObjects] = Ps::to16(mCurrentNbObjects); // Update physical-to-virtual remapping table. + PxU32 tmp = mCurrentNbObjects++; + return (mStamps[tmp]<<16)|tmp; // Return virtual index (handle) to the client app + } +} + +void HandleManager::Remove(Handle handle) +{ + const PxU16 VirtualIndex = PxU16(handle); + if(VirtualIndex>=mMaxNbObjects) return; // Invalid handle + const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get the physical index + if(PhysicalIndex==0xffff) return; // Value has already been deleted + if(PhysicalIndex>=mMaxNbObjects) return; // Invalid index + + // There must be at least one valid entry. + if(mCurrentNbObjects) + { + if(mStamps[VirtualIndex]!=handle>>16) return; // Stamp mismatch => index has been recycled + + // Update list so that there's no hole + mObjects[PhysicalIndex] = mObjects[--mCurrentNbObjects];// Move the real object so that the array has no holes. + mOutToIn[mInToOut[mCurrentNbObjects]] = PhysicalIndex; // Update virtual-to-physical remapping table. + mInToOut[PhysicalIndex] = mInToOut[mCurrentNbObjects]; // Update physical-to-virtual remapping table. + // Keep track of the recyclable virtual index + mInToOut[mCurrentNbObjects] = VirtualIndex; // Store the free virtual index/handle at the end of mInToOut + mOutToIn[VirtualIndex] = 0xffff; // Invalidate the entry + mNbFreeIndices++; // One more free index + mStamps[VirtualIndex]++; // Update stamp + } +} + +void* HandleManager::GetObject(Handle handle) const +{ + const PxU16 VirtualIndex = PxU16(handle); + if(VirtualIndex>=mMaxNbObjects) return NULL; // Invalid handle + const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get physical index + if(PhysicalIndex==0xffff) return NULL; // Object has been deleted + if(PhysicalIndex>=mMaxNbObjects) return NULL; // Index is invalid + if(mStamps[VirtualIndex]!=handle>>16) return NULL; // Index has been recycled + return mObjects[PhysicalIndex]; // Returns stored pointer +} + +bool HandleManager::UpdateObject(Handle handle, void* object) +{ + const PxU16 VirtualIndex = PxU16(handle); + if(VirtualIndex>=mMaxNbObjects) return false; // Invalid handle + const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get physical index + if(PhysicalIndex==0xffff) return false; // Object has been deleted + if(PhysicalIndex>=mMaxNbObjects) return false; // Index is invalid + if(mStamps[VirtualIndex]!=handle>>16) return false; // Index has been recycled + mObjects[PhysicalIndex] = object; // Updates stored pointer + return true; +} + + +// PT: please do not expose these functions outside of this class, +// as we want to retain the ability to easily modify the handle format if necessary. + +#define NEW_ENCODING +#ifdef NEW_ENCODING +static PX_FORCE_INLINE void* encodeInternalHandle(PxU32 index, PxGeometryType::Enum type) +{ + PX_ASSERT(index<=0xffff); + PX_ASSERT(PxU32(type)<=0xffff); + // PT: we do type+1 to ban internal handles with a 0 value, since it's reserved for NULL pointers + return reinterpret_cast((size_t(index)<<16)|(size_t(type)+1)); +} + +static PX_FORCE_INLINE PxGeometryType::Enum decodeInternalType(void* handle) +{ + return PxGeometryType::Enum((PxU32(reinterpret_cast(handle)) & 0xffff)-1); +} + +static PX_FORCE_INLINE PxU32 decodeInternalIndex(void* handle) +{ + return (PxU32(size_t(handle)))>>16; +} +#else +static PX_FORCE_INLINE ObstacleHandle encodeHandle(PxU32 index, PxGeometryType::Enum type) +{ + PX_ASSERT(index<=0xffff); + PX_ASSERT(type<=0xffff); + return (PxU16(index)<<16)|PxU32(type); +} + +static PX_FORCE_INLINE PxGeometryType::Enum decodeType(ObstacleHandle handle) +{ + return PxGeometryType::Enum(handle & 0xffff); +} + +static PX_FORCE_INLINE PxU32 decodeIndex(ObstacleHandle handle) +{ + return handle>>16; +} +#endif + +ObstacleContext::ObstacleContext(CharacterControllerManager& cctMan) + : mCCTManager(cctMan) +{ +} + +ObstacleContext::~ObstacleContext() +{ +} + +void ObstacleContext::release() +{ + mCCTManager.releaseObstacleContext(*this); +} + +PxControllerManager& ObstacleContext::getControllerManager() const +{ + return mCCTManager; +} + +ObstacleHandle ObstacleContext::addObstacle(const PxObstacle& obstacle) +{ + const PxGeometryType::Enum type = obstacle.getType(); + if(type==PxGeometryType::eBOX) + { + const PxU32 index = mBoxObstacles.size(); +#ifdef NEW_ENCODING + const ObstacleHandle handle = mHandleManager.Add(encodeInternalHandle(index, type)); +#else + const ObstacleHandle handle = encodeHandle(index, type); +#endif + mBoxObstacles.pushBack(InternalBoxObstacle(handle, static_cast(obstacle))); + mCCTManager.onObstacleAdded(handle, this); + return handle; + } + else if(type==PxGeometryType::eCAPSULE) + { + const PxU32 index = mCapsuleObstacles.size(); +#ifdef NEW_ENCODING + const ObstacleHandle handle = mHandleManager.Add(encodeInternalHandle(index, type)); +#else + const ObstacleHandle handle = encodeHandle(index, type); +#endif + mCapsuleObstacles.pushBack(InternalCapsuleObstacle(handle, static_cast(obstacle))); + mCCTManager.onObstacleAdded(handle, this); + return handle; + } + else return INVALID_OBSTACLE_HANDLE; +} + +#ifdef NEW_ENCODING +template +static PX_FORCE_INLINE void remove(HandleManager& manager, void* object, ObstacleHandle handle, PxU32 index, PxU32 size, const Ps::Array& obstacles) +{ + manager.Remove(handle); + if(index!=size-1) + { + bool status = manager.UpdateObject(obstacles[size-1].mHandle, object); + PX_ASSERT(status); + PX_UNUSED(status); + } +} +#endif + +bool ObstacleContext::removeObstacle(ObstacleHandle handle) +{ +#ifdef NEW_ENCODING + void* object = mHandleManager.GetObject(handle); + if(!object) + return false; + const PxGeometryType::Enum type = decodeInternalType(object); + const PxU32 index = decodeInternalIndex(object); +#else + const PxGeometryType::Enum type = decodeType(handle); + const PxU32 index = decodeIndex(handle); +#endif + + if(type==PxGeometryType::eBOX) + { + const PxU32 size = mBoxObstacles.size(); + PX_ASSERT(index=size) + return false; + +#ifdef NEW_ENCODING + remove(mHandleManager, object, handle, index, size, mBoxObstacles); +#endif + mBoxObstacles.replaceWithLast(index); +#ifdef NEW_ENCODING + mCCTManager.onObstacleRemoved(handle); +#else + mCCTManager.onObstacleRemoved(handle, encodeHandle(size-1, type)); +#endif + return true; + } + else if(type==PxGeometryType::eCAPSULE) + { + const PxU32 size = mCapsuleObstacles.size(); + PX_ASSERT(index=size) + return false; + +#ifdef NEW_ENCODING + remove(mHandleManager, object, handle, index, size, mCapsuleObstacles); +#endif + + mCapsuleObstacles.replaceWithLast(index); +#ifdef NEW_ENCODING + mCCTManager.onObstacleRemoved(handle); +#else + mCCTManager.onObstacleRemoved(handle, encodeHandle(size-1, type)); +#endif + return true; + } + else return false; +} + +bool ObstacleContext::updateObstacle(ObstacleHandle handle, const PxObstacle& obstacle) +{ +#ifdef NEW_ENCODING + void* object = mHandleManager.GetObject(handle); + if(!object) + return false; + const PxGeometryType::Enum type = decodeInternalType(object); + PX_ASSERT(type==obstacle.getType()); + if(type!=obstacle.getType()) + return false; + const PxU32 index = decodeInternalIndex(object); +#else + const PxGeometryType::Enum type = decodeType(handle); + PX_ASSERT(type==obstacle.getType()); + if(type!=obstacle.getType()) + return false; + const PxU32 index = decodeIndex(handle); +#endif + + if(type==PxGeometryType::eBOX) + { + const PxU32 size = mBoxObstacles.size(); + PX_ASSERT(index=size) + return false; + + mBoxObstacles[index].mData = static_cast(obstacle); + mCCTManager.onObstacleUpdated(handle,this); + return true; + } + else if(type==PxGeometryType::eCAPSULE) + { + const PxU32 size = mCapsuleObstacles.size(); + PX_ASSERT(index=size) + return false; + + mCapsuleObstacles[index].mData = static_cast(obstacle); + mCCTManager.onObstacleUpdated(handle,this); + return true; + } + else return false; +} + +PxU32 ObstacleContext::getNbObstacles() const +{ + return mBoxObstacles.size() + mCapsuleObstacles.size(); +} + +const PxObstacle* ObstacleContext::getObstacle(PxU32 i) const +{ + const PxU32 nbBoxes = mBoxObstacles.size(); + if(i=size) + return NULL; + PX_ASSERT(mBoxObstacles[index].mHandle==handle); + return &mBoxObstacles[index].mData; + } + else if(type==PxGeometryType::eCAPSULE) + { + const PxU32 size = mCapsuleObstacles.size(); + if(index>=size) + return NULL; + PX_ASSERT(mCapsuleObstacles[index].mHandle==handle); + return &mCapsuleObstacles[index].mData; + } + else return NULL; +} + +#include "GuRaycastTests.h" +#include "PxBoxGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PsMathUtils.h" +using namespace Gu; +const PxObstacle* ObstacleContext::raycastSingle(PxRaycastHit& hit, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, ObstacleHandle& obstacleHandle) const +{ + PxRaycastHit localHit; + PxF32 t = FLT_MAX; + const PxObstacle* touchedObstacle = NULL; + + const PxHitFlags hitFlags = PxHitFlags(0); + + { + const RaycastFunc raycastFunc = Gu::getRaycastFuncTable()[PxGeometryType::eBOX]; + PX_ASSERT(raycastFunc); + + const PxU32 nbExtraBoxes = mBoxObstacles.size(); + for(PxU32 i=0;i mBoxObstacles; + + struct InternalCapsuleObstacle + { + InternalCapsuleObstacle(ObstacleHandle handle, const PxCapsuleObstacle& data) : mHandle(handle), mData(data) {} + + ObstacleHandle mHandle; + PxCapsuleObstacle mData; + }; + Ps::Array mCapsuleObstacles; + + private: + ObstacleContext& operator=(const ObstacleContext&); + HandleManager mHandleManager; + CharacterControllerManager& mCCTManager; + }; + + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp new file mode 100644 index 000000000..d6731e02a --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp @@ -0,0 +1,50 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CctSweptBox.h" +#include "CctCharacterController.h" + +using namespace physx; +using namespace Cct; + +SweptBox::SweptBox() +{ + mType = SweptVolumeType::eBOX; +} + +SweptBox::~SweptBox() +{ +} + +void SweptBox::computeTemporalBox(const SweepTest& test, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const +{ + const float radius = PxMax(mExtents.y, mExtents.z); + const float height = 2.0f * mExtents.x; + Cct::computeTemporalBox(box, radius, height, test.mUserParams.mContactOffset, test.mUserParams.mMaxJumpHeight, test.mUserParams.mUpDirection, center, direction); +} diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h new file mode 100644 index 000000000..48ab0d954 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h @@ -0,0 +1,55 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_SWEPT_BOX +#define CCT_SWEPT_BOX + +#include "CctSweptVolume.h" + +namespace physx +{ +namespace Cct +{ + + class SweptBox : public SweptVolume + { + public: + SweptBox(); + virtual ~SweptBox(); + + virtual void computeTemporalBox(const SweepTest&, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const; + + PxVec3 mExtents; + }; + +} // namespace Cct + +} + +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp new file mode 100644 index 000000000..f4d735d84 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp @@ -0,0 +1,48 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CctSweptCapsule.h" +#include "CctCharacterController.h" + +using namespace physx; +using namespace Cct; + +SweptCapsule::SweptCapsule() +{ + mType = SweptVolumeType::eCAPSULE; +} + +SweptCapsule::~SweptCapsule() +{ +} + +void SweptCapsule::computeTemporalBox(const SweepTest& test, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const +{ + Cct::computeTemporalBox(box, mRadius, mHeight, test.mUserParams.mContactOffset, test.mUserParams.mMaxJumpHeight, test.mUserParams.mUpDirection, center, direction); +} diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h new file mode 100644 index 000000000..8c9a2d8a0 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h @@ -0,0 +1,56 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_SWEPT_CAPSULE +#define CCT_SWEPT_CAPSULE + +#include "CctSweptVolume.h" + +namespace physx +{ +namespace Cct +{ + + class SweptCapsule : public SweptVolume + { + public: + SweptCapsule(); + virtual ~SweptCapsule(); + + virtual void computeTemporalBox(const SweepTest&, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const; + + PxF32 mRadius; + PxF32 mHeight; + }; + +} // namespace Cct + +} + +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp new file mode 100644 index 000000000..1d846b460 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CctSweptVolume.h" + +using namespace physx; +using namespace Cct; + +SweptVolume::SweptVolume() +{ + mType = SweptVolumeType::eLAST; +} + +SweptVolume::~SweptVolume() +{ +} + +void Cct::computeTemporalBox(PxExtendedBounds3& _box, float radius, float height, float contactOffset, float maxJumpHeight, const PxVec3& upDirection, const PxExtendedVec3& center, const PxVec3& direction) +{ + const float r = radius + contactOffset; + PxVec3 extents(r); + const float halfHeight = height*0.5f; + extents.x += fabsf(upDirection.x)*halfHeight; + extents.y += fabsf(upDirection.y)*halfHeight; + extents.z += fabsf(upDirection.z)*halfHeight; + + PxExtendedBounds3 box; + setCenterExtents(box, center, extents); + + { + PxExtendedBounds3 destBox; + PxExtendedVec3 tmp = center; + tmp += direction; + setCenterExtents(destBox, tmp, extents); + add(box, destBox); + } + + if(maxJumpHeight!=0.0f) + { + PxExtendedBounds3 destBox; + PxExtendedVec3 tmp = center; + tmp -= upDirection * maxJumpHeight; + setCenterExtents(destBox, tmp, extents); + add(box, destBox); + } + + _box = box; +} diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h new file mode 100644 index 000000000..a29435b04 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_SWEPT_VOLUME +#define CCT_SWEPT_VOLUME + +#include "CctUtils.h" + +namespace physx +{ +namespace Cct +{ + + struct SweptVolumeType + { + enum Enum + { + eBOX, + eCAPSULE, + + eLAST + }; + }; + + class SweepTest; + + class SweptVolume + { + public: + SweptVolume(); + virtual ~SweptVolume(); + + virtual void computeTemporalBox(const SweepTest& test, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const = 0; + + PX_FORCE_INLINE SweptVolumeType::Enum getType() const { return mType; } + + PxExtendedVec3 mCenter; + PxF32 mHalfHeight; // UBI + protected: + SweptVolumeType::Enum mType; + }; + + void computeTemporalBox(PxExtendedBounds3& _box, float radius, float height, float contactOffset, float maxJumpHeight, const PxVec3& upDirection, const PxExtendedVec3& center, const PxVec3& direction); + +} // namespace Cct + +} + +#endif + diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h new file mode 100644 index 000000000..8ac1e7227 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h @@ -0,0 +1,214 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef CCT_UTILS +#define CCT_UTILS + +#include "PxExtended.h" +#include "PxShapeExt.h" + +namespace physx +{ + +PX_FORCE_INLINE bool testSlope(const PxVec3& normal, const PxVec3& upDirection, PxF32 slopeLimit) +{ + const float dp = normal.dot(upDirection); + return dp>=0.0f && dp minimum.x) return false; + if(box.minimum.y > minimum.y) return false; + if(box.minimum.z > minimum.z) return false; + if(box.maximum.x < maximum.x) return false; + if(box.maximum.y < maximum.y) return false; + if(box.maximum.z < maximum.z) return false; + return true; + } + PxExtendedVec3 minimum, maximum; + }; + + PX_INLINE void getCenter(const PxExtendedBounds3& b, PxExtendedVec3& center) + { + center = b.minimum + b.maximum; + center *= 0.5; + } + + PX_INLINE void getExtents(const PxExtendedBounds3& b, PxVec3& extents) + { + extents = b.maximum - b.minimum; + extents *= 0.5f; + } + + PX_INLINE void setCenterExtents(PxExtendedBounds3& b, const PxExtendedVec3& c, const PxVec3& e) + { + b.minimum = c; b.minimum -= e; + b.maximum = c; b.maximum += e; + } + + PX_INLINE void add(PxExtendedBounds3& b, const PxExtendedBounds3& b2) + { + // - if we're empty, minimum = MAX,MAX,MAX => minimum will be b2 in all cases => it will copy b2, ok + // - if b2 is empty, the opposite happens => keep us unchanged => ok + // => same behaviour as before, automatically + b.minimum.minimum(b2.minimum); + b.maximum.maximum(b2.maximum); + } +#else + + #include "foundation/PxBounds3.h" + #include "GuBox.h" + #include "GuCapsule.h" + #include "GuPlane.h" + + typedef Gu::Box PxExtendedBox; + typedef Gu::Sphere PxExtendedSphere; + typedef Gu::Segment PxExtendedSegment; + typedef Gu::Capsule PxExtendedCapsule; + typedef PxBounds3 PxExtendedBounds3; + + PX_INLINE PxExtended distance(const PxVec3& v2, const PxVec3& v) + { + const PxExtended dx = v2.x - v.x; + const PxExtended dy = v2.y - v.y; + const PxExtended dz = v2.z - v.z; + return PxSqrt(dx * dx + dy * dy + dz * dz); + } + + PX_INLINE void getCenter(const PxBounds3& b, PxVec3& center) + { + center = b.minimum + b.maximum; + center *= 0.5; + } + + PX_INLINE void getExtents(const PxBounds3& b, PxVec3& extents) + { + extents = b.maximum - b.minimum; + extents *= 0.5f; + } + + PX_INLINE void setCenterExtents(PxBounds3& b, const PxVec3& center, const PxVec3& extents) + { + b.minimum = center - extents; + b.maximum = center + extents; + } + + PX_INLINE void add(PxBounds3& b, const PxBounds3& b2) + { + // - if we're empty, minimum = MAX,MAX,MAX => minimum will be b2 in all cases => it will copy b2, ok + // - if b2 is empty, the opposite happens => keep us unchanged => ok + // => same behaviour as before, automatically + b.minimum.minimum(b2.minimum); + b.maximum.maximum(b2.maximum); + } +#endif + +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp b/src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp new file mode 100644 index 000000000..4db1c96da --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp @@ -0,0 +1,712 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "EdgeList.h" +#include "Adjacencies.h" +#include "CmRadixSortBuffered.h" +#include "GuSerialize.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Flips the winding. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AdjTriangle::Flip() +{ +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + // Call the Triangle method + IndexedTriangle::Flip(); +#endif + + // Flip links. We flipped vertex references 1 & 2, i.e. links 0 & 1. + physx::shdfnd::swap(mATri[0], mATri[1]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the number of boundary edges in a triangle. + * \return the number of boundary edges. (0 => 3) + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PxU32 AdjTriangle::ComputeNbBoundaryEdges() const +{ + // Look for boundary edges + PxU32 Nb = 0; + if(IS_BOUNDARY(mATri[0])) Nb++; + if(IS_BOUNDARY(mATri[1])) Nb++; + if(IS_BOUNDARY(mATri[2])) Nb++; + return Nb; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the number of valid neighbors. + * \return the number of neighbors. (0 => 3) + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PxU32 AdjTriangle::ComputeNbNeighbors() const +{ + PxU32 Nb = 0; + if(!IS_BOUNDARY(mATri[0])) Nb++; + if(!IS_BOUNDARY(mATri[1])) Nb++; + if(!IS_BOUNDARY(mATri[2])) Nb++; + return Nb; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the triangle has a particular neighbor or not. + * \param tref [in] the triangle reference to look for + * \param index [out] the corresponding index in the triangle (NULL if not needed) + * \return true if the triangle has the given neighbor + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AdjTriangle::HasNeighbor(PxU32 tref, PxU32* index) const +{ + // ### could be optimized + if(!IS_BOUNDARY(mATri[0]) && MAKE_ADJ_TRI(mATri[0])==tref) { if(index) *index = 0; return true; } + if(!IS_BOUNDARY(mATri[1]) && MAKE_ADJ_TRI(mATri[1])==tref) { if(index) *index = 1; return true; } + if(!IS_BOUNDARY(mATri[2]) && MAKE_ADJ_TRI(mATri[2])==tref) { if(index) *index = 2; return true; } + return false; +} + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Adjacencies::Adjacencies() : mNbFaces(0), mFaces(NULL) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Adjacencies::~Adjacencies() +{ + PX_DELETE_ARRAY(mFaces); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the number of boundary edges. + * \return the number of boundary edges. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PxU32 Adjacencies::ComputeNbBoundaryEdges() const +{ + // Checking + if(!mFaces) return 0; + + // Look for boundary edges + PxU32 Nb = 0; + for(PxU32 i=0;iComputeNbBoundaryEdges(); + } + return Nb; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the boundary vertices. A boundary vertex is defined as a vertex shared by at least one boundary edge. + * \param nb_verts [in] the number of vertices + * \param bound_status [out] a user-provided array of bool + * \return true if success. The user-array is filled with true or false (boundary vertex / not boundary vertex) + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY +bool Adjacencies::GetBoundaryVertices(PxU32 nb_verts, bool* bound_status) const +#else +bool Adjacencies::GetBoundaryVertices(PxU32 nb_verts, bool* bound_status, const Gu::TriangleT* faces) const +#endif +{ + // We need the adjacencies + if(!mFaces || !bound_status || !nb_verts) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacencies::GetBoundaryVertices: NULL parameter!"); + return false; + } + +#ifndef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + if(!faces) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacencies::GetBoundaryVertices: NULL parameter!"); + return false; + } +#endif + + // Init + PxMemZero(bound_status, nb_verts*sizeof(bool)); + + // Loop through faces + for(PxU32 i=0;imATri[0])) + { + // Two boundary vertices: 0 - 1 +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + PxU32 VRef0 = CurTri->v[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = CurTri->v[1]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#else + PxU32 VRef0 = faces[i].v[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = faces[i].v[1]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#endif + } + if(IS_BOUNDARY(CurTri->mATri[1])) + { + // Two boundary vertices: 0 - 2 +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + PxU32 VRef0 = CurTri->v[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = CurTri->v[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#else + PxU32 VRef0 = faces[i].v[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = faces[i].v[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#endif + } + if(IS_BOUNDARY(CurTri->mATri[2])) + { + // Two boundary vertices: 1 - 2 +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + PxU32 VRef0 = CurTri->v[1]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = CurTri->v[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#else + PxU32 VRef0 = faces[i].v[1]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = faces[i].v[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#endif + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Assigns a new edge code to the counterpart link of a given link. + * \param link [in] the link to modify - shouldn't be a boundary link + * \param edge_nb [in] the new edge number + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Adjacencies::AssignNewEdgeCode(PxU32 link, PxU8 edge_nb) +{ + if(!IS_BOUNDARY(link)) + { + PxU32 Id = MAKE_ADJ_TRI(link); // Triangle ID + PxU32 Edge = GET_EDGE_NB(link); // Counterpart edge ID + AdjTriangle* Tri = &mFaces[Id]; // Adjacent triangle + + // Get link whose edge code is invalid + PxU32 AdjLink = Tri->mATri[Edge]; // Link to ourself (i.e. to 'link') + SET_EDGE_NB(AdjLink, edge_nb); // Assign new edge code + Tri->mATri[Edge] = AdjLink; // Put link back + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Modifies the existing database so that reference 'vref' of triangle 'curtri' becomes the last one. + * Provided reference must already exist in provided triangle. + * \param cur_tri [in] the triangle + * \param vref [in] the reference + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY +bool Adjacencies::MakeLastRef(AdjTriangle& cur_tri, PxU32 vref) +#else +bool Adjacencies::MakeLastRef(AdjTriangle& cur_tri, PxU32 vref, Gu::TriangleT* cur_topo) +#endif +{ +#ifndef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + // Checkings + if(!cur_topo) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacencies::MakeLastRef: NULL parameter!"); + return false; + } +#endif + // We want pattern (x y vref) + // Edge 0-1 is (x y) + // Edge 0-2 is (x vref) + // Edge 1-2 is (y vref) + + // First thing is to scroll the existing references in order for vref to become the last one. Scrolling assures winding order is conserved. + + // Edge code need fixing as well: + // The two MSB for each link encode the counterpart edge in adjacent triangle. We swap the link positions, but adjacent triangles remain the + // same. In other words, edge codes are still valid for current triangle since counterpart edges have not been swapped. *BUT* edge codes of + // the three possible adjacent triangles *are* now invalid. We need to fix edge codes, but for adjacent triangles... + +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + if(cur_tri.v[0]==vref) +#else + if(cur_topo->v[0]==vref) +#endif + { + // Pattern is (vref x y) + // Edge 0-1 is (vref x) + // Edge 0-2 is (vref y) + // Edge 1-2 is (x y) + + // Catch original data +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + PxU32 Ref0 = cur_tri.v[0]; PxU32 Link01 = cur_tri.mATri[0]; + PxU32 Ref1 = cur_tri.v[1]; PxU32 Link02 = cur_tri.mATri[1]; + PxU32 Ref2 = cur_tri.v[2]; PxU32 Link12 = cur_tri.mATri[2]; + + // Swap + cur_tri.v[0] = Ref1; + cur_tri.v[1] = Ref2; + cur_tri.v[2] = Ref0; +#else + PxU32 Ref0 = cur_topo->v[0]; PxU32 Link01 = cur_tri.mATri[0]; + PxU32 Ref1 = cur_topo->v[1]; PxU32 Link02 = cur_tri.mATri[1]; + PxU32 Ref2 = cur_topo->v[2]; PxU32 Link12 = cur_tri.mATri[2]; + + // Swap + cur_topo->v[0] = Ref1; + cur_topo->v[1] = Ref2; + cur_topo->v[2] = Ref0; +#endif + cur_tri.mATri[0] = Link12; // Edge 0-1 now encodes Ref1-Ref2, i.e. previous Link12 + cur_tri.mATri[1] = Link01; // Edge 0-2 now encodes Ref1-Ref0, i.e. previous Link01 + cur_tri.mATri[2] = Link02; // Edge 1-2 now encodes Ref2-Ref0, i.e. previous Link02 + + // Fix edge codes + AssignNewEdgeCode(Link01, 1); + AssignNewEdgeCode(Link02, 2); + AssignNewEdgeCode(Link12, 0); + + return true; + } +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + else if(cur_tri.v[1]==vref) +#else + else if(cur_topo->v[1]==vref) +#endif + { + // Pattern is (x vref y) + // Edge 0-1 is (x vref) + // Edge 0-2 is (x y) + // Edge 1-2 is (vref y) + + // Catch original data +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + PxU32 Ref0 = cur_tri.v[0]; PxU32 Link01 = cur_tri.mATri[0]; + PxU32 Ref1 = cur_tri.v[1]; PxU32 Link02 = cur_tri.mATri[1]; + PxU32 Ref2 = cur_tri.v[2]; PxU32 Link12 = cur_tri.mATri[2]; + + // Swap + cur_tri.v[0] = Ref2; + cur_tri.v[1] = Ref0; + cur_tri.v[2] = Ref1; +#else + PxU32 Ref0 = cur_topo->v[0]; PxU32 Link01 = cur_tri.mATri[0]; + PxU32 Ref1 = cur_topo->v[1]; PxU32 Link02 = cur_tri.mATri[1]; + PxU32 Ref2 = cur_topo->v[2]; PxU32 Link12 = cur_tri.mATri[2]; + + // Swap + cur_topo->v[0] = Ref2; + cur_topo->v[1] = Ref0; + cur_topo->v[2] = Ref1; +#endif + cur_tri.mATri[0] = Link02; // Edge 0-1 now encodes Ref2-Ref0, i.e. previous Link02 + cur_tri.mATri[1] = Link12; // Edge 0-2 now encodes Ref2-Ref1, i.e. previous Link12 + cur_tri.mATri[2] = Link01; // Edge 1-2 now encodes Ref0-Ref1, i.e. previous Link01 + + // Fix edge codes + AssignNewEdgeCode(Link01, 2); + AssignNewEdgeCode(Link02, 0); + AssignNewEdgeCode(Link12, 1); + + return true; + } +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + else if(cur_tri.v[2]==vref) +#else + else if(cur_topo->v[2]==vref) +#endif + { + // Nothing to do, provided reference already is the last one + return true; + } + + // Here the provided reference doesn't belong to the provided triangle. + return false; +} + +bool Adjacencies::Load(PxInputStream& stream) +{ + // Import header + PxU32 Version; + bool Mismatch; + if(!ReadHeader('A', 'D', 'J', 'A', Version, Mismatch, stream)) + return false; + + // Import adjacencies + mNbFaces = readDword(Mismatch, stream); + mFaces = PX_NEW(AdjTriangle)[mNbFaces]; + stream.read(mFaces, sizeof(AdjTriangle)*mNbFaces); + + return true; +} + +//#ifdef PX_COOKING + + //! An edge class used to compute the adjacency structures. + class AdjEdge : public Gu::EdgeData, public Ps::UserAllocated + { + public: + //! Constructor + PX_INLINE AdjEdge() {} + //! Destructor + PX_INLINE ~AdjEdge() {} + + PxU32 mFaceNb; //!< Owner face + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Adds a new edge to the database. + * \param ref0 [in] vertex reference for the new edge + * \param ref1 [in] vertex reference for the new edge + * \param face [in] owner face + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + static void AddEdge(PxU32 ref0, PxU32 ref1, PxU32 face, PxU32& nb_edges, AdjEdge* edges) + { + // Store edge data + edges[nb_edges].Ref0 = ref0; + edges[nb_edges].Ref1 = ref1; + edges[nb_edges].mFaceNb = face; + nb_edges++; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Adds a new triangle to the database. + * \param ref0 [in] vertex reference for the new triangle + * \param ref1 [in] vertex reference for the new triangle + * \param ref2 [in] vertex reference for the new triangle + * \param id [in] triangle index + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + static void AddTriangle(PxU32 ref0, PxU32 ref1, PxU32 ref2, PxU32 id, AdjTriangle* faces, PxU32& nb_edges, AdjEdge* edges) + { +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + // Store vertex-references + faces[id].v[0] = ref0; + faces[id].v[1] = ref1; + faces[id].v[2] = ref2; +#endif + // Reset links + faces[id].mATri[0] = PX_INVALID_U32; + faces[id].mATri[1] = PX_INVALID_U32; + faces[id].mATri[2] = PX_INVALID_U32; + + // Add edge 01 to database + if(ref0 FirstTri, SecondTri; + if(create.DFaces) + { + FirstTri.v[0] = create.DFaces[first_tri*3+0]; + FirstTri.v[1] = create.DFaces[first_tri*3+1]; + FirstTri.v[2] = create.DFaces[first_tri*3+2]; + SecondTri.v[0] = create.DFaces[second_tri*3+0]; + SecondTri.v[1] = create.DFaces[second_tri*3+1]; + SecondTri.v[2] = create.DFaces[second_tri*3+2]; + } + if(create.WFaces) + { + FirstTri.v[0] = create.WFaces[first_tri*3+0]; + FirstTri.v[1] = create.WFaces[first_tri*3+1]; + FirstTri.v[2] = create.WFaces[first_tri*3+2]; + SecondTri.v[0] = create.WFaces[second_tri*3+0]; + SecondTri.v[1] = create.WFaces[second_tri*3+1]; + SecondTri.v[2] = create.WFaces[second_tri*3+2]; + } + + // Get the edge IDs. 0xff means input references are wrong. + const PxU8 EdgeNb0 = FirstTri.findEdge(ref0, ref1); + const PxU8 EdgeNb1 = SecondTri.findEdge(ref0, ref1); + if(EdgeNb0==0xff || EdgeNb1==0xff) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacencies::UpdateLink: invalid edge reference"); + return false; + } + + // Update links. The two most significant bits contain the counterpart edge's ID. + faces[first_tri].mATri[EdgeNb0] = second_tri |(PxU32(EdgeNb1)<<30); + faces[second_tri].mATri[EdgeNb1] = first_tri |(PxU32(EdgeNb0)<<30); +#endif + return true; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Creates the adjacency structures. + * \return true if success. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + static bool CreateDatabase(AdjTriangle* faces, PxU32 nb_edges, const AdjEdge* edges) +#else + static bool CreateDatabase(AdjTriangle* faces, PxU32 nb_edges, const AdjEdge* edges, const ADJACENCIESCREATE& create) +#endif + { + Cm::RadixSortBuffered Core; + { + // Multiple sorts - this rewritten version uses less ram + // PT: TTP 2994: the mesh has 343000+ edges, so yeah, sure, allocating more than 1mb on the stack causes overflow... + PxU32* VRefs = PX_NEW_TEMP(PxU32)[nb_edges]; + + // Sort according to mRef0, then mRef1 + PxU32 i; + for(i=0;i edge is a boundary edge: it belongs to a single triangle. + // Hence there's no need to update a link to an adjacent triangle. +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + if(!UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces)) return false; +#else + if(!UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces, create)) return false; +#endif + } + // Reset for next edge + Count = 0; + TmpBuffer[Count++] = Face; + LastRef0 = Ref0; + LastRef1 = Ref1; + } + } + bool Status = true; +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + if(Count==2) Status = UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces); +#else + if(Count==2) Status = UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces, create); +#endif + return Status; + } + +AdjacenciesBuilder::AdjacenciesBuilder() +{ +} + +AdjacenciesBuilder::~AdjacenciesBuilder() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes the component. + * \param create [in] the creation structure + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AdjacenciesBuilder::Init(const ADJACENCIESCREATE& create) +{ + // Checkings + if(!create.NbFaces) return false; + + // Get some bytes + mNbFaces = create.NbFaces; + mFaces = PX_NEW(AdjTriangle)[mNbFaces]; + + AdjEdge* Edges = PX_NEW_TEMP(AdjEdge)[mNbFaces*3]; + PxU32 NbEdges=0; + + // Feed me with triangles..... + for(PxU32 i=0;i>30) //!< Transforms a link into a counterpart edge ID. +// #define IS_BOUNDARY(x) (x==PX_INVALID_U32) //!< Returns true for boundary edges. + #define IS_BOUNDARY(x) ((x & ADJ_TRIREF_MASK)==ADJ_TRIREF_MASK) //!< Returns true for boundary edges. + + // Forward declarations + class Adjacencies; + + enum SharedEdgeIndex + { + EDGE01 = 0, + EDGE02 = 1, + EDGE12 = 2 + }; + +/* PX_INLINE void GetEdgeIndices(SharedEdgeIndex edge_index, PxU32& id0, PxU32& id1) + { + if(edge_index==0) + { + id0 = 0; + id1 = 1; + } + else if(edge_index==1) + { + id0 = 0; + id1 = 2; + } + else if(edge_index==2) + { + id0 = 1; + id1 = 2; + } + }*/ + + //! Sets a new edge code + #define SET_EDGE_NB(link, code) \ + link&=ADJ_TRIREF_MASK; \ + link|=code<<30; \ + + //! A triangle class used to compute the adjacency structures. + class AdjTriangle +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + : public IndexedTriangle +#else + : public Ps::UserAllocated +#endif + { + public: + //! Constructor + PX_INLINE AdjTriangle() {} + //! Destructor + PX_INLINE ~AdjTriangle() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the number of boundary edges in a triangle. + * \return the number of boundary edges. (0 => 3) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PxU32 ComputeNbBoundaryEdges() const; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the number of valid neighbors. + * \return the number of neighbors. (0 => 3) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PxU32 ComputeNbNeighbors() const; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the triangle has a particular neighbor or not. + * \param tref [in] the triangle reference to look for + * \param index [out] the corresponding index in the triangle (NULL if not needed) + * \return true if the triangle has the given neighbor + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool HasNeighbor(PxU32 tref, PxU32* index=NULL) const; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Flips the winding. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Flip(); + + // Data access + PX_INLINE PxU32 GetLink(SharedEdgeIndex edge_index) const { return mATri[edge_index]; } + PX_INLINE PxU32 GetAdjTri(SharedEdgeIndex edge_index) const { return MAKE_ADJ_TRI(mATri[edge_index]); } + PX_INLINE PxU32 GetAdjEdge(SharedEdgeIndex edge_index) const { return GET_EDGE_NB(mATri[edge_index]); } + PX_INLINE Ps::IntBool IsBoundaryEdge(SharedEdgeIndex edge_index) const { return IS_BOUNDARY(mATri[edge_index]); } +#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS + PX_INLINE Ps::IntBool HasActiveEdge01() const { return Ps::IntBool(IS_CONVEX_EDGE(mATri[EDGE01])); } + PX_INLINE Ps::IntBool HasActiveEdge20() const { return Ps::IntBool(IS_CONVEX_EDGE(mATri[EDGE02])); } + PX_INLINE Ps::IntBool HasActiveEdge12() const { return Ps::IntBool(IS_CONVEX_EDGE(mATri[EDGE12])); } + PX_INLINE Ps::IntBool HasActiveEdge(PxU32 i) const { return Ps::IntBool(IS_CONVEX_EDGE(mATri[i])); } +#endif +// private: + //! Links/References of adjacent triangles. The 2 most significant bits contains the counterpart edge in the adjacent triangle. + //! mATri[0] refers to edge 0-1 + //! mATri[1] refers to edge 0-2 + //! mATri[2] refers to edge 1-2 + PxU32 mATri[3]; + }; + + //! The adjacencies creation structure. + struct ADJACENCIESCREATE + { + //! Constructor + ADJACENCIESCREATE() : NbFaces(0), DFaces(NULL), WFaces(NULL) + { +#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS + Verts = NULL; + Epsilon = 0.1f; +// Epsilon = 0.001f; +#endif + } + + PxU32 NbFaces; //!< Number of faces in source topo + const PxU32* DFaces; //!< List of faces (dwords) or NULL + const PxU16* WFaces; //!< List of faces (words) or NULL +#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS + const PxVec3* Verts; + float Epsilon; +#endif + }; + + class Adjacencies : public Ps::UserAllocated + { + public: + Adjacencies(); + ~Adjacencies(); + + PxU32 mNbFaces; //!< Number of faces involved in the computation. + AdjTriangle* mFaces; //!< A list of AdjTriangles (one/face) + + bool Load(PxInputStream& stream); + // Basic mesh walking + PX_INLINE const AdjTriangle* GetAdjacentFace(const AdjTriangle& current_tri, SharedEdgeIndex edge_nb) const + { + // No checkings here, make sure mFaces has been created + + // Catch the link + PxU32 Link = current_tri.GetLink(edge_nb); + + // Returns NULL for boundary edges + if(IS_BOUNDARY(Link)) return NULL; + + // Else transform into face index + PxU32 Id = MAKE_ADJ_TRI(Link); + + // Possible counterpart edge is: + // PxU32 Edge = GET_EDGE_NB(Link); + + // And returns adjacent triangle + return &mFaces[Id]; + } + // Helpers + PxU32 ComputeNbBoundaryEdges() const; +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + bool GetBoundaryVertices(PxU32 nb_verts, bool* bound_status) const; +#else + bool GetBoundaryVertices(PxU32 nb_verts, bool* bound_status, const Gu::TriangleT* faces) const; +#endif + // +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + bool MakeLastRef(AdjTriangle& cur_tri, PxU32 vref); +#else + bool MakeLastRef(AdjTriangle& cur_tri, PxU32 vref, Gu::TriangleT* cur_topo); +#endif + private: + // New edge codes assignment + void AssignNewEdgeCode(PxU32 link, PxU8 edge_nb); + }; + +//#ifdef PX_COOKING + class AdjacenciesBuilder : public Adjacencies + { + public: + AdjacenciesBuilder(); + ~AdjacenciesBuilder(); + + bool Init(const ADJACENCIESCREATE& create); +// bool Save(Stream& stream) const; + }; +//#endif + +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp new file mode 100644 index 000000000..77e9e5f3a --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxIO.h" +#include "BVHStructureBuilder.h" +#include "GuAABBTreeBuild.h" +#include "GuSerialize.h" +#include "GuBVHStructure.h" + +using namespace physx; +using namespace Gu; + +// A.B. move this to some common place? +#define NB_OBJECTS_PER_NODE 4 + +// PT: TODO: - check that this is compatible with Gu::computeBounds(..., SQ_PRUNER_INFLATION, ...) +// PT: TODO: - refactor with "inflateBounds" in GuBounds.cpp if possible +// PT: TODO: - use SQ_PRUNER_INFLATION instead of hardcoding "0.01f" +// A.B. move to common place +PX_FORCE_INLINE void inflateBounds(PxBounds3& dst, const PxBounds3& src) +{ + using namespace physx::shdfnd::aos; + + const Vec4V minV = V4LoadU(&src.minimum.x); + const Vec4V maxV = V4LoadU(&src.maximum.x); + const Vec4V eV = V4Scale(V4Sub(maxV, minV), FLoad(0.5f * 0.01f)); + + V4StoreU(V4Sub(minV, eV), &dst.minimum.x); + PX_ALIGN(16, PxVec4) max4; + V4StoreA(V4Add(maxV, eV), &max4.x); + dst.maximum = PxVec3(max4.x, max4.y, max4.z); +} + +void flatten(const NodeAllocator& nodeAllocator, BVHNode* dest) +{ + // PT: gathers all build nodes allocated so far and flatten them to a linear destination array of smaller runtime nodes + PxU32 offset = 0; + const PxU32 nbSlabs = nodeAllocator.mSlabs.size(); + for(PxU32 s=0;s= nodeAllocator.mSlabs[j].mPool && pool[i].mPos < nodeAllocator.mSlabs[j].mPool + nodeAllocator.mSlabs[j].mNbUsedNodes) + { + localNodeIndex = PxU32(pool[i].mPos - nodeAllocator.mSlabs[j].mPool); + break; + } + nodeBase += nodeAllocator.mSlabs[j].mNbUsedNodes; + } + const PxU32 nodeIndex = nodeBase + localNodeIndex; + dest[offset].mData = nodeIndex<<1; + } + offset++; + } + } +} + +BVHStructureBuilder::BVHStructureBuilder(): + mBounds(NULL), + mNumVolumes(0), + mNumNodes(0), + mNodes(NULL), + mIndices(NULL) +{ +} + +BVHStructureBuilder::~BVHStructureBuilder() +{ + PX_FREE_AND_RESET(mBounds); + PX_FREE_AND_RESET(mNodes); + PX_FREE_AND_RESET(mIndices); +} + +bool BVHStructureBuilder::loadFromDesc(const PxBVHStructureDesc& desc) +{ + PX_ASSERT(desc.isValid()); + + const PxU32 numPrimitives = desc.bounds.count; + + // allocate one more for safe SIMD vec4 load + mBounds = reinterpret_cast(PX_ALLOC((numPrimitives + 1) * sizeof(PxBounds3), "PxBounds3")); + + const PxU8* sB = reinterpret_cast(desc.bounds.data); + + for(PxU32 i = 0; i < numPrimitives; i++) + { + inflateBounds(mBounds[i], *reinterpret_cast(sB)); + + sB += desc.bounds.stride; + } + mNumVolumes = numPrimitives; + + // build the BVH + AABBTreeBuildParams params; + params.mNbPrimitives = desc.bounds.count; + params.mAABBArray = mBounds; + params.mLimit = NB_OBJECTS_PER_NODE; + BuildStats stats; + NodeAllocator nodeAllocator; + + const bool buildStatus = buildAABBTree(params, nodeAllocator, stats, mIndices); + PX_UNUSED(buildStatus); + PX_ASSERT(buildStatus); + + // store the computed hierarchy + mNumNodes = stats.getCount(); + mNodes = reinterpret_cast(PX_ALLOC(sizeof(BVHNode)*mNumNodes, "AABB tree nodes")); + PX_ASSERT(mNumNodes==nodeAllocator.mTotalNbNodes); + + // store the results into BVHNode list + flatten(nodeAllocator, mNodes); + nodeAllocator.release(); + + return true; +} + +// A.B. move to load code +#define PX_BVH_STRUCTURE_VERSION 1 + +bool BVHStructureBuilder::save(PxOutputStream& stream, bool endian) const +{ + // write header + if(!writeHeader('B', 'V', 'H', 'S', PX_BVH_STRUCTURE_VERSION, endian, stream)) + return false; + + // write mData members + writeDword(mNumVolumes, endian, stream); + writeDword(mNumNodes, endian, stream); + + // write indices and bounds + for(PxU32 i = 0; i < mNumVolumes; i++) + { + writeDword(mIndices[i], endian, stream); + } + + for(PxU32 i = 0; i < mNumVolumes; i++) + { + writeFloatBuffer(&mBounds[i].minimum.x, 3, endian, stream); + writeFloatBuffer(&mBounds[i].maximum.x, 3, endian, stream); + } + + // write nodes + for(PxU32 i = 0; i < mNumNodes; i++) + { + writeDword(mNodes[i].mData, endian, stream); + + writeFloatBuffer(&mNodes[i].mBV.minimum.x, 3, endian, stream); + writeFloatBuffer(&mNodes[i].mBV.maximum.x, 3, endian, stream); + } + + return true; +} + +void BVHStructureBuilder::moveData(Gu::BVHStructureData& bvhData) +{ + bvhData.mBounds = mBounds; + bvhData.mIndices = mIndices; + bvhData.mNodes = mNodes; + bvhData.mNumNodes = mNumNodes; + bvhData.mNumVolumes = mNumVolumes; + + // set pointers to NULL so we do not release the memory that has been passed to physics create + mBounds = NULL; + mIndices = NULL; + mNodes = NULL; + mNumNodes = 0; + mNumVolumes = 0; +} diff --git a/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h new file mode 100644 index 000000000..93bf575b4 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_BVH_STRUCTURE_BUILDER_H +#define PX_BVH_STRUCTURE_BUILDER_H + +#include "PsUserAllocated.h" +#include "PxCooking.h" +#include "GuBVHStructure.h" + +namespace physx +{ + namespace Gu + { + struct BVHStructureData; + } + + class BVHStructureBuilder + { + public: + BVHStructureBuilder(); + ~BVHStructureBuilder(); + + bool loadFromDesc(const PxBVHStructureDesc& desc); + + bool save(PxOutputStream& stream, bool platformMismatch) const; + + void moveData(Gu::BVHStructureData& bvhData); + + private: + PxBounds3* mBounds; + PxU32 mNumVolumes; + PxU32 mNumNodes; + Gu::BVHNode* mNodes; + PxU32* mIndices; + }; + +} + +#endif + diff --git a/src/PhysX/physx/source/physxcooking/src/Cooking.cpp b/src/PhysX/physx/source/physxcooking/src/Cooking.cpp new file mode 100644 index 000000000..a56376309 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/Cooking.cpp @@ -0,0 +1,554 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxErrorCallback.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "PsFPU.h" +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" +#include "PxSimpleTriangleMesh.h" +#include "PxTriangleMeshDesc.h" +#include "PxConvexMeshDesc.h" +#include "PxCooking.h" +#include "Cooking.h" +#include "mesh/TriangleMeshBuilder.h" +#include "GuConvexMesh.h" +#include "ConvexMeshBuilder.h" +#include "BVHStructureBuilder.h" +#include "QuickHullConvexHullLib.h" +#include "CmIO.h" +#include "PxHeightFieldDesc.h" +#include "GuHeightField.h" +#include "HeightFieldCooking.h" +#include "common/PxPhysicsInsertionCallback.h" +#include "CmUtils.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using namespace physx; +using namespace Gu; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Cooking::setParams(const PxCookingParams& params) +{ + mParams = params; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const PxCookingParams& Cooking::getParams() const +{ + return mParams; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Cooking::platformMismatch() const +{ + // Get current endianness (the one for the platform where cooking is performed) + const PxI8 currentEndian = Ps::littleEndian(); + + const bool mismatch = currentEndian!=1; // The files must be little endian - we don't have big endian platforms anymore. + return mismatch; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Cooking::release() +{ + delete this; + + Ps::Foundation::decRefCount(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Cooking::validateTriangleMesh(const PxTriangleMeshDesc& desc) const +{ + // cooking code does lots of float bitwise reinterpretation that generates exceptions + PX_FPU_GUARD; + + if(!desc.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::validateTriangleMesh: user-provided triangle mesh descriptor is invalid!"); + return false; + } + + // PT: validation code doesn't look at midphase data, so ideally we wouldn't build the midphase structure at all here. + BV4TriangleMeshBuilder builder(mParams); + return builder.loadFromDesc(desc, NULL, true /*doValidate*/); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Cooking::cookTriangleMesh(TriangleMeshBuilder& builder, const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition) const +{ + // cooking code does lots of float bitwise reinterpretation that generates exceptions + PX_FPU_GUARD; + + if (condition) + *condition = PxTriangleMeshCookingResult::eSUCCESS; + if(!builder.loadFromDesc(desc, condition, false)) + { + return false; + } + + builder.save(stream, platformMismatch(), mParams); + return true; +} + +bool Cooking::cookTriangleMesh(const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition) const +{ + if(mParams.midphaseDesc.getType() == PxMeshMidPhase::eBVH33) + { + RTreeTriangleMeshBuilder builder(mParams); + return cookTriangleMesh(builder, desc, stream, condition); + } + else + { + BV4TriangleMeshBuilder builder(mParams); + return cookTriangleMesh(builder, desc, stream, condition); + } +} + +PxTriangleMesh* Cooking::createTriangleMesh(TriangleMeshBuilder& builder, const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition) const +{ + // cooking code does lots of float bitwise reinterpretation that generates exceptions + PX_FPU_GUARD; + + if (condition) + *condition = PxTriangleMeshCookingResult::eSUCCESS; + if(!builder.loadFromDesc(desc, condition, false)) + { + return NULL; + } + + // check if the indices can be moved from 32bits to 16bits + if(!(mParams.meshPreprocessParams & PxMeshPreprocessingFlag::eFORCE_32BIT_INDICES)) + builder.checkMeshIndicesSize(); + + PxConcreteType::Enum type; + if(builder.getMidphaseID()==PxMeshMidPhase::eBVH33) + type = PxConcreteType::eTRIANGLE_MESH_BVH33; + else + type = PxConcreteType::eTRIANGLE_MESH_BVH34; + + return static_cast(insertionCallback.buildObjectFromData(type, &builder.getMeshData())); +} + +PxTriangleMesh* Cooking::createTriangleMesh(const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition) const +{ + if(mParams.midphaseDesc.getType() == PxMeshMidPhase::eBVH33) + { + RTreeTriangleMeshBuilder builder(mParams); + return createTriangleMesh(builder, desc, insertionCallback, condition); + } + else + { + BV4TriangleMeshBuilder builder(mParams); + return createTriangleMesh(builder, desc, insertionCallback, condition); + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cook convex mesh from given desc, internal function to be shared between create/cook convex mesh +bool Cooking::cookConvexMeshInternal(const PxConvexMeshDesc& desc_, ConvexMeshBuilder& meshBuilder, ConvexHullLib* hullLib, + PxConvexMeshCookingResult::Enum* condition) const +{ + if (condition) + *condition = PxConvexMeshCookingResult::eFAILURE; + + if (!desc_.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::cookConvexMesh: user-provided convex mesh descriptor is invalid!"); + return false; + } + + if (mParams.areaTestEpsilon <= 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::cookConvexMesh: provided cooking parameter areaTestEpsilon is invalid!"); + return false; + } + + if(mParams.planeTolerance < 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::cookConvexMesh: provided cooking parameter planeTolerance is invalid!"); + return false; + } + + PxConvexMeshDesc desc = desc_; + bool polygonsLimitReached = false; + + // the convex will be cooked from provided points + if (desc_.flags & PxConvexFlag::eCOMPUTE_CONVEX) + { + PX_ASSERT(hullLib); + + // clean up the indices information, it could have been set by accident + desc.flags &= ~PxConvexFlag::e16_BIT_INDICES; + desc.indices.count = 0; + desc.indices.data = NULL; + desc.indices.stride = 0; + desc.polygons.count = 0; + desc.polygons.data = NULL; + desc.polygons.stride = 0; + + PxConvexMeshCookingResult::Enum res = hullLib->createConvexHull(); + if (res == PxConvexMeshCookingResult::eSUCCESS || res == PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED) + { + if (res == PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED) + polygonsLimitReached = true; + + hullLib->fillConvexMeshDesc(desc); + } + else + { + if (res == PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED) + { + *condition = PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED; + } + + return false; + } + } + + if (desc.points.count >= 256) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Cooking::cookConvexMesh: user-provided hull must have less than 256 vertices!"); + return false; + } + + if (!meshBuilder.build(desc, mParams.gaussMapLimit, false, hullLib)) + { + return false; + } + + if (condition) + { + *condition = polygonsLimitReached ? PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED : PxConvexMeshCookingResult::eSUCCESS; + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// cook convex mesh from given desc, save the results into stream +bool Cooking::cookConvexMesh(const PxConvexMeshDesc& desc_, PxOutputStream& stream, PxConvexMeshCookingResult::Enum* condition) const +{ + PX_FPU_GUARD; + // choose cooking library if needed + ConvexHullLib* hullLib = NULL; + PxConvexMeshDesc desc = desc_; + + if(desc_.flags & PxConvexFlag::eCOMPUTE_CONVEX) + { + const PxU16 gpuMaxVertsLimit = 64; + + // GRB supports 64 verts max + if(desc_.flags & PxConvexFlag::eGPU_COMPATIBLE) + { + desc.vertexLimit = PxMin(desc.vertexLimit, gpuMaxVertsLimit); + } + + hullLib = PX_NEW(QuickHullConvexHullLib) (desc, mParams); + } + + ConvexMeshBuilder meshBuilder(mParams.buildGPUData); + if(!cookConvexMeshInternal(desc,meshBuilder,hullLib , condition)) + { + if(hullLib) + PX_DELETE(hullLib); + return false; + } + + // save the cooked results into stream + if(!meshBuilder.save(stream, platformMismatch())) + { + if (condition) + { + *condition = PxConvexMeshCookingResult::eFAILURE; + } + if(hullLib) + PX_DELETE(hullLib); + return false; + } + + if(hullLib) + PX_DELETE(hullLib); + return true; +} + +////////////////////////////////////////////////////////////////////////// +// cook convex mesh from given desc, copy the results into internal convex mesh +// and insert the mesh into PxPhysics +PxConvexMesh* Cooking::createConvexMesh(const PxConvexMeshDesc& desc_, PxPhysicsInsertionCallback& insertionCallback, PxConvexMeshCookingResult::Enum* condition) const +{ + PX_FPU_GUARD; + // choose cooking library if needed + ConvexHullLib* hullLib = NULL; + PxConvexMeshDesc desc = desc_; + + if(desc.flags & PxConvexFlag::eCOMPUTE_CONVEX) + { + const PxU16 gpuMaxVertsLimit = 64; + + // GRB supports 64 verts max + if(desc_.flags & PxConvexFlag::eGPU_COMPATIBLE) + { + desc.vertexLimit = PxMin(desc.vertexLimit, gpuMaxVertsLimit); + } + + hullLib = PX_NEW(QuickHullConvexHullLib) (desc, mParams); + } + + // cook the mesh + ConvexMeshBuilder meshBuilder(mParams.buildGPUData); + if (!cookConvexMeshInternal(desc, meshBuilder, hullLib, condition)) + { + if(hullLib) + PX_DELETE(hullLib); + return NULL; + } + + // copy the constructed data into the new mesh + + PxU32 nb = 0; + Gu::ConvexHullData meshData; + meshBuilder.copy(meshData, nb); + + // insert into physics + Gu::ConvexMesh* convexMesh = static_cast(insertionCallback.buildObjectFromData(PxConcreteType::eCONVEX_MESH, &meshData)); + if (!convexMesh) + { + if(condition) + *condition = PxConvexMeshCookingResult::eFAILURE; + if (hullLib) + PX_DELETE(hullLib); + return NULL; + } + + convexMesh->setNb(nb); + convexMesh->setMass(meshBuilder.getMass()); + convexMesh->setInertia(meshBuilder.getInertia()); + if(meshBuilder.getBigConvexData()) + { + convexMesh->setBigConvexData(meshBuilder.getBigConvexData()); + meshBuilder.setBigConvexData(NULL); + } + + if(hullLib) + PX_DELETE(hullLib); + return convexMesh; +} + +////////////////////////////////////////////////////////////////////////// + +bool Cooking::validateConvexMesh(const PxConvexMeshDesc& desc) const +{ + ConvexMeshBuilder mesh(mParams.buildGPUData); + return mesh.build(desc, mParams.gaussMapLimit, true); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Cooking::computeHullPolygons(const PxSimpleTriangleMesh& mesh, PxAllocatorCallback& inCallback,PxU32& nbVerts, PxVec3*& vertices, + PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& hullPolygons) const +{ + PxVec3* geometry = reinterpret_cast(PxAlloca(sizeof(PxVec3)*mesh.points.count)); + Cooking::gatherStrided(mesh.points.data, geometry, mesh.points.count, sizeof(PxVec3), mesh.points.stride); + + PxU32* topology = reinterpret_cast(PxAlloca(sizeof(PxU32)*3*mesh.triangles.count)); + if (mesh.flags & PxMeshFlag::e16_BIT_INDICES) + { + // conversion; 16 bit index -> 32 bit index & stride + PxU32* dest = topology; + const PxU32* pastLastDest = topology + 3*mesh.triangles.count; + const PxU8* source = reinterpret_cast(mesh.triangles.data); + while (dest < pastLastDest) + { + const PxU16 * trig16 = reinterpret_cast(source); + *dest++ = trig16[0]; + *dest++ = trig16[1]; + *dest++ = trig16[2]; + source += mesh.triangles.stride; + } + } + else + { + Cooking::gatherStrided(mesh.triangles.data, topology, mesh.triangles.count, sizeof(PxU32) * 3, mesh.triangles.stride); + } + + ConvexMeshBuilder meshBuilder(mParams.buildGPUData); + if(!meshBuilder.computeHullPolygons(mesh.points.count,geometry,mesh.triangles.count,topology,inCallback, nbVerts, vertices,nbIndices,indices,nbPolygons,hullPolygons)) + return false; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Cooking::cookHeightField(const PxHeightFieldDesc& desc, PxOutputStream& stream) const +{ + PX_FPU_GUARD; + + if(!desc.isValid()) + { + #if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!"); + #endif + return false; + } + + Gu::HeightField hf(NULL); + + if(!hf.loadFromDesc(desc)) + { + hf.releaseMemory(); + return false; + } + + if (!saveHeightField(hf, stream, platformMismatch())) + { + hf.releaseMemory(); + return false; + } + + hf.releaseMemory(); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxHeightField* Cooking::createHeightField(const PxHeightFieldDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const +{ + PX_FPU_GUARD; + + if(!desc.isValid()) + { + #if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!"); + #endif + return NULL; + } + + Gu::HeightField* hf; + PX_NEW_SERIALIZED(hf, Gu::HeightField)(NULL); + + if(!hf->loadFromDesc(desc)) + { + PX_DELETE(hf); + return NULL; + } + + // create heightfield and set the HF data + Gu::HeightField* heightField = static_cast(insertionCallback.buildObjectFromData(PxConcreteType::eHEIGHTFIELD, &hf->mData)); + if(!heightField) + { + PX_DELETE(hf); + return NULL; + } + + // copy the Gu::HeightField variables + heightField->mSampleStride = hf->mSampleStride; + heightField->mNbSamples = hf->mNbSamples; + heightField->mMinHeight = hf->mMinHeight; + heightField->mMaxHeight = hf->mMaxHeight; + heightField->mModifyCount = hf->mModifyCount; + + PX_DELETE(hf); + return heightField; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Cooking::cookBVHStructure(const PxBVHStructureDesc& desc, PxOutputStream& stream) const +{ + PX_FPU_GUARD; + + if(!desc.isValid()) + { + #if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!"); + #endif + return false; + } + + BVHStructureBuilder builder; + if(!builder.loadFromDesc(desc)) + { + return false; + } + + builder.save(stream, platformMismatch()); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxBVHStructure* Cooking::createBVHStructure(const PxBVHStructureDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const +{ + PX_FPU_GUARD; + + if(!desc.isValid()) + { + #if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!"); + #endif + return NULL; + } + + BVHStructureBuilder builder; + if(!builder.loadFromDesc(desc)) + { + return NULL; + } + + Gu::BVHStructureData bvhData; + builder.moveData(bvhData); + Gu::BVHStructure* bvhStructure = static_cast(insertionCallback.buildObjectFromData(PxConcreteType::eBVH_STRUCTURE, &bvhData)); + + return bvhStructure; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxCooking* PxCreateCooking(PxU32 /*version*/, PxFoundation& foundation, const PxCookingParams& params) +{ + PX_ASSERT(static_cast(&foundation) == &Ps::Foundation::getInstance()); + PX_UNUSED(foundation); + + Ps::Foundation::incRefCount(); + + return PX_NEW(Cooking)(params); +} + diff --git a/src/PhysX/physx/source/physxcooking/src/Cooking.h b/src/PhysX/physx/source/physxcooking/src/Cooking.h new file mode 100644 index 000000000..7ee48ba51 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/Cooking.h @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PSCOOKING_H +#define PX_PSCOOKING_H + +#include "foundation/PxMemory.h" +#include "PxCooking.h" +#include "PsUserAllocated.h" + +namespace physx +{ +class TriangleMeshBuilder; +class ConvexMeshBuilder; +class ConvexHullLib; + +class Cooking: public PxCooking, public Ps::UserAllocated +{ +public: + Cooking(const PxCookingParams& params): mParams(params) {} + + virtual void release(); + virtual void setParams(const PxCookingParams& params); + virtual const PxCookingParams& getParams() const; + virtual bool platformMismatch() const; + virtual bool cookTriangleMesh(const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition = NULL) const; + virtual PxTriangleMesh* createTriangleMesh(const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition = NULL) const; + virtual bool validateTriangleMesh(const PxTriangleMeshDesc& desc) const; + + virtual bool cookConvexMesh(const PxConvexMeshDesc& desc, PxOutputStream& stream, PxConvexMeshCookingResult::Enum* condition) const; + virtual PxConvexMesh* createConvexMesh(const PxConvexMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxConvexMeshCookingResult::Enum* condition) const; + virtual bool validateConvexMesh(const PxConvexMeshDesc& desc) const; + virtual bool computeHullPolygons(const PxSimpleTriangleMesh& mesh, PxAllocatorCallback& inCallback,PxU32& nbVerts, PxVec3*& vertices, + PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& hullPolygons) const; + virtual bool cookHeightField(const PxHeightFieldDesc& desc, PxOutputStream& stream) const; + virtual PxHeightField* createHeightField(const PxHeightFieldDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const; + virtual bool cookBVHStructure(const PxBVHStructureDesc& desc, PxOutputStream& stream) const; + virtual PxBVHStructure* createBVHStructure(const PxBVHStructureDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const; + + PX_FORCE_INLINE static void gatherStrided(const void* src, void* dst, PxU32 nbElem, PxU32 elemSize, PxU32 stride) + { + const PxU8* s = reinterpret_cast(src); + PxU8* d = reinterpret_cast(dst); + while(nbElem--) + { + PxMemCopy(d, s, elemSize); + d += elemSize; + s += stride; + } + } + +private: + bool cookConvexMeshInternal(const PxConvexMeshDesc& desc, ConvexMeshBuilder& meshBuilder, ConvexHullLib* hullLib, PxConvexMeshCookingResult::Enum* condition) const; + bool cookTriangleMesh(TriangleMeshBuilder& builder, const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition) const; + + PxTriangleMesh* createTriangleMesh(TriangleMeshBuilder& builder, const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition) const; + +private: + PxCookingParams mParams; + +}; + +} +#endif //#define PX_PSCOOKING_H diff --git a/src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp b/src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp new file mode 100644 index 000000000..ed50a5c9d --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMath.h" +#include "CookingUtils.h" +#include "CmRadixSortBuffered.h" +#include "PsAllocator.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Cm; + +ReducedVertexCloud::ReducedVertexCloud(const PxVec3* verts, PxU32 nb_verts) : mNbRVerts(0), mRVerts(NULL), mXRef(NULL) +{ + mVerts = verts; + mNbVerts = nb_verts; +} + +ReducedVertexCloud::~ReducedVertexCloud() +{ + Clean(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Frees used ram. +* \return Self-reference +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +ReducedVertexCloud& ReducedVertexCloud::Clean() +{ + PX_DELETE_POD(mXRef); + PX_FREE_AND_RESET(mRVerts); + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Reduction method. Use this to create a minimal vertex cloud. +* \param rc [out] result structure +* \return true if success +* \warning This is not about welding nearby vertices, here we look for real redundant ones. +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool ReducedVertexCloud::Reduce(REDUCEDCLOUD* rc) +{ + Clean(); + + mXRef = PX_NEW(PxU32)[mNbVerts]; + + float* f = PX_NEW_TEMP(float)[mNbVerts]; + + for(PxU32 i=0;i(f), mNbVerts, RADIX_UNSIGNED); + + for(PxU32 i=0;i(f), mNbVerts, RADIX_UNSIGNED); + + for(PxU32 i=0;i(f), mNbVerts, RADIX_UNSIGNED).GetRanks(); + + PX_DELETE_POD(f); + + mNbRVerts = 0; + const PxU32 Junk[] = {PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32}; + const PxU32* Previous = Junk; + mRVerts = reinterpret_cast(PX_ALLOC(sizeof(PxVec3) * mNbVerts, "PxVec3")); + PxU32 Nb = mNbVerts; + while(Nb--) + { + const PxU32 Vertex = *Sorted++; // Vertex number + + const PxU32* current = reinterpret_cast(&mVerts[Vertex]); + if(current[0]!=Previous[0] || current[1]!=Previous[1] || current[2]!=Previous[2]) + mRVerts[mNbRVerts++] = mVerts[Vertex]; + + Previous = current; + + mXRef[Vertex] = mNbRVerts-1; + } + + if(rc) + { + rc->CrossRef = mXRef; + rc->NbRVerts = mNbRVerts; + rc->RVerts = mRVerts; + } + return true; +} diff --git a/src/PhysX/physx/source/physxcooking/src/CookingUtils.h b/src/PhysX/physx/source/physxcooking/src/CookingUtils.h new file mode 100644 index 000000000..7f5544b71 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/CookingUtils.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COOKINGUTILS +#define PX_COOKINGUTILS + +#include "foundation/PxVec3.h" +#include "PxPhysXConfig.h" + +namespace physx +{ + //! Vertex cloud reduction result structure + struct REDUCEDCLOUD + { + // Out + PxVec3* RVerts; //!< Reduced list + PxU32 NbRVerts; //!< Reduced number of vertices + PxU32* CrossRef; //!< nb_verts remapped indices + }; + + class ReducedVertexCloud + { + public: + // Constructors/destructor + ReducedVertexCloud(const PxVec3* verts, PxU32 nb_verts); + ~ReducedVertexCloud(); + // Free used bytes + ReducedVertexCloud& Clean(); + // Cloud reduction + bool Reduce(REDUCEDCLOUD* rc=NULL); + // Data access + PX_INLINE PxU32 GetNbVerts() const { return mNbVerts; } + PX_INLINE PxU32 GetNbReducedVerts() const { return mNbRVerts; } + PX_INLINE const PxVec3* GetReducedVerts() const { return mRVerts; } + PX_INLINE const PxVec3& GetReducedVertex(PxU32 i) const { return mRVerts[i]; } + PX_INLINE const PxU32* GetCrossRefTable() const { return mXRef; } + + private: + // Original vertex cloud + PxU32 mNbVerts; //!< Number of vertices + const PxVec3* mVerts; //!< List of vertices (pointer copy) + + // Reduced vertex cloud + PxU32 mNbRVerts; //!< Reduced number of vertices + PxVec3* mRVerts; //!< Reduced list of vertices + PxU32* mXRef; //!< Cross-reference table (used to remap topologies) + }; + +} + +#endif + diff --git a/src/PhysX/physx/source/physxcooking/src/EdgeList.cpp b/src/PhysX/physx/source/physxcooking/src/EdgeList.cpp new file mode 100644 index 000000000..aca9db0d8 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/EdgeList.cpp @@ -0,0 +1,753 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxMemory.h" +#include "EdgeList.h" +#include "PxTriangle.h" +#include "PsMathUtils.h" +#include "CmRadixSortBuffered.h" +#include "GuSerialize.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + +Gu::EdgeList::EdgeList() +{ + mData.mNbEdges = 0; + mData.mEdgeFaces = NULL; + mData.mEdges = NULL; + mData.mEdgeToTriangles = NULL; + mData.mFacesByEdges = NULL; +} + +Gu::EdgeList::~EdgeList() +{ + PX_FREE_AND_RESET(mData.mFacesByEdges); + PX_FREE_AND_RESET(mData.mEdgeToTriangles); + PX_FREE_AND_RESET(mData.mEdges); + PX_DELETE_POD(mData.mEdgeFaces); +} + +bool Gu::EdgeList::load(PxInputStream& stream) +{ + // Import header + PxU32 Version; + bool Mismatch; + if(!ReadHeader('E', 'D', 'G', 'E', Version, Mismatch, stream)) + return false; + + // Import edges + mData.mNbEdges = readDword(Mismatch, stream); + //mEdges = ICE_NEW_MEM(Edge[mNbEdges],Edge); + mData.mEdges = reinterpret_cast(PX_ALLOC(sizeof(EdgeData)*mData.mNbEdges, "EdgeData")); + stream.read(mData.mEdges, sizeof(EdgeData)*mData.mNbEdges); + + mData.mNbFaces = readDword(Mismatch, stream); + //mEdgeFaces = ICE_NEW_MEM(EdgeTriangle[mNbFaces],EdgeTriangle); + mData.mEdgeFaces = reinterpret_cast(PX_ALLOC(sizeof(EdgeTriangleData)*mData.mNbFaces, "EdgeTriangleData")); + stream.read(mData.mEdgeFaces, sizeof(EdgeTriangleData)*mData.mNbFaces); + + //mEdgeToTriangles = ICE_NEW_MEM(EdgeDesc[mNbEdges],EdgeDesc); + mData.mEdgeToTriangles = reinterpret_cast(PX_ALLOC(sizeof(EdgeDescData)*mData.mNbEdges, "EdgeDescData")); + stream.read(mData.mEdgeToTriangles, sizeof(EdgeDescData)*mData.mNbEdges); + + + PxU32 LastOffset = mData.mEdgeToTriangles[mData.mNbEdges-1].Offset + mData.mEdgeToTriangles[mData.mNbEdges-1].Count; + mData.mFacesByEdges = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*LastOffset, "EdgeList FacesByEdges")); + stream.read(mData.mFacesByEdges, sizeof(PxU32)*LastOffset); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes the edge-list. + * \param create [in] edge-list creation structure + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Gu::EdgeListBuilder::init(const EDGELISTCREATE& create) +{ + bool FacesToEdges = create.Verts ? true : create.FacesToEdges; + bool EdgesToFaces = create.Verts ? true : create.EdgesToFaces; + + // "FacesToEdges" maps each face to three edges. + if(FacesToEdges && !createFacesToEdges(create.NbFaces, create.DFaces, create.WFaces)) + return false; + + // "EdgesToFaces" maps each edge to the set of faces sharing this edge + if(EdgesToFaces && !createEdgesToFaces(create.NbFaces, create.DFaces, create.WFaces)) + return false; + + // Create active edges + if(create.Verts && !computeActiveEdges(create.NbFaces, create.DFaces, create.WFaces, create.Verts, create.Epsilon)) + return false; + + // Get rid of useless data + if(!create.FacesToEdges) + { + PX_FREE_AND_RESET(mData.mEdgeFaces); + } + if(!create.EdgesToFaces) + { + PX_FREE_AND_RESET(mData.mEdgeToTriangles); + PX_FREE_AND_RESET(mData.mFacesByEdges); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes FacesToEdges. + * After the call: + * - mNbEdges is updated with the number of non-redundant edges + * - mEdges is a list of mNbEdges edges (one edge is 2 vertex-references) + * - mEdgesRef is a list of nbfaces structures with 3 indexes in mEdges for each face + * + * \param nb_faces [in] a number of triangles + * \param dfaces [in] list of triangles with PxU32 vertex references (or NULL) + * \param wfaces [in] list of triangles with PxU16 vertex references (or NULL) + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Gu::EdgeListBuilder::createFacesToEdges(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces) +{ + // Checkings + if(!nb_faces || (!dfaces && !wfaces)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "EdgeList::CreateFacesToEdges: NULL parameter!"); + return false; + } + + if(mData.mEdgeFaces) + return true; // Already computed! + + // 1) Get some bytes: I need one EdgesRefs for each face, and some temp buffers + mData.mEdgeFaces = PX_NEW(EdgeTriangleData)[nb_faces]; // Link faces to edges + PxU32* VRefs0 = PX_NEW_TEMP(PxU32)[nb_faces*3]; // Temp storage + PxU32* VRefs1 = PX_NEW_TEMP(PxU32)[nb_faces*3]; // Temp storage + EdgeData* Buffer = PX_NEW_TEMP(EdgeData)[nb_faces*3]; // Temp storage + + // 2) Create a full redundant list of 3 edges / face. + for(PxU32 i=0;i stored in temp buffer + Buffer[mData.mNbEdges].Ref0 = SortedRef0; + Buffer[mData.mNbEdges].Ref1 = SortedRef1; + mData.mNbEdges++; + } + PreviousRef0 = SortedRef0; + PreviousRef1 = SortedRef1; + + // Create mEdgesRef on the fly + mData.mEdgeFaces[Face/3].mLink[ID] = mData.mNbEdges-1; + } + + // 5) Here, mNbEdges==#non redundant edges + mData.mEdges = reinterpret_cast(PX_ALLOC(sizeof(EdgeData)*mData.mNbEdges, "EdgeData")); + + // Create real edges-list. + PxMemCopy(mData.mEdges, Buffer, mData.mNbEdges*sizeof(EdgeData)); + + // 6) Free ram and exit + PX_DELETE_POD(Buffer); + PX_DELETE_POD(VRefs1); + PX_DELETE_POD(VRefs0); + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes EdgesToFaces. + * After the call: + * - mEdgeToTriangles is created + * - mFacesByEdges is created + * + * \param nb_faces [in] a number of triangles + * \param dfaces [in] list of triangles with PxU32 vertex references (or NULL) + * \param wfaces [in] list of triangles with PxU16 vertex references (or NULL) + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Gu::EdgeListBuilder::createEdgesToFaces(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces) +{ + // 1) I need FacesToEdges ! + if(!createFacesToEdges(nb_faces, dfaces, wfaces)) + return false; + + // 2) Get some bytes: one Pair structure / edge + mData.mEdgeToTriangles = reinterpret_cast(PX_ALLOC(sizeof(EdgeDescData)*mData.mNbEdges, "EdgeDescData")); + PxMemZero(mData.mEdgeToTriangles, sizeof(EdgeDescData)*mData.mNbEdges); + + // 3) Create Counters, ie compute the #faces sharing each edge + for(PxU32 i=0;i(PX_ALLOC(sizeof(PxU32)*LastOffset, "EdgeListBuilder FacesByEdges")); + + // 5) Create mFacesByEdges + for(PxU32 i=0;i(PX_ALLOC_TEMP(sizeof(bool)*NbEdges, "bool")); + + // Loop through edges and look for convex ones + bool* CurrentMark = ActiveEdges; + + while(NbEdges--) + { + // Get number of triangles sharing current edge + PxU32 Count = ED->Count; + // Boundary edges are active => keep them (actually they're silhouette edges directly) + // Internal edges can be active => test them + // Singular edges ? => discard them + bool Active = false; + if(Count==1) + { + Active = true; + } + else if(Count==2) + { + PxU32 FaceIndex0 = FBE[ED->Offset+0]*3; + PxU32 FaceIndex1 = FBE[ED->Offset+1]*3; + + PxU32 VRef00, VRef01, VRef02; + PxU32 VRef10, VRef11, VRef12; + + if(dfaces) + { + VRef00 = dfaces[FaceIndex0+0]; + VRef01 = dfaces[FaceIndex0+1]; + VRef02 = dfaces[FaceIndex0+2]; + VRef10 = dfaces[FaceIndex1+0]; + VRef11 = dfaces[FaceIndex1+1]; + VRef12 = dfaces[FaceIndex1+2]; + } + else //if(wfaces) + { + PX_ASSERT(wfaces); + VRef00 = wfaces[FaceIndex0+0]; + VRef01 = wfaces[FaceIndex0+1]; + VRef02 = wfaces[FaceIndex0+2]; + VRef10 = wfaces[FaceIndex1+0]; + VRef11 = wfaces[FaceIndex1+1]; + VRef12 = wfaces[FaceIndex1+2]; + } + + { + // We first check the opposite vertex against the plane + + PxU32 Op = OppositeVertex(VRef00, VRef01, VRef02, Edges->Ref0, Edges->Ref1); + + PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]); + + if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges + { + PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); + PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); + + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + const float a = Ps::angle(N0, N1); + + if(fabsf(a)>epsilon) Active = true; + } + else + { + PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); + PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + + if(N0.dot(N1) < -0.999f) + { + Active = true; + } + + } + } + + } + else + { + //Connected to more than 2 + //We need to loop through the triangles and count the number of unique triangles (considering back-face triangles as non-unique). If we end up with more than 2 unique triangles, + //then by definition this is an inactive edge. However, if we end up with 2 unique triangles (say like a double-sided tesselated surface), then it depends on the same rules as above + + PxU32 FaceInd0 = FBE[ED->Offset]*3; + PxU32 VRef00, VRef01, VRef02; + PxU32 VRef10=0, VRef11=0, VRef12=0; + if(dfaces) + { + VRef00 = dfaces[FaceInd0+0]; + VRef01 = dfaces[FaceInd0+1]; + VRef02 = dfaces[FaceInd0+2]; + } + else //if(wfaces) + { + PX_ASSERT(wfaces); + VRef00 = wfaces[FaceInd0+0]; + VRef01 = wfaces[FaceInd0+1]; + VRef02 = wfaces[FaceInd0+2]; + } + + + PxU32 numUniqueTriangles = 1; + bool doubleSided0 = false; + bool doubleSided1 = 0; + + for(PxU32 a = 1; a < Count; ++a) + { + PxU32 FaceInd = FBE[ED->Offset+a]*3; + + PxU32 VRef0, VRef1, VRef2; + if(dfaces) + { + VRef0 = dfaces[FaceInd+0]; + VRef1 = dfaces[FaceInd+1]; + VRef2 = dfaces[FaceInd+2]; + } + else //if(wfaces) + { + PX_ASSERT(wfaces); + VRef0 = wfaces[FaceInd+0]; + VRef1 = wfaces[FaceInd+1]; + VRef2 = wfaces[FaceInd+2]; + } + + if(((VRef0 != VRef00) && (VRef0 != VRef01) && (VRef0 != VRef02)) || + ((VRef1 != VRef00) && (VRef1 != VRef01) && (VRef1 != VRef02)) || + ((VRef2 != VRef00) && (VRef2 != VRef01) && (VRef2 != VRef02))) + { + //Not the same as trig 0 + if(numUniqueTriangles == 2) + { + if(((VRef0 != VRef10) && (VRef0 != VRef11) && (VRef0 != VRef12)) || + ((VRef1 != VRef10) && (VRef1 != VRef11) && (VRef1 != VRef12)) || + ((VRef2 != VRef10) && (VRef2 != VRef11) && (VRef2 != VRef12))) + { + //Too many unique triangles - terminate and mark as inactive + numUniqueTriangles++; + break; + } + else + { + PxTriangle T0(verts[VRef10], verts[VRef11], verts[VRef12]); + PxTriangle T1(verts[VRef0], verts[VRef1], verts[VRef2]); + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + + if(N0.dot(N1) < -0.999f) + doubleSided1 = true; + } + } + else + { + VRef10 = VRef0; + VRef11 = VRef1; + VRef12 = VRef2; + numUniqueTriangles++; + } + } + else + { + //Check for double sided... + PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); + PxTriangle T1(verts[VRef0], verts[VRef1], verts[VRef2]); + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + + if(N0.dot(N1) < -0.999f) + doubleSided0 = true; + } + } + + if(numUniqueTriangles == 1) + Active = true; + if(numUniqueTriangles == 2) + { + //Potentially active. Let's check the angles between the surfaces... + + if(doubleSided0 || doubleSided1) + { + + // Plane PL1 = faces[FBE[ED->Offset+1]].PlaneEquation(verts); + PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]); + + // if(PL1.Distance(verts[Op])<-epsilon) Active = true; + //if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges + //KS - can't test signed distance for concave edges. This is a double-sided poly + { + PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); + PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); + + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + const float a = Ps::angle(N0, N1); + + if(fabsf(a)>epsilon) + Active = true; + } + } + else + { + + //Not double sided...must have had a bunch of duplicate triangles!!!! + //Treat as normal + PxU32 Op = OppositeVertex(VRef00, VRef01, VRef02, Edges->Ref0, Edges->Ref1); + + // Plane PL1 = faces[FBE[ED->Offset+1]].PlaneEquation(verts); + PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]); + + // if(PL1.Distance(verts[Op])<-epsilon) Active = true; + if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges + { + PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); + PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); + + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + const float a = Ps::angle(N0, N1); + + if(fabsf(a)>epsilon) + Active = true; + } + } + } + else + { + //Lots of triangles all smooshed together. Just activate the edge in this case + Active = true; + } + + } + + *CurrentMark++ = Active; + ED++; + Edges++; + } + + + // Now copy bits back into already existing edge structures + // - first in edge triangles + for(PxU32 i=0;iMaxIndex) MaxIndex = VRef0; + if(VRef1>MaxIndex) MaxIndex = VRef1; + if(VRef2>MaxIndex) MaxIndex = VRef2; + } + + MaxIndex++; + bool* ActiveVerts = reinterpret_cast(PX_ALLOC_TEMP(sizeof(bool)*MaxIndex, "bool")); + PxMemZero(ActiveVerts, MaxIndex*sizeof(bool)); + + PX_ASSERT(dfaces || wfaces); + for(PxU32 i=0;i mark edge vertices as active + PxU32 r0, r1; + if(j==0) { r0=0; r1=1; } + else if(j==1) { r0=1; r1=2; } + else /*if(j==2)*/ { PX_ASSERT(j==2); r0=0; r1=2; } + ActiveVerts[VRef[r0]] = ActiveVerts[VRef[r1]] = true; + } + } + } + +/* for(PxU32 i=0;i mark edge vertices as inactive + PxU32 r0, r1; + if(j==0) { r0=0; r1=1; } + if(j==1) { r0=1; r1=2; } + if(j==2) { r0=0; r1=2; } + ActiveVerts[VRef[r0]] = ActiveVerts[VRef[r1]] = false; + } + } + }*/ + + // Now stuff this into the structure + for(PxU32 i=0;i(&v.x); + const PxU32 f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0 + return (f>>22)^(f>>12)^(f); +} + +static PX_FORCE_INLINE PxU32 getHashValue(const Indices& v) +{ +// const PxU32* h = v.mRef; +// const PxU32 f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0 +// return (f>>22)^(f>>12)^(f); + + PxU32 a = v.mRef[0]; + PxU32 b = v.mRef[1]; + PxU32 c = v.mRef[2]; + a=a-b; a=a-c; a=a^(c >> 13); + b=b-c; b=b-a; b=b^(a << 8); + c=c-a; c=c-b; c=c^(b >> 13); + a=a-b; a=a-c; a=a^(c >> 12); + b=b-c; b=b-a; b=b^(a << 16); + c=c-a; c=c-b; c=c^(b >> 5); + a=a-b; a=a-c; a=a^(c >> 3); + b=b-c; b=b-a; b=b^(a << 10); + c=c-a; c=c-b; c=c^(b >> 15); + return c; +} + +MeshCleaner::MeshCleaner(PxU32 nbVerts, const PxVec3* srcVerts, PxU32 nbTris, const PxU32* srcIndices, PxF32 meshWeldTolerance) +{ + PxVec3* cleanVerts = reinterpret_cast(PX_ALLOC(sizeof(PxVec3)*nbVerts, "MeshCleaner")); + PX_ASSERT(cleanVerts); + + PxU32* indices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTris*3, "MeshCleaner")); + + PxU32* remapTriangles = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTris, "MeshCleaner")); + + PxU32* vertexIndices = NULL; + if(meshWeldTolerance!=0.0f) + { + vertexIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbVerts, "MeshCleaner")); + const PxF32 weldTolerance = 1.0f / meshWeldTolerance; + // snap to grid + for(PxU32 i=0; i(PX_ALLOC(sizeof(PxU32)*(hashSize + maxNbElems), "MeshCleaner")); + PX_ASSERT(hashTable); + memset(hashTable, 0xff, hashSize * sizeof(PxU32)); + PxU32* const next = hashTable + hashSize; + + PxU32* remapVerts = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbVerts, "MeshCleaner")); + memset(remapVerts, 0xff, nbVerts * sizeof(PxU32)); + + for(PxU32 i=0;i=nbVerts || vref1>=nbVerts || vref2>=nbVerts) + continue; + + // PT: you can still get zero-area faces when the 3 vertices are perfectly aligned + const PxVec3& p0 = srcVerts[vref0]; + const PxVec3& p1 = srcVerts[vref1]; + const PxVec3& p2 = srcVerts[vref2]; + const float area2 = ((p0 - p1).cross(p0 - p2)).magnitudeSquared(); + if(area2==0.0f) + continue; + + vref0 = remapVerts[vref0]; + vref1 = remapVerts[vref1]; + vref2 = remapVerts[vref2]; + if(vref0==vref1 || vref1==vref2 || vref2==vref0) + continue; + + indices[nbCleanedTris*3+0] = vref0; + indices[nbCleanedTris*3+1] = vref1; + indices[nbCleanedTris*3+2] = vref2; + remapTriangles[nbCleanedTris] = i; + nbCleanedTris++; + } + PX_FREE(remapVerts); + + PxU32 nbToGo = nbCleanedTris; + nbCleanedTris = 0; + memset(hashTable, 0xff, hashSize * sizeof(PxU32)); + + Indices* const I = reinterpret_cast(indices); + bool idtRemap = true; + for(PxU32 i=0;i +PxU32 kmeans_cluster(const Vec *input, + PxU32 inputCount, + PxU32 clumpCount, + Vec *clusters, + PxU32 *outputIndices, + Type threshold, // controls how long it works to converge towards a least errors solution. + Type collapseDistance) // distance between clumps to consider them to be essentially equal. +{ + PxU32 convergeCount = 64; // maximum number of iterations attempting to converge to a solution.. + PxU32* counts = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxU32)*clumpCount, "PxU32")); + Type error=0; + if ( inputCount <= clumpCount ) // if the number of input points is less than our clumping size, just return the input points. + { + clumpCount = inputCount; + for (PxU32 i=0; i (PX_ALLOC_TEMP(sizeof(PxVec3)*clumpCount, "PxVec3")); + + // Take a sampling of the input points as initial centroid estimates. + for (PxU32 i=0; i threshold ); // keep going until the error is reduced by this threshold amount. + + PX_FREE(centroids); + } + + // ok..now we prune the clumps if necessary. + // The rules are; first, if a clump has no 'counts' then we prune it as it's unused. + // The second, is if the centroid of this clump is essentially the same (based on the distance tolerance) + // as an existing clump, then it is pruned and all indices which used to point to it, now point to the one + // it is closest too. + PxU32 outCount = 0; // number of clumps output after pruning performed. + Type d2 = collapseDistance*collapseDistance; // squared collapse distance. + for (PxU32 i=0; i(input, inputSize, clumpCount, outputClusters, outputIndices, errorThreshold, collapseDistance); +} + +class QuantizerImpl : public Quantizer, public Ps::UserAllocated +{ +public: + QuantizerImpl(void) + { + mScale = PxVec3(1.0f, 1.0f, 1.0f); + mCenter = PxVec3(0.0f, 0.0f, 0.0f); + } + + // Use the k-means quantizer, similar results, but much slower. + virtual const PxVec3 * kmeansQuantize3D(PxU32 vcount, + const PxVec3 *vertices, + PxU32 stride, + bool denormalizeResults, + PxU32 maxVertices, + PxU32 &outVertsCount) + { + const PxVec3 *ret = NULL; + outVertsCount = 0; + mNormalizedInput.clear(); + mQuantizedOutput.clear(); + + if ( vcount > 0 ) + { + normalizeInput(vcount,vertices, stride); + + PxVec3* quantizedOutput = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxVec3)*vcount, "PxVec3")); + PxU32* quantizedIndices = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxU32)*vcount, "PxU32")); + outVertsCount = kmeans_cluster3d(&mNormalizedInput[0], vcount, maxVertices, quantizedOutput, quantizedIndices, 0.01f, 0.0001f ); + if ( outVertsCount > 0 ) + { + if ( denormalizeResults ) + { + for (PxU32 i=0; i (vertices); + mNormalizedInput.clear(); + mQuantizedOutput.clear(); + PxBounds3 bounds; + bounds.setEmpty(); + for (PxU32 i=0; i (vtx); + vtx += stride; + + bounds.include(v); + } + + mCenter = bounds.getCenter(); + + PxVec3 dim = bounds.getDimensions(); + dim *= 1.001f; + mScale = dim*0.5f; + + for (PxU32 i = 0; i < 3; i++) + { + if(dim[i] == 0) + mScale[i] = 1.0f; + } + + PxVec3 recip; + recip.x = 1.0f / mScale.x; + recip.y = 1.0f / mScale.y; + recip.z = 1.0f / mScale.z; + + vtx = reinterpret_cast (vertices); + for (PxU32 i=0; i (vtx); + vtx += stride; + + v = (v - mCenter).multiply(recip); + + mNormalizedInput.pushBack(v); + } + } + + virtual ~QuantizerImpl(void) + { + + } + + private: + PxVec3 mScale; + PxVec3 mCenter; + Ps::Array mNormalizedInput; + Ps::Array mQuantizedOutput; +}; + +Quantizer * physx::createQuantizer(void) +{ + QuantizerImpl *m = PX_NEW(QuantizerImpl); + return static_cast< Quantizer *>(m); +} diff --git a/src/PhysX/physx/source/physxcooking/src/Quantizer.h b/src/PhysX/physx/source/physxcooking/src/Quantizer.h new file mode 100644 index 000000000..cbab0e12e --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/Quantizer.h @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef QUANTIZER_H +#define QUANTIZER_H + +#include "foundation/Px.h" + +namespace physx +{ + + ////////////////////////////////////////////////////////////////////////// + // K-means quantization class + // see http://en.wikipedia.org/wiki/K-means_clustering + // implementation from John Ratcliff http://codesuppository.blogspot.ch/2010/12/k-means-clustering-algorithm.html + class Quantizer + { + public: + // quantize the input vertices + virtual const PxVec3* kmeansQuantize3D(PxU32 vcount, + const PxVec3 *vertices, + PxU32 stride, + bool denormalizeResults, + PxU32 maxVertices, + PxU32 &outVertsCount) = 0; + + // returns the denormalized scale + virtual const PxVec3& getDenormalizeScale(void) const = 0; + + // returns the denormalized center + virtual const PxVec3& getDenormalizeCenter(void) const = 0; + + // release internal data + virtual void release(void) = 0; + + + protected: + virtual ~Quantizer(void) + { + + } + }; + + // creates the quantizer class + Quantizer * createQuantizer(void); + +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp new file mode 100644 index 000000000..5db4c6abe --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp @@ -0,0 +1,358 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsUserAllocated.h" +#include "PsUtilities.h" +#include "PsMathUtils.h" +#include "PsVecMath.h" + +#include "PxCooking.h" + +#include "GuConvexMeshData.h" +#include "GuBigConvexData2.h" +#include "GuIntersectionRayPlane.h" +#include "GuSerialize.h" + +#include "BigConvexDataBuilder.h" +#include "EdgeList.h" + +#include "ConvexHullBuilder.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +static const PxU32 gSupportVersion = 0; +static const PxU32 gVersion = 0; + +BigConvexDataBuilder::BigConvexDataBuilder(const Gu::ConvexHullData* hull, BigConvexData* gm, const PxVec3* hullVerts) : mHullVerts(hullVerts) +{ + mSVM = gm; + mHull = hull; +} + +BigConvexDataBuilder::~BigConvexDataBuilder() +{ +} + +bool BigConvexDataBuilder::initialize() +{ + mSVM->mData.mSamples = PX_NEW(PxU8)[mSVM->mData.mNbSamples*2u]; + +#if PX_DEBUG +// printf("SVM: %d bytes\n", mNbSamples*sizeof(PxU8)*2); +#endif + + return true; +} + +bool BigConvexDataBuilder::save(PxOutputStream& stream, bool platformMismatch) const +{ + // Export header + if(!WriteHeader('S', 'U', 'P', 'M', gSupportVersion, platformMismatch, stream)) + return false; + + // Save base gaussmap +// if(!GaussMapBuilder::Save(stream, platformMismatch)) return false; + // Export header + if(!WriteHeader('G', 'A', 'U', 'S', gVersion, platformMismatch, stream)) + return false; + + // Export basic info + // stream.StoreDword(mSubdiv); + writeDword(mSVM->mData.mSubdiv, platformMismatch, stream); // PT: could now write Word here + // stream.StoreDword(mNbSamples); + writeDword(mSVM->mData.mNbSamples, platformMismatch, stream); // PT: could now write Word here + + // Save map data + // It's an array of bytes so we don't care about 'PlatformMismatch' + stream.write(mSVM->mData.mSamples, sizeof(PxU8)*mSVM->mData.mNbSamples*2); + + if(!saveValencies(stream, platformMismatch)) + return false; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// compute valencies for each vertex +// we dont compute the edges again here, we have them temporary stored in mHullDataFacesByAllEdges8 structure +bool BigConvexDataBuilder::computeValencies(const ConvexHullBuilder& meshBuilder) +{ + // Create valencies + const PxU32 numVertices = meshBuilder.mHull->mNbHullVertices; + mSVM->mData.mNbVerts = numVertices; + + // Get ram for valencies and adjacent verts + const PxU32 numAlignedVerts = (numVertices+3)&~3; + const PxU32 TotalSize = sizeof(Gu::Valency)*numAlignedVerts + sizeof(PxU8)*meshBuilder.mHull->mNbEdges*2u; + mSVM->mVBuffer = PX_ALLOC(TotalSize, "BigConvexData data"); + mSVM->mData.mValencies = reinterpret_cast(mSVM->mVBuffer); + mSVM->mData.mAdjacentVerts = (reinterpret_cast(mSVM->mVBuffer)) + sizeof(Gu::Valency)*numAlignedVerts; + + PxMemZero(mSVM->mData.mValencies, numVertices*sizeof(Gu::Valency)); + PxU8 vertexMarker[256]; + PxMemZero(vertexMarker,numVertices); + + // Compute valencies + for (PxU32 i = 0; i < meshBuilder.mHull->mNbPolygons; i++) + { + const PxU32 numVerts = meshBuilder.mHullDataPolygons[i].mNbVerts; + const PxU8* Data = meshBuilder.mHullDataVertexData8 + meshBuilder.mHullDataPolygons[i].mVRef8; + for (PxU32 j = 0; j < numVerts; j++) + { + mSVM->mData.mValencies[Data[j]].mCount++; + PX_ASSERT(mSVM->mData.mValencies[Data[j]].mCount != 0xffff); + } + } + + // Create offsets + mSVM->CreateOffsets(); + + // mNbAdjVerts = mOffsets[mNbVerts-1] + mValencies[mNbVerts-1]; + mSVM->mData.mNbAdjVerts = PxU32(mSVM->mData.mValencies[mSVM->mData.mNbVerts - 1].mOffset + mSVM->mData.mValencies[mSVM->mData.mNbVerts - 1].mCount); + PX_ASSERT(mSVM->mData.mNbAdjVerts == PxU32(meshBuilder.mHull->mNbEdges * 2)); + + // Create adjacent vertices + // parse the polygons and its vertices + for (PxU32 i = 0; i < meshBuilder.mHull->mNbPolygons; i++) + { + PxU32 numVerts = meshBuilder.mHullDataPolygons[i].mNbVerts; + const PxU8* Data = meshBuilder.mHullDataVertexData8 + meshBuilder.mHullDataPolygons[i].mVRef8; + for (PxU32 j = 0; j < numVerts; j++) + { + const PxU8 vertexIndex = Data[j]; + PxU8 numAdj = 0; + // if we did not parsed this vertex, traverse to the adjacent face and then + // again to next till we hit back the original polygon + if(vertexMarker[vertexIndex] == 0) + { + PxU8 prevIndex = Data[(j+1)%numVerts]; + mSVM->mData.mAdjacentVerts[mSVM->mData.mValencies[vertexIndex].mOffset++] = prevIndex; + numAdj++; + // now traverse the neighbors + const PxU16 edgeIndex = PxU16(meshBuilder.mEdgeData16[meshBuilder.mHullDataPolygons[i].mVRef8 + j]*2); + PxU8 n0 = meshBuilder.mHullDataFacesByEdges8[edgeIndex]; + PxU8 n1 = meshBuilder.mHullDataFacesByEdges8[edgeIndex + 1]; + + PxU32 neighborPolygon = n0 == i ? n1 : n0; + while (neighborPolygon != i) + { + PxU32 numNeighborVerts = meshBuilder.mHullDataPolygons[neighborPolygon].mNbVerts; + const PxU8* neighborData = meshBuilder.mHullDataVertexData8 + meshBuilder.mHullDataPolygons[neighborPolygon].mVRef8; + PxU32 nextEdgeIndex = 0; + // search in the neighbor face for the tested vertex + for (PxU32 k = 0; k < numNeighborVerts; k++) + { + // search the vertexIndex + if(neighborData[k] == vertexIndex) + { + const PxU8 nextIndex = neighborData[(k+1)%numNeighborVerts]; + // next index already there, pick the previous + if(nextIndex == prevIndex) + { + prevIndex = k == 0 ? neighborData[numNeighborVerts - 1] : neighborData[k-1]; + nextEdgeIndex = k == 0 ? numNeighborVerts - 1 : k-1; + } + else + { + prevIndex = nextIndex; + nextEdgeIndex = k; + } + mSVM->mData.mAdjacentVerts[mSVM->mData.mValencies[vertexIndex].mOffset++] = prevIndex; + numAdj++; + break; + } + } + + // now move to next neighbor + const PxU16 edgeIndex2 = PxU16(meshBuilder.mEdgeData16[(meshBuilder.mHullDataPolygons[neighborPolygon].mVRef8 + nextEdgeIndex)]*2); + n0 = meshBuilder.mHullDataFacesByEdges8[edgeIndex2]; + n1 = meshBuilder.mHullDataFacesByEdges8[edgeIndex2 + 1]; + + neighborPolygon = n0 == neighborPolygon ? n1 : n0; + } + vertexMarker[vertexIndex] = numAdj; + } + } + } + + // Recreate offsets + mSVM->CreateOffsets(); + return true; +} + +////////////////////////////////////////////////////////////////////////// +// compute the min dot product from the verts for given dir +void BigConvexDataBuilder::precomputeSample(const PxVec3& dir, PxU8& startIndex_, float negativeDir) +{ + PxU8 startIndex = startIndex_; + + const PxVec3* verts = mHullVerts; + const Valency* valency = mSVM->mData.mValencies; + const PxU8* adjacentVerts = mSVM->mData.mAdjacentVerts; + + // we have only 256 verts + PxU32 smallBitMap[8] = {0,0,0,0,0,0,0,0}; + + float minimum = negativeDir * verts[startIndex].dot(dir); + PxU32 initialIndex = startIndex; + do + { + initialIndex = startIndex; + const PxU32 numNeighbours = valency[startIndex].mCount; + const PxU32 offset = valency[startIndex].mOffset; + + for (PxU32 a = 0; a < numNeighbours; ++a) + { + const PxU8 neighbourIndex = adjacentVerts[offset + a]; + const float dist = negativeDir * verts[neighbourIndex].dot(dir); + if (dist < minimum) + { + const PxU32 ind = PxU32(neighbourIndex >> 5); + const PxU32 mask = PxU32(1 << (neighbourIndex & 31)); + if ((smallBitMap[ind] & mask) == 0) + { + smallBitMap[ind] |= mask; + minimum = dist; + startIndex = neighbourIndex; + } + } + } + + } while (startIndex != initialIndex); + + startIndex_ = startIndex; +} + +////////////////////////////////////////////////////////////////////////// +// Precompute the min/max vertices for cube directions. +bool BigConvexDataBuilder::precompute(PxU32 subdiv) +{ + mSVM->mData.mSubdiv = Ps::to16(subdiv); + mSVM->mData.mNbSamples = Ps::to16(6 * subdiv*subdiv); + + if (!initialize()) + return false; + + PxU8 startIndex[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + PxU8 startIndex2[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + const float halfSubdiv = float(subdiv - 1) * 0.5f; + for (PxU32 j = 0; j < subdiv; j++) + { + for (PxU32 i = j; i < subdiv; i++) + { + const float iSubDiv = 1.0f - i / halfSubdiv; + const float jSubDiv = 1.0f - j / halfSubdiv; + + PxVec3 tempDir(1.0f, iSubDiv, jSubDiv); + // we need to normalize only once, then we permute the components + // as before for each i,j and j,i face direction + tempDir.normalize(); + + const PxVec3 dirs[12] = { + PxVec3(-tempDir.x, tempDir.y, tempDir.z), + PxVec3(tempDir.x, tempDir.y, tempDir.z), + + PxVec3(tempDir.z, -tempDir.x, tempDir.y), + PxVec3(tempDir.z, tempDir.x, tempDir.y), + + PxVec3(tempDir.y, tempDir.z, -tempDir.x), + PxVec3(tempDir.y, tempDir.z, tempDir.x), + + PxVec3(-tempDir.x, tempDir.z, tempDir.y), + PxVec3(tempDir.x, tempDir.z, tempDir.y), + + PxVec3(tempDir.y, -tempDir.x, tempDir.z), + PxVec3(tempDir.y, tempDir.x, tempDir.z), + + PxVec3(tempDir.z, tempDir.y, -tempDir.x), + PxVec3(tempDir.z, tempDir.y, tempDir.x) + }; + + // compute in each direction + negative/positive dot, we have + // then two start indexes, which are used then for hill climbing + for (PxU32 dStep = 0; dStep < 12; dStep++) + { + precomputeSample(dirs[dStep], startIndex[dStep], 1.0f); + precomputeSample(dirs[dStep], startIndex2[dStep], -1.0f); + } + + // decompose the vector results into face directions + for (PxU32 k = 0; k < 6; k++) + { + const PxU32 ksub = k*subdiv*subdiv; + const PxU32 offset = j + i*subdiv + ksub; + const PxU32 offset2 = i + j*subdiv + ksub; + PX_ASSERT(offset < mSVM->mData.mNbSamples); + PX_ASSERT(offset2 < mSVM->mData.mNbSamples); + + mSVM->mData.mSamples[offset] = startIndex[k]; + mSVM->mData.mSamples[offset + mSVM->mData.mNbSamples] = startIndex2[k]; + + mSVM->mData.mSamples[offset2] = startIndex[k + 6]; + mSVM->mData.mSamples[offset2 + mSVM->mData.mNbSamples] = startIndex2[k + 6]; + } + } + } + return true; +} + +static const PxU32 gValencyVersion = 2; + +////////////////////////////////////////////////////////////////////////// + +bool BigConvexDataBuilder::saveValencies(PxOutputStream& stream, bool platformMismatch) const +{ + // Export header + if(!WriteHeader('V', 'A', 'L', 'E', gValencyVersion, platformMismatch, stream)) + return false; + + writeDword(mSVM->mData.mNbVerts, platformMismatch, stream); + writeDword(mSVM->mData.mNbAdjVerts, platformMismatch, stream); + + { + PxU16* temp = PX_NEW_TEMP(PxU16)[mSVM->mData.mNbVerts]; + for(PxU32 i=0;imData.mNbVerts;i++) + temp[i] = mSVM->mData.mValencies[i].mCount; + + const PxU32 maxIndex = computeMaxIndex(temp, mSVM->mData.mNbVerts); + writeDword(maxIndex, platformMismatch, stream); + StoreIndices(Ps::to16(maxIndex), mSVM->mData.mNbVerts, temp, stream, platformMismatch); + + PX_DELETE_POD(temp); + } + stream.write(mSVM->mData.mAdjacentVerts, mSVM->mData.mNbAdjVerts); + + return true; +} diff --git a/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h new file mode 100644 index 000000000..3317fc2b6 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h @@ -0,0 +1,100 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BIG_CONVEX_DATA_BUILDER_H +#define BIG_CONVEX_DATA_BUILDER_H + +#include "foundation/PxMemory.h" +#include "PsVecMath.h" + +namespace physx +{ + struct HullTriangleData; + class BigConvexData; + class ConvexHullBuilder; + + ////////////////////////////////////////////////////////////////////////// + //! Valencies creation structure + struct ValenciesCreate + { + //! Constructor + ValenciesCreate() { PxMemZero(this, sizeof(*this)); } + + PxU32 nbVerts; //!< Number of vertices + PxU32 nbFaces; //!< Number of faces + const PxU32* dFaces; //!< List of faces (triangle list) + const PxU16* wFaces; //!< List of faces (triangle list) + bool adjacentList; //!< Compute list of adjacent vertices or not + }; + + ////////////////////////////////////////////////////////////////////////// + + class BigConvexDataBuilder : public Ps::UserAllocated + { + public: + BigConvexDataBuilder(const Gu::ConvexHullData* hull, BigConvexData* gm, const PxVec3* hullVerts); + ~BigConvexDataBuilder(); + // Support vertex map + bool precompute(PxU32 subdiv); + + bool initialize(); + + bool save(PxOutputStream& stream, bool platformMismatch) const; + + bool computeValencies(const ConvexHullBuilder& meshBuilder); + //~Support vertex map + + // Valencies + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes valencies and adjacent vertices. + * After the call, get results with the appropriate accessors. + * + * \param vc [in] creation structure + * \return true if success. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool compute(const ValenciesCreate& vc) const; + + bool saveValencies(PxOutputStream& stream, bool platformMismatch) const; + //~Valencies + protected: + PX_FORCE_INLINE void precomputeSample(const PxVec3& dir, PxU8& startIndex, float negativeDir); + + private: + const Gu::ConvexHullData* mHull; + BigConvexData* mSVM; + const PxVec3* mHullVerts; + + }; + +} + +#endif // BIG_CONVEX_DATA_BUILDER_H diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp new file mode 100644 index 000000000..c7c43414e --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp @@ -0,0 +1,755 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "foundation/PxMemory.h" +#include "EdgeList.h" +#include "GuTriangle32.h" +#include "GuConvexMesh.h" +#include "PxCooking.h" +#include "CookingUtils.h" +#include "ConvexHullBuilder.h" +#include "ConvexHullLib.h" +#include "CmRadixSortBuffered.h" +#include "MeshCleaner.h" +#include "PsArray.h" +#include "PsFoundation.h" +#include "PsVecMath.h" + + +// 7: added mHullDataFacesByVertices8 +// 8: added mEdges +static const physx::PxU32 gVersion = 8; + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +#define USE_PRECOMPUTED_HULL_PROJECTION + +////////////////////////////////////////////////////////////////////////// +// default constructor +ConvexHullBuilder::ConvexHullBuilder(Gu::ConvexHullData* hull, const bool buildGRBData) : + mHullDataHullVertices (NULL), + mHullDataPolygons (NULL), + mHullDataVertexData8 (NULL), + mHullDataFacesByEdges8 (NULL), + mHullDataFacesByVertices8 (NULL), + mEdgeData16 (NULL), + mEdges (NULL), + mHull (hull), + mBuildGRBData (buildGRBData) +{ +} + +////////////////////////////////////////////////////////////////////////// +// default destructor +ConvexHullBuilder::~ConvexHullBuilder() +{ + PX_DELETE_POD(mEdgeData16); + PX_DELETE_POD(mEdges); + + PX_DELETE_POD(mHullDataHullVertices); + PX_DELETE_POD(mHullDataPolygons); + PX_DELETE_POD(mHullDataVertexData8); + PX_DELETE_POD(mHullDataFacesByEdges8); + PX_DELETE_POD(mHullDataFacesByVertices8); +} + +////////////////////////////////////////////////////////////////////////// +// initialize the convex hull +// \param nbVerts [in] number of vertices used +// \param verts [in] vertices array +// \param indices [in] indices array +// \param nbPolygons [in] number of polygons +// \param hullPolygons [in] polygons array +// \param doValidation [in] specifies whether we should run the validation code +// \param hullLib [in] if hullLib is provided, we can reuse the hull create data, hulllib is NULL in case of user provided polygons +bool ConvexHullBuilder::init(PxU32 nbVerts, const PxVec3* verts, const PxU32* indices, const PxU32 nbIndices, + const PxU32 nbPolygons, const PxHullPolygon* hullPolygons, bool doValidation, ConvexHullLib* hullLib) +{ + PX_ASSERT(indices); + PX_ASSERT(verts); + PX_ASSERT(hullPolygons); + PX_ASSERT(nbVerts); + PX_ASSERT(nbPolygons); + + mHullDataHullVertices = NULL; + mHullDataPolygons = NULL; + mHullDataVertexData8 = NULL; + mHullDataFacesByEdges8 = NULL; + mHullDataFacesByVertices8 = NULL; + + mEdges = NULL; + mEdgeData16 = NULL; + + mHull->mNbHullVertices = Ps::to8(nbVerts); + // allocate additional vec3 for V4 safe load in VolumeInteration + mHullDataHullVertices = reinterpret_cast(PX_ALLOC(sizeof(PxVec3) * mHull->mNbHullVertices + 1, "PxVec3")); + PxMemCopy(mHullDataHullVertices, verts, mHull->mNbHullVertices*sizeof(PxVec3)); + + // Cleanup + mHull->mNbPolygons = 0; + PX_DELETE_POD(mHullDataVertexData8); + PX_FREE_AND_RESET(mHullDataPolygons); + + if(nbPolygons>255) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder::init: convex hull has more than 255 polygons!"); + return false; + } + + // Precompute hull polygon structures + mHull->mNbPolygons = Ps::to8(nbPolygons); + mHullDataPolygons = reinterpret_cast(PX_ALLOC(sizeof(Gu::HullPolygonData)*mHull->mNbPolygons, "Gu::HullPolygonData")); + + mHullDataVertexData8 = PX_NEW(PxU8)[nbIndices]; + PxU8* dest = mHullDataVertexData8; + for(PxU32 i=0;i=3); // Else something very wrong happened... + mHullDataPolygons[i].mNbVerts = Ps::to8(numVerts); + + for (PxU32 j = 0; j < numVerts; j++) + { + dest[j] = Ps::to8(indices[inPolygon.mIndexBase + j]); + } + + mHullDataPolygons[i].mPlane = PxPlane(inPolygon.mPlane[0],inPolygon.mPlane[1],inPolygon.mPlane[2],inPolygon.mPlane[3]); + + // Next one + dest += numVerts; + } + + if(!calculateVertexMapTable(nbPolygons, (hullLib != NULL) ? false : true)) + return false; + + // moved create edge list here from save, copy. This is a part of the validation process and + // we need to create the edge list anyway + if(!hullLib || !hullLib->createEdgeList(nbIndices, mHullDataVertexData8, &mHullDataFacesByEdges8, &mEdgeData16, &mEdges)) + { + if (!createEdgeList(doValidation, nbIndices)) + return false; + } + else + { + mHull->mNbEdges = PxU16(nbIndices/2); + } + +#ifdef USE_PRECOMPUTED_HULL_PROJECTION + // Loop through polygons + for (PxU32 j = 0; j < nbPolygons; j++) + { + // Precompute hull projection along local polygon normal + PxU32 NbVerts = mHull->mNbHullVertices; + const PxVec3* Verts = mHullDataHullVertices; + Gu::HullPolygonData& polygon = mHullDataPolygons[j]; + PxReal min = PX_MAX_F32; + PxU8 minIndex = 0xff; + for (PxU8 i = 0; i < NbVerts; i++) + { + float dp = (*Verts++).dot(polygon.mPlane.n); + if (dp < min) + { + min = dp; + minIndex = i; + } + } + polygon.mMinIndex = minIndex; + } +#endif + + if(doValidation) + return checkHullPolygons(); + else + return true; +} + +////////////////////////////////////////////////////////////////////////// +// hull polygons check +bool ConvexHullBuilder::checkHullPolygons() const +{ + const PxVec3* hullVerts = mHullDataHullVertices; + const PxU8* vertexData = mHullDataVertexData8; + Gu::HullPolygonData* hullPolygons = mHullDataPolygons; + + // Check hull validity + if(!hullVerts || !hullPolygons) + return false; + + if(mHull->mNbPolygons<4) + return false; + + PxVec3 max(-FLT_MAX,-FLT_MAX,-FLT_MAX); + + PxVec3 hullMax = hullVerts[0]; + PxVec3 hullMin = hullVerts[0]; + + for(PxU32 j=0;jmNbHullVertices;j++) + { + const PxVec3& hullVert = hullVerts[j]; + if(fabsf(hullVert.x) > max.x) + max.x = fabsf(hullVert.x); + + if(fabsf(hullVert.y) > max.y) + max.y = fabsf(hullVert.y); + + if(fabsf(hullVert.z) > max.z) + max.z = fabsf(hullVert.z); + + if (hullVert.x > hullMax.x) + { + hullMax.x = hullVert.x; + } + else if (hullVert.x < hullMin.x) + { + hullMin.x = hullVert.x; + } + + if (hullVert.y > hullMax.y) + { + hullMax.y = hullVert.y; + } + else if (hullVert.y < hullMin.y) + { + hullMin.y = hullVert.y; + } + + if (hullVert.z > hullMax.z) + { + hullMax.z = hullVert.z; + } + else if (hullVert.z < hullMin.z) + { + hullMin.z = hullVert.z; + } + } + + max += PxVec3(0.02f,0.02f,0.02f); + + PxVec3 testVectors[8]; + bool foundPlane[8]; + for (PxU32 i = 0; i < 8; i++) + { + foundPlane[i] = false; + } + + testVectors[0] = PxVec3(max.x,max.y,max.z); + testVectors[1] = PxVec3(max.x,-max.y,-max.z); + testVectors[2] = PxVec3(max.x,max.y,-max.z); + testVectors[3] = PxVec3(max.x,-max.y,max.z); + testVectors[4] = PxVec3(-max.x,max.y,max.z); + testVectors[5] = PxVec3(-max.x,-max.y,max.z); + testVectors[6] = PxVec3(-max.x,max.y,-max.z); + testVectors[7] = PxVec3(-max.x,-max.y,-max.z); + + + // Extra convex hull validity check. This is less aggressive than previous convex decomposer! + // Loop through polygons + for(PxU32 i=0;imNbPolygons;i++) + { + const PxPlane& P = hullPolygons[i].mPlane; + + for (PxU32 k = 0; k < 8; k++) + { + if(!foundPlane[k]) + { + const float d = P.distance(testVectors[k]); + if(d >= 0) + { + foundPlane[k] = true; + } + } + } + + // Test hull vertices against polygon plane + // compute the test epsilon the same way we construct the hull, verts are considered coplanar within this epsilon + const float planeTolerance = 0.02f; + const float testEpsilon = PxMax(planeTolerance * (PxMax(PxAbs(hullMax.x), PxAbs(hullMin.x)) + + PxMax(PxAbs(hullMax.y), PxAbs(hullMin.y)) + + PxMax(PxAbs(hullMax.z), PxAbs(hullMin.z))), planeTolerance); + + for(PxU32 j=0;jmNbHullVertices;j++) + { + // Don't test vertex if it belongs to plane (to prevent numerical issues) + PxU32 nb = hullPolygons[i].mNbVerts; + bool discard=false; + for(PxU32 k=0;k0.0001f) + //if(d>0.02f) + if(d > testEpsilon) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::ConvexMesh::checkHullPolygons: Some hull vertices seems to be too far from hull planes."); + return false; + } + } + } + } + + for (PxU32 i = 0; i < 8; i++) + { + if(!foundPlane[i]) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::ConvexMesh::checkHullPolygons: Hull seems to have opened volume or do (some) faces have reversed winding?"); + return false; + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Computes the center of the hull. It should be inside it ! +* \param center [out] hull center +* \return true if success +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool ConvexHullBuilder::computeGeomCenter(PxVec3& center, PxU32 numFaces, HullTriangleData* faces) const +{ + // Checkings + const PxVec3* PX_RESTRICT hullVerts = mHullDataHullVertices; + if (!mHull->mNbHullVertices || !hullVerts) return false; + + // Use the topological method + float totalArea = 0.0f; + center = PxVec3(0); + for (PxU32 i = 0; i < numFaces; i++) + { + Gu::TriangleT curTri(faces[i].mRef[0], faces[i].mRef[1], faces[i].mRef[2]); + const float area = curTri.area(hullVerts); + PxVec3 curCenter; curTri.center(hullVerts, curCenter); + center += area * curCenter; + totalArea += area; + } + center /= totalArea; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// hull data store +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeDescData)==8); +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeData)==8); +bool ConvexHullBuilder::save(PxOutputStream& stream, bool platformMismatch) const +{ + // Export header + if(!WriteHeader('C', 'L', 'H', 'L', gVersion, platformMismatch, stream)) + return false; + + // Export header + if(!WriteHeader('C', 'V', 'H', 'L', gVersion, platformMismatch, stream)) + return false; + + // Export figures + + //embed grb flag into mNbEdges + PxU16 hasGRBData = PxU16(mBuildGRBData); + hasGRBData = PxU16(hasGRBData << 15); + PX_ASSERT(mHull->mNbEdges <( (1 << 15) - 1)); + const PxU16 nbEdges = PxU16(mHull->mNbEdges | hasGRBData); + writeDword(mHull->mNbHullVertices, platformMismatch, stream); + writeDword(nbEdges, platformMismatch, stream); + writeDword(computeNbPolygons(), platformMismatch, stream); // Use accessor to lazy-build + PxU32 nb=0; + for(PxU32 i=0;imNbPolygons;i++) + nb += mHullDataPolygons[i].mNbVerts; + writeDword(nb, platformMismatch, stream); + + // Export triangles + + writeFloatBuffer(&mHullDataHullVertices->x, PxU32(mHull->mNbHullVertices*3), platformMismatch, stream); + + // Export polygons + // TODO: allow lazy-evaluation + // We can't really store the buffer in one run anymore! + for(PxU32 i=0;imNbPolygons;i++) + { + Gu::HullPolygonData tmpCopy = mHullDataPolygons[i]; + if(platformMismatch) + flipData(tmpCopy); + + stream.write(&tmpCopy, sizeof(Gu::HullPolygonData)); + } + + // PT: why not storeBuffer here? + for(PxU32 i=0;imNbEdges*2)); + stream.write(mHullDataFacesByVertices8, PxU32(mHull->mNbHullVertices*3)); + + if (mBuildGRBData) + writeWordBuffer(mEdges, PxU32(mHull->mNbEdges * 2), platformMismatch, stream); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool ConvexHullBuilder::copy(ConvexHullData& hullData, PxU32& mNb) +{ + // set the numbers + hullData.mNbHullVertices = mHull->mNbHullVertices; + PxU16 hasGRBData = PxU16(mBuildGRBData); + hasGRBData = PxU16(hasGRBData << 15); + PX_ASSERT(mHull->mNbEdges <((1 << 15) - 1)); + hullData.mNbEdges = PxU16(mHull->mNbEdges | hasGRBData);; + hullData.mNbPolygons = Ps::to8(computeNbPolygons()); + PxU32 nb = 0; + for (PxU32 i = 0; i < mHull->mNbPolygons; i++) + nb += mHullDataPolygons[i].mNbVerts; + + mNb = nb; + + PxU32 bytesNeeded = Gu::computeBufferSize(hullData, nb); + + // allocate the memory first. + void* dataMemory = PX_ALLOC(bytesNeeded, "ConvexHullData data"); + + PxU8* address = reinterpret_cast(dataMemory); + + // set data pointers + hullData.mPolygons = reinterpret_cast(address); address += sizeof(Gu::HullPolygonData) * hullData.mNbPolygons; + PxVec3* dataHullVertices = reinterpret_cast(address); address += sizeof(PxVec3) * hullData.mNbHullVertices; + PxU8* dataFacesByEdges8 = reinterpret_cast(address); address += sizeof(PxU8) * hullData.mNbEdges * 2; + PxU8* dataFacesByVertices8 = reinterpret_cast(address); address += sizeof(PxU8) * hullData.mNbHullVertices * 3; + PxU16* dataEdges = reinterpret_cast(address); address += hullData.mNbEdges.isBitSet() ? sizeof(PxU16) *hullData.mNbEdges * 2 : 0; + PxU8* dataVertexData8 = reinterpret_cast(address); address += sizeof(PxU8) * nb; // PT: leave that one last, so that we don't need to serialize "Nb" + + PX_ASSERT(!(size_t(dataHullVertices) % sizeof(PxReal))); + PX_ASSERT(!(size_t(hullData.mPolygons) % sizeof(PxReal))); + PX_ASSERT(size_t(address) <= size_t(dataMemory) + bytesNeeded); + + PX_ASSERT(mHullDataHullVertices); + PX_ASSERT(mHullDataPolygons); + PX_ASSERT(mHullDataVertexData8); + PX_ASSERT(mHullDataFacesByEdges8); + PX_ASSERT(mHullDataFacesByVertices8); + + // copy the data + PxMemCopy(dataHullVertices, &mHullDataHullVertices->x, PxU32(mHull->mNbHullVertices * 3)*sizeof(float)); + PxMemCopy(hullData.mPolygons, mHullDataPolygons , hullData.mNbPolygons*sizeof(Gu::HullPolygonData)); + PxMemCopy(dataVertexData8, mHullDataVertexData8, nb); + PxMemCopy(dataFacesByEdges8,mHullDataFacesByEdges8, PxU32(mHull->mNbEdges * 2)); + if (mBuildGRBData) + PxMemCopy(dataEdges, mEdges, PxU32(mHull->mNbEdges * 2) * sizeof(PxU16)); + PxMemCopy(dataFacesByVertices8, mHullDataFacesByVertices8, PxU32(mHull->mNbHullVertices * 3)); + return true; +} + +////////////////////////////////////////////////////////////////////////// +// calculate vertex map table +bool ConvexHullBuilder::calculateVertexMapTable(PxU32 nbPolygons, bool userPolygons) +{ + mHullDataFacesByVertices8 = PX_NEW(PxU8)[mHull->mNbHullVertices*3u]; + PxU8 vertexMarker[256]; + PxMemSet(vertexMarker, 0, mHull->mNbHullVertices); + + for (PxU32 i = 0; i < nbPolygons; i++) + { + const Gu::HullPolygonData& polygon = mHullDataPolygons[i]; + for (PxU32 k = 0; k < polygon.mNbVerts; ++k) + { + const PxU8 index = mHullDataVertexData8[polygon.mVRef8 + k]; + if (vertexMarker[index] < 3) + { + //Found a polygon + mHullDataFacesByVertices8[index*3 + vertexMarker[index]++] = Ps::to8(i); + } + } + } + + bool noPlaneShift = false; + for (PxU32 i = 0; i < mHull->mNbHullVertices; ++i) + { + if(vertexMarker[i] != 3) + noPlaneShift = true; + } + + if (noPlaneShift) + { + //PCM will use the original shape, which means it will have a huge performance drop + if (!userPolygons) + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder: convex hull does not have vertex-to-face info! Try to use different convex mesh cooking settings."); + else + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder: convex hull does not have vertex-to-face info! Some of the vertices have less than 3 neighbor polygons. The vertex is most likely inside a polygon or on an edge between 2 polygons, please remove those vertices."); + for (PxU32 i = 0; i < mHull->mNbHullVertices; ++i) + { + mHullDataFacesByVertices8[i * 3 + 0] = 0xFF; + mHullDataFacesByVertices8[i * 3 + 1] = 0xFF; + mHullDataFacesByVertices8[i * 3 + 2] = 0xFF; + } + return false; + } + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +// create edge list +bool ConvexHullBuilder::createEdgeList(bool doValidation, PxU32 nbEdges) +{ + // Code below could be greatly simplified if we assume manifold meshes! + + //feodorb: ok, let's assume manifold meshes, since the code before this change + //would fail on non-maniflold meshes anyways + + // We need the adjacency graph for hull polygons, similar to what we have for triangles. + // - sort the polygon edges and walk them in order + // - each edge should appear exactly twice since a convex is a manifold mesh without boundary edges + // - the polygon index is implicit when we walk the sorted list => get the 2 polygons back and update adjacency graph + // + // Two possible structures: + // - polygon to edges: needed for local search (actually: polygon to polygons) + // - edge to polygons: needed to compute edge normals on-the-fly + + // Below is largely copied from the edge-list code + + // Polygon to edges: + // + // We're dealing with convex polygons made of N vertices, defining N edges. For each edge we want the edge in + // an edge array. + // + // Edges to polygon: + // + // For each edge in the array, we want two polygon indices - ie an edge. + + // 0) Compute the total size needed for "polygon to edges" + const PxU32 nbPolygons = mHull->mNbPolygons; + PxU32 nbEdgesUnshared = nbEdges; + + // in a manifold mesh, each edge is repeated exactly twice as it shares exactly 2 faces + if (nbEdgesUnshared % 2 != 0) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Cooking::cookConvexMesh: non-manifold mesh cannot be used, invalid mesh!"); + return false; + } + + // 1) Get some bytes: I need one EdgesRefs for each face, and some temp buffers + + // Face indices by edge indices. First face is the one where the edge is ordered from tail to head. + PX_DELETE_POD(mHullDataFacesByEdges8); + mHullDataFacesByEdges8 = PX_NEW(PxU8)[nbEdgesUnshared]; + + PxU32* tempBuffer = PX_NEW_TEMP(PxU32)[nbEdgesUnshared*8]; // Temp storage + PxU32* bufferAdd = tempBuffer; + PxU32* PX_RESTRICT vRefs0 = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* PX_RESTRICT vRefs1 = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* polyIndex = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* vertexIndex = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* polyIndex2 = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* vertexIndex2 = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* edgeIndex = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* edgeData = tempBuffer; tempBuffer += nbEdgesUnshared; + + // TODO avoroshilov: use the same "tempBuffer" + bool* flippedVRefs = PX_NEW_TEMP(bool)[nbEdgesUnshared]; // Temp storage + + PxU32* run0 = vRefs0; + PxU32* run1 = vRefs1; + PxU32* run2 = polyIndex; + PxU32* run3 = vertexIndex; + bool* run4 = flippedVRefs; + + // 2) Create a full redundant list of edges + PxU32 edgeCounter = 0; + for(PxU32 i=0;ivRef1; + + if (flipped) + physx::shdfnd::swap(vRef0, vRef1); + + *run0++ = vRef0; + *run1++ = vRef1; + *run2++ = i; + *run3++ = j; + *run4++ = flipped; + edgeData[edgeCounter] = edgeCounter; + edgeCounter++; + } + } + PX_ASSERT(PxU32(run0-vRefs0)==nbEdgesUnshared); + PX_ASSERT(PxU32(run1-vRefs1)==nbEdgesUnshared); + + // 3) Sort the list according to both keys (VRefs0 and VRefs1) + Cm::RadixSortBuffered sorter; + const PxU32* PX_RESTRICT sorted = sorter.Sort(vRefs1, nbEdgesUnshared,Cm::RADIX_UNSIGNED).Sort(vRefs0, nbEdgesUnshared,Cm::RADIX_UNSIGNED).GetRanks(); + + PX_DELETE_POD(mEdges); + // Edges by their tail and head VRefs. NbEdgesUnshared == nbEdges * 2 + // mEdges[edgeIdx*2 + 0] = tailVref, mEdges[edgeIdx*2 + 1] = headVref + // Tails and heads should be consistent with face refs, so that the edge is given in the order of + // his first face and opposite to the order of his second face + mEdges = PX_NEW(PxU16)[nbEdgesUnshared]; + + PX_DELETE_POD(mEdgeData16); + // Face to edge mapping + mEdgeData16 = PX_NEW(PxU16)[nbEdgesUnshared]; + + // TODO avoroshilov: remove this comment + //mHull->mNbEdges = Ps::to16(nbEdgesUnshared / 2); // #non-redundant edges + + mHull->mNbEdges = 0; // #non-redundant edges + + // 4) Loop through all possible edges + // - clean edges list by removing redundant edges + // - create EdgesRef list + // mNbFaces = nbFaces; + + // TODO avoroshilov: + PxU32 numFacesPerEdgeVerificationCounter = 0; + + PxU16* edgeVertOutput = mEdges; + + PxU32 previousRef0 = PX_INVALID_U32; + PxU32 previousRef1 = PX_INVALID_U32; + PxU32 previousPolyId = PX_INVALID_U32; + + PxU16 nbHullEdges = 0; + for (PxU32 i = 0; i < nbEdgesUnshared; i++) + { + const PxU32 sortedIndex = sorted[i]; // Between 0 and Nb + const PxU32 polyID = polyIndex[sortedIndex]; // Poly index + const PxU32 vertexID = vertexIndex[sortedIndex]; // Poly index + PxU32 sortedRef0 = vRefs0[sortedIndex]; // (SortedRef0, SortedRef1) is the sorted edge + PxU32 sortedRef1 = vRefs1[sortedIndex]; + bool flipped = flippedVRefs[sortedIndex]; + + if (sortedRef0 != previousRef0 || sortedRef1 != previousRef1) + { + // TODO avoroshilov: remove this? + if (i != 0 && numFacesPerEdgeVerificationCounter != 1) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Cooking::cookConvexMesh: non-manifold mesh cannot be used, invalid mesh!"); + return false; + } + numFacesPerEdgeVerificationCounter = 0; + + // ### TODO: change this in edge list as well + previousRef0 = sortedRef0; + previousRef1 = sortedRef1; + previousPolyId = polyID; + + //feodorb:restore the original order of VRefs (tail and head) + if (flipped) + physx::shdfnd::swap(sortedRef0, sortedRef1); + + *edgeVertOutput++ = Ps::to16(sortedRef0); + *edgeVertOutput++ = Ps::to16(sortedRef1); + + nbHullEdges++; + } + else + { + mHullDataFacesByEdges8[(nbHullEdges - 1) * 2] = Ps::to8(previousPolyId); + mHullDataFacesByEdges8[(nbHullEdges - 1) * 2 + 1] = Ps::to8(polyID); + + ++numFacesPerEdgeVerificationCounter; + } + + mEdgeData16[mHullDataPolygons[polyID].mVRef8 + vertexID] = Ps::to16(i / 2); + + // Create mEdgesRef on the fly + + polyIndex2[i] = polyID; + vertexIndex2[i] = vertexID; + edgeIndex[i] = PxU32(nbHullEdges - 1); + } + + mHull->mNbEdges = nbHullEdges; + + ////////////////////// + + // 2) Get some bytes: one Pair structure / edge + // create this structure only for validation purpose + // 3) Create Counters, ie compute the #faces sharing each edge + if(doValidation) + { + // + sorted = sorter.Sort(vertexIndex2, nbEdgesUnshared, Cm::RADIX_UNSIGNED).Sort(polyIndex2, nbEdgesUnshared, Cm::RADIX_UNSIGNED).GetRanks(); + + for (PxU32 i = 0; i < nbEdgesUnshared; i++) edgeData[i] = edgeIndex[sorted[i]]; + + Gu::EdgeDescData* edgeToTriangles = PX_NEW(Gu::EdgeDescData)[PxU16(mHull->mNbEdges)]; + PxMemZero(edgeToTriangles, sizeof(Gu::EdgeDescData)*mHull->mNbEdges); + + PxU32* data = edgeData; + for(PxU32 i=0;imNbEdges; i++) + { + if (edgeToTriangles[i].Count != 2) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Cooking::cookConvexMesh: non-manifold mesh cannot be used, invalid mesh!"); + return false; + } + } + PX_DELETE_POD(edgeToTriangles); + } + + // ### free temp ram + PX_DELETE_POD(bufferAdd); + + // TODO avoroshilov: use the same "tempBuffer" + PX_DELETE_POD(flippedVRefs); + + return true; +} + diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h new file mode 100644 index 000000000..e68f5ed0f --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONVEXHULLBUILDER_H +#define PX_CONVEXHULLBUILDER_H + +#include "GuConvexMeshData.h" +#include "PsUserAllocated.h" +#include "PxCooking.h" + +namespace physx +{ + struct PxHullPolygon; + class ConvexHullLib; + + namespace Gu + { + struct EdgeDescData; + struct ConvexHullData; + } // namespace Gu + + struct HullTriangleData + { + PxU32 mRef[3]; + }; + + class ConvexHullBuilder : public Ps::UserAllocated + { + public: + ConvexHullBuilder(Gu::ConvexHullData* hull, const bool buildGRBData); + ~ConvexHullBuilder(); + + bool init(PxU32 nbVerts, const PxVec3* verts, const PxU32* indices, const PxU32 nbIndices, const PxU32 nbPolygons, + const PxHullPolygon* hullPolygons, bool doValidation = true, ConvexHullLib* hullLib = NULL); + + bool save(PxOutputStream& stream, bool platformMismatch) const; + bool copy(Gu::ConvexHullData& hullData, PxU32& nb); + + bool createEdgeList(bool doValidation, PxU32 nbEdges); + bool checkHullPolygons() const; + + bool calculateVertexMapTable(PxU32 nbPolygons, bool userPolygons = false); + + PX_INLINE PxU32 computeNbPolygons() const + { + PX_ASSERT(mHull->mNbPolygons); + return mHull->mNbPolygons; + } + + PxVec3* mHullDataHullVertices; + Gu::HullPolygonData* mHullDataPolygons; + PxU8* mHullDataVertexData8; + PxU8* mHullDataFacesByEdges8; + PxU8* mHullDataFacesByVertices8; + + PxU16* mEdgeData16; //!< Edge indices indexed by hull polygons + PxU16* mEdges; //!< Edge to vertex mapping + + Gu::ConvexHullData* mHull; + bool mBuildGRBData; + + protected: + bool computeGeomCenter(PxVec3& , PxU32 numFaces, HullTriangleData* faces) const; + }; +} + +#endif // PX_CONVEXHULLBUILDER_H + diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp new file mode 100644 index 000000000..f5b1033f3 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp @@ -0,0 +1,352 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ConvexHullLib.h" +#include "Quantizer.h" +#include "PsAllocator.h" +#include "foundation/PxBounds3.h" +#include "foundation/PxMemory.h" + +using namespace physx; + +namespace local +{ + ////////////////////////////////////////////////////////////////////////// + // constants + static const float DISTANCE_EPSILON = 0.000001f; // close enough to consider two floating point numbers to be 'the same'. + static const float NORMAL_DISTANCE_EPSILON = 0.001f; // close enough to consider two floating point numbers to be 'the same' in normalized points cloud. + static const float RESIZE_VALUE = 0.01f; // if the provided points AABB is very thin resize it to this size + + ////////////////////////////////////////////////////////////////////////// + // checks if points form a valid AABB cube, if not construct a default CUBE + static bool checkPointsAABBValidity(PxU32 numPoints, const PxVec3* points, PxU32 stride , float distanceEpsilon, + float resizeValue, PxVec3& center, PxVec3& scale, PxU32& vcount, PxVec3* vertices, bool fCheck = false) + { + const char* vtx = reinterpret_cast (points); + PxBounds3 bounds; + bounds.setEmpty(); + + // get the bounding box + for (PxU32 i = 0; i < numPoints; i++) + { + const PxVec3& p = *reinterpret_cast (vtx); + vtx += stride; + + bounds.include(p); + } + + PxVec3 dim = bounds.getDimensions(); + center = bounds.getCenter(); + + // special case, the AABB is very thin or user provided us with only input 2 points + // we construct an AABB cube and return it + if ( dim.x < distanceEpsilon || dim.y < distanceEpsilon || dim.z < distanceEpsilon || numPoints < 3 ) + { + float len = FLT_MAX; + + // pick the shortest size bigger than the distance epsilon + if ( dim.x > distanceEpsilon && dim.x < len ) + len = dim.x; + if ( dim.y > distanceEpsilon && dim.y < len ) + len = dim.y; + if ( dim.z > distanceEpsilon && dim.z < len ) + len = dim.z; + + // if the AABB is small in all dimensions, resize it + if ( len == FLT_MAX ) + { + dim = PxVec3(resizeValue); + } + // if one edge is small, set to 1/5th the shortest non-zero edge. + else + { + if ( dim.x < distanceEpsilon ) + dim.x = len * 0.05f; + else + dim.x *= 0.5f; + if ( dim.y < distanceEpsilon ) + dim.y = len * 0.05f; + else + dim.y *= 0.5f; + if ( dim.z < distanceEpsilon ) + dim.z = len * 0.05f; + else + dim.z *= 0.5f; + } + + // construct the AABB + const PxVec3 extPos = center + dim; + const PxVec3 extNeg = center - dim; + + if(fCheck) + vcount = 0; + + vertices[vcount++] = extNeg; + vertices[vcount++] = PxVec3(extPos.x,extNeg.y,extNeg.z); + vertices[vcount++] = PxVec3(extPos.x,extPos.y,extNeg.z); + vertices[vcount++] = PxVec3(extNeg.x,extPos.y,extNeg.z); + vertices[vcount++] = PxVec3(extNeg.x,extNeg.y,extPos.z); + vertices[vcount++] = PxVec3(extPos.x,extNeg.y,extPos.z); + vertices[vcount++] = extPos; + vertices[vcount++] = PxVec3(extNeg.x,extPos.y,extPos.z); + return true; // return cube + } + else + { + scale = dim; + } + return false; + } + +} + + +////////////////////////////////////////////////////////////////////////// +// shift vertices around origin and normalize point cloud, remove duplicates! +bool ConvexHullLib::shiftAndcleanupVertices(PxU32 svcount, const PxVec3* svertices, PxU32 stride, + PxU32& vcount, PxVec3* vertices, PxVec3& scale, PxVec3& center) +{ + mShiftedVerts = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxVec3)*svcount, "PxVec3")); + const char* vtx = reinterpret_cast (svertices); + PxBounds3 bounds; + bounds.setEmpty(); + + // get the bounding box + for (PxU32 i = 0; i < svcount; i++) + { + const PxVec3& p = *reinterpret_cast (vtx); + vtx += stride; + + bounds.include(p); + } + mOriginShift = bounds.getCenter(); + vtx = reinterpret_cast (svertices); + for (PxU32 i = 0; i < svcount; i++) + { + const PxVec3& p = *reinterpret_cast (vtx); + vtx += stride; + + mShiftedVerts[i] = p - mOriginShift; + } + return cleanupVertices(svcount, mShiftedVerts, sizeof(PxVec3), vcount, vertices, scale, center); +} + +////////////////////////////////////////////////////////////////////////// +// Shift verts/planes in the desc back +void ConvexHullLib::shiftConvexMeshDesc(PxConvexMeshDesc& desc) +{ + PX_ASSERT(mConvexMeshDesc.flags & PxConvexFlag::eSHIFT_VERTICES); + + PxVec3* points = reinterpret_cast(const_cast(desc.points.data)); + for(PxU32 i = 0; i < desc.points.count; i++) + { + points[i] = points[i] + mOriginShift; + } + + PxHullPolygon* polygons = reinterpret_cast(const_cast(desc.polygons.data)); + for(PxU32 i = 0; i < desc.polygons.count; i++) + { + polygons[i].mPlane[3] -= PxVec3(polygons[i].mPlane[0], polygons[i].mPlane[1], polygons[i].mPlane[2]).dot(mOriginShift); + } +} + +////////////////////////////////////////////////////////////////////////// +// normalize point cloud, remove duplicates! +bool ConvexHullLib::cleanupVertices(PxU32 svcount, const PxVec3* svertices, PxU32 stride, + PxU32& vcount, PxVec3* vertices, PxVec3& scale, PxVec3& center) +{ + if (svcount == 0) + return false; + + const PxVec3* verticesToClean = svertices; + PxU32 numVerticesToClean = svcount; + Quantizer* quantizer = NULL; + + // if quantization is enabled, parse the input vertices and produce new qantized vertices, + // that will be then cleaned the same way + if (mConvexMeshDesc.flags & PxConvexFlag::eQUANTIZE_INPUT) + { + quantizer = createQuantizer(); + PxU32 vertsOutCount; + const PxVec3* vertsOut = quantizer->kmeansQuantize3D(svcount, svertices, stride,true, mConvexMeshDesc.quantizedCount, vertsOutCount); + + if (vertsOut) + { + numVerticesToClean = vertsOutCount; + verticesToClean = vertsOut; + } + } + + const float distanceEpsilon = local::DISTANCE_EPSILON * mCookingParams.scale.length; + const float resizeValue = local::RESIZE_VALUE * mCookingParams.scale.length; + const float normalEpsilon = local::NORMAL_DISTANCE_EPSILON; // used to determine if 2 points are the same + + vcount = 0; + PxVec3 recip; + + scale = PxVec3(1.0f); + + // check for the AABB from points, if its very tiny return a resized CUBE + if (local::checkPointsAABBValidity(numVerticesToClean, verticesToClean, stride, distanceEpsilon, resizeValue, center, scale, vcount, vertices, false)) + { + if (quantizer) + quantizer->release(); + return true; + } + + recip[0] = 1 / scale[0]; + recip[1] = 1 / scale[1]; + recip[2] = 1 / scale[2]; + + center = center.multiply(recip); + + // normalize the point cloud + const char * vtx = reinterpret_cast (verticesToClean); + for (PxU32 i = 0; i(vtx); + vtx+=stride; + + PxVec3 normalizedP = p.multiply(recip); // normalize + + PxU32 j; + + // parse the already stored vertices and check the distance + for (j=0; j dist2 ) + { + v = normalizedP; + } + break; + } + } + + // we dont have that vertex in the output, add it + if ( j == vcount ) + { + vertices[vcount] = normalizedP; + vcount++; + } + } + + // scale the verts back + for (PxU32 i = 0; i < vcount; i++) + { + vertices[i] = vertices[i].multiply(scale); + } + + // ok..now make sure we didn't prune so many vertices it is now invalid. + // note, that the output vertices are again scaled, we need to scale them back then + local::checkPointsAABBValidity(vcount, vertices, sizeof(PxVec3), distanceEpsilon, resizeValue, center, scale, vcount, vertices, true); + + if (quantizer) + quantizer->release(); + return true; +} + +void ConvexHullLib::swapLargestFace(PxConvexMeshDesc& desc) +{ + const PxHullPolygon* polygons = reinterpret_cast(desc.polygons.data); + PxHullPolygon* polygonsOut = const_cast(polygons); + + PxU32 largestFace = 0; + for (PxU32 i = 1; i < desc.polygons.count; i++) + { + if(polygons[largestFace].mNbVerts < polygons[i].mNbVerts) + largestFace = i; + } + + // early exit if no swap needs to be done + if(largestFace == 0) + return; + + const PxU32* indices = reinterpret_cast(desc.indices.data); + mSwappedIndices = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxU32)*desc.indices.count, "PxU32")); + + PxHullPolygon replacedPolygon = polygons[0]; + PxHullPolygon largestPolygon = polygons[largestFace]; + polygonsOut[0] = polygons[largestFace]; + polygonsOut[largestFace] = replacedPolygon; + + // relocate indices + PxU16 indexBase = 0; + for (PxU32 i = 0; i < desc.polygons.count; i++) + { + if(i == 0) + { + PxMemCopy(mSwappedIndices, &indices[largestPolygon.mIndexBase],sizeof(PxU32)*largestPolygon.mNbVerts); + polygonsOut[0].mIndexBase = indexBase; + indexBase += largestPolygon.mNbVerts; + } + else + { + if(i == largestFace) + { + PxMemCopy(&mSwappedIndices[indexBase], &indices[replacedPolygon.mIndexBase], sizeof(PxU32)*replacedPolygon.mNbVerts); + polygonsOut[i].mIndexBase = indexBase; + indexBase += replacedPolygon.mNbVerts; + } + else + { + PxMemCopy(&mSwappedIndices[indexBase], &indices[polygons[i].mIndexBase], sizeof(PxU32)*polygons[i].mNbVerts); + polygonsOut[i].mIndexBase = indexBase; + indexBase += polygons[i].mNbVerts; + } + } + } + + PX_ASSERT(indexBase == desc.indices.count); + + desc.indices.data = mSwappedIndices; +} + +ConvexHullLib::~ConvexHullLib() +{ + if (mSwappedIndices) + PX_FREE(mSwappedIndices); + + if(mShiftedVerts) + PX_FREE(mShiftedVerts); +} diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h new file mode 100644 index 000000000..4d250f7c2 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONVEXHULLLIB_H +#define PX_CONVEXHULLLIB_H + +#include "PxConvexMeshDesc.h" +#include "PxCooking.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + ////////////////////////////////////////////////////////////////////////// + // base class for the convex hull libraries - inflation based and quickhull + class ConvexHullLib + { + PX_NOCOPY(ConvexHullLib) + public: + // functions + ConvexHullLib(const PxConvexMeshDesc& desc, const PxCookingParams& params) + : mConvexMeshDesc(desc), mCookingParams(params), mSwappedIndices(NULL), + mShiftedVerts(NULL) + { + } + + virtual ~ConvexHullLib(); + + // computes the convex hull from provided points + virtual PxConvexMeshCookingResult::Enum createConvexHull() = 0; + + // fills the PxConvexMeshDesc with computed hull data + virtual void fillConvexMeshDesc(PxConvexMeshDesc& desc) = 0; + + // compute the edge list information if possible + virtual bool createEdgeList(const PxU32 nbIndices, const PxU8* indices, PxU8** hullDataFacesByEdges8, PxU16** edgeData16, PxU16** edges) = 0; + + static const PxU32 gpuMaxVertsPerFace = 32; + + protected: + + // clean input vertices from duplicates, normalize etc. + bool cleanupVertices(PxU32 svcount, // input vertex count + const PxVec3* svertices, // vertices + PxU32 stride, // stride + PxU32& vcount, // output number of vertices + PxVec3* vertices, // location to store the results. + PxVec3& scale, // scale + PxVec3& center); // center + + // shift vertices around origin and clean input vertices from duplicates, normalize etc. + bool shiftAndcleanupVertices(PxU32 svcount, // input vertex count + const PxVec3* svertices, // vertices + PxU32 stride, // stride + PxU32& vcount, // output number of vertices + PxVec3* vertices, // location to store the results. + PxVec3& scale, // scale + PxVec3& center); // center + + void swapLargestFace(PxConvexMeshDesc& desc); + + void shiftConvexMeshDesc(PxConvexMeshDesc& desc); + + protected: + const PxConvexMeshDesc& mConvexMeshDesc; + const PxCookingParams& mCookingParams; + PxU32* mSwappedIndices; + PxVec3 mOriginShift; + PxVec3* mShiftedVerts; + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp new file mode 100644 index 000000000..89309da59 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp @@ -0,0 +1,925 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxBounds3.h" +#include "foundation/PxMathUtils.h" + +#include "ConvexHullUtils.h" +#include "VolumeIntegration.h" +#include "PsUtilities.h" +#include "PsVecMath.h" +#include "GuBox.h" +#include "GuConvexMeshData.h" + +using namespace physx; +using namespace Ps::aos; + +namespace local +{ + static const float MIN_ADJACENT_ANGLE = 3.0f; // in degrees - result wont have two adjacent facets within this angle of each other. + static const float MAXDOT_MINANG = cosf(Ps::degToRad(MIN_ADJACENT_ANGLE)); // adjacent angle for dot product tests + + ////////////////////////////////////////////////////////////////////////// + // helper class for ConvexHullCrop + class VertFlag + { + public: + PxU8 planetest; + PxU8 undermap; + PxU8 overmap; + }; + + //////////////////////////////////////////////////////////////////////////| + // helper class for ConvexHullCrop + class EdgeFlag + { + public: + PxI16 undermap; + }; + + //////////////////////////////////////////////////////////////////////////| + // helper class for ConvexHullCrop + class Coplanar + { + public: + PxU16 ea; + PxU8 v0; + PxU8 v1; + }; + + ////////////////////////////////////////////////////////////////////////// + // plane test + enum PlaneTestResult + { + eCOPLANAR = 0, + eUNDER = 1 << 0, + eOVER = 1 << 1 + }; + + ////////////////////////////////////////////////////////////////////////// + // test where vertex lies in respect to the plane + static PlaneTestResult planeTest(const PxPlane& p, const PxVec3& v, float epsilon) + { + const float a = v.dot(p.n) + p.d; + PlaneTestResult flag = (a > epsilon) ? eOVER : ((a < -epsilon) ? eUNDER : eCOPLANAR); + return flag; + } + + // computes the OBB for this set of points relative to this transform matrix. SIMD version + void computeOBBSIMD(PxU32 vcount, const Vec4V* points, Vec4V& sides, const QuatV& rot, Vec4V& trans) + { + PX_ASSERT(vcount); + + Vec4V minV = V4Load(FLT_MAX); + Vec4V maxV = V4Load(FLT_MIN); + for (PxU32 i = 0; i < vcount; i++) + { + const Vec4V& vertexV = points[i]; + const Vec4V t = V4Sub(vertexV, trans); + const Vec4V v = Vec4V_From_Vec3V(QuatRotateInv(rot, Vec3V_From_Vec4V(t))); + + minV = V4Min(minV, v); + maxV = V4Max(maxV, v); + } + + sides = V4Sub(maxV, minV); + + Mat33V tmpMat; + QuatGetMat33V(rot, tmpMat.col0, tmpMat.col1, tmpMat.col2); + const FloatV coe = FLoad(0.5f); + + const Vec4V deltaVec = V4Sub(maxV, V4Scale(sides, coe)); + + const Vec4V t0 = V4Scale(Vec4V_From_Vec3V(tmpMat.col0), V4GetX(deltaVec)); + trans = V4Add(trans, t0); + + const Vec4V t1 = V4Scale(Vec4V_From_Vec3V(tmpMat.col1), V4GetY(deltaVec)); + trans = V4Add(trans, t1); + + const Vec4V t2 = V4Scale(Vec4V_From_Vec3V(tmpMat.col2), V4GetZ(deltaVec)); + trans = V4Add(trans, t2); + } +} + +////////////////////////////////////////////////////////////////////////// +// construct the base cube from given min/max +ConvexHull::ConvexHull(const PxVec3& bmin, const PxVec3& bmax, const Ps::Array& inPlanes) +: mInputPlanes(inPlanes) +{ + // min max verts of the cube - 8 verts + mVertices.pushBack(PxVec3(bmin.x, bmin.y, bmin.z)); // --- + mVertices.pushBack(PxVec3(bmin.x, bmin.y, bmax.z)); // --+ + mVertices.pushBack(PxVec3(bmin.x, bmax.y, bmin.z)); // -+- + mVertices.pushBack(PxVec3(bmin.x, bmax.y, bmax.z)); // -++ + mVertices.pushBack(PxVec3(bmax.x, bmin.y, bmin.z)); // +-- + mVertices.pushBack(PxVec3(bmax.x, bmin.y, bmax.z)); // +-+ + mVertices.pushBack(PxVec3(bmax.x, bmax.y, bmin.z)); // ++- + mVertices.pushBack(PxVec3(bmax.x, bmax.y, bmax.z)); // +++ + + // cube planes - 6 planes + mFacets.pushBack(PxPlane(PxVec3(-1.f, 0, 0), bmin.x)); // 0,1,3,2 + mFacets.pushBack(PxPlane(PxVec3(1.f, 0, 0), -bmax.x)); // 6,7,5,4 + mFacets.pushBack(PxPlane(PxVec3(0, -1.f, 0), bmin.y)); // 0,4,5,1 + mFacets.pushBack(PxPlane(PxVec3(0, 1.f, 0), -bmax.y)); // 3,7,6,2 + mFacets.pushBack(PxPlane(PxVec3(0, 0, -1.f), bmin.z)); // 0,2,6,4 + mFacets.pushBack(PxPlane(PxVec3(0, 0, 1.f), -bmax.z)); // 1,5,7,3 + + // cube edges - 24 edges + mEdges.pushBack(HalfEdge(11, 0, 0)); + mEdges.pushBack(HalfEdge(23, 1, 0)); + mEdges.pushBack(HalfEdge(15, 3, 0)); + mEdges.pushBack(HalfEdge(16, 2, 0)); + + mEdges.pushBack(HalfEdge(13, 6, 1)); + mEdges.pushBack(HalfEdge(21, 7, 1)); + mEdges.pushBack(HalfEdge(9, 5, 1)); + mEdges.pushBack(HalfEdge(18, 4, 1)); + + mEdges.pushBack(HalfEdge(19, 0, 2)); + mEdges.pushBack(HalfEdge(6, 4, 2)); + mEdges.pushBack(HalfEdge(20, 5, 2)); + mEdges.pushBack(HalfEdge(0, 1, 2)); + + mEdges.pushBack(HalfEdge(22, 3, 3)); + mEdges.pushBack(HalfEdge(4, 7, 3)); + mEdges.pushBack(HalfEdge(17, 6, 3)); + mEdges.pushBack(HalfEdge(2, 2, 3)); + + mEdges.pushBack(HalfEdge(3, 0, 4)); + mEdges.pushBack(HalfEdge(14, 2, 4)); + mEdges.pushBack(HalfEdge(7, 6, 4)); + mEdges.pushBack(HalfEdge(8, 4, 4)); + + mEdges.pushBack(HalfEdge(10, 1, 5)); + mEdges.pushBack(HalfEdge(5, 5, 5)); + mEdges.pushBack(HalfEdge(12, 7, 5)); + mEdges.pushBack(HalfEdge(1, 3, 5)); +} + +////////////////////////////////////////////////////////////////////////// +// create the initial convex hull from given OBB +ConvexHull::ConvexHull(const PxVec3& extent, const PxTransform& transform, const Ps::Array& inPlanes) + : mInputPlanes(inPlanes) +{ + // get the OBB corner points + PxVec3 extentPoints[8]; + PxMat33 rot(transform.q); + Gu::computeOBBPoints(extentPoints, transform.p, extent, rot.column0, rot.column1, rot.column2); + + mVertices.pushBack(PxVec3(extentPoints[0].x, extentPoints[0].y, extentPoints[0].z)); // --- + mVertices.pushBack(PxVec3(extentPoints[4].x, extentPoints[4].y, extentPoints[4].z)); // --+ + mVertices.pushBack(PxVec3(extentPoints[3].x, extentPoints[3].y, extentPoints[3].z)); // -+- + mVertices.pushBack(PxVec3(extentPoints[7].x, extentPoints[7].y, extentPoints[7].z)); // -++ + mVertices.pushBack(PxVec3(extentPoints[1].x, extentPoints[1].y, extentPoints[1].z)); // +-- + mVertices.pushBack(PxVec3(extentPoints[5].x, extentPoints[5].y, extentPoints[5].z)); // +-+ + mVertices.pushBack(PxVec3(extentPoints[2].x, extentPoints[2].y, extentPoints[2].z)); // ++- + mVertices.pushBack(PxVec3(extentPoints[6].x, extentPoints[6].y, extentPoints[6].z)); // +++ + + // cube planes - 6 planes + PxPlane plane0(extentPoints[0], extentPoints[4], extentPoints[7]); // 0,1,3,2 + mFacets.pushBack(PxPlane(plane0.n, plane0.d)); + + PxPlane plane1(extentPoints[2], extentPoints[6], extentPoints[5]); // 6,7,5,4 + mFacets.pushBack(PxPlane(plane1.n, plane1.d)); + + PxPlane plane2(extentPoints[0], extentPoints[1], extentPoints[5]); // 0,4,5,1 + mFacets.pushBack(PxPlane(plane2.n, plane2.d)); + + PxPlane plane3(extentPoints[7], extentPoints[6], extentPoints[2]); // 3,7,6,2 + mFacets.pushBack(PxPlane(plane3.n, plane3.d)); + + PxPlane plane4(extentPoints[0], extentPoints[3], extentPoints[2]); // 0,2,6,4 + mFacets.pushBack(PxPlane(plane4.n, plane4.d)); + + PxPlane plane5(extentPoints[4], extentPoints[5], extentPoints[6]); // 1,5,7,3 + mFacets.pushBack(PxPlane(plane5.n, plane5.d)); + + // cube edges - 24 edges + mEdges.pushBack(HalfEdge(11, 0, 0)); + mEdges.pushBack(HalfEdge(23, 1, 0)); + mEdges.pushBack(HalfEdge(15, 3, 0)); + mEdges.pushBack(HalfEdge(16, 2, 0)); + + mEdges.pushBack(HalfEdge(13, 6, 1)); + mEdges.pushBack(HalfEdge(21, 7, 1)); + mEdges.pushBack(HalfEdge(9, 5, 1)); + mEdges.pushBack(HalfEdge(18, 4, 1)); + + mEdges.pushBack(HalfEdge(19, 0, 2)); + mEdges.pushBack(HalfEdge(6, 4, 2)); + mEdges.pushBack(HalfEdge(20, 5, 2)); + mEdges.pushBack(HalfEdge(0, 1, 2)); + + mEdges.pushBack(HalfEdge(22, 3, 3)); + mEdges.pushBack(HalfEdge(4, 7, 3)); + mEdges.pushBack(HalfEdge(17, 6, 3)); + mEdges.pushBack(HalfEdge(2, 2, 3)); + + mEdges.pushBack(HalfEdge(3, 0, 4)); + mEdges.pushBack(HalfEdge(14, 2, 4)); + mEdges.pushBack(HalfEdge(7, 6, 4)); + mEdges.pushBack(HalfEdge(8, 4, 4)); + + mEdges.pushBack(HalfEdge(10, 1, 5)); + mEdges.pushBack(HalfEdge(5, 5, 5)); + mEdges.pushBack(HalfEdge(12, 7, 5)); + mEdges.pushBack(HalfEdge(1, 3, 5)); +} + +////////////////////////////////////////////////////////////////////////// +// finds the candidate plane, returns -1 otherwise +PxI32 ConvexHull::findCandidatePlane(float planeTestEpsilon, float epsilon) const +{ + PxI32 p = -1; + float md = 0.0f; + PxU32 i, j; + for (i = 0; i < mInputPlanes.size(); i++) + { + float d = 0.0f; + float dmax = 0.0f; + float dmin = 0.0f; + for (j = 0; j < mVertices.size(); j++) + { + dmax = PxMax(dmax, mVertices[j].dot(mInputPlanes[i].n) + mInputPlanes[i].d); + dmin = PxMin(dmin, mVertices[j].dot(mInputPlanes[i].n) + mInputPlanes[i].d); + } + + float dr = dmax - dmin; + if (dr < planeTestEpsilon) + dr = 1.0f; // shouldn't happen. + d = dmax / dr; + // we have a better candidate try another one + if (d <= md) + continue; + // check if we dont have already that plane or if the normals are nearly the same + for (j = 0; j local::MAXDOT_MINANG) + { + for (PxU32 k = 0; k < mEdges.size(); k++) + { + if (mEdges[k].p != j) + continue; + if (mVertices[mEdges[k].v].dot(mInputPlanes[i].n) + mInputPlanes[i].d < 0) + { + d = 0; // so this plane wont get selected. + break; + } + } + } + } + if (d>md) + { + p = PxI32(i); + md = d; + } + } + return (md > epsilon) ? p : -1; +} + +////////////////////////////////////////////////////////////////////////// +// internal hull check +bool ConvexHull::assertIntact(float epsilon) const +{ + PxU32 i; + PxU32 estart = 0; + for (i = 0; i < mEdges.size(); i++) + { + if (mEdges[estart].p != mEdges[i].p) + { + estart = i; + } + PxU32 inext = i + 1; + if (inext >= mEdges.size() || mEdges[inext].p != mEdges[i].p) + { + inext = estart; + } + PX_ASSERT(mEdges[inext].p == mEdges[i].p); + PxI16 nb = mEdges[i].ea; + if (nb == 255 || nb == -1) + return false; + PX_ASSERT(nb != -1); + PX_ASSERT(i == PxU32(mEdges[PxU32(nb)].ea)); + // Check that the vertex of the next edge is the vertex of the adjacent half edge. + // Otherwise the two half edges are not really adjacent and we have a hole. + PX_ASSERT(mEdges[PxU32(nb)].v == mEdges[inext].v); + if (!(mEdges[PxU32(nb)].v == mEdges[inext].v)) + return false; + } + + for (i = 0; i < mEdges.size(); i++) + { + PX_ASSERT(local::eCOPLANAR == local::planeTest(mFacets[mEdges[i].p], mVertices[mEdges[i].v], epsilon)); + if (local::eCOPLANAR != local::planeTest(mFacets[mEdges[i].p], mVertices[mEdges[i].v], epsilon)) + return false; + if (mEdges[estart].p != mEdges[i].p) + { + estart = i; + } + PxU32 i1 = i + 1; + if (i1 >= mEdges.size() || mEdges[i1].p != mEdges[i].p) { + i1 = estart; + } + PxU32 i2 = i1 + 1; + if (i2 >= mEdges.size() || mEdges[i2].p != mEdges[i].p) { + i2 = estart; + } + if (i == i2) + continue; // i sliced tangent to an edge and created 2 meaningless edges + + // check the face normal against the triangle from edges + PxVec3 localNormal = (mVertices[mEdges[i1].v] - mVertices[mEdges[i].v]).cross(mVertices[mEdges[i2].v] - mVertices[mEdges[i1].v]); + const float m = localNormal.magnitude(); + if (m == 0.0f) + localNormal = PxVec3(1.f, 0.0f, 0.0f); + localNormal *= (1.0f / m); + if (localNormal.dot(mFacets[mEdges[i].p].n) <= 0.0f) + return false; + } + return true; +} + +// returns the maximum number of vertices on a face +PxU32 ConvexHull::maxNumVertsPerFace() const +{ + PxU32 maxVerts = 0; + PxU32 currentVerts = 0; + PxU32 estart = 0; + for (PxU32 i = 0; i < mEdges.size(); i++) + { + if (mEdges[estart].p != mEdges[i].p) + { + if(currentVerts > maxVerts) + { + maxVerts = currentVerts + 1; + } + currentVerts = 0; + estart = i; + } + else + { + currentVerts++; + } + } + return maxVerts; +} + +////////////////////////////////////////////////////////////////////////// +// slice the input convexHull with the slice plane +ConvexHull* physx::convexHullCrop(const ConvexHull& convex, const PxPlane& slice, float planeTestEpsilon) +{ + static const PxU8 invalidIndex = PxU8(-1); + PxU32 i; + PxU32 vertCountUnder = 0; // Running count of the vertices UNDER the slicing plane. + + PX_ASSERT(convex.getEdges().size() < 480); + + // Arrays of mapping information associated with features in the input convex. + // edgeflag[i].undermap - output index of input edge convex->edges[i] + // vertflag[i].undermap - output index of input vertex convex->vertices[i] + // vertflag[i].planetest - the side-of-plane classification of convex->vertices[i] + // (There are other members but they are unused.) + local::EdgeFlag edgeFlag[512]; + local::VertFlag vertFlag[256]; + + // Lists of output features. Populated during clipping. + // Coplanar edges have one sibling in tmpunderedges and one in coplanaredges. + // coplanaredges holds the sibling that belong to the new polygon created from slicing. + ConvexHull::HalfEdge tmpUnderEdges[512]; // The output edge list. + PxPlane tmpUnderPlanes[128]; // The output plane list. + local::Coplanar coplanarEdges[512]; // The coplanar edge list. + + PxU32 coplanarEdgesNum = 0; // Running count of coplanar edges. + + // Created vertices on the slicing plane (stored for output after clipping). + Ps::Array createdVerts; + + // Logical OR of individual vertex flags. + PxU32 convexClipFlags = 0; + + // Classify each vertex against the slicing plane as OVER | COPLANAR | UNDER. + // OVER - Vertex is over (outside) the slicing plane. Will not be output. + // COPLANAR - Vertex is on the slicing plane. A copy will be output. + // UNDER - Vertex is under (inside) the slicing plane. Will be output. + // We keep an array of information structures for each vertex in the input convex. + // vertflag[i].undermap - The (computed) index of convex->vertices[i] in the output. + // invalidIndex for OVER vertices - they are not output. + // initially invalidIndex for COPLANAR vertices - set later. + // vertflag[i].overmap - Unused - we don't care about the over part. + // vertflag[i].planetest - The classification (clip flag) of convex->vertices[i]. + for (i = 0; i < convex.getVertices().size(); i++) + { + local::PlaneTestResult vertexClipFlag = local::planeTest(slice, convex.getVertices()[i], planeTestEpsilon); + switch (vertexClipFlag) + { + case local::eOVER: + case local::eCOPLANAR: + vertFlag[i].undermap = invalidIndex; // Initially invalid for COPLANAR + vertFlag[i].overmap = invalidIndex; + break; + case local::eUNDER: + vertFlag[i].undermap = Ps::to8(vertCountUnder++); + vertFlag[i].overmap = invalidIndex; + break; + } + vertFlag[i].planetest = PxU8(vertexClipFlag); + convexClipFlags |= vertexClipFlag; + } + + // Check special case: everything UNDER or COPLANAR. + // This way we know we wont end up with silly faces / edges later on. + if ((convexClipFlags & local::eOVER) == 0) + { + // Just return a copy of the same convex. + ConvexHull* dst = PX_NEW_TEMP(ConvexHull)(convex); + return dst; + } + + PxU16 underEdgeCount = 0; // Running count of output edges. + PxU16 underPlanesCount = 0; // Running count of output planes. + + // Clipping Loop + // ============= + // + // for each plane + // + // for each edge + // + // if first UNDER & second !UNDER + // output current edge -> tmpunderedges + // if we have done the sibling + // connect current edge to its sibling + // set vout = first vertex of sibling + // else if second is COPLANAR + // if we havent already copied it + // copy second -> createdverts + // set vout = index of created vertex + // else + // generate a new vertex -> createdverts + // set vout = index of created vertex + // if vin is already set and vin != vout (non-trivial edge) + // output coplanar edge -> tmpunderedges (one sibling) + // set coplanaredge to new edge index (for connecting the other sibling) + // + // else if first !UNDER & second UNDER + // if we have done the sibling + // connect current edge to its sibling + // set vin = second vertex of sibling (this is a bit of a pain) + // else if first is COPLANAR + // if we havent already copied it + // copy first -> createdverts + // set vin = index of created vertex + // else + // generate a new vertex -> createdverts + // set vin = index of created vertex + // if vout is already set and vin != vout (non-trivial edge) + // output coplanar edge -> tmpunderedges (one sibling) + // set coplanaredge to new edge index (for connecting the other sibling) + // output current edge -> tmpunderedges + // + // else if first UNDER & second UNDER + // output current edge -> tmpunderedges + // + // next edge + // + // if part of current plane was UNDER + // output current plane -> tmpunderplanes + // + // if coplanaredge is set + // output coplanar edge -> coplanaredges + // + // next plane + // + + // Indexing is a bit tricky here: + // + // e0 - index of the current edge + // e1 - index of the next edge + // estart - index of the first edge in the current plane + // currentplane - index of the current plane + // enextface - first edge of next plane + + PxU32 e0 = 0; + + for (PxU32 currentplane = 0; currentplane < convex.getFacets().size(); currentplane++) + { + + PxU32 eStart = e0; + PxU32 eNextFace = 0xffffffff; + PxU32 e1 = e0 + 1; + + PxU8 vout = invalidIndex; + PxU8 vin = invalidIndex; + + PxU32 coplanarEdge = invalidIndex; + + // Logical OR of individual vertex flags in the current plane. + PxU32 planeSide = 0; + + do{ + + // Next edge modulo logic + if (e1 >= convex.getEdges().size() || convex.getEdges()[e1].p != currentplane) + { + eNextFace = e1; + e1 = eStart; + } + + const ConvexHull::HalfEdge& edge0 = convex.getEdges()[e0]; + const ConvexHull::HalfEdge& edge1 = convex.getEdges()[e1]; + const ConvexHull::HalfEdge& edgea = convex.getEdges()[PxU32(edge0.ea)]; + + planeSide |= vertFlag[edge0.v].planetest; + + if (vertFlag[edge0.v].planetest == local::eUNDER && vertFlag[edge1.v].planetest != local::eUNDER) + { + // first is UNDER, second is COPLANAR or OVER + + // Output current edge. + edgeFlag[e0].undermap = short(underEdgeCount); + tmpUnderEdges[underEdgeCount].v = vertFlag[edge0.v].undermap; + tmpUnderEdges[underEdgeCount].p = PxU8(underPlanesCount); + PX_ASSERT(tmpUnderEdges[underEdgeCount].v != invalidIndex); + + if (PxU32(edge0.ea) < e0) + { + // We have already done the sibling. + // Connect current edge to its sibling. + PX_ASSERT(edgeFlag[edge0.ea].undermap != invalidIndex); + tmpUnderEdges[underEdgeCount].ea = edgeFlag[edge0.ea].undermap; + tmpUnderEdges[edgeFlag[edge0.ea].undermap].ea = short(underEdgeCount); + // Set vout = first vertex of (output, clipped) sibling. + vout = tmpUnderEdges[edgeFlag[edge0.ea].undermap].v; + } + else if (vertFlag[edge1.v].planetest == local::eCOPLANAR) + { + // Boundary case. + // We output coplanar vertices once. + if (vertFlag[edge1.v].undermap == invalidIndex) + { + createdVerts.pushBack(convex.getVertices()[edge1.v]); + // Remember the index so we don't output it again. + vertFlag[edge1.v].undermap = Ps::to8(vertCountUnder++); + } + vout = vertFlag[edge1.v].undermap; + } + else + { + // Add new vertex. + const PxPlane& p0 = convex.getFacets()[edge0.p]; + const PxPlane& pa = convex.getFacets()[edgea.p]; + createdVerts.pushBack(threePlaneIntersection(p0, pa, slice)); + vout = Ps::to8(vertCountUnder++); + } + + // We added an edge, increment the counter + underEdgeCount++; + + if (vin != invalidIndex && vin != vout) + { + // We already have vin and a non-trivial edge + // Output coplanar edge + PX_ASSERT(vout != invalidIndex); + coplanarEdge = underEdgeCount; + tmpUnderEdges[underEdgeCount].v = vout; + tmpUnderEdges[underEdgeCount].p = PxU8(underPlanesCount); + tmpUnderEdges[underEdgeCount].ea = invalidIndex; + underEdgeCount++; + } + } + else if (vertFlag[edge0.v].planetest != local::eUNDER && vertFlag[edge1.v].planetest == local::eUNDER) + { + // First is OVER or COPLANAR, second is UNDER. + + if (PxU32(edge0.ea) < e0) + { + // We have already done the sibling. + // We need the second vertex of the sibling. + // Which is the vertex of the next edge in the adjacent poly. + int nea = edgeFlag[edge0.ea].undermap + 1; + int p = tmpUnderEdges[edgeFlag[edge0.ea].undermap].p; + if (nea >= underEdgeCount || tmpUnderEdges[nea].p != p) + { + // End of polygon, next edge is first edge + nea -= 2; + while (nea > 0 && tmpUnderEdges[nea - 1].p == p) + nea--; + } + vin = tmpUnderEdges[nea].v; + PX_ASSERT(vin < vertCountUnder); + } + else if (vertFlag[edge0.v].planetest == local::eCOPLANAR) + { + // Boundary case. + // We output coplanar vertices once. + if (vertFlag[edge0.v].undermap == invalidIndex) + { + createdVerts.pushBack(convex.getVertices()[edge0.v]); + // Remember the index so we don't output it again. + vertFlag[edge0.v].undermap = Ps::to8(vertCountUnder++); + } + vin = vertFlag[edge0.v].undermap; + } + else + { + // Add new vertex. + const PxPlane& p0 = convex.getFacets()[edge0.p]; + const PxPlane& pa = convex.getFacets()[edgea.p]; + createdVerts.pushBack(threePlaneIntersection(p0, pa, slice)); + vin = Ps::to8(vertCountUnder++); + } + + if (vout != invalidIndex && vin != vout) + { + // We have been in and out, Add the coplanar edge + coplanarEdge = underEdgeCount; + tmpUnderEdges[underEdgeCount].v = vout; + tmpUnderEdges[underEdgeCount].p = Ps::to8(underPlanesCount); + tmpUnderEdges[underEdgeCount].ea = invalidIndex; + underEdgeCount++; + } + + // Output current edge. + tmpUnderEdges[underEdgeCount].v = vin; + tmpUnderEdges[underEdgeCount].p = Ps::to8(underPlanesCount); + edgeFlag[e0].undermap = short(underEdgeCount); + + if (PxU32(edge0.ea) < e0) + { + // We have already done the sibling. + // Connect current edge to its sibling. + PX_ASSERT(edgeFlag[edge0.ea].undermap != invalidIndex); + tmpUnderEdges[underEdgeCount].ea = edgeFlag[edge0.ea].undermap; + tmpUnderEdges[edgeFlag[edge0.ea].undermap].ea = short(underEdgeCount); + } + + PX_ASSERT(edgeFlag[e0].undermap == underEdgeCount); + underEdgeCount++; + } + else if (vertFlag[edge0.v].planetest == local::eUNDER && vertFlag[edge1.v].planetest == local::eUNDER) + { + // Both UNDER + + // Output current edge. + edgeFlag[e0].undermap = short(underEdgeCount); + tmpUnderEdges[underEdgeCount].v = vertFlag[edge0.v].undermap; + tmpUnderEdges[underEdgeCount].p = Ps::to8(underPlanesCount); + if (PxU32(edge0.ea) < e0) + { + // We have already done the sibling. + // Connect current edge to its sibling. + PX_ASSERT(edgeFlag[edge0.ea].undermap != invalidIndex); + tmpUnderEdges[underEdgeCount].ea = edgeFlag[edge0.ea].undermap; + tmpUnderEdges[edgeFlag[edge0.ea].undermap].ea = short(underEdgeCount); + } + underEdgeCount++; + } + + e0 = e1; + e1++; // do the modulo at the beginning of the loop + + } while (e0 != eStart); + + e0 = eNextFace; + + if (planeSide & local::eUNDER) + { + // At least part of current plane is UNDER. + // Output current plane. + tmpUnderPlanes[underPlanesCount] = convex.getFacets()[currentplane]; + underPlanesCount++; + } + + if (coplanarEdge != invalidIndex) + { + // We have a coplanar edge. + // Add to coplanaredges for later processing. + // (One sibling is in place but one is missing) + PX_ASSERT(vin != invalidIndex); + PX_ASSERT(vout != invalidIndex); + PX_ASSERT(coplanarEdge != 511); + coplanarEdges[coplanarEdgesNum].ea = PxU8(coplanarEdge); + coplanarEdges[coplanarEdgesNum].v0 = vin; + coplanarEdges[coplanarEdgesNum].v1 = vout; + coplanarEdgesNum++; + } + + // Reset coplanar edge infos for next poly + vin = invalidIndex; + vout = invalidIndex; + coplanarEdge = invalidIndex; + } + + // Add the new plane to the mix: + if (coplanarEdgesNum > 0) + { + tmpUnderPlanes[underPlanesCount++] = slice; + } + + // Sort the coplanar edges in winding order. + for (i = 0; i < coplanarEdgesNum - 1; i++) + { + if (coplanarEdges[i].v1 != coplanarEdges[i + 1].v0) + { + PxU32 j = 0; + for (j = i + 2; j < coplanarEdgesNum; j++) + { + if (coplanarEdges[i].v1 == coplanarEdges[j].v0) + { + local::Coplanar tmp = coplanarEdges[i + 1]; + coplanarEdges[i + 1] = coplanarEdges[j]; + coplanarEdges[j] = tmp; + break; + } + } + if (j >= coplanarEdgesNum) + { + // PX_ASSERT(j(desc.points.data)); + const PxU32* ind = (reinterpret_cast(desc.indices.data)); + const PxHullPolygon* polygons = (reinterpret_cast(desc.polygons.data)); + PxVec3 mean(0.0f); + for (PxU32 i = 0; i < desc.points.count; i++) + mean += verts[i]; + mean *= (1.0f / desc.points.count); + + PxU8* indices = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxU8)*desc.indices.count, "PxU8")); + for (PxU32 i = 0; i < desc.indices.count; i++) + { + indices[i] = Ps::to8(ind[i]); + } + // we need to move the polygon data to internal format + Gu::HullPolygonData* polygonData = reinterpret_cast (PX_ALLOC_TEMP(sizeof(Gu::HullPolygonData)*desc.polygons.count, "Gu::HullPolygonData")); + for (PxU32 i = 0; i < desc.polygons.count; i++) + { + polygonData[i].mPlane = PxPlane(polygons[i].mPlane[0], polygons[i].mPlane[1], polygons[i].mPlane[2], polygons[i].mPlane[3]); + polygonData[i].mNbVerts = Ps::to8(polygons[i].mNbVerts); + polygonData[i].mVRef8 = polygons[i].mIndexBase; + } + + PxConvexMeshDesc inDesc; + inDesc.points.data = desc.points.data; + inDesc.points.count = desc.points.count; + + inDesc.polygons.data = polygonData; + inDesc.polygons.count = desc.polygons.count; + + inDesc.indices.data = indices; + inDesc.indices.count = desc.indices.count; + + // compute volume integrals to get basis axis + bool status = (desc.flags & PxConvexFlag::eFAST_INERTIA_COMPUTATION) ? + computeVolumeIntegralsEberlySIMD(inDesc, 1.0f, integrals, mean) : computeVolumeIntegralsEberly(inDesc, 1.0f, integrals, mean); + if (status) + { + Vec4V* pointsV = reinterpret_cast (PX_ALLOC_TEMP(sizeof(Vec4V)*desc.points.count, "Vec4V")); + for (PxU32 i = 0; i < desc.points.count; i++) + { + // safe to V4 load, same as volume integration - we allocate one more vector + pointsV[i] = V4LoadU(&verts[i].x); + } + + PxMat33 inertia; + integrals.getOriginInertia(inertia); + PxQuat inertiaQuat; + PxDiagonalize(inertia, inertiaQuat); + PxMat33 baseAxis(inertiaQuat); + Vec4V center = V4LoadU(&integrals.COM.x); + + const PxU32 numSteps = 20; + const float subStep = Ps::degToRad(float(360/numSteps)); + + float bestVolume = 1e9; + + for (PxU32 axis = 0; axis < 3; axis++) + { + for (PxU32 iStep = 0; iStep < numSteps; iStep++) + { + PxQuat quat(iStep*subStep, baseAxis[axis]); + + Vec4V transV = center; + Vec4V psidesV; + + const QuatV rotV = QuatVLoadU(&quat.x); + local::computeOBBSIMD(desc.points.count, pointsV, psidesV, rotV, transV); + + PxVec3 psides; + V3StoreU(Vec3V_From_Vec4V(psidesV), psides); + + const float volume = psides[0] * psides[1] * psides[2]; // the volume of the cube + + if (volume <= bestVolume) + { + bestVolume = volume; + sides = psides; + + V4StoreU(rotV, &matrix.q.x); + V3StoreU(Vec3V_From_Vec4V(transV), matrix.p); + } + } + } + + PX_FREE_AND_RESET(pointsV); + } + else + { + PX_FREE_AND_RESET(indices); + PX_FREE_AND_RESET(polygonData); + return false; + } + + PX_FREE_AND_RESET(indices); + PX_FREE_AND_RESET(polygonData); + return true; +} diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h new file mode 100644 index 000000000..c97ff2b61 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h @@ -0,0 +1,177 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONVEXHULLUTILS_H +#define PX_CONVEXHULLUTILS_H + +#include "foundation/PxMemory.h" +#include "foundation/PxPlane.h" + +#include "CmPhysXCommon.h" + +#include "PsUserAllocated.h" +#include "PsArray.h" +#include "PsMathUtils.h" + +#include "PxConvexMeshDesc.h" + +namespace physx +{ + + ////////////////////////////////////////////////////////////////////////// + // helper class for hull construction, holds the vertices and planes together + // while cropping the hull with planes + class ConvexHull : public Ps::UserAllocated + { + public: + + // Helper class for halfedge representation + class HalfEdge + { + public: + PxI16 ea; // the other half of the edge (index into edges list) + PxU8 v; // the vertex at the start of this edge (index into vertices list) + PxU8 p; // the facet on which this edge lies (index into facets list) + HalfEdge(){} + HalfEdge(PxI16 _ea, PxU8 _v, PxU8 _p) :ea(_ea), v(_v), p(_p){} + }; + + ConvexHull& operator = (const ConvexHull&); + + // construct the base cube hull from given max/min AABB + ConvexHull(const PxVec3& bmin, const PxVec3& bmax, const Ps::Array& inPlanes); + + // construct the base cube hull from given OBB + ConvexHull(const PxVec3& extent, const PxTransform& transform, const Ps::Array& inPlanes); + + // copy constructor + ConvexHull(const ConvexHull& srcHull) + : mInputPlanes(srcHull.getInputPlanes()) + { + copyHull(srcHull); + } + + // construct plain hull + ConvexHull(const Ps::Array& inPlanes) + : mInputPlanes(inPlanes) + { + } + + // finds the candidate plane, returns -1 otherwise + PxI32 findCandidatePlane(float planetestepsilon, float epsilon) const; + + // internal check of the hull integrity + bool assertIntact(float epsilon) const; + + // return vertices + const Ps::Array& getVertices() const + { + return mVertices; + } + + // return edges + const Ps::Array& getEdges() const + { + return mEdges; + } + + // return faces + const Ps::Array& getFacets() const + { + return mFacets; + } + + // return input planes + const Ps::Array& getInputPlanes() const + { + return mInputPlanes; + } + + // return vertices + Ps::Array& getVertices() + { + return mVertices; + } + + // return edges + Ps::Array& getEdges() + { + return mEdges; + } + + // return faces + Ps::Array& getFacets() + { + return mFacets; + } + + // returns the maximum number of vertices on a face + PxU32 maxNumVertsPerFace() const; + + // copy the hull from source + void copyHull(const ConvexHull& src) + { + mVertices.resize(src.getVertices().size()); + mEdges.resize(src.getEdges().size()); + mFacets.resize(src.getFacets().size()); + + PxMemCopy(mVertices.begin(), src.getVertices().begin(), src.getVertices().size()*sizeof(PxVec3)); + PxMemCopy(mEdges.begin(), src.getEdges().begin(), src.getEdges().size()*sizeof(HalfEdge)); + PxMemCopy(mFacets.begin(), src.getFacets().begin(), src.getFacets().size()*sizeof(PxPlane)); + } + + private: + Ps::Array mVertices; + Ps::Array mEdges; + Ps::Array mFacets; + const Ps::Array& mInputPlanes; + }; + + //////////////////////////////////////////////////////////////////////////| + // Crops the hull with a provided plane and with given epsilon + // returns new hull if succeeded + ConvexHull* convexHullCrop(const ConvexHull& convex, const PxPlane& slice, float planetestepsilon); + + //////////////////////////////////////////////////////////////////////////| + // three planes intersection + PX_FORCE_INLINE PxVec3 threePlaneIntersection(const PxPlane& p0, const PxPlane& p1, const PxPlane& p2) + { + PxMat33 mp = (PxMat33(p0.n, p1.n, p2.n)).getTranspose(); + PxMat33 mi = (mp).getInverse(); + PxVec3 b(p0.d, p1.d, p2.d); + return -mi.transform(b); + } + + ////////////////////////////////////////////////////////////////////////// + // Compute OBB around given convex hull + bool computeOBBFromConvex(const PxConvexMeshDesc& desc, PxVec3& sides, PxTransform& matrix); +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp new file mode 100644 index 000000000..c484be77d --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp @@ -0,0 +1,502 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuConvexMesh.h" +#include "PsFoundation.h" +#include "PsMathUtils.h" +#include "Cooking.h" + +#include "GuHillClimbing.h" +#include "GuBigConvexData2.h" +#include "GuInternal.h" +#include "GuSerialize.h" +#include "VolumeIntegration.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "VolumeIntegration.h" +#include "ConvexHullBuilder.h" +#include "ConvexMeshBuilder.h" +#include "BigConvexDataBuilder.h" + +#include "CmUtils.h" +#include "PsVecMath.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +ConvexMeshBuilder::ConvexMeshBuilder(const bool buildGRBData) : hullBuilder(&mHullData, buildGRBData), mBigConvexData(NULL), mMass(0.0f), mInertia(PxIdentity) +{ +} + +ConvexMeshBuilder::~ConvexMeshBuilder() +{ + PX_DELETE_AND_RESET(mBigConvexData); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// load the mesh data from given polygons +bool ConvexMeshBuilder::build(const PxConvexMeshDesc& desc, PxU32 gaussMapVertexLimit, bool validateOnly, ConvexHullLib* hullLib) +{ + if(!desc.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Gu::ConvexMesh::loadFromDesc: desc.isValid() failed!"); + return false; + } + + if(!loadConvexHull(desc, hullLib)) + return false; + + // Compute local bounds (*after* hull has been created) + PxBounds3 minMaxBounds; + computeBoundsAroundVertices(minMaxBounds, mHullData.mNbHullVertices, hullBuilder.mHullDataHullVertices); + mHullData.mAABB = CenterExtents(minMaxBounds); + + if(mHullData.mNbHullVertices > gaussMapVertexLimit) + { + if(!computeGaussMaps()) + { + return false; + } + } + + if(validateOnly) + return true; + +// TEST_INTERNAL_OBJECTS + computeInternalObjects(); +//~TEST_INTERNAL_OBJECTS + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PX_COMPILE_TIME_ASSERT(sizeof(PxMaterialTableIndex)==sizeof(PxU16)); +bool ConvexMeshBuilder::save(PxOutputStream& stream, bool platformMismatch) const +{ + // Export header + if(!writeHeader('C', 'V', 'X', 'M', PX_CONVEX_VERSION, platformMismatch, stream)) + return false; + + // Export serialization flags + PxU32 serialFlags = 0; + + writeDword(serialFlags, platformMismatch, stream); + + if(!hullBuilder.save(stream, platformMismatch)) + return false; + + // Export local bounds +// writeFloat(geomEpsilon, platformMismatch, stream); + writeFloat(0.0f, platformMismatch, stream); + writeFloat(mHullData.mAABB.getMin(0), platformMismatch, stream); + writeFloat(mHullData.mAABB.getMin(1), platformMismatch, stream); + writeFloat(mHullData.mAABB.getMin(2), platformMismatch, stream); + writeFloat(mHullData.mAABB.getMax(0), platformMismatch, stream); + writeFloat(mHullData.mAABB.getMax(1), platformMismatch, stream); + writeFloat(mHullData.mAABB.getMax(2), platformMismatch, stream); + + // Export mass info + writeFloat(mMass, platformMismatch, stream); + writeFloatBuffer(reinterpret_cast(&mInertia), 9, platformMismatch, stream); + writeFloatBuffer(&mHullData.mCenterOfMass.x, 3, platformMismatch, stream); + + // Export gaussmaps + if(mBigConvexData) + { + writeFloat(1.0f, platformMismatch, stream); //gauss map flag true + BigConvexDataBuilder SVMB(&mHullData, mBigConvexData, hullBuilder.mHullDataHullVertices); + SVMB.save(stream, platformMismatch); + } + else + writeFloat(-1.0f, platformMismatch, stream); //gauss map flag false + +// TEST_INTERNAL_OBJECTS + writeFloat(mHullData.mInternal.mRadius, platformMismatch, stream); + writeFloat(mHullData.mInternal.mExtents[0], platformMismatch, stream); + writeFloat(mHullData.mInternal.mExtents[1], platformMismatch, stream); + writeFloat(mHullData.mInternal.mExtents[2], platformMismatch, stream); +//~TEST_INTERNAL_OBJECTS + return true; +} + +////////////////////////////////////////////////////////////////////////// +// instead of saving the data into stream, we copy the mesh data +// into internal Gu::ConvexMesh. +bool ConvexMeshBuilder::copy(Gu::ConvexHullData& hullData, PxU32& nb) +{ + // hull builder data copy + hullBuilder.copy(hullData, nb); + + // mass props + hullData.mAABB = mHullData.mAABB; + hullData.mCenterOfMass = mHullData.mCenterOfMass; + + // big convex data + if(mBigConvexData) + { + hullData.mBigConvexRawData = &mBigConvexData->mData; + } + else + hullData.mBigConvexRawData = NULL; + + // internal data + hullData.mInternal.mRadius = mHullData.mInternal.mRadius; + hullData.mInternal.mExtents[0] = mHullData.mInternal.mExtents[0]; + hullData.mInternal.mExtents[1] = mHullData.mInternal.mExtents[1]; + hullData.mInternal.mExtents[2] = mHullData.mInternal.mExtents[2]; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// compute mass and inertia of the convex mesh +void ConvexMeshBuilder::computeMassInfo(bool lowerPrecision) +{ + if(mMass <= 0.0f) //not yet computed. + { + PxIntegrals integrals; + PxConvexMeshDesc meshDesc; + meshDesc.points.count = mHullData.mNbHullVertices; + meshDesc.points.data = hullBuilder.mHullDataHullVertices; + meshDesc.points.stride = sizeof(PxVec3); + + meshDesc.polygons.data = hullBuilder.mHullDataPolygons; + meshDesc.polygons.stride = sizeof(Gu::HullPolygonData); + meshDesc.polygons.count = hullBuilder.mHull->mNbPolygons; + + meshDesc.indices.data = hullBuilder.mHullDataVertexData8; + + // using the centroid of the convex for the volume integration solved accuracy issues in cases where the inertia tensor + // ended up close to not being positive definite and after a few further transforms the diagonalized inertia tensor ended + // up with negative values. + PxVec3 mean(0.0f); + for(PxU32 i=0; i < mHullData.mNbHullVertices; i++) + mean += hullBuilder.mHullDataHullVertices[i]; + mean *= (1.0f / mHullData.mNbHullVertices); + + bool status = lowerPrecision ? + computeVolumeIntegralsEberlySIMD(meshDesc, 1.0f, integrals, mean) : computeVolumeIntegralsEberly(meshDesc, 1.0f, integrals, mean); + if(status) + { + + integrals.getOriginInertia(reinterpret_cast(mInertia)); + mHullData.mCenterOfMass = integrals.COM; + + //note: the mass will be negative for an inside-out mesh! + if(mInertia.column0.isFinite() && mInertia.column1.isFinite() && mInertia.column2.isFinite() + && mHullData.mCenterOfMass.isFinite() && PxIsFinite(PxReal(integrals.mass))) + { + if (integrals.mass < 0) + { + Ps::getFoundation().error(PX_WARN, "Gu::ConvexMesh: Mesh has a negative volume! Is it open or do (some) faces have reversed winding? (Taking absolute value.)"); + integrals.mass = -integrals.mass; + mInertia = -mInertia; + } + + mMass = PxReal(integrals.mass); //set mass to valid value. + return; + } + } + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::ConvexMesh: Error computing mesh mass properties!\n"); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4996) // permitting use of gatherStrided until we have a replacement. +#endif + +bool ConvexMeshBuilder::loadConvexHull(const PxConvexMeshDesc& desc, ConvexHullLib* hullLib) +{ + // gather points + PxVec3* geometry = reinterpret_cast(PxAlloca(sizeof(PxVec3)*desc.points.count)); + Cooking::gatherStrided(desc.points.data, geometry, desc.points.count, sizeof(PxVec3), desc.points.stride); + + PxU32* topology = NULL; + + // gather indices + // store the indices into topology if we have the polygon data + if(desc.indices.data) + { + topology = reinterpret_cast(PxAlloca(sizeof(PxU32)*desc.indices.count)); + if (desc.flags & PxConvexFlag::e16_BIT_INDICES) + { + // conversion; 16 bit index -> 32 bit index & stride + PxU32* dest = topology; + const PxU32* pastLastDest = topology + desc.indices.count; + const PxU8* source = reinterpret_cast(desc.indices.data); + while (dest < pastLastDest) + { + const PxU16 * trig16 = reinterpret_cast(source); + *dest++ = *trig16; + source += desc.indices.stride; + } + } + else + { + Cooking::gatherStrided(desc.indices.data, topology, desc.indices.count, sizeof(PxU32), desc.indices.stride); + } + } + + // gather polygons + PxHullPolygon* hullPolygons = NULL; + if(desc.polygons.data) + { + hullPolygons = reinterpret_cast(PxAlloca(sizeof(PxHullPolygon)*desc.polygons.count)); + Cooking::gatherStrided(desc.polygons.data,hullPolygons,desc.polygons.count,sizeof(PxHullPolygon),desc.polygons.stride); + + // if user polygons, make sure the largest one is the first one + if (!hullLib) + { + PxU32 largestPolygon = 0; + for (PxU32 i = 1; i < desc.polygons.count; i++) + { + if(hullPolygons[i].mNbVerts > hullPolygons[largestPolygon].mNbVerts) + largestPolygon = i; + } + if(largestPolygon != 0) + { + PxHullPolygon movedPolygon = hullPolygons[0]; + hullPolygons[0] = hullPolygons[largestPolygon]; + hullPolygons[largestPolygon] = movedPolygon; + } + } + } + + const bool doValidation = desc.flags & PxConvexFlag::eDISABLE_MESH_VALIDATION ? false : true; + if(!hullBuilder.init(desc.points.count, geometry, topology, desc.indices.count, desc.polygons.count, hullPolygons, doValidation, hullLib)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::ConvexMesh::loadConvexHull: convex hull init failed!"); + return false; + } + computeMassInfo(desc.flags & PxConvexFlag::eFAST_INERTIA_COMPUTATION); + + return true; +} + +#if PX_VC +#pragma warning(pop) +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// compute polygons from given triangles. This is support function used in extensions. We do not accept triangles as an input for convex mesh desc. +bool ConvexMeshBuilder::computeHullPolygons(const PxU32& nbVerts,const PxVec3* verts, const PxU32& nbTriangles, const PxU32* triangles, PxAllocatorCallback& inAllocator, + PxU32& outNbVerts, PxVec3*& outVertices , PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& polygons) +{ + if(!hullBuilder.computeHullPolygons(nbVerts,verts,nbTriangles,triangles)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexMeshBuilder::computeHullPolygons: compute convex hull polygons failed. Provided triangles dont form a convex hull."); + return false; + } + + outNbVerts = hullBuilder.mHull->mNbHullVertices; + nbPolygons = hullBuilder.mHull->mNbPolygons; + + outVertices = reinterpret_cast(inAllocator.allocate(outNbVerts*sizeof(PxVec3),"PxVec3",__FILE__,__LINE__)); + PxMemCopy(outVertices,hullBuilder.mHullDataHullVertices,outNbVerts*sizeof(PxVec3)); + + nbIndices = 0; + for (PxU32 i = 0; i < nbPolygons; i++) + { + nbIndices += hullBuilder.mHullDataPolygons[i].mNbVerts; + } + + indices = reinterpret_cast(inAllocator.allocate(nbIndices*sizeof(PxU32),"PxU32",__FILE__,__LINE__)); + for (PxU32 i = 0; i < nbIndices; i++) + { + indices[i] = hullBuilder.mHullDataVertexData8[i]; + } + + polygons = reinterpret_cast(inAllocator.allocate(nbPolygons*sizeof(PxHullPolygon),"PxHullPolygon",__FILE__,__LINE__)); + + for (PxU32 i = 0; i < nbPolygons; i++) + { + const Gu::HullPolygonData& polygonData = hullBuilder.mHullDataPolygons[i]; + PxHullPolygon& outPolygon = polygons[i]; + outPolygon.mPlane[0] = polygonData.mPlane.n.x; + outPolygon.mPlane[1] = polygonData.mPlane.n.y; + outPolygon.mPlane[2] = polygonData.mPlane.n.z; + outPolygon.mPlane[3] = polygonData.mPlane.d; + + outPolygon.mNbVerts = polygonData.mNbVerts; + outPolygon.mIndexBase = polygonData.mVRef8; + + for (PxU32 j = 0; j < polygonData.mNbVerts; j++) + { + PX_ASSERT(indices[outPolygon.mIndexBase + j] == hullBuilder.mHullDataVertexData8[polygonData.mVRef8+j]); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// compute big convex data +bool ConvexMeshBuilder::computeGaussMaps() +{ + // The number of polygons is limited to 256 because the gaussmap encode 256 polys maximum + + PxU32 density = 16; + // density = 64; + // density = 8; + // density = 2; + + PX_DELETE(mBigConvexData); + PX_NEW_SERIALIZED(mBigConvexData,BigConvexData); + BigConvexDataBuilder SVMB(&mHullData, mBigConvexData, hullBuilder.mHullDataHullVertices); + // valencies we need to compute first, they are needed for min/max precompute + SVMB.computeValencies(hullBuilder); + SVMB.precompute(density); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TEST_INTERNAL_OBJECTS + +static void ComputeInternalExtent(Gu::ConvexHullData& data, const Gu::HullPolygonData* hullPolys) +{ + const PxVec3 e = data.mAABB.getMax() - data.mAABB.getMin(); + + // PT: For that formula, see %SDKRoot%\InternalDocumentation\Cooking\InternalExtents.png + const float r = data.mInternal.mRadius / sqrtf(3.0f); + + const float epsilon = 1E-7f; + + const PxU32 largestExtent = Ps::largestAxis(e); + PxU32 e0 = Ps::getNextIndex3(largestExtent); + PxU32 e1 = Ps::getNextIndex3(e0); + if(e[e0] < e[e1]) + Ps::swap(e0,e1); + + data.mInternal.mExtents[0] = FLT_MAX; + data.mInternal.mExtents[1] = FLT_MAX; + data.mInternal.mExtents[2] = FLT_MAX; + + // PT: the following code does ray-vs-plane raycasts. + + // find the largest box along the largest extent, with given internal radius + for(PxU32 i = 0; i < data.mNbPolygons; i++) + { + // concurrent with search direction + const float d = hullPolys[i].mPlane.n[largestExtent]; + if((-epsilon < d && d < epsilon)) + continue; + + const float numBase = -hullPolys[i].mPlane.d - hullPolys[i].mPlane.n.dot(data.mCenterOfMass); + const float denBase = 1.0f/hullPolys[i].mPlane.n[largestExtent]; + const float numn0 = r * hullPolys[i].mPlane.n[e0]; + const float numn1 = r * hullPolys[i].mPlane.n[e1]; + + float num = numBase - numn0 - numn1; + float ext = PxMax(fabsf(num*denBase), r); + if(ext < data.mInternal.mExtents[largestExtent]) + data.mInternal.mExtents[largestExtent] = ext; + + num = numBase - numn0 + numn1; + ext = PxMax(fabsf(num *denBase), r); + if(ext < data.mInternal.mExtents[largestExtent]) + data.mInternal.mExtents[largestExtent] = ext; + + num = numBase + numn0 + numn1; + ext = PxMax(fabsf(num *denBase), r); + if(ext < data.mInternal.mExtents[largestExtent]) + data.mInternal.mExtents[largestExtent] = ext; + + num = numBase + numn0 - numn1; + ext = PxMax(fabsf(num *denBase), r); + if(ext < data.mInternal.mExtents[largestExtent]) + data.mInternal.mExtents[largestExtent] = ext; + } + + // Refine the box along e0,e1 + for(PxU32 i = 0; i < data.mNbPolygons; i++) + { + const float denumAdd = hullPolys[i].mPlane.n[e0] + hullPolys[i].mPlane.n[e1]; + const float denumSub = hullPolys[i].mPlane.n[e0] - hullPolys[i].mPlane.n[e1]; + + const float numBase = -hullPolys[i].mPlane.d - hullPolys[i].mPlane.n.dot(data.mCenterOfMass); + const float numn0 = data.mInternal.mExtents[largestExtent] * hullPolys[i].mPlane.n[largestExtent]; + + if(!(-epsilon < denumAdd && denumAdd < epsilon)) + { + float num = numBase - numn0; + float ext = PxMax(fabsf(num/ denumAdd), r); + if(ext < data.mInternal.mExtents[e0]) + data.mInternal.mExtents[e0] = ext; + + num = numBase + numn0; + ext = PxMax(fabsf(num / denumAdd), r); + if(ext < data.mInternal.mExtents[e0]) + data.mInternal.mExtents[e0] = ext; + } + + if(!(-epsilon < denumSub && denumSub < epsilon)) + { + float num = numBase - numn0; + float ext = PxMax(fabsf(num / denumSub), r); + if(ext < data.mInternal.mExtents[e0]) + data.mInternal.mExtents[e0] = ext; + + num = numBase + numn0; + ext = PxMax(fabsf(num / denumSub), r); + if(ext < data.mInternal.mExtents[e0]) + data.mInternal.mExtents[e0] = ext; + } + } + data.mInternal.mExtents[e1] = data.mInternal.mExtents[e0]; +} + +////////////////////////////////////////////////////////////////////////// +// compute internal objects, get the internal extent and radius +void ConvexMeshBuilder::computeInternalObjects() +{ + const Gu::HullPolygonData* hullPolys = hullBuilder.mHullDataPolygons; + Gu::ConvexHullData& data = mHullData; + + // compute the internal radius + data.mInternal.mRadius = FLT_MAX; + for(PxU32 i=0;i +PX_INLINE PxPlane PlaneEquation(const T& t, const PxVec3* verts) +{ + const PxVec3& p0 = verts[t.v[0]]; + const PxVec3& p1 = verts[t.v[1]]; + const PxVec3& p2 = verts[t.v[2]]; + return PxPlane(p0, p1, p2); +} + +////////////////////////////////////////////////////////////////////////// +// negate plane +static PX_FORCE_INLINE void negatePlane(Gu::HullPolygonData& data) +{ + data.mPlane.n = -data.mPlane.n; + data.mPlane.d = -data.mPlane.d; +} + +////////////////////////////////////////////////////////////////////////// +// Inverse a buffer in-place +static bool inverseBuffer(PxU32 nbEntries, PxU8* entries) +{ + if(!nbEntries || !entries) return false; + + for(PxU32 i=0; i < (nbEntries>>1); i++) + Ps::swap(entries[i], entries[nbEntries-1-i]); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// Extracts a line-strip from a list of non-sorted line-segments (slow) +static bool findLineStrip(Ps::Array& lineStrip, const Ps::Array& lineSegments) +{ + // Ex: + // + // 4-2 + // 0-1 + // 2-3 + // 4-0 + // 7-3 + // 7-1 + // + // => 0-1-7-3-2-4-0 + + // 0-0-1-1-2-2-3-3-4-4-7-7 + + // 0-1 + // 0-4 + // 1-7 + // 2-3 + // 2-4 + // 3-7 + + // Naive implementation below + + Ps::Array Copy(lineSegments); + +RunAgain: + { + PxU32 nbSegments = Copy.size(); + for(PxU32 j=0;j remove both + PX_ASSERT(Copy.size()>=2); + Copy.remove(i); + Copy.remove(j); + goto RunAgain; + } + } + } + // Goes through when everything's fine + } + + PxU32 ref0 = 0xffffffff; + PxU32 ref1 = 0xffffffff; + if(Copy.size()>=1) + { + Pair* Segments = Copy.begin(); + if(Segments) + { + ref0 = Segments->id0; + ref1 = Segments->id1; + lineStrip.pushBack(ref0); + lineStrip.pushBack(ref1); + PX_ASSERT(Copy.size()>=1); + Copy.remove(0); + } + } + +Wrap: + // Look for same vertex ref in remaining segments + PxU32 nb = Copy.size(); + if(!nb) + { + // ### check the line is actually closed? + return true; + } + + for(PxU32 i=0;i r1 - x + lineStrip.pushBack(newRef0); // Output the other reference + ref0 = newRef1; + ref1 = newRef0; + Copy.remove(i); + goto Wrap; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +// Test for duplicate triangles +PX_COMPILE_TIME_ASSERT(sizeof(Gu::TriangleT)==sizeof(PxVec3)); // ... +static bool TestDuplicateTriangles(PxU32& nbFaces, Gu::TriangleT* faces, bool repair) +{ + if(!nbFaces || !faces) + return true; + + Gu::TriangleT* indices32 = reinterpret_cast*>(PxAlloca(nbFaces*sizeof(Gu::TriangleT))); + for(PxU32 i=0;i(indices32), nbFaces); + REDUCEDCLOUD rc; + reducer.Reduce(&rc); + if(rc.NbRVerts* curTri = reinterpret_cast*>(&rc.RVerts[i]); + faces[i].v[0] = curTri->v[0]; + faces[i].v[1] = curTri->v[1]; + faces[i].v[2] = curTri->v[2]; + } + } + return false; // Test failed + } + return true; // Test succeeded +} + +////////////////////////////////////////////////////////////////////////// +// plane culling test +static PX_FORCE_INLINE bool testCulling(const Gu::TriangleT& triangle, const PxVec3* verts, const PxVec3& center) +{ + const PxPlane plane(verts[triangle.v[0]], verts[triangle.v[1]], verts[triangle.v[2]]); + return plane.distance(center)>0.0f; +} + +////////////////////////////////////////////////////////////////////////// +// face normals test +static bool TestUnifiedNormals(PxU32 nbVerts, const PxVec3* verts, PxU32 nbFaces, Gu::TriangleT* faces, bool repair) +{ + if(!nbVerts || !verts || !nbFaces || !faces) + return false; + + // Unify normals so that all hull faces are well oriented + + // Compute geometric center - we need a vertex inside the hull + const float coeff = 1.0f / float(nbVerts); + PxVec3 geomCenter(0.0f, 0.0f, 0.0f); + for(PxU32 i=0;i* faces, PxU32& nbVerts, PxVec3* verts) +{ + // Brute force mesh cleaning. + // PT: I added this back on Feb-18-05 because it fixes bugs with hulls from QHull. + MeshCleaner cleaner(nbVerts, verts, nbFaces, faces->v, 0.0f); + if (!cleaner.mNbTris) + return false; + + nbVerts = cleaner.mNbVerts; + nbFaces = cleaner.mNbTris; + + PxMemCopy(verts, cleaner.mVerts, cleaner.mNbVerts*sizeof(PxVec3)); + + for (PxU32 i = 0; i < cleaner.mNbTris; i++) + { + faces[i].v[0] = cleaner.mIndices[i * 3 + 0]; + faces[i].v[1] = cleaner.mIndices[i * 3 + 1]; + faces[i].v[2] = cleaner.mIndices[i * 3 + 2]; + } + + // Get rid of duplicates + TestDuplicateTriangles(nbFaces, faces, true); + + // Unify normals + TestUnifiedNormals(nbVerts, verts, nbFaces, faces, true); + + // Remove zero-area triangles + // TestZeroAreaTriangles(nbFaces, faces, verts, true); + + // Unify normals again + TestUnifiedNormals(nbVerts, verts, nbFaces, faces, true); + + // Get rid of duplicates again + TestDuplicateTriangles(nbFaces, faces, true); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// check the newly constructed faces +static bool CheckFaces(PxU32 nbFaces, const Gu::TriangleT* faces, PxU32 nbVerts, const PxVec3* verts) +{ + // Remove const since we use functions that can do both testing & repairing. But we won't change the data. + Gu::TriangleT* f = const_cast*>(faces); + + // Test duplicate faces + if(!TestDuplicateTriangles(nbFaces, f, false)) + return false; + + // Test unified normals + if(!TestUnifiedNormals(nbVerts, verts, nbFaces, f, false)) + return false; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// compute the newell plane from the face verts +static bool computeNewellPlane(PxPlane& plane, PxU32 nbVerts, const PxU8* indices, const PxVec3* verts) +{ + if(!nbVerts || !indices || !verts) + return false; + + PxVec3 centroid(0,0,0), normal(0,0,0); + for(PxU32 i=nbVerts-1, j=0; j& polygon_data, const ConvexPolygonsBuilder& hull, Ps::Array& triangle_data, Ps::Array& redundantVertices) +{ + const PxU32* dFaces = reinterpret_cast(hull.getFaces()); + bool needToSplitPolygons = false; + + bool* polygonMarkers = reinterpret_cast(PxAlloca(nb_polygons*sizeof(bool))); + PxMemZero(polygonMarkers, nb_polygons*sizeof(bool)); + + bool* redundancyMarkers = reinterpret_cast(PxAlloca(redundantVertices.size()*sizeof(bool))); + PxMemZero(redundancyMarkers, redundantVertices.size()*sizeof(bool)); + + // parse through the redundant vertices and if we cannot remove them split just the actual polygon if possible + Ps::Array polygonsContainer; + PxU32 numEntries = 0; + for (PxU32 i = redundantVertices.size(); i--;) + { + numEntries = 0; + polygonsContainer.clear(); + // go through polygons, if polygons does have only 3 verts we cannot remove any vertex from it, try to decompose the second one + PxU32* Data = polygon_data.begin(); + for(PxU32 t=0;t=3); // Else something very wrong happened... + + for(PxU32 j=0;j newPolygon_data; + Ps::Array newTriangle_data; + PxU32 newNb_polygons = 0; + + PxU32* data = polygon_data.begin(); + PxU32* triData = triangle_data.begin(); + for(PxU32 i=0;i& polygon_data, const ConvexHull& hull) +* \param nb_polygons [out] number of extracted polygons +* \param polygon_data [out] polygon data: (Nb indices, index 0, index 1... index N)(Nb indices, index 0, index 1... index N)(...) +* \param hull [in] convex hull +* \param triangle_data [out] triangle data +* \param rendundantVertices [out] redundant vertices found inside the polygons - we want to remove them because of PCM +* \return true if success +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static bool extractHullPolygons(PxU32& nb_polygons, Ps::Array& polygon_data, const ConvexPolygonsBuilder& hull, Ps::Array* triangle_data, Ps::Array& rendundantVertices) +{ + PxU32 nbFaces = hull.getNbFaces(); + const PxVec3* hullVerts = hull.mHullDataHullVertices; + const PxU32 nbVertices = hull.mHull->mNbHullVertices; + + const PxU16* wFaces = NULL; + const PxU32* dFaces = reinterpret_cast(hull.getFaces()); + PX_ASSERT(wFaces || dFaces); + + ADJACENCIESCREATE create; + create.NbFaces = nbFaces; + create.DFaces = dFaces; + create.WFaces = wFaces; + create.Verts = hullVerts; + //Create.Epsilon = 0.01f; // PT: trying to fix Rob Elam bug. Also fixes TTP 2467 + // Create.Epsilon = 0.001f; // PT: for "Bruno's bug" + create.Epsilon = 0.005f; // PT: middle-ground seems to fix both. Expose this param? + + + AdjacenciesBuilder adj; + if(!adj.Init(create)) return false; + + PxU32 nbBoundaryEdges = adj.ComputeNbBoundaryEdges(); + if(nbBoundaryEdges) return false; // A valid hull shouldn't have open edges!! + + bool* markers = reinterpret_cast(PxAlloca(nbFaces*sizeof(bool))); + PxMemZero(markers, nbFaces*sizeof(bool)); + + PxU8* vertexMarkers = reinterpret_cast(PxAlloca(nbVertices*sizeof(PxU8))); + PxMemZero(vertexMarkers, nbVertices*sizeof(PxU8)); + + PxU32 currentFace = 0; // Start with first triangle + nb_polygons = 0; + do + { + currentFace = 0; + while(currentFace& indices, const AdjTriangle* faces, PxU32 current, bool* inMarkers) + { + if(inMarkers[current]) return; + inMarkers[current] = true; + + indices.pushBack(current); + const AdjTriangle& AT = faces[current]; + + // We can floodfill through inactive edges since the mesh is convex (inactive==planar) + if(!AT.HasActiveEdge01()) FloodFill(indices, faces, AT.GetAdjTri(EDGE01), inMarkers); + if(!AT.HasActiveEdge20()) FloodFill(indices, faces, AT.GetAdjTri(EDGE02), inMarkers); + if(!AT.HasActiveEdge12()) FloodFill(indices, faces, AT.GetAdjTri(EDGE12), inMarkers); + } + + static bool GetNeighborFace(PxU32 index,PxU32 triangleIndex,const AdjTriangle* faces, const PxU32* dfaces, PxU32& neighbor, PxU32& current) + { + PxU32 currentIndex = index; + PxU32 previousIndex = index; + bool firstFace = true; + bool next = true; + while (next) + { + const AdjTriangle& currentAT = faces[currentIndex]; + PxU32 refTr0 = dfaces[currentIndex*3 + 0]; + PxU32 refTr1 = dfaces[currentIndex*3 + 1]; + + PxU32 edge[2]; + edge[0] = 1; + edge[1] = 2; + if(triangleIndex == refTr0) + { + edge[0] = 0; + edge[1] = 1; + } + else + { + if(triangleIndex == refTr1) + { + edge[0] = 0; + edge[1] = 2; + } + } + + if(currentAT.HasActiveEdge(edge[0]) && currentAT.HasActiveEdge(edge[1])) + { + return false; + } + + if(!currentAT.HasActiveEdge(edge[0]) && !currentAT.HasActiveEdge(edge[1])) + { + // not interested in testing transition vertices + if(currentIndex == index) + { + return false; + } + + // transition one + for (PxU32 i = 0; i < 2; i++) + { + PxU32 testIndex = currentAT.GetAdjTri(SharedEdgeIndex(edge[i])); + + // exit if we circle around the vertex back to beginning + if(testIndex == index && previousIndex != index) + { + return false; + } + + if(testIndex != previousIndex) + { + // move to next + previousIndex = currentIndex; + currentIndex = testIndex; + break; + } + } + } + else + { + if(!currentAT.HasActiveEdge(edge[0])) + { + PxU32 t = edge[0]; + edge[0] = edge[1]; + edge[1] = t; + } + + if(currentAT.HasActiveEdge(edge[0])) + { + PxU32 testIndex = currentAT.GetAdjTri(SharedEdgeIndex(edge[0])); + if(firstFace) + { + firstFace = false; + } + else + { + neighbor = testIndex; + current = currentIndex; + return true; + } + } + + if(!currentAT.HasActiveEdge(edge[1])) + { + PxU32 testIndex = currentAT.GetAdjTri(SharedEdgeIndex(edge[1])); + if(testIndex != index) + { + previousIndex = currentIndex; + currentIndex = testIndex; + } + } + } + + } + + return false; + } + + static bool CheckFloodFillFace(PxU32 index,const AdjTriangle* faces, const PxU32* dfaces) + { + if(!dfaces) + return true; + + const AdjTriangle& checkedAT = faces[index]; + + PxU32 refTr0 = dfaces[index*3 + 0]; + PxU32 refTr1 = dfaces[index*3 + 1]; + PxU32 refTr2 = dfaces[index*3 + 2]; + + for (PxU32 i = 0; i < 3; i++) + { + if(!checkedAT.HasActiveEdge(i)) + { + PxU32 testTr0 = refTr1; + PxU32 testTr1 = refTr2; + PxU32 testIndex0 = 0; + PxU32 testIndex1 = 1; + if(i == 0) + { + testTr0 = refTr0; + testTr1 = refTr1; + testIndex0 = 1; + testIndex1 = 2; + } + else + { + if(i == 1) + { + testTr0 = refTr0; + testTr1 = refTr2; + testIndex0 = 0; + testIndex1 = 2; + } + } + + PxU32 adjFaceTested = checkedAT.GetAdjTri(SharedEdgeIndex(testIndex0)); + + PxU32 neighborIndex00; + PxU32 neighborIndex01; + bool found0 = GetNeighborFace(index,testTr0,faces,dfaces, neighborIndex00, neighborIndex01); + PxU32 neighborIndex10; + PxU32 neighborIndex11; + bool found1 = GetNeighborFace(adjFaceTested,testTr0,faces,dfaces, neighborIndex10, neighborIndex11); + + if(found0 && found1 && neighborIndex00 == neighborIndex11 && neighborIndex01 == neighborIndex10) + { + return false; + } + + adjFaceTested = checkedAT.GetAdjTri(SharedEdgeIndex(testIndex1)); + found0 = GetNeighborFace(index,testTr1,faces,dfaces,neighborIndex00,neighborIndex01); + found1 = GetNeighborFace(adjFaceTested,testTr1,faces,dfaces,neighborIndex10,neighborIndex11); + + if(found0 && found1 && neighborIndex00 == neighborIndex11 && neighborIndex01 == neighborIndex10) + { + return false; + } + + } + } + + return true; + } + + static bool CheckFloodFill(Ps::Array& indices,AdjTriangle* faces,bool* inMarkers, const PxU32* dfaces) + { + bool valid = true; + + for(PxU32 i=0;i indices; // Indices of triangles forming hull polygon + + bool doFill = true; + while (doFill) + { + Local::FloodFill(indices, adj.mFaces, currentFace, markers); + + doFill = Local::CheckFloodFill(indices,adj.mFaces,markers, dFaces); + } + + // Now it would be nice to recreate a closed linestrip, similar to silhouette extraction. The line is composed of active edges, this time. + + + Ps::Array activeSegments; + //Container ActiveSegments; + // Loop through triangles composing the polygon + for(PxU32 i=0;i lineStrip; + if(findLineStrip(lineStrip, activeSegments)) + { + PxU32 nb = lineStrip.size(); + if(nb) + { + const PxU32* entries = lineStrip.begin(); + PX_ASSERT(entries[0] == entries[nb-1]); // findLineStrip() is designed that way. Might not be what we want! + + // We get rid of the last (duplicated) index + polygon_data.pushBack(nb-1); + for (PxU32 i = 0; i < nb-1; i++) + { + vertexMarkers[entries[i]]++; + polygon_data.pushBack(entries[i]); + } + nb_polygons++; + + // Loop through vertices composing the line strip polygon end mark the redundant vertices inside the polygon + for(PxU32 i=0;ipushBack(indices.size()); + for (PxU32 j = 0; j < indices.size(); j++) + triangle_data->pushBack(indices[j]); + } + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Meshmerizer::extractHullPolygons: line strip extraction failed"); + return false; + } + } + } + while(currentFace!=nbFaces); + + for (PxU32 i = 0; i < nbVertices; i++) + { + if(vertexMarkers[i] < 3) + { + if(rendundantVertices.find(i) == rendundantVertices.end()) + rendundantVertices.pushBack(i); + } + } + + if(rendundantVertices.size() > 0 && triangle_data) + checkRedundantVertices(nb_polygons,polygon_data,hull,*triangle_data,rendundantVertices); + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +ConvexPolygonsBuilder::ConvexPolygonsBuilder(Gu::ConvexHullData* hull, const bool buildGRBData) + : ConvexHullBuilder(hull, buildGRBData), mNbHullFaces(0), mFaces(NULL) +{ +} + +////////////////////////////////////////////////////////////////////////// + +ConvexPolygonsBuilder::~ConvexPolygonsBuilder() +{ + PX_DELETE_POD(mFaces); +} + +////////////////////////////////////////////////////////////////////////// +// compute hull polygons from given hull triangles +bool ConvexPolygonsBuilder::computeHullPolygons(const PxU32& nbVerts,const PxVec3* verts, const PxU32& nbTriangles, const PxU32* triangles) +{ + PX_ASSERT(triangles); + PX_ASSERT(verts); + + mHullDataHullVertices = NULL; + mHullDataPolygons = NULL; + mHullDataVertexData8 = NULL; + mHullDataFacesByEdges8 = NULL; + mHullDataFacesByVertices8 = NULL; + + mNbHullFaces = nbTriangles; + mHull->mNbHullVertices = Ps::to8(nbVerts); + // allocate additional vec3 for V4 safe load in VolumeInteration + mHullDataHullVertices = reinterpret_cast(PX_ALLOC(sizeof(PxVec3) * mHull->mNbHullVertices + 1, "PxVec3")); + PxMemCopy(mHullDataHullVertices, verts, mHull->mNbHullVertices*sizeof(PxVec3)); + + mFaces = PX_NEW(HullTriangleData)[mNbHullFaces]; + for(PxU32 i=0;i* hullAsIndexedTriangle = reinterpret_cast*>(mFaces); + + // We don't trust the user at all... So, clean the hull. + PxU32 nbHullVerts = mHull->mNbHullVertices; + CleanFaces(mNbHullFaces, hullAsIndexedTriangle, nbHullVerts, mHullDataHullVertices); + PX_ASSERT(nbHullVerts<256); + mHull->mNbHullVertices = Ps::to8(nbHullVerts); + + // ...and then run the full tests again. + if(!CheckFaces(mNbHullFaces, hullAsIndexedTriangle, mHull->mNbHullVertices, mHullDataHullVertices)) + return false; + + // Transform triangles-to-polygons + if(!createPolygonData()) + return false; + + return checkHullPolygons(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Computes polygon data. +* \return true if success +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool ConvexPolygonsBuilder::createPolygonData() +{ + // Cleanup + mHull->mNbPolygons = 0; + PX_DELETE_POD(mHullDataVertexData8); + PX_DELETE_POD(mHullDataFacesByVertices8); + PX_FREE_AND_RESET(mHullDataPolygons); + + // Extract polygon data from triangle data + Ps::Array temp; + Ps::Array temp2; + Ps::Array rendundantVertices; + PxU32 nbPolygons; + if(!extractHullPolygons(nbPolygons, temp, *this, &temp2,rendundantVertices)) + return false; + + PxVec3* reducedHullDataHullVertices = mHullDataHullVertices; + PxU8 numReducedHullDataVertices = mHull->mNbHullVertices; + + if(rendundantVertices.size() > 0) + { + numReducedHullDataVertices = Ps::to8(mHull->mNbHullVertices - rendundantVertices.size()); + reducedHullDataHullVertices = static_cast (PX_ALLOC_TEMP(sizeof(PxVec3)*numReducedHullDataVertices,"Reduced vertices hull data")); + PxU8* remapTable = PX_NEW(PxU8)[mHull->mNbHullVertices]; + + PxU8 currentIndex = 0; + for (PxU8 i = 0; i < mHull->mNbHullVertices; i++) + { + if(rendundantVertices.find(i) == rendundantVertices.end()) + { + PX_ASSERT(currentIndex < numReducedHullDataVertices); + reducedHullDataHullVertices[currentIndex] = mHullDataHullVertices[i]; + remapTable[i] = currentIndex; + currentIndex++; + } + else + { + remapTable[i] = 0xFF; + } + } + + PxU32* data = temp.begin(); + for(PxU32 i=0;i=3); // Else something very wrong happened... + + for(PxU32 j=0;jmNbHullVertices); + data[j] = remapTable[data[j]]; + } + + data += nbVerts; + } + + PX_DELETE_POD(remapTable); + } + + if(nbPolygons>255) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder: convex hull has more than 255 polygons!"); + return false; + } + + // Precompute hull polygon structures + mHull->mNbPolygons = Ps::to8(nbPolygons); + mHullDataPolygons = reinterpret_cast(PX_ALLOC(sizeof(Gu::HullPolygonData)*mHull->mNbPolygons, "Gu::HullPolygonData")); + PxMemZero(mHullDataPolygons, sizeof(Gu::HullPolygonData)*mHull->mNbPolygons); + + // The winding hasn't been preserved so we need to handle this. Basically we need to "unify normals" + // exactly as we did at hull creation time - except this time we work on polygons + PxVec3 geomCenter; + computeGeomCenter(geomCenter, mNbHullFaces, mFaces); + + // Loop through polygons + // We have N polygons => remove N entries for number of vertices + PxU32 tmp = temp.size() - nbPolygons; + mHullDataVertexData8 = PX_NEW(PxU8)[tmp]; + PxU8* dest = mHullDataVertexData8; + const PxU32* data = temp.begin(); + const PxU32* triData = temp2.begin(); + for(PxU32 i=0;i=3); // Else something very wrong happened... + mHullDataPolygons[i].mNbVerts = Ps::to8(nbVerts); + + PxU32 index = 0; + for(PxU32 j=0;j& T = reinterpret_cast&>(mFaces[triIndex]); + const PxPlane PL = PlaneEquation(T, mHullDataHullVertices); + if(k==0 && PL.n.dot(mHullDataPolygons[i].mPlane.n) < 0.0f) + { + flip = true; + } + } + if(flip) + { + negatePlane(mHullDataPolygons[i]); + inverseBuffer(mHullDataPolygons[i].mNbVerts, dest); + } + + for(PxU32 j=0;jmNbHullVertices;j++) + { + float d = - (mHullDataPolygons[i].mPlane.n).dot(mHullDataHullVertices[j]); + if(d0.0f) + { + inverseBuffer(mHullDataPolygons[i].mNbVerts, dest); + + negatePlane(mHullDataPolygons[i]); + PX_ASSERT(mHullDataPolygons[i].mPlane.distance(geomCenter)<=0.0f); + } + + // Next one + data += nbVerts; // Skip vertex indices + dest += mHullDataPolygons[i].mNbVerts; + } + + if(reducedHullDataHullVertices != mHullDataHullVertices) + { + PxMemCopy(mHullDataHullVertices,reducedHullDataHullVertices,sizeof(PxVec3)*numReducedHullDataVertices); + PX_FREE(reducedHullDataHullVertices); + + mHull->mNbHullVertices = numReducedHullDataVertices; + } + + //calculate the vertex map table + if(!calculateVertexMapTable(nbPolygons)) + return false; + +#ifdef USE_PRECOMPUTED_HULL_PROJECTION + // Loop through polygons + for(PxU32 j=0;jmNbHullVertices; + const PxVec3* verts = mHullDataHullVertices; + Gu::HullPolygonData& polygon = mHullDataPolygons[j]; + PxReal min = PX_MAX_F32; + PxU8 minIndex = 0xff; + for (PxU8 i = 0; i < nbVerts; i++) + { + float dp = (*verts++).dot(polygon.mPlane.n); + if(dp < min) + { + min = dp; + minIndex = i; + } + } + polygon.mMinIndex = minIndex; + } +#endif + + // Triangulate newly created polygons to recreate a clean vertex cloud. + return createTrianglesFromPolygons(); +} + +////////////////////////////////////////////////////////////////////////// +// create back triangles from polygons +bool ConvexPolygonsBuilder::createTrianglesFromPolygons() +{ + if (!mHull->mNbPolygons || !mHullDataPolygons) return false; + + PxU32 maxNbTriangles = 0; + for (PxU32 i = 0; i < mHull->mNbPolygons; i++) + { + if (mHullDataPolygons[i].mNbVerts < 3) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder::CreateTrianglesFromPolygons: convex hull has a polygon with less than 3 vertices!"); + return false; + } + maxNbTriangles += mHullDataPolygons[i].mNbVerts - 2; + } + + HullTriangleData* tmpFaces = PX_NEW(HullTriangleData)[maxNbTriangles]; + + HullTriangleData* currFace = tmpFaces; + PxU32 nbTriangles = 0; + const PxU8* vertexData = mHullDataVertexData8; + const PxVec3* hullVerts = mHullDataHullVertices; + for (PxU32 i = 0; i < mHull->mNbPolygons; i++) + { + const PxU8* data = vertexData + mHullDataPolygons[i].mVRef8; + PxU32 nbVerts = mHullDataPolygons[i].mNbVerts; + + // Triangulate the polygon such that all all generated triangles have one and the same vertex + // in common. + // + // Make sure to avoid creating zero area triangles. Imagine the following polygon: + // + // 4 3 + // *------------------* + // | | + // *---*----*----*----* + // 5 6 0 1 2 + // + // Choosing vertex 0 as the shared vertex, the following zero area triangles will be created: + // [0 1 2], [0 5 6] + // + // Check for these triangles and discard them + // Note: Such polygons should only occur if the user defines the convex hull, i.e., the triangles + // of the convex shape, himself. If the convex hull is built from the vertices only, the + // hull algorithm removes the useless vertices. + // + for (PxU32 j = 0; j < nbVerts - 2; j++) + { + currFace->mRef[0] = data[0]; + currFace->mRef[1] = data[(j + 1) % nbVerts]; + currFace->mRef[2] = data[(j + 2) % nbVerts]; + + const PxVec3& p0 = hullVerts[currFace->mRef[0]]; + const PxVec3& p1 = hullVerts[currFace->mRef[1]]; + const PxVec3& p2 = hullVerts[currFace->mRef[2]]; + + const float area = ((p1 - p0).cross(p2 - p0)).magnitudeSquared(); + + if (area != 0.0f) // Else discard the triangle + { + nbTriangles++; + currFace++; + } + } + } + + PX_DELETE_POD(mFaces); + HullTriangleData* faces; + PX_ASSERT(nbTriangles <= maxNbTriangles); + if (maxNbTriangles == nbTriangles) + { + // No zero area triangles, hence the face buffer has correct size and can be used directly. + faces = tmpFaces; + } + else + { + // Resize face buffer because some triangles were discarded. + faces = PX_NEW(HullTriangleData)[nbTriangles]; + if (!faces) + { + PX_DELETE_POD(tmpFaces); + return false; + } + PxMemCopy(faces, tmpFaces, sizeof(HullTriangleData)*nbTriangles); + PX_DELETE_POD(tmpFaces); + } + mFaces = faces; + mNbHullFaces = nbTriangles; + // TODO: at this point useless vertices should be removed from the hull. The current fix is to initialize + // support vertices to known valid vertices, but it's not really convincing. + + // Re-unify normals + PxVec3 geomCenter; + computeGeomCenter(geomCenter, mNbHullFaces, mFaces); + + for (PxU32 i = 0; i < mNbHullFaces; i++) + { + const PxPlane P(hullVerts[mFaces[i].mRef[0]], + hullVerts[mFaces[i].mRef[1]], + hullVerts[mFaces[i].mRef[2]]); + if (P.distance(geomCenter) > 0.0f) + { + Flip(mFaces[i]); + } + } + return true; +} + diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h new file mode 100644 index 000000000..ee2f67452 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONVEXPOLYGONSBUILDER_H +#define PX_CONVEXPOLYGONSBUILDER_H + +#include "ConvexHullBuilder.h" + +namespace physx +{ + ////////////////////////////////////////////////////////////////////////// + // extended convex hull builder for a case where we build polygons from input triangles + class ConvexPolygonsBuilder : public ConvexHullBuilder + { + public: + ConvexPolygonsBuilder(Gu::ConvexHullData* hull, const bool buildGRBData); + ~ConvexPolygonsBuilder(); + + bool computeHullPolygons(const PxU32& nbVerts,const PxVec3* verts, const PxU32& nbTriangles, const PxU32* triangles); + + PX_INLINE PxU32 getNbFaces()const { return mNbHullFaces; } + PX_INLINE const HullTriangleData* getFaces() const { return mFaces; } + + + private: + bool createPolygonData(); + bool createTrianglesFromPolygons(); + + private: + PxU32 mNbHullFaces; //!< Number of faces in the convex hull + HullTriangleData* mFaces; //!< Triangles. + + }; +} + +#endif // PX_CONVEXHULLBUILDER_H + diff --git a/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp new file mode 100644 index 000000000..9b280d9e4 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp @@ -0,0 +1,2581 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "QuickHullConvexHullLib.h" +#include "ConvexHullUtils.h" + +#include "PsAllocator.h" +#include "PsUserAllocated.h" +#include "PsSort.h" +#include "PsMathUtils.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "PsBitUtils.h" + +#include "foundation/PxMath.h" +#include "foundation/PxPlane.h" +#include "foundation/PxBounds3.h" +#include "foundation/PxMemory.h" + +using namespace physx; + +namespace local +{ + ////////////////////////////////////////////////////////////////////////// + static const float MIN_ADJACENT_ANGLE = 3.0f; // in degrees - result wont have two adjacent facets within this angle of each other. + static const float PLANE_THICKNES = 3.0f * PX_EPS_F32; // points within this distance are considered on a plane + static const float MAXDOT_MINANG = cosf(Ps::degToRad(MIN_ADJACENT_ANGLE)); // adjacent angle for dot product tests + + ////////////////////////////////////////////////////////////////////////// + + struct QuickHullFace; + class ConvexHull; + class HullPlanes; + + ////////////////////////////////////////////////////////////////////////// + template + class MemBlock + { + public: + MemBlock(PxU32 preallocateSize) + : mPreallocateSize(preallocateSize), mCurrentBlock(0), mCurrentIndex(0) + { + PX_ASSERT(preallocateSize); + T* block = reinterpret_cast(PX_ALLOC_TEMP(sizeof(T)*preallocateSize, "Quickhull MemBlock")); + mBlocks.pushBack(block); + } + + MemBlock() + : mPreallocateSize(0), mCurrentBlock(0), mCurrentIndex(0) + { + } + + void init(PxU32 preallocateSize) + { + PX_ASSERT(preallocateSize); + mPreallocateSize = preallocateSize; + T* block = reinterpret_cast(PX_ALLOC_TEMP(sizeof(T)*preallocateSize, "Quickhull MemBlock")); + if(useIndexing) + { + for (PxU32 i = 0; i < mPreallocateSize; i++) + { + // placement new to index data + PX_PLACEMENT_NEW(&block[i], T)(i); + } + } + mBlocks.pushBack(block); + } + + ~MemBlock() + { + for (PxU32 i = 0; i < mBlocks.size(); i++) + { + PX_FREE(mBlocks[i]); + } + mBlocks.clear(); + } + + void reset() + { + for (PxU32 i = 0; i < mBlocks.size(); i++) + { + PX_FREE(mBlocks[i]); + } + mBlocks.clear(); + + mCurrentBlock = 0; + mCurrentIndex = 0; + + init(mPreallocateSize); + } + + T* getItem(PxU32 index) + { + const PxU32 block = index/mPreallocateSize; + const PxU32 itemIndex = index % mPreallocateSize; + PX_ASSERT(block <= mCurrentBlock); + PX_ASSERT(itemIndex < mPreallocateSize); + return &(mBlocks[block])[itemIndex]; + } + + T* getFreeItem() + { + PX_ASSERT(mPreallocateSize); + // check if we have enough space in block, otherwise allocate new block + if(mCurrentIndex < mPreallocateSize) + { + return &(mBlocks[mCurrentBlock])[mCurrentIndex++]; + } + else + { + T* block = reinterpret_cast(PX_ALLOC_TEMP(sizeof(T)*mPreallocateSize, "Quickhull MemBlock")); + mCurrentBlock++; + if (useIndexing) + { + for (PxU32 i = 0; i < mPreallocateSize; i++) + { + // placement new to index data + PX_PLACEMENT_NEW(&block[i], T)(mCurrentBlock*mPreallocateSize + i); + } + } + mBlocks.pushBack(block); + mCurrentIndex = 0; + return &(mBlocks[mCurrentBlock])[mCurrentIndex++]; + } + } + + private: + PxU32 mPreallocateSize; + PxU32 mCurrentBlock; + PxU32 mCurrentIndex; + Ps::Array mBlocks; + }; + + ////////////////////////////////////////////////////////////////////////// + // representation of quick hull vertex + struct QuickHullVertex + { + PxVec3 point; // point vector + PxU32 index; // point index for compare + float dist; // distance from plane if necessary + + QuickHullVertex* next; // link to next vertex, linked list used for conflict list + + PX_FORCE_INLINE bool operator==(const QuickHullVertex& vertex) const + { + return index == vertex.index ? true : false; + } + + PX_FORCE_INLINE bool operator <(const QuickHullVertex& vertex) const + { + return dist < vertex.dist ? true : false; + } + }; + + ////////////////////////////////////////////////////////////////////////// + // representation of quick hull half edge + struct QuickHullHalfEdge + { + QuickHullHalfEdge() : prev(NULL), next(NULL), twin(NULL), face(NULL), edgeIndex(0xFFFFFFFF) + { + } + + QuickHullHalfEdge(PxU32 ) + : prev(NULL), next(NULL), twin(NULL), face(NULL), edgeIndex(0xFFFFFFFF) + { + } + + QuickHullVertex tail; // tail vertex, head vertex is the tail of the twin + + QuickHullHalfEdge* prev; // previous edge + QuickHullHalfEdge* next; // next edge + QuickHullHalfEdge* twin; // twin/opposite edge + + QuickHullFace* face; // face where the edge belong + + PxU32 edgeIndex; // edge index used for edge creation + + PX_FORCE_INLINE const QuickHullVertex& getTail() const + { + return tail; + } + + PX_FORCE_INLINE const QuickHullVertex& getHead() const + { + PX_ASSERT(twin); + return twin->tail; + } + + PX_FORCE_INLINE void setTwin(QuickHullHalfEdge* edge) + { + twin = edge; + edge->twin = this; + } + + PX_FORCE_INLINE QuickHullFace* getOppositeFace() const + { + return twin->face; + } + + float getOppositeFaceDistance() const; + }; + + ////////////////////////////////////////////////////////////////////////// + + typedef Ps::Array QuickHullVertexArray; + typedef Ps::Array QuickHullHalfEdgeArray; + typedef Ps::Array QuickHullFaceArray; + + ////////////////////////////////////////////////////////////////////////// + // representation of quick hull face + struct QuickHullFace + { + enum FaceState + { + eVISIBLE, + eDELETED, + eNON_CONVEX + }; + + QuickHullHalfEdge* edge; // starting edge + PxU16 numEdges; // num edges on the face + QuickHullVertex* conflictList; // conflict list, used to determine unclaimed vertices + + PxVec3 normal; // Newell plane normal + float area; // face area + PxVec3 centroid; // face centroid + + float planeOffset; // Newell plane offset + float expandOffset; // used for plane expansion if vertex limit reached + + FaceState state; // face validity state + + QuickHullFace* nextFace; // used to indicate next free face in faceList + PxU32 index; // face index for compare identification + PxU8 outIndex; // face index used for output descriptor + + public: + QuickHullFace() + : edge(NULL), numEdges(0), conflictList(NULL), area(0.0f), planeOffset(0.0f), expandOffset(-FLT_MAX), + state(eVISIBLE), nextFace(NULL), outIndex(0) + { + } + + QuickHullFace(PxU32 ind) + : edge(NULL), numEdges(0), conflictList(NULL), area(0.0f), planeOffset(0.0f), expandOffset(-FLT_MAX), + state(eVISIBLE), nextFace(NULL), index(ind), outIndex(0) + { + } + + ~QuickHullFace() + { + } + + // get edge on index + PX_FORCE_INLINE QuickHullHalfEdge* getEdge(PxU32 i) const + { + QuickHullHalfEdge* he = edge; + while (i > 0) + { + he = he->next; + i--; + } + return he; + } + + // distance from a plane to provided point + PX_FORCE_INLINE float distanceToPlane(const PxVec3 p) const + { + return normal.dot(p) - planeOffset; + } + + // compute face normal and centroid + PX_FORCE_INLINE void computeNormalAndCentroid() + { + PX_ASSERT(edge); + normal = PxVec3(PxZero); + numEdges = 1; + + QuickHullHalfEdge* testEdge = edge; + QuickHullHalfEdge* startEdge = NULL; + float maxDist = 0.0f; + for (PxU32 i = 0; i < 3; i++) + { + const float d = (testEdge->tail.point - testEdge->next->tail.point).magnitudeSquared(); + if (d > maxDist) + { + maxDist = d; + startEdge = testEdge; + } + testEdge = testEdge->next; + } + PX_ASSERT(startEdge); + + QuickHullHalfEdge* he = startEdge->next; + const PxVec3& p0 = startEdge->tail.point; + const PxVec3 d = he->tail.point - p0; + centroid = startEdge->tail.point; + + do + { + numEdges++; + centroid += he->tail.point; + + normal += d.cross(he->next->tail.point - p0); + + he = he->next; + } while (he != startEdge); + + area = normal.normalize(); + centroid *= (1.0f / float(numEdges)); + + planeOffset = normal.dot(centroid); + } + + // merge adjacent face + bool mergeAdjacentFace(QuickHullHalfEdge* halfEdge, QuickHullFaceArray& discardedFaces); + + // check face consistency + bool checkFaceConsistency(); + + private: + // connect halfedges + QuickHullFace* connectHalfEdges(QuickHullHalfEdge* hedgePrev, QuickHullHalfEdge* hedge); + + // check if the face does have only 3 vertices + PX_FORCE_INLINE bool isTriangle() const + { + return numEdges == 3 ? true : false; + } + + }; + + ////////////////////////////////////////////////////////////////////////// + struct QuickHullResult + { + enum Enum + { + eSUCCESS, // ok + eZERO_AREA_TEST_FAILED, // area test failed for simplex + eVERTEX_LIMIT_REACHED, // vertex limit reached need to expand hull + ePOLYGONS_LIMIT_REACHED, // polygons hard limit reached + eFAILURE // general failure + }; + }; + + ////////////////////////////////////////////////////////////////////////// + // Quickhull base class holding the hull during construction + class QuickHull : public Ps::UserAllocated + { + PX_NOCOPY(QuickHull) + public: + + QuickHull(const PxCookingParams& params, const PxConvexMeshDesc& desc); + + ~QuickHull(); + + // preallocate the edges, faces, vertices + void preallocate(PxU32 numVertices); + + // parse the input verts, store them into internal format + void parseInputVertices(const PxVec3* verts, PxU32 numVerts); + + // release the hull and data + void releaseHull(); + + // sets the precomputed min/max data + void setPrecomputedMinMax(const QuickHullVertex* minVertex,const QuickHullVertex* maxVertex, const float tolerance,const float planeTolerance); + + // main entry function to build the hull from provided points + QuickHullResult::Enum buildHull(); + + PxU32 maxNumVertsPerFace() const; + + protected: + // compute min max verts + void computeMinMaxVerts(); + + // find the initial simplex + bool findSimplex(); + + // add the initial simplex + void addSimplex(QuickHullVertex* simplex, bool flipTriangle); + + // finds next point to add + QuickHullVertex* nextPointToAdd(QuickHullFace*& eyeFace); + + // adds point to the hull + bool addPointToHull(const QuickHullVertex* vertex, QuickHullFace& face, bool& addFailed); + + // creates new face from given triangles + QuickHullFace* createTriangle(const QuickHullVertex& v0, const QuickHullVertex& v1, const QuickHullVertex& v2); + + // adds point to the face conflict list + void addPointToFace(QuickHullFace& face, QuickHullVertex* vertex, float dist); + + // removes eye point from the face conflict list + void removeEyePointFromFace(QuickHullFace& face, const QuickHullVertex* vertex); + + // calculate the horizon fro the eyePoint against a given face + void calculateHorizon(const PxVec3& eyePoint, QuickHullHalfEdge* edge, QuickHullFace& face, QuickHullHalfEdgeArray& horizon, QuickHullFaceArray& removedFaces); + + // adds new faces from given horizon and eyePoint + void addNewFacesFromHorizon(const QuickHullVertex* eyePoint, const QuickHullHalfEdgeArray& horizon, QuickHullFaceArray& newFaces); + + // merge adjacent face + bool doAdjacentMerge(QuickHullFace& face, bool mergeWrtLargeFace, bool& mergeFailed); + + // merge adjacent face doing normal test + bool doPostAdjacentMerge(QuickHullFace& face, const float minAngle); + + // delete face points + void deleteFacePoints(QuickHullFace& faceToDelete, QuickHullFace* absorbingFace); + + // resolve unclaimed points + void resolveUnclaimedPoints(const QuickHullFaceArray& newFaces); + + // merges polygons with similar normals + void postMergeHull(); + + // check if 2 faces can be merged + bool canMergeFaces(const QuickHullHalfEdge& he); + + // get next free face + PX_FORCE_INLINE QuickHullFace* getFreeHullFace() + { + return mFreeFaces.getFreeItem(); + } + + // get next free half edge + PX_FORCE_INLINE QuickHullHalfEdge* getFreeHullHalfEdge() + { + return mFreeHalfEdges.getFreeItem(); + } + + PX_FORCE_INLINE PxU32 getNbHullVerts() { return mOutputNumVertices; } + + protected: + friend class physx::QuickHullConvexHullLib; + + const PxCookingParams& mCookingParams; // cooking params + const PxConvexMeshDesc& mConvexDesc; // convex desc + + PxVec3 mInteriorPoint; // interior point for int/ext tests + + PxU32 mMaxVertices; // maximum number of vertices (can be different as we may add vertices during the cleanup + PxU32 mNumVertices; // actual number of input vertices + PxU32 mOutputNumVertices; // num vertices of the computed hull + PxU32 mTerminalVertex; // in case we failed to generate hull in a regular run we set the terminal vertex and rerun + + QuickHullVertex* mVerticesList; // vertices list preallocated + MemBlock mFreeHalfEdges; // free half edges + MemBlock mFreeFaces; // free faces + + QuickHullFaceArray mHullFaces; // actual hull faces, contains also invalid and not used faces + PxU32 mNumHullFaces; // actual number of hull faces + + bool mPrecomputedMinMax; // if we got the precomputed min/max values + QuickHullVertex mMinVertex[3]; // min vertex + QuickHullVertex mMaxVertex[3]; // max vertex + float mTolerance; // hull tolerance, used for plane thickness and merge strategy + float mPlaneTolerance; // used for post merge stage + + QuickHullVertexArray mUnclaimedPoints; // holds temp unclaimed points + + QuickHullHalfEdgeArray mHorizon; // array for horizon computation + QuickHullFaceArray mNewFaces; // new faces created during horizon computation + QuickHullFaceArray mRemovedFaces; // removd faces during horizon computation + QuickHullFaceArray mDiscardedFaces; // discarded faces during face merging + }; + + ////////////////////////////////////////////////////////////////////////// + // return the distance from opposite face + float QuickHullHalfEdge::getOppositeFaceDistance() const + { + PX_ASSERT(face); + PX_ASSERT(twin); + return face->distanceToPlane(twin->face->centroid); + } + + ////////////////////////////////////////////////////////////////////////// + // merge adjacent face from provided half edge. + // 1. set new half edges + // 2. connect the new half edges - check we did not produced redundant triangles, discard them + // 3. recompute the plane and check consistency + // Returns false if merge failed + bool QuickHullFace::mergeAdjacentFace(QuickHullHalfEdge* hedgeAdj, QuickHullFaceArray& discardedFaces) + { + QuickHullFace* oppFace = hedgeAdj->getOppositeFace(); + + discardedFaces.pushBack(oppFace); + oppFace->state = QuickHullFace::eDELETED; + + QuickHullHalfEdge* hedgeOpp = hedgeAdj->twin; + + QuickHullHalfEdge* hedgeAdjPrev = hedgeAdj->prev; + QuickHullHalfEdge* hedgeAdjNext = hedgeAdj->next; + QuickHullHalfEdge* hedgeOppPrev = hedgeOpp->prev; + QuickHullHalfEdge* hedgeOppNext = hedgeOpp->next; + + // check if we are lining up with the face in adjPrev dir + QuickHullHalfEdge* breakEdge = hedgeAdjPrev; + while (hedgeAdjPrev->getOppositeFace() == oppFace) + { + hedgeAdjPrev = hedgeAdjPrev->prev; + hedgeOppNext = hedgeOppNext->next; + + // Edge case merge face is degenerated and we need to abort merging + if (hedgeAdjPrev == breakEdge) + { + return false; + } + } + + // check if we are lining up with the face in adjNext dir + breakEdge = hedgeAdjNext; + while (hedgeAdjNext->getOppositeFace() == oppFace) + { + hedgeOppPrev = hedgeOppPrev->prev; + hedgeAdjNext = hedgeAdjNext->next; + + // Edge case merge face is degenerated and we need to abort merging + if (hedgeAdjNext == breakEdge) + { + return false; + } + } + + QuickHullHalfEdge* hedge; + + // set new face owner for the line up edges + for (hedge = hedgeOppNext; hedge != hedgeOppPrev->next; hedge = hedge->next) + { + hedge->face = this; + } + + // if we are about to delete the shared edge, check if its not the starting edge of the face + if (hedgeAdj == edge) + { + edge = hedgeAdjNext; + } + + // handle the half edges at the head + QuickHullFace* discardedFace; + discardedFace = connectHalfEdges(hedgeOppPrev, hedgeAdjNext); + if (discardedFace != NULL) + { + discardedFaces.pushBack(discardedFace); + } + + // handle the half edges at the tail + discardedFace = connectHalfEdges(hedgeAdjPrev, hedgeOppNext); + if (discardedFace != NULL) + { + discardedFaces.pushBack(discardedFace); + } + + computeNormalAndCentroid(); + PX_ASSERT(checkFaceConsistency()); + + return true; + } + + ////////////////////////////////////////////////////////////////////////// + // connect half edges of 2 adjacent faces + // if we find redundancy - edges are in a line, we drop the addional face if it is just a skinny triangle + QuickHullFace* QuickHullFace::connectHalfEdges(QuickHullHalfEdge* hedgePrev, QuickHullHalfEdge* hedge) + { + QuickHullFace* discardedFace = NULL; + + // redundant edge - can be in a line + if (hedgePrev->getOppositeFace() == hedge->getOppositeFace()) + { + // then there is a redundant edge that we can get rid off + QuickHullFace* oppFace = hedge->getOppositeFace(); + QuickHullHalfEdge* hedgeOpp; + + if (hedgePrev == edge) + { + edge = hedge; + } + + // check if its not a skinny face with just 3 vertices - 3 edges + if (oppFace->isTriangle()) + { + // then we can get rid of the opposite face altogether + hedgeOpp = hedge->twin->prev->twin; + + oppFace->state = QuickHullFace::eDELETED; + discardedFace = oppFace; + } + else + { + // if not triangle, merge the 2 opposite halfedges into one + hedgeOpp = hedge->twin->next; + + if (oppFace->edge == hedgeOpp->prev) + { + oppFace->edge = hedgeOpp; + } + hedgeOpp->prev = hedgeOpp->prev->prev; + hedgeOpp->prev->next = hedgeOpp; + } + + hedge->prev = hedgePrev->prev; + hedge->prev->next = hedge; + + hedge->twin = hedgeOpp; + hedgeOpp->twin = hedge; + + // oppFace was modified, so need to recompute + oppFace->computeNormalAndCentroid(); + } + else + { + // just merge the halfedges + hedgePrev->next = hedge; + hedge->prev = hedgePrev; + } + return discardedFace; + } + + ////////////////////////////////////////////////////////////////////////// + // check face consistency + bool QuickHullFace::checkFaceConsistency() + { + // do a sanity check on the face + QuickHullHalfEdge* hedge = edge; + PxU32 numv = 0; + + // check degenerate face + do + { + numv++; + hedge = hedge->next; + } while (hedge != edge); + + // degenerate face found + PX_ASSERT(numv > 2); + + numv = 0; + hedge = edge; + do + { + QuickHullHalfEdge* hedgeOpp = hedge->twin; + + // check if we have twin set + PX_ASSERT(hedgeOpp != NULL); + + // twin for the twin must be the original edge + PX_ASSERT(hedgeOpp->twin == hedge); + + QuickHullFace* oppFace = hedgeOpp->face; + + PX_UNUSED(oppFace); + + // opposite edge face must be set and valid + PX_ASSERT(oppFace != NULL); + PX_ASSERT(oppFace->state != QuickHullFace::eDELETED); + + // edges face must be this one + PX_ASSERT(hedge->face == this); + + hedge = hedge->next; + } while (hedge != edge); + + return true; + } + + ////////////////////////////////////////////////////////////////////////// + + QuickHull::QuickHull(const PxCookingParams& params, const PxConvexMeshDesc& desc) + : mCookingParams(params), mConvexDesc(desc), mOutputNumVertices(0), mTerminalVertex(0xFFFFFFFF), mVerticesList(NULL), mNumHullFaces(0), mPrecomputedMinMax(false), + mTolerance(-1.0f), mPlaneTolerance(-1.0f) + { + } + + ////////////////////////////////////////////////////////////////////////// + + QuickHull::~QuickHull() + { + } + + ////////////////////////////////////////////////////////////////////////// + // sets the precomputed min/max values + void QuickHull::setPrecomputedMinMax(const QuickHullVertex* minVertex,const QuickHullVertex* maxVertex, const float tolerance,const float planeTolerance) + { + for (PxU32 i = 0; i < 3; i++) + { + mMinVertex[i] = minVertex[i]; + mMaxVertex[i] = maxVertex[i]; + } + + mTolerance = tolerance; + mPlaneTolerance = planeTolerance; + + mPrecomputedMinMax = true; + } + + ////////////////////////////////////////////////////////////////////////// + // preallocate internal buffers + void QuickHull::preallocate(PxU32 numVertices) + { + PX_ASSERT(numVertices > 0); + + // max num vertices = numVertices + mMaxVertices = PxMax(PxU32(8), numVertices); // 8 is min, since we can expand to AABB during the clean vertices phase + mVerticesList = reinterpret_cast (PX_ALLOC_TEMP(sizeof(QuickHullVertex)*mMaxVertices, "QuickHullVertex")); + + // estimate the max half edges + PxU32 maxHalfEdges = (3 * mMaxVertices - 6) * 3; + mFreeHalfEdges.init(maxHalfEdges); + + // estimate the max faces + PxU32 maxFaces = (2 * mMaxVertices - 4); + mFreeFaces.init(maxFaces*2); + + mHullFaces.reserve(maxFaces); + mUnclaimedPoints.reserve(numVertices); + + mNewFaces.reserve(32); + mRemovedFaces.reserve(32); + mDiscardedFaces.reserve(32); + mHorizon.reserve(PxMin(numVertices,PxU32(128))); + } + + ////////////////////////////////////////////////////////////////////////// + // release internal buffers + void QuickHull::releaseHull() + { + if (mVerticesList) + { + PX_FREE_AND_RESET(mVerticesList); + } + mHullFaces.clear(); + } + + ////////////////////////////////////////////////////////////////////////// + // returns the maximum number of vertices on a face + PxU32 QuickHull::maxNumVertsPerFace() const + { + PxU32 numFaces = mHullFaces.size(); + PxU32 maxVerts = 0; + for (PxU32 i = 0; i < numFaces; i++) + { + const local::QuickHullFace& face = *mHullFaces[i]; + if (face.state == local::QuickHullFace::eVISIBLE) + { + if (face.numEdges > maxVerts) + maxVerts = face.numEdges; + } + } + return maxVerts; + } + + ////////////////////////////////////////////////////////////////////////// + // parse the input vertices and store them in the hull + void QuickHull::parseInputVertices(const PxVec3* verts, PxU32 numVerts) + { + PX_ASSERT(verts); + PX_ASSERT(numVerts <= mMaxVertices); + + mNumVertices = numVerts; + for (PxU32 i = 0; i < numVerts; i++) + { + mVerticesList[i].point = verts[i]; + mVerticesList[i].index = i; + } + } + + ////////////////////////////////////////////////////////////////////////// + // compute min max verts + void QuickHull::computeMinMaxVerts() + { + for (PxU32 i = 0; i < 3; i++) + { + mMinVertex[i] = mVerticesList[0]; + mMaxVertex[i] = mVerticesList[0]; + } + + PxVec3 max = mVerticesList[0].point; + PxVec3 min = mVerticesList[0].point; + + // get the max min vertices along the x,y,z + for (PxU32 i = 1; i < mNumVertices; i++) + { + const QuickHullVertex& testVertex = mVerticesList[i]; + const PxVec3& testPoint = testVertex.point; + if (testPoint.x > max.x) + { + max.x = testPoint.x; + mMaxVertex[0] = testVertex; + } + else if (testPoint.x < min.x) + { + min.x = testPoint.x; + mMinVertex[0] = testVertex; + } + + if (testPoint.y > max.y) + { + max.y = testPoint.y; + mMaxVertex[1] = testVertex; + } + else if (testPoint.y < min.y) + { + min.y = testPoint.y; + mMinVertex[1] = testVertex; + } + + if (testPoint.z > max.z) + { + max.z = testPoint.z; + mMaxVertex[2] = testVertex; + } + else if (testPoint.z < min.z) + { + min.z = testPoint.z; + mMinVertex[2] = testVertex; + } + } + + const float sizeTol = (max.x-min.x + max.y - min.y + max.z - min.z)*0.5f; + mTolerance = PxMax(local::PLANE_THICKNES * sizeTol, local::PLANE_THICKNES); + mPlaneTolerance = PxMax(mCookingParams.planeTolerance * sizeTol, mCookingParams.planeTolerance); + } + + ////////////////////////////////////////////////////////////////////////// + // find the initial simplex + // 1. search in max axis from compute min,max + // 2. 3rd point is the furthest vertex from the initial line + // 3. 4th vertex is along the line, 3rd vertex normal + bool QuickHull::findSimplex() + { + float max = 0; + PxU32 imax = 0; + + for (PxU32 i = 0; i < 3; i++) + { + float diff = mMaxVertex[i].point[i] - mMinVertex[i].point[i]; + if (diff > max) + { + max = diff; + imax = i; + } + } + + if (max <= mTolerance) + { + // should not happen as we clear the vertices before and expand them if they are really close to each other + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "QuickHullConvexHullLib::findSimplex: Simplex input points appers to be almost at the same place"); + return false; + } + + QuickHullVertex simplex[4]; + + // set first two vertices to be those with the greatest + // one dimensional separation + simplex[0] = mMaxVertex[imax]; + simplex[1] = mMinVertex[imax]; + + // set third vertex to be the vertex farthest from + // the line between simplex[0] and simplex[1] + PxVec3 normal; + float maxDist = 0; + PxVec3 u01 = (simplex[1].point - simplex[0].point); + u01.normalize(); + + for (PxU32 i = 0; i < mNumVertices; i++) + { + const QuickHullVertex& testVert = mVerticesList[i]; + const PxVec3& testPoint = testVert.point; + const PxVec3 diff = testPoint - simplex[0].point; + const PxVec3 xprod = u01.cross(diff); + const float lenSqr = xprod.magnitudeSquared(); + if (lenSqr > maxDist && testVert.index != simplex[0].index && testVert.index != simplex[1].index) + { + maxDist = lenSqr; + simplex[2] = testVert; + normal = xprod; + } + } + + if (PxSqrt(maxDist) <= mTolerance) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "QuickHullConvexHullLib::findSimplex: Simplex input points appers to be colinear."); + return false; + } + normal.normalize(); + + // set the forth vertex in the normal direction + const float d0 = simplex[2].point.dot(normal); + maxDist = 0.0f; + for (PxU32 i = 0; i < mNumVertices; i++) + { + const QuickHullVertex& testVert = mVerticesList[i]; + const PxVec3& testPoint = testVert.point; + const float dist = PxAbs(testPoint.dot(normal) - d0); + if (dist > maxDist && testVert.index != simplex[0].index && + testVert.index != simplex[1].index && testVert.index != simplex[2].index) + { + maxDist = dist; + simplex[3] = testVert; + } + } + + if (PxAbs(maxDist) <= mTolerance) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "QuickHullConvexHullLib::findSimplex: Simplex input points appers to be coplanar."); + return false; + } + + // now create faces from those triangles + addSimplex(&simplex[0], simplex[3].point.dot(normal) - d0 < 0); + + return true; + } + + ////////////////////////////////////////////////////////////////////////// + // create triangle from given vertices, produce new face and connect the half edges + QuickHullFace* QuickHull::createTriangle(const QuickHullVertex& v0, const QuickHullVertex& v1, const QuickHullVertex& v2) + { + QuickHullFace* face = getFreeHullFace(); + + QuickHullHalfEdge* he0 = getFreeHullHalfEdge(); + he0->face = face; + he0->tail = v0; + + QuickHullHalfEdge* he1 = getFreeHullHalfEdge(); + he1->face = face; + he1->tail = v1; + + QuickHullHalfEdge* he2 = getFreeHullHalfEdge(); + he2->face = face; + he2->tail = v2; + + he0->prev = he2; + he0->next = he1; + he1->prev = he0; + he1->next = he2; + he2->prev = he1; + he2->next = he0; + + face->edge = he0; + face->nextFace = NULL; + + // compute the normal and offset + face->computeNormalAndCentroid(); + return face; + } + + + ////////////////////////////////////////////////////////////////////////// + // add initial simplex to the quickhull + // construct triangles from the simplex points and connect them with half edges + void QuickHull::addSimplex(QuickHullVertex* simplex, bool flipTriangle) + { + PX_ASSERT(simplex); + + // get interior point + PxVec3 vectorSum = simplex[0].point; + for (PxU32 i = 1; i < 4; i++) + { + vectorSum += simplex[i].point; + } + mInteriorPoint = vectorSum / 4.0f; + + QuickHullFace* tris[4]; + // create the triangles from the initial simplex + if (flipTriangle) + { + tris[0] = createTriangle(simplex[0], simplex[1], simplex[2]); + tris[1] = createTriangle(simplex[3], simplex[1], simplex[0]); + tris[2] = createTriangle(simplex[3], simplex[2], simplex[1]); + tris[3] = createTriangle(simplex[3], simplex[0], simplex[2]); + + for (PxU32 i = 0; i < 3; i++) + { + PxU32 k = (i + 1) % 3; + tris[i + 1]->getEdge(1)->setTwin(tris[k + 1]->getEdge(0)); + tris[i + 1]->getEdge(2)->setTwin(tris[0]->getEdge(k)); + } + } + else + { + tris[0] = createTriangle(simplex[0], simplex[2], simplex[1]); + tris[1] = createTriangle(simplex[3], simplex[0], simplex[1]); + tris[2] = createTriangle(simplex[3], simplex[1], simplex[2]); + tris[3] = createTriangle(simplex[3], simplex[2], simplex[0]); + + for (PxU32 i = 0; i < 3; i++) + { + PxU32 k = (i + 1) % 3; + tris[i + 1]->getEdge(0)->setTwin(tris[k + 1]->getEdge(1)); + tris[i + 1]->getEdge(2)->setTwin(tris[0]->getEdge((3 - i) % 3)); + } + } + + // push back the first 4 faces created from the simplex + for (PxU32 i = 0; i < 4; i++) + { + mHullFaces.pushBack(tris[i]); + } + mNumHullFaces = 4; + + // go through points and add point to faces if they are on the plane + for (PxU32 i = 0; i < mNumVertices; i++) + { + const QuickHullVertex& v = mVerticesList[i]; + + if (v == simplex[0] || v == simplex[1] || v == simplex[2] || v == simplex[3]) + { + continue; + } + + float maxDist = mTolerance; + QuickHullFace* maxFace = NULL; + for (PxU32 k = 0; k < 4; k++) + { + const float dist = tris[k]->distanceToPlane(v.point); + if (dist > maxDist) + { + maxFace = tris[k]; + maxDist = dist; + } + } + + if (maxFace != NULL) + { + addPointToFace(*maxFace, &mVerticesList[i], maxDist); + } + } + } + + ////////////////////////////////////////////////////////////////////////// + // adds a point to the conflict list + // the trick here is to store the most furthest point as the last, thats the only one we care about + // the rest is not important, we just need to store them and claim to new faces later, if the + // faces most furthest point is the current global maximum + void QuickHull::addPointToFace(QuickHullFace& face, QuickHullVertex* vertex, float dist) + { + // if we dont have a conflict list, store the vertex as the first one in the conflict list + vertex->dist = dist; + if(!face.conflictList) + { + face.conflictList = vertex; + vertex->dist = dist; + vertex->next = NULL; + return; + } + + PX_ASSERT(face.conflictList); + + // this is not the furthest vertex, store it as next in the linked list + if (face.conflictList->dist > dist) + { + vertex->next = face.conflictList->next; + face.conflictList->next = vertex; + } + else + { + // this is the furthest vertex, store it as first in the linked list + vertex->next = face.conflictList; + face.conflictList = vertex; + } + } + + ////////////////////////////////////////////////////////////////////////// + // removes eye point from a conflict list + // we know that the vertex must the last, as we store it at the back, so just popback() + void QuickHull::removeEyePointFromFace(QuickHullFace& face, const QuickHullVertex* vertex) + { + PX_UNUSED(vertex); + // the picked vertex should always be the first in the linked list + PX_ASSERT(face.conflictList == vertex); + + face.conflictList = face.conflictList->next; + } + + ////////////////////////////////////////////////////////////////////////// + // merge polygons with similar normals + void QuickHull::postMergeHull() + { + // merge faces with similar normals + for (PxU32 i = 0; i < mHullFaces.size(); i++) + { + QuickHullFace& face = *mHullFaces[i]; + + if (face.state == QuickHullFace::eVISIBLE) + { + PX_ASSERT(face.checkFaceConsistency()); + while (doPostAdjacentMerge(face, local::MAXDOT_MINANG)); + } + } + } + + ////////////////////////////////////////////////////////////////////////// + // builds the hull + // 1. find the initial simplex + // 2. check if simplex has a valid area + // 3. add vertices to the hull. We add vertex most furthest from the hull + // 4. terminate if hull limit reached or we have added all vertices + QuickHullResult::Enum QuickHull::buildHull() + { + QuickHullVertex* eyeVtx = NULL; + QuickHullFace* eyeFace; + + // compute the vertex min max along x,y,z + if(!mPrecomputedMinMax) + computeMinMaxVerts(); + + // find the initial simplex of the hull + if (!findSimplex()) + { + return QuickHullResult::eFAILURE; + } + + // simplex area test + const bool useAreaTest = mConvexDesc.flags & PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES ? true : false; + const float areaEpsilon = mCookingParams.areaTestEpsilon * 2.0f; + if (useAreaTest) + { + for (PxU32 i = 0; i < mHullFaces.size(); i++) + { + if (mHullFaces[i]->area < areaEpsilon) + { + return QuickHullResult::eZERO_AREA_TEST_FAILED; + } + } + } + + // add points to the hull + PxU32 numVerts = 4; // initial vertex count - simplex vertices + while ((eyeVtx = nextPointToAdd(eyeFace)) != NULL && eyeVtx->index != mTerminalVertex) + { + // if plane shifting vertex limit, we need the reduced hull + if((mConvexDesc.flags & PxConvexFlag::ePLANE_SHIFTING) && (numVerts >= mConvexDesc.vertexLimit)) + break; + + bool addFailed = false; + PX_ASSERT(eyeFace); + if (!addPointToHull(eyeVtx, *eyeFace, addFailed)) + { + mOutputNumVertices = numVerts; + // we hit the polygons hard limit + return QuickHullResult::ePOLYGONS_LIMIT_REACHED; + } + // We failed to add the vertex, store the vertex as terminal vertex and re run the hull generator + if(addFailed) + { + // set the terminal vertex + mTerminalVertex = eyeVtx->index; + + // reset the edges/faces memory + mFreeHalfEdges.reset(); + mFreeFaces.reset(); + + // reset the hull state + mHullFaces.clear(); + mNumHullFaces = 0; + mUnclaimedPoints.clear(); + mHorizon.clear(); + mNewFaces.clear(); + mRemovedFaces.clear(); + mDiscardedFaces.clear(); + + // rerun the hull generator + return buildHull(); + } + numVerts++; + } + mOutputNumVertices = numVerts; + + // vertex limit has been reached. We did not stopped the iteration, since we + // will use the produced hull to compute OBB from it and use the planes + // to slice the initial OBB + if (numVerts > mConvexDesc.vertexLimit) + { + return QuickHullResult::eVERTEX_LIMIT_REACHED; + } + + return QuickHullResult::eSUCCESS; + } + + ////////////////////////////////////////////////////////////////////////// + // finds the best point to add to the hull + // go through the faces conflict list and pick the global maximum + QuickHullVertex* QuickHull::nextPointToAdd(QuickHullFace*& eyeFace) + { + QuickHullVertex* eyeVtx = NULL; + QuickHullFace* eyeF = NULL; + float maxDist = mPlaneTolerance; + for (PxU32 i = 0; i < mHullFaces.size(); i++) + { + if (mHullFaces[i]->state == QuickHullFace::eVISIBLE && mHullFaces[i]->conflictList) + { + const float dist = mHullFaces[i]->conflictList->dist; + if (maxDist < dist) + { + maxDist = dist; + eyeVtx = mHullFaces[i]->conflictList; + eyeF = mHullFaces[i]; + } + } + } + + eyeFace = eyeF; + return eyeVtx; + } + + ////////////////////////////////////////////////////////////////////////// + // adds vertex to the hull + // sets addFailed to true if we failed to add a point because the merging failed + // this can happen as the face plane equation changes and some faces might become concave + // returns false if the new faces count would hit the hull face hard limit (255) + bool QuickHull::addPointToHull(const QuickHullVertex* eyeVtx, QuickHullFace& eyeFace, bool& addFailed) + { + addFailed = false; + + // removes the eyePoint from the conflict list + removeEyePointFromFace(eyeFace, eyeVtx); + + // calculates the horizon from the eyePoint + calculateHorizon(eyeVtx->point, NULL, eyeFace, mHorizon, mRemovedFaces); + + // check if we dont hit the polygons hard limit + if (mNumHullFaces + mHorizon.size() > 255) + { + // make the faces visible again and quit + for (PxU32 i = 0; i < mRemovedFaces.size(); i++) + { + mRemovedFaces[i]->state = QuickHullFace::eVISIBLE; + } + mNumHullFaces += mRemovedFaces.size(); + return false; + } + + // adds new faces from given horizon and eyePoint + addNewFacesFromHorizon(eyeVtx, mHorizon, mNewFaces); + + bool mergeFailed = false; + // first merge pass ... merge faces which are non-convex + // as determined by the larger face + for (PxU32 i = 0; i < mNewFaces.size(); i++) + { + QuickHullFace& face = *mNewFaces[i]; + + if (face.state == QuickHullFace::eVISIBLE) + { + PX_ASSERT(face.checkFaceConsistency()); + while (doAdjacentMerge(face, true, mergeFailed)); + } + } + if (mergeFailed) + { + addFailed = true; + return true; + } + + // second merge pass ... merge faces which are non-convex + // wrt either face + for (PxU32 i = 0; i < mNewFaces.size(); i++) + { + QuickHullFace& face = *mNewFaces[i]; + if (face.state == QuickHullFace::eNON_CONVEX) + { + face.state = QuickHullFace::eVISIBLE; + while (doAdjacentMerge(face, false, mergeFailed)); + } + } + if (mergeFailed) + { + addFailed = true; + return true; + } + + resolveUnclaimedPoints(mNewFaces); + + mHorizon.clear(); + mNewFaces.clear(); + mRemovedFaces.clear(); + + return true; + } + + ////////////////////////////////////////////////////////////////////////// + // merge adjacent faces + // We merge 2 adjacent faces if they lie on the same thick plane defined by the mTolerance + // we do this in 2 steps to ensure we dont leave non-convex faces + bool QuickHull::doAdjacentMerge(QuickHullFace& face, bool mergeWrtLargeFace, bool& mergeFailed) + { + QuickHullHalfEdge* hedge = face.edge; + mergeFailed = false; + + bool convex = true; + do + { + const QuickHullFace& oppFace = *hedge->getOppositeFace(); + bool merge = false; + + if (mergeWrtLargeFace) + { + // merge faces if they are parallel or non-convex + // wrt to the larger face; otherwise, just mark + // the face non-convex for the second pass. + if (face.area > oppFace.area) + { + if (hedge->getOppositeFaceDistance() > -mTolerance) + { + merge = true; + } + else if (hedge->twin->getOppositeFaceDistance() > -mTolerance) + { + convex = false; + } + } + else + { + if (hedge->twin->getOppositeFaceDistance() > -mTolerance) + { + merge = true; + } + else if (hedge->getOppositeFaceDistance() > -mTolerance) + { + convex = false; + } + } + } + else + { + // then merge faces if they are definitively non-convex + if (hedge->getOppositeFaceDistance() > -mTolerance || + hedge->twin->getOppositeFaceDistance() > -mTolerance) + { + merge = true; + } + } + + if (merge) + { + mDiscardedFaces.clear(); + if (!face.mergeAdjacentFace(hedge, mDiscardedFaces)) + { + mergeFailed = true; + return false; + } + mNumHullFaces -= mDiscardedFaces.size(); + for (PxU32 i = 0; i < mDiscardedFaces.size(); i++) + { + deleteFacePoints(*mDiscardedFaces[i], &face); + } + PX_ASSERT(face.checkFaceConsistency()); + return true; + } + hedge = hedge->next; + } while (hedge != face.edge); + + if (!convex) + { + face.state = QuickHullFace::eNON_CONVEX; + } + return false; + } + + ////////////////////////////////////////////////////////////////////////// + // merge adjacent faces doing normal test + // we try to merge more aggressively 2 faces with the same normal. + bool QuickHull::doPostAdjacentMerge(QuickHullFace& face, const float maxdot_minang) + { + QuickHullHalfEdge* hedge = face.edge; + + do + { + const QuickHullFace& oppFace = *hedge->getOppositeFace(); + bool merge = false; + const PxVec3& ni = face.normal; + const PxVec3& nj = oppFace.normal; + const float dotP = ni.dot(nj); + + if (dotP > maxdot_minang) + { + if (face.area >= oppFace.area) + { + // check if we can merge the 2 faces + merge = canMergeFaces(*hedge); + } + } + + if (merge) + { + QuickHullFaceArray discardedFaces; + face.mergeAdjacentFace(hedge, discardedFaces); + mNumHullFaces -= discardedFaces.size(); + for (PxU32 i = 0; i < discardedFaces.size(); i++) + { + deleteFacePoints(*discardedFaces[i], &face); + } + PX_ASSERT(face.checkFaceConsistency()); + return true; + } + hedge = hedge->next; + } while (hedge != face.edge); + + return false; + } + + ////////////////////////////////////////////////////////////////////////// + // checks if 2 adjacent faces can be merged + // 1. creates a face with merged vertices + // 2. computes new normal and centroid + // 3. checks that all verts are not too far away from the plane + // 4. checks that the new polygon is still convex + // 5. checks if we are about to merge only 2 neighbor faces, we dont + // want to merge additional faces, that might corrupt the convexity + bool QuickHull::canMergeFaces(const QuickHullHalfEdge& he) + { + const QuickHullFace& face1 = *he.face; + const QuickHullFace& face2 = *he.twin->face; + + // construct the merged face + PX_ALLOCA(edges, QuickHullHalfEdge, (face1.numEdges + face2.numEdges)); + PxMemSet(edges, 0, (face1.numEdges + face2.numEdges)*sizeof(QuickHullHalfEdge)); + QuickHullFace mergedFace; + mergedFace.edge = &edges[0]; + + // copy the first face edges + PxU32 currentEdge = 0; + const QuickHullHalfEdge* heTwin = NULL; + const QuickHullHalfEdge* heCopy = NULL; + const QuickHullHalfEdge* startEdge = (face1.edge != &he) ? face1.edge : face1.edge->next; + const QuickHullHalfEdge* copyHe = startEdge; + do + { + edges[currentEdge].face = &mergedFace; + edges[currentEdge].tail = copyHe->tail; + if(copyHe == &he) + { + heTwin = copyHe->twin; + heCopy = &edges[currentEdge]; + } + const PxU32 nextIndex = (copyHe->next == startEdge) ? 0 : currentEdge + 1; + const PxU32 prevIndex = (currentEdge == 0) ? face1.numEdges - 1 : currentEdge - 1; + edges[currentEdge].next = &edges.mPointer[nextIndex]; + edges[currentEdge].prev = &edges.mPointer[prevIndex]; + + currentEdge++; + copyHe = copyHe->next; + } while (copyHe != startEdge); + + // copy the second face edges + copyHe = face2.edge; + do + { + edges[currentEdge].face = &mergedFace; + edges[currentEdge].tail = copyHe->tail; + if(heTwin == copyHe) + heTwin = &edges[currentEdge]; + const PxU32 nextIndex = (copyHe->next == face2.edge) ? face1.numEdges : currentEdge + 1; + const PxU32 prevIndex = (currentEdge == face1.numEdges) ? face1.numEdges + face2.numEdges - 1 : currentEdge - 1; + edges[currentEdge].next = &edges.mPointer[nextIndex]; + edges[currentEdge].prev = &edges.mPointer[prevIndex]; + + currentEdge++; + copyHe = copyHe->next; + } while (copyHe != face2.edge); + + PX_ASSERT(heTwin); + + QuickHullHalfEdge* hedgeAdjPrev = heCopy->prev; + QuickHullHalfEdge* hedgeAdjNext = heCopy->next; + QuickHullHalfEdge* hedgeOppPrev = heTwin->prev; + QuickHullHalfEdge* hedgeOppNext = heTwin->next; + + hedgeOppPrev->next = hedgeAdjNext; + hedgeAdjNext->prev = hedgeOppPrev; + + hedgeAdjPrev->next = hedgeOppNext; + hedgeOppNext->prev = hedgeAdjPrev; + + // compute normal and centroid + mergedFace.computeNormalAndCentroid(); + + // test the vertex distance + const float maxDist = mPlaneTolerance; + for(PxU32 iVerts=0; iVerts< mNumVertices; iVerts++) + { + const QuickHullVertex& vertex = mVerticesList[iVerts]; + const float dist = mergedFace.distanceToPlane(vertex.point); + if (dist > maxDist) + { + return false; + } + } + + // check the convexity + QuickHullHalfEdge* qhe = mergedFace.edge; + do + { + const QuickHullVertex& vertex = qhe->tail; + const QuickHullVertex& nextVertex = qhe->next->tail; + + PxVec3 edgeVector = nextVertex.point - vertex.point; + edgeVector.normalize(); + const PxVec3 outVector = -mergedFace.normal.cross(edgeVector); + + QuickHullHalfEdge* testHe = qhe->next; + do + { + const QuickHullVertex& testVertex = testHe->tail; + const float dist = (testVertex.point - vertex.point).dot(outVector); + + if (dist > mTolerance) + return false; + + testHe = testHe->next; + } while (testHe != qhe->next); + + qhe = qhe->next; + } while (qhe != mergedFace.edge); + + + const QuickHullFace* oppFace = he.getOppositeFace(); + + QuickHullHalfEdge* hedgeOpp = he.twin; + + hedgeAdjPrev = he.prev; + hedgeAdjNext = he.next; + hedgeOppPrev = hedgeOpp->prev; + hedgeOppNext = hedgeOpp->next; + + // check if we are lining up with the face in adjPrev dir + while (hedgeAdjPrev->getOppositeFace() == oppFace) + { + hedgeAdjPrev = hedgeAdjPrev->prev; + hedgeOppNext = hedgeOppNext->next; + } + + // check if we are lining up with the face in adjNext dir + while (hedgeAdjNext->getOppositeFace() == oppFace) + { + hedgeOppPrev = hedgeOppPrev->prev; + hedgeAdjNext = hedgeAdjNext->next; + } + + // no redundant merges, just clean merge of 2 neighbour faces + if (hedgeOppPrev->getOppositeFace() == hedgeAdjNext->getOppositeFace()) + { + return false; + } + + if (hedgeAdjPrev->getOppositeFace() == hedgeOppNext->getOppositeFace()) + { + return false; + } + + return true; + } + + ////////////////////////////////////////////////////////////////////////// + // delete face points and store them as unclaimed, so we can add them back to new faces later + void QuickHull::deleteFacePoints(QuickHullFace& face, QuickHullFace* absorbingFace) + { + // no conflict list for this face + if(!face.conflictList) + return; + + QuickHullVertex* unclaimedVertex = face.conflictList; + QuickHullVertex* vertexToClaim = NULL; + while (unclaimedVertex) + { + vertexToClaim = unclaimedVertex; + unclaimedVertex = unclaimedVertex->next; + vertexToClaim->next = NULL; + if (!absorbingFace) + { + mUnclaimedPoints.pushBack(vertexToClaim); + } + else + { + const float dist = absorbingFace->distanceToPlane(vertexToClaim->point); + if (dist > mTolerance) + { + addPointToFace(*absorbingFace, vertexToClaim, dist); + } + else + { + mUnclaimedPoints.pushBack(vertexToClaim); + } + } + } + + face.conflictList = NULL; + } + + ////////////////////////////////////////////////////////////////////////// + // calculate the horizon from the eyePoint against a given face + void QuickHull::calculateHorizon(const PxVec3& eyePoint, QuickHullHalfEdge* edge0, QuickHullFace& face, QuickHullHalfEdgeArray& horizon, QuickHullFaceArray& removedFaces) + { + deleteFacePoints(face, NULL); + face.state = QuickHullFace::eDELETED; + removedFaces.pushBack(&face); + mNumHullFaces--; + QuickHullHalfEdge* edge; + if (edge0 == NULL) + { + edge0 = face.getEdge(0); + edge = edge0; + } + else + { + edge = edge0->next; + } + + do + { + QuickHullFace* oppFace = edge->getOppositeFace(); + if (oppFace->state == QuickHullFace::eVISIBLE) + { + const float dist = oppFace->distanceToPlane(eyePoint); + if (dist > mTolerance) + { + calculateHorizon(eyePoint, edge->twin, *oppFace, horizon, removedFaces); + } + else + { + horizon.pushBack(edge); + } + } + edge = edge->next; + } while (edge != edge0); + } + + ////////////////////////////////////////////////////////////////////////// + // adds new faces from given horizon and eyePoint + void QuickHull::addNewFacesFromHorizon(const QuickHullVertex* eyePoint, const QuickHullHalfEdgeArray& horizon, QuickHullFaceArray& newFaces) + { + QuickHullHalfEdge* hedgeSidePrev = NULL; + QuickHullHalfEdge* hedgeSideBegin = NULL; + + for (PxU32 i = 0; i < horizon.size(); i++) + { + const QuickHullHalfEdge& horizonHe = *horizon[i]; + + QuickHullFace* face = createTriangle(*eyePoint, horizonHe.getHead(), horizonHe.getTail()); + mHullFaces.pushBack(face); + mNumHullFaces++; + face->getEdge(2)->setTwin(horizonHe.twin); + + QuickHullHalfEdge* hedgeSide = face->edge; + if (hedgeSidePrev != NULL) + { + hedgeSide->next->setTwin(hedgeSidePrev); + } + else + { + hedgeSideBegin = hedgeSide; + } + newFaces.pushBack(face); + hedgeSidePrev = hedgeSide; + } + hedgeSideBegin->next->setTwin(hedgeSidePrev); + } + + ////////////////////////////////////////////////////////////////////////// + // resolve unclaimed points + void QuickHull::resolveUnclaimedPoints(const QuickHullFaceArray& newFaces) + { + for (PxU32 i = 0; i < mUnclaimedPoints.size(); i++) + { + QuickHullVertex* vtx = mUnclaimedPoints[i]; + + float maxDist = mTolerance; + QuickHullFace* maxFace = NULL; + for (PxU32 j = 0; j < newFaces.size(); j++) + { + const QuickHullFace& newFace = *newFaces[j]; + if (newFace.state == QuickHullFace::eVISIBLE) + { + const float dist = newFace.distanceToPlane(vtx->point); + if (dist > maxDist) + { + maxDist = dist; + maxFace = newFaces[j]; + } + } + } + if (maxFace != NULL) + { + addPointToFace(*maxFace, vtx, maxDist); + } + } + + mUnclaimedPoints.clear(); + } + + ////////////////////////////////////////////////////////////////////////// + // helper struct for hull expand point + struct ExpandPoint + { + PxPlane plane[3]; // the 3 planes that will give us the point + PxU32 planeIndex[3]; // index of the planes for identification + + bool operator==(const ExpandPoint& expPoint) const + { + if (expPoint.planeIndex[0] == planeIndex[0] && expPoint.planeIndex[1] == planeIndex[1] && + expPoint.planeIndex[2] == planeIndex[2]) + return true; + else + return false; +} + }; + +////////////////////////////////////////////////////////////////////////// + // gets the half edge neighbors and form the expand point + void getExpandPoint(const QuickHullHalfEdge& he, ExpandPoint& expandPoint, const Ps::Array* translationTable = NULL) + { + // set the first 2 - the edge face and the twin face + expandPoint.planeIndex[0] = (translationTable) ? ((*translationTable)[he.face->index]) : (he.face->index); + + PxU32 index = translationTable ? ((*translationTable)[he.twin->face->index]) : he.twin->face->index; + if (index < expandPoint.planeIndex[0]) + { + expandPoint.planeIndex[1] = expandPoint.planeIndex[0]; + expandPoint.planeIndex[0] = index; + } + else + { + expandPoint.planeIndex[1] = index; + } + + // now the 3rd one is the next he twin index + index = translationTable ? (*translationTable)[he.next->twin->face->index] : he.next->twin->face->index; + if (index < expandPoint.planeIndex[0]) + { + expandPoint.planeIndex[2] = expandPoint.planeIndex[1]; + expandPoint.planeIndex[1] = expandPoint.planeIndex[0]; + expandPoint.planeIndex[0] = index; + } + else + { + if (index < expandPoint.planeIndex[1]) + { + expandPoint.planeIndex[2] = expandPoint.planeIndex[1]; + expandPoint.planeIndex[1] = index; + } + else + { + expandPoint.planeIndex[2] = index; + } + } + } + + ////////////////////////////////////////////////////////////////////////// + // adds the expand point, don't add similar point + void addExpandPoint(const ExpandPoint& expandPoint, Ps::Array& expandPoints) + { + for (PxU32 i = expandPoints.size(); i--;) + { + if (expandPoint == expandPoints[i]) + { + return; + } + } + + expandPoints.pushBack(expandPoint); + } + + ////////////////////////////////////////////////////////////////////////// + // helper for 3 planes intersection + static PxVec3 threePlaneIntersection(const PxPlane &p0, const PxPlane &p1, const PxPlane &p2) + { + PxMat33 mp = (PxMat33(p0.n, p1.n, p2.n)).getTranspose(); + PxMat33 mi = (mp).getInverse(); + PxVec3 b(p0.d, p1.d, p2.d); + return -mi.transform(b); + } +} + +////////////////////////////////////////////////////////////////////////// + +QuickHullConvexHullLib::QuickHullConvexHullLib(const PxConvexMeshDesc& desc, const PxCookingParams& params) + : ConvexHullLib(desc, params),mQuickHull(NULL), mCropedConvexHull(NULL), mOutMemoryBuffer(NULL), mFaceTranslateTable(NULL) +{ + mQuickHull = PX_NEW_TEMP(local::QuickHull)(params, desc); + mQuickHull->preallocate(desc.points.count); +} + +////////////////////////////////////////////////////////////////////////// + +QuickHullConvexHullLib::~QuickHullConvexHullLib() +{ + mQuickHull->releaseHull(); + PX_DELETE(mQuickHull); + + if(mCropedConvexHull) + { + PX_DELETE(mCropedConvexHull); + } + + PX_FREE(mOutMemoryBuffer); + mFaceTranslateTable = NULL; // memory is a part of mOutMemoryBuffer +} + +////////////////////////////////////////////////////////////////////////// +// create the hull +// 1. clean the input vertices +// 2. check we can construct the simplex, if not expand the input verts +// 3. prepare the quickhull - preallocate, parse input verts +// 4. construct the hull +// 5. post merge faces if limit not reached +// 6. if limit reached, expand the hull +PxConvexMeshCookingResult::Enum QuickHullConvexHullLib::createConvexHull() +{ + PxConvexMeshCookingResult::Enum res = PxConvexMeshCookingResult::eFAILURE; + + PxU32 vcount = mConvexMeshDesc.points.count; + if ( vcount < 8 ) + vcount = 8; + + PxVec3* outvsource = reinterpret_cast (PX_ALLOC_TEMP( sizeof(PxVec3)*vcount, "PxVec3")); + PxVec3 scale; + PxVec3 center; + PxU32 outvcount; + + // cleanup the vertices first + if(mConvexMeshDesc.flags & PxConvexFlag::eSHIFT_VERTICES) + { + if(!shiftAndcleanupVertices(mConvexMeshDesc.points.count, reinterpret_cast (mConvexMeshDesc.points.data), mConvexMeshDesc.points.stride, + outvcount, outvsource, scale, center )) + { + PX_FREE(outvsource); + return res; + } + } + else + { + if(!cleanupVertices(mConvexMeshDesc.points.count, reinterpret_cast (mConvexMeshDesc.points.data), mConvexMeshDesc.points.stride, + outvcount, outvsource, scale, center )) + { + PX_FREE(outvsource); + return res; + } + } + + // scale vertices back to their original size. + // move the vertices to the origin + for (PxU32 i=0; i< outvcount; i++) + { + PxVec3& v = outvsource[i]; + v.multiply(scale); + } + + local::QuickHullVertex minimumVertex[3]; + local::QuickHullVertex maximumVertex[3]; + float tolerance; + float planeTolerance; + bool canReuse = cleanupForSimplex(outvsource, outvcount, &minimumVertex[0], &maximumVertex[0], tolerance, planeTolerance); + + mQuickHull->parseInputVertices(outvsource,outvcount); + + if(canReuse) + { + mQuickHull->setPrecomputedMinMax(minimumVertex, maximumVertex, tolerance, planeTolerance); + } + + local::QuickHullResult::Enum qhRes = mQuickHull->buildHull(); + + switch(qhRes) + { + case local::QuickHullResult::eZERO_AREA_TEST_FAILED: + res = PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED; + break; + case local::QuickHullResult::eSUCCESS: + mQuickHull->postMergeHull(); + res = PxConvexMeshCookingResult::eSUCCESS; + break; + case local::QuickHullResult::ePOLYGONS_LIMIT_REACHED: + if(mQuickHull->getNbHullVerts() > mConvexMeshDesc.vertexLimit) + { + // expand the hull + if(mConvexMeshDesc.flags & PxConvexFlag::ePLANE_SHIFTING) + res = expandHull(); + else + res = expandHullOBB(); + } + res = PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED; + break; + case local::QuickHullResult::eVERTEX_LIMIT_REACHED: + { + // expand the hull + if(mConvexMeshDesc.flags & PxConvexFlag::ePLANE_SHIFTING) + res = expandHull(); + else + res = expandHullOBB(); + } + break; + case local::QuickHullResult::eFAILURE: + break; + }; + + // check if we need to build GRB compatible mesh + // if hull was cropped we already have a compatible mesh, if not check + // the max verts per face + if((mConvexMeshDesc.flags & PxConvexFlag::eGPU_COMPATIBLE) && !mCropedConvexHull && + res == PxConvexMeshCookingResult::eSUCCESS) + { + PX_ASSERT(mQuickHull); + // if we hit the vertex per face limit, expand the hull by cropping OBB + if(mQuickHull->maxNumVertsPerFace() > gpuMaxVertsPerFace) + { + res = expandHullOBB(); + } + } + + PX_FREE(outvsource); + return res; +} + +////////////////////////////////////////////////////////////////////////// +// fixup the input vertices to be not colinear or coplanar for the initial simplex find +bool QuickHullConvexHullLib::cleanupForSimplex(PxVec3* vertices, PxU32 vertexCount, local::QuickHullVertex* minimumVertex, + local::QuickHullVertex* maximumVertex, float& tolerance, float& planeTolerance) +{ + bool retVal = true; + + for (PxU32 i = 0; i < 3; i++) + { + minimumVertex[i].point = vertices[0]; + minimumVertex[i].index = 0; + maximumVertex[i].point = vertices[0]; + maximumVertex[i].index = 0; + + } + + PxVec3 max = vertices[0]; + PxVec3 min = vertices[0]; + + // get the max min vertices along the x,y,z + for (PxU32 i = 1; i < vertexCount; i++) + { + const PxVec3& testPoint = vertices[i]; + if (testPoint.x > max.x) + { + max.x = testPoint.x; + maximumVertex[0].point = testPoint; + maximumVertex[0].index = i; + } + else if (testPoint.x < min.x) + { + min.x = testPoint.x; + minimumVertex[0].point = testPoint; + minimumVertex[0].index = i; + } + + if (testPoint.y > max.y) + { + max.y = testPoint.y; + maximumVertex[1].point = testPoint; + maximumVertex[1].index = i; + } + else if (testPoint.y < min.y) + { + min.y = testPoint.y; + minimumVertex[1].point = testPoint; + minimumVertex[1].index = i; + } + + if (testPoint.z > max.z) + { + max.z = testPoint.z; + maximumVertex[2].point = testPoint; + maximumVertex[2].index = i; + } + else if (testPoint.z < min.z) + { + min.z = testPoint.z; + minimumVertex[2].point = testPoint; + minimumVertex[2].index = i; + } + } + + const float sizeTol = (max.x-min.x + max.y - min.y + max.z - min.z)*0.5f; + tolerance = PxMax(local::PLANE_THICKNES * sizeTol, local::PLANE_THICKNES); + planeTolerance = PxMax(mCookingParams.planeTolerance *sizeTol, mCookingParams.planeTolerance); + + float fmax = 0; + PxU32 imax = 0; + + for (PxU32 i = 0; i < 3; i++) + { + float diff = (maximumVertex[i].point)[i] - (minimumVertex[i].point)[i]; + if (diff > fmax) + { + fmax = diff; + imax = i; + } + } + + PxVec3 simplex[4]; + + // set first two vertices to be those with the greatest + // one dimensional separation + simplex[0] = maximumVertex[imax].point; + simplex[1] = minimumVertex[imax].point; + + // set third vertex to be the vertex farthest from + // the line between simplex[0] and simplex[1] + PxVec3 normal; + float maxDist = 0; + imax = 0; + PxVec3 u01 = (simplex[1] - simplex[0]); + u01.normalize(); + + for (PxU32 i = 0; i < vertexCount; i++) + { + const PxVec3& testPoint = vertices[i]; + const PxVec3 diff = testPoint - simplex[0]; + const PxVec3 xprod = u01.cross(diff); + const float lenSqr = xprod.magnitudeSquared(); + if (lenSqr > maxDist) + { + maxDist = lenSqr; + simplex[2] = testPoint; + normal = xprod; + imax = i; + } + } + + if (PxSqrt(maxDist) < tolerance) + { + // points are collinear, we have to move the point further + PxVec3 u02 = simplex[2] - simplex[0]; + float fT = u02.dot(u01); + const float sqrLen = u01.magnitudeSquared(); + fT /= sqrLen; + PxVec3 n = u02 - fT*u01; + n.normalize(); + const PxVec3 mP = simplex[2] + n * tolerance; + simplex[2] = mP; + vertices[imax] = mP; + retVal = false; + } + normal.normalize(); + + // set the forth vertex in the normal direction + float d0 = simplex[2].dot(normal); + maxDist = 0.0f; + imax = 0; + for (PxU32 i = 0; i < vertexCount; i++) + { + const PxVec3& testPoint = vertices[i]; + float dist = PxAbs(testPoint.dot(normal) - d0); + if (dist > maxDist) + { + maxDist = dist; + simplex[3] = testPoint; + imax = i; + } + } + + if (PxAbs(maxDist) < tolerance) + { + float dist = (vertices[imax].dot(normal) - d0); + if (dist > 0) + vertices[imax] = vertices[imax] + normal * tolerance; + else + vertices[imax] = vertices[imax] - normal * tolerance; + retVal = false; + } + + return retVal; +} + +////////////////////////////////////////////////////////////////////////// +// expand the hull with the from the limited triangles set +// expand hull will do following steps: +// 1. get expand points from hull that form the best hull with given vertices +// 2. expand the planes to have all vertices inside the planes volume +// 3. compute new points by 3 adjacency planes intersections +// 4. take those points and create the hull from them +PxConvexMeshCookingResult::Enum QuickHullConvexHullLib::expandHull() +{ + Ps::Array expandPoints; + expandPoints.reserve(mQuickHull->mNumVertices); + + // go over faces and gather expand points + for (PxU32 i = 0; i < mQuickHull->mHullFaces.size(); i++) + { + const local::QuickHullFace& face = *mQuickHull->mHullFaces[i]; + if(face.state == local::QuickHullFace::eVISIBLE) + { + local::ExpandPoint expandPoint; + local::QuickHullHalfEdge* he = face.edge; + local::getExpandPoint(*he, expandPoint); + local::addExpandPoint(expandPoint, expandPoints); + he = he->next; + while (he != face.edge) + { + local::getExpandPoint(*he, expandPoint); + local::addExpandPoint(expandPoint, expandPoints); + he = he->next; + } + } + } + + + // go over the planes now and expand them + for(PxU32 iVerts=0;iVerts< mQuickHull->mNumVertices;iVerts++) + { + const local::QuickHullVertex& vertex = mQuickHull->mVerticesList[iVerts]; + + for (PxU32 i = 0; i < mQuickHull->mHullFaces.size(); i++) + { + local::QuickHullFace& face = *mQuickHull->mHullFaces[i]; + if(face.state == local::QuickHullFace::eVISIBLE) + { + const float dist = face.distanceToPlane(vertex.point); + if(dist > 0 && dist > face.expandOffset) + { + face.expandOffset = dist; + } + } + } + } + + // fill the expand points planes + for(PxU32 i=0;imFreeFaces.getItem(expandPoint.planeIndex[k]); + PX_ASSERT(face.index == expandPoint.planeIndex[k]); + PxPlane plane; + plane.n = face.normal; + plane.d = -face.planeOffset; + if(face.expandOffset > 0.0f) + plane.d -= face.expandOffset; + expandPoint.plane[k] = plane; + } + } + + // now find the plane intersection + PX_ALLOCA(vertices,PxVec3,expandPoints.size()); + for(PxU32 i=0;imCookingParams, mQuickHull->mConvexDesc); + newHull->preallocate(expandPoints.size()); + newHull->parseInputVertices(vertices,expandPoints.size()); + + local::QuickHullResult::Enum qhRes = newHull->buildHull(); + switch(qhRes) + { + case local::QuickHullResult::eZERO_AREA_TEST_FAILED: + { + newHull->releaseHull(); + PX_DELETE(newHull); + return PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED; + } + case local::QuickHullResult::eSUCCESS: + case local::QuickHullResult::eVERTEX_LIMIT_REACHED: + case local::QuickHullResult::ePOLYGONS_LIMIT_REACHED: + { + mQuickHull->releaseHull(); + PX_DELETE(mQuickHull); + mQuickHull = newHull; + } + break; + case local::QuickHullResult::eFAILURE: + { + newHull->releaseHull(); + PX_DELETE(newHull); + return PxConvexMeshCookingResult::eFAILURE; + } + }; + + return PxConvexMeshCookingResult::eSUCCESS; +} + +////////////////////////////////////////////////////////////////////////// +// expand the hull from the limited triangles set +// 1. collect all planes +// 2. create OBB from the input verts +// 3. slice the OBB with the planes +// 5. iterate till vlimit is reached +PxConvexMeshCookingResult::Enum QuickHullConvexHullLib::expandHullOBB() +{ + Ps::Array expandPlanes; + expandPlanes.reserve(mQuickHull->mHullFaces.size()); + + // collect expand planes + for (PxU32 i = 0; i < mQuickHull->mHullFaces.size(); i++) + { + local::QuickHullFace& face = *mQuickHull->mHullFaces[i]; + if (face.state == local::QuickHullFace::eVISIBLE) + { + PxPlane plane; + plane.n = face.normal; + plane.d = -face.planeOffset; + if (face.expandOffset > 0.0f) + plane.d -= face.expandOffset; + + expandPlanes.pushBack(plane); + } + } + + + PxTransform obbTransform; + PxVec3 sides; + + // compute the OBB + PxConvexMeshDesc convexDesc; + fillConvexMeshDescFromQuickHull(convexDesc); + convexDesc.flags = mConvexMeshDesc.flags; + computeOBBFromConvex(convexDesc, sides, obbTransform); + + // free the memory used for the convex mesh desc + PX_FREE_AND_RESET(mOutMemoryBuffer); + mFaceTranslateTable = NULL; + + // crop the OBB + PxU32 maxplanes = PxMin(PxU32(256), expandPlanes.size()); + + ConvexHull* c = PX_NEW_TEMP(ConvexHull)(sides*0.5f,obbTransform, expandPlanes); + + const float planeTolerance = mQuickHull->mPlaneTolerance; + const float epsilon = mQuickHull->mTolerance; + + PxI32 k; + while (maxplanes-- && (k = c->findCandidatePlane(planeTolerance, epsilon)) >= 0) + { + ConvexHull* tmp = c; + c = convexHullCrop(*tmp, expandPlanes[PxU32(k)], planeTolerance); + if (c == NULL) + { + c = tmp; + break; + } // might want to debug this case better!!! + if (!c->assertIntact(planeTolerance)) + { + PX_DELETE(c); + c = tmp; + break; + } // might want to debug this case better too!!! + + // check for vertex limit + if (c->getVertices().size() > mConvexMeshDesc.vertexLimit) + { + PX_DELETE(c); + c = tmp; + maxplanes = 0; + break; + } + // check for vertex limit per face if necessary, GRB supports max 32 verts per face + if ((mConvexMeshDesc.flags & PxConvexFlag::eGPU_COMPATIBLE) && c->maxNumVertsPerFace() > gpuMaxVertsPerFace) + { + PX_DELETE(c); + c = tmp; + maxplanes = 0; + break; + } + PX_DELETE(tmp); + } + + PX_ASSERT(c->assertIntact(planeTolerance)); + + mCropedConvexHull = c; + + return PxConvexMeshCookingResult::eSUCCESS; +} + +////////////////////////////////////////////////////////////////////////// + +bool QuickHullConvexHullLib::createEdgeList(const PxU32 nbIndices, const PxU8* indices, PxU8** outHullDataFacesByEdges8, PxU16** outEdgeData16, PxU16** outEdges) +{ + // if we croped hull, we dont have the edge information, early exit + if (mCropedConvexHull) + return false; + + PX_ASSERT(mQuickHull); + + // Make sure we did recieved empty buffers + PX_ASSERT(*outHullDataFacesByEdges8 == NULL); + PX_ASSERT(*outEdges == NULL); + PX_ASSERT(*outEdgeData16 == NULL); + + // Allocated the out bufferts + PxU8* hullDataFacesByEdges8 = PX_NEW(PxU8)[nbIndices]; + PxU16* edges = PX_NEW(PxU16)[nbIndices]; + PxU16* edgeData16 = PX_NEW(PxU16)[nbIndices]; + + *outHullDataFacesByEdges8 = hullDataFacesByEdges8; + *outEdges = edges; + *outEdgeData16 = edgeData16; + + PxU16 edgeIndex = 0; + PxU32 edgeOffset = 0; + for(PxU32 i = 0; i < mQuickHull->mNumHullFaces; i++) + { + const local::QuickHullFace& face = *mQuickHull->mHullFaces[mFaceTranslateTable[i]]; + + // Face must be visible + PX_ASSERT(face.state == local::QuickHullFace::eVISIBLE); + + // parse the edges + const PxU32 startEdgeOffset = edgeOffset; + local::QuickHullHalfEdge* hedge = face.edge; + do + { + // check if hedge has been stored + if(hedge->edgeIndex == 0xFFFFFFFF) + { + edges[edgeIndex*2] = indices[edgeOffset]; + edges[edgeIndex*2 + 1] = indices[(hedge->next != face.edge) ? edgeOffset + 1 : startEdgeOffset]; + + hullDataFacesByEdges8[edgeIndex*2] = hedge->face->outIndex; + hullDataFacesByEdges8[edgeIndex*2 + 1] = hedge->next->twin->face->outIndex; + + edgeData16[edgeOffset] = edgeIndex; + + hedge->edgeIndex = edgeIndex; + hedge->next->twin->prev->edgeIndex = edgeIndex; + + edgeIndex++; + } + else + { + edgeData16[edgeOffset] = Ps::to16(hedge->edgeIndex); + } + + hedge = hedge->next; + edgeOffset++; + } while (hedge != face.edge); + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// fill the descriptor with computed verts, indices and polygons +void QuickHullConvexHullLib::fillConvexMeshDesc(PxConvexMeshDesc& desc) +{ + if (mCropedConvexHull) + fillConvexMeshDescFromCroppedHull(desc); + else + fillConvexMeshDescFromQuickHull(desc); + + if(mConvexMeshDesc.flags & PxConvexFlag::eSHIFT_VERTICES) + shiftConvexMeshDesc(desc); +} + +////////////////////////////////////////////////////////////////////////// +// fill the descriptor with computed verts, indices and polygons from quickhull convex +void QuickHullConvexHullLib::fillConvexMeshDescFromQuickHull(PxConvexMeshDesc& desc) +{ + // get the number of indices needed + PxU32 numIndices = 0; + PxU32 numFaces = mQuickHull->mHullFaces.size(); + PxU32 numFacesOut = 0; + PxU32 largestFace = 0; // remember the largest face, we store it as the first face, required for GRB test (max 32 vers per face supported) + for (PxU32 i = 0; i < numFaces; i++) + { + const local::QuickHullFace& face = *mQuickHull->mHullFaces[i]; + if(face.state == local::QuickHullFace::eVISIBLE) + { + numFacesOut++; + numIndices += face.numEdges; + if(face.numEdges > mQuickHull->mHullFaces[largestFace]->numEdges) + largestFace = i; + } + } + + // allocate out buffers + const PxU32 indicesBufferSize = sizeof(PxU32)*numIndices; + const PxU32 verticesBufferSize = sizeof(PxVec3)*(mQuickHull->mNumVertices + 1); + const PxU32 facesBufferSize = sizeof(PxHullPolygon)*numFacesOut; + const PxU32 faceTranslationTableSize = sizeof(PxU16)*numFacesOut; + const PxU32 translationTableSize = sizeof(PxU32)*mQuickHull->mNumVertices; + const PxU32 bufferMemorySize = indicesBufferSize + verticesBufferSize + facesBufferSize + faceTranslationTableSize + translationTableSize; + mOutMemoryBuffer = reinterpret_cast(PX_ALLOC_TEMP(bufferMemorySize, "ConvexMeshDesc")); + + PxU32* indices = reinterpret_cast (mOutMemoryBuffer); + PxVec3* vertices = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize); + PxHullPolygon* polygons = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize + verticesBufferSize); + mFaceTranslateTable = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize + verticesBufferSize + facesBufferSize); + PxI32* translateTable = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize + verticesBufferSize + facesBufferSize + faceTranslationTableSize); + PxMemSet(translateTable,-1,mQuickHull->mNumVertices*sizeof(PxU32)); + + // go over the hullPolygons and mark valid vertices, create translateTable + PxU32 numVertices = 0; + for (PxU32 i = 0; i < numFaces; i++) + { + const local::QuickHullFace& face = *mQuickHull->mHullFaces[i]; + if(face.state == local::QuickHullFace::eVISIBLE) + { + local::QuickHullHalfEdge* he = face.edge; + if(translateTable[he->tail.index] == -1) + { + vertices[numVertices] = he->tail.point; + translateTable[he->tail.index] = PxI32(numVertices); + numVertices++; + } + he = he->next; + while (he != face.edge) + { + if(translateTable[he->tail.index] == -1) + { + vertices[numVertices] = he->tail.point; + translateTable[he->tail.index] = PxI32(numVertices); + numVertices++; + } + he = he->next; + } + } + } + + + desc.points.count = numVertices; + desc.points.data = vertices; + desc.points.stride = sizeof(PxVec3); + + desc.indices.count = numIndices; + desc.indices.data = indices; + desc.indices.stride = sizeof(PxU32); + + desc.polygons.count = numFacesOut; + desc.polygons.data = polygons; + desc.polygons.stride = sizeof(PxHullPolygon); + + PxU16 indexOffset = 0; + numFacesOut = 0; + for (PxU32 i = 0; i < numFaces; i++) + { + // faceIndex - store the largest face first then the rest + PxU32 faceIndex; + if(i == 0) + { + faceIndex = largestFace; + } + else + { + faceIndex = (i == largestFace) ? 0 : i; + } + + local::QuickHullFace& face = *mQuickHull->mHullFaces[faceIndex]; + if(face.state == local::QuickHullFace::eVISIBLE) + { + //create index data + local::QuickHullHalfEdge* he = face.edge; + PxU32 index = 0; + he->edgeIndex = 0xFFFFFFFF; + indices[index + indexOffset] = PxU32(translateTable[he->tail.index]); + index++; + he = he->next; + while (he != face.edge) + { + indices[index + indexOffset] = PxU32(translateTable[he->tail.index]); + index++; + he->edgeIndex = 0xFFFFFFFF; + he = he->next; + } + + // create polygon + PxHullPolygon polygon; + polygon.mPlane[0] = face.normal[0]; + polygon.mPlane[1] = face.normal[1]; + polygon.mPlane[2] = face.normal[2]; + polygon.mPlane[3] = -face.planeOffset; + + polygon.mIndexBase = indexOffset; + polygon.mNbVerts = face.numEdges; + indexOffset += face.numEdges; + polygons[numFacesOut] = polygon; + mFaceTranslateTable[numFacesOut] = Ps::to16(faceIndex); + face.outIndex = Ps::to8(numFacesOut); + numFacesOut++; + } + } + + PX_ASSERT(mQuickHull->mNumHullFaces == numFacesOut); +} + +////////////////////////////////////////////////////////////////////////// +// fill the desc from cropped hull data +void QuickHullConvexHullLib::fillConvexMeshDescFromCroppedHull(PxConvexMeshDesc& outDesc) +{ + PX_ASSERT(mCropedConvexHull); + + // allocate the output buffers + const PxU32 numIndices = mCropedConvexHull->getEdges().size(); + const PxU32 numPolygons = mCropedConvexHull->getFacets().size(); + const PxU32 numVertices = mCropedConvexHull->getVertices().size(); + const PxU32 indicesBufferSize = sizeof(PxU32)*numIndices; + const PxU32 facesBufferSize = sizeof(PxHullPolygon)*numPolygons; + const PxU32 verticesBufferSize = sizeof(PxVec3)*(numVertices + 1); // allocate additional vec3 for V4 safe load in VolumeInteration + const PxU32 bufferMemorySize = indicesBufferSize + verticesBufferSize + facesBufferSize; + mOutMemoryBuffer = reinterpret_cast(PX_ALLOC_TEMP(bufferMemorySize, "ConvexMeshDesc")); + + // parse the hullOut and fill the result with vertices and polygons + PxU32* indicesOut = reinterpret_cast (mOutMemoryBuffer); + PxHullPolygon* polygonsOut = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize); + PxVec3* vertsOut = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize + facesBufferSize); + PxMemCopy(vertsOut, mCropedConvexHull->getVertices().begin(), sizeof(PxVec3)*numVertices); + + PxU32 i = 0; + PxU32 k = 0; + PxU32 j = 1; + while (i < mCropedConvexHull->getEdges().size()) + { + j = 1; + PxHullPolygon& polygon = polygonsOut[k]; + // get num indices per polygon + while (j + i < mCropedConvexHull->getEdges().size() && mCropedConvexHull->getEdges()[i].p == mCropedConvexHull->getEdges()[i + j].p) + { + j++; + } + polygon.mNbVerts = Ps::to16(j); + polygon.mIndexBase = Ps::to16(i); + + // get the plane + polygon.mPlane[0] = mCropedConvexHull->getFacets()[k].n[0]; + polygon.mPlane[1] = mCropedConvexHull->getFacets()[k].n[1]; + polygon.mPlane[2] = mCropedConvexHull->getFacets()[k].n[2]; + + polygon.mPlane[3] = mCropedConvexHull->getFacets()[k].d; + + while (j--) + { + indicesOut[i] = mCropedConvexHull->getEdges()[i].v; + i++; + } + k++; + } + + PX_ASSERT(k == mCropedConvexHull->getFacets().size()); + + outDesc.indices.count = numIndices; + outDesc.indices.stride = sizeof(PxU32); + outDesc.indices.data = indicesOut; + + outDesc.points.count = numVertices; + outDesc.points.stride = sizeof(PxVec3); + outDesc.points.data = vertsOut; + + outDesc.polygons.count = numPolygons; + outDesc.polygons.stride = sizeof(PxHullPolygon); + outDesc.polygons.data = polygonsOut; + + swapLargestFace(outDesc); +} + diff --git a/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h new file mode 100644 index 000000000..7d576ed1f --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_QUICKHULL_CONVEXHULLLIB_H +#define PX_QUICKHULL_CONVEXHULLLIB_H + +#include "ConvexHullLib.h" +#include "Ps.h" +#include "PsArray.h" +#include "PsUserAllocated.h" + +namespace local +{ + class QuickHull; + struct QuickHullVertex; +} + +namespace physx +{ + class ConvexHull; + + ////////////////////////////////////////////////////////////////////////// + // Quickhull lib constructs the hull from given input points. The resulting hull + // will only contain a subset of the input points. The algorithm does incrementally + // adds most furthest vertices to the starting simplex. The produced hulls are build with high precision + // and produce more stable and correct results, than the legacy algorithm. + class QuickHullConvexHullLib: public ConvexHullLib, public Ps::UserAllocated + { + PX_NOCOPY(QuickHullConvexHullLib) + public: + + // functions + QuickHullConvexHullLib(const PxConvexMeshDesc& desc, const PxCookingParams& params); + + ~QuickHullConvexHullLib(); + + // computes the convex hull from provided points + virtual PxConvexMeshCookingResult::Enum createConvexHull(); + + // fills the convexmeshdesc with computed hull data + virtual void fillConvexMeshDesc(PxConvexMeshDesc& desc); + + // provide the edge list information + virtual bool createEdgeList(const PxU32, const PxU8* , PxU8** , PxU16** , PxU16** ); + + protected: + // if vertex limit reached we need to expand the hull using the OBB slicing + PxConvexMeshCookingResult::Enum expandHullOBB(); + + // if vertex limit reached we need to expand the hull using the plane shifting + PxConvexMeshCookingResult::Enum expandHull(); + + // checks for collinearity and co planarity + // returns true if the simplex was ok, we can reuse the computed tolerances and min/max values + bool cleanupForSimplex(PxVec3* vertices, PxU32 vertexCount, local::QuickHullVertex* minimumVertex, + local::QuickHullVertex* maximumVertex, float& tolerance, float& planeTolerance); + + // fill the result desc from quick hull convex + void fillConvexMeshDescFromQuickHull(PxConvexMeshDesc& desc); + + // fill the result desc from cropped hull convex + void fillConvexMeshDescFromCroppedHull(PxConvexMeshDesc& desc); + + private: + local::QuickHull* mQuickHull; // the internal quick hull representation + ConvexHull* mCropedConvexHull; //the hull cropped from OBB, used for vertex limit path + + PxU8* mOutMemoryBuffer; // memory buffer used for output data + PxU16* mFaceTranslateTable; // translation table mapping output faces to internal quick hull table + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp new file mode 100644 index 000000000..0c739bc90 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp @@ -0,0 +1,797 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +//#ifdef PX_COOKING + +/* +* This code computes volume integrals needed to compute mass properties of polyhedral bodies. +* Based on public domain code by Brian Mirtich. +*/ +#include "foundation/PxMemory.h" +#include "VolumeIntegration.h" +#include "PxSimpleTriangleMesh.h" +#include "PxConvexMeshDesc.h" +#include "GuConvexMeshData.h" +#include "PsUtilities.h" +#include "PsVecMath.h" + + +namespace physx +{ + + using namespace Ps::aos; + +namespace +{ + + class VolumeIntegrator + { + public: + VolumeIntegrator(PxSimpleTriangleMesh mesh, PxF64 mDensity); + ~VolumeIntegrator(); + bool computeVolumeIntegrals(PxIntegrals& ir); + private: + struct Normal + { + PxVec3 normal; + PxF32 w; + }; + + struct Face + { + PxF64 Norm[3]; + PxF64 w; + PxU32 Verts[3]; + }; + + // Data structures + PxF64 mMass; //!< Mass + PxF64 mDensity; //!< Density + PxSimpleTriangleMesh mesh; + //Normal * faceNormals; //!< temp face normal data structure + + + + + unsigned int mA; //!< Alpha + unsigned int mB; //!< Beta + unsigned int mC; //!< Gamma + + // Projection integrals + PxF64 mP1; + PxF64 mPa; //!< Pi Alpha + PxF64 mPb; //!< Pi Beta + PxF64 mPaa; //!< Pi Alpha^2 + PxF64 mPab; //!< Pi AlphaBeta + PxF64 mPbb; //!< Pi Beta^2 + PxF64 mPaaa; //!< Pi Alpha^3 + PxF64 mPaab; //!< Pi Alpha^2Beta + PxF64 mPabb; //!< Pi AlphaBeta^2 + PxF64 mPbbb; //!< Pi Beta^3 + + // Face integrals + PxF64 mFa; //!< FAlpha + PxF64 mFb; //!< FBeta + PxF64 mFc; //!< FGamma + PxF64 mFaa; //!< FAlpha^2 + PxF64 mFbb; //!< FBeta^2 + PxF64 mFcc; //!< FGamma^2 + PxF64 mFaaa; //!< FAlpha^3 + PxF64 mFbbb; //!< FBeta^3 + PxF64 mFccc; //!< FGamma^3 + PxF64 mFaab; //!< FAlpha^2Beta + PxF64 mFbbc; //!< FBeta^2Gamma + PxF64 mFcca; //!< FGamma^2Alpha + + // The 10 volume integrals + PxF64 mT0; //!< ~Total mass + PxF64 mT1[3]; //!< Location of the center of mass + PxF64 mT2[3]; //!< Moments of inertia + PxF64 mTP[3]; //!< Products of inertia + + // Internal methods + // bool Init(); + PxVec3 computeCenterOfMass(); + void computeInertiaTensor(PxF64* J); + void computeCOMInertiaTensor(PxF64* J); + void computeFaceNormal(Face & f, PxU32 * indices); + + void computeProjectionIntegrals(const Face& f); + void computeFaceIntegrals(const Face& f); + }; + + #define X 0u + #define Y 1u + #define Z 2u + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Constructor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + VolumeIntegrator::VolumeIntegrator(PxSimpleTriangleMesh mesh_, PxF64 density) + { + mDensity = density; + mMass = 0.0; + this->mesh = mesh_; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Destructor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + VolumeIntegrator::~VolumeIntegrator() + { + } + + void VolumeIntegrator::computeFaceNormal(Face & f, PxU32 * indices) + { + const PxU8 * vertPointer = reinterpret_cast(mesh.points.data); + + //two edges + PxVec3 d1 = (*reinterpret_cast(vertPointer + mesh.points.stride * indices[1] )) - (*reinterpret_cast(vertPointer + mesh.points.stride * indices[0] )); + PxVec3 d2 = (*reinterpret_cast(vertPointer + mesh.points.stride * indices[2] )) - (*reinterpret_cast(vertPointer + mesh.points.stride * indices[1] )); + + + PxVec3 normal = d1.cross(d2); + + normal.normalize(); + + f.w = - PxF64(normal.dot( (*reinterpret_cast(vertPointer + mesh.points.stride * indices[0] )) )); + + f.Norm[0] = PxF64(normal.x); + f.Norm[1] = PxF64(normal.y); + f.Norm[2] = PxF64(normal.z); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes volume integrals for a polyhedron by summing surface integrals over its faces. + * \param ir [out] a result structure. + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool VolumeIntegrator::computeVolumeIntegrals(PxIntegrals& ir) + { + // Clear all integrals + mT0 = mT1[X] = mT1[Y] = mT1[Z] = mT2[X] = mT2[Y] = mT2[Z] = mTP[X] = mTP[Y] = mTP[Z] = 0; + + Face f; + const PxU8 * trigPointer = reinterpret_cast(mesh.triangles.data); + for(PxU32 i=0;i(trigPointer))[0]; + f.Verts[1] = (reinterpret_cast(trigPointer))[1]; + f.Verts[2] = (reinterpret_cast(trigPointer))[2]; + } + else + { + f.Verts[0] = (reinterpret_cast(trigPointer)[0]); + f.Verts[1] = (reinterpret_cast(trigPointer)[1]); + f.Verts[2] = (reinterpret_cast(trigPointer)[2]); + } + + if (mesh.flags & PxMeshFlag::eFLIPNORMALS) + { + PxU32 t = f.Verts[1]; + f.Verts[1] = f.Verts[2]; + f.Verts[2] = t; + } + + //compute face normal: + computeFaceNormal(f,f.Verts); + + // Compute alpha/beta/gamma as the right-handed permutation of (x,y,z) that maximizes |n| + PxF64 nx = fabs(f.Norm[X]); + PxF64 ny = fabs(f.Norm[Y]); + PxF64 nz = fabs(f.Norm[Z]); + if (nx > ny && nx > nz) mC = X; + else mC = (ny > nz) ? Y : Z; + mA = (mC + 1) % 3; + mB = (mA + 1) % 3; + + // Compute face contribution + computeFaceIntegrals(f); + + // Update integrals + mT0 += f.Norm[X] * ((mA == X) ? mFa : ((mB == X) ? mFb : mFc)); + + mT1[mA] += f.Norm[mA] * mFaa; + mT1[mB] += f.Norm[mB] * mFbb; + mT1[mC] += f.Norm[mC] * mFcc; + + mT2[mA] += f.Norm[mA] * mFaaa; + mT2[mB] += f.Norm[mB] * mFbbb; + mT2[mC] += f.Norm[mC] * mFccc; + + mTP[mA] += f.Norm[mA] * mFaab; + mTP[mB] += f.Norm[mB] * mFbbc; + mTP[mC] += f.Norm[mC] * mFcca; + } + + mT1[X] /= 2; mT1[Y] /= 2; mT1[Z] /= 2; + mT2[X] /= 3; mT2[Y] /= 3; mT2[Z] /= 3; + mTP[X] /= 2; mTP[Y] /= 2; mTP[Z] /= 2; + + // Fill result structure + ir.COM = computeCenterOfMass(); + computeInertiaTensor(reinterpret_cast(ir.inertiaTensor)); + computeCOMInertiaTensor(reinterpret_cast(ir.COMInertiaTensor)); + ir.mass = mMass; + return true; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the center of mass. + * \return The center of mass. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PxVec3 VolumeIntegrator::computeCenterOfMass() + { + // Compute center of mass + PxVec3 COM(0.0f, 0.0f, 0.0f); + if(mT0!=0.0) + { + COM.x = float(mT1[X] / mT0); + COM.y = float(mT1[Y] / mT0); + COM.z = float(mT1[Z] / mT0); + } + return COM; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups the inertia tensor relative to the origin. + * \param it [out] the returned inertia tensor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void VolumeIntegrator::computeInertiaTensor(PxF64* it) + { + PxF64 J[3][3]; + + // Compute inertia tensor + J[X][X] = mDensity * (mT2[Y] + mT2[Z]); + J[Y][Y] = mDensity * (mT2[Z] + mT2[X]); + J[Z][Z] = mDensity * (mT2[X] + mT2[Y]); + + J[X][Y] = J[Y][X] = - mDensity * mTP[X]; + J[Y][Z] = J[Z][Y] = - mDensity * mTP[Y]; + J[Z][X] = J[X][Z] = - mDensity * mTP[Z]; + + PxMemCopy(it, J, 9*sizeof(PxF64)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups the inertia tensor relative to the COM. + * \param it [out] the returned inertia tensor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void VolumeIntegrator::computeCOMInertiaTensor(PxF64* it) + { + PxF64 J[3][3]; + + mMass = mDensity * mT0; + + const PxVec3 COM = computeCenterOfMass(); + const PxVec3 MassCOM(PxF32(mMass) * COM); + const PxVec3 MassCOM2(MassCOM.x * COM.x, MassCOM.y * COM.y, MassCOM.z * COM.z); + + // Compute initial inertia tensor + computeInertiaTensor(reinterpret_cast(J)); + + // Translate inertia tensor to center of mass + // Huyghens' theorem: + // Jx'x' = Jxx - m*(YG^2+ZG^2) + // Jy'y' = Jyy - m*(ZG^2+XG^2) + // Jz'z' = Jzz - m*(XG^2+YG^2) + // XG, YG, ZG = new origin + // YG^2+ZG^2 = dx^2 + J[X][X] -= PxF64(MassCOM2.y + MassCOM2.z); + J[Y][Y] -= PxF64(MassCOM2.z + MassCOM2.x); + J[Z][Z] -= PxF64(MassCOM2.x + MassCOM2.y); + + // Huyghens' theorem: + // Jx'y' = Jxy - m*XG*YG + // Jy'z' = Jyz - m*YG*ZG + // Jz'x' = Jzx - m*ZG*XG + // ### IS THE SIGN CORRECT ? + J[X][Y] = J[Y][X] += PxF64(MassCOM.x * COM.y); + J[Y][Z] = J[Z][Y] += PxF64(MassCOM.y * COM.z); + J[Z][X] = J[X][Z] += PxF64(MassCOM.z * COM.x); + + PxMemCopy(it, J, 9*sizeof(PxF64)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes integrals over a face projection from the coordinates of the projections vertices. + * \param f [in] a face structure. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void VolumeIntegrator::computeProjectionIntegrals(const Face& f) + { + mP1 = mPa = mPb = mPaa = mPab = mPbb = mPaaa = mPaab = mPabb = mPbbb = 0.0; + + const PxU8* vertPointer = reinterpret_cast(mesh.points.data); + for(PxU32 i=0;i<3;i++) + { + const PxVec3& p0 = *reinterpret_cast(vertPointer + mesh.points.stride * (f.Verts[i]) ); + const PxVec3& p1 = *reinterpret_cast(vertPointer + mesh.points.stride * (f.Verts[(i+1) % 3]) ); + + + PxF64 a0 = PxF64(p0[mA]); + PxF64 b0 = PxF64(p0[mB]); + PxF64 a1 = PxF64(p1[mA]); + PxF64 b1 = PxF64(p1[mB]); + + PxF64 da = a1 - a0; // DeltaA + PxF64 db = b1 - b0; // DeltaB + + PxF64 a0_2 = a0 * a0; // Alpha0^2 + PxF64 a0_3 = a0_2 * a0; // ... + PxF64 a0_4 = a0_3 * a0; + + PxF64 b0_2 = b0 * b0; + PxF64 b0_3 = b0_2 * b0; + PxF64 b0_4 = b0_3 * b0; + + PxF64 a1_2 = a1 * a1; + PxF64 a1_3 = a1_2 * a1; + + PxF64 b1_2 = b1 * b1; + PxF64 b1_3 = b1_2 * b1; + + PxF64 C1 = a1 + a0; + + PxF64 Ca = a1*C1 + a0_2; + PxF64 Caa = a1*Ca + a0_3; + PxF64 Caaa = a1*Caa + a0_4; + + PxF64 Cb = b1*(b1 + b0) + b0_2; + PxF64 Cbb = b1*Cb + b0_3; + PxF64 Cbbb = b1*Cbb + b0_4; + + PxF64 Cab = 3*a1_2 + 2*a1*a0 + a0_2; + PxF64 Kab = a1_2 + 2*a1*a0 + 3*a0_2; + + PxF64 Caab = a0*Cab + 4*a1_3; + PxF64 Kaab = a1*Kab + 4*a0_3; + + PxF64 Cabb = 4*b1_3 + 3*b1_2*b0 + 2*b1*b0_2 + b0_3; + PxF64 Kabb = b1_3 + 2*b1_2*b0 + 3*b1*b0_2 + 4*b0_3; + + mP1 += db*C1; + mPa += db*Ca; + mPaa += db*Caa; + mPaaa += db*Caaa; + mPb += da*Cb; + mPbb += da*Cbb; + mPbbb += da*Cbbb; + mPab += db*(b1*Cab + b0*Kab); + mPaab += db*(b1*Caab + b0*Kaab); + mPabb += da*(a1*Cabb + a0*Kabb); + } + + mP1 /= 2.0; + mPa /= 6.0; + mPaa /= 12.0; + mPaaa /= 20.0; + mPb /= -6.0; + mPbb /= -12.0; + mPbbb /= -20.0; + mPab /= 24.0; + mPaab /= 60.0; + mPabb /= -60.0; + } + + #define SQR(x) ((x)*(x)) //!< Returns x square + #define CUBE(x) ((x)*(x)*(x)) //!< Returns x cube + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes surface integrals over a polyhedral face from the integrals over its projection. + * \param f [in] a face structure. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void VolumeIntegrator::computeFaceIntegrals(const Face& f) + { + computeProjectionIntegrals(f); + + PxF64 w = f.w; + const PxF64* n = f.Norm; + PxF64 k1 = 1 / n[mC]; + PxF64 k2 = k1 * k1; + PxF64 k3 = k2 * k1; + PxF64 k4 = k3 * k1; + + mFa = k1 * mPa; + mFb = k1 * mPb; + mFc = -k2 * (n[mA]*mPa + n[mB]*mPb + w*mP1); + + mFaa = k1 * mPaa; + mFbb = k1 * mPbb; + mFcc = k3 * (SQR(n[mA])*mPaa + 2*n[mA]*n[mB]*mPab + SQR(n[mB])*mPbb + w*(2*(n[mA]*mPa + n[mB]*mPb) + w*mP1)); + + mFaaa = k1 * mPaaa; + mFbbb = k1 * mPbbb; + mFccc = -k4 * (CUBE(n[mA])*mPaaa + 3*SQR(n[mA])*n[mB]*mPaab + + 3*n[mA]*SQR(n[mB])*mPabb + CUBE(n[mB])*mPbbb + + 3*w*(SQR(n[mA])*mPaa + 2*n[mA]*n[mB]*mPab + SQR(n[mB])*mPbb) + + w*w*(3*(n[mA]*mPa + n[mB]*mPb) + w*mP1)); + + mFaab = k1 * mPaab; + mFbbc = -k2 * (n[mA]*mPabb + n[mB]*mPbbb + w*mPbb); + mFcca = k3 * (SQR(n[mA])*mPaaa + 2*n[mA]*n[mB]*mPaab + SQR(n[mB])*mPabb + w*(2*(n[mA]*mPaa + n[mB]*mPab) + w*mPa)); + } + + /* + * This code computes volume integrals needed to compute mass properties of polyhedral bodies. + * Based on public domain code by David Eberly. + */ + + class VolumeIntegratorEberly + { + public: + VolumeIntegratorEberly(const PxConvexMeshDesc& mesh, PxF64 mDensity); + ~VolumeIntegratorEberly(); + bool computeVolumeIntegralsSIMD(PxIntegrals& ir, const PxVec3& origin); + bool computeVolumeIntegrals(PxIntegrals& ir, const PxVec3& origin); + + private: + VolumeIntegratorEberly& operator=(const VolumeIntegratorEberly&); + const PxConvexMeshDesc& mDesc; + PxF64 mMass; + PxReal mMassR; + PxF64 mDensity; + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Constructor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + VolumeIntegratorEberly::VolumeIntegratorEberly(const PxConvexMeshDesc& desc, PxF64 density) + : mDesc(desc), mMass(0), mMassR(0), mDensity(density) + { + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Destructor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + VolumeIntegratorEberly::~VolumeIntegratorEberly() + { + } + + PX_FORCE_INLINE void subexpressions(PxF64 w0, PxF64 w1, PxF64 w2, PxF64& f1, PxF64& f2, PxF64& f3, PxF64& g0, PxF64& g1, PxF64& g2) + { + PxF64 temp0 = w0 + w1; + f1 = temp0 + w2; + PxF64 temp1 = w0*w0; + PxF64 temp2 = temp1 + w1*temp0; + f2 = temp2 + w2*f1; + f3 = w0*temp1 + w1*temp2 + w2*f2; + g0 = f2 + w0*(f1 + w0); + g1 = f2 + w1*(f1 + w1); + g2 = f2 + w2*(f1 + w2); + } + + PX_FORCE_INLINE void subexpressionsSIMD(const Vec4V& w0, const Vec4V& w1, const Vec4V& w2, + Vec4V& f1, Vec4V& f2, Vec4V& f3, Vec4V& g0, Vec4V& g1, Vec4V& g2) + { + const Vec4V temp0 = V4Add(w0, w1); + f1 = V4Add(temp0, w2); + const Vec4V temp1 = V4Mul(w0,w0); + const Vec4V temp2 = V4MulAdd(w1, temp0, temp1); + f2 = V4MulAdd(w2, f1, temp2); + + // f3 = w0.multiply(temp1) + w1.multiply(temp2) + w2.multiply(f2); + const Vec4V ad0 = V4Mul(w0, temp1); + const Vec4V ad1 = V4MulAdd(w1, temp2, ad0); + f3 = V4MulAdd(w2, f2, ad1); + + g0 = V4MulAdd(w0, V4Add(f1, w0), f2); // f2 + w0.multiply(f1 + w0); + g1 = V4MulAdd(w1, V4Add(f1, w1), f2); // f2 + w1.multiply(f1 + w1); + g2 = V4MulAdd(w2, V4Add(f1, w2), f2); // f2 + w2.multiply(f1 + w2); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes volume integrals for a polyhedron by summing surface integrals over its faces. SIMD version + * \param ir [out] a result structure. + * \param origin [in] the origin of the mesh vertices. All vertices will be shifted accordingly prior to computing the volume integrals. + Can improve accuracy, for example, if the centroid is used in the case of a convex mesh. Note: the returned inertia will not be relative to this origin but relative to (0,0,0). + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool VolumeIntegratorEberly::computeVolumeIntegralsSIMD(PxIntegrals& ir, const PxVec3& origin) + { + FloatV mult = FLoad(1.0f/6.0f); + const Vec4V multV = V4Load(1.0f/24.0f); + const Vec4V multV2 = V4Load(1.0f/60.0f); + const Vec4V multVV = V4Load(1.0f/120.0f); + + // order: 1, x, y, z, x^2, y^2, z^2, xy, yz, zx + FloatV intg = FLoad(0.0f); + Vec4V intgV = V4Load(0.0f); + Vec4V intgV2 = V4Load(0.0f); + Vec4V intgVV = V4Load(0.0f); + + const Vec4V originV = Vec4V_From_PxVec3_WUndefined(origin); + const FloatV zeroV = FLoad(0.0f); + + const PxVec3* hullVerts = static_cast (mDesc.points.data); + const Gu::HullPolygonData* hullPolygons = static_cast (mDesc.polygons.data); + + for (PxU32 i = 0; i < mDesc.polygons.count; i++) + { + const Gu::HullPolygonData& polygon = hullPolygons[i]; + const PxU8* data = static_cast(mDesc.indices.data) + polygon.mVRef8; + const PxU32 nbVerts = polygon.mNbVerts; + + PX_ASSERT(nbVerts > 2); + + const Vec4V normalV = V4LoadU(&polygon.mPlane.n.x); + + for (PxU32 j = 0; j < nbVerts - 2; j++) + { + // Should be safe to V4Load, we allocate one more vertex each time + const Vec4V vertex0 = V4LoadU(&hullVerts[data[0]].x); + const Vec4V vertex1 = V4LoadU(&hullVerts[data[j + 1]].x); + const Vec4V vertex2 = V4LoadU(&hullVerts[data[j + 2]].x); + + const Vec4V p0 = V4Sub(vertex0, originV); + Vec4V p1 = V4Sub(vertex1, originV); + Vec4V p2 = V4Sub(vertex2, originV); + + const Vec4V p0YZX = V4PermYZXW(p0); + const Vec4V p1YZX = V4PermYZXW(p1); + const Vec4V p2YZX = V4PermYZXW(p2); + + // get edges and cross product of edges + Vec4V d = V4Cross(V4Sub(p1, p0), V4Sub(p2, p0)); // (p1 - p0).cross(p2 - p0); + + const FloatV dist = V4Dot3(d, normalV); + //if(cp.dot(normalV) < 0) + if(FAllGrtr(zeroV, dist)) + { + d = V4Neg(d); + Vec4V temp = p1; + p1 = p2; + p2 = temp; + } + + // compute integral terms + Vec4V f1; Vec4V f2; Vec4V f3; Vec4V g0; Vec4V g1; Vec4V g2; + + subexpressionsSIMD(p0, p1, p2, f1, f2, f3, g0, g1, g2); + + // update integrals + intg = FScaleAdd(V4GetX(d), V4GetX(f1), intg); //intg += d.x*f1.x; + + intgV = V4MulAdd(d, f2, intgV); // intgV +=d.multiply(f2); + intgV2 = V4MulAdd(d, f3, intgV2); // intgV2 += d.multiply(f3); + + const Vec4V ad0 = V4Mul(p0YZX, g0); + const Vec4V ad1 = V4MulAdd(p1YZX, g1, ad0); + const Vec4V ad2 = V4MulAdd(p2YZX, g2, ad1); + intgVV = V4MulAdd(d, ad2, intgVV); //intgVV += d.multiply(p0YZX.multiply(g0) + p1YZX.multiply(g1) + p2YZX.multiply(g2)); + } + } + + intg = FMul(intg, mult); // intg *= mult; + intgV = V4Mul(intgV, multV); + intgV2 = V4Mul(intgV2, multV2); + intgVV = V4Mul(intgVV, multVV); + + // center of mass ir.COM = intgV/mMassR; + const Vec4V comV = V4ScaleInv(intgV, intg); + // we rewrite the mass, but then we set it back + V4StoreU(comV, &ir.COM.x); + + FStore(intg, &mMassR); + ir.mass = PxF64(mMassR); // = intg; + + PxVec3 intg2; + V3StoreU(Vec3V_From_Vec4V(intgV2), intg2); + + PxVec3 intVV; + V3StoreU(Vec3V_From_Vec4V(intgVV), intVV); + + // inertia tensor relative to the provided origin parameter + ir.inertiaTensor[0][0] = PxF64(intg2.y + intg2.z); + ir.inertiaTensor[1][1] = PxF64(intg2.x + intg2.z); + ir.inertiaTensor[2][2] = PxF64(intg2.x + intg2.y); + ir.inertiaTensor[0][1] = ir.inertiaTensor[1][0] = PxF64(-intVV.x); + ir.inertiaTensor[1][2] = ir.inertiaTensor[2][1] = PxF64(-intVV.y); + ir.inertiaTensor[0][2] = ir.inertiaTensor[2][0] = PxF64(-intVV.z); + + // inertia tensor relative to center of mass + ir.COMInertiaTensor[0][0] = ir.inertiaTensor[0][0] -PxF64(mMassR*(ir.COM.y*ir.COM.y+ir.COM.z*ir.COM.z)); + ir.COMInertiaTensor[1][1] = ir.inertiaTensor[1][1] -PxF64(mMassR*(ir.COM.z*ir.COM.z+ir.COM.x*ir.COM.x)); + ir.COMInertiaTensor[2][2] = ir.inertiaTensor[2][2] -PxF64(mMassR*(ir.COM.x*ir.COM.x+ir.COM.y*ir.COM.y)); + ir.COMInertiaTensor[0][1] = ir.COMInertiaTensor[1][0] = (ir.inertiaTensor[0][1] +PxF64(mMassR*ir.COM.x*ir.COM.y)); + ir.COMInertiaTensor[1][2] = ir.COMInertiaTensor[2][1] = (ir.inertiaTensor[1][2] +PxF64(mMassR*ir.COM.y*ir.COM.z)); + ir.COMInertiaTensor[0][2] = ir.COMInertiaTensor[2][0] = (ir.inertiaTensor[0][2] +PxF64(mMassR*ir.COM.z*ir.COM.x)); + + // inertia tensor relative to (0,0,0) + if (!origin.isZero()) + { + PxVec3 sum = ir.COM + origin; + ir.inertiaTensor[0][0] -= PxF64(mMassR*((ir.COM.y*ir.COM.y+ir.COM.z*ir.COM.z) - (sum.y*sum.y+sum.z*sum.z))); + ir.inertiaTensor[1][1] -= PxF64(mMassR*((ir.COM.z*ir.COM.z+ir.COM.x*ir.COM.x) - (sum.z*sum.z+sum.x*sum.x))); + ir.inertiaTensor[2][2] -= PxF64(mMassR*((ir.COM.x*ir.COM.x+ir.COM.y*ir.COM.y) - (sum.x*sum.x+sum.y*sum.y))); + ir.inertiaTensor[0][1] = ir.inertiaTensor[1][0] = ir.inertiaTensor[0][1] + PxF64(mMassR*((ir.COM.x*ir.COM.y) - (sum.x*sum.y))); + ir.inertiaTensor[1][2] = ir.inertiaTensor[2][1] = ir.inertiaTensor[1][2] + PxF64(mMassR*((ir.COM.y*ir.COM.z) - (sum.y*sum.z))); + ir.inertiaTensor[0][2] = ir.inertiaTensor[2][0] = ir.inertiaTensor[0][2] + PxF64(mMassR*((ir.COM.z*ir.COM.x) - (sum.z*sum.x))); + ir.COM = sum; + } + + return true; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes volume integrals for a polyhedron by summing surface integrals over its faces. + * \param ir [out] a result structure. + * \param origin [in] the origin of the mesh vertices. All vertices will be shifted accordingly prior to computing the volume integrals. + Can improve accuracy, for example, if the centroid is used in the case of a convex mesh. Note: the returned inertia will not be relative to this origin but relative to (0,0,0). + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool VolumeIntegratorEberly::computeVolumeIntegrals(PxIntegrals& ir, const PxVec3& origin) + { + const PxF64 mult[10] = {1.0/6.0,1.0/24.0,1.0/24.0,1.0/24.0,1.0/60.0,1.0/60.0,1.0/60.0,1.0/120.0,1.0/120.0,1.0/120.0}; + PxF64 intg[10] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; // order: 1, x, y, z, x^2, y^2, z^2, xy, yz, zx + const PxVec3* hullVerts = static_cast (mDesc.points.data); + + for (PxU32 i = 0; i < mDesc.polygons.count; i++) + { + const Gu::HullPolygonData& polygon = (static_cast (mDesc.polygons.data))[i]; + const PxU8* Data = static_cast(mDesc.indices.data) + polygon.mVRef8; + const PxU32 NbVerts = polygon.mNbVerts; + for (PxU32 j = 0; j < NbVerts - 2; j++) + { + const PxVec3 p0 = hullVerts[Data[0]] - origin; + PxVec3 p1 = hullVerts[Data[(j + 1) % NbVerts]] - origin; + PxVec3 p2 = hullVerts[Data[(j + 2) % NbVerts]] - origin; + + PxVec3 cp = (p1 - p0).cross(p2 - p0); + + if(cp.dot(polygon.mPlane.n) < 0) + { + cp = -cp; + Ps::swap(p1,p2); + } + + PxF64 x0 = PxF64(p0.x); PxF64 y0 = PxF64(p0.y); PxF64 z0 = PxF64(p0.z); + PxF64 x1 = PxF64(p1.x); PxF64 y1 = PxF64(p1.y); PxF64 z1 = PxF64(p1.z); + PxF64 x2 = PxF64(p2.x); PxF64 y2 = PxF64(p2.y); PxF64 z2 = PxF64(p2.z); + + // get edges and cross product of edges + PxF64 d0 = PxF64(cp.x); PxF64 d1 = PxF64(cp.y); PxF64 d2 = PxF64(cp.z); + + // compute integral terms + PxF64 f1x; PxF64 f2x; PxF64 f3x; PxF64 g0x; PxF64 g1x; PxF64 g2x; + PxF64 f1y; PxF64 f2y; PxF64 f3y; PxF64 g0y; PxF64 g1y; PxF64 g2y; + PxF64 f1z; PxF64 f2z; PxF64 f3z; PxF64 g0z; PxF64 g1z; PxF64 g2z; + + subexpressions(x0, x1, x2, f1x, f2x, f3x, g0x, g1x, g2x); + subexpressions(y0, y1, y2, f1y, f2y, f3y, g0y, g1y, g2y); + subexpressions(z0, z1, z2, f1z, f2z, f3z, g0z, g1z, g2z); + + // update integrals + intg[0] += d0*f1x; + intg[1] += d0*f2x; intg[2] += d1*f2y; intg[3] += d2*f2z; + intg[4] += d0*f3x; intg[5] += d1*f3y; intg[6] += d2*f3z; + intg[7] += d0*(y0*g0x + y1*g1x + y2*g2x); + intg[8] += d1*(z0*g0y + z1*g1y + z2*g2y); + intg[9] += d2*(x0*g0z + x1*g1z + x2*g2z); + + } + } + + for (PxU32 i = 0; i < 10; i++) + { + intg[i] *= mult[i]; + } + + ir.mass = mMass = intg[0]; + // center of mass + ir.COM.x = PxReal(intg[1]/mMass); + ir.COM.y = PxReal(intg[2]/mMass); + ir.COM.z = PxReal(intg[3]/mMass); + + // inertia tensor relative to the provided origin parameter + ir.inertiaTensor[0][0] = intg[5]+intg[6]; + ir.inertiaTensor[1][1] = intg[4]+intg[6]; + ir.inertiaTensor[2][2] = intg[4]+intg[5]; + ir.inertiaTensor[0][1] = ir.inertiaTensor[1][0] = -intg[7]; + ir.inertiaTensor[1][2] = ir.inertiaTensor[2][1] = -intg[8]; + ir.inertiaTensor[0][2] = ir.inertiaTensor[2][0] = -intg[9]; + + // inertia tensor relative to center of mass + ir.COMInertiaTensor[0][0] = ir.inertiaTensor[0][0] -mMass*PxF64((ir.COM.y*ir.COM.y+ir.COM.z*ir.COM.z)); + ir.COMInertiaTensor[1][1] = ir.inertiaTensor[1][1] -mMass*PxF64((ir.COM.z*ir.COM.z+ir.COM.x*ir.COM.x)); + ir.COMInertiaTensor[2][2] = ir.inertiaTensor[2][2] -mMass*PxF64((ir.COM.x*ir.COM.x+ir.COM.y*ir.COM.y)); + ir.COMInertiaTensor[0][1] = ir.COMInertiaTensor[1][0] = (ir.inertiaTensor[0][1] +mMass*PxF64(ir.COM.x*ir.COM.y)); + ir.COMInertiaTensor[1][2] = ir.COMInertiaTensor[2][1] = (ir.inertiaTensor[1][2] +mMass*PxF64(ir.COM.y*ir.COM.z)); + ir.COMInertiaTensor[0][2] = ir.COMInertiaTensor[2][0] = (ir.inertiaTensor[0][2] +mMass*PxF64(ir.COM.z*ir.COM.x)); + + // inertia tensor relative to (0,0,0) + if (!origin.isZero()) + { + PxVec3 sum = ir.COM + origin; + ir.inertiaTensor[0][0] -= mMass*PxF64((ir.COM.y*ir.COM.y+ir.COM.z*ir.COM.z) - (sum.y*sum.y+sum.z*sum.z)); + ir.inertiaTensor[1][1] -= mMass*PxF64((ir.COM.z*ir.COM.z+ir.COM.x*ir.COM.x) - (sum.z*sum.z+sum.x*sum.x)); + ir.inertiaTensor[2][2] -= mMass*PxF64((ir.COM.x*ir.COM.x+ir.COM.y*ir.COM.y) - (sum.x*sum.x+sum.y*sum.y)); + ir.inertiaTensor[0][1] = ir.inertiaTensor[1][0] = ir.inertiaTensor[0][1] + mMass*PxF64((ir.COM.x*ir.COM.y) - (sum.x*sum.y)); + ir.inertiaTensor[1][2] = ir.inertiaTensor[2][1] = ir.inertiaTensor[1][2] + mMass*PxF64((ir.COM.y*ir.COM.z) - (sum.y*sum.z)); + ir.inertiaTensor[0][2] = ir.inertiaTensor[2][0] = ir.inertiaTensor[0][2] + mMass*PxF64((ir.COM.z*ir.COM.x) - (sum.z*sum.x)); + ir.COM = sum; + } + + return true; + } +} // namespace + +// Wrapper +bool computeVolumeIntegrals(const PxSimpleTriangleMesh& mesh, PxReal density, PxIntegrals& integrals) +{ + VolumeIntegrator v(mesh, PxF64(density)); + return v.computeVolumeIntegrals(integrals); +} + +// Wrapper +bool computeVolumeIntegralsEberly(const PxConvexMeshDesc& mesh, PxReal density, PxIntegrals& integrals, const PxVec3& origin) +{ + VolumeIntegratorEberly v(mesh, PxF64(density)); + v.computeVolumeIntegrals(integrals, origin); + return true; +} + +// Wrapper +bool computeVolumeIntegralsEberlySIMD(const PxConvexMeshDesc& mesh, PxReal density, PxIntegrals& integrals, const PxVec3& origin) +{ + VolumeIntegratorEberly v(mesh, PxF64(density)); + v.computeVolumeIntegralsSIMD(integrals, origin); + return true; +} + +} + +//#endif diff --git a/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h new file mode 100644 index 000000000..b8ae55beb --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h @@ -0,0 +1,102 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_FOUNDATION_NXVOLUMEINTEGRATION +#define PX_FOUNDATION_NXVOLUMEINTEGRATION +/** \addtogroup foundation + @{ +*/ + + +#include "foundation/Px.h" +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +class PxSimpleTriangleMesh; +class PxConvexMeshDesc; + +/** +\brief Data structure used to store mass properties. +*/ +struct PxIntegrals + { + PxVec3 COM; //!< Center of mass + PxF64 mass; //!< Total mass + PxF64 inertiaTensor[3][3]; //!< Inertia tensor (mass matrix) relative to the origin + PxF64 COMInertiaTensor[3][3]; //!< Inertia tensor (mass matrix) relative to the COM + + /** + \brief Retrieve the inertia tensor relative to the center of mass. + + \param inertia Inertia tensor. + */ + void getInertia(PxMat33& inertia) + { + for(PxU32 j=0;j<3;j++) + { + for(PxU32 i=0;i<3;i++) + { + inertia(i,j) = PxF32(COMInertiaTensor[i][j]); + } + } + } + + /** + \brief Retrieve the inertia tensor relative to the origin. + + \param inertia Inertia tensor. + */ + void getOriginInertia(PxMat33& inertia) + { + for(PxU32 j=0;j<3;j++) + { + for(PxU32 i=0;i<3;i++) + { + inertia(i,j) = PxF32(inertiaTensor[i][j]); + } + } + } + }; + + bool computeVolumeIntegrals(const PxSimpleTriangleMesh& mesh, PxReal density, PxIntegrals& integrals); + + // specialized method taking polygons directly, so we don't need to compute and store triangles for each polygon + bool computeVolumeIntegralsEberly(const PxConvexMeshDesc& mesh, PxReal density, PxIntegrals& integrals, const PxVec3& origin); // Eberly simplified method + + // specialized method taking polygons directly, so we don't need to compute and store triangles for each polygon, SIMD version + bool computeVolumeIntegralsEberlySIMD(const PxConvexMeshDesc& mesh, PxReal density, PxIntegrals& integrals, const PxVec3& origin); // Eberly simplified method +} + + /** @} */ +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp new file mode 100644 index 000000000..c0f4beeea --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp @@ -0,0 +1,29 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h new file mode 100644 index 000000000..5cc742b7a --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h @@ -0,0 +1,337 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_GRBTRIANGLEMESHCOOKING +#define PX_COLLISION_GRBTRIANGLEMESHCOOKING + +#include "GuMeshData.h" +#include "cooking/PxCooking.h" + +namespace physx +{ + namespace Gu + { + + } + +// TODO avoroshilov: remove duplicate definitions +static const PxU32 BOUNDARY = 0xffffffff; +static const PxU32 NONCONVEX_FLAG = 0x80000000; + +struct EdgeTriLookup +{ + PxU32 edgeId0, edgeId1; + PxU32 triId; + + bool operator < (const EdgeTriLookup& edge1) const + { + return edgeId0 < edge1.edgeId0 || (edgeId0 == edge1.edgeId0 && edgeId1 < edge1.edgeId1); + } + + bool operator <=(const EdgeTriLookup& edge1) const + { + return edgeId0 < edge1.edgeId0 || (edgeId0 == edge1.edgeId0 && edgeId1 <= edge1.edgeId1); + } +}; + + +static PxU32 binarySearch(const EdgeTriLookup* __restrict data, const PxU32 numElements, const EdgeTriLookup& value) +{ + PxU32 left = 0; + PxU32 right = numElements; + + while ((right - left) > 1) + { + PxU32 pos = (left + right) / 2; + const EdgeTriLookup& element = data[pos]; + if (element <= value) + { + left = pos; + } + else + { + right = pos; + } + } + + return left; +} + +// slightly different behavior from collide2: boundary edges are filtered out + +static PxU32 findAdjacent(const PxVec3* triVertices, const PxVec3* triNormals, const uint3* triIndices, + PxU32 nbTris, PxU32 i0, PxU32 i1, const PxPlane& plane, + EdgeTriLookup* triLookups, PxU32 triangleIndex) +{ + PxU32 result = BOUNDARY; + PxReal bestCos = -FLT_MAX; + + EdgeTriLookup lookup; + lookup.edgeId0 = PxMin(i0, i1); + lookup.edgeId1 = PxMax(i0, i1); + + PxU32 startIndex = binarySearch(triLookups, nbTris * 3, lookup); + + for (PxU32 a = startIndex; a > 0; --a) + { + if (triLookups[a - 1].edgeId0 == lookup.edgeId0 && triLookups[a - 1].edgeId1 == lookup.edgeId1) + startIndex = a - 1; + else + break; + } + + for (PxU32 a = startIndex; a < nbTris * 3; ++a) + { + EdgeTriLookup& edgeTri = triLookups[a]; + + if (edgeTri.edgeId0 != lookup.edgeId0 || edgeTri.edgeId1 != lookup.edgeId1) + break; + + if (edgeTri.triId == triangleIndex) + continue; + + const uint3& triIdx = triIndices[edgeTri.triId]; + PxU32 vIdx0 = triIdx.x; + PxU32 vIdx1 = triIdx.y; + PxU32 vIdx2 = triIdx.z; + + PxU32 other = vIdx0 + vIdx1 + vIdx2 - (i0 + i1); + + if (plane.distance(triVertices[other]) >= 0) + return NONCONVEX_FLAG | edgeTri.triId; + + PxReal c = plane.n.dot(triNormals[edgeTri.triId]); + if (c>bestCos) + { + bestCos = c; + result = edgeTri.triId; + } + + } + + return result; +} + + +static void buildAdjacencies(uint4* triAdjacencies, PxVec3* tempNormalsPerTri_prealloc, const PxVec3* triVertices, const uint3* triIndices, PxU32 nbTris) +{ + //PxVec3 * triNormals = new PxVec3[nbTris]; + + EdgeTriLookup* edgeLookups = reinterpret_cast(PX_ALLOC(sizeof(EdgeTriLookup) * nbTris * 3, PX_DEBUG_EXP("edgeLookups"))); + + + for (PxU32 i = 0; i < nbTris; i++) + { + const uint3& triIdx = triIndices[i]; + PxU32 vIdx0 = triIdx.x; + PxU32 vIdx1 = triIdx.y; + PxU32 vIdx2 = triIdx.z; + + tempNormalsPerTri_prealloc[i] = (triVertices[vIdx1] - triVertices[vIdx0]).cross(triVertices[vIdx2] - triVertices[vIdx0]).getNormalized(); + + edgeLookups[i * 3].edgeId0 = PxMin(vIdx0, vIdx1); + edgeLookups[i * 3].edgeId1 = PxMax(vIdx0, vIdx1); + edgeLookups[i * 3].triId = i; + + edgeLookups[i * 3 + 1].edgeId0 = PxMin(vIdx1, vIdx2); + edgeLookups[i * 3 + 1].edgeId1 = PxMax(vIdx1, vIdx2); + edgeLookups[i * 3 + 1].triId = i; + + edgeLookups[i * 3 + 2].edgeId0 = PxMin(vIdx0, vIdx2); + edgeLookups[i * 3 + 2].edgeId1 = PxMax(vIdx0, vIdx2); + edgeLookups[i * 3 + 2].triId = i; + } + + Ps::sort(edgeLookups, PxU32(nbTris * 3)); + + for (PxU32 i = 0; i < nbTris; i++) + { + const uint3& triIdx = triIndices[i]; + PxU32 vIdx0 = triIdx.x; + PxU32 vIdx1 = triIdx.y; + PxU32 vIdx2 = triIdx.z; + + PxPlane triPlane(triVertices[vIdx0], tempNormalsPerTri_prealloc[i]); + uint4 triAdjIdx; + + triAdjIdx.x = findAdjacent(triVertices, tempNormalsPerTri_prealloc, triIndices, nbTris, vIdx0, vIdx1, triPlane, edgeLookups, i); + triAdjIdx.y = findAdjacent(triVertices, tempNormalsPerTri_prealloc, triIndices, nbTris, vIdx1, vIdx2, triPlane, edgeLookups, i); + triAdjIdx.z = findAdjacent(triVertices, tempNormalsPerTri_prealloc, triIndices, nbTris, vIdx2, vIdx0, triPlane, edgeLookups, i); + triAdjIdx.w = 0; + + triAdjacencies[i] = triAdjIdx; + } + + + PX_FREE(edgeLookups); +} + +static bool isEdgeNonconvex(PxU32 adjEdgeIndex) +{ + return (adjEdgeIndex != BOUNDARY) && (adjEdgeIndex & NONCONVEX_FLAG); +} + +PX_INLINE void buildVertexConnection_p1(PxU32 * vertValency, PxU32 * vertNeighborStart, PxU32 & tempNumAdjVertices, const float4 * /*triVertices*/, const uint4 * triIndices, const uint4 * triAdjacencies, PxU32 nbVerts, PxU32 nbTris) +{ + tempNumAdjVertices = 0; + memset(vertValency, 0, nbVerts*sizeof(PxU32)); + + // Calculate max num of adjVerts + for (PxU32 i = 0; i < nbTris; i++) + { + uint4 triIdx = triIndices[i]; + PxU32 vi0 = triIdx.x; + PxU32 vi1 = triIdx.y; + PxU32 vi2 = triIdx.z; + + uint4 triAdjIdx = triAdjacencies[i]; + + PxU32 totalVertsAdded = 0; + if (!isEdgeNonconvex(triAdjIdx.x)) + { + ++vertValency[vi0]; + ++vertValency[vi1]; + totalVertsAdded += 2; + } + if (!isEdgeNonconvex(triAdjIdx.y)) + { + ++vertValency[vi1]; + ++vertValency[vi2]; + totalVertsAdded += 2; + } + if (!isEdgeNonconvex(triAdjIdx.z)) + { + ++vertValency[vi2]; + ++vertValency[vi0]; + totalVertsAdded += 2; + } + tempNumAdjVertices += totalVertsAdded; + } + PxU32 offset = 0; + for (PxU32 i = 0; i < nbVerts; i++) + { + vertNeighborStart[i] = offset; + offset += vertValency[i]; + } + + memset(vertValency, 0, nbVerts*sizeof(PxU32)); +} + +PX_INLINE PxU32 buildVertexConnection_p2(PxU32 * vertValency, PxU32 * vertNeighborStart, PxU32 * vertNeighboringPairs_prealloc, PxU32 tempNumAdjVertices, const float4 * /*triVertices*/, const uint4 * triIndices, const uint4 * triAdjacencies, PxU32 /*nbVerts*/, PxU32 nbTris) +{ + memset(vertNeighboringPairs_prealloc, 0xff, tempNumAdjVertices*2*sizeof(PxU32)); + + PxU32 newAdjVertsNum = 0; + for (PxU32 i = 0; i < nbTris; i++) + { + uint4 triIdx = triIndices[i]; + PxU32 vi[3] = + { + triIdx.x, + triIdx.y, + triIdx.z + }; + uint4 triAdjIdx = triAdjacencies[i]; + PxU32 ta[3] = + { + triAdjIdx.x, + triAdjIdx.y, + triAdjIdx.z + }; + + for (int tvi = 0; tvi < 3; ++tvi) + { + PxU32 curIdx = vi[tvi]; + PxU32 nextIdx = vi[(tvi+1)%3]; + if (!isEdgeNonconvex(ta[tvi])) + { + bool matchFound = false; + for (PxU32 valIdx = vertNeighborStart[curIdx], valIdxEnd = vertNeighborStart[curIdx] + vertValency[curIdx]; valIdx < valIdxEnd; ++valIdx) + { + if (vertNeighboringPairs_prealloc[valIdx*2+1] == nextIdx) + { + matchFound = true; + break; + } + } + + if (!matchFound) + { + PxU32 curPairIdx; + + curPairIdx = vertNeighborStart[curIdx] + vertValency[curIdx]; + vertNeighboringPairs_prealloc[curPairIdx*2+0] = curIdx; + vertNeighboringPairs_prealloc[curPairIdx*2+1] = nextIdx; + ++vertValency[curIdx]; + + curPairIdx = vertNeighborStart[nextIdx] + vertValency[nextIdx]; + vertNeighboringPairs_prealloc[curPairIdx*2+0] = nextIdx; + vertNeighboringPairs_prealloc[curPairIdx*2+1] = curIdx; + ++vertValency[nextIdx]; + + newAdjVertsNum += 2; + } + } + } + } + + return newAdjVertsNum; +} + +PX_INLINE void buildVertexConnection_p3(PxU32 * vertNeighbors, PxU32 * /*vertValency*/, PxU32 * vertNeighborStart, PxU32 * vertNeighboringPairs_prealloc, PxU32 tempNumAdjVertices, PxU32 newNumAdjVertices, const float4 * /*triVertices*/, const uint4 * /*triIndices*/, const uint4 * /*triAdjacencies*/, PxU32 /*nbVerts*/, PxU32 /*nbTris*/) +{ + PX_UNUSED(newNumAdjVertices); + PxU32 prevVertex = 0xFFffFFff; + PxU32 writingIndex = 0; + for (PxU32 i = 0; i < tempNumAdjVertices; ++i) + { + PxU32 curPairIdx0 = vertNeighboringPairs_prealloc[i*2+0]; + if (curPairIdx0 == 0xFFffFFff) + { + continue; + } + + PxU32 curPairIdx1 = vertNeighboringPairs_prealloc[i*2+1]; + vertNeighbors[writingIndex] = curPairIdx1; + if (curPairIdx0 != prevVertex) + { + vertNeighborStart[curPairIdx0] = writingIndex; + } + prevVertex = curPairIdx0; + + ++writingIndex; + } + + PX_ASSERT(writingIndex == newNumAdjVertices); +} + +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp new file mode 100644 index 000000000..2b6056b83 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp @@ -0,0 +1,84 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "GuHeightField.h" +#include "GuSerialize.h" + +using namespace physx; +using namespace Gu; + +namespace physx +{ + +bool saveHeightField(const HeightField& hf, PxOutputStream& stream, bool endian) +{ + // write header + if(!writeHeader('H', 'F', 'H', 'F', PX_HEIGHTFIELD_VERSION, endian, stream)) + return false; + + const Gu::HeightFieldData& hfData = hf.getData(); + + // write mData members + writeDword(hfData.rows, endian, stream); + writeDword(hfData.columns, endian, stream); + writeFloat(hfData.rowLimit, endian, stream); + writeFloat(hfData.colLimit, endian, stream); + writeFloat(hfData.nbColumns, endian, stream); + writeFloat(0.0f, endian, stream); // thickness + writeFloat(hfData.convexEdgeThreshold, endian, stream); + writeWord(hfData.flags, endian, stream); + writeDword(hfData.format, endian, stream); + + writeFloat(hfData.mAABB.getMin(0), endian, stream); + writeFloat(hfData.mAABB.getMin(1), endian, stream); + writeFloat(hfData.mAABB.getMin(2), endian, stream); + writeFloat(hfData.mAABB.getMax(0), endian, stream); + writeFloat(hfData.mAABB.getMax(1), endian, stream); + writeFloat(hfData.mAABB.getMax(2), endian, stream); + + // write this-> members + writeDword(hf.mSampleStride, endian, stream); + writeDword(hf.mNbSamples, endian, stream); + writeFloat(hf.mMinHeight, endian, stream); + writeFloat(hf.mMaxHeight, endian, stream); + + // write samples + for(PxU32 i = 0; i < hf.mNbSamples; i++) + { + const PxHeightFieldSample& s = hfData.samples[i]; + writeWord(PxU16(s.height), endian, stream); + stream.write(&s.materialIndex0, sizeof(s.materialIndex0)); + stream.write(&s.materialIndex1, sizeof(s.materialIndex1)); + } + + return true; +} + +} diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h new file mode 100644 index 000000000..e3d5ef89f --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h @@ -0,0 +1,35 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuHeightField.h" + +namespace physx +{ + bool saveHeightField(const Gu::HeightField& hf, PxOutputStream& stream, bool endianSwap); +} diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp new file mode 100644 index 000000000..4da4ad3af --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "MeshBuilder.h" +#include "GuInternal.h" + +namespace physx +{ + //#define PROFILE_BOUNDS +#ifdef PROFILE_BOUNDS +#include +#pragma comment(lib, "winmm.lib") +#endif + + void MeshBulider::computeLocalBounds(Gu::MeshDataBase& meshData) + { +#ifdef PROFILE_BOUNDS + int time = timeGetTime(); +#endif + + PxBounds3& localBounds = meshData.mAABB; + Gu::computeBoundsAroundVertices(localBounds, meshData.mNbVertices, meshData.mVertices); + + // Derive a good geometric epsilon from local bounds. We must do this before bounds extrusion for heightfields. + // + // From Charles Bloom: + // "Epsilon must be big enough so that the consistency condition abs(D(Hit)) + // <= Epsilon is satisfied for all queries. You want the smallest epsilon + // you can have that meets that constraint. Normal floats have a 24 bit + // mantissa. When you do any float addition, you may have round-off error + // that makes the result off by roughly 2^-24 * result. Our result is + // scaled by the position values. If our world is strictly required to be + // in a box of world size W (each coordinate in -W to W), then the maximum + // error is 2^-24 * W. Thus Epsilon must be at least >= 2^-24 * W. If + // you're doing coordinate transforms, that may scale your error up by some + // amount, so you'll need a bigger epsilon. In general something like + // 2^-22*W is reasonable. If you allow scaled transforms, it needs to be + // something like 2^-22*W*MAX_SCALE." + // PT: TODO: runtime checkings for this + PxReal geomEpsilon = 0.0f; + for (PxU32 i = 0; i < 3; i++) + geomEpsilon = PxMax(geomEpsilon, PxMax(PxAbs(localBounds.maximum[i]), PxAbs(localBounds.minimum[i]))); + geomEpsilon *= powf(2.0f, -22.0f); + meshData.mGeomEpsilon = geomEpsilon; + +#ifdef PROFILE_BOUNDS + int deltaTime = timeGetTime() - time; + printf("Bounds time: %f\n", float(deltaTime)*0.001f); +#endif + } + +} diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h new file mode 100644 index 000000000..b7cb901c4 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h @@ -0,0 +1,45 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_MESH_BUILDER +#define PX_COLLISION_MESH_BUILDER + +#include "GuMeshData.h" + +namespace physx +{ + class MeshBulider + { + protected: + void computeLocalBounds(Gu::MeshDataBase& meshData); + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h b/src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h new file mode 100644 index 000000000..e108f701a --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h @@ -0,0 +1,114 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef QUICKSELECT_H +#define QUICKSELECT_H + +#include "foundation/PxSimpleTypes.h" + +// Google "wikipedia QuickSelect" for algorithm explanation +namespace physx { namespace quickSelect { + + + #define SWAP32(x, y) { PxU32 tmp = y; y = x; x = tmp; } + + // left is the index of the leftmost element of the subarray + // right is the index of the rightmost element of the subarray (inclusive) + // number of elements in subarray = right-left+1 + template + PxU32 partition(PxU32* PX_RESTRICT a, PxU32 left, PxU32 right, PxU32 pivotIndex, const LtEq& cmpLtEq) + { + PX_ASSERT(pivotIndex >= left && pivotIndex <= right); + PxU32 pivotValue = a[pivotIndex]; + SWAP32(a[pivotIndex], a[right]) // Move pivot to end + PxU32 storeIndex = left; + for (PxU32 i = left; i < right; i++) // left <= i < right + if (cmpLtEq(a[i], pivotValue)) + { + SWAP32(a[i], a[storeIndex]); + storeIndex++; + } + SWAP32(a[storeIndex], a[right]); // Move pivot to its final place + for (PxU32 i = left; i < storeIndex; i++) + PX_ASSERT(cmpLtEq(a[i], a[storeIndex])); + for (PxU32 i = storeIndex+1; i <= right; i++) + PX_ASSERT(cmpLtEq(a[storeIndex], a[i])); + return storeIndex; + } + + // left is the index of the leftmost element of the subarray + // right is the index of the rightmost element of the subarray (inclusive) + // number of elements in subarray = right-left+1 + // recursive version + template + void quickFindFirstK(PxU32* PX_RESTRICT a, PxU32 left, PxU32 right, PxU32 k, const LtEq& cmpLtEq) + { + PX_ASSERT(k <= right-left+1); + if (right > left) + { + // select pivotIndex between left and right + PxU32 pivotIndex = (left + right) >> 1; + PxU32 pivotNewIndex = partition(a, left, right, pivotIndex, cmpLtEq); + // now all elements to the left of pivotNewIndex are < old value of a[pivotIndex] (bottom half values) + if (pivotNewIndex > left + k) // new condition + quickFindFirstK(a, left, pivotNewIndex-1, k, cmpLtEq); + if (pivotNewIndex < left + k) + quickFindFirstK(a, pivotNewIndex+1, right, k+left-pivotNewIndex-1, cmpLtEq); + } + } + + // non-recursive version + template + void quickSelectFirstK(PxU32* PX_RESTRICT a, PxU32 left, PxU32 right, PxU32 k, const LtEq& cmpLtEq) + { + PX_ASSERT(k <= right-left+1); + for (;;) + { + PxU32 pivotIndex = (left+right) >> 1; + PxU32 pivotNewIndex = partition(a, left, right, pivotIndex, cmpLtEq); + PxU32 pivotDist = pivotNewIndex - left + 1; + if (pivotDist == k) + return; + else if (k < pivotDist) + { + PX_ASSERT(pivotNewIndex > 0); + right = pivotNewIndex - 1; + } + else + { + k = k - pivotDist; + left = pivotNewIndex+1; + } + } + } + +} } // namespace quickSelect, physx + +#endif + diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp new file mode 100644 index 000000000..f42a1c3c3 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp @@ -0,0 +1,894 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxBounds3.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "RTreeCooking.h" +#include "PsSort.h" +#include "PsMathUtils.h" +#include "PsAllocator.h" +#include "PsVecMath.h" +#include "PxTolerancesScale.h" +#include "QuickSelect.h" +#include "PsInlineArray.h" +#include "GuRTree.h" + +#define PRINT_RTREE_COOKING_STATS 0 // AP: keeping this frequently used macro for diagnostics/benchmarking + +#if PRINT_RTREE_COOKING_STATS +#include +#endif + +using namespace physx::Gu; +using namespace physx::shdfnd; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +// Intermediate non-quantized representation for RTree node in a page (final format is SIMD transposed page) +struct RTreeNodeNQ +{ + PxBounds3 bounds; + PxI32 childPageFirstNodeIndex; // relative to the beginning of all build tree nodes array + PxI32 leafCount; // -1 for empty nodes, 0 for non-terminal nodes, number of enclosed tris if non-zero (LeafTriangles), also means a terminal node + + struct U {}; // selector struct for uninitialized constructor + RTreeNodeNQ(U) {} // uninitialized constructor + RTreeNodeNQ() : bounds(PxBounds3::empty()), childPageFirstNodeIndex(-1), leafCount(0) {} +}; + +// SIMD version of bounds class +struct PxBounds3V +{ + struct U {}; // selector struct for uninitialized constructor + Vec3V mn, mx; + PxBounds3V(Vec3VArg mn_, Vec3VArg mx_) : mn(mn_), mx(mx_) {} + PxBounds3V(U) {} // uninitialized constructor + + PX_FORCE_INLINE Vec3V getExtents() const { return V3Sub(mx, mn); } + PX_FORCE_INLINE void include(const PxBounds3V& other) { mn = V3Min(mn, other.mn); mx = V3Max(mx, other.mx); } + + // convert vector extents to PxVec3 + PX_FORCE_INLINE const PxVec3 getMinVec3() const { PxVec3 ret; V3StoreU(mn, ret); return ret; } + PX_FORCE_INLINE const PxVec3 getMaxVec3() const { PxVec3 ret; V3StoreU(mx, ret); return ret; } +}; + +static void buildFromBounds( + Gu::RTree& resultTree, const PxBounds3V* allBounds, PxU32 numBounds, + Array& resultPermute, RTreeCooker::RemapCallback* rc, Vec3VArg allMn, Vec3VArg allMx, + PxReal sizePerfTradeOff, PxMeshCookingHint::Enum hint); + +///////////////////////////////////////////////////////////////////////// +void RTreeCooker::buildFromTriangles( + Gu::RTree& result, const PxVec3* verts, PxU32 numVerts, const PxU16* tris16, const PxU32* tris32, PxU32 numTris, + Array& resultPermute, RTreeCooker::RemapCallback* rc, PxReal sizePerfTradeOff01, PxMeshCookingHint::Enum hint) +{ + PX_UNUSED(numVerts); + Array allBounds; + allBounds.reserve(numTris); + Vec3V allMn = Vec3V_From_FloatV(FMax()), allMx = Vec3V_From_FloatV(FNegMax()); + Vec3V eps = V3Splat(FLoad(5e-4f)); // AP scaffold: use PxTolerancesScale here? + + // build RTree AABB bounds from triangles, conservative bound inflation is also performed here + for(PxU32 i = 0; i < numTris; i ++) + { + PxU32 i0, i1, i2; + PxU32 i3 = i*3; + if(tris16) + { + i0 = tris16[i3]; i1 = tris16[i3+1]; i2 = tris16[i3+2]; + } else + { + i0 = tris32[i3]; i1 = tris32[i3+1]; i2 = tris32[i3+2]; + } + PX_ASSERT_WITH_MESSAGE(i0 < numVerts && i1 < numVerts && i2 < numVerts ,"Input mesh triangle's vertex index exceeds specified numVerts."); + Vec3V v0 = V3LoadU(verts[i0]), v1 = V3LoadU(verts[i1]), v2 = V3LoadU(verts[i2]); + Vec3V mn = V3Sub(V3Min(V3Min(v0, v1), v2), eps); // min over 3 verts, subtract eps to inflate + Vec3V mx = V3Add(V3Max(V3Max(v0, v1), v2), eps); // max over 3 verts, add eps to inflate + allMn = V3Min(allMn, mn); allMx = V3Max(allMx, mx); + allBounds.pushBack(PxBounds3V(mn, mx)); + } + + buildFromBounds(result, allBounds.begin(), numTris, resultPermute, rc, allMn, allMx, sizePerfTradeOff01, hint); +} + +///////////////////////////////////////////////////////////////////////// +// Fast but lower quality 4-way split sorting using repeated application of quickselect + +// comparator template struct for sortin gbounds centers given a coordinate index (x,y,z=0,1,2) +struct BoundsLTE +{ + PxU32 coordIndex; + const PxVec3* PX_RESTRICT boundCenters; // AP: precomputed centers are faster than recomputing the centers + BoundsLTE(PxU32 coordIndex_, const PxVec3* boundCenters_) + : coordIndex(coordIndex_), boundCenters(boundCenters_) + {} + + PX_FORCE_INLINE bool operator()(const PxU32 & idx1, const PxU32 & idx2) const + { + PxF32 center1 = boundCenters[idx1][coordIndex]; + PxF32 center2 = boundCenters[idx2][coordIndex]; + return (center1 <= center2); + } +}; + +// ====================================================================== +// Quick sorting method +// recursive sorting procedure: +// 1. find min and max extent along each axis for the current cluster +// 2. split input cluster into two 3 times using quickselect, splitting off a quarter of the initial cluster size each time +// 3. the axis is potentialy different for each split using the following +// approximate splitting heuristic - reduce max length by some estimated factor to encourage split along other axis +// since we cut off between a quarter to a half of elements in this direction per split +// the reduction for first split should be *0.75f but we use 0.8 +// to account for some node overlap. This is somewhat of an arbitrary choice and there's room for improvement. +// 4. recurse on new clusters (goto step 1) +// +struct SubSortQuick +{ + static const PxReal reductionFactors[RTREE_N-1]; + + enum { NTRADEOFF = 9 }; + static const PxU32 stopAtTrisPerLeaf1[NTRADEOFF]; // presets for PxCookingParams::meshSizePerformanceTradeoff implementation + + const PxU32* permuteEnd; + const PxU32* permuteStart; + const PxBounds3V* allBounds; + Array boundCenters; + PxU32 maxBoundsPerLeafPage; + + // initialize the context for the sorting routine + SubSortQuick(PxU32* permute, const PxBounds3V* allBounds_, PxU32 allBoundsSize, PxReal sizePerfTradeOff01) + : allBounds(allBounds_) + { + permuteEnd = permute + allBoundsSize; + permuteStart = permute; + PxU32 boundsCount = allBoundsSize; + boundCenters.reserve(boundsCount); // AP - measured that precomputing centers helps with perf significantly (~20% on 1k verts) + for(PxU32 i = 0; i < boundsCount; i++) + boundCenters.pushBack( allBounds[i].getMinVec3() + allBounds[i].getMaxVec3() ); + PxU32 iTradeOff = PxMin( PxU32(PxMax(0.0f, sizePerfTradeOff01)*NTRADEOFF), NTRADEOFF-1 ); + maxBoundsPerLeafPage = stopAtTrisPerLeaf1[iTradeOff]; + } + + // implements the sorting/splitting procedure + void sort4( + PxU32* PX_RESTRICT permute, const PxU32 clusterSize, // beginning and size of current recursively processed cluster + Array& resultTree, PxU32& maxLevels, + PxBounds3V& subTreeBound, PxU32 level = 0) + { + if(level == 0) + maxLevels = 1; + else + maxLevels = PxMax(maxLevels, level+1); + + PX_ASSERT(permute + clusterSize <= permuteEnd); + PX_ASSERT(maxBoundsPerLeafPage >= RTREE_N-1); + + const PxU32 cluster4 = PxMax(clusterSize/RTREE_N, 1); + + PX_ASSERT(clusterSize > 0); + // find min and max world bound for current cluster + Vec3V mx = allBounds[permute[0]].mx, mn = allBounds[permute[0]].mn; PX_ASSERT(permute[0] < boundCenters.size()); + for(PxU32 i = 1; i < clusterSize; i ++) + { + PX_ASSERT(permute[i] < boundCenters.size()); + mx = V3Max(mx, allBounds[permute[i]].mx); + mn = V3Min(mn, allBounds[permute[i]].mn); + } + PX_ALIGN_PREFIX(16) PxReal maxElem[4] PX_ALIGN_SUFFIX(16); + V3StoreA(V3Sub(mx, mn), *reinterpret_cast(maxElem)); // compute the dimensions and store into a scalar maxElem array + + // split along the longest axis + const PxU32 maxDiagElement = PxU32(maxElem[0] > maxElem[1] && maxElem[0] > maxElem[2] ? 0 : (maxElem[1] > maxElem[2] ? 1 : 2)); + BoundsLTE cmpLte(maxDiagElement, boundCenters.begin()); + + const PxU32 startNodeIndex = resultTree.size(); + resultTree.resizeUninitialized(startNodeIndex+RTREE_N); // at each recursion level we add 4 nodes to the tree + + PxBounds3V childBound( (PxBounds3V::U()) ); // start off uninitialized for performance + const PxI32 leftover = PxMax(PxI32(clusterSize - cluster4*(RTREE_N-1)), 0); + PxU32 totalCount = 0; + for(PxU32 i = 0; i < RTREE_N; i++) + { + // split off cluster4 count nodes out of the entire cluster for each i + const PxU32 clusterOffset = cluster4*i; + PxU32 count1; // cluster4 or leftover depending on whether it's the last cluster + if(i < RTREE_N-1) + { + // only need to so quickSelect for the first pagesize-1 clusters + if(clusterOffset <= clusterSize-1) + { + quickSelect::quickSelectFirstK(permute, clusterOffset, clusterSize-1, cluster4, cmpLte); + // approximate heuristic - reduce max length by some estimated factor to encourage split along other axis + // since we cut off a quarter of elements in this direction the reduction should be *0.75f but we use 0.8 + // to account for some node overlap. This is somewhat of an arbitrary choice though + maxElem[cmpLte.coordIndex] *= reductionFactors[i]; + // recompute cmpLte.coordIndex from updated maxElements + cmpLte.coordIndex = PxU32(maxElem[0] > maxElem[1] && maxElem[0] > maxElem[2] ? 0 : (maxElem[1] > maxElem[2] ? 1 : 2)); + } + count1 = cluster4; + } else + { + count1 = PxU32(leftover); + // verify that leftover + sum of previous clusters adds up to clusterSize or leftover is 0 + // leftover can be 0 if clusterSize higher index = better perf +static const PxU32 NTRADEOFF = 15; + // % -24 -23 -17 -15 -10 -8 -5 -3 0 +3 +3 +5 +7 +8 +9 - % raycast MeshSurface*Random benchmark perf + // K 717 734 752 777 793 811 824 866 903 939 971 1030 1087 1139 1266 - testzone size in K + // # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - preset number +static const PxU32 stopAtTrisPerPage[NTRADEOFF] = { 64, 60, 56, 48, 46, 44, 40, 36, 32, 28, 24, 20, 16, 12, 12}; +static const PxU32 stopAtTrisPerLeaf[NTRADEOFF] = { 16, 14, 12, 10, 9, 8, 8, 6, 5, 5, 5, 4, 4, 4, 2}; // capped at 2 anyway + +///////////////////////////////////////////////////////////////////////// +// comparator struct for sorting the bounds along a specified coordIndex (coordIndex=0,1,2 for X,Y,Z) +struct SortBoundsPredicate +{ + PxU32 coordIndex; + const PxBounds3V* allBounds; + SortBoundsPredicate(PxU32 coordIndex_, const PxBounds3V* allBounds_) : coordIndex(coordIndex_), allBounds(allBounds_) + {} + + bool operator()(const PxU32 & idx1, const PxU32 & idx2) const + { + // using the bounds center for comparison + PxF32 center1 = V3ReadXYZ(allBounds[idx1].mn)[coordIndex] + V3ReadXYZ(allBounds[idx1].mx)[coordIndex]; + PxF32 center2 = V3ReadXYZ(allBounds[idx2].mn)[coordIndex] + V3ReadXYZ(allBounds[idx2].mx)[coordIndex]; + return (center1 < center2); + } +}; + + +///////////////////////////////////////////////////////////////////////// +// auxiliary class for SAH build (SAH = surface area heuristic) +struct Interval +{ + PxU32 start, count; + Interval(PxU32 s, PxU32 c) : start(s), count(c) {} +}; + +// SAH function - returns surface area for given AABB extents +static PX_FORCE_INLINE void PxSAH(const Vec3VArg v, PxF32& sah) +{ + FStore(V3Dot(v, V3PermZXY(v)), &sah); // v.x*v.y + v.y*v.z + v.x*v.z; +} + +struct SubSortSAH +{ + PxU32* PX_RESTRICT permuteStart, *PX_RESTRICT tempPermute; + const PxBounds3V* PX_RESTRICT allBounds; + PxF32* PX_RESTRICT metricL; + PxF32* PX_RESTRICT metricR; + const PxU32* PX_RESTRICT xOrder, *PX_RESTRICT yOrder, *PX_RESTRICT zOrder; + const PxU32* PX_RESTRICT xRanks, *PX_RESTRICT yRanks, *PX_RESTRICT zRanks; + PxU32* PX_RESTRICT tempRanks; + PxU32 nbTotalBounds; + PxU32 iTradeOff; + + // precompute various values used during sort + SubSortSAH( + PxU32* permute, const PxBounds3V* allBounds_, PxU32 numBounds, + const PxU32* xOrder_, const PxU32* yOrder_, const PxU32* zOrder_, + const PxU32* xRanks_, const PxU32* yRanks_, const PxU32* zRanks_, PxReal sizePerfTradeOff01) + : permuteStart(permute), allBounds(allBounds_), + xOrder(xOrder_), yOrder(yOrder_), zOrder(zOrder_), + xRanks(xRanks_), yRanks(yRanks_), zRanks(zRanks_), nbTotalBounds(numBounds) + { + metricL = reinterpret_cast(PX_ALLOC(sizeof(PxF32)*numBounds, PX_DEBUG_EXP("metricL"))); + metricR = reinterpret_cast(PX_ALLOC(sizeof(PxF32)*numBounds, PX_DEBUG_EXP("metricR"))); + tempPermute = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*(numBounds*2+1), PX_DEBUG_EXP("tempPermute"))); + tempRanks = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*numBounds, PX_DEBUG_EXP("tempRanks"))); + iTradeOff = PxMin( PxU32(PxMax(0.0f, sizePerfTradeOff01)*NTRADEOFF), NTRADEOFF-1 ); + } + + ~SubSortSAH() // release temporarily used memory + { + PX_FREE_AND_RESET(metricL); + PX_FREE_AND_RESET(metricR); + PX_FREE_AND_RESET(tempPermute); + PX_FREE_AND_RESET(tempRanks); + } + + //////////////////////////////////////////////////////////////////// + // returns split position for second array start relative to permute ptr + PxU32 split(PxU32* permute, PxU32 clusterSize) + { + if(clusterSize <= 1) + return 0; + if(clusterSize == 2) + return 1; + + PxI32 minCount = clusterSize >= 4 ? 2 : 1; + PxI32 splitStartL = minCount; // range=[startL->endL) + PxI32 splitEndL = PxI32(clusterSize-minCount); + PxI32 splitStartR = PxI32(clusterSize-splitStartL); // range=(endR<-startR], startR > endR + PxI32 splitEndR = PxI32(clusterSize-splitEndL); + PX_ASSERT(splitEndL-splitStartL == splitStartR-splitEndR); + PX_ASSERT(splitStartL <= splitEndL); + PX_ASSERT(splitStartR >= splitEndR); + PX_ASSERT(splitEndR >= 1); + PX_ASSERT(splitEndL < PxI32(clusterSize)); + + // pick the best axis with some splitting metric + // axis index is X=0, Y=1, Z=2 + PxF32 minMetric[3]; + PxU32 minMetricSplit[3]; + const PxU32* ranks3[3] = { xRanks, yRanks, zRanks }; + const PxU32* orders3[3] = { xOrder, yOrder, zOrder }; + for(PxU32 coordIndex = 0; coordIndex <= 2; coordIndex++) + { + SortBoundsPredicate sortPredicateLR(coordIndex, allBounds); + + const PxU32* rank = ranks3[coordIndex]; + const PxU32* order = orders3[coordIndex]; + + // build ranks in tempPermute + if(clusterSize == nbTotalBounds) // AP: about 4% perf gain from this optimization + { + // if this is a full cluster sort, we already have it done + for(PxU32 i = 0; i < clusterSize; i ++) + tempPermute[i] = order[i]; + } else + { + // sort the tempRanks + for(PxU32 i = 0; i < clusterSize; i ++) + tempRanks[i] = rank[permute[i]]; + Ps::sort(tempRanks, clusterSize); + for(PxU32 i = 0; i < clusterSize; i ++) // convert back from ranks to indices + tempPermute[i] = order[tempRanks[i]]; + } + + // we consider overlapping intervals for minimum sum of metrics + // left interval is from splitStartL up to splitEndL + // right interval is from splitStartR down to splitEndR + + + // first compute the array metricL + Vec3V boundsLmn = allBounds[tempPermute[0]].mn; // init with 0th bound + Vec3V boundsLmx = allBounds[tempPermute[0]].mx; // init with 0th bound + PxI32 ii; + for(ii = 1; ii < splitStartL; ii++) // sweep right to include all bounds up to splitStartL-1 + { + boundsLmn = V3Min(boundsLmn, allBounds[tempPermute[ii]].mn); + boundsLmx = V3Max(boundsLmx, allBounds[tempPermute[ii]].mx); + } + + PxU32 countL0 = 0; + for(ii = splitStartL; ii <= splitEndL; ii++) // compute metric for inclusive bounds from splitStartL to splitEndL + { + boundsLmn = V3Min(boundsLmn, allBounds[tempPermute[ii]].mn); + boundsLmx = V3Max(boundsLmx, allBounds[tempPermute[ii]].mx); + PxSAH(V3Sub(boundsLmx, boundsLmn), metricL[countL0++]); + } + // now we have metricL + + // now compute the array metricR + Vec3V boundsRmn = allBounds[tempPermute[clusterSize-1]].mn; // init with last bound + Vec3V boundsRmx = allBounds[tempPermute[clusterSize-1]].mx; // init with last bound + for(ii = PxI32(clusterSize-2); ii > splitStartR; ii--) // include bounds to the left of splitEndR down to splitStartR + { + boundsRmn = V3Min(boundsRmn, allBounds[tempPermute[ii]].mn); + boundsRmx = V3Max(boundsRmx, allBounds[tempPermute[ii]].mx); + } + + PxU32 countR0 = 0; + for(ii = splitStartR; ii >= splitEndR; ii--) // continue sweeping left, including bounds and recomputing the metric + { + boundsRmn = V3Min(boundsRmn, allBounds[tempPermute[ii]].mn); + boundsRmx = V3Max(boundsRmx, allBounds[tempPermute[ii]].mx); + PxSAH(V3Sub(boundsRmx, boundsRmn), metricR[countR0++]); + } + + PX_ASSERT((countL0 == countR0) && (countL0 == PxU32(splitEndL-splitStartL+1))); + + // now iterate over splitRange and compute the minimum sum of SAHLeft*countLeft + SAHRight*countRight + PxU32 minMetricSplitPosition = 0; + PxF32 minMetricLocal = PX_MAX_REAL; + const PxI32 hsI32 = PxI32(clusterSize/2); + const PxI32 splitRange = (splitEndL-splitStartL+1); + for(ii = 0; ii < splitRange; ii++) + { + PxF32 countL = PxF32(ii+minCount); // need to add minCount since ii iterates over splitRange + PxF32 countR = PxF32(splitRange-ii-1+minCount); + PX_ASSERT(PxU32(countL + countR) == clusterSize); + + const PxF32 metric = (countL*metricL[ii] + countR*metricR[splitRange-ii-1]); + const PxU32 splitPos = PxU32(ii+splitStartL); + if(metric < minMetricLocal || + (metric <= minMetricLocal && // same metric but more even split + PxAbs(PxI32(splitPos)-hsI32) < PxAbs(PxI32(minMetricSplitPosition)-hsI32))) + { + minMetricLocal = metric; + minMetricSplitPosition = splitPos; + } + } + + minMetric[coordIndex] = minMetricLocal; + minMetricSplit[coordIndex] = minMetricSplitPosition; + + // sum of axis lengths for both left and right AABBs + } + + PxU32 winIndex = 2; + if(minMetric[0] <= minMetric[1] && minMetric[0] <= minMetric[2]) + winIndex = 0; + else if(minMetric[1] <= minMetric[2]) + winIndex = 1; + + const PxU32* rank = ranks3[winIndex]; + const PxU32* order = orders3[winIndex]; + if(clusterSize == nbTotalBounds) // AP: about 4% gain from this special case optimization + { + // if this is a full cluster sort, we already have it done + for(PxU32 i = 0; i < clusterSize; i ++) + permute[i] = order[i]; + } else + { + // sort the tempRanks + for(PxU32 i = 0; i < clusterSize; i ++) + tempRanks[i] = rank[permute[i]]; + Ps::sort(tempRanks, clusterSize); + for(PxU32 i = 0; i < clusterSize; i ++) + permute[i] = order[tempRanks[i]]; + } + + PxU32 splitPoint = minMetricSplit[winIndex]; + if(clusterSize == 3 && splitPoint == 0) + splitPoint = 1; // special case due to rounding + return splitPoint; + } + + // compute surface area for a given split + PxF32 computeSA(const PxU32* permute, const Interval& split) // both permute and i are relative + { + PX_ASSERT(split.count >= 1); + Vec3V bmn = allBounds[permute[split.start]].mn; + Vec3V bmx = allBounds[permute[split.start]].mx; + for(PxU32 i = 1; i < split.count; i++) + { + const PxBounds3V& b1 = allBounds[permute[split.start+i]]; + bmn = V3Min(bmn, b1.mn); bmx = V3Max(bmx, b1.mx); + } + + PxF32 ret; PxSAH(V3Sub(bmx, bmn), ret); + return ret; + } + + //////////////////////////////////////////////////////////////////// + // main SAH sort routine + void sort4(PxU32* permute, PxU32 clusterSize, + Array& resultTree, PxU32& maxLevels, PxU32 level = 0, RTreeNodeNQ* parentNode = NULL) + { + PX_UNUSED(parentNode); + + if(level == 0) + maxLevels = 1; + else + maxLevels = PxMax(maxLevels, level+1); + + PxU32 splitPos[RTREE_N]; + for(PxU32 j = 0; j < RTREE_N; j++) + splitPos[j] = j+1; + + if(clusterSize >= RTREE_N) + { + // split into RTREE_N number of regions via RTREE_N-1 subsequent splits + // each split is represented as a current interval + // we iterate over currently active intervals and compute it's surface area + // then we split the interval with maximum surface area + // AP scaffold: possible optimization - seems like computeSA can be cached for unchanged intervals + InlineArray splits; + splits.pushBack(Interval(0, clusterSize)); + for(PxU32 iSplit = 0; iSplit < RTREE_N-1; iSplit++) + { + PxF32 maxSAH = -FLT_MAX; + PxU32 maxSplit = 0xFFFFffff; + for(PxU32 i = 0; i < splits.size(); i++) + { + if(splits[i].count == 1) + continue; + PxF32 SAH = computeSA(permute, splits[i])*splits[i].count; + if(SAH > maxSAH) + { + maxSAH = SAH; + maxSplit = i; + } + } + PX_ASSERT(maxSplit != 0xFFFFffff); + + // maxSplit is now the index of the interval in splits array with maximum surface area + // we now split it into 2 using the split() function + Interval old = splits[maxSplit]; + PX_ASSERT(old.count > 1); + PxU32 splitLocal = split(permute+old.start, old.count); // relative split pos + + PX_ASSERT(splitLocal >= 1); + PX_ASSERT(old.count-splitLocal >= 1); + splits.pushBack(Interval(old.start, splitLocal)); + splits.pushBack(Interval(old.start+splitLocal, old.count-splitLocal)); + splits.replaceWithLast(maxSplit); + splitPos[iSplit] = old.start+splitLocal; + } + + // verification code, make sure split counts add up to clusterSize + PX_ASSERT(splits.size() == RTREE_N); + PxU32 sum = 0; + for(PxU32 j = 0; j < RTREE_N; j++) + sum += splits[j].count; + PX_ASSERT(sum == clusterSize); + } + else // clusterSize < RTREE_N + { + // make it so splitCounts based on splitPos add up correctly for small cluster sizes + for(PxU32 i = clusterSize; i < RTREE_N-1; i++) + splitPos[i] = clusterSize; + } + + // sort splitPos index array using quicksort (just a few values) + Ps::sort(splitPos, RTREE_N-1); + splitPos[RTREE_N-1] = clusterSize; // splitCount[n] is computed as splitPos[n+1]-splitPos[n], so we need to add this last value + + // now compute splitStarts and splitCounts from splitPos[] array. Also perform a bunch of correctness verification + PxU32 splitStarts[RTREE_N]; + PxU32 splitCounts[RTREE_N]; + splitStarts[0] = 0; + splitCounts[0] = splitPos[0]; + PxU32 sumCounts = splitCounts[0]; + for(PxU32 j = 1; j < RTREE_N; j++) + { + splitStarts[j] = splitPos[j-1]; + PX_ASSERT(splitStarts[j-1]<=splitStarts[j]); + splitCounts[j] = splitPos[j]-splitPos[j-1]; + PX_ASSERT(splitCounts[j] > 0 || clusterSize < RTREE_N); + sumCounts += splitCounts[j]; + PX_ASSERT(splitStarts[j-1]+splitCounts[j-1]<=splitStarts[j]); + } + PX_ASSERT(sumCounts == clusterSize); + PX_ASSERT(splitStarts[RTREE_N-1]+splitCounts[RTREE_N-1]<=clusterSize); + + // mark this cluster as terminal based on clusterSize <= stopAtTrisPerPage parameter for current iTradeOff user specified preset + bool terminalClusterByTotalCount = (clusterSize <= stopAtTrisPerPage[iTradeOff]); + // iterate over splitCounts for the current cluster, if any of counts exceed 16 (which is the maximum supported by LeafTriangles + // we cannot mark this cluster as terminal (has to be split more) + for(PxU32 s = 0; s < RTREE_N; s++) + if(splitCounts[s] > 16) // LeafTriangles doesn't support > 16 tris + terminalClusterByTotalCount = false; + + // iterate over all the splits + for(PxU32 s = 0; s < RTREE_N; s++) + { + RTreeNodeNQ rtn; + PxU32 splitCount = splitCounts[s]; + if(splitCount > 0) // splits shouldn't be empty generally + { + // sweep left to right and compute min and max SAH for each individual bound in current split + PxBounds3V b = allBounds[permute[splitStarts[s]]]; + PxF32 sahMin; PxSAH(b.getExtents(), sahMin); + PxF32 sahMax = sahMin; + // AP scaffold - looks like this could be optimized (we are recomputing bounds top down) + for(PxU32 i = 1; i < splitCount; i++) + { + PxU32 localIndex = i + splitStarts[s]; + const PxBounds3V& b1 = allBounds[permute[localIndex]]; + PxF32 sah1; PxSAH(b1.getExtents(), sah1); + sahMin = PxMin(sahMin, sah1); + sahMax = PxMax(sahMax, sah1); + b.include(b1); + } + + rtn.bounds.minimum = V3ReadXYZ(b.mn); + rtn.bounds.maximum = V3ReadXYZ(b.mx); + + // if bounds differ widely (according to some heuristic preset), we continue splitting + // this is important for a mixed cluster with large and small triangles + bool okSAH = (sahMax/sahMin < 40.0f); + if(!okSAH) + terminalClusterByTotalCount = false; // force splitting this cluster + + bool stopSplitting = // compute the final splitting criterion + splitCount <= 2 || (okSAH && splitCount <= 3) // stop splitting at 2 nodes or if SAH ratio is OK and splitCount <= 3 + || terminalClusterByTotalCount || splitCount <= stopAtTrisPerLeaf[iTradeOff]; + if(stopSplitting) + { + // this is a terminal page then, mark as such + // first node index is relative to the top level input array beginning + rtn.childPageFirstNodeIndex = PxI32(splitStarts[s]+(permute-permuteStart)); + rtn.leafCount = PxI32(splitCount); + PX_ASSERT(splitCount <= 16); // LeafTriangles doesn't support more + } + else + { + // this is not a terminal page, we will recompute this later, after we recurse on subpages (label ZZZ) + rtn.childPageFirstNodeIndex = -1; + rtn.leafCount = 0; + } + } + else // splitCount == 0 at this point, this is an empty paddding node (with current presets it's very rare) + { + PX_ASSERT(splitCount == 0); + rtn.bounds.setEmpty(); + rtn.childPageFirstNodeIndex = -1; + rtn.leafCount = -1; + } + resultTree.pushBack(rtn); // push the new node into the resultTree array + } + + if(terminalClusterByTotalCount) // abort recursion if terminal cluster + return; + + // recurse on subpages + PxU32 parentIndex = resultTree.size() - RTREE_N; // save the parentIndex as specified (array can be resized during recursion) + for(PxU32 s = 0; sleafCount == 0) // only split pages that were marked as non-terminal during splitting (see "label ZZZ" above) + { + // all child nodes will be pushed inside of this recursive call, + // so we set the child pointer for parent node to resultTree.size() + sParent->childPageFirstNodeIndex = PxI32(resultTree.size()); + sort4(permute+splitStarts[s], splitCounts[s], resultTree, maxLevels, level+1, sParent); + } + } + } +}; + + + + +///////////////////////////////////////////////////////////////////////// +// initializes the input permute array with identity permutation +// and shuffles it so that new sorted index, newIndex = resultPermute[oldIndex] +static void buildFromBounds( + Gu::RTree& result, const PxBounds3V* allBounds, PxU32 numBounds, + Array& permute, RTreeCooker::RemapCallback* rc, Vec3VArg allMn, Vec3VArg allMx, + PxReal sizePerfTradeOff01, PxMeshCookingHint::Enum hint) +{ + PX_UNUSED(sizePerfTradeOff01); + PxBounds3V treeBounds(allMn, allMx); + + // start off with an identity permutation + permute.resize(0); + permute.reserve(numBounds+1); + for(PxU32 j = 0; j < numBounds; j ++) + permute.pushBack(j); + const PxU32 sentinel = 0xABCDEF01; + permute.pushBack(sentinel); + + // load sorted nodes into an RTreeNodeNQ tree representation + // build the tree structure from sorted nodes + const PxU32 pageSize = RTREE_N; + Array resultTree; + resultTree.reserve(numBounds*2); + + PxU32 maxLevels = 0; + if(hint == PxMeshCookingHint::eSIM_PERFORMANCE) // use high quality SAH build + { + Array xRanks(numBounds), yRanks(numBounds), zRanks(numBounds), xOrder(numBounds), yOrder(numBounds), zOrder(numBounds); + PxMemCopy(xOrder.begin(), permute.begin(), sizeof(xOrder[0])*numBounds); + PxMemCopy(yOrder.begin(), permute.begin(), sizeof(yOrder[0])*numBounds); + PxMemCopy(zOrder.begin(), permute.begin(), sizeof(zOrder[0])*numBounds); + // sort by shuffling the permutation, precompute sorted ranks for x,y,z-orders + Ps::sort(xOrder.begin(), xOrder.size(), SortBoundsPredicate(0, allBounds)); + for(PxU32 i = 0; i < numBounds; i++) xRanks[xOrder[i]] = i; + Ps::sort(yOrder.begin(), yOrder.size(), SortBoundsPredicate(1, allBounds)); + for(PxU32 i = 0; i < numBounds; i++) yRanks[yOrder[i]] = i; + Ps::sort(zOrder.begin(), zOrder.size(), SortBoundsPredicate(2, allBounds)); + for(PxU32 i = 0; i < numBounds; i++) zRanks[zOrder[i]] = i; + + SubSortSAH ss(permute.begin(), allBounds, numBounds, + xOrder.begin(), yOrder.begin(), zOrder.begin(), xRanks.begin(), yRanks.begin(), zRanks.begin(), sizePerfTradeOff01); + ss.sort4(permute.begin(), numBounds, resultTree, maxLevels); + } else + { // use fast cooking path + PX_ASSERT(hint == PxMeshCookingHint::eCOOKING_PERFORMANCE); + SubSortQuick ss(permute.begin(), allBounds, numBounds, sizePerfTradeOff01); + PxBounds3V discard((PxBounds3V::U())); + ss.sort4(permute.begin(), permute.size()-1, resultTree, maxLevels, discard); // AP scaffold: need to implement build speed/runtime perf slider + } + + PX_ASSERT(permute[numBounds] == sentinel); // verify we didn't write past the array + permute.popBack(); // discard the sentinel value + + #if PRINT_RTREE_COOKING_STATS // stats code + PxU32 totalLeafTris = 0; + PxU32 numLeaves = 0; + PxI32 maxLeafTris = 0; + PxU32 numEmpty = 0; + for(PxU32 i = 0; i < resultTree.size(); i++) + { + PxI32 leafCount = resultTree[i].leafCount; + numEmpty += (resultTree[i].bounds.isEmpty()); + if(leafCount > 0) + { + numLeaves++; + totalLeafTris += leafCount; + if(leafCount > maxLeafTris) + maxLeafTris = leafCount; + } + } + + printf("AABBs total/empty=%d/%d\n", resultTree.size(), numEmpty); + printf("numTris=%d, numLeafAABBs=%d, avgTrisPerLeaf=%.2f, maxTrisPerLeaf = %d\n", + numBounds, numLeaves, PxF32(totalLeafTris)/numLeaves, maxLeafTris); + #endif + + PX_ASSERT(RTREE_N*sizeof(RTreeNodeQ) == sizeof(RTreePage)); // needed for nodePtrMultiplier computation to be correct + const int nodePtrMultiplier = sizeof(RTreeNodeQ); // convert offset as count in qnodes to page ptr + + // Quantize the tree. AP scaffold - might be possible to merge this phase with the page pass below this loop + Array qtreeNodes; + PxU32 firstEmptyIndex = PxU32(-1); + PxU32 resultCount = resultTree.size(); + qtreeNodes.reserve(resultCount); + + for(PxU32 i = 0; i < resultCount; i++) // AP scaffold - eliminate this pass + { + RTreeNodeNQ & u = resultTree[i]; + RTreeNodeQ q; + q.setLeaf(u.leafCount > 0); // set the leaf flag + if(u.childPageFirstNodeIndex == -1) // empty node? + { + if(firstEmptyIndex == PxU32(-1)) + firstEmptyIndex = qtreeNodes.size(); + q.minx = q.miny = q.minz = FLT_MAX; // AP scaffold improvement - use empty 1e30 bounds instead and reference a valid leaf + q.maxx = q.maxy = q.maxz = -FLT_MAX; // that will allow to remove the empty node test from the runtime + + q.ptr = firstEmptyIndex*nodePtrMultiplier; PX_ASSERT((q.ptr & 1) == 0); + q.setLeaf(true); // label empty node as leaf node + } else + { + // non-leaf node + q.minx = u.bounds.minimum.x; + q.miny = u.bounds.minimum.y; + q.minz = u.bounds.minimum.z; + q.maxx = u.bounds.maximum.x; + q.maxy = u.bounds.maximum.y; + q.maxz = u.bounds.maximum.z; + if(u.leafCount > 0) + { + q.ptr = PxU32(u.childPageFirstNodeIndex); + rc->remap(&q.ptr, q.ptr, PxU32(u.leafCount)); + PX_ASSERT(q.isLeaf()); // remap is expected to set the isLeaf bit + } + else + { + // verify that all children bounds are included in the parent bounds + for(PxU32 s = 0; s < RTREE_N; s++) + { + const RTreeNodeNQ& child = resultTree[u.childPageFirstNodeIndex+s]; + PX_UNUSED(child); + // is a sentinel node or is inside parent's bounds + PX_ASSERT(child.leafCount == -1 || child.bounds.isInside(u.bounds)); + } + + q.ptr = PxU32(u.childPageFirstNodeIndex * nodePtrMultiplier); + PX_ASSERT(q.ptr % RTREE_N == 0); + q.setLeaf(false); + } + } + qtreeNodes.pushBack(q); + } + + // build the final rtree image + result.mInvDiagonal = PxVec4(1.0f); + PX_ASSERT(qtreeNodes.size() % RTREE_N == 0); + result.mTotalNodes = qtreeNodes.size(); + result.mTotalPages = result.mTotalNodes / pageSize; + result.mPages = static_cast( + Ps::AlignedAllocator<128>().allocate(sizeof(RTreePage)*result.mTotalPages, __FILE__, __LINE__)); + result.mBoundsMin = PxVec4(V3ReadXYZ(treeBounds.mn), 0.0f); + result.mBoundsMax = PxVec4(V3ReadXYZ(treeBounds.mx), 0.0f); + result.mDiagonalScaler = (result.mBoundsMax - result.mBoundsMin) / 65535.0f; + result.mPageSize = pageSize; + result.mNumLevels = maxLevels; + PX_ASSERT(result.mTotalNodes % pageSize == 0); + result.mNumRootPages = 1; + + for(PxU32 j = 0; j < result.mTotalPages; j++) + { + RTreePage& page = result.mPages[j]; + for(PxU32 k = 0; k < RTREE_N; k ++) + { + const RTreeNodeQ& n = qtreeNodes[j*RTREE_N+k]; + page.maxx[k] = n.maxx; + page.maxy[k] = n.maxy; + page.maxz[k] = n.maxz; + page.minx[k] = n.minx; + page.miny[k] = n.miny; + page.minz[k] = n.minz; + page.ptrs[k] = n.ptr; + } + } + + //printf("Tree size=%d\n", result.mTotalPages*sizeof(RTreePage)); +#if PX_DEBUG + result.validate(); // make sure the child bounds are included in the parent and other validation +#endif +} + +} // namespace physx diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h new file mode 100644 index 000000000..6faf2f674 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h @@ -0,0 +1,51 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CmPhysXCommon.h" +#include "GuMeshData.h" +#include "PxCooking.h" +#include "PsArray.h" +#include "GuRTree.h" + +namespace physx +{ + struct RTreeCooker + { + struct RemapCallback // a callback to convert indices from triangle to LeafTriangles or other uses + { + virtual ~RemapCallback() {} + virtual void remap(PxU32* rtreePtr, PxU32 start, PxU32 leafCount) = 0; + }; + + // triangles will be remapped so that newIndex = resultPermute[oldIndex] + static void buildFromTriangles( + Gu::RTree& resultTree, const PxVec3* verts, PxU32 numVerts, const PxU16* tris16, const PxU32* tris32, PxU32 numTris, + Ps::Array& resultPermute, RemapCallback* rc, PxReal sizePerfTradeOff01, PxMeshCookingHint::Enum hint); + }; +} diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp new file mode 100644 index 000000000..67cacd35f --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp @@ -0,0 +1,1385 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "RTreeCooking.h" +#include "TriangleMeshBuilder.h" +#include "EdgeList.h" +#include "MeshCleaner.h" +#include "GuConvexEdgeFlags.h" +#include "PxTriangleMeshDesc.h" +#include "GuSerialize.h" +#include "Cooking.h" +#include "GuMeshData.h" +#include "GuTriangle32.h" +#include "GuRTree.h" +#include "GuInternal.h" +#include "GuBV4Build.h" +#include "GuBV32Build.h" +#include "PsFoundation.h" +#include "PsHashMap.h" +#include "PsSort.h" + +namespace physx { + +struct int3 +{ + int x, y, z; +}; + +struct uint3 +{ + unsigned int x, y, z; +}; + +PX_ALIGN_PREFIX(16) +struct uint4 +{ + unsigned int x, y, z, w; +} +PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct float4 +{ + float x, y, z, w; +} +PX_ALIGN_SUFFIX(16); + +} + +#include "GrbTriangleMeshCooking.h" + +using namespace physx; +using namespace Gu; +using namespace Ps; + +namespace physx { + +TriangleMeshBuilder::TriangleMeshBuilder(TriangleMeshData& m, const PxCookingParams& params) : + edgeList (NULL), + mParams (params), + mMeshData (m) +{ +} + +TriangleMeshBuilder::~TriangleMeshBuilder() +{ + releaseEdgeList(); +} + +void TriangleMeshBuilder::remapTopology(const PxU32* order) +{ + if(!mMeshData.mNbTriangles) + return; + + // Remap one array at a time to limit memory usage + + Gu::TriangleT* newTopo = reinterpret_cast*>(PX_ALLOC(mMeshData.mNbTriangles * sizeof(Gu::TriangleT), "Gu::TriangleT")); + for(PxU32 i=0;i*>(mMeshData.mTriangles)[order[i]]; + PX_FREE_AND_RESET(mMeshData.mTriangles); + mMeshData.mTriangles = newTopo; + + if(mMeshData.mMaterialIndices) + { + PxMaterialTableIndex* newMat = PX_NEW(PxMaterialTableIndex)[mMeshData.mNbTriangles]; + for(PxU32 i=0;i(mMeshData.mTriangles), meshWeldTolerance); + if(!cleaner.mNbTris) + return false; + + if(validate) + { + // if we do only validate, we check if cleaning did not remove any verts or triangles. + // such a mesh can be then directly used for cooking without clean flag + if((cleaner.mNbVerts != mMeshData.mNbVertices) || (cleaner.mNbTris != mMeshData.mNbTriangles)) + { + return false; + } + } + + // PT: deal with the remap table + { + // PT: TODO: optimize this + if(cleaner.mRemap) + { + const PxU32 newNbTris = cleaner.mNbTris; + + // Remap material array + if(mMeshData.mMaterialIndices) + { + PxMaterialTableIndex* tmp = PX_NEW(PxMaterialTableIndex)[newNbTris]; + for(PxU32 i=0;i*>(mMeshData.mTriangles)[i].v[0] = vref0; + reinterpret_cast*>(mMeshData.mTriangles)[i].v[1] = vref1; + reinterpret_cast*>(mMeshData.mTriangles)[i].v[2] = vref2; + + if( (v[vref0] - v[vref1]).magnitudeSquared() >= testLength + || (v[vref1] - v[vref2]).magnitudeSquared() >= testLength + || (v[vref2] - v[vref0]).magnitudeSquared() >= testLength + ) + bigTriangle = true; + } + if(bigTriangle) + { + if(condition) + *condition = PxTriangleMeshCookingResult::eLARGE_TRIANGLE; + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "TriangleMesh: triangles are too big, reduce their size to increase simulation stability!"); + } + } + + return true; +} + +void TriangleMeshBuilder::createSharedEdgeData(bool buildAdjacencies, bool buildActiveEdges) +{ + if(buildAdjacencies) // building edges is required if buildAdjacencies is requested + buildActiveEdges = true; + + PX_ASSERT(mMeshData.mExtraTrigData == NULL); + PX_ASSERT(mMeshData.mAdjacencies == NULL); + + if(!buildActiveEdges) + return; + + const PxU32 nTrigs = mMeshData.mNbTriangles; + + mMeshData.mExtraTrigData = PX_NEW(PxU8)[nTrigs]; + memset(mMeshData.mExtraTrigData, 0, sizeof(PxU8)*nTrigs); + + const Gu::TriangleT* trigs = reinterpret_cast*>(mMeshData.mTriangles); + if(0x40000000 <= nTrigs) + { + //mesh is too big for this algo, need to be able to express trig indices in 30 bits, and still have an index reserved for "unused": + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "TriangleMesh: mesh is too big for this algo!"); + return; + } + + createEdgeList(); + if(edgeList) + { + PX_ASSERT(edgeList->getNbFaces()==mMeshData.mNbTriangles); + if(edgeList->getNbFaces()==mMeshData.mNbTriangles) + { + for(PxU32 i=0;igetNbFaces();i++) + { + const Gu::EdgeTriangleData& ET = edgeList->getEdgeTriangle(i); + // Replicate flags + if(Gu::EdgeTriangleAC::HasActiveEdge01(ET)) mMeshData.mExtraTrigData[i] |= Gu::ETD_CONVEX_EDGE_01; + if(Gu::EdgeTriangleAC::HasActiveEdge12(ET)) mMeshData.mExtraTrigData[i] |= Gu::ETD_CONVEX_EDGE_12; + if(Gu::EdgeTriangleAC::HasActiveEdge20(ET)) mMeshData.mExtraTrigData[i] |= Gu::ETD_CONVEX_EDGE_20; + } + } + } + + // fill the adjacencies + if(buildAdjacencies) + { + mMeshData.mAdjacencies = PX_NEW(PxU32)[nTrigs*3]; + memset(mMeshData.mAdjacencies, 0xFFFFffff, sizeof(PxU32)*nTrigs*3); + + PxU32 NbEdges = edgeList->getNbEdges(); + const Gu::EdgeDescData* ED = edgeList->getEdgeToTriangles(); + const Gu::EdgeData* Edges = edgeList->getEdges(); + const PxU32* FBE = edgeList->getFacesByEdges(); + + while(NbEdges--) + { + // Get number of triangles sharing current edge + PxU32 Count = ED->Count; + + if(Count > 1) + { + PxU32 FaceIndex0 = FBE[ED->Offset+0]; + PxU32 FaceIndex1 = FBE[ED->Offset+1]; + + const Gu::EdgeData& edgeData = *Edges; + const Gu::TriangleT& T0 = trigs[FaceIndex0]; + const Gu::TriangleT& T1 = trigs[FaceIndex1]; + + PxU32 offset0 = T0.findEdgeCCW(edgeData.Ref0,edgeData.Ref1); + PxU32 offset1 = T1.findEdgeCCW(edgeData.Ref0,edgeData.Ref1); + + mMeshData.setTriangleAdjacency(FaceIndex0, FaceIndex1, offset0); + mMeshData.setTriangleAdjacency(FaceIndex1, FaceIndex0, offset1); + } + ED++; + Edges++; + } + } + +#if PX_DEBUG + for(PxU32 i=0;i& T = trigs[i]; + PX_UNUSED(T); + const Gu::EdgeTriangleData& ET = edgeList->getEdgeTriangle(i); + PX_ASSERT((Gu::EdgeTriangleAC::HasActiveEdge01(ET) && (mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_01)) || (!Gu::EdgeTriangleAC::HasActiveEdge01(ET) && !(mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_01))); + PX_ASSERT((Gu::EdgeTriangleAC::HasActiveEdge12(ET) && (mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_12)) || (!Gu::EdgeTriangleAC::HasActiveEdge12(ET) && !(mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_12))); + PX_ASSERT((Gu::EdgeTriangleAC::HasActiveEdge20(ET) && (mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_20)) || (!Gu::EdgeTriangleAC::HasActiveEdge20(ET) && !(mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_20))); + } +#endif + return; +} + +namespace GrbTrimeshCookerHelper +{ + +struct SortedNeighbor +{ + PxU32 v, a; // vertex and adjacent vertex + bool boundary; + + SortedNeighbor(PxU32 v_, PxU32 a_, bool b_): v(v_), a(a_), boundary(b_) {} + + // sort boundary edges to the front so that they are kept when duplicates are removed + bool operator<(const SortedNeighbor& b) const + { + return (v mLocalToMesh; + Ps::HashMap mMeshToLocal; +}; + +#include + + +void findSharpVertices( + Ps::Array& pairList, + Ps::Array& edgeRanges, + /*const Ps::Array& triangles,*/ + const uint3* triIndices, + const uint4* triAdjacencies, + PxU32 nbTris, + PxU32 nbVerts + ) +{ + // sort the edges which are sharp or boundary + for(PxU32 i=0;i=3) + edgeRanges[pairList[p].v] = SharpEdgeRange(p, u-p); + } +} + +#if 0 +PxU32 buildVertexConnectionNew_p1( + Ps::Array & pairList, + Ps::Array & edgeRanges, + LocalIndexer & vertexMap, + + const uint4 * triIndices, + const uint4 * triAdjacencies, + + PxU32 nbTris, + PxU32 nbVerts + ) +{ + findSharpVertices( + pairList, + edgeRanges, + triIndices, + triAdjacencies, + nbTris, + nbVerts + ); + + // add all the original triangles and vertices and record how big the core is + for(PxU32 i=0; i& pairList, + Ps::Array& edgeRanges, + LocalIndexer & vertexMap, + + const uint4 * /*triIndices*/, + const uint4 * /*triAdjacencies*/, + + PxU32 /*nbTris*/, + PxU32 nbVerts, + PxU32 /*nbNeighbors*/ + ) +{ + PxU32 n = 0; + for(PxU32 i=0;i & pairList, + Ps::Array & edgeRanges, + + const uint3* triIndices, + const uint4 * triAdjacencies, + + PxU32 nbTris, + PxU32 nbVerts + ) +{ + findSharpVertices( + pairList, + edgeRanges, + triIndices, + triAdjacencies, + nbTris, + nbVerts + ); + + + // add the neighbors of the sharp vertices + PxU32 nbNeighbors = 0; + for (PxU32 i = 0; i& pairList, + Ps::Array& edgeRanges, + PxU32 nbVerts + ) +{ + PxU32 n = 0; + for (PxU32 i = 0; i(PX_ALLOC(numTris * sizeof(PxVec3), PX_DEBUG_EXP("tempNormalsPerTri_prealloc"))); + + mMeshData.mGRB_primAdjacencies = PX_ALLOC(sizeof(uint4)*numTris, PX_DEBUG_EXP("GRB_triAdjacencies")); + + + buildAdjacencies( + reinterpret_cast(mMeshData.mGRB_primAdjacencies), + tempNormalsPerTri_prealloc, + mMeshData.mVertices, + reinterpret_cast(mMeshData.mGRB_primIndices), + numTris + ); + + + PX_FREE(tempNormalsPerTri_prealloc); + +} + +void TriangleMeshBuilder::createGRBMidPhaseAndData(const PxU32 originalTriangleCount) +{ + if (mParams.buildGPUData) + { + + PX_ASSERT(!(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES)); + + BV32Tree* bv32Tree = PX_NEW(BV32Tree); + mMeshData.mGRB_BV32Tree = bv32Tree; + + BV32TriangleMeshBuilder::createMidPhaseStructure(mParams, mMeshData, *bv32Tree); + + createGRBData(); + + //create a remap table from GPU to CPU remap table + PxU32* orignalToRemap = PX_NEW(PxU32)[originalTriangleCount]; + + PX_ASSERT(mMeshData.mFaceRemap); + + + for (PxU32 i = 0; i < mMeshData.mNbTriangles; ++i) + { + const PxU32 index = mMeshData.mFaceRemap[i]; + PX_ASSERT(index < originalTriangleCount); + orignalToRemap[index] = i; + } + + + //map CPU remap triangle index to GPU remap triangle index + for (PxU32 i = 0; i < mMeshData.mNbTriangles; ++i) + { + const PxU32 index = mMeshData.mGRB_faceRemap[i]; + mMeshData.mGRB_faceRemap[i] = orignalToRemap[index]; + } + +#if BV32_VALIDATE + IndTri32* grbTriIndices = reinterpret_cast(mMeshData.mGRB_triIndices); + IndTri32* cpuTriIndices = reinterpret_cast(mMeshData.mTriangles); + //map CPU remap triangle index to GPU remap triangle index + for (PxU32 i = 0; i < mMeshData.mNbTriangles; ++i) + { + PX_ASSERT(grbTriIndices[i].mRef[0] == cpuTriIndices[mMeshData.mGRB_faceRemap[i]].mRef[0]); + PX_ASSERT(grbTriIndices[i].mRef[1] == cpuTriIndices[mMeshData.mGRB_faceRemap[i]].mRef[1]); + PX_ASSERT(grbTriIndices[i].mRef[2] == cpuTriIndices[mMeshData.mGRB_faceRemap[i]].mRef[2]); + } +#endif + + if (orignalToRemap) + PX_DELETE_POD(orignalToRemap); + + } +} + +void TriangleMeshBuilder::createEdgeList() +{ + Gu::EDGELISTCREATE create; + create.NbFaces = mMeshData.mNbTriangles; + if(mMeshData.has16BitIndices()) + { + create.DFaces = NULL; + create.WFaces = reinterpret_cast(mMeshData.mTriangles); + } + else + { + create.DFaces = reinterpret_cast(mMeshData.mTriangles); + create.WFaces = NULL; + } + create.FacesToEdges = true; + create.EdgesToFaces = true; + create.Verts = mMeshData.mVertices; + //create.Epsilon = 0.1f; + // create.Epsilon = convexEdgeThreshold; + edgeList = PX_NEW(Gu::EdgeListBuilder); + if(!edgeList->init(create)) + { + PX_DELETE(edgeList); + edgeList = 0; + } +} + +void TriangleMeshBuilder::releaseEdgeList() +{ + PX_DELETE_AND_RESET(edgeList); +} + +// +// When suppressTriangleMeshRemapTable is true, the face remap table is not created. This saves a significant amount of memory, +// but the SDK will not be able to provide information about which mesh triangle is hit in collisions, sweeps or raycasts hits. +// +// The sequence is as follows: + +bool TriangleMeshBuilder::loadFromDesc(const PxTriangleMeshDesc& _desc, PxTriangleMeshCookingResult::Enum* condition, bool validateMesh) +{ + const PxU32 originalTriangleCount = _desc.triangles.count; + if(!_desc.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "TriangleMesh::loadFromDesc: desc.isValid() failed!"); + return false; + } + + // verify the mesh params + if(!mParams.midphaseDesc.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "TriangleMesh::loadFromDesc: mParams.midphaseDesc.isValid() failed!"); + return false; + } + + // Create a local copy that we can modify + PxTriangleMeshDesc desc = _desc; + + // Save simple params + { + // Handle implicit topology + PxU32* topology = NULL; + if(!desc.triangles.data) + { + // We'll create 32-bit indices + desc.flags &= ~PxMeshFlag::e16_BIT_INDICES; + desc.triangles.stride = sizeof(PxU32)*3; + + { + // Non-indexed mesh => create implicit topology + desc.triangles.count = desc.points.count/3; + // Create default implicit topology + topology = PX_NEW_TEMP(PxU32)[desc.points.count]; + for(PxU32 i=0;i* tris = reinterpret_cast*>(mMeshData.mTriangles); + for(PxU32 i=0;imaxIndex) maxIndex = tris[i].v[0]; + if(tris[i].v[1]>maxIndex) maxIndex = tris[i].v[1]; + if(tris[i].v[2]>maxIndex) maxIndex = tris[i].v[2]; + } + + bool force32 = (params.meshPreprocessParams & PxMeshPreprocessingFlag::eFORCE_32BIT_INDICES); + if (maxIndex <= 0xFFFF && !force32) + serialFlags |= (maxIndex <= 0xFF ? Gu::IMSF_8BIT_INDICES : Gu::IMSF_16BIT_INDICES); + writeDword(serialFlags, platformMismatch, stream); + + // Export mesh + writeDword(mMeshData.mNbVertices, platformMismatch, stream); + writeDword(mMeshData.mNbTriangles, platformMismatch, stream); + writeFloatBuffer(&mMeshData.mVertices->x, mMeshData.mNbVertices*3, platformMismatch, stream); + if(serialFlags & Gu::IMSF_8BIT_INDICES) + { + const PxU32* indices = tris->v; + for(PxU32 i=0;iv; + for(PxU32 i=0;iv, mMeshData.mNbTriangles*3, platformMismatch, stream); + + if(mMeshData.mMaterialIndices) + writeWordBuffer(mMeshData.mMaterialIndices, mMeshData.mNbTriangles, platformMismatch, stream); + + if(mMeshData.mFaceRemap) + { + PxU32 maxId = computeMaxIndex(mMeshData.mFaceRemap, mMeshData.mNbTriangles); + writeDword(maxId, platformMismatch, stream); + storeIndices(maxId, mMeshData.mNbTriangles, mMeshData.mFaceRemap, stream, platformMismatch); +// writeIntBuffer(mMeshData.mFaceRemap, mMeshData.mNbTriangles, platformMismatch, stream); + } + + if(mMeshData.mAdjacencies) + writeIntBuffer(mMeshData.mAdjacencies, mMeshData.mNbTriangles*3, platformMismatch, stream); + + // Export midphase structure + saveMidPhaseStructure(stream, platformMismatch); + + // Export local bounds + writeFloat(mMeshData.mGeomEpsilon, platformMismatch, stream); + + writeFloat(mMeshData.mAABB.minimum.x, platformMismatch, stream); + writeFloat(mMeshData.mAABB.minimum.y, platformMismatch, stream); + writeFloat(mMeshData.mAABB.minimum.z, platformMismatch, stream); + writeFloat(mMeshData.mAABB.maximum.x, platformMismatch, stream); + writeFloat(mMeshData.mAABB.maximum.y, platformMismatch, stream); + writeFloat(mMeshData.mAABB.maximum.z, platformMismatch, stream); + + if(mMeshData.mExtraTrigData) + { + writeDword(mMeshData.mNbTriangles, platformMismatch, stream); + // No need to convert those bytes + stream.write(mMeshData.mExtraTrigData, mMeshData.mNbTriangles*sizeof(PxU8)); + } + else + writeDword(0, platformMismatch, stream); + + // GRB write ----------------------------------------------------------------- + if (params.buildGPUData) + { + const PxU32* indices = reinterpret_cast(mMeshData.mGRB_primIndices); + if (serialFlags & Gu::IMSF_8BIT_INDICES) + { + for (PxU32 i = 0; i(mMeshData.mGRB_triIndices), , mMeshData.mNbTriangles*3, platformMismatch, stream); + + //writeIntBuffer(reinterpret_cast(mMeshData.mGRB_triIndices), mMeshData.mNbTriangles*4, platformMismatch, stream); + + writeIntBuffer(reinterpret_cast(mMeshData.mGRB_primAdjacencies), mMeshData.mNbTriangles*4, platformMismatch, stream); + writeIntBuffer(mMeshData.mGRB_faceRemap, mMeshData.mNbTriangles, platformMismatch, stream); + + //Export GPU midphase structure + BV32Tree* bv32Tree = reinterpret_cast(mMeshData.mGRB_BV32Tree); + BV32TriangleMeshBuilder::saveMidPhaseStructure(bv32Tree, stream, platformMismatch); + } + + // End of GRB write ---------------------------------------------------------- + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4996) // permitting use of gatherStrided until we have a replacement. +#endif + +bool TriangleMeshBuilder::importMesh(const PxTriangleMeshDesc& desc,const PxCookingParams& params,PxTriangleMeshCookingResult::Enum* condition, bool validate) +{ + //convert and clean the input mesh + //this is where the mesh data gets copied from user mem to our mem + + PxVec3* verts = mMeshData.allocateVertices(desc.points.count); + Gu::TriangleT* tris = reinterpret_cast*>(mMeshData.allocateTriangles(desc.triangles.count, true, PxU32(params.buildGPUData))); + + //copy, and compact to get rid of strides: + Cooking::gatherStrided(desc.points.data, verts, mMeshData.mNbVertices, sizeof(PxVec3), desc.points.stride); + +#if PX_CHECKED + // PT: check all input vertices are valid + for(PxU32 i=0;i* dest = tris; + const Gu::TriangleT* pastLastDest = tris + mMeshData.mNbTriangles; + const PxU8* source = reinterpret_cast(desc.triangles.data); + + //4 combos of 16 vs 32 and flip vs no flip + PxU32 c = (desc.flags & PxMeshFlag::eFLIPNORMALS)?PxU32(1):0; + if (desc.flags & PxMeshFlag::e16_BIT_INDICES) + { + //index stride conversion is also needed, I don't think flexicopy can do that for us. + while (dest < pastLastDest) + { + const PxU16 * trig16 = reinterpret_cast(source); + dest->v[0] = trig16[0]; + dest->v[1] = trig16[1+c]; + dest->v[2] = trig16[2-c]; + dest ++; + source += desc.triangles.stride; + } + } + else + { + while (dest < pastLastDest) + { + const PxU32 * trig32 = reinterpret_cast(source); + dest->v[0] = trig32[0]; + dest->v[1] = trig32[1+c]; + dest->v[2] = trig32[2-c]; + dest ++; + source += desc.triangles.stride; + } + } + + //copy the material index list if any: + if(desc.materialIndices.data) + { + PxMaterialTableIndex* materials = mMeshData.allocateMaterials(); + Cooking::gatherStrided(desc.materialIndices.data, materials, mMeshData.mNbTriangles, sizeof(PxMaterialTableIndex), desc.materialIndices.stride); + + // Check material indices + for(PxU32 i=0;i it works with a clean mesh + + if (!(params.meshPreprocessParams & PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH) || validate) + { + if(!cleanMesh(validate, condition)) + { + if(!validate) + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "cleaning the mesh failed"); + return false; + } + } + else + { + // we need to fill the remap table if no cleaning was done + if(params.suppressTriangleMeshRemapTable == false) + { + PX_ASSERT(mMeshData.mFaceRemap == NULL); + mMeshData.mFaceRemap = PX_NEW(PxU32)[mMeshData.mNbTriangles]; + for (PxU32 i = 0; i < mMeshData.mNbTriangles; i++) + mMeshData.mFaceRemap[i] = i; + } + } + return true; +} + +#if PX_VC +#pragma warning(pop) +#endif +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void TriangleMeshBuilder::checkMeshIndicesSize() +{ + Gu::TriangleMeshData& m = mMeshData; + + // check if we can change indices from 32bits to 16bits + if(m.mNbVertices <= 0xffff && !m.has16BitIndices()) + { + const PxU32 numTriangles = m.mNbTriangles; + PxU32* PX_RESTRICT indices32 = reinterpret_cast (m.mTriangles); + PxU32* PX_RESTRICT grbIndices32 = reinterpret_cast(m.mGRB_primIndices); + + m.mTriangles = 0; // force a realloc + m.allocateTriangles(numTriangles, false, grbIndices32 != NULL ? 1u : 0u); + PX_ASSERT(m.has16BitIndices()); // realloc'ing without the force32bit flag changed it. + + PxU16* PX_RESTRICT indices16 = reinterpret_cast (m.mTriangles); + for (PxU32 i = 0; i < numTriangles * 3; i++) + indices16[i] = Ps::to16(indices32[i]); + + PX_FREE(indices32); + + if (grbIndices32) + { + PxU16* PX_RESTRICT grbIndices16 = reinterpret_cast (m.mGRB_primIndices); + for (PxU32 i = 0; i < numTriangles * 3; i++) + grbIndices16[i] = Ps::to16(grbIndices32[i]); + } + + PX_FREE(grbIndices32); + + onMeshIndexFormatChange(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +BV4TriangleMeshBuilder::BV4TriangleMeshBuilder(const PxCookingParams& params) : TriangleMeshBuilder(mData, params) +{ +} + +BV4TriangleMeshBuilder::~BV4TriangleMeshBuilder() +{ +} + +void BV4TriangleMeshBuilder::onMeshIndexFormatChange() +{ + IndTri32* triangles32 = NULL; + IndTri16* triangles16 = NULL; + if(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) + triangles16 = reinterpret_cast(mMeshData.mTriangles); + else + triangles32 = reinterpret_cast(mMeshData.mTriangles); + + mData.mMeshInterface.setPointers(triangles32, triangles16, mMeshData.mVertices); +} + +void BV4TriangleMeshBuilder::createMidPhaseStructure() +{ + const float gBoxEpsilon = 2e-4f; +// const float gBoxEpsilon = 0.1f; + mData.mMeshInterface.initRemap(); + mData.mMeshInterface.setNbVertices(mMeshData.mNbVertices); + mData.mMeshInterface.setNbTriangles(mMeshData.mNbTriangles); + + IndTri32* triangles32 = NULL; + IndTri16* triangles16 = NULL; + if (mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) + { + triangles16 = reinterpret_cast(mMeshData.mTriangles); + } + else + { + triangles32 = reinterpret_cast(mMeshData.mTriangles); + } + + mData.mMeshInterface.setPointers(triangles32, triangles16, mMeshData.mVertices); + + const PxU32 nbTrisPerLeaf = (mParams.midphaseDesc.getType() == PxMeshMidPhase::eBVH34) ? mParams.midphaseDesc.mBVH34Desc.numPrimsPerLeaf : 4; + + if(!BuildBV4Ex(mData.mBV4Tree, mData.mMeshInterface, gBoxEpsilon, nbTrisPerLeaf)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV4 tree failed to build."); + return; + } + +// remapTopology(mData.mMeshInterface); + + const PxU32* order = mData.mMeshInterface.getRemap(); + if(mMeshData.mMaterialIndices) + { + PxMaterialTableIndex* newMat = PX_NEW(PxMaterialTableIndex)[mMeshData.mNbTriangles]; + for(PxU32 i=0;i1 we now do the same as for other structures in the SDK: the data is + // exported either as little or big-endian depending on the passed parameter. + const PxU32 bv4StructureVersion = 3; + + writeChunk('B', 'V', '4', ' ', stream); + writeDword(bv4StructureVersion, mismatch, stream); + + writeFloat(mData.mBV4Tree.mLocalBounds.mCenter.x, mismatch, stream); + writeFloat(mData.mBV4Tree.mLocalBounds.mCenter.y, mismatch, stream); + writeFloat(mData.mBV4Tree.mLocalBounds.mCenter.z, mismatch, stream); + writeFloat(mData.mBV4Tree.mLocalBounds.mExtentsMagnitude, mismatch, stream); + + writeDword(mData.mBV4Tree.mInitData, mismatch, stream); + + writeFloat(mData.mBV4Tree.mCenterOrMinCoeff.x, mismatch, stream); + writeFloat(mData.mBV4Tree.mCenterOrMinCoeff.y, mismatch, stream); + writeFloat(mData.mBV4Tree.mCenterOrMinCoeff.z, mismatch, stream); + writeFloat(mData.mBV4Tree.mExtentsOrMaxCoeff.x, mismatch, stream); + writeFloat(mData.mBV4Tree.mExtentsOrMaxCoeff.y, mismatch, stream); + writeFloat(mData.mBV4Tree.mExtentsOrMaxCoeff.z, mismatch, stream); + + // PT: version 3 + writeDword(PxU32(mData.mBV4Tree.mQuantized), mismatch, stream); + + writeDword(mData.mBV4Tree.mNbNodes, mismatch, stream); + +#ifdef GU_BV4_USE_SLABS + // PT: we use BVDataPacked to get the size computation right, but we're dealing with BVDataSwizzled here! + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + const PxU32 NodeSize = mData.mBV4Tree.mQuantized ? sizeof(BVDataPackedQ) : sizeof(BVDataPackedNQ); + #else + const PxU32 NodeSize = sizeof(BVDataPackedQ); + #endif + stream.write(mData.mBV4Tree.mNodes, NodeSize*mData.mBV4Tree.mNbNodes); + PX_ASSERT(!mismatch); +#else + #error Not implemented +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BV32TriangleMeshBuilder::createMidPhaseStructure(const PxCookingParams& params, Gu::TriangleMeshData& meshData, Gu::BV32Tree& bv32Tree) +{ + const float gBoxEpsilon = 2e-4f; + + Gu::SourceMesh meshInterface; + // const float gBoxEpsilon = 0.1f; + meshInterface.initRemap(); + meshInterface.setNbVertices(meshData.mNbVertices); + meshInterface.setNbTriangles(meshData.mNbTriangles); + + PX_ASSERT(!(meshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES)); + + IndTri32* triangles32 = reinterpret_cast(meshData.mGRB_primIndices); + + meshInterface.setPointers(triangles32, NULL, meshData.mVertices); + + PxU32 nbTrisPerLeaf = 32; + + if (!BuildBV32Ex(bv32Tree, meshInterface, gBoxEpsilon, nbTrisPerLeaf)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV32 tree failed to build."); + return; + } + + const PxU32* order = meshInterface.getRemap(); + + if (!params.suppressTriangleMeshRemapTable || params.buildGPUData) + { + PxU32* newMap = PX_NEW(PxU32)[meshData.mNbTriangles]; + for (PxU32 i = 0; i1 we now do the same as for other structures in the SDK: the data is + // exported either as little or big-endian depending on the passed parameter. + const PxU32 bv32StructureVersion = 2; + + writeChunk('B', 'V', '3', '2', stream); + writeDword(bv32StructureVersion, mismatch, stream); + + writeFloat(bv32Tree->mLocalBounds.mCenter.x, mismatch, stream); + writeFloat(bv32Tree->mLocalBounds.mCenter.y, mismatch, stream); + writeFloat(bv32Tree->mLocalBounds.mCenter.z, mismatch, stream); + writeFloat(bv32Tree->mLocalBounds.mExtentsMagnitude, mismatch, stream); + + writeDword(bv32Tree->mInitData, mismatch, stream); + + writeDword(bv32Tree->mNbPackedNodes, mismatch, stream); + + PX_ASSERT(bv32Tree->mNbPackedNodes > 0); + for (PxU32 i = 0; i < bv32Tree->mNbPackedNodes; ++i) + { + BV32DataPacked& node = bv32Tree->mPackedNodes[i]; + + const PxU32 nbElements = node.mNbNodes * 4; + writeDword(node.mNbNodes, mismatch, stream); + WriteDwordBuffer(node.mData, node.mNbNodes, mismatch, stream); + writeFloatBuffer(&node.mCenter[0].x, nbElements, mismatch, stream); + writeFloatBuffer(&node.mExtents[0].x, nbElements, mismatch, stream); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +RTreeTriangleMeshBuilder::RTreeTriangleMeshBuilder(const PxCookingParams& params) : TriangleMeshBuilder(mData, params) +{ +} + +RTreeTriangleMeshBuilder::~RTreeTriangleMeshBuilder() +{ +} + +struct RTreeCookerRemap : RTreeCooker::RemapCallback +{ + PxU32 mNbTris; + RTreeCookerRemap(PxU32 numTris) : mNbTris(numTris) + { + } + + virtual void remap(PxU32* val, PxU32 start, PxU32 leafCount) + { + PX_ASSERT(leafCount > 0); + PX_ASSERT(leafCount <= 16); // sanity check + PX_ASSERT(start < mNbTris); + PX_ASSERT(start+leafCount <= mNbTris); + PX_ASSERT(val); + LeafTriangles lt; + // here we remap from ordered leaf index in the rtree to index in post-remap in triangles + // this post-remap will happen later + lt.SetData(leafCount, start); + *val = lt.Data; + } +}; + +void RTreeTriangleMeshBuilder::createMidPhaseStructure() +{ + const PxReal meshSizePerformanceTradeOff = mParams.midphaseDesc.mBVH33Desc.meshSizePerformanceTradeOff; + const PxMeshCookingHint::Enum meshCookingHint = mParams.midphaseDesc.mBVH33Desc.meshCookingHint; + + Array resultPermute; + RTreeCookerRemap rc(mMeshData.mNbTriangles); + RTreeCooker::buildFromTriangles( + mData.mRTree, + mMeshData.mVertices, mMeshData.mNbVertices, + (mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? reinterpret_cast(mMeshData.mTriangles) : NULL, + !(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? reinterpret_cast(mMeshData.mTriangles) : NULL, + mMeshData.mNbTriangles, resultPermute, &rc, meshSizePerformanceTradeOff, meshCookingHint); + + PX_ASSERT(resultPermute.size() == mMeshData.mNbTriangles); + + remapTopology(resultPermute.begin()); +} + +void RTreeTriangleMeshBuilder::saveMidPhaseStructure(PxOutputStream& stream, bool mismatch) const +{ + // PT: in version 1 we defined "mismatch" as: + // const bool mismatch = (littleEndian() == 1); + // i.e. the data was *always* saved to file in big-endian format no matter what. + // In version>1 we now do the same as for other structures in the SDK: the data is + // exported either as little or big-endian depending on the passed parameter. + const PxU32 rtreeStructureVersion = 2; + + // save the RTree root structure followed immediately by RTreePage pages to an output stream + writeChunk('R', 'T', 'R', 'E', stream); + + writeDword(rtreeStructureVersion, mismatch, stream); + const RTree& d = mData.mRTree; + writeFloatBuffer(&d.mBoundsMin.x, 4, mismatch, stream); + writeFloatBuffer(&d.mBoundsMax.x, 4, mismatch, stream); + writeFloatBuffer(&d.mInvDiagonal.x, 4, mismatch, stream); + writeFloatBuffer(&d.mDiagonalScaler.x, 4, mismatch, stream); + writeDword(d.mPageSize, mismatch, stream); + writeDword(d.mNumRootPages, mismatch, stream); + writeDword(d.mNumLevels, mismatch, stream); + writeDword(d.mTotalNodes, mismatch, stream); + writeDword(d.mTotalPages, mismatch, stream); + PxU32 unused = 0; writeDword(unused, mismatch, stream); // backwards compatibility + for (PxU32 j = 0; j < d.mTotalPages; j++) + { + writeFloatBuffer(d.mPages[j].minx, RTREE_N, mismatch, stream); + writeFloatBuffer(d.mPages[j].miny, RTREE_N, mismatch, stream); + writeFloatBuffer(d.mPages[j].minz, RTREE_N, mismatch, stream); + writeFloatBuffer(d.mPages[j].maxx, RTREE_N, mismatch, stream); + writeFloatBuffer(d.mPages[j].maxy, RTREE_N, mismatch, stream); + writeFloatBuffer(d.mPages[j].maxz, RTREE_N, mismatch, stream); + WriteDwordBuffer(d.mPages[j].ptrs, RTREE_N, mismatch, stream); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +} diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h new file mode 100644 index 000000000..4b187ec9e --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h @@ -0,0 +1,123 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_TriangleMeshBUILDER +#define PX_COLLISION_TriangleMeshBUILDER + +#include "GuMeshData.h" +#include "cooking/PxCooking.h" +#include "MeshBuilder.h" + +namespace physx +{ + namespace Gu + { + class EdgeListBuilder; + } + + class TriangleMeshBuilder : public MeshBulider + { + public: + TriangleMeshBuilder(Gu::TriangleMeshData& mesh, const PxCookingParams& params); + virtual ~TriangleMeshBuilder(); + + virtual PxMeshMidPhase::Enum getMidphaseID() const = 0; + // Called by base code when midphase structure should be built + virtual void createMidPhaseStructure() = 0; + + // Called by base code when midphase structure should be saved + virtual void saveMidPhaseStructure(PxOutputStream& stream, bool mismatch) const = 0; + // Called by base code when mesh index format has changed and the change should be reflected in midphase structure + virtual void onMeshIndexFormatChange() {} + + bool cleanMesh(bool validate, PxTriangleMeshCookingResult::Enum* condition); + void remapTopology(const PxU32* order); + + void createSharedEdgeData(bool buildAdjacencies, bool buildActiveEdges); + + void recordTriangleIndices(); + void createGRBMidPhaseAndData(const PxU32 originalTriangleCount); + void createGRBData(); + + bool loadFromDesc(const PxTriangleMeshDesc&, PxTriangleMeshCookingResult::Enum* condition, bool validate = false); + bool save(PxOutputStream& stream, bool platformMismatch, const PxCookingParams& params) const; + void checkMeshIndicesSize(); + PX_FORCE_INLINE Gu::TriangleMeshData& getMeshData() { return mMeshData; } + protected: + //void computeLocalBounds(); + bool importMesh(const PxTriangleMeshDesc& desc, const PxCookingParams& params, PxTriangleMeshCookingResult::Enum* condition, bool validate = false); + + TriangleMeshBuilder& operator=(const TriangleMeshBuilder&); + Gu::EdgeListBuilder* edgeList; + const PxCookingParams& mParams; + Gu::TriangleMeshData& mMeshData; + + void releaseEdgeList(); + void createEdgeList(); + }; + + class RTreeTriangleMeshBuilder : public TriangleMeshBuilder + { + public: + RTreeTriangleMeshBuilder(const PxCookingParams& params); + virtual ~RTreeTriangleMeshBuilder(); + + virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH33; } + virtual void createMidPhaseStructure(); + virtual void saveMidPhaseStructure(PxOutputStream& stream, bool mismatch) const; + + Gu::RTreeTriangleData mData; + }; + + class BV4TriangleMeshBuilder : public TriangleMeshBuilder + { + public: + BV4TriangleMeshBuilder(const PxCookingParams& params); + virtual ~BV4TriangleMeshBuilder(); + + virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH34; } + virtual void createMidPhaseStructure(); + virtual void saveMidPhaseStructure(PxOutputStream& stream, bool mismatch) const; + virtual void onMeshIndexFormatChange(); + + Gu::BV4TriangleData mData; + }; + + class BV32TriangleMeshBuilder + { + public: + static void createMidPhaseStructure(const PxCookingParams& params, Gu::TriangleMeshData& meshData, Gu::BV32Tree& bv32Tree); + static void saveMidPhaseStructure(Gu::BV32Tree* tree, PxOutputStream& stream, bool mismatch); + }; + + +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp b/src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp new file mode 100644 index 000000000..177dbe40b --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp @@ -0,0 +1,82 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "windows/PxWindowsDelayLoadHook.h" +#include "windows/PsWindowsInclude.h" +#include "windows/CmWindowsLoadLibrary.h" + +// Prior to Visual Studio 2015 Update 3, these hooks were non-const. +#define DELAYIMP_INSECURE_WRITABLE_HOOKS +#include + +static const physx::PxDelayLoadHook* gDelayLoadHook = NULL; + +void physx::PxSetPhysXCookingDelayLoadHook(const physx::PxDelayLoadHook* hook) +{ + gDelayLoadHook = hook; +} + +using namespace physx; + +#pragma comment(lib, "delayimp") + +FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli) +{ + switch (dliNotify) { + case dliStartProcessing : + break; + + case dliNotePreLoadLibrary : + { + return Cm::physXCommonDliNotePreLoadLibrary(pdli->szDll,gDelayLoadHook); + } + break; + + case dliNotePreGetProcAddress : + break; + + case dliFailLoadLib : + break; + + case dliFailGetProc : + break; + + case dliNoteEndProcessing : + break; + + default : + + return NULL; + } + + return NULL; +} + +PfnDliHook __pfnDliNotifyHook2 = delayHook; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp b/src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp new file mode 100644 index 000000000..ae638fb47 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxBounds3.h" +#include "PxBroadPhaseExt.h" +#include "PsFoundation.h" +#include "CmPhysXCommon.h" + +using namespace physx; + +PxU32 PxBroadPhaseExt::createRegionsFromWorldBounds(PxBounds3* regions, const PxBounds3& globalBounds, PxU32 nbSubdiv, PxU32 upAxis) +{ + PX_CHECK_MSG(globalBounds.isValid(), "PxBroadPhaseExt::createRegionsFromWorldBounds(): invalid bounds provided!"); + PX_CHECK_MSG(upAxis<3, "PxBroadPhaseExt::createRegionsFromWorldBounds(): invalid up-axis provided!"); + + const PxVec3& min = globalBounds.minimum; + const PxVec3& max = globalBounds.maximum; + const float dx = (max.x - min.x) / float(nbSubdiv); + const float dy = (max.y - min.y) / float(nbSubdiv); + const float dz = (max.z - min.z) / float(nbSubdiv); + PxU32 nbRegions = 0; + PxVec3 currentMin, currentMax; + for(PxU32 j=0;j releasableObjects; + + for (PxU32 i = 0; i < collection.getNbObjects(); ++i) + { + PxBase* s = &collection.getObject(i); + // pruning structure must be released before its actors + if(s->is()) + { + if(!releasableObjects.empty()) + { + PxBase* first = releasableObjects[0]; + releasableObjects.pushBack(first); + releasableObjects[0] = s; + } + } + else + { + if (s->isReleasable() && (releaseExclusiveShapes || !s->is() || !s->is()->isExclusive())) + releasableObjects.pushBack(s); + } + } + + for (PxU32 i = 0; i < releasableObjects.size(); ++i) + releasableObjects[i]->release(); + + while (collection.getNbObjects() > 0) + collection.remove(collection.getObject(0)); +} + + +void PxCollectionExt::remove(PxCollection& collection, PxType concreteType, PxCollection* to) +{ + shdfnd::Array removeObjects; + + for (PxU32 i = 0; i < collection.getNbObjects(); i++) + { + PxBase& object = collection.getObject(i); + if(concreteType == object.getConcreteType()) + { + if(to) + to->add(object); + + removeObjects.pushBack(&object); + } + } + + for (PxU32 i = 0; i < removeObjects.size(); ++i) + collection.remove(*removeObjects[i]); +} + +PxCollection* PxCollectionExt::createCollection(PxPhysics& physics) +{ + PxCollection* collection = PxCreateCollection(); + if (!collection) + return NULL; + + // Collect convexes + { + shdfnd::Array objects(physics.getNbConvexMeshes()); + const PxU32 nb = physics.getConvexMeshes(objects.begin(), objects.size()); + PX_ASSERT(nb == objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + // Collect triangle meshes + { + shdfnd::Array objects(physics.getNbTriangleMeshes()); + const PxU32 nb = physics.getTriangleMeshes(objects.begin(), objects.size()); + + PX_ASSERT(nb == objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + // Collect heightfields + { + shdfnd::Array objects(physics.getNbHeightFields()); + const PxU32 nb = physics.getHeightFields(objects.begin(), objects.size()); + + PX_ASSERT(nb == objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + // Collect materials + { + shdfnd::Array objects(physics.getNbMaterials()); + const PxU32 nb = physics.getMaterials(objects.begin(), objects.size()); + + PX_ASSERT(nb == objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + // Collect shapes + { + shdfnd::Array objects(physics.getNbShapes()); + const PxU32 nb = physics.getShapes(objects.begin(), objects.size()); + + PX_ASSERT(nb == objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + return collection; +} + +PxCollection* PxCollectionExt::createCollection(PxScene& scene) +{ + PxCollection* collection = PxCreateCollection(); + if (!collection) + return NULL; + + // Collect actors + { + PxActorTypeFlags selectionFlags = PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC; + + + shdfnd::Array objects(scene.getNbActors(selectionFlags)); + const PxU32 nb = scene.getActors(selectionFlags, objects.begin(), objects.size()); + + PX_ASSERT(nb==objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + + // Collect constraints + { + shdfnd::Array objects(scene.getNbConstraints()); + const PxU32 nb = scene.getConstraints(objects.begin(), objects.size()); + + PX_ASSERT(nb==objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;i(objects[i]->getExternalReference(typeId)); + if(typeId == PxConstraintExtIDs::eJOINT) + collection->add(*joint); + } + } + + // Collect articulations + { + shdfnd::Array objects(scene.getNbArticulations()); + const PxU32 nb = scene.getArticulations(objects.begin(), objects.size()); + + PX_ASSERT(nb==objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + // Collect aggregates + { + shdfnd::Array objects(scene.getNbAggregates()); + const PxU32 nb = scene.getAggregates(objects.begin(), objects.size()); + + PX_ASSERT(nb==objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + return collection; +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h b/src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h new file mode 100644 index 000000000..f9cdb45a2 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h @@ -0,0 +1,387 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_CONSTRAINT_HELPER_H +#define NP_CONSTRAINT_HELPER_H + +#include "foundation/PxAssert.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" +#include "PxJointLimit.h" +#include "ExtJoint.h" + +namespace physx +{ +namespace Ext +{ + namespace joint + { + PX_INLINE void computeJointFrames(PxTransform& cA2w, PxTransform& cB2w, const JointData& data, const PxTransform& bA2w, const PxTransform& bB2w) + { + PX_ASSERT(bA2w.isValid() && bB2w.isValid()); + + cA2w = bA2w.transform(data.c2b[0]); + cB2w = bB2w.transform(data.c2b[1]); + + PX_ASSERT(cA2w.isValid() && cB2w.isValid()); + } + + PX_INLINE void computeDerived(const JointData& data, + const PxTransform& bA2w, const PxTransform& bB2w, + PxTransform& cA2w, PxTransform& cB2w, PxTransform& cB2cA, + bool useShortestPath=true) + { + computeJointFrames(cA2w, cB2w, data, bA2w, bB2w); + + if(useShortestPath) + { + if(cA2w.q.dot(cB2w.q)<0.0f) // minimum error quat + cB2w.q = -cB2w.q; + } + + cB2cA = cA2w.transformInv(cB2w); + PX_ASSERT(cB2cA.isValid()); + } + + PX_INLINE PxVec3 truncateLinear(const PxVec3& in, PxReal tolerance, bool& truncated) + { + const PxReal m = in.magnitudeSquared(); + truncated = m>tolerance * tolerance; + return truncated ? in * PxRecipSqrt(m) * tolerance : in; + } + + PX_INLINE PxQuat truncateAngular(const PxQuat& in, PxReal sinHalfTol, PxReal cosHalfTol, bool& truncated) + { + truncated = false; + + if(sinHalfTol > 0.9999f) // fixes numerical tolerance issue of projecting because quat is not exactly normalized + return in; + + const PxQuat q = in.w>=0.0f ? in : -in; + + const PxVec3 im = q.getImaginaryPart(); + const PxReal m = im.magnitudeSquared(); + truncated = m>sinHalfTol*sinHalfTol; + if(!truncated) + return in; + + const PxVec3 outV = im * sinHalfTol * PxRecipSqrt(m); + return PxQuat(outV.x, outV.y, outV.z, cosHalfTol); + } + + PX_FORCE_INLINE void projectTransforms(PxTransform& bA2w, PxTransform& bB2w, + const PxTransform& cA2w, const PxTransform& cB2w, + const PxTransform& cB2cA, const JointData& data, bool projectToA) + { + PX_ASSERT(cB2cA.isValid()); + + // normalization here is unfortunate: long chains of projected constraints can result in + // accumulation of error in the quaternion which eventually leaves the quaternion + // magnitude outside the validation range. The approach here is slightly overconservative + // in that we could just normalize the quaternions which are out of range, but since we + // regard projection as an occasional edge case it shouldn't be perf-sensitive, and + // this way we maintain the invariant (also maintained by the dynamics integrator) that + // body quats are properly normalized up to FP error. + + if(projectToA) + { + bB2w = cA2w.transform(cB2cA.transform(data.c2b[1].getInverse())); + bB2w.q.normalize(); + } + else + { + bA2w = cB2w.transform(cB2cA.transformInv(data.c2b[0].getInverse())); + bA2w.q.normalize(); + } + + PX_ASSERT(bA2w.isValid()); + PX_ASSERT(bB2w.isValid()); + } + + PX_INLINE void computeJacobianAxes(PxVec3 row[3], const PxQuat& qa, const PxQuat& qb) + { + // Compute jacobian matrix for (qa* qb) [[* means conjugate in this expr]] + // d/dt (qa* qb) = 1/2 L(qa*) R(qb) (omega_b - omega_a) + // result is L(qa*) R(qb), where L(q) and R(q) are left/right q multiply matrix + + const PxReal wa = qa.w, wb = qb.w; + const PxVec3 va(qa.x,qa.y,qa.z), vb(qb.x,qb.y,qb.z); + + const PxVec3 c = vb*wa + va*wb; + const PxReal d0 = wa*wb; + const PxReal d1 = va.dot(vb); + const PxReal d = d0 - d1; + + row[0] = (va * vb.x + vb * va.x + PxVec3(d, c.z, -c.y)) * 0.5f; + row[1] = (va * vb.y + vb * va.y + PxVec3(-c.z, d, c.x)) * 0.5f; + row[2] = (va * vb.z + vb * va.z + PxVec3(c.y, -c.x, d)) * 0.5f; + + if((d0 + d1) != 0.0f) // check if relative rotation is 180 degrees which can lead to singular matrix + return; + else + { + row[0].x += PX_EPS_F32; + row[1].y += PX_EPS_F32; + row[2].z += PX_EPS_F32; + } + } + + PX_FORCE_INLINE Px1DConstraint* _linear(const PxVec3& axis, const PxVec3& ra, const PxVec3& rb, PxReal posErr, PxConstraintSolveHint::Enum hint, Px1DConstraint* c) + { + c->solveHint = PxU16(hint); + c->linear0 = axis; + c->angular0 = ra.cross(axis); + c->linear1 = axis; + c->angular1 = rb.cross(axis); + c->geometricError = posErr; + PX_ASSERT(c->linear0.isFinite()); + PX_ASSERT(c->linear1.isFinite()); + PX_ASSERT(c->angular0.isFinite()); + PX_ASSERT(c->angular1.isFinite()); + return c; + } + + PX_FORCE_INLINE Px1DConstraint* _angular(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint, Px1DConstraint* c) + { + c->solveHint = PxU16(hint); + c->linear0 = PxVec3(0.0f); + c->angular0 = axis; + c->linear1 = PxVec3(0.0f); + c->angular1 = axis; + c->geometricError = posErr; + c->flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT; + return c; + } + + class ConstraintHelper + { + Px1DConstraint* mConstraints; + Px1DConstraint* mCurrent; + PxVec3 mRa, mRb; + PxVec3 mCA2w, mCB2w; + + public: + ConstraintHelper(Px1DConstraint* c, const PxVec3& ra, const PxVec3& rb) + : mConstraints(c), mCurrent(c), mRa(ra), mRb(rb) {} + + ConstraintHelper(Px1DConstraint* c, PxConstraintInvMassScale& invMassScale, + PxTransform& cA2w, PxTransform& cB2w, + PxVec3& body0WorldOffset, + const JointData& data, const PxTransform& bA2w, const PxTransform& bB2w) + : mConstraints(c), mCurrent(c) + { + invMassScale = data.invMassScale; + + computeJointFrames(cA2w, cB2w, data, bA2w, bB2w); + + body0WorldOffset = cB2w.p - bA2w.p; + + mRa = cB2w.p - bA2w.p; + mRb = cB2w.p - bB2w.p; + + mCA2w = cA2w.p; + mCB2w = cB2w.p; + } + + PX_FORCE_INLINE const PxVec3& getRa() const { return mRa; } + PX_FORCE_INLINE const PxVec3& getRb() const { return mRb; } + + // hard linear & angular + PX_FORCE_INLINE void linearHard(const PxVec3& axis, PxReal posErr) + { + Px1DConstraint* c = linear(axis, posErr, PxConstraintSolveHint::eEQUALITY); + c->flags |= Px1DConstraintFlag::eOUTPUT_FORCE; + } + + PX_FORCE_INLINE void angularHard(const PxVec3& axis, PxReal posErr) + { + Px1DConstraint* c = angular(axis, posErr, PxConstraintSolveHint::eEQUALITY); + c->flags |= Px1DConstraintFlag::eOUTPUT_FORCE; + } + + // limited linear & angular + PX_FORCE_INLINE void linearLimit(const PxVec3& axis, PxReal ordinate, PxReal limitValue, const PxJointLimitParameters& limit) + { + const PxReal pad = limit.isSoft() ? 0.0f : limit.contactDistance; + + if(ordinate + pad > limitValue) + addLimit(linear(axis, limitValue - ordinate, PxConstraintSolveHint::eNONE), limit); + } + + PX_FORCE_INLINE void angularLimit(const PxVec3& axis, PxReal ordinate, PxReal limitValue, PxReal pad, const PxJointLimitParameters& limit) + { + if(limit.isSoft()) + pad = 0.0f; + + if(ordinate + pad > limitValue) + addLimit(angular(axis, limitValue - ordinate, PxConstraintSolveHint::eNONE), limit); + } + + PX_FORCE_INLINE void angularLimit(const PxVec3& axis, PxReal error, const PxJointLimitParameters& limit) + { + addLimit(angular(axis, error, PxConstraintSolveHint::eNONE), limit); + } + + PX_FORCE_INLINE void anglePair(PxReal angle, PxReal lower, PxReal upper, PxReal pad, const PxVec3& axis, const PxJointLimitParameters& limit) + { + PX_ASSERT(lower upper-pad) + angularLimit(axis, (upper - angle), limit); + } + + // driven linear & angular + + PX_FORCE_INLINE void linear(const PxVec3& axis, PxReal velTarget, PxReal error, const PxD6JointDrive& drive) + { + addDrive(linear(axis, error, PxConstraintSolveHint::eNONE), velTarget, drive); + } + + PX_FORCE_INLINE void angular(const PxVec3& axis, PxReal velTarget, PxReal error, const PxD6JointDrive& drive, PxConstraintSolveHint::Enum hint = PxConstraintSolveHint::eNONE) + { + addDrive(angular(axis, error, hint), velTarget, drive); + } + + PX_FORCE_INLINE PxU32 getCount() const { return PxU32(mCurrent - mConstraints); } + + void prepareLockedAxes(const PxQuat& qA, const PxQuat& qB, const PxVec3& cB2cAp, PxU32 lin, PxU32 ang, + PxVec3& raOut, PxVec3& rbOut) + { + Px1DConstraint* current = mCurrent; + + PxVec3 errorVector(0.f); + + PxVec3 ra = mRa; + PxVec3 rb = mRb; + if(lin) + { + const PxMat33 axes(qA); + + if(lin&1) errorVector -= axes.column0 * cB2cAp.x; + if(lin&2) errorVector -= axes.column1 * cB2cAp.y; + if(lin&4) errorVector -= axes.column2 * cB2cAp.z; + + ra += errorVector; + + if(lin&1) _linear(axes.column0, ra, rb, -cB2cAp.x, PxConstraintSolveHint::eEQUALITY, current++); + if(lin&2) _linear(axes.column1, ra, rb, -cB2cAp.y, PxConstraintSolveHint::eEQUALITY, current++); + if(lin&4) _linear(axes.column2, ra, rb, -cB2cAp.z, PxConstraintSolveHint::eEQUALITY, current++); + + + } + + if (ang) + { + const PxQuat qB2qA = qA.getConjugate() * qB; + + PxVec3 row[3]; + computeJacobianAxes(row, qA, qB); + if (ang & 1) _angular(row[0], -qB2qA.x, PxConstraintSolveHint::eEQUALITY, current++); + if (ang & 2) _angular(row[1], -qB2qA.y, PxConstraintSolveHint::eEQUALITY, current++); + if (ang & 4) _angular(row[2], -qB2qA.z, PxConstraintSolveHint::eEQUALITY, current++); + } + + raOut = ra; + rbOut = rb; + + for(Px1DConstraint* front = mCurrent; front < current; front++) + front->flags |= Px1DConstraintFlag::eOUTPUT_FORCE; + + mCurrent = current; + } + + PX_FORCE_INLINE Px1DConstraint* getConstraintRow() + { + return mCurrent++; + } + + private: + PX_FORCE_INLINE Px1DConstraint* linear(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint) + { + return _linear(axis, mRa, mRb, posErr, hint, mCurrent++); + } + + PX_FORCE_INLINE Px1DConstraint* angular(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint) + { + return _angular(axis, posErr, hint, mCurrent++); + } + + void addLimit(Px1DConstraint* c, const PxJointLimitParameters& limit) + { + PxU16 flags = PxU16(c->flags | Px1DConstraintFlag::eOUTPUT_FORCE); + + if(limit.isSoft()) + { + flags |= Px1DConstraintFlag::eSPRING; + c->mods.spring.stiffness = limit.stiffness; + c->mods.spring.damping = limit.damping; + } + else + { + c->solveHint = PxConstraintSolveHint::eINEQUALITY; + c->mods.bounce.restitution = limit.restitution; + c->mods.bounce.velocityThreshold = limit.bounceThreshold; + if(c->geometricError>0.0f) + flags |= Px1DConstraintFlag::eKEEPBIAS; + if(limit.restitution>0.0f) + flags |= Px1DConstraintFlag::eRESTITUTION; + } + + c->flags = flags; + c->minImpulse = 0.0f; + } + + void addDrive(Px1DConstraint* c, PxReal velTarget, const PxD6JointDrive& drive) + { + c->velocityTarget = velTarget; + + PxU16 flags = PxU16(c->flags | Px1DConstraintFlag::eSPRING | Px1DConstraintFlag::eHAS_DRIVE_LIMIT); + if(drive.flags & PxD6JointDriveFlag::eACCELERATION) + flags |= Px1DConstraintFlag::eACCELERATION_SPRING; + c->flags = flags; + c->mods.spring.stiffness = drive.stiffness; + c->mods.spring.damping = drive.damping; + + c->minImpulse = -drive.forceLimit; + c->maxImpulse = drive.forceLimit; + + PX_ASSERT(c->linear0.isFinite()); + PX_ASSERT(c->angular0.isFinite()); + } + }; + } +} // namespace + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp new file mode 100644 index 000000000..449eb6724 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp @@ -0,0 +1,250 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtContactJoint.h" +#include "ExtConstraintHelper.h" +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxContactJoint* physx::PxContactJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxContactJointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxContactJointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxContactJointCreate: actors must be different"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxContactJointCreate: at least one actor must be dynamic"); + + ContactJoint* j; + PX_NEW_SERIALIZED(j, ContactJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + if (j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +PxVec3 ContactJoint::getContact() const +{ + return data().contact; +} + +void ContactJoint::setContact(const PxVec3& contact) +{ + PX_CHECK_AND_RETURN(contact.isFinite(), "PxContactJoint::setContact: invalid parameter"); + data().contact = contact; + markDirty(); +} + +PxVec3 ContactJoint::getContactNormal() const +{ + return data().normal; +} + +void ContactJoint::setContactNormal(const PxVec3& normal) +{ + PX_CHECK_AND_RETURN(normal.isFinite(), "PxContactJoint::setContactNormal: invalid parameter"); + data().normal = normal; + markDirty(); +} + +PxReal ContactJoint::getPenetration() const +{ + return data().penetration; +} + +void ContactJoint::setPenetration(PxReal penetration) +{ + PX_CHECK_AND_RETURN(PxIsFinite(penetration), "ContactJoint::setPenetration: invalid parameter"); + data().penetration = penetration; + markDirty(); +} + + +PxReal ContactJoint::getResititution() const +{ + return data().restitution; +} + +void ContactJoint::setResititution(const PxReal restitution) +{ + PX_CHECK_AND_RETURN(PxIsFinite(restitution) && restitution >= 0.f && restitution <= 1.f, "ContactJoint::setResititution: invalid parameter"); + data().restitution = restitution; + markDirty(); +} + +PxReal ContactJoint::getBounceThreshold() const +{ + return data().bounceThreshold; +} + +void ContactJoint::setBounceThreshold(const PxReal bounceThreshold) +{ + PX_CHECK_AND_RETURN(PxIsFinite(bounceThreshold) && bounceThreshold > 0.f, "ContactJoint::setBounceThreshold: invalid parameter"); + data().bounceThreshold = bounceThreshold; + markDirty(); +} + +bool ContactJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(ContactJointData)); + return mPxConstraint != NULL; +} + + +void ContactJoint::exportExtraData(PxSerializationContext& stream) +{ + if (mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(ContactJointData)); + } + stream.writeName(mName); +} + +void ContactJoint::importExtraData(PxDeserializationContext& context) +{ + if (mData) + mData = context.readExtraData(); + + context.readName(mName); +} + +void ContactJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +void ContactJoint::computeJacobians(PxJacobianRow* jacobian) const +{ + const PxVec3 cp = data().contact; + const PxVec3 normal = data().normal; + + PxRigidActor* actor0, *actor1; + this->getActors(actor0, actor1); + + PxVec3 raXn(0.f), rbXn(0.f); + + if (actor0 && actor0->is()) + { + PxRigidBody* dyn = actor0->is(); + PxTransform cmassPose = dyn->getGlobalPose() * dyn->getCMassLocalPose(); + raXn = (cp - cmassPose.p).cross(normal); + } + + if (actor1 && actor1->is()) + { + PxRigidBody* dyn = actor1->is(); + PxTransform cmassPose = dyn->getGlobalPose() * dyn->getCMassLocalPose(); + rbXn = (cp - cmassPose.p).cross(normal); + } + + jacobian->linear0 = normal; + jacobian->angular0 = raXn; + jacobian->linear1 = -normal; + jacobian->angular1 = -rbXn; + +} +PxU32 ContactJoint::getNbJacobianRows() const +{ + return 1; +} + + +ContactJoint* ContactJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + ContactJoint* obj = new (address) ContactJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(ContactJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetContactJointShaderTable() +{ + return &ContactJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void ContactJointProject(const void* /*constantBlock*/, PxTransform& /*bodyAToWorld*/, PxTransform& /*bodyBToWorld*/, bool /*projectToA*/) +{ + // Not required +} + +static void ContactJointVisualize(PxConstraintVisualizer& /*viz*/, const void* /*constantBlock*/, const PxTransform& /*body0Transform*/, const PxTransform& /*body1Transform*/, PxU32 /*flags*/) +{ + //TODO +} + +static PxU32 ContactJointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& /*invMassScale*/, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const ContactJointData& data = *reinterpret_cast(constantBlock); + + const PxVec3& contact = data.contact; + const PxVec3& normal = data.normal; + + cA2wOut = contact; + cB2wOut = contact; + + const PxVec3 ra = contact - bA2w.p; + const PxVec3 rb = contact - bB2w.p; + + body0WorldOffset = PxVec3(0.f); + + Px1DConstraint& con = constraints[0]; + con.linear0 = normal; + con.linear1 = normal; + con.angular0 = ra.cross(normal); + con.angular1 = rb.cross(normal); + + con.geometricError = data.penetration; + con.minImpulse = 0.f; + con.maxImpulse = PX_MAX_F32; + + con.velocityTarget = 0.f; + con.forInternalUse = 0.f; + con.solveHint = 0; + con.flags = Px1DConstraintFlag::eOUTPUT_FORCE; + con.mods.bounce.restitution = data.restitution; + con.mods.bounce.velocityThreshold = data.bounceThreshold; + + return 1; +} + +PxConstraintShaderTable Ext::ContactJoint::sShaders = { ContactJointSolverPrep, ContactJointProject, ContactJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h new file mode 100644 index 000000000..08ad31d10 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h @@ -0,0 +1,122 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef NP_CONTACTJOINTCONSTRAINT_H +#define NP_CONTACTJOINTCONSTRAINT_H + +#include "PsUserAllocated.h" +#include "ExtJoint.h" +#include "PxContactJoint.h" +#include "PxTolerancesScale.h" +#include "CmUtils.h" + +namespace physx +{ +struct PxContactJointGeneratedValues; +namespace Ext +{ + struct ContactJointData : public JointData + { + PxVec3 contact; + PxVec3 normal; + PxReal penetration; + PxReal restitution; + PxReal bounceThreshold; + }; + + typedef Joint ContactJointT; + class ContactJoint : public ContactJointT + { + public: + // PX_SERIALIZATION + ContactJoint(PxBaseFlags baseFlags) : ContactJointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static ContactJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + ContactJoint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + ContactJointT(PxJointConcreteType::eCONTACT, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(ContactJointData), "ContactJointData") + { + PX_UNUSED(scale); + + ContactJointData* data = static_cast(mData); + + data->contact = PxVec3(0.f); + data->normal = PxVec3(0.f); + data->penetration = 0.f; + data->restitution = 0.f; + data->bounceThreshold = 0.f; + } + + // PxContactJoint + virtual PxVec3 getContact() const; + virtual void setContact(const PxVec3& contact); + virtual PxVec3 getContactNormal() const; + virtual void setContactNormal(const PxVec3& normal); + virtual PxReal getPenetration() const; + virtual void setPenetration(const PxReal penetration); + virtual PxReal getResititution() const; + virtual void setResititution(const PxReal resititution); + virtual PxReal getBounceThreshold() const; + virtual void setBounceThreshold(const PxReal bounceThreshold); + virtual void computeJacobians(PxJacobianRow* jacobian) const; + virtual PxU32 getNbJacobianRows() const; + + //~PxContactJoint + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep()const { return sShaders.solverPrep; } + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE ContactJointData& data() const + { + return *static_cast(mData); + } + }; + + +}// namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetContactJointShaderTable(); +} + +}//physx +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp new file mode 100644 index 000000000..97174e1e6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp @@ -0,0 +1,91 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxConvexMeshExt.h" +#include "PxConvexMeshGeometry.h" +#include "PxConvexMesh.h" +#include "foundation/PxTransform.h" + +using namespace physx; + +static const PxReal gEpsilon = .01f; + +PxU32 physx::PxFindFaceIndex(const PxConvexMeshGeometry& convexGeom, const PxTransform& pose, + const PxVec3& impactPos, const PxVec3& unitDir) +{ + PX_ASSERT(unitDir.isFinite()); + PX_ASSERT(unitDir.isNormalized()); + PX_ASSERT(impactPos.isFinite()); + PX_ASSERT(pose.isFinite()); + + const PxVec3 impact = impactPos - unitDir * gEpsilon; + + const PxVec3 localPoint = pose.transformInv(impact); + const PxVec3 localDir = pose.rotateInv(unitDir); + + // Create shape to vertex scale transformation matrix + const PxMeshScale& meshScale = convexGeom.scale; + const PxMat33 rot(meshScale.rotation); + PxMat33 shape2VertexSkew = rot.getTranspose(); + const PxMat33 diagonal = PxMat33::createDiagonal(PxVec3(1.0f / meshScale.scale.x, 1.0f / meshScale.scale.y, 1.0f / meshScale.scale.z)); + shape2VertexSkew = shape2VertexSkew * diagonal; + shape2VertexSkew = shape2VertexSkew * rot; + + const PxU32 nbPolys = convexGeom.convexMesh->getNbPolygons(); + PxU32 minIndex = 0; + PxReal minD = PX_MAX_REAL; + for (PxU32 j = 0; j < nbPolys; j++) + { + PxHullPolygon hullPolygon; + convexGeom.convexMesh->getPolygonData(j, hullPolygon); + + // transform hull plane into shape space + PxPlane plane; + const PxVec3 tmp = shape2VertexSkew.transformTranspose(PxVec3(hullPolygon.mPlane[0],hullPolygon.mPlane[1],hullPolygon.mPlane[2])); + const PxReal denom = 1.0f / tmp.magnitude(); + plane.n = tmp * denom; + plane.d = hullPolygon.mPlane[3] * denom; + + PxReal d = plane.distance(localPoint); + if (d < 0.0f) + continue; + + const PxReal tweak = plane.n.dot(localDir) * gEpsilon; + d += tweak; + + if (d < minD) + { + minIndex = j; + minD = d; + } + } + return minIndex; +} + diff --git a/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp new file mode 100644 index 000000000..30e17c35a --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp @@ -0,0 +1,106 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "task/PxTask.h" +#include "ExtCpuWorkerThread.h" +#include "ExtDefaultCpuDispatcher.h" +#include "ExtTaskQueueHelper.h" +#include "PsFPU.h" + +using namespace physx; + +Ext::CpuWorkerThread::CpuWorkerThread() +: mQueueEntryPool(EXT_TASK_QUEUE_ENTRY_POOL_SIZE), + mThreadId(0) +{ +} + + +Ext::CpuWorkerThread::~CpuWorkerThread() +{ +} + + +void Ext::CpuWorkerThread::initialize(DefaultCpuDispatcher* ownerDispatcher) +{ + mOwner = ownerDispatcher; +} + + +bool Ext::CpuWorkerThread::tryAcceptJobToLocalQueue(PxBaseTask& task, Ps::Thread::Id taskSubmitionThread) +{ + if(taskSubmitionThread == mThreadId) + { + SharedQueueEntry* entry = mQueueEntryPool.getEntry(&task); + if (entry) + { + mLocalJobList.push(*entry); + return true; + } + else + return false; + } + + return false; +} + + +PxBaseTask* Ext::CpuWorkerThread::giveUpJob() +{ + return TaskQueueHelper::fetchTask(mLocalJobList, mQueueEntryPool); +} + + +void Ext::CpuWorkerThread::execute() +{ + mThreadId = getId(); + + while (!quitIsSignalled()) + { + mOwner->resetWakeSignal(); + + PxBaseTask* task = TaskQueueHelper::fetchTask(mLocalJobList, mQueueEntryPool); + + if(!task) + task = mOwner->fetchNextTask(); + + if (task) + { + mOwner->runTask(*task); + task->release(); + } + else + { + mOwner->waitForWork(); + } + } + + quit(); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h new file mode 100644 index 000000000..e3f37b3f4 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_NP_CPU_WORKER_THREAD_H +#define PX_PHYSICS_EXTENSIONS_NP_CPU_WORKER_THREAD_H + +#include "CmPhysXCommon.h" +#include "PsThread.h" +#include "ExtDefaultCpuDispatcher.h" +#include "ExtSharedQueueEntryPool.h" + + +namespace physx +{ +namespace Ext +{ +class DefaultCpuDispatcher; + + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif // Because of the SList member I assume + + class CpuWorkerThread : public Ps::Thread + { + public: + CpuWorkerThread(); + ~CpuWorkerThread(); + + void initialize(DefaultCpuDispatcher* ownerDispatcher); + void execute(); + bool tryAcceptJobToLocalQueue(PxBaseTask& task, Ps::Thread::Id taskSubmitionThread); + PxBaseTask* giveUpJob(); + Ps::Thread::Id getWorkerThreadId() const { return mThreadId; } + + protected: + SharedQueueEntryPool<> mQueueEntryPool; + DefaultCpuDispatcher* mOwner; + Ps::SList mLocalJobList; + Ps::Thread::Id mThreadId; + }; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Ext + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp new file mode 100644 index 000000000..ac2e5fcea --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp @@ -0,0 +1,1089 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtD6Joint.h" +#include "ExtConstraintHelper.h" +#include "CmRenderOutput.h" +#include "CmConeLimitHelper.h" +#include "PxTolerancesScale.h" +#include "CmUtils.h" +#include "PxConstraint.h" + +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxD6Joint* physx::PxD6JointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxD6JointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxD6JointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxD6JointCreate: actors must be different"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxD6JointCreate: at least one actor must be dynamic"); + + D6Joint* j; + PX_NEW_SERIALIZED(j, D6Joint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +D6Joint::D6Joint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + D6JointT(PxJointConcreteType::eD6, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(D6JointData), "D6JointData"), + mRecomputeMotion (true) +{ + D6JointData* data = static_cast(mData); + + for(PxU32 i=0;i<6;i++) + data->motion[i] = PxD6Motion::eLOCKED; + + data->twistLimit = PxJointAngularLimitPair(-PxPi/2, PxPi/2); + data->swingLimit = PxJointLimitCone(PxPi/2, PxPi/2); + data->pyramidSwingLimit = PxJointLimitPyramid(-PxPi/2, PxPi/2, -PxPi/2, PxPi/2); + data->distanceLimit = PxJointLinearLimit(scale, PX_MAX_F32); + data->distanceMinDist = 1e-6f*scale.length; + + data->linearLimitX = PxJointLinearLimitPair(scale); + data->linearLimitY = PxJointLinearLimitPair(scale); + data->linearLimitZ = PxJointLinearLimitPair(scale); + + for(PxU32 i=0;idrive[i] = PxD6JointDrive(); + + data->drivePosition = PxTransform(PxIdentity); + data->driveLinearVelocity = PxVec3(0.0f); + data->driveAngularVelocity = PxVec3(0.0f); + + data->projectionLinearTolerance = 1e10f; + data->projectionAngularTolerance = PxPi; + + data->mUseDistanceLimit = false; + data->mUseNewLinearLimits = false; + data->mUseConeLimit = false; + data->mUsePyramidLimits = false; +} + +PxD6Motion::Enum D6Joint::getMotion(PxD6Axis::Enum index) const +{ + return data().motion[index]; +} + +void D6Joint::setMotion(PxD6Axis::Enum index, PxD6Motion::Enum t) +{ + data().motion[index] = t; + mRecomputeMotion = true; + markDirty(); +} + +PxReal D6Joint::getTwistAngle() const +{ + return getTwistAngle_Internal(); +} + +PxReal D6Joint::getSwingYAngle() const +{ + return getSwingYAngle_Internal(); +} + +PxReal D6Joint::getSwingZAngle() const +{ + return getSwingZAngle_Internal(); +} + +PxD6JointDrive D6Joint::getDrive(PxD6Drive::Enum index) const +{ + return data().drive[index]; +} + +void D6Joint::setDrive(PxD6Drive::Enum index, const PxD6JointDrive& d) +{ + PX_CHECK_AND_RETURN(d.isValid(), "PxD6Joint::setDrive: drive is invalid"); + + data().drive[index] = d; + mRecomputeMotion = true; + markDirty(); +} + +void D6Joint::setDistanceLimit(const PxJointLinearLimit& l) +{ + PX_CHECK_AND_RETURN(l.isValid(), "PxD6Joint::setDistanceLimit: limit invalid"); + data().distanceLimit = l; + data().mUseDistanceLimit = true; + markDirty(); +} + +PxJointLinearLimit D6Joint::getDistanceLimit() const +{ + return data().distanceLimit; +} + +void D6Joint::setLinearLimit(PxD6Axis::Enum axis, const PxJointLinearLimitPair& limit) +{ + PX_CHECK_AND_RETURN(axis>=PxD6Axis::eX && axis<=PxD6Axis::eZ, "PxD6Joint::setLinearLimit: invalid axis value"); + PX_CHECK_AND_RETURN(limit.isValid(), "PxD6Joint::setLinearLimit: limit invalid"); + D6JointData& d = data(); + if(axis==PxD6Axis::eX) + d.linearLimitX = limit; + else if(axis==PxD6Axis::eY) + d.linearLimitY = limit; + else if(axis==PxD6Axis::eZ) + d.linearLimitZ = limit; + else + return; + d.mUseNewLinearLimits = true; + markDirty(); +} + +PxJointLinearLimitPair D6Joint::getLinearLimit(PxD6Axis::Enum axis) const +{ + PX_CHECK_AND_RETURN_VAL(axis>=PxD6Axis::eX && axis<=PxD6Axis::eZ, "PxD6Joint::getLinearLimit: invalid axis value", PxJointLinearLimitPair(PxTolerancesScale(), 0.0f, 0.0f)); + const D6JointData& d = data(); + if(axis==PxD6Axis::eX) + return d.linearLimitX; + else if(axis==PxD6Axis::eY) + return d.linearLimitY; + else if(axis==PxD6Axis::eZ) + return d.linearLimitZ; + return PxJointLinearLimitPair(PxTolerancesScale(), 0.0f, 0.0f); +} + +PxJointAngularLimitPair D6Joint::getTwistLimit() const +{ + return data().twistLimit; +} + +void D6Joint::setTwistLimit(const PxJointAngularLimitPair& l) +{ + PX_CHECK_AND_RETURN(l.isValid(), "PxD6Joint::setTwistLimit: limit invalid"); + // PT: the tangent version is not compatible with the double-cover feature, since the potential limit extent in that case is 4*PI. + // i.e. we'd potentially take the tangent of something equal to PI/2. So the tangent stuff makes the limits less accurate, and it + // also reduces the available angular range for the joint. All that for questionable performance gains. + PX_CHECK_AND_RETURN(l.lower>-PxTwoPi && l.upper=0 && tolerance <= PxPi, "PxD6Joint::setProjectionAngularTolerance: tolerance invalid"); + data().projectionAngularTolerance = tolerance; + markDirty(); +} + +PxReal D6Joint::getProjectionAngularTolerance() const +{ + return data().projectionAngularTolerance; +} + +void D6Joint::setProjectionLinearTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxD6Joint::setProjectionLinearTolerance: invalid parameter"); + data().projectionLinearTolerance = tolerance; + markDirty(); +} + +PxReal D6Joint::getProjectionLinearTolerance() const +{ + return data().projectionLinearTolerance; +} + +void* D6Joint::prepareData() +{ + D6JointData& d = data(); + + if(mRecomputeMotion) + { + mRecomputeMotion = false; + + d.driving = 0; + d.limited = 0; + d.locked = 0; + + for(PxU32 i=0;iD6JointT::prepareData(); + + return mData; +} + +bool D6Joint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(D6JointData)); + return mPxConstraint!=NULL; +} + +void D6Joint::exportExtraData(PxSerializationContext& stream) +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(D6JointData)); + } + stream.writeName(mName); +} + +void D6Joint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + + context.readName(mName); +} + +void D6Joint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +D6Joint* D6Joint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + D6Joint* obj = new (address) D6Joint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(D6Joint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetD6JointShaderTable() +{ + return &D6Joint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +// Notes: +/* + +This used to be in the linear drive model: + + if(motion[PxD6Axis::eX+i] == PxD6Motion::eLIMITED) + { + if(data.driveLinearVelocity[i] < 0.0f && cB2cA.p[i] < -mLimits[PxD6Limit::eLINEAR].mValue || + data.driveLinearVelocity[i] > 0.0f && cB2cA.p[i] > mLimits[PxD6Limit::eLINEAR].mValue) + continue; + } + +it doesn't seem like a good idea though, because it turns off drive altogether, despite the fact that positional +drive might pull us back in towards the limit. Might be better to make the drive unilateral so it can only pull +us in from the limit + +This used to be in angular locked: + + // Angular locked + //TODO fix this properly. + if(PxAbs(cB2cA.q.x) < 0.0001f) cB2cA.q.x = 0; + if(PxAbs(cB2cA.q.y) < 0.0001f) cB2cA.q.y = 0; + if(PxAbs(cB2cA.q.z) < 0.0001f) cB2cA.q.z = 0; + if(PxAbs(cB2cA.q.w) < 0.0001f) cB2cA.q.w = 0; +*/ + +static PxQuat truncate(const PxQuat& qIn, PxReal minCosHalfTol, bool& truncated) +{ + const PxQuat q = qIn.w >= 0.0f ? qIn : -qIn; + truncated = q.w < minCosHalfTol; + if(!truncated) + return q; + const PxVec3 v = q.getImaginaryPart().getNormalized() * PxSqrt(1.0f - minCosHalfTol * minCosHalfTol); + return PxQuat(v.x, v.y, v.z, minCosHalfTol); +} + +// we decompose the quaternion as q1 * q2, where q1 is a rotation orthogonal to the unit axis, and q2 a rotation around it. +// (so for example if 'axis' is the twist axis, this is separateSwingTwist). +static PxQuat project(const PxQuat& q, const PxVec3& axis, PxReal cosHalfTol, bool& truncated) +{ + const PxReal a = q.getImaginaryPart().dot(axis); + const PxQuat q2 = PxAbs(a) >= 1e-6f ? PxQuat(a*axis.x, a*axis.y, a*axis.z, q.w).getNormalized() : PxQuat(PxIdentity); + const PxQuat q1 = q * q2.getConjugate(); + + PX_ASSERT(PxAbs(q1.getImaginaryPart().dot(q2.getImaginaryPart())) < 1e-6f); + + return truncate(q1, cosHalfTol, truncated) * q2; +} + +// Here's how the angular part works: +// * if no DOFs are locked, there's nothing to do. +// * if all DOFs are locked, we just truncate the rotation +// * if two DOFs are locked +// * we decompose the rotation into swing * twist, where twist is a rotation around the free DOF and swing is a rotation around an axis orthogonal to the free DOF +// * then we truncate swing +// The case of one locked DOF is currently unimplemented, but one option would be: +// * if one DOF is locked (the tricky case), we define the 'free' axis as follows (as the velocity solver prep function does) +// TWIST: cB[0] +// SWING1: cB[0].cross(cA[2]) +// SWING2: cB[0].cross(cA[1]) +// then, as above, we decompose into swing * free, and truncate the free rotation + +//export this in the physx namespace so we can unit test it +namespace physx +{ +PxQuat angularProject(PxU32 lockedDofs, const PxQuat& q, PxReal cosHalfTol, bool& truncated) +{ + PX_ASSERT(lockedDofs <= 7); + truncated = false; + + switch(lockedDofs) + { + case 0: return q; + case 1: return q; // currently unimplemented + case 2: return q; // currently unimplemented + case 3: return project(q, PxVec3(0.0f, 0.0f, 1.0f), cosHalfTol, truncated); + case 4: return q; // currently unimplemented + case 5: return project(q, PxVec3(0.0f, 1.0f, 0.0f), cosHalfTol, truncated); + case 6: return project(q, PxVec3(1.0f, 0.0f, 0.0f), cosHalfTol, truncated); + case 7: return truncate(q, cosHalfTol, truncated); + default: return PxQuat(PxIdentity); + } +} +} + +static void D6JointProject(const void* constantBlock, PxTransform& bodyAToWorld, PxTransform& bodyBToWorld, bool projectToA) +{ + const D6JointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w, cB2cA, projected; + joint::computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA, false); + + const PxVec3 v(data.locked & 1 ? cB2cA.p.x : 0.0f, + data.locked & 2 ? cB2cA.p.y : 0.0f, + data.locked & 4 ? cB2cA.p.z : 0.0f); + + bool linearTrunc, angularTrunc = false; + projected.p = joint::truncateLinear(v, data.projectionLinearTolerance, linearTrunc) + (cB2cA.p - v); + + projected.q = angularProject(data.locked >> 3, cB2cA.q, PxCos(data.projectionAngularTolerance / 2), angularTrunc); + + if(linearTrunc || angularTrunc) + joint::projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA); +} + +static PX_FORCE_INLINE PxReal computePhi(const PxQuat& q) +{ + PxQuat twist = q; + twist.normalize(); + + PxReal angle = twist.getAngle(); + if(twist.x<0.0f) + angle = -angle; + return angle; +} + +static void visualizeAngularLimit(PxConstraintVisualizer& viz, const D6JointData& data, const PxTransform& t, float swingYZ, float swingW, float swingLimitYZ) +{ + bool active = PxAbs(computeSwingAngle(swingYZ, swingW)) > swingLimitYZ - data.swingLimit.contactDistance; + viz.visualizeAngularLimit(t, -swingLimitYZ, swingLimitYZ, active); +} + +static void visualizeDoubleCone(PxConstraintVisualizer& viz, const D6JointData& data, const PxTransform& t, float sin, float swingLimitYZ) +{ + const PxReal angle = PxAsin(sin); + const PxReal pad = data.swingLimit.contactDistance; + const PxReal low = -swingLimitYZ; + const PxReal high = swingLimitYZ; + + const bool active = isLimitActive(data.swingLimit, pad, angle, low, high); + viz.visualizeDoubleCone(t, swingLimitYZ, active); +} + +// PT: TODO: refactor with spherical joint code +static void visualizeCone(PxConstraintVisualizer& viz, const D6JointData& data, const PxQuat& swing, const PxTransform& cA2w) +{ + const PxVec3 swingAngle(0.0f, computeSwingAngle(swing.y, swing.w), computeSwingAngle(swing.z, swing.w)); + const PxReal pad = data.swingLimit.isSoft() ? 0.0f : data.swingLimit.contactDistance; + Cm::ConeLimitHelperTanLess coneHelper(data.swingLimit.yAngle, data.swingLimit.zAngle, pad); + viz.visualizeLimitCone(cA2w, PxTan(data.swingLimit.zAngle / 4), PxTan(data.swingLimit.yAngle / 4), !coneHelper.contains(swingAngle)); +} + +static PX_FORCE_INLINE bool isLinearLimitActive(const PxJointLinearLimitPair& limit, float ordinate) +{ + const PxReal pad = limit.isSoft() ? 0.0f : limit.contactDistance; + return (ordinate < limit.lower + pad) || (ordinate > limit.upper - pad); +} + +static void visualizeLine(PxConstraintVisualizer& viz, const PxVec3& origin, const PxVec3& axis, const PxJointLinearLimitPair& limit, float ordinate) +{ + const bool active = isLinearLimitActive(limit, ordinate); + const PxVec3 p0 = origin + axis * limit.lower; + const PxVec3 p1 = origin + axis * limit.upper; + viz.visualizeLine(p0, p1, active ? 0xff0000u : 0xffffffu); +} + +static void visualizeQuad(PxConstraintVisualizer& viz, const PxVec3& origin, const PxVec3& axis0, const PxJointLinearLimitPair& limit0, float ordinate0, + const PxVec3& axis1, const PxJointLinearLimitPair& limit1, float ordinate1) +{ + const bool active0 = isLinearLimitActive(limit0, ordinate0); + const bool active1 = isLinearLimitActive(limit1, ordinate1); + const PxU32 color = (active0 || active1) ? 0xff0000u : 0xffffffu; + + const PxVec3 l0 = axis0 * limit0.lower; + const PxVec3 u0 = axis0 * limit0.upper; + const PxVec3 l1 = axis1 * limit1.lower; + const PxVec3 u1 = axis1 * limit1.upper; + + const PxVec3 p0 = origin + l0 + l1; + const PxVec3 p1 = origin + u0 + l1; + const PxVec3 p2 = origin + u0 + u1; + const PxVec3 p3 = origin + l0 + u1; + + viz.visualizeLine(p0, p1, color); + viz.visualizeLine(p1, p2, color); + viz.visualizeLine(p2, p3, color); + viz.visualizeLine(p3, p0, color); +} + +static void visualizeBox(PxConstraintVisualizer& viz, const PxVec3& origin, const PxVec3& axis0, const PxJointLinearLimitPair& limit0, float ordinate0, + const PxVec3& axis1, const PxJointLinearLimitPair& limit1, float ordinate1, + const PxVec3& axis2, const PxJointLinearLimitPair& limit2, float ordinate2) +{ + const bool active0 = isLinearLimitActive(limit0, ordinate0); + const bool active1 = isLinearLimitActive(limit1, ordinate1); + const bool active2 = isLinearLimitActive(limit2, ordinate2); + const PxU32 color = (active0 || active1 || active2) ? 0xff0000u : 0xffffffu; + + const PxVec3 l0 = axis0 * limit0.lower; + const PxVec3 u0 = axis0 * limit0.upper; + const PxVec3 l1 = axis1 * limit1.lower; + const PxVec3 u1 = axis1 * limit1.upper; + const PxVec3 l2 = axis2 * limit2.lower; + const PxVec3 u2 = axis2 * limit2.upper; + + const PxVec3 p0 = origin + l0 + l1 + l2; + const PxVec3 p1 = origin + u0 + l1 + l2; + const PxVec3 p2 = origin + u0 + u1 + l2; + const PxVec3 p3 = origin + l0 + u1 + l2; + const PxVec3 p0b = origin + l0 + l1 + u2; + const PxVec3 p1b = origin + u0 + l1 + u2; + const PxVec3 p2b = origin + u0 + u1 + u2; + const PxVec3 p3b = origin + l0 + u1 + u2; + + viz.visualizeLine(p0, p1, color); + viz.visualizeLine(p1, p2, color); + viz.visualizeLine(p2, p3, color); + viz.visualizeLine(p3, p0, color); + viz.visualizeLine(p0b, p1b, color); + viz.visualizeLine(p1b, p2b, color); + viz.visualizeLine(p2b, p3b, color); + viz.visualizeLine(p3b, p0b, color); + viz.visualizeLine(p0, p0b, color); + viz.visualizeLine(p1, p1b, color); + viz.visualizeLine(p2, p2b, color); + viz.visualizeLine(p3, p3b, color); +} + +static float computeLimitedDistance(const D6JointData& data, const PxTransform& cB2cA, const PxMat33& cA2w_m, PxVec3& _limitDir) +{ + PxVec3 limitDir(0.0f); + + for(PxU32 i = 0; i<3; i++) + { + if(data.limited & (1 << (PxD6Axis::eX + i))) + limitDir += cA2w_m[i] * cB2cA.p[i]; + } + + _limitDir = limitDir; + return limitDir.magnitude(); +} + +void _setRotY(PxMat33& m, PxReal angle) +{ + m = PxMat33(PxIdentity); + + const PxReal cos = cosf(angle); + const PxReal sin = sinf(angle); + + m[0][0] = m[2][2] = cos; + m[0][2] = -sin; + m[2][0] = sin; +} + +void _setRotZ(PxMat33& m, PxReal angle) +{ + m = PxMat33(PxIdentity); + + const PxReal cos = cosf(angle); + const PxReal sin = sinf(angle); + + m[0][0] = m[1][1] = cos; + m[0][1] = sin; + m[1][0] = -sin; +} + +PxQuat _getRotYQuat(float angle) +{ + PxMat33 m; + _setRotY(m, angle); + return PxQuat(m); +} + +PxQuat _getRotZQuat(float angle) +{ + PxMat33 m; + _setRotZ(m, angle); + return PxQuat(m); +} + +void _setRotX(PxMat33& m, PxReal angle) +{ + m = PxMat33(PxIdentity); + + const PxReal cos = cosf(angle); + const PxReal sin = sinf(angle); + + m[1][1] = m[2][2] = cos; + m[1][2] = sin; + m[2][1] = -sin; +} + +PxQuat _getRotXQuat(float angle) +{ + PxMat33 m; + _setRotX(m, angle); + return PxQuat(m); +} + +static void drawPyramid(PxConstraintVisualizer& viz, const D6JointData& data, const PxTransform& cA2w, const PxQuat& swing, bool useY, bool useZ) +{ + struct Local + { + static void drawArc(PxConstraintVisualizer& _viz, const PxTransform& _cA2w, float ymin, float ymax, float zmin, float zmax, PxU32 color) + { + // PT: we use 32 segments for the cone case, so that's 32/4 segments per arc in the pyramid case + const PxU32 nb = 32/4; + PxVec3 prev(0.0f); + for(PxU32 i=0;i(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + viz.visualizeJointFrames(cA2w, cB2w); + + if(flags & PxConstraintVisualizationFlag::eLIMITS) + { + // PT: it is a mistake to use the neighborhood operator since it + // prevents us from using the quat's double-cover feature. +// if(cA2w.q.dot(cB2w.q)<0.0f) +// cB2w.q = -cB2w.q; + + const PxTransform cB2cA = cA2w.transformInv(cB2w); + const PxMat33 cA2w_m(cA2w.q), cB2w_m(cB2w.q); + + if(data.mUseNewLinearLimits) + { + switch(data.limited) + { + case 1< data.distanceMinDist) + { + PxU32 color = 0x00ff00; + if(distance>data.distanceLimit.value) + color = 0xff0000; + + viz.visualizeLine(cA2w.p, cB2w.p, color); + } + } + + PxQuat swing, twist; + Ps::separateSwingTwist(cB2cA.q, swing, twist); + + const PxVec3& bX = cB2w_m.column0; + const PxVec3& aY = cA2w_m.column1; + const PxVec3& aZ = cA2w_m.column2; + + if(data.limited&TWIST_FLAG) + { + const PxReal angle = computePhi(twist); + const PxReal pad = data.twistLimit.contactDistance; + const PxReal low = data.twistLimit.lower; + const PxReal high = data.twistLimit.upper; + + const bool active = isLimitActive(data.twistLimit, pad, angle, low, high); + viz.visualizeAngularLimit(cA2w, data.twistLimit.lower, data.twistLimit.upper, active); + } + + const bool swing1Limited = (data.limited & SWING1_FLAG)!=0, swing2Limited = (data.limited & SWING2_FLAG)!=0; + + if(swing1Limited && swing2Limited) + { + if(data.mUseConeLimit) + visualizeCone(viz, data, swing, cA2w); + + if(data.mUsePyramidLimits) + drawPyramid(viz, data, cA2w, swing, true, true); + } + else if(swing1Limited ^ swing2Limited) + { + const PxTransform yToX(PxVec3(0.0f), PxQuat(-PxPi/2.0f, PxVec3(0.0f, 0.0f, 1.0f))); + const PxTransform zToX(PxVec3(0.0f), PxQuat(PxPi/2.0f, PxVec3(0.0f, 1.0f, 0.0f))); + + if(swing1Limited) + { + if(data.locked & SWING2_FLAG) + { + if(data.mUsePyramidLimits) + drawPyramid(viz, data, cA2w, swing, true, false); + else + visualizeAngularLimit(viz, data, cA2w * yToX, swing.y, swing.w, data.swingLimit.yAngle); // PT: swing Y limited, swing Z locked + } + else + if(!data.mUsePyramidLimits) + visualizeDoubleCone(viz, data, cA2w * zToX, aZ.dot(bX), data.swingLimit.yAngle); // PT: swing Y limited, swing Z free + } + else + { + if(data.locked & SWING1_FLAG) + { + if(data.mUsePyramidLimits) + drawPyramid(viz, data, cA2w, swing, false, true); + else + visualizeAngularLimit(viz, data, cA2w * zToX, swing.z, swing.w, data.swingLimit.zAngle); // PT: swing Z limited, swing Y locked + } + else + if(!data.mUsePyramidLimits) + visualizeDoubleCone(viz, data, cA2w * yToX, aY.dot(bX), data.swingLimit.zAngle); // PT: swing Z limited, swing Y free + } + } + } +} + +static PX_FORCE_INLINE void setupSingleSwingLimit(joint::ConstraintHelper& ch, const D6JointData& data, const PxVec3& axis, float swingYZ, float swingW, float swingLimitYZ) +{ + ch.anglePair(computeSwingAngle(swingYZ, swingW), -swingLimitYZ, swingLimitYZ, data.swingLimit.contactDistance, axis, data.swingLimit); +} + +static PX_FORCE_INLINE void setupDualConeSwingLimits(joint::ConstraintHelper& ch, const D6JointData& data, const PxVec3& axis, float sin, float swingLimitYZ) +{ + ch.anglePair(PxAsin(sin), -swingLimitYZ, swingLimitYZ, data.swingLimit.contactDistance, axis.getNormalized(), data.swingLimit); +} + +// PT: TODO: refactor with spherical joint code +static void setupConeSwingLimits(joint::ConstraintHelper& ch, const D6JointData& data, const PxQuat& swing, const PxTransform& cA2w) +{ + PxVec3 axis; + PxReal error; + const PxReal pad = data.swingLimit.isSoft() ? 0.0f : data.swingLimit.contactDistance; + const Cm::ConeLimitHelperTanLess coneHelper(data.swingLimit.yAngle, data.swingLimit.zAngle, pad); + bool active = coneHelper.getLimit(swing, axis, error); + if(active) + ch.angularLimit(cA2w.rotate(axis), error, data.swingLimit); +} + +static void setupPyramidSwingLimits(joint::ConstraintHelper& ch, const D6JointData& data, const PxQuat& swing, const PxTransform& cA2w, bool useY, bool useZ) +{ + const PxQuat q = cA2w.q * swing; + const PxJointLimitPyramid& l = data.pyramidSwingLimit; + if(useY) + ch.anglePair(computeSwingAngle(swing.y, swing.w), l.yAngleMin, l.yAngleMax, l.contactDistance, q.getBasisVector1(), l); + if(useZ) + ch.anglePair(computeSwingAngle(swing.z, swing.w), l.zAngleMin, l.zAngleMax, l.contactDistance, q.getBasisVector2(), l); +} + +static void setupLinearLimit(joint::ConstraintHelper& ch, const PxJointLinearLimitPair& limit, const float origin, const PxVec3& axis) +{ + ch.linearLimit(axis, origin, limit.upper, limit); + ch.linearLimit(-axis, -origin, -limit.lower, limit); +} + +static PxU32 D6JointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool useExtendedLimits, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const D6JointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + const PxU32 SWING1_FLAG = 1<0.0f ? data.drivePosition.q : -data.drivePosition.q; + + const PxVec3& v = data.driveAngularVelocity; + const PxQuat delta = d2cA_q.getConjugate() * cB2cA.q; + + if(driving & (1< data.distanceMinDist) + ch.linearLimit(limitDir * (1.0f/distance), distance, data.distanceLimit.value, data.distanceLimit); + } + + if(data.mUseNewLinearLimits) // PT: new asymmetric linear limits + { + const PxVec3& bOriginInA = cB2cA.p; + + // PT: TODO: we check that the DOFs are not "locked" to be consistent with the prismatic joint, but it + // doesn't look like this case is possible, since it would be caught by the "isValid" check when setting + // the limits. And in fact the "distance" linear limit above doesn't do this check. + if((limited & (1<>3, ra, rb); + + cA2wOut = ra + bA2w.p; + cB2wOut = rb + bB2w.p; + + /*cA2wOut = cA2w.p; + cB2wOut = cB2w.p;*/ + + // PT: TODO: check the number cannot be too high now + return ch.getCount(); +} + +PxConstraintShaderTable Ext::D6Joint::sShaders = { D6JointSolverPrep, D6JointProject, D6JointVisualize, /*PxConstraintFlag::Enum(0)*/PxConstraintFlag::eGPU_COMPATIBLE }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h new file mode 100644 index 000000000..93d3da2c7 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h @@ -0,0 +1,200 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_D6JOINTCONSTRAINT_H +#define NP_D6JOINTCONSTRAINT_H + +#include "ExtJoint.h" +#include "PxD6Joint.h" +#include "PsMathUtils.h" + +namespace physx +{ +struct PxD6JointGeneratedValues; +namespace Ext +{ + struct D6JointData : public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxD6Motion::Enum motion[6]; + PxJointLinearLimit distanceLimit; + PxJointLinearLimitPair linearLimitX; + PxJointLinearLimitPair linearLimitY; + PxJointLinearLimitPair linearLimitZ; + PxJointAngularLimitPair twistLimit; + PxJointLimitCone swingLimit; + PxJointLimitPyramid pyramidSwingLimit; + + PxD6JointDrive drive[PxD6Drive::eCOUNT]; + + PxTransform drivePosition; + PxVec3 driveLinearVelocity; + PxVec3 driveAngularVelocity; + + // derived quantities + + PxU32 locked; // bitmap of locked DOFs + PxU32 limited; // bitmap of limited DOFs + PxU32 driving; // bitmap of active drives (implies driven DOFs not locked) + + PxReal distanceMinDist; // distance limit minimum distance to get a good direction + + // projection quantities + PxReal projectionLinearTolerance; + PxReal projectionAngularTolerance; + + // PT: the PxD6Motion values are now shared for both kind of linear limits, so we need + // an extra bool to know which one(s) should be actually used. + bool mUseDistanceLimit; + bool mUseNewLinearLimits; + + // PT: the swing limits can now be a cone or a pyramid, so we need + // an extra bool to know which one(s) should be actually used. + bool mUseConeLimit; + bool mUsePyramidLimits; + + // forestall compiler complaints about not being able to generate a constructor + private: + D6JointData(const PxJointLinearLimit& distance, + const PxJointLinearLimitPair& linearX, + const PxJointLinearLimitPair& linearY, + const PxJointLinearLimitPair& linearZ, + const PxJointAngularLimitPair& twist, + const PxJointLimitCone& swing, + const PxJointLimitPyramid& pyramid) : + distanceLimit (distance), + linearLimitX (linearX), + linearLimitY (linearY), + linearLimitZ (linearZ), + twistLimit (twist), + swingLimit (swing), + pyramidSwingLimit (pyramid), + mUseDistanceLimit (false), + mUseNewLinearLimits (false), + mUseConeLimit (false), + mUsePyramidLimits (false) + {} + }; + + typedef Joint D6JointT; + + class D6Joint : public Joint + { + public: +// PX_SERIALIZATION + D6Joint(PxBaseFlags baseFlags) : D6JointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static D6Joint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + D6Joint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + + // PxD6Joint + virtual void setMotion(PxD6Axis::Enum index, PxD6Motion::Enum t); + virtual PxD6Motion::Enum getMotion(PxD6Axis::Enum index) const; + virtual PxReal getTwistAngle() const; + virtual PxReal getSwingYAngle() const; + virtual PxReal getSwingZAngle() const; + virtual void setDistanceLimit(const PxJointLinearLimit& l); + virtual PxJointLinearLimit getDistanceLimit() const; + virtual void setLinearLimit(PxD6Axis::Enum axis, const PxJointLinearLimitPair& limit); + virtual PxJointLinearLimitPair getLinearLimit(PxD6Axis::Enum axis) const; + virtual void setTwistLimit(const PxJointAngularLimitPair& l); + virtual PxJointAngularLimitPair getTwistLimit() const; + virtual void setSwingLimit(const PxJointLimitCone& l); + virtual PxJointLimitCone getSwingLimit() const; + virtual void setPyramidSwingLimit(const PxJointLimitPyramid& limit); + virtual PxJointLimitPyramid getPyramidSwingLimit() const; + virtual void setDrive(PxD6Drive::Enum index, const PxD6JointDrive& d); + virtual PxD6JointDrive getDrive(PxD6Drive::Enum index) const; + virtual void setDrivePosition(const PxTransform& pose, bool autowake = true); + virtual PxTransform getDrivePosition() const; + virtual void setDriveVelocity(const PxVec3& linear, const PxVec3& angular, bool autowake = true); + virtual void getDriveVelocity(PxVec3& linear, PxVec3& angular) const; + virtual void setProjectionLinearTolerance(PxReal tolerance); + virtual PxReal getProjectionLinearTolerance() const; + virtual void setProjectionAngularTolerance(PxReal tolerance); + virtual PxReal getProjectionAngularTolerance() const; + //~PxD6Joint + + void visualize(PxRenderBuffer& out, + const void* constantBlock, + const PxTransform& body0Transform, + const PxTransform& body1Transform, + PxReal frameScale, + PxReal limitScale, + PxU32 flags) const; + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep() const { return sShaders.solverPrep; } + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE D6JointData& data() const + { + return *static_cast(mData); + } + + bool active(const PxD6Drive::Enum index) const + { + PxD6JointDrive& d = data().drive[index]; + return d.stiffness!=0 || d.damping != 0; + } + + void* prepareData(); + + bool mRecomputeMotion; + bool mPadding[3]; // PT: padding from prev bool + }; + +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetD6JointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp b/src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp new file mode 100644 index 000000000..dc4e7205d --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp @@ -0,0 +1,289 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxD6JointCreate.h" +#include "PxD6Joint.h" +#include "PxFixedJoint.h" +#include "PxRevoluteJoint.h" +#include "PxSphericalJoint.h" +#include "PxPrismaticJoint.h" +#include "PxDistanceJoint.h" +#include "PxPhysics.h" +#include "foundation/PxMathUtils.h" + +using namespace physx; + +static const PxVec3 gX(1.0f, 0.0f, 0.0f); + +static void setRotY(PxMat33& m, PxReal angle) +{ + m = PxMat33(PxIdentity); + + const PxReal cos = cosf(angle); + const PxReal sin = sinf(angle); + + m[0][0] = m[2][2] = cos; + m[0][2] = -sin; + m[2][0] = sin; +} + +static void setRotZ(PxMat33& m, PxReal angle) +{ + m = PxMat33(PxIdentity); + + const PxReal cos = cosf(angle); + const PxReal sin = sinf(angle); + + m[0][0] = m[1][1] = cos; + m[0][1] = sin; + m[1][0] = -sin; +} + +static PxQuat getRotYQuat(float angle) +{ + PxMat33 m; + setRotY(m, angle); + return PxQuat(m); +} + +static PxQuat getRotZQuat(float angle) +{ + PxMat33 m; + setRotZ(m, angle); + return PxQuat(m); +} + +PxJoint* physx::PxD6JointCreate_Fixed(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, bool useD6) +{ + const PxTransform jointFrame0(localPos0); + const PxTransform jointFrame1(localPos1); + if(useD6) + // PT: by default all D6 axes are locked, i.e. it is a fixed joint. + return PxD6JointCreate(physics, actor0, jointFrame0, actor1, jointFrame1); + else + return PxFixedJointCreate(physics, actor0, jointFrame0, actor1, jointFrame1); +} + +PxJoint* physx::PxD6JointCreate_Distance(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, float maxDist, bool useD6) +{ + const PxTransform localFrame0(localPos0); + const PxTransform localFrame1(localPos1); + + if(useD6) + { + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE); + + j->setMotion(PxD6Axis::eX, PxD6Motion::eLIMITED); + j->setMotion(PxD6Axis::eY, PxD6Motion::eLIMITED); + j->setMotion(PxD6Axis::eZ, PxD6Motion::eLIMITED); + j->setDistanceLimit(PxJointLinearLimit(PxTolerancesScale(), maxDist)); + return j; + } + else + { + PxDistanceJoint* j = PxDistanceJointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setDistanceJointFlag(PxDistanceJointFlag::eMAX_DISTANCE_ENABLED, true); + j->setMaxDistance(maxDist); + return j; + } +} + +PxJoint* physx::PxD6JointCreate_Prismatic(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float minLimit, float maxLimit, bool useD6) +{ + const PxQuat q = PxShortestRotation(gX, axis); + const PxTransform localFrame0(localPos0, q); + const PxTransform localFrame1(localPos1, q); + + const PxJointLinearLimitPair limit(PxTolerancesScale(), minLimit, maxLimit); + + if(useD6) + { + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setMotion(PxD6Axis::eX, PxD6Motion::eFREE); + if(minLimit==maxLimit) + j->setMotion(PxD6Axis::eX, PxD6Motion::eLOCKED); + else if(minLimit>maxLimit) + j->setMotion(PxD6Axis::eX, PxD6Motion::eFREE); + else// if(minLimitsetMotion(PxD6Axis::eX, PxD6Motion::eLIMITED); + j->setLinearLimit(PxD6Axis::eX, limit); + } + return j; + } + else + { + PxPrismaticJoint* j = PxPrismaticJointCreate(physics, actor0, localFrame0, actor1, localFrame1); + if(minLimitsetPrismaticJointFlag(PxPrismaticJointFlag::eLIMIT_ENABLED, true); + j->setLimit(limit); + } + return j; + } +} + +PxJoint* physx::PxD6JointCreate_Revolute(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float minLimit, float maxLimit, bool useD6) +{ + const PxQuat q = PxShortestRotation(gX, axis); + const PxTransform localFrame0(localPos0, q); + const PxTransform localFrame1(localPos1, q); + + const PxJointAngularLimitPair limit(minLimit, maxLimit); + + if(useD6) + { + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + if(minLimit==maxLimit) + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eLOCKED); + else if(minLimit>maxLimit) + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); + else// if(minLimitsetMotion(PxD6Axis::eTWIST, PxD6Motion::eLIMITED); + j->setTwistLimit(limit); + } + return j; + } + else + { + PxRevoluteJoint* j = PxRevoluteJointCreate(physics, actor0, localFrame0, actor1, localFrame1); + if(minLimitsetRevoluteJointFlag(PxRevoluteJointFlag::eLIMIT_ENABLED, true); + j->setLimit(limit); + } + return j; + } +} + +PxJoint* physx::PxD6JointCreate_Spherical(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float limit1, float limit2, bool useD6) +{ + const PxQuat q = PxShortestRotation(gX, axis); + const PxTransform localFrame0(localPos0, q); + const PxTransform localFrame1(localPos1, q); + + const PxJointLimitCone limit(limit1, limit2); + + if(useD6) + { + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); + if(limit1>0.0f && limit2>0.0f) + { + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eLIMITED); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eLIMITED); + j->setSwingLimit(limit); + } + else + { + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE); + } + return j; + } + else + { + PxSphericalJoint* j = PxSphericalJointCreate(physics, actor0, localFrame0, actor1, localFrame1); + if(limit1>0.0f && limit2>0.0f) + { + j->setSphericalJointFlag(PxSphericalJointFlag::eLIMIT_ENABLED, true); + j->setLimitCone(limit); + } + return j; + } +} + +PxJoint* physx::PxD6JointCreate_GenericCone(float& apiroty, float& apirotz, PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, float minLimit1, float maxLimit1, float minLimit2, float maxLimit2, bool useD6) +{ + const float DesiredMinSwingY = minLimit1; + const float DesiredMaxSwingY = maxLimit1; + const float DesiredMinSwingZ = minLimit2; + const float DesiredMaxSwingZ = maxLimit2; + const float APIMaxY = (DesiredMaxSwingY - DesiredMinSwingY)*0.5f; + const float APIMaxZ = (DesiredMaxSwingZ - DesiredMinSwingZ)*0.5f; + const float APIRotY = (DesiredMaxSwingY + DesiredMinSwingY)*0.5f; + const float APIRotZ = (DesiredMaxSwingZ + DesiredMinSwingZ)*0.5f; + apiroty = APIRotY; + apirotz = APIRotZ; + + const PxQuat RotY = getRotYQuat(APIRotY); + const PxQuat RotZ = getRotZQuat(APIRotZ); + const PxQuat Rot = RotY * RotZ; + + const PxTransform localFrame0(localPos0, Rot); + const PxTransform localFrame1(localPos1); + + const PxJointLimitCone limit(APIMaxY, APIMaxZ); + + if(useD6) + { + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eLIMITED); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eLIMITED); + j->setSwingLimit(limit); + return j; + } + else + { + PxSphericalJoint* j = PxSphericalJointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setSphericalJointFlag(PxSphericalJointFlag::eLIMIT_ENABLED, true); + j->setLimitCone(limit); + return j; + } +} + +PxJoint* physx::PxD6JointCreate_Pyramid(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, + float minLimit1, float maxLimit1, float minLimit2, float maxLimit2) +{ + const PxQuat q = PxShortestRotation(gX, axis); + const PxTransform localFrame0(localPos0, q); + const PxTransform localFrame1(localPos1, q); + + const PxJointLimitPyramid limit(minLimit1, maxLimit1, minLimit2, maxLimit2); + + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); + if(limit.isValid()) + { + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eLIMITED); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eLIMITED); + j->setPyramidSwingLimit(limit); + } + else + { + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE); + } + return j; +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp new file mode 100644 index 000000000..e8a1470f4 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp @@ -0,0 +1,205 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtDefaultCpuDispatcher.h" +#include "ExtCpuWorkerThread.h" +#include "ExtTaskQueueHelper.h" +#include "PsString.h" + +using namespace physx; + +PxDefaultCpuDispatcher* physx::PxDefaultCpuDispatcherCreate(PxU32 numThreads, PxU32* affinityMasks) +{ + return PX_NEW(Ext::DefaultCpuDispatcher)(numThreads, affinityMasks); +} + +#if !PX_PS4 && !PX_XBOXONE && !PX_SWITCH +void Ext::DefaultCpuDispatcher::getAffinityMasks(PxU32* affinityMasks, PxU32 threadCount) +{ + for(PxU32 i=0; i < threadCount; i++) + { + affinityMasks[i] = 0; + } +} +#endif + +Ext::DefaultCpuDispatcher::DefaultCpuDispatcher(PxU32 numThreads, PxU32* affinityMasks) + : mQueueEntryPool(EXT_TASK_QUEUE_ENTRY_POOL_SIZE, "QueueEntryPool"), mNumThreads(numThreads), mShuttingDown(false) +#if PX_PROFILE + ,mRunProfiled(true) +#else + ,mRunProfiled(false) +#endif +{ + PxU32* defaultAffinityMasks = NULL; + + if(!affinityMasks) + { + defaultAffinityMasks = reinterpret_cast(PX_ALLOC(numThreads * sizeof(PxU32), "ThreadAffinityMasks")); + getAffinityMasks(defaultAffinityMasks, numThreads); + affinityMasks = defaultAffinityMasks; + } + + // initialize threads first, then start + + mWorkerThreads = reinterpret_cast(PX_ALLOC(numThreads * sizeof(CpuWorkerThread), "CpuWorkerThread")); + const PxU32 nameLength = 32; + mThreadNames = reinterpret_cast(PX_ALLOC(nameLength * numThreads, "CpuWorkerThreadName")); + + if (mWorkerThreads) + { + for(PxU32 i = 0; i < numThreads; ++i) + { + PX_PLACEMENT_NEW(mWorkerThreads+i, CpuWorkerThread)(); + mWorkerThreads[i].initialize(this); + } + + for(PxU32 i = 0; i < numThreads; ++i) + { + if (mThreadNames) + { + char* threadName = reinterpret_cast(mThreadNames + (i*nameLength)); + Ps::snprintf(threadName, nameLength, "PxWorker%02d", i); + mWorkerThreads[i].setName(threadName); + } + + mWorkerThreads[i].setAffinityMask(affinityMasks[i]); + mWorkerThreads[i].start(Ps::Thread::getDefaultStackSize()); + } + + if (defaultAffinityMasks) + PX_FREE(defaultAffinityMasks); + } + else + { + mNumThreads = 0; + } +} + +Ext::DefaultCpuDispatcher::~DefaultCpuDispatcher() +{ + for(PxU32 i = 0; i < mNumThreads; ++i) + mWorkerThreads[i].signalQuit(); + + mShuttingDown = true; + mWorkReady.set(); + for(PxU32 i = 0; i < mNumThreads; ++i) + mWorkerThreads[i].waitForQuit(); + + for(PxU32 i = 0; i < mNumThreads; ++i) + mWorkerThreads[i].~CpuWorkerThread(); + + PX_FREE(mWorkerThreads); + + if (mThreadNames) + PX_FREE(mThreadNames); +} + +void Ext::DefaultCpuDispatcher::submitTask(PxBaseTask& task) +{ + if(!mNumThreads) + { + // no worker threads, run directly + runTask(task); + task.release(); + return; + } + + // TODO: Could use TLS to make this more efficient + const Ps::Thread::Id currentThread = Ps::Thread::getId(); + for(PxU32 i = 0; i < mNumThreads; ++i) + { + if(mWorkerThreads[i].tryAcceptJobToLocalQueue(task, currentThread)) + return mWorkReady.set(); + } + + SharedQueueEntry* entry = mQueueEntryPool.getEntry(&task); + if (entry) + { + mJobList.push(*entry); + mWorkReady.set(); + } +} + +PxBaseTask* Ext::DefaultCpuDispatcher::fetchNextTask() +{ + PxBaseTask* task = getJob(); + + if(!task) + task = stealJob(); + + return task; +} + +void Ext::DefaultCpuDispatcher::release() +{ + PX_DELETE(this); +} + +PxBaseTask* Ext::DefaultCpuDispatcher::getJob(void) +{ + return TaskQueueHelper::fetchTask(mJobList, mQueueEntryPool); +} + +PxBaseTask* Ext::DefaultCpuDispatcher::stealJob() +{ + PxBaseTask* ret = NULL; + + for(PxU32 i = 0; i < mNumThreads; ++i) + { + ret = mWorkerThreads[i].giveUpJob(); + + if(ret != NULL) + break; + } + + return ret; +} + +void Ext::DefaultCpuDispatcher::resetWakeSignal() +{ + mWorkReady.reset(); + + // The code below is necessary to avoid deadlocks on shut down. + // A thread usually loops as follows: + // while quit is not signaled + // 1) reset wake signal + // 2) fetch work + // 3) if work -> process + // 4) else -> wait for wake signal + // + // If a thread reaches 1) after the thread pool signaled wake up, + // the wake up sync gets reset and all other threads which have not + // passed 4) already will wait forever. + // The code below makes sure that on shutdown, the wake up signal gets + // sent again after it was reset + // + if (mShuttingDown) + mWorkReady.set(); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h new file mode 100644 index 000000000..fce239635 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h @@ -0,0 +1,122 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_EXTENSIONS_NP_DEFAULT_CPU_DISPATCHER_H +#define PX_PHYSICS_EXTENSIONS_NP_DEFAULT_CPU_DISPATCHER_H + +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PsSync.h" +#include "PsSList.h" +#include "PxDefaultCpuDispatcher.h" +#include "ExtSharedQueueEntryPool.h" +#include "task/PxTask.h" +#include "common/PxProfileZone.h" + +namespace physx +{ + +namespace Ext +{ + class CpuWorkerThread; + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif // Because of the SList member I assume + + class DefaultCpuDispatcher : public PxDefaultCpuDispatcher, public Ps::UserAllocated + { + friend class TaskQueueHelper; + + private: + DefaultCpuDispatcher() : mQueueEntryPool(0) {} + ~DefaultCpuDispatcher(); + public: + DefaultCpuDispatcher(PxU32 numThreads, PxU32* affinityMasks); + + //--------------------------------------------------------------------------------- + // PxCpuDispatcher implementation + //--------------------------------------------------------------------------------- + virtual void submitTask(PxBaseTask& task); + virtual PxU32 getWorkerCount() const { return mNumThreads; } + + //--------------------------------------------------------------------------------- + // PxDefaultCpuDispatcher implementation + //--------------------------------------------------------------------------------- + virtual void release(); + + virtual void setRunProfiled(bool runProfiled) { mRunProfiled = runProfiled; } + + virtual bool getRunProfiled() const { return mRunProfiled; } + + //--------------------------------------------------------------------------------- + // DefaultCpuDispatcher + //--------------------------------------------------------------------------------- + PxBaseTask* getJob(); + PxBaseTask* stealJob(); + PxBaseTask* fetchNextTask(); + + PX_FORCE_INLINE void runTask(PxBaseTask& task) + { +#if PX_SUPPORT_PXTASK_PROFILING + if(mRunProfiled) + { + PX_PROFILE_ZONE(task.getName(), task.getContextId()); + task.run(); + } + else +#endif + task.run(); + } + + void waitForWork() { mWorkReady.wait(); } + void resetWakeSignal(); + + static void getAffinityMasks(PxU32* affinityMasks, PxU32 threadCount); + + protected: + CpuWorkerThread* mWorkerThreads; + SharedQueueEntryPool<> mQueueEntryPool; + Ps::SList mJobList; + Ps::Sync mWorkReady; + PxU8* mThreadNames; + PxU32 mNumThreads; + bool mShuttingDown; + bool mRunProfiled; + }; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Ext +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp new file mode 100644 index 000000000..d938a28b0 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp @@ -0,0 +1,104 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" +#include "PxDefaultErrorCallback.h" +#include "PsString.h" +#include "PsThread.h" +#include + +using namespace physx; + + +PxDefaultErrorCallback::PxDefaultErrorCallback() +{ +} + +PxDefaultErrorCallback::~PxDefaultErrorCallback() +{ +} + +void PxDefaultErrorCallback::reportError(PxErrorCode::Enum e, const char* message, const char* file, int line) +{ + const char* errorCode = NULL; + + switch (e) + { + case PxErrorCode::eNO_ERROR: + errorCode = "no error"; + break; + case PxErrorCode::eINVALID_PARAMETER: + errorCode = "invalid parameter"; + break; + case PxErrorCode::eINVALID_OPERATION: + errorCode = "invalid operation"; + break; + case PxErrorCode::eOUT_OF_MEMORY: + errorCode = "out of memory"; + break; + case PxErrorCode::eDEBUG_INFO: + errorCode = "info"; + break; + case PxErrorCode::eDEBUG_WARNING: + errorCode = "warning"; + break; + case PxErrorCode::ePERF_WARNING: + errorCode = "performance warning"; + break; + case PxErrorCode::eABORT: + errorCode = "abort"; + break; + case PxErrorCode::eINTERNAL_ERROR: + errorCode = "internal error"; + break; + case PxErrorCode::eMASK_ALL: + errorCode = "unknown error"; + break; + } + + PX_ASSERT(errorCode); + if(errorCode) + { + char buffer[1024]; + sprintf(buffer, "%s (%d) : %s : %s\n", file, line, errorCode, message); + + physx::shdfnd::printString(buffer); + + // in debug builds halt execution for abort codes + PX_ASSERT(e != PxErrorCode::eABORT); + + // in release builds we also want to halt execution + // and make sure that the error message is flushed + while (e == PxErrorCode::eABORT) + { + physx::shdfnd::printString(buffer); + physx::shdfnd::Thread::sleep(1000); + } + } +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp new file mode 100644 index 000000000..16e28fba5 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp @@ -0,0 +1,336 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxDefaultSimulationFilterShader.h" +#include "PsIntrinsics.h" +#include "PxRigidActor.h" +#include "PxShape.h" +#include "PsAllocator.h" +#include "CmPhysXCommon.h" +#include "PsInlineArray.h" +#include "PsFoundation.h" + +using namespace physx; + +namespace +{ + #define GROUP_SIZE 32 + + struct PxCollisionBitMap + { + PX_INLINE PxCollisionBitMap() : enable(true) {} + + bool operator()() const { return enable; } + bool& operator= (const bool &v) { enable = v; return enable; } + + private: + bool enable; + }; + + PxCollisionBitMap gCollisionTable[GROUP_SIZE][GROUP_SIZE]; + + PxFilterOp::Enum gFilterOps[3] = { PxFilterOp::PX_FILTEROP_AND, PxFilterOp::PX_FILTEROP_AND, PxFilterOp::PX_FILTEROP_AND }; + + PxGroupsMask gFilterConstants[2]; + + bool gFilterBool = false; + + static void gAND(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(mask0.bits0 & mask1.bits0); + results.bits1 = PxU16(mask0.bits1 & mask1.bits1); + results.bits2 = PxU16(mask0.bits2 & mask1.bits2); + results.bits3 = PxU16(mask0.bits3 & mask1.bits3); + } + static void gOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(mask0.bits0 | mask1.bits0); + results.bits1 = PxU16(mask0.bits1 | mask1.bits1); + results.bits2 = PxU16(mask0.bits2 | mask1.bits2); + results.bits3 = PxU16(mask0.bits3 | mask1.bits3); + } + static void gXOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(mask0.bits0 ^ mask1.bits0); + results.bits1 = PxU16(mask0.bits1 ^ mask1.bits1); + results.bits2 = PxU16(mask0.bits2 ^ mask1.bits2); + results.bits3 = PxU16(mask0.bits3 ^ mask1.bits3); + } + static void gNAND(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(~(mask0.bits0 & mask1.bits0)); + results.bits1 = PxU16(~(mask0.bits1 & mask1.bits1)); + results.bits2 = PxU16(~(mask0.bits2 & mask1.bits2)); + results.bits3 = PxU16(~(mask0.bits3 & mask1.bits3)); + } + static void gNOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(~(mask0.bits0 | mask1.bits0)); + results.bits1 = PxU16(~(mask0.bits1 | mask1.bits1)); + results.bits2 = PxU16(~(mask0.bits2 | mask1.bits2)); + results.bits3 = PxU16(~(mask0.bits3 | mask1.bits3)); + } + static void gNXOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(~(mask0.bits0 ^ mask1.bits0)); + results.bits1 = PxU16(~(mask0.bits1 ^ mask1.bits1)); + results.bits2 = PxU16(~(mask0.bits2 ^ mask1.bits2)); + results.bits3 = PxU16(~(mask0.bits3 ^ mask1.bits3)); + } + + static void gSWAP_AND(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(mask0.bits0 & mask1.bits2); + results.bits1 = PxU16(mask0.bits1 & mask1.bits3); + results.bits2 = PxU16(mask0.bits2 & mask1.bits0); + results.bits3 = PxU16(mask0.bits3 & mask1.bits1); + } + + typedef void (*FilterFunction) (PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1); + + FilterFunction const gTable[] = { gAND, gOR, gXOR, gNAND, gNOR, gNXOR, gSWAP_AND }; + + static PxFilterData convert(const PxGroupsMask& mask) + { + PxFilterData fd; + + fd.word2 = PxU32(mask.bits0 | (mask.bits1 << 16)); + fd.word3 = PxU32(mask.bits2 | (mask.bits3 << 16)); + + return fd; + } + + static PxGroupsMask convert(const PxFilterData& fd) + { + PxGroupsMask mask; + + mask.bits0 = PxU16((fd.word2 & 0xffff)); + mask.bits1 = PxU16((fd.word2 >> 16)); + mask.bits2 = PxU16((fd.word3 & 0xffff)); + mask.bits3 = PxU16((fd.word3 >> 16)); + + return mask; + } + + static bool getFilterData(const PxActor& actor, PxFilterData& fd) + { + PxActorType::Enum aType = actor.getType(); + switch (aType) + { + case PxActorType::eRIGID_DYNAMIC: + case PxActorType::eRIGID_STATIC: + case PxActorType::eARTICULATION_LINK: + { + const PxRigidActor& rActor = static_cast(actor); + PX_CHECK_AND_RETURN_VAL(rActor.getNbShapes() >= 1,"There must be a shape in actor", false); + + PxShape* shape = NULL; + rActor.getShapes(&shape, 1); + + fd = shape->getSimulationFilterData(); + } + break; + + case PxActorType::eACTOR_COUNT: + case PxActorType::eACTOR_FORCE_DWORD: + break; + } + + return true; + } + + PX_FORCE_INLINE static void adjustFilterData(bool groupsMask, const PxFilterData& src, PxFilterData& dst) + { + if (groupsMask) + { + dst.word2 = src.word2; + dst.word3 = src.word3; + } + else + dst.word0 = src.word0; + } + + template + static void setFilterData(PxActor& actor, const PxFilterData& fd) + { + PxActorType::Enum aType = actor.getType(); + switch (aType) + { + case PxActorType::eRIGID_DYNAMIC: + case PxActorType::eRIGID_STATIC: + case PxActorType::eARTICULATION_LINK: + { + const PxRigidActor& rActor = static_cast(actor); + + PxShape* shape; + for(PxU32 i=0; i < rActor.getNbShapes(); i++) + { + rActor.getShapes(&shape, 1, i); + + // retrieve current group mask + PxFilterData resultFd = shape->getSimulationFilterData(); + + adjustFilterData(TGroupsMask, fd, resultFd); + + // set new filter data + shape->setSimulationFilterData(resultFd); + } + } + break; + case PxActorType::eACTOR_COUNT: + case PxActorType::eACTOR_FORCE_DWORD: + break; + } + } +} + +PxFilterFlags physx::PxDefaultSimulationFilterShader( + PxFilterObjectAttributes attributes0, + PxFilterData filterData0, + PxFilterObjectAttributes attributes1, + PxFilterData filterData1, + PxPairFlags& pairFlags, + const void* constantBlock, + PxU32 constantBlockSize) +{ + PX_UNUSED(constantBlock); + PX_UNUSED(constantBlockSize); + + // let triggers through + if(PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1)) + { + pairFlags = PxPairFlag::eTRIGGER_DEFAULT; + return PxFilterFlags(); + } + + // Collision Group + if (!gCollisionTable[filterData0.word0][filterData1.word0]()) + { + return PxFilterFlag::eSUPPRESS; + } + + // Filter function + PxGroupsMask g0 = convert(filterData0); + PxGroupsMask g1 = convert(filterData1); + + PxGroupsMask g0k0; gTable[gFilterOps[0]](g0k0, g0, gFilterConstants[0]); + PxGroupsMask g1k1; gTable[gFilterOps[1]](g1k1, g1, gFilterConstants[1]); + PxGroupsMask final; gTable[gFilterOps[2]](final, g0k0, g1k1); + + bool r = final.bits0 || final.bits1 || final.bits2 || final.bits3; + if (r != gFilterBool) + { + return PxFilterFlag::eSUPPRESS; + } + + pairFlags = PxPairFlag::eCONTACT_DEFAULT; + + return PxFilterFlags(); +} + +bool physx::PxGetGroupCollisionFlag(const PxU16 group1, const PxU16 group2) +{ + PX_CHECK_AND_RETURN_NULL(group1 < 32 && group2 < 32, "Group must be less than 32"); + + return gCollisionTable[group1][group2](); +} + +void physx::PxSetGroupCollisionFlag(const PxU16 group1, const PxU16 group2, const bool enable) +{ + PX_CHECK_AND_RETURN(group1 < 32 && group2 < 32, "Group must be less than 32"); + + gCollisionTable[group1][group2] = enable; + gCollisionTable[group2][group1] = enable; +} + +PxU16 physx::PxGetGroup(const PxActor& actor) +{ + PxFilterData fd; + getFilterData(actor, fd); + return PxU16(fd.word0); +} + +void physx::PxSetGroup(PxActor& actor, const PxU16 collisionGroup) +{ + PX_CHECK_AND_RETURN(collisionGroup < 32,"Collision group must be less than 32"); + PxFilterData fd(collisionGroup, 0, 0, 0); + setFilterData(actor, fd); +} + +void physx::PxGetFilterOps(PxFilterOp::Enum& op0, PxFilterOp::Enum& op1, PxFilterOp::Enum& op2) +{ + op0 = gFilterOps[0]; + op1 = gFilterOps[1]; + op2 = gFilterOps[2]; +} + +void physx::PxSetFilterOps(const PxFilterOp::Enum& op0, const PxFilterOp::Enum& op1, const PxFilterOp::Enum& op2) +{ + gFilterOps[0] = op0; + gFilterOps[1] = op1; + gFilterOps[2] = op2; +} + +bool physx::PxGetFilterBool() +{ + return gFilterBool; +} + +void physx::PxSetFilterBool(const bool enable) +{ + gFilterBool = enable; +} + +void physx::PxGetFilterConstants(PxGroupsMask& c0, PxGroupsMask& c1) +{ + c0 = gFilterConstants[0]; + c1 = gFilterConstants[1]; +} + +void physx::PxSetFilterConstants(const PxGroupsMask& c0, const PxGroupsMask& c1) +{ + gFilterConstants[0] = c0; + gFilterConstants[1] = c1; +} + +PxGroupsMask physx::PxGetGroupsMask(const PxActor& actor) +{ + PxFilterData fd; + if (getFilterData(actor, fd)) + return convert(fd); + else + return PxGroupsMask(); +} + +void physx::PxSetGroupsMask(PxActor& actor, const PxGroupsMask& mask) +{ + PxFilterData fd = convert(mask); + setFilterData(actor, fd); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp new file mode 100644 index 000000000..44e5fb9c9 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp @@ -0,0 +1,194 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxAssert.h" +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxMemory.h" + +#include +#include "PsFoundation.h" +#include "PxDefaultStreams.h" +#include "SnFile.h" +#include "CmPhysXCommon.h" +#include "PsUtilities.h" +#include "PsBitUtils.h" + +using namespace physx; + +PxDefaultMemoryOutputStream::PxDefaultMemoryOutputStream(PxAllocatorCallback &allocator) +: mAllocator (allocator) +, mData (NULL) +, mSize (0) +, mCapacity (0) +{ +} + +PxDefaultMemoryOutputStream::~PxDefaultMemoryOutputStream() +{ + if(mData) + mAllocator.deallocate(mData); +} + +PxU32 PxDefaultMemoryOutputStream::write(const void* src, PxU32 size) +{ + PxU32 expectedSize = mSize + size; + if(expectedSize > mCapacity) + { + mCapacity = PxMax(Ps::nextPowerOfTwo(expectedSize), 4096u); + + PxU8* newData = reinterpret_cast(mAllocator.allocate(mCapacity,"PxDefaultMemoryOutputStream",__FILE__,__LINE__)); + PX_ASSERT(newData!=NULL); + + PxMemCopy(newData, mData, mSize); + if(mData) + mAllocator.deallocate(mData); + + mData = newData; + } + PxMemCopy(mData+mSize, src, size); + mSize += size; + return size; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxDefaultMemoryInputData::PxDefaultMemoryInputData(PxU8* data, PxU32 length) : + mSize (length), + mData (data), + mPos (0) +{ +} + +PxU32 PxDefaultMemoryInputData::read(void* dest, PxU32 count) +{ + PxU32 length = PxMin(count, mSize-mPos); + PxMemCopy(dest, mData+mPos, length); + mPos += length; + return length; +} + +PxU32 PxDefaultMemoryInputData::getLength() const +{ + return mSize; +} + +void PxDefaultMemoryInputData::seek(PxU32 offset) +{ + mPos = PxMin(mSize, offset); +} + +PxU32 PxDefaultMemoryInputData::tell() const +{ + return mPos; +} + +PxDefaultFileOutputStream::PxDefaultFileOutputStream(const char* filename) +{ + mFile = NULL; + sn::fopen_s(&mFile, filename, "wb"); + // PT: when this fails, check that: + // - the path is correct + // - the file does not already exist. If it does, check that it is not write protected. + if(NULL == mFile) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Unable to open file %s, errno 0x%x\n",filename,errno); + } + PX_ASSERT(mFile); +} + +PxDefaultFileOutputStream::~PxDefaultFileOutputStream() +{ + if(mFile) + fclose(mFile); +} + +PxU32 PxDefaultFileOutputStream::write(const void* src, PxU32 count) +{ + return mFile ? PxU32(fwrite(src, 1, count, mFile)) : 0; +} + +bool PxDefaultFileOutputStream::isValid() +{ + return mFile != NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxDefaultFileInputData::PxDefaultFileInputData(const char* filename) +{ + mFile = NULL; + sn::fopen_s(&mFile, filename, "rb"); + + if(mFile) + { + fseek(mFile, 0, SEEK_END); + mLength = PxU32(ftell(mFile)); + fseek(mFile, 0, SEEK_SET); + } + else + { + mLength = 0; + } +} + +PxDefaultFileInputData::~PxDefaultFileInputData() +{ + if(mFile) + fclose(mFile); +} + +PxU32 PxDefaultFileInputData::read(void* dest, PxU32 count) +{ + PX_ASSERT(mFile); + const size_t size = fread(dest, 1, count, mFile); + // there should be no assert here since by spec of PxInputStream we can read fewer bytes than expected + return PxU32(size); +} + +PxU32 PxDefaultFileInputData::getLength() const +{ + return mLength; +} + +void PxDefaultFileInputData::seek(PxU32 pos) +{ + fseek(mFile, long(pos), SEEK_SET); +} + +PxU32 PxDefaultFileInputData::tell() const +{ + return PxU32(ftell(mFile)); +} + +bool PxDefaultFileInputData::isValid() const +{ + return mFile != NULL; +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp new file mode 100644 index 000000000..33e08fe1e --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp @@ -0,0 +1,334 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtDistanceJoint.h" +#include "ExtConstraintHelper.h" +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxDistanceJoint* physx::PxDistanceJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxDistanceJointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxDistanceJointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxDistanceJointCreate: actors must be different"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxD6JointCreate: at least one actor must be dynamic"); + + DistanceJoint* j; + PX_NEW_SERIALIZED(j, DistanceJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +PxReal DistanceJoint::getDistance() const +{ + return getRelativeTransform().p.magnitude(); +} + +void DistanceJoint::setMinDistance(PxReal distance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(distance), "PxDistanceJoint::setMinDistance: invalid parameter"); + data().minDistance = distance; + markDirty(); +} + +PxReal DistanceJoint::getMinDistance() const +{ + return data().minDistance; +} + +void DistanceJoint::setMaxDistance(PxReal distance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(distance), "PxDistanceJoint::setMaxDistance: invalid parameter"); + data().maxDistance = distance; + markDirty(); +} + +PxReal DistanceJoint::getMaxDistance() const +{ + return data().maxDistance; +} + +void DistanceJoint::setTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance), "PxDistanceJoint::setTolerance: invalid parameter"); + data().tolerance = tolerance; + markDirty(); +} + +PxReal DistanceJoint::getTolerance() const +{ + return data().tolerance; +} + +void DistanceJoint::setStiffness(PxReal stiffness) +{ + PX_CHECK_AND_RETURN(PxIsFinite(stiffness), "PxDistanceJoint::setStiffness: invalid parameter"); + data().stiffness = stiffness; + markDirty(); +} + +PxReal DistanceJoint::getStiffness() const +{ + return data().stiffness; +} + +void DistanceJoint::setDamping(PxReal damping) +{ + PX_CHECK_AND_RETURN(PxIsFinite(damping), "PxDistanceJoint::setDamping: invalid parameter"); + data().damping = damping; + markDirty(); +} + +PxReal DistanceJoint::getDamping() const +{ + return data().damping; +} + +PxDistanceJointFlags DistanceJoint::getDistanceJointFlags(void) const +{ + return data().jointFlags; +} + +void DistanceJoint::setDistanceJointFlags(PxDistanceJointFlags flags) +{ + data().jointFlags = flags; + markDirty(); +} + +void DistanceJoint::setDistanceJointFlag(PxDistanceJointFlag::Enum flag, bool value) +{ + if(value) + data().jointFlags |= flag; + else + data().jointFlags &= ~flag; + markDirty(); +} + +bool DistanceJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(DistanceJointData)); + return mPxConstraint!=NULL; +} + +void DistanceJoint::exportExtraData(PxSerializationContext& stream) +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(DistanceJointData)); + } + stream.writeName(mName); +} + +void DistanceJoint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + + context.readName(mName); +} + +void DistanceJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +DistanceJoint* DistanceJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + DistanceJoint* obj = new (address) DistanceJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(DistanceJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetDistanceJointShaderTable() +{ + return &DistanceJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void DistanceJointProject(const void* /*constantBlock*/, PxTransform& /*bodyAToWorld*/, PxTransform& /*bodyBToWorld*/, bool /*projectToA*/) +{ + // TODO +} + +static void DistanceJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags) +{ + const DistanceJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + viz.visualizeJointFrames(cA2w, cB2w); + + // PT: we consider the following is part of the joint's "limits" since that's the only available flag we have + if(flags & PxConstraintVisualizationFlag::eLIMITS) + { + const bool enforceMax = (data.jointFlags & PxDistanceJointFlag::eMAX_DISTANCE_ENABLED); + const bool enforceMin = (data.jointFlags & PxDistanceJointFlag::eMIN_DISTANCE_ENABLED); + if(!enforceMin && !enforceMax) + return; + + PxVec3 dir = cB2w.p - cA2w.p; + const float currentDist = dir.normalize(); + + PxU32 color = 0x00ff00; + if(enforceMax && currentDist>data.maxDistance) + color = 0xff0000; + if(enforceMin && currentDist(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + cA2wOut = cB2w.p; + cB2wOut = cB2w.p; + + PxVec3 direction = cA2w.p - cB2w.p; + const PxReal distance = direction.normalize(); + + const bool enforceMax = (data.jointFlags & PxDistanceJointFlag::eMAX_DISTANCE_ENABLED); + const bool enforceMin = (data.jointFlags & PxDistanceJointFlag::eMIN_DISTANCE_ENABLED); + +#define EPS_REAL 1.192092896e-07F + + if(distance < EPS_REAL) + direction = PxVec3(1.0f, 0.0f, 0.0f); + + Px1DConstraint* c = constraints; + + const PxVec3 angular0 = ch.getRa().cross(direction); + const PxVec3 angular1 = ch.getRb().cross(direction); + + setupContraint(*c, direction, angular0, angular1, data); + + //add tolerance so we don't have contact-style jitter problem. + + if(data.minDistance == data.maxDistance && enforceMin && enforceMax) + { + const PxReal error = distance - data.maxDistance; + c->geometricError = error > data.tolerance ? error - data.tolerance : + error < -data.tolerance ? error + data.tolerance : 0.0f; + } + else if(enforceMax && distance > data.maxDistance) + { + c->geometricError = distance - data.maxDistance - data.tolerance; + c->maxImpulse = 0.0f; + } + else if(enforceMin && distance < data.minDistance) + { + c->geometricError = distance - data.minDistance + data.tolerance; + c->minImpulse = 0.0f; + } + else + { + if(enforceMin && enforceMax) + { + // since we dont know the current rigid velocity, we need to insert row for both limits + Px1DConstraint* minConstraint = constraints; + minConstraint->geometricError = distance - data.minDistance; + minConstraint->minImpulse = 0.0f; + minConstraint->maxImpulse = FLT_MAX; + minConstraint->flags |= Px1DConstraintFlag::eKEEPBIAS; + + Px1DConstraint* maxConstraint = constraints; + maxConstraint++; + + setupContraint(*maxConstraint, direction, angular0, angular1, data); + + maxConstraint->geometricError = distance - data.maxDistance; + maxConstraint->minImpulse = -FLT_MAX; + maxConstraint->maxImpulse = 0.0f; + maxConstraint->flags |= Px1DConstraintFlag::eKEEPBIAS; + + return 2; + } + else if(enforceMax) + { + c->geometricError = distance - data.maxDistance; + c->minImpulse = -FLT_MAX; + c->maxImpulse = 0.0f; + c->flags |= Px1DConstraintFlag::eKEEPBIAS; + return 0; + } + else if(enforceMin) + { + c->geometricError = distance - data.minDistance; + c->minImpulse = 0.0f; + c->maxImpulse = FLT_MAX; + c->flags |= Px1DConstraintFlag::eKEEPBIAS; + return 0; + } + } + + return 1; +} + +PxConstraintShaderTable Ext::DistanceJoint::sShaders = { DistanceJointSolverPrep, DistanceJointProject, DistanceJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h new file mode 100644 index 000000000..09c136434 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h @@ -0,0 +1,139 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef NP_DISTANCEJOINTCONSTRAINT_H +#define NP_DISTANCEJOINTCONSTRAINT_H + +#include "PsUserAllocated.h" +#include "ExtJoint.h" +#include "PxDistanceJoint.h" +#include "PxTolerancesScale.h" +#include "CmUtils.h" + +namespace physx +{ +struct PxDistanceJointGeneratedValues; +namespace Ext +{ + + struct DistanceJointData : public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxReal minDistance; + PxReal maxDistance; + PxReal tolerance; + PxReal stiffness; + PxReal damping; + + PxDistanceJointFlags jointFlags; + }; + + typedef Joint DistanceJointT; + class DistanceJoint : public DistanceJointT + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + // PX_SERIALIZATION + DistanceJoint(PxBaseFlags baseFlags) : DistanceJointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static DistanceJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + DistanceJoint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + DistanceJointT(PxJointConcreteType::eDISTANCE, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(DistanceJointData), "DistanceJointData") + { + DistanceJointData* data = static_cast(mData); + + data->stiffness = 0.0f; + data->damping = 0.0f; + data->minDistance = 0.0f; + data->maxDistance = 0.0f; + data->tolerance = 0.025f * scale.length; + data->jointFlags = PxDistanceJointFlag::eMAX_DISTANCE_ENABLED; + } + + // PxDistanceJoint + virtual PxReal getDistance() const; + virtual void setMinDistance(PxReal distance); + virtual PxReal getMinDistance() const; + virtual void setMaxDistance(PxReal distance); + virtual PxReal getMaxDistance() const; + virtual void setTolerance(PxReal tolerance); + virtual PxReal getTolerance() const; + virtual void setStiffness(PxReal spring); + virtual PxReal getStiffness() const; + virtual void setDamping(PxReal damping); + virtual PxReal getDamping() const; + virtual void setDistanceJointFlags(PxDistanceJointFlags flags); + virtual void setDistanceJointFlag(PxDistanceJointFlag::Enum flag, bool value); + virtual PxDistanceJointFlags getDistanceJointFlags() const; + //~PxDistanceJoint + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep()const {return sShaders.solverPrep;} + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE DistanceJointData& data() const + { + return *static_cast(mData); + } + }; + +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetDistanceJointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp b/src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp new file mode 100644 index 000000000..05cd12411 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp @@ -0,0 +1,193 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "PxExtensionsAPI.h" +#include "PsFoundation.h" +#include "PxMetaData.h" +#include "ExtDistanceJoint.h" +#include "ExtD6Joint.h" +#include "ExtFixedJoint.h" +#include "ExtPrismaticJoint.h" +#include "ExtRevoluteJoint.h" +#include "ExtSphericalJoint.h" +#include "PxRepXSerializer.h" +#include "SnRepXCoreSerializer.h" +#include "SnJointRepXSerializer.h" +#include "PxExtensionMetaDataObjects.h" +#include "PxSerializer.h" +#include "ExtSerialization.h" + +#if PX_SUPPORT_PVD +#include "ExtPvd.h" +#include "PxPvdDataStream.h" +#include "PxPvdClient.h" +#include "PsPvd.h" +#endif + +using namespace physx; +using namespace physx::pvdsdk; + +#if PX_SUPPORT_PVD +struct JointConnectionHandler : public PvdClient +{ + JointConnectionHandler() : mPvd(NULL),mConnected(false){} + + PvdDataStream* getDataStream() + { + return NULL; + } + PvdUserRenderer* getUserRender() + { + return NULL; + } + + void onPvdConnected() + { + PvdDataStream* stream = PvdDataStream::create(mPvd); + if(stream) + { + mConnected = true; + Ext::Pvd::sendClassDescriptions(*stream); + stream->release(); + } + } + + bool isConnected() const + { + return mConnected; + } + + void onPvdDisconnected() + { + mConnected = false; + } + + void flush() + { + } + + PsPvd* mPvd; + bool mConnected; +}; + +static JointConnectionHandler gPvdHandler; +#endif + +bool PxInitExtensions(PxPhysics& physics, PxPvd* pvd) +{ + PX_ASSERT(static_cast(&physics.getFoundation()) == &Ps::Foundation::getInstance()); + PX_UNUSED(physics); + PX_UNUSED(pvd); + Ps::Foundation::incRefCount(); + +#if PX_SUPPORT_PVD + if(pvd) + { + gPvdHandler.mPvd = static_cast(pvd); + gPvdHandler.mPvd->addClient(&gPvdHandler); + } +#endif + + return true; +} + +void PxCloseExtensions(void) +{ + Ps::Foundation::decRefCount(); + +#if PX_SUPPORT_PVD + if(gPvdHandler.mConnected) + { + PX_ASSERT(gPvdHandler.mPvd); + gPvdHandler.mPvd->removeClient(&gPvdHandler); + gPvdHandler.mPvd = NULL; + } +#endif +} + +void Ext::RegisterExtensionsSerializers(PxSerializationRegistry& sr) +{ + //for repx serialization + sr.registerRepXSerializer(PxConcreteType::eMATERIAL, PX_NEW_REPX_SERIALIZER( PxMaterialRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eSHAPE, PX_NEW_REPX_SERIALIZER( PxShapeRepXSerializer )); +// sr.registerRepXSerializer(PxConcreteType::eTRIANGLE_MESH, PX_NEW_REPX_SERIALIZER( PxTriangleMeshRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH33, PX_NEW_REPX_SERIALIZER( PxBVH33TriangleMeshRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH34, PX_NEW_REPX_SERIALIZER( PxBVH34TriangleMeshRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eHEIGHTFIELD, PX_NEW_REPX_SERIALIZER( PxHeightFieldRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eCONVEX_MESH, PX_NEW_REPX_SERIALIZER( PxConvexMeshRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eRIGID_STATIC, PX_NEW_REPX_SERIALIZER( PxRigidStaticRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eRIGID_DYNAMIC, PX_NEW_REPX_SERIALIZER( PxRigidDynamicRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eARTICULATION, PX_NEW_REPX_SERIALIZER( PxArticulationRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eAGGREGATE, PX_NEW_REPX_SERIALIZER( PxAggregateRepXSerializer )); + + sr.registerRepXSerializer(PxJointConcreteType::eFIXED, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + sr.registerRepXSerializer(PxJointConcreteType::eDISTANCE, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + sr.registerRepXSerializer(PxJointConcreteType::eD6, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + sr.registerRepXSerializer(PxJointConcreteType::ePRISMATIC, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + sr.registerRepXSerializer(PxJointConcreteType::eREVOLUTE, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + sr.registerRepXSerializer(PxJointConcreteType::eSPHERICAL, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + + //for binary serialization + sr.registerSerializer(PxJointConcreteType::eFIXED, PX_NEW_SERIALIZER_ADAPTER( FixedJoint )); + sr.registerSerializer(PxJointConcreteType::eDISTANCE, PX_NEW_SERIALIZER_ADAPTER( DistanceJoint )); + sr.registerSerializer(PxJointConcreteType::eD6, PX_NEW_SERIALIZER_ADAPTER( D6Joint) ); + sr.registerSerializer(PxJointConcreteType::ePRISMATIC, PX_NEW_SERIALIZER_ADAPTER( PrismaticJoint )); + sr.registerSerializer(PxJointConcreteType::eREVOLUTE, PX_NEW_SERIALIZER_ADAPTER( RevoluteJoint )); + sr.registerSerializer(PxJointConcreteType::eSPHERICAL, PX_NEW_SERIALIZER_ADAPTER( SphericalJoint )); +} + +void Ext::UnregisterExtensionsSerializers(PxSerializationRegistry& sr) +{ + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eFIXED)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eDISTANCE)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eD6 )); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::ePRISMATIC)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eREVOLUTE)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eSPHERICAL)); + + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eMATERIAL)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eSHAPE)); +// PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eTRIANGLE_MESH)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH33)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH34)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eHEIGHTFIELD)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eCONVEX_MESH)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eRIGID_STATIC)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eRIGID_DYNAMIC)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eARTICULATION)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eAGGREGATE)); + + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eFIXED)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eDISTANCE)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eD6)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::ePRISMATIC)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eREVOLUTE)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eSPHERICAL)); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp new file mode 100644 index 000000000..ab9538df9 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtFixedJoint.h" +#include "ExtConstraintHelper.h" +#include "CmVisualization.h" + +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxFixedJoint* physx::PxFixedJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxFixedJointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxFixedJointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxFixedJointCreate: at least one actor must be dynamic"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxFixedJointCreate: actors must be different"); + + FixedJoint* j; + PX_NEW_SERIALIZED(j, FixedJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +PxReal FixedJoint::getProjectionLinearTolerance() const +{ + return data().projectionLinearTolerance; +} + +void FixedJoint::setProjectionLinearTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxFixedJoint::setProjectionLinearTolerance: invalid parameter"); + data().projectionLinearTolerance = tolerance; + markDirty(); +} + +PxReal FixedJoint::getProjectionAngularTolerance() const +{ + return data().projectionAngularTolerance; +} + +void FixedJoint::setProjectionAngularTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0 && tolerance <= PxPi, "PxFixedJoint::setProjectionAngularTolerance: invalid parameter"); + data().projectionAngularTolerance = tolerance; markDirty(); +} + +bool FixedJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(FixedJointData)); + return mPxConstraint!=NULL; +} + +void FixedJoint::exportExtraData(PxSerializationContext& stream) const +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(FixedJointData)); + } + stream.writeName(mName); +} + +void FixedJoint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + context.readName(mName); +} + +void FixedJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +FixedJoint* FixedJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + FixedJoint* obj = new (address) FixedJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(FixedJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetFixedJointShaderTable() +{ + return &FixedJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void FixedJointProject(const void* constantBlock, PxTransform& bodyAToWorld, PxTransform& bodyBToWorld, bool projectToA) +{ + const FixedJointData &data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w, cB2cA, projected; + joint::computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA); + + bool linearTrunc, angularTrunc; + projected.p = joint::truncateLinear(cB2cA.p, data.projectionLinearTolerance, linearTrunc); + projected.q = joint::truncateAngular(cB2cA.q, PxSin(data.projectionAngularTolerance/2), PxCos(data.projectionAngularTolerance/2), angularTrunc); + + if(linearTrunc || angularTrunc) + joint::projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA); +} + +static void FixedJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags) +{ + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + { + const FixedJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + viz.visualizeJointFrames(cA2w, cB2w); + } +} + +static PxU32 FixedJointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool /*useExtendedLimits*/, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const FixedJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + if (cA2w.q.dot(cB2w.q)<0.0f) // minimum dist quat (equiv to flipping cB2bB.q, which we don't use anywhere) + cB2w.q = -cB2w.q; + + PxVec3 ra, rb; + ch.prepareLockedAxes(cA2w.q, cB2w.q, cA2w.transformInv(cB2w.p), 7, 7, ra, rb); + cA2wOut = ra + bA2w.p; + cB2wOut = rb + bB2w.p; + + + return ch.getCount(); +} + +PxConstraintShaderTable Ext::FixedJoint::sShaders = { FixedJointSolverPrep, FixedJointProject, FixedJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h new file mode 100644 index 000000000..34b8e3397 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_FIXEDJOINTCONSTRAINT_H +#define NP_FIXEDJOINTCONSTRAINT_H + +#include "ExtJoint.h" +#include "PxFixedJoint.h" +#include "CmUtils.h" + +namespace physx +{ +struct PxFixedJointGeneratedValues; +namespace Ext +{ + + struct FixedJointData : public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxReal projectionLinearTolerance; + PxReal projectionAngularTolerance; + }; + + typedef Joint FixedJointT; + + class FixedJoint : public FixedJointT + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + FixedJoint(PxBaseFlags baseFlags) : FixedJointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context) const; + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static FixedJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + // PxFixedJoint + virtual void setProjectionLinearTolerance(PxReal tolerance); + virtual PxReal getProjectionLinearTolerance() const; + virtual void setProjectionAngularTolerance(PxReal tolerance); + virtual PxReal getProjectionAngularTolerance() const; + //~PxFixedJoint + + FixedJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + FixedJointT(PxJointConcreteType::eFIXED, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(FixedJointData), "FixedJointData") + { + FixedJointData* data = static_cast(mData); + + data->projectionLinearTolerance = 1e10f; + data->projectionAngularTolerance = PxPi; + } + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep() const { return sShaders.solverPrep; } + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE FixedJointData& data() const + { + return *static_cast(mData); + } + }; +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetFixedJointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h b/src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h new file mode 100644 index 000000000..dcef2415c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h @@ -0,0 +1,404 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_INERTIATENSOR_H +#define PX_PHYSICS_EXTENSIONS_INERTIATENSOR_H + +#include "foundation/PxMat33.h" +#include "foundation/PxMathUtils.h" +#include "CmPhysXCommon.h" +#include "PsMathUtils.h" + +namespace physx +{ +namespace Ext +{ + class InertiaTensorComputer + { + public: + InertiaTensorComputer(bool initTozero = true); + InertiaTensorComputer(const PxMat33& inertia, const PxVec3& com, PxReal mass); + ~InertiaTensorComputer(); + + PX_INLINE void zero(); //sets to zero mass + PX_INLINE void setDiagonal(PxReal mass, const PxVec3& diagonal); //sets as a diagonal tensor + PX_INLINE void rotate(const PxMat33& rot); //rotates the mass + void translate(const PxVec3& t); //translates the mass + PX_INLINE void transform(const PxTransform& transform); //transforms the mass + PX_INLINE void scaleDensity(PxReal densityScale); //scales by a density factor + PX_INLINE void add(const InertiaTensorComputer& it); //adds a mass + PX_INLINE void center(); //recenters inertia around center of mass + + void setBox(const PxVec3& halfWidths); //sets as an axis aligned box + PX_INLINE void setBox(const PxVec3& halfWidths, const PxTransform* pose); //sets as an oriented box + + void setSphere(PxReal radius); + PX_INLINE void setSphere(PxReal radius, const PxTransform* pose); + + void setCylinder(int dir, PxReal r, PxReal l); + PX_INLINE void setCylinder(int dir, PxReal r, PxReal l, const PxTransform* pose); + + void setCapsule(int dir, PxReal r, PxReal l); + PX_INLINE void setCapsule(int dir, PxReal r, PxReal l, const PxTransform* pose); + void addCapsule(PxReal density, int dir, PxReal r, PxReal l, const PxTransform* pose = 0); + + void setEllipsoid(PxReal rx, PxReal ry, PxReal rz); + PX_INLINE void setEllipsoid(PxReal rx, PxReal ry, PxReal rz, const PxTransform* pose); + + PX_INLINE PxVec3 getCenterOfMass() const { return mG; } + PX_INLINE PxReal getMass() const { return mMass; } + PX_INLINE PxMat33 getInertia() const { return mI; } + + private: + PxMat33 mI; + PxVec3 mG; + PxReal mMass; + }; + + + //-------------------------------------------------------------- + // + // Helper routines + // + //-------------------------------------------------------------- + + // Special version allowing 2D quads + PX_INLINE PxReal volume(const PxVec3& extents) + { + PxReal v = 1.0f; + if(extents.x != 0.0f) v*=extents.x; + if(extents.y != 0.0f) v*=extents.y; + if(extents.z != 0.0f) v*=extents.z; + return v; + } + + // Sphere + PX_INLINE PxReal computeSphereRatio(PxReal radius) { return (4.0f/3.0f) * PxPi * radius * radius * radius; } + PxReal computeSphereMass(PxReal radius, PxReal density) { return density * computeSphereRatio(radius); } + PxReal computeSphereDensity(PxReal radius, PxReal mass) { return mass / computeSphereRatio(radius); } + + // Box + PX_INLINE PxReal computeBoxRatio(const PxVec3& extents) { return volume(extents); } + PxReal computeBoxMass(const PxVec3& extents, PxReal density) { return density * computeBoxRatio(extents); } + PxReal computeBoxDensity(const PxVec3& extents, PxReal mass) { return mass / computeBoxRatio(extents); } + + // Ellipsoid + PX_INLINE PxReal computeEllipsoidRatio(const PxVec3& extents) { return (4.0f/3.0f) * PxPi * volume(extents); } + PxReal computeEllipsoidMass(const PxVec3& extents, PxReal density) { return density * computeEllipsoidRatio(extents); } + PxReal computeEllipsoidDensity(const PxVec3& extents, PxReal mass) { return mass / computeEllipsoidRatio(extents); } + + // Cylinder + PX_INLINE PxReal computeCylinderRatio(PxReal r, PxReal l) { return PxPi * r * r * (2.0f*l); } + PxReal computeCylinderMass(PxReal r, PxReal l, PxReal density) { return density * computeCylinderRatio(r, l); } + PxReal computeCylinderDensity(PxReal r, PxReal l, PxReal mass) { return mass / computeCylinderRatio(r, l); } + + // Capsule + PX_INLINE PxReal computeCapsuleRatio(PxReal r, PxReal l) { return computeSphereRatio(r) + computeCylinderRatio(r, l);} + PxReal computeCapsuleMass(PxReal r, PxReal l, PxReal density) { return density * computeCapsuleRatio(r, l); } + PxReal computeCapsuleDensity(PxReal r, PxReal l, PxReal mass) { return mass / computeCapsuleRatio(r, l); } + + // Cone + PX_INLINE PxReal computeConeRatio(PxReal r, PxReal l) { return PxPi * r * r * PxAbs(l)/3.0f; } + PxReal computeConeMass(PxReal r, PxReal l, PxReal density) { return density * computeConeRatio(r, l); } + PxReal computeConeDensity(PxReal r, PxReal l, PxReal mass) { return mass / computeConeRatio(r, l); } + + void computeBoxInertiaTensor(PxVec3& inertia, PxReal mass, PxReal xlength, PxReal ylength, PxReal zlength); + void computeSphereInertiaTensor(PxVec3& inertia, PxReal mass, PxReal radius, bool hollow); + bool jacobiTransform(PxI32 n, PxF64 a[], PxF64 w[]); + bool diagonalizeInertiaTensor(const PxMat33& denseInertia, PxVec3& diagonalInertia, PxMat33& rotation); + +} // namespace Ext + +void Ext::computeBoxInertiaTensor(PxVec3& inertia, PxReal mass, PxReal xlength, PxReal ylength, PxReal zlength) +{ + //to model a hollow block, one would have to multiply coeff by up to two. + const PxReal coeff = mass/12; + inertia.x = coeff * (ylength*ylength + zlength*zlength); + inertia.y = coeff * (xlength*xlength + zlength*zlength); + inertia.z = coeff * (xlength*xlength + ylength*ylength); + + PX_ASSERT(inertia.x != 0.0f); + PX_ASSERT(inertia.y != 0.0f); + PX_ASSERT(inertia.z != 0.0f); + PX_ASSERT(inertia.isFinite()); +} + + +void Ext::computeSphereInertiaTensor(PxVec3& inertia, PxReal mass, PxReal radius, bool hollow) +{ + inertia.x = mass * radius * radius; + if (hollow) + inertia.x *= PxReal(2 / 3.0); + else + inertia.x *= PxReal(2 / 5.0); + + inertia.z = inertia.y = inertia.x; + PX_ASSERT(inertia.isFinite()); +} + +//-------------------------------------------------------------- +// +// InertiaTensorComputer implementation +// +//-------------------------------------------------------------- + +Ext::InertiaTensorComputer::InertiaTensorComputer(bool initTozero) +{ + if (initTozero) + zero(); +} + + +Ext::InertiaTensorComputer::InertiaTensorComputer(const PxMat33& inertia, const PxVec3& com, PxReal mass) : + mI(inertia), + mG(com), + mMass(mass) +{ +} + + +Ext::InertiaTensorComputer::~InertiaTensorComputer() +{ + //nothing +} + + +PX_INLINE void Ext::InertiaTensorComputer::zero() +{ + mMass = 0.0f; + mI = PxMat33(PxZero); + mG = PxVec3(0); +} + + +PX_INLINE void Ext::InertiaTensorComputer::setDiagonal(PxReal mass, const PxVec3& diag) +{ + mMass = mass; + mI = PxMat33::createDiagonal(diag); + mG = PxVec3(0); + PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite()); + PX_ASSERT(PxIsFinite(mMass)); +} + + +void Ext::InertiaTensorComputer::setBox(const PxVec3& halfWidths) +{ + // Setup inertia tensor for a cube with unit density + const PxReal mass = 8.0f * computeBoxRatio(halfWidths); + const PxReal s =(1.0f/3.0f) * mass; + + const PxReal x = halfWidths.x*halfWidths.x; + const PxReal y = halfWidths.y*halfWidths.y; + const PxReal z = halfWidths.z*halfWidths.z; + + setDiagonal(mass, PxVec3(y+z, z+x, x+y) * s); +} + + +PX_INLINE void Ext::InertiaTensorComputer::rotate(const PxMat33& rot) +{ + //well known inertia tensor rotation expression is: RIR' -- this could be optimized due to symmetry, see code to do that in Body::updateGlobalInverseInertia + mI = rot * mI * rot.getTranspose(); + PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite()); + //com also needs to be rotated + mG = rot * mG; + PX_ASSERT(mG.isFinite()); +} + + +void Ext::InertiaTensorComputer::translate(const PxVec3& t) +{ + if (!t.isZero()) //its common for this to be zero + { + PxMat33 t1, t2; + + t1.column0 = PxVec3(0, mG.z, -mG.y); + t1.column1 = PxVec3(-mG.z, 0, mG.x); + t1.column2 = PxVec3(mG.y, -mG.x, 0); + + PxVec3 sum = mG + t; + if (sum.isZero()) + { + mI += (t1 * t1)*mMass; + } + else + { + t2.column0 = PxVec3(0, sum.z, -sum.y); + t2.column1 = PxVec3(-sum.z, 0, sum.x); + t2.column2 = PxVec3(sum.y, -sum.x, 0); + mI += (t1 * t1 - t2 * t2)*mMass; + } + + //move center of mass + mG += t; + + PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite()); + PX_ASSERT(mG.isFinite()); + } +} + + +PX_INLINE void Ext::InertiaTensorComputer::transform(const PxTransform& transform) +{ + rotate(PxMat33(transform.q)); + translate(transform.p); +} + + +PX_INLINE void Ext::InertiaTensorComputer::setBox(const PxVec3& halfWidths, const PxTransform* pose) +{ + setBox(halfWidths); + if (pose) + transform(*pose); + +} + + +PX_INLINE void Ext::InertiaTensorComputer::scaleDensity(PxReal densityScale) +{ + mI *= densityScale; + mMass *= densityScale; + PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite()); + PX_ASSERT(PxIsFinite(mMass)); +} + + +PX_INLINE void Ext::InertiaTensorComputer::add(const InertiaTensorComputer& it) +{ + const PxReal TotalMass = mMass + it.mMass; + mG = (mG * mMass + it.mG * it.mMass) / TotalMass; + + mMass = TotalMass; + mI += it.mI; + PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite()); + PX_ASSERT(mG.isFinite()); + PX_ASSERT(PxIsFinite(mMass)); +} + + +PX_INLINE void Ext::InertiaTensorComputer::center() +{ + PxVec3 center = -mG; + translate(center); +} + + +void Ext::InertiaTensorComputer::setSphere(PxReal radius) +{ + // Compute mass of the sphere + const PxReal m = computeSphereRatio(radius); + // Compute moment of inertia + const PxReal s = m * radius * radius * (2.0f/5.0f); + setDiagonal(m,PxVec3(s,s,s)); +} + + +PX_INLINE void Ext::InertiaTensorComputer::setSphere(PxReal radius, const PxTransform* pose) +{ + setSphere(radius); + if (pose) + transform(*pose); +} + + +void Ext::InertiaTensorComputer::setCylinder(int dir, PxReal r, PxReal l) +{ + // Compute mass of cylinder + const PxReal m = computeCylinderRatio(r, l); + + const PxReal i1 = r*r*m/2.0f; + const PxReal i2 = (3.0f*r*r+4.0f*l*l)*m/12.0f; + + switch(dir) + { + case 0: setDiagonal(m,PxVec3(i1,i2,i2)); break; + case 1: setDiagonal(m,PxVec3(i2,i1,i2)); break; + default: setDiagonal(m,PxVec3(i2,i2,i1)); break; + } +} + + +PX_INLINE void Ext::InertiaTensorComputer::setCylinder(int dir, PxReal r, PxReal l, const PxTransform* pose) +{ + setCylinder(dir, r, l); + if (pose) + transform(*pose); +} + + +void Ext::InertiaTensorComputer::setCapsule(int dir, PxReal r, PxReal l) +{ + // Compute mass of capsule + const PxReal m = computeCapsuleRatio(r, l); + + const PxReal t = PxPi * r * r; + const PxReal i1 = t * ((r*r*r * 8.0f/15.0f) + (l*r*r)); + const PxReal i2 = t * ((r*r*r * 8.0f/15.0f) + (l*r*r * 3.0f/2.0f) + (l*l*r * 4.0f/3.0f) + (l*l*l * 2.0f/3.0f)); + + switch(dir) + { + case 0: setDiagonal(m,PxVec3(i1,i2,i2)); break; + case 1: setDiagonal(m,PxVec3(i2,i1,i2)); break; + default: setDiagonal(m,PxVec3(i2,i2,i1)); break; + } +} + + +PX_INLINE void Ext::InertiaTensorComputer::setCapsule(int dir, PxReal r, PxReal l, const PxTransform* pose) +{ + setCapsule(dir, r, l); + if (pose) + transform(*pose); +} + + +void Ext::InertiaTensorComputer::setEllipsoid(PxReal rx, PxReal ry, PxReal rz) +{ + // Compute mass of ellipsoid + const PxReal m = computeEllipsoidRatio(PxVec3(rx, ry, rz)); + + // Compute moment of inertia + const PxReal s = m * (2.0f/5.0f); + + // Setup inertia tensor for an ellipsoid centered at the origin + setDiagonal(m,PxVec3(ry*rz,rz*rx,rx*ry)*s); +} + + +PX_INLINE void Ext::InertiaTensorComputer::setEllipsoid(PxReal rx, PxReal ry, PxReal rz, const PxTransform* pose) +{ + setEllipsoid(rx,ry,rz); + if (pose) + transform(*pose); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp new file mode 100644 index 000000000..32ee392b4 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp @@ -0,0 +1,134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "foundation/PxMat33.h" +#include "PxConstraint.h" +#include "PxJoint.h" +#include "ExtJoint.h" + +using namespace physx; +using namespace Ext; + +PxConstraint* physx::resolveConstraintPtr(PxDeserializationContext& v, + PxConstraint* old, + PxConstraintConnector* connector, + PxConstraintShaderTable &shaders) +{ + v.translatePxBase(old); + PxConstraint* new_nx = static_cast(old); + new_nx->setConstraintFunctions(*connector, shaders); + return new_nx; +} +//~PX_SERIALIZATION + + +static void normalToTangents(const PxVec3& n, PxVec3& t1, PxVec3& t2) +{ + const PxReal m_sqrt1_2 = PxReal(0.7071067811865475244008443621048490); + if(fabsf(n.z) > m_sqrt1_2) + { + const PxReal a = n.y*n.y + n.z*n.z; + const PxReal k = PxReal(1.0)/PxSqrt(a); + t1 = PxVec3(0,-n.z*k,n.y*k); + t2 = PxVec3(a*k,-n.x*t1.z,n.x*t1.y); + } + else + { + const PxReal a = n.x*n.x + n.y*n.y; + const PxReal k = PxReal(1.0)/PxSqrt(a); + t1 = PxVec3(-n.y*k,n.x*k,0); + t2 = PxVec3(-n.z*t1.y,n.z*t1.x,a*k); + } + t1.normalize(); + t2.normalize(); +} + +void PxSetJointGlobalFrame(PxJoint& joint, const PxVec3* wsAnchor, const PxVec3* axisIn) +{ + PxRigidActor* actors[2]; + joint.getActors(actors[0], actors[1]); + + PxTransform localPose[2]; + for(PxU32 i=0; i<2; i++) + localPose[i] = PxTransform(PxIdentity); + + // 1) global anchor + if(wsAnchor) + { + //transform anchorPoint to local space + for(PxU32 i=0; i<2; i++) + localPose[i].p = actors[i] ? actors[i]->getGlobalPose().transformInv(*wsAnchor) : *wsAnchor; + } + + // 2) global axis + if(axisIn) + { + PxVec3 localAxis[2], localNormal[2]; + + //find 2 orthogonal vectors. + //gotta do this in world space, if we choose them + //separately in local space they won't match up in worldspace. + PxVec3 axisw = *axisIn; + axisw.normalize(); + + PxVec3 normalw, binormalw; + ::normalToTangents(axisw, binormalw, normalw); + //because axis is supposed to be the Z axis of a frame with the other two being X and Y, we need to negate + //Y to make the frame right handed. Note that the above call makes a right handed frame if we pass X --> Y,Z, so + //it need not be changed. + + for(PxU32 i=0; i<2; i++) + { + if(actors[i]) + { + const PxTransform& m = actors[i]->getGlobalPose(); + PxMat33 mM(m.q); + localAxis[i] = mM.transformTranspose(axisw); + localNormal[i] = mM.transformTranspose(normalw); + } + else + { + localAxis[i] = axisw; + localNormal[i] = normalw; + } + + PxMat33 rot(localAxis[i], localNormal[i], localAxis[i].cross(localNormal[i])); + + localPose[i].q = PxQuat(rot); + localPose[i].q.normalize(); + } + } + + for(PxU32 i=0; i<2; i++) + joint.setLocalPose(static_cast( i ), localPose[i]); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtJoint.h new file mode 100644 index 000000000..0bce87e3b --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtJoint.h @@ -0,0 +1,640 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_JOINTCONSTRAINT_H +#define NP_JOINTCONSTRAINT_H + +#include "PsAllocator.h" +#include "PsUtilities.h" +#include "PsMathUtils.h" +#include "PxConstraint.h" +#include "PxConstraintExt.h" +#include "PxJoint.h" +#include "PxD6Joint.h" +#include "PxRigidDynamic.h" +#include "PxRigidStatic.h" +#include "PxScene.h" +#include "ExtPvd.h" +#include "PxMetaData.h" +#include "CmRenderOutput.h" +#include "PxPhysics.h" +#include "PsFoundation.h" + +#if PX_SUPPORT_PVD +#include "PxScene.h" +#include "PxPvdClient.h" +#include "PxPvdSceneClient.h" +#endif + +// PX_SERIALIZATION + +namespace physx +{ + +PxConstraint* resolveConstraintPtr(PxDeserializationContext& v, PxConstraint* old, PxConstraintConnector* connector, PxConstraintShaderTable& shaders); + +// ~PX_SERIALIZATION + +namespace Ext +{ + PX_FORCE_INLINE float computeSwingAngle(float swingYZ, float swingW) + { + return 4.0f * PxAtan2(swingYZ, 1.0f + swingW); // tan (t/2) = sin(t)/(1+cos t), so this is the quarter angle + } + + struct JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + PxConstraintInvMassScale invMassScale; //16 + PxTransform c2b[2]; //72 + PxU32 pading[2]; //80 + protected: + ~JointData(){} + }; + + template + class Joint : public Base, + public PxConstraintConnector, + public Ps::UserAllocated + { + + public: +// PX_SERIALIZATION + Joint(PxBaseFlags baseFlags) : Base(baseFlags) {} + virtual void requiresObjects(PxProcessPxBaseCallback& c) + { + c.process(*mPxConstraint); + + { + PxRigidActor* a0 = NULL; + PxRigidActor* a1 = NULL; + mPxConstraint->getActors(a0,a1); + + if (a0) + { + c.process(*a0); + } + if (a1) + { + c.process(*a1); + } + } + } +//~PX_SERIALIZATION + +#if PX_SUPPORT_PVD + // PxConstraintConnector + virtual bool updatePvdProperties(physx::pvdsdk::PvdDataStream& pvdConnection, const PxConstraint* c, PxPvdUpdateType::Enum updateType) const + { + if(updateType == PxPvdUpdateType::UPDATE_SIM_PROPERTIES) + { + Ext::Pvd::simUpdate(pvdConnection, *this); + return true; + } + else if(updateType == PxPvdUpdateType::UPDATE_ALL_PROPERTIES) + { + Ext::Pvd::updatePvdProperties(pvdConnection, *this); + return true; + } + else if(updateType == PxPvdUpdateType::CREATE_INSTANCE) + { + Ext::Pvd::createPvdInstance(pvdConnection, *c, *this); + return true; + } + else if(updateType == PxPvdUpdateType::RELEASE_INSTANCE) + { + Ext::Pvd::releasePvdInstance(pvdConnection, *c, *this); + return true; + } + return false; + } +#else + virtual bool updatePvdProperties(physx::pvdsdk::PvdDataStream&, const PxConstraint*, PxPvdUpdateType::Enum) const + { + return false; + } +#endif + + // PxJoint + virtual void setActors(PxRigidActor* actor0, PxRigidActor* actor1) + { + //TODO SDK-DEV + //You can get the debugger stream from the NpScene + //Ext::Pvd::setActors( stream, this, mPxConstraint, actor0, actor1 ); + PX_CHECK_AND_RETURN(actor0 != actor1, "PxJoint::setActors: actors must be different"); + PX_CHECK_AND_RETURN((actor0 && !actor0->is()) || (actor1 && !actor1->is()), "PxJoint::setActors: at least one actor must be non-static"); + +#if PX_SUPPORT_PVD + PxScene* scene = getScene(); + if(scene) + { + //if pvd not connect data stream is NULL + physx::pvdsdk::PvdDataStream* conn = scene->getScenePvdClient()->getClientInternal()->getDataStream(); + if( conn != NULL ) + Ext::Pvd::setActors( + *conn, + *this, + *mPxConstraint, + actor0, + actor1 + ); + } +#endif + mPxConstraint->setActors(actor0, actor1); + mData->c2b[0] = getCom(actor0).transformInv(mLocalPose[0]); + mData->c2b[1] = getCom(actor1).transformInv(mLocalPose[1]); + mPxConstraint->markDirty(); + } + + // PxJoint + virtual void getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const + { + if ( mPxConstraint ) mPxConstraint->getActors(actor0,actor1); + else + { + actor0 = NULL; + actor1 = NULL; + } + } + + // this is the local pose relative to the actor, and we store internally the local + // pose relative to the body + + // PxJoint + virtual void setLocalPose(PxJointActorIndex::Enum actor, const PxTransform& pose) + { + PX_CHECK_AND_RETURN(pose.isSane(), "PxJoint::setLocalPose: transform is invalid"); + PxTransform p = pose.getNormalized(); + mLocalPose[actor] = p; + mData->c2b[actor] = getCom(actor).transformInv(p); + mPxConstraint->markDirty(); + } + + // PxJoint + virtual PxTransform getLocalPose(PxJointActorIndex::Enum actor) const + { + return mLocalPose[actor]; + } + + static PxTransform getGlobalPose(const PxRigidActor* actor) + { + if(!actor) + return PxTransform(PxIdentity); + return actor->getGlobalPose(); + } + + static void getActorVelocity(const PxRigidActor* actor, PxVec3& linear, PxVec3& angular) + { + if(!actor || actor->is()) + { + linear = angular = PxVec3(0.0f); + return; + } + + linear = static_cast(actor)->getLinearVelocity(); + angular = static_cast(actor)->getAngularVelocity(); + } + + // PxJoint + virtual PxTransform getRelativeTransform() const + { + PxRigidActor* actor0, * actor1; + mPxConstraint->getActors(actor0, actor1); + const PxTransform t0 = getGlobalPose(actor0) * mLocalPose[0]; + const PxTransform t1 = getGlobalPose(actor1) * mLocalPose[1]; + return t0.transformInv(t1); + } + + // PxJoint + virtual PxVec3 getRelativeLinearVelocity() const + { + PxRigidActor* actor0, * actor1; + PxVec3 l0, a0, l1, a1; + mPxConstraint->getActors(actor0, actor1); + + PxTransform t0 = getCom(actor0), t1 = getCom(actor1); + getActorVelocity(actor0, l0, a0); + getActorVelocity(actor1, l1, a1); + + PxVec3 p0 = t0.q.rotate(mLocalPose[0].p), + p1 = t1.q.rotate(mLocalPose[1].p); + return t0.transformInv(l1 - a1.cross(p1) - l0 + a0.cross(p0)); + } + + // PxJoint + virtual PxVec3 getRelativeAngularVelocity() const + { + PxRigidActor* actor0, * actor1; + PxVec3 l0, a0, l1, a1; + mPxConstraint->getActors(actor0, actor1); + + PxTransform t0 = getCom(actor0); + getActorVelocity(actor0, l0, a0); + getActorVelocity(actor1, l1, a1); + + return t0.transformInv(a1 - a0); + } + + // PxJoint + virtual void setBreakForce(PxReal force, PxReal torque) + { + PX_CHECK_AND_RETURN(PxIsFinite(force) && PxIsFinite(torque), "NpJoint::setBreakForce: invalid float"); + mPxConstraint->setBreakForce(force,torque); + } + + // PxJoint + virtual void getBreakForce(PxReal& force, PxReal& torque) const + { + mPxConstraint->getBreakForce(force,torque); + } + + // PxJoint + virtual void setConstraintFlags(PxConstraintFlags flags) + { + mPxConstraint->setFlags(flags); + } + + // PxJoint + virtual void setConstraintFlag(PxConstraintFlag::Enum flag, bool value) + { + mPxConstraint->setFlag(flag, value); + } + + // PxJoint + virtual PxConstraintFlags getConstraintFlags() const + { + return mPxConstraint->getFlags(); + } + + // PxJoint + virtual void setInvMassScale0(PxReal invMassScale) + { + PX_CHECK_AND_RETURN(PxIsFinite(invMassScale) && invMassScale>=0, "PxJoint::setInvMassScale0: scale must be non-negative"); + mData->invMassScale.linear0 = invMassScale; + mPxConstraint->markDirty(); + } + + // PxJoint + virtual PxReal getInvMassScale0() const + { + return mData->invMassScale.linear0; + } + + // PxJoint + virtual void setInvInertiaScale0(PxReal invInertiaScale) + { + PX_CHECK_AND_RETURN(PxIsFinite(invInertiaScale) && invInertiaScale>=0, "PxJoint::setInvInertiaScale0: scale must be non-negative"); + mData->invMassScale.angular0 = invInertiaScale; + mPxConstraint->markDirty(); + } + + // PxJoint + virtual PxReal getInvInertiaScale0() const + { + return mData->invMassScale.angular0; + } + + // PxJoint + virtual void setInvMassScale1(PxReal invMassScale) + { + PX_CHECK_AND_RETURN(PxIsFinite(invMassScale) && invMassScale>=0, "PxJoint::setInvMassScale1: scale must be non-negative"); + mData->invMassScale.linear1 = invMassScale; + mPxConstraint->markDirty(); + } + + // PxJoint + virtual PxReal getInvMassScale1() const + { + return mData->invMassScale.linear1; + } + + // PxJoint + virtual void setInvInertiaScale1(PxReal invInertiaScale) + { + PX_CHECK_AND_RETURN(PxIsFinite(invInertiaScale) && invInertiaScale>=0, "PxJoint::setInvInertiaScale: scale must be non-negative"); + mData->invMassScale.angular1 = invInertiaScale; + mPxConstraint->markDirty(); + } + + // PxJoint + virtual PxReal getInvInertiaScale1() const + { + return mData->invMassScale.angular1; + } + + // PxJoint + virtual PxConstraint* getConstraint() const + { + return mPxConstraint; + } + + // PxJoint + virtual void setName(const char* name) + { + mName = name; + } + + // PxJoint + virtual const char* getName() const + { + return mName; + } + + // PxJoint + virtual void release() + { + mPxConstraint->release(); + } + + // PxJoint + virtual PxScene* getScene() const + { + return mPxConstraint ? mPxConstraint->getScene() : NULL; + } + + // PxConstraintConnector + virtual void onComShift(PxU32 actor) + { + mData->c2b[actor] = getCom(actor).transformInv(mLocalPose[actor]); + markDirty(); + } + + // PxConstraintConnector + virtual void onOriginShift(const PxVec3& shift) + { + PxRigidActor* a[2]; + mPxConstraint->getActors(a[0], a[1]); + + if (!a[0]) + { + mLocalPose[0].p -= shift; + mData->c2b[0].p -= shift; + markDirty(); + } + else if (!a[1]) + { + mLocalPose[1].p -= shift; + mData->c2b[1].p -= shift; + markDirty(); + } + } + + // PxConstraintConnector + virtual void* prepareData() + { + return mData; + } + + // PxConstraintConnector + virtual void* getExternalReference(PxU32& typeID) + { + typeID = PxConstraintExtIDs::eJOINT; + return static_cast( this ); + } + + // PxConstraintConnector + virtual PxBase* getSerializable() + { + return this; + } + + // PxConstraintConnector + virtual void onConstraintRelease() + { + PX_FREE_AND_RESET(mData); + delete this; + } + + // PxConstraintConnector + virtual const void* getConstantBlock() const + { + return mData; + } + + private: + PxTransform getCom(PxU32 index) const + { + PxRigidActor* a[2]; + mPxConstraint->getActors(a[0],a[1]); + return getCom(a[index]); + } + + PxTransform getCom(PxRigidActor* actor) const + { + if (!actor) + return PxTransform(PxIdentity); + else if (actor->getType() == PxActorType::eRIGID_DYNAMIC || actor->getType() == PxActorType::eARTICULATION_LINK) + return static_cast(actor)->getCMassLocalPose(); + else + { + PX_ASSERT(actor->getType() == PxActorType::eRIGID_STATIC); + return static_cast(actor)->getGlobalPose().getInverse(); + } + } + + protected: + + Joint(PxType concreteType, PxBaseFlags baseFlags, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1, PxU32 size, const char* name) : + Base (concreteType, baseFlags), + mName (NULL), + mPxConstraint (NULL) + { + PX_UNUSED(name); + Base::userData = NULL; + + JointData* data = reinterpret_cast(PX_ALLOC(size, name)); + Cm::markSerializedMem(data, size); + + mLocalPose[0] = localFrame0.getNormalized(); + mLocalPose[1] = localFrame1.getNormalized(); + data->c2b[0] = getCom(actor0).transformInv(localFrame0); + data->c2b[1] = getCom(actor1).transformInv(localFrame1); + data->invMassScale.linear0 = 1.0f; + data->invMassScale.angular0 = 1.0f; + data->invMassScale.linear1 = 1.0f; + data->invMassScale.angular1 = 1.0f; + + mData = data; + } + + virtual ~Joint() + { + if(Base::getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + PX_FREE_AND_RESET(mData); + } + + PX_FORCE_INLINE void markDirty() + { + mPxConstraint->markDirty(); + } + + PX_FORCE_INLINE PxConstraintConnector* getConnector() + { + return this; + } + + PX_FORCE_INLINE PxConstraint* getPxConstraint() + { + return mPxConstraint; + } + + PX_FORCE_INLINE void setPxConstraint(PxConstraint* pxConstraint) + { + mPxConstraint = pxConstraint; + } + + void wakeUpActors() + { + PxRigidActor* a[2]; + mPxConstraint->getActors(a[0], a[1]); + for(PxU32 i = 0; i < 2; i++) + { + if(a[i] && a[i]->getScene() && a[i]->getType() == PxActorType::eRIGID_DYNAMIC) + { + PxRigidDynamic* rd = static_cast(a[i]); + if(!(rd->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC)) + { + const PxScene* scene = rd->getScene(); + const PxReal wakeCounterResetValue = scene->getWakeCounterResetValue(); + + PxReal wakeCounter = rd->getWakeCounter(); + + bool needsWakingUp = rd->isSleeping(); + if (wakeCounter < wakeCounterResetValue) + { + wakeCounter = wakeCounterResetValue; + needsWakingUp = true; + } + + if (needsWakingUp) + { + rd->wakeUp(); + rd->setWakeCounter(wakeCounter); + } + } + } + } + } + + PX_FORCE_INLINE PxQuat getTwistOrSwing(bool needTwist) const + { + const PxQuat q = getRelativeTransform().q; + // PT: TODO: we don't need to compute both quats here + PxQuat swing, twist; + Ps::separateSwingTwist(q, swing, twist); + return needTwist ? twist : swing; + } + + PxReal getTwistAngle_Internal() const + { + const PxQuat twist = getTwistOrSwing(true); + + // PT: the angle-axis formulation creates the quat like this: + // + // const float a = angleRadians * 0.5f; + // const float s = PxSin(a); + // w = PxCos(a); + // x = unitAxis.x * s; + // y = unitAxis.y * s; + // z = unitAxis.z * s; + // + // With the twist axis = (1;0;0) this gives: + // + // w = PxCos(angleRadians * 0.5f); + // x = PxSin(angleRadians * 0.5f); + // y = 0.0f; + // z = 0.0f; + // + // Thus the quat's "getAngle" function returns: + // + // angle = PxAcos(w) * 2.0f; + // + // PxAcos will return an angle between 0 and PI in radians, so "getAngle" will return an angle between 0 and PI*2. + + PxReal angle = twist.getAngle(); + + if(twist.x<0.0f) + angle = -angle; + + return angle; + } + + PxReal getSwingYAngle_Internal() const + { + PxQuat swing = getTwistOrSwing(false); + + if(swing.w < 0.0f) // choose the shortest rotation + swing = -swing; + + const PxReal angle = computeSwingAngle(swing.y, swing.w); + PX_ASSERT(angle>-PxPi && angle<=PxPi); // since |y| < w+1, the atan magnitude is < PI/4 + return angle; + } + + PxReal getSwingZAngle_Internal() const + { + PxQuat swing = getTwistOrSwing(false); + + if(swing.w < 0.0f) // choose the shortest rotation + swing = -swing; + + const PxReal angle = computeSwingAngle(swing.z, swing.w); + PX_ASSERT(angle>-PxPi && angle <= PxPi); // since |y| < w+1, the atan magnitude is < PI/4 + return angle; + } + + const char* mName; + PxTransform mLocalPose[2]; + PxConstraint* mPxConstraint; + JointData* mData; + }; + + PX_FORCE_INLINE bool isLimitActive(const PxJointLimitParameters& limit, PxReal pad, PxReal angle, PxReal low, PxReal high) + { + PX_ASSERT(low high - pad) + active = true; + return active; + } + +} // namespace Ext + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h b/src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h new file mode 100644 index 000000000..8c35c211d --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h @@ -0,0 +1,65 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef EXT_JOINT_META_DATA_EXTENSIONS_H +#define EXT_JOINT_META_DATA_EXTENSIONS_H +#include "PvdMetaDataExtensions.h" + +namespace physx +{ + +namespace pvdsdk +{ + + +struct PxExtensionPvdOnlyProperties +{ + enum Enum + { + FirstProp = PxExtensionsPropertyInfoName::LastPxPropertyInfoName, + DEFINE_ENUM_RANGE( PxJoint_Actors, 2 ), + DEFINE_ENUM_RANGE( PxJoint_BreakForce, 2 ), + DEFINE_ENUM_RANGE( PxD6Joint_DriveVelocity, 2 ), + DEFINE_ENUM_RANGE( PxD6Joint_Motion, PxD6Axis::eCOUNT ), + DEFINE_ENUM_RANGE( PxD6Joint_Drive, PxD6Drive::eCOUNT * ( PxExtensionsPropertyInfoName::PxD6JointDrive_PropertiesStop - PxExtensionsPropertyInfoName::PxD6JointDrive_PropertiesStart ) ), + DEFINE_ENUM_RANGE( PxD6Joint_LinearLimit, 100 ), + DEFINE_ENUM_RANGE( PxD6Joint_SwingLimit, 100 ), + DEFINE_ENUM_RANGE( PxD6Joint_TwistLimit, 100 ), + DEFINE_ENUM_RANGE( PxPrismaticJoint_Limit, 100 ), + DEFINE_ENUM_RANGE( PxRevoluteJoint_Limit, 100 ), + DEFINE_ENUM_RANGE( PxSphericalJoint_LimitCone, 100 ), + DEFINE_ENUM_RANGE( PxJoint_LocalPose, 2 ) + }; +}; + +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp b/src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp new file mode 100644 index 000000000..411ede6b3 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp @@ -0,0 +1,445 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "PxJoint.h" +#include "ExtJoint.h" +#include "ExtD6Joint.h" +#include "ExtFixedJoint.h" +#include "ExtSphericalJoint.h" +#include "ExtDistanceJoint.h" +#include "ExtContactJoint.h" +#include "ExtSphericalJoint.h" +#include "ExtRevoluteJoint.h" +#include "ExtPrismaticJoint.h" +#include "serialization/SnSerializationRegistry.h" +#include "serialization/Binary/SnSerializationContext.h" + +using namespace physx; +using namespace Cm; +using namespace Ext; + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_JointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, JointData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, JointData, PxTransform, c2b, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, JointData, PxU32, paddingFromFlags, PxMetaDataFlag::ePADDING) +#endif + PX_DEF_BIN_METADATA_ITEM(stream, JointData, PxConstraintInvMassScale, invMassScale, 0) +} + +static void getBinaryMetaData_PxD6JointDrive(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxD6JointDriveFlags, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, PxD6JointDrive) + PX_DEF_BIN_METADATA_ITEM(stream, PxD6JointDrive, PxReal, stiffness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxD6JointDrive, PxReal, damping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxD6JointDrive, PxReal, forceLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxD6JointDrive, PxD6JointDriveFlags, flags, 0) +} + +static void getBinaryMetaData_PxJointLimitParameters(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitParameters, PxReal, restitution, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitParameters, PxReal, bounceThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitParameters, PxReal, stiffness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitParameters, PxReal, damping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitParameters, PxReal, contactDistance, 0) +} + +static void getBinaryMetaData_PxJointLinearLimit(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointLinearLimit) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJointLinearLimit, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLinearLimit, PxReal, value, 0) +} + +static void getBinaryMetaData_PxJointLinearLimitPair(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointLinearLimitPair) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJointLinearLimitPair, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLinearLimitPair, PxReal, upper, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLinearLimitPair, PxReal, lower, 0) +} + +static void getBinaryMetaData_PxJointAngularLimitPair(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointAngularLimitPair) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJointAngularLimitPair, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointAngularLimitPair, PxReal, upper, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointAngularLimitPair, PxReal, lower, 0) +} + +static void getBinaryMetaData_PxJointLimitCone(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointLimitCone) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJointLimitCone, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitCone, PxReal, yAngle, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitCone, PxReal, zAngle, 0) +} + +static void getBinaryMetaData_PxJointLimitPyramid(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointLimitPyramid) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJointLimitPyramid, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitPyramid, PxReal, yAngleMin, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitPyramid, PxReal, yAngleMax, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitPyramid, PxReal, zAngleMin, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitPyramid, PxReal, zAngleMax, 0) +} + +void PxJoint::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_VCLASS(stream, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJoint, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJoint, void, userData, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_RevoluteJointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxRevoluteJointFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, RevoluteJointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, RevoluteJointData, JointData) + + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxReal, driveVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxReal, driveForceLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxReal, driveGearRatio, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxJointAngularLimitPair,limit, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxReal, projectionLinearTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxReal, projectionAngularTolerance, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxRevoluteJointFlags, jointFlags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxU16, paddingFromFlags, PxMetaDataFlag::ePADDING) +#endif +} + +void RevoluteJoint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_RevoluteJointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, RevoluteJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, RevoluteJoint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, RevoluteJoint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJoint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, RevoluteJoint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJoint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJoint, JointData, mData, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, RevoluteJoint, RevoluteJointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, RevoluteJoint, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_SphericalJointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxSphericalJointFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, SphericalJointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, SphericalJointData, JointData) + + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJointData, PxJointLimitCone, limit, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJointData, PxReal, projectionLinearTolerance, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJointData, PxSphericalJointFlags, jointFlags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJointData, PxU16, paddingFromFlags, PxMetaDataFlag::ePADDING) +#endif +} + +void SphericalJoint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_SphericalJointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, SphericalJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, SphericalJoint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, SphericalJoint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJoint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, SphericalJoint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJoint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJoint, JointData, mData, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, SphericalJoint, SphericalJointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, SphericalJoint, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_DistanceJointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxDistanceJointFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, DistanceJointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, DistanceJointData, JointData) + + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxReal, minDistance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxReal, maxDistance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxReal, tolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxReal, stiffness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxReal, damping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxDistanceJointFlags, jointFlags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxU16, paddingFromFlags, PxMetaDataFlag::ePADDING) +#endif +} + +void DistanceJoint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_DistanceJointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, DistanceJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, DistanceJoint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, DistanceJoint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJoint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, DistanceJoint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJoint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJoint, JointData, mData, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, DistanceJoint, DistanceJointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, DistanceJoint, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_D6JointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxD6Motion::Enum, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, D6JointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, D6JointData, JointData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, D6JointData, PxD6Motion::Enum, motion, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLinearLimit, distanceLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLinearLimitPair, linearLimitX, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLinearLimitPair, linearLimitY, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLinearLimitPair, linearLimitZ, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointAngularLimitPair, twistLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLimitCone, swingLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLimitPyramid, pyramidSwingLimit, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, D6JointData, PxD6JointDrive, drive, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxTransform, drivePosition, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxVec3, driveLinearVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxVec3, driveAngularVelocity, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxU32, locked, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxU32, limited, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxU32, driving, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxReal, distanceMinDist, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxReal, projectionLinearTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxReal, projectionAngularTolerance, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, bool, mUseDistanceLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, bool, mUseNewLinearLimits, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, bool, mUseConeLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, bool, mUsePyramidLimits, 0) +} + +void D6Joint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_D6JointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, D6Joint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, D6Joint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, D6Joint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, D6Joint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, D6Joint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6Joint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, D6Joint, JointData, mData, PxMetaDataFlag::ePTR) + + PX_DEF_BIN_METADATA_ITEM(stream, D6Joint, bool, mRecomputeMotion, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, D6Joint, bool, mPadding, PxMetaDataFlag::ePADDING) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, D6Joint, D6JointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, D6Joint, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PrismaticJointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxPrismaticJointFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, PrismaticJointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PrismaticJointData, JointData) + + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJointData, PxJointLinearLimitPair, limit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJointData, PxReal, projectionLinearTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJointData, PxReal, projectionAngularTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJointData, PxPrismaticJointFlags, jointFlags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJointData, PxU16, paddingFromFlags, PxMetaDataFlag::ePADDING) +#endif +} + +void PrismaticJoint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PrismaticJointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, PrismaticJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PrismaticJoint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PrismaticJoint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJoint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PrismaticJoint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJoint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJoint, JointData, mData, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, PrismaticJoint, PrismaticJointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, PrismaticJoint, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_FixedJointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, FixedJointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, FixedJointData, JointData) + + PX_DEF_BIN_METADATA_ITEM(stream, FixedJointData, PxReal, projectionLinearTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, FixedJointData, PxReal, projectionAngularTolerance, 0) +} + +void FixedJoint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_FixedJointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, FixedJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, FixedJoint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, FixedJoint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, FixedJoint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, FixedJoint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, FixedJoint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, FixedJoint, JointData, mData, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, FixedJoint, FixedJointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, FixedJoint, mName, 0) +} + + +void getBinaryMetaData_SerializationContext(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxSerialObjectId, PxU64) + PX_DEF_BIN_METADATA_TYPEDEF(stream, SerialObjectIndex, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, Sn::ManifestEntry) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ManifestEntry, PxU32, offset, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ManifestEntry, PxType, type, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, Sn::ImportReference) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ImportReference, PxSerialObjectId, id, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ImportReference, PxType, type, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, Sn::ExportReference) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ExportReference, PxSerialObjectId, id, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ExportReference, SerialObjectIndex, objIndex, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, Sn::InternalReferencePtr) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferencePtr, void, reference, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferencePtr, PxU32, kind, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferencePtr, SerialObjectIndex, objIndex, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, Sn::InternalReferenceIdx) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferenceIdx, PxU32, reference, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferenceIdx, PxU32, kind, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferenceIdx, SerialObjectIndex, objIndex, 0) +} + +namespace physx +{ +namespace Ext +{ +void GetExtensionsBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_VCLASS(stream,PxConstraintConnector) + + getBinaryMetaData_JointData(stream); + getBinaryMetaData_PxD6JointDrive(stream); + getBinaryMetaData_PxJointLimitParameters(stream); + getBinaryMetaData_PxJointLimitCone(stream); + getBinaryMetaData_PxJointLimitPyramid(stream); + getBinaryMetaData_PxJointLinearLimit(stream); + getBinaryMetaData_PxJointLinearLimitPair(stream); + getBinaryMetaData_PxJointAngularLimitPair(stream); + + PxJoint::getBinaryMetaData(stream); + RevoluteJoint::getBinaryMetaData(stream); + SphericalJoint::getBinaryMetaData(stream); + DistanceJoint::getBinaryMetaData(stream); + D6Joint::getBinaryMetaData(stream); + PrismaticJoint::getBinaryMetaData(stream); + FixedJoint::getBinaryMetaData(stream); + + getBinaryMetaData_SerializationContext(stream); +} + +} +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPlatform.h b/src/PhysX/physx/source/physxextensions/src/ExtPlatform.h new file mode 100644 index 000000000..8a7701970 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPlatform.h @@ -0,0 +1,39 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PLATFORM_H +#define PLATFORM_H + +#include +#include "foundation/Px.h" +#include "PsThread.h" +#include "CmPhysXCommon.h" + +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp new file mode 100644 index 000000000..86b9eaff5 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp @@ -0,0 +1,236 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtPrismaticJoint.h" +#include "ExtConstraintHelper.h" +#include "CmRenderOutput.h" +#include "CmVisualization.h" + +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxPrismaticJoint* physx::PxPrismaticJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxPrismaticJointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxPrismaticJointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxPrismaticJointCreate: at least one actor must be dynamic"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxPrismaticJointCreate: actors must be different"); + + PrismaticJoint* j; + PX_NEW_SERIALIZED(j, PrismaticJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +void PrismaticJoint::setProjectionAngularTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0 && tolerance <= PxPi, "PxPrismaticJoint::setProjectionAngularTolerance: invalid parameter"); + data().projectionAngularTolerance = tolerance; + markDirty(); +} + +PxReal PrismaticJoint::getProjectionAngularTolerance() const +{ + return data().projectionAngularTolerance; +} + +void PrismaticJoint::setProjectionLinearTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxPrismaticJoint::setProjectionLinearTolerance: invalid parameter"); + data().projectionLinearTolerance = tolerance; + markDirty(); +} + +PxReal PrismaticJoint::getProjectionLinearTolerance() const +{ + return data().projectionLinearTolerance; +} + +PxPrismaticJointFlags PrismaticJoint::getPrismaticJointFlags(void) const +{ + return data().jointFlags; +} + +void PrismaticJoint::setPrismaticJointFlags(PxPrismaticJointFlags flags) +{ + data().jointFlags = flags; markDirty(); +} + +void PrismaticJoint::setPrismaticJointFlag(PxPrismaticJointFlag::Enum flag, bool value) +{ + if(value) + data().jointFlags |= flag; + else + data().jointFlags &= ~flag; + markDirty(); +} + +PxJointLinearLimitPair PrismaticJoint::getLimit() const +{ + return data().limit; +} + +void PrismaticJoint::setLimit(const PxJointLinearLimitPair& limit) +{ + PX_CHECK_AND_RETURN(limit.isValid(), "PxPrismaticJoint::setLimit: invalid parameter"); + data().limit = limit; + markDirty(); +} + +bool PrismaticJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(PrismaticJointData)); + return mPxConstraint!=NULL; +} + +void PrismaticJoint::exportExtraData(PxSerializationContext& stream) +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(PrismaticJointData)); + } + stream.writeName(mName); +} + +void PrismaticJoint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + context.readName(mName); +} + +void PrismaticJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +PrismaticJoint* PrismaticJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PrismaticJoint* obj = new (address) PrismaticJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PrismaticJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetPrismaticJointShaderTable() +{ + return &PrismaticJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void PrismaticJointProject(const void* constantBlock, PxTransform& bodyAToWorld, PxTransform& bodyBToWorld, bool projectToA) +{ + const PrismaticJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w, cB2cA, projected; + joint::computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA); + + const PxVec3 v(0.0f, cB2cA.p.y, cB2cA.p.z); + bool linearTrunc, angularTrunc; + projected.p = joint::truncateLinear(v, data.projectionLinearTolerance, linearTrunc); + projected.q = joint::truncateAngular(cB2cA.q, PxSin(data.projectionAngularTolerance/2), PxCos(data.projectionAngularTolerance/2), angularTrunc); + + if(linearTrunc || angularTrunc) + { + projected.p.x = cB2cA.p.x; + joint::projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA); + } +} + +static void PrismaticJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags) +{ + const PrismaticJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + viz.visualizeJointFrames(cA2w, cB2w); + + if((flags & PxConstraintVisualizationFlag::eLIMITS) && (data.jointFlags & PxPrismaticJointFlag::eLIMIT_ENABLED)) + { + const PxVec3 bOriginInA = cA2w.transformInv(cB2w.p); + const PxReal ordinate = bOriginInA.x; + + const PxReal pad = data.limit.isSoft() ? 0.0f : data.limit.contactDistance; + viz.visualizeLinearLimit(cA2w, cB2w, data.limit.lower, ordinate < data.limit.lower + pad); + viz.visualizeLinearLimit(cA2w, cB2w, data.limit.upper, ordinate > data.limit.upper - pad); + } +} + +static PxU32 PrismaticJointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool /*useExtendedLimits*/, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const PrismaticJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + if (cA2w.q.dot(cB2w.q)<0.0f) // minimum dist quat (equiv to flipping cB2bB.q, which we don't use anywhere) + cB2w.q = -cB2w.q; + + const bool limitEnabled = data.jointFlags & PxPrismaticJointFlag::eLIMIT_ENABLED; + const PxJointLinearLimitPair& limit = data.limit; + const bool limitIsLocked = limitEnabled && limit.lower >= limit.upper; + + const PxVec3 bOriginInA = cA2w.transformInv(cB2w.p); + PxVec3 ra, rb; + ch.prepareLockedAxes(cA2w.q, cB2w.q, bOriginInA, limitIsLocked ? 7ul : 6ul, 7ul, ra, rb); + cA2wOut = ra + bA2w.p; + cB2wOut = rb + bB2w.p; + + if(limitEnabled && !limitIsLocked) + { + const PxVec3 axis = cA2w.rotate(PxVec3(1.0f, 0.0f, 0.0f)); // PT: TODO: this has already been computed as part of the quat-to-matrix transform within prepareLockedAxes + const PxReal ordinate = bOriginInA.x; + + ch.linearLimit(axis, ordinate, limit.upper, limit); + ch.linearLimit(-axis, -ordinate, -limit.lower, limit); + } + + return ch.getCount(); +} + +PxConstraintShaderTable Ext::PrismaticJoint::sShaders = { PrismaticJointSolverPrep, PrismaticJointProject, PrismaticJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h new file mode 100644 index 000000000..f03fca2e6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_PRISMATICJOINTCONSTRAINT_H +#define NP_PRISMATICJOINTCONSTRAINT_H + +#include "ExtJoint.h" +#include "PxPrismaticJoint.h" +#include "PxTolerancesScale.h" +#include "CmUtils.h" + +namespace physx +{ +struct PxPrismaticJointGeneratedValues; +namespace Ext +{ + struct PrismaticJointData : public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxJointLinearLimitPair limit; + PxReal projectionLinearTolerance; + PxReal projectionAngularTolerance; + + PxPrismaticJointFlags jointFlags; + // forestall compiler complaints about not being able to generate a constructor + private: + PrismaticJointData(const PxJointLinearLimitPair &pair): + limit(pair) {} + }; + + typedef Joint PrismaticJointT; + + class PrismaticJoint : public PrismaticJointT + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + PrismaticJoint(PxBaseFlags baseFlags) : PrismaticJointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static PrismaticJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PrismaticJoint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + PrismaticJointT(PxJointConcreteType::ePRISMATIC, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(PrismaticJointData), "PrismaticJointData") + { + PrismaticJointData* data = static_cast(mData); + + data->limit = PxJointLinearLimitPair(scale); + data->projectionLinearTolerance = 1e10f; + data->projectionAngularTolerance = PxPi; + data->jointFlags = PxPrismaticJointFlags(); + } + + // PxPrismaticJoint + virtual PxReal getPosition() const { return getRelativeTransform().p.x; } + virtual PxReal getVelocity() const { return getRelativeLinearVelocity().x; } + virtual void setLimit(const PxJointLinearLimitPair& limit); + virtual PxJointLinearLimitPair getLimit() const; + virtual void setPrismaticJointFlags(PxPrismaticJointFlags flags); + virtual void setPrismaticJointFlag(PxPrismaticJointFlag::Enum flag, bool value); + virtual PxPrismaticJointFlags getPrismaticJointFlags() const; + virtual void setProjectionLinearTolerance(PxReal tolerance); + virtual PxReal getProjectionLinearTolerance() const; + virtual void setProjectionAngularTolerance(PxReal tolerance); + virtual PxReal getProjectionAngularTolerance() const; + //~PxPrismaticJoint + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep() const { return sShaders.solverPrep; } + + private: + PX_FORCE_INLINE PrismaticJointData& data() const + { + return *static_cast(mData); + } + + static PxConstraintShaderTable sShaders; + }; +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetPrismaticJointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp b/src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp new file mode 100644 index 000000000..ca1f40670 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp @@ -0,0 +1,165 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// suppress LNK4221 +#include "foundation/PxPreprocessor.h" +PX_DUMMY_SYMBOL + +#if PX_SUPPORT_PVD +#include "ExtPvd.h" +#include "PxExtensionMetaDataObjects.h" + +#include "ExtD6Joint.h" +#include "ExtFixedJoint.h" +#include "ExtSphericalJoint.h" +#include "ExtDistanceJoint.h" +#include "ExtSphericalJoint.h" +#include "ExtRevoluteJoint.h" +#include "ExtPrismaticJoint.h" +#include "ExtJointMetaDataExtensions.h" +#include "PvdMetaDataPropertyVisitor.h" +#include "PvdMetaDataDefineProperties.h" + +namespace physx +{ +namespace Ext +{ + using namespace physx::Vd; + + template + inline void visitPvdInstanceProperties( TOperator inOperator ) + { + PxClassInfoTraits().Info.visitInstanceProperties( makePvdPropertyFilter( inOperator ), 0 ); + } + + template + inline void visitPvdProperties( TOperator inOperator ) + { + PvdPropertyFilter theFilter( makePvdPropertyFilter( inOperator ) ); + PxU32 thePropCount = PxClassInfoTraits().Info.visitBaseProperties( theFilter ); + PxClassInfoTraits().Info.visitInstanceProperties( theFilter, thePropCount ); + } + + Pvd::PvdNameSpace::PvdNameSpace(physx::pvdsdk::PvdDataStream& conn, const char* /*name*/) + : mConnection(conn) + { + } + + Pvd::PvdNameSpace::~PvdNameSpace() + { + } + + void Pvd::releasePvdInstance(physx::pvdsdk::PvdDataStream& pvdConnection, const PxConstraint& c, const PxJoint& joint) + { + if(!pvdConnection.isConnected()) + return; + //remove from scene and from any attached actors. + PxRigidActor* actor0, *actor1; + c.getActors( actor0, actor1 ); + PxScene* scene = c.getScene(); + if(scene) pvdConnection.removeObjectRef( scene, "Joints", &joint ); + if ( actor0 && actor0->getScene() ) pvdConnection.removeObjectRef( actor0, "Joints", &joint ); + if ( actor1 && actor1->getScene()) pvdConnection.removeObjectRef( actor1, "Joints", &joint ); + pvdConnection.destroyInstance(&joint); + } + + template + void registerProperties( PvdDataStream& inStream ) + { + inStream.createClass(); + PvdPropertyDefinitionHelper& theHelper( inStream.getPropertyDefinitionHelper() ); + PvdClassInfoDefine theDefinitionObj( theHelper, getPvdNamespacedNameForType() ); + visitPvdInstanceProperties( theDefinitionObj ); + } + + template + void registerPropertiesAndValueStruct( PvdDataStream& inStream ) + { + inStream.createClass(); + inStream.deriveClass(); + PvdPropertyDefinitionHelper& theHelper( inStream.getPropertyDefinitionHelper() ); + { + PvdClassInfoDefine theDefinitionObj( theHelper, getPvdNamespacedNameForType() ); + visitPvdInstanceProperties( theDefinitionObj ); + } + { + PvdClassInfoValueStructDefine theDefinitionObj( theHelper ); + visitPvdProperties( theDefinitionObj ); + theHelper.addPropertyMessage(); + } + } + + void Pvd::sendClassDescriptions(physx::pvdsdk::PvdDataStream& inStream) + { + if (inStream.isClassExist()) + return; + + { //PxJoint + registerProperties( inStream ); + inStream.createProperty( "Parent", "parents" ); + registerPropertiesAndValueStruct( inStream); + registerPropertiesAndValueStruct(inStream); + registerPropertiesAndValueStruct( inStream); + registerPropertiesAndValueStruct( inStream); + registerPropertiesAndValueStruct( inStream); + registerPropertiesAndValueStruct( inStream); + registerPropertiesAndValueStruct( inStream); + } + } + + void Pvd::setActors( physx::pvdsdk::PvdDataStream& inStream, const PxJoint& inJoint, const PxConstraint& c, const PxActor* newActor0, const PxActor* newActor1 ) + { + PxRigidActor* actor0, *actor1; + c.getActors( actor0, actor1 ); + if ( actor0 ) + inStream.removeObjectRef( actor0, "Joints", &inJoint ); + if ( actor1 ) + inStream.removeObjectRef( actor1, "Joints", &inJoint ); + + if ( newActor0 && newActor0->getScene()) + inStream.pushBackObjectRef( newActor0, "Joints", &inJoint ); + if ( newActor1 && newActor1->getScene()) + inStream.pushBackObjectRef( newActor1, "Joints", &inJoint ); + + inStream.setPropertyValue( &inJoint, "Actors.actor0", reinterpret_cast(newActor0) ); + inStream.setPropertyValue( &inJoint, "Actors.actor1", reinterpret_cast(newActor1) ); + const void* parent = newActor0 ? newActor0 : newActor1; + inStream.setPropertyValue( &inJoint, "Parent", parent ); + + if((newActor0 && !newActor0->getScene()) || (newActor1 && !newActor1->getScene())) + { + inStream.removeObjectRef( c.getScene(), "Joints", &inJoint ); + } + + } +} + +} + +#endif // PX_SUPPORT_PVD diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPvd.h b/src/PhysX/physx/source/physxextensions/src/ExtPvd.h new file mode 100644 index 000000000..1f9d5796b --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPvd.h @@ -0,0 +1,191 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef EXT_PVD_H +#define EXT_PVD_H + +#if PX_SUPPORT_PVD + +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PxJoint.h" +#include "PxPvdDataStream.h" +#include "PxExtensionMetaDataObjects.h" +#include "PvdTypeNames.h" +#include "PxPvdObjectModelBaseTypes.h" + +namespace physx +{ + +class PxJoint; +class PxD6Joint; +class PxDistanceJoint; +class PxFixedJoint; +class PxPrismaticJoint; +class PxRevoluteJoint; +class PxSphericalJoint; +class PxContactJoint; +} + +#define JOINT_GROUP 3 +namespace physx +{ +namespace pvdsdk { + #define DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP( type ) DEFINE_PVD_TYPE_NAME_MAP( physx::type, "physx3", #type ) + + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxFixedJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxFixedJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxDistanceJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxDistanceJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxContactJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxContactJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPrismaticJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPrismaticJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRevoluteJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRevoluteJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSphericalJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSphericalJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxD6Joint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxD6JointGeneratedValues) +#undef DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP +} //pvdsdk +} // physx + +namespace physx +{ +namespace Ext +{ + using namespace physx::pvdsdk; + + class Pvd: public physx::shdfnd::UserAllocated + { + Pvd& operator=(const Pvd&); + public: + class PvdNameSpace + { + + public: + PvdNameSpace(PvdDataStream& conn, const char* name); + ~PvdNameSpace(); + private: + PvdNameSpace& operator=(const PvdNameSpace&); + PvdDataStream& mConnection; + }; + + static void setActors( PvdDataStream& PvdDataStream, + const PxJoint& inJoint, const PxConstraint& c, const PxActor* newActor0, const PxActor* newActor1 ); + + template + static void createInstance( PvdDataStream& inStream, const PxConstraint& c, const TObjType& inSource ) + { + inStream.createInstance( &inSource ); + inStream.pushBackObjectRef( c.getScene(), "Joints", &inSource ); + + class ConstraintUpdateCmd : public PvdDataStream::PvdCommand + { + ConstraintUpdateCmd &operator=(const ConstraintUpdateCmd&) { PX_ASSERT(0); return *this; } //PX_NOCOPY doesn't work for local classes + public: + + const PxConstraint& mConstraint; + const PxJoint& mJoint; + + PxRigidActor* actor0, *actor1; + ConstraintUpdateCmd(const PxConstraint& constraint, const PxJoint& joint):PvdDataStream::PvdCommand(), mConstraint(constraint), mJoint(joint) + { + mConstraint.getActors( actor0, actor1 ); + } + + //Assigned is needed for copying + ConstraintUpdateCmd(const ConstraintUpdateCmd& cmd) + :PvdDataStream::PvdCommand(), mConstraint(cmd.mConstraint), mJoint(cmd.mJoint) + { + } + + virtual bool canRun(PvdInstanceDataStream &inStream_ ) + { + PX_ASSERT(inStream_.isInstanceValid(&mJoint)); + //When run this command, the constraint maybe buffer removed + return ((actor0 == NULL) || inStream_.isInstanceValid(actor0)) + && ((actor1 == NULL) || inStream_.isInstanceValid(actor1)); + } + virtual void run( PvdInstanceDataStream &inStream_ ) + { + //When run this command, the constraint maybe buffer removed + if(!inStream_.isInstanceValid(&mJoint)) + return; + + PxRigidActor* actor0_, *actor1_; + mConstraint.getActors( actor0_, actor1_ ); + + if ( actor0_ && (inStream_.isInstanceValid(actor0_)) ) + inStream_.pushBackObjectRef( actor0_, "Joints", &mJoint ); + if ( actor1_ && (inStream_.isInstanceValid(actor1_)) ) + inStream_.pushBackObjectRef( actor1_, "Joints", &mJoint ); + const void* parent = actor0_ ? actor0_ : actor1_; + inStream_.setPropertyValue( &mJoint, "Parent", parent ); + } + }; + + ConstraintUpdateCmd* cmd = PX_PLACEMENT_NEW(inStream.allocateMemForCmd(sizeof(ConstraintUpdateCmd)), + ConstraintUpdateCmd)(c, inSource); + + if(cmd->canRun( inStream )) + cmd->run( inStream ); + else + inStream.pushPvdCommand( *cmd ); + } + + template + static void updatePvdProperties(PvdDataStream& pvdConnection, const jointtype& joint) + { + structValue theValueStruct( &joint ); + pvdConnection.setPropertyMessage( &joint, theValueStruct ); + } + + template + static void simUpdate(PvdDataStream& /*pvdConnection*/, const jointtype& /*joint*/) {} + + template + static void createPvdInstance(PvdDataStream& pvdConnection, const PxConstraint& c, const jointtype& joint) + { + createInstance( pvdConnection, c, joint ); + } + + static void releasePvdInstance(PvdDataStream& pvdConnection, const PxConstraint& c, const PxJoint& joint); + static void sendClassDescriptions(PvdDataStream& pvdConnection); + }; +} // ext + +} // physx + +#endif // PX_SUPPORT_PVD +#endif // EXT_PVD_H diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp b/src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp new file mode 100644 index 000000000..8d640a4ae --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxAllocatorCallback.h" +#include "PxStringTableExt.h" +#include "PxProfileAllocatorWrapper.h" //tools for using a custom allocator +#include "PsString.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + using namespace physx::profile; + + class PxStringTableImpl : public PxStringTable, public Ps::UserAllocated + { + typedef PxProfileHashMap THashMapType; + PxProfileAllocatorWrapper mWrapper; + THashMapType mHashMap; + public: + + PxStringTableImpl( PxAllocatorCallback& inAllocator ) + : mWrapper ( inAllocator ) + , mHashMap ( mWrapper ) + { + } + + virtual ~PxStringTableImpl() + { + for ( THashMapType::Iterator iter = mHashMap.getIterator(); + iter.done() == false; + ++iter ) + PX_PROFILE_DELETE( mWrapper, const_cast( iter->first ) ); + mHashMap.clear(); + } + + + virtual const char* allocateStr( const char* inSrc ) + { + if ( inSrc == NULL ) + inSrc = ""; + const THashMapType::Entry* existing( mHashMap.find( inSrc ) ); + if ( existing == NULL ) + { + size_t len( strlen( inSrc ) ); + len += 1; + char* newMem = reinterpret_cast(mWrapper.getAllocator().allocate( len, "PxStringTableImpl: const char*", __FILE__, __LINE__ )); + physx::shdfnd::strlcpy( newMem, len, inSrc ); + mHashMap.insert( newMem, 1 ); + return newMem; + } + else + { + ++const_cast(existing)->second; + return existing->first; + } + } + + /** + * Release the string table and all the strings associated with it. + */ + virtual void release() + { + PX_PROFILE_DELETE( mWrapper.getAllocator(), this ); + } + }; + + PxStringTable& PxStringTableExt::createStringTable( PxAllocatorCallback& inAllocator ) + { + return *PX_PROFILE_NEW( inAllocator, PxStringTableImpl )( inAllocator ); + } +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp new file mode 100644 index 000000000..64a97800d --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp @@ -0,0 +1,305 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxRaycastCCD.h" + +using namespace physx; + +#include "geometry/PxBoxGeometry.h" +#include "geometry/PxSphereGeometry.h" +#include "geometry/PxCapsuleGeometry.h" +#include "geometry/PxConvexMeshGeometry.h" +#include "geometry/PxConvexMesh.h" +#include "PxScene.h" +#include "PxRigidDynamic.h" +#include "extensions/PxShapeExt.h" +#include "PsArray.h" + +namespace physx +{ +class RaycastCCDManagerInternal +{ + PX_NOCOPY(RaycastCCDManagerInternal) + public: + RaycastCCDManagerInternal(PxScene* scene) : mScene(scene) {} + ~RaycastCCDManagerInternal(){} + + bool registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape); + + void doRaycastCCD(bool doDynamicDynamicCCD); + + struct CCDObject + { + PX_FORCE_INLINE CCDObject(PxRigidDynamic* actor, PxShape* shape, const PxVec3& witness) : mActor(actor), mShape(shape), mWitness(witness) {} + PxRigidDynamic* mActor; + PxShape* mShape; + PxVec3 mWitness; + }; + + private: + PxScene* mScene; + physx::shdfnd::Array mObjects; +}; +} + +static PxVec3 getShapeCenter(PxShape* shape, const PxTransform& pose) +{ + PxVec3 offset(0.0f); + if(shape->getGeometryType()==PxGeometryType::eCONVEXMESH) + { + PxConvexMeshGeometry geometry; + bool status = shape->getConvexMeshGeometry(geometry); + PX_UNUSED(status); + PX_ASSERT(status); + + PxReal mass; + PxMat33 localInertia; + PxVec3 localCenterOfMass; + geometry.convexMesh->getMassInformation(mass, localInertia, localCenterOfMass); + + offset += localCenterOfMass; + } + return pose.transform(offset); +} + +static PX_FORCE_INLINE PxVec3 getShapeCenter(PxRigidActor* actor, PxShape* shape) +{ + const PxTransform pose = PxShapeExt::getGlobalPose(*shape, *actor); + return getShapeCenter(shape, pose); +} + +static PxReal computeInternalRadius(PxRigidActor* actor, PxShape* shape, const PxVec3& dir) +{ + const PxBounds3 bounds = PxShapeExt::getWorldBounds(*shape, *actor); + const PxReal diagonal = (bounds.maximum - bounds.minimum).magnitude(); + const PxReal offsetFromOrigin = diagonal * 2.0f; + + PxTransform pose = PxShapeExt::getGlobalPose(*shape, *actor); + + PxReal internalRadius = 0.0f; + const PxReal length = offsetFromOrigin*2.0f; + + switch(shape->getGeometryType()) + { + case PxGeometryType::eSPHERE: + { + PxSphereGeometry geometry; + bool status = shape->getSphereGeometry(geometry); + PX_UNUSED(status); + PX_ASSERT(status); + + internalRadius = geometry.radius; + } + break; + + case PxGeometryType::eBOX: + case PxGeometryType::eCAPSULE: + { + pose.p = PxVec3(0.0f); + const PxVec3 virtualOrigin = pose.p + dir * offsetFromOrigin; + + PxRaycastHit hit; + PxU32 nbHits = PxGeometryQuery::raycast(virtualOrigin, -dir, shape->getGeometry().any(), pose, length, PxHitFlags(0), 1, &hit); + PX_UNUSED(nbHits); + PX_ASSERT(nbHits); + + internalRadius = offsetFromOrigin - hit.distance; + } + break; + + case PxGeometryType::eCONVEXMESH: + { + PxVec3 shapeCenter = getShapeCenter(shape, pose); + shapeCenter -= pose.p; + pose.p = PxVec3(0.0f); + + const PxVec3 virtualOrigin = shapeCenter + dir * offsetFromOrigin; + PxRaycastHit hit; + PxU32 nbHits = PxGeometryQuery::raycast(virtualOrigin, -dir, shape->getGeometry().any(), pose, length, PxHitFlags(0), 1, &hit); + PX_UNUSED(nbHits); + PX_ASSERT(nbHits); + + internalRadius = offsetFromOrigin - hit.distance; + } + break; + + case PxGeometryType::ePLANE: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + return internalRadius; +} + +class CCDRaycastFilterCallback : public PxQueryFilterCallback +{ +public: + CCDRaycastFilterCallback(PxRigidActor* actor, PxShape* shape) : mActor(actor), mShape(shape){} + + PxRigidActor* mActor; + PxShape* mShape; + + virtual PxQueryHitType::Enum preFilter(const PxFilterData&, const PxShape* shape, const PxRigidActor* actor, PxHitFlags&) + { + if(mActor==actor && mShape==shape) + return PxQueryHitType::eNONE; + return PxQueryHitType::eBLOCK; + } + + virtual PxQueryHitType::Enum postFilter(const PxFilterData&, const PxQueryHit&) + { + return PxQueryHitType::eNONE; + } +}; + +static bool CCDRaycast(PxScene* scene, PxRigidActor* actor, PxShape* shape, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, PxRaycastHit& hit, bool dyna_dyna) +{ + const PxQueryFlags qf(dyna_dyna ? PxQueryFlags(PxQueryFlag::eSTATIC|PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER) : PxQueryFlags(PxQueryFlag::eSTATIC)); + const PxQueryFilterData filterData(PxFilterData(), qf); + + CCDRaycastFilterCallback CB(actor, shape); + + PxRaycastBuffer buf1; + scene->raycast(origin, unitDir, distance, buf1, PxHitFlags(0), filterData, &CB); + hit = buf1.block; + return buf1.hasBlock; +} + +static PxRigidDynamic* canDoCCD(PxRigidActor& actor, PxShape* /*shape*/) +{ + if(actor.getConcreteType()!=PxConcreteType::eRIGID_DYNAMIC) + return NULL; // PT: no need to do it for statics + PxRigidDynamic* dyna = static_cast(&actor); + + const PxU32 nbShapes = dyna->getNbShapes(); + if(nbShapes!=1) + return NULL; // PT: only works with simple actors for now + + if(dyna->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC) + return NULL; // PT: no need to do it for kinematics + + return dyna; +} + +static bool doRaycastCCD(PxScene* scene, const RaycastCCDManagerInternal::CCDObject& object, PxTransform& newPose, PxVec3& newShapeCenter, bool dyna_dyna) +{ + PxRigidDynamic* dyna = canDoCCD(*object.mActor, object.mShape); + if(!dyna) + return true; + + bool updateCCDWitness = true; + + const PxVec3 offset = newPose.p - newShapeCenter; + const PxVec3& origin = object.mWitness; + const PxVec3& dest = newShapeCenter; + + PxVec3 dir = dest - origin; + const PxReal length = dir.magnitude(); + if(length!=0.0f) + { + dir /= length; + + const PxReal internalRadius = computeInternalRadius(object.mActor, object.mShape, dir); + + PxRaycastHit hit; + if(internalRadius!=0.0f && CCDRaycast(scene, object.mActor, object.mShape, origin, dir, length, hit, dyna_dyna)) + { + updateCCDWitness = false; + + const PxReal radiusLimit = internalRadius * 0.75f; + if(hit.distance>radiusLimit) + { + newShapeCenter = origin + dir * (hit.distance - radiusLimit); + } + else + { + if(hit.actor->getConcreteType()==PxConcreteType::eRIGID_DYNAMIC) + return true; + + newShapeCenter = origin; + } + + newPose.p = offset + newShapeCenter; + const PxTransform shapeLocalPose = object.mShape->getLocalPose(); + const PxTransform inverseShapeLocalPose = shapeLocalPose.getInverse(); + const PxTransform newGlobalPose = newPose * inverseShapeLocalPose; + dyna->setGlobalPose(newGlobalPose); + } + } + return updateCCDWitness; +} + +bool RaycastCCDManagerInternal::registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape) +{ + if(!actor || !shape) + return false; + + mObjects.pushBack(CCDObject(actor, shape, getShapeCenter(actor, shape))); + return true; +} + +void RaycastCCDManagerInternal::doRaycastCCD(bool doDynamicDynamicCCD) +{ + const PxU32 nbObjects = mObjects.size(); + for(PxU32 i=0;iisSleeping()) + continue; + + PxTransform newPose = PxShapeExt::getGlobalPose(*object.mShape, *object.mActor); + PxVec3 newShapeCenter = getShapeCenter(object.mShape, newPose); + + if(::doRaycastCCD(mScene, object, newPose, newShapeCenter, doDynamicDynamicCCD)) + object.mWitness = newShapeCenter; + } +} + +RaycastCCDManager::RaycastCCDManager(PxScene* scene) +{ + mImpl = new RaycastCCDManagerInternal(scene); +} + +RaycastCCDManager::~RaycastCCDManager() +{ + delete mImpl; +} + +bool RaycastCCDManager::registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape) +{ + return mImpl->registerRaycastCCDObject(actor, shape); +} + +void RaycastCCDManager::doRaycastCCD(bool doDynamicDynamicCCD) +{ + mImpl->doRaycastCCD(doDynamicDynamicCCD); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp new file mode 100644 index 000000000..39819b6a3 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp @@ -0,0 +1,345 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtRevoluteJoint.h" +#include "PsUtilities.h" +#include "ExtConstraintHelper.h" +#include "CmRenderOutput.h" +#include "PsMathUtils.h" +#include "CmVisualization.h" +#include "CmUtils.h" + +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxRevoluteJoint* physx::PxRevoluteJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxRevoluteJointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxRevoluteJointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxRevoluteJointCreate: actors must be different"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxRevoluteJointCreate: at least one actor must be dynamic"); + + RevoluteJoint* j; + PX_NEW_SERIALIZED(j, RevoluteJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +PxReal RevoluteJoint::getAngle() const +{ + return getTwistAngle_Internal(); +} + +PxReal RevoluteJoint::getVelocity() const +{ + return getRelativeAngularVelocity().magnitude(); +} + +PxJointAngularLimitPair RevoluteJoint::getLimit() const +{ + return data().limit; +} + +void RevoluteJoint::setLimit(const PxJointAngularLimitPair& limit) +{ + PX_CHECK_AND_RETURN(limit.isValid(), "PxRevoluteJoint::setLimit: limit invalid"); + PX_CHECK_AND_RETURN(limit.lower>-PxTwoPi && limit.upper0, "PxRevoluteJoint::setDriveGearRatio: invalid parameter"); + data().driveGearRatio = gearRatio; + markDirty(); +} + +void RevoluteJoint::setProjectionAngularTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance>=0 && tolerance<=PxPi, "PxRevoluteJoint::setProjectionAngularTolerance: invalid parameter"); + data().projectionAngularTolerance = tolerance; + markDirty(); +} + +PxReal RevoluteJoint::getProjectionAngularTolerance() const +{ + return data().projectionAngularTolerance; +} + +void RevoluteJoint::setProjectionLinearTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxRevoluteJoint::setProjectionLinearTolerance: invalid parameter"); + data().projectionLinearTolerance = tolerance; + markDirty(); +} + +PxReal RevoluteJoint::getProjectionLinearTolerance() const +{ + return data().projectionLinearTolerance; +} + +PxRevoluteJointFlags RevoluteJoint::getRevoluteJointFlags(void) const +{ + return data().jointFlags; +} + +void RevoluteJoint::setRevoluteJointFlags(PxRevoluteJointFlags flags) +{ + data().jointFlags = flags; +} + +void RevoluteJoint::setRevoluteJointFlag(PxRevoluteJointFlag::Enum flag, bool value) +{ + if(value) + data().jointFlags |= flag; + else + data().jointFlags &= ~flag; + markDirty(); +} + +bool RevoluteJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(RevoluteJointData)); + return mPxConstraint!=NULL; +} + +void RevoluteJoint::exportExtraData(PxSerializationContext& stream) +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(RevoluteJointData)); + } + stream.writeName(mName); +} + +void RevoluteJoint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + context.readName(mName); +} + +void RevoluteJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +RevoluteJoint* RevoluteJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + RevoluteJoint* obj = new (address) RevoluteJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(RevoluteJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetRevoluteJointShaderTable() +{ + return &RevoluteJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void RevoluteJointProject(const void* constantBlock, PxTransform& bodyAToWorld, PxTransform& bodyBToWorld, bool projectToA) +{ + const RevoluteJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w, cB2cA, projected; + joint::computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA, false); + + bool linearTrunc, angularTrunc; + projected.p = joint::truncateLinear(cB2cA.p, data.projectionLinearTolerance, linearTrunc); + + PxQuat swing, twist, projSwing; + Ps::separateSwingTwist(cB2cA.q, swing, twist); + projSwing = joint::truncateAngular(swing, PxSin(data.projectionAngularTolerance/2), PxCos(data.projectionAngularTolerance/2), angularTrunc); + + if(linearTrunc || angularTrunc) + { + projected.q = projSwing * twist; + joint::projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA); + } +} + +static PxQuat computeTwist(const PxTransform& cA2w, const PxTransform& cB2w) +{ + // PT: following code is the same as this part of the "getAngle" function: + // const PxQuat q = getRelativeTransform().q; + // PxQuat swing, twist; + // Ps::separateSwingTwist(q, swing, twist); + // But it's done a little bit more efficiently since we don't need the swing quat. + + // PT: rotation part of "const PxTransform cB2cA = cA2w.transformInv(cB2w);" + const PxQuat cB2cAq = cA2w.q.getConjugate() * cB2w.q; + + // PT: twist part of "Ps::separateSwingTwist(cB2cAq,swing,twist)" (more or less) + return PxQuat(cB2cAq.x, 0.0f, 0.0f, cB2cAq.w); +} + +// PT: this version is similar to the "getAngle" function, but the twist is computed slightly differently. +static PX_FORCE_INLINE PxReal computePhi(const PxTransform& cA2w, const PxTransform& cB2w) +{ + PxQuat twist = computeTwist(cA2w, cB2w); + twist.normalize(); + + PxReal angle = twist.getAngle(); + if(twist.x<0.0f) + angle = -angle; + return angle; +} + +static void RevoluteJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags) +{ + const RevoluteJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + viz.visualizeJointFrames(cA2w, cB2w); + + if((data.jointFlags & PxRevoluteJointFlag::eLIMIT_ENABLED) && (flags & PxConstraintVisualizationFlag::eLIMITS)) + { + const PxReal angle = computePhi(cA2w, cB2w); + const PxReal pad = data.limit.contactDistance; + const PxReal low = data.limit.lower; + const PxReal high = data.limit.upper; + + const bool active = isLimitActive(data.limit, pad, angle, low, high); + viz.visualizeAngularLimit(cA2w, data.limit.lower, data.limit.upper, active); + } +} + +static PxU32 RevoluteJointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool useExtendedLimits, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const RevoluteJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + const PxJointAngularLimitPair& limit = data.limit; + + const bool limitEnabled = data.jointFlags & PxRevoluteJointFlag::eLIMIT_ENABLED; + const bool limitIsLocked = limitEnabled && limit.lower >= limit.upper; + + // PT: it is a mistake to use the neighborhood operator since it + // prevents us from using the quat's double-cover feature. + if(!useExtendedLimits && cB2w.q.dot(cA2w.q)<0.0f) + cB2w.q = -cB2w.q; + + PxVec3 ra, rb; + ch.prepareLockedAxes(cA2w.q, cB2w.q, cA2w.transformInv(cB2w.p), 7, PxU32(limitIsLocked ? 7 : 6), ra, rb); + cA2wOut = ra + bA2w.p; + cB2wOut = rb + bB2w.p; + + if(limitIsLocked) + return ch.getCount(); + + const PxVec3 axis = cA2w.rotate(PxVec3(1.0f, 0.0f, 0.0f)); + + if(data.jointFlags & PxRevoluteJointFlag::eDRIVE_ENABLED) + { + Px1DConstraint* c = ch.getConstraintRow(); + + c->solveHint = PxConstraintSolveHint::eNONE; + c->linear0 = PxVec3(0.0f); + c->angular0 = -axis; + c->linear1 = PxVec3(0.0f); + c->angular1 = -axis * data.driveGearRatio; + c->velocityTarget = data.driveVelocity; + c->minImpulse = -data.driveForceLimit; + c->maxImpulse = data.driveForceLimit; + c->flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT; + if(data.jointFlags & PxRevoluteJointFlag::eDRIVE_FREESPIN) + { + if(data.driveVelocity > 0.0f) + c->minImpulse = 0.0f; + if(data.driveVelocity < 0.0f) + c->maxImpulse = 0.0f; + } + c->flags |= Px1DConstraintFlag::eHAS_DRIVE_LIMIT; + } + + if(limitEnabled) + { + const PxReal phi = computePhi(cA2w, cB2w); + ch.anglePair(phi, data.limit.lower, data.limit.upper, data.limit.contactDistance, axis, limit); + } + + return ch.getCount(); +} + +PxConstraintShaderTable Ext::RevoluteJoint::sShaders = { RevoluteJointSolverPrep, RevoluteJointProject, RevoluteJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h new file mode 100644 index 000000000..36fa1eefe --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h @@ -0,0 +1,153 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_REVOLUTEJOINTCONSTRAINT_H +#define NP_REVOLUTEJOINTCONSTRAINT_H + +#include "ExtJoint.h" +#include "PxRevoluteJoint.h" +#include "PsIntrinsics.h" +#include "CmUtils.h" + +namespace physx +{ + +class PxConstraintSolverPrepKernel; +class PxConstraintProjectionKernel; +struct PxRevoluteJointGeneratedValues; + +namespace Ext +{ + struct RevoluteJointData : public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxReal driveVelocity; + PxReal driveForceLimit; + PxReal driveGearRatio; + + PxJointAngularLimitPair limit; + + PxReal projectionLinearTolerance; + PxReal projectionAngularTolerance; + + PxRevoluteJointFlags jointFlags; + // forestall compiler complaints about not being able to generate a constructor + private: + RevoluteJointData(const PxJointAngularLimitPair &pair): + limit(pair) {} + }; + + typedef Joint RevoluteJointT; + + class RevoluteJoint : public RevoluteJointT + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + RevoluteJoint(PxBaseFlags baseFlags) : RevoluteJointT(baseFlags) {} + void resolveReferences(PxDeserializationContext& context); + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + static RevoluteJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + RevoluteJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + RevoluteJointT(PxJointConcreteType::eREVOLUTE, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(RevoluteJointData), "RevoluteJointData") + { + RevoluteJointData* data = static_cast(mData); + + data->projectionLinearTolerance = 1e10f; + data->projectionAngularTolerance = PxPi; + data->driveForceLimit = PX_MAX_F32; + data->driveVelocity = 0.0f; + data->driveGearRatio = 1.0f; + data->limit = PxJointAngularLimitPair(-PxPi/2, PxPi/2); + data->jointFlags = PxRevoluteJointFlags(); + } + + // PxRevoluteJoint + virtual PxReal getAngle() const; + virtual PxReal getVelocity() const; + virtual void setLimit(const PxJointAngularLimitPair& limit); + virtual PxJointAngularLimitPair getLimit() const; + virtual void setDriveVelocity(PxReal velocity, bool autowake = true); + virtual PxReal getDriveVelocity() const; + virtual void setDriveForceLimit(PxReal forceLimit); + virtual PxReal getDriveForceLimit() const; + virtual void setDriveGearRatio(PxReal gearRatio); + virtual PxReal getDriveGearRatio() const; + virtual void setRevoluteJointFlags(PxRevoluteJointFlags flags); + virtual void setRevoluteJointFlag(PxRevoluteJointFlag::Enum flag, bool value); + virtual PxRevoluteJointFlags getRevoluteJointFlags() const; + virtual void setProjectionLinearTolerance(PxReal distance); + virtual PxReal getProjectionLinearTolerance() const; + virtual void setProjectionAngularTolerance(PxReal tolerance); + virtual PxReal getProjectionAngularTolerance() const; + //~PxRevoluteJoint + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep() const { return sShaders.solverPrep; } + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE RevoluteJointData& data() const + { + return *static_cast(mData); + } + + }; + +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetRevoluteJointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp new file mode 100644 index 000000000..acfe736f6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp @@ -0,0 +1,71 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxRigidActorExt.h" +#include "PsAllocator.h" +#include "PsInlineArray.h" +#include "CmPhysXCommon.h" +#include "PxShape.h" +#include "PxGeometryQuery.h" + +using namespace physx; + +PxBounds3* PxRigidActorExt::getRigidActorShapeLocalBoundsList(const PxRigidActor& actor, PxU32& numBounds) +{ + const PxU32 numShapes = actor.getNbShapes(); + if(numShapes == 0) + return NULL; + + Ps::InlineArray shapes("PxShape*"); + shapes.resize(numShapes); + + actor.getShapes(shapes.begin(), shapes.size()); + + PxU32 numSqShapes = 0; + for(PxU32 i = 0; i < numShapes; i++) + { + if(shapes[i]->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE) + numSqShapes++; + } + + PxBounds3* bounds = reinterpret_cast(PX_ALLOC(numSqShapes * sizeof(PxBounds3), "PxBounds3")); + + numSqShapes = 0; + for(PxU32 i = 0; i < numShapes; i++) + { + if(shapes[i]->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE) + { + bounds[numSqShapes++] = PxGeometryQuery::getWorldBounds(shapes[i]->getGeometry().any(), shapes[i]->getLocalPose(), 1.0f); + } + } + + numBounds = numSqShapes; + return bounds; +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp new file mode 100644 index 000000000..3f8e79259 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp @@ -0,0 +1,648 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxRigidBodyExt.h" +#include "PxShapeExt.h" +#include "PxMassProperties.h" + +#include "ExtInertiaTensor.h" +#include "PsAllocator.h" +#include "PsFoundation.h" + +#include "PxShape.h" +#include "PxScene.h" + +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxTriangleMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "PxGeometryHelpers.h" + +#include "PxConvexMesh.h" + +#include "PxBatchQuery.h" + +#include "PxRigidDynamic.h" +#include "PxRigidStatic.h" +#include "CmUtils.h" + +using namespace physx; +using namespace Cm; + +static bool computeMassAndDiagInertia(Ext::InertiaTensorComputer& inertiaComp, + PxVec3& diagTensor, PxQuat& orient, PxReal& massOut, PxVec3& coM, bool lockCOM, const PxRigidBody& body, const char* errorStr) +{ + // The inertia tensor and center of mass is relative to the actor at this point. Transform to the + // body frame directly if CoM is specified, else use computed center of mass + if (lockCOM) + { + inertiaComp.translate(-coM); // base the tensor on user's desired center of mass. + } + else + { + //get center of mass - has to be done BEFORE centering. + coM = inertiaComp.getCenterOfMass(); + + //the computed result now needs to be centered around the computed center of mass: + inertiaComp.center(); + } + // The inertia matrix is now based on the body's center of mass desc.massLocalPose.p + + massOut = inertiaComp.getMass(); + diagTensor = PxDiagonalize(inertiaComp.getInertia(), orient); + + if ((diagTensor.x > 0.0f) && (diagTensor.y > 0.0f) && (diagTensor.z > 0.0f)) + return true; + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "%s: inertia tensor has negative components (ill-conditioned input expected). Approximation for inertia tensor will be used instead.", errorStr); + + // keep center of mass but use the AABB as a crude approximation for the inertia tensor + PxBounds3 bounds = body.getWorldBounds(); + PxTransform pose = body.getGlobalPose(); + bounds = PxBounds3::transformFast(pose.getInverse(), bounds); + Ext::InertiaTensorComputer it(false); + it.setBox(bounds.getExtents()); + it.scaleDensity(massOut / it.getMass()); + PxMat33 inertia = it.getInertia(); + diagTensor = PxVec3(inertia.column0.x, inertia.column1.y, inertia.column2.z); + orient = PxQuat(PxIdentity); + + return true; + } +} + +static bool computeMassAndInertia(bool multipleMassOrDensity, PxRigidBody& body, const PxReal* densities, const PxReal* masses, PxU32 densityOrMassCount, bool includeNonSimShapes, Ext::InertiaTensorComputer& computer) +{ + PX_ASSERT(!densities || !masses); + PX_ASSERT((densities || masses) && (densityOrMassCount > 0)); + + Ext::InertiaTensorComputer inertiaComp(true); + + Ps::InlineArray shapes("PxShape*"); shapes.resize(body.getNbShapes()); + + body.getShapes(shapes.begin(), shapes.size()); + + PxU32 validShapeIndex = 0; + PxReal currentMassOrDensity; + const PxReal* massOrDensityArray; + if (densities) + { + massOrDensityArray = densities; + currentMassOrDensity = densities[0]; + } + else + { + massOrDensityArray = masses; + currentMassOrDensity = masses[0]; + } + if (!PxIsFinite(currentMassOrDensity)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "computeMassAndInertia: Provided mass or density has no valid value"); + return false; + } + + for(PxU32 i=0; i < shapes.size(); i++) + { + if ((!(shapes[i]->getFlags() & PxShapeFlag::eSIMULATION_SHAPE)) && (!includeNonSimShapes)) + continue; + + if (multipleMassOrDensity) + { + if (validShapeIndex < densityOrMassCount) + { + currentMassOrDensity = massOrDensityArray[validShapeIndex]; + + if (!PxIsFinite(currentMassOrDensity)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "computeMassAndInertia: Provided mass or density has no valid value"); + return false; + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "computeMassAndInertia: Not enough mass/density values provided for all (simulation) shapes"); + return false; + } + } + + Ext::InertiaTensorComputer it(false); + + switch(shapes[i]->getGeometryType()) + { + case PxGeometryType::eSPHERE : + { + PxSphereGeometry g; + bool ok = shapes[i]->getSphereGeometry(g); + PX_ASSERT(ok); + PX_UNUSED(ok); + PxTransform temp(shapes[i]->getLocalPose()); + + it.setSphere(g.radius, &temp); + } + break; + + case PxGeometryType::eBOX : + { + PxBoxGeometry g; + bool ok = shapes[i]->getBoxGeometry(g); + PX_ASSERT(ok); + PX_UNUSED(ok); + PxTransform temp(shapes[i]->getLocalPose()); + + it.setBox(g.halfExtents, &temp); + } + break; + + case PxGeometryType::eCAPSULE : + { + PxCapsuleGeometry g; + bool ok = shapes[i]->getCapsuleGeometry(g); + PX_ASSERT(ok); + PX_UNUSED(ok); + PxTransform temp(shapes[i]->getLocalPose()); + + it.setCapsule(0, g.radius, g.halfHeight, &temp); + } + break; + + case PxGeometryType::eCONVEXMESH : + { + PxConvexMeshGeometry g; + bool ok = shapes[i]->getConvexMeshGeometry(g); + PX_ASSERT(ok); + PX_UNUSED(ok); + PxConvexMesh& convMesh = *g.convexMesh; + + PxReal convMass; + PxMat33 convInertia; + PxVec3 convCoM; + convMesh.getMassInformation(convMass, reinterpret_cast(convInertia), convCoM); + + if (!g.scale.isIdentity()) + { + //scale the mass properties + convMass *= (g.scale.scale.x * g.scale.scale.y * g.scale.scale.z); + convCoM = g.scale.rotation.rotateInv(g.scale.scale.multiply(g.scale.rotation.rotate(convCoM))); + convInertia = PxMassProperties::scaleInertia(convInertia, g.scale.rotation, g.scale.scale); + } + + it = Ext::InertiaTensorComputer(convInertia, convCoM, convMass); + it.transform(shapes[i]->getLocalPose()); + } + break; + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eINVALID: + case PxGeometryType::eGEOMETRY_COUNT: + { + + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "computeMassAndInertia: Dynamic actor with illegal collision shapes"); + return false; + } + } + + if (densities) + it.scaleDensity(currentMassOrDensity); + else if (multipleMassOrDensity) // mass per shape -> need to scale density per shape + it.scaleDensity(currentMassOrDensity / it.getMass()); + + inertiaComp.add(it); + + validShapeIndex++; + } + + if (validShapeIndex && masses && (!multipleMassOrDensity)) // at least one simulation shape and single mass for all shapes -> scale density at the end + { + inertiaComp.scaleDensity(currentMassOrDensity / inertiaComp.getMass()); + } + + computer = inertiaComp; + return true; +} + +static bool updateMassAndInertia(bool multipleMassOrDensity, PxRigidBody& body, const PxReal* densities, PxU32 densityCount, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + bool success; + + // default values in case there were no shapes + PxReal massOut = 1.0f; + PxVec3 diagTensor(1.f,1.f,1.f); + PxQuat orient = PxQuat(PxIdentity); + bool lockCom = massLocalPose != NULL; + PxVec3 com = lockCom ? *massLocalPose : PxVec3(0); + const char* errorStr = "PxRigidBodyExt::updateMassAndInertia"; + + if (densities && densityCount) + { + Ext::InertiaTensorComputer inertiaComp(true); + if(computeMassAndInertia(multipleMassOrDensity, body, densities, NULL, densityCount, includeNonSimShapes, inertiaComp)) + { + if(inertiaComp.getMass()!=0 && computeMassAndDiagInertia(inertiaComp, diagTensor, orient, massOut, com, lockCom, body, errorStr)) + success = true; + else + success = false; // body with no shapes provided or computeMassAndDiagInertia() failed + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: Mass and inertia computation failed, setting mass to 1 and inertia to (1,1,1)", errorStr); + + success = false; + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: No density specified, setting mass to 1 and inertia to (1,1,1)", errorStr); + + success = false; + } + + PX_ASSERT(orient.isFinite()); + PX_ASSERT(diagTensor.isFinite()); + PX_ASSERT(PxIsFinite(massOut)); + + body.setMass(massOut); + body.setMassSpaceInertiaTensor(diagTensor); + body.setCMassLocalPose(PxTransform(com, orient)); + + return success; +} + +bool PxRigidBodyExt::updateMassAndInertia(PxRigidBody& body, const PxReal* densities, PxU32 densityCount, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + return ::updateMassAndInertia(true, body, densities, densityCount, massLocalPose, includeNonSimShapes); +} + +bool PxRigidBodyExt::updateMassAndInertia(PxRigidBody& body, PxReal density, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + return ::updateMassAndInertia(false, body, &density, 1, massLocalPose, includeNonSimShapes); +} + +static bool setMassAndUpdateInertia(bool multipleMassOrDensity, PxRigidBody& body, const PxReal* masses, PxU32 massCount, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + bool success; + + // default values in case there were no shapes + PxReal massOut = 1.0f; + PxVec3 diagTensor(1.0f,1.0f,1.0f); + PxQuat orient = PxQuat(PxIdentity); + bool lockCom = massLocalPose != NULL; + PxVec3 com = lockCom ? *massLocalPose : PxVec3(0); + const char* errorStr = "PxRigidBodyExt::setMassAndUpdateInertia"; + + if(masses && massCount) + { + Ext::InertiaTensorComputer inertiaComp(true); + if(computeMassAndInertia(multipleMassOrDensity, body, NULL, masses, massCount, includeNonSimShapes, inertiaComp)) + { + success = true; + + if (inertiaComp.getMass()!=0 && !computeMassAndDiagInertia(inertiaComp, diagTensor, orient, massOut, com, lockCom, body, errorStr)) + success = false; // computeMassAndDiagInertia() failed (mass zero?) + + if (massCount == 1) + massOut = masses[0]; // to cover special case where body has no simulation shape + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: Mass and inertia computation failed, setting mass to 1 and inertia to (1,1,1)", errorStr); + + success = false; + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: No mass specified, setting mass to 1 and inertia to (1,1,1)", errorStr); + success = false; + } + + PX_ASSERT(orient.isFinite()); + PX_ASSERT(diagTensor.isFinite()); + + body.setMass(massOut); + body.setMassSpaceInertiaTensor(diagTensor); + body.setCMassLocalPose(PxTransform(com, orient)); + + return success; +} + +bool PxRigidBodyExt::setMassAndUpdateInertia(PxRigidBody& body, const PxReal* masses, PxU32 massCount, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + return ::setMassAndUpdateInertia(true, body, masses, massCount, massLocalPose, includeNonSimShapes); +} + +bool PxRigidBodyExt::setMassAndUpdateInertia(PxRigidBody& body, PxReal mass, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + return ::setMassAndUpdateInertia(false, body, &mass, 1, massLocalPose, includeNonSimShapes); +} + +PxMassProperties PxRigidBodyExt::computeMassPropertiesFromShapes(const PxShape* const* shapes, PxU32 shapeCount) +{ + Ps::InlineArray massProps; + massProps.reserve(shapeCount); + Ps::InlineArray localTransforms; + localTransforms.reserve(shapeCount); + + for(PxU32 shapeIdx=0; shapeIdx < shapeCount; shapeIdx++) + { + const PxShape* shape = shapes[shapeIdx]; + PxMassProperties mp(shape->getGeometry().any()); + massProps.pushBack(mp); + localTransforms.pushBack(shape->getLocalPose()); + } + + return PxMassProperties::sum(massProps.begin(), localTransforms.begin(), shapeCount); +} + +PX_INLINE void addForceAtPosInternal(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup) +{ + if(mode == PxForceMode::eACCELERATION || mode == PxForceMode::eVELOCITY_CHANGE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxRigidBodyExt::addForce methods do not support eACCELERATION or eVELOCITY_CHANGE modes"); + return; + } + + const PxTransform globalPose = body.getGlobalPose(); + const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p); + + const PxVec3 torque = (pos - centerOfMass).cross(force); + body.addForce(force, mode, wakeup); + body.addTorque(torque, mode, wakeup); +} + +void PxRigidBodyExt::addForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup) +{ + addForceAtPosInternal(body, force, pos, mode, wakeup); +} + +void PxRigidBodyExt::addForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup) +{ + //transform pos to world space + const PxVec3 globalForcePos = body.getGlobalPose().transform(pos); + + addForceAtPosInternal(body, force, globalForcePos, mode, wakeup); +} + +void PxRigidBodyExt::addLocalForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup) +{ + const PxVec3 globalForce = body.getGlobalPose().rotate(force); + + addForceAtPosInternal(body, globalForce, pos, mode, wakeup); +} + +void PxRigidBodyExt::addLocalForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup) +{ + const PxTransform globalPose = body.getGlobalPose(); + const PxVec3 globalForcePos = globalPose.transform(pos); + const PxVec3 globalForce = globalPose.rotate(force); + + addForceAtPosInternal(body, globalForce, globalForcePos, mode, wakeup); +} + +PX_INLINE PxVec3 getVelocityAtPosInternal(const PxRigidBody& body, const PxVec3& point) +{ + PxVec3 velocity = body.getLinearVelocity(); + velocity += body.getAngularVelocity().cross(point); + + return velocity; +} + +PxVec3 PxRigidBodyExt::getVelocityAtPos(const PxRigidBody& body, const PxVec3& point) +{ + const PxTransform globalPose = body.getGlobalPose(); + const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p); + const PxVec3 rpoint = point - centerOfMass; + + return getVelocityAtPosInternal(body, rpoint); +} + +PxVec3 PxRigidBodyExt::getLocalVelocityAtLocalPos(const PxRigidBody& body, const PxVec3& point) +{ + const PxTransform globalPose = body.getGlobalPose(); + const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p); + const PxVec3 rpoint = globalPose.transform(point) - centerOfMass; + + return getVelocityAtPosInternal(body, rpoint); +} + +PxVec3 PxRigidBodyExt::getVelocityAtOffset(const PxRigidBody& body, const PxVec3& point) +{ + const PxTransform globalPose = body.getGlobalPose(); + const PxVec3 centerOfMass = globalPose.rotate(body.getCMassLocalPose().p); + const PxVec3 rpoint = point - centerOfMass; + + return getVelocityAtPosInternal(body, rpoint); +} + +void PxRigidBodyExt::computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale, + const PxReal invInertiaScale, PxVec3& linearVelocityChange, PxVec3& angularVelocityChange) +{ + const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p); + const PxReal invMass = body.getInvMass() * invMassScale; + const PxVec3 invInertiaMS = body.getMassSpaceInvInertiaTensor() * invInertiaScale; + + PxMat33 invInertia; + transformInertiaTensor(invInertiaMS, PxMat33(globalPose.q), invInertia); + linearVelocityChange = impulse * invMass; + const PxVec3 rXI = (point - centerOfMass).cross(impulse); + angularVelocityChange = invInertia * rXI; +} + +void PxRigidBodyExt::computeLinearAngularImpulse(const PxRigidBody& body, const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale, + const PxReal invInertiaScale, PxVec3& linearImpulse, PxVec3& angularImpulse) +{ + const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p); + linearImpulse = impulse * invMassScale; + angularImpulse = (point - centerOfMass).cross(impulse) * invInertiaScale; +} + + + +//================================================================================= +// Single closest hit compound sweep +bool PxRigidBodyExt::linearSweepSingle( + PxRigidBody& body, PxScene& scene, const PxVec3& unitDir, const PxReal distance, + PxHitFlags outputFlags, PxSweepHit& closestHit, PxU32& shapeIndex, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache, const PxReal inflation) +{ + shapeIndex = 0xFFFFffff; + PxReal closestDist = distance; + PxU32 nbShapes = body.getNbShapes(); + for(PxU32 i=0; i < nbShapes; i++) + { + PxShape* shape = NULL; + body.getShapes(&shape, 1, i); + PX_ASSERT(shape != NULL); + PxTransform pose = PxShapeExt::getGlobalPose(*shape, body); + PxQueryFilterData fd; + fd.flags = filterData.flags; + PxU32 or4 = (filterData.data.word0 | filterData.data.word1 | filterData.data.word2 | filterData.data.word3); + fd.data = or4 ? filterData.data : shape->getQueryFilterData(); + PxGeometryHolder anyGeom = shape->getGeometry(); + + PxSweepBuffer subHit; // touching hits are not allowed to be returned from the filters + scene.sweep(anyGeom.any(), pose, unitDir, distance, subHit, outputFlags, fd, filterCall, cache, inflation); + if (subHit.hasBlock && subHit.block.distance < closestDist) + { + closestDist = subHit.block.distance; + closestHit = subHit.block; + shapeIndex = i; + } + } + + return (shapeIndex != 0xFFFFffff); +} + +//================================================================================= +// Multiple hits compound sweep +// AP: we might be able to improve the return results API but no time for it in 3.3 +PxU32 PxRigidBodyExt::linearSweepMultiple( + PxRigidBody& body, PxScene& scene, const PxVec3& unitDir, const PxReal distance, PxHitFlags outputFlags, + PxSweepHit* hitBuffer, PxU32* hitShapeIndices, PxU32 hitBufferSize, PxSweepHit& block, PxI32& blockingHitShapeIndex, + bool& overflow, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache, const PxReal inflation) +{ + overflow = false; + blockingHitShapeIndex = -1; + + for (PxU32 i = 0; i < hitBufferSize; i++) + hitShapeIndices[i] = 0xFFFFffff; + + PxI32 sumNbResults = 0; + + PxU32 nbShapes = body.getNbShapes(); + PxF32 shrunkMaxDistance = distance; + for(PxU32 i=0; i < nbShapes; i++) + { + PxShape* shape = NULL; + body.getShapes(&shape, 1, i); + PX_ASSERT(shape != NULL); + PxTransform pose = PxShapeExt::getGlobalPose(*shape, body); + PxQueryFilterData fd; + fd.flags = filterData.flags; + PxU32 or4 = (filterData.data.word0 | filterData.data.word1 | filterData.data.word2 | filterData.data.word3); + fd.data = or4 ? filterData.data : shape->getQueryFilterData(); + PxGeometryHolder anyGeom = shape->getGeometry(); + + PxU32 bufSizeLeft = hitBufferSize-sumNbResults; + PxSweepHit extraHit; + PxSweepBuffer buffer(bufSizeLeft == 0 ? &extraHit : hitBuffer+sumNbResults, bufSizeLeft == 0 ? 1 : hitBufferSize-sumNbResults); + scene.sweep(anyGeom.any(), pose, unitDir, shrunkMaxDistance, buffer, outputFlags, fd, filterCall, cache, inflation); + + // Check and abort on overflow. Assume overflow if result count is bufSize. + PxU32 nbNewResults = buffer.getNbTouches(); + overflow |= (nbNewResults >= bufSizeLeft); + if (bufSizeLeft == 0) // this is for when we used the extraHit buffer + nbNewResults = 0; + + // set hitShapeIndices for each new non-blocking hit + for (PxU32 j = 0; j < nbNewResults; j++) + if (sumNbResults + PxU32(j) < hitBufferSize) + hitShapeIndices[sumNbResults+j] = i; + + if (buffer.hasBlock) // there's a blocking hit in the most recent sweepMultiple results + { + // overwrite the return result blocking hit with the new blocking hit if under + if (blockingHitShapeIndex == -1 || buffer.block.distance < block.distance) + { + blockingHitShapeIndex = PxI32(i); + block = buffer.block; + } + + // Remove all the old touching hits below the new maxDist + // sumNbResults is not updated yet at this point + // and represents the count accumulated so far excluding the very last query + PxI32 nbNewResultsSigned = PxI32(nbNewResults); // need a signed version, see nbNewResultsSigned-- below + for (PxI32 j = sumNbResults-1; j >= 0; j--) // iterate over "old" hits (up to shapeIndex-1) + if (buffer.block.distance < hitBuffer[j].distance) + { + // overwrite with last "new" hit + PxI32 sourceIndex = PxI32(sumNbResults)+nbNewResultsSigned-1; PX_ASSERT(sourceIndex >= j); + hitBuffer[j] = hitBuffer[sourceIndex]; + hitShapeIndices[j] = hitShapeIndices[sourceIndex]; + nbNewResultsSigned--; // can get negative, that means we are shifting the last results array + } + + sumNbResults += nbNewResultsSigned; + } else // if there was no new blocking hit we don't need to do anything special, simply append all results to touch array + sumNbResults += nbNewResults; + + PX_ASSERT(sumNbResults >= 0 && sumNbResults <= PxI32(hitBufferSize)); + } + + return PxU32(sumNbResults); +} + +void PxRigidBodyExt::computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxVec3& impulsiveForce, const PxVec3& impulsiveTorque, PxVec3& deltaLinearVelocity, PxVec3& deltaAngularVelocity) +{ + { + const PxF32 recipMass = body.getInvMass(); + deltaLinearVelocity = impulsiveForce*recipMass; + } + + { + const PxTransform globalPose = body.getGlobalPose(); + const PxTransform cmLocalPose = body.getCMassLocalPose(); + const PxTransform body2World = globalPose*cmLocalPose; + PxMat33 M(body2World.q); + + const PxVec3 recipInertiaBodySpace = body.getMassSpaceInvInertiaTensor(); + + PxMat33 recipInertiaWorldSpace; + const float axx = recipInertiaBodySpace.x*M(0,0), axy = recipInertiaBodySpace.x*M(1,0), axz = recipInertiaBodySpace.x*M(2,0); + const float byx = recipInertiaBodySpace.y*M(0,1), byy = recipInertiaBodySpace.y*M(1,1), byz = recipInertiaBodySpace.y*M(2,1); + const float czx = recipInertiaBodySpace.z*M(0,2), czy = recipInertiaBodySpace.z*M(1,2), czz = recipInertiaBodySpace.z*M(2,2); + recipInertiaWorldSpace(0,0) = axx*M(0,0) + byx*M(0,1) + czx*M(0,2); + recipInertiaWorldSpace(1,1) = axy*M(1,0) + byy*M(1,1) + czy*M(1,2); + recipInertiaWorldSpace(2,2) = axz*M(2,0) + byz*M(2,1) + czz*M(2,2); + recipInertiaWorldSpace(0,1) = recipInertiaWorldSpace(1,0) = axx*M(1,0) + byx*M(1,1) + czx*M(1,2); + recipInertiaWorldSpace(0,2) = recipInertiaWorldSpace(2,0) = axx*M(2,0) + byx*M(2,1) + czx*M(2,2); + recipInertiaWorldSpace(1,2) = recipInertiaWorldSpace(2,1) = axy*M(2,0) + byy*M(2,1) + czy*M(2,2); + + deltaAngularVelocity = recipInertiaWorldSpace*(impulsiveTorque); + } +} + + diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp new file mode 100644 index 000000000..577f2ef34 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp @@ -0,0 +1,188 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxSceneQueryExt.h" + +using namespace physx; + +bool PxSceneQueryExt::raycastAny( const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryHit& hit, const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache) +{ + PxSceneQueryFilterData fdAny = filterData; + fdAny.flags |= PxQueryFlag::eANY_HIT; + PxRaycastBuffer buf; + scene.raycast(origin, unitDir, distance, buf, PxHitFlags(), fdAny, filterCall, cache); + hit = buf.block; + return buf.hasBlock; +} + +bool PxSceneQueryExt::raycastSingle(const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, PxRaycastHit& hit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache) +{ + PxRaycastBuffer buf; + PxQueryFilterData fd1 = filterData; + scene.raycast(origin, unitDir, distance, buf, outputFlags, fd1, filterCall, cache); + hit = buf.block; + return buf.hasBlock; +} + +PxI32 PxSceneQueryExt::raycastMultiple( const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, + PxRaycastHit* hitBuffer, PxU32 hitBufferSize, bool& blockingHit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache) +{ + PxRaycastBuffer buf(hitBuffer, hitBufferSize); + PxQueryFilterData fd1 = filterData; + scene.raycast(origin, unitDir, distance, buf, outputFlags, fd1, filterCall, cache); + blockingHit = buf.hasBlock; + if(blockingHit) + { + if(buf.nbTouches < hitBufferSize) + { + hitBuffer[buf.nbTouches] = buf.block; + return PxI32(buf.nbTouches+1); + } + else // overflow, drop the last touch + { + hitBuffer[hitBufferSize-1] = buf.block; + return -1; + } + } else + // no block + return PxI32(buf.nbTouches); +} + +bool PxSceneQueryExt::sweepAny( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags queryFlags, + PxSceneQueryHit& hit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, + const PxSceneQueryCache* cache, + PxReal inflation) +{ + PxSceneQueryFilterData fdAny = filterData; + fdAny.flags |= PxQueryFlag::eANY_HIT; + PxSweepBuffer buf; + scene.sweep(geometry, pose, unitDir, distance, buf, queryFlags, fdAny, filterCall, cache, inflation); + hit = buf.block; + return buf.hasBlock; +} + +bool PxSceneQueryExt::sweepSingle( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, + PxSweepHit& hit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, + const PxSceneQueryCache* cache, + PxReal inflation) +{ + PxSweepBuffer buf; + PxQueryFilterData fd1 = filterData; + scene.sweep(geometry, pose, unitDir, distance, buf, outputFlags, fd1, filterCall, cache, inflation); + hit = buf.block; + return buf.hasBlock; +} + +PxI32 PxSceneQueryExt::sweepMultiple( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, PxSweepHit* hitBuffer, PxU32 hitBufferSize, bool& blockingHit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache, + PxReal inflation) +{ + PxQueryFilterData fd1 = filterData; + PxSweepBuffer buf(hitBuffer, hitBufferSize); + scene.sweep(geometry, pose, unitDir, distance, buf, outputFlags, fd1, filterCall, cache, inflation); + blockingHit = buf.hasBlock; + if(blockingHit) + { + if(buf.nbTouches < hitBufferSize) + { + hitBuffer[buf.nbTouches] = buf.block; + return PxI32(buf.nbTouches+1); + } + else // overflow, drop the last touch + { + hitBuffer[hitBufferSize-1] = buf.block; + return -1; + } + } else + // no block + return PxI32(buf.nbTouches); +} + +PxI32 PxSceneQueryExt::overlapMultiple( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, + PxOverlapHit* hitBuffer, PxU32 hitBufferSize, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall) +{ + PxQueryFilterData fd1 = filterData; + fd1.flags |= PxQueryFlag::eNO_BLOCK; + PxOverlapBuffer buf(hitBuffer, hitBufferSize); + scene.overlap(geometry, pose, buf, fd1, filterCall); + if(buf.hasBlock) + { + if(buf.nbTouches < hitBufferSize) + { + hitBuffer[buf.nbTouches] = buf.block; + return PxI32(buf.nbTouches+1); + } + else // overflow, drop the last touch + { + hitBuffer[hitBufferSize-1] = buf.block; + return -1; + } + } else + // no block + return PxI32(buf.nbTouches); +} + +bool PxSceneQueryExt::overlapAny( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, + PxOverlapHit& hit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall) +{ + PxSceneQueryFilterData fdAny = filterData; + fdAny.flags |= (PxQueryFlag::eANY_HIT | PxQueryFlag::eNO_BLOCK); + PxOverlapBuffer buf; + scene.overlap(geometry, pose, buf, fdAny, filterCall); + hit = buf.block; + return buf.hasBlock; +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSerialization.h b/src/PhysX/physx/source/physxextensions/src/ExtSerialization.h new file mode 100644 index 000000000..387566bf6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSerialization.h @@ -0,0 +1,44 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef EXT_SERIALIZATION_H +#define EXT_SERIALIZATION_H + +namespace physx +{ +namespace Ext +{ + void RegisterExtensionsSerializers(PxSerializationRegistry& sr); + void UnregisterExtensionsSerializers(PxSerializationRegistry& sr); + void GetExtensionsBinaryMetaData(PxOutputStream& stream); +} +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h b/src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h new file mode 100644 index 000000000..9728df4d2 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h @@ -0,0 +1,156 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_NP_SHARED_QUEUE_ENTRY_POOL_H +#define PX_PHYSICS_EXTENSIONS_NP_SHARED_QUEUE_ENTRY_POOL_H + +#include "CmPhysXCommon.h" +#include "PsAllocator.h" +#include "PsArray.h" +#include "PsSList.h" + +namespace physx +{ +namespace Ext +{ + class SharedQueueEntry : public Ps::SListEntry + { + public: + SharedQueueEntry(void* objectRef) : mObjectRef(objectRef), mPooledEntry(false) {} + SharedQueueEntry() : mObjectRef(NULL), mPooledEntry(true) {} + + public: + void* mObjectRef; + bool mPooledEntry; // True if the entry was preallocated in a pool + }; + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif // Because of the SList member I assume*/ + + template::Type > + class SharedQueueEntryPool : private Alloc + { + public: + SharedQueueEntryPool(PxU32 poolSize, const Alloc& alloc = Alloc(PX_DEBUG_EXP("SharedQueueEntryPool"))); + ~SharedQueueEntryPool(); + + SharedQueueEntry* getEntry(void* objectRef); + void putEntry(SharedQueueEntry& entry); + + private: + SharedQueueEntry* mTaskEntryPool; + Ps::SList mTaskEntryPtrPool; + }; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Ext + + +template +Ext::SharedQueueEntryPool::SharedQueueEntryPool(PxU32 poolSize, const Alloc& alloc) + : Alloc(alloc) +{ + Ps::AlignedAllocator alignedAlloc("SharedQueueEntryPool"); + + mTaskEntryPool = poolSize ? reinterpret_cast(alignedAlloc.allocate(sizeof(SharedQueueEntry) * poolSize, __FILE__, __LINE__)) : NULL; + + if (mTaskEntryPool) + { + for(PxU32 i=0; i < poolSize; i++) + { + PX_ASSERT((size_t(&mTaskEntryPool[i]) & (PX_SLIST_ALIGNMENT-1)) == 0); // The SList entry must be aligned according to PX_SLIST_ALIGNMENT + + PX_PLACEMENT_NEW(&mTaskEntryPool[i], SharedQueueEntry)(); + PX_ASSERT(mTaskEntryPool[i].mPooledEntry == true); + mTaskEntryPtrPool.push(mTaskEntryPool[i]); + } + } +} + + +template +Ext::SharedQueueEntryPool::~SharedQueueEntryPool() +{ + if (mTaskEntryPool) + { + Ps::AlignedAllocator alignedAlloc("SharedQueueEntryPool"); + alignedAlloc.deallocate(mTaskEntryPool); + } +} + + +template +Ext::SharedQueueEntry* Ext::SharedQueueEntryPool::getEntry(void* objectRef) +{ + SharedQueueEntry* e = static_cast(mTaskEntryPtrPool.pop()); + if (e) + { + PX_ASSERT(e->mPooledEntry == true); + e->mObjectRef = objectRef; + return e; + } + else + { + Ps::AlignedAllocator alignedAlloc; + e = reinterpret_cast(alignedAlloc.allocate(sizeof(SharedQueueEntry), __FILE__, __LINE__)); + if (e) + { + PX_PLACEMENT_NEW(e, SharedQueueEntry)(objectRef); + PX_ASSERT(e->mPooledEntry == false); + } + + return e; + } +} + + +template +void Ext::SharedQueueEntryPool::putEntry(Ext::SharedQueueEntry& entry) +{ + if (entry.mPooledEntry) + { + entry.mObjectRef = NULL; + mTaskEntryPtrPool.push(entry); + } + else + { + Ps::AlignedAllocator alignedAlloc; + alignedAlloc.deallocate(&entry); + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp b/src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp new file mode 100644 index 000000000..35f899e21 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp @@ -0,0 +1,384 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMathUtils.h" +#include "foundation/PxQuat.h" +#include "CmPhysXCommon.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "PsInlineArray.h" +#include "PxSimpleFactory.h" +#include "PxRigidStatic.h" +#include "PxSphereGeometry.h" +#include "PxBoxGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxRigidBodyExt.h" +#include "PxRigidStatic.h" +#include "PxScene.h" +#include "PxShape.h" +#include "PxRigidDynamic.h" +#include "CmPhysXCommon.h" +#include "PxPhysics.h" + +using namespace physx; +using namespace shdfnd; + +namespace +{ + +bool isDynamicGeometry(PxGeometryType::Enum type) +{ + return type == PxGeometryType::eBOX + || type == PxGeometryType::eSPHERE + || type == PxGeometryType::eCAPSULE + || type == PxGeometryType::eCONVEXMESH; +} +} + +namespace physx +{ +PxRigidDynamic* PxCreateDynamic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape, + PxReal density) +{ + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateDynamic: transform is not valid."); + + PxRigidDynamic* actor = sdk.createRigidDynamic(transform); + if(actor) + { + actor->attachShape(shape); + PxRigidBodyExt::updateMassAndInertia(*actor, density); + } + return actor; +} + +PxRigidDynamic* PxCreateDynamic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + PxReal density, + const PxTransform& shapeOffset) +{ + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateDynamic: transform is not valid."); + PX_CHECK_AND_RETURN_NULL(shapeOffset.isValid(), "PxCreateDynamic: shapeOffset is not valid."); + + if(!isDynamicGeometry(geometry.getType()) || density <= 0.0f) + return NULL; + + PxShape* shape = sdk.createShape(geometry, material, true); + if(!shape) + return NULL; + + shape->setLocalPose(shapeOffset); + + PxRigidDynamic* body = shape ? PxCreateDynamic(sdk, transform, *shape, density) : NULL; + shape->release(); + return body; +} + + + +PxRigidDynamic* PxCreateKinematic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape, + PxReal density) +{ + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateKinematic: transform is not valid."); + + bool isDynGeom = isDynamicGeometry(shape.getGeometryType()); + if(isDynGeom && density <= 0.0f) + return NULL; + + PxRigidDynamic* actor = sdk.createRigidDynamic(transform); + if(actor) + { + actor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true); + if(!isDynGeom) + shape.setFlag(PxShapeFlag::eSIMULATION_SHAPE, false); + + actor->attachShape(shape); + + if(isDynGeom) + PxRigidBodyExt::updateMassAndInertia(*actor, density); + else + { + actor->setMass(1.f); + actor->setMassSpaceInertiaTensor(PxVec3(1.f,1.f,1.f)); + } + } + + return actor; +} + + +PxRigidDynamic* PxCreateKinematic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + PxReal density, + const PxTransform& shapeOffset) +{ + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateKinematic: transform is not valid."); + PX_CHECK_AND_RETURN_NULL(shapeOffset.isValid(), "PxCreateKinematic: shapeOffset is not valid."); + + bool isDynGeom = isDynamicGeometry(geometry.getType()); + if(isDynGeom && density <= 0.0f) + return NULL; + + PxShape* shape = sdk.createShape(geometry, material, true); + if(!shape) + return NULL; + + shape->setLocalPose(shapeOffset); + + PxRigidDynamic* body = PxCreateKinematic(sdk, transform, *shape, density); + shape->release(); + return body; +} + + + + +PxRigidStatic* PxCreateStatic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape) +{ + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateStatic: transform is not valid."); + + PxRigidStatic* s = sdk.createRigidStatic(transform); + if(s) + s->attachShape(shape); + return s; +} + +PxRigidStatic* PxCreateStatic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + const PxTransform& shapeOffset) +{ + + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateStatic: transform is not valid."); + PX_CHECK_AND_RETURN_NULL(shapeOffset.isValid(), "PxCreateStatic: shapeOffset is not valid."); + + PxShape* shape = sdk.createShape(geometry, material, true); + if(!shape) + return NULL; + + shape->setLocalPose(shapeOffset); + + PxRigidStatic* s = PxCreateStatic(sdk, transform, *shape); + shape->release(); + return s; +} + + + + +PxRigidStatic* PxCreatePlane(PxPhysics& sdk, + const PxPlane& plane, + PxMaterial& material) +{ + PX_CHECK_AND_RETURN_NULL(plane.n.isFinite(), "PxCreatePlane: plane normal is not valid."); + + if (!plane.n.isNormalized()) + return NULL; + + return PxCreateStatic(sdk, PxTransformFromPlaneEquation(plane), PxPlaneGeometry(), material); +} + + +PxShape* PxCloneShape(PxPhysics &physics, const PxShape& from, bool isExclusive) +{ + Ps::InlineArray materials; + PxU16 materialCount = from.getNbMaterials(); + materials.resize(materialCount); + from.getMaterials(materials.begin(), materialCount); + + PxShape* to = physics.createShape(from.getGeometry().any(), materials.begin(), materialCount, isExclusive, from.getFlags()); + + to->setLocalPose(from.getLocalPose()); + to->setContactOffset(from.getContactOffset()); + to->setRestOffset(from.getRestOffset()); + to->setSimulationFilterData(from.getSimulationFilterData()); + to->setQueryFilterData(from.getQueryFilterData()); + return to; +} + + +namespace +{ + void copyStaticProperties(PxPhysics& physics, PxRigidActor& to, const PxRigidActor& from) + { + Ps::InlineArray shapes; + shapes.resize(from.getNbShapes()); + + PxU32 shapeCount = from.getNbShapes(); + from.getShapes(shapes.begin(), shapeCount); + + for(PxU32 i = 0; i < shapeCount; i++) + { + PxShape* s = shapes[i]; + if(!s->isExclusive()) + to.attachShape(*s); + else + { + PxShape* newShape = physx::PxCloneShape(physics, *s, true); + to.attachShape(*newShape); + newShape->release(); + } + } + + to.setActorFlags(from.getActorFlags()); + to.setOwnerClient(from.getOwnerClient()); + to.setDominanceGroup(from.getDominanceGroup()); + } +} + +PxRigidStatic* PxCloneStatic(PxPhysics& physicsSDK, + const PxTransform& transform, + const PxRigidActor& from) +{ + PxRigidStatic* to = physicsSDK.createRigidStatic(transform); + if(!to) + return NULL; + + copyStaticProperties(physicsSDK, *to, from); + + return to; +} + +PxRigidDynamic* PxCloneDynamic(PxPhysics& physicsSDK, + const PxTransform& transform, + const PxRigidDynamic& from) +{ + PxRigidDynamic* to = physicsSDK.createRigidDynamic(transform); + if(!to) + return NULL; + + copyStaticProperties(physicsSDK, *to, from); + + + to->setRigidBodyFlags(from.getRigidBodyFlags()); + + to->setMass(from.getMass()); + to->setMassSpaceInertiaTensor(from.getMassSpaceInertiaTensor()); + to->setCMassLocalPose(from.getCMassLocalPose()); + + to->setLinearVelocity(from.getLinearVelocity()); + to->setAngularVelocity(from.getAngularVelocity()); + + to->setLinearDamping(from.getAngularDamping()); + to->setAngularDamping(from.getAngularDamping()); + + PxU32 posIters, velIters; + from.getSolverIterationCounts(posIters, velIters); + to->setSolverIterationCounts(posIters, velIters); + + to->setMaxAngularVelocity(from.getMaxAngularVelocity()); + to->setMaxDepenetrationVelocity(from.getMaxDepenetrationVelocity()); + to->setSleepThreshold(from.getSleepThreshold()); + to->setStabilizationThreshold(from.getStabilizationThreshold()); + to->setMinCCDAdvanceCoefficient(from.getMinCCDAdvanceCoefficient()); + to->setContactReportThreshold(from.getContactReportThreshold()); + to->setMaxContactImpulse(from.getMaxContactImpulse()); + + return to; +} + +namespace +{ + PxTransform scalePosition(const PxTransform& t, PxReal scale) + { + return PxTransform(t.p*scale, t.q); + } +} + +void PxScaleRigidActor(PxRigidActor& actor, PxReal scale, bool scaleMassProps) +{ + PX_CHECK_AND_RETURN(scale > 0, + "PxScaleRigidActor requires that the scale parameter is greater than zero"); + + Ps::InlineArray shapes; + shapes.resize(actor.getNbShapes()); + actor.getShapes(shapes.begin(), shapes.size()); + + for(PxU32 i=0;isetLocalPose(scalePosition(shapes[i]->getLocalPose(), scale)); + PxGeometryHolder h = shapes[i]->getGeometry(); + + switch(h.getType()) + { + case PxGeometryType::eSPHERE: + h.sphere().radius *= scale; + break; + case PxGeometryType::ePLANE: + break; + case PxGeometryType::eCAPSULE: + h.capsule().halfHeight *= scale; + h.capsule().radius *= scale; + break; + case PxGeometryType::eBOX: + h.box().halfExtents *= scale; + break; + case PxGeometryType::eCONVEXMESH: + h.convexMesh().scale.scale *= scale; + break; + case PxGeometryType::eTRIANGLEMESH: + h.triangleMesh().scale.scale *= scale; + break; + case PxGeometryType::eHEIGHTFIELD: + h.heightField().heightScale *= scale; + h.heightField().rowScale *= scale; + h.heightField().columnScale *= scale; + break; + case PxGeometryType::eINVALID: + case PxGeometryType::eGEOMETRY_COUNT: + PX_ASSERT(0); + } + shapes[i]->setGeometry(h.any()); + } + + if(!scaleMassProps) + return; + + PxRigidDynamic* dynamic = (&actor)->is(); + if(!dynamic) + return; + + PxReal scale3 = scale*scale*scale; + dynamic->setMass(dynamic->getMass()*scale3); + dynamic->setMassSpaceInertiaTensor(dynamic->getMassSpaceInertiaTensor()*scale3*scale*scale); + dynamic->setCMassLocalPose(scalePosition(dynamic->getCMassLocalPose(), scale)); +} +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp b/src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp new file mode 100644 index 000000000..fe2034ede --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp @@ -0,0 +1,145 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxMemory.h" +#include "PxSmoothNormals.h" +#include "PsMathUtils.h" +#include "PsUserAllocated.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" + +using namespace physx; + +static PxReal computeAngle(const PxVec3* verts, const PxU32* refs, PxU32 vref) +{ + PxU32 e0=0,e2=0; + if(vref==refs[0]) + { + e0 = 2; + e2 = 1; + } + else if(vref==refs[1]) + { + e0 = 2; + e2 = 0; + } + else if(vref==refs[2]) + { + e0 = 0; + e2 = 1; + } + else + { + PX_ASSERT(0); + } + const PxVec3 edge0 = verts[refs[e0]] - verts[vref]; + const PxVec3 edge1 = verts[refs[e2]] - verts[vref]; + + return Ps::angle(edge0, edge1); +} + +bool PxBuildSmoothNormals(PxU32 nbTris, PxU32 nbVerts, const PxVec3* verts, const PxU32* dFaces, const PxU16* wFaces, PxVec3* normals, bool flip) +{ + if(!verts || !normals || !nbTris || !nbVerts) + return false; + + // Get correct destination buffers + // - if available, write directly to user-provided buffers + // - else get some ram and keep track of it + PxVec3* FNormals = reinterpret_cast(PX_ALLOC_TEMP(sizeof(PxVec3)*nbTris, "PxVec3")); + if(!FNormals) return false; + + // Compute face normals + const PxU32 c = PxU32(flip!=0); + for(PxU32 i=0; i(PX_ALLOC_TEMP(sizeof(PxVec3)*nbVerts, "PxVec3")); + PxMemSet(TmpNormals, 0, nbVerts*sizeof(PxVec3)); + for(PxU32 i=0;iis()) || (actor1 && actor1->is()), "PxSphericalJointCreate: at least one actor must be dynamic"); + + SphericalJoint* j; + PX_NEW_SERIALIZED(j, SphericalJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +void SphericalJoint::setProjectionLinearTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxSphericalJoint::setProjectionLinearTolerance: invalid parameter"); + data().projectionLinearTolerance = tolerance; + markDirty(); +} + +PxReal SphericalJoint::getProjectionLinearTolerance() const +{ + return data().projectionLinearTolerance; +} + +void SphericalJoint::setLimitCone(const PxJointLimitCone &limit) +{ + PX_CHECK_AND_RETURN(limit.isValid(), "PxSphericalJoint::setLimit: invalid parameter"); + data().limit = limit; + markDirty(); +} + +PxJointLimitCone SphericalJoint::getLimitCone() const +{ + return data().limit; +} + +PxSphericalJointFlags SphericalJoint::getSphericalJointFlags(void) const +{ + return data().jointFlags; +} + +void SphericalJoint::setSphericalJointFlags(PxSphericalJointFlags flags) +{ + data().jointFlags = flags; +} + +void SphericalJoint::setSphericalJointFlag(PxSphericalJointFlag::Enum flag, bool value) +{ + if(value) + data().jointFlags |= flag; + else + data().jointFlags &= ~flag; + markDirty(); +} + +PxReal SphericalJoint::getSwingYAngle() const +{ + return getSwingYAngle_Internal(); +} + +PxReal SphericalJoint::getSwingZAngle() const +{ + return getSwingZAngle_Internal(); +} + +bool SphericalJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(SphericalJointData)); + return mPxConstraint!=NULL; +} + +void SphericalJoint::exportExtraData(PxSerializationContext& stream) +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(SphericalJointData)); + } + stream.writeName(mName); +} + +void SphericalJoint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + context.readName(mName); +} + +void SphericalJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +SphericalJoint* SphericalJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + SphericalJoint* obj = new (address) SphericalJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(SphericalJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetSphericalJointShaderTable() +{ + return &SphericalJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void SphericalJointProject(const void* constantBlock, PxTransform& bodyAToWorld, PxTransform& bodyBToWorld, bool projectToA) +{ + const SphericalJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w, cB2cA, projected; + joint::computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA); + + bool linearTrunc; + projected.p = joint::truncateLinear(cB2cA.p, data.projectionLinearTolerance, linearTrunc); + + if(linearTrunc) + { + projected.q = cB2cA.q; + joint::projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA); + } +} + +static void SphericalJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags) +{ + const SphericalJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + viz.visualizeJointFrames(cA2w, cB2w); + + if((flags & PxConstraintVisualizationFlag::eLIMITS) && (data.jointFlags & PxSphericalJointFlag::eLIMIT_ENABLED)) + { + if(cA2w.q.dot(cB2w.q)<0.0f) + cB2w.q = -cB2w.q; + + const PxTransform cB2cA = cA2w.transformInv(cB2w); + PxQuat swing, twist; + Ps::separateSwingTwist(cB2cA.q,swing,twist); + + // PT: TODO: refactor with D6 joint code + const PxReal pad = data.limit.isSoft() ? 0.0f : data.limit.contactDistance; + const PxVec3 swingAngle(0.0f, computeSwingAngle(swing.y, swing.w), computeSwingAngle(swing.z, swing.w)); + Cm::ConeLimitHelperTanLess coneHelper(data.limit.yAngle, data.limit.zAngle, pad); + viz.visualizeLimitCone(cA2w, PxTan(data.limit.zAngle/4), PxTan(data.limit.yAngle/4), !coneHelper.contains(swingAngle)); + } +} + +static PxU32 SphericalJointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool /*useExtendedLimits*/, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const SphericalJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + if(cB2w.q.dot(cA2w.q)<0.0f) + cB2w.q = -cB2w.q; + + if(data.jointFlags & PxSphericalJointFlag::eLIMIT_ENABLED) + { + PxQuat swing, twist; + Ps::separateSwingTwist(cA2w.q.getConjugate() * cB2w.q, swing, twist); + PX_ASSERT(PxAbs(swing.x)<1e-6f); + + // PT: TODO: refactor with D6 joint code + PxVec3 axis; + PxReal error; + const PxReal pad = data.limit.isSoft() ? 0.0f : data.limit.contactDistance; + const Cm::ConeLimitHelperTanLess coneHelper(data.limit.yAngle, data.limit.zAngle, pad); + const bool active = coneHelper.getLimit(swing, axis, error); + if(active) + ch.angularLimit(cA2w.rotate(axis), error, data.limit); + } + + PxVec3 ra, rb; + ch.prepareLockedAxes(cA2w.q, cB2w.q, cA2w.transformInv(cB2w.p), 7, 0, ra, rb); + cA2wOut = ra + bA2w.p; + cB2wOut = rb + bB2w.p; + + return ch.getCount(); +} + +PxConstraintShaderTable Ext::SphericalJoint::sShaders = { SphericalJointSolverPrep, SphericalJointProject, SphericalJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h new file mode 100644 index 000000000..81694b529 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h @@ -0,0 +1,129 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_SPHERICALJOINTCONSTRAINT_H +#define NP_SPHERICALJOINTCONSTRAINT_H + +#include "ExtJoint.h" +#include "PxSphericalJoint.h" +#include "CmUtils.h" + +namespace physx +{ +struct PxSphericalJointGeneratedValues; +namespace Ext +{ + struct SphericalJointData: public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + PxJointLimitCone limit; + + PxReal projectionLinearTolerance; + + PxSphericalJointFlags jointFlags; + // forestall compiler complaints about not being able to generate a constructor + private: + SphericalJointData(const PxJointLimitCone &cone): + limit(cone) {} + }; + + typedef Joint SphericalJointT; + + class SphericalJoint : public SphericalJointT + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + SphericalJoint(PxBaseFlags baseFlags) : SphericalJointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static SphericalJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + SphericalJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + SphericalJointT(PxJointConcreteType::eSPHERICAL, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(SphericalJointData), "SphericalJointData") + { + SphericalJointData* data = static_cast(mData); + + data->projectionLinearTolerance = 1e10f; + data->limit = PxJointLimitCone(PxPi/2, PxPi/2); + data->jointFlags = PxSphericalJointFlags(); + } + + // PxSphericalJoint + virtual void setLimitCone(const PxJointLimitCone &limit); + virtual PxJointLimitCone getLimitCone() const; + virtual void setSphericalJointFlags(PxSphericalJointFlags flags); + virtual void setSphericalJointFlag(PxSphericalJointFlag::Enum flag, bool value); + virtual PxSphericalJointFlags getSphericalJointFlags(void) const; + virtual void setProjectionLinearTolerance(PxReal distance); + virtual PxReal getProjectionLinearTolerance() const; + virtual PxReal getSwingYAngle() const; + virtual PxReal getSwingZAngle() const; + //~PxSphericalJoint + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep() const { return sShaders.solverPrep; } + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE SphericalJointData& data() const + { + return *static_cast(mData); + } + }; + +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetSphericalJointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h b/src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h new file mode 100644 index 000000000..450382c73 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_NP_TASK_QUEUE_HELPER_H +#define PX_PHYSICS_EXTENSIONS_NP_TASK_QUEUE_HELPER_H + +#include "task/PxTask.h" +#include "CmPhysXCommon.h" +#include "ExtSharedQueueEntryPool.h" + +namespace physx +{ + +#define EXT_TASK_QUEUE_ENTRY_POOL_SIZE 128 + +namespace Ext +{ + class TaskQueueHelper + { + public: + static PxBaseTask* fetchTask(Ps::SList& taskQueue, Ext::SharedQueueEntryPool<>& entryPool) + { + SharedQueueEntry* entry = static_cast(taskQueue.pop()); + if (entry) + { + PxBaseTask* task = reinterpret_cast(entry->mObjectRef); + entryPool.putEntry(*entry); + return task; + } + else + return NULL; + } + }; + +} // namespace Ext + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp new file mode 100644 index 000000000..6559d6346 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp @@ -0,0 +1,164 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxTriangleMeshExt.h" +#include "PxMeshQuery.h" +#include "PxGeometryQuery.h" +#include "PxTriangleMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "PxTriangleMesh.h" +#include "PsAllocator.h" + +using namespace physx; + +PxMeshOverlapUtil::PxMeshOverlapUtil() : mResultsMemory(mResults), mNbResults(0), mMaxNbResults(256) +{ +} + +PxMeshOverlapUtil::~PxMeshOverlapUtil() +{ + if(mResultsMemory != mResults) + PX_FREE(mResultsMemory); +} + +PxU32 PxMeshOverlapUtil::findOverlap(const PxGeometry& geom, const PxTransform& geomPose, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose) +{ + bool overflow; + PxU32 nbTouchedTris = PxMeshQuery::findOverlapTriangleMesh(geom, geomPose, meshGeom, meshPose, mResultsMemory, mMaxNbResults, 0, overflow); + + if(overflow) + { + const PxU32 maxNbTris = meshGeom.triangleMesh->getNbTriangles(); + if(!maxNbTris) + { + mNbResults = 0; + return 0; + } + + if(mMaxNbResults(PX_ALLOC(sizeof(PxU32)*maxNbTris, "PxMeshOverlapUtil::findOverlap")); + mMaxNbResults = maxNbTris; + } + nbTouchedTris = PxMeshQuery::findOverlapTriangleMesh(geom, geomPose, meshGeom, meshPose, mResultsMemory, mMaxNbResults, 0, overflow); + PX_ASSERT(nbTouchedTris); + PX_ASSERT(!overflow); + } + mNbResults = nbTouchedTris; + return nbTouchedTris; +} + +PxU32 PxMeshOverlapUtil::findOverlap(const PxGeometry& geom, const PxTransform& geomPose, const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose) +{ + bool overflow = true; + PxU32 nbTouchedTris = 0; + do + { + nbTouchedTris = PxMeshQuery::findOverlapHeightField(geom, geomPose, hfGeom, hfPose, mResultsMemory, mMaxNbResults, 0, overflow); + if(overflow) + { + const PxU32 maxNbTris = mMaxNbResults * 2; + + if(mResultsMemory != mResults) + PX_FREE(mResultsMemory); + + mResultsMemory = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*maxNbTris, "PxMeshOverlapUtil::findOverlap")); + mMaxNbResults = maxNbTris; + } + }while(overflow); + + mNbResults = nbTouchedTris; + return nbTouchedTris; +} +namespace +{ +template +bool computeMeshPenetrationT(PxVec3& direction, + PxReal& depth, + const PxGeometry& geom, + const PxTransform& geomPose, + const MeshGeometry& meshGeom, + const PxTransform& meshPose, + PxU32 maxIter, + PxU32* nbIterOut) +{ + PxU32 nbIter = 0; + PxTransform pose = geomPose; + for (; nbIter < maxIter; nbIter++) + { + PxVec3 currentDir; + PxF32 currentDepth; + + if (!PxGeometryQuery::computePenetration(currentDir, currentDepth, geom, pose, meshGeom, meshPose)) + break; + + pose.p += currentDir * currentDepth; + } + + if(nbIterOut) + *nbIterOut = nbIter; + + PxVec3 diff = pose.p - geomPose.p; + depth = diff.magnitude(); + + if (depth>0) + direction = diff / depth; + + return nbIter!=0; +} +} + +bool physx::PxComputeTriangleMeshPenetration(PxVec3& direction, + PxReal& depth, + const PxGeometry& geom, + const PxTransform& geomPose, + const PxTriangleMeshGeometry& meshGeom, + const PxTransform& meshPose, + PxU32 maxIter, + PxU32* nbIter) +{ + return computeMeshPenetrationT(direction, depth, geom, geomPose, meshGeom, meshPose, maxIter, nbIter); +} + +bool physx::PxComputeHeightFieldPenetration(PxVec3& direction, + PxReal& depth, + const PxGeometry& geom, + const PxTransform& geomPose, + const PxHeightFieldGeometry& hfGeom, + const PxTransform& meshPose, + PxU32 maxIter, + PxU32* nbIter) +{ + return computeMeshPenetrationT(direction, depth, geom, geomPose, hfGeom, meshPose, maxIter, nbIter); +} + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp new file mode 100644 index 000000000..ecb87b4ca --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp @@ -0,0 +1,305 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsHash.h" +#include "PsHashMap.h" +#include "PsFoundation.h" +#include "CmIO.h" +#include "SnFile.h" +#include "PsString.h" +#include "extensions/PxSerialization.h" +#include "PxPhysics.h" +#include "PxPhysicsSerialization.h" +#include "SnSerializationContext.h" +#include "PxSerializer.h" +#include "serialization/SnSerializationRegistry.h" +#include "serialization/SnSerialUtils.h" +#include "CmCollection.h" +#include "SnConvX_Align.h" + +using namespace physx; +using namespace Sn; + +namespace +{ + PX_INLINE PxU8* alignPtr(PxU8* ptr, PxU32 alignment = PX_SERIAL_ALIGN) + { + if(!alignment) + return ptr; + + const PxU32 padding = getPadding(size_t(ptr), alignment); + PX_ASSERT(!getPadding(size_t(ptr + padding), alignment)); + return ptr + padding; + } + + PX_FORCE_INLINE PxU32 read32(PxU8*& address) + { + const PxU32 value = *reinterpret_cast(address); + address += sizeof(PxU32); + return value; + } + + bool readHeader(PxU8*& address, PxU32& version) + { + const PxU32 header = read32(address); + PX_UNUSED(header); + + version = read32(address); + + const PxU32 binaryVersion = read32(address); + PX_UNUSED(binaryVersion); + const PxU32 buildNumber = read32(address); + PX_UNUSED(buildNumber); + const PxU32 platformTag = read32(address); + PX_UNUSED(platformTag); + const PxU32 markedPadding = read32(address); + PX_UNUSED(markedPadding); + +#if PX_CHECKED + if (header != PX_MAKE_FOURCC('S','E','B','D')) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "Buffer contains data with wrong header indicating invalid binary data."); + return false; + } + + if (!checkCompatibility(version, binaryVersion)) + { + char buffer[512]; + getCompatibilityVersionsStr(buffer, 512); + + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "Buffer contains data version (%x-%d) is incompatible with this PhysX sdk.\n These versions would be compatible: %s", + version, binaryVersion, buffer); + return false; + } + + if (platformTag != getBinaryPlatformTag()) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "Buffer contains data with platform mismatch:\nExpected: %s \nActual: %s\n", + getBinaryPlatformName(getBinaryPlatformTag()), + getBinaryPlatformName(platformTag)); + return false; + } +#endif + return true; + } + + bool checkImportReferences(const ImportReference* importReferences, PxU32 nbImportReferences, const Cm::Collection* externalRefs) + { + if (!externalRefs) + { + if (nbImportReferences > 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxSerialization::createCollectionFromBinary: External references needed but no externalRefs collection specified."); + return false; + } + } + else + { + for (PxU32 i=0; ifind(id); + if (!referencedObject) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxSerialization::createCollectionFromBinary: External reference %" PX_PRIu64 " expected in externalRefs collection but not found.", id); + return false; + } + if (referencedObject->getConcreteType() != type) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxSerialization::createCollectionFromBinary: External reference %d type mismatch. Expected %d but found %d in externalRefs collection.", type, referencedObject->getConcreteType()); + return false; + } + } + } + return true; + } + +} + +PxCollection* PxSerialization::createCollectionFromBinary(void* memBlock, PxSerializationRegistry& sr, const PxCollection* pxExternalRefs) +{ +#if PX_CHECKED + if(size_t(memBlock) & (PX_SERIAL_FILE_ALIGN-1)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Buffer must be 128-bytes aligned."); + return NULL; + } +#endif + PxU8* address = reinterpret_cast(memBlock); + const Cm::Collection* externalRefs = static_cast(pxExternalRefs); + + PxU32 version; + if (!readHeader(address, version)) + { + return NULL; + } + + ManifestEntry* manifestTable; + PxU32 nbObjectsInCollection; + PxU32 objectDataEndOffset; + + // read number of objects in collection + address = alignPtr(address); + nbObjectsInCollection = read32(address); + + // read manifest (PxU32 offset, PxConcreteType type) + { + address = alignPtr(address); + PxU32 nbManifestEntries = read32(address); + PX_ASSERT(*reinterpret_cast(address) == 0); //first offset is always 0 + manifestTable = (nbManifestEntries > 0) ? reinterpret_cast(address) : NULL; + address += nbManifestEntries*sizeof(ManifestEntry); + objectDataEndOffset = read32(address); + } + + ImportReference* importReferences; + PxU32 nbImportReferences; + // read import references + { + address = alignPtr(address); + nbImportReferences = read32(address); + importReferences = (nbImportReferences > 0) ? reinterpret_cast(address) : NULL; + address += nbImportReferences*sizeof(ImportReference); + } + + if (!checkImportReferences(importReferences, nbImportReferences, externalRefs)) + { + return NULL; + } + + ExportReference* exportReferences; + PxU32 nbExportReferences; + // read export references + { + address = alignPtr(address); + nbExportReferences = read32(address); + exportReferences = (nbExportReferences > 0) ? reinterpret_cast(address) : NULL; + address += nbExportReferences*sizeof(ExportReference); + } + + // read internal references arrays + PxU32 nbInternalPtrReferences = 0; + PxU32 nbInternalIdxReferences = 0; + InternalReferencePtr* internalPtrReferences = NULL; + InternalReferenceIdx* internalIdxReferences = NULL; + { + address = alignPtr(address); + + nbInternalPtrReferences = read32(address); + internalPtrReferences = (nbInternalPtrReferences > 0) ? reinterpret_cast(address) : NULL; + address += nbInternalPtrReferences*sizeof(InternalReferencePtr); + + nbInternalIdxReferences = read32(address); + internalIdxReferences = (nbInternalIdxReferences > 0) ? reinterpret_cast(address) : NULL; + address += nbInternalIdxReferences*sizeof(InternalReferenceIdx); + } + + // create internal references map + PxF32 loadFactor = 0.75f; + PxF32 _loadFactor = 1.0f / loadFactor; + PxU32 hashSize = PxU32((nbInternalPtrReferences + nbInternalIdxReferences + 1)*_loadFactor); + InternalRefMap internalReferencesMap(hashSize, loadFactor); + { + //create hash (we should load the hashes directly from memory) + for (PxU32 i=0;i(sr); + Cm::Collection* collection = static_cast(PxCreateCollection()); + PX_ASSERT(collection); + collection->mObjects.reserve(PxU32(nbObjectsInCollection*_loadFactor) + 1); + if(nbExportReferences > 0) + collection->mIds.reserve(PxU32(nbExportReferences*_loadFactor) + 1); + + PxU8* addressObjectData = alignPtr(address); + PxU8* addressExtraData = alignPtr(addressObjectData + objectDataEndOffset); + + DeserializationContext context(manifestTable, importReferences, addressObjectData, internalReferencesMap, externalRefs, addressExtraData, version); + + // iterate over memory containing PxBase objects, create the instances, resolve the addresses, import the external data, add to collection. + { + PxU32 nbObjects = nbObjectsInCollection; + + while(nbObjects--) + { + address = alignPtr(address); + context.alignExtraData(); + + // read PxBase header with type and get corresponding serializer. + PxBase* header = reinterpret_cast(address); + const PxType classType = header->getConcreteType(); + const PxSerializer* serializer = sn.getSerializer(classType); + PX_ASSERT(serializer); + + PxBase* instance = serializer->createObject(address, context); + if (!instance) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "Cannot create class instance for concrete type %d.", classType); + collection->release(); + return NULL; + } + + collection->internalAdd(instance); + } + } + + PX_ASSERT(nbObjectsInCollection == collection->internalGetNbObjects()); + + // update new collection with export references + { + PX_ASSERT(addressObjectData != NULL); + for (PxU32 i=0;i(addressObjectData + manifestTable[manifestIndex].offset); + collection->mIds.insertUnique(exportReferences[i].id, obj); + collection->mObjects[obj] = exportReferences[i].id; + } + } + + PxAddCollectionToPhysics(*collection); + return collection; +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp new file mode 100644 index 000000000..4c4d279d2 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp @@ -0,0 +1,402 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsHash.h" +#include "PsHashMap.h" +#include "CmIO.h" +#include "SnFile.h" +#include "PsString.h" +#include "PsIntrinsics.h" +#include "extensions/PxSerialization.h" +#include "SnSerializationContext.h" +#include "PxSerializer.h" +#include "serialization/SnSerialUtils.h" +#include "serialization/SnSerializationRegistry.h" +#include "SnConvX_Align.h" +#include "PxDefaultStreams.h" +#include "CmCollection.h" +#include "PxPhysicsVersion.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Cm; +using namespace Sn; + +//------------------------------------------------------------------------------------ +//// Binary Serialized PxCollection, format documentation +//------------------------------------------------------------------------------------ +// +// +//------------------------------------------------------------------------------------ +//// overview: +//// header information +//// manifest table +//// import references +//// export references +//// internal references +//// object data +//// extra data +//------------------------------------------------------------------------------------ +// +// +//------------------------------------------------------------------------------------ +//// header information: +//// header tag plus various version and platform information +//------------------------------------------------------------------------------------ +// header SEBD +// PX_PHYSICS_VERSION +// PX_BINARY_SERIAL_VERSION +// PX_BUILD_NUMBER (or 0 if not defined) +// platform tag +// markedPadding (on for PX_CHECKED) +// nbObjectsInCollection +// +// +//------------------------------------------------------------------------------------ +//// manifest table: +//// one entry per collected object +//// offsets relative to object data buffer +//------------------------------------------------------------------------------------ +// alignment +// PxU32 size +// (PxU32 offset, PxType type)*size +// PxU32 endOffset +// +// +//------------------------------------------------------------------------------------ +//// import references: +//// one entry per required reference to external collection +//------------------------------------------------------------------------------------ +// alignment +// PxU32 size +// (PxSerialObjectId id, PxType type)*size +// +// +//------------------------------------------------------------------------------------ +//// export references: +//// one entry per object in the collection with id +//// object indices point into the manifest table (objects in the same collection) +//------------------------------------------------------------------------------------ +// alignment +// PxU32 size +// (PxSerialObjectId id, SerialObjectIndex objIndex)*size +// +// +//------------------------------------------------------------------------------------ +//// internal references: +//// one entry per reference, kind pair +//// object indices point either into the manifest table or into the import references +//// depending on whether the entry references the same collection or the external one +//// one section for pointer type references and one for index type references. +//------------------------------------------------------------------------------------ +// alignment +// PxU32 sizePtrs; +// (size_t reference, PxU32 kind, SerialObjectIndex objIndex)*sizePtrs +// PxU32 sizeIdx; +// (PxU32 reference, PxU32 kind, SerialObjectIndex objIndex)*sizePtrs +// +// +//------------------------------------------------------------------------------------ +//// object data: +//// serialized PxBase derived class instances +//// each object size depends on specific class +//// offsets are stored in manifest table +//------------------------------------------------------------------------------------ +// alignment +// (PxConcreteType type, -----) +// alignment +// (PxConcreteType type, --------) +// alignment +// (PxConcreteType type, --) +// . +// . +// +// +// ----------------------------------------------------------------------------------- +//// extra data: +//// extra data memory block +//// serialized and deserialized by PxBase implementations +////---------------------------------------------------------------------------------- +// extra data +// +//------------------------------------------------------------------------------------ + +namespace +{ + + class LegacySerialStream : public PxSerializationContext + { + public: + LegacySerialStream(OutputStreamWriter& writer, + const PxCollection& collection, + bool exportNames) : mWriter(writer), mCollection(collection), mExportNames(exportNames) {} + void writeData(const void* buffer, PxU32 size) { mWriter.write(buffer, size); } + PxU32 getTotalStoredSize() { return mWriter.getStoredSize(); } + void alignData(PxU32 alignment) + { + if(!alignment) + return; + + PxI32 bytesToPad = PxI32(getPadding(getTotalStoredSize(), alignment)); + static const PxI32 BUFSIZE = 64; + char buf[BUFSIZE]; + PxMemSet(buf, 0, bytesToPad < BUFSIZE ? PxU32(bytesToPad) : PxU32(BUFSIZE)); + while(bytesToPad > 0) + { + writeData(buf, bytesToPad < BUFSIZE ? PxU32(bytesToPad) : PxU32(BUFSIZE)); + bytesToPad -= BUFSIZE; + } + PX_ASSERT(!getPadding(getTotalStoredSize(), alignment)); + } + + virtual void registerReference(PxBase&, PxU32, size_t) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Cannot register references during exportData, exportExtraData."); + } + + virtual const PxCollection& getCollection() const + { + return mCollection; + } + virtual void writeName(const char* name) + { + PxU32 len = name && mExportNames ? PxU32(strlen(name)) + 1 : 0; + writeData(&len, sizeof(len)); + if(len) writeData(name, len); + } + + private: + LegacySerialStream& operator=(const LegacySerialStream&); + OutputStreamWriter& mWriter; + const PxCollection& mCollection; + bool mExportNames; + }; + + void writeHeader(PxSerializationContext& stream, bool hasDeserializedAssets) + { + PX_UNUSED(hasDeserializedAssets); + struct Header + { + PxU32 header; + PxU32 version; + PxU32 binaryVersion; + PxU32 buildNumber; + PxU32 platformTag; + PxU32 markedPadding; + PxU32 materialOffset; + }; + + //serialized binary data. + const PxU32 header = PX_MAKE_FOURCC('S','E','B','D'); + stream.writeData(&header, sizeof(PxU32)); + + PxU32 version = PX_PHYSICS_VERSION; + stream.writeData(&version, sizeof(PxU32)); + + PxU32 binaryVersion = PX_BINARY_SERIAL_VERSION; + stream.writeData(&binaryVersion, sizeof(PxU32)); + + PxU32 buildNumber = 0; +#if defined(PX_BUILD_NUMBER) + buildNumber = PX_BUILD_NUMBER; +#endif + stream.writeData(&buildNumber, sizeof(PxU32)); + + PxU32 platformTag = getBinaryPlatformTag(); + stream.writeData(&platformTag, sizeof(PxU32)); + + PxU32 markedPadding = 0; +#if PX_CHECKED + if(!hasDeserializedAssets) + markedPadding = 1; +#endif + stream.writeData(&markedPadding, sizeof(PxU32)); + } +} + +bool PxSerialization::serializeCollectionToBinary(PxOutputStream& outputStream, PxCollection& pxCollection, PxSerializationRegistry& sr, const PxCollection* pxExternalRefs, bool exportNames) +{ + if(!PxSerialization::isSerializable(pxCollection, sr, pxExternalRefs)) + return false; + + Collection& collection = static_cast(pxCollection); + const Collection* externalRefs = static_cast(pxExternalRefs); + + //temporary memory stream which allows fixing up data up stream + + SerializationRegistry& sn = static_cast(sr); + + // sort collection by "order" value (this will be the order in which they get serialized) + sortCollection(collection, sn, false); + + //initialized the context with the sorted collection. + SerializationContext context(collection, externalRefs); + + // gather reference information + bool hasDeserializedAssets = false; + { + const PxU32 nb = collection.internalGetNbObjects(); + for(PxU32 i=0;igetConcreteType()); +#if PX_CHECKED + //can't guarantee marked padding for deserialized instances + if(!(s->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY)) + hasDeserializedAssets = true; +#endif + const PxSerializer* serializer = sn.getSerializer(s->getConcreteType()); + PX_ASSERT(serializer); + serializer->registerReferences(*s, context); + } + } + + // now start the actual serialization into the output stream + OutputStreamWriter writer(outputStream); + LegacySerialStream stream(writer, collection, exportNames); + + writeHeader(stream, hasDeserializedAssets); + + // write size of collection + stream.alignData(PX_SERIAL_ALIGN); + PxU32 nbObjectsInCollection = collection.internalGetNbObjects(); + stream.writeData(&nbObjectsInCollection, sizeof(PxU32)); + + // write the manifest table (PxU32 offset, PxConcreteType type) + { + Ps::Array manifestTable(collection.internalGetNbObjects()); + PxU32 headerOffset = 0; + for(PxU32 i=0;igetConcreteType()); + PxType concreteType = s->getConcreteType(); + const PxSerializer* serializer = sn.getSerializer(concreteType); + PX_ASSERT(serializer); + manifestTable[i] = ManifestEntry(headerOffset, concreteType); + PxU32 classSize = PxU32(serializer->getClassSize()); + headerOffset += getPadding(classSize, PX_SERIAL_ALIGN) + classSize; + } + stream.alignData(PX_SERIAL_ALIGN); + const PxU32 nb = manifestTable.size(); + stream.writeData(&nb, sizeof(PxU32)); + stream.writeData(manifestTable.begin(), manifestTable.size()*sizeof(ManifestEntry)); + + //store offset for end of object buffer (PxU32 offset) + stream.writeData(&headerOffset, sizeof(PxU32)); + } + + // write import references + { + const Ps::Array& importReferences = context.getImportReferences(); + stream.alignData(PX_SERIAL_ALIGN); + const PxU32 nb = importReferences.size(); + stream.writeData(&nb, sizeof(PxU32)); + stream.writeData(importReferences.begin(), importReferences.size()*sizeof(ImportReference)); + } + + // write export references + { + PxU32 nbIds = collection.getNbIds(); + Ps::Array exportReferences(nbIds); + //we can't get quickly from id to object index in collection. + //if we only need this here, its not worth to build a hash + nbIds = 0; + for (PxU32 i=0;i internalReferencesPtr(internalReferencesPtrMap.size()); + PxU32 nbInternalPtrReferences = 0; + for(InternalRefMap::Iterator iter = internalReferencesPtrMap.getIterator(); !iter.done(); ++iter) + internalReferencesPtr[nbInternalPtrReferences++] = InternalReferencePtr(iter->first.first, iter->first.second, iter->second); + + InternalRefMap& internalReferencesIdxMap = context.getInternalReferencesIdxMap(); + Ps::Array internalReferencesIdx(internalReferencesIdxMap.size()); + PxU32 nbInternalIdxReferences = 0; + for(InternalRefMap::Iterator iter = internalReferencesIdxMap.getIterator(); !iter.done(); ++iter) + internalReferencesIdx[nbInternalIdxReferences++] = InternalReferenceIdx(Ps::to32(iter->first.first), iter->first.second, iter->second); + + stream.alignData(PX_SERIAL_ALIGN); + + stream.writeData(&nbInternalPtrReferences, sizeof(PxU32)); + stream.writeData(internalReferencesPtr.begin(), internalReferencesPtr.size()*sizeof(InternalReferencePtr)); + + stream.writeData(&nbInternalIdxReferences, sizeof(PxU32)); + stream.writeData(internalReferencesIdx.begin(), internalReferencesIdx.size()*sizeof(InternalReferenceIdx)); + } + + // write object data + { + stream.alignData(PX_SERIAL_ALIGN); + const PxU32 nb = collection.internalGetNbObjects(); + for(PxU32 i=0;igetConcreteType()); + const PxSerializer* serializer = sn.getSerializer(s->getConcreteType()); + PX_ASSERT(serializer); + stream.alignData(PX_SERIAL_ALIGN); + serializer->exportData(*s, stream); + } + } + + // write extra data + { + const PxU32 nb = collection.internalGetNbObjects(); + for(PxU32 i=0;igetConcreteType()); + + const PxSerializer* serializer = sn.getSerializer(s->getConcreteType()); + PX_ASSERT(serializer); + + stream.alignData(PX_SERIAL_ALIGN); + serializer->exportExtraData(*s, stream); + } + } + + return true; +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp new file mode 100644 index 000000000..10ca76946 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp @@ -0,0 +1,166 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +/* +- get rid of STL + +- NpArticulationLinkArray with more than 4 entries +- big convexes Xbox => PC + +- put a cache in "convertClass" function +- remove MD one at a time and check what happens in the converter. Make it robust/report errors +- for xbox, compare against source xbox file if it exists +- maybe put some info in the header to display "File generated by ConvX 1.xx" on screen (to debug) +- report inconsistent naming convention in each class!!!! +- try to automatically discover padding bytes? use "0xcd" pattern? +* do last param of XXXX_ITEMS macro automatically +- what if source files are 64bits? can we safely convert a 64bit ptr to 32bit? +*/ + +#include "foundation/PxErrorCallback.h" +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxIO.h" +#include "SnConvX.h" +#include "serialization/SnSerializationRegistry.h" +#include +#include "PsFoundation.h" + +using namespace physx; + +Sn::ConvX::ConvX() : + mMetaData_Src (NULL), + mMetaData_Dst (NULL), + mOutStream (NULL), + mMustFlip (false), + mOutputSize (0), + mSrcPtrSize (0), + mDstPtrSize (0), + mNullPtr (false), + mNoOutput (false), + mMarkedPadding (false), + mNbErrors (0), + mNbWarnings (0), + mReportMode (PxConverterReportMode::eNORMAL), + mPerformConversion (true) +{ + // memset(mZeros, 0, CONVX_ZERO_BUFFER_SIZE); + memset(mZeros, 0x42, CONVX_ZERO_BUFFER_SIZE); +} + +Sn::ConvX::~ConvX() +{ + resetNbErrors(); + resetConvexFlags(); + releaseMetaData(); + resetUnions(); +} + +void Sn::ConvX::release() +{ + delete this; +} + +bool Sn::ConvX::setMetaData(PxInputStream& inputStream, MetaDataType type) +{ + resetNbErrors(); + return (loadMetaData(inputStream, type) != NULL); +} + +bool Sn::ConvX::setMetaData(PxInputStream& srcMetaData, PxInputStream& dstMetaData) +{ + releaseMetaData(); + resetUnions(); + + if(!setMetaData(srcMetaData, META_DATA_SRC)) + return false; + if(!setMetaData(dstMetaData, META_DATA_DST)) + return false; + + return true; +} + +bool Sn::ConvX::compareMetaData() const +{ + if (!mMetaData_Src || !mMetaData_Dst) { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "PxBinaryConverter: metadata not defined. Call PxBinaryConverter::setMetaData first.\n"); + return false; + } + + return mMetaData_Src->compare(*mMetaData_Dst); +} + +bool Sn::ConvX::convert(PxInputStream& srcStream, PxU32 srcSize, PxOutputStream& targetStream) +{ + if(!mMetaData_Src || !mMetaData_Dst) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "PxBinaryConverter: metadata not defined. Call PxBinaryConverter::setMetaData first.\n"); + return false; + } + + resetConvexFlags(); + resetNbErrors(); + + bool conversionStatus = false; + if(mPerformConversion) + { + if(srcSize == 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxBinaryConverter: source serialized data size is zero.\n"); + return false; + } + + void* memory = PX_ALLOC_TEMP(srcSize+ALIGN_FILE, "ConvX source file"); + void* memoryA = reinterpret_cast((size_t(memory) + ALIGN_FILE)&~(ALIGN_FILE-1)); + + const PxU32 nbBytesRead = srcStream.read(memoryA, srcSize); + if(nbBytesRead != srcSize) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxBinaryConverter: failure on reading source serialized data.\n"); + PX_FREE(memory); + return false; + } + + displayMessage(PxErrorCode::eDEBUG_INFO, "\n\nConverting...\n\n"); + + { + if(!initOutput(targetStream)) + { + PX_FREE(memory); + return false; + } + conversionStatus = convert(memoryA, int(srcSize)); + closeOutput(); + } + + PX_FREE(memory); + } + return conversionStatus; +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h new file mode 100644 index 000000000..9b07a101c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h @@ -0,0 +1,183 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PX_CONVX_H +#define PX_CONVX_H + +#include "foundation/PxErrors.h" +#include "PxBinaryConverter.h" +#include "PxTypeInfo.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PsArray.h" +#include "SnConvX_Common.h" +#include "SnConvX_Union.h" +#include "SnConvX_MetaData.h" +#include "SnConvX_Align.h" + +#define CONVX_ZERO_BUFFER_SIZE 256 + +namespace physx { + +class PxSerializationRegistry; + +namespace Sn { + + struct HeightFieldData; + class PointerRemap + { + public: + PointerRemap(); + ~PointerRemap(); + + bool checkRefIsNotUsed(PxU32 ref) const; + void setObjectRef(PxU64 object64, PxU32 ref); + bool getObjectRef(PxU64 object64, PxU32& ref) const; + + struct InternalData + { + PxU64 object; + PxU32 id; + }; + + Ps::Array mData; + }; + + class ConvX : public physx::PxBinaryConverter, public shdfnd::UserAllocated + { + public: + ConvX(); + virtual ~ConvX(); + + virtual void release(); + virtual void setReportMode(PxConverterReportMode::Enum mode) { mReportMode = mode; } + PX_FORCE_INLINE bool silentMode() const { return mReportMode==PxConverterReportMode::eNONE; } + PX_FORCE_INLINE bool verboseMode() const { return mReportMode==PxConverterReportMode::eVERBOSE; } + + virtual bool setMetaData(PxInputStream& srcMetaData, PxInputStream& dstMetaData); + virtual bool compareMetaData() const; + virtual bool convert(PxInputStream& srcStream, PxU32 srcSize, PxOutputStream& targetStream); + + private: + ConvX& operator=(const ConvX&); + bool setMetaData(PxInputStream& inputStream, MetaDataType type); + + // Meta-data + void releaseMetaData(); + const MetaData* loadMetaData(PxInputStream& inputStream, MetaDataType type); + const MetaData* getBinaryMetaData(MetaDataType type); + int getNbMetaClasses(MetaDataType type); + MetaClass* getMetaClass(unsigned int i, MetaDataType type) const; + MetaClass* getMetaClass(const char* name, MetaDataType type) const; + MetaClass* getMetaClass(PxConcreteType::Enum concreteType, MetaDataType type); + MetaData* mMetaData_Src; + MetaData* mMetaData_Dst; + + // Convert + + bool convert(const void* buffer, int fileSize); + void resetConvexFlags(); + void _enumerateFields(const MetaClass* mc, ExtraDataEntry2* entries, int& nb, int baseOffset, MetaDataType type) const; + void _enumerateExtraData(const char* address, const MetaClass* mc, ExtraDataEntry* entries, int& nb, int offset, MetaDataType type) const; + PxU64 read64(const void*& buffer); + int read32(const void*& buffer); + short read16(const void*& buffer); + bool convertClass(const char* buffer, const MetaClass* mc, int offset); + const char* convertExtraData_Array(const char* Address, const char* lastAddress, const char* objectAddress, const ExtraDataEntry& ed); + const char* convertExtraData_Ptr(const char* Address, const char* lastAddress, const PxMetaDataEntry& entry, int count, int ptrSize_Src, int ptrSize_Dst); + int getConcreteType(const char* buffer); + bool convertCollection(const void* buffer, int fileSize, int nbObjects); + const void* convertManifestTable(const void* buffer, int& fileSize); + const void* convertImportReferences(const void* buffer, int& fileSize); + const void* convertExportReferences(const void* buffer, int& fileSize); + const void* convertInternalReferences(const void* buffer, int& fileSize); + const void* convertReferenceTables(const void* buffer, int& fileSize, int& nbObjectsInCollection); + bool checkPaddingBytes(const char* buffer, int byteCount); + + // ---- big convex surgery ---- + PsArray mConvexFlags; + // Align + const char* alignStream(const char* buffer, int alignment=ALIGN_DEFAULT); + void alignTarget(int alignment); + + char mZeros[CONVX_ZERO_BUFFER_SIZE]; + // Unions + bool registerUnion(const char* name); + bool registerUnionType(const char* unionName, const char* typeName, int typeValue); + const char* getTypeName(const char* unionName, int typeValue); + void resetUnions(); + PsArray mUnions; + // Output + void setNullPtr(bool); + void setNoOutput(bool); + bool initOutput(PxOutputStream& targetStream); + void closeOutput(); + int getCurrentOutputSize(); + void output(short value); + void output(int value); + void output(PxU64 value); + void output(const char* buffer, int nbBytes); + void convert8 (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convertPad8 (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convert16 (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convert32 (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convert64 (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convertFloat(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convertPtr (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + PxOutputStream* mOutStream; + bool mMustFlip; + int mOutputSize; + int mSrcPtrSize; + int mDstPtrSize; + bool mNullPtr; + bool mNoOutput; + bool mMarkedPadding; + + // Errors + void resetNbErrors(); + int getNbErrors() const; + void displayMessage(physx::PxErrorCode::Enum code, const char* format, ...); + int mNbErrors; + int mNbWarnings; + + // Settings + PxConverterReportMode::Enum mReportMode; + bool mPerformConversion; + + // Remap pointers + void exportIntAsPtr(int value); + void exportInt(int value); + void exportInt64(PxU64 value); + PointerRemap mRemap; + PointerRemap* mActiveRemap; + PxU32 mPointerRemapCounter; + + friend class MetaData; + friend struct MetaClass; + }; +} } +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp new file mode 100644 index 000000000..45579ba90 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "SnConvX.h" +#include "SnConvX_Align.h" +#include +using namespace physx; + +void Sn::ConvX::alignTarget(int alignment) +{ + const int outputSize = getCurrentOutputSize(); + const PxU32 outPadding = getPadding(size_t(outputSize), PxU32(alignment)); + if(outPadding) + { + assert(outPadding + +using namespace physx; +using namespace physx::Sn; +using namespace Cm; + +void Sn::ConvX::resetConvexFlags() +{ + mConvexFlags.clear(); +} + +void Sn::ConvX::_enumerateFields(const MetaClass* mc, ExtraDataEntry2* entries, int& nb, int baseOffset, MetaDataType type) const +{ + PxU32 nbFields = mc->mFields.size(); + int offsetCheck = baseOffset; + for(PxU32 j=0;jmFields[j]; + if(entry.mFlags & PxMetaDataFlag::eCLASS || entry.mFlags & PxMetaDataFlag::eEXTRA_DATA) + continue; + + assert(offsetCheck == baseOffset + entry.mOffset); + + int currentOffset = baseOffset + entry.mOffset; + + //for(int c=0;cmCallback) + { + entries[nb].entry = entry; + entries[nb].offset = currentOffset; + entries[nb].cb = fieldType->mCallback; + nb++; + } + else + { + for(int c=0;cmFields.size(); + for(PxU32 j=0;jmFields[j]; + if(entry.mFlags & PxMetaDataFlag::eCLASS /*|| entry.mFlags & PxMetaDataFlag::ePTR*/ || entry.mFlags & PxMetaDataFlag::eTYPEDEF) + continue; + + const char* entryType = entry.mType; + + // + // Insanely Twisted Shadow GeometryUnion + // + // Special code is needed as long as there are no meta data tags to describe our unions properly. The way it is done here is + // not future-proof at all. There should be a tag to describe where the union type can be found and the number of bytes + // this type id needs. Then a mapping needs to get added from each union type id to the proper meta class name. + // + if (entry.mFlags & PxMetaDataFlag::eUNION) + { + if (!mc->mClassName || strcmp(mc->mClassName, "Gu::GeometryUnion")!=0) + continue; + else + { + // ### hardcoded bit here, will only work when union type is the first int of the struct + const int* tmp = reinterpret_cast(address + offset); + const int unionType = *tmp; + + ConvX* tmpConv = const_cast(this); // ... don't ask + const char* typeName = tmpConv->getTypeName(entry.mType, unionType); + assert(typeName); + + bool isTriMesh = (strcmp(typeName, "PxTriangleMeshGeometryLL") == 0); + bool isHeightField = (strcmp(typeName, "PxHeightFieldGeometryLL") == 0); + if (!isTriMesh && !isHeightField) + { + continue; + } + else + { + entryType = typeName; + } + } + } + + // MetaClass* extraDataType = getMetaClass(entry.mType, type); + // if(!extraDataType) + // continue; + + if(entry.mFlags & PxMetaDataFlag::eEXTRA_DATA) + { + entries[nb].entry = entry; + entries[nb].offset = offset+entry.mOffset; + nb++; + } + else + { + if(entry.mFlags & PxMetaDataFlag::ePTR) + continue; + + MetaClass* extraDataType = getMetaClass(entryType, type); + if(!extraDataType) + continue; + + if(!extraDataType->mCallback) + _enumerateExtraData(address, extraDataType, entries, nb, offset+entry.mOffset, type); + } + } +} + +PxU64 Sn::ConvX::read64(const void*& buffer) +{ + const PxU64* buf64 = reinterpret_cast(buffer); + buffer = reinterpret_cast(size_t(buffer) + sizeof(PxU64)); + PxU64 value = *buf64; + output(value); + return value; +} + +int Sn::ConvX::read32(const void*& buffer) +{ + const int* buf32 = reinterpret_cast(buffer); + buffer = reinterpret_cast(size_t(buffer) + sizeof(int)); + int value = *buf32; + output(value); + return value; +} + +short Sn::ConvX::read16(const void*& buffer) +{ + const short* buf16 = reinterpret_cast(buffer); + buffer = reinterpret_cast(size_t(buffer) + sizeof(short)); + short value = *buf16; + output(value); + return value; +} + +#if PX_CHECKED +extern const char* gVTable; +static bool compareEntries(const ExtraDataEntry2& e0, const ExtraDataEntry2& e1) +{ + if(e0.entry.isVTablePtr() && e1.entry.isVTablePtr()) + return true; + + if((e0.entry.mFlags & PxMetaDataFlag::eUNION) && (e1.entry.mFlags & PxMetaDataFlag::eUNION)) + { + if(e0.entry.mType && e1.entry.mType) + { + // We can't compare the ptrs since they index different string tables + if(strcmp(e0.entry.mType, e1.entry.mType)==0) + return true; + } + return false; + } + + if(e0.entry.mName && e1.entry.mName) + { + // We can't compare the ptrs since they index different string tables + if(strcmp(e0.entry.mName, e1.entry.mName)==0) + return true; + } + return false; +} +#endif + +// TODO: optimize this +bool Sn::ConvX::convertClass(const char* buffer, const MetaClass* mc, int offset) +{ + // ---- big convex surgery ---- + bool convexSurgery = false; + bool foundNbVerts = false; + bool removeBigData = false; + + // force reference + (void)foundNbVerts; + + displayMessage(PxErrorCode::eDEBUG_INFO, "%s\n", mc->mClassName); + displayMessage(PxErrorCode::eDEBUG_INFO, "+++++++++++++++++++++++++++++++++++++++++++++\n"); + + if(strcmp(mc->mClassName, "ConvexMesh")==0) + { + convexSurgery = true; + } + // ---- big convex surgery ---- + + int nbSrcEntries = 0; + PX_ALLOCA(srcEntries, ExtraDataEntry2, 256); // ### painful ctors here + int nbDstEntries = 0; + PX_ALLOCA(dstEntries, ExtraDataEntry2, 256); // ### painful ctors here + // Find corresponding meta-class for target platform + const MetaClass* target_mc = getMetaClass(mc->mClassName, META_DATA_DST); + assert(target_mc); + + if(mc->mCallback) + { + srcEntries[0].cb = mc->mCallback; + srcEntries[0].offset = offset; + srcEntries[0].entry.mType = mc->mClassName; + srcEntries[0].entry.mName = mc->mClassName; + srcEntries[0].entry.mOffset = offset; + srcEntries[0].entry.mSize = mc->mSize; + srcEntries[0].entry.mCount = 1; + srcEntries[0].entry.mFlags = 0; + nbSrcEntries = 1; + + assert(target_mc->mCallback); + dstEntries[0].cb = target_mc->mCallback; + dstEntries[0].offset = offset; + dstEntries[0].entry.mType = target_mc->mClassName; + dstEntries[0].entry.mName = target_mc->mClassName; + dstEntries[0].entry.mOffset = offset; + dstEntries[0].entry.mSize = target_mc->mSize; + dstEntries[0].entry.mCount = 1; + dstEntries[0].entry.mFlags = 0; + nbDstEntries = 1; + } + else + { + nbSrcEntries = 0; + _enumerateFields(mc, srcEntries, nbSrcEntries, 0, META_DATA_SRC); + assert(nbSrcEntries<256); + + nbDstEntries = 0; + _enumerateFields(target_mc, dstEntries, nbDstEntries, 0, META_DATA_DST); + assert(nbDstEntries<256); + + // nb = mc->mNbEntries; + // assert(nb>=0); + // memcpy(entries, mc->mEntries, nb*sizeof(ExtraDataEntry2)); + } + + int srcOffsetCheck = 0; + int dstOffsetCheck = 0; + int j = 0; + // Track cases where the vtable pointer location is different for different platforms. + // The variables indicate whether a platform has a vtable pointer entry that has not been converted yet + // and they will remember the index of the corrssponding entry. This works because there can only + // be one open vtable pointer entry at a time. + int srcOpenVTablePtrEntry = -1; + int dstOpenVTablePtrEntry = -1; + + //if the src and dst platform place the vtable pointers at different locations some fiddling with the iteration count can be necessary. + int addVTablePtrShiftIteration = 0; + const int maxNb = nbSrcEntries > nbDstEntries ? nbSrcEntries : nbDstEntries; + for(int i=0; i < (maxNb + addVTablePtrShiftIteration); i++) + { + + if (i < nbSrcEntries) + { + displayMessage(PxErrorCode::eDEBUG_INFO, "\t0x%p\t%02x\t%d\t%d\t%s", buffer + srcOffsetCheck, + static_cast(buffer[srcOffsetCheck]), srcOffsetCheck, srcEntries[i].entry.mOffset, srcEntries[i].entry.mName); + for (int byteCount = 1; byteCount < srcEntries[i].entry.mSize; ++byteCount) + displayMessage(PxErrorCode::eDEBUG_INFO, "\t0x%p\t%02x\t%d\t%d\t.", buffer + srcOffsetCheck + byteCount, + static_cast(buffer[srcOffsetCheck + byteCount]), srcOffsetCheck + byteCount, srcEntries[i].entry.mOffset + byteCount); + } + + bool handlePadding = true; + bool skipLoop = false; + while(handlePadding) + { + const int pad0 = i0) + { + displayMessage(PxErrorCode::eDEBUG_WARNING, + "PxBinaryConverter warning: Bytes after %s::%s don't look like padding bytes. Likely mismatch between binary data and metadata.\n", + mc->mClassName, srcEntries[i-1].entry.mName ); + } + else + displayMessage(PxErrorCode::eDEBUG_WARNING, + "PxBinaryConverter warning: Bytes after %s don't look like padding bytes. Likely mismatch between binary data and metadata.\n", + mc->mClassName); + + } +#endif + if(pad1) + { + // Both have padding + // ### check sizes, output bytes + if(srcEntries[i].entry.mSize==dstEntries[j].entry.mSize) + { + // I guess we can just go on with the normal code here + handlePadding = false; + } + else + { + // Output padding + assert(srcEntries[i].cb); + assert(srcEntries[i].offset == srcOffsetCheck); + + const int padSize = dstEntries[j].entry.mSize; + char* paddingBytes = reinterpret_cast(PX_ALLOC(sizeof(char)*padSize, "paddingByte")); + memset(paddingBytes, 0, size_t(padSize)); + assert(dstEntries[j].cb); + (this->*dstEntries[j].cb)(paddingBytes, dstEntries[j].entry, dstEntries[j].entry); + assert(dstOffsetCheck==dstEntries[j].offset); + dstOffsetCheck += padSize; + PX_FREE(paddingBytes); + + // srcEntries[i].cb(buffer+srcOffsetCheck, srcEntries[i].entry, dstEntries[j].entry); + // assert(dstOffsetCheck==dstEntries[j].offset); + // dstOffsetCheck += dstEntries[j].entry.mSize; + srcOffsetCheck += srcEntries[i].entry.mSize; + + // Skip dest padding field + j++; + + // continue; // ### BUG, doesn't go back to the "for" + skipLoop = true; + handlePadding = false; + } + } + else + { + // Src has padding, dst has not => skip conversion + // Don't increase j + skipLoop = true; + handlePadding = false; + srcOffsetCheck += srcEntries[i].entry.mSize; + } + } + else + { + if(pad1) + { + // Dst has padding, src has not + + // Output padding + const int padSize = dstEntries[j].entry.mSize; + char* paddingBytes = reinterpret_cast(PX_ALLOC(sizeof(char)*padSize, "paddingByte")); + memset(paddingBytes, 0, size_t(padSize)); + assert(dstEntries[j].cb); + (this->*dstEntries[j].cb)(paddingBytes, dstEntries[j].entry, dstEntries[j].entry); + assert(dstOffsetCheck==dstEntries[j].offset); + dstOffsetCheck += padSize; + PX_FREE(paddingBytes); + + // Skip dest padding field, keep same src field + j++; + } + else + { + assert(0); + } + } + } + else handlePadding = false; + } + + if(skipLoop) + continue; + + int modSrcOffsetCheck = srcOffsetCheck; + const ExtraDataEntry2* srcEntryPtr = &srcEntries[i]; + const ExtraDataEntry2* dstEntryPtr = &dstEntries[j]; + + bool isSrcVTablePtr = (i < nbSrcEntries) ? srcEntryPtr->entry.isVTablePtr() : false; + if (isSrcVTablePtr && (dstOpenVTablePtrEntry != -1)) + { + // vtable ptr position mismatch: + // this check is necessary to align src and dst index again when the + // dst vtable pointer has been written already and the src vtable ptr + // element is reached. + // + // i + // src: | a | b | vt-ptr | c | ... + // dst: | vt-ptr | a | b | c | ... + // j + // + // it needs special treatment because the following case fails otherwise + // i + // src: | a | b | vt-ptr | c | vt-ptr | ... + // dst: | vt-ptr | a | b | vt-ptr | c | ... + // j + + // + // This entry has been written already -> advance to next src entry + // + srcOffsetCheck += srcEntryPtr->entry.mSize; + i++; + isSrcVTablePtr = (i < nbSrcEntries) ? srcEntryPtr->entry.isVTablePtr() : false; + PX_ASSERT(dstOpenVTablePtrEntry < nbDstEntries); + PX_ASSERT(dstEntries[dstOpenVTablePtrEntry].entry.isVTablePtr()); + dstOpenVTablePtrEntry = -1; + PX_ASSERT(addVTablePtrShiftIteration == 0); + } + bool isDstVTablePtr = (j < nbDstEntries) ? dstEntryPtr->entry.isVTablePtr() : false; + if (isDstVTablePtr && (srcOpenVTablePtrEntry != -1)) + { + // i + // src: | vt-ptr | a | b | c | ... + // dst: | a | b | vt-ptr | c | ... + // j + + i--; // next iteration the current element should get processed + isSrcVTablePtr = true; + PX_ASSERT(srcOpenVTablePtrEntry < nbSrcEntries); + srcEntryPtr = &srcEntries[srcOpenVTablePtrEntry]; + PX_ASSERT(srcEntryPtr->entry.isVTablePtr()); + modSrcOffsetCheck = srcEntryPtr->offset; + srcOffsetCheck -= srcEntryPtr->entry.mSize; // to make sure total change is 0 after this iteration + srcOpenVTablePtrEntry = -1; + PX_ASSERT(addVTablePtrShiftIteration == 1); + addVTablePtrShiftIteration = 0; + } + + if(i==nbSrcEntries && j==nbDstEntries) + { + PX_ASSERT((srcOpenVTablePtrEntry == -1) && (dstOpenVTablePtrEntry == -1)); + break; + } + + if (isSrcVTablePtr || isDstVTablePtr) + { + if (!isSrcVTablePtr) + { + // i + // src: | a | b | vt-ptr | c | ... + // dst: | vt-ptr | a | b | c | ... + // j + + PX_ASSERT(dstOpenVTablePtrEntry == -1); // the other case should be detected and treated earlier + PX_ASSERT(srcOpenVTablePtrEntry == -1); + PX_ASSERT(addVTablePtrShiftIteration == 0); + + int k; + for(k=i+1; k < nbSrcEntries; k++) + { + if (srcEntries[k].entry.isVTablePtr()) + break; + } + PX_ASSERT(k < nbSrcEntries); + + srcEntryPtr = &srcEntries[k]; + modSrcOffsetCheck = srcEntryPtr->offset; + srcOffsetCheck -= srcEntryPtr->entry.mSize; // to make sure total change is 0 after this iteration + + dstOpenVTablePtrEntry = j; + i--; // to make sure the original entry gets processed in the next iteration + } + else if (!isDstVTablePtr) + { + // i ---> i + // src: | vt-ptr | a | b | c | ... + // dst: | a | b | vt-ptr | c | ... + // j + + PX_ASSERT(srcOpenVTablePtrEntry == -1); // the other case should be detected and treated earlier + PX_ASSERT(dstOpenVTablePtrEntry == -1); + PX_ASSERT(addVTablePtrShiftIteration == 0); + + srcOffsetCheck += srcEntryPtr->entry.mSize; + modSrcOffsetCheck = srcOffsetCheck; + srcOpenVTablePtrEntry = i; + i++; + srcEntryPtr = &srcEntries[i]; + + addVTablePtrShiftIteration = 1; // additional iteration might be needed to process vtable pointer at the end of a class + + PX_ASSERT((i < nbSrcEntries) && ((srcEntryPtr->entry.mFlags & PxMetaDataFlag::ePADDING) == 0)); + // if the second check fails, this whole section might have to be done before the padding bytes get processed. Not sure + // what other consequences that might have though. + } + } +#if PX_CHECKED + else + { + if(!compareEntries(*srcEntryPtr, *dstEntryPtr)) + { + displayMessage(PxErrorCode::eINVALID_PARAMETER, "\rConvX::convertClass: %s, src meta data and dst meta data don't match!", mc->mClassName); + return false; + } + } +#endif + + const ExtraDataEntry2& srcEntry = *srcEntryPtr; + const ExtraDataEntry2& dstEntry = *dstEntryPtr; + + if(srcEntry.entry.mFlags & PxMetaDataFlag::eUNION) + { + // ### hardcoded bit here, will only work when union type is the first int of the struct + const int* tmp = reinterpret_cast(buffer + modSrcOffsetCheck); + const int unionType = *tmp; + + const char* typeName = getTypeName(srcEntry.entry.mType, unionType); + assert(typeName); + + MetaClass* unionMC = getMetaClass(typeName, META_DATA_SRC); + assert(unionMC); + + convertClass(buffer + modSrcOffsetCheck, unionMC, 0); // ### recurse + + dstOffsetCheck += dstEntry.entry.mSize; + + MetaClass* targetUnionMC = getMetaClass(typeName, META_DATA_DST); + assert(targetUnionMC); + + const int delta = dstEntry.entry.mSize - targetUnionMC->mSize; + char* deltaBytes = reinterpret_cast(PX_ALLOC(sizeof(char)*delta, "deltaBytes")); + memset(deltaBytes, 0, size_t(delta)); + output(deltaBytes, delta); // Skip unused bytes at the end of the union + PX_FREE(deltaBytes); + srcOffsetCheck += srcEntry.entry.mSize; // do not use modSrcOffsetCheck here! + } + else + { + assert(srcEntry.cb); + assert(srcEntry.offset == modSrcOffsetCheck); + + // ---- big convex surgery ---- + if(convexSurgery) + { + if(strcmp(srcEntry.entry.mName, "mNbHullVertices")==0) + { + assert(srcEntry.entry.mSize==1); + const int nbVerts = int(*(buffer+modSrcOffsetCheck)); + assert(!foundNbVerts); + foundNbVerts = true; + + const int gaussMapLimit = getBinaryMetaData(META_DATA_DST)->getGaussMapLimit(); + if(nbVerts > gaussMapLimit) + { + // We need a gauss map and we have one => keep it + } + else + { + // We don't need a gauss map and we have one => remove it + removeBigData = true; + } + } + else + { + if(removeBigData) + { + const bool isBigConvexData = strcmp(srcEntry.entry.mType, "BigConvexData")==0 || + strcmp(srcEntry.entry.mType, "BigConvexRawData")==0; + if(isBigConvexData) + { + assert(foundNbVerts); + setNullPtr(true); + } + } + } + } + // ---- big convex surgery ---- + + (this->*srcEntry.cb)(buffer+modSrcOffsetCheck, srcEntry.entry, dstEntry.entry); + assert(dstOffsetCheck==dstEntry.offset); + dstOffsetCheck += dstEntry.entry.mSize; + srcOffsetCheck += srcEntry.entry.mSize; // do not use modSrcOffsetCheck here! + + // ---- big convex surgery ---- + if(convexSurgery && removeBigData) + setNullPtr(false); + // ---- big convex surgery ---- + } + + j++; + } + + displayMessage(PxErrorCode::eDEBUG_INFO, "---------------------------------------------\n"); + + while(jmSize); + assert(srcOffsetCheck==mc->mSize); + + // ---- big convex surgery ---- + if(convexSurgery) + mConvexFlags.pushBack(removeBigData); + // ---- big convex surgery ---- + + return true; +} + +// Handles data defined with PX_DEF_BIN_METADATA_EXTRA_ARRAY +const char* Sn::ConvX::convertExtraData_Array(const char* Address, const char* lastAddress, const char* objectAddress, + const ExtraDataEntry& ed) +{ + (void)lastAddress; + MetaClass* mc = getMetaClass(ed.entry.mType, META_DATA_SRC); + assert(mc); + + // PT: safe to cast to int here since we're reading a count. + const int count = int(peek(ed.entry.mSize, objectAddress + ed.offset, ed.entry.mFlags)); + + // if(ed.entry.mCount) // Reused as align value + if(ed.entry.mAlignment) + { + Address = alignStream(Address, ed.entry.mAlignment); + // Address = alignStream(Address, ed.entry.mCount); + assert(Address<=lastAddress); + } + + for(int c=0;cmSize; + assert(Address<=lastAddress); + } + return Address; +} + +const char* Sn::ConvX::convertExtraData_Ptr(const char* Address, const char* lastAddress, const PxMetaDataEntry& entry, int count, + int ptrSize_Src, int ptrSize_Dst) +{ + (void)lastAddress; + + PxMetaDataEntry tmpSrc = entry; + tmpSrc.mCount = count; + tmpSrc.mSize = count * ptrSize_Src; + + PxMetaDataEntry tmpDst = entry; + tmpDst.mCount = count; + tmpDst.mSize = count * ptrSize_Dst; + + + displayMessage(PxErrorCode::eDEBUG_INFO, "extra data ptrs\n"); + displayMessage(PxErrorCode::eDEBUG_INFO, "+++++++++++++++++++++++++++++++++++++++++++++\n"); + displayMessage(PxErrorCode::eDEBUG_INFO, "\t0x%p\t%02x\t\t\t%s", Address, static_cast(Address[0]), entry.mName); + for (int byteCount = 1; byteCount < ptrSize_Src*count; ++byteCount) + displayMessage(PxErrorCode::eDEBUG_INFO, "\t0x%p\t%02x\t\t\t.", Address + byteCount, static_cast(Address[byteCount])); + + convertPtr(Address, tmpSrc, tmpDst); + Address += count * ptrSize_Src; + assert(Address<=lastAddress); + return Address; +} + +static bool decodeControl(PxU64 control, const ExtraDataEntry& ed, PxU64 controlMask = 0) +{ + if(ed.entry.mFlags & PxMetaDataFlag::eCONTROL_FLIP) + { + if(controlMask) + { + return (control & controlMask) ? false : true; + } + else + { + return control==0; + } + } + else + { + if(controlMask) + { + return (control & controlMask) ? true : false; + } + else + { + return control!=0; + } + } + +} + +// ### currently hardcoded, should change +int Sn::ConvX::getConcreteType(const char* buffer) +{ + MetaClass* mc = getMetaClass("PxBase", META_DATA_SRC); + assert(mc); + PxMetaDataEntry entry; + if(mc->getFieldByType("PxType", entry)) + { + // PT: safe to cast to int here since we're reading our own PxType + return int(peek(entry.mSize, buffer + entry.mOffset)); + } + assert(0); + return 0xffffffff; +} + +struct Item : public shdfnd::UserAllocated +{ + MetaClass* mc; + const char* address; +}; + +bool Sn::ConvX::convertCollection(const void* buffer, int fileSize, int nbObjects) +{ + const char* lastAddress = reinterpret_cast(buffer) + fileSize; + const char* Address = alignStream(reinterpret_cast(buffer)); + + const int ptrSize_Src = mSrcPtrSize; + const int ptrSize_Dst = mDstPtrSize; + Item* objects = PX_NEW(Item)[PxU32(nbObjects)]; + + for(PxU32 i=0;imSize; + assert(Address<=lastAddress); + } + + // Fields / extra data + if(1) + { + // ---- big convex surgery ---- + unsigned int nbConvexes = 0; + // ---- big convex surgery ---- + //const char* StartAddress2 = Address; + //int startDstSize2 = getCurrentOutputSize(); + for(int i=0;imClassName); + // if(strcmp(mc->mClassName, "TriangleMesh")==0) + // if(strcmp(mc->mClassName, "NpRigidDynamic")==0) + if(strcmp(mc0->mClassName, "HybridModel")==0) + { + int stop=1; + (void)(stop); + } + + // ### we actually need to collect all extra data for this class, including data from embedded members. + + PX_ALLOCA(entries, ExtraDataEntry, 256); + int nbEntries = 0; + _enumerateExtraData(objectAddress, mc0, entries, nbEntries, 0, META_DATA_SRC); + assert(nbEntries<256); + + Address = alignStream(Address); + assert(Address<=lastAddress); + + for(int j=0;jmSize; + assert(Address<=lastAddress); + + // Enumerate extra data for this optional class, and convert it too. + // This assumes the extra data for the optional class is always appended to the class itself, + // which is something we'll need to enforce in the SDK. So far this is only to handle optional + // inline arrays. + + // ### this should probably be recursive eventually + PX_ALLOCA(entries2, ExtraDataEntry, 256); + int nbEntries2 = 0; + _enumerateExtraData(objectAddress, extraDataType, entries2, nbEntries2, 0, META_DATA_SRC); + assert(nbEntries2<256); + for(int k=0;k> 16; + } + + if(decodeControl(controlValue2, ed2, controlMask)) + { + // PT: safe to cast to int here since we're reading a count + int count = int(peek(countSize, classAddress + countOffset, ed2.entry.mFlags)); + + if(ed2.entry.mAlignment) + { + assert(0); // Never tested + Address = alignStream(Address, ed2.entry.mAlignment); + assert(Address<=lastAddress); + } + + if(ed2.entry.mFlags & PxMetaDataFlag::ePTR) + { + assert(0); // Never tested + } + else + { + MetaClass* mc = getMetaClass(ed2.entry.mType, META_DATA_SRC); + assert(mc); + + while(count--) + { + convertClass(Address, mc, 0); + Address += mc->mSize; + assert(Address<=lastAddress); + } + } + + } + } + else + { + if( (ed2.entry.mFlags & PxMetaDataFlag::eALIGNMENT) && ed2.entry.mAlignment) + { + Address = alignStream(Address, ed2.entry.mAlignment); + assert(Address<=lastAddress); + } + else + { + // We assume it's an normal array, e.g. the ones from "big convexes" + assert(!(ed2.entry.mFlags & PxMetaDataFlag::eEXTRA_ITEM)); + + Address = convertExtraData_Array(Address, lastAddress, classAddress, ed2); + } + } + } + + } + else + { + int stop = 0; + (void)(stop); + } + + // ---- big convex surgery ---- + setNoOutput(false); + // ---- big convex surgery ---- + } + else if(ed.entry.mFlags & PxMetaDataFlag::eEXTRA_ITEMS) + { + // PX_DEF_BIN_METADATA_EXTRA_ITEMS + int reloc = ed.offset - ed.entry.mOffset; // ### because the enum code only fixed the "controlOffset"! + const int controlOffset = ed.entry.mOffset; + const int controlSize = ed.entry.mSize; + const int countOffset = ed.entry.mCount; + const int countSize = ed.entry.mOffsetSize; + + // const int controlValue2 = peek(controlSize, objectAddress + controlOffset); + const PxU64 controlValue2 = peek(controlSize, objectAddress + controlOffset + reloc); + + PxU64 controlMask = 0; + if(ed.entry.mFlags & PxMetaDataFlag::eCONTROL_MASK) + { + controlMask = PxU64(ed.entry.mFlags & (PxMetaDataFlag::eCONTROL_MASK_RANGE << 16)); + controlMask = controlMask >> 16; + } + + if(decodeControl(controlValue2, ed, controlMask)) + { + // PT: safe to cast to int here since we're reading a count + // int count = peek(countSize, objectAddress + countOffset); // ### + int count = int(peek(countSize, objectAddress + countOffset + reloc, ed.entry.mFlags)); // ### + + if(ed.entry.mAlignment) + { + Address = alignStream(Address, ed.entry.mAlignment); + assert(Address<=lastAddress); + } + + if(ed.entry.mFlags & PxMetaDataFlag::ePTR) + { + Address = convertExtraData_Ptr(Address, lastAddress, ed.entry, count, ptrSize_Src, ptrSize_Dst); + } + else + { + MetaClass* mc = getMetaClass(ed.entry.mType, META_DATA_SRC); + assert(mc); + + while(count--) + { + convertClass(Address, mc, 0); + Address += mc->mSize; + assert(Address<=lastAddress); + } + } + } + + } + else if(ed.entry.mFlags & PxMetaDataFlag::eALIGNMENT) + { + if(ed.entry.mAlignment) + { + displayMessage(PxErrorCode::eDEBUG_INFO, " align to %d bytes\n", ed.entry.mAlignment); + displayMessage(PxErrorCode::eDEBUG_INFO, "---------------------------------------------\n"); + + Address = alignStream(Address, ed.entry.mAlignment); + assert(Address<=lastAddress); + } + } + else if(ed.entry.mFlags & PxMetaDataFlag::eEXTRA_NAME) + { + if(ed.entry.mAlignment) + { + Address = alignStream(Address, ed.entry.mAlignment); + assert(Address<=lastAddress); + } + + //get string count + MetaClass* mc = getMetaClass("PxU32", META_DATA_SRC); + assert(mc); + //safe to cast to int here since we're reading a count. + const int count = int(peek(mc->mSize, Address, 0)); + + displayMessage(PxErrorCode::eDEBUG_INFO, " convert %d bytes string\n", count); + + convertClass(Address, mc, 0); + Address += mc->mSize; + + mc = getMetaClass(ed.entry.mType, META_DATA_SRC); + assert(mc); + + for(int c=0;cmSize; + assert(Address<=lastAddress); + } + } + else + { + Address = convertExtraData_Array(Address, lastAddress, objectAddress, ed); + } + } + } + PX_DELETE_ARRAY(objects); + assert(nbConvexes==mConvexFlags.size()); + } + + assert(Address==lastAddress); + + return true; +} + +bool Sn::ConvX::convert(const void* buffer, int fileSize) +{ + // Test initial alignment + if(size_t(buffer) & (ALIGN_DEFAULT-1)) + { + assert(0); + return false; + } + + const int header = read32(buffer); fileSize -= 4; (void)header; + + if (header != PX_MAKE_FOURCC('S','E','B','D')) + { + displayMessage(physx::PxErrorCode::eINVALID_PARAMETER, + "PxBinaryConverter: Buffer contains data with bad header indicating invalid serialized data."); + return false; + } + + const int version = read32(buffer); fileSize -= 4; (void)version; + + const int binaryVersion = read32(buffer); fileSize -= 4; + + if (!checkCompatibility(PxU32(version), PxU32(binaryVersion))) + { + char buf[512]; + getCompatibilityVersionsStr(buf, 512); + + displayMessage(physx::PxErrorCode::eINVALID_PARAMETER, + "PxBinaryConverter: Buffer contains data version (%x-%d) is incompatible with this PhysX sdk.\n These versions would be compatible: %s", + version, binaryVersion, buf); + return false; + } + const int buildNumber = read32(buffer); fileSize -= 4; (void)buildNumber; + + //read src platform tag and write dst platform tag according dst meta data + const int srcPlatformTag = *reinterpret_cast(buffer); + buffer = reinterpret_cast(size_t(buffer) + 4); + fileSize -= 4; + const int dstPlatformTag = mMetaData_Dst->getPlatformTag(); + output(dstPlatformTag); + + if (srcPlatformTag != mMetaData_Src->getPlatformTag()) + { + displayMessage(physx::PxErrorCode::eINVALID_PARAMETER, + "PxBinaryConverter: Mismatch of platform tags of binary data and metadata:\n Binary Data: %s\n MetaData: %s\n", + getBinaryPlatformName(PxU32(srcPlatformTag)), + getBinaryPlatformName(PxU32(mMetaData_Src->getPlatformTag()))); + return false; + } + + //read whether input data has marked padding, and set it for the output data (since 0xcd is written into pads on conversion) + const int srcMarkedPadding = *reinterpret_cast(buffer); + buffer = reinterpret_cast(size_t(buffer) + 4); + fileSize -= 4; + mMarkedPadding = srcMarkedPadding != 0; + const int dstMarkedPadding = 1; + output(dstMarkedPadding); + + int nbObjectsInCollection; + + buffer = convertReferenceTables(buffer, fileSize, nbObjectsInCollection); + if(!buffer) + return false; + + bool ret = convertCollection(buffer, fileSize, nbObjectsInCollection); + mMarkedPadding = false; + return ret; +} + +// PT: code below added to support 64bit-to-32bit conversions +void Sn::ConvX::exportIntAsPtr(int value) +{ + const int ptrSize_Src = mSrcPtrSize; + const int ptrSize_Dst = mDstPtrSize; + + PxMetaDataEntry entry; + + const char* address = NULL; + const PxU32 value32 = PxU32(value); + const PxU64 value64 = PxU64(value)&0xffffffff; + + if(ptrSize_Src==4) + { + address = reinterpret_cast(&value32); + } + else if(ptrSize_Src==8) + { + address = reinterpret_cast(&value64); + } + else assert(0); + + convertExtraData_Ptr(address, address + ptrSize_Src, entry, 1, ptrSize_Src, ptrSize_Dst); +} + +void Sn::ConvX::exportInt(int value) +{ + output(value); +} + +void Sn::ConvX::exportInt64(PxU64 value) +{ + output(value); +} + +PointerRemap::PointerRemap() +{ +} + +PointerRemap::~PointerRemap() +{ +} + +bool PointerRemap::checkRefIsNotUsed(PxU32 ref) const +{ + const PxU32 size = mData.size(); + for(PxU32 i=0;i(buffer)); + fileSize -= padding; + int nb = read32(buffer); + fileSize -= 4; + + MetaClass* mc_src = getMetaClass("Sn::ManifestEntry", META_DATA_SRC); + assert(mc_src); + + MetaClass* mc_dst = getMetaClass("Sn::ManifestEntry", META_DATA_DST); + assert(mc_dst); + + bool mdOk; + PxMetaDataEntry srcTypeField; + mdOk = mc_src->getFieldByName("type", srcTypeField); + PX_UNUSED(mdOk); + PX_ASSERT(mdOk); + + PxMetaDataEntry dstOffsetField; + mdOk = mc_dst->getFieldByName("offset", dstOffsetField); + PX_ASSERT(mdOk); + + const char* address = reinterpret_cast(buffer); + PxU32 headerOffset = 0; + for(int i=0;imSize)); + + //restore output state + mOutStream = outStream; + mOutputSize = int(outputSize); + } + + //output patched offset + PX_ASSERT(dstOffsetField.mOffset == 0); //assuming offset is the first data + output(int(headerOffset)); + + //output rest of ManifestEntry + PxU32 restSize = PxU32(mc_dst->mSize - dstOffsetField.mSize); + mOutStream->write(tmpStream.getData() + dstOffsetField.mSize, restSize); + mOutputSize += restSize; + + //increment source stream + address += mc_src->mSize; + fileSize -= mc_src->mSize; + assert(fileSize>=0); + + //update headerOffset using the type and dst meta data of the type + MetaClass* mc_classType_dst = getMetaClass(classType, META_DATA_DST); + if(!mc_classType_dst) + return NULL; + headerOffset += getPadding(size_t(mc_classType_dst->mSize), PX_SERIAL_ALIGN) + mc_classType_dst->mSize; + } + + output(int(headerOffset)); //endoffset + buffer = address + 4; + fileSize -= 4; + return buffer; +} + +const void* Sn::ConvX::convertImportReferences(const void* buffer, int& fileSize) +{ + PxU32 padding = getPadding(size_t(buffer), ALIGN_DEFAULT); + buffer = alignStream(reinterpret_cast(buffer)); + fileSize -= padding; + int nb = read32(buffer); + fileSize -= 4; + + if(!nb) + return buffer; + + MetaClass* mc = getMetaClass("Sn::ImportReference", META_DATA_SRC); + assert(mc); + + const char* address = reinterpret_cast(buffer); + for(int i=0;imSize; + fileSize -= mc->mSize; + assert(fileSize>=0); + } + return address; +} + +const void* Sn::ConvX::convertExportReferences(const void* buffer, int& fileSize) +{ + PxU32 padding = getPadding(size_t(buffer), ALIGN_DEFAULT); + buffer = alignStream(reinterpret_cast(buffer)); + fileSize -= padding; + int nb = read32(buffer); + fileSize -= 4; + + if(!nb) + return buffer; + + MetaClass* mc = getMetaClass("Sn::ExportReference", META_DATA_SRC); + assert(mc); + + const char* address = reinterpret_cast(buffer); + for(int i=0;imSize; + fileSize -= mc->mSize; + assert(fileSize>=0); + } + return address; +} + +const void* Sn::ConvX::convertInternalReferences(const void* buffer, int& fileSize) +{ + PxU32 padding = getPadding(size_t(buffer), ALIGN_DEFAULT); + buffer = alignStream(reinterpret_cast(buffer)); + fileSize -= padding; + + //pointer references + int nbPtrReferences = read32(buffer); + fileSize -= 4; + if(nbPtrReferences) + { + const char* address = reinterpret_cast(buffer); + MetaClass* mc = getMetaClass("Sn::InternalReferencePtr", META_DATA_SRC); + assert(mc); + for(int i=0;imSize; + fileSize -= mc->mSize; + assert(fileSize>=0); + } + buffer = address; + } + + //index references + int nbIdxReferences = read32(buffer); + fileSize -= 4; + if (nbIdxReferences) + { + const char* address = reinterpret_cast(buffer); + MetaClass* mc = getMetaClass("Sn::InternalReferenceIdx", META_DATA_SRC); + assert(mc); + for(int i=0;imSize; + fileSize -= mc->mSize; + assert(fileSize>=0); + } + buffer = address; + } + return buffer; +} + + +const void* Sn::ConvX::convertReferenceTables(const void* buffer, int& fileSize, int& nbObjectsInCollection) +{ + // PT: the map should not be used while creating it, so use one indirection + mActiveRemap = NULL; + mRemap.mData.clear(); + mPointerRemapCounter = 0; + + PxU32 padding = getPadding(size_t(buffer), ALIGN_DEFAULT); + buffer = alignStream(reinterpret_cast(buffer)); + fileSize -= padding; + + nbObjectsInCollection = read32(buffer); + if (nbObjectsInCollection == 0) + displayMessage(PxErrorCode::eDEBUG_INFO, "\n\nConverting empty collection!\n\n"); + fileSize -= 4; + + buffer = convertManifestTable(buffer, fileSize); + + if(!buffer) + return NULL; + + buffer = convertImportReferences(buffer, fileSize); + buffer = convertExportReferences(buffer, fileSize); + buffer = convertInternalReferences(buffer, fileSize); + + // PT: the map can now be used + mActiveRemap = &mRemap; + + return buffer; +} + +bool Sn::ConvX::checkPaddingBytes(const char* buffer, int byteCount) +{ + const unsigned char* src = reinterpret_cast(buffer); + + int i = 0; + while ((i < byteCount) && (src[i] == 0xcd)) + i++; + return (i == byteCount); +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp new file mode 100644 index 000000000..f67666f88 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "foundation/PxErrorCallback.h" +#include "SnConvX.h" +#include +#include "PsString.h" +#include "PsFoundation.h" + +#define MAX_DISPLAYED_ISSUES 10 + +using namespace physx; + +void Sn::ConvX::resetNbErrors() +{ + mNbErrors = 0; + mNbWarnings = 0; +} + +int Sn::ConvX::getNbErrors() const +{ + return mNbErrors; +} + +void Sn::ConvX::displayMessage(PxErrorCode::Enum code, const char* format, ...) +{ + if(silentMode()) + return; + + int sum = mNbWarnings + mNbErrors; + if(sum >= MAX_DISPLAYED_ISSUES) + return; + + bool display = false; + + if(code==PxErrorCode::eINTERNAL_ERROR || code==PxErrorCode::eINVALID_OPERATION || code==PxErrorCode::eINVALID_PARAMETER) + { + mNbErrors++; + display = true; + } + else if(code == PxErrorCode::eDEBUG_WARNING) + { + mNbWarnings++; + display = true; + } + + if(display || ((sum == 0) && verboseMode()) ) + { + va_list va; + va_start(va, format); + Ps::getFoundation().errorImpl(code, __FILE__, __LINE__, format, va); + va_end(va); + } + + if(display) + { + if( sum == 0) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_INFO, __FILE__, __LINE__, "Hit warnings or errors: skipping further verbose output.\n"); + } + else if(sum == MAX_DISPLAYED_ISSUES-1) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_INFO, __FILE__, __LINE__, "Exceeding 10 warnings or errors: skipping further output.\n"); + } + } + + return; +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp new file mode 100644 index 000000000..64fbb942a --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp @@ -0,0 +1,960 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "foundation/PxIO.h" +#include "foundation/PxMemory.h" +#include "SnConvX.h" +#include "common/PxSerialFramework.h" +#include "serialization/SnSerialUtils.h" +#include + +using namespace physx; +using namespace physx::Sn; + +//#define REMOVE_EXPLICIT_PADDING + +static const char gVTablePtr[] = "v-table ptr"; +static const char gAutoPadding[] = "auto-generated padding"; +static const char gByte[] = "paddingByte"; + +/////////////////////////////////////////////////////////////////////////////// + +bool PxMetaDataEntry::isVTablePtr() const +{ + return mType==gVTablePtr; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool MetaClass::getFieldByType(const char* type, PxMetaDataEntry& entry) const +{ + assert(type); + PxU32 nbFields = mFields.size(); + for(PxU32 i=0;i(PX_ALLOC(sizeof(bool)*mSize, "bool")); + memset(map, 0, size_t(mSize)); + + const PxU32 nbFields = mFields.size(); + for(PxU32 i=0;i=0 && byteStart=0 && byteEnd<=mSize); + + int startOffset = -1; + int nbBytes = 0; + for(int j=byteStart;jmSize*current.mCount==current.mSize); + } + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +MetaData::MetaData(ConvX& convx) : + mConvX (convx), + mType (META_DATA_NONE), + mNbEntries (0), + mEntries (NULL), + mStringTable (NULL), + mVersion (0), + mBuildNumber (0), + mSizeOfPtr (0), + mPlatformTag (0), + mGaussMapLimit (0), + mFlip (false) +{ +} + +MetaData::~MetaData() +{ + PxU32 nbMetaClasses = mMetaClasses.size(); + for(PxU32 i=0;imClassName, name)==0) + { + while(current->mMaster) + current = current->mMaster; + return current; + } + } + return NULL; +} + +MetaClass* MetaData::getMetaClass(PxConcreteType::Enum concreteType) const +{ + for(PxU32 i=0; i< mConcreteTypeTable.size(); i++) + { + if(mConcreteTypeTable[i].first == concreteType) + { + const char* className = offsetToText(reinterpret_cast(size_t(mConcreteTypeTable[i].second))); + return getMetaClass(className); + } + } + return NULL; +} + +MetaClass* MetaData::addNewClass(const char* name, int size, MetaClass* master, ConvertCallback callback) +{ + // PT: if you reach this assert, you used PX_DEF_BIN_METADATA_TYPEDEF twice on the same type + assert(!getMetaClass(name)); + MetaClass* mc = PX_NEW(MetaClass); + mc->mCallback = callback; + mc->mMaster = master; + mc->mClassName = name; + mc->mSize = size; + mc->mDepth = 0; + mc->mProcessed = false; +// mc->mNbEntries = -1; + + mMetaClasses.pushBack(mc); + + return mc; +} + +bool MetaData::load(PxInputStream& inputStream, MetaDataType type) +{ + assert(type!=META_DATA_NONE); + + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, "Loading %s meta-data...\n", type==META_DATA_SRC ? "source" : "target"); + + mType = type; + + mFlip = false; + { + int header; + inputStream.read(&header, 4); + if(header==PX_MAKE_FOURCC('M','E','T','A')) + { + mFlip = false; + } + else if(header==PX_MAKE_FOURCC('A','T','E','M')) + { + mFlip = true; + } + else + { + mConvX.displayMessage(PxErrorCode::eINVALID_PARAMETER, "PxBinaryConverter: invalid meta-data file!\n"); + return false; + } + + if (type == META_DATA_SRC && mFlip) + { + mConvX.displayMessage(PxErrorCode::eINVALID_PARAMETER, + "PxBinaryConverter: source meta data needs to match endianness with current system!"); + return false; + } + + inputStream.read(&mVersion, 4); + inputStream.read(&mBinaryVersion, 4); + if(mFlip) + { + flip(mVersion); + flip(mBinaryVersion); + } + + if (!checkCompatibility(PxU32(mVersion), PxU32(mBinaryVersion))) + { + char buffer[512]; + getCompatibilityVersionsStr(buffer, 512); + + mConvX.displayMessage(PxErrorCode::eINVALID_PARAMETER, + "PxBinaryConverter: data version (%x-%d) is incompatible with this PhysX sdk.\n These versions would be compatible: %s", + mVersion, mBinaryVersion, buffer); + + return false; + } + inputStream.read(&mBuildNumber, 4); + if(mFlip) + flip(mBuildNumber); + + + inputStream.read(&mSizeOfPtr, 4); + if(mFlip) + flip(mSizeOfPtr); + + inputStream.read(&mPlatformTag, 4); + if(mFlip) + flip(mPlatformTag); + + if (!Sn::isBinaryPlatformTagValid(PxU32(mPlatformTag))) + { + mConvX.displayMessage(PxErrorCode::eINVALID_PARAMETER, "PxBinaryConverter: Unknown meta data platform tag"); + return false; + } + + inputStream.read(&mGaussMapLimit, 4); + if(mFlip) + flip(mGaussMapLimit); + + inputStream.read(&mNbEntries, 4); + if(mFlip) + flip(mNbEntries); + + mEntries = PX_NEW(PxMetaDataEntry)[PxU32(mNbEntries)]; + if(mSizeOfPtr==8) + { + for(int i=0;i(size_t(tmp.mType)); + mEntries[i].mName = reinterpret_cast(size_t(tmp.mName)); + mEntries[i].mOffset = tmp.mOffset; + mEntries[i].mSize = tmp.mSize; + mEntries[i].mCount = tmp.mCount; + mEntries[i].mOffsetSize = tmp.mOffsetSize; + mEntries[i].mFlags = tmp.mFlags; + mEntries[i].mAlignment = tmp.mAlignment; + } + } + else + { + assert(mSizeOfPtr==4); +// inputStream.read(mEntries, mNbEntries*sizeof(PxMetaDataEntry)); + for(int i=0;i(size_t(tmp.mType)); + mEntries[i].mName = reinterpret_cast(size_t(tmp.mName)); + mEntries[i].mOffset = tmp.mOffset; + mEntries[i].mSize = tmp.mSize; + mEntries[i].mCount = tmp.mCount; + mEntries[i].mOffsetSize = tmp.mOffsetSize; + mEntries[i].mFlags = tmp.mFlags; + mEntries[i].mAlignment = tmp.mAlignment; + } + } + + if(mFlip) + { + for(int i=0;i(PxConcreteType::Enum(concreteType), nameOffset) ); + } + + int tableSize; + inputStream.read(&tableSize, 4); + if(mFlip) + flip(tableSize); + + mStringTable = reinterpret_cast(PX_ALLOC(sizeof(char)*tableSize, "MetaData StringTable")); + inputStream.read(mStringTable, PxU32(tableSize)); + } + + // Register atomic types + { + addNewClass("bool", 1, NULL, &ConvX::convert8); + addNewClass("char", 1, NULL, &ConvX::convert8); + addNewClass("short", 2, NULL, &ConvX::convert16); + addNewClass("int", 4, NULL, &ConvX::convert32); + addNewClass("PxU64", 8, NULL, &ConvX::convert64); + addNewClass("float", 4, NULL, &ConvX::convertFloat); + + addNewClass("paddingByte", 1, NULL, &ConvX::convertPad8); + } + + { + MetaClass* currentClass = NULL; + for(int i=0;i %s\n", mEntries[i].mName, mEntries[i].mType); + MetaClass* mc = getMetaClass(mEntries[i].mName); + if(mc) + addNewClass(mEntries[i].mType, mc->mSize, mc, mc->mCallback); + else + mConvX.displayMessage(PxErrorCode::eINTERNAL_ERROR, + "PxBinaryConverter: Invalid typedef - Missing metadata for: %s, please check the source metadata.\n" + , mEntries[i].mName); + } + else if(mEntries[i].mFlags & PxMetaDataFlag::eCLASS) + { + if(!mEntries[i].mName) + { + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, "Found class: %s\n", mEntries[i].mType); + currentClass = addNewClass(mEntries[i].mType, mEntries[i].mSize); + + if(mEntries[i].mFlags & PxMetaDataFlag::eVIRTUAL) + { + PxMetaDataEntry vtable; + vtable.mType = gVTablePtr; + vtable.mName = gVTablePtr; + vtable.mOffset = 0; + vtable.mSize = mSizeOfPtr; + vtable.mCount = 1; + vtable.mFlags = PxMetaDataFlag::ePTR; + currentClass->mFields.pushBack(vtable); + } + } + else + { + assert(currentClass); + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, " - inherits from: %s\n", mEntries[i].mName); + currentClass->mBaseClasses.pushBack(mEntries[i]); + } + } + else + { + const int isUnion = mEntries[i].mFlags & PxMetaDataFlag::eUNION; + + if(isUnion && !mEntries[i].mSize) + { + mConvX.registerUnionType(mEntries[i].mType, mEntries[i].mName, mEntries[i].mOffset); + } + else + { + if(isUnion) + { + mConvX.registerUnion(mEntries[i].mType); + } + + const int isPadding = mEntries[i].mFlags & PxMetaDataFlag::ePADDING; + + assert(currentClass); +#ifdef REMOVE_EXPLICIT_PADDING + if(!isPadding) +#endif + currentClass->mFields.pushBack(mEntries[i]); + + if(isPadding) + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, + " - contains padding: %s - %s\n", mEntries[i].mType, mEntries[i].mName); + else if(mEntries[i].mFlags & PxMetaDataFlag::eEXTRA_DATA) + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, + " - contains extra data: %s%s\n", mEntries[i].mType, mEntries[i].mFlags & PxMetaDataFlag::ePTR ? "*" : ""); + else + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, + " - contains field: %s%s\n", mEntries[i].mType, mEntries[i].mFlags & PxMetaDataFlag::ePTR ? "*" : ""); + + } + } + } + } + + // Sort classes by depth + struct Local + { + static bool _computeDepth(const MetaData& md, MetaClass* current, int currentDepth, int& maxDepth) + { + if(currentDepth>maxDepth) + maxDepth = currentDepth; + + PxU32 nbBases = current->mBaseClasses.size(); + for(PxU32 i=0;imBaseClasses[i]; + MetaClass* baseClass = md.getMetaClass(baseClassEntry.mName); + if(!baseClass) + { + md.mConvX.displayMessage(PxErrorCode::eINTERNAL_ERROR, + "PxBinaryConverter: Can't find class %s metadata, please check the source metadata.\n", baseClassEntry.mName); + return false; + } + if (!_computeDepth(md, baseClass, currentDepth+1, maxDepth)) + return false; + } + return true; + } + + static int compareClasses(const void* c0, const void* c1) + { + MetaClass** mc0 = reinterpret_cast(const_cast(c0)); + MetaClass** mc1 = reinterpret_cast(const_cast(c1)); +// return (*mc0)->mSize - (*mc1)->mSize; + return (*mc0)->mDepth - (*mc1)->mDepth; + } + + static int compareEntries(const void* c0, const void* c1) + { + PxMetaDataEntry* mc0 = reinterpret_cast(const_cast(c0)); + PxMetaDataEntry* mc1 = reinterpret_cast(const_cast(c1)); + //mOffset is used to access control information for extra data, and not for offsets of the data itself. + assert(!(mc0->mFlags & PxMetaDataFlag::eEXTRA_DATA)); + assert(!(mc1->mFlags & PxMetaDataFlag::eEXTRA_DATA)); + return mc0->mOffset - mc1->mOffset; + } + }; + { + // Compute depths + const PxU32 nbMetaClasses = mMetaClasses.size(); + for(PxU32 i=0;imDepth = maxDepth; + } + + // Sort by depth + MetaClass** metaClasses = &mMetaClasses[0]; + qsort(metaClasses, size_t(nbMetaClasses), sizeof(MetaClass*), Local::compareClasses); + } + + // Replicate fields from base classes + { + PxU32 nbMetaClasses = mMetaClasses.size(); + for(PxU32 k=0;kmBaseClasses.size(); + + // merge entries of base classes and current class in the right order + // this is needed for extra data ordering, which is not covered by the mOffset sort + // in the next stage below + PsArray mergedEntries; + + for(PxU32 i=0;imBaseClasses[i]; + MetaClass* baseClass = getMetaClass(baseClassEntry.mName); + assert(baseClass); + assert(baseClass->mBaseClasses.size()==0 || baseClass->mProcessed); + + PxU32 nbBaseFields = baseClass->mFields.size(); + for(PxU32 j=0;jmFields[j]; + // Don't merge primary v-tables to avoid redundant v-table entries. + // It means the base v-table won't be inherited & needs to be explicitly defined in the metadata. Seems reasonable. + // Could be done better though. + + if(f.mType==gVTablePtr && !f.mOffset && !baseClassEntry.mOffset) + continue; + + f.mOffset += baseClassEntry.mOffset; + mergedEntries.pushBack(f); + } + current->mProcessed = true; + } + + //append current fields to base class fields + for (PxU32 i = 0; i < current->mFields.size(); i++) + { + mergedEntries.pushBack(current->mFields[i]); + } + current->mFields.clear(); + current->mFields.assign(mergedEntries.begin(), mergedEntries.end()); + } + } + + // Check classes + { + PxU32 nbMetaClasses = mMetaClasses.size(); + for(PxU32 i=0;icheck(*this)) + return false; + } + } + + // Sort meta-data by offset + { + PxU32 nbMetaClasses = mMetaClasses.size(); + for(PxU32 i=0;imFields.size(); + if(nbFields<2) + continue; + PxMetaDataEntry* entries = ¤t->mFields[0]; + + PxMetaDataEntry* newEntries = PX_NEW(PxMetaDataEntry)[nbFields]; + PxU32 nb = 0; + for(PxU32 j=0;jmClassName); + + if (mcDst == nullptr) + { + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, "dst is missing meta class %s", mcSrc->mClassName); + } + } + + //find classes missing in src + for (PxU32 i = 0; imClassName); + + if (mcSrc == nullptr) + { + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, "dst is missing meta class %s", mcSrc->mClassName); + } + } + + //compare classes present in src and dst + for (PxU32 i = 0; imClassName; + MetaClass* mcSrc = getMetaClass(className); + MetaClass* mcDst = dst.getMetaClass(className); + if (mcSrc != nullptr && mcDst != nullptr) + { + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mCallback) + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mMaster) //should be 0 for both anyway + COMPARE_METADATA_STRING_MD(MetaClass, *mcSrc, *mcDst, mClassName) + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mSize) + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mDepth) + + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mBaseClasses.size()) + if (mcSrc->mBaseClasses.size() == mcDst->mBaseClasses.size()) + { + for (PxU32 b = 0; b < mcSrc->mBaseClasses.size(); b++) + { + COMPARE_METADATA_STRING_MD(PxMetaDataEntry, mcSrc->mBaseClasses[b], mcDst->mBaseClasses[b], mName); + } + } + + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mFields.size()) + if (mcSrc->mFields.size() == mcDst->mFields.size()) + { + for (PxU32 f = 0; f < mcSrc->mFields.size(); f++) + { + PxMetaDataEntry srcMde = mcSrc->mFields[f]; + PxMetaDataEntry dstMde = mcDst->mFields[f]; + + COMPARE_METADATA_STRING_MD(PxMetaDataEntry, srcMde, dstMde, mType) + COMPARE_METADATA_STRING_MD(PxMetaDataEntry, srcMde, dstMde, mName) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mOffset) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mSize) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mCount) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mOffsetSize) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mFlags) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mAlignment) + } + } + } + } + return isEquivalent; +} + +#undef COMPARE_METADATA_BOOL_MD +#undef COMPARE_METADATA_INT_MD +#undef COMPARE_METADATA_STRING_MD + +/////////////////////////////////////////////////////////////////////////////// + +void ConvX::releaseMetaData() +{ + DELETESINGLE(mMetaData_Dst); + DELETESINGLE(mMetaData_Src); +} + +const MetaData* ConvX::loadMetaData(PxInputStream& inputStream, MetaDataType type) +{ + if (type != META_DATA_SRC && type != META_DATA_DST) + { + displayMessage(PxErrorCode::eINTERNAL_ERROR, + "PxBinaryConverter: Wrong meta data type, please check the source metadata.\n"); + return NULL; + } + + PX_ASSERT(type == META_DATA_SRC || type == META_DATA_DST); + + MetaData*& metaDataPtr = (type == META_DATA_SRC) ? mMetaData_Src : mMetaData_Dst; + metaDataPtr = PX_NEW(MetaData)(*this); + if(!(metaDataPtr)->load(inputStream, type)) + DELETESINGLE(metaDataPtr); + return metaDataPtr; +} + +const MetaData* ConvX::getBinaryMetaData(MetaDataType type) +{ + if(type==META_DATA_SRC) + return mMetaData_Src; + if(type==META_DATA_DST) + return mMetaData_Dst; + PX_ASSERT(0); + return NULL; +} + +int ConvX::getNbMetaClasses(MetaDataType type) +{ + if(type==META_DATA_SRC) + return mMetaData_Src->getNbMetaClasses(); + if(type==META_DATA_DST) + return mMetaData_Dst->getNbMetaClasses(); + PX_ASSERT(0); + return 0; +} + +MetaClass* ConvX::getMetaClass(unsigned int i, MetaDataType type) const +{ + if(type==META_DATA_SRC) + return mMetaData_Src->getMetaClass(i); + if(type==META_DATA_DST) + return mMetaData_Dst->getMetaClass(i); + PX_ASSERT(0); + return NULL; +} + +MetaClass* ConvX::getMetaClass(const char* name, MetaDataType type) const +{ + if(type==META_DATA_SRC) + return mMetaData_Src->getMetaClass(name); + if(type==META_DATA_DST) + return mMetaData_Dst->getMetaClass(name); + PX_ASSERT(0); + return NULL; +} + +MetaClass* ConvX::getMetaClass(PxConcreteType::Enum concreteType, MetaDataType type) +{ + MetaClass* metaClass = NULL; + if(type==META_DATA_SRC) + metaClass = mMetaData_Src->getMetaClass(concreteType); + if(type==META_DATA_DST) + metaClass = mMetaData_Dst->getMetaClass(concreteType); + + if(!metaClass) + { + displayMessage(PxErrorCode::eINTERNAL_ERROR, + "PxBinaryConverter: Missing concreteType %d metadata! serialized a class without dumping metadata. Please check the metadata.", + concreteType); + return NULL; + } + + return metaClass; +} + +/////////////////////////////////////////////////////////////////////////////// + +// Peek & poke, yes sir. +PxU64 physx::Sn::peek(int size, const char* buffer, int flags) +{ + const int maskMSB = flags & PxMetaDataFlag::eCOUNT_MASK_MSB; + const int skipIfOne = flags & PxMetaDataFlag::eCOUNT_SKIP_IF_ONE; + switch(size) + { + case 1: + { + unsigned char value = *(reinterpret_cast(buffer)); + if(maskMSB) + value &= 0x7f; + if(skipIfOne && value==1) + return 0; + return PxU64(value); + } + case 2: + { + unsigned short value = *(reinterpret_cast(buffer)); + if(maskMSB) + value &= 0x7fff; + if(skipIfOne && value==1) + return 0; + return PxU64(value); + } + case 4: + { + unsigned int value = *(reinterpret_cast(buffer)); + if(maskMSB) + value &= 0x7fffffff; + if(skipIfOne && value==1) + return 0; + return PxU64(value); + } + case 8: + { + PxU64 value = *(reinterpret_cast(buffer)); + if(maskMSB) + value &= (PxU64(-1))>>1; + if(skipIfOne && value==1) + return 0; + return value; + } + }; + PX_ASSERT(0); + return PxU64(-1); +} + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h new file mode 100644 index 000000000..8b1e5a1b6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h @@ -0,0 +1,188 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PX_CONVX_METADATA_H +#define PX_CONVX_METADATA_H + +#include "SnConvX_Output.h" +#include "PxMetaDataFlags.h" + +namespace physx { namespace Sn { + +#if PX_VC +#pragma warning (push) +#pragma warning (disable : 4371) //layout of class may have changed from a previous version of the compiler due to better packing of member +#endif + + + // PT: beware, must match corresponding structure in PxMetaData.h + struct PxMetaDataEntry : public shdfnd::UserAllocated + { + PxMetaDataEntry() + { + memset(this, 0, sizeof(*this)); + } + bool isVTablePtr() const; + + const char* mType; //!< Field type (bool, byte, quaternion, etc) + const char* mName; //!< Field name (appears exactly as in the source file) + int mOffset; //!< Offset from the start of the class (ie from "this", field is located at "this"+Offset) + int mSize; //!< sizeof(Type) + int mCount; //!< Number of items of type Type (0 for dynamic sizes) + int mOffsetSize; //!< Offset of dynamic size param, for dynamic arrays + int mFlags; //!< Field parameters + int mAlignment; //!< Explicit alignment added for DE1340 + }; + + struct MetaDataEntry32 + { + PxI32 mType; //!< Field type (bool, byte, quaternion, etc) + PxI32 mName; //!< Field name (appears exactly as in the source file) + int mOffset; //!< Offset from the start of the class (ie from "this", field is located at "this"+Offset) + int mSize; //!< sizeof(Type) + int mCount; //!< Number of items of type Type (0 for dynamic sizes) + int mOffsetSize; //!< Offset of dynamic size param, for dynamic arrays + int mFlags; //!< Field parameters + int mAlignment; //!< Explicit alignment added for DE1340 + }; + + struct MetaDataEntry64 + { + PxI64 mType; //!< Field type (bool, byte, quaternion, etc) + PxI64 mName; //!< Field name (appears exactly as in the source file) + int mOffset; //!< Offset from the start of the class (ie from "this", field is located at "this"+Offset) + int mSize; //!< sizeof(Type) + int mCount; //!< Number of items of type Type (0 for dynamic sizes) + int mOffsetSize; //!< Offset of dynamic size param, for dynamic arrays + int mFlags; //!< Field parameters + int mAlignment; //!< Explicit alignment added for DE1340 + }; + + struct ExtraDataEntry + { + PxMetaDataEntry entry; + int offset; + }; + + struct ExtraDataEntry2 : ExtraDataEntry + { + ConvertCallback cb; + }; + + class MetaData; + + struct MetaClass : public shdfnd::UserAllocated + { + bool getFieldByType(const char* type, PxMetaDataEntry& entry) const; + bool getFieldByName(const char* name, PxMetaDataEntry& entry) const; + bool check(const MetaData& owner); + + ConvertCallback mCallback; + MetaClass* mMaster; + const char* mClassName; + int mSize; + int mDepth; + PsArray mBaseClasses; + PsArray mFields; + bool mProcessed; + +// int mNbEntries; +// ExtraDataEntry2 mEntries[256]; + + private: + void checkAndCompleteClass(const MetaData& owner, int& startOffset, int& nbBytes); + }; + + enum MetaDataType + { + META_DATA_NONE, + META_DATA_SRC, + META_DATA_DST + }; + + class ConvX; + class MetaData : public shdfnd::UserAllocated + { + public: + MetaData(Sn::ConvX&); + ~MetaData(); + + bool load(PxInputStream& inputStream, MetaDataType type); + + inline_ MetaDataType getType() const { return mType; } + inline_ int getVersion() const { return mVersion; } + inline_ int getBuildNumber() const { return mBuildNumber; } + inline_ int getPtrSize() const { return mSizeOfPtr; } + inline_ int getPlatformTag() const { return mPlatformTag; } + inline_ int getGaussMapLimit() const { return mGaussMapLimit; } + inline_ int getNbMetaClasses() const { return int(mMetaClasses.size()); } + inline_ MetaClass* getMetaClass(unsigned int i) const { return mMetaClasses[i]; } + inline_ bool getFlip() const { return mFlip; } + + MetaClass* getMetaClass(const char* name) const; + MetaClass* getMetaClass(PxConcreteType::Enum concreteType) const; + MetaClass* addNewClass(const char* name, int size, MetaClass* master=NULL, ConvertCallback callback=NULL); + + bool compare(const MetaData& candidate) const; + private: + MetaData& operator=(const MetaData&); + Sn::ConvX& mConvX; + MetaDataType mType; + int mNbEntries; + PxMetaDataEntry* mEntries; + char* mStringTable; + PsArray mMetaClasses; + int mVersion; + int mBinaryVersion; + int mBuildNumber; + int mSizeOfPtr; + int mPlatformTag; + int mGaussMapLimit; + bool mFlip; + + PsArray< Ps::Pair > mConcreteTypeTable; + + inline_ const char* offsetToText(const char* text) const + { + const size_t offset = size_t(text); + const PxU32 offset32 = PxU32(offset); +// if(offset==-1) + if(offset32==0xffffffff) + return NULL; + return mStringTable + offset32; + } + friend struct MetaClass; + }; + + PxU64 peek(int size, const char* buffer, int flags=0); + +#if PX_VC +#pragma warning (pop) +#endif +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp new file mode 100644 index 000000000..d9fb8dada --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp @@ -0,0 +1,451 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "foundation/PxIO.h" +#include "foundation/PxErrorCallback.h" +#include "SnConvX.h" + +#if PX_VC +#pragma warning(disable:4389) // signed/unsigned mismatch +#endif + +using namespace physx; + +void Sn::ConvX::setNullPtr(bool flag) +{ + mNullPtr = flag; +} + +void Sn::ConvX::setNoOutput(bool flag) +{ + mNoOutput = flag; +} + +bool Sn::ConvX::initOutput(PxOutputStream& targetStream) +{ + mOutStream = &targetStream; + + mOutputSize = 0; + mNullPtr = false; + mNoOutput = false; + + const MetaData* srcMetaData = getBinaryMetaData(META_DATA_SRC); + PX_ASSERT(srcMetaData); + const MetaData* dstMetaData = getBinaryMetaData(META_DATA_DST); + PX_ASSERT(dstMetaData); + + mSrcPtrSize = srcMetaData->getPtrSize(); + mDstPtrSize = dstMetaData->getPtrSize(); + + PX_ASSERT(!srcMetaData->getFlip()); + mMustFlip = dstMetaData->getFlip(); + return true; +} + +void Sn::ConvX::closeOutput() +{ + mOutStream = NULL; +} + +int Sn::ConvX::getCurrentOutputSize() +{ + return mOutputSize; +} + +void Sn::ConvX::output(short value) +{ + if(mNoOutput) + return; + + if(mMustFlip) + flip(value); + + PX_ASSERT(mOutStream); + const size_t size = mOutStream->write(&value, 2); + PX_ASSERT(size==2); + mOutputSize += int(size); +} + +void Sn::ConvX::output(int value) +{ + if(mNoOutput) + return; + + if(mMustFlip) + flip(value); + + PX_ASSERT(mOutStream); + const size_t size = mOutStream->write(&value, 4); + PX_ASSERT(size==4); + mOutputSize += int(size); +} + +//ntohll is a macro on apple yosemite +static PxU64 ntohll_internal(const PxU64 value) +{ + union + { + PxU64 ull; + PxU8 c[8]; + } x; + + x.ull = value; + + PxU8 c = 0; + c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c; + c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c; + c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c; + c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c; + + return x.ull; +} + +void Sn::ConvX::output(PxU64 value) +{ + if(mNoOutput) + return; + + if(mMustFlip) +// flip(value); + value = ntohll_internal(value); + + PX_ASSERT(mOutStream); + const size_t size = mOutStream->write(&value, 8); + PX_ASSERT(size==8); + mOutputSize += int(size); +} + +void Sn::ConvX::output(const char* buffer, int nbBytes) +{ + if(mNoOutput) + return; + + if(!nbBytes) + return; + + PX_ASSERT(mOutStream); + const PxU32 size = mOutStream->write(buffer, PxU32(nbBytes)); + PX_ASSERT(size== PxU32(nbBytes)); + mOutputSize += int(size); +} + +void Sn::ConvX::convert8(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==1*entry.mCount); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + const PxU32 size = mOutStream->write(src, PxU32(entry.mSize)); + PX_ASSERT(size== PxU32(entry.mSize)); + mOutputSize += int(size); +} + +// This is called to convert auto-generated "padding bytes" (or so we think). +// We use a special converter to check the input bytes and issue warnings when it doesn't look like padding +void Sn::ConvX::convertPad8(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + (void)src; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize); + PX_ASSERT(entry.mSize==1*entry.mCount); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + // PT: we don't output the source data on purpose, to catch missing meta-data + // sschirm: changed that to 0xcd, so we can mark the output as "having marked pads" + const unsigned char b = 0xcd; + for(int i=0;iwrite(&b, 1); + (void)size; + } + mOutputSize += entry.mSize; +} + +void Sn::ConvX::convert16(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==int(sizeof(short)*entry.mCount)); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + const short* data = reinterpret_cast(src); + for(int i=0;iwrite(&value, sizeof(short)); + PX_ASSERT(size==sizeof(short)); + mOutputSize += int(size); + } +} + +void Sn::ConvX::convert32(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==int(sizeof(int)*entry.mCount)); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + const int* data = reinterpret_cast(src); + for(int i=0;iwrite(&value, sizeof(int)); + PX_ASSERT(size==sizeof(int)); + mOutputSize += int(size); + } +} + +void Sn::ConvX::convert64(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==int(sizeof(PxU64)*entry.mCount)); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + const PxU64* data = reinterpret_cast(src); + for(int i=0;iwrite(&value, sizeof(PxU64)); + PX_ASSERT(size==sizeof(PxU64)); + mOutputSize += int(size); + } +} + +void Sn::ConvX::convertFloat(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==int(sizeof(float)*entry.mCount)); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + const float* data = reinterpret_cast(src); + for(int i=0;iwrite(&value, sizeof(float)); + PX_ASSERT(size==sizeof(float)); + mOutputSize += int(size); + } +} + +void Sn::ConvX::convertPtr(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==mSrcPtrSize*entry.mCount); + PX_ASSERT(mOutStream); + + char buffer[16]; + for(int i=0;i(src); + PxU32 value = *data++; + src = reinterpret_cast(data); + + if(mActiveRemap) + { + PxU32 ref; + if(mActiveRemap->getObjectRef(value, ref)) + { + value = ref; + } + else if(value) + { + // value = 0; + //We use the pointer of mName for its length + // PT: on serialization mName is transformed to an index by the name manager, so we should not modify its value. + if(!entry.mName || strcmp(entry.mName, "mName")) + value=0x12345678; + } + } + else + { + //we should only get here during convertReferenceTables to build up the pointer map + PxU32 ref; + if (mRemap.getObjectRef(value, ref)) + { + value = ref; + } + else if(value) + { + const PxU32 remappedRef = 0x80000000 | (mPointerRemapCounter++ +1); + mRemap.setObjectRef(value, remappedRef); + value = remappedRef; + } + } + + if(mMustFlip) + flip(value); + + if(mNullPtr) + value = 0; + + *reinterpret_cast(buffer) = value; + } + else + { + PX_ASSERT(mSrcPtrSize==8); + PX_ASSERT(sizeof(PxU64)==8); + const PxU64* data = reinterpret_cast(src); + PxU64 value = *data++; + src = reinterpret_cast(data); + + if(mActiveRemap) + { + PxU32 ref; + if(mActiveRemap->getObjectRef(value, ref)) + { + value = ref; + } + else if(value) + { + // value = 0; + //We use the pointer of mName for its length + // PT: on serialization mName is transformed to an index by the name manager, so we should not modify its value. + if(!entry.mName || strcmp(entry.mName, "mName")) + value=0x12345678; + } + } + else + { + //we should only get here during convertReferenceTables to build up the pointer map + PxU32 ref; + if (mRemap.getObjectRef(value, ref)) + { + value = ref; + } + else if(value) + { + const PxU32 remappedRef = 0x80000000 | (mPointerRemapCounter++ +1); + mRemap.setObjectRef(value, remappedRef); + value = remappedRef; + } + } + +// PX_ASSERT(!mMustFlip); +// if(mMustFlip) +// flip(value); + + if(mNullPtr) + value = 0; + + testValue = value; + + *reinterpret_cast(buffer) = value; + } + + if(mSrcPtrSize==mDstPtrSize) + { + const size_t size = mOutStream->write(buffer, PxU32(mSrcPtrSize)); + PX_ASSERT(size==PxU32(mSrcPtrSize)); + mOutputSize += int(size); + } + else + { + if(mDstPtrSize>mSrcPtrSize) + { + // 32bit to 64bit + PX_ASSERT(mDstPtrSize==8); + PX_ASSERT(mSrcPtrSize==4); + + // We need to output the lower 32bits first for PC. Might be different on a 64bit console.... + + // Output src ptr for the lower 32bits + const size_t size = mOutStream->write(buffer, PxU32(mSrcPtrSize)); + PX_ASSERT(size==PxU32(mSrcPtrSize)); + mOutputSize += int(size); + + // Output zeros for the higher 32bits + const int zero = 0; + const size_t size0 = mOutStream->write(&zero, 4); + PX_ASSERT(size0==4); + mOutputSize += int(size0); + } + else + { + // 64bit to 32bit + PX_ASSERT(mSrcPtrSize==8); + PX_ASSERT(mDstPtrSize==4); + + // Not sure how we can safely convert 64bit ptrs to 32bit... just drop the high 32 bits?!? + + PxU32 ptr32 = *reinterpret_cast(buffer); + (void)ptr32; + PxU32 ptr32b = PxU32(testValue); + (void)ptr32b; + + if(mMustFlip) + flip(ptr32b); + + // Output src ptr for the lower 32bits + const size_t size = mOutStream->write(&ptr32b, 4); + PX_ASSERT(size==4); + mOutputSize += int(size); + } + } + } +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h new file mode 100644 index 000000000..18d03fc9f --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h @@ -0,0 +1,112 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PX_CONVX_OUTPUT_H +#define PX_CONVX_OUTPUT_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx { namespace Sn { + + struct PxMetaDataEntry; + class ConvX; + + typedef void (Sn::ConvX::*ConvertCallback) (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + + inline_ void flip(PxI16& v) + { + PxI8* b = reinterpret_cast(&v); + PxI8 temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + inline_ void flip(PxU16& v) + { + flip(reinterpret_cast(v)); + } + + inline_ void flip32(PxI8* b) + { + PxI8 temp = b[0]; + b[0] = b[3]; + b[3] = temp; + temp = b[1]; + b[1] = b[2]; + b[2] = temp; + } + + inline_ void flip(PxI32& v) + { + PxI8* b = reinterpret_cast(&v); + flip32(b); + } + + inline_ void flip(PxU32& v) + { + PxI8* b = reinterpret_cast(&v); + flip32(b); + } + + inline_ void flip(PxI64& v) + { + PxI8* b = reinterpret_cast(&v); + + PxI8 temp = b[0]; + b[0] = b[7]; + b[7] = temp; + temp = b[1]; + b[1] = b[6]; + b[6] = temp; + temp = b[2]; + b[2] = b[5]; + b[5] = temp; + temp = b[3]; + b[3] = b[4]; + b[4] = temp; + } + + inline_ void flip(PxF32& v) + { + PxI8* b = reinterpret_cast(&v); + flip32(b); + } + + inline_ void flip(void*& v) + { + PxI8* b = reinterpret_cast(&v); + flip32(b); + } + + inline_ void flip(const PxI8*& v) + { + PxI8* b = const_cast(v); + flip32(b); + } +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp new file mode 100644 index 000000000..d8dc6bdb1 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "SnConvX.h" +#include + +using namespace physx; + +void Sn::ConvX::resetUnions() +{ + mUnions.clear(); +} + +bool Sn::ConvX::registerUnion(const char* name) +{ + displayMessage(PxErrorCode::eDEBUG_INFO, "Registering union: %s\n", name); + + Sn::Union u; + u.mName = name; + + mUnions.pushBack(u); + return true; +} + +bool Sn::ConvX::registerUnionType(const char* unionName, const char* typeName, int typeValue) +{ + const PxU32 nb = mUnions.size(); + for(PxU32 i=0;i mTypes; + }; +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp new file mode 100644 index 000000000..e7ed9d827 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp @@ -0,0 +1,94 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxBase.h" +#include "SnSerializationContext.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Sn; + +PxBase* DeserializationContext::resolveReference(PxU32 kind, size_t reference) const +{ + const InternalRefMap::Entry* entry0 = mInternalReferencesMap.find(InternalRefKey(reference, kind)); + PX_ASSERT(entry0); + SerialObjectIndex objIndex = entry0->second; + bool isExternal; + PxU32 index = objIndex.getIndex(isExternal); + PxBase* base = NULL; + if (isExternal) + { + const ImportReference& entry = mImportReferences[index]; + base = mExternalRefs->find(entry.id); + } + else + { + const ManifestEntry& entry = mManifestTable[index]; + base = reinterpret_cast(mObjectDataAddress + entry.offset); + } + PX_ASSERT(base); + return base; +} + +void SerializationContext::registerReference(PxBase& serializable, PxU32 kind, size_t reference) +{ +#if PX_CHECKED + if ((kind & PX_SERIAL_REF_KIND_PTR_TYPE_BIT) == 0 && reference > 0xffffffff) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxSerializationContext::registerReference: only 32 bit indices supported."); + return; + } +#endif + + bool isExternal = mExternalRefs && mExternalRefs->contains(serializable); + PxU32 index; + if (isExternal) + { + PxSerialObjectId id = mExternalRefs->getId(serializable); + PX_ASSERT(id != PX_SERIAL_OBJECT_ID_INVALID); + if (const Ps::HashMap::Entry* entry = mImportReferencesMap.find(id)) + { + index = entry->second; + } + else + { + index = mImportReferences.size(); + mImportReferencesMap.insert(id, index); + mImportReferences.pushBack(ImportReference(id, serializable.getConcreteType())); + } + } + else + { + PX_ASSERT(mCollection.contains(serializable)); + index = mObjToCollectionIndexMap[&serializable]; + } + + InternalRefMap& targetMap = (kind & PX_SERIAL_REF_KIND_PTR_TYPE_BIT) ? mInternalReferencesPtrMap : mInternalReferencesIdxMap; + targetMap[InternalRefKey(reference, kind)] = SerialObjectIndex(index, isExternal); +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h new file mode 100644 index 000000000..624b50f79 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h @@ -0,0 +1,303 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SN_SERIALIZATION_CONTEXT +#define PX_PHYSICS_SN_SERIALIZATION_CONTEXT + +#include "foundation/PxAssert.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "PsHash.h" +#include "PsUserAllocated.h" +#include "PxSerialFramework.h" +#include "CmCollection.h" +#include "CmUtils.h" +#include "PxDefaultStreams.h" +#include "PsFoundation.h" +#include "SnConvX_Align.h" + +namespace physx +{ + namespace Sn + { + + struct ManifestEntry + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_FORCE_INLINE ManifestEntry(PxU32 _offset, PxType _type) + { + Cm::markSerializedMem(this, sizeof(ManifestEntry)); + offset = _offset; + type = _type; + } + PX_FORCE_INLINE ManifestEntry() { Cm::markSerializedMem(this, sizeof(ManifestEntry)); } + PX_FORCE_INLINE void operator =(const ManifestEntry& m) + { + PxMemCopy(this, &m, sizeof(ManifestEntry)); + } + + PxU32 offset; + PxType type; + }; + + struct ImportReference + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_FORCE_INLINE ImportReference(PxSerialObjectId _id, PxType _type) + { + Cm::markSerializedMem(this, sizeof(ImportReference)); + id = _id; + type = _type; + } + PX_FORCE_INLINE ImportReference() { Cm::markSerializedMem(this, sizeof(ImportReference)); } + PX_FORCE_INLINE void operator =(const ImportReference& m) + { + PxMemCopy(this, &m, sizeof(ImportReference)); + } + PxSerialObjectId id; + PxType type; + }; + +#define SERIAL_OBJECT_INDEX_TYPE_BIT (1u<<31) + struct SerialObjectIndex + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_FORCE_INLINE SerialObjectIndex(PxU32 index, bool external) { setIndex(index, external); } + PX_FORCE_INLINE SerialObjectIndex(const SerialObjectIndex& objIndex) : mObjIndex(objIndex.mObjIndex) {} + PX_FORCE_INLINE SerialObjectIndex() : mObjIndex(PX_INVALID_U32) {} + + PX_FORCE_INLINE void setIndex(PxU32 index, bool external) + { + PX_ASSERT((index & SERIAL_OBJECT_INDEX_TYPE_BIT) == 0); + mObjIndex = index | (external ? SERIAL_OBJECT_INDEX_TYPE_BIT : 0); + } + + PX_FORCE_INLINE PxU32 getIndex(bool& isExternal) + { + PX_ASSERT(mObjIndex != PX_INVALID_U32); + isExternal = (mObjIndex & SERIAL_OBJECT_INDEX_TYPE_BIT) > 0; + return mObjIndex & ~SERIAL_OBJECT_INDEX_TYPE_BIT; + } + + private: + PxU32 mObjIndex; + }; + + struct ExportReference + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_FORCE_INLINE ExportReference(PxSerialObjectId _id, SerialObjectIndex _objIndex) + { + Cm::markSerializedMem(this, sizeof(ExportReference)); + id = _id; + objIndex = _objIndex; + } + PX_FORCE_INLINE ExportReference() { Cm::markSerializedMem(this, sizeof(ExportReference)); } + PX_FORCE_INLINE void operator =(const ExportReference& m) + { + PxMemCopy(this, &m, sizeof(ExportReference)); + } + PxSerialObjectId id; + SerialObjectIndex objIndex; + }; + + template + struct InternalReference + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_FORCE_INLINE InternalReference(ReferenceType _reference, PxU32 _kind, SerialObjectIndex _objIndex) + { + Cm::markSerializedMem(this, sizeof(InternalReference)); + reference = _reference; + kind = _kind; + objIndex = _objIndex; + } + PX_FORCE_INLINE InternalReference() { Cm::markSerializedMem(this, sizeof(InternalReference)); } + PX_FORCE_INLINE void operator =(const InternalReference& m) + { + PxMemCopy(this, &m, sizeof(InternalReference)); + } + ReferenceType reference; + PxU32 kind; + SerialObjectIndex objIndex; + }; + + typedef InternalReference InternalReferencePtr; + typedef InternalReference InternalReferenceIdx; + + typedef shdfnd::Pair InternalRefKey; + typedef Cm::CollectionHashMap InternalRefMap; + + class DeserializationContext : public PxDeserializationContext, public Ps::UserAllocated + { + PX_NOCOPY(DeserializationContext) + + public: + DeserializationContext(const ManifestEntry* manifestTable, + const ImportReference* importReferences, + PxU8* objectDataAddress, + const InternalRefMap& internalReferencesMap, + const Cm::Collection* externalRefs, + PxU8* extraData, + PxU32 physxVersion) + : mManifestTable(manifestTable) + , mImportReferences(importReferences) + , mObjectDataAddress(objectDataAddress) + , mInternalReferencesMap(internalReferencesMap) + , mExternalRefs(externalRefs) + , mPhysXVersion(physxVersion) + { + mExtraDataAddress = extraData; + } + + virtual PxBase* resolveReference(PxU32 kind, size_t reference) const; + + PxU32 getPhysXVersion() const { return mPhysXVersion; } + private: + //various pointers to deserialized data + const ManifestEntry* mManifestTable; + const ImportReference* mImportReferences; + PxU8* mObjectDataAddress; + + //internal references map for resolving references. + const InternalRefMap& mInternalReferencesMap; + + //external collection for resolving import references. + const Cm::Collection* mExternalRefs; + const PxU32 mPhysXVersion; + }; + + class SerializationContext : public PxSerializationContext, public Ps::UserAllocated + { + PX_NOCOPY(SerializationContext) + public: + SerializationContext(const Cm::Collection& collection, const Cm::Collection* externalRefs) + : mCollection(collection) + , mExternalRefs(externalRefs) + { + // fill object to collection index map (same ordering as manifest) + for (PxU32 i=0;i 0) + { + mMemStream.write(buf, bytesToPad < BUFSIZE ? PxU32(bytesToPad) : PxU32(BUFSIZE)); + bytesToPad -= BUFSIZE; + } + PX_ASSERT(!getPadding(getTotalStoredSize(), alignment)); + } + + virtual void writeName(const char*) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Cannot export names during exportData."); + } + + const PxCollection& getCollection() const { return mCollection; } + + virtual void registerReference(PxBase& serializable, PxU32 kind, size_t reference); + + const Ps::Array& getImportReferences() { return mImportReferences; } + InternalRefMap& getInternalReferencesPtrMap() { return mInternalReferencesPtrMap; } + InternalRefMap& getInternalReferencesIdxMap() { return mInternalReferencesIdxMap; } + + PxU32 getSize() const { return mMemStream.getSize(); } + PxU8* getData() const { return mMemStream.getData(); } + + + + private: + //import reference map for unique registration of import references and corresponding buffer. + Ps::HashMap mImportReferencesMap; + Ps::Array mImportReferences; + + //maps for unique registration of internal references + InternalRefMap mInternalReferencesPtrMap; + InternalRefMap mInternalReferencesIdxMap; + + //map for quick lookup of manifest index. + Ps::HashMap mObjToCollectionIndexMap; + + //collection and externalRefs collection for assigning references. + const Cm::Collection& mCollection; + const Cm::Collection* mExternalRefs; + + PxDefaultMemoryOutputStream mMemStream; + + }; + + } // namespace Sn +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h b/src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h new file mode 100644 index 000000000..a4a08373c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h @@ -0,0 +1,85 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SN_FILE_H +#define SN_FILE_H + +// fopen_s - returns 0 on success, non-zero on failure + +#if PX_MICROSOFT_FAMILY + +#include + +namespace physx +{ +namespace sn +{ +PX_INLINE PxI32 fopen_s(FILE** file, const char* name, const char* mode) +{ + static const PxU32 MAX_LEN = 300; + char buf[MAX_LEN+1]; + + PxU32 i; + for(i = 0; i + +namespace physx +{ +namespace sn +{ +PX_INLINE PxI32 fopen_s(FILE** file, const char* name, const char* mode) +{ + FILE* fp = ::fopen(name, mode); + if(fp) + { + *file = fp; + return PxI32(0); + } + return -1; +} +} // namespace sn +} // namespace physx +#else +#error "Platform not supported!" +#endif + +#endif //SN_FILE_H + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp new file mode 100644 index 000000000..a7c73ce78 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp @@ -0,0 +1,159 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "SnSerialUtils.h" +#include "PsString.h" +#include "PxSerialization.h" +#include "PxPhysicsVersion.h" +#include "PsBasicTemplates.h" + +using namespace physx; + +namespace +{ + +#define SN_NUM_BINARY_PLATFORMS 13 +const PxU32 sBinaryPlatformTags[SN_NUM_BINARY_PLATFORMS] = +{ + PX_MAKE_FOURCC('W','_','3','2'), + PX_MAKE_FOURCC('W','_','6','4'), + PX_MAKE_FOURCC('L','_','3','2'), + PX_MAKE_FOURCC('L','_','6','4'), + PX_MAKE_FOURCC('M','_','3','2'), + PX_MAKE_FOURCC('M','_','6','4'), + PX_MAKE_FOURCC('M','O','C','A'), + PX_MAKE_FOURCC('A','N','D','R'), + PX_MAKE_FOURCC('A','I','O','S'), + PX_MAKE_FOURCC('A','A','6','4'), + PX_MAKE_FOURCC('X','O','N','E'), + PX_MAKE_FOURCC('N','X','3','2'), + PX_MAKE_FOURCC('N','X','6','4') +}; + +const char* sBinaryPlatformNames[SN_NUM_BINARY_PLATFORMS] = +{ + "win32", + "win64", + "linux32", + "linux64", + "macOSX32", + "macOSX64", + "ps4", + "android", + "ios", + "ios64", + "xboxone", + "switch32", + "switch64" +}; + +#define SN_NUM_BINARY_COMPATIBLE_VERSIONS 1 + +// +// Important: if you adjust the following structure, please adjust the comment for PX_BINARY_SERIAL_VERSION as well +// +const Ps::Pair sBinaryCompatibleVersions[SN_NUM_BINARY_COMPATIBLE_VERSIONS] = +{ + Ps::Pair(PX_PHYSICS_VERSION, PX_BINARY_SERIAL_VERSION) +}; + +} + +namespace physx { namespace Sn { + +PxU32 getBinaryPlatformTag() +{ +#if PX_WINDOWS && PX_X86 + return sBinaryPlatformTags[0]; +#elif PX_WINDOWS && PX_X64 + return sBinaryPlatformTags[1]; +#elif PX_LINUX && (PX_X86 || PX_ARM) + return sBinaryPlatformTags[2]; +#elif PX_LINUX && (PX_X64 || PX_A64) + return sBinaryPlatformTags[3]; +#elif PX_OSX && PX_X86 + return sBinaryPlatformTags[4]; +#elif PX_OSX && PX_X64 + return sBinaryPlatformTags[5]; +#elif PX_PS4 + return sBinaryPlatformTags[6]; +#elif PX_ANDROID + return sBinaryPlatformTags[7]; +#elif PX_IOS && PX_ARM + return sBinaryPlatformTags[8]; +#elif PX_IOS && PX_A64 + return sBinaryPlatformTags[9]; +#elif PX_XBOXONE + return sBinaryPlatformTags[10]; +#elif PX_SWITCH && !PX_A64 + return sBinaryPlatformTags[11]; +#elif PX_SWITCH && PX_A64 + return sBinaryPlatformTags[12]; +#else + #error Unknown binary platform +#endif +} + +bool isBinaryPlatformTagValid(physx::PxU32 platformTag) +{ + PxU32 platformIndex = 0; + while (platformIndex < SN_NUM_BINARY_PLATFORMS && platformTag != sBinaryPlatformTags[platformIndex]) platformIndex++; + return platformIndex < SN_NUM_BINARY_PLATFORMS; +} + +const char* getBinaryPlatformName(physx::PxU32 platformTag) +{ + PxU32 platformIndex = 0; + while (platformIndex < SN_NUM_BINARY_PLATFORMS && platformTag != sBinaryPlatformTags[platformIndex]) platformIndex++; + return (platformIndex == SN_NUM_BINARY_PLATFORMS) ? "unknown" : sBinaryPlatformNames[platformIndex]; +} + +bool checkCompatibility(const PxU32 version, const PxU32 binaryVersion) +{ + for(PxU32 i =0; icontains(base))) + return; + if(!required.contains(base)) + required.add(base); + } + + PxCollection& required; + PxCollection& complete; + const PxCollection* external; + PX_NOCOPY(CompleteCallback) + }; + + void getRequiresCollection(PxCollection& required, PxCollection& collection, PxCollection& complete, const PxCollection* external, PxSerializationRegistry& sr, bool followJoints) + { + CompleteCallback callback(required, complete, external); + for (PxU32 i = 0; i < collection.getNbObjects(); ++i) + { + PxBase& s = collection.getObject(i); + const PxSerializer* serializer = sr.getSerializer(s.getConcreteType()); + PX_ASSERT(serializer); + serializer->requiresObjects(s, callback); + + if(followJoints) + { + PxRigidActor* actor = s.is(); + if(actor) + { + Ps::Array objects(actor->getNbConstraints()); + actor->getConstraints(objects.begin(), objects.size()); + + for(PxU32 j=0;j(objects[j]->getExternalReference(typeId)); + if(typeId == PxConstraintExtIDs::eJOINT) + { + const PxSerializer* sj = sr.getSerializer(joint->getConcreteType()); + PX_ASSERT(sj); + sj->requiresObjects(*joint, callback); + if(!required.contains(*joint)) + required.add(*joint); + } + } + } + } + } + } +} + +bool PxSerialization::isSerializable(PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* externalReferences) +{ + PxCollection* subordinateCollection = PxCreateCollection(); + PX_ASSERT(subordinateCollection); + + for(PxU32 i = 0; i < collection.getNbObjects(); ++i) + { + PxBase& s = collection.getObject(i); + const PxSerializer* serializer = sr.getSerializer(s.getConcreteType()); + PX_ASSERT(serializer); + if(serializer->isSubordinate()) + subordinateCollection->add(s); + + if(externalReferences) + { + PxSerialObjectId id = collection.getId(s); + if(id != PX_SERIAL_OBJECT_ID_INVALID) + { + PxBase* object = externalReferences->find(id); + if(object && (object != &s)) + { + subordinateCollection->release(); + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: Reference id %" PX_PRIu64 " used both in current collection and in externalReferences. " + "Please use unique identifiers.", id); + return false; + } + } + } + } + + PxCollection* requiresCollection = PxCreateCollection(); + PX_ASSERT(requiresCollection); + + RequiresCallback requiresCallback0(*requiresCollection); + + for (PxU32 i = 0; i < collection.getNbObjects(); ++i) + { + PxBase& s = collection.getObject(i); + const PxSerializer* serializer = sr.getSerializer(s.getConcreteType()); + PX_ASSERT(serializer); + serializer->requiresObjects(s, requiresCallback0); + + Cm::Collection* cmRequiresCollection = static_cast(requiresCollection); + + for(PxU32 j = 0; j < cmRequiresCollection->getNbObjects(); ++j) + { + PxBase& s0 = cmRequiresCollection->getObject(j); + + if(subordinateCollection->contains(s0)) + { + subordinateCollection->remove(s0); + continue; + } + + bool requiredIsInCollection = collection.contains(s0); + if(!requiredIsInCollection) + { + if(externalReferences) + { + if(!externalReferences->contains(s0)) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: Object of type %s references a missing object of type %s. " + "The missing object needs to be added to either the current collection or the externalReferences collection.", + s.getConcreteTypeName(), s0.getConcreteTypeName()); + } + else if(externalReferences->getId(s0) == PX_SERIAL_OBJECT_ID_INVALID) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: Object of type %s in externalReferences collection requires an id.", + s0.getConcreteTypeName()); + } + else + continue; + } + else + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: Object of type %s references a missing serial object of type %s. " + "Please completed the collection or specify an externalReferences collection containing the object.", + s.getConcreteTypeName(), s0.getConcreteTypeName()); + } + subordinateCollection->release(); + requiresCollection->release(); + return false; + } + } + cmRequiresCollection->mObjects.clear(); + } + requiresCollection->release(); + + PxU32 numOrphans = subordinateCollection->getNbObjects(); + + for(PxU32 j = 0; j < numOrphans; ++j) + { + PxBase& subordinate = subordinateCollection->getObject(j); + + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: An object of type %s is subordinate but not required " + "by other objects in the collection (orphan). Please remove the object from the collection or add its owner.", + subordinate.getConcreteTypeName()); + } + + subordinateCollection->release(); + + if(numOrphans>0) + return false; + + if(externalReferences) + { + PxCollection* oppositeRequiresCollection = PxCreateCollection(); + PX_ASSERT(oppositeRequiresCollection); + + RequiresCallback requiresCallback(*oppositeRequiresCollection); + + for (PxU32 i = 0; i < externalReferences->getNbObjects(); ++i) + { + PxBase& s = externalReferences->getObject(i); + const PxSerializer* serializer = sr.getSerializer(s.getConcreteType()); + PX_ASSERT(serializer); + serializer->requiresObjects(s, requiresCallback); + + Cm::Collection* cmCollection = static_cast(oppositeRequiresCollection); + + for(PxU32 j = 0; j < cmCollection->getNbObjects(); ++j) + { + PxBase& s0 = cmCollection->getObject(j); + + if(collection.contains(s0)) + { + oppositeRequiresCollection->release(); + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: Object of type %s in externalReferences references an object " + "of type %s in collection (circular dependency).", + s.getConcreteTypeName(), s0.getConcreteTypeName()); + return false; + } + } + cmCollection->mObjects.clear(); + } + oppositeRequiresCollection->release(); + } + + return true; +} + +void PxSerialization::complete(PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* exceptFor, bool followJoints) +{ + PxCollection* curCollection = PxCreateCollection(); + PX_ASSERT(curCollection); + curCollection->add(collection); + + PxCollection* requiresCollection = PxCreateCollection(); + PX_ASSERT(requiresCollection); + + do + { + getRequiresCollection(*requiresCollection, *curCollection, collection, exceptFor, sr, followJoints); + + collection.add(*requiresCollection); + PxCollection* swap = curCollection; + curCollection = requiresCollection; + requiresCollection = swap; + (static_cast(requiresCollection))->mObjects.clear(); + + }while(curCollection->getNbObjects() > 0); + + requiresCollection->release(); + curCollection->release(); + +} + +void PxSerialization::createSerialObjectIds(PxCollection& collection, const PxSerialObjectId base) +{ + PxSerialObjectId localBase = base; + PxU32 nbObjects = collection.getNbObjects(); + + for (PxU32 i = 0; i < nbObjects; ++i) + { + while(collection.find(localBase)) + { + localBase++; + } + + PxBase& s = collection.getObject(i); + if(PX_SERIAL_OBJECT_ID_INVALID == collection.getId(s)) + { + collection.addId(s, localBase); + localBase++; + } + } +} + +namespace physx { namespace Sn +{ + static PxU32 addToStringTable(physx::shdfnd::Array& stringTable, const char* str) + { + if(!str) + return 0xffffffff; + + PxI32 length = PxI32(stringTable.size()); + const char* table = stringTable.begin(); + const char* start = table; + while(length) + { + if(strcmp(table, str)==0) + return PxU32(table - start); + + const char* saved = table; + while(*table++); + length -= PxU32(table - saved); + PX_ASSERT(length>=0); + } + + const PxU32 offset = stringTable.size(); + + while(*str) + stringTable.pushBack(*str++); + stringTable.pushBack(0); + return offset; + } +} } + +void PxSerialization::dumpBinaryMetaData(PxOutputStream& outputStream, PxSerializationRegistry& sr) +{ + class MetaDataStream : public PxOutputStream + { + public: + bool addNewType(const char* typeName) + { + for(PxU32 i=0;i(src); + if(( entry->flags & PxMetaDataFlag::eTYPEDEF) || ((entry->flags & PxMetaDataFlag::eCLASS) && (!entry->name)) ) + newType = addNewType(entry->type); + if(newType) + metaData.pushBack(*entry); + return count; + } + shdfnd::Array metaData; + shdfnd::Array types; + bool newType; + }s; + + SerializationRegistry& sn = static_cast(sr); + sn.getBinaryMetaData(s); + + shdfnd::Array stringTable; + + PxU32 nb = s.metaData.size(); + PxMetaDataEntry* entries = s.metaData.begin(); + for(PxU32 i=0;i(size_t(addToStringTable(stringTable, entries[i].type))); + entries[i].name = reinterpret_cast(size_t(addToStringTable(stringTable, entries[i].name))); + } + + PxU32 platformTag = getBinaryPlatformTag(); + + const PxU32 gaussMapLimit = 32; + + const PxU32 header = PX_MAKE_FOURCC('M','E','T','A'); + const PxU32 version = PX_PHYSICS_VERSION; + const PxU32 binaryVersion = PX_BINARY_SERIAL_VERSION; + const PxU32 ptrSize = sizeof(void*); + PxU32 buildNumber = 0; +#if defined(PX_BUILD_NUMBER) + buildNumber = PX_BUILD_NUMBER; +#endif + outputStream.write(&header, 4); + outputStream.write(&version, 4); + outputStream.write(&binaryVersion, 4); + outputStream.write(&buildNumber, 4); + outputStream.write(&ptrSize, 4); + outputStream.write(&platformTag, 4); + outputStream.write(&gaussMapLimit, 4); + + outputStream.write(&nb, 4); + outputStream.write(entries, nb*sizeof(PxMetaDataEntry)); + + //concreteTypes to name + PxU32 num = sn.getNbSerializers(); + outputStream.write(&num, 4); + for(PxU32 i=0; i Object; + + class Element + { + public: + Object object; + Ps::Array children; + bool isFinished; + + Element(PxBase* obj = NULL) : object(obj, PX_SERIAL_OBJECT_ID_INVALID), isFinished(false) {} + }; + + public: + CollectionSorter(Cm::Collection& collection, Sn::SerializationRegistry& sr, bool isRepx) : mCollection(collection), mSr(sr), mIsRepx(isRepx) {} + virtual ~CollectionSorter(){} + + void process(PxBase& base) + { + addChild(&base); + //ArticulationLink is not a repx serializer, so should require Articulation here + if( mIsRepx && PxConcreteType::eARTICULATION_LINK == base.getConcreteType() ) + { + PxArticulationLink* link = static_cast(&base); + PxBase* a = reinterpret_cast(&link->getArticulation()); + if(mCurElement->object.first != a ) //don't require itself + addChild(a); + } + } + + void sort() + { + Element element; + PxU32 i; + + PxU32 nbObject = mCollection.internalGetNbObjects(); + const Cm::Collection::ObjectToIdMap::Entry* objectdatas = mCollection.internalGetObjects(); + for( i = 0; i < nbObject; ++i ) + { + element.object.first = objectdatas[i].first; + element.object.second = objectdatas[i].second; + mObjToIdMap.insert(objectdatas[i].first, mElements.size()); + mElements.pushBack(element); + } + + for( i = 0; i < nbObject; ++i ) + { + mCurElement = &mElements[i]; + const PxSerializer* serializer = mSr.getSerializer(mCurElement->object.first->getConcreteType()); + PX_ASSERT(serializer); + serializer->requiresObjects(*mCurElement->object.first, *this); + } + + for( i = 0; i < nbObject; ++i ) + { + if( mElements[i].isFinished ) + continue; + + AddElement(mElements[i]); + } + + mCollection.mObjects.clear(); + for(Ps::Array::Iterator o = mSorted.begin(); o != mSorted.end(); ++o ) + { + mCollection.internalAdd(o->first, o->second); + } + } + + void AddElement(Element& e) + { + if( !e.isFinished ) + { + for( Ps::Array::Iterator child = e.children.begin(); child != e.children.end(); ++child ) + { + AddElement(mElements[*child]); + } + mSorted.pushBack(e.object); + e.isFinished = true; + } + } + + private: + PX_INLINE void addChild(PxBase* base) + { + PX_ASSERT(mCurElement); + const Ps::HashMap::Entry* entry = mObjToIdMap.find(base); + if(entry) + mCurElement->children.pushBack(entry->second); + } + + CollectionSorter& operator=(const CollectionSorter&); + Ps::HashMap mObjToIdMap; + Ps::Array mElements; + Cm::Collection& mCollection; + Sn::SerializationRegistry& mSr; + Ps::Array mSorted; + Element* mCurElement; + bool mIsRepx; + }; +} + +namespace physx { namespace Sn { + +SerializationRegistry::SerializationRegistry(PxPhysics& physics) + : mPhysics(physics) +{ + PxRegisterPhysicsSerializers(*this); + Ext::RegisterExtensionsSerializers(*this); + + registerBinaryMetaDataCallback(PxGetPhysicsBinaryMetaData); + registerBinaryMetaDataCallback(Ext::GetExtensionsBinaryMetaData); +} + +SerializationRegistry::~SerializationRegistry() +{ + PxUnregisterPhysicsSerializers(*this); + Ext::UnregisterExtensionsSerializers(*this); + + if(mSerializers.size() > 0) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::release(): some registered PxSerializer instances were not unregistered"); + } + + if(mRepXSerializers.size() > 0) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::release(): some registered PxRepXSerializer instances were not unregistered"); + } +} + +void SerializationRegistry::registerSerializer(PxType type, PxSerializer& serializer) +{ + if(mSerializers.find(type)) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::registerSerializer: Type %d has already been registered", type); + } + + mSerializers.insert(type, &serializer); +} + +PxSerializer* SerializationRegistry::unregisterSerializer(PxType type) +{ + const SerializerMap::Entry* e = mSerializers.find(type); + PxSerializer* s = e ? e->second : NULL; + + if(!mSerializers.erase(type)) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::unregisterSerializer: failed to find PxSerializer instance for type %d", type); + } + return s; +} + +const PxSerializer* SerializationRegistry::getSerializer(PxType type) const +{ + const SerializerMap::Entry* e = mSerializers.find(type); +#if PX_CHECKED + if (!e) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::getSerializer: failed to find PxSerializer instance for type %d", type); + } +#endif + return e ? e->second : NULL; +} + +PxType SerializationRegistry::getSerializerType(PxU32 index) const +{ + PX_ASSERT(index < mSerializers.size()); + return mSerializers.getEntries()[index].first; +} + +const char* SerializationRegistry::getSerializerName(PxU32 index) const +{ + PX_ASSERT(index < mSerializers.size()); + return mSerializers.getEntries()[index].second->getConcreteTypeName(); +} + +void SerializationRegistry::registerBinaryMetaDataCallback(PxBinaryMetaDataCallback callback) +{ + mMetaDataCallbacks.pushBack(callback); +} + +void SerializationRegistry::getBinaryMetaData(PxOutputStream& stream) const +{ + for(PxU32 i = 0; i < mMetaDataCallbacks.size(); i++) + { + mMetaDataCallbacks[i](stream); + } +} + +void SerializationRegistry::registerRepXSerializer(PxType type, PxRepXSerializer& serializer) +{ + if(mRepXSerializers.find(type)) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::registerRepXSerializer: Type %d has already been registered", type); + } + + mRepXSerializers.insert(type, &serializer); +} + +PxRepXSerializer* SerializationRegistry::getRepXSerializer(const char* typeName) const +{ + SerializationRegistry* sr = const_cast(this); + for( RepXSerializerMap::Iterator iter = sr->mRepXSerializers.getIterator(); !iter.done(); ++iter) + { + if ( physx::shdfnd::stricmp( iter->second->getTypeName(), typeName ) == 0 ) + return iter->second; + } + return NULL; +} + +PxRepXSerializer* SerializationRegistry::unregisterRepXSerializer(PxType type) +{ + const RepXSerializerMap::Entry* e = mRepXSerializers.find(type); + PxRepXSerializer* s = e ? e->second : NULL; + + if(!mRepXSerializers.erase(type)) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::unregisterRepXSerializer: failed to find PxRepXSerializer instance for type %d", type); + } + return s; +} + +void sortCollection(Cm::Collection& collection, SerializationRegistry& sr, bool isRepx) +{ + CollectionSorter sorter(collection, sr, isRepx); + sorter.sort(); +} + +} // Sn + +PxSerializationRegistry* PxSerialization::createSerializationRegistry(PxPhysics& physics) +{ + return PX_NEW(Sn::SerializationRegistry)(physics); +} + +} // physx + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h new file mode 100644 index 000000000..8c660d294 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h @@ -0,0 +1,91 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SN_SERIALIZATION_REGISTRY +#define PX_PHYSICS_SN_SERIALIZATION_REGISTRY + +#include "PxSerialization.h" +#include "PxRepXSerializer.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PsArray.h" +#include "PsHashMap.h" + +namespace physx +{ + +namespace Cm { class Collection; } + +namespace Sn { + + class SerializationRegistry : public PxSerializationRegistry, public Ps::UserAllocated + { + public: + SerializationRegistry(PxPhysics& physics); + virtual ~SerializationRegistry(); + + virtual void release(){ PX_DELETE(this); } + + PxPhysics& getPhysics() const { return mPhysics; } + + //binary + void registerSerializer(PxType type, PxSerializer& serializer); + PxSerializer* unregisterSerializer(PxType type); + void registerBinaryMetaDataCallback(PxBinaryMetaDataCallback callback); + void getBinaryMetaData(PxOutputStream& stream) const; + const PxSerializer* getSerializer(PxType type) const; + const char* getSerializerName(PxU32 index) const; + PxType getSerializerType(PxU32 index) const; + PxU32 getNbSerializers() const { return mSerializers.size(); } + //repx + void registerRepXSerializer(PxType type, PxRepXSerializer& serializer); + PxRepXSerializer* getRepXSerializer(const char* typeName) const; + PxRepXSerializer* unregisterRepXSerializer(PxType type); + + protected: + SerializationRegistry &operator=(const SerializationRegistry &); + private: + typedef Ps::CoalescedHashMap SerializerMap; + typedef Ps::HashMap RepXSerializerMap; + + PxPhysics& mPhysics; + SerializerMap mSerializers; + RepXSerializerMap mRepXSerializers; + Ps::Array mMetaDataCallbacks; + }; + + void sortCollection(Cm::Collection& collection, SerializationRegistry& sr, bool isRepx); +} // Sn + +} // physx + + + +#endif + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp new file mode 100644 index 000000000..924740be6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp @@ -0,0 +1,145 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#include "PxMetaDataObjects.h" +#include "PxExtensionMetaDataObjects.h" +#include "ExtJointMetaDataExtensions.h" +#include "SnJointRepXSerializer.h" + +namespace physx { + + template + inline TJointType* createJoint( PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1 ) + { + PX_UNUSED(physics); + PX_UNUSED(actor0); + PX_UNUSED(actor1); + PX_UNUSED(localFrame0); + PX_UNUSED(localFrame1); + return NULL; + } + + template<> + inline PxD6Joint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxD6JointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxDistanceJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxDistanceJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxContactJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxContactJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxFixedJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxFixedJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxPrismaticJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxPrismaticJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxRevoluteJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxRevoluteJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxSphericalJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxSphericalJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template + PxRepXObject PxJointRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + PxRigidActor* actor0 = NULL; + PxRigidActor* actor1 = NULL; + PxTransform localPose0 = PxTransform(PxIdentity); + PxTransform localPose1 = PxTransform(PxIdentity); + bool ok = true; + if ( inReader.gotoChild( "Actors" ) ) + { + ok = readReference( inReader, *inCollection, "actor0", actor0 ); + ok &= readReference( inReader, *inCollection, "actor1", actor1 ); + inReader.leaveChild(); + } + TJointType* theJoint = !ok ? NULL : createJoint( inArgs.physics, actor0, localPose0, actor1, localPose1 ); + + if ( theJoint ) + { + PxConstraint* constraint = theJoint->getConstraint(); + PX_ASSERT( constraint ); + inCollection->add( *constraint ); + this->fileToObjectImpl( theJoint, inReader, inAllocator, inArgs, inCollection ); + } + return PxCreateRepXObject(theJoint); + } + + template + void PxJointRepXSerializer::objectToFileImpl( const TJointType* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& ) + { + writeAllProperties( inObj, inWriter, inTempBuffer, *inCollection ); + } + + // explicit template instantiations + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h new file mode 100644 index 000000000..3dd215690 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef SN_JOINT_REPX_SERIALIZER_H +#define SN_JOINT_REPX_SERIALIZER_H +/** \addtogroup RepXSerializers + @{ +*/ + +#include "extensions/PxRepXSimpleType.h" +#include "SnRepXSerializerImpl.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class XmlReader; + class XmlMemoryAllocator; + class XmlWriter; + class MemoryBuffer; + + template + struct PxJointRepXSerializer : public RepXSerializerImpl + { + PxJointRepXSerializer(PxAllocatorCallback& inAllocator) : RepXSerializerImpl(inAllocator) {} + virtual PxRepXObject fileToObject(XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection); + virtual void objectToFileImpl(const TJointType* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs&); + virtual TJointType* allocateObject(PxRepXInstantiationArgs&) { return NULL; } + }; + +#if PX_SUPPORT_EXTERN_TEMPLATE + // explicit template instantiations declarations + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h new file mode 100644 index 000000000..fbc666854 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h @@ -0,0 +1,134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_PXSTREAMOPERATORS_H +#define PX_PXSTREAMOPERATORS_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxBounds3.h" +#include "PxFiltering.h" + +#include "PsString.h" + +namespace physx +{ + static inline PxU32 strLenght( const char* inStr ) + { + return inStr ? PxU32(strlen(inStr)) : 0; + } +} + +namespace physx // ADL requires we put the operators in the same namespace as the underlying type of PxOutputStream +{ + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const char* inString ) + { + if ( inString && *inString ) + { + ioStream.write( inString, PxU32(strlen(inString)) ); + } + return ioStream; + } + + template + inline PxOutputStream& toStream( PxOutputStream& ioStream, const char* inFormat, const TDataType inData ) + { + char buffer[128] = { 0 }; + Ps::snprintf( buffer, 128, inFormat, inData ); + ioStream << buffer; + return ioStream; + } + + struct endl_obj {}; + //static endl_obj endl; + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, bool inData ) { ioStream << (inData ? "true" : "false"); return ioStream; } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxI32 inData ) { return toStream( ioStream, "%d", inData ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU16 inData ) { return toStream( ioStream, "%u", PxU32(inData) ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU8 inData ) { return toStream( ioStream, "%u", PxU32(inData) ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, char inData ) { return toStream( ioStream, "%c", inData ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU32 inData ) { return toStream( ioStream, "%u", inData ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU64 inData ) { return toStream( ioStream, "%" PX_PRIu64, inData ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const void* inData ) { return ioStream << static_cast(reinterpret_cast(inData)); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxF32 inData ) { return toStream( ioStream, "%g", PxF64(inData) ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxF64 inData ) { return toStream( ioStream, "%g", inData ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, endl_obj) { return ioStream << "\n"; } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxVec3& inData ) + { + ioStream << inData[0]; + ioStream << " "; + ioStream << inData[1]; + ioStream << " "; + ioStream << inData[2]; + return ioStream; + } + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxQuat& inData ) + { + ioStream << inData.x; + ioStream << " "; + ioStream << inData.y; + ioStream << " "; + ioStream << inData.z; + ioStream << " "; + ioStream << inData.w; + return ioStream; + } + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxTransform& inData ) + { + ioStream << inData.q; + ioStream << " "; + ioStream << inData.p; + return ioStream; + } + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxBounds3& inData ) + { + ioStream << inData.minimum; + ioStream << " "; + ioStream << inData.maximum; + return ioStream; + } + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxFilterData& inData ) + { + ioStream << inData.word0 << " " << inData.word1 << " " << inData.word2 << " " << inData.word3; + return ioStream; + } + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, struct PxMetaDataPlane& inData ) + { + ioStream << inData.normal; + ioStream << " "; + ioStream << inData.distance; + return ioStream; + } +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h new file mode 100644 index 000000000..4c8cd665e --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h @@ -0,0 +1,245 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + DEFINE_REPX_DEFAULT_PROPERTY("PxBoxGeometry.HalfExtents", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphereGeometry.Radius", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.ConvexMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.MeshFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.TriangleMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightField", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.RowScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.ColumnScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightFieldFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Length", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Mass", "1000" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Speed", "10" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFrictionV", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFrictionV", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DirOfAnisotropy", "1 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.FrictionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.RestitutionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.LocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.SimulationFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.QueryFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.ContactOffset", "0.02" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.RestOffset", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Flags", "eSIMULATION_SHAPE|eSCENE_QUERY_SHAPE|eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Mass", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MassSpaceInertiaTensor", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearDamping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularDamping", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MaxAngularVelocity", "7" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SleepEnergyThreshold", "0.005" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ContactReportThreshold", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.RigidDynamicFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ParentPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ChildPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetOrientation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.InternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ExternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.yLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.zLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.lower", "-0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.upper", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Mass", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.MassSpaceInertiaTensor", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.MaxProjectionIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SeparationTolerance", "0.1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.InternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.ExternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eX", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eY", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eZ", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eTWIST", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING1", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING2", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Value", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DrivePosition", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.linear", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.angular", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MinDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MaxDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Tolerance", "0.025" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.DistanceJointFlags", "eMAX_DISTANCE_ENABLED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveVelocity", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveGearRatio", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.RevoluteJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.ContactDistance", "0.01" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Upper", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Lower", "-3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.PrismaticJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.SphericalJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("THEEND", "false" ) diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h new file mode 100644 index 000000000..ed175fc4a --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h @@ -0,0 +1,274 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Length", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Mass", "1000" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Speed", "10" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxBoxGeometry.HalfExtents", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphereGeometry.Radius", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.ConvexMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.MeshFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.TriangleMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightField", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.RowScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.ColumnScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightFieldFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFrictionV", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFrictionV", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DirOfAnisotropy", "1 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.FrictionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.RestitutionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.LocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.SimulationFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.QueryFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.ContactOffset", "0.02" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.RestOffset", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Flags", "eSIMULATION_SHAPE|eSCENE_QUERY_SHAPE|eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Mass", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MassSpaceInertiaTensor", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearDamping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularDamping", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MaxAngularVelocity", "7" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SleepThreshold", "0.005" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ContactReportThreshold", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.RigidDynamicFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ParentPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ChildPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetOrientation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.InternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ExternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.yLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.zLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialSpring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialDamping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.lower", "-0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.upper", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Mass", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.MassSpaceInertiaTensor", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.MaxProjectionIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SeparationTolerance", "0.1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.InternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.ExternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SleepThreshold", "0.005" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eX", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eY", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eZ", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eTWIST", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING1", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING2", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Value", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DrivePosition", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.linear", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.angular", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MinDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MaxDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Tolerance", "0.025" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.DistanceJointFlags", "eMAX_DISTANCE_ENABLED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveVelocity", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveGearRatio", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.RevoluteJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.ContactDistance", "0.01" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Upper", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Lower", "-3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.PrismaticJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.SphericalJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.scale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.bias", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ExternalAcceleration", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DampingCoefficient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SolverFrequency", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SleepLinearVelocity", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("THEEND", "false" ) diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h new file mode 100644 index 000000000..ac656bfad --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h @@ -0,0 +1,313 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Length", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Mass", "1000" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Speed", "10" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxBoxGeometry.HalfExtents", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphereGeometry.Radius", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.ConvexMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.MeshFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.TriangleMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightField", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.RowScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.ColumnScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightFieldFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.FrictionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.RestitutionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.LocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.SimulationFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.QueryFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.ContactOffset", "0.02" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.RestOffset", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Flags", "eSIMULATION_SHAPE|eSCENE_QUERY_SHAPE|eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Mass", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MassSpaceInertiaTensor", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearDamping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularDamping", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MaxAngularVelocity", "7" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SleepThreshold", "0.005" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ContactReportThreshold", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.RigidDynamicFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ParentPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ChildPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetOrientation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.InternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ExternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.yLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.zLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialSpring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialDamping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.lower", "-0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.upper", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Mass", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.MassSpaceInertiaTensor", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.MaxProjectionIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SeparationTolerance", "0.1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.InternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.ExternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SleepThreshold", "0.005" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eX", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eY", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eZ", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eTWIST", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING1", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING2", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Value", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DrivePosition", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.linear", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.angular", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MinDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MaxDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Tolerance", "0.025" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.DistanceJointFlags", "eMAX_DISTANCE_ENABLED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveVelocity", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveGearRatio", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.RevoluteJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.ContactDistance", "0.01" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Upper", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Lower", "-3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.PrismaticJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.SphericalJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.scale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.bias", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ExternalAcceleration", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DampingCoefficient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SolverFrequency", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SleepLinearVelocity", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.InertiaScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.FrictionCoefficient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DragCoefficient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.CollisionMassScale", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ExternalAcceleration", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ParticleMass", "0.001" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.Restitution", "0.5" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.DynamicFriction", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.StaticFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.SimulationFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.MaxMotionDistance", "0.06" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.RestOffset", "0.004" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ContactOffset", "0.008" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.GridSize", "0.96" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ProjectionPlane", "0 0 1 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ParticleReadDataFlags", "ePOSITION_BUFFER|eFLAGS_BUFFER" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ParticleBaseFlags", "eCOLLISION_WITH_DYNAMIC_ACTORS|eENABLED|ePER_PARTICLE_REST_OFFSET|ePER_PARTICLE_COLLISION_CACHE_HINT") + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ExternalAcceleration", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ParticleMass", "0.001" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Restitution", "0.5" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.DynamicFriction", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.StaticFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.SimulationFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.MaxMotionDistance", "0.06" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.RestOffset", "0.004" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ContactOffset", "0.008" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.GridSize", "0.64" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ProjectionPlane", "0 0 1 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Stiffness", "20" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Viscosity", "6" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.RestParticleDistance", "0.02" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ParticleReadDataFlags", "ePOSITION_BUFFER|eFLAGS_BUFFER" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ParticleBaseFlags", "eCOLLISION_WITH_DYNAMIC_ACTORS|eENABLED|ePER_PARTICLE_REST_OFFSET|ePER_PARTICLE_COLLISION_CACHE_HINT") + DEFINE_REPX_DEFAULT_PROPERTY("PxAggregate.SelfCollision", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("THEEND", "false" ) diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h new file mode 100644 index 000000000..56e2a4b0a --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h @@ -0,0 +1,173 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_REPXCOLLECTION_H +#define PX_REPXCOLLECTION_H + +#include "common/PxTolerancesScale.h" +#include "PxRepXSerializer.h" + +namespace physx { namespace Sn { + + struct XmlNode; + + struct RepXCollectionItem + { + PxRepXObject liveObject; + XmlNode* descriptor; + RepXCollectionItem( PxRepXObject inItem = PxRepXObject(), XmlNode* inDescriptor = NULL ) + : liveObject( inItem ) + , descriptor( inDescriptor ) + { + } + }; + + struct RepXDefaultEntry + { + const char* name; + const char* value; + RepXDefaultEntry( const char* pn, const char* val ) : name( pn ), value( val ){} + }; + + /** + * The result of adding an object to the collection. + */ + struct RepXAddToCollectionResult + { + enum Enum + { + Success, + SerializerNotFound, + InvalidParameters, //Null data passed in. + AlreadyInCollection + }; + + PxSerialObjectId collectionId; + Enum result; + + RepXAddToCollectionResult( Enum inResult = Success, const PxSerialObjectId inId = 0 ) + : collectionId( inId ) + , result( inResult ) + { + } + bool isValid() { return result == Success && collectionId != 0; } + }; + /** + * A RepX collection contains a set of static data objects that can be transformed + * into live objects. It uses RepX serializer to do two transformations: + * live object <-> collection object (descriptor) + * collection object <-> file system. + * + * A live object is considered to be something live in the physics + * world such as a material or a rigidstatic. + * + * A collection object is a piece of data from which a live object + * of identical characteristics can be created. + * + * Clients need to pass PxCollection so that objects can resolve + * references. In addition, objects must be added in an order such that + * references can be resolved in the first place. So objects must be added + * to the collection *after* objects they are dependent upon. + * + * When deserializing from a file, the collection will allocate char*'s that will + * not be freed when the collection itself is freed. The user must be responsible + * for these character allocations. + */ + class RepXCollection + { + protected: + virtual ~RepXCollection(){} + + public: + virtual void destroy() = 0; + + /** + * Set the scale on this collection. The scale is saved with the collection. + * + * If the scale wasn't set, it will be invalid. + */ + virtual void setTolerancesScale( const PxTolerancesScale& inScale ) = 0; + + /** + * Get the scale that was set at collection creation time or at load time. + * If this is a loaded file and the source data does not contain a scale + * this value will be invalid (PxTolerancesScale::isValid()). + */ + virtual PxTolerancesScale getTolerancesScale() const = 0; + + /** + * Set the up vector on this collection. The up vector is saved with the collection. + * + * If the up vector wasn't set, it will be (0,0,0). + */ + virtual void setUpVector( const PxVec3& inUpVector ) = 0; + + /** + * If the up vector wasn't set, it will be (0,0,0). Else this will be the up vector + * optionally set when the collection was created. + */ + virtual PxVec3 getUpVector() const = 0; + + virtual const char* getVersion() = 0; + static const char* getLatestVersion(); + + //Necessary accessor functions for translation/upgrading. + virtual const RepXCollectionItem* begin() const = 0; + virtual const RepXCollectionItem* end() const = 0; + + + //Performs a deep copy of the repx node. + virtual XmlNode* copyRepXNode( const XmlNode* srcNode ) = 0; + + virtual void addCollectionItem( RepXCollectionItem inItem ) = 0; + + //Create a new repx node with this name. Its value is unset. + virtual XmlNode& createRepXNode( const char* name ) = 0; + + virtual RepXCollection& createCollection( const char* inVersionStr ) = 0; + //Release this when finished. + virtual XmlReaderWriter& createNodeEditor() = 0; + + virtual PxAllocatorCallback& getAllocator() = 0; + + virtual bool instantiateCollection( PxRepXInstantiationArgs& inArgs, PxCollection& inPxCollection ) = 0; + + + virtual RepXAddToCollectionResult addRepXObjectToCollection( const PxRepXObject& inObject, PxCollection* inCollection, PxRepXInstantiationArgs& inArgs ) = 0; + + /** + * Save this collection out to a file stream. Uses the RepX serialize to perform + * collection object->file conversions. + * + * /param[in] inStream Write-only stream to save collection out to. + */ + virtual void save( PxOutputStream& inStream ) = 0; + }; +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp new file mode 100644 index 000000000..eb0ab6e8c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp @@ -0,0 +1,553 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPhysicsAPI.h" +#include "PxMetaDataObjects.h" +#include "CmIO.h" +#include "SnPxStreamOperators.h" +#include "PsUtilities.h" +#include "SnXmlImpl.h" +#include "SnXmlSerializer.h" +#include "SnXmlDeserializer.h" +#include "SnRepXCoreSerializer.h" + +using namespace physx::Sn; +namespace physx { + typedef PxReadOnlyPropertyInfo TIncomingJointPropType; + + //************************************************************* + // Actual RepXSerializer implementations for PxMaterial + //************************************************************* + PxMaterial* PxMaterialRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs ) + { + return inArgs.physics.createMaterial(0, 0, 0); + } + + PxRepXObject PxShapeRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + PxProfileAllocatorWrapper wrapper( inAllocator.getAllocator() ); + TReaderNameStack names( wrapper ); + PxProfileArray contexts( wrapper ); + bool hadError = false; + RepXVisitorReader theVisitor( names, contexts, inArgs, inReader, NULL, inAllocator, *inCollection, hadError ); + + Ps::Array materials; + PxGeometry* geometry = NULL; + parseShape( theVisitor, geometry, materials ); + if(hadError) + return PxRepXObject(); + PxShape *theShape = inArgs.physics.createShape( *geometry, materials.begin(), Ps::to16(materials.size()) ); + + switch(geometry->getType()) + { + case PxGeometryType::eSPHERE : + static_cast(geometry)->~PxSphereGeometry(); + break; + case PxGeometryType::ePLANE : + static_cast(geometry)->~PxPlaneGeometry(); + break; + case PxGeometryType::eCAPSULE : + static_cast(geometry)->~PxCapsuleGeometry(); + break; + case PxGeometryType::eBOX : + static_cast(geometry)->~PxBoxGeometry(); + break; + case PxGeometryType::eCONVEXMESH : + static_cast(geometry)->~PxConvexMeshGeometry(); + break; + case PxGeometryType::eTRIANGLEMESH : + static_cast(geometry)->~PxTriangleMeshGeometry(); + break; + case PxGeometryType::eHEIGHTFIELD : + static_cast(geometry)->~PxHeightFieldGeometry(); + break; + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ASSERT(0); + } + inAllocator.getAllocator().deallocate(geometry); + + bool ret = readAllProperties( inArgs, inReader, theShape, inAllocator, *inCollection ); + + return ret ? PxCreateRepXObject(theShape) : PxRepXObject(); + } + + //************************************************************* + // Actual RepXSerializer implementations for PxTriangleMesh + //************************************************************* + + template + inline void writeTriangle( MemoryBuffer& inTempBuffer, const Triangle& inTriangle ) + { + inTempBuffer << inTriangle.mIdx0 + << " " << inTriangle.mIdx1 + << " " << inTriangle.mIdx2; + } + + PxU32 materialAccess( const PxTriangleMesh* inMesh, PxU32 inIndex ) { return inMesh->getTriangleMaterialIndex( inIndex ); } + template + void writeDatatype( MemoryBuffer& inTempBuffer, const TDataType& inType ) { inTempBuffer << inType; } + + void PxBVH33TriangleMeshRepXSerializer::objectToFileImpl( const PxBVH33TriangleMesh* mesh, PxCollection* /*inCollection*/, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs ) + { + bool hasMatIndex = mesh->getTriangleMaterialIndex(0) != 0xffff; + PxU32 numVertices = mesh->getNbVertices(); + const PxVec3* vertices = mesh->getVertices(); + writeBuffer( inWriter, inTempBuffer, 2, vertices, numVertices, "Points", writePxVec3 ); + bool isU16 = mesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false; + PxU32 triCount = mesh->getNbTriangles(); + const void* indices = mesh->getTriangles(); + if ( isU16 ) + writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast* >( indices ), triCount, "Triangles", writeTriangle ); + else + writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast* >( indices ), triCount, "Triangles", writeTriangle ); + if ( hasMatIndex ) + writeBuffer( inWriter, inTempBuffer, 6, mesh, materialAccess, triCount, "materialIndices", writeDatatype ); + + //Cooked stream + PxTriangleMeshDesc meshDesc; + meshDesc.points.count = numVertices; + meshDesc.points.data = vertices; + meshDesc.points.stride = sizeof(PxVec3); + meshDesc.triangles.count = triCount; + meshDesc.triangles.data = indices; + meshDesc.triangles.stride = isU16?3*sizeof(PxU16):3*sizeof(PxU32); + + if(isU16) + { + meshDesc.triangles.stride = sizeof(PxU16)*3; + meshDesc.flags |= PxMeshFlag::e16_BIT_INDICES; + } + else + { + meshDesc.triangles.stride = sizeof(PxU32)*3; + } + + if(hasMatIndex) + { + PxMaterialTableIndex* materialIndices = new PxMaterialTableIndex[triCount]; + for(PxU32 i = 0; i < triCount; i++) + materialIndices[i] = mesh->getTriangleMaterialIndex(i); + + meshDesc.materialIndices.data = materialIndices; + meshDesc.materialIndices.stride = sizeof(PxMaterialTableIndex); + + } + + if(inArgs.cooker != NULL) + { + TMemoryPoolManager theManager(mAllocator); + MemoryBuffer theTempBuf( &theManager ); + theTempBuf.clear(); + inArgs.cooker->cookTriangleMesh( meshDesc, theTempBuf ); + + writeBuffer( inWriter, inTempBuffer, 16, theTempBuf.mBuffer, theTempBuf.mWriteOffset, "CookedData", writeDatatype ); + } + + delete []meshDesc.materialIndices.data; + } + + PxRepXObject PxBVH33TriangleMeshRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* /*inCollection*/ ) + { + //We can't do a simple inverse; we *have* to cook data to get a mesh. + PxTriangleMeshDesc theDesc; + readStridedBufferProperty( inReader, "points", theDesc.points, inAllocator); + readStridedBufferProperty >( inReader, "triangles", theDesc.triangles, inAllocator); + PxU32 triCount; + readStridedBufferProperty( inReader, "materialIndices", theDesc.materialIndices, triCount, inAllocator); + PxStridedData cookedData; + cookedData.stride = sizeof(PxU8); + PxU32 dataSize; + readStridedBufferProperty( inReader, "CookedData", cookedData, dataSize, inAllocator); + + TMemoryPoolManager theManager(inAllocator.getAllocator()); + MemoryBuffer theTempBuf( &theManager ); + +// PxTriangleMesh* theMesh = NULL; + PxBVH33TriangleMesh* theMesh = NULL; + + if(dataSize != 0) + { + theTempBuf.write(cookedData.data, dataSize*sizeof(PxU8)); +// theMesh = inArgs.physics.createTriangleMesh( theTempBuf ); + theMesh = static_cast(inArgs.physics.createTriangleMesh( theTempBuf )); + } + + if(theMesh == NULL) + { + PX_ASSERT(inArgs.cooker); + theTempBuf.clear(); + + { + PxCookingParams params = inArgs.cooker->getParams(); + params.midphaseDesc = PxMeshMidPhase::eBVH33; + inArgs.cooker->setParams(params); + } + + inArgs.cooker->cookTriangleMesh( theDesc, theTempBuf ); +// theMesh = inArgs.physics.createTriangleMesh( theTempBuf ); + theMesh = static_cast(inArgs.physics.createTriangleMesh( theTempBuf )); + } + + return PxCreateRepXObject( theMesh ); + } + + void PxBVH34TriangleMeshRepXSerializer::objectToFileImpl( const PxBVH34TriangleMesh* mesh, PxCollection* /*inCollection*/, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs ) + { + bool hasMatIndex = mesh->getTriangleMaterialIndex(0) != 0xffff; + PxU32 numVertices = mesh->getNbVertices(); + const PxVec3* vertices = mesh->getVertices(); + writeBuffer( inWriter, inTempBuffer, 2, vertices, numVertices, "Points", writePxVec3 ); + bool isU16 = mesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false; + PxU32 triCount = mesh->getNbTriangles(); + const void* indices = mesh->getTriangles(); + if ( isU16 ) + writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast* >( indices ), triCount, "Triangles", writeTriangle ); + else + writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast* >( indices ), triCount, "Triangles", writeTriangle ); + if ( hasMatIndex ) + writeBuffer( inWriter, inTempBuffer, 6, mesh, materialAccess, triCount, "materialIndices", writeDatatype ); + + //Cooked stream + PxTriangleMeshDesc meshDesc; + meshDesc.points.count = numVertices; + meshDesc.points.data = vertices; + meshDesc.points.stride = sizeof(PxVec3); + meshDesc.triangles.count = triCount; + meshDesc.triangles.data = indices; + meshDesc.triangles.stride = isU16?3*sizeof(PxU16):3*sizeof(PxU32); + + if(isU16) + { + meshDesc.triangles.stride = sizeof(PxU16)*3; + meshDesc.flags |= PxMeshFlag::e16_BIT_INDICES; + } + else + { + meshDesc.triangles.stride = sizeof(PxU32)*3; + } + + if(hasMatIndex) + { + PxMaterialTableIndex* materialIndices = new PxMaterialTableIndex[triCount]; + for(PxU32 i = 0; i < triCount; i++) + materialIndices[i] = mesh->getTriangleMaterialIndex(i); + + meshDesc.materialIndices.data = materialIndices; + meshDesc.materialIndices.stride = sizeof(PxMaterialTableIndex); + + } + + if(inArgs.cooker != NULL) + { + TMemoryPoolManager theManager(mAllocator); + MemoryBuffer theTempBuf( &theManager ); + theTempBuf.clear(); + inArgs.cooker->cookTriangleMesh( meshDesc, theTempBuf ); + + writeBuffer( inWriter, inTempBuffer, 16, theTempBuf.mBuffer, theTempBuf.mWriteOffset, "CookedData", writeDatatype ); + } + + delete []meshDesc.materialIndices.data; + } + + PxRepXObject PxBVH34TriangleMeshRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* /*inCollection*/ ) + { + //We can't do a simple inverse; we *have* to cook data to get a mesh. + PxTriangleMeshDesc theDesc; + readStridedBufferProperty( inReader, "points", theDesc.points, inAllocator); + readStridedBufferProperty >( inReader, "triangles", theDesc.triangles, inAllocator); + PxU32 triCount; + readStridedBufferProperty( inReader, "materialIndices", theDesc.materialIndices, triCount, inAllocator); + PxStridedData cookedData; + cookedData.stride = sizeof(PxU8); + PxU32 dataSize; + readStridedBufferProperty( inReader, "CookedData", cookedData, dataSize, inAllocator); + + TMemoryPoolManager theManager(inAllocator.getAllocator()); + MemoryBuffer theTempBuf( &theManager ); + +// PxTriangleMesh* theMesh = NULL; + PxBVH34TriangleMesh* theMesh = NULL; + + if(dataSize != 0) + { + theTempBuf.write(cookedData.data, dataSize*sizeof(PxU8)); +// theMesh = inArgs.physics.createTriangleMesh( theTempBuf ); + theMesh = static_cast(inArgs.physics.createTriangleMesh( theTempBuf )); + } + + if(theMesh == NULL) + { + PX_ASSERT(inArgs.cooker); + theTempBuf.clear(); + + { + PxCookingParams params = inArgs.cooker->getParams(); + params.midphaseDesc = PxMeshMidPhase::eBVH34; + inArgs.cooker->setParams(params); + } + + inArgs.cooker->cookTriangleMesh( theDesc, theTempBuf ); +// theMesh = inArgs.physics.createTriangleMesh( theTempBuf ); + theMesh = static_cast(inArgs.physics.createTriangleMesh( theTempBuf )); + } + + return PxCreateRepXObject(theMesh); + } + + + //************************************************************* + // Actual RepXSerializer implementations for PxHeightField + //************************************************************* + void PxHeightFieldRepXSerializer::objectToFileImpl( const PxHeightField* inHeightField, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/) + { + PxHeightFieldDesc theDesc; + + theDesc.nbRows = inHeightField->getNbRows(); + theDesc.nbColumns = inHeightField->getNbColumns(); + theDesc.format = inHeightField->getFormat(); + theDesc.samples.stride = inHeightField->getSampleStride(); + theDesc.samples.data = NULL; + theDesc.convexEdgeThreshold = inHeightField->getConvexEdgeThreshold(); + theDesc.flags = inHeightField->getFlags(); + + PxU32 theCellCount = inHeightField->getNbRows() * inHeightField->getNbColumns(); + PxU32 theSampleStride = sizeof( PxHeightFieldSample ); + PxU32 theSampleBufSize = theCellCount * theSampleStride; + PxHeightFieldSample* theSamples = reinterpret_cast< PxHeightFieldSample*> ( inTempBuffer.mManager->allocate( theSampleBufSize ) ); + inHeightField->saveCells( theSamples, theSampleBufSize ); + theDesc.samples.data = theSamples; + writeAllProperties( &theDesc, inWriter, inTempBuffer, *inCollection ); + writeStridedBufferProperty( inWriter, inTempBuffer, "samples", theDesc.samples, theDesc.nbRows * theDesc.nbColumns, 6, writeHeightFieldSample); + inTempBuffer.mManager->deallocate( reinterpret_cast(theSamples) ); + } + + PxRepXObject PxHeightFieldRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + PX_ASSERT(inArgs.cooker); + PxHeightFieldDesc theDesc; + readAllProperties( inArgs, inReader, &theDesc, inAllocator, *inCollection ); + //Now read the data... + PxU32 count = 0; //ignored becaues numRows and numColumns tells the story + readStridedBufferProperty( inReader, "samples", theDesc.samples, count, inAllocator); + PxHeightField* retval = inArgs.cooker->createHeightField( theDesc, inArgs.physics.getPhysicsInsertionCallback() ); + return PxCreateRepXObject(retval); + } + + //************************************************************* + // Actual RepXSerializer implementations for PxConvexMesh + //************************************************************* + void PxConvexMeshRepXSerializer::objectToFileImpl( const PxConvexMesh* mesh, PxCollection* /*inCollection*/, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs ) + { + writeBuffer( inWriter, inTempBuffer, 2, mesh->getVertices(), mesh->getNbVertices(), "points", writePxVec3 ); + + if(inArgs.cooker != NULL) + { + //Cache cooked Data + PxConvexMeshDesc theDesc; + theDesc.points.data = mesh->getVertices(); + theDesc.points.stride = sizeof(PxVec3); + theDesc.points.count = mesh->getNbVertices(); + + theDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX; + TMemoryPoolManager theManager(mAllocator); + MemoryBuffer theTempBuf( &theManager ); + inArgs.cooker->cookConvexMesh( theDesc, theTempBuf ); + + writeBuffer( inWriter, inTempBuffer, 16, theTempBuf.mBuffer, theTempBuf.mWriteOffset, "CookedData", writeDatatype ); + } + + } + + //Conversion from scene object to descriptor. + PxRepXObject PxConvexMeshRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* /*inCollection*/) + { + PxConvexMeshDesc theDesc; + readStridedBufferProperty( inReader, "points", theDesc.points, inAllocator); + theDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX; + + PxStridedData cookedData; + cookedData.stride = sizeof(PxU8); + PxU32 dataSize; + readStridedBufferProperty( inReader, "CookedData", cookedData, dataSize, inAllocator); + + TMemoryPoolManager theManager(inAllocator.getAllocator()); + MemoryBuffer theTempBuf( &theManager ); + + PxConvexMesh* theMesh = NULL; + + if(dataSize != 0) + { + theTempBuf.write(cookedData.data, dataSize*sizeof(PxU8)); + theMesh = inArgs.physics.createConvexMesh( theTempBuf ); + } + + if(theMesh == NULL) + { + PX_ASSERT(inArgs.cooker); + theTempBuf.clear(); + + inArgs.cooker->cookConvexMesh( theDesc, theTempBuf ); + theMesh = inArgs.physics.createConvexMesh( theTempBuf ); + } + + return PxCreateRepXObject(theMesh); + } + + //************************************************************* + // Actual RepXSerializer implementations for PxRigidStatic + //************************************************************* + PxRigidStatic* PxRigidStaticRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs ) + { + return inArgs.physics.createRigidStatic( PxTransform(PxIdentity) ); + } + + //************************************************************* + // Actual RepXSerializer implementations for PxRigidDynamic + //************************************************************* + PxRigidDynamic* PxRigidDynamicRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs ) + { + return inArgs.physics.createRigidDynamic( PxTransform(PxIdentity) ); + } + + //************************************************************* + // Actual RepXSerializer implementations for PxArticulation + //************************************************************* + void PxArticulationRepXSerializer::objectToFileImpl( const PxArticulation* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/) + { + TNameStack nameStack( inTempBuffer.mManager->mWrapper ); + Sn::TArticulationLinkLinkMap linkMap( inTempBuffer.mManager->mWrapper ); + RepXVisitorWriter writer( nameStack, inWriter, inObj, inTempBuffer, *inCollection, &linkMap ); + RepXPropertyFilter > theOp( writer ); + visitAllProperties( theOp ); + } + PxArticulation* PxArticulationRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs ) { return inArgs.physics.createArticulation(); } + + //************************************************************* + // Actual RepXSerializer implementations for PxAggregate + //************************************************************* + void PxAggregateRepXSerializer::objectToFileImpl( const PxAggregate* data, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/) + { + PxArticulationLink *link = NULL; + inWriter.addAndGotoChild( "Actors" ); + for(PxU32 i = 0; i < data->getNbActors(); ++i) + { + PxActor* actor; + + if(data->getActors(&actor, 1, i)) + { + link = actor->is(); + } + + if(link && !link->getInboundJoint() ) + { + writeProperty( inWriter, *inCollection, inTempBuffer, "PxArticulationRef", &link->getArticulation()); + } + else if( !link ) + { + PxSerialObjectId theId = 0; + + theId = inCollection->getId( *actor ); + if( theId == 0 ) + theId = static_cast(reinterpret_cast(actor)); + + writeProperty( inWriter, *inCollection, inTempBuffer, "PxActorRef", theId ); + } + } + + inWriter.leaveChild( ); + + writeProperty( inWriter, *inCollection, inTempBuffer, "NumActors", data->getNbActors() ); + writeProperty( inWriter, *inCollection, inTempBuffer, "MaxNbActors", data->getMaxNbActors() ); + writeProperty( inWriter, *inCollection, inTempBuffer, "SelfCollision", data->getSelfCollision() ); + + writeAllProperties( data, inWriter, inTempBuffer, *inCollection ); + } + + PxRepXObject PxAggregateRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + PxU32 numActors; + readProperty( inReader, "NumActors", numActors ); + PxU32 maxNbActors; + readProperty( inReader, "MaxNbActors", maxNbActors ); + + bool selfCollision; + bool ret = readProperty( inReader, "SelfCollision", selfCollision ); + + PxAggregate* theAggregate = inArgs.physics.createAggregate(maxNbActors, selfCollision); + ret &= readAllProperties( inArgs, inReader, theAggregate, inAllocator, *inCollection ); + + inReader.pushCurrentContext(); + if ( inReader.gotoChild( "Actors" ) ) + { + inReader.pushCurrentContext(); + for( bool matSuccess = inReader.gotoFirstChild(); matSuccess; + matSuccess = inReader.gotoNextSibling() ) + { + const char* actorType = inReader.getCurrentItemName(); + if ( 0 == physx::shdfnd::stricmp( actorType, "PxActorRef" ) ) + { + PxActor *actor = NULL; + ret &= readReference( inReader, *inCollection, actor ); + + if(actor) + { + PxScene *currScene = actor->getScene(); + if(currScene) + { + currScene->removeActor(*actor); + } + theAggregate->addActor(*actor); + } + } + else if ( 0 == physx::shdfnd::stricmp( actorType, "PxArticulationRef" ) ) + { + PxArticulation* articulation = NULL; + ret &= readReference( inReader, *inCollection, articulation ); + if(articulation) + { + PxScene *currScene = articulation->getScene(); + if(currScene) + { + currScene->removeArticulation(*articulation); + } + theAggregate->addArticulation(*articulation); + } + } + } + inReader.popCurrentContext(); + inReader.leaveChild(); + } + inReader.popCurrentContext(); + + return ret ? PxCreateRepXObject(theAggregate) : PxRepXObject(); + } +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h new file mode 100644 index 000000000..d04c46cd3 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h @@ -0,0 +1,125 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef SN_REPX_CORE_SERIALIZER_H +#define SN_REPX_CORE_SERIALIZER_H +/** \addtogroup RepXSerializers + @{ +*/ +#include "foundation/PxSimpleTypes.h" +#include "SnRepXSerializerImpl.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class XmlReader; + class XmlMemoryAllocator; + class XmlWriter; + class MemoryBuffer; + + struct PxMaterialRepXSerializer : RepXSerializerImpl + { + PxMaterialRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual PxMaterial* allocateObject( PxRepXInstantiationArgs& ); + }; + + struct PxShapeRepXSerializer : public RepXSerializerImpl + { + PxShapeRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxShape* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + + struct PxBVH33TriangleMeshRepXSerializer : public RepXSerializerImpl + { + PxBVH33TriangleMeshRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxBVH33TriangleMesh*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxBVH33TriangleMesh* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + struct PxBVH34TriangleMeshRepXSerializer : public RepXSerializerImpl + { + PxBVH34TriangleMeshRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxBVH34TriangleMesh*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxBVH34TriangleMesh* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + + struct PxHeightFieldRepXSerializer : public RepXSerializerImpl + { + PxHeightFieldRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxHeightField*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxHeightField* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + + struct PxConvexMeshRepXSerializer : public RepXSerializerImpl + { + PxConvexMeshRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxConvexMesh*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxConvexMesh* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + + struct PxRigidStaticRepXSerializer : public RepXSerializerImpl + { + PxRigidStaticRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual PxRigidStatic* allocateObject( PxRepXInstantiationArgs& ); + }; + + struct PxRigidDynamicRepXSerializer : public RepXSerializerImpl + { + PxRigidDynamicRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual PxRigidDynamic* allocateObject( PxRepXInstantiationArgs& ); + }; + + struct PxArticulationRepXSerializer : public RepXSerializerImpl + { + PxArticulationRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxArticulation*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxArticulation* allocateObject( PxRepXInstantiationArgs& ); + }; + + struct PxAggregateRepXSerializer : public RepXSerializerImpl + { + PxAggregateRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxAggregate*, PxCollection*, XmlWriter& , MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxAggregate* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif +/** @} */ + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h new file mode 100644 index 000000000..95f7a3448 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_REPX_SERIALIZER_IMPL_H +#define PX_REPX_SERIALIZER_IMPL_H + +#include "PsUserAllocated.h" +#include "SnXmlVisitorWriter.h" +#include "SnXmlVisitorReader.h" + +namespace physx { + using namespace Sn; + + /** + * The repx serializer impl takes the raw, untyped repx extension interface + * and implements the simpler functions plus does the reinterpret-casts required + * for any object to implement the serializer safely. + */ + template + struct RepXSerializerImpl : public PxRepXSerializer, shdfnd::UserAllocated + { + protected: + RepXSerializerImpl( const RepXSerializerImpl& inOther ); + RepXSerializerImpl& operator=( const RepXSerializerImpl& inOther ); + + public: + PxAllocatorCallback& mAllocator; + + RepXSerializerImpl( PxAllocatorCallback& inAllocator ) + : mAllocator( inAllocator ) + { + } + + virtual const char* getTypeName() { return PxTypeInfo::name(); } + + virtual void objectToFile( const PxRepXObject& inLiveObject, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs ) + { + const TLiveType* theObj = reinterpret_cast( inLiveObject.serializable ); + objectToFileImpl( theObj, inCollection, inWriter, inTempBuffer, inArgs ); + } + + virtual PxRepXObject fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + TLiveType* theObj( allocateObject( inArgs ) ); + if ( theObj ) + if(fileToObjectImpl( theObj, inReader, inAllocator, inArgs, inCollection )) + return PxCreateRepXObject(theObj); + return PxRepXObject(); + } + + virtual void objectToFileImpl( const TLiveType* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/) + { + writeAllProperties( inObj, inWriter, inTempBuffer, *inCollection ); + } + + virtual bool fileToObjectImpl( TLiveType* inObj, XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + return readAllProperties( inArgs, inReader, inObj, inAllocator, *inCollection ); + } + + virtual TLiveType* allocateObject( PxRepXInstantiationArgs& inArgs ) = 0; + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp new file mode 100644 index 000000000..978e98b2e --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp @@ -0,0 +1,465 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "SnXmlImpl.h" +#include "SnXmlReader.h" +#include "SnXmlMemoryAllocator.h" +#include "PsFoundation.h" +#include "SnRepXCollection.h" +#include "SnRepXUpgrader.h" + +using namespace physx::profile; + +namespace physx { namespace Sn { + + #define DEFINE_REPX_DEFAULT_PROPERTY( name, val ) RepXDefaultEntry( name, val ), + + static RepXDefaultEntry gRepX1_0Defaults[] = { + #include "SnRepX1_0Defaults.h" + }; + static PxU32 gNumRepX1_0Default = sizeof( gRepX1_0Defaults ) / sizeof ( *gRepX1_0Defaults ); + + static RepXDefaultEntry gRepX3_1Defaults[] = { + #include "SnRepX3_1Defaults.h" + }; + static PxU32 gNumRepX3_1Defaults = sizeof( gRepX3_1Defaults ) / sizeof ( *gRepX3_1Defaults ); + + static RepXDefaultEntry gRepX3_2Defaults[] = { + #include "SnRepX3_2Defaults.h" + }; + static PxU32 gNumRepX3_2Defaults = sizeof( gRepX3_2Defaults ) / sizeof ( *gRepX3_2Defaults ); + + inline const char* nextPeriod( const char* str ) + { + for( ++str; str && *str && *str != '.'; ++str ); //empty loop intentional + return str; + } + + inline bool safeStrEq(const char* lhs, const char* rhs) + { + if (lhs == rhs) + return true; + //If they aren't equal, and one of them is null, + //then they can't be equal. + //This is assuming that the null char* is not equal to + //the empty "" char*. + if (!lhs || !rhs) + return false; + + return ::strcmp(lhs, rhs) == 0; + } + + typedef PxProfileHashMap TNameOffsetMap; + + void setMissingPropertiesToDefault( XmlNode* topNode, XmlReaderWriter& editor, const RepXDefaultEntry* defaults, PxU32 numDefaults, TNameOffsetMap& map ) + { + for ( XmlNode* child = topNode->mFirstChild; child != NULL; child = child->mNextSibling ) + setMissingPropertiesToDefault( child, editor, defaults, numDefaults, map ); + + const TNameOffsetMap::Entry* entry( map.find( topNode->mName ) ); + if ( entry ) + { + XmlReaderWriter& theReader( editor ); + theReader.setNode( *topNode ); + char nameBuffer[512] = {0}; + size_t nameLen = strlen( topNode->mName ); + //For each default property entry for this node type. + for ( const RepXDefaultEntry* item = defaults + entry->second; strncmp( item->name, topNode->mName, nameLen ) == 0; ++item ) + { + bool childAdded = false; + const char* nameStart = item->name + nameLen; + ++nameStart; + theReader.pushCurrentContext(); + const char* str = nameStart; + while( *str ) + { + const char *period = nextPeriod( str ); + size_t len = size_t(PxMin( period - str, ptrdiff_t(1023) )); //can't be too careful these days. + PxMemCopy( nameBuffer, str, PxU32(len) ); + nameBuffer[len] = 0; + if ( theReader.gotoChild( nameBuffer ) == false ) + { + childAdded = true; + theReader.addOrGotoChild( nameBuffer ); + } + if (*period ) + str = period + 1; + else + str = period; + } + if ( childAdded ) + theReader.setCurrentItemValue( item->value ); + theReader.popCurrentContext(); + } + } + } + + + static void setMissingPropertiesToDefault( RepXCollection& collection, XmlReaderWriter& editor, const RepXDefaultEntry* defaults, PxU32 numDefaults ) + { + PxProfileAllocatorWrapper wrapper( collection.getAllocator() ); + //Release all strings at once, instead of piece by piece + XmlMemoryAllocatorImpl alloc( collection.getAllocator() ); + //build a hashtable of the initial default value strings. + TNameOffsetMap nameOffsets( wrapper ); + for ( PxU32 idx = 0; idx < numDefaults; ++idx ) + { + const RepXDefaultEntry& item( defaults[idx] ); + size_t nameLen = 0; + const char* periodPtr = nextPeriod (item.name); + for ( ; periodPtr && *periodPtr; ++periodPtr ) if( *periodPtr == '.' ) break; + if ( periodPtr == NULL || *periodPtr != '.' ) continue; + nameLen = size_t(periodPtr - item.name); + char* newMem = reinterpret_cast(alloc.allocate( PxU32(nameLen + 1) )); + PxMemCopy( newMem, item.name, PxU32(nameLen) ); + newMem[nameLen] = 0; + + if ( nameOffsets.find( newMem ) ) + alloc.deallocate( reinterpret_cast(newMem) ); + else + nameOffsets.insert( newMem, idx ); + } + //Run through each collection item, and recursively find it and its children + //If an object's name is in the hash map, check and add any properties that don't exist. + //else return. + for ( const RepXCollectionItem* item = collection.begin(), *end = collection.end(); item != end; ++ item ) + { + RepXCollectionItem theItem( *item ); + setMissingPropertiesToDefault( theItem.descriptor, editor, defaults, numDefaults, nameOffsets ); + } + } + + struct RecursiveTraversal + { + RecursiveTraversal(XmlReaderWriter& editor): mEditor(editor) {} + void traverse() + { + mEditor.pushCurrentContext(); + updateNode(); + for(bool exists = mEditor.gotoFirstChild(); exists; exists = mEditor.gotoNextSibling()) + traverse(); + mEditor.popCurrentContext(); + } + virtual void updateNode() = 0; + virtual ~RecursiveTraversal() {} + XmlReaderWriter& mEditor; + protected: + RecursiveTraversal& operator=(const RecursiveTraversal&){return *this;} + }; + + + RepXCollection& RepXUpgrader::upgrade10CollectionTo3_1Collection(RepXCollection& src) + { + XmlReaderWriter& editor( src.createNodeEditor() ); + setMissingPropertiesToDefault(src, editor, gRepX1_0Defaults, gNumRepX1_0Default ); + + + RepXCollection* dest = &src.createCollection("3.1.1"); + + for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item ) + { + //either src or dest could do the copy operation, it doesn't matter who does it. + RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) ); + editor.setNode( *const_cast( newItem.descriptor ) ); + //Some old files have this name in their system. + editor.renameProperty( "MassSpaceInertia", "MassSpaceInertiaTensor" ); + editor.renameProperty( "SleepEnergyThreshold", "SleepThreshold" ); + + if ( strstr( newItem.liveObject.typeName, "Joint" ) || strstr( newItem.liveObject.typeName, "joint" ) ) + { + //Joints changed format a bit. old joints looked like: + /* + 1627536 + 1628368 + 0 0 0 1 0.5 0.5 0.5 + 0 0 0 1 0.3 0.3 0.3*/ + //New joints look like: + /* + + 58320336 + 56353568 + + + 0 0 0 1 0.5 0.5 0.5 + 0 0 0 1 0.3 0.3 0.3 + + */ + const char* actor0, *actor1, *lp0, *lp1; + editor.readAndRemoveProperty( "Actor0", actor0 ); + editor.readAndRemoveProperty( "Actor1", actor1 ); + editor.readAndRemoveProperty( "LocalPose0", lp0 ); + editor.readAndRemoveProperty( "LocalPose1", lp1 ); + + editor.addOrGotoChild( "Actors" ); + editor.writePropertyIfNotEmpty( "actor0", actor0 ); + editor.writePropertyIfNotEmpty( "actor1", actor1 ); + editor.leaveChild(); + + editor.addOrGotoChild( "LocalPose" ); + editor.writePropertyIfNotEmpty( "eACTOR0", lp0 ); + editor.writePropertyIfNotEmpty( "eACTOR1", lp1 ); + editor.leaveChild(); + } + + + + //now desc owns the new node. Collections share a single allocation pool, however, + //which will get destroyed when all the collections referencing it are destroyed themselves. + //Data on nodes is shared between nodes, but the node structure itself is allocated. + dest->addCollectionItem( newItem ); + } + editor.release(); + src.destroy(); + return *dest; + } + + RepXCollection& RepXUpgrader::upgrade3_1CollectionTo3_2Collection(RepXCollection& src) + { + XmlReaderWriter& editor( src.createNodeEditor() ); + setMissingPropertiesToDefault(src, editor, gRepX3_1Defaults, gNumRepX3_1Defaults ); + + RepXCollection* dest = &src.createCollection("3.2.0"); + + for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item ) + { + //either src or dest could do the copy operation, it doesn't matter who does it. + RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) ); + editor.setNode( *const_cast( newItem.descriptor ) ); + + if ( strstr( newItem.liveObject.typeName, "PxMaterial" ) ) + { + editor.removeChild( "DynamicFrictionV" ); + editor.removeChild( "StaticFrictionV" ); + editor.removeChild( "dirOfAnisotropy" ); + } + //now desc owns the new node. Collections share a single allocation pool, however, + //which will get destroyed when all the collections referencing it are destroyed themselves. + //Data on nodes is shared between nodes, but the node structure itself is allocated. + dest->addCollectionItem( newItem ); + } + editor.release(); + src.destroy(); + return *dest; + } + + RepXCollection& RepXUpgrader::upgrade3_2CollectionTo3_3Collection(RepXCollection& src) + { + XmlReaderWriter& editor( src.createNodeEditor() ); + setMissingPropertiesToDefault(src, editor, gRepX3_2Defaults, gNumRepX3_2Defaults ); + + RepXCollection* dest = &src.createCollection("3.3.0"); + + + + struct RenameSpringToStiffness : public RecursiveTraversal + { + RenameSpringToStiffness(XmlReaderWriter& editor_): RecursiveTraversal(editor_) {} + + void updateNode() + { + mEditor.renameProperty("Spring", "Stiffness"); + mEditor.renameProperty("TangentialSpring", "TangentialStiffness"); + } + }; + + + struct UpdateArticulationSwingLimit : public RecursiveTraversal + { + UpdateArticulationSwingLimit(XmlReaderWriter& editor_): RecursiveTraversal(editor_) {} + + void updateNode() + { + if(!Ps::stricmp(mEditor.getCurrentItemName(), "yLimit") && !Ps::stricmp(mEditor.getCurrentItemValue(), "0")) + mEditor.setCurrentItemValue("0.785398"); + + if(!Ps::stricmp(mEditor.getCurrentItemName(), "zLimit") && !Ps::stricmp(mEditor.getCurrentItemValue(), "0")) + mEditor.setCurrentItemValue("0.785398"); + + if(!Ps::stricmp(mEditor.getCurrentItemName(), "TwistLimit")) + { + mEditor.gotoFirstChild(); + PxReal lower = PxReal(strtod(mEditor.getCurrentItemValue(), NULL)); + mEditor.gotoNextSibling(); + PxReal upper = PxReal(strtod(mEditor.getCurrentItemValue(), NULL)); + mEditor.leaveChild(); + if(lower>=upper) + { + mEditor.writePropertyIfNotEmpty("lower", "-0.785398"); + mEditor.writePropertyIfNotEmpty("upper", "0.785398"); + } + } + } + }; + + + for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item ) + { + //either src or dest could do the copy operation, it doesn't matter who does it. + RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) ); + + if ( strstr( newItem.liveObject.typeName, "PxCloth" ) || strstr( newItem.liveObject.typeName, "PxClothFabric" ) ) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Didn't suppot PxCloth upgrate from 3.2 to 3.3! "); + continue; + } + + if ( strstr( newItem.liveObject.typeName, "PxParticleSystem" ) || strstr( newItem.liveObject.typeName, "PxParticleFluid" ) ) + { + editor.setNode( *const_cast( newItem.descriptor ) ); + editor.renameProperty( "PositionBuffer", "Positions" ); + editor.renameProperty( "VelocityBuffer", "Velocities" ); + editor.renameProperty( "RestOffsetBuffer", "RestOffsets" ); + } + + if(strstr(newItem.liveObject.typeName, "PxPrismaticJoint" ) + || strstr(newItem.liveObject.typeName, "PxRevoluteJoint") + || strstr(newItem.liveObject.typeName, "PxSphericalJoint") + || strstr(newItem.liveObject.typeName, "PxD6Joint") + || strstr(newItem.liveObject.typeName, "PxArticulation")) + { + editor.setNode( *const_cast( newItem.descriptor ) ); + RenameSpringToStiffness(editor).traverse(); + } + + if(strstr(newItem.liveObject.typeName, "PxArticulation")) + { + editor.setNode( *const_cast( newItem.descriptor ) ); + UpdateArticulationSwingLimit(editor).traverse(); + } + + + + //now dest owns the new node. Collections share a single allocation pool, however, + //which will get destroyed when all the collections referencing it are destroyed themselves. + //Data on nodes is shared between nodes, but the node structure itself is allocated. + + dest->addCollectionItem( newItem ); + + } + editor.release(); + src.destroy(); + + return *dest; + } + + RepXCollection& RepXUpgrader::upgrade3_3CollectionTo3_4Collection(RepXCollection& src) + { + RepXCollection* dest = &src.createCollection("3.4.0"); + + for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item ) + { + if(strstr(item->liveObject.typeName, "PxTriangleMesh")) + { + PxRepXObject newMeshRepXObj("PxBVH33TriangleMesh", item->liveObject.serializable, item->liveObject.id); + XmlNode* newMeshNode = src.copyRepXNode( item->descriptor ); + newMeshNode->mName = "PxBVH33TriangleMesh"; + RepXCollectionItem newMeshItem(newMeshRepXObj, newMeshNode); + dest->addCollectionItem( newMeshItem ); + continue; + } + + RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) ); + dest->addCollectionItem( newItem ); + } + src.destroy(); + return *dest; + } + + RepXCollection& RepXUpgrader::upgrade3_4CollectionTo4_0Collection(RepXCollection& src) + { + RepXCollection* dest = &src.createCollection("4.0.0"); + + for (const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++item) + { + if (strstr(item->liveObject.typeName, "PxParticleFluid") || + strstr(item->liveObject.typeName, "PxParticleSystem") || + strstr(item->liveObject.typeName, "PxClothFabric") || + strstr(item->liveObject.typeName, "PxCloth")) + { + continue; + } + + RepXCollectionItem newItem(item->liveObject, src.copyRepXNode(item->descriptor)); + dest->addCollectionItem(newItem); + } + src.destroy(); + return *dest; + } + + RepXCollection& RepXUpgrader::upgradeCollection(RepXCollection& src) + { + const char* srcVersion = src.getVersion(); + if( safeStrEq( srcVersion, RepXCollection::getLatestVersion() )) + return src; + + typedef RepXCollection& (*UPGRADE_FUNCTION)(RepXCollection& src); + + struct Upgrade { const char* versionString; UPGRADE_FUNCTION upgradeFunction; }; + + static const Upgrade upgradeTable[] = + { + { "1.0", upgrade10CollectionTo3_1Collection }, + { "3.1", NULL }, + { "3.1.1", upgrade3_1CollectionTo3_2Collection }, + { "3.2.0", upgrade3_2CollectionTo3_3Collection }, + { "3.3.0", NULL }, + { "3.3.1", NULL }, + { "3.3.2", NULL }, + { "3.3.3", NULL }, + { "3.3.4", upgrade3_3CollectionTo3_4Collection }, + { "3.4.0", NULL }, + { "3.4.1", NULL }, + { "3.4.2", upgrade3_4CollectionTo4_0Collection } + }; //increasing order and complete + + const PxU32 upgradeTableSize = sizeof(upgradeTable)/sizeof(upgradeTable[0]); + + PxU32 repxVersion = UINT16_MAX; + + for (PxU32 i=0; i + class SimpleXmlWriterImpl : public SimpleXmlWriter + { + PxProfileAllocatorWrapper mWrapper; + TStreamType& mStream; + SimpleXmlWriterImpl( const SimpleXmlWriterImpl& inOther ); + SimpleXmlWriterImpl& operator=( const SimpleXmlWriterImpl& inOther ); + PxProfileArray mTags; + bool mTagOpen; + PxU32 mInitialTagDepth; + public: + + SimpleXmlWriterImpl( TStreamType& inStream, PxAllocatorCallback& inAllocator, PxU32 inInitialTagDepth = 0 ) + : mWrapper( inAllocator ) + , mStream( inStream ) + , mTags( mWrapper ) + , mTagOpen( false ) + , mInitialTagDepth( inInitialTagDepth ) + { + } + virtual ~SimpleXmlWriterImpl() + { + while( mTags.size() ) + endTag(); + } + PxU32 tabCount() { return mTags.size() + mInitialTagDepth; } + + void writeTabs( PxU32 inSize ) + { + inSize += mInitialTagDepth; + for ( PxU32 idx =0; idx < inSize; ++idx ) + mStream << "\t"; + } + void beginTag( const char* inTagname ) + { + closeTag(); + writeTabs(mTags.size()); + mTags.pushBack( inTagname ); + mStream << "<" << inTagname; + mTagOpen = true; + } + void addAttribute( const char* inName, const char* inValue ) + { + PX_ASSERT( mTagOpen ); + mStream << " " << inName << "=" << "\"" << inValue << "\""; + } + void closeTag(bool useNewline = true) + { + if ( mTagOpen ) + { + mStream << " " << ">"; + if (useNewline ) + mStream << "\n"; + } + mTagOpen = false; + } + void doEndOpenTag() + { + mStream << "" << "\n"; + } + void endTag() + { + PX_ASSERT( mTags.size() ); + if ( mTagOpen ) + mStream << " " << "/>" << "\n"; + else + { + writeTabs(mTags.size()-1); + doEndOpenTag(); + } + mTagOpen = false; + mTags.popBack(); + } + + static bool IsNormalizableWhitespace(char c) { return c == 0x9 || c == 0xA || c == 0xD; } + static bool IsValidXmlCharacter(char c) { return IsNormalizableWhitespace(c) || c >= 0x20; } + + void addContent( const char* inContent ) + { + closeTag(false); + //escape xml + for( ; *inContent; inContent++ ) + { + switch (*inContent) + { + case '<': + mStream << "<"; + break; + case '>': + mStream << ">"; + break; + case '&': + mStream << "&"; + break; + case '\'': + mStream << "'"; + break; + case '"': + mStream << """; + break; + default: + if (IsValidXmlCharacter(*inContent)) { + if (IsNormalizableWhitespace(*inContent)) + { + char s[32]; + Ps::snprintf(s, 32, "&#x%02X;", unsigned(*inContent)); + mStream << s; + } + else + mStream << *inContent; + } + break; + } + } + } + + void writeContentTag( const char* inTag, const char* inContent ) + { + beginTag( inTag ); + addContent( inContent ); + doEndOpenTag(); + mTags.popBack(); + } + void insertXml( const char* inXml ) + { + closeTag(); + mStream << inXml; + } + }; + + struct BeginTag + { + const char* mTagName; + BeginTag( const char* inTagName ) + : mTagName( inTagName ) { } + }; + + struct EndTag + { + EndTag() {} + }; + + struct Att + { + const char* mAttName; + const char* mAttValue; + Att( const char* inAttName, const char* inAttValue ) + : mAttName( inAttName ) + , mAttValue( inAttValue ) { } + }; + + struct Content + { + const char* mContent; + Content( const char* inContent ) + : mContent( inContent ) { } + }; + + + struct ContentTag + { + const char* mTagName; + const char* mContent; + ContentTag( const char* inTagName, const char* inContent ) + : mTagName( inTagName ) + , mContent( inContent ) { } + }; + + inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const BeginTag& inTag ) { inWriter.beginTag( inTag.mTagName ); return inWriter; } + inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const EndTag& inTag ) { PX_UNUSED(inTag); inWriter.endTag(); return inWriter; } + inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const Att& inTag ) { inWriter.addAttribute(inTag.mAttName, inTag.mAttValue); return inWriter; } + inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const Content& inTag ) { inWriter.addContent(inTag.mContent); return inWriter; } + inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const ContentTag& inTag ) { inWriter.writeContentTag(inTag.mTagName, inTag.mContent); return inWriter; } + + inline void writeProperty( SimpleXmlWriter& inWriter, MemoryBuffer& tempBuffer, const char* inPropName ) + { + PxU8 data = 0; + tempBuffer.write( &data, sizeof(PxU8) ); + inWriter.writeContentTag( inPropName, reinterpret_cast( tempBuffer.mBuffer ) ); + tempBuffer.clear(); + } + + template + inline void writeProperty( SimpleXmlWriter& inWriter, MemoryBuffer& tempBuffer, const char* inPropName, TDataType inValue ) + { + tempBuffer << inValue; + writeProperty( inWriter, tempBuffer, inPropName ); + } +} } +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h new file mode 100644 index 000000000..cfad99207 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h @@ -0,0 +1,197 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_DESERIALIZER_H +#define PX_XML_DESERIALIZER_H + +#include "SnXmlVisitorReader.h" + +namespace physx { namespace Sn { + + //Definitions needed internally in the Serializer headers. + template + struct Triangle + { + TTriIndexElem mIdx0; + TTriIndexElem mIdx1; + TTriIndexElem mIdx2; + Triangle( TTriIndexElem inIdx0 = 0, TTriIndexElem inIdx1 = 0, TTriIndexElem inIdx2 = 0) + : mIdx0( inIdx0 ) + , mIdx1( inIdx1 ) + , mIdx2( inIdx2 ) + { + } + }; + + struct XmlMemoryAllocateMemoryPoolAllocator + { + XmlMemoryAllocator* mAllocator; + XmlMemoryAllocateMemoryPoolAllocator( XmlMemoryAllocator* inAlloc ) : mAllocator( inAlloc ) {} + + PxU8* allocate( PxU32 inSize ) { return mAllocator->allocate( inSize ); } + void deallocate( PxU8* inMem ) { mAllocator->deallocate( inMem ); } + }; + + inline bool isEmpty(const char *s) + { + while (*s != '\0') + { + if (!isspace(*s)) + return false; + s++; + } + return true; + } + + inline void strtoLong( Triangle& ioDatatype,const char*& ioData ) + { + strto( ioDatatype.mIdx0, ioData ); + strto( ioDatatype.mIdx1, ioData ); + strto( ioDatatype.mIdx2, ioData ); + } + + inline void strtoLong( PxHeightFieldSample& ioDatatype,const char*& ioData ) + { + PxU32 tempData; + strto( tempData, ioData ); + if ( isBigEndian() ) + { + PxU32& theItem(tempData); + PxU32 theDest = 0; + PxU8* theReadPtr( reinterpret_cast< PxU8* >( &theItem ) ); + PxU8* theWritePtr( reinterpret_cast< PxU8* >( &theDest ) ); + //A height field sample is a 16 bit number + //followed by two bytes. + + //We write this out as a 32 bit integer, LE. + //Thus, on a big endian, we need to move the bytes + //around a bit. + //LE - 1 2 3 4 + //BE - 4 3 2 1 - after convert from xml number + //Correct BE - 2 1 3 4, just like LE but with the 16 number swapped + theWritePtr[0] = theReadPtr[2]; + theWritePtr[1] = theReadPtr[3]; + theWritePtr[2] = theReadPtr[1]; + theWritePtr[3] = theReadPtr[0]; + theItem = theDest; + } + ioDatatype = *reinterpret_cast( &tempData ); + } + + template + inline void readStridedFlagsProperty( XmlReader& ioReader, const char* inPropName, TDataType*& outData, PxU32& outStride, PxU32& outCount, XmlMemoryAllocator& inAllocator, + const PxU32ToName* inConversions) + { + const char* theSrcData; + outStride = sizeof( TDataType ); + outData = NULL; + outCount = 0; + if ( ioReader.read( inPropName, theSrcData ) ) + { + XmlMemoryAllocateMemoryPoolAllocator tempAllocator( &inAllocator ); + MemoryBufferBase tempBuffer( &tempAllocator ); + + if ( theSrcData ) + { + static PxU32 theCount = 0; + ++theCount; + char* theStartData = const_cast< char*>( copyStr( &tempAllocator, theSrcData ) ); + char* aData = strtok(theStartData, " \n"); + while( aData ) + { + TDataType tempValue; + stringToFlagsType( aData, inAllocator, tempValue, inConversions ); + aData = strtok(NULL," \n"); + tempBuffer.write( &tempValue, sizeof(TDataType) ); + } + outData = reinterpret_cast< TDataType* >( tempBuffer.mBuffer ); + outCount = tempBuffer.mWriteOffset / sizeof( TDataType ); + tempAllocator.deallocate( reinterpret_cast(theStartData) ); + } + tempBuffer.releaseBuffer(); + } + } + + template + inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, TDataType*& outData, PxU32& outStride, PxU32& outCount, XmlMemoryAllocator& inAllocator) + { + const char* theSrcData; + outStride = sizeof( TDataType ); + outData = NULL; + outCount = 0; + if ( ioReader.read( inPropName, theSrcData ) ) + { + XmlMemoryAllocateMemoryPoolAllocator tempAllocator( &inAllocator ); + MemoryBufferBase tempBuffer( &tempAllocator ); + + if ( theSrcData ) + { + static PxU32 theCount = 0; + ++theCount; + char* theStartData = const_cast< char*>( copyStr( &tempAllocator, theSrcData ) ); + const char* theData = theStartData; + while( !isEmpty(theData) ) + { + //These buffers are whitespace delimited. + TDataType theType; + strtoLong( theType, theData ); + tempBuffer.write( &theType, sizeof(theType) ); + } + outData = reinterpret_cast< TDataType* >( tempBuffer.mBuffer ); + outCount = tempBuffer.mWriteOffset / sizeof( TDataType ); + tempAllocator.deallocate( reinterpret_cast(theStartData) ); + } + tempBuffer.releaseBuffer(); + } + } + + template + inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, PxStridedData& ioData, PxU32& outCount, XmlMemoryAllocator& inAllocator) + { + TDataType* tempData = NULL; + readStridedBufferProperty( ioReader, inPropName, tempData, ioData.stride, outCount, inAllocator ); + ioData.data = tempData; + } + + template + inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, PxTypedStridedData& ioData, PxU32& outCount, XmlMemoryAllocator& inAllocator) + { + TDataType* tempData = NULL; + readStridedBufferProperty( ioReader, inPropName, tempData, ioData.stride, outCount, inAllocator ); + ioData.data = reinterpret_cast( tempData ); + } + + template + inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, PxBoundedData& ioData, XmlMemoryAllocator& inAllocator) + { + return readStridedBufferProperty( ioReader, inPropName, ioData, ioData.count, inAllocator ); + } + +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h new file mode 100644 index 000000000..261ab313a --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h @@ -0,0 +1,243 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_IMPL_H +#define PX_XML_IMPL_H + +#include "SnXmlMemoryPool.h" +#include "PsString.h" +#include "foundation/PxMemory.h" + +namespace physx { namespace Sn { + +typedef CMemoryPoolManager TMemoryPoolManager; + +namespace snXmlImpl { + + inline PxU32 strLen( const char* inStr ) + { + PxU32 len = 0; + if ( inStr ) + { + while ( *inStr ) + { + ++len; + ++inStr; + } + } + return len; + } +} + inline const char* copyStr( PxAllocatorCallback& inAllocator, const char* inStr ) + { + if ( inStr && *inStr ) + { + PxU32 theLen = snXmlImpl::strLen( inStr ); + //The memory will never be released by repx. If you want it released, you need to pass in a custom allocator + //that tracks all allocations and releases unreleased allocations yourself. + char* dest = reinterpret_cast( inAllocator.allocate( theLen + 1, "Repx::const char*", __FILE__, __LINE__ ) ); + PxMemCopy( dest, inStr, theLen ); + dest[theLen] = 0; + return dest; + } + return ""; + } + + template + inline const char* copyStr( TManagerType* inMgr, const char* inStr ) + { + if ( inStr && *inStr ) + { + PxU32 theLen = snXmlImpl::strLen( inStr ); + char* dest = reinterpret_cast( inMgr->allocate( theLen + 1 ) ); + PxMemCopy( dest, inStr, theLen ); + dest[theLen] = 0; + return dest; + } + return ""; + } + + inline void releaseStr( TMemoryPoolManager* inMgr, const char* inStr, PxU32 ) + { + if ( inStr && *inStr ) + { + inMgr->deallocate( reinterpret_cast< PxU8* >( const_cast( inStr ) ) ); + } + } + + inline void releaseStr( TMemoryPoolManager* inMgr, const char* inStr ) + { + if ( inStr && *inStr ) + { + PxU32 theLen = snXmlImpl::strLen( inStr ); + releaseStr( inMgr, inStr, theLen ); + } + } + + struct XmlNode + { + const char* mName; //Never released until all collections are released + const char* mData; //Never released until all collections are released + + XmlNode* mNextSibling; + XmlNode* mPreviousSibling; + XmlNode* mFirstChild; + XmlNode* mParent; + XmlNode( const XmlNode& ); + XmlNode& operator=( const XmlNode& ); + + PX_INLINE void initPtrs() + { + mNextSibling = NULL; + mPreviousSibling = NULL; + mFirstChild = NULL; + mParent = NULL; + } + + PX_INLINE XmlNode( const char* inName = "", const char* inData = "" ) + : mName( inName ) + , mData( inData ) + { initPtrs(); } + + void addChild( XmlNode* inItem ) + { + inItem->mParent = this; + if ( mFirstChild == NULL ) + mFirstChild = inItem; + else + { + XmlNode* theNode = mFirstChild; + //Follow the chain till the end. + while( theNode->mNextSibling != NULL ) + theNode = theNode->mNextSibling; + theNode->mNextSibling = inItem; + inItem->mPreviousSibling = theNode; + } + } + + PX_INLINE XmlNode* findChildByName( const char* inName ) + { + for ( XmlNode* theNode = mFirstChild; theNode; theNode = theNode->mNextSibling ) + { + XmlNode* theRepXNode = theNode; + if ( physx::shdfnd::stricmp( theRepXNode->mName, inName ) == 0 ) + return theNode; + } + return NULL; + } + + PX_INLINE void orphan() + { + if ( mParent ) + { + if ( mParent->mFirstChild == this ) + mParent->mFirstChild = mNextSibling; + } + if ( mPreviousSibling ) + mPreviousSibling->mNextSibling = mNextSibling; + if ( mNextSibling ) + mNextSibling->mPreviousSibling = mPreviousSibling; + if ( mFirstChild ) + mFirstChild->mParent = NULL; + initPtrs(); + } + }; + + inline XmlNode* allocateRepXNode( TMemoryPoolManager* inManager, const char* inName, const char* inData ) + { + XmlNode* retval = inManager->allocate(); + retval->mName = copyStr( inManager, inName ); + retval->mData = copyStr( inManager, inData ); + return retval; + } + + inline void release( TMemoryPoolManager* inManager, XmlNode* inNode ) + { + //We *don't* release the strings associated with the node + //because they could be shared. Instead, we just let them 'leak' + //in some sense, at least until the memory manager itself is deleted. + //DO NOT UNCOMMENT THE LINES BELOW!! + //releaseStr( inManager, inNode->mName ); + //releaseStr( inManager, inNode->mData ); + inManager->deallocate( inNode ); + } + + static PX_INLINE void releaseNodeAndChildren( TMemoryPoolManager* inManager, XmlNode* inNode ) + { + if ( inNode->mFirstChild ) + { + XmlNode* childNode( inNode->mFirstChild ); + while( childNode ) + { + XmlNode* _node( childNode ); + childNode = _node->mNextSibling; + releaseNodeAndChildren( inManager, _node ); + } + } + inNode->orphan(); + release( inManager, inNode ); + } + + static XmlNode* copyRepXNodeAndSiblings( TMemoryPoolManager* inManager, const XmlNode* inNode, XmlNode* inParent ); + + static XmlNode* copyRepXNode( TMemoryPoolManager* inManager, const XmlNode* inNode, XmlNode* inParent = NULL ) + { + XmlNode* newNode( allocateRepXNode( inManager, NULL, NULL ) ); + newNode->mName = inNode->mName; //Some light structural sharing + newNode->mData = inNode->mData; //Some light structural sharing + newNode->mParent = inParent; + if ( inNode->mFirstChild ) + newNode->mFirstChild = copyRepXNodeAndSiblings( inManager, inNode->mFirstChild, newNode ); + return newNode; + } + + static XmlNode* copyRepXNodeAndSiblings( TMemoryPoolManager* inManager, const XmlNode* inNode, XmlNode* inParent ) + { + XmlNode* sibling = inNode->mNextSibling; + if ( sibling ) sibling = copyRepXNodeAndSiblings( inManager, sibling, inParent ); + XmlNode* newNode = copyRepXNode( inManager, inNode, inParent ); + newNode->mNextSibling = sibling; + if ( sibling ) sibling->mPreviousSibling = newNode; + return newNode; + } + + inline bool isBigEndian() { int i = 1; return *(reinterpret_cast(&i))==0; } + + + struct NameStackEntry + { + const char* mName; + bool mOpen; + NameStackEntry( const char* nm ) : mName( nm ), mOpen( false ) {} + }; + + typedef PxProfileArray TNameStack; +} } + + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h new file mode 100644 index 000000000..a81e6141c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h @@ -0,0 +1,129 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_MEMORY_ALLOCATOR_H +#define PX_XML_MEMORY_ALLOCATOR_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx { + + class XmlMemoryAllocator + { + protected: + virtual ~XmlMemoryAllocator(){} + public: + virtual PxU8* allocate(PxU32 inSize) = 0; + virtual void deallocate( PxU8* inMem ) = 0; + virtual PxAllocatorCallback& getAllocator() = 0; + template + TObjectType* allocate() + { + TObjectType* retval = reinterpret_cast< TObjectType* >( allocate( sizeof( TObjectType ) ) ); + new (retval) TObjectType(); + return retval; + } + + template + TObjectType* allocate(const TArgType &arg) + { + TObjectType* retval = reinterpret_cast< TObjectType* >( allocate( sizeof( TObjectType ) ) ); + new (retval) TObjectType(arg); + return retval; + } + + template + void deallocate( TObjectType* inObject ) + { + deallocate( reinterpret_cast( inObject ) ); + } + template + inline TObjectType* batchAllocate(PxU32 inCount ) + { + TObjectType* retval = reinterpret_cast( allocate( sizeof(TObjectType) * inCount ) ); + for ( PxU32 idx = 0; idx < inCount; ++idx ) + { + new (retval + idx) TObjectType(); + } + return retval; + } + + template + inline TObjectType* batchAllocate(PxU32 inCount, const TArgType &arg) + { + TObjectType* retval = reinterpret_cast( allocate( sizeof(TObjectType) * inCount ) ); + for ( PxU32 idx = 0; idx < inCount; ++idx ) + { + new (retval + idx) TObjectType(arg); + } + return retval; + } + + + //Duplicate function definition for gcc. + template + inline TObjectType* batchAllocate(TObjectType*, PxU32 inCount ) + { + TObjectType* retval = reinterpret_cast( allocate( sizeof(TObjectType) * inCount ) ); + for ( PxU32 idx = 0; idx < inCount; ++idx ) + { + new (retval + idx) TObjectType(); + } + return retval; + } + }; + + struct XmlMemoryAllocatorImpl : public XmlMemoryAllocator + { + Sn::TMemoryPoolManager mManager; + + XmlMemoryAllocatorImpl( PxAllocatorCallback& inAllocator ) + : mManager( inAllocator ) + { + } + XmlMemoryAllocatorImpl &operator=(const XmlMemoryAllocatorImpl &); + virtual PxAllocatorCallback& getAllocator() + { + return mManager.getWrapper().getAllocator(); + } + + virtual PxU8* allocate(PxU32 inSize ) + { + if ( !inSize ) + return NULL; + + return mManager.allocate( inSize ); + } + virtual void deallocate( PxU8* inMem ) + { + if ( inMem ) + mManager.deallocate( inMem ); + } + }; +} +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h new file mode 100644 index 000000000..537ffb5ad --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h @@ -0,0 +1,373 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_MEMORYPOOL_H +#define PX_XML_MEMORYPOOL_H + +#include "foundation/PxAssert.h" +#include "PsArray.h" +#include "PxProfileAllocatorWrapper.h" + +namespace physx { + + using namespace physx::profile; + + /** + * Linked list used to store next node ptr. + */ + struct SMemPoolNode + { + SMemPoolNode* mNextNode; + }; + + /** + * Template arguments are powers of two. + * A very fast memory pool that is not memory efficient. It contains a vector of pointers + * to blocks of memory along with a linked list of free sections. All sections are + * of the same size so allocating memory is very fast, there isn't a linear search + * through blocks of indeterminate size. It also means there is memory wasted + * when objects aren't sized to powers of two. + */ + template + class CMemoryPool + { + typedef PxProfileArray TPxU8PtrList; + + PxProfileAllocatorWrapper& mWrapper; + TPxU8PtrList mAllMemory; + SMemPoolNode* mFirstFreeNode; + public: + CMemoryPool(PxProfileAllocatorWrapper& inWrapper) + : mWrapper( inWrapper ) + , mAllMemory( inWrapper ) + , mFirstFreeNode( NULL ) + {} + ~CMemoryPool() + { + TPxU8PtrList::ConstIterator theEnd = mAllMemory.end(); + for ( TPxU8PtrList::ConstIterator theIter = mAllMemory.begin(); + theIter != theEnd; + ++theIter ) + { + PxU8* thePtr = *theIter; + mWrapper.getAllocator().deallocate( thePtr ); + } + mAllMemory.clear(); + mFirstFreeNode = NULL; + } + //Using deallocated memory to hold the pointers to the next amount of memory. + PxU8* allocate() + { + if ( mFirstFreeNode ) + { + PxU8* retval = reinterpret_cast(mFirstFreeNode); + mFirstFreeNode = mFirstFreeNode->mNextNode; + return retval; + } + PxU32 itemSize = GetItemSize(); + PxU32 itemCount = 1 << TItemCount; + //No free nodes, make some more. + PxU8* retval = reinterpret_cast(mWrapper.getAllocator().allocate( itemCount * itemSize, "RepX fixed-size memory pool", __FILE__, __LINE__ )); + PxU8* dataPtr = retval + itemSize; + //Free extra chunks + for( PxU32 idx = 1; idx < itemCount; ++idx, dataPtr += itemSize ) + deallocate( dataPtr ); + mAllMemory.pushBack(retval); + return retval; + } + void deallocate( PxU8* inData ) + { + SMemPoolNode* nodePtr = reinterpret_cast(inData); + nodePtr->mNextNode = mFirstFreeNode; + mFirstFreeNode = nodePtr; + } + //We have to have at least a pointer's worth of memory + inline PxU32 GetItemSize() { return sizeof(SMemPoolNode) << TItemSize; } + }; + + typedef PxU32 TMemAllocSizeType; + + struct SVariableMemPoolNode : SMemPoolNode + { + TMemAllocSizeType mSize; + SVariableMemPoolNode* NextNode() { return static_cast< SVariableMemPoolNode* >( mNextNode ); } + }; + + /** + * Manages variable sized allocations. + * Keeps track of freed allocations in a insertion sorted + * list. Allocating new memory traverses the list linearly. + * This object will split nodes if the node is more than + * twice as large as the request memory allocation. + */ + class CVariableMemoryPool + { + typedef PxProfileHashMap TFreeNodeMap; + typedef PxProfileArray TPxU8PtrList; + PxProfileAllocatorWrapper& mWrapper; + TPxU8PtrList mAllMemory; + TFreeNodeMap mFreeNodeMap; + PxU32 mMinAllocationSize; + + CVariableMemoryPool &operator=(const CVariableMemoryPool &); + + public: + CVariableMemoryPool(PxProfileAllocatorWrapper& inWrapper, PxU32 inMinAllocationSize = 0x20 ) + : mWrapper( inWrapper ) + , mAllMemory( inWrapper ) + , mFreeNodeMap( inWrapper) + , mMinAllocationSize( inMinAllocationSize ) + {} + + ~CVariableMemoryPool() + { + TPxU8PtrList::ConstIterator theEnd = mAllMemory.end(); + for ( TPxU8PtrList::ConstIterator theIter = mAllMemory.begin(); + theIter != theEnd; + ++theIter ) + { + PxU8* thePtr = *theIter; + mWrapper.getAllocator().deallocate( thePtr ); + } + mAllMemory.clear(); + mFreeNodeMap.clear(); + } + PxU8* MarkMem( PxU8* inMem, TMemAllocSizeType inSize ) + { + PX_ASSERT( inSize >= sizeof( SVariableMemPoolNode ) ); + SVariableMemPoolNode* theMem = reinterpret_cast( inMem ); + theMem->mSize = inSize; + return reinterpret_cast< PxU8* >( theMem + 1 ); + } + //Using deallocated memory to hold the pointers to the next amount of memory. + PxU8* allocate( PxU32 size ) + { + //Ensure we can place the size of the memory at the start + //of the memory block. + //Kai: to reduce the size of hash map, the requested size is aligned to 128 bytes + PxU32 theRequestedSize = (size + sizeof(SVariableMemPoolNode) + 127) & ~127; + + TFreeNodeMap::Entry* entry = const_cast( mFreeNodeMap.find( theRequestedSize ) ); + if ( NULL != entry ) + { + SVariableMemPoolNode* theNode = entry->second; + PX_ASSERT( NULL != theNode ); + PX_ASSERT( theNode->mSize == theRequestedSize ); + entry->second = theNode->NextNode(); + if (entry->second == NULL) + mFreeNodeMap.erase( theRequestedSize ); + + return reinterpret_cast< PxU8* >( theNode + 1 ); + } + + if ( theRequestedSize < mMinAllocationSize ) + theRequestedSize = mMinAllocationSize; + + //No large enough free nodes, make some more. + PxU8* retval = reinterpret_cast(mWrapper.getAllocator().allocate( size_t(theRequestedSize), "RepX variable sized memory pool", __FILE__, __LINE__ )); + //If we allocated it, we free it. + mAllMemory.pushBack( retval ); + return MarkMem( retval, theRequestedSize ); + } + + //The size is stored at the beginning of the memory block. + void deallocate( PxU8* inData ) + { + SVariableMemPoolNode* theData = reinterpret_cast< SVariableMemPoolNode* >( inData ) - 1; + TMemAllocSizeType theSize = theData->mSize; + AddFreeMem( reinterpret_cast< PxU8* >( theData ), theSize ); + } + + void CheckFreeListInvariant( SVariableMemPoolNode* inNode ) + { + if ( inNode && inNode->mNextNode ) + { + PX_ASSERT( inNode->mSize <= inNode->NextNode()->mSize ); + } + } + + void AddFreeMem( PxU8* inMemory, TMemAllocSizeType inSize ) + { + PX_ASSERT( inSize >= sizeof( SVariableMemPoolNode ) ); + SVariableMemPoolNode* theNewNode = reinterpret_cast< SVariableMemPoolNode* >( inMemory ); + theNewNode->mNextNode = NULL; + theNewNode->mSize = inSize; + TFreeNodeMap::Entry* entry = const_cast( mFreeNodeMap.find( inSize ) ); + if (NULL != entry) + { + theNewNode->mNextNode = entry->second; + entry->second = theNewNode; + } + else + { + mFreeNodeMap.insert( inSize, theNewNode ); + } + } + }; + + /** + * The manager keeps a list of memory pools for different sizes of allocations. + * Anything too large simply gets allocated using the new operator. + * This doesn't mark the memory with the size of the allocated memory thus + * allowing much more efficient allocation of small items. For large enough + * allocations, it does mark the size. + * + * When using as a general memory manager, you need to wrap this class with + * something that actually does mark the returned allocation with the size + * of the allocation. + */ + class CMemoryPoolManager + { + CMemoryPoolManager &operator=(const CMemoryPoolManager &); + + public: + PxProfileAllocatorWrapper mWrapper; + + //CMemoryPool<0,8> m0ItemPool; + //CMemoryPool<1,8> m1ItemPool; + //CMemoryPool<2,8> m2ItemPool; + //CMemoryPool<3,8> m3ItemPool; + //CMemoryPool<4,8> m4ItemPool; + //CMemoryPool<5,8> m5ItemPool; + //CMemoryPool<6,8> m6ItemPool; + //CMemoryPool<7,8> m7ItemPool; + //CMemoryPool<8,8> m8ItemPool; + CVariableMemoryPool mVariablePool; + CMemoryPoolManager( PxAllocatorCallback& inAllocator ) + : mWrapper( inAllocator ) + //, m0ItemPool( mWrapper ) + //, m1ItemPool( mWrapper ) + //, m2ItemPool( mWrapper ) + //, m3ItemPool( mWrapper ) + //, m4ItemPool( mWrapper ) + //, m5ItemPool( mWrapper ) + //, m6ItemPool( mWrapper ) + //, m7ItemPool( mWrapper ) + //, m8ItemPool( mWrapper ) + , mVariablePool( mWrapper ) + { + } + PxProfileAllocatorWrapper& getWrapper() { return mWrapper; } + inline PxU8* allocate( PxU32 inSize ) + { + /* + if ( inSize <= m0ItemPool.GetItemSize() ) + return m0ItemPool.allocate(); + if ( inSize <= m1ItemPool.GetItemSize() ) + return m1ItemPool.allocate(); + if ( inSize <= m2ItemPool.GetItemSize() ) + return m2ItemPool.allocate(); + if ( inSize <= m3ItemPool.GetItemSize() ) + return m3ItemPool.allocate(); + if ( inSize <= m4ItemPool.GetItemSize() ) + return m4ItemPool.allocate(); + if ( inSize <= m5ItemPool.GetItemSize() ) + return m5ItemPool.allocate(); + if ( inSize <= m6ItemPool.GetItemSize() ) + return m6ItemPool.allocate(); + if ( inSize <= m7ItemPool.GetItemSize() ) + return m7ItemPool.allocate(); + if ( inSize <= m8ItemPool.GetItemSize() ) + return m8ItemPool.allocate(); + */ + return mVariablePool.allocate( inSize ); + } + inline void deallocate( PxU8* inMemory ) + { + if ( inMemory == NULL ) + return; + /* + if ( inSize <= m0ItemPool.GetItemSize() ) + m0ItemPool.deallocate(inMemory); + else if ( inSize <= m1ItemPool.GetItemSize() ) + m1ItemPool.deallocate(inMemory); + else if ( inSize <= m2ItemPool.GetItemSize() ) + m2ItemPool.deallocate(inMemory); + else if ( inSize <= m3ItemPool.GetItemSize() ) + m3ItemPool.deallocate(inMemory); + else if ( inSize <= m4ItemPool.GetItemSize() ) + m4ItemPool.deallocate(inMemory); + else if ( inSize <= m5ItemPool.GetItemSize() ) + m5ItemPool.deallocate(inMemory); + else if ( inSize <= m6ItemPool.GetItemSize() ) + m6ItemPool.deallocate(inMemory); + else if ( inSize <= m7ItemPool.GetItemSize() ) + m7ItemPool.deallocate(inMemory); + else if ( inSize <= m8ItemPool.GetItemSize() ) + m8ItemPool.deallocate(inMemory); + else + */ + mVariablePool.deallocate(inMemory); + } + /** + * allocate an object. Calls constructor on the new memory. + */ + template + inline TObjectType* allocate() + { + TObjectType* retval = reinterpret_cast( allocate( sizeof(TObjectType) ) ); + new (retval)TObjectType(); + return retval; + } + + /** + * deallocate an object calling the destructor on the object. + * This *must* be the concrete type, it cannot be a generic type. + */ + template + inline void deallocate( TObjectType* inObject ) + { + inObject->~TObjectType(); + deallocate( reinterpret_cast(inObject) ); + } + + /** + * allocate an object. Calls constructor on the new memory. + */ + template + inline TObjectType* BatchAllocate(PxU32 inCount ) + { + TObjectType* retval = reinterpret_cast( allocate( sizeof(TObjectType) * inCount ) ); + return retval; + } + + /** + * deallocate an object calling the destructor on the object. + * This *must* be the concrete type, it cannot be a generic type. + */ + template + inline void BatchDeallocate( TObjectType* inObject, PxU32 inCount ) + { + PX_UNUSED(inCount); + deallocate( reinterpret_cast(inObject) ); + } + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h new file mode 100644 index 000000000..a1e65dabd --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h @@ -0,0 +1,173 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_MEMORY_POOL_STREAMS_H +#define PX_XML_MEMORY_POOL_STREAMS_H + +#include "foundation/PxTransform.h" +#include "foundation/PxIO.h" +#include "SnXmlMemoryPool.h" +#include "CmPhysXCommon.h" + +namespace physx { + + template + struct XmlDefaultValue + { + bool force_compile_error; + }; + + +#define XML_DEFINE_DEFAULT_VALUE(type, defVal ) \ + template<> \ + struct XmlDefaultValue \ + { \ + type getDefaultValue() { return type(defVal); } \ + }; + + XML_DEFINE_DEFAULT_VALUE(PxU8, 0) + XML_DEFINE_DEFAULT_VALUE(PxI8, 0) + XML_DEFINE_DEFAULT_VALUE(PxU16, 0) + XML_DEFINE_DEFAULT_VALUE(PxI16, 0) + XML_DEFINE_DEFAULT_VALUE(PxU32, 0) + XML_DEFINE_DEFAULT_VALUE(PxI32, 0) + XML_DEFINE_DEFAULT_VALUE(PxU64, 0) + XML_DEFINE_DEFAULT_VALUE(PxI64, 0) + XML_DEFINE_DEFAULT_VALUE(PxF32, 0) + XML_DEFINE_DEFAULT_VALUE(PxF64, 0) + +#undef XML_DEFINE_DEFAULT_VALUE + + template<> + struct XmlDefaultValue + { + PxVec3 getDefaultValue() { return PxVec3( 0,0,0 ); } + }; + + template<> + struct XmlDefaultValue + { + PxTransform getDefaultValue() { return PxTransform(PxIdentity); } + }; + + template<> + struct XmlDefaultValue + { + PxQuat getDefaultValue() { return PxQuat(PxIdentity); } + }; + +/** + * Mapping of PxOutputStream to a memory pool manager. + * Allows write-then-read semantics of a set of + * data. Can safely write up to 4GB of data; then you + * will silently fail... + */ + +template +struct MemoryBufferBase : public PxOutputStream, public PxInputStream +{ + TAllocatorType* mManager; + mutable PxU32 mWriteOffset; + mutable PxU32 mReadOffset; + PxU8* mBuffer; + PxU32 mCapacity; + + + MemoryBufferBase( TAllocatorType* inManager ) + : mManager( inManager ) + , mWriteOffset( 0 ) + , mReadOffset( 0 ) + , mBuffer( NULL ) + , mCapacity( 0 ) + { + } + virtual ~MemoryBufferBase() + { + mManager->deallocate( mBuffer ); + } + PxU8* releaseBuffer() + { + clear(); + mCapacity = 0; + PxU8* retval(mBuffer); + mBuffer = NULL; + return retval; + } + void clear() + { + mWriteOffset = mReadOffset = 0; + } + + virtual PxU32 read(void* dest, PxU32 count) + { + bool fits = ( mReadOffset + count ) <= mWriteOffset; + PX_ASSERT( fits ); + if ( fits ) + { + PxMemCopy( dest, mBuffer + mReadOffset, count ); + mReadOffset += count; + return count; + } + return 0; + } + + inline void checkCapacity( PxU32 inNewCapacity ) + { + if ( mCapacity < inNewCapacity ) + { + PxU32 newCapacity = 32; + while( newCapacity < inNewCapacity ) + newCapacity = newCapacity << 1; + + PxU8* newData( mManager->allocate( newCapacity ) ); + if ( mWriteOffset ) + PxMemCopy( newData, mBuffer, mWriteOffset ); + mManager->deallocate( mBuffer ); + mBuffer = newData; + mCapacity = newCapacity; + } + } + + virtual PxU32 write(const void* src, PxU32 count) + { + checkCapacity( mWriteOffset + count ); + PxMemCopy( mBuffer + mWriteOffset, src, count ); + mWriteOffset += count; + return count; + } +}; + +class MemoryBuffer : public MemoryBufferBase +{ +public: + MemoryBuffer( CMemoryPoolManager* inManager ) : MemoryBufferBase( inManager ) {} +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h new file mode 100644 index 000000000..d5caa465b --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_READER_H +#define PX_XML_READER_H + +#include "foundation/PxSimpleTypes.h" +#include "extensions/PxRepXSimpleType.h" + +namespace physx { + namespace Sn { struct XmlNode; } + + /** + * Reader used to read data out of the repx format. + */ + class XmlReader + { + protected: + virtual ~XmlReader(){} + public: + /** Read a key-value pair out of the database */ + virtual bool read( const char* inName, const char*& outData ) = 0; + /** Read an object id out of the database */ + virtual bool read( const char* inName, PxSerialObjectId& outId ) = 0; + /** Goto a child element by name. That child becomes this reader's context */ + virtual bool gotoChild( const char* inName ) = 0; + /** Goto the first child regardless of name */ + virtual bool gotoFirstChild() = 0; + /** Goto the next sibling regardless of name */ + virtual bool gotoNextSibling() = 0; + /** Count all children of the current object */ + virtual PxU32 countChildren() = 0; + /** Get the name of the current item */ + virtual const char* getCurrentItemName() = 0; + /** Get the value of the current item */ + virtual const char* getCurrentItemValue() = 0; + /** Leave the current child */ + virtual bool leaveChild() = 0; + /** Get reader for the parental object */ + virtual XmlReader* getParentReader() = 0; + + /** + * Ensures we don't leave the reader in an odd state + * due to not leaving a given child + */ + virtual void pushCurrentContext() = 0; + /** Pop the current context back to where it during push*/ + virtual void popCurrentContext() = 0; + }; + + //Used when upgrading a repx collection + class XmlReaderWriter : public XmlReader + { + public: + //Clears the stack of nodes (push/pop current node reset) + //and sets the current node to inNode. + virtual void setNode( Sn::XmlNode& node ) = 0; + //If the child exists, add it. + //the either way goto that child. + virtual void addOrGotoChild( const char* name ) = 0; + //Value is copied into the collection, inValue has no further references + //to it. + virtual void setCurrentItemValue( const char* value ) = 0; + //Removes the child but does not release the char* name or char* data ptrs. + //Those pointers are never released and are shared among collections. + //Thus copying nodes is cheap and safe. + virtual bool removeChild( const char* name ) = 0; + virtual void release() = 0; + + bool renameProperty( const char* oldName, const char* newName ) + { + if ( gotoChild( oldName ) ) + { + const char* value = getCurrentItemValue(); + leaveChild(); + removeChild( oldName ); + addOrGotoChild( newName ); + setCurrentItemValue( value ); + leaveChild(); + return true; + } + return false; + } + bool readAndRemoveProperty( const char* name, const char*& outValue ) + { + bool retval = read( name, outValue ); + if ( retval ) removeChild( name ); + return retval; + } + + bool writePropertyIfNotEmpty( const char* name, const char* value ) + { + if ( value && *value ) + { + addOrGotoChild( name ); + setCurrentItemValue( value ); + leaveChild(); + return true; + } + return false; + } + }; + +} +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp new file mode 100644 index 000000000..dc9f7a87c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp @@ -0,0 +1,834 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#include "SnXmlImpl.h" +#include "PsHash.h" +#include "PsHashMap.h" +#include "SnSimpleXmlWriter.h" +#include "PsSort.h" +#include "PsFastXml.h" +#include "PsString.h" +#include "SnXmlMemoryPool.h" +#include "PxExtensionMetaDataObjects.h" +#include "SnXmlVisitorWriter.h" +#include "SnXmlVisitorReader.h" +#include "SnXmlMemoryAllocator.h" +#include "SnXmlStringToType.h" +#include "PsString.h" +#include "SnRepXCollection.h" +#include "SnRepXUpgrader.h" +#include "../SnSerializationRegistry.h" +#include "PsFoundation.h" +#include "CmCollection.h" + +using namespace physx; +using namespace Sn; + +using namespace physx::profile; //for the foundation wrapper system. + +namespace physx { namespace Sn { + + class XmlNodeWriter : public SimpleXmlWriter + { + XmlMemoryAllocatorImpl& mParseAllocator; + XmlNode* mCurrentNode; + XmlNode* mTopNode; + PxU32 mTabCount; + + public: + XmlNodeWriter( XmlMemoryAllocatorImpl& inAllocator, PxU32 inTabCount = 0 ) + : mParseAllocator( inAllocator ) + , mCurrentNode( NULL ) + , mTopNode( NULL ) + , mTabCount( inTabCount ) + {} + XmlNodeWriter& operator=(const XmlNodeWriter&); + virtual ~XmlNodeWriter(){} + void onNewNode( XmlNode* newNode ) + { + if ( mCurrentNode != NULL ) + mCurrentNode->addChild( newNode ); + if ( mTopNode == NULL ) + mTopNode = newNode; + mCurrentNode = newNode; + ++mTabCount; + } + + XmlNode* getTopNode() const { return mTopNode; } + + virtual void beginTag( const char* inTagname ) + { + onNewNode( allocateRepXNode( &mParseAllocator.mManager, inTagname, NULL ) ); + } + virtual void endTag() + { + if ( mCurrentNode ) + mCurrentNode = mCurrentNode->mParent; + if ( mTabCount ) + --mTabCount; + } + virtual void addAttribute( const char*, const char* ) + { + PX_ASSERT( false ); + } + virtual void writeContentTag( const char* inTag, const char* inContent ) + { + onNewNode( allocateRepXNode( &mParseAllocator.mManager, inTag, inContent ) ); + endTag(); + } + virtual void addContent( const char* inContent ) + { + if ( mCurrentNode->mData ) + releaseStr( &mParseAllocator.mManager, mCurrentNode->mData ); + mCurrentNode->mData = copyStr( &mParseAllocator.mManager, inContent ); + } + virtual PxU32 tabCount() { return mTabCount; } + }; + + struct XmlWriterImpl : public XmlWriter + { + PxU32 mTagDepth; + SimpleXmlWriter* mWriter; + MemoryBuffer* mMemBuffer; + + XmlWriterImpl( SimpleXmlWriter* inWriter, MemoryBuffer* inMemBuffer ) + : mTagDepth( 0 ) + , mWriter( inWriter ) + , mMemBuffer( inMemBuffer ) + { + } + ~XmlWriterImpl() + { + while( mTagDepth ) + { + --mTagDepth; + mWriter->endTag(); + } + } + virtual void write( const char* inName, const char* inData ) + { + mWriter->writeContentTag( inName, inData ); + } + virtual void write( const char* inName, const PxRepXObject& inLiveObject ) + { + (*mMemBuffer) << inLiveObject.id; + writeProperty( *mWriter, *mMemBuffer, inName ); + } + virtual void addAndGotoChild( const char* inName ) + { + mWriter->beginTag( inName ); + mTagDepth++; + } + virtual void leaveChild() + { + if ( mTagDepth ) + { + mWriter->endTag(); + --mTagDepth; + } + } + }; + + struct XmlParseArgs + { + XmlMemoryAllocatorImpl* mAllocator; + PxProfileArray* mCollection; + + XmlParseArgs( XmlMemoryAllocatorImpl* inAllocator + , PxProfileArray* inCollection) + : mAllocator( inAllocator ) + , mCollection( inCollection ) + { + } + }; + + struct XmlNodeReader : public XmlReaderWriter + { + PxProfileAllocatorWrapper mWrapper; + CMemoryPoolManager& mManager; + XmlNode* mCurrentNode; + XmlNode* mTopNode; + PxProfileArray mContext; + XmlNodeReader( XmlNode* inCurrentNode, PxAllocatorCallback& inAllocator, CMemoryPoolManager& nodePoolManager ) + : mWrapper( inAllocator ) + , mManager( nodePoolManager ) + , mCurrentNode( inCurrentNode ) + , mTopNode( inCurrentNode ) + , mContext( mWrapper ) + { + } + + //Does this node exist as data in the format. + virtual bool read( const char* inName, const char*& outData ) + { + XmlNode* theChild( mCurrentNode->findChildByName( inName ) ); + if ( theChild ) + { + outData = theChild->mData; + return outData && *outData; + } + return false; + } + + virtual bool read( const char* inName, PxSerialObjectId& outId ) + { + XmlNode* theChild( mCurrentNode->findChildByName( inName ) ); + if ( theChild ) + { + const char* theValue( theChild->mData ); + strto( outId, theValue ); + return true; + } + return false; + } + + virtual bool gotoChild( const char* inName ) + { + XmlNode* theChild( mCurrentNode->findChildByName( inName ) ); + if ( theChild ) + { + mCurrentNode =theChild; + return true; + } + return false; + } + virtual bool gotoFirstChild() + { + if ( mCurrentNode->mFirstChild ) + { + mCurrentNode = mCurrentNode->mFirstChild; + return true; + } + return false; + } + virtual bool gotoNextSibling() + { + if ( mCurrentNode->mNextSibling ) + { + mCurrentNode = mCurrentNode->mNextSibling; + return true; + } + return false; + } + virtual PxU32 countChildren() + { + PxU32 retval= 0; + for ( XmlNode* theChild = mCurrentNode->mFirstChild; theChild != NULL; theChild = theChild->mNextSibling ) + ++retval; + return retval; + } + virtual const char* getCurrentItemName() + { + return mCurrentNode->mName; + } + virtual const char* getCurrentItemValue() + { + return mCurrentNode->mData; + } + + virtual bool leaveChild() + { + if ( mCurrentNode != mTopNode && mCurrentNode->mParent ) + { + mCurrentNode = mCurrentNode->mParent; + return true; + } + return false; + } + + virtual void pushCurrentContext() + { + mContext.pushBack( mCurrentNode ); + } + virtual void popCurrentContext() + { + if ( mContext.size() ) + { + mCurrentNode = mContext.back(); + mContext.popBack(); + } + } + + virtual void setNode( XmlNode& inNode ) + { + mContext.clear(); + mCurrentNode = &inNode; + mTopNode = mCurrentNode; + } + + virtual XmlReader* getParentReader() + { + XmlReader* retval = PX_PLACEMENT_NEW((mWrapper.getAllocator().allocate(sizeof(XmlNodeReader), "createNodeEditor", __FILE__, __LINE__ )), XmlNodeReader) + ( mTopNode, mWrapper.getAllocator(), mManager ); + return retval; + } + + virtual void addOrGotoChild( const char* inName ) + { + if ( gotoChild( inName )== false ) + { + XmlNode* newNode = allocateRepXNode( &mManager, inName, NULL ); + mCurrentNode->addChild( newNode ); + mCurrentNode = newNode; + } + } + virtual void setCurrentItemValue( const char* inValue ) + { + mCurrentNode->mData = copyStr( &mManager, inValue ); + } + virtual bool removeChild( const char* name ) + { + XmlNode* theChild( mCurrentNode->findChildByName( name ) ); + if ( theChild ) + { + releaseNodeAndChildren( &mManager, theChild ); + return true; + } + return false; + } + virtual void release() { this->~XmlNodeReader(); mWrapper.getAllocator().deallocate(this); } + + private: + XmlNodeReader& operator=(const XmlNodeReader&); + }; + + PX_INLINE void freeNodeAndChildren( XmlNode* tempNode, TMemoryPoolManager& inManager ) + { + for( XmlNode* theNode = tempNode->mFirstChild; theNode != NULL; theNode = theNode->mNextSibling ) + freeNodeAndChildren( theNode, inManager ); + tempNode->orphan(); + release( &inManager, tempNode ); + } + + class XmlParser : public Ps::FastXml::Callback + { + XmlParseArgs mParseArgs; + //For parse time only allocations + XmlMemoryAllocatorImpl& mParseAllocator; + XmlNode* mCurrentNode; + XmlNode* mTopNode; + + public: + XmlParser( XmlParseArgs inArgs, XmlMemoryAllocatorImpl& inParseAllocator ) + : mParseArgs( inArgs ) + , mParseAllocator( inParseAllocator ) + , mCurrentNode( NULL ) + , mTopNode( NULL ) + { + } + + virtual ~XmlParser(){} + + virtual bool processComment(const char* /*comment*/) { return true; } + // 'element' is the name of the element that is being closed. + // depth is the recursion depth of this element. + // Return true to continue processing the XML file. + // Return false to stop processing the XML file; leaves the read pointer of the stream right after this close tag. + // The bool 'isError' indicates whether processing was stopped due to an error, or intentionally canceled early. + virtual bool processClose(const char* /*element*/,physx::PxU32 /*depth*/,bool& /*isError*/) + { + mCurrentNode = mCurrentNode->mParent; + return true; + } + + // return true to continue processing the XML document, false to skip. + virtual bool processElement( + const char *elementName, // name of the element + const char *elementData, // element data, null if none + const Ps::FastXml::AttributePairs& attr, // attributes + PxI32 /*lineno*/) + { + XmlNode* newNode = allocateRepXNode( &mParseAllocator.mManager, elementName, elementData ); + if ( mCurrentNode ) + mCurrentNode->addChild( newNode ); + mCurrentNode = newNode; + //Add the elements as children. + for( PxI32 item = 0; item < attr.getNbAttr(); item ++ ) + { + XmlNode* node = allocateRepXNode( &mParseAllocator.mManager, attr.getKey(PxU32(item)), attr.getValue(PxU32(item)) ); + mCurrentNode->addChild( node ); + } + if ( mTopNode == NULL ) mTopNode = newNode; + return true; + } + + XmlNode* getTopNode() { return mTopNode; } + + virtual void * allocate(PxU32 size) + { + if ( size ) + return mParseAllocator.allocate(size); + return NULL; + } + virtual void deallocate(void *mem) + { + if ( mem ) + mParseAllocator.deallocate(reinterpret_cast(mem)); + } + + private: + XmlParser& operator=(const XmlParser&); + }; + + struct RepXCollectionSharedData + { + PxProfileAllocatorWrapper mWrapper; + XmlMemoryAllocatorImpl mAllocator; + PxU32 mRefCount; + + RepXCollectionSharedData( PxAllocatorCallback& inAllocator ) + : mWrapper( inAllocator ) + , mAllocator( inAllocator ) + , mRefCount( 0 ) + { + } + ~RepXCollectionSharedData() {} + + void addRef() { ++mRefCount;} + void release() + { + if ( mRefCount ) --mRefCount; + if ( !mRefCount ) { this->~RepXCollectionSharedData(); mWrapper.getAllocator().deallocate(this);} + } + }; + + struct SharedDataPtr + { + RepXCollectionSharedData* mData; + SharedDataPtr( RepXCollectionSharedData* inData ) + : mData( inData ) + { + mData->addRef(); + } + SharedDataPtr( const SharedDataPtr& inOther ) + : mData( inOther.mData ) + { + mData->addRef(); + } + SharedDataPtr& operator=( const SharedDataPtr& inOther ); + ~SharedDataPtr() + { + mData->release(); + mData = NULL; + } + RepXCollectionSharedData* operator->() { return mData; } + const RepXCollectionSharedData* operator->() const { return mData; } + }; + + class RepXCollectionImpl : public RepXCollection, public Ps::UserAllocated + { + SharedDataPtr mSharedData; + + XmlMemoryAllocatorImpl& mAllocator; + PxSerializationRegistry& mSerializationRegistry; + PxProfileArray mCollection; + TMemoryPoolManager mSerializationManager; + MemoryBuffer mPropertyBuffer; + PxTolerancesScale mScale; + PxVec3 mUpVector; + const char* mVersionStr; + PxCollection* mPxCollection; + + public: + RepXCollectionImpl( PxSerializationRegistry& inRegistry, PxAllocatorCallback& inAllocator, PxCollection& inPxCollection ) + : mSharedData( &PX_NEW_REPX_SERIALIZER( RepXCollectionSharedData )) + , mAllocator( mSharedData->mAllocator ) + , mSerializationRegistry( inRegistry ) + , mCollection( mSharedData->mWrapper ) + , mSerializationManager( inAllocator ) + , mPropertyBuffer( &mSerializationManager ) + , mUpVector( 0,0,0 ) + , mVersionStr( getLatestVersion() ) + , mPxCollection( &inPxCollection ) + { + memset( &mScale, 0, sizeof( PxTolerancesScale ) ); + PX_ASSERT( mScale.isValid() == false ); + } + + RepXCollectionImpl( PxSerializationRegistry& inRegistry, const RepXCollectionImpl& inSrc, const char* inNewVersion ) + : mSharedData( inSrc.mSharedData ) + , mAllocator( mSharedData->mAllocator ) + , mSerializationRegistry( inRegistry ) + , mCollection( mSharedData->mWrapper ) + , mSerializationManager( mSharedData->mWrapper.getAllocator() ) + , mPropertyBuffer( &mSerializationManager ) + , mScale( inSrc.mScale ) + , mUpVector( inSrc.mUpVector ) + , mVersionStr( inNewVersion ) + , mPxCollection( NULL ) + { + } + + virtual ~RepXCollectionImpl() + { + PxU32 numItems = mCollection.size(); + for ( PxU32 idx = 0; idx < numItems; ++idx ) + { + XmlNode* theNode = mCollection[idx].descriptor; + releaseNodeAndChildren( &mAllocator.mManager, theNode ); + } + } + RepXCollectionImpl& operator=(const RepXCollectionImpl&); + + virtual void destroy() + { + PxProfileAllocatorWrapper tempWrapper( mSharedData->mWrapper.getAllocator() ); + this->~RepXCollectionImpl(); + tempWrapper.getAllocator().deallocate(this); + } + + virtual void setTolerancesScale(const PxTolerancesScale& inScale) { mScale = inScale; } + virtual PxTolerancesScale getTolerancesScale() const { return mScale; } + virtual void setUpVector( const PxVec3& inUpVector ) { mUpVector = inUpVector; } + virtual PxVec3 getUpVector() const { return mUpVector; } + + + PX_INLINE RepXCollectionItem findItemBySceneItem( const PxRepXObject& inObject ) const + { + //See if the object is in the collection + for ( PxU32 idx =0; idx < mCollection.size(); ++idx ) + if ( mCollection[idx].liveObject.serializable == inObject.serializable ) + return mCollection[idx]; + return RepXCollectionItem(); + } + + virtual RepXAddToCollectionResult addRepXObjectToCollection( const PxRepXObject& inObject, PxCollection* inCollection, PxRepXInstantiationArgs& inArgs ) + { + PX_ASSERT( inObject.serializable ); + PX_ASSERT( inObject.id ); + if ( inObject.serializable == NULL || inObject.id == 0 ) + return RepXAddToCollectionResult( RepXAddToCollectionResult::InvalidParameters ); + + PxRepXSerializer* theSerializer = mSerializationRegistry.getRepXSerializer( inObject.typeName ); + if ( theSerializer == NULL ) + return RepXAddToCollectionResult( RepXAddToCollectionResult::SerializerNotFound ); + + RepXCollectionItem existing = findItemBySceneItem( inObject ); + if ( existing.liveObject.serializable ) + return RepXAddToCollectionResult( RepXAddToCollectionResult::AlreadyInCollection, existing.liveObject.id ); + + XmlNodeWriter theXmlWriter( mAllocator, 1 ); + XmlWriterImpl theRepXWriter( &theXmlWriter, &mPropertyBuffer ); + { + SimpleXmlWriter::STagWatcher theWatcher( theXmlWriter, inObject.typeName ); + writeProperty( theXmlWriter, mPropertyBuffer, "Id", inObject.id ); + theSerializer->objectToFile( inObject, inCollection, theRepXWriter, mPropertyBuffer,inArgs ); + } + mCollection.pushBack( RepXCollectionItem( inObject, theXmlWriter.getTopNode() ) ); + return RepXAddToCollectionResult( RepXAddToCollectionResult::Success, inObject.id ); + } + + virtual bool instantiateCollection( PxRepXInstantiationArgs& inArgs, PxCollection& inCollection ) + { + for ( PxU32 idx =0; idx < mCollection.size(); ++idx ) + { + RepXCollectionItem theItem( mCollection[idx] ); + PxRepXSerializer* theSerializer = mSerializationRegistry.getRepXSerializer( theItem.liveObject.typeName ); + if (theSerializer ) + { + XmlNodeReader theReader( theItem.descriptor, mAllocator.getAllocator(), mAllocator.mManager ); + XmlMemoryAllocatorImpl instantiationAllocator( mAllocator.getAllocator() ); + PxRepXObject theLiveObject = theSerializer->fileToObject( theReader, instantiationAllocator, inArgs, &inCollection ); + if (theLiveObject.isValid()) + { + const PxBase* s = reinterpret_cast( theLiveObject.serializable ) ; + inCollection.add( *const_cast(s), PxSerialObjectId( theItem.liveObject.id )); + } + else + return false; + } + else + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PxSerialization::createCollectionFromXml: " + "PxRepXSerializer missing for type %s", theItem.liveObject.typeName); + return false; + } + } + + return true; + } + + void saveXmlNode( XmlNode* inNode, SimpleXmlWriter& inWriter ) + { + XmlNode* theNode( inNode ); + if ( theNode->mData && *theNode->mData && theNode->mFirstChild == NULL ) + inWriter.writeContentTag( theNode->mName, theNode->mData ); + else + { + inWriter.beginTag( theNode->mName ); + if ( theNode->mData && *theNode->mData ) + inWriter.addContent( theNode->mData ); + for ( XmlNode* theChild = theNode->mFirstChild; + theChild != NULL; + theChild = theChild->mNextSibling ) + saveXmlNode( theChild, inWriter ); + inWriter.endTag(); + } + } + + virtual void save( PxOutputStream& inStream ) + { + SimpleXmlWriterImpl theWriter( inStream, mAllocator.getAllocator() ); + theWriter.beginTag( "PhysX30Collection" ); + theWriter.addAttribute( "version", mVersionStr ); + { + XmlWriterImpl theRepXWriter( &theWriter, &mPropertyBuffer ); + writeProperty( theWriter, mPropertyBuffer, "UpVector", mUpVector ); + theRepXWriter.addAndGotoChild( "Scale" ); + writeAllProperties( &mScale, theRepXWriter, mPropertyBuffer, *mPxCollection); + theRepXWriter.leaveChild(); + } + for ( PxU32 idx =0; idx < mCollection.size(); ++idx ) + { + RepXCollectionItem theItem( mCollection[idx] ); + XmlNode* theNode( theItem.descriptor ); + saveXmlNode( theNode, theWriter ); + } + } + + void load( PxInputData& inFileBuf, SerializationRegistry& s ) + { + inFileBuf.seek(0); + XmlParser theParser( XmlParseArgs( &mAllocator, &mCollection ), mAllocator ); + Ps::FastXml* theFastXml = Ps::createFastXml( &theParser ); + theFastXml->processXml( inFileBuf ); + XmlNode* theTopNode = theParser.getTopNode(); + if ( theTopNode != NULL ) + { + { + + XmlMemoryAllocatorImpl instantiationAllocator( mAllocator.getAllocator() ); + XmlNodeReader theReader( theTopNode, mAllocator.getAllocator(), mAllocator.mManager ); + readProperty( theReader, "UpVector", mUpVector ); + if ( theReader.gotoChild( "Scale" ) ) + { + readAllProperties( PxRepXInstantiationArgs( s.getPhysics() ), theReader, &mScale, instantiationAllocator, *mPxCollection); + theReader.leaveChild(); + } + const char* verStr = NULL; + if ( theReader.read( "version", verStr ) ) + mVersionStr = verStr; + } + for ( XmlNode* theChild = theTopNode->mFirstChild; + theChild != NULL; + theChild = theChild->mNextSibling ) + { + if ( physx::shdfnd::stricmp( theChild->mName, "scale" ) == 0 + || physx::shdfnd::stricmp( theChild->mName, "version" ) == 0 + || physx::shdfnd::stricmp( theChild->mName, "upvector" ) == 0 ) + continue; + XmlNodeReader theReader( theChild, mAllocator.getAllocator(), mAllocator.mManager ); + PxRepXObject theObject; + theObject.typeName = theChild->mName; + theObject.serializable = NULL; + PxSerialObjectId theId = 0; + theReader.read( "Id", theId ); + theObject.id = theId; + mCollection.pushBack( RepXCollectionItem( theObject, theChild ) ); + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Cannot parse any object from the input buffer, please check the input repx data."); + } + theFastXml->release(); + } + + virtual const char* getVersion() { return mVersionStr; } + + virtual const RepXCollectionItem* begin() const + { + return mCollection.begin(); + } + virtual const RepXCollectionItem* end() const + { + return mCollection.end(); + } + + virtual RepXCollection& createCollection( const char* inVersionStr ) + { + PxAllocatorCallback& allocator = mSharedData->mWrapper.getAllocator(); + RepXCollectionImpl* retval = PX_PLACEMENT_NEW((allocator.allocate(sizeof(RepXCollectionImpl), "createCollection", __FILE__, __LINE__ )), RepXCollectionImpl) ( mSerializationRegistry, *this, inVersionStr ); + + return *retval; + } + + //Performs a deep copy of the repx node. + virtual XmlNode* copyRepXNode( const XmlNode* srcNode ) + { + return physx::Sn::copyRepXNode( &mAllocator.mManager, srcNode ); + } + + virtual void addCollectionItem( RepXCollectionItem inItem ) + { + mCollection.pushBack( inItem ); + } + + virtual PxAllocatorCallback& getAllocator() { return mSharedData->mAllocator.getAllocator(); } + //Create a new repx node with this name. Its value is unset. + virtual XmlNode& createRepXNode( const char* name ) + { + XmlNode* newNode = allocateRepXNode( &mSharedData->mAllocator.mManager, name, NULL ); + return *newNode; + } + + //Release this when finished. + virtual XmlReaderWriter& createNodeEditor() + { + PxAllocatorCallback& allocator = mSharedData->mWrapper.getAllocator(); + XmlReaderWriter* retval = PX_PLACEMENT_NEW((allocator.allocate(sizeof(XmlNodeReader), "createNodeEditor", __FILE__, __LINE__ )), XmlNodeReader) ( NULL, allocator, mAllocator.mManager ); + return *retval; + } + }; + + const char* RepXCollection::getLatestVersion() + { +#define TOSTR_(x) #x +#define CONCAT_(a, b, c) TOSTR_(a.##b.##c) +#define MAKE_VERSION_STR(a,b,c) CONCAT_(a, b, c) + + return MAKE_VERSION_STR(PX_PHYSICS_VERSION_MAJOR,PX_PHYSICS_VERSION_MINOR,PX_PHYSICS_VERSION_BUGFIX); + + } + + static RepXCollection* create(SerializationRegistry& s, PxAllocatorCallback& inAllocator, PxCollection& inCollection ) + { + return PX_PLACEMENT_NEW((inAllocator.allocate(sizeof(RepXCollectionImpl), "RepXCollection::create", __FILE__, __LINE__ )), RepXCollectionImpl) ( s, inAllocator, inCollection ); + } + + static RepXCollection* create(SerializationRegistry& s, PxInputData &data, PxAllocatorCallback& inAllocator, PxCollection& inCollection ) + { + RepXCollectionImpl* theCollection = static_cast( create(s, inAllocator, inCollection ) ); + theCollection->load( data, s ); + return theCollection; + } +} + + bool PxSerialization::serializeCollectionToXml( PxOutputStream& outputStream, PxCollection& collection, PxSerializationRegistry& sr, PxCooking* cooking, const PxCollection* externalRefs, PxXmlMiscParameter* inArgs ) + { + if( !PxSerialization::isSerializable(collection, sr, const_cast(externalRefs)) ) + return false; + + bool bRet = true; + + SerializationRegistry& sn = static_cast(sr); + PxRepXInstantiationArgs args( sn.getPhysics(), cooking ); + + PxCollection* tmpCollection = PxCreateCollection(); + PX_ASSERT(tmpCollection); + + tmpCollection->add( collection ); + if(externalRefs) + { + tmpCollection->add(*const_cast(externalRefs)); + } + + PxAllocatorCallback& allocator = PxGetFoundation().getAllocatorCallback(); + Sn::RepXCollection* theRepXCollection = Sn::create(sn, allocator, *tmpCollection ); + + if(inArgs != NULL) + { + theRepXCollection->setTolerancesScale(inArgs->scale); + theRepXCollection->setUpVector(inArgs->upVector); + } + + PxU32 nbObjects = collection.getNbObjects(); + if( nbObjects ) + { + sortCollection( static_cast(collection), sn, true); + + for( PxU32 i = 0; i < nbObjects; i++ ) + { + PxBase& s = collection.getObject(i); + if( PxConcreteType::eSHAPE == s.getConcreteType() ) + { + PxShape& shape = static_cast(s); + if( shape.isExclusive() ) + continue; + } + + PxSerialObjectId id = collection.getId(s); + if(id == PX_SERIAL_OBJECT_ID_INVALID) + id = static_cast( reinterpret_cast( &s )); + + PxRepXObject ro = PxCreateRepXObject( &s, id ); + if ( ro.serializable == NULL || ro.id == 0 ) + { + bRet = false; + break; + } + + theRepXCollection->addRepXObjectToCollection( ro, tmpCollection, args ); + } + } + tmpCollection->release(); + + theRepXCollection->save(outputStream); + theRepXCollection->destroy(); + + + return bRet; + } + + PxCollection* PxSerialization::createCollectionFromXml(PxInputData& inputData, PxCooking& cooking, PxSerializationRegistry& sr, const PxCollection* externalRefs, PxStringTable* stringTable, PxXmlMiscParameter* outArgs) + { + SerializationRegistry& sn = static_cast(sr); + PxCollection* collection = PxCreateCollection(); + PX_ASSERT(collection); + + if( externalRefs ) + collection->add(*const_cast(externalRefs)); + + PxAllocatorCallback& allocator = PxGetFoundation().getAllocatorCallback(); + Sn::RepXCollection* theRepXCollection = Sn::create(sn, inputData, allocator, *collection); + theRepXCollection = &Sn::RepXUpgrader::upgradeCollection( *theRepXCollection ); + + PxRepXInstantiationArgs args( sn.getPhysics(), &cooking, stringTable ); + if( !theRepXCollection->instantiateCollection(args, *collection) ) + { + collection->release(); + theRepXCollection->destroy(); + return NULL; + } + + if( externalRefs ) + collection->remove(*const_cast(externalRefs)); + + if(outArgs != NULL) + { + outArgs->upVector = theRepXCollection->getUpVector(); + outArgs->scale = theRepXCollection->getTolerancesScale(); + } + + theRepXCollection->destroy(); + + return collection; + } +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h new file mode 100644 index 000000000..caf71e286 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_SERIALIZER_H +#define PX_XML_SERIALIZER_H + +#include "PxExtensionMetaDataObjects.h" +#include "SnXmlVisitorWriter.h" + +namespace physx { + +namespace Sn { + + void writeHeightFieldSample( PxOutputStream& inStream, const PxHeightFieldSample& inSample ) + { + PxU32 retval = 0; + PxU8* writePtr( reinterpret_cast< PxU8*>( &retval ) ); + const PxU8* inPtr( reinterpret_cast( &inSample ) ); + if ( isBigEndian() ) + { + //Height field samples are a + //16 bit integer followed by two bytes. + //right now, data is 2 1 3 4 + //We need a 32 bit integer that + //when read in by a LE system is 4 3 2 1. + //Thus, we need a BE integer that looks like: + //4 3 2 1 + + writePtr[0] = inPtr[3]; + writePtr[1] = inPtr[2]; + writePtr[2] = inPtr[0]; + writePtr[3] = inPtr[1]; + } + else + { + writePtr[0] = inPtr[0]; + writePtr[1] = inPtr[1]; + writePtr[2] = inPtr[2]; + writePtr[3] = inPtr[3]; + } + inStream << retval; + } + + + + template + inline void writeStridedBufferProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, const void* inData, PxU32 inStride, PxU32 inCount, PxU32 inItemsPerLine, TWriteOperator inOperator ) + { + PX_ASSERT( inStride == 0 || inStride == sizeof( TDataType ) ); + PX_UNUSED( inStride ); + writeBuffer( writer, tempBuffer + , inItemsPerLine, reinterpret_cast( inData ) + , inCount, inPropName, inOperator ); + } + + template + inline void writeStridedBufferProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, const PxStridedData& inData, PxU32 inCount, PxU32 inItemsPerLine, TWriteOperator inOperator ) + { + writeStridedBufferProperty( writer, tempBuffer, inPropName, inData.data, inData.stride, inCount, inItemsPerLine, inOperator ); + } + + template + inline void writeStridedBufferProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, const PxTypedStridedData& inData, PxU32 inCount, PxU32 inItemsPerLine, TWriteOperator inOperator ) + { + writeStridedBufferProperty( writer, tempBuffer, inPropName, inData.data, inData.stride, inCount, inItemsPerLine, inOperator ); + } + + template + inline void writeStridedBufferProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, const PxBoundedData& inData, PxU32 inItemsPerLine, TWriteOperator inWriteOperator ) + { + writeStridedBufferProperty( writer, tempBuffer, inPropName, inData, inData.count, inItemsPerLine, inWriteOperator ); + } + + template + inline void writeStridedBufferProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, PxStrideIterator& inData, PxU32 inCount, PxU32 inItemsPerLine, TWriteOperator inWriteOperator ) + { + writeStrideBuffer(writer, tempBuffer + , inItemsPerLine, inData, PtrAccess + , inCount, inPropName, inData.stride(), inWriteOperator ); + } + + template + inline void writeStridedFlagsProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, PxStrideIterator& inData, PxU32 inCount, PxU32 inItemsPerLine, const PxU32ToName* inTable ) + { + writeStrideFlags(writer, tempBuffer + , inItemsPerLine, inData, PtrAccess + , inCount, inPropName, inTable ); + } + +} +} +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h new file mode 100644 index 000000000..3de9049eb --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_SIMPLEXMLWRITER_H +#define PX_SIMPLEXMLWRITER_H + +#include "PsArray.h" +#include "SnXmlMemoryPoolStreams.h" +#include "CmPhysXCommon.h" + +namespace physx { namespace Sn { + class XmlWriter + { + public: + + struct STagWatcher + { + typedef XmlWriter TXmlWriterType; + TXmlWriterType& mWriter; + STagWatcher( const STagWatcher& inOther ); + STagWatcher& operator-( const STagWatcher& inOther ); + STagWatcher( TXmlWriterType& inWriter, const char* inTagName ) + : mWriter( inWriter ) + { + mWriter.beginTag( inTagName ); + } + ~STagWatcher() { mWriter.endTag(); } + }; + + virtual ~XmlWriter(){} + virtual void beginTag( const char* inTagname ) = 0; + virtual void endTag() = 0; + virtual void addAttribute( const char* inName, const char* inValue ) = 0; + virtual void writeContentTag( const char* inTag, const char* inContent ) = 0; + virtual void addContent( const char* inContent ) = 0; + virtual PxU32 tabCount() = 0; + }; + + template + class XmlWriterImpl : public XmlWriter + { + PxProfileAllocatorWrapper mWrapper; + TStreamType& mStream; + XmlWriterImpl( const XmlWriterImpl& inOther ); + XmlWriterImpl& operator=( const XmlWriterImpl& inOther ); + PxProfileArray mTags; + bool mTagOpen; + PxU32 mInitialTagDepth; + public: + + XmlWriterImpl( TStreamType& inStream, PxAllocatorCallback& inAllocator, PxU32 inInitialTagDepth = 0 ) + : mWrapper( inAllocator ) + , mStream( inStream ) + , mTags( mWrapper ) + , mTagOpen( false ) + , mInitialTagDepth( inInitialTagDepth ) + { + } + virtual ~XmlWriterImpl() + { + while( mTags.size() ) + endTag(); + } + PxU32 tabCount() { return mTags.size() + mInitialTagDepth; } + + void writeTabs( PxU32 inSize ) + { + inSize += mInitialTagDepth; + for ( PxU32 idx =0; idx < inSize; ++idx ) + mStream << "\t"; + } + void beginTag( const char* inTagname ) + { + closeTag(); + writeTabs(mTags.size()); + mTags.pushBack( inTagname ); + mStream << "<" << inTagname; + mTagOpen = true; + } + void addAttribute( const char* inName, const char* inValue ) + { + PX_ASSERT( mTagOpen ); + mStream << " " << inName << "=" << "\"" << inValue << "\""; + } + void closeTag(bool useNewline = true) + { + if ( mTagOpen ) + { + mStream << " " << ">"; + if (useNewline ) + mStream << "\n"; + } + mTagOpen = false; + } + void doEndOpenTag() + { + mStream << "" << "\n"; + } + void endTag() + { + PX_ASSERT( mTags.size() ); + if ( mTagOpen ) + mStream << " " << "/>" << "\n"; + else + { + writeTabs(mTags.size()-1); + doEndOpenTag(); + } + mTagOpen = false; + mTags.popBack(); + } + void addContent( const char* inContent ) + { + closeTag(false); + mStream << inContent; + } + void writeContentTag( const char* inTag, const char* inContent ) + { + beginTag( inTag ); + addContent( inContent ); + doEndOpenTag(); + mTags.popBack(); + } + void insertXml( const char* inXml ) + { + closeTag(); + mStream << inXml; + } + }; + + struct BeginTag + { + const char* mTagName; + BeginTag( const char* inTagName ) + : mTagName( inTagName ) { } + }; + + struct EndTag + { + EndTag() {} + }; + + struct Att + { + const char* mAttName; + const char* mAttValue; + Att( const char* inAttName, const char* inAttValue ) + : mAttName( inAttName ) + , mAttValue( inAttValue ) { } + }; + + struct Content + { + const char* mContent; + Content( const char* inContent ) + : mContent( inContent ) { } + }; + + + struct ContentTag + { + const char* mTagName; + const char* mContent; + ContentTag( const char* inTagName, const char* inContent ) + : mTagName( inTagName ) + , mContent( inContent ) { } + }; + + inline XmlWriter& operator<<( XmlWriter& inWriter, const BeginTag& inTag ) { inWriter.beginTag( inTag.mTagName ); return inWriter; } + inline XmlWriter& operator<<( XmlWriter& inWriter, const EndTag& inTag ) { inWriter.endTag(); return inWriter; } + inline XmlWriter& operator<<( XmlWriter& inWriter, const Att& inTag ) { inWriter.addAttribute(inTag.mAttName, inTag.mAttValue); return inWriter; } + inline XmlWriter& operator<<( XmlWriter& inWriter, const Content& inTag ) { inWriter.addContent(inTag.mContent); return inWriter; } + inline XmlWriter& operator<<( XmlWriter& inWriter, const ContentTag& inTag ) { inWriter.writeContentTag(inTag.mTagName, inTag.mContent); return inWriter; } + + inline void writeProperty( XmlWriter& inWriter, MemoryBuffer& tempBuffer, const char* inPropName ) + { + PxU8 data = 0; + tempBuffer.write( &data, sizeof(PxU8) ); + inWriter.writeContentTag( inPropName, reinterpret_cast( tempBuffer.mBuffer ) ); + tempBuffer.clear(); + } + + template + inline void writeProperty( XmlWriter& inWriter, MemoryBuffer& tempBuffer, const char* inPropName, TDataType inValue ) + { + tempBuffer << inValue; + writeProperty( inWriter, tempBuffer, inPropName ); + } +} } +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h new file mode 100644 index 000000000..aa594d868 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h @@ -0,0 +1,275 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_STRINGTOTYPE_H +#define PX_XML_STRINGTOTYPE_H + +#include +#include +#include "PsString.h" +#include "PxCoreUtilityTypes.h" +#include "PxFiltering.h" + +//Remapping function name for gcc-based systems. +#ifndef _MSC_VER +#define _strtoui64 strtoull +#endif + + +namespace physx { namespace Sn { + + template + struct StrToImpl + { + bool compile_error; + }; + + template<> struct StrToImpl { + //Id's (void ptrs) are written to file as unsigned + //64 bit integers, so this method gets called more + //often than one might think. + PX_INLINE void strto( PxU64& ioDatatype,const char*& ioData ) + { + ioDatatype = _strtoui64( ioData, const_cast(&ioData), 10 ); + } + }; + + PX_INLINE PxF32 strToFloat(const char *str,const char **nextScan) + { + PxF32 ret; + while ( *str && isspace(static_cast(*str))) str++; // skip leading whitespace + char temp[256] = ""; + char *dest = temp; + char *end = &temp[255]; + const char *begin = str; + while ( *str && !isspace(static_cast(*str)) && dest < end ) // copy the number up to the first whitespace or eos + { + *dest++ = *str++; + } + *dest = 0; + ret = PxF32(strtod(temp,&end)); + if ( nextScan ) + { + *nextScan = begin+(end-temp); + } + return ret; + } + + + template<> struct StrToImpl { + PX_INLINE void strto( PxU32& ioDatatype,const char*& ioData ) + { + ioDatatype = static_cast( strtoul( ioData,const_cast(&ioData), 10 ) ); + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxI32& ioDatatype,const char*& ioData ) + { + ioDatatype = static_cast( strtoul( ioData,const_cast(&ioData), 10 ) ); + } + }; + + + template<> struct StrToImpl { + PX_INLINE void strto( PxU16& ioDatatype,const char*& ioData ) + { + ioDatatype = static_cast( strtoul( ioData,const_cast(&ioData), 10 ) ); + } + }; + + PX_INLINE void eatwhite(const char*& ioData ) + { + if ( ioData ) + { + while( isspace( static_cast(*ioData) ) ) + ++ioData; + } + } + + // copy the source data to the dest buffer until the first whitespace is encountered. + // Do not overflow the buffer based on the bufferLen provided. + // Advance the input 'ioData' pointer so that it sits just at the next whitespace + PX_INLINE void nullTerminateWhite(const char*& ioData,char *buffer,PxU32 bufferLen) + { + if ( ioData ) + { + char *eof = buffer+(bufferLen-1); + char *dest = buffer; + while( *ioData && !isspace(static_cast(*ioData)) && dest < eof ) + { + *dest++ = *ioData++; + } + *dest = 0; + } + } + + inline void nonNullTerminateWhite(const char*& ioData ) + { + if ( ioData ) + { + while( *ioData && !isspace( static_cast(*ioData) ) ) + ++ioData; + } + } + + template<> struct StrToImpl { + inline void strto( PxF32& ioDatatype,const char*& ioData ) + { + ioDatatype = strToFloat(ioData,&ioData); + } + + }; + + + template<> struct StrToImpl { + inline void strto( void*& ioDatatype,const char*& ioData ) + { + PxU64 theData; + StrToImpl().strto( theData, ioData ); + ioDatatype = reinterpret_cast( static_cast( theData ) ); + } + }; + + + template<> struct StrToImpl { + inline void strto( physx::PxVec3& ioDatatype,const char*& ioData ) + { + StrToImpl().strto( ioDatatype[0], ioData ); + StrToImpl().strto( ioDatatype[1], ioData ); + StrToImpl().strto( ioDatatype[2], ioData ); + } + }; + + template<> struct StrToImpl { + inline void strto( PxU8*& /*ioDatatype*/,const char*& /*ioData*/) + { + } + }; + + template<> struct StrToImpl { + inline void strto( bool& ioType,const char*& inValue ) + { + ioType = physx::shdfnd::stricmp( inValue, "true" ) == 0 ? true : false; + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxU8& ioType,const char* & inValue) + { + ioType = static_cast( strtoul( inValue,const_cast(&inValue), 10 ) ); + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxFilterData& ioType,const char*& inValue) + { + ioType.word0 = static_cast( strtoul( inValue,const_cast(&inValue), 10 ) ); + ioType.word1 = static_cast( strtoul( inValue,const_cast(&inValue), 10 ) ); + ioType.word2 = static_cast( strtoul( inValue,const_cast(&inValue), 10 ) ); + ioType.word3 = static_cast( strtoul( inValue, NULL, 10 ) ); + } + }; + + + template<> struct StrToImpl { + PX_INLINE void strto( PxQuat& ioType,const char*& inValue ) + { + ioType.x = static_cast( strToFloat( inValue, &inValue ) ); + ioType.y = static_cast( strToFloat( inValue, &inValue ) ); + ioType.z = static_cast( strToFloat( inValue, &inValue ) ); + ioType.w = static_cast( strToFloat( inValue, &inValue ) ); + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxTransform& ioType,const char*& inValue) + { + ioType.q.x = static_cast( strToFloat( inValue, &inValue ) ); + ioType.q.y = static_cast( strToFloat( inValue, &inValue ) ); + ioType.q.z = static_cast( strToFloat( inValue, &inValue ) ); + ioType.q.w = static_cast( strToFloat( inValue, &inValue ) ); + + ioType.p[0] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.p[1] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.p[2] = static_cast( strToFloat( inValue, &inValue ) ); + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxBounds3& ioType,const char*& inValue) + { + ioType.minimum[0] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.minimum[1] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.minimum[2] = static_cast( strToFloat( inValue, &inValue ) ); + + ioType.maximum[0] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.maximum[1] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.maximum[2] = static_cast( strToFloat( inValue, &inValue ) ); + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxMetaDataPlane& ioType,const char*& inValue) + { + ioType.normal.x = static_cast( strToFloat( inValue, &inValue ) ); + ioType.normal.y = static_cast( strToFloat( inValue, &inValue ) ); + ioType.normal.z = static_cast( strToFloat( inValue, &inValue ) ); + ioType.distance = static_cast( strToFloat( inValue, &inValue ) ); + } + }; + + + template<> struct StrToImpl { + PX_INLINE void strto( PxRigidDynamic*& /*ioDatatype*/,const char*& /*ioData*/) + { + } + }; + + template + inline void strto( TDataType& ioType,const char*& ioData ) + { + if ( ioData && *ioData ) StrToImpl().strto( ioType, ioData ); + } + + template + inline void strtoLong( TDataType& ioType,const char*& ioData ) + { + if ( ioData && *ioData ) StrToImpl().strto( ioType, ioData ); + } + + template + inline void stringToType( const char* inValue, TDataType& ioType ) + { + const char* theValue( inValue ); + return strto( ioType, theValue ); + } + +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h new file mode 100644 index 000000000..461be83b3 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h @@ -0,0 +1,907 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_VISITOR_READER_H +#define PX_XML_VISITOR_READER_H + +#include "PsArray.h" +#include "PsUtilities.h" +#include "RepXMetaDataPropertyVisitor.h" +#include "SnPxStreamOperators.h" +#include "SnXmlMemoryPoolStreams.h" +#include "SnXmlReader.h" +#include "SnXmlImpl.h" +#include "SnXmlMemoryAllocator.h" +#include "SnXmlStringToType.h" + +namespace physx { namespace Sn { + + + inline PxU32 findEnumByName( const char* inName, const PxU32ToName* inTable ) + { + for ( PxU32 idx = 0; inTable[idx].mName != NULL; ++idx ) + { + if ( physx::shdfnd::stricmp( inTable[idx].mName, inName ) == 0 ) + return inTable[idx].mValue; + } + return 0; + } + + PX_INLINE void stringToFlagsType( const char* strData, XmlMemoryAllocator& alloc, PxU32& ioType, const PxU32ToName* inTable ) + { + if ( inTable == NULL ) + return; + ioType = 0; + if ( strData && *strData) + { + //Destructively parse the string to get out the different flags. + char* theValue = const_cast( copyStr( &alloc, strData ) ); + char* theMarker = theValue; + char* theNext = theValue; + while( theNext && *theNext ) + { + ++theNext; + if( *theNext == '|' ) + { + *theNext = 0; + ++theNext; + ioType |= static_cast< PxU32 > ( findEnumByName( theMarker, inTable ) ); + theMarker = theNext; + } + } + if ( theMarker && *theMarker ) + ioType |= static_cast< PxU32 > ( findEnumByName( theMarker, inTable ) ); + alloc.deallocate( reinterpret_cast( theValue ) ); + } + } + + template + PX_INLINE void stringToEnumType( const char* strData, TDataType& ioType, const PxU32ToName* inTable ) + { + ioType = static_cast( findEnumByName( strData, inTable ) ); + } + + template + PX_INLINE bool readProperty( XmlReader& inReader, const char* pname, TDataType& ioType ) + { + const char* value; + if ( inReader.read( pname, value ) ) + { + stringToType( value, ioType ); + return true; + } + return false; + } + + template + inline TObjType* findReferencedObject( PxCollection& collection, PxSerialObjectId id) + { + PX_ASSERT(id > 0); + TObjType* outObject = static_cast(const_cast(collection.find(id))); + if (outObject == NULL) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::createCollectionFromXml: " + "Reference to ID %d cannot be resolved. Make sure externalRefs collection is specified if required and " + "check Xml file for completeness.", + id); + } + return outObject; + } + + template + inline bool readReference( XmlReader& inReader, PxCollection& collection, TObjType*& outObject ) + { + PxSerialObjectId theId; + const char* theValue = inReader.getCurrentItemValue(); + strto( theId, theValue ); + if( theId == 0) + { + // the NULL pointer is a valid pointer if the input id is 0 + outObject = NULL; + return true; + } + else + { + outObject = findReferencedObject(collection, theId); + return outObject != NULL; + } + } + + template + inline bool readReference( XmlReader& inReader, PxCollection& inCollection, const char* pname, TObjType*& outObject ) + { + outObject = NULL; + PxSerialObjectId theId = 0; + if (readProperty ( inReader, pname, theId ) && theId ) + { + outObject = findReferencedObject(inCollection, theId); + } + // the NULL pointer is a valid pointer if the input id is 0 + return (outObject != NULL) || 0 == theId; + } + + template + inline bool readFlagsProperty( XmlReader& reader, XmlMemoryAllocator& allocator, const char* pname, const PxU32ToName* inConversions, PxFlags& outFlags ) + { + const char* value; + if ( reader.read( pname, value ) ) + { + PxU32 tempValue = 0; + stringToFlagsType( value, allocator, tempValue, inConversions ); + outFlags = PxFlags(Ps::to16(tempValue) ); + return true; + } + return false; + } + + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj, TInfoType& info); + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj); + + template + inline PxGeometry* parseGeometry( TReaderType& reader, TGeomType& /*inGeom*/) + { + PxAllocatorCallback& inAllocator = reader.mAllocator.getAllocator(); + + TGeomType* shape = PX_PLACEMENT_NEW((inAllocator.allocate(sizeof(TGeomType), "parseGeometry", __FILE__, __LINE__ )), TGeomType); + PxClassInfoTraits info; + readComplexObj( reader, shape); + return shape; + } + + + template + inline void parseShape( TReaderType& visitor, PxGeometry*& outResult, Ps::Array& outMaterials) + { + XmlReader& theReader( visitor.mReader ); + PxCollection& collection = visitor.mCollection; + visitor.pushCurrentContext(); + if ( visitor.gotoTopName() ) + { + visitor.pushCurrentContext(); + if ( visitor.gotoChild( "Materials" ) ) + { + for( bool matSuccess = visitor.gotoFirstChild(); matSuccess; + matSuccess = visitor.gotoNextSibling() ) + { + PxMaterial* material = NULL; + if(!readReference( theReader, collection, material )) + visitor.mHadError = true; + if ( material ) + outMaterials.pushBack( material ); + } + } + visitor.popCurrentContext(); + visitor.pushCurrentContext(); + + PxPlaneGeometry plane; + PxHeightFieldGeometry heightField; + PxSphereGeometry sphere; + PxTriangleMeshGeometry mesh; + PxConvexMeshGeometry convex; + PxBoxGeometry box; + PxCapsuleGeometry capsule; + if ( visitor.gotoChild( "Geometry" ) ) + { + if ( visitor.gotoFirstChild() ) + { + const char* geomTypeName = visitor.getCurrentItemName(); + + if ( physx::shdfnd::stricmp( geomTypeName, "PxSphereGeometry" ) == 0 ) outResult = parseGeometry(visitor, sphere); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxPlaneGeometry" ) == 0 ) outResult = parseGeometry(visitor, plane); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxCapsuleGeometry" ) == 0 ) outResult = parseGeometry(visitor, capsule); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxBoxGeometry" ) == 0 ) outResult = parseGeometry(visitor, box); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxConvexMeshGeometry" ) == 0 ) outResult = parseGeometry(visitor, convex); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxTriangleMeshGeometry" ) == 0 ) outResult = parseGeometry(visitor, mesh); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxHeightFieldGeometry" ) == 0 ) outResult = parseGeometry(visitor, heightField); + else + PX_ASSERT( false ); + } + } + visitor.popCurrentContext(); + } + visitor.popCurrentContext(); + + return; + } + + template + inline void readShapesProperty( TReaderType& visitor, TObjType* inObj, const PxRigidActorShapeCollection* inProp = NULL, bool isSharedShape = false ) + { + PX_UNUSED(isSharedShape); + PX_UNUSED(inProp); + + XmlReader& theReader( visitor.mReader ); + PxCollection& collection( visitor.mCollection ); + + visitor.pushCurrentContext(); + if ( visitor.gotoTopName() ) + { + //uggh working around the shape collection api. + //read out materials and geometry + for ( bool success = visitor.gotoFirstChild(); success; + success = visitor.gotoNextSibling() ) + { + if( 0 == physx::shdfnd::stricmp( visitor.getCurrentItemName(), "PxShapeRef" ) ) + { + PxShape* shape = NULL; + if(!readReference( theReader, collection, shape )) + visitor.mHadError = true; + if(shape) + inObj->attachShape( *shape ); + } + else + { + Ps::Array materials; + PxGeometry* geometry = NULL; + parseShape( visitor, geometry, materials); + PxShape* theShape = NULL; + if ( materials.size() ) + { + theShape = visitor.mArgs.physics.createShape( *geometry, materials.begin(), Ps::to16(materials.size()), true ); + if ( theShape ) + { + readComplexObj( visitor, theShape ); + + if(theShape) + { + inObj->attachShape(*theShape); + collection.add( *theShape ); + } + } + } + + switch(geometry->getType()) + { + case PxGeometryType::eSPHERE : + static_cast(geometry)->~PxSphereGeometry(); + break; + case PxGeometryType::ePLANE : + static_cast(geometry)->~PxPlaneGeometry(); + break; + case PxGeometryType::eCAPSULE : + static_cast(geometry)->~PxCapsuleGeometry(); + break; + case PxGeometryType::eBOX : + static_cast(geometry)->~PxBoxGeometry(); + break; + case PxGeometryType::eCONVEXMESH : + static_cast(geometry)->~PxConvexMeshGeometry(); + break; + case PxGeometryType::eTRIANGLEMESH : + static_cast(geometry)->~PxTriangleMeshGeometry(); + break; + case PxGeometryType::eHEIGHTFIELD : + static_cast(geometry)->~PxHeightFieldGeometry(); + break; + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ASSERT(0); + } + visitor.mAllocator.getAllocator().deallocate(geometry); + } + } + } + visitor.popCurrentContext(); + } + + struct ReaderNameStackEntry : NameStackEntry + { + bool mValid; + ReaderNameStackEntry( const char* nm, bool valid ) : NameStackEntry(nm), mValid(valid) {} + }; + + typedef PxProfileArray TReaderNameStack; + + template + struct RepXVisitorReaderBase + { + + protected: + RepXVisitorReaderBase& operator=(const RepXVisitorReaderBase&); + public: + TReaderNameStack& mNames; + PxProfileArray& mContexts; + PxRepXInstantiationArgs mArgs; + XmlReader& mReader; + TObjType* mObj; + XmlMemoryAllocator& mAllocator; + PxCollection& mCollection; + bool mValid; + bool& mHadError; + + RepXVisitorReaderBase( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, TObjType* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& hadError ) + : mNames( names ) + , mContexts( contexts ) + , mArgs( args ) + , mReader( reader ) + , mObj( obj ) + , mAllocator( alloc ) + , mCollection( collection ) + , mValid( true ) + , mHadError(hadError) + { + } + RepXVisitorReaderBase( const RepXVisitorReaderBase& other ) + : mNames( other.mNames ) + , mContexts( other.mContexts ) + , mArgs( other.mArgs ) + , mReader( other.mReader ) + , mObj( other.mObj ) + , mAllocator( other.mAllocator ) + , mCollection( other.mCollection ) + , mValid( other.mValid ) + , mHadError( other.mHadError ) + { + } + + + void pushName( const char* name ) + { + gotoTopName(); + mNames.pushBack( ReaderNameStackEntry( name, mValid ) ); + } + void pushBracketedName( const char* name ) { pushName( name ); } + void popName() + { + if ( mNames.size() ) + { + if ( mNames.back().mOpen && mNames.back().mValid ) + mReader.leaveChild(); + mNames.popBack(); + } + mValid =true; + if ( mNames.size() && mNames.back().mValid == false ) + mValid = false; + } + + void pushCurrentContext() + { + mContexts.pushBack( static_cast( mNames.size() ) ); + } + void popCurrentContext() + { + if ( mContexts.size() ) + { + PxU32 depth = mContexts.back(); + PX_ASSERT( mNames.size() >= depth ); + while( mNames.size() > depth ) + popName(); + mContexts.popBack(); + } + } + + bool updateLastEntryAfterOpen() + { + mNames.back().mValid = mValid; + mNames.back().mOpen = mValid; + return mValid; + } + + bool gotoTopName() + { + if ( mNames.size() && mNames.back().mOpen == false ) + { + if ( mValid ) + mValid = mReader.gotoChild( mNames.back().mName ); + updateLastEntryAfterOpen(); + } + return mValid; + } + + bool isValid() const { return mValid; } + + bool gotoChild( const char* name ) + { + pushName( name ); + return gotoTopName(); + } + + bool gotoFirstChild() + { + pushName( "__child" ); + if ( mValid ) mValid = mReader.gotoFirstChild(); + return updateLastEntryAfterOpen(); + } + + bool gotoNextSibling() + { + bool retval = mValid; + if ( mValid ) retval = mReader.gotoNextSibling(); + return retval; + } + + const char* getCurrentItemName() { if (mValid ) return mReader.getCurrentItemName(); return ""; } + + const char* topName() const + { + if ( mNames.size() ) return mNames.back().mName; + PX_ASSERT( false ); + return "bad__repx__name"; + } + + const char* getCurrentValue() + { + const char* value = NULL; + if ( isValid() && mReader.read( topName(), value ) ) + return value; + return NULL; + } + + template + bool readProperty(TDataType& outType) + { + const char* value = getCurrentValue(); + if ( value && *value ) + { + stringToType( value, outType ); + return true; + } + return false; + } + + template + bool readExtendedIndexProperty(TDataType& outType) + { + const char* value = mReader.getCurrentItemValue(); + if ( value && *value ) + { + stringToType( value, outType ); + return true; + } + return false; + } + + + template + bool readReference(TRefType*& outRef) + { + return physx::Sn::readReference( mReader, mCollection, topName(), outRef ); + } + + inline bool readProperty(const char*& outProp ) + { + outProp = ""; + const char* value = getCurrentValue(); + if ( value && *value && mArgs.stringTable ) + { + outProp = mArgs.stringTable->allocateStr( value ); + return true; + } + return false; + } + + inline bool readProperty(PxConvexMesh*& outProp ) + { + return readReference( outProp ); + } + + inline bool readProperty(PxTriangleMesh*& outProp ) + { + return readReference( outProp ); + } + inline bool readProperty(PxBVH33TriangleMesh*& outProp ) + { + return readReference( outProp ); + } + inline bool readProperty(PxBVH34TriangleMesh*& outProp ) + { + return readReference( outProp ); + } + + inline bool readProperty(PxHeightField*& outProp ) + { + return readReference( outProp ); + } + + inline bool readProperty( PxRigidActor *& outProp ) + { + return readReference( outProp ); + } + + template + void simpleProperty( PxU32 /*key*/, TAccessorType& inProp ) + { + typedef typename TAccessorType::prop_type TPropertyType; + TPropertyType value; + if ( readProperty( value ) ) + inProp.set( mObj, value ); + } + + template + void enumProperty( PxU32 /*key*/, TAccessorType& inProp, const PxU32ToName* inConversions ) + { + typedef typename TAccessorType::prop_type TPropertyType; + const char* strVal = getCurrentValue(); + if ( strVal && *strVal ) + { + TPropertyType pval; + stringToEnumType( strVal, pval, inConversions ); + inProp.set( mObj, pval ); + } + } + + template + void flagsProperty( PxU32 /*key*/, const TAccessorType& inProp, const PxU32ToName* inConversions ) + { + typedef typename TAccessorType::prop_type TPropertyType; + typedef typename TPropertyType::InternalType TInternalType; + + const char* strVal = getCurrentValue(); + if ( strVal && *strVal ) + { + PxU32 tempValue = 0; + stringToFlagsType( strVal, mAllocator, tempValue, inConversions ); + inProp.set( mObj, TPropertyType(TInternalType( tempValue ))); + } + } + + template + void complexProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + if ( gotoTopName() ) + { + TPropertyType propVal = inProp.get( mObj ); + readComplexObj( *this, &propVal, inInfo ); + inProp.set( mObj, propVal ); + } + } + + template + void bufferCollectionProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + Ps::InlineArray theData; + + this->pushCurrentContext(); + if ( this->gotoTopName() ) + { + for ( bool success = this->gotoFirstChild(); success; + success = this->gotoNextSibling() ) + { + TPropertyType propVal; + readComplexObj( *this, &propVal, inInfo ); + theData.pushBack(propVal); + } + } + this->popCurrentContext(); + + inProp.set( mObj, theData.begin(), theData.size() ); + + } + + template + void extendedIndexedProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + + this->pushCurrentContext(); + if ( this->gotoTopName() ) + { + PxU32 index = 0; + for ( bool success = this->gotoFirstChild(); success; + success = this->gotoNextSibling() ) + { + TPropertyType propVal; + readComplexObj( *this, &propVal, inInfo ); + inProp.set(mObj, index, propVal); + ++index; + } + } + this->popCurrentContext(); + } + + template + void PxFixedSizeLookupTableProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + const_cast(inProp).clear( mObj ); + + this->pushCurrentContext(); + if ( this->gotoTopName() ) + { + for ( bool success = this->gotoFirstChild(); success; + success = this->gotoNextSibling() ) + { + TPropertyType propXVal; + readComplexObj( *this, &propXVal, inInfo ); + + if(this->gotoNextSibling()) + { + TPropertyType propYVal; + readComplexObj( *this, &propYVal, inInfo ); + const_cast(inProp).addPair(mObj, propXVal, propYVal); + } + } + } + this->popCurrentContext(); + } + + void handleShapes( const PxRigidActorShapeCollection& inProp ) + { + physx::Sn::readShapesProperty( *this, mObj, &inProp ); + } + }; + + template + struct RepXVisitorReader : public RepXVisitorReaderBase + { + RepXVisitorReader( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, TObjType* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& ret) + : RepXVisitorReaderBase( names, contexts, args, reader, obj, alloc, collection, ret) + { + } + RepXVisitorReader( const RepXVisitorReader& other ) + : RepXVisitorReaderBase( other ) + { + } + }; + + // Specialized template to load dynamic rigid, to determine the kinematic state first + template<> + struct RepXVisitorReader : public RepXVisitorReaderBase + { + RepXVisitorReader( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, PxRigidDynamic* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& ret) + : RepXVisitorReaderBase( names, contexts, args, reader, obj, alloc, collection, ret) + { + } + RepXVisitorReader( const RepXVisitorReader& other ) + : RepXVisitorReaderBase( other ) + { + } + + void handleShapes( const PxRigidActorShapeCollection& inProp ) + { + // Need to read the parental actor to check if actor is kinematic + // in that case we need to apply the kinematic flag before a shape is set + XmlReaderWriter* parentReader = static_cast(mReader.getParentReader()); + if(mObj) + { + const char* value; + if (parentReader->read( "RigidBodyFlags", value )) + { + if(strstr(value, "eKINEMATIC")) + { + mObj->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true); + } + } + } + physx::Sn::readShapesProperty( *this, mObj, &inProp ); + parentReader->release(); + } + + + template + void simpleProperty( PxU32 /*key*/, TAccessorType& inProp ) + { + typedef typename TAccessorType::prop_type TPropertyType; + TPropertyType value; + if (readProperty(value)) + { + // If the rigid body is kinematic, we cannot set the LinearVelocity or AngularVelocity + const bool kinematic = (mObj->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC); + if(kinematic && (inProp.mProperty.mKey == PxPropertyInfoName::PxRigidBody_LinearVelocity || inProp.mProperty.mKey == PxPropertyInfoName::PxRigidBody_AngularVelocity)) + return; + + inProp.set(mObj, value ); + } + } + private: + RepXVisitorReader& operator=(const RepXVisitorReader&); + }; + + template<> + struct RepXVisitorReader : public RepXVisitorReaderBase + { + RepXVisitorReader( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, PxShape* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& ret ) + : RepXVisitorReaderBase( names, contexts, args, reader, obj, alloc, collection, ret ) + { + } + RepXVisitorReader( const RepXVisitorReader& other ) + : RepXVisitorReaderBase( other ) + { + } + void handleShapeMaterials( const PxShapeMaterialsProperty& ) //these were handled during construction. + { + } + void handleGeometryProperty( const PxShapeGeometryProperty& ) + { + } + private: + RepXVisitorReader& operator=(const RepXVisitorReader&); + }; + + template<> + struct RepXVisitorReader : public RepXVisitorReaderBase + { + RepXVisitorReader( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, PxArticulationLink* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& ret ) + : RepXVisitorReaderBase( names, contexts, args, reader, obj, alloc, collection, ret ) + { + } + RepXVisitorReader( const RepXVisitorReader& other ) + : RepXVisitorReaderBase( other ) + { + } + void handleIncomingJoint( const TIncomingJointPropType& prop ) + { + pushName( "Joint" ); + if ( gotoTopName() ) + { + PxArticulationBase::Enum type = mObj->getArticulation().getType(); + if (type == PxArticulationBase::eMaximumCoordinate) + { + PxArticulationJoint* theJoint = static_cast((prop.get(mObj))); + + readComplexObj(*this, theJoint); + + //Add joint to PxCollection, since PxArticulation requires PxArticulationLink and joint. + mCollection.add(*theJoint); + + } + else + { + PxArticulationJointReducedCoordinate* theJoint = static_cast((prop.get(mObj))); + + readComplexObj(*this, theJoint); + + //Add joint to PxCollection, since PxArticulation requires PxArticulationLink and joint. + mCollection.add(*theJoint); + } + + + + } + popName(); + } + private: + RepXVisitorReader& operator=(const RepXVisitorReader&); + }; + + inline void readProperty( RepXVisitorReaderBase& inSerializer, PxArticulation* inObj, const PxArticulationLinkCollectionProp& /*inProp*/) + { + PxProfileAllocatorWrapper theWrapper( inSerializer.mAllocator.getAllocator() ); + PxCollection& collection( inSerializer.mCollection ); + + TArticulationLinkLinkMap linkRemapMap( theWrapper ); + inSerializer.pushCurrentContext(); + if( inSerializer.gotoTopName() ) + { + for ( bool links = inSerializer.gotoFirstChild(); + links != false; + links = inSerializer.gotoNextSibling() ) + { + //Need enough information to create the link... + PxSerialObjectId theParentPtr = 0; + const PxArticulationLink* theParentLink = NULL; + if ( inSerializer.mReader.read( "Parent", theParentPtr ) ) + { + const TArticulationLinkLinkMap::Entry* theRemappedParent( linkRemapMap.find( theParentPtr ) ); + //If we have a valid at write time, we had better have a valid parent at read time. + PX_ASSERT( theRemappedParent ); + theParentLink = theRemappedParent->second; + } + PxArticulationLink* newLink = inObj->createLink( const_cast( theParentLink ), PxTransform(PxIdentity) ); + PxSerialObjectId theIdPtr = 0; + inSerializer.mReader.read( "Id", theIdPtr ); + + linkRemapMap.insert( theIdPtr, newLink ); + readComplexObj( inSerializer, newLink ); + + //Add link to PxCollection, since PxArticulation requires PxArticulationLink and joint. + collection.add( *newLink, theIdPtr ); + } + } + inSerializer.popCurrentContext(); + } + + template<> + struct RepXVisitorReader : public RepXVisitorReaderBase + { + RepXVisitorReader( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, PxArticulation* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& ret) + : RepXVisitorReaderBase( names, contexts, args, reader, obj, alloc, collection, ret) + { + } + RepXVisitorReader( const RepXVisitorReader& other ) + : RepXVisitorReaderBase( other ) + { + } + + void handleArticulationLinks( const PxArticulationLinkCollectionProp& inProp ) + { + physx::Sn::readProperty( *this, mObj, inProp ); + } + }; + + template + inline bool readAllProperties( PxRepXInstantiationArgs args, TReaderNameStack& names, PxProfileArray& contexts, XmlReader& reader, TObjType* obj, XmlMemoryAllocator& alloc, PxCollection& collection, TInfoType& info ) + { + bool hadError = false; + RepXVisitorReader theReader( names, contexts, args, reader, obj, alloc, collection, hadError); + RepXPropertyFilter > theOp( theReader ); + info.visitBaseProperties( theOp ); + info.visitInstanceProperties( theOp ); + return !hadError; + } + + + template + inline bool readAllProperties( PxRepXInstantiationArgs args, XmlReader& reader, TObjType* obj, XmlMemoryAllocator& alloc, PxCollection& collection ) + { + PxProfileAllocatorWrapper wrapper( alloc.getAllocator() ); + TReaderNameStack names( wrapper ); + PxProfileArray contexts( wrapper ); + PxClassInfoTraits info; + return readAllProperties( args, names, contexts, reader, obj, alloc, collection, info.Info ); + } + + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj, TInfoType& info) + { + if(!readAllProperties( oldVisitor.mArgs, oldVisitor.mNames, oldVisitor.mContexts, oldVisitor.mReader, inObj, oldVisitor.mAllocator, oldVisitor.mCollection, info )) + oldVisitor.mHadError = true; + } + + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj, const TInfoType& info) + { + if(!readAllProperties( oldVisitor.mArgs, oldVisitor.mNames, oldVisitor.mContexts, oldVisitor.mReader, inObj, oldVisitor.mAllocator, oldVisitor.mCollection, info )) + oldVisitor.mHadError = true; + } + + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj, const PxUnknownClassInfo& /*info*/) + { + const char* value = oldVisitor.mReader.getCurrentItemValue(); + if ( value && *value ) + { + stringToType( value, *inObj ); + return; + } + oldVisitor.mHadError = true; + } + + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj) + { + PxClassInfoTraits info; + if(!readAllProperties( oldVisitor.mArgs, oldVisitor.mNames, oldVisitor.mContexts, oldVisitor.mReader, inObj, oldVisitor.mAllocator, oldVisitor.mCollection, info.Info )) + oldVisitor.mHadError = true; + } + +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h new file mode 100644 index 000000000..5caacaf2d --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h @@ -0,0 +1,801 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_VISITOR_WRITER_H +#define PX_XML_VISITOR_WRITER_H + +#include "PsInlineArray.h" +#include "CmPhysXCommon.h" +#include "RepXMetaDataPropertyVisitor.h" +#include "SnPxStreamOperators.h" +#include "SnXmlMemoryPoolStreams.h" +#include "SnXmlWriter.h" +#include "SnXmlImpl.h" +#include "PsFoundation.h" +#include "foundation/PxStrideIterator.h" + +namespace physx { namespace Sn { + + template + inline void writeReference( XmlWriter& writer, PxCollection& inCollection, const char* inPropName, const TDataType* inDatatype ) + { + const PxBase* s = static_cast( inDatatype ) ; + if( inDatatype && !inCollection.contains( *const_cast(s) )) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PxSerialization::serializeCollectionToXml: Reference \"%s\" could not be resolved.", inPropName); + } + + PxSerialObjectId theId = 0; + if( s ) + { + theId = inCollection.getId( *s ); + if( theId == 0 ) + theId = static_cast(reinterpret_cast(inDatatype)); + } + + writer.write( inPropName, PxCreateRepXObject( inDatatype, theId ) ); + } + + inline void writeProperty( XmlWriter& inWriter, MemoryBuffer& inBuffer, const char* inProp ) + { + PxU8 data = 0; + inBuffer.write( &data, sizeof(PxU8) ); + inWriter.write( inProp, reinterpret_cast( inBuffer.mBuffer ) ); + inBuffer.clear(); + } + + template + inline void writeProperty( XmlWriter& inWriter, PxCollection&, MemoryBuffer& inBuffer, const char* inPropName, TDataType inValue ) + { + inBuffer << inValue; + writeProperty( inWriter, inBuffer, inPropName ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxConvexMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxConvexMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxTriangleMesh* inDatatype ) + { + if (inDatatype->getConcreteType() == PxConcreteType::eTRIANGLE_MESH_BVH33) + { + const PxBVH33TriangleMesh* dataType = inDatatype->is(); + writeReference(writer, inCollection, inPropName, dataType); + } + else if (inDatatype->getConcreteType() == PxConcreteType::eTRIANGLE_MESH_BVH34) + { + const PxBVH34TriangleMesh* dataType = inDatatype->is(); + writeReference(writer, inCollection, inPropName, dataType); + } + else + { + PX_ASSERT(0); + } + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxTriangleMesh* inDatatype ) + { + if (inDatatype->getConcreteType() == PxConcreteType::eTRIANGLE_MESH_BVH33) + { + PxBVH33TriangleMesh* dataType = inDatatype->is(); + writeReference(writer, inCollection, inPropName, dataType); + } + else if (inDatatype->getConcreteType() == PxConcreteType::eTRIANGLE_MESH_BVH34) + { + PxBVH34TriangleMesh* dataType = inDatatype->is(); + writeReference(writer, inCollection, inPropName, dataType); + } + else + { + PX_ASSERT(0); + } + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxBVH33TriangleMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxBVH33TriangleMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxBVH34TriangleMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxBVH34TriangleMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxHeightField* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxHeightField* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxRigidActor* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxArticulation* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxRigidActor* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeFlagsProperty( XmlWriter& inWriter, MemoryBuffer& tempBuf, const char* inPropName, PxU32 inFlags, const PxU32ToName* inTable ) + { + if ( inTable ) + { + PxU32 flagValue( inFlags ); + if ( flagValue ) + { + for ( PxU32 idx =0; inTable[idx].mName != NULL; ++idx ) + { + if ( (inTable[idx].mValue & flagValue) == inTable[idx].mValue ) + { + if ( tempBuf.mWriteOffset != 0 ) + tempBuf << "|"; + tempBuf << inTable[idx].mName; + } + } + writeProperty( inWriter, tempBuf, inPropName ); + } + } + } + + inline void writeFlagsBuffer( MemoryBuffer& tempBuf, PxU32 flagValue, const PxU32ToName* inTable ) + { + PX_ASSERT(inTable); + bool added = false; + + if ( flagValue ) + { + for ( PxU32 item =0; inTable[item].mName != NULL; ++item ) + { + if ( (inTable[item].mValue & flagValue) != 0 ) + { + if ( added ) + tempBuf << "|"; + tempBuf << inTable[item].mName; + added = true; + } + } + } + } + + inline void writePxVec3( PxOutputStream& inStream, const PxVec3& inVec ) { inStream << inVec; } + + + template + inline const TDataType& PtrAccess( const TDataType* inPtr, PxU32 inIndex ) + { + return inPtr[inIndex]; + } + + template + inline void BasicDatatypeWrite( PxOutputStream& inStream, const TDataType& item ) { inStream << item; } + + template + inline void writeBuffer( XmlWriter& inWriter, MemoryBuffer& inTempBuffer + , PxU32 inObjPerLine, const TObjType* inObjType, TAccessOperator inAccessOperator + , PxU32 inBufSize, const char* inPropName, TWriteOperator inOperator ) + { + if ( inBufSize && inObjType ) + { + for ( PxU32 idx = 0; idx < inBufSize; ++idx ) + { + if ( idx && ( idx % inObjPerLine == 0 ) ) + inTempBuffer << "\n\t\t\t"; + else + inTempBuffer << " "; + inOperator( inTempBuffer, inAccessOperator( inObjType, idx ) ); + } + writeProperty( inWriter, inTempBuffer, inPropName ); + } + } + + template + inline void writeStrideBuffer( XmlWriter& inWriter, MemoryBuffer& inTempBuffer + , PxU32 inObjPerLine, PxStrideIterator& inData, TAccessOperator inAccessOperator + , PxU32 inBufSize, const char* inPropName, PxU32 /*inStride*/, TWriteOperator inOperator ) + { +#if PX_SWITCH + const auto *dat = &inData[0]; + if (inBufSize && dat != NULL) +#else + if ( inBufSize && &inData[0]) +#endif + { + for ( PxU32 idx = 0; idx < inBufSize; ++idx ) + { + if ( idx && ( idx % inObjPerLine == 0 ) ) + inTempBuffer << "\n\t\t\t"; + else + inTempBuffer << " "; + + inOperator( inTempBuffer, inAccessOperator( &inData[idx], 0 ) ); + } + writeProperty( inWriter, inTempBuffer, inPropName ); + } + } + + + template + inline void writeStrideFlags( XmlWriter& inWriter, MemoryBuffer& inTempBuffer + , PxU32 inObjPerLine, PxStrideIterator& inData, TAccessOperator /*inAccessOperator*/ + , PxU32 inBufSize, const char* inPropName, const PxU32ToName* inTable) + { +#if PX_SWITCH + const auto *dat = &inData[0]; + if (inBufSize && dat != NULL) +#else + if ( inBufSize && &inData[0]) +#endif + { + for ( PxU32 idx = 0; idx < inBufSize; ++idx ) + { + writeFlagsBuffer(inTempBuffer, inData[idx], inTable); + + if ( idx && ( idx % inObjPerLine == 0 ) ) + inTempBuffer << "\n\t\t\t"; + else + inTempBuffer << " "; + } + writeProperty( inWriter, inTempBuffer, inPropName ); + } + } + + template + inline void writeBuffer( XmlWriter& inWriter, MemoryBuffer& inTempBuffer + , PxU32 inObjPerLine, const TDataType* inBuffer + , PxU32 inBufSize, const char* inPropName, TWriteOperator inOperator ) + { + writeBuffer( inWriter, inTempBuffer, inObjPerLine, inBuffer, PtrAccess, inBufSize, inPropName, inOperator ); + } + + template + inline void writeEnumProperty( XmlWriter& inWriter, const char* inPropName, TEnumType inEnumValue, const PxU32ToName* inConversions ) + { + PxU32 theValue = static_cast( inEnumValue ); + for ( const PxU32ToName* conv = inConversions; conv->mName != NULL; ++conv ) + if ( conv->mValue == theValue ) inWriter.write( inPropName, conv->mName ); + } + + + + template + inline void handleComplexObj( TWriterType& oldVisitor, const TObjType* inObj, TInfoType& info); + + template + void handleComplexCollection( TVisitor& visitor, const TPropType& inProp, const char* childName, TInfoType& inInfo ) + { + PxU32 count( inProp.size( visitor.mObj ) ); + if ( count ) + { + Ps::InlineArray theData; + theData.resize( count ); + inProp.get( visitor.mObj, theData.begin(), count ); + for( PxU32 idx =0; idx < count; ++idx ) + { + visitor.pushName( childName ); + handleComplexObj( visitor, theData[idx], inInfo ); + visitor.popName(); + } + } + } + + template + void handleBufferCollection( TVisitor& visitor, const TPropType& inProp, const char* childName, TInfoType& inInfo ) + { + PxU32 count( inProp.size( visitor.mObj ) ); + if ( count ) + { + Ps::InlineArray theData; + theData.resize( count ); + inProp.get( visitor.mObj, theData.begin()); + + for( PxU32 idx =0; idx < count; ++idx ) + { + visitor.pushName( childName ); + handleComplexObj( visitor, theData[idx], inInfo ); + visitor.popName(); + } + } + } + + template + void handleShapes( TVisitor& visitor, const PxRigidActorShapeCollection& inProp ) + { + PxShapeGeneratedInfo theInfo; + + PxU32 count( inProp.size( visitor.mObj ) ); + if ( count ) + { + Ps::InlineArray theData; + theData.resize( count ); + inProp.get( visitor.mObj, theData.begin(), count ); + for( PxU32 idx = 0; idx < count; ++idx ) + { + const PxShape* shape = theData[idx]; + visitor.pushName( "PxShape" ); + + if( !shape->isExclusive() ) + { + writeReference( visitor.mWriter, visitor.mCollection, "PxShapeRef", shape ); + } + else + { + handleComplexObj( visitor, shape, theInfo ); + } + visitor.popName(); + } + } + } + + template + void handleShapeMaterials( TVisitor& visitor, const PxShapeMaterialsProperty& inProp ) + { + PxU32 count( inProp.size( visitor.mObj ) ); + if ( count ) + { + Ps::InlineArray theData; + theData.resize( count ); + inProp.get( visitor.mObj, theData.begin(), count ); + visitor.pushName( "PxMaterialRef" ); + + for( PxU32 idx =0; idx < count; ++idx ) + writeReference( visitor.mWriter, visitor.mCollection, "PxMaterialRef", theData[idx] ); + visitor.popName(); + } + } + + template + struct RepXVisitorWriterBase + { + TNameStack& mNameStack; + XmlWriter& mWriter; + const TObjType* mObj; + MemoryBuffer& mTempBuffer; + PxCollection& mCollection; + + RepXVisitorWriterBase( TNameStack& ns, XmlWriter& writer, const TObjType* obj, MemoryBuffer& buf, PxCollection& collection ) + : mNameStack( ns ) + , mWriter( writer ) + , mObj( obj ) + , mTempBuffer( buf ) + , mCollection( collection ) + { + } + + RepXVisitorWriterBase( const RepXVisitorWriterBase& other ) + : mNameStack( other.mNameStack ) + , mWriter( other.mWriter ) + , mObj( other.mObj ) + , mTempBuffer( other.mTempBuffer ) + , mCollection( other.mCollection ) + { + } + + RepXVisitorWriterBase& operator=( const RepXVisitorWriterBase& ){ PX_ASSERT( false ); return *this; } + + void gotoTopName() + { + if ( mNameStack.size() && mNameStack.back().mOpen == false ) + { + mWriter.addAndGotoChild( mNameStack.back().mName ); + mNameStack.back().mOpen = true; + } + } + + void pushName( const char* inName ) + { + gotoTopName(); + mNameStack.pushBack( inName ); + } + + void pushBracketedName( const char* inName ) { pushName( inName ); } + void popName() + { + if ( mNameStack.size() ) + { + if ( mNameStack.back().mOpen ) + mWriter.leaveChild(); + mNameStack.popBack(); + } + } + + const char* topName() const + { + if ( mNameStack.size() ) return mNameStack.back().mName; + PX_ASSERT( false ); + return "bad__repx__name"; + } + + template + void simpleProperty( PxU32 /*key*/, TAccessorType& inProp ) + { + typedef typename TAccessorType::prop_type TPropertyType; + TPropertyType propVal = inProp.get( mObj ); + writeProperty( mWriter, mCollection, mTempBuffer, topName(), propVal ); + } + + template + void enumProperty( PxU32 /*key*/, TAccessorType& inProp, const PxU32ToName* inConversions ) + { + writeEnumProperty( mWriter, topName(), inProp.get( mObj ), inConversions ); + } + + template + void flagsProperty( PxU32 /*key*/, const TAccessorType& inProp, const PxU32ToName* inConversions ) + { + writeFlagsProperty( mWriter, mTempBuffer, topName(), inProp.get( mObj ), inConversions ); + } + + template + void complexProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + TPropertyType propVal = inProp.get( mObj ); + handleComplexObj( *this, &propVal, inInfo ); + } + + template + void bufferCollectionProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + + PxU32 count( inProp.size( mObj ) ); + Ps::InlineArray theData; + theData.resize( count ); + + PxClassInfoTraits theTraits; + PX_UNUSED(theTraits); + PxU32 numItems = inProp.get( mObj, theData.begin(), count ); + PX_ASSERT( numItems == count ); + for( PxU32 idx =0; idx < numItems; ++idx ) + { + pushName( inProp.name() ); + handleComplexObj( *this, &theData[idx], inInfo ); + popName(); + } + } + + template + void extendedIndexedProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& /*inInfo */) + { + typedef typename TAccessorType::prop_type TPropertyType; + + PxU32 count( inProp.size( mObj ) ); + Ps::InlineArray theData; + theData.resize( count ); + + for(PxU32 i = 0; i < count; ++i) + { + char buffer[32] = { 0 }; + sprintf( buffer, "id_%u", i ); + pushName( buffer ); + + TPropertyType propVal = inProp.get( mObj, i ); + TInfoType& infoType = PxClassInfoTraits().Info; + handleComplexObj(*this, &propVal, infoType); + popName(); + } + } + + template + void PxFixedSizeLookupTableProperty( PxU32* /*key*/, TAccessorType& inProp, TInfoType& /*inInfo */) + { + typedef typename TAccessorType::prop_type TPropertyType; + PxU32 count( inProp.size( mObj ) ); + + PxU32 index = 0; + for(PxU32 i = 0; i < count; ++i) + { + char buffer[32] = { 0 }; + sprintf( buffer, "id_%u", index++ ); + pushName( buffer ); + TPropertyType propVal = inProp.getX( mObj , i); + writeProperty( mWriter, mCollection, mTempBuffer, topName(), propVal ); + popName(); + + sprintf( buffer, "id_%u", index++ ); + pushName( buffer ); + propVal = inProp.getY( mObj , i); + writeProperty( mWriter, mCollection, mTempBuffer, topName(), propVal ); + popName(); + } + } + + + void handleShapes( const PxRigidActorShapeCollection& inProp ) + { + physx::Sn::handleShapes( *this, inProp ); + } + + void handleShapeMaterials( const PxShapeMaterialsProperty& inProp ) + { + physx::Sn::handleShapeMaterials( *this, inProp ); + } + }; + + template + struct RepXVisitorWriter : RepXVisitorWriterBase + { + RepXVisitorWriter( TNameStack& ns, XmlWriter& writer, const TObjType* obj, MemoryBuffer& buf, PxCollection& collection ) + : RepXVisitorWriterBase( ns, writer, obj, buf, collection ) + { + } + + RepXVisitorWriter( const RepXVisitorWriter& other ) + : RepXVisitorWriterBase( other ) + { + } + }; + + template<> + struct RepXVisitorWriter : RepXVisitorWriterBase + { + RepXVisitorWriter( TNameStack& ns, XmlWriter& writer, const PxArticulationLink* obj, MemoryBuffer& buf, PxCollection& collection ) + : RepXVisitorWriterBase( ns, writer, obj, buf, collection ) + { + } + + RepXVisitorWriter( const RepXVisitorWriter& other ) + : RepXVisitorWriterBase( other ) + { + } + + void handleIncomingJoint( const TIncomingJointPropType& prop ) + { + const PxArticulationJointBase* theJoint( prop.get( mObj ) ); + if ( theJoint ) + { + PxArticulationJointGeneratedInfo info; + pushName( "Joint" ); + handleComplexObj( *this, theJoint, info ); + popName(); + } + } + }; + + typedef PxProfileHashMap< const PxSerialObjectId, const PxArticulationLink* > TArticulationLinkLinkMap; + + template<> + struct RepXVisitorWriter : RepXVisitorWriterBase + { + TArticulationLinkLinkMap& mArticulationLinkParents; + + RepXVisitorWriter( TNameStack& ns, XmlWriter& writer, const PxArticulation* inArticulation, MemoryBuffer& buf, PxCollection& collection, TArticulationLinkLinkMap* artMap = NULL ) + : RepXVisitorWriterBase( ns, writer, inArticulation, buf, collection ) + , mArticulationLinkParents( *artMap ) + { + Ps::InlineArray > linkList( PxProfileWrapperReflectionAllocator( buf.mManager->getWrapper() ) ); + PxU32 numLinks = inArticulation->getNbLinks(); + linkList.resize( numLinks ); + inArticulation->getLinks( linkList.begin(), numLinks ); + for ( PxU32 idx = 0; idx < numLinks; ++idx ) + { + const PxArticulationLink* theLink( linkList[idx] ); + + Ps::InlineArray theChildList; + PxU32 numChildren = theLink->getNbChildren(); + theChildList.resize( numChildren ); + theLink->getChildren( theChildList.begin(), numChildren ); + for ( PxU32 childIdx = 0; childIdx < numChildren; ++childIdx ) + mArticulationLinkParents.insert(static_cast(reinterpret_cast(theChildList[childIdx])), theLink ); + } + } + + RepXVisitorWriter( const RepXVisitorWriter& other ) + : RepXVisitorWriterBase( other ) + , mArticulationLinkParents( other.mArticulationLinkParents ) + { + } + template + void complexProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + TPropertyType propVal = inProp.get( mObj ); + handleComplexObj( *this, &propVal, inInfo ); + } + + void writeArticulationLink( const PxArticulationLink* inLink ) + { + pushName( "PxArticulationLink" ); + gotoTopName(); + + const TArticulationLinkLinkMap::Entry* theParentPtr = mArticulationLinkParents.find(static_cast(reinterpret_cast(inLink)) ); + if ( theParentPtr != NULL ) + writeProperty( mWriter, mCollection, mTempBuffer, "Parent", theParentPtr->second ); + writeProperty( mWriter, mCollection, mTempBuffer, "Id", inLink ); + + PxArticulationLinkGeneratedInfo info; + handleComplexObj( *this, inLink, info ); + popName(); + } + + void recurseAddLinkAndChildren( const PxArticulationLink* inLink, Ps::InlineArray& ioLinks ) + { + ioLinks.pushBack( inLink ); + Ps::InlineArray theChildren; + PxU32 childCount( inLink->getNbChildren() ); + theChildren.resize( childCount ); + inLink->getChildren( theChildren.begin(), childCount ); + for ( PxU32 idx = 0; idx < childCount; ++idx ) + recurseAddLinkAndChildren( theChildren[idx], ioLinks ); + } + + void handleArticulationLinks( const PxArticulationLinkCollectionProp& inProp ) + { + //topologically sort the links as per my discussion with Dilip because + //links aren't guaranteed to have the parents before the children in the + //overall link list and it is unlikely to be done by beta 1. + PxU32 count( inProp.size( mObj ) ); + if ( count ) + { + Ps::InlineArray theLinks; + theLinks.resize( count ); + inProp.get( mObj, theLinks.begin(), count ); + + Ps::InlineArray theSortedLinks; + for ( PxU32 idx = 0; idx < count; ++idx ) + { + const PxArticulationLink* theLink( theLinks[idx] ); + if ( mArticulationLinkParents.find(static_cast(reinterpret_cast(theLink)) ) == NULL ) + recurseAddLinkAndChildren( theLink, theSortedLinks ); + } + PX_ASSERT( theSortedLinks.size() == count ); + for ( PxU32 idx = 0; idx < count; ++idx ) + writeArticulationLink( theSortedLinks[idx] ); + popName(); + } + } + private: + RepXVisitorWriter& operator=(const RepXVisitorWriter&); + }; + + template<> + struct RepXVisitorWriter : RepXVisitorWriterBase + { + RepXVisitorWriter( TNameStack& ns, XmlWriter& writer, const PxShape* obj, MemoryBuffer& buf, PxCollection& collection ) + : RepXVisitorWriterBase( ns, writer, obj, buf, collection ) + { + } + + RepXVisitorWriter( const RepXVisitorWriter& other ) + : RepXVisitorWriterBase( other ) + { + } + + template + inline void writeGeometryProperty( const PxShapeGeometryProperty& inProp, const char* inTypeName ) + { + pushName( "Geometry" ); + pushName( inTypeName ); + GeometryType theType; + inProp.getGeometry( mObj, theType ); + PxClassInfoTraits theTraits; + PxU32 count = theTraits.Info.totalPropertyCount(); + if(count) + { + handleComplexObj( *this, &theType, theTraits.Info); + } + else + { + writeProperty(mWriter, mTempBuffer, inTypeName); + } + popName(); + popName(); + } + + void handleGeometryProperty( const PxShapeGeometryProperty& inProp ) + { + switch( mObj->getGeometryType() ) + { + case PxGeometryType::eSPHERE: writeGeometryProperty( inProp, "PxSphereGeometry" ); break; + case PxGeometryType::ePLANE: writeGeometryProperty( inProp, "PxPlaneGeometry" ); break; + case PxGeometryType::eCAPSULE: writeGeometryProperty( inProp, "PxCapsuleGeometry" ); break; + case PxGeometryType::eBOX: writeGeometryProperty( inProp, "PxBoxGeometry" ); break; + case PxGeometryType::eCONVEXMESH: writeGeometryProperty( inProp, "PxConvexMeshGeometry" ); break; + case PxGeometryType::eTRIANGLEMESH: writeGeometryProperty( inProp, "PxTriangleMeshGeometry" ); break; + case PxGeometryType::eHEIGHTFIELD: writeGeometryProperty( inProp, "PxHeightFieldGeometry" ); break; + case PxGeometryType::eINVALID: + case PxGeometryType::eGEOMETRY_COUNT: + PX_ASSERT( false ); + } + } + }; + + template + inline void writeAllProperties( TNameStack& inNameStack, const TObjType* inObj, XmlWriter& writer, MemoryBuffer& buffer, PxCollection& collection ) + { + RepXVisitorWriter newVisitor( inNameStack, writer, inObj, buffer, collection ); + RepXPropertyFilter > theOp( newVisitor ); + PxClassInfoTraits info; + info.Info.visitBaseProperties( theOp ); + info.Info.visitInstanceProperties( theOp ); + } + + template + inline void writeAllProperties( TNameStack& inNameStack, TObjType* inObj, XmlWriter& writer, MemoryBuffer& buffer, PxCollection& collection ) + { + RepXVisitorWriter newVisitor( inNameStack, writer, inObj, buffer, collection ); + RepXPropertyFilter > theOp( newVisitor ); + PxClassInfoTraits info; + info.Info.visitBaseProperties( theOp ); + info.Info.visitInstanceProperties( theOp ); + } + + template + inline void writeAllProperties( const TObjType* inObj, XmlWriter& writer, MemoryBuffer& buffer, PxCollection& collection ) + { + TNameStack theNames( buffer.mManager->getWrapper() ); + writeAllProperties( theNames, inObj, writer, buffer, collection ); + } + + template + inline void handleComplexObj( TWriterType& oldVisitor, const TObjType* inObj, const TInfoType& /*info*/) + { + writeAllProperties( oldVisitor.mNameStack, inObj, oldVisitor.mWriter, oldVisitor.mTempBuffer, oldVisitor.mCollection ); + } + + template + inline void handleComplexObj( TWriterType& oldVisitor, const TObjType* inObj, TInfoType& /*info*/) + { + writeAllProperties( oldVisitor.mNameStack, inObj, oldVisitor.mWriter, oldVisitor.mTempBuffer, oldVisitor.mCollection ); + } + + template + inline void handleComplexObj( TWriterType& oldVisitor, const TObjType* inObj, const PxUnknownClassInfo& /*info*/) + { + writeProperty( oldVisitor.mWriter, oldVisitor.mCollection, oldVisitor.mTempBuffer, oldVisitor.topName(), *inObj ); + } + +} } +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h new file mode 100644 index 000000000..666374573 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_WRITER_H +#define PX_XML_WRITER_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx { + + struct PxRepXObject; + + /** + * Writer used by extensions to write elements to a file or database + */ + class XmlWriter + { + protected: + virtual ~XmlWriter(){} + public: + /** Write a key-value pair into the current item */ + virtual void write( const char* inName, const char* inData ) = 0; + /** Write an object id into the current item */ + virtual void write( const char* inName, const PxRepXObject& inLiveObject ) = 0; + /** Add a child that then becomes the current context */ + virtual void addAndGotoChild( const char* inName ) = 0; + /** Leave the current child */ + virtual void leaveChild() = 0; + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h b/src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h new file mode 100644 index 000000000..57fb564d2 --- /dev/null +++ b/src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h @@ -0,0 +1,189 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSX_GPU_H +#define PX_PHYSX_GPU_H + +#include "task/PxTask.h" + +#include "Ps.h" +#include "PsArray.h" +#include "foundation/PxBounds3.h" +#include "common/PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PxSceneDesc.h" + + +namespace physx +{ + +class PxFoundation; +class PxCudaContextManagerDesc; +class PxvNphaseImplementationContext; +class PxsContext; +class PxsKernelWranglerManager; +class PxvNphaseImplementationFallback; +struct PxgDynamicsMemoryConfig; +class PxsMemoryManager; +class PxsHeapMemoryAllocatorManager; +class PxsSimulationController; +class PxsSimulationControllerCallback; +class PxDelayLoadHook; + +struct PxvSimStats; + +namespace Bp +{ + class BroadPhase; +} + +namespace Dy +{ + class Context; +} + +namespace IG +{ + class IslandSim; + class SimpleIslandManager; +} + +namespace Cm +{ + class FlushPool; +} + +/** +\brief Interface to create and run CUDA enabled PhysX features. + +The methods of this interface are expected not to be called concurrently. +Also they are expected to not be called concurrently with any tasks spawned before the end pipeline ... TODO make clear. +*/ +class PxPhysXGpu +{ +public: + /** + \brief Closes this instance of the interface. + */ + virtual void release() = 0; + + /** + Create GPU memory manager. + */ + virtual PxsMemoryManager* createGpuMemoryManager(PxGpuDispatcher* gpuDispatcher, class PxGraphicsContextManager* graphicsContextManager) = 0; + + virtual PxsHeapMemoryAllocatorManager* createGpuHeapMemoryAllocatorManager( + const PxU32 heapCapacity, + PxsMemoryManager* memoryManager, + const PxU32 gpuComputeVersion) = 0; + + /** + Create GPU kernel wrangler manager. + */ + virtual PxsKernelWranglerManager* createGpuKernelWranglerManager( + PxGpuDispatcher* gpuDispatcher, + PxErrorCallback& errorCallback, + const PxU32 gpuComputeVersion) = 0; + + /** + Create GPU broadphase. + */ + virtual Bp::BroadPhase* createGpuBroadPhase( + PxsKernelWranglerManager* gpuKernelWrangler, + PxGpuDispatcher* gpuDispatch, + PxGraphicsContextManager* graphicsContext, + const PxU32 gpuComputeVersion, + const PxgDynamicsMemoryConfig& config, + PxsHeapMemoryAllocatorManager* heapMemoryManager) = 0; + + /** + Create GPU narrow phase context. + */ + virtual PxvNphaseImplementationContext* createGpuNphaseImplementationContext(PxsContext& context, + PxsKernelWranglerManager* gpuKernelWrangler, + PxvNphaseImplementationFallback* fallbackForUnsupportedCMs, + const PxgDynamicsMemoryConfig& gpuDynamicsConfig, void* contactStreamBase, void* patchStreamBase, void* forceAndIndiceStreamBase, + Ps::Array& bounds, IG::IslandSim* islandSim, + physx::Dy::Context* dynamicsContext, const PxU32 gpuComputeVersion, PxsHeapMemoryAllocatorManager* heapMemoryManager) = 0; + + /** + Create GPU simulation controller. + */ + virtual PxsSimulationController* createGpuSimulationController(PxsKernelWranglerManager* gpuWranglerManagers, + PxGpuDispatcher* gpuDispatcher, PxGraphicsContextManager* graphicsContextManager, + Dy::Context* dynamicContext, PxvNphaseImplementationContext* npContext, Bp::BroadPhase* bp, + const bool useGpuBroadphase, IG::SimpleIslandManager* simpleIslandSim, + PxsSimulationControllerCallback* callback, const PxU32 gpuComputeVersion, PxsHeapMemoryAllocatorManager* heapMemoryManager) = 0; + + /** + Create GPU dynamics context. + */ + virtual Dy::Context* createGpuDynamicsContext(Cm::FlushPool& taskPool, PxsKernelWranglerManager* gpuKernelWragler, + PxGpuDispatcher* gpuDispatcher, PxGraphicsContextManager* graphicsContextManager, + const PxgDynamicsMemoryConfig& config, IG::IslandSim* accurateIslandSim, const PxU32 maxNumPartitions, + const bool enableStabilization, const bool useEnhancedDeterminism, const bool useAdaptiveForce, const PxReal maxBiasCoefficient, + const PxU32 gpuComputeVersion, PxvSimStats& simStats, PxsHeapMemoryAllocatorManager* heapMemoryManager, + const bool frictionEveryIteration, PxSolverType::Enum solverType) = 0; + +}; + +} + +#if PX_WINDOWS +/** +Sets delay load hook instance for PhysXGpu dll. +*/ +PX_C_EXPORT PX_PHYSX_GPU_API void PX_CALL_CONV PxSetPhysXGpuDelayLoadHook(const physx::PxDelayLoadHook* hook); +#endif + +/** +Create PxPhysXGpu interface class. +*/ +PX_C_EXPORT PX_PHYSX_GPU_API physx::PxPhysXGpu* PX_CALL_CONV PxCreatePhysXGpu(); + +/** +Create a cuda context manager. +*/ +PX_C_EXPORT PX_PHYSX_GPU_API physx::PxCudaContextManager* PX_CALL_CONV PxCreateCudaContextManager(physx::PxFoundation& foundation, const physx::PxCudaContextManagerDesc& desc); + +/** +Query the device ordinal - depends on control panel settings. +*/ +PX_C_EXPORT PX_PHYSX_GPU_API int PX_CALL_CONV PxGetSuggestedCudaDeviceOrdinal(physx::PxErrorCallback& errc); + +namespace grid +{ + class ServerImpl; + class ClientContextPredictionManager; +} + +PX_C_EXPORT PX_PHYSX_GPU_API grid::ClientContextPredictionManager* PX_CALL_CONV PxCreateCudaClientContextManager(grid::ServerImpl* server, physx::PxU32 maxNbSleepMsg); + +#endif // PX_PHYSX_GPU_H diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h new file mode 100644 index 000000000..27933da36 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h @@ -0,0 +1,352 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PVD_META_DATA_DEFINE_PROPERTIES_H +#define PVD_META_DATA_DEFINE_PROPERTIES_H + +#if PX_SUPPORT_PVD + +#include "PvdMetaDataPropertyVisitor.h" +#include "PxPvdDataStreamHelpers.h" +#include "PxPvdDataStream.h" +#include "PxCoreUtilityTypes.h" + + +namespace physx +{ +namespace Vd +{ + using namespace physx::shdfnd; + using namespace physx::pvdsdk; + + template + struct PropertyDefinitionOp + { + void defineProperty( PvdPropertyDefinitionHelper& mHelper, NamespacedName mClassKey ) + { + mHelper.createProperty( mClassKey, "", getPvdNamespacedNameForType(), PropertyType::Scalar ); + } + }; + template<> + struct PropertyDefinitionOp + { + void defineProperty( PvdPropertyDefinitionHelper& mHelper, NamespacedName mClassKey ) + { + mHelper.createProperty( mClassKey, "", getPvdNamespacedNameForType(), PropertyType::Scalar ); + } + }; +#define DEFINE_PROPERTY_DEFINITION_OP_NOP( type ) \ + template<> struct PropertyDefinitionOp { void defineProperty( PvdPropertyDefinitionHelper&, NamespacedName ){} }; + + //NOP out these two types. + DEFINE_PROPERTY_DEFINITION_OP_NOP( PxStridedData ) + DEFINE_PROPERTY_DEFINITION_OP_NOP( PxBoundedData ) + +#define DEFINE_PROPERTY_DEFINITION_OBJECT_REF( type ) \ + template<> struct PropertyDefinitionOp { \ + void defineProperty( PvdPropertyDefinitionHelper& mHelper, NamespacedName mClassKey) \ + { \ + mHelper.createProperty( mClassKey, "", getPvdNamespacedNameForType(), PropertyType::Scalar ); \ + } \ + }; + + DEFINE_PROPERTY_DEFINITION_OBJECT_REF( PxTriangleMesh* ) + DEFINE_PROPERTY_DEFINITION_OBJECT_REF( PxBVH33TriangleMesh* ) + DEFINE_PROPERTY_DEFINITION_OBJECT_REF( PxBVH34TriangleMesh* ) + DEFINE_PROPERTY_DEFINITION_OBJECT_REF( PxConvexMesh* ) + DEFINE_PROPERTY_DEFINITION_OBJECT_REF( PxHeightField* ) + + +struct PvdClassInfoDefine +{ + PvdPropertyDefinitionHelper& mHelper; + NamespacedName mClassKey; + + PvdClassInfoDefine( PvdPropertyDefinitionHelper& info, NamespacedName inClassName ) + : mHelper( info ) + , mClassKey( inClassName ) { } + + PvdClassInfoDefine( const PvdClassInfoDefine& other ) + : mHelper( other.mHelper ) + , mClassKey( other.mClassKey ) + { + } + + void defineProperty( NamespacedName inDtype, const char* semantic = "", PropertyType::Enum inPType = PropertyType::Scalar ) + { + mHelper.createProperty( mClassKey, semantic, inDtype, inPType ); + } + + void pushName( const char* inName ) + { + mHelper.pushName( inName ); + } + + void pushBracketedName( const char* inName) + { + mHelper.pushBracketedName( inName ); + } + + void popName() + { + mHelper.popName(); + } + + inline void defineNameValueDefs( const PxU32ToName* theConversions ) + { + while( theConversions->mName != NULL ) + { + mHelper.addNamedValue( theConversions->mName, theConversions->mValue ); + ++theConversions; + } + } + + template + void simpleProperty( PxU32, TAccessorType& /*inProp */) + { + typedef typename TAccessorType::prop_type TPropertyType; + PropertyDefinitionOp().defineProperty( mHelper, mClassKey ); + } + + template + void extendedIndexedProperty( PxU32* key, const TAccessorType& inProp, TInfoType& ) + { + simpleProperty(*key, inProp); + } + + template + static NamespacedName getNameForEnumType() + { + size_t s = sizeof( TDataType ); + switch(s) + { + case 1: return getPvdNamespacedNameForType(); + case 2: return getPvdNamespacedNameForType(); + case 4: return getPvdNamespacedNameForType(); + default: return getPvdNamespacedNameForType(); + } + } + + template + void enumProperty( PxU32 /*key*/, TAccessorType& /*inProp*/, const PxU32ToName* inConversions ) + { + typedef typename TAccessorType::prop_type TPropType; + defineNameValueDefs( inConversions ); + defineProperty( getNameForEnumType(), "Enumeration Value" ); + } + + template + void flagsProperty( PxU32 /*key*/, const TAccessorType& /*inAccessor*/, const PxU32ToName* inConversions ) + { + typedef typename TAccessorType::prop_type TPropType; + defineNameValueDefs( inConversions ); + defineProperty( getNameForEnumType(), "Bitflag" ); + } + + template + void complexProperty( PxU32* key, const TAccessorType& inAccessor, TInfoType& inInfo ) + { + PxU32 theOffset = inAccessor.mOffset; + inInfo.visitBaseProperties( makePvdPropertyFilter( *this, key, &theOffset ) ); + inInfo.visitInstanceProperties( makePvdPropertyFilter( *this, key, &theOffset ) ); + } + + template + void bufferCollectionProperty( PxU32* key, const TAccessorType& inAccessor, TInfoType& inInfo ) + { + complexProperty(key, inAccessor, inInfo); + } + + template + void handleBuffer( const PxBufferPropertyInfo&, TEnableFlag>& inProp ) + { + mHelper.pushName( inProp.mName ); + defineProperty( getPvdNamespacedNameForType(), "", PropertyType::Array ); + mHelper.popName(); + } + + template + void handleCollection( const PxReadOnlyCollectionPropertyInfo& inProp ) + { + mHelper.pushName( inProp.mName ); + defineProperty( getPvdNamespacedNameForType(), "", PropertyType::Array ); + mHelper.popName(); + } + + template + void handleCollection( const PxReadOnlyCollectionPropertyInfo& inProp, const PxU32ToName* inConversions ) + { + mHelper.pushName( inProp.mName ); + defineNameValueDefs( inConversions ); + defineProperty( getNameForEnumType(), "Enumeration Value", PropertyType::Array ); + mHelper.popName(); + } + +private: + PvdClassInfoDefine& operator=(const PvdClassInfoDefine&); +}; + +template +struct SimplePropertyValueStructOp +{ + void addPropertyMessageArg( PvdPropertyDefinitionHelper& mHelper, PxU32 inOffset ) + { + mHelper.addPropertyMessageArg( inOffset ); + } +}; + +#define DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_OP_NOP( type ) \ +template<> struct SimplePropertyValueStructOp { void addPropertyMessageArg( PvdPropertyDefinitionHelper&, PxU32 ){}}; + +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_OP_NOP( PxStridedData ) +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_OP_NOP( PxBoundedData ) + +#define DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( type ) \ +template<> struct SimplePropertyValueStructOp { \ +void addPropertyMessageArg( PvdPropertyDefinitionHelper& mHelper, PxU32 inOffset ) \ +{ \ + mHelper.addPropertyMessageArg( inOffset ); \ +} \ +}; + +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( PxTriangleMesh* ) +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( PxBVH33TriangleMesh* ) +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( PxBVH34TriangleMesh* ) +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( PxConvexMesh* ) +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( PxHeightField* ) + + +struct PvdClassInfoValueStructDefine +{ +private: + PvdClassInfoValueStructDefine& operator=(const PvdClassInfoValueStructDefine&); +public: + + PvdPropertyDefinitionHelper& mHelper; + + PvdClassInfoValueStructDefine( PvdPropertyDefinitionHelper& info ) + : mHelper( info ) + { } + + PvdClassInfoValueStructDefine( const PvdClassInfoValueStructDefine& other ) + : mHelper( other.mHelper ) + { + } + + void defineValueStructOffset( const ValueStructOffsetRecord& inProp, PxU32 inPropSize ) + { + if( inProp.mHasValidOffset ) + { + switch( inPropSize ) + { + case 8: mHelper.addPropertyMessageArg( inProp.mOffset ); break; + case 4: mHelper.addPropertyMessageArg( inProp.mOffset ); break; + case 2: mHelper.addPropertyMessageArg( inProp.mOffset ); break; + default: + PX_ASSERT(1 == inPropSize); + mHelper.addPropertyMessageArg( inProp.mOffset ); break; + } + } + } + + void pushName( const char* inName ) + { + mHelper.pushName( inName ); + } + + void pushBracketedName( const char* inName) + { + mHelper.pushBracketedName( inName ); + } + + void popName() + { + mHelper.popName(); + } + + template + void bufferCollectionProperty( PxU32* /*key*/, const TAccessorType& /*inAccessor*/, TInfoType& /*inInfo*/ ) + { + //complexProperty(key, inAccessor, inInfo); + } + + template + void simpleProperty( PxU32 /*key*/, TAccessorType& inProp ) + { + typedef typename TAccessorType::prop_type TPropertyType; + if ( inProp.mHasValidOffset ) + { + SimplePropertyValueStructOp().addPropertyMessageArg( mHelper, inProp.mOffset ); + } + } + + template + void enumProperty( PxU32 /*key*/, TAccessorType& inAccessor, const PxU32ToName* /*inConversions */) + { + typedef typename TAccessorType::prop_type TPropType; + defineValueStructOffset( inAccessor, sizeof( TPropType ) ); + } + + template + void flagsProperty( PxU32 /*key*/, const TAccessorType& inAccessor, const PxU32ToName* /*inConversions */) + { + typedef typename TAccessorType::prop_type TPropType; + defineValueStructOffset( inAccessor, sizeof( TPropType ) ); + } + + template + void complexProperty( PxU32* key, const TAccessorType& inAccessor, TInfoType& inInfo ) + { + PxU32 theOffset = inAccessor.mOffset; + inInfo.visitBaseProperties( makePvdPropertyFilter( *this, key, &theOffset ) ); + inInfo.visitInstanceProperties( makePvdPropertyFilter( *this, key, &theOffset ) ); + } + + template + void handleCollection( const PxReadOnlyCollectionPropertyInfo& /*prop*/ ) + { + } + + template + void handleCollection( const PxReadOnlyCollectionPropertyInfo& /*prop*/, const PxU32ToName* /*inConversions */) + { + } + + template + void handleCollection( const PxBufferCollectionPropertyInfo& /*prop*/, const TInfoType& /*inInfo */) + { + } +}; + +} + +} + +#endif +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h new file mode 100644 index 000000000..2e067ab49 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h @@ -0,0 +1,318 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_META_DATA_EXTENSIONS_H +#define PX_META_DATA_EXTENSIONS_H +#include "PxMetaDataObjects.h" + +#if PX_SUPPORT_PVD +#include "PxPvdObjectModelBaseTypes.h" + +namespace physx { namespace pvdsdk { + + template<> PX_INLINE NamespacedName getPvdNamespacedNameForType() { return getPvdNamespacedNameForType(); } + template<> PX_INLINE NamespacedName getPvdNamespacedNameForType() { return getPvdNamespacedNameForType(); } + +}} +#endif + +namespace physx +{ +namespace Vd +{ +//Additional properties that exist only in pvd land. +struct PxPvdOnlyProperties +{ + enum Enum + { + FirstProp = PxPropertyInfoName::LastPxPropertyInfoName, + PxScene_Frame, + PxScene_Contacts, + PxScene_SimulateElapsedTime, +#define DEFINE_ENUM_RANGE( stem, count ) \ + stem##Begin, \ + stem##End = stem##Begin + count + + //I can't easily add up the number of required property entries, but it is large due to the below + //geometry count squared properties. Thus I punt and allocate way more than I need right now. + DEFINE_ENUM_RANGE( PxScene_SimulationStatistics, 1000 ), + DEFINE_ENUM_RANGE( PxSceneDesc_Limits, PxPropertyInfoName::PxSceneLimits_PropertiesStop - PxPropertyInfoName::PxSceneLimits_PropertiesStart ), + DEFINE_ENUM_RANGE( PxSimulationStatistics_NumShapes, PxGeometryType::eGEOMETRY_COUNT ), + DEFINE_ENUM_RANGE( PxSimulationStatistics_NumDiscreteContactPairs, PxGeometryType::eGEOMETRY_COUNT * PxGeometryType::eGEOMETRY_COUNT ), + DEFINE_ENUM_RANGE( PxSimulationStatistics_NumModifiedContactPairs, PxGeometryType::eGEOMETRY_COUNT * PxGeometryType::eGEOMETRY_COUNT ), + DEFINE_ENUM_RANGE( PxSimulationStatistics_NumSweptIntegrationPairs, PxGeometryType::eGEOMETRY_COUNT * PxGeometryType::eGEOMETRY_COUNT ), + DEFINE_ENUM_RANGE( PxSimulationStatistics_NumTriggerPairs, PxGeometryType::eGEOMETRY_COUNT * PxGeometryType::eGEOMETRY_COUNT ), + DEFINE_ENUM_RANGE( PxRigidDynamic_SolverIterationCounts, 2 ), + DEFINE_ENUM_RANGE( PxArticulation_SolverIterationCounts, 2 ), + DEFINE_ENUM_RANGE( PxArticulationJoint_SwingLimit, 2 ), + DEFINE_ENUM_RANGE( PxArticulationJoint_TwistLimit, 2 ), + DEFINE_ENUM_RANGE( PxConvexMeshGeometry_Scale, PxPropertyInfoName::PxMeshScale_PropertiesStop - PxPropertyInfoName::PxMeshScale_PropertiesStart ), + DEFINE_ENUM_RANGE( PxTriangleMeshGeometry_Scale, PxPropertyInfoName::PxMeshScale_PropertiesStop - PxPropertyInfoName::PxMeshScale_PropertiesStart ), + + LastPxPvdOnlyProperty + }; +}; + +template +struct PxBufferPropertyInfo : PxReadOnlyPropertyInfo< TKey, TObjectType, TPropertyType > +{ + typedef PxReadOnlyPropertyInfo< TKey, TObjectType, TPropertyType > TBaseType; + typedef typename TBaseType::TGetterType TGetterType; + PxBufferPropertyInfo( const char* inName, TGetterType inGetter ) + : TBaseType( inName, inGetter ) + { + } + bool isEnabled( PxU32 inFlags ) const { return (inFlags & TEnableFlag) > 0; } +}; + + +#define DECLARE_BUFFER_PROPERTY( objectType, baseType, propType, propName, fieldName, flagName ) \ +typedef PxBufferPropertyInfo< PxPvdOnlyProperties::baseType##_##propName, objectType, propType, flagName > T##objectType##propName##Base; \ +inline propType get##propName( const objectType* inData ) { return inData->fieldName; } \ +struct baseType##propName##Property : T##objectType##propName##Base \ +{ \ + baseType##propName##Property() : T##objectType##propName##Base( #propName, get##propName ){} \ +}; + +template +struct IndexerToNameMap +{ + PxEnumTraits Converter; +}; + +struct ValueStructOffsetRecord +{ + mutable bool mHasValidOffset; + mutable PxU32 mOffset; + ValueStructOffsetRecord() : mHasValidOffset( false ), mOffset( 0 ) {} + void setupValueStructOffset( PxU32 inValue ) const + { + mHasValidOffset = true; + mOffset = inValue; + } +}; + +template +struct PxPvdReadOnlyPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxReadOnlyPropertyInfo TPropertyInfoType; + typedef TPropertyType prop_type; + + const TPropertyInfoType mProperty; + PxPvdReadOnlyPropertyAccessor( const TPropertyInfoType& inProp ) + : mProperty( inProp ) + { + } + prop_type get( const TObjectType* inObj ) const { return mProperty.get( inObj ); } + +private: + PxPvdReadOnlyPropertyAccessor& operator=(const PxPvdReadOnlyPropertyAccessor&); +}; + +template +struct PxBufferCollectionPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxBufferCollectionPropertyInfo< TKey, TObjectType, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + const TPropertyInfoType& mProperty; + const char* mName; + + PxBufferCollectionPropertyAccessor( const TPropertyInfoType& inProp, const char* inName ) + : mProperty( inProp ) + , mName( inName ) + { + } + + const char* name() const { return mName; } + PxU32 size( const TObjectType* inObj ) const { return mProperty.size( inObj ); } + PxU32 get( const TObjectType* inObj, prop_type* buffer, PxU32 inNumItems) const { return mProperty.get( inObj, buffer, inNumItems); } + void set( TObjectType* inObj, prop_type* inBuffer, PxU32 inNumItems ) const { mProperty.set( inObj, inBuffer, inNumItems ); } +}; + +template +struct PxPvdIndexedPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxIndexedPropertyInfo< TKey, TObjectType, TIndexType, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + TIndexType mIndex; + const TPropertyInfoType& mProperty; + PxPvdIndexedPropertyAccessor( const TPropertyInfoType& inProp, PxU32 inIndex ) + : mIndex( static_cast( inIndex ) ) + , mProperty( inProp ) + { + } + prop_type get( const TObjectType* inObj ) const { return mProperty.get( inObj, mIndex ); } + void set( TObjectType* inObj, prop_type val ) const { mProperty.set( inObj, mIndex, val ); } + + void operator = (PxPvdIndexedPropertyAccessor&) {} +}; + +template +struct PxPvdExtendedIndexedPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxExtendedIndexedPropertyInfo< TKey, TObjectType, TIndexType, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + TIndexType mIndex; + const TPropertyInfoType& mProperty; + PxPvdExtendedIndexedPropertyAccessor( const TPropertyInfoType& inProp, PxU32 inIndex ) + : mIndex( static_cast( inIndex ) ) + , mProperty( inProp ) + { + } + + PxU32 size( const TObjectType* inObj ) const { return mProperty.size( inObj ); } + prop_type get( const TObjectType* inObj, TIndexType index ) const { return mProperty.get( inObj, index ); } + void set( TObjectType* inObj, TIndexType index, prop_type val ) const { mProperty.set( inObj, index, val ); } + + void operator = (PxPvdExtendedIndexedPropertyAccessor&) {} +}; + +template +struct PxPvdFixedSizeLookupTablePropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxFixedSizeLookupTablePropertyInfo< TKey, TObjectType, TIndexType, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + TIndexType mIndex; + + const TPropertyInfoType& mProperty; + PxPvdFixedSizeLookupTablePropertyAccessor( const TPropertyInfoType& inProp, const PxU32 inIndex3 ) + : mIndex( static_cast( inIndex3 ) ) + , mProperty( inProp ) + { + } + + PxU32 size( const TObjectType* inObj ) const { return mProperty.size( inObj ); } + prop_type getX( const TObjectType* inObj, const TIndexType index ) const { return mProperty.getX( inObj, index ); } + prop_type getY( const TObjectType* inObj, const TIndexType index ) const { return mProperty.getY( inObj, index ); } + void addPair( TObjectType* inObj, const PxReal x, const PxReal y ) { const_cast(mProperty).addPair( inObj, x, y ); } + void clear( TObjectType* inObj ) { const_cast(mProperty).clear( inObj ); } + void operator = (PxPvdFixedSizeLookupTablePropertyAccessor&) {} +}; + +template +struct PxPvdDualIndexedPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxDualIndexedPropertyInfo< TKey, TObjectType, TIdx0Type, TIdx1Type, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + TIdx0Type mIdx0; + TIdx1Type mIdx1; + const TPropertyInfoType& mProperty; + + PxPvdDualIndexedPropertyAccessor( const TPropertyInfoType& inProp, PxU32 idx0, PxU32 idx1 ) + : mIdx0( static_cast( idx0 ) ) + , mIdx1( static_cast( idx1 ) ) + , mProperty( inProp ) + { + } + prop_type get( const TObjectType* inObj ) const { return mProperty.get( inObj, mIdx0, mIdx1 ); } + void set( TObjectType* inObj, prop_type val ) const { mProperty.set( inObj, mIdx0, mIdx1, val ); } + +private: + PxPvdDualIndexedPropertyAccessor& operator = (const PxPvdDualIndexedPropertyAccessor&); +}; + +template +struct PxPvdExtendedDualIndexedPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxExtendedDualIndexedPropertyInfo< TKey, TObjectType, TIdx0Type, TIdx1Type, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + TIdx0Type mIdx0; + TIdx1Type mIdx1; + const TPropertyInfoType& mProperty; + + PxPvdExtendedDualIndexedPropertyAccessor( const TPropertyInfoType& inProp, PxU32 idx0, PxU32 idx1 ) + : mIdx0( static_cast( idx0 ) ) + , mIdx1( static_cast( idx1 ) ) + , mProperty( inProp ) + { + } + prop_type get( const TObjectType* inObj ) const { return mProperty.get( inObj, mIdx0, mIdx1 ); } + void set( TObjectType* inObj, prop_type val ) const { mProperty.set( inObj, mIdx0, mIdx1, val ); } + +private: + PxPvdExtendedDualIndexedPropertyAccessor& operator = (const PxPvdExtendedDualIndexedPropertyAccessor&); +}; + +template +struct PxPvdRangePropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxRangePropertyInfo TPropertyInfoType; + typedef TPropertyType prop_type; + bool mFirstValue; + const TPropertyInfoType& mProperty; + + PxPvdRangePropertyAccessor( const TPropertyInfoType& inProp, bool inFirstValue ) + : mFirstValue( inFirstValue ) + , mProperty( inProp ) + { + } + + prop_type get( const TObjType* inObj ) const { + prop_type first,second; + mProperty.get( inObj, first, second ); + return mFirstValue ? first : second; + } + void set( TObjType* inObj, prop_type val ) const + { + prop_type first,second; + mProperty.get( inObj, first, second ); + if ( mFirstValue ) mProperty.set( inObj, val, second ); + else mProperty.set( inObj, first, val ); + } + + void operator = (PxPvdRangePropertyAccessor&) {} +}; + + +template +struct IsFlagsType +{ + bool FlagData; +}; + +template +struct IsFlagsType > +{ + const PxU32ToName* FlagData; + IsFlagsType > () : FlagData( PxEnumTraits().NameConversion ) {} +}; + + + +template +struct PvdClassForType +{ + bool Unknown; +}; + +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h new file mode 100644 index 000000000..6509ef818 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h @@ -0,0 +1,539 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_META_DATA_PROPERTY_VISITOR_H +#define PX_META_DATA_PROPERTY_VISITOR_H + +#include +#include "PvdMetaDataExtensions.h" +namespace physx +{ + +namespace Vd +{ + +//PVD only deals with read-only properties, indexed, and properties like this by in large. +//so we have a filter that expands properties to a level where we can get to them and expands them into +//named functions that make more sense and are easier to read than operator() +template +struct PvdPropertyFilter +{ + +private: + PvdPropertyFilter& operator=(const PvdPropertyFilter&); + +public: + + TOperatorType mOperator; + PxU32* mKeyOverride; + PxU32* mOffsetOverride; + + PvdPropertyFilter( TOperatorType& inOperator ) + : mOperator( inOperator ) + , mKeyOverride( 0 ) + , mOffsetOverride( 0 ) {} + + PvdPropertyFilter( TOperatorType& inOperator, PxU32* inKeyOverride, PxU32* inOffsetOverride ) + : mOperator( inOperator ) + , mKeyOverride( inKeyOverride ) + , mOffsetOverride( inOffsetOverride ) {} + + PvdPropertyFilter( const PvdPropertyFilter& inOther ) : mOperator( inOther.mOperator ), mKeyOverride( inOther.mKeyOverride ), mOffsetOverride( inOther.mOffsetOverride ) {} + + template + void dispatchAccessor( PxU32 inKey, const TAccessorType& inAccessor, bool, bool, bool) + { + mOperator.simpleProperty(inKey, inAccessor ); + } + + template + void dispatchAccessor( PxU32 inKey, const TAccessorType& inAccessor, bool, bool, const PxU32ToName* inConversions ) + { + mOperator.flagsProperty(inKey, inAccessor, inConversions ); + } + + template + void dispatchAccessor( PxU32 inKey, const TAccessorType& inAccessor, const PxU32ToName* inConversions, bool, bool ) + { + mOperator.enumProperty( inKey, inAccessor, inConversions ); + } + + template + void dispatchAccessor(PxU32, const TAccessorType& inAccessor, bool, const TInfoType* inInfo, bool ) + { + PxU32 rangeStart = TKey; + PxU32& propIdx = mKeyOverride == NULL ? rangeStart : *mKeyOverride; + mOperator.complexProperty( &propIdx, inAccessor, *inInfo ); + } + + PxU32 getKeyValue( PxU32 inPropertyKey ) + { + PxU32 retval = inPropertyKey; + if ( mKeyOverride ) + { + retval = *mKeyOverride; + (*mKeyOverride)++; + } + return retval; + } + + + void setupValueStructOffset( const ValueStructOffsetRecord&, bool, PxU32* ) {} + void setupValueStructOffset( const ValueStructOffsetRecord& inAccessor, PxU32 inOffset, PxU32* inAdditionalOffset ) + { + //This allows us to nest properties correctly. + if ( inAdditionalOffset ) inOffset += *inAdditionalOffset; + inAccessor.setupValueStructOffset( inOffset ); + } + + template + void handleAccessor( PxU32 inKey, const TAccessorType& inAccessor ) + { + typedef typename TAccessorType::prop_type TPropertyType; + dispatchAccessor( inKey + , inAccessor + , PxEnumTraits().NameConversion + , PxClassInfoTraits().getInfo() + , IsFlagsType().FlagData ); + } + + + template + void handleAccessor( const TAccessorType& inAccessor ) + { + setupValueStructOffset( inAccessor, PxPropertyToValueStructMemberMap().Offset, mOffsetOverride ); + handleAccessor( getKeyValue( TKey ), inAccessor ); + } + + template + void operator()( const PxReadOnlyPropertyInfo& inProperty, PxU32 ) + { + PxPvdReadOnlyPropertyAccessor< TKey, TObjType, TPropertyType > theAccessor( inProperty ); + mOperator.pushName( inProperty.mName ); + handleAccessor( theAccessor ); + mOperator.popName(); + } + + //We don't handle unbounded indexed properties + template + void indexedProperty( PxU32, const PxIndexedPropertyInfo&, bool, TValueConversionType, const TInfoType& ) {} + + + template + void indexedProperty( PxU32, const PxIndexedPropertyInfo& inProp, const PxU32ToName* theConversions, const PxUnknownClassInfo& ) + { + mOperator.pushName( inProp.mName ); + PxU32 rangeStart = TKey; + PxU32& propIdx = mKeyOverride == NULL ? rangeStart : *mKeyOverride; + PxU32 theOffset = 0; + if ( mOffsetOverride ) theOffset = *mOffsetOverride; + + while( theConversions->mName != NULL ) + { + mOperator.pushBracketedName( theConversions->mName ); + PxPvdIndexedPropertyAccessor theAccessor( inProp, theConversions->mValue ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + handleAccessor( propIdx, theAccessor ); + mOperator.popName(); + ++propIdx; + ++theConversions; + theOffset += sizeof( TPropertyType ); + } + mOperator.popName(); + } + + template + void indexedProperty( PxU32, const PxIndexedPropertyInfo& inProp, const PxU32ToName* theConversions, const TInfoType& inInfo ) + { + //ouch, not nice. Indexed complex property. + mOperator.pushName( inProp.mName ); + PxU32 propIdx = TKey; + PxU32 theOffset = 0; + if ( mOffsetOverride ) theOffset = *mOffsetOverride; + + while( theConversions->mName != NULL ) + { + mOperator.pushBracketedName( theConversions->mName ); + PxPvdIndexedPropertyAccessor theAccessor( inProp, theConversions->mValue ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + PX_ASSERT( theAccessor.mHasValidOffset ); + mOperator.complexProperty( &propIdx, theAccessor, inInfo ); + mOperator.popName(); + ++theConversions; + theOffset += sizeof( TPropertyType ); + } + mOperator.popName(); + } + + static char* myStrcat(const char* a,const char * b) + { + size_t len = strlen(a)+strlen(b); + char* result = new char[len+1]; + + strcpy(result,a); + strcat(result,b); + result[len] = 0; + + return result; + } + + template + void handleBufferCollectionProperty(PxU32 , const PxBufferCollectionPropertyInfo& inProp, const TInfoType& inInfo) + { + //append 'Collection' to buffer properties + char* name = myStrcat(inProp.mName, "Collection"); + + mOperator.pushName(name); + PxU32 propIdx = TKey; + PxU32 theOffset = 0; + + PxBufferCollectionPropertyAccessor theAccessor( inProp, inProp.mName ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + + mOperator.bufferCollectionProperty( &propIdx, theAccessor, inInfo ); + mOperator.popName(); + delete []name; + } + + template + void operator()( const PxIndexedPropertyInfo& inProp, PxU32 inIndex ) + { + indexedProperty( inIndex, inProp, IndexerToNameMap().Converter.NameConversion + , PxClassInfoTraits().Info ); + } + + template + void handleExtendedIndexProperty(PxU32 inIndex, const PxExtendedIndexedPropertyInfo& inProp, const TInfoType& inInfo) + { + mOperator.pushName(inProp.mName); + PxU32 propIdx = TKey; + PxU32 theOffset = 0; + + PxPvdExtendedIndexedPropertyAccessor theAccessor( inProp, inIndex ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + + mOperator.extendedIndexedProperty( &propIdx, theAccessor, inInfo ); + mOperator.popName(); + } + + template + void handlePxFixedSizeLookupTableProperty( const PxU32 inIndex, const PxFixedSizeLookupTablePropertyInfo& inProp, const TInfoType& inInfo) + { + mOperator.pushName(inProp.mName); + + PxU32 propIdx = TKey; + PxU32 theOffset = 0; + + PxPvdFixedSizeLookupTablePropertyAccessor theAccessor( inProp, inIndex ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + + mOperator.PxFixedSizeLookupTableProperty( &propIdx, theAccessor, inInfo ); + + mOperator.popName(); + } + + template + void operator()( const PxExtendedIndexedPropertyInfo& inProp, PxU32 inIndex ) + { + handleExtendedIndexProperty( inIndex, inProp, PxClassInfoTraits().Info ); + } + + template + void operator()( const PxFixedSizeLookupTablePropertyInfo& inProp, PxU32 inIndex) + { + handlePxFixedSizeLookupTableProperty(inIndex, inProp, PxClassInfoTraits().Info); + } + + //We don't handle unbounded indexed properties + template + void dualIndexedProperty( PxU32 idx, const PxDualIndexedPropertyInfo&, TNameConv, TNameConv2 ) { PX_UNUSED(idx); } + + template + void dualIndexedProperty( PxU32 /*idx*/, const PxDualIndexedPropertyInfo& inProp, const PxU32ToName* c1, const PxU32ToName* c2 ) + { + mOperator.pushName( inProp.mName ); + PxU32 rangeStart = TKey; + PxU32& propIdx = mKeyOverride == NULL ? rangeStart : *mKeyOverride; + PxU32 theOffset = 0; + if ( mOffsetOverride ) theOffset = *mOffsetOverride; + while( c1->mName != NULL ) + { + mOperator.pushBracketedName( c1->mName ); + const PxU32ToName* c2Idx = c2; + while( c2Idx->mName != NULL ) + { + mOperator.pushBracketedName( c2Idx->mName ); + PxPvdDualIndexedPropertyAccessor theAccessor( inProp, c1->mValue, c2Idx->mValue ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + handleAccessor( propIdx, theAccessor ); + mOperator.popName(); + ++propIdx; + ++c2Idx; + theOffset += sizeof( TPropertyType ); + } + mOperator.popName(); + ++c1; + } + mOperator.popName(); + } + + template + void extendedDualIndexedProperty( PxU32 /*idx*/, const PxExtendedDualIndexedPropertyInfo& inProp, PxU32 id0Count, PxU32 id1Count ) + { + mOperator.pushName( inProp.mName ); + PxU32 rangeStart = TKey; + PxU32& propIdx = mKeyOverride == NULL ? rangeStart : *mKeyOverride; + PxU32 theOffset = 0; + if ( mOffsetOverride ) theOffset = *mOffsetOverride; + for(PxU32 i = 0; i < id0Count; ++i) + { + char buffer1[32] = { 0 }; + sprintf( buffer1, "eId1_%u", i ); + + mOperator.pushBracketedName( buffer1 ); + for(PxU32 j = 0; j < id1Count; ++j) + { + char buffer2[32] = { 0 }; + sprintf( buffer2, "eId2_%u", j ); + + mOperator.pushBracketedName( buffer2 ); + PxPvdExtendedDualIndexedPropertyAccessor theAccessor( inProp, i, j ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + handleAccessor( propIdx, theAccessor ); + mOperator.popName(); + ++propIdx; + theOffset += sizeof( TPropertyType ); + } + mOperator.popName(); + } + mOperator.popName(); + } + + template + void operator()( const PxDualIndexedPropertyInfo& inProp, PxU32 idx ) + { + dualIndexedProperty( idx, inProp + , IndexerToNameMap().Converter.NameConversion + , IndexerToNameMap().Converter.NameConversion ); + } + + template + void operator()( const PxExtendedDualIndexedPropertyInfo& inProp, PxU32 idx ) + { + extendedDualIndexedProperty( idx, inProp, inProp.mId0Count, inProp.mId1Count ); + } + + template + void operator()( const PxRangePropertyInfo& inProperty, PxU32 /*idx*/) + { + PxU32 rangeStart = TKey; + PxU32& propIdx = mKeyOverride == NULL ? rangeStart : *mKeyOverride; + PxU32 theOffset = 0; + if ( mOffsetOverride ) theOffset = *mOffsetOverride; + + mOperator.pushName( inProperty.mName ); + mOperator.pushName( inProperty.mArg0Name ); + PxPvdRangePropertyAccessor theAccessor( inProperty, true ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + handleAccessor( propIdx, theAccessor ); + ++propIdx; + theOffset += sizeof( TPropertyType ); + mOperator.popName(); + mOperator.pushName( inProperty.mArg1Name ); + theAccessor.mFirstValue = false; + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + handleAccessor( propIdx, theAccessor ); + mOperator.popName(); + mOperator.popName(); + } + + template + void operator()( const PxBufferCollectionPropertyInfo& inProp, PxU32 count ) + { + handleBufferCollectionProperty( count, inProp, PxClassInfoTraits().Info ); + } + + template + void handleBuffer( const PxBufferPropertyInfo& inProp ) + { + mOperator.handleBuffer( inProp ); + } + + template + void operator()( const PxBufferPropertyInfo& inProp, PxU32 ) + { + handleBuffer( inProp ); + } + + template + void operator()( const PxReadOnlyCollectionPropertyInfo& prop, PxU32 ) + { + mOperator.handleCollection( prop ); + } + + template + void operator()( const PxReadOnlyCollectionPropertyInfo& prop, PxU32 ) + { + mOperator.handleCollection( prop ); + } + + template + void operator()( const PxWriteOnlyPropertyInfo&, PxU32 ) {} + + template + void operator()( const PxReadOnlyCollectionPropertyInfo&, PxU32 ) {} + + template + void operator()( const PxReadOnlyFilteredCollectionPropertyInfo&, PxU32 ) {} + + + //We don't deal with these property datatypes. +#define DEFINE_PVD_PROPERTY_NOP(datatype) \ + template \ + void operator()( const PxReadOnlyPropertyInfo& inProperty, PxU32 ){PX_UNUSED(inProperty); } + + DEFINE_PVD_PROPERTY_NOP( const void* ) + DEFINE_PVD_PROPERTY_NOP( void* ) + DEFINE_PVD_PROPERTY_NOP( PxSimulationFilterCallback * ) + DEFINE_PVD_PROPERTY_NOP( physx::PxTaskManager * ) + DEFINE_PVD_PROPERTY_NOP( PxSimulationFilterShader * ) + DEFINE_PVD_PROPERTY_NOP( PxSimulationFilterShader) + DEFINE_PVD_PROPERTY_NOP( PxContactModifyCallback * ) + DEFINE_PVD_PROPERTY_NOP( PxCCDContactModifyCallback * ) + DEFINE_PVD_PROPERTY_NOP( PxSimulationEventCallback * ) + DEFINE_PVD_PROPERTY_NOP( physx::PxGpuDispatcher* ) + DEFINE_PVD_PROPERTY_NOP( physx::PxCpuDispatcher * ) + DEFINE_PVD_PROPERTY_NOP( PxRigidActor ) + DEFINE_PVD_PROPERTY_NOP( const PxRigidActor ) + DEFINE_PVD_PROPERTY_NOP( PxRigidActor& ) + DEFINE_PVD_PROPERTY_NOP( const PxRigidActor& ) + DEFINE_PVD_PROPERTY_NOP( PxScene* ) + DEFINE_PVD_PROPERTY_NOP( PxConstraint ) + DEFINE_PVD_PROPERTY_NOP( PxConstraint* ) + DEFINE_PVD_PROPERTY_NOP( PxConstraint& ) + DEFINE_PVD_PROPERTY_NOP( const PxConstraint& ) + DEFINE_PVD_PROPERTY_NOP( PxAggregate * ) + DEFINE_PVD_PROPERTY_NOP( PxArticulation& ) + DEFINE_PVD_PROPERTY_NOP( PxArticulationBase& ) + DEFINE_PVD_PROPERTY_NOP( PxArticulationReducedCoordinate&) + DEFINE_PVD_PROPERTY_NOP( const PxArticulationLink * ) + DEFINE_PVD_PROPERTY_NOP( const PxRigidDynamic * ) + DEFINE_PVD_PROPERTY_NOP( const PxRigidStatic * ) + DEFINE_PVD_PROPERTY_NOP( PxArticulationJointBase * ) + DEFINE_PVD_PROPERTY_NOP( PxArticulationJointReducedCoordinate * ) + DEFINE_PVD_PROPERTY_NOP( PxArticulationJoint * ) + DEFINE_PVD_PROPERTY_NOP( PxBroadPhaseCallback * ) + DEFINE_PVD_PROPERTY_NOP( const PxBroadPhaseRegion * ) + DEFINE_PVD_PROPERTY_NOP( PxU32 * ) + +}; + +template +inline PvdPropertyFilter makePvdPropertyFilter( TOperator inOperator ) +{ + return PvdPropertyFilter( inOperator ); +} + +template +inline PvdPropertyFilter makePvdPropertyFilter( TOperator inOperator, PxU32* inKey, PxU32* inOffset ) +{ + return PvdPropertyFilter( inOperator, inKey, inOffset ); +} + +template +inline void visitWithPvdFilter( TOperator inOperator, TFuncType inFuncType ) +{ + PX_UNUSED(inFuncType); + TFuncType( makePvdPropertyFilter( inOperator ) ); +} + +template +inline void visitInstancePvdProperties( TOperator inOperator ) +{ + visitInstanceProperties( makePvdPropertyFilter( inOperator ) ); +} + +template +struct PvdAllPropertyVisitor +{ + TOperator mOperator; + PvdAllPropertyVisitor( TOperator op ) : mOperator( op ) {} + template + bool operator()( const TObjectType* ) { visitInstancePvdProperties( mOperator ); return false; } +}; + + +template +struct PvdAllInfoVisitor +{ + TOperator mOperator; + PvdAllInfoVisitor( TOperator op ) : mOperator( op ) {} + template + void operator()( TInfoType inInfo ) + { + inInfo.template visitType( PvdAllPropertyVisitor( mOperator ) ); + inInfo.visitBases( *this ); + } +}; + + +template +inline void visitAllPvdProperties( TOperator inOperator ) +{ + visitAllProperties( makePvdPropertyFilter( inOperator ) ); +} + + + +template +inline void visitRigidDynamicPerFrameProperties( TOperator inOperator ) +{ + PvdPropertyFilter theFilter( inOperator ); + PxRigidDynamicGeneratedInfo theInfo; + theFilter( theInfo.GlobalPose, 0 ); + theFilter( theInfo.LinearVelocity, 1 ); + theFilter( theInfo.AngularVelocity, 2 ); + theFilter( theInfo.IsSleeping, 3 ); +} + +template +inline void visitArticulationLinkPerFrameProperties( TOperator inOperator ) +{ + PvdPropertyFilter theFilter( inOperator ); + PxArticulationLinkGeneratedInfo theInfo; + theFilter( theInfo.GlobalPose, 0 ); + theFilter( theInfo.LinearVelocity, 1 ); + theFilter( theInfo.AngularVelocity, 2 ); +} + +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h new file mode 100644 index 000000000..f8c538f75 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h @@ -0,0 +1,373 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +PxPhysics_PropertiesStart, +PxPhysics_TolerancesScale, +PxPhysics_TriangleMeshes, +PxPhysics_HeightFields, +PxPhysics_ConvexMeshes, +PxPhysics_BVHStructures, +PxPhysics_Scenes, +PxPhysics_Shapes, +PxPhysics_Materials, +PxPhysics_PropertiesStop, +PxMaterial_PropertiesStart, +PxMaterial_ReferenceCount, +PxMaterial_DynamicFriction, +PxMaterial_StaticFriction, +PxMaterial_Restitution, +PxMaterial_Flags, +PxMaterial_FrictionCombineMode, +PxMaterial_RestitutionCombineMode, +PxMaterial_ConcreteTypeName, +PxMaterial_UserData, +PxMaterial_PropertiesStop, +PxActor_PropertiesStart, +PxActor_Scene, +PxActor_Name, +PxActor_ActorFlags, +PxActor_DominanceGroup, +PxActor_OwnerClient, +PxActor_Aggregate, +PxActor_UserData, +PxActor_PropertiesStop, +PxRigidActor_PropertiesStart, +PxRigidActor_GlobalPose, +PxRigidActor_Shapes, +PxRigidActor_Constraints, +PxRigidActor_PropertiesStop, +PxRigidBody_PropertiesStart, +PxRigidBody_CMassLocalPose, +PxRigidBody_Mass, +PxRigidBody_InvMass, +PxRigidBody_MassSpaceInertiaTensor, +PxRigidBody_MassSpaceInvInertiaTensor, +PxRigidBody_LinearDamping, +PxRigidBody_AngularDamping, +PxRigidBody_LinearVelocity, +PxRigidBody_AngularVelocity, +PxRigidBody_MaxAngularVelocity, +PxRigidBody_MaxLinearVelocity, +PxRigidBody_RigidBodyFlags, +PxRigidBody_MinCCDAdvanceCoefficient, +PxRigidBody_MaxDepenetrationVelocity, +PxRigidBody_MaxContactImpulse, +PxRigidBody_PropertiesStop, +PxRigidDynamic_PropertiesStart, +PxRigidDynamic_IsSleeping, +PxRigidDynamic_SleepThreshold, +PxRigidDynamic_StabilizationThreshold, +PxRigidDynamic_RigidDynamicLockFlags, +PxRigidDynamic_WakeCounter, +PxRigidDynamic_SolverIterationCounts, +PxRigidDynamic_ContactReportThreshold, +PxRigidDynamic_ConcreteTypeName, +PxRigidDynamic_PropertiesStop, +PxRigidStatic_PropertiesStart, +PxRigidStatic_ConcreteTypeName, +PxRigidStatic_PropertiesStop, +PxArticulationLink_PropertiesStart, +PxArticulationLink_InboundJoint, +PxArticulationLink_InboundJointDof, +PxArticulationLink_LinkIndex, +PxArticulationLink_Children, +PxArticulationLink_ConcreteTypeName, +PxArticulationLink_PropertiesStop, +PxArticulationJointBase_PropertiesStart, +PxArticulationJointBase_ParentPose, +PxArticulationJointBase_ChildPose, +PxArticulationJointBase_PropertiesStop, +PxArticulationJoint_PropertiesStart, +PxArticulationJoint_TargetOrientation, +PxArticulationJoint_TargetVelocity, +PxArticulationJoint_DriveType, +PxArticulationJoint_Stiffness, +PxArticulationJoint_Damping, +PxArticulationJoint_InternalCompliance, +PxArticulationJoint_ExternalCompliance, +PxArticulationJoint_SwingLimit, +PxArticulationJoint_TangentialStiffness, +PxArticulationJoint_TangentialDamping, +PxArticulationJoint_SwingLimitContactDistance, +PxArticulationJoint_SwingLimitEnabled, +PxArticulationJoint_TwistLimit, +PxArticulationJoint_TwistLimitEnabled, +PxArticulationJoint_TwistLimitContactDistance, +PxArticulationJoint_ConcreteTypeName, +PxArticulationJoint_PropertiesStop, +PxArticulationBase_PropertiesStart, +PxArticulationBase_Scene, +PxArticulationBase_SolverIterationCounts, +PxArticulationBase_IsSleeping, +PxArticulationBase_SleepThreshold, +PxArticulationBase_StabilizationThreshold, +PxArticulationBase_WakeCounter, +PxArticulationBase_Links, +PxArticulationBase_Name, +PxArticulationBase_Aggregate, +PxArticulationBase_Type, +PxArticulationBase_UserData, +PxArticulationBase_PropertiesStop, +PxArticulation_PropertiesStart, +PxArticulation_MaxProjectionIterations, +PxArticulation_SeparationTolerance, +PxArticulation_InternalDriveIterations, +PxArticulation_ExternalDriveIterations, +PxArticulation_PropertiesStop, +PxAggregate_PropertiesStart, +PxAggregate_MaxNbActors, +PxAggregate_Actors, +PxAggregate_SelfCollision, +PxAggregate_ConcreteTypeName, +PxAggregate_PropertiesStop, +PxConstraint_PropertiesStart, +PxConstraint_Scene, +PxConstraint_Actors, +PxConstraint_Flags, +PxConstraint_IsValid, +PxConstraint_BreakForce, +PxConstraint_MinResponseThreshold, +PxConstraint_ConcreteTypeName, +PxConstraint_PropertiesStop, +PxShape_PropertiesStart, +PxShape_ReferenceCount, +PxShape_GeometryType, +PxShape_Geometry, +PxShape_LocalPose, +PxShape_SimulationFilterData, +PxShape_QueryFilterData, +PxShape_Materials, +PxShape_ContactOffset, +PxShape_RestOffset, +PxShape_TorsionalPatchRadius, +PxShape_MinTorsionalPatchRadius, +PxShape_Flags, +PxShape_IsExclusive, +PxShape_Name, +PxShape_ConcreteTypeName, +PxShape_UserData, +PxShape_PropertiesStop, +PxPruningStructure_PropertiesStart, +PxPruningStructure_RigidActors, +PxPruningStructure_ConcreteTypeName, +PxPruningStructure_PropertiesStop, +PxTolerancesScale_PropertiesStart, +PxTolerancesScale_IsValid, +PxTolerancesScale_Length, +PxTolerancesScale_Speed, +PxTolerancesScale_PropertiesStop, +PxGeometry_PropertiesStart, +PxGeometry_PropertiesStop, +PxBoxGeometry_PropertiesStart, +PxBoxGeometry_HalfExtents, +PxBoxGeometry_PropertiesStop, +PxCapsuleGeometry_PropertiesStart, +PxCapsuleGeometry_Radius, +PxCapsuleGeometry_HalfHeight, +PxCapsuleGeometry_PropertiesStop, +PxMeshScale_PropertiesStart, +PxMeshScale_Scale, +PxMeshScale_Rotation, +PxMeshScale_PropertiesStop, +PxConvexMeshGeometry_PropertiesStart, +PxConvexMeshGeometry_Scale, +PxConvexMeshGeometry_ConvexMesh, +PxConvexMeshGeometry_MeshFlags, +PxConvexMeshGeometry_PropertiesStop, +PxSphereGeometry_PropertiesStart, +PxSphereGeometry_Radius, +PxSphereGeometry_PropertiesStop, +PxPlaneGeometry_PropertiesStart, +PxPlaneGeometry_PropertiesStop, +PxTriangleMeshGeometry_PropertiesStart, +PxTriangleMeshGeometry_Scale, +PxTriangleMeshGeometry_MeshFlags, +PxTriangleMeshGeometry_TriangleMesh, +PxTriangleMeshGeometry_PropertiesStop, +PxHeightFieldGeometry_PropertiesStart, +PxHeightFieldGeometry_HeightField, +PxHeightFieldGeometry_HeightScale, +PxHeightFieldGeometry_RowScale, +PxHeightFieldGeometry_ColumnScale, +PxHeightFieldGeometry_HeightFieldFlags, +PxHeightFieldGeometry_PropertiesStop, +PxHeightFieldDesc_PropertiesStart, +PxHeightFieldDesc_NbRows, +PxHeightFieldDesc_NbColumns, +PxHeightFieldDesc_Format, +PxHeightFieldDesc_Samples, +PxHeightFieldDesc_ConvexEdgeThreshold, +PxHeightFieldDesc_Flags, +PxHeightFieldDesc_PropertiesStop, +PxArticulationJointReducedCoordinate_PropertiesStart, +PxArticulationJointReducedCoordinate_JointType, +PxArticulationJointReducedCoordinate_Motion, +PxArticulationJointReducedCoordinate_FrictionCoefficient, +PxArticulationJointReducedCoordinate_ConcreteTypeName, +PxArticulationJointReducedCoordinate_MaxJointVelocity, +PxArticulationJointReducedCoordinate_PropertiesStop, +PxScene_PropertiesStart, +PxScene_Flags, +PxScene_Limits, +PxScene_Timestamp, +PxScene_Actors, +PxScene_Articulations, +PxScene_Constraints, +PxScene_Aggregates, +PxScene_CpuDispatcher, +PxScene_GpuDispatcher, +PxScene_SimulationEventCallback, +PxScene_ContactModifyCallback, +PxScene_CCDContactModifyCallback, +PxScene_BroadPhaseCallback, +PxScene_FilterShaderDataSize, +PxScene_FilterShader, +PxScene_FilterCallback, +PxScene_Gravity, +PxScene_BounceThresholdVelocity, +PxScene_CCDMaxPasses, +PxScene_FrictionOffsetThreshold, +PxScene_FrictionType, +PxScene_VisualizationCullingBox, +PxScene_StaticStructure, +PxScene_DynamicStructure, +PxScene_DynamicTreeRebuildRateHint, +PxScene_SceneQueryUpdateMode, +PxScene_SceneQueryStaticTimestamp, +PxScene_BroadPhaseType, +PxScene_BroadPhaseRegions, +PxScene_TaskManager, +PxScene_NbContactDataBlocks, +PxScene_MaxNbContactDataBlocksUsed, +PxScene_ContactReportStreamBufferSize, +PxScene_SolverBatchSize, +PxScene_WakeCounterResetValue, +PxScene_UserData, +PxScene_SimulationStatistics, +PxScene_PropertiesStop, +PxSceneLimits_PropertiesStart, +PxSceneLimits_MaxNbActors, +PxSceneLimits_MaxNbBodies, +PxSceneLimits_MaxNbStaticShapes, +PxSceneLimits_MaxNbDynamicShapes, +PxSceneLimits_MaxNbAggregates, +PxSceneLimits_MaxNbConstraints, +PxSceneLimits_MaxNbRegions, +PxSceneLimits_MaxNbBroadPhaseOverlaps, +PxSceneLimits_PropertiesStop, +PxgDynamicsMemoryConfig_PropertiesStart, +PxgDynamicsMemoryConfig_ConstraintBufferCapacity, +PxgDynamicsMemoryConfig_ContactBufferCapacity, +PxgDynamicsMemoryConfig_TempBufferCapacity, +PxgDynamicsMemoryConfig_ContactStreamSize, +PxgDynamicsMemoryConfig_PatchStreamSize, +PxgDynamicsMemoryConfig_ForceStreamCapacity, +PxgDynamicsMemoryConfig_HeapCapacity, +PxgDynamicsMemoryConfig_FoundLostPairsCapacity, +PxgDynamicsMemoryConfig_PropertiesStop, +PxSceneDesc_PropertiesStart, +PxSceneDesc_ToDefault, +PxSceneDesc_Gravity, +PxSceneDesc_SimulationEventCallback, +PxSceneDesc_ContactModifyCallback, +PxSceneDesc_CcdContactModifyCallback, +PxSceneDesc_FilterShaderData, +PxSceneDesc_FilterShaderDataSize, +PxSceneDesc_FilterShader, +PxSceneDesc_FilterCallback, +PxSceneDesc_KineKineFilteringMode, +PxSceneDesc_StaticKineFilteringMode, +PxSceneDesc_BroadPhaseType, +PxSceneDesc_BroadPhaseCallback, +PxSceneDesc_Limits, +PxSceneDesc_FrictionType, +PxSceneDesc_SolverType, +PxSceneDesc_BounceThresholdVelocity, +PxSceneDesc_FrictionOffsetThreshold, +PxSceneDesc_CcdMaxSeparation, +PxSceneDesc_SolverOffsetSlop, +PxSceneDesc_Flags, +PxSceneDesc_CpuDispatcher, +PxSceneDesc_GpuDispatcher, +PxSceneDesc_StaticStructure, +PxSceneDesc_DynamicStructure, +PxSceneDesc_DynamicTreeRebuildRateHint, +PxSceneDesc_SceneQueryUpdateMode, +PxSceneDesc_UserData, +PxSceneDesc_SolverBatchSize, +PxSceneDesc_NbContactDataBlocks, +PxSceneDesc_MaxNbContactDataBlocks, +PxSceneDesc_MaxBiasCoefficient, +PxSceneDesc_ContactReportStreamBufferSize, +PxSceneDesc_CcdMaxPasses, +PxSceneDesc_WakeCounterResetValue, +PxSceneDesc_SanityBounds, +PxSceneDesc_GpuDynamicsConfig, +PxSceneDesc_GpuMaxNumPartitions, +PxSceneDesc_GpuComputeVersion, +PxSceneDesc_PropertiesStop, +PxSimulationStatistics_PropertiesStart, +PxSimulationStatistics_NbActiveConstraints, +PxSimulationStatistics_NbActiveDynamicBodies, +PxSimulationStatistics_NbActiveKinematicBodies, +PxSimulationStatistics_NbStaticBodies, +PxSimulationStatistics_NbDynamicBodies, +PxSimulationStatistics_NbAggregates, +PxSimulationStatistics_NbArticulations, +PxSimulationStatistics_NbAxisSolverConstraints, +PxSimulationStatistics_CompressedContactSize, +PxSimulationStatistics_RequiredContactConstraintMemory, +PxSimulationStatistics_PeakConstraintMemory, +PxSimulationStatistics_NbDiscreteContactPairsTotal, +PxSimulationStatistics_NbDiscreteContactPairsWithCacheHits, +PxSimulationStatistics_NbDiscreteContactPairsWithContacts, +PxSimulationStatistics_NbNewPairs, +PxSimulationStatistics_NbLostPairs, +PxSimulationStatistics_NbNewTouches, +PxSimulationStatistics_NbLostTouches, +PxSimulationStatistics_NbPartitions, +PxSimulationStatistics_NbBroadPhaseAdds, +PxSimulationStatistics_NbBroadPhaseRemoves, +PxSimulationStatistics_NbDiscreteContactPairs, +PxSimulationStatistics_NbModifiedContactPairs, +PxSimulationStatistics_NbCCDPairs, +PxSimulationStatistics_NbTriggerPairs, +PxSimulationStatistics_NbShapes, +PxSimulationStatistics_PropertiesStop, + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h new file mode 100644 index 000000000..2f8fd805a --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h @@ -0,0 +1,2888 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +#define PX_PROPERTY_INFO_NAME PxPropertyInfoName + static PxU32ToName g_physx__PxShapeFlag__EnumConversion[] = { + { "eSIMULATION_SHAPE", static_cast( physx::PxShapeFlag::eSIMULATION_SHAPE ) }, + { "eSCENE_QUERY_SHAPE", static_cast( physx::PxShapeFlag::eSCENE_QUERY_SHAPE ) }, + { "eTRIGGER_SHAPE", static_cast( physx::PxShapeFlag::eTRIGGER_SHAPE ) }, + { "eVISUALIZATION", static_cast( physx::PxShapeFlag::eVISUALIZATION ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxShapeFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxShapeFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxPhysics; + struct PxPhysicsGeneratedValues + { + PxTolerancesScale TolerancesScale; + PX_PHYSX_CORE_API PxPhysicsGeneratedValues( const PxPhysics* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPhysics, TolerancesScale, PxPhysicsGeneratedValues) + struct PxPhysicsGeneratedInfo + + { + static const char* getClassName() { return "PxPhysics"; } + PxReadOnlyPropertyInfo TolerancesScale; + PxFactoryCollectionPropertyInfo TriangleMeshes; + PxFactoryCollectionPropertyInfo HeightFields; + PxFactoryCollectionPropertyInfo ConvexMeshes; + PxFactoryCollectionPropertyInfo BVHStructures; + PxFactoryCollectionPropertyInfo Scenes; + PxReadOnlyCollectionPropertyInfo Shapes; + PxReadOnlyCollectionPropertyInfo Materials; + + PX_PHYSX_CORE_API PxPhysicsGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 8; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( TolerancesScale, inStartIndex + 0 );; + inOperator( TriangleMeshes, inStartIndex + 1 );; + inOperator( HeightFields, inStartIndex + 2 );; + inOperator( ConvexMeshes, inStartIndex + 3 );; + inOperator( BVHStructures, inStartIndex + 4 );; + inOperator( Scenes, inStartIndex + 5 );; + inOperator( Shapes, inStartIndex + 6 );; + inOperator( Materials, inStartIndex + 7 );; + return 8 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxPhysicsGeneratedInfo Info; + const PxPhysicsGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxMaterialFlag__EnumConversion[] = { + { "eDISABLE_FRICTION", static_cast( physx::PxMaterialFlag::eDISABLE_FRICTION ) }, + { "eDISABLE_STRONG_FRICTION", static_cast( physx::PxMaterialFlag::eDISABLE_STRONG_FRICTION ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxMaterialFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxMaterialFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxCombineMode__EnumConversion[] = { + { "eAVERAGE", static_cast( physx::PxCombineMode::eAVERAGE ) }, + { "eMIN", static_cast( physx::PxCombineMode::eMIN ) }, + { "eMULTIPLY", static_cast( physx::PxCombineMode::eMULTIPLY ) }, + { "eMAX", static_cast( physx::PxCombineMode::eMAX ) }, + { "eN_VALUES", static_cast( physx::PxCombineMode::eN_VALUES ) }, + { "ePAD_32", static_cast( physx::PxCombineMode::ePAD_32 ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxCombineMode::Enum > { PxEnumTraits() : NameConversion( g_physx__PxCombineMode__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxMaterial; + struct PxMaterialGeneratedValues + { + PxU32 ReferenceCount; + PxReal DynamicFriction; + PxReal StaticFriction; + PxReal Restitution; + PxMaterialFlags Flags; + PxCombineMode::Enum FrictionCombineMode; + PxCombineMode::Enum RestitutionCombineMode; + const char * ConcreteTypeName; + void * UserData; + PX_PHYSX_CORE_API PxMaterialGeneratedValues( const PxMaterial* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, ReferenceCount, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, DynamicFriction, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, StaticFriction, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, Restitution, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, Flags, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, FrictionCombineMode, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, RestitutionCombineMode, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, ConcreteTypeName, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, UserData, PxMaterialGeneratedValues) + struct PxMaterialGeneratedInfo + + { + static const char* getClassName() { return "PxMaterial"; } + PxReadOnlyPropertyInfo ReferenceCount; + PxPropertyInfo DynamicFriction; + PxPropertyInfo StaticFriction; + PxPropertyInfo Restitution; + PxPropertyInfo Flags; + PxPropertyInfo FrictionCombineMode; + PxPropertyInfo RestitutionCombineMode; + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo UserData; + + PX_PHYSX_CORE_API PxMaterialGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 9; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ReferenceCount, inStartIndex + 0 );; + inOperator( DynamicFriction, inStartIndex + 1 );; + inOperator( StaticFriction, inStartIndex + 2 );; + inOperator( Restitution, inStartIndex + 3 );; + inOperator( Flags, inStartIndex + 4 );; + inOperator( FrictionCombineMode, inStartIndex + 5 );; + inOperator( RestitutionCombineMode, inStartIndex + 6 );; + inOperator( ConcreteTypeName, inStartIndex + 7 );; + inOperator( UserData, inStartIndex + 8 );; + return 9 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxMaterialGeneratedInfo Info; + const PxMaterialGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxActorType__EnumConversion[] = { + { "eRIGID_STATIC", static_cast( physx::PxActorType::eRIGID_STATIC ) }, + { "eRIGID_DYNAMIC", static_cast( physx::PxActorType::eRIGID_DYNAMIC ) }, + { "eARTICULATION_LINK", static_cast( physx::PxActorType::eARTICULATION_LINK ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxActorType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxActorType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxActorFlag__EnumConversion[] = { + { "eVISUALIZATION", static_cast( physx::PxActorFlag::eVISUALIZATION ) }, + { "eDISABLE_GRAVITY", static_cast( physx::PxActorFlag::eDISABLE_GRAVITY ) }, + { "eSEND_SLEEP_NOTIFIES", static_cast( physx::PxActorFlag::eSEND_SLEEP_NOTIFIES ) }, + { "eDISABLE_SIMULATION", static_cast( physx::PxActorFlag::eDISABLE_SIMULATION ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxActorFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxActorFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxActor; + struct PxActorGeneratedValues + { + PxScene * Scene; + const char * Name; + PxActorFlags ActorFlags; + PxDominanceGroup DominanceGroup; + PxClientID OwnerClient; + PxAggregate * Aggregate; + void * UserData; + PX_PHYSX_CORE_API PxActorGeneratedValues( const PxActor* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, Scene, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, Name, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, ActorFlags, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, DominanceGroup, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, OwnerClient, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, Aggregate, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, UserData, PxActorGeneratedValues) + struct PxActorGeneratedInfo + + { + static const char* getClassName() { return "PxActor"; } + PxReadOnlyPropertyInfo Scene; + PxPropertyInfo Name; + PxPropertyInfo ActorFlags; + PxPropertyInfo DominanceGroup; + PxPropertyInfo OwnerClient; + PxReadOnlyPropertyInfo Aggregate; + PxPropertyInfo UserData; + + PX_PHYSX_CORE_API PxActorGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 7; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scene, inStartIndex + 0 );; + inOperator( Name, inStartIndex + 1 );; + inOperator( ActorFlags, inStartIndex + 2 );; + inOperator( DominanceGroup, inStartIndex + 3 );; + inOperator( OwnerClient, inStartIndex + 4 );; + inOperator( Aggregate, inStartIndex + 5 );; + inOperator( UserData, inStartIndex + 6 );; + return 7 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxActorGeneratedInfo Info; + const PxActorGeneratedInfo* getInfo() { return &Info; } + }; + + class PxRigidActor; + struct PxRigidActorGeneratedValues + : PxActorGeneratedValues { + PxTransform GlobalPose; + PX_PHYSX_CORE_API PxRigidActorGeneratedValues( const PxRigidActor* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidActor, GlobalPose, PxRigidActorGeneratedValues) + struct PxRigidActorGeneratedInfo + : PxActorGeneratedInfo + { + static const char* getClassName() { return "PxRigidActor"; } + PxPropertyInfo GlobalPose; + PxRigidActorShapeCollection Shapes; + PxReadOnlyCollectionPropertyInfo Constraints; + + PX_PHYSX_CORE_API PxRigidActorGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxActorGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxActorGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxActorGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( GlobalPose, inStartIndex + 0 );; + inOperator( Shapes, inStartIndex + 1 );; + inOperator( Constraints, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxRigidActorGeneratedInfo Info; + const PxRigidActorGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxForceMode__EnumConversion[] = { + { "eFORCE", static_cast( physx::PxForceMode::eFORCE ) }, + { "eIMPULSE", static_cast( physx::PxForceMode::eIMPULSE ) }, + { "eVELOCITY_CHANGE", static_cast( physx::PxForceMode::eVELOCITY_CHANGE ) }, + { "eACCELERATION", static_cast( physx::PxForceMode::eACCELERATION ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxForceMode::Enum > { PxEnumTraits() : NameConversion( g_physx__PxForceMode__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxRigidBodyFlag__EnumConversion[] = { + { "eKINEMATIC", static_cast( physx::PxRigidBodyFlag::eKINEMATIC ) }, + { "eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES", static_cast( physx::PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES ) }, + { "eENABLE_CCD", static_cast( physx::PxRigidBodyFlag::eENABLE_CCD ) }, + { "eENABLE_CCD_FRICTION", static_cast( physx::PxRigidBodyFlag::eENABLE_CCD_FRICTION ) }, + { "eENABLE_POSE_INTEGRATION_PREVIEW", static_cast( physx::PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW ) }, + { "eENABLE_SPECULATIVE_CCD", static_cast( physx::PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD ) }, + { "eENABLE_CCD_MAX_CONTACT_IMPULSE", static_cast( physx::PxRigidBodyFlag::eENABLE_CCD_MAX_CONTACT_IMPULSE ) }, + { "eRETAIN_ACCELERATIONS", static_cast( physx::PxRigidBodyFlag::eRETAIN_ACCELERATIONS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxRigidBodyFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxRigidBodyFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxRigidBody; + struct PxRigidBodyGeneratedValues + : PxRigidActorGeneratedValues { + PxTransform CMassLocalPose; + PxReal Mass; + PxReal InvMass; + PxVec3 MassSpaceInertiaTensor; + PxVec3 MassSpaceInvInertiaTensor; + PxReal LinearDamping; + PxReal AngularDamping; + PxVec3 LinearVelocity; + PxVec3 AngularVelocity; + PxReal MaxAngularVelocity; + PxReal MaxLinearVelocity; + PxRigidBodyFlags RigidBodyFlags; + PxReal MinCCDAdvanceCoefficient; + PxReal MaxDepenetrationVelocity; + PxReal MaxContactImpulse; + PX_PHYSX_CORE_API PxRigidBodyGeneratedValues( const PxRigidBody* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, CMassLocalPose, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, Mass, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, InvMass, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MassSpaceInertiaTensor, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MassSpaceInvInertiaTensor, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, LinearDamping, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, AngularDamping, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, LinearVelocity, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, AngularVelocity, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MaxAngularVelocity, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MaxLinearVelocity, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, RigidBodyFlags, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MinCCDAdvanceCoefficient, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MaxDepenetrationVelocity, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MaxContactImpulse, PxRigidBodyGeneratedValues) + struct PxRigidBodyGeneratedInfo + : PxRigidActorGeneratedInfo + { + static const char* getClassName() { return "PxRigidBody"; } + PxPropertyInfo CMassLocalPose; + PxPropertyInfo Mass; + PxReadOnlyPropertyInfo InvMass; + PxPropertyInfo MassSpaceInertiaTensor; + PxReadOnlyPropertyInfo MassSpaceInvInertiaTensor; + PxPropertyInfo LinearDamping; + PxPropertyInfo AngularDamping; + PxPropertyInfo LinearVelocity; + PxPropertyInfo AngularVelocity; + PxPropertyInfo MaxAngularVelocity; + PxPropertyInfo MaxLinearVelocity; + PxPropertyInfo RigidBodyFlags; + PxPropertyInfo MinCCDAdvanceCoefficient; + PxPropertyInfo MaxDepenetrationVelocity; + PxPropertyInfo MaxContactImpulse; + + PX_PHYSX_CORE_API PxRigidBodyGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxRigidActorGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxRigidActorGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 15; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxRigidActorGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( CMassLocalPose, inStartIndex + 0 );; + inOperator( Mass, inStartIndex + 1 );; + inOperator( InvMass, inStartIndex + 2 );; + inOperator( MassSpaceInertiaTensor, inStartIndex + 3 );; + inOperator( MassSpaceInvInertiaTensor, inStartIndex + 4 );; + inOperator( LinearDamping, inStartIndex + 5 );; + inOperator( AngularDamping, inStartIndex + 6 );; + inOperator( LinearVelocity, inStartIndex + 7 );; + inOperator( AngularVelocity, inStartIndex + 8 );; + inOperator( MaxAngularVelocity, inStartIndex + 9 );; + inOperator( MaxLinearVelocity, inStartIndex + 10 );; + inOperator( RigidBodyFlags, inStartIndex + 11 );; + inOperator( MinCCDAdvanceCoefficient, inStartIndex + 12 );; + inOperator( MaxDepenetrationVelocity, inStartIndex + 13 );; + inOperator( MaxContactImpulse, inStartIndex + 14 );; + return 15 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxRigidBodyGeneratedInfo Info; + const PxRigidBodyGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxRigidDynamicLockFlag__EnumConversion[] = { + { "eLOCK_LINEAR_X", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_X ) }, + { "eLOCK_LINEAR_Y", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_Y ) }, + { "eLOCK_LINEAR_Z", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_Z ) }, + { "eLOCK_ANGULAR_X", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_X ) }, + { "eLOCK_ANGULAR_Y", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y ) }, + { "eLOCK_ANGULAR_Z", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxRigidDynamicLockFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxRigidDynamicLockFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxRigidDynamic; + struct PxRigidDynamicGeneratedValues + : PxRigidBodyGeneratedValues { + _Bool IsSleeping; + PxReal SleepThreshold; + PxReal StabilizationThreshold; + PxRigidDynamicLockFlags RigidDynamicLockFlags; + PxReal WakeCounter; + PxU32 SolverIterationCounts[2]; + PxReal ContactReportThreshold; + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxRigidDynamicGeneratedValues( const PxRigidDynamic* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, IsSleeping, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, SleepThreshold, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, StabilizationThreshold, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, RigidDynamicLockFlags, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, WakeCounter, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, SolverIterationCounts, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, ContactReportThreshold, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, ConcreteTypeName, PxRigidDynamicGeneratedValues) + struct PxRigidDynamicGeneratedInfo + : PxRigidBodyGeneratedInfo + { + static const char* getClassName() { return "PxRigidDynamic"; } + PxReadOnlyPropertyInfo IsSleeping; + PxPropertyInfo SleepThreshold; + PxPropertyInfo StabilizationThreshold; + PxPropertyInfo RigidDynamicLockFlags; + PxPropertyInfo WakeCounter; + PxRangePropertyInfo SolverIterationCounts; + PxPropertyInfo ContactReportThreshold; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxRigidDynamicGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxRigidBodyGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxRigidBodyGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 8; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxRigidBodyGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( IsSleeping, inStartIndex + 0 );; + inOperator( SleepThreshold, inStartIndex + 1 );; + inOperator( StabilizationThreshold, inStartIndex + 2 );; + inOperator( RigidDynamicLockFlags, inStartIndex + 3 );; + inOperator( WakeCounter, inStartIndex + 4 );; + inOperator( SolverIterationCounts, inStartIndex + 5 );; + inOperator( ContactReportThreshold, inStartIndex + 6 );; + inOperator( ConcreteTypeName, inStartIndex + 7 );; + return 8 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxRigidDynamicGeneratedInfo Info; + const PxRigidDynamicGeneratedInfo* getInfo() { return &Info; } + }; + + class PxRigidStatic; + struct PxRigidStaticGeneratedValues + : PxRigidActorGeneratedValues { + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxRigidStaticGeneratedValues( const PxRigidStatic* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidStatic, ConcreteTypeName, PxRigidStaticGeneratedValues) + struct PxRigidStaticGeneratedInfo + : PxRigidActorGeneratedInfo + { + static const char* getClassName() { return "PxRigidStatic"; } + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxRigidStaticGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxRigidActorGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxRigidActorGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxRigidActorGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ConcreteTypeName, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxRigidStaticGeneratedInfo Info; + const PxRigidStaticGeneratedInfo* getInfo() { return &Info; } + }; + + class PxArticulationLink; + struct PxArticulationLinkGeneratedValues + : PxRigidBodyGeneratedValues { + PxArticulationJointBase * InboundJoint; + PxU32 InboundJointDof; + PxU32 LinkIndex; + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxArticulationLinkGeneratedValues( const PxArticulationLink* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationLink, InboundJoint, PxArticulationLinkGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationLink, InboundJointDof, PxArticulationLinkGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationLink, LinkIndex, PxArticulationLinkGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationLink, ConcreteTypeName, PxArticulationLinkGeneratedValues) + struct PxArticulationLinkGeneratedInfo + : PxRigidBodyGeneratedInfo + { + static const char* getClassName() { return "PxArticulationLink"; } + PxReadOnlyPropertyInfo InboundJoint; + PxReadOnlyPropertyInfo InboundJointDof; + PxReadOnlyPropertyInfo LinkIndex; + PxReadOnlyCollectionPropertyInfo Children; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxArticulationLinkGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxRigidBodyGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxRigidBodyGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxRigidBodyGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( InboundJoint, inStartIndex + 0 );; + inOperator( InboundJointDof, inStartIndex + 1 );; + inOperator( LinkIndex, inStartIndex + 2 );; + inOperator( Children, inStartIndex + 3 );; + inOperator( ConcreteTypeName, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationLinkGeneratedInfo Info; + const PxArticulationLinkGeneratedInfo* getInfo() { return &Info; } + }; + + class PxArticulationJointBase; + struct PxArticulationJointBaseGeneratedValues + { + PxTransform ParentPose; + PxTransform ChildPose; + PX_PHYSX_CORE_API PxArticulationJointBaseGeneratedValues( const PxArticulationJointBase* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointBase, ParentPose, PxArticulationJointBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointBase, ChildPose, PxArticulationJointBaseGeneratedValues) + struct PxArticulationJointBaseGeneratedInfo + + { + static const char* getClassName() { return "PxArticulationJointBase"; } + PxPropertyInfo ParentPose; + PxPropertyInfo ChildPose; + + PX_PHYSX_CORE_API PxArticulationJointBaseGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ParentPose, inStartIndex + 0 );; + inOperator( ChildPose, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationJointBaseGeneratedInfo Info; + const PxArticulationJointBaseGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxArticulationJointDriveType__EnumConversion[] = { + { "eTARGET", static_cast( physx::PxArticulationJointDriveType::eTARGET ) }, + { "eERROR", static_cast( physx::PxArticulationJointDriveType::eERROR ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxArticulationJointDriveType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxArticulationJointDriveType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxArticulationJoint; + struct PxArticulationJointGeneratedValues + : PxArticulationJointBaseGeneratedValues { + PxQuat TargetOrientation; + PxVec3 TargetVelocity; + PxArticulationJointDriveType::Enum DriveType; + PxReal Stiffness; + PxReal Damping; + PxReal InternalCompliance; + PxReal ExternalCompliance; + PxReal SwingLimit[2]; + PxReal TangentialStiffness; + PxReal TangentialDamping; + PxReal SwingLimitContactDistance; + _Bool SwingLimitEnabled; + PxReal TwistLimit[2]; + _Bool TwistLimitEnabled; + PxReal TwistLimitContactDistance; + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxArticulationJointGeneratedValues( const PxArticulationJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TargetOrientation, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TargetVelocity, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, DriveType, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, Stiffness, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, Damping, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, InternalCompliance, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, ExternalCompliance, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, SwingLimit, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TangentialStiffness, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TangentialDamping, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, SwingLimitContactDistance, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, SwingLimitEnabled, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TwistLimit, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TwistLimitEnabled, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TwistLimitContactDistance, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, ConcreteTypeName, PxArticulationJointGeneratedValues) + struct PxArticulationJointGeneratedInfo + : PxArticulationJointBaseGeneratedInfo + { + static const char* getClassName() { return "PxArticulationJoint"; } + PxPropertyInfo TargetOrientation; + PxPropertyInfo TargetVelocity; + PxPropertyInfo DriveType; + PxPropertyInfo Stiffness; + PxPropertyInfo Damping; + PxPropertyInfo InternalCompliance; + PxPropertyInfo ExternalCompliance; + PxRangePropertyInfo SwingLimit; + PxPropertyInfo TangentialStiffness; + PxPropertyInfo TangentialDamping; + PxPropertyInfo SwingLimitContactDistance; + PxPropertyInfo SwingLimitEnabled; + PxRangePropertyInfo TwistLimit; + PxPropertyInfo TwistLimitEnabled; + PxPropertyInfo TwistLimitContactDistance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxArticulationJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxArticulationJointBaseGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxArticulationJointBaseGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 16; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxArticulationJointBaseGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( TargetOrientation, inStartIndex + 0 );; + inOperator( TargetVelocity, inStartIndex + 1 );; + inOperator( DriveType, inStartIndex + 2 );; + inOperator( Stiffness, inStartIndex + 3 );; + inOperator( Damping, inStartIndex + 4 );; + inOperator( InternalCompliance, inStartIndex + 5 );; + inOperator( ExternalCompliance, inStartIndex + 6 );; + inOperator( SwingLimit, inStartIndex + 7 );; + inOperator( TangentialStiffness, inStartIndex + 8 );; + inOperator( TangentialDamping, inStartIndex + 9 );; + inOperator( SwingLimitContactDistance, inStartIndex + 10 );; + inOperator( SwingLimitEnabled, inStartIndex + 11 );; + inOperator( TwistLimit, inStartIndex + 12 );; + inOperator( TwistLimitEnabled, inStartIndex + 13 );; + inOperator( TwistLimitContactDistance, inStartIndex + 14 );; + inOperator( ConcreteTypeName, inStartIndex + 15 );; + return 16 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationJointGeneratedInfo Info; + const PxArticulationJointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxArticulationBase__EnumConversion[] = { + { "eReducedCoordinate", static_cast( physx::PxArticulationBase::eReducedCoordinate ) }, + { "eMaximumCoordinate", static_cast( physx::PxArticulationBase::eMaximumCoordinate ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxArticulationBase::Enum > { PxEnumTraits() : NameConversion( g_physx__PxArticulationBase__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxArticulationBase; + struct PxArticulationBaseGeneratedValues + { + PxScene * Scene; + PxU32 SolverIterationCounts[2]; + _Bool IsSleeping; + PxReal SleepThreshold; + PxReal StabilizationThreshold; + PxReal WakeCounter; + const char * Name; + PxAggregate * Aggregate; + PxArticulationBase::Enum Type; + void * UserData; + PX_PHYSX_CORE_API PxArticulationBaseGeneratedValues( const PxArticulationBase* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, Scene, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, SolverIterationCounts, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, IsSleeping, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, SleepThreshold, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, StabilizationThreshold, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, WakeCounter, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, Name, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, Aggregate, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, Type, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, UserData, PxArticulationBaseGeneratedValues) + struct PxArticulationBaseGeneratedInfo + + { + static const char* getClassName() { return "PxArticulationBase"; } + PxReadOnlyPropertyInfo Scene; + PxRangePropertyInfo SolverIterationCounts; + PxReadOnlyPropertyInfo IsSleeping; + PxPropertyInfo SleepThreshold; + PxPropertyInfo StabilizationThreshold; + PxPropertyInfo WakeCounter; + PxArticulationLinkCollectionProp Links; + PxPropertyInfo Name; + PxReadOnlyPropertyInfo Aggregate; + PxReadOnlyPropertyInfo Type; + PxPropertyInfo UserData; + + PX_PHYSX_CORE_API PxArticulationBaseGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 11; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scene, inStartIndex + 0 );; + inOperator( SolverIterationCounts, inStartIndex + 1 );; + inOperator( IsSleeping, inStartIndex + 2 );; + inOperator( SleepThreshold, inStartIndex + 3 );; + inOperator( StabilizationThreshold, inStartIndex + 4 );; + inOperator( WakeCounter, inStartIndex + 5 );; + inOperator( Links, inStartIndex + 6 );; + inOperator( Name, inStartIndex + 7 );; + inOperator( Aggregate, inStartIndex + 8 );; + inOperator( Type, inStartIndex + 9 );; + inOperator( UserData, inStartIndex + 10 );; + return 11 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationBaseGeneratedInfo Info; + const PxArticulationBaseGeneratedInfo* getInfo() { return &Info; } + }; + + class PxArticulation; + struct PxArticulationGeneratedValues + : PxArticulationBaseGeneratedValues { + PxU32 MaxProjectionIterations; + PxReal SeparationTolerance; + PxU32 InternalDriveIterations; + PxU32 ExternalDriveIterations; + PX_PHYSX_CORE_API PxArticulationGeneratedValues( const PxArticulation* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulation, MaxProjectionIterations, PxArticulationGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulation, SeparationTolerance, PxArticulationGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulation, InternalDriveIterations, PxArticulationGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulation, ExternalDriveIterations, PxArticulationGeneratedValues) + struct PxArticulationGeneratedInfo + : PxArticulationBaseGeneratedInfo + { + static const char* getClassName() { return "PxArticulation"; } + PxPropertyInfo MaxProjectionIterations; + PxPropertyInfo SeparationTolerance; + PxPropertyInfo InternalDriveIterations; + PxPropertyInfo ExternalDriveIterations; + + PX_PHYSX_CORE_API PxArticulationGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxArticulationBaseGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxArticulationBaseGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxArticulationBaseGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MaxProjectionIterations, inStartIndex + 0 );; + inOperator( SeparationTolerance, inStartIndex + 1 );; + inOperator( InternalDriveIterations, inStartIndex + 2 );; + inOperator( ExternalDriveIterations, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationGeneratedInfo Info; + const PxArticulationGeneratedInfo* getInfo() { return &Info; } + }; + + class PxAggregate; + struct PxAggregateGeneratedValues + { + PxU32 MaxNbActors; + _Bool SelfCollision; + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxAggregateGeneratedValues( const PxAggregate* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxAggregate, MaxNbActors, PxAggregateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxAggregate, SelfCollision, PxAggregateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxAggregate, ConcreteTypeName, PxAggregateGeneratedValues) + struct PxAggregateGeneratedInfo + + { + static const char* getClassName() { return "PxAggregate"; } + PxReadOnlyPropertyInfo MaxNbActors; + PxReadOnlyCollectionPropertyInfo Actors; + PxReadOnlyPropertyInfo SelfCollision; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxAggregateGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MaxNbActors, inStartIndex + 0 );; + inOperator( Actors, inStartIndex + 1 );; + inOperator( SelfCollision, inStartIndex + 2 );; + inOperator( ConcreteTypeName, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxAggregateGeneratedInfo Info; + const PxAggregateGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxConstraintFlag__EnumConversion[] = { + { "eBROKEN", static_cast( physx::PxConstraintFlag::eBROKEN ) }, + { "ePROJECT_TO_ACTOR0", static_cast( physx::PxConstraintFlag::ePROJECT_TO_ACTOR0 ) }, + { "ePROJECT_TO_ACTOR1", static_cast( physx::PxConstraintFlag::ePROJECT_TO_ACTOR1 ) }, + { "ePROJECTION", static_cast( physx::PxConstraintFlag::ePROJECTION ) }, + { "eCOLLISION_ENABLED", static_cast( physx::PxConstraintFlag::eCOLLISION_ENABLED ) }, + { "eVISUALIZATION", static_cast( physx::PxConstraintFlag::eVISUALIZATION ) }, + { "eDRIVE_LIMITS_ARE_FORCES", static_cast( physx::PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES ) }, + { "eIMPROVED_SLERP", static_cast( physx::PxConstraintFlag::eIMPROVED_SLERP ) }, + { "eDISABLE_PREPROCESSING", static_cast( physx::PxConstraintFlag::eDISABLE_PREPROCESSING ) }, + { "eENABLE_EXTENDED_LIMITS", static_cast( physx::PxConstraintFlag::eENABLE_EXTENDED_LIMITS ) }, + { "eGPU_COMPATIBLE", static_cast( physx::PxConstraintFlag::eGPU_COMPATIBLE ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxConstraintFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxConstraintFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxConstraint; + struct PxConstraintGeneratedValues + { + PxScene * Scene; + PxRigidActor * Actors[2]; + PxConstraintFlags Flags; + _Bool IsValid; + PxReal BreakForce[2]; + PxReal MinResponseThreshold; + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxConstraintGeneratedValues( const PxConstraint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, Scene, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, Actors, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, Flags, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, IsValid, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, BreakForce, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, MinResponseThreshold, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, ConcreteTypeName, PxConstraintGeneratedValues) + struct PxConstraintGeneratedInfo + + { + static const char* getClassName() { return "PxConstraint"; } + PxReadOnlyPropertyInfo Scene; + PxRangePropertyInfo Actors; + PxPropertyInfo Flags; + PxReadOnlyPropertyInfo IsValid; + PxRangePropertyInfo BreakForce; + PxPropertyInfo MinResponseThreshold; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxConstraintGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 7; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scene, inStartIndex + 0 );; + inOperator( Actors, inStartIndex + 1 );; + inOperator( Flags, inStartIndex + 2 );; + inOperator( IsValid, inStartIndex + 3 );; + inOperator( BreakForce, inStartIndex + 4 );; + inOperator( MinResponseThreshold, inStartIndex + 5 );; + inOperator( ConcreteTypeName, inStartIndex + 6 );; + return 7 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxConstraintGeneratedInfo Info; + const PxConstraintGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxGeometryType__EnumConversion[] = { + { "eSPHERE", static_cast( physx::PxGeometryType::eSPHERE ) }, + { "ePLANE", static_cast( physx::PxGeometryType::ePLANE ) }, + { "eCAPSULE", static_cast( physx::PxGeometryType::eCAPSULE ) }, + { "eBOX", static_cast( physx::PxGeometryType::eBOX ) }, + { "eCONVEXMESH", static_cast( physx::PxGeometryType::eCONVEXMESH ) }, + { "eTRIANGLEMESH", static_cast( physx::PxGeometryType::eTRIANGLEMESH ) }, + { "eHEIGHTFIELD", static_cast( physx::PxGeometryType::eHEIGHTFIELD ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxGeometryType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxGeometryType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxShape; + struct PxShapeGeneratedValues + { + PxU32 ReferenceCount; + PxGeometryType::Enum GeometryType; + PxGeometryHolder Geometry; + PxTransform LocalPose; + PxFilterData SimulationFilterData; + PxFilterData QueryFilterData; + PxReal ContactOffset; + PxReal RestOffset; + PxReal TorsionalPatchRadius; + PxReal MinTorsionalPatchRadius; + PxShapeFlags Flags; + _Bool IsExclusive; + const char * Name; + const char * ConcreteTypeName; + void * UserData; + PX_PHYSX_CORE_API PxShapeGeneratedValues( const PxShape* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, ReferenceCount, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, GeometryType, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, Geometry, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, LocalPose, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, SimulationFilterData, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, QueryFilterData, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, ContactOffset, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, RestOffset, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, TorsionalPatchRadius, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, MinTorsionalPatchRadius, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, Flags, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, IsExclusive, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, Name, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, ConcreteTypeName, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, UserData, PxShapeGeneratedValues) + struct PxShapeGeneratedInfo + + { + static const char* getClassName() { return "PxShape"; } + PxReadOnlyPropertyInfo ReferenceCount; + PxReadOnlyPropertyInfo GeometryType; + PxShapeGeometryProperty Geometry; + PxPropertyInfo LocalPose; + PxPropertyInfo SimulationFilterData; + PxPropertyInfo QueryFilterData; + PxShapeMaterialsProperty Materials; + PxPropertyInfo ContactOffset; + PxPropertyInfo RestOffset; + PxPropertyInfo TorsionalPatchRadius; + PxPropertyInfo MinTorsionalPatchRadius; + PxPropertyInfo Flags; + PxReadOnlyPropertyInfo IsExclusive; + PxPropertyInfo Name; + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo UserData; + + PX_PHYSX_CORE_API PxShapeGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 16; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ReferenceCount, inStartIndex + 0 );; + inOperator( GeometryType, inStartIndex + 1 );; + inOperator( Geometry, inStartIndex + 2 );; + inOperator( LocalPose, inStartIndex + 3 );; + inOperator( SimulationFilterData, inStartIndex + 4 );; + inOperator( QueryFilterData, inStartIndex + 5 );; + inOperator( Materials, inStartIndex + 6 );; + inOperator( ContactOffset, inStartIndex + 7 );; + inOperator( RestOffset, inStartIndex + 8 );; + inOperator( TorsionalPatchRadius, inStartIndex + 9 );; + inOperator( MinTorsionalPatchRadius, inStartIndex + 10 );; + inOperator( Flags, inStartIndex + 11 );; + inOperator( IsExclusive, inStartIndex + 12 );; + inOperator( Name, inStartIndex + 13 );; + inOperator( ConcreteTypeName, inStartIndex + 14 );; + inOperator( UserData, inStartIndex + 15 );; + return 16 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxShapeGeneratedInfo Info; + const PxShapeGeneratedInfo* getInfo() { return &Info; } + }; + + class PxPruningStructure; + struct PxPruningStructureGeneratedValues + { + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxPruningStructureGeneratedValues( const PxPruningStructure* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPruningStructure, ConcreteTypeName, PxPruningStructureGeneratedValues) + struct PxPruningStructureGeneratedInfo + + { + static const char* getClassName() { return "PxPruningStructure"; } + PxReadOnlyCollectionPropertyInfo RigidActors; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxPruningStructureGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( RigidActors, inStartIndex + 0 );; + inOperator( ConcreteTypeName, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxPruningStructureGeneratedInfo Info; + const PxPruningStructureGeneratedInfo* getInfo() { return &Info; } + }; + + class PxTolerancesScale; + struct PxTolerancesScaleGeneratedValues + { + _Bool IsValid; + PxReal Length; + PxReal Speed; + PX_PHYSX_CORE_API PxTolerancesScaleGeneratedValues( const PxTolerancesScale* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTolerancesScale, IsValid, PxTolerancesScaleGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTolerancesScale, Length, PxTolerancesScaleGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTolerancesScale, Speed, PxTolerancesScaleGeneratedValues) + struct PxTolerancesScaleGeneratedInfo + + { + static const char* getClassName() { return "PxTolerancesScale"; } + PxReadOnlyPropertyInfo IsValid; + PxPropertyInfo Length; + PxPropertyInfo Speed; + + PX_PHYSX_CORE_API PxTolerancesScaleGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( IsValid, inStartIndex + 0 );; + inOperator( Length, inStartIndex + 1 );; + inOperator( Speed, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxTolerancesScaleGeneratedInfo Info; + const PxTolerancesScaleGeneratedInfo* getInfo() { return &Info; } + }; + + class PxGeometry; + struct PxGeometryGeneratedValues + { + PX_PHYSX_CORE_API PxGeometryGeneratedValues( const PxGeometry* inSource ); + }; + struct PxGeometryGeneratedInfo + + { + static const char* getClassName() { return "PxGeometry"; } + + PX_PHYSX_CORE_API PxGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 0; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return 0 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxGeometryGeneratedInfo Info; + const PxGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxBoxGeometry; + struct PxBoxGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxVec3 HalfExtents; + PX_PHYSX_CORE_API PxBoxGeometryGeneratedValues( const PxBoxGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxBoxGeometry, HalfExtents, PxBoxGeometryGeneratedValues) + struct PxBoxGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxBoxGeometry"; } + PxPropertyInfo HalfExtents; + + PX_PHYSX_CORE_API PxBoxGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( HalfExtents, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxBoxGeometryGeneratedInfo Info; + const PxBoxGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxCapsuleGeometry; + struct PxCapsuleGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxReal Radius; + PxReal HalfHeight; + PX_PHYSX_CORE_API PxCapsuleGeometryGeneratedValues( const PxCapsuleGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxCapsuleGeometry, Radius, PxCapsuleGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxCapsuleGeometry, HalfHeight, PxCapsuleGeometryGeneratedValues) + struct PxCapsuleGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxCapsuleGeometry"; } + PxPropertyInfo Radius; + PxPropertyInfo HalfHeight; + + PX_PHYSX_CORE_API PxCapsuleGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Radius, inStartIndex + 0 );; + inOperator( HalfHeight, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxCapsuleGeometryGeneratedInfo Info; + const PxCapsuleGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxMeshScale; + struct PxMeshScaleGeneratedValues + { + PxVec3 Scale; + PxQuat Rotation; + PX_PHYSX_CORE_API PxMeshScaleGeneratedValues( const PxMeshScale* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMeshScale, Scale, PxMeshScaleGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMeshScale, Rotation, PxMeshScaleGeneratedValues) + struct PxMeshScaleGeneratedInfo + + { + static const char* getClassName() { return "PxMeshScale"; } + PxPropertyInfo Scale; + PxPropertyInfo Rotation; + + PX_PHYSX_CORE_API PxMeshScaleGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scale, inStartIndex + 0 );; + inOperator( Rotation, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxMeshScaleGeneratedInfo Info; + const PxMeshScaleGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxConvexMeshGeometryFlag__EnumConversion[] = { + { "eTIGHT_BOUNDS", static_cast( physx::PxConvexMeshGeometryFlag::eTIGHT_BOUNDS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxConvexMeshGeometryFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxConvexMeshGeometryFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxConvexMeshGeometry; + struct PxConvexMeshGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxMeshScale Scale; + PxConvexMesh * ConvexMesh; + PxConvexMeshGeometryFlags MeshFlags; + PX_PHYSX_CORE_API PxConvexMeshGeometryGeneratedValues( const PxConvexMeshGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConvexMeshGeometry, Scale, PxConvexMeshGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConvexMeshGeometry, ConvexMesh, PxConvexMeshGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConvexMeshGeometry, MeshFlags, PxConvexMeshGeometryGeneratedValues) + struct PxConvexMeshGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxConvexMeshGeometry"; } + PxPropertyInfo Scale; + PxPropertyInfo ConvexMesh; + PxPropertyInfo MeshFlags; + + PX_PHYSX_CORE_API PxConvexMeshGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scale, inStartIndex + 0 );; + inOperator( ConvexMesh, inStartIndex + 1 );; + inOperator( MeshFlags, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxConvexMeshGeometryGeneratedInfo Info; + const PxConvexMeshGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxSphereGeometry; + struct PxSphereGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxReal Radius; + PX_PHYSX_CORE_API PxSphereGeometryGeneratedValues( const PxSphereGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphereGeometry, Radius, PxSphereGeometryGeneratedValues) + struct PxSphereGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxSphereGeometry"; } + PxPropertyInfo Radius; + + PX_PHYSX_CORE_API PxSphereGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Radius, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSphereGeometryGeneratedInfo Info; + const PxSphereGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxPlaneGeometry; + struct PxPlaneGeometryGeneratedValues + : PxGeometryGeneratedValues { + PX_PHYSX_CORE_API PxPlaneGeometryGeneratedValues( const PxPlaneGeometry* inSource ); + }; + struct PxPlaneGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxPlaneGeometry"; } + + PX_PHYSX_CORE_API PxPlaneGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 0; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return 0 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxPlaneGeometryGeneratedInfo Info; + const PxPlaneGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxMeshGeometryFlag__EnumConversion[] = { + { "eDOUBLE_SIDED", static_cast( physx::PxMeshGeometryFlag::eDOUBLE_SIDED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxMeshGeometryFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxMeshGeometryFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxTriangleMeshGeometry; + struct PxTriangleMeshGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxMeshScale Scale; + PxMeshGeometryFlags MeshFlags; + PxTriangleMesh * TriangleMesh; + PX_PHYSX_CORE_API PxTriangleMeshGeometryGeneratedValues( const PxTriangleMeshGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTriangleMeshGeometry, Scale, PxTriangleMeshGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTriangleMeshGeometry, MeshFlags, PxTriangleMeshGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTriangleMeshGeometry, TriangleMesh, PxTriangleMeshGeometryGeneratedValues) + struct PxTriangleMeshGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxTriangleMeshGeometry"; } + PxPropertyInfo Scale; + PxPropertyInfo MeshFlags; + PxPropertyInfo TriangleMesh; + + PX_PHYSX_CORE_API PxTriangleMeshGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scale, inStartIndex + 0 );; + inOperator( MeshFlags, inStartIndex + 1 );; + inOperator( TriangleMesh, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxTriangleMeshGeometryGeneratedInfo Info; + const PxTriangleMeshGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxHeightFieldGeometry; + struct PxHeightFieldGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxHeightField * HeightField; + PxReal HeightScale; + PxReal RowScale; + PxReal ColumnScale; + PxMeshGeometryFlags HeightFieldFlags; + PX_PHYSX_CORE_API PxHeightFieldGeometryGeneratedValues( const PxHeightFieldGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldGeometry, HeightField, PxHeightFieldGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldGeometry, HeightScale, PxHeightFieldGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldGeometry, RowScale, PxHeightFieldGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldGeometry, ColumnScale, PxHeightFieldGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldGeometry, HeightFieldFlags, PxHeightFieldGeometryGeneratedValues) + struct PxHeightFieldGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxHeightFieldGeometry"; } + PxPropertyInfo HeightField; + PxPropertyInfo HeightScale; + PxPropertyInfo RowScale; + PxPropertyInfo ColumnScale; + PxPropertyInfo HeightFieldFlags; + + PX_PHYSX_CORE_API PxHeightFieldGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( HeightField, inStartIndex + 0 );; + inOperator( HeightScale, inStartIndex + 1 );; + inOperator( RowScale, inStartIndex + 2 );; + inOperator( ColumnScale, inStartIndex + 3 );; + inOperator( HeightFieldFlags, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxHeightFieldGeometryGeneratedInfo Info; + const PxHeightFieldGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxHeightFieldFormat__EnumConversion[] = { + { "eS16_TM", static_cast( physx::PxHeightFieldFormat::eS16_TM ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxHeightFieldFormat::Enum > { PxEnumTraits() : NameConversion( g_physx__PxHeightFieldFormat__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxHeightFieldFlag__EnumConversion[] = { + { "eNO_BOUNDARY_EDGES", static_cast( physx::PxHeightFieldFlag::eNO_BOUNDARY_EDGES ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxHeightFieldFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxHeightFieldFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxHeightFieldDesc; + struct PxHeightFieldDescGeneratedValues + { + PxU32 NbRows; + PxU32 NbColumns; + PxHeightFieldFormat::Enum Format; + PxStridedData Samples; + PxReal ConvexEdgeThreshold; + PxHeightFieldFlags Flags; + PX_PHYSX_CORE_API PxHeightFieldDescGeneratedValues( const PxHeightFieldDesc* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, NbRows, PxHeightFieldDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, NbColumns, PxHeightFieldDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, Format, PxHeightFieldDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, Samples, PxHeightFieldDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, ConvexEdgeThreshold, PxHeightFieldDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, Flags, PxHeightFieldDescGeneratedValues) + struct PxHeightFieldDescGeneratedInfo + + { + static const char* getClassName() { return "PxHeightFieldDesc"; } + PxPropertyInfo NbRows; + PxPropertyInfo NbColumns; + PxPropertyInfo Format; + PxPropertyInfo Samples; + PxPropertyInfo ConvexEdgeThreshold; + PxPropertyInfo Flags; + + PX_PHYSX_CORE_API PxHeightFieldDescGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 6; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( NbRows, inStartIndex + 0 );; + inOperator( NbColumns, inStartIndex + 1 );; + inOperator( Format, inStartIndex + 2 );; + inOperator( Samples, inStartIndex + 3 );; + inOperator( ConvexEdgeThreshold, inStartIndex + 4 );; + inOperator( Flags, inStartIndex + 5 );; + return 6 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxHeightFieldDescGeneratedInfo Info; + const PxHeightFieldDescGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxArticulationJointType__EnumConversion[] = { + { "ePRISMATIC", static_cast( physx::PxArticulationJointType::ePRISMATIC ) }, + { "eREVOLUTE", static_cast( physx::PxArticulationJointType::eREVOLUTE ) }, + { "eSPHERICAL", static_cast( physx::PxArticulationJointType::eSPHERICAL ) }, + { "eFIX", static_cast( physx::PxArticulationJointType::eFIX ) }, + { "eUNDEFINED", static_cast( physx::PxArticulationJointType::eUNDEFINED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxArticulationJointType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxArticulationJointType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxArticulationAxis__EnumConversion[] = { + { "eTWIST", static_cast( physx::PxArticulationAxis::eTWIST ) }, + { "eSWING1", static_cast( physx::PxArticulationAxis::eSWING1 ) }, + { "eSWING2", static_cast( physx::PxArticulationAxis::eSWING2 ) }, + { "eX", static_cast( physx::PxArticulationAxis::eX ) }, + { "eY", static_cast( physx::PxArticulationAxis::eY ) }, + { "eZ", static_cast( physx::PxArticulationAxis::eZ ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxArticulationAxis::Enum > { PxEnumTraits() : NameConversion( g_physx__PxArticulationAxis__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxArticulationMotion__EnumConversion[] = { + { "eLOCKED", static_cast( physx::PxArticulationMotion::eLOCKED ) }, + { "eLIMITED", static_cast( physx::PxArticulationMotion::eLIMITED ) }, + { "eFREE", static_cast( physx::PxArticulationMotion::eFREE ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxArticulationMotion::Enum > { PxEnumTraits() : NameConversion( g_physx__PxArticulationMotion__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxArticulationJointReducedCoordinate; + struct PxArticulationJointReducedCoordinateGeneratedValues + : PxArticulationJointBaseGeneratedValues { + PxArticulationJointType::Enum JointType; + PxArticulationMotion::Enum Motion[physx::PxArticulationAxis::eCOUNT]; + PxReal FrictionCoefficient; + const char * ConcreteTypeName; + PxReal MaxJointVelocity; + PX_PHYSX_CORE_API PxArticulationJointReducedCoordinateGeneratedValues( const PxArticulationJointReducedCoordinate* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointReducedCoordinate, JointType, PxArticulationJointReducedCoordinateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointReducedCoordinate, Motion, PxArticulationJointReducedCoordinateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointReducedCoordinate, FrictionCoefficient, PxArticulationJointReducedCoordinateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointReducedCoordinate, ConcreteTypeName, PxArticulationJointReducedCoordinateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointReducedCoordinate, MaxJointVelocity, PxArticulationJointReducedCoordinateGeneratedValues) + struct PxArticulationJointReducedCoordinateGeneratedInfo + : PxArticulationJointBaseGeneratedInfo + { + static const char* getClassName() { return "PxArticulationJointReducedCoordinate"; } + PxPropertyInfo JointType; + PxIndexedPropertyInfo Motion; + PxPropertyInfo FrictionCoefficient; + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MaxJointVelocity; + + PX_PHYSX_CORE_API PxArticulationJointReducedCoordinateGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxArticulationJointBaseGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxArticulationJointBaseGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxArticulationJointBaseGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( JointType, inStartIndex + 0 );; + inOperator( Motion, inStartIndex + 1 );; + inOperator( FrictionCoefficient, inStartIndex + 2 );; + inOperator( ConcreteTypeName, inStartIndex + 3 );; + inOperator( MaxJointVelocity, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationJointReducedCoordinateGeneratedInfo Info; + const PxArticulationJointReducedCoordinateGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxSceneFlag__EnumConversion[] = { + { "eENABLE_ACTIVE_ACTORS", static_cast( physx::PxSceneFlag::eENABLE_ACTIVE_ACTORS ) }, + { "eENABLE_CCD", static_cast( physx::PxSceneFlag::eENABLE_CCD ) }, + { "eDISABLE_CCD_RESWEEP", static_cast( physx::PxSceneFlag::eDISABLE_CCD_RESWEEP ) }, + { "eADAPTIVE_FORCE", static_cast( physx::PxSceneFlag::eADAPTIVE_FORCE ) }, + { "eENABLE_KINEMATIC_STATIC_PAIRS", static_cast( physx::PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS ) }, + { "eENABLE_KINEMATIC_PAIRS", static_cast( physx::PxSceneFlag::eENABLE_KINEMATIC_PAIRS ) }, + { "eENABLE_PCM", static_cast( physx::PxSceneFlag::eENABLE_PCM ) }, + { "eDISABLE_CONTACT_REPORT_BUFFER_RESIZE", static_cast( physx::PxSceneFlag::eDISABLE_CONTACT_REPORT_BUFFER_RESIZE ) }, + { "eDISABLE_CONTACT_CACHE", static_cast( physx::PxSceneFlag::eDISABLE_CONTACT_CACHE ) }, + { "eREQUIRE_RW_LOCK", static_cast( physx::PxSceneFlag::eREQUIRE_RW_LOCK ) }, + { "eENABLE_STABILIZATION", static_cast( physx::PxSceneFlag::eENABLE_STABILIZATION ) }, + { "eENABLE_AVERAGE_POINT", static_cast( physx::PxSceneFlag::eENABLE_AVERAGE_POINT ) }, + { "eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS", static_cast( physx::PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS ) }, + { "eENABLE_GPU_DYNAMICS", static_cast( physx::PxSceneFlag::eENABLE_GPU_DYNAMICS ) }, + { "eENABLE_ENHANCED_DETERMINISM", static_cast( physx::PxSceneFlag::eENABLE_ENHANCED_DETERMINISM ) }, + { "eENABLE_FRICTION_EVERY_ITERATION", static_cast( physx::PxSceneFlag::eENABLE_FRICTION_EVERY_ITERATION ) }, + { "eMUTABLE_FLAGS", static_cast( physx::PxSceneFlag::eMUTABLE_FLAGS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxSceneFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxSceneFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxActorTypeFlag__EnumConversion[] = { + { "eRIGID_STATIC", static_cast( physx::PxActorTypeFlag::eRIGID_STATIC ) }, + { "eRIGID_DYNAMIC", static_cast( physx::PxActorTypeFlag::eRIGID_DYNAMIC ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxActorTypeFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxActorTypeFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxFrictionType__EnumConversion[] = { + { "ePATCH", static_cast( physx::PxFrictionType::ePATCH ) }, + { "eONE_DIRECTIONAL", static_cast( physx::PxFrictionType::eONE_DIRECTIONAL ) }, + { "eTWO_DIRECTIONAL", static_cast( physx::PxFrictionType::eTWO_DIRECTIONAL ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxFrictionType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxFrictionType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxVisualizationParameter__EnumConversion[] = { + { "eSCALE", static_cast( physx::PxVisualizationParameter::eSCALE ) }, + { "eWORLD_AXES", static_cast( physx::PxVisualizationParameter::eWORLD_AXES ) }, + { "eBODY_AXES", static_cast( physx::PxVisualizationParameter::eBODY_AXES ) }, + { "eBODY_MASS_AXES", static_cast( physx::PxVisualizationParameter::eBODY_MASS_AXES ) }, + { "eBODY_LIN_VELOCITY", static_cast( physx::PxVisualizationParameter::eBODY_LIN_VELOCITY ) }, + { "eBODY_ANG_VELOCITY", static_cast( physx::PxVisualizationParameter::eBODY_ANG_VELOCITY ) }, + { "eCONTACT_POINT", static_cast( physx::PxVisualizationParameter::eCONTACT_POINT ) }, + { "eCONTACT_NORMAL", static_cast( physx::PxVisualizationParameter::eCONTACT_NORMAL ) }, + { "eCONTACT_ERROR", static_cast( physx::PxVisualizationParameter::eCONTACT_ERROR ) }, + { "eCONTACT_FORCE", static_cast( physx::PxVisualizationParameter::eCONTACT_FORCE ) }, + { "eACTOR_AXES", static_cast( physx::PxVisualizationParameter::eACTOR_AXES ) }, + { "eCOLLISION_AABBS", static_cast( physx::PxVisualizationParameter::eCOLLISION_AABBS ) }, + { "eCOLLISION_SHAPES", static_cast( physx::PxVisualizationParameter::eCOLLISION_SHAPES ) }, + { "eCOLLISION_AXES", static_cast( physx::PxVisualizationParameter::eCOLLISION_AXES ) }, + { "eCOLLISION_COMPOUNDS", static_cast( physx::PxVisualizationParameter::eCOLLISION_COMPOUNDS ) }, + { "eCOLLISION_FNORMALS", static_cast( physx::PxVisualizationParameter::eCOLLISION_FNORMALS ) }, + { "eCOLLISION_EDGES", static_cast( physx::PxVisualizationParameter::eCOLLISION_EDGES ) }, + { "eCOLLISION_STATIC", static_cast( physx::PxVisualizationParameter::eCOLLISION_STATIC ) }, + { "eCOLLISION_DYNAMIC", static_cast( physx::PxVisualizationParameter::eCOLLISION_DYNAMIC ) }, + { "eDEPRECATED_COLLISION_PAIRS", static_cast( physx::PxVisualizationParameter::eDEPRECATED_COLLISION_PAIRS ) }, + { "eJOINT_LOCAL_FRAMES", static_cast( physx::PxVisualizationParameter::eJOINT_LOCAL_FRAMES ) }, + { "eJOINT_LIMITS", static_cast( physx::PxVisualizationParameter::eJOINT_LIMITS ) }, + { "eCULL_BOX", static_cast( physx::PxVisualizationParameter::eCULL_BOX ) }, + { "eMBP_REGIONS", static_cast( physx::PxVisualizationParameter::eMBP_REGIONS ) }, + { "eNUM_VALUES", static_cast( physx::PxVisualizationParameter::eNUM_VALUES ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxVisualizationParameter::Enum > { PxEnumTraits() : NameConversion( g_physx__PxVisualizationParameter__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxPruningStructureType__EnumConversion[] = { + { "eNONE", static_cast( physx::PxPruningStructureType::eNONE ) }, + { "eDYNAMIC_AABB_TREE", static_cast( physx::PxPruningStructureType::eDYNAMIC_AABB_TREE ) }, + { "eSTATIC_AABB_TREE", static_cast( physx::PxPruningStructureType::eSTATIC_AABB_TREE ) }, + { "eLAST", static_cast( physx::PxPruningStructureType::eLAST ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxPruningStructureType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxPruningStructureType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxSceneQueryUpdateMode__EnumConversion[] = { + { "eBUILD_ENABLED_COMMIT_ENABLED", static_cast( physx::PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED ) }, + { "eBUILD_ENABLED_COMMIT_DISABLED", static_cast( physx::PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_DISABLED ) }, + { "eBUILD_DISABLED_COMMIT_DISABLED", static_cast( physx::PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxSceneQueryUpdateMode::Enum > { PxEnumTraits() : NameConversion( g_physx__PxSceneQueryUpdateMode__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxHitFlag__EnumConversion[] = { + { "ePOSITION", static_cast( physx::PxHitFlag::ePOSITION ) }, + { "eNORMAL", static_cast( physx::PxHitFlag::eNORMAL ) }, + { "eUV", static_cast( physx::PxHitFlag::eUV ) }, + { "eASSUME_NO_INITIAL_OVERLAP", static_cast( physx::PxHitFlag::eASSUME_NO_INITIAL_OVERLAP ) }, + { "eMESH_MULTIPLE", static_cast( physx::PxHitFlag::eMESH_MULTIPLE ) }, + { "eMESH_ANY", static_cast( physx::PxHitFlag::eMESH_ANY ) }, + { "eMESH_BOTH_SIDES", static_cast( physx::PxHitFlag::eMESH_BOTH_SIDES ) }, + { "ePRECISE_SWEEP", static_cast( physx::PxHitFlag::ePRECISE_SWEEP ) }, + { "eMTD", static_cast( physx::PxHitFlag::eMTD ) }, + { "eFACE_INDEX", static_cast( physx::PxHitFlag::eFACE_INDEX ) }, + { "eDEFAULT", static_cast( physx::PxHitFlag::eDEFAULT ) }, + { "eMODIFIABLE_FLAGS", static_cast( physx::PxHitFlag::eMODIFIABLE_FLAGS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxHitFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxHitFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxBroadPhaseType__EnumConversion[] = { + { "eSAP", static_cast( physx::PxBroadPhaseType::eSAP ) }, + { "eMBP", static_cast( physx::PxBroadPhaseType::eMBP ) }, + { "eABP", static_cast( physx::PxBroadPhaseType::eABP ) }, + { "eGPU", static_cast( physx::PxBroadPhaseType::eGPU ) }, + { "eLAST", static_cast( physx::PxBroadPhaseType::eLAST ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxBroadPhaseType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxBroadPhaseType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxScene; + struct PxSceneGeneratedValues + { + PxSceneFlags Flags; + PxSceneLimits Limits; + PxU32 Timestamp; + PxCpuDispatcher * CpuDispatcher; + PxGpuDispatcher * GpuDispatcher; + PxSimulationEventCallback * SimulationEventCallback; + PxContactModifyCallback * ContactModifyCallback; + PxCCDContactModifyCallback * CCDContactModifyCallback; + PxBroadPhaseCallback * BroadPhaseCallback; + PxU32 FilterShaderDataSize; + PxSimulationFilterShader FilterShader; + PxSimulationFilterCallback * FilterCallback; + PxVec3 Gravity; + PxReal BounceThresholdVelocity; + PxU32 CCDMaxPasses; + PxReal FrictionOffsetThreshold; + PxFrictionType::Enum FrictionType; + PxBounds3 VisualizationCullingBox; + PxPruningStructureType::Enum StaticStructure; + PxPruningStructureType::Enum DynamicStructure; + PxU32 DynamicTreeRebuildRateHint; + PxSceneQueryUpdateMode::Enum SceneQueryUpdateMode; + PxU32 SceneQueryStaticTimestamp; + PxBroadPhaseType::Enum BroadPhaseType; + PxTaskManager * TaskManager; + PxU32 MaxNbContactDataBlocksUsed; + PxU32 ContactReportStreamBufferSize; + PxU32 SolverBatchSize; + PxReal WakeCounterResetValue; + void * UserData; + PxSimulationStatistics SimulationStatistics; + PX_PHYSX_CORE_API PxSceneGeneratedValues( const PxScene* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, Flags, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, Limits, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, Timestamp, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, CpuDispatcher, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, GpuDispatcher, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, SimulationEventCallback, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, ContactModifyCallback, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, CCDContactModifyCallback, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, BroadPhaseCallback, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, FilterShaderDataSize, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, FilterShader, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, FilterCallback, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, Gravity, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, BounceThresholdVelocity, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, CCDMaxPasses, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, FrictionOffsetThreshold, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, FrictionType, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, VisualizationCullingBox, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, StaticStructure, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, DynamicStructure, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, DynamicTreeRebuildRateHint, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, SceneQueryUpdateMode, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, SceneQueryStaticTimestamp, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, BroadPhaseType, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, TaskManager, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, MaxNbContactDataBlocksUsed, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, ContactReportStreamBufferSize, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, SolverBatchSize, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, WakeCounterResetValue, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, UserData, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, SimulationStatistics, PxSceneGeneratedValues) + struct PxSceneGeneratedInfo + + { + static const char* getClassName() { return "PxScene"; } + PxReadOnlyPropertyInfo Flags; + PxPropertyInfo Limits; + PxReadOnlyPropertyInfo Timestamp; + PxReadOnlyFilteredCollectionPropertyInfo Actors; + PxReadOnlyCollectionPropertyInfo Articulations; + PxReadOnlyCollectionPropertyInfo Constraints; + PxReadOnlyCollectionPropertyInfo Aggregates; + PxReadOnlyPropertyInfo CpuDispatcher; + PxReadOnlyPropertyInfo GpuDispatcher; + PxPropertyInfo SimulationEventCallback; + PxPropertyInfo ContactModifyCallback; + PxPropertyInfo CCDContactModifyCallback; + PxPropertyInfo BroadPhaseCallback; + PxReadOnlyPropertyInfo FilterShaderDataSize; + PxReadOnlyPropertyInfo FilterShader; + PxReadOnlyPropertyInfo FilterCallback; + PxPropertyInfo Gravity; + PxPropertyInfo BounceThresholdVelocity; + PxPropertyInfo CCDMaxPasses; + PxReadOnlyPropertyInfo FrictionOffsetThreshold; + PxPropertyInfo FrictionType; + PxPropertyInfo VisualizationCullingBox; + PxReadOnlyPropertyInfo StaticStructure; + PxReadOnlyPropertyInfo DynamicStructure; + PxPropertyInfo DynamicTreeRebuildRateHint; + PxPropertyInfo SceneQueryUpdateMode; + PxReadOnlyPropertyInfo SceneQueryStaticTimestamp; + PxReadOnlyPropertyInfo BroadPhaseType; + PxReadOnlyCollectionPropertyInfo BroadPhaseRegions; + PxReadOnlyPropertyInfo TaskManager; + PxWriteOnlyPropertyInfo NbContactDataBlocks; + PxReadOnlyPropertyInfo MaxNbContactDataBlocksUsed; + PxReadOnlyPropertyInfo ContactReportStreamBufferSize; + PxPropertyInfo SolverBatchSize; + PxReadOnlyPropertyInfo WakeCounterResetValue; + PxPropertyInfo UserData; + SimulationStatisticsProperty SimulationStatistics; + + PX_PHYSX_CORE_API PxSceneGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 37; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Flags, inStartIndex + 0 );; + inOperator( Limits, inStartIndex + 1 );; + inOperator( Timestamp, inStartIndex + 2 );; + inOperator( Actors, inStartIndex + 3 );; + inOperator( Articulations, inStartIndex + 4 );; + inOperator( Constraints, inStartIndex + 5 );; + inOperator( Aggregates, inStartIndex + 6 );; + inOperator( CpuDispatcher, inStartIndex + 7 );; + inOperator( GpuDispatcher, inStartIndex + 8 );; + inOperator( SimulationEventCallback, inStartIndex + 9 );; + inOperator( ContactModifyCallback, inStartIndex + 10 );; + inOperator( CCDContactModifyCallback, inStartIndex + 11 );; + inOperator( BroadPhaseCallback, inStartIndex + 12 );; + inOperator( FilterShaderDataSize, inStartIndex + 13 );; + inOperator( FilterShader, inStartIndex + 14 );; + inOperator( FilterCallback, inStartIndex + 15 );; + inOperator( Gravity, inStartIndex + 16 );; + inOperator( BounceThresholdVelocity, inStartIndex + 17 );; + inOperator( CCDMaxPasses, inStartIndex + 18 );; + inOperator( FrictionOffsetThreshold, inStartIndex + 19 );; + inOperator( FrictionType, inStartIndex + 20 );; + inOperator( VisualizationCullingBox, inStartIndex + 21 );; + inOperator( StaticStructure, inStartIndex + 22 );; + inOperator( DynamicStructure, inStartIndex + 23 );; + inOperator( DynamicTreeRebuildRateHint, inStartIndex + 24 );; + inOperator( SceneQueryUpdateMode, inStartIndex + 25 );; + inOperator( SceneQueryStaticTimestamp, inStartIndex + 26 );; + inOperator( BroadPhaseType, inStartIndex + 27 );; + inOperator( BroadPhaseRegions, inStartIndex + 28 );; + inOperator( TaskManager, inStartIndex + 29 );; + inOperator( NbContactDataBlocks, inStartIndex + 30 );; + inOperator( MaxNbContactDataBlocksUsed, inStartIndex + 31 );; + inOperator( ContactReportStreamBufferSize, inStartIndex + 32 );; + inOperator( SolverBatchSize, inStartIndex + 33 );; + inOperator( WakeCounterResetValue, inStartIndex + 34 );; + inOperator( UserData, inStartIndex + 35 );; + inOperator( SimulationStatistics, inStartIndex + 36 );; + return 37 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSceneGeneratedInfo Info; + const PxSceneGeneratedInfo* getInfo() { return &Info; } + }; + + class PxSceneLimits; + struct PxSceneLimitsGeneratedValues + { + PxU32 MaxNbActors; + PxU32 MaxNbBodies; + PxU32 MaxNbStaticShapes; + PxU32 MaxNbDynamicShapes; + PxU32 MaxNbAggregates; + PxU32 MaxNbConstraints; + PxU32 MaxNbRegions; + PxU32 MaxNbBroadPhaseOverlaps; + PX_PHYSX_CORE_API PxSceneLimitsGeneratedValues( const PxSceneLimits* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbActors, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbBodies, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbStaticShapes, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbDynamicShapes, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbAggregates, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbConstraints, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbRegions, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbBroadPhaseOverlaps, PxSceneLimitsGeneratedValues) + struct PxSceneLimitsGeneratedInfo + + { + static const char* getClassName() { return "PxSceneLimits"; } + PxPropertyInfo MaxNbActors; + PxPropertyInfo MaxNbBodies; + PxPropertyInfo MaxNbStaticShapes; + PxPropertyInfo MaxNbDynamicShapes; + PxPropertyInfo MaxNbAggregates; + PxPropertyInfo MaxNbConstraints; + PxPropertyInfo MaxNbRegions; + PxPropertyInfo MaxNbBroadPhaseOverlaps; + + PX_PHYSX_CORE_API PxSceneLimitsGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 8; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MaxNbActors, inStartIndex + 0 );; + inOperator( MaxNbBodies, inStartIndex + 1 );; + inOperator( MaxNbStaticShapes, inStartIndex + 2 );; + inOperator( MaxNbDynamicShapes, inStartIndex + 3 );; + inOperator( MaxNbAggregates, inStartIndex + 4 );; + inOperator( MaxNbConstraints, inStartIndex + 5 );; + inOperator( MaxNbRegions, inStartIndex + 6 );; + inOperator( MaxNbBroadPhaseOverlaps, inStartIndex + 7 );; + return 8 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSceneLimitsGeneratedInfo Info; + const PxSceneLimitsGeneratedInfo* getInfo() { return &Info; } + }; + + struct PxgDynamicsMemoryConfig; + struct PxgDynamicsMemoryConfigGeneratedValues + { + PxU32 ConstraintBufferCapacity; + PxU32 ContactBufferCapacity; + PxU32 TempBufferCapacity; + PxU32 ContactStreamSize; + PxU32 PatchStreamSize; + PxU32 ForceStreamCapacity; + PxU32 HeapCapacity; + PxU32 FoundLostPairsCapacity; + PX_PHYSX_CORE_API PxgDynamicsMemoryConfigGeneratedValues( const PxgDynamicsMemoryConfig* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, ConstraintBufferCapacity, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, ContactBufferCapacity, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, TempBufferCapacity, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, ContactStreamSize, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, PatchStreamSize, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, ForceStreamCapacity, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, HeapCapacity, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, FoundLostPairsCapacity, PxgDynamicsMemoryConfigGeneratedValues) + struct PxgDynamicsMemoryConfigGeneratedInfo + + { + static const char* getClassName() { return "PxgDynamicsMemoryConfig"; } + PxPropertyInfo ConstraintBufferCapacity; + PxPropertyInfo ContactBufferCapacity; + PxPropertyInfo TempBufferCapacity; + PxPropertyInfo ContactStreamSize; + PxPropertyInfo PatchStreamSize; + PxPropertyInfo ForceStreamCapacity; + PxPropertyInfo HeapCapacity; + PxPropertyInfo FoundLostPairsCapacity; + + PX_PHYSX_CORE_API PxgDynamicsMemoryConfigGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 8; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ConstraintBufferCapacity, inStartIndex + 0 );; + inOperator( ContactBufferCapacity, inStartIndex + 1 );; + inOperator( TempBufferCapacity, inStartIndex + 2 );; + inOperator( ContactStreamSize, inStartIndex + 3 );; + inOperator( PatchStreamSize, inStartIndex + 4 );; + inOperator( ForceStreamCapacity, inStartIndex + 5 );; + inOperator( HeapCapacity, inStartIndex + 6 );; + inOperator( FoundLostPairsCapacity, inStartIndex + 7 );; + return 8 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxgDynamicsMemoryConfigGeneratedInfo Info; + const PxgDynamicsMemoryConfigGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxPairFilteringMode__EnumConversion[] = { + { "eKEEP", static_cast( physx::PxPairFilteringMode::eKEEP ) }, + { "eSUPPRESS", static_cast( physx::PxPairFilteringMode::eSUPPRESS ) }, + { "eKILL", static_cast( physx::PxPairFilteringMode::eKILL ) }, + { "eDEFAULT", static_cast( physx::PxPairFilteringMode::eDEFAULT ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxPairFilteringMode::Enum > { PxEnumTraits() : NameConversion( g_physx__PxPairFilteringMode__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxSolverType__EnumConversion[] = { + { "ePGS", static_cast( physx::PxSolverType::ePGS ) }, + { "eTGS", static_cast( physx::PxSolverType::eTGS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxSolverType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxSolverType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxSceneDesc; + struct PxSceneDescGeneratedValues + { + PxVec3 Gravity; + PxSimulationEventCallback * SimulationEventCallback; + PxContactModifyCallback * ContactModifyCallback; + PxCCDContactModifyCallback * CcdContactModifyCallback; + const void * FilterShaderData; + PxU32 FilterShaderDataSize; + PxSimulationFilterShader FilterShader; + PxSimulationFilterCallback * FilterCallback; + PxPairFilteringMode::Enum KineKineFilteringMode; + PxPairFilteringMode::Enum StaticKineFilteringMode; + PxBroadPhaseType::Enum BroadPhaseType; + PxBroadPhaseCallback * BroadPhaseCallback; + PxSceneLimits Limits; + PxFrictionType::Enum FrictionType; + PxSolverType::Enum SolverType; + PxReal BounceThresholdVelocity; + PxReal FrictionOffsetThreshold; + PxReal CcdMaxSeparation; + PxReal SolverOffsetSlop; + PxSceneFlags Flags; + PxCpuDispatcher * CpuDispatcher; + PxGpuDispatcher * GpuDispatcher; + PxPruningStructureType::Enum StaticStructure; + PxPruningStructureType::Enum DynamicStructure; + PxU32 DynamicTreeRebuildRateHint; + PxSceneQueryUpdateMode::Enum SceneQueryUpdateMode; + void * UserData; + PxU32 SolverBatchSize; + PxU32 NbContactDataBlocks; + PxU32 MaxNbContactDataBlocks; + PxReal MaxBiasCoefficient; + PxU32 ContactReportStreamBufferSize; + PxU32 CcdMaxPasses; + PxReal WakeCounterResetValue; + PxBounds3 SanityBounds; + PxgDynamicsMemoryConfig GpuDynamicsConfig; + PxU32 GpuMaxNumPartitions; + PxU32 GpuComputeVersion; + PX_PHYSX_CORE_API PxSceneDescGeneratedValues( const PxSceneDesc* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, Gravity, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SimulationEventCallback, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, ContactModifyCallback, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, CcdContactModifyCallback, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FilterShaderData, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FilterShaderDataSize, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FilterShader, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FilterCallback, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, KineKineFilteringMode, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, StaticKineFilteringMode, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, BroadPhaseType, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, BroadPhaseCallback, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, Limits, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FrictionType, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SolverType, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, BounceThresholdVelocity, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FrictionOffsetThreshold, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, CcdMaxSeparation, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SolverOffsetSlop, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, Flags, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, CpuDispatcher, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, GpuDispatcher, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, StaticStructure, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, DynamicStructure, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, DynamicTreeRebuildRateHint, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SceneQueryUpdateMode, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, UserData, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SolverBatchSize, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, NbContactDataBlocks, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, MaxNbContactDataBlocks, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, MaxBiasCoefficient, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, ContactReportStreamBufferSize, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, CcdMaxPasses, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, WakeCounterResetValue, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SanityBounds, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, GpuDynamicsConfig, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, GpuMaxNumPartitions, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, GpuComputeVersion, PxSceneDescGeneratedValues) + struct PxSceneDescGeneratedInfo + + { + static const char* getClassName() { return "PxSceneDesc"; } + PxWriteOnlyPropertyInfo ToDefault; + PxPropertyInfo Gravity; + PxPropertyInfo SimulationEventCallback; + PxPropertyInfo ContactModifyCallback; + PxPropertyInfo CcdContactModifyCallback; + PxPropertyInfo FilterShaderData; + PxPropertyInfo FilterShaderDataSize; + PxPropertyInfo FilterShader; + PxPropertyInfo FilterCallback; + PxPropertyInfo KineKineFilteringMode; + PxPropertyInfo StaticKineFilteringMode; + PxPropertyInfo BroadPhaseType; + PxPropertyInfo BroadPhaseCallback; + PxPropertyInfo Limits; + PxPropertyInfo FrictionType; + PxPropertyInfo SolverType; + PxPropertyInfo BounceThresholdVelocity; + PxPropertyInfo FrictionOffsetThreshold; + PxPropertyInfo CcdMaxSeparation; + PxPropertyInfo SolverOffsetSlop; + PxPropertyInfo Flags; + PxPropertyInfo CpuDispatcher; + PxPropertyInfo GpuDispatcher; + PxPropertyInfo StaticStructure; + PxPropertyInfo DynamicStructure; + PxPropertyInfo DynamicTreeRebuildRateHint; + PxPropertyInfo SceneQueryUpdateMode; + PxPropertyInfo UserData; + PxPropertyInfo SolverBatchSize; + PxPropertyInfo NbContactDataBlocks; + PxPropertyInfo MaxNbContactDataBlocks; + PxPropertyInfo MaxBiasCoefficient; + PxPropertyInfo ContactReportStreamBufferSize; + PxPropertyInfo CcdMaxPasses; + PxPropertyInfo WakeCounterResetValue; + PxPropertyInfo SanityBounds; + PxPropertyInfo GpuDynamicsConfig; + PxPropertyInfo GpuMaxNumPartitions; + PxPropertyInfo GpuComputeVersion; + + PX_PHYSX_CORE_API PxSceneDescGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 39; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ToDefault, inStartIndex + 0 );; + inOperator( Gravity, inStartIndex + 1 );; + inOperator( SimulationEventCallback, inStartIndex + 2 );; + inOperator( ContactModifyCallback, inStartIndex + 3 );; + inOperator( CcdContactModifyCallback, inStartIndex + 4 );; + inOperator( FilterShaderData, inStartIndex + 5 );; + inOperator( FilterShaderDataSize, inStartIndex + 6 );; + inOperator( FilterShader, inStartIndex + 7 );; + inOperator( FilterCallback, inStartIndex + 8 );; + inOperator( KineKineFilteringMode, inStartIndex + 9 );; + inOperator( StaticKineFilteringMode, inStartIndex + 10 );; + inOperator( BroadPhaseType, inStartIndex + 11 );; + inOperator( BroadPhaseCallback, inStartIndex + 12 );; + inOperator( Limits, inStartIndex + 13 );; + inOperator( FrictionType, inStartIndex + 14 );; + inOperator( SolverType, inStartIndex + 15 );; + inOperator( BounceThresholdVelocity, inStartIndex + 16 );; + inOperator( FrictionOffsetThreshold, inStartIndex + 17 );; + inOperator( CcdMaxSeparation, inStartIndex + 18 );; + inOperator( SolverOffsetSlop, inStartIndex + 19 );; + inOperator( Flags, inStartIndex + 20 );; + inOperator( CpuDispatcher, inStartIndex + 21 );; + inOperator( GpuDispatcher, inStartIndex + 22 );; + inOperator( StaticStructure, inStartIndex + 23 );; + inOperator( DynamicStructure, inStartIndex + 24 );; + inOperator( DynamicTreeRebuildRateHint, inStartIndex + 25 );; + inOperator( SceneQueryUpdateMode, inStartIndex + 26 );; + inOperator( UserData, inStartIndex + 27 );; + inOperator( SolverBatchSize, inStartIndex + 28 );; + inOperator( NbContactDataBlocks, inStartIndex + 29 );; + inOperator( MaxNbContactDataBlocks, inStartIndex + 30 );; + inOperator( MaxBiasCoefficient, inStartIndex + 31 );; + inOperator( ContactReportStreamBufferSize, inStartIndex + 32 );; + inOperator( CcdMaxPasses, inStartIndex + 33 );; + inOperator( WakeCounterResetValue, inStartIndex + 34 );; + inOperator( SanityBounds, inStartIndex + 35 );; + inOperator( GpuDynamicsConfig, inStartIndex + 36 );; + inOperator( GpuMaxNumPartitions, inStartIndex + 37 );; + inOperator( GpuComputeVersion, inStartIndex + 38 );; + return 39 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSceneDescGeneratedInfo Info; + const PxSceneDescGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxSimulationStatistics__RbPairStatsTypeConversion[] = { + { "eDISCRETE_CONTACT_PAIRS", static_cast( physx::PxSimulationStatistics::eDISCRETE_CONTACT_PAIRS ) }, + { "eCCD_PAIRS", static_cast( physx::PxSimulationStatistics::eCCD_PAIRS ) }, + { "eMODIFIED_CONTACT_PAIRS", static_cast( physx::PxSimulationStatistics::eMODIFIED_CONTACT_PAIRS ) }, + { "eTRIGGER_PAIRS", static_cast( physx::PxSimulationStatistics::eTRIGGER_PAIRS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxSimulationStatistics::RbPairStatsType > { PxEnumTraits() : NameConversion( g_physx__PxSimulationStatistics__RbPairStatsTypeConversion ) {} const PxU32ToName* NameConversion; }; + class PxSimulationStatistics; + struct PxSimulationStatisticsGeneratedValues + { + PxU32 NbActiveConstraints; + PxU32 NbActiveDynamicBodies; + PxU32 NbActiveKinematicBodies; + PxU32 NbStaticBodies; + PxU32 NbDynamicBodies; + PxU32 NbAggregates; + PxU32 NbArticulations; + PxU32 NbAxisSolverConstraints; + PxU32 CompressedContactSize; + PxU32 RequiredContactConstraintMemory; + PxU32 PeakConstraintMemory; + PxU32 NbDiscreteContactPairsTotal; + PxU32 NbDiscreteContactPairsWithCacheHits; + PxU32 NbDiscreteContactPairsWithContacts; + PxU32 NbNewPairs; + PxU32 NbLostPairs; + PxU32 NbNewTouches; + PxU32 NbLostTouches; + PxU32 NbPartitions; + PxU32 NbBroadPhaseAdds; + PxU32 NbBroadPhaseRemoves; + PxU32 NbDiscreteContactPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 NbModifiedContactPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 NbCCDPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 NbTriggerPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 NbShapes[PxGeometryType::eGEOMETRY_COUNT]; + PX_PHYSX_CORE_API PxSimulationStatisticsGeneratedValues( const PxSimulationStatistics* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbActiveConstraints, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbActiveDynamicBodies, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbActiveKinematicBodies, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbStaticBodies, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbDynamicBodies, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbAggregates, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbArticulations, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbAxisSolverConstraints, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, CompressedContactSize, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, RequiredContactConstraintMemory, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, PeakConstraintMemory, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbDiscreteContactPairsTotal, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbDiscreteContactPairsWithCacheHits, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbDiscreteContactPairsWithContacts, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbNewPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbLostPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbNewTouches, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbLostTouches, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbPartitions, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbBroadPhaseAdds, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbBroadPhaseRemoves, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbDiscreteContactPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbModifiedContactPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbCCDPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbTriggerPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbShapes, PxSimulationStatisticsGeneratedValues) + struct PxSimulationStatisticsGeneratedInfo + + { + static const char* getClassName() { return "PxSimulationStatistics"; } + PxPropertyInfo NbActiveConstraints; + PxPropertyInfo NbActiveDynamicBodies; + PxPropertyInfo NbActiveKinematicBodies; + PxPropertyInfo NbStaticBodies; + PxPropertyInfo NbDynamicBodies; + PxPropertyInfo NbAggregates; + PxPropertyInfo NbArticulations; + PxPropertyInfo NbAxisSolverConstraints; + PxPropertyInfo CompressedContactSize; + PxPropertyInfo RequiredContactConstraintMemory; + PxPropertyInfo PeakConstraintMemory; + PxPropertyInfo NbDiscreteContactPairsTotal; + PxPropertyInfo NbDiscreteContactPairsWithCacheHits; + PxPropertyInfo NbDiscreteContactPairsWithContacts; + PxPropertyInfo NbNewPairs; + PxPropertyInfo NbLostPairs; + PxPropertyInfo NbNewTouches; + PxPropertyInfo NbLostTouches; + PxPropertyInfo NbPartitions; + PxPropertyInfo NbBroadPhaseAdds; + PxPropertyInfo NbBroadPhaseRemoves; + NbDiscreteContactPairsProperty NbDiscreteContactPairs; + NbModifiedContactPairsProperty NbModifiedContactPairs; + NbCCDPairsProperty NbCCDPairs; + NbTriggerPairsProperty NbTriggerPairs; + NbShapesProperty NbShapes; + + PX_PHYSX_CORE_API PxSimulationStatisticsGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 26; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( NbActiveConstraints, inStartIndex + 0 );; + inOperator( NbActiveDynamicBodies, inStartIndex + 1 );; + inOperator( NbActiveKinematicBodies, inStartIndex + 2 );; + inOperator( NbStaticBodies, inStartIndex + 3 );; + inOperator( NbDynamicBodies, inStartIndex + 4 );; + inOperator( NbAggregates, inStartIndex + 5 );; + inOperator( NbArticulations, inStartIndex + 6 );; + inOperator( NbAxisSolverConstraints, inStartIndex + 7 );; + inOperator( CompressedContactSize, inStartIndex + 8 );; + inOperator( RequiredContactConstraintMemory, inStartIndex + 9 );; + inOperator( PeakConstraintMemory, inStartIndex + 10 );; + inOperator( NbDiscreteContactPairsTotal, inStartIndex + 11 );; + inOperator( NbDiscreteContactPairsWithCacheHits, inStartIndex + 12 );; + inOperator( NbDiscreteContactPairsWithContacts, inStartIndex + 13 );; + inOperator( NbNewPairs, inStartIndex + 14 );; + inOperator( NbLostPairs, inStartIndex + 15 );; + inOperator( NbNewTouches, inStartIndex + 16 );; + inOperator( NbLostTouches, inStartIndex + 17 );; + inOperator( NbPartitions, inStartIndex + 18 );; + inOperator( NbBroadPhaseAdds, inStartIndex + 19 );; + inOperator( NbBroadPhaseRemoves, inStartIndex + 20 );; + inOperator( NbDiscreteContactPairs, inStartIndex + 21 );; + inOperator( NbModifiedContactPairs, inStartIndex + 22 );; + inOperator( NbCCDPairs, inStartIndex + 23 );; + inOperator( NbTriggerPairs, inStartIndex + 24 );; + inOperator( NbShapes, inStartIndex + 25 );; + return 26 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSimulationStatisticsGeneratedInfo Info; + const PxSimulationStatisticsGeneratedInfo* getInfo() { return &Info; } + }; + + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON +#undef PX_PROPERTY_INFO_NAME diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h new file mode 100644 index 000000000..bdadf8d63 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h @@ -0,0 +1,388 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_METADATACOMPARE_H +#define PX_METADATACOMPARE_H +#include "PxMetaDataObjects.h" +#include "PsInlineArray.h" + +//Implement a basic equality comparison system based on the meta data system. +//if you implement a particular areequal specialized to exactly your type +//before including this file it will be called in preference to the completely +//generic one shown here. + + +//If you don't care about the failure prop name you are welcome to pass in 'null', +template +bool areEqual( const TBaseObjType& lhs, const TBaseObjType& rhs, const char** outFailurePropName ); +//We don't have the ability right now to handle these types. +inline bool areEqual( const PxAggregate&, const PxAggregate& ) { return true; } +inline bool areEqual( const PxSimulationFilterShader&, const PxSimulationFilterShader& ) { return true; } +inline bool areEqual( const PxSimulationFilterCallback&, const PxSimulationFilterCallback& ) { return true; } +inline bool areEqual( const PxConvexMesh&, const PxConvexMesh& ) { return true; } +inline bool areEqual( const PxTriangleMesh&, const PxTriangleMesh& ) { return true; } +inline bool areEqual( const PxBVH33TriangleMesh&, const PxBVH33TriangleMesh& ) { return true; } +inline bool areEqual( const PxBVH34TriangleMesh&, const PxBVH34TriangleMesh& ) { return true; } +inline bool areEqual( const PxHeightField&, const PxHeightField& ) { return true; } +inline bool areEqual( const void* inLhs, const void* inRhs ) { return inLhs == inRhs; } +inline bool areEqual( void* inLhs, void* inRhs ) { return inLhs == inRhs; } + +//Operators are copied, so this object needs to point +//to the important data rather than reference or own it. +template +struct EqualityOp +{ + bool* mVal; + const TBaseObjType* mLhs; + const TBaseObjType* mRhs; + const char** mFailurePropName; + + EqualityOp( bool& inVal, const TBaseObjType& inLhs, const TBaseObjType& inRhs, const char*& inFailurePropName ) + : mVal( &inVal ) + , mLhs( &inLhs ) + , mRhs( &inRhs ) + , mFailurePropName( &inFailurePropName ) + { + } + + bool hasFailed() { return *mVal == false; } + //Ensure failure propagates the result a ways. + void update( bool inResult, const char* inName ) + { + *mVal = *mVal && inResult; + if ( hasFailed() ) + *mFailurePropName = inName; + } + + //ignore any properties pointering back to the scene. + template + void operator()( const PxReadOnlyPropertyInfo & inProp, PxU32 ) {} + + template + void operator()( const PxReadOnlyPropertyInfo & inProp, PxU32 ) {} + + //ignore any properties pointering back to the impl. + template + void operator()(const PxReadOnlyPropertyInfo & inProp, PxU32) {} + + template + void operator()(const PxReadOnlyPropertyInfo & inProp, PxU32) {} + + //ignore all of these properties because they just point back to the 'this' object and cause + //a stack overflow. + + //Children is unnecessary and articulation points back to the source. + void operator()( const PxReadOnlyCollectionPropertyInfo& inProp, PxU32 ) {} + void operator()( const PxReadOnlyCollectionPropertyInfo& inProp, PxU32 ){} + void operator()( const PxReadOnlyCollectionPropertyInfo& inProp, PxU32 ) {} + + template + void operator()( const PxBufferCollectionPropertyInfo & inProp, PxU32 ) + { + + } + + + template + void operator()( const PxReadOnlyPropertyInfo & inProp, PxU32 ) + { + if ( hasFailed() ) return; + TGetPropType lhs( inProp.get( mLhs ) ); + TGetPropType rhs( inProp.get( mRhs ) ); + update( areEqual( lhs, rhs, NULL ), inProp.mName ); + } + + template + void operator()( const PxRangePropertyInfo & inProp, PxU32 ) + { + if ( hasFailed() ) return; + TPropType lhsl, lhsr, rhsl, rhsr; + inProp.get( mLhs, lhsl, lhsr ); + inProp.get( mRhs, rhsl, rhsr ); + update( areEqual( lhsl, rhsl, NULL ), inProp.mName ); + update( areEqual( lhsr, rhsr, NULL ), inProp.mName ); + } + + //Indexed properties where we don't know the range of index types are ignored + template + void compareIndex( const PxIndexedPropertyInfo &, bool ) {} + + template + void compareIndex( const PxIndexedPropertyInfo &inProp, const PxU32ToName* inNames ) + { + for ( const PxU32ToName* theName = inNames; + theName->mName != NULL && !hasFailed(); + ++theName ) + { + TIndexType theIndex( static_cast( theName->mValue ) ); + update( areEqual( inProp.get( mLhs, theIndex ), inProp.get( mRhs, theIndex ), NULL ), inProp.mName ); + } + } + + template + void operator()( const PxIndexedPropertyInfo & inProp, PxU32 ) + { + if ( hasFailed() ) return; + compareIndex( inProp, PxEnumTraits().NameConversion ); + } + + template + void operator()( const PxReadOnlyCollectionPropertyInfo & inProp, PxU32 ) + { + if ( hasFailed() ) return; + physx::shdfnd::InlineArray lhsArray; + physx::shdfnd::InlineArray rhsArray; + PxU32 size = inProp.size( mLhs ); + if ( size != inProp.size( mRhs ) ) + update( false, inProp.mName ); + else + { + lhsArray.resize( size ); + rhsArray.resize( size ); + inProp.get( mLhs, lhsArray.begin(), size ); + inProp.get( mRhs, rhsArray.begin(), size ); + for ( PxU32 idx =0; idx < size && !hasFailed(); ++idx ) + update( areEqual( lhsArray[idx], rhsArray[idx], NULL ), inProp.mName ); + } + } + + //Filtered collections where we can't know the range of filter values are ignored. + template + void compare( const PxReadOnlyFilteredCollectionPropertyInfo< TKey, TObjType, TFilterType, TCollectionType >&, bool ) {} + + template + void compare( const PxReadOnlyFilteredCollectionPropertyInfo< TKey, TObjType, TFilterType, TCollectionType >& inProp, const PxU32ToName* inNames ) + { + //Exaustively compare all items. + physx::shdfnd::InlineArray lhsArray; + physx::shdfnd::InlineArray rhsArray; + for ( const PxU32ToName* theName = inNames; + theName->mName != NULL && !hasFailed(); + ++theName ) + { + TFilterType theFilter( static_cast( theName->mValue ) ); + PxU32 size = inProp.size( mLhs, theFilter ); + if ( size != inProp.size( mRhs, theFilter ) ) + update( false, inProp.mName ); + else + { + lhsArray.resize( size ); + rhsArray.resize( size ); + inProp.get( mLhs, theFilter, lhsArray.begin(), size ); + inProp.get( mRhs, theFilter, rhsArray.begin(), size ); + for ( PxU32 idx =0; idx < size && !hasFailed(); ++idx ) + update( areEqual( lhsArray[idx], rhsArray[idx], NULL ), inProp.mName ); + } + } + } + + template + void operator()( const PxReadOnlyFilteredCollectionPropertyInfo< TKey, TObjType, TFilterType, TCollectionType >& inProp, PxU32 ) + { + if ( hasFailed() ) return; + compare( inProp, PxEnumTraits().NameConversion ); + } + + template + void compareGeometry( const TPropertyType& inProp ) + { + TGeometryType lhs; + TGeometryType rhs; + bool lsuc = inProp.getGeometry( mLhs, lhs ); + bool rsuc = inProp.getGeometry( mRhs, rhs ); + if ( !( lsuc && rsuc ) ) + update( false, inProp.mName ); + else + update( areEqual( lhs, rhs, NULL ), inProp.mName ); + } + + void operator()( const PxShapeGeometryProperty& inProp, PxU32 ) + { + if ( hasFailed() ) return; + PxGeometryType::Enum lhsType( inProp.getGeometryType( mLhs ) ); + PxGeometryType::Enum rhsType( inProp.getGeometryType( mRhs ) ); + if ( lhsType != rhsType ) + update( false, inProp.mName ); + else + { + switch( lhsType ) + { + case PxGeometryType::eSPHERE: compareGeometry(inProp); break; + case PxGeometryType::ePLANE: compareGeometry(inProp); break; + case PxGeometryType::eCAPSULE: compareGeometry(inProp); break; + case PxGeometryType::eBOX: compareGeometry(inProp); break; + case PxGeometryType::eCONVEXMESH: compareGeometry(inProp); break; + case PxGeometryType::eTRIANGLEMESH: compareGeometry(inProp); break; + case PxGeometryType::eHEIGHTFIELD: compareGeometry(inProp); break; + default: PX_ASSERT( false ); break; + } + } + } +}; + +inline bool areEqual( const char* lhs, const char* rhs, const char**, const PxUnknownClassInfo& ) +{ + if ( lhs && rhs ) return strcmp( lhs, rhs ) == 0; + if ( lhs || rhs ) return false; + return true; +} + +inline bool areEqual( PxReal inLhs, PxReal inRhs ) +{ + return PxAbs( inLhs - inRhs ) < 1e-5f; +} + +inline bool areEqual( PxVec3& lhs, PxVec3& rhs ) +{ + return areEqual( lhs.x, rhs.x ) + && areEqual( lhs.y, rhs.y ) + && areEqual( lhs.z, rhs.z ); +} + +inline bool areEqual( const PxVec3& lhs, const PxVec3& rhs ) +{ + return areEqual( lhs.x, rhs.x ) + && areEqual( lhs.y, rhs.y ) + && areEqual( lhs.z, rhs.z ); +} + +inline bool areEqual( const PxVec4& lhs, const PxVec4& rhs ) +{ + return areEqual( lhs.x, rhs.x ) + && areEqual( lhs.y, rhs.y ) + && areEqual( lhs.z, rhs.z ) + && areEqual( lhs.w, rhs.w ); +} + +inline bool areEqual( const PxQuat& lhs, const PxQuat& rhs ) +{ + return areEqual( lhs.x, rhs.x ) + && areEqual( lhs.y, rhs.y ) + && areEqual( lhs.z, rhs.z ) + && areEqual( lhs.w, rhs.w ); +} + + +inline bool areEqual( const PxTransform& lhs, const PxTransform& rhs ) +{ + return areEqual(lhs.p, rhs.p) && areEqual(lhs.q, rhs.q); +} + + +inline bool areEqual( const PxBounds3& inLhs, const PxBounds3& inRhs ) +{ + return areEqual(inLhs.minimum,inRhs.minimum) + && areEqual(inLhs.maximum,inRhs.maximum); +} + +inline bool areEqual( const PxMetaDataPlane& lhs, const PxMetaDataPlane& rhs ) +{ + return areEqual( lhs.normal.x, rhs.normal.x ) + && areEqual( lhs.normal.y, rhs.normal.y ) + && areEqual( lhs.normal.z, rhs.normal.z ) + && areEqual( lhs.distance, rhs.distance ); +} + +template +inline bool areEqual( const TBaseObjType& lhs, const TBaseObjType& rhs ) +{ + return lhs == rhs; +} + +//If we don't know the class type, we must result in == operator +template +inline bool areEqual( const TBaseObjType& lhs, const TBaseObjType& rhs, const char**, const PxUnknownClassInfo& ) +{ + return areEqual( lhs, rhs ); +} + +//If we don't know the class type, we must result in == operator +template +inline bool areEqual( const TBaseObjType& lhs, const TBaseObjType& rhs, const char** outFailurePropName, const TTraitsType& ) +{ + const char* theFailureName = NULL; + bool result = true; + static int i = 0; + ++i; + visitAllProperties( EqualityOp( result, lhs, rhs, theFailureName ) ); + if ( outFailurePropName != NULL && theFailureName ) + *outFailurePropName = theFailureName; + return result; +} + + +template +inline bool areEqualPointerCheck( const TBaseObjType& lhs, const TBaseObjType& rhs, const char** outFailurePropName, int ) +{ + return areEqual( lhs, rhs, outFailurePropName, PxClassInfoTraits().Info ); +} + +inline bool areEqualPointerCheck( const void* lhs, const void* rhs, const char**, bool ) +{ + return lhs == rhs; +} + +inline bool areEqualPointerCheck( const char* lhs, const char* rhs, const char** outFailurePropName, bool ) +{ + bool bRet = true; + if ( lhs && rhs ) bRet = strcmp( lhs, rhs ) == 0; + else if ( lhs || rhs ) bRet = false; + + return bRet; +} + +inline bool areEqualPointerCheck( void* lhs, void* rhs, const char**, bool ) +{ + return lhs == rhs; +} + +template +inline bool areEqualPointerCheck( const TBaseObjType& lhs, const TBaseObjType& rhs, const char** outFailurePropName, bool ) +{ + if ( lhs && rhs ) + return areEqual( *lhs, *rhs, outFailurePropName ); + if ( lhs || rhs ) + return false; + return true; +} + +template < typename Tp > +struct is_pointer { static const int val = 0; }; + +template < typename Tp > +struct is_pointer { static const bool val = true; }; + + +template +inline bool areEqual( const TBaseObjType& lhs, const TBaseObjType& rhs, const char** outFailurePropName ) +{ + return areEqualPointerCheck( lhs, rhs, outFailurePropName, is_pointer::val ); +} + +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h new file mode 100644 index 000000000..c6190e35f --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h @@ -0,0 +1,37 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_META_DATA_CPP_PREFIX_H +#define PX_META_DATA_CPP_PREFIX_H + +//Header that is only included by the clang-generated cpp files. +//Used to change compilation settings where necessary for only those files. + + +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h new file mode 100644 index 000000000..b4cc65bdf --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h @@ -0,0 +1,662 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_METADATAOBJECTS_H +#define PX_METADATAOBJECTS_H +#include "foundation/PxMemory.h" + +// include the base headers instead of the PxPhysicsAPI.h +//Geometry Library +#include "geometry/PxBoxGeometry.h" +#include "geometry/PxCapsuleGeometry.h" +#include "geometry/PxConvexMesh.h" +#include "geometry/PxConvexMeshGeometry.h" +#include "geometry/PxGeometry.h" +#include "geometry/PxGeometryHelpers.h" +#include "geometry/PxGeometryQuery.h" +#include "geometry/PxHeightField.h" +#include "geometry/PxHeightFieldDesc.h" +#include "geometry/PxHeightFieldFlag.h" +#include "geometry/PxHeightFieldGeometry.h" +#include "geometry/PxHeightFieldSample.h" +#include "geometry/PxMeshQuery.h" +#include "geometry/PxMeshScale.h" +#include "geometry/PxPlaneGeometry.h" +#include "geometry/PxSimpleTriangleMesh.h" +#include "geometry/PxSphereGeometry.h" +#include "geometry/PxTriangle.h" +#include "geometry/PxTriangleMesh.h" +#include "geometry/PxTriangleMeshGeometry.h" + +// PhysX Core SDK +#include "PxActor.h" +#include "PxAggregate.h" +#include "PxArticulation.h" +#include "PxArticulationJoint.h" +#include "PxArticulationReducedCoordinate.h" +#include "PxArticulationJointReducedCoordinate.h" +#include "PxArticulationLink.h" +#include "PxBatchQuery.h" +#include "PxBatchQueryDesc.h" +#include "PxClient.h" +#include "PxConstraint.h" +#include "PxConstraintDesc.h" +#include "PxContact.h" +#include "PxContactModifyCallback.h" +#include "PxDeletionListener.h" +#include "PxFiltering.h" +#include "PxForceMode.h" +#include "PxLockedData.h" +#include "PxMaterial.h" +#include "PxPhysics.h" +#include "PxPhysicsVersion.h" +#include "PxPhysXConfig.h" +#include "PxQueryFiltering.h" +#include "PxQueryReport.h" +#include "PxRigidActor.h" +#include "PxRigidBody.h" +#include "PxRigidDynamic.h" +#include "PxRigidStatic.h" +#include "PxScene.h" +#include "PxSceneDesc.h" +#include "PxSceneLock.h" +#include "PxShape.h" +#include "PxSimulationEventCallback.h" +#include "PxSimulationStatistics.h" +#include "PxVisualizationParameter.h" +#include "PxPruningStructure.h" + + +/** \addtogroup physics +@{ +*/ + +namespace physx +{ + +class PxArticulationLink; +class PxArticulationJoint; +class PxArticulationJointReducedCoordinate; +class PxArticulationJointBase; + +struct PxPropertyInfoName +{ + enum Enum + { + Unnamed = 0, +#include "PxAutoGeneratedMetaDataObjectNames.h" + LastPxPropertyInfoName + }; +}; + +struct PxU32ToName +{ + const char* mName; + PxU32 mValue; +}; + +struct PxPropertyInfoBase +{ + const char* mName; + PxU32 mKey; + PxPropertyInfoBase( const char* n, PxU32 inKey ) + : mName( n ) + , mKey( inKey ) + { + } +}; + +template +struct PxPropertyInfoParameterizedBase : public PxPropertyInfoBase +{ + PxPropertyInfoParameterizedBase( const char* inName ) + : PxPropertyInfoBase( inName, TKey ) {} +}; + +template +struct PxReadOnlyPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef TPropertyType (*TGetterType)( const TObjType* ); + TGetterType mGetter; + PxReadOnlyPropertyInfo( const char* inName, TGetterType inGetter ) + : PxPropertyInfoParameterizedBase( inName ) + , mGetter( inGetter ) {} + TPropertyType get( const TObjType* inObj ) const { return mGetter( inObj ); } +}; + +template +struct PxWriteOnlyPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef void(*TSetterType)( TObjType*, TPropertyType inArg ); + TSetterType mSetter; + PxWriteOnlyPropertyInfo( const char* inName, TSetterType inSetter ) + : PxPropertyInfoParameterizedBase( inName ) + , mSetter( inSetter ) {} + void set( TObjType* inObj, TPropertyType inArg ) const { mSetter( inObj, inArg ); } +}; + + +//Define the property types on the auto-generated objects. +template +struct PxPropertyInfo : public PxReadOnlyPropertyInfo +{ + typedef typename PxReadOnlyPropertyInfo::TGetterType TGetterType; + typedef void(*TSetterType)( TObjType*, TSetPropType inArg ); + TSetterType mSetter; + + PxPropertyInfo( const char* inName, TSetterType inSetter, TGetterType inGetter ) + : PxReadOnlyPropertyInfo( inName, inGetter ) + , mSetter( inSetter ) {} + void set( TObjType* inObj, TSetPropType inArg ) const { mSetter( inObj, inArg ); } +}; + +template +struct PxRangePropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef void (*TSetterType)( TObjType*,TPropertyType,TPropertyType); + typedef void (*TGetterType)( const TObjType*,TPropertyType&,TPropertyType&); + + const char* mArg0Name; + const char* mArg1Name; + + TSetterType mSetter; + TGetterType mGetter; + + PxRangePropertyInfo( const char* name, const char* arg0Name, const char* arg1Name + , TSetterType setter, TGetterType getter ) + : PxPropertyInfoParameterizedBase( name ) + , mArg0Name( arg0Name ) + , mArg1Name( arg1Name ) + , mSetter( setter ) + , mGetter( getter ) + { + } + void set( TObjType* inObj, TPropertyType arg0, TPropertyType arg1 ) const { mSetter( inObj, arg0, arg1 ); } + void get( const TObjType* inObj, TPropertyType& arg0, TPropertyType& arg1 ) const { mGetter( inObj, arg0, arg1 ); } +}; + +template +struct PxIndexedPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef void (*TSetterType)( TObjType*, TIndexType, TPropertyType ); + typedef TPropertyType (*TGetterType)( const TObjType* inObj, TIndexType ); + + TSetterType mSetter; + TGetterType mGetter; + + PxIndexedPropertyInfo( const char* name, TSetterType setter, TGetterType getter ) + : PxPropertyInfoParameterizedBase( name ) + , mSetter( setter ) + , mGetter( getter ) + { + } + void set( TObjType* inObj, TIndexType inIndex, TPropertyType arg ) const { mSetter( inObj, inIndex, arg ); } + TPropertyType get( const TObjType* inObj, TIndexType inIndex ) const { return mGetter( inObj, inIndex ); } +}; + +template +struct PxExtendedIndexedPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef PxU32 (*TNbObjectsMember)( const TObjType* ); + typedef void (*TSetterType)( TObjType*, TIndexType, TPropertyType ); + typedef TPropertyType (*TGetterType)( const TObjType* inObj, TIndexType ); + + TSetterType mSetter; + TGetterType mGetter; + PxU32 mCount; + TNbObjectsMember mNbObjectsMember; + + PxExtendedIndexedPropertyInfo( const char* name, TGetterType getter, TNbObjectsMember inNb, TSetterType setter) + : PxPropertyInfoParameterizedBase( name ) + , mSetter( setter ) + , mGetter( getter ) + , mNbObjectsMember( inNb ) + { + } + + PxU32 size( const TObjType* inObj ) const { return mNbObjectsMember( inObj ); } + void set( TObjType* inObj, TIndexType inIndex, TPropertyType arg ) const { mSetter( inObj, inIndex, arg ); } + TPropertyType get( const TObjType* inObj, TIndexType inIndex ) const { return mGetter( inObj, inIndex ); } +}; + +template +struct PxDualIndexedPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef void (*TSetterType)( TObjType*, TIndex1Type, TIndex2Type, TPropertyType ); + typedef TPropertyType (*TGetterType)( const TObjType* inObj, TIndex1Type, TIndex2Type ); + + TSetterType mSetter; + TGetterType mGetter; + + PxDualIndexedPropertyInfo( const char* name, TSetterType setter, TGetterType getter ) + : PxPropertyInfoParameterizedBase( name ) + , mSetter( setter ) + , mGetter( getter ) + { + } + void set( TObjType* inObj, TIndex1Type inIdx1, TIndex2Type inIdx2, TPropertyType arg ) const { mSetter( inObj, inIdx1, inIdx2, arg ); } + TPropertyType get( const TObjType* inObj, TIndex1Type inIdx1, TIndex2Type inIdx2 ) const { return mGetter( inObj, inIdx1, inIdx2 ); } +}; + +template +struct PxExtendedDualIndexedPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef void (*TSetterType)( TObjType*, TIndex1Type, TIndex2Type, TPropertyType ); + typedef TPropertyType (*TGetterType)( const TObjType* inObj, TIndex1Type, TIndex2Type ); + + TSetterType mSetter; + TGetterType mGetter; + PxU32 mId0Count; + PxU32 mId1Count; + + PxExtendedDualIndexedPropertyInfo( const char* name, TSetterType setter, TGetterType getter, PxU32 id0Count, PxU32 id1Count ) + : PxPropertyInfoParameterizedBase( name ) + , mSetter( setter ) + , mGetter( getter ) + , mId0Count( id0Count ) + , mId1Count( id1Count ) + { + } + + void set( TObjType* inObj, TIndex1Type inIdx1, TIndex2Type inIdx2, TPropertyType arg ) const { mSetter( inObj, inIdx1, inIdx2, arg ); } + TPropertyType get( const TObjType* inObj, TIndex1Type inIdx1, TIndex2Type inIdx2 ) const { return mGetter( inObj, inIdx1, inIdx2 ); } +}; + +template +struct PxBufferCollectionPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef PxU32 (*TNbObjectsMember)( const TObjType* ); + typedef PxU32 (*TGetObjectsMember)( const TObjType*, TCollectionType*, PxU32 ); + typedef void (*TSetObjectsMember)( TObjType*, TCollectionType*, PxU32 ); + + TGetObjectsMember mGetObjectsMember; + TNbObjectsMember mNbObjectsMember; + TSetObjectsMember mSetObjectsMember; + + PxBufferCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb, TSetObjectsMember inSet ) + : PxPropertyInfoParameterizedBase( inName ) + , mGetObjectsMember( inGetter ) + , mNbObjectsMember( inNb ) + , mSetObjectsMember( inSet ) + { + } + PxU32 size( const TObjType* inObj ) const { return mNbObjectsMember( inObj ); } + PxU32 get( const TObjType* inObj, TCollectionType* inBuffer, PxU32 inNumItems ) const { return mGetObjectsMember( inObj, inBuffer, inNumItems ); } + void set( TObjType* inObj, TCollectionType* inBuffer, PxU32 inNumItems ) const { mSetObjectsMember( inObj, inBuffer, inNumItems); } +}; + +template +struct PxFixedSizeLookupTablePropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef PxU32 (*TNbObjectsMember)( const TObjType* ); + typedef PxReal (*TGetXMember)( const TObjType*, PxU32 ); + typedef PxReal (*TGetYMember)( const TObjType*, PxU32 ); + typedef void (*TAddPairMember)( TObjType*, PxReal, PxReal ); + typedef void (*TClearMember)( TObjType* ); + + TGetXMember mGetXMember; + TGetYMember mGetYMember; + TNbObjectsMember mNbObjectsMember; + TAddPairMember mAddPairMember; + TClearMember mClearMember; + + PxFixedSizeLookupTablePropertyInfo( const char* inName, TGetXMember inGetterX, TGetYMember inGetterY, TNbObjectsMember inNb, TAddPairMember inAddPair, TClearMember inClear ) + : PxPropertyInfoParameterizedBase( inName ) + , mGetXMember( inGetterX ) + , mGetYMember( inGetterY ) + , mNbObjectsMember( inNb ) + , mAddPairMember( inAddPair ) + , mClearMember( inClear ) + + { + } + PxU32 size( const TObjType* inObj ) const { return mNbObjectsMember( inObj ); } + PxReal getX( const TObjType* inObj, const PxU32 index ) const { return mGetXMember( inObj, index ); } + PxReal getY( const TObjType* inObj, const PxU32 index ) const { return mGetYMember( inObj, index ); } + void addPair( TObjType* inObj, const PxReal x, const PxReal y ) { mAddPairMember( inObj, x, y ); } + void clear( TObjType* inObj ) { mClearMember( inObj ); } +}; + +template +struct PxReadOnlyCollectionPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef PxU32 (*TNbObjectsMember)( const TObjType* ); + typedef PxU32 (*TGetObjectsMember)( const TObjType*, TCollectionType*, PxU32 ); + + TGetObjectsMember mGetObjectsMember; + TNbObjectsMember mNbObjectsMember; + + PxReadOnlyCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb ) + : PxPropertyInfoParameterizedBase( inName ) + , mGetObjectsMember( inGetter ) + , mNbObjectsMember( inNb ) + { + } + PxU32 size( const TObjType* inObj ) const { return mNbObjectsMember( inObj ); } + PxU32 get( const TObjType* inObj, TCollectionType* inBuffer, PxU32 inBufSize ) const { return mGetObjectsMember( inObj, inBuffer, inBufSize); } +}; + + +template +struct PxReadOnlyFilteredCollectionPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef PxU32 (*TNbObjectsMember)( const TObjType*, TFilterType ); + typedef PxU32 (*TGetObjectsMember)( const TObjType*, TFilterType, TCollectionType*, PxU32 ); + + TGetObjectsMember mGetObjectsMember; + TNbObjectsMember mNbObjectsMember; + + PxReadOnlyFilteredCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb ) + : PxPropertyInfoParameterizedBase( inName ) + , mGetObjectsMember( inGetter ) + , mNbObjectsMember( inNb ) + { + } + + PxU32 size( const TObjType* inObj, TFilterType inFilter ) const { return mNbObjectsMember( inObj, inFilter ); } + PxU32 get( const TObjType* inObj, TFilterType inFilter, TCollectionType* inBuffer, PxU32 inBufSize ) const { return mGetObjectsMember( inObj, inFilter, inBuffer, inBufSize); } +}; + +template +struct PxFactoryCollectionPropertyInfo : public PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType > +{ + typedef typename PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >::TGetObjectsMember TGetObjectsMember; + typedef typename PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >::TNbObjectsMember TNbObjectsMember; + typedef TCollectionType (*TCreateMember)( TObjType*, TCreateArg ); + + TCreateMember mCreateMember; + PxFactoryCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb, TCreateMember inMember ) + : PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >( inName, inGetter, inNb ) + , mCreateMember( inMember ) + { + } + TCollectionType create( TObjType* inObj, TCreateArg inArg ) const { return mCreateMember( inObj, inArg ); } +}; + + +template +struct PxCollectionPropertyInfo : public PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType > +{ + typedef typename PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >::TGetObjectsMember TGetObjectsMember; + typedef typename PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >::TNbObjectsMember TNbObjectsMember; + typedef void (*TAddMember)(TObjType*, TCollectionType&); + typedef void (*TRemoveMember)(TObjType*, TCollectionType&); + + TAddMember mAddMember; + TRemoveMember mRemoveMember; + + PxCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb, TAddMember inMember, TRemoveMember inRemoveMember ) + : PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >( inName, inGetter, inNb ) + , mAddMember( inMember ) + , mRemoveMember( inRemoveMember ) + { + } + void add( TObjType* inObj, TCollectionType& inArg ) const { mAddMember(inObj, inArg ); } + void remove( TObjType* inObj, TCollectionType& inArg ) const { mRemoveMember( inObj, inArg ); } +}; + +template +struct PxFilteredCollectionPropertyInfo : public PxReadOnlyFilteredCollectionPropertyInfo +{ + typedef typename PxReadOnlyFilteredCollectionPropertyInfo< TKey, TObjType, TCollectionType, TFilterType >::TGetObjectsMember TGetObjectsMember; + typedef typename PxReadOnlyFilteredCollectionPropertyInfo< TKey, TObjType, TCollectionType, TFilterType >::TNbObjectsMember TNbObjectsMember; + typedef void (*TAddMember)(TObjType*, TCollectionType&); + typedef void (*TRemoveMember)(TObjType*, TCollectionType&); + + TAddMember mAddMember; + TRemoveMember mRemoveMember; + + PxFilteredCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb, TAddMember inMember, TRemoveMember inRemoveMember ) + : PxReadOnlyFilteredCollectionPropertyInfo( inName, inGetter, inNb ) + , mAddMember( inMember ) + , mRemoveMember( inRemoveMember ) + { + } + void add( TObjType* inObj, TCollectionType& inArg ) const { mAddMember(inObj, inArg ); } + void remove( TObjType* inObj, TCollectionType& inArg ) const { mRemoveMember( inObj, inArg ); } +}; + +//create a default info class for when we can't match +//the type correctly. +struct PxUnknownClassInfo +{ + static const char* getClassName() { return "__unknown_class"; } + template + TReturnType visitType( TOperator ) + { + return TReturnType(); + } + template + void visitBases( TOperator ) + { + } + template + PxU32 visitBaseProperties( TOperator, PxU32 inStartIndex = 0 ) const + { + return inStartIndex; + } + template + PxU32 visitInstanceProperties( TOperator, PxU32 inStartIndex = 0 ) const + { + return inStartIndex; + } +}; + +template +struct PxClassInfoTraits +{ + PxUnknownClassInfo Info; + static bool getInfo() { return false;} +}; + +//move the bool typedef to the global namespace. +typedef bool _Bool; + + +template +struct PxPropertyToValueStructMemberMap +{ + bool Offset; +}; + + +#define DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( type, prop, valueStruct ) \ + template<> struct PxPropertyToValueStructMemberMap< PxPropertyInfoName::type##_##prop > \ + { \ + PxU32 Offset; \ + PxPropertyToValueStructMemberMap< PxPropertyInfoName::type##_##prop >() : Offset( PX_OFFSET_OF_RT( valueStruct, prop ) ) {} \ + template void visitProp( TOperator inOperator, valueStruct& inStruct ) { inOperator( inStruct.prop ); } \ + }; + + + +struct PxShapeGeometryPropertyHelper +{ + PX_PHYSX_CORE_API PxGeometryType::Enum getGeometryType(const PxShape* inShape) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxBoxGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxSphereGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxCapsuleGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxPlaneGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxConvexMeshGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxTriangleMeshGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxHeightFieldGeometry& geometry) const; +}; + + +struct PxShapeGeometryProperty : public PxWriteOnlyPropertyInfo< PxPropertyInfoName::PxShape_Geometry, PxShape, const PxGeometry & > + , public PxShapeGeometryPropertyHelper +{ + typedef PxWriteOnlyPropertyInfo< PxPropertyInfoName::PxShape_Geometry, PxShape, const PxGeometry & >::TSetterType TSetterType; + typedef PxGeometryHolder (*TGetterType)( const PxShape* inObj ); + PxShapeGeometryProperty( const char* inName, TSetterType inSetter, TGetterType ) + : PxWriteOnlyPropertyInfo< PxPropertyInfoName::PxShape_Geometry, PxShape, const PxGeometry & >( inName, inSetter ) + { + } +}; + +struct PxShapeMaterialsPropertyHelper +{ + PX_PHYSX_CORE_API void setMaterials(PxShape* inShape, PxMaterial*const* materials, PxU16 materialCount) const; +}; + +struct PxShapeMaterialsProperty : public PxReadOnlyCollectionPropertyInfo + , public PxShapeMaterialsPropertyHelper +{ + typedef PxReadOnlyCollectionPropertyInfo< PxPropertyInfoName::PxShape_Materials, PxShape, PxMaterial* >::TGetObjectsMember TGetObjectsMember; + typedef PxReadOnlyCollectionPropertyInfo< PxPropertyInfoName::PxShape_Materials, PxShape, PxMaterial* >::TNbObjectsMember TNbObjectsMember; + PxShapeMaterialsProperty( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb ) + : PxReadOnlyCollectionPropertyInfo( inName, inGetter, inNb ) + { + } +}; + +struct PxRigidActorShapeCollectionHelper +{ + PX_PHYSX_CORE_API PxShape* createShape(PxRigidActor* inActor, const PxGeometry& geometry, PxMaterial& material, PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) const; + PX_PHYSX_CORE_API PxShape* createShape(PxRigidActor* inActor, const PxGeometry& geometry, PxMaterial *const* materials, PxU16 materialCount, PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) const; +}; + +struct PxRigidActorShapeCollection : public PxReadOnlyCollectionPropertyInfo + , public PxRigidActorShapeCollectionHelper +{ + typedef PxReadOnlyCollectionPropertyInfo< PxPropertyInfoName::PxRigidActor_Shapes, PxRigidActor, PxShape* >::TGetObjectsMember TGetObjectsMember; + typedef PxReadOnlyCollectionPropertyInfo< PxPropertyInfoName::PxRigidActor_Shapes, PxRigidActor, PxShape* >::TNbObjectsMember TNbObjectsMember; + PxRigidActorShapeCollection( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb ) + : PxReadOnlyCollectionPropertyInfo( inName, inGetter, inNb ) + { + } +}; + +struct PxArticulationLinkCollectionPropHelper +{ + PX_PHYSX_CORE_API PxArticulationLink* createLink(PxArticulation* inArticulation, PxArticulationLink* parent, const PxTransform& pose) const; +}; + +struct PxArticulationLinkCollectionProp : public PxReadOnlyCollectionPropertyInfo + , public PxArticulationLinkCollectionPropHelper +{ + PxArticulationLinkCollectionProp( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb ) + : PxReadOnlyCollectionPropertyInfo( inName, inGetter, inNb ) + { + } +}; + +template +struct PxEnumTraits { PxEnumTraits() : NameConversion( false ) {} bool NameConversion; }; + +struct NbShapesProperty : public PxIndexedPropertyInfo +{ + PX_PHYSX_CORE_API NbShapesProperty(); +}; + + +struct NbDiscreteContactPairsProperty : public PxDualIndexedPropertyInfo +{ + PX_PHYSX_CORE_API NbDiscreteContactPairsProperty(); +}; +struct NbModifiedContactPairsProperty : public PxDualIndexedPropertyInfo +{ + PX_PHYSX_CORE_API NbModifiedContactPairsProperty(); +}; + +struct NbCCDPairsProperty : public PxDualIndexedPropertyInfo +{ + PX_PHYSX_CORE_API NbCCDPairsProperty(); +}; + +struct NbTriggerPairsProperty : public PxDualIndexedPropertyInfo +{ + PX_PHYSX_CORE_API NbTriggerPairsProperty(); +}; + + +struct SimulationStatisticsProperty : public PxReadOnlyPropertyInfo +{ + PX_PHYSX_CORE_API SimulationStatisticsProperty(); +}; + +struct PxMetaDataPlane +{ + PxVec3 normal; + PxReal distance; + PxMetaDataPlane( PxVec3 n = PxVec3( 0, 0, 0 ), PxReal d = 0 ) + : normal( n ) + , distance( d ) + { + } +}; + +#include "PxAutoGeneratedMetaDataObjects.h" + +#undef DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP + +static PxU32ToName g_physx__PxQueryFlag__EnumConversion[] = { + { "eSTATIC", static_cast( PxQueryFlag::eSTATIC ) }, + { "eDYNAMIC", static_cast( PxQueryFlag::eDYNAMIC ) }, + { "ePREFILTER", static_cast( PxQueryFlag::ePREFILTER ) }, + { "ePOSTFILTER", static_cast( PxQueryFlag::ePOSTFILTER ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits { PxEnumTraits() : NameConversion( g_physx__PxQueryFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + + +template +inline PxU32 visitAllProperties( TOperator inOperator ) +{ + PxU32 thePropCount = PxClassInfoTraits().Info.visitBaseProperties( inOperator ); + return PxClassInfoTraits().Info.visitInstanceProperties( inOperator, thePropCount ); +} + +template +inline void visitInstanceProperties( TOperator inOperator ) +{ + PxClassInfoTraits().Info.visitInstanceProperties( inOperator, 0 ); +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h b/src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h new file mode 100644 index 000000000..062300b3b --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h @@ -0,0 +1,213 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_REPX_META_DATA_PROPERTY_VISITOR_H +#define PX_REPX_META_DATA_PROPERTY_VISITOR_H +#include "PvdMetaDataPropertyVisitor.h" + +namespace physx { + + template + struct PxRepXPropertyAccessor : public Vd::ValueStructOffsetRecord + { + typedef PxPropertyInfo TPropertyInfoType; + typedef TPropertyType prop_type; + + const TPropertyInfoType mProperty; + PxRepXPropertyAccessor( const TPropertyInfoType& inProp ) + : mProperty( inProp ) + { + } + prop_type get( const TObjectType* inObj ) const { return mProperty.get( inObj ); } + void set( TObjectType* inObj, prop_type val ) const { return mProperty.set( inObj, val ); } + + private: + PxRepXPropertyAccessor& operator=(const PxRepXPropertyAccessor&); + }; + + template + struct PxRepXPropertyAccessor : public Vd::ValueStructOffsetRecord + { + typedef PxPropertyInfo TPropertyInfoType; + typedef TPropertyType prop_type; + + const TPropertyInfoType mProperty; + PxRepXPropertyAccessor( const TPropertyInfoType& inProp ) + : mProperty( inProp ) + { + } + prop_type get( const PxRigidDynamic* inObj ) const { return mProperty.get( inObj ); } + void set( PxRigidDynamic* inObj, prop_type val ) const + { + PX_UNUSED(val); + PxRigidBodyFlags flags = inObj->getRigidBodyFlags(); + if( !(flags & PxRigidBodyFlag::eKINEMATIC) ) + return mProperty.set( inObj, val ); + } + private: + PxRepXPropertyAccessor& operator=(const PxRepXPropertyAccessor&); + }; + + typedef PxReadOnlyPropertyInfo TIncomingJointPropType; + + + //RepX cares about fewer property types than PVD does, + //but I want to reuse the accessor architecture as it + //greatly simplifies clients dealing with complex datatypes + template + struct RepXPropertyFilter + { + RepXPropertyFilter &operator=(const RepXPropertyFilter &); + + Vd::PvdPropertyFilter mFilter; + RepXPropertyFilter( TOperatorType op ) : mFilter( op ) {} + RepXPropertyFilter( const RepXPropertyFilter& other ) : mFilter( other.mFilter ) {} + + template + void operator()( const PxReadOnlyPropertyInfo&, PxU32 ) {} //repx ignores read only and write only properties + template + void operator()( const PxWriteOnlyPropertyInfo&, PxU32 ) {} + template + void operator()( const PxReadOnlyCollectionPropertyInfo&, PxU32 ) {} + + template + void operator()( const PxReadOnlyFilteredCollectionPropertyInfo&, PxU32 ) {} + //forward these properties verbatim. + template + void operator()( const PxIndexedPropertyInfo& inProp, PxU32 idx ) + { + mFilter( inProp, idx ); + } + + template + void operator()( const PxFixedSizeLookupTablePropertyInfo& inProp, PxU32 idx ) + { + mFilter( inProp, idx ); + } + + template + void operator()( const PxExtendedIndexedPropertyInfo& inProp, PxU32 idx) + { + mFilter( inProp, idx); + } + + template + void operator()( const PxDualIndexedPropertyInfo& inProp, PxU32 idx ) + { + mFilter( inProp, idx ); + } + + template + void operator()( const PxExtendedDualIndexedPropertyInfo& inProp, PxU32 idx ) + { + mFilter( inProp, idx ); + } + + template + void operator()( const PxRangePropertyInfo& inProp, PxU32 idx ) + { + mFilter( inProp, idx ); + } + + template + void operator()( const PxBufferCollectionPropertyInfo& inProp, PxU32 count ) + { + mFilter( inProp, count ); + } + + template + void operator()( const PxPropertyInfo& prop, PxU32 ) + { + PxRepXPropertyAccessor< TKey, TObjType, TSetPropType, TPropertyType > theAccessor( prop ); + mFilter.mOperator.pushName( prop.mName ); + mFilter.template handleAccessor( theAccessor ); + mFilter.mOperator.popName(); + } + + void operator()( const PxRigidActorShapeCollection& inProp, PxU32 ) + { + mFilter.mOperator.pushName( "Shapes" ); + mFilter.mOperator.handleShapes( inProp ); + mFilter.mOperator.popName(); + } + + void operator()( const PxArticulationLinkCollectionProp& inProp, PxU32 ) + { + mFilter.mOperator.pushName( "Links" ); + mFilter.mOperator.handleArticulationLinks( inProp ); + mFilter.mOperator.popName(); + } + + void operator()( const PxShapeMaterialsProperty& inProp, PxU32 ) + { + mFilter.mOperator.pushName( "Materials" ); + mFilter.mOperator.handleShapeMaterials( inProp ); + mFilter.mOperator.popName(); + } + + void operator()( const TIncomingJointPropType& inProp, PxU32 ) + { + mFilter.mOperator.handleIncomingJoint( inProp ); + } + + void operator()( const PxShapeGeometryProperty& inProp, PxU32 ) + { + mFilter.mOperator.handleGeometryProperty( inProp ); + } + +#define DEFINE_REPX_PROPERTY_NOP(datatype) \ + template \ + void operator()( const PxPropertyInfo&, PxU32 ){} + + DEFINE_REPX_PROPERTY_NOP( const void* ) + DEFINE_REPX_PROPERTY_NOP( void* ) + DEFINE_REPX_PROPERTY_NOP( PxSimulationFilterCallback * ) + DEFINE_REPX_PROPERTY_NOP( physx::PxTaskManager * ) + DEFINE_REPX_PROPERTY_NOP( PxSimulationFilterShader * ) + DEFINE_REPX_PROPERTY_NOP( PxSimulationFilterShader) + DEFINE_REPX_PROPERTY_NOP( PxContactModifyCallback * ) + DEFINE_REPX_PROPERTY_NOP( PxCCDContactModifyCallback * ) + DEFINE_REPX_PROPERTY_NOP( PxSimulationEventCallback * ) + DEFINE_REPX_PROPERTY_NOP( physx::PxGpuDispatcher* ) + DEFINE_REPX_PROPERTY_NOP( physx::PxCpuDispatcher * ) + DEFINE_REPX_PROPERTY_NOP( PxRigidActor ) + DEFINE_REPX_PROPERTY_NOP( const PxRigidActor ) + DEFINE_REPX_PROPERTY_NOP( PxRigidActor& ) + DEFINE_REPX_PROPERTY_NOP( const PxRigidActor& ) + DEFINE_REPX_PROPERTY_NOP( PxScene* ) + DEFINE_REPX_PROPERTY_NOP( PxAggregate * ) + DEFINE_REPX_PROPERTY_NOP( PxArticulation& ) + DEFINE_REPX_PROPERTY_NOP( const PxArticulationLink * ) + DEFINE_REPX_PROPERTY_NOP( const PxRigidDynamic * ) + DEFINE_REPX_PROPERTY_NOP( const PxRigidStatic * ) + DEFINE_REPX_PROPERTY_NOP( PxStridedData ) //These are handled in a custom fasion. + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp b/src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp new file mode 100644 index 000000000..b23f1cf6f --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp @@ -0,0 +1,1236 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#include "PxMetaDataObjects.h" +#include "PxMetaDataCppPrefix.h" +using namespace physx; +const PxTolerancesScale getPxPhysics_TolerancesScale( const PxPhysics* inObj ) { return inObj->getTolerancesScale(); } +PxU32 getPxPhysics_TriangleMeshes( const PxPhysics* inObj, PxTriangleMesh ** outBuffer, PxU32 inBufSize ) { return inObj->getTriangleMeshes( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_TriangleMeshes( const PxPhysics* inObj ) { return inObj->getNbTriangleMeshes( ); } +PxTriangleMesh * createPxPhysics_TriangleMeshes( PxPhysics* inObj, PxInputStream & inCreateParam ){ return inObj->createTriangleMesh( inCreateParam ); } +PxU32 getPxPhysics_HeightFields( const PxPhysics* inObj, PxHeightField ** outBuffer, PxU32 inBufSize ) { return inObj->getHeightFields( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_HeightFields( const PxPhysics* inObj ) { return inObj->getNbHeightFields( ); } +PxHeightField * createPxPhysics_HeightFields( PxPhysics* inObj, PxInputStream & inCreateParam ){ return inObj->createHeightField( inCreateParam ); } +PxU32 getPxPhysics_ConvexMeshes( const PxPhysics* inObj, PxConvexMesh ** outBuffer, PxU32 inBufSize ) { return inObj->getConvexMeshes( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_ConvexMeshes( const PxPhysics* inObj ) { return inObj->getNbConvexMeshes( ); } +PxConvexMesh * createPxPhysics_ConvexMeshes( PxPhysics* inObj, PxInputStream & inCreateParam ){ return inObj->createConvexMesh( inCreateParam ); } +PxU32 getPxPhysics_BVHStructures( const PxPhysics* inObj, PxBVHStructure ** outBuffer, PxU32 inBufSize ) { return inObj->getBVHStructures( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_BVHStructures( const PxPhysics* inObj ) { return inObj->getNbBVHStructures( ); } +PxBVHStructure * createPxPhysics_BVHStructures( PxPhysics* inObj, PxInputStream & inCreateParam ){ return inObj->createBVHStructure( inCreateParam ); } +PxU32 getPxPhysics_Scenes( const PxPhysics* inObj, PxScene ** outBuffer, PxU32 inBufSize ) { return inObj->getScenes( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_Scenes( const PxPhysics* inObj ) { return inObj->getNbScenes( ); } +PxScene * createPxPhysics_Scenes( PxPhysics* inObj, const PxSceneDesc & inCreateParam ){ return inObj->createScene( inCreateParam ); } +PxU32 getPxPhysics_Shapes( const PxPhysics* inObj, PxShape ** outBuffer, PxU32 inBufSize ) { return inObj->getShapes( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_Shapes( const PxPhysics* inObj ) { return inObj->getNbShapes( ); } +PxU32 getPxPhysics_Materials( const PxPhysics* inObj, PxMaterial ** outBuffer, PxU32 inBufSize ) { return inObj->getMaterials( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_Materials( const PxPhysics* inObj ) { return inObj->getNbMaterials( ); } +PX_PHYSX_CORE_API PxPhysicsGeneratedInfo::PxPhysicsGeneratedInfo() + : TolerancesScale( "TolerancesScale", getPxPhysics_TolerancesScale) + , TriangleMeshes( "TriangleMeshes", getPxPhysics_TriangleMeshes, getNbPxPhysics_TriangleMeshes, createPxPhysics_TriangleMeshes ) + , HeightFields( "HeightFields", getPxPhysics_HeightFields, getNbPxPhysics_HeightFields, createPxPhysics_HeightFields ) + , ConvexMeshes( "ConvexMeshes", getPxPhysics_ConvexMeshes, getNbPxPhysics_ConvexMeshes, createPxPhysics_ConvexMeshes ) + , BVHStructures( "BVHStructures", getPxPhysics_BVHStructures, getNbPxPhysics_BVHStructures, createPxPhysics_BVHStructures ) + , Scenes( "Scenes", getPxPhysics_Scenes, getNbPxPhysics_Scenes, createPxPhysics_Scenes ) + , Shapes( "Shapes", getPxPhysics_Shapes, getNbPxPhysics_Shapes ) + , Materials( "Materials", getPxPhysics_Materials, getNbPxPhysics_Materials ) +{} +PX_PHYSX_CORE_API PxPhysicsGeneratedValues::PxPhysicsGeneratedValues( const PxPhysics* inSource ) + :TolerancesScale( getPxPhysics_TolerancesScale( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxU32 getPxMaterial_ReferenceCount( const PxMaterial* inObj ) { return inObj->getReferenceCount(); } +void setPxMaterial_DynamicFriction( PxMaterial* inObj, PxReal inArg){ inObj->setDynamicFriction( inArg ); } +PxReal getPxMaterial_DynamicFriction( const PxMaterial* inObj ) { return inObj->getDynamicFriction(); } +void setPxMaterial_StaticFriction( PxMaterial* inObj, PxReal inArg){ inObj->setStaticFriction( inArg ); } +PxReal getPxMaterial_StaticFriction( const PxMaterial* inObj ) { return inObj->getStaticFriction(); } +void setPxMaterial_Restitution( PxMaterial* inObj, PxReal inArg){ inObj->setRestitution( inArg ); } +PxReal getPxMaterial_Restitution( const PxMaterial* inObj ) { return inObj->getRestitution(); } +void setPxMaterial_Flags( PxMaterial* inObj, PxMaterialFlags inArg){ inObj->setFlags( inArg ); } +PxMaterialFlags getPxMaterial_Flags( const PxMaterial* inObj ) { return inObj->getFlags(); } +void setPxMaterial_FrictionCombineMode( PxMaterial* inObj, PxCombineMode::Enum inArg){ inObj->setFrictionCombineMode( inArg ); } +PxCombineMode::Enum getPxMaterial_FrictionCombineMode( const PxMaterial* inObj ) { return inObj->getFrictionCombineMode(); } +void setPxMaterial_RestitutionCombineMode( PxMaterial* inObj, PxCombineMode::Enum inArg){ inObj->setRestitutionCombineMode( inArg ); } +PxCombineMode::Enum getPxMaterial_RestitutionCombineMode( const PxMaterial* inObj ) { return inObj->getRestitutionCombineMode(); } +const char * getPxMaterial_ConcreteTypeName( const PxMaterial* inObj ) { return inObj->getConcreteTypeName(); } +inline void * getPxMaterialUserData( const PxMaterial* inOwner ) { return inOwner->userData; } +inline void setPxMaterialUserData( PxMaterial* inOwner, void * inData) { inOwner->userData = inData; } +PX_PHYSX_CORE_API PxMaterialGeneratedInfo::PxMaterialGeneratedInfo() + : ReferenceCount( "ReferenceCount", getPxMaterial_ReferenceCount) + , DynamicFriction( "DynamicFriction", setPxMaterial_DynamicFriction, getPxMaterial_DynamicFriction) + , StaticFriction( "StaticFriction", setPxMaterial_StaticFriction, getPxMaterial_StaticFriction) + , Restitution( "Restitution", setPxMaterial_Restitution, getPxMaterial_Restitution) + , Flags( "Flags", setPxMaterial_Flags, getPxMaterial_Flags) + , FrictionCombineMode( "FrictionCombineMode", setPxMaterial_FrictionCombineMode, getPxMaterial_FrictionCombineMode) + , RestitutionCombineMode( "RestitutionCombineMode", setPxMaterial_RestitutionCombineMode, getPxMaterial_RestitutionCombineMode) + , ConcreteTypeName( "ConcreteTypeName", getPxMaterial_ConcreteTypeName) + , UserData( "UserData", setPxMaterialUserData, getPxMaterialUserData ) +{} +PX_PHYSX_CORE_API PxMaterialGeneratedValues::PxMaterialGeneratedValues( const PxMaterial* inSource ) + :ReferenceCount( getPxMaterial_ReferenceCount( inSource ) ) + ,DynamicFriction( getPxMaterial_DynamicFriction( inSource ) ) + ,StaticFriction( getPxMaterial_StaticFriction( inSource ) ) + ,Restitution( getPxMaterial_Restitution( inSource ) ) + ,Flags( getPxMaterial_Flags( inSource ) ) + ,FrictionCombineMode( getPxMaterial_FrictionCombineMode( inSource ) ) + ,RestitutionCombineMode( getPxMaterial_RestitutionCombineMode( inSource ) ) + ,ConcreteTypeName( getPxMaterial_ConcreteTypeName( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); +} +PxScene * getPxActor_Scene( const PxActor* inObj ) { return inObj->getScene(); } +void setPxActor_Name( PxActor* inObj, const char * inArg){ inObj->setName( inArg ); } +const char * getPxActor_Name( const PxActor* inObj ) { return inObj->getName(); } +void setPxActor_ActorFlags( PxActor* inObj, PxActorFlags inArg){ inObj->setActorFlags( inArg ); } +PxActorFlags getPxActor_ActorFlags( const PxActor* inObj ) { return inObj->getActorFlags(); } +void setPxActor_DominanceGroup( PxActor* inObj, PxDominanceGroup inArg){ inObj->setDominanceGroup( inArg ); } +PxDominanceGroup getPxActor_DominanceGroup( const PxActor* inObj ) { return inObj->getDominanceGroup(); } +void setPxActor_OwnerClient( PxActor* inObj, PxClientID inArg){ inObj->setOwnerClient( inArg ); } +PxClientID getPxActor_OwnerClient( const PxActor* inObj ) { return inObj->getOwnerClient(); } +PxAggregate * getPxActor_Aggregate( const PxActor* inObj ) { return inObj->getAggregate(); } +inline void * getPxActorUserData( const PxActor* inOwner ) { return inOwner->userData; } +inline void setPxActorUserData( PxActor* inOwner, void * inData) { inOwner->userData = inData; } +PX_PHYSX_CORE_API PxActorGeneratedInfo::PxActorGeneratedInfo() + : Scene( "Scene", getPxActor_Scene) + , Name( "Name", setPxActor_Name, getPxActor_Name) + , ActorFlags( "ActorFlags", setPxActor_ActorFlags, getPxActor_ActorFlags) + , DominanceGroup( "DominanceGroup", setPxActor_DominanceGroup, getPxActor_DominanceGroup) + , OwnerClient( "OwnerClient", setPxActor_OwnerClient, getPxActor_OwnerClient) + , Aggregate( "Aggregate", getPxActor_Aggregate) + , UserData( "UserData", setPxActorUserData, getPxActorUserData ) +{} +PX_PHYSX_CORE_API PxActorGeneratedValues::PxActorGeneratedValues( const PxActor* inSource ) + :Scene( getPxActor_Scene( inSource ) ) + ,Name( getPxActor_Name( inSource ) ) + ,ActorFlags( getPxActor_ActorFlags( inSource ) ) + ,DominanceGroup( getPxActor_DominanceGroup( inSource ) ) + ,OwnerClient( getPxActor_OwnerClient( inSource ) ) + ,Aggregate( getPxActor_Aggregate( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); +} +void setPxRigidActor_GlobalPose( PxRigidActor* inObj, const PxTransform & inArg){ inObj->setGlobalPose( inArg ); } +PxTransform getPxRigidActor_GlobalPose( const PxRigidActor* inObj ) { return inObj->getGlobalPose(); } +PxU32 getPxRigidActor_Shapes( const PxRigidActor* inObj, PxShape ** outBuffer, PxU32 inBufSize ) { return inObj->getShapes( outBuffer, inBufSize ); } +PxU32 getNbPxRigidActor_Shapes( const PxRigidActor* inObj ) { return inObj->getNbShapes( ); } +PxU32 getPxRigidActor_Constraints( const PxRigidActor* inObj, PxConstraint ** outBuffer, PxU32 inBufSize ) { return inObj->getConstraints( outBuffer, inBufSize ); } +PxU32 getNbPxRigidActor_Constraints( const PxRigidActor* inObj ) { return inObj->getNbConstraints( ); } +PX_PHYSX_CORE_API PxRigidActorGeneratedInfo::PxRigidActorGeneratedInfo() + : GlobalPose( "GlobalPose", setPxRigidActor_GlobalPose, getPxRigidActor_GlobalPose) + , Shapes( "Shapes", getPxRigidActor_Shapes, getNbPxRigidActor_Shapes ) + , Constraints( "Constraints", getPxRigidActor_Constraints, getNbPxRigidActor_Constraints ) +{} +PX_PHYSX_CORE_API PxRigidActorGeneratedValues::PxRigidActorGeneratedValues( const PxRigidActor* inSource ) + :PxActorGeneratedValues( inSource ) + ,GlobalPose( getPxRigidActor_GlobalPose( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxRigidBody_CMassLocalPose( PxRigidBody* inObj, const PxTransform & inArg){ inObj->setCMassLocalPose( inArg ); } +PxTransform getPxRigidBody_CMassLocalPose( const PxRigidBody* inObj ) { return inObj->getCMassLocalPose(); } +void setPxRigidBody_Mass( PxRigidBody* inObj, PxReal inArg){ inObj->setMass( inArg ); } +PxReal getPxRigidBody_Mass( const PxRigidBody* inObj ) { return inObj->getMass(); } +PxReal getPxRigidBody_InvMass( const PxRigidBody* inObj ) { return inObj->getInvMass(); } +void setPxRigidBody_MassSpaceInertiaTensor( PxRigidBody* inObj, const PxVec3 & inArg){ inObj->setMassSpaceInertiaTensor( inArg ); } +PxVec3 getPxRigidBody_MassSpaceInertiaTensor( const PxRigidBody* inObj ) { return inObj->getMassSpaceInertiaTensor(); } +PxVec3 getPxRigidBody_MassSpaceInvInertiaTensor( const PxRigidBody* inObj ) { return inObj->getMassSpaceInvInertiaTensor(); } +void setPxRigidBody_LinearDamping( PxRigidBody* inObj, PxReal inArg){ inObj->setLinearDamping( inArg ); } +PxReal getPxRigidBody_LinearDamping( const PxRigidBody* inObj ) { return inObj->getLinearDamping(); } +void setPxRigidBody_AngularDamping( PxRigidBody* inObj, PxReal inArg){ inObj->setAngularDamping( inArg ); } +PxReal getPxRigidBody_AngularDamping( const PxRigidBody* inObj ) { return inObj->getAngularDamping(); } +void setPxRigidBody_LinearVelocity( PxRigidBody* inObj, const PxVec3 & inArg){ inObj->setLinearVelocity( inArg ); } +PxVec3 getPxRigidBody_LinearVelocity( const PxRigidBody* inObj ) { return inObj->getLinearVelocity(); } +void setPxRigidBody_AngularVelocity( PxRigidBody* inObj, const PxVec3 & inArg){ inObj->setAngularVelocity( inArg ); } +PxVec3 getPxRigidBody_AngularVelocity( const PxRigidBody* inObj ) { return inObj->getAngularVelocity(); } +void setPxRigidBody_MaxAngularVelocity( PxRigidBody* inObj, PxReal inArg){ inObj->setMaxAngularVelocity( inArg ); } +PxReal getPxRigidBody_MaxAngularVelocity( const PxRigidBody* inObj ) { return inObj->getMaxAngularVelocity(); } +void setPxRigidBody_MaxLinearVelocity( PxRigidBody* inObj, PxReal inArg){ inObj->setMaxLinearVelocity( inArg ); } +PxReal getPxRigidBody_MaxLinearVelocity( const PxRigidBody* inObj ) { return inObj->getMaxLinearVelocity(); } +void setPxRigidBody_RigidBodyFlags( PxRigidBody* inObj, PxRigidBodyFlags inArg){ inObj->setRigidBodyFlags( inArg ); } +PxRigidBodyFlags getPxRigidBody_RigidBodyFlags( const PxRigidBody* inObj ) { return inObj->getRigidBodyFlags(); } +void setPxRigidBody_MinCCDAdvanceCoefficient( PxRigidBody* inObj, PxReal inArg){ inObj->setMinCCDAdvanceCoefficient( inArg ); } +PxReal getPxRigidBody_MinCCDAdvanceCoefficient( const PxRigidBody* inObj ) { return inObj->getMinCCDAdvanceCoefficient(); } +void setPxRigidBody_MaxDepenetrationVelocity( PxRigidBody* inObj, PxReal inArg){ inObj->setMaxDepenetrationVelocity( inArg ); } +PxReal getPxRigidBody_MaxDepenetrationVelocity( const PxRigidBody* inObj ) { return inObj->getMaxDepenetrationVelocity(); } +void setPxRigidBody_MaxContactImpulse( PxRigidBody* inObj, PxReal inArg){ inObj->setMaxContactImpulse( inArg ); } +PxReal getPxRigidBody_MaxContactImpulse( const PxRigidBody* inObj ) { return inObj->getMaxContactImpulse(); } +PX_PHYSX_CORE_API PxRigidBodyGeneratedInfo::PxRigidBodyGeneratedInfo() + : CMassLocalPose( "CMassLocalPose", setPxRigidBody_CMassLocalPose, getPxRigidBody_CMassLocalPose) + , Mass( "Mass", setPxRigidBody_Mass, getPxRigidBody_Mass) + , InvMass( "InvMass", getPxRigidBody_InvMass) + , MassSpaceInertiaTensor( "MassSpaceInertiaTensor", setPxRigidBody_MassSpaceInertiaTensor, getPxRigidBody_MassSpaceInertiaTensor) + , MassSpaceInvInertiaTensor( "MassSpaceInvInertiaTensor", getPxRigidBody_MassSpaceInvInertiaTensor) + , LinearDamping( "LinearDamping", setPxRigidBody_LinearDamping, getPxRigidBody_LinearDamping) + , AngularDamping( "AngularDamping", setPxRigidBody_AngularDamping, getPxRigidBody_AngularDamping) + , LinearVelocity( "LinearVelocity", setPxRigidBody_LinearVelocity, getPxRigidBody_LinearVelocity) + , AngularVelocity( "AngularVelocity", setPxRigidBody_AngularVelocity, getPxRigidBody_AngularVelocity) + , MaxAngularVelocity( "MaxAngularVelocity", setPxRigidBody_MaxAngularVelocity, getPxRigidBody_MaxAngularVelocity) + , MaxLinearVelocity( "MaxLinearVelocity", setPxRigidBody_MaxLinearVelocity, getPxRigidBody_MaxLinearVelocity) + , RigidBodyFlags( "RigidBodyFlags", setPxRigidBody_RigidBodyFlags, getPxRigidBody_RigidBodyFlags) + , MinCCDAdvanceCoefficient( "MinCCDAdvanceCoefficient", setPxRigidBody_MinCCDAdvanceCoefficient, getPxRigidBody_MinCCDAdvanceCoefficient) + , MaxDepenetrationVelocity( "MaxDepenetrationVelocity", setPxRigidBody_MaxDepenetrationVelocity, getPxRigidBody_MaxDepenetrationVelocity) + , MaxContactImpulse( "MaxContactImpulse", setPxRigidBody_MaxContactImpulse, getPxRigidBody_MaxContactImpulse) +{} +PX_PHYSX_CORE_API PxRigidBodyGeneratedValues::PxRigidBodyGeneratedValues( const PxRigidBody* inSource ) + :PxRigidActorGeneratedValues( inSource ) + ,CMassLocalPose( getPxRigidBody_CMassLocalPose( inSource ) ) + ,Mass( getPxRigidBody_Mass( inSource ) ) + ,InvMass( getPxRigidBody_InvMass( inSource ) ) + ,MassSpaceInertiaTensor( getPxRigidBody_MassSpaceInertiaTensor( inSource ) ) + ,MassSpaceInvInertiaTensor( getPxRigidBody_MassSpaceInvInertiaTensor( inSource ) ) + ,LinearDamping( getPxRigidBody_LinearDamping( inSource ) ) + ,AngularDamping( getPxRigidBody_AngularDamping( inSource ) ) + ,LinearVelocity( getPxRigidBody_LinearVelocity( inSource ) ) + ,AngularVelocity( getPxRigidBody_AngularVelocity( inSource ) ) + ,MaxAngularVelocity( getPxRigidBody_MaxAngularVelocity( inSource ) ) + ,MaxLinearVelocity( getPxRigidBody_MaxLinearVelocity( inSource ) ) + ,RigidBodyFlags( getPxRigidBody_RigidBodyFlags( inSource ) ) + ,MinCCDAdvanceCoefficient( getPxRigidBody_MinCCDAdvanceCoefficient( inSource ) ) + ,MaxDepenetrationVelocity( getPxRigidBody_MaxDepenetrationVelocity( inSource ) ) + ,MaxContactImpulse( getPxRigidBody_MaxContactImpulse( inSource ) ) +{ + PX_UNUSED(inSource); +} +_Bool getPxRigidDynamic_IsSleeping( const PxRigidDynamic* inObj ) { return inObj->isSleeping(); } +void setPxRigidDynamic_SleepThreshold( PxRigidDynamic* inObj, PxReal inArg){ inObj->setSleepThreshold( inArg ); } +PxReal getPxRigidDynamic_SleepThreshold( const PxRigidDynamic* inObj ) { return inObj->getSleepThreshold(); } +void setPxRigidDynamic_StabilizationThreshold( PxRigidDynamic* inObj, PxReal inArg){ inObj->setStabilizationThreshold( inArg ); } +PxReal getPxRigidDynamic_StabilizationThreshold( const PxRigidDynamic* inObj ) { return inObj->getStabilizationThreshold(); } +void setPxRigidDynamic_RigidDynamicLockFlags( PxRigidDynamic* inObj, PxRigidDynamicLockFlags inArg){ inObj->setRigidDynamicLockFlags( inArg ); } +PxRigidDynamicLockFlags getPxRigidDynamic_RigidDynamicLockFlags( const PxRigidDynamic* inObj ) { return inObj->getRigidDynamicLockFlags(); } +void setPxRigidDynamic_WakeCounter( PxRigidDynamic* inObj, PxReal inArg){ inObj->setWakeCounter( inArg ); } +PxReal getPxRigidDynamic_WakeCounter( const PxRigidDynamic* inObj ) { return inObj->getWakeCounter(); } +void setPxRigidDynamic_SolverIterationCounts( PxRigidDynamic* inObj, PxU32 inArg0, PxU32 inArg1 ) { inObj->setSolverIterationCounts( inArg0, inArg1 ); } +void getPxRigidDynamic_SolverIterationCounts( const PxRigidDynamic* inObj, PxU32& inArg0, PxU32& inArg1 ) { inObj->getSolverIterationCounts( inArg0, inArg1 ); } +void setPxRigidDynamic_ContactReportThreshold( PxRigidDynamic* inObj, PxReal inArg){ inObj->setContactReportThreshold( inArg ); } +PxReal getPxRigidDynamic_ContactReportThreshold( const PxRigidDynamic* inObj ) { return inObj->getContactReportThreshold(); } +const char * getPxRigidDynamic_ConcreteTypeName( const PxRigidDynamic* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxRigidDynamicGeneratedInfo::PxRigidDynamicGeneratedInfo() + : IsSleeping( "IsSleeping", getPxRigidDynamic_IsSleeping) + , SleepThreshold( "SleepThreshold", setPxRigidDynamic_SleepThreshold, getPxRigidDynamic_SleepThreshold) + , StabilizationThreshold( "StabilizationThreshold", setPxRigidDynamic_StabilizationThreshold, getPxRigidDynamic_StabilizationThreshold) + , RigidDynamicLockFlags( "RigidDynamicLockFlags", setPxRigidDynamic_RigidDynamicLockFlags, getPxRigidDynamic_RigidDynamicLockFlags) + , WakeCounter( "WakeCounter", setPxRigidDynamic_WakeCounter, getPxRigidDynamic_WakeCounter) + , SolverIterationCounts( "SolverIterationCounts", "minPositionIters", "minVelocityIters", setPxRigidDynamic_SolverIterationCounts, getPxRigidDynamic_SolverIterationCounts) + , ContactReportThreshold( "ContactReportThreshold", setPxRigidDynamic_ContactReportThreshold, getPxRigidDynamic_ContactReportThreshold) + , ConcreteTypeName( "ConcreteTypeName", getPxRigidDynamic_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxRigidDynamicGeneratedValues::PxRigidDynamicGeneratedValues( const PxRigidDynamic* inSource ) + :PxRigidBodyGeneratedValues( inSource ) + ,IsSleeping( getPxRigidDynamic_IsSleeping( inSource ) ) + ,SleepThreshold( getPxRigidDynamic_SleepThreshold( inSource ) ) + ,StabilizationThreshold( getPxRigidDynamic_StabilizationThreshold( inSource ) ) + ,RigidDynamicLockFlags( getPxRigidDynamic_RigidDynamicLockFlags( inSource ) ) + ,WakeCounter( getPxRigidDynamic_WakeCounter( inSource ) ) + ,ContactReportThreshold( getPxRigidDynamic_ContactReportThreshold( inSource ) ) + ,ConcreteTypeName( getPxRigidDynamic_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); + getPxRigidDynamic_SolverIterationCounts( inSource, SolverIterationCounts[0], SolverIterationCounts[1] ); +} +const char * getPxRigidStatic_ConcreteTypeName( const PxRigidStatic* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxRigidStaticGeneratedInfo::PxRigidStaticGeneratedInfo() + : ConcreteTypeName( "ConcreteTypeName", getPxRigidStatic_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxRigidStaticGeneratedValues::PxRigidStaticGeneratedValues( const PxRigidStatic* inSource ) + :PxRigidActorGeneratedValues( inSource ) + ,ConcreteTypeName( getPxRigidStatic_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxArticulationJointBase * getPxArticulationLink_InboundJoint( const PxArticulationLink* inObj ) { return inObj->getInboundJoint(); } +PxU32 getPxArticulationLink_InboundJointDof( const PxArticulationLink* inObj ) { return inObj->getInboundJointDof(); } +PxU32 getPxArticulationLink_LinkIndex( const PxArticulationLink* inObj ) { return inObj->getLinkIndex(); } +PxU32 getPxArticulationLink_Children( const PxArticulationLink* inObj, PxArticulationLink ** outBuffer, PxU32 inBufSize ) { return inObj->getChildren( outBuffer, inBufSize ); } +PxU32 getNbPxArticulationLink_Children( const PxArticulationLink* inObj ) { return inObj->getNbChildren( ); } +const char * getPxArticulationLink_ConcreteTypeName( const PxArticulationLink* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxArticulationLinkGeneratedInfo::PxArticulationLinkGeneratedInfo() + : InboundJoint( "InboundJoint", getPxArticulationLink_InboundJoint) + , InboundJointDof( "InboundJointDof", getPxArticulationLink_InboundJointDof) + , LinkIndex( "LinkIndex", getPxArticulationLink_LinkIndex) + , Children( "Children", getPxArticulationLink_Children, getNbPxArticulationLink_Children ) + , ConcreteTypeName( "ConcreteTypeName", getPxArticulationLink_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxArticulationLinkGeneratedValues::PxArticulationLinkGeneratedValues( const PxArticulationLink* inSource ) + :PxRigidBodyGeneratedValues( inSource ) + ,InboundJoint( getPxArticulationLink_InboundJoint( inSource ) ) + ,InboundJointDof( getPxArticulationLink_InboundJointDof( inSource ) ) + ,LinkIndex( getPxArticulationLink_LinkIndex( inSource ) ) + ,ConcreteTypeName( getPxArticulationLink_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxArticulationJointBase_ParentPose( PxArticulationJointBase* inObj, const PxTransform & inArg){ inObj->setParentPose( inArg ); } +PxTransform getPxArticulationJointBase_ParentPose( const PxArticulationJointBase* inObj ) { return inObj->getParentPose(); } +void setPxArticulationJointBase_ChildPose( PxArticulationJointBase* inObj, const PxTransform & inArg){ inObj->setChildPose( inArg ); } +PxTransform getPxArticulationJointBase_ChildPose( const PxArticulationJointBase* inObj ) { return inObj->getChildPose(); } +PX_PHYSX_CORE_API PxArticulationJointBaseGeneratedInfo::PxArticulationJointBaseGeneratedInfo() + : ParentPose( "ParentPose", setPxArticulationJointBase_ParentPose, getPxArticulationJointBase_ParentPose) + , ChildPose( "ChildPose", setPxArticulationJointBase_ChildPose, getPxArticulationJointBase_ChildPose) +{} +PX_PHYSX_CORE_API PxArticulationJointBaseGeneratedValues::PxArticulationJointBaseGeneratedValues( const PxArticulationJointBase* inSource ) + :ParentPose( getPxArticulationJointBase_ParentPose( inSource ) ) + ,ChildPose( getPxArticulationJointBase_ChildPose( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxArticulationJoint_TargetOrientation( PxArticulationJoint* inObj, const PxQuat & inArg){ inObj->setTargetOrientation( inArg ); } +PxQuat getPxArticulationJoint_TargetOrientation( const PxArticulationJoint* inObj ) { return inObj->getTargetOrientation(); } +void setPxArticulationJoint_TargetVelocity( PxArticulationJoint* inObj, const PxVec3 & inArg){ inObj->setTargetVelocity( inArg ); } +PxVec3 getPxArticulationJoint_TargetVelocity( const PxArticulationJoint* inObj ) { return inObj->getTargetVelocity(); } +void setPxArticulationJoint_DriveType( PxArticulationJoint* inObj, PxArticulationJointDriveType::Enum inArg){ inObj->setDriveType( inArg ); } +PxArticulationJointDriveType::Enum getPxArticulationJoint_DriveType( const PxArticulationJoint* inObj ) { return inObj->getDriveType(); } +void setPxArticulationJoint_Stiffness( PxArticulationJoint* inObj, PxReal inArg){ inObj->setStiffness( inArg ); } +PxReal getPxArticulationJoint_Stiffness( const PxArticulationJoint* inObj ) { return inObj->getStiffness(); } +void setPxArticulationJoint_Damping( PxArticulationJoint* inObj, PxReal inArg){ inObj->setDamping( inArg ); } +PxReal getPxArticulationJoint_Damping( const PxArticulationJoint* inObj ) { return inObj->getDamping(); } +void setPxArticulationJoint_InternalCompliance( PxArticulationJoint* inObj, PxReal inArg){ inObj->setInternalCompliance( inArg ); } +PxReal getPxArticulationJoint_InternalCompliance( const PxArticulationJoint* inObj ) { return inObj->getInternalCompliance(); } +void setPxArticulationJoint_ExternalCompliance( PxArticulationJoint* inObj, PxReal inArg){ inObj->setExternalCompliance( inArg ); } +PxReal getPxArticulationJoint_ExternalCompliance( const PxArticulationJoint* inObj ) { return inObj->getExternalCompliance(); } +void setPxArticulationJoint_SwingLimit( PxArticulationJoint* inObj, PxReal inArg0, PxReal inArg1 ) { inObj->setSwingLimit( inArg0, inArg1 ); } +void getPxArticulationJoint_SwingLimit( const PxArticulationJoint* inObj, PxReal& inArg0, PxReal& inArg1 ) { inObj->getSwingLimit( inArg0, inArg1 ); } +void setPxArticulationJoint_TangentialStiffness( PxArticulationJoint* inObj, PxReal inArg){ inObj->setTangentialStiffness( inArg ); } +PxReal getPxArticulationJoint_TangentialStiffness( const PxArticulationJoint* inObj ) { return inObj->getTangentialStiffness(); } +void setPxArticulationJoint_TangentialDamping( PxArticulationJoint* inObj, PxReal inArg){ inObj->setTangentialDamping( inArg ); } +PxReal getPxArticulationJoint_TangentialDamping( const PxArticulationJoint* inObj ) { return inObj->getTangentialDamping(); } +void setPxArticulationJoint_SwingLimitContactDistance( PxArticulationJoint* inObj, PxReal inArg){ inObj->setSwingLimitContactDistance( inArg ); } +PxReal getPxArticulationJoint_SwingLimitContactDistance( const PxArticulationJoint* inObj ) { return inObj->getSwingLimitContactDistance(); } +void setPxArticulationJoint_SwingLimitEnabled( PxArticulationJoint* inObj, _Bool inArg){ inObj->setSwingLimitEnabled( inArg ); } +_Bool getPxArticulationJoint_SwingLimitEnabled( const PxArticulationJoint* inObj ) { return inObj->getSwingLimitEnabled(); } +void setPxArticulationJoint_TwistLimit( PxArticulationJoint* inObj, PxReal inArg0, PxReal inArg1 ) { inObj->setTwistLimit( inArg0, inArg1 ); } +void getPxArticulationJoint_TwistLimit( const PxArticulationJoint* inObj, PxReal& inArg0, PxReal& inArg1 ) { inObj->getTwistLimit( inArg0, inArg1 ); } +void setPxArticulationJoint_TwistLimitEnabled( PxArticulationJoint* inObj, _Bool inArg){ inObj->setTwistLimitEnabled( inArg ); } +_Bool getPxArticulationJoint_TwistLimitEnabled( const PxArticulationJoint* inObj ) { return inObj->getTwistLimitEnabled(); } +void setPxArticulationJoint_TwistLimitContactDistance( PxArticulationJoint* inObj, PxReal inArg){ inObj->setTwistLimitContactDistance( inArg ); } +PxReal getPxArticulationJoint_TwistLimitContactDistance( const PxArticulationJoint* inObj ) { return inObj->getTwistLimitContactDistance(); } +const char * getPxArticulationJoint_ConcreteTypeName( const PxArticulationJoint* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxArticulationJointGeneratedInfo::PxArticulationJointGeneratedInfo() + : TargetOrientation( "TargetOrientation", setPxArticulationJoint_TargetOrientation, getPxArticulationJoint_TargetOrientation) + , TargetVelocity( "TargetVelocity", setPxArticulationJoint_TargetVelocity, getPxArticulationJoint_TargetVelocity) + , DriveType( "DriveType", setPxArticulationJoint_DriveType, getPxArticulationJoint_DriveType) + , Stiffness( "Stiffness", setPxArticulationJoint_Stiffness, getPxArticulationJoint_Stiffness) + , Damping( "Damping", setPxArticulationJoint_Damping, getPxArticulationJoint_Damping) + , InternalCompliance( "InternalCompliance", setPxArticulationJoint_InternalCompliance, getPxArticulationJoint_InternalCompliance) + , ExternalCompliance( "ExternalCompliance", setPxArticulationJoint_ExternalCompliance, getPxArticulationJoint_ExternalCompliance) + , SwingLimit( "SwingLimit", "zLimit", "yLimit", setPxArticulationJoint_SwingLimit, getPxArticulationJoint_SwingLimit) + , TangentialStiffness( "TangentialStiffness", setPxArticulationJoint_TangentialStiffness, getPxArticulationJoint_TangentialStiffness) + , TangentialDamping( "TangentialDamping", setPxArticulationJoint_TangentialDamping, getPxArticulationJoint_TangentialDamping) + , SwingLimitContactDistance( "SwingLimitContactDistance", setPxArticulationJoint_SwingLimitContactDistance, getPxArticulationJoint_SwingLimitContactDistance) + , SwingLimitEnabled( "SwingLimitEnabled", setPxArticulationJoint_SwingLimitEnabled, getPxArticulationJoint_SwingLimitEnabled) + , TwistLimit( "TwistLimit", "lower", "upper", setPxArticulationJoint_TwistLimit, getPxArticulationJoint_TwistLimit) + , TwistLimitEnabled( "TwistLimitEnabled", setPxArticulationJoint_TwistLimitEnabled, getPxArticulationJoint_TwistLimitEnabled) + , TwistLimitContactDistance( "TwistLimitContactDistance", setPxArticulationJoint_TwistLimitContactDistance, getPxArticulationJoint_TwistLimitContactDistance) + , ConcreteTypeName( "ConcreteTypeName", getPxArticulationJoint_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxArticulationJointGeneratedValues::PxArticulationJointGeneratedValues( const PxArticulationJoint* inSource ) + :PxArticulationJointBaseGeneratedValues( inSource ) + ,TargetOrientation( getPxArticulationJoint_TargetOrientation( inSource ) ) + ,TargetVelocity( getPxArticulationJoint_TargetVelocity( inSource ) ) + ,DriveType( getPxArticulationJoint_DriveType( inSource ) ) + ,Stiffness( getPxArticulationJoint_Stiffness( inSource ) ) + ,Damping( getPxArticulationJoint_Damping( inSource ) ) + ,InternalCompliance( getPxArticulationJoint_InternalCompliance( inSource ) ) + ,ExternalCompliance( getPxArticulationJoint_ExternalCompliance( inSource ) ) + ,TangentialStiffness( getPxArticulationJoint_TangentialStiffness( inSource ) ) + ,TangentialDamping( getPxArticulationJoint_TangentialDamping( inSource ) ) + ,SwingLimitContactDistance( getPxArticulationJoint_SwingLimitContactDistance( inSource ) ) + ,SwingLimitEnabled( getPxArticulationJoint_SwingLimitEnabled( inSource ) ) + ,TwistLimitEnabled( getPxArticulationJoint_TwistLimitEnabled( inSource ) ) + ,TwistLimitContactDistance( getPxArticulationJoint_TwistLimitContactDistance( inSource ) ) + ,ConcreteTypeName( getPxArticulationJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); + getPxArticulationJoint_SwingLimit( inSource, SwingLimit[0], SwingLimit[1] ); + getPxArticulationJoint_TwistLimit( inSource, TwistLimit[0], TwistLimit[1] ); +} +PxScene * getPxArticulationBase_Scene( const PxArticulationBase* inObj ) { return inObj->getScene(); } +void setPxArticulationBase_SolverIterationCounts( PxArticulationBase* inObj, PxU32 inArg0, PxU32 inArg1 ) { inObj->setSolverIterationCounts( inArg0, inArg1 ); } +void getPxArticulationBase_SolverIterationCounts( const PxArticulationBase* inObj, PxU32& inArg0, PxU32& inArg1 ) { inObj->getSolverIterationCounts( inArg0, inArg1 ); } +_Bool getPxArticulationBase_IsSleeping( const PxArticulationBase* inObj ) { return inObj->isSleeping(); } +void setPxArticulationBase_SleepThreshold( PxArticulationBase* inObj, PxReal inArg){ inObj->setSleepThreshold( inArg ); } +PxReal getPxArticulationBase_SleepThreshold( const PxArticulationBase* inObj ) { return inObj->getSleepThreshold(); } +void setPxArticulationBase_StabilizationThreshold( PxArticulationBase* inObj, PxReal inArg){ inObj->setStabilizationThreshold( inArg ); } +PxReal getPxArticulationBase_StabilizationThreshold( const PxArticulationBase* inObj ) { return inObj->getStabilizationThreshold(); } +void setPxArticulationBase_WakeCounter( PxArticulationBase* inObj, PxReal inArg){ inObj->setWakeCounter( inArg ); } +PxReal getPxArticulationBase_WakeCounter( const PxArticulationBase* inObj ) { return inObj->getWakeCounter(); } +PxU32 getPxArticulationBase_Links( const PxArticulationBase* inObj, PxArticulationLink ** outBuffer, PxU32 inBufSize ) { return inObj->getLinks( outBuffer, inBufSize ); } +PxU32 getNbPxArticulationBase_Links( const PxArticulationBase* inObj ) { return inObj->getNbLinks( ); } +void setPxArticulationBase_Name( PxArticulationBase* inObj, const char * inArg){ inObj->setName( inArg ); } +const char * getPxArticulationBase_Name( const PxArticulationBase* inObj ) { return inObj->getName(); } +PxAggregate * getPxArticulationBase_Aggregate( const PxArticulationBase* inObj ) { return inObj->getAggregate(); } +PxArticulationBase::Enum getPxArticulationBase_Type( const PxArticulationBase* inObj ) { return inObj->getType(); } +inline void * getPxArticulationBaseUserData( const PxArticulationBase* inOwner ) { return inOwner->userData; } +inline void setPxArticulationBaseUserData( PxArticulationBase* inOwner, void * inData) { inOwner->userData = inData; } +PX_PHYSX_CORE_API PxArticulationBaseGeneratedInfo::PxArticulationBaseGeneratedInfo() + : Scene( "Scene", getPxArticulationBase_Scene) + , SolverIterationCounts( "SolverIterationCounts", "minPositionIters", "minVelocityIters", setPxArticulationBase_SolverIterationCounts, getPxArticulationBase_SolverIterationCounts) + , IsSleeping( "IsSleeping", getPxArticulationBase_IsSleeping) + , SleepThreshold( "SleepThreshold", setPxArticulationBase_SleepThreshold, getPxArticulationBase_SleepThreshold) + , StabilizationThreshold( "StabilizationThreshold", setPxArticulationBase_StabilizationThreshold, getPxArticulationBase_StabilizationThreshold) + , WakeCounter( "WakeCounter", setPxArticulationBase_WakeCounter, getPxArticulationBase_WakeCounter) + , Links( "Links", getPxArticulationBase_Links, getNbPxArticulationBase_Links ) + , Name( "Name", setPxArticulationBase_Name, getPxArticulationBase_Name) + , Aggregate( "Aggregate", getPxArticulationBase_Aggregate) + , Type( "Type", getPxArticulationBase_Type) + , UserData( "UserData", setPxArticulationBaseUserData, getPxArticulationBaseUserData ) +{} +PX_PHYSX_CORE_API PxArticulationBaseGeneratedValues::PxArticulationBaseGeneratedValues( const PxArticulationBase* inSource ) + :Scene( getPxArticulationBase_Scene( inSource ) ) + ,IsSleeping( getPxArticulationBase_IsSleeping( inSource ) ) + ,SleepThreshold( getPxArticulationBase_SleepThreshold( inSource ) ) + ,StabilizationThreshold( getPxArticulationBase_StabilizationThreshold( inSource ) ) + ,WakeCounter( getPxArticulationBase_WakeCounter( inSource ) ) + ,Name( getPxArticulationBase_Name( inSource ) ) + ,Aggregate( getPxArticulationBase_Aggregate( inSource ) ) + ,Type( getPxArticulationBase_Type( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); + getPxArticulationBase_SolverIterationCounts( inSource, SolverIterationCounts[0], SolverIterationCounts[1] ); +} +void setPxArticulation_MaxProjectionIterations( PxArticulation* inObj, PxU32 inArg){ inObj->setMaxProjectionIterations( inArg ); } +PxU32 getPxArticulation_MaxProjectionIterations( const PxArticulation* inObj ) { return inObj->getMaxProjectionIterations(); } +void setPxArticulation_SeparationTolerance( PxArticulation* inObj, PxReal inArg){ inObj->setSeparationTolerance( inArg ); } +PxReal getPxArticulation_SeparationTolerance( const PxArticulation* inObj ) { return inObj->getSeparationTolerance(); } +void setPxArticulation_InternalDriveIterations( PxArticulation* inObj, PxU32 inArg){ inObj->setInternalDriveIterations( inArg ); } +PxU32 getPxArticulation_InternalDriveIterations( const PxArticulation* inObj ) { return inObj->getInternalDriveIterations(); } +void setPxArticulation_ExternalDriveIterations( PxArticulation* inObj, PxU32 inArg){ inObj->setExternalDriveIterations( inArg ); } +PxU32 getPxArticulation_ExternalDriveIterations( const PxArticulation* inObj ) { return inObj->getExternalDriveIterations(); } +PX_PHYSX_CORE_API PxArticulationGeneratedInfo::PxArticulationGeneratedInfo() + : MaxProjectionIterations( "MaxProjectionIterations", setPxArticulation_MaxProjectionIterations, getPxArticulation_MaxProjectionIterations) + , SeparationTolerance( "SeparationTolerance", setPxArticulation_SeparationTolerance, getPxArticulation_SeparationTolerance) + , InternalDriveIterations( "InternalDriveIterations", setPxArticulation_InternalDriveIterations, getPxArticulation_InternalDriveIterations) + , ExternalDriveIterations( "ExternalDriveIterations", setPxArticulation_ExternalDriveIterations, getPxArticulation_ExternalDriveIterations) +{} +PX_PHYSX_CORE_API PxArticulationGeneratedValues::PxArticulationGeneratedValues( const PxArticulation* inSource ) + :PxArticulationBaseGeneratedValues( inSource ) + ,MaxProjectionIterations( getPxArticulation_MaxProjectionIterations( inSource ) ) + ,SeparationTolerance( getPxArticulation_SeparationTolerance( inSource ) ) + ,InternalDriveIterations( getPxArticulation_InternalDriveIterations( inSource ) ) + ,ExternalDriveIterations( getPxArticulation_ExternalDriveIterations( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxU32 getPxAggregate_MaxNbActors( const PxAggregate* inObj ) { return inObj->getMaxNbActors(); } +PxU32 getPxAggregate_Actors( const PxAggregate* inObj, PxActor ** outBuffer, PxU32 inBufSize ) { return inObj->getActors( outBuffer, inBufSize ); } +PxU32 getNbPxAggregate_Actors( const PxAggregate* inObj ) { return inObj->getNbActors( ); } +_Bool getPxAggregate_SelfCollision( const PxAggregate* inObj ) { return inObj->getSelfCollision(); } +const char * getPxAggregate_ConcreteTypeName( const PxAggregate* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxAggregateGeneratedInfo::PxAggregateGeneratedInfo() + : MaxNbActors( "MaxNbActors", getPxAggregate_MaxNbActors) + , Actors( "Actors", getPxAggregate_Actors, getNbPxAggregate_Actors ) + , SelfCollision( "SelfCollision", getPxAggregate_SelfCollision) + , ConcreteTypeName( "ConcreteTypeName", getPxAggregate_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxAggregateGeneratedValues::PxAggregateGeneratedValues( const PxAggregate* inSource ) + :MaxNbActors( getPxAggregate_MaxNbActors( inSource ) ) + ,SelfCollision( getPxAggregate_SelfCollision( inSource ) ) + ,ConcreteTypeName( getPxAggregate_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxScene * getPxConstraint_Scene( const PxConstraint* inObj ) { return inObj->getScene(); } +void setPxConstraint_Actors( PxConstraint* inObj, PxRigidActor * inArg0, PxRigidActor * inArg1 ) { inObj->setActors( inArg0, inArg1 ); } +void getPxConstraint_Actors( const PxConstraint* inObj, PxRigidActor *& inArg0, PxRigidActor *& inArg1 ) { inObj->getActors( inArg0, inArg1 ); } +void setPxConstraint_Flags( PxConstraint* inObj, PxConstraintFlags inArg){ inObj->setFlags( inArg ); } +PxConstraintFlags getPxConstraint_Flags( const PxConstraint* inObj ) { return inObj->getFlags(); } +_Bool getPxConstraint_IsValid( const PxConstraint* inObj ) { return inObj->isValid(); } +void setPxConstraint_BreakForce( PxConstraint* inObj, PxReal inArg0, PxReal inArg1 ) { inObj->setBreakForce( inArg0, inArg1 ); } +void getPxConstraint_BreakForce( const PxConstraint* inObj, PxReal& inArg0, PxReal& inArg1 ) { inObj->getBreakForce( inArg0, inArg1 ); } +void setPxConstraint_MinResponseThreshold( PxConstraint* inObj, PxReal inArg){ inObj->setMinResponseThreshold( inArg ); } +PxReal getPxConstraint_MinResponseThreshold( const PxConstraint* inObj ) { return inObj->getMinResponseThreshold(); } +const char * getPxConstraint_ConcreteTypeName( const PxConstraint* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxConstraintGeneratedInfo::PxConstraintGeneratedInfo() + : Scene( "Scene", getPxConstraint_Scene) + , Actors( "Actors", "actor0", "actor1", setPxConstraint_Actors, getPxConstraint_Actors) + , Flags( "Flags", setPxConstraint_Flags, getPxConstraint_Flags) + , IsValid( "IsValid", getPxConstraint_IsValid) + , BreakForce( "BreakForce", "linear", "angular", setPxConstraint_BreakForce, getPxConstraint_BreakForce) + , MinResponseThreshold( "MinResponseThreshold", setPxConstraint_MinResponseThreshold, getPxConstraint_MinResponseThreshold) + , ConcreteTypeName( "ConcreteTypeName", getPxConstraint_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxConstraintGeneratedValues::PxConstraintGeneratedValues( const PxConstraint* inSource ) + :Scene( getPxConstraint_Scene( inSource ) ) + ,Flags( getPxConstraint_Flags( inSource ) ) + ,IsValid( getPxConstraint_IsValid( inSource ) ) + ,MinResponseThreshold( getPxConstraint_MinResponseThreshold( inSource ) ) + ,ConcreteTypeName( getPxConstraint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); + getPxConstraint_Actors( inSource, Actors[0], Actors[1] ); + getPxConstraint_BreakForce( inSource, BreakForce[0], BreakForce[1] ); +} +PxU32 getPxShape_ReferenceCount( const PxShape* inObj ) { return inObj->getReferenceCount(); } +PxGeometryType::Enum getPxShape_GeometryType( const PxShape* inObj ) { return inObj->getGeometryType(); } +void setPxShape_Geometry( PxShape* inObj, const PxGeometry & inArg){ inObj->setGeometry( inArg ); } +PxGeometryHolder getPxShape_Geometry( const PxShape* inObj ) { return inObj->getGeometry(); } +void setPxShape_LocalPose( PxShape* inObj, const PxTransform & inArg){ inObj->setLocalPose( inArg ); } +PxTransform getPxShape_LocalPose( const PxShape* inObj ) { return inObj->getLocalPose(); } +void setPxShape_SimulationFilterData( PxShape* inObj, const PxFilterData & inArg){ inObj->setSimulationFilterData( inArg ); } +PxFilterData getPxShape_SimulationFilterData( const PxShape* inObj ) { return inObj->getSimulationFilterData(); } +void setPxShape_QueryFilterData( PxShape* inObj, const PxFilterData & inArg){ inObj->setQueryFilterData( inArg ); } +PxFilterData getPxShape_QueryFilterData( const PxShape* inObj ) { return inObj->getQueryFilterData(); } +PxU32 getPxShape_Materials( const PxShape* inObj, PxMaterial ** outBuffer, PxU32 inBufSize ) { return inObj->getMaterials( outBuffer, inBufSize ); } +PxU32 getNbPxShape_Materials( const PxShape* inObj ) { return inObj->getNbMaterials( ); } +void setPxShape_ContactOffset( PxShape* inObj, PxReal inArg){ inObj->setContactOffset( inArg ); } +PxReal getPxShape_ContactOffset( const PxShape* inObj ) { return inObj->getContactOffset(); } +void setPxShape_RestOffset( PxShape* inObj, PxReal inArg){ inObj->setRestOffset( inArg ); } +PxReal getPxShape_RestOffset( const PxShape* inObj ) { return inObj->getRestOffset(); } +void setPxShape_TorsionalPatchRadius( PxShape* inObj, PxReal inArg){ inObj->setTorsionalPatchRadius( inArg ); } +PxReal getPxShape_TorsionalPatchRadius( const PxShape* inObj ) { return inObj->getTorsionalPatchRadius(); } +void setPxShape_MinTorsionalPatchRadius( PxShape* inObj, PxReal inArg){ inObj->setMinTorsionalPatchRadius( inArg ); } +PxReal getPxShape_MinTorsionalPatchRadius( const PxShape* inObj ) { return inObj->getMinTorsionalPatchRadius(); } +void setPxShape_Flags( PxShape* inObj, PxShapeFlags inArg){ inObj->setFlags( inArg ); } +PxShapeFlags getPxShape_Flags( const PxShape* inObj ) { return inObj->getFlags(); } +_Bool getPxShape_IsExclusive( const PxShape* inObj ) { return inObj->isExclusive(); } +void setPxShape_Name( PxShape* inObj, const char * inArg){ inObj->setName( inArg ); } +const char * getPxShape_Name( const PxShape* inObj ) { return inObj->getName(); } +const char * getPxShape_ConcreteTypeName( const PxShape* inObj ) { return inObj->getConcreteTypeName(); } +inline void * getPxShapeUserData( const PxShape* inOwner ) { return inOwner->userData; } +inline void setPxShapeUserData( PxShape* inOwner, void * inData) { inOwner->userData = inData; } +PX_PHYSX_CORE_API PxShapeGeneratedInfo::PxShapeGeneratedInfo() + : ReferenceCount( "ReferenceCount", getPxShape_ReferenceCount) + , GeometryType( "GeometryType", getPxShape_GeometryType) + , Geometry( "Geometry", setPxShape_Geometry, getPxShape_Geometry) + , LocalPose( "LocalPose", setPxShape_LocalPose, getPxShape_LocalPose) + , SimulationFilterData( "SimulationFilterData", setPxShape_SimulationFilterData, getPxShape_SimulationFilterData) + , QueryFilterData( "QueryFilterData", setPxShape_QueryFilterData, getPxShape_QueryFilterData) + , Materials( "Materials", getPxShape_Materials, getNbPxShape_Materials ) + , ContactOffset( "ContactOffset", setPxShape_ContactOffset, getPxShape_ContactOffset) + , RestOffset( "RestOffset", setPxShape_RestOffset, getPxShape_RestOffset) + , TorsionalPatchRadius( "TorsionalPatchRadius", setPxShape_TorsionalPatchRadius, getPxShape_TorsionalPatchRadius) + , MinTorsionalPatchRadius( "MinTorsionalPatchRadius", setPxShape_MinTorsionalPatchRadius, getPxShape_MinTorsionalPatchRadius) + , Flags( "Flags", setPxShape_Flags, getPxShape_Flags) + , IsExclusive( "IsExclusive", getPxShape_IsExclusive) + , Name( "Name", setPxShape_Name, getPxShape_Name) + , ConcreteTypeName( "ConcreteTypeName", getPxShape_ConcreteTypeName) + , UserData( "UserData", setPxShapeUserData, getPxShapeUserData ) +{} +PX_PHYSX_CORE_API PxShapeGeneratedValues::PxShapeGeneratedValues( const PxShape* inSource ) + :ReferenceCount( getPxShape_ReferenceCount( inSource ) ) + ,GeometryType( getPxShape_GeometryType( inSource ) ) + ,Geometry( getPxShape_Geometry( inSource ) ) + ,LocalPose( getPxShape_LocalPose( inSource ) ) + ,SimulationFilterData( getPxShape_SimulationFilterData( inSource ) ) + ,QueryFilterData( getPxShape_QueryFilterData( inSource ) ) + ,ContactOffset( getPxShape_ContactOffset( inSource ) ) + ,RestOffset( getPxShape_RestOffset( inSource ) ) + ,TorsionalPatchRadius( getPxShape_TorsionalPatchRadius( inSource ) ) + ,MinTorsionalPatchRadius( getPxShape_MinTorsionalPatchRadius( inSource ) ) + ,Flags( getPxShape_Flags( inSource ) ) + ,IsExclusive( getPxShape_IsExclusive( inSource ) ) + ,Name( getPxShape_Name( inSource ) ) + ,ConcreteTypeName( getPxShape_ConcreteTypeName( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); +} +PxU32 getPxPruningStructure_RigidActors( const PxPruningStructure* inObj, PxRigidActor ** outBuffer, PxU32 inBufSize ) { return inObj->getRigidActors( outBuffer, inBufSize ); } +PxU32 getNbPxPruningStructure_RigidActors( const PxPruningStructure* inObj ) { return inObj->getNbRigidActors( ); } +const char * getPxPruningStructure_ConcreteTypeName( const PxPruningStructure* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxPruningStructureGeneratedInfo::PxPruningStructureGeneratedInfo() + : RigidActors( "RigidActors", getPxPruningStructure_RigidActors, getNbPxPruningStructure_RigidActors ) + , ConcreteTypeName( "ConcreteTypeName", getPxPruningStructure_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxPruningStructureGeneratedValues::PxPruningStructureGeneratedValues( const PxPruningStructure* inSource ) + :ConcreteTypeName( getPxPruningStructure_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +_Bool getPxTolerancesScale_IsValid( const PxTolerancesScale* inObj ) { return inObj->isValid(); } +inline PxReal getPxTolerancesScaleLength( const PxTolerancesScale* inOwner ) { return inOwner->length; } +inline void setPxTolerancesScaleLength( PxTolerancesScale* inOwner, PxReal inData) { inOwner->length = inData; } +inline PxReal getPxTolerancesScaleSpeed( const PxTolerancesScale* inOwner ) { return inOwner->speed; } +inline void setPxTolerancesScaleSpeed( PxTolerancesScale* inOwner, PxReal inData) { inOwner->speed = inData; } +PX_PHYSX_CORE_API PxTolerancesScaleGeneratedInfo::PxTolerancesScaleGeneratedInfo() + : IsValid( "IsValid", getPxTolerancesScale_IsValid) + , Length( "Length", setPxTolerancesScaleLength, getPxTolerancesScaleLength ) + , Speed( "Speed", setPxTolerancesScaleSpeed, getPxTolerancesScaleSpeed ) +{} +PX_PHYSX_CORE_API PxTolerancesScaleGeneratedValues::PxTolerancesScaleGeneratedValues( const PxTolerancesScale* inSource ) + :IsValid( getPxTolerancesScale_IsValid( inSource ) ) + ,Length( inSource->length ) + ,Speed( inSource->speed ) +{ + PX_UNUSED(inSource); +} +PX_PHYSX_CORE_API PxGeometryGeneratedInfo::PxGeometryGeneratedInfo() +{} +PX_PHYSX_CORE_API PxGeometryGeneratedValues::PxGeometryGeneratedValues( const PxGeometry* inSource ) +{ + PX_UNUSED(inSource); +} +inline PxVec3 getPxBoxGeometryHalfExtents( const PxBoxGeometry* inOwner ) { return inOwner->halfExtents; } +inline void setPxBoxGeometryHalfExtents( PxBoxGeometry* inOwner, PxVec3 inData) { inOwner->halfExtents = inData; } +PX_PHYSX_CORE_API PxBoxGeometryGeneratedInfo::PxBoxGeometryGeneratedInfo() + : HalfExtents( "HalfExtents", setPxBoxGeometryHalfExtents, getPxBoxGeometryHalfExtents ) +{} +PX_PHYSX_CORE_API PxBoxGeometryGeneratedValues::PxBoxGeometryGeneratedValues( const PxBoxGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,HalfExtents( inSource->halfExtents ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxCapsuleGeometryRadius( const PxCapsuleGeometry* inOwner ) { return inOwner->radius; } +inline void setPxCapsuleGeometryRadius( PxCapsuleGeometry* inOwner, PxReal inData) { inOwner->radius = inData; } +inline PxReal getPxCapsuleGeometryHalfHeight( const PxCapsuleGeometry* inOwner ) { return inOwner->halfHeight; } +inline void setPxCapsuleGeometryHalfHeight( PxCapsuleGeometry* inOwner, PxReal inData) { inOwner->halfHeight = inData; } +PX_PHYSX_CORE_API PxCapsuleGeometryGeneratedInfo::PxCapsuleGeometryGeneratedInfo() + : Radius( "Radius", setPxCapsuleGeometryRadius, getPxCapsuleGeometryRadius ) + , HalfHeight( "HalfHeight", setPxCapsuleGeometryHalfHeight, getPxCapsuleGeometryHalfHeight ) +{} +PX_PHYSX_CORE_API PxCapsuleGeometryGeneratedValues::PxCapsuleGeometryGeneratedValues( const PxCapsuleGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,Radius( inSource->radius ) + ,HalfHeight( inSource->halfHeight ) +{ + PX_UNUSED(inSource); +} +inline PxVec3 getPxMeshScaleScale( const PxMeshScale* inOwner ) { return inOwner->scale; } +inline void setPxMeshScaleScale( PxMeshScale* inOwner, PxVec3 inData) { inOwner->scale = inData; } +inline PxQuat getPxMeshScaleRotation( const PxMeshScale* inOwner ) { return inOwner->rotation; } +inline void setPxMeshScaleRotation( PxMeshScale* inOwner, PxQuat inData) { inOwner->rotation = inData; } +PX_PHYSX_CORE_API PxMeshScaleGeneratedInfo::PxMeshScaleGeneratedInfo() + : Scale( "Scale", setPxMeshScaleScale, getPxMeshScaleScale ) + , Rotation( "Rotation", setPxMeshScaleRotation, getPxMeshScaleRotation ) +{} +PX_PHYSX_CORE_API PxMeshScaleGeneratedValues::PxMeshScaleGeneratedValues( const PxMeshScale* inSource ) + :Scale( inSource->scale ) + ,Rotation( inSource->rotation ) +{ + PX_UNUSED(inSource); +} +inline PxMeshScale getPxConvexMeshGeometryScale( const PxConvexMeshGeometry* inOwner ) { return inOwner->scale; } +inline void setPxConvexMeshGeometryScale( PxConvexMeshGeometry* inOwner, PxMeshScale inData) { inOwner->scale = inData; } +inline PxConvexMesh * getPxConvexMeshGeometryConvexMesh( const PxConvexMeshGeometry* inOwner ) { return inOwner->convexMesh; } +inline void setPxConvexMeshGeometryConvexMesh( PxConvexMeshGeometry* inOwner, PxConvexMesh * inData) { inOwner->convexMesh = inData; } +inline PxConvexMeshGeometryFlags getPxConvexMeshGeometryMeshFlags( const PxConvexMeshGeometry* inOwner ) { return inOwner->meshFlags; } +inline void setPxConvexMeshGeometryMeshFlags( PxConvexMeshGeometry* inOwner, PxConvexMeshGeometryFlags inData) { inOwner->meshFlags = inData; } +PX_PHYSX_CORE_API PxConvexMeshGeometryGeneratedInfo::PxConvexMeshGeometryGeneratedInfo() + : Scale( "Scale", setPxConvexMeshGeometryScale, getPxConvexMeshGeometryScale ) + , ConvexMesh( "ConvexMesh", setPxConvexMeshGeometryConvexMesh, getPxConvexMeshGeometryConvexMesh ) + , MeshFlags( "MeshFlags", setPxConvexMeshGeometryMeshFlags, getPxConvexMeshGeometryMeshFlags ) +{} +PX_PHYSX_CORE_API PxConvexMeshGeometryGeneratedValues::PxConvexMeshGeometryGeneratedValues( const PxConvexMeshGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,Scale( inSource->scale ) + ,ConvexMesh( inSource->convexMesh ) + ,MeshFlags( inSource->meshFlags ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxSphereGeometryRadius( const PxSphereGeometry* inOwner ) { return inOwner->radius; } +inline void setPxSphereGeometryRadius( PxSphereGeometry* inOwner, PxReal inData) { inOwner->radius = inData; } +PX_PHYSX_CORE_API PxSphereGeometryGeneratedInfo::PxSphereGeometryGeneratedInfo() + : Radius( "Radius", setPxSphereGeometryRadius, getPxSphereGeometryRadius ) +{} +PX_PHYSX_CORE_API PxSphereGeometryGeneratedValues::PxSphereGeometryGeneratedValues( const PxSphereGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,Radius( inSource->radius ) +{ + PX_UNUSED(inSource); +} +PX_PHYSX_CORE_API PxPlaneGeometryGeneratedInfo::PxPlaneGeometryGeneratedInfo() +{} +PX_PHYSX_CORE_API PxPlaneGeometryGeneratedValues::PxPlaneGeometryGeneratedValues( const PxPlaneGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) +{ + PX_UNUSED(inSource); +} +inline PxMeshScale getPxTriangleMeshGeometryScale( const PxTriangleMeshGeometry* inOwner ) { return inOwner->scale; } +inline void setPxTriangleMeshGeometryScale( PxTriangleMeshGeometry* inOwner, PxMeshScale inData) { inOwner->scale = inData; } +inline PxMeshGeometryFlags getPxTriangleMeshGeometryMeshFlags( const PxTriangleMeshGeometry* inOwner ) { return inOwner->meshFlags; } +inline void setPxTriangleMeshGeometryMeshFlags( PxTriangleMeshGeometry* inOwner, PxMeshGeometryFlags inData) { inOwner->meshFlags = inData; } +inline PxTriangleMesh * getPxTriangleMeshGeometryTriangleMesh( const PxTriangleMeshGeometry* inOwner ) { return inOwner->triangleMesh; } +inline void setPxTriangleMeshGeometryTriangleMesh( PxTriangleMeshGeometry* inOwner, PxTriangleMesh * inData) { inOwner->triangleMesh = inData; } +PX_PHYSX_CORE_API PxTriangleMeshGeometryGeneratedInfo::PxTriangleMeshGeometryGeneratedInfo() + : Scale( "Scale", setPxTriangleMeshGeometryScale, getPxTriangleMeshGeometryScale ) + , MeshFlags( "MeshFlags", setPxTriangleMeshGeometryMeshFlags, getPxTriangleMeshGeometryMeshFlags ) + , TriangleMesh( "TriangleMesh", setPxTriangleMeshGeometryTriangleMesh, getPxTriangleMeshGeometryTriangleMesh ) +{} +PX_PHYSX_CORE_API PxTriangleMeshGeometryGeneratedValues::PxTriangleMeshGeometryGeneratedValues( const PxTriangleMeshGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,Scale( inSource->scale ) + ,MeshFlags( inSource->meshFlags ) + ,TriangleMesh( inSource->triangleMesh ) +{ + PX_UNUSED(inSource); +} +inline PxHeightField * getPxHeightFieldGeometryHeightField( const PxHeightFieldGeometry* inOwner ) { return inOwner->heightField; } +inline void setPxHeightFieldGeometryHeightField( PxHeightFieldGeometry* inOwner, PxHeightField * inData) { inOwner->heightField = inData; } +inline PxReal getPxHeightFieldGeometryHeightScale( const PxHeightFieldGeometry* inOwner ) { return inOwner->heightScale; } +inline void setPxHeightFieldGeometryHeightScale( PxHeightFieldGeometry* inOwner, PxReal inData) { inOwner->heightScale = inData; } +inline PxReal getPxHeightFieldGeometryRowScale( const PxHeightFieldGeometry* inOwner ) { return inOwner->rowScale; } +inline void setPxHeightFieldGeometryRowScale( PxHeightFieldGeometry* inOwner, PxReal inData) { inOwner->rowScale = inData; } +inline PxReal getPxHeightFieldGeometryColumnScale( const PxHeightFieldGeometry* inOwner ) { return inOwner->columnScale; } +inline void setPxHeightFieldGeometryColumnScale( PxHeightFieldGeometry* inOwner, PxReal inData) { inOwner->columnScale = inData; } +inline PxMeshGeometryFlags getPxHeightFieldGeometryHeightFieldFlags( const PxHeightFieldGeometry* inOwner ) { return inOwner->heightFieldFlags; } +inline void setPxHeightFieldGeometryHeightFieldFlags( PxHeightFieldGeometry* inOwner, PxMeshGeometryFlags inData) { inOwner->heightFieldFlags = inData; } +PX_PHYSX_CORE_API PxHeightFieldGeometryGeneratedInfo::PxHeightFieldGeometryGeneratedInfo() + : HeightField( "HeightField", setPxHeightFieldGeometryHeightField, getPxHeightFieldGeometryHeightField ) + , HeightScale( "HeightScale", setPxHeightFieldGeometryHeightScale, getPxHeightFieldGeometryHeightScale ) + , RowScale( "RowScale", setPxHeightFieldGeometryRowScale, getPxHeightFieldGeometryRowScale ) + , ColumnScale( "ColumnScale", setPxHeightFieldGeometryColumnScale, getPxHeightFieldGeometryColumnScale ) + , HeightFieldFlags( "HeightFieldFlags", setPxHeightFieldGeometryHeightFieldFlags, getPxHeightFieldGeometryHeightFieldFlags ) +{} +PX_PHYSX_CORE_API PxHeightFieldGeometryGeneratedValues::PxHeightFieldGeometryGeneratedValues( const PxHeightFieldGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,HeightField( inSource->heightField ) + ,HeightScale( inSource->heightScale ) + ,RowScale( inSource->rowScale ) + ,ColumnScale( inSource->columnScale ) + ,HeightFieldFlags( inSource->heightFieldFlags ) +{ + PX_UNUSED(inSource); +} +inline PxU32 getPxHeightFieldDescNbRows( const PxHeightFieldDesc* inOwner ) { return inOwner->nbRows; } +inline void setPxHeightFieldDescNbRows( PxHeightFieldDesc* inOwner, PxU32 inData) { inOwner->nbRows = inData; } +inline PxU32 getPxHeightFieldDescNbColumns( const PxHeightFieldDesc* inOwner ) { return inOwner->nbColumns; } +inline void setPxHeightFieldDescNbColumns( PxHeightFieldDesc* inOwner, PxU32 inData) { inOwner->nbColumns = inData; } +inline PxHeightFieldFormat::Enum getPxHeightFieldDescFormat( const PxHeightFieldDesc* inOwner ) { return inOwner->format; } +inline void setPxHeightFieldDescFormat( PxHeightFieldDesc* inOwner, PxHeightFieldFormat::Enum inData) { inOwner->format = inData; } +inline PxStridedData getPxHeightFieldDescSamples( const PxHeightFieldDesc* inOwner ) { return inOwner->samples; } +inline void setPxHeightFieldDescSamples( PxHeightFieldDesc* inOwner, PxStridedData inData) { inOwner->samples = inData; } +inline PxReal getPxHeightFieldDescConvexEdgeThreshold( const PxHeightFieldDesc* inOwner ) { return inOwner->convexEdgeThreshold; } +inline void setPxHeightFieldDescConvexEdgeThreshold( PxHeightFieldDesc* inOwner, PxReal inData) { inOwner->convexEdgeThreshold = inData; } +inline PxHeightFieldFlags getPxHeightFieldDescFlags( const PxHeightFieldDesc* inOwner ) { return inOwner->flags; } +inline void setPxHeightFieldDescFlags( PxHeightFieldDesc* inOwner, PxHeightFieldFlags inData) { inOwner->flags = inData; } +PX_PHYSX_CORE_API PxHeightFieldDescGeneratedInfo::PxHeightFieldDescGeneratedInfo() + : NbRows( "NbRows", setPxHeightFieldDescNbRows, getPxHeightFieldDescNbRows ) + , NbColumns( "NbColumns", setPxHeightFieldDescNbColumns, getPxHeightFieldDescNbColumns ) + , Format( "Format", setPxHeightFieldDescFormat, getPxHeightFieldDescFormat ) + , Samples( "Samples", setPxHeightFieldDescSamples, getPxHeightFieldDescSamples ) + , ConvexEdgeThreshold( "ConvexEdgeThreshold", setPxHeightFieldDescConvexEdgeThreshold, getPxHeightFieldDescConvexEdgeThreshold ) + , Flags( "Flags", setPxHeightFieldDescFlags, getPxHeightFieldDescFlags ) +{} +PX_PHYSX_CORE_API PxHeightFieldDescGeneratedValues::PxHeightFieldDescGeneratedValues( const PxHeightFieldDesc* inSource ) + :NbRows( inSource->nbRows ) + ,NbColumns( inSource->nbColumns ) + ,Format( inSource->format ) + ,Samples( inSource->samples ) + ,ConvexEdgeThreshold( inSource->convexEdgeThreshold ) + ,Flags( inSource->flags ) +{ + PX_UNUSED(inSource); +} +void setPxArticulationJointReducedCoordinate_JointType( PxArticulationJointReducedCoordinate* inObj, PxArticulationJointType::Enum inArg){ inObj->setJointType( inArg ); } +PxArticulationJointType::Enum getPxArticulationJointReducedCoordinate_JointType( const PxArticulationJointReducedCoordinate* inObj ) { return inObj->getJointType(); } +void setPxArticulationJointReducedCoordinate_Motion( PxArticulationJointReducedCoordinate* inObj, PxArticulationAxis::Enum inIndex, PxArticulationMotion::Enum inArg ){ inObj->setMotion( inIndex, inArg ); } +PxArticulationMotion::Enum getPxArticulationJointReducedCoordinate_Motion( const PxArticulationJointReducedCoordinate* inObj, PxArticulationAxis::Enum inIndex ) { return inObj->getMotion( inIndex ); } +void setPxArticulationJointReducedCoordinate_FrictionCoefficient( PxArticulationJointReducedCoordinate* inObj, const PxReal inArg){ inObj->setFrictionCoefficient( inArg ); } +PxReal getPxArticulationJointReducedCoordinate_FrictionCoefficient( const PxArticulationJointReducedCoordinate* inObj ) { return inObj->getFrictionCoefficient(); } +const char * getPxArticulationJointReducedCoordinate_ConcreteTypeName( const PxArticulationJointReducedCoordinate* inObj ) { return inObj->getConcreteTypeName(); } +void setPxArticulationJointReducedCoordinate_MaxJointVelocity( PxArticulationJointReducedCoordinate* inObj, const PxReal inArg){ inObj->setMaxJointVelocity( inArg ); } +PxReal getPxArticulationJointReducedCoordinate_MaxJointVelocity( const PxArticulationJointReducedCoordinate* inObj ) { return inObj->getMaxJointVelocity(); } +PX_PHYSX_CORE_API PxArticulationJointReducedCoordinateGeneratedInfo::PxArticulationJointReducedCoordinateGeneratedInfo() + : JointType( "JointType", setPxArticulationJointReducedCoordinate_JointType, getPxArticulationJointReducedCoordinate_JointType) + , Motion( "Motion", setPxArticulationJointReducedCoordinate_Motion, getPxArticulationJointReducedCoordinate_Motion) + , FrictionCoefficient( "FrictionCoefficient", setPxArticulationJointReducedCoordinate_FrictionCoefficient, getPxArticulationJointReducedCoordinate_FrictionCoefficient) + , ConcreteTypeName( "ConcreteTypeName", getPxArticulationJointReducedCoordinate_ConcreteTypeName) + , MaxJointVelocity( "MaxJointVelocity", setPxArticulationJointReducedCoordinate_MaxJointVelocity, getPxArticulationJointReducedCoordinate_MaxJointVelocity) +{} +PX_PHYSX_CORE_API PxArticulationJointReducedCoordinateGeneratedValues::PxArticulationJointReducedCoordinateGeneratedValues( const PxArticulationJointReducedCoordinate* inSource ) + :PxArticulationJointBaseGeneratedValues( inSource ) + ,JointType( getPxArticulationJointReducedCoordinate_JointType( inSource ) ) + ,FrictionCoefficient( getPxArticulationJointReducedCoordinate_FrictionCoefficient( inSource ) ) + ,ConcreteTypeName( getPxArticulationJointReducedCoordinate_ConcreteTypeName( inSource ) ) + ,MaxJointVelocity( getPxArticulationJointReducedCoordinate_MaxJointVelocity( inSource ) ) +{ + PX_UNUSED(inSource); + for ( PxU32 idx = 0; idx < static_cast( physx::PxArticulationAxis::eCOUNT ); ++idx ) + Motion[idx] = getPxArticulationJointReducedCoordinate_Motion( inSource, static_cast< PxArticulationAxis::Enum >( idx ) ); +} +PxSceneFlags getPxScene_Flags( const PxScene* inObj ) { return inObj->getFlags(); } +void setPxScene_Limits( PxScene* inObj, const PxSceneLimits & inArg){ inObj->setLimits( inArg ); } +PxSceneLimits getPxScene_Limits( const PxScene* inObj ) { return inObj->getLimits(); } +PxU32 getPxScene_Timestamp( const PxScene* inObj ) { return inObj->getTimestamp(); } +PxU32 getPxScene_Actors( const PxScene* inObj, PxActorTypeFlags inFilter, PxActor ** outBuffer, PxU32 inBufSize ) { return inObj->getActors( inFilter, outBuffer, inBufSize ); } +PxU32 getNbPxScene_Actors( const PxScene* inObj, PxActorTypeFlags inFilter ) { return inObj->getNbActors( inFilter ); } +PxU32 getPxScene_Articulations( const PxScene* inObj, PxArticulationBase ** outBuffer, PxU32 inBufSize ) { return inObj->getArticulations( outBuffer, inBufSize ); } +PxU32 getNbPxScene_Articulations( const PxScene* inObj ) { return inObj->getNbArticulations( ); } +PxU32 getPxScene_Constraints( const PxScene* inObj, PxConstraint ** outBuffer, PxU32 inBufSize ) { return inObj->getConstraints( outBuffer, inBufSize ); } +PxU32 getNbPxScene_Constraints( const PxScene* inObj ) { return inObj->getNbConstraints( ); } +PxU32 getPxScene_Aggregates( const PxScene* inObj, PxAggregate ** outBuffer, PxU32 inBufSize ) { return inObj->getAggregates( outBuffer, inBufSize ); } +PxU32 getNbPxScene_Aggregates( const PxScene* inObj ) { return inObj->getNbAggregates( ); } +PxCpuDispatcher * getPxScene_CpuDispatcher( const PxScene* inObj ) { return inObj->getCpuDispatcher(); } +PxGpuDispatcher * getPxScene_GpuDispatcher( const PxScene* inObj ) { return inObj->getGpuDispatcher(); } +void setPxScene_SimulationEventCallback( PxScene* inObj, PxSimulationEventCallback * inArg){ inObj->setSimulationEventCallback( inArg ); } +PxSimulationEventCallback * getPxScene_SimulationEventCallback( const PxScene* inObj ) { return inObj->getSimulationEventCallback(); } +void setPxScene_ContactModifyCallback( PxScene* inObj, PxContactModifyCallback * inArg){ inObj->setContactModifyCallback( inArg ); } +PxContactModifyCallback * getPxScene_ContactModifyCallback( const PxScene* inObj ) { return inObj->getContactModifyCallback(); } +void setPxScene_CCDContactModifyCallback( PxScene* inObj, PxCCDContactModifyCallback * inArg){ inObj->setCCDContactModifyCallback( inArg ); } +PxCCDContactModifyCallback * getPxScene_CCDContactModifyCallback( const PxScene* inObj ) { return inObj->getCCDContactModifyCallback(); } +void setPxScene_BroadPhaseCallback( PxScene* inObj, PxBroadPhaseCallback * inArg){ inObj->setBroadPhaseCallback( inArg ); } +PxBroadPhaseCallback * getPxScene_BroadPhaseCallback( const PxScene* inObj ) { return inObj->getBroadPhaseCallback(); } +PxU32 getPxScene_FilterShaderDataSize( const PxScene* inObj ) { return inObj->getFilterShaderDataSize(); } +PxSimulationFilterShader getPxScene_FilterShader( const PxScene* inObj ) { return inObj->getFilterShader(); } +PxSimulationFilterCallback * getPxScene_FilterCallback( const PxScene* inObj ) { return inObj->getFilterCallback(); } +void setPxScene_Gravity( PxScene* inObj, const PxVec3 & inArg){ inObj->setGravity( inArg ); } +PxVec3 getPxScene_Gravity( const PxScene* inObj ) { return inObj->getGravity(); } +void setPxScene_BounceThresholdVelocity( PxScene* inObj, const PxReal inArg){ inObj->setBounceThresholdVelocity( inArg ); } +PxReal getPxScene_BounceThresholdVelocity( const PxScene* inObj ) { return inObj->getBounceThresholdVelocity(); } +void setPxScene_CCDMaxPasses( PxScene* inObj, PxU32 inArg){ inObj->setCCDMaxPasses( inArg ); } +PxU32 getPxScene_CCDMaxPasses( const PxScene* inObj ) { return inObj->getCCDMaxPasses(); } +PxReal getPxScene_FrictionOffsetThreshold( const PxScene* inObj ) { return inObj->getFrictionOffsetThreshold(); } +void setPxScene_FrictionType( PxScene* inObj, PxFrictionType::Enum inArg){ inObj->setFrictionType( inArg ); } +PxFrictionType::Enum getPxScene_FrictionType( const PxScene* inObj ) { return inObj->getFrictionType(); } +void setPxScene_VisualizationCullingBox( PxScene* inObj, const PxBounds3 & inArg){ inObj->setVisualizationCullingBox( inArg ); } +PxBounds3 getPxScene_VisualizationCullingBox( const PxScene* inObj ) { return inObj->getVisualizationCullingBox(); } +PxPruningStructureType::Enum getPxScene_StaticStructure( const PxScene* inObj ) { return inObj->getStaticStructure(); } +PxPruningStructureType::Enum getPxScene_DynamicStructure( const PxScene* inObj ) { return inObj->getDynamicStructure(); } +void setPxScene_DynamicTreeRebuildRateHint( PxScene* inObj, PxU32 inArg){ inObj->setDynamicTreeRebuildRateHint( inArg ); } +PxU32 getPxScene_DynamicTreeRebuildRateHint( const PxScene* inObj ) { return inObj->getDynamicTreeRebuildRateHint(); } +void setPxScene_SceneQueryUpdateMode( PxScene* inObj, PxSceneQueryUpdateMode::Enum inArg){ inObj->setSceneQueryUpdateMode( inArg ); } +PxSceneQueryUpdateMode::Enum getPxScene_SceneQueryUpdateMode( const PxScene* inObj ) { return inObj->getSceneQueryUpdateMode(); } +PxU32 getPxScene_SceneQueryStaticTimestamp( const PxScene* inObj ) { return inObj->getSceneQueryStaticTimestamp(); } +PxBroadPhaseType::Enum getPxScene_BroadPhaseType( const PxScene* inObj ) { return inObj->getBroadPhaseType(); } +PxU32 getPxScene_BroadPhaseRegions( const PxScene* inObj, PxBroadPhaseRegionInfo* outBuffer, PxU32 inBufSize ) { return inObj->getBroadPhaseRegions( outBuffer, inBufSize ); } +PxU32 getNbPxScene_BroadPhaseRegions( const PxScene* inObj ) { return inObj->getNbBroadPhaseRegions( ); } +PxTaskManager * getPxScene_TaskManager( const PxScene* inObj ) { return inObj->getTaskManager(); } +void setPxScene_NbContactDataBlocks( PxScene* inObj, PxU32 inArg){ inObj->setNbContactDataBlocks( inArg ); } +PxU32 getPxScene_MaxNbContactDataBlocksUsed( const PxScene* inObj ) { return inObj->getMaxNbContactDataBlocksUsed(); } +PxU32 getPxScene_ContactReportStreamBufferSize( const PxScene* inObj ) { return inObj->getContactReportStreamBufferSize(); } +void setPxScene_SolverBatchSize( PxScene* inObj, PxU32 inArg){ inObj->setSolverBatchSize( inArg ); } +PxU32 getPxScene_SolverBatchSize( const PxScene* inObj ) { return inObj->getSolverBatchSize(); } +PxReal getPxScene_WakeCounterResetValue( const PxScene* inObj ) { return inObj->getWakeCounterResetValue(); } +inline void * getPxSceneUserData( const PxScene* inOwner ) { return inOwner->userData; } +inline void setPxSceneUserData( PxScene* inOwner, void * inData) { inOwner->userData = inData; } +PX_PHYSX_CORE_API PxSceneGeneratedInfo::PxSceneGeneratedInfo() + : Flags( "Flags", getPxScene_Flags) + , Limits( "Limits", setPxScene_Limits, getPxScene_Limits) + , Timestamp( "Timestamp", getPxScene_Timestamp) + , Actors( "Actors", getPxScene_Actors, getNbPxScene_Actors ) + , Articulations( "Articulations", getPxScene_Articulations, getNbPxScene_Articulations ) + , Constraints( "Constraints", getPxScene_Constraints, getNbPxScene_Constraints ) + , Aggregates( "Aggregates", getPxScene_Aggregates, getNbPxScene_Aggregates ) + , CpuDispatcher( "CpuDispatcher", getPxScene_CpuDispatcher) + , GpuDispatcher( "GpuDispatcher", getPxScene_GpuDispatcher) + , SimulationEventCallback( "SimulationEventCallback", setPxScene_SimulationEventCallback, getPxScene_SimulationEventCallback) + , ContactModifyCallback( "ContactModifyCallback", setPxScene_ContactModifyCallback, getPxScene_ContactModifyCallback) + , CCDContactModifyCallback( "CCDContactModifyCallback", setPxScene_CCDContactModifyCallback, getPxScene_CCDContactModifyCallback) + , BroadPhaseCallback( "BroadPhaseCallback", setPxScene_BroadPhaseCallback, getPxScene_BroadPhaseCallback) + , FilterShaderDataSize( "FilterShaderDataSize", getPxScene_FilterShaderDataSize) + , FilterShader( "FilterShader", getPxScene_FilterShader) + , FilterCallback( "FilterCallback", getPxScene_FilterCallback) + , Gravity( "Gravity", setPxScene_Gravity, getPxScene_Gravity) + , BounceThresholdVelocity( "BounceThresholdVelocity", setPxScene_BounceThresholdVelocity, getPxScene_BounceThresholdVelocity) + , CCDMaxPasses( "CCDMaxPasses", setPxScene_CCDMaxPasses, getPxScene_CCDMaxPasses) + , FrictionOffsetThreshold( "FrictionOffsetThreshold", getPxScene_FrictionOffsetThreshold) + , FrictionType( "FrictionType", setPxScene_FrictionType, getPxScene_FrictionType) + , VisualizationCullingBox( "VisualizationCullingBox", setPxScene_VisualizationCullingBox, getPxScene_VisualizationCullingBox) + , StaticStructure( "StaticStructure", getPxScene_StaticStructure) + , DynamicStructure( "DynamicStructure", getPxScene_DynamicStructure) + , DynamicTreeRebuildRateHint( "DynamicTreeRebuildRateHint", setPxScene_DynamicTreeRebuildRateHint, getPxScene_DynamicTreeRebuildRateHint) + , SceneQueryUpdateMode( "SceneQueryUpdateMode", setPxScene_SceneQueryUpdateMode, getPxScene_SceneQueryUpdateMode) + , SceneQueryStaticTimestamp( "SceneQueryStaticTimestamp", getPxScene_SceneQueryStaticTimestamp) + , BroadPhaseType( "BroadPhaseType", getPxScene_BroadPhaseType) + , BroadPhaseRegions( "BroadPhaseRegions", getPxScene_BroadPhaseRegions, getNbPxScene_BroadPhaseRegions ) + , TaskManager( "TaskManager", getPxScene_TaskManager) + , NbContactDataBlocks( "NbContactDataBlocks", setPxScene_NbContactDataBlocks) + , MaxNbContactDataBlocksUsed( "MaxNbContactDataBlocksUsed", getPxScene_MaxNbContactDataBlocksUsed) + , ContactReportStreamBufferSize( "ContactReportStreamBufferSize", getPxScene_ContactReportStreamBufferSize) + , SolverBatchSize( "SolverBatchSize", setPxScene_SolverBatchSize, getPxScene_SolverBatchSize) + , WakeCounterResetValue( "WakeCounterResetValue", getPxScene_WakeCounterResetValue) + , UserData( "UserData", setPxSceneUserData, getPxSceneUserData ) +{} +PX_PHYSX_CORE_API PxSceneGeneratedValues::PxSceneGeneratedValues( const PxScene* inSource ) + :Flags( getPxScene_Flags( inSource ) ) + ,Limits( getPxScene_Limits( inSource ) ) + ,Timestamp( getPxScene_Timestamp( inSource ) ) + ,CpuDispatcher( getPxScene_CpuDispatcher( inSource ) ) + ,GpuDispatcher( getPxScene_GpuDispatcher( inSource ) ) + ,SimulationEventCallback( getPxScene_SimulationEventCallback( inSource ) ) + ,ContactModifyCallback( getPxScene_ContactModifyCallback( inSource ) ) + ,CCDContactModifyCallback( getPxScene_CCDContactModifyCallback( inSource ) ) + ,BroadPhaseCallback( getPxScene_BroadPhaseCallback( inSource ) ) + ,FilterShaderDataSize( getPxScene_FilterShaderDataSize( inSource ) ) + ,FilterShader( getPxScene_FilterShader( inSource ) ) + ,FilterCallback( getPxScene_FilterCallback( inSource ) ) + ,Gravity( getPxScene_Gravity( inSource ) ) + ,BounceThresholdVelocity( getPxScene_BounceThresholdVelocity( inSource ) ) + ,CCDMaxPasses( getPxScene_CCDMaxPasses( inSource ) ) + ,FrictionOffsetThreshold( getPxScene_FrictionOffsetThreshold( inSource ) ) + ,FrictionType( getPxScene_FrictionType( inSource ) ) + ,VisualizationCullingBox( getPxScene_VisualizationCullingBox( inSource ) ) + ,StaticStructure( getPxScene_StaticStructure( inSource ) ) + ,DynamicStructure( getPxScene_DynamicStructure( inSource ) ) + ,DynamicTreeRebuildRateHint( getPxScene_DynamicTreeRebuildRateHint( inSource ) ) + ,SceneQueryUpdateMode( getPxScene_SceneQueryUpdateMode( inSource ) ) + ,SceneQueryStaticTimestamp( getPxScene_SceneQueryStaticTimestamp( inSource ) ) + ,BroadPhaseType( getPxScene_BroadPhaseType( inSource ) ) + ,TaskManager( getPxScene_TaskManager( inSource ) ) + ,MaxNbContactDataBlocksUsed( getPxScene_MaxNbContactDataBlocksUsed( inSource ) ) + ,ContactReportStreamBufferSize( getPxScene_ContactReportStreamBufferSize( inSource ) ) + ,SolverBatchSize( getPxScene_SolverBatchSize( inSource ) ) + ,WakeCounterResetValue( getPxScene_WakeCounterResetValue( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); + inSource->getSimulationStatistics(SimulationStatistics); +} +inline PxU32 getPxSceneLimitsMaxNbActors( const PxSceneLimits* inOwner ) { return inOwner->maxNbActors; } +inline void setPxSceneLimitsMaxNbActors( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbActors = inData; } +inline PxU32 getPxSceneLimitsMaxNbBodies( const PxSceneLimits* inOwner ) { return inOwner->maxNbBodies; } +inline void setPxSceneLimitsMaxNbBodies( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbBodies = inData; } +inline PxU32 getPxSceneLimitsMaxNbStaticShapes( const PxSceneLimits* inOwner ) { return inOwner->maxNbStaticShapes; } +inline void setPxSceneLimitsMaxNbStaticShapes( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbStaticShapes = inData; } +inline PxU32 getPxSceneLimitsMaxNbDynamicShapes( const PxSceneLimits* inOwner ) { return inOwner->maxNbDynamicShapes; } +inline void setPxSceneLimitsMaxNbDynamicShapes( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbDynamicShapes = inData; } +inline PxU32 getPxSceneLimitsMaxNbAggregates( const PxSceneLimits* inOwner ) { return inOwner->maxNbAggregates; } +inline void setPxSceneLimitsMaxNbAggregates( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbAggregates = inData; } +inline PxU32 getPxSceneLimitsMaxNbConstraints( const PxSceneLimits* inOwner ) { return inOwner->maxNbConstraints; } +inline void setPxSceneLimitsMaxNbConstraints( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbConstraints = inData; } +inline PxU32 getPxSceneLimitsMaxNbRegions( const PxSceneLimits* inOwner ) { return inOwner->maxNbRegions; } +inline void setPxSceneLimitsMaxNbRegions( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbRegions = inData; } +inline PxU32 getPxSceneLimitsMaxNbBroadPhaseOverlaps( const PxSceneLimits* inOwner ) { return inOwner->maxNbBroadPhaseOverlaps; } +inline void setPxSceneLimitsMaxNbBroadPhaseOverlaps( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbBroadPhaseOverlaps = inData; } +PX_PHYSX_CORE_API PxSceneLimitsGeneratedInfo::PxSceneLimitsGeneratedInfo() + : MaxNbActors( "MaxNbActors", setPxSceneLimitsMaxNbActors, getPxSceneLimitsMaxNbActors ) + , MaxNbBodies( "MaxNbBodies", setPxSceneLimitsMaxNbBodies, getPxSceneLimitsMaxNbBodies ) + , MaxNbStaticShapes( "MaxNbStaticShapes", setPxSceneLimitsMaxNbStaticShapes, getPxSceneLimitsMaxNbStaticShapes ) + , MaxNbDynamicShapes( "MaxNbDynamicShapes", setPxSceneLimitsMaxNbDynamicShapes, getPxSceneLimitsMaxNbDynamicShapes ) + , MaxNbAggregates( "MaxNbAggregates", setPxSceneLimitsMaxNbAggregates, getPxSceneLimitsMaxNbAggregates ) + , MaxNbConstraints( "MaxNbConstraints", setPxSceneLimitsMaxNbConstraints, getPxSceneLimitsMaxNbConstraints ) + , MaxNbRegions( "MaxNbRegions", setPxSceneLimitsMaxNbRegions, getPxSceneLimitsMaxNbRegions ) + , MaxNbBroadPhaseOverlaps( "MaxNbBroadPhaseOverlaps", setPxSceneLimitsMaxNbBroadPhaseOverlaps, getPxSceneLimitsMaxNbBroadPhaseOverlaps ) +{} +PX_PHYSX_CORE_API PxSceneLimitsGeneratedValues::PxSceneLimitsGeneratedValues( const PxSceneLimits* inSource ) + :MaxNbActors( inSource->maxNbActors ) + ,MaxNbBodies( inSource->maxNbBodies ) + ,MaxNbStaticShapes( inSource->maxNbStaticShapes ) + ,MaxNbDynamicShapes( inSource->maxNbDynamicShapes ) + ,MaxNbAggregates( inSource->maxNbAggregates ) + ,MaxNbConstraints( inSource->maxNbConstraints ) + ,MaxNbRegions( inSource->maxNbRegions ) + ,MaxNbBroadPhaseOverlaps( inSource->maxNbBroadPhaseOverlaps ) +{ + PX_UNUSED(inSource); +} +inline PxU32 getPxgDynamicsMemoryConfigConstraintBufferCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->constraintBufferCapacity; } +inline void setPxgDynamicsMemoryConfigConstraintBufferCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->constraintBufferCapacity = inData; } +inline PxU32 getPxgDynamicsMemoryConfigContactBufferCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->contactBufferCapacity; } +inline void setPxgDynamicsMemoryConfigContactBufferCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->contactBufferCapacity = inData; } +inline PxU32 getPxgDynamicsMemoryConfigTempBufferCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->tempBufferCapacity; } +inline void setPxgDynamicsMemoryConfigTempBufferCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->tempBufferCapacity = inData; } +inline PxU32 getPxgDynamicsMemoryConfigContactStreamSize( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->contactStreamSize; } +inline void setPxgDynamicsMemoryConfigContactStreamSize( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->contactStreamSize = inData; } +inline PxU32 getPxgDynamicsMemoryConfigPatchStreamSize( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->patchStreamSize; } +inline void setPxgDynamicsMemoryConfigPatchStreamSize( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->patchStreamSize = inData; } +inline PxU32 getPxgDynamicsMemoryConfigForceStreamCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->forceStreamCapacity; } +inline void setPxgDynamicsMemoryConfigForceStreamCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->forceStreamCapacity = inData; } +inline PxU32 getPxgDynamicsMemoryConfigHeapCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->heapCapacity; } +inline void setPxgDynamicsMemoryConfigHeapCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->heapCapacity = inData; } +inline PxU32 getPxgDynamicsMemoryConfigFoundLostPairsCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->foundLostPairsCapacity; } +inline void setPxgDynamicsMemoryConfigFoundLostPairsCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->foundLostPairsCapacity = inData; } +PX_PHYSX_CORE_API PxgDynamicsMemoryConfigGeneratedInfo::PxgDynamicsMemoryConfigGeneratedInfo() + : ConstraintBufferCapacity( "ConstraintBufferCapacity", setPxgDynamicsMemoryConfigConstraintBufferCapacity, getPxgDynamicsMemoryConfigConstraintBufferCapacity ) + , ContactBufferCapacity( "ContactBufferCapacity", setPxgDynamicsMemoryConfigContactBufferCapacity, getPxgDynamicsMemoryConfigContactBufferCapacity ) + , TempBufferCapacity( "TempBufferCapacity", setPxgDynamicsMemoryConfigTempBufferCapacity, getPxgDynamicsMemoryConfigTempBufferCapacity ) + , ContactStreamSize( "ContactStreamSize", setPxgDynamicsMemoryConfigContactStreamSize, getPxgDynamicsMemoryConfigContactStreamSize ) + , PatchStreamSize( "PatchStreamSize", setPxgDynamicsMemoryConfigPatchStreamSize, getPxgDynamicsMemoryConfigPatchStreamSize ) + , ForceStreamCapacity( "ForceStreamCapacity", setPxgDynamicsMemoryConfigForceStreamCapacity, getPxgDynamicsMemoryConfigForceStreamCapacity ) + , HeapCapacity( "HeapCapacity", setPxgDynamicsMemoryConfigHeapCapacity, getPxgDynamicsMemoryConfigHeapCapacity ) + , FoundLostPairsCapacity( "FoundLostPairsCapacity", setPxgDynamicsMemoryConfigFoundLostPairsCapacity, getPxgDynamicsMemoryConfigFoundLostPairsCapacity ) +{} +PX_PHYSX_CORE_API PxgDynamicsMemoryConfigGeneratedValues::PxgDynamicsMemoryConfigGeneratedValues( const PxgDynamicsMemoryConfig* inSource ) + :ConstraintBufferCapacity( inSource->constraintBufferCapacity ) + ,ContactBufferCapacity( inSource->contactBufferCapacity ) + ,TempBufferCapacity( inSource->tempBufferCapacity ) + ,ContactStreamSize( inSource->contactStreamSize ) + ,PatchStreamSize( inSource->patchStreamSize ) + ,ForceStreamCapacity( inSource->forceStreamCapacity ) + ,HeapCapacity( inSource->heapCapacity ) + ,FoundLostPairsCapacity( inSource->foundLostPairsCapacity ) +{ + PX_UNUSED(inSource); +} +void setPxSceneDesc_ToDefault( PxSceneDesc* inObj, const PxTolerancesScale & inArg){ inObj->setToDefault( inArg ); } +inline PxVec3 getPxSceneDescGravity( const PxSceneDesc* inOwner ) { return inOwner->gravity; } +inline void setPxSceneDescGravity( PxSceneDesc* inOwner, PxVec3 inData) { inOwner->gravity = inData; } +inline PxSimulationEventCallback * getPxSceneDescSimulationEventCallback( const PxSceneDesc* inOwner ) { return inOwner->simulationEventCallback; } +inline void setPxSceneDescSimulationEventCallback( PxSceneDesc* inOwner, PxSimulationEventCallback * inData) { inOwner->simulationEventCallback = inData; } +inline PxContactModifyCallback * getPxSceneDescContactModifyCallback( const PxSceneDesc* inOwner ) { return inOwner->contactModifyCallback; } +inline void setPxSceneDescContactModifyCallback( PxSceneDesc* inOwner, PxContactModifyCallback * inData) { inOwner->contactModifyCallback = inData; } +inline PxCCDContactModifyCallback * getPxSceneDescCcdContactModifyCallback( const PxSceneDesc* inOwner ) { return inOwner->ccdContactModifyCallback; } +inline void setPxSceneDescCcdContactModifyCallback( PxSceneDesc* inOwner, PxCCDContactModifyCallback * inData) { inOwner->ccdContactModifyCallback = inData; } +inline const void * getPxSceneDescFilterShaderData( const PxSceneDesc* inOwner ) { return inOwner->filterShaderData; } +inline void setPxSceneDescFilterShaderData( PxSceneDesc* inOwner, const void * inData) { inOwner->filterShaderData = inData; } +inline PxU32 getPxSceneDescFilterShaderDataSize( const PxSceneDesc* inOwner ) { return inOwner->filterShaderDataSize; } +inline void setPxSceneDescFilterShaderDataSize( PxSceneDesc* inOwner, PxU32 inData) { inOwner->filterShaderDataSize = inData; } +inline PxSimulationFilterShader getPxSceneDescFilterShader( const PxSceneDesc* inOwner ) { return inOwner->filterShader; } +inline void setPxSceneDescFilterShader( PxSceneDesc* inOwner, PxSimulationFilterShader inData) { inOwner->filterShader = inData; } +inline PxSimulationFilterCallback * getPxSceneDescFilterCallback( const PxSceneDesc* inOwner ) { return inOwner->filterCallback; } +inline void setPxSceneDescFilterCallback( PxSceneDesc* inOwner, PxSimulationFilterCallback * inData) { inOwner->filterCallback = inData; } +inline PxPairFilteringMode::Enum getPxSceneDescKineKineFilteringMode( const PxSceneDesc* inOwner ) { return inOwner->kineKineFilteringMode; } +inline void setPxSceneDescKineKineFilteringMode( PxSceneDesc* inOwner, PxPairFilteringMode::Enum inData) { inOwner->kineKineFilteringMode = inData; } +inline PxPairFilteringMode::Enum getPxSceneDescStaticKineFilteringMode( const PxSceneDesc* inOwner ) { return inOwner->staticKineFilteringMode; } +inline void setPxSceneDescStaticKineFilteringMode( PxSceneDesc* inOwner, PxPairFilteringMode::Enum inData) { inOwner->staticKineFilteringMode = inData; } +inline PxBroadPhaseType::Enum getPxSceneDescBroadPhaseType( const PxSceneDesc* inOwner ) { return inOwner->broadPhaseType; } +inline void setPxSceneDescBroadPhaseType( PxSceneDesc* inOwner, PxBroadPhaseType::Enum inData) { inOwner->broadPhaseType = inData; } +inline PxBroadPhaseCallback * getPxSceneDescBroadPhaseCallback( const PxSceneDesc* inOwner ) { return inOwner->broadPhaseCallback; } +inline void setPxSceneDescBroadPhaseCallback( PxSceneDesc* inOwner, PxBroadPhaseCallback * inData) { inOwner->broadPhaseCallback = inData; } +inline PxSceneLimits getPxSceneDescLimits( const PxSceneDesc* inOwner ) { return inOwner->limits; } +inline void setPxSceneDescLimits( PxSceneDesc* inOwner, PxSceneLimits inData) { inOwner->limits = inData; } +inline PxFrictionType::Enum getPxSceneDescFrictionType( const PxSceneDesc* inOwner ) { return inOwner->frictionType; } +inline void setPxSceneDescFrictionType( PxSceneDesc* inOwner, PxFrictionType::Enum inData) { inOwner->frictionType = inData; } +inline PxSolverType::Enum getPxSceneDescSolverType( const PxSceneDesc* inOwner ) { return inOwner->solverType; } +inline void setPxSceneDescSolverType( PxSceneDesc* inOwner, PxSolverType::Enum inData) { inOwner->solverType = inData; } +inline PxReal getPxSceneDescBounceThresholdVelocity( const PxSceneDesc* inOwner ) { return inOwner->bounceThresholdVelocity; } +inline void setPxSceneDescBounceThresholdVelocity( PxSceneDesc* inOwner, PxReal inData) { inOwner->bounceThresholdVelocity = inData; } +inline PxReal getPxSceneDescFrictionOffsetThreshold( const PxSceneDesc* inOwner ) { return inOwner->frictionOffsetThreshold; } +inline void setPxSceneDescFrictionOffsetThreshold( PxSceneDesc* inOwner, PxReal inData) { inOwner->frictionOffsetThreshold = inData; } +inline PxReal getPxSceneDescCcdMaxSeparation( const PxSceneDesc* inOwner ) { return inOwner->ccdMaxSeparation; } +inline void setPxSceneDescCcdMaxSeparation( PxSceneDesc* inOwner, PxReal inData) { inOwner->ccdMaxSeparation = inData; } +inline PxReal getPxSceneDescSolverOffsetSlop( const PxSceneDesc* inOwner ) { return inOwner->solverOffsetSlop; } +inline void setPxSceneDescSolverOffsetSlop( PxSceneDesc* inOwner, PxReal inData) { inOwner->solverOffsetSlop = inData; } +inline PxSceneFlags getPxSceneDescFlags( const PxSceneDesc* inOwner ) { return inOwner->flags; } +inline void setPxSceneDescFlags( PxSceneDesc* inOwner, PxSceneFlags inData) { inOwner->flags = inData; } +inline PxCpuDispatcher * getPxSceneDescCpuDispatcher( const PxSceneDesc* inOwner ) { return inOwner->cpuDispatcher; } +inline void setPxSceneDescCpuDispatcher( PxSceneDesc* inOwner, PxCpuDispatcher * inData) { inOwner->cpuDispatcher = inData; } +inline PxGpuDispatcher * getPxSceneDescGpuDispatcher( const PxSceneDesc* inOwner ) { return inOwner->gpuDispatcher; } +inline void setPxSceneDescGpuDispatcher( PxSceneDesc* inOwner, PxGpuDispatcher * inData) { inOwner->gpuDispatcher = inData; } +inline PxPruningStructureType::Enum getPxSceneDescStaticStructure( const PxSceneDesc* inOwner ) { return inOwner->staticStructure; } +inline void setPxSceneDescStaticStructure( PxSceneDesc* inOwner, PxPruningStructureType::Enum inData) { inOwner->staticStructure = inData; } +inline PxPruningStructureType::Enum getPxSceneDescDynamicStructure( const PxSceneDesc* inOwner ) { return inOwner->dynamicStructure; } +inline void setPxSceneDescDynamicStructure( PxSceneDesc* inOwner, PxPruningStructureType::Enum inData) { inOwner->dynamicStructure = inData; } +inline PxU32 getPxSceneDescDynamicTreeRebuildRateHint( const PxSceneDesc* inOwner ) { return inOwner->dynamicTreeRebuildRateHint; } +inline void setPxSceneDescDynamicTreeRebuildRateHint( PxSceneDesc* inOwner, PxU32 inData) { inOwner->dynamicTreeRebuildRateHint = inData; } +inline PxSceneQueryUpdateMode::Enum getPxSceneDescSceneQueryUpdateMode( const PxSceneDesc* inOwner ) { return inOwner->sceneQueryUpdateMode; } +inline void setPxSceneDescSceneQueryUpdateMode( PxSceneDesc* inOwner, PxSceneQueryUpdateMode::Enum inData) { inOwner->sceneQueryUpdateMode = inData; } +inline void * getPxSceneDescUserData( const PxSceneDesc* inOwner ) { return inOwner->userData; } +inline void setPxSceneDescUserData( PxSceneDesc* inOwner, void * inData) { inOwner->userData = inData; } +inline PxU32 getPxSceneDescSolverBatchSize( const PxSceneDesc* inOwner ) { return inOwner->solverBatchSize; } +inline void setPxSceneDescSolverBatchSize( PxSceneDesc* inOwner, PxU32 inData) { inOwner->solverBatchSize = inData; } +inline PxU32 getPxSceneDescNbContactDataBlocks( const PxSceneDesc* inOwner ) { return inOwner->nbContactDataBlocks; } +inline void setPxSceneDescNbContactDataBlocks( PxSceneDesc* inOwner, PxU32 inData) { inOwner->nbContactDataBlocks = inData; } +inline PxU32 getPxSceneDescMaxNbContactDataBlocks( const PxSceneDesc* inOwner ) { return inOwner->maxNbContactDataBlocks; } +inline void setPxSceneDescMaxNbContactDataBlocks( PxSceneDesc* inOwner, PxU32 inData) { inOwner->maxNbContactDataBlocks = inData; } +inline PxReal getPxSceneDescMaxBiasCoefficient( const PxSceneDesc* inOwner ) { return inOwner->maxBiasCoefficient; } +inline void setPxSceneDescMaxBiasCoefficient( PxSceneDesc* inOwner, PxReal inData) { inOwner->maxBiasCoefficient = inData; } +inline PxU32 getPxSceneDescContactReportStreamBufferSize( const PxSceneDesc* inOwner ) { return inOwner->contactReportStreamBufferSize; } +inline void setPxSceneDescContactReportStreamBufferSize( PxSceneDesc* inOwner, PxU32 inData) { inOwner->contactReportStreamBufferSize = inData; } +inline PxU32 getPxSceneDescCcdMaxPasses( const PxSceneDesc* inOwner ) { return inOwner->ccdMaxPasses; } +inline void setPxSceneDescCcdMaxPasses( PxSceneDesc* inOwner, PxU32 inData) { inOwner->ccdMaxPasses = inData; } +inline PxReal getPxSceneDescWakeCounterResetValue( const PxSceneDesc* inOwner ) { return inOwner->wakeCounterResetValue; } +inline void setPxSceneDescWakeCounterResetValue( PxSceneDesc* inOwner, PxReal inData) { inOwner->wakeCounterResetValue = inData; } +inline PxBounds3 getPxSceneDescSanityBounds( const PxSceneDesc* inOwner ) { return inOwner->sanityBounds; } +inline void setPxSceneDescSanityBounds( PxSceneDesc* inOwner, PxBounds3 inData) { inOwner->sanityBounds = inData; } +inline PxgDynamicsMemoryConfig getPxSceneDescGpuDynamicsConfig( const PxSceneDesc* inOwner ) { return inOwner->gpuDynamicsConfig; } +inline void setPxSceneDescGpuDynamicsConfig( PxSceneDesc* inOwner, PxgDynamicsMemoryConfig inData) { inOwner->gpuDynamicsConfig = inData; } +inline PxU32 getPxSceneDescGpuMaxNumPartitions( const PxSceneDesc* inOwner ) { return inOwner->gpuMaxNumPartitions; } +inline void setPxSceneDescGpuMaxNumPartitions( PxSceneDesc* inOwner, PxU32 inData) { inOwner->gpuMaxNumPartitions = inData; } +inline PxU32 getPxSceneDescGpuComputeVersion( const PxSceneDesc* inOwner ) { return inOwner->gpuComputeVersion; } +inline void setPxSceneDescGpuComputeVersion( PxSceneDesc* inOwner, PxU32 inData) { inOwner->gpuComputeVersion = inData; } +PX_PHYSX_CORE_API PxSceneDescGeneratedInfo::PxSceneDescGeneratedInfo() + : ToDefault( "ToDefault", setPxSceneDesc_ToDefault) + , Gravity( "Gravity", setPxSceneDescGravity, getPxSceneDescGravity ) + , SimulationEventCallback( "SimulationEventCallback", setPxSceneDescSimulationEventCallback, getPxSceneDescSimulationEventCallback ) + , ContactModifyCallback( "ContactModifyCallback", setPxSceneDescContactModifyCallback, getPxSceneDescContactModifyCallback ) + , CcdContactModifyCallback( "CcdContactModifyCallback", setPxSceneDescCcdContactModifyCallback, getPxSceneDescCcdContactModifyCallback ) + , FilterShaderData( "FilterShaderData", setPxSceneDescFilterShaderData, getPxSceneDescFilterShaderData ) + , FilterShaderDataSize( "FilterShaderDataSize", setPxSceneDescFilterShaderDataSize, getPxSceneDescFilterShaderDataSize ) + , FilterShader( "FilterShader", setPxSceneDescFilterShader, getPxSceneDescFilterShader ) + , FilterCallback( "FilterCallback", setPxSceneDescFilterCallback, getPxSceneDescFilterCallback ) + , KineKineFilteringMode( "KineKineFilteringMode", setPxSceneDescKineKineFilteringMode, getPxSceneDescKineKineFilteringMode ) + , StaticKineFilteringMode( "StaticKineFilteringMode", setPxSceneDescStaticKineFilteringMode, getPxSceneDescStaticKineFilteringMode ) + , BroadPhaseType( "BroadPhaseType", setPxSceneDescBroadPhaseType, getPxSceneDescBroadPhaseType ) + , BroadPhaseCallback( "BroadPhaseCallback", setPxSceneDescBroadPhaseCallback, getPxSceneDescBroadPhaseCallback ) + , Limits( "Limits", setPxSceneDescLimits, getPxSceneDescLimits ) + , FrictionType( "FrictionType", setPxSceneDescFrictionType, getPxSceneDescFrictionType ) + , SolverType( "SolverType", setPxSceneDescSolverType, getPxSceneDescSolverType ) + , BounceThresholdVelocity( "BounceThresholdVelocity", setPxSceneDescBounceThresholdVelocity, getPxSceneDescBounceThresholdVelocity ) + , FrictionOffsetThreshold( "FrictionOffsetThreshold", setPxSceneDescFrictionOffsetThreshold, getPxSceneDescFrictionOffsetThreshold ) + , CcdMaxSeparation( "CcdMaxSeparation", setPxSceneDescCcdMaxSeparation, getPxSceneDescCcdMaxSeparation ) + , SolverOffsetSlop( "SolverOffsetSlop", setPxSceneDescSolverOffsetSlop, getPxSceneDescSolverOffsetSlop ) + , Flags( "Flags", setPxSceneDescFlags, getPxSceneDescFlags ) + , CpuDispatcher( "CpuDispatcher", setPxSceneDescCpuDispatcher, getPxSceneDescCpuDispatcher ) + , GpuDispatcher( "GpuDispatcher", setPxSceneDescGpuDispatcher, getPxSceneDescGpuDispatcher ) + , StaticStructure( "StaticStructure", setPxSceneDescStaticStructure, getPxSceneDescStaticStructure ) + , DynamicStructure( "DynamicStructure", setPxSceneDescDynamicStructure, getPxSceneDescDynamicStructure ) + , DynamicTreeRebuildRateHint( "DynamicTreeRebuildRateHint", setPxSceneDescDynamicTreeRebuildRateHint, getPxSceneDescDynamicTreeRebuildRateHint ) + , SceneQueryUpdateMode( "SceneQueryUpdateMode", setPxSceneDescSceneQueryUpdateMode, getPxSceneDescSceneQueryUpdateMode ) + , UserData( "UserData", setPxSceneDescUserData, getPxSceneDescUserData ) + , SolverBatchSize( "SolverBatchSize", setPxSceneDescSolverBatchSize, getPxSceneDescSolverBatchSize ) + , NbContactDataBlocks( "NbContactDataBlocks", setPxSceneDescNbContactDataBlocks, getPxSceneDescNbContactDataBlocks ) + , MaxNbContactDataBlocks( "MaxNbContactDataBlocks", setPxSceneDescMaxNbContactDataBlocks, getPxSceneDescMaxNbContactDataBlocks ) + , MaxBiasCoefficient( "MaxBiasCoefficient", setPxSceneDescMaxBiasCoefficient, getPxSceneDescMaxBiasCoefficient ) + , ContactReportStreamBufferSize( "ContactReportStreamBufferSize", setPxSceneDescContactReportStreamBufferSize, getPxSceneDescContactReportStreamBufferSize ) + , CcdMaxPasses( "CcdMaxPasses", setPxSceneDescCcdMaxPasses, getPxSceneDescCcdMaxPasses ) + , WakeCounterResetValue( "WakeCounterResetValue", setPxSceneDescWakeCounterResetValue, getPxSceneDescWakeCounterResetValue ) + , SanityBounds( "SanityBounds", setPxSceneDescSanityBounds, getPxSceneDescSanityBounds ) + , GpuDynamicsConfig( "GpuDynamicsConfig", setPxSceneDescGpuDynamicsConfig, getPxSceneDescGpuDynamicsConfig ) + , GpuMaxNumPartitions( "GpuMaxNumPartitions", setPxSceneDescGpuMaxNumPartitions, getPxSceneDescGpuMaxNumPartitions ) + , GpuComputeVersion( "GpuComputeVersion", setPxSceneDescGpuComputeVersion, getPxSceneDescGpuComputeVersion ) +{} +PX_PHYSX_CORE_API PxSceneDescGeneratedValues::PxSceneDescGeneratedValues( const PxSceneDesc* inSource ) + :Gravity( inSource->gravity ) + ,SimulationEventCallback( inSource->simulationEventCallback ) + ,ContactModifyCallback( inSource->contactModifyCallback ) + ,CcdContactModifyCallback( inSource->ccdContactModifyCallback ) + ,FilterShaderData( inSource->filterShaderData ) + ,FilterShaderDataSize( inSource->filterShaderDataSize ) + ,FilterShader( inSource->filterShader ) + ,FilterCallback( inSource->filterCallback ) + ,KineKineFilteringMode( inSource->kineKineFilteringMode ) + ,StaticKineFilteringMode( inSource->staticKineFilteringMode ) + ,BroadPhaseType( inSource->broadPhaseType ) + ,BroadPhaseCallback( inSource->broadPhaseCallback ) + ,Limits( inSource->limits ) + ,FrictionType( inSource->frictionType ) + ,SolverType( inSource->solverType ) + ,BounceThresholdVelocity( inSource->bounceThresholdVelocity ) + ,FrictionOffsetThreshold( inSource->frictionOffsetThreshold ) + ,CcdMaxSeparation( inSource->ccdMaxSeparation ) + ,SolverOffsetSlop( inSource->solverOffsetSlop ) + ,Flags( inSource->flags ) + ,CpuDispatcher( inSource->cpuDispatcher ) + ,GpuDispatcher( inSource->gpuDispatcher ) + ,StaticStructure( inSource->staticStructure ) + ,DynamicStructure( inSource->dynamicStructure ) + ,DynamicTreeRebuildRateHint( inSource->dynamicTreeRebuildRateHint ) + ,SceneQueryUpdateMode( inSource->sceneQueryUpdateMode ) + ,UserData( inSource->userData ) + ,SolverBatchSize( inSource->solverBatchSize ) + ,NbContactDataBlocks( inSource->nbContactDataBlocks ) + ,MaxNbContactDataBlocks( inSource->maxNbContactDataBlocks ) + ,MaxBiasCoefficient( inSource->maxBiasCoefficient ) + ,ContactReportStreamBufferSize( inSource->contactReportStreamBufferSize ) + ,CcdMaxPasses( inSource->ccdMaxPasses ) + ,WakeCounterResetValue( inSource->wakeCounterResetValue ) + ,SanityBounds( inSource->sanityBounds ) + ,GpuDynamicsConfig( inSource->gpuDynamicsConfig ) + ,GpuMaxNumPartitions( inSource->gpuMaxNumPartitions ) + ,GpuComputeVersion( inSource->gpuComputeVersion ) +{ + PX_UNUSED(inSource); +} +inline PxU32 getPxSimulationStatisticsNbActiveConstraints( const PxSimulationStatistics* inOwner ) { return inOwner->nbActiveConstraints; } +inline void setPxSimulationStatisticsNbActiveConstraints( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbActiveConstraints = inData; } +inline PxU32 getPxSimulationStatisticsNbActiveDynamicBodies( const PxSimulationStatistics* inOwner ) { return inOwner->nbActiveDynamicBodies; } +inline void setPxSimulationStatisticsNbActiveDynamicBodies( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbActiveDynamicBodies = inData; } +inline PxU32 getPxSimulationStatisticsNbActiveKinematicBodies( const PxSimulationStatistics* inOwner ) { return inOwner->nbActiveKinematicBodies; } +inline void setPxSimulationStatisticsNbActiveKinematicBodies( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbActiveKinematicBodies = inData; } +inline PxU32 getPxSimulationStatisticsNbStaticBodies( const PxSimulationStatistics* inOwner ) { return inOwner->nbStaticBodies; } +inline void setPxSimulationStatisticsNbStaticBodies( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbStaticBodies = inData; } +inline PxU32 getPxSimulationStatisticsNbDynamicBodies( const PxSimulationStatistics* inOwner ) { return inOwner->nbDynamicBodies; } +inline void setPxSimulationStatisticsNbDynamicBodies( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbDynamicBodies = inData; } +inline PxU32 getPxSimulationStatisticsNbAggregates( const PxSimulationStatistics* inOwner ) { return inOwner->nbAggregates; } +inline void setPxSimulationStatisticsNbAggregates( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbAggregates = inData; } +inline PxU32 getPxSimulationStatisticsNbArticulations( const PxSimulationStatistics* inOwner ) { return inOwner->nbArticulations; } +inline void setPxSimulationStatisticsNbArticulations( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbArticulations = inData; } +inline PxU32 getPxSimulationStatisticsNbAxisSolverConstraints( const PxSimulationStatistics* inOwner ) { return inOwner->nbAxisSolverConstraints; } +inline void setPxSimulationStatisticsNbAxisSolverConstraints( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbAxisSolverConstraints = inData; } +inline PxU32 getPxSimulationStatisticsCompressedContactSize( const PxSimulationStatistics* inOwner ) { return inOwner->compressedContactSize; } +inline void setPxSimulationStatisticsCompressedContactSize( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->compressedContactSize = inData; } +inline PxU32 getPxSimulationStatisticsRequiredContactConstraintMemory( const PxSimulationStatistics* inOwner ) { return inOwner->requiredContactConstraintMemory; } +inline void setPxSimulationStatisticsRequiredContactConstraintMemory( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->requiredContactConstraintMemory = inData; } +inline PxU32 getPxSimulationStatisticsPeakConstraintMemory( const PxSimulationStatistics* inOwner ) { return inOwner->peakConstraintMemory; } +inline void setPxSimulationStatisticsPeakConstraintMemory( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->peakConstraintMemory = inData; } +inline PxU32 getPxSimulationStatisticsNbDiscreteContactPairsTotal( const PxSimulationStatistics* inOwner ) { return inOwner->nbDiscreteContactPairsTotal; } +inline void setPxSimulationStatisticsNbDiscreteContactPairsTotal( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbDiscreteContactPairsTotal = inData; } +inline PxU32 getPxSimulationStatisticsNbDiscreteContactPairsWithCacheHits( const PxSimulationStatistics* inOwner ) { return inOwner->nbDiscreteContactPairsWithCacheHits; } +inline void setPxSimulationStatisticsNbDiscreteContactPairsWithCacheHits( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbDiscreteContactPairsWithCacheHits = inData; } +inline PxU32 getPxSimulationStatisticsNbDiscreteContactPairsWithContacts( const PxSimulationStatistics* inOwner ) { return inOwner->nbDiscreteContactPairsWithContacts; } +inline void setPxSimulationStatisticsNbDiscreteContactPairsWithContacts( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbDiscreteContactPairsWithContacts = inData; } +inline PxU32 getPxSimulationStatisticsNbNewPairs( const PxSimulationStatistics* inOwner ) { return inOwner->nbNewPairs; } +inline void setPxSimulationStatisticsNbNewPairs( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbNewPairs = inData; } +inline PxU32 getPxSimulationStatisticsNbLostPairs( const PxSimulationStatistics* inOwner ) { return inOwner->nbLostPairs; } +inline void setPxSimulationStatisticsNbLostPairs( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbLostPairs = inData; } +inline PxU32 getPxSimulationStatisticsNbNewTouches( const PxSimulationStatistics* inOwner ) { return inOwner->nbNewTouches; } +inline void setPxSimulationStatisticsNbNewTouches( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbNewTouches = inData; } +inline PxU32 getPxSimulationStatisticsNbLostTouches( const PxSimulationStatistics* inOwner ) { return inOwner->nbLostTouches; } +inline void setPxSimulationStatisticsNbLostTouches( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbLostTouches = inData; } +inline PxU32 getPxSimulationStatisticsNbPartitions( const PxSimulationStatistics* inOwner ) { return inOwner->nbPartitions; } +inline void setPxSimulationStatisticsNbPartitions( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbPartitions = inData; } +inline PxU32 getPxSimulationStatisticsNbBroadPhaseAdds( const PxSimulationStatistics* inOwner ) { return inOwner->nbBroadPhaseAdds; } +inline void setPxSimulationStatisticsNbBroadPhaseAdds( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbBroadPhaseAdds = inData; } +inline PxU32 getPxSimulationStatisticsNbBroadPhaseRemoves( const PxSimulationStatistics* inOwner ) { return inOwner->nbBroadPhaseRemoves; } +inline void setPxSimulationStatisticsNbBroadPhaseRemoves( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbBroadPhaseRemoves = inData; } +PX_PHYSX_CORE_API PxSimulationStatisticsGeneratedInfo::PxSimulationStatisticsGeneratedInfo() + : NbActiveConstraints( "NbActiveConstraints", setPxSimulationStatisticsNbActiveConstraints, getPxSimulationStatisticsNbActiveConstraints ) + , NbActiveDynamicBodies( "NbActiveDynamicBodies", setPxSimulationStatisticsNbActiveDynamicBodies, getPxSimulationStatisticsNbActiveDynamicBodies ) + , NbActiveKinematicBodies( "NbActiveKinematicBodies", setPxSimulationStatisticsNbActiveKinematicBodies, getPxSimulationStatisticsNbActiveKinematicBodies ) + , NbStaticBodies( "NbStaticBodies", setPxSimulationStatisticsNbStaticBodies, getPxSimulationStatisticsNbStaticBodies ) + , NbDynamicBodies( "NbDynamicBodies", setPxSimulationStatisticsNbDynamicBodies, getPxSimulationStatisticsNbDynamicBodies ) + , NbAggregates( "NbAggregates", setPxSimulationStatisticsNbAggregates, getPxSimulationStatisticsNbAggregates ) + , NbArticulations( "NbArticulations", setPxSimulationStatisticsNbArticulations, getPxSimulationStatisticsNbArticulations ) + , NbAxisSolverConstraints( "NbAxisSolverConstraints", setPxSimulationStatisticsNbAxisSolverConstraints, getPxSimulationStatisticsNbAxisSolverConstraints ) + , CompressedContactSize( "CompressedContactSize", setPxSimulationStatisticsCompressedContactSize, getPxSimulationStatisticsCompressedContactSize ) + , RequiredContactConstraintMemory( "RequiredContactConstraintMemory", setPxSimulationStatisticsRequiredContactConstraintMemory, getPxSimulationStatisticsRequiredContactConstraintMemory ) + , PeakConstraintMemory( "PeakConstraintMemory", setPxSimulationStatisticsPeakConstraintMemory, getPxSimulationStatisticsPeakConstraintMemory ) + , NbDiscreteContactPairsTotal( "NbDiscreteContactPairsTotal", setPxSimulationStatisticsNbDiscreteContactPairsTotal, getPxSimulationStatisticsNbDiscreteContactPairsTotal ) + , NbDiscreteContactPairsWithCacheHits( "NbDiscreteContactPairsWithCacheHits", setPxSimulationStatisticsNbDiscreteContactPairsWithCacheHits, getPxSimulationStatisticsNbDiscreteContactPairsWithCacheHits ) + , NbDiscreteContactPairsWithContacts( "NbDiscreteContactPairsWithContacts", setPxSimulationStatisticsNbDiscreteContactPairsWithContacts, getPxSimulationStatisticsNbDiscreteContactPairsWithContacts ) + , NbNewPairs( "NbNewPairs", setPxSimulationStatisticsNbNewPairs, getPxSimulationStatisticsNbNewPairs ) + , NbLostPairs( "NbLostPairs", setPxSimulationStatisticsNbLostPairs, getPxSimulationStatisticsNbLostPairs ) + , NbNewTouches( "NbNewTouches", setPxSimulationStatisticsNbNewTouches, getPxSimulationStatisticsNbNewTouches ) + , NbLostTouches( "NbLostTouches", setPxSimulationStatisticsNbLostTouches, getPxSimulationStatisticsNbLostTouches ) + , NbPartitions( "NbPartitions", setPxSimulationStatisticsNbPartitions, getPxSimulationStatisticsNbPartitions ) + , NbBroadPhaseAdds( "NbBroadPhaseAdds", setPxSimulationStatisticsNbBroadPhaseAdds, getPxSimulationStatisticsNbBroadPhaseAdds ) + , NbBroadPhaseRemoves( "NbBroadPhaseRemoves", setPxSimulationStatisticsNbBroadPhaseRemoves, getPxSimulationStatisticsNbBroadPhaseRemoves ) +{} +PX_PHYSX_CORE_API PxSimulationStatisticsGeneratedValues::PxSimulationStatisticsGeneratedValues( const PxSimulationStatistics* inSource ) + :NbActiveConstraints( inSource->nbActiveConstraints ) + ,NbActiveDynamicBodies( inSource->nbActiveDynamicBodies ) + ,NbActiveKinematicBodies( inSource->nbActiveKinematicBodies ) + ,NbStaticBodies( inSource->nbStaticBodies ) + ,NbDynamicBodies( inSource->nbDynamicBodies ) + ,NbAggregates( inSource->nbAggregates ) + ,NbArticulations( inSource->nbArticulations ) + ,NbAxisSolverConstraints( inSource->nbAxisSolverConstraints ) + ,CompressedContactSize( inSource->compressedContactSize ) + ,RequiredContactConstraintMemory( inSource->requiredContactConstraintMemory ) + ,PeakConstraintMemory( inSource->peakConstraintMemory ) + ,NbDiscreteContactPairsTotal( inSource->nbDiscreteContactPairsTotal ) + ,NbDiscreteContactPairsWithCacheHits( inSource->nbDiscreteContactPairsWithCacheHits ) + ,NbDiscreteContactPairsWithContacts( inSource->nbDiscreteContactPairsWithContacts ) + ,NbNewPairs( inSource->nbNewPairs ) + ,NbLostPairs( inSource->nbLostPairs ) + ,NbNewTouches( inSource->nbNewTouches ) + ,NbLostTouches( inSource->nbLostTouches ) + ,NbPartitions( inSource->nbPartitions ) + ,NbBroadPhaseAdds( inSource->nbBroadPhaseAdds ) + ,NbBroadPhaseRemoves( inSource->nbBroadPhaseRemoves ) +{ + PX_UNUSED(inSource); + PxMemCopy( NbDiscreteContactPairs, inSource->nbDiscreteContactPairs, sizeof( NbDiscreteContactPairs ) ); + PxMemCopy( NbModifiedContactPairs, inSource->nbModifiedContactPairs, sizeof( NbModifiedContactPairs ) ); + PxMemCopy( NbCCDPairs, inSource->nbCCDPairs, sizeof( NbCCDPairs ) ); + PxMemCopy( NbTriggerPairs, inSource->nbTriggerPairs, sizeof( NbTriggerPairs ) ); + PxMemCopy( NbShapes, inSource->nbShapes, sizeof( NbShapes ) ); +} diff --git a/src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp b/src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp new file mode 100644 index 000000000..2717e54ae --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp @@ -0,0 +1,148 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" + +#include "PxMetaDataObjects.h" + +using namespace physx; + +PX_PHYSX_CORE_API PxGeometryType::Enum PxShapeGeometryPropertyHelper::getGeometryType(const PxShape* inShape) const { return inShape->getGeometryType(); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxBoxGeometry& geometry) const { return inShape->getBoxGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxSphereGeometry& geometry) const { return inShape->getSphereGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxCapsuleGeometry& geometry) const { return inShape->getCapsuleGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxPlaneGeometry& geometry) const { return inShape->getPlaneGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxConvexMeshGeometry& geometry) const { return inShape->getConvexMeshGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxTriangleMeshGeometry& geometry) const { return inShape->getTriangleMeshGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxHeightFieldGeometry& geometry) const { return inShape->getHeightFieldGeometry( geometry ); } + +PX_PHYSX_CORE_API void PxShapeMaterialsPropertyHelper::setMaterials(PxShape* inShape, PxMaterial*const* materials, PxU16 materialCount) const +{ + inShape->setMaterials( materials, materialCount ); +} + +PX_PHYSX_CORE_API PxShape* PxRigidActorShapeCollectionHelper::createShape(PxRigidActor* inActor, const PxGeometry& geometry, PxMaterial& material, + PxShapeFlags shapeFlags ) const +{ + PxMaterial* materialPtr = const_cast(&material); + PxShape* shape = PxGetPhysics().createShape(geometry, &materialPtr, 1, true, shapeFlags); + if (shape) + { + inActor->attachShape(*shape); // attach can fail, if e.g. we try and attach a trimesh simulation shape to a dynamic actor + shape->release(); // if attach fails, we hold the only counted reference, and so this cleans up properly + } + return shape; +} +PX_PHYSX_CORE_API PxShape* PxRigidActorShapeCollectionHelper::createShape(PxRigidActor* inActor, const PxGeometry& geometry, PxMaterial *const* materials, + PxU16 materialCount, PxShapeFlags shapeFlags ) const +{ + PxShape* shape = PxGetPhysics().createShape(geometry, materials, materialCount, true, shapeFlags); + if (shape) + { + inActor->attachShape(*shape); // attach can fail, if e.g. we try and attach a trimesh simulation shape to a dynamic actor + shape->release(); // if attach fails, we hold the only counted reference, and so this cleans up properly + } + return shape; +} + +PX_PHYSX_CORE_API PxArticulationLink* PxArticulationLinkCollectionPropHelper::createLink(PxArticulation* inArticulation, PxArticulationLink* parent, + const PxTransform& pose) const +{ + PX_CHECK_AND_RETURN_NULL(pose.isValid(), "PxArticulationLinkCollectionPropHelper::createLink pose is not valid."); + return inArticulation->createLink(parent, pose ); +} + + + + +inline void SetNbShape( PxSimulationStatistics* inStats, PxGeometryType::Enum data, PxU32 val ) { inStats->nbShapes[data] = val; } +inline PxU32 GetNbShape( const PxSimulationStatistics* inStats, PxGeometryType::Enum data) { return inStats->nbShapes[data]; } + + +PX_PHYSX_CORE_API NbShapesProperty::NbShapesProperty() + : PxIndexedPropertyInfo ( "NbShapes", SetNbShape, GetNbShape ) +{ +} + + +inline void SetNbDiscreteContactPairs( PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2, PxU32 val ) { inStats->nbDiscreteContactPairs[idx1][idx2] = val; } +inline PxU32 GetNbDiscreteContactPairs( const PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2 ) { return inStats->nbDiscreteContactPairs[idx1][idx2]; } +PX_PHYSX_CORE_API NbDiscreteContactPairsProperty::NbDiscreteContactPairsProperty() + : PxDualIndexedPropertyInfo ( "NbDiscreteContactPairs", SetNbDiscreteContactPairs, GetNbDiscreteContactPairs ) +{ +} + +inline void SetNbModifiedContactPairs( PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2, PxU32 val ) { inStats->nbModifiedContactPairs[idx1][idx2] = val; } +inline PxU32 GetNbModifiedContactPairs( const PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2 ) { return inStats->nbModifiedContactPairs[idx1][idx2]; } +PX_PHYSX_CORE_API NbModifiedContactPairsProperty::NbModifiedContactPairsProperty() + : PxDualIndexedPropertyInfo ( "NbModifiedContactPairs", SetNbModifiedContactPairs, GetNbModifiedContactPairs ) +{ +} + +inline void SetNbCCDPairs( PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2, PxU32 val ) { inStats->nbCCDPairs[idx1][idx2] = val; } +inline PxU32 GetNbCCDPairs( const PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2 ) { return inStats->nbCCDPairs[idx1][idx2]; } +PX_PHYSX_CORE_API NbCCDPairsProperty::NbCCDPairsProperty() + : PxDualIndexedPropertyInfo ( "NbCCDPairs", SetNbCCDPairs, GetNbCCDPairs ) +{ +} + +inline void SetNbTriggerPairs( PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2, PxU32 val ) { inStats->nbTriggerPairs[idx1][idx2] = val; } +inline PxU32 GetNbTriggerPairs( const PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2 ) { return inStats->nbTriggerPairs[idx1][idx2]; } +PX_PHYSX_CORE_API NbTriggerPairsProperty::NbTriggerPairsProperty() + : PxDualIndexedPropertyInfo ( "NbTriggerPairs", SetNbTriggerPairs, GetNbTriggerPairs ) +{ +} + +inline PxSimulationStatistics GetStats( const PxScene* inScene ) { PxSimulationStatistics stats; inScene->getSimulationStatistics( stats ); return stats; } +PX_PHYSX_CORE_API SimulationStatisticsProperty::SimulationStatisticsProperty() + : PxReadOnlyPropertyInfo( "SimulationStatistics", GetStats ) +{ +} + diff --git a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h new file mode 100644 index 000000000..1de957676 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h @@ -0,0 +1,162 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +PxJoint_PropertiesStart, +PxJoint_Actors, +PxJoint_LocalPose, +PxJoint_RelativeTransform, +PxJoint_RelativeLinearVelocity, +PxJoint_RelativeAngularVelocity, +PxJoint_BreakForce, +PxJoint_ConstraintFlags, +PxJoint_InvMassScale0, +PxJoint_InvInertiaScale0, +PxJoint_InvMassScale1, +PxJoint_InvInertiaScale1, +PxJoint_Constraint, +PxJoint_Name, +PxJoint_Scene, +PxJoint_UserData, +PxJoint_PropertiesStop, +PxContactJoint_PropertiesStart, +PxContactJoint_Contact, +PxContactJoint_ContactNormal, +PxContactJoint_Penetration, +PxContactJoint_Resititution, +PxContactJoint_BounceThreshold, +PxContactJoint_ConcreteTypeName, +PxContactJoint_PropertiesStop, +PxD6Joint_PropertiesStart, +PxD6Joint_Motion, +PxD6Joint_TwistAngle, +PxD6Joint_Twist, +PxD6Joint_SwingYAngle, +PxD6Joint_SwingZAngle, +PxD6Joint_DistanceLimit, +PxD6Joint_LinearLimit, +PxD6Joint_TwistLimit, +PxD6Joint_SwingLimit, +PxD6Joint_PyramidSwingLimit, +PxD6Joint_Drive, +PxD6Joint_DrivePosition, +PxD6Joint_ProjectionLinearTolerance, +PxD6Joint_ProjectionAngularTolerance, +PxD6Joint_ConcreteTypeName, +PxD6Joint_PropertiesStop, +PxDistanceJoint_PropertiesStart, +PxDistanceJoint_Distance, +PxDistanceJoint_MinDistance, +PxDistanceJoint_MaxDistance, +PxDistanceJoint_Tolerance, +PxDistanceJoint_Stiffness, +PxDistanceJoint_Damping, +PxDistanceJoint_DistanceJointFlags, +PxDistanceJoint_ConcreteTypeName, +PxDistanceJoint_PropertiesStop, +PxFixedJoint_PropertiesStart, +PxFixedJoint_ProjectionLinearTolerance, +PxFixedJoint_ProjectionAngularTolerance, +PxFixedJoint_ConcreteTypeName, +PxFixedJoint_PropertiesStop, +PxPrismaticJoint_PropertiesStart, +PxPrismaticJoint_Position, +PxPrismaticJoint_Velocity, +PxPrismaticJoint_Limit, +PxPrismaticJoint_PrismaticJointFlags, +PxPrismaticJoint_ProjectionLinearTolerance, +PxPrismaticJoint_ProjectionAngularTolerance, +PxPrismaticJoint_ConcreteTypeName, +PxPrismaticJoint_PropertiesStop, +PxRevoluteJoint_PropertiesStart, +PxRevoluteJoint_Angle, +PxRevoluteJoint_Velocity, +PxRevoluteJoint_Limit, +PxRevoluteJoint_DriveVelocity, +PxRevoluteJoint_DriveForceLimit, +PxRevoluteJoint_DriveGearRatio, +PxRevoluteJoint_RevoluteJointFlags, +PxRevoluteJoint_ProjectionLinearTolerance, +PxRevoluteJoint_ProjectionAngularTolerance, +PxRevoluteJoint_ConcreteTypeName, +PxRevoluteJoint_PropertiesStop, +PxSphericalJoint_PropertiesStart, +PxSphericalJoint_LimitCone, +PxSphericalJoint_SwingYAngle, +PxSphericalJoint_SwingZAngle, +PxSphericalJoint_SphericalJointFlags, +PxSphericalJoint_ProjectionLinearTolerance, +PxSphericalJoint_ConcreteTypeName, +PxSphericalJoint_PropertiesStop, +PxJointLimitParameters_PropertiesStart, +PxJointLimitParameters_Restitution, +PxJointLimitParameters_BounceThreshold, +PxJointLimitParameters_Stiffness, +PxJointLimitParameters_Damping, +PxJointLimitParameters_ContactDistance, +PxJointLimitParameters_PropertiesStop, +PxJointLinearLimit_PropertiesStart, +PxJointLinearLimit_Value, +PxJointLinearLimit_PropertiesStop, +PxJointLinearLimitPair_PropertiesStart, +PxJointLinearLimitPair_Upper, +PxJointLinearLimitPair_Lower, +PxJointLinearLimitPair_PropertiesStop, +PxJointAngularLimitPair_PropertiesStart, +PxJointAngularLimitPair_Upper, +PxJointAngularLimitPair_Lower, +PxJointAngularLimitPair_PropertiesStop, +PxJointLimitCone_PropertiesStart, +PxJointLimitCone_YAngle, +PxJointLimitCone_ZAngle, +PxJointLimitCone_PropertiesStop, +PxJointLimitPyramid_PropertiesStart, +PxJointLimitPyramid_YAngleMin, +PxJointLimitPyramid_YAngleMax, +PxJointLimitPyramid_ZAngleMin, +PxJointLimitPyramid_ZAngleMax, +PxJointLimitPyramid_PropertiesStop, +PxSpring_PropertiesStart, +PxSpring_Stiffness, +PxSpring_Damping, +PxSpring_PropertiesStop, +PxD6JointDrive_PropertiesStart, +PxD6JointDrive_ForceLimit, +PxD6JointDrive_Flags, +PxD6JointDrive_PropertiesStop, + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h new file mode 100644 index 000000000..ed59c9e9f --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h @@ -0,0 +1,1229 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +#define PX_PROPERTY_INFO_NAME PxExtensionsPropertyInfoName + static PxU32ToName g_physx__PxJointActorIndex__EnumConversion[] = { + { "eACTOR0", static_cast( physx::PxJointActorIndex::eACTOR0 ) }, + { "eACTOR1", static_cast( physx::PxJointActorIndex::eACTOR1 ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxJointActorIndex::Enum > { PxEnumTraits() : NameConversion( g_physx__PxJointActorIndex__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxJoint; + struct PxJointGeneratedValues + { + PxRigidActor * Actors[2]; + PxTransform LocalPose[physx::PxJointActorIndex::COUNT]; + PxTransform RelativeTransform; + PxVec3 RelativeLinearVelocity; + PxVec3 RelativeAngularVelocity; + PxReal BreakForce[2]; + PxConstraintFlags ConstraintFlags; + PxReal InvMassScale0; + PxReal InvInertiaScale0; + PxReal InvMassScale1; + PxReal InvInertiaScale1; + PxConstraint * Constraint; + const char * Name; + PxScene * Scene; + void * UserData; + PxJointGeneratedValues( const PxJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, Actors, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, LocalPose, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, RelativeTransform, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, RelativeLinearVelocity, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, RelativeAngularVelocity, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, BreakForce, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, ConstraintFlags, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, InvMassScale0, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, InvInertiaScale0, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, InvMassScale1, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, InvInertiaScale1, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, Constraint, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, Name, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, Scene, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, UserData, PxJointGeneratedValues) + struct PxJointGeneratedInfo + + { + static const char* getClassName() { return "PxJoint"; } + PxRangePropertyInfo Actors; + PxIndexedPropertyInfo LocalPose; + PxReadOnlyPropertyInfo RelativeTransform; + PxReadOnlyPropertyInfo RelativeLinearVelocity; + PxReadOnlyPropertyInfo RelativeAngularVelocity; + PxRangePropertyInfo BreakForce; + PxPropertyInfo ConstraintFlags; + PxPropertyInfo InvMassScale0; + PxPropertyInfo InvInertiaScale0; + PxPropertyInfo InvMassScale1; + PxPropertyInfo InvInertiaScale1; + PxReadOnlyPropertyInfo Constraint; + PxPropertyInfo Name; + PxReadOnlyPropertyInfo Scene; + PxPropertyInfo UserData; + + PxJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 15; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Actors, inStartIndex + 0 );; + inOperator( LocalPose, inStartIndex + 1 );; + inOperator( RelativeTransform, inStartIndex + 2 );; + inOperator( RelativeLinearVelocity, inStartIndex + 3 );; + inOperator( RelativeAngularVelocity, inStartIndex + 4 );; + inOperator( BreakForce, inStartIndex + 5 );; + inOperator( ConstraintFlags, inStartIndex + 6 );; + inOperator( InvMassScale0, inStartIndex + 7 );; + inOperator( InvInertiaScale0, inStartIndex + 8 );; + inOperator( InvMassScale1, inStartIndex + 9 );; + inOperator( InvInertiaScale1, inStartIndex + 10 );; + inOperator( Constraint, inStartIndex + 11 );; + inOperator( Name, inStartIndex + 12 );; + inOperator( Scene, inStartIndex + 13 );; + inOperator( UserData, inStartIndex + 14 );; + return 15 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointGeneratedInfo Info; + const PxJointGeneratedInfo* getInfo() { return &Info; } + }; + + class PxContactJoint; + struct PxContactJointGeneratedValues + : PxJointGeneratedValues { + PxVec3 Contact; + PxVec3 ContactNormal; + PxReal Penetration; + PxReal Resititution; + PxReal BounceThreshold; + const char * ConcreteTypeName; + PxContactJointGeneratedValues( const PxContactJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, Contact, PxContactJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, ContactNormal, PxContactJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, Penetration, PxContactJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, Resititution, PxContactJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, BounceThreshold, PxContactJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, ConcreteTypeName, PxContactJointGeneratedValues) + struct PxContactJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxContactJoint"; } + PxPropertyInfo Contact; + PxPropertyInfo ContactNormal; + PxPropertyInfo Penetration; + PxPropertyInfo Resititution; + PxPropertyInfo BounceThreshold; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxContactJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 6; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Contact, inStartIndex + 0 );; + inOperator( ContactNormal, inStartIndex + 1 );; + inOperator( Penetration, inStartIndex + 2 );; + inOperator( Resititution, inStartIndex + 3 );; + inOperator( BounceThreshold, inStartIndex + 4 );; + inOperator( ConcreteTypeName, inStartIndex + 5 );; + return 6 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxContactJointGeneratedInfo Info; + const PxContactJointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxD6Axis__EnumConversion[] = { + { "eX", static_cast( physx::PxD6Axis::eX ) }, + { "eY", static_cast( physx::PxD6Axis::eY ) }, + { "eZ", static_cast( physx::PxD6Axis::eZ ) }, + { "eTWIST", static_cast( physx::PxD6Axis::eTWIST ) }, + { "eSWING1", static_cast( physx::PxD6Axis::eSWING1 ) }, + { "eSWING2", static_cast( physx::PxD6Axis::eSWING2 ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxD6Axis::Enum > { PxEnumTraits() : NameConversion( g_physx__PxD6Axis__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxD6Motion__EnumConversion[] = { + { "eLOCKED", static_cast( physx::PxD6Motion::eLOCKED ) }, + { "eLIMITED", static_cast( physx::PxD6Motion::eLIMITED ) }, + { "eFREE", static_cast( physx::PxD6Motion::eFREE ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxD6Motion::Enum > { PxEnumTraits() : NameConversion( g_physx__PxD6Motion__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxD6Drive__EnumConversion[] = { + { "eX", static_cast( physx::PxD6Drive::eX ) }, + { "eY", static_cast( physx::PxD6Drive::eY ) }, + { "eZ", static_cast( physx::PxD6Drive::eZ ) }, + { "eSWING", static_cast( physx::PxD6Drive::eSWING ) }, + { "eTWIST", static_cast( physx::PxD6Drive::eTWIST ) }, + { "eSLERP", static_cast( physx::PxD6Drive::eSLERP ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxD6Drive::Enum > { PxEnumTraits() : NameConversion( g_physx__PxD6Drive__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxD6Joint; + struct PxD6JointGeneratedValues + : PxJointGeneratedValues { + PxD6Motion::Enum Motion[physx::PxD6Axis::eCOUNT]; + PxReal TwistAngle; + PxReal Twist; + PxReal SwingYAngle; + PxReal SwingZAngle; + PxJointLinearLimit DistanceLimit; + PxJointLinearLimit LinearLimit; + PxJointAngularLimitPair TwistLimit; + PxJointLimitCone SwingLimit; + PxJointLimitPyramid PyramidSwingLimit; + PxD6JointDrive Drive[physx::PxD6Drive::eCOUNT]; + PxTransform DrivePosition; + PxReal ProjectionLinearTolerance; + PxReal ProjectionAngularTolerance; + const char * ConcreteTypeName; + PxD6JointGeneratedValues( const PxD6Joint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, Motion, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, TwistAngle, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, Twist, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, SwingYAngle, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, SwingZAngle, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, DistanceLimit, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, LinearLimit, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, TwistLimit, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, SwingLimit, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, PyramidSwingLimit, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, Drive, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, DrivePosition, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, ProjectionLinearTolerance, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, ProjectionAngularTolerance, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, ConcreteTypeName, PxD6JointGeneratedValues) + struct PxD6JointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxD6Joint"; } + PxIndexedPropertyInfo Motion; + PxReadOnlyPropertyInfo TwistAngle; + PxReadOnlyPropertyInfo Twist; + PxReadOnlyPropertyInfo SwingYAngle; + PxReadOnlyPropertyInfo SwingZAngle; + PxPropertyInfo DistanceLimit; + PxPropertyInfo LinearLimit; + PxPropertyInfo TwistLimit; + PxPropertyInfo SwingLimit; + PxPropertyInfo PyramidSwingLimit; + PxIndexedPropertyInfo Drive; + PxPropertyInfo DrivePosition; + PxPropertyInfo ProjectionLinearTolerance; + PxPropertyInfo ProjectionAngularTolerance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxD6JointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 15; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Motion, inStartIndex + 0 );; + inOperator( TwistAngle, inStartIndex + 1 );; + inOperator( Twist, inStartIndex + 2 );; + inOperator( SwingYAngle, inStartIndex + 3 );; + inOperator( SwingZAngle, inStartIndex + 4 );; + inOperator( DistanceLimit, inStartIndex + 5 );; + inOperator( LinearLimit, inStartIndex + 6 );; + inOperator( TwistLimit, inStartIndex + 7 );; + inOperator( SwingLimit, inStartIndex + 8 );; + inOperator( PyramidSwingLimit, inStartIndex + 9 );; + inOperator( Drive, inStartIndex + 10 );; + inOperator( DrivePosition, inStartIndex + 11 );; + inOperator( ProjectionLinearTolerance, inStartIndex + 12 );; + inOperator( ProjectionAngularTolerance, inStartIndex + 13 );; + inOperator( ConcreteTypeName, inStartIndex + 14 );; + return 15 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxD6JointGeneratedInfo Info; + const PxD6JointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxDistanceJointFlag__EnumConversion[] = { + { "eMAX_DISTANCE_ENABLED", static_cast( physx::PxDistanceJointFlag::eMAX_DISTANCE_ENABLED ) }, + { "eMIN_DISTANCE_ENABLED", static_cast( physx::PxDistanceJointFlag::eMIN_DISTANCE_ENABLED ) }, + { "eSPRING_ENABLED", static_cast( physx::PxDistanceJointFlag::eSPRING_ENABLED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxDistanceJointFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxDistanceJointFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxDistanceJoint; + struct PxDistanceJointGeneratedValues + : PxJointGeneratedValues { + PxReal Distance; + PxReal MinDistance; + PxReal MaxDistance; + PxReal Tolerance; + PxReal Stiffness; + PxReal Damping; + PxDistanceJointFlags DistanceJointFlags; + const char * ConcreteTypeName; + PxDistanceJointGeneratedValues( const PxDistanceJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, Distance, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, MinDistance, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, MaxDistance, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, Tolerance, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, Stiffness, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, Damping, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, DistanceJointFlags, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, ConcreteTypeName, PxDistanceJointGeneratedValues) + struct PxDistanceJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxDistanceJoint"; } + PxReadOnlyPropertyInfo Distance; + PxPropertyInfo MinDistance; + PxPropertyInfo MaxDistance; + PxPropertyInfo Tolerance; + PxPropertyInfo Stiffness; + PxPropertyInfo Damping; + PxPropertyInfo DistanceJointFlags; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxDistanceJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 8; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Distance, inStartIndex + 0 );; + inOperator( MinDistance, inStartIndex + 1 );; + inOperator( MaxDistance, inStartIndex + 2 );; + inOperator( Tolerance, inStartIndex + 3 );; + inOperator( Stiffness, inStartIndex + 4 );; + inOperator( Damping, inStartIndex + 5 );; + inOperator( DistanceJointFlags, inStartIndex + 6 );; + inOperator( ConcreteTypeName, inStartIndex + 7 );; + return 8 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxDistanceJointGeneratedInfo Info; + const PxDistanceJointGeneratedInfo* getInfo() { return &Info; } + }; + + class PxFixedJoint; + struct PxFixedJointGeneratedValues + : PxJointGeneratedValues { + PxReal ProjectionLinearTolerance; + PxReal ProjectionAngularTolerance; + const char * ConcreteTypeName; + PxFixedJointGeneratedValues( const PxFixedJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxFixedJoint, ProjectionLinearTolerance, PxFixedJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxFixedJoint, ProjectionAngularTolerance, PxFixedJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxFixedJoint, ConcreteTypeName, PxFixedJointGeneratedValues) + struct PxFixedJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxFixedJoint"; } + PxPropertyInfo ProjectionLinearTolerance; + PxPropertyInfo ProjectionAngularTolerance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxFixedJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ProjectionLinearTolerance, inStartIndex + 0 );; + inOperator( ProjectionAngularTolerance, inStartIndex + 1 );; + inOperator( ConcreteTypeName, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxFixedJointGeneratedInfo Info; + const PxFixedJointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxPrismaticJointFlag__EnumConversion[] = { + { "eLIMIT_ENABLED", static_cast( physx::PxPrismaticJointFlag::eLIMIT_ENABLED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxPrismaticJointFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxPrismaticJointFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxPrismaticJoint; + struct PxPrismaticJointGeneratedValues + : PxJointGeneratedValues { + PxReal Position; + PxReal Velocity; + PxJointLinearLimitPair Limit; + PxPrismaticJointFlags PrismaticJointFlags; + PxReal ProjectionLinearTolerance; + PxReal ProjectionAngularTolerance; + const char * ConcreteTypeName; + PxPrismaticJointGeneratedValues( const PxPrismaticJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, Position, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, Velocity, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, Limit, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, PrismaticJointFlags, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, ProjectionLinearTolerance, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, ProjectionAngularTolerance, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, ConcreteTypeName, PxPrismaticJointGeneratedValues) + struct PxPrismaticJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxPrismaticJoint"; } + PxReadOnlyPropertyInfo Position; + PxReadOnlyPropertyInfo Velocity; + PxPropertyInfo Limit; + PxPropertyInfo PrismaticJointFlags; + PxPropertyInfo ProjectionLinearTolerance; + PxPropertyInfo ProjectionAngularTolerance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxPrismaticJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 7; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Position, inStartIndex + 0 );; + inOperator( Velocity, inStartIndex + 1 );; + inOperator( Limit, inStartIndex + 2 );; + inOperator( PrismaticJointFlags, inStartIndex + 3 );; + inOperator( ProjectionLinearTolerance, inStartIndex + 4 );; + inOperator( ProjectionAngularTolerance, inStartIndex + 5 );; + inOperator( ConcreteTypeName, inStartIndex + 6 );; + return 7 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxPrismaticJointGeneratedInfo Info; + const PxPrismaticJointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxRevoluteJointFlag__EnumConversion[] = { + { "eLIMIT_ENABLED", static_cast( physx::PxRevoluteJointFlag::eLIMIT_ENABLED ) }, + { "eDRIVE_ENABLED", static_cast( physx::PxRevoluteJointFlag::eDRIVE_ENABLED ) }, + { "eDRIVE_FREESPIN", static_cast( physx::PxRevoluteJointFlag::eDRIVE_FREESPIN ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxRevoluteJointFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxRevoluteJointFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxRevoluteJoint; + struct PxRevoluteJointGeneratedValues + : PxJointGeneratedValues { + PxReal Angle; + PxReal Velocity; + PxJointAngularLimitPair Limit; + PxReal DriveVelocity; + PxReal DriveForceLimit; + PxReal DriveGearRatio; + PxRevoluteJointFlags RevoluteJointFlags; + PxReal ProjectionLinearTolerance; + PxReal ProjectionAngularTolerance; + const char * ConcreteTypeName; + PxRevoluteJointGeneratedValues( const PxRevoluteJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, Angle, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, Velocity, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, Limit, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, DriveVelocity, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, DriveForceLimit, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, DriveGearRatio, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, RevoluteJointFlags, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, ProjectionLinearTolerance, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, ProjectionAngularTolerance, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, ConcreteTypeName, PxRevoluteJointGeneratedValues) + struct PxRevoluteJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxRevoluteJoint"; } + PxReadOnlyPropertyInfo Angle; + PxReadOnlyPropertyInfo Velocity; + PxPropertyInfo Limit; + PxPropertyInfo DriveVelocity; + PxPropertyInfo DriveForceLimit; + PxPropertyInfo DriveGearRatio; + PxPropertyInfo RevoluteJointFlags; + PxPropertyInfo ProjectionLinearTolerance; + PxPropertyInfo ProjectionAngularTolerance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxRevoluteJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 10; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Angle, inStartIndex + 0 );; + inOperator( Velocity, inStartIndex + 1 );; + inOperator( Limit, inStartIndex + 2 );; + inOperator( DriveVelocity, inStartIndex + 3 );; + inOperator( DriveForceLimit, inStartIndex + 4 );; + inOperator( DriveGearRatio, inStartIndex + 5 );; + inOperator( RevoluteJointFlags, inStartIndex + 6 );; + inOperator( ProjectionLinearTolerance, inStartIndex + 7 );; + inOperator( ProjectionAngularTolerance, inStartIndex + 8 );; + inOperator( ConcreteTypeName, inStartIndex + 9 );; + return 10 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxRevoluteJointGeneratedInfo Info; + const PxRevoluteJointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxSphericalJointFlag__EnumConversion[] = { + { "eLIMIT_ENABLED", static_cast( physx::PxSphericalJointFlag::eLIMIT_ENABLED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxSphericalJointFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxSphericalJointFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxSphericalJoint; + struct PxSphericalJointGeneratedValues + : PxJointGeneratedValues { + PxJointLimitCone LimitCone; + PxReal SwingYAngle; + PxReal SwingZAngle; + PxSphericalJointFlags SphericalJointFlags; + PxReal ProjectionLinearTolerance; + const char * ConcreteTypeName; + PxSphericalJointGeneratedValues( const PxSphericalJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, LimitCone, PxSphericalJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, SwingYAngle, PxSphericalJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, SwingZAngle, PxSphericalJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, SphericalJointFlags, PxSphericalJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, ProjectionLinearTolerance, PxSphericalJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, ConcreteTypeName, PxSphericalJointGeneratedValues) + struct PxSphericalJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxSphericalJoint"; } + PxPropertyInfo LimitCone; + PxReadOnlyPropertyInfo SwingYAngle; + PxReadOnlyPropertyInfo SwingZAngle; + PxPropertyInfo SphericalJointFlags; + PxPropertyInfo ProjectionLinearTolerance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxSphericalJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 6; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( LimitCone, inStartIndex + 0 );; + inOperator( SwingYAngle, inStartIndex + 1 );; + inOperator( SwingZAngle, inStartIndex + 2 );; + inOperator( SphericalJointFlags, inStartIndex + 3 );; + inOperator( ProjectionLinearTolerance, inStartIndex + 4 );; + inOperator( ConcreteTypeName, inStartIndex + 5 );; + return 6 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSphericalJointGeneratedInfo Info; + const PxSphericalJointGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointLimitParameters; + struct PxJointLimitParametersGeneratedValues + { + PxReal Restitution; + PxReal BounceThreshold; + PxReal Stiffness; + PxReal Damping; + PxReal ContactDistance; + PxJointLimitParametersGeneratedValues( const PxJointLimitParameters* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitParameters, Restitution, PxJointLimitParametersGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitParameters, BounceThreshold, PxJointLimitParametersGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitParameters, Stiffness, PxJointLimitParametersGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitParameters, Damping, PxJointLimitParametersGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitParameters, ContactDistance, PxJointLimitParametersGeneratedValues) + struct PxJointLimitParametersGeneratedInfo + + { + static const char* getClassName() { return "PxJointLimitParameters"; } + PxPropertyInfo Restitution; + PxPropertyInfo BounceThreshold; + PxPropertyInfo Stiffness; + PxPropertyInfo Damping; + PxPropertyInfo ContactDistance; + + PxJointLimitParametersGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Restitution, inStartIndex + 0 );; + inOperator( BounceThreshold, inStartIndex + 1 );; + inOperator( Stiffness, inStartIndex + 2 );; + inOperator( Damping, inStartIndex + 3 );; + inOperator( ContactDistance, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointLimitParametersGeneratedInfo Info; + const PxJointLimitParametersGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointLinearLimit; + struct PxJointLinearLimitGeneratedValues + : PxJointLimitParametersGeneratedValues { + PxReal Value; + PxJointLinearLimitGeneratedValues( const PxJointLinearLimit* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLinearLimit, Value, PxJointLinearLimitGeneratedValues) + struct PxJointLinearLimitGeneratedInfo + : PxJointLimitParametersGeneratedInfo + { + static const char* getClassName() { return "PxJointLinearLimit"; } + PxPropertyInfo Value; + + PxJointLinearLimitGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointLimitParametersGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Value, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointLinearLimitGeneratedInfo Info; + const PxJointLinearLimitGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointLinearLimitPair; + struct PxJointLinearLimitPairGeneratedValues + : PxJointLimitParametersGeneratedValues { + PxReal Upper; + PxReal Lower; + PxJointLinearLimitPairGeneratedValues( const PxJointLinearLimitPair* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLinearLimitPair, Upper, PxJointLinearLimitPairGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLinearLimitPair, Lower, PxJointLinearLimitPairGeneratedValues) + struct PxJointLinearLimitPairGeneratedInfo + : PxJointLimitParametersGeneratedInfo + { + static const char* getClassName() { return "PxJointLinearLimitPair"; } + PxPropertyInfo Upper; + PxPropertyInfo Lower; + + PxJointLinearLimitPairGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointLimitParametersGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Upper, inStartIndex + 0 );; + inOperator( Lower, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointLinearLimitPairGeneratedInfo Info; + const PxJointLinearLimitPairGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointAngularLimitPair; + struct PxJointAngularLimitPairGeneratedValues + : PxJointLimitParametersGeneratedValues { + PxReal Upper; + PxReal Lower; + PxJointAngularLimitPairGeneratedValues( const PxJointAngularLimitPair* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointAngularLimitPair, Upper, PxJointAngularLimitPairGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointAngularLimitPair, Lower, PxJointAngularLimitPairGeneratedValues) + struct PxJointAngularLimitPairGeneratedInfo + : PxJointLimitParametersGeneratedInfo + { + static const char* getClassName() { return "PxJointAngularLimitPair"; } + PxPropertyInfo Upper; + PxPropertyInfo Lower; + + PxJointAngularLimitPairGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointLimitParametersGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Upper, inStartIndex + 0 );; + inOperator( Lower, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointAngularLimitPairGeneratedInfo Info; + const PxJointAngularLimitPairGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointLimitCone; + struct PxJointLimitConeGeneratedValues + : PxJointLimitParametersGeneratedValues { + PxReal YAngle; + PxReal ZAngle; + PxJointLimitConeGeneratedValues( const PxJointLimitCone* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitCone, YAngle, PxJointLimitConeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitCone, ZAngle, PxJointLimitConeGeneratedValues) + struct PxJointLimitConeGeneratedInfo + : PxJointLimitParametersGeneratedInfo + { + static const char* getClassName() { return "PxJointLimitCone"; } + PxPropertyInfo YAngle; + PxPropertyInfo ZAngle; + + PxJointLimitConeGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointLimitParametersGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( YAngle, inStartIndex + 0 );; + inOperator( ZAngle, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointLimitConeGeneratedInfo Info; + const PxJointLimitConeGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointLimitPyramid; + struct PxJointLimitPyramidGeneratedValues + : PxJointLimitParametersGeneratedValues { + PxReal YAngleMin; + PxReal YAngleMax; + PxReal ZAngleMin; + PxReal ZAngleMax; + PxJointLimitPyramidGeneratedValues( const PxJointLimitPyramid* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitPyramid, YAngleMin, PxJointLimitPyramidGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitPyramid, YAngleMax, PxJointLimitPyramidGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitPyramid, ZAngleMin, PxJointLimitPyramidGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitPyramid, ZAngleMax, PxJointLimitPyramidGeneratedValues) + struct PxJointLimitPyramidGeneratedInfo + : PxJointLimitParametersGeneratedInfo + { + static const char* getClassName() { return "PxJointLimitPyramid"; } + PxPropertyInfo YAngleMin; + PxPropertyInfo YAngleMax; + PxPropertyInfo ZAngleMin; + PxPropertyInfo ZAngleMax; + + PxJointLimitPyramidGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointLimitParametersGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( YAngleMin, inStartIndex + 0 );; + inOperator( YAngleMax, inStartIndex + 1 );; + inOperator( ZAngleMin, inStartIndex + 2 );; + inOperator( ZAngleMax, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointLimitPyramidGeneratedInfo Info; + const PxJointLimitPyramidGeneratedInfo* getInfo() { return &Info; } + }; + + class PxSpring; + struct PxSpringGeneratedValues + { + PxReal Stiffness; + PxReal Damping; + PxSpringGeneratedValues( const PxSpring* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSpring, Stiffness, PxSpringGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSpring, Damping, PxSpringGeneratedValues) + struct PxSpringGeneratedInfo + + { + static const char* getClassName() { return "PxSpring"; } + PxPropertyInfo Stiffness; + PxPropertyInfo Damping; + + PxSpringGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Stiffness, inStartIndex + 0 );; + inOperator( Damping, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSpringGeneratedInfo Info; + const PxSpringGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxD6JointDriveFlag__EnumConversion[] = { + { "eACCELERATION", static_cast( physx::PxD6JointDriveFlag::eACCELERATION ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxD6JointDriveFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxD6JointDriveFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxD6JointDrive; + struct PxD6JointDriveGeneratedValues + : PxSpringGeneratedValues { + PxReal ForceLimit; + PxD6JointDriveFlags Flags; + PxD6JointDriveGeneratedValues( const PxD6JointDrive* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6JointDrive, ForceLimit, PxD6JointDriveGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6JointDrive, Flags, PxD6JointDriveGeneratedValues) + struct PxD6JointDriveGeneratedInfo + : PxSpringGeneratedInfo + { + static const char* getClassName() { return "PxD6JointDrive"; } + PxPropertyInfo ForceLimit; + PxPropertyInfo Flags; + + PxD6JointDriveGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxSpringGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxSpringGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxSpringGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ForceLimit, inStartIndex + 0 );; + inOperator( Flags, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxD6JointDriveGeneratedInfo Info; + const PxD6JointDriveGeneratedInfo* getInfo() { return &Info; } + }; + + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON +#undef PX_PROPERTY_INFO_NAME diff --git a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h new file mode 100644 index 000000000..12be4126c --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h @@ -0,0 +1,72 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_EXTENSION_METADATAOBJECTS_H +#define PX_EXTENSION_METADATAOBJECTS_H +#include "PxPhysicsAPI.h" +#include "extensions/PxExtensionsAPI.h" +#include "PxMetaDataObjects.h" + +/** \addtogroup physics +@{ +*/ + +namespace physx +{ + +class PxD6Joint; +class PxJointLimitPair; +class PxJointLimitCone; + +struct PxExtensionsPropertyInfoName +{ + enum Enum + { + Unnamed = PxPropertyInfoName::LastPxPropertyInfoName, +#include "PxExtensionAutoGeneratedMetaDataObjectNames.h" + LastPxPropertyInfoName + }; +}; + +#define DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( type, prop, valueStruct ) \ + template<> struct PxPropertyToValueStructMemberMap< PxExtensionsPropertyInfoName::type##_##prop > \ + { \ + PxU32 Offset; \ + PxPropertyToValueStructMemberMap< PxExtensionsPropertyInfoName::type##_##prop >() : Offset( PX_OFFSET_OF_RT( valueStruct, prop ) ) {} \ + template void visitProp( TOperator inOperator, valueStruct& inStruct ) { inOperator( inStruct.prop ); } \ + }; + +#include "PxExtensionAutoGeneratedMetaDataObjects.h" + +#undef DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp b/src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp new file mode 100644 index 000000000..3a9f74b1d --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp @@ -0,0 +1,488 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#include "PxExtensionMetaDataObjects.h" +#include "PxMetaDataCppPrefix.h" +#include "PxExtensionsAPI.h" +using namespace physx; +void setPxJoint_Actors( PxJoint* inObj, PxRigidActor * inArg0, PxRigidActor * inArg1 ) { inObj->setActors( inArg0, inArg1 ); } +void getPxJoint_Actors( const PxJoint* inObj, PxRigidActor *& inArg0, PxRigidActor *& inArg1 ) { inObj->getActors( inArg0, inArg1 ); } +void setPxJoint_LocalPose( PxJoint* inObj, PxJointActorIndex::Enum inIndex, PxTransform inArg ){ inObj->setLocalPose( inIndex, inArg ); } +PxTransform getPxJoint_LocalPose( const PxJoint* inObj, PxJointActorIndex::Enum inIndex ) { return inObj->getLocalPose( inIndex ); } +PxTransform getPxJoint_RelativeTransform( const PxJoint* inObj ) { return inObj->getRelativeTransform(); } +PxVec3 getPxJoint_RelativeLinearVelocity( const PxJoint* inObj ) { return inObj->getRelativeLinearVelocity(); } +PxVec3 getPxJoint_RelativeAngularVelocity( const PxJoint* inObj ) { return inObj->getRelativeAngularVelocity(); } +void setPxJoint_BreakForce( PxJoint* inObj, PxReal inArg0, PxReal inArg1 ) { inObj->setBreakForce( inArg0, inArg1 ); } +void getPxJoint_BreakForce( const PxJoint* inObj, PxReal& inArg0, PxReal& inArg1 ) { inObj->getBreakForce( inArg0, inArg1 ); } +void setPxJoint_ConstraintFlags( PxJoint* inObj, PxConstraintFlags inArg){ inObj->setConstraintFlags( inArg ); } +PxConstraintFlags getPxJoint_ConstraintFlags( const PxJoint* inObj ) { return inObj->getConstraintFlags(); } +void setPxJoint_InvMassScale0( PxJoint* inObj, PxReal inArg){ inObj->setInvMassScale0( inArg ); } +PxReal getPxJoint_InvMassScale0( const PxJoint* inObj ) { return inObj->getInvMassScale0(); } +void setPxJoint_InvInertiaScale0( PxJoint* inObj, PxReal inArg){ inObj->setInvInertiaScale0( inArg ); } +PxReal getPxJoint_InvInertiaScale0( const PxJoint* inObj ) { return inObj->getInvInertiaScale0(); } +void setPxJoint_InvMassScale1( PxJoint* inObj, PxReal inArg){ inObj->setInvMassScale1( inArg ); } +PxReal getPxJoint_InvMassScale1( const PxJoint* inObj ) { return inObj->getInvMassScale1(); } +void setPxJoint_InvInertiaScale1( PxJoint* inObj, PxReal inArg){ inObj->setInvInertiaScale1( inArg ); } +PxReal getPxJoint_InvInertiaScale1( const PxJoint* inObj ) { return inObj->getInvInertiaScale1(); } +PxConstraint * getPxJoint_Constraint( const PxJoint* inObj ) { return inObj->getConstraint(); } +void setPxJoint_Name( PxJoint* inObj, const char * inArg){ inObj->setName( inArg ); } +const char * getPxJoint_Name( const PxJoint* inObj ) { return inObj->getName(); } +PxScene * getPxJoint_Scene( const PxJoint* inObj ) { return inObj->getScene(); } +inline void * getPxJointUserData( const PxJoint* inOwner ) { return inOwner->userData; } +inline void setPxJointUserData( PxJoint* inOwner, void * inData) { inOwner->userData = inData; } + PxJointGeneratedInfo::PxJointGeneratedInfo() + : Actors( "Actors", "actor0", "actor1", setPxJoint_Actors, getPxJoint_Actors) + , LocalPose( "LocalPose", setPxJoint_LocalPose, getPxJoint_LocalPose) + , RelativeTransform( "RelativeTransform", getPxJoint_RelativeTransform) + , RelativeLinearVelocity( "RelativeLinearVelocity", getPxJoint_RelativeLinearVelocity) + , RelativeAngularVelocity( "RelativeAngularVelocity", getPxJoint_RelativeAngularVelocity) + , BreakForce( "BreakForce", "force", "torque", setPxJoint_BreakForce, getPxJoint_BreakForce) + , ConstraintFlags( "ConstraintFlags", setPxJoint_ConstraintFlags, getPxJoint_ConstraintFlags) + , InvMassScale0( "InvMassScale0", setPxJoint_InvMassScale0, getPxJoint_InvMassScale0) + , InvInertiaScale0( "InvInertiaScale0", setPxJoint_InvInertiaScale0, getPxJoint_InvInertiaScale0) + , InvMassScale1( "InvMassScale1", setPxJoint_InvMassScale1, getPxJoint_InvMassScale1) + , InvInertiaScale1( "InvInertiaScale1", setPxJoint_InvInertiaScale1, getPxJoint_InvInertiaScale1) + , Constraint( "Constraint", getPxJoint_Constraint) + , Name( "Name", setPxJoint_Name, getPxJoint_Name) + , Scene( "Scene", getPxJoint_Scene) + , UserData( "UserData", setPxJointUserData, getPxJointUserData ) +{} + PxJointGeneratedValues::PxJointGeneratedValues( const PxJoint* inSource ) + :RelativeTransform( getPxJoint_RelativeTransform( inSource ) ) + ,RelativeLinearVelocity( getPxJoint_RelativeLinearVelocity( inSource ) ) + ,RelativeAngularVelocity( getPxJoint_RelativeAngularVelocity( inSource ) ) + ,ConstraintFlags( getPxJoint_ConstraintFlags( inSource ) ) + ,InvMassScale0( getPxJoint_InvMassScale0( inSource ) ) + ,InvInertiaScale0( getPxJoint_InvInertiaScale0( inSource ) ) + ,InvMassScale1( getPxJoint_InvMassScale1( inSource ) ) + ,InvInertiaScale1( getPxJoint_InvInertiaScale1( inSource ) ) + ,Constraint( getPxJoint_Constraint( inSource ) ) + ,Name( getPxJoint_Name( inSource ) ) + ,Scene( getPxJoint_Scene( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); + getPxJoint_Actors( inSource, Actors[0], Actors[1] ); + for ( PxU32 idx = 0; idx < static_cast( physx::PxJointActorIndex::COUNT ); ++idx ) + LocalPose[idx] = getPxJoint_LocalPose( inSource, static_cast< PxJointActorIndex::Enum >( idx ) ); + getPxJoint_BreakForce( inSource, BreakForce[0], BreakForce[1] ); +} +void setPxContactJoint_Contact( PxContactJoint* inObj, const PxVec3 & inArg){ inObj->setContact( inArg ); } +PxVec3 getPxContactJoint_Contact( const PxContactJoint* inObj ) { return inObj->getContact(); } +void setPxContactJoint_ContactNormal( PxContactJoint* inObj, const PxVec3 & inArg){ inObj->setContactNormal( inArg ); } +PxVec3 getPxContactJoint_ContactNormal( const PxContactJoint* inObj ) { return inObj->getContactNormal(); } +void setPxContactJoint_Penetration( PxContactJoint* inObj, const PxReal inArg){ inObj->setPenetration( inArg ); } +PxReal getPxContactJoint_Penetration( const PxContactJoint* inObj ) { return inObj->getPenetration(); } +void setPxContactJoint_Resititution( PxContactJoint* inObj, const PxReal inArg){ inObj->setResititution( inArg ); } +PxReal getPxContactJoint_Resititution( const PxContactJoint* inObj ) { return inObj->getResititution(); } +void setPxContactJoint_BounceThreshold( PxContactJoint* inObj, const PxReal inArg){ inObj->setBounceThreshold( inArg ); } +PxReal getPxContactJoint_BounceThreshold( const PxContactJoint* inObj ) { return inObj->getBounceThreshold(); } +const char * getPxContactJoint_ConcreteTypeName( const PxContactJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxContactJointGeneratedInfo::PxContactJointGeneratedInfo() + : Contact( "Contact", setPxContactJoint_Contact, getPxContactJoint_Contact) + , ContactNormal( "ContactNormal", setPxContactJoint_ContactNormal, getPxContactJoint_ContactNormal) + , Penetration( "Penetration", setPxContactJoint_Penetration, getPxContactJoint_Penetration) + , Resititution( "Resititution", setPxContactJoint_Resititution, getPxContactJoint_Resititution) + , BounceThreshold( "BounceThreshold", setPxContactJoint_BounceThreshold, getPxContactJoint_BounceThreshold) + , ConcreteTypeName( "ConcreteTypeName", getPxContactJoint_ConcreteTypeName) +{} + PxContactJointGeneratedValues::PxContactJointGeneratedValues( const PxContactJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,Contact( getPxContactJoint_Contact( inSource ) ) + ,ContactNormal( getPxContactJoint_ContactNormal( inSource ) ) + ,Penetration( getPxContactJoint_Penetration( inSource ) ) + ,Resititution( getPxContactJoint_Resititution( inSource ) ) + ,BounceThreshold( getPxContactJoint_BounceThreshold( inSource ) ) + ,ConcreteTypeName( getPxContactJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxD6Joint_Motion( PxD6Joint* inObj, PxD6Axis::Enum inIndex, PxD6Motion::Enum inArg ){ inObj->setMotion( inIndex, inArg ); } +PxD6Motion::Enum getPxD6Joint_Motion( const PxD6Joint* inObj, PxD6Axis::Enum inIndex ) { return inObj->getMotion( inIndex ); } +PxReal getPxD6Joint_TwistAngle( const PxD6Joint* inObj ) { return inObj->getTwistAngle(); } +PxReal getPxD6Joint_Twist( const PxD6Joint* inObj ) { return inObj->getTwist(); } +PxReal getPxD6Joint_SwingYAngle( const PxD6Joint* inObj ) { return inObj->getSwingYAngle(); } +PxReal getPxD6Joint_SwingZAngle( const PxD6Joint* inObj ) { return inObj->getSwingZAngle(); } +void setPxD6Joint_DistanceLimit( PxD6Joint* inObj, const PxJointLinearLimit & inArg){ inObj->setDistanceLimit( inArg ); } +PxJointLinearLimit getPxD6Joint_DistanceLimit( const PxD6Joint* inObj ) { return inObj->getDistanceLimit(); } +void setPxD6Joint_LinearLimit( PxD6Joint* inObj, const PxJointLinearLimit & inArg){ inObj->setLinearLimit( inArg ); } +PxJointLinearLimit getPxD6Joint_LinearLimit( const PxD6Joint* inObj ) { return inObj->getLinearLimit(); } +void setPxD6Joint_TwistLimit( PxD6Joint* inObj, const PxJointAngularLimitPair & inArg){ inObj->setTwistLimit( inArg ); } +PxJointAngularLimitPair getPxD6Joint_TwistLimit( const PxD6Joint* inObj ) { return inObj->getTwistLimit(); } +void setPxD6Joint_SwingLimit( PxD6Joint* inObj, const PxJointLimitCone & inArg){ inObj->setSwingLimit( inArg ); } +PxJointLimitCone getPxD6Joint_SwingLimit( const PxD6Joint* inObj ) { return inObj->getSwingLimit(); } +void setPxD6Joint_PyramidSwingLimit( PxD6Joint* inObj, const PxJointLimitPyramid & inArg){ inObj->setPyramidSwingLimit( inArg ); } +PxJointLimitPyramid getPxD6Joint_PyramidSwingLimit( const PxD6Joint* inObj ) { return inObj->getPyramidSwingLimit(); } +void setPxD6Joint_Drive( PxD6Joint* inObj, PxD6Drive::Enum inIndex, PxD6JointDrive inArg ){ inObj->setDrive( inIndex, inArg ); } +PxD6JointDrive getPxD6Joint_Drive( const PxD6Joint* inObj, PxD6Drive::Enum inIndex ) { return inObj->getDrive( inIndex ); } +void setPxD6Joint_DrivePosition( PxD6Joint* inObj, const PxTransform & inArg){ inObj->setDrivePosition( inArg ); } +PxTransform getPxD6Joint_DrivePosition( const PxD6Joint* inObj ) { return inObj->getDrivePosition(); } +void setPxD6Joint_ProjectionLinearTolerance( PxD6Joint* inObj, PxReal inArg){ inObj->setProjectionLinearTolerance( inArg ); } +PxReal getPxD6Joint_ProjectionLinearTolerance( const PxD6Joint* inObj ) { return inObj->getProjectionLinearTolerance(); } +void setPxD6Joint_ProjectionAngularTolerance( PxD6Joint* inObj, PxReal inArg){ inObj->setProjectionAngularTolerance( inArg ); } +PxReal getPxD6Joint_ProjectionAngularTolerance( const PxD6Joint* inObj ) { return inObj->getProjectionAngularTolerance(); } +const char * getPxD6Joint_ConcreteTypeName( const PxD6Joint* inObj ) { return inObj->getConcreteTypeName(); } + PxD6JointGeneratedInfo::PxD6JointGeneratedInfo() + : Motion( "Motion", setPxD6Joint_Motion, getPxD6Joint_Motion) + , TwistAngle( "TwistAngle", getPxD6Joint_TwistAngle) + , Twist( "Twist", getPxD6Joint_Twist) + , SwingYAngle( "SwingYAngle", getPxD6Joint_SwingYAngle) + , SwingZAngle( "SwingZAngle", getPxD6Joint_SwingZAngle) + , DistanceLimit( "DistanceLimit", setPxD6Joint_DistanceLimit, getPxD6Joint_DistanceLimit) + , LinearLimit( "LinearLimit", setPxD6Joint_LinearLimit, getPxD6Joint_LinearLimit) + , TwistLimit( "TwistLimit", setPxD6Joint_TwistLimit, getPxD6Joint_TwistLimit) + , SwingLimit( "SwingLimit", setPxD6Joint_SwingLimit, getPxD6Joint_SwingLimit) + , PyramidSwingLimit( "PyramidSwingLimit", setPxD6Joint_PyramidSwingLimit, getPxD6Joint_PyramidSwingLimit) + , Drive( "Drive", setPxD6Joint_Drive, getPxD6Joint_Drive) + , DrivePosition( "DrivePosition", setPxD6Joint_DrivePosition, getPxD6Joint_DrivePosition) + , ProjectionLinearTolerance( "ProjectionLinearTolerance", setPxD6Joint_ProjectionLinearTolerance, getPxD6Joint_ProjectionLinearTolerance) + , ProjectionAngularTolerance( "ProjectionAngularTolerance", setPxD6Joint_ProjectionAngularTolerance, getPxD6Joint_ProjectionAngularTolerance) + , ConcreteTypeName( "ConcreteTypeName", getPxD6Joint_ConcreteTypeName) +{} + PxD6JointGeneratedValues::PxD6JointGeneratedValues( const PxD6Joint* inSource ) + :PxJointGeneratedValues( inSource ) + ,TwistAngle( getPxD6Joint_TwistAngle( inSource ) ) + ,Twist( getPxD6Joint_Twist( inSource ) ) + ,SwingYAngle( getPxD6Joint_SwingYAngle( inSource ) ) + ,SwingZAngle( getPxD6Joint_SwingZAngle( inSource ) ) + ,DistanceLimit( getPxD6Joint_DistanceLimit( inSource ) ) + ,LinearLimit( getPxD6Joint_LinearLimit( inSource ) ) + ,TwistLimit( getPxD6Joint_TwistLimit( inSource ) ) + ,SwingLimit( getPxD6Joint_SwingLimit( inSource ) ) + ,PyramidSwingLimit( getPxD6Joint_PyramidSwingLimit( inSource ) ) + ,DrivePosition( getPxD6Joint_DrivePosition( inSource ) ) + ,ProjectionLinearTolerance( getPxD6Joint_ProjectionLinearTolerance( inSource ) ) + ,ProjectionAngularTolerance( getPxD6Joint_ProjectionAngularTolerance( inSource ) ) + ,ConcreteTypeName( getPxD6Joint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); + for ( PxU32 idx = 0; idx < static_cast( physx::PxD6Axis::eCOUNT ); ++idx ) + Motion[idx] = getPxD6Joint_Motion( inSource, static_cast< PxD6Axis::Enum >( idx ) ); + for ( PxU32 idx = 0; idx < static_cast( physx::PxD6Drive::eCOUNT ); ++idx ) + Drive[idx] = getPxD6Joint_Drive( inSource, static_cast< PxD6Drive::Enum >( idx ) ); +} +PxReal getPxDistanceJoint_Distance( const PxDistanceJoint* inObj ) { return inObj->getDistance(); } +void setPxDistanceJoint_MinDistance( PxDistanceJoint* inObj, PxReal inArg){ inObj->setMinDistance( inArg ); } +PxReal getPxDistanceJoint_MinDistance( const PxDistanceJoint* inObj ) { return inObj->getMinDistance(); } +void setPxDistanceJoint_MaxDistance( PxDistanceJoint* inObj, PxReal inArg){ inObj->setMaxDistance( inArg ); } +PxReal getPxDistanceJoint_MaxDistance( const PxDistanceJoint* inObj ) { return inObj->getMaxDistance(); } +void setPxDistanceJoint_Tolerance( PxDistanceJoint* inObj, PxReal inArg){ inObj->setTolerance( inArg ); } +PxReal getPxDistanceJoint_Tolerance( const PxDistanceJoint* inObj ) { return inObj->getTolerance(); } +void setPxDistanceJoint_Stiffness( PxDistanceJoint* inObj, PxReal inArg){ inObj->setStiffness( inArg ); } +PxReal getPxDistanceJoint_Stiffness( const PxDistanceJoint* inObj ) { return inObj->getStiffness(); } +void setPxDistanceJoint_Damping( PxDistanceJoint* inObj, PxReal inArg){ inObj->setDamping( inArg ); } +PxReal getPxDistanceJoint_Damping( const PxDistanceJoint* inObj ) { return inObj->getDamping(); } +void setPxDistanceJoint_DistanceJointFlags( PxDistanceJoint* inObj, PxDistanceJointFlags inArg){ inObj->setDistanceJointFlags( inArg ); } +PxDistanceJointFlags getPxDistanceJoint_DistanceJointFlags( const PxDistanceJoint* inObj ) { return inObj->getDistanceJointFlags(); } +const char * getPxDistanceJoint_ConcreteTypeName( const PxDistanceJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxDistanceJointGeneratedInfo::PxDistanceJointGeneratedInfo() + : Distance( "Distance", getPxDistanceJoint_Distance) + , MinDistance( "MinDistance", setPxDistanceJoint_MinDistance, getPxDistanceJoint_MinDistance) + , MaxDistance( "MaxDistance", setPxDistanceJoint_MaxDistance, getPxDistanceJoint_MaxDistance) + , Tolerance( "Tolerance", setPxDistanceJoint_Tolerance, getPxDistanceJoint_Tolerance) + , Stiffness( "Stiffness", setPxDistanceJoint_Stiffness, getPxDistanceJoint_Stiffness) + , Damping( "Damping", setPxDistanceJoint_Damping, getPxDistanceJoint_Damping) + , DistanceJointFlags( "DistanceJointFlags", setPxDistanceJoint_DistanceJointFlags, getPxDistanceJoint_DistanceJointFlags) + , ConcreteTypeName( "ConcreteTypeName", getPxDistanceJoint_ConcreteTypeName) +{} + PxDistanceJointGeneratedValues::PxDistanceJointGeneratedValues( const PxDistanceJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,Distance( getPxDistanceJoint_Distance( inSource ) ) + ,MinDistance( getPxDistanceJoint_MinDistance( inSource ) ) + ,MaxDistance( getPxDistanceJoint_MaxDistance( inSource ) ) + ,Tolerance( getPxDistanceJoint_Tolerance( inSource ) ) + ,Stiffness( getPxDistanceJoint_Stiffness( inSource ) ) + ,Damping( getPxDistanceJoint_Damping( inSource ) ) + ,DistanceJointFlags( getPxDistanceJoint_DistanceJointFlags( inSource ) ) + ,ConcreteTypeName( getPxDistanceJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxFixedJoint_ProjectionLinearTolerance( PxFixedJoint* inObj, PxReal inArg){ inObj->setProjectionLinearTolerance( inArg ); } +PxReal getPxFixedJoint_ProjectionLinearTolerance( const PxFixedJoint* inObj ) { return inObj->getProjectionLinearTolerance(); } +void setPxFixedJoint_ProjectionAngularTolerance( PxFixedJoint* inObj, PxReal inArg){ inObj->setProjectionAngularTolerance( inArg ); } +PxReal getPxFixedJoint_ProjectionAngularTolerance( const PxFixedJoint* inObj ) { return inObj->getProjectionAngularTolerance(); } +const char * getPxFixedJoint_ConcreteTypeName( const PxFixedJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxFixedJointGeneratedInfo::PxFixedJointGeneratedInfo() + : ProjectionLinearTolerance( "ProjectionLinearTolerance", setPxFixedJoint_ProjectionLinearTolerance, getPxFixedJoint_ProjectionLinearTolerance) + , ProjectionAngularTolerance( "ProjectionAngularTolerance", setPxFixedJoint_ProjectionAngularTolerance, getPxFixedJoint_ProjectionAngularTolerance) + , ConcreteTypeName( "ConcreteTypeName", getPxFixedJoint_ConcreteTypeName) +{} + PxFixedJointGeneratedValues::PxFixedJointGeneratedValues( const PxFixedJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,ProjectionLinearTolerance( getPxFixedJoint_ProjectionLinearTolerance( inSource ) ) + ,ProjectionAngularTolerance( getPxFixedJoint_ProjectionAngularTolerance( inSource ) ) + ,ConcreteTypeName( getPxFixedJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxReal getPxPrismaticJoint_Position( const PxPrismaticJoint* inObj ) { return inObj->getPosition(); } +PxReal getPxPrismaticJoint_Velocity( const PxPrismaticJoint* inObj ) { return inObj->getVelocity(); } +void setPxPrismaticJoint_Limit( PxPrismaticJoint* inObj, const PxJointLinearLimitPair & inArg){ inObj->setLimit( inArg ); } +PxJointLinearLimitPair getPxPrismaticJoint_Limit( const PxPrismaticJoint* inObj ) { return inObj->getLimit(); } +void setPxPrismaticJoint_PrismaticJointFlags( PxPrismaticJoint* inObj, PxPrismaticJointFlags inArg){ inObj->setPrismaticJointFlags( inArg ); } +PxPrismaticJointFlags getPxPrismaticJoint_PrismaticJointFlags( const PxPrismaticJoint* inObj ) { return inObj->getPrismaticJointFlags(); } +void setPxPrismaticJoint_ProjectionLinearTolerance( PxPrismaticJoint* inObj, PxReal inArg){ inObj->setProjectionLinearTolerance( inArg ); } +PxReal getPxPrismaticJoint_ProjectionLinearTolerance( const PxPrismaticJoint* inObj ) { return inObj->getProjectionLinearTolerance(); } +void setPxPrismaticJoint_ProjectionAngularTolerance( PxPrismaticJoint* inObj, PxReal inArg){ inObj->setProjectionAngularTolerance( inArg ); } +PxReal getPxPrismaticJoint_ProjectionAngularTolerance( const PxPrismaticJoint* inObj ) { return inObj->getProjectionAngularTolerance(); } +const char * getPxPrismaticJoint_ConcreteTypeName( const PxPrismaticJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxPrismaticJointGeneratedInfo::PxPrismaticJointGeneratedInfo() + : Position( "Position", getPxPrismaticJoint_Position) + , Velocity( "Velocity", getPxPrismaticJoint_Velocity) + , Limit( "Limit", setPxPrismaticJoint_Limit, getPxPrismaticJoint_Limit) + , PrismaticJointFlags( "PrismaticJointFlags", setPxPrismaticJoint_PrismaticJointFlags, getPxPrismaticJoint_PrismaticJointFlags) + , ProjectionLinearTolerance( "ProjectionLinearTolerance", setPxPrismaticJoint_ProjectionLinearTolerance, getPxPrismaticJoint_ProjectionLinearTolerance) + , ProjectionAngularTolerance( "ProjectionAngularTolerance", setPxPrismaticJoint_ProjectionAngularTolerance, getPxPrismaticJoint_ProjectionAngularTolerance) + , ConcreteTypeName( "ConcreteTypeName", getPxPrismaticJoint_ConcreteTypeName) +{} + PxPrismaticJointGeneratedValues::PxPrismaticJointGeneratedValues( const PxPrismaticJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,Position( getPxPrismaticJoint_Position( inSource ) ) + ,Velocity( getPxPrismaticJoint_Velocity( inSource ) ) + ,Limit( getPxPrismaticJoint_Limit( inSource ) ) + ,PrismaticJointFlags( getPxPrismaticJoint_PrismaticJointFlags( inSource ) ) + ,ProjectionLinearTolerance( getPxPrismaticJoint_ProjectionLinearTolerance( inSource ) ) + ,ProjectionAngularTolerance( getPxPrismaticJoint_ProjectionAngularTolerance( inSource ) ) + ,ConcreteTypeName( getPxPrismaticJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxReal getPxRevoluteJoint_Angle( const PxRevoluteJoint* inObj ) { return inObj->getAngle(); } +PxReal getPxRevoluteJoint_Velocity( const PxRevoluteJoint* inObj ) { return inObj->getVelocity(); } +void setPxRevoluteJoint_Limit( PxRevoluteJoint* inObj, const PxJointAngularLimitPair & inArg){ inObj->setLimit( inArg ); } +PxJointAngularLimitPair getPxRevoluteJoint_Limit( const PxRevoluteJoint* inObj ) { return inObj->getLimit(); } +void setPxRevoluteJoint_DriveVelocity( PxRevoluteJoint* inObj, PxReal inArg){ inObj->setDriveVelocity( inArg ); } +PxReal getPxRevoluteJoint_DriveVelocity( const PxRevoluteJoint* inObj ) { return inObj->getDriveVelocity(); } +void setPxRevoluteJoint_DriveForceLimit( PxRevoluteJoint* inObj, PxReal inArg){ inObj->setDriveForceLimit( inArg ); } +PxReal getPxRevoluteJoint_DriveForceLimit( const PxRevoluteJoint* inObj ) { return inObj->getDriveForceLimit(); } +void setPxRevoluteJoint_DriveGearRatio( PxRevoluteJoint* inObj, PxReal inArg){ inObj->setDriveGearRatio( inArg ); } +PxReal getPxRevoluteJoint_DriveGearRatio( const PxRevoluteJoint* inObj ) { return inObj->getDriveGearRatio(); } +void setPxRevoluteJoint_RevoluteJointFlags( PxRevoluteJoint* inObj, PxRevoluteJointFlags inArg){ inObj->setRevoluteJointFlags( inArg ); } +PxRevoluteJointFlags getPxRevoluteJoint_RevoluteJointFlags( const PxRevoluteJoint* inObj ) { return inObj->getRevoluteJointFlags(); } +void setPxRevoluteJoint_ProjectionLinearTolerance( PxRevoluteJoint* inObj, PxReal inArg){ inObj->setProjectionLinearTolerance( inArg ); } +PxReal getPxRevoluteJoint_ProjectionLinearTolerance( const PxRevoluteJoint* inObj ) { return inObj->getProjectionLinearTolerance(); } +void setPxRevoluteJoint_ProjectionAngularTolerance( PxRevoluteJoint* inObj, PxReal inArg){ inObj->setProjectionAngularTolerance( inArg ); } +PxReal getPxRevoluteJoint_ProjectionAngularTolerance( const PxRevoluteJoint* inObj ) { return inObj->getProjectionAngularTolerance(); } +const char * getPxRevoluteJoint_ConcreteTypeName( const PxRevoluteJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxRevoluteJointGeneratedInfo::PxRevoluteJointGeneratedInfo() + : Angle( "Angle", getPxRevoluteJoint_Angle) + , Velocity( "Velocity", getPxRevoluteJoint_Velocity) + , Limit( "Limit", setPxRevoluteJoint_Limit, getPxRevoluteJoint_Limit) + , DriveVelocity( "DriveVelocity", setPxRevoluteJoint_DriveVelocity, getPxRevoluteJoint_DriveVelocity) + , DriveForceLimit( "DriveForceLimit", setPxRevoluteJoint_DriveForceLimit, getPxRevoluteJoint_DriveForceLimit) + , DriveGearRatio( "DriveGearRatio", setPxRevoluteJoint_DriveGearRatio, getPxRevoluteJoint_DriveGearRatio) + , RevoluteJointFlags( "RevoluteJointFlags", setPxRevoluteJoint_RevoluteJointFlags, getPxRevoluteJoint_RevoluteJointFlags) + , ProjectionLinearTolerance( "ProjectionLinearTolerance", setPxRevoluteJoint_ProjectionLinearTolerance, getPxRevoluteJoint_ProjectionLinearTolerance) + , ProjectionAngularTolerance( "ProjectionAngularTolerance", setPxRevoluteJoint_ProjectionAngularTolerance, getPxRevoluteJoint_ProjectionAngularTolerance) + , ConcreteTypeName( "ConcreteTypeName", getPxRevoluteJoint_ConcreteTypeName) +{} + PxRevoluteJointGeneratedValues::PxRevoluteJointGeneratedValues( const PxRevoluteJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,Angle( getPxRevoluteJoint_Angle( inSource ) ) + ,Velocity( getPxRevoluteJoint_Velocity( inSource ) ) + ,Limit( getPxRevoluteJoint_Limit( inSource ) ) + ,DriveVelocity( getPxRevoluteJoint_DriveVelocity( inSource ) ) + ,DriveForceLimit( getPxRevoluteJoint_DriveForceLimit( inSource ) ) + ,DriveGearRatio( getPxRevoluteJoint_DriveGearRatio( inSource ) ) + ,RevoluteJointFlags( getPxRevoluteJoint_RevoluteJointFlags( inSource ) ) + ,ProjectionLinearTolerance( getPxRevoluteJoint_ProjectionLinearTolerance( inSource ) ) + ,ProjectionAngularTolerance( getPxRevoluteJoint_ProjectionAngularTolerance( inSource ) ) + ,ConcreteTypeName( getPxRevoluteJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxSphericalJoint_LimitCone( PxSphericalJoint* inObj, const PxJointLimitCone & inArg){ inObj->setLimitCone( inArg ); } +PxJointLimitCone getPxSphericalJoint_LimitCone( const PxSphericalJoint* inObj ) { return inObj->getLimitCone(); } +PxReal getPxSphericalJoint_SwingYAngle( const PxSphericalJoint* inObj ) { return inObj->getSwingYAngle(); } +PxReal getPxSphericalJoint_SwingZAngle( const PxSphericalJoint* inObj ) { return inObj->getSwingZAngle(); } +void setPxSphericalJoint_SphericalJointFlags( PxSphericalJoint* inObj, PxSphericalJointFlags inArg){ inObj->setSphericalJointFlags( inArg ); } +PxSphericalJointFlags getPxSphericalJoint_SphericalJointFlags( const PxSphericalJoint* inObj ) { return inObj->getSphericalJointFlags(); } +void setPxSphericalJoint_ProjectionLinearTolerance( PxSphericalJoint* inObj, PxReal inArg){ inObj->setProjectionLinearTolerance( inArg ); } +PxReal getPxSphericalJoint_ProjectionLinearTolerance( const PxSphericalJoint* inObj ) { return inObj->getProjectionLinearTolerance(); } +const char * getPxSphericalJoint_ConcreteTypeName( const PxSphericalJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxSphericalJointGeneratedInfo::PxSphericalJointGeneratedInfo() + : LimitCone( "LimitCone", setPxSphericalJoint_LimitCone, getPxSphericalJoint_LimitCone) + , SwingYAngle( "SwingYAngle", getPxSphericalJoint_SwingYAngle) + , SwingZAngle( "SwingZAngle", getPxSphericalJoint_SwingZAngle) + , SphericalJointFlags( "SphericalJointFlags", setPxSphericalJoint_SphericalJointFlags, getPxSphericalJoint_SphericalJointFlags) + , ProjectionLinearTolerance( "ProjectionLinearTolerance", setPxSphericalJoint_ProjectionLinearTolerance, getPxSphericalJoint_ProjectionLinearTolerance) + , ConcreteTypeName( "ConcreteTypeName", getPxSphericalJoint_ConcreteTypeName) +{} + PxSphericalJointGeneratedValues::PxSphericalJointGeneratedValues( const PxSphericalJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,LimitCone( getPxSphericalJoint_LimitCone( inSource ) ) + ,SwingYAngle( getPxSphericalJoint_SwingYAngle( inSource ) ) + ,SwingZAngle( getPxSphericalJoint_SwingZAngle( inSource ) ) + ,SphericalJointFlags( getPxSphericalJoint_SphericalJointFlags( inSource ) ) + ,ProjectionLinearTolerance( getPxSphericalJoint_ProjectionLinearTolerance( inSource ) ) + ,ConcreteTypeName( getPxSphericalJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointLimitParametersRestitution( const PxJointLimitParameters* inOwner ) { return inOwner->restitution; } +inline void setPxJointLimitParametersRestitution( PxJointLimitParameters* inOwner, PxReal inData) { inOwner->restitution = inData; } +inline PxReal getPxJointLimitParametersBounceThreshold( const PxJointLimitParameters* inOwner ) { return inOwner->bounceThreshold; } +inline void setPxJointLimitParametersBounceThreshold( PxJointLimitParameters* inOwner, PxReal inData) { inOwner->bounceThreshold = inData; } +inline PxReal getPxJointLimitParametersStiffness( const PxJointLimitParameters* inOwner ) { return inOwner->stiffness; } +inline void setPxJointLimitParametersStiffness( PxJointLimitParameters* inOwner, PxReal inData) { inOwner->stiffness = inData; } +inline PxReal getPxJointLimitParametersDamping( const PxJointLimitParameters* inOwner ) { return inOwner->damping; } +inline void setPxJointLimitParametersDamping( PxJointLimitParameters* inOwner, PxReal inData) { inOwner->damping = inData; } +inline PxReal getPxJointLimitParametersContactDistance( const PxJointLimitParameters* inOwner ) { return inOwner->contactDistance; } +inline void setPxJointLimitParametersContactDistance( PxJointLimitParameters* inOwner, PxReal inData) { inOwner->contactDistance = inData; } + PxJointLimitParametersGeneratedInfo::PxJointLimitParametersGeneratedInfo() + : Restitution( "Restitution", setPxJointLimitParametersRestitution, getPxJointLimitParametersRestitution ) + , BounceThreshold( "BounceThreshold", setPxJointLimitParametersBounceThreshold, getPxJointLimitParametersBounceThreshold ) + , Stiffness( "Stiffness", setPxJointLimitParametersStiffness, getPxJointLimitParametersStiffness ) + , Damping( "Damping", setPxJointLimitParametersDamping, getPxJointLimitParametersDamping ) + , ContactDistance( "ContactDistance", setPxJointLimitParametersContactDistance, getPxJointLimitParametersContactDistance ) +{} + PxJointLimitParametersGeneratedValues::PxJointLimitParametersGeneratedValues( const PxJointLimitParameters* inSource ) + :Restitution( inSource->restitution ) + ,BounceThreshold( inSource->bounceThreshold ) + ,Stiffness( inSource->stiffness ) + ,Damping( inSource->damping ) + ,ContactDistance( inSource->contactDistance ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointLinearLimitValue( const PxJointLinearLimit* inOwner ) { return inOwner->value; } +inline void setPxJointLinearLimitValue( PxJointLinearLimit* inOwner, PxReal inData) { inOwner->value = inData; } + PxJointLinearLimitGeneratedInfo::PxJointLinearLimitGeneratedInfo() + : Value( "Value", setPxJointLinearLimitValue, getPxJointLinearLimitValue ) +{} + PxJointLinearLimitGeneratedValues::PxJointLinearLimitGeneratedValues( const PxJointLinearLimit* inSource ) + :PxJointLimitParametersGeneratedValues( inSource ) + ,Value( inSource->value ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointLinearLimitPairUpper( const PxJointLinearLimitPair* inOwner ) { return inOwner->upper; } +inline void setPxJointLinearLimitPairUpper( PxJointLinearLimitPair* inOwner, PxReal inData) { inOwner->upper = inData; } +inline PxReal getPxJointLinearLimitPairLower( const PxJointLinearLimitPair* inOwner ) { return inOwner->lower; } +inline void setPxJointLinearLimitPairLower( PxJointLinearLimitPair* inOwner, PxReal inData) { inOwner->lower = inData; } + PxJointLinearLimitPairGeneratedInfo::PxJointLinearLimitPairGeneratedInfo() + : Upper( "Upper", setPxJointLinearLimitPairUpper, getPxJointLinearLimitPairUpper ) + , Lower( "Lower", setPxJointLinearLimitPairLower, getPxJointLinearLimitPairLower ) +{} + PxJointLinearLimitPairGeneratedValues::PxJointLinearLimitPairGeneratedValues( const PxJointLinearLimitPair* inSource ) + :PxJointLimitParametersGeneratedValues( inSource ) + ,Upper( inSource->upper ) + ,Lower( inSource->lower ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointAngularLimitPairUpper( const PxJointAngularLimitPair* inOwner ) { return inOwner->upper; } +inline void setPxJointAngularLimitPairUpper( PxJointAngularLimitPair* inOwner, PxReal inData) { inOwner->upper = inData; } +inline PxReal getPxJointAngularLimitPairLower( const PxJointAngularLimitPair* inOwner ) { return inOwner->lower; } +inline void setPxJointAngularLimitPairLower( PxJointAngularLimitPair* inOwner, PxReal inData) { inOwner->lower = inData; } + PxJointAngularLimitPairGeneratedInfo::PxJointAngularLimitPairGeneratedInfo() + : Upper( "Upper", setPxJointAngularLimitPairUpper, getPxJointAngularLimitPairUpper ) + , Lower( "Lower", setPxJointAngularLimitPairLower, getPxJointAngularLimitPairLower ) +{} + PxJointAngularLimitPairGeneratedValues::PxJointAngularLimitPairGeneratedValues( const PxJointAngularLimitPair* inSource ) + :PxJointLimitParametersGeneratedValues( inSource ) + ,Upper( inSource->upper ) + ,Lower( inSource->lower ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointLimitConeYAngle( const PxJointLimitCone* inOwner ) { return inOwner->yAngle; } +inline void setPxJointLimitConeYAngle( PxJointLimitCone* inOwner, PxReal inData) { inOwner->yAngle = inData; } +inline PxReal getPxJointLimitConeZAngle( const PxJointLimitCone* inOwner ) { return inOwner->zAngle; } +inline void setPxJointLimitConeZAngle( PxJointLimitCone* inOwner, PxReal inData) { inOwner->zAngle = inData; } + PxJointLimitConeGeneratedInfo::PxJointLimitConeGeneratedInfo() + : YAngle( "YAngle", setPxJointLimitConeYAngle, getPxJointLimitConeYAngle ) + , ZAngle( "ZAngle", setPxJointLimitConeZAngle, getPxJointLimitConeZAngle ) +{} + PxJointLimitConeGeneratedValues::PxJointLimitConeGeneratedValues( const PxJointLimitCone* inSource ) + :PxJointLimitParametersGeneratedValues( inSource ) + ,YAngle( inSource->yAngle ) + ,ZAngle( inSource->zAngle ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointLimitPyramidYAngleMin( const PxJointLimitPyramid* inOwner ) { return inOwner->yAngleMin; } +inline void setPxJointLimitPyramidYAngleMin( PxJointLimitPyramid* inOwner, PxReal inData) { inOwner->yAngleMin = inData; } +inline PxReal getPxJointLimitPyramidYAngleMax( const PxJointLimitPyramid* inOwner ) { return inOwner->yAngleMax; } +inline void setPxJointLimitPyramidYAngleMax( PxJointLimitPyramid* inOwner, PxReal inData) { inOwner->yAngleMax = inData; } +inline PxReal getPxJointLimitPyramidZAngleMin( const PxJointLimitPyramid* inOwner ) { return inOwner->zAngleMin; } +inline void setPxJointLimitPyramidZAngleMin( PxJointLimitPyramid* inOwner, PxReal inData) { inOwner->zAngleMin = inData; } +inline PxReal getPxJointLimitPyramidZAngleMax( const PxJointLimitPyramid* inOwner ) { return inOwner->zAngleMax; } +inline void setPxJointLimitPyramidZAngleMax( PxJointLimitPyramid* inOwner, PxReal inData) { inOwner->zAngleMax = inData; } + PxJointLimitPyramidGeneratedInfo::PxJointLimitPyramidGeneratedInfo() + : YAngleMin( "YAngleMin", setPxJointLimitPyramidYAngleMin, getPxJointLimitPyramidYAngleMin ) + , YAngleMax( "YAngleMax", setPxJointLimitPyramidYAngleMax, getPxJointLimitPyramidYAngleMax ) + , ZAngleMin( "ZAngleMin", setPxJointLimitPyramidZAngleMin, getPxJointLimitPyramidZAngleMin ) + , ZAngleMax( "ZAngleMax", setPxJointLimitPyramidZAngleMax, getPxJointLimitPyramidZAngleMax ) +{} + PxJointLimitPyramidGeneratedValues::PxJointLimitPyramidGeneratedValues( const PxJointLimitPyramid* inSource ) + :PxJointLimitParametersGeneratedValues( inSource ) + ,YAngleMin( inSource->yAngleMin ) + ,YAngleMax( inSource->yAngleMax ) + ,ZAngleMin( inSource->zAngleMin ) + ,ZAngleMax( inSource->zAngleMax ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxSpringStiffness( const PxSpring* inOwner ) { return inOwner->stiffness; } +inline void setPxSpringStiffness( PxSpring* inOwner, PxReal inData) { inOwner->stiffness = inData; } +inline PxReal getPxSpringDamping( const PxSpring* inOwner ) { return inOwner->damping; } +inline void setPxSpringDamping( PxSpring* inOwner, PxReal inData) { inOwner->damping = inData; } + PxSpringGeneratedInfo::PxSpringGeneratedInfo() + : Stiffness( "Stiffness", setPxSpringStiffness, getPxSpringStiffness ) + , Damping( "Damping", setPxSpringDamping, getPxSpringDamping ) +{} + PxSpringGeneratedValues::PxSpringGeneratedValues( const PxSpring* inSource ) + :Stiffness( inSource->stiffness ) + ,Damping( inSource->damping ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxD6JointDriveForceLimit( const PxD6JointDrive* inOwner ) { return inOwner->forceLimit; } +inline void setPxD6JointDriveForceLimit( PxD6JointDrive* inOwner, PxReal inData) { inOwner->forceLimit = inData; } +inline PxD6JointDriveFlags getPxD6JointDriveFlags( const PxD6JointDrive* inOwner ) { return inOwner->flags; } +inline void setPxD6JointDriveFlags( PxD6JointDrive* inOwner, PxD6JointDriveFlags inData) { inOwner->flags = inData; } + PxD6JointDriveGeneratedInfo::PxD6JointDriveGeneratedInfo() + : ForceLimit( "ForceLimit", setPxD6JointDriveForceLimit, getPxD6JointDriveForceLimit ) + , Flags( "Flags", setPxD6JointDriveFlags, getPxD6JointDriveFlags ) +{} + PxD6JointDriveGeneratedValues::PxD6JointDriveGeneratedValues( const PxD6JointDrive* inSource ) + :PxSpringGeneratedValues( inSource ) + ,ForceLimit( inSource->forceLimit ) + ,Flags( inSource->flags ) +{ + PX_UNUSED(inSource); +} diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp new file mode 100644 index 000000000..12f67eab3 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp @@ -0,0 +1,222 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleComponents.h" +#include "PxVehicleDefaults.h" +#include "CmPhysXCommon.h" +#include "CmBitMap.h" +#include "PsFoundation.h" + +namespace physx +{ + +bool PxVehicleChassisData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mMOI.x>0.0f && mMOI.y>0.0f && mMOI.z>0.0f, "Illegal PxVehicleChassisData.mMOI - each element of the chassis moi needs to non-zero", false); + PX_CHECK_AND_RETURN_VAL(mMass>0.0f, "Ilegal PxVehicleChassisData.mMass - chassis mass needs to be non-zero", false); + return true; +} + + +bool PxVehicleEngineData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mPeakTorque>0.0f, "PxVehicleEngineData.mPeakTorque must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mMaxOmega>0.0f, "PxVehicleEngineData.mMaxOmega must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mDampingRateFullThrottle>0.0f, "PxVehicleEngineData.mDampingRateFullThrottle must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mDampingRateZeroThrottleClutchEngaged>0.0f, "PxVehicleEngineData.mDampingRateZeroThrottleClutchEngaged must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mDampingRateZeroThrottleClutchDisengaged>0.0f, "PxVehicleEngineData.mDampingRateZeroThrottleClutchDisengaged must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mRecipMOI>0.0f, "PxVehicleEngineData.mRecipMOI must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mRecipMaxOmega>0.0f, "PxVehicleEngineData.mRecipMaxOmega must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/mMaxOmega)-mRecipMaxOmega) <= 0.001f, "PxVehicleEngineData.mMaxOmega and PxVehicleEngineData.mRecipMaxOmega don't match", false); + return true; +} + +bool PxVehicleGearsData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mFinalRatio>0, "PxVehicleGearsData.mFinalRatio must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mNbRatios>=1, "PxVehicleGearsData.mNbRatios must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mSwitchTime>=0.0f, "PxVehicleGearsData.mSwitchTime must be greater than or equal to zero", false); + + PX_CHECK_AND_RETURN_VAL(mRatios[PxVehicleGearsData::eREVERSE]<0.0f, "PxVehicleGearsData.mRatios[PxVehicleGearsData::eREVERSE] must be less than zero", false); + PX_CHECK_AND_RETURN_VAL(mRatios[PxVehicleGearsData::eNEUTRAL]==0.0f, "PxVehicleGearsData.mRatios[PxVehicleGearsData::eNEUTRAL] must be zero", false); + for(PxU32 i=PxVehicleGearsData::eFIRST;i0.0f, "Forward gear ratios must be greater than zero", false); + } + for(PxU32 i=PxVehicleGearsData::eSECOND;i=0.0f, "PxVehicleAutoBoxData.mUpRatios must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mDownRatios[i]>=0.0f, "PxVehicleAutoBoxData.mDownRatios must be greater than or equal to zero", false); + } + return true; +} + +bool PxVehicleDifferential4WData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mFrontRearSplit<=1.0f && mFrontRearSplit>=0.0f, "PxVehicleDifferential4WData.mFrontRearSplit must be in range (0,1)", false); + PX_CHECK_AND_RETURN_VAL(mFrontLeftRightSplit<=1.0f && mFrontLeftRightSplit>=0.0f, "PxVehicleDifferential4WData.mFrontLeftRightSplit must be in range (0,1)", false); + PX_CHECK_AND_RETURN_VAL(mRearLeftRightSplit<=1.0f || mRearLeftRightSplit>=0.0f, "PxVehicleDifferential4WData.mRearLeftRightSplit must be in range (0,1)", false); + PX_CHECK_AND_RETURN_VAL(mCentreBias>=1.0f, "PxVehicleDifferential4WData.mCentreBias must be greater than or equal to 1.0f", false); + PX_CHECK_AND_RETURN_VAL(mFrontBias>=1.0f, "PxVehicleDifferential4WData.mFrontBias must be greater than or equal to 1.0f", false); + PX_CHECK_AND_RETURN_VAL(mRearBias>=1.0f, "PxVehicleDifferential4WData.mRearBias must be greater than or equal to 1.0f", false); + PX_CHECK_AND_RETURN_VAL(mType> 5); + PxU32 numDrivenWheels=mNbDrivenWheels; + if(drivenState) + { + if(!bitmap.test(wheelId)) + { + numDrivenWheels++; + bitmap.set(wheelId); + mInvNbDrivenWheels=1.0f/(1.0f*numDrivenWheels); + } + } + else if(bitmap.test(wheelId)) + { + numDrivenWheels--; + bitmap.reset(wheelId); + mInvNbDrivenWheels = numDrivenWheels>0.0f ? 1.0f/(1.0f*numDrivenWheels) : 0.0f; + } + mNbDrivenWheels=numDrivenWheels; +} + +bool PxVehicleDifferentialNWData::getIsDrivenWheel(const PxU32 wheelId) const +{ + Cm::BitMap bitmap; + bitmap.setWords(const_cast(mBitmapBuffer),((PX_MAX_NB_WHEELS + 31) & ~31) >> 5); + return (bitmap.test(wheelId) ? true : false); +} + +PxU32 PxVehicleDifferentialNWData::getDrivenWheelStatus() const +{ + PX_ASSERT(((PX_MAX_NB_WHEELS + 31) & ~31) >> 5 == 1); + return mBitmapBuffer[0]; +} + +void PxVehicleDifferentialNWData::setDrivenWheelStatus(PxU32 status) +{ + PX_ASSERT(((PX_MAX_NB_WHEELS + 31) & ~31) >> 5 == 1); + + Cm::BitMap bitmap; + bitmap.setWords(&status, 1); + + for(PxU32 i = 0; i < PX_MAX_NB_WHEELS; ++i) + { + setDrivenWheel(i, !!bitmap.test(i)); + } +} + +bool PxVehicleDifferentialNWData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mNbDrivenWheels<=PX_MAX_NB_WHEELS, "PxVehicleDifferentialNWData.mNbDrivenWheels must be in range (0,20)", false); + return true; +} + +bool PxVehicleAckermannGeometryData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mAccuracy>=0.0f && mAccuracy<=1.0f, "PxVehicleAckermannGeometryData.mAccuracy must be in range (0,1)", false); + PX_CHECK_AND_RETURN_VAL(mFrontWidth>0.0f, "PxVehicleAckermannGeometryData.mFrontWidth must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mRearWidth>0.0f, "PxVehicleAckermannGeometryData.mRearWidth must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mAxleSeparation>0.0f, "PxVehicleAckermannGeometryData.mAxleSeparation must be greater than zero", false); + return true; +} + +bool PxVehicleClutchData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mStrength>0, "PxVehicleClutchData.mStrength must be greater than zero", false); + return true; +} + +bool PxVehicleTireLoadFilterData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mMaxNormalisedLoad>=mMinNormalisedLoad, "PxVehicleTireLoadFilterData.mMaxNormalisedLoad must be greater than or equal to PxVehicleTireLoadFilterData.mMinNormalisedLoad", false); + PX_CHECK_AND_RETURN_VAL(mMaxFilteredNormalisedLoad>0, "PxVehicleTireLoadFilterData.mMaxFilteredNormalisedLoad must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/(mMaxNormalisedLoad - mMinNormalisedLoad)) - mDenominator) < 0.001f, "PxVehicleTireLoadFilterData.mMaxFilteredNormalisedLoad, PxVehicleTireLoadFilterData.mMinNormalisedLoad, and PxVehicleTireLoadFilterData.mDenominator don't match", false); + return true; +} + +bool PxVehicleWheelData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mRadius>0.0f, "PxVehicleWheelData.mRadius must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mWidth>0.0f, "PxVehicleWheelData.mWidth must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mMass>0.0f, "PxVehicleWheelData.mMass must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mMOI>0.0f, "PxVehicleWheelData.mMOI must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mDampingRate>0.0f, "PxVehicleWheelData.mDampingRate must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mMaxBrakeTorque>=0.0f, "PxVehicleWheelData.mMaxBrakeTorque must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mMaxHandBrakeTorque>=0.0f, "PxVehicleWheelData.mMaxHandBrakeTorque must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mToeAngle<=PxPi, "PxVehicleWheelData.mToeAngle must be less than Pi", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/mRadius) - mRecipRadius) < 0.001f, "PxVehicleWheelData.mRadius and PxVehicleWheelData.mRecipRadius don't match", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/mMOI) - mRecipMOI) < 0.001f, "PxVehicleWheelData.mMOI and PxVehicleWheelData.mRecipMOI don't match", false); + return true; +} + +bool PxVehicleSuspensionData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mSpringStrength>=0.0f, "PxVehicleSuspensionData.mSpringStrength must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mSpringDamperRate>=0.0f, "PxVehicleSuspensionData.mSpringDamperRate must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mMaxCompression>=0.0f, "PxVehicleSuspensionData.mMaxCompression must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mMaxDroop>=0.0f, "PxVehicleSuspensionData.mMaxDroop must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mSprungMass>=0.0f, "PxVehicleSuspensionData.mSprungMass must be greater than or equal to zero", false); + return true; +} + +bool PxVehicleAntiRollBarData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(((mWheel0< PX_MAX_NB_WHEELS) && (mWheel1 < PX_MAX_NB_WHEELS)), "PxVehicleAntiRoll.mWheel0 and PxVehicleAntiRoll.mWheel1 are an illegal pair", false); + PX_CHECK_AND_RETURN_VAL((mWheel0 != mWheel1), "PxVehicleAntiRoll.mWheel0 == PxVehicleAntiRoll.mWheel1", false); + PX_CHECK_AND_RETURN_VAL(mStiffness>=0, "PxVehicleAntiRoll::mStiffness must be greater than or equal to zero", false); + return true; +} + +bool PxVehicleTireData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mFrictionVsSlipGraph[0][0]>=0.0f && mFrictionVsSlipGraph[0][1]>=0.0f, "Illegal values for mFrictionVsSlipGraph[0]", false); + PX_CHECK_AND_RETURN_VAL(mFrictionVsSlipGraph[1][0]>=0.0f && mFrictionVsSlipGraph[1][1]>=0.0f, "Illegal values for mFrictionVsSlipGraph[1]", false); + PX_CHECK_AND_RETURN_VAL(mFrictionVsSlipGraph[2][0]>=0.0f && mFrictionVsSlipGraph[2][1]>=0.0f, "Illegal values for mFrictionVsSlipGraph[2]", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/(mFrictionVsSlipGraph[1][0]-mFrictionVsSlipGraph[0][0])) - mFrictionVsSlipGraphRecipx1Minusx0) < 0.001f, "PxVehicleTireData.mFrictionVsSlipGraphRecipx1Minusx0 not set up", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/(mFrictionVsSlipGraph[2][0]-mFrictionVsSlipGraph[1][0])) - mFrictionVsSlipGraphRecipx2Minusx1) < 0.001f, "PxVehicleTireData.mFrictionVsSlipGraphRecipx2Minusx1 not set up", false); + return true; +} +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h new file mode 100644 index 000000000..d823f380a --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_DEFAULTS_H +#define PX_VEHICLE_DEFAULTS_H +/** \addtogroup vehicle + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_DEFAULTS_H diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp new file mode 100644 index 000000000..e92047d90 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp @@ -0,0 +1,213 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleDrive.h" +#include "PxVehicleSDK.h" +#include "PxVehicleDefaults.h" +#include "PxRigidDynamic.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +bool PxVehicleDriveSimData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mEngine.isValid(), "Invalid PxVehicleCoreSimulationData.mEngine", false); + PX_CHECK_AND_RETURN_VAL(mGears.isValid(), "Invalid PxVehicleCoreSimulationData.mGears", false); + PX_CHECK_AND_RETURN_VAL(mClutch.isValid(), "Invalid PxVehicleCoreSimulationData.mClutch", false); + PX_CHECK_AND_RETURN_VAL(mAutoBox.isValid(), "Invalid PxVehicleCoreSimulationData.mAutoBox", false); + return true; +} + +void PxVehicleDriveSimData::setEngineData(const PxVehicleEngineData& engine) +{ + PX_CHECK_AND_RETURN(engine.mTorqueCurve.getNbDataPairs()>0, "Engine torque curve must specify at least one entry"); + PX_CHECK_AND_RETURN(engine.mPeakTorque>0, "Engine peak torque must be greater than zero"); + PX_CHECK_AND_RETURN(engine.mMaxOmega>0, "Engine max omega must be greater than zero"); + PX_CHECK_AND_RETURN(engine.mDampingRateFullThrottle>=0, "Full throttle damping rate must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(engine.mDampingRateZeroThrottleClutchEngaged>=0, "Zero throttle clutch engaged damping rate must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(engine.mDampingRateZeroThrottleClutchDisengaged>=0, "Zero throttle clutch disengaged damping rate must be greater than or equal to zero"); + + mEngine=engine; + mEngine.mRecipMOI=1.0f/engine.mMOI; + mEngine.mRecipMaxOmega=1.0f/engine.mMaxOmega; +} + +void PxVehicleDriveSimData::setGearsData(const PxVehicleGearsData& gears) +{ + PX_CHECK_AND_RETURN(gears.mRatios[PxVehicleGearsData::eREVERSE]<0, "Reverse gear ratio must be negative"); + PX_CHECK_AND_RETURN(gears.mRatios[PxVehicleGearsData::eNEUTRAL]==0, "Neutral gear ratio must be zero"); + PX_CHECK_AND_RETURN(gears.mRatios[PxVehicleGearsData::eFIRST]>0, "First gear ratio must be positive"); + PX_CHECK_AND_RETURN(PxVehicleGearsData::eSECOND>=gears.mNbRatios || (gears.mRatios[PxVehicleGearsData::eSECOND]>0 && gears.mRatios[PxVehicleGearsData::eSECOND] < gears.mRatios[PxVehicleGearsData::eFIRST]), "Second gear ratio must be positive and less than first gear ratio"); + PX_CHECK_AND_RETURN(PxVehicleGearsData::eTHIRD>=gears.mNbRatios || (gears.mRatios[PxVehicleGearsData::eTHIRD]>0 && gears.mRatios[PxVehicleGearsData::eTHIRD] < gears.mRatios[PxVehicleGearsData::eSECOND]), "Third gear ratio must be positive and less than second gear ratio"); + PX_CHECK_AND_RETURN(PxVehicleGearsData::eFOURTH>=gears.mNbRatios || (gears.mRatios[PxVehicleGearsData::eFOURTH]>0 && gears.mRatios[PxVehicleGearsData::eFOURTH] < gears.mRatios[PxVehicleGearsData::eTHIRD]), "Fourth gear ratio must be positive and less than third gear ratio"); + PX_CHECK_AND_RETURN(PxVehicleGearsData::eFIFTH>=gears.mNbRatios || (gears.mRatios[PxVehicleGearsData::eFIFTH]>0 && gears.mRatios[PxVehicleGearsData::eFIFTH] < gears.mRatios[PxVehicleGearsData::eFOURTH]), "Fifth gear ratio must be positive and less than fourth gear ratio"); + PX_CHECK_AND_RETURN(PxVehicleGearsData::eSIXTH>=gears.mNbRatios || (gears.mRatios[PxVehicleGearsData::eSIXTH]>0 && gears.mRatios[PxVehicleGearsData::eSIXTH] < gears.mRatios[PxVehicleGearsData::eFIFTH]), "Sixth gear ratio must be positive and less than fifth gear ratio"); + PX_CHECK_AND_RETURN(gears.mFinalRatio>0, "Final gear ratio must be greater than zero"); + PX_CHECK_AND_RETURN(gears.mNbRatios>=3, "Number of gear ratios must be at least 3 - we need at least reverse, neutral, and a forward gear"); + + mGears=gears; +} + +void PxVehicleDriveSimData::setClutchData(const PxVehicleClutchData& clutch) +{ + PX_CHECK_AND_RETURN(clutch.mStrength>0, "Clutch strength must be greater than zero"); + PX_CHECK_AND_RETURN(PxVehicleClutchAccuracyMode::eBEST_POSSIBLE==clutch.mAccuracyMode || clutch.mEstimateIterations > 0, "Clutch mEstimateIterations must be greater than zero in eESTIMATE mode."); + + mClutch=clutch; +} + +void PxVehicleDriveSimData::setAutoBoxData(const PxVehicleAutoBoxData& autobox) +{ + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eREVERSE]>=0, "Autobox gearup ratio in reverse must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eNEUTRAL]>=0, "Autobox gearup ratio in neutral must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eFIRST]>=0, "Autobox gearup ratio in first must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eSECOND]>=0, "Autobox gearup ratio in second must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eTHIRD]>=0, "Autobox gearup ratio in third must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eFOURTH]>=0, "Autobox gearup ratio in fourth must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eFIFTH]>=0, "Autobox gearup ratio in fifth must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eREVERSE]>=0, "Autobox geardown ratio in reverse must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eNEUTRAL]>=0, "Autobox geardown ratio in neutral must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eFIRST]>=0, "Autobox geardown ratio in first must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eSECOND]>=0, "Autobox geardown ratio in second must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eTHIRD]>=0, "Autobox geardown ratio in third must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eFOURTH]>=0, "Autobox geardown ratio in fourth must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eFIFTH]>=0, "Autobox geardown ratio in fifth must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eSIXTH]>=0, "Autobox geardown ratio in fifth must be greater than zero"); + + mAutoBox=autobox; +} + +/////////////////////////////////// + +PxVehicleDriveDynData::PxVehicleDriveDynData() + : mUseAutoGears(false), + mGearUpPressed(false), + mGearDownPressed(false), + mCurrentGear(PxVehicleGearsData::eNEUTRAL), + mTargetGear(PxVehicleGearsData::eNEUTRAL), + mEnginespeed(0.0f), + mGearSwitchTime(0.0f), + mAutoBoxSwitchTime(0.0f) +{ + for(PxU32 i=0;i=-1.01f && analogVal<=1.01f, "PxVehicleDriveDynData::setAnalogInput - analogVal must be in range (-1,1)"); + PX_CHECK_AND_RETURN(type=0 && diff.mFrontRearSplit<=1.0f), "Diff torque split between front and rear must be in range (0,1)"); + PX_CHECK_AND_RETURN(diff.mType!=PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD || (diff.mCentreBias>=1), "Diff centre bias must be greater than or equal to 1"); + PX_CHECK_AND_RETURN((diff.mType!=PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD && diff.mType!=PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD) || (diff.mFrontBias>=1), "Diff front bias must be greater than or equal to 1"); + PX_CHECK_AND_RETURN((diff.mType!=PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD && diff.mType!=PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD) || (diff.mRearBias>=1), "Diff rear bias must be greater than or equal to 1"); + PX_CHECK_AND_RETURN(diff.mType 0, "Illegal ackermannData.mFrontWidth - must be greater than zero"); + PX_CHECK_AND_RETURN(ackermannData.mRearWidth > 0, "Illegal ackermannData.mRearWidth - must be greater than zero"); + PX_CHECK_AND_RETURN(ackermannData.mAxleSeparation > 0, "Illegal ackermannData.mAxleSeparation - must be greater than zero"); + + mAckermannGeometry = ackermannData; +} + +/////////////////////////////////// + +bool PxVehicleDrive4W::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(PxVehicleDrive::isValid(), "invalid PxVehicleDrive", false); + PX_CHECK_AND_RETURN_VAL(mDriveSimData.isValid(), "Invalid PxVehicleNW.mCoreSimData", false); + return true; +} + +PxVehicleDrive4W* PxVehicleDrive4W::allocate(const PxU32 numWheels) +{ + PX_CHECK_AND_RETURN_NULL(numWheels>=4, "PxVehicleDrive4W::allocate - needs to have at least 4 wheels"); + PX_CHECK_AND_RETURN_NULL(gToleranceScaleLength > 0, "PxVehicleDrive4W::allocate - need to call PxInitVehicleSDK"); + + //Compute the bytes needed. + const PxU32 byteSize = sizeof(PxVehicleDrive4W) + PxVehicleDrive::computeByteSize(numWheels); + + //Allocate the memory. + PxVehicleDrive4W* veh = static_cast(PX_ALLOC(byteSize, "PxVehicleDrive4W")); + Cm::markSerializedMem(veh, byteSize); + new(veh) PxVehicleDrive4W(); + + //Patch up the pointers. + PxU8* ptr = reinterpret_cast(veh) + sizeof(PxVehicleDrive4W); + ptr=PxVehicleDrive::patchupPointers(numWheels, veh, ptr); + + //Initialise wheels. + veh->init(numWheels); + + //Set the vehicle type. + veh->mType = PxVehicleTypes::eDRIVE4W; + + return veh; +} + +void PxVehicleDrive4W::free() +{ + PxVehicleDrive::free(); +} + +void PxVehicleDrive4W::setup +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, + const PxU32 numNonDrivenWheels) +{ + PX_CHECK_AND_RETURN(driveData.isValid(), "PxVehicleDrive4W::setup - invalid driveData"); + PX_CHECK_AND_RETURN(wheelsData.getNbWheels() >= 4, "PxVehicleDrive4W::setup - needs to have at least 4 wheels"); + + //Set up the wheels. + PxVehicleDrive::setup(physics,vehActor,wheelsData,4,numNonDrivenWheels); + + //Start setting up the drive. + PX_CHECK_MSG(driveData.isValid(), "PxVehicle4WDrive - invalid driveData"); + + //Copy the simulation data. + mDriveSimData = driveData; +} + +PxVehicleDrive4W* PxVehicleDrive4W::create +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, + const PxU32 numNonDrivenWheels) +{ + PxVehicleDrive4W* veh4W=PxVehicleDrive4W::allocate(4+numNonDrivenWheels); + veh4W->setup(physics,vehActor,wheelsData,driveData,numNonDrivenWheels); + return veh4W; +} + + +void PxVehicleDrive4W::setToRestState() +{ + //Set core to rest state. + PxVehicleDrive::setToRestState(); +} +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp new file mode 100644 index 000000000..8ca91c6f9 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleDriveNW.h" +#include "PxVehicleDrive.h" +#include "PxVehicleSDK.h" +#include "PxVehicleSuspWheelTire4.h" +#include "PxVehicleSuspLimitConstraintShader.h" +#include "PxVehicleDefaults.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" +#include "PxScene.h" +#include "CmUtils.h" + + +namespace physx +{ + +extern PxF32 gToleranceScaleLength; + +void PxVehicleDriveSimDataNW::setDiffData(const PxVehicleDifferentialNWData& diff) +{ + PX_CHECK_AND_RETURN(diff.isValid(), "Invalid PxVehicleCoreSimulationData.mDiff"); + mDiff=diff; +} + +bool PxVehicleDriveSimDataNW::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mDiff.isValid(), "Invalid PxVehicleDifferentialNWData", false); + PX_CHECK_AND_RETURN_VAL(PxVehicleDriveSimData::isValid(), "Invalid PxVehicleDriveSimDataNW", false); + return true; +} + +/////////////////////////////////// + +bool PxVehicleDriveNW::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(PxVehicleDrive::isValid(), "invalid PxVehicleDrive", false); + PX_CHECK_AND_RETURN_VAL(mDriveSimData.isValid(), "Invalid PxVehicleNW.mCoreSimData", false); + return true; +} + +PxVehicleDriveNW* PxVehicleDriveNW::allocate(const PxU32 numWheels) +{ + PX_CHECK_AND_RETURN_NULL(numWheels>0, "Cars with zero wheels are illegal"); + PX_CHECK_AND_RETURN_NULL(gToleranceScaleLength > 0, "PxVehicleDriveNW::allocate - need to call PxInitVehicleSDK"); + + //Compute the bytes needed. + const PxU32 byteSize = sizeof(PxVehicleDriveNW) + PxVehicleDrive::computeByteSize(numWheels); + + //Allocate the memory. + PxVehicleDriveNW* veh = static_cast(PX_ALLOC(byteSize, "PxVehicleDriveNW")); + Cm::markSerializedMem(veh, byteSize); + new(veh) PxVehicleDriveNW(); + + //Patch up the pointers. + PxU8* ptr = reinterpret_cast(veh) + sizeof(PxVehicleDriveNW); + ptr=PxVehicleDrive::patchupPointers(numWheels, veh, ptr); + + //Initialise + veh->init(numWheels); + + //Set the vehicle type. + veh->mType = PxVehicleTypes::eDRIVENW; + + return veh; +} + +void PxVehicleDriveNW::free() +{ + PxVehicleDrive::free(); +} + +void PxVehicleDriveNW::setup +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimDataNW& driveData, + const PxU32 numWheels) +{ + PX_CHECK_AND_RETURN(driveData.isValid(), "PxVehicleDriveNW::setup - invalid driveData"); + + //Set up the wheels. + PxVehicleDrive::setup(physics,vehActor,wheelsData,numWheels,0); + + //Start setting up the drive. + PX_CHECK_MSG(driveData.isValid(), "PxVehicleNWDrive - invalid driveData"); + + //Copy the simulation data. + mDriveSimData = driveData; +} + +PxVehicleDriveNW* PxVehicleDriveNW::create +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimDataNW& driveData, + const PxU32 numWheels) +{ + PxVehicleDriveNW* vehNW=PxVehicleDriveNW::allocate(numWheels); + vehNW->setup(physics,vehActor,wheelsData,driveData,numWheels); + return vehNW; +} + + +void PxVehicleDriveNW::setToRestState() +{ + //Set core to rest state. + PxVehicleDrive::setToRestState(); +} + + + + + + + + + + + + +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp new file mode 100644 index 000000000..30cb281d8 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleDriveTank.h" +#include "PxVehicleWheels.h" +#include "PxVehicleSDK.h" +#include "PxVehicleDefaults.h" +#include "PxRigidDynamic.h" +#include "CmPhysXCommon.h" +#include "CmUtils.h" +#include "PsFoundation.h" + +namespace physx +{ + +extern PxF32 gToleranceScaleLength; + +bool PxVehicleDriveTank::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(PxVehicleDrive::isValid(), "invalid PxVehicleDrive", false); + PX_CHECK_AND_RETURN_VAL(mDriveSimData.isValid(), "Invalid PxVehicleDriveTank.mCoreSimData", false); + return true; +} + +PxVehicleDriveTank* PxVehicleDriveTank::allocate(const PxU32 numWheels) +{ + PX_CHECK_AND_RETURN_NULL(numWheels>0, "Cars with zero wheels are illegal"); + PX_CHECK_AND_RETURN_NULL(0 == (numWheels % 2), "PxVehicleDriveTank::allocate - needs to have even number of wheels"); + PX_CHECK_AND_RETURN_NULL(gToleranceScaleLength > 0, "PxVehicleDriveTank::allocate - need to call PxInitVehicleSDK"); + + //Compute the bytes needed. + const PxU32 byteSize = sizeof(PxVehicleDriveTank) + PxVehicleDrive::computeByteSize(numWheels); + + //Allocate the memory. + PxVehicleDriveTank* veh = static_cast(PX_ALLOC(byteSize, "PxVehicleDriveTank")); + Cm::markSerializedMem(veh, byteSize); + new(veh) PxVehicleDriveTank(); + + //Patch up the pointers. + PxU8* ptr = reinterpret_cast(veh) + sizeof(PxVehicleDriveTank); + PxVehicleDrive::patchupPointers(numWheels, veh, ptr); + + //Initialise. + veh->init(numWheels); + + //Set the vehicle type. + veh->mType = PxVehicleTypes::eDRIVETANK; + + //Set the default drive model. + veh->mDriveModel = PxVehicleDriveTankControlModel::eSTANDARD; + + return veh; +} + +void PxVehicleDriveTank::free() +{ + PxVehicleDrive::free(); +} + +void PxVehicleDriveTank::setup +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData& driveData, + const PxU32 numDrivenWheels) +{ + PX_CHECK_AND_RETURN(driveData.isValid(), "PxVehicleDriveTank::setup - illegal drive data"); + + //Set up the wheels. + PxVehicleDrive::setup(physics,vehActor,wheelsData,numDrivenWheels,0); + + //Start setting up the drive. + PX_CHECK_MSG(driveData.isValid(), "PxVehicle4WDrive - invalid driveData"); + + //Copy the simulation data. + mDriveSimData = driveData; +} + +PxVehicleDriveTank* PxVehicleDriveTank::create +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData& driveData, + const PxU32 numDrivenWheels) +{ + PxVehicleDriveTank* tank=PxVehicleDriveTank::allocate(numDrivenWheels); + tank->setup(physics,vehActor,wheelsData,driveData,numDrivenWheels); + return tank; +} + + +void PxVehicleDriveTank::setToRestState() +{ + //Set core to rest state. + PxVehicleDrive::setToRestState(); +} +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h new file mode 100644 index 000000000..84c08f726 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h @@ -0,0 +1,433 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_LINEAR_MATH_H +#define PX_VEHICLE_LINEAR_MATH_H +/** \addtogroup vehicle + @{ +*/ + +#include "PxVehicleSDK.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#define MAX_VECTORN_SIZE (PX_MAX_NB_WHEELS+3) + +class VectorN +{ +public: + + VectorN(const PxU32 size) + : mSize(size) + { + PX_ASSERT(mSize <= MAX_VECTORN_SIZE); + } + ~VectorN() + { + } + + VectorN(const VectorN& src) + { + for(PxU32 i = 0; i < src.mSize; i++) + { + mValues[i] = src.mValues[i]; + } + mSize = src.mSize; + } + + PX_FORCE_INLINE VectorN& operator=(const VectorN& src) + { + for(PxU32 i = 0; i < src.mSize; i++) + { + mValues[i] = src.mValues[i]; + } + mSize = src.mSize; + return *this; + } + + PX_FORCE_INLINE PxF32& operator[] (const PxU32 i) + { + PX_ASSERT(i < mSize); + return (mValues[i]); + } + + PX_FORCE_INLINE const PxF32& operator[] (const PxU32 i) const + { + PX_ASSERT(i < mSize); + return (mValues[i]); + } + + PX_FORCE_INLINE PxU32 getSize() const {return mSize;} + +private: + + PxF32 mValues[MAX_VECTORN_SIZE]; + PxU32 mSize; +}; + +class MatrixNN +{ +public: + + MatrixNN() + : mSize(0) + { + } + MatrixNN(const PxU32 size) + : mSize(size) + { + PX_ASSERT(mSize <= MAX_VECTORN_SIZE); + } + MatrixNN(const MatrixNN& src) + { + for(PxU32 i = 0; i < src.mSize; i++) + { + for(PxU32 j = 0; j < src.mSize; j++) + { + mValues[i][j] = src.mValues[i][j]; + } + } + mSize=src.mSize; + } + ~MatrixNN() + { + } + + PX_FORCE_INLINE MatrixNN& operator=(const MatrixNN& src) + { + for(PxU32 i = 0;i < src.mSize; i++) + { + for(PxU32 j = 0;j < src.mSize; j++) + { + mValues[i][j] = src.mValues[i][j]; + } + } + mSize = src.mSize; + return *this; + } + + PX_FORCE_INLINE PxF32 get(const PxU32 i, const PxU32 j) const + { + PX_ASSERT(i < mSize); + PX_ASSERT(j < mSize); + return mValues[i][j]; + } + PX_FORCE_INLINE void set(const PxU32 i, const PxU32 j, const PxF32 val) + { + PX_ASSERT(i < mSize); + PX_ASSERT(j < mSize); + mValues[i][j] = val; + } + + PX_FORCE_INLINE PxU32 getSize() const {return mSize;} + + PX_FORCE_INLINE void setSize(const PxU32 size) + { + PX_ASSERT(size <= MAX_VECTORN_SIZE); + mSize = size; + } + +public: + + PxF32 mValues[MAX_VECTORN_SIZE][MAX_VECTORN_SIZE]; + PxU32 mSize; +}; + + +/* + LUPQ decomposition + + Based upon "Outer Product LU with Complete Pivoting," from Matrix Computations (4th Edition), Golub and Van Loan + + Solve A*x = b using: + + MatrixNNLUSolver solver; + solver.decomposeLU(A); + solver.solve(b, x); +*/ +class MatrixNNLUSolver +{ +private: + + MatrixNN mLU; + PxU32 mP[MAX_VECTORN_SIZE-1]; // Row permutation + PxU32 mQ[MAX_VECTORN_SIZE-1]; // Column permutation + PxF32 mdetM; + +public: + + MatrixNNLUSolver(){} + ~MatrixNNLUSolver(){} + + PxF32 getDet() const {return mdetM;} + + void decomposeLU(const MatrixNN& A) + { + const PxU32 D = A.mSize; + + mLU = A; + + mdetM = 1.0f; + + for (PxU32 k = 0; k < D-1; ++k) + { + PxU32 pivot_row = k; + PxU32 pivot_col = k; + float abs_pivot_elem = 0.0f; + for (PxU32 c = k; c < D; ++c) + { + for (PxU32 r = k; r < D; ++r) + { + const PxF32 abs_elem = PxAbs(mLU.get(r,c)); + if (abs_elem > abs_pivot_elem) + { + abs_pivot_elem = abs_elem; + pivot_row = r; + pivot_col = c; + } + } + } + + mP[k] = pivot_row; + if (pivot_row != k) + { + mdetM = -mdetM; + for (PxU32 c = 0; c < D; ++c) + { + //swap(m_LU(k,c), m_LU(pivot_row,c)); + const PxF32 pivotrowc = mLU.get(pivot_row, c); + mLU.set(pivot_row, c, mLU.get(k, c)); + mLU.set(k, c, pivotrowc); + } + } + + mQ[k] = pivot_col; + if (pivot_col != k) + { + mdetM = -mdetM; + for (PxU32 r = 0; r < D; ++r) + { + //swap(m_LU(r,k), m_LU(r,pivot_col)); + const PxF32 rpivotcol = mLU.get(r, pivot_col); + mLU.set(r,pivot_col, mLU.get(r,k)); + mLU.set(r, k, rpivotcol); + } + } + + mdetM *= mLU.get(k,k); + + if (mLU.get(k,k) != 0.0f) + { + for (PxU32 r = k+1; r < D; ++r) + { + mLU.set(r, k, mLU.get(r,k) / mLU.get(k,k)); + for (PxU32 c = k+1; c < D; ++c) + { + //m_LU(r,c) -= m_LU(r,k)*m_LU(k,c); + const PxF32 rc = mLU.get(r, c); + const PxF32 rk = mLU.get(r, k); + const PxF32 kc = mLU.get(k, c); + mLU.set(r, c, rc - rk*kc); + } + } + } + } + + mdetM *= mLU.get(D-1,D-1); + } + + //Given a matrix A and a vector b find x that satisfies Ax = b, where the matrix A is the matrix that was passed to decomposeLU. + //Returns true if the lu decomposition indicates that the matrix has an inverse and x was successfully computed. + //Returns false if the lu decomposition resulted in zero determinant ie the matrix has no inverse and no solution exists for x. + //Returns false if the size of either b or x doesn't match the size of the matrix passed to decomposeLU. + //If false is returned then each relevant element of x is set to zero. + bool solve(const VectorN& b, VectorN& x) const + { + const PxU32 D = x.getSize(); + + if((b.getSize() != x.getSize()) || (b.getSize() != mLU.getSize()) || (0.0f == mdetM)) + { + for(PxU32 i = 0; i < D; i++) + { + x[i] = 0.0f; + } + return false; + } + + x = b; + + // Perform row permutation to get Pb + for(PxU32 i = 0; i < D-1; ++i) + { + //swap(x(i), x(m_P[i])); + const PxF32 xp = x[mP[i]]; + x[mP[i]] = x[i]; + x[i] = xp; + } + + // Forward substitute to get (L^-1)Pb + for (PxU32 r = 1; r < D; ++r) + { + for (PxU32 i = 0; i < r; ++i) + { + x[r] -= mLU.get(r,i)*x[i]; + } + } + + // Back substitute to get (U^-1)(L^-1)Pb + for (PxU32 r = D; r-- > 0;) + { + for (PxU32 i = r+1; i < D; ++i) + { + x[r] -= mLU.get(r,i)*x[i]; + } + x[r] /= mLU.get(r,r); + } + + // Perform column permutation to get the solution (Q^T)(U^-1)(L^-1)Pb + for (PxU32 i = D-1; i-- > 0;) + { + //swap(x(i), x(m_Q[i])); + const PxF32 xq = x[mQ[i]]; + x[mQ[i]] = x[i]; + x[i] = xq; + } + + return true; + } + +}; + + +class MatrixNGaussSeidelSolver +{ +public: + + void solve(const PxU32 maxIterations, const PxF32 tolerance, const MatrixNN& A, const VectorN& b, VectorN& result) const + { + const PxU32 N = A.getSize(); + + VectorN DInv(N); + PxF32 bLength2 = 0.0f; + for(PxU32 i = 0; i < N; i++) + { + DInv[i] = 1.0f/A.get(i,i); + bLength2 += (b[i] * b[i]); + } + + PxU32 iteration = 0; + PxF32 error = PX_MAX_F32; + while(iteration < maxIterations && tolerance < error) + { + for(PxU32 i = 0; i < N; i++) + { + PxF32 l = 0.0f; + for(PxU32 j = 0; j < i; j++) + { + l += A.get(i,j) * result[j]; + } + + PxF32 u = 0.0f; + for(PxU32 j = i + 1; j < N; j++) + { + u += A.get(i,j) * result[j]; + } + + result[i] = DInv[i] * (b[i] - l - u); + } + + //Compute the error. + PxF32 rLength2 = 0; + for(PxU32 i = 0; i < N; i++) + { + PxF32 e = -b[i]; + for(PxU32 j = 0; j < N; j++) + { + e += A.get(i,j) * result[j]; + } + rLength2 += e * e; + } + error = (rLength2 / (bLength2 + 1e-10f)); + + iteration++; + } + } +}; + +class Matrix33Solver +{ +public: + + bool solve(const MatrixNN& _A_, const VectorN& _b_, VectorN& result) const + { + const PxF32 a = _A_.get(0,0); + const PxF32 b = _A_.get(0,1); + const PxF32 c = _A_.get(0,2); + + const PxF32 d = _A_.get(1,0); + const PxF32 e = _A_.get(1,1); + const PxF32 f = _A_.get(1,2); + + const PxF32 g = _A_.get(2,0); + const PxF32 h = _A_.get(2,1); + const PxF32 k = _A_.get(2,2); + + const PxF32 detA = a*(e*k - f*h) - b*(k*d - f*g) + c*(d*h - e*g); + if(0.0f == detA) + { + return false; + } + const PxF32 detAInv = 1.0f/detA; + + const PxF32 A = (e*k - f*h); + const PxF32 D = -(b*k - c*h); + const PxF32 G = (b*f - c*e); + const PxF32 B = -(d*k - f*g); + const PxF32 E = (a*k - c*g); + const PxF32 H = -(a*f - c*d); + const PxF32 C = (d*h - e*g); + const PxF32 F = -(a*h - b*g); + const PxF32 K = (a*e - b*d); + + result[0] = detAInv*(A*_b_[0] + D*_b_[1] + G*_b_[2]); + result[1] = detAInv*(B*_b_[0] + E*_b_[1] + H*_b_[2]); + result[2] = detAInv*(C*_b_[0] + F*_b_[1] + K*_b_[2]); + + return true; + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif //PX_VEHICLE_LINEAR_MATH_H diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp new file mode 100644 index 000000000..172b5f123 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp @@ -0,0 +1,541 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleComponents.h" +#include "PxVehicleDrive.h" +#include "PxVehicleNoDrive.h" +#include "PxVehicleDrive4W.h" +#include "PxVehicleDriveNW.h" +#include "PxVehicleDriveTank.h" +#include "PxVehicleSuspWheelTire4.h" +#include "PxVehicleSuspLimitConstraintShader.h" + +#include "PxMetaData.h" + +using namespace physx; + +namespace +{ + typedef PxFixedSizeLookupTable PxVehicleEngineTable; + class ShadowLookupTable : public PxVehicleEngineTable + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowLookupTable) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream_, PxVehicleEngineTable, PxReal, mDataPairs, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, PxVehicleEngineTable, PxU32, mNbDataPairs, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream_, PxVehicleEngineTable, PxU32, mPad, PxMetaDataFlag::ePADDING) + } + }; +} + +static void getBinaryMetaData_PxFixedSizeLookupTable(PxOutputStream& stream) +{ + ShadowLookupTable::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxFixedSizeLookupTable, ShadowLookupTable) +} + +void PxVehicleDriveSimData::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PxFixedSizeLookupTable(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxVehicleClutchAccuracyMode::Enum, PxU32) + + //PxVehicleEngineData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleEngineData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, ShadowLookupTable, mTorqueCurve, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mMOI, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mPeakTorque, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mMaxOmega, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mDampingRateFullThrottle, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mDampingRateZeroThrottleClutchEngaged, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mDampingRateZeroThrottleClutchDisengaged, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mRecipMOI, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mRecipMaxOmega, 0) + + + //PxVehicleGearsData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleGearsData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleGearsData, PxReal, mRatios, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleGearsData, PxReal, mFinalRatio, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleGearsData, PxU32, mNbRatios, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleGearsData, PxReal, mSwitchTime, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleGearsData, PxReal, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleClutchData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleClutchData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleClutchData, PxReal, mStrength, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleClutchData, PxVehicleClutchAccuracyMode::Enum, mAccuracyMode, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleClutchData, PxU32, mEstimateIterations, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleClutchData, PxU8, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleAutoBoxData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleAutoBoxData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleAutoBoxData, PxReal, mUpRatios, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleAutoBoxData, PxReal, mDownRatios, 0) + + //PxVehicleDriveSimData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDriveSimData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData, PxVehicleEngineData, mEngine, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData, PxVehicleGearsData, mGears, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData, PxVehicleClutchData, mClutch, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData, PxVehicleAutoBoxData, mAutoBox, 0) +} + +void PxVehicleDrive::getBinaryMetaData(PxOutputStream& stream) +{ + //PxVehicleDriveDynData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDriveDynData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleDriveDynData, PxReal, mControlAnalogVals, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, bool, mUseAutoGears, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, bool, mGearUpPressed, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, bool, mGearDownPressed, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, PxU32, mCurrentGear, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, PxU32, mTargetGear, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, PxReal, mEnginespeed, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, PxReal, mGearSwitchTime, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, PxReal, mAutoBoxSwitchTime, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleDriveDynData, PxU32, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleDrive + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleDrive) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDrive, PxVehicleWheels) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDrive, PxVehicleDriveDynData, mDriveDynData, 0) +} + +void PxVehicleDriveSimData4W::getBinaryMetaData(PxOutputStream& stream) +{ + //PxVehicleDifferential4WData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDifferential4WData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mFrontRearSplit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mFrontLeftRightSplit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mRearLeftRightSplit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mCentreBias, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mFrontBias, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mRearBias, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxU32, mType, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleDifferential4WData, PxReal, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleAckermannGeometryData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleAckermannGeometryData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAckermannGeometryData, PxReal, mAccuracy, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAckermannGeometryData, PxReal, mFrontWidth, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAckermannGeometryData, PxReal, mRearWidth, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAckermannGeometryData, PxReal, mAxleSeparation, 0) + + //PxVehicleDriveSimData4W + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDriveSimData4W) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDriveSimData4W, PxVehicleDriveSimData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData4W, PxVehicleDifferential4WData, mDiff, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData4W, PxVehicleAckermannGeometryData, mAckermannGeometry, 0) +} + +void PxVehicleDriveSimDataNW::getBinaryMetaData(PxOutputStream& stream) +{ + //PxVehicleDifferentialWData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDifferentialNWData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleDifferentialNWData, PxU32, mBitmapBuffer, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferentialNWData, PxU32, mNbDrivenWheels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferentialNWData, PxReal, mInvNbDrivenWheels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferentialNWData, PxU32, mPad, 0) + + //PxVehicleDriveSimDataNW + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDriveSimDataNW) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDriveSimDataNW, PxVehicleDriveSimData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimDataNW, PxVehicleDifferentialNWData, mDiff, 0) +} + +void PxVehicleNoDrive::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleDrive::getBinaryMetaData(stream); + PxVehicleWheels::getBinaryMetaData(stream); + PxVehicleDriveSimData::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleNoDrive) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleNoDrive, PxVehicleWheels) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleNoDrive, PxReal, mSteerAngles, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleNoDrive, PxReal, mDriveTorques, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleNoDrive, PxReal, mBrakeTorques, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleNoDrive, PxU32, mPad, PxMetaDataFlag::ePADDING) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mSteerAngles, mWheelsSimData.mNbWheels4, 0, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mSteerAngles, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mSteerAngles, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mSteerAngles, mWheelsSimData.mNbWheels4, 0, 0) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mDriveTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mDriveTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mDriveTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mDriveTorques, mWheelsSimData.mNbWheels4, 0, 0) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mBrakeTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mBrakeTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mBrakeTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mBrakeTorques, mWheelsSimData.mNbWheels4, 0, 0) +} + +void PxVehicleNoDrive::exportExtraData(PxSerializationContext& stream) +{ + PxVehicleWheels::exportExtraData(stream); + + PxU32 size = sizeof(PxReal)*4*mWheelsSimData.mNbWheels4; + stream.alignData(); + stream.writeData(mSteerAngles, size); + stream.writeData(mDriveTorques, size); + stream.writeData(mBrakeTorques, size); +} + +void PxVehicleNoDrive::importExtraData(PxDeserializationContext& context) +{ + PxVehicleWheels::importExtraData(context); + + context.alignExtraData(); + mSteerAngles = context.readExtraData(4*mWheelsSimData.mNbWheels4); + mDriveTorques = context.readExtraData(4*mWheelsSimData.mNbWheels4); + mBrakeTorques = context.readExtraData(4*mWheelsSimData.mNbWheels4); +} + +PxVehicleNoDrive* PxVehicleNoDrive::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PxVehicleNoDrive* obj = new (address) PxVehicleNoDrive(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PxVehicleNoDrive); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void PxVehicleDrive4W::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleDrive::getBinaryMetaData(stream); + PxVehicleWheels::getBinaryMetaData(stream); + PxVehicleDriveSimData::getBinaryMetaData(stream); + PxVehicleDriveSimData4W::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleDrive4W) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDrive4W, PxVehicleDrive) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDrive4W, PxVehicleDriveSimData4W, mDriveSimData, 0) +} + +PxVehicleDrive4W* PxVehicleDrive4W::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PxVehicleDrive4W* obj = new (address) PxVehicleDrive4W(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PxVehicleDrive4W); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void PxVehicleDriveNW::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleDriveSimDataNW::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleDriveNW) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDriveNW, PxVehicleDrive) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveNW, PxVehicleDriveSimDataNW, mDriveSimData, 0) +} + +PxVehicleDriveNW* PxVehicleDriveNW::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PxVehicleDriveNW* obj = new (address) PxVehicleDriveNW(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PxVehicleDriveNW); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void PxVehicleDriveTank::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleDriveTank) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDriveTank, PxVehicleDrive) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveTank, PxVehicleDriveSimData, mDriveSimData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveTank, PxU32, mDriveModel, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleDriveTank, PxU32, mPad, PxMetaDataFlag::ePADDING) +} + +PxVehicleDriveTank* PxVehicleDriveTank::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PxVehicleDriveTank* obj = new (address) PxVehicleDriveTank(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PxVehicleDriveTank); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void PxVehicleWheelsSimData::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleWheels4SimData::getBinaryMetaData(stream); + //PxVehicleTireLoadFilterData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleTireLoadFilterData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireLoadFilterData, PxReal, mMinNormalisedLoad, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireLoadFilterData, PxReal, mMinFilteredNormalisedLoad, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireLoadFilterData, PxReal, mMaxNormalisedLoad, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireLoadFilterData, PxReal, mMaxFilteredNormalisedLoad, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireLoadFilterData, PxReal, mDenominator, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleTireLoadFilterData, PxU32, mPad, PxMetaDataFlag::ePADDING) + + //Add anti-roll here to save us having to add an extra function. + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleAntiRollBarData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAntiRollBarData, PxU32, mWheel0, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAntiRollBarData, PxU32, mWheel1, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAntiRollBarData, PxReal, mStiffness, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleAntiRollBarData, PxU32, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleWheelsSimData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleWheelsSimData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxVehicleTireLoadFilterData, mNormalisedLoadFilter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxVehicleWheels4SimData, mWheels4SimData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mNbWheels4, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mNbActiveWheels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxVehicleAntiRollBarData, mAntiRollBars, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mNbAntiRollBars4, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mNbActiveAntiRollBars, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mActiveWheelsBitmapBuffer, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxReal, mThresholdLongitudinalSpeed, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mLowForwardSpeedSubStepCount, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mHighForwardSpeedSubStepCount, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mMinLongSlipDenominator, 0) + +#if PX_P64_FAMILY + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheelsSimData, PxU32, mPad, PxMetaDataFlag::ePADDING) +#else + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheelsSimData, PxU32, mPad, PxMetaDataFlag::ePADDING) +#endif + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsSimData, PxVehicleWheels4SimData, mWheels4SimData, mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsSimData, PxVehicleAntiRollBarData, mAntiRollBars, mNbAntiRollBars4, 0, 0) +} + +void PxVehicleWheelsDynData::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleWheels4DynData::getBinaryMetaData(stream); + PxVehicleConstraintShader::getBinaryMetaData(stream); + + //PxVehicleTireForceCalculator + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleTireForceCalculator) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireForceCalculator, void*, mShaderData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireForceCalculator, PxU32, mShader, PxMetaDataFlag::ePTR) + +#if !PX_P64_FAMILY + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleTireForceCalculator, PxU32, mPad, PxMetaDataFlag::ePADDING) +#endif + + //PxVehicleWheelsDynData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleWheelsDynData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsDynData, PxVehicleWheels4DynData, mWheels4DynData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsDynData, PxVehicleTireForceCalculator, mTireForceCalculators, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsDynData, PxU32, mUserDatas, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsDynData, PxU32, mNbWheels4, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsDynData, PxU32, mNbActiveWheels, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheelsDynData, PxU32, mPad, PxMetaDataFlag::ePADDING) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, PxVehicleWheels4DynData, mWheels4DynData, mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, PxVehicleWheelsDynData, PxVehicleTireForceCalculator, mTireForceCalculators, 0) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mTireForceCalculators, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mTireForceCalculators, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mTireForceCalculators, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mTireForceCalculators, mNbWheels4, PxMetaDataFlag::ePTR, 0) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mUserDatas, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mUserDatas, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mUserDatas, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mUserDatas, mNbWheels4, PxMetaDataFlag::ePTR, 0) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, PxVehicleConstraintShader, mWheels4DynData, mNbWheels4, 0, 0) +} + +void PxVehicleWheels::exportExtraData(PxSerializationContext& stream) +{ + PxU32 size = computeByteSize(mWheelsSimData.mNbActiveWheels); + stream.alignData(); + stream.writeData(mWheelsSimData.mWheels4SimData, size); +} + +void PxVehicleWheels::importExtraData(PxDeserializationContext& context) +{ + PxU32 size = computeByteSize(mWheelsSimData.mNbActiveWheels); + PxU8* ptr = context.readExtraData(size); + patchupPointers(mWheelsSimData.mNbActiveWheels, this, ptr); +} + +void PxVehicleWheels::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleWheelsSimData::getBinaryMetaData(stream); + PxVehicleWheelsDynData::getBinaryMetaData(stream); + + //PxVehicleWheels + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleWheels) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleWheels, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxVehicleWheelsSimData, mWheelsSimData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxVehicleWheelsDynData, mWheelsDynData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxRigidDynamic, mActor, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxU32, mNbNonDrivenWheels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxU8, mOnConstraintReleaseCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxU8, mType, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels, PxU8, mPad0, PxMetaDataFlag::ePADDING) +} + +void PxVehicleConstraintShader::getBinaryMetaData(PxOutputStream& stream) +{ + //SuspLimitConstraintData + PX_DEF_BIN_METADATA_CLASS(stream, SuspLimitConstraintData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, SuspLimitConstraintData, PxVec3, mCMOffsets, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, SuspLimitConstraintData, PxVec3, mDirs, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, SuspLimitConstraintData, PxReal, mErrors, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, SuspLimitConstraintData, bool, mActiveFlags, 0) + + //StickyTireConstraintData + PX_DEF_BIN_METADATA_CLASS(stream, StickyTireConstraintData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, StickyTireConstraintData, PxVec3, mCMOffsets, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, StickyTireConstraintData, PxVec3, mDirs, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, StickyTireConstraintData, PxReal, mTargetSpeeds, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, StickyTireConstraintData, bool, mActiveFlags, 0) + + //VehicleConstraintData + PX_DEF_BIN_METADATA_CLASS(stream, VehicleConstraintData) + PX_DEF_BIN_METADATA_ITEM(stream, VehicleConstraintData, SuspLimitConstraintData, mSuspLimitData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, VehicleConstraintData, StickyTireConstraintData, mStickyTireForwardData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, VehicleConstraintData, StickyTireConstraintData, mStickyTireSideData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, VehicleConstraintData, PxQuat, mCMassRotation, 0) + + //PxVehicleConstraintShader + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleConstraintShader) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleConstraintShader, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleConstraintShader, VehicleConstraintData, mData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleConstraintShader, PxConstraint, mConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleConstraintShader, PxVehicleWheels, mVehicle, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleConstraintShader, PxU32, mPad, PxMetaDataFlag::ePADDING) +} + +void PxVehicleWheels4SimData::getBinaryMetaData(PxOutputStream& stream) +{ + //PxVehicleSuspensionData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleSuspensionData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mSpringStrength, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mSpringDamperRate, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mMaxCompression, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mMaxDroop, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mSprungMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mCamberAtRest, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mCamberAtMaxCompression, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mCamberAtMaxDroop, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mRecipMaxCompression, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mRecipMaxDroop, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleSuspensionData, PxU32, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleWheelData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleWheelData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mRadius, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mWidth, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mMOI, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mDampingRate, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mMaxBrakeTorque, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mMaxHandBrakeTorque, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mMaxSteer, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mToeAngle, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mRecipRadius, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mRecipMOI, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheelData, PxReal, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleTireData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleTireData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mLatStiffX, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mLatStiffY, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mLongitudinalStiffnessPerUnitGravity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mCamberStiffnessPerUnitGravity, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleTireData, PxReal, mFrictionVsSlipGraph, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxU32, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mRecipLongitudinalStiffnessPerUnitGravity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mFrictionVsSlipGraphRecipx1Minusx0, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mFrictionVsSlipGraphRecipx2Minusx1, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleTireData, PxReal, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleWheels4SimData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleWheels4SimData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVehicleSuspensionData, mSuspensions, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVehicleWheelData, mWheels, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVehicleTireData, mTires, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVec3, mSuspDownwardTravelDirections, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVec3, mSuspForceAppPointOffsets, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVec3, mTireForceAppPointOffsets, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVec3, mWheelCentreOffsets, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxReal, mTireRestLoads, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxReal, mRecipTireRestLoads, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxFilterData, mSqFilterData, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxU8, mWheelShapeMap, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxU32, mPad, PxMetaDataFlag::ePADDING) +} + +/* +void PxVehicleAntiRollBar::getBinaryMetaData(PxOutputStream& stream) +{ +} +*/ + +void PxVehicleWheels4DynData::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleWheels4DynData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mWheelSpeeds, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mCorrectedWheelSpeeds, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mWheelRotationAngles, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mTireLowForwardSpeedTimers, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mTireLowSideSpeedTimers, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxU8, mQueryOrCachedHitResults, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mJounces, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels4DynData, PxVehicleConstraintShader, mVehicleConstraints, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels4DynData, PxRaycastQueryResult, mRaycastResults, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels4DynData, PxSweepQueryResult, mSweepResults, PxMetaDataFlag::ePTR) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels4DynData, bool, mHasCachedRaycastHitPlane, 0) +#if PX_P64_FAMILY + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxU32, mPad, PxMetaDataFlag::ePADDING) +#endif +} diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp new file mode 100644 index 000000000..a8a87f5fb --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "PxVehicleNoDrive.h" +#include "PxVehicleWheels.h" +#include "PxVehicleDefaults.h" +#include "PxRigidDynamic.h" +#include "CmPhysXCommon.h" +#include "CmUtils.h" +#include "PsFoundation.h" + +namespace physx +{ + +extern PxF32 gToleranceScaleLength; + +bool PxVehicleNoDrive::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(PxVehicleWheels::isValid(), "invalid PxVehicleDrive", false); + return true; +} + +PxVehicleNoDrive* PxVehicleNoDrive::allocate(const PxU32 numWheels) +{ + PX_CHECK_AND_RETURN_NULL(numWheels>0, "Cars with zero wheels are illegal"); + PX_CHECK_AND_RETURN_NULL(gToleranceScaleLength > 0, "PxVehicleNoDrive::allocate - need to call PxInitVehicleSDK"); + + //Compute the bytes needed. + const PxU32 numWheels4 = (((numWheels + 3) & ~3) >> 2); + const PxU32 inputByteSize16 = sizeof(PxReal)*numWheels4*4; + const PxU32 byteSize = sizeof(PxVehicleNoDrive) + 3*inputByteSize16 + PxVehicleWheels::computeByteSize(numWheels); + + //Allocate the memory. + PxVehicleNoDrive* veh = static_cast(PX_ALLOC(byteSize, "PxVehicleNoDrive")); + Cm::markSerializedMem(veh, byteSize); + new(veh) PxVehicleNoDrive(); + + //Patch up the pointers. + PxU8* ptr = reinterpret_cast(veh) + sizeof(PxVehicleNoDrive); + veh->mSteerAngles = reinterpret_cast(ptr); + ptr += inputByteSize16; + veh->mDriveTorques = reinterpret_cast(ptr); + ptr += inputByteSize16; + veh->mBrakeTorques = reinterpret_cast(ptr); + ptr += inputByteSize16; + ptr = PxVehicleWheels::patchupPointers(numWheels, veh, ptr); + + //Initialise. + PxMemZero(veh->mSteerAngles, inputByteSize16); + PxMemZero(veh->mDriveTorques, inputByteSize16); + PxMemZero(veh->mBrakeTorques, inputByteSize16); + veh->init(numWheels); + + //Set the vehicle type. + veh->mType = PxVehicleTypes::eNODRIVE; + + return veh; +} + +void PxVehicleNoDrive::free() +{ + PxVehicleWheels::free(); +} + +void PxVehicleNoDrive::setup +(PxPhysics* physics, PxRigidDynamic* vehActor, const PxVehicleWheelsSimData& wheelsData) +{ + //Set up the wheels. + PxVehicleWheels::setup(physics,vehActor,wheelsData,0,wheelsData.getNbWheels()); +} + +PxVehicleNoDrive* PxVehicleNoDrive::create +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData) +{ + PxVehicleNoDrive* veh=PxVehicleNoDrive::allocate(wheelsData.getNbWheels()); + veh->setup(physics,vehActor,wheelsData); + return veh; +} + +void PxVehicleNoDrive::setToRestState() +{ + const PxU32 numWheels4 = (((mWheelsSimData.getNbWheels() + 3) & ~3) >> 2); + const PxU32 inputByteSize = sizeof(PxReal)*numWheels4*4; + const PxU32 inputByteSize16 = (inputByteSize + 15) & ~15; + PxMemZero(mSteerAngles, 3*inputByteSize16); + + //Set core to rest state. + PxVehicleWheels::setToRestState(); +} + +void PxVehicleNoDrive::setBrakeTorque(const PxU32 id, const PxReal brakeTorque) +{ + PX_CHECK_AND_RETURN(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::setBrakeTorque - Illegal wheel"); + PX_CHECK_AND_RETURN(brakeTorque>=0, "PxVehicleNoDrive::setBrakeTorque - negative brake torques are illegal"); + mBrakeTorques[id] = brakeTorque; +} + +void PxVehicleNoDrive::setDriveTorque(const PxU32 id, const PxReal driveTorque) +{ + PX_CHECK_AND_RETURN(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::setDriveTorque - Illegal wheel"); + mDriveTorques[id] = driveTorque; +} + +void PxVehicleNoDrive::setSteerAngle(const PxU32 id, const PxReal steerAngle) +{ + PX_CHECK_AND_RETURN(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::setSteerAngle - Illegal wheel"); + mSteerAngles[id] = steerAngle; +} + +PxReal PxVehicleNoDrive::getBrakeTorque(const PxU32 id) const +{ + PX_CHECK_AND_RETURN_VAL(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::getBrakeTorque - Illegal wheel", 0); + return mBrakeTorques[id]; +} + +PxReal PxVehicleNoDrive::getDriveTorque(const PxU32 id) const +{ + PX_CHECK_AND_RETURN_VAL(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::getDriveTorque - Illegal wheel",0); + return mDriveTorques[id]; +} + +PxReal PxVehicleNoDrive::getSteerAngle(const PxU32 id) const +{ + PX_CHECK_AND_RETURN_VAL(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::getSteerAngle - Illegal wheel",0); + return mSteerAngles[id]; +} + +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp new file mode 100644 index 000000000..18310f07e --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp @@ -0,0 +1,115 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleSDK.h" +#include "PxPhysics.h" +#include "PxTolerancesScale.h" +#include "CmPhysXCommon.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "PxVehicleDrive4W.h" +#include "PxVehicleMetaDataObjects.h" +#include "PxVehicleSerialization.h" +#include "SnRepXSerializerImpl.h" +#include "PxSerializer.h" +#include "PxVehicleDriveTank.h" +#include "PxVehicleNoDrive.h" +#include "PxVehicleDriveNW.h" + +namespace physx +{ + +void setVehicleToleranceScale(const PxTolerancesScale& ts); +void resetVehicleToleranceScale(); +void setSerializationRegistryPtr(const PxSerializationRegistry* sr); +const PxSerializationRegistry* resetSerializationRegistryPtr(); +void setVehicleDefaults(); + +bool PxInitVehicleSDK(PxPhysics& physics, PxSerializationRegistry* sr) +{ + PX_ASSERT(static_cast(&physics.getFoundation()) == &Ps::Foundation::getInstance()); + Ps::Foundation::incRefCount(); + setVehicleToleranceScale(physics.getTolerancesScale()); + + setVehicleDefaults(); + + setSerializationRegistryPtr(sr); + if(sr) + { + sr->registerRepXSerializer(PxVehicleConcreteType::eVehicleDrive4W, PX_NEW_REPX_SERIALIZER(PxVehicleRepXSerializer)); + sr->registerRepXSerializer(PxVehicleConcreteType::eVehicleDriveTank, PX_NEW_REPX_SERIALIZER(PxVehicleRepXSerializer)); + sr->registerRepXSerializer(PxVehicleConcreteType::eVehicleDriveNW, PX_NEW_REPX_SERIALIZER(PxVehicleRepXSerializer)); + sr->registerRepXSerializer(PxVehicleConcreteType::eVehicleNoDrive, PX_NEW_REPX_SERIALIZER(PxVehicleRepXSerializer)); + + sr->registerSerializer(PxVehicleConcreteType::eVehicleDrive4W, PX_NEW_SERIALIZER_ADAPTER(PxVehicleDrive4W)); + sr->registerSerializer(PxVehicleConcreteType::eVehicleDriveTank, PX_NEW_SERIALIZER_ADAPTER(PxVehicleDriveTank)); + sr->registerSerializer(PxVehicleConcreteType::eVehicleNoDrive, PX_NEW_SERIALIZER_ADAPTER(PxVehicleNoDrive)); + sr->registerSerializer(PxVehicleConcreteType::eVehicleDriveNW, PX_NEW_SERIALIZER_ADAPTER(PxVehicleDriveNW)); + + sr->registerBinaryMetaDataCallback(PxVehicleDrive4W::getBinaryMetaData); + sr->registerBinaryMetaDataCallback(PxVehicleDriveTank::getBinaryMetaData); + sr->registerBinaryMetaDataCallback(PxVehicleNoDrive::getBinaryMetaData); + sr->registerBinaryMetaDataCallback(PxVehicleDriveNW::getBinaryMetaData); + } + return true; +} + +void PxCloseVehicleSDK(PxSerializationRegistry* sr) +{ + Ps::Foundation::decRefCount(); + resetVehicleToleranceScale(); + + setVehicleDefaults(); + + if (sr != resetSerializationRegistryPtr()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxCloseVehicleSDK called with different PxSerializationRegistry instance than PxInitVehicleSDK."); + return; + } + + if(sr) + { + PX_DELETE_SERIALIZER_ADAPTER(sr->unregisterSerializer(PxVehicleConcreteType::eVehicleDrive4W)); + PX_DELETE_SERIALIZER_ADAPTER(sr->unregisterSerializer(PxVehicleConcreteType::eVehicleDriveTank)); + PX_DELETE_SERIALIZER_ADAPTER(sr->unregisterSerializer(PxVehicleConcreteType::eVehicleNoDrive)); + PX_DELETE_SERIALIZER_ADAPTER(sr->unregisterSerializer(PxVehicleConcreteType::eVehicleDriveNW)); + + PX_DELETE_REPX_SERIALIZER(sr->unregisterRepXSerializer(PxVehicleConcreteType::eVehicleDrive4W)); + PX_DELETE_REPX_SERIALIZER(sr->unregisterRepXSerializer(PxVehicleConcreteType::eVehicleDriveTank)); + PX_DELETE_REPX_SERIALIZER(sr->unregisterRepXSerializer(PxVehicleConcreteType::eVehicleNoDrive)); + PX_DELETE_REPX_SERIALIZER(sr->unregisterRepXSerializer(PxVehicleConcreteType::eVehicleDriveNW)); + } +} +///////////////////////// + + + + +}//physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp new file mode 100644 index 000000000..dbb616c9b --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp @@ -0,0 +1,203 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxRepXSimpleType.h" +#include "PxBase.h" +#include "PxCollection.h" +#include "PxVehicleMetaDataObjects.h" +#include "SnRepXSerializerImpl.h" +#include "PxVehicleSerialization.h" +#include "PxVehicleSuspWheelTire4.h" +#include "PxVehicleSuspLimitConstraintShader.h" +#include "PsFPU.h" + + +namespace physx +{ + using namespace Sn; + + template + inline void* createVehicle( PxPhysics& physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, const PxVehicleDriveSimDataNW& driveDataNW, + const PxU32 numWheels, const PxU32 numNonDrivenWheels) + { + PX_UNUSED(physics); + PX_UNUSED(vehActor); + PX_UNUSED(wheelsData); + PX_UNUSED(driveData); + PX_UNUSED(driveDataNW); + PX_UNUSED(numWheels); + PX_UNUSED(numNonDrivenWheels); + return NULL; + } + + template<> + inline void* createVehicle(PxPhysics& physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, const PxVehicleDriveSimDataNW& /*driveDataNW*/, + const PxU32 numWheels, const PxU32 numNonDrivenWheels) + { + PxVehicleDrive4W* vehDrive4W = PxVehicleDrive4W::allocate(numWheels); + vehDrive4W->setup(&physics, vehActor->is(), wheelsData, driveData, numNonDrivenWheels); + return vehDrive4W; + } + + template<> + inline void* createVehicle(PxPhysics& physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, const PxVehicleDriveSimDataNW& /*driveDataNW*/, + const PxU32 numWheels, const PxU32 numNonDrivenWheels) + { + PxVehicleDriveTank* tank = PxVehicleDriveTank::allocate(numWheels); + tank->setup(&physics, vehActor->is(), wheelsData, driveData, numWheels - numNonDrivenWheels); + return tank; + } + + template<> + inline void* createVehicle(PxPhysics& physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& /*driveData*/, const PxVehicleDriveSimDataNW& driveDataNW, + const PxU32 numWheels, const PxU32 numNonDrivenWheels) + { + PxVehicleDriveNW* vehDriveNW = PxVehicleDriveNW::allocate(numWheels); + vehDriveNW->setup(&physics, vehActor->is(), wheelsData, driveDataNW, numWheels - numNonDrivenWheels); + return vehDriveNW; + } + + template<> + inline void* createVehicle(PxPhysics& physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& /*driveData*/, const PxVehicleDriveSimDataNW& /*driveDataNW*/, + const PxU32 numWheels, const PxU32 /*numNonDrivenWheels*/) + { + PxVehicleNoDrive* vehNoDrive = PxVehicleNoDrive::allocate(numWheels); + vehNoDrive->setup(&physics, vehActor->is(), wheelsData); + return vehNoDrive; + } + + template + PxRepXObject PxVehicleRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + PxRigidActor* vehActor = NULL; + readReference( inReader, *inCollection, "PxRigidDynamicRef", vehActor ); + if ( vehActor == NULL ) + return PxRepXObject(); + + PxU32 numWheels = 0; + readProperty( inReader, "NumWheels", numWheels ); + if( numWheels == 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::createCollectionFromXml: PxVehicleRepXSerializer: Xml field NumWheels is zero!"); + return PxRepXObject(); + } + + PxU32 numNonDrivenWheels = 0; + readProperty( inReader, "NumNonDrivenWheels", numNonDrivenWheels ); + + //change to numwheel + PxVehicleWheelsSimData* wheelsSimData=PxVehicleWheelsSimData::allocate(numWheels); + { + inReader.pushCurrentContext(); + if ( inReader.gotoChild( "MWheelsSimData" ) ) + { + readAllProperties( inArgs, inReader, wheelsSimData, inAllocator, *inCollection ); + } + + inReader.popCurrentContext(); + } + + PxVehicleDriveSimData4W driveSimData; + { + inReader.pushCurrentContext(); + if ( inReader.gotoChild( "MDriveSimData" ) ) + { + readAllProperties( inArgs, inReader, &driveSimData, inAllocator, *inCollection ); + } + + inReader.popCurrentContext(); + } + + PxVehicleDriveSimDataNW nmSimData; + { + inReader.pushCurrentContext(); + if ( inReader.gotoChild( "MDriveSimDataNW" ) ) + { + readAllProperties( inArgs, inReader, &driveSimData, inAllocator, *inCollection ); + } + inReader.popCurrentContext(); + } + TVehicleType* drive = static_cast(createVehicle(inArgs.physics, vehActor->is(), *wheelsSimData, driveSimData, nmSimData, numWheels, numNonDrivenWheels)); + readAllProperties( inArgs, inReader, drive, inAllocator, *inCollection ); + + PxVehicleWheels4DynData* wheel4DynData = drive->mWheelsDynData.getWheel4DynData(); + PX_ASSERT( wheel4DynData ); + for(PxU32 i=0;igetNbWheels4();i++) + { + PxConstraint* constraint = wheel4DynData[i].getVehicletConstraintShader().getPxConstraint(); + if( constraint ) + inCollection->add(*constraint); + } + + if( wheelsSimData ) + wheelsSimData->free(); + + return PxCreateRepXObject(drive); + } + + template + void PxVehicleRepXSerializer::objectToFileImpl( const TVehicleType* drive, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/ ) + { + PX_SIMD_GUARD; // denorm exception triggered in PxVehicleGearsDataGeneratedInfo::visitInstanceProperties on osx + writeReference( inWriter, *inCollection, "PxRigidDynamicRef", drive->getRigidDynamicActor() ); + writeProperty( inWriter, *inCollection, inTempBuffer, "NumWheels", drive->mWheelsSimData.getNbWheels() ); + writeProperty( inWriter, *inCollection, inTempBuffer, "NumNonDrivenWheels", drive->getNbNonDrivenWheels()); + writeAllProperties( drive, inWriter, inTempBuffer, *inCollection ); + } + + PxVehicleNoDrive::PxVehicleNoDrive() + : PxVehicleWheels(PxVehicleConcreteType::eVehicleNoDrive, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) + {} + + PxVehicleDrive4W::PxVehicleDrive4W() + : PxVehicleDrive(PxVehicleConcreteType::eVehicleDrive4W, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) + {} + + PxVehicleDriveNW::PxVehicleDriveNW() + : PxVehicleDrive(PxVehicleConcreteType::eVehicleDriveNW, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) + {} + + PxVehicleDriveTank::PxVehicleDriveTank() + : PxVehicleDrive(PxVehicleConcreteType::eVehicleDriveTank, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) + , mDriveModel(PxVehicleDriveTankControlModel::eSTANDARD) + {} + + // explicit template instantiations + template struct PxVehicleRepXSerializer; + template struct PxVehicleRepXSerializer; + template struct PxVehicleRepXSerializer; + template struct PxVehicleRepXSerializer; + +} diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h new file mode 100644 index 000000000..8e5914887 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_SERIALIZATION_H +#define PX_VEHICLE_SERIALIZATION_H + +#include "extensions/PxRepXSimpleType.h" +#include "SnRepXSerializerImpl.h" + +namespace physx +{ + class PxRepXSerializer; + class PxSerializationRegistry; + class XmlReader; + class XmlMemoryAllocator; + class XmlWriter; + class MemoryBuffer; + + PX_DEFINE_TYPEINFO(PxVehicleNoDrive, PxVehicleConcreteType::eVehicleNoDrive) + PX_DEFINE_TYPEINFO(PxVehicleDrive4W, PxVehicleConcreteType::eVehicleDrive4W) + PX_DEFINE_TYPEINFO(PxVehicleDriveNW, PxVehicleConcreteType::eVehicleDriveNW) + PX_DEFINE_TYPEINFO(PxVehicleDriveTank, PxVehicleConcreteType::eVehicleDriveTank) + + template + struct PxVehicleRepXSerializer : public RepXSerializerImpl + { + PxVehicleRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual PxRepXObject fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ); + virtual void objectToFileImpl( const TVehicleType* , PxCollection* , XmlWriter& , MemoryBuffer& , PxRepXInstantiationArgs& ); + virtual TVehicleType* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + +#if PX_SUPPORT_EXTERN_TEMPLATE + // explicit template instantiation declarations + extern template struct PxVehicleRepXSerializer; + extern template struct PxVehicleRepXSerializer; + extern template struct PxVehicleRepXSerializer; + extern template struct PxVehicleRepXSerializer; +#endif + +} + + +#endif//PX_VEHICLE_REPX_SERIALIZER_H diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h new file mode 100644 index 000000000..d8864054d --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h @@ -0,0 +1,317 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_SUSP_LIMIT_CONSTRAINT_SHADER_H +#define PX_VEHICLE_SUSP_LIMIT_CONSTRAINT_SHADER_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxTransform.h" +#include "extensions/PxConstraintExt.h" +#include "PxConstraintDesc.h" +#include "PxConstraint.h" +#include "PsAllocator.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxVehicleConstraintShader : public PxConstraintConnector +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleWheels; + + PxVehicleConstraintShader(PxVehicleWheels* vehicle, PxConstraint* constraint = NULL) + : mConstraint(constraint), + mVehicle(vehicle) + { + } + PxVehicleConstraintShader(){} + ~PxVehicleConstraintShader() + { + } + + static void getBinaryMetaData(PxOutputStream& stream); + + void release() + { + if(mConstraint) + { + mConstraint->release(); + } + } + + virtual void onComShift(PxU32 actor) { PX_UNUSED(actor); } + + virtual void onOriginShift(const PxVec3& shift) { PX_UNUSED(shift); } + + virtual void* prepareData() + { + return &mData; + } + + virtual bool updatePvdProperties(pvdsdk::PvdDataStream& pvdConnection, + const PxConstraint* c, + PxPvdUpdateType::Enum updateType) const { PX_UNUSED(c); PX_UNUSED(updateType); PX_UNUSED(&pvdConnection); return true;} + + virtual void onConstraintRelease() + { + mVehicle->mOnConstraintReleaseCounter--; + if(0==mVehicle->mOnConstraintReleaseCounter) + { + PX_FREE(mVehicle); + } + } + + virtual void* getExternalReference(PxU32& typeID) { typeID = PxConstraintExtIDs::eVEHICLE_SUSP_LIMIT; return this; } + virtual PxBase* getSerializable() { return NULL; } + + + static PxU32 vehicleSuspLimitConstraintSolverPrep( + Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 maxConstraints, + PxConstraintInvMassScale&, + const void* constantBlock, + const PxTransform& bodyAToWorld, + const PxTransform& bodyBToWorld, + bool, + PxVec3& cA2w, PxVec3& cB2w + ) + { + PX_UNUSED(maxConstraints); + PX_UNUSED(body0WorldOffset); + PX_UNUSED(bodyBToWorld); + PX_ASSERT(bodyAToWorld.isValid()); PX_ASSERT(bodyBToWorld.isValid()); + + const VehicleConstraintData* data = static_cast(constantBlock); + PxU32 numActive=0; + const PxQuat bodyRotation = bodyAToWorld.q * data->mCMassRotation.getConjugate(); + + //KS - the substep solver will use raXn to try to add to the angular part of the linear constraints. + //We overcome this by setting the ra and rb offsets to be 0. In addition, we will + cA2w = bodyAToWorld.p; + cB2w = bodyBToWorld.p; + + //Susp limit constraints. + for(PxU32 i=0;i<4;i++) + { + if(data->mSuspLimitData.mActiveFlags[i]) + { + Px1DConstraint& p=constraints[numActive]; + p.linear0 = bodyRotation.rotate(data->mSuspLimitData.mDirs[i]); + p.angular0 = bodyRotation.rotate(data->mSuspLimitData.mCMOffsets[i].cross(data->mSuspLimitData.mDirs[i])); + p.geometricError=data->mSuspLimitData.mErrors[i]; + p.linear1=PxVec3(0); + p.angular1=PxVec3(0); + p.minImpulse=-FLT_MAX; + p.maxImpulse=0; + p.velocityTarget=0; + p.flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT; + numActive++; + } + } + + //Sticky tire friction constraints. + for(PxU32 i=0;i<4;i++) + { + if(data->mStickyTireForwardData.mActiveFlags[i]) + { + Px1DConstraint& p=constraints[numActive]; + p.linear0=data->mStickyTireForwardData.mDirs[i]; + p.angular0=data->mStickyTireForwardData.mCMOffsets[i].cross(data->mStickyTireForwardData.mDirs[i]); + p.geometricError=0.0f; + p.linear1=PxVec3(0); + p.angular1=PxVec3(0); + p.minImpulse=-FLT_MAX; + p.maxImpulse=FLT_MAX; + p.velocityTarget=data->mStickyTireForwardData.mTargetSpeeds[i]; + p.mods.spring.damping = 1000.0f; + p.flags = Px1DConstraintFlag::eSPRING | Px1DConstraintFlag::eACCELERATION_SPRING; + p.flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT; + numActive++; + } + } + + //Sticky tire friction constraints. + for(PxU32 i=0;i<4;i++) + { + if(data->mStickyTireSideData.mActiveFlags[i]) + { + Px1DConstraint& p=constraints[numActive]; + p.linear0=data->mStickyTireSideData.mDirs[i]; + p.angular0=data->mStickyTireSideData.mCMOffsets[i].cross(data->mStickyTireSideData.mDirs[i]); + p.geometricError=0.0f; + p.linear1=PxVec3(0); + p.angular1=PxVec3(0); + p.minImpulse=-FLT_MAX; + p.maxImpulse=FLT_MAX; + p.velocityTarget=data->mStickyTireSideData.mTargetSpeeds[i]; + p.mods.spring.damping = 1000.0f; + p.flags = Px1DConstraintFlag::eSPRING | Px1DConstraintFlag::eACCELERATION_SPRING; + p.flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT; + numActive++; + } + } + + + return numActive; + } + + static void visualiseConstraint(PxConstraintVisualizer &viz, + const void* constantBlock, + const PxTransform& body0Transform, + const PxTransform& body1Transform, + PxU32 flags){ PX_UNUSED(&viz); PX_UNUSED(constantBlock); PX_UNUSED(body0Transform); + PX_UNUSED(body1Transform); PX_UNUSED(flags); + PX_ASSERT(body0Transform.isValid()); PX_ASSERT(body1Transform.isValid()); } + +public: + + struct SuspLimitConstraintData + { + PxVec3 mCMOffsets[4]; + PxVec3 mDirs[4]; + PxReal mErrors[4]; + bool mActiveFlags[4]; + }; + struct StickyTireConstraintData + { + PxVec3 mCMOffsets[4]; + PxVec3 mDirs[4]; + PxReal mTargetSpeeds[4]; + bool mActiveFlags[4]; + }; + + struct VehicleConstraintData + { + SuspLimitConstraintData mSuspLimitData; + StickyTireConstraintData mStickyTireForwardData; + StickyTireConstraintData mStickyTireSideData; + PxQuat mCMassRotation; + }; + VehicleConstraintData mData; + + PxConstraint* mConstraint; + + PX_INLINE void setPxConstraint(PxConstraint* pxConstraint) + { + mConstraint = pxConstraint; + } + + PX_INLINE PxConstraint* getPxConstraint() + { + return mConstraint; + } + + PxConstraintConnector* getConnector() + { + return this; + } + + virtual PxConstraintSolverPrep getPrep()const { return NULL; /*KS - TODO. Figure out what to return here. Probably nothing is OK*/ } + virtual const void* getConstantBlock()const { return NULL; /*KS - TODO. Figure out what to return here. Probably nothing is OK*/ } + +private: + + PxVehicleWheels* mVehicle; + +#if !PX_P64_FAMILY + PxU32 mPad[2]; +#else + PxU32 mPad[1]; +#endif +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleConstraintShader)& 0x0f)); + + +/** +\brief Default implementation of PxVehicleComputeTireForce +@see PxVehicleComputeTireForce, PxVehicleTireForceCalculator +*/ +void PxVehicleComputeTireForceDefault + (const void* shaderData, + const PxF32 tireFriction, + const PxF32 longSlip, const PxF32 latSlip, const PxF32 camber, + const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 recipWheelRadius, + const PxF32 restTireLoad, const PxF32 normalisedTireLoad, const PxF32 tireLoad, + const PxF32 gravity, const PxF32 recipGravity, + PxF32& wheelTorque, PxF32& tireLongForceMag, PxF32& tireLatForceMag, PxF32& tireAlignMoment); + + +/** +\brief Structure containing shader data for each tire of a vehicle and a shader function that computes individual tire forces +*/ +class PxVehicleTireForceCalculator +{ +public: + + PxVehicleTireForceCalculator() + : mShader(PxVehicleComputeTireForceDefault) + { + } + + /** + \brief Array of shader data - one data entry per tire. + Default values are pointers to PxVehicleTireData (stored in PxVehicleWheelsSimData) and are set in PxVehicleDriveTank::setup or PxVehicleDrive4W::setup + @see PxVehicleComputeTireForce, PxVehicleComputeTireForceDefault, PxVehicleWheelsSimData, PxVehicleDriveTank::setup, PxVehicleDrive4W::setup + */ + const void** mShaderData; + + /** + \brief Shader function. + Default value is PxVehicleComputeTireForceDefault and is set in PxVehicleDriveTank::setup or PxVehicleDrive4W::setup + @see PxVehicleComputeTireForce, PxVehicleComputeTireForceDefault, PxVehicleWheelsSimData, PxVehicleDriveTank::setup, PxVehicleDrive4W::setup + */ + PxVehicleComputeTireForce mShader; + +#if !PX_P64_FAMILY + PxU32 mPad[2]; +#endif +}; + +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleTireForceCalculator) & 15)); + + +#if !PX_DOXYGEN +} // namespace physx +#endif + + +/** @} */ +#endif //PX_VEHICLE_SUSP_LIMIT_CONSTRAINT_SHADER_H diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp new file mode 100644 index 000000000..61b61f176 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp @@ -0,0 +1,191 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleSuspWheelTire4.h" +#include "PxVehicleDefaults.h" +#include "PsFoundation.h" +#include "CmPhysXCommon.h" +#include "PsUtilities.h" + +namespace physx +{ + +PxVehicleWheels4SimData::PxVehicleWheels4SimData() +{ + for(PxU32 i=0;i<4;i++) + { + mSuspDownwardTravelDirections[i]=PxVec3(0,0,0); //Must be filled out + mSuspForceAppPointOffsets[i]=PxVec3(0,0,0); //Must be filled out + mTireForceAppPointOffsets[i]=PxVec3(0,0,0); //Must be filled out + mWheelCentreOffsets[i]=PxVec3(0,0,0); //Must be filled out + + mTireRestLoads[i]=20.0f + 1500.0f; + mRecipTireRestLoads[i]=1.0f/mTireRestLoads[i]; + } +} + +bool PxVehicleWheels4SimData::isValid(const PxU32 id) const +{ + PX_ASSERT(id<4); + PX_CHECK_AND_RETURN_VAL(mSuspensions[id].isValid(), "Invalid PxVehicleSuspWheelTire4SimulationData.mSuspensions", false); + PX_CHECK_AND_RETURN_VAL(mWheels[id].isValid(), "Invalid PxVehicleSuspWheelTire4SimulationData.mWheels", false); + PX_CHECK_AND_RETURN_VAL(mTires[id].isValid(), "Invalid PxVehicleSuspWheelTire4SimulationData.mTires", false); + PX_CHECK_AND_RETURN_VAL(mSuspDownwardTravelDirections[id].magnitude()>=0.999f && mSuspDownwardTravelDirections[id].magnitude()<=1.001f, "Invalid PxVehicleSuspWheelTire4SimulationData.mSuspDownwardTravelDirections", false); + PX_CHECK_AND_RETURN_VAL(mSuspForceAppPointOffsets[id].magnitude()!=0.0f, "Invalid PxVehicleSuspWheelTire4SimulationData.mSuspForceAppPointOffsets.mSuspForceAppPointOffsets", false); + PX_CHECK_AND_RETURN_VAL(mTireForceAppPointOffsets[id].magnitude()!=0.0f, "Invalid PxVehicleSuspWheelTire4SimulationData.mTireForceAppPointOffsets.mTireForceAppPointOffsets", false); + PX_CHECK_AND_RETURN_VAL(mWheelCentreOffsets[id].magnitude()!=0.0f, "Invalid PxVehicleSuspWheelTire4SimulationData.mWheelCentreOffsets.mWheelCentreOffsets", false); + PX_CHECK_AND_RETURN_VAL(mTireRestLoads[id]>0.0f, "Invalid PxVehicleSuspWheelTire4SimulationData.mTireRestLoads", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/mTireRestLoads[id]) - mRecipTireRestLoads[id]) <= 0.001f, "Invalid PxVehicleSuspWheelTire4SimulationData.mRecipTireRestLoads", false); + PX_UNUSED(id); + return true; +} + +void PxVehicleWheels4SimData::setSuspensionData(const PxU32 id, const PxVehicleSuspensionData& susp) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal suspension id"); + PX_CHECK_AND_RETURN(susp.mSpringStrength>0, "Susp spring strength must be greater than zero"); + PX_CHECK_AND_RETURN(susp.mSpringDamperRate>=0, "Susp spring damper rate must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(susp.mMaxCompression>=0, "Susp max compression must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(susp.mMaxDroop>=0, "Susp max droop must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(susp.mMaxDroop>0 || susp.mMaxCompression>0, "Either one of max droop or max compression must be greater than zero"); + PX_CHECK_AND_RETURN(susp.mSprungMass>0, "Susp spring mass must be greater than zero"); + + mSuspensions[id]=susp; + mSuspensions[id].mRecipMaxCompression = 1.0f/((susp.mMaxCompression > 0.0f) ? susp.mMaxCompression : 1.0f); + mSuspensions[id].mRecipMaxDroop = 1.0f/((susp.mMaxDroop > 0.0f) ? susp.mMaxDroop : 1.0f); + + mTireRestLoads[id]=mWheels[id].mMass+mSuspensions[id].mSprungMass; + mRecipTireRestLoads[id]=1.0f/mTireRestLoads[id]; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setWheelData(const PxU32 id, const PxVehicleWheelData& wheel) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal wheel id"); + PX_CHECK_AND_RETURN(wheel.mRadius>0, "Wheel radius must be greater than zero"); + PX_CHECK_AND_RETURN(wheel.mMaxBrakeTorque>=0, "Wheel brake torque must be zero or be a positive value"); + PX_CHECK_AND_RETURN(wheel.mMaxHandBrakeTorque>=0, "Wheel handbrake torque must be zero or be a positive value"); + PX_CHECK_AND_RETURN(PxAbs(wheel.mMaxSteer)0, "Wheel mass must be greater than zero"); + PX_CHECK_AND_RETURN(wheel.mMOI>0, "Wheel moi must be greater than zero"); + PX_CHECK_AND_RETURN(wheel.mToeAngle>-PxHalfPi && wheel.mToeAngle0, "Wheel width must be greater than zero"); + PX_CHECK_AND_RETURN(wheel.mDampingRate>=0, "Wheel damping rate must be greater than or equal to zero"); + + mWheels[id]=wheel; + mWheels[id].mRecipRadius=1.0f/mWheels[id].mRadius; + mWheels[id].mRecipMOI=1.0f/mWheels[id].mMOI; + + mTireRestLoads[id]=mWheels[id].mMass+mSuspensions[id].mSprungMass; + mRecipTireRestLoads[id]=1.0f/mTireRestLoads[id]; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setTireData(const PxU32 id, const PxVehicleTireData& tire) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal tire id"); + PX_CHECK_AND_RETURN(tire.mLatStiffX>0, "Tire mLatStiffX must greater than zero"); + PX_CHECK_AND_RETURN(tire.mLatStiffY>0, "Tire mLatStiffY must greater than zero"); + PX_CHECK_AND_RETURN(tire.mLongitudinalStiffnessPerUnitGravity>0, "Tire longitudinal stiffness must greater than zero"); + PX_CHECK_AND_RETURN(tire.mCamberStiffnessPerUnitGravity>=0, "Tire camber stiffness must greater than or equal to zero"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[0][0]==0, "mFrictionVsSlipGraph[0][0] must be zero"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[0][1]>0, "mFrictionVsSlipGraph[0][0] must be greater than zero"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[1][0]>0, "mFrictionVsSlipGraph[1][0] must be greater than zero"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[1][1]>=tire.mFrictionVsSlipGraph[0][1], "mFrictionVsSlipGraph[1][1] must be greater than mFrictionVsSlipGraph[0][1]"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[2][0]> tire.mFrictionVsSlipGraph[1][0], "mFrictionVsSlipGraph[2][0] must be greater than mFrictionVsSlipGraph[1][0]"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[2][1]<=tire.mFrictionVsSlipGraph[1][1], "mFrictionVsSlipGraph[2][1] must be less than or equal to mFrictionVsSlipGraph[1][1]"); + + mTires[id]=tire; + mTires[id].mRecipLongitudinalStiffnessPerUnitGravity=1.0f/mTires[id].mLongitudinalStiffnessPerUnitGravity; + mTires[id].mFrictionVsSlipGraphRecipx1Minusx0=1.0f/(mTires[id].mFrictionVsSlipGraph[1][0]-mTires[id].mFrictionVsSlipGraph[0][0]); + mTires[id].mFrictionVsSlipGraphRecipx2Minusx1=1.0f/(mTires[id].mFrictionVsSlipGraph[2][0]-mTires[id].mFrictionVsSlipGraph[1][0]); +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setSuspTravelDirection(const PxU32 id, const PxVec3& dir) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal suspension id"); + PX_CHECK_AND_RETURN(dir.magnitude()>0.999f && dir.magnitude()<1.0001f, "Suspension travel dir must be unit vector"); + + mSuspDownwardTravelDirections[id]=dir; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setSuspForceAppPointOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal suspension id"); + PX_CHECK_AND_RETURN(offset.magnitude()>0, "Susp force app point must be offset from centre of mass"); + + mSuspForceAppPointOffsets[id]=offset; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setTireForceAppPointOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal tire id"); + PX_CHECK_AND_RETURN(offset.magnitude()>0, "Tire force app point must be offset from centre of mass"); + + mTireForceAppPointOffsets[id]=offset; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setWheelCentreOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal wheel id"); + PX_CHECK_AND_RETURN(offset.magnitude()>0, "Tire force app point must be offset from centre of mass"); + + mWheelCentreOffsets[id]=offset; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setWheelShapeMapping(const PxU32 id, const PxI32 shapeId) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal wheel id"); + PX_CHECK_AND_RETURN((-1==shapeId) || (PxU32(shapeId) < PX_MAX_U8), "Illegal shapeId: must be -1 or less than PX_MAX_U8"); + mWheelShapeMap[id] = Ps::to8(-1!=shapeId ? shapeId : PX_MAX_U8); +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setSceneQueryFilterData(const PxU32 id, const PxFilterData& sqFilterData) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal wheel id"); + mSqFilterData[id]=sqFilterData; +} + + +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h new file mode 100644 index 000000000..776bb345d --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h @@ -0,0 +1,393 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_SUSPWHEELTIRE_H +#define PX_VEHICLE_SUSPWHEELTIRE_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec3.h" +#include "foundation/PxVec4.h" +#include "foundation/PxTransform.h" +#include "foundation/PxIO.h" +#include "geometry/PxGeometryHelpers.h" +#include "PxVehicleComponents.h" +#include "PxBatchQueryDesc.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxVehicleConstraintShader; +class PxMaterial; +class PxShape; + + + +class PxVehicleWheels4SimData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + PxVehicleWheels4SimData(); + + bool isValid(const PxU32 id) const; + + static void getBinaryMetaData(PxOutputStream& stream); + +public: + + PX_FORCE_INLINE const PxVehicleSuspensionData& getSuspensionData(const PxU32 id) const {return mSuspensions[id];} + PX_FORCE_INLINE const PxVehicleWheelData& getWheelData(const PxU32 id) const {return mWheels[id];} + PX_FORCE_INLINE const PxVehicleTireData& getTireData(const PxU32 id) const {return mTires[id];} + PX_FORCE_INLINE const PxVec3& getSuspTravelDirection(const PxU32 id) const {return mSuspDownwardTravelDirections[id];} + PX_FORCE_INLINE const PxVec3& getSuspForceAppPointOffset(const PxU32 id) const {return mSuspForceAppPointOffsets[id];} + PX_FORCE_INLINE const PxVec3& getTireForceAppPointOffset(const PxU32 id) const {return mTireForceAppPointOffsets[id];} + PX_FORCE_INLINE const PxVec3& getWheelCentreOffset(const PxU32 id) const {return mWheelCentreOffsets[id];} + PX_FORCE_INLINE PxI32 getWheelShapeMapping(const PxU32 id) const {return (PX_MAX_U8 != mWheelShapeMap[id]) ? mWheelShapeMap[id] : -1;} + PX_FORCE_INLINE const PxFilterData& getSceneQueryFilterData(const PxU32 id) const {return mSqFilterData[id];} + PX_FORCE_INLINE const PxReal* getTireRestLoadsArray() const {return mTireRestLoads;} + PX_FORCE_INLINE const PxReal* getRecipTireRestLoadsArray() const {return mRecipTireRestLoads;} + + void setSuspensionData (const PxU32 id, const PxVehicleSuspensionData& susp); + void setWheelData (const PxU32 id, const PxVehicleWheelData& susp); + void setTireData (const PxU32 id, const PxVehicleTireData& tire); + void setSuspTravelDirection (const PxU32 id, const PxVec3& dir); + void setSuspForceAppPointOffset (const PxU32 id, const PxVec3& offset); + void setTireForceAppPointOffset (const PxU32 id, const PxVec3& offset); + void setWheelCentreOffset (const PxU32 id, const PxVec3& offset); + void setWheelShapeMapping (const PxU32 id, const PxI32 shapeId); + void setSceneQueryFilterData (const PxU32 id, const PxFilterData& sqFilterData); + +private: + + /** + \brief Suspension simulation data + @see setSuspensionData, getSuspensionData + */ + PxVehicleSuspensionData mSuspensions[4]; + + /** + \brief Wheel simulation data + @see setWheelData, getWheelData + */ + PxVehicleWheelData mWheels[4]; + + /** + \brief Tire simulation data + @see setTireData, getTireData + */ + PxVehicleTireData mTires[4]; + + /** + \brief Direction of suspension travel, pointing downwards. + */ + PxVec3 mSuspDownwardTravelDirections[4]; + + /** + \brief Application point of suspension force specified as an offset from the rigid body centre of mass. + */ + PxVec3 mSuspForceAppPointOffsets[4]; //Offset from cm + + /** + \brief Application point of tire forces specified as an offset from the rigid body centre of mass. + */ + PxVec3 mTireForceAppPointOffsets[4]; //Offset from cm + + /** + \brief Position of wheel center specified as an offset from the rigid body centre of mass. + */ + PxVec3 mWheelCentreOffsets[4]; //Offset from cm + + /** + \brief Normalized tire load on each tire (load/rest load) at zero suspension jounce under gravity. + */ + PxReal mTireRestLoads[4]; + + /** + \brief Reciprocal normalized tire load on each tire at zero suspension jounce under gravity. + */ + PxReal mRecipTireRestLoads[4]; + + /** + \brief Scene query filter data used by each suspension line. + Anything relating to the actor belongs in PxVehicleWheels. + */ + PxFilterData mSqFilterData[4]; + + /** + \brief Mapping between wheel id and shape id. + The PxShape that corresponds to the ith wheel can be found with + If mWheelShapeMap[i]<0 then the wheel has no corresponding shape. + Otherwise, the shape corresponds to: + PxShape* shapeBuffer[1]; + mActor->getShapes(shapeBuffer,1,mWheelShapeMap[i]); + Anything relating to the actor belongs in PxVehicleWheels. + */ + PxU8 mWheelShapeMap[4]; + + PxU32 mPad[3]; +}; +PX_COMPILE_TIME_ASSERT(0 == (sizeof(PxVehicleWheels4SimData) & 15)); + +class PxVehicleWheels4DynData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + PxVehicleWheels4DynData() + : mRaycastResults(NULL), + mSweepResults(NULL) + { + setToRestState(); + } + ~PxVehicleWheels4DynData() + { + } + + bool isValid() const {return true;} + + static void getBinaryMetaData(PxOutputStream& stream); + + void setToRestState() + { + for(PxU32 i=0;i<4;i++) + { + mWheelSpeeds[i]=0.0f; + mCorrectedWheelSpeeds[i]=0.0f; + mWheelRotationAngles[i]=0.0f; + mTireLowForwardSpeedTimers[i]=0.0f; + mTireLowSideSpeedTimers[i]=0.0f; + mJounces[i] = PX_MAX_F32; + } + PxMemZero(mQueryOrCachedHitResults, sizeof(SuspLineSweep)); + + mRaycastResults = NULL; + mSweepResults = NULL; + mHasCachedRaycastHitPlane = false; + } + + void setInternalDynamicsToZero() + { + for(PxU32 i=0;i<4;i++) + { + mWheelSpeeds[i] = 0.0f; + mCorrectedWheelSpeeds[i] = 0.0f; + mJounces[i] = PX_MAX_F32; //Ensure that the jounce speed is zero when the car wakes up again. + } + } + + /** + \brief Rotation speeds of wheels + @see PxVehicle4WSetToRestState, PxVehicle4WGetWheelRotationSpeed, PxVehicle4WGetEngineRotationSpeed + */ + PxReal mWheelSpeeds[4]; + + /** + \brief Rotation speeds of wheels used to update the wheel rotation angles. + */ + PxReal mCorrectedWheelSpeeds[4]; + + /** + \brief Reported rotation angle about rolling axis. + @see PxVehicle4WSetToRestState, PxVehicle4WGetWheelRotationAngle + */ + PxReal mWheelRotationAngles[4]; + + /** + \brief Timers used to trigger sticky friction to hold the car perfectly at rest. + \note Used only internally. + */ + PxReal mTireLowForwardSpeedTimers[4]; + + /** + \brief Timers used to trigger sticky friction to hold the car perfectly at rest. + \note Used only internally. + */ + PxReal mTireLowSideSpeedTimers[4]; + + /** + \brief Previous suspension jounce. + \note Used only internally to compute the jounce speed by comparing cached jounce and latest jounce. + */ + PxReal mJounces[4]; + + struct SuspLineSweep + { + /** + \brief Geometry suspension line sweep used in most recent scene query. + @see PxVehicleSuspensionSweeps + */ + PxGeometryHolder mGometries[4]; + + /** + \brief Start poses of suspension line sweep used in most recent scene query. + @see PxVehicleSuspensionSweeps + */ + PxTransform mStartPose[4]; + + /** + \brief Directions of suspension line sweeps used in most recent scene query. + @see PxVehicleSuspensionSweeps + */ + PxVec3 mDirs[4]; + + /** + \brief Lengths of suspension line sweeps used in most recent scene query. + @see PxVehicleSuspensionSweeps + */ + PxReal mLengths[4]; + }; + + struct SuspLineRaycast + { + /** + \brief Start point of suspension line raycasts used in most recent scene query. + @see PxVehicleSuspensionRaycasts + */ + PxVec3 mStarts[4]; + + /** + \brief Directions of suspension line raycasts used in most recent scene query. + @see PxVehicleSuspensionRaycasts + */ + PxVec3 mDirs[4]; + + /** + \brief Lengths of suspension line raycasts used in most recent scene query. + @see PxVehicleSuspensionRaycasts + */ + PxReal mLengths[4]; + + PxU32 mPad[16]; + }; + + struct CachedSuspLineSceneQuerytHitResult + { + /** + \brief Cached raycast hit planes. These are the planes found from the last scene queries. + @see PxVehicleSuspensionRaycasts, PxVehicleSuspensionSweeps + */ + PxVec4 mPlanes[4]; + + /** + \brief Cached friction. + @see PxVehicleSuspensionRaycasts, PxVehicleSuspensionSweeps + */ + PxF32 mFrictionMultipliers[4]; + + /** + \brief Cached raycast hit distance. These are the hit distances found from the last scene queries. + */ + PxF32 mDistances[4]; + + /** + \brief Cached raycast hit counts. These are the hit counts found from the last scene queries. + @see PxVehicleSuspensionRaycasts, , PxVehicleSuspensionSweeps + */ + PxU16 mCounts[4]; + + /** + \brief Store 0 if cached results are from raycasts, store 1 if cached results are from sweeps. + */ + PxU16 mQueryTypes[4]; + + PxU32 mPad1[16]; + }; + + /** + \brief We either have a fresh raycast that was just performed or a cached raycast result that will be used if no raycast was just performed. + */ + PxU8 mQueryOrCachedHitResults[sizeof(SuspLineSweep)]; + + /** + \brief Used only internally. + */ + void setVehicleConstraintShader(PxVehicleConstraintShader* shader) {mVehicleConstraints=shader;} + PxVehicleConstraintShader& getVehicletConstraintShader() const {return *mVehicleConstraints;} + +private: + + //Susp limits and sticky tire friction for all wheels. + PxVehicleConstraintShader* mVehicleConstraints; + +public: + + /** + \brief Set by PxVehicleSuspensionRaycasts + @see PxVehicleSuspensionRaycasts + */ + const PxRaycastQueryResult* mRaycastResults; + + /** + \brief Set by PxVehicleSuspensionSweeps + @see PxVehicleSuspensionSweeps + */ + const PxSweepQueryResult* mSweepResults; + + /** + \brief Set true if a raycast hit plane has been recorded and cached. + This requires a raycast to be performed and then followed by PxVehicleUpdates + at least once. Reset to false in setToRestState. + */ + bool mHasCachedRaycastHitPlane; + + +#if PX_P64_FAMILY + PxU32 mPad[12]; +#endif +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleWheels4DynData) & 15)); +PX_COMPILE_TIME_ASSERT((0 == (sizeof(PxVehicleWheels4DynData::SuspLineSweep) & 0x0f))); +PX_COMPILE_TIME_ASSERT(sizeof(PxVehicleWheels4DynData::SuspLineRaycast) <= sizeof(PxVehicleWheels4DynData::SuspLineSweep)); +PX_COMPILE_TIME_ASSERT(sizeof(PxVehicleWheels4DynData::CachedSuspLineSceneQuerytHitResult) <= sizeof(PxVehicleWheels4DynData::SuspLineSweep)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_SUSPWHEELTIRE_H diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp new file mode 100644 index 000000000..57a5df790 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "PxVehicleTireFriction.h" +#include "CmPhysXCommon.h" +#include "PsFoundation.h" + +namespace physx +{ + +PX_FORCE_INLINE PxU32 computeByteSize(const PxU32 maxNbTireTypes, const PxU32 maxNbSurfaceTypes) +{ + PxU32 byteSize = ((sizeof(PxU32)*(maxNbTireTypes*maxNbSurfaceTypes) + 15) & ~15); + byteSize += ((sizeof(PxMaterial*)*maxNbSurfaceTypes + 15) & ~15); + byteSize += ((sizeof(PxVehicleDrivableSurfaceType)*maxNbSurfaceTypes + 15) & ~15); + byteSize += ((sizeof(PxVehicleDrivableSurfaceToTireFrictionPairs) + 15) & ~ 15); + return byteSize; +} + +PxVehicleDrivableSurfaceToTireFrictionPairs* PxVehicleDrivableSurfaceToTireFrictionPairs::allocate +(const PxU32 maxNbTireTypes, const PxU32 maxNbSurfaceTypes) +{ + PX_CHECK_AND_RETURN_VAL(maxNbSurfaceTypes <= eMAX_NB_SURFACE_TYPES, "maxNbSurfaceTypes must be less than eMAX_NB_SURFACE_TYPES", NULL); + + PxU32 byteSize = computeByteSize(maxNbTireTypes, maxNbSurfaceTypes); + PxU8* ptr = static_cast(PX_ALLOC(byteSize, "PxVehicleDrivableSurfaceToTireFrictionPairs")); + PxMemSet(ptr, 0, byteSize); + PxVehicleDrivableSurfaceToTireFrictionPairs* pairs = reinterpret_cast(ptr); + + pairs->mPairs = NULL; + pairs->mDrivableSurfaceMaterials = NULL; + pairs->mDrivableSurfaceTypes = NULL; + pairs->mNbTireTypes = 0; + pairs->mMaxNbTireTypes = maxNbTireTypes; + pairs->mNbSurfaceTypes = 0; + pairs->mMaxNbSurfaceTypes = maxNbSurfaceTypes; + + return pairs; +} + +void PxVehicleDrivableSurfaceToTireFrictionPairs::setup +(const PxU32 numTireTypes, const PxU32 numSurfaceTypes, const PxMaterial** drivableSurfaceMaterials, const PxVehicleDrivableSurfaceType* drivableSurfaceTypes) +{ + PX_CHECK_AND_RETURN(numTireTypes <= mMaxNbTireTypes, "numTireTypes must be less than mMaxNumSurfaceTypes"); + PX_CHECK_AND_RETURN(numSurfaceTypes <= mMaxNbSurfaceTypes, "numSurfaceTypes must be less than mMaxNumSurfaceTypes"); + + PxU8* ptr = reinterpret_cast(this); + + const PxU32 maxNbTireTypes = mMaxNbTireTypes; + const PxU32 maxNbSurfaceTypes = mMaxNbSurfaceTypes; + PxU32 byteSize = computeByteSize(mMaxNbTireTypes, mMaxNbSurfaceTypes); + PxMemSet(ptr, 0, byteSize); + mMaxNbTireTypes = maxNbTireTypes; + mMaxNbSurfaceTypes = maxNbSurfaceTypes; + + PxVehicleDrivableSurfaceToTireFrictionPairs* pairs = reinterpret_cast(ptr); + ptr += ((sizeof(PxVehicleDrivableSurfaceToTireFrictionPairs) + 15) & ~ 15); + + mPairs = reinterpret_cast(ptr); + ptr += ((sizeof(PxU32)*(numTireTypes*numSurfaceTypes) + 15) & ~15); + mDrivableSurfaceMaterials = reinterpret_cast(ptr); + ptr += ((sizeof(PxMaterial*)*numSurfaceTypes + 15) & ~15); + mDrivableSurfaceTypes = reinterpret_cast(ptr); + ptr += ((sizeof(PxVehicleDrivableSurfaceType)*numSurfaceTypes +15) & ~15); + + for(PxU32 i=0;imNbTireTypes=numTireTypes; + pairs->mNbSurfaceTypes=numSurfaceTypes; +} + +void PxVehicleDrivableSurfaceToTireFrictionPairs::release() +{ + PX_FREE(this); +} + +void PxVehicleDrivableSurfaceToTireFrictionPairs::setTypePairFriction(const PxU32 surfaceType, const PxU32 tireType, const PxReal value) +{ + PX_CHECK_AND_RETURN(tireType 0.0f && pointRejectAngle < PxPi, "PxVehicleSetSweepHitRejectionAngles - pointRejectAngle must be in range (0, Pi)"); + PX_CHECK_AND_RETURN(normalRejectAngle > 0.0f && normalRejectAngle < PxPi, "PxVehicleSetSweepHitRejectionAngles - normalRejectAngle must be in range (0, Pi)"); + gPointRejectAngleThreshold = PxCos(pointRejectAngle); + gNormalRejectAngleThreshold = PxCos(normalRejectAngle); +} + +//////////////////////////////////////////////////////////////////////////// +//Implementation of public api function PxVehicleSetSweepHitRejectionAngles +//////////////////////////////////////////////////////////////////////////// + +const PxF32 gMaxHitActorAccelerationDefault = PX_MAX_REAL; +PxF32 gMaxHitActorAcceleration; + +void PxVehicleSetMaxHitActorAcceleration(const PxF32 maxHitActorAcceleration) +{ + PX_CHECK_AND_RETURN(maxHitActorAcceleration >= 0.0f, "PxVehicleSetMaxHitActorAcceleration - maxHitActorAcceleration must be greater than or equal to zero"); + gMaxHitActorAcceleration = maxHitActorAcceleration; +} + +//////////////////////////////////////////////////////////////////////////// +//Set all defaults from PxVehicleInitSDK +//////////////////////////////////////////////////////////////////////////// + +void setVehicleDefaults() +{ + gRight = gRightDefault; + gUp = gUpDefault; + gForward = gForwardDefault; + + gApplyForces = gApplyForcesDefault; + + gPointRejectAngleThreshold = gPointRejectAngleThresholdDefault; + gNormalRejectAngleThreshold = gNormalRejectAngleThresholdDefault; + + gMaxHitActorAcceleration = gMaxHitActorAccelerationDefault; +} + +//////////////////////////////////////////////////////////////////////////// +//Called from PxVehicleInitSDK/PxCloseVehicleSDK +//////////////////////////////////////////////////////////////////////////// + +//*********************** + +PxF32 gThresholdForwardSpeedForWheelAngleIntegration=0; +PxF32 gRecipThresholdForwardSpeedForWheelAngleIntegration=0; +PxF32 gMinLatSpeedForTireModel=0; +PxF32 gStickyTireFrictionThresholdSpeed=0; +PxF32 gToleranceScaleLength=0; +PxF32 gMinimumSlipThreshold=0; + +void setVehicleToleranceScale(const PxTolerancesScale& ts) +{ + gThresholdForwardSpeedForWheelAngleIntegration=5.0f*ts.length; + gRecipThresholdForwardSpeedForWheelAngleIntegration=1.0f/gThresholdForwardSpeedForWheelAngleIntegration; + + gMinLatSpeedForTireModel=1.0f*ts.length; + + gStickyTireFrictionThresholdSpeed=0.2f*ts.length; + + gToleranceScaleLength=ts.length; + + gMinimumSlipThreshold = 1e-5f; +} + +void resetVehicleToleranceScale() +{ + gThresholdForwardSpeedForWheelAngleIntegration=0; + gRecipThresholdForwardSpeedForWheelAngleIntegration=0; + + gMinLatSpeedForTireModel=0; + + gStickyTireFrictionThresholdSpeed=0; + + gToleranceScaleLength=0; + + gMinimumSlipThreshold=0; +} + +//*********************** + +const PxSerializationRegistry* gSerializationRegistry=NULL; + +void setSerializationRegistryPtr(const PxSerializationRegistry* sr) +{ + gSerializationRegistry = sr; +} + +const PxSerializationRegistry* resetSerializationRegistryPtr() +{ + const PxSerializationRegistry* tmp = gSerializationRegistry; + gSerializationRegistry = NULL; + return tmp; +} + +//////////////////////////////////////////////////////////////////////////// +//Global values used to trigger and control sticky tire friction constraints. +//////////////////////////////////////////////////////////////////////////// + +const PxF32 gStickyTireFrictionForwardDamping=0.01f; +const PxF32 gStickyTireFrictionSideDamping=0.1f; +const PxF32 gLowForwardSpeedThresholdTime=1.0f; +const PxF32 gLowSideSpeedThresholdTime=1.0f; + +//////////////////////////////////////////////////////////////////////////// +//Global values used to control max iteration count if estimate mode is chosen +//////////////////////////////////////////////////////////////////////////// + +const PxF32 gSolverTolerance = 1e-10f; + +//////////////////////////////////////////////////////////////////////////// +//Compute the sprung masses that satisfy the centre of mass and sprung mass coords. +//////////////////////////////////////////////////////////////////////////// + +#define DETERMINANT_THRESHOLD (1e-6f) + +void computeSprungMasses(const PxU32 numSprungMasses, const PxVec3* sprungMassCoordinates, const PxVec3& centreOfMass, const PxReal totalMass, const PxU32 gravityDirection, PxReal* sprungMasses) +{ +#if PX_CHECKED + PX_CHECK_AND_RETURN(numSprungMasses > 0, "PxVehicleComputeSprungMasses: numSprungMasses must be greater than zero"); + PX_CHECK_AND_RETURN(numSprungMasses <= PX_MAX_NB_WHEELS, "PxVehicleComputeSprungMasses: numSprungMasses must be less than or equal to 20"); + for(PxU32 i=0;i 0.0f, "PxVehicleComputeSprungMasses: totalMass must be greater than zero"); + PX_CHECK_AND_RETURN(gravityDirection<=2, "PxVehicleComputeSprungMasses: gravityDirection must be 0 or 1 or 2"); + PX_CHECK_AND_RETURN(sprungMasses, "PxVehicleComputeSprungMasses: sprungMasses must be a non-null pointer"); +#endif + + if(1==numSprungMasses) + { + sprungMasses[0]=totalMass; + } + else if(2==numSprungMasses) + { + PxVec3 v=sprungMassCoordinates[0]; + v[gravityDirection]=0; + PxVec3 w=sprungMassCoordinates[1]-sprungMassCoordinates[0]; + w[gravityDirection]=0; + w.normalize(); + + PxVec3 cm=centreOfMass; + cm[gravityDirection]=0; + PxF32 t=w.dot(cm-v); + PxVec3 p=v+w*t; + + PxVec3 x0=sprungMassCoordinates[0]; + x0[gravityDirection]=0; + PxVec3 x1=sprungMassCoordinates[1]; + x1[gravityDirection]=0; + const PxF32 r0=(x0-p).dot(w); + const PxF32 r1=(x1-p).dot(w); + + PX_CHECK_AND_RETURN(PxAbs(r0-r1) > DETERMINANT_THRESHOLD, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); + + const PxF32 m0=totalMass*r1/(r1-r0); + const PxF32 m1=totalMass-m0; + + sprungMasses[0]=m0; + sprungMasses[1]=m1; + } + else if(3==numSprungMasses) + { + const PxU32 d0=(gravityDirection+1)%3; + const PxU32 d1=(gravityDirection+2)%3; + + MatrixNN A(3); + VectorN b(3); + A.set(0,0,sprungMassCoordinates[0][d0]); + A.set(0,1,sprungMassCoordinates[1][d0]); + A.set(0,2,sprungMassCoordinates[2][d0]); + A.set(1,0,sprungMassCoordinates[0][d1]); + A.set(1,1,sprungMassCoordinates[1][d1]); + A.set(1,2,sprungMassCoordinates[2][d1]); + A.set(2,0,1.f); + A.set(2,1,1.f); + A.set(2,2,1.f); + b[0]=totalMass*centreOfMass[d0]; + b[1]=totalMass*centreOfMass[d1]; + b[2]=totalMass; + + VectorN result(3); + MatrixNNLUSolver solver; + solver.decomposeLU(A); + PX_CHECK_AND_RETURN(PxAbs(solver.getDet()) > DETERMINANT_THRESHOLD, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); + solver.solve(b,result); + + sprungMasses[0]=result[0]; + sprungMasses[1]=result[1]; + sprungMasses[2]=result[2]; + } + else if(numSprungMasses>=4) + { + const PxU32 d0=(gravityDirection+1)%3; + const PxU32 d1=(gravityDirection+2)%3; + + const PxF32 mbar = totalMass/(numSprungMasses*1.0f); + + //See http://en.wikipedia.org/wiki/Lagrange_multiplier + //particularly the section on multiple constraints. + + //3 Constraint equations. + //g0 = sum_ xi*mi=xcm + //g1 = sum_ zi*mi=zcm + //g2 = sum_ mi = totalMass + //Minimisation function to achieve solution with minimum mass variance. + //f = sum_ (mi - mave)^2 + //Lagrange terms (N equations, N+3 unknowns) + //2*mi - xi*lambda0 - zi*lambda1 - 1*lambda2 = 2*mave + + MatrixNN A(numSprungMasses+3); + VectorN b(numSprungMasses+3); + + //g0, g1, g2 + for(PxU32 i=0;i DETERMINANT_THRESHOLD, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); + + for(PxU32 i=0;i= 0, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); + cm += sprungMassCoordinates[i]*sprungMasses[i]; + m += sprungMasses[i]; + } + cm *= (1.0f/totalMass); + PX_CHECK_AND_RETURN((PxAbs(totalMass - m)/totalMass) < 1e-3f, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); + PxVec3 diff = cm - centreOfMass; + diff[gravityDirection]=0; + const PxF32 diffMag = diff.magnitude(); + PX_CHECK_AND_RETURN(numSprungMasses <=2 || diffMag < 1e-3f, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); +#endif +} + +//////////////////////////////////////////////////////////////////////////// +//Work out if all wheels are in the air. +//////////////////////////////////////////////////////////////////////////// + +bool PxVehicleIsInAir(const PxVehicleWheelQueryResult& vehWheelQueryResults) +{ + if(!vehWheelQueryResults.wheelQueryResults) + { + return true; + } + + for(PxU32 i=0;igetGlobalPose(); + vehWheelTransform = contactModifyPair.transform[0]; + isOtherDynamic = contactModifyPair.actor[1] && contactModifyPair.actor[1]->is(); + } + else + { + PX_ASSERT(contactModifyPair.actor[1] == vehActor); + normalMultiplier = -1.0f; + targetVelMultiplier = -1.0f; + vehActorTransform = contactModifyPair.actor[1]->getGlobalPose(); + vehWheelTransform = contactModifyPair.transform[1]; + isOtherDynamic = contactModifyPair.actor[0] && contactModifyPair.actor[0]->is(); + } + + //Compute the "right" vector of the transform. + const PxVec3 right = vehWheelTransform.q.rotate(gRight); + + //The wheel transform includes rotation about the rolling axis. + //We want to compute the wheel transform at zero wheel rotation angle. + PxTransform correctedWheelShapeTransform; + { + const PxF32 wheelRotationAngle = vehicle.mWheelsDynData.getWheelRotationAngle(wheelId); + const PxQuat wheelRotateQuat(-wheelRotationAngle, right); + correctedWheelShapeTransform = PxTransform(vehWheelTransform.p, wheelRotateQuat*vehWheelTransform.q); + } + + //Construct a plane for the wheel + //n.p + d = 0 + PxPlane wheelPlane; + wheelPlane.n = right; + wheelPlane.d = -(right.dot(correctedWheelShapeTransform.p)); + + //Compute the suspension travel vector. + const PxVec3 suspTravelDir = correctedWheelShapeTransform.rotate(vehicle.mWheelsSimData.getSuspTravelDirection(wheelId)); + + //Get the wheel centre. + const PxVec3 wheelCentre = correctedWheelShapeTransform.p; + + //Test each point. + PxContactSet& contactSet = contactModifyPair.contacts; + const PxU32 numContacts = contactSet.size(); + for(PxU32 i = 0; i < numContacts; i++) + { + //Get the next contact point to analyse. + const PxVec3& contactPoint = contactSet.getPoint(i); + bool ignorePoint = false; + + //Project the contact point on to the wheel plane. + const PxF32 distanceToPlane = wheelPlane.n.dot(contactPoint) + wheelPlane.d; + const PxVec3 contactPointOnPlane = contactPoint - wheelPlane.n*distanceToPlane; + + //Construct a vector from the wheel centre to the contact point on the plane. + PxVec3 dir = contactPointOnPlane - wheelCentre; + const PxF32 effectiveRadius = dir.normalize(); + + if(!ignorePoint && rejectPoints) + { + //Work out the dot product of the suspension direction and the direction from wheel centre to contact point. + const PxF32 dotProduct = dir.dot(suspTravelDir); + if (dotProduct > gPointRejectAngleThreshold) + { + ignorePoint = true; + numIgnoredContacts++; + contactSet.ignore(i); + } + } + + //Ignore contact normals that are near the raycast direction. + if(!ignorePoint && rejectNormals) + { + const PxVec3& contactNormal = contactSet.getNormal(i) * normalMultiplier; + const PxF32 dotProduct = -(contactNormal.dot(suspTravelDir)); + if(dotProduct > gNormalRejectAngleThreshold) + { + ignorePoint = true; + numIgnoredContacts++; + contactSet.ignore(i); + } + } + + //For points that remain we want to modify the contact speed to account for the spinning wheel. + //We also want the applied impulse to go through the suspension geometry so we set the contact point to be the suspension force + //application point. + if(!ignorePoint) + { + //Compute the tangent velocity. + //Retain only the component that lies perpendicular to the contact normal. + const PxF32 wheelRotationSpeed = vehicle.mWheelsDynData.getWheelRotationSpeed(wheelId); + const PxVec3 tangentVelocityDir = right.cross(dir); + PxVec3 tangentVelocity = tangentVelocityDir*(effectiveRadius*wheelRotationSpeed); + tangentVelocity -= contactSet.getNormal(i)*(tangentVelocity.dot(contactSet.getNormal(i))); + + //We want to add velocity in the opposite direction to the tangent velocity. + const PxVec3 targetTangentVelocity = -wheelTangentVelocityMultiplier*tangentVelocity; + + //Relative velocity is computed from actor0 - actor1 + //If vehicle is actor 0 we want to add to the target velocity: targetVelocity = [(vel0 + targetTangentVelocity) - vel1] = vel0 - vel1 + targetTangentVelocity + //If vehicle is actor 1 we want to subtract from the target velocity: targetVelocity = [vel0 - (vel1 + targetTangentVelocity)] = vel0 - vel1 - targetTangentVelocity + const PxVec3 targetVelocity = targetTangentVelocity*targetVelMultiplier; + + //Add the target velocity. + contactSet.setTargetVelocity(i, targetVelocity); + + //Set the max impulse that can be applied. + //Only apply this if the wheel has hit a dynamic. + if (isOtherDynamic) + { + contactSet.setMaxImpulse(i, maxImpulse); + } + + //Set the contact point to be the suspension force application point because all forces applied through the wheel go through the suspension geometry. + const PxVec3 suspAppPoint = vehActorTransform.transform(vehActor->getCMassLocalPose().p + vehicle.mWheelsSimData.getSuspForceAppPointOffset(wheelId)); + contactSet.setPoint(i, suspAppPoint); + } + } + + return numIgnoredContacts; +} + +//////////////////////////////////////////////////////////////////////////// +//Enable a 4W vehicle in either tadpole or delta configuration. +//////////////////////////////////////////////////////////////////////////// + +void computeDirection(PxU32& rightDirection, PxU32& upDirection) +{ + //Work out the up and right vectors. + rightDirection = 0xffffffff; + if(gRight == PxVec3(1.f,0,0) || gRight == PxVec3(-1.f,0,0)) + { + rightDirection = 0; + } + else if(gRight == PxVec3(0,1.f,0) || gRight == PxVec3(0,-1.f,0)) + { + rightDirection = 1; + } + else if(gRight == PxVec3(0,0,1.f) || gRight == PxVec3(0,0,-1.f)) + { + rightDirection = 2; + } + upDirection = 0xffffffff; + if(gUp== PxVec3(1.f,0,0) || gUp == PxVec3(-1.f,0,0)) + { + upDirection = 0; + } + else if(gUp == PxVec3(0,1.f,0) || gUp == PxVec3(0,-1.f,0)) + { + upDirection = 1; + } + else if(gUp == PxVec3(0,0,1.f) || gUp == PxVec3(0,0,-1.f)) + { + upDirection = 2; + } +} + +void enable3WMode(const PxU32 rightDirection, const PxU32 upDirection, const bool removeFrontWheel, PxVehicleWheelsSimData& wheelsSimData, PxVehicleWheelsDynData& wheelsDynData, PxVehicleDriveSimData4W& driveSimData) +{ + PX_ASSERT(rightDirection < 3); + PX_ASSERT(upDirection < 3); + + const PxU32 wheelToRemove = removeFrontWheel ? PxVehicleDrive4WWheelOrder::eFRONT_LEFT : PxVehicleDrive4WWheelOrder::eREAR_LEFT; + const PxU32 wheelToModify = removeFrontWheel ? PxVehicleDrive4WWheelOrder::eFRONT_RIGHT : PxVehicleDrive4WWheelOrder::eREAR_RIGHT; + + //Disable the wheel. + wheelsSimData.disableWheel(wheelToRemove); + + //Make sure the wheel's corresponding PxShape does not get posed again. + wheelsSimData.setWheelShapeMapping(wheelToRemove, -1); + + //Set the angular speed to 0.0f + wheelsDynData.setWheelRotationSpeed(wheelToRemove, 0.0f); + + //Disable Ackermann steering. + //If the front wheel is to be removed and the front wheels can steer then disable Ackermann correction. + //If the rear wheel is to be removed and the rear wheels can steer then disable Ackermann correction. + if(removeFrontWheel && + (wheelsSimData.getWheelData(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT).mMaxSteer!=0.0f || + wheelsSimData.getWheelData(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).mMaxSteer!=0.0f)) + { + PxVehicleAckermannGeometryData ackermannData=driveSimData.getAckermannGeometryData(); + ackermannData.mAccuracy=0.0f; + driveSimData.setAckermannGeometryData(ackermannData); + } + if(!removeFrontWheel && + (wheelsSimData.getWheelData(PxVehicleDrive4WWheelOrder::eREAR_RIGHT).mMaxSteer!=0.0f || + wheelsSimData.getWheelData(PxVehicleDrive4WWheelOrder::eREAR_LEFT).mMaxSteer!=0.0f)) + { + PxVehicleAckermannGeometryData ackermannData=driveSimData.getAckermannGeometryData(); + ackermannData.mAccuracy=0.0f; + driveSimData.setAckermannGeometryData(ackermannData); + } + + //We need to set up the differential to make sure that no drive torque is delivered to the disabled wheel. + PxVehicleDifferential4WData diffData =driveSimData.getDiffData(); + if(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT==wheelToModify) + { + diffData.mFrontBias=PX_MAX_F32; + diffData.mFrontLeftRightSplit=0.0f; + } + else + { + diffData.mRearBias=PX_MAX_F32; + diffData.mRearLeftRightSplit=0.0f; + } + driveSimData.setDiffData(diffData); + + //Now reposition the disabled wheel so that it lies at the center of its axle. + //The following assumes that the front and rear axles lie along the x-axis. + { + PxVec3 wheelCentreOffset=wheelsSimData.getWheelCentreOffset(wheelToModify); + wheelCentreOffset[rightDirection]=0.0f; + wheelsSimData.setWheelCentreOffset(wheelToModify,wheelCentreOffset); + + PxVec3 suspOffset=wheelsSimData.getSuspForceAppPointOffset(wheelToModify); + suspOffset[rightDirection]=0; + wheelsSimData.setSuspForceAppPointOffset(wheelToModify,suspOffset); + + PxVec3 tireOffset=wheelsSimData.getTireForceAppPointOffset(wheelToModify); + tireOffset[rightDirection]=0; + wheelsSimData.setTireForceAppPointOffset(wheelToModify,tireOffset); + } + + //Redistribute the mass supported by 4 wheels among the 3 remaining enabled wheels. + //Compute the total mass supported by all 4 wheels. + const PxF32 totalMass = + wheelsSimData.getSuspensionData(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).mSprungMass + + wheelsSimData.getSuspensionData(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT).mSprungMass + + wheelsSimData.getSuspensionData(PxVehicleDrive4WWheelOrder::eREAR_LEFT).mSprungMass + + wheelsSimData.getSuspensionData(PxVehicleDrive4WWheelOrder::eREAR_RIGHT).mSprungMass; + //Get the wheel cm offsets of the 3 enabled wheels. + PxVec3 cmOffsets[3]= + { + wheelsSimData.getWheelCentreOffset((wheelToRemove+1) % 4), + wheelsSimData.getWheelCentreOffset((wheelToRemove+2) % 4), + wheelsSimData.getWheelCentreOffset((wheelToRemove+3) % 4), + }; + //Re-compute the sprung masses. + PxF32 sprungMasses[3]; + computeSprungMasses(3, cmOffsets, PxVec3(0,0,0), totalMass, upDirection, sprungMasses); + + //Now set the new sprung masses. + //Do this in a way that preserves the natural frequency and damping ratio of the spring. + for(PxU32 i=0;i<3;i++) + { + PxVehicleSuspensionData suspData = wheelsSimData.getSuspensionData((wheelToRemove+1+i) % 4); + + const PxF32 oldSprungMass = suspData.mSprungMass; + const PxF32 oldStrength = suspData.mSpringStrength; + const PxF32 oldDampingRate = suspData.mSpringDamperRate; + const PxF32 oldNaturalFrequency = PxSqrt(oldStrength/oldSprungMass); + + const PxF32 newSprungMass = sprungMasses[i]; + const PxF32 newStrength = oldNaturalFrequency*oldNaturalFrequency*newSprungMass; + const PxF32 newDampingRate = oldDampingRate; + + suspData.mSprungMass = newSprungMass; + suspData.mSpringStrength = newStrength; + suspData.mSpringDamperRate = newDampingRate; + wheelsSimData.setSuspensionData((wheelToRemove+1+i) % 4, suspData); + } +} + + +//////////////////////////////////////////////////////////////////////////// +//Maximum number of allowed blocks of 4 wheels +//////////////////////////////////////////////////////////////////////////// + +#define PX_MAX_NB_SUSPWHEELTIRE4 (PX_MAX_NB_WHEELS >>2) + + +//////////////////////////////////////////////////////////////////////////// +//Pointers to telemetry data. +//Set to NULL if no telemetry data is to be recorded during a vehicle update. +//Functions used throughout vehicle update to record specific vehicle data. +//////////////////////////////////////////////////////////////////////////// + +#if PX_DEBUG_VEHICLE_ON + +//Render data. +PxVec3* gCarTireForceAppPoints=NULL; +PxVec3* gCarSuspForceAppPoints=NULL; + +//Graph data +PxF32* gCarWheelGraphData[PX_MAX_NB_WHEELS]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; +PxF32* gCarEngineGraphData=NULL; + +PX_FORCE_INLINE void updateGraphDataInternalWheelDynamics(const PxU32 startIndex, const PxF32* carWheelSpeeds) +{ + //Grab the internal rotation speeds for graphing before we update them. + if(gCarWheelGraphData[startIndex]) + { + for(PxU32 i=0;i<4;i++) + { + PX_ASSERT((startIndex+i) < PX_MAX_NB_WHEELS); + PX_ASSERT(gCarWheelGraphData[startIndex+i]); + gCarWheelGraphData[startIndex+i][PxVehicleWheelGraphChannel::eWHEEL_OMEGA]=carWheelSpeeds[i]; + } + } +} + +PX_FORCE_INLINE void updateGraphDataInternalEngineDynamics(const PxF32 carEngineSpeed) +{ + if(gCarEngineGraphData) + gCarEngineGraphData[PxVehicleDriveGraphChannel::eENGINE_REVS]=carEngineSpeed; +} + +PX_FORCE_INLINE void updateGraphDataControlInputs(const PxF32 accel, const PxF32 brake, const PxF32 handbrake, const PxF32 steerLeft, const PxF32 steerRight) +{ + if(gCarEngineGraphData) + { + gCarEngineGraphData[PxVehicleDriveGraphChannel::eACCEL_CONTROL]=accel; + gCarEngineGraphData[PxVehicleDriveGraphChannel::eBRAKE_CONTROL]=brake; + gCarEngineGraphData[PxVehicleDriveGraphChannel::eHANDBRAKE_CONTROL]=handbrake; + gCarEngineGraphData[PxVehicleDriveGraphChannel::eSTEER_LEFT_CONTROL]=steerLeft; + gCarEngineGraphData[PxVehicleDriveGraphChannel::eSTEER_RIGHT_CONTROL]=steerRight; + } +} +PX_FORCE_INLINE void updateGraphDataGearRatio(const PxF32 G) +{ + if(gCarEngineGraphData) + gCarEngineGraphData[PxVehicleDriveGraphChannel::eGEAR_RATIO]=G; +} +PX_FORCE_INLINE void updateGraphDataEngineDriveTorque(const PxF32 engineDriveTorque) +{ + if(gCarEngineGraphData) + gCarEngineGraphData[PxVehicleDriveGraphChannel::eENGINE_DRIVE_TORQUE]=engineDriveTorque; +} +PX_FORCE_INLINE void updateGraphDataClutchSlip(const PxF32* wheelSpeeds, const PxF32* aveWheelSpeedContributions, const PxF32 engineSpeed, const PxF32 G) +{ + if(gCarEngineGraphData) + { + PxF32 averageWheelSpeed=0; + for(PxU32 i=0;i<4;i++) + { + averageWheelSpeed+=wheelSpeeds[i]*aveWheelSpeedContributions[i]; + } + averageWheelSpeed*=G; + gCarEngineGraphData[PxVehicleDriveGraphChannel::eCLUTCH_SLIP]=averageWheelSpeed-engineSpeed; + } +} +PX_FORCE_INLINE void updateGraphDataClutchSlipNW(const PxU32 numWheels4, const PxVehicleWheels4DynData* wheelsDynData, const PxF32* aveWheelSpeedContributions, const PxF32 engineSpeed, const PxF32 G) +{ + if(gCarEngineGraphData) + { + PxF32 averageWheelSpeed=0; + for(PxU32 i=0;i0) + { + //Compute the number of bits to right-shift that gives the maximum number of unique hashes. + //Keep searching until we find either a set of completely unique hashes or a peak count of unique hashes. + PxU32 prevShift=0; + PxU32 shift=2; + PxU32 prevNumUniqueHashes=0; + PxU32 currNumUniqueHashes=0; + while( ((currNumUniqueHashes=computeNumUniqueHashes(shift)) > prevNumUniqueHashes) && currNumUniqueHashes!=mNbEntries) + { + prevNumUniqueHashes=currNumUniqueHashes; + prevShift=shift; + shift = (shift << 1); + } + if(currNumUniqueHashes!=mNbEntries) + { + //Stopped searching because we have gone past the peak number of unqiue hashes. + mShift = prevShift; + } + else + { + //Stopped searching because we found a unique hash for each key. + mShift = shift; + } + + //Compute the hash values with the optimum shift. + for(PxU32 i=0;i> shift); + const uintptr_t hash = (ptr & (eHASH_SIZE-1)); + return PxU32(hash); + } + + PxU32 computeNumUniqueHashes(const PxU32 shift) const + { + PxU32 words[eHASH_SIZE >>5]; + PxU8* bitmapBuffer[sizeof(BitMap)]; + BitMap* bitmap=reinterpret_cast(bitmapBuffer); + bitmap->setWords(words, eHASH_SIZE >>5); + + PxU32 numUniqueHashes=0; + PxMemZero(words, sizeof(PxU32)*(eHASH_SIZE >>5)); + for(PxU32 i=0;itest(hash)) + { + bitmap->set(hash); + numUniqueHashes++; + } + } + return numUniqueHashes; + } + + enum + { + eHASH_SIZE=PxVehicleDrivableSurfaceToTireFrictionPairs::eMAX_NB_SURFACE_TYPES + }; + PxU32 mHeadIds[eHASH_SIZE]; + enum + { + eMAX_NB_KEYS=PxVehicleDrivableSurfaceToTireFrictionPairs::eMAX_NB_SURFACE_TYPES + }; + PxU32 mNextIds[eMAX_NB_KEYS]; + + PxU32 mShift; +}; + + +//////////////////////////////////////////////////////////////////////////// +//Compute the suspension line raycast start point and direction. +//////////////////////////////////////////////////////////////////////////// + +PX_INLINE void computeSuspensionRaycast +(const PxTransform& carChassisTrnsfm, const PxVec3& bodySpaceWheelCentreOffset, const PxVec3& bodySpaceSuspTravelDir, const PxF32 radius, const PxF32 maxBounce, + PxVec3& suspLineStart, PxVec3& suspLineDir) +{ + //Direction of raycast. + suspLineDir=carChassisTrnsfm.rotate(bodySpaceSuspTravelDir); + + //Position at top of wheel at maximum compression. + suspLineStart=carChassisTrnsfm.transform(bodySpaceWheelCentreOffset); + suspLineStart-=suspLineDir*(radius+maxBounce); +} + +PX_INLINE void computeSuspensionSweep +(const PxTransform& carChassisTrnsfm, + const PxQuat& wheelLocalPoseRotation, const PxF32 wheelTheta, + const PxVec3 bodySpaceWheelCentreOffset, const PxVec3& bodySpaceSuspTravelDir, const PxF32 radius, const PxF32 maxBounce, + PxTransform& suspStartPose, PxVec3& suspLineDir) +{ + //Direction of raycast. + suspLineDir=carChassisTrnsfm.rotate(bodySpaceSuspTravelDir); + + //Position of wheel at maximum compression. + suspStartPose.p = carChassisTrnsfm.transform(bodySpaceWheelCentreOffset); + suspStartPose.p -= suspLineDir*(radius + maxBounce); + + //Rotation of wheel. + const PxVec3 right = wheelLocalPoseRotation.rotate(gRight); + const PxQuat negativeRotation(-wheelTheta, right); + suspStartPose.q = carChassisTrnsfm.q*(negativeRotation*wheelLocalPoseRotation); +} + + +//////////////////////////////////////////////////////////////////////////// +//Functions used to integrate rigid body transform and velocity. +//The sub-step system divides a specified timestep into N equal sub-steps +//and integrates the velocity and transform each sub-step. +//After all sub-steps are complete the velocity required to move the +//associated PxRigidBody from the start transform to the transform at the end +//of the timestep is computed and set. If the update mode is chosen to be +//acceleration then the acceleration is computed/set that will move the rigid body +//from the start to end transform. The PxRigidBody never actually has its transform +//updated and only has its velocity or acceleration set at the very end of the timestep. +//////////////////////////////////////////////////////////////////////////// + +PX_INLINE void integrateBody +(const PxF32 inverseMass, const PxVec3& invInertia, const PxVec3& force, const PxVec3& torque, const PxF32 dt, + PxVec3& v, PxVec3& w, PxTransform& t) +{ + //Integrate linear velocity. + v+=force*(inverseMass*dt); + + //Integrate angular velocity. + PxMat33 inverseInertia; + transformInertiaTensor(invInertia, PxMat33(t.q), inverseInertia); + w+=inverseInertia*(torque*dt); + + //Integrate position. + t.p+=v*dt; + + //Integrate quaternion. + PxQuat wq(w.x,w.y,w.z,0.0f); + PxQuat q=t.q; + PxQuat qdot=wq*q*(dt*0.5f); + q+=qdot; + q.normalize(); + t.q=q; +} + +/* +PX_INLINE void computeVelocity(const PxTransform& t1, const PxTransform& t2, const PxF32 invDT, PxVec3& v, PxVec3& w) +{ + //Linear velocity. + v = (t2.p - t1.p)*invDT; + + //Angular velocity. + PxQuat qw = (t2.q - t1.q)*t1.q.getConjugate()*(2.0f*invDT); + w.x=qw.x; + w.y=qw.y; + w.z=qw.z; +} +*/ + + +///////////////////////////////////////////////////////////////////////////////////////// +//Use fsel to compute the sign of a float: +1 for positive values, -1 for negative values +///////////////////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE PxF32 computeSign(const PxF32 f) +{ + return physx::intrinsics::fsel(f, physx::intrinsics::fsel(-f, 0.0f, 1.0f), -1.0f); +} + + +///////////////////////////////////////////////////////////////////////////////////////// +//Get the accel/brake/handbrake as floats in range (0,1) from the inputs stored in the vehicle. +//Equivalent for tank involving thrustleft/thrustright and brakeleft/brakeright. +///////////////////////////////////////////////////////////////////////////////////////// + + +PX_FORCE_INLINE void getVehicle4WControlValues(const PxVehicleDriveDynData& driveDynData, PxF32& accel, PxF32& brake, PxF32& handbrake, PxF32& steerLeft, PxF32& steerRight) +{ + accel=driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]; + brake=driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE]; + handbrake=driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE]; + steerLeft=driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT]; + steerRight=driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]; +} + +PX_FORCE_INLINE void getVehicleNWControlValues(const PxVehicleDriveDynData& driveDynData, PxF32& accel, PxF32& brake, PxF32& handbrake, PxF32& steerLeft, PxF32& steerRight) +{ + accel=driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_ACCEL]; + brake=driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_BRAKE]; + handbrake=driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_HANDBRAKE]; + steerLeft=driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_LEFT]; + steerRight=driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_RIGHT]; +} + +PX_FORCE_INLINE void getTankControlValues(const PxVehicleDriveDynData& driveDynData, PxF32& accel, PxF32& brakeLeft, PxF32& brakeRight, PxF32& thrustLeft, PxF32& thrustRight) +{ + accel=driveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL]; + brakeLeft=driveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT]; + brakeRight=driveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT]; + thrustLeft=driveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]; + thrustRight=driveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]; +} + + +//////////////////////////////////////////////////////////////////////////// +//Process autobox to initiate automatic gear changes. +//The autobox can be turned off and simulated externally by setting +//the target gear as required prior to calling PxVehicleUpdates. +//////////////////////////////////////////////////////////////////////////// + + +PX_FORCE_INLINE PxF32 processAutoBox(const PxU32 accelIndex, const PxF32 timestep, const PxVehicleDriveSimData& vehCoreSimData, PxVehicleDriveDynData& vehDynData) +{ + PX_ASSERT(vehDynData.getUseAutoGears()); + + PxF32 autoboxCompensatedAnalogAccel = vehDynData.mControlAnalogVals[accelIndex]; + + //If still undergoing a gear change triggered by the autobox + //then turn off the accelerator pedal. This happens in autoboxes + //to stop the driver revving the engine crazily then damaging the + //clutch when the clutch re-engages at the end of the gear change. + const PxU32 currentGear=vehDynData.getCurrentGear(); + const PxU32 targetGear=vehDynData.getTargetGear(); + if(targetGear!=currentGear && PxVehicleGearsData::eNEUTRAL==currentGear) + { + autoboxCompensatedAnalogAccel = 0; + } + + //Only process the autobox if no gear change is underway and the time passed since the last autobox + //gear change is greater than the autobox latency. + PxF32 autoBoxSwitchTime=vehDynData.getAutoBoxSwitchTime(); + const PxF32 autoBoxLatencyTime=vehCoreSimData.getAutoBoxData().mDownRatios[PxVehicleGearsData::eREVERSE]; + if(targetGear==currentGear && autoBoxSwitchTime > autoBoxLatencyTime) + { + //Work out if the autobox wants to switch up or down. + const PxF32 normalisedEngineOmega=vehDynData.getEngineRotationSpeed()*vehCoreSimData.getEngineData().getRecipMaxOmega(); + const PxVehicleAutoBoxData& autoBoxData=vehCoreSimData.getAutoBoxData(); + + bool gearUp=false; + if(normalisedEngineOmega > autoBoxData.mUpRatios[currentGear] && PxVehicleGearsData::eREVERSE!=currentGear) + { + //If revs too high and not in reverse and not undergoing a gear change then switch up. + gearUp=true; + } + + bool gearDown=false; + if(normalisedEngineOmega < autoBoxData.mDownRatios[currentGear] && currentGear > PxVehicleGearsData::eFIRST) + { + //If revs too low and in gear greater than first and not undergoing a gear change then change down. + gearDown=true; + } + + //Start the gear change and reset the time since the last autobox gear change. + if(gearUp || gearDown) + { + vehDynData.setGearUp(gearUp); + vehDynData.setGearDown(gearDown); + vehDynData.setAutoBoxSwitchTime(0.f); + } + } + else + { + autoBoxSwitchTime+=timestep; + vehDynData.setAutoBoxSwitchTime(autoBoxSwitchTime); + } + + return autoboxCompensatedAnalogAccel; +} + + +//////////////////////////////////////////////////////////////////////////// +//Process gear changes. +//If target gear not equal to current gear then a gear change needs to start. +//The gear change process begins by switching immediately in neutral and +//staying there for a specified time. The process ends by setting current +//gear equal to target gear when the gear switch time has passed. +//This can be bypassed by always forcing target gear = current gear and then +//externally managing gear changes prior to calling PxVehicleUpdates. +//////////////////////////////////////////////////////////////////////////// + +void processGears(const PxF32 timestep, const PxVehicleGearsData& gears, PxVehicleDriveDynData& car) +{ + //const PxVehicleGearsData& gears=car.mVehicleSimData.getGearsData(); + + //Process the gears. + if(car.getGearUp() && gears.mNbRatios-1!=car.getCurrentGear() && car.getCurrentGear()==car.getTargetGear()) + { + //Car wants to go up a gear and can go up a gear and not already undergoing a gear change. + if(PxVehicleGearsData::eREVERSE==car.getCurrentGear()) + { + //In reverse so switch to first through neutral. + car.setGearSwitchTime(0); + car.setTargetGear(PxVehicleGearsData::eFIRST); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + else if(PxVehicleGearsData::eNEUTRAL==car.getCurrentGear()) + { + //In neutral so switch to first and stay in neutral. + car.setGearSwitchTime(0); + car.setTargetGear(PxVehicleGearsData::eFIRST); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + else + { + //Switch up a gear through neutral. + car.setGearSwitchTime(0); + car.setTargetGear(car.getCurrentGear() + 1); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + } + if(car.getGearDown() && PxVehicleGearsData::eREVERSE!=car.getCurrentGear() && car.getCurrentGear()==car.getTargetGear()) + { + //Car wants to go down a gear and can go down a gear and not already undergoing a gear change + if(PxVehicleGearsData::eFIRST==car.getCurrentGear()) + { + //In first so switch to reverse through neutral. + car.setGearSwitchTime(0); + car.setTargetGear(PxVehicleGearsData::eREVERSE); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + else if(PxVehicleGearsData::eNEUTRAL==car.getCurrentGear()) + { + //In neutral so switch to reverse and stay in neutral. + car.setGearSwitchTime(0); + car.setTargetGear(PxVehicleGearsData::eREVERSE); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + else + { + //Switch down a gear through neutral. + car.setGearSwitchTime(0); + car.setTargetGear(car.getCurrentGear() - 1); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + } + if(car.getCurrentGear()!=car.getTargetGear()) + { + if(car.getGearSwitchTime()>gears.mSwitchTime) + { + car.setCurrentGear(car.getTargetGear()); + car.setGearSwitchTime(0); + car.setGearDown(false); + car.setGearUp(false); + } + else + { + car.setGearSwitchTime(car.getGearSwitchTime() + timestep); + } + } +} + +//////////////////////////////////////////////////////////////////////////// +//Helper functions to compute +//1. the gear ratio from the current gear. +//2. the drive torque from the state of the accelerator pedal and torque curve of available torque against engine speed. +//3. engine damping rate (a blend between a rate when not accelerating and a rate when fully accelerating). +//4. clutch strength (rate at which clutch will regulate difference between engine and averaged wheel speed). +//////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE PxF32 computeGearRatio(const PxVehicleGearsData& gearsData, const PxU32 currentGear) +{ + const PxF32 gearRatio=gearsData.mRatios[currentGear]*gearsData.mFinalRatio; + return gearRatio; +} + +PX_FORCE_INLINE PxF32 computeEngineDriveTorque(const PxVehicleEngineData& engineData, const PxF32 omega, const PxF32 accel) +{ + const PxF32 engineDriveTorque=accel*engineData.mPeakTorque*engineData.mTorqueCurve.getYVal(omega*engineData.getRecipMaxOmega()); + return engineDriveTorque; +} + +PX_FORCE_INLINE PxF32 computeEngineDampingRate(const PxVehicleEngineData& engineData, const PxU32 gear, const PxF32 accel) +{ + const PxF32 fullThrottleDamping = engineData.mDampingRateFullThrottle; + const PxF32 zeroThrottleDamping = (PxVehicleGearsData::eNEUTRAL!=gear) ? engineData.mDampingRateZeroThrottleClutchEngaged : engineData.mDampingRateZeroThrottleClutchDisengaged; + const PxF32 engineDamping = zeroThrottleDamping + (fullThrottleDamping-zeroThrottleDamping)*accel; + return engineDamping; +} + +PX_FORCE_INLINE PxF32 computeClutchStrength(const PxVehicleClutchData& clutchData, const PxU32 currentGear) +{ + return ((PxVehicleGearsData::eNEUTRAL!=currentGear) ? clutchData.mStrength : 0.0f); +} + + +//////////////////////////////////////////////////////////////////////////// +//Limited slip differential. +//Split the available drive torque as a fraction of the total between up to 4 driven wheels. +//Compute the fraction that each wheel contributes to the averages wheel speed at the clutch. +//////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE void splitTorque +(const PxF32 w1, const PxF32 w2, const PxF32 diffBias, const PxF32 defaultSplitRatio, + PxF32* t1, PxF32* t2) +{ + PX_ASSERT(computeSign(w1)==computeSign(w2) && 0.0f!=computeSign(w1)); + const PxF32 w1Abs=PxAbs(w1); + const PxF32 w2Abs=PxAbs(w2); + const PxF32 omegaMax=PxMax(w1Abs,w2Abs); + const PxF32 omegaMin=PxMin(w1Abs,w2Abs); + const PxF32 delta=omegaMax-diffBias*omegaMin; + const PxF32 deltaTorque=physx::intrinsics::fsel(delta, delta/omegaMax , 0.0f); + const PxF32 f1=physx::intrinsics::fsel(w1Abs-w2Abs, defaultSplitRatio*(1.0f-deltaTorque), defaultSplitRatio*(1.0f+deltaTorque)); + const PxF32 f2=physx::intrinsics::fsel(w1Abs-w2Abs, (1.0f-defaultSplitRatio)*(1.0f+deltaTorque), (1.0f-defaultSplitRatio)*(1.0f-deltaTorque)); + const PxF32 denom=1.0f/(f1+f2); + *t1=f1*denom; + *t2=f2*denom; + PX_ASSERT((*t1 + *t2) >=0.999f && (*t1 + *t2) <=1.001f); +} + +PX_FORCE_INLINE void computeDiffTorqueRatios +(const PxVehicleDifferential4WData& diffData, const PxF32 handbrake, const PxF32* PX_RESTRICT wheelOmegas, PxF32* PX_RESTRICT diffTorqueRatios) +{ + //If the handbrake is on only deliver torque to the front wheels. + PxU32 type=diffData.mType; + if(handbrake>0) + { + switch(diffData.mType) + { + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD: + type=PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD: + type=PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD: + case PxVehicleDifferential4WData::eMAX_NB_DIFF_TYPES: + break; + } + } + + const PxF32 wfl=wheelOmegas[PxVehicleDrive4WWheelOrder::eFRONT_LEFT]; + const PxF32 wfr=wheelOmegas[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT]; + const PxF32 wrl=wheelOmegas[PxVehicleDrive4WWheelOrder::eREAR_LEFT]; + const PxF32 wrr=wheelOmegas[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]; + + const PxF32 centreBias=diffData.mCentreBias; + const PxF32 frontBias=diffData.mFrontBias; + const PxF32 rearBias=diffData.mRearBias; + + const PxF32 frontRearSplit=diffData.mFrontRearSplit; + const PxF32 frontLeftRightSplit=diffData.mFrontLeftRightSplit; + const PxF32 rearLeftRightSplit=diffData.mRearLeftRightSplit; + + const PxF32 oneMinusFrontRearSplit=1.0f-diffData.mFrontRearSplit; + const PxF32 oneMinusFrontLeftRightSplit=1.0f-diffData.mFrontLeftRightSplit; + const PxF32 oneMinusRearLeftRightSplit=1.0f-diffData.mRearLeftRightSplit; + + const PxF32 swfl=computeSign(wfl); + + //Split a torque of 1 between front and rear. + //Then split that torque between left and right. + PxF32 torqueFrontLeft=0; + PxF32 torqueFrontRight=0; + PxF32 torqueRearLeft=0; + PxF32 torqueRearRight=0; + switch(type) + { + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD: + if(0.0f!=swfl && swfl==computeSign(wfr) && swfl==computeSign(wrl) && swfl==computeSign(wrr)) + { + PxF32 torqueFront,torqueRear; + const PxF32 omegaFront=PxAbs(wfl+wfr); + const PxF32 omegaRear=PxAbs(wrl+wrr); + splitTorque(omegaFront,omegaRear,centreBias,frontRearSplit,&torqueFront,&torqueRear); + splitTorque(wfl,wfr,frontBias,frontLeftRightSplit,&torqueFrontLeft,&torqueFrontRight); + splitTorque(wrl,wrr,rearBias,rearLeftRightSplit,&torqueRearLeft,&torqueRearRight); + torqueFrontLeft*=torqueFront; + torqueFrontRight*=torqueFront; + torqueRearLeft*=torqueRear; + torqueRearRight*=torqueRear; + } + else + { + //TODO: need to handle this case. + torqueFrontLeft=frontRearSplit*frontLeftRightSplit; + torqueFrontRight=frontRearSplit*oneMinusFrontLeftRightSplit; + torqueRearLeft=oneMinusFrontRearSplit*rearLeftRightSplit; + torqueRearRight=oneMinusFrontRearSplit*oneMinusRearLeftRightSplit; + } + break; + + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD: + if(0.0f!=swfl && swfl==computeSign(wfr)) + { + splitTorque(wfl,wfr,frontBias,frontLeftRightSplit,&torqueFrontLeft,&torqueFrontRight); + } + else + { + torqueFrontLeft=frontLeftRightSplit; + torqueFrontRight=oneMinusFrontLeftRightSplit; + } + break; + + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD: + + if(0.0f!=computeSign(wrl) && computeSign(wrl)==computeSign(wrr)) + { + splitTorque(wrl,wrr,rearBias,rearLeftRightSplit,&torqueRearLeft,&torqueRearRight); + } + else + { + torqueRearLeft=rearLeftRightSplit; + torqueRearRight=oneMinusRearLeftRightSplit; + } + break; + + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD: + torqueFrontLeft=frontRearSplit*frontLeftRightSplit; + torqueFrontRight=frontRearSplit*oneMinusFrontLeftRightSplit; + torqueRearLeft=oneMinusFrontRearSplit*rearLeftRightSplit; + torqueRearRight=oneMinusFrontRearSplit*oneMinusRearLeftRightSplit; + break; + + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD: + torqueFrontLeft=frontLeftRightSplit; + torqueFrontRight=oneMinusFrontLeftRightSplit; + break; + + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD: + torqueRearLeft=rearLeftRightSplit; + torqueRearRight=oneMinusRearLeftRightSplit; + break; + + default: + PX_ASSERT(false); + break; + } + + diffTorqueRatios[PxVehicleDrive4WWheelOrder::eFRONT_LEFT]=torqueFrontLeft; + diffTorqueRatios[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT]=torqueFrontRight; + diffTorqueRatios[PxVehicleDrive4WWheelOrder::eREAR_LEFT]=torqueRearLeft; + diffTorqueRatios[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]=torqueRearRight; + + PX_ASSERT(((torqueFrontLeft+torqueFrontRight+torqueRearLeft+torqueRearRight) >= 0.999f) && ((torqueFrontLeft+torqueFrontRight+torqueRearLeft+torqueRearRight) <= 1.001f)); +} + +PX_FORCE_INLINE void computeDiffAveWheelSpeedContributions +(const PxVehicleDifferential4WData& diffData, const PxF32 handbrake, PxF32* PX_RESTRICT diffAveWheelSpeedContributions) +{ + PxU32 type=diffData.mType; + + //If the handbrake is on only deliver torque to the front wheels. + if(handbrake>0) + { + switch(diffData.mType) + { + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD: + type=PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD: + type=PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD: + case PxVehicleDifferential4WData::eMAX_NB_DIFF_TYPES: + break; + } + } + + const PxF32 frontRearSplit=diffData.mFrontRearSplit; + const PxF32 frontLeftRightSplit=diffData.mFrontLeftRightSplit; + const PxF32 rearLeftRightSplit=diffData.mRearLeftRightSplit; + + const PxF32 oneMinusFrontRearSplit=1.0f-diffData.mFrontRearSplit; + const PxF32 oneMinusFrontLeftRightSplit=1.0f-diffData.mFrontLeftRightSplit; + const PxF32 oneMinusRearLeftRightSplit=1.0f-diffData.mRearLeftRightSplit; + + switch(type) + { + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD: + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_LEFT]=frontRearSplit*frontLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT]=frontRearSplit*oneMinusFrontLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_LEFT]=oneMinusFrontRearSplit*rearLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]=oneMinusFrontRearSplit*oneMinusRearLeftRightSplit; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD: + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_LEFT]=frontLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT]=oneMinusFrontLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_LEFT]=0.0f; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]=0.0f; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD: + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_LEFT]=0.0f; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT]=0.0f; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_LEFT]=rearLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]=oneMinusRearLeftRightSplit; + break; + default: + PX_ASSERT(false); + break; + } + + PX_ASSERT((diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_LEFT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_LEFT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]) >= 0.999f && + (diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_LEFT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_LEFT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]) <= 1.001f); +} + + +/////////////////////////////////////////////////////// +//Tank differential +/////////////////////////////////////////////////////// + +void computeTankDiff +(const PxF32 thrustLeft, const PxF32 thrustRight, + const PxU32 numActiveWheels, const bool* activeWheelStates, + PxF32* aveWheelSpeedContributions, PxF32* diffTorqueRatios, PxF32* wheelGearings) +{ + const PxF32 thrustLeftAbs=PxAbs(thrustLeft); + const PxF32 thrustRightAbs=PxAbs(thrustRight); + + //Work out now many left wheels are enabled. + PxF32 numLeftWheels=0.0f; + for(PxU32 i=0;i 0 ? 1.0f/numLeftWheels : 0.0f; + + //Work out now many right wheels are enabled. + PxF32 numRightWheels=0.0f; + for(PxU32 i=1;i 0 ? 1.0f/numRightWheels : 0.0f; + + //Split the diff torque between left and right. + PxF32 diffTorqueRatioLeft=0.5f; + PxF32 diffTorqueRatioRight=0.5f; + if((thrustLeftAbs + thrustRightAbs) > 1e-3f) + { + const PxF32 thrustDiff = 0.5f*(thrustLeftAbs - thrustRightAbs)/(thrustLeftAbs + thrustRightAbs); + diffTorqueRatioLeft += thrustDiff; + diffTorqueRatioRight -= thrustDiff; + + } + diffTorqueRatioLeft *= invNumEnabledWheelsLeft; + diffTorqueRatioRight *= invNumEnabledWheelsRight; + + //Compute the per wheel gearing. + PxF32 wheelGearingLeft=1.0f; + PxF32 wheelGearingRight=1.0f; + if((thrustLeftAbs + thrustRightAbs) > 1e-3f) + { + wheelGearingLeft=computeSign(thrustLeft); + wheelGearingRight=computeSign(thrustRight); + } + + //Compute the contribution of each wheel to the average speed at the clutch. + const PxF32 aveWheelSpeedContributionLeft = 0.5f*invNumEnabledWheelsLeft; + const PxF32 aveWheelSpeedContributionRight = 0.5f*invNumEnabledWheelsRight; + + //Set all the left wheels. + for(PxU32 i=0;i=-1.01f && steer<=1.01f); + PX_ASSERT(steerGain> 5); + + if(bm.test(startId + 0)) + { + activeStates[0]=true; + } + if(bm.test(startId + 1)) + { + activeStates[1]=true; + } + if(bm.test(startId + 2)) + { + activeStates[2]=true; + } + if(bm.test(startId + 3)) + { + activeStates[3]=true; + } +} + + +//////////////////////////////////////////////////////////////////////////// +//Compute the brake and handbrake torques for different vehicle types. +//Also compute a boolean for each tire to know if the brake is applied or not. +//Can't use a single function for all types because not all vehicle types have +//handbrakes and the brake control mechanism is different for different vehicle +//types. +//////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE void computeNoDriveBrakeTorques +(const PxVehicleWheelData* PX_RESTRICT wheelDatas, const PxF32* PX_RESTRICT wheelOmegas, const PxF32* PX_RESTRICT rawBrakeTroques, + PxF32* PX_RESTRICT brakeTorques, bool* PX_RESTRICT isBrakeApplied) +{ + PX_UNUSED(wheelDatas); + + const PxF32 sign0=computeSign(wheelOmegas[0]); + brakeTorques[0]=(-sign0*rawBrakeTroques[0]); + isBrakeApplied[0]=(rawBrakeTroques[0]!=0); + + const PxF32 sign1=computeSign(wheelOmegas[1]); + brakeTorques[1]=(-sign1*rawBrakeTroques[1]); + isBrakeApplied[1]=(rawBrakeTroques[1]!=0); + + const PxF32 sign2=computeSign(wheelOmegas[2]); + brakeTorques[2]=(-sign2*rawBrakeTroques[2]); + isBrakeApplied[2]=(rawBrakeTroques[2]!=0); + + const PxF32 sign3=computeSign(wheelOmegas[3]); + brakeTorques[3]=(-sign3*rawBrakeTroques[3]); + isBrakeApplied[3]=(rawBrakeTroques[3]!=0); +} + +PX_FORCE_INLINE void computeBrakeAndHandBrakeTorques +(const PxVehicleWheelData* PX_RESTRICT wheelDatas, const PxF32* PX_RESTRICT wheelOmegas, const PxF32 brake, const PxF32 handbrake, + PxF32* PX_RESTRICT brakeTorques, bool* isBrakeApplied) +{ + //At zero speed offer no brake torque allowed. + + const PxF32 sign0=computeSign(wheelOmegas[0]); + brakeTorques[0]=(-brake*sign0*wheelDatas[0].mMaxBrakeTorque-handbrake*sign0*wheelDatas[0].mMaxHandBrakeTorque); + isBrakeApplied[0]=((brake*wheelDatas[0].mMaxBrakeTorque+handbrake*wheelDatas[0].mMaxHandBrakeTorque)!=0); + + const PxF32 sign1=computeSign(wheelOmegas[1]); + brakeTorques[1]=(-brake*sign1*wheelDatas[1].mMaxBrakeTorque-handbrake*sign1*wheelDatas[1].mMaxHandBrakeTorque); + isBrakeApplied[1]=((brake*wheelDatas[1].mMaxBrakeTorque+handbrake*wheelDatas[1].mMaxHandBrakeTorque)!=0); + + const PxF32 sign2=computeSign(wheelOmegas[2]); + brakeTorques[2]=(-brake*sign2*wheelDatas[2].mMaxBrakeTorque-handbrake*sign2*wheelDatas[2].mMaxHandBrakeTorque); + isBrakeApplied[2]=((brake*wheelDatas[2].mMaxBrakeTorque+handbrake*wheelDatas[2].mMaxHandBrakeTorque)!=0); + + const PxF32 sign3=computeSign(wheelOmegas[3]); + brakeTorques[3]=(-brake*sign3*wheelDatas[3].mMaxBrakeTorque-handbrake*sign3*wheelDatas[3].mMaxHandBrakeTorque); + isBrakeApplied[3]=((brake*wheelDatas[3].mMaxBrakeTorque+handbrake*wheelDatas[3].mMaxHandBrakeTorque)!=0); +} + +PX_FORCE_INLINE void computeTankBrakeTorques +(const PxVehicleWheelData* PX_RESTRICT wheelDatas, const PxF32* PX_RESTRICT wheelOmegas, const PxF32 brakeLeft, const PxF32 brakeRight, + PxF32* PX_RESTRICT brakeTorques, bool* isBrakeApplied) +{ + //At zero speed offer no brake torque allowed. + + const PxF32 sign0=computeSign(wheelOmegas[0]); + brakeTorques[0]=(-brakeLeft*sign0*wheelDatas[0].mMaxBrakeTorque); + isBrakeApplied[0]=((brakeLeft*wheelDatas[0].mMaxBrakeTorque)!=0); + + const PxF32 sign1=computeSign(wheelOmegas[1]); + brakeTorques[1]=(-brakeRight*sign1*wheelDatas[1].mMaxBrakeTorque); + isBrakeApplied[1]=((brakeRight*wheelDatas[1].mMaxBrakeTorque)!=0); + + const PxF32 sign2=computeSign(wheelOmegas[2]); + brakeTorques[2]=(-brakeLeft*sign2*wheelDatas[2].mMaxBrakeTorque); + isBrakeApplied[2]=((brakeLeft*wheelDatas[2].mMaxBrakeTorque)!=0); + + const PxF32 sign3=computeSign(wheelOmegas[3]); + brakeTorques[3]=(-brakeRight*sign3*wheelDatas[3].mMaxBrakeTorque); + isBrakeApplied[3]=((brakeRight*wheelDatas[3].mMaxBrakeTorque)!=0); +} + + +//////////////////////////////////////////////////////////////////////////// +//Functions to compute inputs to tire force calculation. +//1. Filter the normalised tire load to smooth any spikes in load. +//2. Compute the tire lat and long directions in the ground plane. +//3. Compute the tire lat and long slips. +//4. Compute the friction from a graph of friction vs slip. +//////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE PxF32 computeFilteredNormalisedTireLoad(const PxVehicleTireLoadFilterData& filterData, const PxF32 normalisedLoad) +{ + if(normalisedLoad <= filterData.mMinNormalisedLoad) + { + return filterData.mMinFilteredNormalisedLoad; + } + else if(normalisedLoad >= filterData.mMaxNormalisedLoad) + { + return filterData.mMaxFilteredNormalisedLoad; + } + else + { + const PxF32 x=normalisedLoad; + const PxF32 xmin=filterData.mMinNormalisedLoad; + const PxF32 ymin=filterData.mMinFilteredNormalisedLoad; + const PxF32 ymax=filterData.mMaxFilteredNormalisedLoad; + const PxF32 recipXmaxMinusXMin=filterData.getDenominator(); + return (ymin + (x-xmin)*(ymax-ymin)*recipXmaxMinusXMin); + } +} + +PX_FORCE_INLINE void computeTireDirs(const PxVec3& chassisLatDir, const PxVec3& hitNorm, const PxF32 wheelSteerAngle, PxVec3& tireLongDir, PxVec3& tireLatDir) +{ + PX_ASSERT(chassisLatDir.magnitude()>0.999f && chassisLatDir.magnitude()<1.001f); + PX_ASSERT(hitNorm.magnitude()>0.999f && hitNorm.magnitude()<1.001f); + + //Compute the tire axes in the ground plane. + PxVec3 tzRaw=chassisLatDir.cross(hitNorm); + PxVec3 txRaw=hitNorm.cross(tzRaw); + tzRaw.normalize(); + txRaw.normalize(); + //Rotate the tire using the steer angle. + const PxF32 cosWheelSteer=PxCos(wheelSteerAngle); + const PxF32 sinWheelSteer=PxSin(wheelSteerAngle); + const PxVec3 tz=tzRaw*cosWheelSteer + txRaw*sinWheelSteer; + const PxVec3 tx=txRaw*cosWheelSteer - tzRaw*sinWheelSteer; + tireLongDir=tz; + tireLatDir=tx; +} + +PX_FORCE_INLINE void computeTireSlips +(const PxF32 longSpeed, const PxF32 latSpeed, const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 maxDenominator, + const bool isAccelApplied, const bool isBrakeApplied, + const bool isTank, + PxF32& longSlip, PxF32& latSlip) +{ + PX_ASSERT(maxDenominator>=0.0f); + + const PxF32 longSpeedAbs=PxAbs(longSpeed); + const PxF32 wheelSpeed=wheelOmega*wheelRadius; + const PxF32 wheelSpeedAbs=PxAbs(wheelSpeed); + + //Lateral slip is easy. + latSlip = PxAtan(latSpeed/(longSpeedAbs+gMinLatSpeedForTireModel));//TODO: do we really use PxAbs(vz) as denominator? + + //If nothing is moving just avoid a divide by zero and set the long slip to zero. + if(longSpeed==0 && wheelOmega==0) + { + longSlip=0.0f; + return; + } + + //Longitudinal slip is a bit harder because we can end up wtih zero on the denominator. + if(isTank) + { + if(isBrakeApplied || isAccelApplied) + { + //Wheel experiencing an applied torque. + //Use the raw denominator value plus an offset to avoid anything approaching a divide by zero. + //When accelerating from rest the small denominator will generate really quite large + //slip values, which will, in turn, generate large longitudinal forces. With large + //time-steps this might lead to a temporary oscillation in longSlip direction and an + //oscillation in wheel speed direction. The amplitude of the oscillation should be low + //unless the timestep is really large. + //There's not really an obvious solution to this without setting the denominator offset higher + //(or decreasing the timestep). Setting the denominator higher affects handling everywhere so + //settling for a potential temporary oscillation is probably the least worst compromise. + //Note that we always use longSpeedAbs as denominator because in order to turn on the spot the + //tank needs to get strong longitudinal force when it isn't moving but the wheels are slipping. + longSlip = (wheelSpeed - longSpeed)/(longSpeedAbs + 0.1f*gToleranceScaleLength); + } + else + { + //Wheel not experiencing an applied torque. + //If the denominator becomes too small then the longSlip becomes large and the longitudinal force + //can overshoot zero at large timesteps. This can be really noticeable so it's harder to justify + //not taking action. Further, the car isn't being actually driven so there is a strong case to fiddle + //with the denominator because it doesn't really affect active handling. + //Don't let the denominator fall below a user-specified value. This can be tuned upwards until the + //oscillation in the sign of longSlip disappears. + longSlip = (wheelSpeed - longSpeed)/(PxMax(maxDenominator, PxMax(longSpeedAbs,wheelSpeedAbs))); + } + } + else + { + if(isBrakeApplied || isAccelApplied) + { + //Wheel experiencing an applied torque. + //Use the raw denominator value plus an offset to avoid anything approaching a divide by zero. + //When accelerating from rest the small denominator will generate really quite large + //slip values, which will, in turn, generate large longitudinal forces. With large + //time-steps this might lead to a temporary oscillation in longSlip direction and an + //oscillation in wheel speed direction. The amplitude of the oscillation should be low + //unless the timestep is really large. + //There's not really an obvious solution to this without setting the denominator offset higher + //(or decreasing the timestep). Setting the denominator higher affects handling everywhere so + //settling for a potential temporary oscillation is probably the least worst compromise. + longSlip = (wheelSpeed - longSpeed)/(PxMax(longSpeedAbs,wheelSpeedAbs)+0.1f*gToleranceScaleLength); + } + else + { + //Wheel not experiencing an applied torque. + //If the denominator becomes too small then the longSlip becomes large and the longitudinal force + //can overshoot zero at large timesteps. This can be really noticeable so it's harder to justify + //not taking action. Further, the car isn't being actually driven so there is a strong case to fiddle + //with the denominator because it doesn't really affect active handling. + //Don't let the denominator fall below a user-specified value. This can be tuned upwards until the + //oscillation in the sign of longSlip disappears. + longSlip = (wheelSpeed - longSpeed)/(PxMax(maxDenominator,PxMax(longSpeedAbs,wheelSpeedAbs))); + } + } +} + +PX_FORCE_INLINE void computeTireFriction(const PxVehicleTireData& tireData, const PxF32 longSlip, const PxF32 frictionMultiplier, PxF32& friction) +{ + const PxF32 x0=tireData.mFrictionVsSlipGraph[0][0]; + const PxF32 y0=tireData.mFrictionVsSlipGraph[0][1]; + const PxF32 x1=tireData.mFrictionVsSlipGraph[1][0]; + const PxF32 y1=tireData.mFrictionVsSlipGraph[1][1]; + const PxF32 x2=tireData.mFrictionVsSlipGraph[2][0]; + const PxF32 y2=tireData.mFrictionVsSlipGraph[2][1]; + const PxF32 recipx1Minusx0=tireData.getFrictionVsSlipGraphRecipx1Minusx0(); + const PxF32 recipx2Minusx1=tireData.getFrictionVsSlipGraphRecipx2Minusx1(); + const PxF32 longSlipAbs=PxAbs(longSlip); + PxF32 mu; + if(longSlipAbs=0); + friction=mu*frictionMultiplier; +} + +//////////////////////////////////////////////////////////////////////////// +//Sticky tire constraints. +//Increment a timer each update that a tire has a very low longitudinal speed. +//Activate a sticky constraint when the tire has had an unbroken low long speed +//for at least a threshold time. +//The longer the sticky constraint is active, the slower the target constraint speed +//along the long dir. Quickly tends towards zero. +//When the sticky constraint is activated set the long slip to zero and let +//the sticky constraint take over. +//////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE void updateLowForwardSpeedTimer +(const PxF32 longSpeed, const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 recipWheelRadius, const bool isIntentionToAccelerate, + const PxF32 timestep, PxF32& lowForwardSpeedTime) +{ + PX_UNUSED(wheelRadius); + PX_UNUSED(recipWheelRadius); + + //If the tire is rotating slowly and the forward speed is slow then increment the slow forward speed timer. + //If the intention of the driver is to accelerate the vehicle then reset the timer because the intention has been signalled NOT to bring + //the wheel to rest. + PxF32 longSpeedAbs=PxAbs(longSpeed); + if((longSpeedAbsgLowForwardSpeedThresholdTime) + { + stickyTireActiveFlag=true; + stickyTireTargetSpeed=longSpeed*gStickyTireFrictionForwardDamping; + } +} + +PX_FORCE_INLINE void activateStickyFrictionSideConstraint +(const PxF32 latSpeed, const PxF32 lowSpeedForwardTimer, const PxF32 lowSideSpeedTimer, const bool isIntentionToAccelerate, + bool& stickyTireActiveFlag, PxF32& stickyTireTargetSpeed) +{ + PX_UNUSED(latSpeed); + PX_UNUSED(isIntentionToAccelerate); + + //Setup the sticky friction constraint to bring the vehicle to rest at the tire contact point. + //Only do this if we can guarantee that the intention is to bring the car to rest (no accel pedal applied). + //Smoothly reduce error to zero to avoid bringing car immediately to rest. This avoids graphical glitchiness. + //We're going to replace the lateral tire force with the sticky friction so set the lat slip to zero to ensure zero lat force. + //Apply sticky friction to this tire if + //(1) the low forward speed timer is > 0. + //(2) the accumulated time of low forward speed is greater than a threshold. + stickyTireActiveFlag=false; + stickyTireTargetSpeed=0.0f; + if((lowSpeedForwardTimer > 0) && lowSideSpeedTimer>gLowSideSpeedThresholdTime) + { + stickyTireActiveFlag=true; + stickyTireTargetSpeed=latSpeed*gStickyTireFrictionSideDamping; + } +} + + + +//////////////////////////////////////////////////////////////////////////// +//Default tire force shader function. +//Taken from Michigan tire model. +//Computes tire long and lat forces plus the aligning moment arising from +//the lat force and the torque to apply back to the wheel arising from the +//long force (application of Newton's 3rd law). +//////////////////////////////////////////////////////////////////////////// + + +#define ONE_TWENTYSEVENTH 0.037037f +#define ONE_THIRD 0.33333f +PX_FORCE_INLINE PxF32 smoothingFunction1(const PxF32 K) +{ + //Equation 20 in CarSimEd manual Appendix F. + //Looks a bit like a curve of sqrt(x) for 0=0.0f); + return PxMin(1.0f, K - ONE_THIRD*K*K + ONE_TWENTYSEVENTH*K*K*K); +} +PX_FORCE_INLINE PxF32 smoothingFunction2(const PxF32 K) +{ + //Equation 21 in CarSimEd manual Appendix F. + //Rises to a peak at K=0.75 and falls back to zero by K=3 + PX_ASSERT(K>=0.0f); + return (K - K*K + ONE_THIRD*K*K*K - ONE_TWENTYSEVENTH*K*K*K*K); +} + +void PxVehicleComputeTireForceDefault +(const void* tireShaderData, + const PxF32 tireFriction, + const PxF32 longSlipUnClamped, const PxF32 latSlipUnClamped, const PxF32 camberUnclamped, + const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 recipWheelRadius, + const PxF32 restTireLoad, const PxF32 normalisedTireLoad, const PxF32 tireLoad, + const PxF32 gravity, const PxF32 recipGravity, + PxF32& wheelTorque, PxF32& tireLongForceMag, PxF32& tireLatForceMag, PxF32& tireAlignMoment) +{ + PX_UNUSED(wheelOmega); + PX_UNUSED(recipWheelRadius); + + const PxVehicleTireData& tireData=*reinterpret_cast(tireShaderData); + + PX_ASSERT(tireFriction>0); + PX_ASSERT(tireLoad>0); + + wheelTorque=0.0f; + tireLongForceMag=0.0f; + tireLatForceMag=0.0f; + tireAlignMoment=0.0f; + + //Clamp the slips to a minimum value. + const PxF32 latSlip = PxAbs(latSlipUnClamped) >= gMinimumSlipThreshold ? latSlipUnClamped : 0.0f; + const PxF32 longSlip = PxAbs(longSlipUnClamped) >= gMinimumSlipThreshold ? longSlipUnClamped : 0.0f; + const PxF32 camber = PxAbs(camberUnclamped) >= gMinimumSlipThreshold ? camberUnclamped : 0.0f; + + //If long slip/lat slip/camber are all zero than there will be zero tire force. + if((0==latSlip)&&(0==longSlip)&&(0==camber)) + { + return; + } + + //Compute the lateral stiffness + const PxF32 latStiff=restTireLoad*tireData.mLatStiffY*smoothingFunction1(normalisedTireLoad*3.0f/tireData.mLatStiffX); + + //Get the longitudinal stiffness + const PxF32 longStiff=tireData.mLongitudinalStiffnessPerUnitGravity*gravity; + const PxF32 recipLongStiff=tireData.getRecipLongitudinalStiffnessPerUnitGravity()*recipGravity; + + //Get the camber stiffness. + const PxF32 camberStiff=tireData.mCamberStiffnessPerUnitGravity*gravity; + + //Carry on and compute the forces. + const PxF32 TEff = PxTan(latSlip - camber*camberStiff/latStiff); + const PxF32 K = PxSqrt(latStiff*TEff*latStiff*TEff + longStiff*longSlip*longStiff*longSlip) /(tireFriction*tireLoad); + //const PxF32 KAbs=PxAbs(K); + PxF32 FBar = smoothingFunction1(K);//K - ONE_THIRD*PxAbs(K)*K + ONE_TWENTYSEVENTH*K*K*K; + PxF32 MBar = smoothingFunction2(K); //K - KAbs*K + ONE_THIRD*K*K*K - ONE_TWENTYSEVENTH*KAbs*K*K*K; + //Mbar = PxMin(Mbar, 1.0f); + PxF32 nu=1; + if(K <= 2.0f*PxPi) + { + const PxF32 latOverlLong=latStiff*recipLongStiff; + nu = 0.5f*(1.0f + latOverlLong - (1.0f - latOverlLong)*PxCos(K*0.5f)); + } + const PxF32 FZero = tireFriction*tireLoad / (PxSqrt(longSlip*longSlip + nu*TEff*nu*TEff)); + const PxF32 fz = longSlip*FBar*FZero; + const PxF32 fx = -nu*TEff*FBar*FZero; + //TODO: pneumatic trail. + const PxF32 pneumaticTrail=1.0f; + const PxF32 fMy= nu * pneumaticTrail * TEff * MBar * FZero; + + //We can add the torque to the wheel. + wheelTorque=-fz*wheelRadius; + tireLongForceMag=fz; + tireLatForceMag=fx; + tireAlignMoment=fMy; +} + + +//////////////////////////////////////////////////////////////////////////// +//Functions required to intersect the wheel with the hit plane +//We support raycasts and sweeps. +//////////////////////////////////////////////////////////////////////////// + +bool intersectRayPlane +(const PxTransform& carChassisTrnsfm, + const PxVec3& bodySpaceWheelCentreOffset, const PxVec3& bodySpaceSuspTravelDir, + const PxF32 width, const PxF32 radius, const PxF32 maxCompression, + const PxVec4& hitPlane, + PxF32& jounce, PxVec3& wheelBottomPos) +{ + PX_UNUSED(width); + + //Compute the raycast start pos and direction. + PxVec3 v, w; + computeSuspensionRaycast(carChassisTrnsfm, bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, radius, maxCompression, v, w); + + //If the raycast starts inside the hit plane then return false + if(hitPlane.x*v.x + hitPlane.y*v.y + hitPlane.z*v.z + hitPlane.w < 0.0f) + { + return false; + } + + //Store a point through the centre of the wheel. + //We'll use this later to compute a position at the bottom of the wheel. + const PxVec3 pos = v; + + //Remove this code because we handle tire width with sweeps now. + //Work out if the inner or outer disc is deeper in the plane. + //const PxVec3 latDir = carChassisTrnsfm.rotate(gRight); + //const PxF32 signDot = computeSign(hitNorm.dot(latDir)); + //v -= latDir*(signDot*0.5f*width); + + //Work out the point on the susp line that touches the intersection plane. + //n.(v+wt)+d=0 where n,d describe the plane; v,w describe the susp ray; t is the point on the susp line. + //t=-(n.v + d)/n.w + const PxF32 hitD = hitPlane.w; + + const PxVec3 n = PxVec3(hitPlane.x, hitPlane.y, hitPlane.z); + const PxF32 d = hitD; + const PxF32 T=-(n.dot(v) + d)/(n.dot(w)); + + //The rest pos of the susp line is 2*radius + maxBounce. + const PxF32 restT = 2.0f*radius+maxCompression; + + //Compute the spring compression ie the difference between T and restT. + //+ve means that the spring is compressed + //-ve means that the spring is elongated. + jounce = restT-T; + + //Compute the bottom of the wheel. + //Always choose a point through the centre of the wheel. + wheelBottomPos = pos + w*(restT - jounce); + + return true; +} + +bool intersectPlanes(const PxVec4& a, const PxVec4& b, PxVec3& v, PxVec3& w) +{ + const PxF32 n1x = a.x; + const PxF32 n1y = a.y; + const PxF32 n1z = a.z; + const PxF32 n1d = a.w; + + const PxF32 n2x = b.x; + const PxF32 n2y = b.y; + const PxF32 n2z = b.z; + const PxF32 n2d = b.w; + + PxF32 dx = (n1y * n2z) - (n1z * n2y); + PxF32 dy = (n1z * n2x) - (n1x * n2z); + PxF32 dz = (n1x * n2y) - (n1y * n2x); + + const PxF32 dx2 = dx * dx; + const PxF32 dy2 = dy * dy; + const PxF32 dz2 = dz * dz; + + PxF32 px, py, pz; + bool success = true; + if ((dz2 > dy2) && (dz2 > dx2) && (dz2 > 0)) + { + px = ((n1y * n2d) - (n2y * n1d)) / dz; + py = ((n2x * n1d) - (n1x * n2d)) / dz; + pz = 0; + } + else if ((dy2 > dx2) && (dy2 > 0)) + { + px = -((n1z * n2d) - (n2z * n1d)) / dy; + py = 0; + pz = -((n2x * n1d) - (n1x * n2d)) / dy; + } + else if (dx2 > 0) + { + px = 0; + py = ((n1z * n2d) - (n2z * n1d)) / dx; + pz = ((n2y * n1d) - (n1y * n2d)) / dx; + } + else + { + px=0; + py=0; + pz=0; + success=false; + } + + const PxF32 ld = PxSqrt(dx2 + dy2 + dz2); + + dx /= ld; + dy /= ld; + dz /= ld; + + w = PxVec3(dx,dy,dz); + v = PxVec3(px,py,pz); + + return success; +} + +bool intersectCylinderPlane +(const PxTransform& wheelPoseAtZeroJounce, const PxVec3 suspDir, + const PxF32 width, const PxF32 radius, const PxF32 maxCompression, + const PxVec4& hitPlane, + const bool rejectFromThresholds, + PxF32& jounce, PxVec3& wheelBottomPos) +{ + PX_UNUSED(maxCompression); + PX_UNUSED(width); + + //Reject based on the contact normal. + if (rejectFromThresholds) + { + if (suspDir.dot(-hitPlane.getXYZ()) < gNormalRejectAngleThreshold) + { + return false; + } + } + + //Construct the wheel plane that contains the wheel disc. + PxVec4 wheelPlane; + { + const PxVec3 n = wheelPoseAtZeroJounce.rotate(gRight); + const PxF32 d = - n.dot(wheelPoseAtZeroJounce.p); + wheelPlane.x = n.x; + wheelPlane.y = n.y; + wheelPlane.z = n.z; + wheelPlane.w = d; + } + + //Intersect the plane of the wheel with the hit plane. + //This generates an intersection edge. + PxVec3 intersectionEdgeV, intersectionEdgeW; + const bool intersectPlaneSuccess = intersectPlanes(wheelPlane, hitPlane, intersectionEdgeV, intersectionEdgeW); + if(!intersectPlaneSuccess) + { + jounce = 0.0f; + wheelBottomPos = PxVec3(0,0,0); + return false; + } + + //Compute the position on the intersection edge that is closest to the wheel centre. + PxVec3 closestPointOnIntersectionEdge; + { + const PxVec3& p = wheelPoseAtZeroJounce.p; + const PxVec3& w = intersectionEdgeW; + const PxVec3& v = intersectionEdgeV; + const PxF32 t = (p - v).dot(w); + closestPointOnIntersectionEdge = v + w*t; + } + + //Compute the vector that joins the wheel centre to the intersection edge; + PxVec3 dir; + { + const PxF32 wheelCentreD = hitPlane.x*wheelPoseAtZeroJounce.p.x + hitPlane.y*wheelPoseAtZeroJounce.p.y + hitPlane.z*wheelPoseAtZeroJounce.p.z + hitPlane.w; + dir = ((wheelCentreD >= 0) ? closestPointOnIntersectionEdge - wheelPoseAtZeroJounce.p : wheelPoseAtZeroJounce.p - closestPointOnIntersectionEdge); + dir.normalize(); + } + + //Now work out if we accept the hit. + //Compare dir with the suspension direction. + if (rejectFromThresholds) + { + if (suspDir.dot(dir) < gPointRejectAngleThreshold) + { + return false; + } + } + + //Compute the point on the disc diameter that will be the closest to the hit plane or the deepest inside the hit plane. + PxVec3 pos; + { + pos = wheelPoseAtZeroJounce.p + dir*radius; + } + + //If the sweep started inside the hit plane then return false + const PxVec3 startPos = pos - suspDir*(radius + maxCompression); + if(hitPlane.x*startPos.x + hitPlane.y*startPos.y + hitPlane.z*startPos.z + hitPlane.w < 0.0f) + { + return false; + } + + //Now compute the maximum depth of the inside and outside discs against the plane. + PxF32 depth; + { + const PxVec3 latDir = wheelPoseAtZeroJounce.rotate(gRight); + const PxF32 signDot = computeSign(hitPlane.x*latDir.x + hitPlane.y*latDir.y + hitPlane.z*latDir.z); + const PxVec3 deepestPos = pos - latDir*(signDot*0.5f*width); + depth = hitPlane.x*deepestPos.x + hitPlane.y*deepestPos.y + hitPlane.z*deepestPos.z + hitPlane.w; + } + + + //How far along the susp dir do we have to move to place the wheel exactly on the plane. + const PxF32 t = -depth/(hitPlane.x*suspDir.x + hitPlane.y*suspDir.y + hitPlane.z*suspDir.z); + + //+ve means that the spring is compressed + //-ve means that the spring is elongated. + jounce = -t; + + //Compute a point at the bottom of the wheel that is at the centre. + wheelBottomPos = pos + suspDir*t; + + //Finished. + return true; +} + +bool intersectCylinderPlane +(const PxTransform& carChassisTrnsfm, + const PxQuat& wheelLocalPoseRotation, const PxF32 wheelTheta, + const PxVec3& bodySpaceWheelCentreOffset, const PxVec3& bodySpaceSuspTravelDir, const PxF32 width, const PxF32 radius, const PxF32 maxCompression, + const PxVec4& hitPlane, + const bool rejectFromThresholds, + PxF32& jounce, PxVec3& wheelBottomPos) +{ + //Compute the pose of the wheel + PxTransform wheelPostsAtZeroJounce; + PxVec3 suspDir; + computeSuspensionSweep( + carChassisTrnsfm, + wheelLocalPoseRotation, wheelTheta, + bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, 0.0f, 0.0f, + wheelPostsAtZeroJounce, suspDir); + + //Perform the intersection. + return intersectCylinderPlane + (wheelPostsAtZeroJounce, suspDir, + width, radius, maxCompression, + hitPlane, + rejectFromThresholds, + jounce, wheelBottomPos); +} + +//////////////////////////////////////////////////////////////////////////// +//Structures used to process blocks of 4 wheels: process the raycast result, +//compute the suspension and tire force, store a number of report variables +//such as tire slip, hit shape, hit material, friction etc. +//////////////////////////////////////////////////////////////////////////// + +class PxVehicleTireForceCalculator4 +{ +public: + + const void* mShaderData[4]; + PxVehicleComputeTireForce mShader; +private: +}; + +//This data structure is passed to processSuspTireWheels +//and represents the data that is logically constant across all sub-steps of each dt update. +struct ProcessSuspWheelTireConstData +{ + //We are integrating dt over N sub-steps. + //timeFraction is 1/N. + PxF32 timeFraction; + //We are integrating dt over N sub-steps. + //subTimeStep is dt/N. + PxF32 subTimeStep; + PxF32 recipSubTimeStep; + + //Gravitational acceleration vector + PxVec3 gravity; + //Length of gravitational acceleration vector (saves a square root each time we need it) + PxF32 gravityMagnitude; + //Reciprocal length of gravitational acceleration vector (saves a square root and divide each time we need it). + PxF32 recipGravityMagnitude; + + //True for tanks, false for all other vehicle types. + //Used when computing the longitudinal and lateral slips. + bool isTank; + + //Minimum denominator allowed in longitudinal slip computation. + PxF32 minLongSlipDenominator; + + //Pointer to physx actor that represents the vehicle. + const PxRigidDynamic* vehActor; + + //Pointer to table of friction values for each combination of material and tire type. + const PxVehicleDrivableSurfaceToTireFrictionPairs* frictionPairs; +}; + +//This data structure is passed to processSuspTireWheels +//and represents the data that is physically constant across each sub-steps of each dt update. +struct ProcessSuspWheelTireInputData +{ +public: + + //True if the driver intends to pass drive torque to any wheel of the vehicle, + //even if none of the wheels in the block of 4 wheels processed in processSuspTireWheels are given drive torque. + //False if the driver does not intend the vehicle to accelerate. + //If the player intends to accelerate then no wheel will be given a sticky tire constraint. + //This data is actually logically constant. + bool isIntentionToAccelerate; + + //True if a wheel has a non-zero diff torque, false if a wheel has zero diff torque. + //This data is actually logically constant. + const bool* isAccelApplied; + + //True if a wheel has a non-zero brake torque, false if a wheel has zero brake torque. + //This data is actually logically constant. + const bool* isBrakeApplied; + + //Steer angles of each wheel in radians. + //This data is actually logically constant. + const PxF32* steerAngles; + + //True if the wheel is not disabled, false if wheel is disabled. + //This data is actually logically constant. + bool* activeWheelStates; + + //Properties of the rigid body - transform. + //This data is actually logically constant. + PxTransform carChassisTrnsfm; + //Properties of the rigid body - linear velocity. + //This data is actually logically constant. + PxVec3 carChassisLinVel; + //Properties of the rigid body - angular velocity + //This data is actually logically constant. + PxVec3 carChassisAngVel; + + //Properties of the wheel shapes at the last sweep. + const PxQuat* wheelLocalPoseRotations; + const PxF32* wheelThetas; + + //Simulation data for the 4 wheels being processed in processSuspTireWheels + //This data is actually logically constant. + const PxVehicleWheels4SimData* vehWheels4SimData; + + //Dynamics data for the 4 wheels being processed in processSuspTireWheels + //This data is a mixture of logically and physically constant. + //We could update some of the data in vehWheels4DynData in processSuspTireWheels + //but we choose to do it after. By specifying the non-constant data members explicitly + //in ProcessSuspWheelTireOutputData we are able to more easily keep a track of the + //constant and non-constant data members. After processSuspTireWheels is complete + //we explicitly transfer the updated data in ProcessSuspWheelTireOutputData to vehWheels4DynData. + //Examples are low long and lat forward speed timers. + const PxVehicleWheels4DynData* vehWheels4DynData; + + //Shaders to calculate the tire forces. + //This data is actually logically constant. + const PxVehicleTireForceCalculator4* vehWheels4TireForceCalculator; + + //Filter function to filter tire load. + //This data is actually logically constant. + const PxVehicleTireLoadFilterData* vehWheels4TireLoadFilterData; + + //How many of the 4 wheels are real wheels (eg a 6-wheeled car has a + //block of 4 wheels then a 2nd block of 4 wheels with only 2 active wheels) + //This data is actually logically constant. + PxU32 numActiveWheels; +}; + +struct ProcessSuspWheelTireOutputData +{ +public: + + ProcessSuspWheelTireOutputData() + { + PxMemZero(this, sizeof(ProcessSuspWheelTireOutputData)); + for(PxU32 i=0;i<4;i++) + { + isInAir[i]=true; + tireSurfaceTypes[i]=PxU32(PxVehicleDrivableSurfaceType::eSURFACE_TYPE_UNKNOWN); + } + } + + + //////////////////////////////////////////////////////////////////////////////////////////// + //The following data is stored so that it may be later passed to PxVehicleWheelQueryResult + ///////////////////////////////////////////////////////////////////////////////////////////// + + //Raycast start [most recent raycast start coord or (0,0,0) if using a cached raycast] + PxVec3 suspLineStarts[4]; + //Raycast start [most recent raycast direction or (0,0,0) if using a cached raycast] + PxVec3 suspLineDirs[4]; + //Raycast start [most recent raycast length or 0 if using a cached raycast] + PxF32 suspLineLengths[4]; + //False if wheel cannot touch the ground. + bool isInAir[4]; + //Actor hit by most recent raycast, NULL if using a cached raycast. + PxActor* tireContactActors[4]; + //Shape hit by most recent raycast, NULL if using a cached raycast. + PxShape* tireContactShapes[4]; + //Material hit by most recent raycast, NULL if using a cached raycast. + PxMaterial* tireSurfaceMaterials[4]; + //Surface type of material hit by most recent raycast, eSURFACE_TYPE_UNKNOWN if using a cached raycast. + PxU32 tireSurfaceTypes[4]; + //Contact point of raycast against either fresh contact plane from fresh raycast or cached contact plane. + PxVec3 tireContactPoints[4]; + //Contact normal of raycast against either fresh contact plane from fresh raycast or cached contact plane. + PxVec3 tireContactNormals[4]; + //Friction experienced by tire (value from friction table for surface/tire type combos multiplied by friction vs slip graph) + PxF32 frictions[4]; + //Jounce experienced by suspension against fresh or cached contact plane. + PxF32 jounces[4]; + //Suspension force to be applied to rigid body. + PxF32 suspensionSpringForces[4]; + //Longitudinal direction of tire in the ground contact plane. + PxVec3 tireLongitudinalDirs[4]; + //Lateral direction of tire in the ground contact plane. + PxVec3 tireLateralDirs[4]; + //Longitudinal slip. + PxF32 longSlips[4]; + //Lateral slip. + PxF32 latSlips[4]; + + //Forward speed of rigid body along tire longitudinal direction at tire base. + //Used later to blend the integrated wheel rotation angle between rolling speed and computed speed + //when the wheel rotation speeds become unreliable at low forward speeds. + PxF32 forwardSpeeds[4]; + + //Torque to be applied to wheel as 1d rigid body. Taken from the longitudinal tire force. + //(Newton's 3rd law means the longitudinal tire force must have an equal and opposite force). + //(The lateral tire force is assumed to be absorbed by the suspension geometry). + PxF32 tireTorques[4]; + + //Force to be applied to rigid body (accumulated across all 4 wheels/tires/suspensions). + PxVec3 chassisForce; + //Torque to be applied to rigid body (accumulated across all 4 wheels/tires/suspensions). + PxVec3 chassisTorque; + + //Updated time spend at low forward speed. + //Needs copied back to vehWheels4DynData + PxF32 newLowForwardSpeedTimers[4]; + //Updated time spend at low lateral speed. + //Needs copied back to vehWheels4DynData + PxF32 newLowSideSpeedTimers[4]; + + //Constraint data for sticky tire constraints and suspension limit constraints. + //Needs copied back to vehWheels4DynData + PxVehicleConstraintShader::VehicleConstraintData vehConstraintData; + + //Store the details of the raycast hit results so that they may be re-used + //next update in the event that no raycast is performed. + //If no raycast was performed then the cached values are just re-copied here + //so that they can be recycled without having to do further tests on whether + //raycasts were performed or not. + //Needs copied back to vehWheels4DynData after the last call to processSuspTireWheels. + //The union of cached hit data and susp raycast data means we don't want to overwrite the + //raycast data until we don't need it any more. + PxU32 cachedHitCounts[4]; + PxVec4 cachedHitPlanes[4]; + PxF32 cachedHitDistances[4]; + PxF32 cachedFrictionMultipliers[4]; + PxU16 cachedHitQueryTypes[4]; + + //Store the details of the force applied to any dynamic actor hit by wheel raycasts. + PxRigidDynamic* hitActors[4]; + PxVec3 hitActorForces[4]; + PxVec3 hitActorForcePositions[4]; +}; + + + +//////////////////////////////////////////////////////////////////////////// +//Monster function to +//1. compute the tire/susp forces +//2. compute the torque to apply to the 1D rigid body wheel arising from the long tire force +//3. process the sticky tire friction constraints +// (monitor and increment the low long + lat speed timers, compute data for the sticky tire constraint if necessary) +//4. process the suspension limit constraints +// (monitor the suspension jounce versus the suspension travel limit, compute the data for the suspension limit constraint if necessary). +//5. record the contact plane so that it may be re-used in future updates in the absence of fresh raycasts. +//6. record telemetry data (if necessary) and record data for reporting such as hit material, hit normal etc. +//////////////////////////////////////////////////////////////////////////// + + +void storeHit +(const ProcessSuspWheelTireConstData& constData, const ProcessSuspWheelTireInputData& inputData, + const PxU16 hitQueryType, + const PxLocationHit& hit, const PxVec4& hitPlane, + const PxU32 i, + PxU32* hitCounts4, + PxF32* hitDistances4, + PxVec4* hitPlanes4, + PxF32* hitFrictionMultipliers4, + PxU16* hitQueryTypes4, + PxShape** hitContactShapes4, + PxRigidActor** hitContactActors4, + PxMaterial** hitContactMaterials4, + PxU32* hitSurfaceTypes4, + PxVec3* hitContactPoints4, + PxVec3* hitContactNormals4, + PxU32* cachedHitCounts, + PxVec4* cachedHitPlanes, + PxF32* cachedHitDistances, + PxF32* cachedFrictionMultipliers, + PxU16* cachedHitQueryTypes) +{ + //Hit count. + hitCounts4[i] = 1; + + //Hit distance. + hitDistances4[i] = hit.distance; + + //Hit plane. + hitPlanes4[i] = hitPlane; + + //Hit friction. + PxU32 surfaceType = 0; + PxMaterial* material = NULL; + { + //Only get the material if the raycast started outside the hit shape. + material = (hit.distance != 0.0f) ? hit.shape->getMaterialFromInternalFaceIndex(hit.faceIndex) : NULL; + } + //Hash table for quick lookup of drivable surface type from material. + const PxVehicleDrivableSurfaceToTireFrictionPairs* PX_RESTRICT frictionPairs = constData.frictionPairs; + VehicleSurfaceTypeHashTable surfaceTypeHashTable(*constData.frictionPairs); + if (NULL != material) + { + surfaceType = surfaceTypeHashTable.get(material); + } + const PxVehicleTireData& tire = inputData.vehWheels4SimData->getTireData(i); + const PxF32 frictionMultiplier = frictionPairs->getTypePairFriction(surfaceType, tire.mType); + PX_ASSERT(frictionMultiplier >= 0); + hitFrictionMultipliers4[i] = frictionMultiplier; + + //Hit type. + hitQueryTypes4[i] = hitQueryType; + + //Hit report. + hitContactShapes4[i] = hit.shape; + hitContactActors4[i] = hit.actor; + hitContactMaterials4[i] = material; + hitSurfaceTypes4[i] = surfaceType; + hitContactPoints4[i] = hit.position; + hitContactNormals4[i] = hit.normal; + + //When we're finished here we need to copy this back to the vehicle. + cachedHitCounts[i] = 1; + cachedHitPlanes[i] = hitPlane; + cachedHitDistances[i] = hit.distance; + cachedFrictionMultipliers[i] = frictionMultiplier; + cachedHitQueryTypes[i] = hitQueryType; +} + +void processSuspTireWheels +(const PxU32 startWheelIndex, + const ProcessSuspWheelTireConstData& constData, const ProcessSuspWheelTireInputData& inputData, + ProcessSuspWheelTireOutputData& outputData) +{ + PX_SIMD_GUARD; //tzRaw.normalize(); in computeTireDirs threw a denorm exception on osx + +#if PX_DEBUG_VEHICLE_ON + PX_ASSERT(0==(startWheelIndex & 3)); +#endif + +#if PX_DEBUG_VEHICLE_ON + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eJOUNCE); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eSUSPFORCE); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eTIRELOAD); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eNORMALIZED_TIRELOAD); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eNORM_TIRE_LONG_FORCE); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eNORM_TIRE_LAT_FORCE); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eTIRE_LONG_SLIP); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eTIRE_LAT_SLIP); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eTIRE_FRICTION); +#endif + + //Unpack the logically constant data. + const PxVec3& gravity=constData.gravity; + const PxF32 timeFraction=constData.timeFraction; + const PxF32 timeStep=constData.subTimeStep; + const PxF32 recipTimeStep=constData.recipSubTimeStep; + const PxF32 recipGravityMagnitude=constData.recipGravityMagnitude; + const PxF32 gravityMagnitude=constData.gravityMagnitude; + const bool isTank=constData.isTank; + const PxF32 minLongSlipDenominator=constData.minLongSlipDenominator; + + //Unpack the input data (physically constant data). + const PxVehicleWheels4SimData& wheelsSimData=*inputData.vehWheels4SimData; + const PxVehicleWheels4DynData& wheelsDynData=*inputData.vehWheels4DynData; + const PxVehicleTireForceCalculator4& tireForceCalculator=*inputData.vehWheels4TireForceCalculator; + const PxVehicleTireLoadFilterData& tireLoadFilterData=*inputData.vehWheels4TireLoadFilterData; + //More constant data describing the 4 wheels under consideration. + const PxF32* PX_RESTRICT tireRestLoads=wheelsSimData.getTireRestLoadsArray(); + const PxF32* PX_RESTRICT recipTireRestLoads=wheelsSimData.getRecipTireRestLoadsArray(); + //Compute the right direction for later. + const PxTransform& carChassisTrnsfm=inputData.carChassisTrnsfm; + const PxVec3 latDir=inputData.carChassisTrnsfm.rotate(gRight); + //Unpack the linear and angular velocity of the rigid body. + const PxVec3& carChassisLinVel=inputData.carChassisLinVel; + const PxVec3& carChassisAngVel=inputData.carChassisAngVel; + //Wheel local poses + const PxQuat* PX_RESTRICT wheelLocalPoseRotations = inputData.wheelLocalPoseRotations; + const PxF32* PX_RESTRICT wheelThetas = inputData.wheelThetas; + //Inputs (accel, steer, brake). + const bool isIntentionToAccelerate=inputData.isIntentionToAccelerate; + const PxF32* steerAngles=inputData.steerAngles; + const bool* isBrakeApplied=inputData.isBrakeApplied; + const bool* isAccelApplied=inputData.isAccelApplied; + //Disabled/enabled wheel states. + const bool* activeWheelStates=inputData.activeWheelStates; + //Current low forward/side speed timers. Note that the updated timers + //are stored in newLowForwardSpeedTimers and newLowSideSpeedTimers. + const PxF32* PX_RESTRICT lowForwardSpeedTimers=wheelsDynData.mTireLowForwardSpeedTimers; + const PxF32* PX_RESTRICT lowSideSpeedTimers=wheelsDynData.mTireLowSideSpeedTimers; + //Susp jounces and speeds from previous call to processSuspTireWheels. + const PxF32* PX_RESTRICT prevJounces=wheelsDynData.mJounces; + + //Unpack the output data (the data we are going to compute). + //Start with the data stored for reporting to PxVehicleWheelQueryResult. + //PxVec3* suspLineStarts=outputData.suspLineStarts; + //PxVec3* suspLineDirs=outputData.suspLineDirs; + //PxF32* suspLineLengths=outputData.suspLineLengths; + bool* isInAirs=outputData.isInAir; + PxActor** tireContactActors=outputData.tireContactActors; + PxShape** tireContactShapes=outputData.tireContactShapes; + PxMaterial** tireSurfaceMaterials=outputData.tireSurfaceMaterials; + PxU32* tireSurfaceTypes=outputData.tireSurfaceTypes; + PxVec3* tireContactPoints=outputData.tireContactPoints; + PxVec3* tireContactNormals=outputData.tireContactNormals; + PxF32* frictions=outputData.frictions; + PxF32* jounces=outputData.jounces; + PxF32* suspensionSpringForces=outputData.suspensionSpringForces; + PxVec3* tireLongitudinalDirs=outputData.tireLongitudinalDirs; + PxVec3* tireLateralDirs=outputData.tireLateralDirs; + PxF32* longSlips=outputData.longSlips; + PxF32* latSlips=outputData.latSlips; + //Now unpack the forward speeds that are used later to blend the integrated wheel + //rotation angle between rolling speed and computed speed when the wheel rotation + //speeds become unreliable at low forward speeds. + PxF32* forwardSpeeds=outputData.forwardSpeeds; + //Unpack the real outputs of this function (wheel torques to apply to 1d rigid body wheel and forces/torques + //to apply to 3d rigid body chassis). + PxF32* tireTorques=outputData.tireTorques; + PxVec3& chassisForce=outputData.chassisForce; + PxVec3& chassisTorque=outputData.chassisTorque; + //Unpack the low speed timers that will be computed. + PxF32* newLowForwardSpeedTimers=outputData.newLowForwardSpeedTimers; + PxF32* newLowSideSpeedTimers=outputData.newLowSideSpeedTimers; + //Unpack the constraint data for suspensions limit and sticky tire constraints. + //Susp limits. + bool* suspLimitActiveFlags=outputData.vehConstraintData.mSuspLimitData.mActiveFlags; + PxVec3* suspLimitDirs=outputData.vehConstraintData.mSuspLimitData.mDirs; + PxVec3* suspLimitCMOffsets=outputData.vehConstraintData.mSuspLimitData.mCMOffsets; + PxF32* suspLimitErrors=outputData.vehConstraintData.mSuspLimitData.mErrors; + //Longitudinal sticky tires. + bool* stickyTireForwardActiveFlags=outputData.vehConstraintData.mStickyTireForwardData.mActiveFlags; + PxVec3* stickyTireForwardDirs=outputData.vehConstraintData.mStickyTireForwardData.mDirs; + PxVec3* stickyTireForwardCMOffsets=outputData.vehConstraintData.mStickyTireForwardData.mCMOffsets; + PxF32* stickyTireForwardTargetSpeeds=outputData.vehConstraintData.mStickyTireForwardData.mTargetSpeeds; + //Lateral sticky tires. + bool* stickyTireSideActiveFlags=outputData.vehConstraintData.mStickyTireSideData.mActiveFlags; + PxVec3* stickyTireSideDirs=outputData.vehConstraintData.mStickyTireSideData.mDirs; + PxVec3* stickyTireSideCMOffsets=outputData.vehConstraintData.mStickyTireSideData.mCMOffsets; + PxF32* stickyTireSideTargetSpeeds=outputData.vehConstraintData.mStickyTireSideData.mTargetSpeeds; + //Hit data. Store the contact data so it can be reused. + PxU32* cachedHitCounts=outputData.cachedHitCounts; + PxVec4* cachedHitPlanes=outputData.cachedHitPlanes; + PxF32* cachedHitDistances=outputData.cachedHitDistances; + PxF32* cachedFrictionMultipliers=outputData.cachedFrictionMultipliers; + PxU16* cachedHitQueryTypes=outputData.cachedHitQueryTypes; + //Hit actor data. + PxRigidDynamic** hitActors=outputData.hitActors; + PxVec3* hitActorForces=outputData.hitActorForces; + PxVec3* hitActorForcePositions=outputData.hitActorForcePositions; + + //Set the cmass rotation straight away (we might need this, we might not but we don't know that yet so just set it). + outputData.vehConstraintData.mCMassRotation = constData.vehActor->getCMassLocalPose().q; + + //Compute all the hit data (counts, distances, planes, frictions, actors, shapes, materials etc etc). + //If we just did a raycast/sweep then we need to compute all this from the hit reports. + //If we are using cached raycast/sweep results then just copy the cached hit result data. + PxU32 hitCounts4[4]; + PxF32 hitDistances4[4]; + PxVec4 hitPlanes4[4]; + PxF32 hitFrictionMultipliers4[4]; + PxU16 hitQueryTypes4[4]; + PxShape* hitContactShapes4[4]; + PxRigidActor* hitContactActors4[4]; + PxMaterial* hitContactMaterials4[4]; + PxU32 hitSurfaceTypes4[4]; + PxVec3 hitContactPoints4[4]; + PxVec3 hitContactNormals4[4]; + const PxRaycastQueryResult* PX_RESTRICT raycastResults=inputData.vehWheels4DynData->mRaycastResults; + const PxSweepQueryResult* PX_RESTRICT sweepResults=inputData.vehWheels4DynData->mSweepResults; + if(raycastResults || sweepResults) + { + const PxU16 queryType = raycastResults ? 0u : 1u; + + //If we have a blocking hit then always take that. + //If we don't have a blocking hit then search for the "best" hit from all the touches. + for(PxU32 i=0;i(raycastResults[i].block) : static_cast(sweepResults[i].block); + + //Test that the hit actor isn't the vehicle itself. + PX_CHECK_AND_RETURN(constData.vehActor != hit.actor, "Vehicle raycast has hit itself. Please check the filter data to avoid this."); + + //Reject if the sweep started inside the hit shape. + if (hit.distance != 0) + { + //Compute the plane of the hit. + const PxVec3 hitPos = hit.position; + const PxVec3 hitNorm = hit.normal; + const PxF32 hitD = -hitNorm.dot(hitPos); + PxVec4 hitPlane(hitNorm, hitD); + + //Store the hit data in the various arrays. + storeHit(constData, inputData, + queryType, + hit, hitPlane, + i, + hitCounts4, + hitDistances4, + hitPlanes4, + hitFrictionMultipliers4, + hitQueryTypes4, + hitContactShapes4, + hitContactActors4, + hitContactMaterials4, + hitSurfaceTypes4, + hitContactPoints4, + hitContactNormals4, + cachedHitCounts, + cachedHitPlanes, + cachedHitDistances, + cachedFrictionMultipliers, + cachedHitQueryTypes); + + hitCount = 1; + } + } + else if (sweepResults && sweepResults[i].nbTouches) + { + //We need wheel info so that we can analyse the hit and reject/accept it. + //Get what we need now. + const PxVehicleWheelData& wheel = wheelsSimData.getWheelData(i); + const PxVehicleSuspensionData& susp = wheelsSimData.getSuspensionData(i); + const PxVec3& bodySpaceWheelCentreOffset = wheelsSimData.getWheelCentreOffset(i); + const PxVec3& bodySpaceSuspTravelDir = wheelsSimData.getSuspTravelDirection(i); + const PxQuat& wheelLocalPoseRotation = wheelLocalPoseRotations[i]; + const PxF32 wheelTheta = wheelThetas[i]; + const PxF32 width = wheel.mWidth; + const PxF32 radius = wheel.mRadius; + const PxF32 maxBounce = susp.mMaxCompression; + + //Compute the global pose of the wheel at zero jounce. + PxTransform suspPose; + PxVec3 suspDir; + computeSuspensionSweep( + carChassisTrnsfm, + wheelLocalPoseRotation, wheelTheta, + bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, 0.0f, 0.0f, + suspPose, suspDir); + + //Iterate over all touches and cache the deepest hit that we accept. + PxF32 bestTouchDistance = -PX_MAX_F32; + for (PxU32 j = 0; j < sweepResults[i].nbTouches; j++) + { + //Get the next candidate hit. + const PxLocationHit& hit = sweepResults[i].touches[j]; + + //Test that the hit actor isn't the vehicle itself. + PX_CHECK_AND_RETURN(constData.vehActor != hit.actor, "Vehicle raycast has hit itself. Please check the filter data to avoid this."); + + //Reject if the sweep started inside the hit shape. + if (hit.distance != 0.0f) + { + //Compute the plane of the hit. + const PxVec3 hitPos = hit.position; + const PxVec3 hitNorm = hit.normal; + const PxF32 hitD = -hitNorm.dot(hitPos); + PxVec4 hitPlane(hitNorm, hitD); + + //Intersect the wheel disc with the hit plane and compute the jounce required to move the wheel free of the hit plane. + PxF32 dx; + PxVec3 wheelBottomPos; + bool successIntersection = + intersectCylinderPlane + (suspPose, suspDir, + width, radius, maxBounce, + hitPlane, + true, + dx, wheelBottomPos); + + //If we accept the intersection and it requires more jounce than previously encountered then + //store the hit. + if (successIntersection && dx > bestTouchDistance) + { + storeHit(constData, inputData, + queryType, + hit, hitPlane, + i, + hitCounts4, + hitDistances4, + hitPlanes4, + hitFrictionMultipliers4, + hitQueryTypes4, + hitContactShapes4, + hitContactActors4, + hitContactMaterials4, + hitSurfaceTypes4, + hitContactPoints4, + hitContactNormals4, + cachedHitCounts, + cachedHitPlanes, + cachedHitDistances, + cachedFrictionMultipliers, + cachedHitQueryTypes); + + bestTouchDistance = dx; + + hitCount = 1; + } + } + } + } + + if(0 == hitCount) + { + hitCounts4[i]=0; + hitDistances4[i]=0; + hitPlanes4[i]=PxVec4(0,0,0,0); + hitFrictionMultipliers4[i]=0; + hitQueryTypes4[i]=0u; + hitContactShapes4[i]=NULL; + hitContactActors4[i]=NULL; + hitContactMaterials4[i]=NULL; + hitSurfaceTypes4[i]=PxU32(PxVehicleDrivableSurfaceType::eSURFACE_TYPE_UNKNOWN); + hitContactPoints4[i]=PxVec3(0,0,0); + hitContactNormals4[i]=PxVec3(0,0,0); + + //When we're finished here we need to copy this back to the vehicle. + cachedHitCounts[i]=0; + cachedHitPlanes[i]=PxVec4(0,0,0,0); + cachedHitDistances[i]=0; + cachedFrictionMultipliers[i]=0; + cachedHitQueryTypes[i] = 0u; + } + } + } + else + { + //If we have no sq results then we must have a cached raycast hit result. + const PxVehicleWheels4DynData::CachedSuspLineSceneQuerytHitResult& cachedHitResult = + reinterpret_cast(inputData.vehWheels4DynData->mQueryOrCachedHitResults); + + for(PxU32 i=0;i 0 && hitDistances4[i] != 0.0f && hitNorm.dot(w) < 0.0f) + { + //Get the friction multiplier from the combination of surface type and tire type. + const PxF32 frictionMultiplier=hitFrictionMultipliers4[i]; + PX_ASSERT(frictionMultiplier>=0); + + PxF32 dx; + PxVec3 wheelBottomPos; + bool successIntersection = true; + if(0 == hitQueryTypes4[i]) + { + successIntersection = intersectRayPlane + (carChassisTrnsfm, + bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, wheel.mWidth, wheel.mRadius, susp.mMaxCompression, + hitPlanes4[i], + dx, wheelBottomPos); + } + else + { + PX_ASSERT(1 == hitQueryTypes4[i]); + successIntersection = intersectCylinderPlane + (carChassisTrnsfm, + wheelLocalPoseRotations[i], wheelThetas[i], + bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, wheel.mWidth, wheel.mRadius, susp.mMaxCompression, + hitPlanes4[i], + false, + dx, wheelBottomPos); + } + + //If the spring is elongated past its max droop then the wheel isn't touching the ground. + //In this case the spring offers zero force and provides no support for the chassis/sprung mass. + //Only carry on computing the spring force if the wheel is touching the ground. + PX_ASSERT(susp.mMaxCompression>=0); + PX_ASSERT(susp.mMaxDroop>=0); + if(dx > -susp.mMaxDroop && successIntersection) + { + //We can record the hit shape, hit actor, hit material, hit surface type, hit point, and hit normal now because we've got a hit. + tireContactShapes[i]=hitContactShapes4[i]; + tireContactActors[i]=hitContactActors4[i]; + tireSurfaceMaterials[i]=hitContactMaterials4[i]; + tireSurfaceTypes[i]=hitSurfaceTypes4[i]; + tireContactPoints[i]=hitContactPoints4[i]; + tireContactNormals[i]=hitContactNormals4[i]; + + //We know that the vehicle is not in the air. + isInAirs[i]=false; + + //Clamp the spring compression so that it is never greater than the max bounce. + //Apply the susp limit constraint if the spring compression is greater than the max bounce. + suspLimitErrors[i] = (w.dot(hitNorm))*(-dx + susp.mMaxCompression); + suspLimitActiveFlags[i] = (dx > susp.mMaxCompression); + suspLimitCMOffsets[i] = bodySpaceWheelCentreOffset; + suspLimitDirs[i] = bodySpaceSuspTravelDir; + jounce=PxMin(dx,susp.mMaxCompression); + + //Store the jounce (having a local copy avoids lhs). + jounces[i]=jounce; + + //Store the jounce in the graph. +#if PX_DEBUG_VEHICLE_ON + updateGraphDataSuspJounce(startWheelIndex, i,jounce); +#endif + + //Compute the speed of the rigid body along the suspension travel dir at the + //bottom of the wheel. + const PxVec3 r=wheelBottomPos-carChassisTrnsfm.p; + PxVec3 wheelBottomVel=carChassisLinVel; + wheelBottomVel+=carChassisAngVel.cross(r); + + //Modify the relative velocity at the wheel contact point if the hit actor is a dynamic. + PxRigidDynamic* dynamicHitActor=NULL; + PxVec3 hitActorVelocity(0,0,0); + if(hitContactActors4[i] && ((dynamicHitActor = hitContactActors4[i]->is()) != NULL)) + { + hitActorVelocity = PxRigidBodyExt::getVelocityAtPos(*dynamicHitActor,wheelBottomPos); + wheelBottomVel -= hitActorVelocity; + } + + //Get the speed of the jounce. + const PxF32 jounceSpeed = (PX_MAX_F32 != prevJounces[i]) ? (jounce - prevJounces[i])*recipTimeStep : 0.0f; + + //Decompose gravity into a term along w and a term perpendicular to w + //gravity = w*alpha + T*beta + //where T is a unit vector perpendicular to w; alpha and beta are scalars. + //The vector w*alpha*mass is the component of gravitational force that acts along the spring direction. + //The vector T*beta*mass is the component of gravitational force that will be resisted by the spring + //because the spring only supports a single degree of freedom along w. + //We only really need to know T*beta so don't bother calculating T or beta. + const PxF32 alpha = PxMax(0.0f, gravity.dot(w)); + const PxVec3 TTimesBeta = (0.0f != alpha) ? gravity - w*alpha : PxVec3(0,0,0); + //Compute the magnitude of the force along w. + PxF32 suspensionForceW = + PxMax(0.0f, + susp.mSprungMass*alpha + //force to support sprung mass at zero jounce + susp.mSpringStrength*jounce); //linear spring + suspensionForceW += susp.mSpringDamperRate*jounceSpeed; //damping + //Compute the total force acting on the suspension. + //Remember that the spring force acts along -w. + //Remember to account for the term perpendicular to w and that it acts along -TTimesBeta + PxF32 suspensionForceMag = hitNorm.dot(-w*suspensionForceW - TTimesBeta*susp.mSprungMass); + + //Apply the opposite force to the hit object. + //Clamp suspensionForceMag if required. + if (dynamicHitActor && !(dynamicHitActor->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC)) + { + const PxF32 dynamicActorInvMass = dynamicHitActor->getInvMass(); + const PxF32 dynamicActorMass = dynamicHitActor->getMass(); + const PxF32 forceSign = computeSign(suspensionForceMag); + const PxF32 forceMag = PxAbs(suspensionForceMag); + const PxF32 clampedAccelMag = PxMin(forceMag*dynamicActorInvMass, gMaxHitActorAcceleration); + const PxF32 clampedForceMag = clampedAccelMag*dynamicActorMass*forceSign; + PX_ASSERT(clampedForceMag*suspensionForceMag >= 0.0f); + + suspensionForceMag = clampedForceMag; + + hitActors[i] = dynamicHitActor; + hitActorForces[i] = hitNorm*(-clampedForceMag*timeFraction); + hitActorForcePositions[i] = hitContactPoints4[i]; + } + + //Store the spring force now (having a local copy avoids lhs). + suspensionSpringForces[i] = suspensionForceMag; + + //Store the spring force in the graph. +#if PX_DEBUG_VEHICLE_ON + updateGraphDataSuspForce(startWheelIndex, i, suspensionForceMag); +#endif + + //Suspension force can be computed now. + const PxVec3 suspensionForce = hitNorm*suspensionForceMag; + + //Torque from spring force. + const PxVec3 suspForceCMOffset = carChassisTrnsfm.rotate(wheelsSimData.getSuspForceAppPointOffset(i)); + const PxVec3 suspensionTorque = suspForceCMOffset.cross(suspensionForce); + + //Add the suspension force/torque to the chassis force/torque. + chassisForce+=suspensionForce; + chassisTorque+=suspensionTorque; + + //Now compute the tire load. + const PxF32 tireLoad = suspensionForceMag; + + //Normalize the tire load + //Now work out the normalized tire load. + const PxF32 normalisedTireLoad=tireLoad*recipGravityMagnitude*recipTireRestLoads[i]; + //Filter the normalized tire load and compute the filtered tire load too. + const PxF32 filteredNormalisedTireLoad=computeFilteredNormalisedTireLoad(tireLoadFilterData,normalisedTireLoad); + const PxF32 filteredTireLoad=filteredNormalisedTireLoad*gravityMagnitude*tireRestLoads[i]; + +#if PX_DEBUG_VEHICLE_ON + updateGraphDataTireLoad(startWheelIndex,i,filteredTireLoad); + updateGraphDataNormTireLoad(startWheelIndex,i,filteredNormalisedTireLoad); +#endif + + //Compute the lateral and longitudinal tire axes in the ground plane. + PxVec3 tireLongDir; + PxVec3 tireLatDir; + computeTireDirs(latDir,hitNorm,steerAngles[i],tireLongDir,tireLatDir); + + //Store the tire long and lat dirs now (having a local copy avoids lhs). + tireLongitudinalDirs[i]= tireLongDir; + tireLateralDirs[i]=tireLatDir; + + //Now compute the speeds along each of the tire axes. + const PxF32 tireLongSpeed=wheelBottomVel.dot(tireLongDir); + const PxF32 tireLatSpeed=wheelBottomVel.dot(tireLatDir); + + //Store the forward speed (having a local copy avoids lhs). + forwardSpeeds[i]=tireLongSpeed; + + //Now compute the slips along each axes. + const bool hasAccel=isAccelApplied[i]; + const bool hasBrake=isBrakeApplied[i]; + const PxF32 wheelOmega=wheelsDynData.mWheelSpeeds[i]; + const PxF32 wheelRadius=wheel.mRadius; + PxF32 longSlip; + PxF32 latSlip; + computeTireSlips + (tireLongSpeed,tireLatSpeed,wheelOmega,wheelRadius,minLongSlipDenominator, + hasAccel,hasBrake, + isTank, + longSlip,latSlip); + + //Store the lat and long slip (having local copies avoids lhs). + longSlips[i]=longSlip; + latSlips[i]=latSlip; + + //Camber angle. + PxF32 camber=susp.mCamberAtRest; + if(jounce>0) + { + camber += jounce*susp.mCamberAtMaxCompression*susp.getRecipMaxCompression(); + } + else + { + camber -= jounce*susp.mCamberAtMaxDroop*susp.getRecipMaxDroop(); + } + + //Compute the friction that will be experienced by the tire. + PxF32 friction; + computeTireFriction(tire,longSlip,frictionMultiplier,friction); + + //Store the friction (having a local copy avoids lhs). + frictions[i]=friction; + + if(filteredTireLoad*frictionMultiplier>0) + { + //Either tire forces or sticky tire friction constraint will be applied here. + const PxVec3 tireForceCMOffset = carChassisTrnsfm.rotate(wheelsSimData.getTireForceAppPointOffset(i)); + + PxF32 newLowForwardSpeedTimer; + { + //check the accel value here + //Update low forward speed timer. + const PxF32 recipWheelRadius=wheel.getRecipRadius(); + newLowForwardSpeedTimer=newLowForwardSpeedTimers[i]; + updateLowForwardSpeedTimer(tireLongSpeed,wheelOmega,wheelRadius,recipWheelRadius,isIntentionToAccelerate,timeStep,newLowForwardSpeedTimer); + + //Activate sticky tire forward friction constraint if required. + //If sticky tire friction is active then set the longitudinal slip to zero because + //the sticky tire constraint will take care of the longitudinal component of motion. + bool stickyTireForwardActiveFlag=false; + PxF32 stickyTireForwardTargetSpeed=0.0f; + activateStickyFrictionForwardConstraint(tireLongSpeed,wheelOmega,newLowForwardSpeedTimer,isIntentionToAccelerate,stickyTireForwardActiveFlag,stickyTireForwardTargetSpeed); + stickyTireForwardTargetSpeed += hitActorVelocity.dot(tireLongDir); + + //Store the sticky tire data (having local copies avoids lhs). + newLowForwardSpeedTimers[i] = newLowForwardSpeedTimer; + stickyTireForwardActiveFlags[i]=stickyTireForwardActiveFlag; + stickyTireForwardTargetSpeeds[i]=stickyTireForwardTargetSpeed; + stickyTireForwardDirs[i]=tireLongDir; + stickyTireForwardCMOffsets[i]=tireForceCMOffset; + + //Deactivate the long slip if sticky tire constraint is active. + longSlip=(!stickyTireForwardActiveFlag ? longSlip : 0.0f); + + //Store the long slip (having local copies avoids lhs). + longSlips[i]=longSlip; + } + + PxF32 newLowSideSpeedTimer; + { + //check the accel value here + //Update low side speed timer. + newLowSideSpeedTimer=newLowSideSpeedTimers[i]; + updateLowSideSpeedTimer(tireLatSpeed,isIntentionToAccelerate,timeStep,newLowSideSpeedTimer); + + //Activate sticky tire side friction constraint if required. + //If sticky tire friction is active then set the lateral slip to zero because + //the sticky tire constraint will take care of the lateral component of motion. + bool stickyTireSideActiveFlag=false; + PxF32 stickyTireSideTargetSpeed=0.0f; + activateStickyFrictionSideConstraint(tireLatSpeed,newLowForwardSpeedTimer,newLowSideSpeedTimer,isIntentionToAccelerate,stickyTireSideActiveFlag,stickyTireSideTargetSpeed); + stickyTireSideTargetSpeed += hitActorVelocity.dot(tireLatDir); + + //Store the sticky tire data (having local copies avoids lhs). + newLowSideSpeedTimers[i] = newLowSideSpeedTimer; + stickyTireSideActiveFlags[i]=stickyTireSideActiveFlag; + stickyTireSideTargetSpeeds[i]=stickyTireSideTargetSpeed; + stickyTireSideDirs[i]=tireLatDir; + stickyTireSideCMOffsets[i]=tireForceCMOffset; + + //Deactivate the lat slip if sticky tire constraint is active. + latSlip=(!stickyTireSideActiveFlag ? latSlip : 0.0f); + + //Store the long slip (having local copies avoids lhs). + latSlips[i]=latSlip; + } + + //Compute the various tire torques. + PxF32 wheelTorque=0; + PxF32 tireLongForceMag=0; + PxF32 tireLatForceMag=0; + PxF32 tireAlignMoment=0; + const PxF32 restTireLoad=gravityMagnitude*tireRestLoads[i]; + const PxF32 recipWheelRadius=wheel.getRecipRadius(); + tireForceCalculator.mShader( + tireForceCalculator.mShaderData[i], + friction, + longSlip,latSlip,camber, + wheelOmega,wheelRadius,recipWheelRadius, + restTireLoad,filteredNormalisedTireLoad,filteredTireLoad, + gravityMagnitude, recipGravityMagnitude, + wheelTorque,tireLongForceMag,tireLatForceMag,tireAlignMoment); + + //Store the tire torque ((having a local copy avoids lhs). + tireTorques[i]=wheelTorque; + + //Apply the torque to the chassis. + //Compute the tire force to apply to the chassis. + const PxVec3 tireLongForce=tireLongDir*tireLongForceMag; + const PxVec3 tireLatForce=tireLatDir*tireLatForceMag; + const PxVec3 tireForce=tireLongForce+tireLatForce; + //Compute the torque to apply to the chassis. + const PxVec3 tireTorque=tireForceCMOffset.cross(tireForce); + //Add all the forces/torques together. + chassisForce+=tireForce; + chassisTorque+=tireTorque; + + //Graph all the data we just computed. +#if PX_DEBUG_VEHICLE_ON + if(gCarTireForceAppPoints) + gCarTireForceAppPoints[i]=carChassisTrnsfm.p + tireForceCMOffset; + if(gCarSuspForceAppPoints) + gCarSuspForceAppPoints[i]=carChassisTrnsfm.p + suspForceCMOffset; + + if(gCarWheelGraphData[0]) + { + updateGraphDataNormLongTireForce(startWheelIndex, i, PxAbs(tireLongForceMag)*normalisedTireLoad/tireLoad); + updateGraphDataNormLatTireForce(startWheelIndex, i, PxAbs(tireLatForceMag)*normalisedTireLoad/tireLoad); + updateGraphDataNormTireAligningMoment(startWheelIndex, i, tireAlignMoment*normalisedTireLoad/tireLoad); + updateGraphDataLongTireSlip(startWheelIndex, i,longSlips[i]); + updateGraphDataLatTireSlip(startWheelIndex, i,latSlips[i]); + updateGraphDataTireFriction(startWheelIndex, i,frictions[i]); + } +#endif + }//filteredTireLoad*frictionMultiplier>0 + }//if(dx > -susp.mMaxCompression) + }//if(numHits>0) + }//i +} + + +void procesAntiRollSuspension +(const PxVehicleWheelsSimData& wheelsSimData, + const PxTransform& carChassisTransform, const PxWheelQueryResult* wheelQueryResults, + PxVec3& chassisTorque) +{ + const PxU32 numAntiRollBars = wheelsSimData.getNbAntiRollBars(); + for(PxU32 i = 0; i < numAntiRollBars; i++) + { + const PxVehicleAntiRollBarData& antiRoll = wheelsSimData.getAntiRollBarData(i); + const PxU32 w0 = antiRoll.mWheel0; + const PxU32 w1 = antiRoll.mWheel1; + + //At least one wheel must be on the ground for the anti-roll to work. + const bool w0InAir = wheelQueryResults[w0].isInAir; + const bool w1InAir = wheelQueryResults[w1].isInAir; + if(!w0InAir || !w1InAir) + { + //Compute the difference in jounce and compute the force. + const PxF32 w0Jounce = wheelQueryResults[w0].suspJounce; + const PxF32 w1Jounce = wheelQueryResults[w1].suspJounce; + const PxF32 antiRollForceMag = (w0Jounce - w1Jounce)*antiRoll.mStiffness; + + //Apply the antiRollForce postiviely to wheel0, negatively to wheel 1 + PxU32 wheelIds[2] = {0xffffffff, 0xffffffff}; + PxF32 antiRollForceMags[2]; + PxU32 numWheelIds = 0; + if(!w0InAir) + { + wheelIds[numWheelIds] = w0; + antiRollForceMags[numWheelIds] = -antiRollForceMag; + numWheelIds++; + } + if(!w1InAir) + { + wheelIds[numWheelIds] = w1; + antiRollForceMags[numWheelIds] = +antiRollForceMag; + numWheelIds++; + } + + for(PxU32 j = 0; j < numWheelIds; j++) + { + const PxU32 wheelId = wheelIds[j]; + + //Force + const PxVec3 suspDir = carChassisTransform.q.rotate(wheelsSimData.getSuspTravelDirection(wheelId)); + const PxVec3 antiRollForce = suspDir*antiRollForceMags[j]; + //Torque + const PxVec3 r = carChassisTransform.q.rotate(wheelsSimData.getSuspForceAppPointOffset(wheelId)); + const PxVec3 antiRollTorque = r.cross(antiRollForce); + chassisTorque += antiRollTorque; + } + } + } +} + + + +//////////////////////////////////////////////////////////////////////////// +//Set the low long speed timers computed in processSuspTireWheels +//Call immediately after completing processSuspTireWheels. +//////////////////////////////////////////////////////////////////////////// + +void updateLowSpeedTimers(const PxF32* PX_RESTRICT newLowSpeedTimers, PxF32* PX_RESTRICT lowSpeedTimers) +{ + for(PxU32 i=0;i<4;i++) + { + lowSpeedTimers[i]=(newLowSpeedTimers[i]!=lowSpeedTimers[i] ? newLowSpeedTimers[i] : 0.0f); + } +} + +//////////////////////////////////////////////////////////////////////////// +//Set the jounce values computed in processSuspTireWheels +//Call immediately after completing processSuspTireWheels. +//////////////////////////////////////////////////////////////////////////// +void updateJounces(const PxF32* PX_RESTRICT jounces, PxF32* PX_RESTRICT prevJounces) +{ + for(PxU32 i=0;i<4;i++) + { + prevJounces[i] = jounces[i]; + } +} + +/////////////////////////////////////////////////////////////////////////////// +//Set the hit plane, hit distance and hit friction multplier computed in processSuspTireWheels +//Call immediately after completing processSuspTireWheels. +//////////////////////////////////////////////////////////////////////////// + +void updateCachedHitData +(const PxU32* PX_RESTRICT cachedHitCounts, const PxVec4* PX_RESTRICT cachedHitPlanes, const PxF32* PX_RESTRICT cachedHitDistances, const PxF32* PX_RESTRICT cachedFrictionMultipliers, const PxU16* cachedQueryTypes, + PxVehicleWheels4DynData* wheels4DynData) +{ + if(wheels4DynData->mRaycastResults || wheels4DynData->mSweepResults) + { + wheels4DynData->mHasCachedRaycastHitPlane = true; + } + + PxVehicleWheels4DynData::CachedSuspLineSceneQuerytHitResult* cachedRaycastHitResults = + reinterpret_cast(wheels4DynData->mQueryOrCachedHitResults); + + + for(PxU32 i=0;i<4;i++) + { + cachedRaycastHitResults->mCounts[i]=Ps::to16(cachedHitCounts[i]); + cachedRaycastHitResults->mPlanes[i]=cachedHitPlanes[i]; + cachedRaycastHitResults->mDistances[i]=cachedHitDistances[i]; + cachedRaycastHitResults->mFrictionMultipliers[i]=cachedFrictionMultipliers[i]; + cachedRaycastHitResults->mQueryTypes[i] = cachedQueryTypes[i]; + } +} + + + +//////////////////////////////////////////////////////////////////////////// +//Solve the system of engine speed + wheel rotation speeds using an implicit integrator. +//The following functions only compute the speed of wheels connected to the diff. +//Worth going to the length of the implicit integrator because after gear changes +//the difference in speed at the clutch can be hard to integrate. +//Separate functions for 4W, NW and tank because the differential works in slightly +//different ways. With driveNW we end up with (N+1)*(N+1) problem, with drive4W we end up +//with 5*5 and with tanks we end up with just 3*3. Tanks use the method of least squares +//to apply the rule that all left/right wheels have the same speed. +//Remember that the following functions don't integrate wheels not connected to the diff +//so these need integrated separately. +//////////////////////////////////////////////////////////////////////////// + +#if PX_CHECKED +bool isValid(const MatrixNN& A, const VectorN& b, const VectorN& result) +{ + PX_ASSERT(A.getSize()==b.getSize()); + PX_ASSERT(A.getSize()==result.getSize()); + const PxU32 size=A.getSize(); + + //r=A*result-b + VectorN r(size); + for(PxU32 i=0;i 0, false if brakeTorques[i]==0 + const bool* isBrakeApplied; + + //Tire torques to apply to each 1d rigid body wheel. + const PxF32* tireTorques; + + //Sim and dyn data. + PxU32 numWheels4; + PxU32 numActiveWheels; + const PxVehicleWheels4SimData* wheels4SimData; + const PxVehicleDriveSimData* driveSimData; +}; + +struct ImplicitSolverOutput +{ + PxVehicleWheels4DynData* wheelsDynData; + PxVehicleDriveDynData* driveDynData; +}; + +void solveDrive4WInternaDynamicsEnginePlusDrivenWheels +(const ImplicitSolverInput& input, ImplicitSolverOutput* output) +{ + const PxF32 subTimestep = input.subTimeStep; + const PxF32 K = input.K; + const PxF32 G = input.G; + const PxVehicleClutchAccuracyMode::Enum accuracyMode = input.accuracyMode; + const PxU32 maxIterations = input.maxNumIterations; + const PxF32 engineDriveTorque = input.engineDriveTorque; + const PxF32 engineDampingRate = input.engineDampingRate; + const PxF32* PX_RESTRICT diffTorqueRatios = input.diffTorqueRatios; + const PxF32* PX_RESTRICT aveWheelSpeedContributions = input.aveWheelSpeedContributions; + const PxF32* PX_RESTRICT brakeTorques = input.brakeTorques; + const bool* PX_RESTRICT isBrakeApplied = input.isBrakeApplied; + const PxF32* PX_RESTRICT tireTorques = input.tireTorques; + const PxVehicleWheels4SimData& wheels4SimData = *input.wheels4SimData; + const PxVehicleDriveSimData4W& driveSimData = *static_cast(input.driveSimData); + + PxVehicleDriveDynData* driveDynData = output->driveDynData; + PxVehicleWheels4DynData* wheels4DynData = output->wheelsDynData; + + const PxF32 KG=K*G; + const PxF32 KGG=K*G*G; + + MatrixNN A(4+1); + VectorN b(4+1); + VectorN result(4+1); + + const PxVehicleEngineData& engineData=driveSimData.getEngineData(); + + const PxF32* PX_RESTRICT wheelSpeeds=wheels4DynData->mWheelSpeeds; + const PxF32 engineOmega=driveDynData->getEngineRotationSpeed(); + + // + //torque at clutch: + //tc = K*{G*[alpha0*w0 + alpha1*w1 + alpha2*w2 + ..... alpha(N-1)*w(N-1)] - wEng} + //where + //(i) G is the gearing ratio, + //(ii) alphai is the fractional contribution of the ith wheel to the average wheel speed at the clutch (alpha(i) is zero for undriven wheels) + //(iii) wi is the angular speed of the ith wheel + //(iv) K is the clutch strength + //(v) wEng is the angular speed of the engine + + //torque applied to ith wheel is + //ti = G*gammai*tc + bt(i) + tt(i) + //where + //gammai is the fractional proportion of the clutch torque that the differential delivers to the ith wheel + //bt(i) is the brake torque applied to the ith wheel + //tt(i) is the tire torque applied to the ith wheel + + //acceleration applied to ith wheel is + //ai = G*gammai*K*{G*[alpha0*w0 + alpha1*w1 alpha2*w2 + ..... alpha(N-1)*w(N-1)] - wEng}/Ii + (bt(i) + tt(i))/Ii + //wheer Ii is the moi of the ith wheel + + //express ai as + //ai = [wi(t+dt) - wi(t)]/dt + //and rearrange + //wi(t+dt) - wi(t)] = dt*G*gammai*K*{G*[alpha0*w0(t+dt) + alpha1*w1(t+dt) + alpha2*w2(t+dt) + ..... alpha(N-1)*w(N-1)(t+dt)] - wEng(t+dt)}/Ii + dt*(bt(i) + tt(i))/Ii + + //Do the same for tEng (torque applied to engine) + //tEng = -tc + engineDriveTorque + //where engineDriveTorque is the drive torque applied to the engine + //Assuming the engine has unit mass then + //wEng(t+dt) -wEng(t) = -dt*K*{G*[alpha0*w0(t+dt) + alpha1*w1(t+dt) + alpha2*w2(t+dt) + ..... alpha(N-1)*w(N-1(t+dt))] - wEng(t+dt)}/Ieng + dt*engineDriveTorque]/IEng + + //Introduce the vector w=(w0,w1,w2....w(N-1), wEng) + //and re-express as a matrix after collecting all unknowns at (t+dt) and knowns at time t. + //A*w(t+dt)=b(t); + + //Wheels. + { + for(PxU32 i=0;i<4;i++) + { + const PxF32 dt=subTimestep*wheels4SimData.getWheelData(i).getRecipMOI(); + const PxF32 R=diffTorqueRatios[i]; + const PxF32 dtKGGR=dt*KGG*R; + A.set(i,0,dtKGGR*aveWheelSpeedContributions[0]); + A.set(i,1,dtKGGR*aveWheelSpeedContributions[1]); + A.set(i,2,dtKGGR*aveWheelSpeedContributions[2]); + A.set(i,3,dtKGGR*aveWheelSpeedContributions[3]); + A.set(i,i,1.0f+dtKGGR*aveWheelSpeedContributions[i]+dt*wheels4SimData.getWheelData(i).mDampingRate); + A.set(i,4,-dt*KG*R); + b[i] = wheelSpeeds[i] + dt*(brakeTorques[i]+tireTorques[i]); + result[i] = wheelSpeeds[i]; + } + } + + //Engine. + { + const PxF32 dt=subTimestep*driveSimData.getEngineData().getRecipMOI(); + const PxF32 dtKG=dt*K*G; + A.set(4,0,-dtKG*aveWheelSpeedContributions[0]); + A.set(4,1,-dtKG*aveWheelSpeedContributions[1]); + A.set(4,2,-dtKG*aveWheelSpeedContributions[2]); + A.set(4,3,-dtKG*aveWheelSpeedContributions[3]); + A.set(4,4,1.0f + dt*(K+engineDampingRate)); + b[4] = engineOmega + dt*engineDriveTorque; + result[4] = engineOmega; + } + + //Solve Aw=b + if(PxVehicleClutchAccuracyMode::eBEST_POSSIBLE == accuracyMode) + { + MatrixNNLUSolver solver; + solver.decomposeLU(A); + solver.solve(b,result); + PX_WARN_ONCE_IF(!isValid(A,b,result), "Unable to compute new PxVehicleDrive4W internal rotation speeds. Please check vehicle sim data, especially clutch strength; engine moi and damping; wheel moi and damping"); + } + else + { + MatrixNGaussSeidelSolver solver; + solver.solve(maxIterations, gSolverTolerance, A, b, result); + } + + //Check for sanity in the resultant internal rotation speeds. + //If the brakes are on and the wheels have switched direction then lock them at zero. + //A consequence of this quick fix is that locked wheels remain locked until the brake is entirely released. + //This isn't strictly mathematically or physically correct - a more accurate solution would either formulate the + //brake as a lcp problem or repeatedly solve with constraints that locked wheels remain at zero rotation speed. + //The physically correct solution will certainly be more expensive so let's live with the restriction that + //locked wheels remain locked until the brake is released. + //newOmega=result[i], oldOmega=wheelSpeeds[i], if newOmega*oldOmega<=0 and isBrakeApplied then lock wheel. + result[0]=(isBrakeApplied[0] && (wheelSpeeds[0]*result[0]<=0)) ? 0.0f : result[0]; + result[1]=(isBrakeApplied[1] && (wheelSpeeds[1]*result[1]<=0)) ? 0.0f : result[1]; + result[2]=(isBrakeApplied[2] && (wheelSpeeds[2]*result[2]<=0)) ? 0.0f : result[2]; + result[3]=(isBrakeApplied[3] && (wheelSpeeds[3]*result[3]<=0)) ? 0.0f : result[3]; + //Clamp the engine revs. + //Again, this is not physically or mathematically correct but the loss in behaviour will be hard to notice. + //The alternative would be to add constraints to the solver, which would be much more expensive. + result[4]=PxClamp(result[4],0.0f,engineData.mMaxOmega); + + //Copy back to the car's internal rotation speeds. + wheels4DynData->mWheelSpeeds[0]=result[0]; + wheels4DynData->mWheelSpeeds[1]=result[1]; + wheels4DynData->mWheelSpeeds[2]=result[2]; + wheels4DynData->mWheelSpeeds[3]=result[3]; + driveDynData->setEngineRotationSpeed(result[4]); +} + +void solveDriveNWInternalDynamicsEnginePlusDrivenWheels +(const ImplicitSolverInput& input, ImplicitSolverOutput* output) +{ + const PxF32 subTimestep = input.subTimeStep; + //const PxF32 brake = input.brake; + //const PxF32 handbrake = input.handBrake; + const PxF32 K = input.K; + const PxF32 G = input.G; + const PxVehicleClutchAccuracyMode::Enum accuracyMode = input.accuracyMode; + const PxU32 maxIterations = input.maxNumIterations; + const PxF32 engineDriveTorque = input.engineDriveTorque; + const PxF32 engineDampingRate = input.engineDampingRate; + const PxF32* PX_RESTRICT diffTorqueRatios = input.diffTorqueRatios; + const PxF32* PX_RESTRICT aveWheelSpeedContributions = input.aveWheelSpeedContributions; + const PxF32* PX_RESTRICT brakeTorques = input.brakeTorques; + const bool* PX_RESTRICT isBrakeApplied = input.isBrakeApplied; + const PxF32* PX_RESTRICT tireTorques = input.tireTorques; + //const PxU32 numWheels4 = input.numWheels4; + const PxU32 numActiveWheels = input.numActiveWheels; + const PxVehicleWheels4SimData* PX_RESTRICT wheels4SimDatas = input.wheels4SimData; + const PxVehicleDriveSimDataNW& driveSimData = *static_cast(input.driveSimData); + + PxVehicleDriveDynData* driveDynData = output->driveDynData; + PxVehicleWheels4DynData* wheels4DynDatas = output->wheelsDynData; + + const PxF32 KG=K*G; + const PxF32 KGG=K*G*G; + + MatrixNN A(numActiveWheels+1); + VectorN b(numActiveWheels+1); + VectorN result(numActiveWheels+1); + + const PxVehicleEngineData& engineData=driveSimData.getEngineData(); + const PxF32 engineOmega=driveDynData->getEngineRotationSpeed(); + + // + //torque at clutch: + //tc = K*{G*[alpha0*w0 + alpha1*w1 + alpha2*w2 + ..... alpha(N-1)*w(N-1)] - wEng} + //where + //(i) G is the gearing ratio, + //(ii) alphai is the fractional contribution of the ith wheel to the average wheel speed at the clutch (alpha(i) is zero for undriven wheels) + //(iii) wi is the angular speed of the ith wheel + //(iv) K is the clutch strength + //(v) wEng is the angular speed of the engine + + //torque applied to ith wheel is + //ti = G*gammai*tc + bt(i) + tt(i) + //where + //gammai is the fractional proportion of the clutch torque that the differential delivers to the ith wheel + //bt(i) is the brake torque applied to the ith wheel + //tt(i) is the tire torque applied to the ith wheel + + //acceleration applied to ith wheel is + //ai = G*gammai*K*{G*[alpha0*w0 + alpha1*w1 alpha2*w2 + ..... alpha(N-1)*w(N-1)] - wEng}/Ii + (bt(i) + tt(i))/Ii + //wheer Ii is the moi of the ith wheel. + + //express ai as + //ai = [wi(t+dt) - wi(t)]/dt + //and rearrange + //wi(t+dt) - wi(t)] = dt*G*gammai*K*{G*[alpha0*w0(t+dt) + alpha1*w1(t+dt) + alpha2*w2(t+dt) + ..... alpha(N-1)*w(N-1)(t+dt)] - wEng(t+dt)}/Ii + dt*(bt(i) + tt(i))/Ii + + //Do the same for tEng (torque applied to engine) + //tEng = -tc + engineDriveTorque + //where engineDriveTorque is the drive torque applied to the engine + //Assuming the engine has unit mass then + //wEng(t+dt) -wEng(t) = -dt*K*{G*[alpha0*w0(t+dt) + alpha1*w1(t+dt) + alpha2*w2(t+dt) + ..... alpha(N-1)*w(N-1(t+dt))] - wEng(t+dt)}/Ieng + dt*engineDriveTorque/Ieng + + //Introduce the vector w=(w0,w1,w2....w(N-1), wEng) + //and re-express as a matrix after collecting all unknowns at (t+dt) and knowns at time t. + //A*w(t+dt)=b(t); + + //Wheels. + for(PxU32 i=0;i>2].getWheelData(i&3).getRecipMOI(); + const PxF32 R=diffTorqueRatios[i]; + const PxF32 dtKGGR=dt*KGG*R; + + for(PxU32 j=0;j>2].getWheelData(i&3).mDampingRate); + A.set(i,numActiveWheels,-dt*KG*R); + b[i] = wheels4DynDatas[i>>2].mWheelSpeeds[i&3] + dt*(brakeTorques[i]+tireTorques[i]); + result[i] = wheels4DynDatas[i>>2].mWheelSpeeds[i&3]; + } + + //Engine. + { + const PxF32 dt=subTimestep*driveSimData.getEngineData().getRecipMOI(); + const PxF32 dtKG=dt*K*G; + for(PxU32 i=0;i>2].mWheelSpeeds[i&3]*result[i]<=0)) ? 0.0f : result[i]; + } + //Clamp the engine revs. + //Again, this is not physically or mathematically correct but the loss in behaviour will be hard to notice. + result[numActiveWheels]=PxClamp(result[numActiveWheels],0.0f,engineData.mMaxOmega); + + //Copy back to the car's internal rotation speeds. + for(PxU32 i=0;i>2].mWheelSpeeds[i&3]=result[i]; + } + driveDynData->setEngineRotationSpeed(result[numActiveWheels]); +} + + +void solveTankInternaDynamicsEnginePlusDrivenWheels +(const ImplicitSolverInput& input, const bool* PX_RESTRICT activeWheelStates, const PxF32* PX_RESTRICT wheelGearings, ImplicitSolverOutput* output) +{ + PX_SIMD_GUARD; // denormal exception triggered at oldOmega*newOmega on osx + const PxF32 subTimestep = input.subTimeStep; + const PxF32 K = input.K; + const PxF32 G = input.G; + const PxF32 engineDriveTorque = input.engineDriveTorque; + const PxF32 engineDampingRate = input.engineDampingRate; + const PxF32* PX_RESTRICT diffTorqueRatios = input.diffTorqueRatios; + const PxF32* PX_RESTRICT aveWheelSpeedContributions = input.aveWheelSpeedContributions; + const PxF32* PX_RESTRICT brakeTorques = input.brakeTorques; + const bool* PX_RESTRICT isBrakeApplied = input.isBrakeApplied; + const PxF32* PX_RESTRICT tireTorques = input.tireTorques; + const PxU32 numWheels4 = input.numWheels4; + const PxU32 numActiveWheels = input.numActiveWheels; + const PxVehicleWheels4SimData* PX_RESTRICT wheels4SimDatas = input.wheels4SimData; + const PxVehicleDriveSimData& driveSimData = *input.driveSimData; + + PxVehicleWheels4DynData* PX_RESTRICT wheels4DynDatas = output->wheelsDynData; + PxVehicleDriveDynData* driveDynData = output->driveDynData; + + const PxF32 KG=K*G; + const PxF32 KGG=K*G*G; + + //Rearrange data in a single array rather than scattered in blocks of 4. + //This makes it easier later on. + PxF32 recipMOI[PX_MAX_NB_WHEELS]; + PxF32 dampingRates[PX_MAX_NB_WHEELS]; + PxF32 wheelSpeeds[PX_MAX_NB_WHEELS]; + PxF32 wheelRecipRadii[PX_MAX_NB_WHEELS]; + + for(PxU32 i=0;igetEngineRotationSpeed(); + + const PxF32 dt=subTimestep*driveSimData.getEngineData().getRecipMOI(); + const PxF32 dtKG=dt*K*G; + for(PxU32 i=0;isetEngineRotationSpeed(newEngineOmega); +} + +//////////////////////////////////////////////////////////////////////////// +//Integrate wheel rotation speeds of wheels not connected to the differential. +//Obviously, no wheels in a PxVehicleNoDrive are connected to a diff so all require +//direct integration. +//Only the first 4 wheels of a PxVehicleDrive4W are connected to the diff so +//any extra wheels need direct integration. +//All tank wheels are connected to the diff so none need integrated in a separate pass. +//What about undriven wheels in a PxVehicleDriveNW? This vehicle type treats all +//wheels as being connected to the diff but sets the diff contribution to zero for +//all undriven wheels. No wheels from a PxVehicleNW need integrated in a separate pass. +//////////////////////////////////////////////////////////////////////////// + +void integrateNoDriveWheelSpeeds +(const PxF32 subTimestep, + const PxF32* PX_RESTRICT brakeTorques, const bool* PX_RESTRICT isBrakeApplied, const PxF32* driveTorques, const PxF32* PX_RESTRICT tireTorques, const PxF32* PX_RESTRICT dampingRates, + const PxVehicleWheels4SimData& vehSuspWheelTire4SimData, PxVehicleWheels4DynData& vehSuspWheelTire4) +{ + //w(t+dt) = w(t) + (1/inertia)*(brakeTorque + driveTorque + tireTorque)*dt - (1/inertia)*damping*w(t)*dt ) (1) + //Apply implicit trick and rearrange. + //w(t+dt)[1 + (1/inertia)*damping*dt] = w(t) + (1/inertia)*(brakeTorque + driveTorque + tireTorque)*dt (2) + + //Introduce (1/inertia)*dt to avoid duplication in (2) + PxF32 subTimeSteps[4] = + { + subTimestep*vehSuspWheelTire4SimData.getWheelData(0).getRecipMOI(), + subTimestep*vehSuspWheelTire4SimData.getWheelData(1).getRecipMOI(), + subTimestep*vehSuspWheelTire4SimData.getWheelData(2).getRecipMOI(), + subTimestep*vehSuspWheelTire4SimData.getWheelData(3).getRecipMOI() + }; + + //Integrate. + //w += torque*dt/inertia - damping*dt*w + //Use implicit integrate trick and rearrange + //w(t+dt) = [w(t) + torque*dt/inertia]/[1 + damping*dt] + const PxF32* PX_RESTRICT wheelSpeeds=vehSuspWheelTire4.mWheelSpeeds; + PxF32 result[4]= + { + (wheelSpeeds[0] + subTimeSteps[0]*(tireTorques[0] + driveTorques[0] + brakeTorques[0]))/(1.0f + dampingRates[0]*subTimeSteps[0]), + (wheelSpeeds[1] + subTimeSteps[1]*(tireTorques[1] + driveTorques[1] + brakeTorques[1]))/(1.0f + dampingRates[1]*subTimeSteps[1]), + (wheelSpeeds[2] + subTimeSteps[2]*(tireTorques[2] + driveTorques[2] + brakeTorques[2]))/(1.0f + dampingRates[2]*subTimeSteps[2]), + (wheelSpeeds[3] + subTimeSteps[3]*(tireTorques[3] + driveTorques[3] + brakeTorques[3]))/(1.0f + dampingRates[3]*subTimeSteps[3]), + }; + + //Check for sanity in the resultant internal rotation speeds. + //If the brakes are on and the wheels have switched direction then lock them at zero. + //newOmega=result[i], oldOmega=wheelSpeeds[i], if newOmega*oldOmega<=0 and isBrakeApplied then lock wheel. + result[0]=(isBrakeApplied[0] && (wheelSpeeds[0]*result[0]<=0)) ? 0.0f : result[0]; + result[1]=(isBrakeApplied[1] && (wheelSpeeds[1]*result[1]<=0)) ? 0.0f : result[1]; + result[2]=(isBrakeApplied[2] && (wheelSpeeds[2]*result[2]<=0)) ? 0.0f : result[2]; + result[3]=(isBrakeApplied[3] && (wheelSpeeds[3]*result[3]<=0)) ? 0.0f : result[3]; + + //Copy back to the car's internal rotation speeds. + vehSuspWheelTire4.mWheelSpeeds[0]=result[0]; + vehSuspWheelTire4.mWheelSpeeds[1]=result[1]; + vehSuspWheelTire4.mWheelSpeeds[2]=result[2]; + vehSuspWheelTire4.mWheelSpeeds[3]=result[3]; +} + +void integrateUndriveWheelRotationSpeeds +(const PxF32 subTimestep, + const PxF32 brake, const PxF32 handbrake, const PxF32* PX_RESTRICT tireTorques, const PxF32* PX_RESTRICT brakeTorques, + const PxVehicleWheels4SimData& vehSuspWheelTire4SimData, PxVehicleWheels4DynData& vehSuspWheelTire4) +{ + for(PxU32 i=0;i<4;i++) + { + //Compute the new angular speed of the wheel. + const PxF32 oldOmega=vehSuspWheelTire4.mWheelSpeeds[i]; + const PxF32 dtI = subTimestep*vehSuspWheelTire4SimData.getWheelData(i).getRecipMOI(); + const PxF32 gamma = vehSuspWheelTire4SimData.getWheelData(i).mDampingRate; + const PxF32 newOmega=(oldOmega+dtI*(tireTorques[i]+brakeTorques[i]))/(1.0f + gamma*dtI); + + //Has the brake been applied? It's hard to tell from brakeTorques[j] because that + //will be zero if the wheel is locked. Work it out from the brake and handbrake data. + const PxF32 brakeGain=vehSuspWheelTire4SimData.getWheelData(i).mMaxBrakeTorque; + const PxF32 handbrakeGain=vehSuspWheelTire4SimData.getWheelData(i).mMaxHandBrakeTorque; + + //Work out if the wheel should be locked. + const bool brakeApplied=((brake*brakeGain + handbrake*handbrakeGain)!=0.0f); + const bool wheelReversed=(oldOmega*newOmega <=0); + const bool wheelLocked=(brakeApplied && wheelReversed); + + //Lock the wheel or apply its new angular speed. + if(!wheelLocked) + { + vehSuspWheelTire4.mWheelSpeeds[i]=newOmega; + } + else + { + vehSuspWheelTire4.mWheelSpeeds[i]=0.0f; + } + } +} + + +//////////////////////////////////////////////////////////////////////////// +//Pose the wheels. +//First integrate the wheel rotation angles and clamp them to a range (-10*pi, 10*pi) +//PxVehicleNoDrive has a different way of telling if a wheel is driven by a drive torque so has a separate function. +//Use the wheel steer/rotation/camber angle and suspension jounce to compute the local transform of each wheel. +//////////////////////////////////////////////////////////////////////////// + +void integrateWheelRotationAngles +(const PxF32 timestep, + const PxF32 K, const PxF32 G, const PxF32 engineDriveTorque, + const PxF32* PX_RESTRICT jounces, const PxF32* PX_RESTRICT diffTorqueRatios, const PxF32* PX_RESTRICT forwardSpeeds, const bool* isBrakeApplied, + const PxVehicleDriveSimData& vehCoreSimData, const PxVehicleWheels4SimData& vehSuspWheelTire4SimData, + PxVehicleDriveDynData& vehCore, PxVehicleWheels4DynData& vehSuspWheelTire4) +{ + PX_SIMD_GUARD; //denorm exception on newRotAngle=wheelRotationAngles[j]+wheelOmega*timestep; on osx + + PX_UNUSED(vehCore); + PX_UNUSED(vehCoreSimData); + + const PxF32 KG=K*G; + + PxF32* PX_RESTRICT wheelSpeeds=vehSuspWheelTire4.mWheelSpeeds; + PxF32* PX_RESTRICT wheelRotationAngles=vehSuspWheelTire4.mWheelRotationAngles; + PxF32* PX_RESTRICT correctedWheelSpeeds = vehSuspWheelTire4.mCorrectedWheelSpeeds; + + for(PxU32 j=0;j<4;j++) + { + //At low vehicle forward speeds we have some numerical difficulties getting the + //wheel rotation speeds to be correct due to the tire model's difficulties at low vz. + //The solution is to blend between the rolling speed at the wheel and the wheel's actual rotation speed. + //If the wheel is + //(i) in the air or, + //(ii) under braking torque or, + //(iii) driven by the engine through the gears and diff + //then always use the wheel's actual rotation speed. + //Just to be clear, this means we will blend when the wheel + //(i) is on the ground and + //(ii) has no brake applied and + //(iii) has no drive torque applied from the clutch and + //(iv) is at low forward speed + PxF32 wheelOmega=wheelSpeeds[j]; + if(jounces[j] > -vehSuspWheelTire4SimData.getSuspensionData(j).mMaxDroop && //(i) wheel touching ground + false==isBrakeApplied[j] && //(ii) no brake applied + 0.0f==diffTorqueRatios[j]*KG*engineDriveTorque && //(iii) no drive torque applied + PxAbs(forwardSpeeds[j]) -vehSuspWheelTire4SimData.getSuspensionData(j).mMaxDroop && //(i) wheel touching ground + false==isBrakeApplied[j] && //(ii) no brake applied + 0.0f==driveTorques[j] && //(iii) no drive torque applied + PxAbs(forwardSpeeds[j]) 0.0f) + { + camberAngle += jounce*suspData.mCamberAtMaxCompression*suspData.getRecipMaxCompression(); + } + else + { + camberAngle -= jounce*suspData.mCamberAtMaxDroop*suspData.getRecipMaxDroop(); + } + + //Compute the transform of the wheel shapes. + const PxVec3 pos=cmOffset+wheelsSimData.getWheelCentreOffset(i)-wheelsSimData.getSuspTravelDirection(i)*jounce; + const PxQuat quat(wheelQueryResults[i].steerAngle, gUp); + const PxQuat quat2(camberAngle, quat.rotate(forward)); + const PxQuat quat3=quat2*quat; + const PxQuat quat4(rotAngles[i],quat3.rotate(gRight)); + const PxTransform t(pos,quat4*quat3); + localPoses[i] = t; + } +} + +void poseWheels +(const PxVehicleWheels4SimData& wheelsSimData, + const PxTransform* localPoses, + const PxU32 numWheelsToPose, + PxRigidDynamic* vehActor) +{ + PxShape* shapeBuffer[128]; + vehActor->getShapes(shapeBuffer,128,0); + + for(PxU32 i=0;igetShapes(shapeBuffer2,1,PxU32(shapeIndex)); + currShape = shapeBuffer2[0]; + } + PX_ASSERT(currShape); + currShape->setLocalPose(localPoses[i]); + } + } +} + + + +//////////////////////////////////////////////////////////////////////////// +//Update each vehicle type with a special function +//////////////////////////////////////////////////////////////////////////// + +class PxVehicleUpdate +{ +public: + +#if PX_DEBUG_VEHICLE_ON + static void updateSingleVehicleAndStoreTelemetryData( + const PxF32 timestep, const PxVec3& gravity, const PxVehicleDrivableSurfaceToTireFrictionPairs& vehicleDrivableSurfaceToTireFrictionPairs, + PxVehicleWheels* focusVehicle, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleTelemetryData& telemetryData); +#endif + + static void update( + const PxF32 timestep, const PxVec3& gravity, const PxVehicleDrivableSurfaceToTireFrictionPairs& vehicleDrivableSurfaceToTireFrictionPairs, + const PxU32 numVehicles, PxVehicleWheels** vehicles, PxVehicleWheelQueryResult* wheelQueryResults, PxVehicleConcurrentUpdateData* vehicleConcurrentUpdates); + + static void updatePost( + const PxVehicleConcurrentUpdateData* vehicleConcurrentUpdates, const PxU32 numVehicles, PxVehicleWheels** vehicles); + + static void suspensionRaycasts( + PxBatchQuery* batchQuery, + const PxU32 numVehicles, PxVehicleWheels** vehicles, const PxU32 numSceneQueryResults, PxRaycastQueryResult* sceneQueryResults, + const bool* vehiclesToRaycast); + + static void suspensionSweeps( + PxBatchQuery* batchQuery, + const PxU32 numVehicles, PxVehicleWheels** vehicles, + const PxU32 numSceneQueryResults, PxSweepQueryResult* sceneQueryResults, const PxU16 nbHitsPerQuery, + const bool* vehiclesToRaycast, + const PxF32 sweepWidthScale, const PxF32 sweepRadiusScale); + + static void updateDrive4W( + const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleDrive4W* vehDrive4W, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates); + + static void updateDriveNW( + const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleDriveNW* vehDriveNW, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates); + + static void updateTank( + const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleDriveTank* vehDriveTank, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates); + + static void updateNoDrive( + const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleNoDrive* vehDriveTank, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates); + + static PxU32 computeNumberOfSubsteps(const PxVehicleWheelsSimData& wheelsSimData, const PxVec3& linVel, const PxTransform& globalPose, const PxVec3& forward) + { + const PxVec3 z=globalPose.q.rotate(forward); + const PxF32 vz=PxAbs(linVel.dot(z)); + const PxF32 thresholdVz=wheelsSimData.mThresholdLongitudinalSpeed; + const PxU32 lowCount=wheelsSimData.mLowForwardSpeedSubStepCount; + const PxU32 highCount=wheelsSimData.mHighForwardSpeedSubStepCount; + const PxU32 count=(vz> 2; + PxVehicleWheels4DynData* wheels4 = wheels.getWheel4DynData(); + for(PxU32 i = 0; i < nbWheels4; i++) + { + wheels4[i].setInternalDynamicsToZero(); + } + } + + PX_INLINE static void setInternalDynamicsToZero(PxVehicleDriveDynData& drive) + { + drive.setEngineRotationSpeed(0.0f); + } + + PX_INLINE static void setInternalDynamicsToZero(PxVehicleNoDrive* veh) + { + setInternalDynamicsToZero(veh->mWheelsDynData); + } + + PX_INLINE static void setInternalDynamicsToZero(PxVehicleDrive4W* veh) + { + setInternalDynamicsToZero(veh->mWheelsDynData); + setInternalDynamicsToZero(veh->mDriveDynData); + } + + PX_INLINE static void setInternalDynamicsToZero(PxVehicleDriveNW* veh) + { + veh->mDriveDynData.setEngineRotationSpeed(0.0f); + setInternalDynamicsToZero(veh->mWheelsDynData); + setInternalDynamicsToZero(veh->mDriveDynData); + } + + PX_INLINE static void setInternalDynamicsToZero(PxVehicleDriveTank* veh) + { + setInternalDynamicsToZero(veh->mWheelsDynData); + setInternalDynamicsToZero(veh->mDriveDynData); + } + + + PX_INLINE static bool isOnDynamicActor(const PxVehicleWheelsSimData& wheelsSimData, const PxVehicleWheelsDynData& wheelsDynData) + { + const PxU32 numWheels4 = wheelsSimData.mNbWheels4; + const PxVehicleWheels4DynData* PX_RESTRICT wheels4DynDatas = wheelsDynData.mWheels4DynData; + + for(PxU32 i=0;i(raycastResults[j].block) : static_cast(sweepResults[j].block); + if(hitCount && hit.actor && hit.actor->is()) + { + return true; + } + } + } + } + + return false; + } + + PX_INLINE static void storeRaycasts(const PxVehicleWheels4DynData& dynData, PxWheelQueryResult* wheelQueryResults) + { + if(dynData.mRaycastResults) + { + for(PxU32 i=0;i<4;i++) + { + const PxVehicleWheels4DynData::SuspLineRaycast& raycast = + reinterpret_cast(dynData.mQueryOrCachedHitResults); + + wheelQueryResults[i].suspLineStart=raycast.mStarts[i]; + wheelQueryResults[i].suspLineDir=raycast.mDirs[i]; + wheelQueryResults[i].suspLineLength=raycast.mLengths[i]; + } + } + else if(dynData.mSweepResults) + { + for(PxU32 i=0;i<4;i++) + { + const PxVehicleWheels4DynData::SuspLineSweep& sweep = + reinterpret_cast(dynData.mQueryOrCachedHitResults); + + wheelQueryResults[i].suspLineStart=sweep.mStartPose[i].p; + wheelQueryResults[i].suspLineDir=sweep.mDirs[i]; + wheelQueryResults[i].suspLineLength=sweep.mLengths[i]; + } + } + else + { + for(PxU32 i=0;i<4;i++) + { + wheelQueryResults[i].suspLineStart=PxVec3(0,0,0); + wheelQueryResults[i].suspLineDir=PxVec3(0,0,0); + wheelQueryResults[i].suspLineLength=0; + } + } + } + + PX_INLINE static void storeSuspWheelTireResults + (const ProcessSuspWheelTireOutputData& outputData, const PxF32* steerAngles, PxWheelQueryResult* wheelQueryResults, const PxU32 numWheels) + { + for(PxU32 i=0;imDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]>-0.01f && + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]<1.01f, + "Illegal vehicle control value - accel must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE]>-0.01f && + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE]<1.01f, + "Illegal vehicle control value - brake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE]>-0.01f && + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE]<1.01f, + "Illegal vehicle control value - handbrake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT]>-1.01f && + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT]<1.01f, + "Illegal vehicle control value - left steer must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]>-1.01f && + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]<1.01f, + "Illegal vehicle control value - right steer must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + PxAbs(vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]- + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT])<1.01f, + "Illegal vehicle control value - right steer value minus left steer value must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + !(vehDrive4W->getRigidDynamicActor()->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC), + "Attempting to update a drive4W with a kinematic actor - this isn't allowed"); + PX_CHECK_AND_RETURN( + NULL==vehWheelQueryResults || vehWheelQueryResults->nbWheelQueryResults >= vehDrive4W->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + PX_CHECK_AND_RETURN( + NULL==vehConcurrentUpdates || vehConcurrentUpdates->nbConcurrentWheelUpdates >= vehDrive4W->mWheelsSimData.getNbWheels(), + "vehConcurrentUpdates->nbConcurrentWheelUpdates must always be greater than or equal to number of wheels in corresponding vehicle"); + +#if PX_CHECKED + { + //Check that the sense of left/right and forward/rear is true. + const PxVec3 fl=vehDrive4W->mWheelsSimData.mWheels4SimData[0].getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT); + const PxVec3 fr=vehDrive4W->mWheelsSimData.mWheels4SimData[0].getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT); + const PxVec3 rl=vehDrive4W->mWheelsSimData.mWheels4SimData[0].getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT); + const PxVec3 rr=vehDrive4W->mWheelsSimData.mWheels4SimData[0].getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_RIGHT); + const PxVec3 right=gRight; + const PxF32 s0=computeSign((fr-fl).dot(right)); + const PxF32 s1=computeSign((rr-rl).dot(right)); + PX_CHECK_AND_RETURN(0==s0 || 0==s1 || s0==s1, "PxVehicle4W does not obey the rule that the eFRONT_RIGHT/eREAR_RIGHT wheels are to the right of the eFRONT_LEFT/eREAR_LEFT wheels"); + } +#endif + + END_TIMER(TIMER_ADMIN); + START_TIMER(TIMER_GRAPHS); + +#if PX_DEBUG_VEHICLE_ON + for(PxU32 i=0;imWheelsSimData.mNbWheels4;i++) + { + updateGraphDataInternalWheelDynamics(4*i,vehDrive4W->mWheelsDynData.mWheels4DynData[i].mWheelSpeeds); + } + updateGraphDataInternalEngineDynamics(vehDrive4W->mDriveDynData.getEngineRotationSpeed()); +#endif + + END_TIMER(TIMER_GRAPHS); + START_TIMER(TIMER_ADMIN); + + //Unpack the vehicle. + //Unpack the 4W simulation and instanced dynamics components. + const PxVehicleWheels4SimData* wheels4SimDatas=vehDrive4W->mWheelsSimData.mWheels4SimData; + const PxVehicleTireLoadFilterData& tireLoadFilterData=vehDrive4W->mWheelsSimData.mNormalisedLoadFilter; + PxVehicleWheels4DynData* wheels4DynDatas=vehDrive4W->mWheelsDynData.mWheels4DynData; + const PxU32 numWheels4=vehDrive4W->mWheelsSimData.mNbWheels4; + const PxU32 numActiveWheels=vehDrive4W->mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=4-(4*numWheels4 - numActiveWheels); + const PxVehicleDriveSimData4W driveSimData=vehDrive4W->mDriveSimData; + PxVehicleDriveDynData& driveDynData=vehDrive4W->mDriveDynData; + PxRigidDynamic* vehActor=vehDrive4W->mActor; + + //We need to store that data we are going to write to actors so we can do this at the end in one go with fewer write locks. + PxVehicleWheelConcurrentUpdateData wheelConcurrentUpdates[PX_MAX_NB_WHEELS]; + PxVehicleConcurrentUpdateData vehicleConcurrentUpdates; + vehicleConcurrentUpdates.nbConcurrentWheelUpdates = numActiveWheels; + vehicleConcurrentUpdates.concurrentWheelUpdates = wheelConcurrentUpdates; + + //Test if a non-zero drive torque was applied or if a non-zero steer angle was applied. + bool finiteInputApplied=false; + if(0!=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT) || + 0!=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT) || + 0!=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL) || + driveDynData.getGearDown() || driveDynData.getGearUp()) + { + finiteInputApplied=true; + } + + //Awake or sleep. + if(vehActor->isSleeping()) + { + if(finiteInputApplied) + { + //Driving inputs so we need the actor to start moving. + vehicleConcurrentUpdates.wakeup = true; + } + else if(isOnDynamicActor(vehDrive4W->mWheelsSimData, vehDrive4W->mWheelsDynData)) + { + //Driving on dynamic so we need to keep moving. + vehicleConcurrentUpdates.wakeup = true; + } + else + { + //No driving inputs and the actor is asleep. + //Set internal dynamics to zero. + setInternalDynamicsToZero(vehDrive4W); + if(vehConcurrentUpdates) vehConcurrentUpdates->staySleeping = true; + return; + } + } + + //In each block of 4 wheels record how many wheels are active. + PxU32 numActiveWheelsPerBlock4[PX_MAX_NB_SUSPWHEELTIRE4]={0,0,0,0,0}; + numActiveWheelsPerBlock4[0]=PxMin(numActiveWheels,PxU32(4)); + for(PxU32 i=1;imWheelsDynData.mTireForceCalculators->mShaderData[4*i+0]; + tires4ForceCalculators[i].mShaderData[1]=vehDrive4W->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+1]; + tires4ForceCalculators[i].mShaderData[2]=vehDrive4W->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+2]; + tires4ForceCalculators[i].mShaderData[3]=vehDrive4W->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+3]; + tires4ForceCalculators[i].mShader=vehDrive4W->mWheelsDynData.mTireForceCalculators->mShader; + } + + //Mark the constraints as dirty to force them to be updated in the sdk. + for(PxU32 i=0;imarkDirty(); + } + + //We need to store data to pose the wheels at the end. + PxWheelQueryResult wheelQueryResults[PX_MAX_NB_WHEELS]; + + END_TIMER(TIMER_ADMIN); + START_TIMER(TIMER_COMPONENTS_UPDATE); + + //Center of mass local pose. + PxTransform carChassisCMLocalPose; + //Compute the transform of the center of mass. + PxTransform origCarChassisTransform; + PxTransform carChassisTransform; + //Inverse mass and inertia to apply the tire/suspension forces as impulses. + PxF32 inverseChassisMass; + PxVec3 inverseInertia; + //Linear and angular velocity. + PxVec3 carChassisLinVel; + PxVec3 carChassisAngVel; + { + carChassisCMLocalPose = vehActor->getCMassLocalPose(); + carChassisCMLocalPose.q = PxQuat(PxIdentity); + origCarChassisTransform = vehActor->getGlobalPose().transform(carChassisCMLocalPose); + carChassisTransform = origCarChassisTransform; + const PxF32 chassisMass = vehActor->getMass(); + inverseChassisMass = 1.0f/chassisMass; + inverseInertia = vehActor->getMassSpaceInvInertiaTensor(); + carChassisLinVel = vehActor->getLinearVelocity(); + carChassisAngVel = vehActor->getAngularVelocity(); + } + + //Get the local poses of the wheel shapes. + //These are the poses from the last frame and equal to the poses used for the raycast we will process. + PxQuat wheelLocalPoseRotations[PX_MAX_NB_WHEELS]; + PxF32 wheelThetas[PX_MAX_NB_WHEELS]; + { + for (PxU32 i = 0; i < numActiveWheels; i++) + { + const PxI32 shapeId = vehDrive4W->mWheelsSimData.getWheelShapeMapping(i); + if (-1 != shapeId) + { + PxShape* shape = NULL; + vehActor->getShapes(&shape, 1, PxU32(shapeId)); + wheelLocalPoseRotations[i] = shape->getLocalPose().q; + wheelThetas[i] = vehDrive4W->mWheelsDynData.getWheelRotationAngle(i); + } + } + } + + //Update the auto-box and decide whether to change gear up or down. + PxF32 autoboxCompensatedAnalogAccel = driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]; + if(driveDynData.getUseAutoGears()) + { + autoboxCompensatedAnalogAccel = processAutoBox(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL,timestep,driveSimData,driveDynData); + } + + //Process gear-up/gear-down commands. + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + processGears(timestep,gearsData,driveDynData); + } + + //Clutch strength; + PxF32 K; + { + const PxVehicleClutchData& clutchData=driveSimData.getClutchData(); + const PxU32 currentGear=driveDynData.getCurrentGear(); + K=computeClutchStrength(clutchData, currentGear); + } + + //Clutch accuracy + PxVehicleClutchAccuracyMode::Enum clutchAccuracyMode; + PxU32 clutchMaxIterations; + { + const PxVehicleClutchData& clutchData=driveSimData.getClutchData(); + clutchAccuracyMode = clutchData.mAccuracyMode; + clutchMaxIterations = clutchData.mEstimateIterations; + } + + //Gear ratio. + PxF32 G; + PxU32 currentGear; + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + currentGear=driveDynData.getCurrentGear(); + G=computeGearRatio(gearsData,currentGear); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataGearRatio(G); +#endif + } + + //Retrieve control values from vehicle controls. + PxF32 accel,brake,handbrake,steerLeft,steerRight; + PxF32 steer; + bool isIntentionToAccelerate; + { + getVehicle4WControlValues(driveDynData,accel,brake,handbrake,steerLeft,steerRight); + steer=steerRight-steerLeft; + accel=autoboxCompensatedAnalogAccel; + isIntentionToAccelerate = (accel>0.0f && 0.0f==brake && 0.0f==handbrake && PxVehicleGearsData::eNEUTRAL != currentGear); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataControlInputs(accel,brake,handbrake,steerLeft,steerRight); +#endif + } + + //Active wheels (wheels which have not been disabled). + bool activeWheelStates[4]={false,false,false,false}; + { + computeWheelActiveStates(4*0, vehDrive4W->mWheelsSimData.mActiveWheelsBitmapBuffer, activeWheelStates); + } + + //Get the drive wheels (the first 4 wheels are the drive wheels). + const PxVehicleWheels4SimData& wheels4SimData=wheels4SimDatas[0]; + PxVehicleWheels4DynData& wheels4DynData=wheels4DynDatas[0]; + const PxVehicleTireForceCalculator4& tires4ForceCalculator=tires4ForceCalculators[0]; + + //Contribution of each driven wheel to average wheel speed at clutch. + //With 4 driven wheels the average wheel speed at clutch is + //wAve = alpha0*w0 + alpha1*w1 + alpha2*w2 + alpha3*w3. + //This next bit of code computes alpha0,alpha1,alpha2,alpha3. + //For rear wheel drive alpha0=alpha1=0 + //For front wheel drive alpha2=alpha3=0 + PxF32 aveWheelSpeedContributions[4]={0.0f,0.0f,0.0f,0.0f}; + { + const PxVehicleDifferential4WData& diffData=driveSimData.getDiffData(); + computeDiffAveWheelSpeedContributions(diffData,handbrake,aveWheelSpeedContributions); + +#if PX_DEBUG_VEHICLE_ON + updateGraphDataClutchSlip(wheels4DynData.mWheelSpeeds,aveWheelSpeedContributions,driveDynData.getEngineRotationSpeed(),G); +#endif + + PX_CHECK_AND_RETURN( + (activeWheelStates[0] || 0.0f==aveWheelSpeedContributions[0]) && + (activeWheelStates[1] || 0.0f==aveWheelSpeedContributions[1]) && + (activeWheelStates[2] || 0.0f==aveWheelSpeedContributions[2]) && + (activeWheelStates[3] || 0.0f==aveWheelSpeedContributions[3]), + "PxVehicleDifferential4WData must be configured so that no torque is delivered to a disabled wheel"); + } + + //Compute a per-wheel accelerator pedal value. + bool isAccelApplied[4]={false,false,false,false}; + if(isIntentionToAccelerate) + { + PX_ASSERT(accel>0); + computeIsAccelApplied(aveWheelSpeedContributions, isAccelApplied); + } + + //Ackermann-corrected steering angles. + //http://en.wikipedia.org/wiki/Ackermann_steering_geometry + PxF32 steerAngles[4]={0.0f,0.0f,0.0f,0.0f}; + { + computeAckermannCorrectedSteerAngles(driveSimData,wheels4SimData,steer,steerAngles); + } + + END_TIMER(TIMER_COMPONENTS_UPDATE); + START_TIMER(TIMER_ADMIN); + + //Store the susp line raycast data. + for(PxU32 i=0;imWheelsSimData,carChassisLinVel,carChassisTransform,gForward); + const PxF32 timeFraction=1.0f/(1.0f*numSubSteps); + const PxF32 subTimestep=timestep*timeFraction; + const PxF32 recipSubTimeStep=1.0f/subTimestep; + const PxF32 recipTimestep=1.0f/timestep; + const PxF32 minLongSlipDenominator=vehDrive4W->mWheelsSimData.mMinLongSlipDenominator; + ProcessSuspWheelTireConstData constData={timeFraction, subTimestep, recipSubTimeStep, gravity, gravityMagnitude, recipGravityMagnitude, false, minLongSlipDenominator, vehActor, &drivableSurfaceToTireFrictionPairs}; + + END_TIMER(TIMER_ADMIN); + + for(PxU32 k=0;k(inputData.vehWheels4DynData->mTireLowForwardSpeedTimers)); + updateLowSpeedTimers(outputData.newLowSideSpeedTimers, const_cast(inputData.vehWheels4DynData->mTireLowSideSpeedTimers)); + updateJounces(outputData.jounces, const_cast(inputData.vehWheels4DynData->mJounces)); + if((numSubSteps-1) == k) + { + updateCachedHitData(outputData.cachedHitCounts, outputData.cachedHitPlanes, outputData.cachedHitDistances, outputData.cachedFrictionMultipliers, outputData.cachedHitQueryTypes, &wheels4DynData); + } + chassisForce+=outputData.chassisForce; + chassisTorque+=outputData.chassisTorque; + if(0 == k) + { + wheels4DynData.mVehicleConstraints->mData=outputData.vehConstraintData; + } + storeSuspWheelTireResults(outputData, inputData.steerAngles, &wheelQueryResults[4*0], numActiveWheelsPerBlock4[0]); + storeHitActorForces(outputData, &vehicleConcurrentUpdates.concurrentWheelUpdates[4*0], numActiveWheelsPerBlock4[0]); + + END_TIMER(TIMER_WHEELS); + START_TIMER(TIMER_INTERNAL_DYNAMICS_SOLVER); + + //Diff torque ratios needed (how we split the torque between the drive wheels). + //The sum of the torque ratios is always 1.0f. + //The drive torque delivered to each wheel is the total available drive torque multiplied by the + //diff torque ratio for each wheel. + PxF32 diffTorqueRatios[4]={0.0f,0.0f,0.0f,0.0f}; + computeDiffTorqueRatios(driveSimData.getDiffData(),handbrake,wheels4DynData.mWheelSpeeds,diffTorqueRatios); + + PX_CHECK_AND_RETURN( + (activeWheelStates[0] || 0.0f==diffTorqueRatios[0]) && + (activeWheelStates[1] || 0.0f==diffTorqueRatios[1]) && + (activeWheelStates[2] || 0.0f==diffTorqueRatios[2]) && + (activeWheelStates[3] || 0.0f==diffTorqueRatios[3]), + "PxVehicleDifferential4WData must be configured so that no torque is delivered to a disabled wheel"); + + PxF32 engineDriveTorque; + { + const PxVehicleEngineData& engineData=driveSimData.getEngineData(); + const PxF32 engineOmega=driveDynData.getEngineRotationSpeed(); + engineDriveTorque=computeEngineDriveTorque(engineData,engineOmega,accel); + #if PX_DEBUG_VEHICLE_ON + updateGraphDataEngineDriveTorque(engineDriveTorque); + #endif + } + + PxF32 engineDampingRate; + { + const PxVehicleEngineData& engineData=driveSimData.getEngineData(); + engineDampingRate=computeEngineDampingRate(engineData,currentGear,accel); + } + + //Update the wheel and engine speeds - 5x5 matrix coupling engine and wheels. + ImplicitSolverInput implicitSolverInput= + { + subTimestep, + brake, handbrake, + K, G, + clutchAccuracyMode, clutchMaxIterations, + engineDriveTorque, engineDampingRate, + diffTorqueRatios, aveWheelSpeedContributions, + brakeTorques, isBrakeApplied, outputData.tireTorques, + 1, 4, + &wheels4SimData, &driveSimData + }; + ImplicitSolverOutput implicitSolverOutput= + { + &wheels4DynData, &driveDynData + }; + solveDrive4WInternaDynamicsEnginePlusDrivenWheels(implicitSolverInput, &implicitSolverOutput); + + END_TIMER(TIMER_INTERNAL_DYNAMICS_SOLVER); + START_TIMER(TIMER_POSTUPDATE1); + + //Integrate wheel rotation angle (theta += omega*dt) + integrateWheelRotationAngles + (subTimestep, + K,G,engineDriveTorque, + outputData.jounces,diffTorqueRatios,outputData.forwardSpeeds,isBrakeApplied, + driveSimData,wheels4SimData, + driveDynData,wheels4DynData); + } + + END_TIMER(TIMER_POSTUPDATE1); + + ////////////////////////////////////////////////////////////////////////// + //susp and tire forces from extra wheels (non-driven wheels) + ////////////////////////////////////////////////////////////////////////// + for(PxU32 j=1;jmWheelsSimData.mActiveWheelsBitmapBuffer, extraWheelActiveStates); + + ProcessSuspWheelTireInputData extraInputData= + { + isIntentionToAccelerate, extraIsAccelApplied, extraIsBrakeApplied, extraWheelSteerAngles, extraWheelActiveStates, + carChassisTransform, carChassisLinVel, carChassisAngVel, + &wheelLocalPoseRotations[j], &wheelThetas[j], &wheels4SimDatas[j], &wheels4DynDatas[j], &tires4ForceCalculators[j], &tireLoadFilterData, numActiveWheelsPerBlock4[j], + }; + ProcessSuspWheelTireOutputData extraOutputData; + processSuspTireWheels(4*j, constData, extraInputData, extraOutputData); + updateLowSpeedTimers(extraOutputData.newLowForwardSpeedTimers, const_cast(extraInputData.vehWheels4DynData->mTireLowForwardSpeedTimers)); + updateLowSpeedTimers(extraOutputData.newLowSideSpeedTimers, const_cast(extraInputData.vehWheels4DynData->mTireLowSideSpeedTimers)); + updateJounces(extraOutputData.jounces, const_cast(extraInputData.vehWheels4DynData->mJounces)); + if((numSubSteps-1) == k) + { + updateCachedHitData(extraOutputData.cachedHitCounts, extraOutputData.cachedHitPlanes, extraOutputData.cachedHitDistances, extraOutputData.cachedFrictionMultipliers, extraOutputData.cachedHitQueryTypes, &wheels4DynDatas[j]); + } + chassisForce+=extraOutputData.chassisForce; + chassisTorque+=extraOutputData.chassisTorque; + if(0 == k) + { + wheels4DynDatas[j].mVehicleConstraints->mData=extraOutputData.vehConstraintData; + } + storeSuspWheelTireResults(extraOutputData, extraInputData.steerAngles, &wheelQueryResults[4*j], numActiveWheelsPerBlock4[j]); + storeHitActorForces(extraOutputData, &vehicleConcurrentUpdates.concurrentWheelUpdates[4*j], numActiveWheelsPerBlock4[j]); + + //Integrate the tire torques (omega += (tireTorque + brakeTorque)*dt) + integrateUndriveWheelRotationSpeeds(subTimestep, brake, handbrake, extraOutputData.tireTorques, extraWheelBrakeTorques, wheels4SimDatas[j], wheels4DynDatas[j]); + + //Integrate wheel rotation angle (theta += omega*dt) + integrateWheelRotationAngles + (subTimestep, + 0,0,0, + extraOutputData.jounces,extraWheelsDiffTorqueRatios,extraOutputData.forwardSpeeds,extraIsBrakeApplied, + driveSimData,wheels4SimDatas[j], + driveDynData,wheels4DynDatas[j]); + } + + START_TIMER(TIMER_POSTUPDATE2); + + //Apply the anti-roll suspension. + procesAntiRollSuspension(vehDrive4W->mWheelsSimData, carChassisTransform, wheelQueryResults, chassisTorque); + + //Integrate one sustep. + integrateBody(inverseChassisMass, inverseInertia ,chassisForce, chassisTorque, subTimestep, carChassisLinVel, carChassisAngVel, carChassisTransform); + + END_TIMER(TIMER_POSTUPDATE2); + } + + START_TIMER(TIMER_POSTUPDATE3); + + //Set the new chassis linear/angular velocity. + if(!gApplyForces) + { + vehicleConcurrentUpdates.linearMomentumChange = carChassisLinVel; + vehicleConcurrentUpdates.angularMomentumChange = carChassisAngVel; + } + else + { + //integration steps are: + //v = v0 + a*dt (1) + //x = x0 + v*dt (2) + //Sub (2) into (1. + //x = x0 + v0*dt + a*dt*dt; + //Rearrange for a + //a = (x -x0 - v0*dt)/(dt*dt) = [(x-x0)/dt - v0/dt] + //Rearrange again with v = (x-x0)/dt + //a = (v - v0)/dt + vehicleConcurrentUpdates.linearMomentumChange = (carChassisLinVel-carChassisLinVelOrig)*recipTimestep;; + vehicleConcurrentUpdates.angularMomentumChange = (carChassisAngVel-carChassisAngVelOrig)*recipTimestep;; + } + + //Compute and pose the wheels from jounces, rotations angles, and steer angles. + PxTransform localPoses0[4] = {PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity)}; + computeWheelLocalPoses(wheels4SimDatas[0],wheels4DynDatas[0],&wheelQueryResults[4*0],numActiveWheelsPerBlock4[0],carChassisCMLocalPose,localPoses0); + //Copy the poses to the wheelQueryResults + wheelQueryResults[4*0 + 0].localPose = localPoses0[0]; + wheelQueryResults[4*0 + 1].localPose = localPoses0[1]; + wheelQueryResults[4*0 + 2].localPose = localPoses0[2]; + wheelQueryResults[4*0 + 3].localPose = localPoses0[3]; + //Copy the poses to the concurrent update data. + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 0].localPose = localPoses0[0]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 1].localPose = localPoses0[1]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 2].localPose = localPoses0[2]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 3].localPose = localPoses0[3]; + for(PxU32 i=1;iwheelQueryResults) + { + PxMemCopy(vehWheelQueryResults->wheelQueryResults, wheelQueryResults, sizeof(PxWheelQueryResult)*numActiveWheels); + } + + if(vehConcurrentUpdates) + { + //Copy across to input data structure so that writes can be applied later. + PxMemCopy(vehConcurrentUpdates->concurrentWheelUpdates, vehicleConcurrentUpdates.concurrentWheelUpdates, sizeof(PxVehicleWheelConcurrentUpdateData)*numActiveWheels); + vehConcurrentUpdates->linearMomentumChange = vehicleConcurrentUpdates.linearMomentumChange; + vehConcurrentUpdates->angularMomentumChange = vehicleConcurrentUpdates.angularMomentumChange; + vehConcurrentUpdates->staySleeping = vehicleConcurrentUpdates.staySleeping; + vehConcurrentUpdates->wakeup = vehicleConcurrentUpdates.wakeup; + } + else + { + //Apply the writes immediately. + PxVehicleWheels* vehWheels[1]={vehDrive4W}; + PxVehiclePostUpdates(&vehicleConcurrentUpdates, 1, vehWheels); + } + + END_TIMER(TIMER_POSTUPDATE3); +} + +void PxVehicleUpdate::updateDriveNW +(const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleDriveNW* vehDriveNW, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates) +{ + PX_SIMD_GUARD; // denorm exception triggered in transformInertiaTensor() on osx + + START_TIMER(TIMER_ADMIN); + + PX_CHECK_AND_RETURN( + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_ACCEL]>-0.01f && + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_ACCEL]<1.01f, + "Illegal vehicle control value - accel must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_BRAKE]>-0.01f && + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_BRAKE]<1.01f, + "Illegal vehicle control value - brake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_HANDBRAKE]>-0.01f && + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_HANDBRAKE]<1.01f, + "Illegal vehicle control value - handbrake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_LEFT]>-1.01f && + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_LEFT]<1.01f, + "Illegal vehicle control value - left steer must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_RIGHT]>-1.01f && + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_RIGHT]<1.01f, + "Illegal vehicle control value - right steer must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + PxAbs(vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_RIGHT]- + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_LEFT])<1.01f, + "Illegal vehicle control value - right steer value minus left steer value must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + !(vehDriveNW->getRigidDynamicActor()->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC), + "Attempting to update a drive4W with a kinematic actor - this isn't allowed"); + PX_CHECK_AND_RETURN( + NULL==vehWheelQueryResults || vehWheelQueryResults->nbWheelQueryResults >= vehDriveNW->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + +#if PX_CHECKED + for(PxU32 i=0;imWheelsSimData.getNbWheels();i++) + { + PX_CHECK_AND_RETURN(!vehDriveNW->mWheelsSimData.getIsWheelDisabled(i) || !vehDriveNW->mDriveSimData.getDiffData().getIsDrivenWheel(i), + "PxVehicleDifferentialNWData must be configured so that no torque is delivered to a disabled wheel"); + } +#endif + + END_TIMER(TIMER_ADMIN); + START_TIMER(TIMER_GRAPHS); + +#if PX_DEBUG_VEHICLE_ON + for(PxU32 i=0;imWheelsSimData.mNbWheels4;i++) + { + updateGraphDataInternalWheelDynamics(4*i,vehDriveNW->mWheelsDynData.mWheels4DynData[i].mWheelSpeeds); + } + updateGraphDataInternalEngineDynamics(vehDriveNW->mDriveDynData.getEngineRotationSpeed()); +#endif + + END_TIMER(TIMER_GRAPHS); + START_TIMER(TIMER_ADMIN); + + //Unpack the vehicle. + //Unpack the NW simulation and instanced dynamics components. + const PxVehicleWheels4SimData* wheels4SimDatas=vehDriveNW->mWheelsSimData.mWheels4SimData; + const PxVehicleTireLoadFilterData& tireLoadFilterData=vehDriveNW->mWheelsSimData.mNormalisedLoadFilter; + PxVehicleWheels4DynData* wheels4DynDatas=vehDriveNW->mWheelsDynData.mWheels4DynData; + const PxU32 numWheels4=vehDriveNW->mWheelsSimData.mNbWheels4; + const PxU32 numActiveWheels=vehDriveNW->mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=4-(4*numWheels4 - numActiveWheels); + const PxVehicleDriveSimDataNW driveSimData=vehDriveNW->mDriveSimData; + PxVehicleDriveDynData& driveDynData=vehDriveNW->mDriveDynData; + PxRigidDynamic* vehActor=vehDriveNW->mActor; + + //We need to store that data we are going to write to actors so we can do this at the end in one go with fewer write locks. + PxVehicleWheelConcurrentUpdateData wheelConcurrentUpdates[PX_MAX_NB_WHEELS]; + PxVehicleConcurrentUpdateData vehicleConcurrentUpdates; + vehicleConcurrentUpdates.nbConcurrentWheelUpdates = numActiveWheels; + vehicleConcurrentUpdates.concurrentWheelUpdates = wheelConcurrentUpdates; + + //In each block of 4 wheels record how many wheels are active. + PxU32 numActiveWheelsPerBlock4[PX_MAX_NB_SUSPWHEELTIRE4]={0,0,0,0,0}; + numActiveWheelsPerBlock4[0]=PxMin(numActiveWheels,PxU32(4)); + for(PxU32 i=1;iisSleeping()) + { + if(finiteInputApplied) + { + //Driving inputs so we need the actor to start moving. + vehicleConcurrentUpdates.wakeup = true; + } + else if(isOnDynamicActor(vehDriveNW->mWheelsSimData, vehDriveNW->mWheelsDynData)) + { + //Driving on dynamic so we need to keep moving. + vehicleConcurrentUpdates.wakeup = true; + } + else + { + //No driving inputs and the actor is asleep. + //Set internal dynamics to sleep. + setInternalDynamicsToZero(vehDriveNW); + if(vehConcurrentUpdates) vehConcurrentUpdates->staySleeping = true; + return; + } + } + } + + //Organise the shader data in blocks of 4. + PxVehicleTireForceCalculator4 tires4ForceCalculators[PX_MAX_NB_SUSPWHEELTIRE4]; + for(PxU32 i=0;imWheelsDynData.mTireForceCalculators->mShaderData[4*i+0]; + tires4ForceCalculators[i].mShaderData[1]=vehDriveNW->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+1]; + tires4ForceCalculators[i].mShaderData[2]=vehDriveNW->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+2]; + tires4ForceCalculators[i].mShaderData[3]=vehDriveNW->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+3]; + tires4ForceCalculators[i].mShader=vehDriveNW->mWheelsDynData.mTireForceCalculators->mShader; + } + + //Mark the constraints as dirty to force them to be updated in the sdk. + for(PxU32 i=0;imarkDirty(); + } + + //Need to store report data to pose the wheels. + PxWheelQueryResult wheelQueryResults[PX_MAX_NB_WHEELS]; + + END_TIMER(TIMER_ADMIN); + START_TIMER(TIMER_COMPONENTS_UPDATE); + + //Center of mass local pose. + PxTransform carChassisCMLocalPose; + //Compute the transform of the center of mass. + PxTransform origCarChassisTransform; + PxTransform carChassisTransform; + //Inverse mass and inertia to apply the tire/suspension forces as impulses. + PxF32 inverseChassisMass; + PxVec3 inverseInertia; + //Linear and angular velocity. + PxVec3 carChassisLinVel; + PxVec3 carChassisAngVel; + { + carChassisCMLocalPose = vehActor->getCMassLocalPose(); + carChassisCMLocalPose.q = PxQuat(PxIdentity); + origCarChassisTransform = vehActor->getGlobalPose().transform(carChassisCMLocalPose); + carChassisTransform = origCarChassisTransform; + const PxF32 chassisMass = vehActor->getMass(); + inverseChassisMass = 1.0f/chassisMass; + inverseInertia = vehActor->getMassSpaceInvInertiaTensor(); + carChassisLinVel = vehActor->getLinearVelocity(); + carChassisAngVel = vehActor->getAngularVelocity(); + } + + //Get the local poses of the wheel shapes. + //These are the poses from the last frame and equal to the poses used for the raycast we will process. + PxQuat wheelLocalPoseRotations[PX_MAX_NB_WHEELS]; + PxF32 wheelThetas[PX_MAX_NB_WHEELS]; + { + for (PxU32 i = 0; i < numActiveWheels; i++) + { + const PxI32 shapeId = vehDriveNW->mWheelsSimData.getWheelShapeMapping(i); + if (-1 != shapeId) + { + PxShape* shape = NULL; + vehActor->getShapes(&shape, 1, PxU32(shapeId)); + wheelLocalPoseRotations[i] = shape->getLocalPose().q; + wheelThetas[i] = vehDriveNW->mWheelsDynData.getWheelRotationAngle(i); + } + } + } + + //Update the auto-box and decide whether to change gear up or down. + PxF32 autoboxCompensatedAnalogAccel = driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_ACCEL]; + if(driveDynData.getUseAutoGears()) + { + autoboxCompensatedAnalogAccel = processAutoBox(PxVehicleDriveNWControl::eANALOG_INPUT_ACCEL,timestep,driveSimData,driveDynData); + } + + //Process gear-up/gear-down commands. + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + processGears(timestep,gearsData,driveDynData); + } + + //Clutch strength. + PxF32 K; + { + const PxVehicleClutchData& clutchData=driveSimData.getClutchData(); + const PxU32 currentGear=driveDynData.getCurrentGear(); + K=computeClutchStrength(clutchData, currentGear); + } + + //Clutch accuracy. + PxVehicleClutchAccuracyMode::Enum clutchAccuracyMode; + PxU32 clutchMaxIterations; + { + const PxVehicleClutchData& clutchData=driveSimData.getClutchData(); + clutchAccuracyMode=clutchData.mAccuracyMode; + clutchMaxIterations=clutchData.mEstimateIterations; + } + + //Gear ratio. + PxF32 G; + PxU32 currentGear; + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + currentGear=driveDynData.getCurrentGear(); + G=computeGearRatio(gearsData,currentGear); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataGearRatio(G); +#endif + } + + //Retrieve control values from vehicle controls. + PxF32 accel,brake,handbrake,steerLeft,steerRight; + PxF32 steer; + bool isIntentionToAccelerate; + { + getVehicleNWControlValues(driveDynData,accel,brake,handbrake,steerLeft,steerRight); + steer=steerRight-steerLeft; + accel=autoboxCompensatedAnalogAccel; + isIntentionToAccelerate = (accel>0.0f && 0.0f==brake && 0.0f==handbrake && PxVehicleGearsData::eNEUTRAL != currentGear); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataControlInputs(accel,brake,handbrake,steerLeft,steerRight); +#endif + } + + //Compute the wheels that are disabled or enabled. + bool activeWheelStates[PX_MAX_NB_WHEELS]; + PxMemSet(activeWheelStates, 0, sizeof(bool)*PX_MAX_NB_WHEELS); + for(PxU32 i=0;imWheelsSimData.mActiveWheelsBitmapBuffer, &activeWheelStates[4*i]); + } + + //Contribution of each driven wheel to average wheel speed at clutch. + //For NW drive equal torque split is supported. + const PxVehicleDifferentialNWData diffData=driveSimData.getDiffData(); + const PxF32 invNumDrivenWheels=diffData.mInvNbDrivenWheels; + PxF32 aveWheelSpeedContributions[PX_MAX_NB_WHEELS]; + PxMemSet(aveWheelSpeedContributions, 0, sizeof(PxF32)*PX_MAX_NB_WHEELS); + for(PxU32 i=0;i0); + for(PxU32 i=0;imWheelsSimData.getWheelData(i); + const PxF32 steerGain=wheelData.mMaxSteer; + const PxF32 toe=wheelData.mToeAngle; + steerAngles[i]=steerGain*steer + toe; + } + + //Diff torque ratios needed (how we split the torque between the drive wheels). + //The sum of the torque ratios is always 1.0f. + //The drive torque delivered to each wheel is the total available drive torque multiplied by the + //diff torque ratio for each wheel. + PxF32 diffTorqueRatios[PX_MAX_NB_WHEELS]; + PxMemSet(diffTorqueRatios, 0, sizeof(PxF32)*PX_MAX_NB_WHEELS); + for(PxU32 i=0;imWheelsSimData,carChassisLinVel,carChassisTransform,gForward); + const PxF32 timeFraction=1.0f/(1.0f*numSubSteps); + const PxF32 subTimestep=timestep*timeFraction; + const PxF32 recipSubTimeStep=1.0f/subTimestep; + const PxF32 recipTimestep=1.0f/timestep; + const PxF32 minLongSlipDenominator=vehDriveNW->mWheelsSimData.mMinLongSlipDenominator; + ProcessSuspWheelTireConstData constData={timeFraction, subTimestep, recipSubTimeStep, gravity, gravityMagnitude, recipGravityMagnitude, false, minLongSlipDenominator, vehActor, &drivableSurfaceToTireFrictionPairs}; + + END_TIMER(TIMER_ADMIN); + + for(PxU32 k=0;k(inputData.vehWheels4DynData->mTireLowForwardSpeedTimers)); + updateLowSpeedTimers(outputData[i].newLowSideSpeedTimers, const_cast(inputData.vehWheels4DynData->mTireLowSideSpeedTimers)); + updateJounces(outputData[i].jounces, const_cast(inputData.vehWheels4DynData->mJounces)); + if((numSubSteps-1) == k) + { + updateCachedHitData(outputData[i].cachedHitCounts, outputData[i].cachedHitPlanes, outputData[i].cachedHitDistances, outputData[i].cachedFrictionMultipliers, outputData[i].cachedHitQueryTypes, &wheels4DynDatas[i]); + } + chassisForce+=outputData[i].chassisForce; + chassisTorque+=outputData[i].chassisTorque; + if(0 == k) + { + wheels4DynDatas[i].mVehicleConstraints->mData=outputData[i].vehConstraintData; + } + storeSuspWheelTireResults(outputData[i], inputData.steerAngles, &wheelQueryResults[4*i], numActiveWheelsPerBlock4[i]); + storeHitActorForces(outputData[i], &vehicleConcurrentUpdates.concurrentWheelUpdates[4*i], numActiveWheelsPerBlock4[i]); + } + + //Store the tire torques in a single array. + PxF32 tireTorques[PX_MAX_NB_WHEELS]; + for(PxU32 i=0;imWheelsSimData, carChassisTransform, wheelQueryResults, chassisTorque); + + //Integrate the chassis velocity by applying the accumulated force and torque. + integrateBody(inverseChassisMass, inverseInertia, chassisForce, chassisTorque, subTimestep, carChassisLinVel, carChassisAngVel, carChassisTransform); + + END_TIMER(TIMER_POSTUPDATE2); + } + + //Set the new chassis linear/angular velocity. + if(!gApplyForces) + { + vehicleConcurrentUpdates.linearMomentumChange = carChassisLinVel; + vehicleConcurrentUpdates.angularMomentumChange = carChassisAngVel; + } + else + { + //integration steps are: + //v = v0 + a*dt (1) + //x = x0 + v*dt (2) + //Sub (2) into (1. + //x = x0 + v0*dt + a*dt*dt; + //Rearrange for a + //a = (x -x0 - v0*dt)/(dt*dt) = [(x-x0)/dt - v0/dt] + //Rearrange again with v = (x-x0)/dt + //a = (v - v0)/dt + vehicleConcurrentUpdates.linearMomentumChange = (carChassisLinVel-carChassisLinVelOrig)*recipTimestep; + vehicleConcurrentUpdates.angularMomentumChange = (carChassisAngVel-carChassisAngVelOrig)*recipTimestep; + } + + START_TIMER(TIMER_POSTUPDATE3); + + //Pose the wheels from jounces, rotations angles, and steer angles. + PxTransform localPoses0[4] = {PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity)}; + computeWheelLocalPoses(wheels4SimDatas[0],wheels4DynDatas[0],&wheelQueryResults[4*0],numActiveWheelsPerBlock4[0],carChassisCMLocalPose,localPoses0); + wheelQueryResults[4*0 + 0].localPose = localPoses0[0]; + wheelQueryResults[4*0 + 1].localPose = localPoses0[1]; + wheelQueryResults[4*0 + 2].localPose = localPoses0[2]; + wheelQueryResults[4*0 + 3].localPose = localPoses0[3]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 0].localPose = localPoses0[0]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 1].localPose = localPoses0[1]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 2].localPose = localPoses0[2]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 3].localPose = localPoses0[3]; + for(PxU32 i=1;iwheelQueryResults) + { + PxMemCopy(vehWheelQueryResults->wheelQueryResults, wheelQueryResults, sizeof(PxWheelQueryResult)*numActiveWheels); + } + + if(vehConcurrentUpdates) + { + //Copy across to input data structure so that writes can be applied later. + PxMemCopy(vehConcurrentUpdates->concurrentWheelUpdates, vehicleConcurrentUpdates.concurrentWheelUpdates, sizeof(PxVehicleWheelConcurrentUpdateData)*numActiveWheels); + vehConcurrentUpdates->linearMomentumChange = vehicleConcurrentUpdates.linearMomentumChange; + vehConcurrentUpdates->angularMomentumChange = vehicleConcurrentUpdates.angularMomentumChange; + vehConcurrentUpdates->staySleeping = vehicleConcurrentUpdates.staySleeping; + vehConcurrentUpdates->wakeup = vehicleConcurrentUpdates.wakeup; + } + else + { + //Apply the writes immediately. + PxVehicleWheels* vehWheels[1]={vehDriveNW}; + PxVehiclePostUpdates(&vehicleConcurrentUpdates, 1, vehWheels); + } + + END_TIMER(TIMER_POSTUPDATE3); +} + +void PxVehicleUpdate::updateTank +(const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleDriveTank* vehDriveTank, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates) +{ + PX_SIMD_GUARD; // denorm exception in transformInertiaTensor() + + PX_CHECK_AND_RETURN( + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL]>-0.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL]<1.01f, + "Illegal tank control value - accel must be in range (0,1)" ); + PX_CHECK_AND_RETURN( + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT]>-0.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT]<1.01f, + "Illegal tank control value - left brake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT]>-0.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT]<1.01f, + "Illegal tank control right value - right brake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]>-1.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]<1.01f, + "Illegal tank control value - left thrust must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]>-1.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]<1.01f, + "Illegal tank control value - right thrust must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + PxVehicleDriveTankControlModel::eSPECIAL==vehDriveTank->mDriveModel || + (vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]>-0.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]<1.01f), + "Illegal tank control value - left thrust must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + PxVehicleDriveTankControlModel::eSPECIAL==vehDriveTank->mDriveModel || + (vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]>-0.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]<1.01f), + "Illegal tank control value - right thrust must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + PxVehicleDriveTankControlModel::eSPECIAL==vehDriveTank->mDriveModel || + 0.0f== + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]* + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT], + "Illegal tank control value - thrust left and brake left simultaneously non-zero in standard drive mode"); + PX_CHECK_AND_RETURN( + PxVehicleDriveTankControlModel::eSPECIAL==vehDriveTank->mDriveModel || + 0.0f== + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]* + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT], + "Illegal tank control value - thrust right and brake right simultaneously non-zero in standard drive mode"); + PX_CHECK_AND_RETURN( + !(vehDriveTank->getRigidDynamicActor()->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC), + "Attempting to update a tank with a kinematic actor - this isn't allowed"); + PX_CHECK_AND_RETURN( + NULL==vehWheelQueryResults || vehWheelQueryResults->nbWheelQueryResults >= vehDriveTank->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + +#if PX_CHECKED + { + PxVec3 fl=vehDriveTank->mWheelsSimData.getWheelCentreOffset(PxVehicleDriveTankWheelOrder::eFRONT_LEFT); + PxVec3 fr=vehDriveTank->mWheelsSimData.getWheelCentreOffset(PxVehicleDriveTankWheelOrder::eFRONT_RIGHT); + const PxVec3 right=gRight; + const PxF32 s0=computeSign((fr-fl).dot(right)); + for(PxU32 i=PxVehicleDriveTankWheelOrder::e1ST_FROM_FRONT_LEFT;imWheelsSimData.getNbWheels();i+=2) + { + PxVec3 rl=vehDriveTank->mWheelsSimData.getWheelCentreOffset(i); + PxVec3 rr=vehDriveTank->mWheelsSimData.getWheelCentreOffset(i+1); + const PxF32 t0=computeSign((rr-rl).dot(right)); + PX_CHECK_AND_RETURN(s0==t0 || 0==s0 || 0==t0, "Tank wheels must be ordered with odd wheels on one side and even wheels on the other side"); + } + } +#endif + + +#if PX_DEBUG_VEHICLE_ON + for(PxU32 i=0;imWheelsSimData.mNbWheels4;i++) + { + updateGraphDataInternalWheelDynamics(4*i,vehDriveTank->mWheelsDynData.mWheels4DynData[i].mWheelSpeeds); + } + updateGraphDataInternalEngineDynamics(vehDriveTank->mDriveDynData.getEngineRotationSpeed()); +#endif + + //Unpack the tank simulation and instanced dynamics components. + const PxVehicleWheels4SimData* wheels4SimDatas=vehDriveTank->mWheelsSimData.mWheels4SimData; + const PxVehicleTireLoadFilterData& tireLoadFilterData=vehDriveTank->mWheelsSimData.mNormalisedLoadFilter; + PxVehicleWheels4DynData* wheels4DynDatas=vehDriveTank->mWheelsDynData.mWheels4DynData; + const PxU32 numWheels4=vehDriveTank->mWheelsSimData.mNbWheels4; + const PxU32 numActiveWheels=vehDriveTank->mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=4-(4*numWheels4-numActiveWheels); + const PxVehicleDriveSimData driveSimData=vehDriveTank->mDriveSimData; + PxVehicleDriveDynData& driveDynData=vehDriveTank->mDriveDynData; + PxRigidDynamic* vehActor=vehDriveTank->mActor; + + //We need to store that data we are going to write to actors so we can do this at the end in one go with fewer write locks. + PxVehicleWheelConcurrentUpdateData wheelConcurrentUpdates[PX_MAX_NB_WHEELS]; + PxVehicleConcurrentUpdateData vehicleConcurrentUpdates; + vehicleConcurrentUpdates.nbConcurrentWheelUpdates = numActiveWheels; + vehicleConcurrentUpdates.concurrentWheelUpdates = wheelConcurrentUpdates; + + //Test if a non-zero drive torque was applied or if a non-zero steer angle was applied. + bool finiteInputApplied=false; + if(0!=driveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT) || + 0!=driveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT) || + 0!=driveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL) || + driveDynData.getGearDown() || driveDynData.getGearUp()) + { + finiteInputApplied=true; + } + + //Awake or sleep. + { + if(vehActor->isSleeping()) + { + if(finiteInputApplied) + { + //Driving inputs so we need the actor to start moving. + vehicleConcurrentUpdates.wakeup = true; + } + else if(isOnDynamicActor(vehDriveTank->mWheelsSimData, vehDriveTank->mWheelsDynData)) + { + //Driving on dynamic so we need to keep moving. + vehicleConcurrentUpdates.wakeup = true; + } + else + { + //No driving inputs and the actor is asleep. + //Set internal dynamics to sleep. + setInternalDynamicsToZero(vehDriveTank); + if(vehConcurrentUpdates) vehConcurrentUpdates->staySleeping = true; + return; + } + } + } + + //In each block of 4 wheels record how many wheels are active. + PxU32 numActiveWheelsPerBlock4[PX_MAX_NB_SUSPWHEELTIRE4]={0,0,0,0,0}; + numActiveWheelsPerBlock4[0]=PxMin(numActiveWheels,PxU32(4)); + for(PxU32 i=1;imWheelsDynData.mTireForceCalculators->mShaderData[4*i+0]; + tires4ForceCalculators[i].mShaderData[1]=vehDriveTank->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+1]; + tires4ForceCalculators[i].mShaderData[2]=vehDriveTank->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+2]; + tires4ForceCalculators[i].mShaderData[3]=vehDriveTank->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+3]; + tires4ForceCalculators[i].mShader=vehDriveTank->mWheelsDynData.mTireForceCalculators->mShader; + } + + //Mark the suspension/tire constraints as dirty to force them to be updated in the sdk. + for(PxU32 i=0;imarkDirty(); + } + + //Need to store report data to pose the wheels. + PxWheelQueryResult wheelQueryResults[PX_MAX_NB_WHEELS]; + + + //Center of mass local pose. + PxTransform carChassisCMLocalPose; + //Compute the transform of the center of mass. + PxTransform origCarChassisTransform; + PxTransform carChassisTransform; + //Inverse mass and inertia to apply the tire/suspension forces as impulses. + PxF32 inverseChassisMass; + PxVec3 inverseInertia; + //Linear and angular velocity. + PxVec3 carChassisLinVel; + PxVec3 carChassisAngVel; + { + carChassisCMLocalPose = vehActor->getCMassLocalPose(); + carChassisCMLocalPose.q = PxQuat(PxIdentity); + origCarChassisTransform = vehActor->getGlobalPose().transform(carChassisCMLocalPose); + carChassisTransform = origCarChassisTransform; + const PxF32 chassisMass = vehActor->getMass(); + inverseChassisMass = 1.0f/chassisMass; + inverseInertia = vehActor->getMassSpaceInvInertiaTensor(); + carChassisLinVel = vehActor->getLinearVelocity(); + carChassisAngVel = vehActor->getAngularVelocity(); + } + + //Get the local poses of the wheel shapes. + //These are the poses from the last frame and equal to the poses used for the raycast we will process. + PxQuat wheelLocalPoseRotations[PX_MAX_NB_WHEELS]; + PxF32 wheelThetas[PX_MAX_NB_WHEELS]; + { + for (PxU32 i = 0; i < numActiveWheels; i++) + { + const PxI32 shapeId = vehDriveTank->mWheelsSimData.getWheelShapeMapping(i); + if (-1 != shapeId) + { + PxShape* shape = NULL; + vehActor->getShapes(&shape, 1, PxU32(shapeId)); + wheelLocalPoseRotations[i] = shape->getLocalPose().q; + wheelThetas[i] = vehDriveTank->mWheelsDynData.getWheelRotationAngle(i); + } + } + } + + + //Retrieve control values from vehicle controls. + PxF32 accel,brakeLeft,brakeRight,thrustLeft,thrustRight; + { + getTankControlValues(driveDynData,accel,brakeLeft,brakeRight,thrustLeft,thrustRight); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataControlInputs(accel,brakeLeft,brakeRight,thrustLeft,thrustRight); +#endif + } + + //Update the auto-box and decide whether to change gear up or down. + //If the tank is supposed to turn sharply don't process the auto-box. + bool useAutoGears; + if(vehDriveTank->getDriveModel()==PxVehicleDriveTankControlModel::eSPECIAL) + { + useAutoGears = driveDynData.getUseAutoGears() ? ((((thrustRight*thrustLeft) >= 0.0f) || (0.0f==thrustLeft && 0.0f==thrustRight)) ? true : false) : false; + } + else + { + useAutoGears = driveDynData.getUseAutoGears() ? (thrustRight*brakeLeft>0 || thrustLeft*brakeRight>0 ? false : true) : false; + } + if(useAutoGears) + { + processAutoBox(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL,timestep,driveSimData,driveDynData); + } + + //Process gear-up/gear-down commands. + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + processGears(timestep,gearsData,driveDynData); + } + + //Clutch strength; + PxF32 K; + { + const PxVehicleClutchData& clutchData=driveSimData.getClutchData(); + const PxU32 currentGear=driveDynData.getCurrentGear(); + K=computeClutchStrength(clutchData, currentGear); + } + + //Gear ratio. + PxF32 G; + PxU32 currentGear; + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + currentGear=driveDynData.getCurrentGear(); + G=computeGearRatio(gearsData,currentGear); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataGearRatio(G); +#endif + } + + bool isIntentionToAccelerate; + { + const PxF32 thrustLeftAbs=PxAbs(thrustLeft); + const PxF32 thrustRightAbs=PxAbs(thrustRight); + isIntentionToAccelerate = (accel*(thrustLeftAbs+thrustRightAbs)>0 && PxVehicleGearsData::eNEUTRAL != currentGear); + } + + + //Compute the wheels that are enabled/disabled. + bool activeWheelStates[PX_MAX_NB_WHEELS]; + PxMemZero(activeWheelStates, sizeof(bool)*PX_MAX_NB_WHEELS); + for(PxU32 i=0;imWheelsSimData.mActiveWheelsBitmapBuffer, &activeWheelStates[4*i]); + } + + //Set up contribution of each wheel to the average wheel speed at the clutch + //Set up the torque ratio delivered by the diff to each wheel. + //Set the sign of the gearing applied to the left and right wheels. + PxF32 aveWheelSpeedContributions[PX_MAX_NB_WHEELS]; + PxF32 diffTorqueRatios[PX_MAX_NB_WHEELS]; + PxF32 wheelGearings[PX_MAX_NB_WHEELS]; + PxMemZero(aveWheelSpeedContributions, sizeof(PxF32)*PX_MAX_NB_WHEELS); + PxMemZero(diffTorqueRatios, sizeof(PxF32)*PX_MAX_NB_WHEELS); + PxMemZero(wheelGearings, sizeof(PxF32)*PX_MAX_NB_WHEELS); + computeTankDiff + (thrustLeft, thrustRight, + numActiveWheels, activeWheelStates, + aveWheelSpeedContributions, diffTorqueRatios, wheelGearings); + + //Compute an accelerator pedal value per wheel. + bool isAccelApplied[PX_MAX_NB_WHEELS]; + PxMemZero(isAccelApplied, sizeof(bool)*PX_MAX_NB_WHEELS); + if(isIntentionToAccelerate) + { + PX_ASSERT(accel>0); + for(PxU32 i=0;imWheelsSimData,carChassisLinVel,carChassisTransform,gForward); + const PxF32 timeFraction=1.0f/(1.0f*numSubSteps); + const PxF32 subTimestep=timestep*timeFraction; + const PxF32 recipSubTimeStep=1.0f/subTimestep; + const PxF32 recipTimestep=1.0f/timestep; + const PxF32 minLongSlipDenominator=vehDriveTank->mWheelsSimData.mMinLongSlipDenominator; + ProcessSuspWheelTireConstData constData={timeFraction, subTimestep, recipSubTimeStep, gravity, gravityMagnitude, recipGravityMagnitude, true, minLongSlipDenominator, vehActor, &drivableSurfaceToTireFrictionPairs}; + + for(PxU32 k=0;k(inputData.vehWheels4DynData->mTireLowForwardSpeedTimers)); + updateLowSpeedTimers(outputData[i].newLowSideSpeedTimers, const_cast(inputData.vehWheels4DynData->mTireLowSideSpeedTimers)); + updateJounces(outputData[i].jounces, const_cast(inputData.vehWheels4DynData->mJounces)); + if((numSubSteps-1) == k) + { + updateCachedHitData(outputData[i].cachedHitCounts, outputData[i].cachedHitPlanes, outputData[i].cachedHitDistances, outputData[i].cachedFrictionMultipliers, outputData[i].cachedHitQueryTypes, &wheels4DynDatas[i]); + } + chassisForce+=outputData[i].chassisForce; + chassisTorque+=outputData[i].chassisTorque; + if(0 == k) + { + wheels4DynDatas[i].mVehicleConstraints->mData=outputData[i].vehConstraintData; + } + storeSuspWheelTireResults(outputData[i], inputData.steerAngles, &wheelQueryResults[4*i], numActiveWheelsPerBlock4[i]); + storeHitActorForces(outputData[i], &vehicleConcurrentUpdates.concurrentWheelUpdates[4*i], numActiveWheelsPerBlock4[i]); + } + + //Copy the tire torques to a single array. + PxF32 tireTorques[PX_MAX_NB_WHEELS]; + for(PxU32 i=0;imWheelsSimData, carChassisTransform, wheelQueryResults, chassisTorque); + + //Integrate the chassis velocity by applying the accumulated force and torque. + integrateBody(inverseChassisMass, inverseInertia, chassisForce, chassisTorque, subTimestep, carChassisLinVel, carChassisAngVel, carChassisTransform); + } + + //Set the new chassis linear/angular velocity. + if(!gApplyForces) + { + vehicleConcurrentUpdates.linearMomentumChange = carChassisLinVel; + vehicleConcurrentUpdates.angularMomentumChange = carChassisAngVel; + } + else + { + //integration steps are: + //v = v0 + a*dt (1) + //x = x0 + v*dt (2) + //Sub (2) into (1. + //x = x0 + v0*dt + a*dt*dt; + //Rearrange for a + //a = (x -x0 - v0*dt)/(dt*dt) = [(x-x0)/dt - v0/dt] + //Rearrange again with v = (x-x0)/dt + //a = (v - v0)/dt + vehicleConcurrentUpdates.linearMomentumChange = (carChassisLinVel-carChassisLinVelOrig)*recipTimestep; + vehicleConcurrentUpdates.angularMomentumChange = (carChassisAngVel-carChassisAngVelOrig)*recipTimestep; + } + + //Pose the wheels from jounces, rotations angles, and steer angles. + PxTransform localPoses0[4] = {PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity)}; + computeWheelLocalPoses(wheels4SimDatas[0],wheels4DynDatas[0],&wheelQueryResults[4*0],numActiveWheelsPerBlock4[0],carChassisCMLocalPose,localPoses0); + wheelQueryResults[4*0 + 0].localPose = localPoses0[0]; + wheelQueryResults[4*0 + 1].localPose = localPoses0[1]; + wheelQueryResults[4*0 + 2].localPose = localPoses0[2]; + wheelQueryResults[4*0 + 3].localPose = localPoses0[3]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 0].localPose = localPoses0[0]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 1].localPose = localPoses0[1]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 2].localPose = localPoses0[2]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 3].localPose = localPoses0[3]; + for(PxU32 i=1;iwheelQueryResults) + { + PxMemCopy(vehWheelQueryResults->wheelQueryResults, wheelQueryResults, sizeof(PxWheelQueryResult)*numActiveWheels); + } + + if(vehConcurrentUpdates) + { + //Copy across to input data structure so that writes can be applied later. + PxMemCopy(vehConcurrentUpdates->concurrentWheelUpdates, vehicleConcurrentUpdates.concurrentWheelUpdates, sizeof(PxVehicleWheelConcurrentUpdateData)*numActiveWheels); + vehConcurrentUpdates->linearMomentumChange = vehicleConcurrentUpdates.linearMomentumChange; + vehConcurrentUpdates->angularMomentumChange = vehicleConcurrentUpdates.angularMomentumChange; + vehConcurrentUpdates->staySleeping = vehicleConcurrentUpdates.staySleeping; + vehConcurrentUpdates->wakeup = vehicleConcurrentUpdates.wakeup; + } + else + { + //Apply the writes immediately. + PxVehicleWheels* vehWheels[1]={vehDriveTank}; + PxVehiclePostUpdates(&vehicleConcurrentUpdates, 1, vehWheels); + } +} + +void PxVehicleUpdate::updateNoDrive +(const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleNoDrive* vehNoDrive, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates) +{ + PX_SIMD_GUARD; // denorm exception in transformInertiaTensor() on osx + + PX_CHECK_AND_RETURN( + !(vehNoDrive->getRigidDynamicActor()->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC), + "Attempting to update a PxVehicleNoDrive with a kinematic actor - this isn't allowed"); + + PX_CHECK_AND_RETURN( + NULL==vehWheelQueryResults || vehWheelQueryResults->nbWheelQueryResults >= vehNoDrive->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + +#if PX_CHECKED + for(PxU32 i=0;imWheelsSimData.getNbWheels();i++) + { + PX_CHECK_AND_RETURN( + !vehNoDrive->mWheelsSimData.getIsWheelDisabled(i) || 0==vehNoDrive->getDriveTorque(i), + "Disabled wheels should have zero drive torque applied to them."); + } +#endif + +#if PX_DEBUG_VEHICLE_ON + for(PxU32 i=0;imWheelsSimData.mNbWheels4;i++) + { + updateGraphDataInternalWheelDynamics(4*i,vehNoDrive->mWheelsDynData.mWheels4DynData[i].mWheelSpeeds); + } +#endif + + //Unpack the tank simulation and instanced dynamics components. + const PxVehicleWheels4SimData* wheels4SimDatas=vehNoDrive->mWheelsSimData.mWheels4SimData; + const PxVehicleTireLoadFilterData& tireLoadFilterData=vehNoDrive->mWheelsSimData.mNormalisedLoadFilter; + PxVehicleWheels4DynData* wheels4DynDatas=vehNoDrive->mWheelsDynData.mWheels4DynData; + const PxU32 numWheels4=vehNoDrive->mWheelsSimData.mNbWheels4; + const PxU32 numActiveWheels=vehNoDrive->mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=4-(4*numWheels4-numActiveWheels); + PxRigidDynamic* vehActor=vehNoDrive->mActor; + + //We need to store that data we are going to write to actors so we can do this at the end in one go with fewer write locks. + PxVehicleWheelConcurrentUpdateData wheelConcurrentUpdates[PX_MAX_NB_WHEELS]; + PxVehicleConcurrentUpdateData vehicleConcurrentUpdates; + vehicleConcurrentUpdates.nbConcurrentWheelUpdates = numActiveWheels; + vehicleConcurrentUpdates.concurrentWheelUpdates = wheelConcurrentUpdates; + + //Test if a non-zero drive torque was applied or if a non-zero steer angle was applied. + bool finiteInputApplied=false; + for(PxU32 i=0;igetDriveTorque(i) != 0.0f) + { + finiteInputApplied=true; + break; + } + if(vehNoDrive->getSteerAngle(i)!=0.0f) + { + finiteInputApplied=true; + break; + } + } + + //Wake or sleep. + { + if(vehActor->isSleeping()) + { + if(finiteInputApplied) + { + //Driving inputs so we need the actor to start moving. + vehicleConcurrentUpdates.wakeup = true; + } + else if(isOnDynamicActor(vehNoDrive->mWheelsSimData, vehNoDrive->mWheelsDynData)) + { + //Driving on dynamic so we need to keep moving. + vehicleConcurrentUpdates.wakeup = true; + } + else + { + //No driving inputs and the actor is asleep. + //Set internal dynamics to sleep. + setInternalDynamicsToZero(vehNoDrive); + if(vehConcurrentUpdates) vehConcurrentUpdates->staySleeping = true; + return; + } + } + } + + //In each block of 4 wheels record how many wheels are active. + PxU32 numActiveWheelsPerBlock4[PX_MAX_NB_SUSPWHEELTIRE4]={0,0,0,0,0}; + numActiveWheelsPerBlock4[0]=PxMin(numActiveWheels,PxU32(4)); + for(PxU32 i=1;imWheelsDynData.mTireForceCalculators->mShaderData[4*i+0]; + tires4ForceCalculators[i].mShaderData[1]=vehNoDrive->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+1]; + tires4ForceCalculators[i].mShaderData[2]=vehNoDrive->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+2]; + tires4ForceCalculators[i].mShaderData[3]=vehNoDrive->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+3]; + tires4ForceCalculators[i].mShader=vehNoDrive->mWheelsDynData.mTireForceCalculators->mShader; + } + + //Mark the suspension/tire constraints as dirty to force them to be updated in the sdk. + for(PxU32 i=0;imarkDirty(); + } + + //Need to store report data to pose the wheels. + PxWheelQueryResult wheelQueryResults[PX_MAX_NB_WHEELS]; + + //Center of mass local pose. + PxTransform carChassisCMLocalPose; + //Compute the transform of the center of mass. + PxTransform origCarChassisTransform; + PxTransform carChassisTransform; + //Inverse mass and inertia to apply the tire/suspension forces as impulses. + PxF32 inverseChassisMass; + PxVec3 inverseInertia; + //Linear and angular velocity. + PxVec3 carChassisLinVel; + PxVec3 carChassisAngVel; + { + carChassisCMLocalPose = vehActor->getCMassLocalPose(); + carChassisCMLocalPose.q = PxQuat(PxIdentity); + origCarChassisTransform = vehActor->getGlobalPose().transform(carChassisCMLocalPose); + carChassisTransform = origCarChassisTransform; + const PxF32 chassisMass = vehActor->getMass(); + inverseChassisMass = 1.0f/chassisMass; + inverseInertia = vehActor->getMassSpaceInvInertiaTensor(); + carChassisLinVel = vehActor->getLinearVelocity(); + carChassisAngVel = vehActor->getAngularVelocity(); + } + + //Get the local poses of the wheel shapes. + //These are the poses from the last frame and equal to the poses used for the raycast we will process. + PxQuat wheelLocalPoseRotations[PX_MAX_NB_WHEELS]; + PxF32 wheelThetas[PX_MAX_NB_WHEELS]; + { + for (PxU32 i = 0; i < numActiveWheels; i++) + { + const PxI32 shapeId = vehNoDrive->mWheelsSimData.getWheelShapeMapping(i); + if (-1 != shapeId) + { + PxShape* shape = NULL; + vehActor->getShapes(&shape, 1, PxU32(shapeId)); + wheelLocalPoseRotations[i] = shape->getLocalPose().q; + wheelThetas[i] = vehNoDrive->mWheelsDynData.getWheelRotationAngle(i); + } + } + } + + PxF32 maxAccel=0; + PxF32 maxBrake=0; + for(PxU32 i=0;imDriveTorques[i]), maxAccel); + maxBrake = PxMax(PxAbs(vehNoDrive->mBrakeTorques[i]), maxBrake); + } + const bool isIntentionToAccelerate = (maxAccel>0.0f && 0.0f==maxBrake); + + //Store the susp line raycast data. + for(PxU32 i=0;imWheelsSimData,carChassisLinVel,carChassisTransform,gForward); + const PxF32 timeFraction=1.0f/(1.0f*numSubSteps); + const PxF32 subTimestep=timestep*timeFraction; + const PxF32 recipSubTimeStep=1.0f/subTimestep; + const PxF32 recipTimestep=1.0f/timestep; + const PxF32 minLongSlipDenominator=vehNoDrive->mWheelsSimData.mMinLongSlipDenominator; + ProcessSuspWheelTireConstData constData={timeFraction, subTimestep, recipSubTimeStep, gravity, gravityMagnitude, recipGravityMagnitude, false, minLongSlipDenominator, vehActor, &drivableSurfaceToTireFrictionPairs}; + + for(PxU32 k=0;kmBrakeTorques[4*i]; + const PxF32* PX_RESTRICT rawSteerAngles=&vehNoDrive->mSteerAngles[4*i]; + const PxF32* PX_RESTRICT rawDriveTorques=&vehNoDrive->mDriveTorques[4*i]; + + //Work out which wheels are enabled. + bool activeWheelStates[4]={false,false,false,false}; + computeWheelActiveStates(4*i, vehNoDrive->mWheelsSimData.mActiveWheelsBitmapBuffer, activeWheelStates); + + const PxVehicleWheels4SimData& wheels4SimData=wheels4SimDatas[i]; + PxVehicleWheels4DynData& wheels4DynData=wheels4DynDatas[i]; + + //Compute the brake torques. + PxF32 brakeTorques[4]={0.0f,0.0f,0.0f,0.0f}; + bool isBrakeApplied[4]={false,false,false,false}; + computeNoDriveBrakeTorques + (wheels4SimData.mWheels,wheels4DynData.mWheelSpeeds,rawBrakeTorques, + brakeTorques,isBrakeApplied); + + //Compute the per wheel accel pedal values. + bool isAccelApplied[4]={false,false,false,false}; + if(isIntentionToAccelerate) + { + computeIsAccelApplied(rawDriveTorques, isAccelApplied); + } + + //Compute jounces, slips, tire forces, suspension forces etc. + ProcessSuspWheelTireInputData inputData= + { + isIntentionToAccelerate, isAccelApplied, isBrakeApplied, rawSteerAngles, activeWheelStates, + carChassisTransform, carChassisLinVel, carChassisAngVel, + &wheelLocalPoseRotations[i], &wheelThetas[i], &wheels4SimData, &wheels4DynData, &tires4ForceCalculators[i], &tireLoadFilterData, numActiveWheelsPerBlock4[i] + }; + ProcessSuspWheelTireOutputData outputData; + processSuspTireWheels(4*i, constData, inputData, outputData); + updateLowSpeedTimers(outputData.newLowForwardSpeedTimers, const_cast(inputData.vehWheels4DynData->mTireLowForwardSpeedTimers)); + updateLowSpeedTimers(outputData.newLowSideSpeedTimers, const_cast(inputData.vehWheels4DynData->mTireLowSideSpeedTimers)); + updateJounces(outputData.jounces, const_cast(inputData.vehWheels4DynData->mJounces)); + if((numSubSteps-1) == k) + { + updateCachedHitData(outputData.cachedHitCounts, outputData.cachedHitPlanes, outputData.cachedHitDistances, outputData.cachedFrictionMultipliers, outputData.cachedHitQueryTypes, &wheels4DynData); + } + chassisForce+=outputData.chassisForce; + chassisTorque+=outputData.chassisTorque; + if(0 == k) + { + wheels4DynDatas[i].mVehicleConstraints->mData=outputData.vehConstraintData; + } + storeSuspWheelTireResults(outputData, inputData.steerAngles, &wheelQueryResults[4*i], numActiveWheelsPerBlock4[i]); + storeHitActorForces(outputData, &vehicleConcurrentUpdates.concurrentWheelUpdates[4*i], numActiveWheelsPerBlock4[i]); + + //Integrate wheel speeds. + const PxF32 wheelDampingRates[4]= + { + wheels4SimData.getWheelData(0).mDampingRate, + wheels4SimData.getWheelData(1).mDampingRate, + wheels4SimData.getWheelData(2).mDampingRate, + wheels4SimData.getWheelData(3).mDampingRate + }; + integrateNoDriveWheelSpeeds( + subTimestep, + brakeTorques,isBrakeApplied,rawDriveTorques,outputData.tireTorques,wheelDampingRates, + wheels4SimData,wheels4DynData); + + integrateNoDriveWheelRotationAngles( + subTimestep, + rawDriveTorques, + outputData.jounces, outputData.forwardSpeeds, isBrakeApplied, + wheels4SimData, + wheels4DynData); + } + + //Apply the anti-roll suspension. + procesAntiRollSuspension(vehNoDrive->mWheelsSimData, carChassisTransform, wheelQueryResults, chassisTorque); + + //Integrate the chassis velocity by applying the accumulated force and torque. + integrateBody(inverseChassisMass, inverseInertia, chassisForce, chassisTorque, subTimestep, carChassisLinVel, carChassisAngVel, carChassisTransform); + } + + //Set the new chassis linear/angular velocity. + if(!gApplyForces) + { + vehicleConcurrentUpdates.linearMomentumChange = carChassisLinVel; + vehicleConcurrentUpdates.angularMomentumChange = carChassisAngVel; + } + else + { + //integration steps are: + //v = v0 + a*dt (1) + //x = x0 + v*dt (2) + //Sub (2) into (1. + //x = x0 + v0*dt + a*dt*dt; + //Rearrange for a + //a = (x -x0 - v0*dt)/(dt*dt) = [(x-x0)/dt - v0/dt] + //Rearrange again with v = (x-x0)/dt + //a = (v - v0)/dt + vehicleConcurrentUpdates.linearMomentumChange = (carChassisLinVel-carChassisLinVelOrig)*recipTimestep; + vehicleConcurrentUpdates.angularMomentumChange = (carChassisAngVel-carChassisAngVelOrig)*recipTimestep; + } + + //Pose the wheels from jounces, rotations angles, and steer angles. + PxTransform localPoses0[4] = {PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity)}; + computeWheelLocalPoses(wheels4SimDatas[0],wheels4DynDatas[0],&wheelQueryResults[4*0],numActiveWheelsPerBlock4[0],carChassisCMLocalPose,localPoses0); + wheelQueryResults[4*0 + 0].localPose = localPoses0[0]; + wheelQueryResults[4*0 + 1].localPose = localPoses0[1]; + wheelQueryResults[4*0 + 2].localPose = localPoses0[2]; + wheelQueryResults[4*0 + 3].localPose = localPoses0[3]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 0].localPose = localPoses0[0]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 1].localPose = localPoses0[1]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 2].localPose = localPoses0[2]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 3].localPose = localPoses0[3]; + for(PxU32 i=1;iwheelQueryResults) + { + PxMemCopy(vehWheelQueryResults->wheelQueryResults, wheelQueryResults, sizeof(PxWheelQueryResult)*numActiveWheels); + } + + if(vehConcurrentUpdates) + { + //Copy across to input data structure so that writes can be applied later. + PxMemCopy(vehConcurrentUpdates->concurrentWheelUpdates, vehicleConcurrentUpdates.concurrentWheelUpdates, sizeof(PxVehicleWheelConcurrentUpdateData)*numActiveWheels); + vehConcurrentUpdates->linearMomentumChange = vehicleConcurrentUpdates.linearMomentumChange; + vehConcurrentUpdates->angularMomentumChange = vehicleConcurrentUpdates.angularMomentumChange; + vehConcurrentUpdates->staySleeping = vehicleConcurrentUpdates.staySleeping; + vehConcurrentUpdates->wakeup = vehicleConcurrentUpdates.wakeup; + } + else + { + //Apply the writes immediately. + PxVehicleWheels* vehWheels[1]={vehNoDrive}; + PxVehiclePostUpdates(&vehicleConcurrentUpdates, 1, vehWheels); + } +} + + +void PxVehicleUpdate::shiftOrigin(const PxVec3& shift, const PxU32 numVehicles, PxVehicleWheels** vehicles) +{ + for(PxU32 i=0; i < numVehicles; i++) + { + //Get the current car. + PxVehicleWheels& veh = *vehicles[i]; + PxVehicleWheels4DynData* PX_RESTRICT wheels4DynData=veh.mWheelsDynData.mWheels4DynData; + const PxU32 numWheels4=veh.mWheelsSimData.mNbWheels4; + + //Blocks of 4 wheels. + for(PxU32 j=0; j < numWheels4; j++) + { + bool activeWheelStates[4]={false,false,false,false}; + computeWheelActiveStates(4*j, veh.mWheelsSimData.mActiveWheelsBitmapBuffer, activeWheelStates); + + if (wheels4DynData[j].mRaycastResults) // this is set when a query has been scheduled + { + PxVehicleWheels4DynData::SuspLineRaycast& raycast = + reinterpret_cast(wheels4DynData[j].mQueryOrCachedHitResults); + + for(PxU32 k=0; k < 4; k++) + { + if (activeWheelStates[k]) + { + raycast.mStarts[k] -= shift; + + if (wheels4DynData[j].mRaycastResults[k].hasBlock) + const_cast(wheels4DynData[j].mRaycastResults[k].block.position) -= shift; + } + } + } + else if(wheels4DynData[i].mSweepResults) + { + PxVehicleWheels4DynData::SuspLineSweep& sweep = + reinterpret_cast(wheels4DynData[j].mQueryOrCachedHitResults); + + for(PxU32 k=0; k < 4; k++) + { + if (activeWheelStates[k]) + { + sweep.mStartPose[k].p -= shift; + + if (wheels4DynData[j].mSweepResults[k].hasBlock) + const_cast(wheels4DynData[j].mSweepResults[k].block.position) -= shift; + } + } + } + } + } +} + +}//namespace physx + +#if PX_DEBUG_VEHICLE_ON + +///////////////////////////////////////////////////////////////////////////////// +//Update a single vehicle of any type and record the associated telemetry data. +///////////////////////////////////////////////////////////////////////////////// + +void PxVehicleUpdate::updateSingleVehicleAndStoreTelemetryData +(const PxF32 timestep, const PxVec3& gravity, const PxVehicleDrivableSurfaceToTireFrictionPairs& vehicleDrivableSurfaceToTireFrictionPairs, + PxVehicleWheels* vehWheels, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleTelemetryData& telemetryData) +{ + START_TIMER(TIMER_ALL); + + PX_CHECK_MSG(gravity.magnitude()>0, "gravity vector must have non-zero length"); + PX_CHECK_MSG(timestep>0, "timestep must be greater than zero"); + PX_CHECK_AND_RETURN(gThresholdForwardSpeedForWheelAngleIntegration>0, "PxInitVehicleSDK needs to be called before ever calling PxVehicleUpdateSingleVehicleAndStoreTelemetryData"); + PX_CHECK_MSG(vehWheels->mWheelsSimData.getNbWheels()==telemetryData.getNbWheelGraphs(), "vehicle and telemetry data need to have the same number of wheels"); + PX_CHECK_AND_RETURN(NULL==vehWheelQueryResults || vehWheelQueryResults->nbWheelQueryResults >= vehWheels->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + +#if PX_CHECKED + for(PxU32 i=0;imWheelsSimData.mNbWheels4;i++) + { + PX_CHECK_MSG(vehWheels->mWheelsDynData.mWheels4DynData[i].mRaycastResults || vehWheels->mWheelsDynData.mWheels4DynData[i].mSweepResults, + "Need to call PxVehicleSuspensionRaycasts or PxVehicleSuspensionSweeps before trying to update"); + } + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + PX_CHECK_MSG(vehWheels->mWheelsDynData.mTireForceCalculators->mShaderData[i], "Need to set non-null tire force shader data ptr"); + } + PX_CHECK_MSG(vehWheels->mWheelsDynData.mTireForceCalculators->mShader, "Need to set non-null tire force shader function"); + + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(i) || -1==vehWheels->mWheelsSimData.getWheelShapeMapping(i), + "Disabled wheels must not be associated with a PxShape: use setWheelShapeMapping to remove the association"); + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(i) || 0==vehWheels->mWheelsDynData.getWheelRotationSpeed(i), + "Disabled wheels must have zero rotation speed: use setWheelRotationSpeed to set the wheel to zero rotation speed"); + } +#endif + + PxF32 engineGraphData[PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS]; + PxMemZero(&engineGraphData[0], PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS*sizeof(PxF32)); + gCarEngineGraphData=engineGraphData; + + PxF32 wheelGraphData[PX_MAX_NB_WHEELS][PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS]; + PxMemZero(&wheelGraphData[0][0], PX_MAX_NB_WHEELS*PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS*sizeof(PxF32)); + for(PxU32 i=0;i<4*vehWheels->mWheelsSimData.mNbWheels4;i++) + { + gCarWheelGraphData[i]=wheelGraphData[i]; + } + for(PxU32 i=4*vehWheels->mWheelsSimData.mNbWheels4; imType) + { + case PxVehicleTypes::eDRIVE4W: + { + PxVehicleDrive4W* vehDrive4W=static_cast(vehWheels); + + PxVehicleUpdate::updateDrive4W( + timestep, + gravity, gravityMagnitude, recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDrive4W, vehWheelQueryResults, NULL); + + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + telemetryData.mWheelGraphs[i].updateTimeSlice(wheelGraphData[i]); + telemetryData.mSuspforceAppPoints[i]=suspForceAppPoints[i]; + telemetryData.mTireforceAppPoints[i]=tireForceAppPoints[i]; + } + telemetryData.mEngineGraph->updateTimeSlice(engineGraphData); + } + break; + case PxVehicleTypes::eDRIVENW: + { + PxVehicleDriveNW* vehDriveNW=static_cast(vehWheels); + + PxVehicleUpdate::updateDriveNW( + timestep, + gravity, gravityMagnitude, recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveNW, vehWheelQueryResults, NULL); + + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + telemetryData.mWheelGraphs[i].updateTimeSlice(wheelGraphData[i]); + telemetryData.mSuspforceAppPoints[i]=suspForceAppPoints[i]; + telemetryData.mTireforceAppPoints[i]=tireForceAppPoints[i]; + } + telemetryData.mEngineGraph->updateTimeSlice(engineGraphData); + } + break; + + case PxVehicleTypes::eDRIVETANK: + { + PxVehicleDriveTank* vehDriveTank=static_cast(vehWheels); + + PxVehicleUpdate::updateTank( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveTank, vehWheelQueryResults, NULL); + + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + telemetryData.mWheelGraphs[i].updateTimeSlice(wheelGraphData[i]); + telemetryData.mSuspforceAppPoints[i]=suspForceAppPoints[i]; + telemetryData.mTireforceAppPoints[i]=tireForceAppPoints[i]; + } + telemetryData.mEngineGraph->updateTimeSlice(engineGraphData); + } + break; + case PxVehicleTypes::eNODRIVE: + { + PxVehicleNoDrive* vehDriveNoDrive=static_cast(vehWheels); + + PxVehicleUpdate::updateNoDrive( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveNoDrive, vehWheelQueryResults, NULL); + + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + telemetryData.mWheelGraphs[i].updateTimeSlice(wheelGraphData[i]); + telemetryData.mSuspforceAppPoints[i]=suspForceAppPoints[i]; + telemetryData.mTireforceAppPoints[i]=tireForceAppPoints[i]; + } + } + break; + + default: + PX_CHECK_MSG(false, "updateSingleVehicleAndStoreTelemetryData - unsupported vehicle type"); + break; + } + + END_TIMER(TIMER_ALL); + +#if PX_VEHICLE_PROFILE + + gTimerCount++; + if(10==gTimerCount) + { + + /* + printf("%f %f %f %f %f %f %f %f %f\n", + localTimers[TIMER_ADMIN]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_GRAPHS]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_COMPONENTS_UPDATE]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_WHEELS]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_INTERNAL_DYNAMICS_SOLVER]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_POSTUPDATE1]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_POSTUPDATE2]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_POSTUPDATE3]/(1.0f*localTimers[TIMER_ALL]), + floatTimeIn10sOfNs); + */ + + printf("%f %f %f %f %f %f \n", + getTimerFraction(TIMER_WHEELS), + getTimerFraction(TIMER_INTERNAL_DYNAMICS_SOLVER), + getTimerFraction(TIMER_POSTUPDATE2), + getTimerFraction(TIMER_POSTUPDATE3), + getTimerInMilliseconds(TIMER_ALL), + getTimerInMilliseconds(TIMER_RAYCASTS)); + + gTimerCount=0; + for(PxU32 i=0;i0, "gravity vector must have non-zero length"); + PX_CHECK_AND_RETURN(timestep>0, "timestep must be greater than zero"); + PX_CHECK_AND_RETURN(gThresholdForwardSpeedForWheelAngleIntegration>0, "PxInitVehicleSDK needs to be called before ever calling PxVehicleUpdates"); + +#if PX_CHECKED + for(PxU32 i=0;imWheelsSimData.mNbWheels4;j++) + { + PX_CHECK_MSG( + vehWheels->mWheelsDynData.mWheels4DynData[j].mRaycastResults || + vehWheels->mWheelsDynData.mWheels4DynData[j].mSweepResults || + vehWheels->mWheelsDynData.mWheels4DynData[0].mHasCachedRaycastHitPlane || + (vehWheels->mWheelsSimData.getIsWheelDisabled(4*j+0) && + vehWheels->mWheelsSimData.getIsWheelDisabled(4*j+1) && + vehWheels->mWheelsSimData.getIsWheelDisabled(4*j+2) && + vehWheels->mWheelsSimData.getIsWheelDisabled(4*j+3)), + "Need to call PxVehicleSuspensionRaycasts or PxVehicleSuspensionSweeps at least once before trying to update"); + } + for(PxU32 j=0;jmWheelsSimData.mNbActiveWheels;j++) + { + PX_CHECK_MSG(vehWheels->mWheelsDynData.mTireForceCalculators->mShaderData[j], "Need to set non-null tire force shader data ptr"); + } + PX_CHECK_MSG(vehWheels->mWheelsDynData.mTireForceCalculators->mShader, "Need to set non-null tire force shader function"); + + PX_CHECK_AND_RETURN(NULL==vehicleWheelQueryResults || vehicleWheelQueryResults[i].nbWheelQueryResults >= vehicles[i]->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + + for(PxU32 j=0;jmWheelsSimData.mNbActiveWheels;j++) + { + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(j) || -1==vehWheels->mWheelsSimData.getWheelShapeMapping(j), + "Disabled wheels must not be associated with a PxShape: use setWheelShapeMapping to remove the association"); + + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(j) || 0==vehWheels->mWheelsDynData.getWheelRotationSpeed(j), + "Disabled wheels must have zero rotation speed: use setWheelRotationSpeed to set the wheel to zero rotation speed"); + } + + PX_CHECK_AND_RETURN(!vehicleConcurrentUpdates || (vehicleConcurrentUpdates[i].concurrentWheelUpdates && vehicleConcurrentUpdates[i].nbConcurrentWheelUpdates >= vehicles[i]->mWheelsSimData.getNbWheels()), + "vehicleConcurrentUpdates is illegally configured with either null pointers or with insufficient memory for successful concurrent updates."); + + for(PxU32 j=0; j < vehWheels->mWheelsSimData.mNbActiveAntiRollBars; j++) + { + const PxVehicleAntiRollBarData antiRoll = vehWheels->mWheelsSimData.getAntiRollBarData(j); + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(antiRoll.mWheel0), "Wheel0 of antiroll bar is disabled. This is not supported."); + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(antiRoll.mWheel1), "Wheel1 of antiroll bar is disabled. This is not supported."); + } + } +#endif + +#if PX_DEBUG_VEHICLE_ON + gCarEngineGraphData=NULL; + for(PxU32 j=0;jmType) + { + case PxVehicleTypes::eDRIVE4W: + { + PxVehicleDrive4W* vehDrive4W=static_cast(vehWheels); + + PxVehicleUpdate::updateDrive4W( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDrive4W, vehWheelQueryResults, vehConcurrentUpdateData); + } + break; + + case PxVehicleTypes::eDRIVENW: + { + PxVehicleDriveNW* vehDriveNW=static_cast(vehWheels); + + PxVehicleUpdate::updateDriveNW( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveNW, vehWheelQueryResults, vehConcurrentUpdateData); + } + break; + + case PxVehicleTypes::eDRIVETANK: + { + PxVehicleDriveTank* vehDriveTank=static_cast(vehWheels); + + PxVehicleUpdate::updateTank( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveTank, vehWheelQueryResults, vehConcurrentUpdateData); + } + break; + + case PxVehicleTypes::eNODRIVE: + { + PxVehicleNoDrive* vehDriveNoDrive=static_cast(vehWheels); + + PxVehicleUpdate::updateNoDrive( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveNoDrive, vehWheelQueryResults, vehConcurrentUpdateData); + } + break; + + default: + PX_CHECK_MSG(false, "update - unsupported vehicle type"); + break; + } + } +} + + +void PxVehicleUpdate::updatePost +(const PxVehicleConcurrentUpdateData* vehicleConcurrentUpdates, const PxU32 numVehicles, PxVehicleWheels** vehicles) +{ + PX_CHECK_AND_RETURN(vehicleConcurrentUpdates, "vehicleConcurrentUpdates must be non-null."); + +#if PX_CHECKED + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;j++) + { + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(j) || -1==vehWheels->mWheelsSimData.getWheelShapeMapping(j), + "Disabled wheels must not be associated with a PxShape: use setWheelShapeMapping to remove the association"); + + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(j) || 0==vehWheels->mWheelsDynData.getWheelRotationSpeed(j), + "Disabled wheels must have zero rotation speed: use setWheelRotationSpeed to set the wheel to zero rotation speed"); + + PX_CHECK_AND_RETURN(vehicleConcurrentUpdates[i].concurrentWheelUpdates && vehicleConcurrentUpdates[i].nbConcurrentWheelUpdates >= vehWheels->mWheelsSimData.getNbWheels(), + "vehicleConcurrentUpdates is illegally configured with either null pointers or insufficient memory for successful concurrent vehicle updates."); + } + } +#endif + + for(PxU32 i=0;igetRigidDynamicActor(); + + //Get the concurrent update data for the ith vehicle. + //This contains the data that couldn't get updated concurrently and now must be + //set sequentially. + const PxVehicleConcurrentUpdateData& vehicleConcurrentUpdate = vehicleConcurrentUpdates[i]; + + //Test if the actor is to remain sleeping. + //If the actor is to remain sleeping then do nothing. + if(!vehicleConcurrentUpdate.staySleeping) + { + //Wake the vehicle's actor up as required. + if(vehicleConcurrentUpdate.wakeup) + { + vehActor->wakeUp(); + } + + //Apply momentum changes to vehicle's actor + if(!gApplyForces) + { + vehActor->setLinearVelocity(vehicleConcurrentUpdate.linearMomentumChange, false); + vehActor->setAngularVelocity(vehicleConcurrentUpdate.angularMomentumChange, false); + } + else + { + vehActor->addForce(vehicleConcurrentUpdate.linearMomentumChange, PxForceMode::eACCELERATION, false); + vehActor->addTorque(vehicleConcurrentUpdate.angularMomentumChange, PxForceMode::eACCELERATION, false); + } + + //In each block of 4 wheels record how many wheels are active. + const PxU32 numActiveWheels=vehWheels->mWheelsSimData.mNbActiveWheels; + const PxU32 numWheels4 = vehWheels->mWheelsSimData.getNbWheels4(); + const PxU32 numActiveWheelsInLast4=4-(4*numWheels4 - numActiveWheels); + PxU32 numActiveWheelsPerBlock4[PX_MAX_NB_SUSPWHEELTIRE4]={0,0,0,0,0}; + numActiveWheelsPerBlock4[0]=PxMin(numActiveWheels,PxU32(4)); + for(PxU32 j=1;jmWheelsSimData.mWheels4SimData[j],localPoses,numActiveWheelsPerBlock4[j],vehActor); + } + + //Apply forces to dynamic actors hit by the wheels. + for(PxU32 j=0;jgetCMassLocalPose(); + massXform.q = PxQuat(PxIdentity); + PxTransform carChassisTrnsfm = vehActor->getGlobalPose().transform(massXform); + + //Add a raycast for each wheel. + for(PxU32 j=0;j=0); + PX_ASSERT(maxDroop>=0); + + if(!activeWheelStates[j]) + { + //For disabled wheels just issue a raycast of almost zero length. + //This should be very cheap and ought to hit nothing. + bodySpaceWheelCentreOffset=PxVec3(0,0,0); + maxDroop=1e-5f*gToleranceScaleLength; + maxBounce=1e-5f*gToleranceScaleLength; + radius=1e-5f*gToleranceScaleLength; + } + + PxVec3 suspLineStart; + PxVec3 suspLineDir; + computeSuspensionRaycast(carChassisTrnsfm,bodySpaceWheelCentreOffset,bodySpaceSuspTravelDir,radius,maxBounce,suspLineStart,suspLineDir); + + //Total length from top of wheel at max compression to bottom of wheel at max droop. + PxF32 suspLineLength=radius + maxBounce + maxDroop + radius; + //Add another radius on for good measure. + suspLineLength+=radius; + + //Store the susp line ray for later use. + PxVehicleWheels4DynData::SuspLineRaycast& raycast = + reinterpret_cast(wheels4DynData.mQueryOrCachedHitResults); + raycast.mStarts[j]=suspLineStart; + raycast.mDirs[j]=suspLineDir; + raycast.mLengths[j]=suspLineLength; + + //Add the raycast to the scene query. + batchQuery->raycast( + suspLineStart, suspLineDir, suspLineLength, 0, + PxHitFlag::ePOSITION|PxHitFlag::eNORMAL|PxHitFlag::eUV, carFilterData[j]); + } +} + +void PxVehicleUpdate::suspensionRaycasts(PxBatchQuery* batchQuery, const PxU32 numVehicles, PxVehicleWheels** vehicles, const PxU32 numSceneQueryResults, PxRaycastQueryResult* sceneQueryResults, const bool* vehiclesToRaycast) +{ + START_TIMER(TIMER_RAYCASTS); + + //Reset all hit counts to zero. + for(PxU32 i=0;i> 2); + const PxU32 numActiveWheels=veh.mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=numActiveWheels-4*numWheels4; + PxRigidDynamic* vehActor=veh.mActor; + + //Set the results pointer and start the raycasts. + PX_ASSERT(numActiveWheelsInLast4<4); + + //Blocks of 4 wheels. + for(PxU32 j=0;j= (sqres+4)) + { + carFilterData[0].data=wheels4SimData[j].getSceneQueryFilterData(0); + carFilterData[1].data=wheels4SimData[j].getSceneQueryFilterData(1); + carFilterData[2].data=wheels4SimData[j].getSceneQueryFilterData(2); + carFilterData[3].data=wheels4SimData[j].getSceneQueryFilterData(3); + wheels4DynData[j].mRaycastResults=sqres; + PxVehicleWheels4SuspensionRaycasts(batchQuery,wheels4SimData[j],wheels4DynData[j],carFilterData,activeWheelStates,4,vehActor); + } + else + { + PX_CHECK_MSG(false, "PxVehicleUpdate::suspensionRaycasts - numSceneQueryResults not big enough to support one raycast hit report per wheel. Increase size of sceneQueryResults"); + } + sqres+=4; + } + } + //Remainder that don't make up a block of 4. + if(numActiveWheelsInLast4>0) + { + const PxU32 j=numWheels4; + + bool activeWheelStates[4]={false,false,false,false}; + computeWheelActiveStates(4*j, veh.mWheelsSimData.mActiveWheelsBitmapBuffer, activeWheelStates); + + wheels4DynData[j].mRaycastResults=NULL; + wheels4DynData[j].mSweepResults=NULL; + + if(NULL==vehiclesToRaycast || vehiclesToRaycast[i]) + { + if((sceneQueryResults + numSceneQueryResults) >= (sqres+numActiveWheelsInLast4)) + { + if(0execute(); + + END_TIMER(TIMER_RAYCASTS); +} + +void physx::PxVehicleSuspensionRaycasts(PxBatchQuery* batchQuery, const PxU32 numVehicles, PxVehicleWheels** vehicles, const PxU32 numSceneQueryesults, PxRaycastQueryResult* sceneQueryResults, const bool* vehiclesToRaycast) +{ + PX_PROFILE_ZONE("PxVehicleSuspensionRaycasts::ePROFILE_RAYCASTS",0); + PxVehicleUpdate::suspensionRaycasts(batchQuery, numVehicles, vehicles, numSceneQueryesults, sceneQueryResults, vehiclesToRaycast); +} + + +void PxVehicleWheels4SuspensionSweeps +(PxBatchQuery* batchQuery, + const PxVehicleWheels4SimData& wheels4SimData, PxVehicleWheels4DynData& wheels4DynData, + const PxQueryFilterData* carFilterData, const bool* activeWheelStates, const PxU32 numActiveWheels, + const PxU16 nbHitsPerQuery, + const PxI32* wheelShapeIds, + PxRigidDynamic* vehActor, + const PxF32 sweepWidthScale, const PxF32 sweepRadiusScale) +{ + PX_UNUSED(sweepWidthScale); + PX_UNUSED(sweepRadiusScale); + + //Get the transform of the chassis. + PxTransform carChassisTrnsfm=vehActor->getGlobalPose().transform(vehActor->getCMassLocalPose()); + + //Add a raycast for each wheel. + for(PxU32 j=0;jgetShapes(&wheelShape, 1, PxU32(wheelShapeIds[j])); + + PxGeometryHolder suspGeometry; + if (PxGeometryType::eCONVEXMESH == wheelShape->getGeometryType()) + { + PxConvexMeshGeometry convMeshGeom; + wheelShape->getConvexMeshGeometry(convMeshGeom); + convMeshGeom.scale.scale = + PxVec3( + PxAbs(gRight.x*sweepWidthScale + (gUp.x + gForward.x)*sweepRadiusScale), + PxAbs(gRight.y*sweepWidthScale + (gUp.y + gForward.y)*sweepRadiusScale), + PxAbs(gRight.z*sweepWidthScale + (gUp.z + gForward.z)*sweepRadiusScale)); + suspGeometry.storeAny(convMeshGeom); + } + else if (PxGeometryType::eCAPSULE == wheelShape->getGeometryType()) + { + PxCapsuleGeometry capsuleGeom; + wheelShape->getCapsuleGeometry(capsuleGeom); + capsuleGeom.halfHeight *= sweepWidthScale; + capsuleGeom.radius *= sweepRadiusScale; + suspGeometry.storeAny(capsuleGeom); + } + else + { + PX_ASSERT(PxGeometryType::eSPHERE == wheelShape->getGeometryType()); + PxSphereGeometry sphereGeom; + wheelShape->getSphereGeometry(sphereGeom); + sphereGeom.radius *= sweepRadiusScale; + suspGeometry.storeAny(sphereGeom); + } + + const PxQuat wheelLocalPoseRotation = wheelShape->getLocalPose().q; + const PxF32 wheelTheta = wheels4DynData.mWheelRotationAngles[j]; + + const PxVec3& bodySpaceSuspTravelDir = wheels4SimData.getSuspTravelDirection(j); + PxVec3 bodySpaceWheelCentreOffset = wheels4SimData.getWheelCentreOffset(j); + PxF32 maxDroop = susp.mMaxDroop; + PxF32 maxBounce = susp.mMaxCompression; + PxF32 radius = wheel.mRadius; + PX_ASSERT(maxBounce >= 0); + PX_ASSERT(maxDroop >= 0); + + if(!activeWheelStates[j]) + { + //For disabled wheels just issue a raycast of almost zero length. + //This should be very cheap and ought to hit nothing. + bodySpaceWheelCentreOffset = PxVec3(0,0,0); + maxDroop = 1e-5f*gToleranceScaleLength; + maxBounce = 1e-5f*gToleranceScaleLength; + radius = 1e-5f*gToleranceScaleLength; + } + + PxTransform suspPoseStart; + PxVec3 suspLineDir; + computeSuspensionSweep( + carChassisTrnsfm, + wheelLocalPoseRotation, wheelTheta, + bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, radius, maxBounce, + suspPoseStart, suspLineDir); + const PxF32 suspLineLength = radius + maxBounce + maxDroop + radius; + + //Store the susp line ray for later use. + PxVehicleWheels4DynData::SuspLineSweep& sweep = + reinterpret_cast(wheels4DynData.mQueryOrCachedHitResults); + sweep.mStartPose[j] = suspPoseStart; + sweep.mDirs[j] = suspLineDir; + sweep.mLengths[j] = suspLineLength; + sweep.mGometries[j] = suspGeometry; + + //Add the raycast to the scene query. + batchQuery->sweep(sweep.mGometries[j].any(), + suspPoseStart, suspLineDir, suspLineLength, nbHitsPerQuery, + PxHitFlag::ePOSITION|PxHitFlag::eNORMAL|PxHitFlag::eUV, + carFilterData[j]); + } +} + + +void PxVehicleUpdate::suspensionSweeps +(PxBatchQuery* batchQuery, + const PxU32 numVehicles, PxVehicleWheels** vehicles, + const PxU32 numSceneQueryResults, PxSweepQueryResult* sceneQueryResults, const PxU16 nbHitsPerQuery, + const bool* vehiclesToSweep, + const PxF32 sweepWidthScale, const PxF32 sweepRadiusScale) +{ + PX_CHECK_MSG(sweepWidthScale > 0.0f, "PxVehicleUpdate::suspensionSweeps - sweepWidthScale must be greater than 0.0"); + PX_CHECK_MSG(sweepRadiusScale > 0.0f, "PxVehicleUpdate::suspensionSweeps - sweepRadiusScale must be greater than 0.0"); + + START_TIMER(TIMER_SWEEPS); + + //Reset all hit counts to zero. + for(PxU32 i=0;i> 2); + const PxU32 numActiveWheels=veh.mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=numActiveWheels-4*numWheels4; + PxRigidDynamic* vehActor=veh.mActor; + + //Set the results pointer and start the raycasts. + PX_ASSERT(numActiveWheelsInLast4<4); + + //Get the shape ids for the wheels. + PxI32 wheelShapeIds[PX_MAX_NB_WHEELS]; + PxMemSet(wheelShapeIds, 0xff, sizeof(PxI32)*PX_MAX_NB_WHEELS); + for(PxU32 j = 0; j < veh.mWheelsSimData.getNbWheels(); j++) + { + PX_CHECK_AND_RETURN(veh.mWheelsSimData.getWheelShapeMapping(j) != -1, "PxVehicleUpdate::suspensionSweeps - trying to sweep a shape that doesn't exist."); + wheelShapeIds[j] = veh.mWheelsSimData.getWheelShapeMapping(j); + } + + //Blocks of 4 wheels. + for(PxU32 j=0;j= (sqres+4)) + { + carFilterData[0].data=wheels4SimData[j].getSceneQueryFilterData(0); + carFilterData[1].data=wheels4SimData[j].getSceneQueryFilterData(1); + carFilterData[2].data=wheels4SimData[j].getSceneQueryFilterData(2); + carFilterData[3].data=wheels4SimData[j].getSceneQueryFilterData(3); + wheels4DynData[j].mSweepResults=sqres; + PxVehicleWheels4SuspensionSweeps( + batchQuery, + wheels4SimData[j], wheels4DynData[j], + carFilterData, activeWheelStates, 4, + nbHitsPerQuery, + wheelShapeIds4, + vehActor, + sweepWidthScale, sweepRadiusScale); + } + else + { + PX_CHECK_MSG(false, "PxVehicleUpdate::suspensionRaycasts - numSceneQueryResults not big enough to support one raycast hit report per wheel. Increase size of sceneQueryResults"); + } + sqres+=4; + } + } + //Remainder that don't make up a block of 4. + if(numActiveWheelsInLast4>0) + { + const PxU32 j=numWheels4; + + bool activeWheelStates[4]={false,false,false,false}; + computeWheelActiveStates(4*j, veh.mWheelsSimData.mActiveWheelsBitmapBuffer, activeWheelStates); + + const PxI32* wheelShapeIds4 = wheelShapeIds + 4*j; + + wheels4DynData[j].mRaycastResults=NULL; + wheels4DynData[j].mSweepResults=NULL; + + if(NULL==vehiclesToSweep || vehiclesToSweep[i]) + { + if((sceneQueryResults + numSceneQueryResults) >= (sqres+numActiveWheelsInLast4)) + { + if(0execute(); + + END_TIMER(TIMER_SWEEPS); +} + +namespace physx +{ + void PxVehicleSuspensionSweeps + (PxBatchQuery* batchQuery, + const PxU32 nbVehicles, PxVehicleWheels** vehicles, + const PxU32 nbSceneQueryResults, PxSweepQueryResult* sceneQueryResults, const PxU16 nbHitsPerQuery, + const bool* vehiclesToSweep, + const PxF32 sweepWidthScale, const PxF32 sweepRadiusScale) + { + PX_PROFILE_ZONE("PxVehicleSuspensionSweeps::ePROFILE_SWEEPS",0); + PxVehicleUpdate::suspensionSweeps( + batchQuery, nbVehicles, vehicles, nbSceneQueryResults, sceneQueryResults, nbHitsPerQuery, vehiclesToSweep, sweepWidthScale, sweepRadiusScale); + } +} + + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp new file mode 100644 index 000000000..1b2a67be3 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp @@ -0,0 +1,857 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleWheels.h" +#include "PxVehicleSuspWheelTire4.h" +#include "PxVehicleSuspLimitConstraintShader.h" +#include "PxVehicleDefaults.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" +#include "CmBitMap.h" +#include "PxPhysics.h" +#include "PsIntrinsics.h" +#include "PsFoundation.h" + +namespace physx +{ + +extern PxVec3 gRight; +extern PxVec3 gUp; +extern PxVec3 gForward; + +PxF32 gThresholdLongSpeed=5.0f; +PxU32 gLowLongSpeedSubstepCount=3; +PxU32 gHighLongSpeedSubstepCount=1; +PxF32 gMinLongSlipDenominator=4.0f; + +extern PxF32 gToleranceScaleLength; + + +PxU32 PxVehicleWheelsSimData::computeByteSize(const PxU32 numWheels) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + const PxU32 byteSize = sizeof(PxVehicleWheels4SimData)*numWheels4 + sizeof(PxVehicleAntiRollBarData)*2*numWheels4; + return byteSize; +} + +PxU8* PxVehicleWheelsSimData::patchUpPointers(const PxU32 numWheels, PxVehicleWheelsSimData* simData, PxU8* ptrIn) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + PxU8* ptr = ptrIn; + simData->mWheels4SimData = reinterpret_cast(ptr); + ptr += sizeof(PxVehicleWheels4SimData)*numWheels4; + simData->mAntiRollBars = reinterpret_cast(ptr); + ptr += sizeof(PxVehicleAntiRollBarData)*numWheels4*2; + PX_ASSERT((ptrIn + computeByteSize(numWheels)) == ptr); + return ptr; +} + +PxVehicleWheelsSimData::PxVehicleWheelsSimData(const PxU32 numWheels) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + + //Set numWheels + mNbWheels4 = numWheels4; + mNbActiveWheels = numWheels; + + //Set numAntiRollBars to zero. + mNbAntiRollBars4 = 2*numWheels4; + mNbActiveAntiRollBars = 0; + + //Placement new for wheels4 + for(PxU32 i=0;i> 5)); + for(PxU32 i=0;i(PX_ALLOC(byteSize, "PxVehicleWheelsSimData")); + + //Patchup pointers + PxU8* ptr = ptrStart; + PxVehicleWheelsSimData* simData = reinterpret_cast(ptr); + ptr += sizeof(PxVehicleWheelsSimData); + ptr = patchUpPointers(numWheels, simData, ptr); + PX_ASSERT((ptrStart+ byteSize) == ptr); + + //Constructor. + new(simData) PxVehicleWheelsSimData(numWheels); + + //Finished. + return simData; +} + +void PxVehicleWheelsSimData::setChassisMass(const PxF32 chassisMass) +{ + + //Target spring natural frequency = 9.66 + //Target spring damping ratio = 0.62 + const PxF32 mult=1.0f/(1.0f*mNbActiveWheels); + const PxF32 sprungMass=chassisMass*mult; + const PxF32 w0=9.66f; + const PxF32 r=0.62f; + for(PxU32 i=0;i> 5)); + + return *this; +} + +void PxVehicleWheelsSimData::copy(const PxVehicleWheelsSimData& src, const PxU32 srcWheel, const PxU32 wheel) +{ + PX_CHECK_AND_RETURN(srcWheel < src.mNbActiveWheels, "Illegal src wheel"); + PX_CHECK_AND_RETURN(wheel < mNbActiveWheels, "Illegal target wheel"); + + setSuspensionData(wheel,src.getSuspensionData(srcWheel)); + setWheelData(wheel,src.getWheelData(srcWheel)); + setTireData(wheel,src.getTireData(srcWheel)); + setSuspTravelDirection(wheel,src.getSuspTravelDirection(srcWheel)); + setSuspForceAppPointOffset(wheel,src.getSuspForceAppPointOffset(srcWheel)); + setTireForceAppPointOffset(wheel,src.getTireForceAppPointOffset(srcWheel)); + setWheelCentreOffset(wheel,src.getWheelCentreOffset(srcWheel)); + setWheelShapeMapping(wheel, src.getWheelShapeMapping(srcWheel)); + setSceneQueryFilterData(wheel, src.getSceneQueryFilterData(srcWheel)); + if(src.getIsWheelDisabled(srcWheel)) + disableWheel(wheel); + else + enableWheel(wheel); +} + +bool PxVehicleWheelsSimData::isValid() const +{ + for(PxU32 i=0;i> 5); + bm.reset(wheel); +} + +void PxVehicleWheelsSimData::enableWheel(const PxU32 wheel) +{ + PX_CHECK_AND_RETURN(wheel < 4*mNbWheels4, "PxVehicleWheelsSimData::disableWheel - Illegal wheel"); + + Cm::BitMap bm; + bm.setWords(mActiveWheelsBitmapBuffer, ((PX_MAX_NB_WHEELS + 31) & ~31) >> 5); + bm.set(wheel); +} + +bool PxVehicleWheelsSimData::getIsWheelDisabled(const PxU32 wheel) const +{ + PX_CHECK_AND_RETURN_VAL(wheel < 4*mNbWheels4, "PxVehicleWheelsSimData::getIsWheelDisabled - Illegal wheel", false); + Cm::BitMap bm; + bm.setWords(const_cast(mActiveWheelsBitmapBuffer), ((PX_MAX_NB_WHEELS + 31) & ~31) >> 5); + return (bm.test(wheel) ? false : true); +} + +const PxVehicleSuspensionData& PxVehicleWheelsSimData::getSuspensionData(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getSuspensionData - Illegal wheel"); + return mWheels4SimData[id>>2].getSuspensionData(id & 3); +} + +const PxVehicleWheelData& PxVehicleWheelsSimData::getWheelData(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getWheelData - Illegal wheel"); + return mWheels4SimData[id>>2].getWheelData(id & 3); +} + +const PxVehicleTireData& PxVehicleWheelsSimData::getTireData(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getTireData - Illegal wheel"); + return mWheels4SimData[id>>2].getTireData(id & 3); +} + +const PxVec3& PxVehicleWheelsSimData::getSuspTravelDirection(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getSuspTravelDirection - Illegal wheel"); + return mWheels4SimData[id>>2].getSuspTravelDirection(id & 3); +} + +const PxVec3& PxVehicleWheelsSimData::getSuspForceAppPointOffset(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getSuspForceAppPointOffset - Illegal wheel"); + return mWheels4SimData[id>>2].getSuspForceAppPointOffset(id & 3); +} + +const PxVec3& PxVehicleWheelsSimData::getTireForceAppPointOffset(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getTireForceAppPointOffset - Illegal wheel"); + return mWheels4SimData[id>>2].getTireForceAppPointOffset(id & 3); +} + +const PxVec3& PxVehicleWheelsSimData::getWheelCentreOffset(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getWheelCentreOffset - Illegal wheel"); + return mWheels4SimData[id>>2].getWheelCentreOffset(id & 3); +} + +PxI32 PxVehicleWheelsSimData::getWheelShapeMapping(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getWheelShapeMapping - Illegal wheel"); + return mWheels4SimData[id>>2].getWheelShapeMapping(id & 3); +} + +const PxFilterData& PxVehicleWheelsSimData::getSceneQueryFilterData(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getSceneQueryFilterData - Illegal wheel"); + return mWheels4SimData[id>>2].getSceneQueryFilterData(id & 3); +} + +const PxVehicleAntiRollBarData& PxVehicleWheelsSimData::getAntiRollBarData(const PxU32 antiRollId) const +{ + PX_CHECK_MSG(antiRollId < mNbActiveAntiRollBars, "PxVehicleWheelsSimData::getAntiRollBarData - Illegal anti-roll bar"); + return mAntiRollBars[antiRollId]; +} + +void PxVehicleWheelsSimData::setSuspensionData(const PxU32 id, const PxVehicleSuspensionData& susp) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setSuspensionData - Illegal wheel"); + mWheels4SimData[id>>2].setSuspensionData(id & 3, susp); +} + +void PxVehicleWheelsSimData::setWheelData(const PxU32 id, const PxVehicleWheelData& wheel) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setWheelData - Illegal wheel"); + mWheels4SimData[id>>2].setWheelData(id & 3, wheel); +} + +void PxVehicleWheelsSimData::setTireData(const PxU32 id, const PxVehicleTireData& tire) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setTireData - Illegal wheel"); + mWheels4SimData[id>>2].setTireData(id & 3, tire); +} + +void PxVehicleWheelsSimData::setSuspTravelDirection(const PxU32 id, const PxVec3& dir) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setSuspTravelDirection - Illegal wheel"); + mWheels4SimData[id>>2].setSuspTravelDirection(id & 3, dir); +} + +void PxVehicleWheelsSimData::setSuspForceAppPointOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setSuspForceAppPointOffset - Illegal wheel"); + mWheels4SimData[id>>2].setSuspForceAppPointOffset(id & 3, offset); +} + +void PxVehicleWheelsSimData::setTireForceAppPointOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setTireForceAppPointOffset - Illegal wheel"); + mWheels4SimData[id>>2].setTireForceAppPointOffset(id & 3, offset); +} + +void PxVehicleWheelsSimData::setWheelCentreOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setWheelCentreOffset - Illegal wheel"); + mWheels4SimData[id>>2].setWheelCentreOffset(id & 3, offset); +} + +void PxVehicleWheelsSimData::setWheelShapeMapping(const PxU32 id, const PxI32 shapeId) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setWheelShapeMapping - Illegal wheel"); + mWheels4SimData[id>>2].setWheelShapeMapping(id & 3, shapeId); +} + +void PxVehicleWheelsSimData::setSceneQueryFilterData(const PxU32 id, const PxFilterData& sqFilterData) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setSceneQueryFilterData - Illegal wheel"); + mWheels4SimData[id>>2].setSceneQueryFilterData(id & 3, sqFilterData); +} + +void PxVehicleWheelsSimData::setTireLoadFilterData(const PxVehicleTireLoadFilterData& tireLoadFilter) +{ + PX_CHECK_AND_RETURN(tireLoadFilter.mMaxNormalisedLoad>tireLoadFilter.mMinNormalisedLoad, "Illegal graph points"); + PX_CHECK_AND_RETURN(tireLoadFilter.mMaxFilteredNormalisedLoad>0, "Max filtered load must be greater than zero"); + mNormalisedLoadFilter=tireLoadFilter; + mNormalisedLoadFilter.mDenominator=1.0f/(mNormalisedLoadFilter.mMaxNormalisedLoad-mNormalisedLoadFilter.mMinNormalisedLoad); +} + +PxU32 PxVehicleWheelsSimData::addAntiRollBarData(const PxVehicleAntiRollBarData& antiRollBar) +{ + PX_CHECK_AND_RETURN_VAL(antiRollBar.isValid(), "Illegal antiRollBar", 0xffffffff) + PX_CHECK_AND_RETURN_VAL(antiRollBar.mWheel0 < mNbActiveWheels, "Illegal wheel0", 0xffffffff); + PX_CHECK_AND_RETURN_VAL(antiRollBar.mWheel1 < mNbActiveWheels, "Illegal wheel1", 0xffffffff); + + //If the anti-roll pair already exists then modify it. + for(PxU32 i = 0; i < mNbActiveAntiRollBars; i++) + { + if( ((mAntiRollBars[i].mWheel0 == antiRollBar.mWheel0) && (mAntiRollBars[i].mWheel1 == antiRollBar.mWheel1)) || + ((mAntiRollBars[i].mWheel1 == antiRollBar.mWheel0) && (mAntiRollBars[i].mWheel0 == antiRollBar.mWheel1)) ) + { + mAntiRollBars[i].mStiffness = antiRollBar.mStiffness; + return i; + } + } + + //Check we have space for an extra anti-roll bar. + PX_CHECK_AND_RETURN_VAL(mNbActiveAntiRollBars < 2*mNbWheels4, "The buffer of anti-roll bars is full", 0xffffffff); + + //Add a new anti-roll bar. + const PxU32 id = mNbActiveAntiRollBars; + mAntiRollBars[mNbActiveAntiRollBars] = antiRollBar; + mNbActiveAntiRollBars++; + + //Finished. + return id; +} + +//Only used for serialization. +void PxVehicleWheelsSimData::setAntiRollBarData(const PxU32 id, const PxVehicleAntiRollBarData& antiRoll) +{ + PX_UNUSED(id); + addAntiRollBarData(antiRoll); +} + +void PxVehicleWheelsSimData::setSubStepCount(const PxReal thresholdLongitudinalSpeed, const PxU32 lowForwardSpeedSubStepCount, const PxU32 highForwardSpeedSubStepCount) +{ + PX_CHECK_AND_RETURN(thresholdLongitudinalSpeed>=0, "thresholdLongitudinalSpeed must be greater than or equal to zero."); + PX_CHECK_AND_RETURN(lowForwardSpeedSubStepCount>0, "lowForwardSpeedSubStepCount must be greater than zero."); + PX_CHECK_AND_RETURN(highForwardSpeedSubStepCount>0, "highForwardSpeedSubStepCount must be greater than zero."); + + mThresholdLongitudinalSpeed=thresholdLongitudinalSpeed; + mLowForwardSpeedSubStepCount=lowForwardSpeedSubStepCount; + mHighForwardSpeedSubStepCount=highForwardSpeedSubStepCount; +} + +void PxVehicleWheelsSimData::setMinLongSlipDenominator(const PxReal minLongSlipDenominator) +{ + PX_CHECK_AND_RETURN(minLongSlipDenominator>0, "minLongSlipDenominator must be greater than or equal to zero."); + mMinLongSlipDenominator=minLongSlipDenominator; +} + + + +///////////////////////////// + +PxU32 PxVehicleWheelsDynData::computeByteSize(const PxU32 numWheels) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + const PxU32 byteSize = + sizeof(PxVehicleWheels4DynData)*numWheels4 + + sizeof(PxVehicleTireForceCalculator) + sizeof(void*)*4*numWheels4 + + sizeof(void*)*4*numWheels4 + + sizeof(PxVehicleConstraintShader)*numWheels4; + return byteSize; +} + +PxU8* PxVehicleWheelsDynData::patchUpPointers(const PxU32 numWheels, PxVehicleWheelsDynData* dynData, PxU8* ptrIn) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + PxU8* ptr = ptrIn; + dynData->mWheels4DynData = reinterpret_cast(ptr); + ptr += sizeof(PxVehicleWheels4DynData)*numWheels4; + dynData->mTireForceCalculators = reinterpret_cast(ptr); + ptr += sizeof(PxVehicleTireForceCalculator); + dynData->mTireForceCalculators->mShaderData = reinterpret_cast(ptr); + ptr += sizeof(void*)*4*numWheels4; + dynData->mUserDatas = reinterpret_cast(ptr); + ptr += sizeof(void*)*4*numWheels4; + for(PxU32 i=0;imWheels4DynData[i].setVehicleConstraintShader(shader); + ptr += sizeof(PxVehicleConstraintShader); + } + + PX_ASSERT((ptrIn + computeByteSize(numWheels)) == ptr); + return ptr; +} + +PxVehicleWheelsDynData::PxVehicleWheelsDynData(const PxU32 numWheels) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + + mNbWheels4=numWheels4; + mNbActiveWheels=numWheels; + + //Placement new for wheels4 + for(PxU32 i=0;imShaderData[i]=NULL; + } + new(mTireForceCalculators) PxVehicleTireForceCalculator; + + //Initialise user data + for(PxU32 i=0;i<4*numWheels4;i++) + { + mUserDatas[i]=NULL; + } +} + +bool PxVehicleWheelsDynData::isValid() const +{ + for(PxU32 i=0;imShader=tireForceShaderFn; +} + +void PxVehicleWheelsDynData::setTireForceShaderData(const PxU32 tireId, const void* tireForceShaderData) +{ + PX_CHECK_AND_RETURN(tireId < mNbActiveWheels, "PxVehicleWheelsDynData::setTireForceShaderData - Illegal tire"); + mTireForceCalculators->mShaderData[tireId]=tireForceShaderData; +} + +const void* PxVehicleWheelsDynData::getTireForceShaderData(const PxU32 tireId) const +{ + PX_CHECK_AND_RETURN_VAL(tireId < mNbActiveWheels, "PxVehicleWheelsDynData::getTireForceShaderData - Illegal tire", NULL); + return mTireForceCalculators->mShaderData[tireId]; +} + +void PxVehicleWheelsDynData::setWheelRotationSpeed(const PxU32 wheelIdx, const PxReal speed) +{ + PX_CHECK_AND_RETURN(wheelIdx < mNbActiveWheels, "PxVehicleWheelsDynData::setWheelRotationSpeed - Illegal wheel"); + PxVehicleWheels4DynData& suspWheelTire4=mWheels4DynData[(wheelIdx>>2)]; + suspWheelTire4.mWheelSpeeds[wheelIdx & 3] = speed; + suspWheelTire4.mCorrectedWheelSpeeds[wheelIdx & 3] = speed; +} + +PxReal PxVehicleWheelsDynData::getWheelRotationSpeed(const PxU32 wheelIdx) const +{ + PX_CHECK_AND_RETURN_VAL(wheelIdx < mNbActiveWheels, "PxVehicleWheelsDynData::getWheelRotationSpeed - Illegal wheel", 0.0f); + const PxVehicleWheels4DynData& suspWheelTire4=mWheels4DynData[(wheelIdx>>2)]; + return suspWheelTire4.mCorrectedWheelSpeeds[wheelIdx & 3]; +} + +void PxVehicleWheelsDynData::setWheelRotationAngle(const PxU32 wheelIdx, const PxReal angle) +{ + PX_CHECK_AND_RETURN(wheelIdx < mNbActiveWheels, "PxVehicleWheelsDynData::setWheelRotationAngle - Illegal wheel"); + PxVehicleWheels4DynData& suspWheelTire4=mWheels4DynData[(wheelIdx>>2)]; + suspWheelTire4.mWheelRotationAngles[wheelIdx & 3] = angle; +} + +PxReal PxVehicleWheelsDynData::getWheelRotationAngle(const PxU32 wheelIdx) const +{ + PX_CHECK_AND_RETURN_VAL(wheelIdx < mNbActiveWheels, "PxVehicleWheelsDynData::getWheelRotationAngle - Illegal wheel", 0.0f); + const PxVehicleWheels4DynData& suspWheelTire4=mWheels4DynData[(wheelIdx>>2)]; + return suspWheelTire4.mWheelRotationAngles[wheelIdx & 3]; +} + +void PxVehicleWheels::setToRestState() +{ + //Set the rigid body to rest and clear all the accumulated forces and impulses. + if(!(mActor->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC)) + { + mActor->setLinearVelocity(PxVec3(0,0,0)); + mActor->setAngularVelocity(PxVec3(0,0,0)); + mActor->clearForce(PxForceMode::eACCELERATION); + mActor->clearForce(PxForceMode::eVELOCITY_CHANGE); + mActor->clearTorque(PxForceMode::eACCELERATION); + mActor->clearTorque(PxForceMode::eVELOCITY_CHANGE); + } + + //Set the wheels to rest state. + mWheelsDynData.setToRestState(); +} + +bool PxVehicleWheels::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mWheelsSimData.isValid(), "invalid mWheelsSimData", false); + PX_CHECK_AND_RETURN_VAL(mWheelsDynData.isValid(), "invalid mWheelsDynData", false); + return true; +} + +PxU32 PxVehicleWheels::computeByteSize(const PxU32 numWheels) +{ + const PxU32 byteSize = + PxVehicleWheelsSimData::computeByteSize(numWheels) + + PxVehicleWheelsDynData::computeByteSize(numWheels); + return byteSize; +} + +PxU8* PxVehicleWheels::patchupPointers(const PxU32 numWheels, PxVehicleWheels* vehWheels, PxU8* ptrIn) +{ + PxU8* ptr = ptrIn; + ptr = PxVehicleWheelsSimData::patchUpPointers(numWheels, &vehWheels->mWheelsSimData, ptr); + ptr = PxVehicleWheelsDynData::patchUpPointers(numWheels, &vehWheels->mWheelsDynData, ptr); + PX_ASSERT((ptrIn + computeByteSize(numWheels)) == ptr); + return ptr; +} + +void PxVehicleWheels::init(const PxU32 numWheels) +{ + new(&mWheelsSimData) PxVehicleWheelsSimData(numWheels); + new(&mWheelsDynData) PxVehicleWheelsDynData(numWheels); + + for(PxU32 i = 0; i < mWheelsSimData.mNbWheels4; i++) + { + new(&mWheelsDynData.mWheels4DynData[i].getVehicletConstraintShader()) PxVehicleConstraintShader(this); + } + + mOnConstraintReleaseCounter = Ps::to8(mWheelsSimData.mNbWheels4); +} + +void PxVehicleWheels::free() +{ + PX_CHECK_AND_RETURN(mWheelsSimData.mNbWheels4>0, "Cars with zero wheels are illegal"); + + const PxU32 numSuspWheelTire4 = mWheelsSimData.mNbWheels4; + + for(PxU32 i=0;igetMass()-totalSprungMass)/vehActor->getMass()) < 0.01f, "Sum of suspension sprung masses doesn't match actor mass"); +#endif + + //Copy the simulation data. + mWheelsSimData=wheelsData; + + //Set the actor pointer. + mActor=vehActor; + + //Set all the sq result ptrs to null. + const PxU32 numSuspWheelTire4=wheelsData.mNbWheels4; + for(PxU32 i=0;icreateConstraint(vehActor, NULL, shader, t, sizeof(PxVehicleConstraintShader::VehicleConstraintData)); + shader.mConstraint->markDirty(); + } + + //Set up the shader data ptrs. + for(PxU32 i=0;igetNbShapes(), "Illegal wheel shape mapping, shape does not exist on actor"); + + //Compute the shape local pose + const PxTransform chassisCMOffset=mActor->getCMassLocalPose(); + PxTransform wheelOffset=chassisCMOffset; + wheelOffset.p+=mWheelsSimData.getWheelCentreOffset(i); + //Pose the shape. + PxShape* shapeBuffer[1]; + mActor->getShapes(shapeBuffer,1,PxU32(shapeId)); + shapeBuffer[0]->setLocalPose(wheelOffset); + } + } +} + +void PxVehicleWheels::requiresObjects(PxProcessPxBaseCallback& c) +{ + c.process(*mActor); + + for(PxU32 i=0;i(old); + new_nx->setConstraintFunctions(*connector, shaders); + return new_nx; +} + +void PxVehicleWheels::resolveReferences(PxDeserializationContext& context) +{ + context.translatePxBase(mActor); + + for(PxU32 i=0;igetGlobalPose().transform(mActor->getCMassLocalPose()); + return mActor->getLinearVelocity().dot(vehicleChassisTrnsfm.q.rotate(gForward)); +} + +PxReal PxVehicleWheels::computeSidewaysSpeed() const +{ + const PxTransform vehicleChassisTrnsfm=mActor->getGlobalPose().transform(mActor->getCMassLocalPose()); + return mActor->getLinearVelocity().dot(vehicleChassisTrnsfm.q.rotate(gRight)); +} + +//////////////////////////////////////////////////////////////////////////// + +void PxVehicleWheelsDynData::setUserData(const PxU32 tireIdx, void* userData) +{ + PX_CHECK_AND_RETURN(tireIdx < mNbActiveWheels, "PxVehicleWheelsDynData::setUserData - Illegal wheel"); + mUserDatas[tireIdx]=userData; +} + +void* PxVehicleWheelsDynData::getUserData(const PxU32 tireIdx) const +{ + PX_CHECK_AND_RETURN_VAL(tireIdx < mNbActiveWheels, "PxVehicleWheelsDynData::setUserData - Illegal wheel", NULL); + return mUserDatas[tireIdx]; +} + + +//////////////////////////////////////////////////////////////////////////// + +void PxVehicleWheelsDynData::copy(const PxVehicleWheelsDynData& src, const PxU32 srcWheel, const PxU32 trgWheel) +{ + PX_CHECK_AND_RETURN(srcWheel < src.mNbActiveWheels, "PxVehicleWheelsDynData::copy - Illegal src wheel"); + PX_CHECK_AND_RETURN(trgWheel < mNbActiveWheels, "PxVehicleWheelsDynData::copy - Illegal trg wheel"); + + const PxVehicleWheels4DynData& src4 = src.mWheels4DynData[(srcWheel>>2)]; + PxVehicleWheels4DynData& trg4 = mWheels4DynData[(trgWheel>>2)]; + + trg4.mWheelSpeeds[trgWheel & 3] = src4.mWheelSpeeds[srcWheel & 3]; + trg4.mCorrectedWheelSpeeds[trgWheel & 3] = src4.mCorrectedWheelSpeeds[srcWheel & 3]; + trg4.mTireLowForwardSpeedTimers[trgWheel & 3] = src4.mTireLowForwardSpeedTimers[srcWheel & 3]; + trg4.mTireLowSideSpeedTimers[trgWheel & 3] = src4.mTireLowSideSpeedTimers[srcWheel & 3]; + trg4.mWheelRotationAngles[trgWheel & 3] = src4.mWheelRotationAngles[srcWheel & 3]; + + if(src4.mRaycastResults) + { + const PxVehicleWheels4DynData::SuspLineRaycast& suspLineRaycastSrc = reinterpret_cast(src4.mQueryOrCachedHitResults); + PxVehicleWheels4DynData::SuspLineRaycast& suspLineRaycastTrg = reinterpret_cast(trg4.mQueryOrCachedHitResults); + + suspLineRaycastTrg.mStarts[trgWheel & 3] = suspLineRaycastSrc.mStarts[srcWheel & 3]; + suspLineRaycastTrg.mDirs[trgWheel & 3] = suspLineRaycastSrc.mDirs[srcWheel & 3]; + suspLineRaycastTrg.mLengths[trgWheel & 3] = suspLineRaycastSrc.mLengths[srcWheel & 3]; + } + else if(src4.mSweepResults) + { + const PxVehicleWheels4DynData::SuspLineSweep& suspLineSweepSrc = reinterpret_cast(src4.mQueryOrCachedHitResults); + PxVehicleWheels4DynData::SuspLineSweep& suspLineSweepTrg = reinterpret_cast(trg4.mQueryOrCachedHitResults); + + suspLineSweepTrg.mStartPose[trgWheel & 3] = suspLineSweepSrc.mStartPose[srcWheel & 3]; + suspLineSweepTrg.mDirs[trgWheel & 3] = suspLineSweepSrc.mDirs[srcWheel & 3]; + suspLineSweepTrg.mLengths[trgWheel & 3] = suspLineSweepSrc.mLengths[srcWheel & 3]; + } + else + { + const PxVehicleWheels4DynData::CachedSuspLineSceneQuerytHitResult& cachedHitResultSrc = reinterpret_cast(src4.mQueryOrCachedHitResults); + PxVehicleWheels4DynData::CachedSuspLineSceneQuerytHitResult& cachedHitResultTrg = reinterpret_cast(trg4.mQueryOrCachedHitResults); + + cachedHitResultTrg.mPlanes[trgWheel & 3] = cachedHitResultSrc.mPlanes[srcWheel & 3]; + cachedHitResultTrg.mFrictionMultipliers[trgWheel & 3] = cachedHitResultSrc.mFrictionMultipliers[srcWheel & 3]; + cachedHitResultTrg.mCounts[trgWheel & 3] = cachedHitResultSrc.mCounts[srcWheel & 3]; + cachedHitResultTrg.mDistances[trgWheel & 3] = cachedHitResultSrc.mDistances[srcWheel & 3]; + } +} + + +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp new file mode 100644 index 000000000..380ae49b8 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp @@ -0,0 +1,474 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleUtilControl.h" +#include "PxVehicleDrive4W.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +#if PX_CHECKED +void testValidAnalogValue(const PxF32 actualValue, const PxF32 minVal, const PxF32 maxVal, const char* errorString) +{ + const PxF32 tolerance = 1e-2f; + PX_CHECK_MSG((actualValue > (minVal - tolerance)) && (actualValue < (maxVal + tolerance)), errorString); +} +#endif + + +PxF32 processDigitalValue +(const PxU32 inputType, + const PxVehicleKeySmoothingData& keySmoothing, const bool digitalValue, + const PxF32 timestep, + const PxF32 analogVal) +{ + PxF32 newAnalogVal=analogVal; + if(digitalValue) + { + newAnalogVal+=keySmoothing.mRiseRates[inputType]*timestep; + } + else + { + newAnalogVal-=keySmoothing.mFallRates[inputType]*timestep; + } + + return PxClamp(newAnalogVal,0.0f,1.0f); +} + +void PxVehicleDriveSmoothDigitalRawInputsAndSetAnalogInputs +(const PxVehicleKeySmoothingData& keySmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxF32 timestep, + const bool isVehicleInAir, + const PxVehicleWheels& vehicle, PxVehicleDriveDynData& driveDynData) +{ + const bool gearup=rawInputData.getGearUp(); + const bool geardown=rawInputData.getGearDown(); + driveDynData.setGearDown(geardown); + driveDynData.setGearUp(gearup); + + const PxF32 accel=processDigitalValue(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL,keySmoothing,rawInputData.getDigitalAccel(),timestep,driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL)); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL,accel); + + const PxF32 brake=processDigitalValue(PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE,keySmoothing,rawInputData.getDigitalBrake(),timestep,driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE)); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE,brake); + + const PxF32 handbrake=processDigitalValue(PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE,keySmoothing,rawInputData.getDigitalHandbrake(),timestep,driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE)); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE,handbrake); + + PxF32 steerLeft=processDigitalValue(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT,keySmoothing,rawInputData.getDigitalSteerLeft(),timestep,driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT)); + PxF32 steerRight=processDigitalValue(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT,keySmoothing,rawInputData.getDigitalSteerRight(),timestep,driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT)); + const PxF32 vz=vehicle.computeForwardSpeed(); + const PxF32 vzAbs=PxAbs(vz); + const PxF32 maxSteer=(isVehicleInAir ? 1.0f :steerVsForwardSpeedTable.getYVal(vzAbs)); + const PxF32 steer=PxAbs(steerRight-steerLeft); + if(steer>maxSteer) + { + const PxF32 k=maxSteer/steer; + steerLeft*=k; + steerRight*=k; + } + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT, steerLeft); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT, steerRight); +} + +////////////////////////////////// + +//process value in range(0,1) +PX_FORCE_INLINE PxF32 processPositiveAnalogValue +(const PxF32 riseRate, const PxF32 fallRate, + const PxF32 currentVal, const PxF32 targetVal, + const PxF32 timestep) +{ + PX_ASSERT(targetVal>=-0.01f && targetVal<=1.01f); + PxF32 val; + if(currentVal0) + { + val=currentVal-fallRate*timestep; + val=PxMax(val,0.0f); + } + else if(currentVal<0) + { + val=currentVal+fallRate*timestep; + val=PxMin(val,0.0f); + } + } + else + { + if(currentVal < targetVal) + { + if(currentVal<0) + { + val=currentVal + fallRate*timestep; + val=PxMin(val,targetVal); + } + else + { + val=currentVal + riseRate*timestep; + val=PxMin(val,targetVal); + } + } + else + { + if(currentVal>0) + { + val=currentVal - fallRate*timestep; + val=PxMax(val,targetVal); + } + else + { + val=currentVal - riseRate*timestep; + val=PxMax(val,targetVal); + } + } + } + return val; +} + +void PxVehicleDriveSmoothAnalogRawInputsAndSetAnalogInputs +(const PxVehiclePadSmoothingData& padSmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxF32 timestep, + const bool isVehicleInAir, + const PxVehicleWheels& vehicle, PxVehicleDriveDynData& driveDynData) +{ + //gearup/geardown + const bool gearup=rawInputData.getGearUp(); + const bool geardown=rawInputData.getGearDown(); + driveDynData.setGearUp(gearup); + driveDynData.setGearDown(geardown); + + //Update analog inputs for focus vehicle. + + //Process the accel. + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]; + const PxF32 currentVal=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL); + const PxF32 targetVal=rawInputData.getAnalogAccel(); + const PxF32 accel=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL, accel); + } + + //Process the brake + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE]; + const PxF32 currentVal=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE); + const PxF32 targetVal=rawInputData.getAnalogBrake(); + const PxF32 brake=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE, brake); + } + + //Process the handbrake. + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE]; + const PxF32 currentVal=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE); + const PxF32 targetVal=rawInputData.getAnalogHandbrake(); + const PxF32 handbrake=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE, handbrake); + } + + //Process the steer + { + const PxF32 vz=vehicle.computeForwardSpeed(); + const PxF32 vzAbs=PxAbs(vz); + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]; + const PxF32 currentVal=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT)-driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT); + const PxF32 targetVal=rawInputData.getAnalogSteer()*(isVehicleInAir ? 1.0f :steerVsForwardSpeedTable.getYVal(vzAbs)); + const PxF32 steer=processAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT, 0.0f); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT, steer); + } +} + + +//////////////// + +void PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs +(const PxVehicleKeySmoothingData& keySmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxF32 timestep, + const bool isVehicleInAir, + PxVehicleDrive4W& focusVehicle) +{ + PxVehicleDriveSmoothDigitalRawInputsAndSetAnalogInputs + (keySmoothing, steerVsForwardSpeedTable, rawInputData, timestep, isVehicleInAir, focusVehicle, focusVehicle.mDriveDynData); +} + +void PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs +(const PxVehiclePadSmoothingData& padSmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxF32 timestep, + const bool isVehicleInAir, + PxVehicleDrive4W& focusVehicle) +{ + PxVehicleDriveSmoothAnalogRawInputsAndSetAnalogInputs + (padSmoothing,steerVsForwardSpeedTable,rawInputData,timestep,isVehicleInAir,focusVehicle,focusVehicle.mDriveDynData); +} + +//////////////// + +void PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs +(const PxVehicleKeySmoothingData& keySmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDriveNWRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDriveNW& focusVehicle) +{ + PxVehicleDriveSmoothDigitalRawInputsAndSetAnalogInputs + (keySmoothing,steerVsForwardSpeedTable,rawInputData,timestep,isVehicleInAir,focusVehicle,focusVehicle.mDriveDynData); +} + +void PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs +(const PxVehiclePadSmoothingData& padSmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDriveNWRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDriveNW& focusVehicle) +{ + PxVehicleDriveSmoothAnalogRawInputsAndSetAnalogInputs + (padSmoothing,steerVsForwardSpeedTable,rawInputData,timestep,isVehicleInAir,focusVehicle,focusVehicle.mDriveDynData); +} + +//////////////// + +void PxVehicleDriveTankSmoothAnalogRawInputsAndSetAnalogInputs +(const PxVehiclePadSmoothingData& padSmoothing, + const PxVehicleDriveTankRawInputData& rawInputData, + const PxReal timestep, + PxVehicleDriveTank& focusVehicle) +{ + //Process the gearup/geardown buttons. + const bool gearup=rawInputData.getGearUp(); + const bool geardown=rawInputData.getGearDown(); + focusVehicle.mDriveDynData.setGearUp(gearup); + focusVehicle.mDriveDynData.setGearDown(geardown); + + //Process the accel. + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL); + const PxF32 targetVal=rawInputData.getAnalogAccel(); + const PxF32 accel=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL, accel); + } + + PX_ASSERT(focusVehicle.getDriveModel()==rawInputData.getDriveModel()); + switch(rawInputData.getDriveModel()) + { + case PxVehicleDriveTankControlModel::eSPECIAL: + { + //Process the left brake. + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT); + const PxF32 targetVal=rawInputData.getAnalogLeftBrake(); + const PxF32 accel=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, accel); + } + + //Process the right brake. + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT); + const PxF32 targetVal=rawInputData.getAnalogRightBrake(); + const PxF32 accel=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, accel); + } + + //Left thrust + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT); + const PxF32 targetVal=rawInputData.getAnalogLeftThrust(); + const PxF32 val=processAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, val); + } + + //Right thrust + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT); + const PxF32 targetVal=rawInputData.getAnalogRightThrust(); + const PxF32 val=processAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, val); + } + } + break; + + case PxVehicleDriveTankControlModel::eSTANDARD: + { + //Right thrust + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT); + const PxF32 targetVal=rawInputData.getAnalogRightThrust()-rawInputData.getAnalogRightBrake(); + const PxF32 val=processAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + if(val>0) + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, val); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, 0.0f); + } + else + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, 0.0f); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, -val); + } + } + + //Left thrust + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT); + const PxF32 targetVal=rawInputData.getAnalogLeftThrust()-rawInputData.getAnalogLeftBrake(); + const PxF32 val=processAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + if(val>0) + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, val); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, 0.0f); + } + else + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, 0.0f); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, -val); + } + } + } + break; + } +} + +void PxVehicleDriveTankSmoothDigitalRawInputsAndSetAnalogInputs +(const PxVehicleKeySmoothingData& keySmoothing, + const PxVehicleDriveTankRawInputData& rawInputData, + const PxF32 timestep, + PxVehicleDriveTank& focusVehicle) +{ + PxF32 val; + val=processDigitalValue(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL,keySmoothing,rawInputData.getDigitalAccel(),timestep,focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL)); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL, val); + val=processDigitalValue(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT,keySmoothing,rawInputData.getDigitalLeftThrust(),timestep,focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT)); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, val); + val=processDigitalValue(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT,keySmoothing,rawInputData.getDigitalRightThrust(),timestep,focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT)); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, val); + val=processDigitalValue(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT,keySmoothing,rawInputData.getDigitalLeftBrake(),timestep,focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT)); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, val); + val=processDigitalValue(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT,keySmoothing,rawInputData.getDigitalRightBrake(),timestep,focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT)); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, val); + + //Update digital inputs for focus vehicle. + focusVehicle.mDriveDynData.setGearUp(rawInputData.getGearUp()); + focusVehicle.mDriveDynData.setGearDown(rawInputData.getGearDown()); + + switch(rawInputData.getDriveModel()) + { + case PxVehicleDriveTankControlModel::eSPECIAL: + { + const PxF32 thrustL=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, thrustL); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, 0.0f); + + const PxF32 thrustR=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, thrustR); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, 0.0f); + } + break; + case PxVehicleDriveTankControlModel::eSTANDARD: + { + const PxF32 thrustL=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT); + if(thrustL>0) + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, thrustL); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, 0.0f); + } + else + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, 0.0f); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, -thrustL); + } + + const PxF32 thrustR=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT); + if(thrustR>0) + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, thrustR); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, 0.0f); + } + else + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, 0.0f); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, -thrustR); + } + } + break; + } + +} + + + +} //physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp new file mode 100644 index 000000000..32604c924 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp @@ -0,0 +1,246 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMath.h" +#include "PxVehicleUtilSetup.h" +#include "PxVehicleDrive4W.h" +#include "PxVehicleDriveNW.h" +#include "PxVehicleDriveTank.h" +#include "PxVehicleNoDrive.h" +#include "PxVehicleWheels.h" +#include "PxVehicleUtil.h" +#include "PxVehicleUpdate.h" +#include "PsFoundation.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +void enable3WMode(const PxU32 rightDirection, const PxU32 upDirection, const bool removeFrontWheel, PxVehicleWheelsSimData& wheelsSimData, PxVehicleWheelsDynData& wheelsDynData, PxVehicleDriveSimData4W& driveSimData); + +void computeDirection(PxU32& rightDirection, PxU32& upDirection); + +void PxVehicle4WEnable3WTadpoleMode(PxVehicleWheelsSimData& wheelsSimData, PxVehicleWheelsDynData& wheelsDynData, PxVehicleDriveSimData4W& driveSimData) +{ + PX_CHECK_AND_RETURN + (!wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eFRONT_LEFT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eREAR_LEFT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eREAR_RIGHT), "PxVehicle4WEnable3WTadpoleMode requires no wheels to be disabled"); + + PxU32 rightDirection=0xffffffff; + PxU32 upDirection=0xffffffff; + computeDirection(rightDirection, upDirection); + PX_CHECK_AND_RETURN(rightDirection<3 && upDirection<3, "PxVehicle4WEnable3WTadpoleMode requires the vectors set in PxVehicleSetBasisVectors to be axis-aligned"); + + enable3WMode(rightDirection, upDirection, false, wheelsSimData, wheelsDynData, driveSimData); +} + +void PxVehicle4WEnable3WDeltaMode(PxVehicleWheelsSimData& wheelsSimData, PxVehicleWheelsDynData& wheelsDynData, PxVehicleDriveSimData4W& driveSimData) +{ + PX_CHECK_AND_RETURN + (!wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eFRONT_LEFT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eREAR_LEFT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eREAR_RIGHT), "PxVehicle4WEnable3WDeltaMode requires no wheels to be disabled"); + + PxU32 rightDirection=0xffffffff; + PxU32 upDirection=0xffffffff; + computeDirection(rightDirection, upDirection); + PX_CHECK_AND_RETURN(rightDirection<3 && upDirection<3, "PxVehicle4WEnable3WTadpoleMode requires the vectors set in PxVehicleSetBasisVectors to be axis-aligned"); + + enable3WMode(rightDirection, upDirection, true, wheelsSimData, wheelsDynData, driveSimData); +} + +void computeSprungMasses(const PxU32 numSprungMasses, const PxVec3* sprungMassCoordinates, const PxVec3& centreOfMass, const PxReal totalMass, const PxU32 gravityDirection, PxReal* sprungMasses); + +void PxVehicleComputeSprungMasses(const PxU32 numSprungMasses, const PxVec3* sprungMassCoordinates, const PxVec3& centreOfMass, const PxReal totalMass, const PxU32 gravityDirection, PxReal* sprungMasses) +{ + computeSprungMasses(numSprungMasses, sprungMassCoordinates, centreOfMass, totalMass, gravityDirection, sprungMasses); +} + +void PxVehicleCopyDynamicsData(const PxVehicleCopyDynamicsMap& wheelMap, const PxVehicleWheels& src, PxVehicleWheels* trg) +{ + PX_CHECK_AND_RETURN(trg, "PxVehicleCopyDynamicsData requires that trg is a valid vehicle pointer"); + + PX_CHECK_AND_RETURN(src.getVehicleType() == trg->getVehicleType(), "PxVehicleCopyDynamicsData requires that both src and trg are the same type of vehicle"); + +#if PX_CHECKED + { + const PxU32 numWheelsSrc = src.mWheelsSimData.getNbWheels(); + const PxU32 numWheelsTrg = trg->mWheelsSimData.getNbWheels(); + PxU8 copiedWheelsSrc[PX_MAX_NB_WHEELS]; + PxMemZero(copiedWheelsSrc, sizeof(PxU8) * PX_MAX_NB_WHEELS); + PxU8 setWheelsTrg[PX_MAX_NB_WHEELS]; + PxMemZero(setWheelsTrg, sizeof(PxU8) * PX_MAX_NB_WHEELS); + for(PxU32 i = 0; i < PxMin(numWheelsSrc, numWheelsTrg); i++) + { + const PxU32 srcWheelId = wheelMap.sourceWheelIds[i]; + PX_CHECK_AND_RETURN(srcWheelId < numWheelsSrc, "PxVehicleCopyDynamicsData - wheelMap contains illegal source wheel id"); + PX_CHECK_AND_RETURN(0 == copiedWheelsSrc[srcWheelId], "PxVehicleCopyDynamicsData - wheelMap contains illegal source wheel id"); + copiedWheelsSrc[srcWheelId] = 1; + + const PxU32 trgWheelId = wheelMap.targetWheelIds[i]; + PX_CHECK_AND_RETURN(trgWheelId < numWheelsTrg, "PxVehicleCopyDynamicsData - wheelMap contains illegal target wheel id"); + PX_CHECK_AND_RETURN(0 == setWheelsTrg[trgWheelId], "PxVehicleCopyDynamicsData - wheelMap contains illegal target wheel id"); + setWheelsTrg[trgWheelId]=1; + } + } +#endif + + + const PxU32 numWheelsSrc = src.mWheelsSimData.getNbWheels(); + const PxU32 numWheelsTrg = trg->mWheelsSimData.getNbWheels(); + + //Set all dynamics data on the target to zero. + //Be aware that setToRestState sets the rigid body to + //rest so set the momentum back after calling setToRestState. + PxRigidDynamic* actorTrg = trg->getRigidDynamicActor(); + PxVec3 linVel = actorTrg->getLinearVelocity(); + PxVec3 angVel = actorTrg->getAngularVelocity(); + switch(src.getVehicleType()) + { + case PxVehicleTypes::eDRIVE4W: + static_cast(trg)->setToRestState(); + break; + case PxVehicleTypes::eDRIVENW: + static_cast(trg)->setToRestState(); + break; + case PxVehicleTypes::eDRIVETANK: + static_cast(trg)->setToRestState(); + break; + case PxVehicleTypes::eNODRIVE: + static_cast(trg)->setToRestState(); + break; + default: + break; + } + actorTrg->setLinearVelocity(linVel); + actorTrg->setAngularVelocity(angVel); + + + //Keep a track of the wheels on trg that have their dynamics data set as a copy from src. + PxU8 setWheelsTrg[PX_MAX_NB_WHEELS]; + PxMemZero(setWheelsTrg, sizeof(PxU8) * PX_MAX_NB_WHEELS); + + //Keep a track of the average wheel rotation speed of all enabled wheels on src. + PxU32 numEnabledWheelsSrc = 0; + PxF32 accumulatedWheelRotationSpeedSrc = 0.0f; + + //Copy wheel dynamics data from src wheels to trg wheels. + //Track the target wheels that have been given dynamics data from src wheels. + //Compute the accumulated wheel rotation speed of all enabled src wheels. + const PxU32 numMappedWheels = PxMin(numWheelsSrc, numWheelsTrg); + for(PxU32 i = 0; i < numMappedWheels; i++) + { + const PxU32 srcWheelId = wheelMap.sourceWheelIds[i]; + const PxU32 trgWheelId = wheelMap.targetWheelIds[i]; + + trg->mWheelsDynData.copy(src.mWheelsDynData, srcWheelId, trgWheelId); + + setWheelsTrg[trgWheelId] = 1; + + if(!src.mWheelsSimData.getIsWheelDisabled(srcWheelId)) + { + numEnabledWheelsSrc++; + accumulatedWheelRotationSpeedSrc += src.mWheelsDynData.getWheelRotationSpeed(srcWheelId); + } + } + + //Compute the average wheel rotation speed of src. + PxF32 averageWheelRotationSpeedSrc = 0; + if(numEnabledWheelsSrc > 0) + { + averageWheelRotationSpeedSrc = (accumulatedWheelRotationSpeedSrc/ (1.0f * numEnabledWheelsSrc)); + } + + //For wheels on trg that have not had their dynamics data copied from src just set + //their wheel rotation speed to the average wheel rotation speed. + for(PxU32 i = 0; i < numWheelsTrg; i++) + { + if(0 == setWheelsTrg[i] && !trg->mWheelsSimData.getIsWheelDisabled(i)) + { + trg->mWheelsDynData.setWheelRotationSpeed(i, averageWheelRotationSpeedSrc); + } + } + + //Copy the engine rotation speed/gear states/autobox states/etc. + switch(src.getVehicleType()) + { + case PxVehicleTypes::eDRIVE4W: + case PxVehicleTypes::eDRIVENW: + case PxVehicleTypes::eDRIVETANK: + { + const PxVehicleDriveDynData& driveDynDataSrc = static_cast(src).mDriveDynData; + PxVehicleDriveDynData* driveDynDataTrg = &static_cast(trg)->mDriveDynData; + *driveDynDataTrg = driveDynDataSrc; + } + break; + default: + break; + } +} + +bool areEqual(const PxQuat& q0, const PxQuat& q1) +{ + return ((q0.x == q1.x) && (q0.y == q1.y) && (q0.z == q1.z) && (q0.w == q1.w)); +} + +void PxVehicleUpdateCMassLocalPose(const PxTransform& oldCMassLocalPose, const PxTransform& newCMassLocalPose, const PxU32 gravityDirection, PxVehicleWheels* vehicle) +{ + PX_CHECK_AND_RETURN(areEqual(PxQuat(PxIdentity), oldCMassLocalPose.q), "Only center of mass poses with identity rotation are supported"); + PX_CHECK_AND_RETURN(areEqual(PxQuat(PxIdentity), newCMassLocalPose.q), "Only center of mass poses with identity rotation are supported"); + PX_CHECK_AND_RETURN(0==gravityDirection || 1==gravityDirection || 2==gravityDirection, "gravityDirection must be 0 or 1 or 2."); + + //Update the offsets from the rigid body center of mass. + PxVec3 wheelCenterCMOffsets[PX_MAX_NB_WHEELS]; + const PxU32 nbWheels = vehicle->mWheelsSimData.getNbWheels(); + for(PxU32 i = 0; i < nbWheels; i++) + { + wheelCenterCMOffsets[i] = vehicle->mWheelsSimData.getWheelCentreOffset(i) + oldCMassLocalPose.p - newCMassLocalPose.p; + vehicle->mWheelsSimData.setWheelCentreOffset(i, vehicle->mWheelsSimData.getWheelCentreOffset(i) + oldCMassLocalPose.p - newCMassLocalPose.p); + vehicle->mWheelsSimData.setSuspForceAppPointOffset(i, vehicle->mWheelsSimData.getSuspForceAppPointOffset(i) + oldCMassLocalPose.p - newCMassLocalPose.p); + vehicle->mWheelsSimData.setTireForceAppPointOffset(i, vehicle->mWheelsSimData.getTireForceAppPointOffset(i) + oldCMassLocalPose.p - newCMassLocalPose.p); + } + + //Now update the sprung masses. + PxF32 sprungMasses[PX_MAX_NB_WHEELS]; + PxVehicleComputeSprungMasses(nbWheels, wheelCenterCMOffsets, PxVec3(0,0,0), vehicle->getRigidDynamicActor()->getMass(), gravityDirection, sprungMasses); + for(PxU32 i = 0; i < nbWheels; i++) + { + PxVehicleSuspensionData suspData = vehicle->mWheelsSimData.getSuspensionData(i); + const PxF32 massRatio = sprungMasses[i]/suspData.mSprungMass; + suspData.mSprungMass = sprungMasses[i]; + suspData.mSpringStrength *= massRatio; + suspData.mSpringDamperRate *= massRatio; + vehicle->mWheelsSimData.setSuspensionData(i, suspData); + } +} + +}//physx diff --git a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp new file mode 100644 index 000000000..459d26a2f --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp @@ -0,0 +1,577 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleUtilTelemetry.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "stdio.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +#if PX_DEBUG_VEHICLE_ON + +PxVehicleGraphDesc::PxVehicleGraphDesc() +: mPosX(PX_MAX_F32), + mPosY(PX_MAX_F32), + mSizeX(PX_MAX_F32), + mSizeY(PX_MAX_F32), + mBackgroundColor(PxVec3(PX_MAX_F32,PX_MAX_F32,PX_MAX_F32)), + mAlpha(PX_MAX_F32) +{ +} + +bool PxVehicleGraphDesc::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mPosX != PX_MAX_F32, "PxVehicleGraphDesc.mPosX must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mPosY != PX_MAX_F32, "PxVehicleGraphDesc.mPosY must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mSizeX != PX_MAX_F32, "PxVehicleGraphDesc.mSizeX must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mSizeY != PX_MAX_F32, "PxVehicleGraphDesc.mSizeY must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mBackgroundColor.x != PX_MAX_F32 && mBackgroundColor.y != PX_MAX_F32 && mBackgroundColor.z != PX_MAX_F32, "PxVehicleGraphDesc.mBackgroundColor must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mAlpha != PX_MAX_F32, "PxVehicleGraphDesc.mAlpha must be initialised", false); + return true; +} + +PxVehicleGraphChannelDesc::PxVehicleGraphChannelDesc() +: mMinY(PX_MAX_F32), + mMaxY(PX_MAX_F32), + mMidY(PX_MAX_F32), + mColorLow(PxVec3(PX_MAX_F32,PX_MAX_F32,PX_MAX_F32)), + mColorHigh(PxVec3(PX_MAX_F32,PX_MAX_F32,PX_MAX_F32)), + mTitle(NULL) +{ +} + +bool PxVehicleGraphChannelDesc::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mMinY != PX_MAX_F32, "PxVehicleGraphChannelDesc.mMinY must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mMaxY != PX_MAX_F32, "PxVehicleGraphChannelDesc.mMaxY must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mMidY != PX_MAX_F32, "PxVehicleGraphChannelDesc.mMidY must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mColorLow.x != PX_MAX_F32 && mColorLow.y != PX_MAX_F32 && mColorLow.z != PX_MAX_F32, "PxVehicleGraphChannelDesc.mColorLow must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mColorHigh.x != PX_MAX_F32 && mColorHigh.y != PX_MAX_F32 && mColorHigh.z != PX_MAX_F32, "PxVehicleGraphChannelDesc.mColorHigh must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mTitle, "PxVehicleGraphChannelDesc.mTitle must be initialised", false); + return true; +} + +PxVehicleGraph::PxVehicleGraph() +{ + mBackgroundMinX=0; + mBackgroundMaxX=0; + mBackgroundMinY=0; + mBackgroundMaxY=0; + mSampleTide=0; + mBackgroundColor=PxVec3(255.f,255.f,255.f); + mBackgroundAlpha=1.0f; + for(PxU32 i=0;i= size_t(PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS) && size_t(PxVehicleGraph::eMAX_NB_CHANNELS) >= size_t(PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS)); +} + +PxVehicleGraph::~PxVehicleGraph() +{ +} + +void PxVehicleGraph::setup(const PxVehicleGraphDesc& desc, const PxVehicleGraphType::Enum graphType) +{ + mBackgroundMinX = (desc.mPosX - 0.5f*desc.mSizeX); + mBackgroundMaxX = (desc.mPosX + 0.5f*desc.mSizeX); + mBackgroundMinY = (desc.mPosY - 0.5f*desc.mSizeY); + mBackgroundMaxY = (desc.mPosY + 0.5f*desc.mSizeY); + + mBackgroundColor=desc.mBackgroundColor; + mBackgroundAlpha=desc.mAlpha; + + mNbChannels = (PxVehicleGraphType::eWHEEL==graphType) ? PxU32(PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS) : PxU32(PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS); +} + +void PxVehicleGraph::setChannel(PxVehicleGraphChannelDesc& desc, const PxU32 channel) +{ + PX_ASSERT(channel(PX_ALLOC(size, "PxVehicleNWTelemetryData")); + + //Patch up the pointers. + PxU8* ptr = reinterpret_cast(vehTelData) + sizeof(PxVehicleTelemetryData); + vehTelData->mEngineGraph = reinterpret_cast(ptr); + new(vehTelData->mEngineGraph) PxVehicleGraph(); + ptr += sizeof(PxVehicleGraph); + vehTelData->mWheelGraphs = reinterpret_cast(ptr); + for(PxU32 i=0;imWheelGraphs[i]) PxVehicleGraph(); + } + ptr += sizeof(PxVehicleGraph)*numWheels; + vehTelData->mSuspforceAppPoints = reinterpret_cast(ptr); + ptr += sizeof(PxVec3)*numWheels; + vehTelData->mTireforceAppPoints = reinterpret_cast(ptr); + ptr += sizeof(PxVec3)*numWheels; + + //Set the number of wheels in each structure that needs it. + vehTelData->mNbActiveWheels=numWheels; + + //Finished. + return vehTelData; +} + +void PxVehicleTelemetryData::free() +{ + PX_FREE(this); +} + +void physx::PxVehicleTelemetryData::setup +(const PxF32 graphSizeX, const PxF32 graphSizeY, +const PxF32 engineGraphPosX, const PxF32 engineGraphPosY, +const PxF32* const wheelGraphPosX, const PxF32* const wheelGraphPosY, +const PxVec3& backgroundColor, const PxVec3& lineColorHigh, const PxVec3& lineColorLow) +{ + mEngineGraph->setupEngineGraph + (graphSizeX, graphSizeY, engineGraphPosX, engineGraphPosY, + backgroundColor, lineColorHigh, lineColorLow); + + const PxU32 numActiveWheels=mNbActiveWheels; + for(PxU32 k=0;kclearRecordedChannelData(); + + const PxU32 numActiveWheels=mNbActiveWheels; + for(PxU32 k=0;k or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +PxVehicleChassisData_PropertiesStart, +PxVehicleChassisData_MMOI, +PxVehicleChassisData_MMass, +PxVehicleChassisData_MCMOffset, +PxVehicleChassisData_PropertiesStop, +PxVehicleEngineData_PropertiesStart, +PxVehicleEngineData_RecipMOI, +PxVehicleEngineData_RecipMaxOmega, +PxVehicleEngineData_MTorqueCurve, +PxVehicleEngineData_MMOI, +PxVehicleEngineData_MPeakTorque, +PxVehicleEngineData_MMaxOmega, +PxVehicleEngineData_MDampingRateFullThrottle, +PxVehicleEngineData_MDampingRateZeroThrottleClutchEngaged, +PxVehicleEngineData_MDampingRateZeroThrottleClutchDisengaged, +PxVehicleEngineData_PropertiesStop, +PxVehicleGearsData_PropertiesStart, +PxVehicleGearsData_GearRatio, +PxVehicleGearsData_MFinalRatio, +PxVehicleGearsData_MNbRatios, +PxVehicleGearsData_MSwitchTime, +PxVehicleGearsData_PropertiesStop, +PxVehicleAutoBoxData_PropertiesStart, +PxVehicleAutoBoxData_Latency, +PxVehicleAutoBoxData_UpRatios, +PxVehicleAutoBoxData_DownRatios, +PxVehicleAutoBoxData_PropertiesStop, +PxVehicleDifferential4WData_PropertiesStart, +PxVehicleDifferential4WData_MFrontRearSplit, +PxVehicleDifferential4WData_MFrontLeftRightSplit, +PxVehicleDifferential4WData_MRearLeftRightSplit, +PxVehicleDifferential4WData_MCentreBias, +PxVehicleDifferential4WData_MFrontBias, +PxVehicleDifferential4WData_MRearBias, +PxVehicleDifferential4WData_MType, +PxVehicleDifferential4WData_PropertiesStop, +PxVehicleDifferentialNWData_PropertiesStart, +PxVehicleDifferentialNWData_DrivenWheelStatus, +PxVehicleDifferentialNWData_PropertiesStop, +PxVehicleAckermannGeometryData_PropertiesStart, +PxVehicleAckermannGeometryData_MAccuracy, +PxVehicleAckermannGeometryData_MFrontWidth, +PxVehicleAckermannGeometryData_MRearWidth, +PxVehicleAckermannGeometryData_MAxleSeparation, +PxVehicleAckermannGeometryData_PropertiesStop, +PxVehicleClutchData_PropertiesStart, +PxVehicleClutchData_MStrength, +PxVehicleClutchData_MAccuracyMode, +PxVehicleClutchData_MEstimateIterations, +PxVehicleClutchData_PropertiesStop, +PxVehicleTireLoadFilterData_PropertiesStart, +PxVehicleTireLoadFilterData_Denominator, +PxVehicleTireLoadFilterData_MMinNormalisedLoad, +PxVehicleTireLoadFilterData_MMinFilteredNormalisedLoad, +PxVehicleTireLoadFilterData_MMaxNormalisedLoad, +PxVehicleTireLoadFilterData_MMaxFilteredNormalisedLoad, +PxVehicleTireLoadFilterData_PropertiesStop, +PxVehicleWheelData_PropertiesStart, +PxVehicleWheelData_RecipRadius, +PxVehicleWheelData_RecipMOI, +PxVehicleWheelData_MRadius, +PxVehicleWheelData_MWidth, +PxVehicleWheelData_MMass, +PxVehicleWheelData_MMOI, +PxVehicleWheelData_MDampingRate, +PxVehicleWheelData_MMaxBrakeTorque, +PxVehicleWheelData_MMaxHandBrakeTorque, +PxVehicleWheelData_MMaxSteer, +PxVehicleWheelData_MToeAngle, +PxVehicleWheelData_PropertiesStop, +PxVehicleSuspensionData_PropertiesStart, +PxVehicleSuspensionData_RecipMaxCompression, +PxVehicleSuspensionData_RecipMaxDroop, +PxVehicleSuspensionData_MassAndPreserveNaturalFrequency, +PxVehicleSuspensionData_MSpringStrength, +PxVehicleSuspensionData_MSpringDamperRate, +PxVehicleSuspensionData_MMaxCompression, +PxVehicleSuspensionData_MMaxDroop, +PxVehicleSuspensionData_MSprungMass, +PxVehicleSuspensionData_MCamberAtRest, +PxVehicleSuspensionData_MCamberAtMaxCompression, +PxVehicleSuspensionData_MCamberAtMaxDroop, +PxVehicleSuspensionData_PropertiesStop, +PxVehicleAntiRollBarData_PropertiesStart, +PxVehicleAntiRollBarData_MWheel0, +PxVehicleAntiRollBarData_MWheel1, +PxVehicleAntiRollBarData_MStiffness, +PxVehicleAntiRollBarData_PropertiesStop, +PxVehicleTireData_PropertiesStart, +PxVehicleTireData_RecipLongitudinalStiffnessPerUnitGravity, +PxVehicleTireData_FrictionVsSlipGraphRecipx1Minusx0, +PxVehicleTireData_FrictionVsSlipGraphRecipx2Minusx1, +PxVehicleTireData_MLatStiffX, +PxVehicleTireData_MLatStiffY, +PxVehicleTireData_MLongitudinalStiffnessPerUnitGravity, +PxVehicleTireData_MCamberStiffnessPerUnitGravity, +PxVehicleTireData_MType, +PxVehicleTireData_MFrictionVsSlipGraph, +PxVehicleTireData_PropertiesStop, +PxVehicleWheels4SimData_PropertiesStart, +PxVehicleWheels4SimData_TireRestLoadsArray, +PxVehicleWheels4SimData_RecipTireRestLoadsArray, +PxVehicleWheels4SimData_PropertiesStop, +PxVehicleWheelsSimData_PropertiesStart, +PxVehicleWheelsSimData_ChassisMass, +PxVehicleWheelsSimData_SuspensionData, +PxVehicleWheelsSimData_WheelData, +PxVehicleWheelsSimData_TireData, +PxVehicleWheelsSimData_SuspTravelDirection, +PxVehicleWheelsSimData_SuspForceAppPointOffset, +PxVehicleWheelsSimData_TireForceAppPointOffset, +PxVehicleWheelsSimData_WheelCentreOffset, +PxVehicleWheelsSimData_WheelShapeMapping, +PxVehicleWheelsSimData_SceneQueryFilterData, +PxVehicleWheelsSimData_AntiRollBarData, +PxVehicleWheelsSimData_TireLoadFilterData, +PxVehicleWheelsSimData_MinLongSlipDenominator, +PxVehicleWheelsSimData_ThresholdLongSpeed, +PxVehicleWheelsSimData_LowForwardSpeedSubStepCount, +PxVehicleWheelsSimData_HighForwardSpeedSubStepCount, +PxVehicleWheelsSimData_WheelEnabledState, +PxVehicleWheelsSimData_PropertiesStop, +PxVehicleWheelsDynData_PropertiesStart, +PxVehicleWheelsDynData_TireForceShaderFunction, +PxVehicleWheelsDynData_WheelRotationSpeed, +PxVehicleWheelsDynData_WheelRotationAngle, +PxVehicleWheelsDynData_Wheel4DynData, +PxVehicleWheelsDynData_PropertiesStop, +PxVehicleWheels_PropertiesStart, +PxVehicleWheels_VehicleType, +PxVehicleWheels_RigidDynamicActor, +PxVehicleWheels_ConcreteTypeName, +PxVehicleWheels_MWheelsSimData, +PxVehicleWheels_MWheelsDynData, +PxVehicleWheels_PropertiesStop, +PxVehicleDriveDynData_PropertiesStart, +PxVehicleDriveDynData_AnalogInput, +PxVehicleDriveDynData_GearUp, +PxVehicleDriveDynData_GearDown, +PxVehicleDriveDynData_UseAutoGears, +PxVehicleDriveDynData_CurrentGear, +PxVehicleDriveDynData_TargetGear, +PxVehicleDriveDynData_EngineRotationSpeed, +PxVehicleDriveDynData_GearChange, +PxVehicleDriveDynData_GearSwitchTime, +PxVehicleDriveDynData_AutoBoxSwitchTime, +PxVehicleDriveDynData_MUseAutoGears, +PxVehicleDriveDynData_MGearUpPressed, +PxVehicleDriveDynData_MGearDownPressed, +PxVehicleDriveDynData_MCurrentGear, +PxVehicleDriveDynData_MTargetGear, +PxVehicleDriveDynData_MEnginespeed, +PxVehicleDriveDynData_MGearSwitchTime, +PxVehicleDriveDynData_MAutoBoxSwitchTime, +PxVehicleDriveDynData_PropertiesStop, +PxVehicleDriveSimData_PropertiesStart, +PxVehicleDriveSimData_EngineData, +PxVehicleDriveSimData_GearsData, +PxVehicleDriveSimData_ClutchData, +PxVehicleDriveSimData_AutoBoxData, +PxVehicleDriveSimData_PropertiesStop, +PxVehicleDriveSimData4W_PropertiesStart, +PxVehicleDriveSimData4W_DiffData, +PxVehicleDriveSimData4W_AckermannGeometryData, +PxVehicleDriveSimData4W_PropertiesStop, +PxVehicleDrive_PropertiesStart, +PxVehicleDrive_ConcreteTypeName, +PxVehicleDrive_MDriveDynData, +PxVehicleDrive_PropertiesStop, +PxVehicleDrive4W_PropertiesStart, +PxVehicleDrive4W_ConcreteTypeName, +PxVehicleDrive4W_MDriveSimData, +PxVehicleDrive4W_PropertiesStop, +PxVehicleDriveTank_PropertiesStart, +PxVehicleDriveTank_DriveModel, +PxVehicleDriveTank_ConcreteTypeName, +PxVehicleDriveTank_MDriveSimData, +PxVehicleDriveTank_PropertiesStop, +PxVehicleDriveSimDataNW_PropertiesStart, +PxVehicleDriveSimDataNW_DiffData, +PxVehicleDriveSimDataNW_PropertiesStop, +PxVehicleDriveNW_PropertiesStart, +PxVehicleDriveNW_ConcreteTypeName, +PxVehicleDriveNW_MDriveSimData, +PxVehicleDriveNW_PropertiesStop, +PxVehicleNoDrive_PropertiesStart, +PxVehicleNoDrive_BrakeTorque, +PxVehicleNoDrive_DriveTorque, +PxVehicleNoDrive_SteerAngle, +PxVehicleNoDrive_ConcreteTypeName, +PxVehicleNoDrive_PropertiesStop, + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h new file mode 100644 index 000000000..22275e592 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h @@ -0,0 +1,1797 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +#define PX_PROPERTY_INFO_NAME PxVehiclePropertyInfoName + class PxVehicleChassisData; + struct PxVehicleChassisDataGeneratedValues + { + PxVec3 MMOI; + PxReal MMass; + PxVec3 MCMOffset; + PxVehicleChassisDataGeneratedValues( const PxVehicleChassisData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleChassisData, MMOI, PxVehicleChassisDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleChassisData, MMass, PxVehicleChassisDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleChassisData, MCMOffset, PxVehicleChassisDataGeneratedValues) + struct PxVehicleChassisDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleChassisData"; } + PxPropertyInfo MMOI; + PxPropertyInfo MMass; + PxPropertyInfo MCMOffset; + + PxVehicleChassisDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MMOI, inStartIndex + 0 );; + inOperator( MMass, inStartIndex + 1 );; + inOperator( MCMOffset, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleChassisDataGeneratedInfo Info; + const PxVehicleChassisDataGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxEMPTYConversion[] = { + { "PxEmpty", static_cast( physx::PxEmpty ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< const physx::PxEMPTY > { PxEnumTraits() : NameConversion( g_physx__PxEMPTYConversion ) {} const PxU32ToName* NameConversion; }; + class PxVehicleEngineData; + struct PxVehicleEngineDataGeneratedValues + { + PxReal RecipMOI; + PxReal RecipMaxOmega; + PxReal MMOI; + PxReal MPeakTorque; + PxReal MMaxOmega; + PxReal MDampingRateFullThrottle; + PxReal MDampingRateZeroThrottleClutchEngaged; + PxReal MDampingRateZeroThrottleClutchDisengaged; + PxVehicleEngineDataGeneratedValues( const PxVehicleEngineData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, RecipMOI, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, RecipMaxOmega, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MMOI, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MPeakTorque, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MMaxOmega, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MDampingRateFullThrottle, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MDampingRateZeroThrottleClutchEngaged, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MDampingRateZeroThrottleClutchDisengaged, PxVehicleEngineDataGeneratedValues) + struct PxVehicleEngineDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleEngineData"; } + PxReadOnlyPropertyInfo RecipMOI; + PxReadOnlyPropertyInfo RecipMaxOmega; + MTorqueCurveProperty MTorqueCurve; + PxPropertyInfo MMOI; + PxPropertyInfo MPeakTorque; + PxPropertyInfo MMaxOmega; + PxPropertyInfo MDampingRateFullThrottle; + PxPropertyInfo MDampingRateZeroThrottleClutchEngaged; + PxPropertyInfo MDampingRateZeroThrottleClutchDisengaged; + + PxVehicleEngineDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 9; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( RecipMOI, inStartIndex + 0 );; + inOperator( RecipMaxOmega, inStartIndex + 1 );; + inOperator( MTorqueCurve, inStartIndex + 2 );; + inOperator( MMOI, inStartIndex + 3 );; + inOperator( MPeakTorque, inStartIndex + 4 );; + inOperator( MMaxOmega, inStartIndex + 5 );; + inOperator( MDampingRateFullThrottle, inStartIndex + 6 );; + inOperator( MDampingRateZeroThrottleClutchEngaged, inStartIndex + 7 );; + inOperator( MDampingRateZeroThrottleClutchDisengaged, inStartIndex + 8 );; + return 9 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleEngineDataGeneratedInfo Info; + const PxVehicleEngineDataGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxVehicleGearsData__EnumConversion[] = { + { "eREVERSE", static_cast( physx::PxVehicleGearsData::eREVERSE ) }, + { "eNEUTRAL", static_cast( physx::PxVehicleGearsData::eNEUTRAL ) }, + { "eFIRST", static_cast( physx::PxVehicleGearsData::eFIRST ) }, + { "eSECOND", static_cast( physx::PxVehicleGearsData::eSECOND ) }, + { "eTHIRD", static_cast( physx::PxVehicleGearsData::eTHIRD ) }, + { "eFOURTH", static_cast( physx::PxVehicleGearsData::eFOURTH ) }, + { "eFIFTH", static_cast( physx::PxVehicleGearsData::eFIFTH ) }, + { "eSIXTH", static_cast( physx::PxVehicleGearsData::eSIXTH ) }, + { "eSEVENTH", static_cast( physx::PxVehicleGearsData::eSEVENTH ) }, + { "eEIGHTH", static_cast( physx::PxVehicleGearsData::eEIGHTH ) }, + { "eNINTH", static_cast( physx::PxVehicleGearsData::eNINTH ) }, + { "eTENTH", static_cast( physx::PxVehicleGearsData::eTENTH ) }, + { "eELEVENTH", static_cast( physx::PxVehicleGearsData::eELEVENTH ) }, + { "eTWELFTH", static_cast( physx::PxVehicleGearsData::eTWELFTH ) }, + { "eTHIRTEENTH", static_cast( physx::PxVehicleGearsData::eTHIRTEENTH ) }, + { "eFOURTEENTH", static_cast( physx::PxVehicleGearsData::eFOURTEENTH ) }, + { "eFIFTEENTH", static_cast( physx::PxVehicleGearsData::eFIFTEENTH ) }, + { "eSIXTEENTH", static_cast( physx::PxVehicleGearsData::eSIXTEENTH ) }, + { "eSEVENTEENTH", static_cast( physx::PxVehicleGearsData::eSEVENTEENTH ) }, + { "eEIGHTEENTH", static_cast( physx::PxVehicleGearsData::eEIGHTEENTH ) }, + { "eNINETEENTH", static_cast( physx::PxVehicleGearsData::eNINETEENTH ) }, + { "eTWENTIETH", static_cast( physx::PxVehicleGearsData::eTWENTIETH ) }, + { "eTWENTYFIRST", static_cast( physx::PxVehicleGearsData::eTWENTYFIRST ) }, + { "eTWENTYSECOND", static_cast( physx::PxVehicleGearsData::eTWENTYSECOND ) }, + { "eTWENTYTHIRD", static_cast( physx::PxVehicleGearsData::eTWENTYTHIRD ) }, + { "eTWENTYFOURTH", static_cast( physx::PxVehicleGearsData::eTWENTYFOURTH ) }, + { "eTWENTYFIFTH", static_cast( physx::PxVehicleGearsData::eTWENTYFIFTH ) }, + { "eTWENTYSIXTH", static_cast( physx::PxVehicleGearsData::eTWENTYSIXTH ) }, + { "eTWENTYSEVENTH", static_cast( physx::PxVehicleGearsData::eTWENTYSEVENTH ) }, + { "eTWENTYEIGHTH", static_cast( physx::PxVehicleGearsData::eTWENTYEIGHTH ) }, + { "eTWENTYNINTH", static_cast( physx::PxVehicleGearsData::eTWENTYNINTH ) }, + { "eTHIRTIETH", static_cast( physx::PxVehicleGearsData::eTHIRTIETH ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxVehicleGearsData::Enum > { PxEnumTraits() : NameConversion( g_physx__PxVehicleGearsData__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxVehicleGearsData; + struct PxVehicleGearsDataGeneratedValues + { + PxReal GearRatio[physx::PxVehicleGearsData::eGEARSRATIO_COUNT]; + PxReal MFinalRatio; + PxU32 MNbRatios; + PxReal MSwitchTime; + PxVehicleGearsDataGeneratedValues( const PxVehicleGearsData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleGearsData, GearRatio, PxVehicleGearsDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleGearsData, MFinalRatio, PxVehicleGearsDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleGearsData, MNbRatios, PxVehicleGearsDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleGearsData, MSwitchTime, PxVehicleGearsDataGeneratedValues) + struct PxVehicleGearsDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleGearsData"; } + PxIndexedPropertyInfo GearRatio; + PxPropertyInfo MFinalRatio; + PxPropertyInfo MNbRatios; + PxPropertyInfo MSwitchTime; + + PxVehicleGearsDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( GearRatio, inStartIndex + 0 );; + inOperator( MFinalRatio, inStartIndex + 1 );; + inOperator( MNbRatios, inStartIndex + 2 );; + inOperator( MSwitchTime, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleGearsDataGeneratedInfo Info; + const PxVehicleGearsDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleAutoBoxData; + struct PxVehicleAutoBoxDataGeneratedValues + { + PxReal Latency; + PxReal UpRatios[physx::PxVehicleGearsData::eGEARSRATIO_COUNT]; + PxReal DownRatios[physx::PxVehicleGearsData::eGEARSRATIO_COUNT]; + PxVehicleAutoBoxDataGeneratedValues( const PxVehicleAutoBoxData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAutoBoxData, Latency, PxVehicleAutoBoxDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAutoBoxData, UpRatios, PxVehicleAutoBoxDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAutoBoxData, DownRatios, PxVehicleAutoBoxDataGeneratedValues) + struct PxVehicleAutoBoxDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleAutoBoxData"; } + PxPropertyInfo Latency; + PxIndexedPropertyInfo UpRatios; + PxIndexedPropertyInfo DownRatios; + + PxVehicleAutoBoxDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Latency, inStartIndex + 0 );; + inOperator( UpRatios, inStartIndex + 1 );; + inOperator( DownRatios, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleAutoBoxDataGeneratedInfo Info; + const PxVehicleAutoBoxDataGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxVehicleDifferential4WData__EnumConversion[] = { + { "eDIFF_TYPE_LS_4WD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD ) }, + { "eDIFF_TYPE_LS_FRONTWD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD ) }, + { "eDIFF_TYPE_LS_REARWD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD ) }, + { "eDIFF_TYPE_OPEN_4WD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD ) }, + { "eDIFF_TYPE_OPEN_FRONTWD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD ) }, + { "eDIFF_TYPE_OPEN_REARWD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD ) }, + { "eMAX_NB_DIFF_TYPES", static_cast( physx::PxVehicleDifferential4WData::eMAX_NB_DIFF_TYPES ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxVehicleDifferential4WData::Enum > { PxEnumTraits() : NameConversion( g_physx__PxVehicleDifferential4WData__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxVehicleDifferential4WData; + struct PxVehicleDifferential4WDataGeneratedValues + { + PxReal MFrontRearSplit; + PxReal MFrontLeftRightSplit; + PxReal MRearLeftRightSplit; + PxReal MCentreBias; + PxReal MFrontBias; + PxReal MRearBias; + PxVehicleDifferential4WData::Enum MType; + PxVehicleDifferential4WDataGeneratedValues( const PxVehicleDifferential4WData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MFrontRearSplit, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MFrontLeftRightSplit, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MRearLeftRightSplit, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MCentreBias, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MFrontBias, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MRearBias, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MType, PxVehicleDifferential4WDataGeneratedValues) + struct PxVehicleDifferential4WDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleDifferential4WData"; } + PxPropertyInfo MFrontRearSplit; + PxPropertyInfo MFrontLeftRightSplit; + PxPropertyInfo MRearLeftRightSplit; + PxPropertyInfo MCentreBias; + PxPropertyInfo MFrontBias; + PxPropertyInfo MRearBias; + PxPropertyInfo MType; + + PxVehicleDifferential4WDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 7; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MFrontRearSplit, inStartIndex + 0 );; + inOperator( MFrontLeftRightSplit, inStartIndex + 1 );; + inOperator( MRearLeftRightSplit, inStartIndex + 2 );; + inOperator( MCentreBias, inStartIndex + 3 );; + inOperator( MFrontBias, inStartIndex + 4 );; + inOperator( MRearBias, inStartIndex + 5 );; + inOperator( MType, inStartIndex + 6 );; + return 7 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDifferential4WDataGeneratedInfo Info; + const PxVehicleDifferential4WDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDifferentialNWData; + struct PxVehicleDifferentialNWDataGeneratedValues + { + PxU32 DrivenWheelStatus; + PxVehicleDifferentialNWDataGeneratedValues( const PxVehicleDifferentialNWData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferentialNWData, DrivenWheelStatus, PxVehicleDifferentialNWDataGeneratedValues) + struct PxVehicleDifferentialNWDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleDifferentialNWData"; } + PxPropertyInfo DrivenWheelStatus; + + PxVehicleDifferentialNWDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( DrivenWheelStatus, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDifferentialNWDataGeneratedInfo Info; + const PxVehicleDifferentialNWDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleAckermannGeometryData; + struct PxVehicleAckermannGeometryDataGeneratedValues + { + PxReal MAccuracy; + PxReal MFrontWidth; + PxReal MRearWidth; + PxReal MAxleSeparation; + PxVehicleAckermannGeometryDataGeneratedValues( const PxVehicleAckermannGeometryData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAckermannGeometryData, MAccuracy, PxVehicleAckermannGeometryDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAckermannGeometryData, MFrontWidth, PxVehicleAckermannGeometryDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAckermannGeometryData, MRearWidth, PxVehicleAckermannGeometryDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAckermannGeometryData, MAxleSeparation, PxVehicleAckermannGeometryDataGeneratedValues) + struct PxVehicleAckermannGeometryDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleAckermannGeometryData"; } + PxPropertyInfo MAccuracy; + PxPropertyInfo MFrontWidth; + PxPropertyInfo MRearWidth; + PxPropertyInfo MAxleSeparation; + + PxVehicleAckermannGeometryDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MAccuracy, inStartIndex + 0 );; + inOperator( MFrontWidth, inStartIndex + 1 );; + inOperator( MRearWidth, inStartIndex + 2 );; + inOperator( MAxleSeparation, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleAckermannGeometryDataGeneratedInfo Info; + const PxVehicleAckermannGeometryDataGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxVehicleClutchAccuracyMode__EnumConversion[] = { + { "eESTIMATE", static_cast( physx::PxVehicleClutchAccuracyMode::eESTIMATE ) }, + { "eBEST_POSSIBLE", static_cast( physx::PxVehicleClutchAccuracyMode::eBEST_POSSIBLE ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxVehicleClutchAccuracyMode::Enum > { PxEnumTraits() : NameConversion( g_physx__PxVehicleClutchAccuracyMode__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxVehicleClutchData; + struct PxVehicleClutchDataGeneratedValues + { + PxReal MStrength; + PxVehicleClutchAccuracyMode::Enum MAccuracyMode; + PxU32 MEstimateIterations; + PxVehicleClutchDataGeneratedValues( const PxVehicleClutchData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleClutchData, MStrength, PxVehicleClutchDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleClutchData, MAccuracyMode, PxVehicleClutchDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleClutchData, MEstimateIterations, PxVehicleClutchDataGeneratedValues) + struct PxVehicleClutchDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleClutchData"; } + PxPropertyInfo MStrength; + PxPropertyInfo MAccuracyMode; + PxPropertyInfo MEstimateIterations; + + PxVehicleClutchDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MStrength, inStartIndex + 0 );; + inOperator( MAccuracyMode, inStartIndex + 1 );; + inOperator( MEstimateIterations, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleClutchDataGeneratedInfo Info; + const PxVehicleClutchDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleTireLoadFilterData; + struct PxVehicleTireLoadFilterDataGeneratedValues + { + PxReal Denominator; + PxReal MMinNormalisedLoad; + PxReal MMinFilteredNormalisedLoad; + PxReal MMaxNormalisedLoad; + PxReal MMaxFilteredNormalisedLoad; + PxVehicleTireLoadFilterDataGeneratedValues( const PxVehicleTireLoadFilterData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireLoadFilterData, Denominator, PxVehicleTireLoadFilterDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireLoadFilterData, MMinNormalisedLoad, PxVehicleTireLoadFilterDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireLoadFilterData, MMinFilteredNormalisedLoad, PxVehicleTireLoadFilterDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireLoadFilterData, MMaxNormalisedLoad, PxVehicleTireLoadFilterDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireLoadFilterData, MMaxFilteredNormalisedLoad, PxVehicleTireLoadFilterDataGeneratedValues) + struct PxVehicleTireLoadFilterDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleTireLoadFilterData"; } + PxReadOnlyPropertyInfo Denominator; + PxPropertyInfo MMinNormalisedLoad; + PxPropertyInfo MMinFilteredNormalisedLoad; + PxPropertyInfo MMaxNormalisedLoad; + PxPropertyInfo MMaxFilteredNormalisedLoad; + + PxVehicleTireLoadFilterDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Denominator, inStartIndex + 0 );; + inOperator( MMinNormalisedLoad, inStartIndex + 1 );; + inOperator( MMinFilteredNormalisedLoad, inStartIndex + 2 );; + inOperator( MMaxNormalisedLoad, inStartIndex + 3 );; + inOperator( MMaxFilteredNormalisedLoad, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleTireLoadFilterDataGeneratedInfo Info; + const PxVehicleTireLoadFilterDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleWheelData; + struct PxVehicleWheelDataGeneratedValues + { + PxReal RecipRadius; + PxReal RecipMOI; + PxReal MRadius; + PxReal MWidth; + PxReal MMass; + PxReal MMOI; + PxReal MDampingRate; + PxReal MMaxBrakeTorque; + PxReal MMaxHandBrakeTorque; + PxReal MMaxSteer; + PxReal MToeAngle; + PxVehicleWheelDataGeneratedValues( const PxVehicleWheelData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, RecipRadius, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, RecipMOI, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MRadius, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MWidth, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MMass, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MMOI, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MDampingRate, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MMaxBrakeTorque, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MMaxHandBrakeTorque, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MMaxSteer, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MToeAngle, PxVehicleWheelDataGeneratedValues) + struct PxVehicleWheelDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleWheelData"; } + PxReadOnlyPropertyInfo RecipRadius; + PxReadOnlyPropertyInfo RecipMOI; + PxPropertyInfo MRadius; + PxPropertyInfo MWidth; + PxPropertyInfo MMass; + PxPropertyInfo MMOI; + PxPropertyInfo MDampingRate; + PxPropertyInfo MMaxBrakeTorque; + PxPropertyInfo MMaxHandBrakeTorque; + PxPropertyInfo MMaxSteer; + PxPropertyInfo MToeAngle; + + PxVehicleWheelDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 11; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( RecipRadius, inStartIndex + 0 );; + inOperator( RecipMOI, inStartIndex + 1 );; + inOperator( MRadius, inStartIndex + 2 );; + inOperator( MWidth, inStartIndex + 3 );; + inOperator( MMass, inStartIndex + 4 );; + inOperator( MMOI, inStartIndex + 5 );; + inOperator( MDampingRate, inStartIndex + 6 );; + inOperator( MMaxBrakeTorque, inStartIndex + 7 );; + inOperator( MMaxHandBrakeTorque, inStartIndex + 8 );; + inOperator( MMaxSteer, inStartIndex + 9 );; + inOperator( MToeAngle, inStartIndex + 10 );; + return 11 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleWheelDataGeneratedInfo Info; + const PxVehicleWheelDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleSuspensionData; + struct PxVehicleSuspensionDataGeneratedValues + { + PxReal RecipMaxCompression; + PxReal RecipMaxDroop; + PxReal MSpringStrength; + PxReal MSpringDamperRate; + PxReal MMaxCompression; + PxReal MMaxDroop; + PxReal MSprungMass; + PxReal MCamberAtRest; + PxReal MCamberAtMaxCompression; + PxReal MCamberAtMaxDroop; + PxVehicleSuspensionDataGeneratedValues( const PxVehicleSuspensionData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, RecipMaxCompression, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, RecipMaxDroop, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MSpringStrength, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MSpringDamperRate, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MMaxCompression, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MMaxDroop, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MSprungMass, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MCamberAtRest, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MCamberAtMaxCompression, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MCamberAtMaxDroop, PxVehicleSuspensionDataGeneratedValues) + struct PxVehicleSuspensionDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleSuspensionData"; } + PxReadOnlyPropertyInfo RecipMaxCompression; + PxReadOnlyPropertyInfo RecipMaxDroop; + PxWriteOnlyPropertyInfo MassAndPreserveNaturalFrequency; + PxPropertyInfo MSpringStrength; + PxPropertyInfo MSpringDamperRate; + PxPropertyInfo MMaxCompression; + PxPropertyInfo MMaxDroop; + PxPropertyInfo MSprungMass; + PxPropertyInfo MCamberAtRest; + PxPropertyInfo MCamberAtMaxCompression; + PxPropertyInfo MCamberAtMaxDroop; + + PxVehicleSuspensionDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 11; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( RecipMaxCompression, inStartIndex + 0 );; + inOperator( RecipMaxDroop, inStartIndex + 1 );; + inOperator( MassAndPreserveNaturalFrequency, inStartIndex + 2 );; + inOperator( MSpringStrength, inStartIndex + 3 );; + inOperator( MSpringDamperRate, inStartIndex + 4 );; + inOperator( MMaxCompression, inStartIndex + 5 );; + inOperator( MMaxDroop, inStartIndex + 6 );; + inOperator( MSprungMass, inStartIndex + 7 );; + inOperator( MCamberAtRest, inStartIndex + 8 );; + inOperator( MCamberAtMaxCompression, inStartIndex + 9 );; + inOperator( MCamberAtMaxDroop, inStartIndex + 10 );; + return 11 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleSuspensionDataGeneratedInfo Info; + const PxVehicleSuspensionDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleAntiRollBarData; + struct PxVehicleAntiRollBarDataGeneratedValues + { + PxU32 MWheel0; + PxU32 MWheel1; + PxF32 MStiffness; + PxVehicleAntiRollBarDataGeneratedValues( const PxVehicleAntiRollBarData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAntiRollBarData, MWheel0, PxVehicleAntiRollBarDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAntiRollBarData, MWheel1, PxVehicleAntiRollBarDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAntiRollBarData, MStiffness, PxVehicleAntiRollBarDataGeneratedValues) + struct PxVehicleAntiRollBarDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleAntiRollBarData"; } + PxPropertyInfo MWheel0; + PxPropertyInfo MWheel1; + PxPropertyInfo MStiffness; + + PxVehicleAntiRollBarDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MWheel0, inStartIndex + 0 );; + inOperator( MWheel1, inStartIndex + 1 );; + inOperator( MStiffness, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleAntiRollBarDataGeneratedInfo Info; + const PxVehicleAntiRollBarDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleTireData; + struct PxVehicleTireDataGeneratedValues + { + PxReal RecipLongitudinalStiffnessPerUnitGravity; + PxReal FrictionVsSlipGraphRecipx1Minusx0; + PxReal FrictionVsSlipGraphRecipx2Minusx1; + PxReal MLatStiffX; + PxReal MLatStiffY; + PxReal MLongitudinalStiffnessPerUnitGravity; + PxReal MCamberStiffnessPerUnitGravity; + PxU32 MType; + PxReal MFrictionVsSlipGraph[3][2]; + PxVehicleTireDataGeneratedValues( const PxVehicleTireData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, RecipLongitudinalStiffnessPerUnitGravity, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, FrictionVsSlipGraphRecipx1Minusx0, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, FrictionVsSlipGraphRecipx2Minusx1, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MLatStiffX, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MLatStiffY, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MLongitudinalStiffnessPerUnitGravity, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MCamberStiffnessPerUnitGravity, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MType, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MFrictionVsSlipGraph, PxVehicleTireDataGeneratedValues) + struct PxVehicleTireDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleTireData"; } + PxReadOnlyPropertyInfo RecipLongitudinalStiffnessPerUnitGravity; + PxReadOnlyPropertyInfo FrictionVsSlipGraphRecipx1Minusx0; + PxReadOnlyPropertyInfo FrictionVsSlipGraphRecipx2Minusx1; + PxPropertyInfo MLatStiffX; + PxPropertyInfo MLatStiffY; + PxPropertyInfo MLongitudinalStiffnessPerUnitGravity; + PxPropertyInfo MCamberStiffnessPerUnitGravity; + PxPropertyInfo MType; + MFrictionVsSlipGraphProperty MFrictionVsSlipGraph; + + PxVehicleTireDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 9; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( RecipLongitudinalStiffnessPerUnitGravity, inStartIndex + 0 );; + inOperator( FrictionVsSlipGraphRecipx1Minusx0, inStartIndex + 1 );; + inOperator( FrictionVsSlipGraphRecipx2Minusx1, inStartIndex + 2 );; + inOperator( MLatStiffX, inStartIndex + 3 );; + inOperator( MLatStiffY, inStartIndex + 4 );; + inOperator( MLongitudinalStiffnessPerUnitGravity, inStartIndex + 5 );; + inOperator( MCamberStiffnessPerUnitGravity, inStartIndex + 6 );; + inOperator( MType, inStartIndex + 7 );; + inOperator( MFrictionVsSlipGraph, inStartIndex + 8 );; + return 9 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleTireDataGeneratedInfo Info; + const PxVehicleTireDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleWheels4SimData; + struct PxVehicleWheels4SimDataGeneratedValues + { + const PxReal * TireRestLoadsArray; + const PxReal * RecipTireRestLoadsArray; + PxVehicleWheels4SimDataGeneratedValues( const PxVehicleWheels4SimData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels4SimData, TireRestLoadsArray, PxVehicleWheels4SimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels4SimData, RecipTireRestLoadsArray, PxVehicleWheels4SimDataGeneratedValues) + struct PxVehicleWheels4SimDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleWheels4SimData"; } + PxReadOnlyPropertyInfo TireRestLoadsArray; + PxReadOnlyPropertyInfo RecipTireRestLoadsArray; + + PxVehicleWheels4SimDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( TireRestLoadsArray, inStartIndex + 0 );; + inOperator( RecipTireRestLoadsArray, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleWheels4SimDataGeneratedInfo Info; + const PxVehicleWheels4SimDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleWheelsSimData; + struct PxVehicleWheelsSimDataGeneratedValues + { + PxVehicleTireLoadFilterData TireLoadFilterData; + PxF32 MinLongSlipDenominator; + PxF32 ThresholdLongSpeed; + PxU32 LowForwardSpeedSubStepCount; + PxU32 HighForwardSpeedSubStepCount; + PxVehicleWheelsSimDataGeneratedValues( const PxVehicleWheelsSimData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsSimData, TireLoadFilterData, PxVehicleWheelsSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsSimData, MinLongSlipDenominator, PxVehicleWheelsSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsSimData, ThresholdLongSpeed, PxVehicleWheelsSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsSimData, LowForwardSpeedSubStepCount, PxVehicleWheelsSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsSimData, HighForwardSpeedSubStepCount, PxVehicleWheelsSimDataGeneratedValues) + struct PxVehicleWheelsSimDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleWheelsSimData"; } + PxWriteOnlyPropertyInfo ChassisMass; + PxExtendedIndexedPropertyInfo SuspensionData; + PxExtendedIndexedPropertyInfo WheelData; + PxExtendedIndexedPropertyInfo TireData; + PxExtendedIndexedPropertyInfo SuspTravelDirection; + PxExtendedIndexedPropertyInfo SuspForceAppPointOffset; + PxExtendedIndexedPropertyInfo TireForceAppPointOffset; + PxExtendedIndexedPropertyInfo WheelCentreOffset; + PxExtendedIndexedPropertyInfo WheelShapeMapping; + PxExtendedIndexedPropertyInfo SceneQueryFilterData; + PxExtendedIndexedPropertyInfo AntiRollBarData; + PxPropertyInfo TireLoadFilterData; + PxPropertyInfo MinLongSlipDenominator; + PxPropertyInfo ThresholdLongSpeed; + PxPropertyInfo LowForwardSpeedSubStepCount; + PxPropertyInfo HighForwardSpeedSubStepCount; + PxExtendedIndexedPropertyInfo WheelEnabledState; + + PxVehicleWheelsSimDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 17; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ChassisMass, inStartIndex + 0 );; + inOperator( SuspensionData, inStartIndex + 1 );; + inOperator( WheelData, inStartIndex + 2 );; + inOperator( TireData, inStartIndex + 3 );; + inOperator( SuspTravelDirection, inStartIndex + 4 );; + inOperator( SuspForceAppPointOffset, inStartIndex + 5 );; + inOperator( TireForceAppPointOffset, inStartIndex + 6 );; + inOperator( WheelCentreOffset, inStartIndex + 7 );; + inOperator( WheelShapeMapping, inStartIndex + 8 );; + inOperator( SceneQueryFilterData, inStartIndex + 9 );; + inOperator( AntiRollBarData, inStartIndex + 10 );; + inOperator( TireLoadFilterData, inStartIndex + 11 );; + inOperator( MinLongSlipDenominator, inStartIndex + 12 );; + inOperator( ThresholdLongSpeed, inStartIndex + 13 );; + inOperator( LowForwardSpeedSubStepCount, inStartIndex + 14 );; + inOperator( HighForwardSpeedSubStepCount, inStartIndex + 15 );; + inOperator( WheelEnabledState, inStartIndex + 16 );; + return 17 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleWheelsSimDataGeneratedInfo Info; + const PxVehicleWheelsSimDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleWheelsDynData; + struct PxVehicleWheelsDynDataGeneratedValues + { + PxVehicleWheels4DynData * Wheel4DynData; + PxVehicleWheelsDynDataGeneratedValues( const PxVehicleWheelsDynData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsDynData, Wheel4DynData, PxVehicleWheelsDynDataGeneratedValues) + struct PxVehicleWheelsDynDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleWheelsDynData"; } + PxWriteOnlyPropertyInfo TireForceShaderFunction; + PxExtendedIndexedPropertyInfo WheelRotationSpeed; + PxExtendedIndexedPropertyInfo WheelRotationAngle; + PxReadOnlyPropertyInfo Wheel4DynData; + + PxVehicleWheelsDynDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( TireForceShaderFunction, inStartIndex + 0 );; + inOperator( WheelRotationSpeed, inStartIndex + 1 );; + inOperator( WheelRotationAngle, inStartIndex + 2 );; + inOperator( Wheel4DynData, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleWheelsDynDataGeneratedInfo Info; + const PxVehicleWheelsDynDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleWheels; + struct PxVehicleWheelsGeneratedValues + { + PxU32 VehicleType; + const PxRigidDynamic * RigidDynamicActor; + const char * ConcreteTypeName; + PxVehicleWheelsSimData MWheelsSimData; + PxVehicleWheelsDynData MWheelsDynData; + PxVehicleWheelsGeneratedValues( const PxVehicleWheels* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels, VehicleType, PxVehicleWheelsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels, RigidDynamicActor, PxVehicleWheelsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels, ConcreteTypeName, PxVehicleWheelsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels, MWheelsSimData, PxVehicleWheelsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels, MWheelsDynData, PxVehicleWheelsGeneratedValues) + struct PxVehicleWheelsGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleWheels"; } + PxReadOnlyPropertyInfo VehicleType; + PxReadOnlyPropertyInfo RigidDynamicActor; + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MWheelsSimData; + PxPropertyInfo MWheelsDynData; + + PxVehicleWheelsGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( VehicleType, inStartIndex + 0 );; + inOperator( RigidDynamicActor, inStartIndex + 1 );; + inOperator( ConcreteTypeName, inStartIndex + 2 );; + inOperator( MWheelsSimData, inStartIndex + 3 );; + inOperator( MWheelsDynData, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleWheelsGeneratedInfo Info; + const PxVehicleWheelsGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDriveDynData; + struct PxVehicleDriveDynDataGeneratedValues + { + _Bool GearUp; + _Bool GearDown; + _Bool UseAutoGears; + PxU32 CurrentGear; + PxU32 TargetGear; + PxReal EngineRotationSpeed; + PxU32 GearChange; + PxReal GearSwitchTime; + PxReal AutoBoxSwitchTime; + _Bool MUseAutoGears; + _Bool MGearUpPressed; + _Bool MGearDownPressed; + PxU32 MCurrentGear; + PxU32 MTargetGear; + PxReal MEnginespeed; + PxReal MGearSwitchTime; + PxReal MAutoBoxSwitchTime; + PxVehicleDriveDynDataGeneratedValues( const PxVehicleDriveDynData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, GearUp, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, GearDown, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, UseAutoGears, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, CurrentGear, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, TargetGear, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, EngineRotationSpeed, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, GearChange, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, GearSwitchTime, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, AutoBoxSwitchTime, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MUseAutoGears, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MGearUpPressed, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MGearDownPressed, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MCurrentGear, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MTargetGear, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MEnginespeed, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MGearSwitchTime, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MAutoBoxSwitchTime, PxVehicleDriveDynDataGeneratedValues) + struct PxVehicleDriveDynDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleDriveDynData"; } + PxExtendedIndexedPropertyInfo AnalogInput; + PxPropertyInfo GearUp; + PxPropertyInfo GearDown; + PxPropertyInfo UseAutoGears; + PxPropertyInfo CurrentGear; + PxPropertyInfo TargetGear; + PxPropertyInfo EngineRotationSpeed; + PxPropertyInfo GearChange; + PxPropertyInfo GearSwitchTime; + PxPropertyInfo AutoBoxSwitchTime; + PxPropertyInfo MUseAutoGears; + PxPropertyInfo MGearUpPressed; + PxPropertyInfo MGearDownPressed; + PxPropertyInfo MCurrentGear; + PxPropertyInfo MTargetGear; + PxPropertyInfo MEnginespeed; + PxPropertyInfo MGearSwitchTime; + PxPropertyInfo MAutoBoxSwitchTime; + + PxVehicleDriveDynDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 18; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( AnalogInput, inStartIndex + 0 );; + inOperator( GearUp, inStartIndex + 1 );; + inOperator( GearDown, inStartIndex + 2 );; + inOperator( UseAutoGears, inStartIndex + 3 );; + inOperator( CurrentGear, inStartIndex + 4 );; + inOperator( TargetGear, inStartIndex + 5 );; + inOperator( EngineRotationSpeed, inStartIndex + 6 );; + inOperator( GearChange, inStartIndex + 7 );; + inOperator( GearSwitchTime, inStartIndex + 8 );; + inOperator( AutoBoxSwitchTime, inStartIndex + 9 );; + inOperator( MUseAutoGears, inStartIndex + 10 );; + inOperator( MGearUpPressed, inStartIndex + 11 );; + inOperator( MGearDownPressed, inStartIndex + 12 );; + inOperator( MCurrentGear, inStartIndex + 13 );; + inOperator( MTargetGear, inStartIndex + 14 );; + inOperator( MEnginespeed, inStartIndex + 15 );; + inOperator( MGearSwitchTime, inStartIndex + 16 );; + inOperator( MAutoBoxSwitchTime, inStartIndex + 17 );; + return 18 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveDynDataGeneratedInfo Info; + const PxVehicleDriveDynDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDriveSimData; + struct PxVehicleDriveSimDataGeneratedValues + { + PxVehicleEngineData EngineData; + PxVehicleGearsData GearsData; + PxVehicleClutchData ClutchData; + PxVehicleAutoBoxData AutoBoxData; + PxVehicleDriveSimDataGeneratedValues( const PxVehicleDriveSimData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData, EngineData, PxVehicleDriveSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData, GearsData, PxVehicleDriveSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData, ClutchData, PxVehicleDriveSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData, AutoBoxData, PxVehicleDriveSimDataGeneratedValues) + struct PxVehicleDriveSimDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleDriveSimData"; } + PxPropertyInfo EngineData; + PxPropertyInfo GearsData; + PxPropertyInfo ClutchData; + PxPropertyInfo AutoBoxData; + + PxVehicleDriveSimDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( EngineData, inStartIndex + 0 );; + inOperator( GearsData, inStartIndex + 1 );; + inOperator( ClutchData, inStartIndex + 2 );; + inOperator( AutoBoxData, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveSimDataGeneratedInfo Info; + const PxVehicleDriveSimDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDriveSimData4W; + struct PxVehicleDriveSimData4WGeneratedValues + : PxVehicleDriveSimDataGeneratedValues { + PxVehicleDifferential4WData DiffData; + PxVehicleAckermannGeometryData AckermannGeometryData; + PxVehicleDriveSimData4WGeneratedValues( const PxVehicleDriveSimData4W* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData4W, DiffData, PxVehicleDriveSimData4WGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData4W, AckermannGeometryData, PxVehicleDriveSimData4WGeneratedValues) + struct PxVehicleDriveSimData4WGeneratedInfo + : PxVehicleDriveSimDataGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDriveSimData4W"; } + PxPropertyInfo DiffData; + PxPropertyInfo AckermannGeometryData; + + PxVehicleDriveSimData4WGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleDriveSimDataGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleDriveSimDataGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleDriveSimDataGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( DiffData, inStartIndex + 0 );; + inOperator( AckermannGeometryData, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveSimData4WGeneratedInfo Info; + const PxVehicleDriveSimData4WGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDrive; + struct PxVehicleDriveGeneratedValues + : PxVehicleWheelsGeneratedValues { + const char * ConcreteTypeName; + PxVehicleDriveDynData MDriveDynData; + PxVehicleDriveGeneratedValues( const PxVehicleDrive* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDrive, ConcreteTypeName, PxVehicleDriveGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDrive, MDriveDynData, PxVehicleDriveGeneratedValues) + struct PxVehicleDriveGeneratedInfo + : PxVehicleWheelsGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDrive"; } + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MDriveDynData; + + PxVehicleDriveGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleWheelsGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleWheelsGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleWheelsGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ConcreteTypeName, inStartIndex + 0 );; + inOperator( MDriveDynData, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveGeneratedInfo Info; + const PxVehicleDriveGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDrive4W; + struct PxVehicleDrive4WGeneratedValues + : PxVehicleDriveGeneratedValues { + const char * ConcreteTypeName; + PxVehicleDriveSimData4W MDriveSimData; + PxVehicleDrive4WGeneratedValues( const PxVehicleDrive4W* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDrive4W, ConcreteTypeName, PxVehicleDrive4WGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDrive4W, MDriveSimData, PxVehicleDrive4WGeneratedValues) + struct PxVehicleDrive4WGeneratedInfo + : PxVehicleDriveGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDrive4W"; } + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MDriveSimData; + + PxVehicleDrive4WGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleDriveGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleDriveGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleDriveGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ConcreteTypeName, inStartIndex + 0 );; + inOperator( MDriveSimData, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDrive4WGeneratedInfo Info; + const PxVehicleDrive4WGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxVehicleDriveTankControlModel__EnumConversion[] = { + { "eSTANDARD", static_cast( physx::PxVehicleDriveTankControlModel::eSTANDARD ) }, + { "eSPECIAL", static_cast( physx::PxVehicleDriveTankControlModel::eSPECIAL ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxVehicleDriveTankControlModel::Enum > { PxEnumTraits() : NameConversion( g_physx__PxVehicleDriveTankControlModel__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxVehicleDriveTank; + struct PxVehicleDriveTankGeneratedValues + : PxVehicleDriveGeneratedValues { + PxVehicleDriveTankControlModel::Enum DriveModel; + const char * ConcreteTypeName; + PxVehicleDriveSimData MDriveSimData; + PxVehicleDriveTankGeneratedValues( const PxVehicleDriveTank* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveTank, DriveModel, PxVehicleDriveTankGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveTank, ConcreteTypeName, PxVehicleDriveTankGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveTank, MDriveSimData, PxVehicleDriveTankGeneratedValues) + struct PxVehicleDriveTankGeneratedInfo + : PxVehicleDriveGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDriveTank"; } + PxPropertyInfo DriveModel; + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MDriveSimData; + + PxVehicleDriveTankGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleDriveGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleDriveGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleDriveGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( DriveModel, inStartIndex + 0 );; + inOperator( ConcreteTypeName, inStartIndex + 1 );; + inOperator( MDriveSimData, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveTankGeneratedInfo Info; + const PxVehicleDriveTankGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDriveSimDataNW; + struct PxVehicleDriveSimDataNWGeneratedValues + : PxVehicleDriveSimDataGeneratedValues { + PxVehicleDifferentialNWData DiffData; + PxVehicleDriveSimDataNWGeneratedValues( const PxVehicleDriveSimDataNW* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimDataNW, DiffData, PxVehicleDriveSimDataNWGeneratedValues) + struct PxVehicleDriveSimDataNWGeneratedInfo + : PxVehicleDriveSimDataGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDriveSimDataNW"; } + PxPropertyInfo DiffData; + + PxVehicleDriveSimDataNWGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleDriveSimDataGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleDriveSimDataGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleDriveSimDataGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( DiffData, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveSimDataNWGeneratedInfo Info; + const PxVehicleDriveSimDataNWGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDriveNW; + struct PxVehicleDriveNWGeneratedValues + : PxVehicleDriveGeneratedValues { + const char * ConcreteTypeName; + PxVehicleDriveSimDataNW MDriveSimData; + PxVehicleDriveNWGeneratedValues( const PxVehicleDriveNW* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveNW, ConcreteTypeName, PxVehicleDriveNWGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveNW, MDriveSimData, PxVehicleDriveNWGeneratedValues) + struct PxVehicleDriveNWGeneratedInfo + : PxVehicleDriveGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDriveNW"; } + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MDriveSimData; + + PxVehicleDriveNWGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleDriveGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleDriveGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleDriveGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ConcreteTypeName, inStartIndex + 0 );; + inOperator( MDriveSimData, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveNWGeneratedInfo Info; + const PxVehicleDriveNWGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleNoDrive; + struct PxVehicleNoDriveGeneratedValues + : PxVehicleWheelsGeneratedValues { + const char * ConcreteTypeName; + PxVehicleNoDriveGeneratedValues( const PxVehicleNoDrive* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleNoDrive, ConcreteTypeName, PxVehicleNoDriveGeneratedValues) + struct PxVehicleNoDriveGeneratedInfo + : PxVehicleWheelsGeneratedInfo + { + static const char* getClassName() { return "PxVehicleNoDrive"; } + PxExtendedIndexedPropertyInfo BrakeTorque; + PxExtendedIndexedPropertyInfo DriveTorque; + PxExtendedIndexedPropertyInfo SteerAngle; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxVehicleNoDriveGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleWheelsGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleWheelsGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleWheelsGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( BrakeTorque, inStartIndex + 0 );; + inOperator( DriveTorque, inStartIndex + 1 );; + inOperator( SteerAngle, inStartIndex + 2 );; + inOperator( ConcreteTypeName, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleNoDriveGeneratedInfo Info; + const PxVehicleNoDriveGeneratedInfo* getInfo() { return &Info; } + }; + + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON +#undef PX_PROPERTY_INFO_NAME diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h new file mode 100644 index 000000000..35d2b4760 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h @@ -0,0 +1,97 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_METADATAOBJECTS_H +#define PX_VEHICLE_METADATAOBJECTS_H +#include "PxPhysicsAPI.h" +#include "extensions/PxExtensionsAPI.h" +#include "PxMetaDataObjects.h" +#include "PxExtensionMetaDataObjects.h" + +/** \addtogroup physics +@{ +*/ + +namespace physx +{ + +struct PxVehiclePropertyInfoName +{ + enum Enum + { + Unnamed = PxExtensionsPropertyInfoName::LastPxPropertyInfoName, +#include "PxVehicleAutoGeneratedMetaDataObjectNames.h" + LastPxPropertyInfoName + }; +}; + +#define DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( type, prop, valueStruct ) \ + template<> struct PxPropertyToValueStructMemberMap< PxVehiclePropertyInfoName::type##_##prop > \ + { \ + PxU32 Offset; \ + PxPropertyToValueStructMemberMap< PxVehiclePropertyInfoName::type##_##prop >() : Offset( PX_OFFSET_OF_RT( valueStruct, prop ) ) {} \ + template void visitProp( TOperator inOperator, valueStruct& inStruct ) { inOperator( inStruct.prop ); } \ + }; + +struct MFrictionVsSlipGraphProperty : public PxExtendedDualIndexedPropertyInfo +{ + PX_PHYSX_CORE_API MFrictionVsSlipGraphProperty(); +}; + +struct MTorqueCurveProperty : public PxFixedSizeLookupTablePropertyInfo +{ + PX_PHYSX_CORE_API MTorqueCurveProperty(); +}; + +#if PX_CLANG +#if __has_warning("-Wshadow-field") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wshadow-field" +#endif +#endif +#include "PxVehicleAutoGeneratedMetaDataObjects.h" +#if PX_CLANG +#if __has_warning("-Wshadow-field") +#pragma clang diagnostic pop +#endif +#endif + +#undef DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp new file mode 100644 index 000000000..7056b9989 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp @@ -0,0 +1,727 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#include "PxVehicleMetaDataObjects.h" +#include "PxMetaDataCppPrefix.h" +#include "PxVehicleSuspWheelTire4.h" +using namespace physx; +inline PxVec3 getPxVehicleChassisDataMMOI( const PxVehicleChassisData* inOwner ) { return inOwner->mMOI; } +inline void setPxVehicleChassisDataMMOI( PxVehicleChassisData* inOwner, PxVec3 inData) { inOwner->mMOI = inData; } +inline PxReal getPxVehicleChassisDataMMass( const PxVehicleChassisData* inOwner ) { return inOwner->mMass; } +inline void setPxVehicleChassisDataMMass( PxVehicleChassisData* inOwner, PxReal inData) { inOwner->mMass = inData; } +inline PxVec3 getPxVehicleChassisDataMCMOffset( const PxVehicleChassisData* inOwner ) { return inOwner->mCMOffset; } +inline void setPxVehicleChassisDataMCMOffset( PxVehicleChassisData* inOwner, PxVec3 inData) { inOwner->mCMOffset = inData; } + PxVehicleChassisDataGeneratedInfo::PxVehicleChassisDataGeneratedInfo() + : MMOI( "MMOI", setPxVehicleChassisDataMMOI, getPxVehicleChassisDataMMOI ) + , MMass( "MMass", setPxVehicleChassisDataMMass, getPxVehicleChassisDataMMass ) + , MCMOffset( "MCMOffset", setPxVehicleChassisDataMCMOffset, getPxVehicleChassisDataMCMOffset ) +{} + PxVehicleChassisDataGeneratedValues::PxVehicleChassisDataGeneratedValues( const PxVehicleChassisData* inSource ) + :MMOI( inSource->mMOI ) + ,MMass( inSource->mMass ) + ,MCMOffset( inSource->mCMOffset ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleEngineData_RecipMOI( const PxVehicleEngineData* inObj ) { return inObj->getRecipMOI(); } +PxReal getPxVehicleEngineData_RecipMaxOmega( const PxVehicleEngineData* inObj ) { return inObj->getRecipMaxOmega(); } +inline PxReal getPxVehicleEngineDataMMOI( const PxVehicleEngineData* inOwner ) { return inOwner->mMOI; } +inline void setPxVehicleEngineDataMMOI( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mMOI = inData; } +inline PxReal getPxVehicleEngineDataMPeakTorque( const PxVehicleEngineData* inOwner ) { return inOwner->mPeakTorque; } +inline void setPxVehicleEngineDataMPeakTorque( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mPeakTorque = inData; } +inline PxReal getPxVehicleEngineDataMMaxOmega( const PxVehicleEngineData* inOwner ) { return inOwner->mMaxOmega; } +inline void setPxVehicleEngineDataMMaxOmega( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mMaxOmega = inData; } +inline PxReal getPxVehicleEngineDataMDampingRateFullThrottle( const PxVehicleEngineData* inOwner ) { return inOwner->mDampingRateFullThrottle; } +inline void setPxVehicleEngineDataMDampingRateFullThrottle( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mDampingRateFullThrottle = inData; } +inline PxReal getPxVehicleEngineDataMDampingRateZeroThrottleClutchEngaged( const PxVehicleEngineData* inOwner ) { return inOwner->mDampingRateZeroThrottleClutchEngaged; } +inline void setPxVehicleEngineDataMDampingRateZeroThrottleClutchEngaged( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mDampingRateZeroThrottleClutchEngaged = inData; } +inline PxReal getPxVehicleEngineDataMDampingRateZeroThrottleClutchDisengaged( const PxVehicleEngineData* inOwner ) { return inOwner->mDampingRateZeroThrottleClutchDisengaged; } +inline void setPxVehicleEngineDataMDampingRateZeroThrottleClutchDisengaged( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mDampingRateZeroThrottleClutchDisengaged = inData; } + PxVehicleEngineDataGeneratedInfo::PxVehicleEngineDataGeneratedInfo() + : RecipMOI( "RecipMOI", getPxVehicleEngineData_RecipMOI) + , RecipMaxOmega( "RecipMaxOmega", getPxVehicleEngineData_RecipMaxOmega) + , MMOI( "MMOI", setPxVehicleEngineDataMMOI, getPxVehicleEngineDataMMOI ) + , MPeakTorque( "MPeakTorque", setPxVehicleEngineDataMPeakTorque, getPxVehicleEngineDataMPeakTorque ) + , MMaxOmega( "MMaxOmega", setPxVehicleEngineDataMMaxOmega, getPxVehicleEngineDataMMaxOmega ) + , MDampingRateFullThrottle( "MDampingRateFullThrottle", setPxVehicleEngineDataMDampingRateFullThrottle, getPxVehicleEngineDataMDampingRateFullThrottle ) + , MDampingRateZeroThrottleClutchEngaged( "MDampingRateZeroThrottleClutchEngaged", setPxVehicleEngineDataMDampingRateZeroThrottleClutchEngaged, getPxVehicleEngineDataMDampingRateZeroThrottleClutchEngaged ) + , MDampingRateZeroThrottleClutchDisengaged( "MDampingRateZeroThrottleClutchDisengaged", setPxVehicleEngineDataMDampingRateZeroThrottleClutchDisengaged, getPxVehicleEngineDataMDampingRateZeroThrottleClutchDisengaged ) +{} + PxVehicleEngineDataGeneratedValues::PxVehicleEngineDataGeneratedValues( const PxVehicleEngineData* inSource ) + :RecipMOI( getPxVehicleEngineData_RecipMOI( inSource ) ) + ,RecipMaxOmega( getPxVehicleEngineData_RecipMaxOmega( inSource ) ) + ,MMOI( inSource->mMOI ) + ,MPeakTorque( inSource->mPeakTorque ) + ,MMaxOmega( inSource->mMaxOmega ) + ,MDampingRateFullThrottle( inSource->mDampingRateFullThrottle ) + ,MDampingRateZeroThrottleClutchEngaged( inSource->mDampingRateZeroThrottleClutchEngaged ) + ,MDampingRateZeroThrottleClutchDisengaged( inSource->mDampingRateZeroThrottleClutchDisengaged ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleGearsData_GearRatio( PxVehicleGearsData* inObj, PxVehicleGearsData::Enum inIndex, PxReal inArg ){ inObj->setGearRatio( inIndex, inArg ); } +PxReal getPxVehicleGearsData_GearRatio( const PxVehicleGearsData* inObj, PxVehicleGearsData::Enum inIndex ) { return inObj->getGearRatio( inIndex ); } +inline PxReal getPxVehicleGearsDataMFinalRatio( const PxVehicleGearsData* inOwner ) { return inOwner->mFinalRatio; } +inline void setPxVehicleGearsDataMFinalRatio( PxVehicleGearsData* inOwner, PxReal inData) { inOwner->mFinalRatio = inData; } +inline PxU32 getPxVehicleGearsDataMNbRatios( const PxVehicleGearsData* inOwner ) { return inOwner->mNbRatios; } +inline void setPxVehicleGearsDataMNbRatios( PxVehicleGearsData* inOwner, PxU32 inData) { inOwner->mNbRatios = inData; } +inline PxReal getPxVehicleGearsDataMSwitchTime( const PxVehicleGearsData* inOwner ) { return inOwner->mSwitchTime; } +inline void setPxVehicleGearsDataMSwitchTime( PxVehicleGearsData* inOwner, PxReal inData) { inOwner->mSwitchTime = inData; } + PxVehicleGearsDataGeneratedInfo::PxVehicleGearsDataGeneratedInfo() + : GearRatio( "GearRatio", setPxVehicleGearsData_GearRatio, getPxVehicleGearsData_GearRatio) + , MFinalRatio( "MFinalRatio", setPxVehicleGearsDataMFinalRatio, getPxVehicleGearsDataMFinalRatio ) + , MNbRatios( "MNbRatios", setPxVehicleGearsDataMNbRatios, getPxVehicleGearsDataMNbRatios ) + , MSwitchTime( "MSwitchTime", setPxVehicleGearsDataMSwitchTime, getPxVehicleGearsDataMSwitchTime ) +{} + PxVehicleGearsDataGeneratedValues::PxVehicleGearsDataGeneratedValues( const PxVehicleGearsData* inSource ) + :MFinalRatio( inSource->mFinalRatio ) + ,MNbRatios( inSource->mNbRatios ) + ,MSwitchTime( inSource->mSwitchTime ) +{ + PX_UNUSED(inSource); + for ( PxU32 idx = 0; idx < static_cast( physx::PxVehicleGearsData::eGEARSRATIO_COUNT ); ++idx ) + GearRatio[idx] = getPxVehicleGearsData_GearRatio( inSource, static_cast< PxVehicleGearsData::Enum >( idx ) ); +} +void setPxVehicleAutoBoxData_Latency( PxVehicleAutoBoxData* inObj, const PxReal inArg){ inObj->setLatency( inArg ); } +PxReal getPxVehicleAutoBoxData_Latency( const PxVehicleAutoBoxData* inObj ) { return inObj->getLatency(); } +void setPxVehicleAutoBoxData_UpRatios( PxVehicleAutoBoxData* inObj, PxVehicleGearsData::Enum inIndex, PxReal inArg ){ inObj->setUpRatios( inIndex, inArg ); } +PxReal getPxVehicleAutoBoxData_UpRatios( const PxVehicleAutoBoxData* inObj, PxVehicleGearsData::Enum inIndex ) { return inObj->getUpRatios( inIndex ); } +void setPxVehicleAutoBoxData_DownRatios( PxVehicleAutoBoxData* inObj, PxVehicleGearsData::Enum inIndex, PxReal inArg ){ inObj->setDownRatios( inIndex, inArg ); } +PxReal getPxVehicleAutoBoxData_DownRatios( const PxVehicleAutoBoxData* inObj, PxVehicleGearsData::Enum inIndex ) { return inObj->getDownRatios( inIndex ); } + PxVehicleAutoBoxDataGeneratedInfo::PxVehicleAutoBoxDataGeneratedInfo() + : Latency( "Latency", setPxVehicleAutoBoxData_Latency, getPxVehicleAutoBoxData_Latency) + , UpRatios( "UpRatios", setPxVehicleAutoBoxData_UpRatios, getPxVehicleAutoBoxData_UpRatios) + , DownRatios( "DownRatios", setPxVehicleAutoBoxData_DownRatios, getPxVehicleAutoBoxData_DownRatios) +{} + PxVehicleAutoBoxDataGeneratedValues::PxVehicleAutoBoxDataGeneratedValues( const PxVehicleAutoBoxData* inSource ) + :Latency( getPxVehicleAutoBoxData_Latency( inSource ) ) +{ + PX_UNUSED(inSource); + for ( PxU32 idx = 0; idx < static_cast( physx::PxVehicleGearsData::eGEARSRATIO_COUNT ); ++idx ) + UpRatios[idx] = getPxVehicleAutoBoxData_UpRatios( inSource, static_cast< PxVehicleGearsData::Enum >( idx ) ); + for ( PxU32 idx = 0; idx < static_cast( physx::PxVehicleGearsData::eGEARSRATIO_COUNT ); ++idx ) + DownRatios[idx] = getPxVehicleAutoBoxData_DownRatios( inSource, static_cast< PxVehicleGearsData::Enum >( idx ) ); +} +inline PxReal getPxVehicleDifferential4WDataMFrontRearSplit( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mFrontRearSplit; } +inline void setPxVehicleDifferential4WDataMFrontRearSplit( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mFrontRearSplit = inData; } +inline PxReal getPxVehicleDifferential4WDataMFrontLeftRightSplit( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mFrontLeftRightSplit; } +inline void setPxVehicleDifferential4WDataMFrontLeftRightSplit( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mFrontLeftRightSplit = inData; } +inline PxReal getPxVehicleDifferential4WDataMRearLeftRightSplit( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mRearLeftRightSplit; } +inline void setPxVehicleDifferential4WDataMRearLeftRightSplit( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mRearLeftRightSplit = inData; } +inline PxReal getPxVehicleDifferential4WDataMCentreBias( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mCentreBias; } +inline void setPxVehicleDifferential4WDataMCentreBias( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mCentreBias = inData; } +inline PxReal getPxVehicleDifferential4WDataMFrontBias( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mFrontBias; } +inline void setPxVehicleDifferential4WDataMFrontBias( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mFrontBias = inData; } +inline PxReal getPxVehicleDifferential4WDataMRearBias( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mRearBias; } +inline void setPxVehicleDifferential4WDataMRearBias( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mRearBias = inData; } +inline PxVehicleDifferential4WData::Enum getPxVehicleDifferential4WDataMType( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mType; } +inline void setPxVehicleDifferential4WDataMType( PxVehicleDifferential4WData* inOwner, PxVehicleDifferential4WData::Enum inData) { inOwner->mType = inData; } + PxVehicleDifferential4WDataGeneratedInfo::PxVehicleDifferential4WDataGeneratedInfo() + : MFrontRearSplit( "MFrontRearSplit", setPxVehicleDifferential4WDataMFrontRearSplit, getPxVehicleDifferential4WDataMFrontRearSplit ) + , MFrontLeftRightSplit( "MFrontLeftRightSplit", setPxVehicleDifferential4WDataMFrontLeftRightSplit, getPxVehicleDifferential4WDataMFrontLeftRightSplit ) + , MRearLeftRightSplit( "MRearLeftRightSplit", setPxVehicleDifferential4WDataMRearLeftRightSplit, getPxVehicleDifferential4WDataMRearLeftRightSplit ) + , MCentreBias( "MCentreBias", setPxVehicleDifferential4WDataMCentreBias, getPxVehicleDifferential4WDataMCentreBias ) + , MFrontBias( "MFrontBias", setPxVehicleDifferential4WDataMFrontBias, getPxVehicleDifferential4WDataMFrontBias ) + , MRearBias( "MRearBias", setPxVehicleDifferential4WDataMRearBias, getPxVehicleDifferential4WDataMRearBias ) + , MType( "MType", setPxVehicleDifferential4WDataMType, getPxVehicleDifferential4WDataMType ) +{} + PxVehicleDifferential4WDataGeneratedValues::PxVehicleDifferential4WDataGeneratedValues( const PxVehicleDifferential4WData* inSource ) + :MFrontRearSplit( inSource->mFrontRearSplit ) + ,MFrontLeftRightSplit( inSource->mFrontLeftRightSplit ) + ,MRearLeftRightSplit( inSource->mRearLeftRightSplit ) + ,MCentreBias( inSource->mCentreBias ) + ,MFrontBias( inSource->mFrontBias ) + ,MRearBias( inSource->mRearBias ) + ,MType( inSource->mType ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleDifferentialNWData_DrivenWheelStatus( PxVehicleDifferentialNWData* inObj, PxU32 inArg){ inObj->setDrivenWheelStatus( inArg ); } +PxU32 getPxVehicleDifferentialNWData_DrivenWheelStatus( const PxVehicleDifferentialNWData* inObj ) { return inObj->getDrivenWheelStatus(); } + PxVehicleDifferentialNWDataGeneratedInfo::PxVehicleDifferentialNWDataGeneratedInfo() + : DrivenWheelStatus( "DrivenWheelStatus", setPxVehicleDifferentialNWData_DrivenWheelStatus, getPxVehicleDifferentialNWData_DrivenWheelStatus) +{} + PxVehicleDifferentialNWDataGeneratedValues::PxVehicleDifferentialNWDataGeneratedValues( const PxVehicleDifferentialNWData* inSource ) + :DrivenWheelStatus( getPxVehicleDifferentialNWData_DrivenWheelStatus( inSource ) ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxVehicleAckermannGeometryDataMAccuracy( const PxVehicleAckermannGeometryData* inOwner ) { return inOwner->mAccuracy; } +inline void setPxVehicleAckermannGeometryDataMAccuracy( PxVehicleAckermannGeometryData* inOwner, PxReal inData) { inOwner->mAccuracy = inData; } +inline PxReal getPxVehicleAckermannGeometryDataMFrontWidth( const PxVehicleAckermannGeometryData* inOwner ) { return inOwner->mFrontWidth; } +inline void setPxVehicleAckermannGeometryDataMFrontWidth( PxVehicleAckermannGeometryData* inOwner, PxReal inData) { inOwner->mFrontWidth = inData; } +inline PxReal getPxVehicleAckermannGeometryDataMRearWidth( const PxVehicleAckermannGeometryData* inOwner ) { return inOwner->mRearWidth; } +inline void setPxVehicleAckermannGeometryDataMRearWidth( PxVehicleAckermannGeometryData* inOwner, PxReal inData) { inOwner->mRearWidth = inData; } +inline PxReal getPxVehicleAckermannGeometryDataMAxleSeparation( const PxVehicleAckermannGeometryData* inOwner ) { return inOwner->mAxleSeparation; } +inline void setPxVehicleAckermannGeometryDataMAxleSeparation( PxVehicleAckermannGeometryData* inOwner, PxReal inData) { inOwner->mAxleSeparation = inData; } + PxVehicleAckermannGeometryDataGeneratedInfo::PxVehicleAckermannGeometryDataGeneratedInfo() + : MAccuracy( "MAccuracy", setPxVehicleAckermannGeometryDataMAccuracy, getPxVehicleAckermannGeometryDataMAccuracy ) + , MFrontWidth( "MFrontWidth", setPxVehicleAckermannGeometryDataMFrontWidth, getPxVehicleAckermannGeometryDataMFrontWidth ) + , MRearWidth( "MRearWidth", setPxVehicleAckermannGeometryDataMRearWidth, getPxVehicleAckermannGeometryDataMRearWidth ) + , MAxleSeparation( "MAxleSeparation", setPxVehicleAckermannGeometryDataMAxleSeparation, getPxVehicleAckermannGeometryDataMAxleSeparation ) +{} + PxVehicleAckermannGeometryDataGeneratedValues::PxVehicleAckermannGeometryDataGeneratedValues( const PxVehicleAckermannGeometryData* inSource ) + :MAccuracy( inSource->mAccuracy ) + ,MFrontWidth( inSource->mFrontWidth ) + ,MRearWidth( inSource->mRearWidth ) + ,MAxleSeparation( inSource->mAxleSeparation ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxVehicleClutchDataMStrength( const PxVehicleClutchData* inOwner ) { return inOwner->mStrength; } +inline void setPxVehicleClutchDataMStrength( PxVehicleClutchData* inOwner, PxReal inData) { inOwner->mStrength = inData; } +inline PxVehicleClutchAccuracyMode::Enum getPxVehicleClutchDataMAccuracyMode( const PxVehicleClutchData* inOwner ) { return inOwner->mAccuracyMode; } +inline void setPxVehicleClutchDataMAccuracyMode( PxVehicleClutchData* inOwner, PxVehicleClutchAccuracyMode::Enum inData) { inOwner->mAccuracyMode = inData; } +inline PxU32 getPxVehicleClutchDataMEstimateIterations( const PxVehicleClutchData* inOwner ) { return inOwner->mEstimateIterations; } +inline void setPxVehicleClutchDataMEstimateIterations( PxVehicleClutchData* inOwner, PxU32 inData) { inOwner->mEstimateIterations = inData; } + PxVehicleClutchDataGeneratedInfo::PxVehicleClutchDataGeneratedInfo() + : MStrength( "MStrength", setPxVehicleClutchDataMStrength, getPxVehicleClutchDataMStrength ) + , MAccuracyMode( "MAccuracyMode", setPxVehicleClutchDataMAccuracyMode, getPxVehicleClutchDataMAccuracyMode ) + , MEstimateIterations( "MEstimateIterations", setPxVehicleClutchDataMEstimateIterations, getPxVehicleClutchDataMEstimateIterations ) +{} + PxVehicleClutchDataGeneratedValues::PxVehicleClutchDataGeneratedValues( const PxVehicleClutchData* inSource ) + :MStrength( inSource->mStrength ) + ,MAccuracyMode( inSource->mAccuracyMode ) + ,MEstimateIterations( inSource->mEstimateIterations ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleTireLoadFilterData_Denominator( const PxVehicleTireLoadFilterData* inObj ) { return inObj->getDenominator(); } +inline PxReal getPxVehicleTireLoadFilterDataMMinNormalisedLoad( const PxVehicleTireLoadFilterData* inOwner ) { return inOwner->mMinNormalisedLoad; } +inline void setPxVehicleTireLoadFilterDataMMinNormalisedLoad( PxVehicleTireLoadFilterData* inOwner, PxReal inData) { inOwner->mMinNormalisedLoad = inData; } +inline PxReal getPxVehicleTireLoadFilterDataMMinFilteredNormalisedLoad( const PxVehicleTireLoadFilterData* inOwner ) { return inOwner->mMinFilteredNormalisedLoad; } +inline void setPxVehicleTireLoadFilterDataMMinFilteredNormalisedLoad( PxVehicleTireLoadFilterData* inOwner, PxReal inData) { inOwner->mMinFilteredNormalisedLoad = inData; } +inline PxReal getPxVehicleTireLoadFilterDataMMaxNormalisedLoad( const PxVehicleTireLoadFilterData* inOwner ) { return inOwner->mMaxNormalisedLoad; } +inline void setPxVehicleTireLoadFilterDataMMaxNormalisedLoad( PxVehicleTireLoadFilterData* inOwner, PxReal inData) { inOwner->mMaxNormalisedLoad = inData; } +inline PxReal getPxVehicleTireLoadFilterDataMMaxFilteredNormalisedLoad( const PxVehicleTireLoadFilterData* inOwner ) { return inOwner->mMaxFilteredNormalisedLoad; } +inline void setPxVehicleTireLoadFilterDataMMaxFilteredNormalisedLoad( PxVehicleTireLoadFilterData* inOwner, PxReal inData) { inOwner->mMaxFilteredNormalisedLoad = inData; } + PxVehicleTireLoadFilterDataGeneratedInfo::PxVehicleTireLoadFilterDataGeneratedInfo() + : Denominator( "Denominator", getPxVehicleTireLoadFilterData_Denominator) + , MMinNormalisedLoad( "MMinNormalisedLoad", setPxVehicleTireLoadFilterDataMMinNormalisedLoad, getPxVehicleTireLoadFilterDataMMinNormalisedLoad ) + , MMinFilteredNormalisedLoad( "MMinFilteredNormalisedLoad", setPxVehicleTireLoadFilterDataMMinFilteredNormalisedLoad, getPxVehicleTireLoadFilterDataMMinFilteredNormalisedLoad ) + , MMaxNormalisedLoad( "MMaxNormalisedLoad", setPxVehicleTireLoadFilterDataMMaxNormalisedLoad, getPxVehicleTireLoadFilterDataMMaxNormalisedLoad ) + , MMaxFilteredNormalisedLoad( "MMaxFilteredNormalisedLoad", setPxVehicleTireLoadFilterDataMMaxFilteredNormalisedLoad, getPxVehicleTireLoadFilterDataMMaxFilteredNormalisedLoad ) +{} + PxVehicleTireLoadFilterDataGeneratedValues::PxVehicleTireLoadFilterDataGeneratedValues( const PxVehicleTireLoadFilterData* inSource ) + :Denominator( getPxVehicleTireLoadFilterData_Denominator( inSource ) ) + ,MMinNormalisedLoad( inSource->mMinNormalisedLoad ) + ,MMinFilteredNormalisedLoad( inSource->mMinFilteredNormalisedLoad ) + ,MMaxNormalisedLoad( inSource->mMaxNormalisedLoad ) + ,MMaxFilteredNormalisedLoad( inSource->mMaxFilteredNormalisedLoad ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleWheelData_RecipRadius( const PxVehicleWheelData* inObj ) { return inObj->getRecipRadius(); } +PxReal getPxVehicleWheelData_RecipMOI( const PxVehicleWheelData* inObj ) { return inObj->getRecipMOI(); } +inline PxReal getPxVehicleWheelDataMRadius( const PxVehicleWheelData* inOwner ) { return inOwner->mRadius; } +inline void setPxVehicleWheelDataMRadius( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mRadius = inData; } +inline PxReal getPxVehicleWheelDataMWidth( const PxVehicleWheelData* inOwner ) { return inOwner->mWidth; } +inline void setPxVehicleWheelDataMWidth( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mWidth = inData; } +inline PxReal getPxVehicleWheelDataMMass( const PxVehicleWheelData* inOwner ) { return inOwner->mMass; } +inline void setPxVehicleWheelDataMMass( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mMass = inData; } +inline PxReal getPxVehicleWheelDataMMOI( const PxVehicleWheelData* inOwner ) { return inOwner->mMOI; } +inline void setPxVehicleWheelDataMMOI( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mMOI = inData; } +inline PxReal getPxVehicleWheelDataMDampingRate( const PxVehicleWheelData* inOwner ) { return inOwner->mDampingRate; } +inline void setPxVehicleWheelDataMDampingRate( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mDampingRate = inData; } +inline PxReal getPxVehicleWheelDataMMaxBrakeTorque( const PxVehicleWheelData* inOwner ) { return inOwner->mMaxBrakeTorque; } +inline void setPxVehicleWheelDataMMaxBrakeTorque( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mMaxBrakeTorque = inData; } +inline PxReal getPxVehicleWheelDataMMaxHandBrakeTorque( const PxVehicleWheelData* inOwner ) { return inOwner->mMaxHandBrakeTorque; } +inline void setPxVehicleWheelDataMMaxHandBrakeTorque( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mMaxHandBrakeTorque = inData; } +inline PxReal getPxVehicleWheelDataMMaxSteer( const PxVehicleWheelData* inOwner ) { return inOwner->mMaxSteer; } +inline void setPxVehicleWheelDataMMaxSteer( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mMaxSteer = inData; } +inline PxReal getPxVehicleWheelDataMToeAngle( const PxVehicleWheelData* inOwner ) { return inOwner->mToeAngle; } +inline void setPxVehicleWheelDataMToeAngle( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mToeAngle = inData; } + PxVehicleWheelDataGeneratedInfo::PxVehicleWheelDataGeneratedInfo() + : RecipRadius( "RecipRadius", getPxVehicleWheelData_RecipRadius) + , RecipMOI( "RecipMOI", getPxVehicleWheelData_RecipMOI) + , MRadius( "MRadius", setPxVehicleWheelDataMRadius, getPxVehicleWheelDataMRadius ) + , MWidth( "MWidth", setPxVehicleWheelDataMWidth, getPxVehicleWheelDataMWidth ) + , MMass( "MMass", setPxVehicleWheelDataMMass, getPxVehicleWheelDataMMass ) + , MMOI( "MMOI", setPxVehicleWheelDataMMOI, getPxVehicleWheelDataMMOI ) + , MDampingRate( "MDampingRate", setPxVehicleWheelDataMDampingRate, getPxVehicleWheelDataMDampingRate ) + , MMaxBrakeTorque( "MMaxBrakeTorque", setPxVehicleWheelDataMMaxBrakeTorque, getPxVehicleWheelDataMMaxBrakeTorque ) + , MMaxHandBrakeTorque( "MMaxHandBrakeTorque", setPxVehicleWheelDataMMaxHandBrakeTorque, getPxVehicleWheelDataMMaxHandBrakeTorque ) + , MMaxSteer( "MMaxSteer", setPxVehicleWheelDataMMaxSteer, getPxVehicleWheelDataMMaxSteer ) + , MToeAngle( "MToeAngle", setPxVehicleWheelDataMToeAngle, getPxVehicleWheelDataMToeAngle ) +{} + PxVehicleWheelDataGeneratedValues::PxVehicleWheelDataGeneratedValues( const PxVehicleWheelData* inSource ) + :RecipRadius( getPxVehicleWheelData_RecipRadius( inSource ) ) + ,RecipMOI( getPxVehicleWheelData_RecipMOI( inSource ) ) + ,MRadius( inSource->mRadius ) + ,MWidth( inSource->mWidth ) + ,MMass( inSource->mMass ) + ,MMOI( inSource->mMOI ) + ,MDampingRate( inSource->mDampingRate ) + ,MMaxBrakeTorque( inSource->mMaxBrakeTorque ) + ,MMaxHandBrakeTorque( inSource->mMaxHandBrakeTorque ) + ,MMaxSteer( inSource->mMaxSteer ) + ,MToeAngle( inSource->mToeAngle ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleSuspensionData_RecipMaxCompression( const PxVehicleSuspensionData* inObj ) { return inObj->getRecipMaxCompression(); } +PxReal getPxVehicleSuspensionData_RecipMaxDroop( const PxVehicleSuspensionData* inObj ) { return inObj->getRecipMaxDroop(); } +void setPxVehicleSuspensionData_MassAndPreserveNaturalFrequency( PxVehicleSuspensionData* inObj, const PxReal inArg){ inObj->setMassAndPreserveNaturalFrequency( inArg ); } +inline PxReal getPxVehicleSuspensionDataMSpringStrength( const PxVehicleSuspensionData* inOwner ) { return inOwner->mSpringStrength; } +inline void setPxVehicleSuspensionDataMSpringStrength( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mSpringStrength = inData; } +inline PxReal getPxVehicleSuspensionDataMSpringDamperRate( const PxVehicleSuspensionData* inOwner ) { return inOwner->mSpringDamperRate; } +inline void setPxVehicleSuspensionDataMSpringDamperRate( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mSpringDamperRate = inData; } +inline PxReal getPxVehicleSuspensionDataMMaxCompression( const PxVehicleSuspensionData* inOwner ) { return inOwner->mMaxCompression; } +inline void setPxVehicleSuspensionDataMMaxCompression( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mMaxCompression = inData; } +inline PxReal getPxVehicleSuspensionDataMMaxDroop( const PxVehicleSuspensionData* inOwner ) { return inOwner->mMaxDroop; } +inline void setPxVehicleSuspensionDataMMaxDroop( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mMaxDroop = inData; } +inline PxReal getPxVehicleSuspensionDataMSprungMass( const PxVehicleSuspensionData* inOwner ) { return inOwner->mSprungMass; } +inline void setPxVehicleSuspensionDataMSprungMass( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mSprungMass = inData; } +inline PxReal getPxVehicleSuspensionDataMCamberAtRest( const PxVehicleSuspensionData* inOwner ) { return inOwner->mCamberAtRest; } +inline void setPxVehicleSuspensionDataMCamberAtRest( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mCamberAtRest = inData; } +inline PxReal getPxVehicleSuspensionDataMCamberAtMaxCompression( const PxVehicleSuspensionData* inOwner ) { return inOwner->mCamberAtMaxCompression; } +inline void setPxVehicleSuspensionDataMCamberAtMaxCompression( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mCamberAtMaxCompression = inData; } +inline PxReal getPxVehicleSuspensionDataMCamberAtMaxDroop( const PxVehicleSuspensionData* inOwner ) { return inOwner->mCamberAtMaxDroop; } +inline void setPxVehicleSuspensionDataMCamberAtMaxDroop( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mCamberAtMaxDroop = inData; } + PxVehicleSuspensionDataGeneratedInfo::PxVehicleSuspensionDataGeneratedInfo() + : RecipMaxCompression( "RecipMaxCompression", getPxVehicleSuspensionData_RecipMaxCompression) + , RecipMaxDroop( "RecipMaxDroop", getPxVehicleSuspensionData_RecipMaxDroop) + , MassAndPreserveNaturalFrequency( "MassAndPreserveNaturalFrequency", setPxVehicleSuspensionData_MassAndPreserveNaturalFrequency) + , MSpringStrength( "MSpringStrength", setPxVehicleSuspensionDataMSpringStrength, getPxVehicleSuspensionDataMSpringStrength ) + , MSpringDamperRate( "MSpringDamperRate", setPxVehicleSuspensionDataMSpringDamperRate, getPxVehicleSuspensionDataMSpringDamperRate ) + , MMaxCompression( "MMaxCompression", setPxVehicleSuspensionDataMMaxCompression, getPxVehicleSuspensionDataMMaxCompression ) + , MMaxDroop( "MMaxDroop", setPxVehicleSuspensionDataMMaxDroop, getPxVehicleSuspensionDataMMaxDroop ) + , MSprungMass( "MSprungMass", setPxVehicleSuspensionDataMSprungMass, getPxVehicleSuspensionDataMSprungMass ) + , MCamberAtRest( "MCamberAtRest", setPxVehicleSuspensionDataMCamberAtRest, getPxVehicleSuspensionDataMCamberAtRest ) + , MCamberAtMaxCompression( "MCamberAtMaxCompression", setPxVehicleSuspensionDataMCamberAtMaxCompression, getPxVehicleSuspensionDataMCamberAtMaxCompression ) + , MCamberAtMaxDroop( "MCamberAtMaxDroop", setPxVehicleSuspensionDataMCamberAtMaxDroop, getPxVehicleSuspensionDataMCamberAtMaxDroop ) +{} + PxVehicleSuspensionDataGeneratedValues::PxVehicleSuspensionDataGeneratedValues( const PxVehicleSuspensionData* inSource ) + :RecipMaxCompression( getPxVehicleSuspensionData_RecipMaxCompression( inSource ) ) + ,RecipMaxDroop( getPxVehicleSuspensionData_RecipMaxDroop( inSource ) ) + ,MSpringStrength( inSource->mSpringStrength ) + ,MSpringDamperRate( inSource->mSpringDamperRate ) + ,MMaxCompression( inSource->mMaxCompression ) + ,MMaxDroop( inSource->mMaxDroop ) + ,MSprungMass( inSource->mSprungMass ) + ,MCamberAtRest( inSource->mCamberAtRest ) + ,MCamberAtMaxCompression( inSource->mCamberAtMaxCompression ) + ,MCamberAtMaxDroop( inSource->mCamberAtMaxDroop ) +{ + PX_UNUSED(inSource); +} +inline PxU32 getPxVehicleAntiRollBarDataMWheel0( const PxVehicleAntiRollBarData* inOwner ) { return inOwner->mWheel0; } +inline void setPxVehicleAntiRollBarDataMWheel0( PxVehicleAntiRollBarData* inOwner, PxU32 inData) { inOwner->mWheel0 = inData; } +inline PxU32 getPxVehicleAntiRollBarDataMWheel1( const PxVehicleAntiRollBarData* inOwner ) { return inOwner->mWheel1; } +inline void setPxVehicleAntiRollBarDataMWheel1( PxVehicleAntiRollBarData* inOwner, PxU32 inData) { inOwner->mWheel1 = inData; } +inline PxF32 getPxVehicleAntiRollBarDataMStiffness( const PxVehicleAntiRollBarData* inOwner ) { return inOwner->mStiffness; } +inline void setPxVehicleAntiRollBarDataMStiffness( PxVehicleAntiRollBarData* inOwner, PxF32 inData) { inOwner->mStiffness = inData; } + PxVehicleAntiRollBarDataGeneratedInfo::PxVehicleAntiRollBarDataGeneratedInfo() + : MWheel0( "MWheel0", setPxVehicleAntiRollBarDataMWheel0, getPxVehicleAntiRollBarDataMWheel0 ) + , MWheel1( "MWheel1", setPxVehicleAntiRollBarDataMWheel1, getPxVehicleAntiRollBarDataMWheel1 ) + , MStiffness( "MStiffness", setPxVehicleAntiRollBarDataMStiffness, getPxVehicleAntiRollBarDataMStiffness ) +{} + PxVehicleAntiRollBarDataGeneratedValues::PxVehicleAntiRollBarDataGeneratedValues( const PxVehicleAntiRollBarData* inSource ) + :MWheel0( inSource->mWheel0 ) + ,MWheel1( inSource->mWheel1 ) + ,MStiffness( inSource->mStiffness ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleTireData_RecipLongitudinalStiffnessPerUnitGravity( const PxVehicleTireData* inObj ) { return inObj->getRecipLongitudinalStiffnessPerUnitGravity(); } +PxReal getPxVehicleTireData_FrictionVsSlipGraphRecipx1Minusx0( const PxVehicleTireData* inObj ) { return inObj->getFrictionVsSlipGraphRecipx1Minusx0(); } +PxReal getPxVehicleTireData_FrictionVsSlipGraphRecipx2Minusx1( const PxVehicleTireData* inObj ) { return inObj->getFrictionVsSlipGraphRecipx2Minusx1(); } +inline PxReal getPxVehicleTireDataMLatStiffX( const PxVehicleTireData* inOwner ) { return inOwner->mLatStiffX; } +inline void setPxVehicleTireDataMLatStiffX( PxVehicleTireData* inOwner, PxReal inData) { inOwner->mLatStiffX = inData; } +inline PxReal getPxVehicleTireDataMLatStiffY( const PxVehicleTireData* inOwner ) { return inOwner->mLatStiffY; } +inline void setPxVehicleTireDataMLatStiffY( PxVehicleTireData* inOwner, PxReal inData) { inOwner->mLatStiffY = inData; } +inline PxReal getPxVehicleTireDataMLongitudinalStiffnessPerUnitGravity( const PxVehicleTireData* inOwner ) { return inOwner->mLongitudinalStiffnessPerUnitGravity; } +inline void setPxVehicleTireDataMLongitudinalStiffnessPerUnitGravity( PxVehicleTireData* inOwner, PxReal inData) { inOwner->mLongitudinalStiffnessPerUnitGravity = inData; } +inline PxReal getPxVehicleTireDataMCamberStiffnessPerUnitGravity( const PxVehicleTireData* inOwner ) { return inOwner->mCamberStiffnessPerUnitGravity; } +inline void setPxVehicleTireDataMCamberStiffnessPerUnitGravity( PxVehicleTireData* inOwner, PxReal inData) { inOwner->mCamberStiffnessPerUnitGravity = inData; } +inline PxU32 getPxVehicleTireDataMType( const PxVehicleTireData* inOwner ) { return inOwner->mType; } +inline void setPxVehicleTireDataMType( PxVehicleTireData* inOwner, PxU32 inData) { inOwner->mType = inData; } + PxVehicleTireDataGeneratedInfo::PxVehicleTireDataGeneratedInfo() + : RecipLongitudinalStiffnessPerUnitGravity( "RecipLongitudinalStiffnessPerUnitGravity", getPxVehicleTireData_RecipLongitudinalStiffnessPerUnitGravity) + , FrictionVsSlipGraphRecipx1Minusx0( "FrictionVsSlipGraphRecipx1Minusx0", getPxVehicleTireData_FrictionVsSlipGraphRecipx1Minusx0) + , FrictionVsSlipGraphRecipx2Minusx1( "FrictionVsSlipGraphRecipx2Minusx1", getPxVehicleTireData_FrictionVsSlipGraphRecipx2Minusx1) + , MLatStiffX( "MLatStiffX", setPxVehicleTireDataMLatStiffX, getPxVehicleTireDataMLatStiffX ) + , MLatStiffY( "MLatStiffY", setPxVehicleTireDataMLatStiffY, getPxVehicleTireDataMLatStiffY ) + , MLongitudinalStiffnessPerUnitGravity( "MLongitudinalStiffnessPerUnitGravity", setPxVehicleTireDataMLongitudinalStiffnessPerUnitGravity, getPxVehicleTireDataMLongitudinalStiffnessPerUnitGravity ) + , MCamberStiffnessPerUnitGravity( "MCamberStiffnessPerUnitGravity", setPxVehicleTireDataMCamberStiffnessPerUnitGravity, getPxVehicleTireDataMCamberStiffnessPerUnitGravity ) + , MType( "MType", setPxVehicleTireDataMType, getPxVehicleTireDataMType ) +{} + PxVehicleTireDataGeneratedValues::PxVehicleTireDataGeneratedValues( const PxVehicleTireData* inSource ) + :RecipLongitudinalStiffnessPerUnitGravity( getPxVehicleTireData_RecipLongitudinalStiffnessPerUnitGravity( inSource ) ) + ,FrictionVsSlipGraphRecipx1Minusx0( getPxVehicleTireData_FrictionVsSlipGraphRecipx1Minusx0( inSource ) ) + ,FrictionVsSlipGraphRecipx2Minusx1( getPxVehicleTireData_FrictionVsSlipGraphRecipx2Minusx1( inSource ) ) + ,MLatStiffX( inSource->mLatStiffX ) + ,MLatStiffY( inSource->mLatStiffY ) + ,MLongitudinalStiffnessPerUnitGravity( inSource->mLongitudinalStiffnessPerUnitGravity ) + ,MCamberStiffnessPerUnitGravity( inSource->mCamberStiffnessPerUnitGravity ) + ,MType( inSource->mType ) +{ + PX_UNUSED(inSource); + PxMemCopy( MFrictionVsSlipGraph, inSource->mFrictionVsSlipGraph, sizeof( MFrictionVsSlipGraph ) ); +} +const PxReal * getPxVehicleWheels4SimData_TireRestLoadsArray( const PxVehicleWheels4SimData* inObj ) { return inObj->getTireRestLoadsArray(); } +const PxReal * getPxVehicleWheels4SimData_RecipTireRestLoadsArray( const PxVehicleWheels4SimData* inObj ) { return inObj->getRecipTireRestLoadsArray(); } + PxVehicleWheels4SimDataGeneratedInfo::PxVehicleWheels4SimDataGeneratedInfo() + : TireRestLoadsArray( "TireRestLoadsArray", getPxVehicleWheels4SimData_TireRestLoadsArray) + , RecipTireRestLoadsArray( "RecipTireRestLoadsArray", getPxVehicleWheels4SimData_RecipTireRestLoadsArray) +{} + PxVehicleWheels4SimDataGeneratedValues::PxVehicleWheels4SimDataGeneratedValues( const PxVehicleWheels4SimData* inSource ) + :TireRestLoadsArray( getPxVehicleWheels4SimData_TireRestLoadsArray( inSource ) ) + ,RecipTireRestLoadsArray( getPxVehicleWheels4SimData_RecipTireRestLoadsArray( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleWheelsSimData_ChassisMass( PxVehicleWheelsSimData* inObj, const PxF32 inArg){ inObj->setChassisMass( inArg ); } +PxVehicleSuspensionData getPxVehicleWheelsSimData_SuspensionData( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getSuspensionData( index ); } +PxU32 getNbPxVehicleWheelsSimData_SuspensionData( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbSuspensionData( ); } +void setPxVehicleWheelsSimData_SuspensionData( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVehicleSuspensionData inValue ){ inObj->setSuspensionData( inIndex, inValue ); } +PxVehicleWheelData getPxVehicleWheelsSimData_WheelData( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getWheelData( index ); } +PxU32 getNbPxVehicleWheelsSimData_WheelData( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbWheelData( ); } +void setPxVehicleWheelsSimData_WheelData( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVehicleWheelData inValue ){ inObj->setWheelData( inIndex, inValue ); } +PxVehicleTireData getPxVehicleWheelsSimData_TireData( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getTireData( index ); } +PxU32 getNbPxVehicleWheelsSimData_TireData( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbTireData( ); } +void setPxVehicleWheelsSimData_TireData( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVehicleTireData inValue ){ inObj->setTireData( inIndex, inValue ); } +PxVec3 getPxVehicleWheelsSimData_SuspTravelDirection( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getSuspTravelDirection( index ); } +PxU32 getNbPxVehicleWheelsSimData_SuspTravelDirection( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbSuspTravelDirection( ); } +void setPxVehicleWheelsSimData_SuspTravelDirection( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVec3 inValue ){ inObj->setSuspTravelDirection( inIndex, inValue ); } +PxVec3 getPxVehicleWheelsSimData_SuspForceAppPointOffset( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getSuspForceAppPointOffset( index ); } +PxU32 getNbPxVehicleWheelsSimData_SuspForceAppPointOffset( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbSuspForceAppPointOffset( ); } +void setPxVehicleWheelsSimData_SuspForceAppPointOffset( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVec3 inValue ){ inObj->setSuspForceAppPointOffset( inIndex, inValue ); } +PxVec3 getPxVehicleWheelsSimData_TireForceAppPointOffset( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getTireForceAppPointOffset( index ); } +PxU32 getNbPxVehicleWheelsSimData_TireForceAppPointOffset( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbTireForceAppPointOffset( ); } +void setPxVehicleWheelsSimData_TireForceAppPointOffset( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVec3 inValue ){ inObj->setTireForceAppPointOffset( inIndex, inValue ); } +PxVec3 getPxVehicleWheelsSimData_WheelCentreOffset( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getWheelCentreOffset( index ); } +PxU32 getNbPxVehicleWheelsSimData_WheelCentreOffset( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbWheelCentreOffset( ); } +void setPxVehicleWheelsSimData_WheelCentreOffset( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVec3 inValue ){ inObj->setWheelCentreOffset( inIndex, inValue ); } +PxI32 getPxVehicleWheelsSimData_WheelShapeMapping( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getWheelShapeMapping( index ); } +PxU32 getNbPxVehicleWheelsSimData_WheelShapeMapping( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbWheelShapeMapping( ); } +void setPxVehicleWheelsSimData_WheelShapeMapping( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxI32 inValue ){ inObj->setWheelShapeMapping( inIndex, inValue ); } +PxFilterData getPxVehicleWheelsSimData_SceneQueryFilterData( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getSceneQueryFilterData( index ); } +PxU32 getNbPxVehicleWheelsSimData_SceneQueryFilterData( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbSceneQueryFilterData( ); } +void setPxVehicleWheelsSimData_SceneQueryFilterData( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxFilterData inValue ){ inObj->setSceneQueryFilterData( inIndex, inValue ); } +PxVehicleAntiRollBarData getPxVehicleWheelsSimData_AntiRollBarData( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getAntiRollBarData( index ); } +PxU32 getNbPxVehicleWheelsSimData_AntiRollBarData( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbAntiRollBarData( ); } +void setPxVehicleWheelsSimData_AntiRollBarData( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVehicleAntiRollBarData inValue ){ inObj->setAntiRollBarData( inIndex, inValue ); } +void setPxVehicleWheelsSimData_TireLoadFilterData( PxVehicleWheelsSimData* inObj, const PxVehicleTireLoadFilterData & inArg){ inObj->setTireLoadFilterData( inArg ); } +PxVehicleTireLoadFilterData getPxVehicleWheelsSimData_TireLoadFilterData( const PxVehicleWheelsSimData* inObj ) { return inObj->getTireLoadFilterData(); } +void setPxVehicleWheelsSimData_MinLongSlipDenominator( PxVehicleWheelsSimData* inObj, const PxReal inArg){ inObj->setMinLongSlipDenominator( inArg ); } +PxF32 getPxVehicleWheelsSimData_MinLongSlipDenominator( const PxVehicleWheelsSimData* inObj ) { return inObj->getMinLongSlipDenominator(); } +void setPxVehicleWheelsSimData_ThresholdLongSpeed( PxVehicleWheelsSimData* inObj, const PxF32 inArg){ inObj->setThresholdLongSpeed( inArg ); } +PxF32 getPxVehicleWheelsSimData_ThresholdLongSpeed( const PxVehicleWheelsSimData* inObj ) { return inObj->getThresholdLongSpeed(); } +void setPxVehicleWheelsSimData_LowForwardSpeedSubStepCount( PxVehicleWheelsSimData* inObj, const PxU32 inArg){ inObj->setLowForwardSpeedSubStepCount( inArg ); } +PxU32 getPxVehicleWheelsSimData_LowForwardSpeedSubStepCount( const PxVehicleWheelsSimData* inObj ) { return inObj->getLowForwardSpeedSubStepCount(); } +void setPxVehicleWheelsSimData_HighForwardSpeedSubStepCount( PxVehicleWheelsSimData* inObj, const PxU32 inArg){ inObj->setHighForwardSpeedSubStepCount( inArg ); } +PxU32 getPxVehicleWheelsSimData_HighForwardSpeedSubStepCount( const PxVehicleWheelsSimData* inObj ) { return inObj->getHighForwardSpeedSubStepCount(); } +_Bool getPxVehicleWheelsSimData_WheelEnabledState( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getWheelEnabledState( index ); } +PxU32 getNbPxVehicleWheelsSimData_WheelEnabledState( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbWheelEnabledState( ); } +void setPxVehicleWheelsSimData_WheelEnabledState( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, _Bool inValue ){ inObj->setWheelEnabledState( inIndex, inValue ); } + PxVehicleWheelsSimDataGeneratedInfo::PxVehicleWheelsSimDataGeneratedInfo() + : ChassisMass( "ChassisMass", setPxVehicleWheelsSimData_ChassisMass) + , SuspensionData( "SuspensionData", getPxVehicleWheelsSimData_SuspensionData, getNbPxVehicleWheelsSimData_SuspensionData, setPxVehicleWheelsSimData_SuspensionData ) + , WheelData( "WheelData", getPxVehicleWheelsSimData_WheelData, getNbPxVehicleWheelsSimData_WheelData, setPxVehicleWheelsSimData_WheelData ) + , TireData( "TireData", getPxVehicleWheelsSimData_TireData, getNbPxVehicleWheelsSimData_TireData, setPxVehicleWheelsSimData_TireData ) + , SuspTravelDirection( "SuspTravelDirection", getPxVehicleWheelsSimData_SuspTravelDirection, getNbPxVehicleWheelsSimData_SuspTravelDirection, setPxVehicleWheelsSimData_SuspTravelDirection ) + , SuspForceAppPointOffset( "SuspForceAppPointOffset", getPxVehicleWheelsSimData_SuspForceAppPointOffset, getNbPxVehicleWheelsSimData_SuspForceAppPointOffset, setPxVehicleWheelsSimData_SuspForceAppPointOffset ) + , TireForceAppPointOffset( "TireForceAppPointOffset", getPxVehicleWheelsSimData_TireForceAppPointOffset, getNbPxVehicleWheelsSimData_TireForceAppPointOffset, setPxVehicleWheelsSimData_TireForceAppPointOffset ) + , WheelCentreOffset( "WheelCentreOffset", getPxVehicleWheelsSimData_WheelCentreOffset, getNbPxVehicleWheelsSimData_WheelCentreOffset, setPxVehicleWheelsSimData_WheelCentreOffset ) + , WheelShapeMapping( "WheelShapeMapping", getPxVehicleWheelsSimData_WheelShapeMapping, getNbPxVehicleWheelsSimData_WheelShapeMapping, setPxVehicleWheelsSimData_WheelShapeMapping ) + , SceneQueryFilterData( "SceneQueryFilterData", getPxVehicleWheelsSimData_SceneQueryFilterData, getNbPxVehicleWheelsSimData_SceneQueryFilterData, setPxVehicleWheelsSimData_SceneQueryFilterData ) + , AntiRollBarData( "AntiRollBarData", getPxVehicleWheelsSimData_AntiRollBarData, getNbPxVehicleWheelsSimData_AntiRollBarData, setPxVehicleWheelsSimData_AntiRollBarData ) + , TireLoadFilterData( "TireLoadFilterData", setPxVehicleWheelsSimData_TireLoadFilterData, getPxVehicleWheelsSimData_TireLoadFilterData) + , MinLongSlipDenominator( "MinLongSlipDenominator", setPxVehicleWheelsSimData_MinLongSlipDenominator, getPxVehicleWheelsSimData_MinLongSlipDenominator) + , ThresholdLongSpeed( "ThresholdLongSpeed", setPxVehicleWheelsSimData_ThresholdLongSpeed, getPxVehicleWheelsSimData_ThresholdLongSpeed) + , LowForwardSpeedSubStepCount( "LowForwardSpeedSubStepCount", setPxVehicleWheelsSimData_LowForwardSpeedSubStepCount, getPxVehicleWheelsSimData_LowForwardSpeedSubStepCount) + , HighForwardSpeedSubStepCount( "HighForwardSpeedSubStepCount", setPxVehicleWheelsSimData_HighForwardSpeedSubStepCount, getPxVehicleWheelsSimData_HighForwardSpeedSubStepCount) + , WheelEnabledState( "WheelEnabledState", getPxVehicleWheelsSimData_WheelEnabledState, getNbPxVehicleWheelsSimData_WheelEnabledState, setPxVehicleWheelsSimData_WheelEnabledState ) +{} + PxVehicleWheelsSimDataGeneratedValues::PxVehicleWheelsSimDataGeneratedValues( const PxVehicleWheelsSimData* inSource ) + :TireLoadFilterData( getPxVehicleWheelsSimData_TireLoadFilterData( inSource ) ) + ,MinLongSlipDenominator( getPxVehicleWheelsSimData_MinLongSlipDenominator( inSource ) ) + ,ThresholdLongSpeed( getPxVehicleWheelsSimData_ThresholdLongSpeed( inSource ) ) + ,LowForwardSpeedSubStepCount( getPxVehicleWheelsSimData_LowForwardSpeedSubStepCount( inSource ) ) + ,HighForwardSpeedSubStepCount( getPxVehicleWheelsSimData_HighForwardSpeedSubStepCount( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleWheelsDynData_TireForceShaderFunction( PxVehicleWheelsDynData* inObj, PxVehicleComputeTireForce inArg){ inObj->setTireForceShaderFunction( inArg ); } +PxReal getPxVehicleWheelsDynData_WheelRotationSpeed( const PxVehicleWheelsDynData* inObj, const PxU32 index ) { return inObj->getWheelRotationSpeed( index ); } +PxU32 getNbPxVehicleWheelsDynData_WheelRotationSpeed( const PxVehicleWheelsDynData* inObj ) { return inObj->getNbWheelRotationSpeed( ); } +void setPxVehicleWheelsDynData_WheelRotationSpeed( PxVehicleWheelsDynData* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setWheelRotationSpeed( inIndex, inValue ); } +PxReal getPxVehicleWheelsDynData_WheelRotationAngle( const PxVehicleWheelsDynData* inObj, const PxU32 index ) { return inObj->getWheelRotationAngle( index ); } +PxU32 getNbPxVehicleWheelsDynData_WheelRotationAngle( const PxVehicleWheelsDynData* inObj ) { return inObj->getNbWheelRotationAngle( ); } +void setPxVehicleWheelsDynData_WheelRotationAngle( PxVehicleWheelsDynData* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setWheelRotationAngle( inIndex, inValue ); } +PxVehicleWheels4DynData * getPxVehicleWheelsDynData_Wheel4DynData( const PxVehicleWheelsDynData* inObj ) { return inObj->getWheel4DynData(); } + PxVehicleWheelsDynDataGeneratedInfo::PxVehicleWheelsDynDataGeneratedInfo() + : TireForceShaderFunction( "TireForceShaderFunction", setPxVehicleWheelsDynData_TireForceShaderFunction) + , WheelRotationSpeed( "WheelRotationSpeed", getPxVehicleWheelsDynData_WheelRotationSpeed, getNbPxVehicleWheelsDynData_WheelRotationSpeed, setPxVehicleWheelsDynData_WheelRotationSpeed ) + , WheelRotationAngle( "WheelRotationAngle", getPxVehicleWheelsDynData_WheelRotationAngle, getNbPxVehicleWheelsDynData_WheelRotationAngle, setPxVehicleWheelsDynData_WheelRotationAngle ) + , Wheel4DynData( "Wheel4DynData", getPxVehicleWheelsDynData_Wheel4DynData) +{} + PxVehicleWheelsDynDataGeneratedValues::PxVehicleWheelsDynDataGeneratedValues( const PxVehicleWheelsDynData* inSource ) + :Wheel4DynData( getPxVehicleWheelsDynData_Wheel4DynData( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxU32 getPxVehicleWheels_VehicleType( const PxVehicleWheels* inObj ) { return inObj->getVehicleType(); } +const PxRigidDynamic * getPxVehicleWheels_RigidDynamicActor( const PxVehicleWheels* inObj ) { return inObj->getRigidDynamicActor(); } +const char * getPxVehicleWheels_ConcreteTypeName( const PxVehicleWheels* inObj ) { return inObj->getConcreteTypeName(); } +inline PxVehicleWheelsSimData getPxVehicleWheelsMWheelsSimData( const PxVehicleWheels* inOwner ) { return inOwner->mWheelsSimData; } +inline void setPxVehicleWheelsMWheelsSimData( PxVehicleWheels* inOwner, PxVehicleWheelsSimData inData) { inOwner->mWheelsSimData = inData; } +inline PxVehicleWheelsDynData getPxVehicleWheelsMWheelsDynData( const PxVehicleWheels* inOwner ) { return inOwner->mWheelsDynData; } +inline void setPxVehicleWheelsMWheelsDynData( PxVehicleWheels* inOwner, PxVehicleWheelsDynData inData) { inOwner->mWheelsDynData = inData; } + PxVehicleWheelsGeneratedInfo::PxVehicleWheelsGeneratedInfo() + : VehicleType( "VehicleType", getPxVehicleWheels_VehicleType) + , RigidDynamicActor( "RigidDynamicActor", getPxVehicleWheels_RigidDynamicActor) + , ConcreteTypeName( "ConcreteTypeName", getPxVehicleWheels_ConcreteTypeName) + , MWheelsSimData( "MWheelsSimData", setPxVehicleWheelsMWheelsSimData, getPxVehicleWheelsMWheelsSimData ) + , MWheelsDynData( "MWheelsDynData", setPxVehicleWheelsMWheelsDynData, getPxVehicleWheelsMWheelsDynData ) +{} + PxVehicleWheelsGeneratedValues::PxVehicleWheelsGeneratedValues( const PxVehicleWheels* inSource ) + :VehicleType( getPxVehicleWheels_VehicleType( inSource ) ) + ,RigidDynamicActor( getPxVehicleWheels_RigidDynamicActor( inSource ) ) + ,ConcreteTypeName( getPxVehicleWheels_ConcreteTypeName( inSource ) ) + ,MWheelsSimData( inSource->mWheelsSimData ) + ,MWheelsDynData( inSource->mWheelsDynData ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleDriveDynData_AnalogInput( const PxVehicleDriveDynData* inObj, const PxU32 index ) { return inObj->getAnalogInput( index ); } +PxU32 getNbPxVehicleDriveDynData_AnalogInput( const PxVehicleDriveDynData* inObj ) { return inObj->getNbAnalogInput( ); } +void setPxVehicleDriveDynData_AnalogInput( PxVehicleDriveDynData* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setAnalogInput( inIndex, inValue ); } +void setPxVehicleDriveDynData_GearUp( PxVehicleDriveDynData* inObj, const _Bool inArg){ inObj->setGearUp( inArg ); } +_Bool getPxVehicleDriveDynData_GearUp( const PxVehicleDriveDynData* inObj ) { return inObj->getGearUp(); } +void setPxVehicleDriveDynData_GearDown( PxVehicleDriveDynData* inObj, const _Bool inArg){ inObj->setGearDown( inArg ); } +_Bool getPxVehicleDriveDynData_GearDown( const PxVehicleDriveDynData* inObj ) { return inObj->getGearDown(); } +void setPxVehicleDriveDynData_UseAutoGears( PxVehicleDriveDynData* inObj, const _Bool inArg){ inObj->setUseAutoGears( inArg ); } +_Bool getPxVehicleDriveDynData_UseAutoGears( const PxVehicleDriveDynData* inObj ) { return inObj->getUseAutoGears(); } +void setPxVehicleDriveDynData_CurrentGear( PxVehicleDriveDynData* inObj, PxU32 inArg){ inObj->setCurrentGear( inArg ); } +PxU32 getPxVehicleDriveDynData_CurrentGear( const PxVehicleDriveDynData* inObj ) { return inObj->getCurrentGear(); } +void setPxVehicleDriveDynData_TargetGear( PxVehicleDriveDynData* inObj, PxU32 inArg){ inObj->setTargetGear( inArg ); } +PxU32 getPxVehicleDriveDynData_TargetGear( const PxVehicleDriveDynData* inObj ) { return inObj->getTargetGear(); } +void setPxVehicleDriveDynData_EngineRotationSpeed( PxVehicleDriveDynData* inObj, const PxF32 inArg){ inObj->setEngineRotationSpeed( inArg ); } +PxReal getPxVehicleDriveDynData_EngineRotationSpeed( const PxVehicleDriveDynData* inObj ) { return inObj->getEngineRotationSpeed(); } +void setPxVehicleDriveDynData_GearChange( PxVehicleDriveDynData* inObj, const PxU32 inArg){ inObj->setGearChange( inArg ); } +PxU32 getPxVehicleDriveDynData_GearChange( const PxVehicleDriveDynData* inObj ) { return inObj->getGearChange(); } +void setPxVehicleDriveDynData_GearSwitchTime( PxVehicleDriveDynData* inObj, const PxReal inArg){ inObj->setGearSwitchTime( inArg ); } +PxReal getPxVehicleDriveDynData_GearSwitchTime( const PxVehicleDriveDynData* inObj ) { return inObj->getGearSwitchTime(); } +void setPxVehicleDriveDynData_AutoBoxSwitchTime( PxVehicleDriveDynData* inObj, const PxReal inArg){ inObj->setAutoBoxSwitchTime( inArg ); } +PxReal getPxVehicleDriveDynData_AutoBoxSwitchTime( const PxVehicleDriveDynData* inObj ) { return inObj->getAutoBoxSwitchTime(); } +inline _Bool getPxVehicleDriveDynDataMUseAutoGears( const PxVehicleDriveDynData* inOwner ) { return inOwner->mUseAutoGears; } +inline void setPxVehicleDriveDynDataMUseAutoGears( PxVehicleDriveDynData* inOwner, _Bool inData) { inOwner->mUseAutoGears = inData; } +inline _Bool getPxVehicleDriveDynDataMGearUpPressed( const PxVehicleDriveDynData* inOwner ) { return inOwner->mGearUpPressed; } +inline void setPxVehicleDriveDynDataMGearUpPressed( PxVehicleDriveDynData* inOwner, _Bool inData) { inOwner->mGearUpPressed = inData; } +inline _Bool getPxVehicleDriveDynDataMGearDownPressed( const PxVehicleDriveDynData* inOwner ) { return inOwner->mGearDownPressed; } +inline void setPxVehicleDriveDynDataMGearDownPressed( PxVehicleDriveDynData* inOwner, _Bool inData) { inOwner->mGearDownPressed = inData; } +inline PxU32 getPxVehicleDriveDynDataMCurrentGear( const PxVehicleDriveDynData* inOwner ) { return inOwner->mCurrentGear; } +inline void setPxVehicleDriveDynDataMCurrentGear( PxVehicleDriveDynData* inOwner, PxU32 inData) { inOwner->mCurrentGear = inData; } +inline PxU32 getPxVehicleDriveDynDataMTargetGear( const PxVehicleDriveDynData* inOwner ) { return inOwner->mTargetGear; } +inline void setPxVehicleDriveDynDataMTargetGear( PxVehicleDriveDynData* inOwner, PxU32 inData) { inOwner->mTargetGear = inData; } +inline PxReal getPxVehicleDriveDynDataMEnginespeed( const PxVehicleDriveDynData* inOwner ) { return inOwner->mEnginespeed; } +inline void setPxVehicleDriveDynDataMEnginespeed( PxVehicleDriveDynData* inOwner, PxReal inData) { inOwner->mEnginespeed = inData; } +inline PxReal getPxVehicleDriveDynDataMGearSwitchTime( const PxVehicleDriveDynData* inOwner ) { return inOwner->mGearSwitchTime; } +inline void setPxVehicleDriveDynDataMGearSwitchTime( PxVehicleDriveDynData* inOwner, PxReal inData) { inOwner->mGearSwitchTime = inData; } +inline PxReal getPxVehicleDriveDynDataMAutoBoxSwitchTime( const PxVehicleDriveDynData* inOwner ) { return inOwner->mAutoBoxSwitchTime; } +inline void setPxVehicleDriveDynDataMAutoBoxSwitchTime( PxVehicleDriveDynData* inOwner, PxReal inData) { inOwner->mAutoBoxSwitchTime = inData; } + PxVehicleDriveDynDataGeneratedInfo::PxVehicleDriveDynDataGeneratedInfo() + : AnalogInput( "AnalogInput", getPxVehicleDriveDynData_AnalogInput, getNbPxVehicleDriveDynData_AnalogInput, setPxVehicleDriveDynData_AnalogInput ) + , GearUp( "GearUp", setPxVehicleDriveDynData_GearUp, getPxVehicleDriveDynData_GearUp) + , GearDown( "GearDown", setPxVehicleDriveDynData_GearDown, getPxVehicleDriveDynData_GearDown) + , UseAutoGears( "UseAutoGears", setPxVehicleDriveDynData_UseAutoGears, getPxVehicleDriveDynData_UseAutoGears) + , CurrentGear( "CurrentGear", setPxVehicleDriveDynData_CurrentGear, getPxVehicleDriveDynData_CurrentGear) + , TargetGear( "TargetGear", setPxVehicleDriveDynData_TargetGear, getPxVehicleDriveDynData_TargetGear) + , EngineRotationSpeed( "EngineRotationSpeed", setPxVehicleDriveDynData_EngineRotationSpeed, getPxVehicleDriveDynData_EngineRotationSpeed) + , GearChange( "GearChange", setPxVehicleDriveDynData_GearChange, getPxVehicleDriveDynData_GearChange) + , GearSwitchTime( "GearSwitchTime", setPxVehicleDriveDynData_GearSwitchTime, getPxVehicleDriveDynData_GearSwitchTime) + , AutoBoxSwitchTime( "AutoBoxSwitchTime", setPxVehicleDriveDynData_AutoBoxSwitchTime, getPxVehicleDriveDynData_AutoBoxSwitchTime) + , MUseAutoGears( "MUseAutoGears", setPxVehicleDriveDynDataMUseAutoGears, getPxVehicleDriveDynDataMUseAutoGears ) + , MGearUpPressed( "MGearUpPressed", setPxVehicleDriveDynDataMGearUpPressed, getPxVehicleDriveDynDataMGearUpPressed ) + , MGearDownPressed( "MGearDownPressed", setPxVehicleDriveDynDataMGearDownPressed, getPxVehicleDriveDynDataMGearDownPressed ) + , MCurrentGear( "MCurrentGear", setPxVehicleDriveDynDataMCurrentGear, getPxVehicleDriveDynDataMCurrentGear ) + , MTargetGear( "MTargetGear", setPxVehicleDriveDynDataMTargetGear, getPxVehicleDriveDynDataMTargetGear ) + , MEnginespeed( "MEnginespeed", setPxVehicleDriveDynDataMEnginespeed, getPxVehicleDriveDynDataMEnginespeed ) + , MGearSwitchTime( "MGearSwitchTime", setPxVehicleDriveDynDataMGearSwitchTime, getPxVehicleDriveDynDataMGearSwitchTime ) + , MAutoBoxSwitchTime( "MAutoBoxSwitchTime", setPxVehicleDriveDynDataMAutoBoxSwitchTime, getPxVehicleDriveDynDataMAutoBoxSwitchTime ) +{} + PxVehicleDriveDynDataGeneratedValues::PxVehicleDriveDynDataGeneratedValues( const PxVehicleDriveDynData* inSource ) + :GearUp( getPxVehicleDriveDynData_GearUp( inSource ) ) + ,GearDown( getPxVehicleDriveDynData_GearDown( inSource ) ) + ,UseAutoGears( getPxVehicleDriveDynData_UseAutoGears( inSource ) ) + ,CurrentGear( getPxVehicleDriveDynData_CurrentGear( inSource ) ) + ,TargetGear( getPxVehicleDriveDynData_TargetGear( inSource ) ) + ,EngineRotationSpeed( getPxVehicleDriveDynData_EngineRotationSpeed( inSource ) ) + ,GearChange( getPxVehicleDriveDynData_GearChange( inSource ) ) + ,GearSwitchTime( getPxVehicleDriveDynData_GearSwitchTime( inSource ) ) + ,AutoBoxSwitchTime( getPxVehicleDriveDynData_AutoBoxSwitchTime( inSource ) ) + ,MUseAutoGears( inSource->mUseAutoGears ) + ,MGearUpPressed( inSource->mGearUpPressed ) + ,MGearDownPressed( inSource->mGearDownPressed ) + ,MCurrentGear( inSource->mCurrentGear ) + ,MTargetGear( inSource->mTargetGear ) + ,MEnginespeed( inSource->mEnginespeed ) + ,MGearSwitchTime( inSource->mGearSwitchTime ) + ,MAutoBoxSwitchTime( inSource->mAutoBoxSwitchTime ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleDriveSimData_EngineData( PxVehicleDriveSimData* inObj, const PxVehicleEngineData & inArg){ inObj->setEngineData( inArg ); } +PxVehicleEngineData getPxVehicleDriveSimData_EngineData( const PxVehicleDriveSimData* inObj ) { return inObj->getEngineData(); } +void setPxVehicleDriveSimData_GearsData( PxVehicleDriveSimData* inObj, const PxVehicleGearsData & inArg){ inObj->setGearsData( inArg ); } +PxVehicleGearsData getPxVehicleDriveSimData_GearsData( const PxVehicleDriveSimData* inObj ) { return inObj->getGearsData(); } +void setPxVehicleDriveSimData_ClutchData( PxVehicleDriveSimData* inObj, const PxVehicleClutchData & inArg){ inObj->setClutchData( inArg ); } +PxVehicleClutchData getPxVehicleDriveSimData_ClutchData( const PxVehicleDriveSimData* inObj ) { return inObj->getClutchData(); } +void setPxVehicleDriveSimData_AutoBoxData( PxVehicleDriveSimData* inObj, const PxVehicleAutoBoxData & inArg){ inObj->setAutoBoxData( inArg ); } +PxVehicleAutoBoxData getPxVehicleDriveSimData_AutoBoxData( const PxVehicleDriveSimData* inObj ) { return inObj->getAutoBoxData(); } + PxVehicleDriveSimDataGeneratedInfo::PxVehicleDriveSimDataGeneratedInfo() + : EngineData( "EngineData", setPxVehicleDriveSimData_EngineData, getPxVehicleDriveSimData_EngineData) + , GearsData( "GearsData", setPxVehicleDriveSimData_GearsData, getPxVehicleDriveSimData_GearsData) + , ClutchData( "ClutchData", setPxVehicleDriveSimData_ClutchData, getPxVehicleDriveSimData_ClutchData) + , AutoBoxData( "AutoBoxData", setPxVehicleDriveSimData_AutoBoxData, getPxVehicleDriveSimData_AutoBoxData) +{} + PxVehicleDriveSimDataGeneratedValues::PxVehicleDriveSimDataGeneratedValues( const PxVehicleDriveSimData* inSource ) + :EngineData( getPxVehicleDriveSimData_EngineData( inSource ) ) + ,GearsData( getPxVehicleDriveSimData_GearsData( inSource ) ) + ,ClutchData( getPxVehicleDriveSimData_ClutchData( inSource ) ) + ,AutoBoxData( getPxVehicleDriveSimData_AutoBoxData( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleDriveSimData4W_DiffData( PxVehicleDriveSimData4W* inObj, const PxVehicleDifferential4WData & inArg){ inObj->setDiffData( inArg ); } +PxVehicleDifferential4WData getPxVehicleDriveSimData4W_DiffData( const PxVehicleDriveSimData4W* inObj ) { return inObj->getDiffData(); } +void setPxVehicleDriveSimData4W_AckermannGeometryData( PxVehicleDriveSimData4W* inObj, const PxVehicleAckermannGeometryData & inArg){ inObj->setAckermannGeometryData( inArg ); } +PxVehicleAckermannGeometryData getPxVehicleDriveSimData4W_AckermannGeometryData( const PxVehicleDriveSimData4W* inObj ) { return inObj->getAckermannGeometryData(); } + PxVehicleDriveSimData4WGeneratedInfo::PxVehicleDriveSimData4WGeneratedInfo() + : DiffData( "DiffData", setPxVehicleDriveSimData4W_DiffData, getPxVehicleDriveSimData4W_DiffData) + , AckermannGeometryData( "AckermannGeometryData", setPxVehicleDriveSimData4W_AckermannGeometryData, getPxVehicleDriveSimData4W_AckermannGeometryData) +{} + PxVehicleDriveSimData4WGeneratedValues::PxVehicleDriveSimData4WGeneratedValues( const PxVehicleDriveSimData4W* inSource ) + :PxVehicleDriveSimDataGeneratedValues( inSource ) + ,DiffData( getPxVehicleDriveSimData4W_DiffData( inSource ) ) + ,AckermannGeometryData( getPxVehicleDriveSimData4W_AckermannGeometryData( inSource ) ) +{ + PX_UNUSED(inSource); +} +const char * getPxVehicleDrive_ConcreteTypeName( const PxVehicleDrive* inObj ) { return inObj->getConcreteTypeName(); } +inline PxVehicleDriveDynData getPxVehicleDriveMDriveDynData( const PxVehicleDrive* inOwner ) { return inOwner->mDriveDynData; } +inline void setPxVehicleDriveMDriveDynData( PxVehicleDrive* inOwner, PxVehicleDriveDynData inData) { inOwner->mDriveDynData = inData; } + PxVehicleDriveGeneratedInfo::PxVehicleDriveGeneratedInfo() + : ConcreteTypeName( "ConcreteTypeName", getPxVehicleDrive_ConcreteTypeName) + , MDriveDynData( "MDriveDynData", setPxVehicleDriveMDriveDynData, getPxVehicleDriveMDriveDynData ) +{} + PxVehicleDriveGeneratedValues::PxVehicleDriveGeneratedValues( const PxVehicleDrive* inSource ) + :PxVehicleWheelsGeneratedValues( inSource ) + ,ConcreteTypeName( getPxVehicleDrive_ConcreteTypeName( inSource ) ) + ,MDriveDynData( inSource->mDriveDynData ) +{ + PX_UNUSED(inSource); +} +const char * getPxVehicleDrive4W_ConcreteTypeName( const PxVehicleDrive4W* inObj ) { return inObj->getConcreteTypeName(); } +inline PxVehicleDriveSimData4W getPxVehicleDrive4WMDriveSimData( const PxVehicleDrive4W* inOwner ) { return inOwner->mDriveSimData; } +inline void setPxVehicleDrive4WMDriveSimData( PxVehicleDrive4W* inOwner, PxVehicleDriveSimData4W inData) { inOwner->mDriveSimData = inData; } + PxVehicleDrive4WGeneratedInfo::PxVehicleDrive4WGeneratedInfo() + : ConcreteTypeName( "ConcreteTypeName", getPxVehicleDrive4W_ConcreteTypeName) + , MDriveSimData( "MDriveSimData", setPxVehicleDrive4WMDriveSimData, getPxVehicleDrive4WMDriveSimData ) +{} + PxVehicleDrive4WGeneratedValues::PxVehicleDrive4WGeneratedValues( const PxVehicleDrive4W* inSource ) + :PxVehicleDriveGeneratedValues( inSource ) + ,ConcreteTypeName( getPxVehicleDrive4W_ConcreteTypeName( inSource ) ) + ,MDriveSimData( inSource->mDriveSimData ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleDriveTank_DriveModel( PxVehicleDriveTank* inObj, const PxVehicleDriveTankControlModel::Enum inArg){ inObj->setDriveModel( inArg ); } +PxVehicleDriveTankControlModel::Enum getPxVehicleDriveTank_DriveModel( const PxVehicleDriveTank* inObj ) { return inObj->getDriveModel(); } +const char * getPxVehicleDriveTank_ConcreteTypeName( const PxVehicleDriveTank* inObj ) { return inObj->getConcreteTypeName(); } +inline PxVehicleDriveSimData getPxVehicleDriveTankMDriveSimData( const PxVehicleDriveTank* inOwner ) { return inOwner->mDriveSimData; } +inline void setPxVehicleDriveTankMDriveSimData( PxVehicleDriveTank* inOwner, PxVehicleDriveSimData inData) { inOwner->mDriveSimData = inData; } + PxVehicleDriveTankGeneratedInfo::PxVehicleDriveTankGeneratedInfo() + : DriveModel( "DriveModel", setPxVehicleDriveTank_DriveModel, getPxVehicleDriveTank_DriveModel) + , ConcreteTypeName( "ConcreteTypeName", getPxVehicleDriveTank_ConcreteTypeName) + , MDriveSimData( "MDriveSimData", setPxVehicleDriveTankMDriveSimData, getPxVehicleDriveTankMDriveSimData ) +{} + PxVehicleDriveTankGeneratedValues::PxVehicleDriveTankGeneratedValues( const PxVehicleDriveTank* inSource ) + :PxVehicleDriveGeneratedValues( inSource ) + ,DriveModel( getPxVehicleDriveTank_DriveModel( inSource ) ) + ,ConcreteTypeName( getPxVehicleDriveTank_ConcreteTypeName( inSource ) ) + ,MDriveSimData( inSource->mDriveSimData ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleDriveSimDataNW_DiffData( PxVehicleDriveSimDataNW* inObj, const PxVehicleDifferentialNWData & inArg){ inObj->setDiffData( inArg ); } +PxVehicleDifferentialNWData getPxVehicleDriveSimDataNW_DiffData( const PxVehicleDriveSimDataNW* inObj ) { return inObj->getDiffData(); } + PxVehicleDriveSimDataNWGeneratedInfo::PxVehicleDriveSimDataNWGeneratedInfo() + : DiffData( "DiffData", setPxVehicleDriveSimDataNW_DiffData, getPxVehicleDriveSimDataNW_DiffData) +{} + PxVehicleDriveSimDataNWGeneratedValues::PxVehicleDriveSimDataNWGeneratedValues( const PxVehicleDriveSimDataNW* inSource ) + :PxVehicleDriveSimDataGeneratedValues( inSource ) + ,DiffData( getPxVehicleDriveSimDataNW_DiffData( inSource ) ) +{ + PX_UNUSED(inSource); +} +const char * getPxVehicleDriveNW_ConcreteTypeName( const PxVehicleDriveNW* inObj ) { return inObj->getConcreteTypeName(); } +inline PxVehicleDriveSimDataNW getPxVehicleDriveNWMDriveSimData( const PxVehicleDriveNW* inOwner ) { return inOwner->mDriveSimData; } +inline void setPxVehicleDriveNWMDriveSimData( PxVehicleDriveNW* inOwner, PxVehicleDriveSimDataNW inData) { inOwner->mDriveSimData = inData; } + PxVehicleDriveNWGeneratedInfo::PxVehicleDriveNWGeneratedInfo() + : ConcreteTypeName( "ConcreteTypeName", getPxVehicleDriveNW_ConcreteTypeName) + , MDriveSimData( "MDriveSimData", setPxVehicleDriveNWMDriveSimData, getPxVehicleDriveNWMDriveSimData ) +{} + PxVehicleDriveNWGeneratedValues::PxVehicleDriveNWGeneratedValues( const PxVehicleDriveNW* inSource ) + :PxVehicleDriveGeneratedValues( inSource ) + ,ConcreteTypeName( getPxVehicleDriveNW_ConcreteTypeName( inSource ) ) + ,MDriveSimData( inSource->mDriveSimData ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleNoDrive_BrakeTorque( const PxVehicleNoDrive* inObj, const PxU32 index ) { return inObj->getBrakeTorque( index ); } +PxU32 getNbPxVehicleNoDrive_BrakeTorque( const PxVehicleNoDrive* inObj ) { return inObj->getNbBrakeTorque( ); } +void setPxVehicleNoDrive_BrakeTorque( PxVehicleNoDrive* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setBrakeTorque( inIndex, inValue ); } +PxReal getPxVehicleNoDrive_DriveTorque( const PxVehicleNoDrive* inObj, const PxU32 index ) { return inObj->getDriveTorque( index ); } +PxU32 getNbPxVehicleNoDrive_DriveTorque( const PxVehicleNoDrive* inObj ) { return inObj->getNbDriveTorque( ); } +void setPxVehicleNoDrive_DriveTorque( PxVehicleNoDrive* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setDriveTorque( inIndex, inValue ); } +PxReal getPxVehicleNoDrive_SteerAngle( const PxVehicleNoDrive* inObj, const PxU32 index ) { return inObj->getSteerAngle( index ); } +PxU32 getNbPxVehicleNoDrive_SteerAngle( const PxVehicleNoDrive* inObj ) { return inObj->getNbSteerAngle( ); } +void setPxVehicleNoDrive_SteerAngle( PxVehicleNoDrive* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setSteerAngle( inIndex, inValue ); } +const char * getPxVehicleNoDrive_ConcreteTypeName( const PxVehicleNoDrive* inObj ) { return inObj->getConcreteTypeName(); } + PxVehicleNoDriveGeneratedInfo::PxVehicleNoDriveGeneratedInfo() + : BrakeTorque( "BrakeTorque", getPxVehicleNoDrive_BrakeTorque, getNbPxVehicleNoDrive_BrakeTorque, setPxVehicleNoDrive_BrakeTorque ) + , DriveTorque( "DriveTorque", getPxVehicleNoDrive_DriveTorque, getNbPxVehicleNoDrive_DriveTorque, setPxVehicleNoDrive_DriveTorque ) + , SteerAngle( "SteerAngle", getPxVehicleNoDrive_SteerAngle, getNbPxVehicleNoDrive_SteerAngle, setPxVehicleNoDrive_SteerAngle ) + , ConcreteTypeName( "ConcreteTypeName", getPxVehicleNoDrive_ConcreteTypeName) +{} + PxVehicleNoDriveGeneratedValues::PxVehicleNoDriveGeneratedValues( const PxVehicleNoDrive* inSource ) + :PxVehicleWheelsGeneratedValues( inSource ) + ,ConcreteTypeName( getPxVehicleNoDrive_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp new file mode 100644 index 000000000..b8b2bf691 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp @@ -0,0 +1,86 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPhysicsAPI.h" +#include "extensions/PxExtensionsAPI.h" +#include "PxVehicleMetaDataObjects.h" +#include "PxExtensionMetaDataObjects.h" + +namespace physx +{ + inline void SetMFrictionVsSlipGraph( PxVehicleTireData* inTireData, PxU32 idx1, PxU32 idx2, PxReal val ) { inTireData->mFrictionVsSlipGraph[idx1][idx2] = val; } + inline PxReal GetMFrictionVsSlipGraph( const PxVehicleTireData* inTireData, PxU32 idx1, PxU32 idx2 ) + { + return inTireData->mFrictionVsSlipGraph[idx1][idx2]; + } + PX_PHYSX_CORE_API MFrictionVsSlipGraphProperty::MFrictionVsSlipGraphProperty() + : PxExtendedDualIndexedPropertyInfo ( "MFrictionVsSlipGraph", SetMFrictionVsSlipGraph, GetMFrictionVsSlipGraph, 3, 2 ) + { + + } + + inline PxU32 GetNbWheels( const PxVehicleWheels* inStats ) { return inStats->mWheelsSimData.getNbWheels(); } + + inline PxU32 GetNbTorqueCurvePair( const PxVehicleEngineData* inStats ) { return inStats->mTorqueCurve.getNbDataPairs(); } + + + inline PxReal getXTorqueCurvePair( const PxVehicleEngineData* inStats, PxU32 index) + { + return inStats->mTorqueCurve.getX(index); + } + inline PxReal getYTorqueCurvePair( const PxVehicleEngineData* inStats, PxU32 index) + { + return inStats->mTorqueCurve.getY(index); + } + + void addTorqueCurvePair(PxVehicleEngineData* inStats, const PxReal x, const PxReal y) + { + inStats->mTorqueCurve.addPair(x, y); + } + + void clearTorqueCurvePair(PxVehicleEngineData* inStats) + { + inStats->mTorqueCurve.clear(); + } + + PX_PHYSX_CORE_API MTorqueCurveProperty::MTorqueCurveProperty() + : PxFixedSizeLookupTablePropertyInfo("MTorqueCurve", getXTorqueCurvePair, getYTorqueCurvePair, GetNbTorqueCurvePair, addTorqueCurvePair, clearTorqueCurvePair) + { + } + + +} + diff --git a/src/PhysX/physx/source/pvd/include/PsPvd.h b/src/PhysX/physx/source/pvd/include/PsPvd.h new file mode 100644 index 000000000..62b476bd2 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PsPvd.h @@ -0,0 +1,83 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PSPVD_H +#define PXPVDSDK_PSPVD_H + +/** \addtogroup pvd +@{ +*/ +#include "pvd/PxPvd.h" +#include "PsBroadcast.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPvdTransport; + +#if !PX_DOXYGEN +namespace pvdsdk +{ +#endif + +class PvdDataStream; +class PvdClient; +class PvdOMMetaDataProvider; + +// PsPvd is used for advanced user, it support custom pvd client API +class PsPvd : public physx::PxPvd, public shdfnd::AllocationListener +{ + public: + virtual void addClient(PvdClient* client) = 0; + virtual void removeClient(PvdClient* client) = 0; + + virtual bool registerObject(const void* inItem) = 0; + virtual bool unRegisterObject(const void* inItem) = 0; + + //AllocationListener + void onAllocation(size_t size, const char* typeName, const char* filename, int line, void* allocatedMemory) = 0; + void onDeallocation(void* addr) = 0; + + virtual PvdOMMetaDataProvider& getMetaDataProvider() = 0; + + virtual uint64_t getNextStreamId() = 0; + // Call to flush events to PVD + virtual void flush() = 0; + +}; + +#if !PX_DOXYGEN +} // namespace pvdsdk +} // namespace physx +#endif + +/** @} */ +#endif // PXPVDSDK_PSPVD_H diff --git a/src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h b/src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h new file mode 100644 index 000000000..160ddded4 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h @@ -0,0 +1,231 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEALLOCATORWRAPPER_H +#define PXPVDSDK_PXPROFILEALLOCATORWRAPPER_H + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxErrorCallback.h" +#include "foundation/PxAssert.h" + +#include "PsArray.h" +#include "PsHashMap.h" + +namespace physx { namespace profile { + + /** + \brief Helper struct to encapsulate the user allocator callback + Useful for array and hash templates + */ + struct PxProfileAllocatorWrapper + { + PxAllocatorCallback* mUserAllocator; + + PxProfileAllocatorWrapper( PxAllocatorCallback& inUserAllocator ) + : mUserAllocator( &inUserAllocator ) + { + } + + PxProfileAllocatorWrapper( PxAllocatorCallback* inUserAllocator ) + : mUserAllocator( inUserAllocator ) + { + } + + PxAllocatorCallback& getAllocator() const + { + PX_ASSERT( NULL != mUserAllocator ); + return *mUserAllocator; + } + }; + + /** + \brief Helper class to encapsulate the reflection allocator + */ + template + class PxProfileWrapperReflectionAllocator + { + static const char* getName() + { +#if PX_LINUX || PX_ANDROID || PX_PS4 || PX_IOS || PX_OSX || PX_EMSCRIPTEN || PX_SWITCH + return __PRETTY_FUNCTION__; +#else + return typeid(T).name(); +#endif + } + PxProfileAllocatorWrapper* mWrapper; + + public: + PxProfileWrapperReflectionAllocator(PxProfileAllocatorWrapper& inWrapper) : mWrapper( &inWrapper ) {} + PxProfileWrapperReflectionAllocator( const PxProfileWrapperReflectionAllocator& inOther ) + : mWrapper( inOther.mWrapper ) + { + } + PxProfileWrapperReflectionAllocator& operator=( const PxProfileWrapperReflectionAllocator& inOther ) + { + mWrapper = inOther.mWrapper; + return *this; + } + PxAllocatorCallback& getAllocator() { return mWrapper->getAllocator(); } + void* allocate(size_t size, const char* filename, int line) + { +#if PX_CHECKED // checked and debug builds + if(!size) + return 0; + return getAllocator().allocate(size, getName(), filename, line); +#else + return getAllocator().allocate(size, "", filename, line); +#endif + } + void deallocate(void* ptr) + { + if(ptr) + getAllocator().deallocate(ptr); + } + }; + + /** + \brief Helper class to encapsulate the named allocator + */ + struct PxProfileWrapperNamedAllocator + { + PxProfileAllocatorWrapper* mWrapper; + const char* mAllocationName; + PxProfileWrapperNamedAllocator(PxProfileAllocatorWrapper& inWrapper, const char* inAllocationName) + : mWrapper( &inWrapper ) + , mAllocationName( inAllocationName ) + {} + PxProfileWrapperNamedAllocator( const PxProfileWrapperNamedAllocator& inOther ) + : mWrapper( inOther.mWrapper ) + , mAllocationName( inOther.mAllocationName ) + { + } + PxProfileWrapperNamedAllocator& operator=( const PxProfileWrapperNamedAllocator& inOther ) + { + mWrapper = inOther.mWrapper; + mAllocationName = inOther.mAllocationName; + return *this; + } + PxAllocatorCallback& getAllocator() { return mWrapper->getAllocator(); } + void* allocate(size_t size, const char* filename, int line) + { + if(!size) + return 0; + return getAllocator().allocate(size, mAllocationName, filename, line); + } + void deallocate(void* ptr) + { + if(ptr) + getAllocator().deallocate(ptr); + } + }; + + /** + \brief Helper struct to encapsulate the array + */ + template + struct PxProfileArray : public shdfnd::Array > + { + typedef PxProfileWrapperReflectionAllocator TAllocatorType; + + PxProfileArray( PxProfileAllocatorWrapper& inWrapper ) + : shdfnd::Array( TAllocatorType( inWrapper ) ) + { + } + + PxProfileArray( const PxProfileArray< T >& inOther ) + : shdfnd::Array( inOther, inOther ) + { + } + }; + + /** + \brief Helper struct to encapsulate the array + */ + template > + struct PxProfileHashMap : public shdfnd::HashMap > + { + typedef shdfnd::HashMap > THashMapType; + typedef PxProfileWrapperReflectionAllocator TAllocatorType; + PxProfileHashMap( PxProfileAllocatorWrapper& inWrapper ) + : THashMapType( TAllocatorType( inWrapper ) ) + { + } + }; + + /** + \brief Helper function to encapsulate the profile allocation + */ + template + inline TDataType* PxProfileAllocate( PxAllocatorCallback* inAllocator, const char* file, int inLine ) + { + PxProfileAllocatorWrapper wrapper( inAllocator ); + typedef PxProfileWrapperReflectionAllocator< TDataType > TAllocator; + TAllocator theAllocator( wrapper ); + return reinterpret_cast( theAllocator.allocate( sizeof( TDataType ), file, inLine ) ); + } + + /** + \brief Helper function to encapsulate the profile allocation + */ + template + inline TDataType* PxProfileAllocate( PxAllocatorCallback& inAllocator, const char* file, int inLine ) + { + return PxProfileAllocate( &inAllocator, file, inLine ); + } + + /** + \brief Helper function to encapsulate the profile deallocation + */ + template + inline void PxProfileDeleteAndDeallocate( PxProfileAllocatorWrapper& inAllocator, TDataType* inDType ) + { + PX_ASSERT(inDType); + PxAllocatorCallback& allocator( inAllocator.getAllocator() ); + inDType->~TDataType(); + allocator.deallocate( inDType ); + } + + /** + \brief Helper function to encapsulate the profile deallocation + */ + template + inline void PxProfileDeleteAndDeallocate( PxAllocatorCallback& inAllocator, TDataType* inDType ) + { + PxProfileAllocatorWrapper wrapper( &inAllocator ); + PxProfileDeleteAndDeallocate( wrapper, inDType ); + } + +} } + +#define PX_PROFILE_NEW( allocator, dtype ) new (physx::profile::PxProfileAllocate( allocator, __FILE__, __LINE__ )) dtype +#define PX_PROFILE_DELETE( allocator, obj ) physx::profile::PxProfileDeleteAndDeallocate( allocator, obj ); + +#endif // PXPVDSDK_PXPROFILEALLOCATORWRAPPER_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdClient.h b/src/PhysX/physx/source/pvd/include/PxPvdClient.h new file mode 100644 index 000000000..6ba6a3011 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdClient.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDCLIENT_H +#define PXPVDSDK_PXPVDCLIENT_H + +/** \addtogroup pvd +@{ +*/ +#include "foundation/PxFlags.h" +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +class PvdDataStream; +class PvdUserRenderer; + +/** +\brief PvdClient is the per-client connection to PVD. +It provides callback when PVD is connected/disconnted. +It provides access to the internal object so that advanced users can create extension client. +*/ +class PvdClient +{ + public: + virtual PvdDataStream* getDataStream() = 0; + virtual PvdUserRenderer* getUserRender() = 0; + + virtual bool isConnected() const = 0; + virtual void onPvdConnected() = 0; + virtual void onPvdDisconnected() = 0; + virtual void flush() = 0; + + protected: + virtual ~PvdClient() + { + } +}; + +#if !PX_DOXYGEN +} // namespace pvdsdk +} // namespace physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDCLIENT_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdDataStream.h b/src/PhysX/physx/source/pvd/include/PxPvdDataStream.h new file mode 100644 index 000000000..c16d35b46 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdDataStream.h @@ -0,0 +1,272 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#ifndef PXPVDSDK_PXPVDDATASTREAM_H +#define PXPVDSDK_PXPVDDATASTREAM_H + +/** \addtogroup pvd +@{ +*/ +#include "pvd/PxPvd.h" +#include "PxPvdErrorCodes.h" +#include "PxPvdObjectModelBaseTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +class PvdPropertyDefinitionHelper; + +class PvdMetaDataStream +{ + protected: + virtual ~PvdMetaDataStream() + { + } + + public: + virtual PvdError createClass(const NamespacedName& nm) = 0; + template + PvdError createClass() + { + return createClass(getPvdNamespacedNameForType()); + } + + virtual PvdError deriveClass(const NamespacedName& parent, const NamespacedName& child) = 0; + template + PvdError deriveClass() + { + return deriveClass(getPvdNamespacedNameForType(), getPvdNamespacedNameForType()); + } + + virtual bool isClassExist(const NamespacedName& nm) = 0; + template + bool isClassExist() + { + return isClassExist(getPvdNamespacedNameForType()); + } + + virtual PvdError createProperty(const NamespacedName& clsName, const char* name, const char* semantic, + const NamespacedName& dtypeName, PropertyType::Enum propertyType, + DataRef values = DataRef()) = 0; + template + PvdError createProperty(String name, String semantic = "", PropertyType::Enum propertyType = PropertyType::Scalar, + DataRef values = DataRef()) + { + return createProperty(getPvdNamespacedNameForType(), name, semantic, + getPvdNamespacedNameForType(), propertyType, values); + } + + virtual PvdError createPropertyMessage(const NamespacedName& cls, const NamespacedName& msgName, + DataRef entries, uint32_t messageSizeInBytes) = 0; + + template + PvdError createPropertyMessage(DataRef entries) + { + return createPropertyMessage(getPvdNamespacedNameForType(), getPvdNamespacedNameForType(), + entries, sizeof(TMsgType)); + } +}; + +class PvdInstanceDataStream +{ + protected: + virtual ~PvdInstanceDataStream() + { + } + + public: + virtual PvdError createInstance(const NamespacedName& cls, const void* instance) = 0; + + template + PvdError createInstance(const TDataType* inst) + { + return createInstance(getPvdNamespacedNameForType(), inst); + } + virtual bool isInstanceValid(const void* instance) = 0; + + // If the property will fit or is already completely in memory + virtual PvdError setPropertyValue(const void* instance, String name, DataRef data, + const NamespacedName& incomingTypeName) = 0; + template + PvdError setPropertyValue(const void* instance, String name, const TDataType& value) + { + const uint8_t* dataStart = reinterpret_cast(&value); + return setPropertyValue(instance, name, DataRef(dataStart, dataStart + sizeof(TDataType)), + getPvdNamespacedNameForType()); + } + + template + PvdError setPropertyValue(const void* instance, String name, const TDataType* value, uint32_t numItems) + { + const uint8_t* dataStart = reinterpret_cast(value); + return setPropertyValue(instance, name, + DataRef(dataStart, dataStart + sizeof(TDataType) * numItems), + getPvdNamespacedNameForType()); + } + + // Else if the property is very large (contact reports) you can send it in chunks. + virtual PvdError beginSetPropertyValue(const void* instance, String name, const NamespacedName& incomingTypeName) = 0; + + template + PvdError beginSetPropertyValue(const void* instance, String name) + { + return beginSetPropertyValue(instance, name, getPvdNamespacedNameForType()); + } + virtual PvdError appendPropertyValueData(DataRef data) = 0; + + template + PvdError appendPropertyValueData(const TDataType* value, uint32_t numItems) + { + const uint8_t* dataStart = reinterpret_cast(value); + return appendPropertyValueData(DataRef(dataStart, dataStart + numItems * sizeof(TDataType))); + } + + virtual PvdError endSetPropertyValue() = 0; + + // Set a set of properties to various values on an object. + + virtual PvdError setPropertyMessage(const void* instance, const NamespacedName& msgName, + DataRef data) = 0; + + template + PvdError setPropertyMessage(const void* instance, const TDataType& value) + { + const uint8_t* dataStart = reinterpret_cast(&value); + return setPropertyMessage(instance, getPvdNamespacedNameForType(), + DataRef(dataStart, sizeof(TDataType))); + } + // If you need to send of lot of identical messages, this avoids a hashtable lookup per message. + virtual PvdError beginPropertyMessageGroup(const NamespacedName& msgName) = 0; + + template + PvdError beginPropertyMessageGroup() + { + return beginPropertyMessageGroup(getPvdNamespacedNameForType()); + } + virtual PvdError sendPropertyMessageFromGroup(const void* instance, DataRef data) = 0; + + template + PvdError sendPropertyMessageFromGroup(const void* instance, const TDataType& value) + { + const uint8_t* dataStart = reinterpret_cast(&value); + return sendPropertyMessageFromGroup(instance, DataRef(dataStart, sizeof(TDataType))); + } + + virtual PvdError endPropertyMessageGroup() = 0; + + // These functions ensure the target array doesn't contain duplicates + virtual PvdError pushBackObjectRef(const void* instId, String propName, const void* objRef) = 0; + virtual PvdError removeObjectRef(const void* instId, String propName, const void* objRef) = 0; + + // Instance elimination. + virtual PvdError destroyInstance(const void* key) = 0; + + // Profiling hooks + virtual PvdError beginSection(const void* instance, String name) = 0; + virtual PvdError endSection(const void* instance, String name) = 0; + + // Origin Shift + virtual PvdError originShift(const void* scene, PxVec3 shift) = 0; + + public: + /*For some cases, pvd command cannot be run immediately. For example, when create joints, while the actors may still + *pending for insert, the joints update commands can be run deffered. + */ + class PvdCommand + { + public: + // Assigned is needed for copying + PvdCommand(const PvdCommand&) + { + } + PvdCommand& operator=(const PvdCommand&) + { + return *this; + } + + public: + PvdCommand() + { + } + virtual ~PvdCommand() + { + } + + // Not pure virtual so can have default PvdCommand obj + virtual bool canRun(PvdInstanceDataStream&) + { + return false; + } + virtual void run(PvdInstanceDataStream&) + { + } + }; + + // PVD SDK provide this helper function to allocate cmd's memory and release them at after flush the command queue + virtual void* allocateMemForCmd(uint32_t length) = 0; + + // PVD will call the destructor of PvdCommand object at the end fo flushPvdCommand + virtual void pushPvdCommand(PvdCommand& cmd) = 0; + virtual void flushPvdCommand() = 0; +}; + +class PvdDataStream : public PvdInstanceDataStream, public PvdMetaDataStream +{ + protected: + virtual ~PvdDataStream() + { + } + + public: + virtual void release() = 0; + virtual bool isConnected() = 0; + + virtual void addProfileZone(void* zone, const char* name) = 0; + virtual void addProfileZoneEvent(void* zone, const char* name, uint16_t eventId, bool compileTimeEnabled) = 0; + + virtual PvdPropertyDefinitionHelper& getPropertyDefinitionHelper() = 0; + + virtual void setIsTopLevelUIElement(const void* instance, bool topLevel) = 0; + virtual void sendErrorMessage(uint32_t code, const char* message, const char* file, uint32_t line) = 0; + virtual void updateCamera(const char* name, const PxVec3& origin, const PxVec3& up, const PxVec3& target) = 0; + +/** + \brief Create a new PvdDataStream. + \param pvd A pointer to a valid PxPvd instance. This must be non-null. +*/ + static PvdDataStream* create(PxPvd* pvd); +}; +#if !PX_DOXYGEN +} // pvdsdk +} // physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDDATASTREAM_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h b/src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h new file mode 100644 index 000000000..503491b0c --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#ifndef PXPVDSDK_PXPVDDATASTREAMHELPERS_H +#define PXPVDSDK_PXPVDDATASTREAMHELPERS_H + +/** \addtogroup pvd +@{ +*/ +#include "PxPvdObjectModelBaseTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +class PvdPropertyDefinitionHelper +{ + protected: + virtual ~PvdPropertyDefinitionHelper() + { + } + + public: + /** + Push a name c such that it appends such as a.b.c. + */ + virtual void pushName(const char* inName, const char* inAppendStr = ".") = 0; + /** + Push a name c such that it appends like a.b[c] + */ + virtual void pushBracketedName(const char* inName, const char* leftBracket = "[", const char* rightBracket = "]") = 0; + /** + * Pop the current name + */ + virtual void popName() = 0; + + virtual void clearNameStack() = 0; + /** + * Get the current name at the top of the name stack. + * Would return "a.b.c" or "a.b[c]" in the above examples. + */ + virtual const char* getTopName() = 0; + + virtual void addNamedValue(const char* name, uint32_t value) = 0; + virtual void clearNamedValues() = 0; + virtual DataRef getNamedValues() = 0; + + /** + * Define a property using the top of the name stack and the passed-in semantic + */ + virtual void createProperty(const NamespacedName& clsName, const char* inSemantic, const NamespacedName& dtypeName, + PropertyType::Enum propType = PropertyType::Scalar) = 0; + + template + void createProperty(const char* inSemantic = "", PropertyType::Enum propType = PropertyType::Scalar) + { + createProperty(getPvdNamespacedNameForType(), inSemantic, getPvdNamespacedNameForType(), + propType); + } + + // The datatype used for instances needs to be pointer unless you actually have pvdsdk::InstanceId members on your + // value structs. + virtual void addPropertyMessageArg(const NamespacedName& inDatatype, uint32_t inOffset, uint32_t inSize) = 0; + + template + void addPropertyMessageArg(uint32_t offset) + { + addPropertyMessageArg(getPvdNamespacedNameForType(), offset, static_cast(sizeof(TDataType))); + } + virtual void addPropertyMessage(const NamespacedName& clsName, const NamespacedName& msgName, + uint32_t inStructSizeInBytes) = 0; + template + void addPropertyMessage() + { + addPropertyMessage(getPvdNamespacedNameForType(), getPvdNamespacedNameForType(), + static_cast(sizeof(TMsgType))); + } + virtual void clearPropertyMessageArgs() = 0; + + void clearBufferedData() + { + clearNameStack(); + clearPropertyMessageArgs(); + clearNamedValues(); + } +}; + +#if !PX_DOXYGEN +} // pvdsdk +} // physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDDATASTREAMHELPERS_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h b/src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h new file mode 100644 index 000000000..8865b6aa6 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h @@ -0,0 +1,62 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#ifndef PXPVDSDK_PXPVDERRORCODES_H +#define PXPVDSDK_PXPVDERRORCODES_H + +/** \addtogroup pvd +@{ +*/ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +struct PvdErrorType +{ + enum Enum + { + Success = 0, + NetworkError, + ArgumentError, + Disconnect, + InternalProblem + }; +}; + +typedef PvdErrorType::Enum PvdError; + +#if !PX_DOXYGEN +} +} +#endif +/** @} */ +#endif // PXPVDSDK_PXPVDERRORCODES_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h b/src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h new file mode 100644 index 000000000..66384a726 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h @@ -0,0 +1,428 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDOBJECTMODELBASETYPES_H +#define PXPVDSDK_PXPVDOBJECTMODELBASETYPES_H + +/** \addtogroup pvd +@{ +*/ +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +using namespace physx; + +inline const char* nonNull(const char* str) +{ + return str ? str : ""; +} +// strcmp will crash if passed a null string, however, +// so we need to make sure that doesn't happen. We do that +// by equating NULL and the empty string, "". +inline bool safeStrEq(const char* lhs, const char* rhs) +{ + return ::strcmp(nonNull(lhs), nonNull(rhs)) == 0; +} + +// Does this string have useful information in it. +inline bool isMeaningful(const char* str) +{ + return *(nonNull(str)) > 0; +} + +inline uint32_t safeStrLen(const char* str) +{ + str = nonNull(str); + return static_cast(strlen(str)); +} + +struct ObjectRef +{ + int32_t mInstanceId; + + ObjectRef(int32_t iid = -1) : mInstanceId(iid) + { + } + operator int32_t() const + { + return mInstanceId; + } + bool hasValue() const + { + return mInstanceId > 0; + } +}; + +struct U32Array4 +{ + uint32_t mD0; + uint32_t mD1; + uint32_t mD2; + uint32_t mD3; + U32Array4(uint32_t d0, uint32_t d1, uint32_t d2, uint32_t d3) : mD0(d0), mD1(d1), mD2(d2), mD3(d3) + { + } + U32Array4() : mD0(0), mD1(0), mD2(0), mD3(0) + { + } +}; + +typedef bool PvdBool; +typedef const char* String; +typedef void* VoidPtr; +typedef double PvdF64; +typedef float PvdF32; +typedef int64_t PvdI64; +typedef uint64_t PvdU64; +typedef int32_t PvdI32; +typedef uint32_t PvdU32; +typedef int16_t PvdI16; +typedef uint16_t PvdU16; +typedef int8_t PvdI8; +typedef uint8_t PvdU8; + +struct PvdColor +{ + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + PvdColor(uint8_t _r, uint8_t _g, uint8_t _b, uint8_t _a = 255) : r(_r), g(_g), b(_b), a(_a) + { + } + PvdColor() : r(0), g(0), b(0), a(255) + { + } + PvdColor(uint32_t abgr) + { + uint8_t* valPtr = reinterpret_cast(&abgr); + r = valPtr[0]; + g = valPtr[1]; + b = valPtr[2]; + a = valPtr[3]; + } +}; + +struct StringHandle +{ + uint32_t mHandle; + StringHandle(uint32_t val = 0) : mHandle(val) + { + } + operator uint32_t() const + { + return mHandle; + } +}; + +#define DECLARE_TYPES \ +DECLARE_BASE_PVD_TYPE(PvdI8) \ +DECLARE_BASE_PVD_TYPE(PvdU8) \ +DECLARE_BASE_PVD_TYPE(PvdI16) \ +DECLARE_BASE_PVD_TYPE(PvdU16) \ +DECLARE_BASE_PVD_TYPE(PvdI32) \ +DECLARE_BASE_PVD_TYPE(PvdU32) \ +DECLARE_BASE_PVD_TYPE(PvdI64) \ +DECLARE_BASE_PVD_TYPE(PvdU64) \ +DECLARE_BASE_PVD_TYPE(PvdF32) \ +DECLARE_BASE_PVD_TYPE(PvdF64) \ +DECLARE_BASE_PVD_TYPE(PvdBool) \ +DECLARE_BASE_PVD_TYPE(PvdColor) \ +DECLARE_BASE_PVD_TYPE(String) \ +DECLARE_BASE_PVD_TYPE(StringHandle) \ +DECLARE_BASE_PVD_TYPE(ObjectRef) \ +DECLARE_BASE_PVD_TYPE(VoidPtr) \ +DECLARE_BASE_PVD_TYPE(PxVec2) \ +DECLARE_BASE_PVD_TYPE(PxVec3) \ +DECLARE_BASE_PVD_TYPE(PxVec4) \ +DECLARE_BASE_PVD_TYPE(PxBounds3) \ +DECLARE_BASE_PVD_TYPE(PxQuat) \ +DECLARE_BASE_PVD_TYPE(PxTransform) \ +DECLARE_BASE_PVD_TYPE(PxMat33) \ +DECLARE_BASE_PVD_TYPE(PxMat44) \ +DECLARE_BASE_PVD_TYPE(U32Array4) + +struct PvdBaseType +{ + enum Enum + { + None = 0, + InternalStart = 1, + InternalStop = 64, +#define DECLARE_BASE_PVD_TYPE(type) type, + DECLARE_TYPES + Last +#undef DECLARE_BASE_PVD_TYPE + }; +}; +struct NamespacedName +{ + String mNamespace; + String mName; + NamespacedName(String ns, String nm) : mNamespace(ns), mName(nm) + { + } + NamespacedName(String nm = "") : mNamespace(""), mName(nm) + { + } + bool operator==(const NamespacedName& other) const + { + return safeStrEq(mNamespace, other.mNamespace) && safeStrEq(mName, other.mName); + } +}; + +struct NamedValue +{ + String mName; + uint32_t mValue; + NamedValue(String nm = "", uint32_t val = 0) : mName(nm), mValue(val) + { + } +}; + +template +struct BaseDataTypeToTypeMap +{ + bool compile_error; +}; +template +struct BaseTypeToDataTypeMap +{ + bool compile_error; +}; + +// Users can extend this mapping with new datatypes. +template +struct PvdDataTypeToNamespacedNameMap +{ + bool Name; +}; +// This mapping tells you the what class id to use for the base datatypes +// +#define DECLARE_BASE_PVD_TYPE(type) \ + template <> \ + struct BaseDataTypeToTypeMap \ + { \ + enum Enum \ + { \ + BaseTypeEnum = PvdBaseType::type \ + }; \ + }; \ + template <> \ + struct BaseDataTypeToTypeMap \ + { \ + enum Enum \ + { \ + BaseTypeEnum = PvdBaseType::type \ + }; \ + }; \ + template <> \ + struct BaseTypeToDataTypeMap \ + { \ + typedef type TDataType; \ + }; \ + template <> \ + struct PvdDataTypeToNamespacedNameMap \ + { \ + NamespacedName Name; \ + PvdDataTypeToNamespacedNameMap() : Name("physx3", #type) \ + { \ + } \ + }; \ + template <> \ + struct PvdDataTypeToNamespacedNameMap \ + { \ + NamespacedName Name; \ + PvdDataTypeToNamespacedNameMap() : Name("physx3", #type) \ + { \ + } \ + }; + +DECLARE_TYPES +#undef DECLARE_BASE_PVD_TYPE + +template +inline int32_t getPvdTypeForType() +{ + return static_cast(BaseDataTypeToTypeMap::BaseTypeEnum); +} +template +inline NamespacedName getPvdNamespacedNameForType() +{ + return PvdDataTypeToNamespacedNameMap().Name; +} + +#define DEFINE_PVD_TYPE_NAME_MAP(type, ns, name) \ + template <> \ + struct PvdDataTypeToNamespacedNameMap \ + { \ + NamespacedName Name; \ + PvdDataTypeToNamespacedNameMap() : Name(ns, name) \ + { \ + } \ + }; + +#define DEFINE_PVD_TYPE_ALIAS(newType, oldType) \ + template <> \ + struct PvdDataTypeToNamespacedNameMap \ + { \ + NamespacedName Name; \ + PvdDataTypeToNamespacedNameMap() : Name(PvdDataTypeToNamespacedNameMap().Name) \ + { \ + } \ + }; + +DEFINE_PVD_TYPE_ALIAS(const void*, void*) + +struct ArrayData +{ + uint8_t* mBegin; + uint8_t* mEnd; + uint8_t* mCapacity; //>= stop + ArrayData(uint8_t* beg = NULL, uint8_t* end = NULL, uint8_t* cap = NULL) : mBegin(beg), mEnd(end), mCapacity(cap) + { + } + uint8_t* begin() + { + return mBegin; + } + uint8_t* end() + { + return mEnd; + } + uint32_t byteCapacity() + { + return static_cast(mCapacity - mBegin); + } + uint32_t byteSize() const + { + return static_cast(mEnd - mBegin); + } // in bytes + uint32_t numberOfItems(uint32_t objectByteSize) + { + if(objectByteSize) + return byteSize() / objectByteSize; + return 0; + } + + void forgetData() + { + mBegin = mEnd = mCapacity = 0; + } +}; + +template +class DataRef +{ + const T* mBegin; + const T* mEnd; + + public: + DataRef(const T* b, uint32_t count) : mBegin(b), mEnd(b + count) + { + } + DataRef(const T* b = NULL, const T* e = NULL) : mBegin(b), mEnd(e) + { + } + DataRef(const DataRef& o) : mBegin(o.mBegin), mEnd(o.mEnd) + { + } + DataRef& operator=(const DataRef& o) + { + mBegin = o.mBegin; + mEnd = o.mEnd; + return *this; + } + uint32_t size() const + { + return static_cast(mEnd - mBegin); + } + const T* begin() const + { + return mBegin; + } + const T* end() const + { + return mEnd; + } + const T& operator[](uint32_t idx) const + { + PX_ASSERT(idx < size()); + return mBegin[idx]; + } + const T& back() const + { + PX_ASSERT(mEnd > mBegin); + return *(mEnd - 1); + } +}; + +struct PropertyType +{ + enum Enum + { + Unknown = 0, + Scalar, + Array + }; +}; + +// argument to the create property message function +struct PropertyMessageArg +{ + String mPropertyName; + NamespacedName mDatatypeName; + // where in the message this property starts. + uint32_t mMessageOffset; + // size of this entry object + uint32_t mByteSize; + + PropertyMessageArg(String propName, NamespacedName dtype, uint32_t msgOffset, uint32_t byteSize) + : mPropertyName(propName), mDatatypeName(dtype), mMessageOffset(msgOffset), mByteSize(byteSize) + { + } + PropertyMessageArg() : mPropertyName(""), mMessageOffset(0), mByteSize(0) + { + } +}; + +class PvdUserRenderer; +DEFINE_PVD_TYPE_NAME_MAP(PvdUserRenderer, "_debugger_", "PvdUserRenderer") + +#if !PX_DOXYGEN +} +} +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDOBJECTMODELBASETYPES_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h b/src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h new file mode 100644 index 000000000..ef851c596 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h @@ -0,0 +1,140 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDRENDERBUFFER_H +#define PXPVDSDK_PXPVDRENDERBUFFER_H + +/** \addtogroup pvd +@{ +*/ + +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +/** +\brief Default color values used for debug rendering. +*/ +struct PvdDebugColor +{ + enum Enum + { + eARGB_BLACK = 0xff000000, + eARGB_RED = 0xffff0000, + eARGB_GREEN = 0xff00ff00, + eARGB_BLUE = 0xff0000ff, + eARGB_YELLOW = 0xffffff00, + eARGB_MAGENTA = 0xffff00ff, + eARGB_CYAN = 0xff00ffff, + eARGB_WHITE = 0xffffffff, + eARGB_GREY = 0xff808080, + eARGB_DARKRED = 0x88880000, + eARGB_DARKGREEN = 0x88008800, + eARGB_DARKBLUE = 0x88000088 + }; +}; + +/** +\brief Used to store a single point and colour for debug rendering. +*/ +struct PvdDebugPoint +{ + PvdDebugPoint(const PxVec3& p, const uint32_t& c) : pos(p), color(c) + { + } + + PxVec3 pos; + uint32_t color; +}; + +/** +\brief Used to store a single line and colour for debug rendering. +*/ +struct PvdDebugLine +{ + PvdDebugLine(const PxVec3& p0, const PxVec3& p1, const uint32_t& c) : pos0(p0), color0(c), pos1(p1), color1(c) + { + } + + PxVec3 pos0; + uint32_t color0; + PxVec3 pos1; + uint32_t color1; +}; + +/** +\brief Used to store a single triangle and colour for debug rendering. +*/ +struct PvdDebugTriangle +{ + PvdDebugTriangle(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const uint32_t& c) + : pos0(p0), color0(c), pos1(p1), color1(c), pos2(p2), color2(c) + { + } + + PxVec3 pos0; + uint32_t color0; + PxVec3 pos1; + uint32_t color1; + PxVec3 pos2; + uint32_t color2; +}; + +/** +\brief Used to store a text for debug rendering. Doesn't own 'string' array. +*/ +struct PvdDebugText +{ + PvdDebugText() : string(0) + { + } + + PvdDebugText(const PxVec3& p, const float& s, const uint32_t& c, const char* str) + : position(p), size(s), color(c), string(str) + { + } + + PxVec3 position; + float size; + uint32_t color; + const char* string; +}; + +#if !PX_DOXYGEN +} +} // namespace physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDRENDERBUFFER_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h b/src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h new file mode 100644 index 000000000..9598fe410 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h @@ -0,0 +1,107 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#ifndef PXPVDSDK_PXPVDUSERRENDERER_H +#define PXPVDSDK_PXPVDUSERRENDERER_H + +/** \addtogroup pvd +@{ +*/ +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "pvd/PxPvd.h" + +#include "PxPvdDataStream.h" +#include "PxPvdRenderBuffer.h" +#include "PsUserAllocated.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPvd; + +#if !PX_DOXYGEN +namespace pvdsdk +{ +#endif + +class RendererEventClient; + +class PvdUserRenderer : public shdfnd::UserAllocated +{ + protected: + virtual ~PvdUserRenderer() + { + } + + public: + virtual void release() = 0; + virtual void setClient(RendererEventClient* client) = 0; + + // Instance to associate the further rendering with. + virtual void setInstanceId(const void* instanceId) = 0; + // Draw these points associated with this instance + virtual void drawPoints(const PvdDebugPoint* points, uint32_t count) = 0; + // Draw these lines associated with this instance + virtual void drawLines(const PvdDebugLine* lines, uint32_t count) = 0; + // Draw these triangles associated with this instance + virtual void drawTriangles(const PvdDebugTriangle* triangles, uint32_t count) = 0; + // Draw this text associated with this instance + virtual void drawText(const PvdDebugText& text) = 0; + + // Draw SDK debug render + virtual void drawRenderbuffer(const PvdDebugPoint* pointData, uint32_t pointCount, const PvdDebugLine* lineData, + uint32_t lineCount, const PvdDebugTriangle* triangleData, uint32_t triangleCount) = 0; + + // Constraint visualization routines + virtual void visualizeJointFrames(const PxTransform& parent, const PxTransform& child) = 0; + virtual void visualizeLinearLimit(const PxTransform& t0, const PxTransform& t1, float value, bool active) = 0; + virtual void visualizeAngularLimit(const PxTransform& t0, float lower, float upper, bool active) = 0; + virtual void visualizeLimitCone(const PxTransform& t, float tanQSwingY, float tanQSwingZ, bool active) = 0; + virtual void visualizeDoubleCone(const PxTransform& t, float angle, bool active) = 0; + + // Clear the immedate buffer. + virtual void flushRenderEvents() = 0; + + static PvdUserRenderer* create(uint32_t bufferSize = 0x2000); +}; + +class RendererEventClient +{ + public: + virtual ~RendererEventClient(){} + + virtual void handleBufferFlush(const uint8_t* inData, uint32_t inLength) = 0; +}; + +#if !PX_DOXYGEN +} +} +#endif +/** @} */ +#endif // PXPVDSDK_PXPVDUSERRENDERER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h b/src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h new file mode 100644 index 000000000..5ac872652 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h @@ -0,0 +1,58 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILECONTEXTPROVIDER_H +#define PXPVDSDK_PXPROFILECONTEXTPROVIDER_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + + struct PxProfileEventExecutionContext + { + uint32_t mThreadId; + uint8_t mCpuId; + uint8_t mThreadPriority; + + PxProfileEventExecutionContext( uint32_t inThreadId = 0, uint8_t inThreadPriority = 2 /*eThreadPriorityNormal*/, uint8_t inCpuId = 0 ) + : mThreadId( inThreadId ) + , mCpuId( inCpuId ) + , mThreadPriority( inThreadPriority ) + { + } + + bool operator==( const PxProfileEventExecutionContext& inOther ) const + { + return mThreadId == inOther.mThreadId + && mCpuId == inOther.mCpuId + && mThreadPriority == inOther.mThreadPriority; + } + }; + +} } + +#endif // PXPVDSDK_PXPROFILECONTEXTPROVIDER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h b/src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h new file mode 100644 index 000000000..fdf68ec72 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h @@ -0,0 +1,52 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILECONTEXTPROVIDERIMPL_H +#define PXPVDSDK_PXPROFILECONTEXTPROVIDERIMPL_H + +#include "PxProfileContextProvider.h" + +#include "PsThread.h" + +namespace physx { namespace profile { + + struct PxDefaultContextProvider + { + PxProfileEventExecutionContext getExecutionContext() + { + shdfnd::Thread::Id theId( shdfnd::Thread::getId() ); + return PxProfileEventExecutionContext( static_cast( theId ), static_cast( shdfnd::ThreadPriority::eNORMAL ), 0 ); + } + + uint32_t getThreadId() + { + return static_cast( shdfnd::Thread::getId() ); + } + }; +} } + +#endif // PXPVDSDK_PXPROFILECONTEXTPROVIDERIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h new file mode 100644 index 000000000..9b8d4c0cf --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h @@ -0,0 +1,167 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEDATABUFFER_H +#define PXPVDSDK_PXPROFILEDATABUFFER_H + +#include "PxProfileAllocatorWrapper.h" +#include "PxProfileMemoryBuffer.h" +#include "PxProfileEventBufferClient.h" + +namespace physx { namespace profile { + + template + class DataBuffer //base class for buffers that cache data and then dump the data to clients. + { + public: + typedef TMutex TMutexType; + typedef TScopedLock TScopedLockType; + typedef PxProfileWrapperNamedAllocator TU8AllocatorType; + + typedef MemoryBuffer TMemoryBufferType; + typedef PxProfileArray TBufferClientArray; + + protected: + + PxProfileAllocatorWrapper mWrapper; + TMemoryBufferType mDataArray; + TBufferClientArray mBufferClients; + uint32_t mBufferFullAmount; + EventContextInformation mEventContextInformation; + TMutexType* mBufferMutex; + volatile bool mHasClients; + EventSerializer mSerializer; + + public: + + DataBuffer( PxAllocatorCallback* inFoundation + , uint32_t inBufferFullAmount + , TMutexType* inBufferMutex + , const char* inAllocationName ) + : mWrapper( inFoundation ) + , mDataArray( TU8AllocatorType( mWrapper, inAllocationName ) ) + , mBufferClients( mWrapper ) + , mBufferFullAmount( inBufferFullAmount ) + , mBufferMutex( inBufferMutex ) + , mHasClients( false ) + , mSerializer( &mDataArray ) + { + //The data array is never resized really. We ensure + //it is bigger than it will ever need to be. + mDataArray.reserve( inBufferFullAmount + 68 ); + } + + virtual ~DataBuffer() + { + while(mBufferClients.size() ) + { + removeClient( *mBufferClients[0] ); + } + } + + PxProfileAllocatorWrapper& getWrapper() { return mWrapper; } + TMutexType* getBufferMutex() { return mBufferMutex; } + void setBufferMutex(TMutexType* mutex) { mBufferMutex = mutex; } + + void addClient( PxProfileEventBufferClient& inClient ) + { + TScopedLockType lock( mBufferMutex ); + mBufferClients.pushBack( &inClient ); + mHasClients = true; + } + + void removeClient( PxProfileEventBufferClient& inClient ) + { + TScopedLockType lock( mBufferMutex ); + for ( uint32_t idx =0; idx < mBufferClients.size(); ++idx ) + { + if (mBufferClients[idx] == &inClient ) + { + inClient.handleClientRemoved(); + mBufferClients.replaceWithLast( idx ); + break; + } + } + mHasClients = mBufferClients.size() != 0; + } + + + bool hasClients() const + { + return mHasClients; + } + + virtual void flushEvents() + { + TScopedLockType lock(mBufferMutex); + const uint8_t* theData = mDataArray.begin(); + uint32_t theDataSize = mDataArray.size(); + sendDataToClients(theData, theDataSize); + mDataArray.clear(); + clearCachedData(); + } + + //Used for chaining together event buffers. + virtual void handleBufferFlush( const uint8_t* inData, uint32_t inDataSize ) + { + TScopedLockType lock( mBufferMutex ); + if ( inData && inDataSize ) + { + clearCachedData(); + if ( mDataArray.size() + inDataSize >= mBufferFullAmount ) + flushEvents(); + if ( inDataSize >= mBufferFullAmount ) + sendDataToClients( inData, inDataSize ); + else + mDataArray.write( inData, inDataSize ); + } + } + + protected: + virtual void clearCachedData() + { + } + + private: + + void sendDataToClients( const uint8_t* inData, uint32_t inDataSize ) + { + uint32_t clientCount = mBufferClients.size(); + for( uint32_t idx =0; idx < clientCount; ++idx ) + mBufferClients[idx]->handleBufferFlush( inData, inDataSize ); + } + + }; + +}} + + +#endif // PXPVDSDK_PXPROFILEDATABUFFER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h b/src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h new file mode 100644 index 000000000..a3e87860b --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h @@ -0,0 +1,218 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEDATAPARSING_H +#define PXPVDSDK_PXPROFILEDATAPARSING_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + + //Converts datatypes without using type punning. + struct BlockParserDataConverter + { + union + { + uint8_t mU8[8]; + uint16_t mU16[4]; + uint32_t mU32[2]; + uint64_t mU64[1]; + + int8_t mI8[8]; + int16_t mI16[4]; + int32_t mI32[2]; + int64_t mI64[1]; + + + float mF32[2]; + double mF64[1]; + }; + + template inline TDataType convert() { PX_ASSERT( false ); return TDataType(); } + + template + inline void convert( const TDataType& ) {} + }; + + template<> inline uint8_t BlockParserDataConverter::convert() { return mU8[0]; } + template<> inline uint16_t BlockParserDataConverter::convert() { return mU16[0]; } + template<> inline uint32_t BlockParserDataConverter::convert() { return mU32[0]; } + template<> inline uint64_t BlockParserDataConverter::convert() { return mU64[0]; } + template<> inline int8_t BlockParserDataConverter::convert() { return mI8[0]; } + template<> inline int16_t BlockParserDataConverter::convert() { return mI16[0]; } + template<> inline int32_t BlockParserDataConverter::convert() { return mI32[0]; } + template<> inline int64_t BlockParserDataConverter::convert() { return mI64[0]; } + template<> inline float BlockParserDataConverter::convert() { return mF32[0]; } + template<> inline double BlockParserDataConverter::convert() { return mF64[0]; } + + template<> inline void BlockParserDataConverter::convert( const uint8_t& inData ) { mU8[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const uint16_t& inData ) { mU16[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const uint32_t& inData ) { mU32[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const uint64_t& inData ) { mU64[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const int8_t& inData ) { mI8[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const int16_t& inData ) { mI16[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const int32_t& inData ) { mI32[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const int64_t& inData ) { mI64[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const float& inData ) { mF32[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const double& inData ) { mF64[0] = inData; } + + + //Handles various details around parsing blocks of uint8_t data. + struct BlockParseFunctions + { + template + static inline void swapBytes( uint8_t* inData ) + { + for ( uint32_t idx = 0; idx < ByteCount/2; ++idx ) + { + uint32_t endIdx = ByteCount-idx-1; + uint8_t theTemp = inData[idx]; + inData[idx] = inData[endIdx]; + inData[endIdx] = theTemp; + } + } + + static inline bool checkLength( const uint8_t* inStart, const uint8_t* inStop, uint32_t inLength ) + { + return static_cast(inStop - inStart) >= inLength; + } + //warning work-around + template + static inline T val(T v) {return v;} + + template + static inline bool parse( const uint8_t*& inStart, const uint8_t* inStop, TDataType& outData ) + { + if ( checkLength( inStart, inStop, sizeof( TDataType ) ) ) + { + BlockParserDataConverter theConverter; + for ( uint32_t idx =0; idx < sizeof( TDataType ); ++idx ) + theConverter.mU8[idx] = inStart[idx]; + if ( val(DoSwapBytes)) + swapBytes( theConverter.mU8 ); + outData = theConverter.convert(); + inStart += sizeof( TDataType ); + return true; + } + return false; + } + + template + static inline bool parseBlock( const uint8_t*& inStart, const uint8_t* inStop, TDataType* outData, uint32_t inNumItems ) + { + uint32_t desired = sizeof(TDataType)*inNumItems; + if ( checkLength( inStart, inStop, desired ) ) + { + if ( val(DoSwapBytes) ) + { + for ( uint32_t item = 0; item < inNumItems; ++item ) + { + BlockParserDataConverter theConverter; + for ( uint32_t idx =0; idx < sizeof( TDataType ); ++idx ) + theConverter.mU8[idx] = inStart[idx]; + swapBytes( theConverter.mU8 ); + outData[item] = theConverter.convert(); + inStart += sizeof(TDataType); + } + } + else + { + uint8_t* target = reinterpret_cast(outData); + memmove( target, inStart, desired ); + inStart += desired; + } + return true; + } + return false; + } + + //In-place byte swapping block + template + static inline bool parseBlock( uint8_t*& inStart, const uint8_t* inStop, uint32_t inNumItems ) + { + uint32_t desired = sizeof(TDataType)*inNumItems; + if ( checkLength( inStart, inStop, desired ) ) + { + if ( val(DoSwapBytes) ) + { + for ( uint32_t item = 0; item < inNumItems; ++item, inStart += sizeof( TDataType ) ) + swapBytes( inStart ); //In-place swap. + } + else + inStart += sizeof( TDataType ) * inNumItems; + return true; + } + return false; + } + }; + + //Wraps the begin/end keeping track of them. + template + struct BlockParser + { + const uint8_t* mBegin; + const uint8_t* mEnd; + BlockParser( const uint8_t* inBegin=NULL, const uint8_t* inEnd=NULL ) + : mBegin( inBegin ) + , mEnd( inEnd ) + { + } + inline bool hasMoreData() const { return mBegin != mEnd; } + inline bool checkLength( uint32_t inLength ) { return BlockParseFunctions::checkLength( mBegin, mEnd, inLength ); } + + template + inline bool read( TDataType& outDatatype ) { return BlockParseFunctions::parse( mBegin, mEnd, outDatatype ); } + + template + inline bool readBlock( TDataType* outDataPtr, uint32_t inNumItems ) { return BlockParseFunctions::parseBlock( mBegin, mEnd, outDataPtr, inNumItems ); } + + template + inline bool readBlock( uint32_t inNumItems ) + { + uint8_t* theTempPtr = const_cast(mBegin); + bool retval = BlockParseFunctions::parseBlock( theTempPtr, mEnd, inNumItems ); + mBegin = theTempPtr; + return retval; + } + + uint32_t amountLeft() const { return static_cast( mEnd - mBegin ); } + }; + + //Reads the data without checking for error conditions + template + inline TDataType blockParserRead( TBlockParserType& inType ) + { + TDataType retval; + inType.read( retval ); + return retval; + } +}} + +#endif // PXPVDSDK_PXPROFILEDATAPARSING_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h new file mode 100644 index 000000000..00c82db80 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h @@ -0,0 +1,267 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEEVENTBUFFER_H +#define PXPVDSDK_PXPROFILEEVENTBUFFER_H + +#include "PxProfileEvents.h" +#include "PxProfileEventSerialization.h" +#include "PxProfileDataBuffer.h" +#include "PxProfileContextProvider.h" + +#include "PsTime.h" + +namespace physx { namespace profile { + + /** + * An event buffer maintains an in-memory buffer of events. When this buffer is full + * it sends to buffer to all handlers registered and resets the buffer. + * + * It is parameterized in four ways. The first is a context provider that provides + * both thread id and context id. + * + * The second is the mutex (which may be null) and a scoped locking mechanism. Thus the buffer + * may be used in a multithreaded context but clients of the buffer don't pay for this if they + * don't intend to use it this way. + * + * Finally the buffer may use an event filtering mechanism. This mechanism needs one function, + * namely isEventEnabled( uint8_t subsystem, uint8_t eventId ). + * + * All of these systems can be parameterized at compile time leading to an event buffer + * that should be as fast as possible given the constraints. + * + * Buffers may be chained together as this buffer has a handleBufferFlush method that + * will grab the mutex and add the data to this event buffer. + * + * Overall, lets look at the PhysX SDK an how all the pieces fit together. + * The SDK should have a mutex-protected event buffer where actual devs or users of PhysX + * can register handlers. This buffer has slow but correct implementations of the + * context provider interface. + * + * The SDK object should also have a concrete event filter which was used in the + * construction of the event buffer and which it exposes through opaque interfaces. + * + * The SDK should protect its event buffer and its event filter from multithreaded + * access and thus this provides the safest and slowest way to log events and to + * enable/disable events. + * + * Each scene should also have a concrete event filter. This filter is updated from + * the SDK event filter (in a mutex protected way) every frame. Thus scenes can change + * their event filtering on a frame-by-frame basis. It means that tasks running + * under the scene don't need a mutex when accessing the filter. + * + * Furthermore the scene should have an event buffer that always sets the context id + * on each event to the scene. This allows PVD and other systems to correlate events + * to scenes. Scenes should provide access only to a relative event sending system + * that looks up thread id upon each event but uses the scene id. + * + * The SDK's event buffer should be setup as an EventBufferClient for each scene's + * event buffer. Thus the SDK should expose an EventBufferClient interface that + * any client can use. + * + * For extremely *extremely* performance sensitive areas we should create a specialized + * per-scene, per-thread event buffer that is set on the task for these occasions. This buffer + * uses a trivial event context setup with the scene's context id and the thread id. It should + * share the scene's concrete event filter and it should have absolutely no locking. It should + * empty into the scene's event buffer which in some cases should empty into the SDK's event buffer + * which when full will push events all the way out of the system. The task should *always* flush + * the event buffer (if it has one) when it is finished; nothing else will work reliably. + * + * If the per-scene,per-thread event buffer is correctly parameterized and fully defined adding + * a new event should be an inline operation requiring no mutex grabs in the common case. I don't + * believe you can get faster event production than this; the events are as small as possible (all + * relative events) and they are all produced inline resulting in one 4 byte header and one + * 8 byte timestamp per event. Reducing the memory pressure in this way reduces the communication + * overhead, the mutex grabs, basically everything that makes profiling expensive at the cost + * of a per-scene,per-thread event buffer (which could easily be reduced to a per-thread event + * buffer. + */ + template + class EventBuffer : public DataBuffer + { + public: + typedef DataBuffer TBaseType; + typedef TContextProvider TContextProviderType; + typedef TEventFilter TEventFilterType; + typedef typename TBaseType::TMutexType TMutexType; + typedef typename TBaseType::TScopedLockType TScopedLockType; + typedef typename TBaseType::TU8AllocatorType TU8AllocatorType; + typedef typename TBaseType::TMemoryBufferType TMemoryBufferType; + typedef typename TBaseType::TBufferClientArray TBufferClientArray; + + private: + EventContextInformation mEventContextInformation; + uint64_t mLastTimestamp; + TContextProvider mContextProvider; + TEventFilterType mEventFilter; + + public: + EventBuffer(PxAllocatorCallback* inFoundation + , uint32_t inBufferFullAmount + , const TContextProvider& inProvider + , TMutexType* inBufferMutex + , const TEventFilterType& inEventFilter ) + : TBaseType( inFoundation, inBufferFullAmount, inBufferMutex, "struct physx::profile::ProfileEvent" ) + , mLastTimestamp( 0 ) + , mContextProvider( inProvider ) + , mEventFilter( inEventFilter ) + { + memset(&mEventContextInformation,0,sizeof(EventContextInformation)); + } + + TContextProvider& getContextProvider() { return mContextProvider; } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint32_t threadId, uint64_t contextId, uint8_t cpuId, uint8_t threadPriority, uint64_t inTimestamp) + { + TScopedLockType lock(TBaseType::mBufferMutex); + if ( mEventFilter.isEventEnabled( inId ) ) + { + StartEvent theEvent; + theEvent.init( threadId, contextId, cpuId, threadPriority, inTimestamp ); + doAddProfileEvent( inId, theEvent ); + } + } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint64_t contextId) + { + PxProfileEventExecutionContext ctx( mContextProvider.getExecutionContext() ); + startEvent( inId, ctx.mThreadId, contextId, ctx.mCpuId, static_cast(ctx.mThreadPriority), shdfnd::Time::getCurrentCounterValue() ); + } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint64_t contextId, uint32_t threadId) + { + startEvent( inId, threadId, contextId, 0, 0, shdfnd::Time::getCurrentCounterValue() ); + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint32_t threadId, uint64_t contextId, uint8_t cpuId, uint8_t threadPriority, uint64_t inTimestamp) + { + TScopedLockType lock(TBaseType::mBufferMutex); + if ( mEventFilter.isEventEnabled( inId ) ) + { + StopEvent theEvent; + theEvent.init( threadId, contextId, cpuId, threadPriority, inTimestamp ); + doAddProfileEvent( inId, theEvent ); + } + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint64_t contextId) + { + PxProfileEventExecutionContext ctx( mContextProvider.getExecutionContext() ); + stopEvent( inId, ctx.mThreadId, contextId, ctx.mCpuId, static_cast(ctx.mThreadPriority), shdfnd::Time::getCurrentCounterValue() ); + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint64_t contextId, uint32_t threadId) + { + stopEvent( inId, threadId, contextId, 0, 0, shdfnd::Time::getCurrentCounterValue() ); + } + + inline void eventValue( uint16_t inId, uint64_t contextId, int64_t inValue ) + { + eventValue( inId, mContextProvider.getThreadId(), contextId, inValue ); + } + + inline void eventValue( uint16_t inId, uint32_t threadId, uint64_t contextId, int64_t inValue ) + { + TScopedLockType lock( TBaseType::mBufferMutex ); + EventValue theEvent; + theEvent.init( inValue, contextId, threadId ); + EventHeader theHeader( static_cast( getEventType() ), inId ); + //set the header relative timestamp; + EventValue& theType( theEvent ); + theType.setupHeader( theHeader ); + sendEvent( theHeader, theType ); + } + + void flushProfileEvents() + { + TBaseType::flushEvents(); + } + + void release() + { + PX_PROFILE_DELETE( TBaseType::mWrapper.mUserFoundation, this ); + } + protected: + //Clears the cache meaning event compression + //starts over again. + //only called when the buffer mutex is held + void clearCachedData() + { + mEventContextInformation.setToDefault(); + mLastTimestamp = 0; + } + + template + PX_FORCE_INLINE void doAddProfileEvent(uint16_t eventId, const TProfileEventType& inType) + { + TScopedLockType lock(TBaseType::mBufferMutex); + if (mEventContextInformation == inType.mContextInformation) + doAddEvent(static_cast(inType.getRelativeEventType()), eventId, inType.getRelativeEvent()); + else + { + mEventContextInformation = inType.mContextInformation; + doAddEvent( static_cast( getEventType() ), eventId, inType ); + } + } + + template + PX_FORCE_INLINE void doAddEvent(uint8_t inEventType, uint16_t eventId, const TDataType& inType) + { + EventHeader theHeader( inEventType, eventId ); + //set the header relative timestamp; + TDataType& theType( const_cast( inType ) ); + uint64_t currentTs = inType.getTimestamp(); + theType.setupHeader(theHeader, mLastTimestamp); + mLastTimestamp = currentTs; + sendEvent( theHeader, theType ); + } + + template + PX_FORCE_INLINE void sendEvent( EventHeader& inHeader, TDataType& inType ) + { + uint32_t sizeToWrite = sizeof(inHeader) + inType.getEventSize(inHeader); + PX_UNUSED(sizeToWrite); + + uint32_t writtenSize = inHeader.streamify( TBaseType::mSerializer ); + writtenSize += inType.streamify(TBaseType::mSerializer, inHeader); + + PX_ASSERT(writtenSize == sizeToWrite); + + if ( TBaseType::mDataArray.size() >= TBaseType::mBufferFullAmount ) + flushProfileEvents(); + + } + + }; +}} +#endif // PXPVDSDK_PXPROFILEEVENTBUFFER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h new file mode 100644 index 000000000..0d252f23e --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h @@ -0,0 +1,318 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEEVENTBUFFERATOMIC_H +#define PXPVDSDK_PXPROFILEEVENTBUFFERATOMIC_H + +#include "PxProfileEvents.h" +#include "PxProfileEventSerialization.h" +#include "PxProfileDataBuffer.h" + +#include "PsArray.h" +#include "PsAlloca.h" +#include "PsTime.h" +#include "PsCpu.h" +#include "PsAtomic.h" +#include "PsAllocator.h" + + +namespace physx { + namespace profile { + + static const uint32_t LOCAL_BUFFER_SIZE = 512; + + /** + * An event buffer maintains an in-memory buffer of events. When this buffer is full + * it sends to buffer to all handlers registered and resets the buffer. + * + * It is parameterized in four ways. The first is a context provider that provides + * both thread id and context id. + * + * The second is the mutex (which may be null) and a scoped locking mechanism. Thus the buffer + * may be used in a multithreaded context but clients of the buffer don't pay for this if they + * don't intend to use it this way. + * + * Finally the buffer may use an event filtering mechanism. This mechanism needs one function, + * namely isEventEnabled( uint8_t subsystem, uint8_t eventId ). + * + * All of these systems can be parameterized at compile time leading to an event buffer + * that should be as fast as possible given the constraints. + * + * Buffers may be chained together as this buffer has a handleBufferFlush method that + * will grab the mutex and add the data to this event buffer. + * + * Overall, lets look at the PhysX SDK an how all the pieces fit together. + * The SDK should have a mutex-protected event buffer where actual devs or users of PhysX + * can register handlers. This buffer has slow but correct implementations of the + * context provider interface. + * + * The SDK object should also have a concrete event filter which was used in the + * construction of the event buffer and which it exposes through opaque interfaces. + * + * The SDK should protect its event buffer and its event filter from multithreaded + * access and thus this provides the safest and slowest way to log events and to + * enable/disable events. + * + * Each scene should also have a concrete event filter. This filter is updated from + * the SDK event filter (in a mutex protected way) every frame. Thus scenes can change + * their event filtering on a frame-by-frame basis. It means that tasks running + * under the scene don't need a mutex when accessing the filter. + * + * Furthermore the scene should have an event buffer that always sets the context id + * on each event to the scene. This allows PVD and other systems to correlate events + * to scenes. Scenes should provide access only to a relative event sending system + * that looks up thread id upon each event but uses the scene id. + * + * The SDK's event buffer should be setup as an EventBufferClient for each scene's + * event buffer. Thus the SDK should expose an EventBufferClient interface that + * any client can use. + * + * For extremely *extremely* performance sensitive areas we should create a specialized + * per-scene, per-thread event buffer that is set on the task for these occasions. This buffer + * uses a trivial event context setup with the scene's context id and the thread id. It should + * share the scene's concrete event filter and it should have absolutely no locking. It should + * empty into the scene's event buffer which in some cases should empty into the SDK's event buffer + * which when full will push events all the way out of the system. The task should *always* flush + * the event buffer (if it has one) when it is finished; nothing else will work reliably. + * + * If the per-scene,per-thread event buffer is correctly parameterized and fully defined adding + * a new event should be an inline operation requiring no mutex grabs in the common case. I don't + * believe you can get faster event production than this; the events are as small as possible (all + * relative events) and they are all produced inline resulting in one 4 byte header and one + * 8 byte timestamp per event. Reducing the memory pressure in this way reduces the communication + * overhead, the mutex grabs, basically everything that makes profiling expensive at the cost + * of a per-scene,per-thread event buffer (which could easily be reduced to a per-thread event + * buffer. + */ + template + class EventBufferAtomic : public DataBuffer < TMutex, TScopedLock > + { + public: + typedef DataBuffer TBaseType; + typedef TContextProvider TContextProviderType; + typedef TEventFilter TEventFilterType; + typedef typename TBaseType::TMutexType TMutexType; + typedef typename TBaseType::TScopedLockType TScopedLockType; + typedef typename TBaseType::TU8AllocatorType TU8AllocatorType; + typedef typename TBaseType::TMemoryBufferType TMemoryBufferType; + typedef typename TBaseType::TBufferClientArray TBufferClientArray; + + private: + TContextProvider mContextProvider; + TEventFilterType mEventFilter; + volatile int32_t mReserved; + volatile int32_t mWritten; + + public: + EventBufferAtomic(PxAllocatorCallback* inFoundation + , uint32_t inBufferFullAmount + , const TContextProvider& inProvider + , TMutexType* inBufferMutex + , const TEventFilterType& inEventFilter) + : TBaseType(inFoundation, inBufferFullAmount, inBufferMutex, "struct physx::profile::ProfileEvent") + , mContextProvider(inProvider) + , mEventFilter(inEventFilter) + , mReserved(0) + , mWritten(0) + { + } + + TContextProvider& getContextProvider() { return mContextProvider; } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint32_t threadId, uint64_t contextId, uint8_t cpuId, uint8_t threadPriority, uint64_t inTimestamp) + { + if (mEventFilter.isEventEnabled(inId)) + { + StartEvent theEvent; + theEvent.init(threadId, contextId, cpuId, threadPriority, inTimestamp); + doAddProfileEvent(inId, theEvent); + } + } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint64_t contextId) + { + PxProfileEventExecutionContext ctx(mContextProvider.getExecutionContext()); + startEvent(inId, ctx.mThreadId, contextId, ctx.mCpuId, static_cast(ctx.mThreadPriority), shdfnd::Time::getCurrentCounterValue()); + } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint64_t contextId, uint32_t threadId) + { + startEvent(inId, threadId, contextId, 0, 0, shdfnd::Time::getCurrentCounterValue()); + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint32_t threadId, uint64_t contextId, uint8_t cpuId, uint8_t threadPriority, uint64_t inTimestamp) + { + if (mEventFilter.isEventEnabled(inId)) + { + StopEvent theEvent; + theEvent.init(threadId, contextId, cpuId, threadPriority, inTimestamp); + doAddProfileEvent(inId, theEvent); + } + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint64_t contextId) + { + PxProfileEventExecutionContext ctx(mContextProvider.getExecutionContext()); + stopEvent(inId, ctx.mThreadId, contextId, ctx.mCpuId, static_cast(ctx.mThreadPriority), shdfnd::Time::getCurrentCounterValue()); + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint64_t contextId, uint32_t threadId) + { + stopEvent(inId, threadId, contextId, 0, 0, shdfnd::Time::getCurrentCounterValue()); + } + + inline void eventValue(uint16_t inId, uint64_t contextId, int64_t inValue) + { + eventValue(inId, mContextProvider.getThreadId(), contextId, inValue); + } + + inline void eventValue(uint16_t inId, uint32_t threadId, uint64_t contextId, int64_t inValue) + { + EventValue theEvent; + theEvent.init(inValue, contextId, threadId); + EventHeader theHeader(static_cast(getEventType()), inId); + //set the header relative timestamp; + EventValue& theType(theEvent); + theType.setupHeader(theHeader); + + int32_t sizeToWrite = int32_t(sizeof(theHeader) + theType.getEventSize(theHeader)); + int32_t reserved = shdfnd::atomicAdd(&mReserved, sizeToWrite); + sendEvent(theHeader, theType, reserved, sizeToWrite); + } + + void flushProfileEvents(int32_t reserved = -1) + { + TScopedLockType lock(TBaseType::mBufferMutex); + + // set the buffer full to lock additional writes + int32_t reservedOld = shdfnd::atomicExchange(&mReserved, int32_t(TBaseType::mBufferFullAmount + 1)); + if (reserved == -1) + reserved = reservedOld; + + // spin till we have written all the data + while (reserved > mWritten) + { + } + + // check if we have written all data + PX_ASSERT(reserved == mWritten); + + // set the correct size of the serialization data buffer + TBaseType::mSerializer.mArray->setEnd(TBaseType::mSerializer.mArray->begin() + mWritten); + + // flush events + TBaseType::flushEvents(); + + // write master timestamp and set reserved/written to start writing to buffer again + mWritten = 0; + mReserved = 0; + } + + void release() + { + PX_PROFILE_DELETE(TBaseType::mWrapper.mUserFoundation, this); + } + protected: + //Clears the cache meaning event compression + //starts over again. + //only called when the buffer mutex is held + void clearCachedData() + { + } + + template + PX_FORCE_INLINE void doAddProfileEvent(uint16_t eventId, const TProfileEventType& inType) + { + doAddEvent(static_cast(getEventType()), eventId, inType); + } + + template + PX_FORCE_INLINE void doAddEvent(uint8_t inEventType, uint16_t eventId, const TDataType& inType) + { + EventHeader theHeader(inEventType, eventId); + TDataType& theType(const_cast(inType)); + theType.setupHeader(theHeader, 0); + + const int32_t sizeToWrite = int32_t(sizeof(theHeader) + theType.getEventSize(theHeader)); + + int32_t reserved = shdfnd::atomicAdd(&mReserved, sizeToWrite); + sendEvent(theHeader, theType, reserved, sizeToWrite); + } + + template + PX_FORCE_INLINE void sendEvent(EventHeader& inHeader, TDataType& inType, int32_t reserved, int32_t sizeToWrite) + { + // if we don't fit to the buffer, we wait till it is flushed + if (reserved - sizeToWrite >= int32_t(TBaseType::mBufferFullAmount)) + { + while (reserved - sizeToWrite >= int32_t(TBaseType::mBufferFullAmount)) + { + // I32 overflow + if (mReserved < int32_t(TBaseType::mBufferFullAmount)) + { + reserved = shdfnd::atomicAdd(&mReserved, sizeToWrite); + } + } + } + + int32_t writeIndex = reserved - sizeToWrite; + uint32_t writtenSize = 0; + + PX_ASSERT(writeIndex >= 0); + + PX_ALLOCA(tempBuffer, uint8_t, sizeToWrite); + TempMemoryBuffer memoryBuffer(tempBuffer, sizeToWrite); + EventSerializer eventSerializer(&memoryBuffer); + + writtenSize = inHeader.streamify(eventSerializer); + writtenSize += inType.streamify(eventSerializer, inHeader); + + TBaseType::mSerializer.mArray->reserve(writeIndex + writtenSize); + TBaseType::mSerializer.mArray->write(&tempBuffer[0], writtenSize, writeIndex); + + PX_ASSERT(writtenSize == uint32_t(sizeToWrite)); + shdfnd::atomicAdd(&mWritten, sizeToWrite); + + if (reserved >= int32_t(TBaseType::mBufferFullAmount)) + { + TScopedLockType lock(TBaseType::mBufferMutex); + // we flush the buffer if its full and we did not flushed him in the meantime + if(mReserved >= reserved) + flushProfileEvents(reserved); + } + } + + }; + } +} +#endif // PXPVDSDK_PXPROFILEEVENTBUFFERATOMIC_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h new file mode 100644 index 000000000..869fdbd36 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h @@ -0,0 +1,80 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTBUFFERCLIENT_H +#define PXPVDSDK_PXPROFILEEVENTBUFFERCLIENT_H + +#include "PxProfileEventNames.h" + +namespace physx { namespace profile { + + /** + \brief Client handles the data when an event buffer flushes. This data + can be parsed (PxProfileEventHandler.h) as a binary set of events. + */ + class PxProfileEventBufferClient + { + protected: + virtual ~PxProfileEventBufferClient(){} + public: + /** + \brief Callback when the event buffer is full. This data is serialized profile events + and can be read back using: PxProfileEventHandler::parseEventBuffer. + + \param inData Provided buffer data. + \param inLength Data length. + + @see PxProfileEventHandler::parseEventBuffer. + */ + virtual void handleBufferFlush( const uint8_t* inData, uint32_t inLength ) = 0; + + /** + \brief Happens if something removes all the clients from the manager. + */ + virtual void handleClientRemoved() = 0; + }; + + /** + \brief Client handles new profile event add. + */ + class PxProfileZoneClient : public PxProfileEventBufferClient + { + protected: + virtual ~PxProfileZoneClient(){} + public: + /** + \brief Callback when new profile event is added. + + \param inName Added profile event name. + */ + virtual void handleEventAdded( const PxProfileEventName& inName ) = 0; + }; + +} } + + +#endif // PXPVDSDK_PXPROFILEEVENTBUFFERCLIENT_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h new file mode 100644 index 000000000..86ed6fb24 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h @@ -0,0 +1,94 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTBUFFERCLIENTMANAGER_H +#define PXPVDSDK_PXPROFILEEVENTBUFFERCLIENTMANAGER_H + +#include "PxProfileEventBufferClient.h" + +namespace physx { namespace profile { + + /** + \brief Manager keep collections of PxProfileEventBufferClient clients. + + @see PxProfileEventBufferClient + */ + class PxProfileEventBufferClientManager + { + protected: + virtual ~PxProfileEventBufferClientManager(){} + public: + /** + \brief Adds new client. + \param inClient Client to add. + */ + virtual void addClient( PxProfileEventBufferClient& inClient ) = 0; + + /** + \brief Removes a client. + \param inClient Client to remove. + */ + virtual void removeClient( PxProfileEventBufferClient& inClient ) = 0; + + /** + \brief Check if manager has clients. + \return True if manager has added clients. + */ + virtual bool hasClients() const = 0; + }; + + /** + \brief Manager keep collections of PxProfileZoneClient clients. + + @see PxProfileZoneClient + */ + class PxProfileZoneClientManager + { + protected: + virtual ~PxProfileZoneClientManager(){} + public: + /** + \brief Adds new client. + \param inClient Client to add. + */ + virtual void addClient( PxProfileZoneClient& inClient ) = 0; + + /** + \brief Removes a client. + \param inClient Client to remove. + */ + virtual void removeClient( PxProfileZoneClient& inClient ) = 0; + + /** + \brief Check if manager has clients. + \return True if manager has added clients. + */ + virtual bool hasClients() const = 0; + }; +} } + +#endif // PXPVDSDK_PXPROFILEEVENTBUFFERCLIENTMANAGER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventId.h b/src/PhysX/physx/source/pvd/src/PxProfileEventId.h new file mode 100644 index 000000000..260abd55d --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventId.h @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTID_H +#define PXPVDSDK_PXPROFILEEVENTID_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + /** + \brief A event id structure. Optionally includes information about + if the event was enabled at compile time. + */ + struct PxProfileEventId + { + uint16_t eventId; + mutable bool compileTimeEnabled; + + /** + \brief Profile event id constructor. + \param inId Profile event id. + \param inCompileTimeEnabled Compile time enabled. + */ + PxProfileEventId( uint16_t inId = 0, bool inCompileTimeEnabled = true ) + : eventId( inId ) + , compileTimeEnabled( inCompileTimeEnabled ) + { + } + + operator uint16_t () const { return eventId; } + + bool operator==( const PxProfileEventId& inOther ) const + { + return eventId == inOther.eventId; + } + }; + +} } + +#endif // PXPVDSDK_PXPROFILEEVENTID_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp b/src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp new file mode 100644 index 000000000..8b037e8e2 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxProfileEventBuffer.h" +#include "PxProfileZoneImpl.h" +#include "PxProfileZoneManagerImpl.h" +#include "PxProfileMemoryEventBuffer.h" +#include "PsUserAllocated.h" + +namespace physx { namespace profile { + + struct PxProfileNameProviderForward + { + PxProfileNames mNames; + PxProfileNameProviderForward( PxProfileNames inNames ) + : mNames( inNames ) + { + } + PxProfileNames getProfileNames() const { return mNames; } + }; + + PxProfileZone& PxProfileZone::createProfileZone( PxAllocatorCallback* inAllocator, const char* inSDKName, PxProfileNames inNames, uint32_t inEventBufferByteSize ) + { + typedef ZoneImpl TSDKType; + return *PX_PROFILE_NEW( inAllocator, TSDKType ) ( inAllocator, inSDKName, inEventBufferByteSize, PxProfileNameProviderForward( inNames ) ); + } + + PxProfileZoneManager& PxProfileZoneManager::createProfileZoneManager(PxAllocatorCallback* inAllocator ) + { + return *PX_PROFILE_NEW( inAllocator, ZoneManagerImpl ) ( inAllocator ); + } + + PxProfileMemoryEventBuffer& PxProfileMemoryEventBuffer::createMemoryEventBuffer( PxAllocatorCallback& inAllocator, uint32_t inBufferSize ) + { + return *PX_PROFILE_NEW( &inAllocator, PxProfileMemoryEventBufferImpl )( inAllocator, inBufferSize ); + } + +} } + diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h b/src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h new file mode 100644 index 000000000..287cb57e2 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEEVENTMUTEX_H +#define PXPVDSDK_PXPROFILEEVENTMUTEX_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + + /** + * Mutex interface that hides implementation around lock and unlock. + * The event system locks the mutex for every interaction. + */ + class PxProfileEventMutex + { + protected: + virtual ~PxProfileEventMutex(){} + public: + virtual void lock() = 0; + virtual void unlock() = 0; + }; + + /** + * Take any mutex type that implements lock and unlock and make an EventMutex out of it. + */ + template + struct PxProfileEventMutexImpl : public PxProfileEventMutex + { + TMutexType* mMutex; + PxProfileEventMutexImpl( TMutexType* inMtx ) : mMutex( inMtx ) {} + virtual void lock() { mMutex->lock(); } + virtual void unlock() { mMutex->unlock(); } + }; + +} } + +#endif // PXPVDSDK_PXPROFILEEVENTMUTEX_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventNames.h b/src/PhysX/physx/source/pvd/src/PxProfileEventNames.h new file mode 100644 index 000000000..508252b9e --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventNames.h @@ -0,0 +1,89 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTNAMES_H +#define PXPVDSDK_PXPROFILEEVENTNAMES_H + +#include "PxProfileEventId.h" + +namespace physx { namespace profile { + + /** + \brief Mapping from event id to name. + */ + struct PxProfileEventName + { + const char* name; + PxProfileEventId eventId; + + /** + \brief Default constructor. + \param inName Profile event name. + \param inId Profile event id. + */ + PxProfileEventName( const char* inName, PxProfileEventId inId ) : name( inName ), eventId( inId ) {} + }; + + /** + \brief Aggregator of event id -> name mappings + */ + struct PxProfileNames + { + /** + \brief Default constructor that doesn't point to any names. + \param inEventCount Number of provided events. + \param inSubsystems Event names array. + */ + PxProfileNames( uint32_t inEventCount = 0, const PxProfileEventName* inSubsystems = NULL ) + : eventCount( inEventCount ) + , events( inSubsystems ) + { + } + + uint32_t eventCount; + const PxProfileEventName* events; + }; + + /** + \brief Provides a mapping from event ID -> name. + */ + class PxProfileNameProvider + { + public: + /** + \brief Returns profile event names. + \return Profile event names. + */ + virtual PxProfileNames getProfileNames() const = 0; + + protected: + virtual ~PxProfileNameProvider(){} + PxProfileNameProvider& operator=(const PxProfileNameProvider&) { return *this; } + }; +} } + +#endif // PXPVDSDK_PXPROFILEEVENTNAMES_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventSender.h b/src/PhysX/physx/source/pvd/src/PxProfileEventSender.h new file mode 100644 index 000000000..ee80096b6 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventSender.h @@ -0,0 +1,111 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTSENDER_H +#define PXPVDSDK_PXPROFILEEVENTSENDER_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + + /** + \brief Tagging interface to indicate an object that is capable of flushing a profile + event stream at a certain point. + */ + class PxProfileEventFlusher + { + protected: + virtual ~PxProfileEventFlusher(){} + public: + /** + \brief Flush profile events. Sends the profile event buffer to hooked clients. + */ + virtual void flushProfileEvents() = 0; + }; + + /** + \brief Sends the full events where the caller must provide the context and thread id. + */ + class PxProfileEventSender + { + protected: + virtual ~PxProfileEventSender(){} + public: + + /** + \brief Use this as a thread id for events that start on one thread and end on another + */ + static const uint32_t CrossThreadId = 99999789; + + /** + \brief Send a start profile event, optionally with a context. Events are sorted by thread + and context in the client side. + \param inId Profile event id. + \param contextId Context id. + */ + virtual void startEvent( uint16_t inId, uint64_t contextId) = 0; + /** + \brief Send a stop profile event, optionally with a context. Events are sorted by thread + and context in the client side. + \param inId Profile event id. + \param contextId Context id. + */ + virtual void stopEvent( uint16_t inId, uint64_t contextId) = 0; + + /** + \brief Send a start profile event, optionally with a context. Events are sorted by thread + and context in the client side. + \param inId Profile event id. + \param contextId Context id. + \param threadId Thread id. + */ + virtual void startEvent( uint16_t inId, uint64_t contextId, uint32_t threadId) = 0; + /** + \brief Send a stop profile event, optionally with a context. Events are sorted by thread + and context in the client side. + \param inId Profile event id. + \param contextId Context id. + \param threadId Thread id. + */ + virtual void stopEvent( uint16_t inId, uint64_t contextId, uint32_t threadId ) = 0; + + virtual void atEvent(uint16_t inId, uint64_t contextId, uint32_t threadId, uint64_t start, uint64_t stop) = 0; + + /** + \brief Set an specific events value. This is different than the profiling value + for the event; it is a value recorded and kept around without a timestamp associated + with it. This value is displayed when the event itself is processed. + \param inId Profile event id. + \param contextId Context id. + \param inValue Value to set for the event. + */ + virtual void eventValue( uint16_t inId, uint64_t contextId, int64_t inValue ) = 0; + }; + +} } + +#endif // PXPVDSDK_PXPROFILEEVENTSENDER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h b/src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h new file mode 100644 index 000000000..ca15a019e --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h @@ -0,0 +1,257 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEEVENTSERIALIZATION_H +#define PXPVDSDK_PXPROFILEEVENTSERIALIZATION_H + +#include "PxProfileDataParsing.h" +#include "PxProfileEvents.h" + +namespace physx { namespace profile { + + /** + * Array type must be a pxu8 container. Templated so that this object can write + * to different collections. + */ + + template + struct EventSerializer + { + TArrayType* mArray; + EventSerializer( TArrayType* inA ) : mArray( inA ) {} + + template + uint32_t streamify( const char*, const TDataType& inType ) + { + return mArray->write( inType ); + } + + uint32_t streamify( const char*, const char*& inType ) + { + PX_ASSERT( inType != NULL ); + uint32_t len( static_cast( strlen( inType ) ) ); + ++len; //include the null terminator + uint32_t writtenSize = 0; + writtenSize = mArray->write(len); + writtenSize += mArray->write(inType, len); + return writtenSize; + } + + uint32_t streamify( const char*, const uint8_t* inData, uint32_t len ) + { + uint32_t writtenSize = mArray->write(len); + if ( len ) + writtenSize += mArray->write(inData, len); + return writtenSize; + } + + uint32_t streamify( const char* nm, const uint64_t& inType, EventStreamCompressionFlags::Enum inFlags ) + { + uint32_t writtenSize = 0; + switch( inFlags ) + { + case EventStreamCompressionFlags::U8: + writtenSize = streamify(nm, static_cast(inType)); + break; + case EventStreamCompressionFlags::U16: + writtenSize = streamify(nm, static_cast(inType)); + break; + case EventStreamCompressionFlags::U32: + writtenSize = streamify(nm, static_cast(inType)); + break; + case EventStreamCompressionFlags::U64: + writtenSize = streamify(nm, inType); + break; + } + return writtenSize; + } + + uint32_t streamify( const char* nm, const uint32_t& inType, EventStreamCompressionFlags::Enum inFlags ) + { + uint32_t writtenSize = 0; + switch( inFlags ) + { + case EventStreamCompressionFlags::U8: + writtenSize = streamify(nm, static_cast(inType)); + break; + case EventStreamCompressionFlags::U16: + writtenSize = streamify(nm, static_cast(inType)); + break; + case EventStreamCompressionFlags::U32: + case EventStreamCompressionFlags::U64: + writtenSize = streamify(nm, inType); + break; + } + return writtenSize; + } + }; + + /** + * The event deserializes takes a buffer implements the streamify functions + * by setting the passed in data to the data in the buffer. + */ + template + struct EventDeserializer + { + const uint8_t* mData; + uint32_t mLength; + bool mFail; + + EventDeserializer( const uint8_t* inData, uint32_t inLength ) + : mData( inData ) + , mLength( inLength ) + , mFail( false ) + { + if ( mData == NULL ) + mLength = 0; + } + + bool val() { return TSwapBytes; } + + uint32_t streamify( const char* , uint8_t& inType ) + { + uint8_t* theData = reinterpret_cast( &inType ); //type punned pointer... + if ( mFail || sizeof( inType ) > mLength ) + { + PX_ASSERT( false ); + mFail = true; + } + else + { + for( uint32_t idx = 0; idx < sizeof( uint8_t ); ++idx, ++mData, --mLength ) + theData[idx] = *mData; + } + return 0; + } + + //default streamify reads things natively as bytes. + template + uint32_t streamify( const char* , TDataType& inType ) + { + uint8_t* theData = reinterpret_cast( &inType ); //type punned pointer... + if ( mFail || sizeof( inType ) > mLength ) + { + PX_ASSERT( false ); + mFail = true; + } + else + { + for( uint32_t idx = 0; idx < sizeof( TDataType ); ++idx, ++mData, --mLength ) + theData[idx] = *mData; + bool temp = val(); + if ( temp ) + BlockParseFunctions::swapBytes( theData ); + } + return 0; + } + + uint32_t streamify( const char*, const char*& inType ) + { + uint32_t theLen; + streamify( "", theLen ); + theLen = PxMin( theLen, mLength ); + inType = reinterpret_cast( mData ); + mData += theLen; + mLength -= theLen; + return 0; + } + + uint32_t streamify( const char*, const uint8_t*& inData, uint32_t& len ) + { + uint32_t theLen; + streamify( "", theLen ); + theLen = PxMin( theLen, mLength ); + len = theLen; + inData = reinterpret_cast( mData ); + mData += theLen; + mLength -= theLen; + return 0; + } + + uint32_t streamify( const char* nm, uint64_t& inType, EventStreamCompressionFlags::Enum inFlags ) + { + switch( inFlags ) + { + case EventStreamCompressionFlags::U8: + { + uint8_t val=0; + streamify( nm, val ); + inType = val; + } + break; + case EventStreamCompressionFlags::U16: + { + uint16_t val; + streamify( nm, val ); + inType = val; + } + break; + case EventStreamCompressionFlags::U32: + { + uint32_t val; + streamify( nm, val ); + inType = val; + } + break; + case EventStreamCompressionFlags::U64: + streamify( nm, inType ); + break; + } + return 0; + } + + uint32_t streamify( const char* nm, uint32_t& inType, EventStreamCompressionFlags::Enum inFlags ) + { + switch( inFlags ) + { + case EventStreamCompressionFlags::U8: + { + uint8_t val=0; + streamify( nm, val ); + inType = val; + } + break; + case EventStreamCompressionFlags::U16: + { + uint16_t val=0; + streamify( nm, val ); + inType = val; + } + break; + case EventStreamCompressionFlags::U32: + case EventStreamCompressionFlags::U64: + streamify( nm, inType ); + break; + } + return 0; + } + }; +}} +#endif // PXPVDSDK_PXPROFILEEVENTSERIALIZATION_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEvents.h b/src/PhysX/physx/source/pvd/src/PxProfileEvents.h new file mode 100644 index 000000000..9bf02ed8c --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEvents.h @@ -0,0 +1,705 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTS_H +#define PXPVDSDK_PXPROFILEEVENTS_H + +#include "foundation/PxMath.h" +#include "foundation/PxAssert.h" + +#include "PxProfileEventId.h" + + +#define PX_PROFILE_UNION_1(a) physx::profile::TUnion +#define PX_PROFILE_UNION_2(a,b) physx::profile::TUnion +#define PX_PROFILE_UNION_3(a,b,c) physx::profile::TUnion +#define PX_PROFILE_UNION_4(a,b,c,d) physx::profile::TUnion +#define PX_PROFILE_UNION_5(a,b,c,d,e) physx::profile::TUnion +#define PX_PROFILE_UNION_6(a,b,c,d,e,f) physx::profile::TUnion +#define PX_PROFILE_UNION_7(a,b,c,d,e,f,g) physx::profile::TUnion +#define PX_PROFILE_UNION_8(a,b,c,d,e,f,g,h) physx::profile::TUnion +#define PX_PROFILE_UNION_9(a,b,c,d,e,f,g,h,i) physx::profile::TUnion + +namespace physx { namespace profile { + + struct Empty {}; + + template struct Type2Type {}; + + template + union TUnion + { + typedef U Head; + typedef V Tail; + + Head head; + Tail tail; + + template + void init(const TDataType& inData) + { + toType(Type2Type()).init(inData); + } + + template + PX_FORCE_INLINE TDataType& toType(const Type2Type& outData) { return tail.toType(outData); } + + PX_FORCE_INLINE Head& toType(const Type2Type&) { return head; } + + template + PX_FORCE_INLINE const TDataType& toType(const Type2Type& outData) const { return tail.toType(outData); } + + PX_FORCE_INLINE const Head& toType(const Type2Type&) const { return head; } + }; + + struct EventTypes + { + enum Enum + { + Unknown = 0, + StartEvent, + StopEvent, + RelativeStartEvent, //reuses context,id from the earlier event. + RelativeStopEvent, //reuses context,id from the earlier event. + EventValue, + CUDAProfileBuffer //obsolete, placeholder to skip data from PhysX SDKs < 3.4 + }; + }; + + struct EventStreamCompressionFlags + { + enum Enum + { + U8 = 0, + U16 = 1, + U32 = 2, + U64 = 3, + CompressionMask = 3 + }; + }; + +#if (PX_PS4) || (PX_APPLE_FAMILY) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#endif + + //Find the smallest value that will represent the incoming value without loss. + //We can enlarge the current compression value, but we can't make is smaller. + //In this way, we can use this function to find the smallest compression setting + //that will work for a set of values. + inline EventStreamCompressionFlags::Enum findCompressionValue( uint64_t inValue, EventStreamCompressionFlags::Enum inCurrentCompressionValue = EventStreamCompressionFlags::U8 ) + { + PX_ASSERT_WITH_MESSAGE( (inCurrentCompressionValue >= EventStreamCompressionFlags::U8) && + (inCurrentCompressionValue <= EventStreamCompressionFlags::U64), + "Invalid inCurrentCompressionValue in profile::findCompressionValue"); + + //Fallthrough is intentional + switch( inCurrentCompressionValue ) + { + case EventStreamCompressionFlags::U8: + if ( inValue <= UINT8_MAX ) + return EventStreamCompressionFlags::U8; + case EventStreamCompressionFlags::U16: + if ( inValue <= UINT16_MAX ) + return EventStreamCompressionFlags::U16; + case EventStreamCompressionFlags::U32: + if ( inValue <= UINT32_MAX ) + return EventStreamCompressionFlags::U32; + case EventStreamCompressionFlags::U64: + break; + } + return EventStreamCompressionFlags::U64; + } + + //Find the smallest value that will represent the incoming value without loss. + //We can enlarge the current compression value, but we can't make is smaller. + //In this way, we can use this function to find the smallest compression setting + //that will work for a set of values. + inline EventStreamCompressionFlags::Enum findCompressionValue( uint32_t inValue, EventStreamCompressionFlags::Enum inCurrentCompressionValue = EventStreamCompressionFlags::U8 ) + { + PX_ASSERT_WITH_MESSAGE( (inCurrentCompressionValue >= EventStreamCompressionFlags::U8) && + (inCurrentCompressionValue <= EventStreamCompressionFlags::U64), + "Invalid inCurrentCompressionValue in profile::findCompressionValue"); + + //Fallthrough is intentional + switch( inCurrentCompressionValue ) + { + case EventStreamCompressionFlags::U8: + if ( inValue <= UINT8_MAX ) + return EventStreamCompressionFlags::U8; + case EventStreamCompressionFlags::U16: + if ( inValue <= UINT16_MAX ) + return EventStreamCompressionFlags::U16; + case EventStreamCompressionFlags::U32: + case EventStreamCompressionFlags::U64: + break; + } + return EventStreamCompressionFlags::U32; + } + +#if (PX_PS4) || (PX_APPLE_FAMILY) +#pragma clang diagnostic pop +#endif + + //Event header is 32 bytes and precedes all events. + struct EventHeader + { + uint8_t mEventType; //Used to parse the correct event out of the stream + uint8_t mStreamOptions; //Timestamp compression, etc. + uint16_t mEventId; //16 bit per-event-system event id + EventHeader( uint8_t type = 0, uint16_t id = 0 ) + : mEventType( type ) + , mStreamOptions( uint8_t(-1) ) + , mEventId( id ) + { + } + + EventHeader( EventTypes::Enum type, uint16_t id ) + : mEventType( static_cast( type ) ) + , mStreamOptions( uint8_t(-1) ) + , mEventId( id ) + { + } + + EventStreamCompressionFlags::Enum getTimestampCompressionFlags() const + { + return static_cast ( mStreamOptions & EventStreamCompressionFlags::CompressionMask ); + } + + uint64_t compressTimestamp( uint64_t inLastTimestamp, uint64_t inCurrentTimestamp ) + { + mStreamOptions = EventStreamCompressionFlags::U64; + uint64_t retval = inCurrentTimestamp; + if ( inLastTimestamp ) + { + retval = inCurrentTimestamp - inLastTimestamp; + EventStreamCompressionFlags::Enum compressionValue = findCompressionValue( retval ); + mStreamOptions = static_cast( compressionValue ); + if ( compressionValue == EventStreamCompressionFlags::U64 ) + retval = inCurrentTimestamp; //just send the timestamp as is. + } + return retval; + } + + uint64_t uncompressTimestamp( uint64_t inLastTimestamp, uint64_t inCurrentTimestamp ) const + { + if ( getTimestampCompressionFlags() != EventStreamCompressionFlags::U64 ) + return inLastTimestamp + inCurrentTimestamp; + return inCurrentTimestamp; + } + + void setContextIdCompressionFlags( uint64_t inContextId ) + { + uint8_t options = static_cast( findCompressionValue( inContextId ) ); + mStreamOptions = uint8_t(mStreamOptions | options << 2); + } + + EventStreamCompressionFlags::Enum getContextIdCompressionFlags() const + { + return static_cast< EventStreamCompressionFlags::Enum >( ( mStreamOptions >> 2 ) & EventStreamCompressionFlags::CompressionMask ); + } + + bool operator==( const EventHeader& inOther ) const + { + return mEventType == inOther.mEventType + && mStreamOptions == inOther.mStreamOptions + && mEventId == inOther.mEventId; + } + + template + inline uint32_t streamify( TStreamType& inStream ) + { + uint32_t writtenSize = inStream.streamify( "EventType", mEventType ); + writtenSize += inStream.streamify("StreamOptions", mStreamOptions); //Timestamp compression, etc. + writtenSize += inStream.streamify("EventId", mEventId); //16 bit per-event-system event id + return writtenSize; + } + + + }; + + //Declaration of type level getEventType function that maps enumeration event types to datatypes + template + inline EventTypes::Enum getEventType() { PX_ASSERT( false ); return EventTypes::Unknown; } + + //Relative profile event means this event is sharing the context and thread id + //with the event before it. + struct RelativeProfileEvent + { + uint64_t mTensOfNanoSeconds; //timestamp is in tensOfNanonseconds + void init( uint64_t inTs ) { mTensOfNanoSeconds = inTs; } + void init( const RelativeProfileEvent& inData ) { mTensOfNanoSeconds = inData.mTensOfNanoSeconds; } + bool operator==( const RelativeProfileEvent& other ) const + { + return mTensOfNanoSeconds == other.mTensOfNanoSeconds; + } + template + uint32_t streamify( TStreamType& inStream, const EventHeader& inHeader ) + { + return inStream.streamify( "TensOfNanoSeconds", mTensOfNanoSeconds, inHeader.getTimestampCompressionFlags() ); + } + uint64_t getTimestamp() const { return mTensOfNanoSeconds; } + void setTimestamp( uint64_t inTs ) { mTensOfNanoSeconds = inTs; } + void setupHeader( EventHeader& inHeader, uint64_t inLastTimestamp ) + { + mTensOfNanoSeconds = inHeader.compressTimestamp( inLastTimestamp, mTensOfNanoSeconds ); + } + + uint32_t getEventSize(const EventHeader& inHeader) + { + uint32_t size = 0; + switch (inHeader.getTimestampCompressionFlags()) + { + case EventStreamCompressionFlags::U8: + size = 1; + break; + case EventStreamCompressionFlags::U16: + size = 2; + break; + case EventStreamCompressionFlags::U32: + size = 4; + break; + case EventStreamCompressionFlags::U64: + size = 8; + break; + } + return size; + } + }; + + //Start version of the relative event. + struct RelativeStartEvent : public RelativeProfileEvent + { + void init( uint64_t inTs = 0 ) { RelativeProfileEvent::init( inTs ); } + void init( const RelativeStartEvent& inData ) { RelativeProfileEvent::init( inData ); } + template + void handle( THandlerType* inHdlr, uint16_t eventId, uint32_t thread, uint64_t context, uint8_t inCpuId, uint8_t threadPriority ) const + { + inHdlr->onStartEvent( PxProfileEventId( eventId ), thread, context, inCpuId, threadPriority, mTensOfNanoSeconds ); + } + }; + + template<> inline EventTypes::Enum getEventType() { return EventTypes::RelativeStartEvent; } + + //Stop version of relative event. + struct RelativeStopEvent : public RelativeProfileEvent + { + void init( uint64_t inTs = 0 ) { RelativeProfileEvent::init( inTs ); } + void init( const RelativeStopEvent& inData ) { RelativeProfileEvent::init( inData ); } + template + void handle( THandlerType* inHdlr, uint16_t eventId, uint32_t thread, uint64_t context, uint8_t inCpuId, uint8_t threadPriority ) const + { + inHdlr->onStopEvent( PxProfileEventId( eventId ), thread, context, inCpuId, threadPriority, mTensOfNanoSeconds ); + } + }; + + template<> inline EventTypes::Enum getEventType() { return EventTypes::RelativeStopEvent; } + + struct EventContextInformation + { + uint64_t mContextId; + uint32_t mThreadId; //Thread this event was taken from + uint8_t mThreadPriority; + uint8_t mCpuId; + + void init( uint32_t inThreadId = UINT32_MAX + , uint64_t inContextId = (uint64_t(-1)) + , uint8_t inPriority = UINT8_MAX + , uint8_t inCpuId = UINT8_MAX ) + { + mContextId = inContextId; + mThreadId = inThreadId; + mThreadPriority = inPriority; + mCpuId = inCpuId; + } + + void init( const EventContextInformation& inData ) + { + mContextId = inData.mContextId; + mThreadId = inData.mThreadId; + mThreadPriority = inData.mThreadPriority; + mCpuId = inData.mCpuId; + } + + template + uint32_t streamify( TStreamType& inStream, EventStreamCompressionFlags::Enum inContextIdFlags ) + { + uint32_t writtenSize = inStream.streamify( "ThreadId", mThreadId ); + writtenSize += inStream.streamify("ContextId", mContextId, inContextIdFlags); + writtenSize += inStream.streamify("ThreadPriority", mThreadPriority); + writtenSize += inStream.streamify("CpuId", mCpuId); + return writtenSize; + } + + bool operator==( const EventContextInformation& other ) const + { + return mThreadId == other.mThreadId + && mContextId == other.mContextId + && mThreadPriority == other.mThreadPriority + && mCpuId == other.mCpuId; + } + + void setToDefault() + { + *this = EventContextInformation(); + } + }; + + //Profile event contains all the data required to tell the profile what is going + //on. + struct ProfileEvent + { + EventContextInformation mContextInformation; + RelativeProfileEvent mTimeData; //timestamp in seconds. + void init( uint32_t inThreadId, uint64_t inContextId, uint8_t inCpuId, uint8_t inPriority, uint64_t inTs ) + { + mContextInformation.init( inThreadId, inContextId, inPriority, inCpuId ); + mTimeData.init( inTs ); + } + + void init( const ProfileEvent& inData ) + { + mContextInformation.init( inData.mContextInformation ); + mTimeData.init( inData.mTimeData ); + } + + bool operator==( const ProfileEvent& other ) const + { + return mContextInformation == other.mContextInformation + && mTimeData == other.mTimeData; + } + + template + uint32_t streamify( TStreamType& inStream, const EventHeader& inHeader ) + { + uint32_t writtenSize = mContextInformation.streamify(inStream, inHeader.getContextIdCompressionFlags()); + writtenSize += mTimeData.streamify(inStream, inHeader); + return writtenSize; + } + + uint32_t getEventSize(const EventHeader& inHeader) + { + uint32_t eventSize = 0; + // time is stored depending on the conpress flag mTimeData.streamify(inStream, inHeader); + switch (inHeader.getTimestampCompressionFlags()) + { + case EventStreamCompressionFlags::U8: + eventSize++; + break; + case EventStreamCompressionFlags::U16: + eventSize += 2; + break; + case EventStreamCompressionFlags::U32: + eventSize += 4; + break; + case EventStreamCompressionFlags::U64: + eventSize += 8; + break; + } + + // context information + // mContextInformation.streamify( inStream, inHeader.getContextIdCompressionFlags() ); + eventSize += 6; // uint32_t mThreadId; uint8_t mThreadPriority; uint8_t mCpuId; + switch (inHeader.getContextIdCompressionFlags()) + { + case EventStreamCompressionFlags::U8: + eventSize++; + break; + case EventStreamCompressionFlags::U16: + eventSize += 2; + break; + case EventStreamCompressionFlags::U32: + eventSize += 4; + break; + case EventStreamCompressionFlags::U64: + eventSize += 8; + break; + } + + return eventSize; + } + + uint64_t getTimestamp() const { return mTimeData.getTimestamp(); } + void setTimestamp( uint64_t inTs ) { mTimeData.setTimestamp( inTs ); } + + void setupHeader( EventHeader& inHeader, uint64_t inLastTimestamp ) + { + mTimeData.setupHeader( inHeader, inLastTimestamp ); + inHeader.setContextIdCompressionFlags( mContextInformation.mContextId ); + } + }; + + //profile start event starts the profile session. + struct StartEvent : public ProfileEvent + { + void init( uint32_t inThreadId = 0, uint64_t inContextId = 0, uint8_t inCpuId = 0, uint8_t inPriority = 0, uint64_t inTensOfNanoSeconds = 0 ) + { + ProfileEvent::init( inThreadId, inContextId, inCpuId, inPriority, inTensOfNanoSeconds ); + } + void init( const StartEvent& inData ) + { + ProfileEvent::init( inData ); + } + + RelativeStartEvent getRelativeEvent() const { RelativeStartEvent theEvent; theEvent.init( mTimeData.mTensOfNanoSeconds ); return theEvent; } + EventTypes::Enum getRelativeEventType() const { return getEventType(); } + }; + + template<> inline EventTypes::Enum getEventType() { return EventTypes::StartEvent; } + + //Profile stop event stops the profile session. + struct StopEvent : public ProfileEvent + { + void init( uint32_t inThreadId = 0, uint64_t inContextId = 0, uint8_t inCpuId = 0, uint8_t inPriority = 0, uint64_t inTensOfNanoSeconds = 0 ) + { + ProfileEvent::init( inThreadId, inContextId, inCpuId, inPriority, inTensOfNanoSeconds ); + } + void init( const StopEvent& inData ) + { + ProfileEvent::init( inData ); + } + RelativeStopEvent getRelativeEvent() const { RelativeStopEvent theEvent; theEvent.init( mTimeData.mTensOfNanoSeconds ); return theEvent; } + EventTypes::Enum getRelativeEventType() const { return getEventType(); } + }; + + template<> inline EventTypes::Enum getEventType() { return EventTypes::StopEvent; } + + struct EventValue + { + uint64_t mValue; + uint64_t mContextId; + uint32_t mThreadId; + void init( int64_t inValue = 0, uint64_t inContextId = 0, uint32_t inThreadId = 0 ) + { + mValue = static_cast( inValue ); + mContextId = inContextId; + mThreadId = inThreadId; + } + + void init( const EventValue& inData ) + { + mValue = inData.mValue; + mContextId = inData.mContextId; + mThreadId = inData.mThreadId; + } + + int64_t getValue() const { return static_cast( mValue ); } + + void setupHeader( EventHeader& inHeader ) + { + mValue = inHeader.compressTimestamp( 0, mValue ); + inHeader.setContextIdCompressionFlags( mContextId ); + } + + template + uint32_t streamify( TStreamType& inStream, const EventHeader& inHeader ) + { + uint32_t writtenSize = inStream.streamify("Value", mValue, inHeader.getTimestampCompressionFlags()); + writtenSize += inStream.streamify("ContextId", mContextId, inHeader.getContextIdCompressionFlags()); + writtenSize += inStream.streamify("ThreadId", mThreadId); + return writtenSize; + } + + uint32_t getEventSize(const EventHeader& inHeader) + { + uint32_t eventSize = 0; + // value + switch (inHeader.getTimestampCompressionFlags()) + { + case EventStreamCompressionFlags::U8: + eventSize++; + break; + case EventStreamCompressionFlags::U16: + eventSize += 2; + break; + case EventStreamCompressionFlags::U32: + eventSize += 4; + break; + case EventStreamCompressionFlags::U64: + eventSize += 8; + break; + } + + // context information + switch (inHeader.getContextIdCompressionFlags()) + { + case EventStreamCompressionFlags::U8: + eventSize++; + break; + case EventStreamCompressionFlags::U16: + eventSize += 2; + break; + case EventStreamCompressionFlags::U32: + eventSize += 4; + break; + case EventStreamCompressionFlags::U64: + eventSize += 8; + break; + } + + eventSize += 4; // uint32_t mThreadId; + + return eventSize; + } + + bool operator==( const EventValue& other ) const + { + return mValue == other.mValue + && mContextId == other.mContextId + && mThreadId == other.mThreadId; + } + + template + void handle( THandlerType* inHdlr, uint16_t eventId ) const + { + inHdlr->onEventValue( PxProfileEventId( eventId ), mThreadId, mContextId, getValue() ); + } + + }; + template<> inline EventTypes::Enum getEventType() { return EventTypes::EventValue; } + + //obsolete, placeholder to skip data from PhysX SDKs < 3.4 + struct CUDAProfileBuffer + { + uint64_t mTimestamp; + float mTimespan; + const uint8_t* mCudaData; + uint32_t mBufLen; + uint32_t mVersion; + + template + uint32_t streamify( TStreamType& inStream, const EventHeader& ) + { + uint32_t writtenSize = inStream.streamify("Timestamp", mTimestamp); + writtenSize += inStream.streamify("Timespan", mTimespan); + writtenSize += inStream.streamify("CudaData", mCudaData, mBufLen); + writtenSize += inStream.streamify("BufLen", mBufLen); + writtenSize += inStream.streamify("Version", mVersion); + return writtenSize; + } + + bool operator==( const CUDAProfileBuffer& other ) const + { + return mTimestamp == other.mTimestamp + && mTimespan == other.mTimespan + && mBufLen == other.mBufLen + && memcmp( mCudaData, other.mCudaData, mBufLen ) == 0 + && mVersion == other.mVersion; + } + }; + + template<> inline EventTypes::Enum getEventType() { return EventTypes::CUDAProfileBuffer; } + + //Provides a generic equal operation for event data objects. + template + struct EventDataEqualOperator + { + TEventData mData; + EventDataEqualOperator( const TEventData& inD ) : mData( inD ) {} + template bool operator()( const TDataType& inRhs ) const { return mData.toType( Type2Type() ) == inRhs; } + bool operator()() const { return false; } + }; + + /** + * Generic event container that combines and even header with the generic event data type. + * Provides unsafe and typesafe access to the event data. + */ + class Event + { + public: + typedef PX_PROFILE_UNION_7(StartEvent, StopEvent, RelativeStartEvent, RelativeStopEvent, EventValue, CUDAProfileBuffer, uint8_t) EventData; + + private: + EventHeader mHeader; + EventData mData; + public: + Event() {} + + template + Event( EventHeader inHeader, const TDataType& inData ) + : mHeader( inHeader ) + { + mData.init(inData); + } + + template + Event( uint16_t eventId, const TDataType& inData ) + : mHeader( getEventType(), eventId ) + { + mData.init(inData); + } + const EventHeader& getHeader() const { return mHeader; } + const EventData& getData() const { return mData; } + + template + const TDataType& getValue() const { PX_ASSERT( mHeader.mEventType == getEventType() ); return mData.toType(); } + + template + TDataType& getValue() { PX_ASSERT( mHeader.mEventType == getEventType() ); return mData.toType(); } + + template + inline TRetVal visit( TOperator inOp ) const; + + bool operator==( const Event& inOther ) const + { + if ( !(mHeader == inOther.mHeader ) ) return false; + if ( mHeader.mEventType ) + return inOther.visit( EventDataEqualOperator( mData ) ); + return true; + } + }; + + //Combining the above union type with an event type means that an object can get the exact + //data out of the union. Using this function means that all callsites will be forced to + //deal with the newer datatypes and that the switch statement only exists in once place. + //Implements conversion from enum -> datatype + template + TRetVal visit( EventTypes::Enum inEventType, const Event::EventData& inData, TOperator inOperator ) + { + switch( inEventType ) + { + case EventTypes::StartEvent: return inOperator( inData.toType( Type2Type() ) ); + case EventTypes::StopEvent: return inOperator( inData.toType( Type2Type() ) ); + case EventTypes::RelativeStartEvent: return inOperator( inData.toType( Type2Type() ) ); + case EventTypes::RelativeStopEvent: return inOperator( inData.toType( Type2Type() ) ); + case EventTypes::EventValue: return inOperator( inData.toType( Type2Type() ) ); + //obsolete, placeholder to skip data from PhysX SDKs < 3.4 + case EventTypes::CUDAProfileBuffer: return inOperator( inData.toType( Type2Type() ) ); + case EventTypes::Unknown: break; + } + uint8_t type = static_cast( inEventType ); + return inOperator( type ); + } + + template + inline TRetVal Event::visit( TOperator inOp ) const + { + return physx::profile::visit( static_cast(mHeader.mEventType), mData, inOp ); + } +} } + +#endif // PXPVDSDK_PXPROFILEEVENTS_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemory.h b/src/PhysX/physx/source/pvd/src/PxProfileMemory.h new file mode 100644 index 000000000..604d5091e --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemory.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEMEMORY_H +#define PXPVDSDK_PXPROFILEMEMORY_H + +#include "PxProfileEventBufferClientManager.h" +#include "PxProfileEventSender.h" +#include "PsBroadcast.h" + +namespace physx { namespace profile { + + /** + \brief Record events so a late-connecting client knows about + all outstanding allocations + */ + class PxProfileMemoryEventRecorder : public shdfnd::AllocationListener + { + protected: + virtual ~PxProfileMemoryEventRecorder(){} + public: + /** + \brief Set the allocation listener + \param inListener Allocation listener. + */ + virtual void setListener(AllocationListener* inListener) = 0; + /** + \brief Release the instance. + */ + virtual void release() = 0; + }; + + /** + \brief Stores memory events into the memory buffer. + */ + class PxProfileMemoryEventBuffer + : public shdfnd::AllocationListener //add a new event to the buffer + , public PxProfileEventBufferClientManager //add clients to handle the serialized memory events + , public PxProfileEventFlusher //flush the buffer + { + protected: + virtual ~PxProfileMemoryEventBuffer(){} + public: + + /** + \brief Release the instance. + */ + virtual void release() = 0; + + /** + \brief Create a non-mutex-protected event buffer. + \param inAllocator Allocation callback. + \param inBufferSize Internal buffer size. + */ + static PxProfileMemoryEventBuffer& createMemoryEventBuffer(PxAllocatorCallback& inAllocator, uint32_t inBufferSize = 0x1000); + }; + + + +} } // namespace physx + + +#endif // PXPVDSDK_PXPROFILEMEMORY_H + + diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h new file mode 100644 index 000000000..ca986aad7 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h @@ -0,0 +1,192 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEMEMORYBUFFER_H +#define PXPVDSDK_PXPROFILEMEMORYBUFFER_H + +#include "PsAllocator.h" +#include "foundation/PxMemory.h" + +namespace physx { namespace profile { + + template::Type > + class MemoryBuffer : public TAllocator + { + uint8_t* mBegin; + uint8_t* mEnd; + uint8_t* mCapacityEnd; + + public: + MemoryBuffer( const TAllocator& inAlloc = TAllocator() ) : TAllocator( inAlloc ), mBegin( 0 ), mEnd( 0 ), mCapacityEnd( 0 ) {} + ~MemoryBuffer() + { + if ( mBegin ) TAllocator::deallocate( mBegin ); + } + uint32_t size() const { return static_cast( mEnd - mBegin ); } + uint32_t capacity() const { return static_cast( mCapacityEnd - mBegin ); } + uint8_t* begin() { return mBegin; } + uint8_t* end() { return mEnd; } + void setEnd(uint8_t* nEnd) { mEnd = nEnd; } + const uint8_t* begin() const { return mBegin; } + const uint8_t* end() const { return mEnd; } + void clear() { mEnd = mBegin; } + uint32_t write( uint8_t inValue ) + { + growBuf( 1 ); + *mEnd = inValue; + ++mEnd; + return 1; + } + + template + uint32_t write( const TDataType& inValue ) + { + uint32_t writtenSize = sizeof(TDataType); + growBuf(writtenSize); + const uint8_t* __restrict readPtr = reinterpret_cast< const uint8_t* >( &inValue ); + uint8_t* __restrict writePtr = mEnd; + for ( uint32_t idx = 0; idx < sizeof(TDataType); ++idx ) writePtr[idx] = readPtr[idx]; + mEnd += writtenSize; + return writtenSize; + } + + template + uint32_t write( const TDataType* inValue, uint32_t inLength ) + { + if ( inValue && inLength ) + { + uint32_t writeSize = inLength * sizeof( TDataType ); + growBuf( writeSize ); + PxMemCopy( mBegin + size(), inValue, writeSize ); + mEnd += writeSize; + return writeSize; + } + return 0; + } + + // used by atomic write. Store the data and write the end afterwards + // we dont check the buffer size, it should not resize on the fly + template + uint32_t write(const TDataType* inValue, uint32_t inLength, int32_t index) + { + if (inValue && inLength) + { + uint32_t writeSize = inLength * sizeof(TDataType); + PX_ASSERT(mBegin + index + writeSize < mCapacityEnd); + PxMemCopy(mBegin + index, inValue, writeSize); + return writeSize; + } + return 0; + } + + void growBuf( uint32_t inAmount ) + { + uint32_t newSize = size() + inAmount; + reserve( newSize ); + } + void resize( uint32_t inAmount ) + { + reserve( inAmount ); + mEnd = mBegin + inAmount; + } + void reserve( uint32_t newSize ) + { + uint32_t currentSize = size(); + if ( newSize >= capacity() ) + { + const uint32_t allocSize = mBegin ? newSize * 2 : newSize; + + uint8_t* newData = static_cast(TAllocator::allocate(allocSize, __FILE__, __LINE__)); + memset(newData, 0xf,allocSize); + if ( mBegin ) + { + PxMemCopy( newData, mBegin, currentSize ); + TAllocator::deallocate( mBegin ); + } + mBegin = newData; + mEnd = mBegin + currentSize; + mCapacityEnd = mBegin + allocSize; + } + } + }; + + + class TempMemoryBuffer + { + uint8_t* mBegin; + uint8_t* mEnd; + uint8_t* mCapacityEnd; + + public: + TempMemoryBuffer(uint8_t* data, int32_t size) : mBegin(data), mEnd(data), mCapacityEnd(data + size) {} + ~TempMemoryBuffer() + { + } + uint32_t size() const { return static_cast(mEnd - mBegin); } + uint32_t capacity() const { return static_cast(mCapacityEnd - mBegin); } + const uint8_t* begin() { return mBegin; } + uint8_t* end() { return mEnd; } + const uint8_t* begin() const { return mBegin; } + const uint8_t* end() const { return mEnd; } + uint32_t write(uint8_t inValue) + { + *mEnd = inValue; + ++mEnd; + return 1; + } + + template + uint32_t write(const TDataType& inValue) + { + uint32_t writtenSize = sizeof(TDataType); + const uint8_t* __restrict readPtr = reinterpret_cast(&inValue); + uint8_t* __restrict writePtr = mEnd; + for (uint32_t idx = 0; idx < sizeof(TDataType); ++idx) writePtr[idx] = readPtr[idx]; + mEnd += writtenSize; + return writtenSize; + } + + template + uint32_t write(const TDataType* inValue, uint32_t inLength) + { + if (inValue && inLength) + { + uint32_t writeSize = inLength * sizeof(TDataType); + PxMemCopy(mBegin + size(), inValue, writeSize); + mEnd += writeSize; + return writeSize; + } + return 0; + } + }; + +}} + +#endif // PXPVDSDK_PXPROFILEMEMORYBUFFER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h new file mode 100644 index 000000000..e3784f5ae --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEMEMORYEVENTBUFFER_H +#define PXPVDSDK_PXPROFILEMEMORYEVENTBUFFER_H + +#include "PxProfileDataBuffer.h" +#include "PxProfileMemoryEvents.h" +#include "PxProfileMemory.h" +#include "PxProfileScopedMutexLock.h" +#include "PxProfileAllocatorWrapper.h" +#include "PxProfileEventMutex.h" + +#include "PsHash.h" +#include "PsHashMap.h" +#include "PsUserAllocated.h" + +namespace physx { namespace profile { + + template + class MemoryEventBuffer : public DataBuffer + { + public: + typedef DataBuffer TBaseType; + typedef typename TBaseType::TMutexType TMutexType; + typedef typename TBaseType::TScopedLockType TScopedLockType; + typedef typename TBaseType::TU8AllocatorType TU8AllocatorType; + typedef typename TBaseType::TMemoryBufferType TMemoryBufferType; + typedef typename TBaseType::TBufferClientArray TBufferClientArray; + typedef shdfnd::HashMap, TU8AllocatorType> TCharPtrToHandleMap; + + protected: + TCharPtrToHandleMap mStringTable; + + public: + + MemoryEventBuffer( PxAllocatorCallback& cback + , uint32_t inBufferFullAmount + , TMutexType* inBufferMutex ) + : TBaseType( &cback, inBufferFullAmount, inBufferMutex, "struct physx::profile::MemoryEvent" ) + , mStringTable( TU8AllocatorType( TBaseType::getWrapper(), "MemoryEventStringBuffer" ) ) + { + } + + uint32_t getHandle( const char* inData ) + { + if ( inData == NULL ) inData = ""; + const typename TCharPtrToHandleMap::Entry* result( mStringTable.find( inData ) ); + if ( result ) + return result->second; + uint32_t hdl = mStringTable.size() + 1; + mStringTable.insert( inData, hdl ); + StringTableEvent theEvent; + theEvent.init( inData, hdl ); + sendEvent( theEvent ); + return hdl; + } + + void onAllocation( size_t inSize, const char* inType, const char* inFile, uint32_t inLine, uint64_t addr ) + { + if ( addr == 0 ) + return; + uint32_t typeHdl( getHandle( inType ) ); + uint32_t fileHdl( getHandle( inFile ) ); + AllocationEvent theEvent; + theEvent.init( inSize, typeHdl, fileHdl, inLine, addr ); + sendEvent( theEvent ); + } + + void onDeallocation( uint64_t addr ) + { + if ( addr == 0 ) + return; + DeallocationEvent theEvent; + theEvent.init( addr ); + sendEvent( theEvent ); + } + + void flushProfileEvents() + { + TBaseType::flushEvents(); + } + + protected: + + template + void sendEvent( TDataType inType ) + { + MemoryEventHeader theHeader( getMemoryEventType() ); + inType.setup( theHeader ); + theHeader.streamify( TBaseType::mSerializer ); + inType.streamify( TBaseType::mSerializer, theHeader ); + if ( TBaseType::mDataArray.size() >= TBaseType::mBufferFullAmount ) + flushProfileEvents(); + } + }; + + class PxProfileMemoryEventBufferImpl : public shdfnd::UserAllocated + , public PxProfileMemoryEventBuffer + { + typedef MemoryEventBuffer TMemoryBufferType; + TMemoryBufferType mBuffer; + + public: + PxProfileMemoryEventBufferImpl( PxAllocatorCallback& alloc, uint32_t inBufferFullAmount ) + : mBuffer( alloc, inBufferFullAmount, NULL ) + { + } + + virtual void onAllocation( size_t size, const char* typeName, const char* filename, int line, void* allocatedMemory ) + { + mBuffer.onAllocation( size, typeName, filename, uint32_t(line), static_cast(reinterpret_cast(allocatedMemory)) ); + } + virtual void onDeallocation( void* allocatedMemory ) + { + mBuffer.onDeallocation(static_cast(reinterpret_cast(allocatedMemory)) ); + } + + virtual void addClient( PxProfileEventBufferClient& inClient ) { mBuffer.addClient( inClient ); } + virtual void removeClient( PxProfileEventBufferClient& inClient ) { mBuffer.removeClient( inClient ); } + virtual bool hasClients() const { return mBuffer.hasClients(); } + + virtual void flushProfileEvents() { mBuffer.flushProfileEvents(); } + + virtual void release(){ PX_PROFILE_DELETE( mBuffer.getWrapper().getAllocator(), this ); } + }; +}} + +#endif // PXPVDSDK_PXPROFILEMEMORYEVENTBUFFER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h new file mode 100644 index 000000000..4a206c70d --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h @@ -0,0 +1,411 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEMEMORYEVENTS_H +#define PXPVDSDK_PXPROFILEMEMORYEVENTS_H + +#include "PxProfileEvents.h" + +//Memory events define their own event stream + +namespace physx { namespace profile { + struct MemoryEventTypes + { + enum Enum + { + Unknown = 0, + StringTableEvent, //introduce a new mapping of const char* -> integer + AllocationEvent, + DeallocationEvent, + FullAllocationEvent + }; + }; + + template + inline unsigned char convertToNBits( TDataType inType ) + { + uint8_t conversion = static_cast( inType ); + PX_ASSERT( conversion < (1 << numBits) ); + return conversion; + } + + template + inline unsigned char convertToTwoBits( TDataType inType ) + { + return convertToNBits<2>( inType ); + } + + template + inline unsigned char convertToFourBits( TDataType inType ) + { + return convertToNBits<4>( inType ); + } + + inline EventStreamCompressionFlags::Enum fromNumber( uint8_t inNum ) { return static_cast( inNum ); } + + template + inline void compileCheckSize() + { + PX_COMPILE_TIME_ASSERT( lhs <= rhs ); + } + + //Used for predictable bit fields. + template + struct BitMaskSetter + { + //Create a mask that masks out the orginal value shift into place + static TDataType createOffsetMask() { return TDataType(createMask() << TOffset); } + //Create a mask of TNumBits number of tis + static TDataType createMask() { return static_cast((1 << TNumBits) - 1); } + void setValue( TDataType& inCurrent, TInputType inData ) + { + PX_ASSERT( inData < ( 1 << TNumBits ) ); + + //Create a mask to remove the current value. + TDataType theMask = TDataType(~(createOffsetMask())); + //Clear out current value. + inCurrent = TDataType(inCurrent & theMask); + //Create the new value. + TDataType theAddition = static_cast( inData << TOffset ); + //or it into the existing value. + inCurrent = TDataType(inCurrent | theAddition); + } + + TInputType getValue( TDataType inCurrent ) + { + return static_cast( ( inCurrent >> TOffset ) & createMask() ); + } + }; + + + struct MemoryEventHeader + { + uint16_t mValue; + + typedef BitMaskSetter TTypeBitmask; + typedef BitMaskSetter TAddrCompressBitmask; + typedef BitMaskSetter TTypeCompressBitmask; + typedef BitMaskSetter TFnameCompressBitmask; + typedef BitMaskSetter TSizeCompressBitmask; + typedef BitMaskSetter TLineCompressBitmask; + + //That leaves size as the only thing not compressed usually. + + MemoryEventHeader( MemoryEventTypes::Enum inType = MemoryEventTypes::Unknown ) + : mValue( 0 ) + { + uint8_t defaultCompression( convertToTwoBits( EventStreamCompressionFlags::U64 ) ); + TTypeBitmask().setValue( mValue, convertToFourBits( inType ) ); + TAddrCompressBitmask().setValue( mValue, defaultCompression ); + TTypeCompressBitmask().setValue( mValue, defaultCompression ); + TFnameCompressBitmask().setValue( mValue, defaultCompression ); + TSizeCompressBitmask().setValue( mValue, defaultCompression ); + TLineCompressBitmask().setValue( mValue, defaultCompression ); + } + + MemoryEventTypes::Enum getType() const { return static_cast( TTypeBitmask().getValue( mValue ) ); } + +#define DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( name ) \ + void set##name( EventStreamCompressionFlags::Enum inEnum ) { T##name##Bitmask().setValue( mValue, convertToTwoBits( inEnum ) ); } \ + EventStreamCompressionFlags::Enum get##name() const { return fromNumber( T##name##Bitmask().getValue( mValue ) ); } + + DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( AddrCompress ) + DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( TypeCompress ) + DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( FnameCompress ) + DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( SizeCompress ) + DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( LineCompress ) + +#undef DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR + + bool operator==( const MemoryEventHeader& inOther ) const + { + return mValue == inOther.mValue; + } + template + void streamify( TStreamType& inStream ) + { + inStream.streamify( "Header", mValue ); + } + }; + + //Declaration of type level getMemoryEventType function that maps enumeration event types to datatypes + template + inline MemoryEventTypes::Enum getMemoryEventType() { PX_ASSERT( false ); return MemoryEventTypes::Unknown; } + + inline bool safeStrEq( const char* lhs, const char* rhs ) + { + if ( lhs == rhs ) + return true; + //If they aren't equal, and one of them is null, + //then they can't be equal. + //This is assuming that the null char* is not equal to + //the empty "" char*. + if ( !lhs || !rhs ) + return false; + + return ::strcmp( lhs, rhs ) == 0; + } + + struct StringTableEvent + { + const char* mString; + uint32_t mHandle; + + void init( const char* inStr = "", uint32_t inHdl = 0 ) + { + mString = inStr; + mHandle = inHdl; + } + + void init( const StringTableEvent& inData ) + { + mString = inData.mString; + mHandle = inData.mHandle; + } + + bool operator==( const StringTableEvent& inOther ) const + { + return mHandle == inOther.mHandle + && safeStrEq( mString, inOther.mString ); + } + + void setup( MemoryEventHeader& ) const {} + + template + void streamify( TStreamType& inStream, const MemoryEventHeader& ) + { + inStream.streamify( "String", mString ); + inStream.streamify( "Handle", mHandle ); + } + }; + template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::StringTableEvent; } + + struct MemoryEventData + { + uint64_t mAddress; + void init( uint64_t addr ) + { + mAddress = addr; + } + + void init( const MemoryEventData& inData) + { + mAddress = inData.mAddress; + } + + bool operator==( const MemoryEventData& inOther ) const + { + return mAddress == inOther.mAddress; + } + + void setup( MemoryEventHeader& inHeader ) const + { + inHeader.setAddrCompress( findCompressionValue( mAddress ) ); + } + + template + void streamify( TStreamType& inStream, const MemoryEventHeader& inHeader ) + { + inStream.streamify( "Address", mAddress, inHeader.getAddrCompress() ); + } + }; + + struct AllocationEvent : public MemoryEventData + { + uint32_t mSize; + uint32_t mType; + uint32_t mFile; + uint32_t mLine; + void init( size_t size = 0, uint32_t type = 0, uint32_t file = 0, uint32_t line = 0, uint64_t addr = 0 ) + { + MemoryEventData::init( addr ); + mSize = static_cast( size ); + mType = type; + mFile = file; + mLine = line; + } + + void init( const AllocationEvent& inData ) + { + MemoryEventData::init( inData ); + mSize = inData.mSize; + mType = inData.mType; + mFile = inData.mFile; + mLine = inData.mLine; + } + + bool operator==( const AllocationEvent& inOther ) const + { + return MemoryEventData::operator==( inOther ) + && mSize == inOther.mSize + && mType == inOther.mType + && mFile == inOther.mFile + && mLine == inOther.mLine; + } + + void setup( MemoryEventHeader& inHeader ) const + { + inHeader.setTypeCompress( findCompressionValue( mType ) ); + inHeader.setFnameCompress( findCompressionValue( mFile ) ); + inHeader.setSizeCompress( findCompressionValue( mSize ) ); + inHeader.setLineCompress( findCompressionValue( mLine ) ); + MemoryEventData::setup( inHeader ); + } + + template + void streamify( TStreamType& inStream, const MemoryEventHeader& inHeader ) + { + inStream.streamify( "Size", mSize, inHeader.getSizeCompress() ); + inStream.streamify( "Type", mType, inHeader.getTypeCompress() ); + inStream.streamify( "File", mFile, inHeader.getFnameCompress() ); + inStream.streamify( "Line", mLine, inHeader.getLineCompress() ); + MemoryEventData::streamify( inStream, inHeader ); + } + }; + template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::AllocationEvent; } + + + struct FullAllocationEvent : public MemoryEventData + { + size_t mSize; + const char* mType; + const char* mFile; + uint32_t mLine; + void init( size_t size, const char* type, const char* file, uint32_t line, uint64_t addr ) + { + MemoryEventData::init( addr ); + mSize = size; + mType = type; + mFile = file; + mLine = line; + } + + void init( const FullAllocationEvent& inData ) + { + MemoryEventData::init( inData ); + mSize = inData.mSize; + mType = inData.mType; + mFile = inData.mFile; + mLine = inData.mLine; + } + + bool operator==( const FullAllocationEvent& inOther ) const + { + return MemoryEventData::operator==( inOther ) + && mSize == inOther.mSize + && safeStrEq( mType, inOther.mType ) + && safeStrEq( mFile, inOther.mFile ) + && mLine == inOther.mLine; + } + + void setup( MemoryEventHeader& ) const {} + }; + + template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::FullAllocationEvent; } + + struct DeallocationEvent : public MemoryEventData + { + void init( uint64_t addr = 0 ) { MemoryEventData::init( addr ); } + void init( const DeallocationEvent& inData ) { MemoryEventData::init( inData ); } + }; + + template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::DeallocationEvent; } + + class MemoryEvent + { + public: + typedef PX_PROFILE_UNION_5(StringTableEvent, AllocationEvent, DeallocationEvent, FullAllocationEvent, uint8_t) EventData; + + private: + MemoryEventHeader mHeader; + EventData mData; + public: + + MemoryEvent() {} + MemoryEvent( MemoryEventHeader inHeader, const EventData& inData = EventData() ) + : mHeader( inHeader ) + , mData( inData ) + { + } + + template + MemoryEvent( const TDataType& inType ) + : mHeader( getMemoryEventType() ) + , mData( inType ) + { + //set the appropriate compression bits. + inType.setup( mHeader ); + } + const MemoryEventHeader& getHeader() const { return mHeader; } + const EventData& getData() const { return mData; } + + template + const TDataType& getValue() const { PX_ASSERT( mHeader.getType() == getMemoryEventType() ); return mData.toType(); } + + template + TDataType& getValue() { PX_ASSERT( mHeader.getType() == getMemoryEventType() ); return mData.toType(); } + + template + inline TRetVal visit( TOperator inOp ) const; + + bool operator==( const MemoryEvent& inOther ) const + { + if ( !(mHeader == inOther.mHeader ) ) return false; + if ( mHeader.getType() ) + return inOther.visit( EventDataEqualOperator( mData ) ); + return true; + } + }; + + template + inline TRetVal visit( MemoryEventTypes::Enum inEventType, const MemoryEvent::EventData& inData, TOperator inOperator ) + { + switch( inEventType ) + { + case MemoryEventTypes::StringTableEvent: return inOperator( inData.toType( Type2Type() ) ); + case MemoryEventTypes::AllocationEvent: return inOperator( inData.toType( Type2Type() ) ); + case MemoryEventTypes::DeallocationEvent: return inOperator( inData.toType( Type2Type() ) ); + case MemoryEventTypes::FullAllocationEvent: return inOperator( inData.toType( Type2Type() ) ); + case MemoryEventTypes::Unknown: return inOperator( static_cast( inEventType ) ); + } + return TRetVal(); + } + + template + inline TRetVal MemoryEvent::visit( TOperator inOp ) const + { + return physx::profile::visit( mHeader.getType(), mData, inOp ); + } +}} + +#endif // PXPVDSDK_PXPROFILEMEMORYEVENTS_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h b/src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h new file mode 100644 index 000000000..fe53314dd --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h @@ -0,0 +1,107 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILESCOPEDEVENT_H +#define PXPVDSDK_PXPROFILESCOPEDEVENT_H + +#include "PxProfileEventId.h" +#include "PxProfileCompileTimeEventFilter.h" + +namespace physx { namespace profile { + + /** + \brief Template version of startEvent, called directly on provided profile buffer. + + \param inBuffer Profile event buffer. + \param inId Profile event id. + \param inContext Profile event context. + */ + template + inline void startEvent( TBufferType* inBuffer, const PxProfileEventId& inId, uint64_t inContext ) + { + if ( TEnabled && inBuffer ) inBuffer->startEvent( inId, inContext ); + } + + /** + \brief Template version of stopEvent, called directly on provided profile buffer. + + \param inBuffer Profile event buffer. + \param inId Profile event id. + \param inContext Profile event context. + */ + template + inline void stopEvent( TBufferType* inBuffer, const PxProfileEventId& inId, uint64_t inContext ) + { + if ( TEnabled && inBuffer ) inBuffer->stopEvent( inId, inContext ); + } + + /** + \brief Template version of startEvent, called directly on provided profile buffer. + + \param inEnabled If profile event is enabled. + \param inBuffer Profile event buffer. + \param inId Profile event id. + \param inContext Profile event context. + */ + template + inline void startEvent( bool inEnabled, TBufferType* inBuffer, const PxProfileEventId& inId, uint64_t inContext ) + { + if ( inEnabled && inBuffer ) inBuffer->startEvent( inId, inContext ); + } + + /** + \brief Template version of stopEvent, called directly on provided profile buffer. + + \param inEnabled If profile event is enabled. + \param inBuffer Profile event buffer. + \param inId Profile event id. + \param inContext Profile event context. + */ + template + inline void stopEvent( bool inEnabled, TBufferType* inBuffer, const PxProfileEventId& inId, uint64_t inContext ) + { + if ( inEnabled && inBuffer ) inBuffer->stopEvent( inId, inContext ); + } + + /** + \brief Template version of eventValue, called directly on provided profile buffer. + + \param inEnabled If profile event is enabled. + \param inBuffer Profile event buffer. + \param inId Profile event id. + \param inContext Profile event context. + \param inValue Event value. + */ + template + inline void eventValue( bool inEnabled, TBufferType* inBuffer, const PxProfileEventId& inId, uint64_t inContext, int64_t inValue ) + { + if ( inEnabled && inBuffer ) inBuffer->eventValue( inId, inContext, inValue ); + } + +}} + +#endif // PXPVDSDK_PXPROFILESCOPEDEVENT_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h b/src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h new file mode 100644 index 000000000..dac1e4cb0 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILESCOPEDMUTEXLOCK_H +#define PXPVDSDK_PXPROFILESCOPEDMUTEXLOCK_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + + /** + * Generic class to wrap any mutex type that has lock and unlock methods + */ + template + struct ScopedLockImpl + { + TMutexType* mMutex; + ScopedLockImpl( TMutexType* inM ) : mMutex( inM ) + { + if ( mMutex ) mMutex->lock(); + } + ~ScopedLockImpl() + { + if ( mMutex ) mMutex->unlock(); + } + }; + + /** + * Null locking system that does nothing. + */ + struct NullLock + { + template NullLock( TDataType*) {} + }; +}} + +#endif // PXPVDSDK_PXPROFILESCOPEDMUTEXLOCK_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h b/src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h new file mode 100644 index 000000000..cb768367b --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h @@ -0,0 +1,315 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEZONEIMPL_H +#define PXPVDSDK_PXPROFILEZONEIMPL_H + +#include "PxPvdProfileZone.h" +#include "PxProfileZoneManager.h" +#include "PxProfileContextProviderImpl.h" +#include "PxProfileScopedMutexLock.h" +#include "PxProfileEventBufferAtomic.h" +#include "PsMutex.h" + +namespace physx { namespace profile { + + /** + \brief Simple event filter that enables all events. + */ + struct PxProfileNullEventFilter + { + void setEventEnabled( const PxProfileEventId&, bool) { PX_ASSERT(false); } + bool isEventEnabled( const PxProfileEventId&) const { return true; } + }; + + typedef shdfnd::MutexT > TZoneMutexType; + typedef ScopedLockImpl TZoneLockType; + typedef EventBuffer< PxDefaultContextProvider, TZoneMutexType, TZoneLockType, PxProfileNullEventFilter > TZoneEventBufferType; + //typedef EventBufferAtomic< PxDefaultContextProvider, TZoneMutexType, TZoneLockType, PxProfileNullEventFilter > TZoneEventBufferType; + + template + class ZoneImpl : TZoneEventBufferType //private inheritance intended + , public PxProfileZone + , public PxProfileEventBufferClient + { + typedef shdfnd::MutexT > TMutexType; + typedef PxProfileHashMap TNameToEvtIndexMap; + //ensure we don't reuse event ids. + typedef PxProfileHashMap TEvtIdToNameMap; + typedef TMutexType::ScopedLock TLockType; + + + const char* mName; + mutable TMutexType mMutex; + PxProfileArray mEventNames; + // to avoid locking, read-only and read-write map exist + TNameToEvtIndexMap mNameToEvtIndexMapR; + TNameToEvtIndexMap mNameToEvtIndexMapRW; + //ensure we don't reuse event ids. + TEvtIdToNameMap mEvtIdToNameMap; + + PxProfileZoneManager* mProfileZoneManager; + + PxProfileArray mZoneClients; + volatile bool mEventsActive; + + PX_NOCOPY(ZoneImpl) + public: + ZoneImpl( PxAllocatorCallback* inAllocator, const char* inName, uint32_t bufferSize = 0x10000 /*64k*/, const TNameProvider& inProvider = TNameProvider() ) + : TZoneEventBufferType( inAllocator, bufferSize, PxDefaultContextProvider(), NULL, PxProfileNullEventFilter() ) + , mName( inName ) + , mMutex( PxProfileWrapperReflectionAllocator( mWrapper ) ) + , mEventNames( mWrapper ) + , mNameToEvtIndexMapR( mWrapper ) + , mNameToEvtIndexMapRW( mWrapper ) + , mEvtIdToNameMap( mWrapper ) + , mProfileZoneManager( NULL ) + , mZoneClients( mWrapper ) + , mEventsActive( false ) + { + TZoneEventBufferType::setBufferMutex( &mMutex ); + //Initialize the event name structure with existing names from the name provider. + PxProfileNames theNames( inProvider.getProfileNames() ); + for ( uint32_t idx = 0; idx < theNames.eventCount; ++idx ) + { + const PxProfileEventName& theName (theNames.events[idx]); + doAddName( theName.name, theName.eventId.eventId, theName.eventId.compileTimeEnabled ); + } + TZoneEventBufferType::addClient( *this ); + } + + virtual ~ZoneImpl() { + if ( mProfileZoneManager != NULL ) + mProfileZoneManager->removeProfileZone( *this ); + mProfileZoneManager = NULL; + TZoneEventBufferType::removeClient( *this ); + } + + void doAddName( const char* inName, uint16_t inEventId, bool inCompileTimeEnabled ) + { + TLockType theLocker( mMutex ); + mEvtIdToNameMap.insert( inEventId, inName ); + uint32_t idx = static_cast( mEventNames.size() ); + mNameToEvtIndexMapRW.insert( inName, idx ); + mEventNames.pushBack( PxProfileEventName( inName, PxProfileEventId( inEventId, inCompileTimeEnabled ) ) ); + } + + virtual void flushEventIdNameMap() + { + // copy the RW map into R map + if (mNameToEvtIndexMapRW.size()) + { + for (TNameToEvtIndexMap::Iterator iter = mNameToEvtIndexMapRW.getIterator(); !iter.done(); ++iter) + { + mNameToEvtIndexMapR.insert(iter->first, iter->second); + } + mNameToEvtIndexMapRW.clear(); + } + } + + virtual uint16_t getEventIdForName( const char* inName ) + { + return getEventIdsForNames( &inName, 1 ); + } + + virtual uint16_t getEventIdsForNames( const char** inNames, uint32_t inLen ) + { + if ( inLen == 0 ) + return 0; + + // search the read-only map first + const TNameToEvtIndexMap::Entry* theEntry( mNameToEvtIndexMapR.find( inNames[0] ) ); + if ( theEntry ) + return mEventNames[theEntry->second].eventId; + + TLockType theLocker(mMutex); + + const TNameToEvtIndexMap::Entry* theReEntry(mNameToEvtIndexMapRW.find(inNames[0])); + if (theReEntry) + return mEventNames[theReEntry->second].eventId; + + //Else git R dun. + uint16_t nameSize = static_cast( mEventNames.size() ); + //We don't allow 0 as an event id. + uint16_t eventId = nameSize; + //Find a contiguous set of unique event ids + bool foundAnEventId = false; + do + { + foundAnEventId = false; + ++eventId; + for ( uint16_t idx = 0; idx < inLen && foundAnEventId == false; ++idx ) + foundAnEventId = mEvtIdToNameMap.find( uint16_t(eventId + idx) ) != NULL; + } + while( foundAnEventId ); + + uint32_t clientCount = mZoneClients.size(); + for ( uint16_t nameIdx = 0; nameIdx < inLen; ++nameIdx ) + { + uint16_t newId = uint16_t(eventId + nameIdx); + doAddName( inNames[nameIdx], newId, true ); + for( uint32_t clientIdx =0; clientIdx < clientCount; ++clientIdx ) + mZoneClients[clientIdx]->handleEventAdded( PxProfileEventName( inNames[nameIdx], PxProfileEventId( newId ) ) ); + } + + return eventId; + } + + virtual void setProfileZoneManager(PxProfileZoneManager* inMgr) + { + mProfileZoneManager = inMgr; + } + + virtual PxProfileZoneManager* getProfileZoneManager() + { + return mProfileZoneManager; + } + + + + const char* getName() { return mName; } + + PxProfileEventBufferClient* getEventBufferClient() { return this; } + + //SDK implementation + + void addClient( PxProfileZoneClient& inClient ) + { + TLockType lock( mMutex ); + mZoneClients.pushBack( &inClient ); + mEventsActive = true; + } + + void removeClient( PxProfileZoneClient& inClient ) + { + TLockType lock( mMutex ); + for ( uint32_t idx =0; idx < mZoneClients.size(); ++idx ) + { + if (mZoneClients[idx] == &inClient ) + { + inClient.handleClientRemoved(); + mZoneClients.replaceWithLast( idx ); + break; + } + } + mEventsActive = mZoneClients.size() != 0; + } + + virtual bool hasClients() const + { + return mEventsActive; + } + + virtual PxProfileNames getProfileNames() const + { + TLockType theLocker( mMutex ); + const PxProfileEventName* theNames = mEventNames.begin(); + uint32_t theEventCount = uint32_t(mEventNames.size()); + return PxProfileNames( theEventCount, theNames ); + } + + virtual void release() + { + PX_PROFILE_DELETE( mWrapper.getAllocator(), this ); + } + + //Implementation chaining the buffer flush to our clients + virtual void handleBufferFlush( const uint8_t* inData, uint32_t inLength ) + { + TLockType theLocker( mMutex ); + + uint32_t clientCount = mZoneClients.size(); + for( uint32_t idx =0; idx < clientCount; ++idx ) + mZoneClients[idx]->handleBufferFlush( inData, inLength ); + } + //Happens if something removes all the clients from the manager. + virtual void handleClientRemoved() {} + + //Send a profile event, optionally with a context. Events are sorted by thread + //and context in the client side. + virtual void startEvent( uint16_t inId, uint64_t contextId) + { + if( mEventsActive ) + { + TZoneEventBufferType::startEvent( inId, contextId ); + } + } + virtual void stopEvent( uint16_t inId, uint64_t contextId) + { + if( mEventsActive ) + { + TZoneEventBufferType::stopEvent( inId, contextId ); + } + } + + virtual void startEvent( uint16_t inId, uint64_t contextId, uint32_t threadId) + { + if( mEventsActive ) + { + TZoneEventBufferType::startEvent( inId, contextId, threadId ); + } + } + virtual void stopEvent( uint16_t inId, uint64_t contextId, uint32_t threadId ) + { + if( mEventsActive ) + { + TZoneEventBufferType::stopEvent( inId, contextId, threadId ); + } + } + + virtual void atEvent(uint16_t inId, uint64_t contextId, uint32_t threadId, uint64_t start, uint64_t stop) + { + if (mEventsActive) + { + TZoneEventBufferType::startEvent(inId, threadId, contextId, 0, 0, start); + TZoneEventBufferType::stopEvent(inId, threadId, contextId, 0, 0, stop); + } + } + + /** + * Set an specific events value. This is different than the profiling value + * for the event; it is a value recorded and kept around without a timestamp associated + * with it. This value is displayed when the event itself is processed. + */ + virtual void eventValue( uint16_t inId, uint64_t contextId, int64_t inValue ) + { + if( mEventsActive ) + { + TZoneEventBufferType::eventValue( inId, contextId, inValue ); + } + } + virtual void flushProfileEvents() + { + TZoneEventBufferType::flushProfileEvents(); + } + }; + +}} +#endif // PXPVDSDK_PXPROFILEZONEIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h b/src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h new file mode 100644 index 000000000..5a1f37076 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h @@ -0,0 +1,155 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEZONEMANAGER_H +#define PXPVDSDK_PXPROFILEZONEMANAGER_H + +#include "PxProfileEventSender.h" +#include "PxProfileEventNames.h" + +namespace physx { + + class PxAllocatorCallback; + + namespace profile { + + class PxProfileZone; + class PxProfileNameProvider; + + /** + \brief Profile zone handler for zone add/remove notification. + */ + class PxProfileZoneHandler + { + protected: + virtual ~PxProfileZoneHandler(){} + public: + /** + \brief On zone added notification + + \note Not a threadsafe call; handlers are expected to be able to handle + this from any thread. + + \param inSDK Added zone. + */ + virtual void onZoneAdded( PxProfileZone& inSDK ) = 0; + /** + \brief On zone removed notification + + \note Not a threadsafe call; handlers are expected to be able to handle + this from any thread. + + \param inSDK removed zone. + */ + virtual void onZoneRemoved( PxProfileZone& inSDK ) = 0; + }; + + /** + \brief The profiling system was setup in the expectation that there would be several + systems that each had its own island of profile information. PhysX, client code, + and APEX would be the first examples of these. Each one of these islands is represented + by a profile zone. + + The Manager is a singleton-like object where all these different systems can be registered + so that clients of the profiling system can have one point to capture *all* profiling events. + + Flushing the manager implies that you want to loop through all the profile zones and flush + each one. + + @see PxProfileEventFlusher + */ + class PxProfileZoneManager + : public PxProfileEventFlusher //Tell all SDK's to flush their queue of profile events. + { + protected: + virtual ~PxProfileZoneManager(){} + public: + /** + \brief Add new profile zone for the manager. + \note Threadsafe call, can be done from any thread. Handlers that are already connected + will get a new callback on the current thread. + + \param inSDK Profile zone to add. + */ + virtual void addProfileZone( PxProfileZone& inSDK ) = 0; + /** + \brief Removes profile zone from the manager. + \note Threadsafe call, can be done from any thread. Handlers that are already connected + will get a new callback on the current thread. + + \param inSDK Profile zone to remove. + */ + virtual void removeProfileZone( PxProfileZone& inSDK ) = 0; + + /** + \brief Add profile zone handler callback for the profile zone notifications. + + \note Threadsafe call. The new handler will immediately be notified about all + known SDKs. + + \param inHandler Profile zone handler to add. + */ + virtual void addProfileZoneHandler( PxProfileZoneHandler& inHandler ) = 0; + /** + \brief Removes profile zone handler callback for the profile zone notifications. + + \note Threadsafe call. The new handler will immediately be notified about all + known SDKs. + + \param inHandler Profile zone handler to remove. + */ + virtual void removeProfileZoneHandler( PxProfileZoneHandler& inHandler ) = 0; + + + /** + \brief Create a new profile zone. This means you don't need access to a PxFoundation to + create your profile zone object, and your object is automatically registered with + the profile zone manager. + + You still need to release your object when you are finished with it. + \param inSDKName Name of the SDK object. + \param inNames Option set of event id to name mappings. + \param inEventBufferByteSize rough maximum size of the event buffer. May exceed this size + by sizeof one event. When full an immediate call to all listeners is made. + */ + virtual PxProfileZone& createProfileZone( const char* inSDKName, PxProfileNames inNames = PxProfileNames(), uint32_t inEventBufferByteSize = 0x4000 /*16k*/ ) = 0; + + /** + \brief Releases the profile manager instance. + */ + virtual void release() = 0; + + /** + \brief Create the profile zone manager. + \param inAllocatorCallback Allocator callback. + */ + static PxProfileZoneManager& createProfileZoneManager(PxAllocatorCallback* inAllocatorCallback ); + }; + +} } + +#endif // PXPVDSDK_PXPROFILEZONEMANAGER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h b/src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h new file mode 100644 index 000000000..a336a763c --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h @@ -0,0 +1,173 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEZONEMANAGERIMPL_H +#define PXPVDSDK_PXPROFILEZONEMANAGERIMPL_H + +#include "PxProfileZoneManager.h" +#include "PxProfileScopedMutexLock.h" +#include "PxPvdProfileZone.h" +#include "PxProfileAllocatorWrapper.h" + +#include "PsArray.h" +#include "PsMutex.h" + +namespace physx { namespace profile { + + struct NullEventNameProvider : public PxProfileNameProvider + { + virtual PxProfileNames getProfileNames() const { return PxProfileNames( 0, 0 ); } + }; + + class ZoneManagerImpl : public PxProfileZoneManager + { + typedef ScopedLockImpl TScopedLockType; + PxProfileAllocatorWrapper mWrapper; + PxProfileArray mZones; + PxProfileArray mHandlers; + shdfnd::Mutex mMutex; + + ZoneManagerImpl( const ZoneManagerImpl& inOther ); + ZoneManagerImpl& operator=( const ZoneManagerImpl& inOther ); + + public: + + ZoneManagerImpl(PxAllocatorCallback* inFoundation) + : mWrapper( inFoundation ) + , mZones( mWrapper ) + , mHandlers( mWrapper ) + {} + + virtual ~ZoneManagerImpl() + { + //This assert would mean that a profile zone is outliving us. + //This will cause a crash when the profile zone is released. + PX_ASSERT( mZones.size() == 0 ); + while( mZones.size() ) + removeProfileZone( *mZones.back() ); + } + + virtual void addProfileZone( PxProfileZone& inSDK ) + { + TScopedLockType lock( &mMutex ); + + if ( inSDK.getProfileZoneManager() != NULL ) + { + if ( inSDK.getProfileZoneManager() == this ) + return; + else //there must be two managers in the system somehow. + { + PX_ASSERT( false ); + inSDK.getProfileZoneManager()->removeProfileZone( inSDK ); + } + } + mZones.pushBack( &inSDK ); + inSDK.setProfileZoneManager( this ); + for ( uint32_t idx =0; idx < mHandlers.size(); ++idx ) + mHandlers[idx]->onZoneAdded( inSDK ); + } + + virtual void removeProfileZone( PxProfileZone& inSDK ) + { + TScopedLockType lock( &mMutex ); + if ( inSDK.getProfileZoneManager() == NULL ) + return; + + else if ( inSDK.getProfileZoneManager() != this ) + { + PX_ASSERT( false ); + inSDK.getProfileZoneManager()->removeProfileZone( inSDK ); + return; + } + + inSDK.setProfileZoneManager( NULL ); + for ( uint32_t idx = 0; idx < mZones.size(); ++idx ) + { + if ( mZones[idx] == &inSDK ) + { + for ( uint32_t handler =0; handler < mHandlers.size(); ++handler ) + mHandlers[handler]->onZoneRemoved( inSDK ); + mZones.replaceWithLast( idx ); + } + } + } + + virtual void flushProfileEvents() + { + uint32_t sdkCount = mZones.size(); + for ( uint32_t idx = 0; idx < sdkCount; ++idx ) + mZones[idx]->flushProfileEvents(); + } + + virtual void addProfileZoneHandler( PxProfileZoneHandler& inHandler ) + { + TScopedLockType lock( &mMutex ); + mHandlers.pushBack( &inHandler ); + for ( uint32_t idx = 0; idx < mZones.size(); ++idx ) + inHandler.onZoneAdded( *mZones[idx] ); + } + + virtual void removeProfileZoneHandler( PxProfileZoneHandler& inHandler ) + { + TScopedLockType lock( &mMutex ); + for( uint32_t idx = 0; idx < mZones.size(); ++idx ) + inHandler.onZoneRemoved( *mZones[idx] ); + for( uint32_t idx = 0; idx < mHandlers.size(); ++idx ) + { + if ( mHandlers[idx] == &inHandler ) + mHandlers.replaceWithLast( idx ); + } + } + + virtual PxProfileZone& createProfileZone( const char* inSDKName, PxProfileNameProvider* inProvider, uint32_t inEventBufferByteSize ) + { + NullEventNameProvider nullProvider; + if ( inProvider == NULL ) + inProvider = &nullProvider; + return createProfileZone( inSDKName, inProvider->getProfileNames(), inEventBufferByteSize ); + } + + + virtual PxProfileZone& createProfileZone( const char* inSDKName, PxProfileNames inNames, uint32_t inEventBufferByteSize ) + { + PxProfileZone& retval( PxProfileZone::createProfileZone( &mWrapper.getAllocator(), inSDKName, inNames, inEventBufferByteSize ) ); + addProfileZone( retval ); + return retval; + } + + virtual void release() + { + PX_PROFILE_DELETE( mWrapper.getAllocator(), this ); + } + }; +} } + + +#endif // PXPVDSDK_PXPROFILEZONEMANAGERIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvd.cpp b/src/PhysX/physx/source/pvd/src/PxPvd.cpp new file mode 100644 index 000000000..7873e373a --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvd.cpp @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdImpl.h" +namespace physx +{ +namespace pvdsdk +{ +ForwardingAllocator gForwardingAllocator; +PxAllocatorCallback* gPvdAllocatorCallback = &gForwardingAllocator; +} // namespace pvdsdk + +PxPvd* PxCreatePvd(PxFoundation& foundation) +{ + pvdsdk::gPvdAllocatorCallback = &foundation.getAllocatorCallback(); + pvdsdk::PvdImpl::initialize(); + return pvdsdk::PvdImpl::getInstance(); +} + +} // namespace physx diff --git a/src/PhysX/physx/source/pvd/src/PxPvdBits.h b/src/PhysX/physx/source/pvd/src/PxPvdBits.h new file mode 100644 index 000000000..2d2b93a7a --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdBits.h @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDBITS_H +#define PXPVDSDK_PXPVDBITS_H + +#include "PxPvdObjectModelBaseTypes.h" + +namespace physx +{ +namespace pvdsdk +{ + +// Marshallers cannot assume src is aligned, but they can assume dest is aligned. +typedef void (*TSingleMarshaller)(const uint8_t* src, uint8_t* dest); +typedef void (*TBlockMarshaller)(const uint8_t* src, uint8_t* dest, uint32_t numItems); + +template +static inline void doSwapBytes(uint8_t* __restrict inData) +{ + for(uint32_t idx = 0; idx < ByteCount / 2; ++idx) + { + uint32_t endIdx = ByteCount - idx - 1; + uint8_t theTemp = inData[idx]; + inData[idx] = inData[endIdx]; + inData[endIdx] = theTemp; + } +} + +template +static inline void doSwapBytes(uint8_t* __restrict inData, uint32_t itemCount) +{ + uint8_t* end = inData + itemCount * ByteCount; + for(; inData < end; inData += ByteCount) + doSwapBytes(inData); +} + +static inline void swapBytes(uint8_t* __restrict dataPtr, uint32_t numBytes, uint32_t itemWidth) +{ + uint32_t numItems = numBytes / itemWidth; + switch(itemWidth) + { + case 1: + break; + case 2: + doSwapBytes<2>(dataPtr, numItems); + break; + case 4: + doSwapBytes<4>(dataPtr, numItems); + break; + case 8: + doSwapBytes<8>(dataPtr, numItems); + break; + case 16: + doSwapBytes<16>(dataPtr, numItems); + break; + default: + PX_ASSERT(false); + break; + } +} + +static inline void swapBytes(uint8_t&) +{ +} +static inline void swapBytes(int8_t&) +{ +} +static inline void swapBytes(uint16_t& inData) +{ + doSwapBytes<2>(reinterpret_cast(&inData)); +} +static inline void swapBytes(int16_t& inData) +{ + doSwapBytes<2>(reinterpret_cast(&inData)); +} +static inline void swapBytes(uint32_t& inData) +{ + doSwapBytes<4>(reinterpret_cast(&inData)); +} +static inline void swapBytes(int32_t& inData) +{ + doSwapBytes<4>(reinterpret_cast(&inData)); +} +static inline void swapBytes(float& inData) +{ + doSwapBytes<4>(reinterpret_cast(&inData)); +} +static inline void swapBytes(uint64_t& inData) +{ + doSwapBytes<8>(reinterpret_cast(&inData)); +} +static inline void swapBytes(int64_t& inData) +{ + doSwapBytes<8>(reinterpret_cast(&inData)); +} +static inline void swapBytes(double& inData) +{ + doSwapBytes<8>(reinterpret_cast(&inData)); +} + +static inline bool checkLength(const uint8_t* inStart, const uint8_t* inStop, uint32_t inLength) +{ + return static_cast(inStop - inStart) >= inLength; +} +} +} +#endif // PXPVDSDK_PXPVDBITS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h b/src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h new file mode 100644 index 000000000..0a3d3fccc --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h @@ -0,0 +1,126 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDBYTESTREAMS_H +#define PXPVDSDK_PXPVDBYTESTREAMS_H +#include "PxPvdObjectModelBaseTypes.h" + +namespace physx +{ +namespace pvdsdk +{ + +static inline uint32_t strLen(const char* inStr) +{ + uint32_t len = 0; + if(inStr) + { + while(*inStr) + { + ++len; + ++inStr; + } + } + return len; +} + +class PvdInputStream +{ + protected: + virtual ~PvdInputStream() + { + } + + public: + // Return false if you can't write the number of bytes requested + // But make an absolute best effort to read the data... + virtual bool read(uint8_t* buffer, uint32_t& len) = 0; + + template + bool read(TDataType* buffer, uint32_t numItems) + { + uint32_t expected = numItems; + uint32_t amountToRead = numItems * sizeof(TDataType); + read(reinterpret_cast(buffer), amountToRead); + numItems = amountToRead / sizeof(TDataType); + PX_ASSERT(numItems == expected); + return expected == numItems; + } + + template + PvdInputStream& operator>>(TDataType& data) + { + uint32_t dataSize = static_cast(sizeof(TDataType)); + bool success = read(reinterpret_cast(&data), dataSize); + // PX_ASSERT( success ); + // PX_ASSERT( dataSize == sizeof( data ) ); + (void)success; + return *this; + } +}; + +class PvdOutputStream +{ + protected: + virtual ~PvdOutputStream() + { + } + + public: + // Return false if you can't write the number of bytes requested + // But make an absolute best effort to write the data... + virtual bool write(const uint8_t* buffer, uint32_t len) = 0; + virtual bool directCopy(PvdInputStream& inStream, uint32_t len) = 0; + + template + bool write(const TDataType* buffer, uint32_t numItems) + { + return write(reinterpret_cast(buffer), numItems * sizeof(TDataType)); + } + + template + PvdOutputStream& operator<<(const TDataType& data) + { + bool success = write(reinterpret_cast(&data), sizeof(data)); + PX_ASSERT(success); + (void)success; + return *this; + } + + PvdOutputStream& operator<<(const char* inString) + { + if(inString && *inString) + { + uint32_t len(strLen(inString)); + write(inString, len); + } + return *this; + } +}; +} +} +#endif // PXPVDSDK_PXPVDBYTESTREAMS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h new file mode 100644 index 000000000..d0b843680 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h @@ -0,0 +1,55 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDCOMMSTREAMEVENTSINK_H +#define PXPVDSDK_PXPVDCOMMSTREAMEVENTSINK_H + +#include "PxPvdObjectModelBaseTypes.h" +#include "PxPvdCommStreamEvents.h" +#include "PxPvdCommStreamTypes.h" + +namespace physx +{ +namespace pvdsdk +{ + +class PvdCommStreamEventSink +{ + public: + template + static void writeStreamEvent(const EventSerializeable& evt, PvdCommStreamEventTypes::Enum evtType, TStreamType& stream) + { + EventStreamifier streamifier_concrete(stream); + PvdEventSerializer& streamifier(streamifier_concrete); + streamifier.streamify(evtType); + const_cast(evt).serialize(streamifier); + } +}; + +} // pvd +} // physx +#endif // PXPVDSDK_PXPVDCOMMSTREAMEVENTSINK_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h new file mode 100644 index 000000000..89361be66 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h @@ -0,0 +1,987 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDCOMMSTREAMEVENTS_H +#define PXPVDSDK_PXPVDCOMMSTREAMEVENTS_H + +#include "foundation/PxVec3.h" +#include "foundation/PxFlags.h" + +#include "PxPvdObjectModelBaseTypes.h" +#include "PsTime.h" + +namespace physx +{ +namespace pvdsdk +{ + +struct CommStreamFlagTypes +{ + enum Enum + { + Is64BitPtr = 1 + }; +}; + +typedef PxFlags CommStreamFlags; + +template +struct PvdCommVariableSizedEventCheck +{ + bool variable_size_check; +}; + +// Pick out the events that are possibly very large. +// This helps us keep our buffers close to the size the user requested. +#define DECLARE_TYPE_VARIABLE_SIZED(type) \ + template <> \ + struct PvdCommVariableSizedEventCheck \ + { \ + uint32_t variable_size_check; \ + }; + +struct NameHandleValue; +struct StreamPropMessageArg; +struct StringHandleEvent; +struct CreateClass; +struct DeriveClass; +struct CreateProperty; +struct CreatePropertyMessage; +struct CreateInstance; +struct SetPropertyValue; +struct BeginSetPropertyValue; +struct AppendPropertyValueData; +struct EndSetPropertyValue; +struct SetPropertyMessage; +struct BeginPropertyMessageGroup; +struct SendPropertyMessageFromGroup; +struct EndPropertyMessageGroup; +struct CreateDestroyInstanceProperty; +struct PushBackObjectRef; +struct RemoveObjectRef; +struct BeginSection; +struct EndSection; +struct SetPickable; +struct SetColor; +struct SetIsTopLevel; +struct SetCamera; +struct AddProfileZone; +struct AddProfileZoneEvent; +struct StreamEndEvent; +struct ErrorMessage; +struct OriginShift; +struct DestroyInstance; + +#define DECLARE_COMM_STREAM_EVENTS \ + \ +DECLARE_PVD_COMM_STREAM_EVENT(StringHandleEvent) \ +DECLARE_PVD_COMM_STREAM_EVENT(CreateClass) \ +DECLARE_PVD_COMM_STREAM_EVENT(DeriveClass) \ +DECLARE_PVD_COMM_STREAM_EVENT(CreateProperty) \ +DECLARE_PVD_COMM_STREAM_EVENT(CreatePropertyMessage) \ +DECLARE_PVD_COMM_STREAM_EVENT(CreateInstance) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetPropertyValue) \ +DECLARE_PVD_COMM_STREAM_EVENT(BeginSetPropertyValue) \ +DECLARE_PVD_COMM_STREAM_EVENT(AppendPropertyValueData) \ +DECLARE_PVD_COMM_STREAM_EVENT(EndSetPropertyValue) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetPropertyMessage) \ +DECLARE_PVD_COMM_STREAM_EVENT(BeginPropertyMessageGroup) \ +DECLARE_PVD_COMM_STREAM_EVENT(SendPropertyMessageFromGroup) \ +DECLARE_PVD_COMM_STREAM_EVENT(EndPropertyMessageGroup) \ +DECLARE_PVD_COMM_STREAM_EVENT(DestroyInstance) \ +DECLARE_PVD_COMM_STREAM_EVENT(PushBackObjectRef) \ +DECLARE_PVD_COMM_STREAM_EVENT(RemoveObjectRef) \ +DECLARE_PVD_COMM_STREAM_EVENT(BeginSection) \ +DECLARE_PVD_COMM_STREAM_EVENT(EndSection) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetPickable) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetColor) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetIsTopLevel) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetCamera) \ +DECLARE_PVD_COMM_STREAM_EVENT(AddProfileZone) \ +DECLARE_PVD_COMM_STREAM_EVENT(AddProfileZoneEvent) \ +DECLARE_PVD_COMM_STREAM_EVENT(StreamEndEvent) \ +DECLARE_PVD_COMM_STREAM_EVENT(ErrorMessage) \ +DECLARE_PVD_COMM_STREAM_EVENT_NO_COMMA(OriginShift) + +struct PvdCommStreamEventTypes +{ + enum Enum + { + Unknown = 0, +#define DECLARE_PVD_COMM_STREAM_EVENT(x) x, +#define DECLARE_PVD_COMM_STREAM_EVENT_NO_COMMA(x) x + DECLARE_COMM_STREAM_EVENTS +#undef DECLARE_PVD_COMM_STREAM_EVENT_NO_COMMA +#undef DECLARE_PVD_COMM_STREAM_EVENT + , Last + }; +}; + +template +struct DatatypeToCommEventType +{ + bool compile_error; +}; +template +struct CommEventTypeToDatatype +{ + bool compile_error; +}; + +#define DECLARE_PVD_COMM_STREAM_EVENT(x) \ + template <> \ + struct DatatypeToCommEventType \ + { \ + enum Enum \ + { \ + EEventTypeMap = PvdCommStreamEventTypes::x \ + }; \ + }; \ + template <> \ + struct CommEventTypeToDatatype \ + { \ + typedef x TEventType; \ + }; +#define DECLARE_PVD_COMM_STREAM_EVENT_NO_COMMA(x) \ + \ +template<> struct DatatypeToCommEventType \ + { \ + enum Enum \ + { \ + EEventTypeMap = PvdCommStreamEventTypes::x \ + }; \ + }; \ + \ +template<> struct CommEventTypeToDatatype \ + { \ + typedef x TEventType; \ + }; + +DECLARE_COMM_STREAM_EVENTS +#undef DECLARE_PVD_COMM_STREAM_EVENT_NO_COMMA +#undef DECLARE_PVD_COMM_STREAM_EVENT + +template +PvdCommStreamEventTypes::Enum getCommStreamEventType() +{ + return static_cast(DatatypeToCommEventType::EEventTypeMap); +} + +struct StreamNamespacedName +{ + StringHandle mNamespace; // StringHandle handles + StringHandle mName; + StreamNamespacedName(StringHandle ns = 0, StringHandle nm = 0) : mNamespace(ns), mName(nm) + { + } +}; + +class EventSerializeable; + +class PvdEventSerializer +{ + protected: + virtual ~PvdEventSerializer() + { + } + + public: + virtual void streamify(uint8_t& val) = 0; + virtual void streamify(uint16_t& val) = 0; + virtual void streamify(uint32_t& val) = 0; + virtual void streamify(float& val) = 0; + virtual void streamify(uint64_t& val) = 0; + virtual void streamify(String& val) = 0; + virtual void streamify(DataRef& data) = 0; + virtual void streamify(DataRef& data) = 0; + virtual void streamify(DataRef& data) = 0; + virtual void streamify(DataRef& data) = 0; + + void streamify(StringHandle& hdl) + { + streamify(hdl.mHandle); + } + void streamify(CommStreamFlags& flags) + { + uint32_t val(flags); + streamify(val); + flags = CommStreamFlags(val); + } + + void streamify(PvdCommStreamEventTypes::Enum& val) + { + uint8_t detyped = static_cast(val); + streamify(detyped); + val = static_cast(detyped); + } + void streamify(PropertyType::Enum& val) + { + uint8_t detyped = static_cast(val); + streamify(detyped); + val = static_cast(detyped); + } + + void streamify(bool& val) + { + uint8_t detyped = uint8_t(val ? 1 : 0); + streamify(detyped); + val = detyped ? true : false; + } + + void streamify(StreamNamespacedName& name) + { + streamify(name.mNamespace); + streamify(name.mName); + } + + void streamify(PvdColor& color) + { + streamify(color.r); + streamify(color.g); + streamify(color.b); + streamify(color.a); + } + + void streamify(PxVec3& vec) + { + streamify(vec.x); + streamify(vec.y); + streamify(vec.z); + } + + static uint32_t measure(const EventSerializeable& evt); +}; + +class EventSerializeable +{ + protected: + virtual ~EventSerializeable() + { + } + + public: + virtual void serialize(PvdEventSerializer& serializer) = 0; +}; + +/** Numbers generated from random.org +129919156 17973702 401496246 144984007 336950759 +907025328 837150850 679717896 601529147 269478202 +*/ +struct StreamInitialization : public EventSerializeable +{ + static uint32_t getStreamId() + { + return 837150850; + } + static uint32_t getStreamVersion() + { + return 1; + } + + uint32_t mStreamId; + uint32_t mStreamVersion; + uint64_t mTimestampNumerator; + uint64_t mTimestampDenominator; + CommStreamFlags mStreamFlags; + StreamInitialization() + : mStreamId(getStreamId()) + , mStreamVersion(getStreamVersion()) + , mTimestampNumerator(physx::shdfnd::Time::getCounterFrequency().mNumerator * 10) + , mTimestampDenominator(physx::shdfnd::Time::getCounterFrequency().mDenominator) + , mStreamFlags(sizeof(void*) == 4 ? 0 : 1) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mStreamId); + s.streamify(mStreamVersion); + s.streamify(mTimestampNumerator); + s.streamify(mTimestampDenominator); + s.streamify(mStreamFlags); + } +}; + +struct EventGroup : public EventSerializeable +{ + uint32_t mDataSize; // in bytes, data directly follows this header + uint32_t mNumEvents; + uint64_t mStreamId; + uint64_t mTimestamp; + + EventGroup(uint32_t dataSize = 0, uint32_t numEvents = 0, uint64_t streamId = 0, uint64_t ts = 0) + : mDataSize(dataSize), mNumEvents(numEvents), mStreamId(streamId), mTimestamp(ts) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mDataSize); + s.streamify(mNumEvents); + s.streamify(mStreamId); + s.streamify(mTimestamp); + } +}; + +struct StringHandleEvent : public EventSerializeable +{ + String mString; + uint32_t mHandle; + StringHandleEvent(String str, uint32_t hdl) : mString(str), mHandle(hdl) + { + } + StringHandleEvent() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mString); + s.streamify(mHandle); + } +}; + +DECLARE_TYPE_VARIABLE_SIZED(StringHandleEvent) + +typedef uint64_t Timestamp; + +struct CreateClass : public EventSerializeable +{ + StreamNamespacedName mName; + CreateClass(StreamNamespacedName nm) : mName(nm) + { + } + CreateClass() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mName); + } +}; + +struct DeriveClass : public EventSerializeable +{ + StreamNamespacedName mParent; + StreamNamespacedName mChild; + + DeriveClass(StreamNamespacedName p, StreamNamespacedName c) : mParent(p), mChild(c) + { + } + DeriveClass() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mParent); + s.streamify(mChild); + } +}; + +struct NameHandleValue : public EventSerializeable +{ + StringHandle mName; + uint32_t mValue; + NameHandleValue(StringHandle name, uint32_t val) : mName(name), mValue(val) + { + } + NameHandleValue() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mName); + s.streamify(mValue); + } +}; +/*virtual PvdError createProperty( StreamNamespacedName clsName, StringHandle name, StringHandle semantic + , StreamNamespacedName dtypeName, PropertyType::Enum propertyType + , DataRef values = DataRef() ) = 0; */ +struct CreateProperty : public EventSerializeable +{ + StreamNamespacedName mClass; + StringHandle mName; + StringHandle mSemantic; + StreamNamespacedName mDatatypeName; + PropertyType::Enum mPropertyType; + DataRef mValues; + + CreateProperty(StreamNamespacedName cls, StringHandle name, StringHandle semantic, StreamNamespacedName dtypeName, + PropertyType::Enum ptype, DataRef values) + : mClass(cls), mName(name), mSemantic(semantic), mDatatypeName(dtypeName), mPropertyType(ptype), mValues(values) + { + } + CreateProperty() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mClass); + s.streamify(mName); + s.streamify(mSemantic); + s.streamify(mDatatypeName); + s.streamify(mPropertyType); + s.streamify(mValues); + } +}; + +struct StreamPropMessageArg : public EventSerializeable +{ + StringHandle mPropertyName; + StreamNamespacedName mDatatypeName; + uint32_t mMessageOffset; + uint32_t mByteSize; + StreamPropMessageArg(StringHandle pname, StreamNamespacedName dtypeName, uint32_t offset, uint32_t byteSize) + : mPropertyName(pname), mDatatypeName(dtypeName), mMessageOffset(offset), mByteSize(byteSize) + { + } + + StreamPropMessageArg() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mPropertyName); + s.streamify(mDatatypeName); + s.streamify(mMessageOffset); + s.streamify(mByteSize); + } +}; + +/* + virtual PvdError createPropertyMessage( StreamNamespacedName cls, StreamNamespacedName msgName + , DataRef entries, uint32_t messageSizeInBytes ) = + 0;*/ +struct CreatePropertyMessage : public EventSerializeable +{ + StreamNamespacedName mClass; + StreamNamespacedName mMessageName; + DataRef mMessageEntries; + uint32_t mMessageByteSize; + + CreatePropertyMessage(StreamNamespacedName cls, StreamNamespacedName msgName, DataRef propArg, + uint32_t messageByteSize) + : mClass(cls), mMessageName(msgName), mMessageEntries(propArg), mMessageByteSize(messageByteSize) + { + } + CreatePropertyMessage() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mClass); + s.streamify(mMessageName); + s.streamify(mMessageEntries); + s.streamify(mMessageByteSize); + } +}; + +/**Changing immediate data on instances*/ + +// virtual PvdError createInstance( StreamNamespacedName cls, uint64_t instance ) = 0; +struct CreateInstance : public EventSerializeable +{ + StreamNamespacedName mClass; + uint64_t mInstanceId; + + CreateInstance(StreamNamespacedName cls, uint64_t streamId) : mClass(cls), mInstanceId(streamId) + { + } + CreateInstance() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mClass); + s.streamify(mInstanceId); + } +}; + +// virtual PvdError setPropertyValue( uint64_t instance, StringHandle name, DataRef data, +// StreamNamespacedName incomingTypeName ) = 0; +struct SetPropertyValue : public EventSerializeable +{ + uint64_t mInstanceId; + StringHandle mPropertyName; + DataRef mData; + StreamNamespacedName mIncomingTypeName; + uint32_t mNumItems; + + SetPropertyValue(uint64_t instance, StringHandle name, DataRef data, + StreamNamespacedName incomingTypeName, uint32_t numItems) + : mInstanceId(instance), mPropertyName(name), mData(data), mIncomingTypeName(incomingTypeName), mNumItems(numItems) + { + } + + SetPropertyValue() + { + } + + void serializeBeginning(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mPropertyName); + s.streamify(mIncomingTypeName); + s.streamify(mNumItems); + } + + void serialize(PvdEventSerializer& s) + { + serializeBeginning(s); + s.streamify(mData); + } +}; + +DECLARE_TYPE_VARIABLE_SIZED(SetPropertyValue) + +struct BeginSetPropertyValue : public EventSerializeable +{ + uint64_t mInstanceId; + StringHandle mPropertyName; + StreamNamespacedName mIncomingTypeName; + + BeginSetPropertyValue(uint64_t instance, StringHandle name, StreamNamespacedName incomingTypeName) + : mInstanceId(instance), mPropertyName(name), mIncomingTypeName(incomingTypeName) + { + } + BeginSetPropertyValue() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mPropertyName); + s.streamify(mIncomingTypeName); + } +}; + +// virtual PvdError appendPropertyValueData( DataRef data ) = 0; +struct AppendPropertyValueData : public EventSerializeable +{ + DataRef mData; + uint32_t mNumItems; + AppendPropertyValueData(DataRef data, uint32_t numItems) : mData(data), mNumItems(numItems) + { + } + AppendPropertyValueData() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mData); + s.streamify(mNumItems); + } +}; + +DECLARE_TYPE_VARIABLE_SIZED(AppendPropertyValueData) + +// virtual PvdError endSetPropertyValue() = 0; +struct EndSetPropertyValue : public EventSerializeable +{ + EndSetPropertyValue() + { + } + + void serialize(PvdEventSerializer&) + { + } +}; + +// virtual PvdError setPropertyMessage( uint64_t instance, StreamNamespacedName msgName, DataRef data ) = +// 0; +struct SetPropertyMessage : public EventSerializeable +{ + uint64_t mInstanceId; + StreamNamespacedName mMessageName; + DataRef mData; + + SetPropertyMessage(uint64_t instance, StreamNamespacedName msgName, DataRef data) + : mInstanceId(instance), mMessageName(msgName), mData(data) + { + } + + SetPropertyMessage() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mMessageName); + s.streamify(mData); + } +}; + +DECLARE_TYPE_VARIABLE_SIZED(SetPropertyMessage) + +// virtual PvdError beginPropertyMessageGroup( StreamNamespacedName msgName ) = 0; +struct BeginPropertyMessageGroup : public EventSerializeable +{ + StreamNamespacedName mMsgName; + BeginPropertyMessageGroup(StreamNamespacedName msgName) : mMsgName(msgName) + { + } + BeginPropertyMessageGroup() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mMsgName); + } +}; + +// virtual PvdError sendPropertyMessageFromGroup( uint64_t instance, DataRef data ) = 0; +struct SendPropertyMessageFromGroup : public EventSerializeable +{ + uint64_t mInstance; + DataRef mData; + + SendPropertyMessageFromGroup(uint64_t instance, DataRef data) : mInstance(instance), mData(data) + { + } + SendPropertyMessageFromGroup() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstance); + s.streamify(mData); + } +}; + +DECLARE_TYPE_VARIABLE_SIZED(SendPropertyMessageFromGroup) + +// virtual PvdError endPropertyMessageGroup() = 0; +struct EndPropertyMessageGroup : public EventSerializeable +{ + EndPropertyMessageGroup() + { + } + + void serialize(PvdEventSerializer&) + { + } +}; + +struct PushBackObjectRef : public EventSerializeable +{ + uint64_t mInstanceId; + StringHandle mProperty; + uint64_t mObjectRef; + + PushBackObjectRef(uint64_t instId, StringHandle prop, uint64_t objRef) + : mInstanceId(instId), mProperty(prop), mObjectRef(objRef) + { + } + + PushBackObjectRef() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mProperty); + s.streamify(mObjectRef); + } +}; + +struct RemoveObjectRef : public EventSerializeable +{ + uint64_t mInstanceId; + StringHandle mProperty; + uint64_t mObjectRef; + + RemoveObjectRef(uint64_t instId, StringHandle prop, uint64_t objRef) + : mInstanceId(instId), mProperty(prop), mObjectRef(objRef) + { + } + + RemoveObjectRef() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mProperty); + s.streamify(mObjectRef); + } +}; + +// virtual PvdError destroyInstance( uint64_t key ) = 0; +struct DestroyInstance : public EventSerializeable +{ + uint64_t mInstanceId; + DestroyInstance(uint64_t instance) : mInstanceId(instance) + { + } + DestroyInstance() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + } +}; + +// virtual PvdError beginSection( uint64_t sectionId, StringHandle name ) = 0; +struct BeginSection : public EventSerializeable +{ + uint64_t mSectionId; + StringHandle mName; + Timestamp mTimestamp; + BeginSection(uint64_t sectionId, StringHandle name, uint64_t timestamp) + : mSectionId(sectionId), mName(name), mTimestamp(timestamp) + { + } + BeginSection() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mSectionId); + s.streamify(mName); + s.streamify(mTimestamp); + } +}; +// virtual PvdError endSection( uint64_t sectionId, StringHandle name ) = 0; +struct EndSection : public EventSerializeable +{ + uint64_t mSectionId; + StringHandle mName; + Timestamp mTimestamp; + EndSection(uint64_t sectionId, StringHandle name, uint64_t timestamp) + : mSectionId(sectionId), mName(name), mTimestamp(timestamp) + { + } + EndSection() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mSectionId); + s.streamify(mName); + s.streamify(mTimestamp); + } +}; + +// virtual void setPickable( void* instance, bool pickable ) = 0; +struct SetPickable : public EventSerializeable +{ + uint64_t mInstanceId; + bool mPickable; + SetPickable(uint64_t instId, bool pick) : mInstanceId(instId), mPickable(pick) + { + } + SetPickable() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mPickable); + } +}; +// virtual void setColor( void* instance, const PvdColor& color ) = 0; +struct SetColor : public EventSerializeable +{ + uint64_t mInstanceId; + PvdColor mColor; + SetColor(uint64_t instId, PvdColor color) : mInstanceId(instId), mColor(color) + { + } + SetColor() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mColor); + } +}; + +// virtual void setColor( void* instance, const PvdColor& color ) = 0; +struct SetIsTopLevel : public EventSerializeable +{ + uint64_t mInstanceId; + bool mIsTopLevel; + + SetIsTopLevel(uint64_t instId, bool topLevel) : mInstanceId(instId), mIsTopLevel(topLevel) + { + } + SetIsTopLevel() : mIsTopLevel(false) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mIsTopLevel); + } +}; + +struct SetCamera : public EventSerializeable +{ + String mName; + PxVec3 mPosition; + PxVec3 mUp; + PxVec3 mTarget; + SetCamera(String name, const PxVec3& pos, const PxVec3& up, const PxVec3& target) + : mName(name), mPosition(pos), mUp(up), mTarget(target) + { + } + SetCamera() : mName(NULL) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mName); + s.streamify(mPosition); + s.streamify(mUp); + s.streamify(mTarget); + } +}; + +struct ErrorMessage : public EventSerializeable +{ + uint32_t mCode; + String mMessage; + String mFile; + uint32_t mLine; + + ErrorMessage(uint32_t code, String message, String file, uint32_t line) + : mCode(code), mMessage(message), mFile(file), mLine(line) + { + } + + ErrorMessage() : mMessage(NULL), mFile(NULL) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mCode); + s.streamify(mMessage); + s.streamify(mFile); + s.streamify(mLine); + } +}; + +struct AddProfileZone : public EventSerializeable +{ + uint64_t mInstanceId; + String mName; + AddProfileZone(uint64_t iid, String nm) : mInstanceId(iid), mName(nm) + { + } + AddProfileZone() : mName(NULL) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mName); + } +}; + +struct AddProfileZoneEvent : public EventSerializeable +{ + uint64_t mInstanceId; + String mName; + uint16_t mEventId; + bool mCompileTimeEnabled; + AddProfileZoneEvent(uint64_t iid, String nm, uint16_t eid, bool cte) + : mInstanceId(iid), mName(nm), mEventId(eid), mCompileTimeEnabled(cte) + { + } + AddProfileZoneEvent() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mName); + s.streamify(mEventId); + s.streamify(mCompileTimeEnabled); + } +}; + +struct StreamEndEvent : public EventSerializeable +{ + String mName; + StreamEndEvent() : mName("StreamEnd") + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mName); + } +}; + +struct OriginShift : public EventSerializeable +{ + uint64_t mInstanceId; + PxVec3 mShift; + + OriginShift(uint64_t iid, const PxVec3& shift) : mInstanceId(iid), mShift(shift) + { + } + OriginShift() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mShift); + } +}; +} // pvdsdk +} // physx + +#endif // PXPVDSDK_PXPVDCOMMSTREAMEVENTS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h new file mode 100644 index 000000000..b57075086 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h @@ -0,0 +1,229 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDCOMMSTREAMTYPES_H +#define PXPVDSDK_PXPVDCOMMSTREAMTYPES_H + +#include "foundation/PxErrorCallback.h" +#include "pvd/PxPvdTransport.h" + +#include "PxPvdRenderBuffer.h" +#include "PxPvdObjectModelBaseTypes.h" +#include "PxPvdCommStreamEvents.h" +#include "PxPvdDataStream.h" +#include "PsMutex.h" + +namespace physx +{ +namespace profile +{ +class PxProfileZone; +class PxProfileMemoryEventBuffer; +} +namespace pvdsdk +{ +struct PvdErrorMessage; +class PvdObjectModelMetaData; + +DEFINE_PVD_TYPE_NAME_MAP(profile::PxProfileZone, "_debugger_", "PxProfileZone") +DEFINE_PVD_TYPE_NAME_MAP(profile::PxProfileMemoryEventBuffer, "_debugger_", "PxProfileMemoryEventBuffer") +DEFINE_PVD_TYPE_NAME_MAP(PvdErrorMessage, "_debugger_", "PvdErrorMessage") +// All event streams are on the 'events' property of objects of these types +static inline NamespacedName getMemoryEventTotalsClassName() +{ + return NamespacedName("_debugger", "MemoryEventTotals"); +} + +class PvdOMMetaDataProvider +{ + protected: + virtual ~PvdOMMetaDataProvider() + { + } + + public: + virtual void addRef() = 0; + virtual void release() = 0; + virtual PvdObjectModelMetaData& lock() = 0; + virtual void unlock() = 0; + virtual bool createInstance(const NamespacedName& clsName, const void* instance) = 0; + virtual bool isInstanceValid(const void* instance) = 0; + virtual void destroyInstance(const void* instance) = 0; + virtual int32_t getInstanceClassType(const void* instance) = 0; +}; + +class PvdCommStreamEmbeddedTypes +{ + public: + static const char* getProfileEventStreamSemantic() + { + return "profile event stream"; + } + static const char* getMemoryEventStreamSemantic() + { + return "memory event stream"; + } + static const char* getRendererEventStreamSemantic() + { + return "render event stream"; + } +}; + +class PvdCommStreamEventBufferClient; + +template +struct EventStreamifier : public PvdEventSerializer +{ + TStreamType& mBuffer; + EventStreamifier(TStreamType& buf) : mBuffer(buf) + { + } + + template + void write(const TDataType& type) + { + mBuffer.write(reinterpret_cast(&type), sizeof(TDataType)); + } + template + void write(const TDataType* type, uint32_t count) + { + mBuffer.write(reinterpret_cast(type), count * sizeof(TDataType)); + } + + void writeRef(DataRef data) + { + uint32_t amount = static_cast(data.size()); + write(amount); + write(data.begin(), amount); + } + void writeRef(DataRef data) + { + uint32_t amount = static_cast(data.size()); + write(amount); + write(data.begin(), amount); + } + template + void writeRef(DataRef data) + { + uint32_t amount = static_cast(data.size()); + write(amount); + for(uint32_t idx = 0; idx < amount; ++idx) + { + TDataType& dtype(const_cast(data[idx])); + dtype.serialize(*this); + } + } + + virtual void streamify(uint16_t& val) + { + write(val); + } + virtual void streamify(uint8_t& val) + { + write(val); + } + virtual void streamify(uint32_t& val) + { + write(val); + } + virtual void streamify(float& val) + { + write(val); + } + virtual void streamify(uint64_t& val) + { + write(val); + } + virtual void streamify(PvdDebugText& val) + { + write(val.color); + write(val.position); + write(val.size); + streamify(val.string); + } + + virtual void streamify(String& val) + { + uint32_t len = 0; + String temp = nonNull(val); + if(*temp) + len = static_cast(strlen(temp) + 1); + write(len); + write(val, len); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + + private: + EventStreamifier& operator=(const EventStreamifier&); +}; + +struct MeasureStream +{ + uint32_t mSize; + MeasureStream() : mSize(0) + { + } + template + void write(const TDataType& val) + { + mSize += sizeof(val); + } + template + void write(const TDataType*, uint32_t count) + { + mSize += sizeof(TDataType) * count; + } +}; + +struct DataStreamState +{ + enum Enum + { + Open, + SetPropertyValue, + PropertyMessageGroup + }; +}; + +} // pvdsdk +} // physx +#endif // PXPVDSDK_PXPVDCOMMSTREAMTYPES_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp b/src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp new file mode 100644 index 000000000..5195c7d44 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp @@ -0,0 +1,864 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxPvdCommStreamEventSink.h" +#include "PxPvdDataStreamHelpers.h" +#include "PxPvdObjectModelInternalTypes.h" +#include "PxPvdImpl.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace physx::pvdsdk; +using namespace physx::shdfnd; + +namespace +{ + +struct ScopedMetaData +{ + PvdOMMetaDataProvider& mProvider; + PvdObjectModelMetaData& mMeta; + ScopedMetaData(PvdOMMetaDataProvider& provider) : mProvider(provider), mMeta(provider.lock()) + { + } + ~ScopedMetaData() + { + mProvider.unlock(); + } + PvdObjectModelMetaData* operator->() + { + return &mMeta; + } + + private: + ScopedMetaData& operator=(const ScopedMetaData&); +}; + +struct PropertyDefinitionHelper : public PvdPropertyDefinitionHelper +{ + PvdDataStream* mStream; + PvdOMMetaDataProvider& mProvider; + Array mNameBuffer; + Array mNameStack; + Array mNamedValues; + Array mPropertyMessageArgs; + + PropertyDefinitionHelper(PvdOMMetaDataProvider& provider) + : mStream(NULL) + , mProvider(provider) + , mNameBuffer("PropertyDefinitionHelper::mNameBuffer") + , mNameStack("PropertyDefinitionHelper::mNameStack") + , mNamedValues("PropertyDefinitionHelper::mNamedValues") + , mPropertyMessageArgs("PropertyDefinitionHelper::mPropertyMessageArgs") + { + } + void setStream(PvdDataStream* stream) + { + mStream = stream; + } + + inline void appendStrToBuffer(const char* str) + { + if(str == NULL) + return; + size_t strLen = strlen(str); + size_t endBufOffset = mNameBuffer.size(); + size_t resizeLen = endBufOffset; + // account for null + if(mNameBuffer.empty()) + resizeLen += 1; + else + endBufOffset -= 1; + + mNameBuffer.resize(static_cast(resizeLen + strLen)); + char* endPtr = mNameBuffer.begin() + endBufOffset; + PxMemCopy(endPtr, str, static_cast(strLen)); + } + + virtual void pushName(const char* nm, const char* appender = ".") + { + size_t nameBufLen = mNameBuffer.size(); + mNameStack.pushBack(static_cast(nameBufLen)); + if(mNameBuffer.empty() == false) + appendStrToBuffer(appender); + appendStrToBuffer(nm); + mNameBuffer.back() = 0; + } + + virtual void pushBracketedName(const char* inName, const char* leftBracket = "[", const char* rightBracket = "]") + { + size_t nameBufLen = mNameBuffer.size(); + mNameStack.pushBack(static_cast(nameBufLen)); + appendStrToBuffer(leftBracket); + appendStrToBuffer(inName); + appendStrToBuffer(rightBracket); + mNameBuffer.back() = 0; + } + + virtual void popName() + { + if(mNameStack.empty()) + return; + mNameBuffer.resize(static_cast(mNameStack.back())); + mNameStack.popBack(); + if(mNameBuffer.empty() == false) + mNameBuffer.back() = 0; + } + + virtual const char* getTopName() + { + if(mNameBuffer.size()) + return mNameBuffer.begin(); + return ""; + } + virtual void clearNameStack() + { + mNameBuffer.clear(); + mNameStack.clear(); + } + + virtual void addNamedValue(const char* name, uint32_t value) + { + mNamedValues.pushBack(NamedValue(name, value)); + } + virtual void clearNamedValues() + { + mNamedValues.clear(); + } + + virtual DataRef getNamedValues() + { + return DataRef(mNamedValues.begin(), mNamedValues.size()); + } + + virtual void createProperty(const NamespacedName& clsName, const char* inSemantic, const NamespacedName& dtypeName, + PropertyType::Enum propType) + { + mStream->createProperty(clsName, getTopName(), inSemantic, dtypeName, propType, getNamedValues()); + clearNamedValues(); + } + const char* registerStr(const char* str) + { + ScopedMetaData scopedProvider(mProvider); + return scopedProvider->getStringTable().registerStr(str); + } + virtual void addPropertyMessageArg(const NamespacedName& inDatatype, uint32_t inOffset, uint32_t inSize) + { + mPropertyMessageArgs.pushBack(PropertyMessageArg(registerStr(getTopName()), inDatatype, inOffset, inSize)); + } + virtual void addPropertyMessage(const NamespacedName& clsName, const NamespacedName& msgName, + uint32_t inStructSizeInBytes) + { + if(mPropertyMessageArgs.empty()) + { + PX_ASSERT(false); + return; + } + mStream->createPropertyMessage( + clsName, msgName, DataRef(mPropertyMessageArgs.begin(), mPropertyMessageArgs.size()), + inStructSizeInBytes); + } + virtual void clearPropertyMessageArgs() + { + mPropertyMessageArgs.clear(); + } + + private: + PropertyDefinitionHelper& operator=(const PropertyDefinitionHelper&); +}; + +class PvdMemPool +{ + // Link List + Array mMemBuffer; + uint32_t mLength; + uint32_t mBufIndex; + + // 4k for one page + static const int BUFFER_LENGTH = 4096; + PX_NOCOPY(PvdMemPool) + public: + PvdMemPool(const char* bufDataName) : mMemBuffer(bufDataName), mLength(0), mBufIndex(0) + { + grow(); + } + + ~PvdMemPool() + { + for(uint32_t i = 0; i < mMemBuffer.size(); i++) + { + PX_FREE(mMemBuffer[i]); + } + } + + void grow() + { + if(mBufIndex + 1 < mMemBuffer.size()) + { + mBufIndex++; + } + else + { + uint8_t* Buf = reinterpret_cast(PX_ALLOC(BUFFER_LENGTH, "PvdMemPool::mMemBuffer.buf")); + mMemBuffer.pushBack(Buf); + mBufIndex = mMemBuffer.size() - 1; + } + mLength = 0; + } + + void* allocate(uint32_t length) + { + if(length > uint32_t(BUFFER_LENGTH)) + return NULL; + + if(length + mLength > uint32_t(BUFFER_LENGTH)) + grow(); + + void* mem = reinterpret_cast(&mMemBuffer[mBufIndex][mLength]); + mLength += length; + return mem; + } + + void clear() + { + mLength = 0; + mBufIndex = 0; + } +}; +struct PvdOutStream : public PvdDataStream, public UserAllocated +{ + HashMap mStringHashMap; + PvdOMMetaDataProvider& mMetaDataProvider; + Array mTempBuffer; + PropertyDefinitionHelper mPropertyDefinitionHelper; + DataStreamState::Enum mStreamState; + + ClassDescription mSPVClass; + PropertyMessageDescription mMessageDesc; + // Set property value and SetPropertyMessage calls require + // us to write the data out to a separate buffer + // when strings are involved. + ForwardingMemoryBuffer mSPVBuffer; + uint32_t mEventCount; + uint32_t mPropertyMessageSize; + bool mConnected; + uint64_t mStreamId; + Array mPvdCommandArray; + PvdMemPool mPvdCommandPool; + PxPvdTransport& mTransport; + + PvdOutStream(PxPvdTransport& transport, PvdOMMetaDataProvider& provider, uint64_t streamId) + : mStringHashMap("PvdOutStream::mStringHashMap") + , mMetaDataProvider(provider) + , mTempBuffer("PvdOutStream::mTempBuffer") + , mPropertyDefinitionHelper(mMetaDataProvider) + , mStreamState(DataStreamState::Open) + , mSPVBuffer("PvdCommStreamBufferedEventSink::mSPVBuffer") + , mEventCount(0) + , mPropertyMessageSize(0) + , mConnected(true) + , mStreamId(streamId) + , mPvdCommandArray("PvdCommStreamBufferedEventSink::mPvdCommandArray") + , mPvdCommandPool("PvdCommStreamBufferedEventSink::mPvdCommandPool") + , mTransport(transport) + { + mPropertyDefinitionHelper.setStream(this); + } + virtual ~PvdOutStream() + { + } + + virtual void release() + { + PVD_DELETE(this); + } + + StringHandle toStream(String nm) + { + if(nm == NULL || *nm == 0) + return 0; + const HashMap::Entry* entry(mStringHashMap.find(nm)); + if(entry) + return entry->second; + ScopedMetaData meta(mMetaDataProvider); + StringHandle hdl = meta->getStringTable().strToHandle(nm); + nm = meta->getStringTable().handleToStr(hdl); + handlePvdEvent(StringHandleEvent(nm, hdl)); + mStringHashMap.insert(nm, hdl); + return hdl; + } + + StreamNamespacedName toStream(const NamespacedName& nm) + { + return StreamNamespacedName(toStream(nm.mNamespace), toStream(nm.mName)); + } + + bool isClassExist(const NamespacedName& nm) + { + ScopedMetaData meta(mMetaDataProvider); + return meta->findClass(nm).hasValue(); + } + + bool createMetaClass(const NamespacedName& nm) + { + ScopedMetaData meta(mMetaDataProvider); + meta->getOrCreateClass(nm); + return true; + } + + bool deriveMetaClass(const NamespacedName& parent, const NamespacedName& child) + { + ScopedMetaData meta(mMetaDataProvider); + return meta->deriveClass(parent, child); + } + +// You will notice that some functions are #pragma'd out throughout this file. +// This is because they are only called from asserts which means they aren't +// called in release. This causes warnings when building using snc which break +// the build. +#if PX_DEBUG + + bool propertyExists(const NamespacedName& nm, String pname) + { + ScopedMetaData meta(mMetaDataProvider); + return meta->findProperty(nm, pname).hasValue(); + } + +#endif + + PvdError boolToError(bool val) + { + if(val) + return PvdErrorType::Success; + return PvdErrorType::NetworkError; + } + + // PvdMetaDataStream + virtual PvdError createClass(const NamespacedName& nm) + { + PX_ASSERT(mStreamState == DataStreamState::Open); +#if PX_DEBUG + PX_ASSERT(isClassExist(nm) == false); +#endif + createMetaClass(nm); + return boolToError(handlePvdEvent(CreateClass(toStream(nm)))); + } + + virtual PvdError deriveClass(const NamespacedName& parent, const NamespacedName& child) + { + PX_ASSERT(mStreamState == DataStreamState::Open); +#if PX_DEBUG + PX_ASSERT(isClassExist(parent)); + PX_ASSERT(isClassExist(child)); +#endif + deriveMetaClass(parent, child); + return boolToError(handlePvdEvent(DeriveClass(toStream(parent), toStream(child)))); + } + + template + TDataType* allocTemp(uint32_t numItems) + { + uint32_t desiredBytes = numItems * sizeof(TDataType); + if(desiredBytes > mTempBuffer.size()) + mTempBuffer.resize(desiredBytes); + TDataType* retval = reinterpret_cast(mTempBuffer.begin()); + if(numItems) + { + PVD_FOREACH(idx, numItems) new (retval + idx) TDataType(); + } + return retval; + } + +#if PX_DEBUG + + // Property datatypes need to be uniform. + // At this point, the data stream cannot handle properties that + // A struct with a float member and a char member would work. + // A struct with a float member and a long member would work (more efficiently). + bool isValidPropertyDatatype(const NamespacedName& dtypeName) + { + ScopedMetaData meta(mMetaDataProvider); + ClassDescription clsDesc(meta->findClass(dtypeName)); + return clsDesc.mRequiresDestruction == false; + } + +#endif + + NamespacedName createMetaProperty(const NamespacedName& clsName, String name, String semantic, + const NamespacedName& dtypeName, PropertyType::Enum propertyType) + { + ScopedMetaData meta(mMetaDataProvider); + int32_t dtypeType = meta->findClass(dtypeName)->mClassId; + NamespacedName typeName = dtypeName; + if(dtypeType == getPvdTypeForType()) + { + dtypeType = getPvdTypeForType(); + typeName = getPvdNamespacedNameForType(); + } + Option propOpt = + meta->createProperty(meta->findClass(clsName)->mClassId, name, semantic, dtypeType, propertyType); + PX_ASSERT(propOpt.hasValue()); + PX_UNUSED(propOpt); + return typeName; + } + + virtual PvdError createProperty(const NamespacedName& clsName, String name, String semantic, + const NamespacedName& incomingDtypeName, PropertyType::Enum propertyType, + DataRef values) + { + PX_ASSERT(mStreamState == DataStreamState::Open); +#if PX_DEBUG + PX_ASSERT(isClassExist(clsName)); + PX_ASSERT(propertyExists(clsName, name) == false); +#endif + NamespacedName dtypeName(incomingDtypeName); + if(safeStrEq(dtypeName.mName, "VoidPtr")) + dtypeName.mName = "ObjectRef"; +#if PX_DEBUG + PX_ASSERT(isClassExist(dtypeName)); + PX_ASSERT(isValidPropertyDatatype(dtypeName)); +#endif + NamespacedName typeName = createMetaProperty(clsName, name, semantic, dtypeName, propertyType); + // Can't have arrays of strings or arrays of string handles due to the difficulty + // of quickly dealing with them on the network receiving side. + if(propertyType == PropertyType::Array && safeStrEq(typeName.mName, "StringHandle")) + { + PX_ASSERT(false); + return PvdErrorType::ArgumentError; + } + uint32_t numItems = values.size(); + NameHandleValue* streamValues = allocTemp(numItems); + PVD_FOREACH(idx, numItems) + streamValues[idx] = NameHandleValue(toStream(values[idx].mName), values[idx].mValue); + CreateProperty evt(toStream(clsName), toStream(name), toStream(semantic), toStream(typeName), propertyType, + DataRef(streamValues, numItems)); + return boolToError(handlePvdEvent(evt)); + } + + bool createMetaPropertyMessage(const NamespacedName& cls, const NamespacedName& msgName, + DataRef entries, uint32_t messageSizeInBytes) + { + ScopedMetaData meta(mMetaDataProvider); + return meta->createPropertyMessage(cls, msgName, entries, messageSizeInBytes).hasValue(); + } +#if PX_DEBUG + + bool messageExists(const NamespacedName& msgName) + { + ScopedMetaData meta(mMetaDataProvider); + return meta->findPropertyMessage(msgName).hasValue(); + } + +#endif + + virtual PvdError createPropertyMessage(const NamespacedName& cls, const NamespacedName& msgName, + DataRef entries, uint32_t messageSizeInBytes) + { + PX_ASSERT(mStreamState == DataStreamState::Open); +#if PX_DEBUG + PX_ASSERT(isClassExist(cls)); + PX_ASSERT(messageExists(msgName) == false); +#endif + createMetaPropertyMessage(cls, msgName, entries, messageSizeInBytes); + uint32_t numItems = entries.size(); + StreamPropMessageArg* streamValues = allocTemp(numItems); + PVD_FOREACH(idx, numItems) + streamValues[idx] = + StreamPropMessageArg(toStream(entries[idx].mPropertyName), toStream(entries[idx].mDatatypeName), + entries[idx].mMessageOffset, entries[idx].mByteSize); + CreatePropertyMessage evt(toStream(cls), toStream(msgName), + DataRef(streamValues, numItems), messageSizeInBytes); + return boolToError(handlePvdEvent(evt)); + } + + uint64_t toStream(const void* instance) + { + return PVD_POINTER_TO_U64(instance); + } + virtual PvdError createInstance(const NamespacedName& cls, const void* instance) + { + PX_ASSERT(isInstanceValid(instance) == false); + PX_ASSERT(mStreamState == DataStreamState::Open); + bool success = mMetaDataProvider.createInstance(cls, instance); + PX_ASSERT(success); + (void)success; + return boolToError(handlePvdEvent(CreateInstance(toStream(cls), toStream(instance)))); + } + + virtual bool isInstanceValid(const void* instance) + { + return mMetaDataProvider.isInstanceValid(instance); + } + +#if PX_DEBUG + + // If the property will fit or is already completely in memory + bool checkPropertyType(const void* instance, String name, const NamespacedName& incomingType) + { + int32_t instType = mMetaDataProvider.getInstanceClassType(instance); + ScopedMetaData meta(mMetaDataProvider); + Option prop = meta->findProperty(instType, name); + if(prop.hasValue() == false) + return false; + int32_t propType = prop->mDatatype; + int32_t incomingTypeId = meta->findClass(incomingType)->mClassId; + if(incomingTypeId != getPvdTypeForType()) + { + MarshalQueryResult result = meta->checkMarshalling(incomingTypeId, propType); + bool possible = result.needsMarshalling == false || result.canMarshal; + return possible; + } + else + { + if(propType != getPvdTypeForType()) + return false; + } + return true; + } + +#endif + + DataRef bufferPropertyValue(ClassDescriptionSizeInfo info, DataRef data) + { + uint32_t realSize = info.mByteSize; + uint32_t numItems = data.size() / realSize; + if(info.mPtrOffsets.size() != 0) + { + mSPVBuffer.clear(); + PVD_FOREACH(item, numItems) + { + const uint8_t* itemPtr = data.begin() + item * realSize; + mSPVBuffer.write(itemPtr, realSize); + PVD_FOREACH(stringIdx, info.mPtrOffsets.size()) + { + PtrOffset offset(info.mPtrOffsets[stringIdx]); + if(offset.mOffsetType == PtrOffsetType::VoidPtrOffset) + continue; + const char* strPtr; + physx::intrinsics::memCopy(&strPtr, itemPtr + offset.mOffset, sizeof(char*)); + strPtr = nonNull(strPtr); + uint32_t len = safeStrLen(strPtr) + 1; + mSPVBuffer.write(strPtr, len); + } + } + data = DataRef(mSPVBuffer.begin(), mSPVBuffer.size()); + } + return data; + } + + virtual PvdError setPropertyValue(const void* instance, String name, DataRef data, + const NamespacedName& incomingTypeName) + { + + PX_ASSERT(isInstanceValid(instance)); +#if PX_DEBUG + PX_ASSERT(isClassExist(incomingTypeName)); +#endif + PX_ASSERT(mStreamState == DataStreamState::Open); + ClassDescription clsDesc; + { + ScopedMetaData meta(mMetaDataProvider); + clsDesc = meta->findClass(incomingTypeName); + } + uint32_t realSize = clsDesc.getNativeSize(); + uint32_t numItems = data.size() / realSize; + data = bufferPropertyValue(clsDesc.getNativeSizeInfo(), data); + SetPropertyValue evt(toStream(instance), toStream(name), data, toStream(incomingTypeName), numItems); + return boolToError(handlePvdEvent(evt)); + } + + // Else if the property is very large (contact reports) you can send it in chunks. + virtual PvdError beginSetPropertyValue(const void* instance, String name, const NamespacedName& incomingTypeName) + { + PX_ASSERT(isInstanceValid(instance)); +#if PX_DEBUG + PX_ASSERT(isClassExist(incomingTypeName)); + PX_ASSERT(checkPropertyType(instance, name, incomingTypeName)); +#endif + PX_ASSERT(mStreamState == DataStreamState::Open); + mStreamState = DataStreamState::SetPropertyValue; + { + ScopedMetaData meta(mMetaDataProvider); + mSPVClass = meta->findClass(incomingTypeName); + } + BeginSetPropertyValue evt(toStream(instance), toStream(name), toStream(incomingTypeName)); + return boolToError(handlePvdEvent(evt)); + } + + virtual PvdError appendPropertyValueData(DataRef data) + { + uint32_t realSize = mSPVClass.getNativeSize(); + uint32_t numItems = data.size() / realSize; + data = bufferPropertyValue(mSPVClass.getNativeSizeInfo(), data); + PX_ASSERT(mStreamState == DataStreamState::SetPropertyValue); + return boolToError(handlePvdEvent(AppendPropertyValueData(data, numItems))); + } + virtual PvdError endSetPropertyValue() + { + PX_ASSERT(mStreamState == DataStreamState::SetPropertyValue); + mStreamState = DataStreamState::Open; + return boolToError(handlePvdEvent(EndSetPropertyValue())); + } + +#if PX_DEBUG + + bool checkPropertyMessage(const void* instance, const NamespacedName& msgName) + { + int32_t clsId = mMetaDataProvider.getInstanceClassType(instance); + ScopedMetaData meta(mMetaDataProvider); + PropertyMessageDescription desc(meta->findPropertyMessage(msgName)); + bool retval = meta->isDerivedFrom(clsId, desc.mClassId); + return retval; + } + +#endif + + DataRef bufferPropertyMessage(const PropertyMessageDescription& desc, DataRef data) + { + if(desc.mStringOffsets.size()) + { + mSPVBuffer.clear(); + mSPVBuffer.write(data.begin(), data.size()); + PVD_FOREACH(idx, desc.mStringOffsets.size()) + { + const char* strPtr; + physx::intrinsics::memCopy(&strPtr, data.begin() + desc.mStringOffsets[idx], sizeof(char*)); + strPtr = nonNull(strPtr); + uint32_t len = safeStrLen(strPtr) + 1; + mSPVBuffer.write(strPtr, len); + } + data = DataRef(mSPVBuffer.begin(), mSPVBuffer.end()); + } + return data; + } + + virtual PvdError setPropertyMessage(const void* instance, const NamespacedName& msgName, DataRef data) + { + ScopedMetaData meta(mMetaDataProvider); + PX_ASSERT(isInstanceValid(instance)); +#if PX_DEBUG + PX_ASSERT(messageExists(msgName)); + PX_ASSERT(checkPropertyMessage(instance, msgName)); +#endif + PropertyMessageDescription desc(meta->findPropertyMessage(msgName)); + if(data.size() < desc.mMessageByteSize) + { + PX_ASSERT(false); + return PvdErrorType::ArgumentError; + } + data = bufferPropertyMessage(desc, data); + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent(SetPropertyMessage(toStream(instance), toStream(msgName), data))); + } + +#if PX_DEBUG + + bool checkBeginPropertyMessageGroup(const NamespacedName& msgName) + { + ScopedMetaData meta(mMetaDataProvider); + PropertyMessageDescription desc(meta->findPropertyMessage(msgName)); + return desc.mStringOffsets.size() == 0; + } + +#endif + // If you need to send of lot of identical messages, this avoids a hashtable lookup per message. + virtual PvdError beginPropertyMessageGroup(const NamespacedName& msgName) + { +#if PX_DEBUG + PX_ASSERT(messageExists(msgName)); + PX_ASSERT(checkBeginPropertyMessageGroup(msgName)); +#endif + PX_ASSERT(mStreamState == DataStreamState::Open); + mStreamState = DataStreamState::PropertyMessageGroup; + ScopedMetaData meta(mMetaDataProvider); + mMessageDesc = meta->findPropertyMessage(msgName); + return boolToError(handlePvdEvent(BeginPropertyMessageGroup(toStream(msgName)))); + } + + virtual PvdError sendPropertyMessageFromGroup(const void* instance, DataRef data) + { + PX_ASSERT(mStreamState == DataStreamState::PropertyMessageGroup); + PX_ASSERT(isInstanceValid(instance)); +#if PX_DEBUG + PX_ASSERT(checkPropertyMessage(instance, mMessageDesc.mMessageName)); +#endif + if(mMessageDesc.mMessageByteSize != data.size()) + { + PX_ASSERT(false); + return PvdErrorType::ArgumentError; + } + if(data.size() < mMessageDesc.mMessageByteSize) + return PvdErrorType::ArgumentError; + data = bufferPropertyMessage(mMessageDesc, data); + return boolToError(handlePvdEvent(SendPropertyMessageFromGroup(toStream(instance), data))); + } + virtual PvdError endPropertyMessageGroup() + { + PX_ASSERT(mStreamState == DataStreamState::PropertyMessageGroup); + mStreamState = DataStreamState::Open; + return boolToError(handlePvdEvent(EndPropertyMessageGroup())); + } + virtual PvdError pushBackObjectRef(const void* instance, String propName, const void* data) + { + PX_ASSERT(isInstanceValid(instance)); + PX_ASSERT(isInstanceValid(data)); + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent(PushBackObjectRef(toStream(instance), toStream(propName), toStream(data)))); + } + virtual PvdError removeObjectRef(const void* instance, String propName, const void* data) + { + PX_ASSERT(isInstanceValid(instance)); + PX_ASSERT(isInstanceValid(data)); + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent(RemoveObjectRef(toStream(instance), toStream(propName), toStream(data)))); + } + // Instance elimination. + virtual PvdError destroyInstance(const void* instance) + { + PX_ASSERT(isInstanceValid(instance)); + PX_ASSERT(mStreamState == DataStreamState::Open); + mMetaDataProvider.destroyInstance(instance); + return boolToError(handlePvdEvent(DestroyInstance(toStream(instance)))); + } + + // Profiling hooks + virtual PvdError beginSection(const void* instance, String name) + { + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent( + BeginSection(toStream(instance), toStream(name), Time::getCurrentCounterValue()))); + } + + virtual PvdError endSection(const void* instance, String name) + { + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent( + EndSection(toStream(instance), toStream(name), Time::getCurrentCounterValue()))); + } + + virtual PvdError originShift(const void* scene, PxVec3 shift) + { + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent(OriginShift(toStream(scene), shift))); + } + + virtual void addProfileZone(void* zone, const char* name) + { + handlePvdEvent(AddProfileZone(toStream(zone), name)); + } + virtual void addProfileZoneEvent(void* zone, const char* name, uint16_t eventId, bool compileTimeEnabled) + { + handlePvdEvent(AddProfileZoneEvent(toStream(zone), name, eventId, compileTimeEnabled)); + } + + // add a variable sized event + void addEvent(const EventSerializeable& evt, PvdCommStreamEventTypes::Enum evtType) + { + MeasureStream measure; + PvdCommStreamEventSink::writeStreamEvent(evt, evtType, measure); + EventGroup evtGroup(measure.mSize, 1, mStreamId, Time::getCurrentCounterValue()); + EventStreamifier streamifier(mTransport.lock()); + evtGroup.serialize(streamifier); + PvdCommStreamEventSink::writeStreamEvent(evt, evtType, mTransport); + mTransport.unlock(); + } + + void setIsTopLevelUIElement(const void* instance, bool topLevel) + { + addEvent(SetIsTopLevel(static_cast(reinterpret_cast(instance)), topLevel), + getCommStreamEventType()); + } + + void sendErrorMessage(uint32_t code, const char* message, const char* file, uint32_t line) + { + addEvent(ErrorMessage(code, message, file, line), getCommStreamEventType()); + } + + void updateCamera(const char* name, const PxVec3& origin, const PxVec3& up, const PxVec3& target) + { + addEvent(SetCamera(name, origin, up, target), getCommStreamEventType()); + } + + template + bool handlePvdEvent(const TEventType& evt) + { + addEvent(evt, getCommStreamEventType()); + return mConnected; + } + + virtual PvdPropertyDefinitionHelper& getPropertyDefinitionHelper() + { + mPropertyDefinitionHelper.clearBufferedData(); + return mPropertyDefinitionHelper; + } + + virtual bool isConnected() + { + return mConnected; + } + + virtual void* allocateMemForCmd(uint32_t length) + { + return mPvdCommandPool.allocate(length); + } + + virtual void pushPvdCommand(PvdCommand& cmd) + { + mPvdCommandArray.pushBack(&cmd); + } + + virtual void flushPvdCommand() + { + uint32_t cmdQueueSize = mPvdCommandArray.size(); + for(uint32_t i = 0; i < cmdQueueSize; i++) + { + if(mPvdCommandArray[i]) + { + // if(mPvdCommandArray[i]->canRun(*this)) + mPvdCommandArray[i]->run(*this); + mPvdCommandArray[i]->~PvdCommand(); + } + } + mPvdCommandArray.clear(); + mPvdCommandPool.clear(); + } + + PX_NOCOPY(PvdOutStream) +}; +} + +PvdDataStream* PvdDataStream::create(PxPvd* pvd) +{ + if(pvd == NULL) + { + getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PvdDataStream::create - pvd must be non-NULL!"); + return NULL; + } + + PvdImpl* pvdImpl = static_cast(pvd); + return PVD_NEW(PvdOutStream)(*pvdImpl->getTransport(), pvdImpl->getMetaDataProvider(), pvdImpl->getNextStreamId()); +} diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp new file mode 100644 index 000000000..ba939b045 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp @@ -0,0 +1,218 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdDefaultFileTransport.h" + +namespace physx +{ +namespace pvdsdk +{ + +PvdDefaultFileTransport::PvdDefaultFileTransport(const char* name) : mConnected(false), mWrittenData(0), mLocked(false) +{ + mFileBuffer = PX_NEW(PsFileBuffer)(name, PxFileBuf::OPEN_WRITE_ONLY); +} + +PvdDefaultFileTransport::~PvdDefaultFileTransport() +{ +} + +bool PvdDefaultFileTransport::connect() +{ + PX_ASSERT(mFileBuffer); + mConnected = mFileBuffer->isOpen(); + return mConnected; +} + +void PvdDefaultFileTransport::disconnect() +{ + mConnected = false; +} + +bool PvdDefaultFileTransport::isConnected() +{ + return mConnected; +} + +bool PvdDefaultFileTransport::write(const uint8_t* inBytes, uint32_t inLength) +{ + PX_ASSERT(mLocked); + PX_ASSERT(mFileBuffer); + if (mConnected) + { + uint32_t len = mFileBuffer->write(inBytes, inLength); + mWrittenData += len; + return len == inLength; + } + else + return false; +} + +PxPvdTransport& PvdDefaultFileTransport::lock() +{ + mMutex.lock(); + PX_ASSERT(!mLocked); + mLocked = true; + return *this; +} + +void PvdDefaultFileTransport::unlock() +{ + PX_ASSERT(mLocked); + mLocked = false; + mMutex.unlock(); +} + +void PvdDefaultFileTransport::flush() +{ +} + +uint64_t PvdDefaultFileTransport::getWrittenDataSize() +{ + return mWrittenData; +} + +void PvdDefaultFileTransport::release() +{ + if (mFileBuffer) + { + mFileBuffer->close(); + delete mFileBuffer; + } + mFileBuffer = NULL; + PX_DELETE(this); +} + +class NullFileTransport : public physx::PxPvdTransport, public physx::shdfnd::UserAllocated +{ + PX_NOCOPY(NullFileTransport) + public: + NullFileTransport(); + virtual ~NullFileTransport(); + + virtual bool connect(); + virtual void disconnect(); + virtual bool isConnected(); + + virtual bool write(const uint8_t* inBytes, uint32_t inLength); + + virtual PxPvdTransport& lock(); + virtual void unlock(); + + virtual void flush(); + + virtual uint64_t getWrittenDataSize(); + + virtual void release(); + + private: + bool mConnected; + uint64_t mWrittenData; + physx::shdfnd::Mutex mMutex; + bool mLocked; // for debug, remove it when finished +}; + +NullFileTransport::NullFileTransport() : mConnected(false), mWrittenData(0), mLocked(false) +{ +} + +NullFileTransport::~NullFileTransport() +{ +} + +bool NullFileTransport::connect() +{ + mConnected = true; + return true; +} + +void NullFileTransport::disconnect() +{ + mConnected = false; +} + +bool NullFileTransport::isConnected() +{ + return mConnected; +} + +bool NullFileTransport::write(const uint8_t* /*inBytes*/, uint32_t inLength) +{ + PX_ASSERT(mLocked); + if(mConnected) + { + uint32_t len = inLength; + mWrittenData += len; + return len == inLength; + } + else + return false; +} + +PxPvdTransport& NullFileTransport::lock() +{ + mMutex.lock(); + PX_ASSERT(!mLocked); + mLocked = true; + return *this; +} + +void NullFileTransport::unlock() +{ + PX_ASSERT(mLocked); + mLocked = false; + mMutex.unlock(); +} + +void NullFileTransport::flush() +{ +} + +uint64_t NullFileTransport::getWrittenDataSize() +{ + return mWrittenData; +} + +void NullFileTransport::release() +{ + PX_DELETE(this); +} + +} // namespace pvdsdk + +PxPvdTransport* PxDefaultPvdFileTransportCreate(const char* name) +{ + if(name) + return PX_NEW(pvdsdk::PvdDefaultFileTransport)(name); + else + return PX_NEW(pvdsdk::NullFileTransport)(); +} + +} // namespace physx + diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h new file mode 100644 index 000000000..e64b4a0a7 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDDEFAULTFILETRANSPORT_H +#define PXPVDSDK_PXPVDDEFAULTFILETRANSPORT_H + +#include "pvd/PxPvdTransport.h" + +#include "PsUserAllocated.h" +#include "PsFileBuffer.h" +#include "PsMutex.h" + +namespace physx +{ +namespace pvdsdk +{ + +class PvdDefaultFileTransport : public physx::PxPvdTransport, public physx::shdfnd::UserAllocated +{ + PX_NOCOPY(PvdDefaultFileTransport) + public: + PvdDefaultFileTransport(const char* name); + virtual ~PvdDefaultFileTransport(); + + virtual bool connect(); + virtual void disconnect(); + virtual bool isConnected(); + + virtual bool write(const uint8_t* inBytes, uint32_t inLength); + + virtual PxPvdTransport& lock(); + virtual void unlock(); + + virtual void flush(); + + virtual uint64_t getWrittenDataSize(); + + virtual void release(); + + private: + physx::PsFileBuffer* mFileBuffer; + bool mConnected; + uint64_t mWrittenData; + physx::shdfnd::Mutex mMutex; + bool mLocked; // for debug, remove it when finished +}; + +} // pvdsdk +} // physx + +#endif // PXPVDSDK_PXPVDDEFAULTFILETRANSPORT_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp new file mode 100644 index 000000000..3ed5d03d7 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp @@ -0,0 +1,134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdDefaultSocketTransport.h" + +namespace physx +{ +namespace pvdsdk +{ +PvdDefaultSocketTransport::PvdDefaultSocketTransport(const char* host, int port, unsigned int timeoutInMilliseconds) +: mHost(host), mPort(uint16_t(port)), mTimeout(timeoutInMilliseconds), mConnected(false), mWrittenData(0) +{ +} + +PvdDefaultSocketTransport::~PvdDefaultSocketTransport() +{ +} + +bool PvdDefaultSocketTransport::connect() +{ + if(mConnected) + return true; + + if(mSocket.connect(mHost, mPort, mTimeout)) + { + mSocket.setBlocking(true); + mConnected = true; + } + return mConnected; +} + +void PvdDefaultSocketTransport::disconnect() +{ + mSocket.flush(); + mSocket.disconnect(); + mConnected = false; +} + +bool PvdDefaultSocketTransport::isConnected() +{ + return mSocket.isConnected(); +} + +bool PvdDefaultSocketTransport::write(const uint8_t* inBytes, uint32_t inLength) +{ + if(mConnected) + { + if(inLength == 0) + return true; + + uint32_t amountWritten = 0; + uint32_t totalWritten = 0; + do + { + // Sockets don't have to write as much as requested, so we need + // to wrap this call in a do/while loop. + // If they don't write any bytes then we consider them disconnected. + amountWritten = mSocket.write(inBytes, inLength); + inLength -= amountWritten; + inBytes += amountWritten; + totalWritten += amountWritten; + } while(inLength && amountWritten); + + if(amountWritten == 0) + return false; + + mWrittenData += totalWritten; + + return true; + } + else + return false; +} + +PxPvdTransport& PvdDefaultSocketTransport::lock() +{ + mMutex.lock(); + return *this; +} + +void PvdDefaultSocketTransport::unlock() +{ + mMutex.unlock(); +} + +void PvdDefaultSocketTransport::flush() +{ + mSocket.flush(); +} + +uint64_t PvdDefaultSocketTransport::getWrittenDataSize() +{ + return mWrittenData; +} + +void PvdDefaultSocketTransport::release() +{ + PX_DELETE(this); +} + +} // namespace pvdsdk + +PxPvdTransport* PxDefaultPvdSocketTransportCreate(const char* host, int port, unsigned int timeoutInMilliseconds) +{ + return PX_NEW(pvdsdk::PvdDefaultSocketTransport)(host, port, timeoutInMilliseconds); +} + +} // namespace physx diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h new file mode 100644 index 000000000..d0d7d4b2a --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDDEFAULTSOCKETTRANSPORT_H +#define PXPVDSDK_PXPVDDEFAULTSOCKETTRANSPORT_H + +#include "pvd/PxPvdTransport.h" + +#include "PsUserAllocated.h" +#include "PsSocket.h" +#include "PsMutex.h" + +namespace physx +{ +namespace pvdsdk +{ +class PvdDefaultSocketTransport : public PxPvdTransport, public shdfnd::UserAllocated +{ + PX_NOCOPY(PvdDefaultSocketTransport) + public: + PvdDefaultSocketTransport(const char* host, int port, unsigned int timeoutInMilliseconds); + virtual ~PvdDefaultSocketTransport(); + + virtual bool connect(); + virtual void disconnect(); + virtual bool isConnected(); + + virtual bool write(const uint8_t* inBytes, uint32_t inLength); + + virtual void flush(); + + virtual PxPvdTransport& lock(); + virtual void unlock(); + + virtual uint64_t getWrittenDataSize(); + + virtual void release(); + + private: + shdfnd::Socket mSocket; + const char* mHost; + uint16_t mPort; + unsigned int mTimeout; + bool mConnected; + uint64_t mWrittenData; + shdfnd::Mutex mMutex; + bool mlocked; +}; + +} // pvdsdk +} // physx + +#endif // PXPVDSDK_PXPVDDEFAULTSOCKETTRANSPORT_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdFoundation.h b/src/PhysX/physx/source/pvd/src/PxPvdFoundation.h new file mode 100644 index 000000000..52feeee57 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdFoundation.h @@ -0,0 +1,318 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDFOUNDATION_H +#define PXPVDSDK_PXPVDFOUNDATION_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxBounds3.h" + +#include "PsArray.h" +#include "PsHashMap.h" +#include "PsHashSet.h" +#include "PsPool.h" +#include "PsString.h" + +#include "PxPvdObjectModelBaseTypes.h" + +namespace physx +{ +namespace pvdsdk +{ + +extern PxAllocatorCallback* gPvdAllocatorCallback; + +class ForwardingAllocator : public PxAllocatorCallback +{ + void* allocate(size_t size, const char* typeName, const char* filename, int line) + { + return shdfnd::getAllocator().allocate(size, typeName, filename, line); + } + void deallocate(void* ptr) + { + shdfnd::getAllocator().deallocate(ptr); + } +}; + +class RawMemoryBuffer +{ + uint8_t* mBegin; + uint8_t* mEnd; + uint8_t* mCapacityEnd; + const char* mBufDataName; + + public: + RawMemoryBuffer(const char* name) : mBegin(0), mEnd(0), mCapacityEnd(0),mBufDataName(name) + { + PX_UNUSED(mBufDataName); + } + ~RawMemoryBuffer() + { + if(mBegin) + PX_FREE(mBegin); + } + uint32_t size() const + { + return static_cast(mEnd - mBegin); + } + uint32_t capacity() const + { + return static_cast(mCapacityEnd - mBegin); + } + uint8_t* begin() + { + return mBegin; + } + uint8_t* end() + { + return mEnd; + } + const uint8_t* begin() const + { + return mBegin; + } + const uint8_t* end() const + { + return mEnd; + } + void clear() + { + mEnd = mBegin; + } + const char* cStr() + { + if(mEnd && (*mEnd != 0)) + write(0); + return reinterpret_cast(mBegin); + } + uint32_t write(uint8_t inValue) + { + *growBuf(1) = inValue; + return 1; + } + + template + uint32_t write(const TDataType& inValue) + { + const uint8_t* __restrict readPtr = reinterpret_cast(&inValue); + uint8_t* __restrict writePtr = growBuf(sizeof(TDataType)); + for(uint32_t idx = 0; idx < sizeof(TDataType); ++idx) + writePtr[idx] = readPtr[idx]; + return sizeof(TDataType); + } + + template + uint32_t write(const TDataType* inValue, uint32_t inLength) + { + uint32_t writeSize = inLength * sizeof(TDataType); + if(inValue && inLength) + { + physx::intrinsics::memCopy(growBuf(writeSize), inValue, writeSize); + } + if(inLength && !inValue) + { + PX_ASSERT(false); + // You can't not write something, because that will cause + // the receiving end to crash. + for(uint32_t idx = 0; idx < writeSize; ++idx) + write(0); + } + return writeSize; + } + + uint8_t* growBuf(uint32_t inAmount) + { + uint32_t offset = size(); + uint32_t newSize = offset + inAmount; + reserve(newSize); + mEnd += inAmount; + return mBegin + offset; + } + void writeZeros(uint32_t inAmount) + { + uint32_t offset = size(); + growBuf(inAmount); + physx::intrinsics::memZero(begin() + offset, inAmount); + } + void reserve(uint32_t newSize) + { + uint32_t currentSize = size(); + if(newSize && newSize >= capacity()) + { + uint32_t newDataSize = newSize > 4096 ? newSize + (newSize >> 2) : newSize*2; + uint8_t* newData = static_cast(PX_ALLOC(newDataSize, mBufDataName)); + if(mBegin) + { + physx::intrinsics::memCopy(newData, mBegin, currentSize); + PX_FREE(mBegin); + } + mBegin = newData; + mEnd = mBegin + currentSize; + mCapacityEnd = mBegin + newDataSize; + } + } +}; + +struct ForwardingMemoryBuffer : public RawMemoryBuffer +{ + ForwardingMemoryBuffer(const char* bufDataName) : RawMemoryBuffer(bufDataName) + { + } + + ForwardingMemoryBuffer& operator<<(const char* inString) + { + if(inString && *inString) + { + uint32_t len = static_cast(strlen(inString)); + write(inString, len); + } + return *this; + } + + template + inline ForwardingMemoryBuffer& toStream(const char* inFormat, const TDataType inData) + { + char buffer[128] = { 0 }; + shdfnd::snprintf(buffer, 128, inFormat, inData); + *this << buffer; + return *this; + } + + inline ForwardingMemoryBuffer& operator<<(bool inData) + { + *this << (inData ? "true" : "false"); + return *this; + } + inline ForwardingMemoryBuffer& operator<<(int32_t inData) + { + return toStream("%d", inData); + } + inline ForwardingMemoryBuffer& operator<<(uint16_t inData) + { + return toStream("%u", uint32_t(inData)); + } + inline ForwardingMemoryBuffer& operator<<(uint8_t inData) + { + return toStream("%u", uint32_t(inData)); + } + inline ForwardingMemoryBuffer& operator<<(char inData) + { + return toStream("%c", inData); + } + inline ForwardingMemoryBuffer& operator<<(uint32_t inData) + { + return toStream("%u", inData); + } + inline ForwardingMemoryBuffer& operator<<(uint64_t inData) + { + return toStream("%I64u", inData); + } + inline ForwardingMemoryBuffer& operator<<(int64_t inData) + { + return toStream("%I64d", inData); + } + inline ForwardingMemoryBuffer& operator<<(const void* inData) + { + return *this << static_cast(reinterpret_cast(inData)); + } + inline ForwardingMemoryBuffer& operator<<(float inData) + { + return toStream("%g", double(inData)); + } + inline ForwardingMemoryBuffer& operator<<(double inData) + { + return toStream("%g", inData); + } + inline ForwardingMemoryBuffer& operator<<(const PxVec3& inData) + { + *this << inData[0]; + *this << " "; + *this << inData[1]; + *this << " "; + *this << inData[2]; + return *this; + } + + inline ForwardingMemoryBuffer& operator<<(const PxQuat& inData) + { + *this << inData.x; + *this << " "; + *this << inData.y; + *this << " "; + *this << inData.z; + *this << " "; + *this << inData.w; + return *this; + } + + inline ForwardingMemoryBuffer& operator<<(const PxTransform& inData) + { + *this << inData.q; + *this << " "; + *this << inData.p; + return *this; + } + + inline ForwardingMemoryBuffer& operator<<(const PxBounds3& inData) + { + *this << inData.minimum; + *this << " "; + *this << inData.maximum; + return *this; + } + +}; + +template +inline void* PvdAllocate(const char* typeName, const char* file, int line) +{ + PX_ASSERT(gPvdAllocatorCallback); + return gPvdAllocatorCallback->allocate(sizeof(TDataType), typeName, file, line); +} + +template +inline void PvdDeleteAndDeallocate(TDataType* inDType) +{ + PX_ASSERT(gPvdAllocatorCallback); + if(inDType) + { + inDType->~TDataType(); + gPvdAllocatorCallback->deallocate(inDType); + } +} +} +} + +#define PVD_NEW(dtype) new (PvdAllocate(#dtype, __FILE__, __LINE__)) dtype +#define PVD_DELETE(obj) PvdDeleteAndDeallocate(obj); +//#define PVD_NEW(dtype) PX_NEW(dtype) +//#define PVD_DELETE(obj) PX_DELETE(obj) +#define PVD_FOREACH(varname, stop) for(uint32_t varname = 0; varname < stop; ++varname) +#define PVD_POINTER_TO_U64(ptr) static_cast(reinterpret_cast(ptr)) + +#endif // PXPVDSDK_PXPVDFOUNDATION_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp b/src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp new file mode 100644 index 000000000..d6374584e --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp @@ -0,0 +1,399 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdImpl.h" +#include "PxPvdMemClient.h" +#include "PxPvdProfileZoneClient.h" +#include "PxPvdProfileZone.h" + +#include "PsFoundation.h" + +#if PX_NVTX +#include "nvToolsExt.h" +#endif + +namespace +{ + const char* gSdkName = "PhysXSDK"; +} + +namespace physx +{ +namespace pvdsdk +{ + +class CmEventNameProvider : public physx::profile::PxProfileNameProvider +{ +public: + physx::profile::PxProfileNames getProfileNames() const + { + physx::profile::PxProfileNames ret; + ret.eventCount = 0; + return ret; + } +}; + +CmEventNameProvider gProfileNameProvider; + +void initializeModelTypes(PvdDataStream& stream) +{ + stream.createClass(); + stream.createProperty( + "events", PvdCommStreamEmbeddedTypes::getProfileEventStreamSemantic(), PropertyType::Array); + + stream.createClass(); + stream.createProperty( + "events", PvdCommStreamEmbeddedTypes::getMemoryEventStreamSemantic(), PropertyType::Array); + + stream.createClass(); + stream.createProperty( + "events", PvdCommStreamEmbeddedTypes::getRendererEventStreamSemantic(), PropertyType::Array); +} + +PvdImpl* PvdImpl::sInstance = NULL; +uint32_t PvdImpl::sRefCount = 0; + +PvdImpl::PvdImpl() +: mPvdTransport(NULL) +, mSharedMetaProvider(NULL) +, mMemClient(NULL) +, mIsConnected(false) +, mIsNVTXSupportEnabled(true) +, mNVTXContext(0) +, mNextStreamId(1) +, mProfileClient(NULL) +, mProfileZone(NULL) +{ + mProfileZoneManager = &physx::profile::PxProfileZoneManager::createProfileZoneManager(&physx::shdfnd::getAllocator()); + mProfileClient = PVD_NEW(PvdProfileZoneClient)(*this); +} + +PvdImpl::~PvdImpl() +{ + if((mFlags & PxPvdInstrumentationFlag::ePROFILE) ) + { + PxSetProfilerCallback(NULL); + } + + disconnect(); + + if ( mProfileZoneManager ) + { + mProfileZoneManager->release(); + mProfileZoneManager = NULL; + } + + PVD_DELETE(mProfileClient); + mProfileClient = NULL; +} + +bool PvdImpl::connect(PxPvdTransport& transport, PxPvdInstrumentationFlags flags) +{ + if(mIsConnected) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxPvd::connect - recall connect! Should call disconnect before re-connect."); + return false; + } + + mFlags = flags; + mPvdTransport = &transport; + + mIsConnected = mPvdTransport->connect(); + + if(mIsConnected) + { + mSharedMetaProvider = PVD_NEW(MetaDataProvider); + sendTransportInitialization(); + + PvdDataStream* stream = PvdDataStream::create(this); + initializeModelTypes(*stream); + stream->release(); + + if(mFlags & PxPvdInstrumentationFlag::eMEMORY) + { + mMemClient = PVD_NEW(PvdMemClient)(*this); + mPvdClients.pushBack(mMemClient); + } + + if((mFlags & PxPvdInstrumentationFlag::ePROFILE) && mProfileZoneManager) + { + mPvdClients.pushBack(mProfileClient); + mProfileZone = &physx::profile::PxProfileZone::createProfileZone(&physx::shdfnd::getAllocator(),gSdkName,gProfileNameProvider.getProfileNames()); + } + + for(uint32_t i = 0; i < mPvdClients.size(); i++) + mPvdClients[i]->onPvdConnected(); + + if (mProfileZone) + { + mProfileZoneManager->addProfileZoneHandler(*mProfileClient); + mProfileZoneManager->addProfileZone( *mProfileZone ); + } + + if ((mFlags & PxPvdInstrumentationFlag::ePROFILE)) + { + PxSetProfilerCallback(this); + } + } + return mIsConnected; +} + +void PvdImpl::disconnect() +{ + if(mProfileZone) + { + mProfileZoneManager->removeProfileZoneHandler(*mProfileClient); + mProfileZoneManager->removeProfileZone( *mProfileZone ); + mProfileZone->release(); + mProfileZone=NULL; + removeClient(mProfileClient); + } + + if(mIsConnected) + { + for(uint32_t i = 0; i < mPvdClients.size(); i++) + mPvdClients[i]->onPvdDisconnected(); + + if(mMemClient) + { + removeClient(mMemClient); + PvdMemClient* tmp = mMemClient; //avoid tracking deallocation itsself + mMemClient = NULL; + PVD_DELETE(tmp); + } + + mSharedMetaProvider->release(); + mPvdTransport->disconnect(); + mObjectRegistrar.clear(); + mIsConnected = false; + } +} + +void PvdImpl::flush() +{ + for(uint32_t i = 0; i < mPvdClients.size(); i++) + mPvdClients[i]->flush(); + if ( mProfileZone ) + { + mProfileZone->flushEventIdNameMap(); + mProfileZone->flushProfileEvents(); + } +} + +bool PvdImpl::isConnected(bool useCachedStatus) +{ + if(mPvdTransport) + return useCachedStatus ? mIsConnected : mPvdTransport->isConnected(); + else + return false; +} + +PxPvdTransport* PvdImpl::getTransport() +{ + return mPvdTransport; +} + +PxPvdInstrumentationFlags PvdImpl::getInstrumentationFlags() +{ + return mFlags; +} + +void PvdImpl::sendTransportInitialization() +{ + StreamInitialization init; + EventStreamifier stream(mPvdTransport->lock()); + init.serialize(stream); + mPvdTransport->unlock(); +} + +void PvdImpl::addClient(PvdClient* client) +{ + PX_ASSERT(client); + for(uint32_t i = 0; i < mPvdClients.size(); i++) + { + if(client == mPvdClients[i]) + return; + } + mPvdClients.pushBack(client); + if(mIsConnected) + { + client->onPvdConnected(); + } +} + +void PvdImpl::removeClient(PvdClient* client) +{ + for(uint32_t i = 0; i < mPvdClients.size(); i++) + { + if(client == mPvdClients[i]) + { + client->onPvdDisconnected(); + mPvdClients.remove(i); + } + } +} + +void PvdImpl::onAllocation(size_t inSize, const char* inType, const char* inFile, int inLine, void* inAddr) +{ + if(mMemClient) + mMemClient->onAllocation(inSize, inType, inFile, inLine, inAddr); +} + +void PvdImpl::onDeallocation(void* inAddr) +{ + if(mMemClient) + mMemClient->onDeallocation(inAddr); +} + +PvdOMMetaDataProvider& PvdImpl::getMetaDataProvider() +{ + return *mSharedMetaProvider; +} + +bool PvdImpl::registerObject(const void* inItem) +{ + return mObjectRegistrar.addItem(inItem); +} + + +bool PvdImpl::unRegisterObject(const void* inItem) +{ + return mObjectRegistrar.decItem(inItem); +} + +uint64_t PvdImpl::getNextStreamId() +{ + uint64_t retval = ++mNextStreamId; + return retval; +} + +bool PvdImpl::initialize() +{ + if(0 == sRefCount) + { + sInstance = PVD_NEW(PvdImpl)(); + } + ++sRefCount; + return !!sInstance; +} + +void PvdImpl::release() +{ + if(sRefCount > 0) + { + if(--sRefCount) + return; + + PVD_DELETE(sInstance); + sInstance = NULL; + } +} + +PvdImpl* PvdImpl::getInstance() +{ + return sInstance; +} + + +/************************************************************************************************************************** +Instrumented profiling events +***************************************************************************************************************************/ + +static const uint32_t CrossThreadId = 99999789; + +void* PvdImpl::zoneStart(const char* eventName, bool detached, uint64_t contextId) +{ + if(mProfileZone) + { + const uint16_t id = mProfileZone->getEventIdForName(eventName); + if(detached) + mProfileZone->startEvent(id, contextId, CrossThreadId); + else + mProfileZone->startEvent(id, contextId); + } +#if PX_NVTX + if(mIsNVTXSupportEnabled) + { + if(detached) + { + // TODO : Need to use the nvtxRangeStart API for cross thread events + nvtxEventAttributes_t eventAttrib; + memset(&eventAttrib, 0, sizeof(eventAttrib)); + eventAttrib.version = NVTX_VERSION; + eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; + eventAttrib.colorType = NVTX_COLOR_ARGB; + eventAttrib.color = 0xFF00FF00; + eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII; + eventAttrib.message.ascii = eventName; + nvtxMarkEx(&eventAttrib); + } + else + { + nvtxRangePush(eventName); + } + } +#endif + return NULL; +} + +void PvdImpl::zoneEnd(void* /*profilerData*/, const char* eventName, bool detached, uint64_t contextId) +{ + if(mProfileZone) + { + const uint16_t id = mProfileZone->getEventIdForName(eventName); + if(detached) + mProfileZone->stopEvent(id, contextId, CrossThreadId); + else + mProfileZone->stopEvent(id, contextId); + } +#if PX_NVTX + if(mIsNVTXSupportEnabled) + { + if(detached) + { + nvtxEventAttributes_t eventAttrib; + memset(&eventAttrib, 0, sizeof(eventAttrib)); + eventAttrib.version = NVTX_VERSION; + eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; + eventAttrib.colorType = NVTX_COLOR_ARGB; + eventAttrib.color = 0xFFFF0000; + eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII; + eventAttrib.message.ascii = eventName; + nvtxMarkEx(&eventAttrib); + } + else + { + nvtxRangePop(); + } + } +#endif +} +} // pvd + +} // physx diff --git a/src/PhysX/physx/source/pvd/src/PxPvdImpl.h b/src/PhysX/physx/source/pvd/src/PxPvdImpl.h new file mode 100644 index 000000000..ab19343d9 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdImpl.h @@ -0,0 +1,221 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDIMPL_H +#define PXPVDSDK_PXPVDIMPL_H + +#include "foundation/PxProfiler.h" + +#include "PsAllocator.h" +#include "PsPvd.h" +#include "PsArray.h" +#include "PsMutex.h" +#include "PxPvdCommStreamTypes.h" +#include "PxPvdFoundation.h" +#include "PxPvdObjectModelMetaData.h" +#include "PxPvdObjectRegistrar.h" + +namespace physx +{ + +namespace profile +{ + class PxProfileZoneManager; +} + +namespace pvdsdk +{ +class PvdMemClient; +class PvdProfileZoneClient; + +struct MetaDataProvider : public PvdOMMetaDataProvider, public shdfnd::UserAllocated +{ + typedef shdfnd::Mutex::ScopedLock TScopedLockType; + typedef shdfnd::HashMap TInstTypeMap; + PvdObjectModelMetaData& mMetaData; + shdfnd::Mutex mMutex; + uint32_t mRefCount; + TInstTypeMap mTypeMap; + + MetaDataProvider() + : mMetaData(PvdObjectModelMetaData::create()), mRefCount(0), mTypeMap("MetaDataProvider::mTypeMap") + { + mMetaData.addRef(); + } + virtual ~MetaDataProvider() + { + mMetaData.release(); + } + + virtual void addRef() + { + TScopedLockType locker(mMutex); + ++mRefCount; + } + virtual void release() + { + { + TScopedLockType locker(mMutex); + if(mRefCount) + --mRefCount; + } + if(!mRefCount) + PVD_DELETE(this); + } + virtual PvdObjectModelMetaData& lock() + { + mMutex.lock(); + return mMetaData; + } + virtual void unlock() + { + mMutex.unlock(); + } + + virtual bool createInstance(const NamespacedName& clsName, const void* instance) + { + TScopedLockType locker(mMutex); + Option cls(mMetaData.findClass(clsName)); + if(cls.hasValue() == false) + return false; + int32_t instType = cls->mClassId; + mTypeMap.insert(instance, instType); + return true; + } + virtual bool isInstanceValid(const void* instance) + { + TScopedLockType locker(mMutex); + ClassDescription classDesc; + bool retval = mTypeMap.find(instance) != NULL; +#if PX_DEBUG + if(retval) + classDesc = mMetaData.getClass(mTypeMap.find(instance)->second); +#endif + return retval; + } + virtual void destroyInstance(const void* instance) + { + { + TScopedLockType locker(mMutex); + mTypeMap.erase(instance); + } + } + virtual int32_t getInstanceClassType(const void* instance) + { + TScopedLockType locker(mMutex); + const TInstTypeMap::Entry* entry = mTypeMap.find(instance); + if(entry) + return entry->second; + return -1; + } + + private: + MetaDataProvider& operator=(const MetaDataProvider&); + MetaDataProvider(const MetaDataProvider&); +}; + +////////////////////////////////////////////////////////////////////////// +/*! +PvdImpl is the realization of PxPvd. +It implements the interface methods and provides richer functionality for advanced users or internal clients (such as +PhysX or APEX), including handler notification for clients. +*/ +////////////////////////////////////////////////////////////////////////// +class PvdImpl : public PsPvd, public shdfnd::UserAllocated +{ + PX_NOCOPY(PvdImpl) + + typedef shdfnd::Mutex::ScopedLock TScopedLockType; + typedef void (PvdImpl::*TAllocationHandler)(size_t size, const char* typeName, const char* filename, int line, + void* allocatedMemory); + typedef void (PvdImpl::*TDeallocationHandler)(void* allocatedMemory); + + public: + PvdImpl(); + virtual ~PvdImpl(); + void release(); + + bool connect(PxPvdTransport& transport, PxPvdInstrumentationFlags flags); + void disconnect(); + bool isConnected(bool useCachedStatus = true); + void flush(); + + PxPvdTransport* getTransport(); + PxPvdInstrumentationFlags getInstrumentationFlags(); + + void addClient(PvdClient* client); + void removeClient(PvdClient* client); + + PvdOMMetaDataProvider& getMetaDataProvider(); + + bool registerObject(const void* inItem); + bool unRegisterObject(const void* inItem); + + //AllocationListener + void onAllocation(size_t size, const char* typeName, const char* filename, int line, void* allocatedMemory); + void onDeallocation(void* addr); + + uint64_t getNextStreamId(); + + static bool initialize(); + static PvdImpl* getInstance(); + + // Profiling + + virtual void* zoneStart(const char* eventName, bool detached, uint64_t contextId); + + virtual void zoneEnd(void* profilerData, const char *eventName, bool detached, uint64_t contextId); + + private: + void sendTransportInitialization(); + + PxPvdTransport* mPvdTransport; + physx::shdfnd::Array mPvdClients; + + MetaDataProvider* mSharedMetaProvider; // shared between clients + ObjectRegistrar mObjectRegistrar; + + PvdMemClient* mMemClient; + + PxPvdInstrumentationFlags mFlags; + bool mIsConnected; + bool mIsNVTXSupportEnabled; + uint32_t mNVTXContext; + uint64_t mNextStreamId; + physx::profile::PxProfileZoneManager*mProfileZoneManager; + PvdProfileZoneClient* mProfileClient; + physx::profile::PxProfileZone* mProfileZone; + static PvdImpl* sInstance; + static uint32_t sRefCount; +}; + +} // namespace pvdsdk +} + +#endif // PXPVDSDK_PXPVDIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h b/src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h new file mode 100644 index 000000000..71bdcedd1 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDINTERNALBYTESTREAMS_H +#define PXPVDSDK_PXPVDINTERNALBYTESTREAMS_H + +#include "PxPvdByteStreams.h" +#include "PxPvdFoundation.h" + +namespace physx +{ +namespace pvdsdk +{ +struct MemPvdInputStream : public PvdInputStream +{ + const uint8_t* mBegin; + const uint8_t* mEnd; + bool mGood; + + MemPvdInputStream(const uint8_t* beg = NULL, const uint8_t* end = NULL) + { + mBegin = beg; + mEnd = end; + mGood = true; + } + + uint32_t size() const + { + return mGood ? static_cast(mEnd - mBegin) : 0; + } + bool isGood() const + { + return mGood; + } + + void setup(uint8_t* start, uint8_t* stop) + { + mBegin = start; + mEnd = stop; + } + + void nocopyRead(uint8_t*& buffer, uint32_t& len) + { + if(len == 0 || mGood == false) + { + len = 0; + buffer = NULL; + return; + } + uint32_t original = len; + len = PxMin(len, size()); + if(mGood && len != original) + mGood = false; + buffer = const_cast(mBegin); + mBegin += len; + } + + virtual bool read(uint8_t* buffer, uint32_t& len) + { + if(len == 0) + return true; + uint32_t original = len; + len = PxMin(len, size()); + + physx::intrinsics::memCopy(buffer, mBegin, len); + mBegin += len; + if(len < original) + physx::intrinsics::memZero(buffer + len, original - len); + mGood = mGood && len == original; + return mGood; + } +}; +} +} +#endif // PXPVDSDK_PXPVDINTERNALBYTESTREAMS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h b/src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h new file mode 100644 index 000000000..8c80ddc27 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h @@ -0,0 +1,220 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDMARSHALLING_H +#define PXPVDSDK_PXPVDMARSHALLING_H + +#include "foundation/PxIntrinsics.h" + +#include "PxPvdObjectModelBaseTypes.h" +#include "PxPvdBits.h" + +namespace physx +{ +namespace pvdsdk +{ + +// Define marshalling + +template +struct PvdMarshalling +{ + bool canMarshal; + PvdMarshalling() : canMarshal(false) + { + } +}; + +template +static inline void marshalSingleT(const uint8_t* srcData, uint8_t* destData) +{ + smtype incoming; + + physx::intrinsics::memCopy(&incoming, srcData, sizeof(smtype)); + lgtype outgoing = static_cast(incoming); + physx::intrinsics::memCopy(destData, &outgoing, sizeof(lgtype)); +} + +template +static inline void marshalBlockT(const uint8_t* srcData, uint8_t* destData, uint32_t numBytes) +{ + for(const uint8_t* item = srcData, *end = srcData + numBytes; item < end; + item += sizeof(smtype), destData += sizeof(lgtype)) + marshalSingleT(item, destData); +} + +#define PVD_TYPE_MARSHALLER(smtype, lgtype) \ + template <> \ + struct PvdMarshalling \ + { \ + uint32_t canMarshal; \ + static void marshalSingle(const uint8_t* srcData, uint8_t* destData) \ + { \ + marshalSingleT(srcData, destData); \ + } \ + static void marshalBlock(const uint8_t* srcData, uint8_t* destData, uint32_t numBytes) \ + { \ + marshalBlockT(srcData, destData, numBytes); \ + } \ + }; + +// define marshalling tables. +PVD_TYPE_MARSHALLER(int8_t, int16_t) +PVD_TYPE_MARSHALLER(int8_t, uint16_t) +PVD_TYPE_MARSHALLER(int8_t, int32_t) +PVD_TYPE_MARSHALLER(int8_t, uint32_t) +PVD_TYPE_MARSHALLER(int8_t, int64_t) +PVD_TYPE_MARSHALLER(int8_t, uint64_t) +PVD_TYPE_MARSHALLER(int8_t, PvdF32) +PVD_TYPE_MARSHALLER(int8_t, PvdF64) + +PVD_TYPE_MARSHALLER(uint8_t, int16_t) +PVD_TYPE_MARSHALLER(uint8_t, uint16_t) +PVD_TYPE_MARSHALLER(uint8_t, int32_t) +PVD_TYPE_MARSHALLER(uint8_t, uint32_t) +PVD_TYPE_MARSHALLER(uint8_t, int64_t) +PVD_TYPE_MARSHALLER(uint8_t, uint64_t) +PVD_TYPE_MARSHALLER(uint8_t, PvdF32) +PVD_TYPE_MARSHALLER(uint8_t, PvdF64) + +PVD_TYPE_MARSHALLER(int16_t, int32_t) +PVD_TYPE_MARSHALLER(int16_t, uint32_t) +PVD_TYPE_MARSHALLER(int16_t, int64_t) +PVD_TYPE_MARSHALLER(int16_t, uint64_t) +PVD_TYPE_MARSHALLER(int16_t, PvdF32) +PVD_TYPE_MARSHALLER(int16_t, PvdF64) + +PVD_TYPE_MARSHALLER(uint16_t, int32_t) +PVD_TYPE_MARSHALLER(uint16_t, uint32_t) +PVD_TYPE_MARSHALLER(uint16_t, int64_t) +PVD_TYPE_MARSHALLER(uint16_t, uint64_t) +PVD_TYPE_MARSHALLER(uint16_t, PvdF32) +PVD_TYPE_MARSHALLER(uint16_t, PvdF64) + +PVD_TYPE_MARSHALLER(int32_t, int64_t) +PVD_TYPE_MARSHALLER(int32_t, uint64_t) +PVD_TYPE_MARSHALLER(int32_t, PvdF64) +PVD_TYPE_MARSHALLER(int32_t, PvdF32) + +PVD_TYPE_MARSHALLER(uint32_t, int64_t) +PVD_TYPE_MARSHALLER(uint32_t, uint64_t) +PVD_TYPE_MARSHALLER(uint32_t, PvdF64) +PVD_TYPE_MARSHALLER(uint32_t, PvdF32) + +PVD_TYPE_MARSHALLER(PvdF32, PvdF64) +PVD_TYPE_MARSHALLER(PvdF32, uint32_t) +PVD_TYPE_MARSHALLER(PvdF32, int32_t) + +PVD_TYPE_MARSHALLER(uint64_t, PvdF64) +PVD_TYPE_MARSHALLER(int64_t, PvdF64) +PVD_TYPE_MARSHALLER(PvdF64, uint64_t) +PVD_TYPE_MARSHALLER(PvdF64, int64_t) + +template +static inline bool getMarshalOperators(TSingleMarshaller&, TBlockMarshaller&, TMarshaller&, bool) +{ + return false; +} + +template +static inline bool getMarshalOperators(TSingleMarshaller& single, TBlockMarshaller& block, TMarshaller&, uint32_t) +{ + single = TMarshaller::marshalSingle; + block = TMarshaller::marshalBlock; + return true; +} + +template +static inline bool getMarshalOperators(TSingleMarshaller& single, TBlockMarshaller& block) +{ + single = NULL; + block = NULL; + PvdMarshalling marshaller = PvdMarshalling(); + return getMarshalOperators(single, block, marshaller, marshaller.canMarshal); +} + +template +static inline bool getMarshalOperators(TSingleMarshaller& single, TBlockMarshaller& block, int32_t lgtypeId) +{ + switch(lgtypeId) + { + case PvdBaseType::PvdI8: // int8_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdU8: // uint8_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdI16: // int16_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdU16: // uint16_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdI32: // int32_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdU32: // uint32_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdI64: // int64_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdU64: // uint64_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdF32: + return getMarshalOperators(single, block); + case PvdBaseType::PvdF64: + return getMarshalOperators(single, block); + } + return false; +} + +static inline bool getMarshalOperators(TSingleMarshaller& single, TBlockMarshaller& block, int32_t smtypeId, + int32_t lgtypeId) +{ + switch(smtypeId) + { + case PvdBaseType::PvdI8: // int8_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdU8: // uint8_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdI16: // int16_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdU16: // uint16_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdI32: // int32_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdU32: // uint32_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdI64: // int64_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdU64: // uint64_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdF32: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdF64: + return getMarshalOperators(single, block, lgtypeId); + } + return false; +} +} +} + +#endif // PXPVDSDK_PXPVDMARSHALLING_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp new file mode 100644 index 000000000..f6e201982 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdImpl.h" +#include "PxPvdMemClient.h" + +namespace physx +{ +namespace pvdsdk +{ + +PvdMemClient::PvdMemClient(PvdImpl& pvd) +: mSDKPvd(pvd) +, mPvdDataStream(NULL) +, mIsConnected(false) +, mMemEventBuffer(profile::PxProfileMemoryEventBuffer::createMemoryEventBuffer(*gPvdAllocatorCallback)) +{ +} + +PvdMemClient::~PvdMemClient() +{ + mSDKPvd.removeClient(this); + if(mMemEventBuffer.hasClients()) + mPvdDataStream->destroyInstance(&mMemEventBuffer); + mMemEventBuffer.release(); +} + +PvdDataStream* PvdMemClient::getDataStream() +{ + return mPvdDataStream; +} + +PvdUserRenderer* PvdMemClient::getUserRender() +{ + PX_ASSERT(0); + return NULL; +} + +void PvdMemClient::setObjectRegistrar(ObjectRegistrar*) +{ +} + +bool PvdMemClient::isConnected() const +{ + return mIsConnected; +} + +void PvdMemClient::onPvdConnected() +{ + if(mIsConnected) + return; + mIsConnected = true; + + mPvdDataStream = PvdDataStream::create(&mSDKPvd); + mPvdDataStream->createInstance(&mMemEventBuffer); + mMemEventBuffer.addClient(*this); +} + +void PvdMemClient::onPvdDisconnected() +{ + if(!mIsConnected) + return; + mIsConnected = false; + + flush(); + + mMemEventBuffer.removeClient(*this); + mPvdDataStream->release(); + mPvdDataStream = NULL; +} + +void PvdMemClient::onAllocation(size_t inSize, const char* inType, const char* inFile, int inLine, void* inAddr) +{ + mMutex.lock(); + mMemEventBuffer.onAllocation(inSize, inType, inFile, inLine, inAddr); + mMutex.unlock(); +} + +void PvdMemClient::onDeallocation(void* inAddr) +{ + mMutex.lock(); + mMemEventBuffer.onDeallocation(inAddr); + mMutex.unlock(); +} + +void PvdMemClient::flush() +{ + mMutex.lock(); + mMemEventBuffer.flushProfileEvents(); + mMutex.unlock(); +} + +void PvdMemClient::handleBufferFlush(const uint8_t* inData, uint32_t inLength) +{ + if(mPvdDataStream) + mPvdDataStream->setPropertyValue(&mMemEventBuffer, "events", inData, inLength); +} + +void PvdMemClient::handleClientRemoved() +{ +} + +} // pvd +} // physx diff --git a/src/PhysX/physx/source/pvd/src/PxPvdMemClient.h b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.h new file mode 100644 index 000000000..de4c202e8 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.h @@ -0,0 +1,85 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDMEMCLIENT_H +#define PXPVDSDK_PXPVDMEMCLIENT_H + +#include "PxPvdClient.h" +#include "PsHashMap.h" +#include "PsMutex.h" +#include "PsBroadcast.h" +#include "PxProfileEventBufferClient.h" +#include "PxProfileMemory.h" + +namespace physx +{ +class PvdDataStream; + +namespace pvdsdk +{ +class PvdImpl; +class PvdMemClient : public PvdClient, + public profile::PxProfileEventBufferClient, + public shdfnd::UserAllocated +{ + PX_NOCOPY(PvdMemClient) + public: + PvdMemClient(PvdImpl& pvd); + virtual ~PvdMemClient(); + + bool isConnected() const; + void onPvdConnected(); + void onPvdDisconnected(); + void flush(); + + PvdDataStream* getDataStream(); + PvdUserRenderer* getUserRender(); + void setObjectRegistrar(ObjectRegistrar*); + void sendMemEvents(); + + // memory event + void onAllocation(size_t size, const char* typeName, const char* filename, int line, void* allocatedMemory); + void onDeallocation(void* addr); + + private: + PvdImpl& mSDKPvd; + PvdDataStream* mPvdDataStream; + bool mIsConnected; + + // mem profile + shdfnd::Mutex mMutex; // mem onallocation can called from different threads + profile::PxProfileMemoryEventBuffer& mMemEventBuffer; + void handleBufferFlush(const uint8_t* inData, uint32_t inLength); + void handleClientRemoved(); +}; + +} // namespace pvdsdk +} // namespace physx + +#endif // PXPVDSDK_PXPVDMEMCLIENT_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h new file mode 100644 index 000000000..b273ca0f6 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h @@ -0,0 +1,32 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +DECLARE_INTERNAL_PVD_TYPE(ArrayData) + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h new file mode 100644 index 000000000..bf9b6870b --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h @@ -0,0 +1,154 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDOBJECTMODELINTERNALTYPES_H +#define PXPVDSDK_PXPVDOBJECTMODELINTERNALTYPES_H + +#include "foundation/PxMemory.h" +#include "PxPvdObjectModelBaseTypes.h" +#include "PsArray.h" +#include "PxPvdFoundation.h" + +namespace physx +{ +namespace pvdsdk +{ + +struct PvdInternalType +{ + enum Enum + { + None = 0, +#define DECLARE_INTERNAL_PVD_TYPE(type) type, +#include "PxPvdObjectModelInternalTypeDefs.h" + Last +#undef DECLARE_INTERNAL_PVD_TYPE + }; +}; + +PX_COMPILE_TIME_ASSERT(uint32_t(PvdInternalType::Last) <= uint32_t(PvdBaseType::InternalStop)); + +template +struct DataTypeToPvdTypeMap +{ + bool compile_error; +}; +template +struct PvdTypeToDataTypeMap +{ + bool compile_error; +}; + +#define DECLARE_INTERNAL_PVD_TYPE(type) \ + template <> \ + struct DataTypeToPvdTypeMap \ + { \ + enum Enum \ + { \ + BaseTypeEnum = PvdInternalType::type \ + }; \ + }; \ + template <> \ + struct PvdTypeToDataTypeMap \ + { \ + typedef type TDataType; \ + }; \ + template <> \ + struct PvdDataTypeToNamespacedNameMap \ + { \ + NamespacedName Name; \ + PvdDataTypeToNamespacedNameMap() : Name("physx3_debugger_internal", #type) \ + { \ + } \ + }; +#include "PxPvdObjectModelInternalTypeDefs.h" +#undef DECLARE_INTERNAL_PVD_TYPE + +template +DataRef toDataRef(const shdfnd::Array& data) +{ + return DataRef(data.begin(), data.end()); +} + +static inline bool safeStrEq(const DataRef& lhs, const DataRef& rhs) +{ + uint32_t count = lhs.size(); + if(count != rhs.size()) + return false; + for(uint32_t idx = 0; idx < count; ++idx) + if(!safeStrEq(lhs[idx], rhs[idx])) + return false; + return true; +} + +static inline char* copyStr(const char* str) +{ + str = nonNull(str); + uint32_t len = static_cast(strlen(str)); + char* newData = reinterpret_cast(PX_ALLOC(len + 1, "string")); + PxMemCopy(newData, str, len); + newData[len] = 0; + return newData; +} + +// Used for predictable bit fields. +template +struct BitMaskSetter +{ + // Create a mask that masks out the orginal value shift into place + static TDataType createOffsetMask() + { + return createMask() << TOffset; + } + // Create a mask of TNumBits number of tis + static TDataType createMask() + { + return static_cast((1 << TNumBits) - 1); + } + void setValue(TDataType& inCurrent, TInputType inData) + { + PX_ASSERT(inData < (1 << TNumBits)); + + // Create a mask to remove the current value. + TDataType theMask = ~(createOffsetMask()); + // Clear out current value. + inCurrent = inCurrent & theMask; + // Create the new value. + TDataType theAddition = reinterpret_cast(inData << TOffset); + // or it into the existing value. + inCurrent = inCurrent | theAddition; + } + + TInputType getValue(TDataType inCurrent) + { + return static_cast((inCurrent >> TOffset) & createMask()); + } +}; + +} +} +#endif // PXPVDSDK_PXPVDOBJECTMODELINTERNALTYPES_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp new file mode 100644 index 000000000..6d5635240 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp @@ -0,0 +1,1503 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxPvdObjectModelInternalTypes.h" +#include "PxPvdObjectModelMetaData.h" +#include "PxPvdInternalByteStreams.h" +#include "PxPvdMarshalling.h" + +using namespace physx; +using namespace pvdsdk; +using namespace shdfnd; + +namespace +{ + +struct PropDescImpl : public PropertyDescription, public UserAllocated +{ + Array mValueNames; + PropDescImpl(const PropertyDescription& inBase, StringTable& table) + : PropertyDescription(inBase), mValueNames("NamedValue") + { + mName = table.registerStr(mName); + } + PropDescImpl() : mValueNames("NamedValue") + { + } + + template + void serialize(TSerializer& serializer) + { + serializer.streamify(mOwnerClassName); + serializer.streamify(mOwnerClassId); + serializer.streamify(mSemantic); + serializer.streamify(mDatatype); + serializer.streamify(mDatatypeName); + serializer.streamify(mPropertyType); + serializer.streamify(mPropertyId); + serializer.streamify(m32BitOffset); + serializer.streamify(m64BitOffset); + serializer.streamify(mValueNames); + serializer.streamify(mName); + } +}; + +struct ClassDescImpl : public ClassDescription, public UserAllocated +{ + Array mPropImps; + Array m32OffsetArray; + Array m64OffsetArray; + ClassDescImpl(const ClassDescription& inBase) + : ClassDescription(inBase) + , mPropImps("PropDescImpl*") + , m32OffsetArray("ClassDescImpl::m32OffsetArray") + , m64OffsetArray("ClassDescImpl::m64OffsetArray") + { + PVD_FOREACH(idx, get32BitSizeInfo().mPtrOffsets.size()) + m32OffsetArray.pushBack(get32BitSizeInfo().mPtrOffsets[idx]); + PVD_FOREACH(idx, get64BitSizeInfo().mPtrOffsets.size()) + m64OffsetArray.pushBack(get64BitSizeInfo().mPtrOffsets[idx]); + } + ClassDescImpl() + : mPropImps("PropDescImpl*") + , m32OffsetArray("ClassDescImpl::m32OffsetArray") + , m64OffsetArray("ClassDescImpl::m64OffsetArray") + { + } + PropDescImpl* findProperty(String name) + { + PVD_FOREACH(idx, mPropImps.size()) + { + if(safeStrEq(mPropImps[idx]->mName, name)) + return mPropImps[idx]; + } + return NULL; + } + void addProperty(PropDescImpl* prop) + { + mPropImps.pushBack(prop); + } + + void addPtrOffset(PtrOffsetType::Enum type, uint32_t offset32, uint32_t offset64) + { + m32OffsetArray.pushBack(PtrOffset(type, offset32)); + m64OffsetArray.pushBack(PtrOffset(type, offset64)); + get32BitSizeInfo().mPtrOffsets = DataRef(m32OffsetArray.begin(), m32OffsetArray.end()); + get64BitSizeInfo().mPtrOffsets = DataRef(m64OffsetArray.begin(), m64OffsetArray.end()); + } + + template + void serialize(TSerializer& serializer) + { + serializer.streamify(mName); + serializer.streamify(mClassId); + serializer.streamify(mBaseClass); + serializer.streamify(mPackedUniformWidth); + serializer.streamify(mPackedClassType); + serializer.streamify(mLocked); + serializer.streamify(mRequiresDestruction); + serializer.streamify(get32BitSize()); + serializer.streamify(get32BitSizeInfo().mDataByteSize); + serializer.streamify(get32BitSizeInfo().mAlignment); + serializer.streamify(get64BitSize()); + serializer.streamify(get64BitSizeInfo().mDataByteSize); + serializer.streamify(get64BitSizeInfo().mAlignment); + serializer.streamifyLinks(mPropImps); + serializer.streamify(m32OffsetArray); + serializer.streamify(m64OffsetArray); + get32BitSizeInfo().mPtrOffsets = DataRef(m32OffsetArray.begin(), m32OffsetArray.end()); + get64BitSizeInfo().mPtrOffsets = DataRef(m64OffsetArray.begin(), m64OffsetArray.end()); + } +}; + +class StringTableImpl : public StringTable, public UserAllocated +{ + HashMap mStrings; + uint32_t mNextStrHandle; + HashMap mHandleToStr; + HashMap mStrToHandle; + + public: + StringTableImpl() + : mStrings("StringTableImpl::mStrings") + , mNextStrHandle(1) + , mHandleToStr("StringTableImpl::mHandleToStr") + , mStrToHandle("StringTableImpl::mStrToHandle") + { + } + uint32_t nextHandleValue() + { + return mNextStrHandle++; + } + virtual ~StringTableImpl() + { + for(HashMap::Iterator iter = mStrings.getIterator(); !iter.done(); ++iter) + PX_FREE(iter->second); + mStrings.clear(); + } + virtual uint32_t getNbStrs() + { + return mStrings.size(); + } + virtual uint32_t getStrs(const char** outStrs, uint32_t bufLen, uint32_t startIdx = 0) + { + startIdx = PxMin(getNbStrs(), startIdx); + uint32_t numStrs(PxMin(getNbStrs() - startIdx, bufLen)); + HashMap::Iterator iter(mStrings.getIterator()); + for(uint32_t idx = 0; idx < startIdx; ++idx, ++iter) + ; + for(uint32_t idx = 0; idx < numStrs && !iter.done(); ++idx, ++iter) + outStrs[idx] = iter->second; + return numStrs; + } + void addStringHandle(char* str, uint32_t hdl) + { + mHandleToStr.insert(hdl, str); + mStrToHandle.insert(str, hdl); + } + + uint32_t addStringHandle(char* str) + { + uint32_t theNewHandle = nextHandleValue(); + addStringHandle(str, theNewHandle); + return theNewHandle; + } + const char* doRegisterStr(const char* str, bool& outAdded) + { + PX_ASSERT(isMeaningful(str)); + const HashMap::Entry* entry(mStrings.find(str)); + if(entry == NULL) + { + outAdded = true; + char* retval(copyStr(str)); + mStrings.insert(retval, retval); + return retval; + } + return entry->second; + } + virtual const char* registerStr(const char* str, bool& outAdded) + { + outAdded = false; + if(isMeaningful(str) == false) + return ""; + const char* retval = doRegisterStr(str, outAdded); + if(outAdded) + addStringHandle(const_cast(retval)); + return retval; + } + + NamespacedName registerName(const NamespacedName& nm) + { + return NamespacedName(registerStr(nm.mNamespace), registerStr(nm.mName)); + } + const char* registerStr(const char* str) + { + bool ignored; + return registerStr(str, ignored); + } + + virtual StringHandle strToHandle(const char* str) + { + if(isMeaningful(str) == false) + return 0; + const HashMap::Entry* entry(mStrToHandle.find(str)); + if(entry) + return entry->second; + bool added = false; + const char* registeredStr = doRegisterStr(str, added); + uint32_t theNewHandle = addStringHandle(const_cast(registeredStr)); + PX_ASSERT(mStrToHandle.find(str)); + PX_ASSERT(added); + return theNewHandle; + } + + virtual const char* handleToStr(uint32_t hdl) + { + if(hdl == 0) + return ""; + const HashMap::Entry* entry(mHandleToStr.find(hdl)); + if(entry) + return entry->second; + // unregistered handle... + return ""; + } + + void write(PvdOutputStream& stream) + { + uint32_t numStrs = static_cast(mHandleToStr.size()); + stream << numStrs; + stream << mNextStrHandle; + for(HashMap::Iterator iter = mHandleToStr.getIterator(); !iter.done(); ++iter) + { + stream << iter->first; + uint32_t len = static_cast(strlen(iter->second) + 1); + stream << len; + stream.write(reinterpret_cast(iter->second), len); + } + } + + template + void read(TReader& stream) + { + mHandleToStr.clear(); + mStrToHandle.clear(); + uint32_t numStrs; + stream >> numStrs; + stream >> mNextStrHandle; + Array readBuffer("StringTable::read::readBuffer"); + uint32_t bufSize = 0; + for(uint32_t idx = 0; idx < numStrs; ++idx) + { + uint32_t handleValue; + uint32_t bufLen; + stream >> handleValue; + stream >> bufLen; + if(bufSize < bufLen) + readBuffer.resize(bufLen); + bufSize = PxMax(bufSize, bufLen); + stream.read(readBuffer.begin(), bufLen); + bool ignored; + const char* newStr = doRegisterStr(reinterpret_cast(readBuffer.begin()), ignored); + addStringHandle(const_cast(newStr), handleValue); + } + } + + virtual void release() + { + PVD_DELETE(this); + } + + private: + StringTableImpl& operator=(const StringTableImpl&); +}; + +struct NamespacedNameHasher +{ + uint32_t operator()(const NamespacedName& nm) + { + return Hash()(nm.mNamespace) ^ Hash()(nm.mName); + } + bool equal(const NamespacedName& lhs, const NamespacedName& rhs) + { + return safeStrEq(lhs.mNamespace, rhs.mNamespace) && safeStrEq(lhs.mName, rhs.mName); + } +}; + +struct ClassPropertyName +{ + NamespacedName mName; + String mPropName; + ClassPropertyName(const NamespacedName& name = NamespacedName(), String propName = "") + : mName(name), mPropName(propName) + { + } +}; + +struct ClassPropertyNameHasher +{ + uint32_t operator()(const ClassPropertyName& nm) + { + return NamespacedNameHasher()(nm.mName) ^ Hash()(nm.mPropName); + } + bool equal(const ClassPropertyName& lhs, const ClassPropertyName& rhs) + { + return NamespacedNameHasher().equal(lhs.mName, rhs.mName) && safeStrEq(lhs.mPropName, rhs.mPropName); + } +}; + +struct PropertyMessageEntryImpl : public PropertyMessageEntry +{ + PropertyMessageEntryImpl(const PropertyMessageEntry& data) : PropertyMessageEntry(data) + { + } + PropertyMessageEntryImpl() + { + } + template + void serialize(TSerializerType& serializer) + { + serializer.streamify(mDatatypeName); + serializer.streamify(mDatatypeId); + serializer.streamify(mMessageOffset); + serializer.streamify(mByteSize); + serializer.streamify(mDestByteSize); + serializer.streamify(mProperty); + } +}; + +struct PropertyMessageDescriptionImpl : public PropertyMessageDescription, public UserAllocated +{ + Array mEntryImpls; + Array mEntries; + Array mStringOffsetArray; + PropertyMessageDescriptionImpl(const PropertyMessageDescription& data) + : PropertyMessageDescription(data) + , mEntryImpls("PropertyMessageDescriptionImpl::mEntryImpls") + , mEntries("PropertyMessageDescriptionImpl::mEntries") + , mStringOffsetArray("PropertyMessageDescriptionImpl::mStringOffsets") + { + } + PropertyMessageDescriptionImpl() + : mEntryImpls("PropertyMessageDescriptionImpl::mEntryImpls") + , mEntries("PropertyMessageDescriptionImpl::mEntries") + , mStringOffsetArray("PropertyMessageDescriptionImpl::mStringOffsets") + { + } + + ~PropertyMessageDescriptionImpl() + { + } + + void addEntry(const PropertyMessageEntryImpl& entry) + { + mEntryImpls.pushBack(entry); + mEntries.pushBack(entry); + mProperties = DataRef(mEntries.begin(), mEntries.end()); + } + + template + void serialize(TSerializerType& serializer) + { + serializer.streamify(mClassName); + serializer.streamify(mClassId); // No other class has this id, it is DB-unique + serializer.streamify(mMessageName); + serializer.streamify(mMessageId); + serializer.streamify(mMessageByteSize); + serializer.streamify(mEntryImpls); + serializer.streamify(mStringOffsetArray); + if(mEntries.size() != mEntryImpls.size()) + { + mEntries.clear(); + uint32_t numEntries = static_cast(mEntryImpls.size()); + for(uint32_t idx = 0; idx < numEntries; ++idx) + mEntries.pushBack(mEntryImpls[idx]); + } + mProperties = DataRef(mEntries.begin(), mEntries.end()); + mStringOffsets = DataRef(mStringOffsetArray.begin(), mStringOffsetArray.end()); + } + + private: + PropertyMessageDescriptionImpl& operator=(const PropertyMessageDescriptionImpl&); +}; + +struct PvdObjectModelMetaDataImpl : public PvdObjectModelMetaData, public UserAllocated +{ + typedef HashMap TNameToClassMap; + typedef HashMap TNameToPropMap; + typedef HashMap TNameToPropertyMessageMap; + + TNameToClassMap mNameToClasses; + TNameToPropMap mNameToProperties; + Array mClasses; + Array mProperties; + StringTableImpl* mStringTable; + TNameToPropertyMessageMap mPropertyMessageMap; + Array mPropertyMessages; + int32_t mNextClassId; + uint32_t mRefCount; + + PvdObjectModelMetaDataImpl() + : mNameToClasses("NamespacedName->ClassDescImpl*") + , mNameToProperties("ClassPropertyName->PropDescImpl*") + , mClasses("ClassDescImpl*") + , mProperties("PropDescImpl*") + , mStringTable(PVD_NEW(StringTableImpl)()) + , mPropertyMessageMap("PropertyMessageMap") + , mPropertyMessages("PvdObjectModelMetaDataImpl::mPropertyMessages") + , mNextClassId(1) + , mRefCount(0) + { + } + + private: + PvdObjectModelMetaDataImpl& operator=(const PvdObjectModelMetaDataImpl&); + + public: + int32_t nextClassId() + { + return mNextClassId++; + } + void initialize() + { + // Create the default classes. + { + ClassDescImpl& aryData = getOrCreateClassImpl(getPvdNamespacedNameForType(), + DataTypeToPvdTypeMap::BaseTypeEnum); + aryData.get32BitSize() = sizeof(ArrayData); + aryData.get32BitSizeInfo().mAlignment = sizeof(void*); + aryData.get64BitSize() = sizeof(ArrayData); + aryData.get64BitSizeInfo().mAlignment = sizeof(void*); + aryData.mLocked = true; + } +#define CREATE_BASIC_PVD_CLASS(type) \ + { \ + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); \ + cls.get32BitSize() = sizeof(type); \ + cls.get32BitSizeInfo().mAlignment = sizeof(type); \ + cls.get64BitSize() = sizeof(type); \ + cls.get64BitSizeInfo().mAlignment = sizeof(type); \ + cls.mLocked = true; \ + cls.mPackedUniformWidth = sizeof(type); \ + cls.mPackedClassType = getPvdTypeForType(); \ + } + CREATE_BASIC_PVD_CLASS(int8_t) + CREATE_BASIC_PVD_CLASS(uint8_t) + CREATE_BASIC_PVD_CLASS(bool) + CREATE_BASIC_PVD_CLASS(int16_t) + CREATE_BASIC_PVD_CLASS(uint16_t) + CREATE_BASIC_PVD_CLASS(int32_t) + CREATE_BASIC_PVD_CLASS(uint32_t) + // CREATE_BASIC_PVD_CLASS(uint32_t) + CREATE_BASIC_PVD_CLASS(int64_t) + CREATE_BASIC_PVD_CLASS(uint64_t) + CREATE_BASIC_PVD_CLASS(float) + CREATE_BASIC_PVD_CLASS(double) +#undef CREATE_BASIC_PVD_CLASS + +#define CREATE_PTR_TYPE_PVD_CLASS(type, ptrType) \ + { \ + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); \ + cls.get32BitSize() = 4; \ + cls.get32BitSizeInfo().mAlignment = 4; \ + cls.get64BitSize() = 8; \ + cls.get64BitSizeInfo().mAlignment = 8; \ + cls.mLocked = true; \ + cls.addPtrOffset(PtrOffsetType::ptrType, 0, 0); \ + } + + CREATE_PTR_TYPE_PVD_CLASS(String, StringOffset) + CREATE_PTR_TYPE_PVD_CLASS(VoidPtr, VoidPtrOffset) + CREATE_PTR_TYPE_PVD_CLASS(StringHandle, StringOffset) + CREATE_PTR_TYPE_PVD_CLASS(ObjectRef, VoidPtrOffset) + +#undef CREATE_64BIT_ADJUST_PVD_CLASS + + int32_t fltClassType = getPvdTypeForType(); + int32_t u32ClassType = getPvdTypeForType(); + int32_t v3ClassType = getPvdTypeForType(); + int32_t v4ClassType = getPvdTypeForType(); + int32_t qtClassType = getPvdTypeForType(); + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "r", "", getPvdTypeForType(), PropertyType::Scalar); + createProperty(cls.mClassId, "g", "", getPvdTypeForType(), PropertyType::Scalar); + createProperty(cls.mClassId, "b", "", getPvdTypeForType(), PropertyType::Scalar); + createProperty(cls.mClassId, "a", "", getPvdTypeForType(), PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 1); + PX_ASSERT(cls.get32BitSize() == 4); + PX_ASSERT(cls.get64BitSizeInfo().mAlignment == 1); + PX_ASSERT(cls.get64BitSize() == 4); + PX_ASSERT(cls.mPackedUniformWidth == 1); + PX_ASSERT(cls.mPackedClassType == getPvdTypeForType()); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "x", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "y", "", fltClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 8); + PX_ASSERT(cls.get64BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get64BitSize() == 8); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + { + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "x", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "y", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "z", "", fltClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 12); + PX_ASSERT(cls.get64BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get64BitSize() == 12); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + { + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "x", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "y", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "z", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "w", "", fltClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 16); + PX_ASSERT(cls.get64BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get64BitSize() == 16); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "x", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "y", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "z", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "w", "", fltClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 16); + PX_ASSERT(cls.get64BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get64BitSize() == 16); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "minimum", "", v3ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "maximum", "", v3ClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 24); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "q", "", qtClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "p", "", v3ClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 28); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "column0", "", v3ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "column1", "", v3ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "column2", "", v3ClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 36); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "column0", "", v4ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "column1", "", v4ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "column2", "", v4ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "column3", "", v4ClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 64); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "d0", "", u32ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "d1", "", u32ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "d2", "", u32ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "d3", "", u32ClassType, PropertyType::Scalar); + cls.mLocked = true; + } + } + virtual ~PvdObjectModelMetaDataImpl() + { + mStringTable->release(); + PVD_FOREACH(idx, mClasses.size()) + { + if(mClasses[idx] != NULL) + PVD_DELETE(mClasses[idx]); + } + mClasses.clear(); + PVD_FOREACH(idx, mProperties.size()) PVD_DELETE(mProperties[idx]); + mProperties.clear(); + PVD_FOREACH(idx, mPropertyMessages.size()) PVD_DELETE(mPropertyMessages[idx]); + mPropertyMessages.clear(); + } + + ClassDescImpl& getOrCreateClassImpl(const NamespacedName& nm, int32_t idx) + { + ClassDescImpl* impl(getClassImpl(idx)); + if(impl) + return *impl; + NamespacedName safeName(mStringTable->registerStr(nm.mNamespace), mStringTable->registerStr(nm.mName)); + while(idx >= int32_t(mClasses.size())) + mClasses.pushBack(NULL); + mClasses[uint32_t(idx)] = PVD_NEW(ClassDescImpl)(ClassDescription(safeName, idx)); + mNameToClasses.insert(nm, mClasses[uint32_t(idx)]); + mNextClassId = PxMax(mNextClassId, idx + 1); + return *mClasses[uint32_t(idx)]; + } + + ClassDescImpl& getOrCreateClassImpl(const NamespacedName& nm) + { + ClassDescImpl* retval = findClassImpl(nm); + if(retval) + return *retval; + return getOrCreateClassImpl(nm, nextClassId()); + } + virtual ClassDescription getOrCreateClass(const NamespacedName& nm) + { + return getOrCreateClassImpl(nm); + } + // get or create parent, lock parent. deriveFrom getOrCreatechild. + virtual bool deriveClass(const NamespacedName& parent, const NamespacedName& child) + { + ClassDescImpl& p(getOrCreateClassImpl(parent)); + ClassDescImpl& c(getOrCreateClassImpl(child)); + + if(c.mBaseClass >= 0) + { + PX_ASSERT(c.mBaseClass == p.mClassId); + return false; + } + p.mLocked = true; + c.mBaseClass = p.mClassId; + c.get32BitSizeInfo() = p.get32BitSizeInfo(); + c.get64BitSizeInfo() = p.get64BitSizeInfo(); + c.mPackedClassType = p.mPackedClassType; + c.mPackedUniformWidth = p.mPackedUniformWidth; + c.mRequiresDestruction = p.mRequiresDestruction; + c.m32OffsetArray = p.m32OffsetArray; + c.m64OffsetArray = p.m64OffsetArray; + // Add all the parent propertes to this class in the global name map. + for(ClassDescImpl* parent0 = &p; parent0 != NULL; parent0 = getClassImpl(parent0->mBaseClass)) + { + PVD_FOREACH(idx, parent0->mPropImps.size()) + mNameToProperties.insert(ClassPropertyName(c.mName, parent0->mPropImps[idx]->mName), parent0->mPropImps[idx]); + + if(parent0->mBaseClass < 0) + break; + } + + return true; + } + ClassDescImpl* findClassImpl(const NamespacedName& nm) const + { + const TNameToClassMap::Entry* entry(mNameToClasses.find(nm)); + if(entry) + return entry->second; + return NULL; + } + virtual Option findClass(const NamespacedName& nm) const + { + ClassDescImpl* retval = findClassImpl(nm); + if(retval) + return *retval; + return Option(); + } + + ClassDescImpl* getClassImpl(int32_t classId) const + { + if(classId < 0) + return NULL; + uint32_t idx = uint32_t(classId); + if(idx < mClasses.size()) + return mClasses[idx]; + return NULL; + } + + virtual Option getClass(int32_t classId) const + { + ClassDescImpl* impl(getClassImpl(classId)); + if(impl) + return *impl; + return None(); + } + + virtual ClassDescription* getClassPtr(int32_t classId) const + { + return getClassImpl(classId); + } + + virtual Option getParentClass(int32_t classId) const + { + ClassDescImpl* impl(getClassImpl(classId)); + if(impl == NULL) + return None(); + return getClass(impl->mBaseClass); + } + + virtual void lockClass(int32_t classId) + { + ClassDescImpl* impl(getClassImpl(classId)); + PX_ASSERT(impl); + if(impl) + impl->mLocked = true; + } + virtual uint32_t getNbClasses() const + { + uint32_t total = 0; + PVD_FOREACH(idx, mClasses.size()) if(mClasses[idx])++ total; + return total; + } + + virtual uint32_t getClasses(ClassDescription* outClasses, uint32_t requestCount, uint32_t startIndex = 0) const + { + uint32_t classCount(getNbClasses()); + startIndex = PxMin(classCount, startIndex); + uint32_t retAmount = PxMin(requestCount, classCount - startIndex); + + uint32_t idx = 0; + while(startIndex) + { + if(mClasses[idx] != NULL) + --startIndex; + ++idx; + } + + uint32_t inserted = 0; + uint32_t classesSize = static_cast(mClasses.size()); + while(inserted < retAmount && idx < classesSize) + { + if(mClasses[idx] != NULL) + { + outClasses[inserted] = *mClasses[idx]; + ++inserted; + } + ++idx; + } + return inserted; + } + + uint32_t updateByteSizeAndGetPropertyAlignment(ClassDescriptionSizeInfo& dest, const ClassDescriptionSizeInfo& src) + { + uint32_t alignment = src.mAlignment; + dest.mAlignment = PxMax(dest.mAlignment, alignment); + uint32_t offset = align(dest.mDataByteSize, alignment); + dest.mDataByteSize = offset + src.mByteSize; + dest.mByteSize = align(dest.mDataByteSize, dest.mAlignment); + return offset; + } + + void transferPtrOffsets(ClassDescriptionSizeInfo& destInfo, Array& destArray, + const Array& src, uint32_t offset) + { + PVD_FOREACH(idx, src.size()) + destArray.pushBack(PtrOffset(src[idx].mOffsetType, src[idx].mOffset + offset)); + destInfo.mPtrOffsets = DataRef(destArray.begin(), destArray.end()); + } + + virtual Option createProperty(int32_t classId, String name, String semantic, int32_t datatype, + PropertyType::Enum propertyType) + { + ClassDescImpl* cls(getClassImpl(classId)); + PX_ASSERT(cls); + if(!cls) + return None(); + if(cls->mLocked) + { + PX_ASSERT(false); + return None(); + } + PropDescImpl* impl(cls->findProperty(name)); + // duplicate property definition + if(impl) + { + PX_ASSERT(false); + return None(); + } + if(datatype == getPvdTypeForType()) + { + PX_ASSERT(false); + return None(); + } + // The datatype for this property has not been declared. + ClassDescImpl* propDType(getClassImpl(datatype)); + PX_ASSERT(propDType); + if(!propDType) + return None(); + NamespacedName propClsName(propDType->mName); + int32_t propPackedWidth = propDType->mPackedUniformWidth; + int32_t propPackedType = propDType->mPackedClassType; + // The implications of properties being complex types aren't major + //*until* you start trying to undue a property event that set values + // of those complex types. Then things just get too complex. + if(propDType->mRequiresDestruction) + { + PX_ASSERT(false); + return None(); + } + bool requiresDestruction = propDType->mRequiresDestruction || cls->mRequiresDestruction; + + if(propertyType == PropertyType::Array) + { + int32_t tempId = DataTypeToPvdTypeMap::BaseTypeEnum; + propDType = getClassImpl(tempId); + PX_ASSERT(propDType); + if(!propDType) + return None(); + requiresDestruction = true; + } + uint32_t offset32 = updateByteSizeAndGetPropertyAlignment(cls->get32BitSizeInfo(), propDType->get32BitSizeInfo()); + uint32_t offset64 = updateByteSizeAndGetPropertyAlignment(cls->get64BitSizeInfo(), propDType->get64BitSizeInfo()); + transferPtrOffsets(cls->get32BitSizeInfo(), cls->m32OffsetArray, propDType->m32OffsetArray, offset32); + transferPtrOffsets(cls->get64BitSizeInfo(), cls->m64OffsetArray, propDType->m64OffsetArray, offset64); + propDType->mLocked = true; // Can't add members to the property type. + cls->mRequiresDestruction = requiresDestruction; + int32_t propId = int32_t(mProperties.size()); + PropertyDescription newDesc(cls->mName, cls->mClassId, name, semantic, datatype, propClsName, propertyType, + propId, offset32, offset64); + mProperties.pushBack(PVD_NEW(PropDescImpl)(newDesc, *mStringTable)); + mNameToProperties.insert(ClassPropertyName(cls->mName, mProperties.back()->mName), mProperties.back()); + cls->addProperty(mProperties.back()); + bool firstProp = cls->mPropImps.size() == 1; + + if(firstProp) + { + cls->mPackedUniformWidth = propPackedWidth; + cls->mPackedClassType = propPackedType; + } + else + { + bool packed = (propPackedWidth > 0) && (cls->get32BitSizeInfo().mDataByteSize % propPackedWidth) == 0; + if(cls->mPackedClassType >= 0) // maybe uncheck packed class type + { + if(propPackedType < 0 || cls->mPackedClassType != propPackedType + // Object refs require conversion from stream to db id + || + datatype == getPvdTypeForType() + // Strings also require conversion from stream to db id. + || + datatype == getPvdTypeForType() || packed == false) + cls->mPackedClassType = -1; + } + if(cls->mPackedUniformWidth >= 0) // maybe uncheck packed class width + { + if(propPackedWidth < 0 || cls->mPackedUniformWidth != propPackedWidth + // object refs, because they require special treatment during parsing, + // cannot be packed + || + datatype == getPvdTypeForType() + // Likewise, string handles are special because the data needs to be sent *after* + // the + || + datatype == getPvdTypeForType() || packed == false) + cls->mPackedUniformWidth = -1; // invalid packed width. + } + } + return *mProperties.back(); + } + + PropDescImpl* findPropImpl(const NamespacedName& clsName, String prop) const + { + const TNameToPropMap::Entry* entry = mNameToProperties.find(ClassPropertyName(clsName, prop)); + if(entry) + return entry->second; + return NULL; + } + virtual Option findProperty(const NamespacedName& cls, String propName) const + { + PropDescImpl* prop(findPropImpl(cls, propName)); + if(prop) + return *prop; + return None(); + } + + virtual Option findProperty(int32_t clsId, String propName) const + { + ClassDescImpl* cls(getClassImpl(clsId)); + PX_ASSERT(cls); + if(!cls) + return None(); + PropDescImpl* prop(findPropImpl(cls->mName, propName)); + if(prop) + return *prop; + return None(); + } + + PropDescImpl* getPropertyImpl(int32_t propId) const + { + PX_ASSERT(propId >= 0); + if(propId < 0) + return NULL; + uint32_t val = uint32_t(propId); + if(val >= mProperties.size()) + { + PX_ASSERT(false); + return NULL; + } + return mProperties[val]; + } + + virtual Option getProperty(int32_t propId) const + { + PropDescImpl* impl(getPropertyImpl(propId)); + if(impl) + return *impl; + return None(); + } + + virtual void setNamedPropertyValues(DataRef values, int32_t propId) + { + PropDescImpl* impl(getPropertyImpl(propId)); + if(impl) + { + impl->mValueNames.resize(values.size()); + PVD_FOREACH(idx, values.size()) impl->mValueNames[idx] = values[idx]; + } + } + + virtual DataRef getNamedPropertyValues(int32_t propId) const + { + PropDescImpl* impl(getPropertyImpl(propId)); + if(impl) + { + return toDataRef(impl->mValueNames); + } + return DataRef(); + } + + virtual uint32_t getNbProperties(int32_t classId) const + { + uint32_t retval = 0; + for(ClassDescImpl* impl(getClassImpl(classId)); impl; impl = getClassImpl(impl->mBaseClass)) + { + retval += impl->mPropImps.size(); + if(impl->mBaseClass < 0) + break; + } + return retval; + } + + // Properties need to be returned in base class order, so this requires a recursive function. + uint32_t getPropertiesImpl(int32_t classId, PropertyDescription*& outBuffer, uint32_t& numItems, + uint32_t& startIdx) const + { + ClassDescImpl* impl = getClassImpl(classId); + if(impl) + { + uint32_t retval = 0; + if(impl->mBaseClass >= 0) + retval = getPropertiesImpl(impl->mBaseClass, outBuffer, numItems, startIdx); + + uint32_t localStart = PxMin(impl->mPropImps.size(), startIdx); + uint32_t localNumItems = PxMin(numItems, impl->mPropImps.size() - localStart); + PVD_FOREACH(idx, localNumItems) + { + outBuffer[idx] = *impl->mPropImps[localStart + idx]; + } + + startIdx -= localStart; + numItems -= localNumItems; + outBuffer += localNumItems; + return retval + localNumItems; + } + return 0; + } + + virtual uint32_t getProperties(int32_t classId, PropertyDescription* outBuffer, uint32_t numItems, + uint32_t startIdx) const + { + return getPropertiesImpl(classId, outBuffer, numItems, startIdx); + } + + virtual MarshalQueryResult checkMarshalling(int32_t srcClsId, int32_t dstClsId) const + { + Option propTypeOpt(getClass(dstClsId)); + if(propTypeOpt.hasValue() == false) + { + PX_ASSERT(false); + return MarshalQueryResult(); + } + const ClassDescription& propType(propTypeOpt); + + Option incomingTypeOpt(getClass(srcClsId)); + if(incomingTypeOpt.hasValue() == false) + { + PX_ASSERT(false); + return MarshalQueryResult(); + } + const ClassDescription& incomingType(incomingTypeOpt); + // Can only marshal simple things at this point in time. + bool needsMarshalling = false; + bool canMarshal = false; + TSingleMarshaller single = NULL; + TBlockMarshaller block = NULL; + if(incomingType.mClassId != propType.mClassId) + { + // Check that marshalling is even possible. + if((incomingType.mPackedUniformWidth >= 0 && propType.mPackedUniformWidth >= 0) == false) + { + PX_ASSERT(false); + return MarshalQueryResult(); + } + + int32_t srcType = incomingType.mPackedClassType; + int32_t dstType = propType.mPackedClassType; + + int32_t srcWidth = incomingType.mPackedUniformWidth; + int32_t dstWidth = propType.mPackedUniformWidth; + canMarshal = getMarshalOperators(single, block, srcType, dstType); + if(srcWidth == dstWidth) + needsMarshalling = canMarshal; // If the types are the same width, we assume we can convert between some + // of them seamlessly (uint16_t, int16_t) + else + { + needsMarshalling = true; + // If we can't marshall and we have to then we can't set the property value. + // This indicates that the src and dest are different properties and we don't + // know how to convert between them. + if(!canMarshal) + { + PX_ASSERT(false); + return MarshalQueryResult(); + } + } + } + return MarshalQueryResult(srcClsId, dstClsId, canMarshal, needsMarshalling, block); + } + + PropertyMessageDescriptionImpl* findPropertyMessageImpl(const NamespacedName& messageName) const + { + const TNameToPropertyMessageMap::Entry* entry = mPropertyMessageMap.find(messageName); + if(entry) + return entry->second; + return NULL; + } + + PropertyMessageDescriptionImpl* getPropertyMessageImpl(int32_t msg) const + { + int32_t msgCount = int32_t(mPropertyMessages.size()); + if(msg >= 0 && msg < msgCount) + return mPropertyMessages[uint32_t(msg)]; + return NULL; + } + + virtual Option createPropertyMessage(const NamespacedName& clsName, + const NamespacedName& messageName, + DataRef entries, + uint32_t messageSize) + { + PropertyMessageDescriptionImpl* existing(findPropertyMessageImpl(messageName)); + if(existing) + { + PX_ASSERT(false); + return None(); + } + ClassDescImpl* cls = findClassImpl(clsName); + PX_ASSERT(cls); + if(!cls) + return None(); + int32_t msgId = int32_t(mPropertyMessages.size()); + PropertyMessageDescriptionImpl* newMessage = PVD_NEW(PropertyMessageDescriptionImpl)( + PropertyMessageDescription(mStringTable->registerName(clsName), cls->mClassId, + mStringTable->registerName(messageName), msgId, messageSize)); + uint32_t calculatedSize = 0; + PVD_FOREACH(idx, entries.size()) + { + PropertyMessageArg entry(entries[idx]); + ClassDescImpl* dtypeCls = findClassImpl(entry.mDatatypeName); + if(dtypeCls == NULL) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + ClassDescriptionSizeInfo dtypeInfo(dtypeCls->get32BitSizeInfo()); + uint32_t incomingSize = dtypeInfo.mByteSize; + if(entry.mByteSize < incomingSize) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + + calculatedSize = PxMax(calculatedSize, entry.mMessageOffset + entry.mByteSize); + if(calculatedSize > messageSize) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + + Option propName(findProperty(cls->mClassId, entry.mPropertyName)); + if(propName.hasValue() == false) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + + Option propCls(getClass(propName.getValue().mDatatype)); + if(propCls.hasValue() == false) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + + PropertyMessageEntryImpl newEntry(PropertyMessageEntry( + propName, dtypeCls->mName, dtypeCls->mClassId, entry.mMessageOffset, incomingSize, dtypeInfo.mByteSize)); + newMessage->addEntry(newEntry); + + if(newEntry.mDatatypeId == getPvdTypeForType()) + newMessage->mStringOffsetArray.pushBack(entry.mMessageOffset); + + // property messages cannot be marshalled at this time. + if(newEntry.mDatatypeId != getPvdTypeForType() && newEntry.mDatatypeId != getPvdTypeForType()) + { + MarshalQueryResult marshalInfo = checkMarshalling(newEntry.mDatatypeId, newEntry.mProperty.mDatatype); + if(marshalInfo.needsMarshalling) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + } + } + + if(newMessage) + { + newMessage->mStringOffsets = + DataRef(newMessage->mStringOffsetArray.begin(), newMessage->mStringOffsetArray.end()); + mPropertyMessages.pushBack(newMessage); + mPropertyMessageMap.insert(messageName, newMessage); + return *newMessage; + } + + DestroyNewMessage: + if(newMessage) + PVD_DELETE(newMessage); + + return None(); + } + virtual Option findPropertyMessage(const NamespacedName& msgName) const + { + PropertyMessageDescriptionImpl* desc(findPropertyMessageImpl(msgName)); + if(desc) + return *desc; + return None(); + } + + virtual Option getPropertyMessage(int32_t msgId) const + { + PropertyMessageDescriptionImpl* desc(getPropertyMessageImpl(msgId)); + if(desc) + return *desc; + return None(); + } + + virtual uint32_t getNbPropertyMessages() const + { + return mPropertyMessages.size(); + } + + virtual uint32_t getPropertyMessages(PropertyMessageDescription* msgBuf, uint32_t bufLen, uint32_t startIdx = 0) const + { + startIdx = PxMin(startIdx, getNbPropertyMessages()); + bufLen = PxMin(bufLen, getNbPropertyMessages() - startIdx); + PVD_FOREACH(idx, bufLen) msgBuf[idx] = *mPropertyMessages[idx + startIdx]; + return bufLen; + } + + struct MetaDataWriter + { + const PvdObjectModelMetaDataImpl& mMetaData; + PvdOutputStream& mStream; + MetaDataWriter(const PvdObjectModelMetaDataImpl& meta, PvdOutputStream& stream) + : mMetaData(meta), mStream(stream) + { + } + + void streamify(NamespacedName& type) + { + mStream << mMetaData.mStringTable->strToHandle(type.mNamespace); + mStream << mMetaData.mStringTable->strToHandle(type.mName); + } + void streamify(String& type) + { + mStream << mMetaData.mStringTable->strToHandle(type); + } + void streamify(int32_t& type) + { + mStream << type; + } + void streamify(uint32_t& type) + { + mStream << type; + } + void streamify(uint8_t type) + { + mStream << type; + } + void streamify(bool type) + { + streamify( uint8_t(type)); + } + void streamify(PropertyType::Enum type) + { + uint32_t val = static_cast(type); + mStream << val; + } + void streamify(NamedValue& type) + { + streamify(type.mValue); + streamify(type.mName); + } + void streamifyLinks(PropDescImpl* prop) + { + streamify(prop->mPropertyId); + } + void streamify(PropertyDescription& prop) + { + streamify(prop.mPropertyId); + } + void streamify(PropertyMessageEntryImpl& prop) + { + prop.serialize(*this); + } + void streamify(PtrOffset& off) + { + uint32_t type = off.mOffsetType; + mStream << type; + mStream << off.mOffset; + } + template + void streamify(TDataType* type) + { + int32_t existMarker = type ? 1 : 0; + mStream << existMarker; + if(type) + type->serialize(*this); + } + template + void streamify(const Array& type) + { + mStream << static_cast(type.size()); + PVD_FOREACH(idx, type.size()) streamify(const_cast(type[idx])); + } + template + void streamifyLinks(const Array& type) + { + mStream << static_cast(type.size()); + PVD_FOREACH(idx, type.size()) streamifyLinks(const_cast(type[idx])); + } + + private: + MetaDataWriter& operator=(const MetaDataWriter&); + }; + + template + struct MetaDataReader + { + PvdObjectModelMetaDataImpl& mMetaData; + TStreamType& mStream; + MetaDataReader(PvdObjectModelMetaDataImpl& meta, TStreamType& stream) : mMetaData(meta), mStream(stream) + { + } + + void streamify(NamespacedName& type) + { + streamify(type.mNamespace); + streamify(type.mName); + } + + void streamify(String& type) + { + uint32_t handle; + mStream >> handle; + type = mMetaData.mStringTable->handleToStr(handle); + } + void streamify(int32_t& type) + { + mStream >> type; + } + void streamify(uint32_t& type) + { + mStream >> type; + } + void streamify(bool& type) + { + uint8_t data; + mStream >> data; + type = data ? true : false; + } + + void streamify(PropertyType::Enum& type) + { + uint32_t val; + mStream >> val; + type = static_cast(val); + } + void streamify(NamedValue& type) + { + streamify(type.mValue); + streamify(type.mName); + } + void streamify(PropertyMessageEntryImpl& type) + { + type.serialize(*this); + } + void streamify(PtrOffset& off) + { + uint32_t type; + mStream >> type; + mStream >> off.mOffset; + off.mOffsetType = static_cast(type); + } + void streamifyLinks(PropDescImpl*& prop) + { + int32_t propId; + streamify(propId); + prop = mMetaData.getPropertyImpl(propId); + } + void streamify(PropertyDescription& prop) + { + streamify(prop.mPropertyId); + prop = mMetaData.getProperty(prop.mPropertyId); + } + template + void streamify(TDataType*& type) + { + uint32_t existMarker; + mStream >> existMarker; + if(existMarker) + { + TDataType* newType = PVD_NEW(TDataType)(); + newType->serialize(*this); + type = newType; + } + else + type = NULL; + } + template + void streamify(Array& type) + { + uint32_t typeSize; + mStream >> typeSize; + type.resize(typeSize); + PVD_FOREACH(idx, type.size()) streamify(type[idx]); + } + template + void streamifyLinks(Array& type) + { + uint32_t typeSize; + mStream >> typeSize; + type.resize(typeSize); + PVD_FOREACH(idx, type.size()) streamifyLinks(type[idx]); + } + + private: + MetaDataReader& operator=(const MetaDataReader&); + }; + + virtual void write(PvdOutputStream& stream) const + { + stream << getCurrentPvdObjectModelVersion(); + stream << mNextClassId; + mStringTable->write(stream); + MetaDataWriter writer(*this, stream); + writer.streamify(mProperties); + writer.streamify(mClasses); + writer.streamify(mPropertyMessages); + } + + template + void read(TReaderType& stream) + { + uint32_t version; + stream >> version; + stream >> mNextClassId; + mStringTable->read(stream); + MetaDataReader reader(*this, stream); + reader.streamify(mProperties); + reader.streamify(mClasses); + reader.streamify(mPropertyMessages); + + mNameToClasses.clear(); + mNameToProperties.clear(); + mPropertyMessageMap.clear(); + PVD_FOREACH(i, mClasses.size()) + { + ClassDescImpl* cls(mClasses[i]); + if(cls == NULL) + continue; + mNameToClasses.insert(cls->mName, mClasses[i]); + uint32_t propCount = getNbProperties(cls->mClassId); + PropertyDescription descs[16]; + uint32_t offset = 0; + for(uint32_t idx = 0; idx < propCount; idx = offset) + { + uint32_t numProps = getProperties(cls->mClassId, descs, 16, offset); + offset += numProps; + for(uint32_t propIdx = 0; propIdx < numProps; ++propIdx) + { + PropDescImpl* prop = getPropertyImpl(descs[propIdx].mPropertyId); + if(prop) + mNameToProperties.insert(ClassPropertyName(cls->mName, prop->mName), prop); + } + } + } + PVD_FOREACH(idx, mPropertyMessages.size()) + mPropertyMessageMap.insert(mPropertyMessages[idx]->mMessageName, mPropertyMessages[idx]); + } + + virtual StringTable& getStringTable() const + { + return *mStringTable; + } + virtual void addRef() + { + ++mRefCount; + } + virtual void release() + { + if(mRefCount) + --mRefCount; + if(!mRefCount) + PVD_DELETE(this); + } +}; +} + +uint32_t PvdObjectModelMetaData::getCurrentPvdObjectModelVersion() +{ + return 1; +} + +PvdObjectModelMetaData& PvdObjectModelMetaData::create() +{ + PvdObjectModelMetaDataImpl& retval(*PVD_NEW(PvdObjectModelMetaDataImpl)()); + retval.initialize(); + return retval; +} + +PvdObjectModelMetaData& PvdObjectModelMetaData::create(PvdInputStream& stream) +{ + PvdObjectModelMetaDataImpl& retval(*PVD_NEW(PvdObjectModelMetaDataImpl)()); + retval.read(stream); + return retval; +} + +StringTable& StringTable::create() +{ + return *PVD_NEW(StringTableImpl)(); +} diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h new file mode 100644 index 000000000..eb2baea83 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h @@ -0,0 +1,481 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#ifndef PXPVDSDK_PXPVDOBJECTMODELMETADATA_H +#define PXPVDSDK_PXPVDOBJECTMODELMETADATA_H + +#include "foundation/PxAssert.h" +#include "PxPvdObjectModelBaseTypes.h" +#include "PxPvdBits.h" + +namespace physx +{ +namespace pvdsdk +{ + +class PvdInputStream; +class PvdOutputStream; + +struct PropertyDescription +{ + NamespacedName mOwnerClassName; + int32_t mOwnerClassId; + String mName; + String mSemantic; + // The datatype this property corresponds to. + int32_t mDatatype; + // The name of the datatype + NamespacedName mDatatypeName; + // Scalar or array. + PropertyType::Enum mPropertyType; + // No other property under any class has this id, it is DB-unique. + int32_t mPropertyId; + // Offset in bytes into the object's data section where this property starts. + uint32_t m32BitOffset; + // Offset in bytes into the object's data section where this property starts. + uint32_t m64BitOffset; + + PropertyDescription(const NamespacedName& clsName, int32_t classId, String name, String semantic, int32_t datatype, + const NamespacedName& datatypeName, PropertyType::Enum propType, int32_t propId, + uint32_t offset32, uint32_t offset64) + : mOwnerClassName(clsName) + , mOwnerClassId(classId) + , mName(name) + , mSemantic(semantic) + , mDatatype(datatype) + , mDatatypeName(datatypeName) + , mPropertyType(propType) + , mPropertyId(propId) + , m32BitOffset(offset32) + , m64BitOffset(offset64) + { + } + PropertyDescription() + : mOwnerClassId(-1) + , mName("") + , mSemantic("") + , mDatatype(-1) + , mPropertyType(PropertyType::Unknown) + , mPropertyId(-1) + , m32BitOffset(0) + , m64BitOffset(0) + + { + } + + virtual ~PropertyDescription() + { + } +}; + +struct PtrOffsetType +{ + enum Enum + { + UnknownOffset, + VoidPtrOffset, + StringOffset + }; +}; + +struct PtrOffset +{ + PtrOffsetType::Enum mOffsetType; + uint32_t mOffset; + PtrOffset(PtrOffsetType::Enum type, uint32_t offset) : mOffsetType(type), mOffset(offset) + { + } + PtrOffset() : mOffsetType(PtrOffsetType::UnknownOffset), mOffset(0) + { + } +}; + +inline uint32_t align(uint32_t offset, uint32_t alignment) +{ + uint32_t startOffset = offset; + uint32_t alignmentMask = ~(alignment - 1); + offset = (offset + alignment - 1) & alignmentMask; + PX_ASSERT(offset >= startOffset && (offset % alignment) == 0); + (void)startOffset; + return offset; +} + +struct ClassDescriptionSizeInfo +{ + // The size of the data section of this object, padded to alignment. + uint32_t mByteSize; + // The last data member goes to here. + uint32_t mDataByteSize; + // Alignment in bytes of the data section of this object. + uint32_t mAlignment; + // the offsets of string handles in the binary value of this class + DataRef mPtrOffsets; + ClassDescriptionSizeInfo() : mByteSize(0), mDataByteSize(0), mAlignment(0) + { + } +}; + +struct ClassDescription +{ + NamespacedName mName; + // No other class has this id, it is DB-unique + int32_t mClassId; + // Only single derivation supported. + int32_t mBaseClass; + // If this class has properties that are of uniform type, then we note that. + // This means that when deserialization an array of these objects we can just use + // single function to endian convert the entire mess at once. + int32_t mPackedUniformWidth; + // If this class is composed uniformly of members of a given type + // Or all of its properties are composed uniformly of members of + // a give ntype, then this class's packed type is that type. + // PxTransform's packed type would be float. + int32_t mPackedClassType; + // 0: 32Bit 1: 64Bit + ClassDescriptionSizeInfo mSizeInfo[2]; + // No further property additions allowed. + bool mLocked; + // True when this datatype has an array on it that needs to be + // separately deleted. + bool mRequiresDestruction; + + ClassDescription(NamespacedName name, int32_t id) + : mName(name) + , mClassId(id) + , mBaseClass(-1) + , mPackedUniformWidth(-1) + , mPackedClassType(-1) + , mLocked(false) + , mRequiresDestruction(false) + { + } + ClassDescription() + : mClassId(-1), mBaseClass(-1), mPackedUniformWidth(-1), mPackedClassType(-1), mLocked(false), mRequiresDestruction(false) + { + } + virtual ~ClassDescription() + { + } + + ClassDescriptionSizeInfo& get32BitSizeInfo() + { + return mSizeInfo[0]; + } + ClassDescriptionSizeInfo& get64BitSizeInfo() + { + return mSizeInfo[1]; + } + uint32_t& get32BitSize() + { + return get32BitSizeInfo().mByteSize; + } + uint32_t& get64BitSize() + { + return get64BitSizeInfo().mByteSize; + } + + uint32_t get32BitSize() const + { + return mSizeInfo[0].mByteSize; + } + const ClassDescriptionSizeInfo& getNativeSizeInfo() const + { + return mSizeInfo[(sizeof(void*) >> 2) - 1]; + } + uint32_t getNativeSize() const + { + return getNativeSizeInfo().mByteSize; + } +}; + +struct MarshalQueryResult +{ + int32_t srcType; + int32_t dstType; + // If canMarshal != needsMarshalling we have a problem. + bool canMarshal; + bool needsMarshalling; + // Non null if marshalling is possible. + TBlockMarshaller marshaller; + MarshalQueryResult(int32_t _srcType = -1, int32_t _dstType = -1, bool _canMarshal = false, bool _needs = false, + TBlockMarshaller _m = NULL) + : srcType(_srcType), dstType(_dstType), canMarshal(_canMarshal), needsMarshalling(_needs), marshaller(_m) + { + } +}; + +struct PropertyMessageEntry +{ + PropertyDescription mProperty; + NamespacedName mDatatypeName; + // datatype of the data in the message. + int32_t mDatatypeId; + // where in the message this property starts. + uint32_t mMessageOffset; + // size of this entry object + uint32_t mByteSize; + + // If the chain of properties doesn't have any array properties this indicates the + uint32_t mDestByteSize; + + PropertyMessageEntry(PropertyDescription propName, NamespacedName dtypeName, int32_t dtype, uint32_t messageOff, + uint32_t byteSize, uint32_t destByteSize) + : mProperty(propName) + , mDatatypeName(dtypeName) + , mDatatypeId(dtype) + , mMessageOffset(messageOff) + , mByteSize(byteSize) + , mDestByteSize(destByteSize) + { + } + PropertyMessageEntry() : mDatatypeId(-1), mMessageOffset(0), mByteSize(0), mDestByteSize(0) + { + } +}; + +// Create a struct that defines a subset of the properties on an object. +struct PropertyMessageDescription +{ + NamespacedName mClassName; + // No other class has this id, it is DB-unique + int32_t mClassId; + NamespacedName mMessageName; + int32_t mMessageId; + DataRef mProperties; + uint32_t mMessageByteSize; + // Offsets into the property message where const char* items are. + DataRef mStringOffsets; + PropertyMessageDescription(const NamespacedName& nm, int32_t clsId, const NamespacedName& msgName, int32_t msgId, + uint32_t msgSize) + : mClassName(nm), mClassId(clsId), mMessageName(msgName), mMessageId(msgId), mMessageByteSize(msgSize) + { + } + PropertyMessageDescription() : mClassId(-1), mMessageId(-1), mMessageByteSize(0) + { + } + virtual ~PropertyMessageDescription() + { + } +}; + +class StringTable +{ + protected: + virtual ~StringTable() + { + } + + public: + virtual uint32_t getNbStrs() = 0; + virtual uint32_t getStrs(const char** outStrs, uint32_t bufLen, uint32_t startIdx = 0) = 0; + virtual const char* registerStr(const char* str, bool& outAdded) = 0; + const char* registerStr(const char* str) + { + bool ignored; + return registerStr(str, ignored); + } + virtual StringHandle strToHandle(const char* str) = 0; + virtual const char* handleToStr(uint32_t hdl) = 0; + virtual void release() = 0; + + static StringTable& create(); +}; + +struct None +{ +}; + +template +class Option +{ + T mValue; + bool mHasValue; + + public: + Option(const T& val) : mValue(val), mHasValue(true) + { + } + Option(None nothing = None()) : mHasValue(false) + { + (void)nothing; + } + Option(const Option& other) : mValue(other.mValue), mHasValue(other.mHasValue) + { + } + Option& operator=(const Option& other) + { + mValue = other.mValue; + mHasValue = other.mHasValue; + return *this; + } + bool hasValue() const + { + return mHasValue; + } + const T& getValue() const + { + PX_ASSERT(hasValue()); + return mValue; + } + T& getValue() + { + PX_ASSERT(hasValue()); + return mValue; + } + operator const T&() const + { + return getValue(); + } + operator T&() + { + return getValue(); + } + T* operator->() + { + return &getValue(); + } + const T* operator->() const + { + return &getValue(); + } +}; + +/** + * Create new classes and add properties to some existing ones. + * The default classes are created already, the simple types + * along with the basic math types. + * (uint8_t, int8_t, etc ) + * (PxVec3, PxQuat, PxTransform, PxMat33, PxMat34, PxMat44) + */ +class PvdObjectModelMetaData +{ + protected: + virtual ~PvdObjectModelMetaData() + { + } + + public: + virtual ClassDescription getOrCreateClass(const NamespacedName& nm) = 0; + // get or create parent, lock parent. deriveFrom getOrCreatechild. + virtual bool deriveClass(const NamespacedName& parent, const NamespacedName& child) = 0; + virtual Option findClass(const NamespacedName& nm) const = 0; + template + Option findClass() + { + return findClass(getPvdNamespacedNameForType()); + } + virtual Option getClass(int32_t classId) const = 0; + virtual ClassDescription* getClassPtr(int32_t classId) const = 0; + + virtual Option getParentClass(int32_t classId) const = 0; + bool isDerivedFrom(int32_t classId, int32_t parentClass) const + { + if(classId == parentClass) + return true; + ClassDescription* p = getClassPtr(getClassPtr(classId)->mBaseClass); + while(p != NULL) + { + if(p->mClassId == parentClass) + return true; + p = getClassPtr(p->mBaseClass); + } + return false; + } + + virtual void lockClass(int32_t classId) = 0; + + virtual uint32_t getNbClasses() const = 0; + virtual uint32_t getClasses(ClassDescription* outClasses, uint32_t requestCount, uint32_t startIndex = 0) const = 0; + + // Create a nested property. + // This way you can have obj.p.x without explicity defining the class p. + virtual Option createProperty(int32_t classId, String name, String semantic, int32_t datatype, + PropertyType::Enum propertyType = PropertyType::Scalar) = 0; + Option createProperty(NamespacedName clsId, String name, String semantic, NamespacedName dtype, + PropertyType::Enum propertyType = PropertyType::Scalar) + { + return createProperty(findClass(clsId)->mClassId, name, semantic, findClass(dtype)->mClassId, propertyType); + } + Option createProperty(NamespacedName clsId, String name, NamespacedName dtype, + PropertyType::Enum propertyType = PropertyType::Scalar) + { + return createProperty(findClass(clsId)->mClassId, name, "", findClass(dtype)->mClassId, propertyType); + } + Option createProperty(int32_t clsId, String name, int32_t dtype, + PropertyType::Enum propertyType = PropertyType::Scalar) + { + return createProperty(clsId, name, "", dtype, propertyType); + } + template + Option createProperty(int32_t clsId, String name, String semantic = "", + PropertyType::Enum propertyType = PropertyType::Scalar) + { + return createProperty(clsId, name, semantic, getPvdNamespacedNameForType(), propertyType); + } + virtual Option findProperty(const NamespacedName& cls, String prop) const = 0; + virtual Option findProperty(int32_t clsId, String prop) const = 0; + virtual Option getProperty(int32_t propId) const = 0; + virtual void setNamedPropertyValues(DataRef values, int32_t propId) = 0; + // for enumerations and flags. + virtual DataRef getNamedPropertyValues(int32_t propId) const = 0; + + virtual uint32_t getNbProperties(int32_t classId) const = 0; + virtual uint32_t getProperties(int32_t classId, PropertyDescription* outBuffer, uint32_t bufCount, + uint32_t startIdx = 0) const = 0; + + // Does one cls id differ marshalling to another and if so return the functions to do it. + virtual MarshalQueryResult checkMarshalling(int32_t srcClsId, int32_t dstClsId) const = 0; + + // messages and classes are stored in separate maps, so a property message can have the same name as a class. + virtual Option createPropertyMessage(const NamespacedName& cls, + const NamespacedName& msgName, + DataRef entries, + uint32_t messageSize) = 0; + virtual Option findPropertyMessage(const NamespacedName& msgName) const = 0; + virtual Option getPropertyMessage(int32_t msgId) const = 0; + + virtual uint32_t getNbPropertyMessages() const = 0; + virtual uint32_t getPropertyMessages(PropertyMessageDescription* msgBuf, uint32_t bufLen, + uint32_t startIdx = 0) const = 0; + + virtual StringTable& getStringTable() const = 0; + + virtual void write(PvdOutputStream& stream) const = 0; + void save(PvdOutputStream& stream) const + { + write(stream); + } + + virtual void addRef() = 0; + virtual void release() = 0; + + static uint32_t getCurrentPvdObjectModelVersion(); + static PvdObjectModelMetaData& create(); + static PvdObjectModelMetaData& create(PvdInputStream& stream); +}; +} +} +#endif // PXPVDSDK_PXPVDOBJECTMODELMETADATA_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp new file mode 100644 index 000000000..3bdfaaf7f --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp @@ -0,0 +1,80 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdObjectRegistrar.h" + +namespace physx +{ +namespace pvdsdk +{ + +bool ObjectRegistrar::addItem(const void* inItem) +{ + physx::shdfnd::Mutex::ScopedLock lock(mRefCountMapLock); + + if(mRefCountMap.find(inItem)) + { + uint32_t& counter = mRefCountMap[inItem]; + counter++; + return false; + } + else + { + mRefCountMap.insert(inItem, 1); + return true; + } +} + +bool ObjectRegistrar::decItem(const void* inItem) +{ + physx::shdfnd::Mutex::ScopedLock lock(mRefCountMapLock); + const physx::shdfnd::HashMap::Entry* entry = mRefCountMap.find(inItem); + if(entry) + { + uint32_t& retval(const_cast(entry->second)); + if(retval) + --retval; + uint32_t theValue = retval; + if(theValue == 0) + { + mRefCountMap.erase(inItem); + return true; + } + } + return false; +} + +void ObjectRegistrar::clear() +{ + physx::shdfnd::Mutex::ScopedLock lock(mRefCountMapLock); + mRefCountMap.clear(); +} + +} // pvdsdk +} // physx diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h new file mode 100644 index 000000000..5cf0e8549 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h @@ -0,0 +1,71 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDOBJECTREGISTRAR_H +#define PXPVDSDK_PXPVDOBJECTREGISTRAR_H + +/** \addtogroup pvd +@{ +*/ + +#include "PsHashMap.h" +#include "PsMutex.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif +class ObjectRegistrar +{ + PX_NOCOPY(ObjectRegistrar) + public: + ObjectRegistrar() + { + } + virtual ~ObjectRegistrar() + { + } + + bool addItem(const void* inItem); + bool decItem(const void* inItem); + void clear(); + + private: + physx::shdfnd::HashMap mRefCountMap; + physx::shdfnd::Mutex mRefCountMapLock; +}; +#if !PX_DOXYGEN +} // pvdsdk +} // physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDOBJECTREGISTRAR_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h b/src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h new file mode 100644 index 000000000..555b64acd --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDPROFILEZONE_H +#define PXPVDSDK_PXPVDPROFILEZONE_H + +#include "foundation/PxPreprocessor.h" + +#include "PxProfileEventBufferClientManager.h" +#include "PxProfileEventNames.h" +#include "PxProfileEventSender.h" + +namespace physx { + class PxAllocatorCallback; + + namespace profile { + + class PxProfileZoneManager; + + /** + \brief The profiling system was setup in the expectation that there would be several + systems that each had its own island of profile information. PhysX, client code, + and APEX would be the first examples of these. Each one of these islands is represented + by a profile zone. + + A profile zone combines a name, a place where all the events coming from its interface + can flushed, and a mapping from event number to full event name. + + It also provides a top level filtering service where profile events + can be filtered by event id. + + The profile zone implements a system where if there is no one + listening to events it doesn't provide a mechanism to send them. In this way + the event system is short circuited when there aren't any clients. + + All functions on this interface should be considered threadsafe. + + @see PxProfileZoneClientManager, PxProfileNameProvider, PxProfileEventSender, PxProfileEventFlusher + */ + class PxProfileZone : public PxProfileZoneClientManager + , public PxProfileNameProvider + , public PxProfileEventSender + , public PxProfileEventFlusher + { + protected: + virtual ~PxProfileZone(){} + public: + /** + \brief Get profile zone name. + \return Zone name. + */ + virtual const char* getName() = 0; + /** + \brief Release the profile zone. + */ + virtual void release() = 0; + + /** + \brief Set profile zone manager for the zone. + \param inMgr Profile zone manager. + */ + virtual void setProfileZoneManager(PxProfileZoneManager* inMgr) = 0; + /** + \brief Get profile zone manager for the zone. + \return Profile zone manager. + */ + virtual PxProfileZoneManager* getProfileZoneManager() = 0; + + /** + \brief Get or create a new event id for a given name. + If you pass in a previously defined event name (including one returned) + from the name provider) you will just get the same event id back. + \param inName Profile event name. + */ + virtual uint16_t getEventIdForName( const char* inName ) = 0; + + /** + \brief Specifies that it is a safe point to flush read-write name map into + read-only map. Make sure getEventIdForName is not called from a different thread. + */ + virtual void flushEventIdNameMap() = 0; + + /** + \brief Reserve a contiguous set of profile event ids for a set of names. + + This function does not do any meaningful error checking other than to ensure + that if it does generate new ids they are contiguous. If the first name is already + registered, that is the ID that will be returned regardless of what other + names are registered. Thus either use this function alone (without the above + function) or don't use it. + If you register "one","two","three" and the function returns an id of 4, then + "one" is mapped to 4, "two" is mapped to 5, and "three" is mapped to 6. + + \param inNames set of names to register. + \param inLen Length of the name list. + + \return The first id associated with the first name. The rest of the names + will be associated with monotonically incrementing uint16_t values from the first + id. + */ + virtual uint16_t getEventIdsForNames( const char** inNames, uint32_t inLen ) = 0; + + /** + \brief Create a new profile zone. + + \param inAllocator memory allocation is controlled through the foundation if one is passed in. + \param inSDKName Name of the profile zone; useful for clients to understand where events came from. + \param inNames Mapping from event id -> event name. + \param inEventBufferByteSize Size of the canonical event buffer. This does not need to be a large number + as profile events are fairly small individually. + \return a profile zone implementation. + */ + static PxProfileZone& createProfileZone(PxAllocatorCallback* inAllocator, const char* inSDKName, PxProfileNames inNames = PxProfileNames(), uint32_t inEventBufferByteSize = 0x10000 /*64k*/); + + }; +} } + +#endif // PXPVDSDK_PXPVDPROFILEZONE_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp new file mode 100644 index 000000000..ce22db789 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp @@ -0,0 +1,171 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdImpl.h" +#include "PxPvdProfileZoneClient.h" +#include "PxPvdProfileZone.h" + +namespace physx +{ +namespace pvdsdk +{ +struct ProfileZoneClient : public profile::PxProfileZoneClient, public shdfnd::UserAllocated +{ + profile::PxProfileZone& mZone; + PvdDataStream& mStream; + + ProfileZoneClient(profile::PxProfileZone& zone, PvdDataStream& stream) : mZone(zone), mStream(stream) + { + } + + ~ProfileZoneClient() + { + mZone.removeClient(*this); + } + + virtual void createInstance() + { + mStream.addProfileZone(&mZone, mZone.getName()); + mStream.createInstance(&mZone); + mZone.addClient(*this); + profile::PxProfileNames names(mZone.getProfileNames()); + PVD_FOREACH(idx, names.eventCount) + { + handleEventAdded(names.events[idx]); + } + } + + virtual void handleEventAdded(const profile::PxProfileEventName& inName) + { + mStream.addProfileZoneEvent(&mZone, inName.name, inName.eventId.eventId, inName.eventId.compileTimeEnabled); + } + + virtual void handleBufferFlush(const uint8_t* inData, uint32_t inLength) + { + mStream.setPropertyValue(&mZone, "events", inData, inLength); + } + + virtual void handleClientRemoved() + { + mStream.destroyInstance(&mZone); + } + + private: + ProfileZoneClient& operator=(const ProfileZoneClient&); +}; +} +} + +using namespace physx; +using namespace pvdsdk; + +PvdProfileZoneClient::PvdProfileZoneClient(PvdImpl& pvd) : mSDKPvd(pvd), mPvdDataStream(NULL), mIsConnected(false) +{ +} + +PvdProfileZoneClient::~PvdProfileZoneClient() +{ + mSDKPvd.removeClient(this); + // all zones should removed + PX_ASSERT(mProfileZoneClients.size() == 0); +} + +PvdDataStream* PvdProfileZoneClient::getDataStream() +{ + return mPvdDataStream; +} + +PvdUserRenderer* PvdProfileZoneClient::getUserRender() +{ + PX_ASSERT(0); + return NULL; +} + +void PvdProfileZoneClient::setObjectRegistrar(ObjectRegistrar*) +{ +} + +bool PvdProfileZoneClient::isConnected() const +{ + return mIsConnected; +} + +void PvdProfileZoneClient::onPvdConnected() +{ + if(mIsConnected) + return; + mIsConnected = true; + + mPvdDataStream = PvdDataStream::create(&mSDKPvd); + +} + +void PvdProfileZoneClient::onPvdDisconnected() +{ + if(!mIsConnected) + return; + + mIsConnected = false; + flush(); + + mPvdDataStream->release(); + mPvdDataStream = NULL; +} + +void PvdProfileZoneClient::flush() +{ + PVD_FOREACH(idx, mProfileZoneClients.size()) + mProfileZoneClients[idx]->mZone.flushProfileEvents(); +} + +void PvdProfileZoneClient::onZoneAdded(profile::PxProfileZone& zone) +{ + PX_ASSERT(mIsConnected); + ProfileZoneClient* client = PVD_NEW(ProfileZoneClient)(zone, *mPvdDataStream); + mMutex.lock(); + client->createInstance(); + mProfileZoneClients.pushBack(client); + mMutex.unlock(); +} + +void PvdProfileZoneClient::onZoneRemoved(profile::PxProfileZone& zone) +{ + for(uint32_t i = 0; i < mProfileZoneClients.size(); i++) + { + if(&zone == &mProfileZoneClients[i]->mZone) + { + mMutex.lock(); + ProfileZoneClient* client = mProfileZoneClients[i]; + mProfileZoneClients.replaceWithLast(i); + PVD_DELETE(client); + mMutex.unlock(); + return; + } + } +} diff --git a/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h new file mode 100644 index 000000000..4438b8101 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDPROFILEZONECLIENT_H +#define PXPVDSDK_PXPVDPROFILEZONECLIENT_H +#include "PxPvdClient.h" +#include "PsHashMap.h" +#include "PsMutex.h" +#include "PxProfileZoneManager.h" + +namespace physx +{ +namespace pvdsdk +{ +class PvdImpl; +class PvdDataStream; + +struct ProfileZoneClient; + +class PvdProfileZoneClient : public PvdClient, public profile::PxProfileZoneHandler, public shdfnd::UserAllocated +{ + PX_NOCOPY(PvdProfileZoneClient) + public: + PvdProfileZoneClient(PvdImpl& pvd); + virtual ~PvdProfileZoneClient(); + + bool isConnected() const; + void onPvdConnected(); + void onPvdDisconnected(); + void flush(); + + PvdDataStream* getDataStream(); + PvdUserRenderer* getUserRender(); + void setObjectRegistrar(ObjectRegistrar*); + + // PxProfileZoneHandler + void onZoneAdded(profile::PxProfileZone& inSDK); + void onZoneRemoved(profile::PxProfileZone& inSDK); + + private: + shdfnd::Mutex mMutex; // zoneAdded can called from different threads + PvdImpl& mSDKPvd; + PvdDataStream* mPvdDataStream; + physx::shdfnd::Array mProfileZoneClients; + bool mIsConnected; +}; + +} // namespace pvdsdk +} // namespace physx + +#endif // PXPVDSDK_PXPVDPROFILEZONECLIENT_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h new file mode 100644 index 000000000..70b858400 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h @@ -0,0 +1,385 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDUSERRENDERIMPL_H +#define PXPVDSDK_PXPVDUSERRENDERIMPL_H + +#include "PxPvdUserRenderer.h" + +namespace physx +{ +namespace pvdsdk +{ + +struct PvdUserRenderTypes +{ + enum Enum + { + Unknown = 0, +#define DECLARE_PVD_IMMEDIATE_RENDER_TYPE(type) type, +#define DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA(type) type +#include "PxPvdUserRenderTypes.h" +#undef DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA +#undef DECLARE_PVD_IMMEDIATE_RENDER_TYPE + }; +}; + +class RenderSerializer +{ + protected: + virtual ~RenderSerializer() + { + } + + public: + virtual void streamify(uint64_t& val) = 0; + virtual void streamify(float& val) = 0; + virtual void streamify(uint32_t& val) = 0; + virtual void streamify(uint8_t& val) = 0; + virtual void streamify(DataRef& val) = 0; + virtual void streamify(DataRef& val) = 0; + virtual void streamify(DataRef& val) = 0; + virtual void streamify(DataRef& val) = 0; + virtual void streamify(PvdDebugText& val) = 0; + virtual bool isGood() = 0; + virtual uint32_t hasData() = 0; + + void streamify(PvdUserRenderTypes::Enum& val) + { + uint8_t data = static_cast(val); + streamify(data); + val = static_cast(data); + } + void streamify(PxVec3& val) + { + streamify(val[0]); + streamify(val[1]); + streamify(val[2]); + } + + void streamify(PvdColor& val) + { + streamify(val.r); + streamify(val.g); + streamify(val.b); + streamify(val.a); + } + void streamify(PxTransform& val) + { + streamify(val.q.x); + streamify(val.q.y); + streamify(val.q.z); + streamify(val.q.w); + streamify(val.p.x); + streamify(val.p.y); + streamify(val.p.z); + } + void streamify(bool& val) + { + uint8_t tempVal = uint8_t(val ? 1 : 0); + streamify(tempVal); + val = tempVal ? true : false; + } +}; + +template +struct BulkRenderEvent +{ + DataRef mData; + BulkRenderEvent(const TBulkRenderType* data, uint32_t count) : mData(data, count) + { + } + BulkRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(mData); + } +}; +struct SetInstanceIdRenderEvent +{ + uint64_t mInstanceId; + SetInstanceIdRenderEvent(uint64_t iid) : mInstanceId(iid) + { + } + SetInstanceIdRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(mInstanceId); + } +}; +struct PointsRenderEvent : BulkRenderEvent +{ + PointsRenderEvent(const PvdDebugPoint* data, uint32_t count) : BulkRenderEvent(data, count) + { + } + PointsRenderEvent() + { + } +}; +struct LinesRenderEvent : BulkRenderEvent +{ + LinesRenderEvent(const PvdDebugLine* data, uint32_t count) : BulkRenderEvent(data, count) + { + } + LinesRenderEvent() + { + } +}; +struct TrianglesRenderEvent : BulkRenderEvent +{ + TrianglesRenderEvent(const PvdDebugTriangle* data, uint32_t count) : BulkRenderEvent(data, count) + { + } + TrianglesRenderEvent() + { + } +}; +struct DebugRenderEvent +{ + DataRef mPointData; + DataRef mLineData; + DataRef mTriangleData; + DebugRenderEvent(const PvdDebugPoint* pointData, uint32_t pointCount, const PvdDebugLine* lineData, + uint32_t lineCount, const PvdDebugTriangle* triangleData, uint32_t triangleCount) + : mPointData(pointData, pointCount), mLineData(lineData, lineCount), mTriangleData(triangleData, triangleCount) + { + } + + DebugRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(mPointData); + serializer.streamify(mLineData); + serializer.streamify(mTriangleData); + } +}; + +struct TextRenderEvent +{ + PvdDebugText mText; + TextRenderEvent(const PvdDebugText& text) + { + mText.color = text.color; + mText.position = text.position; + mText.size = text.size; + mText.string = text.string; + } + TextRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(mText); + } +}; + +struct JointFramesRenderEvent +{ + PxTransform parent; + PxTransform child; + JointFramesRenderEvent(const PxTransform& p, const PxTransform& c) : parent(p), child(c) + { + } + JointFramesRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(parent); + serializer.streamify(child); + } +}; +struct LinearLimitRenderEvent +{ + PxTransform t0; + PxTransform t1; + float value; + bool active; + LinearLimitRenderEvent(const PxTransform& _t0, const PxTransform& _t1, float _value, bool _active) + : t0(_t0), t1(_t1), value(_value), active(_active) + { + } + LinearLimitRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(t0); + serializer.streamify(t1); + serializer.streamify(value); + serializer.streamify(active); + } +}; +struct AngularLimitRenderEvent +{ + PxTransform t0; + float lower; + float upper; + bool active; + AngularLimitRenderEvent(const PxTransform& _t0, float _lower, float _upper, bool _active) + : t0(_t0), lower(_lower), upper(_upper), active(_active) + { + } + AngularLimitRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(t0); + serializer.streamify(lower); + serializer.streamify(upper); + serializer.streamify(active); + } +}; +struct LimitConeRenderEvent +{ + PxTransform t; + float ySwing; + float zSwing; + bool active; + LimitConeRenderEvent(const PxTransform& _t, float _ySwing, float _zSwing, bool _active) + : t(_t), ySwing(_ySwing), zSwing(_zSwing), active(_active) + { + } + LimitConeRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(t); + serializer.streamify(ySwing); + serializer.streamify(zSwing); + serializer.streamify(active); + } +}; +struct DoubleConeRenderEvent +{ + PxTransform t; + float angle; + bool active; + DoubleConeRenderEvent(const PxTransform& _t, float _angle, bool _active) : t(_t), angle(_angle), active(_active) + { + } + DoubleConeRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(t); + serializer.streamify(angle); + serializer.streamify(active); + } +}; + +template +struct RenderSerializerMap +{ + void serialize(RenderSerializer& s, TDataType& d) + { + d.serialize(s); + } +}; +template <> +struct RenderSerializerMap +{ + void serialize(RenderSerializer& s, uint8_t& d) + { + s.streamify(d); + } +}; + +template <> +struct RenderSerializerMap +{ + void serialize(RenderSerializer& s, PvdDebugPoint& d) + { + s.streamify(d.pos); + s.streamify(d.color); + } +}; + +template <> +struct RenderSerializerMap +{ + void serialize(RenderSerializer& s, PvdDebugLine& d) + { + s.streamify(d.pos0); + s.streamify(d.color0); + s.streamify(d.pos1); + s.streamify(d.color1); + } +}; + +template <> +struct RenderSerializerMap +{ + void serialize(RenderSerializer& s, PvdDebugTriangle& d) + { + s.streamify(d.pos0); + s.streamify(d.color0); + s.streamify(d.pos1); + s.streamify(d.color1); + s.streamify(d.pos2); + s.streamify(d.color2); + } +}; + +template +struct PvdTypeToRenderType +{ + bool compile_error; +}; + +#define DECLARE_PVD_IMMEDIATE_RENDER_TYPE(type) \ + template <> \ + struct PvdTypeToRenderType \ + { \ + enum Enum \ + { \ + EnumVal = PvdUserRenderTypes::type \ + }; \ + }; + +#include "PxPvdUserRenderTypes.h" +#undef DECLARE_PVD_IMMEDIATE_RENDER_TYPE + +template +PvdUserRenderTypes::Enum getPvdRenderTypeFromType() +{ + return static_cast(PvdTypeToRenderType::EnumVal); +} + +} +} + +#endif // PXPVDSDK_PXPVDUSERRENDERIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h new file mode 100644 index 000000000..f313e66f0 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +#ifndef DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA +#define DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA DECLARE_PVD_IMMEDIATE_RENDER_TYPE +#endif + +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(SetInstanceId) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(Points) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(Lines) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(Triangles) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(JointFrames) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(LinearLimit) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(AngularLimit) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(LimitCone) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(DoubleCone) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(Text) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA(Debug) + +#undef DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp new file mode 100644 index 000000000..98c12eba9 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp @@ -0,0 +1,405 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxPvdUserRenderImpl.h" +#include "PxPvdInternalByteStreams.h" +#include "PxPvdBits.h" +#include + +using namespace physx; +using namespace physx::pvdsdk; + +namespace +{ + +template +struct RenderWriter : public RenderSerializer +{ + TStreamType& mStream; + RenderWriter(TStreamType& stream) : mStream(stream) + { + } + template + void write(const TDataType* val, uint32_t count) + { + uint32_t numBytes = count * sizeof(TDataType); + mStream.write(reinterpret_cast(val), numBytes); + } + template + void write(const TDataType& val) + { + write(&val, 1); + } + + template + void writeRef(DataRef& val) + { + uint32_t amount = val.size(); + write(amount); + if(amount) + write(val.begin(), amount); + } + + virtual void streamify(uint64_t& val) + { + write(val); + } + virtual void streamify(uint32_t& val) + { + write(val); + } + virtual void streamify(float& val) + { + write(val); + } + virtual void streamify(uint8_t& val) + { + write(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + + virtual void streamify(PvdDebugText& val) + { + write(val.color); + write(val.position); + write(val.size); + + uint32_t amount = static_cast(strlen(val.string)) + 1; + write(amount); + if(amount) + write(val.string, amount); + } + + virtual void streamify(DataRef& val) + { + writeRef(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + + virtual uint32_t hasData() + { + return false; + } + virtual bool isGood() + { + return true; + } + + private: + RenderWriter& operator=(const RenderWriter&); +}; + +struct UserRenderer : public PvdUserRenderer +{ + ForwardingMemoryBuffer mBuffer; + uint32_t mBufferCapacity; + RendererEventClient* mClient; + + UserRenderer(uint32_t bufferFullAmount) + : mBuffer("UserRenderBuffer"), mBufferCapacity(bufferFullAmount), mClient(NULL) + { + } + virtual ~UserRenderer() + { + } + virtual void release() + { + PVD_DELETE(this); + } + + template + void handleEvent(TEventType evt) + { + RenderWriter _writer(mBuffer); + RenderSerializer& writer(_writer); + + PvdUserRenderTypes::Enum evtType(getPvdRenderTypeFromType()); + writer.streamify(evtType); + evt.serialize(writer); + if(mBuffer.size() >= mBufferCapacity) + flushRenderEvents(); + } + virtual void setInstanceId(const void* iid) + { + handleEvent(SetInstanceIdRenderEvent(PVD_POINTER_TO_U64(iid))); + } + // Draw these points associated with this instance + virtual void drawPoints(const PvdDebugPoint* points, uint32_t count) + { + handleEvent(PointsRenderEvent(points, count)); + } + // Draw these lines associated with this instance + virtual void drawLines(const PvdDebugLine* lines, uint32_t count) + { + handleEvent(LinesRenderEvent(lines, count)); + } + // Draw these triangles associated with this instance + virtual void drawTriangles(const PvdDebugTriangle* triangles, uint32_t count) + { + handleEvent(TrianglesRenderEvent(triangles, count)); + } + + virtual void drawText(const PvdDebugText& text) + { + handleEvent(TextRenderEvent(text)); + } + + virtual void drawRenderbuffer(const PvdDebugPoint* pointData, uint32_t pointCount, const PvdDebugLine* lineData, + uint32_t lineCount, const PvdDebugTriangle* triangleData, uint32_t triangleCount) + { + handleEvent(DebugRenderEvent(pointData, pointCount, lineData, lineCount, triangleData, triangleCount)); + } + + // Constraint visualization routines + virtual void visualizeJointFrames(const PxTransform& parent, const PxTransform& child) + { + handleEvent(JointFramesRenderEvent(parent, child)); + } + virtual void visualizeLinearLimit(const PxTransform& t0, const PxTransform& t1, float value, bool active) + { + handleEvent(LinearLimitRenderEvent(t0, t1, value, active)); + } + virtual void visualizeAngularLimit(const PxTransform& t0, float lower, float upper, bool active) + { + handleEvent(AngularLimitRenderEvent(t0, lower, upper, active)); + } + virtual void visualizeLimitCone(const PxTransform& t, float tanQSwingY, float tanQSwingZ, bool active) + { + handleEvent(LimitConeRenderEvent(t, tanQSwingY, tanQSwingZ, active)); + } + virtual void visualizeDoubleCone(const PxTransform& t, float angle, bool active) + { + handleEvent(DoubleConeRenderEvent(t, angle, active)); + } + // Clear the immedate buffer. + virtual void flushRenderEvents() + { + if(mClient) + mClient->handleBufferFlush(mBuffer.begin(), mBuffer.size()); + mBuffer.clear(); + } + + virtual void setClient(RendererEventClient* client) + { + mClient = client; + } + + private: + UserRenderer& operator=(const UserRenderer&); +}; + +template +struct RenderReader : public RenderSerializer +{ + MemPvdInputStream mStream; + ForwardingMemoryBuffer& mBuffer; + + RenderReader(ForwardingMemoryBuffer& buf) : mBuffer(buf) + { + } + void setData(DataRef data) + { + mStream.setup(const_cast(data.begin()), const_cast(data.end())); + } + virtual void streamify(uint32_t& val) + { + mStream >> val; + } + virtual void streamify(uint64_t& val) + { + mStream >> val; + } + virtual void streamify(float& val) + { + mStream >> val; + } + virtual void streamify(uint8_t& val) + { + mStream >> val; + } + template + void readRef(DataRef& val) + { + uint32_t count; + mStream >> count; + uint32_t numBytes = sizeof(TDataType) * count; + + TDataType* dataPtr = reinterpret_cast(mBuffer.growBuf(numBytes)); + mStream.read(reinterpret_cast(dataPtr), numBytes); + val = DataRef(dataPtr, count); + } + + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(PvdDebugText& val) + { + mStream >> val.color; + mStream >> val.position; + mStream >> val.size; + + uint32_t len = 0; + mStream >> len; + + uint8_t* dataPtr = mBuffer.growBuf(len); + mStream.read(dataPtr, len); + val.string = reinterpret_cast(dataPtr); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual bool isGood() + { + return mStream.isGood(); + } + virtual uint32_t hasData() + { + return uint32_t(mStream.size() > 0); + } + + private: + RenderReader& operator=(const RenderReader&); +}; + +template <> +struct RenderReader : public RenderSerializer +{ + MemPvdInputStream mStream; + ForwardingMemoryBuffer& mBuffer; + RenderReader(ForwardingMemoryBuffer& buf) : mBuffer(buf) + { + } + void setData(DataRef data) + { + mStream.setup(const_cast(data.begin()), const_cast(data.end())); + } + + template + void read(TDataType& val) + { + mStream >> val; + swapBytes(val); + } + virtual void streamify(uint64_t& val) + { + read(val); + } + virtual void streamify(uint32_t& val) + { + read(val); + } + virtual void streamify(float& val) + { + read(val); + } + virtual void streamify(uint8_t& val) + { + read(val); + } + template + void readRef(DataRef& val) + { + uint32_t count; + mStream >> count; + swapBytes(count); + uint32_t numBytes = sizeof(TDataType) * count; + + TDataType* dataPtr = reinterpret_cast(mBuffer.growBuf(numBytes)); + PVD_FOREACH(idx, count) + RenderSerializerMap().serialize(*this, dataPtr[idx]); + val = DataRef(dataPtr, count); + } + + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(PvdDebugText& val) + { + mStream >> val.color; + mStream >> val.position; + mStream >> val.size; + + uint32_t len = 0; + mStream >> len; + + uint8_t* dataPtr = mBuffer.growBuf(len); + mStream.read(dataPtr, len); + val.string = reinterpret_cast(dataPtr); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual bool isGood() + { + return mStream.isGood(); + } + virtual uint32_t hasData() + { + return uint32_t(mStream.size() > 0); + } + + private: + RenderReader& operator=(const RenderReader&); +}; + +} + +PvdUserRenderer* PvdUserRenderer::create(uint32_t bufferSize) +{ + return PVD_NEW(UserRenderer)(bufferSize); +} + diff --git a/src/PhysX/physx/source/scenequery/include/SqPruner.h b/src/PhysX/physx/source/scenequery/include/SqPruner.h new file mode 100644 index 000000000..0a42b2e6b --- /dev/null +++ b/src/PhysX/physx/source/scenequery/include/SqPruner.h @@ -0,0 +1,397 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_PRUNER_H +#define SQ_PRUNER_H + +#include "foundation/PxBounds3.h" +#include "PxGeometry.h" +#include "PxQueryReport.h" +#include "PxQueryFiltering.h" +#include "PsUserAllocated.h" +#include "SqPruningStructure.h" +#include "GuSphere.h" +#include "GuBox.h" +#include "GuCapsule.h" + +namespace physx +{ + namespace Gu + { + class ShapeData; + class BVHStructure; + } +} + +namespace physx +{ + namespace Cm + { + class RenderOutput; + } + +namespace Sq +{ + +typedef PxU32 PrunerHandle; +typedef PxU32 PrunerCompoundId; + +static const PrunerHandle INVALID_PRUNERHANDLE = 0xFFffFFff; +static const PxReal SQ_PRUNER_INFLATION = 1.01f; // pruner test shape inflation (not narrow phase shape) + +struct PrunerPayload +{ + size_t data[2]; + + PX_FORCE_INLINE bool operator == (const PrunerPayload& other) const + { + return (data[0] == other.data[0]) && (data[1] == other.data[1]); + } +}; + +struct PrunerCallback +{ + virtual PxAgain invoke(PxReal& distance, const PrunerPayload& payload) = 0; + virtual ~PrunerCallback() {} +}; + +class Pruner : public Ps::UserAllocated +{ +public: + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * \brief Adds objects to the pruner. + * \param results [out] an array for resulting handles + * \param bounds [in] an array of bounds. These bounds are used as-is so they should be pre-inflated if inflation is needed. + * \param userData [in] an array of object data + * \param count [in] the number of objects in the arrays + * \param hasPruningStructure [in] if added objects have pruning structure. The structure will be merged later, adding the objects will not invalidate the pruner. + * + * \return true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE. + * + * Handles are usable as indices. Each handle is either be a recycled handle returned by the client via removeObjects(), + * or a fresh handle that is either zero, or one greater than the last fresh handle returned. + * + * Objects and bounds in the arrays have the same number of elements and ordering. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* userData, PxU32 count, bool hasPruningStructure) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Removes objects from the pruner. + * \param handles [in] the objects to remove + * \param count [in] the number of objects to remove + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void removeObjects(const PrunerHandle* handles, PxU32 count) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Updates objects after manually updating their bounds via "getPayload" calls. + * \param handles [in] the objects to update + * \param count [in] the number of objects to update + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Updates objects with new indexed bounds. + * \param handles [in] the objects to update + * \param indices [in] the indices of the bounds in the bounds array + * \param newBounds [in] updated bounds array + * \param count [in] the number of objects to update + * + * \warning THESE BOUNDS WILL BE INFLATED ON-THE-FLY. So this is inconsistent with the "addObjects" behavior. + * \warning The inflation value is hardcoded in Sq::inflateBounds(). + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Makes the queries consistent with previous changes. + * This function must be called before starting queries on an updated Pruner and assert otherwise. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void commit() = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Merges pruning structure to current pruner, parameters may differ for each pruner implementation + * \param mergeParams [in] Pruning structure to merge. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void merge(const void* mergeParams) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Query functions + * + * Note: return value may disappear if PrunerCallback contains the necessary information + * currently it is still used for the dynamic pruner internally (to decide if added objects must be queried) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const = 0; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const = 0; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Retrieve the object data associated with the handle + * + * \param handle The handle returned by addObjects() + * + * \return A reference to the object data + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const PrunerPayload& getPayload(PrunerHandle handle) const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Retrieve the object data associated with the handle, plus the destination address for its matrix. The user is then expected to write the new AABB there. + * + * \param handle [in] The handle returned by addObjects() + * \param bounds [out] destination address for this object's bounds + * + * \return A reference to the object data + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const PrunerPayload& getPayload(PrunerHandle handle, PxBounds3*& bounds) const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Preallocate space + * + * \param entries the number of entries to preallocate space for + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void preallocate(PxU32 entries) = 0; + + // shift the origin of the pruner objects + virtual void shiftOrigin(const PxVec3& shift) = 0; + + virtual ~Pruner() {} + + // additional 'internal' interface + virtual void visualize(Cm::RenderOutput&, PxU32) const {} +}; + +////////////////////////////////////////////////////////////////////////// +/** +* Pruner building accel structure over time base class +*/ +////////////////////////////////////////////////////////////////////////// +class IncrementalPruner: public Pruner +{ +public: + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * gets rid of internal accel struct. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void purge() = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * sets the rebuild hint rate used for step building the accel structure. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void setRebuildRateHint(PxU32 nbStepsForRebuild) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Steps the accel structure build. + * synchronousCall specifies if initialization can happen. It should not initialize build when called from a different thread + * returns true if finished + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool buildStep(bool synchronousCall = true) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Prepares new tree build + * returns true if new tree is needed + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool prepareBuild() = 0; +}; + + +////////////////////////////////////////////////////////////////////////// +// Compound flag to use for static/dynamic filtering atm +struct CompoundFlag +{ + enum Enum + { + STATIC_COMPOUND = (1<<0), + DYNAMIC_COMPOUND = (1<<1) + }; +}; + +PX_COMPILE_TIME_ASSERT(PxQueryFlag::eSTATIC & CompoundFlag::STATIC_COMPOUND); +PX_COMPILE_TIME_ASSERT(PxQueryFlag::eDYNAMIC & CompoundFlag::DYNAMIC_COMPOUND); + +////////////////////////////////////////////////////////////////////////// +/** +* Pruner holding compound objects +*/ +////////////////////////////////////////////////////////////////////////// +class CompoundPruner: public Ps::UserAllocated +{ +public: + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * \brief Adds compound to the pruner. + * \param results [out] an array for resulting handles + * \param bvhStructure [in] BVH structure holding bounds and BVH. + * \param compoundId [in] compound id + * \param transform [in] compound transform + * \param userData [in] an array of object data + * + * \return true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE. + * + * Handles are usable as indices. Each handle is either be a recycled handle returned by the client via removeObjects(), + * or a fresh handle that is either zero, or one greater than the last fresh handle returned. + * + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool addCompound(PrunerHandle* results, const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& transform, CompoundFlag::Enum flags, const PrunerPayload* userData) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Removes compound from the pruner. + * \param compoundId [in] compound to remove + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void removeCompound(PrunerCompoundId compoundId) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Updates compound object + * \param compoundId [in] compound to update + * \param transform [in] compound transformation + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void updateCompound(PrunerCompoundId compoundId, const PxTransform& transform) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Updates object after manually updating their bounds via "getPayload" calls. + * \param compoundId [in] compound that the object belongs to + * \param handle [in] the object to update + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void updateObjectAfterManualBoundsUpdates(PrunerCompoundId compoundId, const PrunerHandle handle) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Removes object from compound pruner. + * \param compoundId [in] compound that the object belongs to + * \param handle [in] the object to remove + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void removeObject(PrunerCompoundId compoundId, const PrunerHandle handle) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * \brief Adds object to the pruner. + * \param compoundId [in] compound that the object belongs to + * \param result [out] an array for resulting handles + * \param bounds [in] an array of bounds. These bounds are used as-is so they should be pre-inflated if inflation is needed. + * \param userData [in] an array of object data + * + * \return true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool addObject(PrunerCompoundId compoundId, PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload userData) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Query functions + * + * Note: return value may disappear if PrunerCallback contains the necessary information + * currently it is still used for the dynamic pruner internally (to decide if added objects must be queried) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&, PxQueryFlags flags) const = 0; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&, PxQueryFlags flags) const = 0; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&, PxQueryFlags flags) const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Retrieve the object data associated with the handle + * + * \param handle [in] The handle returned by addObjects() + * \param compoundId [in] The compound id + * + * \return A reference to the object data + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const PrunerPayload& getPayload(PrunerHandle handle, PrunerCompoundId compoundId) const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Retrieve the object data associated with the handle, plus the destination address for its matrix. The user is then expected to write the new AABB there. + * + * \param handle [in] The handle returned by addObjects() + * \param compoundId [in] The compound id + * \param bounds [out] destination address for this object's bounds + * + * \return A reference to the object data + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const PrunerPayload& getPayload(PrunerHandle handle, PrunerCompoundId compoundId, PxBounds3*& bounds) const = 0; + + + // shift the origin of the pruner objects + virtual void shiftOrigin(const PxVec3& shift) = 0; + + virtual ~CompoundPruner() {} + + // additional 'internal' interface + virtual void visualize(Cm::RenderOutput&, PxU32) const {} + +}; + + +////////////////////////////////////////////////////////////////////////// +/** +* Creates AABBPruner +*/ +////////////////////////////////////////////////////////////////////////// +IncrementalPruner* createAABBPruner(bool incrementalRebuild); + +} + +} + +#endif // SQ_PRUNER_H diff --git a/src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h b/src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h new file mode 100644 index 000000000..5e1a9e0a5 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h @@ -0,0 +1,62 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SQ_PRUNER_MERGE_DATA +#define SQ_PRUNER_MERGE_DATA +/** \addtogroup physics +@{ */ + +#include "CmPhysXCommon.h" + +namespace physx +{ + namespace Sq + { + class AABBTreeRuntimeNode; + + struct AABBPrunerMergeData + { + AABBPrunerMergeData(PxU32 nbNodes, const AABBTreeRuntimeNode* nodes, PxU32 nbObjects, const PxU32* indices) + : mNbNodes(nbNodes), mAABBTreeNodes(nodes), mNbObjects(nbObjects), mAABBTreeIndices(indices) + { + } + + PxU32 mNbNodes; // Nb nodes in AABB tree + const AABBTreeRuntimeNode* mAABBTreeNodes; // AABB tree runtime nodes + PxU32 mNbObjects; // Nb objects in AABB tree + const PxU32* mAABBTreeIndices; // AABB tree indices + }; + + } // namespace Sq + +} + +/** @} */ +#endif // SQ_PRUNING_STRUCTURE diff --git a/src/PhysX/physx/source/scenequery/include/SqPruningStructure.h b/src/PhysX/physx/source/scenequery/include/SqPruningStructure.h new file mode 100644 index 000000000..259a28daa --- /dev/null +++ b/src/PhysX/physx/source/scenequery/include/SqPruningStructure.h @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SQ_PRUNING_STRUCTURE +#define SQ_PRUNING_STRUCTURE +/** \addtogroup physics +@{ */ + +#include "CmPhysXCommon.h" + +#include "PxPruningStructure.h" + +#include "PsUserAllocated.h" + +namespace physx +{ + namespace Sq + { + class AABBTreeRuntimeNode; + + struct PruningIndex + { + enum Enum + { + eSTATIC = 0, + eDYNAMIC = 1, + + eCOUNT = 2 + }; + }; + + class PruningStructure : public PxPruningStructure, public Ps::UserAllocated + { + PX_NOCOPY(PruningStructure) + public: + // PX_SERIALIZATION + PruningStructure(PxBaseFlags baseFlags); + virtual void resolveReferences(PxDeserializationContext& ); + static PruningStructure* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext&); + virtual void requiresObjects(PxProcessPxBaseCallback&); + //~PX_SERIALIZATION + + // PX_PRUNING_STRUCTURE + virtual PxU32 getRigidActors(PxRigidActor** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + virtual PxU32 getNbRigidActors() const { return mNbActors; } + void release(); + // ~PX_PRUNING_STRUCTURE + + PruningStructure(); + ~PruningStructure(); + + bool build(PxRigidActor*const* actors, PxU32 nbActors); + + PX_FORCE_INLINE PxU32 getNbActors() const { return mNbActors; } + PX_FORCE_INLINE PxActor*const* getActors() const { return mActors; } + + PX_FORCE_INLINE AABBTreeRuntimeNode* getTreeNodes(PruningIndex::Enum currentTree) const { return mAABBTreeNodes[currentTree]; } + PX_FORCE_INLINE PxU32 getTreeNbNodes(PruningIndex::Enum currentTree) const { return mNbNodes[currentTree]; } + + PX_FORCE_INLINE PxU32* getTreeIndices(PruningIndex::Enum currentTree) const { return mAABBTreeIndices[currentTree]; } + PX_FORCE_INLINE PxU32 getNbObjects(PruningIndex::Enum currentTree) const { return mNbObjects[currentTree]; } + + PX_FORCE_INLINE bool isValid() const { return mValid; } + void invalidate(PxActor* actor); + + private: + PxU32 mNbNodes[2]; // Nb nodes in AABB tree + AABBTreeRuntimeNode* mAABBTreeNodes[2]; // AABB tree runtime nodes + PxU32 mNbObjects[2]; // Nb objects in AABB tree + PxU32* mAABBTreeIndices[2]; // AABB tree indices + PxU32 mNbActors; // Nb actors from which the pruner structure was build + PxActor** mActors; // actors used for pruner structure build, used later for serialization + bool mValid; // pruning structure validity + }; + } // namespace Sq + +} + +/** @} */ +#endif // SQ_PRUNING_STRUCTURE diff --git a/src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h b/src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h new file mode 100644 index 000000000..977eb4092 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h @@ -0,0 +1,221 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCENEQUERYMANAGER +#define PX_PHYSICS_SCENEQUERYMANAGER +/** \addtogroup physics +@{ */ + +#include "PxSceneDesc.h" +#include "CmBitMap.h" +#include "PsArray.h" +#include "SqPruner.h" +#include "PsMutex.h" +#include "PxActor.h" // needed for offset table +#include "ScScene.h" +// threading +#include "PsSync.h" + +namespace physx +{ +namespace Scb +{ + class Scene; + class Shape; + class Actor; +} + +namespace Gu +{ + class BVHStructure; +} + +namespace Sq +{ + typedef size_t PrunerData; + #define SQ_INVALID_PRUNER_DATA 0xffffffff + + struct PrunerPayload; + class Pruner; + class CompoundPruner; + + // PT: extended pruner structure. We might want to move the additional data to the pruner itself later. + struct PrunerExt + { + PrunerExt(); + ~PrunerExt(); + + void init(PxPruningStructureType::Enum type, PxU64 contextID, PxU32 sceneLimit); + void flushMemory(); + void preallocate(PxU32 nbShapes); + void flushShapes(PxU32 index); + + void addToDirtyList(PrunerHandle handle); + Ps::IntBool isDirty(PrunerHandle handle) const; + void removeFromDirtyList(PrunerHandle handle); + void growDirtyList(PrunerHandle handle); + + PX_FORCE_INLINE PxPruningStructureType::Enum type() const { return mPrunerType; } + PX_FORCE_INLINE const Pruner* pruner() const { return mPruner; } + PX_FORCE_INLINE Pruner* pruner() { return mPruner; } + PX_FORCE_INLINE PxU32 timestamp() const { return mTimestamp; } + PX_FORCE_INLINE void invalidateTimestamp() { mTimestamp++; } + + private: + Pruner* mPruner; + Cm::BitMap mDirtyMap; + Ps::Array mDirtyList; + PxPruningStructureType::Enum mPrunerType; + PxU32 mTimestamp; + + PX_NOCOPY(PrunerExt) + + friend class SceneQueryManager; + }; + + typedef Ps::Pair CompoundPair; + typedef Ps::CoalescedHashSet CompoundPrunerSet; + // AB: extended compoud pruner structure, buffers compound shape changes and flushes them. + struct CompoundPrunerExt + { + CompoundPrunerExt(); + ~CompoundPrunerExt(); + + void flushMemory(); + void preallocate(PxU32 nbShapes); + void flushShapes(); + + void addToDirtyList(PrunerCompoundId compoundId, PrunerHandle handle); + Ps::IntBool isDirty(PrunerCompoundId compoundId, PrunerHandle handle) const; + void removeFromDirtyList(PrunerCompoundId compoundId, PrunerHandle handle); + + PX_FORCE_INLINE const CompoundPruner* pruner() const { return mPruner; } + PX_FORCE_INLINE CompoundPruner* pruner() { return mPruner; } + + private: + CompoundPruner* mPruner; + CompoundPrunerSet mDirtyList; + + PX_NOCOPY(CompoundPrunerExt) + + friend class SceneQueryManager; + }; + + + struct DynamicBoundsSync : public Sc::SqBoundsSync + { + virtual void sync(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds, PxU32 count, const Cm::BitMap& dirtyShapeSimMap); + + Pruner* mPruner; + PxU32* mTimestamp; + }; + + class SceneQueryManager : public Ps::UserAllocated + { + PX_NOCOPY(SceneQueryManager) + public: + SceneQueryManager(Scb::Scene& scene, PxPruningStructureType::Enum staticStructure, + PxPruningStructureType::Enum dynamicStructure, PxU32 dynamicTreeRebuildRateHint, + const PxSceneLimits& limits); + ~SceneQueryManager(); + + PrunerData addPrunerShape(const Scb::Shape& scbShape, const Scb::Actor& scbActor, bool dynamic, PrunerCompoundId compoundId, const PxBounds3* bounds=NULL, bool hasPrunerStructure = false); + void removePrunerShape(PrunerCompoundId compoundId, PrunerData shapeData); + const PrunerPayload& getPayload(PrunerCompoundId compoundId, PrunerData shapeData) const; + + void addPruningStructure(const Sq::PruningStructure& ps); + void addCompoundShape(const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& compoundTransform, PrunerData* prunerData, const Scb::Shape** scbShapes, const Scb::Actor& scbActor); + + public: + PX_FORCE_INLINE Scb::Scene& getScene() const { return mScene; } + PX_FORCE_INLINE PxU32 getDynamicTreeRebuildRateHint() const { return mRebuildRateHint; } + + PX_FORCE_INLINE const PrunerExt& get(PruningIndex::Enum index) const { return mPrunerExt[index]; } + PX_FORCE_INLINE PrunerExt& get(PruningIndex::Enum index) { return mPrunerExt[index]; } + + PX_FORCE_INLINE const CompoundPrunerExt& getCompoundPruner() const { return mCompoundPrunerExt; } + + void preallocate(PxU32 staticShapes, PxU32 dynamicShapes); + void markForUpdate(PrunerCompoundId compoundId, PrunerData s); + void setDynamicTreeRebuildRateHint(PxU32 dynTreeRebuildRateHint); + + void flushUpdates(); + void forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure); + void sceneQueryBuildStep(PruningIndex::Enum index); + + void updateCompoundActors(Sc::BodyCore*const* bodies, PxU32 numBodies); + void updateCompoundActor(PrunerCompoundId compoundId, const PxTransform& compoundTransform, bool dynamic); + void removeCompoundActor(PrunerCompoundId compoundId, bool dynamic); + + DynamicBoundsSync& getDynamicBoundsSync() { return mDynamicBoundsSync; } + + bool prepareSceneQueriesUpdate(PruningIndex::Enum index); + + // Force a rebuild of the aabb/loose octree etc to allow raycasting on multiple threads. + void afterSync(PxSceneQueryUpdateMode::Enum updateMode); + void shiftOrigin(const PxVec3& shift); + + void flushMemory(); + private: + PrunerExt mPrunerExt[PruningIndex::eCOUNT]; + CompoundPrunerExt mCompoundPrunerExt; + + PxU32 mRebuildRateHint; + + Scb::Scene& mScene; + + // threading + shdfnd::Mutex mSceneQueryLock; // to make sure only one query updates the dirty pruner structure if multiple queries run in parallel + + DynamicBoundsSync mDynamicBoundsSync; + + volatile bool mPrunerNeedsUpdating; + + void flushShapes(); + }; + + /////////////////////////////////////////////////////////////////////////////// + + // PT: TODO: replace PrunerData with just PxU32 to save memory on Win64. Breaks binary compatibility though. + // PT: was previously called 'ActorShape' but does not contain an actor or shape pointer, contrary to the Np-level struct with the same name. + // PT: it only contains a pruner index (0 or 1) and a pruner handle. Hence the new name. + PX_FORCE_INLINE PrunerData createPrunerData(PxU32 index, PrunerHandle h) { return PrunerData((h << 1) | index); } + PX_FORCE_INLINE PxU32 getPrunerIndex(PrunerData data) { return PxU32(data & 1); } + PX_FORCE_INLINE PrunerHandle getPrunerHandle(PrunerData data) { return PrunerHandle(data >> 1); } + + /////////////////////////////////////////////////////////////////////////////// + + +} // namespace Sq + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp new file mode 100644 index 000000000..f61611555 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp @@ -0,0 +1,848 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "common/PxProfileZone.h" +#include "PsIntrinsics.h" +#include "PsUserAllocated.h" +#include "PsBitUtils.h" +#include "PsFoundation.h" +#include "SqAABBPruner.h" +#include "SqAABBTree.h" +#include "SqPrunerMergeData.h" +#include "GuSphere.h" +#include "GuBox.h" +#include "GuCapsule.h" +#include "GuAABBTreeQuery.h" +#include "GuBounds.h" + +using namespace physx; +using namespace Gu; +using namespace Sq; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +IncrementalPruner* physx::Sq::createAABBPruner(bool incrementalRebuild) +{ + return PX_NEW(Sq::AABBPruner)(incrementalRebuild, 0); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: currently limited to 15 max +#define NB_OBJECTS_PER_NODE 4 + +AABBPruner::AABBPruner(bool incrementalRebuild, PxU64 contextID) : + mAABBTree (NULL), + mNewTree (NULL), + mCachedBoxes (NULL), + mNbCachedBoxes (0), + mNbCalls (0), + mTimeStamp (0), + mBucketPruner (&mPool), + mProgress (BUILD_NOT_STARTED), + mRebuildRateHint (100), + mAdaptiveRebuildTerm(0), + mIncrementalRebuild (incrementalRebuild), + mUncommittedChanges (false), + mNeedsNewTree (false), + mNewTreeFixups (PX_DEBUG_EXP("AABBPruner::mNewTreeFixups")), + mContextID (contextID) +{ +} + +AABBPruner::~AABBPruner() +{ + release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Add, Remove, Update methods + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool AABBPruner::addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* payload, PxU32 count, bool hasPruningStructure) +{ + PX_PROFILE_ZONE("SceneQuery.prunerAddObjects", mContextID); + + if(!count) + return true; + + // no need to do refitMarked for added objects since they are not in the tree + + // if we have provided pruning structure, we will merge it, the changes will be applied after the objects has been addded + if(!hasPruningStructure || !mAABBTree) + mUncommittedChanges = true; + + // PT: TODO: 'addObjects' for bucket pruner too. Not urgent since we always call the function with count=1 at the moment + const PxU32 valid = mPool.addObjects(results, bounds, payload, count); + + // Bucket pruner is only used while the dynamic pruner is rebuilding + // For the static pruner a full rebuild will happen in commit() every time we modify something, this is not true if + // pruning structure was provided. The objects tree will be merged directly into the static tree. No rebuild will be triggered. + if(mIncrementalRebuild && mAABBTree) + { + mNeedsNewTree = true; // each add forces a tree rebuild + + // if a pruner structure is provided, we dont move the new objects into bucket pruner + // the pruning structure will be merged into the bucket pruner + if(!hasPruningStructure) + { + for(PxU32 i=0;imarkNodeForRefit(treeNodeIndex); + else // otherwise it means it should be in the bucket pruner + { + bool found = mBucketPruner.updateObject(newBounds[poolIndex], payloads[poolIndex], poolIndex); + PX_UNUSED(found); PX_ASSERT(found); + } + + if(mProgress==BUILD_NEW_MAPPING || mProgress==BUILD_FULL_REFIT) + mToRefit.pushBack(poolIndex); + } + } +} + +void AABBPruner::updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count) +{ + PX_PROFILE_ZONE("SceneQuery.prunerUpdateObjects", mContextID); + + if(!count) + return; + + mUncommittedChanges = true; + + mPool.updateObjectsAndInflateBounds(handles, indices, newBounds, count); + + if(mIncrementalRebuild && mAABBTree) + { + mNeedsNewTree = true; // each update forces a tree rebuild + PrunerPayload* payloads = mPool.getObjects(); + for(PxU32 i=0; imarkNodeForRefit(treeNodeIndex); + else // otherwise it means it should be in the bucket pruner + { + // PT: TODO: is this line correct? +// bool found = mBucketPruner.updateObject(newBounds[indices[i]], mPool.getPayload(handles[i])); + PX_ASSERT(&payloads[poolIndex]==&mPool.getPayload(handles[i])); + // PT: TODO: don't we need to read the pool's array here, to pass the inflated bounds? + bool found = mBucketPruner.updateObject(newBounds[indices[i]], payloads[poolIndex], poolIndex); + PX_UNUSED(found); PX_ASSERT(found); + } + + if(mProgress == BUILD_NEW_MAPPING || mProgress == BUILD_FULL_REFIT) + mToRefit.pushBack(poolIndex); + } + } +} + +void AABBPruner::removeObjects(const PrunerHandle* handles, PxU32 count) +{ + PX_PROFILE_ZONE("SceneQuery.prunerRemoveObjects", mContextID); + + if(!count) + return; + + mUncommittedChanges = true; + + for(PxU32 i=0; imarkNodeForRefit(treeNodeIndex); // mark the spot as blank + mBucketPruner.swapIndex(poolIndex, swappedPayload, poolRelocatedLastIndex); // if swapped index is in bucket pruner + } + else + { + PX_ASSERT(treeNodeIndex==INVALID_PRUNERHANDLE); + PxU32 timeStamp; + bool status = mBucketPruner.removeObject(removedPayload, poolIndex, swappedPayload, poolRelocatedLastIndex, timeStamp); + PX_ASSERT(status); + PX_UNUSED(status); + } + + mTreeMap.invalidate(poolIndex, poolRelocatedLastIndex, *mAABBTree); + if(mNewTree) + mNewTreeFixups.pushBack(NewTreeFixup(poolIndex, poolRelocatedLastIndex)); + } + } + + if (mPool.getNbActiveObjects()==0) + { + // this is just to make sure we release all the internal data once all the objects are out of the pruner + // since this is the only place we know that and we don't want to keep memory reserved + release(); + + // Pruner API requires a commit before the next query, even if we ended up removing the entire tree here. This + // forces that to happen. + mUncommittedChanges = true; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Query Implementation + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxAgain AABBPruner::overlap(const ShapeData& queryVolume, PrunerCallback& pcb) const +{ + PX_ASSERT(!mUncommittedChanges); + + PxAgain again = true; + + if(mAABBTree) + { + switch(queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if(queryVolume.isOBB()) + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + else + { + const Gu::AABBAABBTest test(queryVolume.getPrunerInflatedWorldAABB()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + } + break; + case PxGeometryType::eCAPSULE: + { + const Gu::Capsule& capsule = queryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest test( capsule.p1, queryVolume.getPrunerWorldRot33().column0, + queryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::eSPHERE: + { + const Gu::Sphere& sphere = queryVolume.getGuSphere(); + Gu::SphereAABBTest test(sphere.center, sphere.radius); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::eCONVEXMESH: + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + } + + if(again && mIncrementalRebuild && mBucketPruner.getNbObjects()) + again = mBucketPruner.overlap(queryVolume, pcb); + + return again; +} + +PxAgain AABBPruner::sweep(const ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PX_ASSERT(!mUncommittedChanges); + + PxAgain again = true; + + if(mAABBTree) + { + const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB(); + const PxVec3 extents = aabb.getExtents(); + again = AABBTreeRaycast()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, aabb.getCenter(), unitDir, inOutDistance, extents, pcb); + } + + if(again && mIncrementalRebuild && mBucketPruner.getNbObjects()) + again = mBucketPruner.sweep(queryVolume, unitDir, inOutDistance, pcb); + + return again; +} + +PxAgain AABBPruner::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PX_ASSERT(!mUncommittedChanges); + + PxAgain again = true; + + if(mAABBTree) + again = AABBTreeRaycast()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, origin, unitDir, inOutDistance, PxVec3(0.0f), pcb); + + if(again && mIncrementalRebuild && mBucketPruner.getNbObjects()) + again = mBucketPruner.raycast(origin, unitDir, inOutDistance, pcb); + + return again; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Other methods of Pruner Interface + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// This isn't part of the pruner virtual interface, but it is part of the public interface +// of AABBPruner - it gets called by SqManager to force a rebuild, and requires a commit() before +// queries can take place + +void AABBPruner::purge() +{ + release(); + mUncommittedChanges = true; // this ensures a commit() must happen before any query +} + +void AABBPruner::setRebuildRateHint(PxU32 nbStepsForRebuild) +{ + PX_ASSERT(nbStepsForRebuild > 3); + mRebuildRateHint = (nbStepsForRebuild-3); // looks like a magic number to account for the rebuild pipeline latency + mAdaptiveRebuildTerm = 0; +} + +// Commit either performs a refit if background rebuild is not yet finished +// or swaps the current tree for the second tree rebuilt in the background +void AABBPruner::commit() +{ + PX_PROFILE_ZONE("SceneQuery.prunerCommit", mContextID); + + if(!mUncommittedChanges && (mProgress != BUILD_FINISHED)) + // Q: seems like this is both for refit and finalization so is this is correct? + // i.e. in a situation when we started rebuilding a tree and didn't add anything since + // who is going to set mUncommittedChanges to true? + // A: it's set in buildStep at final stage, so that finalization is forced. + // Seems a bit difficult to follow and verify correctness. + return; + + mUncommittedChanges = false; + + if(!mAABBTree || !mIncrementalRebuild) + { +#if PX_CHECKED + if(!mIncrementalRebuild && mAABBTree) + Ps::getFoundation().error(PxErrorCode::ePERF_WARNING, __FILE__, __LINE__, "SceneQuery static AABB Tree rebuilt, because a shape attached to a static actor was added, removed or moved, and PxSceneDesc::staticStructure is set to eSTATIC_AABB_TREE."); +#endif + fullRebuildAABBTree(); + return; + } + + // Note: it is not safe to call AABBPruner::build() here + // because the first thread will perform one step of the incremental update, + // continue raycasting, while the second thread performs the next step in + // the incremental update + + // Calling Refit() below is safe. It will call + // StaticPruner::build() when necessary. Both will early + // exit if the tree is already up to date, if it is not already, then we + // must be the first thread performing raycasts on a dirty tree and other + // scene query threads will be locked out by the write lock in + // SceneQueryManager::flushUpdates() + + + if (mProgress != BUILD_FINISHED) + { + // Calling refit because the second tree is not ready to be swapped in (mProgress != BUILD_FINISHED) + // Generally speaking as long as things keep moving the second build will never catch up with true state + refitUpdatedAndRemoved(); + } + else + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeFinalize", mContextID); + + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeSwitch", mContextID); + + PX_DELETE(mAABBTree); // delete the old tree + PX_FREE_AND_RESET(mCachedBoxes); + mProgress = BUILD_NOT_STARTED; // reset the build state to initial + + // Adjust adaptive term to get closer to specified rebuild rate. + // perform an even division correction to make sure the rebuild rate adds up + if (mNbCalls > mRebuildRateHint) + mAdaptiveRebuildTerm++; + else if (mNbCalls < mRebuildRateHint) + mAdaptiveRebuildTerm--; + + // Switch trees +#if PX_DEBUG + mNewTree->validate(); +#endif + mAABBTree = mNewTree; // set current tree to progressively rebuilt tree + mNewTree = NULL; // clear out the progressively rebuild tree pointer + } + + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeMapping", mContextID); + + // rebuild the tree map to match the current (newly built) tree + mTreeMap.initMap(PxMax(mPool.getNbActiveObjects(), mNbCachedBoxes), *mAABBTree); + + // The new mapping has been computed using only indices stored in the new tree. Those indices map the pruning pool + // we had when starting to build the tree. We need to re-apply recorded moves to fix the tree that finished rebuilding. + // AP: the problem here is while we are rebuilding the tree there are ongoing modifications to the current tree + // but the background build has a cached copy of all the AABBs at the time it was started + // (and will produce indices referencing those) + // Things that can happen in the meantime: update, remove, add, commit + for(NewTreeFixup* r = mNewTreeFixups.begin(); r < mNewTreeFixups.end(); r++) + { + // PT: we're not doing a full refit after this point anymore, so the remaining deleted objects must be manually marked for + // refit (otherwise their AABB in the tree would remain valid, leading to crashes when the corresponding index is 0xffffffff). + // We must do this before invalidating the corresponding tree nodes in the map, obviously (otherwise we'd be reading node + // indices that we already invalidated). + const PoolIndex poolIndex = r->removedIndex; + const TreeNodeIndex treeNodeIndex = mTreeMap[poolIndex]; + if(treeNodeIndex!=INVALID_NODE_ID) + mAABBTree->markNodeForRefit(treeNodeIndex); + + mTreeMap.invalidate(r->removedIndex, r->relocatedLastIndex, *mAABBTree); + } + mNewTreeFixups.clear(); // clear out the fixups since we just applied them all + } + + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeFinalRefit", mContextID); + + const PxU32 size = mToRefit.size(); + for(PxU32 i=0;imarkNodeForRefit(treeNodeIndex); + } + mToRefit.clear(); + refitUpdatedAndRemoved(); + } + + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeRemoveObjects", mContextID); + + PxU32 nbRemovedPairs = mBucketPruner.removeMarkedObjects(mTimeStamp-1); + PX_UNUSED(nbRemovedPairs); + + mNeedsNewTree = mBucketPruner.getNbObjects()>0; + } + } + + updateBucketPruner(); +} + + +void AABBPruner::shiftOrigin(const PxVec3& shift) +{ + mPool.shiftOrigin(shift); + + if(mAABBTree) + mAABBTree->shiftOrigin(shift); + + if(mIncrementalRebuild) + mBucketPruner.shiftOrigin(shift); + + if(mNewTree) + mNewTree->shiftOrigin(shift); +} + +#include "CmRenderOutput.h" +void AABBPruner::visualize(Cm::RenderOutput& out, PxU32 color) const +{ + // getAABBTree() asserts when pruner is dirty. NpScene::visualization() does not enforce flushUpdate. see DE7834 + const AABBTree* tree = mAABBTree; + + if(tree && tree->getNodes()) + { + struct Local + { + static void _Draw(const AABBTreeRuntimeNode* root, const AABBTreeRuntimeNode* node, Cm::RenderOutput& out_) + { + out_ << Cm::DebugBox(node->mBV, true); + if (node->isLeaf()) + return; + _Draw(root, node->getPos(root), out_); + _Draw(root, node->getNeg(root), out_); + } + }; + out << PxTransform(PxIdentity); + out << color; + Local::_Draw(tree->getNodes(), tree->getNodes(), out); + } + + // Render added objects not yet in the tree + out << PxTransform(PxIdentity); + out << PxU32(PxDebugColor::eARGB_WHITE); + + if(mIncrementalRebuild && mBucketPruner.getNbObjects()) + mBucketPruner.visualize(out, color); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Internal methods + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool AABBPruner::buildStep(bool synchronousCall) +{ + PX_PROFILE_ZONE("SceneQuery.prunerBuildStep", mContextID); + + PX_ASSERT(mIncrementalRebuild); + if(mNeedsNewTree) + { + if(mProgress==BUILD_NOT_STARTED) + { + if(!synchronousCall || !prepareBuild()) + return false; + } + else if(mProgress==BUILD_INIT) + { + mNewTree->progressiveBuild(mBuilder, mBuildStats, 0, 0); + mProgress = BUILD_IN_PROGRESS; + mNbCalls = 0; + + // Use a heuristic to estimate the number of work units needed for rebuilding the tree. + // The general idea is to use the number of work units of the previous tree to build the new tree. + // This works fine as long as the number of leaves remains more or less the same for the old and the + // new tree. If that is not the case, this estimate can be way off and the work units per step will + // be either much too small or too large. Hence, in that case we will try to estimate the number of work + // units based on the number of leaves of the new tree as follows: + // + // - Assume new tree with n leaves is perfectly-balanced + // - Compute the depth of perfectly-balanced tree with n leaves + // - Estimate number of working units for the new tree + + const PxU32 depth = Ps::ilog2(mBuilder.mNbPrimitives); // Note: This is the depth without counting the leaf layer + const PxU32 estimatedNbWorkUnits = depth * mBuilder.mNbPrimitives; // Estimated number of work units for new tree + const PxU32 estimatedNbWorkUnitsOld = mAABBTree ? mAABBTree->getTotalPrims() : 0; + if ((estimatedNbWorkUnits <= (estimatedNbWorkUnitsOld << 1)) && (estimatedNbWorkUnits >= (estimatedNbWorkUnitsOld >> 1))) + // The two estimates do not differ by more than a factor 2 + mTotalWorkUnits = estimatedNbWorkUnitsOld; + else + { + mAdaptiveRebuildTerm = 0; + mTotalWorkUnits = estimatedNbWorkUnits; + } + + const PxI32 totalWorkUnits = PxI32(mTotalWorkUnits + (mAdaptiveRebuildTerm * mBuilder.mNbPrimitives)); + mTotalWorkUnits = PxU32(PxMax(totalWorkUnits, 0)); + } + else if(mProgress==BUILD_IN_PROGRESS) + { + mNbCalls++; + const PxU32 Limit = 1 + (mTotalWorkUnits / mRebuildRateHint); + // looks like progressiveRebuild returns 0 when finished + if (!mNewTree->progressiveBuild(mBuilder, mBuildStats, 1, Limit)) + { + // Done + mProgress = BUILD_NEW_MAPPING; +#if PX_DEBUG + mNewTree->validate(); +#endif + } + } + else if(mProgress==BUILD_NEW_MAPPING) + { + mNbCalls++; + mProgress = BUILD_FULL_REFIT; + + // PT: we can't call fullRefit without creating the new mapping first: the refit function will fetch boxes from + // the pool using "primitive indices" captured in the tree. But some of these indices may have been invalidated + // if objects got removed while the tree was built. So we need to invalidate the corresponding nodes before refit, + // that way the #prims will be zero and the code won't fetch a wrong box (which may now below to a different object). + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeMapping", mContextID); + + if(mNewTreeFixups.size()) + { + mNewTreeMap.initMap(PxMax(mPool.getNbActiveObjects(), mNbCachedBoxes), *mNewTree); + + // The new mapping has been computed using only indices stored in the new tree. Those indices map the pruning pool + // we had when starting to build the tree. We need to re-apply recorded moves to fix the tree. + for(NewTreeFixup* r = mNewTreeFixups.begin(); r < mNewTreeFixups.end(); r++) + mNewTreeMap.invalidate(r->removedIndex, r->relocatedLastIndex, *mNewTree); + + mNewTreeFixups.clear(); +#if PX_DEBUG + mNewTree->validate(); +#endif + } + } + } + else if(mProgress==BUILD_FULL_REFIT) + { + mNbCalls++; + mProgress = BUILD_LAST_FRAME; + + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeFullRefit", mContextID); + + // We need to refit the new tree because objects may have moved while we were building it. + mNewTree->fullRefit(mPool.getCurrentWorldBoxes()); + } + } + else if(mProgress==BUILD_LAST_FRAME) + { + mProgress = BUILD_FINISHED; + } + + // This is required to be set because commit handles both refit and a portion of build finalization (why?) + // This is overly conservative also only necessary in case there were no updates at all to the tree since the last tree swap + // It also overly conservative in a sense that it could be set only if mProgress was just set to BUILD_FINISHED + // If run asynchronously from a different thread, we touched just the new AABB build phase, we should not mark the main tree as dirty + if(synchronousCall) + mUncommittedChanges = true; + + return mProgress==BUILD_FINISHED; + } + + return false; +} + +bool AABBPruner::prepareBuild() +{ + PX_PROFILE_ZONE("SceneQuery.prepareBuild", mContextID); + + PX_ASSERT(mIncrementalRebuild); + if(mNeedsNewTree) + { + if(mProgress==BUILD_NOT_STARTED) + { + const PxU32 nbObjects = mPool.getNbActiveObjects(); + if(!nbObjects) + return false; + + PX_DELETE(mNewTree); + mNewTree = PX_NEW(AABBTree); + + mNbCachedBoxes = nbObjects; + // PT: we always allocate one extra box, to make sure we can safely use V4 loads on the array + mCachedBoxes = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(nbObjects+1), "PxBound3")); + + PxMemCopy(mCachedBoxes, mPool.getCurrentWorldBoxes(), nbObjects*sizeof(PxBounds3)); + + // PT: objects currently in the bucket pruner will be in the new tree. They are marked with the + // current timestamp (mTimeStamp). However more objects can get added while we compute the new tree, + // and those ones will not be part of it. These new objects will be marked with the new timestamp + // value (mTimeStamp+1), and we can use these different values to remove the proper objects from + // the bucket pruner (when switching to the new tree). + mTimeStamp++; +#if USE_INCREMENTAL_PRUNER + // notify the incremental pruner to swap trees + mBucketPruner.timeStampChange(); +#endif + mBuilder.reset(); + mBuilder.mNbPrimitives = mNbCachedBoxes; + mBuilder.mAABBArray = mCachedBoxes; + mBuilder.mLimit = NB_OBJECTS_PER_NODE; + + mBuildStats.reset(); + + // start recording modifications to the tree made during rebuild to reapply (fix the new tree) eventually + PX_ASSERT(mNewTreeFixups.size()==0); + + mProgress = BUILD_INIT; + } + } + else + return false; + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds an AABB-tree for objects in the pruning pool. + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBPruner::fullRebuildAABBTree() +{ + PX_PROFILE_ZONE("SceneQuery.prunerFullRebuildAABBTree", mContextID); + + // Release possibly already existing tree + PX_DELETE_AND_RESET(mAABBTree); + + // Don't bother building an AABB-tree if there isn't a single static object + const PxU32 nbObjects = mPool.getNbActiveObjects(); + if(!nbObjects) + return true; + + bool Status; + { + // Create a new tree + mAABBTree = PX_NEW(AABBTree); + + AABBTreeBuildParams TB; + TB.mNbPrimitives = nbObjects; + TB.mAABBArray = mPool.getCurrentWorldBoxes(); + TB.mLimit = NB_OBJECTS_PER_NODE; + Status = mAABBTree->build(TB); + } + + // No need for the tree map for static pruner + if(mIncrementalRebuild) + mTreeMap.initMap(PxMax(nbObjects,mNbCachedBoxes),*mAABBTree); + + return Status; +} + +// called in the end of commit(), but only if mIncrementalRebuild is true +void AABBPruner::updateBucketPruner() +{ + PX_PROFILE_ZONE("SceneQuery.prunerUpdateBucketPruner", mContextID); + + PX_ASSERT(mIncrementalRebuild); + mBucketPruner.build(); +} + +PxBounds3 AABBPruner::getAABB(PrunerHandle handle) +{ + return mPool.getWorldAABB(handle); +} + +void AABBPruner::release() // this can be called from purge() +{ + mBucketPruner.release(); + + mTimeStamp = 0; + + mTreeMap.release(); + mNewTreeMap.release(); + + PX_FREE_AND_RESET(mCachedBoxes); + mBuilder.reset(); + PX_DELETE_AND_RESET(mNewTree); + PX_DELETE_AND_RESET(mAABBTree); + + mNbCachedBoxes = 0; + mProgress = BUILD_NOT_STARTED; + mNewTreeFixups.clear(); + mUncommittedChanges = false; +} + +// Refit current tree +void AABBPruner::refitUpdatedAndRemoved() +{ + PX_PROFILE_ZONE("SceneQuery.prunerRefitUpdatedAndRemoved", mContextID); + + PX_ASSERT(mIncrementalRebuild); + AABBTree* tree = getAABBTree(); + if(!tree) + return; + +#if PX_DEBUG + tree->validate(); +#endif + + //### missing a way to skip work if not needed + + const PxU32 nbObjects = mPool.getNbActiveObjects(); + // At this point there still can be objects in the tree that are blanked out so it's an optimization shortcut (not required) + if(!nbObjects) + return; + + mBucketPruner.refitMarkedNodes(mPool.getCurrentWorldBoxes()); + tree->refitMarkedNodes(mPool.getCurrentWorldBoxes()); +} + +void AABBPruner::merge(const void* mergeParams) +{ + const AABBPrunerMergeData& pruningStructure = *reinterpret_cast (mergeParams); + + if(mAABBTree) + { + // index in pruning pool, where new objects were added + const PxU32 pruningPoolIndex = mPool.getNbActiveObjects() - pruningStructure.mNbObjects; + + // create tree from given nodes and indices + AABBTreeMergeData aabbTreeMergeParams(pruningStructure.mNbNodes, pruningStructure.mAABBTreeNodes, + pruningStructure.mNbObjects, pruningStructure.mAABBTreeIndices, pruningPoolIndex); + + if (!mIncrementalRebuild) + { + // merge tree directly + mAABBTree->mergeTree(aabbTreeMergeParams); + } + else + { + mBucketPruner.addTree(aabbTreeMergeParams, mTimeStamp); + } + } +} diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBPruner.h b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.h new file mode 100644 index 000000000..f575443db --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.h @@ -0,0 +1,269 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_AABB_PRUNER_H +#define SQ_AABB_PRUNER_H + +#include "SqPruningPool.h" +#include "SqExtendedBucketPruner.h" +#include "SqAABBTreeUpdateMap.h" +#include "SqAABBTree.h" + +namespace physx +{ + +namespace Sq +{ + // PT: we build the new tree over a number of frames/states, in order to limit perf spikes in 'updatePruningTrees'. + // The states are as follows: + // + // BUILD_NOT_STARTED (1 frame, AABBPruner): + // + // This is the initial state, before the new (AABBTree) build even starts. In this frame/state, we perform the AABBPruner-related + // memory allocations: + // - the new AABB tree is allocated + // - the array of cached bounding boxes is allocated and filled + // + // BUILD_INIT (1 frame, AABBTree): + // + // This is the first frame in which the new tree gets built. It deserves its own special state since various things happen in the + // first frame, that do no happen in subsequent frames. Basically most initial AABBTree-related allocations happen here (but no + // build step per se). + // + // BUILD_IN_PROGRESS (N frames, AABBTree): + // + // This is the core build function, actually building the tree. This should be mostly allocation-free, except here and there when + // building non-complete trees, and during the last call when the tree is finally built. + // + // BUILD_NEW_MAPPING (1 frame, AABBPruner): + // + // After the new AABBTree is built, we recreate an AABBTreeUpdateMap for the new tree, and use it to invalidate nodes whose objects + // have been removed during the build. + // + // We need to do that before doing a full refit in the next stage/frame. If we don't do that, the refit code will fetch a wrong box, + // that may very well belong to an entirely new object. + // + // Note that this mapping/update map (mNewTreeMap) is temporary, and only needed for the next stage. + // + // BUILD_FULL_REFIT (1 frame, AABBPruner): + // + // Once the new update map is available, we fully refit the new tree. AABBs of moved objects get updated. AABBs of removed objects + // become empty. + // + // BUILD_LAST_FRAME (1 frame, AABBPruner): + // + // This is an artificial frame used to delay the tree switching code. The switch happens as soon as we reach the BUILD_FINISHED + // state, but we don't want to execute BUILD_FULL_REFIT and the switch in the same frame. This extra BUILD_LAST_FRAME stage buys + // us one frame, i.e. we have one frame in which we do BUILD_FULL_REFIT, and in the next frame we'll do both BUILD_LAST_FRAME / + // BUILD_FINISHED / the switch. + // + // BUILD_FINISHED (1 frame, AABBPruner): + // + // Several things happen in this 'finalization' frame/stage: + // - We switch the trees (old one is deleted, cached boxes are deleted, new tree pointer is setup) + // - A new (final) update map is created (mTreeMap). The map is used to invalidate objects that may have been removed during + // the BUILD_NEW_MAPPING and BUILD_FULL_REFIT frames. The nodes containing these removed objects are marked for refit. + // - Nodes containing objects that have moved during the BUILD_NEW_MAPPING and BUILD_FULL_REFIT frames are marked for refit. + // - We do a partial refit on the new tree, to take these final changes into account. This small partial refit is usually much + // cheaper than the full refit we previously performed here. + // - We remove old objects from the bucket pruner + // + enum BuildStatus + { + BUILD_NOT_STARTED, + BUILD_INIT, + BUILD_IN_PROGRESS, + BUILD_NEW_MAPPING, + BUILD_FULL_REFIT, + BUILD_LAST_FRAME, + BUILD_FINISHED, + + BUILD_FORCE_DWORD = 0xffffffff + }; + + // This class implements the Pruner interface for internal SQ use with some additional specialized functions + // The underlying data structure is a binary AABB tree + // AABBPruner supports insertions, removals and updates for dynamic objects + // The tree is either entirely rebuilt in a single frame (static pruner) or progressively rebuilt over multiple frames (dynamic pruner) + // The rebuild happens on a copy of the tree + // the copy is then swapped with current tree at the time commit() is called (only if mBuildState is BUILD_FINISHED), + // otherwise commit() will perform a refit operation applying any pending changes to the current tree + // While the tree is being rebuilt a temporary data structure (BucketPruner) is also kept in sync and used to speed up + // queries on updated objects that are not yet in either old or new tree. + // The requirements on the order of calls: + // commit() is required to be called before any queries to apply modifications + // queries can be issued on multiple threads after commit is called + // commit, buildStep, add/remove/update have to be called from the same thread or otherwise strictly serialized by external code + // and cannot be issued while a query is running + class AABBPruner : public IncrementalPruner + { + public: + AABBPruner(bool incrementalRebuild, PxU64 contextID); // true is equivalent to former dynamic pruner + virtual ~AABBPruner(); + + // Pruner + virtual bool addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* userData, PxU32 count, bool hasPruningStructure); + virtual void removeObjects(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count); + virtual void commit(); + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual const PrunerPayload& getPayload(PrunerHandle handle) const { return mPool.getPayload(handle); } + virtual const PrunerPayload& getPayload(PrunerHandle handle, PxBounds3*& bounds) const { return mPool.getPayload(handle, bounds); } + virtual void preallocate(PxU32 entries) { mPool.preallocate(entries); } + virtual void shiftOrigin(const PxVec3& shift); + virtual void visualize(Cm::RenderOutput& out, PxU32 color) const; + virtual void merge(const void* mergeParams); + //~Pruner + + // IncrementalPruner + virtual void purge(); // gets rid of internal accel struct + virtual void setRebuildRateHint(PxU32 nbStepsForRebuild); // Besides the actual rebuild steps, 3 additional steps are needed. + virtual bool buildStep(bool synchronousCall = true); // returns true if finished + virtual bool prepareBuild(); // returns true if new tree is needed + //~IncrementalPruner + + // direct access for test code + + PX_FORCE_INLINE PxU32 getNbAddedObjects() const { return mBucketPruner.getNbObjects(); } + PX_FORCE_INLINE const Sq::AABBTree* getAABBTree() const { PX_ASSERT(!mUncommittedChanges); return mAABBTree; } + PX_FORCE_INLINE Sq::AABBTree* getAABBTree() { PX_ASSERT(!mUncommittedChanges); return mAABBTree; } + PX_FORCE_INLINE void setAABBTree(Sq::AABBTree* tree) { mAABBTree = tree; } + PX_FORCE_INLINE const Sq::AABBTree* hasAABBTree() const { return mAABBTree; } + PX_FORCE_INLINE BuildStatus getBuildStatus() const { return mProgress; } + + // local functions +// private: + Sq::AABBTree* mAABBTree; // current active tree + Gu::AABBTreeBuildParams mBuilder; // this class deals with the details of the actual tree building + Gu::BuildStats mBuildStats; + + // tree with build in progress, assigned to mAABBTree in commit, when mProgress is BUILD_FINISHED + // created in buildStep(), BUILD_NOT_STARTED + // This is non-null when there is a tree rebuild going on in progress + // and thus also indicates that we have to start saving the fixups + Sq::AABBTree* mNewTree; + + // during rebuild the pool might change so we need a copy of boxes for the tree build + PxBounds3* mCachedBoxes; + PxU32 mNbCachedBoxes; + + // incremented in commit(), serves as a progress counter for rebuild + PxU32 mNbCalls; + + // PT: incremented each time we start building a new tree (i.e. effectively identifies a given tree) + // Timestamp is passed to bucket pruner to mark objects added there, linking them to a specific tree. + // When switching to the new tree, timestamp is used to remove old objects (now in the new tree) from + // the bucket pruner. + PxU32 mTimeStamp; + + // this pruner is used for queries on objects that are not in the current tree yet + // includes both the objects in the tree being rebuilt and all the objects added later + ExtendedBucketPruner mBucketPruner; + + BuildStatus mProgress; // current state of second tree build progress + + // Fraction (as in 1/Nth) of the total number of primitives + // that should be processed per step by the AABB builder + // so if this value is 1, all primitives will be rebuilt, 2 => 1/2 of primitives per step etc. + // see also mNbCalls, mNbCalls varies from 0 to mRebuildRateHint-1 + PxU32 mRebuildRateHint; + + // Estimate for how much work has to be done to rebuild the tree. + PxU32 mTotalWorkUnits; + + // Term to correct the work unit estimate if the rebuild rate is not matched + PxI32 mAdaptiveRebuildTerm; + + PruningPool mPool; // Pool of AABBs + + // maps pruning pool indices to aabb tree indices + // maps to INVALID_NODE_ID if the pool entry was removed or "pool index is outside input domain" + // The map is the inverse of the tree mapping: (node[map[poolID]].primitive == poolID) + // So: + // treeNodeIndex = mTreeMap.operator[](poolIndex) + // aabbTree->treeNodes[treeNodeIndex].primitives[0] == poolIndex + AABBTreeUpdateMap mTreeMap; + // Temporary update map, see BuildStatus notes above for details + AABBTreeUpdateMap mNewTreeMap; + + // This is only set once in the constructor and is equivalent to isDynamicTree + // if it set to false then a 1-shot rebuild is performed in commit() + // bucket pruner is only used with incremental rebuild + bool mIncrementalRebuild; + + // A rebuild can be triggered even when the Pruner is not dirty + // mUncommittedChanges is set to true in add, remove, update and buildStep + // mUncommittedChanges is set to false in commit + // mUncommittedChanges has to be false (commit() has to be called) in order to run a query as defined by the + // mUncommittedChanges is not set to true in add, when pruning structure is provided. Scene query shapes + // are merged to current AABB tree directly + // Pruner higher level API + bool mUncommittedChanges; + + // A new AABB tree is built if an object was added, removed or updated + // Changing objects during a build will trigger another rebuild right afterwards + // this is set to true if a new tree has to be created again after the current rebuild is done + bool mNeedsNewTree; + + // This struct is used to record modifications made to the pruner state + // while a tree is building in the background + // this is so we can apply the modifications to the tree at the time of completion + // the recorded fixup information is: removedIndex (in ::remove()) and + // lastIndexMoved which is the last index in the pruner array + // (since the way we remove from PruningPool is by swapping last into removed slot, + // we need to apply a fixup so that it syncs up that operation in the new tree) + struct NewTreeFixup + { + PX_FORCE_INLINE NewTreeFixup(PxU32 removedIndex_, PxU32 relocatedLastIndex_) + : removedIndex(removedIndex_), relocatedLastIndex(relocatedLastIndex_) {} + PxU32 removedIndex; + PxU32 relocatedLastIndex; + }; + Ps::Array mNewTreeFixups; + + Ps::Array mToRefit; + + PxU64 mContextID; + + // Internal methods + bool fullRebuildAABBTree(); // full rebuild function, used with static pruner mode + void release(); + void refitUpdatedAndRemoved(); + void updateBucketPruner(); + PxBounds3 getAABB(PrunerHandle h); + }; + +} // namespace Sq + +} + +#endif // SQ_AABB_PRUNER_H diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp b/src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp new file mode 100644 index 000000000..8faec8104 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp @@ -0,0 +1,920 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "SqAABBTree.h" +#include "SqAABBTreeUpdateMap.h" +#include "SqBounds.h" + +#include "PsMathUtils.h" +#include "PsFoundation.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Sq; +using namespace Gu; + +#define INVALID_ID 0xffffffff + +// Progressive building +class Sq::FIFOStack : public Ps::UserAllocated +{ +public: + FIFOStack() : mStack(PX_DEBUG_EXP("SQFIFOStack")), mCurIndex(0) {} + ~FIFOStack() {} + + PX_FORCE_INLINE PxU32 getNbEntries() const { return mStack.size(); } + PX_FORCE_INLINE void push(AABBTreeBuildNode* entry) { mStack.pushBack(entry); } + bool pop(AABBTreeBuildNode*& entry); +private: + Ps::Array mStack; + PxU32 mCurIndex; //!< Current index within the container +}; + +bool Sq::FIFOStack::pop(AABBTreeBuildNode*& entry) +{ + const PxU32 NbEntries = mStack.size(); // Get current number of entries + if (!NbEntries) + return false; // Can be NULL when no value has been pushed. This is an invalid pop call. + entry = mStack[mCurIndex++]; // Get oldest entry, move to next one + if (mCurIndex == NbEntries) + { + // All values have been poped + mStack.clear(); + mCurIndex = 0; + } + return true; +} +//~Progressive building + +void flatten(const NodeAllocator& nodeAllocator, AABBTreeRuntimeNode* dest) +{ + // PT: gathers all build nodes allocated so far and flatten them to a linear destination array of smaller runtime nodes + PxU32 offset = 0; + const PxU32 nbSlabs = nodeAllocator.mSlabs.size(); + for(PxU32 s=0;s= nodeAllocator.mSlabs[j].mPool && pool[i].mPos < nodeAllocator.mSlabs[j].mPool + nodeAllocator.mSlabs[j].mNbUsedNodes) + { + localNodeIndex = PxU32(pool[i].mPos - nodeAllocator.mSlabs[j].mPool); + break; + } + nodeBase += nodeAllocator.mSlabs[j].mNbUsedNodes; + } + const PxU32 nodeIndex = nodeBase + localNodeIndex; + dest[offset].mData = nodeIndex<<1; + } + offset++; + } + } +} + +AABBTree::AABBTree() : + mIndices (NULL), + mNbIndices (0), + mRuntimePool (NULL), + mParentIndices (NULL), + mTotalNbNodes (0), + mTotalPrims (0) +{ +// Progressive building + mStack = NULL; +//~Progressive building + +// REFIT + mRefitHighestSetWord = 0; +//~REFIT +} + +AABBTree::~AABBTree() +{ + release(false); +} + +void AABBTree::release(bool clearRefitMap) +{ +// Progressive building + PX_DELETE_AND_RESET(mStack); +//~Progressive building + PX_FREE_AND_RESET(mParentIndices); + PX_DELETE_ARRAY(mRuntimePool); + mNodeAllocator.release(); + PX_FREE_AND_RESET(mIndices); + mTotalNbNodes = 0; + mNbIndices = 0; + +// REFIT + if(clearRefitMap) + mRefitBitmask.clearAll(); + mRefitHighestSetWord = 0; +//~REFIT +} + +// Initialize nodes/indices from the input tree merge data +void AABBTree::initTree(const AABBTreeMergeData& tree) +{ + PX_ASSERT(mIndices == NULL); + PX_ASSERT(mRuntimePool == NULL); + PX_ASSERT(mParentIndices == NULL); + + // allocate,copy indices + mIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*tree.mNbIndices, "AABB tree indices")); + mNbIndices = tree.mNbIndices; + PxMemCopy(mIndices, tree.mIndices, sizeof(PxU32)*tree.mNbIndices); + + // allocate,copy nodes + mRuntimePool = PX_NEW(AABBTreeRuntimeNode)[tree.mNbNodes]; + mTotalNbNodes = tree.mNbNodes; + PxMemCopy(mRuntimePool, tree.mNodes, sizeof(AABBTreeRuntimeNode)*tree.mNbNodes); +} + +// Shift indices of the tree by offset. Used for merged trees, when initial indices needs to be shifted to match indices in current pruning pool +void AABBTree::shiftIndices(PxU32 offset) +{ + for (PxU32 i = 0; i < mNbIndices; i++) + { + mIndices[i] += offset; + } +} + +bool AABBTree::buildInit(AABBTreeBuildParams& params, BuildStats& stats) +{ + // Checkings + const PxU32 nbPrimitives = params.mNbPrimitives; + if(!nbPrimitives) + return false; + + // Release previous tree + release(); + + // Initialize indices. This list will be modified during build. + mNbIndices = nbPrimitives; + return initAABBTreeBuild(params, mNodeAllocator, stats, mIndices); +} + +void AABBTree::buildEnd(AABBTreeBuildParams& params, BuildStats& stats) +{ + PX_FREE_AND_RESET(params.mCache); + // Get back total number of nodes + mTotalNbNodes = stats.getCount(); + mTotalPrims = stats.mTotalPrims; + + mRuntimePool = PX_NEW(AABBTreeRuntimeNode)[mTotalNbNodes]; + PX_ASSERT(mTotalNbNodes==mNodeAllocator.mTotalNbNodes); + flatten(mNodeAllocator, mRuntimePool); + mNodeAllocator.release(); +} + +bool AABBTree::build(AABBTreeBuildParams& params) +{ + const PxU32 nbPrimitives = params.mNbPrimitives; + if(!nbPrimitives) + return false; + + // Release previous tree + release(); + + BuildStats stats; + mNbIndices = nbPrimitives; + + const bool buildStatus = buildAABBTree(params, mNodeAllocator, stats, mIndices); + PX_UNUSED(buildStatus); + PX_ASSERT(buildStatus); + + buildEnd(params, stats); + return true; +} + +void AABBTree::shiftOrigin(const PxVec3& shift) +{ + AABBTreeRuntimeNode* const nodeBase = mRuntimePool; + const PxU32 totalNbNodes = mTotalNbNodes; + for(PxU32 i=0; isubdivide(params, stats, nodeBase, indices); + + if(!node->isLeaf()) + { + AABBTreeBuildNode* pos = const_cast(node->getPos()); + PX_ASSERT(pos); + AABBTreeBuildNode* neg = pos + 1; + stack.push(neg); + stack.push(pos); + } + + stats.mTotalPrims += node->mNbPrimitives; + return node->mNbPrimitives; +} + +PxU32 AABBTree::progressiveBuild(AABBTreeBuildParams& params, BuildStats& stats, PxU32 progress, PxU32 limit) +{ + if(progress==0) + { + if(!buildInit(params, stats)) + return PX_INVALID_U32; + + mStack = PX_NEW(FIFOStack); + mStack->push(mNodeAllocator.mPool); + return progress++; + } + else if(progress==1) + { + PxU32 stackCount = mStack->getNbEntries(); + if(stackCount) + { + PxU32 Total = 0; + const PxU32 Limit = limit; + while(Totalpop(Entry)) + Total += incrementalBuildHierarchy(*mStack, Entry, params, stats, mNodeAllocator, mIndices); + else + break; + } + return progress; + } + + buildEnd(params, stats); + + PX_DELETE_AND_RESET(mStack); + + return 0; // Done! + } + return PX_INVALID_U32; +} +//~Progressive building + + + +static PX_FORCE_INLINE PxU32 BitsToDwords(PxU32 nb_bits) +{ + return (nb_bits>>5) + ((nb_bits&31) ? 1 : 0); +} + +bool Sq::BitArray::init(PxU32 nb_bits) +{ + mSize = BitsToDwords(nb_bits); + // Get ram for n bits + PX_FREE(mBits); + mBits = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mSize, "BitArray::mBits")); + // Set all bits to 0 + clearAll(); + return true; +} + +void Sq::BitArray::resize(PxU32 maxBitNumber) +{ + const PxU32 newSize = BitsToDwords(maxBitNumber); + if (newSize <= mSize) + return; + + PxU32* newBits = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*newSize, "BitArray::mBits")); + PxMemZero(newBits + mSize, (newSize - mSize) * sizeof(PxU32)); + PxMemCopy(newBits, mBits, mSize*sizeof(PxU32)); + PX_FREE(mBits); + mBits = newBits; + mSize = newSize; +} + +static PX_FORCE_INLINE PxU32 getNbPrimitives(PxU32 data) { return (data>>1)&15; } +static PX_FORCE_INLINE const PxU32* getPrimitives(const PxU32* base, PxU32 data) { return base + (data>>5); } +static PX_FORCE_INLINE const AABBTreeRuntimeNode* getPos(const AABBTreeRuntimeNode* base, PxU32 data) { return base + (data>>1); } +static PX_FORCE_INLINE PxU32 isLeaf(PxU32 data) { return data&1; } + +static PX_FORCE_INLINE void refitNode(AABBTreeRuntimeNode* PX_RESTRICT current, const PxBounds3* PX_RESTRICT boxes, const PxU32* PX_RESTRICT indices, AABBTreeRuntimeNode* PX_RESTRICT const nodeBase) +{ + // PT: we can safely use V4 loads on both boxes and nodes here: + // - it's safe on boxes because we allocated one extra box in the pruning pool + // - it's safe on nodes because there's always some data within the node, after the BV + + const PxU32 data = current->mData; + + Vec4V resultMinV, resultMaxV; + if(isLeaf(data)) + { + const PxU32 nbPrims = getNbPrimitives(data); + if(nbPrims) + { + const PxU32* primitives = getPrimitives(indices, data); + resultMinV = V4LoadU(&boxes[*primitives].minimum.x); + resultMaxV = V4LoadU(&boxes[*primitives].maximum.x); + + if(nbPrims>1) + { + const PxU32* last = primitives + nbPrims; + primitives++; + + while(primitives!=last) + { + resultMinV = V4Min(resultMinV, V4LoadU(&boxes[*primitives].minimum.x)); + resultMaxV = V4Max(resultMaxV, V4LoadU(&boxes[*primitives].maximum.x)); + primitives++; + } + } + } + else + { + // Might happen after a node has been invalidated + const float max = SQ_EMPTY_BOUNDS_EXTENTS; + resultMinV = V4Load(max); + resultMaxV = V4Load(-max); + } + } + else + { + const AABBTreeRuntimeNode* pos = getPos(nodeBase, data); + const AABBTreeRuntimeNode* neg = pos+1; + + const PxBounds3& posBox = pos->mBV; + const PxBounds3& negBox = neg->mBV; + + resultMinV = V4Min(V4LoadU(&posBox.minimum.x), V4LoadU(&negBox.minimum.x)); +// resultMaxV = V4Max(V4LoadU(&posBox.maximum.x), V4LoadU(&negBox.maximum.x)); + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + Vec4V posMinV = V4LoadU(&posBox.minimum.z); + Vec4V negMinV = V4LoadU(&negBox.minimum.z); + posMinV = _mm_shuffle_ps(posMinV, posMinV, _MM_SHUFFLE(0, 3, 2, 1)); + negMinV = _mm_shuffle_ps(negMinV, negMinV, _MM_SHUFFLE(0, 3, 2, 1)); + resultMaxV = V4Max(posMinV, negMinV); +#else + // PT: fixes the perf issue but not really convincing + resultMaxV = Vec4V_From_Vec3V(V3Max(V3LoadU(&posBox.maximum.x), V3LoadU(&negBox.maximum.x))); +#endif + } + + // PT: the V4 stores overwrite the data after the BV, but we just put it back afterwards + V4StoreU(resultMinV, ¤t->mBV.minimum.x); + V4StoreU(resultMaxV, ¤t->mBV.maximum.x); + current->mData = data; +} + +void AABBTree::fullRefit(const PxBounds3* boxes) +{ + PX_ASSERT(boxes); + + const PxU32* indices = mIndices; + AABBTreeRuntimeNode* const nodeBase = mRuntimePool; + PX_ASSERT(nodeBase); + + // Bottom-up update + PxU32 index = mTotalNbNodes; + while(index--) + { + AABBTreeRuntimeNode* current = nodeBase + index; + if(index) + Ps::prefetch(current - 1); + + refitNode(current, boxes, indices, nodeBase); + } +} + +static void _createParentArray(PxU32 totalNbNodes, PxU32* parentIndices, const AABBTreeRuntimeNode* parentNode, const AABBTreeRuntimeNode* currentNode, const AABBTreeRuntimeNode* root) +{ + const PxU32 parentIndex = PxU32(parentNode - root); + const PxU32 currentIndex = PxU32(currentNode - root); + PX_ASSERT(parentIndexisLeaf()) + { + _createParentArray(totalNbNodes, parentIndices, currentNode, currentNode->getPos(root), root); + _createParentArray(totalNbNodes, parentIndices, currentNode, currentNode->getNeg(root), root); + } +} + +void AABBTree::markNodeForRefit(TreeNodeIndex nodeIndex) +{ + if(!mRefitBitmask.getBits()) + mRefitBitmask.init(mTotalNbNodes); + + PX_ASSERT(nodeIndex(PX_ALLOC(sizeof(PxU32)*mTotalNbNodes, "AABB parent indices")); + _createParentArray(mTotalNbNodes, mParentIndices, mRuntimePool, mRuntimePool, mRuntimePool); + } + + PxU32 currentIndex = nodeIndex; + while(1) + { + PX_ASSERT(currentIndex>5; + mRefitHighestSetWord = PxMax(mRefitHighestSetWord, currentMarkedWord); + + const PxU32 parentIndex = mParentIndices[currentIndex]; + PX_ASSERT(parentIndex == 0 || parentIndex < currentIndex); + if(currentIndex == parentIndex) + break; + currentIndex = parentIndex; + } + } +} + +#define FIRST_VERSION +#ifdef FIRST_VERSION +void AABBTree::refitMarkedNodes(const PxBounds3* boxes) +{ + if(!mRefitBitmask.getBits()) + return; // No refit needed + + { + /*const*/ PxU32* bits = const_cast(mRefitBitmask.getBits()); + PxU32 size = mRefitHighestSetWord+1; +#ifdef _DEBUG + if(1) + { + const PxU32 totalSize = mRefitBitmask.getSize(); + for(PxU32 i=size;i>5); + PX_ASSERT(mask==PxU32(1<<(index&31))); + if(currentBits & mask) + { + refitNode(nodeBase + index, boxes, indices, nodeBase); +#ifdef _DEBUG + nbRefit++; +#endif + } + mask>>=1; + } + bits[size] = 0; + } + + mRefitHighestSetWord = 0; +// mRefitBitmask.clearAll(); + } +} +#endif + + +//#define SECOND_VERSION +#ifdef SECOND_VERSION +void AABBTree::refitMarkedNodes(const PxBounds3* boxes) +{ + /*const*/ PxU32* bits = const_cast(mRefitBitmask.getBits()); + if(!bits) + return; // No refit needed + + const PxU32 lastSetBit = mRefitBitmask.findLast(); + + const PxU32* indices = mIndices; + AABBTreeRuntimeNode* const nodeBase = mRuntimePool; + + for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) + { + for(PxU32 b = bits[w]; b; b &= b-1) + { + const PxU32 index = (PxU32)(w<<5|Ps::lowestSetBit(b)); + + + + while(size--) + { + // Test 32 bits at a time + const PxU32 currentBits = bits[size]; + if(!currentBits) + continue; + + PxU32 index = (size+1)<<5; + PxU32 mask = PxU32(1<<((index-1)&31)); + PxU32 _Count=32; + while(_Count--) + { + index--; + Ps::prefetch(nodeBase + index); + + PX_ASSERT(size==index>>5); + PX_ASSERT(mask==PxU32(1<<(index&31))); + if(currentBits & mask) + { + refitNode(nodeBase + index, boxes, indices, nodeBase); +#ifdef _DEBUG + nbRefit++; +#endif + } + mask>>=1; + } + bits[size] = 0; + } + mRefitHighestSetWord = 0; +// mRefitBitmask.clearAll(); + } +} +#endif + +PX_FORCE_INLINE static void setLeafData(PxU32& leafData, const AABBTreeRuntimeNode& node, const PxU32 indicesOffset) +{ + const PxU32 index = indicesOffset + (node.mData >> 5); + const PxU32 nbPrims = node.getNbPrimitives(); + PX_ASSERT(nbPrims <= 16); + leafData = (index << 5) | ((nbPrims & 15) << 1) | 1; +} + +// Copy the tree into nodes. Update node indices, leaf indices. +void AABBTree::addRuntimeChilds(PxU32& nodeIndex, const AABBTreeMergeData& treeParams) +{ + PX_ASSERT(nodeIndex < mTotalNbNodes + treeParams.mNbNodes + 1); + const PxU32 baseNodeIndex = nodeIndex; + + // copy the src tree into dest tree nodes, update its data + for (PxU32 i = 0; i < treeParams.mNbNodes; i++) + { + PX_ASSERT(nodeIndex < mTotalNbNodes + treeParams.mNbNodes + 1); + mRuntimePool[nodeIndex].mBV = treeParams.mNodes[i].mBV; + if (treeParams.mNodes[i].isLeaf()) + { + setLeafData(mRuntimePool[nodeIndex].mData, treeParams.mNodes[i], mNbIndices); + } + else + { + const PxU32 srcNodeIndex = baseNodeIndex + (treeParams.mNodes[i].getPosIndex()); + mRuntimePool[nodeIndex].mData = srcNodeIndex << 1; + mParentIndices[srcNodeIndex] = nodeIndex; + mParentIndices[srcNodeIndex + 1] = nodeIndex; + } + nodeIndex++; + } +} + +// Merge tree into targetNode, where target node is a leaf +// 1. Allocate new nodes/parent, copy all the nodes/parents +// 2. Create new node at the end, copy the data from target node +// 3. Copy the merge tree after the new node, create the parent map for them, update the leaf indices +// Schematic view: +// Target Nodes: ...Tn... +// Input tree: R1->Rc0, Rc1... +// Merged tree: ...Tnc->...->Nc0,R1->Rc0,Rc1... +// where new node: Nc0==Tn and Tnc is not a leaf anymore and points to Nc0 + +void AABBTree::mergeRuntimeLeaf(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& treeParams, PxU32 targetMergeNodeIndex) +{ + PX_ASSERT(mParentIndices); + PX_ASSERT(targetNode.isLeaf()); + + // 1. Allocate new nodes/parent, copy all the nodes/parents + // allocate new runtime pool with max combine number of nodes + // we allocate only 1 additional node each merge + AABBTreeRuntimeNode* newRuntimePool = PX_NEW(AABBTreeRuntimeNode)[mTotalNbNodes + treeParams.mNbNodes + 1]; + PxU32* newParentIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*(mTotalNbNodes + treeParams.mNbNodes + 1), "AABB parent indices")); + + // copy the whole target nodes, we will add the new node at the end together with the merge tree + PxMemCopy(newRuntimePool, mRuntimePool, sizeof(AABBTreeRuntimeNode)*(mTotalNbNodes)); + PxMemCopy(newParentIndices, mParentIndices, sizeof(PxU32)*(mTotalNbNodes)); + + // 2. Create new node at the end, copy the data from target node + PxU32 nodeIndex = mTotalNbNodes; + // copy the targetNode at the end of the new nodes + newRuntimePool[nodeIndex].mBV = targetNode.mBV; + newRuntimePool[nodeIndex].mData = targetNode.mData; + // update the parent information + newParentIndices[nodeIndex] = targetMergeNodeIndex; + + // mark for refit + if (mRefitBitmask.getBits() && mRefitBitmask.isSet(targetMergeNodeIndex)) + { + mRefitBitmask.setBit(nodeIndex); + const PxU32 currentMarkedWord = nodeIndex >> 5; + mRefitHighestSetWord = PxMax(mRefitHighestSetWord, currentMarkedWord); + } + + // swap pointers + PX_DELETE_ARRAY(mRuntimePool); + mRuntimePool = newRuntimePool; + PX_FREE(mParentIndices); + mParentIndices = newParentIndices; + + // 3. Copy the merge tree after the new node, create the parent map for them, update the leaf indices + nodeIndex++; + addRuntimeChilds(nodeIndex, treeParams); + PX_ASSERT(nodeIndex == mTotalNbNodes + 1 + treeParams.mNbNodes); + + // update the parent information for the input tree root node + mParentIndices[mTotalNbNodes + 1] = targetMergeNodeIndex; + + // fix the child information for the target node, was a leaf before + mRuntimePool[targetMergeNodeIndex].mData = mTotalNbNodes << 1; + + // update the total number of nodes + mTotalNbNodes = mTotalNbNodes + 1 + treeParams.mNbNodes; +} + +// Merge tree into targetNode, where target node is not a leaf +// 1. Allocate new nodes/parent, copy the nodes/parents till targetNodePosIndex +// 2. Create new node , copy the data from target node +// 3. Copy the rest of the target tree nodes/parents at the end -> targetNodePosIndex + 1 + treeParams.mNbNodes +// 4. Copy the merge tree after the new node, create the parent map for them, update the leaf indices +// 5. Go through the nodes copied at the end and fix the parents/childs +// Schematic view: +// Target Nodes: ...Tn->...->Tc0,Tc1... +// Input tree: R1->Rc0, Rc1... +// Merged tree: ...Tn->...->Nc0,R1->Rc0,Rc1...,Tc0,Tc1... +// where new node: Nc0->...->Tc0,Tc1 +void AABBTree::mergeRuntimeNode(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& treeParams, PxU32 targetMergeNodeIndex) +{ + PX_ASSERT(mParentIndices); + PX_ASSERT(!targetNode.isLeaf()); + + // Get the target node child pos, this is where we insert the new node and the input tree + const PxU32 targetNodePosIndex = targetNode.getPosIndex(); + + // 1. Allocate new nodes/parent, copy the nodes/parents till targetNodePosIndex + // allocate new runtime pool with max combine number of nodes + // we allocate only 1 additional node each merge + AABBTreeRuntimeNode* newRuntimePool = PX_NEW(AABBTreeRuntimeNode)[mTotalNbNodes + treeParams.mNbNodes + 1]; + PxU32* newParentIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*(mTotalNbNodes + treeParams.mNbNodes + 1), "AABB parent indices")); + // copy the untouched part of the nodes and parents + PxMemCopy(newRuntimePool, mRuntimePool, sizeof(AABBTreeRuntimeNode)*(targetNodePosIndex)); + PxMemCopy(newParentIndices, mParentIndices, sizeof(PxU32)*(targetNodePosIndex)); + + PxU32 nodeIndex = targetNodePosIndex; + // 2. Create new node , copy the data from target node + newRuntimePool[nodeIndex].mBV = targetNode.mBV; + newRuntimePool[nodeIndex].mData = ((targetNode.mData >> 1) + 1 + treeParams.mNbNodes) << 1; + // update parent information + newParentIndices[nodeIndex] = targetMergeNodeIndex; + + // handle mark for refit + if(mRefitBitmask.getBits() && mRefitBitmask.isSet(targetMergeNodeIndex)) + { + mRefitBitmask.setBit(nodeIndex); + const PxU32 currentMarkedWord = nodeIndex >> 5; + mRefitHighestSetWord = PxMax(mRefitHighestSetWord, currentMarkedWord); + } + + // 3. Copy the rest of the target tree nodes/parents at the end -> targetNodePosIndex + 1 + treeParams.mNbNodes + if(mTotalNbNodes - targetNodePosIndex) + { + PX_ASSERT(mTotalNbNodes - targetNodePosIndex > 0); + PxMemCopy(newRuntimePool + targetNodePosIndex + 1 + treeParams.mNbNodes, mRuntimePool + targetNodePosIndex, sizeof(AABBTreeRuntimeNode)*(mTotalNbNodes - targetNodePosIndex)); + PxMemCopy(newParentIndices + targetNodePosIndex + 1 + treeParams.mNbNodes, mParentIndices + targetNodePosIndex, sizeof(PxU32)*(mTotalNbNodes - targetNodePosIndex)); + } + // swap the pointers, release the old memory + PX_DELETE_ARRAY(mRuntimePool); + mRuntimePool = newRuntimePool; + PX_FREE(mParentIndices); + mParentIndices = newParentIndices; + + // 4. Copy the merge tree after the new node, create the parent map for them, update the leaf indices + nodeIndex++; + addRuntimeChilds(nodeIndex, treeParams); + PX_ASSERT(nodeIndex == targetNodePosIndex + 1 + treeParams.mNbNodes); + // update the total number of nodes + mTotalNbNodes = mTotalNbNodes + 1 + treeParams.mNbNodes; + + // update the parent information for the input tree root node + mParentIndices[targetNodePosIndex + 1] = targetMergeNodeIndex; + + // 5. Go through the nodes copied at the end and fix the parents/childs + for (PxU32 i = targetNodePosIndex + 1 + treeParams.mNbNodes; i < mTotalNbNodes; i++) + { + // check if the parent is the targetNode, if yes update the parent to new node + if(mParentIndices[i] == targetMergeNodeIndex) + { + mParentIndices[i] = targetNodePosIndex; + } + else + { + // if parent node has been moved, update the parent node + if(mParentIndices[i] >= targetNodePosIndex) + { + mParentIndices[i] = mParentIndices[i] + 1 + treeParams.mNbNodes; + } + else + { + // if parent has not been moved, update its child information + const PxU32 parentIndex = mParentIndices[i]; + // update the child information to point to Pos child + if(i % 2 != 0) + { + const PxU32 srcNodeIndex = mRuntimePool[parentIndex].getPosIndex(); + // if child index points to a node that has been moved, update the child index + PX_ASSERT(!mRuntimePool[parentIndex].isLeaf()); + PX_ASSERT(srcNodeIndex > targetNodePosIndex); + mRuntimePool[parentIndex].mData = (1 + treeParams.mNbNodes + srcNodeIndex) << 1; + } + } + } + if(!mRuntimePool[i].isLeaf()) + { + // update the child node index + const PxU32 srcNodeIndex = 1 + treeParams.mNbNodes + mRuntimePool[i].getPosIndex(); + mRuntimePool[i].mData = srcNodeIndex << 1; + } + } +} + +// traverse the target node, the tree is inside the targetNode, and find the best place where merge the tree +void AABBTree::traverseRuntimeNode(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& treeParams, PxU32 nodeIndex) +{ + const AABBTreeRuntimeNode& srcNode = treeParams.getRootNode(); + PX_ASSERT(srcNode.mBV.isInside(targetNode.mBV)); + + // Check if the srcNode(tree) can fit inside any of the target childs. If yes, traverse the target tree child + AABBTreeRuntimeNode& targetPosChild = *targetNode.getPos(mRuntimePool); + if(srcNode.mBV.isInside(targetPosChild.mBV)) + { + return traverseRuntimeNode(targetPosChild, treeParams, targetNode.getPosIndex()); + } + + AABBTreeRuntimeNode& targetNegChild = *targetNode.getNeg(mRuntimePool); + if (srcNode.mBV.isInside(targetNegChild.mBV)) + { + return traverseRuntimeNode(targetNegChild, treeParams, targetNode.getNegIndex()); + } + + // we cannot traverse target anymore, lets add the srcTree to current target node + if(targetNode.isLeaf()) + mergeRuntimeLeaf(targetNode, treeParams, nodeIndex); + else + mergeRuntimeNode(targetNode, treeParams, nodeIndex); +} + +// Merge the input tree into current tree. +// Traverse the tree and find the smallest node, where the whole new tree fits. When we find the node +// we create one new node pointing to the original children and the to the input tree root. +void AABBTree::mergeTree(const AABBTreeMergeData& treeParams) +{ + // allocate new indices buffer + PxU32* newIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*(mNbIndices + treeParams.mNbIndices), "AABB tree indices")); + PxMemCopy(newIndices, mIndices, sizeof(PxU32)*mNbIndices); + PX_FREE(mIndices); + mIndices = newIndices; + mTotalPrims += treeParams.mNbIndices; + + // copy the new indices, re-index using the provided indicesOffset. Note that indicesOffset + // must be provided, as original mNbIndices can be different than indicesOffset dues to object releases. + for (PxU32 i = 0; i < treeParams.mNbIndices; i++) + { + mIndices[mNbIndices + i] = treeParams.mIndicesOffset + treeParams.mIndices[i]; + } + + // check the mRefitBitmask if we fit all the new nodes + mRefitBitmask.resize(mTotalNbNodes + treeParams.mNbNodes + 1); + + // create the parent information so we can update it + if(!mParentIndices) + { + mParentIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mTotalNbNodes, "AABB parent indices")); + _createParentArray(mTotalNbNodes, mParentIndices, mRuntimePool, mRuntimePool, mRuntimePool); + } + + // if new tree is inside the root AABB we will traverse the tree to find better node where to attach the tree subnodes + // if the root is a leaf we merge with the root. + if(treeParams.getRootNode().mBV.isInside(mRuntimePool[0].mBV) && !mRuntimePool[0].isLeaf()) + { + traverseRuntimeNode(mRuntimePool[0], treeParams, 0); + } + else + { + if(mRuntimePool[0].isLeaf()) + { + mergeRuntimeLeaf(mRuntimePool[0], treeParams, 0); + } + else + { + mergeRuntimeNode(mRuntimePool[0], treeParams, 0); + } + + // increase the tree root AABB + mRuntimePool[0].mBV.include(treeParams.getRootNode().mBV); + } + +#ifdef _DEBUG + //verify parent indices + for (PxU32 i = 0; i < mTotalNbNodes; i++) + { + if (i) + { + PX_ASSERT(mRuntimePool[mParentIndices[i]].getPosIndex() == i || mRuntimePool[mParentIndices[i]].getNegIndex() == i); + } + if (!mRuntimePool[i].isLeaf()) + { + PX_ASSERT(mParentIndices[mRuntimePool[i].getPosIndex()] == i); + PX_ASSERT(mParentIndices[mRuntimePool[i].getNegIndex()] == i); + } + } + + // verify the tree nodes, leafs + for (PxU32 i = 0; i < mTotalNbNodes; i++) + { + if (mRuntimePool[i].isLeaf()) + { + const PxU32 index = mRuntimePool[i].mData >> 5; + const PxU32 nbPrim = mRuntimePool[i].getNbPrimitives(); + PX_ASSERT(index + nbPrim <= mNbIndices + treeParams.mNbIndices); + } + else + { + const PxU32 nodeIndex = (mRuntimePool[i].getPosIndex()); + PX_ASSERT(nodeIndex < mTotalNbNodes); + } + } +#endif // _DEBUG + + mNbIndices += treeParams.mNbIndices; +} + + + diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBTree.h b/src/PhysX/physx/source/scenequery/src/SqAABBTree.h new file mode 100644 index 000000000..bde284d65 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqAABBTree.h @@ -0,0 +1,259 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_AABBTREE_H +#define SQ_AABBTREE_H + +#include "foundation/PxMemory.h" +#include "foundation/PxBounds3.h" +#include "PsUserAllocated.h" +#include "PsVecMath.h" +#include "SqTypedef.h" +#include "GuAABBTreeBuild.h" +#include "PsArray.h" + +namespace physx +{ + +using namespace shdfnd::aos; + +namespace Sq +{ + class AABBTreeUpdateMap; + + typedef Ps::Pair TreeMergePair; + typedef Ps::Array TreeMergeMap; + + class BitArray + { + public: + BitArray() : mBits(NULL), mSize(0) {} + BitArray(PxU32 nb_bits) { init(nb_bits); } + ~BitArray() { PX_FREE_AND_RESET(mBits); mBits = NULL; } + + bool init(PxU32 nb_bits); + + // Data management + PX_FORCE_INLINE void setBit(PxU32 bit_number) + { + mBits[bit_number>>5] |= 1<<(bit_number&31); + } + PX_FORCE_INLINE void clearBit(PxU32 bit_number) + { + mBits[bit_number>>5] &= ~(1<<(bit_number&31)); + } + PX_FORCE_INLINE void toggleBit(PxU32 bit_number) + { + mBits[bit_number>>5] ^= 1<<(bit_number&31); + } + + PX_FORCE_INLINE void clearAll() { PxMemZero(mBits, mSize*4); } + PX_FORCE_INLINE void setAll() { PxMemSet(mBits, 0xff, mSize*4); } + + void resize(PxU32 maxBitNumber); + + // Data access + PX_FORCE_INLINE Ps::IntBool isSet(PxU32 bit_number) const + { + return Ps::IntBool(mBits[bit_number>>5] & (1<<(bit_number&31))); + } + + PX_FORCE_INLINE const PxU32* getBits() const { return mBits; } + PX_FORCE_INLINE PxU32 getSize() const { return mSize; } + + protected: + PxU32* mBits; //!< Array of bits + PxU32 mSize; //!< Size of the array in dwords + }; + + + //! AABB tree node used for runtime (smaller than for build) + class AABBTreeRuntimeNode : public Ps::UserAllocated + { + public: + PX_FORCE_INLINE AABBTreeRuntimeNode() {} + PX_FORCE_INLINE ~AABBTreeRuntimeNode() {} + + PX_FORCE_INLINE PxU32 isLeaf() const { return mData&1; } + + PX_FORCE_INLINE const PxU32* getPrimitives(const PxU32* base) const { return base + (mData>>5); } + PX_FORCE_INLINE PxU32* getPrimitives(PxU32* base) { return base + (mData>>5); } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return (mData>>1)&15; } + + PX_FORCE_INLINE PxU32 getPosIndex() const { return mData>>1; } + PX_FORCE_INLINE PxU32 getNegIndex() const { return (mData>>1) + 1; } + PX_FORCE_INLINE const AABBTreeRuntimeNode* getPos(const AABBTreeRuntimeNode* base) const { return base + (mData>>1); } + PX_FORCE_INLINE const AABBTreeRuntimeNode* getNeg(const AABBTreeRuntimeNode* base) const { const AABBTreeRuntimeNode* P = getPos(base); return P ? P+1 : NULL;} + + PX_FORCE_INLINE AABBTreeRuntimeNode* getPos(AABBTreeRuntimeNode* base) { return base + (mData >> 1); } + PX_FORCE_INLINE AABBTreeRuntimeNode* getNeg(AABBTreeRuntimeNode* base) { AABBTreeRuntimeNode* P = getPos(base); return P ? P + 1 : NULL; } + + PX_FORCE_INLINE PxU32 getNbRuntimePrimitives() const { return (mData>>1)&15; } + PX_FORCE_INLINE void setNbRunTimePrimitives(PxU32 val) + { + PX_ASSERT(val<16); + PxU32 data = mData & ~(15<<1); + data |= val<<1; + mData = data; + } + + PX_FORCE_INLINE void getAABBCenterExtentsV(Vec3V* center, Vec3V* extents) const + { + const Vec4V minV = V4LoadU(&mBV.minimum.x); + const Vec4V maxV = V4LoadU(&mBV.maximum.x); + + const float half = 0.5f; + const FloatV halfV = FLoad(half); + + *extents = Vec3V_From_Vec4V(V4Scale(V4Sub(maxV, minV), halfV)); + *center = Vec3V_From_Vec4V(V4Scale(V4Add(maxV, minV), halfV)); + } + + PX_FORCE_INLINE void getAABBCenterExtentsV2(Vec3V* center, Vec3V* extents) const + { + const Vec4V minV = V4LoadU(&mBV.minimum.x); + const Vec4V maxV = V4LoadU(&mBV.maximum.x); + + *extents = Vec3V_From_Vec4V(V4Sub(maxV, minV)); + *center = Vec3V_From_Vec4V(V4Add(maxV, minV)); + } + + PX_FORCE_INLINE void getAABBMinMaxV(Vec4V* minV, Vec4V* maxV) const + { + *minV = V4LoadU(&mBV.minimum.x); + *maxV = V4LoadU(&mBV.maximum.x); + } + + PxBounds3 mBV; // Global bounding-volume enclosing all the node-related primitives + PxU32 mData; // 27 bits node or prim index|4 bits #prims|1 bit leaf + }; + + //! Contains AABB-tree merge parameters + class AABBTreeMergeData + { + public: + AABBTreeMergeData(PxU32 nbNodes, const AABBTreeRuntimeNode* nodes, PxU32 nbIndices, const PxU32* indices, PxU32 indicesOffset) : + mNbNodes(nbNodes), mNodes(nodes), mNbIndices(nbIndices), mIndices(indices), mIndicesOffset(indicesOffset) + { + } + + ~AABBTreeMergeData() {} + + PX_FORCE_INLINE const AABBTreeRuntimeNode& getRootNode() const { return mNodes[0]; } + + public: + PxU32 mNbNodes; //!< Number of nodes of AABB tree merge + const AABBTreeRuntimeNode* mNodes; //!< Nodes of AABB tree merge + + PxU32 mNbIndices; //!< Number of indices of AABB tree merge + const PxU32* mIndices; //!< Indices of AABB tree merge + + PxU32 mIndicesOffset; //!< Indices offset from pruning pool + }; + + // Progressive building + class FIFOStack; + //~Progressive building + + //! AABB-tree, N primitives/leaf + class AABBTree : public Ps::UserAllocated + { + public: + AABBTree(); + ~AABBTree(); + // Build + bool build(Gu::AABBTreeBuildParams& params); + // Progressive building + PxU32 progressiveBuild(Gu::AABBTreeBuildParams& params, Gu::BuildStats& stats, PxU32 progress, PxU32 limit); + //~Progressive building + void release(bool clearRefitMap=true); + + // Merge tree with another one + void mergeTree(const AABBTreeMergeData& tree); + // Initialize tree from given merge data + void initTree(const AABBTreeMergeData& tree); + + // Data access + PX_FORCE_INLINE const PxU32* getIndices() const { return mIndices; } + PX_FORCE_INLINE PxU32* getIndices() { return mIndices; } + PX_FORCE_INLINE void setIndices(PxU32* indices) { mIndices = indices; } + PX_FORCE_INLINE PxU32 getNbNodes() const { return mTotalNbNodes; } + PX_FORCE_INLINE const AABBTreeRuntimeNode* getNodes() const { return mRuntimePool; } + PX_FORCE_INLINE AABBTreeRuntimeNode* getNodes() { return mRuntimePool; } + PX_FORCE_INLINE void setNodes(AABBTreeRuntimeNode* nodes) { mRuntimePool = nodes; } + PX_FORCE_INLINE PxU32 getTotalPrims() const { return mTotalPrims; } + +#if PX_DEBUG + void validate() const; +#endif + void shiftOrigin(const PxVec3& shift); + + // Shift indices of the tree by offset. Used for merged trees, when initial indices needs to be shifted to match indices in current pruning pool + void shiftIndices(PxU32 offset); + + private: + PxU32* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation). + PxU32 mNbIndices; //!< Nb indices + AABBTreeRuntimeNode* mRuntimePool; //!< Linear pool of nodes. + Gu::NodeAllocator mNodeAllocator; + PxU32* mParentIndices; //!< PT: hot/cold split, keep parent data in separate array + // Stats + PxU32 mTotalNbNodes; //!< Number of nodes in the tree. + PxU32 mTotalPrims; //!< Copy of final BuildStats::mTotalPrims + + // Progressive building + FIFOStack* mStack; + //~Progressive building + bool buildInit(Gu::AABBTreeBuildParams& params, Gu::BuildStats& stats); + void buildEnd(Gu::AABBTreeBuildParams& params, Gu::BuildStats& stats); + + // tree merge + void mergeRuntimeNode(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& tree, PxU32 targetNodeIndex); + void mergeRuntimeLeaf(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& tree, PxU32 targetNodeIndex); + void addRuntimeChilds(PxU32& nodeIndex, const AABBTreeMergeData& tree); + void traverseRuntimeNode(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& tree, PxU32 nodeIndex); + // REFIT + public: + void fullRefit(const PxBounds3* boxes); + + // adds node[index] to a list of nodes to refit when refitMarkedNodes is called + // Note that this includes updating the hierarchy up the chain + void markNodeForRefit(TreeNodeIndex nodeIndex); + void refitMarkedNodes(const PxBounds3* boxes); + private: + BitArray mRefitBitmask; //!< bit is set for each node index in markForRefit + PxU32 mRefitHighestSetWord; + //~REFIT + }; + +} // namespace Sq + +} + +#endif // SQ_AABBTREE_H diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp b/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp new file mode 100644 index 000000000..e7512aaa6 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp @@ -0,0 +1,197 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "SqAABBTreeUpdateMap.h" +#include "SqAABBTree.h" + +using namespace physx; +using namespace Sq; + +static const PxU32 SHRINK_THRESHOLD = 1024; + +void AABBTreeUpdateMap::initMap(PxU32 nbObjects, const AABBTree& tree) +{ + if(!nbObjects) + { + release(); + return; + } + + // Memory management + { + const PxU32 mapSize = nbObjects; + const PxU32 targetCapacity = mapSize + (mapSize>>2); + + PxU32 currentCapacity = mMapping.capacity(); + if( ( targetCapacity < (currentCapacity>>1) ) && ( (currentCapacity-targetCapacity) > SHRINK_THRESHOLD ) ) + { + // trigger reallocation of a smaller array, there is enough memory to save + currentCapacity = 0; + } + + if(mapSize > currentCapacity) + { + // the mapping values are invalid and reset below in any case + // so there is no need to copy the values at all + mMapping.reset(); + mMapping.reserve(targetCapacity); // since size is 0, reserve will also just allocate + } + + mMapping.forceSize_Unsafe(mapSize); + + for(PxU32 i=0;igetNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= 16); + + // retrieve the primitives pointer + PxU32* primitives = node0->getPrimitives(tree.getIndices()); + PX_ASSERT(primitives); + + // PT: look for desired pool index in the leaf + bool foundIt = false; + for(PxU32 i=0;isetNbRunTimePrimitives(last); + primitives[i] = INVALID_POOL_ID; // Mark primitive index as invalid in the node + mMapping[prunerIndex0] = INVALID_NODE_ID; // invalidate the node index for pool 0 + + // PT: swap within the leaf node. No need to update the mapping since they should all point + // to the same tree node anyway. + if(last!=i) + Ps::swap(primitives[i], primitives[last]); + break; + } + } + PX_ASSERT(foundIt); + PX_UNUSED(foundIt); + } + + if (nodeIndex1!=INVALID_NODE_ID) + { + // PT: with multiple primitives per leaf, tree nodes may very well be the same for different pool indices. + // However the pool indices may be the same when a swap has been skipped in the pruning pool, in which + // case there is nothing to do. + if(prunerIndex0!=prunerIndex1) + { + PX_ASSERT(nodeIndex1 < tree.getNbNodes()); + PX_ASSERT(nodes[nodeIndex1].isLeaf()); + AABBTreeRuntimeNode* node1 = nodes + nodeIndex1; + const PxU32 nbPrims = node1->getNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= 16); + + // retrieve the primitives pointer + PxU32* primitives = node1->getPrimitives(tree.getIndices()); + PX_ASSERT(primitives); + + // PT: look for desired pool index in the leaf + bool foundIt = false; + for(PxU32 i=0;i mMapping; + }; + +} +} + +#endif diff --git a/src/PhysX/physx/source/scenequery/src/SqBounds.cpp b/src/PhysX/physx/source/scenequery/src/SqBounds.cpp new file mode 100644 index 000000000..a0f9cc0fa --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqBounds.cpp @@ -0,0 +1,75 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxTransform.h" +#include "SqBounds.h" +#include "CmTransformUtils.h" +#include "SqPruner.h" +#include "ScbShape.h" +#include "ScbActor.h" +#include "ScbRigidStatic.h" +#include "ScbBody.h" +#include "PsAllocator.h" +#include "GuBounds.h" + +using namespace physx; +using namespace Sq; + +void Sq::computeStaticWorldAABB(PxBounds3& bounds, const Scb::Shape& scbShape, const Scb::Actor& scbActor) +{ + const PxTransform& shape2Actor = scbShape.getShape2Actor(); + + PX_ALIGN(16, PxTransform) globalPose; + + Cm::getStaticGlobalPoseAligned(static_cast(scbActor).getActor2World(), shape2Actor, globalPose); + Gu::computeBounds(bounds, scbShape.getGeometry(), globalPose, 0.0f, NULL, SQ_PRUNER_INFLATION); +} + +void Sq::computeDynamicWorldAABB(PxBounds3& bounds, const Scb::Shape& scbShape, const Scb::Actor& scbActor) +{ + const PxTransform& shape2Actor = scbShape.getShape2Actor(); + + PX_ALIGN(16, PxTransform) globalPose; + { + const Scb::Body& body = static_cast(scbActor); + PX_ALIGN(16, PxTransform) kinematicTarget; + const PxU16 sqktFlags = PxRigidBodyFlag::eKINEMATIC | PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES; + const bool useTarget = (PxU16(body.getFlags()) & sqktFlags) == sqktFlags; + const PxTransform& body2World = (useTarget && body.getKinematicTarget(kinematicTarget)) ? kinematicTarget : body.getBody2World(); + Cm::getDynamicGlobalPoseAligned(body2World, shape2Actor, body.getBody2Actor(), globalPose); + } + + Gu::computeBounds(bounds, scbShape.getGeometry(), globalPose, 0.0f, NULL, SQ_PRUNER_INFLATION); +} + +const ComputeBoundsFunc Sq::gComputeBoundsTable[2] = +{ + computeStaticWorldAABB, + computeDynamicWorldAABB +}; diff --git a/src/PhysX/physx/source/scenequery/src/SqBounds.h b/src/PhysX/physx/source/scenequery/src/SqBounds.h new file mode 100644 index 000000000..592d13098 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqBounds.h @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_BOUNDS_H +#define SQ_BOUNDS_H + +#include "CmPhysXCommon.h" +#include "foundation/PxBounds3.h" +#include "PsVecMath.h" + +namespace physx +{ + namespace Scb + { + class Shape; + class Actor; + } + +namespace Sq +{ + void computeStaticWorldAABB(PxBounds3& bounds, const Scb::Shape& scbShape, const Scb::Actor& scbActor); + void computeDynamicWorldAABB(PxBounds3& bounds, const Scb::Shape& scbShape, const Scb::Actor& scbActor); + + typedef void(*ComputeBoundsFunc) (PxBounds3& bounds, const Scb::Shape& scbShape, const Scb::Actor& scbActor); + + extern const ComputeBoundsFunc gComputeBoundsTable[2]; + + // PT: TODO: - check that this is compatible with Gu::computeBounds(..., SQ_PRUNER_INFLATION, ...) + // PT: TODO: - refactor with "inflateBounds" in GuBounds.cpp if possible + // PT: TODO: - use SQ_PRUNER_INFLATION instead of hardcoding "0.01f" + PX_FORCE_INLINE void inflateBounds(PxBounds3& dst, const PxBounds3& src) + { + using namespace physx::shdfnd::aos; + + const Vec4V minV = V4LoadU(&src.minimum.x); + const Vec4V maxV = V4LoadU(&src.maximum.x); + const Vec4V eV = V4Scale(V4Sub(maxV, minV), FLoad(0.5f * 0.01f)); + + V4StoreU(V4Sub(minV, eV), &dst.minimum.x); + PX_ALIGN(16, PxVec4) max4; + V4StoreA(V4Add(maxV, eV), &max4.x); + dst.maximum = PxVec3(max4.x, max4.y, max4.z); + } + + // PT: the PX_MAX_BOUNDS_EXTENTS value is too large and produces INF floats when the box values are squared in + // some collision routines. Thus, for the SQ subsystem we use this alternative (smaller) value to mark empty bounds. + // See PX-954 for details. + #define SQ_EMPTY_BOUNDS_EXTENTS PxSqrt(0.25f * 1e33f) +} +} + +#endif // SQ_BOUNDS_H diff --git a/src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp new file mode 100644 index 000000000..b36d60432 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp @@ -0,0 +1,2599 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "SqBucketPruner.h" +#include "GuIntersectionBoxBox.h" +#include "GuInternal.h" +#include "PsVecMath.h" +#include "foundation/PxUnionCast.h" +#include "CmRadixSortBuffered.h" +#include "CmRenderOutput.h" +#include "PsFPU.h" +#include "PsBitUtils.h" +#include "PsIntrinsics.h" +#include "GuBounds.h" + +using namespace physx::shdfnd::aos; + +using namespace physx; +using namespace Sq; +using namespace Gu; +using namespace Ps; + +#define INVALID_HANDLE 0xffffffff + +/* +TODO: +- if Core is always available, mSortedObjects could be replaced with just indices to mCoreObjects => less memory. +- UTS: + - test that queries against empty boxes all return false +- invalidate after 16 removes +- check shiftOrigin stuff (esp what happens to emptied boxes) + - isn't there a very hard-to-find bug waiting to happen in there, + when the shift touches the empty box and overrides mdata0/mdata1 with "wrong" values that break the sort? +- revisit updateObject/removeObject +- optimize/cache computation of free global bounds before clipRay + +- remove temp memory buffers (sorted arrays) +- take care of code duplication +- better code to generate SIMD 0x7fffffff +- refactor SIMD tests +- optimize: + - better split values + - optimize update (bitmap, less data copy, etc) + - use ray limits in traversal code too? + - the SIMD XBOX code operates on Min/Max rather than C/E. Change format? + - or just try the alternative ray-box code (as on PC) ==> pretty much exactly the same speed +*/ + +//#define VERIFY_SORT +//#define BRUTE_FORCE_LIMIT 32 +#define LOCAL_SIZE 256 // Size of various local arrays. Dynamic allocations occur if exceeded. +#define USE_SIMD // Use SIMD code or not (sanity performance check) +#define NODE_SORT // Enable/disable node sorting +#define NODE_SORT_MIN_COUNT 16 // Limit above which node sorting is performed +#if PX_INTEL_FAMILY + #if COMPILE_VECTOR_INTRINSICS + #define CAN_USE_MOVEMASK + #endif +#endif + +#define ALIGN16(size) ((unsigned(size)+15) & unsigned(~15)) + +#ifdef _DEBUG + #define AlignedLoad V4LoadU + #define AlignedStore V4StoreU +#else + #define AlignedLoad V4LoadA + #define AlignedStore V4StoreA +#endif + +// SAT-based ray-box overlap test has accuracy issues for long rays, so we clip them against the global AABB to limit these issues. +static void clipRay(const PxVec3& rayOrig, const PxVec3& rayDir, float& maxDist, const PxVec3& boxMin, const PxVec3& boxMax) +{ + const PxVec3 boxCenter = (boxMax + boxMin)*0.5f; + const PxVec3 boxExtents = (boxMax - boxMin)*0.5f; + const float dpc = boxCenter.dot(rayDir); + const float extentsMagnitude = boxExtents.magnitude(); + const float dpMin = dpc - extentsMagnitude; + const float dpMax = dpc + extentsMagnitude; + const float dpO = rayOrig.dot(rayDir); + const float boxLength = extentsMagnitude * 2.0f; + const float distToBox = PxMin(PxAbs(dpMin - dpO), PxAbs(dpMax - dpO)); + maxDist = distToBox + boxLength * 2.0f; +} + +BucketPrunerNode::BucketPrunerNode() +{ + for(PxU32 i=0;i<5;i++) + mBucketBox[i].setEmpty(); +} + +static const PxU8 gCodes[] = { 4, 4, 4, 4, 4, 3, 2, 2, + 4, 1, 0, 0, 4, 1, 0, 0, + 4, 1, 0, 0, 2, 1, 0, 0, + 3, 1, 0, 0, 2, 1, 0, 0}; + +#ifdef CAN_USE_MOVEMASK +/*static PX_FORCE_INLINE PxU32 classifyBox_x86(const BucketBox& box, const PxVec4& limits, const bool useY, const bool isCrossBucket) +{ + const Vec4V extents = AlignedLoad(&box.mExtents.x); + const Vec4V center = AlignedLoad(&box.mCenter.x); + const Vec4V plus = V4Add(extents, center); + const Vec4V minus = V4Sub(extents, center); + + Vec4V tmp; + if(useY) // PT: this is a constant so branch prediction works here + tmp = _mm_shuffle_ps(plus, minus, _MM_SHUFFLE(0,1,0,1)); + else + tmp = _mm_shuffle_ps(plus, minus, _MM_SHUFFLE(0,2,0,2)); + + const Vec4V comp = _mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(0,2,1,3)); // oh well, nm + + const PxU32 Code = (PxU32)_mm_movemask_ps(V4IsGrtr(V4LoadA(&limits.x), comp)); + return gCodes[Code | PxU32(isCrossBucket)<<4]; +}*/ + +static PX_FORCE_INLINE PxU32 classifyBox_x86(const Vec4V boxMin, const Vec4V boxMax, const PxVec4& limits, const bool useY, const bool isCrossBucket) +{ + const Vec4V plus = boxMax; + const Vec4V minus = V4Neg(boxMin); + + Vec4V tmp; + if(useY) // PT: this is a constant so branch prediction works here + tmp = _mm_shuffle_ps(plus, minus, _MM_SHUFFLE(0,1,0,1)); + else + tmp = _mm_shuffle_ps(plus, minus, _MM_SHUFFLE(0,2,0,2)); + + const Vec4V comp = _mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(0,2,1,3)); // oh well, nm + + const PxU32 Code = PxU32(_mm_movemask_ps(V4IsGrtr(V4LoadA(&limits.x), comp))); + return gCodes[Code | PxU32(isCrossBucket)<<4]; +} +#endif + +#ifdef CAN_USE_MOVEMASK + #if PX_DEBUG + #define USE_CLASSIFY_BOX + #endif +#else + #define USE_CLASSIFY_BOX +#endif + +#ifdef USE_CLASSIFY_BOX +static PX_FORCE_INLINE PxU32 classifyBox(const BucketBox& box, const float limitX, const float limitYZ, const PxU32 yz, const bool isCrossBucket) +{ + const bool upperPart = (box.mCenter[yz] + box.mExtents[yz])limitYZ; + const bool leftPart = (box.mCenter.x + box.mExtents.x)limitX; + + // Table-based box classification avoids many branches + const PxU32 Code = PxU32(rightPart)|(PxU32(leftPart)<<1)|(PxU32(lowerPart)<<2)|(PxU32(upperPart)<<3); + return gCodes[Code + (isCrossBucket ? 16 : 0)]; +} +#endif + +void BucketPrunerNode::classifyBoxes( float limitX, float limitYZ, + PxU32 nb, BucketBox* PX_RESTRICT boxes, const PrunerPayload* PX_RESTRICT objects, + BucketBox* PX_RESTRICT sortedBoxes, PrunerPayload* PX_RESTRICT sortedObjects, + bool isCrossBucket, PxU32 sortAxis) +{ + const PxU32 yz = PxU32(sortAxis == 1 ? 2 : 1); + + #ifdef _DEBUG + { + float prev = boxes[0].mDebugMin; + for(PxU32 i=1;i=prev); + prev = current; + } + } + #endif + + // Local (stack-based) min/max bucket bounds + PX_ALIGN(16, PxVec4) bucketBoxMin[5]; + PX_ALIGN(16, PxVec4) bucketBoxMax[5]; + { + const PxBounds3 empty = PxBounds3::empty(); + for(PxU32 i=0;i<5;i++) + { + mCounters[i] = 0; + bucketBoxMin[i] = PxVec4(empty.minimum, 0.0f); + bucketBoxMax[i] = PxVec4(empty.maximum, 0.0f); + } + } + + { +#ifdef CAN_USE_MOVEMASK + // DS: order doesn't play nice with x86 shuffles :-| + PX_ALIGN(16, PxVec4) limits(-limitX, limitX, -limitYZ, limitYZ); + const bool useY = yz==1; +#endif + // Determine in which bucket each object falls, update bucket bounds + for(PxU32 i=0;i=prev); + prev = current; + } + } + } + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +static void processChildBuckets(PxU32 nbAllocated, + BucketBox* sortedBoxesInBucket, PrunerPayload* sortedObjectsInBucket, + const BucketPrunerNode& bucket, BucketPrunerNode* PX_RESTRICT childBucket, + BucketBox* PX_RESTRICT baseBucketsBoxes, PrunerPayload* PX_RESTRICT baseBucketsObjects, + PxU32 sortAxis) +{ + PX_UNUSED(nbAllocated); + + const PxU32 yz = PxU32(sortAxis == 1 ? 2 : 1); + for(PxU32 i=0;i<5;i++) + { + const PxU32 nbInBucket = bucket.mCounters[i]; + if(!nbInBucket) + { + childBucket[i].initCounters(); + continue; + } + BucketBox* bucketsBoxes = baseBucketsBoxes + bucket.mOffsets[i]; + PrunerPayload* bucketsObjects = baseBucketsObjects + bucket.mOffsets[i]; + PX_ASSERT(nbInBucket<=nbAllocated); + + const float limitX = bucket.mBucketBox[i].mCenter.x; + const float limitYZ = bucket.mBucketBox[i].mCenter[yz]; + const bool isCrossBucket = i==4; + childBucket[i].classifyBoxes(limitX, limitYZ, nbInBucket, bucketsBoxes, bucketsObjects, + sortedBoxesInBucket, sortedObjectsInBucket, + isCrossBucket, sortAxis); + + PxMemCopy(bucketsBoxes, sortedBoxesInBucket, sizeof(BucketBox)*nbInBucket); + PxMemCopy(bucketsObjects, sortedObjectsInBucket, sizeof(PrunerPayload)*nbInBucket); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxU32 encodeFloat(PxU32 newPos) +{ + //we may need to check on -0 and 0 + //But it should make no practical difference. + if(newPos & PX_SIGN_BITMASK) //negative? + return ~newPos;//reverse sequence of negative numbers + else + return newPos | PX_SIGN_BITMASK; // flip sign +} + +static PX_FORCE_INLINE void computeRayLimits(float& rayMin, float& rayMax, const PxVec3& rayOrig, const PxVec3& rayDir, float maxDist, PxU32 sortAxis) +{ + const float rayOrigValue = rayOrig[sortAxis]; + const float rayDirValue = rayDir[sortAxis] * maxDist; + rayMin = PxMin(rayOrigValue, rayOrigValue + rayDirValue); + rayMax = PxMax(rayOrigValue, rayOrigValue + rayDirValue); +} + +static PX_FORCE_INLINE void computeRayLimits(float& rayMin, float& rayMax, const PxVec3& rayOrig, const PxVec3& rayDir, float maxDist, const PxVec3& inflate, PxU32 sortAxis) +{ + const float inflateValue = inflate[sortAxis]; + const float rayOrigValue = rayOrig[sortAxis]; + const float rayDirValue = rayDir[sortAxis] * maxDist; + rayMin = PxMin(rayOrigValue, rayOrigValue + rayDirValue) - inflateValue; + rayMax = PxMax(rayOrigValue, rayOrigValue + rayDirValue) + inflateValue; +} + +static PX_FORCE_INLINE void encodeBoxMinMax(BucketBox& box, const PxU32 axis) +{ + const float min = box.mCenter[axis] - box.mExtents[axis]; + const float max = box.mCenter[axis] + box.mExtents[axis]; + + const PxU32* binaryMin = reinterpret_cast(&min); + const PxU32* binaryMax = reinterpret_cast(&max); + box.mData0 = encodeFloat(binaryMin[0]); + box.mData1 = encodeFloat(binaryMax[0]); +} + +/////////////////////////////////////////////////////////////////////////////// + +BucketPrunerCore::BucketPrunerCore(bool externalMemory) : + mCoreNbObjects (0), + mCoreCapacity (0), + mCoreBoxes (NULL), + mCoreObjects (NULL), + mCoreRemap (NULL), + mSortedWorldBoxes (NULL), + mSortedObjects (NULL), + mNbFree (0), + mSortedNb (0), + mSortedCapacity (0), + mSortAxis (0), + mDirty (true), + mOwnMemory (!externalMemory) +{ + mGlobalBox.setEmpty(); + + mLevel1.initCounters(); + + for(PxU32 i=0;i<5;i++) + mLevel2[i].initCounters(); + for(PxU32 j=0;j<5;j++) + for(PxU32 i=0;i<5;i++) + mLevel3[j][i].initCounters(); +} + +BucketPrunerCore::~BucketPrunerCore() +{ + release(); +} + +void BucketPrunerCore::release() +{ + mDirty = true; + mCoreNbObjects = 0; + + mCoreCapacity = 0; + if(mOwnMemory) + { + PX_FREE_AND_RESET(mCoreBoxes); + PX_FREE_AND_RESET(mCoreObjects); + PX_FREE_AND_RESET(mCoreRemap); + } + + PX_FREE_AND_RESET(mSortedWorldBoxes); + PX_FREE_AND_RESET(mSortedObjects); + mSortedNb = 0; + mSortedCapacity = 0; + + mNbFree = 0; +#ifdef USE_REGULAR_HASH_MAP + mMap.clear(); +#else + mMap.purge(); +#endif +} + +void BucketPrunerCore::setExternalMemory(PxU32 nbObjects, PxBounds3* boxes, PrunerPayload* objects) +{ + PX_ASSERT(!mOwnMemory); + mCoreNbObjects = nbObjects; + mCoreBoxes = boxes; + mCoreObjects = objects; + mCoreRemap = NULL; +} + +void BucketPrunerCore::allocateSortedMemory(PxU32 nb) +{ + mSortedNb = nb; + if(nb<=mSortedCapacity && (nb>=mSortedCapacity/2)) + return; + + const PxU32 capacity = Ps::nextPowerOfTwo(nb); + mSortedCapacity = capacity; + + PxU32 bytesNeededForBoxes = capacity*sizeof(BucketBox); + bytesNeededForBoxes = ALIGN16(bytesNeededForBoxes); + + PxU32 bytesNeededForObjects = capacity*sizeof(PrunerPayload); + bytesNeededForObjects = ALIGN16(bytesNeededForObjects); + + PX_FREE(mSortedObjects); + PX_FREE(mSortedWorldBoxes); + mSortedWorldBoxes = reinterpret_cast(PX_ALLOC(bytesNeededForBoxes, "BucketPruner")); + mSortedObjects = reinterpret_cast(PX_ALLOC(bytesNeededForObjects, "BucketPruner")); + PX_ASSERT(!(size_t(mSortedWorldBoxes)&15)); + PX_ASSERT(!(size_t(mSortedObjects)&15)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void BucketPrunerCore::resizeCore() +{ + const PxU32 capacity = mCoreCapacity ? mCoreCapacity*2 : 32; + mCoreCapacity = capacity; + + const PxU32 bytesNeededForBoxes = capacity*sizeof(PxBounds3); + const PxU32 bytesNeededForObjects = capacity*sizeof(PrunerPayload); + const PxU32 bytesNeededForRemap = capacity*sizeof(PxU32); + + PxBounds3* newCoreBoxes = reinterpret_cast(PX_ALLOC(bytesNeededForBoxes, "BucketPruner")); + PrunerPayload* newCoreObjects = reinterpret_cast(PX_ALLOC(bytesNeededForObjects, "BucketPruner")); + PxU32* newCoreRemap = reinterpret_cast(PX_ALLOC(bytesNeededForRemap, "BucketPruner")); + if(mCoreBoxes) + { + PxMemCopy(newCoreBoxes, mCoreBoxes, mCoreNbObjects*sizeof(PxBounds3)); + PX_FREE(mCoreBoxes); + } + if(mCoreObjects) + { + PxMemCopy(newCoreObjects, mCoreObjects, mCoreNbObjects*sizeof(PrunerPayload)); + PX_FREE(mCoreObjects); + } + if(mCoreRemap) + { + PxMemCopy(newCoreRemap, mCoreRemap, mCoreNbObjects*sizeof(PxU32)); + PX_FREE(mCoreRemap); + } + mCoreBoxes = newCoreBoxes; + mCoreObjects = newCoreObjects; + mCoreRemap = newCoreRemap; +} + +PX_FORCE_INLINE void BucketPrunerCore::addObjectInternal(const PrunerPayload& object, const PxBounds3& worldAABB, PxU32 timeStamp) +{ + if(mCoreNbObjects==mCoreCapacity) + resizeCore(); + + const PxU32 index = mCoreNbObjects++; + mCoreObjects[index] = object; + mCoreBoxes[index] = worldAABB; // PT: TODO: check assembly here + mCoreRemap[index] = 0xffffffff; + + // Objects are only inserted into the map once they're part of the main/core arrays. +#ifdef USE_REGULAR_HASH_MAP + bool ok = mMap.insert(object, BucketPrunerPair(index, timeStamp)); +#else + BucketPrunerPair* ok = mMap.addPair(object, index, timeStamp); +#endif + PX_UNUSED(ok); + PX_ASSERT(ok); +} + +bool BucketPrunerCore::addObject(const PrunerPayload& object, const PxBounds3& worldAABB, PxU32 timeStamp) +{ +/* + We should probably use a bigger Payload struct here, which would also contains the external handle. + (EDIT: we can't even do that, because of the setExternalMemory function) + When asked to update/remove an object it would be O(n) to find the proper object in the mSortedObjects array. + + - + + For removing it we can simply empty the corresponding box, and the object will never be returned from queries. + Maybe this isn't even true, since boxes are sorted along one axis. So marking a box as empty could break the code relying on a sorted order. + An alternative is to mark the external handle as invalid, and ignore the object when a hit is found. + + (EDIT: the sorting is now tested via data0/data1 anyway so we could mark the box as empty without breaking this) + + - + + For updating an object we would need to keep the (sub) array sorted (not the whole thing, only the array within a bucket). + We don't know the range (what part of the array maps to our bucket) but we may have the bucket ID somewhere? If we'd have this + we could parse the array left/right and resort just the right boxes. If we don't have this we may be able to "quickly" find the + range by traversing the tree, looking for the proper bucket. In any case I don't think there's a mapping to update within a bucket, + unlike in SAP or MBP. So we should be able to shuffle a bucket without having to update anything. For example there's no mapping + between the Core array and the Sorted array. It's a shame in a way because we'd need one, but it's not there - and in fact I think + we can free the Core array once Sorted is created, we don't need it at all. + + If we don't want to re-sort the full bucket we can just mark it as dirty and ignore the sort-based early exits in the queries. Then we + can incrementally resort it over N frames or something. + + This only works if the updated object remains in the same bucket though. If it moves to another bucket it becomes tempting to just remove + the object and re-insert it. + + - + + Now for adding an object, we can first have a "free pruner" and do the 16 next entries brute-force. Rebuilding every 16 objects might + give a good speedup already. Otherwise we need to do something more complicated. +*/ + + PX_ASSERT(mOwnMemory); + PX_ASSERT(!mDirty || !mNbFree); + if(!mDirty) + { + // In this path the structure is marked as valid. We do not want to invalidate it for each new object... + if(mNbFreesecond.mCoreIndex; + timeStamp = removedEntry->second.mTimeStamp; +#else + PxU32 coreIndex; // This is the object's index in the core arrays. + if(mMap.removePair(object, coreIndex, timeStamp)) + { +#endif + // In this codepath, the object we want to remove exists in the core arrays. + + // We will need to remove it from both the core arrays & the sorted arrays. + const PxU32 sortedIndex = mCoreRemap[coreIndex]; // This is the object's index in the sorted arrays. + +#ifdef USE_REGULAR_HASH_MAP + bool status = mMap.erase(object); + PX_ASSERT(status); + PX_UNUSED(status); +#endif + + // First let's deal with the core arrays + mCoreNbObjects--; + if(coreIndex!=mCoreNbObjects) + { + // If it wasn't the last object in the array, close the gaps as usual + const PrunerPayload& movedObject = mCoreObjects[mCoreNbObjects]; + mCoreBoxes[coreIndex] = mCoreBoxes[mCoreNbObjects]; + mCoreObjects[coreIndex] = movedObject; + mCoreRemap[coreIndex] = mCoreRemap[mCoreNbObjects]; + + // Since we just moved the last object, its index in the core arrays has changed. + // We must reflect this change in the map. +#ifdef USE_REGULAR_HASH_MAP + BucketPrunerMap::Entry* movedEntry = const_cast(mMap.find(movedObject)); + PX_ASSERT(movedEntry->second.mCoreIndex==mCoreNbObjects); + movedEntry->second.mCoreIndex = coreIndex; +#else + BucketPrunerPair* movedEntry = const_cast(mMap.findPair(movedObject)); + PX_ASSERT(movedEntry->mCoreIndex==mCoreNbObjects); + movedEntry->mCoreIndex = coreIndex; +#endif + } + + // Now, let's deal with the sorted arrays. + // If the structure is dirty, the sorted arrays will be rebuilt from scratch so there's no need to + // update them right now. + if(!mDirty) + { + // If the structure is valid, we want to keep it this way to avoid rebuilding sorted arrays after + // each removal. We can't "close the gaps" easily here because order of objects in the arrays matters. + + // Instead we just invalidate the object by setting its bounding box as empty. + // Queries against empty boxes will never return a hit, so this effectively "removes" the object + // from any subsequent query results. Sorted arrays now contain a "disabled" object, until next build. + + // Invalidating the box does not invalidate the sorting, since it's now captured in mData0/mData1. + // That is, mData0/mData1 keep their previous integer-encoded values, as if the box/object was still here. + mSortedWorldBoxes[sortedIndex].mCenter = PxVec3(0.0f); + mSortedWorldBoxes[sortedIndex].mExtents = PxVec3(-SQ_EMPTY_BOUNDS_EXTENTS); + // Note that we don't touch mSortedObjects here. We could, but this is not necessary. + } + return true; + } + + // Here, the object we want to remove exists in the free array. So we just parse it. + for(PxU32 i=0;i(mMap.find(movedObject)); + PX_ASSERT(movedEntry->second.mCoreIndex==coreNbObjects); + movedEntry->second.mCoreIndex = coreIndex; +#else + BucketPrunerPair* movedEntry = const_cast(mMap.findPair(movedObject)); + PX_ASSERT(movedEntry->mCoreIndex==coreNbObjects); + movedEntry->mCoreIndex = coreIndex; +#endif + } + + nbRemoved++; +#ifdef USE_REGULAR_HASH_MAP + bool status = mMap.erase(p.first); + PX_ASSERT(status); + PX_UNUSED(status); +#else + const PxU32 hashValue = hash(p.mPayload) & mMap.mMask; + mMap.removePairInternal(p.mPayload, hashValue, i); +#endif + nbActivePairs--; + } + else i++; + } + mCoreNbObjects = coreNbObjects; + +#ifdef USE_REGULAR_HASH_MAP +#else + mMap.shrinkMemory(); +#endif + } + + // PT: ...then we look in the 'free' array + PxU32 i=0; + while(i0); + Vec4V mergedMinV = V4LoadU(&boxes[nb-1].minimum.x); + Vec4V mergedMaxV = Vec4V_From_Vec3V(V3LoadU(&boxes[nb-1].maximum.x)); + for(PxU32 i=0;i(sortedObjects); + for(PxU32 i=0;i + PX_CUDA_CALLABLE PX_FORCE_INLINE void tswap(T& x, T& y) + { + T tmp = x; + x = y; + y = tmp; + } + +/* PX_FORCE_INLINE __m128 DotV(const __m128 a, const __m128 b) + { + const __m128 dot1 = _mm_mul_ps(a, b); + const __m128 shuf1 = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dot1), _MM_SHUFFLE(0,0,0,0))); + const __m128 shuf2 = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dot1), _MM_SHUFFLE(1,1,1,1))); + const __m128 shuf3 = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dot1), _MM_SHUFFLE(2,2,2,2))); + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); + }*/ + +// PT: hmmm, by construction, isn't the order always the same for all bucket pruners? +// => maybe not because the bucket boxes are still around the merged aabbs, not around the bucket +// Still we could do something here +static /*PX_FORCE_INLINE*/ PxU32 sort(const BucketPrunerNode& parent, const PxVec3& rayDir) +{ + const PxU32 totalCount = parent.mCounters[0]+parent.mCounters[1]+parent.mCounters[2]+parent.mCounters[3]+parent.mCounters[4]; + if(totalCount(dp); + const PxU32* values = PxUnionCast(dp); + + PxU32 value0 = values[0]; + PxU32 value1 = values[1]; + PxU32 value2 = values[2]; + PxU32 value3 = values[3]; + PxU32 value4 = values[4]; + + for(PxU32 j=0;j<5-1;j++) + { + if(value1(dp); + +// const PxU32 mask = ~7U; + const PxU32 mask = 0x7ffffff8; + PxU32 value0 = (values[0]&mask); + PxU32 value1 = (values[1]&mask)|1; + PxU32 value2 = (values[2]&mask)|2; + PxU32 value3 = (values[3]&mask)|3; + PxU32 value4 = (values[4]&mask)|4; + +#define SORT_BLOCK \ + if(value1(PX_ALLOC(nb*sizeof(size_t), "")); +for(PxU32 i=0;iLOCAL_SIZE) + { + tempObjects = reinterpret_cast(PX_ALLOC(sizeof(PrunerPayload)*nb, "BucketPruner")); + tempBoxes = reinterpret_cast(PX_ALLOC(nb*sizeof(BucketBox), "BucketPruner")); + } + else + { + tempObjects = localTempObjects; + tempBoxes = localTempBoxes; + } + + mSortAxis = sortBoxes(nb, mCoreBoxes, mCoreObjects, mGlobalBox, tempBoxes, tempObjects); + + PX_ASSERT(mSortAxis); + + allocateSortedMemory(nb); + BucketBox* sortedBoxes = mSortedWorldBoxes; + PrunerPayload* sortedObjects = mSortedObjects; + + const PxU32 yz = PxU32(mSortAxis == 1 ? 2 : 1); + const float limitX = mGlobalBox.mCenter.x; + const float limitYZ = mGlobalBox.mCenter[yz]; + mLevel1.classifyBoxes(limitX, limitYZ, nb, tempBoxes, tempObjects, + sortedBoxes, sortedObjects, + false, mSortAxis); + + processChildBuckets(nb, tempBoxes, tempObjects, + mLevel1, mLevel2, mSortedWorldBoxes, mSortedObjects, + mSortAxis); + + for(PxU32 j=0;j<5;j++) + processChildBuckets(nb, tempBoxes, tempObjects, + mLevel2[j], mLevel3[j], mSortedWorldBoxes + mLevel1.mOffsets[j], mSortedObjects + mLevel1.mOffsets[j], + mSortAxis); + + { + for(PxU32 i=0;iLOCAL_SIZE) + { + PX_FREE(tempBoxes); + PX_FREE(tempObjects); + } + +for(PxU32 i=0;i(&MaskI)), DataV); + _mm_store_ps(&rayParams->mData.x, DataV); + _mm_store_ps(&rayParams->mData2.x, Data2V); + _mm_store_ps(&rayParams->mFDir.x, FDirV); + #else + const PxVec3 data = 0.5f * rayDir * maxDist; + rayParams->mData = data; + rayParams->mData2 = rayOrig + data; + rayParams->mFDir.x = PxAbs(data.x); + rayParams->mFDir.y = PxAbs(data.y); + rayParams->mFDir.z = PxAbs(data.z); + #endif + } + + template + static PX_FORCE_INLINE IntBool _segmentAABB(const BucketBox& box, const RayParams* PX_RESTRICT params) + { + #ifdef USE_SIMD + const PxU32 maskI = 0x7fffffff; + const __m128 fdirV = _mm_load_ps(¶ms->mFDir.x); +// #ifdef _DEBUG + const __m128 extentsV = inflateT ? _mm_add_ps(_mm_loadu_ps(&box.mExtents.x), _mm_load_ps(¶ms->mInflate.x)) : _mm_loadu_ps(&box.mExtents.x); + const __m128 DV = _mm_sub_ps(_mm_load_ps(¶ms->mData2.x), _mm_loadu_ps(&box.mCenter.x)); +/* #else + const __m128 extentsV = inflateT ? _mm_add_ps(_mm_load_ps(&box.mExtents.x), _mm_load_ps(¶ms->mInflate.x)) : _mm_load_ps(&box.mExtents.x); + const __m128 DV = _mm_sub_ps(_mm_load_ps(¶ms->mData2.x), _mm_load_ps(&box.mCenter.x)); + #endif*/ + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps(reinterpret_cast(&maskI))); + absDV = _mm_cmpgt_ps(absDV, _mm_add_ps(extentsV, fdirV)); + const PxU32 test = PxU32(_mm_movemask_ps(absDV)); + if(test&7) + return 0; + + const __m128 dataZYX_V = _mm_load_ps(¶ms->mData.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const __m128 fV = _mm_sub_ps(_mm_mul_ps(dataZYX_V, DXZY_V), _mm_mul_ps(dataXZY_V, DV)); + + const __m128 fdirZYX_V = _mm_load_ps(¶ms->mFDir.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 fg = _mm_add_ps(_mm_mul_ps(extentsV, fdirXZY_V), _mm_mul_ps(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps(reinterpret_cast(&maskI))); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = PxU32(_mm_movemask_ps(absfV)); + if(test2&7) + return 0; + return 1; + #else + const float boxExtentsx = inflateT ? box.mExtents.x + params->mInflate.x : box.mExtents.x; + const float Dx = params->mData2.x - box.mCenter.x; if(fabsf(Dx) > boxExtentsx + params->mFDir.x) return IntFalse; + + const float boxExtentsz = inflateT ? box.mExtents.z + params->mInflate.z : box.mExtents.z; + const float Dz = params->mData2.z - box.mCenter.z; if(fabsf(Dz) > boxExtentsz + params->mFDir.z) return IntFalse; + + const float boxExtentsy = inflateT ? box.mExtents.y + params->mInflate.y : box.mExtents.y; + const float Dy = params->mData2.y - box.mCenter.y; if(fabsf(Dy) > boxExtentsy + params->mFDir.y) return IntFalse; + + float f; + f = params->mData.y * Dz - params->mData.z * Dy; if(fabsf(f) > boxExtentsy*params->mFDir.z + boxExtentsz*params->mFDir.y) return IntFalse; + f = params->mData.z * Dx - params->mData.x * Dz; if(fabsf(f) > boxExtentsx*params->mFDir.z + boxExtentsz*params->mFDir.x) return IntFalse; + f = params->mData.x * Dy - params->mData.y * Dx; if(fabsf(f) > boxExtentsx*params->mFDir.y + boxExtentsy*params->mFDir.x) return IntFalse; + return IntTrue; + #endif + } +#else + #include "GuBVHTestsSIMD.h" + + typedef RayAABBTest BPRayAABBTest; + +template +static PX_FORCE_INLINE IntBool _segmentAABB(const BucketBox& box, const BPRayAABBTest& test) +{ + return static_cast(test.check(V3LoadU(box.mCenter), V3LoadU(box.mExtents))); +} + +/*static PX_FORCE_INLINE IntBool _segmentAABB(const BucketBox& box, const BPRayAABBTest& test, PxU32 rayMinLimitX, PxU32 rayMaxLimitX) +{ + if(rayMinLimitX>box.mData1) + return 0; + if(rayMaxLimitX +static PxAgain processBucket( + PxU32 nb, const BucketBox* PX_RESTRICT baseBoxes, PrunerPayload* PX_RESTRICT baseObjects, + PxU32 offset, PxU32 totalAllocated, + const PxVec3& rayOrig, const PxVec3& rayDir, float& maxDist, +#ifdef CAN_USE_MOVEMASK + RayParams* PX_RESTRICT rayParams, +#else + BPRayAABBTest& test, const PxVec3& inflate, +#endif + PrunerCallback& pcb, PxU32& _rayMinLimitInt, PxU32& _rayMaxLimitInt, PxU32 sortAxis) +{ + PX_UNUSED(totalAllocated); + + const BucketBox* PX_RESTRICT _boxes = baseBoxes + offset; + PrunerPayload* PX_RESTRICT _objects = baseObjects + offset; + + PxU32 rayMinLimitInt = _rayMinLimitInt; + PxU32 rayMaxLimitInt = _rayMaxLimitInt; + + const BucketBox* last = _boxes + nb; + + while(_boxes!=last) + { + const BucketBox& currentBox = *_boxes++; + PrunerPayload* currentObject = _objects++; + + if(currentBox.mData1rayMaxLimitInt) + goto Exit; + +#ifdef CAN_USE_MOVEMASK + if(!_segmentAABB(currentBox, rayParams)) + continue; +#else + if(!_segmentAABB(currentBox, test)) + continue; +#endif + + const float MaxDist = maxDist; + const PxAgain again = pcb.invoke(maxDist, *currentObject); + if(!again) + return false; + if(maxDist < MaxDist) + { + float rayMinLimit, rayMaxLimit; +#ifdef CAN_USE_MOVEMASK + if(inflateT) + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, rayParams->mInflate, sortAxis); + else + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, sortAxis); + + precomputeRayData(rayParams, rayOrig, rayDir, maxDist); +#else + if(inflateT) + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, inflate, sortAxis); + else + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, sortAxis); + + test.setDistance(maxDist); +#endif + const PxU32* binaryMinLimit = reinterpret_cast(&rayMinLimit); + const PxU32* binaryMaxLimit = reinterpret_cast(&rayMaxLimit); + rayMinLimitInt = encodeFloat(binaryMinLimit[0]); + rayMaxLimitInt = encodeFloat(binaryMaxLimit[0]); + } + } +Exit: + + _rayMinLimitInt = rayMinLimitInt; + _rayMaxLimitInt = rayMaxLimitInt; + return true; +} + +#ifdef NODE_SORT +static PxU32 computeDirMask(const PxVec3& dir) +{ + const PxU32* binary = reinterpret_cast(&dir.x); + const PxU32 X = (binary[0])>>31; + const PxU32 Y = (binary[1])>>31; + const PxU32 Z = (binary[2])>>31; + return Z|(Y<<1)|(X<<2); +} +#endif + +template +static PxAgain stab(const BucketPrunerCore& core, PrunerCallback& pcb, const PxVec3& rayOrig, const PxVec3& rayDir, float& maxDist, const PxVec3 inflate) +{ + const PxU32 nb = core.mSortedNb; + if(!nb && !core.mNbFree) + return true; + + if(maxDist==PX_MAX_F32) + { + /*const*/ PxVec3 boxMin = core.mGlobalBox.getMin() - inflate; + /*const*/ PxVec3 boxMax = core.mGlobalBox.getMax() + inflate; + + if(core.mNbFree) + { + // TODO: optimize this + PxBounds3 freeGlobalBounds; + freeGlobalBounds.setEmpty(); + for(PxU32 i=0;i(tmp, &rayParams)) +#else + if(_segmentAABB(tmp, test)) +#endif + { + if(!pcb.invoke(maxDist, core.mFreeObjects[i])) + return false; + } + } + + if(!nb) + return true; + +#ifdef CAN_USE_MOVEMASK + if(!_segmentAABB(core.mGlobalBox, &rayParams)) + return true; +#else + if(!_segmentAABB(core.mGlobalBox, test)) + return true; +#endif + + const PxU32 sortAxis = core.mSortAxis; + float rayMinLimit, rayMaxLimit; + if(inflateT) + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, inflate, sortAxis); + else + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, sortAxis); + + const PxU32* binaryMinLimit = reinterpret_cast(&rayMinLimit); + const PxU32* binaryMaxLimit = reinterpret_cast(&rayMaxLimit); + PxU32 rayMinLimitInt = encodeFloat(binaryMinLimit[0]); + PxU32 rayMaxLimitInt = encodeFloat(binaryMaxLimit[0]); +/* +float rayMinLimitX, rayMaxLimitX; +if(inflateT) + computeRayLimits(rayMinLimitX, rayMaxLimitX, rayOrig, rayDir, maxDist, inflate, 0); +else + computeRayLimits(rayMinLimitX, rayMaxLimitX, rayOrig, rayDir, maxDist, 0); + +PxU32 rayMinLimitIntX = encodeFloat(PX_IR(rayMinLimitX)); +PxU32 rayMaxLimitIntX = encodeFloat(PX_IR(rayMaxLimitX)); +*/ + + float currentDist = maxDist; + +#ifdef NODE_SORT + const PxU32 dirIndex = computeDirMask(rayDir); + PxU32 orderi = core.mLevel1.mOrder[dirIndex]; +// PxU32 orderi = sort(core.mLevel1, rayDir); + + for(PxU32 i_=0;i_<5;i_++) + { + const PxU32 i = orderi&7; orderi>>=3; +#else + for(PxU32 i=0;i<5;i++) + { +#endif + +#ifdef CAN_USE_MOVEMASK + if(core.mLevel1.mCounters[i] && _segmentAABB(core.mLevel1.mBucketBox[i], &rayParams)) +#else + if(core.mLevel1.mCounters[i] && _segmentAABB(core.mLevel1.mBucketBox[i], test)) +// if(core.mLevel1.mCounters[i] && _segmentAABB(core.mLevel1.mBucketBox[i], test, rayMinLimitIntX, rayMaxLimitIntX)) +#endif + { + +#ifdef NODE_SORT + PxU32 orderj = core.mLevel2[i].mOrder[dirIndex]; +// PxU32 orderj = sort(core.mLevel2[i], rayDir); + + for(PxU32 j_=0;j_<5;j_++) + { + const PxU32 j = orderj&7; orderj>>=3; +#else + for(PxU32 j=0;j<5;j++) + { +#endif + +#ifdef CAN_USE_MOVEMASK + if(core.mLevel2[i].mCounters[j] && _segmentAABB(core.mLevel2[i].mBucketBox[j], &rayParams)) +#else + if(core.mLevel2[i].mCounters[j] && _segmentAABB(core.mLevel2[i].mBucketBox[j], test)) +// if(core.mLevel2[i].mCounters[j] && _segmentAABB(core.mLevel2[i].mBucketBox[j], test, rayMinLimitIntX, rayMaxLimitIntX)) +#endif + { + const BucketPrunerNode& parent = core.mLevel3[i][j]; + const PxU32 parentOffset = core.mLevel1.mOffsets[i] + core.mLevel2[i].mOffsets[j]; + +#ifdef NODE_SORT + PxU32 orderk = parent.mOrder[dirIndex]; +// PxU32 orderk = sort(parent, rayDir); + + for(PxU32 k_=0;k_<5;k_++) + { + const PxU32 k = orderk&7; orderk>>=3; +#else + for(PxU32 k=0;k<5;k++) + { +#endif + const PxU32 nbInBucket = parent.mCounters[k]; +#ifdef CAN_USE_MOVEMASK + if(nbInBucket && _segmentAABB(parent.mBucketBox[k], &rayParams)) +#else + if(nbInBucket && _segmentAABB(parent.mBucketBox[k], test)) +// if(nbInBucket && _segmentAABB(parent.mBucketBox[k], test, rayMinLimitIntX, rayMaxLimitIntX)) +#endif + { + const PxU32 offset = parentOffset + parent.mOffsets[k]; + const PxAgain again = processBucket( nbInBucket, core.mSortedWorldBoxes, core.mSortedObjects, + offset, core.mSortedNb, + rayOrig, rayDir, currentDist, +#ifdef CAN_USE_MOVEMASK + &rayParams, +#else + test, inflate, +#endif + pcb, + rayMinLimitInt, rayMaxLimitInt, + sortAxis); + if(!again) + return false; + } + } + } + } + } + } + + maxDist = currentDist; + return true; +} + +PxAgain BucketPrunerCore::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + return ::stab<0>(*this, pcb, origin, unitDir, inOutDistance, PxVec3(0.0f)); +} + +PxAgain BucketPrunerCore::sweep(const ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + const PxVec3 extents = queryVolume.getPrunerInflatedWorldAABB().getExtents(); + return ::stab<1>(*this, pcb, queryVolume.getPrunerInflatedWorldAABB().getCenter(), unitDir, inOutDistance, extents); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +static PX_FORCE_INLINE bool processBucket( PxU32 nb, const BucketBox* PX_RESTRICT baseBoxes, PrunerPayload* PX_RESTRICT baseObjects, + PxU32 offset, PxU32 totalAllocated, + const Test& test, PrunerCallback& pcb, + PxU32 minLimitInt, PxU32 maxLimitInt) +{ + PX_UNUSED(totalAllocated); + + const BucketBox* PX_RESTRICT boxes = baseBoxes + offset; + PrunerPayload* PX_RESTRICT objects = baseObjects + offset; + + while(nb--) + { + const BucketBox& currentBox = *boxes++; + PrunerPayload* currentObject = objects++; + + if(currentBox.mData1maxLimitInt) + { + if(doAssert) + PX_ASSERT(!test(currentBox)); + return true; + } + + if(test(currentBox)) + { + PxReal dist = -1.0f; // no distance for overlaps + if(!pcb.invoke(dist, *currentObject)) + return false; + } + } + return true; +} + +template +class BucketPrunerOverlapTraversal +{ +public: + PX_FORCE_INLINE BucketPrunerOverlapTraversal() {} + + /*PX_FORCE_INLINE*/ bool operator()(const BucketPrunerCore& core, const Test& test, PrunerCallback& pcb, const PxBounds3& cullBox) const + { + for(PxU32 i=0;i(&boxMinLimit); + const PxU32* binaryMaxLimit = reinterpret_cast(&boxMaxLimit); + const PxU32 rayMinLimitInt = encodeFloat(binaryMinLimit[0]); + const PxU32 rayMaxLimitInt = encodeFloat(binaryMaxLimit[0]); + + for(PxU32 i=0;i<5;i++) + { + if(core.mLevel1.mCounters[i] && test(core.mLevel1.mBucketBox[i])) + { + for(PxU32 j=0;j<5;j++) + { + if(core.mLevel2[i].mCounters[j] && test(core.mLevel2[i].mBucketBox[j])) + { + for(PxU32 k=0;k<5;k++) + { + const PxU32 nbInBucket = core.mLevel3[i][j].mCounters[k]; + if(nbInBucket && test(core.mLevel3[i][j].mBucketBox[k])) + { + const PxU32 offset = core.mLevel1.mOffsets[i] + core.mLevel2[i].mOffsets[j] + core.mLevel3[i][j].mOffsets[k]; + if(!processBucket(nbInBucket, core.mSortedWorldBoxes, core.mSortedObjects, + offset, core.mSortedNb, test, pcb, rayMinLimitInt, rayMaxLimitInt)) + return false; + } + } + } + } + } + } + return true; + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef CAN_USE_MOVEMASK +PX_FORCE_INLINE PxU32 BAllTrue3_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32((moveMask & 0x7) == (0x7)); +} +#endif + +#ifdef USE_SIMD +struct SphereAABBTest_SIMD +{ + PX_FORCE_INLINE SphereAABBTest_SIMD(const Gu::Sphere& sphere) : + #ifdef CAN_USE_MOVEMASK + mCenter (V4LoadU(&sphere.center.x)), + #else + mCenter (V3LoadU(sphere.center)), + #endif + mRadius2(FLoad(sphere.radius * sphere.radius)) + {} + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + #ifdef CAN_USE_MOVEMASK + const Vec4V boxCenter = AlignedLoad(&box.mCenter.x); + const Vec4V boxExtents = AlignedLoad(&box.mExtents.x); + // + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const FloatV dot = V4Dot3(d,d); + return Ps::IntBool(BAllTrue3_R(FIsGrtrOrEq(mRadius2, dot))); + #else + const Vec3V boxCenter = V3LoadU(box.mCenter); + const Vec3V boxExtents = V3LoadU(box.mExtents); + // + const Vec3V offset = V3Sub(mCenter, boxCenter); + const Vec3V closest = V3Clamp(offset, V3Neg(boxExtents), boxExtents); + const Vec3V d = V3Sub(offset, closest); + return Ps::IntBool(BAllEqTTTT(FIsGrtrOrEq(mRadius2, V3Dot(d, d)))); + #endif + } + + PX_FORCE_INLINE Ps::IntBool operator()(const PxBounds3& bounds) const + { + BucketBox tmp; + tmp.mCenter = bounds.getCenter(); + tmp.mExtents = bounds.getExtents(); + return (*this)(tmp); + } + +private: + SphereAABBTest_SIMD& operator=(const SphereAABBTest_SIMD&); + #ifdef CAN_USE_MOVEMASK + const Vec4V mCenter; + #else + const Vec3V mCenter; + #endif + const FloatV mRadius2; +}; +#else +struct SphereAABBTest_Scalar +{ + PX_FORCE_INLINE SphereAABBTest_Scalar(const Gu::Sphere& sphere) : + mCenter (sphere.center), + mRadius2(sphere.radius * sphere.radius) + {} + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + const PxVec3 minimum = box.getMin(); + const PxVec3 maximum = box.getMax(); + + float d = 0.0f; + + //find the square of the distance + //from the sphere to the box + for(PxU32 i=0;i<3;i++) + { + if(mCenter[i]maximum[i]) + { + const float s = mCenter[i] - maximum[i]; + d += s*s; + } + } + return d <= mRadius2; + } + +private: + SphereAABBTest_Scalar& operator=(const SphereAABBTest_Scalar&); + const PxVec3 mCenter; + float mRadius2; +}; +#endif + +#ifdef USE_SIMD +typedef SphereAABBTest_SIMD BucketPrunerSphereAABBTest; +#else +typedef SphereAABBTest_Scalar BucketPrunerSphereAABBTest; +#endif + +/////////////////////////////////////////////////////////////////////////////// + +struct BucketPrunerAABBAABBTest +{ + PX_FORCE_INLINE BucketPrunerAABBAABBTest(const PxBounds3& queryBox) : mBox(queryBox) {} + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + // PT: we don't use PxBounds3::intersects() because isValid() asserts on our empty boxes! + const PxVec3 bucketMin = box.getMin(); + const PxVec3 bucketMax = box.getMax(); + return !(mBox.minimum.x > bucketMax.x || bucketMin.x > mBox.maximum.x || + mBox.minimum.y > bucketMax.y || bucketMin.y > mBox.maximum.y || + mBox.minimum.z > bucketMax.z || bucketMin.z > mBox.maximum.z); + } + + PX_FORCE_INLINE Ps::IntBool operator()(const PxBounds3& bounds) const + { + // PT: we don't use PxBounds3::intersects() because isValid() asserts on our empty boxes! + const PxVec3& bucketMin = bounds.minimum; + const PxVec3& bucketMax = bounds.maximum; + return !(mBox.minimum.x > bucketMax.x || bucketMin.x > mBox.maximum.x || + mBox.minimum.y > bucketMax.y || bucketMin.y > mBox.maximum.y || + mBox.minimum.z > bucketMax.z || bucketMin.z > mBox.maximum.z); + } +private: + BucketPrunerAABBAABBTest& operator=(const BucketPrunerAABBAABBTest&); + const PxBounds3 mBox; +}; + +/*struct BucketPrunerAABBAABBTest_SIMD +{ + PX_FORCE_INLINE BucketPrunerAABBAABBTest_SIMD(const PxBounds3& b) + : mCenter(V3LoadU(b.getCenter())) + , mExtents(V3LoadU(b.getExtents())) + {} + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + return V3AllGrtrOrEq(V3Add(mExtents, AlignedLoad(&box.mExtents.x)), V3Abs(V3Sub(AlignedLoad(&box.mCenter.x), mCenter))); + } +private: + BucketPrunerAABBAABBTest_SIMD& operator=(const BucketPrunerAABBAABBTest_SIMD&); + const Vec3V mCenter, mExtents; +};*/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef USE_SIMD +struct OBBAABBTest_SIMD +{ + OBBAABBTest_SIMD(const PxMat33& rotation, const PxVec3& translation, const PxVec3& extents) + { + const Vec3V eps = V3Load(1e-6f); + + mT = V3LoadU(translation); + mExtents = V3LoadU(extents); + + // storing the transpose matrices yields a simpler SIMD test + mRT = Mat33V_From_PxMat33(rotation.getTranspose()); + mART = Mat33V(V3Add(V3Abs(mRT.col0), eps), V3Add(V3Abs(mRT.col1), eps), V3Add(V3Abs(mRT.col2), eps)); + mBB_xyz = M33TrnspsMulV3(mART, mExtents); + +/* if(fullTest) + { + const Vec3V eYZX = V3PermYZX(mExtents), eZXY = V3PermZXY(mExtents); + + mBB_123 = V3MulAdd(eYZX, V3PermZXY(mART.col0), V3Mul(eZXY, V3PermYZX(mART.col0))); + mBB_456 = V3MulAdd(eYZX, V3PermZXY(mART.col1), V3Mul(eZXY, V3PermYZX(mART.col1))); + mBB_789 = V3MulAdd(eYZX, V3PermZXY(mART.col2), V3Mul(eZXY, V3PermYZX(mART.col2))); + }*/ + } + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + const Vec3V extentsV = V3LoadU(box.mExtents); + + const Vec3V t = V3Sub(mT, V3LoadU(box.mCenter)); + + // class I - axes of AABB + if(V3OutOfBounds(t, V3Add(extentsV, mBB_xyz))) + return Ps::IntFalse; + + const Vec3V rX = mRT.col0, rY = mRT.col1, rZ = mRT.col2; + const Vec3V arX = mART.col0, arY = mART.col1, arZ = mART.col2; + + const FloatV eX = V3GetX(extentsV), eY = V3GetY(extentsV), eZ = V3GetZ(extentsV); + const FloatV tX = V3GetX(t), tY = V3GetY(t), tZ = V3GetZ(t); + + // class II - axes of OBB + { + const Vec3V v = V3ScaleAdd(rZ, tZ, V3ScaleAdd(rY, tY, V3Scale(rX, tX))); + const Vec3V v2 = V3ScaleAdd(arZ, eZ, V3ScaleAdd(arY, eY, V3ScaleAdd(arX, eX, mExtents))); + if(V3OutOfBounds(v, v2)) + return Ps::IntFalse; + } + +// if(!fullTest) + return Ps::IntTrue; + +/* // class III - edge cross products. Almost all OBB tests early-out with type I or type II, + // so early-outs here probably aren't useful (TODO: profile) + + const Vec3V va = V3NegScaleSub(rZ, tY, V3Scale(rY, tZ)); + const Vec3V va2 = V3ScaleAdd(arY, eZ, V3ScaleAdd(arZ, eY, mBB_123)); + const BoolV ba = BOr(V3IsGrtr(va, va2), V3IsGrtr(V3Neg(va2), va)); + + const Vec3V vb = V3NegScaleSub(rX, tZ, V3Scale(rZ, tX)); + const Vec3V vb2 = V3ScaleAdd(arX, eZ, V3ScaleAdd(arZ, eX, mBB_456)); + const BoolV bb = BOr(V3IsGrtr(vb, vb2), V3IsGrtr(V3Neg(vb2), vb)); + + const Vec3V vc = V3NegScaleSub(rY, tX, V3Scale(rX, tY)); + const Vec3V vc2 = V3ScaleAdd(arX, eY, V3ScaleAdd(arY, eX, mBB_789)); + const BoolV bc = BOr(V3IsGrtr(vc, vc2), V3IsGrtr(V3Neg(vc2), vc)); + + return BAllEq(BOr(ba, BOr(bb,bc)), BFFFF());*/ + } + + PX_FORCE_INLINE Ps::IntBool operator()(const PxBounds3& bounds) const + { + BucketBox tmp; + tmp.mCenter = bounds.getCenter(); + tmp.mExtents = bounds.getExtents(); + return (*this)(tmp); + } + + Vec3V mExtents; // extents of OBB + Vec3V mT; // translation of OBB + Mat33V mRT; // transpose of rotation matrix of OBB + Mat33V mART; // transpose of mRT, padded by epsilon + Vec3V mBB_xyz; // extents of OBB along coordinate axes + +/* Vec3V mBB_123; // projections of extents onto edge-cross axes + Vec3V mBB_456; + Vec3V mBB_789;*/ +}; +#else +struct OBBAABBTest_Scalar +{ + OBBAABBTest_Scalar(const PxMat33& rotation, const PxVec3& translation, const PxVec3& extents) + { + mR = rotation; + mT = translation; + mExtents = extents; + + const PxVec3 eps(1e-6f); + mAR = PxMat33(mR[0].abs() + eps, mR[1].abs() + eps, mR[2].abs() + eps); // Epsilon prevents floating-point inaccuracies (strategy borrowed from RAPID) + mBB_xyz = mAR.transform(mExtents); // Precompute box-box data - Courtesy of Erwin de Vries + +/* PxReal ex = mExtents.x, ey = mExtents.y, ez = mExtents.z; + mBB_1 = ey*mAR[2].x + ez*mAR[1].x; mBB_2 = ez*mAR[0].x + ex*mAR[2].x; mBB_3 = ex*mAR[1].x + ey*mAR[0].x; + mBB_4 = ey*mAR[2].y + ez*mAR[1].y; mBB_5 = ez*mAR[0].y + ex*mAR[2].y; mBB_6 = ex*mAR[1].y + ey*mAR[0].y; + mBB_7 = ey*mAR[2].z + ez*mAR[1].z; mBB_8 = ez*mAR[0].z + ex*mAR[2].z; mBB_9 = ex*mAR[1].z + ey*mAR[0].z;*/ + } + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + const PxVec3& c = box.mCenter; + const PxVec3& e = box.mExtents; + + const PxVec3 T = mT - c; + // Class I : A's basis vectors + if(PxAbs(T.x) > e.x + mBB_xyz.x) return Ps::IntFalse; + if(PxAbs(T.y) > e.y + mBB_xyz.y) return Ps::IntFalse; + if(PxAbs(T.z) > e.z + mBB_xyz.z) return Ps::IntFalse; + + // Class II : B's basis vectors + if(PxAbs(T.dot(mR[0])) > e.dot(mAR[0]) + mExtents.x) return Ps::IntFalse; + if(PxAbs(T.dot(mR[1])) > e.dot(mAR[1]) + mExtents.y) return Ps::IntFalse; + if(PxAbs(T.dot(mR[2])) > e.dot(mAR[2]) + mExtents.z) return Ps::IntFalse; + + // Class III : 9 cross products + if(0) + { + if(PxAbs(T.z*mR[0].y - T.y*mR[0].z) > e.y*mAR[0].z + e.z*mAR[0].y + mBB_1) return Ps::IntFalse; // L = A0 x B0 + if(PxAbs(T.z*mR[1].y - T.y*mR[1].z) > e.y*mAR[1].z + e.z*mAR[1].y + mBB_2) return Ps::IntFalse; // L = A0 x B1 + if(PxAbs(T.z*mR[2].y - T.y*mR[2].z) > e.y*mAR[2].z + e.z*mAR[2].y + mBB_3) return Ps::IntFalse; // L = A0 x B2 + + if(PxAbs(T.x*mR[0].z - T.z*mR[0].x) > e.x*mAR[0].z + e.z*mAR[0].x + mBB_4) return Ps::IntFalse; // L = A1 x B0 + if(PxAbs(T.x*mR[1].z - T.z*mR[1].x) > e.x*mAR[1].z + e.z*mAR[1].x + mBB_5) return Ps::IntFalse; // L = A1 x B1 + if(PxAbs(T.x*mR[2].z - T.z*mR[2].x) > e.x*mAR[2].z + e.z*mAR[2].x + mBB_6) return Ps::IntFalse; // L = A1 x B2 + + if(PxAbs(T.y*mR[0].x - T.x*mR[0].y) > e.x*mAR[0].y + e.y*mAR[0].x + mBB_7) return Ps::IntFalse; // L = A2 x B0 + if(PxAbs(T.y*mR[1].x - T.x*mR[1].y) > e.x*mAR[1].y + e.y*mAR[1].x + mBB_8) return Ps::IntFalse; // L = A2 x B1 + if(PxAbs(T.y*mR[2].x - T.x*mR[2].y) > e.x*mAR[2].y + e.y*mAR[2].x + mBB_9) return Ps::IntFalse; // L = A2 x B2 + } + return Ps::IntTrue; + } + +private: + PxMat33 mR; // rotation matrix + PxMat33 mAR; // absolute rotation matrix + PxVec3 mT; // translation from obb space to model space + PxVec3 mExtents; + + PxVec3 mBB_xyz; + + float mBB_1, mBB_2, mBB_3; + float mBB_4, mBB_5, mBB_6; + float mBB_7, mBB_8, mBB_9; +}; +#endif + +#ifdef USE_SIMD +typedef OBBAABBTest_SIMD BucketPrunerOBBAABBTest; +#else +typedef OBBAABBTest_Scalar BucketPrunerOBBAABBTest; +#endif + +/////////////////////////////////////////////////////////////////////////////// + +PxAgain BucketPrunerCore::overlap(const ShapeData& queryVolume, PrunerCallback& pcb) const +{ + PX_ASSERT(!mDirty); + PxAgain again = true; + + const PxBounds3& cullBox = queryVolume.getPrunerInflatedWorldAABB(); + + switch(queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if(queryVolume.isOBB()) + { + const BucketPrunerOverlapTraversal overlap; + again = overlap(*this, + BucketPrunerOBBAABBTest( + queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerWorldPos(), + queryVolume.getPrunerBoxGeomExtentsInflated()), + pcb, cullBox); + } + else + { + const BucketPrunerOverlapTraversal overlap; + again = overlap(*this, BucketPrunerAABBAABBTest(cullBox), pcb, cullBox); + } + } + break; + + case PxGeometryType::eCAPSULE: + { + const BucketPrunerOverlapTraversal overlap; + again = overlap(*this, + BucketPrunerOBBAABBTest( + queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerWorldPos(), + queryVolume.getPrunerBoxGeomExtentsInflated()), + pcb, cullBox); + } + break; + + case PxGeometryType::eSPHERE: + { + const Sphere& sphere = queryVolume.getGuSphere(); + const PxVec3 sphereExtents(sphere.radius); + const BucketPrunerOverlapTraversal overlap; + again = overlap(*this, BucketPrunerSphereAABBTest(sphere), pcb, cullBox); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const BucketPrunerOverlapTraversal overlap; + again = overlap(*this, + BucketPrunerOBBAABBTest( + queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerWorldPos(), + queryVolume.getPrunerBoxGeomExtentsInflated()), + pcb, cullBox); + } + break; + + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + return again; +} + +/////////////////////////////////////////////////////////////////////////////// + +void BucketPrunerCore::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i=0;i the pair is persistent + return &activePairs[offset]; +} + +// Internal version saving hash computation +PX_FORCE_INLINE BucketPrunerPair* BucketPrunerMap::findPair(const PrunerPayload& payload, PxU32 hashValue) const +{ + if(!mHashTable) + return NULL; // Nothing has been allocated yet + + BucketPrunerPair* PX_RESTRICT activePairs = mActivePairs; + const PxU32* PX_RESTRICT next = mNext; + + // Look for it in the table + PxU32 offset = mHashTable[hashValue]; + while(offset!=INVALID_ID && differentPair(activePairs[offset], payload)) + { + offset = next[offset]; // Better to have a separate array for this + } + if(offset==INVALID_ID) + return NULL; + PX_ASSERT(offset the pair is persistent + return &activePairs[offset]; +} + +/////////////////////////////////////////////////////////////////////////////// + +BucketPrunerPair* BucketPrunerMap::addPair(const PrunerPayload& payload, PxU32 coreIndex, PxU32 timeStamp) +{ + PxU32 hashValue = hash(payload) & mMask; + + { + BucketPrunerPair* PX_RESTRICT p = findPair(payload, hashValue); + if(p) + { + PX_ASSERT(p->mCoreIndex==coreIndex); + PX_ASSERT(p->mTimeStamp==timeStamp); + return p; // Persistent pair + } + } + + // This is a new pair + if(mNbActivePairs >= mHashSize) + { + // Get more entries + mHashSize = Ps::nextPowerOfTwo(mNbActivePairs+1); + mMask = mHashSize-1; + + reallocPairs(); + + // Recompute hash value with new hash size + hashValue = hash(payload) & mMask; // ### redundant hash computation here? + } + + BucketPrunerPair* PX_RESTRICT p = &mActivePairs[mNbActivePairs]; + p->mPayload = payload; + p->mCoreIndex = coreIndex; + p->mTimeStamp = timeStamp; + mNext[mNbActivePairs] = mHashTable[hashValue]; + mHashTable[hashValue] = mNbActivePairs++; + return p; +} + +/////////////////////////////////////////////////////////////////////////////// + +void BucketPrunerMap::removePairInternal(const PrunerPayload& /*payload*/, PxU32 hashValue, PxU32 pairIndex) +{ + // Walk the hash table to fix mNext + { + PxU32 offset = mHashTable[hashValue]; + PX_ASSERT(offset!=INVALID_ID); + + PxU32 previous=INVALID_ID; + while(offset!=pairIndex) + { + previous = offset; + offset = mNext[offset]; + } + + // Let us go/jump us + if(previous!=INVALID_ID) + { + PX_ASSERT(mNext[previous]==pairIndex); + mNext[previous] = mNext[pairIndex]; + } + // else we were the first + else mHashTable[hashValue] = mNext[pairIndex]; + // we're now free to reuse mNext[pairIndex] without breaking the list + } +#if PX_DEBUG + mNext[pairIndex]=INVALID_ID; +#endif + // Invalidate entry + + // Fill holes + if(1) + { + // 1) Remove last pair + const PxU32 lastPairIndex = mNbActivePairs-1; + if(lastPairIndex==pairIndex) + { + mNbActivePairs--; + } + else + { + const BucketPrunerPair* last = &mActivePairs[lastPairIndex]; + const PxU32 lastHashValue = hash(last->mPayload) & mMask; + + // Walk the hash table to fix mNext + PxU32 offset = mHashTable[lastHashValue]; + PX_ASSERT(offset!=INVALID_ID); + + PxU32 previous=INVALID_ID; + while(offset!=lastPairIndex) + { + previous = offset; + offset = mNext[offset]; + } + + // Let us go/jump us + if(previous!=INVALID_ID) + { + PX_ASSERT(mNext[previous]==lastPairIndex); + mNext[previous] = mNext[lastPairIndex]; + } + // else we were the first + else mHashTable[lastHashValue] = mNext[lastPairIndex]; + // we're now free to reuse mNext[lastPairIndex] without breaking the list + +#if PX_DEBUG + mNext[lastPairIndex]=INVALID_ID; +#endif + + // Don't invalidate entry since we're going to shrink the array + + // 2) Re-insert in free slot + mActivePairs[pairIndex] = mActivePairs[lastPairIndex]; +#if PX_DEBUG + PX_ASSERT(mNext[pairIndex]==INVALID_ID); +#endif + mNext[pairIndex] = mHashTable[lastHashValue]; + mHashTable[lastHashValue] = pairIndex; + + mNbActivePairs--; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool BucketPrunerMap::removePair(const PrunerPayload& payload, PxU32& coreIndex, PxU32& timeStamp) +{ + const PxU32 hashValue = hash(payload) & mMask; + const BucketPrunerPair* p = findPair(payload, hashValue); + if(!p) + return false; + PX_ASSERT(p->mPayload==payload); + + coreIndex = p->mCoreIndex; + timeStamp = p->mTimeStamp; + + removePairInternal(payload, hashValue, getPairIndex(p)); + + shrinkMemory(); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void BucketPrunerMap::shrinkMemory() +{ + // Check correct memory against actually used memory + const PxU32 correctHashSize = Ps::nextPowerOfTwo(mNbActivePairs); + if(mHashSize==correctHashSize) + return; + + if(mReservedMemory && correctHashSize < mReservedMemory) + return; + + // Reduce memory used + mHashSize = correctHashSize; + mMask = mHashSize-1; + + reallocPairs(); +} + +/////////////////////////////////////////////////////////////////////////////// + + static PX_FORCE_INLINE void storeDwords(PxU32* dest, PxU32 nb, PxU32 value) + { + while(nb--) + *dest++ = value; + } + +void BucketPrunerMap::reallocPairs() +{ + MBP_FREE(mHashTable); + mHashTable = reinterpret_cast(MBP_ALLOC(mHashSize*sizeof(PxU32))); + storeDwords(mHashTable, mHashSize, INVALID_ID); + + // Get some bytes for new entries + BucketPrunerPair* newPairs = reinterpret_cast(MBP_ALLOC(mHashSize * sizeof(BucketPrunerPair))); + PX_ASSERT(newPairs); + + PxU32* newNext = reinterpret_cast(MBP_ALLOC(mHashSize * sizeof(PxU32))); + PX_ASSERT(newNext); + + // Copy old data if needed + if(mNbActivePairs) + PxMemCopy(newPairs, mActivePairs, mNbActivePairs*sizeof(BucketPrunerPair)); + // ### check it's actually needed... probably only for pairs whose hash value was cut by the and + // yeah, since hash(id0, id1) is a constant + // However it might not be needed to recompute them => only less efficient but still ok + for(PxU32 i=0;i 3 bits/index => 3*5=15 bits total, for each of the 8 canonical directions + }PX_ALIGN_SUFFIX(16); + + PX_FORCE_INLINE PxU32 hash(const PrunerPayload& payload) + { +#if PX_P64_FAMILY +// const PxU32 h0 = Ps::hash((const void*)payload.data[0]); +// const PxU32 h1 = Ps::hash((const void*)payload.data[1]); + const PxU32 h0 = PxU32(PX_MAX_U32 & payload.data[0]); + const PxU32 h1 = PxU32(PX_MAX_U32 & payload.data[1]); + return Ps::hash(PxU64(h0)|(PxU64(h1)<<32)); +#else + return Ps::hash(PxU64(payload.data[0])|(PxU64(payload.data[1])<<32)); +#endif + } + +#ifdef USE_REGULAR_HASH_MAP + struct BucketPrunerPair : public Ps::UserAllocated + { + PX_FORCE_INLINE BucketPrunerPair() {} + PX_FORCE_INLINE BucketPrunerPair(PxU32 index, PxU32 stamp) : mCoreIndex(index), mTimeStamp(stamp) {} + PxU32 mCoreIndex; // index in mCoreObjects + PxU32 mTimeStamp; + }; + typedef Ps::HashMap BucketPrunerMap; +#else + struct BucketPrunerPair : public Ps::UserAllocated + { + PrunerPayload mPayload; + PxU32 mCoreIndex; // index in mCoreObjects + PxU32 mTimeStamp; + }; + + // Custom hash-map - currently faster than the regular hash-map (Ps::HashMap), in particular for 'find-and-erase' operations. + class BucketPrunerMap : public Ps::UserAllocated + { + public: + BucketPrunerMap(); + ~BucketPrunerMap(); + + void purge(); + void shrinkMemory(); + + BucketPrunerPair* addPair (const PrunerPayload& payload, PxU32 coreIndex, PxU32 timeStamp); + bool removePair (const PrunerPayload& payload, PxU32& coreIndex, PxU32& timeStamp); + const BucketPrunerPair* findPair (const PrunerPayload& payload) const; + PX_FORCE_INLINE PxU32 getPairIndex (const BucketPrunerPair* pair) const + { + return (PxU32((size_t(pair) - size_t(mActivePairs)))/sizeof(BucketPrunerPair)); + } + + PxU32 mHashSize; + PxU32 mMask; + PxU32 mNbActivePairs; + PxU32* mHashTable; + PxU32* mNext; + BucketPrunerPair* mActivePairs; + PxU32 mReservedMemory; + + PX_FORCE_INLINE BucketPrunerPair* findPair(const PrunerPayload& payload, PxU32 hashValue) const; + void removePairInternal(const PrunerPayload& payload, PxU32 hashValue, PxU32 pairIndex); + void reallocPairs(); + void reserveMemory(PxU32 memSize); + }; +#endif + + class BucketPrunerCore : public Ps::UserAllocated + { + public: + BucketPrunerCore(bool externalMemory=true); + ~BucketPrunerCore(); + + void release(); + + void setExternalMemory(PxU32 nbObjects, PxBounds3* boxes, PrunerPayload* objects); + + bool addObject(const PrunerPayload& object, const PxBounds3& worldAABB, PxU32 timeStamp=0); + bool removeObject(const PrunerPayload& object, PxU32& timeStamp); + bool updateObject(const PxBounds3& worldAABB, const PrunerPayload& object); + + // PT: look for objects marked with input timestamp everywhere in the structure, and remove them. This is the same + // as calling 'removeObject' individually for all these objects, but much more efficient. Returns number of removed objects. + PxU32 removeMarkedObjects(PxU32 timeStamp); + + PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + + void shiftOrigin(const PxVec3& shift); + + void visualize(Cm::RenderOutput& out, PxU32 color) const; + + PX_FORCE_INLINE void build() { classifyBoxes(); } + + PX_FORCE_INLINE PxU32 getNbObjects() const { return mNbFree + mCoreNbObjects; } + +// private: + PxU32 mCoreNbObjects; // Current number of objects in core arrays + PxU32 mCoreCapacity; // Capacity of core arrays + PxBounds3* mCoreBoxes; // Core array + PrunerPayload* mCoreObjects; // Core array + PxU32* mCoreRemap; // Remaps core index to sorted index, i.e. sortedIndex = mCoreRemap[coreIndex] + + BucketBox* mSortedWorldBoxes; // Sorted array + PrunerPayload* mSortedObjects; // Sorted array + + PxU32 mNbFree; // Current number of objects in the "free array" (mFreeObjects/mFreeBounds) + PrunerPayload mFreeObjects[FREE_PRUNER_SIZE]; // mNbFree objects are stored here + PxBounds3 mFreeBounds[FREE_PRUNER_SIZE]; // mNbFree object bounds are stored here + PxU32 mFreeStamps[FREE_PRUNER_SIZE]; + + BucketPrunerMap mMap; // Maps (PrunerPayload) object to corresponding index in core array. + // Objects in the free array do not appear in this map. + PxU32 mSortedNb; + PxU32 mSortedCapacity; + PxU32 mSortAxis; + + BucketBox mGlobalBox; // Global bounds around all objects in the structure (except the ones in the "free" array) + BucketPrunerNode mLevel1; + BucketPrunerNode mLevel2[5]; + BucketPrunerNode mLevel3[5][5]; + + bool mDirty; + bool mOwnMemory; + private: + void classifyBoxes(); + void allocateSortedMemory(PxU32 nb); + void resizeCore(); + PX_FORCE_INLINE void addObjectInternal(const PrunerPayload& object, const PxBounds3& worldAABB, PxU32 timeStamp); + }; + +#if PX_VC + #pragma warning(pop) +#endif + + class BucketPruner : public Pruner + { + public: + BucketPruner(); + virtual ~BucketPruner(); + + // Pruner + virtual bool addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* payload, PxU32 count, bool); + virtual void removeObjects(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count); + virtual void commit(); + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual const PrunerPayload& getPayload(PrunerHandle handle) const { return mPool.getPayload(handle); } + virtual const PrunerPayload& getPayload(PrunerHandle handle, PxBounds3*& bounds) const { return mPool.getPayload(handle, bounds); } + virtual void preallocate(PxU32 entries) { mPool.preallocate(entries); } + virtual void shiftOrigin(const PxVec3& shift); + virtual void visualize(Cm::RenderOutput& out, PxU32 color) const; + // merge not implemented for bucket pruner + virtual void merge(const void* ) {} + //~Pruner + + private: + BucketPrunerCore mCore; + PruningPool mPool; + }; + +} // namespace Sq + +} + +#endif // SQ_BUCKETPRUNER_H diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp new file mode 100644 index 000000000..214633c7b --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp @@ -0,0 +1,598 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsFoundation.h" +#include "SqCompoundPruner.h" +#include "SqIncrementalAABBTree.h" +#include "SqPruningPool.h" +#include "GuAABBTreeQuery.h" +#include "GuSphere.h" +#include "GuBox.h" +#include "GuCapsule.h" +#include "GuBounds.h" +#include "GuBVHStructure.h" + + +using namespace physx; +using namespace Gu; +using namespace Sq; +using namespace Cm; + +#define PARANOIA_CHECKS 0 + +/////////////////////////////////////////////////////////////////////////////////////////////// + +BVHCompoundPruner::BVHCompoundPruner() +{ + mCompoundTreePool.preallocate(32); + mMainTreeUpdateMap.resizeUninitialized(32); + mPoolActorMap.resizeUninitialized(32); + mChangedLeaves.reserve(32); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +BVHCompoundPruner::~BVHCompoundPruner() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool BVHCompoundPruner::addCompound(PrunerHandle* results, const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& transform, CompoundFlag::Enum flags, const PrunerPayload* userData) +{ + PX_ASSERT(bvhStructure.getNbBounds()); + + const PxBounds3 compoundBounds = PxBounds3::transformFast(transform, bvhStructure.getNodes()->mBV); + const PoolIndex poolIndex = mCompoundTreePool.addCompound(results, bvhStructure, compoundBounds, transform, flags, userData); + + mChangedLeaves.clear(); + IncrementalAABBTreeNode* node = mMainTree.insert(poolIndex, mCompoundTreePool.getCurrentCompoundBounds(), mChangedLeaves); + updateMapping(poolIndex, node); + + mActorPoolMap[compoundId] = poolIndex; + mPoolActorMap[poolIndex] = compoundId; + +#if PARANOIA_CHECKS + test(); +#endif + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node) +{ + // resize mapping if needed + if(mMainTreeUpdateMap.size() <= poolIndex) + { + const PxU32 resizeSize = mMainTreeUpdateMap.size() * 2; + mMainTreeUpdateMap.resize(resizeSize); + mPoolActorMap.resize(resizeSize); + } + + // if a node was split we need to update the node indices and also the sibling indices + if(!mChangedLeaves.empty()) + { + if(node && node->isLeaf()) + { + for(PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + mMainTreeUpdateMap[node->getPrimitives(NULL)[j]] = node; + } + } + + for(PxU32 i = 0; i < mChangedLeaves.size(); i++) + { + IncrementalAABBTreeNode* changedNode = mChangedLeaves[i]; + PX_ASSERT(changedNode->isLeaf()); + + for(PxU32 j = 0; j < changedNode->getNbPrimitives(); j++) + { + mMainTreeUpdateMap[changedNode->getPrimitives(NULL)[j]] = changedNode; + } + } + } + else + { + mMainTreeUpdateMap[poolIndex] = node; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::removeCompound(PrunerCompoundId compoundId) +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + + if(poolIndexEntry) + { + const PoolIndex poolIndex = poolIndexEntry->second; + const PoolIndex poolRelocatedLastIndex = mCompoundTreePool.removeCompound(poolIndex); + + IncrementalAABBTreeNode* node = mMainTree.remove(mMainTreeUpdateMap[poolIndex], poolIndex, mCompoundTreePool.getCurrentCompoundBounds()); + // if node moved to its parent + if(node && node->isLeaf()) + { + for (PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + mMainTreeUpdateMap[index] = node; + } + } + + // fix indices if we made a swap + if(poolRelocatedLastIndex != poolIndex) + { + mMainTreeUpdateMap[poolIndex] = mMainTreeUpdateMap[poolRelocatedLastIndex]; + mMainTree.fixupTreeIndices(mMainTreeUpdateMap[poolIndex], poolRelocatedLastIndex, poolIndex); + + mActorPoolMap[mPoolActorMap[poolRelocatedLastIndex]] = poolIndex; + mPoolActorMap[poolIndex] = mPoolActorMap[poolRelocatedLastIndex]; + } + + mActorPoolMap.erase(compoundId); + } + +#if PARANOIA_CHECKS + test(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::updateCompound(PrunerCompoundId compoundId, const PxTransform& transform) +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + + if(poolIndexEntry) + { + const PxU32 poolIndex = poolIndexEntry->second; + PxBounds3 localBounds; + const IncrementalAABBTreeNode* node = mCompoundTreePool.getCompoundTrees()[poolIndex].mTree->getNodes(); + mCompoundTreePool.getCompoundTrees()[poolIndex].mGlobalPose = transform; + V4StoreU(node->mBVMin, &localBounds.minimum.x); + PX_ALIGN(16, PxVec4) max4; + V4StoreA(node->mBVMax, &max4.x); + localBounds.maximum = PxVec3(max4.x, max4.y, max4.z); + const PxBounds3 compoundBounds = PxBounds3::transformFast(transform, localBounds); + mCompoundTreePool.getCurrentCompoundBounds()[poolIndex] = compoundBounds; + mChangedLeaves.clear(); + IncrementalAABBTreeNode* mainTreeNode = mMainTree.update(mMainTreeUpdateMap[poolIndex], poolIndex, mCompoundTreePool.getCurrentCompoundBounds(), mChangedLeaves); + // we removed node during update, need to update the mapping + updateMapping(poolIndex, mainTreeNode); + } + +#if PARANOIA_CHECKS + test(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::test() +{ + + if(mMainTree.getNodes()) + { + for(PxU32 i = 0; i < mCompoundTreePool.getNbObjects(); i++) + { + mMainTree.checkTreeLeaf(mMainTreeUpdateMap[i], i); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::release() +{ +} + +////////////////////////////////////////////////////////////////////////// +// Queries implementation +////////////////////////////////////////////////////////////////////////// +// Raycast/sweeps callback for main AABB tree +template +struct MainTreeRaycastCompoundPrunerCallback +{ + MainTreeRaycastCompoundPrunerCallback(const PxVec3& origin, const PxVec3& unitDir, const PxVec3& extent, PrunerCallback& prunerCallback, PxQueryFlags flags) + : mOrigin(origin), mUnitDir(unitDir), mExtent(extent), mPrunerCallback(prunerCallback), mQueryFlags(flags) + { + } + + virtual ~MainTreeRaycastCompoundPrunerCallback() {} + + virtual PxAgain invoke(PxReal& distance, const CompoundTree& compoundTree) + { + if(!(compoundTree.mFlags & PxU32(mQueryFlags)) || !compoundTree.mTree->getNodes()) + return true; + + // transfer to actor local space + const PxVec3 localOrigin = compoundTree.mGlobalPose.transformInv(mOrigin); + const PxVec3 localDir = compoundTree.mGlobalPose.q.rotateInv(mUnitDir); + PxVec3 localExtent = mExtent; + + if(tInflate) + { + PxBounds3 wBounds = PxBounds3::centerExtents(mOrigin, mExtent); + PxBounds3 localBounds = PxBounds3::transformSafe(compoundTree.mGlobalPose.getInverse(), wBounds); + localExtent = localBounds.getExtents(); + } + + // raycast the merged tree + return AABBTreeRaycast() + (compoundTree.mPruningPool->getObjects(), compoundTree.mPruningPool->getCurrentWorldBoxes(), *compoundTree.mTree, localOrigin, localDir, distance, localExtent, mPrunerCallback); + } + + PX_NOCOPY(MainTreeRaycastCompoundPrunerCallback) + +private: + const PxVec3& mOrigin; + const PxVec3& mUnitDir; + const PxVec3& mExtent; + PrunerCallback& mPrunerCallback; + PxQueryFlags mQueryFlags; +}; + +////////////////////////////////////////////////////////////////////////// +// raycast against the compound pruner +PxAgain BVHCompoundPruner::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& prunerCallback, PxQueryFlags flags) const +{ + PxAgain again = true; + + // search the main tree if there are nodes + if(mMainTree.getNodes()) + { + const PxVec3 extent(0.0f); + // main tree callback + MainTreeRaycastCompoundPrunerCallback pcb(origin, unitDir, extent, prunerCallback, flags); + // traverse the main tree + again = AABBTreeRaycast >() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, origin, unitDir, inOutDistance, extent, pcb); + } + + return again; +} + +////////////////////////////////////////////////////////////////////////// +// overlap main tree callback +// A.B. templated version is complicated due to test transformations, will do a callback per primitive +struct MainTreeOverlapCompoundPrunerCallback +{ + MainTreeOverlapCompoundPrunerCallback(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) + : mQueryVolume(queryVolume), mPrunerCallback(prunerCallback), mQueryFlags(flags) + { + } + + virtual ~MainTreeOverlapCompoundPrunerCallback() {} + + PX_NOCOPY(MainTreeOverlapCompoundPrunerCallback) + +protected: + const Gu::ShapeData& mQueryVolume; + PrunerCallback& mPrunerCallback; + PxQueryFlags mQueryFlags; +}; + +// OBB +struct MainTreeOBBOverlapCompoundPrunerCallback: public MainTreeOverlapCompoundPrunerCallback +{ + MainTreeOBBOverlapCompoundPrunerCallback(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) + : MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags) {} + virtual PxAgain invoke(PxReal& , const CompoundTree& compoundTree) + { + if(!(compoundTree.mFlags & PxU32(mQueryFlags)) || !compoundTree.mTree->getNodes()) + return true; + + const PxVec3 localPos = compoundTree.mGlobalPose.transformInv(mQueryVolume.getPrunerWorldPos()); + const PxMat33 transfMat(compoundTree.mGlobalPose.q); + const PxMat33 localRot = transfMat.getTranspose()*mQueryVolume.getPrunerWorldRot33(); + + const Gu::OBBAABBTest localTest(localPos, localRot, mQueryVolume.getPrunerBoxGeomExtentsInflated()); + // overlap the compound local tree + return AABBTreeOverlap() + (compoundTree.mPruningPool->getObjects(), compoundTree.mPruningPool->getCurrentWorldBoxes(), *compoundTree.mTree, localTest, mPrunerCallback); + } + + PX_NOCOPY(MainTreeOBBOverlapCompoundPrunerCallback) +}; + +// AABB +struct MainTreeAABBOverlapCompoundPrunerCallback: public MainTreeOverlapCompoundPrunerCallback +{ + MainTreeAABBOverlapCompoundPrunerCallback(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) + : MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags) {} + + virtual PxAgain invoke(PxReal& , const CompoundTree& compoundTree) + { + if(!(compoundTree.mFlags & PxU32(mQueryFlags)) || !compoundTree.mTree->getNodes()) + return true; + + const PxVec3 localPos = compoundTree.mGlobalPose.transformInv(mQueryVolume.getPrunerWorldPos()); + const PxMat33 transfMat(compoundTree.mGlobalPose.q); + const PxMat33 localRot = transfMat.getTranspose()*mQueryVolume.getPrunerWorldRot33(); + + // A.B. we dont have the AABB in local space, either we test OBB local space or + // we retest the AABB with the worldSpace AABB of the local tree??? + const Gu::OBBAABBTest localTest(localPos, localRot, mQueryVolume.getPrunerBoxGeomExtentsInflated()); + // overlap the compound local tree + return AABBTreeOverlap() + (compoundTree.mPruningPool->getObjects(), compoundTree.mPruningPool->getCurrentWorldBoxes(), *compoundTree.mTree, localTest, mPrunerCallback); + } + + PX_NOCOPY(MainTreeAABBOverlapCompoundPrunerCallback) +}; + +// Capsule +struct MainTreeCapsuleOverlapCompoundPrunerCallback: public MainTreeOverlapCompoundPrunerCallback +{ + MainTreeCapsuleOverlapCompoundPrunerCallback(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) + : MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags) {} + + virtual PxAgain invoke(PxReal& , const CompoundTree& compoundTree) + { + if(!(compoundTree.mFlags & PxU32(mQueryFlags)) || !compoundTree.mTree->getNodes()) + return true; + + const PxMat33 transfMat(compoundTree.mGlobalPose.q); + const Gu::Capsule& capsule = mQueryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest localTest( + compoundTree.mGlobalPose.transformInv(capsule.p1), + transfMat.getTranspose()*mQueryVolume.getPrunerWorldRot33().column0, + mQueryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + + // overlap the compound local tree + return AABBTreeOverlap() + (compoundTree.mPruningPool->getObjects(), compoundTree.mPruningPool->getCurrentWorldBoxes(), *compoundTree.mTree, localTest, mPrunerCallback); + } + + PX_NOCOPY(MainTreeCapsuleOverlapCompoundPrunerCallback) +}; + +// Sphere +struct MainTreeSphereOverlapCompoundPrunerCallback: public MainTreeOverlapCompoundPrunerCallback +{ + MainTreeSphereOverlapCompoundPrunerCallback(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) + : MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags) {} + + virtual PxAgain invoke(PxReal& , const CompoundTree& compoundTree) + { + if(!(compoundTree.mFlags & PxU32(mQueryFlags)) || !compoundTree.mTree->getNodes()) + return true; + + const Gu::Sphere& sphere = mQueryVolume.getGuSphere(); + Gu::SphereAABBTest localTest(compoundTree.mGlobalPose.transformInv(sphere.center), sphere.radius); + + // overlap the compound local tree + return AABBTreeOverlap() + (compoundTree.mPruningPool->getObjects(), compoundTree.mPruningPool->getCurrentWorldBoxes(), *compoundTree.mTree, localTest, mPrunerCallback); + } + + PX_NOCOPY(MainTreeSphereOverlapCompoundPrunerCallback) +}; + + +////////////////////////////////////////////////////////////////////////// +// overlap implementation +PxAgain BVHCompoundPruner::overlap(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) const +{ + PxAgain again = true; + + if(mMainTree.getNodes()) + { + switch (queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if(queryVolume.isOBB()) + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + MainTreeOBBOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags); + again = AABBTreeOverlap() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, test, pcb); + } + else + { + const Gu::AABBAABBTest test(queryVolume.getPrunerInflatedWorldAABB()); + MainTreeAABBOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags); + again = AABBTreeOverlap() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, test, pcb); + } + } + break; + case PxGeometryType::eCAPSULE: + { + const Gu::Capsule& capsule = queryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest test(capsule.p1, queryVolume.getPrunerWorldRot33().column0, + queryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + MainTreeCapsuleOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags); + again = AABBTreeOverlap() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, test, pcb); + } + break; + case PxGeometryType::eSPHERE: + { + const Gu::Sphere& sphere = queryVolume.getGuSphere(); + Gu::SphereAABBTest test(sphere.center, sphere.radius); + MainTreeSphereOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags); + again = AABBTreeOverlap() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, test, pcb); + } + break; + case PxGeometryType::eCONVEXMESH: + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + MainTreeOBBOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags); + again = AABBTreeOverlap() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, test, pcb); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + } + + return again; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +PxAgain BVHCompoundPruner::sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& prunerCallback, PxQueryFlags flags) const +{ + PxAgain again = true; + + if(mMainTree.getNodes()) + { + const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB(); + const PxVec3 extents = aabb.getExtents(); + const PxVec3 center = aabb.getCenter(); + MainTreeRaycastCompoundPrunerCallback pcb(center, unitDir, extents, prunerCallback, flags); + again = AABBTreeRaycast >() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, center, unitDir, inOutDistance, extents, pcb); + } + return again; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +const PrunerPayload& BVHCompoundPruner::getPayload(PrunerHandle handle, PrunerCompoundId compoundId) const +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + + return mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].mPruningPool->getPayload(handle); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +const PrunerPayload& BVHCompoundPruner::getPayload(PrunerHandle handle, PrunerCompoundId compoundId, PxBounds3*& bounds) const +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + + return mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].mPruningPool->getPayload(handle, bounds); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::updateObjectAfterManualBoundsUpdates(PrunerCompoundId compoundId, const PrunerHandle handle) +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + if(!poolIndexEntry) + return; + + mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].updateObjectAfterManualBoundsUpdates(handle); + + const PxU32 poolIndex = poolIndexEntry->second; + updateMainTreeNode(poolIndex); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::removeObject(PrunerCompoundId compoundId, const PrunerHandle handle) +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + if(!poolIndexEntry) + return; + + const PxU32 poolIndex = poolIndexEntry->second; + + mCompoundTreePool.getCompoundTrees()[poolIndex].removeObject(handle); + + // edge case, we removed all objects for the compound tree, we need to remove it now completely + if(!mCompoundTreePool.getCompoundTrees()[poolIndex].mTree->getNodes()) + { + removeCompound(compoundId); + } + else + { + updateMainTreeNode(poolIndex); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool BVHCompoundPruner::addObject(PrunerCompoundId compoundId, PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload userData) +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + if(!poolIndexEntry) + return false; + + mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].addObject(result, bounds, userData); + + const PxU32 poolIndex = poolIndexEntry->second; + updateMainTreeNode(poolIndex); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::updateMainTreeNode(PoolIndex poolIndex) +{ + PxBounds3 localBounds; + const IncrementalAABBTreeNode* node = mCompoundTreePool.getCompoundTrees()[poolIndex].mTree->getNodes(); + V4StoreU(node->mBVMin, &localBounds.minimum.x); + PX_ALIGN(16, PxVec4) max4; + V4StoreA(node->mBVMax, &max4.x); + localBounds.maximum = PxVec3(max4.x, max4.y, max4.z); + const PxBounds3 compoundBounds = PxBounds3::transformFast(mCompoundTreePool.getCompoundTrees()[poolIndex].mGlobalPose, localBounds); + mCompoundTreePool.getCurrentCompoundBounds()[poolIndex] = compoundBounds; + + mChangedLeaves.clear(); + IncrementalAABBTreeNode* mainTreeNode = mMainTree.update(mMainTreeUpdateMap[poolIndex], poolIndex, mCompoundTreePool.getCurrentCompoundBounds(), mChangedLeaves); + // we removed node during update, need to update the mapping + updateMapping(poolIndex, mainTreeNode); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::shiftOrigin(const PxVec3& shift) +{ + mCompoundTreePool.shiftOrigin(shift); + + mMainTree.shiftOrigin(shift); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::visualize(Cm::RenderOutput&, PxU32) const +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h new file mode 100644 index 000000000..2c2663f62 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SQ_COMPOUNDPRUNER_H +#define SQ_COMPOUNDPRUNER_H + +#include "SqPrunerMergeData.h" +#include "SqCompoundPruningPool.h" +#include "SqPruningPool.h" +#include "SqIncrementalAABBTree.h" +#include "PsHashMap.h" +#include "PsArray.h" + +namespace physx +{ +namespace Sq +{ + + /////////////////////////////////////////////////////////////////////////////////////////////// + + typedef Ps::HashMap ActorIdPoolIndexMap; + typedef Ps::Array PoolIndexActorIdMap; + + /////////////////////////////////////////////////////////////////////////////////////////////// + + class BVHCompoundPruner: public CompoundPruner + { + public: + BVHCompoundPruner(); + ~BVHCompoundPruner(); + + void release(); + + // CompoundPruner + // compound level + virtual bool addCompound(PrunerHandle* results, const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& transform, CompoundFlag::Enum flags, const PrunerPayload* userData); + virtual void removeCompound(PrunerCompoundId compoundId); + virtual void updateCompound(PrunerCompoundId compoundId, const PxTransform& transform); + // object level + virtual void updateObjectAfterManualBoundsUpdates(PrunerCompoundId compoundId, const PrunerHandle handle); + virtual void removeObject(PrunerCompoundId compoundId, const PrunerHandle handle); + virtual bool addObject(PrunerCompoundId compoundId, PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload userData); + //queries + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&, PxQueryFlags flags) const; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&, PxQueryFlags flags) const; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&, PxQueryFlags flags) const; + virtual const PrunerPayload& getPayload(PrunerHandle handle, PrunerCompoundId compoundId) const; + virtual const PrunerPayload& getPayload(PrunerHandle handle, PrunerCompoundId compoundId, PxBounds3*& bounds) const; + virtual void shiftOrigin(const PxVec3& shift); + virtual void visualize(Cm::RenderOutput&, PxU32) const; + // ~CompoundPruner + + private: + void updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node); + void updateMainTreeNode(PoolIndex index); + + void test(); + private: + IncrementalAABBTree mMainTree; + UpdateMap mMainTreeUpdateMap; + + CompoundTreePool mCompoundTreePool; + ActorIdPoolIndexMap mActorPoolMap; + PoolIndexActorIdMap mPoolActorMap; + NodeList mChangedLeaves; + }; +} +} + +#endif + diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp new file mode 100644 index 000000000..be67e1a9e --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp @@ -0,0 +1,279 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsFoundation.h" +#include "PsAllocator.h" +#include "SqCompoundPruningPool.h" +#include "SqAABBTree.h" +#include "SqPruningPool.h" +#include "GuBVHStructure.h" + +using namespace physx; +using namespace Gu; +using namespace Sq; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundTree::updateObjectAfterManualBoundsUpdates(PrunerHandle handle) +{ + const PxBounds3* newBounds = mPruningPool->getCurrentWorldBoxes(); + const PoolIndex poolIndex = mPruningPool->getIndex(handle); + NodeList changedLeaves; + changedLeaves.reserve(8); + IncrementalAABBTreeNode* node = mTree->update((*mUpdateMap)[poolIndex], poolIndex, newBounds, changedLeaves); + // we removed node during update, need to update the mapping + updateMapping(poolIndex, node, changedLeaves); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundTree::removeObject(PrunerHandle handle) +{ + const PoolIndex poolIndex = mPruningPool->getIndex(handle); // save the pool index for removed object + const PoolIndex poolRelocatedLastIndex = mPruningPool->removeObject(handle); // save the lastIndex returned by removeObject + + IncrementalAABBTreeNode* node = mTree->remove((*mUpdateMap)[poolIndex], poolIndex, mPruningPool->getCurrentWorldBoxes()); + // if node moved to its parent + if (node && node->isLeaf()) + { + for (PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + (*mUpdateMap)[index] = node; + } + } + + (*mUpdateMap)[poolIndex] = (*mUpdateMap)[poolRelocatedLastIndex]; + // fix indices if we made a swap + if(poolRelocatedLastIndex != poolIndex) + mTree->fixupTreeIndices((*mUpdateMap)[poolIndex], poolRelocatedLastIndex, poolIndex); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool CompoundTree::addObject(PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload payload) +{ + mPruningPool->addObjects(&result, &bounds, &payload, 1); + + const PoolIndex poolIndex = mPruningPool->getIndex(result); + NodeList changedLeaves; + changedLeaves.reserve(8); + IncrementalAABBTreeNode* node = mTree->insert(poolIndex, mPruningPool->getCurrentWorldBoxes(), changedLeaves); + updateMapping(poolIndex, node, changedLeaves); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundTree::updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node, const NodeList& changedLeaves) +{ + // if a node was split we need to update the node indices and also the sibling indices + if(!changedLeaves.empty()) + { + if(node && node->isLeaf()) + { + for(PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + (*mUpdateMap)[index] = node; + } + } + + for(PxU32 i = 0; i < changedLeaves.size(); i++) + { + IncrementalAABBTreeNode* changedNode = changedLeaves[i]; + PX_ASSERT(changedNode->isLeaf()); + + for(PxU32 j = 0; j < changedNode->getNbPrimitives(); j++) + { + const PoolIndex index = changedNode->getPrimitives(NULL)[j]; + (*mUpdateMap)[index] = changedNode; + } + } + } + else + { + (*mUpdateMap)[poolIndex] = node; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +CompoundTreePool::CompoundTreePool(): + mNbObjects(0), + mMaxNbObjects(0), + mCompoundBounds(NULL), + mCompoundTrees(NULL) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +CompoundTreePool::~CompoundTreePool() +{ + PX_FREE_AND_RESET(mCompoundBounds); + PX_FREE_AND_RESET(mCompoundTrees); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool CompoundTreePool::resize(PxU32 newCapacity) +{ + // PT: we always allocate one extra box, to make sure we can safely use V4 loads on the array + PxBounds3* newBoxes = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(newCapacity+1), "PxBounds3")); + CompoundTree* newTrees = reinterpret_cast(PX_ALLOC(sizeof(CompoundTree)*newCapacity, "IncrementalTrees*")); + + // memzero, we need to set the pointers in the compound tree to NULL + PxMemZero(newTrees, sizeof(CompoundTree)*newCapacity); + + if((NULL==newBoxes) || (NULL==newTrees)) + { + PX_FREE_AND_RESET(newBoxes); + PX_FREE_AND_RESET(newTrees); + return false; + } + + if(mCompoundBounds) PxMemCopy(newBoxes, mCompoundBounds, mNbObjects*sizeof(PxBounds3)); + if(mCompoundTrees) PxMemCopy(newTrees, mCompoundTrees, mNbObjects*sizeof(CompoundTree)); + mMaxNbObjects = newCapacity; + + PX_FREE_AND_RESET(mCompoundBounds); + PX_FREE_AND_RESET(mCompoundTrees); + mCompoundBounds = newBoxes; + mCompoundTrees = newTrees; + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundTreePool::preallocate(PxU32 newCapacity) +{ + if(newCapacity>mMaxNbObjects) + resize(newCapacity); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundTreePool::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i=0; i < mNbObjects; i++) + { + mCompoundBounds[i].minimum -= shift; + mCompoundBounds[i].maximum -= shift; + + mCompoundTrees[i].mGlobalPose.p -= shift; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +PoolIndex CompoundTreePool::addCompound(PrunerHandle* results, const BVHStructure& bvhStructure, const PxBounds3& compoundBounds, const PxTransform& transform, + CompoundFlag::Enum flags, const PrunerPayload* userData) +{ + if(mNbObjects==mMaxNbObjects) // increase the capacity on overflow + { + if(!resize(PxMax(mMaxNbObjects*2, 32))) + { + // pool can return an invalid handle if memory alloc fails + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "CompoundTreePool::addCompound memory allocation in resize failed."); + return INVALID_PRUNERHANDLE; + } + } + PX_ASSERT(mNbObjects!=mMaxNbObjects); + + const PoolIndex index = mNbObjects++; + + mCompoundBounds[index] = compoundBounds; + + const PxU32 nbObjects = bvhStructure.getNbBounds(); + + CompoundTree& tree = mCompoundTrees[index]; + PX_ASSERT(tree.mPruningPool == NULL); + PX_ASSERT(tree.mTree == NULL); + PX_ASSERT(tree.mUpdateMap == NULL); + + tree.mGlobalPose = transform; + tree.mFlags = flags; + + // prepare the pruning pool + PruningPool* pool = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PruningPool), PX_DEBUG_EXP("Pruning pool")), PruningPool); + pool->preallocate(nbObjects); + pool->addObjects(results, bvhStructure.getBounds(), userData, nbObjects); + tree.mPruningPool = pool; + + // prepare update map + UpdateMap* map = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(UpdateMap), PX_DEBUG_EXP("Update map")), UpdateMap); + map->resizeUninitialized(nbObjects); + tree.mUpdateMap = map; + + IncrementalAABBTree* iTree = PX_NEW(IncrementalAABBTree)(); + iTree->copy(bvhStructure, *map); + tree.mTree = iTree; + + return index; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +PoolIndex CompoundTreePool::removeCompound(PoolIndex indexOfRemovedObject) +{ + PX_ASSERT(mNbObjects); + + // release the tree + mCompoundTrees[indexOfRemovedObject].mTree->release(); + mCompoundTrees[indexOfRemovedObject].mTree->~IncrementalAABBTree(); + PX_FREE_AND_RESET(mCompoundTrees[indexOfRemovedObject].mTree); + + mCompoundTrees[indexOfRemovedObject].mUpdateMap->clear(); + mCompoundTrees[indexOfRemovedObject].mUpdateMap->~Array(); + PX_FREE_AND_RESET(mCompoundTrees[indexOfRemovedObject].mUpdateMap); + + mCompoundTrees[indexOfRemovedObject].mPruningPool->~PruningPool(); + PX_FREE_AND_RESET(mCompoundTrees[indexOfRemovedObject].mPruningPool); + + const PoolIndex indexOfLastObject = --mNbObjects; // swap the object at last index with index + if(indexOfLastObject!=indexOfRemovedObject) + { + // PT: move last object's data to recycled spot (from removed object) + + // PT: the last object has moved so we need to handle the mappings for this object + mCompoundBounds [indexOfRemovedObject] = mCompoundBounds [indexOfLastObject]; + mCompoundTrees [indexOfRemovedObject] = mCompoundTrees [indexOfLastObject]; + + mCompoundTrees [indexOfLastObject].mPruningPool = NULL; + mCompoundTrees [indexOfLastObject].mUpdateMap = NULL; + mCompoundTrees [indexOfLastObject].mTree = NULL; + } + + return indexOfLastObject; +} + diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h new file mode 100644 index 000000000..059273e02 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h @@ -0,0 +1,108 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SQ_COMPOUNDPRUNING_POOL_H +#define SQ_COMPOUNDPRUNING_POOL_H + +#include "SqPrunerMergeData.h" +#include "SqIncrementalAABBTree.h" +#include "PsArray.h" + +namespace physx +{ +namespace Sq +{ + class PruningPool; + + /////////////////////////////////////////////////////////////////////////////////////////////// + + typedef Ps::Array UpdateMap; + + /////////////////////////////////////////////////////////////////////////////////////////////// + + class CompoundTree + { + public: + void updateObjectAfterManualBoundsUpdates(PrunerHandle handle); + void removeObject(PrunerHandle handle); + bool addObject(PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload userData); + + private: + void updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node, const NodeList& changedLeaves); + + public: + IncrementalAABBTree* mTree; + PruningPool* mPruningPool; + UpdateMap* mUpdateMap; + PxTransform mGlobalPose; + CompoundFlag::Enum mFlags; + }; + + /////////////////////////////////////////////////////////////////////////////////////////////// + + class CompoundTreePool + { + public: + CompoundTreePool(); + ~CompoundTreePool(); + + void preallocate(PxU32 newCapacity); + + PoolIndex addCompound(PrunerHandle* results, const Gu::BVHStructure& bvhStructure, const PxBounds3& compoundBounds, const PxTransform& transform, CompoundFlag::Enum flags, const PrunerPayload* userData); + PoolIndex removeCompound(PoolIndex index); + + void shiftOrigin(const PxVec3& shift); + + PX_FORCE_INLINE const PxBounds3* getCurrentCompoundBounds() const { return mCompoundBounds; } + PX_FORCE_INLINE PxBounds3* getCurrentCompoundBounds() { return mCompoundBounds; } + + PX_FORCE_INLINE const CompoundTree* getCompoundTrees() const { return mCompoundTrees; } + PX_FORCE_INLINE CompoundTree* getCompoundTrees() { return mCompoundTrees; } + + PX_FORCE_INLINE PxU32 getNbObjects() const { return mNbObjects; } + + + private: + bool resize(PxU32 newCapacity); + + private: + PxU32 mNbObjects; //!< Current number of objects + PxU32 mMaxNbObjects; //!< Max. number of objects (capacity for mWorldBoxes, mObjects) + + //!< these arrays are parallel + PxBounds3* mCompoundBounds; //!< List of compound world boxes, stores mNbObjects, capacity=mMaxNbObjects + CompoundTree* mCompoundTrees; + }; + +} +} + +#endif + diff --git a/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp new file mode 100644 index 000000000..136652d58 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp @@ -0,0 +1,920 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "SqExtendedBucketPruner.h" +#include "SqAABBTree.h" +#include "SqPrunerMergeData.h" +#include "GuAABBTreeQuery.h" +#include "GuBounds.h" +#include "CmBitMap.h" + +using namespace physx; +using namespace Sq; +using namespace Gu; +using namespace Ps; + +#define NB_OBJECTS_PER_NODE 4 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Constructor, preallocate trees, bounds +ExtendedBucketPruner::ExtendedBucketPruner(const PruningPool* pool) + : +#if USE_INCREMENTAL_PRUNER + mPrunerCore(pool), +#else + mPrunerCore(false), +#endif + mPruningPool(pool), mMainTree(NULL), mBounds(NULL), mMergedTrees(NULL), + mCurrentTreeIndex(0), mTreesDirty(false) +{ + // preallocated size for bounds, trees + mCurrentTreeCapacity = 32; + + mBounds = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(mCurrentTreeCapacity + 1), "Bounds")); + mMergedTrees = reinterpret_cast(PX_ALLOC(sizeof(MergedTree)*mCurrentTreeCapacity, "AABB trees")); + mExtendedBucketPrunerMap.reserve(mCurrentTreeCapacity); + + // create empty main tree + mMainTree = PX_NEW(AABBTree); + + // create empty merge trees + for (PxU32 i = 0; i < mCurrentTreeCapacity; i++) + { + mMergedTrees[i].mTimeStamp = 0; + mMergedTrees[i].mTree = PX_NEW(AABBTree); + } +} + +////////////////////////////////////////////////////////////////////////// + +ExtendedBucketPruner::~ExtendedBucketPruner() +{ + // release main tree + if (mMainTree) + { + PX_DELETE_AND_RESET(mMainTree); + } + + // release merged trees + for (PxU32 i = 0; i < mCurrentTreeCapacity; i++) + { + AABBTree* aabbTree = mMergedTrees[i].mTree; + PX_DELETE(aabbTree); + } + + PX_FREE(mBounds); + PX_FREE(mMergedTrees); +} + +////////////////////////////////////////////////////////////////////////// +// release all objects in bucket pruner +void ExtendedBucketPruner::release() +{ + // release core bucket pruner + mPrunerCore.release(); + + mMainTreeUpdateMap.release(); + mMergeTreeUpdateMap.release(); + + // release all objecs from the map + mExtendedBucketPrunerMap.clear(); + + // release all merged trees + for (PxU32 i = 0; i < mCurrentTreeCapacity; i++) + { + mMergedTrees[i].mTimeStamp = 0; + mMergedTrees[i].mTree->release(); + } + + // reset current tree index + mCurrentTreeIndex = 0; +} + +////////////////////////////////////////////////////////////////////////// +// Add a tree from a pruning structure +// 1. get new tree index +// 2. initialize merged tree, bounds +// 3. create update map for the merged tree +// 4. build new tree of trees from given trees bounds +// 5. add new objects into extended bucket pruner map +// 6. shift indices in the merged tree +void ExtendedBucketPruner::addTree(const AABBTreeMergeData& mergeData, PxU32 timeStamp) +{ + // check if we have to resize + if(mCurrentTreeIndex == mCurrentTreeCapacity) + { + resize(mCurrentTreeCapacity*2); + } + + // get current merge tree index + const PxU32 mergeTreeIndex = mCurrentTreeIndex++; + + // get payloads pointers - the pointers start at mIndicesOffset, thats where all + // objects were added before merge was called + const PrunerPayload* payloads = &mPruningPool->getObjects()[mergeData.mIndicesOffset]; + + // setup merged tree with the merge data and timestamp + mMergedTrees[mergeTreeIndex].mTimeStamp = timeStamp; + AABBTree& mergedTree = *mMergedTrees[mergeTreeIndex].mTree; + mergedTree.initTree(mergeData); + // set bounds + mBounds[mergeTreeIndex] = mergeData.getRootNode().mBV; + + // update temporally update map for the current merge tree, map is used to setup the base extended bucket pruner map + mMergeTreeUpdateMap.initMap(mergeData.mNbIndices, mergedTree); + + // create new base tree of trees + buildMainAABBTree(); + + // Add each object into extended bucket pruner hash map + for (PxU32 i = 0; i < mergeData.mNbIndices; i++) + { + ExtendedBucketPrunerData mapData; + mapData.mMergeIndex = mergeTreeIndex; + mapData.mTimeStamp = timeStamp; + PX_ASSERT(mMergeTreeUpdateMap[i] < mergedTree.getNbNodes()); + // get node information from the merge tree update map + mapData.mSubTreeNode = mMergeTreeUpdateMap[i]; + mExtendedBucketPrunerMap.insert(payloads[i], mapData); + } + // merged tree indices needs to be shifted now, we cannot shift it in init - the update map + // could not be constructed otherwise, as the indices wont start from 0. The indices + // needs to be shifted by offset from the pruning pool, where the new objects were added into the pruning pool. + mergedTree.shiftIndices(mergeData.mIndicesOffset); + +#if PX_DEBUG + checkValidity(); +#endif // PX_DEBUG +} + +////////////////////////////////////////////////////////////////////////// +// Builds the new main AABB tree with given current active merged trees and its bounds +void ExtendedBucketPruner::buildMainAABBTree() +{ + // create the AABB tree from given merged trees bounds + AABBTreeBuildParams sTB; + sTB.mNbPrimitives = mCurrentTreeIndex; + sTB.mAABBArray = mBounds; + sTB.mLimit = NB_OBJECTS_PER_NODE; + bool status = mMainTree->build(sTB); + + PX_UNUSED(status); + PX_ASSERT(status); + + // Init main tree update map for the new main tree + mMainTreeUpdateMap.initMap(mCurrentTreeIndex, *mMainTree); +} + +////////////////////////////////////////////////////////////////////////// +// resize internal memory, buffers +void ExtendedBucketPruner::resize(PxU32 size) +{ + PX_ASSERT(size > mCurrentTreeCapacity); + // allocate new bounds + PxBounds3* newBounds = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(size + 1), "Bounds")); + // copy previous bounds + PxMemCopy(newBounds, mBounds, sizeof(PxBounds3)*mCurrentTreeCapacity); + PX_FREE(mBounds); + mBounds = newBounds; + + // allocate new merged trees + MergedTree* newMergeTrees = reinterpret_cast(PX_ALLOC(sizeof(MergedTree)*size, "AABB trees")); + // copy previous merged trees + PxMemCopy(newMergeTrees, mMergedTrees, sizeof(MergedTree)*mCurrentTreeCapacity); + PX_FREE(mMergedTrees); + mMergedTrees = newMergeTrees; + // allocate new trees for merged trees + for (PxU32 i = mCurrentTreeCapacity; i < size; i++) + { + mMergedTrees[i].mTimeStamp = 0; + mMergedTrees[i].mTree = PX_NEW(AABBTree); + } + + mCurrentTreeCapacity = size; +} + +////////////////////////////////////////////////////////////////////////// +// Update object +bool ExtendedBucketPruner::updateObject(const PxBounds3& worldAABB, const PrunerPayload& object, const PoolIndex poolIndex) +{ + const ExtendedBucketPrunerMap::Entry* extendedPrunerEntry = mExtendedBucketPrunerMap.find(object); + + // if object is not in tree of trees, it is in bucket pruner core + if(!extendedPrunerEntry) + { +#if USE_INCREMENTAL_PRUNER + PX_UNUSED(worldAABB); + return mPrunerCore.updateObject(poolIndex); +#else + PX_UNUSED(poolIndex); + return mPrunerCore.updateObject(worldAABB, object); +#endif + } + else + { + const ExtendedBucketPrunerData& data = extendedPrunerEntry->second; + + PX_ASSERT(data.mMergeIndex < mCurrentTreeIndex); + + // update tree where objects belongs to + AABBTree& tree = *mMergedTrees[data.mMergeIndex].mTree; + PX_ASSERT(data.mSubTreeNode < tree.getNbNodes()); + // mark for refit node in merged tree + tree.markNodeForRefit(data.mSubTreeNode); + PX_ASSERT(mMainTreeUpdateMap[data.mMergeIndex] < mMainTree->getNbNodes()); + // mark for refit node in main aabb tree + mMainTree->markNodeForRefit(mMainTreeUpdateMap[data.mMergeIndex]); + mTreesDirty = true; + } + return true; +} + +////////////////////////////////////////////////////////////////////////// +// refit merged nodes +// 1. refit nodes in merged trees +// 2. check if after refit root node is valid - might happen edge case +// where all objects were released - the root node is then invalid +// in this edge case we need to compact the merged trees array +// and create new main AABB tree +// 3. If all merged trees bounds are valid - refit main tree +// 4. If bounds are invalid create new main AABB tree +void ExtendedBucketPruner::refitMarkedNodes(const PxBounds3* boxes) +{ + // if no tree needs update early exit + if(!mTreesDirty) + return; + + // refit trees and update bounds for main tree + PxU32 nbValidTrees = 0; + for (PxU32 i = mCurrentTreeIndex; i--; ) + { + AABBTree& tree = *mMergedTrees[i].mTree; + tree.refitMarkedNodes(boxes); + const PxBounds3& bounds = tree.getNodes()[0].mBV; + // check if bounds are valid, if all objects of the tree were released, the bounds + // will be invalid, in that case we cannot use this tree anymore. + if(bounds.isValid()) + { + nbValidTrees++; + } + mBounds[i] = bounds; + } + + if(nbValidTrees == mCurrentTreeIndex) + { + // no tree has been removed refit main tree + mMainTree->refitMarkedNodes(mBounds); + } + else + { + // edge case path, tree does not have a valid root node bounds - all objects from the tree were released + // we might even fire perf warning + // compact the tree array - no holes in the array, remember the swap position + PxU32* swapMap = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mCurrentTreeIndex + 1, "Swap Map")); + PxU32 writeIndex = 0; + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + AABBTree& tree = *mMergedTrees[i].mTree; + if(tree.getNodes()[0].mBV.isValid()) + { + // we have to store the tree into an empty location + if(i != writeIndex) + { + PX_ASSERT(writeIndex < i); + AABBTree* ptr = mMergedTrees[writeIndex].mTree; + mMergedTrees[writeIndex] = mMergedTrees[i]; + mMergedTrees[i].mTree = ptr; + mBounds[writeIndex] = mBounds[i]; + } + // remember the swap location + swapMap[i] = writeIndex; + writeIndex++; + } + else + { + // tree is not valid, release it + tree.release(); + mMergedTrees[i].mTimeStamp = 0; + } + + // remember the swap + swapMap[mCurrentTreeIndex] = i; + } + + PX_ASSERT(writeIndex == nbValidTrees); + + // new merged trees size + mCurrentTreeIndex = nbValidTrees; + + if(mCurrentTreeIndex) + { + // trees have changed, we need to rebuild the main tree + buildMainAABBTree(); + + // fixup the object entries, the merge index has changed + for (ExtendedBucketPrunerMap::Iterator iter = mExtendedBucketPrunerMap.getIterator(); !iter.done(); ++iter) + { + ExtendedBucketPrunerData& data = iter->second; + PX_ASSERT(swapMap[data.mMergeIndex] < nbValidTrees); + data.mMergeIndex = swapMap[data.mMergeIndex]; + } + } + else + { + // if there is no tree release the main tree + mMainTree->release(); + } + PX_FREE(swapMap); + } +#if PX_DEBUG + checkValidity(); +#endif + mTreesDirty = false; +} + +////////////////////////////////////////////////////////////////////////// +// remove object +bool ExtendedBucketPruner::removeObject(const PrunerPayload& object, PxU32 objectIndex, const PrunerPayload& swapObject, + PxU32 swapObjectIndex, PxU32& timeStamp) +{ + ExtendedBucketPrunerMap::Entry dataEntry; + + // if object is not in tree of trees, it is in bucket pruner core + if (!mExtendedBucketPrunerMap.erase(object, dataEntry)) + { + // we need to call invalidateObjects, it might happen that the swapped object + // does belong to the extended bucket pruner, in that case the objects index + // needs to be swapped. + // do not call additional bucket pruner swap, that does happen during remove + swapIndex(objectIndex, swapObject, swapObjectIndex, false); +#if USE_INCREMENTAL_PRUNER + return mPrunerCore.removeObject(objectIndex, swapObjectIndex, timeStamp); +#else + return mPrunerCore.removeObject(object, timeStamp); +#endif + } + else + { + const ExtendedBucketPrunerData& data = dataEntry.second; + + // mark tree nodes where objects belongs to + AABBTree& tree = *mMergedTrees[data.mMergeIndex].mTree; + PX_ASSERT(data.mSubTreeNode < tree.getNbNodes()); + // mark the merged tree for refit + tree.markNodeForRefit(data.mSubTreeNode); + PX_ASSERT(mMainTreeUpdateMap[data.mMergeIndex] < mMainTree->getNbNodes()); + // mark the main tree for refit + mMainTree->markNodeForRefit(mMainTreeUpdateMap[data.mMergeIndex]); + + // call invalidate object to swap the object indices in the merged trees + invalidateObject(data, objectIndex, swapObject, swapObjectIndex); + + mTreesDirty = true; + } +#if PX_DEBUG + checkValidity(); +#endif // PX_DEBUG + return true; +} + +////////////////////////////////////////////////////////////////////////// +// invalidate object +// remove the objectIndex from the merged tree +void ExtendedBucketPruner::invalidateObject(const ExtendedBucketPrunerData& data, PxU32 objectIndex, const PrunerPayload& swapObject, + PxU32 swapObjectIndex) +{ + // get the merged tree + AABBTree& tree = *mMergedTrees[data.mMergeIndex].mTree; + PX_ASSERT(data.mSubTreeNode < tree.getNbNodes()); + PX_ASSERT(tree.getNodes()[data.mSubTreeNode].isLeaf()); + // get merged tree node + AABBTreeRuntimeNode& node0 = tree.getNodes()[data.mSubTreeNode]; + const PxU32 nbPrims = node0.getNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= NB_OBJECTS_PER_NODE); + + // retrieve the primitives pointer + PxU32* primitives = node0.getPrimitives(tree.getIndices()); + PX_ASSERT(primitives); + + // Look for desired pool index in the leaf + bool foundIt = false; + for (PxU32 i = 0; i < nbPrims; i++) + { + if (objectIndex == primitives[i]) + { + foundIt = true; + const PxU32 last = nbPrims - 1; + node0.setNbRunTimePrimitives(last); + primitives[i] = INVALID_POOL_ID; // Mark primitive index as invalid in the node + + // Swap within the leaf node. No need to update the mapping since they should all point + // to the same tree node anyway. + if (last != i) + Ps::swap(primitives[i], primitives[last]); + break; + } + } + PX_ASSERT(foundIt); + PX_UNUSED(foundIt); + + swapIndex(objectIndex, swapObject, swapObjectIndex); +} + +// Swap object index +// if swapObject is in a merged tree its index needs to be swapped with objectIndex +void ExtendedBucketPruner::swapIndex(PxU32 objectIndex, const PrunerPayload& swapObject, PxU32 swapObjectIndex, bool corePrunerIncluded) +{ + PX_UNUSED(corePrunerIncluded); + if (objectIndex == swapObjectIndex) + return; + + const ExtendedBucketPrunerMap::Entry* extendedPrunerSwapEntry = mExtendedBucketPrunerMap.find(swapObject); + + // if swapped object index is in extended pruner, we have to fix the primitives index + if (extendedPrunerSwapEntry) + { + const ExtendedBucketPrunerData& swapData = extendedPrunerSwapEntry->second; + AABBTree& swapTree = *mMergedTrees[swapData.mMergeIndex].mTree; + // With multiple primitives per leaf, tree nodes may very well be the same for different pool indices. + // However the pool indices may be the same when a swap has been skipped in the pruning pool, in which + // case there is nothing to do. + PX_ASSERT(swapData.mSubTreeNode < swapTree.getNbNodes()); + PX_ASSERT(swapTree.getNodes()[swapData.mSubTreeNode].isLeaf()); + AABBTreeRuntimeNode* node1 = swapTree.getNodes() + swapData.mSubTreeNode; + const PxU32 nbPrims = node1->getNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= NB_OBJECTS_PER_NODE); + + // retrieve the primitives pointer + PxU32* primitives = node1->getPrimitives(swapTree.getIndices()); + PX_ASSERT(primitives); + + // look for desired pool index in the leaf + bool foundIt = false; + for (PxU32 i = 0; i < nbPrims; i++) + { + if (swapObjectIndex == primitives[i]) + { + foundIt = true; + primitives[i] = objectIndex; // point node to the pool object moved to + break; + } + } + PX_ASSERT(foundIt); + PX_UNUSED(foundIt); + } +#if USE_INCREMENTAL_PRUNER + else + { + if(corePrunerIncluded) + mPrunerCore.swapIndex(objectIndex, swapObjectIndex); + } +#endif +} + +////////////////////////////////////////////////////////////////////////// +// Optimized removal of timestamped objects from the extended bucket pruner +PxU32 ExtendedBucketPruner::removeMarkedObjects(PxU32 timeStamp) +{ + // remove objects from the core bucket pruner + PxU32 retVal = mPrunerCore.removeMarkedObjects(timeStamp); + + // nothing to be removed + if(!mCurrentTreeIndex) + return retVal; + + // if last merged tree is the timeStamp to remove, we can clear all + // this is safe as the merged trees array is time ordered, never shifted + if(mMergedTrees[mCurrentTreeIndex - 1].mTimeStamp == timeStamp) + { + retVal += mExtendedBucketPrunerMap.size(); + cleanTrees(); + return retVal; + } + + // get the highest index in the merged trees array, where timeStamp match + // we release than all trees till the index + PxU32 highestTreeIndex = 0xFFFFFFFF; + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + if(mMergedTrees[i].mTimeStamp == timeStamp) + highestTreeIndex = i; + else + break; + } + + // if no timestamp found early exit + if(highestTreeIndex == 0xFFFFFFFF) + { + return retVal; + } + + PX_ASSERT(highestTreeIndex < mCurrentTreeIndex); + // get offset, where valid trees start + const PxU32 mergeTreeOffset = highestTreeIndex + 1; + + // shrink the array to merged trees with a valid timeStamp + mCurrentTreeIndex = mCurrentTreeIndex - mergeTreeOffset; + // go over trees and swap released trees with valid trees from the back (valid trees are at the back) + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + // store bounds, timestamp + mBounds[i] = mMergedTrees[mergeTreeOffset + i].mTree->getNodes()[0].mBV; + mMergedTrees[i].mTimeStamp = mMergedTrees[mergeTreeOffset + i].mTimeStamp; + + // release the tree with timestamp + AABBTree* ptr = mMergedTrees[i].mTree; + ptr->release(); + + // store the valid tree + mMergedTrees[i].mTree = mMergedTrees[mergeTreeOffset + i].mTree; + // store the release tree at the offset + mMergedTrees[mergeTreeOffset + i].mTree = ptr; + mMergedTrees[mergeTreeOffset + i].mTimeStamp = 0; + } + // release the rest of the trees with not valid timestamp + for (PxU32 i = mCurrentTreeIndex; i <= highestTreeIndex; i++) + { + mMergedTrees[i].mTree->release(); + mMergedTrees[i].mTimeStamp = 0; + } + + // build new main AABB tree with only trees with valid valid timeStamp + buildMainAABBTree(); + + // remove all unnecessary trees and map entries + bool removeEntry = false; + PxU32 numRemovedEntries = 0; + ExtendedBucketPrunerMap::EraseIterator eraseIterator = mExtendedBucketPrunerMap.getEraseIterator(); + ExtendedBucketPrunerMap::Entry* entry = eraseIterator.eraseCurrentGetNext(removeEntry); + while (entry) + { + ExtendedBucketPrunerData& data = entry->second; + // data to be removed + if (data.mTimeStamp == timeStamp) + { + removeEntry = true; + numRemovedEntries++; + } + else + { + // update the merge index and main tree node index + PX_ASSERT(highestTreeIndex < data.mMergeIndex); + data.mMergeIndex -= mergeTreeOffset; + removeEntry = false; + } + entry = eraseIterator.eraseCurrentGetNext(removeEntry); + } + +#if PX_DEBUG + checkValidity(); +#endif // PX_DEBUG + // return the number of removed objects + return retVal + numRemovedEntries; +} + +////////////////////////////////////////////////////////////////////////// +// clean all trees, all objects have been released +void ExtendedBucketPruner::cleanTrees() +{ + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + mMergedTrees[i].mTree->release(); + mMergedTrees[i].mTimeStamp = 0; + } + mExtendedBucketPrunerMap.clear(); + mCurrentTreeIndex = 0; + mMainTree->release(); +} + +////////////////////////////////////////////////////////////////////////// +// shift origin +void ExtendedBucketPruner::shiftOrigin(const PxVec3& shift) +{ + mMainTree->shiftOrigin(shift); + + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + mMergedTrees[i].mTree->shiftOrigin(shift); + } + + mPrunerCore.shiftOrigin(shift); +} + +////////////////////////////////////////////////////////////////////////// +// Queries implementation +////////////////////////////////////////////////////////////////////////// +// Raycast/sweeps callback for main AABB tree +template +struct MainTreeRaycastPrunerCallback: public PrunerCallback +{ + MainTreeRaycastPrunerCallback(const PxVec3& origin, const PxVec3& unitDir, const PxVec3& extent, PrunerCallback& prunerCallback, const PruningPool* pool) + : mOrigin(origin), mUnitDir(unitDir), mExtent(extent), mPrunerCallback(prunerCallback), mPruningPool(pool) + { + } + + virtual PxAgain invoke(PxReal& distance, const PrunerPayload& payload) + { + // payload data match merged tree data MergedTree, we can cast it + const AABBTree* aabbTree = reinterpret_cast (payload.data[0]); + // raycast the merged tree + return AABBTreeRaycast()(mPruningPool->getObjects(), mPruningPool->getCurrentWorldBoxes(), *aabbTree, mOrigin, mUnitDir, distance, mExtent, mPrunerCallback); + } + + PX_NOCOPY(MainTreeRaycastPrunerCallback) + +private: + const PxVec3& mOrigin; + const PxVec3& mUnitDir; + const PxVec3& mExtent; + PrunerCallback& mPrunerCallback; + const PruningPool* mPruningPool; +}; + +////////////////////////////////////////////////////////////////////////// +// raycast against the extended bucket pruner +PxAgain ExtendedBucketPruner::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& prunerCallback) const +{ + PxAgain again = true; + + // searc the bucket pruner first + if (mPrunerCore.getNbObjects()) + again = mPrunerCore.raycast(origin, unitDir, inOutDistance, prunerCallback); + + if (again && mExtendedBucketPrunerMap.size()) + { + const PxVec3 extent(0.0f); + // main tree callback + MainTreeRaycastPrunerCallback pcb(origin, unitDir, extent, prunerCallback, mPruningPool); + // traverse the main tree + again = AABBTreeRaycast()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, origin, unitDir, inOutDistance, extent, pcb); + } + + return again; +} + +////////////////////////////////////////////////////////////////////////// +// overlap main tree callback +template +struct MainTreeOverlapPrunerCallback : public PrunerCallback +{ + MainTreeOverlapPrunerCallback(const Test& test, PrunerCallback& prunerCallback, const PruningPool* pool) + : mTest(test), mPrunerCallback(prunerCallback), mPruningPool(pool) + { + } + + virtual PxAgain invoke(PxReal& , const PrunerPayload& payload) + { + // payload data match merged tree data MergedTree, we can cast it + const AABBTree* aabbTree = reinterpret_cast (payload.data[0]); + // overlap the merged tree + return AABBTreeOverlap()(mPruningPool->getObjects(), mPruningPool->getCurrentWorldBoxes(), *aabbTree, mTest, mPrunerCallback); + } + + PX_NOCOPY(MainTreeOverlapPrunerCallback) + +private: + const Test& mTest; + PrunerCallback& mPrunerCallback; + const PruningPool* mPruningPool; +}; + +////////////////////////////////////////////////////////////////////////// +// overlap implementation +PxAgain ExtendedBucketPruner::overlap(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback) const +{ + PxAgain again = true; + + // core bucket pruner overlap + if (mPrunerCore.getNbObjects()) + again = mPrunerCore.overlap(queryVolume, prunerCallback); + + if(again && mExtendedBucketPrunerMap.size()) + { + switch (queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if (queryVolume.isOBB()) + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + MainTreeOverlapPrunerCallback pcb(test, prunerCallback, mPruningPool); + again = AABBTreeOverlap()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, test, pcb); + } + else + { + const Gu::AABBAABBTest test(queryVolume.getPrunerInflatedWorldAABB()); + MainTreeOverlapPrunerCallback pcb(test, prunerCallback, mPruningPool); + again = AABBTreeOverlap()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, test, pcb); + } + } + break; + case PxGeometryType::eCAPSULE: + { + const Gu::Capsule& capsule = queryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest test(capsule.p1, queryVolume.getPrunerWorldRot33().column0, + queryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + MainTreeOverlapPrunerCallback pcb(test, prunerCallback, mPruningPool); + again = AABBTreeOverlap()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, test, pcb); + } + break; + case PxGeometryType::eSPHERE: + { + const Gu::Sphere& sphere = queryVolume.getGuSphere(); + Gu::SphereAABBTest test(sphere.center, sphere.radius); + MainTreeOverlapPrunerCallback pcb(test, prunerCallback, mPruningPool); + again = AABBTreeOverlap()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, test, pcb); + } + break; + case PxGeometryType::eCONVEXMESH: + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + MainTreeOverlapPrunerCallback pcb(test, prunerCallback, mPruningPool); + again = AABBTreeOverlap()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, test, pcb); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + } + + return again; +} + +////////////////////////////////////////////////////////////////////////// +// sweep implementation +PxAgain ExtendedBucketPruner::sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& prunerCallback) const +{ + PxAgain again = true; + + // core bucket pruner sweep + if (mPrunerCore.getNbObjects()) + again = mPrunerCore.sweep(queryVolume, unitDir, inOutDistance, prunerCallback); + + if(again && mExtendedBucketPrunerMap.size()) + { + const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB(); + const PxVec3 extents = aabb.getExtents(); + const PxVec3 center = aabb.getCenter(); + MainTreeRaycastPrunerCallback pcb(center, unitDir, extents, prunerCallback, mPruningPool); + again = AABBTreeRaycast()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, center, unitDir, inOutDistance, extents, pcb); + } + return again; +} + + +////////////////////////////////////////////////////////////////////////// +#include "CmRenderOutput.h" + +// visualization +static void visualizeTree(Cm::RenderOutput& out, PxU32 color, AABBTree* tree) +{ + if(tree && tree->getNodes()) + { + struct Local + { + static void _Draw(const AABBTreeRuntimeNode* root, const AABBTreeRuntimeNode* node, Cm::RenderOutput& out_) + { + out_ << Cm::DebugBox(node->mBV, true); + if (node->isLeaf()) + return; + _Draw(root, node->getPos(root), out_); + _Draw(root, node->getNeg(root), out_); + } + }; + out << PxTransform(PxIdentity); + out << color; + Local::_Draw(tree->getNodes(), tree->getNodes(), out); + } +} + +void ExtendedBucketPruner::visualize(Cm::RenderOutput& out, PxU32 color) const +{ + visualizeTree(out, color, mMainTree); + + for(PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + visualizeTree(out, color, mMergedTrees[i].mTree); + } + + mPrunerCore.visualize(out, color); +} + +////////////////////////////////////////////////////////////////////////// + +#if PX_DEBUG +// extended bucket pruner validity check +bool ExtendedBucketPruner::checkValidity() +{ + Cm::BitMap testBitmap; + testBitmap.resizeAndClear(mCurrentTreeIndex); + for (PxU32 i = 0; i < mMainTree->getNbNodes(); i++) + { + const AABBTreeRuntimeNode& node = mMainTree->getNodes()[i]; + if(node.isLeaf()) + { + const PxU32 nbPrims = node.getNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= NB_OBJECTS_PER_NODE); + + const PxU32* primitives = node.getPrimitives(mMainTree->getIndices()); + for (PxU32 j = 0; j < nbPrims; j++) + { + const PxU32 index = primitives[j]; + // check if index is correct + PX_ASSERT(index < mCurrentTreeIndex); + // mark the index in the test bitmap, must be once set only, all merged trees must be in the main tree + PX_ASSERT(testBitmap.test(index) == IntFalse); + testBitmap.set(index); + } + } + } + + Cm::BitMap mergeTreeTestBitmap; + mergeTreeTestBitmap.resizeAndClear(mPruningPool->getNbActiveObjects()); + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + // check if bounds are the same as the merged tree root bounds + PX_ASSERT(mBounds[i].maximum.x == mMergedTrees[i].mTree->getNodes()[0].mBV.maximum.x); + PX_ASSERT(mBounds[i].maximum.y == mMergedTrees[i].mTree->getNodes()[0].mBV.maximum.y); + PX_ASSERT(mBounds[i].maximum.z == mMergedTrees[i].mTree->getNodes()[0].mBV.maximum.z); + PX_ASSERT(mBounds[i].minimum.x == mMergedTrees[i].mTree->getNodes()[0].mBV.minimum.x); + PX_ASSERT(mBounds[i].minimum.y == mMergedTrees[i].mTree->getNodes()[0].mBV.minimum.y); + PX_ASSERT(mBounds[i].minimum.z == mMergedTrees[i].mTree->getNodes()[0].mBV.minimum.z); + + // check each tree + const AABBTree& mergedTree = *mMergedTrees[i].mTree; + for (PxU32 j = 0; j < mergedTree.getNbNodes(); j++) + { + const AABBTreeRuntimeNode& node = mergedTree.getNodes()[j]; + if (node.isLeaf()) + { + const PxU32 nbPrims = node.getNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= NB_OBJECTS_PER_NODE); + + const PxU32* primitives = node.getPrimitives(mergedTree.getIndices()); + for (PxU32 k = 0; k < nbPrims; k++) + { + const PxU32 index = primitives[k]; + // check if index is correct + PX_ASSERT(index < mPruningPool->getNbActiveObjects()); + // mark the index in the test bitmap, must be once set only, all merged trees must be in the main tree + PX_ASSERT(mergeTreeTestBitmap.test(index) == IntFalse); + mergeTreeTestBitmap.set(index); + + const PrunerPayload& payload = mPruningPool->getObjects()[index]; + const ExtendedBucketPrunerMap::Entry* extendedPrunerSwapEntry = mExtendedBucketPrunerMap.find(payload); + PX_ASSERT(extendedPrunerSwapEntry); + + const ExtendedBucketPrunerData& data = extendedPrunerSwapEntry->second; + PX_ASSERT(data.mMergeIndex == i); + PX_ASSERT(data.mSubTreeNode == j); + } + } + } + } + for (PxU32 i = mCurrentTreeIndex; i < mCurrentTreeCapacity; i++) + { + PX_ASSERT(mMergedTrees[i].mTree->getIndices() == NULL); + PX_ASSERT(mMergedTrees[i].mTree->getNodes() == NULL); + } + for (ExtendedBucketPrunerMap::Iterator iter = mExtendedBucketPrunerMap.getIterator(); !iter.done(); ++iter) + { + const ExtendedBucketPrunerData& data = iter->second; + PX_ASSERT(mMainTreeUpdateMap[data.mMergeIndex] < mMainTree->getNbNodes()); + PX_ASSERT(data.mMergeIndex < mCurrentTreeIndex); + PX_ASSERT(data.mSubTreeNode < mMergedTrees[data.mMergeIndex].mTree->getNbNodes()); + } + return true; +} +#endif + diff --git a/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h new file mode 100644 index 000000000..8ebea6d66 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h @@ -0,0 +1,198 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_EXTENDEDBUCKETPRUNER_H +#define SQ_EXTENDEDBUCKETPRUNER_H + +#include "SqTypedef.h" +#include "SqBucketPruner.h" +#include "SqIncrementalAABBPrunerCore.h" +#include "SqAABBTreeUpdateMap.h" +#include "PsHashMap.h" + +#define USE_INCREMENTAL_PRUNER 1 + +namespace physx +{ +namespace Sq +{ + struct AABBPrunerMergeData; + class AABBTreeMergeData; + +#if USE_INCREMENTAL_PRUNER + typedef IncrementalAABBPrunerCore PrunerCore; +#else + typedef BucketPrunerCore PrunerCore; +#endif + + // Extended bucket pruner data, if an object belongs to the tree of trees, we need to + // remember node for the sub tree, the tree it belongs to and the main tree node + struct ExtendedBucketPrunerData + { + PxU32 mTimeStamp; // timestamp + TreeNodeIndex mSubTreeNode; // sub tree node index + PxU32 mMergeIndex; // index in bounds and merged trees array + }; + + // Merged tree structure, holds tree and its timeStamp, released when no objects is in the tree + // or timeStamped objects are released + struct MergedTree + { + AABBTree* mTree; // AABB tree + size_t mTimeStamp; // needs to be size_t to match PrunerPayload size + }; + // needs to be size_t to match PrunerPayload size, pointer used for AABB tree query callbacks + PX_COMPILE_TIME_ASSERT(sizeof(MergedTree) == sizeof(PrunerPayload)); + + // hashing function for PrunerPaylod key + struct ExtendedBucketPrunerHash + { + PX_FORCE_INLINE uint32_t operator()(const PrunerPayload& payload) const + { +#if PX_P64_FAMILY + // const PxU32 h0 = Ps::hash((const void*)payload.data[0]); + // const PxU32 h1 = Ps::hash((const void*)payload.data[1]); + const PxU32 h0 = PxU32(PX_MAX_U32 & payload.data[0]); + const PxU32 h1 = PxU32(PX_MAX_U32 & payload.data[1]); + return Ps::hash(PxU64(h0) | (PxU64(h1) << 32)); +#else + return Ps::hash(PxU64(payload.data[0]) | (PxU64(payload.data[1]) << 32)); +#endif + } + PX_FORCE_INLINE bool equal(const PrunerPayload& k0, const PrunerPayload& k1) const + { + return (k0.data[0] == k1.data[0]) && (k0.data[1] == k1.data[1]); + } + }; + + // A.B. replace, this is useless, need to be able to traverse the map and release while traversing, also eraseAt failed + typedef Ps::HashMap ExtendedBucketPrunerMap; + + // Extended bucket pruner holds single objects in a bucket pruner and AABBtrees in a tree of trees. + // Base usage of ExtendedBucketPruner is for dynamic AABBPruner new objects, that did not make it + // into new tree. Single objects go directly into a bucket pruner, while merged AABBtrees + // go into a tree of trees. + class ExtendedBucketPruner + { + public: + ExtendedBucketPruner(const PruningPool* pool); + virtual ~ExtendedBucketPruner(); + + // release + void release(); + + // add single object into a bucket pruner directly + PX_FORCE_INLINE bool addObject(const PrunerPayload& object, const PxBounds3& worldAABB, PxU32 timeStamp, const PoolIndex poolIndex) + { +#if USE_INCREMENTAL_PRUNER + PX_UNUSED(worldAABB); + PX_UNUSED(object); + return mPrunerCore.addObject(poolIndex, timeStamp); +#else + PX_UNUSED(poolIndex); + return mPrunerCore.addObject(object, worldAABB, timeStamp); +#endif + } + + // add AABB tree from pruning structure - adds new primitive into main AABB tree + void addTree(const AABBTreeMergeData& mergeData, PxU32 timeStamp); + + // update object + bool updateObject(const PxBounds3& worldAABB, const PrunerPayload& object, const PoolIndex poolIndex); + + // remove object, removed object is replaced in pruning pool by swapped object, indices needs to be updated + bool removeObject(const PrunerPayload& object, PxU32 objectIndex, const PrunerPayload& swapObject, + PxU32 swapObjectIndex, PxU32& timeStamp); + + + // swap object index, the object index can be in core pruner or tree of trees + void swapIndex(PxU32 objectIndex, const PrunerPayload& swapObject, PxU32 swapObjectIndex, bool corePrunerIncluded = true); + + // refit marked nodes in tree of trees + void refitMarkedNodes(const PxBounds3* boxes); + +#if USE_INCREMENTAL_PRUNER + // notify timestampChange - swap trees in incremental pruner + void timeStampChange() { mPrunerCore.timeStampChange(); } +#endif + + + // look for objects marked with input timestamp everywhere in the structure, and remove them. This is the same + // as calling 'removeObject' individually for all these objects, but much more efficient. Returns number of removed objects. + PxU32 removeMarkedObjects(PxU32 timeStamp); + + // queries against the pruner + PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + + // origin shift + void shiftOrigin(const PxVec3& shift); + + // debug visualize + void visualize(Cm::RenderOutput& out, PxU32 color) const; + + PX_FORCE_INLINE void build() { mPrunerCore.build(); } + + PX_FORCE_INLINE PxU32 getNbObjects() const { return mPrunerCore.getNbObjects() + mExtendedBucketPrunerMap.size(); } + + private: + + // separate call for indices invalidation, object can be either in AABBPruner or Bucket pruner, but the swapped object can be + // in the tree of trees + void invalidateObject(const ExtendedBucketPrunerData& object, PxU32 objectIndex, const PrunerPayload& swapObject, + PxU32 swapObjectIndex); + + void resize(PxU32 size); + void buildMainAABBTree(); + void cleanTrees(); + +#if PX_DEBUG + // Extended bucket pruner validity check + bool checkValidity(); +#endif + private: + PrunerCore mPrunerCore; // pruner for single objects + const PruningPool* mPruningPool; // Pruning pool from AABB pruner + ExtendedBucketPrunerMap mExtendedBucketPrunerMap; // Map holding objects from tree merge - objects in tree of trees + AABBTree* mMainTree; // Main tree holding merged trees + AABBTreeUpdateMap mMainTreeUpdateMap; // Main tree updated map - merged trees index to nodes + AABBTreeUpdateMap mMergeTreeUpdateMap; // Merged tree update map used while tree is merged + PxBounds3* mBounds; // Merged trees bounds used for main tree building + MergedTree* mMergedTrees; // Merged trees + PxU32 mCurrentTreeIndex; // Current trees index + PxU32 mCurrentTreeCapacity; // Current tress capacity + bool mTreesDirty; // Dirty marker + }; + +} // namespace Sq + +} + +#endif // SQ_EXTENDEDBUCKETPRUNER_H diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp new file mode 100644 index 000000000..1a90b2b9a --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp @@ -0,0 +1,473 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "SqIncrementalAABBPruner.h" +#include "SqIncrementalAABBTree.h" +#include "SqAABBTree.h" +#include "GuAABBTreeQuery.h" +#include "GuSphere.h" +#include "GuBox.h" +#include "GuCapsule.h" +#include "GuBounds.h" +#include "PsBitUtils.h" + +using namespace physx; +using namespace Gu; +using namespace Sq; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: currently limited to 15 max +#define NB_OBJECTS_PER_NODE 4 + +#define PARANOIA_CHECKS 0 + +IncrementalAABBPruner::IncrementalAABBPruner(PxU32 sceneLimit, PxU64 contextID) : + mAABBTree (NULL), + mContextID (contextID) +{ + PX_UNUSED(mContextID); + mMapping.resizeUninitialized(sceneLimit); + mPool.preallocate(sceneLimit); + + mChangedLeaves.reserve(32); +} + +IncrementalAABBPruner::~IncrementalAABBPruner() +{ + release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Add, Remove, Update methods + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool IncrementalAABBPruner::addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* payload, PxU32 count, bool ) +{ + PX_PROFILE_ZONE("SceneQuery.prunerAddObjects", mContextID); + + if(!count) + return true; + + const PxU32 valid = mPool.addObjects(results, bounds, payload, count); + + if(mAABBTree) + { + for(PxU32 i=0;iinsert(poolIndex, mPool.getCurrentWorldBoxes(), mChangedLeaves); + updateMapping(poolIndex, node); + } + + #if PARANOIA_CHECKS + test(); + #endif + } + + return valid==count; +} + +void IncrementalAABBPruner::updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node) +{ + // resize mapping if needed + if(mMapping.size() <= poolIndex) + { + mMapping.resize(mMapping.size() * 2); + } + + // if a node was split we need to update the node indices and also the sibling indices + if(!mChangedLeaves.empty()) + { + if(node && node->isLeaf()) + { + for(PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + mMapping[node->getPrimitives(NULL)[j]] = node; + } + } + + for(PxU32 i = 0; i < mChangedLeaves.size(); i++) + { + IncrementalAABBTreeNode* changedNode = mChangedLeaves[i]; + PX_ASSERT(changedNode->isLeaf()); + + for(PxU32 j = 0; j < changedNode->getNbPrimitives(); j++) + { + mMapping[changedNode->getPrimitives(NULL)[j]] = changedNode; + } + } + } + else + { + mMapping[poolIndex] = node; + } +} + +void IncrementalAABBPruner::updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count) +{ + PX_PROFILE_ZONE("SceneQuery.prunerUpdateObjects", mContextID); + + if(!count || !mAABBTree) + return; + + const PxBounds3* newBounds = mPool.getCurrentWorldBoxes(); + for(PxU32 i=0; iupdate(mMapping[poolIndex], poolIndex, newBounds, mChangedLeaves); + // we removed node during update, need to update the mapping + updateMapping(poolIndex, node); + } + +#if PARANOIA_CHECKS + test(); +#endif + +} + +void IncrementalAABBPruner::updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count) +{ + PX_PROFILE_ZONE("SceneQuery.prunerUpdateObjects", mContextID); + + if(!count) + return; + + mPool.updateObjectsAndInflateBounds(handles, indices, newBounds, count); + + if(!mAABBTree) + return; + + const PxBounds3* poolBounds = mPool.getCurrentWorldBoxes(); + for(PxU32 i=0; iupdate(mMapping[poolIndex], poolIndex, poolBounds, mChangedLeaves); + // we removed node during update, need to update the mapping + updateMapping(poolIndex, node); + } + +#if PARANOIA_CHECKS + test(); +#endif + +} + +void IncrementalAABBPruner::removeObjects(const PrunerHandle* handles, PxU32 count) +{ + PX_PROFILE_ZONE("SceneQuery.prunerRemoveObjects", mContextID); + + if(!count) + return; + + for(PxU32 i=0; iremove(mMapping[poolIndex], poolIndex, mPool.getCurrentWorldBoxes()); + // if node moved to its parent + if (node && node->isLeaf()) + { + for (PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + mMapping[index] = node; + } + } + mMapping[poolIndex] = mMapping[poolRelocatedLastIndex]; + // fix indices if we made a swap + if(poolRelocatedLastIndex != poolIndex) + mAABBTree->fixupTreeIndices(mMapping[poolIndex], poolRelocatedLastIndex, poolIndex); + + if(!mAABBTree->getNodes()) + { + release(); + } + } + } + +#if PARANOIA_CHECKS + test(); +#endif + +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Query Implementation + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxAgain IncrementalAABBPruner::overlap(const ShapeData& queryVolume, PrunerCallback& pcb) const +{ + PxAgain again = true; + + if(mAABBTree && mAABBTree->getNodes()) + { + switch(queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if(queryVolume.isOBB()) + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + else + { + const Gu::AABBAABBTest test(queryVolume.getPrunerInflatedWorldAABB()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + } + break; + case PxGeometryType::eCAPSULE: + { + const Gu::Capsule& capsule = queryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest test( capsule.p1, queryVolume.getPrunerWorldRot33().column0, + queryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::eSPHERE: + { + const Gu::Sphere& sphere = queryVolume.getGuSphere(); + Gu::SphereAABBTest test(sphere.center, sphere.radius); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::eCONVEXMESH: + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + } + + return again; +} + +PxAgain IncrementalAABBPruner::sweep(const ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PxAgain again = true; + + if(mAABBTree && mAABBTree->getNodes()) + { + const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB(); + const PxVec3 extents = aabb.getExtents(); + again = AABBTreeRaycast()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, aabb.getCenter(), unitDir, inOutDistance, extents, pcb); + } + + return again; +} + +PxAgain IncrementalAABBPruner::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PxAgain again = true; + + if(mAABBTree && mAABBTree->getNodes()) + again = AABBTreeRaycast()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, origin, unitDir, inOutDistance, PxVec3(0.0f), pcb); + + return again; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Other methods of Pruner Interface + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// This isn't part of the pruner virtual interface, but it is part of the public interface +// of AABBPruner - it gets called by SqManager to force a rebuild, and requires a commit() before +// queries can take place + +void IncrementalAABBPruner::purge() +{ + release(); +} + +void IncrementalAABBPruner::setRebuildRateHint(PxU32 ) +{ +} + +bool IncrementalAABBPruner::buildStep(bool ) +{ + return true; +} + +bool IncrementalAABBPruner::prepareBuild() +{ + return false; +} + +// Commit either performs a refit if background rebuild is not yet finished +// or swaps the current tree for the second tree rebuilt in the background +void IncrementalAABBPruner::commit() +{ + PX_PROFILE_ZONE("SceneQuery.prunerCommit", mContextID); + + if (!mAABBTree) + { + fullRebuildAABBTree(); + return; + } +} + +void IncrementalAABBPruner::fullRebuildAABBTree() +{ + // Don't bother building an AABB-tree if there isn't a single static object + const PxU32 nbObjects = mPool.getNbActiveObjects(); + if (!nbObjects) + return; + + const PxU32 indicesSize = Ps::nextPowerOfTwo(nbObjects); + if(indicesSize > mMapping.size()) + { + mMapping.resizeUninitialized(indicesSize); + } + + // copy the temp optimized tree into the new incremental tree + mAABBTree = PX_NEW(IncrementalAABBTree)(); + + AABBTreeBuildParams TB; + TB.mNbPrimitives = nbObjects; + TB.mAABBArray = mPool.getCurrentWorldBoxes(); + TB.mLimit = NB_OBJECTS_PER_NODE; + mAABBTree->build(TB, mMapping); + +#if PARANOIA_CHECKS + test(); +#endif +} + +void IncrementalAABBPruner::shiftOrigin(const PxVec3& shift) +{ + mPool.shiftOrigin(shift); + + if(mAABBTree) + mAABBTree->shiftOrigin(shift); +} + +#include "CmRenderOutput.h" +void IncrementalAABBPruner::visualize(Cm::RenderOutput& out, PxU32 color) const +{ + // getAABBTree() asserts when pruner is dirty. NpScene::visualization() does not enforce flushUpdate. see DE7834 + const IncrementalAABBTree* tree = mAABBTree; + + if(tree && tree->getNodes()) + { + struct Local + { + static void _Draw(const IncrementalAABBTreeNode* root, const IncrementalAABBTreeNode* node, Cm::RenderOutput& out_) + { + PxBounds3 bounds; + V4StoreU(node->mBVMin, &bounds.minimum.x); + V4StoreU(node->mBVMax, &bounds.maximum.x); + out_ << Cm::DebugBox(bounds, true); + if (node->isLeaf()) + return; + _Draw(root, node->getPos(root), out_); + _Draw(root, node->getNeg(root), out_); + } + }; + out << PxTransform(PxIdentity); + out << color; + Local::_Draw(tree->getNodes(), tree->getNodes(), out); + } + + // Render added objects not yet in the tree + out << PxTransform(PxIdentity); + out << PxU32(PxDebugColor::eARGB_WHITE); +} + +void IncrementalAABBPruner::release() // this can be called from purge() +{ + if (mAABBTree) + { + PX_DELETE(mAABBTree); + mAABBTree = NULL; + } +} + +void IncrementalAABBPruner::test() +{ + if(mAABBTree) + { + mAABBTree->hierarchyCheck(mPool.getNbActiveObjects(), mPool.getCurrentWorldBoxes()); + for(PxU32 i = 0; i < mPool.getNbActiveObjects(); i++) + { + mAABBTree->checkTreeLeaf(mMapping[i], i); + } + } +} + +void IncrementalAABBPruner::merge(const void* ) +{ + //const AABBPrunerMergeData& pruningStructure = *reinterpret_cast (mergeParams); + + //if(mAABBTree) + //{ + // // index in pruning pool, where new objects were added + // const PxU32 pruningPoolIndex = mPool.getNbActiveObjects() - pruningStructure.mNbObjects; + + // // create tree from given nodes and indices + // AABBTreeMergeData aabbTreeMergeParams(pruningStructure.mNbNodes, pruningStructure.mAABBTreeNodes, + // pruningStructure.mNbObjects, pruningStructure.mAABBTreeIndices, pruningPoolIndex); + + // if (!mIncrementalRebuild) + // { + // // merge tree directly + // mAABBTree->mergeTree(aabbTreeMergeParams); + // } + // else + // { + // mBucketPruner.addTree(aabbTreeMergeParams, mTimeStamp); + // } + //} +} diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h new file mode 100644 index 000000000..bb29d63d0 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_INCREMENTAL_AABB_PRUNER_H +#define SQ_INCREMENTAL_AABB_PRUNER_H + +#include "SqPruner.h" +#include "SqPruningPool.h" +#include "SqIncrementalAABBTree.h" +#include "SqAABBTreeUpdateMap.h" + +namespace physx +{ + +namespace Sq +{ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + class IncrementalAABBPruner : public IncrementalPruner + { + public: + IncrementalAABBPruner(PxU32 sceneLimit, PxU64 contextID); + virtual ~IncrementalAABBPruner(); + + // Pruner + virtual bool addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* userData, PxU32 count, bool hasPruningStructure); + virtual void removeObjects(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count); + virtual void commit(); + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual const PrunerPayload& getPayload(PrunerHandle handle) const { return mPool.getPayload(handle); } + virtual const PrunerPayload& getPayload(PrunerHandle handle, PxBounds3*& bounds) const { return mPool.getPayload(handle, bounds); } + virtual void preallocate(PxU32 entries) { mPool.preallocate(entries); } + virtual void shiftOrigin(const PxVec3& shift); + virtual void visualize(Cm::RenderOutput& out, PxU32 color) const; + virtual void merge(const void* mergeParams); + //~Pruner + + // IncrementalPruner + virtual void purge(); // gets rid of internal accel struct + virtual void setRebuildRateHint(PxU32 nbStepsForRebuild); // Besides the actual rebuild steps, 3 additional steps are needed. + virtual bool buildStep(bool ); // returns true if finished + virtual bool prepareBuild(); + //~IncrementalPruner + + // direct access for test code + PX_FORCE_INLINE const IncrementalAABBTree* getAABBTree() const { return mAABBTree; } + + // local functions + private: + void release(); + void fullRebuildAABBTree(); + void test(); + void updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node); + + private: + IncrementalAABBTree* mAABBTree; + + PruningPool mPool; // Pool of AABBs + + Ps::Array mMapping; + + PxU64 mContextID; + NodeList mChangedLeaves; + }; + +} // namespace Sq + +} // namespace physx +#endif diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp new file mode 100644 index 000000000..cf9e17420 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp @@ -0,0 +1,435 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "SqIncrementalAABBPrunerCore.h" +#include "SqIncrementalAABBTree.h" +#include "SqPruningPool.h" +#include "SqAABBTree.h" +#include "GuAABBTreeQuery.h" +#include "GuSphere.h" +#include "GuBox.h" +#include "GuCapsule.h" +#include "GuBounds.h" + +using namespace physx; +using namespace Gu; +using namespace Sq; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define PARANOIA_CHECKS 0 + +IncrementalAABBPrunerCore::IncrementalAABBPrunerCore(const PruningPool* pool) : + mCurrentTree (1), + mLastTree (0), + mPool (pool) +{ + mAABBTree[0].mapping.reserve(256); + mAABBTree[1].mapping.reserve(256); + mChangedLeaves.reserve(32); +} + +IncrementalAABBPrunerCore::~IncrementalAABBPrunerCore() +{ + release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void IncrementalAABBPrunerCore::release() // this can be called from purge() +{ + for(PxU32 i = 0; i < NUM_TREES; i++) + { + if(mAABBTree[i].tree) + { + PX_DELETE(mAABBTree[i].tree); + mAABBTree[i].tree = NULL; + } + mAABBTree[i].mapping.clear(); + mAABBTree[i].timeStamp = 0; + } + mCurrentTree = 1; + mLastTree = 0; +} + +bool IncrementalAABBPrunerCore::addObject(const PoolIndex poolIndex, PxU32 timeStamp) +{ + CoreTree& tree = mAABBTree[mCurrentTree]; + if(!tree.tree || !tree.tree->getNodes()) + { + if(!tree.tree) + tree.tree = PX_NEW(IncrementalAABBTree)(); + tree.timeStamp = timeStamp; + } + PX_ASSERT(tree.timeStamp == timeStamp); + + mChangedLeaves.clear(); + IncrementalAABBTreeNode* node = tree.tree->insert(poolIndex, mPool->getCurrentWorldBoxes(), mChangedLeaves); + updateMapping(tree.mapping, poolIndex, node); + +#if PARANOIA_CHECKS + test(); +#endif + + return true; +} + +void IncrementalAABBPrunerCore::updateMapping(IncrementalPrunerMap& mapping, const PoolIndex poolIndex, IncrementalAABBTreeNode* node) +{ + // if some node leaves changed, we need to update mapping + if(!mChangedLeaves.empty()) + { + if(node && node->isLeaf()) + { + for(PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + mapping[index] = node; + } + } + + for(PxU32 i = 0; i < mChangedLeaves.size(); i++) + { + IncrementalAABBTreeNode* changedNode = mChangedLeaves[i]; + PX_ASSERT(changedNode->isLeaf()); + + for(PxU32 j = 0; j < changedNode->getNbPrimitives(); j++) + { + const PoolIndex index = changedNode->getPrimitives(NULL)[j]; + mapping[index] = changedNode; + } + } + } + else + { + PX_ASSERT(node->isLeaf()); + mapping[poolIndex] = node; + } +} + +bool IncrementalAABBPrunerCore::removeObject(const PoolIndex poolIndex, const PoolIndex poolRelocatedLastIndex, PxU32& timeStamp) +{ + // erase the entry and get the data + IncrementalPrunerMap::Entry entry; + bool foundEntry = true; + const PxU32 treeIndex = mAABBTree[mLastTree].mapping.erase(poolIndex, entry) ? mLastTree : mCurrentTree; + // if it was not found in the last tree look at the current tree + if(treeIndex == mCurrentTree) + foundEntry = mAABBTree[mCurrentTree].mapping.erase(poolIndex, entry); + + // exit somethings is wrong here, entry was not found here + PX_ASSERT(foundEntry); + if(!foundEntry) + return false; + + // tree must exist + PX_ASSERT(mAABBTree[treeIndex].tree); + CoreTree& tree = mAABBTree[treeIndex]; + timeStamp = tree.timeStamp; + + // remove the poolIndex from the tree, update the tree bounds immediatelly + IncrementalAABBTreeNode* node = tree.tree->remove(entry.second, poolIndex, mPool->getCurrentWorldBoxes()); + if(node && node->isLeaf()) + { + for(PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + tree.mapping[index] = node; + } + } + + // nothing to swap, last object, early exit + if(poolIndex == poolRelocatedLastIndex) + { +#if PARANOIA_CHECKS + test(); +#endif + return true; + } + + // fix the indices, we need to swap the index with last index + // erase the relocated index from the tree it is + IncrementalPrunerMap::Entry relocatedEntry; + const PxU32 treeRelocatedIndex = mAABBTree[mCurrentTree].mapping.erase(poolRelocatedLastIndex, relocatedEntry) ? mCurrentTree : mLastTree; + foundEntry = true; + if(treeRelocatedIndex == mLastTree) + foundEntry = mAABBTree[mLastTree].mapping.erase(poolRelocatedLastIndex, relocatedEntry); + + if(foundEntry) + { + CoreTree& relocatedTree = mAABBTree[treeRelocatedIndex]; + + // set the new mapping + relocatedTree.mapping[poolIndex] = relocatedEntry.second; + // update the tree indices - swap + relocatedTree.tree->fixupTreeIndices(relocatedEntry.second, poolRelocatedLastIndex, poolIndex); + } + +#if PARANOIA_CHECKS + test(); +#endif + return true; +} + +void IncrementalAABBPrunerCore::swapIndex(const PoolIndex poolIndex, const PoolIndex poolRelocatedLastIndex) +{ + // fix the indices, we need to swap the index with last index + // erase the relocated index from the tre it is + IncrementalPrunerMap::Entry relocatedEntry; + const PxU32 treeRelocatedIndex = mAABBTree[mCurrentTree].mapping.erase(poolRelocatedLastIndex, relocatedEntry) ? mCurrentTree : mLastTree; + bool foundEntry = true; + if(treeRelocatedIndex == mLastTree) + foundEntry = mAABBTree[mLastTree].mapping.erase(poolRelocatedLastIndex, relocatedEntry); + + // relocated index is not here + if(!foundEntry) + return; + + CoreTree& relocatedTree = mAABBTree[treeRelocatedIndex]; + + // set the new mapping + relocatedTree.mapping[poolIndex] = relocatedEntry.second; + // update the tree indices - swap + relocatedTree.tree->fixupTreeIndices(relocatedEntry.second, poolRelocatedLastIndex, poolIndex); +} + +bool IncrementalAABBPrunerCore::updateObject(const PoolIndex poolIndex) +{ + const IncrementalPrunerMap::Entry* entry = mAABBTree[mLastTree].mapping.find(poolIndex); + const PxU32 treeIndex = entry ? mLastTree : mCurrentTree; + if(!entry) + entry = mAABBTree[mCurrentTree].mapping.find(poolIndex); + + // we have not found it + PX_ASSERT(entry); + if(!entry) + return false; + + CoreTree& tree = mAABBTree[treeIndex]; + mChangedLeaves.clear(); + IncrementalAABBTreeNode* node = tree.tree->updateFast(entry->second, poolIndex, mPool->getCurrentWorldBoxes(), mChangedLeaves); + if(!mChangedLeaves.empty() || node != entry->second) + updateMapping(tree.mapping, poolIndex, node); + +#if PARANOIA_CHECKS + test(false); +#endif + + return true; +} + +PxU32 IncrementalAABBPrunerCore::removeMarkedObjects(PxU32 timeStamp) +{ + // early exit is no tree exists + if(!mAABBTree[mLastTree].tree || !mAABBTree[mLastTree].tree->getNodes()) + { + PX_ASSERT(mAABBTree[mLastTree].mapping.size() == 0); + PX_ASSERT(!mAABBTree[mCurrentTree].tree || mAABBTree[mCurrentTree].timeStamp != timeStamp); + return 0; + } + + PX_UNUSED(timeStamp); + PX_ASSERT(timeStamp == mAABBTree[mLastTree].timeStamp); + + // release the last tree + CoreTree& tree = mAABBTree[mLastTree]; + PxU32 nbObjects = tree.mapping.size(); + tree.mapping.clear(); + tree.timeStamp = 0; + + tree.tree->release(); + + return nbObjects; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Query Implementation + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxAgain IncrementalAABBPrunerCore::overlap(const ShapeData& queryVolume, PrunerCallback& pcb) const +{ + PxAgain again = true; + + for(PxU32 i = 0; i < NUM_TREES; i++) + { + const CoreTree& tree = mAABBTree[i]; + if(tree.tree && tree.tree->getNodes() && again) + { + switch(queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if(queryVolume.isOBB()) + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, test, pcb); + } + else + { + const Gu::AABBAABBTest test(queryVolume.getPrunerInflatedWorldAABB()); + again = AABBTreeOverlap()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, test, pcb); + } + } + break; + case PxGeometryType::eCAPSULE: + { + const Gu::Capsule& capsule = queryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest test( capsule.p1, queryVolume.getPrunerWorldRot33().column0, + queryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + again = AABBTreeOverlap()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, test, pcb); + } + break; + case PxGeometryType::eSPHERE: + { + const Gu::Sphere& sphere = queryVolume.getGuSphere(); + Gu::SphereAABBTest test(sphere.center, sphere.radius); + again = AABBTreeOverlap()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, test, pcb); + } + break; + case PxGeometryType::eCONVEXMESH: + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, test, pcb); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + } + } + + return again; +} + +PxAgain IncrementalAABBPrunerCore::sweep(const ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PxAgain again = true; + + for(PxU32 i = 0; i < NUM_TREES; i++) + { + const CoreTree& tree = mAABBTree[i]; + if(tree.tree && tree.tree->getNodes() && again) + { + const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB(); + const PxVec3 extents = aabb.getExtents(); + again = AABBTreeRaycast()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, aabb.getCenter(), unitDir, inOutDistance, extents, pcb); + } + } + + return again; +} + +PxAgain IncrementalAABBPrunerCore::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PxAgain again = true; + + for(PxU32 i = 0; i < NUM_TREES; i++) + { + const CoreTree& tree = mAABBTree[i]; + if(tree.tree && tree.tree->getNodes() && again) + { + again = AABBTreeRaycast()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, origin, unitDir, inOutDistance, PxVec3(0.0f), pcb); + } + } + return again; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void IncrementalAABBPrunerCore::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i = 0; i < NUM_TREES; i++) + { + if(mAABBTree[i].tree) + { + mAABBTree[i].tree->shiftOrigin(shift); + } + } +} + + +#include "CmRenderOutput.h" +void IncrementalAABBPrunerCore::visualize(Cm::RenderOutput& out, PxU32 color) const +{ + for(PxU32 i = 0; i < NUM_TREES; i++) + { + if(mAABBTree[i].tree && mAABBTree[i].tree->getNodes()) + { + struct Local + { + static void _Draw(const IncrementalAABBTreeNode* root, const IncrementalAABBTreeNode* node, Cm::RenderOutput& out_) + { + PxBounds3 bounds; + V4StoreU(node->mBVMin, &bounds.minimum.x); + V4StoreU(node->mBVMax, &bounds.maximum.x); + out_ << Cm::DebugBox(bounds, true); + if (node->isLeaf()) + return; + _Draw(root, node->getPos(root), out_); + _Draw(root, node->getNeg(root), out_); + } + }; + out << PxTransform(PxIdentity); + out << color; + Local::_Draw(mAABBTree[i].tree->getNodes(), mAABBTree[i].tree->getNodes(), out); + + + // Render added objects not yet in the tree + out << PxTransform(PxIdentity); + out << PxU32(PxDebugColor::eARGB_WHITE); + } + } +} + +void IncrementalAABBPrunerCore::test(bool chierarcyCheck) +{ + PxU32 maxDepth[NUM_TREES] = { 0, 0 }; + for(PxU32 i = 0; i < NUM_TREES; i++) + { + if(mAABBTree[i].tree) + { + if(chierarcyCheck) + mAABBTree[i].tree->hierarchyCheck(mPool->getCurrentWorldBoxes()); + for (IncrementalPrunerMap::Iterator iter = mAABBTree[i].mapping.getIterator(); !iter.done(); ++iter) + { + mAABBTree[i].tree->checkTreeLeaf(iter->second, iter->first); + PxU32 depth = mAABBTree[i].tree->getTreeLeafDepth(iter->second); + if(depth > maxDepth[i]) + maxDepth[i] = depth; + } + } + } +} diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h new file mode 100644 index 000000000..4308a5e02 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h @@ -0,0 +1,115 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_INCREMENTAL_AABB_PRUNER_CORE_H +#define SQ_INCREMENTAL_AABB_PRUNER_CORE_H + +#include "SqPruner.h" +#include "SqPruningPool.h" +#include "SqIncrementalAABBTree.h" +#include "SqAABBTreeUpdateMap.h" +#include "PsHashMap.h" + +namespace physx +{ + +namespace Sq +{ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef Ps::HashMap IncrementalPrunerMap; + + struct CoreTree + { + CoreTree(): + timeStamp(0), + tree(NULL) + { + } + + PxU32 timeStamp; + IncrementalAABBTree* tree; + IncrementalPrunerMap mapping; + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + class IncrementalAABBPrunerCore : public Ps::UserAllocated + { + public: + IncrementalAABBPrunerCore(const PruningPool* pool); + ~IncrementalAABBPrunerCore(); + + void release(); + + bool addObject(const PoolIndex poolIndex, PxU32 timeStamp); + bool removeObject(const PoolIndex poolIndex, const PoolIndex poolRelocatedLastIndex, PxU32& timeStamp); + + // if we swap object from bucket pruner index with an index in the regular AABB pruner + void swapIndex(const PoolIndex poolIndex, const PoolIndex poolRelocatedLastIndex); + + bool updateObject(const PoolIndex poolIndex); + + PxU32 removeMarkedObjects(PxU32 timeStamp); + + PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + + void shiftOrigin(const PxVec3& shift); + + void visualize(Cm::RenderOutput& out, PxU32 color) const; + + PX_FORCE_INLINE void timeStampChange() + { + // swap current and last tree + mLastTree = (mLastTree + 1) % 2; + mCurrentTree = (mCurrentTree + 1) % 2; + } + + void build() {} + + PX_FORCE_INLINE PxU32 getNbObjects() const { return mAABBTree[0].mapping.size() + mAABBTree[1].mapping.size(); } + + private: + void updateMapping(IncrementalPrunerMap& mapping, const PoolIndex poolIndex, IncrementalAABBTreeNode* node); + void test(bool chierarcyCheck = true); + + private: + static const PxU32 NUM_TREES = 2; + + PxU32 mCurrentTree; + PxU32 mLastTree; + CoreTree mAABBTree[NUM_TREES]; + const PruningPool* mPool; // Pruning pool from AABB pruner + NodeList mChangedLeaves; + }; + +}} + +#endif + diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp new file mode 100644 index 000000000..a1d1fd43b --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp @@ -0,0 +1,1077 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "SqIncrementalAABBTree.h" +#include "SqAABBTree.h" +#include "SqAABBTreeUpdateMap.h" +#include "SqBounds.h" +#include "GuBVHStructure.h" +#include "PsVecMath.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Sq; +using namespace Gu; +using namespace shdfnd::aos; + +#define SUPPORT_TREE_ROTATION 1 +#define DEALLOCATE_RESET 0 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +IncrementalAABBTree::IncrementalAABBTree(): + mIndicesPool("AABBTreeIndicesPool", 256), + mNodesPool("AABBTreeNodesPool", 256 ), + mRoot(NULL) +{ + +} + +IncrementalAABBTree::~IncrementalAABBTree() +{ + release(); +} + +void IncrementalAABBTree::release() +{ + if(mRoot) + { + releaseNode(mRoot); + mRoot = NULL; + } +} + +void IncrementalAABBTree::releaseNode(IncrementalAABBTreeNode* node) +{ + PX_ASSERT(node); + if(node->isLeaf()) + { + mIndicesPool.deallocate(node->mIndices); + } + else + { + releaseNode(node->mChilds[0]); + releaseNode(node->mChilds[1]); + } + if(!node->mParent) + { + mNodesPool.deallocate(reinterpret_cast(node)); + return; + } + if(node->mParent->mChilds[1] == node) + { + mNodesPool.deallocate(reinterpret_cast(node->mParent->mChilds[0])); + } +} + + +// check if node is inside the given bounds +PX_FORCE_INLINE static bool nodeInsideBounds(const Vec4V& nodeMin, const Vec4V& nodeMax, const Vec4V& parentMin, const Vec4V& parentMax) +{ + return !(Ps::IntBool(V4AnyGrtr3(parentMin, nodeMin)) || Ps::IntBool(V4AnyGrtr3(nodeMax, parentMax))); +} + +// update the node parent hierarchy, when insert happen, we can early exit when the node is inside its parent +// no further update is needed +PX_FORCE_INLINE static void updateHierarchyAfterInsert(IncrementalAABBTreeNode* node) +{ + IncrementalAABBTreeNode* parent = node->mParent; + IncrementalAABBTreeNode* testNode = node; + while(parent) + { + // check if we can early exit + if(!nodeInsideBounds(testNode->mBVMin, testNode->mBVMax, parent->mBVMin, parent->mBVMax)) + { + parent->mBVMin = V4Min(parent->mChilds[0]->mBVMin, parent->mChilds[1]->mBVMin); + parent->mBVMax = V4Max(parent->mChilds[0]->mBVMax, parent->mChilds[1]->mBVMax); + } + else + break; + testNode = parent; + parent = parent->mParent; + } +} + +// add an index into the leaf indices list and update the node bounds +PX_FORCE_INLINE static void addPrimitiveIntoNode(IncrementalAABBTreeNode* node, const PoolIndex index, const Vec4V& minV, const Vec4V& maxV) +{ + PX_ASSERT(node->isLeaf()); + AABBTreeIndices& nodeIndices = *node->mIndices; + PX_ASSERT(nodeIndices.nbIndices < NB_OBJECTS_PER_NODE); + + // store the new handle + nodeIndices.indices[nodeIndices.nbIndices++] = index; + + // increase the node bounds + node->mBVMin = V4Min(node->mBVMin, minV); + node->mBVMax = V4Max(node->mBVMax, maxV); + + updateHierarchyAfterInsert(node); +} + +// check if node does intersect with given bounds +PX_FORCE_INLINE static bool nodeIntersection(IncrementalAABBTreeNode& node, const Vec4V& minV, const Vec4V& maxV) +{ + return !(Ps::IntBool(V4AnyGrtr3(node.mBVMin, maxV)) || Ps::IntBool(V4AnyGrtr3(minV, node.mBVMax))); +} + +// traversal strategy +PX_FORCE_INLINE static PxU32 traversalDirection(const IncrementalAABBTreeNode& child0, const IncrementalAABBTreeNode& child1, const Vec4V& testCenterV, + bool testRotation, bool& rotateNode, PxU32& largesRotateNode) +{ + // traverse in the direction of a node which is closer + // we compare the node and object centers + const Vec4V centerCh0V = V4Add(child0.mBVMax, child0.mBVMin); + const Vec4V centerCh1V = V4Add(child1.mBVMax, child1.mBVMin); + + const Vec4V ch0D = V4Sub(testCenterV, centerCh0V); + const Vec4V ch1D = V4Sub(testCenterV, centerCh1V); + + if(testRotation) + { + // if some volume is 3x larger than we do a rotation + const float volumeCompare = 3.0f; + + PX_ALIGN(16, PxVec4) sizeCh0; + PX_ALIGN(16, PxVec4) sizeCh1; + const Vec4V sizeCh0V = V4Sub(child0.mBVMax, child0.mBVMin); + const Vec4V sizeCh1V = V4Sub(child1.mBVMax, child1.mBVMin); + V4StoreA(sizeCh0V, &sizeCh0.x); + V4StoreA(sizeCh1V, &sizeCh1.x); + + const float volumeCh0 = sizeCh0.x*sizeCh0.y*sizeCh0.z; + const float volumeCh1 = sizeCh1.x*sizeCh1.y*sizeCh1.z; + + if((volumeCh0*volumeCompare < volumeCh1) || (volumeCh1*volumeCompare < volumeCh0)) + { + largesRotateNode = (volumeCh0 > volumeCh1) ? 0u : 1u; + rotateNode = true; + } + } + + const BoolV con = FIsGrtr(V4Dot3(ch0D, ch0D), V4Dot3(ch1D, ch1D)); + return (BAllEqTTTT(con) == 1) ? PxU32(1) : PxU32(0); +} + +// remove an index from the leaf +PX_FORCE_INLINE static void removePrimitiveFromNode(IncrementalAABBTreeNode* node, const PoolIndex index) +{ + AABBTreeIndices& indices = *node->mIndices; + PX_ASSERT(indices.nbIndices > 1); + + for (PxU32 i = indices.nbIndices; i--; ) + { + if(node->mIndices->indices[i] == index) + { + node->mIndices->indices[i] = node->mIndices->indices[--indices.nbIndices]; + return; + } + } + // if handle was not found something is wrong here + PX_ASSERT(0); +} + +// check if bounds are equal with given node min/max +PX_FORCE_INLINE static bool boundsEqual(const Vec4V& testMin, const Vec4V& testMax, const Vec4V& nodeMin, const Vec4V& nodeMax) +{ + return (Ps::IntBool(V4AllEq(nodeMin, testMin)) && Ps::IntBool(V4AllEq(testMax, nodeMax))); +} + +// update the node hierarchy bounds when remove happen, we can early exit if the bounds are equal and no bounds update +// did happen +PX_FORCE_INLINE static void updateHierarchyAfterRemove(IncrementalAABBTreeNode* node, const PxBounds3* bounds) +{ + if(node->isLeaf()) + { + const AABBTreeIndices& indices = *node->mIndices; + PX_ASSERT(indices.nbIndices > 0); + + Vec4V bvMin = V4LoadU(&bounds[indices.indices[0]].minimum.x); + Vec4V bvMax = V4LoadU(&bounds[indices.indices[0]].maximum.x); + for(PxU32 i = 1; i < indices.nbIndices; i++) + { + const Vec4V minV = V4LoadU(&bounds[indices.indices[i]].minimum.x); + const Vec4V maxV = V4LoadU(&bounds[indices.indices[i]].maximum.x); + + bvMin = V4Min(bvMin, minV); + bvMax = V4Max(bvMax, maxV); + } + + node->mBVMin = V4ClearW(bvMin); + node->mBVMax = V4ClearW(bvMax); + } + else + { + node->mBVMin = V4Min(node->mChilds[0]->mBVMin, node->mChilds[1]->mBVMin); + node->mBVMax = V4Max(node->mChilds[0]->mBVMax, node->mChilds[1]->mBVMax); + } + + IncrementalAABBTreeNode* parent = node->mParent; + while(parent) + { + const Vec4V newMinV = V4Min(parent->mChilds[0]->mBVMin, parent->mChilds[1]->mBVMin); + const Vec4V newMaxV = V4Max(parent->mChilds[0]->mBVMax, parent->mChilds[1]->mBVMax); + + const bool earlyExit = boundsEqual(newMinV, newMaxV, parent->mBVMin, parent->mBVMax); + if(earlyExit) + break; + + parent->mBVMin = newMinV; + parent->mBVMax = newMaxV; + + parent = parent->mParent; + } +} + +// split the leaf node along the most significant axis +IncrementalAABBTreeNode* IncrementalAABBTree::splitLeafNode(IncrementalAABBTreeNode* node, const PoolIndex index, const Vec4V& minV, const Vec4V& maxV, const PxBounds3* bounds) +{ + PX_ASSERT(node->isLeaf()); + + IncrementalAABBTreeNode* returnNode = NULL; + + // create new pairs of nodes, parent will remain the node (the one we split) + IncrementalAABBTreeNode* child0 = reinterpret_cast(mNodesPool.allocate()); + IncrementalAABBTreeNode* child1 = child0 + 1; + AABBTreeIndices* newIndices = mIndicesPool.allocate(); + + // get the split axis + PX_ALIGN(16, PxVec4) vars; + PX_ALIGN(16, PxVec4) center; + const float half = 0.5f; + const FloatV halfV = FLoad(half); + const Vec4V newMinV = V4Min(node->mBVMin, minV); + const Vec4V newMaxV = V4Max(node->mBVMax, maxV); + const Vec4V centerV = V4Scale(V4Add(newMaxV, newMinV), halfV); + const Vec4V varsV = V4Sub(newMaxV, newMinV); + V4StoreA(varsV, &vars.x); + V4StoreA(centerV, ¢er.x); + const PxU32 axis = Ps::largestAxis(PxVec3(vars.x, vars.y, vars.z)); + + // setup parent + child0->mParent = node; + child1->mParent = node; + child0->mIndices = node->mIndices; + child0->mChilds[1] = NULL; + child1->mIndices = newIndices; + child1->mChilds[1] = NULL; + + AABBTreeIndices& child0Indices = *child0->mIndices; // the original node indices + AABBTreeIndices& child1Indices = *child1->mIndices; // new empty indices + child1Indices.nbIndices = 0; + + // split the node + for(PxU32 i = child0Indices.nbIndices; i--;) + { + const PxBounds3& primitiveBounds = bounds[child0Indices.indices[i]]; + const float pCenter = primitiveBounds.getCenter(axis); + if(center[axis] >= pCenter) + { + // move to new node + child1Indices.indices[child1Indices.nbIndices++] = child0Indices.indices[i]; + child0Indices.nbIndices--; + child0Indices.indices[i] = child0Indices.indices[child0Indices.nbIndices]; + } + } + + // check where to put the new node, if there is still a free space + if(child0Indices.nbIndices == 0 || child1Indices.nbIndices == NB_OBJECTS_PER_NODE) + { + child0Indices.nbIndices = 1; + child0Indices.indices[0] = index; + returnNode = child0; + } + else + { + if(child0Indices.nbIndices == NB_OBJECTS_PER_NODE) + { + child1Indices.nbIndices = 1; + child1Indices.indices[0] = index; + returnNode = child1; + } + else + { + const PxBounds3& primitiveBounds = bounds[index]; + const float pCenter = primitiveBounds.getCenter(axis); + if(center[axis] >= pCenter) + { + // move to new node + child1Indices.indices[child1Indices.nbIndices++] = index; + returnNode = child1; + } + else + { + // move to old node + child0Indices.indices[child0Indices.nbIndices++] = index; + returnNode = child0; + } + } + } + + // update bounds for the new nodes + Vec4V bvMin = V4LoadU(&bounds[child0Indices.indices[0]].minimum.x); + Vec4V bvMax = V4LoadU(&bounds[child0Indices.indices[0]].maximum.x); + for(PxU32 i = 1; i < child0Indices.nbIndices; i++) + { + const Vec4V nodeMinV = V4LoadU(&bounds[child0Indices.indices[i]].minimum.x); + const Vec4V nodeMaxV = V4LoadU(&bounds[child0Indices.indices[i]].maximum.x); + + bvMin = V4Min(bvMin, nodeMinV); + bvMax = V4Max(bvMax, nodeMaxV); + } + child0->mBVMin = V4ClearW(bvMin); + child0->mBVMax = V4ClearW(bvMax); + + bvMin = V4LoadU(&bounds[child1Indices.indices[0]].minimum.x); + bvMax = V4LoadU(&bounds[child1Indices.indices[0]].maximum.x); + for(PxU32 i = 1; i < child1Indices.nbIndices; i++) + { + const Vec4V nodeMinV = V4LoadU(&bounds[child1Indices.indices[i]].minimum.x); + const Vec4V nodeMaxV = V4LoadU(&bounds[child1Indices.indices[i]].maximum.x); + + bvMin = V4Min(bvMin, nodeMinV); + bvMax = V4Max(bvMax, nodeMaxV); + } + child1->mBVMin = V4ClearW(bvMin); + child1->mBVMax = V4ClearW(bvMax); + + // node parent is the same, setup the new childs + node->mChilds[0] = child0; + node->mChilds[1] = child1; + node->mBVMin = newMinV; + node->mBVMax = newMaxV; + + updateHierarchyAfterInsert(node); + + PX_ASSERT(returnNode); + return returnNode; +} + +void IncrementalAABBTree::rotateTree(IncrementalAABBTreeNode* node, NodeList& changedLeaf, PxU32 largesRotateNodeIn, const PxBounds3* bounds, bool rotateAgain) +{ + PX_ASSERT(!node->isLeaf()); + + IncrementalAABBTreeNode* smallerNode = node->mChilds[(largesRotateNodeIn == 0) ? 1 : 0]; + IncrementalAABBTreeNode* largerNode = node->mChilds[largesRotateNodeIn]; + PX_ASSERT(!largerNode->isLeaf()); + + // take a leaf from larger node and add it to the smaller node + const Vec4V testCenterV = V4Add(smallerNode->mBVMax, smallerNode->mBVMin); + IncrementalAABBTreeNode* rotationNode = NULL; // store a node that seems not balanced + PxU32 largesRotateNode = 0; + bool rotateNode = false; + PxU32 traversalIndex = traversalDirection(*largerNode->mChilds[0], *largerNode->mChilds[1], testCenterV, false, rotateNode, largesRotateNode); + IncrementalAABBTreeNode* closestNode = largerNode->mChilds[traversalIndex]; + while(!closestNode->isLeaf()) + { + Ps::prefetchLine(closestNode->mChilds[0]->mChilds[0]); + Ps::prefetchLine(closestNode->mChilds[1]->mChilds[0]); + + traversalIndex = traversalDirection(*closestNode->mChilds[0], *closestNode->mChilds[1], testCenterV, false, rotateNode, largesRotateNode); + closestNode = closestNode->mChilds[traversalIndex]; + } + + // we have the leaf that we want to rotate + // create new parent and remove the current leaf + changedLeaf.findAndReplaceWithLast(closestNode); + IncrementalAABBTreeNode* parent = closestNode->mParent; + IncrementalAABBTreeNodePair* removedPair = reinterpret_cast(parent->mChilds[0]); + PX_ASSERT(!parent->isLeaf()); + + // copy the remaining child into parent + IncrementalAABBTreeNode* remainingChild = (parent->mChilds[0] == closestNode) ? parent->mChilds[1] : parent->mChilds[0]; + parent->mBVMax = remainingChild->mBVMax; + parent->mBVMin = remainingChild->mBVMin; + if(remainingChild->isLeaf()) + { + parent->mIndices = remainingChild->mIndices; + parent->mChilds[1] = NULL; + changedLeaf.findAndReplaceWithLast(remainingChild); + changedLeaf.pushBack(parent); + } + else + { + parent->mChilds[0] = remainingChild->mChilds[0]; + parent->mChilds[0]->mParent = parent; + parent->mChilds[1] = remainingChild->mChilds[1]; + parent->mChilds[1]->mParent = parent; + } + + // update the hieararchy after the node removal + if(parent->mParent) + { + updateHierarchyAfterRemove(parent->mParent, bounds); + } + + // find new spot for the node + // take a leaf from larger node and add it to the smaller node + IncrementalAABBTreeNode* newSpotNode = NULL; + if(smallerNode->isLeaf()) + { + newSpotNode = smallerNode; + } + else + { + const Vec4V testClosestNodeCenterV = V4Add(closestNode->mBVMax, closestNode->mBVMin); + rotationNode = NULL; // store a node that seems not balanced + largesRotateNode = 0; + rotateNode = false; + bool testRotation = rotateAgain; + traversalIndex = traversalDirection(*smallerNode->mChilds[0], *smallerNode->mChilds[1], testClosestNodeCenterV, testRotation, rotateNode, largesRotateNode); + if(rotateNode && !smallerNode->mChilds[largesRotateNode]->isLeaf()) + { + rotationNode = smallerNode; + testRotation = false; + } + newSpotNode = smallerNode->mChilds[traversalIndex]; + while(!newSpotNode->isLeaf()) + { + Ps::prefetchLine(newSpotNode->mChilds[0]->mChilds[0]); + Ps::prefetchLine(newSpotNode->mChilds[1]->mChilds[0]); + + traversalIndex = traversalDirection(*newSpotNode->mChilds[0], *newSpotNode->mChilds[1], testClosestNodeCenterV, testRotation, rotateNode, largesRotateNode); + if(!rotationNode && rotateNode && !newSpotNode->mChilds[largesRotateNode]->isLeaf()) + { + rotationNode = newSpotNode; + testRotation = false; + } + newSpotNode = newSpotNode->mChilds[traversalIndex]; + } + } + + // we have the closest leaf in the smaller child, lets merge it with the closestNode + if(newSpotNode->getNbPrimitives() + closestNode->getNbPrimitives() <= NB_OBJECTS_PER_NODE) + { + // all primitives fit into new spot, we merge here simply + AABBTreeIndices* targetIndices = newSpotNode->mIndices; + const AABBTreeIndices* sourceIndices = closestNode->mIndices; + for(PxU32 i = 0; i < sourceIndices->nbIndices; i++) + { + targetIndices->indices[targetIndices->nbIndices++] = sourceIndices->indices[i]; + } + PX_ASSERT(targetIndices->nbIndices <= NB_OBJECTS_PER_NODE); + if(changedLeaf.find(newSpotNode) == changedLeaf.end()) + changedLeaf.pushBack(newSpotNode); + mIndicesPool.deallocate(closestNode->mIndices); + + newSpotNode->mBVMin = V4Min(newSpotNode->mBVMin, closestNode->mBVMin); + newSpotNode->mBVMax = V4Max(newSpotNode->mBVMax, closestNode->mBVMax); + updateHierarchyAfterInsert(newSpotNode); + } + else + { + // we need to make new parent with newSpotNode and closestNode as childs + // create new pairs of nodes, parent will remain the node (the one we split) + IncrementalAABBTreeNode* child0 = reinterpret_cast(mNodesPool.allocate()); + IncrementalAABBTreeNode* child1 = child0 + 1; + + // setup parent + child0->mParent = newSpotNode; + child1->mParent = newSpotNode; + child0->mIndices = newSpotNode->mIndices; + child0->mChilds[1] = NULL; + child0->mBVMin = newSpotNode->mBVMin; + child0->mBVMax = newSpotNode->mBVMax; + child1->mIndices = closestNode->mIndices; + child1->mChilds[1] = NULL; + child1->mBVMin = closestNode->mBVMin; + child1->mBVMax = closestNode->mBVMax; + + // node parent is the same, setup the new childs + newSpotNode->mChilds[0] = child0; + newSpotNode->mChilds[1] = child1; + + newSpotNode->mBVMin = V4Min(child0->mBVMin, child1->mBVMin); + newSpotNode->mBVMax = V4Max(child0->mBVMax, child1->mBVMax); + + updateHierarchyAfterInsert(newSpotNode); + + changedLeaf.findAndReplaceWithLast(newSpotNode); + changedLeaf.pushBack(child0); + changedLeaf.pushBack(child1); + } + + // deallocate the closestNode, it has been moved +#if DEALLOCATE_RESET + removedPair->mNode0.mChilds[0] = NULL; + removedPair->mNode0.mChilds[1] = NULL; + + removedPair->mNode1.mChilds[0] = NULL; + removedPair->mNode1.mChilds[1] = NULL; +#endif + mNodesPool.deallocate(removedPair); + + // try to do one more rotation for the newly added node part of tree + if(rotationNode) + { + rotateTree(rotationNode, changedLeaf, largesRotateNode, bounds, false); + } +} + + +// insert new bounds into tree +IncrementalAABBTreeNode* IncrementalAABBTree::insert(const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf) +{ + PX_SIMD_GUARD; + + // get the bounds, reset the W value + const Vec4V minV = V4ClearW(V4LoadU(&bounds[index].minimum.x)); + const Vec4V maxV = V4ClearW(V4LoadU(&bounds[index].maximum.x)); + + // check if tree is empty + if(!mRoot) + { + // make it a leaf + AABBTreeIndices* indices = mIndicesPool.construct(index); + mRoot = reinterpret_cast (mNodesPool.allocate()); + mRoot->mBVMin = minV; + mRoot->mBVMax = maxV; + mRoot->mIndices = indices; + mRoot->mChilds[1] = NULL; + mRoot->mParent = NULL; + + return mRoot; + } + else + { + // check if root is a leaf + if(mRoot->isLeaf()) + { + // if we still can insert the primitive into the leaf, or we need to split + if(mRoot->getNbPrimitives() < NB_OBJECTS_PER_NODE) + { + // simply add the primitive into the current leaf + addPrimitiveIntoNode(mRoot, index, minV, maxV); + return mRoot; + } + else + { + // need to split the node + // check if the leaf is not marked as changed, we need to remove it + if(!changedLeaf.empty()) + { + PX_ASSERT(changedLeaf.size() == 1); + if(changedLeaf[0] == mRoot) + changedLeaf.popBack(); + } + IncrementalAABBTreeNode* retNode = splitLeafNode(mRoot, index, minV, maxV, bounds); + mRoot = retNode->mParent; + IncrementalAABBTreeNode* sibling = (mRoot->mChilds[0] == retNode) ? mRoot->mChilds[1] : mRoot->mChilds[0]; + if(sibling->isLeaf()) + changedLeaf.pushBack(sibling); + changedLeaf.pushBack(retNode); + return retNode; + } + } + else + { + const Vec4V testCenterV = V4Add(maxV, minV); + IncrementalAABBTreeNode* returnNode = NULL; + IncrementalAABBTreeNode* rotationNode = NULL; // store a node that seems not balanced + PxU32 largesRotateNode = 0; + bool rotateNode = false; +#if SUPPORT_TREE_ROTATION + bool testRotation = true; +#else + bool testRotation = false; +#endif + // we dont need to modify root, lets traverse the tree to find the right spot + PxU32 traversalIndex = traversalDirection(*mRoot->mChilds[0], *mRoot->mChilds[1], testCenterV, testRotation, rotateNode, largesRotateNode); + if(rotateNode && !mRoot->mChilds[largesRotateNode]->isLeaf()) + { + rotationNode = mRoot; + testRotation = false; + } + IncrementalAABBTreeNode* baseNode = mRoot->mChilds[traversalIndex]; + while(!baseNode->isLeaf()) + { + Ps::prefetchLine(baseNode->mChilds[0]->mChilds[0]); + Ps::prefetchLine(baseNode->mChilds[1]->mChilds[0]); + + traversalIndex = traversalDirection(*baseNode->mChilds[0], *baseNode->mChilds[1], testCenterV, testRotation, rotateNode, largesRotateNode); + if(!rotationNode && rotateNode && !baseNode->mChilds[largesRotateNode]->isLeaf()) + { + rotationNode = baseNode; + testRotation = false; + } + baseNode = baseNode->mChilds[traversalIndex]; + } + + // if we still can insert the primitive into the leaf, or we need to split + if(baseNode->getNbPrimitives() < NB_OBJECTS_PER_NODE) + { + // simply add the primitive into the current leaf + addPrimitiveIntoNode(baseNode, index, minV, maxV); + returnNode = baseNode; + if(!changedLeaf.empty()) + { + PX_ASSERT(changedLeaf.size() == 1); + if(changedLeaf[0] != baseNode) + changedLeaf.pushBack(baseNode); + } + else + changedLeaf.pushBack(baseNode); + } + else + { + // split + // check if the leaf is not marked as changed, we need to remove it + if(!changedLeaf.empty()) + { + PX_ASSERT(changedLeaf.size() == 1); + if(changedLeaf[0] == baseNode) + changedLeaf.popBack(); + } + IncrementalAABBTreeNode* retNode = splitLeafNode(baseNode, index, minV, maxV, bounds); + const IncrementalAABBTreeNode* splitParent = retNode->mParent; + changedLeaf.pushBack(splitParent->mChilds[0]); + changedLeaf.pushBack(splitParent->mChilds[1]); + + returnNode = retNode; + } + + if(rotationNode) + { + rotateTree(rotationNode, changedLeaf, largesRotateNode, bounds, true); + returnNode = NULL; + } + + return returnNode; + } + } +} + +// update the index, do a full remove/insert update +IncrementalAABBTreeNode* IncrementalAABBTree::update(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf) +{ + PX_SIMD_GUARD; + + IncrementalAABBTreeNode* removedNode = remove(node, index, bounds); + if(removedNode && removedNode->isLeaf()) + { + changedLeaf.pushBack(removedNode); + } + return insert(index, bounds, changedLeaf); +} + +// update the index, faster version with a lazy update of objects that moved just a bit +IncrementalAABBTreeNode* IncrementalAABBTree::updateFast(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf) +{ + PX_SIMD_GUARD; + + const Vec4V minV = V4ClearW(V4LoadU(&bounds[index].minimum.x)); + const Vec4V maxV = V4ClearW(V4LoadU(&bounds[index].maximum.x)); + + // for update fast, we dont care if the tree gets slowly unbalanced, we are building a new tree already + if(nodeIntersection(*node, minV, maxV)) + { + updateHierarchyAfterRemove(node, bounds); + return node; + } + else + { + IncrementalAABBTreeNode* removedNode = remove(node, index, bounds); + if(removedNode && removedNode->isLeaf()) + { + changedLeaf.pushBack(removedNode); + } + return insert(index, bounds, changedLeaf); + } +} + +// remove primitive from the tree, return a node if it moved to its parent +IncrementalAABBTreeNode* IncrementalAABBTree::remove(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds) +{ + PX_SIMD_GUARD; + PX_ASSERT(node->isLeaf()); + // if we just remove the primitive from the list + if(node->getNbPrimitives() > 1) + { + removePrimitiveFromNode(node, index); + + // update the hierarchy + updateHierarchyAfterRemove(node, bounds); + return NULL; + } + else + { + // if root node and the last primitive remove root + if(node == mRoot) + { +#if DEALLOCATE_RESET + IncrementalAABBTreeNodePair* removedPair = reinterpret_cast(node); + removedPair->mNode0.mChilds[0] = NULL; + removedPair->mNode0.mChilds[1] = NULL; + + removedPair->mNode1.mChilds[0] = NULL; + removedPair->mNode1.mChilds[1] = NULL; +#endif + mNodesPool.deallocate(reinterpret_cast(node)); + mRoot = NULL; + return NULL; + } + else + { + // create new parent and remove the current leaf + IncrementalAABBTreeNode* parent = node->mParent; + IncrementalAABBTreeNodePair* removedPair = reinterpret_cast(parent->mChilds[0]); + PX_ASSERT(!parent->isLeaf()); + + // copy the remaining child into parent + IncrementalAABBTreeNode* remainingChild = (parent->mChilds[0] == node) ? parent->mChilds[1] : parent->mChilds[0]; + parent->mBVMax = remainingChild->mBVMax; + parent->mBVMin = remainingChild->mBVMin; + if(remainingChild->isLeaf()) + { + parent->mIndices = remainingChild->mIndices; + parent->mChilds[1] = NULL; + } + else + { + parent->mChilds[0] = remainingChild->mChilds[0]; + parent->mChilds[0]->mParent = parent; + parent->mChilds[1] = remainingChild->mChilds[1]; + parent->mChilds[1]->mParent = parent; + } + + if(parent->mParent) + { + updateHierarchyAfterRemove(parent->mParent, bounds); + } + + mIndicesPool.deallocate(node->mIndices); +#if DEALLOCATE_RESET + removedPair->mNode0.mChilds[0] = NULL; + removedPair->mNode0.mChilds[1] = NULL; + + removedPair->mNode1.mChilds[0] = NULL; + removedPair->mNode1.mChilds[1] = NULL; +#endif + mNodesPool.deallocate(removedPair); + return parent; + } + } +} + +// fixup the indices +void IncrementalAABBTree::fixupTreeIndices(IncrementalAABBTreeNode* node, const PoolIndex index, const PoolIndex newIndex) +{ + PX_ASSERT(node->isLeaf()); + + AABBTreeIndices& indices = *node->mIndices; + for(PxU32 i = 0; i < indices.nbIndices; i++) + { + if(indices.indices[i] == index) + { + indices.indices[i] = newIndex; + return; + } + } + PX_ASSERT(0); +} + +// shift node +static void shiftNode(IncrementalAABBTreeNode* node, const Vec4V& shiftV) +{ + node->mBVMax = V4Sub(node->mBVMax, shiftV); + node->mBVMin = V4Sub(node->mBVMin, shiftV); + + if(!node->isLeaf()) + { + shiftNode(node->mChilds[0], shiftV); + shiftNode(node->mChilds[1], shiftV); + } +} + +// shift origin +void IncrementalAABBTree::shiftOrigin(const PxVec3& shift) +{ + if(mRoot) + { + const Vec4V shiftV = V4ClearW(V4LoadU(&shift.x)); + + shiftNode(mRoot, shiftV); + } +} + +static void checkNode(IncrementalAABBTreeNode* node, IncrementalAABBTreeNode* parent, const PxBounds3* bounds, PoolIndex maxIndex, PxU32& numIndices, PxU32& numNodes) +{ + PX_ASSERT(node->mParent == parent); + PX_ASSERT(!parent->isLeaf()); + PX_ASSERT(parent->mChilds[0] == node || parent->mChilds[1] == node); + + numNodes++; + if(!node->isLeaf()) + { + PX_ASSERT(nodeInsideBounds(node->mChilds[0]->mBVMin, node->mChilds[0]->mBVMax, node->mBVMin, node->mBVMax)); + PX_ASSERT(nodeInsideBounds(node->mChilds[1]->mBVMin, node->mChilds[1]->mBVMax, node->mBVMin, node->mBVMax)); + + const Vec4V testMinV = V4Min(parent->mChilds[0]->mBVMin, parent->mChilds[1]->mBVMin); + const Vec4V testMaxV = V4Max(parent->mChilds[0]->mBVMax, parent->mChilds[1]->mBVMax); + + PX_UNUSED(testMinV); + PX_UNUSED(testMaxV); + PX_ASSERT(nodeInsideBounds(node->mBVMin, node->mBVMax, testMinV, testMaxV)); + + checkNode(node->mChilds[0], node, bounds, maxIndex, numIndices, numNodes); + checkNode(node->mChilds[1], node, bounds, maxIndex, numIndices, numNodes); + } + else + { + const AABBTreeIndices& indices = *node->mIndices; + PX_ASSERT(indices.nbIndices); + Vec4V testMinV = V4ClearW(V4LoadU(&bounds[indices.indices[0]].minimum.x)); + Vec4V testMaxV = V4ClearW(V4LoadU(&bounds[indices.indices[0]].maximum.x)); + for(PxU32 i = 0; i < indices.nbIndices; i++) + { + PX_ASSERT(indices.indices[i] < maxIndex); + numIndices++; + + const Vec4V minV = V4ClearW(V4LoadU(&bounds[indices.indices[i]].minimum.x)); + const Vec4V maxV = V4ClearW(V4LoadU(&bounds[indices.indices[i]].maximum.x)); + + testMinV = V4Min(testMinV, minV); + testMaxV = V4Max(testMaxV, maxV); + + PX_ASSERT(nodeInsideBounds(minV, maxV, node->mBVMin, node->mBVMax)); + } + + PX_ASSERT(boundsEqual(testMinV, testMaxV, node->mBVMin, node->mBVMax)); + } +} + +void IncrementalAABBTree::hierarchyCheck(PoolIndex maxIndex, const PxBounds3* bounds) +{ + PxU32 numHandles = 0; + PxU32 numPosNodes = 0; + PxU32 numNegNodes = 0; + if(mRoot && !mRoot->isLeaf()) + { + checkNode(mRoot->mChilds[0], mRoot, bounds, maxIndex, numHandles, numPosNodes); + checkNode(mRoot->mChilds[1], mRoot, bounds, maxIndex, numHandles, numNegNodes); + + PX_ASSERT(numHandles == maxIndex); + } +} + +void IncrementalAABBTree::hierarchyCheck(const PxBounds3* bounds) +{ + PxU32 numHandles = 0; + PxU32 numPosNodes = 0; + PxU32 numNegNodes = 0; + if(mRoot && !mRoot->isLeaf()) + { + checkNode(mRoot->mChilds[0], mRoot, bounds, 0xFFFFFFFF, numHandles, numPosNodes); + checkNode(mRoot->mChilds[1], mRoot, bounds, 0xFFFFFFFF, numHandles, numNegNodes); + } +} + +void IncrementalAABBTree::checkTreeLeaf(IncrementalAABBTreeNode* leaf, PoolIndex h) +{ + PX_ASSERT(leaf->isLeaf()); + + const AABBTreeIndices& indices = *leaf->mIndices; + bool found = false; + for(PxU32 i = 0; i < indices.nbIndices; i++) + { + if(indices.indices[i] == h) + { + found = true; + break; + } + } + PX_UNUSED(found); + PX_ASSERT(found); +} + +PxU32 IncrementalAABBTree::getTreeLeafDepth(IncrementalAABBTreeNode* leaf) +{ + PxU32 depth = 1; + IncrementalAABBTreeNode* parent = leaf->mParent; + while(parent) + { + depth++; + parent = parent->mParent; + } + return depth; +} + +// build the tree from given bounds +bool IncrementalAABBTree::build(AABBTreeBuildParams& params, Ps::Array& mapping) +{ + // Init stats + BuildStats stats; + const PxU32 nbPrimitives = params.mNbPrimitives; + if (!nbPrimitives) + return false; + + PxU32* indices = NULL; + const bool buildStatus = buildAABBTree(params, mNodeAllocator, stats, indices); + PX_UNUSED(buildStatus); + PX_ASSERT(buildStatus); + + PX_FREE_AND_RESET(params.mCache); + + IncrementalAABBTreeNode** treeNodes = reinterpret_cast(PX_ALLOC(sizeof(IncrementalAABBTreeNode*)*(stats.getCount()), "temp node helper array")); + PxMemSet(treeNodes, 0, sizeof(IncrementalAABBTreeNode*)*(stats.getCount())); + + clone(mapping, indices, treeNodes); + mRoot = treeNodes[0]; + mRoot->mParent = NULL; + + PX_FREE_AND_RESET(indices); + PX_FREE_AND_RESET(treeNodes); + + mNodeAllocator.release(); + return true; +} + +// clone the tree, the tree is computed in the NodeAllocator, similar to AABBTree flatten +void IncrementalAABBTree::clone(Ps::Array& mapping, const PxU32* _indices, IncrementalAABBTreeNode** treeNodes) +{ + PxU32 offset = 0; + const PxU32 nbSlabs = mNodeAllocator.mSlabs.size(); + for (PxU32 s = 0; s(mNodesPool.allocate()); + treeNodes[offset] = destNode; + } + + destNode->mBVMin = V4ClearW(V4LoadU(&pool[i].mBV.minimum.x)); + destNode->mBVMax = V4ClearW(V4LoadU(&pool[i].mBV.maximum.x)); + + if (pool[i].isLeaf()) + { + AABBTreeIndices* indices = mIndicesPool.allocate(); + destNode->mIndices = indices; + destNode->mChilds[1] = NULL; + indices->nbIndices = pool[i].getNbPrimitives(); + PX_ASSERT(indices->nbIndices <= 16); + const PxU32* sourceIndices = _indices + pool[i].mNodeIndex; + for (PxU32 iIndices = 0; iIndices < indices->nbIndices; iIndices++) + { + const PxU32 sourceIndex = sourceIndices[iIndices]; + indices->indices[iIndices] = sourceIndex; + PX_ASSERT(sourceIndex < mapping.size()); + mapping[sourceIndex] = destNode; + } + } + else + { + PX_ASSERT(pool[i].mPos); + PxU32 localNodeIndex = 0xffffffff; + PxU32 nodeBase = 0; + for (PxU32 j = 0; j= mNodeAllocator.mSlabs[j].mPool && pool[i].mPos < mNodeAllocator.mSlabs[j].mPool + mNodeAllocator.mSlabs[j].mNbUsedNodes) + { + localNodeIndex = PxU32(pool[i].mPos - mNodeAllocator.mSlabs[j].mPool); + break; + } + nodeBase += mNodeAllocator.mSlabs[j].mNbUsedNodes; + } + const PxU32 nodeIndex = nodeBase + localNodeIndex; + + IncrementalAABBTreeNode* child0 = treeNodes[nodeIndex]; + IncrementalAABBTreeNode* child1 = treeNodes[nodeIndex + 1]; + if(!child0) + { + PX_ASSERT(!child1); + child0 = reinterpret_cast(mNodesPool.allocate()); + child1 = child0 + 1; + treeNodes[nodeIndex] = child0; + treeNodes[nodeIndex + 1] = child1; + } + + destNode->mChilds[0] = child0; + destNode->mChilds[1] = child1; + child0->mParent = destNode; + child1->mParent = destNode; + } + offset++; + } + } +} + +void IncrementalAABBTree::copyNode(IncrementalAABBTreeNode& destNode, const BVHNode& sourceNode, + const BVHNode* nodeBase, IncrementalAABBTreeNode* parent, const PxU32* primitivesBase, + Ps::Array& mapping) +{ + destNode.mParent = parent; + destNode.mBVMin = V4ClearW(V4LoadU(&sourceNode.mBV.minimum.x)); + destNode.mBVMax = V4ClearW(V4LoadU(&sourceNode.mBV.maximum.x)); + if(sourceNode.isLeaf()) + { + AABBTreeIndices* indices = mIndicesPool.allocate(); + destNode.mIndices = indices; + indices->nbIndices = sourceNode.getNbPrimitives(); + const PxU32* sourceIndices = sourceNode.getPrimitives(primitivesBase); + for(PxU32 i = 0; i < indices->nbIndices; i++) + { + const PxU32 sourceIndex = sourceIndices[i]; + indices->indices[i] = sourceIndex; + mapping[sourceIndex] = &destNode; + } + } + else + { + IncrementalAABBTreeNodePair* nodePair = mNodesPool.construct(); + IncrementalAABBTreeNode* child0 = &nodePair->mNode0; + IncrementalAABBTreeNode* child1 = &nodePair->mNode1; + + destNode.mChilds[0] = child0; + destNode.mChilds[1] = child1; + + copyNode(*destNode.mChilds[0], *sourceNode.getPos(nodeBase), nodeBase, &destNode, primitivesBase, mapping); + copyNode(*destNode.mChilds[1], *sourceNode.getNeg(nodeBase), nodeBase, &destNode, primitivesBase, mapping); + } +} + +// build the tree from the prebuild AABB tree +void IncrementalAABBTree::copy(const BVHStructure& bvhStructure, Ps::Array& mapping) +{ + if(bvhStructure.getNbBounds() == 0) + return; + + IncrementalAABBTreeNodePair* nodePair = mNodesPool.construct(); + mRoot = &nodePair->mNode0; + + const BVHNode* nodes = bvhStructure.getNodes(); + copyNode(*mRoot, *nodes, nodes, NULL, bvhStructure.getIndices(), mapping); +} + diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h new file mode 100644 index 000000000..6f5602739 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_INCREMENTAL_AABB_TREE_H +#define SQ_INCREMENTAL_AABB_TREE_H + +#include "foundation/PxBounds3.h" +#include "PsUserAllocated.h" +#include "PsVecMath.h" +#include "PsPool.h" +#include "PsHashMap.h" +#include "GuAABBTreeBuild.h" +#include "SqPruner.h" +#include "SqTypedef.h" +#include "SqPrunerMergeData.h" + + +namespace physx +{ + namespace Gu + { + struct BVHNode; + } + + using namespace shdfnd::aos; + + namespace Sq + { + class AABBTree; + + #define NB_OBJECTS_PER_NODE 4 + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // tree indices, can change in runtime + struct AABBTreeIndices + { + PX_FORCE_INLINE AABBTreeIndices(PoolIndex index) : + nbIndices(1) + { + indices[0] = index; + for(PxU32 i = 1; i < NB_OBJECTS_PER_NODE; i++) + { + indices[i] = 0; + } + } + + PxU32 nbIndices; + PoolIndex indices[NB_OBJECTS_PER_NODE]; + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // tree node, has parent information + class IncrementalAABBTreeNode : public Ps::UserAllocated + { + public: + PX_FORCE_INLINE IncrementalAABBTreeNode(): + mParent(NULL) + { + mChilds[0] = NULL; + mChilds[1] = NULL; + } + PX_FORCE_INLINE IncrementalAABBTreeNode(AABBTreeIndices* indices): + mParent(NULL) + { + mIndices = indices; + mChilds[1] = NULL; + } + PX_FORCE_INLINE ~IncrementalAABBTreeNode() {} + + PX_FORCE_INLINE PxU32 isLeaf() const { return PxU32(mChilds[1]==0); } + + PX_FORCE_INLINE const PxU32* getPrimitives(const PxU32* ) const { return &mIndices->indices[0]; } + PX_FORCE_INLINE PxU32* getPrimitives(PxU32* ) { return &mIndices->indices[0]; } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mIndices->nbIndices; } + + PX_FORCE_INLINE const IncrementalAABBTreeNode* getPos(const IncrementalAABBTreeNode* ) const { return mChilds[0]; } + PX_FORCE_INLINE const IncrementalAABBTreeNode* getNeg(const IncrementalAABBTreeNode* ) const { return mChilds[1]; } + + PX_FORCE_INLINE IncrementalAABBTreeNode* getPos(IncrementalAABBTreeNode* ) { return mChilds[0]; } + PX_FORCE_INLINE IncrementalAABBTreeNode* getNeg(IncrementalAABBTreeNode* ) { return mChilds[1]; } + + PX_FORCE_INLINE void getAABBCenterExtentsV(physx::shdfnd::aos::Vec3V* center, physx::shdfnd::aos::Vec3V* extents) const + { + const float half = 0.5f; + const FloatV halfV = FLoad(half); + + *extents = Vec3V_From_Vec4V((V4Scale(V4Sub(mBVMax, mBVMin), halfV))); + *center = Vec3V_From_Vec4V((V4Scale(V4Add(mBVMax, mBVMin), halfV))); + } + + PX_FORCE_INLINE void getAABBCenterExtentsV2(physx::shdfnd::aos::Vec3V* center, physx::shdfnd::aos::Vec3V* extents) const + { + *extents = Vec3V_From_Vec4V((V4Sub(mBVMax, mBVMin))); + *center = Vec3V_From_Vec4V((V4Add(mBVMax, mBVMin))); + } + + PX_FORCE_INLINE void getAABBMinMaxV(physx::shdfnd::aos::Vec4V* minV, physx::shdfnd::aos::Vec4V* maxV) const + { + *minV = mBVMin; + *maxV = mBVMax; + } + + Vec4V mBVMin; // Global bounding-volume min enclosing all the node-related primitives + Vec4V mBVMax; // Global bounding-volume max enclosing all the node-related primitives + IncrementalAABBTreeNode* mParent; // node parent + union + { + IncrementalAABBTreeNode* mChilds[2]; // childs of node if not a leaf + AABBTreeIndices* mIndices; // if leaf, indices information + }; + }; + + struct IncrementalAABBTreeNodePair + { + IncrementalAABBTreeNode mNode0; + IncrementalAABBTreeNode mNode1; + }; + + typedef Ps::Array NodeList; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // incremental AABB tree, all changes are immediatelly reflected to the tree + class IncrementalAABBTree : public Ps::UserAllocated + { + public: + IncrementalAABBTree(); + ~IncrementalAABBTree(); + + // Build the tree for the first time + bool build(Gu::AABBTreeBuildParams& params, Ps::Array& mapping); + + // insert a new index into the tree + IncrementalAABBTreeNode* insert(const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf); + + // update the object in the tree - full update insert/remove + IncrementalAABBTreeNode* update(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf); + // update the object in the tree, faster method, that may unballance the tree + IncrementalAABBTreeNode* updateFast(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf); + + // remove object from the tree + IncrementalAABBTreeNode* remove(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds); + + // fixup the tree indices, if we swapped the objects in the pruning pool + void fixupTreeIndices(IncrementalAABBTreeNode* node, const PoolIndex index, const PoolIndex newIndex); + + // origin shift + void shiftOrigin(const PxVec3& shift); + + // get the tree root node + const IncrementalAABBTreeNode* getNodes() const { return mRoot; } + + // define this function so we can share the scene query code with regular AABBTree + const PxU32* getIndices() const { return NULL; } + + // paranoia checks + void hierarchyCheck(PoolIndex maxIndex, const PxBounds3* bounds); + void hierarchyCheck(const PxBounds3* bounds); + void checkTreeLeaf(IncrementalAABBTreeNode* leaf, PoolIndex h); + PxU32 getTreeLeafDepth(IncrementalAABBTreeNode* leaf); + + void release(); + + void copy(const Gu::BVHStructure& bvhStructure, Ps::Array& mapping); + + private: + + // clone the tree from the generic AABB tree that was built + void clone(Ps::Array& mapping, const PxU32* indices, IncrementalAABBTreeNode** treeNodes); + + void copyNode(IncrementalAABBTreeNode& destNode, const Gu::BVHNode& sourceNode, const Gu::BVHNode* nodeBase, + IncrementalAABBTreeNode* parent, const PxU32* primitivesBase, Ps::Array& mapping); + + // split leaf node, the newly added object does not fit in + IncrementalAABBTreeNode* splitLeafNode(IncrementalAABBTreeNode* node, const PoolIndex index, const Vec4V& minV, const Vec4V& maxV, const PxBounds3* bounds); + + void rotateTree(IncrementalAABBTreeNode* node, NodeList& changedLeaf, PxU32 largesRotateNode, const PxBounds3* bounds, bool rotateAgain); + + void releaseNode(IncrementalAABBTreeNode* node); + private: + Ps::Pool mIndicesPool; + Ps::Pool mNodesPool; + IncrementalAABBTreeNode* mRoot; + + Gu::NodeAllocator mNodeAllocator; + }; + } +} + +#endif diff --git a/src/PhysX/physx/source/scenequery/src/SqMetaData.cpp b/src/PhysX/physx/source/scenequery/src/SqMetaData.cpp new file mode 100644 index 000000000..e5a57940f --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqMetaData.cpp @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxMetaData.h" + +#include "SqPruningStructure.h" + +using namespace physx; +using namespace Sq; + +/////////////////////////////////////////////////////////////////////////////// + +void PruningStructure::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_VCLASS(stream, PruningStructure) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PruningStructure, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mNbNodes[0], 0) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mNbNodes[1], 0) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, AABBTreeRuntimeNode, mAABBTreeNodes[0], PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, AABBTreeRuntimeNode, mAABBTreeNodes[1], PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mNbObjects[0], 0) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mNbObjects[1], 0) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mAABBTreeIndices[0], PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mAABBTreeIndices[1], PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mNbActors, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxActor*, mActors, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, bool, mValid, 0) +} + + diff --git a/src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp b/src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp new file mode 100644 index 000000000..327d5ebf0 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp @@ -0,0 +1,182 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "foundation/PxMemory.h" +#include "SqPruningPool.h" + +using namespace physx; +using namespace Sq; +using namespace Cm; + +PruningPool::PruningPool() : + mNbObjects (0), + mMaxNbObjects (0), + mWorldBoxes (NULL), + mObjects (NULL), + mHandleToIndex (NULL), + mIndexToHandle (NULL), + mFirstRecycledHandle(INVALID_PRUNERHANDLE) +{ +} + +PruningPool::~PruningPool() +{ + PX_FREE_AND_RESET(mWorldBoxes); + PX_FREE_AND_RESET(mObjects); + PX_FREE_AND_RESET(mHandleToIndex); + PX_FREE_AND_RESET(mIndexToHandle); +} + +bool PruningPool::resize(PxU32 newCapacity) +{ + // PT: we always allocate one extra box, to make sure we can safely use V4 loads on the array + PxBounds3* newBoxes = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(newCapacity+1), "PxBounds3")); + PrunerPayload* newData = reinterpret_cast(PX_ALLOC(sizeof(PrunerPayload)*newCapacity, "PrunerPayload*")); + PrunerHandle* newIndexToHandle = reinterpret_cast(PX_ALLOC(sizeof(PrunerHandle)*newCapacity, "Pruner Index Mapping")); + PoolIndex* newHandleToIndex = reinterpret_cast(PX_ALLOC(sizeof(PoolIndex)*newCapacity, "Pruner Index Mapping")); + if( (NULL==newBoxes) || (NULL==newData) || (NULL==newIndexToHandle) || (NULL==newHandleToIndex) + ) + { + PX_FREE_AND_RESET(newBoxes); + PX_FREE_AND_RESET(newData); + PX_FREE_AND_RESET(newIndexToHandle); + PX_FREE_AND_RESET(newHandleToIndex); + return false; + } + + if(mWorldBoxes) PxMemCopy(newBoxes, mWorldBoxes, mNbObjects*sizeof(PxBounds3)); + if(mObjects) PxMemCopy(newData, mObjects, mNbObjects*sizeof(PrunerPayload)); + if(mIndexToHandle) PxMemCopy(newIndexToHandle, mIndexToHandle, mNbObjects*sizeof(PrunerHandle)); + if(mHandleToIndex) PxMemCopy(newHandleToIndex, mHandleToIndex, mMaxNbObjects*sizeof(PoolIndex)); + mMaxNbObjects = newCapacity; + + PX_FREE_AND_RESET(mWorldBoxes); + PX_FREE_AND_RESET(mObjects); + PX_FREE_AND_RESET(mHandleToIndex); + PX_FREE_AND_RESET(mIndexToHandle); + mWorldBoxes = newBoxes; + mObjects = newData; + mHandleToIndex = newHandleToIndex; + mIndexToHandle = newIndexToHandle; + + return true; +} + +void PruningPool::preallocate(PxU32 newCapacity) +{ + if(newCapacity>mMaxNbObjects) + resize(newCapacity); +} + +PxU32 PruningPool::addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* payload, PxU32 count) +{ + for(PxU32 i=0;i(mMaxNbObjects*2, 64))) + { + // pool can return an invalid handle if memory alloc fails + // should probably have an error here or not handle this + results[i] = INVALID_PRUNERHANDLE; // PT: we need to write the potentially invalid handle to let users know which object failed first + return i; + } + } + PX_ASSERT(mNbObjects!=mMaxNbObjects); + + const PoolIndex index = mNbObjects++; + + // update mHandleToIndex and mIndexToHandle mappings + PrunerHandle handle; + if(mFirstRecycledHandle != INVALID_PRUNERHANDLE) + { + // mFirstRecycledHandle is an entry into a freelist for removed slots + // this path is only taken if we have any removed slots + handle = mFirstRecycledHandle; + mFirstRecycledHandle = mHandleToIndex[handle]; + } + else + { + handle = index; + } + + // PT: TODO: investigate why we added mIndexToHandle/mHandleToIndex. The initial design with 'Prunable' objects didn't need these arrays. + + // PT: these 3 arrays are "parallel" + mWorldBoxes [index] = bounds[i]; // store the payload and AABB in parallel arrays + mObjects [index] = payload[i]; + mIndexToHandle [index] = handle; + + mHandleToIndex[handle] = index; + results[i] = handle; + } + return count; +} + +PoolIndex PruningPool::removeObject(PrunerHandle h) +{ + PX_ASSERT(mNbObjects); + + // remove the object and its AABB by provided PrunerHandle and update mHandleToIndex and mIndexToHandle mappings + const PoolIndex indexOfRemovedObject = mHandleToIndex[h]; // retrieve object's index from handle + + const PoolIndex indexOfLastObject = --mNbObjects; // swap the object at last index with index + if(indexOfLastObject!=indexOfRemovedObject) + { + // PT: move last object's data to recycled spot (from removed object) + + // PT: the last object has moved so we need to handle the mappings for this object + // PT: TODO: investigate where this double-mapping comes from. Should not be needed... + + // PT: these 3 arrays are "parallel" + const PrunerHandle handleOfLastObject = mIndexToHandle[indexOfLastObject]; + mWorldBoxes [indexOfRemovedObject] = mWorldBoxes [indexOfLastObject]; + mObjects [indexOfRemovedObject] = mObjects [indexOfLastObject]; + mIndexToHandle [indexOfRemovedObject] = handleOfLastObject; + + mHandleToIndex[handleOfLastObject] = indexOfRemovedObject; + } + + // mHandleToIndex also stores the freelist for removed handles (in place of holes formed by removed handles) + mHandleToIndex[h] = mFirstRecycledHandle; // update linked list of available recycled handles + mFirstRecycledHandle = h; // update the list head + + return indexOfLastObject; +} + +void PruningPool::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i=0; i < mNbObjects; i++) + { + mWorldBoxes[i].minimum -= shift; + mWorldBoxes[i].maximum -= shift; + } +} diff --git a/src/PhysX/physx/source/scenequery/src/SqPruningPool.h b/src/PhysX/physx/source/scenequery/src/SqPruningPool.h new file mode 100644 index 000000000..9886f8a23 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqPruningPool.h @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_PRUNINGPOOL_H +#define SQ_PRUNINGPOOL_H + +#include "SqPruner.h" +#include "SqTypedef.h" +#include "SqBounds.h" + +namespace physx +{ +namespace Sq +{ + // This class is designed to maintain a two way mapping between pair(PrunerPayload,AABB) and PrunerHandle + // Internally there's also an index for handles (AP: can be simplified?) + // This class effectively stores bounded pruner payloads, returns a PrunerHandle and allows O(1) + // access to them using a PrunerHandle + // Supported operations are add, remove, update bounds + class PruningPool + { + public: + PruningPool(); + ~PruningPool(); + + PX_FORCE_INLINE const PrunerPayload& getPayload(PrunerHandle handle) const { return mObjects[getIndex(handle)]; } + + PX_FORCE_INLINE const PrunerPayload& getPayload(PrunerHandle handle, PxBounds3*& bounds) const + { + const PoolIndex index = getIndex(handle); + bounds = mWorldBoxes + index; + return mObjects[index]; + } + + void shiftOrigin(const PxVec3& shift); + + // PT: adds 'count' objects to the pool. Needs 'count' bounds and 'count' payloads passed as input. Writes out 'count' handles + // in 'results' array. Function returns number of successfully added objects, ideally 'count' but can be less in case we run + // out of memory. + PxU32 addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* payload, PxU32 count); + + // this function will swap the last object with the hole formed by removed PrunerHandle object + // and return the removed last object's index in the pool + PoolIndex removeObject(PrunerHandle h); + + // Data access + PX_FORCE_INLINE PoolIndex getIndex(PrunerHandle h)const { return mHandleToIndex[h]; } + PX_FORCE_INLINE PrunerPayload* getObjects() const { return mObjects; } + PX_FORCE_INLINE PxU32 getNbActiveObjects() const { return mNbObjects; } + PX_FORCE_INLINE const PxBounds3* getCurrentWorldBoxes() const { return mWorldBoxes; } + PX_FORCE_INLINE PxBounds3* getCurrentWorldBoxes() { return mWorldBoxes; } + + PX_FORCE_INLINE const PxBounds3& getWorldAABB(PrunerHandle h) const + { + return mWorldBoxes[getIndex(h)]; + } + + PX_FORCE_INLINE void updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count) + { + for(PxU32 i=0; igetConcreteType(); + if (type == PxConcreteType::eRIGID_STATIC) + { + static_cast(mActors[i])->getShapeManager().setPruningStructure(NULL); + } + else if (type == PxConcreteType::eRIGID_DYNAMIC) + { + static_cast(mActors[i])->getShapeManager().setPruningStructure(NULL); + } + } + + if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + { + delete this; + } + else + { + this->~PruningStructure(); + } +} + +template +static void getShapeBounds(PxRigidActor* actor, bool dynamic, PxBounds3* bounds, PxU32& numShapes) +{ + PruningIndex::Enum treeStructure = dynamic ? PruningIndex::eDYNAMIC : PruningIndex::eSTATIC; + ActorType& a = *static_cast(actor); + const PxU32 nbShapes = a.getNbShapes(); + for (PxU32 iShape = 0; iShape < nbShapes; iShape++) + { + NpShape* shape = a.getShapeManager().getShapes()[iShape]; + if (shape->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE) + { + const Scb::Shape& scbShape = shape->getScbShape(); + const Scb::Actor& scbActor = a.getScbActorFast(); + + (gComputeBoundsTable[treeStructure])(*bounds, scbShape, scbActor); + bounds++; + numShapes++; + } + } +} + +////////////////////////////////////////////////////////////////////////// +bool PruningStructure::build(PxRigidActor*const* actors, PxU32 nbActors) +{ + PX_ASSERT(actors); + PX_ASSERT(nbActors > 0); + + PxU32 numShapes[2] = { 0, 0 }; + // parse the actors first to get the shapes size + for (PxU32 actorsDone = 0; actorsDone < nbActors; actorsDone++) + { + if (actorsDone + 1 < nbActors) + Ps::prefetch(actors[actorsDone + 1], sizeof(NpRigidDynamic)); // worst case: PxRigidStatic is smaller + + PxType type = actors[actorsDone]->getConcreteType(); + const PxRigidActor& actor = *(actors[actorsDone]); + + Scb::ControlState::Enum cs = NpActor::getScbFromPxActor(actor).getControlState(); + if (!((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING)))) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PrunerStructure::build: Actor already assigned to a scene!"); + return false; + } + + const PxU32 nbShapes = actor.getNbShapes(); + bool hasQueryShape = false; + for (PxU32 iShape = 0; iShape < nbShapes; iShape++) + { + PxShape* shape; + actor.getShapes(&shape, 1, iShape); + if(shape->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE) + { + hasQueryShape = true; + if (type == PxConcreteType::eRIGID_STATIC) + numShapes[PruningIndex::eSTATIC]++; + else + numShapes[PruningIndex::eDYNAMIC]++; + } + } + + // each provided actor must have a query shape + if(!hasQueryShape) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PrunerStructure::build: Provided actor has no scene query shape!"); + return false; + } + + if (type == PxConcreteType::eRIGID_STATIC) + { + NpRigidStatic* rs = static_cast(actors[actorsDone]); + if(rs->getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PrunerStructure::build: Provided actor has already a pruning structure!"); + return false; + } + rs->getShapeManager().setPruningStructure(this); + } + else if (type == PxConcreteType::eRIGID_DYNAMIC) + { + NpRigidDynamic* rd = static_cast(actors[actorsDone]); + if (rd->getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PrunerStructure::build: Provided actor has already a pruning structure!"); + return false; + } + rd->getShapeManager().setPruningStructure(this); + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PrunerStructure::build: Provided actor is not a rigid actor!"); + return false; + } + } + + PxBounds3* bounds[2] = { NULL, NULL }; + + for (PxU32 i = 0; i < 2; i++) + { + if(numShapes[i]) + { + bounds[i] = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(numShapes[i] + 1), "Pruner bounds")); + } + } + + // now I go again and gather bounds and payload + numShapes[PruningIndex::eSTATIC] = 0; + numShapes[PruningIndex::eDYNAMIC] = 0; + for (PxU32 actorsDone = 0; actorsDone < nbActors; actorsDone++) + { + PxType type = actors[actorsDone]->getConcreteType(); + if (type == PxConcreteType::eRIGID_STATIC) + { + getShapeBounds(actors[actorsDone], false, + &bounds[PruningIndex::eSTATIC][numShapes[PruningIndex::eSTATIC]], numShapes[PruningIndex::eSTATIC]); + } + else if (type == PxConcreteType::eRIGID_DYNAMIC) + { + getShapeBounds(actors[actorsDone], true, + &bounds[PruningIndex::eDYNAMIC][numShapes[PruningIndex::eDYNAMIC]], numShapes[PruningIndex::eDYNAMIC]); + } + } + + AABBTree aabbTrees[2]; + for (PxU32 i = 0; i < 2; i++) + { + mNbObjects[i] = numShapes[i]; + if (numShapes[i]) + { + // create the AABB tree + AABBTreeBuildParams sTB; + sTB.mNbPrimitives = numShapes[i]; + sTB.mAABBArray = bounds[i]; + sTB.mLimit = NB_OBJECTS_PER_NODE; + bool status = aabbTrees[i].build(sTB); + + PX_UNUSED(status); + PX_ASSERT(status); + + // store the tree nodes + mNbNodes[i] = aabbTrees[i].getNbNodes(); + mAABBTreeNodes[i] = reinterpret_cast(PX_ALLOC(sizeof(AABBTreeRuntimeNode)*mNbNodes[i], "AABBTreeRuntimeNode")); + PxMemCopy(mAABBTreeNodes[i], aabbTrees[i].getNodes(), sizeof(AABBTreeRuntimeNode)*mNbNodes[i]); + mAABBTreeIndices[i] = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mNbObjects[i], "PxU32")); + PxMemCopy(mAABBTreeIndices[i], aabbTrees[i].getIndices(), sizeof(PxU32)*mNbObjects[i]); + + // discard the data + PX_FREE(bounds[i]); + } + } + + // store the actors for verification and serialization + mNbActors = nbActors; + mActors = reinterpret_cast(PX_ALLOC(sizeof(PxActor*)*mNbActors, "PxActor*")); + PxMemCopy(mActors, actors, sizeof(PxActor*)*mNbActors); + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +PruningStructure* PruningStructure::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PruningStructure* obj = new (address)PruningStructure(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PruningStructure); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +////////////////////////////////////////////////////////////////////////// + +void PruningStructure::resolveReferences(PxDeserializationContext& context) +{ + if (!isValid()) + return; + + for (PxU32 i = 0; i < mNbActors; i++) + { + context.translatePxBase(mActors[i]); + } +} + +////////////////////////////////////////////////////////////////////////// + +void PruningStructure::requiresObjects(PxProcessPxBaseCallback& c) +{ + if (!isValid()) + return; + + for (PxU32 i = 0; i < mNbActors; i++) + { + c.process(*mActors[i]); + } +} + +////////////////////////////////////////////////////////////////////////// + +void PruningStructure::exportExtraData(PxSerializationContext& stream) +{ + if (!isValid()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PrunerStructure::exportExtraData: Pruning structure is invalid!"); + return; + } + + for (PxU32 i = 0; i < 2; i++) + { + if (mAABBTreeNodes[i]) + { + // store nodes + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mAABBTreeNodes[i], mNbNodes[i] * sizeof(AABBTreeRuntimeNode)); + } + + if(mAABBTreeIndices[i]) + { + // store indices + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mAABBTreeIndices[i], mNbObjects[i] * sizeof(PxU32)); + } + } + + if(mActors) + { + // store actor pointers + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mActors, mNbActors * sizeof(PxActor*)); + } +} + +////////////////////////////////////////////////////////////////////////// + +void PruningStructure::importExtraData(PxDeserializationContext& context) +{ + if (!isValid()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PrunerStructure::importExtraData: Pruning structure is invalid!"); + return; + } + + for (PxU32 i = 0; i < 2; i++) + { + if (mAABBTreeNodes[i]) + { + mAABBTreeNodes[i] = context.readExtraData(mNbNodes[i]); + } + if(mAABBTreeIndices[i]) + { + mAABBTreeIndices[i] = context.readExtraData(mNbObjects[i]); + } + } + + if (mActors) + { + // read actor pointers + mActors = context.readExtraData(mNbActors); + } +} + +////////////////////////////////////////////////////////////////////////// + +PxU32 PruningStructure::getRigidActors(PxRigidActor** userBuffer, PxU32 bufferSize, PxU32 startIndex/* =0 */) const +{ + if(!isValid()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PrunerStructure::getRigidActors: Pruning structure is invalid!"); + return 0; + } + + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mActors, mNbActors); +} + +////////////////////////////////////////////////////////////////////////// + +void PruningStructure::invalidate(PxActor* actor) +{ + PX_ASSERT(actor); + + // remove actor from the actor list to avoid mem corruption + // this slow, but should be called only with error msg send to user about invalid behavior + for (PxU32 i = 0; i < mNbActors; i++) + { + if(mActors[i] == actor) + { + // set pruning structure to NULL and remove the actor from the list + PxType type = mActors[i]->getConcreteType(); + if (type == PxConcreteType::eRIGID_STATIC) + { + static_cast(mActors[i])->getShapeManager().setPruningStructure(NULL); + } + else if (type == PxConcreteType::eRIGID_DYNAMIC) + { + static_cast(mActors[i])->getShapeManager().setPruningStructure(NULL); + } + + mActors[i] = mActors[mNbActors--]; + break; + } + } + + mValid = false; +} + diff --git a/src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp b/src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp new file mode 100644 index 000000000..9449ac4c1 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp @@ -0,0 +1,604 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "SqSceneQueryManager.h" +#include "SqAABBPruner.h" +#include "SqIncrementalAABBPruner.h" +#include "SqBucketPruner.h" +#include "SqPrunerMergeData.h" +#include "SqBounds.h" +#include "NpBatchQuery.h" +#include "PxFiltering.h" +#include "NpRigidDynamic.h" +#include "NpRigidStatic.h" +#include "NpArticulationLink.h" +#include "CmTransformUtils.h" +#include "PsAllocator.h" +#include "PxSceneDesc.h" +#include "ScBodyCore.h" +#include "SqPruner.h" +#include "SqCompoundPruner.h" +#include "GuBounds.h" +#include "NpShape.h" +#include "common/PxProfileZone.h" + +using namespace physx; +using namespace Sq; +using namespace Sc; + +PrunerExt::PrunerExt() : + mPruner (NULL), + mDirtyList (PX_DEBUG_EXP("SQmDirtyList")), + mPrunerType (PxPruningStructureType::eLAST), + mTimestamp (0xffffffff) +{ +} + +PrunerExt::~PrunerExt() +{ + PX_DELETE_AND_RESET(mPruner); +} + +void PrunerExt::init(PxPruningStructureType::Enum type, PxU64 contextID, PxU32 ) +{ + if(0) // PT: to force testing the bucket pruner + { + mPrunerType = PxPruningStructureType::eNONE; + mTimestamp = 0; + mPruner = PX_NEW(BucketPruner); + return; + } + + mPrunerType = type; + mTimestamp = 0; + Pruner* pruner = NULL; + switch(type) + { + case PxPruningStructureType::eNONE: { pruner = PX_NEW(BucketPruner); break; } + case PxPruningStructureType::eDYNAMIC_AABB_TREE: { pruner = PX_NEW(AABBPruner)(true, contextID); break; } + case PxPruningStructureType::eSTATIC_AABB_TREE: { pruner = PX_NEW(AABBPruner)(false, contextID); break; } + case PxPruningStructureType::eLAST: break; + } + mPruner = pruner; +} + +void PrunerExt::preallocate(PxU32 nbShapes) +{ + if(nbShapes > mDirtyMap.size()) + mDirtyMap.resize(nbShapes); + + if(mPruner) + mPruner->preallocate(nbShapes); +} + +void PrunerExt::flushMemory() +{ + if(!mDirtyList.size()) + mDirtyList.reset(); + + // PT: TODO: flush bitmap here + + // PT: TODO: flush pruner here? +} + +void PrunerExt::flushShapes(PxU32 index) +{ + const PxU32 numDirtyList = mDirtyList.size(); + if(!numDirtyList) + return; + const PrunerHandle* const prunerHandles = mDirtyList.begin(); + + const ComputeBoundsFunc func = gComputeBoundsTable[index]; + + for(PxU32 i=0; igetPayload(handle, bounds); + (func)(*bounds, *(reinterpret_cast(pp.data[0])), *(reinterpret_cast(pp.data[1]))); //PAYLOAD + } + // PT: batch update happens after the loop instead of once per loop iteration + mPruner->updateObjectsAfterManualBoundsUpdates(prunerHandles, numDirtyList); + mTimestamp += numDirtyList; + mDirtyList.clear(); +} + +// PT: TODO: re-inline this +void PrunerExt::addToDirtyList(PrunerHandle handle) +{ + Cm::BitMap& dirtyMap = mDirtyMap; + if(!dirtyMap.test(handle)) + { + dirtyMap.set(handle); + mDirtyList.pushBack(handle); + mTimestamp++; + } +} + +// PT: TODO: re-inline this +Ps::IntBool PrunerExt::isDirty(PrunerHandle handle) const +{ + return mDirtyMap.test(handle); +} + +// PT: TODO: re-inline this +void PrunerExt::removeFromDirtyList(PrunerHandle handle) +{ + Cm::BitMap& dirtyMap = mDirtyMap; + if(dirtyMap.test(handle)) + { + dirtyMap.reset(handle); + mDirtyList.findAndReplaceWithLast(handle); + } +} + +// PT: TODO: re-inline this +void PrunerExt::growDirtyList(PrunerHandle handle) +{ + // pruners must either provide indices in order or reuse existing indices, so this 'if' is enough to ensure we have space for the new handle + // PT: TODO: fix this. There is just no need for any of it. The pruning pool itself could support the feature for free, similar to what we do + // in MBP. There would be no need for the bitmap or the dirty list array. However doing this through the virtual interface would be clumsy, + // adding the cost of virtual calls for very cheap & simple operations. It would be a lot easier to drop it and go back to what we had before. + + Cm::BitMap& dirtyMap = mDirtyMap; + if(dirtyMap.size() <= handle) + dirtyMap.resize(PxMax(dirtyMap.size() * 2, 1024)); + PX_ASSERT(handle mDirtyList.size()) + mDirtyList.reserve(nbShapes); +} + +void CompoundPrunerExt::flushMemory() +{ + if(!mDirtyList.size()) + mDirtyList.clear(); +} + +void CompoundPrunerExt::flushShapes() +{ + const PxU32 numDirtyList = mDirtyList.size(); + if(!numDirtyList) + return; + + const CompoundPair* const compoundPairs = mDirtyList.getEntries(); + + for(PxU32 i=0; igetPayload(handle, compoundId, bounds); + const Scb::Shape& scbShape = *reinterpret_cast(pp.data[0]); + + const PxTransform& shape2Actor = scbShape.getShape2Actor(); + Gu::computeBounds(*bounds, scbShape.getGeometry(), shape2Actor, 0.0f, NULL, SQ_PRUNER_INFLATION); + + // A.B. not very effective, we might do better here + mPruner->updateObjectAfterManualBoundsUpdates(compoundId, handle); + } + + mDirtyList.clear(); +} + +// PT: TODO: re-inline this +void CompoundPrunerExt::addToDirtyList(PrunerCompoundId compoundId, PrunerHandle handle) +{ + mDirtyList.insert(CompoundPair(compoundId, handle)); +} + +// PT: TODO: re-inline this +Ps::IntBool CompoundPrunerExt::isDirty(PrunerCompoundId compoundId, PrunerHandle handle) const +{ + return mDirtyList.contains(CompoundPair(compoundId, handle)); +} + +// PT: TODO: re-inline this +void CompoundPrunerExt::removeFromDirtyList(PrunerCompoundId compoundId, PrunerHandle handle) +{ + mDirtyList.erase(CompoundPair(compoundId, handle)); +} + +/////////////////////////////////////////////////////////////////////////////// + +SceneQueryManager::SceneQueryManager( Scb::Scene& scene, PxPruningStructureType::Enum staticStructure, + PxPruningStructureType::Enum dynamicStructure, PxU32 dynamicTreeRebuildRateHint, + const PxSceneLimits& limits) : + mScene (scene) +{ + mPrunerExt[PruningIndex::eSTATIC].init(staticStructure, scene.getContextId(), limits.maxNbStaticShapes ? limits.maxNbStaticShapes : 1024); + mPrunerExt[PruningIndex::eDYNAMIC].init(dynamicStructure, scene.getContextId(), limits.maxNbDynamicShapes ? limits.maxNbDynamicShapes : 1024); + + setDynamicTreeRebuildRateHint(dynamicTreeRebuildRateHint); + + preallocate(limits.maxNbStaticShapes, limits.maxNbDynamicShapes); + + mDynamicBoundsSync.mPruner = mPrunerExt[PruningIndex::eDYNAMIC].pruner(); + mDynamicBoundsSync.mTimestamp = &mPrunerExt[PruningIndex::eDYNAMIC].mTimestamp; + + mCompoundPrunerExt.mPruner = PX_NEW(BVHCompoundPruner); + mCompoundPrunerExt.preallocate(32); + + mPrunerNeedsUpdating = false; +} + +SceneQueryManager::~SceneQueryManager() +{ +} + +void SceneQueryManager::flushMemory() +{ + for(PxU32 i=0;iaddObjects(&handle, &b, &pp, 1, hasPrunerStructure); + + mPrunerExt[index].growDirtyList(handle); + } + else + { + PxBounds3 b; + const PxTransform& shape2Actor = scbShape.getShape2Actor(); + Gu::computeBounds(b, scbShape.getGeometry(), shape2Actor, 0.0f, NULL, SQ_PRUNER_INFLATION); + + PX_ASSERT(mCompoundPrunerExt.pruner()); + mCompoundPrunerExt.pruner()->addObject(compoundId, handle, b, pp); + } + + return createPrunerData(index, handle); +} + +const PrunerPayload& SceneQueryManager::getPayload(PrunerCompoundId compoundId, PrunerData data) const +{ + const PxU32 index = getPrunerIndex(data); + const PrunerHandle handle = getPrunerHandle(data); + + if(compoundId == INVALID_PRUNERHANDLE) + return mPrunerExt[index].pruner()->getPayload(handle); + else + return mCompoundPrunerExt.pruner()->getPayload(handle, compoundId); +} + +void SceneQueryManager::removePrunerShape(PrunerCompoundId compoundId, PrunerData data) +{ + mPrunerNeedsUpdating = true; + const PxU32 index = getPrunerIndex(data); + const PrunerHandle handle = getPrunerHandle(data); + + mPrunerExt[index].invalidateTimestamp(); + if(compoundId == INVALID_PRUNERHANDLE) + { + PX_ASSERT(mPrunerExt[index].pruner()); + + mPrunerExt[index].removeFromDirtyList(handle); + + mPrunerExt[index].pruner()->removeObjects(&handle, 1); + } + else + { + mCompoundPrunerExt.removeFromDirtyList(compoundId, handle); + mCompoundPrunerExt.pruner()->removeObject(compoundId, handle); + } +} + +void SceneQueryManager::setDynamicTreeRebuildRateHint(PxU32 rebuildRateHint) +{ + mRebuildRateHint = rebuildRateHint; + + for(PxU32 i=0;i(mPrunerExt[i].pruner())->setRebuildRateHint(rebuildRateHint); + } +} + +void SceneQueryManager::afterSync(PxSceneQueryUpdateMode::Enum updateMode) +{ + PX_PROFILE_ZONE("Sim.sceneQueryBuildStep", mScene.getContextId()); + + if(updateMode == PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED) + { + mPrunerNeedsUpdating = true; + return; + } + + // flush user modified objects + flushShapes(); + + bool commit = updateMode == PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED; + + for(PxU32 i = 0; i<2; i++) + { + if(mPrunerExt[i].pruner() && mPrunerExt[i].type() == PxPruningStructureType::eDYNAMIC_AABB_TREE) + static_cast(mPrunerExt[i].pruner())->buildStep(true); + + if(commit) + mPrunerExt[i].pruner()->commit(); + } + + mPrunerNeedsUpdating = !commit; +} + +void SceneQueryManager::flushShapes() +{ + PX_PROFILE_ZONE("SceneQuery.flushShapes", mScene.getContextId()); + + // must already have acquired writer lock here + + for(PxU32 i=0; icommit(); + + Ps::memoryBarrier(); + mPrunerNeedsUpdating = false; + } + mSceneQueryLock.unlock(); + } +} + +void SceneQueryManager::forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure) +{ + PX_PROFILE_ZONE("SceneQuery.forceDynamicTreeRebuild", mScene.getContextId()); + + const bool rebuild[PruningIndex::eCOUNT] = { rebuildStaticStructure, rebuildDynamicStructure }; + + Ps::Mutex::ScopedLock lock(mSceneQueryLock); + for(PxU32 i=0; i(mPrunerExt[i].pruner())->purge(); + static_cast(mPrunerExt[i].pruner())->commit(); + } + } +} + +void SceneQueryManager::sceneQueryBuildStep(PruningIndex::Enum index) +{ + PX_PROFILE_ZONE("SceneQuery.sceneQueryBuildStep", mScene.getContextId()); + + if (mPrunerExt[index].pruner() && mPrunerExt[index].type() == PxPruningStructureType::eDYNAMIC_AABB_TREE) + { + const bool buildFinished = static_cast(mPrunerExt[index].pruner())->buildStep(false); + if(buildFinished) + { + mPrunerNeedsUpdating = true; + } + } +} + +bool SceneQueryManager::prepareSceneQueriesUpdate(PruningIndex::Enum index) +{ + bool retVal = false; + if (mPrunerExt[index].pruner() && mPrunerExt[index].type() == PxPruningStructureType::eDYNAMIC_AABB_TREE) + { + retVal = static_cast(mPrunerExt[index].pruner())->prepareBuild(); + } + return retVal; +} + +void SceneQueryManager::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i=0; ishiftOrigin(shift); + + mCompoundPrunerExt.pruner()->shiftOrigin(shift); +} + +void DynamicBoundsSync::sync(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds, PxU32 count, const Cm::BitMap& dirtyShapeSimMap) +{ + if(!count) + return; + + PxU32 startIndex = 0; + PxU32 numIndices = count; + + // if shape sim map is not empty, parse the indices and skip update for the dirty one + if(dirtyShapeSimMap.count()) + { + numIndices = 0; + + for(PxU32 i=0; iupdateObjectsAndInflateBounds(handles + startIndex, indices + startIndex, bounds, numIndices); + numIndices = 0; + startIndex = i + 1; + } + else + numIndices++; + } + // PT: we fallback to the next line on purpose - no "else" + } + + mPruner->updateObjectsAndInflateBounds(handles + startIndex, indices + startIndex, bounds, numIndices); + + (*mTimestamp)++; +} + +void SceneQueryManager::addPruningStructure(const Sq::PruningStructure& pS) +{ + if(pS.getTreeNodes(PruningIndex::eSTATIC)) + { + AABBPrunerMergeData params(pS.getTreeNbNodes(PruningIndex::eSTATIC), pS.getTreeNodes(PruningIndex::eSTATIC), + pS.getNbObjects(PruningIndex::eSTATIC), pS.getTreeIndices(PruningIndex::eSTATIC)); + mPrunerExt[PruningIndex::eSTATIC].pruner()->merge(¶ms); + } + if(pS.getTreeNodes(PruningIndex::eDYNAMIC)) + { + AABBPrunerMergeData params(pS.getTreeNbNodes(PruningIndex::eDYNAMIC), pS.getTreeNodes(PruningIndex::eDYNAMIC), + pS.getNbObjects(PruningIndex::eDYNAMIC), pS.getTreeIndices(PruningIndex::eDYNAMIC)); + mPrunerExt[PruningIndex::eDYNAMIC].pruner()->merge(¶ms); + } +} + +void SceneQueryManager::addCompoundShape(const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& compoundTransform, PrunerData* prunerData, const Scb::Shape** scbShapes, const Scb::Actor& scbActor) +{ + PX_ASSERT(mCompoundPrunerExt.mPruner); + + const PxU32 nbShapes = bvhStructure.getNbBounds(); + + PX_ALLOCA(res, PrunerHandle, nbShapes); + PX_ALLOCA(payloads, PrunerPayload, nbShapes); + + for(PxU32 i = 0; i < nbShapes; i++) + { + payloads[i].data[0] = size_t(scbShapes[i]); //PAYLOAD + payloads[i].data[1] = size_t(&scbActor); //PAYLOAD + } + + CompoundFlag::Enum flags = (scbActor.getActorType() == PxActorType::eRIGID_DYNAMIC) ? CompoundFlag::DYNAMIC_COMPOUND : CompoundFlag::STATIC_COMPOUND; + mCompoundPrunerExt.mPruner->addCompound(res, bvhStructure, compoundId, compoundTransform, flags, payloads); + const PxU32 index = (flags & CompoundFlag::STATIC_COMPOUND) ? PxU32(0) : PxU32(1); + mPrunerExt[index].invalidateTimestamp(); + + for(PxU32 i = 0; i < nbShapes; i++) + { + prunerData[i] = createPrunerData(index, res[i]); + } +} + +void SceneQueryManager::updateCompoundActors(Sc::BodyCore*const* bodies, PxU32 numBodies) +{ + PX_ASSERT(mCompoundPrunerExt.mPruner); + for(PxU32 i = 0; i < numBodies; i++) + { + mCompoundPrunerExt.mPruner->updateCompound(bodies[i]->getRigidID(), bodies[i]->getBody2World()); + } + mPrunerExt[1].invalidateTimestamp(); +} + +void SceneQueryManager::updateCompoundActor(PrunerCompoundId compoundId, const PxTransform& compoundTransform, bool dynamic) +{ + mCompoundPrunerExt.mPruner->updateCompound(compoundId, compoundTransform); + mPrunerExt[dynamic].invalidateTimestamp(); +} + +void SceneQueryManager::removeCompoundActor(PrunerCompoundId compoundId, bool dynamic) +{ + PX_ASSERT(mCompoundPrunerExt.mPruner); + mCompoundPrunerExt.mPruner->removeCompound(compoundId); + mPrunerExt[dynamic].invalidateTimestamp(); +} + + diff --git a/src/PhysX/physx/source/scenequery/src/SqTypedef.h b/src/PhysX/physx/source/scenequery/src/SqTypedef.h new file mode 100644 index 000000000..0bcb5e0f1 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqTypedef.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_TYPEDEF_H +#define SQ_TYPEDEF_H + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Sq +{ + typedef PxU32 PoolIndex; + typedef PxU32 TreeNodeIndex; + + class AABBTree; +} +} + +#endif // SQ_TYPEDEF_H diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h new file mode 100644 index 000000000..3dc4f4101 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h @@ -0,0 +1,131 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_ACTOR_CORE +#define PX_COLLISION_ACTOR_CORE + +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PxMetaData.h" +#include "PxActor.h" + +namespace physx +{ + +class PxActor; + +namespace Sc +{ + + class Scene; + class ActorSim; + + class ActorCore : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + ActorCore(const PxEMPTY) : mSim(NULL), mActorFlags(PxEmpty) + { + } + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + ActorCore(PxActorType::Enum actorType, PxU8 actorFlags, + PxClientID owner, PxDominanceGroup dominanceGroup); + /*virtual*/ ~ActorCore(); + + PX_FORCE_INLINE ActorSim* getSim() const { return mSim; } + PX_FORCE_INLINE void setSim(ActorSim* sim) + { + PX_ASSERT((sim==NULL) ^ (mSim==NULL)); + mSim = sim; + } + + PX_FORCE_INLINE PxActorFlags getActorFlags() const { return mActorFlags; } + void setActorFlags(PxActorFlags af); + + PX_FORCE_INLINE PxDominanceGroup getDominanceGroup() const + { + return PxDominanceGroup(mDominanceGroup); + } + void setDominanceGroup(PxDominanceGroup g); + + PX_FORCE_INLINE void setOwnerClient(PxClientID inId) + { + const PxU32 aggid = mAggregateIDOwnerClient & 0x00ffffff; + mAggregateIDOwnerClient = (PxU32(inId)<<24) | aggid; + } + PX_FORCE_INLINE PxClientID getOwnerClient() const + { + return mAggregateIDOwnerClient>>24; + } + + PX_FORCE_INLINE PxActorType::Enum getActorCoreType() const { return PxActorType::Enum(mActorType); } + + void reinsertShapes(); + + PX_FORCE_INLINE void setAggregateID(PxU32 id) + { + PX_ASSERT(id==0xffffffff || id<(1<<24)); + const PxU32 ownerClient = mAggregateIDOwnerClient & 0xff000000; + mAggregateIDOwnerClient = (id & 0x00ffffff) | ownerClient; + } + PX_FORCE_INLINE PxU32 getAggregateID() const + { + const PxU32 id = mAggregateIDOwnerClient & 0x00ffffff; + return id == 0x00ffffff ? PX_INVALID_U32 : id; + } + private: + ActorSim* mSim; // + PxU32 mAggregateIDOwnerClient; // PxClientID (8bit) | aggregate ID (24bit) + // PT: TODO: the remaining members could be packed into just a 16bit mask + PxActorFlags mActorFlags; // PxActor's flags (PxU8) => only 4 bits used + PxU8 mActorType; // Actor type (8 bits, but 3 would be enough) + PxU8 mDominanceGroup; // Dominance group (8 bits, but 5 would be enough because "must be < 32") + }; + +#if PX_P64_FAMILY + PX_COMPILE_TIME_ASSERT(sizeof(Sc::ActorCore)==16); +#else + PX_COMPILE_TIME_ASSERT(sizeof(Sc::ActorCore)==12); +#endif + +} // namespace Sc + +} + +////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h new file mode 100644 index 000000000..5f02cfeb7 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h @@ -0,0 +1,227 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_ARTICULATION_CORE +#define PX_PHYSICS_SCP_ARTICULATION_CORE + +#include "ScActorCore.h" +#include "DyArticulation.h" +#include "DyFeatherstoneArticulation.h" + +namespace physx +{ + +class PxvArticulation; + +namespace IG +{ + class NodeIndex; +} + + +namespace Sc +{ + typedef Dy::FsData ArticulationDriveCache; + + class ArticulationSim; + class BodyCore; + class BodySim; + + class ArticulationCore + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + //--------------------------------------------------------------------------------- + // Construction, destruction & initialization + //--------------------------------------------------------------------------------- + +// PX_SERIALIZATION + public: + ArticulationCore(const PxEMPTY) : mSim(NULL) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + ArticulationCore(); + ~ArticulationCore(); + + //--------------------------------------------------------------------------------- + // External API + //--------------------------------------------------------------------------------- + PxU32 getInternalDriveIterations() const; + void setInternalDriveIterations(const PxU32 v); + + PxU32 getExternalDriveIterations() const; + void setExternalDriveIterations(const PxU32 v); + + PxU32 getMaxProjectionIterations() const; + void setMaxProjectionIterations(const PxU32 v); + + PxReal getSeparationTolerance() const; + void setSeparationTolerance(const PxReal v); + + PxReal getSleepThreshold() const; + void setSleepThreshold(const PxReal v); + + PxReal getFreezeThreshold() const; + void setFreezeThreshold(const PxReal v); + + PxReal getWakeCounter() const; + void setWakeCounter(const PxReal v); + void setWakeCounterInternal(const PxReal v); + + bool isSleeping() const; + void wakeUp(PxReal wakeCounter); + void putToSleep(); + + PxU16 getSolverIterationCounts() const; + void setSolverIterationCounts(PxU16 c); + + PxArticulation* getPxArticulation(); + const PxArticulation* getPxArticulation() const; + + + //--------------------------------------------------------------------------------- + // Drive Cache API + //--------------------------------------------------------------------------------- + ArticulationDriveCache* createDriveCache(PxReal compliance, + PxU32 driveIterations) const; + + void updateDriveCache(ArticulationDriveCache& cache, + PxReal compliance, + PxU32 driveIterations) const; + + void releaseDriveCache(ArticulationDriveCache& cache) const; + + PxU32 getCacheLinkCount(const ArticulationDriveCache& cache) const; + + void applyImpulse(BodyCore& link, + const ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque); + + void computeImpulseResponse(BodyCore& link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const; + + //--------------------------------------------------------------------------------- + // external reduced coordinate API + //--------------------------------------------------------------------------------- + void setArticulationFlags(PxArticulationFlags flags) { mCore.flags = flags; } + PxArticulationFlags getArticulationFlags() const { return mCore.flags; } + + PxU32 getDofs() const; + + PxArticulationCache* createCache() const; + + PxU32 getCacheDataSize() const; + + void zeroCache(PxArticulationCache& cache) const; + + void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag)const; + + void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const; + + void releaseCache(PxArticulationCache& cache) const; + + void packJointData(const PxReal* maximum, PxReal* reduced) const; + + void unpackJointData(const PxReal* reduced, PxReal* maximum) const; + + void commonInit() const; + + void computeGeneralizedGravityForce(PxArticulationCache& cache) const; + + void computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const; + + void computeGeneralizedExternalForce(PxArticulationCache& cache) const; + + void computeJointAcceleration(PxArticulationCache& cache) const; + + void computeJointForce(PxArticulationCache& cache) const; + + void computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const; + + void computeCoefficentMatrix(PxArticulationCache& cache) const; + + bool computeLambda(PxArticulationCache& cache, PxArticulationCache& rollBackCache, const PxReal* const jointTorque, const PxVec3 gravity, const PxU32 maxIter) const; + + void computeGeneralizedMassMatrix(PxArticulationCache& cache) const; + + PxU32 getCoefficentMatrixSize() const; + //--------------------------------------------------------------------------------- + // Internal API + //--------------------------------------------------------------------------------- + public: + PX_FORCE_INLINE void setSim(ArticulationSim* sim) + { + PX_ASSERT((sim==0) ^ (mSim == 0)); + mSim = sim; + } + PX_FORCE_INLINE ArticulationSim* getSim() const { return mSim; } + + PX_FORCE_INLINE const Dy::ArticulationCore& getCore() { return mCore; } + + static PX_FORCE_INLINE ArticulationCore& getArticulationCore(ArticulationCore& core) + { + size_t offset = PX_OFFSET_OF(ArticulationCore, mCore); + return *reinterpret_cast(reinterpret_cast(&core) - offset); + } + + PX_INLINE PxArticulationBase::Enum getArticulationType() const { return PxArticulationBase::Enum(mType); } + PX_INLINE void setArticulationType(PxArticulationBase::Enum type) { mType = type; } + + + IG::NodeIndex getIslandNodeIndex() const; + + void setGlobalPose(); + + void setDirty(const bool dirty); + + + private: + ArticulationSim* mSim; + Dy::ArticulationCore mCore; + PxU32 mType; + }; + + + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h new file mode 100644 index 000000000..fbb96b8d0 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h @@ -0,0 +1,226 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_ARTICULATION_JOINT_CORE +#define PX_PHYSICS_SCP_ARTICULATION_JOINT_CORE + +#include "foundation/PxTransform.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "DyArticulation.h" +#include "PxMetaData.h" + +namespace physx +{ +namespace Sc +{ + + class BodyCore; + class ArticulationJointSim; + class ArticulationCore; + + class ArticulationJointDesc + { + public: + BodyCore* parent; + BodyCore* child; + PxTransform parentPose; + PxTransform childPose; + }; + + class ArticulationJointCore : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + //--------------------------------------------------------------------------------- + // Construction, destruction & initialization + //--------------------------------------------------------------------------------- + public: +// PX_SERIALIZATION + ArticulationJointCore(const PxEMPTY) : mSim(NULL), mCore(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + ArticulationJointCore( const PxTransform& parentFrame, + const PxTransform& childFrame, + PxArticulationBase::Enum type); + + ~ArticulationJointCore(); + + //--------------------------------------------------------------------------------- + // External API + //--------------------------------------------------------------------------------- + + const PxTransform& getParentPose() const { return mCore.parentPose; } + void setParentPose(const PxTransform&); + + const PxTransform& getChildPose() const { return mCore.childPose; } + void setChildPose(const PxTransform&); + + const PxQuat& getTargetOrientation() const { return mCore.targetPosition; } + void setTargetOrientation(const PxQuat&); + + + const PxVec3& getTargetVelocity() const { return mCore.targetVelocity; } + void setTargetVelocity(const PxVec3&); + + PxReal getStiffness() const { return mCore.spring; } + void setStiffness(PxReal); + + PxReal getDamping() const { return mCore.damping; } + void setDamping(PxReal); + + PxReal getInternalCompliance() const { return mCore.internalCompliance; } + void setInternalCompliance(PxReal); + + PxReal getExternalCompliance() const { return mCore.externalCompliance; } + void setExternalCompliance(PxReal); + + void getSwingLimit(PxReal& yLimit, PxReal& zLimit) const { yLimit = mCore.limits[PxArticulationAxis::eSWING1].low; zLimit = mCore.limits[PxArticulationAxis::eSWING2].low; } + void setSwingLimit(PxReal yLimit, PxReal zLimit); + + PxReal getTangentialStiffness() const { return mCore.tangentialStiffness; } + void setTangentialStiffness(PxReal); + + PxReal getTangentialDamping() const { return mCore.tangentialDamping; } + void setTangentialDamping(PxReal); + + bool getSwingLimitEnabled() const { return mCore.swingLimited; } + void setSwingLimitEnabled(bool); + + PxReal getSwingLimitContactDistance() const { return mCore.swingLimitContactDistance; } + void setSwingLimitContactDistance(PxReal); + + void getTwistLimit(PxReal& lower, PxReal& upper) const { lower = mCore.limits[PxArticulationAxis::eTWIST].low; upper = mCore.limits[PxArticulationAxis::eTWIST].high; } + void setTwistLimit(PxReal lower, PxReal upper); + + void getLimit(PxArticulationAxis::Enum axis, PxReal& lower, PxReal& upper) const + { + lower = mCore.limits[axis].low; + upper = mCore.limits[axis].high; + } + + void setLimit(PxArticulationAxis::Enum axis, PxReal lower, PxReal upper) + { + mCore.limits[axis].low = lower; + mCore.limits[axis].high = upper; + } + + void getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration) const + { + stiffness = mCore.drives[axis].stiffness; + damping = mCore.drives[axis].damping; + maxForce = mCore.drives[axis].maxForce; + isAcceleration = mCore.drives[axis].isAcceleration; + } + + void setDrive(PxArticulationAxis::Enum axis, PxReal stiffness, PxReal damping, PxReal maxForce, bool isAcceleration) + { + mCore.drives[axis].stiffness = stiffness; + mCore.drives[axis].damping = damping; + mCore.drives[axis].maxForce = maxForce; + mCore.drives[axis].isAcceleration = isAcceleration; + } + + void setTargetP(PxArticulationAxis::Enum axis, PxReal targetP); + + PxReal getTargetP(PxArticulationAxis::Enum axis) const + { + return mCore.targetP[axis]; + } + + void setTargetV(PxArticulationAxis::Enum axis, PxReal targetV); + + PxReal getTargetV(PxArticulationAxis::Enum axis) const + { + return mCore.targetV[axis]; + } + + bool getTwistLimitEnabled() const { return mCore.twistLimited; } + void setTwistLimitEnabled(bool); + + PxReal getTwistLimitContactDistance() const { return mCore.twistLimitContactDistance; } + void setTwistLimitContactDistance(PxReal); + + void setDriveType(PxArticulationJointDriveType::Enum type); + PxArticulationJointDriveType::Enum + getDriveType() const { return PxArticulationJointDriveType::Enum(mCore.driveType); } + + void setJointType(PxArticulationJointType::Enum type); + PxArticulationJointType::Enum getJointType() const; + + + void setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion); + PxArticulationMotion::Enum + getMotion(PxArticulationAxis::Enum axis) const; + + void setFrictionCoefficient(const PxReal coefficient); + PxReal getFrictionCoefficient() const; + + void setMaxJointVelocity(const PxReal maxJointV); + PxReal getMaxJointVelocity() const; + //--------------------------------------------------------------------------------- + // Low Level data access - some wouldn't be needed if the interface wasn't virtual + //--------------------------------------------------------------------------------- + + PX_FORCE_INLINE ArticulationJointSim* getSim() const { return mSim; } + PX_FORCE_INLINE void setSim(ArticulationJointSim* sim) + { + PX_ASSERT((sim==0) ^ (mSim == 0)); + mSim = sim; + } + + PX_FORCE_INLINE Dy::ArticulationJointCore& getCore() { return mCore; } + + PX_FORCE_INLINE void setArticulation(ArticulationCore* articulation) + { + mArticulation = articulation; + } + + PX_FORCE_INLINE void setRoot(PxArticulationJointBase* base) { mRootType = base; } + PX_FORCE_INLINE PxArticulationJointBase* getRoot() const { return mRootType; } + + private: + ArticulationJointSim* mSim; + Dy::ArticulationJointCore mCore; + ArticulationCore* mArticulation; + PxArticulationJointBase* mRootType; + + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h new file mode 100644 index 000000000..f8592b67f --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h @@ -0,0 +1,213 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_BODYCORE +#define PX_PHYSICS_SCP_BODYCORE + +#include "foundation/PxTransform.h" +#include "ScRigidCore.h" +#include "PxRigidDynamic.h" +#include "PxvDynamics.h" +#include "PxvConfig.h" +#include "PsPool.h" + +namespace physx +{ +class PxRigidBodyDesc; + +namespace Sc +{ + class BodySim; + struct SimStateData; + + struct KinematicTransform + { + PxTransform targetPose; // The body will move to this pose over the superstep following this getting set. + PxU8 targetValid; // User set a kinematic target. + PxU8 pad[2]; + PxU8 type; + }; + + class BodyCore : public RigidCore + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + //--------------------------------------------------------------------------------- + // Construction, destruction & initialization + //--------------------------------------------------------------------------------- + public: +// PX_SERIALIZATION + BodyCore(const PxEMPTY) : RigidCore(PxEmpty), mCore(PxEmpty), mSimStateData(NULL) {} + static void getBinaryMetaData(PxOutputStream& stream); + void disableInternalCaching(bool disable); + size_t getSerialCore(PxsBodyCore& serialCore); +//~PX_SERIALIZATION + BodyCore(PxActorType::Enum type, const PxTransform& bodyPose); + /*virtual*/ ~BodyCore(); + + //--------------------------------------------------------------------------------- + // External API + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE const PxTransform& getBody2World() const { return mCore.body2World; } + void setBody2World(const PxTransform& p); + + PX_FORCE_INLINE const PxVec3& getLinearVelocity() const { return mCore.linearVelocity; } + void setLinearVelocity(const PxVec3& v); + + PX_FORCE_INLINE const PxVec3& getAngularVelocity() const { return mCore.angularVelocity; } + void setAngularVelocity(const PxVec3& v); + + PX_FORCE_INLINE void updateVelocities(const PxVec3& linearVelModPerStep, const PxVec3& angularVelModPerStep) + { + mCore.linearVelocity += linearVelModPerStep; + mCore.angularVelocity += angularVelModPerStep; + } + + PX_FORCE_INLINE const PxTransform& getBody2Actor() const { return mCore.getBody2Actor(); } + void setBody2Actor(const PxTransform& p); + + void addSpatialAcceleration(Ps::Pool* simStateDataPool, const PxVec3* linAcc, const PxVec3* angAcc); + void setSpatialAcceleration(Ps::Pool* simStateDataPool, const PxVec3* linAcc, const PxVec3* angAcc); + void clearSpatialAcceleration(bool force, bool torque); + void addSpatialVelocity(Ps::Pool* simStateDataPool, const PxVec3* linVelDelta, const PxVec3* angVelDelta); + void clearSpatialVelocity(bool force, bool torque); + + PX_FORCE_INLINE PxReal getMaxPenetrationBias() const { return mCore.maxPenBias; } + PX_FORCE_INLINE void setMaxPenetrationBias(PxReal p) { mCore.maxPenBias = p; } + + PxReal getInverseMass() const; + void setInverseMass(PxReal m); + const PxVec3& getInverseInertia() const; + void setInverseInertia(const PxVec3& i); + + PxReal getLinearDamping() const; + void setLinearDamping(PxReal d); + + PxReal getAngularDamping() const; + void setAngularDamping(PxReal d); + + PX_FORCE_INLINE PxRigidBodyFlags getFlags() const { return mCore.mFlags; } + void setFlags(Ps::Pool* simStateDataPool, PxRigidBodyFlags f); + + PX_FORCE_INLINE PxRigidDynamicLockFlags getRigidDynamicLockFlags() const { return mCore.lockFlags; } + + PX_FORCE_INLINE void setRigidDynamicLockFlags(PxRigidDynamicLockFlags flags) { mCore.lockFlags = flags; } + + PX_FORCE_INLINE PxReal getSleepThreshold() const { return mCore.sleepThreshold; } + void setSleepThreshold(PxReal t); + + PX_FORCE_INLINE PxReal getFreezeThreshold() const { return mCore.freezeThreshold; } + void setFreezeThreshold(PxReal t); + + PX_FORCE_INLINE PxReal getMaxContactImpulse() const { return mCore.maxContactImpulse; } + void setMaxContactImpulse(PxReal m); + + PxU32 getInternalIslandNodeIndex() const; + + PX_FORCE_INLINE PxReal getWakeCounter() const { return mCore.wakeCounter; } + void setWakeCounter(PxReal wakeCounter, bool forceWakeUp=false); + + bool isSleeping() const; + PX_FORCE_INLINE void wakeUp(PxReal wakeCounter) { setWakeCounter(wakeCounter, true); } + void putToSleep(); + + PxReal getMaxAngVelSq() const; + void setMaxAngVelSq(PxReal v); + + PxReal getMaxLinVelSq() const; + void setMaxLinVelSq(PxReal v); + + PX_FORCE_INLINE PxU16 getSolverIterationCounts() const { return mCore.solverIterationCounts; } + void setSolverIterationCounts(PxU16 c); + + bool getKinematicTarget(PxTransform& p) const; + bool getHasValidKinematicTarget() const; + void setKinematicTarget(Ps::Pool* simStateDataPool, const PxTransform& p, PxReal wakeCounter); + void invalidateKinematicTarget(); + + + PX_FORCE_INLINE PxReal getContactReportThreshold() const { return mCore.contactReportThreshold; } + void setContactReportThreshold(PxReal t) { mCore.contactReportThreshold = t; } + + void onOriginShift(const PxVec3& shift); + + //--------------------------------------------------------------------------------- + // Internal API + //--------------------------------------------------------------------------------- + + PX_FORCE_INLINE void setLinearVelocityInternal(const PxVec3& v) { mCore.linearVelocity = v; } + PX_FORCE_INLINE void setAngularVelocityInternal(const PxVec3& v) { mCore.angularVelocity = v; } + PX_FORCE_INLINE void setWakeCounterFromSim(PxReal c) { mCore.wakeCounter = c; } + + BodySim* getSim() const; + + PX_FORCE_INLINE PxsBodyCore& getCore() { return mCore; } + PX_FORCE_INLINE const PxsBodyCore& getCore() const { return mCore; } + + PX_FORCE_INLINE PxReal getCCDAdvanceCoefficient() const { return mCore.ccdAdvanceCoefficient; } + PX_FORCE_INLINE void setCCDAdvanceCoefficient(PxReal c) { mCore.ccdAdvanceCoefficient = c; } + + bool setupSimStateData(Ps::Pool* simStateDataPool, const bool isKinematic, const bool targetValid = false); + void tearDownSimStateData(Ps::Pool* simStateDataPool, const bool isKinematic); + + bool checkSimStateKinematicStatus(bool) const; + + Ps::IntBool isFrozen() const; + + PX_FORCE_INLINE const SimStateData* getSimStateData(bool isKinematic) const { return (mSimStateData && (checkSimStateKinematicStatus(isKinematic)) ? mSimStateData : NULL); } + PX_FORCE_INLINE SimStateData* getSimStateData(bool isKinematic) { return (mSimStateData && (checkSimStateKinematicStatus(isKinematic)) ? mSimStateData : NULL); } + + PX_FORCE_INLINE SimStateData* getSimStateData_Unchecked() const { return mSimStateData; } + + static PX_FORCE_INLINE BodyCore& getCore(PxsBodyCore& core) + { + size_t offset = PX_OFFSET_OF_RT(BodyCore, mCore); + return *reinterpret_cast(reinterpret_cast(&core) - offset); + } + + private: + void backup(SimStateData&); + void restore(); + + PX_ALIGN_PREFIX(16) PxsBodyCore mCore PX_ALIGN_SUFFIX(16); + SimStateData* mSimStateData; + }; + + PxActor* getPxActorFromBodyCore(Sc::BodyCore* bodyCore, PxActorType::Enum& type); + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h new file mode 100644 index 000000000..5520b6497 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h @@ -0,0 +1,134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CONSTRAINTCORE +#define PX_PHYSICS_CONSTRAINTCORE + +#include "CmPhysXCommon.h" +#include "PxConstraintDesc.h" +#include "PsAllocator.h" +#include "PxConstraint.h" + +namespace physx +{ + +class PxConstraint; + +namespace Sc +{ + class ConstraintCore; + class ConstraintSim; + class RigidCore; + + + class ConstraintCore : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + ConstraintCore(const PxEMPTY) : mFlags(PxEmpty), mConnector(NULL), mSim(NULL) {} + PX_FORCE_INLINE void setConstraintFunctions(PxConstraintConnector& n, + const PxConstraintShaderTable& shaders) + { + mConnector = &n; + mSolverPrep = shaders.solverPrep; + mProject = shaders.project; + mVisualize = shaders.visualize; + } + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + ConstraintCore(PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize); + ~ConstraintCore(); + + // The two-step protocol here allows us to unlink the constraint prior to deleting + // the actors when synchronizing the scene, then set the bodies after new actors have been inserted + + void prepareForSetBodies(); + void setBodies(RigidCore* r0v, RigidCore* r1v); + + PxConstraint* getPxConstraint(); + const PxConstraint* getPxConstraint() const; + PX_FORCE_INLINE PxConstraintConnector* getPxConnector() const { return mConnector; } + + PX_FORCE_INLINE PxConstraintFlags getFlags() const { return mFlags; } + void setFlags(PxConstraintFlags flags); + + void getForce(PxVec3& force, PxVec3& torque) const; + + bool updateConstants(void* addr); + + void setBreakForce(PxReal linear, PxReal angular); + void getBreakForce(PxReal& linear, PxReal& angular) const; + + void setMinResponseThreshold(PxReal threshold); + PxReal getMinResponseThreshold() const { return mMinResponseThreshold; } + + void breakApart(); + + PX_FORCE_INLINE PxConstraintVisualize getVisualize() const { return mVisualize; } + PX_FORCE_INLINE PxConstraintProject getProject() const { return mProject; } + PX_FORCE_INLINE PxConstraintSolverPrep getSolverPrep() const { return mSolverPrep; } + PX_FORCE_INLINE PxU32 getConstantBlockSize() const { return mDataSize; } + + PX_FORCE_INLINE void setSim(ConstraintSim* sim) + { + PX_ASSERT((sim==0) ^ (mSim == 0)); + mSim = sim; + } + PX_FORCE_INLINE ConstraintSim* getSim() const { return mSim; } + private: + PxConstraintFlags mFlags; + PxU16 mPaddingFromFlags; // PT: because flags are PxU16 + + PxVec3 mAppliedForce; + PxVec3 mAppliedTorque; + + PxConstraintConnector* mConnector; + PxConstraintProject mProject; + PxConstraintSolverPrep mSolverPrep; + PxConstraintVisualize mVisualize; + PxU32 mDataSize; + PxReal mLinearBreakForce; + PxReal mAngularBreakForce; + PxReal mMinResponseThreshold; + + ConstraintSim* mSim; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScIterators.h b/src/PhysX/physx/source/simulationcontroller/include/ScIterators.h new file mode 100644 index 000000000..30905476f --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScIterators.h @@ -0,0 +1,104 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_ITERATOR +#define PX_PHYSICS_SCP_ITERATOR + +#include "foundation/PxVec3.h" +#include "PxContact.h" + +namespace physx +{ +class PxShape; +class PxsContactManagerOutputIterator; + +namespace Sc +{ + class ShapeSim; + class Interaction; + + struct Contact + { + Contact() + : normal(0.0f) + , point(0.0f) + , separation(0.0f) + , normalForce(0.0f) + {} + + PxVec3 normal; + PxVec3 point; + PxShape* shape0; + PxShape* shape1; + PxReal separation; + PxReal normalForce; + PxU32 faceIndex0; // these are the external indices + PxU32 faceIndex1; + bool normalForceAvailable; + }; + + class ContactIterator + { + public: + + class Pair + { + public: + Pair() : mIter(NULL, NULL, NULL, 0, 0) {} + Pair(const void*& contactPatches, const void*& contactPoints, const PxU32 /*contactDataSize*/, const PxReal*& forces, PxU32 numContacts, PxU32 numPatches, ShapeSim& shape0, ShapeSim& shape1); + Contact* getNextContact(); + + private: + PxU32 mIndex; + PxU32 mNumContacts; + PxContactStreamIterator mIter; + const PxReal* mForces; + Contact mCurrentContact; + }; + + ContactIterator() {} + explicit ContactIterator(Interaction** first, Interaction** last, PxsContactManagerOutputIterator& outputs): mCurrent(first), mLast(last), mOffset(0), mOutputs(&outputs) {} + Pair* getNextPair(); + + private: + Interaction** mCurrent; + Interaction** mLast; + Pair mCurrentPair; + PxU32 mOffset; + PxsContactManagerOutputIterator* mOutputs; + + private: + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h new file mode 100644 index 000000000..16d033448 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_MATERIAL_CORE +#define PX_PHYSICS_SCP_MATERIAL_CORE + +#include "foundation/PxVec3.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PxMaterial.h" +#include "PxsMaterialCore.h" + +namespace physx +{ +class PxMaterial; + +namespace Sc +{ +typedef PxsMaterialData MaterialData; + +class MaterialCore : public PxsMaterialCore +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + MaterialCore(const MaterialData& desc) : PxsMaterialCore(desc) {} + MaterialCore(const PxEMPTY) : PxsMaterialCore(PxEmpty) {} + MaterialCore() {} + ~MaterialCore() {} + static void getBinaryMetaData(PxOutputStream& stream); + + PX_FORCE_INLINE void save(MaterialData& data) const { data = *this; } + PX_FORCE_INLINE void load(const MaterialData& data) { static_cast(*this) = data; } // To make synchronization between master material and scene material table less painful +}; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h b/src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h new file mode 100644 index 000000000..465f107fd --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h @@ -0,0 +1,115 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SC_PHYSICS +#define PX_PHYSICS_SC_PHYSICS + +#include "PxPhysics.h" +#include "PxScene.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsBasicTemplates.h" +#include "PxActor.h" + +namespace physx +{ + +class PxMaterial; +class PxTolerancesScale; +struct PxvOffsetTable; + +#if PX_SUPPORT_GPU_PHYSX +class PxPhysXGpu; +#endif + +namespace Sc +{ + class Scene; + class StaticCore; + class RigidCore; + class BodyCore; + class ArticulationCore; + class ConstraintCore; + class ShapeCore; + + struct OffsetTable + { + PX_FORCE_INLINE OffsetTable() {} + + PX_FORCE_INLINE PxActor* convertScRigidStatic2PxActor(StaticCore* sc) const { return Ps::pointerOffset(sc, scRigidStatic2PxActor); } + PX_FORCE_INLINE PxActor* convertScRigidDynamic2PxActor(BodyCore* sc) const { return Ps::pointerOffset(sc, scRigidDynamic2PxActor); } + PX_FORCE_INLINE PxActor* convertScArticulationLink2PxActor(BodyCore* sc) const { return Ps::pointerOffset(sc, scArticulationLink2PxActor); } + + PX_FORCE_INLINE PxShape* convertScShape2Px(ShapeCore* sc) const { return Ps::pointerOffset(sc, scShape2Px); } + PX_FORCE_INLINE const PxShape* convertScShape2Px(const ShapeCore* sc) const { return Ps::pointerOffset(sc, scShape2Px); } + + PX_FORCE_INLINE PxArticulation* convertScArticulation2Px(ArticulationCore* sc) const { return Ps::pointerOffset(sc, scArticulation2Px); } + PX_FORCE_INLINE const PxArticulation* convertScArticulation2Px(const ArticulationCore* sc) const { return Ps::pointerOffset(sc, scArticulation2Px); } + + PX_FORCE_INLINE PxConstraint* convertScConstraint2Px(ConstraintCore* sc) const { return Ps::pointerOffset(sc, scConstraint2Px); } + PX_FORCE_INLINE const PxConstraint* convertScConstraint2Px(const ConstraintCore* sc) const { return Ps::pointerOffset(sc, scConstraint2Px); } + + ptrdiff_t scRigidStatic2PxActor; + ptrdiff_t scRigidDynamic2PxActor; + ptrdiff_t scArticulationLink2PxActor; + ptrdiff_t scShape2Px; + ptrdiff_t scArticulation2Px; + ptrdiff_t scSoftBody2Px; + ptrdiff_t scConstraint2Px; + + ptrdiff_t scCore2PxActor[PxActorType::eACTOR_COUNT]; + }; + extern OffsetTable gOffsetTable; + + class Physics : public Ps::UserAllocated + { + public: + PX_FORCE_INLINE static Physics& getInstance() { return *mInstance; } + + Physics(const PxTolerancesScale&, const PxvOffsetTable& pxvOffsetTable); + ~Physics(); // use release() instead + public: + void release(); + + PX_FORCE_INLINE const PxTolerancesScale& getTolerancesScale() const { return mScale; } + + private: + PxTolerancesScale mScale; + static Physics* mInstance; + + public: + static const PxReal sWakeCounterOnCreation; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h new file mode 100644 index 000000000..732b9f97b --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h @@ -0,0 +1,94 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_RB_CORE +#define PX_PHYSICS_SCP_RB_CORE + +#include "ScActorCore.h" +#include "PxvDynamics.h" +#include "PxShape.h" + +namespace physx +{ + +namespace Sc +{ + + class RigidSim; + + struct ShapeChangeNotifyFlag + { + enum Enum + { + eGEOMETRY = 1<<0, + eMATERIAL = 1<<1, + eSHAPE2BODY = 1<<2, + eFILTERDATA = 1<<3, + eCONTACTOFFSET = 1<<4, + eRESTOFFSET = 1<<5, + eFLAGS = 1<<6, + eRESET_FILTERING = 1<<7 + + }; + }; + typedef PxFlags ShapeChangeNotifyFlags; + PX_FLAGS_OPERATORS(ShapeChangeNotifyFlag::Enum,PxU32) + + + class ShapeCore; + + class RigidCore : public ActorCore + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + PxActor* getPxActor() const; + void addShapeToScene(ShapeCore& shape); + void removeShapeFromScene(ShapeCore& shape, bool wakeOnLostTouch); + void onShapeChange(ShapeCore& shape, ShapeChangeNotifyFlags notifyFlags, PxShapeFlags newShapeFlags = PxShapeFlags(), bool forceBoundsUpdate = false); + + RigidSim* getSim() const; + PxU32 getRigidID() const; + static void getBinaryMetaData(PxOutputStream& stream); + protected: + RigidCore(const PxEMPTY) : ActorCore(PxEmpty) {} + RigidCore(PxActorType::Enum type); + ~RigidCore(); + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScScene.h b/src/PhysX/physx/source/simulationcontroller/include/ScScene.h new file mode 100644 index 000000000..912b2c897 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScScene.h @@ -0,0 +1,993 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_SCENE +#define PX_PHYSICS_SCP_SCENE + +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" +#include "PxScene.h" +#include "PxSceneDesc.h" +#include "PxSimulationEventCallback.h" +#include "PsPool.h" +#include "PsHashSet.h" +#include "CmRenderOutput.h" +#include "CmTask.h" +#include "CmFlushPool.h" +#include "CmPreallocatingPool.h" +#include "CmBitMap.h" +#include "ScIterators.h" +#include "PxsMaterialManager.h" +#include "PxvManager.h" +#include "ScBodyCore.h" +#include "PxArticulationBase.h" + +#define PX_MAX_DOMINANCE_GROUP 32 + +class OverlapFilterTask; + +namespace physx +{ + +// PT: TODO: move INVALID_FILTER_PAIR_INDEX out of public API +struct PxFilterInfo +{ + PX_FORCE_INLINE PxFilterInfo() : filterFlags(0), pairFlags(0), filterPairIndex(INVALID_FILTER_PAIR_INDEX) {} + PX_FORCE_INLINE PxFilterInfo(PxFilterFlags filterFlags_) : filterFlags(filterFlags_), pairFlags(0), filterPairIndex(INVALID_FILTER_PAIR_INDEX) {} + + PxFilterFlags filterFlags; + PxPairFlags pairFlags; + PxU32 filterPairIndex; +}; + +struct PxTriggerPair; + +class PxsIslandManager; +class PxsSimulationController; +class PxsSimulationControllerCallback; +class PxsMemoryManager; + +#if PX_SUPPORT_GPU_PHYSX +class PxsKernelWranglerManager; +class PxsHeapMemoryAllocatorManager; +#endif + +namespace IG +{ + class SimpleIslandManager; +} + +class PxsCCDContext; + +namespace Cm +{ + class DeferredIDPool; + class IDPool; +} + +namespace Bp +{ + class AABBManager; + class BroadPhase; + class BoundsArray; +} + +namespace Sq +{ + typedef PxU32 PrunerHandle; // PT: we should get this from SqPruner.h but it cannot be included from here +} + +namespace Dy +{ + class ArticulationV; + class Context; +} + +namespace Pt +{ + class Context; +} + +namespace Sc +{ + class ActorSim; + class ElementSim; + class Interaction; + + class ShapeCore; + class RigidCore; + class StaticCore; + class ConstraintCore; + class MaterialCore; + class ArticulationCore; + class ArticulationJointCore; + class LLArticulationPool; + class LLArticulationRCPool; + class BodyCore; + + class NPhaseCore; + class LowLevelThreadingThunk; + class Client; + class ConstraintInteraction; + + class BodySim; + class ShapeSim; + class RigidSim; + class StaticSim; + class ConstraintSim; + struct ConstraintGroupNode; + class ConstraintProjectionManager; + struct TriggerPairExtraData; + class ObjectIDTracker; + class ActorPairReport; + class ContactStreamManager; + class SqBoundsManager; + class ShapeInteraction; + class ElementInteractionMarker; + class ArticulationSim; + + class SimStats; + + struct SimStateData; + + struct BatchInsertionState + { + BodySim* bodySim; + StaticSim*staticSim; + ShapeSim* shapeSim; + ptrdiff_t staticActorOffset; + ptrdiff_t staticShapeTableOffset; + ptrdiff_t dynamicActorOffset; + ptrdiff_t dynamicShapeTableOffset; + ptrdiff_t shapeOffset; + }; + + struct BatchRemoveState + { + Ps::InlineArray bufferedShapes; + Ps::InlineArray removedShapes; + }; + + struct InteractionType + { + enum Enum + { + eOVERLAP = 0, // corresponds to ShapeInteraction + eTRIGGER, // corresponds to TriggerInteraction + eMARKER, // corresponds to ElementInteractionMarker + eTRACKED_IN_SCENE_COUNT, // not a real type, interactions above this limit are tracked in the scene + eCONSTRAINTSHADER, // corresponds to ConstraintInteraction + eARTICULATION, // corresponds to ArticulationJointSim + + eINVALID + }; + }; + + struct SceneInternalFlag + { + enum Enum + { + eSCENE_SIP_STATES_DIRTY_DOMINANCE = (1<<1), + eSCENE_SIP_STATES_DIRTY_VISUALIZATION = (1<<2), + eSCENE_DEFAULT = 0 + }; + }; + + struct SimulationStage + { + enum Enum + { + eCOMPLETE, + eCOLLIDE, + eFETCHCOLLIDE, + eADVANCE, + eFETCHRESULT + }; + }; + + // PT: TODO: revisit the need for a virtual interface + struct SqBoundsSync + { + virtual void sync(const Sq::PrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds, PxU32 count, const Cm::BitMap& dirtyShapeSimMap) = 0; + + virtual ~SqBoundsSync() {} + }; + + // PT: TODO: revisit the need for a virtual interface + struct SqRefFinder + { + virtual Sq::PrunerHandle find(const PxRigidBody* body, const PxShape* shape) = 0; + + virtual ~SqRefFinder() {} + }; + + class Scene : public Ps::UserAllocated + { + struct SimpleBodyPair + { + BodySim* body1; + BodySim* body2; + PxU32 body1ID; + PxU32 body2ID; + }; + + PX_NOCOPY(Scene) + + //--------------------------------------------------------------------------------- + // External interface + //--------------------------------------------------------------------------------- + public: + + void release(); + + PX_FORCE_INLINE void setGravity(const PxVec3& g) { mGravity = g; mBodyGravityDirty = true; } + PX_FORCE_INLINE PxVec3 getGravity() const { return mGravity; } + PX_FORCE_INLINE void setElapsedTime(const PxReal t) { mDt = t; mOneOverDt = t > 0.0f ? 1.0f/t : 0.0f; } + + void setBounceThresholdVelocity(const PxReal t); + PxReal getBounceThresholdVelocity() const; + + PX_FORCE_INLINE void setPublicFlags(PxSceneFlags flags) { mPublicFlags = flags; } + PX_FORCE_INLINE PxSceneFlags getPublicFlags() const { return mPublicFlags; } + + void setFrictionType(PxFrictionType::Enum model); + PxFrictionType::Enum getFrictionType() const; + void setPCM(bool enabled); + void setContactCache(bool enabled); + + void addStatic(StaticCore&, void*const *shapes, PxU32 nbShapes, size_t shapePtrOffset, PxBounds3* uninflatedBounds); + void removeStatic(StaticCore&, Ps::InlineArray& removedShapes, bool wakeOnLostTouch); + + void addBody(BodyCore&, void*const *shapes, PxU32 nbShapes, size_t shapePtrOffset, PxBounds3* uninflatedBounds, bool compound); + void removeBody(BodyCore&, Ps::InlineArray& removedShapes, bool wakeOnLostTouch); + + // Batch insertion API. + // the bounds generated here are the uninflated bounds for the shapes, *if* they are trigger or sim shapes. + // It's up to the caller to ensure the bounds array is big enough. + // Some care is required in handling these since sim and SQ tweak the bounds in slightly different ways. + + void startBatchInsertion(BatchInsertionState&); + void addStatic(PxActor* actor, BatchInsertionState&, PxBounds3* outBounds); + void addBody(PxActor* actor, BatchInsertionState&, PxBounds3* outBounds, bool compound); + void finishBatchInsertion(BatchInsertionState&); + + // Batch remove helpers + PX_FORCE_INLINE void setBatchRemove(BatchRemoveState* bs) { mBatchRemoveState = bs; } + PX_FORCE_INLINE BatchRemoveState* getBatchRemove() const { return mBatchRemoveState; } + void prefetchForRemove(const BodyCore& ) const; + void prefetchForRemove(const StaticCore& ) const; + + void addConstraint(ConstraintCore&, RigidCore*, RigidCore*); + void removeConstraint(ConstraintCore&); + + void addArticulation(ArticulationCore&, BodyCore& root); + void removeArticulation(ArticulationCore&); + + void addArticulationJoint(ArticulationJointCore&, BodyCore& parent, BodyCore& child); + void removeArticulationJoint(ArticulationJointCore&); + + void addArticulationSimControl(Sc::ArticulationCore& core); + + PxU32 getNbArticulations() const; + Sc::ArticulationCore* const* getArticulations(); + + PxU32 getNbConstraints() const; + Sc::ConstraintCore*const * getConstraints(); + + void initContactsIterator(ContactIterator&, PxsContactManagerOutputIterator&); + + // Simulation events + void setSimulationEventCallback(PxSimulationEventCallback* callback); + PxSimulationEventCallback* getSimulationEventCallback() const; + + // Contact modification + void setContactModifyCallback(PxContactModifyCallback* callback); + PxContactModifyCallback* getContactModifyCallback() const; + void setCCDContactModifyCallback(PxCCDContactModifyCallback* callback); + PxCCDContactModifyCallback* getCCDContactModifyCallback() const; + + void setCCDMaxPasses(PxU32 ccdMaxPasses); + PxU32 getCCDMaxPasses() const; + + // Broad-phase callback + void setBroadPhaseCallback(PxBroadPhaseCallback* callback); + PxBroadPhaseCallback* getBroadPhaseCallback() const; + + // Broad-phase management + void finishBroadPhase(PxBaseTask* continuation); + void finishBroadPhaseStage2(const PxU32 ccdPass); + void preallocateContactManagers(PxBaseTask* continuation); + + void islandInsertion(PxBaseTask* continuation); + void registerContactManagers(PxBaseTask* continuation); + void registerInteractions(PxBaseTask* continuation); + void registerSceneInteractions(PxBaseTask* continuation); + + void secondPassNarrowPhase(PxBaseTask* continuation); + + // Groups + void setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance); + PxDominanceGroupPair getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const; + + void setSolverBatchSize(PxU32 solverBatchSize); + PxU32 getSolverBatchSize() const; + + void setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value); + PxReal getVisualizationParameter(PxVisualizationParameter::Enum param) const; + + void setVisualizationCullingBox(const PxBounds3& box); + const PxBounds3& getVisualizationCullingBox() const; + + // Run + void simulate(PxReal timeStep, PxBaseTask* continuation); + void advance(PxReal timeStep, PxBaseTask* continuation); + void collide(PxReal timeStep, PxBaseTask* continuation); + void endSimulation(); + void flush(bool sendPendingReports); + void fireBrokenConstraintCallbacks(); + void fireTriggerCallbacks(); + void fireQueuedContactCallbacks(bool asPartOfFlush); + void fireOnAdvanceCallback(); + + const Ps::Array& + getQueuedContactPairHeaders(); + + bool fireOutOfBoundsCallbacks(); + void prepareOutOfBoundsCallbacks(); + void postCallbacksPreSync(); + void postReportsCleanup(); + void fireCallbacksPostSync(); + void syncSceneQueryBounds(SqBoundsSync& sync, SqRefFinder& finder); + + PxU32 getDefaultContactReportStreamBufferSize() const; + + PxReal getFrictionOffsetThreshold() const; + + PX_FORCE_INLINE void setLimits(const PxSceneLimits& limits) { mLimits = limits; } + PX_FORCE_INLINE const PxSceneLimits& getLimits() const { return mLimits; } + + void visualizeStartStep(); + void visualizeEndStep(); + Cm::RenderBuffer& getRenderBuffer(); + + void setNbContactDataBlocks(PxU32 blockCount); + PxU32 getNbContactDataBlocksUsed() const; + PxU32 getMaxNbContactDataBlocksUsed() const; + PxU32 getMaxNbConstraintDataBlocksUsed() const; + + void setScratchBlock(void* addr, PxU32 size); + +// PX_ENABLE_SIM_STATS + void getStats(PxSimulationStatistics& stats) const; + PX_FORCE_INLINE SimStats& getStatsInternal() { return *mStats; } +// PX_ENABLE_SIM_STATS + + void buildActiveActors(); + void buildActiveAndFrozenActors(); + PxActor** getActiveActors(PxU32& nbActorsOut); + void setActiveActors(PxActor** actors, PxU32 nbActors); + + PxActor** getFrozenActors(PxU32& nbActorsOut); + + void finalizeContactStreamAndCreateHeader(PxContactPairHeader& header, + const ActorPairReport& aPair, + ContactStreamManager& cs, PxU32 removedShapeTestMask); + + PxClientID createClient(); + PX_FORCE_INLINE PxU32 getNbClients() const { return mClients.size(); } + + PxTaskManager* getTaskManagerPtr() const { return mTaskManager; } + + void shiftOrigin(const PxVec3& shift); + + PX_FORCE_INLINE Ps::Pool* getSimStateDataPool() { return mSimStateDataPool; } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + //internal public methods: + public: + Scene(const PxSceneDesc& desc, PxU64 contextID); + ~Scene() {} //use release() plz. + + void preAllocate(PxU32 nbStatics, PxU32 nbBodies, PxU32 nbStaticShapes, PxU32 nbDynamicShapes); + + PX_FORCE_INLINE PxsContext* getLowLevelContext() { return mLLContext; } + PX_FORCE_INLINE const PxsContext* getLowLevelContext() const { return mLLContext; } + + PX_FORCE_INLINE Dy::Context* getDynamicsContext() { return mDynamicsContext; } + PX_FORCE_INLINE const Dy::Context* getDynamicsContext() const { return mDynamicsContext; } + + PX_FORCE_INLINE PxsSimulationController* getSimulationController() { return mSimulationController; } + PX_FORCE_INLINE const PxsSimulationController* getSimulationController() const { return mSimulationController; } + + PX_FORCE_INLINE Bp::AABBManager* getAABBManager() { return mAABBManager; } + PX_FORCE_INLINE const Bp::AABBManager* getAABBManager() const { return mAABBManager; } + PX_FORCE_INLINE Ps::Array& getCcdBodies() { return mCcdBodies; } + + /*PX_FORCE_INLINE PxsIslandManager* getIslandManager() { return mIslandManager; } + PX_FORCE_INLINE const PxsIslandManager* getIslandManager() const { return mIslandManager; }*/ + + PX_FORCE_INLINE IG::SimpleIslandManager* getSimpleIslandManager() { return mSimpleIslandManager; } + PX_FORCE_INLINE const IG::SimpleIslandManager* getSimpleIslandManager() const { return mSimpleIslandManager; } + + PX_FORCE_INLINE Sc::SimulationStage::Enum getSimulationStage() const { return mSimulationStage; } + PX_FORCE_INLINE void setSimulationStage(Sc::SimulationStage::Enum stage) { mSimulationStage = stage; } + + void addShape(RigidSim&, ShapeCore&, PxBounds3* uninflatedBounds); + void removeShape(ShapeSim&, bool wakeOnLostTouch); + + void registerShapeInNphase(const ShapeCore& shapeCore); + void unregisterShapeFromNphase(const ShapeCore& shapeCore); + + void notifyNphaseOnUpdateShapeMaterial(const ShapeCore& shapeCore); + + // Get an array of the active actors. + PX_FORCE_INLINE BodyCore*const* getActiveBodiesArray() const { return mActiveBodies.begin(); } + PX_FORCE_INLINE PxU32 getNumActiveBodies() const { return mActiveBodies.size(); } + + PX_FORCE_INLINE BodyCore*const* getActiveCompoundBodiesArray() const { return mActiveCompoundBodies.begin(); } + PX_FORCE_INLINE PxU32 getNumActiveCompoundBodies() const { return mActiveCompoundBodies.size(); } + + PX_FORCE_INLINE PxU32 getNbInteractions(InteractionType::Enum type) const { return mInteractions[type].size(); } + PX_FORCE_INLINE PxU32 getNbActiveInteractions(InteractionType::Enum type) const { return mActiveInteractionCount[type]; } + // Get all interactions of a certain type + PX_FORCE_INLINE Interaction** getInteractions(InteractionType::Enum type) { return mInteractions[type].begin(); } + PX_FORCE_INLINE Interaction** getActiveInteractions(InteractionType::Enum type) { return mInteractions[type].begin(); } + + void registerInteraction(Interaction* interaction, bool active); + void unregisterInteraction(Interaction* interaction); + + void notifyInteractionActivated(Interaction* interaction); + void notifyInteractionDeactivated(Interaction* interaction); + + // for pool management of interaction arrays, a major cause of dynamic allocation + void** allocatePointerBlock(PxU32 size); + void deallocatePointerBlock(void**, PxU32 size); + private: + // Get the number of active one-way dominator actors + PX_FORCE_INLINE PxU32 getActiveKinematicBodiesCount() const { return mActiveKinematicBodyCount; } + + // Get an iterator to the active one-way dominator actors + PX_FORCE_INLINE BodyCore*const* getActiveKinematicBodies() const { return mActiveBodies.begin(); } + + // Get the number of active non-kinematic actors + PX_FORCE_INLINE PxU32 getActiveDynamicBodiesCount() const { return mActiveBodies.size() - mActiveKinematicBodyCount; } + + // Get the active non-kinematic actors + PX_FORCE_INLINE BodyCore*const* getActiveDynamicBodies() const { return mActiveBodies.begin() + mActiveKinematicBodyCount; } + + void swapInteractionArrayIndices(PxU32 id1, PxU32 id2, InteractionType::Enum type); + public: + + PX_FORCE_INLINE Cm::BitMap& getDirtyShapeSimMap() { return mDirtyShapeSimMap; } + + void addToActiveBodyList(BodySim& actor); + void addToActiveCompoundBodyList(BodySim& actor); + void removeFromActiveBodyList(BodySim& actor); + void removeFromActiveCompoundBodyList(BodySim& actor); + void swapInActiveBodyList(BodySim& body); // call when an active body gets switched from dynamic to kinematic or vice versa + + void addToFrozenBodyList(BodySim& actor); + void removeFromFrozenBodyList(BodySim& actor); + void addBrokenConstraint(Sc::ConstraintCore*); + void addActiveBreakableConstraint(Sc::ConstraintSim*, ConstraintInteraction*); + void removeActiveBreakableConstraint(Sc::ConstraintSim*); + //the Actor should register its top level shapes with these. + void removeBody(BodySim&); + + void raiseSceneFlag(SceneInternalFlag::Enum flag) { mInternalFlags |= flag; } + + //lists of actors woken up or put to sleep last simulate + void onBodyWakeUp(BodySim* body); + void onBodySleep(BodySim* body); + + PX_FORCE_INLINE bool isValid() const { return (mLLContext != NULL); } + + void addToLostTouchList(BodySim* body1, BodySim* body2); + + void initDominanceMatrix(); + + void setCreateContactReports(bool s); + + PxU32 createAggregate(void* userData, bool selfCollisions); + void deleteAggregate(PxU32 id); + + Dy::ArticulationV* createLLArticulation(Sc::ArticulationSim* sim); + void destroyLLArticulation(Dy::ArticulationV&); + + + Ps::Pool* + getConstraintInteractionPool() const { return mConstraintInteractionPool; } + public: + PxBroadPhaseType::Enum getBroadPhaseType() const; + bool getBroadPhaseCaps(PxBroadPhaseCaps& caps) const; + PxU32 getNbBroadPhaseRegions() const; + PxU32 getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + PxU32 addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion); + bool removeBroadPhaseRegion(PxU32 handle); + void** getOutOfBoundsAggregates(); + PxU32 getNbOutOfBoundsAggregates(); + void clearOutOfBoundsAggregates(); + + PX_FORCE_INLINE const PxsMaterialManager& getMaterialManager() const { return mMaterialManager; } + PX_FORCE_INLINE PxsMaterialManager& getMaterialManager() { return mMaterialManager; } + + //material management functions to be called from Scb::Scene + void registerMaterialInNP(const PxsMaterialCore& materialCore); + void updateMaterialInNP(const PxsMaterialCore& materialCore); + void unregisterMaterialInNP(const PxsMaterialCore& materialCore); + + // Collision filtering + void setFilterShaderData(const void* data, PxU32 dataSize); + PX_FORCE_INLINE const void* getFilterShaderDataFast() const { return mFilterShaderData; } + PX_FORCE_INLINE PxU32 getFilterShaderDataSizeFast() const { return mFilterShaderDataSize; } + PX_FORCE_INLINE PxSimulationFilterShader getFilterShaderFast() const { return mFilterShader; } + PX_FORCE_INLINE PxSimulationFilterCallback* getFilterCallbackFast() const { return mFilterCallback; } + PX_FORCE_INLINE PxPairFilteringMode::Enum getKineKineFilteringMode() const { return mKineKineFilteringMode; } + PX_FORCE_INLINE PxPairFilteringMode::Enum getStaticKineFilteringMode() const { return mStaticKineFilteringMode; } + + PX_FORCE_INLINE PxU32 getTimeStamp() const { return mTimeStamp; } + PX_FORCE_INLINE PxU32 getReportShapePairTimeStamp() const { return mReportShapePairTimeStamp; } + + PX_FORCE_INLINE PxReal getOneOverDt() const { return mOneOverDt; } + PX_FORCE_INLINE PxReal getDt() const { return mDt; } + + PX_FORCE_INLINE const PxVec3& getGravityFast() const { return mGravity; } + PX_FORCE_INLINE bool readFlag(SceneInternalFlag::Enum flag) const { return (mInternalFlags & flag) != 0; } + PX_FORCE_INLINE bool readPublicFlag(PxSceneFlag::Enum flag) const { return (mPublicFlags & flag); } + + PX_FORCE_INLINE NPhaseCore* getNPhaseCore() const { return mNPhaseCore; } + + void checkConstraintBreakage(); + + PX_FORCE_INLINE Ps::Array& + getTriggerBufferExtraData() { return *mTriggerBufferExtraData; } + PX_FORCE_INLINE Ps::Array& getTriggerBufferAPI() { return mTriggerBufferAPI; } + void reserveTriggerReportBufferSpace(const PxU32 pairCount, PxTriggerPair*& triggerPairBuffer, TriggerPairExtraData*& triggerPairExtraBuffer); + + PX_FORCE_INLINE ObjectIDTracker& getRigidIDTracker() { return *mRigidIDTracker; } + PX_FORCE_INLINE ObjectIDTracker& getShapeIDTracker() { return *mShapeIDTracker; } + + PX_FORCE_INLINE void markReleasedBodyIDForLostTouch(PxU32 id) { mLostTouchPairsDeletedBodyIDs.growAndSet(id); } + void resizeReleasedBodyIDMaps(PxU32 maxActors, PxU32 numActors); + + PX_FORCE_INLINE StaticSim& getStaticAnchor() { return *mStaticAnchor; } + + PX_FORCE_INLINE ConstraintProjectionManager& getProjectionManager() { return *mProjectionManager; } + + PX_FORCE_INLINE Bp::BoundsArray& getBoundsArray() const { return *mBoundsArray; } + PX_FORCE_INLINE void updateContactDistance(PxU32 idx, PxReal distance) { (*mContactDistance)[idx] = distance; mHasContactDistanceChanged = true; } + PX_FORCE_INLINE SqBoundsManager& getSqBoundsManager() const { return *mSqBoundsManager; } + + PX_FORCE_INLINE PxReal getVisualizationScale() const { return mVisualizationScale; } // This is actually redundant but makes checks whether debug visualization is enabled faster + + PX_FORCE_INLINE BodyCore* const* getSleepBodiesArray(PxU32& count) { count = mSleepBodies.size(); return mSleepBodies.getEntries(); } + + PX_FORCE_INLINE PxTaskManager& getTaskManager() const { PX_ASSERT(mTaskManager); return *mTaskManager; } + + Cm::FlushPool* getFlushPool(); + + PX_FORCE_INLINE bool getStabilizationEnabled() const { return mEnableStabilization; } + + PX_FORCE_INLINE void setPostSolverVelocityNeeded() { mContactReportsNeedPostSolverVelocity = true; } + + ObjectIDTracker& getConstraintIDTracker() { return *mConstraintIDTracker; } + + + void* allocateConstraintBlock(PxU32 size); + void deallocateConstraintBlock(void* addr, PxU32 size); + + PX_INLINE ObjectIDTracker& getElementIDPool() { return *mElementIDPool; } + + PX_FORCE_INLINE Cm::BitMap& getVelocityModifyMap() { return mVelocityModifyMap; } + + void stepSetupCollide(PxBaseTask* continuation);//This is very important to guarantee thread safty in the collide + PX_FORCE_INLINE void addToPosePreviewList(BodySim& b) { PX_ASSERT(!mPosePreviewBodies.contains(&b)); mPosePreviewBodies.insert(&b); } + PX_FORCE_INLINE void removeFromPosePreviewList(BodySim& b) { PX_ASSERT(mPosePreviewBodies.contains(&b)); mPosePreviewBodies.erase(&b); } +#if PX_DEBUG + PX_FORCE_INLINE bool isInPosePreviewList(BodySim& b) const { return mPosePreviewBodies.contains(&b); } +#endif + + PX_FORCE_INLINE void setSpeculativeCCDRigidBody(const PxU32 index) { mSpeculativeCCDRigidBodyBitMap.growAndSet(index); } + PX_FORCE_INLINE void resetSpeculativeCCDRigidBody(const PxU32 index) { mSpeculativeCCDRigidBodyBitMap.reset(index); } + + PX_FORCE_INLINE void setSpeculativeCCDArticulationLink(const PxU32 index) { mSpeculativeCDDArticulationBitMap.growAndSet(index); } + PX_FORCE_INLINE void resetSpeculativeCCDArticulationLink(const PxU32 index) { mSpeculativeCDDArticulationBitMap.reset(index); } + + PX_FORCE_INLINE PxU64 getContextId() const { return mContextId; } + PX_FORCE_INLINE bool isUsingGpuRigidBodies() const { return mUseGpuRigidBodies; } + + //internal private methods: + private: + void releaseConstraints(bool endOfScene); + PX_INLINE void clearBrokenConstraintBuffer(); + + ///////////////////////////////////////////////////////////// + + void prepareCollide(); + + void collideStep(PxBaseTask* continuation); + void advanceStep(PxBaseTask* continuation); + + // subroutines of collideStep/solveStep: + void kinematicsSetup(PxBaseTask* continuation); + void stepSetupSolve(PxBaseTask* continuation); + //void stepSetupSimulate(); + + void fetchPatchEvents(PxBaseTask*); + void processNarrowPhaseTouchEvents(); + void processNarrowPhaseTouchEventsStage2(PxBaseTask*); + void setEdgesConnected(PxBaseTask*); + void processNarrowPhaseLostTouchEvents(PxBaseTask*); + void processNarrowPhaseLostTouchEventsIslands(PxBaseTask*); + void processLostTouchPairs(); + void integrateKinematicPose(); + void updateKinematicCached(PxBaseTask* task); + + void beforeSolver(PxBaseTask* continuation); + void checkForceThresholdContactEvents(const PxU32 ccdPass); + void processCallbacks(bool pendingReportsOnly = false); + void endStep(); + private: + PX_FORCE_INLINE void putObjectsToSleep(PxU32 infoFlag); + PX_FORCE_INLINE void putInteractionsToSleep(); + PX_FORCE_INLINE void wakeObjectsUp(PxU32 infoFlag); + + void collectPostSolverVelocitiesBeforeCCD(); + void updateFromVisualizationParameters(); + + void clearSleepWakeBodies(void); + PX_INLINE void cleanUpSleepBodies(); + PX_INLINE void cleanUpWokenBodies(); + PX_INLINE void cleanUpSleepOrWokenBodies(Ps::CoalescedHashSet& bodyList, PxU32 removeFlag, bool& validMarker); + + //internal variables: + private: + // Material manager + PX_ALIGN(16, PxsMaterialManager mMaterialManager); + + PxU64 mContextId; + + Ps::Array mActiveBodies; // Sorted: kinematic before dynamic + PxU32 mActiveKinematicBodyCount; // Number of active kinematics. This is also the index in mActiveBodies where the active dynamic bodies start. + Ps::Array mActiveCompoundBodies; + + Ps::Array mInteractions[InteractionType::eTRACKED_IN_SCENE_COUNT]; + PxU32 mActiveInteractionCount[InteractionType::eTRACKED_IN_SCENE_COUNT]; // Interactions with id < activeInteractionCount are active + + template + struct Block + { + PxU8 mem[sizeof(T)*size]; + Block() {} // get around VS warning C4345, otherwise useless + }; + + typedef Block PointerBlock8; + typedef Block PointerBlock16; + typedef Block PointerBlock32; + + Ps::Pool mPointerBlock8Pool; + Ps::Pool mPointerBlock16Pool; + Ps::Pool mPointerBlock32Pool; + + PxsContext* mLLContext; + + Bp::AABBManager* mAABBManager; + Bp::BroadPhase* mBP; + PxsCCDContext* mCCDContext; + PxI32 mNumFastMovingShapes; + PxU32 mCCDPass; + + //PxsIslandManager* mIslandManager; + + IG::SimpleIslandManager* mSimpleIslandManager; + + Dy::Context* mDynamicsContext; + + + PxsMemoryManager* mMemoryManager; + +#if PX_SUPPORT_GPU_PHYSX + PxsKernelWranglerManager* mGpuWranglerManagers; + PxsHeapMemoryAllocatorManager* mHeapMemoryAllocationManager; +#endif + + PxsSimulationController* mSimulationController; + + PxsSimulationControllerCallback* mSimulationControllerCallback; + + PxSceneLimits mLimits; + + PxVec3 mGravity; //!< Gravity vector + PxU32 mBodyGravityDirty; // Set to true before body->updateForces() when the mGravity has changed + + Ps::Array + mQueuedContactPairHeaders; + //time: + //constants set with setTiming(): + PxReal mDt; //delta time for current step. + PxReal mOneOverDt; //inverse of dt. + //stepping / counters: + PxU32 mTimeStamp; //Counts number of steps. + PxU32 mReportShapePairTimeStamp; //Timestamp for refreshing the shape pair report structure. Updated before delayed shape/actor deletion and before CCD passes. + //containers: + // Those ones contain shape ptrs from Actor, i.e. compound level, not subparts + + Ps::CoalescedHashSet + mConstraints; + + Sc::ConstraintProjectionManager* mProjectionManager; + Bp::BoundsArray* mBoundsArray; + Ps::Array* mContactDistance; + bool mHasContactDistanceChanged; + SqBoundsManager* mSqBoundsManager; + + Ps::Array mCcdBodies; + Ps::Array mProjectedBodies; + Ps::Array mTriggerBufferAPI; + Ps::Array* + mTriggerBufferExtraData; + + PxU32 mRemovedShapeCountAtSimStart; // counter used to detect whether there have been buffered shape removals + + Ps::CoalescedHashSet mArticulations; + + Ps::Array mBrokenConstraints; + Ps::CoalescedHashSet mActiveBreakableConstraints; + + // pools for joint buffers + // Fixed joint is 92 bytes, D6 is 364 bytes right now. So these three pools cover all the internal cases + typedef Block MemBlock128; + typedef Block MemBlock256; + typedef Block MemBlock384; + + Ps::Pool2 mMemBlock128Pool; + Ps::Pool2 mMemBlock256Pool; + Ps::Pool2 mMemBlock384Pool; + + + // broad phase data: + NPhaseCore* mNPhaseCore; + + // Collision filtering + void* mFilterShaderData; + PxU32 mFilterShaderDataSize; + PxU32 mFilterShaderDataCapacity; + PxSimulationFilterShader mFilterShader; + PxSimulationFilterCallback* mFilterCallback; + + PxPairFilteringMode::Enum mKineKineFilteringMode; + PxPairFilteringMode::Enum mStaticKineFilteringMode; + + Ps::CoalescedHashSet mSleepBodies; + Ps::CoalescedHashSet mWokeBodies; + bool mWokeBodyListValid; + bool mSleepBodyListValid; + bool mEnableStabilization; + Ps::Array mClients; //an array of transform arrays, one for each client. + + Ps::Array mActiveActors; + Ps::Array mFrozenActors; + + Ps::Array mClientPosePreviewBodies; // buffer for bodies that requested early report of the integrated pose (eENABLE_POSE_INTEGRATION_PREVIEW). + // This buffer gets exposed to users. Is officially accessible from PxSimulationEventCallback::onAdvance() + // until the next simulate()/advance(). + Ps::Array mClientPosePreviewBuffer; // buffer of newly integrated poses for the bodies that requested a preview. This buffer gets exposed + // to users. + + PxSimulationEventCallback* mSimulationEventCallback; + PxBroadPhaseCallback* mBroadPhaseCallback; + + SimStats* mStats; + PxU32 mInternalFlags; //!< Combination of ::SceneFlag + PxSceneFlags mPublicFlags; //copy of PxSceneDesc::flags, of type PxSceneFlag + + ObjectIDTracker* mConstraintIDTracker; + ObjectIDTracker* mShapeIDTracker; + ObjectIDTracker* mRigidIDTracker; + ObjectIDTracker* mElementIDPool; + + StaticSim* mStaticAnchor; + + Cm::PreallocatingPool* mShapeSimPool; + Cm::PreallocatingPool* mStaticSimPool; + Cm::PreallocatingPool* mBodySimPool; + Ps::Pool* mConstraintSimPool; + LLArticulationPool* mLLArticulationPool; + LLArticulationRCPool* mLLArticulationRCPool; + + Ps::Pool* + mConstraintInteractionPool; + + Ps::Pool* mSimStateDataPool; + + BatchRemoveState* mBatchRemoveState; + + Ps::Array mLostTouchPairs; + Cm::BitMap mLostTouchPairsDeletedBodyIDs; // Need to know which bodies have been deleted when processing the lost touch pair list. + // Can't use the existing rigid object ID tracker class since this map needs to be cleared at + // another point in time. + + Cm::BitMap mVelocityModifyMap; + + Ps::Array mTouchFoundEvents; + Ps::Array mTouchLostEvents; + + Ps::Array mFoundPatchManagers; + Ps::Array mLostPatchManagers; + + Ps::Array mOutOfBoundsIDs; + + Cm::BitMap mDirtyShapeSimMap; + + PxU32 mDominanceBitMatrix[PX_MAX_DOMINANCE_GROUP]; + + PxReal mVisualizationScale; // Redundant but makes checks whether debug visualization is enabled faster + + bool mVisualizationParameterChanged; + + // statics: + PxU32 mNbRigidStatics; + PxU32 mNbRigidDynamics; + PxU32 mNbGeometries[PxGeometryType::eGEOMETRY_COUNT]; + + PxU32 mNumDeactivatingNodes[2]; + + // task decomposition + void preBroadPhase(PxBaseTask* continuation); + void broadPhase(PxBaseTask* continuation); + void postBroadPhase(PxBaseTask* continuation); + void postBroadPhaseContinuation(PxBaseTask* continuation); + void preRigidBodyNarrowPhase(PxBaseTask* continuation); + void postBroadPhaseStage2(PxBaseTask* continuation); + void postBroadPhaseStage3(PxBaseTask* continuation); + void rigidBodyNarrowPhase(PxBaseTask* continuation); + void unblockNarrowPhase(PxBaseTask* continuation); + void islandGen(PxBaseTask* continuation); + void processLostSolverPatches(PxBaseTask* continuation); + void postIslandGen(PxBaseTask* continuation); + void processTriggerInteractions(PxBaseTask* continuation); + void solver(PxBaseTask* continuation); + void updateBodiesAndShapes(PxBaseTask* continuation); + void updateSimulationController(PxBaseTask* continuation); + void updateDynamics(PxBaseTask* continuation); + void processLostContacts(PxBaseTask*); + void processLostContacts2(PxBaseTask*); + void processLostContacts3(PxBaseTask*); + void destroyManagers(PxBaseTask*); + void lostTouchReports(PxBaseTask*); + void unregisterInteractions(PxBaseTask*); + void postThirdPassIslandGen(PxBaseTask*); + void postSolver(PxBaseTask* continuation); + void constraintProjection(PxBaseTask* continuation); + void afterIntegration(PxBaseTask* continuation); // performs sleep check, for instance + void postCCDPass(PxBaseTask* continuation); + void ccdBroadPhaseAABB(PxBaseTask* continuation); + void ccdBroadPhase(PxBaseTask* continuation); + void updateCCDMultiPass(PxBaseTask* continuation); + void updateCCDSinglePass(PxBaseTask* continuation); + void updateCCDSinglePassStage2(PxBaseTask* continuation); + void updateCCDSinglePassStage3(PxBaseTask* continuation); + void finalizationPhase(PxBaseTask* continuation); + + void postNarrowPhase(PxBaseTask* continuation); + + void addShapes(void *const* shapes, PxU32 nbShapes, size_t ptrOffset, RigidSim& sim, PxBounds3* outBounds); + void removeShapes(RigidSim& , Ps::InlineArray& , Ps::InlineArray&, bool wakeOnLostTouch); + + + private: + + void addShapes(void *const* shapes, PxU32 nbShapes, size_t ptrOffset, RigidSim& sim, ShapeSim*& prefetchedShapeSim, PxBounds3* outBounds); + + Cm::DelegateTask mSecondPassNarrowPhase; + Cm::DelegateFanoutTask mPostNarrowPhase; + Cm::DelegateFanoutTask mFinalizationPhase; + Cm::DelegateTask mUpdateCCDMultiPass; + + //multi-pass ccd stuff + Ps::Array > mUpdateCCDSinglePass; + Ps::Array > mUpdateCCDSinglePass2; + Ps::Array > mUpdateCCDSinglePass3; + Ps::Array > mCCDBroadPhaseAABB; + Ps::Array > mCCDBroadPhase; + Ps::Array > mPostCCDPass; + PxU32 mCurrentCCDTask; + + Cm::DelegateTask mAfterIntegration; + Cm::DelegateTask mConstraintProjection; + Cm::DelegateTask mPostSolver; + Cm::DelegateTask mSolver; + Cm::DelegateTask mUpdateBodiesAndShapes; + Cm::DelegateTask mUpdateSimulationController; + Cm::DelegateTask mUpdateDynamics; + Cm::DelegateTask mProcessLostContactsTask; + Cm::DelegateTask mProcessLostContactsTask2; + Cm::DelegateTask mProcessLostContactsTask3; + Cm::DelegateTask mDestroyManagersTask; + Cm::DelegateTask mLostTouchReportsTask; + Cm::DelegateTask mUnregisterInteractionsTask; + Cm::DelegateTask mProcessNarrowPhaseLostTouchTasks; + Cm::DelegateTask mProcessNPLostTouchEvents; + Cm::DelegateTask mPostThirdPassIslandGenTask; + Cm::DelegateTask mPostIslandGen; + Cm::DelegateTask mIslandGen; + Cm::DelegateTask mPreRigidBodyNarrowPhase; + Cm::DelegateTask mSetEdgesConnectedTask; + Cm::DelegateTask mFetchPatchEventsTask; + Cm::DelegateTask mProcessLostPatchesTask; + Cm::DelegateTask mRigidBodyNarrowPhase; + Cm::DelegateTask mRigidBodyNPhaseUnlock; + Cm::DelegateTask mPostBroadPhase; + Cm::DelegateTask mPostBroadPhaseCont; + Cm::DelegateTask mPostBroadPhase2; + Cm::DelegateFanoutTask mPostBroadPhase3; + Cm::DelegateTask mPreallocateContactManagers; + Cm::DelegateTask mIslandInsertion; + Cm::DelegateTask mRegisterContactManagers; + Cm::DelegateTask mRegisterInteractions; + Cm::DelegateTask mRegisterSceneInteractions; + Cm::DelegateTask mBroadPhase; + Cm::DelegateTask mAdvanceStep; + Cm::DelegateTask mCollideStep; + + Cm::FlushPool mTaskPool; + PxTaskManager* mTaskManager; + + bool mContactReportsNeedPostSolverVelocity; + bool mUseGpuRigidBodies; + + SimulationStage::Enum mSimulationStage; + + ConstraintGroupNode** mTmpConstraintGroupRootBuffer; // temporary list of constraint group roots, used for constraint projection + + Ps::CoalescedHashSet mPosePreviewBodies; // list of bodies that requested early report of the integrated pose (eENABLE_POSE_INTEGRATION_PREVIEW). + + Ps::Array mPreallocatedContactManagers; + Ps::Array mPreallocatedShapeInteractions; + Ps::Array mPreallocatedInteractionMarkers; + + OverlapFilterTask* mOverlapFilterTaskHead; + Ps::Array mFilterInfo; + Cm::BitMap mSpeculativeCCDRigidBodyBitMap; + Cm::BitMap mSpeculativeCDDArticulationBitMap; + }; + + bool activateInteraction(Sc::Interaction* interaction, void* data); + bool deactivateInteraction(Sc::Interaction* interaction); + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h new file mode 100644 index 000000000..ce52fa86f --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h @@ -0,0 +1,137 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_SHAPECORE +#define PX_PHYSICS_SCP_SHAPECORE + +#include "PsUserAllocated.h" +#include "GuGeometryUnion.h" +#include "PxvGeometry.h" +#include "PsUtilities.h" +#include "PxFiltering.h" +#include "PxShape.h" + +namespace physx +{ +class PxShape; + +namespace Sc +{ + class Scene; + class RigidCore; + class BodyCore; + class ShapeSim; + class MaterialCore; + + class ShapeCore : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + ShapeCore(const PxEMPTY); + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void resolveMaterialReference(PxU32 materialTableIndex, PxU16 materialIndex); +//~PX_SERIALIZATION + + ShapeCore(const PxGeometry& geometry, + PxShapeFlags shapeFlags, + const PxU16* materialIndices, + PxU16 materialCount); + + ~ShapeCore(); + + PX_FORCE_INLINE PxGeometryType::Enum getGeometryType() const { return mCore.geometry.getType(); } + PxShape* getPxShape(); + const PxShape* getPxShape() const; + + PX_FORCE_INLINE const Gu::GeometryUnion& getGeometryUnion() const { return mCore.geometry; } + PX_FORCE_INLINE const PxGeometry& getGeometry() const { return mCore.geometry.getGeometry(); } + void setGeometry(const PxGeometry& geom); + + PxU16 getNbMaterialIndices() const; + const PxU16* getMaterialIndices() const; + void setMaterialIndices(const PxU16* materialIndices, PxU16 materialIndexCount); + + PX_FORCE_INLINE const PxTransform& getShape2Actor() const { return mCore.transform; } + PX_FORCE_INLINE void setShape2Actor(const PxTransform& s2b) { mCore.transform = s2b; } + + PX_FORCE_INLINE const PxFilterData& getSimulationFilterData() const { return mSimulationFilterData; } + PX_FORCE_INLINE void setSimulationFilterData(const PxFilterData& data) { mSimulationFilterData = data; } + + // PT: this one doesn't need double buffering + PX_FORCE_INLINE const PxFilterData& getQueryFilterData() const { return mQueryFilterData; } + PX_FORCE_INLINE void setQueryFilterData(const PxFilterData& data) { mQueryFilterData = data; } + + PX_FORCE_INLINE PxReal getContactOffset() const { return mCore.contactOffset; } + PX_FORCE_INLINE void setContactOffset(PxReal offset) { mCore.contactOffset = offset; } + + PX_FORCE_INLINE PxReal getRestOffset() const { return mRestOffset; } + PX_FORCE_INLINE void setRestOffset(PxReal offset) { mRestOffset = offset; } + + PX_FORCE_INLINE PxReal getTorsionalPatchRadius() const { return mTorsionalRadius; } + PX_FORCE_INLINE void setTorsionalPatchRadius(PxReal tpr) { mTorsionalRadius = tpr; } + + PX_FORCE_INLINE PxReal getMinTorsionalPatchRadius() const {return mMinTorsionalPatchRadius; } + PX_FORCE_INLINE void setMinTorsionalPatchRadius(PxReal radius) { mMinTorsionalPatchRadius = radius; } + + PX_FORCE_INLINE PxShapeFlags getFlags() const { return PxShapeFlags(mCore.mShapeFlags); } + PX_FORCE_INLINE void setFlags(PxShapeFlags f) { mCore.mShapeFlags = f; } + + PX_FORCE_INLINE const PxsShapeCore& getCore() const { return mCore; } + + static PX_FORCE_INLINE ShapeCore& getCore(PxsShapeCore& core) + { + size_t offset = PX_OFFSET_OF(ShapeCore, mCore); + return *reinterpret_cast(reinterpret_cast(&core) - offset); + } + + protected: + PxFilterData mQueryFilterData; // Query filter data PT: TODO: consider moving this to SceneQueryShapeData + PxFilterData mSimulationFilterData; // Simulation filter data + PxsShapeCore PX_ALIGN(16, mCore); + PxReal mRestOffset; // same as the API property of the same name + PxReal mTorsionalRadius; + PxReal mMinTorsionalPatchRadius; + }; + +} // namespace Sc + + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h new file mode 100644 index 000000000..198488be4 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_STATIC_CORE +#define PX_PHYSICS_SCP_STATIC_CORE + +#include "ScRigidCore.h" +#include "PxvDynamics.h" + +namespace physx +{ +namespace Sc +{ + + class StaticSim; + + class StaticCore: public RigidCore + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + StaticCore(const PxTransform& actor2World): RigidCore(PxActorType::eRIGID_STATIC) + { + mCore.body2World = actor2World; + mCore.mFlags = PxRigidBodyFlags(); + } + + PX_FORCE_INLINE const PxTransform& getActor2World() const { return mCore.body2World; } + void setActor2World(const PxTransform& actor2World); + + PX_FORCE_INLINE PxsRigidCore& getCore() { return mCore; } + + StaticCore(const PxEMPTY) : RigidCore(PxEmpty), mCore(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); + + StaticSim* getSim() const; + + PX_FORCE_INLINE void onOriginShift(const PxVec3& shift) { mCore.body2World.p -= shift; } + private: + PxsRigidCore mCore; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp new file mode 100644 index 000000000..b6ac47aeb --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScActorCore.h" +#include "ScActorSim.h" +#include "ScShapeCore.h" +#include "ScShapeSim.h" +#include "ScBodySim.h" + +using namespace physx; + +Sc::ActorCore::ActorCore(PxActorType::Enum actorType, PxU8 actorFlags, PxClientID owner, PxDominanceGroup dominanceGroup) : + mSim (NULL), + mAggregateIDOwnerClient ((PxU32(owner)<<24)|0x00ffffff), + mActorFlags (actorFlags), + mActorType (PxU8(actorType)), + mDominanceGroup (dominanceGroup) +{ + PX_ASSERT((actorType & 0xff) == actorType); +} + +Sc::ActorCore::~ActorCore() +{ +} + +void Sc::ActorCore::setActorFlags(PxActorFlags af) +{ + const PxActorFlags old = mActorFlags; + if(af!=old) + { + mActorFlags = af; + + if(mSim) + mSim->postActorFlagChange(old, af); + } +} + +void Sc::ActorCore::setDominanceGroup(PxDominanceGroup g) +{ + PX_ASSERT(g<128); + mDominanceGroup = g; + if(mSim) + { + //force all related interactions to refresh, so they fetch new dominance values. + mSim->setActorsInteractionsDirty(InteractionDirtyFlag::eDOMINANCE, NULL, InteractionFlag::eRB_ELEMENT); + } +} + +void Sc::ActorCore::reinsertShapes() +{ + PX_ASSERT(mSim); + if(!mSim) + return; + + ElementSim* current = mSim->getElements_(); + while(current) + { + static_cast(current)->reinsertBroadPhase(); + current = current->mNextInActor; + } +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h b/src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h new file mode 100644 index 000000000..1d8b26fef --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h @@ -0,0 +1,217 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_COLLISION_ACTORPAIR +#define PX_COLLISION_ACTORPAIR + +#include "ScRigidSim.h" +#include "ScContactStream.h" +#include "ScNPhaseCore.h" + +namespace physx +{ +namespace Sc +{ + class ActorPairContactReportData + { + public: + ActorPairContactReportData() : + mStrmResetStamp (0xffffffff), + mActorAID (0xffffffff), + mActorBID (0xffffffff), + mPxActorA (NULL), + mPxActorB (NULL) + {} + + ContactStreamManager mContactStreamManager; + PxU32 mStrmResetStamp; + PxU32 mActorAID; + PxU32 mActorBID; + PxActor* mPxActorA; + PxActor* mPxActorB; + }; + + /** + \brief Class shared by all shape interactions for a pair of actors. + + This base class is used if no shape pair of an actor pair has contact reports requested. + */ + class ActorPair + { + public: + + enum ActorPairFlags + { + eIS_REPORT_PAIR = (1<<0), + eNEXT_FREE = (1<<1) + }; + + PX_FORCE_INLINE ActorPair() : mInternalFlags(0), mTouchCount(0), mRefCount(0) {} + PX_FORCE_INLINE ~ActorPair() {} + + PX_FORCE_INLINE Ps::IntBool isReportPair() const { return (mInternalFlags & eIS_REPORT_PAIR); } + + PX_FORCE_INLINE void incTouchCount() { mTouchCount++; PX_ASSERT(mTouchCount); } + PX_FORCE_INLINE void decTouchCount() { PX_ASSERT(mTouchCount); mTouchCount--; } + PX_FORCE_INLINE PxU32 getTouchCount() const { return mTouchCount; } + + PX_FORCE_INLINE void incRefCount() { ++mRefCount; PX_ASSERT(mRefCount>0); } + PX_FORCE_INLINE PxU32 decRefCount() { PX_ASSERT(mRefCount>0); return --mRefCount; } + PX_FORCE_INLINE PxU32 getRefCount() const { return mRefCount; } + + private: + ActorPair& operator=(const ActorPair&); + + protected: + PxU16 mInternalFlags; + PxU16 mTouchCount; + PxU16 mRefCount; + PxU16 mPad; // instances of this class are stored in a pool which needs an item size of at least size_t + }; + + /** + \brief Class shared by all shape interactions for a pair of actors if contact reports are requested. + + This class is used if at least one shape pair of an actor pair has contact reports requested. + + \note If a pair of actors had contact reports requested for some of the shape interactions but all of them switch to not wanting contact reports + any longer, then the ActorPairReport instance is kept being used and won't get replaced by a simpler ActorPair instance. + */ + class ActorPairReport : public ActorPair + { + public: + + enum ActorPairReportFlags + { + eIS_IN_CONTACT_REPORT_ACTOR_PAIR_SET = ActorPair::eNEXT_FREE // PT: whether the pair is already stored in the 'ContactReportActorPairSet' or not + }; + + PX_FORCE_INLINE ActorPairReport(RigidSim&, RigidSim&); + PX_FORCE_INLINE ~ActorPairReport(); + + PX_INLINE ContactStreamManager& createContactStreamManager(NPhaseCore&); + PX_FORCE_INLINE ContactStreamManager& getContactStreamManager() const { PX_ASSERT(mReportData); return mReportData->mContactStreamManager; } + PX_FORCE_INLINE RigidSim& getActorA() const { return mActorA; } + PX_FORCE_INLINE RigidSim& getActorB() const { return mActorB; } + PX_INLINE PxU32 getActorAID() const { PX_ASSERT(mReportData); return mReportData->mActorAID; } + PX_INLINE PxU32 getActorBID() const { PX_ASSERT(mReportData); return mReportData->mActorBID; } + PX_INLINE PxActor* getPxActorA() const { PX_ASSERT(mReportData); return mReportData->mPxActorA; } + PX_INLINE PxActor* getPxActorB() const { PX_ASSERT(mReportData); return mReportData->mPxActorB; } + PX_FORCE_INLINE bool streamResetNeeded(PxU32 cmpStamp) const; + PX_INLINE bool streamResetStamp(PxU32 cmpStamp); + + PX_FORCE_INLINE PxU16 isInContactReportActorPairSet() const { return PxU16(mInternalFlags & eIS_IN_CONTACT_REPORT_ACTOR_PAIR_SET); } + PX_FORCE_INLINE void setInContactReportActorPairSet() { mInternalFlags |= eIS_IN_CONTACT_REPORT_ACTOR_PAIR_SET; } + PX_FORCE_INLINE void clearInContactReportActorPairSet() { mInternalFlags &= ~eIS_IN_CONTACT_REPORT_ACTOR_PAIR_SET; } + + PX_FORCE_INLINE void createContactReportData(NPhaseCore&); + PX_FORCE_INLINE void releaseContactReportData(NPhaseCore&); + PX_FORCE_INLINE const ActorPairContactReportData* hasReportData() const { return mReportData; } + + PX_FORCE_INLINE void convert(ActorPair& aPair) { PX_ASSERT(!aPair.isReportPair()); mTouchCount = PxU16(aPair.getTouchCount()); mRefCount = PxU16(aPair.getRefCount()); } + + PX_FORCE_INLINE static ActorPairReport& cast(ActorPair& aPair) { PX_ASSERT(aPair.isReportPair()); return static_cast(aPair); } + + private: + ActorPairReport& operator=(const ActorPairReport&); + + RigidSim& mActorA; + RigidSim& mActorB; + + ActorPairContactReportData* mReportData; + }; + +} // namespace Sc + +PX_FORCE_INLINE Sc::ActorPairReport::ActorPairReport(RigidSim& actor0, RigidSim& actor1) : ActorPair(), +mActorA (actor0), +mActorB (actor1), +mReportData (NULL) +{ + PX_ASSERT(mInternalFlags == 0); + mInternalFlags = ActorPair::eIS_REPORT_PAIR; +} + +PX_FORCE_INLINE Sc::ActorPairReport::~ActorPairReport() +{ + PX_ASSERT(mReportData == NULL); +} + +PX_FORCE_INLINE bool Sc::ActorPairReport::streamResetNeeded(PxU32 cmpStamp) const +{ + return (cmpStamp != mReportData->mStrmResetStamp); +} + +PX_INLINE bool Sc::ActorPairReport::streamResetStamp(PxU32 cmpStamp) +{ + PX_ASSERT(mReportData); + const bool ret = streamResetNeeded(cmpStamp); + mReportData->mStrmResetStamp = cmpStamp; + return ret; +} + +PX_INLINE Sc::ContactStreamManager& Sc::ActorPairReport::createContactStreamManager(NPhaseCore& npCore) +{ + // Lazy create report data + if(!mReportData) + createContactReportData(npCore); + + return mReportData->mContactStreamManager; +} + +PX_FORCE_INLINE void Sc::ActorPairReport::createContactReportData(NPhaseCore& npCore) +{ + PX_ASSERT(!mReportData); + Sc::ActorPairContactReportData* reportData = npCore.createActorPairContactReportData(); + mReportData = reportData; + + if(reportData) + { + reportData->mActorAID = mActorA.getRigidID(); + reportData->mActorBID = mActorB.getRigidID(); + + reportData->mPxActorA = mActorA.getPxActor(); + reportData->mPxActorB = mActorB.getPxActor(); + } +} + +PX_FORCE_INLINE void Sc::ActorPairReport::releaseContactReportData(NPhaseCore& npCore) +{ + // Can't take the NPhaseCore (scene) reference from the actors since they're already gone on scene release + + if(mReportData) + { + npCore.releaseActorPairContactReportData(mReportData); + mReportData = NULL; + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp new file mode 100644 index 000000000..a58394641 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp @@ -0,0 +1,144 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CmPhysXCommon.h" +#include "ScActorSim.h" +#include "ScActorCore.h" +#include "ScElementSim.h" +#include "ScScene.h" +#include "ScInteraction.h" + +using namespace physx; + +Sc::ActorSim::ActorSim(Scene& scene, ActorCore& core) : + mFirstElement (NULL), + mElementCount (0), + mScene (scene), + mCore (core) +{ + core.setSim(this); +} + +Sc::ActorSim::~ActorSim() +{ + mInteractions.releaseMem(*this); +} + +void Sc::ActorSim::registerInteractionInActor(Interaction* interaction) +{ + const PxU32 id = mInteractions.size(); + mInteractions.pushBack(interaction, *this); + interaction->setActorId(this, id); +} + +void Sc::ActorSim::unregisterInteractionFromActor(Interaction* interaction) +{ + const PxU32 i = interaction->getActorId(this); + PX_ASSERT(i < mInteractions.size()); + mInteractions.replaceWithLast(i); + if (isetActorId(this, i); +} + +void Sc::ActorSim::onElementAttach(ElementSim& element) +{ + element.mNextInActor = mFirstElement; + mFirstElement = &element; + mElementCount++; +} + +void Sc::ActorSim::onElementDetach(ElementSim& element) +{ + PX_ASSERT(mFirstElement); // PT: else we shouldn't be called + ElementSim* currentElem = mFirstElement; + ElementSim* previousElem = NULL; + while(currentElem) + { + if(currentElem==&element) + { + if(previousElem) + previousElem->mNextInActor = currentElem->mNextInActor; + else + mFirstElement = currentElem->mNextInActor; + mElementCount--; + return; + } + previousElem = currentElem; + currentElem = currentElem->mNextInActor; + } + PX_ASSERT(0); +} + +// PT: TODO: refactor with Sc::ParticlePacketShape::reallocInteractions - sschirm: particles are gone +void Sc::ActorSim::reallocInteractions(Sc::Interaction**& mem, PxU32& capacity, PxU32 size, PxU32 requiredMinCapacity) +{ + Interaction** newMem; + PxU32 newCapacity; + + if(requiredMinCapacity==0) + { + newCapacity = 0; + newMem = 0; + } + else if(requiredMinCapacity<=INLINE_INTERACTION_CAPACITY) + { + newCapacity = INLINE_INTERACTION_CAPACITY; + newMem = mInlineInteractionMem; + } + else + { + newCapacity = Ps::nextPowerOfTwo(requiredMinCapacity-1); + newMem = reinterpret_cast(mScene.allocatePointerBlock(newCapacity)); + } + + PX_ASSERT(newCapacity >= requiredMinCapacity && requiredMinCapacity>=size); + + if(mem) + { + PxMemCopy(newMem, mem, size*sizeof(Interaction*)); + + if(mem!=mInlineInteractionMem) + mScene.deallocatePointerBlock(reinterpret_cast(mem), capacity); + } + + capacity = newCapacity; + mem = newMem; +} + +void Sc::ActorSim::setActorsInteractionsDirty(InteractionDirtyFlag::Enum flag, const ActorSim* other, PxU8 interactionFlag) +{ + PxU32 size = getActorInteractionCount(); + Interaction** interactions = getActorInteractions(); + while(size--) + { + Interaction* interaction = *interactions++; + if((!other || other == &interaction->getActorSim0() || other == &interaction->getActorSim1()) && (interaction->readInteractionFlag(interactionFlag))) + interaction->setDirty(flag); + } +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h new file mode 100644 index 000000000..578695629 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h @@ -0,0 +1,124 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_ACTOR_SIM +#define PX_PHYSICS_SCP_ACTOR_SIM + +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "CmUtils.h" +#include "PxActor.h" +#include "ScInteractionFlags.h" +#include "ScActorCore.h" + +namespace physx +{ + +class PxActor; + +namespace Sc +{ + class Interaction; + class ElementSim; + + class ActorSim : public Ps::UserAllocated + { + friend class Scene; // the scene is allowed to set the scene array index + friend class Interaction; + PX_NOCOPY(ActorSim) + + public: + enum ActivityChangeInfoFlag + { + AS_PART_OF_CREATION = (1 << 0), + AS_PART_OF_ISLAND_GEN = (1 << 1) + }; + + ActorSim(Scene&, ActorCore&); + virtual ~ActorSim(); + + // Get the scene the actor resides in + PX_FORCE_INLINE Scene& getScene() const { return mScene; } + + // Get the number of interactions connected to the actor + PX_FORCE_INLINE PxU32 getActorInteractionCount() const { return mInteractions.size(); } + + // Get an iterator to the interactions connected to the actor + PX_FORCE_INLINE Interaction** getActorInteractions() const { return mInteractions.begin(); } + + // Get first element in the actor (linked list) + PX_FORCE_INLINE ElementSim* getElements_() { return mFirstElement; } + PX_FORCE_INLINE const ElementSim* getElements_() const { return mFirstElement; } + + PX_FORCE_INLINE PxU32 getElementCount() const { return mElementCount; } + + // Get the type ID of the actor + PX_FORCE_INLINE PxActorType::Enum getActorType() const { return mCore.getActorCoreType(); } + + // Returns true if the actor is a dynamic rigid body (including articulation links) + PX_FORCE_INLINE bool isDynamicRigid() const { const PxActorType::Enum type = getActorType(); return type == PxActorType::eRIGID_DYNAMIC || type == PxActorType::eARTICULATION_LINK; } + + void onElementAttach(ElementSim& element); + void onElementDetach(ElementSim& element); + + virtual void postActorFlagChange(PxU32, PxU32) {} + + void setActorsInteractionsDirty(InteractionDirtyFlag::Enum flag, const ActorSim* other, PxU8 interactionFlag); + + PX_FORCE_INLINE ActorCore& getActorCore() const { return mCore; } + + private: + //These are called from interaction creation/destruction + void registerInteractionInActor(Interaction* interaction); + void unregisterInteractionFromActor(Interaction* interaction); + + void reallocInteractions(Sc::Interaction**& mem, PxU32& capacity, PxU32 size, PxU32 requiredMinCapacity); + protected: + // dsequeira: interaction arrays are a major cause of small allocations, so we don't want to delegate them to the heap allocator + // it's not clear this inline array is really needed, we should take it out and see whether the cache perf is worse + + static const PxU32 INLINE_INTERACTION_CAPACITY = 4; + Interaction* mInlineInteractionMem[INLINE_INTERACTION_CAPACITY]; + + Cm::OwnedArray + mInteractions; + + ElementSim* mFirstElement; + PxU32 mElementCount; + + Scene& mScene; + + ActorCore& mCore; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp new file mode 100644 index 000000000..0f73f27bb --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp @@ -0,0 +1,384 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScArticulationCore.h" + +#include "PsFoundation.h" +#include "ScPhysics.h" +#include "ScBodyCore.h" +#include "ScBodySim.h" +#include "ScArticulationSim.h" +#include "DyArticulation.h" + +using namespace physx; + +Sc::ArticulationCore::ArticulationCore() : + mSim(NULL) +{ + const PxTolerancesScale& scale = Physics::getInstance().getTolerancesScale(); + + mCore.internalDriveIterations = 4; + mCore.externalDriveIterations = 4; + mCore.maxProjectionIterations = 4; + mCore.separationTolerance = 0.1f * scale.length; + mCore.solverIterationCounts = 1<<8 | 4; + mCore.sleepThreshold = 5e-5f * scale.speed * scale.speed; + mCore.wakeCounter = Physics::sWakeCounterOnCreation; + mCore.freezeThreshold = 5e-6f * scale.speed * scale.speed; +} + + +Sc::ArticulationCore::~ArticulationCore() +{ +} + + +//-------------------------------------------------------------- +// +// ArticulationCore interface implementation +// +//-------------------------------------------------------------- + +PxU32 Sc::ArticulationCore::getInternalDriveIterations() const +{ + return mCore.internalDriveIterations; +} + +void Sc::ArticulationCore::setInternalDriveIterations(const PxU32 v) +{ + mCore.internalDriveIterations = v; +} + +PxU32 Sc::ArticulationCore::getExternalDriveIterations() const +{ + return mCore.externalDriveIterations; +} + +void Sc::ArticulationCore::setExternalDriveIterations(const PxU32 v) +{ + mCore.externalDriveIterations = v; +} + +PxU32 Sc::ArticulationCore::getMaxProjectionIterations() const +{ + return mCore.maxProjectionIterations; +} + +void Sc::ArticulationCore::setMaxProjectionIterations(const PxU32 v) +{ + mCore.maxProjectionIterations = v; +} + +PxReal Sc::ArticulationCore::getSeparationTolerance() const +{ + return mCore.separationTolerance; +} + +void Sc::ArticulationCore::setSeparationTolerance(const PxReal v) +{ + mCore.separationTolerance = v; +} + +PxReal Sc::ArticulationCore::getWakeCounter() const +{ + return mCore.wakeCounter; +} + +void Sc::ArticulationCore::setWakeCounterInternal(const PxReal v) +{ + mCore.wakeCounter = v; +} + +void Sc::ArticulationCore::setWakeCounter(const PxReal v) +{ + mCore.wakeCounter = v; + +#ifdef _DEBUG + if(mSim) + mSim->debugCheckWakeCounterOfLinks(v); +#endif +} + +bool Sc::ArticulationCore::isSleeping() const +{ + return mSim ? mSim->isSleeping() : (mCore.wakeCounter == 0.0f); +} + +void Sc::ArticulationCore::wakeUp(PxReal wakeCounter) +{ + mCore.wakeCounter = wakeCounter; + +#ifdef _DEBUG + if(mSim) + mSim->debugCheckSleepStateOfLinks(false); +#endif +} + +void Sc::ArticulationCore::putToSleep() +{ + mCore.wakeCounter = 0.0f; + +#ifdef _DEBUG + if(mSim) + mSim->debugCheckSleepStateOfLinks(true); +#endif +} + +PxReal Sc::ArticulationCore::getSleepThreshold() const +{ + return mCore.sleepThreshold; +} + +void Sc::ArticulationCore::setSleepThreshold(const PxReal v) +{ + mCore.sleepThreshold = v; +} + +PxReal Sc::ArticulationCore::getFreezeThreshold() const +{ + return mCore.freezeThreshold; +} + +void Sc::ArticulationCore::setFreezeThreshold(const PxReal v) +{ + mCore.freezeThreshold = v; +} + +PxU16 Sc::ArticulationCore::getSolverIterationCounts() const +{ + return mCore.solverIterationCounts; +} + +void Sc::ArticulationCore::setSolverIterationCounts(const PxU16 v) +{ + mCore.solverIterationCounts = v; +} + + +PxArticulation* Sc::ArticulationCore::getPxArticulation() +{ + return gOffsetTable.convertScArticulation2Px(this); +} + + +const PxArticulation* Sc::ArticulationCore::getPxArticulation() const +{ + return gOffsetTable.convertScArticulation2Px(this); +} + +Sc::ArticulationDriveCache* Sc::ArticulationCore::createDriveCache(PxReal compliance, + PxU32 driveIterations) const +{ + return mSim? mSim->createDriveCache(compliance, driveIterations) : NULL; +} + + +void Sc::ArticulationCore::updateDriveCache(ArticulationDriveCache& cache, + PxReal compliance, + PxU32 driveIterations) const +{ + mSim->updateDriveCache(cache, compliance, driveIterations); +} + + +void Sc::ArticulationCore::releaseDriveCache(Sc::ArticulationDriveCache& driveCache) const +{ + if(mSim) + mSim->releaseDriveCache(driveCache); +} + + +PxU32 Sc::ArticulationCore::getCacheLinkCount(const ArticulationDriveCache& cache) const +{ + return Dy::PxvArticulationDriveCache::getLinkCount(cache); +} + +void Sc::ArticulationCore::applyImpulse(Sc::BodyCore& link, + const Sc::ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) +{ + if(mSim) + mSim->applyImpulse(link, driveCache, force, torque); +} + +void Sc::ArticulationCore::computeImpulseResponse(Sc::BodyCore& link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const Sc::ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const +{ + if(mSim) + mSim->computeImpulseResponse(link, linearResponse, angularResponse, driveCache, force, torque); +} + +PxU32 Sc::ArticulationCore::getDofs() const +{ + return mSim ? mSim->getDofs() : 0; +} + +PxArticulationCache* Sc::ArticulationCore::createCache() const +{ + return mSim ? mSim->createCache() : NULL; +} + +PxU32 Sc::ArticulationCore::getCacheDataSize() const +{ + return mSim ? mSim->getCacheDataSize() : 0; +} + +void Sc::ArticulationCore::zeroCache(PxArticulationCache& cache) const +{ + if (mSim) + mSim->zeroCache(cache); +} + +void Sc::ArticulationCore::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const +{ + if (mSim) + mSim->applyCache(cache, flag); +} + +void Sc::ArticulationCore::copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const +{ + if (mSim) + mSim->copyInternalStateToCache(cache, flag); +} + +void Sc::ArticulationCore::releaseCache(PxArticulationCache& cache) const +{ + if (mSim) + mSim->releaseCache(cache); +} + +void Sc::ArticulationCore::packJointData(const PxReal* maximum, PxReal* reduced) const +{ + if (mSim) + mSim->packJointData(maximum, reduced); +} + +void Sc::ArticulationCore::unpackJointData(const PxReal* reduced, PxReal* maximum) const +{ + if (mSim) + mSim->unpackJointData(reduced, maximum); +} + +void Sc::ArticulationCore::commonInit() const +{ + if (mSim) + mSim->commonInit(); +} + +void Sc::ArticulationCore::computeGeneralizedGravityForce(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeGeneralizedGravityForce(cache); +} + +void Sc::ArticulationCore::computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeCoriolisAndCentrifugalForce(cache); +} + +void Sc::ArticulationCore::computeGeneralizedExternalForce(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeGeneralizedExternalForce(cache); +} + +void Sc::ArticulationCore::computeJointAcceleration(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeJointAcceleration(cache); +} + +void Sc::ArticulationCore::computeJointForce(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeJointForce(cache); +} + +void Sc::ArticulationCore::computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeKinematicJacobian(linkID, cache); +} + + +void Sc::ArticulationCore::computeCoefficentMatrix(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeCoefficentMatrix(cache); +} + +bool Sc::ArticulationCore::computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxVec3 gravity, const PxU32 maxIter) const +{ + if (mSim) + { + return mSim->computeLambda(cache, initialState, jointTorque, gravity, maxIter); + } + + return false; +} + +void Sc::ArticulationCore::computeGeneralizedMassMatrix(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeGeneralizedMassMatrix(cache); +} + +PxU32 Sc::ArticulationCore::getCoefficentMatrixSize() const +{ + if (mSim) + return mSim->getCoefficentMatrixSize(); + + return 0; +} + +IG::NodeIndex Sc::ArticulationCore::getIslandNodeIndex() const +{ + if (mSim) + return mSim->getIslandNodeIndex(); + return IG::NodeIndex(IG_INVALID_NODE); +} + +void Sc::ArticulationCore::setGlobalPose() +{ + if (mSim) + mSim->setGlobalPose(); +} + +void Sc::ArticulationCore::setDirty(const bool dirty) +{ + if (mSim) + mSim->setDirty(dirty); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp new file mode 100644 index 000000000..f9a01b3b9 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp @@ -0,0 +1,300 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScArticulationJointCore.h" +#include "ScArticulationCore.h" +#include "ScArticulationJointSim.h" +#include "ScBodyCore.h" + + +using namespace physx; + +Sc::ArticulationJointCore::ArticulationJointCore(const PxTransform& parentFrame, + const PxTransform& childFrame, + PxArticulationBase::Enum type) : + mSim (NULL) +{ + PX_ASSERT(parentFrame.isValid()); + PX_ASSERT(childFrame.isValid()); + + mCore.parentPose = parentFrame; + mCore.childPose = childFrame; + + mCore.targetPosition = PxQuat(PxIdentity); + mCore.targetVelocity = PxVec3(0.f); + + mCore.driveType = PxArticulationJointDriveType::eTARGET; + + mCore.spring = 0.0f; + mCore.damping = 0.0f; + + mCore.internalCompliance = 1.0f; + mCore.externalCompliance = 1.0f; + + if (type == PxArticulationBase::eMaximumCoordinate) + { + PxReal swingYLimit = PxPi / 4; + PxReal swingZLimit = PxPi / 4; + mCore.limits[PxArticulationAxis::eSWING1].low = swingYLimit; + mCore.limits[PxArticulationAxis::eSWING1].high = swingYLimit; + mCore.limits[PxArticulationAxis::eSWING2].low = swingZLimit; + mCore.limits[PxArticulationAxis::eSWING2].high = swingZLimit; + mCore.swingLimitContactDistance = 0.05f; + + PxReal twistLimitLow = -PxPi / 4; + PxReal twistLimitHigh = PxPi / 4; + + mCore.limits[PxArticulationAxis::eTWIST].low = twistLimitLow; + mCore.limits[PxArticulationAxis::eTWIST].high = twistLimitHigh; + mCore.twistLimitContactDistance = 0.05f; + mCore.tanQSwingY = PxTan(swingYLimit / 4); + mCore.tanQSwingZ = PxTan(swingZLimit / 4); + mCore.tanQSwingPad = PxTan(mCore.swingLimitContactDistance / 4); + + mCore.tanQTwistHigh = PxTan(twistLimitHigh / 4); + mCore.tanQTwistLow = PxTan(twistLimitLow / 4); + mCore.tanQTwistPad = PxTan(mCore.twistLimitContactDistance / 4); + } + else + { + + for (PxU32 i = 0; i < 6; ++i) + { + mCore.limits[i].low = 0.f; + mCore.limits[i].high = 0.f; + mCore.drives[i].stiffness = 0.f; + mCore.drives[i].damping = 0.f; + mCore.drives[i].maxForce = 0.f; + mCore.targetP[i] = 0.f; + mCore.targetV[i] = 0.f; + } + + mCore.twistLimitContactDistance = 0.f; + mCore.tanQSwingY = 0.f; + mCore.tanQSwingZ = 0.f; + mCore.tanQSwingPad = 0.f; + + mCore.tanQTwistHigh = 0.f; + mCore.tanQTwistLow = 0.f; + mCore.tanQTwistPad = 0.f; + } + + mCore.swingLimited = false; + mCore.twistLimited = false; + mCore.prismaticLimited = false; + mCore.tangentialStiffness = 0.0f; + mCore.tangentialDamping = 0.0f; + + mCore.frictionCoefficient = 0.05f; + + mCore.jointType = PxArticulationJointType::eUNDEFINED; + + for(PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + mCore.motion[i] = PxArticulationMotion::eLOCKED; + +} + + +Sc::ArticulationJointCore::~ArticulationJointCore() +{ + PX_ASSERT(getSim() == 0); +} + + +//-------------------------------------------------------------- +// +// ArticulationJointCore interface implementation +// +//-------------------------------------------------------------- + + +void Sc::ArticulationJointCore::setParentPose(const PxTransform& t) +{ + mCore.parentPose = t; + mCore.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::ePOSE; + mArticulation->setDirty(true); +} + + +void Sc::ArticulationJointCore::setChildPose(const PxTransform& t) +{ + mCore.childPose = t; + mCore.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::ePOSE; + mArticulation->setDirty(true); +} + + +void Sc::ArticulationJointCore::setTargetOrientation(const PxQuat& p) +{ + mCore.targetPosition = p; +} + +void Sc::ArticulationJointCore::setTargetVelocity(const PxVec3& v) +{ + mCore.targetVelocity = v; +} + +void Sc::ArticulationJointCore::setDriveType(PxArticulationJointDriveType::Enum type) +{ + mCore.driveType = PxU8(type); +} + +void Sc::ArticulationJointCore::setJointType(PxArticulationJointType::Enum type) +{ + mCore.jointType = PxU8(type); +} + +PxArticulationJointType::Enum Sc::ArticulationJointCore::getJointType() const +{ + return PxArticulationJointType::Enum(mCore.jointType); +} + +void Sc::ArticulationJointCore::setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) +{ + mCore.motion[axis] = motion; + mCore.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::eMOTION; + mArticulation->setDirty(true); +} + +PxArticulationMotion::Enum Sc::ArticulationJointCore::getMotion(PxArticulationAxis::Enum axis) const +{ + return PxArticulationMotion::Enum(PxU8(mCore.motion[axis])); +} + +void Sc::ArticulationJointCore::setFrictionCoefficient(const PxReal coefficient) +{ + mCore.frictionCoefficient = coefficient; +} + +PxReal Sc::ArticulationJointCore::getFrictionCoefficient() const +{ + return mCore.frictionCoefficient; +} + +void Sc::ArticulationJointCore::setMaxJointVelocity(const PxReal maxJointV) +{ + mCore.maxJointVelocity = maxJointV; +} + +PxReal Sc::ArticulationJointCore::getMaxJointVelocity() const +{ + return mCore.maxJointVelocity; +} + +void Sc::ArticulationJointCore::setStiffness(PxReal s) +{ + mCore.spring = s; +} + + +void Sc::ArticulationJointCore::setDamping(PxReal d) +{ + mCore.damping = d; +} + + +void Sc::ArticulationJointCore::setInternalCompliance(PxReal r) +{ + mCore.internalCompliance = r; +} + +void Sc::ArticulationJointCore::setExternalCompliance(PxReal r) +{ + mCore.externalCompliance = r; +} + + +void Sc::ArticulationJointCore::setSwingLimit(PxReal yLimit, PxReal zLimit) +{ + mCore.limits[PxArticulationAxis::eSWING1].low = yLimit; + mCore.limits[PxArticulationAxis::eSWING2].low = zLimit; + + mCore.tanQSwingY = PxTan(yLimit/4); + mCore.tanQSwingZ = PxTan(zLimit/4); +} + + +void Sc::ArticulationJointCore::setTangentialStiffness(PxReal s) +{ + mCore.tangentialStiffness = s; +} + + +void Sc::ArticulationJointCore::setTangentialDamping(PxReal d) +{ + mCore.tangentialDamping = d; +} + + +void Sc::ArticulationJointCore::setSwingLimitEnabled(bool e) +{ + mCore.swingLimited = e; +} + +void Sc::ArticulationJointCore::setSwingLimitContactDistance(PxReal e) +{ + mCore.swingLimitContactDistance = e; + mCore.tanQSwingPad = PxTan(e/4); +} + + +void Sc::ArticulationJointCore::setTwistLimit(PxReal lower, PxReal upper) +{ + mCore.limits[PxArticulationAxis::eTWIST].low = lower; + mCore.limits[PxArticulationAxis::eTWIST].high = upper; + + mCore.tanQTwistHigh = PxTan(upper/4); + mCore.tanQTwistLow = PxTan(lower/4); +} + +void Sc::ArticulationJointCore::setTwistLimitEnabled(bool e) +{ + mCore.twistLimited = e; +} + +void Sc::ArticulationJointCore::setTwistLimitContactDistance(PxReal e) +{ + mCore.twistLimitContactDistance = e; + mCore.tanQTwistPad = PxTan(e/4); +} + +void Sc::ArticulationJointCore::setTargetP(PxArticulationAxis::Enum axis, PxReal targetP) +{ + mCore.targetP[axis] = targetP; + mCore.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::eTARGETPOSE; + mArticulation->setDirty(true); +} + +void Sc::ArticulationJointCore::setTargetV(PxArticulationAxis::Enum axis, PxReal targetV) +{ + mCore.targetV[axis] = targetV; + mCore.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::eTARGETVELOCITY; + mArticulation->setDirty(true); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp new file mode 100644 index 000000000..a0127f707 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScArticulationJointSim.h" +#include "ScArticulationJointCore.h" +#include "ScBodySim.h" +#include "ScScene.h" +#include "PxsRigidBody.h" +#include "DyArticulation.h" +#include "ScArticulationSim.h" +#include "PxsSimpleIslandManager.h" + +using namespace physx; + +Sc::ArticulationJointSim::ArticulationJointSim(ArticulationJointCore& joint, ActorSim& parent, ActorSim& child) : + Interaction (parent, child, InteractionType::eARTICULATION, 0), + mCore (joint) +{ + registerInActors(); + + BodySim& childBody = static_cast(child), + & parentBody = static_cast(parent); + + parentBody.getArticulation()->addBody(childBody, &parentBody, this); + + mCore.setSim(this); +} + +Sc::ArticulationJointSim::~ArticulationJointSim() +{ + // articulation interactions do not make use of the dirty flags yet. If they did, a setClean(true) has to be introduced here. + PX_ASSERT(!readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)); + PX_ASSERT(!getDirtyFlags()); + + unregisterFromActors(); + + BodySim& child = getChild(); + child.getArticulation()->removeBody(child); + + mCore.setSim(NULL); +} + +Sc::BodySim& Sc::ArticulationJointSim::getParent() const +{ + return static_cast(getActorSim0()); +} + +Sc::BodySim& Sc::ArticulationJointSim::getChild() const +{ + return static_cast(getActorSim1()); +} + +bool Sc::ArticulationJointSim::onActivate_(void*) +{ + if(!(getParent().isActive() && getChild().isActive())) + return false; + + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; +} + +bool Sc::ArticulationJointSim::onDeactivate_() +{ + clearInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h new file mode 100644 index 000000000..7924b3c18 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h @@ -0,0 +1,71 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_ARTICULATION_JOINT_SIM +#define PX_PHYSICS_SCP_ARTICULATION_JOINT_SIM + +#include "ScInteraction.h" +#include "DyArticulation.h" + +namespace physx +{ +namespace Sc +{ + class ArticulationJointCore; + class BodySim; + + class ArticulationJointSim : public Interaction + { + ArticulationJointSim& operator=(const ArticulationJointSim &); + + public: + ArticulationJointSim(ArticulationJointCore& joint, ActorSim& parent, ActorSim& child); + ~ArticulationJointSim(); + + bool onActivate_(void*); + bool onDeactivate_(); + + PX_FORCE_INLINE ArticulationJointCore& getCore() const { return mCore; } + + BodySim& getParent() const; + BodySim& getChild() const; + + //--------------------------------------------------------------------------------- + // Low Level data access + //--------------------------------------------------------------------------------- + private: + ArticulationJointCore& mCore; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp new file mode 100644 index 000000000..d89b6ce2e --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp @@ -0,0 +1,813 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScArticulationSim.h" +#include "ScArticulationCore.h" +#include "ScArticulationJointSim.h" +#include "ScArticulationJointCore.h" +#include "ScBodySim.h" +#include "ScConstraintSim.h" +#include "ScScene.h" + +#include "DyArticulation.h" +#include "DyConstraint.h" +#include "DyFeatherstoneArticulation.h" +#include "PxsContext.h" +#include "CmSpatialVector.h" +#include "PsVecMath.h" +#include "PxsSimpleIslandManager.h" +#include "ScShapeSim.h" + +using namespace physx; +using namespace physx::Dy; + + +Sc::ArticulationSim::ArticulationSim(ArticulationCore& core, Scene& scene, BodyCore& root) : + mLLArticulation(NULL), + mScene(scene), + mCore(core), + mLinks (PX_DEBUG_EXP("ScArticulationSim::links")), + mBodies (PX_DEBUG_EXP("ScArticulationSim::bodies")), + mJoints (PX_DEBUG_EXP("ScArticulationSim::joints")), + mMaxDepth(0) +{ + mLinks.reserve(16); + mJoints.reserve(16); + mBodies.reserve(16); + + mLLArticulation = mScene.createLLArticulation(this); + + + mIslandNodeIndex = scene.getSimpleIslandManager()->addArticulation(this, mLLArticulation, false); + + if(!mLLArticulation) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Articulation: could not allocate low-level resources."); + return; + } + + mLLArticulation->setDirty(true); + + PX_ASSERT(root.getSim()); + + addBody(*root.getSim(), NULL, NULL); + + + + mCore.setSim(this); + + mLLArticulation->setDyContext(mScene.getDynamicsContext()); + mLLArticulation->getSolverDesc().core = &core.getCore(); + mLLArticulation->getSolverDesc().internalLoads = NULL; + mLLArticulation->getSolverDesc().externalLoads = NULL; + mLLArticulation->getSolverDesc().articulation = NULL; + mLLArticulation->getSolverDesc().poses = NULL; + mLLArticulation->getSolverDesc().motionVelocity = NULL; + mLLArticulation->getSolverDesc().acceleration = NULL; + mLLArticulation->getSolverDesc().totalDataSize = 0; + mLLArticulation->getSolverDesc().solverDataSize = 0; + mLLArticulation->getSolverDesc().linkCount = 0; + mLLArticulation->getSolverDesc().scratchMemory = NULL; + mLLArticulation->getSolverDesc().scratchMemorySize = 0; + + //mLLArticulation->onUpdateSolverDesc(); +} + + +Sc::ArticulationSim::~ArticulationSim() +{ + if (!mLLArticulation) + return; + + mScene.destroyLLArticulation(*mLLArticulation); + + mScene.getSimpleIslandManager()->removeNode(mIslandNodeIndex); + + mCore.setSim(NULL); +} + +PxU32 Sc::ArticulationSim::findBodyIndex(BodySim& body) const +{ + for(PxU32 i=0; igetBody(0); + BodySim* bodySim1 = constraintSim->getBody(1); + + ArticulationLoopConstraint lConstraint; + if (bodySim0) + lConstraint.linkIndex0 = findBodyIndex(*bodySim0); + else + lConstraint.linkIndex0 = 0x80000000; + + if(bodySim1) + lConstraint.linkIndex1 = findBodyIndex(*bodySim1); + else + lConstraint.linkIndex1 = 0x80000000; + + lConstraint.constraint = &constraintSim->getLowLevelConstraint(); + + mLoopConstraints.pushBack(lConstraint); +} + +void Sc::ArticulationSim::removeLoopConstraint(ConstraintSim* constraintSim) +{ + Dy::Constraint* constraint = &constraintSim->getLowLevelConstraint(); + + const PxU32 size = mLoopConstraints.size(); + PxU32 index = 0; + while (index < size && mLoopConstraints[index].constraint != constraint) + ++index; + + if (index != size) + mLoopConstraints.replaceWithLast(index); +} + +void Sc::ArticulationSim::updateCached(Cm::BitMapPinned* shapeChangedMap) +{ + for(PxU32 i=0; iupdateCached(shapeChangedMap); +} + +void Sc::ArticulationSim::markShapesUpdated(Cm::BitMapPinned* shapeChangedMap) +{ + for (PxU32 a = 0; a < mBodies.size(); ++a) + { + Sc::ElementSim* current = mBodies[a]->getElements_(); + while (current) + { + Sc::ShapeSim* sim = static_cast(current); + if (sim->isInBroadPhase()) + shapeChangedMap->growAndSet(sim->getElementID()); + current = current->mNextInActor; + } + } +} + +void Sc::ArticulationSim::updateContactDistance(PxReal* contactDistance, const PxReal dt, Bp::BoundsArray& boundsArray) +{ + for (PxU32 i = 0; iupdateContactDistance(contactDistance, dt, boundsArray); +} + +ArticulationLinkHandle Sc::ArticulationSim::getLinkHandle(BodySim &body) const +{ + return reinterpret_cast(mLLArticulation) | findBodyIndex(body); +} + +void Sc::ArticulationSim::addBody(BodySim& body, + BodySim* parent, + ArticulationJointSim* joint) +{ + mBodies.pushBack(&body); + mJoints.pushBack(joint); + mLLArticulation->addBody(); + + PxU32 index = mLinks.size(); + + PX_ASSERT((((index==0) && (joint == 0)) && (parent == 0)) || + (((index!=0) && joint) && (parent && (parent->getArticulation() == this)))); + + ArticulationLink &link = mLinks.insert(); + link.body = &body.getLowLevelBody(); + link.bodyCore = &body.getBodyCore().getCore(); + link.children = 0; + bool shouldSleep; + bool currentlyAsleep; + bool bodyReadyForSleep = body.checkSleepReadinessBesidesWakeCounter(); + PxReal wakeCounter = getCore().getWakeCounter(); + + if(parent) + { + currentlyAsleep = !mBodies[0]->isActive(); + shouldSleep = currentlyAsleep && bodyReadyForSleep; + + PxU32 parentIndex = findBodyIndex(*parent); + link.parent = parentIndex; + link.pathToRoot = mLinks[parentIndex].pathToRoot | ArticulationBitField(1)<getCore().getCore(); + mLinks[parentIndex].children |= ArticulationBitField(1)<> 32); + const PxU32 depth = Ps::bitCount(low) + Ps::bitCount(high); + mMaxDepth = PxMax(depth, mMaxDepth); + + mLLArticulation->setMaxDepth(mMaxDepth); + + if (currentlyAsleep && (!shouldSleep)) + { + for(PxU32 i=0; i < (mBodies.size() - 1); i++) + mBodies[i]->internalWakeUpArticulationLink(wakeCounter); + } + + body.setArticulation(this, wakeCounter, shouldSleep, index); +} + + +void Sc::ArticulationSim::removeBody(BodySim &body) +{ + PX_ASSERT(body.getArticulation() == this); + PxU32 index = findBodyIndex(body); + body.setArticulation(NULL, 0.0f, true, 0); + + ArticulationLink &link0 = mLinks[index]; + + PX_ASSERT(link0.children == 0); + PX_UNUSED(link0); + + // copy all the later links down by one + for(PxU32 i=index+1;iindex) + link.pathToRoot = (link.pathToRoot&fixedIndices) | (link.pathToRoot&shiftIndices)>>1; + link.children = (link.children&fixedIndices) | (link.children&shiftIndices)>>1; + + const PxU32 low = PxU32(link.pathToRoot & 0xffffffff); + const PxU32 high = PxU32(link.pathToRoot >> 32); + const PxU32 depth = Ps::bitCount(low) + Ps::bitCount(high); + mMaxDepth = PxMax(depth, mMaxDepth); + } + + mLinks.popBack(); + + mLLArticulation->setMaxDepth(mMaxDepth); + mLLArticulation->removeBody(); +} + + +void Sc::ArticulationSim::checkResize() const +{ + if(!mBodies.size()) + return; + + //if this is needed, we need to re-allocated the link data + mLLArticulation->resize(mLinks.size()); + + mLLArticulation->getSolverDesc().links = const_cast(mLinks.begin()); + mLLArticulation->getSolverDesc().linkCount = Ps::to8(mLinks.size()); + + //if this is needed, we need to re-allocated the joint data + mLLArticulation->onUpdateSolverDesc(); +} + +PxU32 Sc::ArticulationSim::getCCDLinks(BodySim** sims) +{ + PxU32 nbCCDBodies = 0; + for (PxU32 a = 0; a < mBodies.size(); ++a) + { + if (mBodies[a]->getLowLevelBody().getCore().mFlags & PxRigidBodyFlag::eENABLE_CCD) + { + sims[nbCCDBodies++] = mBodies[a]; + } + } + return nbCCDBodies; +} + +void Sc::ArticulationSim::sleepCheck(PxReal dt) +{ + if(!mBodies.size()) + return; + +#if PX_CHECKED + { + PxReal maxTimer = 0.0f, minTimer = PX_MAX_F32; + bool allActive = true, noneActive = true; + for(PxU32 i=0;igetBodyCore().getWakeCounter(); + maxTimer = PxMax(maxTimer, timer); + minTimer = PxMin(minTimer, timer); + bool active = mBodies[i]->isActive(); + allActive &= active; + noneActive &= !active; + } + // either all links are asleep, or no links are asleep + PX_ASSERT(maxTimer==0 || minTimer!=0); + PX_ASSERT(allActive || noneActive); + } + +#endif + + if(!mBodies[0]->isActive()) + return; + + PxReal sleepThreshold = getCore().getCore().sleepThreshold; + + PxReal maxTimer = 0.0f, minTimer = PX_MAX_F32; + + for(PxU32 i=0;igetMotionVelocity(i); + PxReal timer = mBodies[i]->updateWakeCounter(dt, sleepThreshold, motionVelocity); + maxTimer = PxMax(maxTimer, timer); + minTimer = PxMin(minTimer, timer); + } + + mCore.setWakeCounterInternal(maxTimer); + + if(maxTimer != 0.0f) + { + if(minTimer == 0.0f) + { + // make sure nothing goes to sleep unless everything does + for(PxU32 i=0;igetBodyCore().setWakeCounterFromSim(PxMax(1e-6f, mBodies[i]->getBodyCore().getWakeCounter())); + } + return; + } + + for(PxU32 i=0;inotifyReadyForSleeping(); + mBodies[i]->resetSleepFilter(); + } + + mScene.getSimpleIslandManager()->deactivateNode(mIslandNodeIndex); +} + +bool Sc::ArticulationSim::isSleeping() const +{ + bool isActive = mBodies[0]->isActive(); + PX_UNUSED(isActive); + return (mBodies.size() > 0) ? (!mBodies[0]->isActive()) : true; +} + +void Sc::ArticulationSim::internalWakeUp(PxReal wakeCounter) +{ + if(mCore.getWakeCounter() < wakeCounter) + { + mCore.setWakeCounterInternal(wakeCounter); + for(PxU32 i=0;iinternalWakeUpArticulationLink(wakeCounter); + } +} + +void Sc::ArticulationSim::setActive(const bool b, const PxU32 infoFlag) +{ + for(PxU32 i=0;isetActive(b, infoFlag); + } +} + +void Sc::ArticulationSim::updateForces(PxReal dt, bool simUsesAdaptiveForce) +{ + PxU32 count = 0; + + + for(PxU32 i=0;igetType(); + const bool useAccelerations = (type == PxArticulationBase::Enum::eReducedCoordinate); + + mBodies[i]->updateForces(dt, NULL, NULL, count, &mLLArticulation->getSolverDesc().acceleration[i], + useAccelerations, simUsesAdaptiveForce); + } +} + +void Sc::ArticulationSim::saveLastCCDTransform() +{ + for(PxU32 i=0;igetLowLevelBody().saveLastCCDTransform(); + } +} + + +Sc::ArticulationDriveCache* Sc::ArticulationSim::createDriveCache(PxReal compliance, + PxU32 driveIterations) const +{ + + checkResize(); + PxU32 solverDataSize, totalSize, scratchSize; + getLowLevelArticulation()->getDataSizes(mLinks.size(), solverDataSize, totalSize, scratchSize); + + // In principle we should only need solverDataSize here. But right now prepareFsData generates the auxiliary data + // for use in potential debugging, which takes up extra space. + FsData* data = reinterpret_cast(PX_ALLOC(totalSize,"Articulation Drive Cache")); + PxvArticulationDriveCache::initialize(*data, Ps::to16(mLinks.size()), mLinks.begin(), compliance, driveIterations, mLLArticulation->getSolverDesc().scratchMemory, mLLArticulation->getSolverDesc().scratchMemorySize); + + return data; +} + + +void Sc::ArticulationSim::updateDriveCache(ArticulationDriveCache& cache, + PxReal compliance, + PxU32 driveIterations) const +{ + checkResize(); + PxvArticulationDriveCache::initialize(cache, Ps::to16(mLinks.size()), mLinks.begin(), compliance, driveIterations, + mLLArticulation->getSolverDesc().scratchMemory, mLLArticulation->getSolverDesc().scratchMemorySize); +} + + +void Sc::ArticulationSim::releaseDriveCache(Sc::ArticulationDriveCache& driveCache) const +{ + PX_FREE(&driveCache); +} + + +void Sc::ArticulationSim::applyImpulse(Sc::BodyCore& link, + const Sc::ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) +{ + Cm::SpatialVectorV v[DY_ARTICULATION_MAX_SIZE], z[DY_ARTICULATION_MAX_SIZE]; + PxMemZero(z, mLinks.size()*sizeof(Cm::SpatialVector)); + PxMemZero(v, mLinks.size()*sizeof(Cm::SpatialVector)); + + PxU32 bodyIndex = findBodyIndex(*link.getSim()); + z[bodyIndex].linear = Ps::aos::V3LoadU(-force); + z[bodyIndex].angular = Ps::aos::V3LoadU(-torque); + + PxvArticulationDriveCache::applyImpulses(driveCache, z, v); + for(PxU32 i=0;igetBodyCore(); + PxVec3 lv, av; + Ps::aos::V3StoreU(v[i].linear, lv); + Ps::aos::V3StoreU(v[i].angular, av); + + body.setLinearVelocity(body.getLinearVelocity()+lv); + body.setAngularVelocity(body.getAngularVelocity()+av); + } +} + +void Sc::ArticulationSim::computeImpulseResponse(Sc::BodyCore& link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const Sc::ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const +{ + Cm::SpatialVectorV v; + PxvArticulationDriveCache::getImpulseResponse(driveCache, findBodyIndex(*link.getSim()), Cm::SpatialVectorV(Ps::aos::V3LoadU(force), Ps::aos::V3LoadU(torque)), v); + Ps::aos::V3StoreU(v.linear, linearResponse); + Ps::aos::V3StoreU(v.angular, angularResponse); +} + +PxU32 Sc::ArticulationSim::getDofs() const +{ + return mLLArticulation->getDofs(); +} + +PxU32 Sc::ArticulationSim::getDof(const PxU32 linkID) const +{ + return mLLArticulation->getDof(linkID); +} + +PxArticulationCache* Sc::ArticulationSim::createCache() const +{ + + checkResize(); + + PxU32 totalSize = getCacheDataSize() + sizeof(PxArticulationCache); + + PxU8* tCache = reinterpret_cast(PX_ALLOC(totalSize, "Articulation cache")); + + PxMemZero(tCache, totalSize); + + const PxU32 totalDofs = mLLArticulation->getDofs(); + + PxArticulationCache* cache = reinterpret_cast(tCache); + + PxU32 offset = sizeof(PxArticulationCache); + cache->externalForces = reinterpret_cast(tCache + offset); + + offset += sizeof(Cm::SpatialVector) * mLinks.size(); + + cache->jacobian = reinterpret_cast(tCache + offset); + + offset += sizeof(PxReal) * totalDofs * 6;//maximum 6 columns for coefficent matrix + cache->massMatrix = reinterpret_cast(tCache + offset); + + offset += sizeof(PxReal) *totalDofs * totalDofs; + cache->jointVelocity = reinterpret_cast(tCache + offset); + + offset += sizeof(PxReal) * totalDofs; + cache->jointAcceleration = reinterpret_cast(tCache + offset); + + offset += sizeof(PxReal) * totalDofs; + cache->jointPosition = reinterpret_cast(tCache + offset); + + offset += sizeof(PxReal) * totalDofs; + cache->jointForce = reinterpret_cast(tCache + offset); + + offset += sizeof(void*); + cache->coefficentMatrix = reinterpret_cast(tCache + offset); + + offset += sizeof(void*); + cache->lambda = reinterpret_cast(tCache + offset); + + const PxU32 scratchMemorySize = getScratchMemorySize(); + void* scratchMemory = PX_ALLOC(scratchMemorySize, "Cache scratch memory"); + cache->scratchMemory = scratchMemory; + + cache->scratchAllocator = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxcScratchAllocator), "PxScrachAllocator"), PxcScratchAllocator)(); + + reinterpret_cast(cache->scratchAllocator)->setBlock(scratchMemory, scratchMemorySize); + + return cache; +} + +PxU32 Sc::ArticulationSim::getCacheDataSize() const +{ + const PxU32 totalDofs = mLLArticulation->getDofs(); + + const PxU32 jointCount = mLinks.size() - 1; + PxU32 totalSize = + sizeof(Cm::SpatialVector) * mLinks.size() //external force + + sizeof(PxKinematicJacobian) * jointCount //jacobian matrix + + sizeof(PxReal) * totalDofs * totalDofs //mass matrix + + sizeof(PxReal) * totalDofs * 4 //jointVelocity, jointAcceleration, jointPosition, joint force + + sizeof(void*) * 2 //pointer to coefficent matrix and pointer to lambda vector + + sizeof(PxArticulationRootLinkData) //root link data + + sizeof(void*) * 2 //pointer to scratch memory/scratch memory allocator + + sizeof(PxU32); //version + + return totalSize; +} + +PxU32 Sc::ArticulationSim::getCacheConstantDataSize() const +{ + PxU32 totalSize = sizeof(void*) * 2 + sizeof(PxU32); //pointer to scratch memory & scrach memory allocator, version + + return totalSize; +} + +PxU32 Sc::ArticulationSim::getScratchMemorySize() const +{ + const PxU32 totalDofs = mLLArticulation->getDofs(); + const PxU32 linkCount = mLinks.size(); + + PxU32 totalSize = + sizeof(Cm::SpatialVectorF) * linkCount * 5 //motionVelocity, motionAccelerations, coriolisVectors, spatialZAVectors, externalAccels; + + sizeof(Dy::SpatialMatrix) * linkCount //compositeSpatialInertias; + + sizeof(PxReal) * totalDofs * 5; //jointVelocity, jointAcceleration, jointForces, jointPositions, jointFrictionForces + + return totalSize; +} + + +void Sc::ArticulationSim::zeroCache(PxArticulationCache& cache) const +{ + const PxU32 cacheDataSize = getCacheDataSize(); + const PxU32 constantSize = getCacheConstantDataSize(); + + //don't zero gravity, dt and version + PxMemZero(cache.externalForces, (cacheDataSize - constantSize)); +} + +//copy external data to internal data +void Sc::ArticulationSim::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const +{ + //checkResize(); + mLLArticulation->applyCache(cache, flag); +} + +//copy internal data to external data +void Sc::ArticulationSim::copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const +{ + mLLArticulation->copyInternalStateToCache(cache, flag); +} + +//release cache +void Sc::ArticulationSim::releaseCache(PxArticulationCache& cache) const +{ + if (cache.scratchAllocator) + { + PxcScratchAllocator* scratchAlloc = reinterpret_cast(cache.scratchAllocator); + scratchAlloc->~PxcScratchAllocator(); + PX_FREE_AND_RESET(cache.scratchAllocator); + } + + if (cache.scratchMemory) + PX_FREE_AND_RESET(cache.scratchMemory); + + PX_FREE(&cache); +} + +void Sc::ArticulationSim::packJointData(const PxReal* maximum, PxReal* reduced) const +{ + mLLArticulation->packJointData(maximum, reduced); +} + +void Sc::ArticulationSim::unpackJointData(const PxReal* reduced, PxReal* maximum) const +{ + mLLArticulation->unpackJointData(reduced, maximum); +} + +void Sc::ArticulationSim::commonInit() +{ + mLLArticulation->initializeCommonData(); +} + +void Sc::ArticulationSim::computeGeneralizedGravityForce(PxArticulationCache& cache) +{ + mLLArticulation->getGeneralizedGravityForce(mScene.getGravityFast(), cache); +} + +void Sc::ArticulationSim::computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) +{ + mLLArticulation->getCoriolisAndCentrifugalForce(cache); +} + +void Sc::ArticulationSim::computeGeneralizedExternalForce(PxArticulationCache& cache) +{ + mLLArticulation->getGeneralizedExternalForce(cache); +} + +void Sc::ArticulationSim::computeJointAcceleration(PxArticulationCache& cache) +{ + mLLArticulation->getJointAcceleration(mScene.getGravityFast(), cache); +} + +void Sc::ArticulationSim::computeJointForce(PxArticulationCache& cache) +{ + mLLArticulation->getJointForce(cache); +} + +void Sc::ArticulationSim::computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) +{ + mLLArticulation->getKinematicJacobian(linkID, cache); +} + +void Sc::ArticulationSim::computeCoefficentMatrix(PxArticulationCache& cache) +{ + mLLArticulation->getCoefficentMatrixWithLoopJoints(mLoopConstraints.begin(), mLoopConstraints.size(), cache); +} + +bool Sc::ArticulationSim::computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, + const PxReal* const jointTorque, const PxVec3 gravity, const PxU32 maxIter) +{ + return mLLArticulation->getLambda(mLoopConstraints.begin(), mLoopConstraints.size(), cache, initialState, jointTorque, gravity, maxIter); + +} + +void Sc::ArticulationSim::computeGeneralizedMassMatrix(PxArticulationCache& cache) +{ + + mLLArticulation->getGeneralizedMassMatrixCRB(cache); + + + /*const PxU32 totalDofs = mLLArticulation->getDofs(); + + PxReal* massMatrix = reinterpret_cast(PX_ALLOC(sizeof(PxReal) * totalDofs * totalDofs, "MassMatrix")); + PxMemCopy(massMatrix, cache.massMatrix, sizeof(PxReal)*totalDofs * totalDofs); + + mLLArticulation->getGeneralizedMassMatrix(cache); + + PxReal* massMatrix1 = cache.massMatrix; + for (PxU32 i = 0; i < totalDofs; ++i) + { + PxReal* row = &massMatrix1[i * totalDofs]; + + for (PxU32 j = 0; j < totalDofs; ++j) + { + const PxReal dif = row[j] - massMatrix[j*totalDofs + i]; + if (PxAbs(dif) > 2e-4f) + { + int bob = 0; + PX_UNUSED(bob); + } + } + + } + + PX_FREE(massMatrix);*/ + +} + +PxU32 Sc::ArticulationSim::getCoefficentMatrixSize() const +{ + const PxU32 size = mLoopConstraints.size(); + const PxU32 totalDofs = mLLArticulation->getDofs(); + return sizeof(PxReal) * size * totalDofs; +} + +// This method allows user teleport the root links and the articulation +//system update all other links pose +void Sc::ArticulationSim::setGlobalPose() +{ + checkResize(); + mLLArticulation->teleportRootLink(); +} + +void Sc::ArticulationSim::setDirty(const bool dirty) +{ + mLLArticulation->setDirty(dirty); +} + +void Sc::ArticulationSim::debugCheckWakeCounterOfLinks(PxReal wakeCounter) const +{ + PX_UNUSED(wakeCounter); + +#ifdef _DEBUG + // make sure the links are in sync with the articulation + for(PxU32 i=0; i < mBodies.size(); i++) + { + PX_ASSERT(mBodies[i]->getBodyCore().getWakeCounter() == wakeCounter); + } +#endif +} + +void Sc::ArticulationSim::debugCheckSleepStateOfLinks(bool isSleeping) const +{ + PX_UNUSED(isSleeping); + +#ifdef _DEBUG + // make sure the links are in sync with the articulation + for(PxU32 i=0; i < mBodies.size(); i++) + { + if (isSleeping) + { + PX_ASSERT(!mBodies[i]->isActive()); + PX_ASSERT(mBodies[i]->getBodyCore().getWakeCounter() == 0.0f); + PX_ASSERT(mBodies[i]->checkSleepReadinessBesidesWakeCounter()); + } + else + PX_ASSERT(mBodies[i]->isActive()); + } +#endif +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h new file mode 100644 index 000000000..e52000b5d --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h @@ -0,0 +1,213 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_ARTICULATION_SIM +#define PX_PHYSICS_ARTICULATION_SIM + + +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "DyArticulation.h" +#include "ScArticulationCore.h" +#include "PxsSimpleIslandManager.h" + +namespace physx +{ + +namespace Dy +{ + class Articulation; +} + +class PxsTransformCache; +class PxsSimulationController; +class PxJoint; + +namespace Cm +{ + class SpatialVector; + template class BitMapBase; + typedef BitMapBase BitMap; +} + +namespace Bp +{ + class BoundsArray; +} + +namespace Sc +{ + + class BodySim; + class ArticulationJointSim; + class ArticulationCore; + class Scene; + class ConstraintSim; + + + class ArticulationSim : public Ps::UserAllocated + { + public: + ArticulationSim(ArticulationCore& core, + Scene& scene, + BodyCore& root); + + ~ArticulationSim(); + + PX_INLINE Dy::ArticulationV* getLowLevelArticulation() const { return mLLArticulation; } + PX_INLINE ArticulationCore& getCore() const { return mCore; } + + void addBody(BodySim& body, + BodySim* parent, + ArticulationJointSim* joint); + void removeBody(BodySim &sim); + + Dy::ArticulationLinkHandle getLinkHandle(BodySim& body) const; + + void checkResize() const; // resize LL memory if necessary + + void debugCheckWakeCounterOfLinks(PxReal wakeCounter) const; + void debugCheckSleepStateOfLinks(bool isSleeping) const; + + bool isSleeping() const; + void internalWakeUp(PxReal wakeCounter); // called when sim sets sleep timer + void sleepCheck(PxReal dt); + PxU32 getCCDLinks(BodySim** sims); + void updateCached(Cm::BitMapPinned* shapehapeChangedMap); + void markShapesUpdated(Cm::BitMapPinned* shapeChangedMap); + void updateContactDistance(PxReal* contactDistance, const PxReal dt, Bp::BoundsArray& boundsArray); + + void setActive(const bool b, const PxU32 infoFlag=0); + + void updateForces(PxReal dt, bool simUsesAdaptiveForce); + void saveLastCCDTransform(); + + // drive cache implementation + // + ArticulationDriveCache* + createDriveCache(PxReal compliance, + PxU32 driveIterations) const; + + void updateDriveCache(ArticulationDriveCache& cache, + PxReal compliance, + PxU32 driveIterations) const; + + void releaseDriveCache(ArticulationDriveCache& cache) const; + + void applyImpulse(BodyCore& link, + const ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque); + + void computeImpulseResponse(BodyCore& link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const; + //external reduced coordinate implementation + PxU32 getDofs() const; + + //This function return the dof of the inbound joint, which belong to a link with corresponding linkID + PxU32 getDof(const PxU32 linkID) const; + + PxArticulationCache* createCache() const; + + PxU32 getCacheDataSize() const; + + PxU32 getCacheConstantDataSize() const; + + PxU32 getScratchMemorySize() const; + + void zeroCache(PxArticulationCache&) const; + + void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const; + + void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const; + + void releaseCache(PxArticulationCache&) const; + + void packJointData(const PxReal* maximum, PxReal* reduced) const; + + void unpackJointData(const PxReal* reduced, PxReal* maximum) const; + + void commonInit(); + + void computeGeneralizedGravityForce(PxArticulationCache& cache); + + void computeCoriolisAndCentrifugalForce(PxArticulationCache& cache); + + void computeGeneralizedExternalForce(PxArticulationCache& cache); + + void computeJointAcceleration(PxArticulationCache& cache); + + void computeJointForce(PxArticulationCache& cache); + + void computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache); + + void computeCoefficentMatrix(PxArticulationCache& cache); + + bool computeLambda(PxArticulationCache& cache, PxArticulationCache& rollBackCache, const PxReal* jointTorque, const PxVec3 gravity, const PxU32 maxIter); + + void computeGeneralizedMassMatrix(PxArticulationCache& cache); + + PxU32 getCoefficentMatrixSize() const; + //internal method implementation + PX_FORCE_INLINE IG::NodeIndex getIslandNodeIndex() const { return mIslandNodeIndex; } + + void setGlobalPose(); + + void setDirty(const bool dirty); + PxU32 findBodyIndex(BodySim &body) const; + + void addLoopConstraint(ConstraintSim* constraint); + void removeLoopConstraint(ConstraintSim* constraint); + + PxU32 getMaxDepth() { return mMaxDepth; } + + private: + ArticulationSim& operator=(const ArticulationSim&); + + Dy::ArticulationV* mLLArticulation; + Scene& mScene; + ArticulationCore& mCore; + Ps::Array mLinks; + Ps::Array mBodies; + Ps::Array mJoints; + IG::NodeIndex mIslandNodeIndex; + Ps::Array mLoopConstraints; + PxU32 mMaxDepth; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp new file mode 100644 index 000000000..289716af7 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp @@ -0,0 +1,717 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScBodyCore.h" +#include "ScBodySim.h" +#include "ScPhysics.h" +#include "ScScene.h" +#include "PxsSimulationController.h" +#include "PsFoundation.h" + +using namespace physx; + +static void updateBodySim(Sc::BodySim* bodySim) +{ + const bool isArticulationLink = bodySim->isArticulationLink(); + bodySim->getScene().getSimulationController()->updateDynamic(isArticulationLink, bodySim->getNodeIndex()); +} + +static void updateBodySim(Sc::BodyCore& bodyCore) +{ + Sc::BodySim* bodySim = bodyCore.getSim(); + if(bodySim) + updateBodySim(bodySim); +} + +Sc::BodyCore::BodyCore(PxActorType::Enum type, const PxTransform& bodyPose) +: RigidCore(type) +{ + const PxTolerancesScale& scale = Physics::getInstance().getTolerancesScale(); + // sizeof(BodyCore) = 176 => 160 => 144 => 160 bytes + + mCore.wakeCounter = Sc::Physics::sWakeCounterOnCreation; + mCore.inverseInertia = PxVec3(1.0f); + mCore.inverseMass = 1.0f; + mCore.body2World = bodyPose; + + PX_ASSERT(mCore.body2World.p.isFinite()); + PX_ASSERT(mCore.body2World.q.isFinite()); + + mCore.sleepThreshold = 5e-5f * scale.speed * scale.speed; + mCore.freezeThreshold = 2.5e-5f * scale.speed * scale.speed; + mSimStateData = NULL; + mCore.maxPenBias = -1e32f;//-PX_MAX_F32; + mCore.mFlags = PxRigidBodyFlags(); + mCore.linearVelocity = PxVec3(0.0f); + mCore.angularVelocity = PxVec3(0.0f); + mCore.linearDamping = 0.0f; + mCore.solverIterationCounts = (1 << 8) | 4; + mCore.contactReportThreshold = PX_MAX_F32; + mCore.setBody2Actor(PxTransform(PxIdentity)); + mCore.ccdAdvanceCoefficient = 0.15f; + mCore.maxContactImpulse = 1e32f;// PX_MAX_F32; + mCore.isFastMoving = false; + mCore.lockFlags = PxRigidDynamicLockFlags(0); + + if(type == PxActorType::eRIGID_DYNAMIC) + { + mCore.angularDamping = 0.05f; + mCore.maxAngularVelocitySq = 100.0f * 100.0f; +// mCore.maxAngularVelocitySq = 7.0f * 7.0f; + mCore.maxLinearVelocitySq = 1e32f; //PX_MAX_F32; + } + else + { + mCore.linearDamping = 0.05f; + mCore.angularDamping = 0.05f; + mCore.maxAngularVelocitySq = 50.0f * 50.0f; + mCore.maxLinearVelocitySq = 100.f * 100.f * scale.length * scale.length; + } +} + +Sc::BodyCore::~BodyCore() +{ + PX_ASSERT(getSim() == 0); + PX_ASSERT(!mSimStateData); +} + +Sc::BodySim* Sc::BodyCore::getSim() const +{ + return static_cast(Sc::ActorCore::getSim()); +} + +size_t Sc::BodyCore::getSerialCore(PxsBodyCore& serialCore) +{ + serialCore = mCore; + if(mSimStateData && mSimStateData->isKine()) + { + const Kinematic* kine = mSimStateData->getKinematicData(); + serialCore.inverseMass = kine->backupInvMass; + serialCore.inverseInertia = kine->backupInverseInertia; + serialCore.linearDamping = kine->backupLinearDamping; + serialCore.angularDamping = kine->backupAngularDamping; + serialCore.maxAngularVelocitySq = kine->backupMaxAngVelSq; + serialCore.maxLinearVelocitySq = kine->backupMaxLinVelSq; + } + return reinterpret_cast(&mCore); +} + +//-------------------------------------------------------------- +// +// BodyCore interface implementation +// +//-------------------------------------------------------------- + +void Sc::BodyCore::setBody2World(const PxTransform& p) +{ + mCore.body2World = p; + PX_ASSERT(p.p.isFinite()); + PX_ASSERT(p.q.isFinite()); + + BodySim* sim = getSim(); + if(sim) + { + sim->postBody2WorldChange(); + updateBodySim(sim); + } +} + +void Sc::BodyCore::setLinearVelocity(const PxVec3& v) +{ + mCore.linearVelocity = v; + + updateBodySim(*this); +} + +void Sc::BodyCore::setAngularVelocity(const PxVec3& v) +{ + mCore.angularVelocity = v; + + updateBodySim(*this); +} + +void Sc::BodyCore::setBody2Actor(const PxTransform& p) +{ + PX_ASSERT(p.p.isFinite()); + PX_ASSERT(p.q.isFinite()); + + mCore.setBody2Actor(p); + + BodySim* sim = getSim(); + if(sim) + { + sim->notifyShapesOfTransformChange(); + updateBodySim(sim); + } +} + +void Sc::BodyCore::addSpatialAcceleration(Ps::Pool* simStateDataPool, const PxVec3* linAcc, const PxVec3* angAcc) +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if(sim) + sim->notifyAddSpatialAcceleration(); + + if(!mSimStateData || !mSimStateData->isVelMod()) + setupSimStateData(simStateDataPool, false); + + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->notifyAddAcceleration(); + if(linAcc) velmod->accumulateLinearVelModPerSec(*linAcc); + if(angAcc) velmod->accumulateAngularVelModPerSec(*angAcc); +} + +void Sc::BodyCore::setSpatialAcceleration(Ps::Pool* simStateDataPool, const PxVec3* linAcc, const PxVec3* angAcc) +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if (sim) + sim->notifyAddSpatialAcceleration(); + + if (!mSimStateData || !mSimStateData->isVelMod()) + setupSimStateData(simStateDataPool, false); + + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->notifyAddAcceleration(); + if (linAcc) velmod->setLinearVelModPerSec(*linAcc); + if (angAcc) velmod->setAngularVelModPerSec(*angAcc); +} + +void Sc::BodyCore::clearSpatialAcceleration(bool force, bool torque) +{ + PX_ASSERT(force || torque); + + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if(sim) + sim->notifyClearSpatialAcceleration(); + + if(mSimStateData) + { + PX_ASSERT(mSimStateData->isVelMod()); + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->notifyClearAcceleration(); + if(force) + velmod->clearLinearVelModPerSec(); + if(torque) + velmod->clearAngularVelModPerSec(); + } +} + +void Sc::BodyCore::addSpatialVelocity(Ps::Pool* simStateDataPool, const PxVec3* linVelDelta, const PxVec3* angVelDelta) +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if(sim) + sim->notifyAddSpatialVelocity(); + + if(!mSimStateData || !mSimStateData->isVelMod()) + setupSimStateData(simStateDataPool, false); + + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->notifyAddVelocity(); + if(linVelDelta) + velmod->accumulateLinearVelModPerStep(*linVelDelta); + if(angVelDelta) + velmod->accumulateAngularVelModPerStep(*angVelDelta); +} + +void Sc::BodyCore::clearSpatialVelocity(bool force, bool torque) +{ + PX_ASSERT(force || torque); + + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if(sim) + sim->notifyClearSpatialVelocity(); + + if(mSimStateData) + { + PX_ASSERT(mSimStateData->isVelMod()); + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->notifyClearVelocity(); + if(force) + velmod->clearLinearVelModPerStep(); + if(torque) + velmod->clearAngularVelModPerStep(); + } +} + +PxReal Sc::BodyCore::getInverseMass() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupInvMass : mCore.inverseMass; +} + +void Sc::BodyCore::setInverseMass(PxReal m) +{ + if(mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupInvMass = m; + else + { + mCore.inverseMass = m; + updateBodySim(*this); + } +} + +const PxVec3& Sc::BodyCore::getInverseInertia() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupInverseInertia : mCore.inverseInertia; +} + +void Sc::BodyCore::setInverseInertia(const PxVec3& i) +{ + if(mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupInverseInertia = i; + else + { + mCore.inverseInertia = i; + updateBodySim(*this); + } +} + +PxReal Sc::BodyCore::getLinearDamping() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupLinearDamping : mCore.linearDamping; +} + +void Sc::BodyCore::setLinearDamping(PxReal d) +{ + if(mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupLinearDamping = d; + else + { + mCore.linearDamping = d; + updateBodySim(*this); + } +} + +PxReal Sc::BodyCore::getAngularDamping() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupAngularDamping : mCore.angularDamping; +} + +void Sc::BodyCore::setAngularDamping(PxReal v) +{ + if(mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupAngularDamping = v; + else + { + mCore.angularDamping = v; + updateBodySim(*this); + } +} + +PxReal Sc::BodyCore::getMaxAngVelSq() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupMaxAngVelSq : mCore.maxAngularVelocitySq; +} + +void Sc::BodyCore::setMaxAngVelSq(PxReal v) +{ + if(mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupMaxAngVelSq = v; + else + { + mCore.maxAngularVelocitySq = v; + updateBodySim(*this); + } +} + +PxReal Sc::BodyCore::getMaxLinVelSq() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupMaxLinVelSq : mCore.maxLinearVelocitySq; +} + +void Sc::BodyCore::setMaxLinVelSq(PxReal v) +{ + if (mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupMaxLinVelSq = v; + else + { + mCore.maxLinearVelocitySq = v; + updateBodySim(*this); + } +} + +void Sc::BodyCore::setFlags(Ps::Pool* simStateDataPool, PxRigidBodyFlags f) +{ + const PxRigidBodyFlags old = mCore.mFlags; + if(f != old) + { + const PxU32 wasKinematic = old & PxRigidBodyFlag::eKINEMATIC; + const PxU32 isKinematic = f & PxRigidBodyFlag::eKINEMATIC; + const bool switchToKinematic = ((!wasKinematic) && isKinematic); + const bool switchToDynamic = (wasKinematic && (!isKinematic)); + + mCore.mFlags = f; + BodySim* sim = getSim(); + if (sim) + { + PX_ASSERT(simStateDataPool); + + const PxU32 posePreviewFlag = f & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW; + if(PxU32(old & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW) != posePreviewFlag) + sim->postPosePreviewChange(posePreviewFlag); + + // for those who might wonder about the complexity here: + // our current behavior is that you are not allowed to set a kinematic target unless the object is in a scene. + // Thus, the kinematic data should only be created/destroyed when we know for sure that we are in a scene. + + if(switchToKinematic) + { + setupSimStateData(simStateDataPool, true, false); + sim->postSwitchToKinematic(); + } + else if(switchToDynamic) + { + tearDownSimStateData(simStateDataPool, true); + sim->postSwitchToDynamic(); + } + + const PxU32 wasSpeculativeCCD = old & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD; + const PxU32 isSpeculativeCCD = f & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD; + + if(wasSpeculativeCCD ^ isSpeculativeCCD) + { + if(wasSpeculativeCCD) + { + if(sim->isArticulationLink()) + sim->getScene().resetSpeculativeCCDArticulationLink(sim->getNodeIndex().index()); + else + sim->getScene().resetSpeculativeCCDRigidBody(sim->getNodeIndex().index()); + + sim->getLowLevelBody().mInternalFlags &= (~PxcRigidBody::eSPECULATIVE_CCD); + } + else + { + if(sim->isArticulationLink()) + sim->getScene().setSpeculativeCCDArticulationLink(sim->getNodeIndex().index()); + else + sim->getScene().setSpeculativeCCDRigidBody(sim->getNodeIndex().index()); + + sim->getLowLevelBody().mInternalFlags |= (PxcRigidBody::eSPECULATIVE_CCD); + } + } + } + + if(switchToKinematic) + putToSleep(); + + if(sim) + { + const PxRigidBodyFlags ktFlags(PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES | PxRigidBodyFlag::eKINEMATIC); + const bool hadKt = (old & ktFlags) == ktFlags; + const bool hasKt = (f & ktFlags) == ktFlags; + if(hasKt && !hadKt) + sim->destroySqBounds(); + else if(hadKt && !hasKt) + sim->createSqBounds(); + } + } +} + +void Sc::BodyCore::setMaxContactImpulse(PxReal m) +{ + mCore.maxContactImpulse = m; + updateBodySim(*this); +} + +PxU32 Sc::BodyCore::getInternalIslandNodeIndex() const +{ + BodySim* sim = getSim(); + if (sim) + { + return sim->getNodeIndex().index(); + } + + return IG_INVALID_NODE; +} + +void Sc::BodyCore::setWakeCounter(PxReal wakeCounter, bool forceWakeUp) +{ + mCore.wakeCounter = wakeCounter; + BodySim* sim = getSim(); + if(sim) + { + //wake counter change, we need to trigger dma pxgbodysim data again + updateBodySim(sim); + if((wakeCounter > 0.0f) || forceWakeUp) + sim->wakeUp(); + sim->postSetWakeCounter(wakeCounter, forceWakeUp); + } +} + +void Sc::BodyCore::setSleepThreshold(PxReal t) +{ + mCore.sleepThreshold = t; + updateBodySim(*this); +} + +void Sc::BodyCore::setFreezeThreshold(PxReal t) +{ + mCore.freezeThreshold = t; + updateBodySim(*this); +} + +bool Sc::BodyCore::isSleeping() const +{ + BodySim* sim = getSim(); + return sim ? !sim->isActive() : true; +} + +void Sc::BodyCore::putToSleep() +{ + mCore.linearVelocity = PxVec3(0.0f); + mCore.angularVelocity = PxVec3(0.0f); + + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if(sim) + { + sim->notifyClearSpatialAcceleration(); + sim->notifyClearSpatialVelocity(); + } + + //The velmod data is stored in a separate structure so we can record forces before scene insertion. + if(mSimStateData && mSimStateData->isVelMod()) + { + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->clear(); + } + + // important to clear all values before setting the wake counter because the values decide + // whether an object is ready to go to sleep or not. + setWakeCounter(0.0f); + + if(sim) + sim->putToSleep(); +} + +void Sc::BodyCore::disableInternalCaching(bool disable) +{ + PX_ASSERT(!mSimStateData || mSimStateData->isKine()); + + if(mSimStateData) + { + PX_ASSERT(getFlags() & PxRigidBodyFlag::eKINEMATIC); + + if(disable) + restore(); + else + backup(*mSimStateData); + } +} + +bool Sc::BodyCore::setupSimStateData(Ps::Pool* simStateDataPool, const bool isKinematic, const bool targetValid) +{ + SimStateData* data = mSimStateData; + if(!data) + { + data = simStateDataPool->construct(); + if(!data) + return false; + } + + if(isKinematic) + { + PX_ASSERT(!mSimStateData || !mSimStateData->isKine()); + + new(data) SimStateData(SimStateData::eKine); + Kinematic* kine = data->getKinematicData(); + kine->targetValid = PxU8(targetValid ? 1 : 0); + backup(*data); + } + else + { + PX_ASSERT(!mSimStateData || !mSimStateData->isVelMod()); + PX_ASSERT(!targetValid); + + new(data) SimStateData(SimStateData::eVelMod); + VelocityMod* velmod = data->getVelocityModData(); + velmod->clear(); + velmod->flags = 0; + } + mSimStateData = data; + return true; +} + +bool Sc::BodyCore::checkSimStateKinematicStatus(const bool isKinematic) const +{ + PX_ASSERT(mSimStateData); + return mSimStateData->isKine() == isKinematic; +} + +void Sc::BodyCore::tearDownSimStateData(Ps::Pool* simStateDataPool, const bool isKinematic) +{ + PX_ASSERT(!mSimStateData || mSimStateData->isKine() == isKinematic); + + if(mSimStateData) + { + if(isKinematic) + restore(); + + simStateDataPool->destroy(mSimStateData); + mSimStateData=NULL; + } +} + +void Sc::BodyCore::backup(SimStateData& b) +{ + PX_ASSERT(b.isKine()); + + Kinematic* kine = b.getKinematicData(); + kine->backupLinearDamping = mCore.linearDamping; + kine->backupAngularDamping = mCore.angularDamping; + kine->backupInverseInertia = mCore.inverseInertia; + kine->backupInvMass = mCore.inverseMass; + kine->backupMaxAngVelSq = mCore.maxAngularVelocitySq; + kine->backupMaxLinVelSq = mCore.maxLinearVelocitySq; + + mCore.inverseMass = 0.0f; + mCore.inverseInertia = PxVec3(0.0f); + mCore.linearDamping = 0.0f; + mCore.angularDamping = 0.0f; + mCore.maxAngularVelocitySq = PX_MAX_REAL; + mCore.maxLinearVelocitySq = PX_MAX_REAL; +} + +void Sc::BodyCore::restore() +{ + PX_ASSERT(mSimStateData && mSimStateData->isKine()); + + const Kinematic* kine = mSimStateData->getKinematicData(); + mCore.inverseMass = kine->backupInvMass; + mCore.inverseInertia = kine->backupInverseInertia; + mCore.linearDamping = kine->backupLinearDamping; + mCore.angularDamping = kine->backupAngularDamping; + mCore.maxAngularVelocitySq = kine->backupMaxAngVelSq; + mCore.maxLinearVelocitySq = kine->backupMaxLinVelSq; +} + +void Sc::BodyCore::onOriginShift(const PxVec3& shift) +{ + mCore.body2World.p -= shift; + if(mSimStateData && (getFlags() & PxRigidBodyFlag::eKINEMATIC) && mSimStateData->getKinematicData()->targetValid) + mSimStateData->getKinematicData()->targetPose.p -= shift; + + BodySim* b = getSim(); + if(b) + b->onOriginShift(shift); // BodySim might not exist if actor has simulation disabled (PxActorFlag::eDISABLE_SIMULATION) +} + +// PT: TODO: isn't that the same as BodyCore->getPxActor() now? +PxActor* Sc::getPxActorFromBodyCore(Sc::BodyCore* r, PxActorType::Enum& type) +{ + const PxActorType::Enum actorCoretype = r->getActorCoreType(); + type = actorCoretype; + return Ps::pointerOffset(r, Sc::gOffsetTable.scCore2PxActor[actorCoretype]); +} + +// PT: TODO: why do we test againt NULL everywhere but not in 'isFrozen' ? +Ps::IntBool Sc::BodyCore::isFrozen() const +{ + return getSim()->isFrozen(); +} + +void Sc::BodyCore::setSolverIterationCounts(PxU16 c) +{ + mCore.solverIterationCounts = c; + Sc::BodySim* sim = getSim(); + if(sim) + sim->getLowLevelBody().solverIterationCounts = c; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Sc::BodyCore::getKinematicTarget(PxTransform& p) const +{ + PX_ASSERT(mCore.mFlags & PxRigidBodyFlag::eKINEMATIC); + + if(mSimStateData && mSimStateData->isKine() && mSimStateData->getKinematicData()->targetValid) + { + p = mSimStateData->getKinematicData()->targetPose; + return true; + } + else + return false; +} + +bool Sc::BodyCore::getHasValidKinematicTarget() const +{ + //The use pattern for this is that we should only look for kinematic data if we know it is kinematic. + //We might look for velmod data even if it is kinematic. + PX_ASSERT(!mSimStateData || mSimStateData->isKine()); + return mSimStateData && mSimStateData->isKine() && mSimStateData->getKinematicData()->targetValid; +} + +void Sc::BodyCore::setKinematicTarget(Ps::Pool* simStateDataPool, const PxTransform& p, PxReal wakeCounter) +{ + PX_ASSERT(mCore.mFlags & PxRigidBodyFlag::eKINEMATIC); + PX_ASSERT(!mSimStateData || mSimStateData->isKine()); + + if(mSimStateData) + { + Kinematic* kine = mSimStateData->getKinematicData(); + kine->targetPose = p; + kine->targetValid = 1; + + Sc::BodySim* bSim = getSim(); + if(bSim) + bSim->postSetKinematicTarget(); + } + else + { + if(setupSimStateData(simStateDataPool, true, true)) + { + PX_ASSERT(!getSim()); // covers the following scenario: kinematic gets added to scene while sim is running and target gets set (at that point the sim object does not yet exist) + + Kinematic* kine = mSimStateData->getKinematicData(); + kine->targetPose = p; + kine->targetValid = 1; + } + else + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "PxRigidDynamic: setting kinematic target failed, not enough memory."); + } + + wakeUp(wakeCounter); +} + +void Sc::BodyCore::invalidateKinematicTarget() +{ + PX_ASSERT(mSimStateData && mSimStateData->isKine()); + mSimStateData->getKinematicData()->targetValid = 0; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp new file mode 100644 index 000000000..612f4c99d --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp @@ -0,0 +1,973 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScBodySim.h" +#include "ScShapeSim.h" +#include "ScScene.h" +#include "ScArticulationSim.h" +#include "PxsContext.h" +#include "PxsSimpleIslandManager.h" +#include "PxsSimulationController.h" + +using namespace physx; +using namespace physx::Dy; +using namespace Sc; + +#define PX_FREEZE_INTERVAL 1.5f +#define PX_FREE_EXIT_THRESHOLD 4.f +#define PX_FREEZE_TOLERANCE 0.25f + +#define PX_SLEEP_DAMPING 0.5f +#define PX_FREEZE_SCALE 0.9f + +BodySim::BodySim(Scene& scene, BodyCore& core, bool compound) : + RigidSim (scene, core), + mLLBody (&core.getCore()), + mNodeIndex (IG_INVALID_NODE), + mInternalFlags (0), + mVelModState (VMF_GRAVITY_DIRTY), + mActiveListIndex (SC_NOT_IN_SCENE_INDEX), + mActiveCompoundListIndex (SC_NOT_IN_SCENE_INDEX), + mArticulation (NULL), + mConstraintGroup (NULL) +{ + // For 32-bit, sizeof(BodyCore) = 160 bytes, sizeof(BodySim) = 192 bytes + mLLBody.sleepLinVelAcc = PxVec3(0); + mLLBody.sleepAngVelAcc = PxVec3(0); + mLLBody.freezeCount = PX_FREEZE_INTERVAL; + mLLBody.accelScale = 1.f; + mLLBody.solverIterationCounts = core.getCore().solverIterationCounts; + core.getCore().numCountedInteractions = 0; + core.getCore().numBodyInteractions = 0; + mLLBody.mInternalFlags = 0; + if (core.getActorFlags()&PxActorFlag::eDISABLE_GRAVITY) + mLLBody.mInternalFlags |= PxsRigidBody::eDISABLE_GRAVITY; + if (core.getFlags() & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + mLLBody.mInternalFlags |= PxsRigidBody::eSPECULATIVE_CCD; + + //If a body pending insertion was given a force/torque then it will have + //the dirty flags stored in a separate structure. Copy them across + //so we can use them now that the BodySim is constructed. + SimStateData* simStateData = core.getSimStateData(false); + bool hasPendingForce = false; + if(simStateData) + { + VelocityMod* velmod = simStateData->getVelocityModData(); + hasPendingForce = (velmod->flags != 0) && + (!velmod->getLinearVelModPerSec().isZero() || !velmod->getAngularVelModPerSec().isZero() || + !velmod->getLinearVelModPerStep().isZero() || !velmod->getAngularVelModPerStep().isZero()); + mVelModState = velmod->flags; + velmod->flags = 0; + } + + // PT: don't read the core ptr we just wrote, use input param + // PT: at time of writing we get a big L2 here because even though bodycore has been prefetched, the wake counter is 160 bytes away + const bool isAwake = (core.getWakeCounter() > 0) || + (!core.getLinearVelocity().isZero()) || + (!core.getAngularVelocity().isZero()) || + hasPendingForce; + + const bool isKine = isKinematic(); + + IG::SimpleIslandManager* simpleIslandManager = scene.getSimpleIslandManager(); + if (!isArticulationLink()) + { + mNodeIndex = simpleIslandManager->addRigidBody(&mLLBody, isKine, isAwake); + } + else + { + if(mArticulation) + { + const ArticulationLinkHandle articLinkhandle = mArticulation->getLinkHandle(*this); + IG::NodeIndex index = mArticulation->getIslandNodeIndex(); + mNodeIndex.setIndices(index.index(), articLinkhandle & (DY_ARTICULATION_MAX_SIZE-1)); + } + } + + //If a user add force or torque before the body is inserted into the scene, + //this logic will make sure pre solver stage apply external force/torque to the body + if(hasPendingForce && !isArticulationLink()) + scene.getVelocityModifyMap().growAndSet(mNodeIndex.index()); + + PX_ASSERT(mActiveListIndex == SC_NOT_IN_SCENE_INDEX); + + // A.B. need to set the compound rigid flag early enough, so that we add the rigid into + // active list and do not create the shape bounds + if(compound) + raiseInternalFlag(BF_IS_COMPOUND_RIGID); + + setActive(isAwake, ActorSim::AS_PART_OF_CREATION); + + if (isAwake) + { + scene.addToActiveBodyList(*this); + PX_ASSERT(isActive()); + } + else + { + mActiveListIndex = SC_NOT_IN_ACTIVE_LIST_INDEX; + mActiveCompoundListIndex = SC_NOT_IN_ACTIVE_LIST_INDEX; + PX_ASSERT(!isActive()); + + simpleIslandManager->deactivateNode(mNodeIndex); + } + + if (isKine) + { + initKinematicStateBase(core, true); + + const SimStateData* kd = core.getSimStateData(true); + if (!kd) + { + core.setupSimStateData(scene.getSimStateDataPool(), true, false); + notifyPutToSleep(); // sleep state of kinematics is fully controlled by the simulation controller not the island manager + } + else + { + PX_ASSERT(kd->isKine()); + PX_ASSERT(kd->getKinematicData()->targetValid); // the only reason for the kinematic data to exist at that point already is if the target has been set + PX_ASSERT(isAwake); // the expectation is that setting a target also sets the wake counter to a positive value + postSetKinematicTarget(); + } + } +} + +BodySim::~BodySim() +{ + Scene& scene = getScene(); + const bool active = isActive(); + + getBodyCore().tearDownSimStateData(scene.getSimStateDataPool(), isKinematic() ? true : false); + + PX_ASSERT(!readInternalFlag(BF_ON_DEATHROW)); // Before 3.0 it could happen that destroy could get called twice. Assert to make sure this is fixed. + raiseInternalFlag(BF_ON_DEATHROW); + + scene.removeBody(*this); + PX_ASSERT(!getConstraintGroup()); // Removing from scene should erase constraint group node if it existed + + if(mArticulation) + mArticulation->removeBody(*this); + + //Articulations are represented by a single node, so they must only be removed by the articulation and not the links! + if(mArticulation == NULL && mNodeIndex.articulationLinkId() == 0) //If it wasn't an articulation link, then we can remove it + scene.getSimpleIslandManager()->removeNode(mNodeIndex); + + PX_ASSERT(mActiveListIndex != SC_NOT_IN_SCENE_INDEX); + + if (active) + scene.removeFromActiveBodyList(*this); + + mActiveListIndex = SC_NOT_IN_SCENE_INDEX; + mActiveCompoundListIndex = SC_NOT_IN_SCENE_INDEX; + + mCore.setSim(NULL); +} + +void BodySim::updateCached(Cm::BitMapPinned* shapeChangedMap) +{ + if(!(mLLBody.mInternalFlags & PxsRigidBody::eFROZEN)) + { + ElementSim* current = getElements_(); + while(current) + { + static_cast(current)->updateCached(0, shapeChangedMap); + current = current->mNextInActor; + } + } +} + +void BodySim::updateCached(PxsTransformCache& transformCache, Bp::BoundsArray& boundsArray) +{ + PX_ASSERT(!(mLLBody.mInternalFlags & PxsRigidBody::eFROZEN)); // PT: should not be called otherwise + + ElementSim* current = getElements_(); + while(current) + { + static_cast(current)->updateCached(transformCache, boundsArray); + current = current->mNextInActor; + } +} + +void BodySim::updateContactDistance(PxReal* contactDistance, const PxReal dt, Bp::BoundsArray& boundsArray) +{ + if (getLowLevelBody().getCore().mFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD + && !(getLowLevelBody().mInternalFlags & PxcRigidBody::eFROZEN)) + { + const PxVec3 linVel = getLowLevelBody().getLinearVelocity(); + const PxVec3 aVel = getLowLevelBody().getAngularVelocity(); + const PxReal inflation = linVel.magnitude() * dt; + + ElementSim* current = getElements_(); + while(current) + { + static_cast(current)->updateContactDistance(contactDistance, inflation, aVel, dt, boundsArray); + current = current->mNextInActor; + } + } +} + +//-------------------------------------------------------------- +// +// BodyCore interface implementation +// +//-------------------------------------------------------------- + +void BodySim::notifyAddSpatialAcceleration() +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + raiseVelocityModFlag(VMF_ACC_DIRTY); + + if(!isArticulationLink()) + getScene().getVelocityModifyMap().growAndSet(getNodeIndex().index()); +} + +void BodySim::notifyClearSpatialAcceleration() +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + raiseVelocityModFlag(VMF_ACC_DIRTY); + if (!isArticulationLink()) + getScene().getVelocityModifyMap().growAndSet(getNodeIndex().index()); +} + +void BodySim::notifyAddSpatialVelocity() +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + raiseVelocityModFlag(VMF_VEL_DIRTY); + if (!isArticulationLink()) + getScene().getVelocityModifyMap().growAndSet(getNodeIndex().index()); +} + +void BodySim::notifyClearSpatialVelocity() +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + raiseVelocityModFlag(VMF_VEL_DIRTY); + if (!isArticulationLink()) + getScene().getVelocityModifyMap().growAndSet(getNodeIndex().index()); +} + +void BodySim::postActorFlagChange(PxU32 oldFlags, PxU32 newFlags) +{ + // PT: don't convert to bool if not needed + const PxU32 wasWeightless = oldFlags & PxActorFlag::eDISABLE_GRAVITY; + const PxU32 isWeightless = newFlags & PxActorFlag::eDISABLE_GRAVITY; + + if (isWeightless != wasWeightless) + { + if (mVelModState == 0) raiseVelocityModFlag(VMF_GRAVITY_DIRTY); + + if (isWeightless) + mLLBody.mInternalFlags |= PxsRigidBody::eDISABLE_GRAVITY; + else + mLLBody.mInternalFlags &= (~PxsRigidBody::eDISABLE_GRAVITY); + } +} + +void BodySim::postBody2WorldChange() +{ + mLLBody.saveLastCCDTransform(); + notifyShapesOfTransformChange(); +} + +void BodySim::postSetWakeCounter(PxReal t, bool forceWakeUp) +{ + if ((t > 0.0f) || forceWakeUp) + notifyNotReadyForSleeping(); + else + { + const bool readyForSleep = checkSleepReadinessBesidesWakeCounter(); + if (readyForSleep) + notifyReadyForSleeping(); + } +} + +void BodySim::postSetKinematicTarget() +{ + PX_ASSERT(getBodyCore().getSimStateData(true)); + PX_ASSERT(getBodyCore().getSimStateData(true)->isKine()); + PX_ASSERT(getBodyCore().getSimStateData(true)->getKinematicData()->targetValid); + + raiseInternalFlag(BF_KINEMATIC_MOVED); // Important to set this here already because trigger interactions need to have this information when being activated. + clearInternalFlag(BF_KINEMATIC_SURFACE_VELOCITY); +} + +static void updateBPGroup(ElementSim* current) +{ + while(current) + { + static_cast(current)->updateBPGroup(); + current = current->mNextInActor; + } +} + +void BodySim::postSwitchToKinematic() +{ + initKinematicStateBase(getBodyCore(), false); + + // - interactions need to get refiltered to make sure that kinematic-kinematic and kinematic-static pairs get suppressed + // - unlike postSwitchToDynamic(), constraint interactions are not marked dirty here because a transition to kinematic will put the object asleep which in turn + // triggers onDeactivate_() on the constraint pairs that are active. If such pairs get deactivated, they will get removed from the list of active breakable + // constraints automatically. + setActorsInteractionsDirty(InteractionDirtyFlag::eBODY_KINEMATIC, NULL, InteractionFlag::eFILTERABLE); + + getScene().getSimpleIslandManager()->setKinematic(mNodeIndex); + + // + updateBPGroup(getElements_()); +} + +void BodySim::postSwitchToDynamic() +{ + mScene.getSimpleIslandManager()->setDynamic(mNodeIndex); + + setForcesToDefaults(true); + + if(getConstraintGroup()) + getConstraintGroup()->markForProjectionTreeRebuild(mScene.getProjectionManager()); + + // - interactions need to get refiltered to make sure that former kinematic-kinematic and kinematic-static pairs get enabled + // - switching from kinematic to dynamic does not change the sleep state of the body. The constraint interactions are marked dirty + // to check later whether they need to be activated plus potentially tracked for constraint break testing. This special treatment + // is necessary because constraints between two kinematic bodies are considered inactive, no matter whether one of the kinematics + // is active (has a target) or not. + setActorsInteractionsDirty(InteractionDirtyFlag::eBODY_KINEMATIC, NULL, InteractionFlag::eFILTERABLE | InteractionFlag::eCONSTRAINT); + + clearInternalFlag(BF_KINEMATIC_MOVE_FLAGS); + + if(isActive()) + mScene.swapInActiveBodyList(*this); + + // + updateBPGroup(getElements_()); +} + +void BodySim::postPosePreviewChange(const PxU32 posePreviewFlag) +{ + if (isActive()) + { + if (posePreviewFlag & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW) + getScene().addToPosePreviewList(*this); + else + getScene().removeFromPosePreviewList(*this); + } + else + PX_ASSERT(!getScene().isInPosePreviewList(*this)); +} + +//-------------------------------------------------------------- +// +// Sleeping +// +//-------------------------------------------------------------- + +void BodySim::activate() +{ + // Activate body + { + PX_ASSERT((!isKinematic()) || notInScene() || readInternalFlag(InternalFlags(BF_KINEMATIC_MOVED | BF_KINEMATIC_SURFACE_VELOCITY))); // kinematics should only get activated when a target is set. + // exception: object gets newly added, then the state change will happen later + if(!isArticulationLink()) + { + mLLBody.mInternalFlags &= (~PxsRigidBody::eFROZEN); + // Put in list of activated bodies. The list gets cleared at the end of a sim step after the sleep callbacks have been fired. + getScene().onBodyWakeUp(this); + } + + BodyCore& core = getBodyCore(); + if(core.getFlags() & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW) + { + PX_ASSERT(!getScene().isInPosePreviewList(*this)); + getScene().addToPosePreviewList(*this); + } + createSqBounds(); + } + + // Activate interactions + { + const PxU32 nbInteractions = getActorInteractionCount(); + + for(PxU32 i=0; igetType() != InteractionType::eOVERLAP && + interaction->getType() != InteractionType::eMARKER; + + if(!interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE) && (isNotIGControlled)) + { + const bool proceed = activateInteraction(interaction, NULL); + + if (proceed && (interaction->getType() < InteractionType::eTRACKED_IN_SCENE_COUNT)) + getScene().notifyInteractionActivated(interaction); + } + } + } +} + +void BodySim::deactivate() +{ + // Deactivate interactions + { + const PxU32 nbInteractions = getActorInteractionCount(); + + for(PxU32 i=0; igetType() != InteractionType::eOVERLAP && + interaction->getType() != InteractionType::eMARKER; + + if (interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE) && isNotIGControlled) + { + const bool proceed = deactivateInteraction(interaction); + if (proceed && (interaction->getType() < InteractionType::eTRACKED_IN_SCENE_COUNT)) + getScene().notifyInteractionDeactivated(interaction); + } + } + } + + // Deactivate body + { + PX_ASSERT((!isKinematic()) || notInScene() || !readInternalFlag(BF_KINEMATIC_MOVED)); // kinematics should only get deactivated when no target is set. + // exception: object gets newly added, then the state change will happen later + BodyCore& core = getBodyCore(); + if(!readInternalFlag(BF_ON_DEATHROW)) + { + // Set velocity to 0. + // Note: this is also fine if the method gets called because the user puts something to sleep (this behavior is documented in the API) + PX_ASSERT(core.getWakeCounter() == 0.0f); + const PxVec3 zero(0.0f); + core.setLinearVelocityInternal(zero); + core.setAngularVelocityInternal(zero); + + setForcesToDefaults(!(mLLBody.mInternalFlags & PxsRigidBody::eDISABLE_GRAVITY)); + } + + if(!isArticulationLink()) // Articulations have their own sleep logic. + getScene().onBodySleep(this); + + if(core.getFlags() & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW) + { + PX_ASSERT(getScene().isInPosePreviewList(*this)); + getScene().removeFromPosePreviewList(*this); + } + destroySqBounds(); + } +} + +void BodySim::setActive(bool active, PxU32 infoFlag) +{ + PX_ASSERT(!active || isDynamicRigid()); // Currently there should be no need to activate an actor that does not take part in island generation + + const PxU32 asPartOfCreation = infoFlag & ActorSim::AS_PART_OF_CREATION; + if (asPartOfCreation || isActive() != active) + { + PX_ASSERT(!asPartOfCreation || (getActorInteractionCount() == 0)); // On creation or destruction there should be no interactions + + if (active) + { + if (!asPartOfCreation) + { + // Inactive => Active + getScene().addToActiveBodyList(*this); + } + + activate(); + + PX_ASSERT(asPartOfCreation || isActive()); + } + else + { + if (!asPartOfCreation) + { + // Active => Inactive + getScene().removeFromActiveBodyList(*this); + } + + deactivate(); + + PX_ASSERT(asPartOfCreation || (!isActive())); + } + } +} + +void BodySim::wakeUp() +{ + setActive(true); + notifyWakeUp(true); +} + +void BodySim::putToSleep() +{ + PX_ASSERT(getBodyCore().getWakeCounter() == 0.0f); + PX_ASSERT(getBodyCore().getLinearVelocity().isZero()); + PX_ASSERT(getBodyCore().getAngularVelocity().isZero()); +#ifdef _DEBUG + // pending forces should have been cleared at this point + const SimStateData* sd = getBodyCore().getSimStateData(false); + if (sd) + { + const VelocityMod* vm = sd->getVelocityModData(); + PX_ASSERT(vm->linearPerSec.isZero() && vm->linearPerStep.isZero() && vm->angularPerSec.isZero() && vm->angularPerStep.isZero()); + } +#endif + + setActive(false); + notifyPutToSleep(); + + clearInternalFlag(InternalFlags(BF_KINEMATIC_SETTLING | BF_KINEMATIC_SETTLING_2)); // putToSleep is used when a kinematic gets removed from the scene while the sim is running and then gets re-inserted immediately. + // We can move this code when we look into the open task of making buffered re-insertion more consistent with the non-buffered case. +} + +void BodySim::internalWakeUp(PxReal wakeCounterValue) +{ + if(mArticulation) + mArticulation->internalWakeUp(wakeCounterValue); + else + internalWakeUpBase(wakeCounterValue); +} + +void BodySim::internalWakeUpArticulationLink(PxReal wakeCounterValue) +{ + PX_ASSERT(mArticulation); + internalWakeUpBase(wakeCounterValue); +} + +void BodySim::internalWakeUpBase(PxReal wakeCounterValue) //this one can only increase the wake counter, not decrease it, so it can't be used to put things to sleep! +{ + if ((!isKinematic()) && (getBodyCore().getWakeCounter() < wakeCounterValue)) + { + PX_ASSERT(wakeCounterValue > 0.0f); + getBodyCore().setWakeCounterFromSim(wakeCounterValue); + + //we need to update the gpu body sim because we reset the wake counter for the body core + mScene.getSimulationController()->updateDynamic(isArticulationLink(), mNodeIndex); + setActive(true); + notifyWakeUp(false); + mLLBody.mInternalFlags &= (~PxsRigidBody::eFROZEN); + } +} + +void BodySim::notifyReadyForSleeping() +{ + if(mArticulation == NULL) + getScene().getSimpleIslandManager()->deactivateNode(mNodeIndex); +} + +void BodySim::notifyNotReadyForSleeping() +{ + getScene().getSimpleIslandManager()->activateNode(mNodeIndex); +} + +void BodySim::notifyWakeUp(bool /*wakeUpInIslandGen*/) +{ + getScene().getSimpleIslandManager()->activateNode(mNodeIndex); +} + +void BodySim::notifyPutToSleep() +{ + getScene().getSimpleIslandManager()->putNodeToSleep(mNodeIndex); +} + +void BodySim::resetSleepFilter() +{ + mLLBody.sleepAngVelAcc = PxVec3(0.0f); + mLLBody.sleepLinVelAcc = PxVec3(0.0f); +} + +//This function will be called by CPU sleepCheck code +PxReal BodySim::updateWakeCounter(PxReal dt, PxReal energyThreshold, const Cm::SpatialVector& motionVelocity) +{ + // update the body's sleep state and + BodyCore& core = getBodyCore(); + + const PxReal wakeCounterResetTime = ScInternalWakeCounterResetValue; + + PxReal wc = core.getWakeCounter(); + + { + PxVec3 bcSleepLinVelAcc = mLLBody.sleepLinVelAcc; + PxVec3 bcSleepAngVelAcc = mLLBody.sleepAngVelAcc; + + if(wc < wakeCounterResetTime * 0.5f || wc < dt) + { + const PxTransform& body2World = getBody2World(); + + // calculate normalized energy: kinetic energy divided by mass + const PxVec3 t = core.getInverseInertia(); + const PxVec3 inertia(t.x > 0.0f ? 1.0f/t.x : 1.0f, t.y > 0.0f ? 1.0f/t.y : 1.0f, t.z > 0.0f ? 1.0f/t.z : 1.0f); + + PxVec3 sleepLinVelAcc =motionVelocity.linear; + PxVec3 sleepAngVelAcc = body2World.q.rotateInv(motionVelocity.angular); + + bcSleepLinVelAcc += sleepLinVelAcc; + bcSleepAngVelAcc += sleepAngVelAcc; + + PxReal invMass = core.getInverseMass(); + if(invMass == 0.0f) + invMass = 1.0f; + + const PxReal angular = bcSleepAngVelAcc.multiply(bcSleepAngVelAcc).dot(inertia) * invMass; + const PxReal linear = bcSleepLinVelAcc.magnitudeSquared(); + PxReal normalizedEnergy = 0.5f * (angular + linear); + + // scale threshold by cluster factor (more contacts => higher sleep threshold) + const PxReal clusterFactor = PxReal(1 + getNumCountedInteractions()); + const PxReal threshold = clusterFactor*energyThreshold; + + if (normalizedEnergy >= threshold) + { + PX_ASSERT(isActive()); + resetSleepFilter(); + const float factor = threshold == 0.0f ? 2.0f : PxMin(normalizedEnergy/threshold, 2.0f); + PxReal oldWc = wc; + wc = factor * 0.5f * wakeCounterResetTime + dt * (clusterFactor - 1.0f); + core.setWakeCounterFromSim(wc); + if (oldWc == 0.0f) // for the case where a sleeping body got activated by the system (not the user) AND got processed by the solver as well + notifyNotReadyForSleeping(); + + return wc; + } + } + + mLLBody.sleepLinVelAcc = bcSleepLinVelAcc; + mLLBody.sleepAngVelAcc = bcSleepAngVelAcc; + } + + wc = PxMax(wc-dt, 0.0f); + core.setWakeCounterFromSim(wc); + return wc; +} + +//-------------------------------------------------------------- +// +// Kinematics +// +//-------------------------------------------------------------- + +PX_FORCE_INLINE void BodySim::initKinematicStateBase(BodyCore&, bool asPartOfCreation) +{ + PX_ASSERT(!readInternalFlag(BF_KINEMATIC_MOVED)); + + if (!asPartOfCreation && isActive()) + getScene().swapInActiveBodyList(*this); + + //mLLBody.setAccelerationV(Cm::SpatialVector::zero()); + + // Need to be before setting setRigidBodyFlag::KINEMATIC + + if (getConstraintGroup()) + getConstraintGroup()->markForProjectionTreeRebuild(getScene().getProjectionManager()); +} + +void BodySim::calculateKinematicVelocity(PxReal oneOverDt) +{ + PX_ASSERT(isKinematic()); + + /*------------------------------------------------\ + | kinematic bodies are moved directly by the user and are not influenced by external forces + | we simply determine the distance moved since the last simulation frame and + | assign the appropriate delta to the velocity. This vel will be used to shove dynamic + | objects in the solver. + | We have to do this like so in a delayed way, because when the user sets the target pos the dt is not + | yet known. + \------------------------------------------------*/ + PX_ASSERT(isActive()); + + BodyCore& core = getBodyCore(); + + if (readInternalFlag(BF_KINEMATIC_MOVED)) + { + clearInternalFlag(InternalFlags(BF_KINEMATIC_SETTLING | BF_KINEMATIC_SETTLING_2)); + const SimStateData* kData = core.getSimStateData(true); + PX_ASSERT(kData); + PX_ASSERT(kData->isKine()); + PX_ASSERT(kData->getKinematicData()->targetValid); + PxVec3 linVelLL, angVelLL; + const PxTransform targetPose = kData->getKinematicData()->targetPose; + const PxTransform& currBody2World = getBody2World(); + + //the kinematic target pose is now the target of the body (CoM) and not the actor. + + PxVec3 deltaPos = targetPose.p; + deltaPos -= currBody2World.p; + linVelLL = deltaPos * oneOverDt; + + PxQuat q = targetPose.q * currBody2World.q.getConjugate(); + + if (q.w < 0) //shortest angle. + q = -q; + + PxReal angle; + PxVec3 axis; + q.toRadiansAndUnitAxis(angle, axis); + angVelLL = axis * angle * oneOverDt; + + core.getCore().linearVelocity = linVelLL; + core.getCore().angularVelocity = angVelLL; + + // Moving a kinematic should trigger a wakeUp call on a higher level. + PX_ASSERT(core.getWakeCounter()>0); + PX_ASSERT(isActive()); + + } + else if (!readInternalFlag(BF_KINEMATIC_SURFACE_VELOCITY)) + { + core.setLinearVelocity(PxVec3(0)); + core.setAngularVelocity(PxVec3(0)); + } +} + +void BodySim::updateKinematicPose() +{ + /*------------------------------------------------\ + | kinematic bodies are moved directly by the user and are not influenced by external forces + | we simply determine the distance moved since the last simulation frame and + | assign the appropriate delta to the velocity. This vel will be used to shove dynamic + | objects in the solver. + | We have to do this like so in a delayed way, because when the user sets the target pos the dt is not + | yet known. + \------------------------------------------------*/ + + PX_ASSERT(isKinematic()); + PX_ASSERT(isActive()); + + BodyCore& core = getBodyCore(); + + if (readInternalFlag(BF_KINEMATIC_MOVED)) + { + clearInternalFlag(InternalFlags(BF_KINEMATIC_SETTLING | BF_KINEMATIC_SETTLING_2)); + const SimStateData* kData = core.getSimStateData(true); + PX_ASSERT(kData); + PX_ASSERT(kData->isKine()); + PX_ASSERT(kData->getKinematicData()->targetValid); + + const PxTransform targetPose = kData->getKinematicData()->targetPose; + getBodyCore().getCore().body2World = targetPose; + } +} + +bool BodySim::deactivateKinematic() +{ + BodyCore& core = getBodyCore(); + if(readInternalFlag(BF_KINEMATIC_SETTLING_2)) + { + clearInternalFlag(BF_KINEMATIC_SETTLING_2); + core.setWakeCounterFromSim(0); // For sleeping objects the wake counter must be 0. This needs to hold for kinematics too. + notifyReadyForSleeping(); + notifyPutToSleep(); + setActive(false); + return true; + } + else if (readInternalFlag(BF_KINEMATIC_SETTLING)) + { + clearInternalFlag(BF_KINEMATIC_SETTLING); + raiseInternalFlag(BF_KINEMATIC_SETTLING_2); + } + else if (!readInternalFlag(BF_KINEMATIC_SURFACE_VELOCITY)) + { + clearInternalFlag(BF_KINEMATIC_MOVED); + raiseInternalFlag(BF_KINEMATIC_SETTLING); + } + return false; +} + +//-------------------------------------------------------------- +// +// Miscellaneous +// +//-------------------------------------------------------------- + +void BodySim::updateForces(PxReal dt, PxsRigidBody** updatedBodySims, PxU32* updatedBodyNodeIndices, PxU32& index, Cm::SpatialVector* acceleration, const bool useAccelerations, bool simUsesAdaptiveForce) +{ + PxVec3 linAcc(0.0f), angAcc(0.0f); + + const bool accDirty = readVelocityModFlag(VMF_ACC_DIRTY); + const bool velDirty = readVelocityModFlag(VMF_VEL_DIRTY); + + BodyCore& bodyCore = getBodyCore(); + SimStateData* simStateData = NULL; + + //if we change the logic like this, which means we don't need to have two seperate variables in the pxgbodysim to represent linAcc and angAcc. However, this + //means angAcc will be always 0 + if( (accDirty || velDirty) && ((simStateData = bodyCore.getSimStateData(false)) != NULL) ) + { + VelocityMod* velmod = simStateData->getVelocityModData(); + + //we don't have support for articulation yet + if (updatedBodySims) + { + updatedBodySims[index] = &getLowLevelBody(); + updatedBodyNodeIndices[index++] = getNodeIndex().index(); + } + + if(velDirty) + { + linAcc = velmod->getLinearVelModPerStep(); + angAcc = velmod->getAngularVelModPerStep(); + + if (useAccelerations) + { + acceleration->linear = linAcc / dt; + acceleration->angular = angAcc / dt; + + } + else + { + getBodyCore().updateVelocities(linAcc, angAcc); + } + } + + if (accDirty) + { + linAcc = velmod->getLinearVelModPerSec(); + angAcc = velmod->getAngularVelModPerSec(); + + if (acceleration) + { + acceleration->linear = linAcc; + acceleration->angular = angAcc; + } + else + { + PxReal scale = dt; + if (simUsesAdaptiveForce) + { + if (getScene().getSimpleIslandManager()->getAccurateIslandSim().getIslandStaticTouchCount(mNodeIndex) != 0) + { + scale *= mLLBody.accelScale; + } + } + getBodyCore().updateVelocities(linAcc*scale, angAcc*scale); + } + } + } + + setForcesToDefaults(readVelocityModFlag(VMF_ACC_DIRTY)); +} + +void BodySim::onConstraintDetach() +{ + PX_ASSERT(readInternalFlag(BF_HAS_CONSTRAINTS)); + + PxU32 size = getActorInteractionCount(); + Interaction** interactions = getActorInteractions(); + unregisterCountedInteraction(); + + while(size--) + { + const Interaction* interaction = *interactions++; + if(interaction->getType() == InteractionType::eCONSTRAINTSHADER) + return; + } + + clearInternalFlag(BF_HAS_CONSTRAINTS); // There are no other constraint interactions left +} + +void BodySim::setArticulation(ArticulationSim* a, PxReal wakeCounter, bool asleep, PxU32 bodyIndex) +{ + mArticulation = a; + if(a) + { + IG::NodeIndex index = mArticulation->getIslandNodeIndex(); + mNodeIndex.setIndices(index.index(), bodyIndex); + getBodyCore().setWakeCounterFromSim(wakeCounter); + + if (getFlagsFast() & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + getScene().setSpeculativeCCDArticulationLink(mNodeIndex.index()); + + if (!asleep) + { + setActive(true); + notifyWakeUp(false); + } + else + { + notifyReadyForSleeping(); + notifyPutToSleep(); + setActive(false); + } + } + else + { + //Setting a 1 in the articulation ID to avoid returning the node Index to the node index + //manager + mNodeIndex.setIndices(IG_INVALID_NODE, 1); + } +} + +void BodySim::createSqBounds() +{ + if(!isActive() || usingSqKinematicTarget() || readInternalFlag(BF_IS_COMPOUND_RIGID)) + return; + + PX_ASSERT(!isFrozen()); + + ElementSim* current = getElements_(); + while(current) + { + static_cast(current)->createSqBounds(); + current = current->mNextInActor; + } +} + +void BodySim::destroySqBounds() +{ + ElementSim* current = getElements_(); + while(current) + { + static_cast(current)->destroySqBounds(); + current = current->mNextInActor; + } +} + +void BodySim::freezeTransforms(Cm::BitMapPinned* shapeChangedMap) +{ + ElementSim* current = getElements_(); + while(current) + { + ShapeSim* sim = static_cast(current); + sim->updateCached(PxsTransformFlag::eFROZEN, shapeChangedMap); + sim->destroySqBounds(); + current = current->mNextInActor; + } +} + +void BodySim::disableCompound() +{ + if(isActive()) + getScene().removeFromActiveCompoundBodyList(*this); + clearInternalFlag(BF_IS_COMPOUND_RIGID); +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h new file mode 100644 index 000000000..f72f73af3 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h @@ -0,0 +1,333 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_BODYSIM +#define PX_PHYSICS_SCP_BODYSIM + +#include "PsUtilities.h" +#include "PsIntrinsics.h" +#include "ScRigidSim.h" +#include "PxvDynamics.h" +#include "ScBodyCore.h" +#include "ScSimStateData.h" +#include "ScConstraintGroupNode.h" +#include "PxRigidDynamic.h" +#include "DyArticulation.h" +#include "PxsRigidBody.h" +#include "PxsSimpleIslandManager.h" + +namespace physx +{ +namespace Bp +{ + class BoundsArray; +} + class PxsTransformCache; + class PxsSimulationController; +namespace Sc +{ + #define SC_NOT_IN_SCENE_INDEX 0xffffffff // the body is not in the scene yet + #define SC_NOT_IN_ACTIVE_LIST_INDEX 0xfffffffe // the body is in the scene but not in the active list + + class Scene; + class ArticulationSim; + + static const PxReal ScInternalWakeCounterResetValue = 20.0f*0.02f; + + class BodySim : public RigidSim + { + public: + enum InternalFlags + { + //BF_DISABLE_GRAVITY = 1 << 0, // Don't apply the scene's gravity + + BF_HAS_STATIC_TOUCH = 1 << 1, // Set when a body is part of an island with static contacts. Needed to be able to recalculate adaptive force if this changes + BF_KINEMATIC_MOVED = 1 << 2, // Set when the kinematic was moved + + BF_ON_DEATHROW = 1 << 3, // Set when the body is destroyed + + BF_IS_IN_SLEEP_LIST = 1 << 4, // Set when the body is added to the list of bodies which were put to sleep + BF_IS_IN_WAKEUP_LIST = 1 << 5, // Set when the body is added to the list of bodies which were woken up + BF_SLEEP_NOTIFY = 1 << 6, // A sleep notification should be sent for this body (and not a wakeup event, even if the body is part of the woken list as well) + BF_WAKEUP_NOTIFY = 1 << 7, // A wake up notification should be sent for this body (and not a sleep event, even if the body is part of the sleep list as well) + + BF_HAS_CONSTRAINTS = 1 << 8, // Set if the body has one or more constraints + BF_KINEMATIC_SETTLING = 1 << 9, // Set when the body was moved kinematically last frame + BF_KINEMATIC_SETTLING_2 = 1 << 10, + BF_KINEMATIC_MOVE_FLAGS = BF_KINEMATIC_MOVED | BF_KINEMATIC_SETTLING | BF_KINEMATIC_SETTLING_2, //Used to clear kinematic masks in 1 call + BF_KINEMATIC_SURFACE_VELOCITY = 1 << 11, //Set when the application calls setKinematicVelocity. Actor remains awake until application calls clearKinematicVelocity. + BF_IS_COMPOUND_RIGID = 1 << 12 // Set when the body is a compound actor, we dont want to set the sq bounds + + // PT: WARNING: flags stored on 16-bits now. + }; + + public: + BodySim(Scene&, BodyCore&, bool); + virtual ~BodySim(); + + void notifyAddSpatialAcceleration(); + void notifyClearSpatialAcceleration(); + void notifyAddSpatialVelocity(); + void notifyClearSpatialVelocity(); + void updateCached(Cm::BitMapPinned* shapeChangedMap); + void updateCached(PxsTransformCache& transformCache, Bp::BoundsArray& boundsArray); + void updateContactDistance(PxReal* contactDistance, const PxReal dt, Bp::BoundsArray& boundsArray); + + // hooks for actions in body core when it's attached to a sim object. Generally + // we get called after the attribute changed. + + virtual void postActorFlagChange(PxU32 oldFlags, PxU32 newFlags); + void postBody2WorldChange(); + void postSetWakeCounter(PxReal t, bool forceWakeUp); + void postSetKinematicTarget(); + void postSwitchToKinematic(); + void postSwitchToDynamic(); + void postPosePreviewChange(const PxU32 posePreviewFlag); // called when PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW changes + + PX_FORCE_INLINE const PxTransform& getBody2World() const { return getBodyCore().getCore().body2World; } + PX_FORCE_INLINE const PxTransform& getBody2Actor() const { return getBodyCore().getCore().getBody2Actor(); } + PX_FORCE_INLINE const PxsRigidBody& getLowLevelBody() const { return mLLBody; } + PX_FORCE_INLINE PxsRigidBody& getLowLevelBody() { return mLLBody; } + void wakeUp(); // note: for user API call purposes only, i.e., use from BodyCore. For simulation internal purposes there is internalWakeUp(). + void putToSleep(); + + void disableCompound(); + + static PxU32 getRigidBodyOffset() { return PxU32(PX_OFFSET_OF_RT(BodySim, mLLBody));} + + private: + void activate(); + void deactivate(); + + //--------------------------------------------------------------------------------- + // Constraint projection + //--------------------------------------------------------------------------------- + public: + PX_FORCE_INLINE ConstraintGroupNode* getConstraintGroup() { return mConstraintGroup; } + PX_FORCE_INLINE void setConstraintGroup(ConstraintGroupNode* node) { mConstraintGroup = node; } + + //// A list of active projection trees in the scene might be better + //PX_FORCE_INLINE void projectPose() { PX_ASSERT(mConstraintGroup); ConstraintGroupNode::projectPose(*mConstraintGroup); } + + //--------------------------------------------------------------------------------- + // Kinematics + //--------------------------------------------------------------------------------- + public: + PX_FORCE_INLINE bool isKinematic() const { return getBodyCore().getFlags() & PxRigidBodyFlag::eKINEMATIC; } + PX_FORCE_INLINE bool isArticulationLink() const { return getActorType() == PxActorType::eARTICULATION_LINK; } + void calculateKinematicVelocity(PxReal oneOverDt); + void updateKinematicPose(); + bool deactivateKinematic(); + private: + PX_FORCE_INLINE void initKinematicStateBase(BodyCore&, bool asPartOfCreation); + + //--------------------------------------------------------------------------------- + // Sleeping + //--------------------------------------------------------------------------------- + public: + PX_FORCE_INLINE bool isActive() const { return (mActiveListIndex < SC_NOT_IN_ACTIVE_LIST_INDEX); } + void setActive(bool active, PxU32 infoFlag=0); // see ActivityChangeInfoFlag + + PX_FORCE_INLINE PxU32 getActiveListIndex() const { return mActiveListIndex; } // if the body is active, the index is smaller than SC_NOT_IN_ACTIVE_LIST_INDEX + PX_FORCE_INLINE void setActiveListIndex(PxU32 index) { mActiveListIndex = index; } + + PX_FORCE_INLINE PxU32 getActiveCompoundListIndex() const { return mActiveCompoundListIndex; } // if the body is active and is compound, the index is smaller than SC_NOT_IN_ACTIVE_LIST_INDEX + PX_FORCE_INLINE void setActiveCompoundListIndex(PxU32 index) { mActiveCompoundListIndex = index; } + + void internalWakeUp(PxReal wakeCounterValue=ScInternalWakeCounterResetValue); + void internalWakeUpArticulationLink(PxReal wakeCounterValue); // called by ArticulationSim to wake up this link + + PxReal updateWakeCounter(PxReal dt, PxReal energyThreshold, const Cm::SpatialVector& motionVelocity); + + void resetSleepFilter(); + void notifyReadyForSleeping(); // inform the sleep island generation system that the body is ready for sleeping + void notifyNotReadyForSleeping(); // inform the sleep island generation system that the body is not ready for sleeping + PX_FORCE_INLINE bool checkSleepReadinessBesidesWakeCounter(); // for API triggered changes to test sleep readiness + + PX_FORCE_INLINE void registerCountedInteraction() { mLLBody.getCore().numCountedInteractions++; PX_ASSERT(mLLBody.getCore().numCountedInteractions); } + PX_FORCE_INLINE void unregisterCountedInteraction() { PX_ASSERT(mLLBody.getCore().numCountedInteractions); mLLBody.getCore().numCountedInteractions--;} + PX_FORCE_INLINE PxU32 getNumCountedInteractions() const { return mLLBody.getCore().numCountedInteractions; } + + PX_FORCE_INLINE Ps::IntBool isFrozen() const { return Ps::IntBool(mLLBody.mInternalFlags & PxsRigidBody::eFROZEN); } + private: + PX_FORCE_INLINE void notifyWakeUp(bool wakeUpInIslandGen = false); // inform the sleep island generation system that the object got woken up + PX_FORCE_INLINE void notifyPutToSleep(); // inform the sleep island generation system that the object was put to sleep + PX_FORCE_INLINE void internalWakeUpBase(PxReal wakeCounterValue); + + //--------------------------------------------------------------------------------- + // External velocity changes + //--------------------------------------------------------------------------------- + public: + void updateForces(PxReal dt, PxsRigidBody** updatedBodySims, PxU32* updatedBodyNodeIndices, + PxU32& index, Cm::SpatialVector* acceleration, const bool useAcceleration, bool simUsesAdaptiveForce); + private: + PX_FORCE_INLINE void raiseVelocityModFlag(VelocityModFlags f) { mVelModState |= f; } + PX_FORCE_INLINE void clearVelocityModFlag(VelocityModFlags f) { mVelModState &= ~f; } + PX_FORCE_INLINE bool readVelocityModFlag(VelocityModFlags f) { return (mVelModState & f) != 0; } + PX_FORCE_INLINE void setForcesToDefaults(bool enableGravity); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + public: + PX_FORCE_INLINE PxU16 getInternalFlag() const { return mInternalFlags; } + PX_FORCE_INLINE PxU16 readInternalFlag(InternalFlags flag) const { return PxU16(mInternalFlags & flag); } + PX_FORCE_INLINE void raiseInternalFlag(InternalFlags flag) { mInternalFlags |= flag; } + PX_FORCE_INLINE void clearInternalFlag(InternalFlags flag) { mInternalFlags &= ~flag; } + PX_FORCE_INLINE PxU32 getFlagsFast() const { return getBodyCore().getFlags(); } + + PX_FORCE_INLINE void incrementBodyConstraintCounter() { mLLBody.mCore->numBodyInteractions++; } + PX_FORCE_INLINE void decrementBodyConstraintCounter() { PX_ASSERT(mLLBody.mCore->numBodyInteractions>0); mLLBody.mCore->numBodyInteractions--; } + + PX_FORCE_INLINE BodyCore& getBodyCore() const { return static_cast(getRigidCore()); } + + PX_INLINE ArticulationSim* getArticulation() const { return mArticulation; } + void setArticulation(ArticulationSim* a, PxReal wakeCounter, bool asleep, PxU32 bodyIndex); + + PX_FORCE_INLINE IG::NodeIndex getNodeIndex() const { return mNodeIndex; } + + PX_FORCE_INLINE void onConstraintAttach() { raiseInternalFlag(BF_HAS_CONSTRAINTS); registerCountedInteraction(); } + void onConstraintDetach(); + + PX_FORCE_INLINE void onOriginShift(const PxVec3& shift) { mLLBody.mLastTransform.p -= shift; } + + PX_FORCE_INLINE bool notInScene() const { return mActiveListIndex == SC_NOT_IN_SCENE_INDEX; } + + PX_FORCE_INLINE bool usingSqKinematicTarget() const + { + PxU32 ktFlags(PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES | PxRigidBodyFlag::eKINEMATIC); + return (getFlagsFast()&ktFlags) == ktFlags; + } + + PX_FORCE_INLINE PxU32 getNbShapes() const { return mElementCount; } + + void createSqBounds(); + void destroySqBounds(); + void freezeTransforms(Cm::BitMapPinned* shapeChangedMap); + void invalidateSqBounds(); + private: + + //--------------------------------------------------------------------------------- + // Base body + //--------------------------------------------------------------------------------- + PxsRigidBody mLLBody; + + //--------------------------------------------------------------------------------- + // Island manager + //--------------------------------------------------------------------------------- + IG::NodeIndex mNodeIndex; + + //--------------------------------------------------------------------------------- + // External velocity changes + //--------------------------------------------------------------------------------- + // VelocityMod data allocated on the fly when the user applies velocity changes + // which need to be accumulated. + // VelMod dirty flags stored in BodySim so we can save ourselves the expense of looking at + // the separate velmod data if no forces have been set. + PxU16 mInternalFlags; + PxU8 mVelModState; + + //--------------------------------------------------------------------------------- + // Sleeping + //--------------------------------------------------------------------------------- + PxU32 mActiveListIndex; // Used by Scene to track active bodies + PxU32 mActiveCompoundListIndex; // Used by Scene to track active compound bodies + + //--------------------------------------------------------------------------------- + // Articulation + //--------------------------------------------------------------------------------- + ArticulationSim* mArticulation; // NULL if not in an articulation + + //--------------------------------------------------------------------------------- + // Joints & joint groups + //--------------------------------------------------------------------------------- + + // This is a tree data structure that gives us the projection order of joints in which this body is the tree root. + // note: the link of the root body is not necces. the root link due to the re-rooting of the articulation! + ConstraintGroupNode* mConstraintGroup; + }; + +} // namespace Sc + +PX_FORCE_INLINE void Sc::BodySim::setForcesToDefaults(bool enableGravity) +{ + if (!(mLLBody.mCore->mFlags & PxRigidBodyFlag::eRETAIN_ACCELERATIONS)) + { + SimStateData* simStateData = getBodyCore().getSimStateData(false); + if(simStateData) + { + VelocityMod* velmod = simStateData->getVelocityModData(); + velmod->clear(); + } + + if (enableGravity) + mVelModState = VMF_GRAVITY_DIRTY; // We want to keep the gravity flag to make sure the acceleration gets changed to gravity-only + // in the next step (unless the application adds new forces of course) + else + mVelModState = 0; + } + else + { + SimStateData* simStateData = getBodyCore().getSimStateData(false); + if (simStateData) + { + VelocityMod* velmod = simStateData->getVelocityModData(); + velmod->clearPerStep(); + } + + mVelModState &= (~(VMF_VEL_DIRTY)); + } +} + +PX_FORCE_INLINE bool Sc::BodySim::checkSleepReadinessBesidesWakeCounter() +{ + const BodyCore& bodyCore = getBodyCore(); + const SimStateData* simStateData = bodyCore.getSimStateData(false); + const VelocityMod* velmod = simStateData ? simStateData->getVelocityModData() : NULL; + + bool readyForSleep = bodyCore.getLinearVelocity().isZero() && bodyCore.getAngularVelocity().isZero(); + if (readVelocityModFlag(VMF_ACC_DIRTY)) + { + readyForSleep = readyForSleep && (!velmod || velmod->getLinearVelModPerSec().isZero()); + readyForSleep = readyForSleep && (!velmod || velmod->getAngularVelModPerSec().isZero()); + } + if (readVelocityModFlag(VMF_VEL_DIRTY)) + { + readyForSleep = readyForSleep && (!velmod || velmod->getLinearVelModPerStep().isZero()); + readyForSleep = readyForSleep && (!velmod || velmod->getAngularVelModPerStep().isZero()); + } + + return readyForSleep; +} + + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScClient.h b/src/PhysX/physx/source/simulationcontroller/src/ScClient.h new file mode 100644 index 000000000..f19bc3154 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScClient.h @@ -0,0 +1,53 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_CLIENT +#define PX_PHYSICS_CLIENT + +#include "PxScene.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Sc +{ + class Client : public Ps::UserAllocated + { + public: + Client() + {} + + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp new file mode 100644 index 000000000..55009e5e7 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsFoundation.h" + +#include "ScPhysics.h" +#include "ScBodyCore.h" +#include "ScConstraintCore.h" +#include "ScConstraintSim.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Sc::ConstraintCore::ConstraintCore(PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) +: mFlags(PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES) +, mAppliedForce(PxVec3(0)) +, mAppliedTorque(PxVec3(0)) +, mConnector(&connector) +, mProject(shaders.project) +, mSolverPrep(shaders.solverPrep) +, mVisualize(shaders.visualize) +, mDataSize(dataSize) +, mLinearBreakForce(PX_MAX_F32) +, mAngularBreakForce(PX_MAX_F32) +, mMinResponseThreshold(0) +, mSim(NULL) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Sc::ConstraintCore::~ConstraintCore() +{ +} + +void Sc::ConstraintCore::setFlags(PxConstraintFlags flags) +{ + PxConstraintFlags old = mFlags; + flags = flags | (old & PxConstraintFlag::eGPU_COMPATIBLE); + if(flags != old) + { + mFlags = flags; + if(getSim()) + getSim()->postFlagChange(old, flags); + } +} + +void Sc::ConstraintCore::getForce(PxVec3& force, PxVec3& torque) const +{ + if(!mSim) + { + force = PxVec3(0,0,0); + torque = PxVec3(0,0,0); + } + else + mSim->getForce(force, torque); + +} + +void Sc::ConstraintCore::setBodies(RigidCore* r0v, RigidCore* r1v) +{ + if(mSim) + mSim->postBodiesChange(r0v, r1v); +} + +bool Sc::ConstraintCore::updateConstants(void* addr) +{ + if (getSim()) + { + getSim()->setConstantsLL(addr); + return true; + } + return false; +} + +void Sc::ConstraintCore::setBreakForce(PxReal linear, PxReal angular) +{ + mLinearBreakForce = linear; + mAngularBreakForce = angular; + + if (getSim()) + getSim()->setBreakForceLL(linear, angular); +} + +void Sc::ConstraintCore::getBreakForce(PxReal& linear, PxReal& angular) const +{ + linear = mLinearBreakForce; + angular = mAngularBreakForce; +} + +void Sc::ConstraintCore::setMinResponseThreshold(PxReal threshold) +{ + mMinResponseThreshold = threshold; + + if (getSim()) + getSim()->setMinResponseThresholdLL(threshold); +} + +PxConstraint* Sc::ConstraintCore::getPxConstraint() +{ + return gOffsetTable.convertScConstraint2Px(this); +} + +const PxConstraint* Sc::ConstraintCore::getPxConstraint() const +{ + return gOffsetTable.convertScConstraint2Px(this); +} + +void Sc::ConstraintCore::breakApart() +{ + // TODO: probably want to do something with the interaction here + // as well as remove the constraint from LL. + + mFlags |= PxConstraintFlag::eBROKEN; +} + +void Sc::ConstraintCore::prepareForSetBodies() +{ + if(mSim) + mSim->preBodiesChange(); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp new file mode 100644 index 000000000..9177013ff --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp @@ -0,0 +1,138 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScConstraintGroupNode.h" +#include "ScConstraintProjectionManager.h" +#include "PsFoundation.h" +#include "ScBodySim.h" +#include "ScConstraintSim.h" +#include "ScConstraintInteraction.h" + +using namespace physx; + +Sc::ConstraintGroupNode::ConstraintGroupNode(BodySim& b) : + body(&b), + parent(this), + tail(this), + rank(0), + next(NULL), + + projectionFirstRoot(NULL), + projectionNextRoot(NULL), + projectionParent(NULL), + projectionFirstChild(NULL), + projectionNextSibling(NULL), + projectionConstraint(NULL), + + flags(0) +{ +} + + +// +// Implementation of FIND of +// UNION-FIND algo. +// +Sc::ConstraintGroupNode& Sc::ConstraintGroupNode::getRoot() +{ + PX_ASSERT(parent); + + ConstraintGroupNode* root = parent; + + if (root->parent == root) + return *root; + else + { + PxU32 nbHops = 1; + root = root->parent; + + while(root != root->parent) + { + root = root->parent; + nbHops++; + } + + // Write root to all nodes on the path + ConstraintGroupNode* curr = this; + while(nbHops) + { + ConstraintGroupNode* n = curr->parent; + curr->parent = root; + curr = n; + nbHops--; + } + + return *root; + } +} + + +void Sc::ConstraintGroupNode::markForProjectionTreeRebuild(ConstraintProjectionManager& cpManager) +{ + ConstraintGroupNode& root = getRoot(); + if (!root.readFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE)) + { + cpManager.addToPendingTreeUpdates(root); + } +} + + +void Sc::ConstraintGroupNode::initProjectionData(ConstraintGroupNode* parent_, ConstraintSim* c) +{ + projectionConstraint = c; + + //add us to parent's child list: + if (parent_) + { + projectionNextSibling = parent_->projectionFirstChild; + parent_->projectionFirstChild = this; + + projectionParent = parent_; + } +} + + +void Sc::ConstraintGroupNode::clearProjectionData() +{ + projectionFirstRoot = NULL; + projectionNextRoot = NULL; + projectionParent = NULL; + projectionFirstChild = NULL; + projectionNextSibling = NULL; + projectionConstraint = NULL; +} + + +void Sc::ConstraintGroupNode::projectPose(ConstraintGroupNode& node, Ps::Array& projectedBodies) +{ + PX_ASSERT(node.hasProjectionTreeRoot()); + + Sc::ConstraintProjectionTree::projectPose(node, projectedBodies); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h new file mode 100644 index 000000000..144ffaecc --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_CONSTRAINT_GROUP_NODE +#define PX_PHYSICS_SCP_CONSTRAINT_GROUP_NODE + +#include "ScConstraintProjectionTree.h" +#include "PsUtilities.h" // for Ps::to8() + +namespace physx +{ +namespace Sc +{ + class ConstraintSim; + class BodySim; + class ConstraintProjectionManager; + + // A 'simulation island' of constraints. Created by a union-find algorithm every time a new constraint is added to any of the involved bodies. + struct ConstraintGroupNode : public Ps::UserAllocated + { + enum StateFlags + { + eDISCOVERED = 1 << 0, // Used during projection tree generation to mark processed nodes. + eIN_PROJECTION_PASS_LIST = 1 << 1, // Temporarily used to avoid duplicate entries in the list of nodes that should project the pose after the solver + ePENDING_TREE_UPDATE = 1 << 2, // Marks the constraint groups that need their projection trees updated. Must only be set on the root group node. + eNEXT_FREE_SHIFT = 3, + eNEXT_FREE = 1 << eNEXT_FREE_SHIFT + }; + + // these flags should give a rough hint how many projecting constraints to expect in the constraint group. This will be used for + // load balancing when running projection in parallel. The intervals were chosen somewhat arbitrarily but the general motivation was + // to cover very simple constraint setups, simple ragdolls, complex ragdolls and very complex projection setups. Note that the load + // balancing is not waterproof since at the end it is the projection shader from the external constraint implementer (for example, a joint) + // which decides based on some thresholds whether projection runs or not. + enum ProjectionCountHintFlags + { + e1_TO_4 = eNEXT_FREE, + e5_TO_16 = eNEXT_FREE << 1, + e17_TO_64 = eNEXT_FREE << 2, + e65_TO_INF = eNEXT_FREE << 3, + eCLEAR_MASK = ~(0xffffffff << eNEXT_FREE_SHIFT) + }; + + ConstraintGroupNode(BodySim& b); + ~ConstraintGroupNode() + { + PX_ASSERT(!readFlag(ePENDING_TREE_UPDATE)); + PX_ASSERT(projectionFirstRoot == NULL); + } + + PX_FORCE_INLINE void raiseFlag(StateFlags f) { flags |= f; } + PX_FORCE_INLINE void clearFlag(StateFlags f) { flags &= ~f; } + PX_FORCE_INLINE bool readFlag(StateFlags f) const { return (flags & f) != 0; } + PX_FORCE_INLINE PxU32 getProjectionCountHint() const; + PX_FORCE_INLINE void setProjectionCountHint(PxU32 constraintsToProjectCount); + + ConstraintGroupNode& getRoot(); + + PX_FORCE_INLINE void buildProjectionTrees(); //build the projection trees for a constraint group. + void markForProjectionTreeRebuild(ConstraintProjectionManager&); + PX_FORCE_INLINE void purgeProjectionTrees(); + PX_FORCE_INLINE bool hasProjectionTreeRoot() { return projectionFirstRoot != NULL; } + PX_FORCE_INLINE void setProjectionTreeRoot(ConstraintGroupNode* root) { projectionFirstRoot = root; } + + void initProjectionData(ConstraintGroupNode* parent, ConstraintSim* c); + void clearProjectionData(); + + static void projectPose(ConstraintGroupNode& root, Ps::Array& projectedBodies); + + + BodySim* body; //the owner body of this node + + //tree for union/find: + ConstraintGroupNode* parent; + ConstraintGroupNode* tail; //only valid if this is root of group, points to LList tail node. + PxU32 rank; //rank counter for union/find. Initially zero. Is number of hops from root to furthest leaf in tree. This is just a hint to create more balanced trees. + + //linked list for traversal: + ConstraintGroupNode* next; //next in list, NULL at tail. + + //projection tree information + ConstraintGroupNode* projectionFirstRoot; //pointer to first projection tree root node. Only set for constraint group roots + ConstraintGroupNode* projectionNextRoot; //pointer to next projection root node. Only set for constraint group roots + //a constraint group can consist of multiple projection trees if kinematics are involved! Because a kinematic doesn't split + //the constraint group as a static anchor does. + ConstraintGroupNode* projectionParent; //node to project to + ConstraintGroupNode* projectionFirstChild; //first node which gets projected to this one + ConstraintGroupNode* projectionNextSibling; //the next sibling which gets projected to the same node as this one. NULL if projectionParent is NULL. + ConstraintSim* projectionConstraint; //the constraint to project (constraint to projection parent) + + private: + PxU8 flags; + }; + +} // namespace Sc + + +PX_FORCE_INLINE PxU32 Sc::ConstraintGroupNode::getProjectionCountHint() const +{ + // return the mean of the upper and lower bound + + if (flags & ConstraintGroupNode::e65_TO_INF) + return 128; + else if (flags & ConstraintGroupNode::e17_TO_64) + return 40; + else if (flags & ConstraintGroupNode::e5_TO_16) + return 10; + else if (flags & ConstraintGroupNode::e1_TO_4) + return 2; + + return 0; +} + + +PX_FORCE_INLINE void Sc::ConstraintGroupNode::setProjectionCountHint(PxU32 constraintsToProjectCount) +{ + PxU8 tmpFlags = flags; + tmpFlags &= PxU8(ConstraintGroupNode::eCLEAR_MASK); + + if (constraintsToProjectCount >= 65) + tmpFlags |= ConstraintGroupNode::e65_TO_INF; + else if (constraintsToProjectCount >= 17) + tmpFlags |= ConstraintGroupNode::e17_TO_64; + else if (constraintsToProjectCount >= 5) + tmpFlags |= ConstraintGroupNode::e5_TO_16; + else if (constraintsToProjectCount >= 1) + tmpFlags |= ConstraintGroupNode::e1_TO_4; + + flags = tmpFlags; +} + + +PX_FORCE_INLINE void Sc::ConstraintGroupNode::buildProjectionTrees() +{ + PX_ASSERT(this == parent); // Only call for group roots + PX_ASSERT(!hasProjectionTreeRoot()); + + ConstraintProjectionTree::buildProjectionTrees(*this); +} + + +PX_FORCE_INLINE void Sc::ConstraintGroupNode::purgeProjectionTrees() +{ + PX_ASSERT(this == parent); // Only call for group roots + PX_ASSERT(hasProjectionTreeRoot()); + ConstraintProjectionTree::purgeProjectionTrees(*this); +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp new file mode 100644 index 000000000..c2562d8d3 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp @@ -0,0 +1,169 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScConstraintInteraction.h" +#include "ScConstraintSim.h" +#include "ScBodySim.h" +#include "ScScene.h" +#include "PxsRigidBody.h" +#include "PxsSimpleIslandManager.h" + +using namespace physx; +using namespace Sc; + +ConstraintInteraction::ConstraintInteraction(ConstraintSim* constraint, RigidSim& r0, RigidSim& r1) : + Interaction (r0, r1, InteractionType::eCONSTRAINTSHADER, InteractionFlag::eCONSTRAINT), + mConstraint (constraint) +{ + registerInActors(); + + BodySim* b0 = mConstraint->getBody(0); + BodySim* b1 = mConstraint->getBody(1); + + if(b0) + b0->onConstraintAttach(); + if(b1) + b1->onConstraintAttach(); + + IG::SimpleIslandManager* simpleIslandManager = getScene().getSimpleIslandManager(); + mEdgeIndex = simpleIslandManager->addConstraint(&mConstraint->getLowLevelConstraint(), b0 ? b0->getNodeIndex() : IG::NodeIndex(), b1 ? b1->getNodeIndex() : IG::NodeIndex(), this); +} + +ConstraintInteraction::~ConstraintInteraction() +{ + PX_ASSERT(!readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)); + PX_ASSERT(!getDirtyFlags()); + PX_ASSERT(!mConstraint->readFlag(ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED)); +} + +static PX_FORCE_INLINE void removeFromActiveBreakableList(ConstraintSim* constraint, Scene& s) +{ + if(constraint->readFlag(ConstraintSim::eBREAKABLE | ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED) == (ConstraintSim::eBREAKABLE | ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED)) + s.removeActiveBreakableConstraint(constraint); +} + +void ConstraintInteraction::destroy() +{ + setClean(true); // removes the pair from the dirty interaction list etc. + + Scene& scene = getScene(); + + removeFromActiveBreakableList(mConstraint, scene); + + if(mEdgeIndex != IG_INVALID_EDGE) + scene.getSimpleIslandManager()->removeConnection(mEdgeIndex); + mEdgeIndex = IG_INVALID_EDGE; + + unregisterFromActors(); + + BodySim* b0 = mConstraint->getBody(0); + BodySim* b1 = mConstraint->getBody(1); + + if(b0) + b0->onConstraintDetach(); // Note: Has to be done AFTER the interaction has unregistered from the actors + if(b1) + b1->onConstraintDetach(); // Note: Has to be done AFTER the interaction has unregistered from the actors + + clearInteractionFlag(InteractionFlag::eIS_ACTIVE); // ensures that broken constraints do not go into the list of active breakable constraints anymore +} + +void ConstraintInteraction::updateState() +{ + PX_ASSERT(!mConstraint->isBroken()); + PX_ASSERT(getDirtyFlags() & InteractionDirtyFlag::eBODY_KINEMATIC); // at the moment this should be the only reason for this method being called + + // at least one of the bodies got switched from kinematic to dynamic. This will not have changed the sleep state of the interactions, so the + // constraint interactions are just marked dirty and processed as part of the dirty interaction update system. + // + // -> need to check whether to activate the constraint and whether constraint break testing + // is now necessary + // + // the transition from dynamic to kinematic will always trigger an onDeactivate_() (because the body gets deactivated) + // and thus there is no need to consider that case here. + // + + onActivate_(NULL); // note: this will not activate if the necessary conditions are not met, so it can be called even if the pair has been deactivated again before the + // simulation step started +} + +bool ConstraintInteraction::onActivate_(void*) +{ + PX_ASSERT(!mConstraint->isBroken()); + + BodySim* b0 = mConstraint->getBody(0); + BodySim* b1 = mConstraint->getBody(1); + + const bool b0Vote = !b0 || b0->isActive(); + const bool b1Vote = !b1 || b1->isActive(); + + const bool b0Dynamic = b0 && (!b0->isKinematic()); + const bool b1Dynamic = b1 && (!b1->isKinematic()); + + // + // note: constraints between kinematics and kinematics/statics are always inactive and must not be activated + // + if((b0Vote || b1Vote) && (b0Dynamic || b1Dynamic)) + { + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + + if(mConstraint->readFlag(ConstraintSim::eBREAKABLE | ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED) == ConstraintSim::eBREAKABLE) + getScene().addActiveBreakableConstraint(mConstraint, this); + + return true; + } + else + return false; +} + +bool ConstraintInteraction::onDeactivate_() +{ + const BodySim* b0 = mConstraint->getBody(0); + const BodySim* b1 = mConstraint->getBody(1); + + const bool b0Dynamic = b0 && (!b0->isKinematic()); + const bool b1Dynamic = b1 && (!b1->isKinematic()); + + PX_ASSERT( (!b0 && b1 && !b1->isActive()) || + (!b1 && b0 && !b0->isActive()) || + ((b0 && b1 && (!b0->isActive() || !b1->isActive()))) ); + + // + // note: constraints between kinematics and kinematics/statics should always get deactivated + // + if(((!b0 || !b0->isActive()) && (!b1 || !b1->isActive())) || (!b0Dynamic && !b1Dynamic)) + { + removeFromActiveBreakableList(mConstraint, getScene()); + + clearInteractionFlag(InteractionFlag::eIS_ACTIVE); + + return true; + } + else + return false; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h new file mode 100644 index 000000000..b6d38d795 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_CONSTRAINTSHADERINTERACTION +#define PX_PHYSICS_SCP_CONSTRAINTSHADERINTERACTION + +#include "ScInteraction.h" + +namespace physx +{ +namespace Sc +{ + class ConstraintSim; + class RigidSim; + + class ConstraintInteraction : public Interaction + { + public: + ConstraintInteraction(ConstraintSim* shader, RigidSim& r0, RigidSim& r1); + ~ConstraintInteraction(); + + bool onActivate_(void* data); + bool onDeactivate_(); + + void updateState(); + void destroy(); // disables the interaction and unregisters from the system. Does NOT delete the object. This is used on destruction but also when a constraint breaks. + + PX_FORCE_INLINE ConstraintSim* getConstraint() { return mConstraint; } + PX_FORCE_INLINE PxU32 getEdgeIndex() const { return mEdgeIndex; } + + private: + ConstraintSim* mConstraint; + PxU32 mEdgeIndex; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp new file mode 100644 index 000000000..4b69fd29c --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp @@ -0,0 +1,505 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxcScratchAllocator.h" +#include "ScConstraintProjectionManager.h" +#include "ScBodySim.h" +#include "ScConstraintSim.h" +#include "ScConstraintInteraction.h" + +using namespace physx; + + +namespace physx +{ +namespace Sc +{ + +template +class ScratchAllocatorList +{ +private: + struct ElementBlock + { + PX_FORCE_INLINE ElementBlock() {} + PX_FORCE_INLINE void init(PxU32 countAtStart) { next = NULL; count = countAtStart; } + + ElementBlock* next; + PxU32 count; + T elements[elementsPerBlock]; + }; + + PX_FORCE_INLINE const ScratchAllocatorList& operator=(const ScratchAllocatorList&) {} + + +public: + class Iterator + { + friend class ScratchAllocatorList; + + public: + T const* getNext() + { + if (mCurrentBlock) + { + if (mIndex < mCurrentBlock->count) + { + return &mCurrentBlock->elements[mIndex++]; + } + else + { + if (mCurrentBlock->next) + { + PX_ASSERT(mCurrentBlock->count == elementsPerBlock); + mCurrentBlock = mCurrentBlock->next; + PX_ASSERT(mCurrentBlock->count > 0); + + mIndex = 1; + return &mCurrentBlock->elements[0]; + } + else + return NULL; + } + } + else + return NULL; + } + + private: + Iterator(const ElementBlock* startBlock) : mCurrentBlock(startBlock), mIndex(0) {} + + private: + const ElementBlock* mCurrentBlock; + PxU32 mIndex; + }; + + PX_FORCE_INLINE ScratchAllocatorList(PxcScratchAllocator& scratchAllocator) : mScratchAllocator(scratchAllocator) + { + mFirstBlock = reinterpret_cast(scratchAllocator.alloc(sizeof(ElementBlock), true)); + if (mFirstBlock) + mFirstBlock->init(0); + + mCurrentBlock = mFirstBlock; + } + + PX_FORCE_INLINE ~ScratchAllocatorList() + { + freeMemory(); + } + + PX_FORCE_INLINE bool add(const T& element) + { + if (mCurrentBlock) + { + if (mCurrentBlock->count < elementsPerBlock) + { + mCurrentBlock->elements[mCurrentBlock->count] = element; + mCurrentBlock->count++; + return true; + } + else + { + PX_ASSERT(mCurrentBlock->next == NULL); + PX_ASSERT(mCurrentBlock->count == elementsPerBlock); + + ElementBlock* newBlock = reinterpret_cast(mScratchAllocator.alloc(sizeof(ElementBlock), true)); + if (newBlock) + { + newBlock->init(1); + newBlock->elements[0] = element; + mCurrentBlock->next = newBlock; + mCurrentBlock = newBlock; + return true; + } + else + return false; + } + } + else + return false; + } + + PX_FORCE_INLINE Iterator getIterator() const + { + return Iterator(mFirstBlock); + } + + PX_FORCE_INLINE void freeMemory() + { + ElementBlock* block = mFirstBlock; + + while(block) + { + ElementBlock* blockToFree = block; + block = block->next; + + mScratchAllocator.free(blockToFree); + } + } + + +private: + PxcScratchAllocator& mScratchAllocator; + ElementBlock* mFirstBlock; + ElementBlock* mCurrentBlock; +}; + +} +} + + +Sc::ConstraintProjectionManager::ConstraintProjectionManager() : + mNodePool(PX_DEBUG_EXP("projectionNodePool")) +{ +} + + +void Sc::ConstraintProjectionManager::addToPendingGroupUpdates(Sc::ConstraintSim& s) +{ + PX_ASSERT(!s.readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)); + bool isNew = mPendingGroupUpdates.insert(&s); + PX_UNUSED(isNew); + PX_ASSERT(isNew); + + s.setFlag(ConstraintSim::ePENDING_GROUP_UPDATE); +} + + +void Sc::ConstraintProjectionManager::removeFromPendingGroupUpdates(Sc::ConstraintSim& s) +{ + PX_ASSERT(s.readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)); + bool didExist = mPendingGroupUpdates.erase(&s); + PX_UNUSED(didExist); + PX_ASSERT(didExist); + + s.clearFlag(ConstraintSim::ePENDING_GROUP_UPDATE); +} + + +void Sc::ConstraintProjectionManager::addToPendingTreeUpdates(ConstraintGroupNode& n) +{ + PX_ASSERT(&n == &n.getRoot()); + PX_ASSERT(!n.readFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE)); + bool isNew = mPendingTreeUpdates.insert(&n); + PX_UNUSED(isNew); + PX_ASSERT(isNew); + + n.raiseFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE); +} + + +void Sc::ConstraintProjectionManager::removeFromPendingTreeUpdates(ConstraintGroupNode& n) +{ + PX_ASSERT(&n == &n.getRoot()); + PX_ASSERT(n.readFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE)); + bool didExist = mPendingTreeUpdates.erase(&n); + PX_UNUSED(didExist); + PX_ASSERT(didExist); + + n.clearFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE); +} + + +PX_INLINE Sc::ConstraintGroupNode* Sc::ConstraintProjectionManager::createGroupNode(BodySim& b) +{ + ConstraintGroupNode* n = mNodePool.construct(b); + b.setConstraintGroup(n); + return n; +} + + +// +// Implementation of UNION of +// UNION-FIND algo. +// It also updates the group traversal +// linked list. +// +void Sc::ConstraintProjectionManager::groupUnion(ConstraintGroupNode& root0, ConstraintGroupNode& root1) +{ + // Should only get called for the roots + PX_ASSERT(&root0 == root0.parent); + PX_ASSERT(&root1 == root1.parent); + + if (&root0 != &root1) //different groups? If not, its already merged. + { + //UNION(this, other); //union-find algo unites groups. + ConstraintGroupNode* newRoot; + ConstraintGroupNode* otherRoot; + if (root0.rank > root1.rank) + { + //hisGroup appended to mygroup. + newRoot = &root0; + otherRoot = &root1; + } + else + { + //myGroup appended to hisGroup. + newRoot = &root1; + otherRoot = &root0; + //there is a chance that the two ranks were equal, in which case the tree depth just increased. + root1.rank++; + } + + PX_ASSERT(newRoot->parent == newRoot); + otherRoot->parent = newRoot; + + //update traversal linked list: + newRoot->tail->next = otherRoot; + newRoot->tail = otherRoot->tail; + } +} + + +// +// Add a body to a constraint projection group. +// +void Sc::ConstraintProjectionManager::addToGroup(BodySim& b, BodySim* other, ConstraintSim& c) +{ + // If both bodies of the constraint are defined, we want to fetch the reference to the group root + // from body 0 by default (allows to avoid checking both) + PX_ASSERT(&b == c.getBody(0) || (c.getBody(0) == NULL && &b == c.getBody(1))); + PX_UNUSED(c); + + ConstraintGroupNode* myRoot; + if (!b.getConstraintGroup()) + myRoot = createGroupNode(b); + else + { + myRoot = &b.getConstraintGroup()->getRoot(); + if (myRoot->hasProjectionTreeRoot()) + myRoot->purgeProjectionTrees(); // If a new constraint gets added to a constraint group, projection trees need to be recreated + } + + if (other) + { + ConstraintGroupNode* otherRoot; + if (!other->getConstraintGroup()) + otherRoot = createGroupNode(*other); + else + { + otherRoot = &other->getConstraintGroup()->getRoot(); + if (otherRoot->hasProjectionTreeRoot()) + otherRoot->purgeProjectionTrees(); // If a new constraint gets added to a constraint group, projection trees need to be recreated + } + + //merge the two groups, if disjoint. + groupUnion(*myRoot, *otherRoot); + } +} + + +// +// Add all projection constraints connected to the specified body to the pending update list but +// ignore the specified constraint. +// +void Sc::ConstraintProjectionManager::markConnectedConstraintsForUpdate(BodySim& b, ConstraintSim* c) +{ + PxU32 size = b.getActorInteractionCount(); + Interaction** interactions = b.getActorInteractions(); + while(size--) + { + Interaction* interaction = *interactions++; + if (interaction->getType() == InteractionType::eCONSTRAINTSHADER) + { + ConstraintSim* ct = static_cast(interaction)->getConstraint(); + + if ((ct != c) && ct->needsProjection() && (!ct->readFlag(ConstraintSim::ePENDING_GROUP_UPDATE))) + { + //mark constraint for pending update: + addToPendingGroupUpdates(*ct); + } + } + } +} + + +// +// Add all constraints connected to the specified body to an array but +// ignore the specified constraint. +// +PX_FORCE_INLINE static void dumpConnectedConstraints(Sc::BodySim& b, Sc::ConstraintSim* c, Sc::ScratchAllocatorList& constraintList) +{ + PxU32 size = b.getActorInteractionCount(); + Sc::Interaction** interactions = b.getActorInteractions(); + while(size--) + { + Sc::Interaction* interaction = *interactions++; + if (interaction->getType() == Sc::InteractionType::eCONSTRAINTSHADER) + { + Sc::ConstraintSim* ct = static_cast(interaction)->getConstraint(); + + if ((ct != c) && (!ct->readFlag(Sc::ConstraintSim::ePENDING_GROUP_UPDATE))) + { + bool success = constraintList.add(ct); + PX_UNUSED(success); + PX_ASSERT(success); + } + } + } +} + + +PX_FORCE_INLINE void Sc::ConstraintProjectionManager::processConstraintForGroupBuilding(ConstraintSim* c, ScratchAllocatorList& constraintList) +{ + c->clearFlag(ConstraintSim::ePENDING_GROUP_UPDATE); + + // Find all constraints connected to the two bodies of the dirty constraint. + // - Constraints to static anchors are ignored (note: kinematics can't be ignored because they might get switched to dynamics any time which + // does trigger a projection tree rebuild but not a constraint tree rebuild + // - Already processed bodies are ignored as well + BodySim* b0 = c->getBody(0); + if (b0 && !b0->getConstraintGroup()) + { + dumpConnectedConstraints(*b0, c, constraintList); + } + BodySim* b1 = c->getBody(1); + if (b1 && !b1->getConstraintGroup()) + { + dumpConnectedConstraints(*b1, c, constraintList); + } + + BodySim* b = c->getAnyBody(); + PX_ASSERT(b); + + addToGroup(*b, c->getOtherBody(b), *c); //this will eventually merge some body's constraint groups. +} + + +void Sc::ConstraintProjectionManager::processPendingUpdates(PxcScratchAllocator& scratchAllocator) +{ + // + // if there are dirty projection trees, then rebuild them + // + const PxU32 nbProjectionTreesToUpdate = mPendingTreeUpdates.size(); + if (nbProjectionTreesToUpdate) + { + ConstraintGroupNode* const* projectionTreesToUpdate = mPendingTreeUpdates.getEntries(); + for(PxU32 i=0; i < nbProjectionTreesToUpdate; i++) + { + ConstraintGroupNode* n = projectionTreesToUpdate[i]; + + PX_ASSERT(n == &n->getRoot()); // only root nodes should be in that list + PX_ASSERT(n->readFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE)); + + n->clearFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE); + + // note: it is valid to get here and not have a projection root. This is the case if all nodes of a constraint graph are kinematic + // at some point (hence no projection root) and later some of those get switched to dynamic. + if (n->hasProjectionTreeRoot()) + n->purgeProjectionTrees(); + n->buildProjectionTrees(); + } + + mPendingTreeUpdates.clear(); + } + + // + // if there are new/dirty constraints, update groups + // + const PxU32 nbProjectionConstraintsToUpdate = mPendingGroupUpdates.size(); + + if (nbProjectionConstraintsToUpdate) + { + ScratchAllocatorList nonProjectionConstraintList(scratchAllocator); + + ConstraintSim* const* projectionConstraintsToUpdate = mPendingGroupUpdates.getEntries(); + +#if PX_DEBUG + // At the beginning the list should only contain constraints with projection. + // Further below other constraints, connected to the constraints with projection, will be added too. + for(PxU32 i=0; i < nbProjectionConstraintsToUpdate; i++) + { + PX_ASSERT(projectionConstraintsToUpdate[i]->needsProjection()); + } +#endif + for(PxU32 i=0; i < nbProjectionConstraintsToUpdate; i++) + { + processConstraintForGroupBuilding(projectionConstraintsToUpdate[i], nonProjectionConstraintList); + } + + ScratchAllocatorList::Iterator iter = nonProjectionConstraintList.getIterator(); + ConstraintSim* const* nextConstraint = iter.getNext(); + while(nextConstraint) + { + processConstraintForGroupBuilding(*nextConstraint, nonProjectionConstraintList); + + nextConstraint = iter.getNext(); + } + + // Now find all the newly made groups and build projection trees. + // Don't need to iterate over the additionally constraints since the roots are supposed to be + // fetchable from any node. + for (PxU32 i=0; i < nbProjectionConstraintsToUpdate; i++) + { + ConstraintSim* c = projectionConstraintsToUpdate[i]; + BodySim* b = c->getAnyBody(); + PX_ASSERT(b); + PX_ASSERT(b->getConstraintGroup()); + + ConstraintGroupNode& root = b->getConstraintGroup()->getRoot(); + if (!root.hasProjectionTreeRoot()) // Build projection tree only once + root.buildProjectionTrees(); + } + + mPendingGroupUpdates.clear(); + } +} + + +// +// Called if a body or a constraint gets deleted. All projecting constraints of the +// group (except the deleted one) are moved to the dirty list and all group nodes are destroyed. +// +void Sc::ConstraintProjectionManager::invalidateGroup(ConstraintGroupNode& node, ConstraintSim* deletedConstraint) +{ + ConstraintGroupNode* n = &node.getRoot(); + + if (n->readFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE)) + { + removeFromPendingTreeUpdates(*n); + } + + while (n) //go through nodes in constraint group + { + markConnectedConstraintsForUpdate(*n->body, deletedConstraint); + + //destroy the body's constraint group information + + ConstraintGroupNode* next = n->next; //save next node ptr before we destroy it! + + BodySim* b = n->body; + b->setConstraintGroup(NULL); + if (n->hasProjectionTreeRoot()) + n->purgeProjectionTrees(); + mNodePool.destroy(n); + + n = next; + } +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h new file mode 100644 index 000000000..34a0c8185 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h @@ -0,0 +1,84 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_CONSTRAINT_PROJECTION_MANAGER +#define PX_PHYSICS_SCP_CONSTRAINT_PROJECTION_MANAGER + +#include "PsPool.h" +#include "PsHashSet.h" +#include "ScConstraintGroupNode.h" + +namespace physx +{ + class PxcScratchAllocator; + +namespace Sc +{ + class ConstraintSim; + class BodySim; + template class ScratchAllocatorList; + + class ConstraintProjectionManager : public Ps::UserAllocated + { + public: + ConstraintProjectionManager(); + ~ConstraintProjectionManager() {} + + void addToPendingGroupUpdates(ConstraintSim& s); + void removeFromPendingGroupUpdates(ConstraintSim& s); + + void addToPendingTreeUpdates(ConstraintGroupNode& n); + void removeFromPendingTreeUpdates(ConstraintGroupNode& n); + + void processPendingUpdates(PxcScratchAllocator&); + void invalidateGroup(ConstraintGroupNode& node, ConstraintSim* constraintDeleted); + + private: + PX_INLINE Sc::ConstraintGroupNode* createGroupNode(BodySim& b); + + void addToGroup(BodySim& b, BodySim* other, ConstraintSim& c); + void groupUnion(ConstraintGroupNode& root0, ConstraintGroupNode& root1); + void markConnectedConstraintsForUpdate(BodySim& b, ConstraintSim* c); + PX_FORCE_INLINE void processConstraintForGroupBuilding(ConstraintSim* c, ScratchAllocatorList&); + + + private: + Ps::Pool mNodePool; + Ps::CoalescedHashSet mPendingGroupUpdates; //list of constraints for which constraint projection groups need to be generated/updated + Ps::CoalescedHashSet mPendingTreeUpdates; //list of constraint groups that need their projection trees rebuilt. Note: non of the + //constraints in those groups are allowed to be in mPendingGroupUpdates at the same time + //because a group update will automatically trigger tree rebuilds. + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp new file mode 100644 index 000000000..9dc2d245c --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp @@ -0,0 +1,567 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScConstraintProjectionTree.h" +#include "ScScene.h" +#include "ScBodySim.h" +#include "ScConstraintCore.h" +#include "ScConstraintSim.h" +#include "ScConstraintInteraction.h" + +#include "PsFoundation.h" +#include "PsBasicTemplates.h" +#include "PsSort.h" +#include "PsArray.h" + +using namespace physx; + +//------------------------------------------------------------------------------------------ +// +// The projection tree related code +// +// Projection trees are built out of a constraint group/graph. The constraint group just tracks +// the constraint connectivity while the projection trees define the projection root and +// the projection order. +// A constraint group can contain multiple projection trees. +// +//------------------------------------------------------------------------------------------ + +class Sc::BodyRank +{ +public: + PX_INLINE bool operator>(const BodyRank & b) const + { + return rank > b.rank; + } + + Sc::ConstraintGroupNode* startingNode; + Sc::ConstraintSim* constraintToFixedAnchor; + PxU32 rank; + + // + // The following weights are defined to fulfill the projection priorities described further below + // + static const PxU32 sOneWayProjection = PxU32(1 << 31); + static const PxU32 sAttachedToStatic = (1 << 30); + static const PxU32 sAttachedToKinematic = (1 << 29); + static const PxU32 sAllDominantDynamic = (1 << 28); // if for a dynamic body all connections with projection, are one-way towards the body + static const PxU32 sDominantDynamic = (1 << 27); // almost the same as above but there is at least one two-way projection + static const PxU32 sAttachedToDynamic = 1; + static const PxU32 sPrimaryTreeRootMinRank = sOneWayProjection | sAllDominantDynamic; +}; + + +PX_INLINE bool isFixedBody(const Sc::BodySim* b) +{ + return (!b || (b->isKinematic())); +} + + +void Sc::ConstraintProjectionTree::getConstraintStatus(const ConstraintSim& c, const BodySim* b, BodySim*& otherBody, PxU32& projectToBody, PxU32& projectToOtherBody) +{ + const PxU32 isBroken = c.isBroken() ? 0 : 0xffffffff; + const PxU32 projFlags = c.getCore().getFlags() & PxConstraintFlag::ePROJECTION; + + if (b == c.getBody(0)) + { + projectToBody = isBroken & (projFlags & PxConstraintFlag::ePROJECT_TO_ACTOR0); + projectToOtherBody = isBroken & (projFlags & PxConstraintFlag::ePROJECT_TO_ACTOR1); + + otherBody = c.getBody(1); + } + else + { + projectToBody = isBroken & (projFlags & PxConstraintFlag::ePROJECT_TO_ACTOR1); + projectToOtherBody = isBroken & (projFlags & PxConstraintFlag::ePROJECT_TO_ACTOR0); + + otherBody = c.getBody(0); + } +} + + +void Sc::ConstraintProjectionTree::rankConstraint(ConstraintSim& c, BodyRank& br, PxU32& dominanceTracking, PxU32& constraintsToProjectCount) +{ + PxU32 projectToBody, projectToOtherBody; + BodySim* otherB; + getConstraintStatus(c, br.startingNode->body, otherB, projectToBody, projectToOtherBody); + + if (isFixedBody(otherB)) // joint to fixed anchor + { + PxU32 rank; + if (projectToOtherBody) + { + dominanceTracking = 0; // makes sure that the flags below will never get raised again for the body + br.rank &= ~(BodyRank::sAllDominantDynamic | BodyRank::sDominantDynamic); + rank = BodyRank::sOneWayProjection; //we should prefer picking projected constraints as the root over non-projected ones. + constraintsToProjectCount++; + } + else + rank = 0; + + if (!otherB) + rank |= BodyRank::sAttachedToStatic; + else + { + PX_ASSERT(otherB->isKinematic()); + rank |= BodyRank::sAttachedToKinematic; + } + + // the highest ranked fixed anchor constraint should get tracked + if ((!br.constraintToFixedAnchor) || (rank > br.rank)) + br.constraintToFixedAnchor = &c; + + br.rank |= rank; + } + else + { + if (projectToBody && projectToOtherBody) + { + dominanceTracking &= ~BodyRank::sAllDominantDynamic; // makes sure that from now on this will never get raised again for the body + br.rank &= ~BodyRank::sAllDominantDynamic; + constraintsToProjectCount++; + } + else if (projectToOtherBody) + { + dominanceTracking &= ~(BodyRank::sAllDominantDynamic | BodyRank::sDominantDynamic); // makes sure that from now on these will never get raised again for the body + br.rank &= ~(BodyRank::sAllDominantDynamic | BodyRank::sDominantDynamic); + constraintsToProjectCount++; + } + else if (projectToBody) + { + br.rank |= BodyRank::sOneWayProjection | (dominanceTracking & (BodyRank::sAllDominantDynamic | BodyRank::sDominantDynamic)); + constraintsToProjectCount++; + } + + br.rank += BodyRank::sAttachedToDynamic; + } +} + + +/* +the goal here is to take the constraint group whose root is passed, and create one or more projection trees. + +At the moment, the group has to be acyclic and have at most 1 constraint with the ground to be accepted +without being broken up into multiple trees. + +We 'flood fill' the constraint graph several times, starting at bodies where projection trees can be rooted. +Projection tree roots are always dynamic bodies which either need to get projected to a fixed anchor directly +or have projecting constraints between dynamics some way along the tree branches. Static and kinematic actors +are never roots and will not be explicitly part of any tree (but a tree root can project to at most one such fixed node). + +The algo looks like this: + +for all bodies +mark body as undiscovered +rank this body + +The rank of a body depends on the constraints it's connected to. It defines the projection priority which +should be (highest first): +- dynamic attached to static/world with projection +- dynamic attached to kinematic with projection +- all dominant dynamic (has projecting constraints and all of them are one-way towards this dynamic) +---- all the ones above are guaranteed tree roots +- dominant dynamic (same as above but there is at least one projecting two-way constraint as well) +- partially dominant dynamic (has at least one projecting one-way constraints towards this dynamic and at least one projecting one-way constraints towards an other body) +- dynamic attached to static/world without projection +- dynamic attached to kinematic without projection +- dynamic with or without two-way projecting constraints to other dynamics (among these, the one with the highest connectivity count wins) + +for the first three priority types sorted according to rank: +create a projection tree root and grow the tree one connectivity layer at a time + +do the same for dominant dynamic bodies that have not been visited/discovered yet + +for all remaining bodies sorted according to rank: +if the body still hasn't been visited/discovered start a projection tree there and build the whole tree in one go +before moving to the next potential root. +*/ +void Sc::ConstraintProjectionTree::buildProjectionTrees(ConstraintGroupNode& root) +{ + PX_ASSERT(&root == root.parent); + PX_ASSERT(!root.hasProjectionTreeRoot()); + + Ps::InlineArray bodyRankArray PX_DEBUG_EXP("bodyRankArray"); + BodyRank br; + PxU32 constraintsToProjectCount = 0; + ConstraintGroupNode* node0 = &root; + while (node0) //for all nodes in group + { + PX_ASSERT(node0->body); + if (!node0->body->isKinematic()) + { + node0->clearFlag(ConstraintGroupNode::eDISCOVERED); + + //rank + br.startingNode = node0; + br.rank = 0; + br.constraintToFixedAnchor = 0; + PxU32 dominanceTracking = BodyRank::sAllDominantDynamic | BodyRank::sDominantDynamic; + + //go through all constraints connected to body + PxU32 size = node0->body->getActorInteractionCount(); + Sc::Interaction** interactions = node0->body->getActorInteractions(); + while(size--) + { + Interaction* interaction = *interactions++; + if (interaction->getType() == InteractionType::eCONSTRAINTSHADER) + { + ConstraintSim* c = static_cast(interaction)->getConstraint(); + rankConstraint(*c, br, dominanceTracking, constraintsToProjectCount); + } + } + + PX_ASSERT(br.rank); //if it has no constraints then why is it in the constraint group? + + if (br.rank >= BodyRank::sPrimaryTreeRootMinRank) + node0->raiseFlag(ConstraintGroupNode::eDISCOVERED); // we create a tree for each node attached to a fixed anchor, or a node which is an all dominating dynamic + // -> make sure they do not include each other + + bodyRankArray.pushBack(br); + } + else + node0->raiseFlag(ConstraintGroupNode::eDISCOVERED); // a kinematic does not get projected, it might only get projected to and it is never part of a tree. + + node0 = node0->next; + } + + root.setProjectionCountHint(constraintsToProjectCount); + + if (bodyRankArray.size()) // all of the bodies might have been switched to kinematic in which case there will be no ranked body + { + //sort bodyRankArray + + Ps::sort(&bodyRankArray.front(), bodyRankArray.size(), Ps::Greater()); + + ConstraintGroupNode** nodeQueue = reinterpret_cast(PX_ALLOC_TEMP(sizeof(ConstraintGroupNode*)*bodyRankArray.size(), "ProjectionNodeQueue")); + if (nodeQueue) + { + //build the projectionTree + + ConstraintGroupNode* firstProjectionTreeRoot = NULL; + + //go through it in sorted order + + // + // bodies attached to fixed anchors with projecting constraints or all dominant rigid dynamics should get processed first. + // For each of those we create a projection tree for sure, by extending one connectivity level from the root at a time. + // This way we make sure that scenarios like a bridge that is attached to fixed anchors at both ends breaks in the middle + // and not at one of the fixed anchors. + // + // this gets repeated for dominant dynamics. The reason for this is to cover cases where a dominant dynamic is connected to + // a higher ranked node by a chain of two-way constraints. In such a case the two-way constraint should project the dominant + // dynamic towards the higher ranked node and not start a tree on its own. + // + PxU32 brIdx = 0; + PxU32 stopIdx = bodyRankArray.size(); + PxU32 skipCount = 0; + PxU32 ranksToProcess = BodyRank::sPrimaryTreeRootMinRank; + ConstraintGroupNode** nodeQueueEnd; + ConstraintGroupNode** nodeQueueCurrent; + for(PxU32 i=0; i < 2; i++) + { + nodeQueueEnd = nodeQueue; + while((brIdx < stopIdx) && (bodyRankArray[brIdx].rank >= ranksToProcess)) + { + BodyRank& bRank = bodyRankArray[brIdx]; + PX_ASSERT((brIdx == 0) || (bRank.rank <= bodyRankArray[brIdx-1].rank)); + + ConstraintGroupNode& node = *bRank.startingNode; + PX_ASSERT(node.readFlag(ConstraintGroupNode::eDISCOVERED)); + + node.initProjectionData(NULL, bRank.constraintToFixedAnchor); + + if (bRank.rank & (BodyRank::sAttachedToStatic | BodyRank::sAttachedToKinematic)) + { + // for static/kinematic attached, the current node is already a child, so we must not traverse the neighborhood yet + // but rather add the current node to the queue. + PX_ASSERT(bRank.constraintToFixedAnchor); + *nodeQueueEnd = &node; + nodeQueueEnd++; + } + else + { + PX_ASSERT(!bRank.constraintToFixedAnchor); + PxU32 addedNodeCount = projectionTreeBuildStep(node, bRank.constraintToFixedAnchor, nodeQueueEnd); + nodeQueueEnd += addedNodeCount; + } + + node.projectionNextRoot = firstProjectionTreeRoot; + firstProjectionTreeRoot = &node; + + brIdx++; + } + + // first neighbor connectivity level has been pushed to a queue for all chosen tree roots. Now extend the trees one level at a time. + nodeQueueCurrent = nodeQueue; + while(nodeQueueCurrent != nodeQueueEnd) + { + ConstraintGroupNode* node = *nodeQueueCurrent; + PX_ASSERT(node->readFlag(ConstraintGroupNode::eDISCOVERED)); + nodeQueueCurrent++; + + PxU32 addedNodeCount = projectionTreeBuildStep(*node, node->projectionConstraint, nodeQueueEnd); + nodeQueueEnd += addedNodeCount; + } + + brIdx += skipCount; + skipCount = 0; + + // find dominant dynamics that have not been discovered yet and arrange them in a consecutive block + ranksToProcess = BodyRank::sOneWayProjection | BodyRank::sDominantDynamic; + stopIdx = brIdx; + PxU32 k = brIdx; + while((k < bodyRankArray.size()) && (bodyRankArray[k].rank >= ranksToProcess)) + { + ConstraintGroupNode* node = bodyRankArray[k].startingNode; + if (!node->readFlag(ConstraintGroupNode::eDISCOVERED)) + { + node->raiseFlag(ConstraintGroupNode::eDISCOVERED); + bodyRankArray[stopIdx] = bodyRankArray[k]; + stopIdx++; + } + else + skipCount++; + + k++; + } + } + + // + // for every body that has not been discovered yet, we build a tree. Here we do not advance one connectivity level + // at a time because there should be no fight over the nodes among equal roots anymore (or rather no fight that could + // break one-way projection in an unfair way). + // + PX_ASSERT((brIdx == 0) || (brIdx == bodyRankArray.size()) || (bodyRankArray[brIdx].rank < bodyRankArray[brIdx-1].rank)); + for(PxU32 i=brIdx; i < bodyRankArray.size(); i++) + { + nodeQueueEnd = nodeQueue; + + BodyRank& bRank = bodyRankArray[i]; + PX_ASSERT((i == brIdx) || (bRank.rank <= bodyRankArray[i-1].rank)); +#ifdef _DEBUG + if (bRank.rank & (BodyRank::sAttachedToStatic | BodyRank::sAttachedToKinematic)) + { PX_ASSERT(bRank.constraintToFixedAnchor); } + else + { PX_ASSERT(!bRank.constraintToFixedAnchor); } +#endif + + ConstraintGroupNode& node = *bRank.startingNode; + if (!node.readFlag(ConstraintGroupNode::eDISCOVERED)) + { + node.raiseFlag(ConstraintGroupNode::eDISCOVERED); + + PxU32 addedNodeCount = projectionTreeBuildStep(node, bRank.constraintToFixedAnchor, nodeQueueEnd); + nodeQueueEnd += addedNodeCount; + + nodeQueueCurrent = nodeQueue; + while(nodeQueueCurrent != nodeQueueEnd) + { + ConstraintGroupNode* n = *nodeQueueCurrent; + PX_ASSERT(n->readFlag(ConstraintGroupNode::eDISCOVERED)); + PX_ASSERT(n->projectionConstraint); + nodeQueueCurrent++; + + PxU32 nodeCount = projectionTreeBuildStep(*n, n->projectionConstraint, nodeQueueEnd); + nodeQueueEnd += nodeCount; + } + + node.projectionNextRoot = firstProjectionTreeRoot; + firstProjectionTreeRoot = &node; + } + } + + root.setProjectionTreeRoot(firstProjectionTreeRoot); + + PX_FREE(nodeQueue); + } + else + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Allocating projection node queue failed!"); + } +} + + +PxU32 Sc::ConstraintProjectionTree::projectionTreeBuildStep(ConstraintGroupNode& node, ConstraintSim* cToParent, ConstraintGroupNode** nodeQueue) +{ + PX_ASSERT(node.readFlag(ConstraintGroupNode::eDISCOVERED)); + + PxU32 nodeQueueFillCount = 0; + + //go through all constraints attached to the body. + BodySim* body = node.body; + PxU32 size = body->getActorInteractionCount(); + Sc::Interaction** interactions = body->getActorInteractions(); + while(size--) + { + Interaction* interaction = *interactions++; + if (interaction->getType() == InteractionType::eCONSTRAINTSHADER) + { + ConstraintSim* c = static_cast(interaction)->getConstraint(); + + if (c != cToParent) //don't go back along the edge we came from (not really necessary I guess since the ConstraintGroupNode::eDISCOVERED marker should solve this) + { + PxU32 projectToBody, projectToOtherBody; + BodySim* neighbor; + getConstraintStatus(*c, body, neighbor, projectToBody, projectToOtherBody); + + if(!isFixedBody(neighbor) && (!projectToOtherBody || projectToBody)) //just ignore the eventual constraint with environment over here. Body might be attached to multiple fixed anchors. + //Also make sure to ignore one-way projection that goes the opposite way. + { + ConstraintGroupNode* neighborNode = neighbor->getConstraintGroup(); + PX_ASSERT(neighborNode); + + if (!neighborNode->readFlag(ConstraintGroupNode::eDISCOVERED)) + { + *nodeQueue = neighborNode; + + neighborNode->initProjectionData(&node, c); + neighborNode->raiseFlag(ConstraintGroupNode::eDISCOVERED); //flag body nodes that we process so we can detect loops + + nodeQueueFillCount++; + nodeQueue++; + } + } + } + } + } + + return nodeQueueFillCount; +} + + +void Sc::ConstraintProjectionTree::purgeProjectionTrees(ConstraintGroupNode& root) +{ + PX_ASSERT(&root == root.parent); + PX_ASSERT(root.hasProjectionTreeRoot()); + + // CA: New code (non recursive: recursive calls can cause stack overflow with huge trees) + ConstraintGroupNode* projRoot = root.projectionFirstRoot; + do + { + ConstraintGroupNode* currentNode = projRoot; + projRoot = projRoot->projectionNextRoot; // need to do it here because the info might get cleared below + + do + { + // Go down the tree until we find a leaf + if (currentNode->projectionFirstChild) + { + currentNode = currentNode->projectionFirstChild; + continue; + } + + // Delete current node and go to next sibling or parent + ConstraintGroupNode* nodeToDelete = currentNode; + ConstraintGroupNode* parent = currentNode->projectionParent; + currentNode = currentNode->projectionNextSibling; + + // Mark parent as leaf + if (nodeToDelete->projectionParent) + nodeToDelete->projectionParent->projectionFirstChild = NULL; + + // Clear projection info + nodeToDelete->clearProjectionData(); + + if (currentNode != NULL) + continue; + + // No more siblings jump back to parent + currentNode = parent; + + } while (currentNode != NULL); + + } while (projRoot != NULL); + + root.projectionFirstRoot = NULL; // it can happen that the constraint graph root is not part of a projection tree (if it is a kinematic, for example) but it still points to the + // first projection tree root and that needs to get cleaned up as well. + PX_ASSERT(!root.projectionNextRoot); + PX_ASSERT(!root.projectionParent); + PX_ASSERT(!root.projectionFirstChild); + PX_ASSERT(!root.projectionNextSibling); + PX_ASSERT(!root.projectionConstraint); +} + + +void Sc::ConstraintProjectionTree::projectPoseForTree(ConstraintGroupNode& node, Ps::Array& projectedBodies) +{ + // create a dummy node to keep the loops compact while covering the special case of the first node + PX_ASSERT(node.body); + ConstraintGroupNode dummyNode(*node.body); + dummyNode.projectionNextSibling = &node; + ConstraintGroupNode* currentNode = &dummyNode; + + // non recursive: recursive calls can cause stack overflow with huge trees + do + { + ConstraintGroupNode* nextSiblingNode = currentNode->projectionNextSibling; + + while (nextSiblingNode) + { + currentNode = nextSiblingNode; + ConstraintGroupNode* nextChildNode = currentNode; + + do + { + currentNode = nextChildNode; + + //----------------------------------------------------------------------------- + ConstraintSim* c = currentNode->projectionConstraint; + + if (c && c->hasDynamicBody() && c->needsProjection()) + { + c->projectPose(currentNode->body, projectedBodies); + } + //----------------------------------------------------------------------------- + + nextChildNode = currentNode->projectionFirstChild; + + } while (nextChildNode); + + nextSiblingNode = currentNode->projectionNextSibling; + } + + currentNode = currentNode->projectionParent; + + } while (currentNode != NULL); +} + + +void Sc::ConstraintProjectionTree::projectPose(ConstraintGroupNode& root, Ps::Array& projectedBodies) +{ + PX_ASSERT(&root == root.parent); + PX_ASSERT(root.hasProjectionTreeRoot()); + + ConstraintGroupNode* projRoot = root.projectionFirstRoot; + do + { + projectPoseForTree(*projRoot, projectedBodies); + projRoot = projRoot->projectionNextRoot; + + } while (projRoot != NULL); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h new file mode 100644 index 000000000..a13aaf5f8 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h @@ -0,0 +1,75 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_CONSTRAINT_PROJECTION_TREE +#define PX_PHYSICS_SCP_CONSTRAINT_PROJECTION_TREE + +#include "PsArray.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Sc +{ + struct ConstraintGroupNode; + class ConstraintSim; + class BodySim; + class BodyRank; + + class ConstraintProjectionTree + { + /** + This class serves both the static administration of an articulation and the actual articulation itself. + An Articulation object holds several articulation root nodes which make up a simulation island that + is further connected with lagrange joints. + */ + public: + ConstraintProjectionTree() {} + ~ConstraintProjectionTree() {} + + static void buildProjectionTrees(ConstraintGroupNode& root); + static void purgeProjectionTrees(ConstraintGroupNode& root); + + static void projectPose(ConstraintGroupNode& root, Ps::Array& projectedBodies); + + private: + static PxU32 projectionTreeBuildStep(ConstraintGroupNode& node, ConstraintSim* cToParent, ConstraintGroupNode** nodeStack); + + static void getConstraintStatus(const ConstraintSim& c, const BodySim* b, BodySim*& otherBody, PxU32& projectToBody, PxU32& projectToOtherBody); + static void rankConstraint(ConstraintSim&, BodyRank&, PxU32& dominanceTracking, PxU32& constraintsToProjectCount); + static void projectPoseForTree(ConstraintGroupNode& node, Ps::Array& projectedBodies); + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp new file mode 100644 index 000000000..79c255ca0 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp @@ -0,0 +1,490 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScScene.h" +#include "ScConstraintProjectionManager.h" +#include "ScBodySim.h" +#include "ScStaticSim.h" +#include "PxsContext.h" +#include "ScConstraintCore.h" +#include "ScConstraintSim.h" +#include "ScConstraintInteraction.h" +#include "ScElementSimInteraction.h" +#include "CmVisualization.h" +#include "ScObjectIDTracker.h" +#include "DyContext.h" + +using namespace physx; + +PX_FORCE_INLINE void invalidateConstraintGroupsOnAdd(Sc::ConstraintProjectionManager& cpm, Sc::BodySim* b0, Sc::BodySim* b1, Sc::ConstraintSim& constraint) +{ + // constraint groups get built by starting from dirty constraints that need projection. If a non-projecting constraint gets added + // we need to restart the whole process (we do not want to track dirty non-projecting constraints because of a scenario where + // all constraints of a group get switched to non-projecting which should kill the group and not rebuild a new one). + if (b0 && b0->getConstraintGroup()) + cpm.invalidateGroup(*b0->getConstraintGroup(), &constraint); + if (b1 && b1->getConstraintGroup()) + cpm.invalidateGroup(*b1->getConstraintGroup(), &constraint); +} + +Sc::ConstraintSim::ConstraintSim(ConstraintCore& core, RigidCore* r0, RigidCore* r1, Scene& scene) : + mScene (scene), + mCore (core), + mInteraction(NULL), + mFlags (0) +{ + mBodies[0] = (r0 && (r0->getActorCoreType() != PxActorType::eRIGID_STATIC)) ? static_cast(r0->getSim()) : 0; + mBodies[1] = (r1 && (r1->getActorCoreType() != PxActorType::eRIGID_STATIC)) ? static_cast(r1->getSim()) : 0; + + mLowLevelConstraint.index = scene.getConstraintIDTracker().createID(); + Ps::Array& writeBackPool = scene.getDynamicsContext()->getConstraintWriteBackPool(); + if (mLowLevelConstraint.index >= writeBackPool.capacity()) + { + writeBackPool.reserve(writeBackPool.capacity() * 2); + } + + writeBackPool.resize(PxMax(writeBackPool.size(), mLowLevelConstraint.index + 1)); + writeBackPool[mLowLevelConstraint.index].initialize(); + + if (!createLLConstraint()) + return; + + PxReal linBreakForce, angBreakForce; + core.getBreakForce(linBreakForce, angBreakForce); + if ((linBreakForce < PX_MAX_F32) || (angBreakForce < PX_MAX_F32)) + setFlag(eBREAKABLE); + + core.setSim(this); + + ConstraintProjectionManager& cpm = scene.getProjectionManager(); + if (!needsProjection()) + invalidateConstraintGroupsOnAdd(cpm, mBodies[0], mBodies[1], *this); + else + cpm.addToPendingGroupUpdates(*this); + + ConstraintSim* cs = this; // to make the Wii U compiler happy + mInteraction = mScene.getConstraintInteractionPool()->construct(cs, + r0 ? *r0->getSim() : scene.getStaticAnchor(), + r1 ? *r1->getSim() : scene.getStaticAnchor()); + + PX_ASSERT(!mInteraction->isRegistered()); // constraint interactions must not register in the scene, there is a list of Sc::ConstraintSim instead +} + +Sc::ConstraintSim::~ConstraintSim() +{ + PX_ASSERT(mInteraction); // This is fine now, a body which gets removed from the scene removes all constraints automatically + PX_ASSERT(!mInteraction->isRegistered()); // constraint interactions must not register in the scene, there is a list of Sc::ConstraintSim instead + + if (readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)) + mScene.getProjectionManager().removeFromPendingGroupUpdates(*this); + + if (!isBroken()) + mInteraction->destroy(); + + mScene.getConstraintIDTracker().releaseID(mLowLevelConstraint.index); + mScene.getConstraintInteractionPool()->destroy(mInteraction); + + destroyLLConstraint(); + + mCore.setSim(NULL); +} + +bool Sc::ConstraintSim::createLLConstraint() +{ + Dy::Constraint& llc = mLowLevelConstraint; + ConstraintCore& core = getCore(); + PxU32 constantBlockSize = core.getConstantBlockSize(); + + void* constantBlock = mScene.allocateConstraintBlock(constantBlockSize); + if(!constantBlock) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Constraint: could not allocate low-level resources."); + return false; + } + + //Ensure the constant block isn't just random data because some functions may attempt to use it before it is + //setup. Specifically pvd visualization of joints + //-CN + + PxMemZero( constantBlock, constantBlockSize); + + core.getBreakForce(llc.linBreakForce, llc.angBreakForce); + llc.flags = core.getFlags(); + llc.constantBlockSize = PxU16(constantBlockSize); + + llc.solverPrep = core.getSolverPrep(); + llc.project = core.getProject(); + llc.constantBlock = constantBlock; + + //llc.index = mLowLevelConstraint.index; + llc.body0 = mBodies[0] ? &mBodies[0]->getLowLevelBody() : 0; + llc.body1 = mBodies[1] ? &mBodies[1]->getLowLevelBody() : 0; + llc.bodyCore0 = mBodies[0] ? &llc.body0->getCore() : NULL; + llc.bodyCore1 = mBodies[1] ? &llc.body1->getCore() : NULL; + + llc.minResponseThreshold = core.getMinResponseThreshold(); + + return true; +} + +void Sc::ConstraintSim::destroyLLConstraint() +{ + if(mLowLevelConstraint.constantBlock) + { + mScene.deallocateConstraintBlock(mLowLevelConstraint.constantBlock, + mLowLevelConstraint.constantBlockSize); + } +} + +void Sc::ConstraintSim::preBodiesChange() +{ + PX_ASSERT(mInteraction); + + BodySim* b = getConstraintGroupBody(); + if (b) + mScene.getProjectionManager().invalidateGroup(*b->getConstraintGroup(), this); + + if (!isBroken()) + mInteraction->destroy(); + + mScene.getConstraintInteractionPool()->destroy(mInteraction); + mInteraction = NULL; +} + +void Sc::ConstraintSim::postBodiesChange(RigidCore* r0, RigidCore* r1) +{ + PX_ASSERT(mInteraction == NULL); + + BodySim* b0 = (r0 && (r0->getActorCoreType() != PxActorType::eRIGID_STATIC)) ? static_cast(r0->getSim()) : 0; + BodySim* b1 = (r1 && (r1->getActorCoreType() != PxActorType::eRIGID_STATIC)) ? static_cast(r1->getSim()) : 0; + + ConstraintProjectionManager& cpm = mScene.getProjectionManager(); + PxConstraintFlags::InternalType projectionNeeded = getCore().getFlags() & PxConstraintFlag::ePROJECTION; // can not use "needsProjection()" because that takes into account whether the constraint is broken + if (!projectionNeeded) + invalidateConstraintGroupsOnAdd(cpm, b0, b1, *this); + else if (!readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)) + cpm.addToPendingGroupUpdates(*this); + + Dy::Constraint& c = mLowLevelConstraint; + + c.body0 = b0 ? &b0->getLowLevelBody() : NULL; + c.body1 = b1 ? &b1->getLowLevelBody() : NULL; + + c.bodyCore0 = c.body0 ? &c.body0->getCore() : NULL; + c.bodyCore1 = c.body1 ? &c.body1->getCore() : NULL; + + mBodies[0] = b0; + mBodies[1] = b1; + + ConstraintSim* cs = this; // to make the Wii U compiler happy + mInteraction = mScene.getConstraintInteractionPool()->construct(cs, + r0 ? *r0->getSim() : mScene.getStaticAnchor(), + r1 ? *r1->getSim() : mScene.getStaticAnchor()); +} + +void Sc::ConstraintSim::checkMaxForceExceeded() +{ + PX_ASSERT(readFlag(eCHECK_MAX_FORCE_EXCEEDED)); + + Dy::ConstraintWriteback& solverOutput = mScene.getDynamicsContext()->getConstraintWriteBackPool()[mLowLevelConstraint.index]; + if(solverOutput.broken) + { + setFlag(ConstraintSim::eBROKEN); + mScene.addBrokenConstraint(&mCore); + mCore.breakApart(); + mInteraction->destroy(); + + // update related SIPs + { + ActorSim& a0 = mInteraction->getActorSim0(); + ActorSim& a1 = mInteraction->getActorSim1(); + ActorSim& actor = (a0.getActorInteractionCount()< a1.getActorInteractionCount()) ? a0 : a1; + + actor.setActorsInteractionsDirty(InteractionDirtyFlag::eFILTER_STATE, NULL, InteractionFlag::eRB_ELEMENT); + // because broken constraints can re-enable contact response between the two bodies + } + + PX_ASSERT(!readFlag(eCHECK_MAX_FORCE_EXCEEDED)); + } +} + +void Sc::ConstraintSim::getForce(PxVec3& lin, PxVec3& ang) +{ + const PxReal recipDt = mScene.getOneOverDt(); + Dy::ConstraintWriteback& solverOutput= mScene.getDynamicsContext()->getConstraintWriteBackPool()[mLowLevelConstraint.index]; + lin = solverOutput.linearImpulse * recipDt; + ang = solverOutput.angularImpulse * recipDt; +} + +void Sc::ConstraintSim::setBreakForceLL(PxReal linear, PxReal angular) +{ + PxU8 wasBreakable = readFlag(eBREAKABLE); + PxU8 isBreakable; + if ((linear < PX_MAX_F32) || (angular < PX_MAX_F32)) + isBreakable = eBREAKABLE; + else + isBreakable = 0; + + if (isBreakable != wasBreakable) + { + if (isBreakable) + { + PX_ASSERT(!readFlag(eCHECK_MAX_FORCE_EXCEEDED)); + setFlag(eBREAKABLE); + if (mInteraction->readInteractionFlag(InteractionFlag::eIS_ACTIVE)) + mScene.addActiveBreakableConstraint(this, mInteraction); + } + else + { + if (readFlag(eCHECK_MAX_FORCE_EXCEEDED)) + mScene.removeActiveBreakableConstraint(this); + clearFlag(eBREAKABLE); + } + } + + mLowLevelConstraint.linBreakForce = linear; + mLowLevelConstraint.angBreakForce = angular; +} + +void Sc::ConstraintSim::postFlagChange(PxConstraintFlags oldFlags, PxConstraintFlags newFlags) +{ + mLowLevelConstraint.flags = newFlags; + + // PT: don't convert to bool if not needed + const PxU32 hadProjection = (oldFlags & PxConstraintFlag::ePROJECTION); + const PxU32 needsProjection = (newFlags & PxConstraintFlag::ePROJECTION); + + if(needsProjection && !hadProjection) + { + PX_ASSERT(!readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)); // Non-projecting constrainst should not be part of the update list + + Sc::BodySim* b0 = getBody(0); + Sc::BodySim* b1 = getBody(1); + if ((!b0 || b0->getConstraintGroup()) && (!b1 || b1->getConstraintGroup())) + { + // Already part of a constraint group but not as a projection constraint -> re-generate projection tree + PX_ASSERT(b0 != NULL || b1 != NULL); + if (b0) + b0->getConstraintGroup()->markForProjectionTreeRebuild(mScene.getProjectionManager()); + else + b1->getConstraintGroup()->markForProjectionTreeRebuild(mScene.getProjectionManager()); + } + else + { + // Not part of a constraint group yet + mScene.getProjectionManager().addToPendingGroupUpdates(*this); + } + } + else if(!needsProjection && hadProjection) + { + if (!readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)) + { + Sc::BodySim* b = getConstraintGroupBody(); + if (b) + { + PX_ASSERT(b->getConstraintGroup()); + mScene.getProjectionManager().invalidateGroup(*b->getConstraintGroup(), NULL); + } + // This is conservative but it could be the case that this constraint with projection was the only + // one in the group and thus the whole group must be killed. If we had a counter for the number of + // projecting constraints per group, we could just update the projection tree if the counter was + // larger than 1. But switching the projection flag does not seem likely anyway. + } + else + mScene.getProjectionManager().removeFromPendingGroupUpdates(*this); // Was part of a group which got invalidated + + PX_ASSERT(!readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)); // make sure the expected post-condition is met for all paths + } +} + +Sc::RigidSim& Sc::ConstraintSim::getRigid(PxU32 i) +{ + PX_ASSERT(mInteraction); + + if (i == 0) + return static_cast(mInteraction->getActorSim0()); + else + return static_cast(mInteraction->getActorSim1()); +} + +bool Sc::ConstraintSim::hasDynamicBody() +{ + return (mBodies[0] && (!mBodies[0]->isKinematic())) || (mBodies[1] && (!mBodies[1]->isKinematic())); +} + +static void constrainMotion(PxsRigidBody* body, PxTransform& targetPose) +{ + //Now constraint deltaPos and deltaRot + const PxU32 lockFlags = body->mCore->lockFlags; + + if (lockFlags) + { + const PxTransform& currBody2World = body->mCore->body2World; + + PxVec3 deltaPos = targetPose.p - currBody2World.p; + + PxQuat deltaQ = targetPose.q * currBody2World.q.getConjugate(); + + if (deltaQ.w < 0) //shortest angle. + deltaQ = -deltaQ; + + PxReal angle; + PxVec3 axis; + deltaQ.toRadiansAndUnitAxis(angle, axis); + PxVec3 deltaRot = axis * angle; + + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + deltaPos.x = 0.0f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + deltaPos.y = 0.0f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + deltaPos.z = 0.0f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + deltaRot.x = 0.0f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + deltaRot.y = 0.0f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + deltaRot.z = 0.0f; + + targetPose.p = currBody2World.p + deltaPos; + + PxReal w2 = deltaRot.magnitudeSquared(); + if (w2 != 0.0f) + { + PxReal w = PxSqrt(w2); + + const PxReal v = w * 0.5f; + PxReal s, q; + Ps::sincos(v, s, q); + s /= w; + + const PxVec3 pqr = deltaRot * s; + const PxQuat quatVel(pqr.x, pqr.y, pqr.z, 0); + PxQuat result = quatVel * currBody2World.q; + + result += currBody2World.q * q; + + targetPose.q = result.getNormalized(); + } + else + { + targetPose.q = currBody2World.q; + } + } +} + +void Sc::ConstraintSim::projectPose(BodySim* childBody, Ps::Array& projectedBodies) +{ +#if PX_DEBUG + // We expect bodies in low level constraints to have same order as high level counterpart + PxsRigidBody* b0 = mLowLevelConstraint.body0; + PxsRigidBody* b1 = mLowLevelConstraint.body1; + PX_ASSERT( (childBody == getBody(0) && &childBody->getLowLevelBody() == b0) || + (childBody == getBody(1) && &childBody->getLowLevelBody() == b1) ); +#endif + + Dy::Constraint& constraint = getLowLevelConstraint(); + bool projectToBody0 = childBody == getBody(1); + + PxsRigidBody* body0 = constraint.body0, + * body1 = constraint.body1; + + PxTransform body0ToWorld = body0 ? body0->getPose() : PxTransform(PxIdentity); + PxTransform body1ToWorld = body1 ? body1->getPose() : PxTransform(PxIdentity); + + (*constraint.project)(constraint.constantBlock, body0ToWorld, body1ToWorld, projectToBody0); + + if(projectToBody0) + { + PX_ASSERT(body1); + //Constrain new pose to valid world motion + constrainMotion(body1, body1ToWorld); + body1->setPose(body1ToWorld); + projectedBodies.pushBack(getBody(1)); + } + else + { + PX_ASSERT(body0); + //Constrain new pose to valid world motion + constrainMotion(body0, body0ToWorld); + body0->setPose(body0ToWorld); + projectedBodies.pushBack(getBody(0)); + } +} + +bool Sc::ConstraintSim::needsProjection() +{ + const Dy::ConstraintWriteback& solverOutput = mScene.getDynamicsContext()->getConstraintWriteBackPool()[mLowLevelConstraint.index]; + return (getCore().getFlags() & PxConstraintFlag::ePROJECTION ) && !solverOutput.broken; +} + +PX_INLINE Sc::BodySim* Sc::ConstraintSim::getConstraintGroupBody() +{ + BodySim* b = NULL; + if (mBodies[0] && mBodies[0]->getConstraintGroup()) + b = mBodies[0]; + else if (mBodies[1] && mBodies[1]->getConstraintGroup()) + b = mBodies[1]; + + return b; +} + +void Sc::ConstraintSim::visualize(PxRenderBuffer& output) +{ + if(!(getCore().getFlags() & PxConstraintFlag::eVISUALIZATION)) + return; + + PxsRigidBody* b0 = mLowLevelConstraint.body0; + PxsRigidBody* b1 = mLowLevelConstraint.body1; + + const PxTransform idt(PxIdentity); + const PxTransform& t0 = b0 ? b0->getPose() : idt; + const PxTransform& t1 = b1 ? b1->getPose() : idt; + + const PxReal frameScale = mScene.getVisualizationScale() * mScene.getVisualizationParameter(PxVisualizationParameter::eJOINT_LOCAL_FRAMES); + const PxReal limitScale = mScene.getVisualizationScale() * mScene.getVisualizationParameter(PxVisualizationParameter::eJOINT_LIMITS); + + Cm::RenderOutput renderOut(static_cast(output)); + Cm::ConstraintImmediateVisualizer viz(frameScale, limitScale, renderOut); + + PxU32 flags = 0; + if(frameScale!=0.0f) + flags |= PxConstraintVisualizationFlag::eLOCAL_FRAMES; + if(limitScale!=0.0f) + flags |= PxConstraintVisualizationFlag::eLIMITS; + + mCore.getVisualize()(viz, mLowLevelConstraint.constantBlock, t0, t1, flags); +} + +void Sc::ConstraintSim::setConstantsLL(void* addr) +{ + PxMemCopy(mLowLevelConstraint.constantBlock, addr, mLowLevelConstraint.constantBlockSize); + + getAnyBody()->getScene().getSimulationController()->updateJoint(mInteraction->getEdgeIndex(), &mLowLevelConstraint); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h new file mode 100644 index 000000000..30e3462cd --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h @@ -0,0 +1,156 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CONSTRAINT_SIM +#define PX_PHYSICS_CONSTRAINT_SIM + +#include "PxSimulationEventCallback.h" +#include "DyConstraint.h" + +namespace physx +{ +namespace Sc +{ + + class Scene; + class ConstraintInteraction; + class ConstraintCore; + class RigidCore; + class BodySim; + class RigidSim; + + class ConstraintSim : public Ps::UserAllocated + { + public: + enum Enum + { + ePENDING_GROUP_UPDATE = (1<<0), // For constraint projection an island of the bodies connected by constraints is generated. + // Schedule generation/update of the island this constraint is a part of. + eBREAKABLE = (1<<1), // The constraint can break + eCHECK_MAX_FORCE_EXCEEDED = (1<<2), // This constraint will get tested for breakage at the end of the sim step + eBROKEN = (1<<3) + }; + + ConstraintSim(ConstraintCore& core, + RigidCore* r0, + RigidCore* r1, + Scene& scene); + + ~ConstraintSim(); + + void preBodiesChange(); + void postBodiesChange(RigidCore* r0, RigidCore* r1); + + void checkMaxForceExceeded(); + + void setBreakForceLL(PxReal linear, PxReal angular); + PX_INLINE void setMinResponseThresholdLL(PxReal threshold); + PX_INLINE void setAngularConstraintLinearCoefficientLL(PxReal coefficient); + void setConstantsLL(void* addr); + PX_INLINE const void* getConstantsLL() const; + + void postFlagChange(PxConstraintFlags oldFlags, PxConstraintFlags newFlags); + + PX_FORCE_INLINE const Dy::Constraint& getLowLevelConstraint() const { return mLowLevelConstraint; } + PX_FORCE_INLINE Dy::Constraint& getLowLevelConstraint() { return mLowLevelConstraint; } + PX_FORCE_INLINE ConstraintCore& getCore() const { return mCore; } + PX_FORCE_INLINE BodySim* getBody(PxU32 i) const // for static actors or world attached constraints NULL is returned + { + return mBodies[i]; + } + + RigidSim& getRigid(PxU32 i); + + void getForce(PxVec3& force, PxVec3& torque); + + PX_FORCE_INLINE PxU8 readFlag(PxU8 flag) const { return PxU8(mFlags & flag); } + PX_FORCE_INLINE void setFlag(PxU8 flag) { mFlags |= flag; } + PX_FORCE_INLINE void clearFlag(PxU8 flag) { mFlags &= ~flag; } + PX_FORCE_INLINE PxU32 isBroken() const { return PxU32(mFlags) & ConstraintSim::eBROKEN; } + + + //------------------------------------ Projection trees ----------------------------------------- + private: + PX_INLINE BodySim* getConstraintGroupBody(); + + public: + bool hasDynamicBody(); + + void projectPose(BodySim* childBody, Ps::Array& projectedBodies); + PX_INLINE BodySim* getOtherBody(BodySim*); + PX_INLINE BodySim* getAnyBody(); + + bool needsProjection(); + //----------------------------------------------------------------------------------------------- + + void visualize(PxRenderBuffer &out); + private: + ConstraintSim& operator=(const ConstraintSim&); + bool createLLConstraint(); + void destroyLLConstraint(); + private: + Dy::Constraint mLowLevelConstraint; + Scene& mScene; + ConstraintCore& mCore; + ConstraintInteraction* mInteraction; + BodySim* mBodies[2]; + PxU8 mFlags; + }; +} // namespace Sc + + +PX_INLINE void Sc::ConstraintSim::setMinResponseThresholdLL(PxReal threshold) +{ + mLowLevelConstraint.minResponseThreshold = threshold; +} + +PX_INLINE const void* Sc::ConstraintSim::getConstantsLL() const +{ + return mLowLevelConstraint.constantBlock; +} + + +PX_INLINE Sc::BodySim* Sc::ConstraintSim::getOtherBody(BodySim* b) +{ + return (b == mBodies[0]) ? mBodies[1] : mBodies[0]; +} + + +PX_INLINE Sc::BodySim* Sc::ConstraintSim::getAnyBody() +{ + if (mBodies[0]) + return mBodies[0]; + else + return mBodies[1]; +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h b/src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h new file mode 100644 index 000000000..84e95bc82 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h @@ -0,0 +1,174 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_CONTACTREPORTBUFFER +#define PX_PHYSICS_SCP_CONTACTREPORTBUFFER + +#include "foundation/Px.h" +#include "common/PxProfileZone.h" + +namespace physx +{ + namespace Sc + { + class ContactReportBuffer + { + public: + PX_FORCE_INLINE ContactReportBuffer(PxU32 initialSize, bool noResizeAllowed) + : mBuffer(NULL) + ,mCurrentBufferIndex(0) + ,mCurrentBufferSize(initialSize) + ,mDefaultBufferSize(initialSize) + ,mLastBufferIndex(0) + ,mAllocationLocked(noResizeAllowed) + { + mBuffer = allocateBuffer(initialSize); + PX_ASSERT(mBuffer); + } + + ~ContactReportBuffer() + { + PX_FREE(mBuffer); + } + + PX_FORCE_INLINE void reset(); + PX_FORCE_INLINE void flush(); + + PX_FORCE_INLINE PxU8* allocateNotThreadSafe(PxU32 size, PxU32& index, PxU32 alignment= 16); + PX_FORCE_INLINE PxU8* reallocateNotThreadSafe(PxU32 size, PxU32& index, PxU32 alignment= 16, PxU32 lastIndex = 0xFFFFFFFF); + PX_FORCE_INLINE PxU8* getData(const PxU32& index) const { return mBuffer+index; } + + PX_FORCE_INLINE PxU32 getDefaultBufferSize() const {return mDefaultBufferSize;} + + private: + PX_FORCE_INLINE PxU8* allocateBuffer(PxU32 size); + + private: + PxU8* mBuffer; + PxU32 mCurrentBufferIndex; + PxU32 mCurrentBufferSize; + PxU32 mDefaultBufferSize; + PxU32 mLastBufferIndex; + bool mAllocationLocked; + }; + + } // namespace Sc + + ////////////////////////////////////////////////////////////////////////// + + PX_FORCE_INLINE void Sc::ContactReportBuffer::reset() + { + mCurrentBufferIndex = 0; + mLastBufferIndex = 0xFFFFFFFF; + } + + ////////////////////////////////////////////////////////////////////////// + + void Sc::ContactReportBuffer::flush() + { + mCurrentBufferIndex = 0; + mLastBufferIndex = 0xFFFFFFFF; + + if(mCurrentBufferSize != mDefaultBufferSize) + { + PX_FREE(mBuffer); + + mBuffer = allocateBuffer(mDefaultBufferSize); + PX_ASSERT(mBuffer); + + mCurrentBufferSize = mDefaultBufferSize; + } + } + + ////////////////////////////////////////////////////////////////////////// + + PxU8* Sc::ContactReportBuffer::allocateNotThreadSafe(PxU32 size, PxU32& index ,PxU32 alignment/* =16 */) + { + PX_ASSERT(shdfnd::isPowerOfTwo(alignment)); + + // padding for alignment + PxU32 pad = ((mCurrentBufferIndex+alignment-1)&~(alignment-1)) - mCurrentBufferIndex; + + index = mCurrentBufferIndex + pad; + + if (index + size > mCurrentBufferSize) + { + PX_PROFILE_ZONE("ContactReportBuffer::Resize", 0); + if(mAllocationLocked) + return NULL; + + PxU32 oldBufferSize = mCurrentBufferSize; + while(index + size > mCurrentBufferSize) + { + mCurrentBufferSize *= 2; + } + + PxU8* tempBuffer = allocateBuffer(mCurrentBufferSize); + + PxMemCopy(tempBuffer,mBuffer,oldBufferSize); + + PX_FREE(mBuffer); + + mBuffer = tempBuffer; + } + + + PxU8* ptr = mBuffer + index; + mLastBufferIndex = index; + PX_ASSERT((reinterpret_cast(ptr)&(alignment-1)) == 0); + mCurrentBufferIndex += size + pad; + return ptr; + } + + ////////////////////////////////////////////////////////////////////////// + + PxU8* Sc::ContactReportBuffer::reallocateNotThreadSafe(PxU32 size, PxU32& index ,PxU32 alignment/* =16 */, PxU32 lastIndex) + { + if(lastIndex != mLastBufferIndex) + { + return allocateNotThreadSafe(size,index,alignment); + } + else + { + mCurrentBufferIndex = mLastBufferIndex; + return allocateNotThreadSafe(size,index,alignment); + } + } + + ////////////////////////////////////////////////////////////////////////// + + PX_FORCE_INLINE PxU8* Sc::ContactReportBuffer::allocateBuffer(PxU32 size) + { + return (static_cast(PX_ALLOC(size, "ContactReportBuffer"))); + } + +} // namespace physx + +#endif // PX_PHYSICS_SCP_CONTACTREPORTBUFFER diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h b/src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h new file mode 100644 index 000000000..0aa386fa8 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h @@ -0,0 +1,413 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_CONTACTSTREAM +#define PX_PHYSICS_SCP_CONTACTSTREAM + +#include "foundation/Px.h" +#include "PxSimulationEventCallback.h" +#include "ScObjectIDTracker.h" +#include "ScRigidSim.h" +#include "ScStaticSim.h" +#include "ScBodySim.h" + +namespace physx +{ + class PxShape; + +namespace Sc +{ + class ActorPair; + + + // Internal counterpart of PxContactPair + struct ContactShapePair + { + public: + PxShape* shapes[2]; + const PxU8* contactPatches; + const PxU8* contactPoints; + const PxReal* contactForces; + PxU32 requiredBufferSize; + PxU8 contactCount; + PxU8 patchCount; + PxU16 constraintStreamSize; + PxU16 flags; + PxU16 events; + PxU32 shapeID[2]; + //26 (or 38 on 64bit) + }; + PX_COMPILE_TIME_ASSERT(sizeof(ContactShapePair) == sizeof(PxContactPair)); + + struct ContactStreamManagerFlag + { + enum Enum + { + /** + \brief Need to test stream for shapes that were removed from the actor/scene + + Usually this is the case when a shape gets removed from the scene, however, other operations that remove the + broadphase volume of a pair object have to be considered as well since the shape might get removed later after such an + operation. The scenarios to consider are: + + \li shape gets removed (this includes raising PxActorFlag::eDISABLE_SIMULATION) + \li shape switches to eSCENE_QUERY_SHAPE only + \li shape switches to eTRIGGER_SHAPE + \li resetFiltering() + \li actor gets removed from an aggregate + + */ + eTEST_FOR_REMOVED_SHAPES = (1<<0), + + /** + \brief Invalid stream memory not allocated + */ + eINVALID_STREAM = (1<<1), + + /** + \brief Incomplete stream will be reported + */ + eINCOMPLETE_STREAM = (1<<2), + + /** + \brief The stream contains extra data with PxContactPairVelocity items where the post solver velocity needs to get written to. + Only valid for discrete collision (in CCD the post response velocity is available immediately). + */ + eNEEDS_POST_SOLVER_VELOCITY = (1<<3), + + /** + \brief Contains pairs that lost touch + + This info is used as an optimization to only parse the stream and check for removed shapes if there is a potential for + having removed shapes in the stream that won't get detected in any other way. For example, there is the scenario where + during the simulation a pair loses AABB touch and gets deleted. At that point a lost touch event might get written to the + stream. If at fetchResults a buffered shape removal takes place, and that shape was part of the mentioned pair, there is + no way any longer to make the connection to the corresponding event stream (since the pair has been deleted during the sim). + */ + eHAS_PAIRS_THAT_LOST_TOUCH = (1<<4), + + /** + \brief Marker for the next available free flag + */ + eNEXT_FREE_FLAG = (1<<5) + }; + }; + + struct ContactStreamHeader + { + PxU16 contactPass; // marker for extra data to know when a new collison pass started (discrete collision -> CCD pass 1 -> CCD pass 2 -> ...) + PxU16 pad; // to keep the stream 4byte aligned + }; + + /** + \brief Contact report logic and data management. + + The internal contact report stream has the following format: + + ContactStreamHeader | PxContactPairIndex0 | (PxContactPairPose0, PxContactPairVelocity0) | ... | PxContactPairIndexN | (PxContactPairPoseN, PxContactPairVelocityN) | (unused memory up to maxExtraDataSize ) | + PxContactPair0 | ... | PxContactPairM | (unsued pairs up to maxPairCount) + */ + class ContactStreamManager + { + public: + PX_FORCE_INLINE ContactStreamManager() : maxPairCount(0), flags_and_maxExtraDataBlocks(0) {} + PX_FORCE_INLINE ~ContactStreamManager() {} + + PX_FORCE_INLINE void reset(); + + PX_FORCE_INLINE PxU16 getFlags() const; + PX_FORCE_INLINE void raiseFlags(PxU16 flags); + PX_FORCE_INLINE void clearFlags(PxU16 flags); + + PX_FORCE_INLINE PxU32 getMaxExtraDataSize() const; + PX_FORCE_INLINE void setMaxExtraDataSize(PxU32 size); // size in bytes (will translate into blocks internally) + + PX_FORCE_INLINE Sc::ContactShapePair* getShapePairs(PxU8* contactReportPairData) const; + + PX_FORCE_INLINE static void convertDeletedShapesInContactStream(ContactShapePair*, PxU32 pairCount, const ObjectIDTracker&); + + PX_FORCE_INLINE static PxU32 computeExtraDataBlockCount(PxU32 extraDataSize); + PX_FORCE_INLINE static PxU32 computeExtraDataBlockSize(PxU32 extraDataSize); + PX_FORCE_INLINE static PxU16 computeContactReportExtraDataSize(PxU32 extraDataFlags, bool addHeader); + PX_FORCE_INLINE static void fillInContactReportExtraData(PxContactPairVelocity*, PxU32 index, const RigidSim&, bool isCCDPass); + PX_FORCE_INLINE static void fillInContactReportExtraData(PxContactPairPose*, PxU32 index, const RigidSim&, bool isCCDPass, const bool useCurrentTransform); + PX_FORCE_INLINE void fillInContactReportExtraData(PxU8* stream, PxU32 extraDataFlags, const RigidSim&, const RigidSim&, PxU32 ccdPass, const bool useCurrentTransform, PxU32 pairIndex, PxU32 sizeOffset); + PX_FORCE_INLINE void setContactReportPostSolverVelocity(PxU8* stream, const RigidSim&, const RigidSim&); + + PxU32 bufferIndex; // marks the start of the shape pair stream of the actor pair (byte offset with respect to global contact buffer stream) + PxU16 maxPairCount; // used to reserve the same amount of memory as in the last frame (as an initial guess) + PxU16 currentPairCount; // number of shape pairs stored in the buffer + PxU16 extraDataSize; // size of the extra data section in the stream + private: + PxU16 flags_and_maxExtraDataBlocks; // used to reserve the same amount of memory as in the last frame (as an initial guess) + + public: + static const PxU32 sExtraDataBlockSizePow2 = 4; // extra data gets allocated as a multiple of 2^sExtraDataBlockSizePow2 to keep memory low of this struct. + static const PxU32 sFlagMask = (ContactStreamManagerFlag::eNEXT_FREE_FLAG - 1); + static const PxU32 sMaxExtraDataShift = 5; // shift necessary to extract the maximum number of blocks allocated for extra data + + PX_COMPILE_TIME_ASSERT(ContactStreamManagerFlag::eNEXT_FREE_FLAG == (1 << sMaxExtraDataShift)); + }; + +} // namespace Sc + + +PX_FORCE_INLINE void Sc::ContactStreamManager::reset() +{ + currentPairCount = 0; + extraDataSize = 0; + flags_and_maxExtraDataBlocks &= ~sFlagMask; +} + + +PX_FORCE_INLINE PxU16 Sc::ContactStreamManager::getFlags() const +{ + return (flags_and_maxExtraDataBlocks & sFlagMask); +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::raiseFlags(PxU16 flags) +{ + PX_ASSERT(flags < ContactStreamManagerFlag::eNEXT_FREE_FLAG); + + flags_and_maxExtraDataBlocks |= flags; +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::clearFlags(PxU16 flags) +{ + PX_ASSERT(flags < ContactStreamManagerFlag::eNEXT_FREE_FLAG); + + PxU16 tmpFlags = getFlags(); + tmpFlags &= ~flags; + flags_and_maxExtraDataBlocks &= ~sFlagMask; + raiseFlags(tmpFlags); +} + + +PX_FORCE_INLINE PxU32 Sc::ContactStreamManager::getMaxExtraDataSize() const +{ + return PxU32((flags_and_maxExtraDataBlocks >> sMaxExtraDataShift) << sExtraDataBlockSizePow2); +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::setMaxExtraDataSize(PxU32 size) +{ + PxU32 nbBlocks = computeExtraDataBlockCount(size); + flags_and_maxExtraDataBlocks = Ps::to16((flags_and_maxExtraDataBlocks & sFlagMask) | (nbBlocks << sMaxExtraDataShift)); +} + + +PX_FORCE_INLINE Sc::ContactShapePair* Sc::ContactStreamManager::getShapePairs(PxU8* contactReportPairData) const +{ + return reinterpret_cast(contactReportPairData + getMaxExtraDataSize()); +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::convertDeletedShapesInContactStream(ContactShapePair* shapePairs, PxU32 pairCount, const ObjectIDTracker& tracker) +{ + for(PxU32 i=0; i < pairCount; i++) + { + ContactShapePair& csp = shapePairs[i]; + + PxU32 shape0ID = csp.shapeID[0]; + PxU32 shape1ID = csp.shapeID[1]; + + PxU16 flags = csp.flags; + PX_COMPILE_TIME_ASSERT(sizeof(flags) == sizeof((reinterpret_cast(0))->flags)); + + if (tracker.isDeletedID(shape0ID)) + flags |= PxContactPairFlag::eREMOVED_SHAPE_0; + if (tracker.isDeletedID(shape1ID)) + flags |= PxContactPairFlag::eREMOVED_SHAPE_1; + + csp.flags = flags; + } +} + + +PX_FORCE_INLINE PxU32 Sc::ContactStreamManager::computeExtraDataBlockCount(PxU32 extraDataSize_) +{ + PxU32 nbBlocks; + if (extraDataSize_ & ((1 << sExtraDataBlockSizePow2) - 1)) // not a multiple of block size -> need one block more + nbBlocks = (extraDataSize_ >> sExtraDataBlockSizePow2) + 1; + else + nbBlocks = (extraDataSize_ >> sExtraDataBlockSizePow2); + + return nbBlocks; +} + + +PX_FORCE_INLINE PxU32 Sc::ContactStreamManager::computeExtraDataBlockSize(PxU32 extraDataSize_) +{ + return (computeExtraDataBlockCount(extraDataSize_) << sExtraDataBlockSizePow2); +} + + +PX_FORCE_INLINE PxU16 Sc::ContactStreamManager::computeContactReportExtraDataSize(PxU32 extraDataFlags, bool addHeader) +{ + PX_ASSERT(extraDataFlags); + + PxU16 extraDataSize_ = sizeof(PxContactPairIndex); + if (extraDataFlags & PxPairFlag::ePRE_SOLVER_VELOCITY) + extraDataSize_ += sizeof(PxContactPairVelocity); + if (extraDataFlags & PxPairFlag::ePOST_SOLVER_VELOCITY) + extraDataSize_ += sizeof(PxContactPairVelocity); + if (extraDataFlags & PxPairFlag::eCONTACT_EVENT_POSE) + extraDataSize_ += sizeof(PxContactPairPose); + if (addHeader) + extraDataSize_ += sizeof(ContactStreamHeader); + return extraDataSize_; +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::fillInContactReportExtraData(PxContactPairVelocity* cpVel, PxU32 index, const RigidSim& rs, bool isCCDPass) +{ + if (rs.getActorType() != PxActorType::eRIGID_STATIC) + { + const BodySim& bs = static_cast(rs); + if ((!isCCDPass) || (cpVel->type == PxContactPairExtraDataType::ePOST_SOLVER_VELOCITY)) + { + const BodyCore& bc = bs.getBodyCore(); + cpVel->linearVelocity[index] = bc.getLinearVelocity(); + cpVel->angularVelocity[index] = bc.getAngularVelocity(); + } + else + { + PX_ASSERT(cpVel->type == PxContactPairExtraDataType::ePRE_SOLVER_VELOCITY); + const Cm::SpatialVector& vel = bs.getLowLevelBody().getPreSolverVelocities(); + cpVel->linearVelocity[index] = vel.linear; + cpVel->angularVelocity[index] = vel.angular; + } + } + else + { + cpVel->linearVelocity[index] = PxVec3(0.0f); + cpVel->angularVelocity[index] = PxVec3(0.0f); + } +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::fillInContactReportExtraData(PxContactPairPose* cpPose, PxU32 index, const RigidSim& rs, bool isCCDPass, const bool useCurrentTransform) +{ + if(rs.getActorType() != PxActorType::eRIGID_STATIC) + { + const BodySim& bs = static_cast(rs); + const BodyCore& bc = bs.getBodyCore(); + const PxTransform& src = (!isCCDPass && useCurrentTransform) ? bc.getBody2World() : bs.getLowLevelBody().getLastCCDTransform(); + cpPose->globalPose[index] = src * bc.getBody2Actor().getInverse(); + } + else + { + const StaticSim& ss = static_cast(rs); + const StaticCore& sc = ss.getStaticCore(); + cpPose->globalPose[index] = sc.getActor2World(); + } +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::fillInContactReportExtraData(PxU8* stream, PxU32 extraDataFlags, const RigidSim& rs0, const RigidSim& rs1, PxU32 ccdPass, const bool useCurrentTransform, + PxU32 pairIndex, PxU32 sizeOffset) +{ + ContactStreamHeader* strHeader = reinterpret_cast(stream); + strHeader->contactPass = Ps::to16(ccdPass); + + stream += sizeOffset; + PxU8* edStream = stream; + bool isCCDPass = (ccdPass != 0); + + { + PxContactPairIndex* cpIndex = reinterpret_cast(edStream); + cpIndex->type = PxContactPairExtraDataType::eCONTACT_PAIR_INDEX; + cpIndex->index = Ps::to16(pairIndex); + edStream += sizeof(PxContactPairIndex); + + PX_ASSERT(edStream <= reinterpret_cast(getShapePairs(stream))); + } + + // Important: make sure this one is the first after the PxContactPairIndex item for discrete contacts as it needs to get filled in before the reports get sent + // (post solver velocity is not available when it gets created) + if (extraDataFlags & PxPairFlag::ePOST_SOLVER_VELOCITY) + { + PxContactPairVelocity* cpVel = reinterpret_cast(edStream); + cpVel->type = PxContactPairExtraDataType::ePOST_SOLVER_VELOCITY; + edStream += sizeof(PxContactPairVelocity); + + if (!isCCDPass) + raiseFlags(ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY); // don't know the post solver velocity yet + else + { + ContactStreamManager::fillInContactReportExtraData(cpVel, 0, rs0, true); + ContactStreamManager::fillInContactReportExtraData(cpVel, 1, rs1, true); + } + + PX_ASSERT(edStream <= reinterpret_cast(getShapePairs(stream))); + } + if (extraDataFlags & PxPairFlag::ePRE_SOLVER_VELOCITY) + { + PxContactPairVelocity* cpVel = reinterpret_cast(edStream); + cpVel->type = PxContactPairExtraDataType::ePRE_SOLVER_VELOCITY; + ContactStreamManager::fillInContactReportExtraData(cpVel, 0, rs0, isCCDPass); + ContactStreamManager::fillInContactReportExtraData(cpVel, 1, rs1, isCCDPass); + edStream += sizeof(PxContactPairVelocity); + + PX_ASSERT(edStream <= reinterpret_cast(getShapePairs(stream))); + } + if (extraDataFlags & PxPairFlag::eCONTACT_EVENT_POSE) + { + PxContactPairPose* cpPose = reinterpret_cast(edStream); + cpPose->type = PxContactPairExtraDataType::eCONTACT_EVENT_POSE; + ContactStreamManager::fillInContactReportExtraData(cpPose, 0, rs0, isCCDPass, useCurrentTransform); + ContactStreamManager::fillInContactReportExtraData(cpPose, 1, rs1, isCCDPass, useCurrentTransform); + edStream += sizeof(PxContactPairPose); + + PX_ASSERT(edStream <= reinterpret_cast(getShapePairs(stream))); + } + + extraDataSize = Ps::to16(sizeOffset + PxU32(edStream - stream)); +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::setContactReportPostSolverVelocity(PxU8* stream, const RigidSim& rs0, const RigidSim& rs1) +{ + PX_ASSERT(extraDataSize > (sizeof(ContactStreamHeader) + sizeof(PxContactPairIndex))); + PxContactPairVelocity* cpVel = reinterpret_cast(stream + sizeof(ContactStreamHeader) + sizeof(PxContactPairIndex)); + PX_ASSERT(cpVel->type == PxContactPairExtraDataType::ePOST_SOLVER_VELOCITY); + + fillInContactReportExtraData(cpVel, 0, rs0, false); + fillInContactReportExtraData(cpVel, 1, rs1, false); + + clearFlags(ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY); +} + + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp new file mode 100644 index 000000000..7bf63fd1e --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp @@ -0,0 +1,45 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScElementInteractionMarker.h" +#include "ScNPhaseCore.h" + +using namespace physx; + +Sc::ElementInteractionMarker::~ElementInteractionMarker() +{ + if(isRegistered()) + { + Scene& scene = getScene(); + scene.unregisterInteraction(this); + scene.getNPhaseCore()->unregisterInteraction(this); + } + unregisterFromActors(); +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h new file mode 100644 index 000000000..5af3eba15 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_COLLISION_ELEMENT_INTERACTION_MARKER +#define PX_COLLISION_ELEMENT_INTERACTION_MARKER + +#include "ScElementSimInteraction.h" +#include "ScNPhaseCore.h" + +namespace physx +{ +namespace Sc +{ + class ElementInteractionMarker : public ElementSimInteraction + { + public: + PX_INLINE ElementInteractionMarker(ElementSim& element0, ElementSim& element1, bool createParallel/* = false*/); + ~ElementInteractionMarker(); + + bool onActivate_(void*) { return false; } + bool onDeactivate_() { return true; } + }; + +} // namespace Sc + + +PX_INLINE Sc::ElementInteractionMarker::ElementInteractionMarker(ElementSim& element0, ElementSim& element1, bool createParallel) : + ElementSimInteraction(element0, element1, InteractionType::eMARKER, InteractionFlag::eRB_ELEMENT|InteractionFlag::eFILTERABLE) +{ + if(!createParallel) + { + bool active = registerInActors(); + PX_UNUSED(active); + PX_ASSERT(!active); + getScene().registerInteraction(this, false); + getScene().getNPhaseCore()->registerInteraction(this); + } +} + +} + +#endif //PX_COLLISION_SHAPEINTERACTIONMARKER diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp new file mode 100644 index 000000000..717295989 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp @@ -0,0 +1,133 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsFoundation.h" +#include "PxsContext.h" +#include "ScElementSim.h" +#include "ScElementSimInteraction.h" +#include "ScSqBoundsManager.h" +#include "ScSimStats.h" + +using namespace physx; +using namespace Sc; + +static PX_FORCE_INLINE bool interactionHasElement(const Interaction* it, const ElementSim* elem) +{ + if(it->readInteractionFlag(InteractionFlag::eRB_ELEMENT)) + { + PX_ASSERT( (it->getType() == InteractionType::eMARKER) || + (it->getType() == InteractionType::eOVERLAP) || + (it->getType() == InteractionType::eTRIGGER) ); + + const ElementSimInteraction* ei = static_cast(it); + if((&ei->getElement0() == elem) || (&ei->getElement1() == elem)) + return true; + } + return false; +} + +Sc::ElementSimInteraction* Sc::ElementSim::ElementInteractionIterator::getNext() +{ + while(mInteractions!=mInteractionsLast) + { + Interaction* it = *mInteractions++; + if(interactionHasElement(it, mElement)) + return static_cast(it); + } + return NULL; +} + +Sc::ElementSimInteraction* Sc::ElementSim::ElementInteractionReverseIterator::getNext() +{ + while(mInteractions!=mInteractionsLast) + { + Interaction* it = *--mInteractionsLast; + if(interactionHasElement(it, mElement)) + return static_cast(it); + } + return NULL; +} + +Sc::ElementSim::ElementSim(ActorSim& actor) : + mNextInActor (NULL), + mActor (actor), + mInBroadPhase (false) +{ + initID(); + + actor.onElementAttach(*this); +} + +Sc::ElementSim::~ElementSim() +{ + PX_ASSERT(!mInBroadPhase); + releaseID(); + mActor.onElementDetach(*this); +} + +void Sc::ElementSim::setElementInteractionsDirty(InteractionDirtyFlag::Enum flag, PxU8 interactionFlag) +{ + ElementSim::ElementInteractionIterator iter = getElemInteractions(); + ElementSimInteraction* interaction = iter.getNext(); + while(interaction) + { + if(interaction->readInteractionFlag(interactionFlag)) + interaction->setDirty(flag); + + interaction = iter.getNext(); + } +} + +void Sc::ElementSim::addToAABBMgr(PxReal contactDistance, Bp::FilterGroup::Enum group, Ps::IntBool isTrigger) +{ + Sc::Scene& scene = getScene(); + if(!scene.getAABBManager()->addBounds(mElementID, contactDistance, group, this, mActor.getActorCore().getAggregateID(), isTrigger ? Bp::ElementType::eTRIGGER : Bp::ElementType::eSHAPE)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Unable to create broadphase entity because only 32768 shapes are supported"); + return; + } + mInBroadPhase = true; +#if PX_ENABLE_SIM_STATS + scene.getStatsInternal().incBroadphaseAdds(); +#endif +} + +void Sc::ElementSim::removeFromAABBMgr() +{ + PX_ASSERT(mInBroadPhase); + Sc::Scene& scene = getScene(); + scene.getAABBManager()->removeBounds(mElementID); + scene.getAABBManager()->getChangedAABBMgActorHandleMap().growAndReset(mElementID); + + mInBroadPhase = false; +#if PX_ENABLE_SIM_STATS + scene.getStatsInternal().incBroadphaseRemoves(); +#endif +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h new file mode 100644 index 000000000..61cf51868 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_ELEMENT_SIM +#define PX_PHYSICS_SCP_ELEMENT_SIM + +#include "PsUserAllocated.h" +#include "PxFiltering.h" +#include "PxvConfig.h" +#include "ScActorSim.h" +#include "ScInteraction.h" +#include "BpAABBManager.h" +#include "ScObjectIDTracker.h" + +namespace physx +{ +namespace Sc +{ + class ElementSimInteraction; + + /* + A ElementSim is a part of a ActorSim. It contributes to the activation framework by adding its + interactions to the actor. */ + class ElementSim : public Ps::UserAllocated + { + PX_NOCOPY(ElementSim) + + public: + class ElementInteractionIterator + { + public: + PX_FORCE_INLINE ElementInteractionIterator(const ElementSim& e, PxU32 nbInteractions, Interaction** interactions) : + mInteractions(interactions), mInteractionsLast(interactions + nbInteractions), mElement(&e) {} + ElementSimInteraction* getNext(); + + private: + Interaction** mInteractions; + Interaction** mInteractionsLast; + const ElementSim* mElement; + }; + + class ElementInteractionReverseIterator + { + public: + PX_FORCE_INLINE ElementInteractionReverseIterator(const ElementSim& e, PxU32 nbInteractions, Interaction** interactions) : + mInteractions(interactions), mInteractionsLast(interactions + nbInteractions), mElement(&e) {} + ElementSimInteraction* getNext(); + + private: + Interaction** mInteractions; + Interaction** mInteractionsLast; + const ElementSim* mElement; + }; + + ElementSim(ActorSim& actor); + protected: + ~ElementSim(); + public: + + // Get an iterator to the interactions connected to the element + PX_FORCE_INLINE ElementInteractionIterator getElemInteractions() const { return ElementInteractionIterator(*this, mActor.getActorInteractionCount(), mActor.getActorInteractions()); } + PX_FORCE_INLINE ElementInteractionReverseIterator getElemInteractionsReverse() const { return ElementInteractionReverseIterator(*this, mActor.getActorInteractionCount(), mActor.getActorInteractions()); } + + PX_FORCE_INLINE ActorSim& getActor() const { return mActor; } + + PX_FORCE_INLINE Scene& getScene() const { return mActor.getScene(); } + + PX_FORCE_INLINE PxU32 getElementID() const { return mElementID; } + PX_FORCE_INLINE bool isInBroadPhase() const { return mInBroadPhase; } + + void addToAABBMgr(PxReal contactDistance, Bp::FilterGroup::Enum group, Ps::IntBool isTrigger); + void removeFromAABBMgr(); + + void setElementInteractionsDirty(InteractionDirtyFlag::Enum flag, PxU8 interactionFlag); + + PX_FORCE_INLINE void initID() + { + Scene& scene = getScene(); + mElementID = scene.getElementIDPool().createID(); + scene.getBoundsArray().initEntry(mElementID); + } + + PX_FORCE_INLINE void releaseID() + { + getScene().getElementIDPool().releaseID(mElementID); + } + public: + ElementSim* mNextInActor; + private: + ActorSim& mActor; + + PxU32 mElementID : 31; + PxU32 mInBroadPhase : 1; + }; + + PX_FORCE_INLINE void setFilterObjectAttributeType(PxFilterObjectAttributes& attr, PxFilterObjectType::Enum type) + { + PX_ASSERT((attr & (PxFilterObjectType::eMAX_TYPE_COUNT-1)) == 0); + attr |= type; + } +} // namespace Sc +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h new file mode 100644 index 000000000..ccb52bbe0 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_ELEMENT_SIM_INTERACTION +#define PX_PHYSICS_SCP_ELEMENT_SIM_INTERACTION + +#include "ScInteraction.h" +#include "ScElementSim.h" + +namespace physx +{ +namespace Sc +{ + class ElementSimInteraction : public Interaction + { + public: + PX_FORCE_INLINE ElementSim& getElement0() const { return mElement0; } + PX_FORCE_INLINE ElementSim& getElement1() const { return mElement1; } + + PX_FORCE_INLINE void setFilterPairIndex(PxU32 filterPairIndex) { mFilterPairIndex = filterPairIndex; } + PX_FORCE_INLINE PxU32 getFilterPairIndex() const { return mFilterPairIndex; } + + protected: + PX_INLINE ElementSimInteraction(ElementSim& element0, ElementSim& element1, InteractionType::Enum type, PxU8 flags); + virtual ~ElementSimInteraction() {} + + ElementSimInteraction& operator=(const ElementSimInteraction&); + + private: + ElementSim& mElement0; + ElementSim& mElement1; + PxU32 mFilterPairIndex; + }; + +} // namespace Sc + +////////////////////////////////////////////////////////////////////////// + +PX_INLINE Sc::ElementSimInteraction::ElementSimInteraction(ElementSim& element0, ElementSim& element1, InteractionType::Enum type, PxU8 flags) : + Interaction (element0.getActor(), element1.getActor(), type, flags), + mElement0 (element0), + mElement1 (element1), + mFilterPairIndex(INVALID_FILTER_PAIR_INDEX) +{ +} + + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp new file mode 100644 index 000000000..360db5796 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp @@ -0,0 +1,71 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/Px.h" + +#include "ScInteraction.h" +#include "ScNPhaseCore.h" + +using namespace physx; + +Sc::Interaction::Interaction(ActorSim& actor0, ActorSim& actor1, InteractionType::Enum type, PxU8 flags) : + mActor0 (actor0), + mActor1 (actor1), + mSceneId (PX_INVALID_INTERACTION_SCENE_ID), + mActorId0 (PX_INVALID_INTERACTION_ACTOR_ID), + mActorId1 (PX_INVALID_INTERACTION_ACTOR_ID), + mInteractionType (Ps::to8(type)), + mInteractionFlags (flags), + mDirtyFlags (0) +{ + PX_ASSERT_WITH_MESSAGE(&actor0.getScene() == &actor1.getScene(),"Cannot create an interaction between actors belonging to different scenes."); + PX_ASSERT(PxU32(type)<256); // PT: type is now stored on a byte +} + +void Sc::Interaction::addToDirtyList() +{ + getActorSim0().getScene().getNPhaseCore()->addToDirtyInteractionList(this); +} + +void Sc::Interaction::removeFromDirtyList() +{ + getActorSim0().getScene().getNPhaseCore()->removeFromDirtyInteractionList(this); +} + +void Sc::Interaction::setClean(bool removeFromList) +{ + if (readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)) + { + if (removeFromList) // if we process all dirty interactions anyway, then we can just clear the list at the end and save the work here. + removeFromDirtyList(); + clearInteractionFlag(InteractionFlag::eIN_DIRTY_LIST); + } + + mDirtyFlags = 0; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h new file mode 100644 index 000000000..effdddd5b --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h @@ -0,0 +1,210 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_INTERACTION +#define PX_PHYSICS_SCP_INTERACTION + +#include "foundation/Px.h" +#include "ScInteractionFlags.h" +#include "ScScene.h" +#include "ScActorSim.h" +#include "PsUserAllocated.h" +#include "PsUtilities.h" +#include "PsFoundation.h" + +namespace physx +{ +#define PX_INVALID_INTERACTION_ACTOR_ID 0xffffffff +#define PX_INVALID_INTERACTION_SCENE_ID 0xffffffff + +namespace Sc +{ + // Interactions are used for connecting actors into activation groups. An interaction always connects exactly two actors. + // An interaction is implicitly active if at least one of the two actors it connects is active. + + class Interaction : public Ps::UserAllocated + { + PX_NOCOPY(Interaction) + + protected: + Interaction(ActorSim& actor0, ActorSim& actor1, InteractionType::Enum interactionType, PxU8 flags); + ~Interaction() { PX_ASSERT(!readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)); } + + public: + // Interactions automatically register themselves in the actors here + PX_FORCE_INLINE bool registerInActors(void* data = NULL); + + // Interactions automatically unregister themselves from the actors here + PX_FORCE_INLINE void unregisterFromActors(); + + PX_FORCE_INLINE ActorSim& getActorSim0() const { return mActor0; } + PX_FORCE_INLINE ActorSim& getActorSim1() const { return mActor1; } + + PX_FORCE_INLINE Scene& getScene() const { return mActor0.getScene(); } + + PX_FORCE_INLINE InteractionType::Enum getType() const { return InteractionType::Enum(mInteractionType); } + + PX_FORCE_INLINE PxU8 readInteractionFlag(PxU8 flag) const { return PxU8(mInteractionFlags & flag); } + PX_FORCE_INLINE void raiseInteractionFlag(InteractionFlag::Enum flag) { mInteractionFlags |= flag; } + PX_FORCE_INLINE void clearInteractionFlag(InteractionFlag::Enum flag) { mInteractionFlags &= ~flag; } + + /** + \brief Mark the interaction as dirty. This will put the interaction into a list that is processed once per simulation step. + + @see InteractionDirtyFlag + */ + PX_FORCE_INLINE void setDirty(PxU32 dirtyFlags); + + /** + \brief Clear all flags that mark the interaction as dirty and optionally remove the interaction from the list of dirty interactions. + + @see InteractionDirtyFlag + */ + /*PX_FORCE_INLINE*/ void setClean(bool removeFromList); + + PX_FORCE_INLINE Ps::IntBool needsRefiltering() const { return (getDirtyFlags() & InteractionDirtyFlag::eFILTER_STATE); } + + PX_FORCE_INLINE Ps::IntBool isElementInteraction() const; + + // Called when an interaction is activated or created. + // Return true if activation should proceed else return false (for example: joint interaction between two kinematics should not get activated) +// virtual bool onActivate_(void* data) = 0; + + // Called when an interaction is deactivated. + // Return true if deactivation should proceed else return false (for example: joint interaction between two kinematics can ignore deactivation because it always is deactivated) +// virtual bool onDeactivate_() = 0; + + PX_FORCE_INLINE void setInteractionId(PxU32 id) { mSceneId = id; } + PX_FORCE_INLINE PxU32 getInteractionId() const { return mSceneId; } + PX_FORCE_INLINE bool isRegistered() const { return mSceneId != PX_INVALID_INTERACTION_SCENE_ID; } + + PX_FORCE_INLINE void setActorId(ActorSim* actor, PxU32 id); + PX_FORCE_INLINE PxU32 getActorId(const ActorSim* actor) const; + + PX_FORCE_INLINE PxU8 getDirtyFlags() const { return mDirtyFlags; } + + private: + void addToDirtyList(); + void removeFromDirtyList(); + + ActorSim& mActor0; + ActorSim& mActor1; + + // PT: TODO: merge the 6bits of the 3 PxU8s in the top bits of the 3 PxU32s + PxU32 mSceneId; // PT: TODO: merge this with mInteractionType + + // PT: TODO: are those IDs even worth caching? Since the number of interactions per actor is (or should be) small, + // we could just do a linear search and save memory here... + PxU32 mActorId0; // PT: id of this interaction within mActor0's mInteractions array + PxU32 mActorId1; // PT: id of this interaction within mActor1's mInteractions array + protected: + PxU8 mInteractionType; // PT: stored on a byte to save space, should be InteractionType enum, 5/6 bits needed here + PxU8 mInteractionFlags; // PT: 6 bits needed here, see InteractionFlag enum + PxU8 mDirtyFlags; // PT: 6 bits needed here, see InteractionDirtyFlag enum + PxU8 mPadding; + }; + +} // namespace Sc + +////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE bool Sc::Interaction::registerInActors(void* data) +{ + bool active = activateInteraction(this, data); + + mActor0.registerInteractionInActor(this); + mActor1.registerInteractionInActor(this); + + return active; +} + +PX_FORCE_INLINE void Sc::Interaction::unregisterFromActors() +{ + mActor0.unregisterInteractionFromActor(this); + mActor1.unregisterInteractionFromActor(this); +} + +PX_FORCE_INLINE void Sc::Interaction::setActorId(ActorSim* actor, PxU32 id) +{ + PX_ASSERT(id != PX_INVALID_INTERACTION_ACTOR_ID); + PX_ASSERT(&mActor0 == actor || &mActor1 == actor); + if(&mActor0 == actor) + mActorId0 = id; + else + mActorId1 = id; +} + +PX_FORCE_INLINE PxU32 Sc::Interaction::getActorId(const ActorSim* actor) const +{ + PX_ASSERT(&mActor0 == actor || &mActor1 == actor); + return &mActor0 == actor ? mActorId0 : mActorId1; +} + +PX_FORCE_INLINE Ps::IntBool Sc::Interaction::isElementInteraction() const +{ + const Ps::IntBool res = readInteractionFlag(InteractionFlag::eRB_ELEMENT); + PX_ASSERT( (res && + ((getType() == InteractionType::eOVERLAP) || + (getType() == InteractionType::eTRIGGER) || + (getType() == InteractionType::eMARKER))) || + (!res && + ((getType() == InteractionType::eCONSTRAINTSHADER) || + (getType() == InteractionType::eARTICULATION)))); + + return res; +} + +PX_FORCE_INLINE void Sc::Interaction::setDirty(PxU32 dirtyFlags) +{ + PX_ASSERT(getType() != InteractionType::eARTICULATION); + + mDirtyFlags |= Ps::to8(dirtyFlags); + if(!readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)) + { + addToDirtyList(); + raiseInteractionFlag(InteractionFlag::eIN_DIRTY_LIST); + } +} + +//PX_FORCE_INLINE void Sc::Interaction::setClean(bool removeFromList) +//{ +// if (readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)) +// { +// if (removeFromList) // if we process all dirty interactions anyway, then we can just clear the list at the end and save the work here. +// removeFromDirtyList(); +// clearInteractionFlag(InteractionFlag::eIN_DIRTY_LIST); +// } +// +// mDirtyFlags = 0; +//} + + +} + +#endif // PX_PHYSICS_SCP_INTERACTION diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h b/src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h new file mode 100644 index 000000000..cd2b8eaa9 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_INTERACTION_FLAGS +#define PX_PHYSICS_SCP_INTERACTION_FLAGS + +#include "foundation/Px.h" + +namespace physx +{ + +namespace Sc +{ + struct InteractionFlag // PT: TODO: use PxFlags + { + enum Enum + { + eRB_ELEMENT = (1 << 0), // Interactions between rigid body shapes + eCONSTRAINT = (1 << 1), + eFILTERABLE = (1 << 2), // Interactions that go through the filter code + eIN_DIRTY_LIST = (1 << 3), // The interaction is in the dirty list + eIS_FILTER_PAIR = (1 << 4), // The interaction is tracked by the filter callback mechanism + eIS_ACTIVE = (1 << 5) + }; + }; + + struct InteractionDirtyFlag + { + enum Enum + { + eFILTER_STATE = (1 << 0), // All changes filtering related + eMATERIAL = (1 << 1), + eBODY_KINEMATIC = (1 << 2) | eFILTER_STATE, // A transition between dynamic and kinematic (and vice versa) require a refiltering + eDOMINANCE = (1 << 3), + eREST_OFFSET = (1 << 4), + eVISUALIZATION = (1 << 5) + }; + }; + + +} // namespace Sc + + +} // namespace physx + + +#endif // PX_PHYSICS_SCP_INTERACTION_FLAGS diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp new file mode 100644 index 000000000..2b4ad6153 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp @@ -0,0 +1,105 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScIterators.h" +#include "ScBodySim.h" +#include "ScShapeSim.h" +#include "ScShapeInteraction.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////// + +Sc::ContactIterator::Pair::Pair(const void*& contactPatches, const void*& contactPoints, PxU32 /*contactDataSize*/, const PxReal*& forces, PxU32 numContacts, PxU32 numPatches, + ShapeSim& shape0, ShapeSim& shape1) +: mIndex(0) +, mNumContacts(numContacts) +, mIter(reinterpret_cast(contactPatches), reinterpret_cast(contactPoints), reinterpret_cast(forces + numContacts), numPatches, numContacts) +, mForces(forces) +{ + mCurrentContact.shape0 = shape0.getPxShape(); + mCurrentContact.shape1 = shape1.getPxShape(); + mCurrentContact.normalForceAvailable = (forces != NULL); +} + +Sc::ContactIterator::Pair* Sc::ContactIterator::getNextPair() +{ + if(mCurrent < mLast) + { + ShapeInteraction* si = static_cast(*mCurrent); + + const void* contactPatches = NULL; + const void* contactPoints = NULL; + PxU32 contactDataSize = 0; + const PxReal* forces = NULL; + PxU32 numContacts = 0; + PxU32 numPatches = 0; + + PxU32 nextOffset = si->getContactPointData(contactPatches, contactPoints, contactDataSize, numContacts, numPatches, forces, mOffset, *mOutputs); + + if (nextOffset == mOffset) + ++mCurrent; + else + mOffset = nextOffset; + + mCurrentPair = Pair(contactPatches, contactPoints, contactDataSize, forces, numContacts, numPatches, si->getShape0(), si->getShape1()); + return &mCurrentPair; + } + else + return NULL; +} + +Sc::Contact* Sc::ContactIterator::Pair::getNextContact() +{ + if(mIndex < mNumContacts) + { + if(!mIter.hasNextContact()) + { + if(!mIter.hasNextPatch()) + return NULL; + mIter.nextPatch(); + } + PX_ASSERT(mIter.hasNextContact()); + mIter.nextContact(); + + mCurrentContact.normal = mIter.getContactNormal(); + mCurrentContact.point = mIter.getContactPoint(); + mCurrentContact.separation = mIter.getSeparation(); + mCurrentContact.normalForce = mForces ? mForces[mIndex] : 0; + mCurrentContact.faceIndex0 = mIter.getFaceIndex0(); + mCurrentContact.faceIndex1 = mIter.getFaceIndex1(); + + mIndex++; + return &mCurrentContact; + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp new file mode 100644 index 000000000..1a7105e97 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp @@ -0,0 +1,424 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "ScActorCore.h" +#include "ScActorSim.h" +#include "ScBodyCore.h" +#include "ScStaticCore.h" +#include "ScConstraintCore.h" +#include "ScMaterialCore.h" +#include "ScShapeCore.h" +#include "ScArticulationCore.h" +#include "ScArticulationJointCore.h" + +using namespace physx; +using namespace Ps; +using namespace Cm; +using namespace Sc; + +/////////////////////////////////////////////////////////////////////////////// + +template class PxMetaDataArray : public physx::shdfnd::Array +{ +public: + static PX_FORCE_INLINE physx::PxU32 getDataOffset() { return PX_OFFSET_OF(PxMetaDataArray, mData); } + static PX_FORCE_INLINE physx::PxU32 getDataSize() { return PX_SIZE_OF(PxMetaDataArray, mData); } + static PX_FORCE_INLINE physx::PxU32 getSizeOffset() { return PX_OFFSET_OF(PxMetaDataArray, mSize); } + static PX_FORCE_INLINE physx::PxU32 getSizeSize() { return PX_SIZE_OF(PxMetaDataArray, mSize); } + static PX_FORCE_INLINE physx::PxU32 getCapacityOffset() { return PX_OFFSET_OF(PxMetaDataArray, mCapacity); } + static PX_FORCE_INLINE physx::PxU32 getCapacitySize() { return PX_SIZE_OF(PxMetaDataArray, mCapacity); } +}; + +void Sc::ActorCore::getBinaryMetaData(PxOutputStream& stream) +{ + // 16 bytes + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxActorFlags, PxU8) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxDominanceGroup, PxU8) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxClientID, PxU8) + + PX_DEF_BIN_METADATA_CLASS(stream, Sc::ActorCore) + + PX_DEF_BIN_METADATA_ITEM(stream, Sc::ActorCore, ActorSim, mSim, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, Sc::ActorCore, PxU32, mAggregateIDOwnerClient, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sc::ActorCore, PxActorFlags, mActorFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sc::ActorCore, PxU8, mActorType, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sc::ActorCore, PxU8, mDominanceGroup, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PxsRigidCore(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxsRigidCore) + + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxTransform, body2World, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxRigidBodyFlags, mFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxU8, mIdtBody2Actor, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxU16, solverIterationCounts, 0) +} + +namespace +{ + class ShadowPxsBodyCore : public PxsBodyCore + { + public: + static void getBinaryMetaData(PxOutputStream& stream) + { + PX_DEF_BIN_METADATA_CLASS(stream, ShadowPxsBodyCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, ShadowPxsBodyCore, PxsRigidCore) + + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxTransform, body2Actor, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, ccdAdvanceCoefficient, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxVec3, linearVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, maxPenBias, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxVec3, angularVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, contactReportThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, maxAngularVelocitySq, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, maxLinearVelocitySq, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, linearDamping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, angularDamping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxVec3, inverseInertia, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, inverseMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, maxContactImpulse, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, sleepThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, freezeThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, wakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, solverWakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxU32, numCountedInteractions, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxU32, numBodyInteractions, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxU16, isFastMoving, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxU16, lockFlags, 0) + } + }; +} + +static void getBinaryMetaData_PxsBodyCore(PxOutputStream& stream) +{ + getBinaryMetaData_PxsRigidCore(stream); + +/* PX_DEF_BIN_METADATA_CLASS(stream, PxsBodyCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxsBodyCore, PxsRigidCore) + + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxTransform, body2Actor, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, ccdAdvanceCoefficient, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxVec3, linearVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, maxPenBias, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxVec3, angularVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, contactReportThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, maxAngularVelocitySq, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, maxLinearVelocitySq, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, linearDamping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, angularDamping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxVec3, inverseInertia, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, inverseMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, maxContactImpulse, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, sleepThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, freezeThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, wakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, solverWakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxU32, numCountedInteractions, 0)*/ + + ShadowPxsBodyCore::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxsBodyCore, ShadowPxsBodyCore) +} + +/* +We need to fix the header deps by moving the API out of PhysXCore and into its own dir where other code can get to it. +[25.08.2010 18:34:57] Dilip Sequeira: In the meantime, I think it's Ok to include PxSDK.h, but you're right, we need to be very careful about include deps in that direction. +[25.08.2010 18:38:15] Dilip Sequeira: On the memory thing... PxsBodyCore has 28 bytes of padding at the end, for no reason. In addition, it has two words of padding after the velocity fields, to facilitate SIMD loads. But in fact, Vec3FromVec4 is fast enough such that unless you were using it in an inner loop (which we never are with PxsBodyCore) that padding isn't worth it. +[25.08.2010 18:38:58] Dilip Sequeira: So, we should drop the end-padding, and move the damping values to replace the velocity padding. This probably requires a bit of fixup in the places where we do SIMD writes to the velocity. +[25.08.2010 18:39:18] Dilip Sequeira: Then we're down to 92 bytes of data, and 4 bytes of padding I think. +[25.08.2010 18:50:41] Dilip Sequeira: The reason we don't want to put the sleep data there explicitly is that it isn't LL data so I'd rather not have it in an LL interface struct. +[25.08.2010 19:04:53] Gordon Yeoman nvidia: simd loads are faster when they are 16-byte aligned. I think the padding might be to ensure the second vector is also 16-byte aligned. We could drop the second 4-byte pad but dropping the 1st 4-byte pad will likely have performance implications. +[25.08.2010 19:06:22] Dilip Sequeira: We should still align the vec3s, as now - but we shouldn't use padding to do it, since there are a boatload of scalar data fields floating around in that struct too. +*/ +void Sc::BodyCore::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PxsBodyCore(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxRigidBodyFlags, PxU8) + +// 176 => 144 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Sc::BodyCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Sc::BodyCore, Sc::RigidCore) + + PX_DEF_BIN_METADATA_ITEM(stream, Sc::BodyCore, PxsBodyCore, mCore, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sc::BodyCore, SimStateData, mSimStateData, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Sc::ConstraintCore::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxConstraintFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, ConstraintCore) + + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxConstraintFlags, mFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxU16, mPaddingFromFlags, PxMetaDataFlag::ePADDING) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxVec3, mAppliedForce, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxVec3, mAppliedTorque, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxConstraintConnector, mConnector, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxConstraintProject, mProject, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxConstraintSolverPrep, mSolverPrep, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxConstraintVisualize, mVisualize, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxU32, mDataSize, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxReal, mLinearBreakForce, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxReal, mAngularBreakForce, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxReal, mMinResponseThreshold, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, ConstraintSim, mSim, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Sc::MaterialCore::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxCombineMode::Enum, PxU32) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxMaterialFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, MaterialCore) + + // MaterialData + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxReal, dynamicFriction, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxReal, staticFriction, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxReal, restitution, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxMaterialFlags, flags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxU8, fricRestCombineMode, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxU8, padding, PxMetaDataFlag::ePADDING) + + // MaterialCore + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxMaterial, mNxMaterial, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxU32, mMaterialIndex, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Sc::RigidCore::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Sc::RigidCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Sc::RigidCore, Sc::ActorCore) +} + + +/////////////////////////////////////////////////////////////////////////////// + +void Sc::StaticCore::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Sc::StaticCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Sc::StaticCore, Sc::RigidCore) + + PX_DEF_BIN_METADATA_ITEM(stream, Sc::StaticCore, PxsRigidCore, mCore, 0) + +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PxFilterData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxFilterData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxFilterData, PxU32, word0, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxFilterData, PxU32, word1, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxFilterData, PxU32, word2, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxFilterData, PxU32, word3, 0) +} + +static void getBinaryMetaData_PxsShapeCore(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxsShapeCore) + + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, PxTransform, transform, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, Gu::GeometryUnion, geometry, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, PxReal, contactOffset, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, PxShapeFlags, mShapeFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, PxU8, mOwnsMaterialIdxMemory, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, PxU16, materialIndex, 0) +} + +void Sc::ShapeCore::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PxFilterData(stream); + getBinaryMetaData_PxsShapeCore(stream); + + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxShapeFlags, PxU8) + +// 144 => 128 bytes + PX_DEF_BIN_METADATA_CLASS(stream, ShapeCore) + + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxFilterData, mQueryFilterData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxFilterData, mSimulationFilterData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxsShapeCore, mCore, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxReal, mRestOffset, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxReal, mTorsionalRadius, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxReal, mMinTorsionalPatchRadius, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_ArticulationCore(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Dy::ArticulationCore) + + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxArticulationFlags, PxU8) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxU32, internalDriveIterations, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxU32, externalDriveIterations, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxU32, maxProjectionIterations, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxU16, solverIterationCounts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxReal, separationTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxReal, sleepThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxReal, freezeThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxReal, wakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxArticulationFlags, flags, 0) +} + +void Sc::ArticulationCore::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_ArticulationCore(stream); + + PX_DEF_BIN_METADATA_CLASS(stream, ArticulationCore) + + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationCore, ArticulationSim, mSim, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationCore, Dy::ArticulationCore, mCore, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationCore, PxU32, mType, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_ArticulationLimit(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Dy::ArticulationLimit) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationLimit, PxReal, low, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationLimit, PxReal, high, 0) +} + +static void getBinaryMetaData_ArticulationDrive(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Dy::ArticulationDrive) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationDrive, PxReal, stiffness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationDrive, PxReal, damping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationDrive, PxReal, maxForce, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationDrive, bool, isAcceleration, 0) +} + +static void getBinaryMetaData_ArticulationJointCoreBase(PxOutputStream& stream) +{ + getBinaryMetaData_ArticulationLimit(stream); + getBinaryMetaData_ArticulationDrive(stream); + + PX_DEF_BIN_METADATA_CLASS(stream, Dy::ArticulationJointCoreBase) + + PX_DEF_BIN_METADATA_TYPEDEF(stream, ArticulationJointCoreDirtyFlags, PxU8) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxArticulationMotions, PxU8) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxTransform, parentPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxTransform, childPose, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, Dy::ArticulationLimit, limits, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, Dy::ArticulationDrive, drives, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, PxReal, targetP, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, PxReal, targetV, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxQuat, relativeQuat, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxReal, frictionCoefficient, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxU32, jointOffset, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, PxU8, dofIds, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, PxArticulationMotions, motion, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxReal, maxJointVelocity, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, ArticulationJointCoreDirtyFlags, dirtyFlag, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, bool, prismaticLimited, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxU8, jointType, 0) +} + + +static void getBinaryMetaData_ArticulationJointCore(PxOutputStream& stream) +{ + getBinaryMetaData_ArticulationJointCoreBase(stream); + PX_DEF_BIN_METADATA_CLASS(stream, Dy::ArticulationJointCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Dy::ArticulationJointCore, Dy::ArticulationJointCoreBase) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxQuat, targetPosition, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxVec3, targetVelocity, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, spring, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, damping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, internalCompliance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, externalCompliance, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, swingLimitContactDistance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tangentialStiffness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tangentialDamping, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, bool, swingLimited, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, bool, twistLimited, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxU8, driveType, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, twistLimitContactDistance, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQSwingY, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQSwingZ, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQSwingPad, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQTwistHigh, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQTwistLow, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQTwistPad, 0) +} + +void Sc::ArticulationJointCore::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_ArticulationJointCore(stream); + PX_DEF_BIN_METADATA_CLASS(stream, ArticulationJointCore) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationJointCore, ArticulationJointSim, mSim, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationJointCore, Dy::ArticulationJointCore, mCore, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationJointCore, Dy::ArticulationCore, mArticulation, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationJointCore, Dy::PxArticulationJointBase, mRootType, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +#define PX_DEF_BIN_METADATA_ARRAY(stream, Class, type, array) \ +{ PxMetaDataEntry tmp = {"void", #array".mData", PxU32(PX_OFFSET_OF(Class, array)) + PxMetaDataArray::getDataOffset(), PxMetaDataArray::getDataSize(), 1, 0, PxMetaDataFlag::ePTR, 0}; PX_STORE_METADATA(stream, tmp); } \ +{ PxMetaDataEntry tmp = {"PxU32", #array".mSize", PxU32(PX_OFFSET_OF(Class, array)) + PxMetaDataArray::getSizeOffset(), PxMetaDataArray::getSizeSize(), 1, 0, 0, 0}; PX_STORE_METADATA(stream, tmp); } \ +{ PxMetaDataEntry tmp = {"PxU32", #array".mCapacity", PxU32(PX_OFFSET_OF(Class, array)) + PxMetaDataArray::getCapacityOffset(), PxMetaDataArray::getCapacitySize(), 1, 0, PxMetaDataFlag::eCOUNT_MASK_MSB, 0}; PX_STORE_METADATA(stream, tmp); } \ +{ PxMetaDataEntry tmp = {#type, 0, PxU32(PX_OFFSET_OF(Class, array)) + PxMetaDataArray::getSizeOffset(), PxMetaDataArray::getSizeSize(), 0, 0, PxMetaDataFlag::eEXTRA_DATA, 0}; PX_STORE_METADATA(stream, tmp); } + + + + + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp new file mode 100644 index 000000000..a6f8b3165 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp @@ -0,0 +1,2054 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScNPhaseCore.h" +#include "ScShapeInteraction.h" +#include "ScTriggerInteraction.h" +#include "ScElementInteractionMarker.h" +#include "ScConstraintInteraction.h" +#include "ScConstraintSim.h" +#include "ScConstraintCore.h" +#include "ScSimStats.h" +#include "ScObjectIDTracker.h" +#include "ScSimStats.h" + +#include "PsThread.h" +#include "BpBroadPhase.h" +#include "common/PxProfileZone.h" + +using namespace physx; +using namespace Sc; +using namespace Gu; + + +class Sc::FilterPairManager : public Ps::UserAllocated +{ + PX_NOCOPY(FilterPairManager) +public: + FilterPairManager() + : mPairs(PX_DEBUG_EXP("FilterPairManager Array")) + , mFree(INVALID_FILTER_PAIR_INDEX) + {} + + PxU32 acquireIndex() + { + PxU32 index; + if(mFree == INVALID_FILTER_PAIR_INDEX) + { + index = mPairs.size(); + mPairs.pushBack(NULL); + } + else + { + index = PxU32(mFree); + mFree = reinterpret_cast(mPairs[index]); + mPairs[index] = NULL; + } + return index; + } + + void releaseIndex(PxU32 index) + { + mPairs[index] = reinterpret_cast(mFree); + mFree = index; + } + + void setPair(PxU32 index, Sc::ElementSimInteraction* ptr) + { + mPairs[index] = ptr; + } + + Sc::ElementSimInteraction* operator[](PxU32 index) + { + return mPairs[index]; + } + + PxU32 findIndex(Sc::ElementSimInteraction* ptr) + { + return ptr->getFilterPairIndex(); + } + +private: + Ps::Array mPairs; + uintptr_t mFree; +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxU32 hasTriggerFlags(PxShapeFlags flags) { return PxU32(flags) & PxU32(PxShapeFlag::eTRIGGER_SHAPE); } +static void getFilterInfo_ShapeSim(PxFilterObjectAttributes& filterAttr, PxFilterData& filterData, const Sc::ShapeSim& shape) +{ + filterAttr = hasTriggerFlags(shape.getCore().getFlags()) ? PxFilterObjectFlag::eTRIGGER : PxFilterObjectFlag::Enum(0); + + BodySim* b = shape.getBodySim(); + if(b) + { + if(!b->isArticulationLink()) + { + if(b->isKinematic()) + filterAttr |= PxFilterObjectFlag::eKINEMATIC; + + setFilterObjectAttributeType(filterAttr, PxFilterObjectType::eRIGID_DYNAMIC); + } + else + setFilterObjectAttributeType(filterAttr, PxFilterObjectType::eARTICULATION); + } + else + { + setFilterObjectAttributeType(filterAttr, PxFilterObjectType::eRIGID_STATIC); + } + + filterData = shape.getCore().getSimulationFilterData(); +} + +static PX_FORCE_INLINE void getFilterInfo(PxFilterData& fd, PxFilterObjectAttributes& fa, const ElementSim& e) +{ + getFilterInfo_ShapeSim(fa, fd, static_cast(e)); +} + +static void getFilterInfo(PxFilterData& fd0, PxFilterData& fd1, PxFilterObjectAttributes& fa0, PxFilterObjectAttributes& fa1, const ElementSim& e0, const ElementSim& e1) +{ + getFilterInfo(fd0, fa0, e0); + getFilterInfo(fd1, fa1, e1); +} + +static PX_INLINE void callPairLost(Sc::Scene& scene, const ElementSim& e0, const ElementSim& e1, PxU32 pairID, bool objVolumeRemoved) +{ + PxFilterData fd0(PxEmpty), fd1(PxEmpty); + PxFilterObjectAttributes fa0, fa1; + getFilterInfo(fd0, fd1, fa0, fa1, e0, e1); + + scene.getFilterCallbackFast()->pairLost(pairID, fa0, fd0, fa1, fd1, objVolumeRemoved); +} + +// Filtering + +static PX_INLINE void checkFilterFlags(PxFilterFlags& filterFlags) +{ + if((filterFlags & (PxFilterFlag::eKILL | PxFilterFlag::eSUPPRESS)) == (PxFilterFlag::eKILL | PxFilterFlag::eSUPPRESS)) + { +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: eKILL and eSUPPRESS must not be set simultaneously. eSUPPRESS will be used."); +#endif + filterFlags.clear(PxFilterFlag::eKILL); + } +} + +static PX_FORCE_INLINE PxPairFlags checkRbPairFlags(const ShapeSim& s0, const ShapeSim& s1, PxPairFlags pairFlags) +{ +#if PX_CHECKED + // we want to avoid to run contact generation for pairs that should not get resolved or have no contact/trigger reports + if (!(PxU32(pairFlags) & (PxPairFlag::eSOLVE_CONTACT | ShapeInteraction::CONTACT_REPORT_EVENTS))) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: Pair with no contact/trigger reports detected, nor is PxPairFlag::eSOLVE_CONTACT set. It is recommended to suppress/kill such pairs for performance reasons."); + } + else if(!(pairFlags & (PxPairFlag::eDETECT_DISCRETE_CONTACT | PxPairFlag::eDETECT_CCD_CONTACT))) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: Pair did not request either eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT. It is recommended to suppress/kill such pairs for performance reasons."); + } + + if(((s0.getFlags() & PxShapeFlag::eTRIGGER_SHAPE)!=0 || (s1.getFlags() & PxShapeFlag::eTRIGGER_SHAPE)!=0) && + (pairFlags & PxPairFlag::eTRIGGER_DEFAULT) && (pairFlags & PxPairFlag::eDETECT_CCD_CONTACT)) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: CCD isn't supported on Triggers yet"); + } +#else + PX_UNUSED(s0); + PX_UNUSED(s1); +#endif + return pairFlags; +} + +static PX_INLINE PxPairFlags checkRbPairFlags( const ShapeSim& s0, const ShapeSim& s1, + const Sc::BodySim* bs0, const Sc::BodySim* bs1, + PxPairFlags pairFlags, PxFilterFlags filterFlags) +{ + if(filterFlags & (PxFilterFlag::eSUPPRESS | PxFilterFlag::eKILL)) + return pairFlags; + + if (bs0 && bs0->isKinematic() && + bs1 && bs1->isKinematic() && + (pairFlags & PxPairFlag::eSOLVE_CONTACT)) + { +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: Resolving contacts between two kinematic objects is invalid. Contacts will not get resolved."); +#endif + pairFlags.clear(PxPairFlag::eSOLVE_CONTACT); + } + + return checkRbPairFlags(s0, s1, pairFlags); +} + +// PT: version specialized for ShapeSim/ShapeSim +static PX_INLINE PxPairFlags checkRbPairFlags( const ShapeSim& s0, const ShapeSim& s1, + bool kine0, bool kine1, + PxPairFlags pairFlags, PxFilterFlags filterFlags) +{ + if(filterFlags & (PxFilterFlag::eSUPPRESS | PxFilterFlag::eKILL)) + return pairFlags; + + if(kine0 && kine1 && (pairFlags & PxPairFlag::eSOLVE_CONTACT)) + { +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: Resolving contacts between two kinematic objects is invalid. Contacts will not get resolved."); +#endif + pairFlags.clear(PxPairFlag::eSOLVE_CONTACT); + } + + return checkRbPairFlags(s0, s1, pairFlags); +} + +static PX_FORCE_INLINE void fetchActorAndShape(const ElementSim& e, PxActor*& a, PxShape*& s) +{ + const ShapeSim& sim = static_cast(e); + a = sim.getRbSim().getPxActor(); + s = sim.getPxShape(); +} + +static void runFilter(PxFilterInfo& filterInfo, const FilteringContext& context, const ElementSim& e0, const ElementSim& e1, PxU32 filterPairIndex, bool doCallbacks) +{ + PxFilterData fd0(PxEmpty), fd1(PxEmpty); + PxFilterObjectAttributes fa0, fa1; + getFilterInfo(fd0, fd1, fa0, fa1, e0, e1); + + // Run filter shader + filterInfo.filterFlags = context.mFilterShader(fa0, fd0, fa1, fd1, filterInfo.pairFlags, context.mFilterShaderData, context.mFilterShaderDataSize); + + if(filterInfo.filterFlags & PxFilterFlag::eCALLBACK) + { + if(context.mFilterCallback) + { + if(!doCallbacks) + { + return; + } + else + { + if(filterPairIndex == INVALID_FILTER_PAIR_INDEX) + filterPairIndex = context.mFilterPairManager->acquireIndex(); + // If a FilterPair is provided, then we use it, else we create a new one + // (A FilterPair is provided in the case for a pairLost()-pairFound() sequence after refiltering) + + PxActor* a0, *a1; + PxShape* s0, *s1; + fetchActorAndShape(e0, a0, s0); + fetchActorAndShape(e1, a1, s1); + + filterInfo.filterFlags = context.mFilterCallback->pairFound(filterPairIndex, fa0, fd0, a0, s0, fa1, fd1, a1, s1, filterInfo.pairFlags); + filterInfo.filterPairIndex = filterPairIndex; + } + } + else + { + filterInfo.filterFlags.clear(PxFilterFlag::eNOTIFY); + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: eCALLBACK set but no filter callback defined."); + } + } + + checkFilterFlags(filterInfo.filterFlags); + + if(filterPairIndex!=INVALID_FILTER_PAIR_INDEX && ((filterInfo.filterFlags & PxFilterFlag::eKILL) || ((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) != PxFilterFlag::eNOTIFY))) + { + if((filterInfo.filterFlags & PxFilterFlag::eKILL) && ((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) == PxFilterFlag::eNOTIFY)) + context.mFilterCallback->pairLost(filterPairIndex, fa0, fd0, fa1, fd1, false); + + if((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) != PxFilterFlag::eNOTIFY) + { + // No notification, hence we don't need to treat it as a filter callback pair anymore. + // Make sure that eCALLBACK gets removed as well + filterInfo.filterFlags.clear(PxFilterFlag::eNOTIFY); + } + + context.mFilterPairManager->releaseIndex(filterPairIndex); + filterInfo.filterPairIndex = INVALID_FILTER_PAIR_INDEX; + } + + // Sanity checks + PX_ASSERT( (filterInfo.filterFlags != PxFilterFlag::eKILL) || + ((filterInfo.filterFlags == PxFilterFlag::eKILL) && (filterInfo.filterPairIndex == INVALID_FILTER_PAIR_INDEX)) ); + PX_ASSERT( ((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) != PxFilterFlag::eNOTIFY) || + (((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) == PxFilterFlag::eNOTIFY) && filterInfo.filterPairIndex!=INVALID_FILTER_PAIR_INDEX) ); +} + +// PT: version specialized for ShapeSim/ShapeSim +static PX_FORCE_INLINE void runFilterShapeSim(PxFilterInfo& filterInfo, const FilteringContext& context, const ShapeSim& e0, const ShapeSim& e1, const PxFilterObjectAttributes fa0, const PxFilterObjectAttributes fa1) +{ + // Run filter shader + { + const PxFilterData fd0 = e0.getCore().getSimulationFilterData(); + const PxFilterData fd1 = e1.getCore().getSimulationFilterData(); + filterInfo.filterFlags = context.mFilterShader(fa0, fd0, fa1, fd1, filterInfo.pairFlags, context.mFilterShaderData, context.mFilterShaderDataSize); + } + + if(filterInfo.filterFlags & PxFilterFlag::eCALLBACK) + { + if(context.mFilterCallback) + { + return; + } + else + { + filterInfo.filterFlags.clear(PxFilterFlag::eNOTIFY); + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: eCALLBACK set but no filter callback defined."); + } + } + + checkFilterFlags(filterInfo.filterFlags); + + // Sanity checks + PX_ASSERT( (filterInfo.filterFlags != PxFilterFlag::eKILL) || + ((filterInfo.filterFlags == PxFilterFlag::eKILL) && (filterInfo.filterPairIndex == INVALID_FILTER_PAIR_INDEX)) ); + PX_ASSERT( ((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) != PxFilterFlag::eNOTIFY) || + (((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) == PxFilterFlag::eNOTIFY) && filterInfo.filterPairIndex!=INVALID_FILTER_PAIR_INDEX) ); +} + +// helper method for some cleanup code that is used multiple times for early outs in case a rigid body collision pair gets filtered out due to some hardwired filter criteria +static PX_FORCE_INLINE PxFilterInfo filterOutRbCollisionPair(FilterPairManager* filterPairManager, PxU32 filterPairIndex, const PxFilterFlags filterFlags) +{ + if(filterPairIndex!=INVALID_FILTER_PAIR_INDEX) + filterPairManager->releaseIndex(filterPairIndex); + + return PxFilterInfo(filterFlags); +} + +PxFilterInfo Sc::filterRbCollisionPairSecondStage(const FilteringContext& context, const ShapeSim& s0, const ShapeSim& s1, const Sc::BodySim* b0, const Sc::BodySim* b1, PxU32 filterPairIndex, bool runCallbacks) +{ + PxFilterInfo filterInfo; + runFilter(filterInfo, context, s0, s1, filterPairIndex, runCallbacks); + + if(runCallbacks || (!(filterInfo.filterFlags & PxFilterFlag::eCALLBACK))) + filterInfo.pairFlags = checkRbPairFlags(s0, s1, b0, b1, filterInfo.pairFlags, filterInfo.filterFlags); + + return filterInfo; +} + +// PT: version specialized for ShapeSim/ShapeSim +static PX_FORCE_INLINE PxFilterInfo filterRbCollisionPairSecondStage(const FilteringContext& context, const ShapeSim& s0, const ShapeSim& s1, bool kine0, bool kine1, const PxFilterObjectAttributes fa0, const PxFilterObjectAttributes fa1) +{ + PxFilterInfo filterInfo; + runFilterShapeSim(filterInfo, context, s0, s1, fa0, fa1); + + if(!(filterInfo.filterFlags & PxFilterFlag::eCALLBACK)) + filterInfo.pairFlags = checkRbPairFlags(s0, s1, kine0, kine1, filterInfo.pairFlags, filterInfo.filterFlags); + + return filterInfo; +} + +static bool filterArticulationLinks(const ActorSim& rbActor0, const ActorSim& rbActor1) +{ + // If the bodies are articulation links of the same articulation and one link is the parent of the other link, then disable collision + PxU32 size = rbActor0.getActorInteractionCount(); + Interaction** interactions = rbActor0.getActorInteractions(); + while(size--) + { + const Interaction* interaction = *interactions++; + if(interaction->getType() == InteractionType::eARTICULATION) + { + if((&interaction->getActorSim0() == &rbActor1) || (&interaction->getActorSim1() == &rbActor1)) + return true; + } + } + return false; +} + +static bool filterJointedBodies(const Sc::BodySim* body, const ActorSim& other) +{ + if(!body->readInternalFlag(Sc::BodySim::BF_HAS_CONSTRAINTS)) + return false; + + const Sc::ActorSim* actorToMatch; + PxU32 size; + Interaction** interactions; + + if(body->getActorInteractionCount() <= other.getActorInteractionCount()) + { + size = body->getActorInteractionCount(); + interactions = body->getActorInteractions(); + actorToMatch = &other; + } + else + { + size = other.getActorInteractionCount(); + interactions = other.getActorInteractions(); + actorToMatch = body; + } + + while(size--) + { + Interaction* interaction = *interactions++; + if(interaction->getType() == InteractionType::eCONSTRAINTSHADER) + { + ConstraintInteraction* csi = static_cast(interaction); + if((&csi->getActorSim0() == actorToMatch) || (&csi->getActorSim1() == actorToMatch)) + return !(csi->getConstraint()->getCore().getFlags() & PxConstraintFlag::eCOLLISION_ENABLED); + } + } + return false; +} + +static PX_FORCE_INLINE bool filterJointedBodies(const Sc::BodySim* b0, const Sc::BodySim* b1, const ActorSim& rbActor0, const ActorSim& rbActor1) +{ + // If the bodies of the shape pair are connected by a joint, we need to check whether this connection disables the collision. + // Note: As an optimization, the dynamic bodies have a flag which specifies whether they have any constraints at all. That works + // because a constraint has at least one dynamic body and an interaction is tracked by both objects. + + if(b0) + return filterJointedBodies(b0, rbActor1); + + if(b1) + return filterJointedBodies(b1, rbActor0); + + return false; +} + +static PX_FORCE_INLINE bool filterKinematics(const Sc::BodySim* b0, const Sc::BodySim* b1, bool kine0, bool kine1, const PxSceneFlags& sceneFlags) +{ + // if at least one object is kinematic + const bool kinematicPair = kine0 | kine1; + if(kinematicPair) + { + // ...then ignore kinematic vs. static pairs + if(!(sceneFlags & PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS)) + { + if(!b0 || !b1) + return true; + } + + // ...and ignore kinematic vs. kinematic pairs + if(!(sceneFlags & PxSceneFlag::eENABLE_KINEMATIC_PAIRS)) + { + if(kine0 && kine1) + return true; + } + } + return false; +} + +static PxFilterInfo filterRbCollisionPair(const FilteringContext& context, const ShapeSim& s0, const ShapeSim& s1, PxU32 filterPairIndex, bool& isTriggerPair, bool runCallbacks) +{ + const Sc::BodySim* b0 = s0.getBodySim(); + const Sc::BodySim* b1 = s1.getBodySim(); + + const PxU32 trigger0 = s0.getFlags() & PxShapeFlag::eTRIGGER_SHAPE; + const PxU32 trigger1 = s1.getFlags() & PxShapeFlag::eTRIGGER_SHAPE; + isTriggerPair = (trigger0 | trigger1)!=0; + if(isTriggerPair) + { + if(trigger0 && trigger1) // // trigger-trigger pairs are not supported + return filterOutRbCollisionPair(context.mFilterPairManager, filterPairIndex, PxFilterFlag::eKILL); + } + else + { + const bool kine0 = b0 ? b0->isKinematic() : false; + const bool kine1 = b1 ? b1->isKinematic() : false; + + if(filterKinematics(b0, b1, kine0, kine1, context.mSceneFlags)) + return filterOutRbCollisionPair(context.mFilterPairManager, filterPairIndex, PxFilterFlag::eSUPPRESS); + + const ActorSim& rbActor0 = s0.getActor(); + const ActorSim& rbActor1 = s1.getActor(); + + if(filterJointedBodies(b0, b1, rbActor0, rbActor1)) + return filterOutRbCollisionPair(context.mFilterPairManager, filterPairIndex, PxFilterFlag::eSUPPRESS); + + if((rbActor0.getActorType() == PxActorType::eARTICULATION_LINK) && (rbActor1.getActorType() == PxActorType::eARTICULATION_LINK)) + { + if(filterArticulationLinks(rbActor0, rbActor1)) + return filterOutRbCollisionPair(context.mFilterPairManager, filterPairIndex, PxFilterFlag::eKILL); + } + } + return filterRbCollisionPairSecondStage(context, s0, s1, b0, b1, filterPairIndex, runCallbacks); +} + +// PT: indexed by PxActorType +static const PxU32 gTypeData[] = { + PxFilterObjectType::eRIGID_STATIC<<1, + (PxFilterObjectType::eRIGID_DYNAMIC<<1)|1, + (PxFilterObjectType::eARTICULATION<<1)|1 +}; + +// PT: version specialized for ShapeSim/ShapeSim (no triggers) +static PX_FORCE_INLINE PxFilterInfo filterRbCollisionPair(const FilteringContext& context, const ShapeSim& s0, const ShapeSim& s1) +{ + const ActorSim& rbActor0 = s0.getActor(); + const PxActorType::Enum actorType0 = rbActor0.getActorType(); + const PxU32 typeData0 = gTypeData[actorType0]; + PxFilterObjectAttributes filterAttr0 = typeData0>>1; + const Sc::BodySim* b0; + bool kine0; + if(typeData0 & 1) + { + b0 = static_cast(&rbActor0); + kine0 = b0->isKinematic(); + filterAttr0 |= kine0 ? PxFilterObjectFlag::eKINEMATIC : 0; + } + else + { + b0 = NULL; + kine0 = false; + } + + const ActorSim& rbActor1 = s1.getActor(); + const PxActorType::Enum actorType1 = rbActor1.getActorType(); + const PxU32 typeData1 = gTypeData[actorType1]; + PxFilterObjectAttributes filterAttr1 = typeData1>>1; + const Sc::BodySim* b1; + bool kine1; + if(typeData1 & 1) + { + b1 = static_cast(&rbActor1); + kine1 = b1->isKinematic(); + filterAttr1 |= kine1 ? PxFilterObjectFlag::eKINEMATIC : 0; + } + else + { + b1 = NULL; + kine1 = false; + } + + PX_ASSERT(!(s0.getFlags() & PxShapeFlag::eTRIGGER_SHAPE)); + PX_ASSERT(!(s1.getFlags() & PxShapeFlag::eTRIGGER_SHAPE)); + + if(filterKinematics(b0, b1, kine0, kine1, context.mSceneFlags)) + return PxFilterInfo(PxFilterFlag::eSUPPRESS); + + if(filterJointedBodies(b0, b1, rbActor0, rbActor1)) + return PxFilterInfo(PxFilterFlag::eSUPPRESS); + + if((actorType0 == PxActorType::eARTICULATION_LINK) && (actorType1 == PxActorType::eARTICULATION_LINK)) + { + if(filterArticulationLinks(rbActor0, rbActor1)) + return PxFilterInfo(PxFilterFlag::eKILL); + } + + return filterRbCollisionPairSecondStage(context, s0, s1, kine0, kine1, filterAttr0, filterAttr1); +} + +void Sc::NPhaseCore::runOverlapFilters( PxU32 nbToProcess, const Bp::AABBOverlap* PX_RESTRICT pairs, PxFilterInfo* PX_RESTRICT filterInfo, + PxU32& nbToKeep_, PxU32& nbToSuppress_, PxU32& nbToCallback_, PxU32* PX_RESTRICT keepMap, PxU32* PX_RESTRICT callbackMap) +{ + PxU32 nbToKeep = 0; + PxU32 nbToSuppress = 0; + PxU32 nbToCallback = 0; + + const FilteringContext context(mOwnerScene, mFilterPairManager); + + for(PxU32 i=0; i(pair.mUserData0); + Sc::ElementSim* e1 = reinterpret_cast(pair.mUserData1); + PX_ASSERT(!findInteraction(e0, e1)); + + ShapeSim* s0 = static_cast(e0); + ShapeSim* s1 = static_cast(e1); + PX_ASSERT(&s0->getActor() != &s1->getActor()); // No actor internal interactions + + filterInfo[i] = filterRbCollisionPair(context, *s0, *s1); + + const PxFilterFlags filterFlags = filterInfo[i].filterFlags; + + if(!(filterFlags & PxFilterFlag::eKILL)) + { + if(filterFlags & PxFilterFlag::eCALLBACK) + { + nbToCallback++; + callbackMap[i / 32] |= (1 << (i & 31)); + } + else + { + if(!(filterFlags & PxFilterFlag::eSUPPRESS)) + nbToKeep++; + else + nbToSuppress++; + keepMap[i / 32] |= (1 << (i & 31)); + } + } + } + + nbToKeep_ = nbToKeep; + nbToSuppress_ = nbToSuppress; + nbToCallback_ = nbToCallback; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Sc::NPhaseCore::NPhaseCore(Scene& scene, const PxSceneDesc& sceneDesc) : + mOwnerScene (scene), + mContactReportActorPairSet (PX_DEBUG_EXP("contactReportPairSet")), + mPersistentContactEventPairList (PX_DEBUG_EXP("persistentContactEventPairs")), + mNextFramePersistentContactEventPairIndex(0), + mForceThresholdContactEventPairList (PX_DEBUG_EXP("forceThresholdContactEventPairs")), + mContactReportBuffer (sceneDesc.contactReportStreamBufferSize, (sceneDesc.flags & PxSceneFlag::eDISABLE_CONTACT_REPORT_BUFFER_RESIZE)), + mActorPairPool (PX_DEBUG_EXP("actorPairPool")), + mActorPairReportPool (PX_DEBUG_EXP("actorPairReportPool")), + mShapeInteractionPool (Ps::AllocatorTraits::Type(PX_DEBUG_EXP("shapeInteractionPool")), 256), + mTriggerInteractionPool (PX_DEBUG_EXP("triggerInteractionPool")), + mActorPairContactReportDataPool (PX_DEBUG_EXP("actorPairContactReportPool")), + mInteractionMarkerPool (PX_DEBUG_EXP("interactionMarkerPool")) + ,mMergeProcessedTriggerInteractions (scene.getContextId(), this, "ScNPhaseCore.mergeProcessedTriggerInteractions") + ,mTmpTriggerProcessingBlock (NULL) + ,mTriggerPairsToDeactivateCount (0) +{ + mFilterPairManager = PX_NEW(FilterPairManager); +} + +Sc::NPhaseCore::~NPhaseCore() +{ + // Clear pending actor pairs (waiting on contact report callback) + clearContactReportActorPairs(false); + PX_DELETE(mFilterPairManager); +} + +PxU32 Sc::NPhaseCore::getDefaultContactReportStreamBufferSize() const +{ + return mContactReportBuffer.getDefaultBufferSize(); +} + +Sc::ElementSimInteraction* Sc::NPhaseCore::findInteraction(ElementSim* _element0, ElementSim* _element1) +{ + const Ps::HashMap::Entry* pair = mElementSimMap.find(ElementSimKey(_element0, _element1)); + return pair ? pair->second : NULL; +} + +void Sc::NPhaseCore::onOverlapCreated(const Bp::AABBOverlap* PX_RESTRICT pairs, PxU32 pairCount) +{ + for(PxU32 i=0; i(pairs[i].mUserData0); + ElementSim* volume1 = reinterpret_cast(pairs[i].mUserData1); + PX_ASSERT(!findInteraction(volume0, volume1)); + + ShapeSim* shapeHi = static_cast(volume1); + ShapeSim* shapeLo = static_cast(volume0); + + // No actor internal interactions + PX_ASSERT(&shapeHi->getActor() != &shapeLo->getActor()); + + // PT: this case is only for triggers these days + PX_ASSERT((shapeLo->getFlags() & PxShapeFlag::eTRIGGER_SHAPE) || (shapeHi->getFlags() & PxShapeFlag::eTRIGGER_SHAPE)); + + /*Sc::ElementSimInteraction* interaction =*/ createRbElementInteraction(*shapeHi, *shapeLo, NULL, NULL, NULL); + } +} + +void Sc::NPhaseCore::registerInteraction(ElementSimInteraction* interaction) +{ + mElementSimMap.insert(ElementSimKey(&interaction->getElement0(), &interaction->getElement1()), interaction); +} + +void Sc::NPhaseCore::unregisterInteraction(ElementSimInteraction* interaction) +{ + mElementSimMap.erase(ElementSimKey(&interaction->getElement0(), &interaction->getElement1())); +} + +ElementSimInteraction* Sc::NPhaseCore::onOverlapRemovedStage1(ElementSim* volume0, ElementSim* volume1) +{ + return findInteraction(volume0, volume1); +} + +void Sc::NPhaseCore::onOverlapRemoved(ElementSim* volume0, ElementSim* volume1, const PxU32 ccdPass, void* elemSim, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + ElementSim* elementHi = volume1; + ElementSim* elementLo = volume0; + // No actor internal interactions + PX_ASSERT(&elementHi->getActor() != &elementLo->getActor()); + + // PT: TODO: get rid of 'findInteraction', cf US10491 + ElementSimInteraction* interaction = elemSim ? reinterpret_cast(elemSim) : findInteraction(elementHi, elementLo); + // MS: The check below is necessary since at the moment LowLevel broadphase still tracks + // killed pairs and hence reports lost overlaps + if(interaction) + { + PxU32 flags = PxU32(PairReleaseFlag::eWAKE_ON_LOST_TOUCH); + PX_ASSERT(interaction->isElementInteraction()); + releaseElementPair(static_cast(interaction), flags, ccdPass, true, outputs, useAdaptiveForce); + } +} + +// MS: TODO: optimize this for the actor release case? +void Sc::NPhaseCore::onVolumeRemoved(ElementSim* volume, PxU32 flags, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + const PxU32 ccdPass = 0; + + flags |= PairReleaseFlag::eSHAPE_BP_VOLUME_REMOVED; + + // Release interactions + // IMPORTANT: Iterate from the back of the list to the front as we release interactions which + // triggers a replace with last + ElementSim::ElementInteractionReverseIterator iter = volume->getElemInteractionsReverse(); + ElementSimInteraction* interaction = iter.getNext(); + while(interaction) + { + PX_ASSERT( (interaction->getType() == InteractionType::eMARKER) || + (interaction->getType() == InteractionType::eOVERLAP) || + (interaction->getType() == InteractionType::eTRIGGER) ); + + releaseElementPair(interaction, flags, ccdPass, true, outputs, useAdaptiveForce); + + interaction = iter.getNext(); + } +} + +Sc::ElementSimInteraction* Sc::NPhaseCore::createRbElementInteraction(const PxFilterInfo& finfo, ShapeSim& s0, ShapeSim& s1, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction, + Sc::ElementInteractionMarker* interactionMarker, bool isTriggerPair) +{ + ElementSimInteraction* pair = NULL; + + if((finfo.filterFlags & PxFilterFlag::eSUPPRESS) == false) + { + if(!isTriggerPair) + pair = createShapeInteraction(s0, s1, finfo.pairFlags, contactManager, shapeInteraction); + else + pair = createTriggerInteraction(s0, s1, finfo.pairFlags); + } + else + pair = createElementInteractionMarker(s0, s1, interactionMarker); + + if(finfo.filterPairIndex != INVALID_FILTER_PAIR_INDEX) + { + // Mark the pair as a filter callback pair + pair->raiseInteractionFlag(InteractionFlag::eIS_FILTER_PAIR); + + // Filter callback pair: Set the link to the interaction + mFilterPairManager->setPair(finfo.filterPairIndex, pair); + pair->setFilterPairIndex(finfo.filterPairIndex); + } + + return pair; +} + +Sc::ElementSimInteraction* Sc::NPhaseCore::createRbElementInteraction(ShapeSim& s0, ShapeSim& s1, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction, + Sc::ElementInteractionMarker* interactionMarker) +{ + const FilteringContext context(mOwnerScene, mFilterPairManager); + + bool isTriggerPair = false; + const PxFilterInfo finfo = filterRbCollisionPair(context, s0, s1, INVALID_FILTER_PAIR_INDEX, isTriggerPair, false); + + if(finfo.filterFlags & PxFilterFlag::eKILL) + { + PX_ASSERT(finfo.filterPairIndex == INVALID_FILTER_PAIR_INDEX); // No filter callback pair info for killed pairs + return NULL; + } + + return createRbElementInteraction(finfo, s0, s1, contactManager, shapeInteraction, interactionMarker, isTriggerPair); +} + +void Sc::NPhaseCore::managerNewTouch(Sc::ShapeInteraction& interaction) +{ + //(1) if the pair hasn't already been assigned, look it up! + + ActorPair* actorPair = interaction.getActorPair(); + + if(!actorPair) + { + ShapeSim& s0 = static_cast(interaction.getElement0()); + ShapeSim& s1 = static_cast(interaction.getElement1()); + actorPair = findActorPair(&s0, &s1, interaction.isReportPair()); + actorPair->incRefCount(); //It's being referenced by a new pair... + interaction.setActorPair(*actorPair); + } +} + +Sc::ShapeInteraction* Sc::NPhaseCore::createShapeInteraction(ShapeSim& s0, ShapeSim& s1, PxPairFlags pairFlags, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction) +{ + ShapeSim* _s0 = &s0; + ShapeSim* _s1 = &s1; + + /* + This tries to ensure that if one of the bodies is static or kinematic, it will be body B + There is a further optimization to force all pairs that share the same bodies to have + the same body ordering. This reduces the number of required partitions in the parallel solver. + Sorting rules are: + If bodyA is static, swap + If bodyA is rigidDynamic and bodyB is articulation, swap + If bodyA is in an earlier BP group than bodyB, swap + */ + { + Sc::RigidSim& rs0 = s0.getRbSim(); + Sc::RigidSim& rs1 = s1.getRbSim(); + + const PxActorType::Enum actorType0 = rs0.getActorType(); + const PxActorType::Enum actorType1 = rs1.getActorType(); + if( actorType0 == PxActorType::eRIGID_STATIC + || (actorType0 == PxActorType::eRIGID_DYNAMIC && actorType1 == PxActorType::eARTICULATION_LINK) + || ((actorType0 == PxActorType::eRIGID_DYNAMIC && actorType1 == PxActorType::eRIGID_DYNAMIC) && s0.getBodySim()->isKinematic()) + || (actorType0 == actorType1 && rs0.getRigidID() < rs1.getRigidID())) + Ps::swap(_s0, _s1); + } + + ShapeInteraction* si = shapeInteraction ? shapeInteraction : mShapeInteractionPool.allocate(); + PX_PLACEMENT_NEW(si, ShapeInteraction)(*_s0, *_s1, pairFlags, contactManager); + + PX_ASSERT(si->mReportPairIndex == INVALID_REPORT_PAIR_ID); + + return si; +} + +Sc::TriggerInteraction* Sc::NPhaseCore::createTriggerInteraction(ShapeSim& s0, ShapeSim& s1, PxPairFlags triggerFlags) +{ + ShapeSim* triggerShape; + ShapeSim* otherShape; + + if(s1.getFlags() & PxShapeFlag::eTRIGGER_SHAPE) + { + triggerShape = &s1; + otherShape = &s0; + } + else + { + triggerShape = &s0; + otherShape = &s1; + } + TriggerInteraction* pair = mTriggerInteractionPool.construct(*triggerShape, *otherShape); + pair->setTriggerFlags(triggerFlags); + return pair; +} + +Sc::ElementInteractionMarker* Sc::NPhaseCore::createElementInteractionMarker(ElementSim& e0, ElementSim& e1, Sc::ElementInteractionMarker* interactionMarker) +{ + ElementInteractionMarker* pair = interactionMarker ? interactionMarker : mInteractionMarkerPool.allocate(); + PX_PLACEMENT_NEW(pair, ElementInteractionMarker)(e0, e1, interactionMarker != NULL); + return pair; +} + +Sc::ElementSimInteraction* Sc::NPhaseCore::refilterInteraction(ElementSimInteraction* pair, const PxFilterInfo* filterInfo, bool removeFromDirtyList, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + const InteractionType::Enum oldType = pair->getType(); + + switch (oldType) + { + case InteractionType::eTRIGGER: + case InteractionType::eMARKER: + case InteractionType::eOVERLAP: + { + ShapeSim& s0 = static_cast(pair->getElement0()); + ShapeSim& s1 = static_cast(pair->getElement1()); + + PxFilterInfo finfo; + if(filterInfo) + { + // The filter changes are provided by an outside source (the user filter callback) + + finfo = *filterInfo; + PX_ASSERT(finfo.filterPairIndex!=INVALID_FILTER_PAIR_INDEX); + + if((finfo.filterFlags & PxFilterFlag::eKILL) && + ((finfo.filterFlags & PxFilterFlag::eNOTIFY) == PxFilterFlag::eNOTIFY) ) + { + callPairLost(mOwnerScene, pair->getElement0(), pair->getElement1(), finfo.filterPairIndex, false); + mFilterPairManager->releaseIndex(finfo.filterPairIndex); + finfo.filterPairIndex = INVALID_FILTER_PAIR_INDEX; + } + + Sc::BodySim* bs0 = s0.getBodySim(); + Sc::BodySim* bs1 = s1.getBodySim(); + finfo.pairFlags = checkRbPairFlags(s0, s1, bs0, bs1, finfo.pairFlags, finfo.filterFlags); + + //!!!Filter Issue: Need to check whether the requested changes don't violate hard constraints. + // - Assert that the shapes are not connected through a collision disabling joint + } + else + { + PxU32 filterPairIndex = INVALID_FILTER_PAIR_INDEX; + if(pair->readInteractionFlag(InteractionFlag::eIS_FILTER_PAIR)) + { + filterPairIndex = mFilterPairManager->findIndex(pair); + PX_ASSERT(filterPairIndex!=INVALID_FILTER_PAIR_INDEX); + + callPairLost(mOwnerScene, pair->getElement0(), pair->getElement1(), filterPairIndex, false); + } + + const FilteringContext context(mOwnerScene, mFilterPairManager); + + bool isTriggerPair; + finfo = filterRbCollisionPair(context, s0, s1, filterPairIndex, isTriggerPair, true); + PX_UNUSED(isTriggerPair); + } + + if(pair->readInteractionFlag(InteractionFlag::eIS_FILTER_PAIR) && + ((finfo.filterFlags & PxFilterFlag::eNOTIFY) != PxFilterFlag::eNOTIFY) ) + { + // The pair was a filter callback pair but not any longer + pair->clearInteractionFlag(InteractionFlag::eIS_FILTER_PAIR); + + if(finfo.filterPairIndex!=INVALID_FILTER_PAIR_INDEX) + { + mFilterPairManager->releaseIndex(finfo.filterPairIndex); + finfo.filterPairIndex = INVALID_FILTER_PAIR_INDEX; + } + } + + struct Local + { + static Sc::InteractionType::Enum getRbElementInteractionType(const ShapeSim* primitive0, const ShapeSim* primitive1, PxFilterFlags filterFlag) + { + if(filterFlag & PxFilterFlag::eKILL) + return InteractionType::eINVALID; + + if(filterFlag & PxFilterFlag::eSUPPRESS) + return InteractionType::eMARKER; + + if(primitive0->getFlags() & PxShapeFlag::eTRIGGER_SHAPE + || primitive1->getFlags() & PxShapeFlag::eTRIGGER_SHAPE) + return InteractionType::eTRIGGER; + + PX_ASSERT( (primitive0->getGeometryType() != PxGeometryType::eTRIANGLEMESH) || + (primitive1->getGeometryType() != PxGeometryType::eTRIANGLEMESH)); + + return InteractionType::eOVERLAP; + } + }; + + const InteractionType::Enum newType = Local::getRbElementInteractionType(&s0, &s1, finfo.filterFlags); + if(pair->getType() != newType) //Only convert interaction type if the type has changed + { + return convert(pair, newType, finfo, removeFromDirtyList, outputs, useAdaptiveForce); + } + else + { + //The pair flags might have changed, we need to forward the new ones + if(oldType == InteractionType::eOVERLAP) + { + ShapeInteraction* si = static_cast(pair); + + const PxU32 newPairFlags = finfo.pairFlags; + const PxU32 oldPairFlags = si->getPairFlags(); + PX_ASSERT((newPairFlags & ShapeInteraction::PAIR_FLAGS_MASK) == newPairFlags); + PX_ASSERT((oldPairFlags & ShapeInteraction::PAIR_FLAGS_MASK) == oldPairFlags); + + if(newPairFlags != oldPairFlags) + { + if(!(oldPairFlags & ShapeInteraction::CONTACT_REPORT_EVENTS) && (newPairFlags & ShapeInteraction::CONTACT_REPORT_EVENTS) && (si->getActorPair() == NULL || !si->getActorPair()->isReportPair())) + { + // for this actor pair there was no shape pair that requested contact reports but now there is one + // -> all the existing shape pairs need to get re-adjusted to point to an ActorPairReport instance instead. + findActorPair(&s0, &s1, Ps::IntTrue); + } + + if(si->readFlag(ShapeInteraction::IN_PERSISTENT_EVENT_LIST) && (!(newPairFlags & PxPairFlag::eNOTIFY_TOUCH_PERSISTS))) + { + // the new report pair flags don't require persistent checks anymore -> remove from persistent list + // Note: The pair might get added to the force threshold list later + if(si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)) + removeFromPersistentContactEventPairs(si); + else + si->clearFlag(ShapeInteraction::WAS_IN_PERSISTENT_EVENT_LIST); + } + + if(newPairFlags & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS) + { + PX_ASSERT((si->mReportPairIndex == INVALID_REPORT_PAIR_ID) || (!si->readFlag(ShapeInteraction::WAS_IN_PERSISTENT_EVENT_LIST))); + + if(si->mReportPairIndex == INVALID_REPORT_PAIR_ID && si->readInteractionFlag(InteractionFlag::eIS_ACTIVE)) + { + PX_ASSERT(!si->readFlag(ShapeInteraction::WAS_IN_PERSISTENT_EVENT_LIST)); // sanity check: an active pair should never have this flag set + + if(si->hasTouch()) + addToForceThresholdContactEventPairs(si); + } + } + else if((oldPairFlags & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS)) + { + // no force threshold events needed any longer -> clear flags + si->clearFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_FLAGS); + + if(si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)) + removeFromForceThresholdContactEventPairs(si); + } + } + si->setPairFlags(finfo.pairFlags); + } + else if(oldType == InteractionType::eTRIGGER) + static_cast(pair)->setTriggerFlags(finfo.pairFlags); + + return pair; + } + } + case InteractionType::eCONSTRAINTSHADER: + case InteractionType::eARTICULATION: + case InteractionType::eTRACKED_IN_SCENE_COUNT: + case InteractionType::eINVALID: + PX_ASSERT(0); + break; + } + return NULL; +} + +Sc::ActorPair* Sc::NPhaseCore::findActorPair(ShapeSim* s0, ShapeSim* s1, Ps::IntBool isReportPair) +{ + PX_ASSERT(!(s0->getFlags() & PxShapeFlag::eTRIGGER_SHAPE) + && !(s1->getFlags() & PxShapeFlag::eTRIGGER_SHAPE)); + // This method is only for the case where a ShapeInteraction is going to be created. + // Else we might create an ActorPair that does not get referenced and causes a mem leak. + + BodyPairKey key; + + RigidSim* aLess = &s0->getRbSim(); + RigidSim* aMore = &s1->getRbSim(); + + if(aLess->getRigidID() > aMore->getRigidID()) + Ps::swap(aLess, aMore); + + key.mSim0 = aLess->getRigidID(); + key.mSim1 = aMore->getRigidID(); + + Sc::ActorPair*& actorPair = mActorPairMap[key]; + + //PX_ASSERT(actorPair == NULL || actorPair->getRefCount() > 0); + if(actorPair == NULL) + { + if(!isReportPair) + actorPair = mActorPairPool.construct(); + else + actorPair = mActorPairReportPool.construct(s0->getRbSim(), s1->getRbSim()); + } + + Ps::IntBool actorPairHasReports = actorPair->isReportPair(); + + if(!isReportPair || actorPairHasReports) + return actorPair; + else + { + PxU32 size = aLess->getActorInteractionCount(); + Interaction** interactions = aLess->getActorInteractions(); + + ActorPairReport* actorPairReport = mActorPairReportPool.construct(s0->getRbSim(), s1->getRbSim()); + actorPairReport->convert(*actorPair); + + while(size--) + { + Interaction* interaction = *interactions++; + if((&interaction->getActorSim0() == aMore) || (&interaction->getActorSim1() == aMore)) + { + PX_ASSERT(((&interaction->getActorSim0() == aLess) || (&interaction->getActorSim1() == aLess))); + + if(interaction->getType() == InteractionType::eOVERLAP) + { + ShapeInteraction* si = static_cast(interaction); + if(si->getActorPair() != NULL) + si->setActorPair(*actorPairReport); + } + } + } + actorPair = actorPairReport; + } + return actorPair; +} + +PX_FORCE_INLINE void Sc::NPhaseCore::destroyActorPairReport(ActorPairReport& aPair) +{ + PX_ASSERT(aPair.isReportPair()); + + aPair.releaseContactReportData(*this); + mActorPairReportPool.destroy(&aPair); +} + +Sc::ElementSimInteraction* Sc::NPhaseCore::convert(ElementSimInteraction* pair, InteractionType::Enum newType, PxFilterInfo& filterInfo, bool removeFromDirtyList, + PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + PX_ASSERT(newType != pair->getType()); + + ElementSim& elementA = pair->getElement0(); + ElementSim& elementB = pair->getElement1(); + + // Wake up the actors of the pair + if((pair->getActorSim0().getActorType() == PxActorType::eRIGID_DYNAMIC) && !(static_cast(pair->getActorSim0()).isActive())) + static_cast(pair->getActorSim0()).internalWakeUp(); + if((pair->getActorSim1().getActorType() == PxActorType::eRIGID_DYNAMIC) && !(static_cast(pair->getActorSim1()).isActive())) + static_cast(pair->getActorSim1()).internalWakeUp(); + + // Since the FilterPair struct might have been re-used in the newly created interaction, we need to clear + // the filter pair marker of the old interaction to avoid that the FilterPair gets deleted by the releaseElementPair() + // call that follows. + pair->clearInteractionFlag(InteractionFlag::eIS_FILTER_PAIR); + + // PT: we need to unregister the old interaction *before* creating the new one, because Sc::NPhaseCore::registerInteraction will use + // ElementSim pointers which are the same for both. Since "releaseElementPair" will call the unregister function from + // the element's dtor, we don't need to do it explicitly here. Just release the object. + releaseElementPair(pair, PairReleaseFlag::eWAKE_ON_LOST_TOUCH | PairReleaseFlag::eBP_VOLUME_REMOVED, 0, removeFromDirtyList, outputs, useAdaptiveForce); + + ElementSimInteraction* result = NULL; + switch(newType) + { + case InteractionType::eINVALID: + // This means the pair should get killed + break; + case InteractionType::eMARKER: + { + result = createElementInteractionMarker(elementA, elementB, NULL); + break; + } + case InteractionType::eOVERLAP: + { + result = createShapeInteraction(static_cast(elementA), static_cast(elementB), filterInfo.pairFlags, NULL, NULL); + break; + } + case InteractionType::eTRIGGER: + { + result = createTriggerInteraction(static_cast(elementA), static_cast(elementB), filterInfo.pairFlags); + break; + } + case InteractionType::eCONSTRAINTSHADER: + case InteractionType::eARTICULATION: + case InteractionType::eTRACKED_IN_SCENE_COUNT: + PX_ASSERT(0); + break; + }; + + if(filterInfo.filterPairIndex != INVALID_FILTER_PAIR_INDEX) + { + PX_ASSERT(result); + // If a filter callback pair is going to get killed, then the FilterPair struct should already have + // been deleted. + + // Mark the new interaction as a filter callback pair + result->raiseInteractionFlag(InteractionFlag::eIS_FILTER_PAIR); + + mFilterPairManager->setPair(filterInfo.filterPairIndex, result); + result->setFilterPairIndex(filterInfo.filterPairIndex); + } + return result; +} + +namespace physx +{ +namespace Sc +{ +static bool findTriggerContacts(TriggerInteraction* tri, bool toBeDeleted, bool volumeRemoved, + PxTriggerPair& triggerPair, TriggerPairExtraData& triggerPairExtra, + SimStats::TriggerPairCountsNonVolatile& triggerPairStats) +{ + ShapeSim& s0 = tri->getTriggerShape(); + ShapeSim& s1 = tri->getOtherShape(); + + const PxPairFlags pairFlags = tri->getTriggerFlags(); + PxPairFlags pairEvent; + + bool overlap; + PxU8 testForRemovedShapes = 0; + if(toBeDeleted) + { + // The trigger interaction is to lie down in its tomb, hence we know that the overlap is gone. + // What remains is to check whether the interaction was deleted because of a shape removal in + // which case we need to later check for removed shapes. + + overlap = false; + + if(volumeRemoved) + { + // Note: only the first removed volume can be detected when the trigger interaction is deleted but at a later point the second volume might get removed too. + testForRemovedShapes = TriggerPairFlag::eTEST_FOR_REMOVED_SHAPES; + } + } + else + { +#if PX_ENABLE_SIM_STATS + PX_ASSERT(s0.getGeometryType() < PxGeometryType::eCONVEXMESH+1); // The first has to be the trigger shape + triggerPairStats[s0.getGeometryType()][s1.getGeometryType()]++; +#else + PX_UNUSED(triggerPairStats); +#endif + + ShapeSim* primitive0 = &s0; + ShapeSim* primitive1 = &s1; + + PX_ASSERT(primitive0->getFlags() & PxShapeFlag::eTRIGGER_SHAPE + || primitive1->getFlags() & PxShapeFlag::eTRIGGER_SHAPE); + + // Reorder them if needed + if(primitive0->getGeometryType() > primitive1->getGeometryType()) + Ps::swap(primitive0, primitive1); + + const Gu::GeomOverlapFunc overlapFunc = + Gu::getOverlapFuncTable()[primitive0->getGeometryType()][primitive1->getGeometryType()]; + + PX_ALIGN(16, PxTransform globalPose0); + primitive0->getAbsPoseAligned(&globalPose0); + + PX_ALIGN(16, PxTransform globalPose1); + primitive1->getAbsPoseAligned(&globalPose1); + + PX_ASSERT(overlapFunc); + overlap = overlapFunc( primitive0->getCore().getGeometry(), globalPose0, + primitive1->getCore().getGeometry(), globalPose1, + &tri->getTriggerCache()); + } + + const bool hadOverlap = tri->lastFrameHadContacts(); + if(hadOverlap) + { + if(!overlap) + pairEvent = PxPairFlag::eNOTIFY_TOUCH_LOST; + } + else + { + if(overlap) + pairEvent = PxPairFlag::eNOTIFY_TOUCH_FOUND; + } + tri->updateLastFrameHadContacts(overlap); + + const PxPairFlags triggeredFlags = pairEvent & pairFlags; + if(triggeredFlags) + { + triggerPair.triggerShape = s0.getPxShape(); + triggerPair.otherShape = s1.getPxShape(); + triggerPair.status = PxPairFlag::Enum(PxU32(pairEvent)); + triggerPair.flags = PxTriggerPairFlags(testForRemovedShapes); + + const RigidCore& rigidCore0 = s0.getRbSim().getRigidCore(); + const RigidCore& rigidCore1 = s1.getRbSim().getRigidCore(); + + triggerPair.triggerActor = static_cast(rigidCore0.getPxActor()); + triggerPair.otherActor = static_cast(rigidCore1.getPxActor()); + + triggerPairExtra = TriggerPairExtraData(s0.getID(), s1.getID(), + rigidCore0.getOwnerClient(), rigidCore1.getOwnerClient()); + return true; + } + return false; +} + +class TriggerContactTask : public Cm::Task +{ +private: + TriggerContactTask& operator = (const TriggerContactTask&); + +public: + TriggerContactTask( Interaction* const* triggerPairs, PxU32 triggerPairCount, Ps::Mutex& lock, + TriggerInteraction** pairsToDeactivate, volatile PxI32& pairsToDeactivateCount, + Scene& scene) + : + Cm::Task(scene.getContextId()), + mTriggerPairs(triggerPairs), + mTriggerPairCount(triggerPairCount), + mLock(lock), + mPairsToDeactivate(pairsToDeactivate), + mPairsToDeactivateCount(pairsToDeactivateCount), + mScene(scene) + { + } + + virtual void runInternal() + { + SimStats::TriggerPairCountsNonVolatile triggerPairStats; +#if PX_ENABLE_SIM_STATS + PxMemZero(&triggerPairStats, sizeof(SimStats::TriggerPairCountsNonVolatile)); +#endif + PxTriggerPair triggerPair[sTriggerPairsPerTask]; + TriggerPairExtraData triggerPairExtra[sTriggerPairsPerTask]; + PxU32 triggerReportItemCount = 0; + + TriggerInteraction* deactivatePairs[sTriggerPairsPerTask]; + PxI32 deactivatePairCount = 0; + + for(PxU32 i=0; i < mTriggerPairCount; i++) + { + TriggerInteraction* tri = static_cast(mTriggerPairs[i]); + + PX_ASSERT(tri->readInteractionFlag(InteractionFlag::eIS_ACTIVE)); + + if(findTriggerContacts(tri, false, false, triggerPair[triggerReportItemCount], triggerPairExtra[triggerReportItemCount], triggerPairStats)) + triggerReportItemCount++; + + if(!(tri->readFlag(TriggerInteraction::PROCESS_THIS_FRAME))) + { + // active trigger pairs for which overlap tests were not forced should remain in the active list + // to catch transitions between overlap and no overlap + continue; + } + else + { + tri->clearFlag(TriggerInteraction::PROCESS_THIS_FRAME); + + // explicitly scheduled overlap test is done (after object creation, teleport, ...). Check if trigger pair should remain active or not. + + if(!tri->onActivate_(0)) + { + PX_ASSERT(tri->readInteractionFlag(InteractionFlag::eIS_ACTIVE)); + // Why is the assert enough? + // Once an explicit overlap test is scheduled, the interaction can not get deactivated anymore until it got processed. + + tri->clearInteractionFlag(InteractionFlag::eIS_ACTIVE); + deactivatePairs[deactivatePairCount] = tri; + deactivatePairCount++; + } + } + } + + if(triggerReportItemCount) + { + PxTriggerPair* triggerPairBuffer; + TriggerPairExtraData* triggerPairExtraBuffer; + + { + Ps::Mutex::ScopedLock lock(mLock); + + mScene.reserveTriggerReportBufferSpace(triggerReportItemCount, triggerPairBuffer, triggerPairExtraBuffer); + + PxMemCopy(triggerPairBuffer, triggerPair, sizeof(PxTriggerPair) * triggerReportItemCount); + PxMemCopy(triggerPairExtraBuffer, triggerPairExtra, sizeof(TriggerPairExtraData) * triggerReportItemCount); + } + } + + if(deactivatePairCount) + { + PxI32 newSize = Ps::atomicAdd(&mPairsToDeactivateCount, deactivatePairCount); + PxMemCopy(mPairsToDeactivate + newSize - deactivatePairCount, deactivatePairs, sizeof(TriggerInteraction*) * deactivatePairCount); + } + +#if PX_ENABLE_SIM_STATS + SimStats& simStats = mScene.getStatsInternal(); + for(PxU32 i=0; i < PxGeometryType::eCONVEXMESH+1; i++) + { + for(PxU32 j=0; j < PxGeometryType::eGEOMETRY_COUNT; j++) + { + if(triggerPairStats[i][j] != 0) + Ps::atomicAdd(&simStats.numTriggerPairs[i][j], triggerPairStats[i][j]); + } + } +#endif + } + + virtual const char* getName() const + { + return "ScNPhaseCore.triggerInteractionWork"; + } + +public: + static const PxU32 sTriggerPairsPerTask = 64; + +private: + Interaction* const* mTriggerPairs; + const PxU32 mTriggerPairCount; + Ps::Mutex& mLock; + TriggerInteraction** mPairsToDeactivate; + volatile PxI32& mPairsToDeactivateCount; + Scene& mScene; +}; + +} // namespace Sc +} // namespace physx + +void Sc::NPhaseCore::processTriggerInteractions(PxBaseTask* continuation) +{ + PX_ASSERT(!mTmpTriggerProcessingBlock); + PX_ASSERT(mTriggerPairsToDeactivateCount == 0); + + Scene& scene = mOwnerScene; + + // Triggers + Interaction** triggerInteractions = mOwnerScene.getActiveInteractions(InteractionType::eTRIGGER); + const PxU32 pairCount = mOwnerScene.getNbActiveInteractions(InteractionType::eTRIGGER); + + if(pairCount > 0) + { + const PxU32 taskCountWithoutRemainder = pairCount / TriggerContactTask::sTriggerPairsPerTask; + const PxU32 maxTaskCount = taskCountWithoutRemainder + 1; + const PxU32 pairPtrSize = pairCount * sizeof(TriggerInteraction*); + const PxU32 memBlockSize = pairPtrSize + (maxTaskCount * sizeof(TriggerContactTask)); + void* triggerProcessingBlock = scene.getLowLevelContext()->getScratchAllocator().alloc(memBlockSize, true); + if(triggerProcessingBlock) + { + const bool hasMultipleThreads = scene.getTaskManager().getCpuDispatcher()->getWorkerCount() > 1; + const bool moreThanOneBatch = pairCount > TriggerContactTask::sTriggerPairsPerTask; + const bool scheduleTasks = hasMultipleThreads && moreThanOneBatch; + // when running on a single thread, the task system seems to cause the main overhead (locking and atomic operations + // seemed less of an issue). Hence, the tasks get run directly in that case. Same if there is only one batch. + + mTmpTriggerProcessingBlock = triggerProcessingBlock; // note: gets released in the continuation task + if(scheduleTasks) + mMergeProcessedTriggerInteractions.setContinuation(continuation); + + TriggerInteraction** triggerPairsToDeactivateWriteBack = reinterpret_cast(triggerProcessingBlock); + TriggerContactTask* triggerContactTaskBuffer = reinterpret_cast(reinterpret_cast(triggerProcessingBlock) + pairPtrSize); + + PxU32 remainder = pairCount; + while(remainder) + { + const PxU32 nb = remainder > TriggerContactTask::sTriggerPairsPerTask ? TriggerContactTask::sTriggerPairsPerTask : remainder; + remainder -= nb; + + TriggerContactTask* task = triggerContactTaskBuffer; + task = PX_PLACEMENT_NEW(task, TriggerContactTask( triggerInteractions, nb, mTriggerWriteBackLock, + triggerPairsToDeactivateWriteBack, mTriggerPairsToDeactivateCount, scene)); + if(scheduleTasks) + { + task->setContinuation(&mMergeProcessedTriggerInteractions); + task->removeReference(); + } + else + task->runInternal(); + + triggerContactTaskBuffer++; + triggerInteractions += nb; + } + + if(scheduleTasks) + mMergeProcessedTriggerInteractions.removeReference(); + else + mMergeProcessedTriggerInteractions.runInternal(); + } + else + { + Ps::getFoundation().getErrorCallback().reportError(PxErrorCode::eOUT_OF_MEMORY, "Temporary memory for trigger pair processing could not be allocated. Trigger overlap tests will not take place.", __FILE__, __LINE__); + } + } +} + +void Sc::NPhaseCore::mergeProcessedTriggerInteractions(PxBaseTask*) +{ + if(mTmpTriggerProcessingBlock) + { + // deactivate pairs that do not need trigger checks any longer (until woken up again) + TriggerInteraction** triggerPairsToDeactivate = reinterpret_cast(mTmpTriggerProcessingBlock); + for(PxI32 i=0; i < mTriggerPairsToDeactivateCount; i++) + { + mOwnerScene.notifyInteractionDeactivated(triggerPairsToDeactivate[i]); + } + mTriggerPairsToDeactivateCount = 0; + + mOwnerScene.getLowLevelContext()->getScratchAllocator().free(mTmpTriggerProcessingBlock); + mTmpTriggerProcessingBlock = NULL; + } +} + +void Sc::NPhaseCore::visualize(Cm::RenderOutput& renderOut, PxsContactManagerOutputIterator& outputs) +{ + if(mOwnerScene.getVisualizationScale() == 0.0f) + return; + + Interaction** interactions = mOwnerScene.getActiveInteractions(InteractionType::eOVERLAP); + PxU32 nbActiveInteractions = mOwnerScene.getNbActiveInteractions(InteractionType::eOVERLAP); + while(nbActiveInteractions--) + static_cast(*interactions++)->visualize(renderOut, outputs); +} + +class ProcessPersistentContactTask : public Cm::Task +{ + Sc::NPhaseCore& mCore; + ContactReportBuffer& mBuffer; + Ps::Mutex& mMutex; + ShapeInteraction*const* mPersistentEventPairs; + PxU32 mNbPersistentEventPairs; + PxsContactManagerOutputIterator mOutputs; + PX_NOCOPY(ProcessPersistentContactTask) +public: + + ProcessPersistentContactTask(Sc::NPhaseCore& core, ContactReportBuffer& buffer, Ps::Mutex& mutex, ShapeInteraction*const* persistentEventPairs, + PxU32 nbPersistentEventPairs, PxsContactManagerOutputIterator& outputs) : Cm::Task(0), mCore(core), mBuffer(buffer), mMutex(mutex), + mPersistentEventPairs(persistentEventPairs), mNbPersistentEventPairs(nbPersistentEventPairs), mOutputs(outputs) + { + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("ProcessPersistentContactTask", 0); + //ContactReportAllocationManager alloc(mBuffer, mMutex, 16 * 1024); + PxU32 size = mNbPersistentEventPairs; + ShapeInteraction*const* persistentEventPairs = mPersistentEventPairs; + while (size--) + { + ShapeInteraction* pair = *persistentEventPairs++; + if (size) + { + if (size > 1) + { + if (size > 2) + { + ShapeInteraction* nextPair = *(persistentEventPairs + 2); + Ps::prefetchLine(nextPair); + } + + ShapeInteraction* nextPair = *(persistentEventPairs + 1); + ActorPair* aPair = nextPair->getActorPair(); + Ps::prefetchLine(aPair); + + Ps::prefetchLine(&nextPair->getShape0()); + Ps::prefetchLine(&nextPair->getShape1()); + } + ShapeInteraction* nextPair = *(persistentEventPairs); + Ps::prefetchLine(&nextPair->getShape0().getActor()); + Ps::prefetchLine(&nextPair->getShape1().getActor()); + } + + PX_ASSERT(pair->hasTouch()); + PX_ASSERT(pair->isReportPair()); + + const PxU32 pairFlags = pair->getPairFlags(); + if ((pairFlags & PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) == PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) + { + // do not process the pair if only eDETECT_CCD_CONTACT is enabled because at this point CCD did not run yet. Plus the current CCD implementation can not reliably provide eNOTIFY_TOUCH_PERSISTS events + // for performance reasons. + //KS - filter based on edge activity! + + const BodySim* bodySim0 = pair->getShape0().getBodySim(); + const BodySim* bodySim1 = pair->getShape1().getBodySim(); + PX_ASSERT(bodySim0); // the first shape always belongs to a dynamic body + + if (bodySim0->isActive() || (bodySim1 && bodySim1->isActive())) + pair->processUserNotificationAsync(PxPairFlag::eNOTIFY_TOUCH_PERSISTS, 0, false, 0, false, mOutputs/*, &alloc*/); + } + } + } + + virtual const char* getName() const + { + return "ScNPhaseCore.ProcessPersistentContactTask"; + } + +}; + + +void Sc::NPhaseCore::processPersistentContactEvents(PxsContactManagerOutputIterator& outputs, PxBaseTask* continuation) +{ + PX_UNUSED(continuation); + PX_UNUSED(outputs); + PX_PROFILE_ZONE("Sc::NPhaseCore::processPersistentContactEvents", mOwnerScene.getContextId()); + //TODO: put back this optimization -- now we have to do this stuff if at least one client has a callback registered. + // if (ownerScene.getSimulatonEventCallbackFast() != NULL) + { + // Go through ShapeInteractions which requested persistent contact event reports. This is necessary since there are no low level events for persistent contact. + ShapeInteraction*const* persistentEventPairs = getCurrentPersistentContactEventPairs(); + PxU32 size = getCurrentPersistentContactEventPairCount(); + while (size--) + { + ShapeInteraction* pair = *persistentEventPairs++; + if (size) + { + ShapeInteraction* nextPair = *persistentEventPairs; + Ps::prefetchLine(nextPair); + } + + ActorPair* aPair = pair->getActorPair(); + Ps::prefetchLine(aPair); + + PX_ASSERT(pair->hasTouch()); + PX_ASSERT(pair->isReportPair()); + + const PxU32 pairFlags = pair->getPairFlags(); + if ((pairFlags & PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) == PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) + { + // do not process the pair if only eDETECT_CCD_CONTACT is enabled because at this point CCD did not run yet. Plus the current CCD implementation can not reliably provide eNOTIFY_TOUCH_PERSISTS events + // for performance reasons. + //KS - filter based on edge activity! + + const BodySim* bodySim0 = pair->getShape0().getBodySim(); + const BodySim* bodySim1 = pair->getShape1().getBodySim(); + PX_ASSERT(bodySim0); // the first shape always belongs to a dynamic body + + if (bodySim0->isActive() || (bodySim1 && bodySim1->isActive())) + pair->processUserNotification(PxPairFlag::eNOTIFY_TOUCH_PERSISTS, 0, false, 0, false, outputs); + } + } + } + + //{ + // PX_PROFILE_ZONE("Sc::NPhaseCore::processPersistentContactEvents2", mOwnerScene.getContextId()); + // // Go through ShapeInteractions which requested persistent contact event reports. This is necessary since there are no low level events for persistent contact. + // ShapeInteraction*const* persistentEventPairs = getCurrentPersistentContactEventPairs(); + // PxU32 size = getCurrentPersistentContactEventPairCount(); + + // //const PxU32 nbPerTask = 512u; + + // //for (PxU32 a = 0; a < size; a += nbPerTask) + // { + // ProcessPersistentContactTask* task = PX_PLACEMENT_NEW(mOwnerScene.getFlushPool()->allocate(sizeof(ProcessPersistentContactTask)), + // ProcessPersistentContactTask)(*this, mContactReportBuffer, mBufferAllocLock, persistentEventPairs, size, outputs); + // task->setContinuation(continuation); + // task->removeReference(); + // } + + // while (size--) + // { + // ShapeInteraction* pair = *persistentEventPairs++; + // if (size) + // { + // if (size > 1) + // { + // ShapeInteraction* nextPair = *(persistentEventPairs + 1); + // Ps::prefetchLine(nextPair); + // } + // ShapeInteraction* nextPair = *(persistentEventPairs); + // ActorPair* aPair = nextPair->getActorPair(); + // Ps::prefetchLine(aPair); + // } + + // PX_ASSERT(pair->hasTouch()); + // PX_ASSERT(pair->isReportPair()); + + // const PxU32 pairFlags = pair->getPairFlags(); + // if ((pairFlags & PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) == PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) + // { + // if (pair->getActorPair() != NULL) + // { + // ActorPairReport& aPairReport = pair->getActorPairReport(); + + // if (!aPairReport.isInContactReportActorPairSet()) + // { + // aPairReport.setInContactReportActorPairSet(); + // addToContactReportActorPairSet(&aPairReport); + // aPairReport.incRefCount(); + // } + + // //aPairReport.createContactStreamManager(*this); + // } + // } + // } + //} +} + +void Sc::NPhaseCore::fireCustomFilteringCallbacks(PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + PX_PROFILE_ZONE("Sim.fireCustomFilteringCallbacks", mOwnerScene.getContextId()); + + PxSimulationFilterCallback* callback = mOwnerScene.getFilterCallbackFast(); + + if(callback) + { + // Ask user for pair filter status changes + PxU32 pairID; + PxFilterFlags filterFlags; + PxPairFlags pairFlags; + while(callback->statusChange(pairID, pairFlags, filterFlags)) + { + ElementSimInteraction* ei = (*mFilterPairManager)[pairID]; + + PX_ASSERT(ei); + // Check if the user tries to update a pair even though he deleted it earlier in the same frame + + checkFilterFlags(filterFlags); + + PX_ASSERT(ei->readInteractionFlag(InteractionFlag::eIS_FILTER_PAIR)); + + PxFilterInfo finfo; + finfo.filterFlags = filterFlags; + finfo.pairFlags = pairFlags; + finfo.filterPairIndex = pairID; + + ElementSimInteraction* refInt = refilterInteraction(ei, &finfo, true, outputs, useAdaptiveForce); + + // this gets called at the end of the simulation -> there should be no dirty interactions around + PX_ASSERT(!refInt->readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)); + PX_ASSERT(!refInt->getDirtyFlags()); + + if((refInt == ei) && (refInt->getType() == InteractionType::eOVERLAP)) // No interaction conversion happened, the pairFlags were just updated + static_cast(refInt)->updateState(InteractionDirtyFlag::eFILTER_STATE); + } + } +} + +void Sc::NPhaseCore::addToDirtyInteractionList(Interaction* pair) +{ + mDirtyInteractions.insert(pair); +} + +void Sc::NPhaseCore::removeFromDirtyInteractionList(Interaction* pair) +{ + PX_ASSERT(mDirtyInteractions.contains(pair)); + + mDirtyInteractions.erase(pair); +} + +void Sc::NPhaseCore::updateDirtyInteractions(PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + // The sleeping SIs will be updated on activation + // clow: Sleeping SIs are not awaken for visualization updates + const bool dirtyDominance = mOwnerScene.readFlag(SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_DOMINANCE); + const bool dirtyVisualization = mOwnerScene.readFlag(SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_VISUALIZATION); + if(dirtyDominance || dirtyVisualization) + { + // Update all interactions. + + const PxU8 mask = Ps::to8((dirtyDominance ? InteractionDirtyFlag::eDOMINANCE : 0) | (dirtyVisualization ? InteractionDirtyFlag::eVISUALIZATION : 0)); + + Interaction** it = mOwnerScene.getInteractions(Sc::InteractionType::eOVERLAP); + PxU32 size = mOwnerScene.getNbInteractions(Sc::InteractionType::eOVERLAP); + while(size--) + { + Interaction* pair = *it++; + + PX_ASSERT(pair->getType() == InteractionType::eOVERLAP); + + if(!pair->readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)) + { + PX_ASSERT(!pair->getDirtyFlags()); + static_cast(pair)->updateState(mask); + } + else + pair->setDirty(mask); // the pair will get processed further below anyway, so just mark the flags dirty + } + } + + // Update all interactions in the dirty list + const PxU32 dirtyItcCount = mDirtyInteractions.size(); + Sc::Interaction* const* dirtyInteractions = mDirtyInteractions.getEntries(); + for(PxU32 i = 0; i < dirtyItcCount; i++) + { + Interaction* refInt = dirtyInteractions[i]; + Interaction* interaction = refInt; + + if(interaction->isElementInteraction() && interaction->needsRefiltering()) + { + ElementSimInteraction* pair = static_cast(interaction); + + refInt = refilterInteraction(pair, NULL, false, outputs, useAdaptiveForce); + } + + if(interaction == refInt) // Refiltering might convert the pair to another type and kill the old one. In that case we don't want to update the new pair since it has been updated on creation. + { + const InteractionType::Enum iType = interaction->getType(); + if (iType == InteractionType::eOVERLAP) + static_cast(interaction)->updateState(0); + else if (iType == InteractionType::eCONSTRAINTSHADER) + static_cast(interaction)->updateState(); + + interaction->setClean(false); // false because the dirty interactions list gets cleard further below + } + } + + mDirtyInteractions.clear(); +} + +void Sc::NPhaseCore::releaseElementPair(ElementSimInteraction* pair, PxU32 flags, const PxU32 ccdPass, bool removeFromDirtyList, PxsContactManagerOutputIterator& outputs, + bool useAdaptiveForce) +{ + pair->setClean(removeFromDirtyList); // Removes the pair from the dirty interaction list etc. + + if(pair->readInteractionFlag(InteractionFlag::eIS_FILTER_PAIR)) + { + // Check if this is a filter callback pair + const PxU32 filterPairIndex = mFilterPairManager->findIndex(pair); + PX_ASSERT(filterPairIndex!=INVALID_FILTER_PAIR_INDEX); + + // Is this interaction removed because one of the pair object broadphase volumes got deleted by the user? + const bool userRemovedVolume = ((flags & PairReleaseFlag::eBP_VOLUME_REMOVED) != 0); + + callPairLost(mOwnerScene, pair->getElement0(), pair->getElement1(), filterPairIndex, userRemovedVolume); + + mFilterPairManager->releaseIndex(filterPairIndex); + } + + switch(pair->getType()) + { + case InteractionType::eTRIGGER: + { + TriggerInteraction* tri = static_cast(pair); + PxTriggerPair triggerPair; + TriggerPairExtraData triggerPairExtra; + if (findTriggerContacts(tri, true, ((flags & PairReleaseFlag::eBP_VOLUME_REMOVED) != 0), + triggerPair, triggerPairExtra, + const_cast(mOwnerScene.getStatsInternal().numTriggerPairs))) + // cast away volatile-ness (this is fine since the method does not run in parallel) + { + mOwnerScene.getTriggerBufferAPI().pushBack(triggerPair); + mOwnerScene.getTriggerBufferExtraData().pushBack(triggerPairExtra); + } + mTriggerInteractionPool.destroy(tri); + } + break; + case InteractionType::eMARKER: + { + ElementInteractionMarker* interactionMarker = static_cast(pair); + mInteractionMarkerPool.destroy(interactionMarker); + } + break; + case InteractionType::eOVERLAP: + { + ShapeInteraction* si = static_cast(pair); + if(flags & PairReleaseFlag::eSHAPE_BP_VOLUME_REMOVED) + lostTouchReports(si, flags, ccdPass, outputs, useAdaptiveForce); + + mShapeInteractionPool.destroy(si); + } + break; + case InteractionType::eCONSTRAINTSHADER: + case InteractionType::eARTICULATION: + case InteractionType::eTRACKED_IN_SCENE_COUNT: + case InteractionType::eINVALID: + PX_ASSERT(0); + return; + } +} + +void Sc::NPhaseCore::lostTouchReports(ShapeInteraction* si, PxU32 flags, PxU32 ccdPass, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + if(si->hasTouch()) + { + if(si->isReportPair()) + si->sendLostTouchReport((flags & PairReleaseFlag::eBP_VOLUME_REMOVED) != 0, ccdPass, outputs); + + si->adjustCountersOnLostTouch(si->getShape0().getBodySim(), si->getShape1().getBodySim(), useAdaptiveForce); + } + + ActorPair* aPair = si->getActorPair(); + if(aPair && aPair->decRefCount() == 0) + { + RigidSim* sim0 = static_cast(&si->getActorSim0()); + RigidSim* sim1 = static_cast(&si->getActorSim1()); + + BodyPairKey pair; + + if(sim0->getRigidID() > sim1->getRigidID()) + Ps::swap(sim0, sim1); + + pair.mSim0 = sim0->getRigidID(); + pair.mSim1 = sim1->getRigidID(); + + mActorPairMap.erase(pair); + + if(!aPair->isReportPair()) + { + mActorPairPool.destroy(aPair); + } + else + { + ActorPairReport& apr = ActorPairReport::cast(*aPair); + destroyActorPairReport(apr); + } + } + si->clearActorPair(); + + if(si->hasTouch() || (!si->hasKnownTouchState())) + { + BodySim* b0 = si->getShape0().getBodySim(); + BodySim* b1 = si->getShape1().getBodySim(); + + if(flags & PairReleaseFlag::eWAKE_ON_LOST_TOUCH) + { + if(!b0 || !b1) + { + if(b0) + b0->internalWakeUp(); + if(b1) + b1->internalWakeUp(); + } + else if(!si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) + { + mOwnerScene.addToLostTouchList(b0, b1); + } + } + } +} + +void Sc::NPhaseCore::clearContactReportActorPairs(bool shrinkToZero) +{ + for(PxU32 i=0; i < mContactReportActorPairSet.size(); i++) + { + //TODO: prefetch? + ActorPairReport* aPair = mContactReportActorPairSet[i]; + const PxU32 refCount = aPair->getRefCount(); + PX_ASSERT(aPair->isInContactReportActorPairSet()); + PX_ASSERT(refCount > 0); + aPair->decRefCount(); // Reference held by contact callback + if(refCount > 1) + { + aPair->clearInContactReportActorPairSet(); + } + else + { + BodyPairKey pair; + PxU32 actorAID = aPair->getActorAID(); + PxU32 actorBID = aPair->getActorBID(); + pair.mSim0 = PxMin(actorAID, actorBID); + pair.mSim1 = PxMax(actorAID, actorBID); + + mActorPairMap.erase(pair); + destroyActorPairReport(*aPair); + } + } + + if(!shrinkToZero) + mContactReportActorPairSet.clear(); + else + mContactReportActorPairSet.reset(); +} + +void Sc::NPhaseCore::addToPersistentContactEventPairs(ShapeInteraction* si) +{ + // Pairs which request events which do not get triggered by the sdk and thus need to be tested actively every frame. + PX_ASSERT(si->getPairFlags() & (PxPairFlag::eNOTIFY_TOUCH_PERSISTS | ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS)); + PX_ASSERT(si->mReportPairIndex == INVALID_REPORT_PAIR_ID); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + PX_ASSERT(si->hasTouch()); // only pairs which can from now on lose or keep contact should be in this list + + si->raiseFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST); + if(mPersistentContactEventPairList.size() == mNextFramePersistentContactEventPairIndex) + { + si->mReportPairIndex = mPersistentContactEventPairList.size(); + mPersistentContactEventPairList.pushBack(si); + } + else + { + //swap with first entry that will be active next frame + ShapeInteraction* firstDelayedSi = mPersistentContactEventPairList[mNextFramePersistentContactEventPairIndex]; + firstDelayedSi->mReportPairIndex = mPersistentContactEventPairList.size(); + mPersistentContactEventPairList.pushBack(firstDelayedSi); + si->mReportPairIndex = mNextFramePersistentContactEventPairIndex; + mPersistentContactEventPairList[mNextFramePersistentContactEventPairIndex] = si; + } + + mNextFramePersistentContactEventPairIndex++; +} + +void Sc::NPhaseCore::addToPersistentContactEventPairsDelayed(ShapeInteraction* si) +{ + // Pairs which request events which do not get triggered by the sdk and thus need to be tested actively every frame. + PX_ASSERT(si->getPairFlags() & (PxPairFlag::eNOTIFY_TOUCH_PERSISTS | ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS)); + PX_ASSERT(si->mReportPairIndex == INVALID_REPORT_PAIR_ID); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + PX_ASSERT(si->hasTouch()); // only pairs which can from now on lose or keep contact should be in this list + + si->raiseFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST); + si->mReportPairIndex = mPersistentContactEventPairList.size(); + mPersistentContactEventPairList.pushBack(si); +} + +void Sc::NPhaseCore::removeFromPersistentContactEventPairs(ShapeInteraction* si) +{ + PX_ASSERT(si->getPairFlags() & (PxPairFlag::eNOTIFY_TOUCH_PERSISTS | ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS)); + PX_ASSERT(si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + PX_ASSERT(si->hasTouch()); // only pairs which could lose or keep contact should be in this list + + PxU32 index = si->mReportPairIndex; + PX_ASSERT(index != INVALID_REPORT_PAIR_ID); + + if(index < mNextFramePersistentContactEventPairIndex) + { + const PxU32 replaceIdx = mNextFramePersistentContactEventPairIndex - 1; + + if((mNextFramePersistentContactEventPairIndex < mPersistentContactEventPairList.size()) && (index != replaceIdx)) + { + // keep next frame persistent pairs at the back of the list + ShapeInteraction* tmp = mPersistentContactEventPairList[replaceIdx]; + mPersistentContactEventPairList[index] = tmp; + tmp->mReportPairIndex = index; + index = replaceIdx; + } + + mNextFramePersistentContactEventPairIndex--; + } + + si->clearFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST); + si->mReportPairIndex = INVALID_REPORT_PAIR_ID; + mPersistentContactEventPairList.replaceWithLast(index); + if(index < mPersistentContactEventPairList.size()) // Only adjust the index if the removed SIP was not at the end of the list + mPersistentContactEventPairList[index]->mReportPairIndex = index; +} + +void Sc::NPhaseCore::addToForceThresholdContactEventPairs(ShapeInteraction* si) +{ + PX_ASSERT(si->getPairFlags() & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS); + PX_ASSERT(si->mReportPairIndex == INVALID_REPORT_PAIR_ID); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + PX_ASSERT(si->hasTouch()); + + si->raiseFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST); + si->mReportPairIndex = mForceThresholdContactEventPairList.size(); + mForceThresholdContactEventPairList.pushBack(si); +} + +void Sc::NPhaseCore::removeFromForceThresholdContactEventPairs(ShapeInteraction* si) +{ + PX_ASSERT(si->getPairFlags() & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS); + PX_ASSERT(si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(si->hasTouch()); + + const PxU32 index = si->mReportPairIndex; + PX_ASSERT(index != INVALID_REPORT_PAIR_ID); + + si->clearFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST); + si->mReportPairIndex = INVALID_REPORT_PAIR_ID; + mForceThresholdContactEventPairList.replaceWithLast(index); + if(index < mForceThresholdContactEventPairList.size()) // Only adjust the index if the removed SIP was not at the end of the list + mForceThresholdContactEventPairList[index]->mReportPairIndex = index; +} + +PxU8* Sc::NPhaseCore::reserveContactReportPairData(PxU32 pairCount, PxU32 extraDataSize, PxU32& bufferIndex, + ContactReportAllocationManager* alloc) +{ + extraDataSize = Sc::ContactStreamManager::computeExtraDataBlockSize(extraDataSize); + return alloc ? alloc->allocate(extraDataSize + (pairCount * sizeof(Sc::ContactShapePair)), bufferIndex) : mContactReportBuffer.allocateNotThreadSafe(extraDataSize + (pairCount * sizeof(Sc::ContactShapePair)), bufferIndex); +} + +PxU8* Sc::NPhaseCore::resizeContactReportPairData(PxU32 pairCount, PxU32 extraDataSize, Sc::ContactStreamManager& csm) +{ + PX_ASSERT((pairCount > csm.maxPairCount) || (extraDataSize > csm.getMaxExtraDataSize())); + PX_ASSERT((csm.currentPairCount == csm.maxPairCount) || (extraDataSize > csm.getMaxExtraDataSize())); + PX_ASSERT(extraDataSize >= csm.getMaxExtraDataSize()); // we do not support stealing memory from the extra data part when the memory for pair info runs out + + PxU32 bufferIndex; + Ps::prefetch(mContactReportBuffer.getData(csm.bufferIndex)); + + extraDataSize = Sc::ContactStreamManager::computeExtraDataBlockSize(extraDataSize); + PxU8* stream = mContactReportBuffer.reallocateNotThreadSafe(extraDataSize + (pairCount * sizeof(Sc::ContactShapePair)), bufferIndex, 16, csm.bufferIndex); + PxU8* oldStream = mContactReportBuffer.getData(csm.bufferIndex); + if(stream) + { + const PxU32 maxExtraDataSize = csm.getMaxExtraDataSize(); + if(csm.bufferIndex != bufferIndex) + { + if(extraDataSize <= maxExtraDataSize) + PxMemCopy(stream, oldStream, maxExtraDataSize + (csm.currentPairCount * sizeof(Sc::ContactShapePair))); + else + { + PxMemCopy(stream, oldStream, csm.extraDataSize); + PxMemCopy(stream + extraDataSize, oldStream + maxExtraDataSize, csm.currentPairCount * sizeof(Sc::ContactShapePair)); + } + csm.bufferIndex = bufferIndex; + } + else if(extraDataSize > maxExtraDataSize) + PxMemMove(stream + extraDataSize, oldStream + maxExtraDataSize, csm.currentPairCount * sizeof(Sc::ContactShapePair)); + + if(pairCount > csm.maxPairCount) + csm.maxPairCount = Ps::to16(pairCount); + if(extraDataSize > maxExtraDataSize) + csm.setMaxExtraDataSize(extraDataSize); + } + return stream; +} + +Sc::ActorPairContactReportData* Sc::NPhaseCore::createActorPairContactReportData() +{ + Ps::Mutex::ScopedLock lock(mReportAllocLock); + return mActorPairContactReportDataPool.construct(); +} + +void Sc::NPhaseCore::releaseActorPairContactReportData(ActorPairContactReportData* data) +{ + mActorPairContactReportDataPool.destroy(data); +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h new file mode 100644 index 000000000..852975781 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h @@ -0,0 +1,390 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_NPHASE_CORE +#define PX_PHYSICS_SCP_NPHASE_CORE + +#include "CmPhysXCommon.h" +#include "CmRenderOutput.h" +#include "PxPhysXConfig.h" +#include "PsUserAllocated.h" +#include "PsMutex.h" +#include "PsAtomic.h" +#include "PsPool.h" +#include "PsHashSet.h" +#include "PsHashMap.h" +#include "PxSimulationEventCallback.h" +#include "ScTriggerPairs.h" +#include "ScScene.h" +#include "ScContactReportBuffer.h" +#include "PsHash.h" + +namespace physx +{ +namespace Bp +{ + struct AABBOverlap; + struct BroadPhasePair; +} + +namespace Sc +{ + class ActorSim; + class ElementSim; + class ShapeSim; + + class Interaction; + class ElementSimInteraction; + class ElementInteractionMarker; + class TriggerInteraction; + + class ShapeInteraction; + class ActorPair; + class ActorPairReport; + + class ActorPairContactReportData; + struct ContactShapePair; + + class NPhaseContext; + class ContactStreamManager; + + struct FilterPair; + class FilterPairManager; + + class RigidSim; + + struct PairReleaseFlag + { + enum Enum + { + eBP_VOLUME_REMOVED = (1 << 0), // the broadphase volume of one pair object has been removed. + eSHAPE_BP_VOLUME_REMOVED = (1 << 1) | eBP_VOLUME_REMOVED, // the removed broadphase volume was from a rigid body shape. + eWAKE_ON_LOST_TOUCH = (1 << 2) + }; + }; + + /* + Description: NPhaseCore encapsulates the near phase processing to allow multiple implementations(eg threading and non + threaded). + + The broadphase inserts shape pairs into the NPhaseCore, which are then processed into contact point streams. + Pairs can then be processed into AxisConstraints by the GroupSolveCore. + */ + + struct BodyPairKey + { + PxU32 mSim0; + PxU32 mSim1; + + PX_FORCE_INLINE bool operator == (const BodyPairKey& pair) const { return mSim0 == pair.mSim0 && mSim1 == pair.mSim1; } + }; + + PX_INLINE PxU32 hash(const BodyPairKey& key) + { + const PxU32 add0 = key.mSim0; + const PxU32 add1 = key.mSim1; + + const PxU32 base = PxU32((add0 & 0xFFFF) | (add1 << 16)); + + return physx::shdfnd::hash(base); + } + + struct ElementSimKey + { + ElementSim* mSim0, *mSim1; + + ElementSimKey() : mSim0(NULL), mSim1(NULL) + {} + + ElementSimKey(ElementSim* sim0, ElementSim* sim1) + { + if(sim0 > sim1) + Ps::swap(sim0, sim1); + mSim0 = sim0; + mSim1 = sim1; + } + + PX_FORCE_INLINE bool operator == (const ElementSimKey& pair) const { return mSim0 == pair.mSim0 && mSim1 == pair.mSim1; } + }; + + PX_INLINE PxU32 hash(const ElementSimKey& key) + { + PxU32 add0 = (size_t(key.mSim0)) & 0xFFFFFFFF; + PxU32 add1 = (size_t(key.mSim1)) & 0xFFFFFFFF; + + //Clear the lower 2 bits, they will be 0s anyway + add0 = add0 >> 2; + add1 = add1 >> 2; + + const PxU32 base = PxU32((add0 & 0xFFFF) | (add1 << 16)); + + return physx::shdfnd::hash(base); + } + + class ContactReportAllocationManager + { + PxU8* mBuffer; + PxU32 mBufferSize; + PxU32 mCurrentBufferIndex; + PxU32 mCurrentOffset; + ContactReportBuffer& mReportBuffer; + Ps::Mutex& mMutex; + const PxU32 mBuferBlockSize; + PX_NOCOPY(ContactReportAllocationManager) + public: + + ContactReportAllocationManager(ContactReportBuffer& buffer, Ps::Mutex& mutex, const PxU32 bufferBlockSize = 16384) : mBuffer(NULL), mBufferSize(0), mCurrentBufferIndex(0), + mCurrentOffset(0), mReportBuffer(buffer), mMutex(mutex), mBuferBlockSize(bufferBlockSize) + { + } + + PxU8* allocate(const PxU32 size, PxU32& index, PxU32 alignment = 16u) + { + //(1) fix up offsets... + PxU32 pad = ((mCurrentBufferIndex + alignment - 1)&~(alignment - 1)) - mCurrentBufferIndex; + PxU32 currOffset = mCurrentOffset + pad; + + if ((currOffset + size) > mBufferSize) + { + const PxU32 allocSize = PxMax(size, mBuferBlockSize); + + mMutex.lock(); + mBuffer = mReportBuffer.allocateNotThreadSafe(allocSize, mCurrentBufferIndex, alignment); + mCurrentOffset = currOffset = 0; + mBufferSize = allocSize; + mMutex.unlock(); + } + + PxU8* ret = mBuffer + currOffset; + index = mCurrentBufferIndex + currOffset; + mCurrentOffset = currOffset + size; + return ret; + } + }; + + class NPhaseCore : public Ps::UserAllocated + { + PX_NOCOPY(NPhaseCore) + + public: + NPhaseCore(Scene& scene, const PxSceneDesc& desc); + ~NPhaseCore(); + + void onOverlapCreated(const Bp::AABBOverlap* PX_RESTRICT pairs, PxU32 pairCount); + + void runOverlapFilters( PxU32 nbToProcess, const Bp::AABBOverlap* PX_RESTRICT pairs, PxFilterInfo* PX_RESTRICT filterInfo, + PxU32& nbToKeep, PxU32& nbToSuppress, PxU32& nbToCallback, PxU32* PX_RESTRICT keepMap, PxU32* PX_RESTRICT callbackMap); + + ElementSimInteraction* onOverlapRemovedStage1(ElementSim* volume0, ElementSim* volume1); + void onOverlapRemoved(ElementSim* volume0, ElementSim* volume1, const PxU32 ccdPass, void* elemSim, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + void onVolumeRemoved(ElementSim* volume, PxU32 flags, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + + void managerNewTouch(Sc::ShapeInteraction& interaction); + + PxU32 getDefaultContactReportStreamBufferSize() const; + + void fireCustomFilteringCallbacks(PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + + void addToDirtyInteractionList(Interaction* interaction); + void removeFromDirtyInteractionList(Interaction* interaction); + void updateDirtyInteractions(PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + + /* + Description: Perform trigger overlap tests. + */ + void processTriggerInteractions(PxBaseTask* continuation); + + /* + Description: Gather results from trigger overlap tests and clean up. + */ + void mergeProcessedTriggerInteractions(PxBaseTask* continuation); + + /* + Description: Check candidates for persistent touch contact events and create those events if necessary. + */ + void processPersistentContactEvents(PxsContactManagerOutputIterator& outputs, PxBaseTask* continuation); + + /* + Description: Displays visualizations associated with the near phase. + */ + void visualize(Cm::RenderOutput& out, PxsContactManagerOutputIterator& outputs); + + PX_FORCE_INLINE Scene& getScene() const { return mOwnerScene; } + + PX_FORCE_INLINE void addToContactReportActorPairSet(ActorPairReport* pair) { mContactReportActorPairSet.pushBack(pair); } + void clearContactReportActorPairs(bool shrinkToZero); + PX_FORCE_INLINE PxU32 getNbContactReportActorPairs() const { return mContactReportActorPairSet.size(); } + PX_FORCE_INLINE ActorPairReport* const* getContactReportActorPairs() const { return mContactReportActorPairSet.begin(); } + + void addToPersistentContactEventPairs(ShapeInteraction*); + void addToPersistentContactEventPairsDelayed(ShapeInteraction*); + void removeFromPersistentContactEventPairs(ShapeInteraction*); + PX_FORCE_INLINE PxU32 getCurrentPersistentContactEventPairCount() const { return mNextFramePersistentContactEventPairIndex; } + PX_FORCE_INLINE ShapeInteraction* const* getCurrentPersistentContactEventPairs() const { return mPersistentContactEventPairList.begin(); } + PX_FORCE_INLINE PxU32 getAllPersistentContactEventPairCount() const { return mPersistentContactEventPairList.size(); } + PX_FORCE_INLINE ShapeInteraction* const* getAllPersistentContactEventPairs() const { return mPersistentContactEventPairList.begin(); } + PX_FORCE_INLINE void preparePersistentContactEventListForNextFrame(); + + void addToForceThresholdContactEventPairs(ShapeInteraction*); + void removeFromForceThresholdContactEventPairs(ShapeInteraction*); + PX_FORCE_INLINE PxU32 getForceThresholdContactEventPairCount() const { return mForceThresholdContactEventPairList.size(); } + PX_FORCE_INLINE ShapeInteraction* const* getForceThresholdContactEventPairs() const { return mForceThresholdContactEventPairList.begin(); } + + PX_FORCE_INLINE PxU8* getContactReportPairData(const PxU32& bufferIndex) const { return mContactReportBuffer.getData(bufferIndex); } + PxU8* reserveContactReportPairData(PxU32 pairCount, PxU32 extraDataSize, PxU32& bufferIndex, ContactReportAllocationManager* alloc = NULL); + PxU8* resizeContactReportPairData(PxU32 pairCount, PxU32 extraDataSize, Sc::ContactStreamManager& csm); + PX_FORCE_INLINE void clearContactReportStream() { mContactReportBuffer.reset(); } // Do not free memory at all + PX_FORCE_INLINE void freeContactReportStreamMemory() { mContactReportBuffer.flush(); } + + ActorPairContactReportData* createActorPairContactReportData(); + void releaseActorPairContactReportData(ActorPairContactReportData* data); + + void registerInteraction(ElementSimInteraction* interaction); + void unregisterInteraction(ElementSimInteraction* interaction); + + ElementSimInteraction* createRbElementInteraction(const PxFilterInfo& fInfo, ShapeSim& s0, ShapeSim& s1, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction, + Sc::ElementInteractionMarker* interactionMarker, bool isTriggerPair); + + private: + ElementSimInteraction* createRbElementInteraction(ShapeSim& s0, ShapeSim& s1, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction, + Sc::ElementInteractionMarker* interactionMarker); + + void releaseElementPair(ElementSimInteraction* pair, PxU32 flags, const PxU32 ccdPass, bool removeFromDirtyList, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + void lostTouchReports(ShapeInteraction* pair, PxU32 flags, const PxU32 ccdPass, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + + ShapeInteraction* createShapeInteraction(ShapeSim& s0, ShapeSim& s1, PxPairFlags pairFlags, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction); + TriggerInteraction* createTriggerInteraction(ShapeSim& s0, ShapeSim& s1, PxPairFlags triggerFlags); + ElementInteractionMarker* createElementInteractionMarker(ElementSim& e0, ElementSim& e1, ElementInteractionMarker* marker); + + //------------- Filtering ------------- + + ElementSimInteraction* refilterInteraction(ElementSimInteraction* pair, const PxFilterInfo* filterInfo, bool removeFromDirtyList, PxsContactManagerOutputIterator& outputs, + bool useAdaptiveForce); + //------------------------------------- + + ElementSimInteraction* convert(ElementSimInteraction* pair, InteractionType::Enum type, PxFilterInfo& filterInfo, bool removeFromDirtyList, PxsContactManagerOutputIterator& outputs, + bool useAdaptiveForce); + + ActorPair* findActorPair(ShapeSim* s0, ShapeSim* s1, Ps::IntBool isReportPair); + PX_FORCE_INLINE void destroyActorPairReport(ActorPairReport&); + + Sc::ElementSimInteraction* findInteraction(ElementSim* _element0, ElementSim* _element1); + + // Pooling + Scene& mOwnerScene; + + Ps::Array mContactReportActorPairSet; + Ps::Array mPersistentContactEventPairList; // Pairs which request events which do not get triggered by the sdk and thus need to be tested actively every frame. + // May also contain force threshold event pairs (see mForceThresholdContactEventPairList) + // This list is split in two, the elements in front are for the current frame, the elements at the + // back will get added next frame. + PxU32 mNextFramePersistentContactEventPairIndex; // start index of the pairs which need to get added to the persistent list for next frame + + Ps::Array mForceThresholdContactEventPairList; // Pairs which request force threshold contact events. A pair is only in this list if it does have contact. + // Note: If a pair additionally requests PxPairFlag::eNOTIFY_TOUCH_PERSISTS events, then it + // goes into mPersistentContactEventPairList instead. This allows to share the list index. + // + // data layout: + // ContactActorPair0_ExtraData, ContactShapePair0_0, ContactShapePair0_1, ... ContactShapePair0_N, + // ContactActorPair1_ExtraData, ContactShapePair1_0, ... + // + ContactReportBuffer mContactReportBuffer; // Shape pair information for contact reports + + Ps::CoalescedHashSet mDirtyInteractions; + FilterPairManager* mFilterPairManager; + + // Pools + Ps::Pool mActorPairPool; + Ps::Pool mActorPairReportPool; + Ps::Pool mShapeInteractionPool; + Ps::Pool mTriggerInteractionPool; + Ps::Pool mActorPairContactReportDataPool; + Ps::Pool mInteractionMarkerPool; + + Cm::DelegateTask mMergeProcessedTriggerInteractions; + void* mTmpTriggerProcessingBlock; // temporary memory block to process trigger pairs in parallel + Ps::Mutex mTriggerWriteBackLock; + volatile PxI32 mTriggerPairsToDeactivateCount; + Ps::HashMap mActorPairMap; + + Ps::HashMap mElementSimMap; + + Ps::Mutex mBufferAllocLock; + Ps::Mutex mReportAllocLock; + + friend class Sc::Scene; + friend class Sc::ShapeInteraction; + }; + + struct FilteringContext + { + FilteringContext(const Sc::Scene& scene, FilterPairManager* filterPairManager) : + mFilterShader (scene.getFilterShaderFast()), + mFilterShaderData (scene.getFilterShaderDataFast()), + mFilterShaderDataSize (scene.getFilterShaderDataSizeFast()), + mFilterCallback (scene.getFilterCallbackFast()), + mFilterPairManager (filterPairManager), + mSceneFlags (scene.getPublicFlags()) + { + const PxPairFilteringMode::Enum kineKineFilteringMode = scene.getKineKineFilteringMode(); + if(kineKineFilteringMode==PxPairFilteringMode::eKEEP) + mSceneFlags |= PxSceneFlag::eENABLE_KINEMATIC_PAIRS; + else if(kineKineFilteringMode==PxPairFilteringMode::eSUPPRESS) + mSceneFlags &= ~PxSceneFlag::eENABLE_KINEMATIC_PAIRS; + + const PxPairFilteringMode::Enum staticKineFilteringMode = scene.getStaticKineFilteringMode(); + if(staticKineFilteringMode==PxPairFilteringMode::eKEEP) + mSceneFlags |= PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS; + else if(staticKineFilteringMode==PxPairFilteringMode::eSUPPRESS) + mSceneFlags &= ~PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS; + } + + PxSimulationFilterShader mFilterShader; + const void* mFilterShaderData; + PxU32 mFilterShaderDataSize; + PxSimulationFilterCallback* mFilterCallback; + FilterPairManager* mFilterPairManager; + PxSceneFlags mSceneFlags; + }; + + // helper function to run the filter logic after some hardwired filter criteria have been passed successfully + PxFilterInfo filterRbCollisionPairSecondStage(const FilteringContext& context, const ShapeSim& s0, const ShapeSim& s1, const Sc::BodySim* b0, const Sc::BodySim* b1, PxU32 filterPairIndex, bool runCallbacks); + +} // namespace Sc + + +PX_FORCE_INLINE void Sc::NPhaseCore::preparePersistentContactEventListForNextFrame() +{ + // reports have been processed -> "activate" next frame candidates for persistent contact events + mNextFramePersistentContactEventPairIndex = mPersistentContactEventPairList.size(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h b/src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h new file mode 100644 index 000000000..9f3b43add --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SC_OBJECT_ID_TRACKER +#define PX_PHYSICS_SC_OBJECT_ID_TRACKER + +#include "CmPhysXCommon.h" +#include "CmIDPool.h" +#include "CmBitMap.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Sc +{ + // PT: TODO: this has no direct dependency on "Sc". It should really be a "Cm" class. + class ObjectIDTracker : public Ps::UserAllocated + { + PX_NOCOPY(ObjectIDTracker) + public: + ObjectIDTracker() : mPendingReleasedIDs(PX_DEBUG_EXP("objectIDTrackerIDs")) {} + + PX_INLINE PxU32 createID() { return mIDPool.getNewID(); } + PX_INLINE void releaseID(PxU32 id) + { + markIDAsDeleted(id); + mPendingReleasedIDs.pushBack(id); + } + PX_INLINE Ps::IntBool isDeletedID(PxU32 id) const { return mDeletedIDsMap.boundedTest(id); } + PX_FORCE_INLINE PxU32 getDeletedIDCount() const { return mPendingReleasedIDs.size(); } + PX_INLINE void clearDeletedIDMap() { mDeletedIDsMap.clear(); } + PX_INLINE void resizeDeletedIDMap(PxU32 id, PxU32 numIds) + { + mDeletedIDsMap.resize(id); + mPendingReleasedIDs.reserve(numIds); + } + PX_INLINE void processPendingReleases() + { + for(PxU32 i=0; i < mPendingReleasedIDs.size(); i++) + { + mIDPool.freeID(mPendingReleasedIDs[i]); + } + mPendingReleasedIDs.clear(); + } + PX_INLINE void reset() + { + processPendingReleases(); + mPendingReleasedIDs.reset(); + + // Don't free stuff in IDPool, we still need the list of free IDs + + // And it does not seem worth freeing the memory of the bitmap + } + + PX_INLINE PxU32 getMaxID() + { + return mIDPool.getMaxID(); + } + private: + PX_INLINE void markIDAsDeleted(PxU32 id) { PX_ASSERT(!isDeletedID(id)); mDeletedIDsMap.growAndSet(id); } + + + private: + Cm::IDPool mIDPool; + Cm::BitMap mDeletedIDsMap; + Ps::Array mPendingReleasedIDs; // Buffer for released IDs to make sure newly created objects do not re-use these IDs immediately + }; + +} +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp new file mode 100644 index 000000000..7fd2884de --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp @@ -0,0 +1,61 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScPhysics.h" +#include "ScScene.h" +#include "PxvGlobals.h" +#include "PxTolerancesScale.h" + +using namespace physx; + +Sc::Physics* Sc::Physics::mInstance = NULL; +const PxReal Sc::Physics::sWakeCounterOnCreation = 20.0f*0.02f; + +namespace physx +{ + namespace Sc + { + OffsetTable gOffsetTable; + } +} + +Sc::Physics::Physics(const PxTolerancesScale& scale, const PxvOffsetTable& pxvOffsetTable) +: mScale(scale) +{ + mInstance = this; + PxvInit(pxvOffsetTable); +} + + +Sc::Physics::~Physics() +{ + PxvTerm(); + mInstance = 0; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp new file mode 100644 index 000000000..03f6672e7 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp @@ -0,0 +1,125 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScBodyCore.h" +#include "ScStaticCore.h" +#include "ScRigidSim.h" +#include "ScShapeSim.h" +#include "ScScene.h" +#include "ScPhysics.h" + +using namespace physx; +using namespace Sc; + +static ShapeSim& getSimForShape(ShapeCore& core, const ActorSim& actorSim) +{ + const ElementSim* current = actorSim.getElements_(); + while(current) + { + const ShapeSim* sim = static_cast(current); + if(&sim->getCore() == &core) + return *const_cast(sim); + current = current->mNextInActor; + } + + PX_ASSERT(0); // should never fail + return *reinterpret_cast(1); +} + +RigidCore::RigidCore(const PxActorType::Enum type) +: ActorCore(type, PxActorFlag::eVISUALIZATION, PX_DEFAULT_CLIENT, 0) +{ +} + +RigidCore::~RigidCore() +{ +} + +void RigidCore::addShapeToScene(ShapeCore& shapeCore) +{ + RigidSim* sim = getSim(); + PX_ASSERT(sim); + if(!sim) + return; + sim->getScene().addShape(*sim, shapeCore, NULL); +} + +void RigidCore::removeShapeFromScene(ShapeCore& shapeCore, bool wakeOnLostTouch) +{ + RigidSim* sim = getSim(); + if(!sim) + return; + ShapeSim& s = getSimForShape(shapeCore, *sim); + sim->getScene().removeShape(s, wakeOnLostTouch); +} + +void RigidCore::onShapeChange(ShapeCore& shape, ShapeChangeNotifyFlags notifyFlags, PxShapeFlags oldShapeFlags, bool forceBoundsUpdate) +{ + // DS: We pass flags to avoid searching multiple times or exposing RigidSim outside SC, and this form is + // more convenient for the Scb::Shape::syncState method. If we start hitting this a lot we should do it + // a different way, but shape modification after insertion is rare. + + RigidSim* sim = getSim(); + if(!sim) + return; + ShapeSim& s = getSimForShape(shape, *sim); + + if(notifyFlags & ShapeChangeNotifyFlag::eGEOMETRY) + s.onVolumeOrTransformChange(forceBoundsUpdate); + if(notifyFlags & ShapeChangeNotifyFlag::eMATERIAL) + s.onMaterialChange(); + if(notifyFlags & ShapeChangeNotifyFlag::eRESET_FILTERING) + s.onResetFiltering(); + if(notifyFlags & ShapeChangeNotifyFlag::eSHAPE2BODY) + s.onVolumeOrTransformChange(forceBoundsUpdate); + if(notifyFlags & ShapeChangeNotifyFlag::eFILTERDATA) + s.onFilterDataChange(); + if(notifyFlags & ShapeChangeNotifyFlag::eFLAGS) + s.onFlagChange(oldShapeFlags); + if(notifyFlags & ShapeChangeNotifyFlag::eCONTACTOFFSET) + s.onContactOffsetChange(); + if(notifyFlags & ShapeChangeNotifyFlag::eRESTOFFSET) + s.onRestOffsetChange(); +} + +RigidSim* RigidCore::getSim() const +{ + return static_cast(ActorCore::getSim()); +} + +PxU32 RigidCore::getRigidID() const +{ + return static_cast(ActorCore::getSim())->getRigidID(); +} + +PxActor* RigidCore::getPxActor() const +{ + return Ps::pointerOffset(const_cast(this), gOffsetTable.scCore2PxActor[getActorCoreType()]); +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp new file mode 100644 index 000000000..2ffeda1aa --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp @@ -0,0 +1,88 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScScene.h" +#include "ScRigidSim.h" +#include "ScShapeSim.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Sc; + +/* + PT: + + The BP group ID comes from a Cm::IDPool, and RigidSim is the only class releasing the ID. + + The rigid tracker ID comes from a Cm::IDPool internal to an ObjectIDTracker, and RigidSim + is the only class using it. + + Thus we should: + - promote the BP group ID stuff to a "tracker" object + - use the BP group ID as a rigid ID +*/ + +RigidSim::RigidSim(Scene& scene, RigidCore& core) : ActorSim(scene, core) +{ + mRigidId = scene.getRigidIDTracker().createID(); +} + +RigidSim::~RigidSim() +{ + Scene& scScene = getScene(); + scScene.getRigidIDTracker().releaseID(mRigidId); +} + +void notifyActorInteractionsOfTransformChange(ActorSim& actor); +void RigidSim::notifyShapesOfTransformChange() +{ + if(0) + { + ElementSim* current = getElements_(); + while(current) + { + ShapeSim* sim = static_cast(current); + sim->onVolumeOrTransformChange(false); + current = current->mNextInActor; + } + } + else + { + ElementSim* current = getElements_(); + while(current) + { + ShapeSim* sim = static_cast(current); + sim->markBoundsForUpdate(false); + current = current->mNextInActor; + } + + notifyActorInteractionsOfTransformChange(*this); + } +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h new file mode 100644 index 000000000..830b80973 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_RB_SIM +#define PX_PHYSICS_SCP_RB_SIM + +#include "ScActorSim.h" +#include "ScRigidCore.h" + +namespace physx +{ +namespace Sc +{ + class Scene; + + class RigidSim : public ActorSim + { + public: + RigidSim(Scene&, RigidCore&); + virtual ~RigidSim(); + + PX_FORCE_INLINE RigidCore& getRigidCore() const { return static_cast(mCore); } + + PX_FORCE_INLINE PxU32 getRigidID() const { return mRigidId; } + + void notifyShapesOfTransformChange(); + + PxActor* getPxActor() const { return getRigidCore().getPxActor(); } + private: + PxU32 mRigidId; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp new file mode 100644 index 000000000..fc554f6e9 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp @@ -0,0 +1,6223 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#define NOMINMAX + +#include "common/PxProfileZone.h" +#include "ScPhysics.h" +#include "ScScene.h" +#include "ScClient.h" +#include "BpAABBManager.h" +#include "BpBroadPhase.h" +#include "ScStaticSim.h" +#include "ScConstraintSim.h" +#include "ScConstraintProjectionManager.h" +#include "ScConstraintCore.h" +#include "ScArticulationCore.h" +#include "ScArticulationJointCore.h" +#include "ScMaterialCore.h" +#include "ScArticulationSim.h" +#include "ScArticulationJointSim.h" +#include "PsTime.h" +#include "ScConstraintInteraction.h" +#include "ScTriggerInteraction.h" +#include "ScSimStats.h" +#include "ScTriggerPairs.h" +#include "ScObjectIDTracker.h" +#include "DyArticulation.h" +#include "PxvManager.h" +#include "PxvGlobals.h" +#include "DyContext.h" +#include "PxsCCD.h" +#include "PxsSimpleIslandManager.h" +#include "PxsSimulationController.h" +#include "PxsContext.h" +#include "ScSqBoundsManager.h" +#include "ScElementSim.h" + + +#if defined(__APPLE__) && defined(__POWERPC__) +#include +#endif + +#if PX_SUPPORT_GPU_PHYSX +#include "PxPhysXGpu.h" +#include "PxsKernelWrangler.h" +#include "PxsHeapMemoryAllocator.h" +#include "cudamanager/PxCudaContextManager.h" +#endif + +#include "PxsMemoryManager.h" + +//////////// + +#include "PxvNphaseImplementationContext.h" +#include "ScShapeInteraction.h" +#include "ScElementInteractionMarker.h" +#include "PxsContext.h" + +#include "PxRigidDynamic.h" + +#include "PxvDynamics.h" +#include "DyArticulation.h" +#include "DyFeatherstoneArticulation.h" + +using namespace physx; +using namespace physx::shdfnd; +using namespace physx::Cm; +using namespace physx::Dy; + +// slightly ugly, but we don't want a compile-time dependency on DY_ARTICULATION_MAX_SIZE in the ScScene.h header +namespace physx { +#if PX_SUPPORT_GPU_PHYSX + +PX_PHYSX_GPU_API Bp::BPMemoryAllocator* createGpuMemoryAllocator(); + +#endif + +namespace Sc { + +class LLArticulationPool: public Ps::Pool > +{ +public: + LLArticulationPool() {} +}; + +class LLArticulationRCPool : public Ps::Pool > +{ +public: + LLArticulationRCPool() {} +}; + + +static const char* sFilterShaderDataMemAllocId = "SceneDesc filterShaderData"; + +}} + +void PxcClearContactCacheStats(); +void PxcDisplayContactCacheStats(); + +class ScAfterIntegrationTask : public Cm::Task +{ +public: + static const PxU32 MaxTasks = 256; +private: + const IG::NodeIndex* const mIndices; + const PxU32 mNumBodies; + PxsContext* mContext; + Context* mDynamicsContext; + PxsTransformCache& mCache; + Sc::Scene& mScene; + +public: + + ScAfterIntegrationTask(const IG::NodeIndex* const indices, PxU32 numBodies, PxsContext* context, Context* dynamicsContext, PxsTransformCache& cache, Sc::Scene& scene) : + Cm::Task (scene.getContextId()), + mIndices (indices), + mNumBodies (numBodies), + mContext (context), + mDynamicsContext(dynamicsContext), + mCache (cache), + mScene (scene) + { + } + + virtual void runInternal() + { + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + Sc::BodySim* bpUpdates[MaxTasks]; + Sc::BodySim* ccdBodies[MaxTasks]; + Sc::BodySim* activateBodies[MaxTasks]; + Sc::BodySim* deactivateBodies[MaxTasks]; + PxU32 nbBpUpdates = 0, nbCcdBodies = 0; + + IG::SimpleIslandManager& manager = *mScene.getSimpleIslandManager(); + const IG::IslandSim& islandSim = manager.getAccurateIslandSim(); + Bp::BoundsArray& boundsArray = mScene.getBoundsArray(); + + Sc::BodySim* frozen[MaxTasks], * unfrozen[MaxTasks]; + PxU32 nbFrozen = 0, nbUnfrozen = 0; + PxU32 nbActivated = 0, nbDeactivated = 0; + + for(PxU32 i = 0; i < mNumBodies; i++) + { + PxsRigidBody* rigid = islandSim.getRigidBody(mIndices[i]); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(rigid) - rigidBodyOffset); + //This move to PxgPostSolveWorkerTask for the gpu dynamic + //bodySim->sleepCheck(mDt, mOneOverDt, mEnableStabilization); + + PxsBodyCore& bodyCore = bodySim->getBodyCore().getCore(); + //If we got in this code, then this is an active object this frame. The solver computed the new wakeCounter and we + //commit it at this stage. We need to do it this way to avoid a race condition between the solver and the island gen, where + //the island gen may have deactivated a body while the solver decided to change its wake counter. + bodyCore.wakeCounter = bodyCore.solverWakeCounter; + PxsRigidBody& llBody = bodySim->getLowLevelBody(); + + const Ps::IntBool isFrozen = bodySim->isFrozen(); + if(!isFrozen) + { + bpUpdates[nbBpUpdates++] = bodySim; + + // PT: TODO: remove duplicate "isFrozen" test inside updateCached +// bodySim->updateCached(NULL); + bodySim->updateCached(mCache, boundsArray); + } + + if(llBody.isFreezeThisFrame() && isFrozen) + { + frozen[nbFrozen++] = bodySim; + } + else if(llBody.isUnfreezeThisFrame()) + { + unfrozen[nbUnfrozen++] = bodySim; + } + + if(bodyCore.mFlags & PxRigidBodyFlag::eENABLE_CCD) + ccdBodies[nbCcdBodies++] = bodySim; + + if(llBody.isActivateThisFrame()) + { + PX_ASSERT(!llBody.isDeactivateThisFrame()); + activateBodies[nbActivated++] = bodySim; + } + else if(llBody.isDeactivateThisFrame()) + { + deactivateBodies[nbDeactivated++] = bodySim; + } + llBody.clearAllFrameFlags(); + } + if(nbBpUpdates) + { + mCache.setChangedState(); + boundsArray.setChangedState(); + } + + if(nbBpUpdates>0 || nbFrozen > 0 || nbCcdBodies>0 || nbActivated>0 || nbDeactivated>0) + { + //Write active bodies to changed actor map + mContext->getLock().lock(); + Cm::BitMapPinned& changedAABBMgrHandles = mScene.getAABBManager()->getChangedAABBMgActorHandleMap(); + + for(PxU32 i = 0; i < nbBpUpdates; i++) + { + Sc::ElementSim* current = bpUpdates[i]->getElements_(); + while(current) + { + Sc::ShapeSim* sim = static_cast(current); + // PT: TODO: what's the difference between this test and "isInBroadphase" as used in bodySim->updateCached ? + // PT: Also, shouldn't it be "isInAABBManager" rather than BP ? + if(sim->getFlags()&PxU32(PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) // TODO: need trigger shape here? + changedAABBMgrHandles.growAndSet(sim->getElementID()); + current = current->mNextInActor; + } + } + + Ps::Array& sceneCcdBodies = mScene.getCcdBodies(); + for (PxU32 i = 0; i < nbCcdBodies; i++) + sceneCcdBodies.pushBack(ccdBodies[i]); + + for(PxU32 i=0;iisFrozen()); + frozen[i]->freezeTransforms(&changedAABBMgrHandles); + } + + for(PxU32 i=0;iisFrozen()); + unfrozen[i]->createSqBounds(); + } + + for(PxU32 i = 0; i < nbActivated; ++i) + { + activateBodies[i]->notifyNotReadyForSleeping(); + } + + for(PxU32 i = 0; i < nbDeactivated; ++i) + { + deactivateBodies[i]->notifyReadyForSleeping(); + } + + mContext->getLock().unlock(); + } + } + + virtual const char* getName() const + { + return "ScScene.afterIntegrationTask"; + } + +private: + ScAfterIntegrationTask& operator = (const ScAfterIntegrationTask&); +}; + +class ScSimulationControllerCallback : public PxsSimulationControllerCallback +{ + Sc::Scene* mScene; +public: + + ScSimulationControllerCallback(Sc::Scene* scene) : mScene(scene) + { + } + + virtual void updateScBodyAndShapeSim(PxBaseTask* continuation) + { + PxsContext* contextLL = mScene->getLowLevelContext(); + IG::SimpleIslandManager* islandManager = mScene->getSimpleIslandManager(); + Dy::Context* dynamicContext = mScene->getDynamicsContext(); + + Cm::FlushPool& flushPool = contextLL->getTaskPool(); + + const PxU32 MaxBodiesPerTask = ScAfterIntegrationTask::MaxTasks; + + PxsTransformCache& cache = contextLL->getTransformCache(); + + const IG::IslandSim& islandSim = islandManager->getAccurateIslandSim(); + + /*const*/ PxU32 numBodies = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + const IG::NodeIndex*const nodeIndices = islandSim.getActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + if(1) + { + PxU32 nbShapes = 0; + PxU32 startIdx = 0; + for (PxU32 i = 0; i < numBodies; i++) + { + if (nbShapes >= MaxBodiesPerTask) + { + ScAfterIntegrationTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScAfterIntegrationTask)), ScAfterIntegrationTask(nodeIndices + startIdx, i - startIdx, + contextLL, dynamicContext, cache, *mScene)); + task->setContinuation(continuation); + task->removeReference(); + startIdx = i; + nbShapes = 0; + } + + PxsRigidBody* rigid = islandSim.getRigidBody(nodeIndices[i]); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(rigid) - rigidBodyOffset); + nbShapes += PxMax(1u, bodySim->getNbShapes()); //Always add at least 1 shape in, even if the body has zero shapes because there is still some per-body overhead + } + + if (nbShapes) + { + ScAfterIntegrationTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScAfterIntegrationTask)), ScAfterIntegrationTask(nodeIndices + startIdx, numBodies - startIdx, + contextLL, dynamicContext, cache, *mScene)); + task->setContinuation(continuation); + task->removeReference(); + } + } + else + { + // PT: + const PxU32 numCpuTasks = continuation->getTaskManager()->getCpuDispatcher()->getWorkerCount(); + + PxU32 nbPerTask; + if(numCpuTasks) + { + nbPerTask = numBodies > numCpuTasks ? numBodies / numCpuTasks : numBodies; + } + else + { + nbPerTask = numBodies; + } + + // PT: we need to respect that limit even with a single thread, because of hardcoded buffer limits in ScAfterIntegrationTask. + if(nbPerTask>MaxBodiesPerTask) + nbPerTask = MaxBodiesPerTask; + + PxU32 start = 0; + while(numBodies) + { + const PxU32 nb = numBodies < nbPerTask ? numBodies : nbPerTask; + + ScAfterIntegrationTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScAfterIntegrationTask)), ScAfterIntegrationTask(nodeIndices+start, nb, + contextLL, dynamicContext, cache, *mScene)); + + start += nb; + numBodies -= nb; + + task->setContinuation(continuation); + task->removeReference(); + } + } + } + + virtual PxU32 getNbCcdBodies() + { + return mScene->getCcdBodies().size(); + } +}; + +class PxgUpdateBodyAndShapeStatusTask : public Cm::Task +{ +public: + static const PxU32 MaxTasks = 256; +private: + const IG::NodeIndex* const mNodeIndices; + const PxU32 mNumBodies; + Sc::Scene& mScene; + void** mRigidBodyLL; + PxU32* mActivatedBodies; + PxU32* mDeactivatedBodies; + PxI32& mCCDBodyWriteIndex; + +public: + + PxgUpdateBodyAndShapeStatusTask(const IG::NodeIndex* const indices, PxU32 numBodies, void** rigidBodyLL, PxU32* activatedBodies, PxU32* deactivatedBodies, Sc::Scene& scene, PxI32& ccdBodyWriteIndex) : + Cm::Task (scene.getContextId()), + mNodeIndices (indices), + mNumBodies (numBodies), + mScene (scene), + mRigidBodyLL (rigidBodyLL), + mActivatedBodies (activatedBodies), + mDeactivatedBodies (deactivatedBodies), + mCCDBodyWriteIndex (ccdBodyWriteIndex) + { + } + + virtual void runInternal() + { + IG::SimpleIslandManager& islandManager = *mScene.getSimpleIslandManager(); + const IG::IslandSim& islandSim = islandManager.getAccurateIslandSim(); + + PxU32 nbCcdBodies = 0; + + Ps::Array& sceneCcdBodies = mScene.getCcdBodies(); + Sc::BodySim* ccdBodies[MaxTasks]; + + const size_t bodyOffset = PX_OFFSET_OF_RT(Sc::BodySim, getLowLevelBody()); + + for(PxU32 i=0; i(mRigidBodyLL[nodeIndex]); + + PxsBodyCore* bodyCore = &rigidLL->getCore(); + bodyCore->wakeCounter = bodyCore->solverWakeCounter; + //we can set the frozen/unfrozen flag in GPU, but we have copied the internalflags + //from the solverbodysleepdata to pxsbodycore, so we just need to clear the frozen flag in here + rigidLL->clearAllFrameFlags(); + + PX_ASSERT(mActivatedBodies[nodeIndex] <= 1); + PX_ASSERT(mDeactivatedBodies[nodeIndex] <= 1); + if(mActivatedBodies[nodeIndex]) + { + PX_ASSERT(bodyCore->wakeCounter > 0.0f); + islandManager.activateNode(mNodeIndices[i]); + } + else if(mDeactivatedBodies[nodeIndex]) + { + //KS - the CPU code can reset the wake counter due to lost touches in parallel with the solver, so we need to verify + //that the wakeCounter is still 0 before deactivating the node + if(bodyCore->wakeCounter == 0.0f) + islandManager.deactivateNode(mNodeIndices[i]); + } + + if (bodyCore->mFlags & PxRigidBodyFlag::eENABLE_CCD) + { + PxsRigidBody* rigidBody = islandSim.getRigidBody(mNodeIndices[i]); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(rigidBody) - bodyOffset); + ccdBodies[nbCcdBodies++] = bodySim; + } + } + if(nbCcdBodies > 0) + { + PxI32 startIndex = Ps::atomicAdd(&mCCDBodyWriteIndex, PxI32(nbCcdBodies)) - PxI32(nbCcdBodies); + for(PxU32 a = 0; a < nbCcdBodies; ++a) + { + sceneCcdBodies[startIndex + a] = ccdBodies[a]; + } + } + } + + virtual const char* getName() const + { + return "ScScene.PxgUpdateBodyAndShapeStatusTask"; + } + +private: + PxgUpdateBodyAndShapeStatusTask& operator = (const PxgUpdateBodyAndShapeStatusTask&); +}; + +class PxgSimulationControllerCallback : public PxsSimulationControllerCallback +{ + Sc::Scene* mScene; + PxI32 mCcdBodyWriteIndex; + +public: + PxgSimulationControllerCallback(Sc::Scene* scene) : mScene(scene), mCcdBodyWriteIndex(0) + { + } + + virtual void updateScBodyAndShapeSim(PxBaseTask* continuation) + { + IG::SimpleIslandManager* islandManager = mScene->getSimpleIslandManager(); + PxsSimulationController* simulationController = mScene->getSimulationController(); + PxsContext* contextLL = mScene->getLowLevelContext(); + IG::IslandSim& islandSim = islandManager->getAccurateIslandSim(); + const PxU32 numBodies = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + const IG::NodeIndex*const nodeIndices = islandSim.getActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + PxU32* activatedBodies = simulationController->getActiveBodies(); + PxU32* deactivatedBodies = simulationController->getDeactiveBodies(); + + //PxsRigidBody** rigidBodyLL = simulationController->getRigidBodies(); + void** rigidBodyLL = simulationController->getRigidBodies(); + + Cm::FlushPool& flushPool = contextLL->getTaskPool(); + + Ps::Array& ccdBodies = mScene->getCcdBodies(); + ccdBodies.forceSize_Unsafe(0); + ccdBodies.reserve(numBodies); + ccdBodies.forceSize_Unsafe(numBodies); + + mCcdBodyWriteIndex = 0; + + for(PxU32 i = 0; i < numBodies; i+=PxgUpdateBodyAndShapeStatusTask::MaxTasks) + { + PxgUpdateBodyAndShapeStatusTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(PxgUpdateBodyAndShapeStatusTask)), PxgUpdateBodyAndShapeStatusTask(nodeIndices + i, + PxMin(PxgUpdateBodyAndShapeStatusTask::MaxTasks, numBodies - i), rigidBodyLL, activatedBodies, deactivatedBodies, *mScene, mCcdBodyWriteIndex)); + task->setContinuation(continuation); + task->removeReference(); + } + + PxU32* unfrozenShapeIndices = simulationController->getUnfrozenShapes(); + PxU32* frozenShapeIndices = simulationController->getFrozenShapes(); + const PxU32 nbFrozenShapes = simulationController->getNbFrozenShapes(); + const PxU32 nbUnfrozenShapes = simulationController->getNbUnfrozenShapes(); + + PxsShapeSim** shapeSimsLL = simulationController->getShapeSims(); + + const size_t shapeOffset = PX_OFFSET_OF_RT(Sc::ShapeSim, getLLShapeSim()); + + for(PxU32 i=0; i(reinterpret_cast(shapeLL) - shapeOffset); + shape->destroySqBounds(); + } + + for(PxU32 i=0; i(reinterpret_cast(shapeLL) - shapeOffset); + shape->createSqBounds(); + } + } + + virtual PxU32 getNbCcdBodies() + { + return PxU32(mCcdBodyWriteIndex); + } +}; + +Sc::Scene::Scene(const PxSceneDesc& desc, PxU64 contextID) : + mContextId (contextID), + mActiveBodies (PX_DEBUG_EXP("sceneActiveBodies")), + mActiveKinematicBodyCount (0), + mPointerBlock8Pool (PX_DEBUG_EXP("scenePointerBlock8Pool")), + mPointerBlock16Pool (PX_DEBUG_EXP("scenePointerBlock16Pool")), + mPointerBlock32Pool (PX_DEBUG_EXP("scenePointerBlock32Pool")), + mLLContext (0), + mBodyGravityDirty (true), + mDt (0), + mOneOverDt (0), + mTimeStamp (1), // PT: has to start to 1 to fix determinism bug. I don't know why yet but it works. + mReportShapePairTimeStamp (0), + mTriggerBufferAPI (PX_DEBUG_EXP("sceneTriggerBufferAPI")), + mRemovedShapeCountAtSimStart (0), + mArticulations (PX_DEBUG_EXP("sceneArticulations")), + mBrokenConstraints (PX_DEBUG_EXP("sceneBrokenConstraints")), + mActiveBreakableConstraints (PX_DEBUG_EXP("sceneActiveBreakableConstraints")), + mMemBlock128Pool (PX_DEBUG_EXP("PxsContext ConstraintBlock128Pool")), + mMemBlock256Pool (PX_DEBUG_EXP("PxsContext ConstraintBlock256Pool")), + mMemBlock384Pool (PX_DEBUG_EXP("PxsContext ConstraintBlock384Pool")), + mNPhaseCore (NULL), + mKineKineFilteringMode (desc.kineKineFilteringMode), + mStaticKineFilteringMode (desc.staticKineFilteringMode), + mSleepBodies (PX_DEBUG_EXP("sceneSleepBodies")), + mWokeBodies (PX_DEBUG_EXP("sceneWokeBodies")), + mEnableStabilization (desc.flags & PxSceneFlag::eENABLE_STABILIZATION), + mClients (PX_DEBUG_EXP("sceneClients")), + mActiveActors (PX_DEBUG_EXP("clientActiveActors")), + mFrozenActors (PX_DEBUG_EXP("clientFrozenActors")), + mClientPosePreviewBodies (PX_DEBUG_EXP("clientPosePreviewBodies")), + mClientPosePreviewBuffer (PX_DEBUG_EXP("clientPosePreviewBuffer")), + mSimulationEventCallback (NULL), + mBroadPhaseCallback (NULL), + mInternalFlags (SceneInternalFlag::eSCENE_DEFAULT), + mPublicFlags (desc.flags), + mStaticAnchor (NULL), + mBatchRemoveState (NULL), + mLostTouchPairs (PX_DEBUG_EXP("sceneLostTouchPairs")), + mOutOfBoundsIDs (PX_DEBUG_EXP("sceneOutOfBoundsIds")), + mVisualizationScale (0.0f), + mVisualizationParameterChanged (false), + mNbRigidStatics (0), + mNbRigidDynamics (0), + mSecondPassNarrowPhase (contextID, this, "ScScene.secondPassNarrowPhase"), + mPostNarrowPhase (contextID, this, "ScScene.postNarrowPhase"), + mFinalizationPhase (contextID, this, "ScScene.finalizationPhase"), + mUpdateCCDMultiPass (contextID, this, "ScScene.updateCCDMultiPass"), + mAfterIntegration (contextID, this, "ScScene.afterIntegration"), + mConstraintProjection (contextID, this, "ScScene.constraintProjection"), + mPostSolver (contextID, this, "ScScene.postSolver"), + mSolver (contextID, this, "ScScene.rigidBodySolver"), + mUpdateBodiesAndShapes (contextID, this, "ScScene.updateBodiesAndShapes"), + mUpdateSimulationController (contextID, this, "ScScene.updateSimulationController"), + mUpdateDynamics (contextID, this, "ScScene.updateDynamics"), + mProcessLostContactsTask (contextID, this, "ScScene.processLostContact"), + mProcessLostContactsTask2 (contextID, this, "ScScene.processLostContact2"), + mProcessLostContactsTask3 (contextID, this, "ScScene.processLostContact3"), + mDestroyManagersTask (contextID, this, "ScScene.destroyManagers"), + mLostTouchReportsTask (contextID, this, "ScScene.lostTouchReports"), + mUnregisterInteractionsTask (contextID, this, "ScScene.unregisterInteractions"), + mProcessNarrowPhaseLostTouchTasks(contextID, this, "ScScene.processNpLostTouchTask"), + mProcessNPLostTouchEvents (contextID, this, "ScScene.processNPLostTouchEvents"), + mPostThirdPassIslandGenTask (contextID, this, "ScScene.postThirdPassIslandGenTask"), + mPostIslandGen (contextID, this, "ScScene.postIslandGen"), + mIslandGen (contextID, this, "ScScene.islandGen"), + mPreRigidBodyNarrowPhase (contextID, this, "ScScene.preRigidBodyNarrowPhase"), + mSetEdgesConnectedTask (contextID, this, "ScScene.setEdgesConnectedTask"), + mFetchPatchEventsTask (contextID, this, "ScScene.fetchPatchEventsTask"), + mProcessLostPatchesTask (contextID, this, "ScScene.processLostSolverPatchesTask"), + mRigidBodyNarrowPhase (contextID, this, "ScScene.rigidBodyNarrowPhase"), + mRigidBodyNPhaseUnlock (contextID, this, "ScScene.unblockNarrowPhase"), + mPostBroadPhase (contextID, this, "ScScene.postBroadPhase"), + mPostBroadPhaseCont (contextID, this, "ScScene.postBroadPhaseCont"), + mPostBroadPhase2 (contextID, this, "ScScene.postBroadPhase2"), + mPostBroadPhase3 (contextID, this, "ScScene.postBroadPhase3"), + mPreallocateContactManagers (contextID, this, "ScScene.preallocateContactManagers"), + mIslandInsertion (contextID, this, "ScScene.islandInsertion"), + mRegisterContactManagers (contextID, this, "ScScene.registerContactManagers"), + mRegisterInteractions (contextID, this, "ScScene.registerInteractions"), + mRegisterSceneInteractions (contextID, this, "ScScene.registerSceneInteractions"), + mBroadPhase (contextID, this, "ScScene.broadPhase"), + mAdvanceStep (contextID, this, "ScScene.advanceStep"), + mCollideStep (contextID, this, "ScScene.collideStep"), + mTaskPool (16384), + mContactReportsNeedPostSolverVelocity(false), + mUseGpuRigidBodies (false), + mSimulationStage (SimulationStage::eCOMPLETE), + mTmpConstraintGroupRootBuffer (NULL), + mPosePreviewBodies (PX_DEBUG_EXP("scenePosePreviewBodies")), + mOverlapFilterTaskHead (NULL) +{ + + mCCDPass = 0; + for (int i=0; i < InteractionType::eTRACKED_IN_SCENE_COUNT; ++i) + mActiveInteractionCount[i] = 0; + + mStats = PX_NEW(SimStats); + mConstraintIDTracker = PX_NEW(ObjectIDTracker); + mShapeIDTracker = PX_NEW(ObjectIDTracker); + mRigidIDTracker = PX_NEW(ObjectIDTracker); + mElementIDPool = PX_NEW(ObjectIDTracker); + + mTriggerBufferExtraData = reinterpret_cast(PX_ALLOC(sizeof(TriggerBufferExtraData), "ScScene::TriggerBufferExtraData")); + new(mTriggerBufferExtraData) TriggerBufferExtraData(PX_DEBUG_EXP("ScScene::TriggerPairExtraData")); + + mStaticSimPool = PX_NEW(PreallocatingPool)(64, "StaticSim"); + mBodySimPool = PX_NEW(PreallocatingPool)(64, "BodySim"); + mShapeSimPool = PX_NEW(PreallocatingPool)(64, "ShapeSim"); + mConstraintSimPool = PX_NEW(Ps::Pool)(PX_DEBUG_EXP("ScScene::ConstraintSim")); + mConstraintInteractionPool = PX_NEW(Ps::Pool)(PX_DEBUG_EXP("ScScene::ConstraintInteraction")); + mLLArticulationPool = PX_NEW(LLArticulationPool); + mLLArticulationRCPool = PX_NEW(LLArticulationRCPool); + + mSimStateDataPool = PX_NEW(Ps::Pool)(PX_DEBUG_EXP("ScScene::SimStateData")); + + mClients.pushBack(PX_NEW(Client)()); + mProjectionManager = PX_NEW(ConstraintProjectionManager)(); + + mSqBoundsManager = PX_NEW(SqBoundsManager); + + mTaskManager = physx::PxTaskManager::createTaskManager(Ps::getFoundation().getErrorCallback(), desc.cpuDispatcher, desc.gpuDispatcher); + + for(PxU32 i=0; igetGpuDispatcher() == NULL) + { + shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, + __FILE__, __LINE__, "GPU solver pipeline failed, switching to software"); + + useGpuDynamics = false; + } + else if (!mTaskManager->getGpuDispatcher()->getCudaContextManager()->supportsArchSM30()) + useGpuDynamics = false; + } + + if (desc.broadPhaseType == PxBroadPhaseType::eGPU) + { + useGpuBroadphase = true; + if (mTaskManager->getGpuDispatcher() == NULL) + { + shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, + __FILE__, __LINE__, "GPU Bp pipeline failed, switching to software"); + + useGpuBroadphase = false; + } + else if (!mTaskManager->getGpuDispatcher()->getCudaContextManager()->supportsArchSM30()) + useGpuBroadphase = false; + } +#endif + + mUseGpuRigidBodies = useGpuBroadphase || useGpuDynamics; + + mLLContext = PX_NEW(PxsContext)(desc, mTaskManager, mTaskPool, contextID); + + if (mLLContext == 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Failed to create context!"); + return; + } + mLLContext->setMaterialManager(&getMaterialManager()); + + mMemoryManager = NULL; + +#if PX_SUPPORT_GPU_PHYSX + mHeapMemoryAllocationManager = NULL; + mGpuWranglerManagers = NULL; + + if (useGpuBroadphase || useGpuDynamics) + { + mMemoryManager = PxvGetPhysXGpu(true)->createGpuMemoryManager(mLLContext->getTaskManager().getGpuDispatcher(), NULL); + mGpuWranglerManagers = PxvGetPhysXGpu(true)->createGpuKernelWranglerManager(mLLContext->getTaskManager().getGpuDispatcher(), getFoundation().getErrorCallback(),desc.gpuComputeVersion); + mHeapMemoryAllocationManager = PxvGetPhysXGpu(true)->createGpuHeapMemoryAllocatorManager(desc.gpuDynamicsConfig.heapCapacity, mMemoryManager, desc.gpuComputeVersion); + } + else +#endif + { + mMemoryManager = createMemoryManager(); + } + + //Note: broadphase should be independent of AABBManager. MBP uses it to call getBPBounds but it has + //already been passed all bounds in BroadPhase::update() so should use that instead. + if(!useGpuBroadphase) + { + PxBroadPhaseType::Enum broadPhaseType = desc.broadPhaseType; + + if (broadPhaseType == PxBroadPhaseType::eGPU) + broadPhaseType = PxBroadPhaseType::eABP; + + mBP = Bp::BroadPhase::create( + broadPhaseType, + desc.limits.maxNbRegions, + desc.limits.maxNbBroadPhaseOverlaps, + desc.limits.maxNbStaticShapes, + desc.limits.maxNbDynamicShapes, + contextID); + } + else + { +#if PX_SUPPORT_GPU_PHYSX + mBP = PxvGetPhysXGpu(true)->createGpuBroadPhase + ( + mGpuWranglerManagers, + mLLContext->getTaskManager().getGpuDispatcher(), + NULL, + desc.gpuComputeVersion, + desc.gpuDynamicsConfig, + mHeapMemoryAllocationManager); +#endif + } + + //create allocator + Ps::VirtualAllocatorCallback* allocatorCallback = mMemoryManager->createHostMemoryAllocator(desc.gpuComputeVersion); + Ps::VirtualAllocator allocator(allocatorCallback); + + typedef Ps::Array ContactDistArray; + + mBoundsArray = PX_NEW(Bp::BoundsArray)(allocator); + //mBoundsArray = PX_NEW(Bp::BoundsArray); + mContactDistance = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ContactDistArray), PX_DEBUG_EXP("ContactDistance")), ContactDistArray)(allocator); + mHasContactDistanceChanged = false; + + const bool useEnhancedDeterminism = getPublicFlags() & PxSceneFlag::eENABLE_ENHANCED_DETERMINISM; + const bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + mSimpleIslandManager = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(IG::SimpleIslandManager), PX_DEBUG_EXP("SimpleIslandManager")), IG::SimpleIslandManager)(useEnhancedDeterminism, contextID); + + if (!useGpuDynamics) + { + if (desc.solverType == PxSolverType::ePGS) + { + mDynamicsContext = createDynamicsContext + (&mLLContext->getNpMemBlockPool(), mLLContext->getScratchAllocator(), + mLLContext->getTaskPool(), mLLContext->getSimStats(), &mLLContext->getTaskManager(), allocatorCallback, &getMaterialManager(), + &mSimpleIslandManager->getAccurateIslandSim(), contextID, mEnableStabilization, useEnhancedDeterminism, useAdaptiveForce, desc.maxBiasCoefficient, + !!(desc.flags & PxSceneFlag::eENABLE_FRICTION_EVERY_ITERATION)); + } + else + { + mDynamicsContext = createTGSDynamicsContext + (&mLLContext->getNpMemBlockPool(), mLLContext->getScratchAllocator(), + mLLContext->getTaskPool(), mLLContext->getSimStats(), &mLLContext->getTaskManager(), allocatorCallback, &getMaterialManager(), + &mSimpleIslandManager->getAccurateIslandSim(), contextID, mEnableStabilization, useEnhancedDeterminism, useAdaptiveForce, + desc.getTolerancesScale().length); + } + + mLLContext->setNphaseImplementationContext(createNphaseImplementationContext(*mLLContext, &mSimpleIslandManager->getAccurateIslandSim())); + + mSimulationControllerCallback = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ScSimulationControllerCallback), PX_DEBUG_EXP("ScSimulationControllerCallback")), ScSimulationControllerCallback(this)); + mSimulationController = createSimulationController(mSimulationControllerCallback); + + mAABBManager = PX_NEW(Bp::AABBManager)(*mBP, *mBoundsArray, *mContactDistance, + desc.limits.maxNbAggregates, desc.limits.maxNbStaticShapes + desc.limits.maxNbDynamicShapes, allocator, contextID, + desc.kineKineFilteringMode, desc.staticKineFilteringMode); + } + else + { +#if PX_SUPPORT_GPU_PHYSX + mDynamicsContext = PxvGetPhysXGpu(true)->createGpuDynamicsContext(mLLContext->getTaskPool(), mGpuWranglerManagers, mLLContext->getTaskManager().getGpuDispatcher(), NULL, + desc.gpuDynamicsConfig, &mSimpleIslandManager->getAccurateIslandSim(), desc.gpuMaxNumPartitions, mEnableStabilization, useEnhancedDeterminism, useAdaptiveForce, desc.maxBiasCoefficient, desc.gpuComputeVersion, mLLContext->getSimStats(), + mHeapMemoryAllocationManager, !!(desc.flags & PxSceneFlag::eENABLE_FRICTION_EVERY_ITERATION), desc.solverType); + + void* contactStreamBase = NULL; + void* patchStreamBase = NULL; + void* forceAndIndiceStreamBase = NULL; + + mDynamicsContext->getDataStreamBase(contactStreamBase, patchStreamBase, forceAndIndiceStreamBase); + + PxvNphaseImplementationContextUsableAsFallback* cpuNphaseImplementation = createNphaseImplementationContext(*mLLContext, &mSimpleIslandManager->getAccurateIslandSim()); + mLLContext->setNphaseFallbackImplementationContext(cpuNphaseImplementation); + + PxvNphaseImplementationContext* gpuNphaseImplementation = PxvGetPhysXGpu(true)->createGpuNphaseImplementationContext(*mLLContext, mGpuWranglerManagers, cpuNphaseImplementation, desc.gpuDynamicsConfig, contactStreamBase, patchStreamBase, + forceAndIndiceStreamBase, getBoundsArray().getBounds(), &mSimpleIslandManager->getAccurateIslandSim(), mDynamicsContext, desc.gpuComputeVersion, mHeapMemoryAllocationManager); + + mSimulationControllerCallback = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxgSimulationControllerCallback), PX_DEBUG_EXP("PxgSimulationControllerCallback")), PxgSimulationControllerCallback(this)); + + mSimulationController = PxvGetPhysXGpu(true)->createGpuSimulationController(mGpuWranglerManagers, mLLContext->getTaskManager().getGpuDispatcher(), NULL, + mDynamicsContext, gpuNphaseImplementation, mBP, useGpuBroadphase, mSimpleIslandManager, mSimulationControllerCallback, desc.gpuComputeVersion, mHeapMemoryAllocationManager); + + mSimulationController->setBounds(mBoundsArray); + mDynamicsContext->setSimulationController(mSimulationController); + + mLLContext->setNphaseImplementationContext(gpuNphaseImplementation); + + mLLContext->mContactStreamPool = &mDynamicsContext->getContactStreamPool(); + mLLContext->mPatchStreamPool = &mDynamicsContext->getPatchStreamPool(); + mLLContext->mForceAndIndiceStreamPool = &mDynamicsContext->getForceStreamPool(); + + Ps::VirtualAllocator tAllocator(mHeapMemoryAllocationManager->mMappedMemoryAllocators); + + mAABBManager = PX_NEW(Bp::AABBManager)(*mBP, *mBoundsArray, *mContactDistance, + desc.limits.maxNbAggregates, desc.limits.maxNbStaticShapes + desc.limits.maxNbDynamicShapes, tAllocator, contextID, + desc.kineKineFilteringMode, desc.staticKineFilteringMode); +#endif + } + + //Construct the bitmap of updated actors required as input to the broadphase update + if(desc.limits.maxNbBodies) + { + // PT: TODO: revisit this. Why do we handle the added/removed and updated bitmaps entirely differently, in different places? And what is this weird formula here? + mAABBManager->getChangedAABBMgActorHandleMap().resize((2*desc.limits.maxNbBodies + 256) & ~255); + } + + //mLLContext->createTransformCache(mDynamicsContext->getAllocatorCallback()); + + mLLContext->createTransformCache(*allocatorCallback); + mLLContext->setContactDistance(mContactDistance); + + mCCDContext = physx::PxsCCDContext::create(mLLContext, mDynamicsContext->getThresholdStream(), *mLLContext->getNphaseImplementationContext()); + + setSolverBatchSize(desc.solverBatchSize); + mDynamicsContext->setFrictionOffsetThreshold(desc.frictionOffsetThreshold); + mDynamicsContext->setCCDSeparationThreshold(desc.ccdMaxSeparation); + mDynamicsContext->setSolverOffsetSlop(desc.solverOffsetSlop); + + const PxTolerancesScale& scale = Physics::getInstance().getTolerancesScale(); + mDynamicsContext->setCorrelationDistance(0.025f * scale.length); + mLLContext->setMeshContactMargin(0.01f * scale.length); + mLLContext->setToleranceLength(scale.length); + + // the original descriptor uses + // bounce iff impact velocity > threshold + // but LL use + // bounce iff separation velocity < -threshold + // hence we negate here. + + mDynamicsContext->setBounceThreshold(-desc.bounceThresholdVelocity); + + StaticCore* anchorCore = PX_NEW(StaticCore)(PxTransform(PxIdentity)); + + mStaticAnchor = mStaticSimPool->construct(*this, *anchorCore); + + mNPhaseCore = PX_NEW(NPhaseCore)(*this, desc); + + initDominanceMatrix(); + +// DeterminismDebugger::begin(); + + mWokeBodyListValid = true; + mSleepBodyListValid = true; + + //load from desc: + setLimits(desc.limits); + + // Create broad phase + setBroadPhaseCallback(desc.broadPhaseCallback); + + setGravity(desc.gravity); + + setFrictionType(desc.frictionType); + + setPCM(desc.flags & PxSceneFlag::eENABLE_PCM); + + setContactCache(!(desc.flags & PxSceneFlag::eDISABLE_CONTACT_CACHE)); + setSimulationEventCallback(desc.simulationEventCallback); + setContactModifyCallback(desc.contactModifyCallback); + setCCDContactModifyCallback(desc.ccdContactModifyCallback); + setCCDMaxPasses(desc.ccdMaxPasses); + PX_ASSERT(mNPhaseCore); // refactor paranoia + + PX_ASSERT( ((desc.filterShaderData) && (desc.filterShaderDataSize > 0)) || + (!(desc.filterShaderData) && (desc.filterShaderDataSize == 0)) ); + if (desc.filterShaderData) + { + mFilterShaderData = PX_ALLOC(desc.filterShaderDataSize, sFilterShaderDataMemAllocId); + PxMemCopy(mFilterShaderData, desc.filterShaderData, desc.filterShaderDataSize); + mFilterShaderDataSize = desc.filterShaderDataSize; + mFilterShaderDataCapacity = desc.filterShaderDataSize; + } + else + { + mFilterShaderData = NULL; + mFilterShaderDataSize = 0; + mFilterShaderDataCapacity = 0; + } + mFilterShader = desc.filterShader; + mFilterCallback = desc.filterCallback; +} + +void Sc::Scene::release() +{ + // TODO: PT: check virtual stuff + + mTimeStamp++; + + //collisionSpace.purgeAllPairs(); + + //purgePairs(); + //releaseTagData(); + + // We know release all the shapes before the collision space + //collisionSpace.deleteAllShapes(); + + //collisionSpace.release(); + + //DeterminismDebugger::end(); + + ///clear broken constraint list: + clearBrokenConstraintBuffer(); + + PX_DELETE_AND_RESET(mNPhaseCore); + + PX_FREE_AND_RESET(mFilterShaderData); + + if (mStaticAnchor) + { + StaticCore& core = mStaticAnchor->getStaticCore(); + mStaticSimPool->destroy(mStaticAnchor); + delete &core; + } + + // Free object IDs and the deleted object id map + postReportsCleanup(); + + //before the task manager + if (mLLContext) + { + if(mLLContext->getNphaseFallbackImplementationContext()) + { + mLLContext->getNphaseFallbackImplementationContext()->destroy(); + mLLContext->setNphaseFallbackImplementationContext(NULL); + } + + if(mLLContext->getNphaseImplementationContext()) + { + mLLContext->getNphaseImplementationContext()->destroy(); + mLLContext->setNphaseImplementationContext(NULL); + } + } + + PX_DELETE_AND_RESET(mProjectionManager); + PX_DELETE_AND_RESET(mSqBoundsManager); + PX_DELETE_AND_RESET(mBoundsArray); + + for(PxU32 i=0;i~TriggerBufferExtraData(); + PX_FREE(mTriggerBufferExtraData); + + PX_DELETE(mElementIDPool); + PX_DELETE(mRigidIDTracker); + PX_DELETE(mShapeIDTracker); + PX_DELETE(mConstraintIDTracker); + PX_DELETE(mStats); + + mAABBManager->destroy(); + + mBP->destroy(); + + mSimulationControllerCallback->~PxsSimulationControllerCallback(); + PX_FREE(mSimulationControllerCallback); + mSimulationController->~PxsSimulationController(); + PX_FREE(mSimulationController); + + mDynamicsContext->destroy(); + + mCCDContext->destroy(); + + mSimpleIslandManager->~SimpleIslandManager(); + PX_FREE(mSimpleIslandManager); + +#if PX_SUPPORT_GPU_PHYSX + if (mGpuWranglerManagers) + { + mGpuWranglerManagers->~PxsKernelWranglerManager(); + PX_FREE(mGpuWranglerManagers); + mGpuWranglerManagers = NULL; + } + + if (mHeapMemoryAllocationManager) + { + mHeapMemoryAllocationManager->~PxsHeapMemoryAllocatorManager(); + PX_FREE(mHeapMemoryAllocationManager); + mHeapMemoryAllocationManager = NULL; + } +#endif + + if (mTaskManager) + mTaskManager->release(); + + if (mLLContext) + { + PX_DELETE(mLLContext); + mLLContext = NULL; + } + + mContactDistance->~Array(); + PX_FREE(mContactDistance); + + + if (mMemoryManager) + { + mMemoryManager->~PxsMemoryManager(); + PX_FREE(mMemoryManager); + mMemoryManager = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::preAllocate(PxU32 nbStatics, PxU32 nbBodies, PxU32 nbStaticShapes, PxU32 nbDynamicShapes) +{ + // PT: TODO: this is only used for my addActors benchmark for now. Pre-allocate more arrays here. + + mActiveBodies.reserve(PxMax(64,nbBodies)); + + mStaticSimPool->preAllocate(nbStatics); + + mBodySimPool->preAllocate(nbBodies); + + mShapeSimPool->preAllocate(nbStaticShapes + nbDynamicShapes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::addToActiveBodyList(BodySim& body) +{ + PX_ASSERT(body.getActiveListIndex() >= SC_NOT_IN_ACTIVE_LIST_INDEX); + + // Sort: kinematic before dynamic + const PxU32 size = mActiveBodies.size(); + BodyCore* appendedBodyCore = &body.getBodyCore(); // PT: by default we append the incoming body... + PxU32 incomingBodyActiveListIndex = size; // PT: ...at the end of the current array. + + if(body.isKinematic()) // PT: Except if incoming body is kinematic, in which case: + { + const PxU32 nbKinematics = mActiveKinematicBodyCount++; // PT: - we increase their number + if(nbKinematics != size) // PT: - if there's at least one dynamic in the array... + { + PX_ASSERT(appendedBodyCore != mActiveBodies[nbKinematics]); + appendedBodyCore = mActiveBodies[nbKinematics]; // PT: ...then we grab the first dynamic after the kinematics... + appendedBodyCore->getSim()->setActiveListIndex(size); // PT: ...and we move that one back to the end of the array... + + mActiveBodies[nbKinematics] = &body.getBodyCore(); // PT: ...while the incoming kine replaces the dynamic we moved out. + incomingBodyActiveListIndex = nbKinematics; // PT: ...thus the incoming kine's index is the prev #kines. + } + } + // for active compound rigids add to separate array, so we dont have to traverse all active actors + if(body.readInternalFlag(BodySim::BF_IS_COMPOUND_RIGID)) + { + PX_ASSERT(body.getActiveCompoundListIndex() >= SC_NOT_IN_ACTIVE_LIST_INDEX); + const PxU32 compoundIndex = mActiveCompoundBodies.size(); + mActiveCompoundBodies.pushBack(appendedBodyCore); + body.setActiveCompoundListIndex(compoundIndex); + } + body.setActiveListIndex(incomingBodyActiveListIndex); // PT: will be 'size' or 'nbKinematics' + mActiveBodies.pushBack(appendedBodyCore); // PT: will be the incoming object or the first dynamic we moved out. +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::addToActiveCompoundBodyList(BodySim& body) +{ + PX_ASSERT(body.readInternalFlag(BodySim::BF_IS_COMPOUND_RIGID)); + + BodyCore* appendedBodyCore = &body.getBodyCore(); + + PX_ASSERT(body.getActiveCompoundListIndex() >= SC_NOT_IN_ACTIVE_LIST_INDEX); + const PxU32 compoundIndex = mActiveCompoundBodies.size(); + mActiveCompoundBodies.pushBack(appendedBodyCore); + body.setActiveCompoundListIndex(compoundIndex); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::removeFromActiveCompoundBodyList(BodySim& body) +{ + PxU32 removedCompoundIndex = body.getActiveCompoundListIndex(); + PX_ASSERT(removedCompoundIndex < SC_NOT_IN_ACTIVE_LIST_INDEX); + body.setActiveCompoundListIndex(SC_NOT_IN_ACTIVE_LIST_INDEX); + + const PxU32 newCompoundSize = mActiveCompoundBodies.size() - 1; + + if(removedCompoundIndex!=newCompoundSize) + { + Sc::BodyCore* lastBody = mActiveCompoundBodies[newCompoundSize]; + mActiveCompoundBodies[removedCompoundIndex] = lastBody; + lastBody->getSim()->setActiveListIndex(removedCompoundIndex); + } + mActiveCompoundBodies.forceSize_Unsafe(newCompoundSize); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::removeFromActiveBodyList(BodySim& body) +{ + PxU32 removedIndex = body.getActiveListIndex(); + PX_ASSERT(removedIndex < SC_NOT_IN_ACTIVE_LIST_INDEX); + PX_ASSERT(mActiveBodies[removedIndex]==&body.getBodyCore()); + body.setActiveListIndex(SC_NOT_IN_ACTIVE_LIST_INDEX); + + const PxU32 newSize = mActiveBodies.size() - 1; + + // Sort: kinematic before dynamic + if(removedIndex < mActiveKinematicBodyCount) // PT: same as 'body.isKinematic()' but without accessing the Core data + { + PX_ASSERT(mActiveKinematicBodyCount); + PX_ASSERT(body.isKinematic()); + const PxU32 swapIndex = --mActiveKinematicBodyCount; + if(newSize != swapIndex // PT: equal if the array only contains kinematics + && removedIndex < swapIndex) // PT: i.e. "if we don't remove the last kinematic" + { + BodyCore* swapBody = mActiveBodies[swapIndex]; + swapBody->getSim()->setActiveListIndex(removedIndex); + mActiveBodies[removedIndex] = swapBody; + removedIndex = swapIndex; + } + } + + // for active compound rigids add to separate array, so we dont have to traverse all active actors + // A.B. TODO we should handle kinematic switch, no need to hold kinematic rigids in compound list + if(body.readInternalFlag(BodySim::BF_IS_COMPOUND_RIGID)) + { + PxU32 removedCompoundIndex = body.getActiveCompoundListIndex(); + PX_ASSERT(removedCompoundIndex < SC_NOT_IN_ACTIVE_LIST_INDEX); + body.setActiveCompoundListIndex(SC_NOT_IN_ACTIVE_LIST_INDEX); + + const PxU32 newCompoundSize = mActiveCompoundBodies.size() - 1; + + if(removedCompoundIndex!=newCompoundSize) + { + Sc::BodyCore* lastBody = mActiveCompoundBodies[newCompoundSize]; + mActiveCompoundBodies[removedCompoundIndex] = lastBody; + lastBody->getSim()->setActiveCompoundListIndex(removedCompoundIndex); + } + mActiveCompoundBodies.forceSize_Unsafe(newCompoundSize); + } + + if(removedIndex!=newSize) + { + Sc::BodyCore* lastBody = mActiveBodies[newSize]; + mActiveBodies[removedIndex] = lastBody; + lastBody->getSim()->setActiveListIndex(removedIndex); + } + mActiveBodies.forceSize_Unsafe(newSize); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::swapInActiveBodyList(BodySim& body) +{ + const PxU32 activeListIndex = body.getActiveListIndex(); + PX_ASSERT(activeListIndex < SC_NOT_IN_ACTIVE_LIST_INDEX); + + PxU32 swapIndex; + PxU32 newActiveKinematicBodyCount; + if(activeListIndex < mActiveKinematicBodyCount) + { + // kinematic -> dynamic + PX_ASSERT(!body.isKinematic()); // the corresponding flag gets switched before this call + PX_ASSERT(mActiveKinematicBodyCount > 0); // there has to be at least one kinematic body + + swapIndex = mActiveKinematicBodyCount - 1; + newActiveKinematicBodyCount = swapIndex; + } + else + { + // dynamic -> kinematic + PX_ASSERT(body.isKinematic()); // the corresponding flag gets switched before this call + PX_ASSERT(mActiveKinematicBodyCount < mActiveBodies.size()); // there has to be at least one dynamic body + + swapIndex = mActiveKinematicBodyCount; + newActiveKinematicBodyCount = swapIndex + 1; + } + + BodyCore*& swapBodyRef = mActiveBodies[swapIndex]; + body.setActiveListIndex(swapIndex); + BodyCore* swapBody = swapBodyRef; + swapBodyRef = &body.getBodyCore(); + + swapBody->getSim()->setActiveListIndex(activeListIndex); + mActiveBodies[activeListIndex] = swapBody; + + mActiveKinematicBodyCount = newActiveKinematicBodyCount; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::registerInteraction(Interaction* interaction, bool active) +{ + const InteractionType::Enum type = interaction->getType(); + const PxU32 sceneArrayIndex = mInteractions[type].size(); + interaction->setInteractionId(sceneArrayIndex); + if(mInteractions[type].capacity()==0) + mInteractions[type].reserve(64); + + mInteractions[type].pushBack(interaction); + if (active) + { + if (sceneArrayIndex > mActiveInteractionCount[type]) + swapInteractionArrayIndices(sceneArrayIndex, mActiveInteractionCount[type], type); + mActiveInteractionCount[type]++; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::unregisterInteraction(Interaction* interaction) +{ + const InteractionType::Enum type = interaction->getType(); + const PxU32 sceneArrayIndex = interaction->getInteractionId(); + mInteractions[type].replaceWithLast(sceneArrayIndex); + interaction->setInteractionId(PX_INVALID_INTERACTION_SCENE_ID); + if (sceneArrayIndexsetInteractionId(sceneArrayIndex); + if (sceneArrayIndex& interArray = mInteractions[type]; + Interaction* interaction1 = interArray[id1]; + Interaction* interaction2 = interArray[id2]; + interArray[id1] = interaction2; + interArray[id2] = interaction1; + interaction1->setInteractionId(id2); + interaction2->setInteractionId(id1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::notifyInteractionActivated(Interaction* interaction) +{ + PX_ASSERT((interaction->getType() == InteractionType::eOVERLAP) || (interaction->getType() == InteractionType::eTRIGGER)); + PX_ASSERT(interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE)); + PX_ASSERT(interaction->getInteractionId() != PX_INVALID_INTERACTION_SCENE_ID); + + const InteractionType::Enum type = interaction->getType(); + + PX_ASSERT(interaction->getInteractionId() >= mActiveInteractionCount[type]); + + if (mActiveInteractionCount[type] < mInteractions[type].size()) + swapInteractionArrayIndices(mActiveInteractionCount[type], interaction->getInteractionId(), type); + mActiveInteractionCount[type]++; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::notifyInteractionDeactivated(Interaction* interaction) +{ + PX_ASSERT((interaction->getType() == InteractionType::eOVERLAP) || (interaction->getType() == InteractionType::eTRIGGER)); + PX_ASSERT(!interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE)); + PX_ASSERT(interaction->getInteractionId() != PX_INVALID_INTERACTION_SCENE_ID); + + const InteractionType::Enum type = interaction->getType(); + PX_ASSERT(interaction->getInteractionId() < mActiveInteractionCount[type]); + + if (mActiveInteractionCount[type] > 1) + swapInteractionArrayIndices(mActiveInteractionCount[type]-1, interaction->getInteractionId(), type); + mActiveInteractionCount[type]--; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void** Sc::Scene::allocatePointerBlock(PxU32 size) +{ + PX_ASSERT(size>32 || size == 32 || size == 16 || size == 8); + void* ptr; + if(size==8) + ptr = mPointerBlock8Pool.construct(); + else if(size == 16) + ptr = mPointerBlock16Pool.construct(); + else if(size == 32) + ptr = mPointerBlock32Pool.construct(); + else + ptr = PX_ALLOC(size * sizeof(void*), "void*"); + + return reinterpret_cast(ptr); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::deallocatePointerBlock(void** block, PxU32 size) +{ + PX_ASSERT(size>32 || size == 32 || size == 16 || size == 8); + if(size==8) + mPointerBlock8Pool.destroy(reinterpret_cast(block)); + else if(size == 16) + mPointerBlock16Pool.destroy(reinterpret_cast(block)); + else if(size == 32) + mPointerBlock32Pool.destroy(reinterpret_cast(block)); + else + return PX_FREE(block); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxBroadPhaseType::Enum Sc::Scene::getBroadPhaseType() const +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->getType(); +} + +bool Sc::Scene::getBroadPhaseCaps(PxBroadPhaseCaps& caps) const +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->getCaps(caps); +} + +PxU32 Sc::Scene::getNbBroadPhaseRegions() const +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->getNbRegions(); +} + +PxU32 Sc::Scene::getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->getRegions(userBuffer, bufferSize, startIndex); +} + +PxU32 Sc::Scene::addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion) +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->addRegion(region, populateRegion); +} + +bool Sc::Scene::removeBroadPhaseRegion(PxU32 handle) +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->removeRegion(handle); +} + +void** Sc::Scene::getOutOfBoundsAggregates() +{ + PxU32 dummy; + return mAABBManager->getOutOfBoundsAggregates(dummy); +} + +PxU32 Sc::Scene::getNbOutOfBoundsAggregates() +{ + PxU32 val; + mAABBManager->getOutOfBoundsAggregates(val); + return val; +} + +void Sc::Scene::clearOutOfBoundsAggregates() +{ + mAABBManager->clearOutOfBoundsAggregates(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::setFilterShaderData(const void* data, PxU32 dataSize) +{ + PX_UNUSED(sFilterShaderDataMemAllocId); + + if (data) + { + PX_ASSERT(dataSize > 0); + + void* buffer; + + if (dataSize <= mFilterShaderDataCapacity) + buffer = mFilterShaderData; + else + { + buffer = PX_ALLOC(dataSize, sFilterShaderDataMemAllocId); + if (buffer) + { + mFilterShaderDataCapacity = dataSize; + if (mFilterShaderData) + PX_FREE(mFilterShaderData); + } + else + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Failed to allocate memory for filter shader data!"); + return; + } + } + + PxMemCopy(buffer, data, dataSize); + mFilterShaderData = buffer; + mFilterShaderDataSize = dataSize; + } + else + { + PX_ASSERT(dataSize == 0); + + if (mFilterShaderData) + PX_FREE_AND_RESET(mFilterShaderData); + mFilterShaderDataSize = 0; + mFilterShaderDataCapacity = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Cm::RenderBuffer& Sc::Scene::getRenderBuffer() +{ + return mLLContext->getRenderBuffer(); +} + +void Sc::Scene::prepareCollide() +{ + mReportShapePairTimeStamp++; // deleted actors/shapes should get separate pair entries in contact reports + mContactReportsNeedPostSolverVelocity = false; + + mRemovedShapeCountAtSimStart = mShapeIDTracker->getDeletedIDCount(); + + getRenderBuffer().clear(); + + ///clear broken constraint list: + clearBrokenConstraintBuffer(); + + updateFromVisualizationParameters(); + + visualizeStartStep(); + +#ifdef DUMP_PROFILER + dumpProfiler(this); +#endif + + PxcClearContactCacheStats(); +} + +void Sc::Scene::simulate(PxReal timeStep, PxBaseTask* continuation) +{ + if(timeStep != 0.0f) + { + mDt = timeStep; + mOneOverDt = 0.0f < mDt ? 1.0f/mDt : 0.0f; + + mAdvanceStep.setContinuation(continuation); + + prepareCollide(); + stepSetupCollide(&mAdvanceStep); + + mCollideStep.setContinuation(&mAdvanceStep); + + mAdvanceStep.removeReference(); + mCollideStep.removeReference(); + } +} + +void Sc::Scene::advance(PxReal timeStep, PxBaseTask* continuation) +{ + if(timeStep != 0.0f) + { + mDt = timeStep; + mOneOverDt = 0.0f < mDt ? 1.0f/mDt : 0.0f; + + mAdvanceStep.setContinuation(continuation); + + stepSetupSolve(&mAdvanceStep); + + + mAdvanceStep.removeReference(); + } +} + +void Sc::Scene::setBounceThresholdVelocity(const PxReal t) +{ + mDynamicsContext->setBounceThreshold(-t); +} + +PxReal Sc::Scene::getBounceThresholdVelocity() const +{ + return -mDynamicsContext->getBounceThreshold(); +} + +void Sc::Scene::collide(PxReal timeStep, PxBaseTask* continuation) +{ + mDt = timeStep; + + prepareCollide(); + stepSetupCollide(continuation); + + mLLContext->beginUpdate(); + + mCollideStep.setContinuation(continuation); + mCollideStep.removeReference(); +} + +void Sc::Scene::setFrictionType(PxFrictionType::Enum model) +{ + mDynamicsContext->setFrictionType(model); +} + +PxFrictionType::Enum Sc::Scene::getFrictionType() const +{ + return mDynamicsContext->getFrictionType(); +} + +void Sc::Scene::setPCM(bool enabled) +{ + mLLContext->setPCM(enabled); +} + +void Sc::Scene::setContactCache(bool enabled) +{ + mLLContext->setContactCache(enabled); +} + +void Sc::Scene::endSimulation() +{ + // Handle user contact filtering + // Note: Do this before the contact callbacks get fired since the filter callback might + // trigger contact reports (touch lost due to re-filtering) + + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + mNPhaseCore->fireCustomFilteringCallbacks(outputs, mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE); + + mNPhaseCore->preparePersistentContactEventListForNextFrame(); + + mSimulationController->releaseDeferredArticulationIds(); + + endStep(); // - Update time stamps + + PxcDisplayContactCacheStats(); +} + +void Sc::Scene::flush(bool sendPendingReports) +{ + if (sendPendingReports) + { + fireQueuedContactCallbacks(true); + mNPhaseCore->clearContactReportStream(); + mNPhaseCore->clearContactReportActorPairs(true); + + fireTriggerCallbacks(); + } + else + { + mNPhaseCore->clearContactReportActorPairs(true); // To clear the actor pair set + } + postReportsCleanup(); + mNPhaseCore->freeContactReportStreamMemory(); + + mTriggerBufferAPI.reset(); + mTriggerBufferExtraData->reset(); + + clearBrokenConstraintBuffer(); + mBrokenConstraints.reset(); + + clearSleepWakeBodies(); //!!! If we send out these reports on flush then this would not be necessary + + mClients.shrink(); + + mShapeIDTracker->reset(); + mRigidIDTracker->reset(); + + processLostTouchPairs(); // Processes the lost touch bodies + PX_ASSERT(mLostTouchPairs.size() == 0); + mLostTouchPairs.reset(); + // Does not seem worth deleting the bitmap for the lost touch pair list + + mActiveBodies.shrink(); + + for(PxU32 i=0; i < InteractionType::eTRACKED_IN_SCENE_COUNT; i++) + { + mInteractions[i].shrink(); + } + + //!!! TODO: look into retrieving memory from the NPhaseCore & Broadphase class (all the pools in there etc.) + + mLLContext->getNpMemBlockPool().releaseUnusedBlocks(); +} + +// User callbacks + +void Sc::Scene::setSimulationEventCallback(PxSimulationEventCallback* callback) +{ + if(!mSimulationEventCallback && callback) + { + // if there was no callback before, the sleeping bodies have to be prepared for potential notification events (no shortcut possible anymore) + BodyCore* const* sleepingBodies = mSleepBodies.getEntries(); + for(PxU32 i=0; i < mSleepBodies.size(); i++) + sleepingBodies[i]->getSim()->raiseInternalFlag(BodySim::BF_SLEEP_NOTIFY); + } + + mSimulationEventCallback = callback; +} + +PxSimulationEventCallback* Sc::Scene::getSimulationEventCallback() const +{ + return mSimulationEventCallback; +} + +void Sc::Scene::setContactModifyCallback(PxContactModifyCallback* callback) +{ + mLLContext->setContactModifyCallback(callback); +} + +PxContactModifyCallback* Sc::Scene::getContactModifyCallback() const +{ + return mLLContext->getContactModifyCallback(); +} + +void Sc::Scene::setCCDContactModifyCallback(PxCCDContactModifyCallback* callback) +{ + mCCDContext->setCCDContactModifyCallback(callback); +} + +PxCCDContactModifyCallback* Sc::Scene::getCCDContactModifyCallback() const +{ + return mCCDContext->getCCDContactModifyCallback(); +} + +void Sc::Scene::setCCDMaxPasses(PxU32 ccdMaxPasses) +{ + mCCDContext->setCCDMaxPasses(ccdMaxPasses); +} + +PxU32 Sc::Scene::getCCDMaxPasses() const +{ + return mCCDContext->getCCDMaxPasses(); +} + +void Sc::Scene::setBroadPhaseCallback(PxBroadPhaseCallback* callback) +{ + mBroadPhaseCallback = callback; +} + +PxBroadPhaseCallback* Sc::Scene::getBroadPhaseCallback() const +{ + return mBroadPhaseCallback; +} + +void Sc::Scene::removeBody(BodySim& body) //this also notifies any connected joints! +{ + ConstraintGroupNode* node = body.getConstraintGroup(); + if (node) + { + //invalidate the constraint group: + //this adds all constraints of the group to the dirty list such that groups get re-generated next frame + getProjectionManager().invalidateGroup(*node, NULL); + } + + BodyCore& core = body.getBodyCore(); + + // Remove from sleepBodies array + mSleepBodies.erase(&core); + PX_ASSERT(!mSleepBodies.contains(&core)); + + // Remove from wokeBodies array + mWokeBodies.erase(&core); + PX_ASSERT(!mWokeBodies.contains(&core)); + + if (body.isActive() && (core.getFlags() & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW)) + removeFromPosePreviewList(body); + else + PX_ASSERT(!isInPosePreviewList(body)); + + markReleasedBodyIDForLostTouch(body.getRigidID()); +} + +void Sc::Scene::addConstraint(ConstraintCore& constraint, RigidCore* body0, RigidCore* body1) +{ + ConstraintSim* sim = mConstraintSimPool->construct(constraint, body0, body1, *this); + PX_UNUSED(sim); + + mConstraints.insert(&constraint); +} + +void Sc::Scene::removeConstraint(ConstraintCore& constraint) +{ + ConstraintSim* cSim = constraint.getSim(); + + if (cSim) + { + BodySim* b = cSim->getAnyBody(); + ConstraintGroupNode* n = b->getConstraintGroup(); + + if (n) + getProjectionManager().invalidateGroup(*n, cSim); + mConstraintSimPool->destroy(cSim); + } + + mConstraints.erase(&constraint); +} + +void Sc::Scene::addArticulation(ArticulationCore& articulation, BodyCore& root) +{ + ArticulationSim* sim = PX_NEW(ArticulationSim)(articulation, *this, root); + + if (sim && (sim->getLowLevelArticulation() == NULL)) + { + PX_DELETE(sim); + return; + } + mArticulations.insert(&articulation); +} + +void Sc::Scene::removeArticulation(ArticulationCore& articulation) +{ + ArticulationSim* a = articulation.getSim(); + if (a) + PX_DELETE(a); + mArticulations.erase(&articulation); +} + +void Sc::Scene::addArticulationJoint(ArticulationJointCore& joint, BodyCore& parent, BodyCore& child) +{ + ArticulationJointSim* sim = PX_NEW(ArticulationJointSim)(joint, *parent.getSim(), *child.getSim()); + PX_UNUSED(sim); +} + +void Sc::Scene::removeArticulationJoint(ArticulationJointCore& joint) +{ + if (joint.getSim()) + PX_DELETE(joint.getSim()); +} + +void Sc::Scene::addArticulationSimControl(Sc::ArticulationCore& core) +{ + Sc::ArticulationSim* sim = core.getSim(); + if (sim) + mSimulationController->addArticulation(sim->getLowLevelArticulation(), sim->getIslandNodeIndex()); +} + +void Sc::Scene::addBrokenConstraint(Sc::ConstraintCore* c) +{ + PX_ASSERT(mBrokenConstraints.find(c) == mBrokenConstraints.end()); + mBrokenConstraints.pushBack(c); +} + +void Sc::Scene::addActiveBreakableConstraint(Sc::ConstraintSim* c, Sc::ConstraintInteraction* ci) +{ + PX_ASSERT(ci && ci->readInteractionFlag(InteractionFlag::eIS_ACTIVE)); + PX_UNUSED(ci); + PX_ASSERT(!mActiveBreakableConstraints.contains(c)); + PX_ASSERT(!c->isBroken()); + mActiveBreakableConstraints.insert(c); + c->setFlag(ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED); +} + +void Sc::Scene::removeActiveBreakableConstraint(Sc::ConstraintSim* c) +{ + const bool exists = mActiveBreakableConstraints.erase(c); + PX_ASSERT(exists); + PX_UNUSED(exists); + c->clearFlag(ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED); +} + +void* Sc::Scene::allocateConstraintBlock(PxU32 size) +{ + if(size<=128) + return mMemBlock128Pool.construct(); + else if(size<=256) + return mMemBlock256Pool.construct(); + else if(size<=384) + return mMemBlock384Pool.construct(); + else + return PX_ALLOC(size, "ConstraintBlock"); +} + +void Sc::Scene::deallocateConstraintBlock(void* ptr, PxU32 size) +{ + if(size<=128) + mMemBlock128Pool.destroy(reinterpret_cast(ptr)); + else if(size<=256) + mMemBlock256Pool.destroy(reinterpret_cast(ptr)); + else if(size<=384) + mMemBlock384Pool.destroy(reinterpret_cast(ptr)); + else + PX_FREE(ptr); +} + +//int testAxisConstraint(Sc::Scene& scene); +//int testCasts(Shape* shape); +//int testCasts(Shape& shape); + +/*-------------------------------*\ +| Adam's explanation of the RB solver: +| This is a novel idea of mine, +| a combination of ideas on +| Milenkovic's Optimization +| Based Animation, and Trinkle's +| time stepping schemes. +| +| A time step goes like this: +| +| Taking no substeps: +| 0) Compute contact points. +| 1) Update external forces. This may include friction. +| 2) Integrate external forces to current velocities. +| 3) Solve for impulses at contacts which will prevent +| interpenetration at next timestep given some +| velocity integration scheme. +| 4) Use the integration scheme on velocity to +| reach the next state. Here we should not have any +| interpenetration at the old contacts, but perhaps +| at new contacts. If interpenetrating at new contacts, +| just add these to the contact list; no need to repeat +| the time step, because the scheme will get rid of the +| penetration by the next step. +| +| +| Advantages: +| + Large steps, LOD realism. +| + very simple. +| +\*-------------------------------*/ + +void Sc::Scene::advanceStep(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.solveQueueTasks", getContextId()); + + if (mDt != 0.0f) + { + mFinalizationPhase.addDependent(*continuation); + mFinalizationPhase.removeReference(); + + if (mPublicFlags & PxSceneFlag::eENABLE_CCD) + { + mUpdateCCDMultiPass.setContinuation(&mFinalizationPhase); + mAfterIntegration.setContinuation(&mUpdateCCDMultiPass); + mUpdateCCDMultiPass.removeReference(); + } + else + { + mAfterIntegration.setContinuation(&mFinalizationPhase); + } + + mPostSolver.setContinuation(&mAfterIntegration); + mUpdateSimulationController.setContinuation(&mPostSolver); + mUpdateDynamics.setContinuation(&mUpdateSimulationController); + mUpdateBodiesAndShapes.setContinuation(&mUpdateDynamics); + mSolver.setContinuation(&mUpdateBodiesAndShapes); + mPostIslandGen.setContinuation(&mSolver); + mIslandGen.setContinuation(&mPostIslandGen); + mPostNarrowPhase.addDependent(mIslandGen); + mPostNarrowPhase.removeReference(); + + mSecondPassNarrowPhase.setContinuation(&mPostNarrowPhase); + + mFinalizationPhase.removeReference(); + mAfterIntegration.removeReference(); + mPostSolver.removeReference(); + mUpdateSimulationController.removeReference(); + mUpdateDynamics.removeReference(); + mUpdateBodiesAndShapes.removeReference(); + mSolver.removeReference(); + mPostIslandGen.removeReference(); + mIslandGen.removeReference(); + mPostNarrowPhase.removeReference(); + mSecondPassNarrowPhase.removeReference(); + } +} + +//void Sc::Scene::advanceStep(PxBaseTask* continuation) +//{ +// PX_PROFILE_ZONE("Sim.solveQueueTasks", getContextId()); +// +// if(mDt!=0.0f) +// { +// mFinalizationPhase.addDependent(*continuation); +// mFinalizationPhase.removeReference(); +// +// if(mPublicFlags & PxSceneFlag::eENABLE_CCD) +// { +// mUpdateCCDMultiPass.setContinuation(&mFinalizationPhase); +// mAfterIntegration.setContinuation(&mUpdateCCDMultiPass); +// mUpdateCCDMultiPass.removeReference(); +// } +// else +// { +// mAfterIntegration.setContinuation(&mFinalizationPhase); +// } +// +// mPostSolver.setContinuation(&mAfterIntegration); +// mUpdateSimulationController.setContinuation(&mPostSolver); +// mUpdateDynamics.setContinuation(&mUpdateSimulationController); +// mSolver.setContinuation(&mUpdateDynamics); +// mProcessTriggerInteractions.setContinuation(&mSolver); +// mPostIslandGen.setContinuation(&mProcessTriggerInteractions); +// mIslandGen.setContinuation(&mPostIslandGen); +// +// mFinalizationPhase.removeReference(); +// mAfterIntegration.removeReference(); +// mPostSolver.removeReference(); +// mUpdateSimulationController.removeReference(); +// mUpdateDynamics.removeReference(); +// mSolver.removeReference(); +// mProcessTriggerInteractions.removeReference(); +// mPostIslandGen.removeReference(); +// mIslandGen.removeReference(); +// } +//} + +void Sc::Scene::collideStep(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.collideQueueTasks", getContextId()); + PX_PROFILE_START_CROSSTHREAD("Basic.collision", getContextId()); + + mStats->simStart(); + mLLContext->beginUpdate(); + + mPostNarrowPhase.setTaskManager(*continuation->getTaskManager()); + mPostNarrowPhase.addReference(); + + mFinalizationPhase.setTaskManager(*continuation->getTaskManager()); + mFinalizationPhase.addReference(); + + mRigidBodyNarrowPhase.setContinuation(continuation); + mPreRigidBodyNarrowPhase.setContinuation(&mRigidBodyNarrowPhase); + + mRigidBodyNarrowPhase.removeReference(); + mPreRigidBodyNarrowPhase.removeReference(); +} + +void Sc::Scene::broadPhase(PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Basic.broadPhase", getContextId()); + + const PxU32 numCpuTasks = continuation->getTaskManager()->getCpuDispatcher()->getWorkerCount(); + mAABBManager->updateAABBsAndBP(numCpuTasks, mLLContext->getTaskPool(), &mLLContext->getScratchAllocator(), mHasContactDistanceChanged, continuation, &mRigidBodyNPhaseUnlock); +} + +void Sc::Scene::postBroadPhase(PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Basic.postBroadPhase", getContextId()); + + //Notify narrow phase that broad phase has completed + mLLContext->getNphaseImplementationContext()->postBroadPhaseUpdateContactManager(); + mAABBManager->postBroadPhase(continuation, &mRigidBodyNPhaseUnlock, *getFlushPool()); + +} + +void Sc::Scene::postBroadPhaseContinuation(PxBaseTask* continuation) +{ + mAABBManager->getChangedAABBMgActorHandleMap().clear(); + + // - Finishes broadphase update + // - Adds new interactions (and thereby contact managers if needed) + finishBroadPhase(continuation); +} + +void Sc::Scene::postBroadPhaseStage2(PxBaseTask* continuation) +{ + // - Wakes actors that lost touch if appropriate + processLostTouchPairs(); + //Release unused Cms back to the pool (later, this needs to be done in a thread-safe way from multiple worker threads + mIslandInsertion.setContinuation(continuation); + mRegisterContactManagers.setContinuation(continuation); + mRegisterInteractions.setContinuation(continuation); + mRegisterSceneInteractions.setContinuation(continuation); + mIslandInsertion.removeReference(); + mRegisterContactManagers.removeReference(); + mRegisterInteractions.removeReference(); + mRegisterSceneInteractions.removeReference(); + + { + PX_PROFILE_ZONE("Sim.processNewOverlaps.release", getContextId()); + for (PxU32 a = 0; a < mPreallocatedContactManagers.size(); ++a) + { + if ((reinterpret_cast(mPreallocatedContactManagers[a]) & 1) == 0) + mLLContext->getContactManagerPool().put(mPreallocatedContactManagers[a]); + } + + for (PxU32 a = 0; a < mPreallocatedShapeInteractions.size(); ++a) + { + if ((reinterpret_cast(mPreallocatedShapeInteractions[a]) & 1) == 0) + mNPhaseCore->mShapeInteractionPool.deallocate(mPreallocatedShapeInteractions[a]); + } + + for (PxU32 a = 0; a < mPreallocatedInteractionMarkers.size(); ++a) + { + if ((reinterpret_cast(mPreallocatedInteractionMarkers[a]) & 1) == 0) + mNPhaseCore->mInteractionMarkerPool.deallocate(mPreallocatedInteractionMarkers[a]); + } + } +} + +void Sc::Scene::postBroadPhaseStage3(PxBaseTask* /*continuation*/) +{ + finishBroadPhaseStage2(0); + + PX_PROFILE_STOP_CROSSTHREAD("Basic.postBroadPhase", getContextId()); + PX_PROFILE_STOP_CROSSTHREAD("Basic.broadPhase", getContextId()); +} + +class DirtyShapeUpdatesTask : public Cm::Task +{ +public: + static const PxU32 MaxShapes = 256; + + PxsTransformCache& mCache; + Bp::BoundsArray& mBoundsArray; + Sc::ShapeSim* mShapes[MaxShapes]; + PxU32 mNbShapes; + + DirtyShapeUpdatesTask(PxU64 contextID, PxsTransformCache& cache, Bp::BoundsArray& boundsArray) : + Cm::Task (contextID), + mCache (cache), + mBoundsArray(boundsArray), + mNbShapes (0) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbShapes; ++a) + { + mShapes[a]->updateCached(mCache, mBoundsArray); + } + } + + virtual const char* getName() const { return "DirtyShapeUpdatesTask"; } + +private: + PX_NOCOPY(DirtyShapeUpdatesTask) +}; + +class SpeculativeCCDContactDistanceUpdateTask : public Cm::Task +{ +public: + static const PxU32 MaxBodies = 128; + PxReal* mContactDistances; + PxReal mDt; + Sc::BodySim* mBodySims[MaxBodies]; + PxU32 mNbBodies; + + Bp::BoundsArray& mBoundsArray; + + SpeculativeCCDContactDistanceUpdateTask(PxU64 contextID, PxReal* contactDistances, const PxReal dt, Bp::BoundsArray& boundsArray) : + Cm::Task (contextID), + mContactDistances (contactDistances), + mDt (dt), + mNbBodies (0), + mBoundsArray (boundsArray) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbBodies; ++a) + { + mBodySims[a]->updateContactDistance(mContactDistances, mDt, mBoundsArray); + } + } + + virtual const char* getName() const { return "SpeculativeCCDContactDistanceUpdateTask"; } + +private: + PX_NOCOPY(SpeculativeCCDContactDistanceUpdateTask) +}; + +class SpeculativeCCDContactDistanceArticulationUpdateTask : public Cm::Task +{ +public: + PxReal* mContactDistances; + PxReal mDt; + Sc::ArticulationSim* mArticulation; + Bp::BoundsArray& mBoundsArray; + + SpeculativeCCDContactDistanceArticulationUpdateTask(PxU64 contextID, PxReal* contactDistances, const PxReal dt, Bp::BoundsArray& boundsArray) : + Cm::Task (contextID), + mContactDistances (contactDistances), + mDt (dt), + mBoundsArray (boundsArray) + { + } + + virtual void runInternal() + { + mArticulation->updateContactDistance(mContactDistances, mDt, mBoundsArray); + } + + virtual const char* getName() const { return "SpeculativeCCDContactDistanceArticulationUpdateTask"; } + +private: + PX_NOCOPY(SpeculativeCCDContactDistanceArticulationUpdateTask) +}; + + +void Sc::Scene::preRigidBodyNarrowPhase(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Scene.preNarrowPhase", getContextId()); + + PxU32 index; + + Cm::FlushPool& pool = mLLContext->getTaskPool(); + + //calculate contact distance for speculative CCD shapes + Cm::BitMap::Iterator speculativeCCDIter(mSpeculativeCCDRigidBodyBitMap); + + SpeculativeCCDContactDistanceUpdateTask* ccdTask = PX_PLACEMENT_NEW(pool.allocate(sizeof(SpeculativeCCDContactDistanceUpdateTask)), SpeculativeCCDContactDistanceUpdateTask)(getContextId(), mContactDistance->begin(), mDt, *mBoundsArray); + + IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + Cm::BitMapPinned& changedMap = mAABBManager->getChangedAABBMgActorHandleMap(); + + const size_t bodyOffset = PX_OFFSET_OF_RT(Sc::BodySim, getLowLevelBody()); + + bool hasContactDistanceChanged = mHasContactDistanceChanged; + while ((index = speculativeCCDIter.getNext()) != Cm::BitMap::Iterator::DONE) + { + PxsRigidBody* rigidBody = islandSim.getRigidBody(IG::NodeIndex(index)); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(rigidBody)-bodyOffset); + if (bodySim) + { + hasContactDistanceChanged = true; + ccdTask->mBodySims[ccdTask->mNbBodies++] = bodySim; + + ElementSim* current = bodySim->getElements_(); + while (current) + { + changedMap.growAndSet(current->getElementID()); + current = current->mNextInActor; + } + + if (ccdTask->mNbBodies == SpeculativeCCDContactDistanceUpdateTask::MaxBodies) + { + ccdTask->setContinuation(continuation); + ccdTask->removeReference(); + ccdTask = PX_PLACEMENT_NEW(pool.allocate(sizeof(SpeculativeCCDContactDistanceUpdateTask)), SpeculativeCCDContactDistanceUpdateTask)(getContextId(), mContactDistance->begin(), mDt, *mBoundsArray); + } + } + } + + if (ccdTask->mNbBodies != 0) + { + ccdTask->setContinuation(continuation); + ccdTask->removeReference(); + } + + //calculate contact distance for articulation links + SpeculativeCCDContactDistanceArticulationUpdateTask* articulationUpdateTask = NULL; + + Cm::BitMap::Iterator articulateCCDIter(mSpeculativeCDDArticulationBitMap); + while ((index = articulateCCDIter.getNext()) != Cm::BitMap::Iterator::DONE) + { + Sc::ArticulationSim* articulationSim = islandSim.getLLArticulation(IG::NodeIndex(index))->getArticulationSim(); + if (articulationSim) + { + hasContactDistanceChanged = true; + articulationUpdateTask = PX_PLACEMENT_NEW(pool.allocate(sizeof(SpeculativeCCDContactDistanceArticulationUpdateTask)), SpeculativeCCDContactDistanceArticulationUpdateTask)(getContextId(), mContactDistance->begin(), mDt, *mBoundsArray); + articulationUpdateTask->mArticulation = articulationSim; + articulationUpdateTask->setContinuation(continuation); + articulationUpdateTask->removeReference(); + } + } + + mHasContactDistanceChanged = hasContactDistanceChanged; + + //Process dirty shapeSims... + Cm::BitMap::Iterator dirtyShapeIter(mDirtyShapeSimMap); + + PxsTransformCache& cache = mLLContext->getTransformCache(); + Bp::BoundsArray& boundsArray = mAABBManager->getBoundsArray(); + + DirtyShapeUpdatesTask* task = PX_PLACEMENT_NEW(pool.allocate(sizeof(DirtyShapeUpdatesTask)), DirtyShapeUpdatesTask)(getContextId(), cache, boundsArray); + + bool hasDirtyShapes = false; + while ((index = dirtyShapeIter.getNext()) != Cm::BitMap::Iterator::DONE) + { + Sc::ShapeSim* shapeSim = reinterpret_cast(mAABBManager->getUserData(index)); + if (shapeSim) + { + hasDirtyShapes = true; + changedMap.growAndSet(index); + task->mShapes[task->mNbShapes++] = shapeSim; + if (task->mNbShapes == DirtyShapeUpdatesTask::MaxShapes) + { + task->setContinuation(continuation); + task->removeReference(); + task = PX_PLACEMENT_NEW(pool.allocate(sizeof(DirtyShapeUpdatesTask)), DirtyShapeUpdatesTask)(getContextId(), cache, boundsArray); + } + } + } + + if (hasDirtyShapes) + { + //Setting the boundsArray and transform cache as dirty so that they get DMAd to GPU if GPU dynamics and BP are being used respectively. + //These bits are no longer set when we update the cached state for actors due to an optimization avoiding setting these dirty bits multiple times. + getBoundsArray().setChangedState(); + getLowLevelContext()->getTransformCache().setChangedState(); + } + + if (task->mNbShapes != 0) + { + task->setContinuation(continuation); + task->removeReference(); + } + + mDirtyShapeSimMap.clear(); +} + +void Sc::Scene::rigidBodyNarrowPhase(PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Basic.narrowPhase", getContextId()); + + mCCDPass = 0; + + mPostBroadPhase3.addDependent(*continuation); + mPostBroadPhase2.setContinuation(&mPostBroadPhase3); + mPostBroadPhaseCont.setContinuation(&mPostBroadPhase2); + mPostBroadPhase.setContinuation(&mPostBroadPhaseCont); + mBroadPhase.setContinuation(&mPostBroadPhase); + mRigidBodyNPhaseUnlock.setContinuation(continuation); + mRigidBodyNPhaseUnlock.addReference(); + + mLLContext->resetThreadContexts(); + + mLLContext->updateContactManager(mDt, mBoundsArray->hasChanged(), mHasContactDistanceChanged, continuation, &mRigidBodyNPhaseUnlock); // Starts update of contact managers + + mPostBroadPhase3.removeReference(); + mPostBroadPhase2.removeReference(); + mPostBroadPhaseCont.removeReference(); + mPostBroadPhase.removeReference(); + mBroadPhase.removeReference(); +} + +void Sc::Scene::unblockNarrowPhase(PxBaseTask*) +{ + this->mLLContext->getNphaseImplementationContext()->startNarrowPhaseTasks(); +} + +void Sc::Scene::postNarrowPhase(PxBaseTask* /*continuation*/) +{ + mHasContactDistanceChanged = false; + mLLContext->fetchUpdateContactManager(); //Sync on contact gen results! + + releaseConstraints(false); + + PX_PROFILE_STOP_CROSSTHREAD("Basic.narrowPhase", getContextId()); + PX_PROFILE_STOP_CROSSTHREAD("Basic.collision", getContextId()); +} + +void Sc::Scene::fetchPatchEvents(PxBaseTask*) +{ + PxU32 foundPatchCount, lostPatchCount; + + { + PX_PROFILE_ZONE("Sim.preIslandGen.managerPatchEvents", getContextId()); + mLLContext->getManagerPatchEventCount(foundPatchCount, lostPatchCount); + + mFoundPatchManagers.forceSize_Unsafe(0); + mFoundPatchManagers.resizeUninitialized(foundPatchCount); + + mLostPatchManagers.forceSize_Unsafe(0); + mLostPatchManagers.resizeUninitialized(lostPatchCount); + + mLLContext->fillManagerPatchChangedEvents(mFoundPatchManagers.begin(), foundPatchCount, mLostPatchManagers.begin(), lostPatchCount); + + mFoundPatchManagers.forceSize_Unsafe(foundPatchCount); + mLostPatchManagers.forceSize_Unsafe(lostPatchCount); + } +} + +void Sc::Scene::processNarrowPhaseTouchEvents() +{ + PxsContext* context = mLLContext; + + { + PX_PROFILE_ZONE("Sim.preIslandGen", getContextId()); + + // Update touch states from LL + PxU32 newTouchCount, lostTouchCount; + PxU32 ccdTouchCount = 0; + { + PX_PROFILE_ZONE("Sim.preIslandGen.managerTouchEvents", getContextId()); + context->getManagerTouchEventCount(reinterpret_cast(&newTouchCount), reinterpret_cast(&lostTouchCount), NULL); + //PX_ALLOCA(newTouches, PxvContactManagerTouchEvent, newTouchCount); + //PX_ALLOCA(lostTouches, PxvContactManagerTouchEvent, lostTouchCount); + + mTouchFoundEvents.forceSize_Unsafe(0); + mTouchFoundEvents.reserve(newTouchCount); + mTouchFoundEvents.forceSize_Unsafe(newTouchCount); + + mTouchLostEvents.forceSize_Unsafe(0); + mTouchLostEvents.reserve(lostTouchCount); + mTouchLostEvents.forceSize_Unsafe(lostTouchCount); + + { + context->fillManagerTouchEvents(mTouchFoundEvents.begin(), reinterpret_cast(newTouchCount), mTouchLostEvents.begin(), + reinterpret_cast(lostTouchCount), NULL, reinterpret_cast(ccdTouchCount)); + + mTouchFoundEvents.forceSize_Unsafe(newTouchCount); + mTouchLostEvents.forceSize_Unsafe(lostTouchCount); + } + } + + context->getSimStats().mNbNewTouches = newTouchCount; + context->getSimStats().mNbLostTouches = lostTouchCount; + } +} + +class InteractionNewTouchTask : public Cm::Task +{ + PxvContactManagerTouchEvent* mEvents; + const PxU32 mNbEvents; + PxsContactManagerOutputIterator mOutputs; + const bool mUseAdaptiveForce; + +public: + InteractionNewTouchTask(PxU64 contextID, PxvContactManagerTouchEvent* events, PxU32 nbEvents, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) : + Cm::Task (contextID), + mEvents (events), + mNbEvents (nbEvents), + mOutputs (outputs), + mUseAdaptiveForce (useAdaptiveForce) + { + } + + virtual const char* getName() const + { + return "InteractionNewTouchTask"; + } + + void hackInContinuation(PxBaseTask* cont) + { + PX_ASSERT(mCont == NULL); + mCont = cont; + if (mCont) + mCont->addReference(); + } + + virtual void runInternal() + { + for (PxU32 i = 0; i < mNbEvents; ++i) + { + Sc::ShapeInteraction* si = reinterpret_cast(mEvents[i].userData); + PX_ASSERT(si); + si->managerNewTouch(0, true, mOutputs, mUseAdaptiveForce); + } + } +private: + PX_NOCOPY(InteractionNewTouchTask) +}; + +void Sc::Scene::processNarrowPhaseTouchEventsStage2(PxBaseTask* continuation) +{ + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + //Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + PxU32 newTouchCount = mTouchFoundEvents.size(); + + { + const PxU32 nbPerTask = 256; + PX_PROFILE_ZONE("Sim.preIslandGen.newTouches", getContextId()); + + InteractionNewTouchTask* prevTask = NULL; + + //for (PxU32 i = 0; i < newTouchCount; ++i) + for (PxU32 i = 0; i < newTouchCount; i+= nbPerTask) + { + const PxU32 nbToProcess = PxMin(newTouchCount - i, nbPerTask); + + for (PxU32 a = 0; a < nbToProcess; ++a) + { + ShapeInteraction* si = reinterpret_cast(mTouchFoundEvents[i + a].userData); + PX_ASSERT(si); + mNPhaseCore->managerNewTouch(*si); + si->managerNewTouch(0, true, outputs, useAdaptiveForce); + } + + //InteractionNewTouchTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(InteractionNewTouchTask)), InteractionNewTouchTask)(mTouchFoundEvents.begin() + i, nbToProcess, outputs); + ////task->setContinuation(continuation); + //task->setContinuation(*continuation->getTaskManager(), NULL); + //if (prevTask) + //{ + // prevTask->hackInContinuation(task); + // prevTask->removeReference(); + //} + + //prevTask = task; + + //task->removeReference(); + } + + if (prevTask) + { + prevTask->hackInContinuation(continuation); + prevTask->removeReference(); + } + } + + /*{ + PX_PROFILE_ZONE("Sim.preIslandGen.newTouchesInteraction", getContextId()); + for (PxU32 i = 0; i < newTouchCount; ++i) + { + ShapeInteraction* si = reinterpret_cast(mTouchFoundEvents[i].userData); + PX_ASSERT(si); + si->managerNewTouch(0, true, outputs); + } + }*/ + +} + +void Sc::Scene::setEdgesConnected(PxBaseTask*) +{ + { + PxU32 newTouchCount = mTouchFoundEvents.size(); + PX_PROFILE_ZONE("Sim.preIslandGen.islandTouches", getContextId()); + { + PX_PROFILE_ZONE("Sim.preIslandGen.setEdgesConnected", getContextId()); + for (PxU32 i = 0; i < newTouchCount; ++i) + { + ShapeInteraction* si = reinterpret_cast(mTouchFoundEvents[i].userData); + if (!si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) + { + mSimpleIslandManager->setEdgeConnected(si->getEdgeIndex()); + } + } + } + + mSimpleIslandManager->secondPassIslandGen(); + + wakeObjectsUp(ActorSim::AS_PART_OF_ISLAND_GEN); + } +} + +void Sc::Scene::processNarrowPhaseLostTouchEventsIslands(PxBaseTask*) +{ + { + PX_PROFILE_ZONE("Sc::Scene.islandLostTouches", getContextId()); + for (PxU32 i = 0; i < mTouchLostEvents.size(); ++i) + { + ShapeInteraction* si = reinterpret_cast(mTouchLostEvents[i].userData); + mSimpleIslandManager->setEdgeDisconnected(si->getEdgeIndex()); + } + } +} + +void Sc::Scene::processNarrowPhaseLostTouchEvents(PxBaseTask*) +{ + { + PX_PROFILE_ZONE("Sc::Scene.processNarrowPhaseLostTouchEvents", getContextId()); + PxsContactManagerOutputIterator outputs = this->mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + for (PxU32 i = 0; i < mTouchLostEvents.size(); ++i) + { + ShapeInteraction* si = reinterpret_cast(mTouchLostEvents[i].userData); + PX_ASSERT(si); + if (si->managerLostTouch(0, true, outputs, useAdaptiveForce) && !si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) + addToLostTouchList(si->getShape0().getBodySim(), si->getShape1().getBodySim()); + } + } +} + +void Sc::Scene::processLostSolverPatches(PxBaseTask* /*continuation*/) +{ + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + mDynamicsContext->processLostPatches(*mSimpleIslandManager, mLostPatchManagers.begin(), mLostPatchManagers.size(), outputs); +} + +void Sc::Scene::islandGen(PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Basic.rigidBodySolver", getContextId()); + + //mLLContext->runModifiableContactManagers(); //KS - moved here so that we can get up-to-date touch found/lost events in IG + + mProcessLostPatchesTask.setContinuation(&mUpdateDynamics); + mFetchPatchEventsTask.setContinuation(&mProcessLostPatchesTask); + mProcessLostPatchesTask.removeReference(); + mFetchPatchEventsTask.removeReference(); + processNarrowPhaseTouchEvents(); + + mSetEdgesConnectedTask.setContinuation(continuation); + mSetEdgesConnectedTask.removeReference(); + + processNarrowPhaseTouchEventsStage2(continuation); +} + +PX_FORCE_INLINE void Sc::Scene::putObjectsToSleep(PxU32 infoFlag) +{ + PX_PROFILE_ZONE("Sc::Scene::putObjectsToSleep", getContextId()); + const IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + //Set to sleep all bodies that were in awake islands that have just been put to sleep. + const PxU32 nbBodiesToSleep = islandSim.getNbNodesToDeactivate(IG::Node::eRIGID_BODY_TYPE); + const IG::NodeIndex*const bodyIndices = islandSim.getNodesToDeactivate(IG::Node::eRIGID_BODY_TYPE); + + for(PxU32 i=0;i(reinterpret_cast(rigidBody) - Sc::BodySim::getRigidBodyOffset()); + bodySim->setActive(false, infoFlag); + } + } + + const PxU32 nbArticulationsToSleep = islandSim.getNbNodesToDeactivate(IG::Node::eARTICULATION_TYPE); + const IG::NodeIndex*const articIndices = islandSim.getNodesToDeactivate(IG::Node::eARTICULATION_TYPE); + + for(PxU32 i=0;igetArticulationSim(); + if (articSim && !islandSim.getNode(articIndices[i]).isActive()) + articSim->setActive(false, infoFlag); + } +} + +PX_FORCE_INLINE void Sc::Scene::putInteractionsToSleep() +{ + PX_PROFILE_ZONE("Sc::Scene::putInteractionsToSleep", getContextId()); + const IG::IslandSim& islandSim = mSimpleIslandManager->getSpeculativeIslandSim(); + + //KS - only deactivate contact managers based on speculative state to trigger contact gen. When the actors were deactivated based on accurate state + //joints should have been deactivated. + + { + PxU32 nbDeactivatingEdges = islandSim.getNbDeactivatingEdges(IG::Edge::eCONTACT_MANAGER); + const IG::EdgeIndex* deactivatingEdgeIds = islandSim.getDeactivatingEdges(IG::Edge::eCONTACT_MANAGER); + + for (PxU32 i = 0; i < nbDeactivatingEdges; ++i) + { + Sc::Interaction* interaction = mSimpleIslandManager->getInteraction(deactivatingEdgeIds[i]); + + if (interaction && interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE)) + { + if (!islandSim.getEdge(deactivatingEdgeIds[i]).isActive()) + { + const bool proceed = deactivateInteraction(interaction); + if (proceed && (interaction->getType() < InteractionType::eTRACKED_IN_SCENE_COUNT)) + notifyInteractionDeactivated(interaction); + } + } + } + } +} + +PX_FORCE_INLINE void Sc::Scene::wakeObjectsUp(PxU32 infoFlag) +{ + //Wake up all bodies that were in sleeping islands that have just been hit by a moving object. + + const IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + const PxU32 nbBodiesToWake = islandSim.getNbNodesToActivate(IG::Node::eRIGID_BODY_TYPE); + const IG::NodeIndex*const bodyIndices = islandSim.getNodesToActivate(IG::Node::eRIGID_BODY_TYPE); + + for(PxU32 i=0;i(reinterpret_cast(rigidBody) - Sc::BodySim::getRigidBodyOffset()); + bodySim->setActive(true, infoFlag); + } + } + + const PxU32 nbArticulationsToWake = islandSim.getNbNodesToActivate(IG::Node::eARTICULATION_TYPE); + const IG::NodeIndex*const articIndices = islandSim.getNodesToActivate(IG::Node::eARTICULATION_TYPE); + + for(PxU32 i=0;igetArticulationSim(); + if (articSim && islandSim.getNode(articIndices[i]).isActive()) + articSim->setActive(true, infoFlag); + } +} + +void Sc::Scene::postIslandGen(PxBaseTask* continuationTask) +{ + { + PX_PROFILE_ZONE("Sim.postIslandGen", getContextId()); + + // - Performs collision detection for trigger interactions + { + mNPhaseCore->processTriggerInteractions(continuationTask); + } + } +} + +void Sc::Scene::solver(PxBaseTask* continuation) +{ + PX_PROFILE_STOP_CROSSTHREAD("Basic.narrowPhase", getContextId()); + PX_PROFILE_START_CROSSTHREAD("Basic.rigidBodySolver", getContextId()); + //Update forces per body in parallel. This can overlap with the other work in this phase. + beforeSolver(continuation); + + PX_PROFILE_ZONE("Sim.postNarrowPhaseSecondPass", getContextId()); + //Narrowphase is completely finished so the streams can be swapped. + mLLContext->swapStreams(); + + //PxsContactManagerOutputIterator outputs = this->mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + //mNPhaseCore->processPersistentContactEvents(outputs, continuation); +} + +void Sc::Scene::updateBodiesAndShapes(PxBaseTask* continuation) +{ + PX_UNUSED(continuation); + //dma bodies and shapes data to gpu + mSimulationController->updateBodiesAndShapes(continuation); +} + +Cm::FlushPool* Sc::Scene::getFlushPool() +{ + return &mLLContext->getTaskPool(); +} + +void Sc::Scene::postThirdPassIslandGen(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sc::Scene::postThirdPassIslandGen", getContextId()); + putObjectsToSleep(ActorSim::AS_PART_OF_ISLAND_GEN); + putInteractionsToSleep(); + + PxsContactManagerOutputIterator outputs = this->mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + mNPhaseCore->processPersistentContactEvents(outputs, continuation); +} + +void Sc::Scene::processLostContacts(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sc::Scene::processLostContacts", getContextId()); + mProcessNarrowPhaseLostTouchTasks.setContinuation(continuation); + mProcessNarrowPhaseLostTouchTasks.removeReference(); + + //mLostTouchReportsTask.setContinuation(&mProcessLostContactsTask3); + mProcessNPLostTouchEvents.setContinuation(continuation); + mProcessNPLostTouchEvents.removeReference(); + + { + PX_PROFILE_ZONE("Sim.findInteractionsPtrs", getContextId()); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + ElementSim* volume0 = reinterpret_cast(p->mUserData0); + ElementSim* volume1 = reinterpret_cast(p->mUserData1); + p->mPairUserData = mNPhaseCore->onOverlapRemovedStage1(volume0, volume1); + p++; + } + } +} + +void Sc::Scene::lostTouchReports(PxBaseTask*) +{ + { + PX_PROFILE_ZONE("Sim.lostTouchReports", getContextId()); + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + + { + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + if(p->mPairUserData) + { + Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); + if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP) + mNPhaseCore->lostTouchReports(static_cast(elemInteraction), PxU32(PairReleaseFlag::eWAKE_ON_LOST_TOUCH), 0, outputs, useAdaptiveForce); + } + p++; + } + } + } +} + +void Sc::Scene::unregisterInteractions(PxBaseTask*) +{ + { + PX_PROFILE_ZONE("Sim.unregisterInteractions", getContextId()); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + + { + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + if(p->mPairUserData) + { + Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); + if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP || elemInteraction->getType() == Sc::InteractionType::eMARKER) + { + unregisterInteraction(elemInteraction); + mNPhaseCore->unregisterInteraction(elemInteraction); + } + } + p++; + } + } + } +} + +void Sc::Scene::destroyManagers(PxBaseTask*) +{ + { + PX_PROFILE_ZONE("Sim.destroyManagers", getContextId()); + + mPostThirdPassIslandGenTask.setContinuation(mProcessLostContactsTask3.getContinuation()); + + mSimpleIslandManager->thirdPassIslandGen(&mPostThirdPassIslandGenTask); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + if(p->mPairUserData) + { + Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); + if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP) + { + Sc::ShapeInteraction* si = static_cast(elemInteraction); + if(si->getContactManager()) + si->destroyManager(); + } + } + p++; + } + } +} + +void Sc::Scene::processLostContacts2(PxBaseTask* continuation) +{ + mDestroyManagersTask.setContinuation(continuation); + mLostTouchReportsTask.setContinuation(&mDestroyManagersTask); + mLostTouchReportsTask.removeReference(); + + + mUnregisterInteractionsTask.setContinuation(continuation); + mUnregisterInteractionsTask.removeReference(); + + { + PX_PROFILE_ZONE("Sim.clearIslandData", getContextId()); +// PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + { + Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + Sc::ElementSimInteraction* pair = reinterpret_cast(p->mPairUserData); + if(pair) + { + if(pair->getType() == InteractionType::eOVERLAP) + { + ShapeInteraction* si = static_cast(pair); + si->clearIslandGenData(); + } + } + p++; + } + } + } + + mDestroyManagersTask.removeReference(); +} + +void Sc::Scene::processLostContacts3(PxBaseTask* /*continuation*/) +{ + { + PX_PROFILE_ZONE("Sim.processLostOverlapsStage2", getContextId()); + + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + + { + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + ElementSim* volume0 = reinterpret_cast(p->mUserData0); + ElementSim* volume1 = reinterpret_cast(p->mUserData1); + + mNPhaseCore->onOverlapRemoved(volume0, volume1, false, p->mPairUserData, outputs, useAdaptiveForce); + p++; + } + } + + { + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eTRIGGER, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + ElementSim* volume0 = reinterpret_cast(p->mUserData0); + ElementSim* volume1 = reinterpret_cast(p->mUserData1); + + mNPhaseCore->onOverlapRemoved(volume0, volume1, false, NULL, outputs, useAdaptiveForce); + p++; + } + } + + aabbMgr->getBroadPhase()->deletePairs(); + aabbMgr->freeBuffers(); + } + + mPostThirdPassIslandGenTask.removeReference(); +} + +//This is called after solver finish +void Sc::Scene::updateSimulationController(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateSimulationController", getContextId()); + //for pxgdynamicscontext: copy solver body data to body core + mDynamicsContext->updateBodyCore(continuation); + + PxsTransformCache& cache = getLowLevelContext()->getTransformCache(); + Bp::BoundsArray& boundArray = getBoundsArray(); + + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + //changedAABBMgrActorHandles.resizeAndClear(getElementIDPool().getMaxID()); + + mSimulationController->gpuDmabackData(cache, boundArray, changedAABBMgrActorHandles); + //mSimulationController->update(cache, boundArray, changedAABBMgrActorHandles); +} + +void Sc::Scene::updateDynamics(PxBaseTask* continuation) +{ + //Allow processLostContactsTask to run until after 2nd pass of solver completes (update bodies, run sleeping logic etc.) + mProcessLostContactsTask3.setContinuation(static_cast(continuation)->getContinuation()); + mProcessLostContactsTask2.setContinuation(&mProcessLostContactsTask3); + mProcessLostContactsTask.setContinuation(&mProcessLostContactsTask2); + + ////dma bodies and shapes data to gpu + //mSimulationController->updateBodiesAndShapes(); + + mLLContext->getNpMemBlockPool().acquireConstraintMemory(); + + PX_PROFILE_START_CROSSTHREAD("Basic.dynamics", getContextId()); + PxU32 maxPatchCount = mLLContext->getMaxPatchCount(); + + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + PxsContactManagerOutput* cmOutputBase = mLLContext->getNphaseImplementationContext()->getGPUContactManagerOutputBase(); + + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + changedAABBMgrActorHandles.resizeAndClear(getElementIDPool().getMaxID()); + + //mNPhaseCore->processPersistentContactEvents(outputs, continuation); + + mDynamicsContext->update(*mSimpleIslandManager, continuation, &mProcessLostContactsTask, + mFoundPatchManagers.begin(), mFoundPatchManagers.size(), mLostPatchManagers.begin(), mLostPatchManagers.size(), + maxPatchCount, outputs, cmOutputBase, mDt, mGravity, changedAABBMgrActorHandles.getWordCount()); + + mSimpleIslandManager->clearDestroyedEdges(); + + mProcessLostContactsTask3.removeReference(); + mProcessLostContactsTask2.removeReference(); + mProcessLostContactsTask.removeReference(); +} + +void Sc::Scene::updateCCDMultiPass(PxBaseTask* parentContinuation) +{ + getCcdBodies().forceSize_Unsafe(mSimulationControllerCallback->getNbCcdBodies()); + + // second run of the broadphase for making sure objects we have integrated did not tunnel. + if(mPublicFlags & PxSceneFlag::eENABLE_CCD) + { + if (mContactReportsNeedPostSolverVelocity) + { + // the CCD code will overwrite the post solver body velocities, hence, we need to extract the info + // first if any CCD enabled pair requested it. + collectPostSolverVelocitiesBeforeCCD(); + } + + //We use 2 CCD task chains to be able to chain together an arbitrary number of ccd passes + if(mPostCCDPass.size() != 2) + { + mPostCCDPass.clear(); + mUpdateCCDSinglePass.clear(); + mCCDBroadPhase.clear(); + mCCDBroadPhaseAABB.clear(); + mPostCCDPass.reserve(2); + mUpdateCCDSinglePass.reserve(2); + mUpdateCCDSinglePass2.reserve(2); + mUpdateCCDSinglePass3.reserve(2); + mCCDBroadPhase.reserve(2); + mCCDBroadPhaseAABB.reserve(2); + for (int j = 0; j < 2; j++) + { + mPostCCDPass.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.postCCDPass")); + mUpdateCCDSinglePass.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.updateCCDSinglePass")); + mUpdateCCDSinglePass2.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.updateCCDSinglePassStage2")); + mUpdateCCDSinglePass3.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.updateCCDSinglePassStage3")); + mCCDBroadPhase.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.ccdBroadPhase")); + mCCDBroadPhaseAABB.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.ccdBroadPhaseAABB")); + } + } + + //reset thread context in a place we know all tasks possibly accessing it, are in sync with. (see US6664) + mLLContext->resetThreadContexts(); + + mCCDContext->updateCCDBegin(); + + mCCDBroadPhase[0].setContinuation(parentContinuation); + mCCDBroadPhaseAABB[0].setContinuation(&mCCDBroadPhase[0]); + mCCDBroadPhase[0].removeReference(); + mCCDBroadPhaseAABB[0].removeReference(); + } +} + +class UpdateCCDBoundsTask : public Cm::Task +{ + + Sc::BodySim** mBodySims; + PxU32 mNbToProcess; + PxI32* mNumFastMovingShapes; + +public: + + static const PxU32 MaxPerTask = 256; + + UpdateCCDBoundsTask(PxU64 contextID, Sc::BodySim** bodySims, PxU32 nbToProcess, PxI32* numFastMovingShapes) : + Cm::Task (contextID), + mBodySims (bodySims), + mNbToProcess (nbToProcess), + mNumFastMovingShapes(numFastMovingShapes) + { + } + + virtual const char* getName() const { return "UpdateCCDBoundsTask";} + + virtual void runInternal() + { + PxU32 activeShapes = 0; + for (PxU32 i = 0; i < mNbToProcess; i++) + { + PxU32 isFastMoving = 0; + Sc::BodySim& bodySim = *mBodySims[i]; + + Sc::ElementSim* current = bodySim.getElements_(); + while(current) + { + Sc::ShapeSim* sim = static_cast(current); + if(sim->getFlags()&PxU32(PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) + { + Ps::IntBool fastMovingShape = sim->updateSweptBounds(); + activeShapes += fastMovingShape; + + isFastMoving = isFastMoving | fastMovingShape; + } + current = current->mNextInActor; + } + + bodySim.getLowLevelBody().getCore().isFastMoving = PxU16(isFastMoving); + } + + Ps::atomicAdd(mNumFastMovingShapes, PxI32(activeShapes)); + } +}; + +void Sc::Scene::ccdBroadPhaseAABB(PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Sim.ccdBroadPhaseComplete", getContextId()); + PX_PROFILE_ZONE("Sim.ccdBroadPhaseAABB", getContextId()); + PX_UNUSED(continuation); + + PxU32 currentPass = mCCDContext->getCurrentCCDPass(); + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + mNumFastMovingShapes = 0; + + //If we are on the 1st pass or we had some sweep hits previous CCD pass, we need to run CCD again + if( currentPass == 0 || mCCDContext->getNumSweepHits()) + { + for (PxU32 i = 0; i < mCcdBodies.size(); i+= UpdateCCDBoundsTask::MaxPerTask) + { + const PxU32 nbToProcess = PxMin(UpdateCCDBoundsTask::MaxPerTask, mCcdBodies.size() - i); + UpdateCCDBoundsTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(UpdateCCDBoundsTask)), UpdateCCDBoundsTask)(getContextId(), &mCcdBodies[i], nbToProcess, &mNumFastMovingShapes); + task->setContinuation(continuation); + task->removeReference(); + } + } +} + +void Sc::Scene::ccdBroadPhase(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.ccdBroadPhase", getContextId()); + + PxU32 currentPass = mCCDContext->getCurrentCCDPass(); + const PxU32 ccdMaxPasses = mCCDContext->getCCDMaxPasses(); + mCCDPass = currentPass+1; + + //If we are on the 1st pass or we had some sweep hits previous CCD pass, we need to run CCD again + if( (currentPass == 0 || mCCDContext->getNumSweepHits()) && mNumFastMovingShapes != 0) + { + const PxU32 currIndex = currentPass & 1; + const PxU32 nextIndex = 1 - currIndex; + //Initialize the CCD task chain unless this is the final pass + if(currentPass != (ccdMaxPasses - 1)) + { + mCCDBroadPhase[nextIndex].setContinuation(continuation); + mCCDBroadPhaseAABB[nextIndex].setContinuation(&mCCDBroadPhase[nextIndex]); + } + mPostCCDPass[currIndex].setContinuation(currentPass == ccdMaxPasses-1 ? continuation : &mCCDBroadPhaseAABB[nextIndex]); + mUpdateCCDSinglePass3[currIndex].setContinuation(&mPostCCDPass[currIndex]); + mUpdateCCDSinglePass2[currIndex].setContinuation(&mUpdateCCDSinglePass3[currIndex]); + mUpdateCCDSinglePass[currIndex].setContinuation(&mUpdateCCDSinglePass2[currIndex]); + + //Do the actual broad phase + PxBaseTask* continuationTask = &mUpdateCCDSinglePass[currIndex]; + const PxU32 numCpuTasks = continuationTask->getTaskManager()->getCpuDispatcher()->getWorkerCount(); + + mAABBManager->updateAABBsAndBP(numCpuTasks, mLLContext->getTaskPool(), &mLLContext->getScratchAllocator(), false, continuationTask, NULL); + + //Allow the CCD task chain to continue + mPostCCDPass[currIndex].removeReference(); + mUpdateCCDSinglePass3[currIndex].removeReference(); + mUpdateCCDSinglePass2[currIndex].removeReference(); + mUpdateCCDSinglePass[currIndex].removeReference(); + if(currentPass != (ccdMaxPasses - 1)) + { + mCCDBroadPhase[nextIndex].removeReference(); + mCCDBroadPhaseAABB[nextIndex].removeReference(); + } + } + else if (currentPass == 0) + { + PX_PROFILE_STOP_CROSSTHREAD("Sim.ccdBroadPhaseComplete", getContextId()); + mCCDContext->resetContactManagers(); + } +} + +void Sc::Scene::updateCCDSinglePass(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateCCDSinglePass", getContextId()); + mReportShapePairTimeStamp++; // This will makes sure that new report pairs will get created instead of re-using the existing ones. + + mAABBManager->postBroadPhase(NULL, NULL, *getFlushPool()); + finishBroadPhase(continuation); + + const PxU32 currentPass = mCCDContext->getCurrentCCDPass() + 1; // 0 is reserved for discrete collision phase + if(currentPass == 1) // reset the handle map so we only update CCD objects from here on + { + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + //changedAABBMgrActorHandles.clear(); + for(PxU32 i = 0; i < mCcdBodies.size();i++) + { + Sc::ElementSim* current = mCcdBodies[i]->getElements_(); + while(current) + { + Sc::ShapeSim* sim = static_cast(current); + if(sim->getFlags()&PxU32(PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) // TODO: need trigger shape here? + changedAABBMgrActorHandles.growAndSet(sim->getElementID()); + current = current->mNextInActor; + } + } + } +} + +void Sc::Scene::updateCCDSinglePassStage2(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateCCDSinglePassStage2", getContextId()); + postBroadPhaseStage2(continuation); +} + +void Sc::Scene::updateCCDSinglePassStage3(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateCCDSinglePassStage3", getContextId()); + mReportShapePairTimeStamp++; // This will makes sure that new report pairs will get created instead of re-using the existing ones. + + const PxU32 currentPass = mCCDContext->getCurrentCCDPass() + 1; // 0 is reserved for discrete collision phase + finishBroadPhaseStage2(currentPass); + PX_PROFILE_STOP_CROSSTHREAD("Sim.ccdBroadPhaseComplete", getContextId()); + + //reset thread context in a place we know all tasks possibly accessing it, are in sync with. (see US6664) + mLLContext->resetThreadContexts(); + + mCCDContext->updateCCD(mDt, continuation, mSimpleIslandManager->getAccurateIslandSim(), (mPublicFlags & PxSceneFlag::eDISABLE_CCD_RESWEEP), mNumFastMovingShapes); +} + +class ScKinematicPoseUpdateTask : public Cm::Task +{ + Sc::BodyCore*const* mKinematics; + PxU32 mNbKinematics; + +public: + static const PxU32 NbKinematicsPerTask = 1024; + + ScKinematicPoseUpdateTask(Sc::BodyCore*const* kinematics, PxU32 nbKinematics, PxU64 contextID) : + Cm::Task(contextID), mKinematics(kinematics), mNbKinematics(nbKinematics) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbKinematics; ++a) + { + if ((a + 16) < mNbKinematics) + { + Ps::prefetchLine(mKinematics[a + 16]); + + if ((a + 4) < mNbKinematics) + { + Ps::prefetchLine(mKinematics[a + 4]->getSim()); + Ps::prefetchLine(mKinematics[a + 4]->getSimStateData_Unchecked()); + } + } + Sc::BodyCore* b = mKinematics[a]; + PX_ASSERT(b->getSim()->isKinematic()); + PX_ASSERT(b->getSim()->isActive()); + b->getSim()->updateKinematicPose(); + } + } + + virtual const char* getName() const + { + return "ScScene.ScKinematicPoseUpdateTask"; + } +}; + +void Sc::Scene::integrateKinematicPose() +{ + PX_PROFILE_ZONE("Sim.integrateKinematicPose", getContextId()); + + PxU32 nbKinematics = getActiveKinematicBodiesCount(); + BodyCore*const* kinematics = getActiveKinematicBodies(); + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + for(PxU32 i = 0; i < nbKinematics; i+= ScKinematicPoseUpdateTask::NbKinematicsPerTask) + { + ScKinematicPoseUpdateTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicPoseUpdateTask)), ScKinematicPoseUpdateTask) + (kinematics + i, PxMin(nbKinematics - i, ScKinematicPoseUpdateTask::NbKinematicsPerTask), mContextId); + task->setContinuation(&mConstraintProjection); + task->removeReference(); + } +} + +class ScKinematicShapeUpdateTask : public Cm::Task +{ + Sc::BodyCore*const* mKinematics; + PxU32 mNbKinematics; + PxsTransformCache& mCache; + Bp::BoundsArray& mBoundsArray; + + PX_NOCOPY(ScKinematicShapeUpdateTask) + +public: + static const PxU32 NbKinematicsShapesPerTask = 1024; + + ScKinematicShapeUpdateTask(Sc::BodyCore*const* kinematics, PxU32 nbKinematics, PxsTransformCache& cache, Bp::BoundsArray& boundsArray, PxU64 contextID) : + Cm::Task(contextID), mKinematics(kinematics), mNbKinematics(nbKinematics), mCache(cache), mBoundsArray(boundsArray) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbKinematics; ++a) + { + Sc::BodyCore* b = mKinematics[a]; + PX_ASSERT(b->getSim()->isKinematic()); + PX_ASSERT(b->getSim()->isActive()); + + b->getSim()->updateCached(mCache, mBoundsArray); + } + } + + virtual const char* getName() const + { + return "ScScene.KinematicShapeUpdateTask"; + } +}; + +void Sc::Scene::updateKinematicCached(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateKinematicCached", getContextId()); + + PxU32 nbKinematics = getActiveKinematicBodiesCount(); + BodyCore*const* kinematics = getActiveKinematicBodies(); + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + + PxU32 startIndex = 0; + PxU32 nbShapes = 0; + + { + PX_PROFILE_ZONE("ShapeUpdate", getContextId()); + for (PxU32 i = 0; i < nbKinematics; i++) + { + BodyCore* b = kinematics[i]; + BodySim* sim = b->getSim(); + PX_ASSERT(sim->isKinematic()); + PX_ASSERT(sim->isActive()); + + nbShapes += sim->getNbShapes(); + + if (nbShapes >= ScKinematicShapeUpdateTask::NbKinematicsShapesPerTask) + { + ScKinematicShapeUpdateTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicShapeUpdateTask)), ScKinematicShapeUpdateTask) + (kinematics + startIndex, (i + 1) - startIndex, mLLContext->getTransformCache(), *mBoundsArray, mContextId); + + task->setContinuation(continuation); + task->removeReference(); + startIndex = i + 1; + nbShapes = 0; + } + } + + if (nbShapes) + { + ScKinematicShapeUpdateTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicShapeUpdateTask)), ScKinematicShapeUpdateTask) + (kinematics + startIndex, nbKinematics - startIndex, mLLContext->getTransformCache(), *mBoundsArray, mContextId); + + task->setContinuation(continuation); + task->removeReference(); + } + } + + if (nbKinematics) + { + Cm::BitMapPinned& changedAABBMap = mAABBManager->getChangedAABBMgActorHandleMap(); + mLLContext->getTransformCache().setChangedState(); + mBoundsArray->setChangedState(); + for (PxU32 i = 0; i < nbKinematics; ++i) + { + Sc::BodySim* bodySim = kinematics[i]->getSim(); + + if ((i+16) < nbKinematics) + { + Ps::prefetchLine(kinematics[i + 16]); + if ((i + 8) < nbKinematics) + { + Ps::prefetchLine(kinematics[i + 8]->getSim()); + } + if ((i + 4) < nbKinematics) + { + Ps::prefetchLine(kinematics[i + 4]->getSim()->getElements_()); + } + } + + Sc::ElementSim* current = bodySim->getElements_(); + while(current) + { + Sc::ShapeSim* sim = static_cast(current); + //KS - TODO - can we parallelize this? The problem with parallelizing is that it's a bit operation, + //so we would either need to use atomic operations or have some high-level concept that guarantees + //that threads don't write to the same word in the map simultaneously + if (sim->getFlags()&PxU32(PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) + { + changedAABBMap.set(sim->getElementID()); + } + current = current->mNextInActor; + } + + mSimulationController->updateDynamic(false, bodySim->getNodeIndex()); + } + } +} + +class ConstraintProjectionTask : public Cm::Task +{ +private: + ConstraintProjectionTask& operator = (const ConstraintProjectionTask&); + +public: + ConstraintProjectionTask(Sc::ConstraintGroupNode* const* projectionRoots, PxU32 projectionRootCount, Ps::Array& projectedBodies, PxsContext* llContext) : + Cm::Task (llContext->getContextId()), + mProjectionRoots (projectionRoots), + mProjectionRootCount(projectionRootCount), + mProjectedBodies (projectedBodies), + mLLContext (llContext) + { + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("ConstraintProjection", mContextID); + PxcNpThreadContext* context = mLLContext->getNpThreadContext(); + Ps::Array& tempArray = context->mBodySimPool; + tempArray.forceSize_Unsafe(0); + for(PxU32 i=0; i < mProjectionRootCount; i++) + { + PX_ASSERT(mProjectionRoots[i]->hasProjectionTreeRoot()); // else, it must not be in the projection root list + Sc::ConstraintGroupNode::projectPose(*mProjectionRoots[i], tempArray); + mProjectionRoots[i]->clearFlag(Sc::ConstraintGroupNode::eIN_PROJECTION_PASS_LIST); + } + + if (tempArray.size() > 0) + { + mLLContext->getLock().lock(); + for (PxU32 a = 0; a < tempArray.size(); ++a) + mProjectedBodies.pushBack(tempArray[a]); + mLLContext->getLock().unlock(); + } + + mLLContext->putNpThreadContext(context); + } + + virtual const char* getName() const + { + return "ScScene.constraintProjectionWork"; + } + +public: + static const PxU32 sProjectingConstraintsPerTask = 256; // just a guideline, will not match exactly most of the time + +private: + Sc::ConstraintGroupNode* const* mProjectionRoots; + const PxU32 mProjectionRootCount; + Ps::Array& mProjectedBodies; + PxsContext* mLLContext; +}; + +void Sc::Scene::constraintProjection(PxBaseTask* continuation) +{ + if (mConstraints.size() == 0) + return; + PxU32 constraintGroupRootCount = 0; + //BodyCore*const* activeBodies = getActiveBodiesArray(); + //PxU32 activeBodyCount = getNumActiveBodies(); + IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + PxU32 activeBodyCount = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + const IG::NodeIndex* activeNodeIds = islandSim.getActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + PX_ASSERT(!mTmpConstraintGroupRootBuffer); + PxU32 index = 0; + + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + if(activeBodyCount) + { + mTmpConstraintGroupRootBuffer = reinterpret_cast(mLLContext->getScratchAllocator().alloc(sizeof(ConstraintGroupNode*) * activeBodyCount, true)); + if(mTmpConstraintGroupRootBuffer) + { + while(activeBodyCount--) + { + PxsRigidBody* rBody = islandSim.getRigidBody(activeNodeIds[index++]); + + Sc::BodySim* sim = reinterpret_cast(reinterpret_cast(rBody) - rigidBodyOffset); + //This move to PxgPostSolveWorkerTask for the gpu dynamic + //bodySim->sleepCheck(mDt, mOneOverDt, mEnableStabilization); + + if(sim->getConstraintGroup()) + { + ConstraintGroupNode& root = sim->getConstraintGroup()->getRoot(); + if(!root.readFlag(ConstraintGroupNode::eIN_PROJECTION_PASS_LIST) && root.hasProjectionTreeRoot()) + { + mTmpConstraintGroupRootBuffer[constraintGroupRootCount++] = &root; + root.raiseFlag(ConstraintGroupNode::eIN_PROJECTION_PASS_LIST); + } + } + } + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + PxU32 constraintsToProjectCount = 0; + PxU32 startIndex = 0; + for(PxU32 i=0; i < constraintGroupRootCount; i++) + { + ConstraintGroupNode* root = mTmpConstraintGroupRootBuffer[i]; + + constraintsToProjectCount += root->getProjectionCountHint(); // for load balancing + if (constraintsToProjectCount >= ConstraintProjectionTask::sProjectingConstraintsPerTask) + { + ConstraintProjectionTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ConstraintProjectionTask)), + ConstraintProjectionTask(mTmpConstraintGroupRootBuffer + startIndex, i - startIndex + 1, mProjectedBodies, mLLContext)); + task->setContinuation(continuation); + task->removeReference(); + + constraintsToProjectCount = 0; + startIndex = i + 1; + } + } + + if (constraintsToProjectCount) + { + PX_ASSERT(startIndex < constraintGroupRootCount); + + ConstraintProjectionTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ConstraintProjectionTask)), + ConstraintProjectionTask(mTmpConstraintGroupRootBuffer + startIndex, constraintGroupRootCount - startIndex, mProjectedBodies, mLLContext)); + task->setContinuation(continuation); + task->removeReference(); + } + } + else + { + getFoundation().getErrorCallback().reportError(PxErrorCode::eOUT_OF_MEMORY, "List for collecting constraint projection roots could not be allocated. No projection will take place.", __FILE__, __LINE__); + } + } +} + +void Sc::Scene::postSolver(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sc::Scene::postSolver", getContextId()); + PxcNpMemBlockPool& blockPool = mLLContext->getNpMemBlockPool(); + + //Merge... + mDynamicsContext->mergeResults(); + blockPool.releaseConstraintMemory(); + //Swap friction! + blockPool.swapFrictionStreams(); + + mCcdBodies.clear(); + mProjectedBodies.clear(); + +#if PX_ENABLE_SIM_STATS + mLLContext->getSimStats().mPeakConstraintBlockAllocations = blockPool.getPeakConstraintBlockCount(); +#endif + + mConstraintProjection.setContinuation(continuation); + + integrateKinematicPose(); + + mConstraintProjection.removeReference(); + + //afterIntegration(continuation); +} + +void Sc::Scene::postCCDPass(PxBaseTask* /*continuation*/) +{ + // - Performs sleep check + // - Updates touch flags + + PxU32 currentPass = mCCDContext->getCurrentCCDPass(); + PX_ASSERT(currentPass > 0); // to make sure changes to the CCD pass counting get noticed. For contact reports, 0 means discrete collision phase. + + int newTouchCount, lostTouchCount, ccdTouchCount; + mLLContext->getManagerTouchEventCount(&newTouchCount, &lostTouchCount, &ccdTouchCount); + PX_ALLOCA(newTouches, PxvContactManagerTouchEvent, newTouchCount); + PX_ALLOCA(lostTouches, PxvContactManagerTouchEvent, lostTouchCount); + PX_ALLOCA(ccdTouches, PxvContactManagerTouchEvent, ccdTouchCount); + + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + // Note: For contact notifications it is important that the new touch pairs get processed before the lost touch pairs. + // This allows to know for sure if a pair of actors lost all touch (see eACTOR_PAIR_LOST_TOUCH). + mLLContext->fillManagerTouchEvents(newTouches, newTouchCount, lostTouches, lostTouchCount, ccdTouches, ccdTouchCount); + for(PxI32 i=0; i(newTouches[i].userData); + PX_ASSERT(si); + mNPhaseCore->managerNewTouch(*si); + si->managerNewTouch(currentPass, true, outputs, useAdaptiveForce); + if (!si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) + { + mSimpleIslandManager->setEdgeConnected(si->getEdgeIndex()); + } + } + for(PxI32 i=0; i(lostTouches[i].userData); + PX_ASSERT(si); + if (si->managerLostTouch(currentPass, true, outputs, useAdaptiveForce) && !si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) + addToLostTouchList(si->getShape0().getBodySim(), si->getShape1().getBodySim()); + + mSimpleIslandManager->setEdgeDisconnected(si->getEdgeIndex()); + } + for(PxI32 i=0; i(ccdTouches[i].userData); + PX_ASSERT(si); + si->sendCCDRetouch(currentPass, outputs); + } + checkForceThresholdContactEvents(currentPass); + { + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + + for (PxU32 i = 0, s = mCcdBodies.size(); i < s; i++) + { + BodySim*const body = mCcdBodies[i]; + if(i+8 < s) + Ps::prefetch(mCcdBodies[i+8], 512); + + PX_ASSERT(body->getBody2World().p.isFinite()); + PX_ASSERT(body->getBody2World().q.isFinite()); + + body->updateCached(&changedAABBMgrActorHandles); + } + + ArticulationCore* const* articList = mArticulations.getEntries(); + for(PxU32 i=0;igetSim()->updateCached(&changedAABBMgrActorHandles); + } + } +} + +void Sc::Scene::finalizationPhase(PxBaseTask* /*continuation*/) +{ + PX_PROFILE_ZONE("Sim.sceneFinalization", getContextId()); + + if (mCCDContext) + { + //KS - force simulation controller to update any bodies updated by the CCD. When running GPU simulation, this would be required + //to ensure that cached body states are updated + const PxU32 nbUpdatedBodies = mCCDContext->getNumUpdatedBodies(); + PxsRigidBody*const* updatedBodies = mCCDContext->getUpdatedBodies(); + + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + for (PxU32 a = 0; a < nbUpdatedBodies; ++a) + { + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(updatedBodies[a]) - rigidBodyOffset); + mSimulationController->updateDynamic(bodySim->isArticulationLink(), bodySim->getNodeIndex()); + } + + mCCDContext->clearUpdatedBodies(); + } + + if (mTmpConstraintGroupRootBuffer) + { + mLLContext->getScratchAllocator().free(mTmpConstraintGroupRootBuffer); + mTmpConstraintGroupRootBuffer = NULL; + } + + fireOnAdvanceCallback(); // placed here because it needs to be done after sleep check and afrer potential CCD passes + + checkConstraintBreakage(); // Performs breakage tests on breakable constraints + + PX_PROFILE_STOP_CROSSTHREAD("Basic.rigidBodySolver", getContextId()); + + //KS - process deleted elementIDs now - before GPU particles releases elements, causing issues - sschirm: particles are gone... + mElementIDPool->processPendingReleases(); + mElementIDPool->clearDeletedIDMap(); + + visualizeEndStep(); + + mTaskPool.clear(); + + mReportShapePairTimeStamp++; // important to do this before fetchResults() is called to make sure that delayed deleted actors/shapes get + // separate pair entries in contact reports +} + +void Sc::Scene::postReportsCleanup() +{ + mShapeIDTracker->processPendingReleases(); + mShapeIDTracker->clearDeletedIDMap(); + + mRigidIDTracker->processPendingReleases(); + mRigidIDTracker->clearDeletedIDMap(); + + mConstraintIDTracker->processPendingReleases(); + mConstraintIDTracker->clearDeletedIDMap(); +} + +void Sc::Scene::syncSceneQueryBounds(SqBoundsSync& sync, SqRefFinder& finder) +{ + mSqBoundsManager->syncBounds(sync, finder, mBoundsArray->begin(), getContextId(), mDirtyShapeSimMap); +} + +class ScKinematicUpdateTask : public Cm::Task +{ + Sc::BodyCore*const* mKinematics; + PxU32 mNbKinematics; + PxReal mOneOverDt; + +public: + static const PxU32 NbKinematicsPerTask = 1024; + + ScKinematicUpdateTask(Sc::BodyCore*const* kinematics, PxU32 nbKinematics, PxReal oneOverDt, PxU64 contextID) : + Cm::Task(contextID), mKinematics(kinematics), mNbKinematics(nbKinematics), mOneOverDt(oneOverDt) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbKinematics; ++a) + { + Sc::BodyCore* b = mKinematics[a]; + PX_ASSERT(b->getSim()->isKinematic()); + PX_ASSERT(b->getSim()->isActive()); + + b->getSim()->calculateKinematicVelocity(mOneOverDt); + } + } + + virtual const char* getName() const + { + return "ScScene.KinematicUpdateTask"; + } +}; + +class ScKinematicAddDynamicTask : public Cm::Task +{ + Sc::BodyCore*const* mKinematics; + PxU32 mNbKinematics; + PxsSimulationController& mSimulationController; + + PX_NOCOPY(ScKinematicAddDynamicTask) + +public: + + ScKinematicAddDynamicTask(Sc::BodyCore*const* kinematics, PxU32 nbKinematics, PxsSimulationController& simulationController, PxU64 contextID) : + Cm::Task(contextID), mKinematics(kinematics), mNbKinematics(nbKinematics), mSimulationController(simulationController) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbKinematics; ++a) + { + Sc::BodySim* bodySim = mKinematics[a]->getSim(); + mSimulationController.updateDynamic(false, bodySim->getNodeIndex()); + } + } + + virtual const char* getName() const + { + return "ScScene.KinematicAddDynamicTask"; + } +}; + +void Sc::Scene::kinematicsSetup(PxBaseTask* continuation) +{ + PX_UNUSED(continuation); + const PxU32 nbKinematics = getActiveKinematicBodiesCount(); + BodyCore*const* kinematics = getActiveKinematicBodies(); + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + for(PxU32 i = 0; i < nbKinematics; i += ScKinematicUpdateTask::NbKinematicsPerTask) + { + ScKinematicUpdateTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicUpdateTask)), ScKinematicUpdateTask) + (kinematics + i, PxMin(ScKinematicUpdateTask::NbKinematicsPerTask, nbKinematics - i), mOneOverDt, mContextId); + + task->setContinuation(continuation); + task->removeReference(); + } + + if((mPublicFlags & PxSceneFlag::eENABLE_GPU_DYNAMICS)) + { + ScKinematicAddDynamicTask* addTask = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicAddDynamicTask)), ScKinematicAddDynamicTask) + (kinematics, nbKinematics, *mSimulationController, mContextId); + + addTask->setContinuation(continuation); + addTask->removeReference(); + } +} + +//stepSetup is called in solve, but not collide +void Sc::Scene::stepSetupSolve(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.stepSetupSolve", getContextId()); + + kinematicsSetup(continuation); +} + +void Sc::Scene::stepSetupCollide(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.stepSetupCollide", getContextId()); + + { + PX_PROFILE_ZONE("Sim.projectionTreeUpdates", getContextId()); + mProjectionManager->processPendingUpdates(mLLContext->getScratchAllocator()); + } + + kinematicsSetup(continuation); + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + // Update all dirty interactions + mNPhaseCore->updateDirtyInteractions(outputs, mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE); + mInternalFlags &= ~(SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_DOMINANCE | SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_VISUALIZATION); +} + +void Sc::Scene::processLostTouchPairs() +{ + PX_PROFILE_ZONE("Sc::Scene::processLostTouchPairs", getContextId()); + for (PxU32 i=0; iinternalWakeUp(); + if (!deletedBody2) + mLostTouchPairs[i].body2->internalWakeUp(); + continue; + } + + // If both are sleeping, we let them sleep + // (for example, two sleeping objects touch and the user teleports one (without waking it up)) + if (!mLostTouchPairs[i].body1->isActive() && + !mLostTouchPairs[i].body2->isActive()) + { + continue; + } + + // If only one has fallen asleep, we wake them both + if (!mLostTouchPairs[i].body1->isActive() || + !mLostTouchPairs[i].body2->isActive()) + { + mLostTouchPairs[i].body1->internalWakeUp(); + mLostTouchPairs[i].body2->internalWakeUp(); + } + } + + mLostTouchPairs.clear(); + mLostTouchPairsDeletedBodyIDs.clear(); +} + +class ScBeforeSolverTask : public Cm::Task +{ +public: + static const PxU32 MaxBodiesPerTask = 256; + IG::NodeIndex mBodies[MaxBodiesPerTask]; + PxU32 mNumBodies; + const PxReal mDt; + IG::SimpleIslandManager* mIslandManager; + PxsSimulationController* mSimulationController; + bool mSimUsesAdaptiveForce; + +public: + + ScBeforeSolverTask(PxReal dt, IG::SimpleIslandManager* islandManager, PxsSimulationController* simulationController, PxU64 contextID, bool simUsesAdaptiveForce) : + Cm::Task (contextID), + mDt (dt), + mIslandManager (islandManager), + mSimulationController (simulationController), + mSimUsesAdaptiveForce (simUsesAdaptiveForce) + { + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("Sim.ScBeforeSolverTask", mContextID); + const IG::IslandSim& islandSim = mIslandManager->getAccurateIslandSim(); + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + PxsRigidBody* updatedBodySims[MaxBodiesPerTask]; + PxU32 updatedBodyNodeIndices[MaxBodiesPerTask]; + PxU32 nbUpdatedBodySims = 0; + + for(PxU32 i = 0; i < mNumBodies; i++) + { + IG::NodeIndex index = mBodies[i]; + if(islandSim.getActiveNodeIndex(index) != IG_INVALID_NODE) + { + if (islandSim.getNode(index).mType == IG::Node::eRIGID_BODY_TYPE) + { + PxsRigidBody* body = islandSim.getRigidBody(index); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(body) - rigidBodyOffset); + bodySim->updateForces(mDt, updatedBodySims, updatedBodyNodeIndices, nbUpdatedBodySims, NULL, false, mSimUsesAdaptiveForce); + } + } + } + + if(nbUpdatedBodySims) + mSimulationController->updateBodies(updatedBodySims, updatedBodyNodeIndices, nbUpdatedBodySims); + } + + virtual const char* getName() const + { + return "ScScene.beforeSolver"; + } + +private: + ScBeforeSolverTask& operator = (const ScBeforeSolverTask&); +}; + + +class ScArticBeforeSolverTask : public Cm::Task +{ +public: + const IG::NodeIndex* const mArticIndices; + PxU32 mNumArticulations; + const PxReal mDt; + IG::SimpleIslandManager* mIslandManager; + bool mSimUsesAdaptiveForce; + +public: + + ScArticBeforeSolverTask(const IG::NodeIndex* const articIndices, PxU32 nbArtics, PxReal dt, IG::SimpleIslandManager* islandManager, PxU64 contextID, bool simUsesAdaptiveForce) : + Cm::Task(contextID), + mArticIndices(articIndices), + mNumArticulations(nbArtics), + mDt(dt), + mIslandManager(islandManager), + mSimUsesAdaptiveForce(simUsesAdaptiveForce) + { + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("Sim.ScArticBeforeSolverTask", mContextID); + const IG::IslandSim& islandSim = mIslandManager->getAccurateIslandSim(); + + for (PxU32 a = 0; a < mNumArticulations; ++a) + { + Sc::ArticulationSim* PX_RESTRICT articSim = islandSim.getLLArticulation(mArticIndices[a])->getArticulationSim(); + articSim->checkResize(); + articSim->updateForces(mDt, mSimUsesAdaptiveForce); + articSim->saveLastCCDTransform(); + } + } + + virtual const char* getName() const + { + return "ScScene.ScArticBeforeSolverTask"; + } + +private: + ScArticBeforeSolverTask& operator = (const ScArticBeforeSolverTask&); +}; + + +void Sc::Scene::beforeSolver(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateForces", getContextId()); + + // Note: For contact notifications it is important that force threshold checks are done after new/lost touches have been processed + // because pairs might get added to the list processed below + + // Atoms that passed contact force threshold + ThresholdStream& thresholdStream = mDynamicsContext->getThresholdStream(); + thresholdStream.clear(); + + const IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + const PxU32 nbActiveBodies = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + mNumDeactivatingNodes[IG::Node::eRIGID_BODY_TYPE] = 0;//islandSim.getNbNodesToDeactivate(IG::Node::eRIGID_BODY_TYPE); + mNumDeactivatingNodes[IG::Node::eARTICULATION_TYPE] = 0;//islandSim.getNbNodesToDeactivate(IG::Node::eARTICULATION_TYPE); + + const PxU32 MaxBodiesPerTask = ScBeforeSolverTask::MaxBodiesPerTask; + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + mSimulationController->reserve(nbActiveBodies); + + bool simUsesAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + { + Cm::BitMap::Iterator iter(mVelocityModifyMap); + + for (PxU32 i = iter.getNext(); i != Cm::BitMap::Iterator::DONE; /*i = iter.getNext()*/) + { + ScBeforeSolverTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScBeforeSolverTask)), ScBeforeSolverTask(mDt, mSimpleIslandManager, mSimulationController, getContextId(), simUsesAdaptiveForce)); + PxU32 count = 0; + for (; count < MaxBodiesPerTask && i != Cm::BitMap::Iterator::DONE; i = iter.getNext()) + { + PxsRigidBody* body = islandSim.getRigidBody(IG::NodeIndex(i)); + bool retainsAccelerations = false; + if (body) + { + task->mBodies[count++] = IG::NodeIndex(i); + + retainsAccelerations = (body->mCore->mFlags & PxRigidBodyFlag::eRETAIN_ACCELERATIONS); + + } + + if(!retainsAccelerations) + { + mVelocityModifyMap.reset(i); + } + } + task->mNumBodies = count; + task->setContinuation(continuation); + task->removeReference(); + } + } + + const PxU32 nbActiveArticulations = islandSim.getNbActiveNodes(IG::Node::eARTICULATION_TYPE); + const IG::NodeIndex* const articIndices = islandSim.getActiveNodes(IG::Node::eARTICULATION_TYPE); + + const PxU32 nbArticsPerTask = 8; + + for(PxU32 a = 0; a < nbActiveArticulations; a+= nbArticsPerTask) + { + const PxU32 nbToProcess = PxMin(PxU32(nbActiveArticulations - a), nbArticsPerTask); + ScArticBeforeSolverTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScArticBeforeSolverTask)), ScArticBeforeSolverTask(articIndices+a, nbToProcess, + mDt, mSimpleIslandManager, getContextId(), simUsesAdaptiveForce)); + + task->setContinuation(continuation); + task->removeReference(); + } + + mBodyGravityDirty = false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class UpdatProjectedPoseTask : public Cm::Task +{ + Sc::BodySim** mProjectedBodies; + PxU32 mNbBodiesToProcess; + +public: + UpdatProjectedPoseTask(PxU64 contextID, Sc::BodySim** projectedBodies, PxU32 nbBodiesToProcess) : + Cm::Task (contextID), + mProjectedBodies (projectedBodies), + mNbBodiesToProcess (nbBodiesToProcess) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbBodiesToProcess; ++a) + { + mProjectedBodies[a]->updateCached(NULL); + } + } + + virtual const char* getName() const + { + return "ScScene.UpdatProjectedPoseTask"; + } +}; + +class UpdateArticulationTask : public Cm::Task +{ + Sc::ArticulationCore* const* mArticList; + PxU32 mNbArticulations; + PxReal mDt; + +public: + + UpdateArticulationTask(PxU64 contextId, Sc::ArticulationCore* const* articList, PxU32 nbArticulations, PxReal dt) : Cm::Task(contextId), mArticList(articList), mNbArticulations(nbArticulations), + mDt(dt) + { + } + + virtual const char* getName() const { return "UpdateArticulationTask"; } + + virtual void runInternal() + { + for (PxU32 i = 0; i < mNbArticulations; ++i) + { + Sc::ArticulationSim* articSim = mArticList[i]->getSim(); + articSim->sleepCheck(mDt); + articSim->updateCached(NULL); + } + } +}; + +void Sc::Scene::afterIntegration(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sc::Scene::afterIntegration", getContextId()); + mLLContext->getTransformCache().resetChangedState(); //Reset the changed state. If anything outside of the GPU kernels updates any shape's transforms, this will be raised again + getBoundsArray().resetChangedState(); + + PxsTransformCache& cache = getLowLevelContext()->getTransformCache(); + Bp::BoundsArray& boundArray = getBoundsArray(); + + { + PX_PROFILE_ZONE("AfterIntegration::lockStage", getContextId()); + mLLContext->getLock().lock(); + + + mSimulationController->udpateScBodyAndShapeSim(cache, boundArray, continuation); + + const IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + const PxU32 numBodiesToDeactivate = islandSim.getNbNodesToDeactivate(IG::Node::eRIGID_BODY_TYPE); + + const IG::NodeIndex*const deactivatingIndices = islandSim.getNodesToDeactivate(IG::Node::eRIGID_BODY_TYPE); + + PxU32 previousNumBodiesToDeactivate = mNumDeactivatingNodes[IG::Node::eRIGID_BODY_TYPE]; + + { + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + PX_PROFILE_ZONE("AfterIntegration::deactivateStage", getContextId()); + for (PxU32 i = previousNumBodiesToDeactivate; i < numBodiesToDeactivate; i++) + { + PxsRigidBody* rigid = islandSim.getRigidBody(deactivatingIndices[i]); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(rigid) - rigidBodyOffset); + //we need to set the rigid body back to the previous pose for the deactivated objects. This emulates the previous behavior where island gen ran before the solver, ensuring + //that bodies that should be deactivated this frame never reach the solver. We now run the solver in parallel with island gen, so objects that should be deactivated this frame + //still reach the solver and are integrated. However, on the frame when they should be deactivated, we roll back to their state at the beginning of the frame to ensure that the + //user perceives the same behavior as before. + + PxsBodyCore& bodyCore = bodySim->getBodyCore().getCore(); + + //if(!islandSim.getNode(bodySim->getNodeIndex()).isActive()) + rigid->setPose(rigid->getLastCCDTransform()); + + + bodySim->updateCached(&changedAABBMgrActorHandles); + mSimulationController->updateDynamic(bodySim->isArticulationLink(), bodySim->getNodeIndex()); + + //solver is running in parallel with IG(so solver might solving the body which IG identify as deactivatedNodes). After we moved sleepCheck into the solver after integration, sleepChecks + //might have processed bodies that are now considered deactivated. This could have resulted in either freezing or unfreezing one of these bodies this frame, so we need to process those + //events to ensure that the SqManager's bounds arrays are consistently maintained. Also, we need to clear the frame flags for these bodies. + + if (rigid->isFreezeThisFrame()) + bodySim->freezeTransforms(&mAABBManager->getChangedAABBMgActorHandleMap()); + /*else if(bodyCore.isUnfreezeThisFrame()) + bodySim->createSqBounds();*/ + + //PX_ASSERT(bodyCore.wakeCounter == 0.0f); + //KS - the IG deactivates bodies in parallel with the solver. It appears that under certain circumstances, the solver's integration (which performs + //sleep checks) could decide that the body is no longer a candidate for sleeping on the same frame that the island gen decides to deactivate the island + //that the body is contained in. This is a rare occurrence but the behavior we want to emulate is that of IG running before solver so we should therefore + //permit the IG to make the authoritative decision over whether the body should be active or inactive. + bodyCore.wakeCounter = 0.0f; + bodyCore.linearVelocity = PxVec3(0.0f); + bodyCore.angularVelocity = PxVec3(0.0f); + + rigid->clearAllFrameFlags(); + + /*Sc::ShapeSim* sim; + for (Sc::ShapeIterator iterator(*bodySim); (sim = iterator.getNext()) != NULL;) + { + if (sim->isInBroadPhase()) + changedAABBMgrActorHandles.growAndSet(sim->getElementID()); + }*/ + } + } + + const PxU32 maxBodiesPerTask = 256; + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + { + PX_PROFILE_ZONE("AfterIntegration::dispatchTasks", getContextId()); + for (PxU32 a = 0; a < mProjectedBodies.size(); a += maxBodiesPerTask) + { + UpdatProjectedPoseTask* task = + PX_PLACEMENT_NEW(flushPool.allocate(sizeof(UpdatProjectedPoseTask)), UpdatProjectedPoseTask)(getContextId(), &mProjectedBodies[a], PxMin(maxBodiesPerTask, mProjectedBodies.size() - a)); + task->setContinuation(continuation); + task->removeReference(); + } + } + + { + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + PX_PROFILE_ZONE("AfterIntegration::growAndSet", getContextId()); + for (PxU32 a = 0; a < mProjectedBodies.size(); ++a) + { + if (!mProjectedBodies[a]->isFrozen()) + { + Sc::ElementSim* current = mProjectedBodies[a]->getElements_(); + while(current) + { + Sc::ShapeSim* sim = static_cast(current); + if(sim->isInBroadPhase()) + changedAABBMgrActorHandles.growAndSet(sim->getElementID()); + current = current->mNextInActor; + } + } + } + } + { + PX_PROFILE_ZONE("AfterIntegration::managerAndDynamic", getContextId()); + const PxU32 unrollSize = 256; + for (PxU32 a = 0; a < mProjectedBodies.size(); a += unrollSize) + { + PxsRigidBody* tempBodies[unrollSize]; + PxU32 nodeIds[unrollSize]; + const PxU32 nbToProcess = PxMin(unrollSize, mProjectedBodies.size() - a); + for (PxU32 i = 0; i < nbToProcess; ++i) + { + tempBodies[i] = &mProjectedBodies[a + i]->getLowLevelBody(); + nodeIds[i] = mProjectedBodies[a + i]->getNodeIndex().index(); + } + //KS - it seems that grabbing the CUDA context/releasing it is expensive so we should minimize how + //frequently we do that. Batch processing like this helps + mSimulationController->addDynamics(tempBodies, nodeIds, nbToProcess); + } + } + + updateKinematicCached(continuation); + + mLLContext->getLock().unlock(); + } + + //KS - TODO - parallelize this bit!!!!! + if(mArticulations.size()) + { + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + ArticulationCore* const* articList = mArticulations.getEntries(); + + const PxU32 nbArticulationsPerTask = 8; + const PxU32 articCount = mArticulations.size(); + for (PxU32 i = 0; i < articCount; i += nbArticulationsPerTask) + { + UpdateArticulationTask* task = + PX_PLACEMENT_NEW(flushPool.allocate(sizeof(UpdateArticulationTask)), UpdateArticulationTask)(getContextId(), articList + i, PxMin(nbArticulationsPerTask, PxU32(articCount - i)), mDt); + task->setContinuation(continuation); + task->removeReference(); + } + + mLLContext->getLock().lock(); + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + + + Sc::BodySim* ccdBodySims[DY_ARTICULATION_MAX_SIZE]; + + for(PxU32 i=0;igetSim(); + + //KS - check links for CCD flags and add to mCcdBodies list if required.... + PxU32 nbIdx = articSim->getCCDLinks(ccdBodySims); + + for (PxU32 a = 0; a < nbIdx; ++a) + { + mCcdBodies.pushBack(ccdBodySims[a]); + } + + articSim->markShapesUpdated(&changedAABBMgrActorHandles); + + } + mLLContext->getLock().unlock(); + } + + PX_PROFILE_STOP_CROSSTHREAD("Basic.dynamics", getContextId()); + + checkForceThresholdContactEvents(0); +} + +void Sc::Scene::checkForceThresholdContactEvents(const PxU32 ccdPass) +{ + PX_PROFILE_ZONE("Sim.checkForceThresholdContactEvents", getContextId()); + + // Note: For contact notifications it is important that force threshold checks are done after new/lost touches have been processed + // because pairs might get added to the list processed below + + // Bodies that passed contact force threshold + + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + ThresholdStream& thresholdStream = mDynamicsContext->getForceChangedThresholdStream(); + + const PxU32 nbThresholdElements = thresholdStream.size(); + + for (PxU32 i = 0; i< nbThresholdElements; ++i) + { + ThresholdStreamElement& elem = thresholdStream[i]; + ShapeInteraction* si = elem.shapeInteraction; + + //If there is a shapeInteraction and the shapeInteraction points to a contactManager (i.e. the CM was not destroyed in parallel with the solver) + if (si != NULL) + { + PxU32 pairFlags = si->getPairFlags(); + if (pairFlags & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS) + { + si->swapAndClearForceThresholdExceeded(); + + if (elem.accumulatedForce > elem.threshold * mDt) + { + si->raiseFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_NOW); + + PX_ASSERT(si->hasTouch()); + + //If the accumulatedForce is large than the threshold in the current frame and the accumulatedForce is less than the threshold in the previous frame, + //and the user request notify for found event, we will raise eNOTIFY_THRESHOLD_FORCE_FOUND + if ((!si->readFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_BEFORE)) && (pairFlags & PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND)) + { + si->processUserNotification(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND, 0, false, ccdPass, false, outputs); + } + else if (si->readFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_BEFORE) && (pairFlags & PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS)) + { + si->processUserNotification(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS, 0, false, ccdPass, false, outputs); + } + } + else + { + //If the accumulatedForce is less than the threshold in the current frame and the accumulatedForce is large than the threshold in the previous frame, + //and the user request notify for found event, we will raise eNOTIFY_THRESHOLD_FORCE_LOST + if (si->readFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_BEFORE) && (pairFlags & PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST)) + { + si->processUserNotification(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST, 0, false, ccdPass, false, outputs); + } + } + } + } + } +} + +void Sc::Scene::endStep() +{ + mTimeStamp++; +// INVALID_SLEEP_COUNTER is 0xffffffff. Therefore the last bit is masked. Look at Body::isForcedToSleep() for example. +// if(timeStamp==PX_INVALID_U32) timeStamp = 0; // Reserve INVALID_ID for something else + mTimeStamp &= 0x7fffffff; + + mReportShapePairTimeStamp++; // to make sure that deleted shapes/actors after fetchResults() create new report pairs +} + +void Sc::Scene::resizeReleasedBodyIDMaps(PxU32 maxActors, PxU32 numActors) +{ + mLostTouchPairsDeletedBodyIDs.resize(maxActors); + mRigidIDTracker->resizeDeletedIDMap(maxActors,numActors); + mShapeIDTracker->resizeDeletedIDMap(maxActors,numActors); +} + +/** +Render objects before simulation starts +*/ +void Sc::Scene::visualizeStartStep() +{ + PX_PROFILE_ZONE("Sim.visualizeStartStep", getContextId()); + +#if PX_ENABLE_DEBUG_VISUALIZATION + if(!getVisualizationScale()) + { + // make sure visualization inside simulate was skipped + PX_ASSERT(getRenderBuffer().empty()); + return; // early out if visualization scale is 0 + } + + Cm::RenderOutput out(getRenderBuffer()); + + if(getVisualizationParameter(PxVisualizationParameter::eCOLLISION_COMPOUNDS)) + mAABBManager->visualize(out); + + // Visualize joints + Sc::ConstraintCore*const * constraints = mConstraints.getEntries(); + for(PxU32 i=0, size = mConstraints.size();igetSim()->visualize(getRenderBuffer()); + + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + mNPhaseCore->visualize(out, outputs); +#endif +} + +/** +Render objects after simulation finished. Use this for data that is only available after simulation. +*/ +void Sc::Scene::visualizeEndStep() +{ + PX_PROFILE_ZONE("Sim.visualizeEndStep", getContextId()); + +#if PX_ENABLE_DEBUG_VISUALIZATION + if(!getVisualizationScale()) + { + // make sure any visualization before was skipped + PX_ASSERT(getRenderBuffer().empty()); + return; // early out if visualization scale is 0 + } + + Cm::RenderOutput out(getRenderBuffer()); +#endif +} + +void Sc::Scene::collectPostSolverVelocitiesBeforeCCD() +{ + if (mContactReportsNeedPostSolverVelocity) + { + ActorPairReport*const* actorPairs = mNPhaseCore->getContactReportActorPairs(); + PxU32 nbActorPairs = mNPhaseCore->getNbContactReportActorPairs(); + for(PxU32 i=0; i < nbActorPairs; i++) + { + if (i < (nbActorPairs - 1)) + Ps::prefetchLine(actorPairs[i+1]); + + ActorPairReport* aPair = actorPairs[i]; + + ContactStreamManager& cs = aPair->getContactStreamManager(); + + PxU32 streamManagerFlag = cs.getFlags(); + if(streamManagerFlag & ContactStreamManagerFlag::eINVALID_STREAM) + continue; + + PxU8* stream = mNPhaseCore->getContactReportPairData(cs.bufferIndex); + + if(i + 1 < nbActorPairs) + Ps::prefetch(&(actorPairs[i+1]->getContactStreamManager())); + + if (!cs.extraDataSize) + continue; + else if (streamManagerFlag & ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY) + cs.setContactReportPostSolverVelocity(stream, aPair->getActorA(), aPair->getActorB()); + } + } +} + +void Sc::Scene::finalizeContactStreamAndCreateHeader(PxContactPairHeader& header, const ActorPairReport& aPair, ContactStreamManager& cs, PxU32 removedShapeTestMask) +{ + PxU8* stream = mNPhaseCore->getContactReportPairData(cs.bufferIndex); + PxU32 streamManagerFlag = cs.getFlags(); + ContactShapePair* contactPairs = cs.getShapePairs(stream); + const PxU16 nbShapePairs = cs.currentPairCount; + PX_ASSERT(nbShapePairs > 0); + + if (streamManagerFlag & removedShapeTestMask) + { + // At least one shape of this actor pair has been deleted. Need to traverse the contact buffer, + // find the pairs which contain deleted shapes and set the flags accordingly. + + ContactStreamManager::convertDeletedShapesInContactStream(contactPairs, nbShapePairs, getShapeIDTracker()); + } + PX_ASSERT(contactPairs); + + ObjectIDTracker& RigidIDTracker = getRigidIDTracker(); + header.actors[0] = static_cast(aPair.getPxActorA()); + header.actors[1] = static_cast(aPair.getPxActorB()); + PxU16 headerFlags = 0; + if (RigidIDTracker.isDeletedID(aPair.getActorAID())) + headerFlags |= PxContactPairHeaderFlag::eREMOVED_ACTOR_0; + if (RigidIDTracker.isDeletedID(aPair.getActorBID())) + headerFlags |= PxContactPairHeaderFlag::eREMOVED_ACTOR_1; + header.flags = PxContactPairHeaderFlags(headerFlags); + header.pairs = reinterpret_cast(contactPairs); + header.nbPairs = nbShapePairs; + + PxU16 extraDataSize = cs.extraDataSize; + if (!extraDataSize) + header.extraDataStream = NULL; + else + { + PX_ASSERT(extraDataSize >= sizeof(ContactStreamHeader)); + extraDataSize -= sizeof(ContactStreamHeader); + header.extraDataStream = stream + sizeof(ContactStreamHeader); + + if (streamManagerFlag & ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY) + { + PX_ASSERT(!(headerFlags & Ps::to16(PxContactPairHeaderFlag::eREMOVED_ACTOR_0 | PxContactPairHeaderFlag::eREMOVED_ACTOR_1))); + cs.setContactReportPostSolverVelocity(stream, aPair.getActorA(), aPair.getActorB()); + } + } + header.extraDataStreamSize = extraDataSize; +} + +const Ps::Array& Sc::Scene::getQueuedContactPairHeaders() +{ + // if buffered shape removals occured, then the criteria for testing the contact stream for events with removed shape pointers needs to be more strict. + PX_ASSERT(mRemovedShapeCountAtSimStart <= mShapeIDTracker->getDeletedIDCount()); + bool reducedTestForRemovedShapes = mRemovedShapeCountAtSimStart == mShapeIDTracker->getDeletedIDCount(); + const PxU32 removedShapeTestMask = PxU32((reducedTestForRemovedShapes ? ContactStreamManagerFlag::eTEST_FOR_REMOVED_SHAPES : (ContactStreamManagerFlag::eTEST_FOR_REMOVED_SHAPES | ContactStreamManagerFlag::eHAS_PAIRS_THAT_LOST_TOUCH))); + + ActorPairReport*const* actorPairs = mNPhaseCore->getContactReportActorPairs(); + PxU32 nbActorPairs = mNPhaseCore->getNbContactReportActorPairs(); + mQueuedContactPairHeaders.reserve(nbActorPairs); + mQueuedContactPairHeaders.clear(); + + for (PxU32 i = 0; i < nbActorPairs; i++) + { + if (i < (nbActorPairs - 1)) + Ps::prefetchLine(actorPairs[i + 1]); + + ActorPairReport* aPair = actorPairs[i]; + ContactStreamManager& cs = aPair->getContactStreamManager(); + if (cs.getFlags() & ContactStreamManagerFlag::eINVALID_STREAM) + continue; + + if (i + 1 < nbActorPairs) + Ps::prefetch(&(actorPairs[i + 1]->getContactStreamManager())); + + PxContactPairHeader &pairHeader = mQueuedContactPairHeaders.insert(); + finalizeContactStreamAndCreateHeader(pairHeader, *aPair, cs, removedShapeTestMask); + + cs.maxPairCount = cs.currentPairCount; + cs.setMaxExtraDataSize(cs.extraDataSize); + } + + return mQueuedContactPairHeaders; +} + +/* +Threading: called in the context of the user thread, but only after the physics thread has finished its run +*/ +void Sc::Scene::fireQueuedContactCallbacks(bool asPartOfFlush) +{ + if(mSimulationEventCallback) + { + // if buffered shape removals occured, then the criteria for testing the contact stream for events with removed shape pointers needs to be more strict. + PX_ASSERT(asPartOfFlush || (mRemovedShapeCountAtSimStart <= mShapeIDTracker->getDeletedIDCount())); + bool reducedTestForRemovedShapes = asPartOfFlush || (mRemovedShapeCountAtSimStart == mShapeIDTracker->getDeletedIDCount()); + const PxU32 removedShapeTestMask = PxU32(reducedTestForRemovedShapes ? ContactStreamManagerFlag::eTEST_FOR_REMOVED_SHAPES : (ContactStreamManagerFlag::eTEST_FOR_REMOVED_SHAPES | ContactStreamManagerFlag::eHAS_PAIRS_THAT_LOST_TOUCH)); + + ActorPairReport*const* actorPairs = mNPhaseCore->getContactReportActorPairs(); + PxU32 nbActorPairs = mNPhaseCore->getNbContactReportActorPairs(); + for(PxU32 i=0; i < nbActorPairs; i++) + { + if (i < (nbActorPairs - 1)) + Ps::prefetchLine(actorPairs[i+1]); + + ActorPairReport* aPair = actorPairs[i]; + ContactStreamManager* cs = &aPair->getContactStreamManager(); + if (cs == NULL || cs->getFlags() & ContactStreamManagerFlag::eINVALID_STREAM) + continue; + + if (i + 1 < nbActorPairs) + Ps::prefetch(&(actorPairs[i+1]->getContactStreamManager())); + + PxContactPairHeader pairHeader; + finalizeContactStreamAndCreateHeader(pairHeader, *aPair, *cs, removedShapeTestMask); + + mSimulationEventCallback->onContact(pairHeader, pairHeader.pairs, pairHeader.nbPairs); + + // estimates for next frame + cs->maxPairCount = cs->currentPairCount; + cs->setMaxExtraDataSize(cs->extraDataSize); + } + } +} + +PX_FORCE_INLINE void markDeletedShapes(Sc::ObjectIDTracker& idTracker, Sc::TriggerPairExtraData& tped, PxTriggerPair& pair) +{ + PxTriggerPairFlags::InternalType flags = 0; + if (idTracker.isDeletedID(tped.shape0ID)) + flags |= PxTriggerPairFlag::eREMOVED_SHAPE_TRIGGER; + if (idTracker.isDeletedID(tped.shape1ID)) + flags |= PxTriggerPairFlag::eREMOVED_SHAPE_OTHER; + + pair.flags = PxTriggerPairFlags(flags); +} + +void Sc::Scene::fireTriggerCallbacks() +{ + // triggers + const PxU32 nbTriggerPairs = mTriggerBufferAPI.size(); + PX_ASSERT(nbTriggerPairs == mTriggerBufferExtraData->size()); + if(nbTriggerPairs) + { + // cases to take into account: + // - no simulation/trigger shape has been removed -> no need to test shape references for removed shapes + // - simulation/trigger shapes have been removed but only while the simulation was not running -> only test the events that have + // a marker for removed shapes set + // - simulation/trigger shapes have been removed while the simulation was running -> need to test all events (see explanation + // below) + // + // If buffered shape removals occured, then all trigger events need to be tested for removed shape pointers. + // An optimization like in the contact report case is not applicable here because trigger interactions do not + // have a reference to their reported events. It can happen that a trigger overlap found event is created but + // a shape of that pair gets removed while the simulation is running. When processing the lost touch from the + // shape removal, no link to the overlap found event is possible and thus it can not be marked as dirty. + const bool hasRemovedShapes = mShapeIDTracker->getDeletedIDCount() > 0; + const bool forceTestsForRemovedShapes = (mRemovedShapeCountAtSimStart < mShapeIDTracker->getDeletedIDCount()); + + if(mSimulationEventCallback) + { + if (!hasRemovedShapes) + mSimulationEventCallback->onTrigger(mTriggerBufferAPI.begin(), nbTriggerPairs); + else + { + for(PxU32 i = 0; i < nbTriggerPairs; i++) + { + PxTriggerPair& triggerPair = mTriggerBufferAPI[i]; + + if (forceTestsForRemovedShapes || (PxTriggerPairFlags::InternalType(triggerPair.flags) & TriggerPairFlag::eTEST_FOR_REMOVED_SHAPES)) + markDeletedShapes(*mShapeIDTracker, (*mTriggerBufferExtraData)[i], triggerPair); + } + + mSimulationEventCallback->onTrigger(mTriggerBufferAPI.begin(), nbTriggerPairs); + } + } + } + + // PT: clear the buffer **even when there's no simulationEventCallback**. + mTriggerBufferAPI.clear(); + mTriggerBufferExtraData->clear(); +} + +void Sc::Scene::fireBrokenConstraintCallbacks() +{ + if(!mSimulationEventCallback) + return; + + const PxU32 count = mBrokenConstraints.size(); + for(PxU32 i=0;igetSim()) // the constraint might have been removed while the simulation was running + { + PxU32 typeID = 0xffffffff; + void* externalRef = c->getPxConnector()->getExternalReference(typeID); + PX_CHECK_MSG(typeID != 0xffffffff, "onConstraintBreak: Invalid constraint type ID."); + + PxConstraintInfo constraintInfo(c->getPxConstraint(), externalRef, typeID); + mSimulationEventCallback->onConstraintBreak(&constraintInfo, 1); + } + } +} + +/* +Threading: called in the context of the user thread, but only after the physics thread has finished its run +*/ +void Sc::Scene::fireCallbacksPostSync() +{ + // + // Fire sleep & woken callbacks + // + + // A body should be either in the sleep or the woken list. If it is in both, remove it from the list it was + // least recently added to. + + if(!mSleepBodyListValid) + cleanUpSleepBodies(); + + if(!mWokeBodyListValid) + cleanUpWokenBodies(); + + if(mSimulationEventCallback) + { + // allocate temporary data + const PxU32 nbSleep = mSleepBodies.size(); + const PxU32 nbWoken = mWokeBodies.size(); + const PxU32 arrSize = PxMax(nbSleep, nbWoken); + PxActor** actors = arrSize ? reinterpret_cast(PX_ALLOC_TEMP(arrSize*sizeof(PxActor*), "PxActor*")) : NULL; + if(actors) + { + if(nbSleep) + { + PxU32 destSlot = 0; + BodyCore* const* sleepingBodies = mSleepBodies.getEntries(); + for(PxU32 i=0; igetActorFlags() & PxActorFlag::eSEND_SLEEP_NOTIFIES) + actors[destSlot++] = body->getPxActor(); + } + + if(destSlot) + mSimulationEventCallback->onSleep(actors, destSlot); + + //if (PX_DBG_IS_CONNECTED()) + //{ + // for (PxU32 i = 0; i < nbSleep; ++i) + // { + // BodyCore* body = mSleepBodies[i]; + // PX_ASSERT(body->getActorType() == PxActorType::eRIGID_DYNAMIC); + // } + //} + } + + // do the same thing for bodies that have just woken up + + if(nbWoken) + { + PxU32 destSlot = 0; + BodyCore* const* wokenBodies = mWokeBodies.getEntries(); + for(PxU32 i=0; igetActorFlags() & PxActorFlag::eSEND_SLEEP_NOTIFIES) + actors[destSlot++] = body->getPxActor(); + } + + if(destSlot) + mSimulationEventCallback->onWake(actors, destSlot); + + //if (PX_DBG_IS_CONNECTED()) + //{ + // for (PxU32 i = 0; i < nbWoken; ++i) + // { + // BodyCore* body = mWokeBodies[i]; + // PX_ASSERT(actors[i]->getType() == PxActorType::eRIGID_DYNAMIC); + // } + //} + } + + PX_FREE_AND_RESET(actors); + } + } + + clearSleepWakeBodies(); +} + +void Sc::Scene::prepareOutOfBoundsCallbacks() +{ + PxU32 nbOut0; + void** outObjects = mAABBManager->getOutOfBoundsObjects(nbOut0); + + mOutOfBoundsIDs.clear(); + for(PxU32 i=0;i(outObjects[i]); + + Sc::ShapeSim* sim = static_cast(volume); + PxU32 id = sim->getID(); + mOutOfBoundsIDs.pushBack(id); + } +} + +bool Sc::Scene::fireOutOfBoundsCallbacks() +{ + bool outputWarning = false; + + // Actors + { + PxU32 nbOut0; + void** outObjects = mAABBManager->getOutOfBoundsObjects(nbOut0); + + const ObjectIDTracker& tracker = getShapeIDTracker(); + + PxBroadPhaseCallback* cb = mBroadPhaseCallback; + for(PxU32 i=0;i(outObjects[i]); + + Sc::ShapeSim* sim = static_cast(volume); + if(tracker.isDeletedID(mOutOfBoundsIDs[i])) + continue; + + if(cb) + { + ActorSim& actor = volume->getActor(); + RigidSim& rigidSim = static_cast(actor); + PxActor* pxActor = rigidSim.getPxActor(); + PxShape* px = sim->getPxShape(); + cb->onObjectOutOfBounds(*px, *pxActor); + } + else + { + outputWarning = true; + } + } + mAABBManager->clearOutOfBoundsObjects(); + } + + return outputWarning; +} + +void Sc::Scene::fireOnAdvanceCallback() +{ + if(!mSimulationEventCallback) + return; + + const PxU32 nbPosePreviews = mPosePreviewBodies.size(); + if(!nbPosePreviews) + return; + + mClientPosePreviewBodies.clear(); + mClientPosePreviewBodies.reserve(nbPosePreviews); + + mClientPosePreviewBuffer.clear(); + mClientPosePreviewBuffer.reserve(nbPosePreviews); + + const BodySim*const* PX_RESTRICT posePreviewBodies = mPosePreviewBodies.getEntries(); + for(PxU32 i=0; i(b.getPxActor())); + mClientPosePreviewBuffer.pushBack(c.body2World * c.getBody2Actor().getInverse()); + } + } + + const PxU32 bodyCount = mClientPosePreviewBodies.size(); + if(bodyCount) + mSimulationEventCallback->onAdvance(mClientPosePreviewBodies.begin(), mClientPosePreviewBuffer.begin(), bodyCount); +} + +void Sc::Scene::postCallbacksPreSync() +{ + PX_PROFILE_ZONE("Sim.postCallbackPreSync", mContextId); + // clear contact stream data + mNPhaseCore->clearContactReportStream(); + mNPhaseCore->clearContactReportActorPairs(false); + + // Put/prepare kinematics to/for sleep and invalidate target pose + // note: this needs to get done after the contact callbacks because + // the target might get read there. + // + PxU32 nbKinematics = getActiveKinematicBodiesCount(); + BodyCore*const* kinematics = getActiveKinematicBodies(); + + //KS - this method must run over the kinematic actors in reverse. + while(nbKinematics--) + { + if(nbKinematics > 16) + { + Ps::prefetchLine((kinematics[nbKinematics-16])); + } + if (nbKinematics > 4) + { + Ps::prefetchLine(((kinematics[nbKinematics - 4]))->getSimStateData_Unchecked()); + } + + BodyCore* b = kinematics[nbKinematics]; + //kinematics++; + PX_ASSERT(b->getSim()->isKinematic()); + PX_ASSERT(b->getSim()->isActive()); + + b->invalidateKinematicTarget(); + b->getSim()->deactivateKinematic(); + } + + releaseConstraints(true); //release constraint blocks at the end of the frame, so user can retrieve the blocks +} + +void Sc::Scene::setNbContactDataBlocks(PxU32 numBlocks) +{ + mLLContext->getNpMemBlockPool().setBlockCount(numBlocks); +} + +PxU32 Sc::Scene::getNbContactDataBlocksUsed() const +{ + return mLLContext->getNpMemBlockPool().getUsedBlockCount(); +} + +PxU32 Sc::Scene::getMaxNbContactDataBlocksUsed() const +{ + return mLLContext->getNpMemBlockPool().getMaxUsedBlockCount(); +} + +PxU32 Sc::Scene::getMaxNbConstraintDataBlocksUsed() const +{ + return mLLContext->getNpMemBlockPool().getPeakConstraintBlockCount(); +} + +void Sc::Scene::setScratchBlock(void* addr, PxU32 size) +{ + return mLLContext->setScratchBlock(addr, size); +} + +void Sc::Scene::checkConstraintBreakage() +{ + PX_PROFILE_ZONE("Sim.checkConstraintBreakage", getContextId()); + + PxU32 count = mActiveBreakableConstraints.size(); + ConstraintSim* const* constraints = mActiveBreakableConstraints.getEntries(); + while(count) + { + count--; + constraints[count]->checkMaxForceExceeded(); // start from the back because broken constraints get removed from the list + } +} + +void Sc::Scene::getStats(PxSimulationStatistics& s) const +{ + mStats->readOut(s, mLLContext->getSimStats()); + s.nbStaticBodies = mNbRigidStatics; + s.nbDynamicBodies = mNbRigidDynamics; + s.nbArticulations = mArticulations.size(); + + s.nbAggregates = mAABBManager->getNbActiveAggregates(); + for(PxU32 i=0; i(reinterpret_cast(shapes[i])+ptrOffset); + + //PxBounds3* target = uninflatedBounds ? uninflatedBounds + i : uninflatedBounds; + //mShapeSimPool->construct(sim, sc, llBody, target); + + ShapeSim* shapeSim = mShapeSimPool->construct(bodySim, sc); + mNbGeometries[sc.getGeometryType()]++; + + mSimulationController->addShape(&shapeSim->getLLShapeSim(), shapeSim->getID()); + + if (outBounds) + outBounds[i] = mBoundsArray->getBounds(shapeSim->getElementID()); + + mLLContext->getNphaseImplementationContext()->registerShape(sc.getCore()); + } +} + +void Sc::Scene::removeShapes(Sc::RigidSim& sim, Ps::InlineArray& shapesBuffer , Ps::InlineArray& removedShapes, bool wakeOnLostTouch) +{ + Sc::ElementSim* current = sim.getElements_(); + while(current) + { + Sc::ShapeSim* s = static_cast(current); + // can do two 2x the allocs in the worst case, but actors with >64 shapes are not common + shapesBuffer.pushBack(s); + removedShapes.pushBack(&s->getCore()); + current = current->mNextInActor; + } + + for(PxU32 i=0;iconstruct(*this, ro); + + mNbRigidStatics++; + addShapes(shapes, nbShapes, shapePtrOffset, *sim, uninflatedBounds); +} + +void Sc::Scene::prefetchForRemove(const StaticCore& core) const +{ + StaticSim* sim = core.getSim(); + if(sim) + { + Ps::prefetch(sim,sizeof(Sc::StaticSim)); + Ps::prefetch(sim->getElements_(),sizeof(Sc::ElementSim)); + } +} + +void Sc::Scene::prefetchForRemove(const BodyCore& core) const +{ + BodySim *sim = core.getSim(); + if(sim) + { + Ps::prefetch(sim,sizeof(Sc::BodySim)); + Ps::prefetch(sim->getElements_(),sizeof(Sc::ElementSim)); + } +} + +void Sc::Scene::removeStatic(StaticCore& ro, Ps::InlineArray& removedShapes, bool wakeOnLostTouch) +{ + PX_ASSERT(ro.getActorCoreType() == PxActorType::eRIGID_STATIC); + + StaticSim* sim = ro.getSim(); + if(sim) + { + if(mBatchRemoveState) + { + removeShapes(*sim, mBatchRemoveState->bufferedShapes ,removedShapes, wakeOnLostTouch); + } + else + { + Ps::InlineArray shapesBuffer; + removeShapes(*sim, shapesBuffer ,removedShapes, wakeOnLostTouch); + } + mStaticSimPool->destroy(static_cast(ro.getSim())); + mNbRigidStatics--; + } +} + +void Sc::Scene::addBody(BodyCore& body, void*const *shapes, PxU32 nbShapes, size_t shapePtrOffset, PxBounds3* outBounds, bool compound) +{ + // sim objects do all the necessary work of adding themselves to broad phase, + // activation, registering with the interaction system, etc + + BodySim* sim = mBodySimPool->construct(*this, body, compound); + + const bool isArticulationLink = sim->isArticulationLink(); + + if (sim->getLowLevelBody().mCore->mFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + { + if (isArticulationLink) + { + if (sim->getNodeIndex().isValid()) + mSpeculativeCDDArticulationBitMap.growAndSet(sim->getNodeIndex().index()); + } + else + mSpeculativeCCDRigidBodyBitMap.growAndSet(sim->getNodeIndex().index()); + } + //if rigid body is articulation link, the node index will be invalid. We should add the link to the scene after we add the + //articulation for gpu + if(sim->getNodeIndex().isValid()) + mSimulationController->addDynamic(&sim->getLowLevelBody(), sim->getNodeIndex()); + mNbRigidDynamics++; + addShapes(shapes, nbShapes, shapePtrOffset, *sim, outBounds); +} + +void Sc::Scene::removeBody(BodyCore& body, Ps::InlineArray& removedShapes, bool wakeOnLostTouch) +{ + BodySim *sim = body.getSim(); + if(sim) + { + if(mBatchRemoveState) + { + removeShapes(*sim, mBatchRemoveState->bufferedShapes ,removedShapes, wakeOnLostTouch); + } + else + { + Ps::InlineArray shapesBuffer; + removeShapes(*sim,shapesBuffer, removedShapes, wakeOnLostTouch); + } + + if (!sim->isArticulationLink()) + { + //clear bit map + if (sim->getLowLevelBody().mCore->mFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + mSpeculativeCCDRigidBodyBitMap.reset(sim->getNodeIndex().index()); + } + mBodySimPool->destroy(sim); + mNbRigidDynamics--; + } +} + +void Sc::Scene::addShape(RigidSim& owner, ShapeCore& shapeCore, PxBounds3* uninflatedBounds) +{ + ShapeSim* sim = mShapeSimPool->construct(owner, shapeCore); + mNbGeometries[shapeCore.getGeometryType()]++; + + //register shape + mSimulationController->addShape(&sim->getLLShapeSim(), sim->getID()); + if (uninflatedBounds) + *uninflatedBounds = mBoundsArray->getBounds(sim->getElementID()); + registerShapeInNphase(shapeCore); +} + +void Sc::Scene::removeShape(ShapeSim& shape, bool wakeOnLostTouch) +{ + //BodySim* body = shape.getBodySim(); + //if(body) + // body->postShapeDetach(); + + unregisterShapeFromNphase(shape.getCore()); + + mSimulationController->removeShape(shape.getID()); + + mNbGeometries[shape.getCore().getGeometryType()]--; + shape.removeFromBroadPhase(wakeOnLostTouch); + mShapeSimPool->destroy(&shape); +} + +void Sc::Scene::registerShapeInNphase(const ShapeCore& shape) +{ + mLLContext->getNphaseImplementationContext()->registerShape(shape.getCore()); +} + +void Sc::Scene::unregisterShapeFromNphase(const ShapeCore& shape) +{ + mLLContext->getNphaseImplementationContext()->unregisterShape(shape.getCore()); +} + +void Sc::Scene::notifyNphaseOnUpdateShapeMaterial(const ShapeCore& shapeCore) +{ + mLLContext->getNphaseImplementationContext()->updateShapeMaterial(shapeCore.getCore()); +} + +void Sc::Scene::startBatchInsertion(BatchInsertionState&state) +{ + state.shapeSim = mShapeSimPool->allocateAndPrefetch(); + state.staticSim = mStaticSimPool->allocateAndPrefetch(); + state.bodySim = mBodySimPool->allocateAndPrefetch(); +} + +void Sc::Scene::addShapes(void *const* shapes, PxU32 nbShapes, size_t ptrOffset, RigidSim& rigidSim, ShapeSim*& prefetchedShapeSim, PxBounds3* outBounds) +{ + for(PxU32 i=0;iallocateAndPrefetch(); + const ShapeCore& sc = *Ps::pointerOffset(shapes[i], ptrdiff_t(ptrOffset)); + new(prefetchedShapeSim) ShapeSim(rigidSim, sc); + outBounds[i] = mBoundsArray->getBounds(prefetchedShapeSim->getElementID()); + + mSimulationController->addShape(&prefetchedShapeSim->getLLShapeSim(), prefetchedShapeSim->getID()); + prefetchedShapeSim = nextShapeSim; + mNbGeometries[sc.getGeometryType()]++; + + + mLLContext->getNphaseImplementationContext()->registerShape(sc.getCore()); + } +} + +void Sc::Scene::addStatic(PxActor* actor, BatchInsertionState& s, PxBounds3* outBounds) +{ + // static core has been prefetched by caller + Sc::StaticSim* sim = s.staticSim; // static core has been prefetched by the caller + + const Cm::PtrTable* shapeTable = Ps::pointerOffset(actor, s.staticShapeTableOffset); + void*const* shapes = shapeTable->getPtrs(); + if(shapeTable->getCount()) + Ps::prefetch(shapes[0],PxU32(s.shapeOffset+sizeof(Sc::ShapeCore))); + + mStaticSimPool->construct(sim, *this, *Ps::pointerOffset(actor, s.staticActorOffset)); + s.staticSim = mStaticSimPool->allocateAndPrefetch(); + + addShapes(shapes, shapeTable->getCount(), size_t(s.shapeOffset), *sim, s.shapeSim, outBounds); + mNbRigidStatics++; +} + +void Sc::Scene::addBody(PxActor* actor, BatchInsertionState& s, PxBounds3* outBounds, bool compound) +{ + Sc::BodySim* sim = s.bodySim; // body core has been prefetched by the caller + + const Cm::PtrTable* shapeTable = Ps::pointerOffset(actor, s.dynamicShapeTableOffset); + void*const* shapes = shapeTable->getPtrs(); + if(shapeTable->getCount()) + Ps::prefetch(shapes[0], PxU32(s.shapeOffset+sizeof(Sc::ShapeCore))); + + mBodySimPool->construct(sim, *this, *Ps::pointerOffset(actor, s.dynamicActorOffset), compound); + s.bodySim = mBodySimPool->allocateAndPrefetch(); + + const bool isArticulationLink = sim->isArticulationLink(); + + if (isArticulationLink) + { + if (sim->getLowLevelBody().mCore->mFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + mSpeculativeCDDArticulationBitMap.growAndSet(sim->getNodeIndex().index()); + } + else + { + if (sim->getLowLevelBody().mCore->mFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + mSpeculativeCCDRigidBodyBitMap.growAndSet(sim->getNodeIndex().index()); + } + + if(sim->getNodeIndex().isValid()) + mSimulationController->addDynamic(&sim->getLowLevelBody(), sim->getNodeIndex()); + + addShapes(shapes, shapeTable->getCount(), size_t(s.shapeOffset), *sim, s.shapeSim, outBounds); + mNbRigidDynamics++; +} + +void Sc::Scene::finishBatchInsertion(BatchInsertionState& state) +{ + // a little bit lazy - we could deal with the last one in the batch specially to avoid overallocating by one. + + mStaticSimPool->releasePreallocated(static_cast(state.staticSim)); + mBodySimPool->releasePreallocated(static_cast(state.bodySim)); + mShapeSimPool->releasePreallocated(state.shapeSim); +} + +void Sc::Scene::initContactsIterator(ContactIterator& contactIterator, PxsContactManagerOutputIterator& outputs) +{ + outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + Interaction** first = mInteractions[Sc::InteractionType::eOVERLAP].begin(); + contactIterator = ContactIterator(first, first + mActiveInteractionCount[Sc::InteractionType::eOVERLAP], outputs); +} + +void Sc::Scene::setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance) +{ + struct { + void operator()(PxU32& bits, PxDominanceGroup shift, PxReal weight) + { + if(weight != PxReal(0)) + bits |= (PxU32(1) << shift); + else + bits &= ~(PxU32(1) << shift); + } + } bitsetter; + + bitsetter(mDominanceBitMatrix[group1], group2, dominance.dominance0); + bitsetter(mDominanceBitMatrix[group2], group1, dominance.dominance1); + + mInternalFlags |= SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_DOMINANCE; //force an update on all interactions on matrix change -- very expensive but we have no choice!! +} + +PxDominanceGroupPair Sc::Scene::getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const +{ + PxU8 dom0 = PxU8((mDominanceBitMatrix[group1]>>group2) & 0x1 ? 1u : 0u); + PxU8 dom1 = PxU8((mDominanceBitMatrix[group2]>>group1) & 0x1 ? 1u : 0u); + return PxDominanceGroupPair(dom0, dom1); +} + +void Sc::Scene::setCreateContactReports(bool s) +{ + if(mLLContext) + mLLContext->setCreateContactStream(s); +} + +void Sc::Scene::setSolverBatchSize(PxU32 solverBatchSize) +{ + mDynamicsContext->setSolverBatchSize(solverBatchSize); +} + +PxU32 Sc::Scene::getSolverBatchSize() const +{ + return mDynamicsContext->getSolverBatchSize(); +} + +void Sc::Scene::setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) +{ + mVisualizationParameterChanged = true; + + PX_ASSERT(mLLContext->getVisualizationParameter(PxVisualizationParameter::eSCALE) == mVisualizationScale); // Safety check because the scale is duplicated for performance reasons + + mLLContext->setVisualizationParameter(param, value); + + if (param == PxVisualizationParameter::eSCALE) + mVisualizationScale = value; +} + +PxReal Sc::Scene::getVisualizationParameter(PxVisualizationParameter::Enum param) const +{ + PX_ASSERT(mLLContext->getVisualizationParameter(PxVisualizationParameter::eSCALE) == mVisualizationScale); // Safety check because the scale is duplicated for performance reasons + + return mLLContext->getVisualizationParameter(param); +} + +void Sc::Scene::setVisualizationCullingBox(const PxBounds3& box) +{ + mLLContext->setVisualizationCullingBox(box); +} + +const PxBounds3& Sc::Scene::getVisualizationCullingBox() const +{ + return mLLContext->getVisualizationCullingBox(); +} + +PxReal Sc::Scene::getFrictionOffsetThreshold() const +{ + return mDynamicsContext->getFrictionOffsetThreshold(); +} + +PxU32 Sc::Scene::getDefaultContactReportStreamBufferSize() const +{ + return mNPhaseCore->getDefaultContactReportStreamBufferSize(); +} + +void Sc::Scene::buildActiveActors() +{ + PxU32 numActiveBodies = 0; + BodyCore*const* PX_RESTRICT activeBodies; + if(!(getPublicFlags() & PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS)) + { + numActiveBodies = getNumActiveBodies(); + activeBodies = getActiveBodiesArray(); + } + else + { + numActiveBodies = getActiveDynamicBodiesCount(); + activeBodies = getActiveDynamicBodies(); + } + + mActiveActors.clear(); + + for(PxU32 i=0; iisFrozen()) + { + PxRigidActor* ra = static_cast(activeBodies[i]->getPxActor()); + PX_ASSERT(ra); + mActiveActors.pushBack(ra); + } + } +} + +// PT: TODO: unify buildActiveActors & buildActiveAndFrozenActors +void Sc::Scene::buildActiveAndFrozenActors() +{ + PxU32 numActiveBodies = 0; + BodyCore*const* PX_RESTRICT activeBodies; + if(!(getPublicFlags() & PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS)) + { + numActiveBodies = getNumActiveBodies(); + activeBodies = getActiveBodiesArray(); + } + else + { + numActiveBodies = getActiveDynamicBodiesCount(); + activeBodies = getActiveDynamicBodies(); + } + + mActiveActors.clear(); + mFrozenActors.clear(); + + for(PxU32 i=0; i(activeBodies[i]->getPxActor()); + PX_ASSERT(ra); + + if(!activeBodies[i]->isFrozen()) + mActiveActors.pushBack(ra); + else + mFrozenActors.pushBack(ra); + } +} + +PxActor** Sc::Scene::getActiveActors(PxU32& nbActorsOut) +{ + nbActorsOut = mActiveActors.size(); + + if(!nbActorsOut) + return NULL; + + return mActiveActors.begin(); +} + +void Sc::Scene::setActiveActors(PxActor** actors, PxU32 nbActors) +{ + mActiveActors.forceSize_Unsafe(0); + mActiveActors.resize(nbActors); + PxMemCopy(mActiveActors.begin(), actors, sizeof(PxActor*) * nbActors); +} + +PxActor** Sc::Scene::getFrozenActors(PxU32& nbActorsOut) +{ + nbActorsOut = mFrozenActors.size(); + + if(!nbActorsOut) + return NULL; + + return mFrozenActors.begin(); +} + +void Sc::Scene::reserveTriggerReportBufferSpace(const PxU32 pairCount, PxTriggerPair*& triggerPairBuffer, TriggerPairExtraData*& triggerPairExtraBuffer) +{ + const PxU32 oldSize = mTriggerBufferAPI.size(); + const PxU32 newSize = oldSize + pairCount; + const PxU32 newCapacity = PxU32(newSize * 1.5f); + mTriggerBufferAPI.reserve(newCapacity); + mTriggerBufferAPI.forceSize_Unsafe(newSize); + triggerPairBuffer = mTriggerBufferAPI.begin() + oldSize; + + PX_ASSERT(oldSize == mTriggerBufferExtraData->size()); + mTriggerBufferExtraData->reserve(newCapacity); + mTriggerBufferExtraData->forceSize_Unsafe(newSize); + triggerPairExtraBuffer = mTriggerBufferExtraData->begin() + oldSize; +} + +PxClientID Sc::Scene::createClient() +{ + mClients.pushBack(PX_NEW(Client)()); + return PxClientID(mClients.size()-1); +} + +void Sc::Scene::clearSleepWakeBodies(void) +{ + // Clear sleep/woken marker flags + BodyCore* const* sleepingBodies = mSleepBodies.getEntries(); + for(PxU32 i=0; i < mSleepBodies.size(); i++) + { + BodySim* body = sleepingBodies[i]->getSim(); + + PX_ASSERT(!body->readInternalFlag(BodySim::BF_WAKEUP_NOTIFY)); + body->clearInternalFlag(BodySim::BF_SLEEP_NOTIFY); + + // A body can be in both lists depending on the sequence of events + body->clearInternalFlag(BodySim::BF_IS_IN_SLEEP_LIST); + body->clearInternalFlag(BodySim::BF_IS_IN_WAKEUP_LIST); + } + + BodyCore* const* wokenBodies = mWokeBodies.getEntries(); + for(PxU32 i=0; i < mWokeBodies.size(); i++) + { + BodySim* body = wokenBodies[i]->getSim(); + + PX_ASSERT(!body->readInternalFlag(BodySim::BF_SLEEP_NOTIFY)); + body->clearInternalFlag(BodySim::BF_WAKEUP_NOTIFY); + + // A body can be in both lists depending on the sequence of events + body->clearInternalFlag(BodySim::BF_IS_IN_SLEEP_LIST); + body->clearInternalFlag(BodySim::BF_IS_IN_WAKEUP_LIST); + } + + mSleepBodies.clear(); + mWokeBodies.clear(); + mWokeBodyListValid = true; + mSleepBodyListValid = true; +} + +void Sc::Scene::onBodySleep(BodySim* body) +{ + if(mSimulationEventCallback) + { + if (body->readInternalFlag(BodySim::BF_WAKEUP_NOTIFY)) + { + PX_ASSERT(!body->readInternalFlag(BodySim::BF_SLEEP_NOTIFY)); + + // Body is in the list of woken bodies, hence, mark this list as dirty such that it gets cleaned up before + // being sent to the user + body->clearInternalFlag(BodySim::BF_WAKEUP_NOTIFY); + mWokeBodyListValid = false; + } + + body->raiseInternalFlag(BodySim::BF_SLEEP_NOTIFY); + } + + // Avoid multiple insertion (the user can do multiple transitions between asleep and awake) + // + // even if no sleep events are requested, we still need to track the objects which were put to sleep because + // for those we need to sync the buffered state (strictly speaking this only applies to sleep changes that are + // triggered by the simulation and not the user but we do not distinguish here) + if (!body->readInternalFlag(BodySim::BF_IS_IN_SLEEP_LIST)) + { + if(mSimulationEventCallback) + { + PX_ASSERT(!mSleepBodies.contains(&body->getBodyCore())); + } + mSleepBodies.insert(&body->getBodyCore()); + body->raiseInternalFlag(BodySim::BF_IS_IN_SLEEP_LIST); + } +} + +void Sc::Scene::onBodyWakeUp(BodySim* body) +{ + if(!mSimulationEventCallback) + return; + + if (body->readInternalFlag(BodySim::BF_SLEEP_NOTIFY)) + { + PX_ASSERT(!body->readInternalFlag(BodySim::BF_WAKEUP_NOTIFY)); + + // Body is in the list of sleeping bodies, hence, mark this list as dirty such it gets cleaned up before + // being sent to the user + body->clearInternalFlag(BodySim::BF_SLEEP_NOTIFY); + mSleepBodyListValid = false; + } + + body->raiseInternalFlag(BodySim::BF_WAKEUP_NOTIFY); + + // Avoid multiple insertion (the user can do multiple transitions between asleep and awake) + if (!body->readInternalFlag(BodySim::BF_IS_IN_WAKEUP_LIST)) + { + PX_ASSERT(!mWokeBodies.contains(&body->getBodyCore())); + mWokeBodies.insert(&body->getBodyCore()); + body->raiseInternalFlag(BodySim::BF_IS_IN_WAKEUP_LIST); + } +} + +PX_INLINE void Sc::Scene::cleanUpSleepBodies() +{ + BodyCore* const* bodyArray = mSleepBodies.getEntries(); + PxU32 bodyCount = mSleepBodies.size(); + + IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + while (bodyCount--) + { + BodySim* body = bodyArray[bodyCount]->getSim(); + + if (body->readInternalFlag(static_cast(BodySim::BF_WAKEUP_NOTIFY))) + { + body->clearInternalFlag(static_cast(BodySim::BF_IS_IN_WAKEUP_LIST)); + mSleepBodies.erase(bodyArray[bodyCount]); + } + else if (islandSim.getNode(body->getNodeIndex()).isActive()) + { + //This body is still active in the island simulation, so the request to deactivate the actor by the application must have failed. Recover by undoing this + mSleepBodies.erase(bodyArray[bodyCount]); + body->internalWakeUp(); + } + } + + mSleepBodyListValid = true; +} + +PX_INLINE void Sc::Scene::cleanUpWokenBodies() +{ + cleanUpSleepOrWokenBodies(mWokeBodies, BodySim::BF_SLEEP_NOTIFY, mWokeBodyListValid); +} + +PX_INLINE void Sc::Scene::cleanUpSleepOrWokenBodies(Ps::CoalescedHashSet& bodyList, PxU32 removeFlag, bool& validMarker) +{ + // With our current logic it can happen that a body is added to the sleep as well as the woken body list in the + // same frame. + // + // Examples: + // - Kinematic is created (added to woken list) but has not target (-> deactivation -> added to sleep list) + // - Dynamic is created (added to woken list) but is forced to sleep by user (-> deactivation -> added to sleep list) + // + // This code traverses the sleep/woken body list and removes bodies which have been initially added to the given + // list but do not belong to it anymore. + + BodyCore* const* bodyArray = bodyList.getEntries(); + PxU32 bodyCount = bodyList.size(); + while (bodyCount--) + { + BodySim* body = bodyArray[bodyCount]->getSim(); + + if (body->readInternalFlag(static_cast(removeFlag))) + bodyList.erase(bodyArray[bodyCount]); + } + + validMarker = true; +} + +void Sc::Scene::releaseConstraints(bool endOfScene) +{ + PX_ASSERT(mLLContext); + + if(getStabilizationEnabled()) + { + //If stabilization is enabled, we're caching contacts for next frame + if(!endOfScene) + { + //So we only clear memory (flip buffers) when not at the end-of-scene. + //This means we clear after narrow phase completed so we can + //release the previous frame's contact buffers before we enter the solve phase. + mLLContext->getNpMemBlockPool().releaseContacts(); + } + } + else if(endOfScene) + { + //We now have a double-buffered pool of mem blocks so we must + //release both pools (which actually triggers the memory used this + //frame to be released + mLLContext->getNpMemBlockPool().releaseContacts(); + mLLContext->getNpMemBlockPool().releaseContacts(); + } +} + +PX_INLINE void Sc::Scene::clearBrokenConstraintBuffer() +{ + mBrokenConstraints.clear(); +} + +void Sc::Scene::updateFromVisualizationParameters() +{ + if (!mVisualizationParameterChanged) // All up to date + return; + + // Update SIPs if visualization is enabled + if (getVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL) || + getVisualizationParameter(PxVisualizationParameter::eCONTACT_ERROR) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_FORCE)) + mInternalFlags |= SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_VISUALIZATION; + + mVisualizationParameterChanged = false; +} + +void Sc::Scene::addToLostTouchList(BodySim* body1, BodySim* body2) +{ + PX_ASSERT(body1 != 0); + PX_ASSERT(body2 != 0); + SimpleBodyPair p = { body1, body2, body1->getRigidID(), body2->getRigidID() }; + mLostTouchPairs.pushBack(p); +} + +void Sc::Scene::initDominanceMatrix() +{ + //init all dominance pairs such that: + //if g1 == g2, then (1.0f, 1.0f) is returned + //if g1 < g2, then (0.0f, 1.0f) is returned + //if g1 > g2, then (1.0f, 0.0f) is returned + + PxU32 mask = ~PxU32(1); + for (unsigned i = 0; i < PX_MAX_DOMINANCE_GROUP; ++i, mask <<= 1) + mDominanceBitMatrix[i] = ~mask; +} + +ArticulationV* Sc::Scene::createLLArticulation(Sc::ArticulationSim* sim) +{ + ArticulationV* articulation = NULL; + + if (sim->getCore().getArticulationType() == PxArticulationBase::eMaximumCoordinate) + { + articulation = mLLArticulationPool->construct(sim); + } + else + { + PX_ASSERT(sim->getCore().getArticulationType() == PxArticulationBase::eReducedCoordinate); + articulation = mLLArticulationRCPool->construct(sim); + } + + return articulation; +} + +void Sc::Scene::destroyLLArticulation(ArticulationV& articulation) +{ + if (articulation.getType() == PxArticulationBase::eMaximumCoordinate) + mLLArticulationPool->destroy(static_cast(&articulation)); + else + { + PX_ASSERT(articulation.getType() == PxArticulationBase::eReducedCoordinate); + mLLArticulationRCPool->destroy(static_cast(&articulation)); + } +} + +PxU32 Sc::Scene::getNbArticulations() const +{ + return mArticulations.size(); +} + +Sc::ArticulationCore* const* Sc::Scene::getArticulations() +{ + return mArticulations.getEntries(); +} + +PxU32 Sc::Scene::getNbConstraints() const +{ + return mConstraints.size(); +} + +Sc::ConstraintCore*const * Sc::Scene::getConstraints() +{ + return mConstraints.getEntries(); +} + +PxU32 Sc::Scene::createAggregate(void* userData, bool selfCollisions) +{ + const physx::Bp::BoundsIndex index = getElementIDPool().createID(); + mBoundsArray->initEntry(index); +#ifdef BP_USE_AGGREGATE_GROUP_TAIL + return mAABBManager->createAggregate(index, Bp::FilterGroup::eINVALID, userData, selfCollisions); +#else + // PT: TODO: ideally a static compound would have a static group + const PxU32 rigidId = getRigidIDTracker().createID(); + const Bp::FilterGroup::Enum bpGroup = Bp::FilterGroup::Enum(rigidId + Bp::FilterGroup::eDYNAMICS_BASE); + return mAABBManager->createAggregate(index, bpGroup, userData, selfCollisions); +#endif +} + +void Sc::Scene::deleteAggregate(PxU32 id) +{ + Bp::BoundsIndex index; + Bp::FilterGroup::Enum bpGroup; +#ifdef BP_USE_AGGREGATE_GROUP_TAIL + if(mAABBManager->destroyAggregate(index, bpGroup, id)) + { + getElementIDPool().releaseID(index); + } +#else + if(mAABBManager->destroyAggregate(index, bpGroup, id)) + { + getElementIDPool().releaseID(index); + + // PT: this is clumsy.... + const PxU32 rigidId = PxU32(bpGroup) - Bp::FilterGroup::eDYNAMICS_BASE; + getRigidIDTracker().releaseID(rigidId); + } +#endif +} + +void Sc::Scene::shiftOrigin(const PxVec3& shift) +{ + // + // adjust low level context + // + mLLContext->shiftOrigin(shift); + + // adjust bounds array + // + mBoundsArray->shiftOrigin(shift); + + // + // adjust broadphase + // + mAABBManager->shiftOrigin(shift); + + // + // adjust constraints + // + ConstraintCore*const * constraints = mConstraints.getEntries(); + for(PxU32 i=0, size = mConstraints.size(); i < size; i++) + { + constraints[i]->getPxConnector()->onOriginShift(shift); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::islandInsertion(PxBaseTask* /*continuation*/) +{ + { + PX_PROFILE_ZONE("Sim.processNewOverlaps.islandInsertion", getContextId()); + + const PxU32 nbShapeIdxCreated = mPreallocatedShapeInteractions.size(); + for (PxU32 a = 0; a < nbShapeIdxCreated; ++a) + { + size_t address = reinterpret_cast(mPreallocatedShapeInteractions[a]); + if (address & 1) + { + ShapeInteraction* interaction = reinterpret_cast(address&size_t(~1)); + + PxsContactManager* contactManager = const_cast(interaction->getContactManager()); + + Sc::BodySim* bs0 = interaction->getShape0().getBodySim(); + Sc::BodySim* bs1 = interaction->getShape1().getBodySim(); + + IG::NodeIndex nodeIndexB; + if (bs1) + nodeIndexB = bs1->getNodeIndex(); + + IG::EdgeIndex edgeIdx = mSimpleIslandManager->addContactManager(contactManager, bs0->getNodeIndex(), nodeIndexB, interaction); + interaction->mEdgeIndex = edgeIdx; + + if (contactManager) + contactManager->getWorkUnit().mEdgeIndex = edgeIdx; + } + } + + // - Wakes actors that lost touch if appropriate + //processLostTouchPairs(); + + if(mCCDPass == 0) + { + mSimpleIslandManager->firstPassIslandGen(); + } + } +} + +void Sc::Scene::registerContactManagers(PxBaseTask* /*continuation*/) +{ + { + PxvNphaseImplementationContext* nphaseContext = mLLContext->getNphaseImplementationContext(); + PX_PROFILE_ZONE("Sim.processNewOverlaps.registerCms", getContextId()); + //nphaseContext->registerContactManagers(mPreallocatedContactManagers.begin(), mPreallocatedContactManagers.size(), mLLContext->getContactManagerPool().getMaxUsedIndex()); + const PxU32 nbCmsCreated = mPreallocatedContactManagers.size(); + for (PxU32 a = 0; a < nbCmsCreated; ++a) + { + size_t address = reinterpret_cast(mPreallocatedContactManagers[a]); + if (address & 1) + { + PxsContactManager* cm = reinterpret_cast(address&size_t(~1)); + nphaseContext->registerContactManager(cm, 0, 0); + } + } + } +} + +void Sc::Scene::registerInteractions(PxBaseTask* /*continuation*/) +{ + { + PX_PROFILE_ZONE("Sim.processNewOverlaps.registerInteractions", getContextId()); + const PxU32 nbShapeIdxCreated = mPreallocatedShapeInteractions.size(); + for (PxU32 a = 0; a < nbShapeIdxCreated; ++a) + { + size_t address = reinterpret_cast(mPreallocatedShapeInteractions[a]); + if (address & 1) + { + ShapeInteraction* interaction = reinterpret_cast(address&size_t(~1)); + + ActorSim& actorSim0 = interaction->getActorSim0(); + ActorSim& actorSim1 = interaction->getActorSim1(); + actorSim0.registerInteractionInActor(interaction); + actorSim1.registerInteractionInActor(interaction); + + Sc::BodySim* bs0 = actorSim0.isDynamicRigid() ? static_cast(&actorSim0) : NULL; + Sc::BodySim* bs1 = actorSim1.isDynamicRigid() ? static_cast(&actorSim1) : NULL; + + bs0->registerCountedInteraction(); + if(bs1) + bs1->registerCountedInteraction(); + } + } + + const PxU32 nbMarkersCreated = mPreallocatedInteractionMarkers.size(); + for (PxU32 a = 0; a < nbMarkersCreated; ++a) + { + size_t address = reinterpret_cast(mPreallocatedInteractionMarkers[a]); + if (address & 1) + { + ElementInteractionMarker* interaction = reinterpret_cast(address&size_t(~1)); + interaction->registerInActors(NULL); + } + } + } +} + +void Sc::Scene::registerSceneInteractions(PxBaseTask* /*continuation*/) +{ + PX_PROFILE_ZONE("Sim.processNewOverlaps.registerInteractionsScene", getContextId()); + const PxU32 nbShapeIdxCreated = mPreallocatedShapeInteractions.size(); + for (PxU32 a = 0; a < nbShapeIdxCreated; ++a) + { + size_t address = reinterpret_cast(mPreallocatedShapeInteractions[a]); + if (address & 1) + { + ShapeInteraction* interaction = reinterpret_cast(address&size_t(~1)); + registerInteraction(interaction, interaction->getContactManager() != NULL); + mNPhaseCore->registerInteraction(interaction); + + const PxsContactManager* cm = interaction->getContactManager(); + if (cm != NULL) + { + mLLContext->setActiveContactManager(cm); + } + } + } + + const PxU32 nbInteractionMarkers = mPreallocatedInteractionMarkers.size(); + for (PxU32 a = 0; a < nbInteractionMarkers; ++a) + { + size_t address = reinterpret_cast(mPreallocatedInteractionMarkers[a]); + if (address & 1) + { + ElementInteractionMarker* interaction = reinterpret_cast(address&size_t(~1)); + registerInteraction(interaction, false); + mNPhaseCore->registerInteraction(interaction); + } + } +} + +class OverlapFilterTask : public Cm::Task +{ +public: + static const PxU32 MaxPairs = 512; + Sc::NPhaseCore* mNPhaseCore; + const Bp::AABBOverlap* mPairs; + + PxU32 mNbToProcess; + + PxU32 mKeepMap[MaxPairs/32]; + PxU32 mCallbackMap[MaxPairs/32]; + + PxFilterInfo* mFinfo; + + PxU32 mNbToKeep; + PxU32 mNbToSuppress; + PxU32 mNbToCallback; + + OverlapFilterTask* mNext; + + OverlapFilterTask(PxU64 contextID, Sc::NPhaseCore* nPhaseCore, PxFilterInfo* fInfo, const Bp::AABBOverlap* pairs, const PxU32 nbToProcess) : + Cm::Task (contextID), + mNPhaseCore (nPhaseCore), + mPairs (pairs), + mNbToProcess (nbToProcess), + mFinfo (fInfo), + mNbToKeep (0), + mNbToSuppress (0), + mNbToCallback (0), + mNext (NULL) + { + PxMemZero(mKeepMap, sizeof(mKeepMap)); + PxMemZero(mCallbackMap, sizeof(mCallbackMap)); + } + + virtual void runInternal() + { + mNPhaseCore->runOverlapFilters( mNbToProcess, mPairs, mFinfo, mNbToKeep, mNbToSuppress, mNbToCallback, mKeepMap, mCallbackMap); + } + + virtual const char* getName() const { return "OverlapFilterTask"; } +}; + +class OnOverlapCreatedTask : public Cm::Task +{ +public: + Sc::NPhaseCore* mNPhaseCore; + const Bp::AABBOverlap* mPairs; + const PxFilterInfo* mFinfo; + PxsContactManager** mContactManagers; + Sc::ShapeInteraction** mShapeInteractions; + Sc::ElementInteractionMarker** mInteractionMarkers; + PxU32 mNbToProcess; + + OnOverlapCreatedTask(PxU64 contextID, Sc::NPhaseCore* nPhaseCore, const Bp::AABBOverlap* pairs, const PxFilterInfo* fInfo, PxsContactManager** contactManagers, Sc::ShapeInteraction** shapeInteractions, Sc::ElementInteractionMarker** interactionMarkers, + PxU32 nbToProcess) : + Cm::Task (contextID), + mNPhaseCore (nPhaseCore), + mPairs (pairs), + mFinfo (fInfo), + mContactManagers (contactManagers), + mShapeInteractions (shapeInteractions), + mInteractionMarkers (interactionMarkers), + mNbToProcess (nbToProcess) + { + } + + virtual void runInternal() + { + PxsContactManager** currentCm = mContactManagers; + Sc::ShapeInteraction** currentSI = mShapeInteractions; + Sc::ElementInteractionMarker** currentEI = mInteractionMarkers; + + for(PxU32 i=0; i(pair.mUserData1); + Sc::ShapeSim* s1 = reinterpret_cast(pair.mUserData0); + + Sc::ElementSimInteraction* interaction = mNPhaseCore->createRbElementInteraction(mFinfo[i], *s0, *s1, *currentCm, *currentSI, *currentEI, 0); + if(interaction) + { + if(interaction->getType() == Sc::InteractionType::eOVERLAP) + { + *currentSI = reinterpret_cast(reinterpret_cast(*currentSI) | 1); + currentSI++; + + if(static_cast(interaction)->getContactManager()) + { + *currentCm = reinterpret_cast(reinterpret_cast(*currentCm) | 1); + currentCm++; + } + } + else if(interaction->getType() == Sc::InteractionType::eMARKER) + { + *currentEI = reinterpret_cast(reinterpret_cast(*currentEI) | 1); + currentEI++; + } + } + } + } + + virtual const char* getName() const { return "OnOverlapCreatedTask"; } +}; + +void Sc::Scene::finishBroadPhase(PxBaseTask* continuation) +{ + PX_UNUSED(continuation); + PX_PROFILE_ZONE("Sc::Scene::finishBroadPhase", getContextId()); + + Bp::AABBManager* aabbMgr = mAABBManager; + + { + PX_PROFILE_ZONE("Sim.processNewOverlaps", getContextId()); + + { + //KS - these functions call "registerInActors", while OverlapFilterTask reads the list of interactions + //in an actor. This could lead to a race condition and a crash if they occur at the same time, so we + //serialize these operations + PX_PROFILE_ZONE("Sim.processNewOverlaps.createOverlapsNoShapeInteractions", getContextId()); + { + PxU32 createdOverlapCount; + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getCreatedOverlaps(Bp::ElementType::eTRIGGER, createdOverlapCount); + + mLLContext->getSimStats().mNbNewPairs += createdOverlapCount; + mNPhaseCore->onOverlapCreated(p, createdOverlapCount); + } + } + + { + PxU32 createdOverlapCount; + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getCreatedOverlaps(Bp::ElementType::eSHAPE, createdOverlapCount); + { + //We allocate at least 1 element in this array to ensure that the onOverlapCreated functions don't go bang! + mPreallocatedContactManagers.reserve(1); + mPreallocatedShapeInteractions.reserve(1); + mPreallocatedInteractionMarkers.reserve(1); + + mPreallocatedContactManagers.forceSize_Unsafe(1); + mPreallocatedShapeInteractions.forceSize_Unsafe(1); + mPreallocatedInteractionMarkers.forceSize_Unsafe(1); + } + + mLLContext->getSimStats().mNbNewPairs += createdOverlapCount; + + mPreallocateContactManagers.setContinuation(continuation); + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + mFilterInfo.forceSize_Unsafe(0); + mFilterInfo.reserve(createdOverlapCount); + mFilterInfo.forceSize_Unsafe(createdOverlapCount); + + const PxU32 nbPairsPerTask = OverlapFilterTask::MaxPairs; + mOverlapFilterTaskHead = NULL; + OverlapFilterTask* previousTask = NULL; + for(PxU32 a=0; asetContinuation(&mPreallocateContactManagers); + task->removeReference(); + + if(previousTask) + previousTask->mNext = task; + else + mOverlapFilterTaskHead = task; + + previousTask = task; + } + } + + mPreallocateContactManagers.removeReference(); + } +} + +void Sc::Scene::preallocateContactManagers(PxBaseTask* continuation) +{ + //Iterate over all filter tasks and work out how many pairs we need... + + PxU32 createdOverlapCount = 0; + + PxU32 totalCreatedPairs = 0; + PxU32 totalSuppressPairs = 0; + + PxU32 overlapCount; + Bp::AABBOverlap* PX_RESTRICT p = mAABBManager->getCreatedOverlaps(Bp::ElementType::eSHAPE, overlapCount); + PxFilterInfo* fInfo = mFilterInfo.begin(); + + OverlapFilterTask* task = mOverlapFilterTaskHead; + while(task) + { + if(task->mNbToCallback) + { + //Iterate and process callbacks. Refilter then increment the results, setting the appropriate settings + const FilteringContext context(*this, mNPhaseCore->mFilterPairManager); + + for(PxU32 w = 0; w < (OverlapFilterTask::MaxPairs / 32); ++w) + { + for(PxU32 b = task->mCallbackMap[w]; b; b &= b - 1) + { + const PxU32 index = (w << 5) + Ps::lowestSetBit(b); + const Bp::AABBOverlap& pair = task->mPairs[index]; + Sc::ShapeSim* s0 = reinterpret_cast(pair.mUserData0); + Sc::ShapeSim* s1 = reinterpret_cast(pair.mUserData1); + + const PxFilterInfo finfo = filterRbCollisionPairSecondStage(context, *s0, *s1, s0->getBodySim(), s1->getBodySim(), INVALID_FILTER_PAIR_INDEX, true); + + task->mFinfo[index] = finfo; + + if(!(finfo.filterFlags & PxFilterFlag::eKILL)) + { + if((finfo.filterFlags & PxFilterFlag::eSUPPRESS) == false) + task->mNbToKeep++; + else + task->mNbToSuppress++; + task->mKeepMap[index / 32] |= (1 << (index & 31)); + } + } + } + } + + totalCreatedPairs += task->mNbToKeep; + totalSuppressPairs += task->mNbToSuppress; + task = task->mNext; + } + + { + //We allocate at least 1 element in this array to ensure that the onOverlapCreated functions don't go bang! + mPreallocatedContactManagers.reserve(totalCreatedPairs+1); + mPreallocatedShapeInteractions.reserve(totalCreatedPairs+1); + mPreallocatedInteractionMarkers.reserve(totalSuppressPairs+1); + + mPreallocatedContactManagers.forceSize_Unsafe(totalCreatedPairs); + mPreallocatedShapeInteractions.forceSize_Unsafe(totalCreatedPairs); + mPreallocatedInteractionMarkers.forceSize_Unsafe(totalSuppressPairs); + } + + const PxU32 nbPairsPerTask = 256; + PxsContactManager** cms = mPreallocatedContactManagers.begin(); + Sc::ShapeInteraction** shapeInter = mPreallocatedShapeInteractions.begin(); + Sc::ElementInteractionMarker** markerIter = mPreallocatedInteractionMarkers.begin(); + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + struct Local + { + static void processBatch(const PxU32 createdCurrIdx, PxU32& createdStartIdx, const PxU32 suppressedCurrIdx, PxU32& suppressedStartIdx, const PxU32 batchSize, + PxsContext* const context, NPhaseCore* const core, OnOverlapCreatedTask* const createTask, PxBaseTask* const continuation_, + PxsContactManager** const cms_, Sc::ShapeInteraction** const shapeInter_, Sc::ElementInteractionMarker** const markerIter_) + { + const PxU32 nbToCreate = createdCurrIdx - createdStartIdx; + const PxU32 nbToSuppress = suppressedCurrIdx - suppressedStartIdx; + + context->getContactManagerPool().preallocate(nbToCreate, cms_ + createdStartIdx); + for(PxU32 i=0; imShapeInteractionPool.allocate(); + for(PxU32 i=0; imInteractionMarkerPool.allocate(); + + createdStartIdx = createdCurrIdx; + suppressedStartIdx = suppressedCurrIdx; + + createTask->mNbToProcess = batchSize; + createTask->setContinuation(continuation_); + createTask->removeReference(); + } + }; + + // PT: TODO: why do we create the task immediately? Why not create it only when a batch is full? + OnOverlapCreatedTask* createTask = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(OnOverlapCreatedTask)), OnOverlapCreatedTask)(getContextId(), mNPhaseCore, p, fInfo, cms, shapeInter, markerIter, 0); + + PxU32 batchSize = 0; + PxU32 suppressedStartIdx = 0; + PxU32 createdStartIdx = 0; + PxU32 suppressedCurrIdx = 0; + PxU32 createdCurrIdx = 0; + PxU32 currentReadIdx = 0; + + task = mOverlapFilterTaskHead; + while(task) + { + if(task->mNbToKeep || task->mNbToSuppress) + { + for(PxU32 w = 0; w < (OverlapFilterTask::MaxPairs/32); ++w) + { + for(PxU32 b = task->mKeepMap[w]; b; b &= b-1) + { + const PxU32 index = (w<<5) + Ps::lowestSetBit(b); + + if(createdOverlapCount < (index + currentReadIdx)) + { + p[createdOverlapCount] = task->mPairs[index]; + fInfo[createdOverlapCount] = task->mFinfo[index]; + } + createdOverlapCount++; + batchSize++; + } + } + + suppressedCurrIdx += task->mNbToSuppress; + createdCurrIdx += task->mNbToKeep; + + if(batchSize >= nbPairsPerTask) + { + Local::processBatch(createdCurrIdx, createdStartIdx, suppressedCurrIdx, suppressedStartIdx, batchSize, mLLContext, mNPhaseCore, createTask, continuation, cms, shapeInter, markerIter); + + createTask = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(OnOverlapCreatedTask)), OnOverlapCreatedTask)(getContextId(), mNPhaseCore, p + createdOverlapCount, + fInfo + createdOverlapCount, cms + createdStartIdx, shapeInter + createdStartIdx, markerIter + suppressedStartIdx, 0); + + batchSize = 0; + } + } + currentReadIdx += OverlapFilterTask::MaxPairs; + task = task->mNext; + } + + if(batchSize) + Local::processBatch(createdCurrIdx, createdStartIdx, suppressedCurrIdx, suppressedStartIdx, batchSize, mLLContext, mNPhaseCore, createTask, continuation, cms, shapeInter, markerIter); +} + +void Sc::Scene::finishBroadPhaseStage2(const PxU32 ccdPass) +{ + PX_PROFILE_ZONE("Sc::Scene::finishBroadPhase2", getContextId()); + + Bp::AABBManager* aabbMgr = mAABBManager; + + for(PxU32 i=0; igetDestroyedOverlaps(Bp::ElementType::Enum(i), destroyedOverlapCount); + mLLContext->getSimStats().mNbLostPairs += destroyedOverlapCount; + } + + //KS - we need to defer processing lost overlaps until later! + if (ccdPass) + { + PX_PROFILE_ZONE("Sim.processLostOverlaps", getContextId()); + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + const bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + PxU32 destroyedOverlapCount; + + { + Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + + while(destroyedOverlapCount--) + { + ElementSim* volume0 = reinterpret_cast(p->mUserData0); + ElementSim* volume1 = reinterpret_cast(p->mUserData1); + + //KS - this is a bit ugly. We split the "onOverlapRemoved" for shape interactions to parallelize it and that means + //that we have to call each of the individual stages of the remove here. + + //First, we have to get the interaction pointer... + Sc::ElementSimInteraction* interaction = mNPhaseCore->onOverlapRemovedStage1(volume0, volume1); + p->mPairUserData = interaction; + if(interaction) + { + if(interaction->getType() == Sc::InteractionType::eOVERLAP || interaction->getType() == Sc::InteractionType::eMARKER) + { + //If it's a standard "overlap" interaction, we have to send a lost touch report, unregister it, and destroy its manager and island gen data. + if(interaction->getType() == Sc::InteractionType::eOVERLAP) + { + Sc::ShapeInteraction* si = static_cast(interaction); + mNPhaseCore->lostTouchReports(si, PxU32(PairReleaseFlag::eWAKE_ON_LOST_TOUCH), 0, outputs, useAdaptiveForce); + si->destroyManager(); + si->clearIslandGenData(); + } + + unregisterInteraction(interaction); + mNPhaseCore->unregisterInteraction(interaction); + } + + //Then call "onOverlapRemoved" to actually free the interaction + mNPhaseCore->onOverlapRemoved(volume0, volume1, ccdPass, interaction, outputs, useAdaptiveForce); + } + p++; + } + } + + { + Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eTRIGGER, destroyedOverlapCount); + + while(destroyedOverlapCount--) + { + ElementSim* volume0 = reinterpret_cast(p->mUserData0); + ElementSim* volume1 = reinterpret_cast(p->mUserData1); + + p->mPairUserData = NULL; + + //KS - this is a bit ugly. + mNPhaseCore->onOverlapRemoved(volume0, volume1, ccdPass, NULL, outputs, useAdaptiveForce); + p++; + } + } + } + + // - Wakes actors that lost touch if appropriate + processLostTouchPairs(); + + if (ccdPass) + { + aabbMgr->getBroadPhase()->deletePairs(); + + aabbMgr->freeBuffers(); + } +} + +void Sc::Scene::secondPassNarrowPhase(PxBaseTask* /*continuation*/) +{ + { + PX_PROFILE_ZONE("Sim.postIslandGen", getContextId()); + mSimpleIslandManager->additionalSpeculativeActivation(); + // wake interactions + { + PX_PROFILE_ZONE("ScScene.wakeInteractions", getContextId()); + const IG::IslandSim& speculativeSim = mSimpleIslandManager->getSpeculativeIslandSim(); + + //KS - only wake contact managers based on speculative state to trigger contact gen. Waking actors based on accurate state + //should activate and joints. + { + PxU32 nbActivatingEdges = speculativeSim.getNbActivatedEdges(IG::Edge::eCONTACT_MANAGER); + const IG::EdgeIndex* activatingEdges = speculativeSim.getActivatedEdges(IG::Edge::eCONTACT_MANAGER); + + for(PxU32 i = 0; i < nbActivatingEdges; ++i) + { + Sc::Interaction* interaction = mSimpleIslandManager->getInteraction(activatingEdges[i]); + + if(interaction && !interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE)) + { + if(speculativeSim.getEdge(activatingEdges[i]).isActive()) + { + const bool proceed = activateInteraction(interaction, NULL); + + if(proceed && (interaction->getType() < InteractionType::eTRACKED_IN_SCENE_COUNT)) + notifyInteractionActivated(interaction); + } + } + } + } + } + } + mLLContext->secondPassUpdateContactManager(mDt, &mPostNarrowPhase); // Starts update of contact managers +} + +//~BROADPHASE + +void Sc::Scene::registerMaterialInNP(const PxsMaterialCore& materialCore) +{ + mLLContext->getNphaseImplementationContext()->registerMaterial(materialCore); +} + +void Sc::Scene::updateMaterialInNP(const PxsMaterialCore& materialCore) +{ + mLLContext->getNphaseImplementationContext()->updateMaterial(materialCore); +} + +void Sc::Scene::unregisterMaterialInNP(const PxsMaterialCore& materialCore) +{ + mLLContext->getNphaseImplementationContext()->unregisterMaterial(materialCore); +} + +bool Sc::activateInteraction(Sc::Interaction* interaction, void* data) +{ + switch(interaction->getType()) + { + case InteractionType::eOVERLAP: + return static_cast(interaction)->onActivate_(data); + + case InteractionType::eTRIGGER: + return static_cast(interaction)->onActivate_(data); + + case InteractionType::eMARKER: + return static_cast(interaction)->onActivate_(data); + + case InteractionType::eCONSTRAINTSHADER: + return static_cast(interaction)->onActivate_(data); + + case InteractionType::eARTICULATION: + return static_cast(interaction)->onActivate_(data); + + case InteractionType::eTRACKED_IN_SCENE_COUNT: + case InteractionType::eINVALID: + PX_ASSERT(0); + break; + } + return false; +} + +bool Sc::deactivateInteraction(Sc::Interaction* interaction) +{ + switch(interaction->getType()) + { + case InteractionType::eOVERLAP: + return static_cast(interaction)->onDeactivate_(); + + case InteractionType::eTRIGGER: + return static_cast(interaction)->onDeactivate_(); + + case InteractionType::eMARKER: + return static_cast(interaction)->onDeactivate_(); + + case InteractionType::eCONSTRAINTSHADER: + return static_cast(interaction)->onDeactivate_(); + + case InteractionType::eARTICULATION: + return static_cast(interaction)->onDeactivate_(); + + case InteractionType::eTRACKED_IN_SCENE_COUNT: + case InteractionType::eINVALID: + PX_ASSERT(0); + break; + } + return false; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp new file mode 100644 index 000000000..5d53be878 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp @@ -0,0 +1,372 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxErrorCallback.h" +#include "ScShapeSim.h" +#include "ScPhysics.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "GuHeightField.h" +#include "ScMaterialCore.h" + +using namespace physx; +using namespace Sc; + +// djs: temporary cruft + +static PxConvexMeshGeometryLL extendForLL(const PxConvexMeshGeometry& hlGeom) +{ + PxConvexMeshGeometryLL llGeom; + static_cast(llGeom) = hlGeom; + + Gu::ConvexMesh* cm = static_cast(hlGeom.convexMesh); + + llGeom.hullData = &(cm->getHull()); + llGeom.gpuCompatible = hlGeom.convexMesh->isGpuCompatible(); + + return llGeom; +} + +static PxTriangleMeshGeometryLL extendForLL(const PxTriangleMeshGeometry& hlGeom) +{ + PxTriangleMeshGeometryLL llGeom; + static_cast(llGeom) = hlGeom; + + Gu::TriangleMesh* tm = static_cast(hlGeom.triangleMesh); + llGeom.meshData = tm; + llGeom.materialIndices = tm->getMaterials(); + llGeom.materials = static_cast(hlGeom).materials; + + return llGeom; +} + +static PxHeightFieldGeometryLL extendForLL(const PxHeightFieldGeometry& hlGeom) +{ + PxHeightFieldGeometryLL llGeom; + static_cast(llGeom) = hlGeom; + + Gu::HeightField* hf = static_cast(hlGeom.heightField); + + llGeom.heightFieldData = &hf->getData(); + + llGeom.materials = static_cast(hlGeom).materials; + + return llGeom; +} + +ShapeCore::ShapeCore(const PxGeometry& geometry, PxShapeFlags shapeFlags, const PxU16* materialIndices, PxU16 materialCount) : + mRestOffset(0.0f), + mTorsionalRadius(0.f), + mMinTorsionalPatchRadius(0.f) +{ + mCore.mOwnsMaterialIdxMemory = true; + + PX_ASSERT(materialCount > 0); + + const PxTolerancesScale& scale = Physics::getInstance().getTolerancesScale(); + mCore.geometry.set(geometry); + mCore.transform = PxTransform(PxIdentity); + mCore.contactOffset = 0.02f * scale.length; + + mCore.mShapeFlags = shapeFlags; + + setMaterialIndices(materialIndices, materialCount); +} + +// PX_SERIALIZATION +ShapeCore::ShapeCore(const PxEMPTY) : + mQueryFilterData (PxEmpty), + mSimulationFilterData (PxEmpty), + mCore (PxEmpty) +{ + mCore.mOwnsMaterialIdxMemory = false; +} +//~PX_SERIALIZATION + +ShapeCore::~ShapeCore() +{ + if(mCore.geometry.getType() == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + if(mCore.mOwnsMaterialIdxMemory) + meshGeom.materials.deallocate(); + } + else if(mCore.geometry.getType() == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + if(mCore.mOwnsMaterialIdxMemory) + hfGeom.materials.deallocate(); + } +} + +PxU16 Sc::ShapeCore::getNbMaterialIndices() const +{ + const PxGeometryType::Enum geomType = mCore.geometry.getType(); + + if((geomType != PxGeometryType::eTRIANGLEMESH) && (geomType != PxGeometryType::eHEIGHTFIELD)) + { + return 1; + } + else if(geomType == PxGeometryType::eTRIANGLEMESH) + { + const PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + return meshGeom.materials.numIndices; + } + else + { + PX_ASSERT(geomType == PxGeometryType::eHEIGHTFIELD); + const PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + return hfGeom.materials.numIndices; + } +} + +const PxU16* Sc::ShapeCore::getMaterialIndices() const +{ + const PxGeometryType::Enum geomType = mCore.geometry.getType(); + + if((geomType != PxGeometryType::eTRIANGLEMESH) && (geomType != PxGeometryType::eHEIGHTFIELD)) + { + return &mCore.materialIndex; + } + else if(geomType == PxGeometryType::eTRIANGLEMESH) + { + const PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + return meshGeom.materials.indices; + } + else + { + PX_ASSERT(geomType == PxGeometryType::eHEIGHTFIELD); + const PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + return hfGeom.materials.indices; + } +} + +PX_FORCE_INLINE void setMaterialsHelper(MaterialIndicesStruct& materials, const PxU16* materialIndices, PxU16 materialIndexCount, PxU8& ownsMemory) +{ + if(materials.numIndices < materialIndexCount) + { + if(materials.indices && ownsMemory) + materials.deallocate(); + materials.allocate(materialIndexCount); + ownsMemory = true; + } + PxMemCopy(materials.indices, materialIndices, sizeof(PxU16)*materialIndexCount); + materials.numIndices = materialIndexCount; +} + +void ShapeCore::setMaterialIndices(const PxU16* materialIndices, PxU16 materialIndexCount) +{ + const PxGeometryType::Enum geomType = mCore.geometry.getType(); + mCore.materialIndex = materialIndices[0]; + + if(geomType == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + setMaterialsHelper(meshGeom.materials, materialIndices, materialIndexCount, mCore.mOwnsMaterialIdxMemory); + } + else if(geomType == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + setMaterialsHelper(hfGeom.materials, materialIndices, materialIndexCount, mCore.mOwnsMaterialIdxMemory); + } +} + +void ShapeCore::setGeometry(const PxGeometry& geom) +{ + const PxGeometryType::Enum oldGeomType = mCore.geometry.getType(); + const PxGeometryType::Enum newGeomType = geom.getType(); + + // copy material related data to restore it after the new geometry has been set + MaterialIndicesStruct materials; + PX_ASSERT(materials.numIndices == 0); + + if(oldGeomType == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + materials = meshGeom.materials; + } + else if(oldGeomType == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + materials = hfGeom.materials; + } + + mCore.geometry.set(geom); + + if((newGeomType == PxGeometryType::eTRIANGLEMESH) || (newGeomType == PxGeometryType::eHEIGHTFIELD)) + { + MaterialIndicesStruct* newMaterials; + + if(newGeomType == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + newMaterials = &meshGeom.materials; + } + else + { + PX_ASSERT(newGeomType == PxGeometryType::eHEIGHTFIELD); + PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + newMaterials = &hfGeom.materials; + } + + if(materials.numIndices != 0) // old type was mesh type + *newMaterials = materials; + else + { // old type was non-mesh type + newMaterials->allocate(1); + *newMaterials->indices = mCore.materialIndex; + mCore.mOwnsMaterialIdxMemory = true; + } + } + else if((materials.numIndices != 0) && mCore.mOwnsMaterialIdxMemory) + { + // geometry changed to non-mesh type + materials.deallocate(); + } +} + +PxShape* ShapeCore::getPxShape() +{ + return Sc::gOffsetTable.convertScShape2Px(this); +} + +const PxShape* ShapeCore::getPxShape() const +{ + return Sc::gOffsetTable.convertScShape2Px(this); +} + +// PX_SERIALIZATION + +PX_FORCE_INLINE void exportExtraDataMaterials(PxSerializationContext& stream, const MaterialIndicesStruct& materials) +{ + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(materials.indices, sizeof(PxU16)*materials.numIndices); +} + +void ShapeCore::exportExtraData(PxSerializationContext& stream) +{ + const PxGeometryType::Enum geomType = mCore.geometry.getType(); + + if(geomType == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + exportExtraDataMaterials(stream, meshGeom.materials); + } + else if(geomType == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + exportExtraDataMaterials(stream, hfGeom.materials); + } +} + +void ShapeCore::importExtraData(PxDeserializationContext& context) +{ + const PxGeometryType::Enum geomType = mCore.geometry.getType(); + + if(geomType == PxGeometryType::eTRIANGLEMESH) + { + MaterialIndicesStruct& materials = mCore.geometry.get().materials; + materials.indices = context.readExtraData(materials.numIndices); + } + else if(geomType == PxGeometryType::eHEIGHTFIELD) + { + MaterialIndicesStruct& materials = mCore.geometry.get().materials; + materials.indices = context.readExtraData(materials.numIndices); + } +} + +void ShapeCore::resolveMaterialReference(PxU32 materialTableIndex, PxU16 materialIndex) +{ + if(materialTableIndex == 0) + { + mCore.materialIndex = materialIndex; + } + + PxGeometry& geom = const_cast(mCore.geometry.getGeometry()); + + if(geom.getType() == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometryLL& hfGeom = static_cast(geom); + hfGeom.materials.indices[materialTableIndex] = materialIndex; + } + else if(geom.getType() == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = static_cast(geom); + meshGeom.materials.indices[materialTableIndex] = materialIndex; + } +} + +void ShapeCore::resolveReferences(PxDeserializationContext& context) +{ + // Resolve geometry pointers if needed + PxGeometry& geom = const_cast(mCore.geometry.getGeometry()); + + switch(geom.getType()) + { + case PxGeometryType::eCONVEXMESH: + { + PxConvexMeshGeometryLL& convexGeom = static_cast(geom); + context.translatePxBase(convexGeom.convexMesh); + + // update the hullData pointer + static_cast(geom) = extendForLL(convexGeom); + } + break; + + case PxGeometryType::eHEIGHTFIELD: + { + PxHeightFieldGeometryLL& hfGeom = static_cast(geom); + context.translatePxBase(hfGeom.heightField); + + // update hf pointers + static_cast(geom) = extendForLL(hfGeom); + } + break; + + case PxGeometryType::eTRIANGLEMESH: + { + PxTriangleMeshGeometryLL& meshGeom = static_cast(geom); + context.translatePxBase(meshGeom.triangleMesh); + + // update mesh pointers + static_cast(geom) = extendForLL(meshGeom); + } + break; + case PxGeometryType::eSPHERE: + case PxGeometryType::ePLANE: + case PxGeometryType::eCAPSULE: + case PxGeometryType::eBOX: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + + } +} + +//~PX_SERIALIZATION diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp new file mode 100644 index 000000000..d9c9af0b9 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp @@ -0,0 +1,1208 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScShapeInteraction.h" +#include "ScPhysics.h" +#include "PxsContext.h" +#include "PxsMaterialCombiner.h" +#include "GuTriangleMesh.h" +#include "ScStaticSim.h" +#include "PxvManager.h" +#include "PxsSimpleIslandManager.h" +#include "PxvNphaseImplementationContext.h" + +using namespace physx; + +Sc::ShapeInteraction::ShapeInteraction(ShapeSim& s1, ShapeSim& s2, PxPairFlags pairFlags, PxsContactManager* contactManager) : + ElementSimInteraction (s1, s2, InteractionType::eOVERLAP, InteractionFlag::eRB_ELEMENT|InteractionFlag::eFILTERABLE), + mContactReportStamp (PX_INVALID_U32), + mFlags (0), + mActorPair (NULL), + mReportPairIndex (INVALID_REPORT_PAIR_ID), + mManager (NULL), + mEdgeIndex (IG_INVALID_EDGE), + mReportStreamIndex (0) +{ + // The PxPairFlags get stored in the SipFlag, make sure any changes get noticed + PX_COMPILE_TIME_ASSERT(PxPairFlag::eSOLVE_CONTACT == (1<<0)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eMODIFY_CONTACTS == (1<<1)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_FOUND == (1<<2)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_PERSISTS == (1<<3)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_LOST == (1<<4)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_CCD == (1<<5)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND == (1<<6)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS == (1<<7)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST == (1<<8)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_CONTACT_POINTS == (1<<9)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eDETECT_DISCRETE_CONTACT == (1<<10)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eDETECT_CCD_CONTACT == (1<<11)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::ePRE_SOLVER_VELOCITY == (1<<12)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::ePOST_SOLVER_VELOCITY == (1<<13)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eCONTACT_EVENT_POSE == (1<<14)); + PX_COMPILE_TIME_ASSERT((PAIR_FLAGS_MASK & PxPairFlag::eSOLVE_CONTACT) == PxPairFlag::eSOLVE_CONTACT); + PX_COMPILE_TIME_ASSERT((PxPairFlag::eSOLVE_CONTACT | PAIR_FLAGS_MASK) == PAIR_FLAGS_MASK); + PX_COMPILE_TIME_ASSERT((PAIR_FLAGS_MASK & PxPairFlag::eCONTACT_EVENT_POSE) == PxPairFlag::eCONTACT_EVENT_POSE); + PX_COMPILE_TIME_ASSERT((PxPairFlag::eCONTACT_EVENT_POSE | PAIR_FLAGS_MASK) == PAIR_FLAGS_MASK); + + setPairFlags(pairFlags); + + //Add a fresh edge to the island manager. + Scene& scene = getScene(); + Sc::BodySim* bs0 = getShape0().getBodySim(); + Sc::BodySim* bs1 = getShape1().getBodySim(); + + PX_ASSERT(bs0); // The shapes have to be sorted such that the first shape belongs to a dynamic + + updateFlags(scene, bs0, bs1, pairFlags); + + if(contactManager == NULL) + { + IG::NodeIndex indexA, indexB; + //if(bs0) // the first shape always belongs to a dynamic body (we assert for this above) + { + indexA = bs0->getNodeIndex(); + bs0->registerCountedInteraction(); + } + if(bs1) + { + indexB = bs1->getNodeIndex(); + bs1->registerCountedInteraction(); + } + + IG::SimpleIslandManager* simpleIslandManager = scene.getSimpleIslandManager(); + + mEdgeIndex = simpleIslandManager->addContactManager(NULL, indexA, indexB, this); + + const bool active = registerInActors(contactManager); // this will call onActivate_() on the interaction + scene.getNPhaseCore()->registerInteraction(this); + scene.registerInteraction(this, active); + } + else + { + onActivate_(contactManager); + } +} + + +Sc::ShapeInteraction::~ShapeInteraction() +{ + Sc::BodySim* body0 = getShape0().getBodySim(); + Sc::BodySim* body1 = getShape1().getBodySim(); + PX_ASSERT(body0); // the first shape always belongs to a dynamic body + + body0->unregisterCountedInteraction(); + if (body1) + body1->unregisterCountedInteraction(); + + if(mManager) + { + destroyManager(); + } + + if(mEdgeIndex != IG_INVALID_EDGE) + { + Scene& scene = getScene(); + + scene.getSimpleIslandManager()->removeConnection(mEdgeIndex); + mEdgeIndex = IG_INVALID_EDGE; + + scene.unregisterInteraction(this); + scene.getNPhaseCore()->unregisterInteraction(this); + } + + // This will remove the interaction from the actors list, which will prevent + // update calls to this actor because of Body::wakeUp below. + unregisterFromActors(); + + if(mReportPairIndex != INVALID_REPORT_PAIR_ID) + { + removeFromReportPairList(); + } +} + +void Sc::ShapeInteraction::clearIslandGenData() +{ + if(mEdgeIndex != IG_INVALID_EDGE) + { + Scene& scene = getScene(); + scene.getSimpleIslandManager()->removeConnection(mEdgeIndex); + mEdgeIndex = IG_INVALID_EDGE; + } +} + +void Sc::ShapeInteraction::visualize(Cm::RenderOutput& out, PxsContactManagerOutputIterator& outputs) +{ + if(mManager) // sleeping pairs have no contact points -> do not visualize + { + Scene& scene = getScene(); + const PxReal scale = scene.getVisualizationScale(); + + size_t ptrActor0 = reinterpret_cast(&getShape0().getRbSim()); + size_t ptrActor1 = reinterpret_cast(&getShape1().getRbSim()); + const PxReal flipNormal = (ptrActor0 < ptrActor1) ? 1.0f : -1.0f; + + PxU32 offset; + PxU32 nextOffset = 0; + do + { + const void* contactPatches; + const void* contactPoints; + PxU32 contactDataSize; + PxU32 contactPointCount; + PxU32 contactPatchCount; + const PxReal* impulses; + + offset = nextOffset; + nextOffset = getContactPointData(contactPatches, contactPoints, contactDataSize, contactPointCount, contactPatchCount, impulses, offset, outputs); + + const PxReal param_contactForce = scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_FORCE); + const PxReal param_contactNormal = scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL); + const PxReal param_contactError = scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_ERROR); + const PxReal param_contactPoint = scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT); + + const PxU32* faceIndices = reinterpret_cast(impulses + contactPointCount); + PxContactStreamIterator iter(reinterpret_cast(contactPatches), reinterpret_cast(contactPoints), faceIndices, contactPatchCount, contactPointCount); + + PxU32 i = 0; + while(iter.hasNextPatch()) + { + iter.nextPatch(); + while(iter.hasNextContact()) + { + iter.nextContact(); + + PxReal length = 0; + PxU32 color = 0; + + if((param_contactForce != 0.0f) && impulses) + { + length = scale * param_contactForce * impulses[i]; + color = 0xff0000; + } + else if(param_contactNormal != 0.0f) + { + length = scale * param_contactNormal; + color = 0x0000ff; + } + else if(param_contactError != 0.0f) + { + length = PxAbs(scale * param_contactError * iter.getSeparation()); + color = 0xffff00; + } + + if(length != 0.0f) + out << Cm::RenderOutput::LINES << color << iter.getContactPoint() << iter.getContactPoint() + iter.getContactNormal() * length * flipNormal; + + if(param_contactPoint != 0) + { + PxReal s = scale * 0.1f; + PxVec3 point = iter.getContactPoint(); + + if(0) //temp debug to see identical contacts + point.x += scale * 0.01f * (contactPointCount - i + 1); + + out << Cm::RenderOutput::LINES << PxU32(PxDebugColor::eARGB_RED); + out << point + PxVec3(-s,0,0) << point + PxVec3(s,0,0); + out << point + PxVec3(0,-s,0) << point + PxVec3(0,s,0); + out << point + PxVec3(0,0,-s) << point + PxVec3(0,0,s); + + } + } + } + } + while(nextOffset != offset); + } +} + +PX_FORCE_INLINE void Sc::ShapeInteraction::processReportPairOnActivate() +{ + PX_ASSERT(isReportPair()); + PX_ASSERT(mReportPairIndex == INVALID_REPORT_PAIR_ID); + + if(readFlag(WAS_IN_PERSISTENT_EVENT_LIST)) + { + getScene().getNPhaseCore()->addToPersistentContactEventPairs(this); + mFlags &= ~WAS_IN_PERSISTENT_EVENT_LIST; + } +} + +PX_FORCE_INLINE void Sc::ShapeInteraction::processReportPairOnDeactivate() +{ + PX_ASSERT(isReportPair()); + PX_ASSERT(mReportPairIndex != INVALID_REPORT_PAIR_ID); + PX_COMPILE_TIME_ASSERT(IS_IN_PERSISTENT_EVENT_LIST == (WAS_IN_PERSISTENT_EVENT_LIST >> 1)); + PX_ASSERT(!(readFlag(WAS_IN_PERSISTENT_EVENT_LIST))); + + const PxU32 wasInPersList = (mFlags & IS_IN_PERSISTENT_EVENT_LIST) << 1; + mFlags |= wasInPersList; + + removeFromReportPairList(); +} + +void Sc::ShapeInteraction::setContactReportPostSolverVelocity(ContactStreamManager& cs) +{ + Scene& scene = getScene(); + NPhaseCore* npcore = scene.getNPhaseCore(); + PxU8* stream = npcore->getContactReportPairData(cs.bufferIndex); + + ActorPairReport& apr = getActorPairReport(); + cs.setContactReportPostSolverVelocity(stream, apr.getActorA(), apr.getActorB()); +} + +void Sc::ShapeInteraction::resetManagerCachedState() const +{ + if(mManager) + { + Sc::Scene& scene = getScene(); + PxvNphaseImplementationContext* nphaseImplementationContext = scene.getLowLevelContext()->getNphaseImplementationContext(); + PX_ASSERT(nphaseImplementationContext); + + mManager->resetFrictionCachedState(); + nphaseImplementationContext->refreshContactManager(mManager); + } +} + +/* + This method can be called from various stages in the pipeline, some of which operate before the actor has advanced its pose and some after it has advanced its pose. + Discrete touch found events operate before the pose has been updated. This is because we are using the found event to active the bodies before solve so that we can just + solve the activated bodies. + Lost touch events occur after the pose has been updated. +*/ +void Sc::ShapeInteraction::processUserNotificationSync() +{ + PX_ASSERT(hasTouch()); + + if (mManager) + Ps::prefetchLine(mManager); + + Scene& scene = getScene(); + NPhaseCore* npcore = scene.getNPhaseCore(); + PX_UNUSED(npcore); + + // make sure shape A and shape B are the same way round as the actors (in compounds they may be swapped) + // TODO: make "unswapped" a SIP flag and set it in updateState() + if (mActorPair == NULL) + { + return; + } + ActorPairReport& aPairReport = getActorPairReport(); + + if (!aPairReport.isInContactReportActorPairSet()) + { + aPairReport.setInContactReportActorPairSet(); + npcore->addToContactReportActorPairSet(&aPairReport); + aPairReport.incRefCount(); + } + + aPairReport.createContactStreamManager(*npcore); +} + +void Sc::ShapeInteraction::processUserNotificationAsync(PxU32 contactEvent, PxU16 infoFlags, bool touchLost, + const PxU32 ccdPass, const bool useCurrentTransform, PxsContactManagerOutputIterator& outputs, ContactReportAllocationManager* alloc) +{ + + contactEvent = (!ccdPass) ? contactEvent : (contactEvent | PxPairFlag::eNOTIFY_TOUCH_CCD); + + if (mActorPair == NULL) + { + return; + } + + ActorPairReport& aPairReport = getActorPairReport(); + Scene& scene = getScene(); + NPhaseCore* npcore = scene.getNPhaseCore(); + ContactStreamManager& cs = aPairReport.createContactStreamManager(*npcore); + // Prepare user notification + PxU32 timeStamp = scene.getTimeStamp(); + PxU32 shapePairTimeStamp = scene.getReportShapePairTimeStamp(); + + const PxU32 pairFlags = getPairFlags(); + PX_ASSERT(pairFlags & contactEvent); + + const PxU32 extraDataFlags = pairFlags & CONTACT_REPORT_EXTRA_DATA; + + PxU8* stream = NULL; + ContactShapePair* pairStream = NULL; + + const bool unswapped = &aPairReport.getActorA() == &getShape0().getRbSim(); + const Sc::ShapeSim& shapeA = unswapped ? getShape0() : getShape1(); + const Sc::ShapeSim& shapeB = unswapped ? getShape1() : getShape0(); + + if(aPairReport.streamResetStamp(timeStamp)) + { + PX_ASSERT(mContactReportStamp != shapePairTimeStamp); // actor pair and shape pair timestamps must both be out of sync in this case + + PxU16 maxCount; + if(cs.maxPairCount != 0) + maxCount = cs.maxPairCount; // use value from previous report + else + { + // TODO: Use some kind of heuristic + maxCount = 2; + cs.maxPairCount = maxCount; + } + + PxU32 maxExtraDataSize; + if(!extraDataFlags || touchLost) + { + maxExtraDataSize = 0; + cs.setMaxExtraDataSize(maxExtraDataSize); + } + else + { + PxU32 currentMaxExtraDataSize = cs.getMaxExtraDataSize(); + maxExtraDataSize = ContactStreamManager::computeContactReportExtraDataSize(extraDataFlags, true); + PX_ASSERT(maxExtraDataSize > 0); + if(maxExtraDataSize <= currentMaxExtraDataSize) + maxExtraDataSize = currentMaxExtraDataSize; // use value from previous report + else + cs.setMaxExtraDataSize(maxExtraDataSize); + } + + stream = npcore->reserveContactReportPairData(maxCount, maxExtraDataSize, cs.bufferIndex, alloc); + + if(!maxExtraDataSize) // this is the usual case, so set it first for branch prediction + cs.reset(); + else if(stream) + { + cs.reset(); + PX_ASSERT(extraDataFlags); + PX_ASSERT(!touchLost); + + cs.fillInContactReportExtraData(stream, extraDataFlags, aPairReport.getActorA(), aPairReport.getActorB(), ccdPass, useCurrentTransform, 0, sizeof(ContactStreamHeader)); + if((extraDataFlags & PxPairFlag::ePOST_SOLVER_VELOCITY) && (pairFlags & PxPairFlag::eDETECT_CCD_CONTACT)) + scene.setPostSolverVelocityNeeded(); + } + } + else + { + const PxU32 currentPairCount = cs.currentPairCount; + if(currentPairCount != 0) + { + PxU8* tmpStreamPtr = npcore->getContactReportPairData(cs.bufferIndex); + if(!extraDataFlags) + stream = tmpStreamPtr; // this is the usual case, so set it first for branch prediction + else + { + if(!touchLost) + { + // - the first few shape pair events might not request extra data + // - the events so far were due to touch lost + // - multiple reports due to CCD multiple passes + // Hence, the extra data has to be created/extended now. + // + const PxU16 oldExtraDataSize = cs.extraDataSize; + PxI32 lastContactPass; + if(oldExtraDataSize) + { + ContactStreamHeader* strHeader = reinterpret_cast(tmpStreamPtr); + lastContactPass = strHeader->contactPass; + } + else + lastContactPass = -1; + + if(PxI32(ccdPass) > lastContactPass) // do not send extra data mulitple times for the same contact pass + { + const PxU16 extraDataSize = PxU16(oldExtraDataSize + ContactStreamManager::computeContactReportExtraDataSize(extraDataFlags, (oldExtraDataSize == 0))); + PxU8* strPtr; + if (extraDataSize <= cs.getMaxExtraDataSize()) + strPtr = tmpStreamPtr; + else + strPtr = npcore->resizeContactReportPairData(currentPairCount < cs.maxPairCount ? cs.maxPairCount : PxU32(cs.maxPairCount+1), extraDataSize, cs); + // the check for max pair count is there to avoid another potential allocation further below + + if(strPtr) + { + stream = strPtr; + PxU32 sizeOffset; + if(oldExtraDataSize) + sizeOffset = oldExtraDataSize; + else + sizeOffset = sizeof(ContactStreamHeader); + cs.fillInContactReportExtraData(strPtr, extraDataFlags, aPairReport.getActorA(), aPairReport.getActorB(), ccdPass, useCurrentTransform, currentPairCount, sizeOffset); + if((extraDataFlags & PxPairFlag::ePOST_SOLVER_VELOCITY) && (pairFlags & PxPairFlag::eDETECT_CCD_CONTACT)) + scene.setPostSolverVelocityNeeded(); + } + else + { + stream = tmpStreamPtr; + cs.raiseFlags(ContactStreamManagerFlag::eINCOMPLETE_STREAM); + } + } + else + stream = tmpStreamPtr; + } + else + stream = tmpStreamPtr; + } + } + } + + if(stream) + pairStream = cs.getShapePairs(stream); + else + { + cs.raiseFlags(ContactStreamManagerFlag::eINVALID_STREAM); + return; + } + + ContactShapePair* cp; + if(mContactReportStamp != shapePairTimeStamp) + { + // this shape pair is not in the contact notification stream yet + + if(cs.currentPairCount < cs.maxPairCount) + cp = pairStream + cs.currentPairCount; + else + { + const PxU32 newSize = PxU32(cs.currentPairCount + (cs.currentPairCount >> 1) + 1); + stream = npcore->resizeContactReportPairData(newSize, cs.getMaxExtraDataSize(), cs); + if(stream) + { + pairStream = cs.getShapePairs(stream); + cp = pairStream + cs.currentPairCount; + } + else + { + cs.raiseFlags(ContactStreamManagerFlag::eINCOMPLETE_STREAM); + return; + } + } + + //!!! why is alignment important here? Looks almost like some refactor nonsense + PX_ASSERT(0==(reinterpret_cast(stream) & 0x0f)); // check 16Byte alignment + + mReportStreamIndex = cs.currentPairCount; + cp->shapes[0] = shapeA.getPxShape(); + cp->shapes[1] = shapeB.getPxShape(); + cp->contactPatches = NULL; + cp->contactPoints = NULL; + cp->contactForces = NULL; + cp->contactCount = 0; + cp->patchCount = 0; + cp->constraintStreamSize = 0; + cp->requiredBufferSize = 0; + cp->flags = infoFlags; + PX_ASSERT(contactEvent <= 0xffff); + cp->events = PxU16(contactEvent); + cp->shapeID[0] = shapeA.getID(); + cp->shapeID[1] = shapeB.getID(); + + cs.currentPairCount++; + + mContactReportStamp = shapePairTimeStamp; + } + else + { + // this shape pair is in the contact notification stream already but there is a second event (can happen with force threshold reports, for example). + + PX_ASSERT(mReportStreamIndex < cs.currentPairCount); + cp = &pairStream[mReportStreamIndex]; + cp->events |= contactEvent; + if(touchLost && cp->events & PxPairFlag::eNOTIFY_TOUCH_PERSISTS) + cp->events &= PxU16(~PxPairFlag::eNOTIFY_TOUCH_PERSISTS); + cp->flags |= infoFlags; + } + + if((getPairFlags() & PxPairFlag::eNOTIFY_CONTACT_POINTS) && mManager && (!cp->contactPatches) && !(contactEvent & PxU32(PxPairFlag::eNOTIFY_TOUCH_LOST | PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST))) + { + const PxcNpWorkUnit& workUnit = mManager->getWorkUnit(); + PxsContactManagerOutput* output = NULL; + if(workUnit.mNpIndex & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK) + output = &getScene().getLowLevelContext()->getNphaseImplementationContext()->getNewContactManagerOutput(workUnit.mNpIndex); + else + output = &outputs.getContactManager(workUnit.mNpIndex); + + const PxsCCDContactHeader* ccdContactData = reinterpret_cast(workUnit.ccdContacts); + + const bool isCCDPass = (ccdPass != 0); + if((output->nbPatches && !isCCDPass) || (ccdContactData && (!ccdContactData->isFromPreviousPass) && isCCDPass)) + { + const PxU8* contactPatchData; + const PxU8* contactPointData; + PxU32 cDataSize; + PxU32 alignedContactDataSize; + const PxReal* impulses; + + PxU32 nbPoints = output->nbContacts; + PxU32 contactPatchCount = output->nbPatches; + + if(!isCCDPass) + { + PX_ASSERT(0==(reinterpret_cast(output->contactPatches) & 0x0f)); // check 16Byte alignment + contactPatchData = output->contactPatches; + contactPointData = output->contactPoints; + cDataSize = sizeof(PxContactPatch)*output->nbPatches + sizeof(PxContact)*output->nbContacts; + alignedContactDataSize = (cDataSize + 0xf) & 0xfffffff0; + impulses = output->contactForces; + } + else + { + PX_ASSERT(0==(reinterpret_cast(ccdContactData) & 0x0f)); // check 16Byte alignment + contactPatchData = reinterpret_cast(ccdContactData) + sizeof(PxsCCDContactHeader); + contactPointData = contactPatchData + sizeof(PxContactPatch); + cDataSize = ccdContactData->contactStreamSize - sizeof(PxsCCDContactHeader); + PxU32 tmpAlignedSize = (ccdContactData->contactStreamSize + 0xf) & 0xfffffff0; + alignedContactDataSize = tmpAlignedSize - sizeof(PxsCCDContactHeader); + impulses = reinterpret_cast(contactPatchData + alignedContactDataSize); + nbPoints = 1; + contactPatchCount = 1; + } + + infoFlags = cp->flags; + infoFlags |= unswapped ? 0 : PxContactPairFlag::eINTERNAL_CONTACTS_ARE_FLIPPED; + + //PX_ASSERT(0==(reinterpret_cast(impulses) & 0x0f)); + + const PxU32 impulseSize = impulses ? (nbPoints * sizeof(PxReal)) : 0; + if(impulseSize) + infoFlags |= PxContactPairFlag::eINTERNAL_HAS_IMPULSES; + cp->contactPatches = contactPatchData; + cp->contactPoints = contactPointData; + cp->contactCount = Ps::to8(nbPoints); + cp->patchCount = Ps::to8(contactPatchCount); + cp->constraintStreamSize = Ps::to16(cDataSize); + cp->requiredBufferSize = alignedContactDataSize + impulseSize; + cp->contactForces = impulses; + + cp->flags = infoFlags; + } + } +} + +void Sc::ShapeInteraction::processUserNotification(PxU32 contactEvent, PxU16 infoFlags, bool touchLost, const PxU32 ccdPass, const bool useCurrentTransform, PxsContactManagerOutputIterator& outputs) +{ + processUserNotificationSync(); + processUserNotificationAsync(contactEvent, infoFlags, touchLost, ccdPass, useCurrentTransform, outputs); +} + +PxU32 Sc::ShapeInteraction::getContactPointData(const void*& contactPatches, const void*& contactPoints, PxU32& contactDataSize, PxU32& contactPointCount, PxU32& numPatches, const PxReal*& impulses, PxU32 startOffset, + PxsContactManagerOutputIterator& outputs) +{ + // Process LL generated contacts + if(mManager != NULL) + { + PxcNpWorkUnit& workUnit = mManager->getWorkUnit(); + + PxsContactManagerOutput* output = NULL; + + if(workUnit.mNpIndex & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK) + { + output = &getScene().getLowLevelContext()->getNphaseImplementationContext()->getNewContactManagerOutput(workUnit.mNpIndex); + } + else + { + output = &outputs.getContactManager(workUnit.mNpIndex); + } + + /*const void* dcdContactPatches; + const void* dcdContactPoints; + PxU32 dcdContactPatchCount; + const PxReal* dcdImpulses; + const PxsCCDContactHeader* ccdContactStream; + PxU32 dcdContactCount = mManager->getContactPointData(dcdContactPatches, dcdContactPoints, dcdContactPatchCount, dcdImpulses, ccdContactStream); + + PX_ASSERT(((dcdContactCount == 0) && (!ccdContactStream)) || ((dcdContactCount > 0) && hasTouch()) || (ccdContactStream && hasCCDTouch()));*/ + + const PxsCCDContactHeader* ccdContactStream = reinterpret_cast(workUnit.ccdContacts); + + PxU32 idx = 0; + if(output->nbContacts) + { + if(startOffset == 0) + { + contactPatches = output->contactPatches; + contactPoints = output->contactPoints; + contactDataSize = sizeof(PxContactPatch) * output->nbPatches + sizeof(PxContact) * output->nbContacts; + contactPointCount = output->nbContacts; + numPatches = output->nbPatches; + impulses = output->contactForces; + + if(!ccdContactStream) + return startOffset; + else + return (startOffset + 1); + } + + idx++; + } + + while(ccdContactStream) + { + if(startOffset == idx) + { + const PxU8* stream = reinterpret_cast(ccdContactStream); + PxU16 streamSize = ccdContactStream->contactStreamSize; + contactPatches = stream + sizeof(PxsCCDContactHeader); + contactPoints = stream + sizeof(PxsCCDContactHeader) + sizeof(PxContactPatch); + contactDataSize = streamSize - sizeof(PxsCCDContactHeader); + contactPointCount = 1; + numPatches = 1; + impulses = reinterpret_cast(stream + ((streamSize + 0xf) & 0xfffffff0)); + + if(!ccdContactStream->nextStream) + return startOffset; + else + return (startOffset + 1); + } + + idx++; + ccdContactStream = ccdContactStream->nextStream; + } + } + + contactPatches = NULL; + contactPoints = NULL; + contactDataSize = 0; + contactPointCount = 0; + numPatches = 0; + impulses = NULL; + return startOffset; +} + +// Note that LL will not send end touch events for managers that are destroyed while having contact +void Sc::ShapeInteraction::managerNewTouch(const PxU32 ccdPass, bool adjustCounters, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + if(readFlag(HAS_TOUCH)) + return; // Do not count the touch twice (for instance when recreating a manager with touch) + // We have contact this frame + setHasTouch(); + + if(adjustCounters) + adjustCountersOnNewTouch(useAdaptiveForce); + + if(!isReportPair()) + return; + else + { + PX_ASSERT(hasTouch()); + PX_ASSERT(!readFlag(IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(!readFlag(IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + + const PxU32 pairFlags = getPairFlags(); + if(pairFlags & PxPairFlag::eNOTIFY_TOUCH_FOUND) + { + PxU16 infoFlag = 0; + if(mActorPair->getTouchCount() == 1) // this code assumes that the actor pair touch count does get incremented beforehand + { + infoFlag = PxContactPairFlag::eACTOR_PAIR_HAS_FIRST_TOUCH; + } + + processUserNotification(PxPairFlag::eNOTIFY_TOUCH_FOUND, infoFlag, false, ccdPass, true, outputs); + } + + if(pairFlags & PxPairFlag::eNOTIFY_TOUCH_PERSISTS) + { + getScene().getNPhaseCore()->addToPersistentContactEventPairsDelayed(this); // to make sure that from now on, the pairs are tested for persistent contact events + } + else if(pairFlags & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS) + { + // new touch -> need to start checking for force threshold events + // Note: this code assumes that it runs before the pairs get tested for force threshold exceeded + getScene().getNPhaseCore()->addToForceThresholdContactEventPairs(this); + } + } +} + + +bool Sc::ShapeInteraction::managerLostTouch(const PxU32 ccdPass, bool adjustCounters, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + if(!readFlag(HAS_TOUCH)) + return false; + + // We do not have LL contacts this frame and also we lost LL contact this frame + + if(!isReportPair()) + { + setHasNoTouch(); + } + else + { + PX_ASSERT(hasTouch()); + + sendLostTouchReport(false, ccdPass, outputs); + + if(readFlag(IS_IN_CONTACT_EVENT_LIST)) + { + // don't need to worry about persistent/force-threshold contact events until next new touch + + if(readFlag(IS_IN_FORCE_THRESHOLD_EVENT_LIST)) + { + getScene().getNPhaseCore()->removeFromForceThresholdContactEventPairs(this); + } + else + { + PX_ASSERT(readFlag(IS_IN_PERSISTENT_EVENT_LIST)); + getScene().getNPhaseCore()->removeFromPersistentContactEventPairs(this); + } + + clearFlag(FORCE_THRESHOLD_EXCEEDED_FLAGS); + } + + setHasNoTouch(); + } + + BodySim* body0 = getShape0().getBodySim(); + BodySim* body1 = getShape1().getBodySim(); + PX_ASSERT(body0); // the first shape always belongs to a dynamic body + + if(adjustCounters) + adjustCountersOnLostTouch(body0, body1, useAdaptiveForce); + + if(!body1) + { + body0->internalWakeUp(); + return false; + } + return true; +} + + +PX_FORCE_INLINE void Sc::ShapeInteraction::updateFlags(const Sc::Scene& scene, const Sc::BodySim* bs0, const Sc::BodySim* bs1, const PxU32 pairFlags) +{ + PX_ASSERT(bs0); // the first shape always belongs to a dynamic body + + // Check if collision response is disabled + bool enabled = (!bs0->isKinematic()) || (bs1 && !bs1->isKinematic()); // If the pair has no dynamic body then disable response + enabled = enabled && (pairFlags & PxPairFlag::eSOLVE_CONTACT); + setFlag(CONTACTS_RESPONSE_DISABLED, !enabled); + + // Check if contact points needed + setFlag(CONTACTS_COLLECT_POINTS, ( (pairFlags & PxPairFlag::eNOTIFY_CONTACT_POINTS) || + (pairFlags & PxPairFlag::eMODIFY_CONTACTS) || + scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT) || + scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL) || + scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_ERROR) || + scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_FORCE)) ); +} + +void Sc::ShapeInteraction::updateState(const PxU8 externalDirtyFlags) +{ + const PxU32 oldContactState = getManagerContactState(); + + const PxU8 dirtyFlags = PxU8(getDirtyFlags() | externalDirtyFlags); + const PxU32 pairFlags = getPairFlags(); + Scene& scene = getScene(); + + if(dirtyFlags & (InteractionDirtyFlag::eFILTER_STATE | InteractionDirtyFlag::eVISUALIZATION)) + { + Sc::BodySim* bs0 = getShape0().getBodySim(); + Sc::BodySim* bs1 = getShape1().getBodySim(); + + Ps::IntBool wasDisabled = readFlag(CONTACTS_RESPONSE_DISABLED); + + updateFlags(scene, bs0, bs1, pairFlags); + + Ps::IntBool isDisabled = readFlag(CONTACTS_RESPONSE_DISABLED); + + if(!wasDisabled && isDisabled) + { + scene.getSimpleIslandManager()->setEdgeDisconnected(mEdgeIndex); + } + else if(wasDisabled && !isDisabled) + { + if(readFlag(ShapeInteraction::HAS_TOUCH)) + { + scene.getSimpleIslandManager()->setEdgeConnected(mEdgeIndex); + } + } + } + + const PxU32 newContactState = getManagerContactState(); + const bool recreateManager = (oldContactState != newContactState); + + // No use in updating manager properties if the manager is going to be re-created or does not exist yet + if((!recreateManager) && (mManager != 0)) + { + ShapeSim& shapeSim0 = getShape0(); + ShapeSim& shapeSim1 = getShape1(); + + // Update dominance + if(dirtyFlags & InteractionDirtyFlag::eDOMINANCE) + { + Sc::BodySim* bs0 = shapeSim0.getBodySim(); + Sc::BodySim* bs1 = shapeSim1.getBodySim(); + PX_ASSERT(bs0); // the first shape always belongs to a dynamic body + + // Static actors are in dominance group zero and must remain there + const PxDominanceGroup dom0 = bs0->getActorCore().getDominanceGroup(); + const PxDominanceGroup dom1 = bs1 ? bs1->getActorCore().getDominanceGroup() : PxDominanceGroup(0); + + const PxDominanceGroupPair cdom = getScene().getDominanceGroupPair(dom0, dom1); + mManager->setDominance0(cdom.dominance0); + mManager->setDominance1(cdom.dominance1); + } + + if (dirtyFlags & InteractionDirtyFlag::eBODY_KINEMATIC) + { + //Kinematic flags changed - clear flag for kinematic on the pair + Sc::BodySim* bs1 = shapeSim1.getBodySim(); + if (bs1 != NULL) + { + if (bs1->isKinematic()) + { + mManager->getWorkUnit().flags |= PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR; + } + else + { + mManager->getWorkUnit().flags &= (~PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR); + } + } + } + + // Update skin width + if(dirtyFlags & InteractionDirtyFlag::eREST_OFFSET) + { + mManager->setRestDistance(shapeSim0.getRestOffset() + shapeSim1.getRestOffset()); + } + + //we may want to only write these if they have changed, the set code is a bit painful for the integration flags because of bit unpacking + packing. + mManager->setCCD((getPairFlags() & PxPairFlag::eDETECT_CCD_CONTACT) != 0); + } + else if (readInteractionFlag(InteractionFlag::eIS_ACTIVE)) // only re-create the manager if the pair is active + { + PX_ASSERT(mManager); // if the pair is active, there has to be a manager + + if (dirtyFlags & InteractionDirtyFlag::eBODY_KINEMATIC) + { + //Kinematic->dynamic transition + const IG::IslandSim& islandSim = getScene().getSimpleIslandManager()->getSpeculativeIslandSim(); + + // + //check whether active in the speculative sim! + const BodySim* bodySim0 = getShape0().getBodySim(); + const BodySim* bodySim1 = getShape1().getBodySim(); + + if (!islandSim.getNode(bodySim0->getNodeIndex()).isActiveOrActivating() && + (bodySim1 == NULL || !islandSim.getNode(bodySim1->getNodeIndex()).isActiveOrActivating())) + { + onDeactivate_(); + scene.notifyInteractionDeactivated(this); + } + else + { + //Else we are allowed to be active, so recreate + if (mEdgeIndex != IG_INVALID_EDGE) + scene.getSimpleIslandManager()->clearEdgeRigidCM(mEdgeIndex); + destroyManager(); + createManager(NULL); + } + } + else + { + + PX_ASSERT(activeManagerAllowed()); + + // A) This is a newly created pair + // + // B) The contact notification or processing state has changed. + // All existing managers need to be deleted and recreated with the correct flag set + // These flags can only be set at creation in LL + //KS - added this code here because it is no longer done in destroyManager() - a side-effect of the parallelization of the interaction management code + if (mEdgeIndex != IG_INVALID_EDGE) + scene.getSimpleIslandManager()->clearEdgeRigidCM(mEdgeIndex); + destroyManager(); + createManager(NULL); + } + } +} + +bool Sc::ShapeInteraction::onActivate_(void* contactManager) +{ + if(isReportPair())// && !(infoFlag & ActorSim::AS_PART_OF_ISLAND_GEN_PASS_1)) + { + // for pairs that go through a second island pass, there is the possibility that they get put to sleep again after the second pass. + // So we do not want to check for re-insertion into the persistent report pair list yet. + processReportPairOnActivate(); + } + + if(updateManager(contactManager)) + { + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; + } + else + return false; +} + + +bool Sc::ShapeInteraction::onDeactivate_() +{ + PX_ASSERT(getShape0().getActor().isDynamicRigid() || getShape1().getActor().isDynamicRigid()); + + const BodySim* bodySim0 = getShape0().getBodySim(); + const BodySim* bodySim1 = getShape1().getBodySim(); + PX_ASSERT(bodySim0); // the first shape always belongs to a dynamic body + + PX_ASSERT( (!bodySim0 && bodySim1 && !bodySim1->isActive()) || + (!bodySim1 && bodySim0 && !bodySim0->isActive()) || + ((bodySim0 && bodySim1 && (!bodySim0->isActive() || !bodySim1->isActive()))) ); + + if((!bodySim0->isActive()) && (!bodySim1 || !bodySim1->isActive())) + { + if(mReportPairIndex != INVALID_REPORT_PAIR_ID) + { + processReportPairOnDeactivate(); + } + + PX_ASSERT((mManager->getTouchStatus() > 0) == (hasTouch() > 0)); + + if(mManager != 0) + { + if((!readFlag(TOUCH_KNOWN)) && mManager->touchStatusKnown() && (!mManager->getTouchStatus())) + { + // for pairs that are inserted asleep, we do not know the touch state. If they run through narrowphase and a touch is found, + // then a managerNewTouch() call will inform this object about the found touch. However, if narrowphase detects that there + // is no touch, this object will not be informed about it. The low level manager will always know though. Now, before destroying + // the pair manager, we need to record "does not have touch" state if available. + raiseFlag(HAS_NO_TOUCH); + } + + destroyManager(); + if(mEdgeIndex != IG_INVALID_EDGE) + getScene().getSimpleIslandManager()->clearEdgeRigidCM(mEdgeIndex); + } + Scene& scene = getScene(); + scene.getSimpleIslandManager()->deactivateEdge(mEdgeIndex); + + // + // We distinguish two scenarios here: + // + // A) island generation deactivates objects: + // -> the deactivated body was active + // -> narrowphase ran on this pair + // -> the touch status is known + // -> touch: the objects of the pair are in the same island + // -> no touch: the objects of the pair are in different islands + // + // As a consequence, the edge state is not changed. The assumption is that anything that could break the touch status + // from here on will have to mark the edges connected (for example if the object gets moved). + // + // B) user deactivates objects: + // -> the touch status might not be known (for example, the pose gets integrated after the solver which might cause a change + // in touch status. If the object gets put to sleep after that, we have to be conservative and mark the edge connected. + // other example: an active object gets moved by the user and then deactivated). + // + + clearInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; + } + else + { + return false; + } +} + +void Sc::ShapeInteraction::createManager(void* contactManager) +{ + //PX_PROFILE_ZONE("ShapeInteraction.createManager", 0); + + Sc::Scene& scene = getScene(); + + const PxU32 pairFlags = getPairFlags(); + + const int disableCCDContact = !(pairFlags & PxPairFlag::eDETECT_CCD_CONTACT); + + PxsContactManager* manager = scene.getLowLevelContext()->createContactManager(reinterpret_cast(contactManager), !disableCCDContact); + PxcNpWorkUnit& mNpUnit = manager->getWorkUnit(); + + // Check if contact generation callback has been ordered on the pair + int contactChangeable = 0; + if(pairFlags & PxPairFlag::eMODIFY_CONTACTS) + contactChangeable = 1; + + ShapeSim& shapeSim0 = getShape0(); + ShapeSim& shapeSim1 = getShape1(); + + const PxActorType::Enum type0 = shapeSim0.getActor().getActorType(); + const PxActorType::Enum type1 = shapeSim1.getActor().getActorType(); + + const int disableResponse = readFlag(CONTACTS_RESPONSE_DISABLED) ? 1 : 0; + const int disableDiscreteContact = !(pairFlags & PxPairFlag::eDETECT_DISCRETE_CONTACT); + const int reportContactInfo = readFlag(CONTACTS_COLLECT_POINTS); + const int hasForceThreshold = !disableResponse && (pairFlags & CONTACT_FORCE_THRESHOLD_PAIRS); + int touching; + if(readFlag(TOUCH_KNOWN)) + touching = readFlag(HAS_TOUCH) ? 1 : -1; + else + touching = 0; + + // Static actors are in dominance group zero and must remain there + + Sc::BodySim* bs0 = shapeSim0.getBodySim(); + Sc::BodySim* bs1 = shapeSim1.getBodySim(); + + PX_ASSERT(bs0); // The shapes have to be sorted such that the first shape belongs to a dynamic + + const PxDominanceGroup dom0 = bs0->getActorCore().getDominanceGroup(); + const PxDominanceGroup dom1 = bs1 ? bs1->getActorCore().getDominanceGroup() : PxDominanceGroup(0); + + const bool kinematicActor = bs1 ? bs1->isKinematic() : false; + + const PxDominanceGroupPair cdom = scene.getDominanceGroupPair(dom0, dom1); + + const PxI32 hasArticulations= (type0 == PxActorType::eARTICULATION_LINK) | (type1 == PxActorType::eARTICULATION_LINK)<<1; + const PxI32 hasDynamics = (type0 != PxActorType::eRIGID_STATIC) | (type1 != PxActorType::eRIGID_STATIC)<<1; + + const PxsShapeCore* shapeCore0 = &shapeSim0.getCore().getCore(); + const PxsShapeCore* shapeCore1 = &shapeSim1.getCore().getCore(); + + //Initialize the manager.... + + manager->mRigidBody0 = &bs0->getLowLevelBody(); + manager->mRigidBody1 = bs1 ? &bs1->getLowLevelBody() : NULL; + manager->mShapeInteraction = this; + mNpUnit.shapeCore0 = shapeCore0; + mNpUnit.shapeCore1 = shapeCore1; + + PX_ASSERT(shapeCore0->transform.isValid() && shapeCore1->transform.isValid()); + + mNpUnit.rigidCore0 = &shapeSim0.getPxsRigidCore(); + mNpUnit.rigidCore1 = &shapeSim1.getPxsRigidCore(); + + mNpUnit.restDistance = shapeSim0.getRestOffset() + shapeSim1.getRestOffset(); + mNpUnit.dominance0 = cdom.dominance0; + mNpUnit.dominance1 = cdom.dominance1; + mNpUnit.geomType0 = PxU8(shapeCore0->geometry.getType()); + mNpUnit.geomType1 = PxU8(shapeCore1->geometry.getType()); + mNpUnit.mTransformCache0 = shapeSim0.getTransformCacheID(); + mNpUnit.mTransformCache1 = shapeSim1.getTransformCacheID(); + + mNpUnit.mTorsionalPatchRadius = PxMax(shapeSim0.getTorsionalPatchRadius(),shapeSim1.getTorsionalPatchRadius()); + mNpUnit.mMinTorsionalPatchRadius = PxMax(shapeSim0.getMinTorsionalPatchRadius(), shapeSim1.getMinTorsionalPatchRadius()); + + PxU16 wuflags = 0; + + if(hasArticulations & 1) + wuflags |= PxcNpWorkUnitFlag::eARTICULATION_BODY0; + + if(hasArticulations & 2) + wuflags |= PxcNpWorkUnitFlag::eARTICULATION_BODY1; + + if(hasDynamics & 1) + wuflags |= PxcNpWorkUnitFlag::eDYNAMIC_BODY0; + + if(hasDynamics & 2) + wuflags |= PxcNpWorkUnitFlag::eDYNAMIC_BODY1; + + if(!disableResponse && !contactChangeable) + wuflags |= PxcNpWorkUnitFlag::eOUTPUT_CONSTRAINTS; + + if(!disableDiscreteContact) + wuflags |= PxcNpWorkUnitFlag::eDETECT_DISCRETE_CONTACT; + if(kinematicActor) + wuflags |= PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR; + + if(disableResponse) + wuflags |= PxcNpWorkUnitFlag::eDISABLE_RESPONSE; + if(!disableCCDContact) + wuflags |= PxcNpWorkUnitFlag::eDETECT_CCD_CONTACTS; + + // this is just the user req: contact reports can also be generated by body thresholding + if(reportContactInfo || contactChangeable) + wuflags |= PxcNpWorkUnitFlag::eOUTPUT_CONTACTS; + + if(hasForceThreshold) + wuflags |= PxcNpWorkUnitFlag::eFORCE_THRESHOLD; + + if(contactChangeable) + wuflags |= PxcNpWorkUnitFlag::eMODIFIABLE_CONTACT; + + mNpUnit.flags = wuflags; + + manager->mFlags = PxU32(contactChangeable ? PxsContactManager::PXS_CM_CHANGEABLE : 0) | PxU32(disableCCDContact ? 0 : PxsContactManager::PXS_CM_CCD_LINEAR); + + //manager->mUserData = this; + + mNpUnit.mNpIndex = 0xFFffFFff; + + mManager = manager; + + PxU8 statusFlags = 0; + + if(touching > 0) + { + statusFlags |= PxcNpWorkUnitStatusFlag::eHAS_TOUCH; + } + else if (touching < 0) + { + statusFlags |= PxcNpWorkUnitStatusFlag::eHAS_NO_TOUCH; + } + + mNpUnit.statusFlags = statusFlags; + + //KS - do not register the CMs here if contactManager isn't null. This implies this is a newly-found pair so we'll do that addition outside in parallel + + if(contactManager == NULL) + { + scene.getSimpleIslandManager()->setEdgeRigidCM(mEdgeIndex, mManager); + PxvNphaseImplementationContext* nphaseImplementationContext = scene.getLowLevelContext()->getNphaseImplementationContext(); + PX_ASSERT(nphaseImplementationContext); + nphaseImplementationContext->registerContactManager(mManager, touching, 0); + } +} + +void Sc::ShapeInteraction::onShapeChangeWhileSleeping(bool shapeOfDynamicChanged) +{ + // + // if an operation that can break touch status occurs, all deactivated pairs need to set the sleep island edge + // to connected to make sure that potentially joined islands get detected once parts of the island wake up. + // Active interactions can be ignored because the edges of those will be marked connected on deactivation. + // + if(!mManager) + { + Scene& scene = getScene(); + + BodySim* body0 = getShape0().getBodySim(); + PX_ASSERT(body0); // the first shape always belongs to a dynamic body + + if(shapeOfDynamicChanged && !readFlag(TOUCH_KNOWN)) + { + // conservative approach: if a pair was added asleep, and a body/shape gets moved, we want to check next frame + // whether the other body should get woken up. The motivation behind this is to get a similar behavior as in + // the case where the objects fell asleep rather than have been added asleep (in that case the object will be + // woken up with one frame delay). + + BodySim* body1 = getShape1().getBodySim(); + + if(body1 && !readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) // the first shape always belongs to a dynamic body, hence no need to test body0 + scene.addToLostTouchList(body0, body1); // note: this will cause duplicate entries if the pair loses AABB overlap the next frame + } + else if((!shapeOfDynamicChanged) && !body0->isActive()) + { + // if the shape of a static rigid body moves or is scaled, then this can break touch status and a second island gen + // pass will be needed. However, statics do not have island nodes that can be marked accordinlgy and instead we mark + // the dynamics of all pairs if they are asleep (if they are awake, they will get marked when the user puts them to sleep). + + /*PX_ASSERT(!getShape1().getBodySim()); + body0->notifySecondIslandGenPassNeeded();*/ + } + } +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h new file mode 100644 index 000000000..9e18b98a9 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h @@ -0,0 +1,412 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_SHAPE_INTERACTION +#define PX_COLLISION_SHAPE_INTERACTION + +#include "ScElementSimInteraction.h" +#include "ScShapeSim.h" +#include "ScActorPair.h" +#include "ScScene.h" +#include "ScBodySim.h" +#include "PxsContactManager.h" +#include "PxsContext.h" +#include "PxsSimpleIslandManager.h" + +#define INVALID_REPORT_PAIR_ID 0xffffffff + +namespace physx +{ +class PxsContactManagerOutputIterator; +namespace Sc +{ + class ContactReportAllocationManager; + /* + Description: A ShapeInteraction represents a pair of objects which _may_ have contacts. Created by the broadphase + and processed by the NPhaseCore. + */ + class ShapeInteraction : public ElementSimInteraction + { + friend class NPhaseCore; + ShapeInteraction& operator=(const ShapeInteraction&); + public: + enum SiFlag + { + PAIR_FLAGS_MASK = (PxPairFlag::eNEXT_FREE - 1), // Bits where the PxPairFlags get stored + NEXT_FREE = ((PAIR_FLAGS_MASK << 1) & ~PAIR_FLAGS_MASK), + + HAS_TOUCH = (NEXT_FREE << 0), // Tracks the last know touch state + HAS_NO_TOUCH = (NEXT_FREE << 1), // Tracks the last know touch state + TOUCH_KNOWN = (HAS_TOUCH | HAS_NO_TOUCH), // If none of these flags is set, the touch state is not known (for example, this is true for pairs that never ran narrowphase + + CONTACTS_COLLECT_POINTS = (NEXT_FREE << 2), // The user wants to get the contact points (includes debug rendering) + CONTACTS_RESPONSE_DISABLED = (NEXT_FREE << 3), // Collision response disabled (either by the user through PxPairFlag::eSOLVE_CONTACT or because the pair has two kinematics) + + CONTACT_FORCE_THRESHOLD_PAIRS = PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND) | PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS) | PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST), + CONTACT_REPORT_EVENTS = PxU32(PxPairFlag::eNOTIFY_TOUCH_FOUND) | PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS) | PxU32(PxPairFlag::eNOTIFY_TOUCH_LOST) | + PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND) | PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS) | PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST), + CONTACT_REPORT_EXTRA_DATA = PxU32(PxPairFlag::ePRE_SOLVER_VELOCITY) | PxU32(PxPairFlag::ePOST_SOLVER_VELOCITY) | PxU32(PxPairFlag::eCONTACT_EVENT_POSE), + + FORCE_THRESHOLD_EXCEEDED_NOW = (NEXT_FREE << 4), + FORCE_THRESHOLD_EXCEEDED_BEFORE = (NEXT_FREE << 5), + FORCE_THRESHOLD_EXCEEDED_FLAGS = FORCE_THRESHOLD_EXCEEDED_NOW | FORCE_THRESHOLD_EXCEEDED_BEFORE, + + IS_IN_PERSISTENT_EVENT_LIST = (NEXT_FREE << 6), // The pair is in the list of persistent contact events + WAS_IN_PERSISTENT_EVENT_LIST = (NEXT_FREE << 7), // The pair is inactive but used to be in the list of persistent contact events + IN_PERSISTENT_EVENT_LIST = IS_IN_PERSISTENT_EVENT_LIST | WAS_IN_PERSISTENT_EVENT_LIST, + IS_IN_FORCE_THRESHOLD_EVENT_LIST= (NEXT_FREE << 8), // The pair is in the list of force threshold contact events + IS_IN_CONTACT_EVENT_LIST = IS_IN_PERSISTENT_EVENT_LIST | IS_IN_FORCE_THRESHOLD_EVENT_LIST, + + LL_MANAGER_RECREATE_EVENT = CONTACT_REPORT_EVENTS | CONTACTS_COLLECT_POINTS | + CONTACTS_RESPONSE_DISABLED | PxU32(PxPairFlag::eMODIFY_CONTACTS) + }; + ShapeInteraction(ShapeSim& s1, ShapeSim& s2, PxPairFlags pairFlags, PxsContactManager* contactManager); + ~ShapeInteraction(); + + // Submits to contact stream + void processUserNotification(PxU32 contactEvent, PxU16 infoFlags, bool touchLost, const PxU32 ccdPass, const bool useCurrentTransform, + PxsContactManagerOutputIterator& outputs); // ccdPass is 0 for discrete collision and then 1,2,... for the CCD passes + + void processUserNotificationSync(); + + void processUserNotificationAsync(PxU32 contactEvent, PxU16 infoFlags, bool touchLost, const PxU32 ccdPass, const bool useCurrentTransform, + PxsContactManagerOutputIterator& outputs, ContactReportAllocationManager* alloc = NULL); // ccdPass is 0 for discrete collision and then 1,2,... for the CCD passes + + void visualize(Cm::RenderOutput&, PxsContactManagerOutputIterator&); + + PxU32 getContactPointData(const void*& contactPatches, const void*& contactPoints, PxU32& contactDataSize, PxU32& contactPointCount, PxU32& patchCount, const PxReal*& impulses, PxU32 startOffset, PxsContactManagerOutputIterator& outputs); + + bool managerLostTouch(const PxU32 ccdPass, bool adjustCounters, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + void managerNewTouch(const PxU32 ccdPass, bool adjustCounters, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + + PX_FORCE_INLINE void adjustCountersOnLostTouch(BodySim*, BodySim*, bool useAdaptiveForce); + PX_FORCE_INLINE void adjustCountersOnNewTouch(bool useAdaptiveForce); + + PX_FORCE_INLINE void sendCCDRetouch(const PxU32 ccdPass, PxsContactManagerOutputIterator& outputs); + void setContactReportPostSolverVelocity(ContactStreamManager& cs); + PX_FORCE_INLINE void sendLostTouchReport(bool shapeVolumeRemoved, const PxU32 ccdPass, PxsContactManagerOutputIterator& ouptuts); + void resetManagerCachedState() const; + + PX_FORCE_INLINE ActorPair* getActorPair() const { return mActorPair; } + PX_FORCE_INLINE void setActorPair(ActorPair& aPair) { mActorPair = &aPair; } + PX_FORCE_INLINE void clearActorPair() { mActorPair = NULL; } + PX_FORCE_INLINE ActorPairReport& getActorPairReport() const { return ActorPairReport::cast(*mActorPair); } + PX_INLINE Ps::IntBool isReportPair() const { /*PX_ASSERT(!(Ps::IntBool(getPairFlags() & CONTACT_REPORT_EVENTS)) || mActorPair->isReportPair());*/ return Ps::IntBool(getPairFlags() & CONTACT_REPORT_EVENTS); } + PX_INLINE Ps::IntBool hasTouch() const { return readFlag(HAS_TOUCH); } + PX_INLINE Ps::IntBool hasCCDTouch() const { PX_ASSERT(mManager); return mManager->getHadCCDContact(); } + PX_INLINE void swapAndClearForceThresholdExceeded(); + + PX_FORCE_INLINE void raiseFlag(SiFlag flag) { mFlags |= flag; } + PX_FORCE_INLINE Ps::IntBool readFlag(SiFlag flag) const { return Ps::IntBool(mFlags & flag); } + PX_FORCE_INLINE PxU32 getPairFlags() const; + + PX_FORCE_INLINE void removeFromReportPairList(); + + void onShapeChangeWhileSleeping(bool shapeOfDynamicChanged); + + PX_FORCE_INLINE Ps::IntBool hasKnownTouchState() const; + + bool onActivate_(void* data); + bool onDeactivate_(); + + void updateState(const PxU8 externalDirtyFlags); + + const PxsContactManager* getContactManager() const { return mManager; } + + void clearIslandGenData(); + + PX_FORCE_INLINE PxU32 getEdgeIndex() const { return mEdgeIndex; } + + PX_FORCE_INLINE Sc::ShapeSim& getShape0() const { return static_cast(getElement0()); } + PX_FORCE_INLINE Sc::ShapeSim& getShape1() const { return static_cast(getElement1()); } + + private: + PxU32 mContactReportStamp; + PxU32 mFlags; + ActorPair* mActorPair; + PxU32 mReportPairIndex; // Owned by NPhaseCore for its report pair list + + PxsContactManager* mManager; + + PxU32 mEdgeIndex; + + PxU16 mReportStreamIndex; // position of this pair in the contact report stream + + // Internal functions: + + void createManager(void* contactManager); + PX_INLINE void resetManager(); + PX_INLINE bool updateManager(void* contactManager); + PX_INLINE void destroyManager(); + PX_FORCE_INLINE bool activeManagerAllowed() const; + PX_FORCE_INLINE PxU32 getManagerContactState() const { return mFlags & LL_MANAGER_RECREATE_EVENT; } + + PX_FORCE_INLINE void clearFlag(SiFlag flag) { mFlags &= ~flag; } + PX_INLINE void setFlag(SiFlag flag, bool value) + { + if (value) + raiseFlag(flag); + else + clearFlag(flag); + } + PX_FORCE_INLINE void setHasTouch() { clearFlag(HAS_NO_TOUCH); raiseFlag(HAS_TOUCH); } + PX_FORCE_INLINE void setHasNoTouch() { clearFlag(HAS_TOUCH); raiseFlag(HAS_NO_TOUCH); } + + PX_FORCE_INLINE void setPairFlags(PxPairFlags flags); + + PX_FORCE_INLINE void processReportPairOnActivate(); + PX_FORCE_INLINE void processReportPairOnDeactivate(); + + // Certain SiFlag cache properties of the pair. If these properties change then the flags have to be updated. + // For example: is collision enabled for this pair? are contact points requested for this pair? + PX_FORCE_INLINE void updateFlags(const Sc::Scene&, const Sc::BodySim*, const Sc::BodySim*, const PxU32 pairFlags); + + friend class Sc::Scene; + }; + +} // namespace Sc + + +PX_FORCE_INLINE void Sc::ShapeInteraction::sendLostTouchReport(bool shapeVolumeRemoved, const PxU32 ccdPass, PxsContactManagerOutputIterator& outputs) +{ + PX_ASSERT(hasTouch()); + PX_ASSERT(isReportPair()); + + PxU32 thresholdForceLost = readFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_NOW) ? PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST) : 0; // make sure to only send report if force is still above threshold + PxU32 triggeredFlags = getPairFlags() & (PxU32(PxPairFlag::eNOTIFY_TOUCH_LOST) | thresholdForceLost); + if (triggeredFlags) + { + PxU16 infoFlag = 0; + if (mActorPair->getTouchCount() == 1) // this code assumes that the actor pair touch count does get decremented afterwards + { + infoFlag |= PxContactPairFlag::eACTOR_PAIR_LOST_TOUCH; + } + + //Lost touch is processed after solver, so we should use the previous transform to update the pose for objects if user request eCONTACT_EVENT_POSE + processUserNotification(triggeredFlags, infoFlag, true, ccdPass, false, outputs); + } + + ActorPairReport& apr = getActorPairReport(); + if (apr.hasReportData() && !apr.streamResetNeeded(getScene().getTimeStamp())) + { + // If there has been no event recorded yet, there is no need to worry about events with shape pointers which might later reference + // removed shapes due to buffered removal, i.e., removal while the simulation was running. + // This is also correct for CCD scenarios where a touch could get lost and then found again etc. If in such a case there ever is a contact event + // recorded, there will always be another sendLostTouchReport() call at some later point (caused by the simulation or when the shape gets + // removed at fetchResults). + PxU16 flagsToRaise = ContactStreamManagerFlag::eHAS_PAIRS_THAT_LOST_TOUCH; + + ContactStreamManager& cs = apr.getContactStreamManager(); + + if (shapeVolumeRemoved) + { + flagsToRaise |= ContactStreamManagerFlag::eTEST_FOR_REMOVED_SHAPES; + + // if an actor gets deleted while the simulation is running and the actor has a pending contact report with post solver + // velocity extra data, then the post solver velocity needs to get written now because it is too late when the reports + // get fired (the object will have been deleted already) + + if (cs.getFlags() & ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY) + setContactReportPostSolverVelocity(cs); + } + + cs.raiseFlags(flagsToRaise); + } +} + + +PX_FORCE_INLINE void Sc::ShapeInteraction::setPairFlags(PxPairFlags flags) +{ + PX_ASSERT(PxU32(flags) < PxPairFlag::eNEXT_FREE); // to find out if a new PxPairFlag has been added after eLAST instead of in front + + PxU32 newFlags = mFlags; + PxU32 fl = PxU32(flags) & PAIR_FLAGS_MASK; + newFlags &= (~PAIR_FLAGS_MASK); // clear old flags + newFlags |= fl; + + mFlags = newFlags; +} + + +// PT: returning PxU32 instead of PxPairFlags to remove LHS. Please do not undo this. +PX_FORCE_INLINE PxU32 Sc::ShapeInteraction::getPairFlags() const +{ + return (mFlags & PAIR_FLAGS_MASK); +} + + +PX_INLINE void Sc::ShapeInteraction::swapAndClearForceThresholdExceeded() +{ + PxU32 flags = mFlags; + + PX_COMPILE_TIME_ASSERT(FORCE_THRESHOLD_EXCEEDED_NOW == (FORCE_THRESHOLD_EXCEEDED_BEFORE >> 1)); + + PxU32 nowToBefore = (flags & FORCE_THRESHOLD_EXCEEDED_NOW) << 1; + flags &= ~(FORCE_THRESHOLD_EXCEEDED_NOW | FORCE_THRESHOLD_EXCEEDED_BEFORE); + flags |= nowToBefore; + + mFlags = flags; +} + +PX_FORCE_INLINE void Sc::ShapeInteraction::removeFromReportPairList() +{ + // this method should only get called if the pair is in the list for + // persistent or force based contact reports + PX_ASSERT(mReportPairIndex != INVALID_REPORT_PAIR_ID); + PX_ASSERT(readFlag(IS_IN_CONTACT_EVENT_LIST)); + + Scene& scene = getScene(); + + if (readFlag(IS_IN_FORCE_THRESHOLD_EVENT_LIST)) + scene.getNPhaseCore()->removeFromForceThresholdContactEventPairs(this); + else + { + PX_ASSERT(readFlag(IS_IN_PERSISTENT_EVENT_LIST)); + scene.getNPhaseCore()->removeFromPersistentContactEventPairs(this); + } +} + +PX_INLINE bool Sc::ShapeInteraction::updateManager(void* contactManager) +{ + if (activeManagerAllowed()) + { + if (mManager == 0) + createManager(contactManager); + + return (mManager != NULL); // creation might fail (pool reached limit, mem allocation failed etc.) + } + else + return false; +} + +PX_INLINE void Sc::ShapeInteraction::destroyManager() +{ + PX_ASSERT(mManager); + + Scene& scene = getScene(); + + PxvNphaseImplementationContext* nphaseImplementationContext = scene.getLowLevelContext()->getNphaseImplementationContext(); + PX_ASSERT(nphaseImplementationContext); + nphaseImplementationContext->unregisterContactManager(mManager); + + /*if (mEdgeIndex != IG_INVALID_EDGE) + scene.getSimpleIslandManager()->clearEdgeRigidCM(mEdgeIndex);*/ + scene.getLowLevelContext()->destroyContactManager(mManager); + mManager = 0; +} + + +PX_FORCE_INLINE bool Sc::ShapeInteraction::activeManagerAllowed() const +{ + PX_ASSERT(getShape0().getActor().isDynamicRigid() || getShape1().getActor().isDynamicRigid()); + + const BodySim* bodySim0 = getShape0().getBodySim(); + const BodySim* bodySim1 = getShape1().getBodySim(); + PX_ASSERT(bodySim0); // the first shape always belongs to a dynamic body + + const IG::IslandSim& islandSim = getScene().getSimpleIslandManager()->getSpeculativeIslandSim(); + + //if((bodySim0->isActive()) || (bodySim1 && bodySim1->isActive())) + //check whether active in the speculative sim! + if (islandSim.getNode(bodySim0->getNodeIndex()).isActive() || + (bodySim1 && islandSim.getNode(bodySim1->getNodeIndex()).isActive())) + { + return true; + } + else + { + //Sleeping kinematic 0 vs sleeping kinematic 1 + return false; + } +} + + +PX_FORCE_INLINE void Sc::ShapeInteraction::sendCCDRetouch(const PxU32 ccdPass, PxsContactManagerOutputIterator& outputs) +{ + PxU32 pairFlags = getPairFlags(); + if (pairFlags & PxPairFlag::eNOTIFY_TOUCH_CCD) + { + processUserNotification(PxPairFlag::eNOTIFY_TOUCH_CCD, 0, false, ccdPass, false, outputs); + } +} + + +PX_FORCE_INLINE void Sc::ShapeInteraction::adjustCountersOnLostTouch(BodySim* body0, BodySim* body1, bool useAdaptiveForce) +{ + PX_ASSERT(body0); // the first shape always belongs to a dynamic body + + PX_ASSERT(mActorPair->getTouchCount()); + + mActorPair->decTouchCount(); + + if (useAdaptiveForce || mActorPair->getTouchCount() == 0) + { + body0->decrementBodyConstraintCounter(); + if (body1) + body1->decrementBodyConstraintCounter(); + } +} + + +PX_FORCE_INLINE void Sc::ShapeInteraction::adjustCountersOnNewTouch(bool useAdaptiveForce) +{ + BodySim* body0 = getShape0().getBodySim(); + BodySim* body1 = getShape1().getBodySim(); + PX_ASSERT(body0); // the first shape always belongs to a dynamic body + + mActorPair->incTouchCount(); + //If using adaptive force, always record a body constraint, otherwise only record if this is the first constraint + //with this pair of bodies (doubling up usage of this counter for both adaptive force and stabilization) + if (useAdaptiveForce || mActorPair->getTouchCount() == 1) + { + body0->incrementBodyConstraintCounter(); + if (body1) + body1->incrementBodyConstraintCounter(); + } +} + + +PX_FORCE_INLINE Ps::IntBool Sc::ShapeInteraction::hasKnownTouchState() const +{ + // For a pair where the bodies were added asleep, the touch state is not known until narrowphase runs on the pair for the first time. + // If such a pair looses AABB overlap before, the conservative approach is to wake the bodies up. This method provides an indicator that + // this is such a pair. Note: this might also wake up objects that do not touch but that's the price to pay (unless we want to run + // overlap tests on such pairs). + + if (mManager) + return mManager->touchStatusKnown(); + else + return readFlag(TOUCH_KNOWN); +} + + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp new file mode 100644 index 000000000..7752c395d --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp @@ -0,0 +1,494 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScBodySim.h" +#include "ScStaticSim.h" +#include "ScScene.h" +#include "ScElementSimInteraction.h" +#include "ScShapeInteraction.h" +#include "ScTriggerInteraction.h" +#include "ScSimStats.h" +#include "ScObjectIDTracker.h" +#include "GuHeightFieldUtil.h" +#include "GuTriangleMesh.h" +#include "GuConvexMeshData.h" +#include "GuHeightField.h" +#include "PxsContext.h" +#include "PxsTransformCache.h" +#include "CmTransformUtils.h" +#include "GuBounds.h" +#include "PxsRigidBody.h" +#include "ScSqBoundsManager.h" +#include "PxsSimulationController.h" +#include "common/PxProfileZone.h" + +using namespace physx; +using namespace Gu; +using namespace Sc; + +// PT: keep local functions in cpp, no need to pollute the header. Don't force conversions to bool if not necessary. +static PX_FORCE_INLINE PxU32 hasTriggerFlags(PxShapeFlags flags) { return PxU32(flags) & PxU32(PxShapeFlag::eTRIGGER_SHAPE); } +static PX_FORCE_INLINE PxU32 isBroadPhase(PxShapeFlags flags) { return PxU32(flags) & PxU32(PxShapeFlag::eTRIGGER_SHAPE|PxShapeFlag::eSIMULATION_SHAPE); } + +static PX_FORCE_INLINE void resetElementID(Scene& scene, ShapeSim& shapeSim) +{ + PX_ASSERT(!shapeSim.isInBroadPhase()); + + scene.getDirtyShapeSimMap().reset(shapeSim.getElementID()); + + if(shapeSim.getSqBoundsId() != PX_INVALID_U32) + shapeSim.destroySqBounds(); +} + +void ShapeSim::initSubsystemsDependingOnElementID() +{ + Scene& scScene = getScene(); + + Bp::BoundsArray& boundsArray = scScene.getBoundsArray(); + const PxU32 index = getElementID(); + + PX_ALIGN(16, PxTransform absPos); + getAbsPoseAligned(&absPos); + + PxsTransformCache& cache = scScene.getLowLevelContext()->getTransformCache(); + cache.initEntry(index); + cache.setTransformCache(absPos, 0, index); + + boundsArray.updateBounds(absPos, mCore.getGeometryUnion(), index); + + { + PX_PROFILE_ZONE("API.simAddShapeToBroadPhase", scScene.getContextId()); + if(isBroadPhase(mCore.getFlags())) + internalAddToBroadPhase(); + else + scScene.getAABBManager()->reserveSpaceForBounds(index); + scScene.updateContactDistance(index, getContactOffset()); + } + + if(scScene.getDirtyShapeSimMap().size() <= index) + scScene.getDirtyShapeSimMap().resize(PxMax(index+1, (scScene.getDirtyShapeSimMap().size()+1) * 2u)); + + RigidSim& owner = getRbSim(); + if(owner.isDynamicRigid() && static_cast(owner).isActive()) + createSqBounds(); + + // Init LL shape + { + mLLShape.mElementIndex = index; + mLLShape.mShapeCore = const_cast(&mCore.getCore()); + + if(owner.getActorType()==PxActorType::eRIGID_STATIC) + { + mLLShape.mBodySimIndex = IG::NodeIndex(IG_INVALID_NODE); + } + else + { + BodySim& bodySim = static_cast(getActor()); + mLLShape.mBodySimIndex = bodySim.getNodeIndex(); + //mLLShape.mLocalBound = computeBounds(mCore.getGeometry(), PxTransform(PxIdentity)); + } + } +} + +ShapeSim::ShapeSim(RigidSim& owner, const ShapeCore& core) : + ElementSim (owner), + mCore (core), + mSqBoundsId (PX_INVALID_U32) +{ + // sizeof(ShapeSim) = 32 bytes + Scene& scScene = getScene(); + + mId = scScene.getShapeIDTracker().createID(); + + initSubsystemsDependingOnElementID(); +} + +ShapeSim::~ShapeSim() +{ + Scene& scScene = getScene(); + resetElementID(scScene, *this); + scScene.getShapeIDTracker().releaseID(mId); +} + +Bp::FilterGroup::Enum ShapeSim::getBPGroup() const +{ + bool isKinematic = false; + BodySim* bs = getBodySim(); + if(bs) + isKinematic = bs->isKinematic(); + + RigidSim& rbSim = getRbSim(); + return Bp::getFilterGroup(rbSim.getActorType()==PxActorType::eRIGID_STATIC, rbSim.getRigidID(), isKinematic); +} + +PX_FORCE_INLINE void ShapeSim::internalAddToBroadPhase() +{ + PX_ASSERT(!isInBroadPhase()); + + addToAABBMgr(mCore.getContactOffset(), getBPGroup(), Ps::IntBool(mCore.getCore().mShapeFlags & PxShapeFlag::eTRIGGER_SHAPE)); +} + +PX_FORCE_INLINE void ShapeSim::internalRemoveFromBroadPhase(bool wakeOnLostTouch) +{ + PX_ASSERT(isInBroadPhase()); + removeFromAABBMgr(); + + Scene& scene = getScene(); + PxsContactManagerOutputIterator outputs = scene.getLowLevelContext()->getNphaseImplementationContext()->getContactManagerOutputs(); + scene.getNPhaseCore()->onVolumeRemoved(this, wakeOnLostTouch ? PxU32(PairReleaseFlag::eWAKE_ON_LOST_TOUCH) : 0, outputs, scene.getPublicFlags() & PxSceneFlag::eADAPTIVE_FORCE); +} + +void ShapeSim::removeFromBroadPhase(bool wakeOnLostTouch) +{ + if(isInBroadPhase()) + internalRemoveFromBroadPhase(wakeOnLostTouch); +} + +void ShapeSim::reinsertBroadPhase() +{ + if(isInBroadPhase()) + internalRemoveFromBroadPhase(); +// internalAddToBroadPhase(); + + Scene& scene = getScene(); + + // Scene::removeShape + { + //unregisterShapeFromNphase(shape.getCore()); + + // PT: "getID" is const but the addShape call used LLShape, which uses elementID, so.... + scene.getSimulationController()->removeShape(getID()); + } + + // Call ShapeSim dtor + { + resetElementID(scene, *this); + } + + // Call ElementSim dtor + { + releaseID(); + } + + // Call ElementSim ctor + { + initID(); + } + + // Call ShapeSim ctor + { + initSubsystemsDependingOnElementID(); + } + + // Scene::addShape + { + scene.getSimulationController()->addShape(&getLLShapeSim(), getID()); + // PT: TODO: anything else needed here? + //registerShapeInNphase(shapeCore); + } +} + +void ShapeSim::onFilterDataChange() +{ + setElementInteractionsDirty(InteractionDirtyFlag::eFILTER_STATE, InteractionFlag::eFILTERABLE); +} + +void ShapeSim::onResetFiltering() +{ + if(isInBroadPhase()) + reinsertBroadPhase(); +} + +void ShapeSim::onMaterialChange() +{ + setElementInteractionsDirty(InteractionDirtyFlag::eMATERIAL, InteractionFlag::eRB_ELEMENT); +} + +void ShapeSim::onRestOffsetChange() +{ + setElementInteractionsDirty(InteractionDirtyFlag::eREST_OFFSET, InteractionFlag::eRB_ELEMENT); +} + +void ShapeSim::onContactOffsetChange() +{ + if(isInBroadPhase()) + getScene().getAABBManager()->setContactOffset(getElementID(), mCore.getContactOffset()); +} + +void ShapeSim::onFlagChange(PxShapeFlags oldFlags) +{ + const PxShapeFlags newFlags = mCore.getFlags(); + + const bool oldBp = isBroadPhase(oldFlags)!=0; + const bool newBp = isBroadPhase(newFlags)!=0; + + // Change of collision shape flags requires removal/add to broadphase + if(oldBp != newBp) + { + if(!oldBp && newBp) + { + // A.B. if a trigger was removed and inserted within the same frame we need to reinsert + if(hasTriggerFlags(newFlags) && getScene().getAABBManager()->isMarkedForRemove(getElementID())) + { + reinsertBroadPhase(); + } + else + { + internalAddToBroadPhase(); + } + } + else + internalRemoveFromBroadPhase(); + } + else + { + const bool wasTrigger = hasTriggerFlags(oldFlags)!=0; + const bool isTrigger = hasTriggerFlags(newFlags)!=0; + if(wasTrigger != isTrigger) + reinsertBroadPhase(); // re-insertion is necessary because trigger pairs get killed + } + + const PxShapeFlags hadSq = oldFlags & PxShapeFlag::eSCENE_QUERY_SHAPE; + const PxShapeFlags hasSq = newFlags & PxShapeFlag::eSCENE_QUERY_SHAPE; + if(hasSq && !hadSq) + { + BodySim* body = getBodySim(); + if(body && body->isActive()) + createSqBounds(); + } + else if(hadSq && !hasSq) + destroySqBounds(); +} + +void ShapeSim::getAbsPoseAligned(PxTransform* PX_RESTRICT globalPose) const +{ + const PxTransform& shape2Actor = mCore.getCore().transform; + const PxTransform* actor2World = NULL; + if(getActor().getActorType()==PxActorType::eRIGID_STATIC) + { + PxsRigidCore& core = static_cast(getActor()).getStaticCore().getCore(); + actor2World = &core.body2World; + } + else + { + PxsBodyCore& core = static_cast(getActor()).getBodyCore().getCore(); + if(!core.mIdtBody2Actor) + { + Cm::getDynamicGlobalPoseAligned(core.body2World, shape2Actor, core.getBody2Actor(), *globalPose); + return; + } + actor2World = &core.body2World; + } + Cm::getStaticGlobalPoseAligned(*actor2World, shape2Actor, *globalPose); +} + +BodySim* ShapeSim::getBodySim() const +{ + ActorSim& a = getActor(); + return a.isDynamicRigid() ? static_cast(&a) : NULL; +} + +PxsRigidCore& ShapeSim::getPxsRigidCore() const +{ + ActorSim& a = getActor(); + return a.isDynamicRigid() ? static_cast(a).getBodyCore().getCore() + : static_cast(a).getStaticCore().getCore(); +} + +void ShapeSim::updateCached(PxU32 transformCacheFlags, Cm::BitMapPinned* shapeChangedMap) +{ + PX_ALIGN(16, PxTransform absPose); + getAbsPoseAligned(&absPose); + + Scene& scene = getScene(); + const PxU32 index = getElementID(); + + scene.getLowLevelContext()->getTransformCache().setTransformCache(absPose, transformCacheFlags, index); + scene.getBoundsArray().updateBounds(absPose, mCore.getGeometryUnion(), index); + if (shapeChangedMap && isInBroadPhase()) + shapeChangedMap->growAndSet(index); +} + +void ShapeSim::updateCached(PxsTransformCache& transformCache, Bp::BoundsArray& boundsArray) +{ + const PxU32 index = getElementID(); + + PxsCachedTransform& ct = transformCache.getTransformCache(index); + Ps::prefetchLine(&ct); + + getAbsPoseAligned(&ct.transform); + + ct.flags = 0; + + PxBounds3& b = boundsArray.begin()[index]; + Gu::computeBounds(b, mCore.getGeometryUnion().getGeometry(), ct.transform, 0.0f, NULL, 1.0f); +} + +void ShapeSim::updateContactDistance(PxReal* contactDistance, const PxReal inflation, const PxVec3 angVel, const PxReal dt, Bp::BoundsArray& boundsArray) +{ + const PxU32 index = getElementID(); + + const PxBounds3& bounds = boundsArray.getBounds(index); + + PxReal radius = bounds.getExtents().magnitude(); + + //Heuristic for angular velocity... + PxReal angularInflation = angVel.magnitude() * dt * radius; + + contactDistance[index] = getContactOffset() + inflation + angularInflation; +} + +Ps::IntBool ShapeSim::updateSweptBounds() +{ + Vec3p endOrigin, endExtent; + const ShapeCore& shapeCore = mCore; + const PxTransform& endPose = getScene().getLowLevelContext()->getTransformCache().getTransformCache(getElementID()).transform; + PxReal ccdThreshold = computeBoundsWithCCDThreshold(endOrigin, endExtent, shapeCore.getGeometry(), endPose, NULL); + + PxBounds3 bounds = PxBounds3::centerExtents(endOrigin, endExtent); + + BodySim* body = getBodySim(); + PxcRigidBody& rigidBody = body->getLowLevelBody(); + PxsBodyCore& bodyCore = body->getBodyCore().getCore(); + PX_ALIGN(16, PxTransform shape2World); + Cm::getDynamicGlobalPoseAligned(rigidBody.mLastTransform, shapeCore.getShape2Actor(), bodyCore.getBody2Actor(), shape2World); + PxBounds3 startBounds = computeBounds(shapeCore.getGeometry(), shape2World); + + const Ps::IntBool isFastMoving = (startBounds.getCenter() - endOrigin).magnitudeSquared() >= ccdThreshold * ccdThreshold ? 1 : 0; + + if (isFastMoving) + bounds.include(startBounds); + + PX_ASSERT(bounds.minimum.x <= bounds.maximum.x + && bounds.minimum.y <= bounds.maximum.y + && bounds.minimum.z <= bounds.maximum.z); + + getScene().getBoundsArray().setBounds(bounds, getElementID()); + + return isFastMoving; +} + +void ShapeSim::updateBPGroup() +{ + if(isInBroadPhase()) + { + Sc::Scene& scene = getScene(); + scene.getAABBManager()->setBPGroup(getElementID(), getBPGroup()); + + reinsertBroadPhase(); +// internalRemoveFromBroadPhase(); +// internalAddToBroadPhase(); + } +} + +void ShapeSim::markBoundsForUpdate(bool forceBoundsUpdate) +{ + Scene& scene = getScene(); + if(forceBoundsUpdate) + updateCached(0, &scene.getAABBManager()->getChangedAABBMgActorHandleMap()); + else if(isInBroadPhase()) + scene.getDirtyShapeSimMap().growAndSet(getElementID()); +} + +static PX_FORCE_INLINE void updateInteraction(Scene& scene, Interaction* i, const bool isDynamic, const bool isAsleep) +{ + if(i->getType() == InteractionType::eOVERLAP) + { + ShapeInteraction* si = static_cast(i); + si->resetManagerCachedState(); + + if (isAsleep) + si->onShapeChangeWhileSleeping(isDynamic); + } + else if(i->getType() == InteractionType::eTRIGGER) + (static_cast(i))->forceProcessingThisFrame(scene); // trigger pairs need to be checked next frame +} + +void ShapeSim::onVolumeOrTransformChange(bool forceBoundsUpdate) +{ + Scene& scene = getScene(); + BodySim* body = getBodySim(); + const bool isDynamic = (body != NULL); + const bool isAsleep = body ? !body->isActive() : true; + + ElementSim::ElementInteractionIterator iter = getElemInteractions(); + ElementSimInteraction* i = iter.getNext(); + while(i) + { + updateInteraction(scene, i, isDynamic, isAsleep); + i = iter.getNext(); + } + + markBoundsForUpdate(forceBoundsUpdate); +} + +void notifyActorInteractionsOfTransformChange(ActorSim& actor) +{ + bool isDynamic; + bool isAsleep; + if(actor.isDynamicRigid()) + { + isDynamic = true; + isAsleep = !static_cast(actor).isActive(); + } + else + { + isDynamic = false; + isAsleep = true; + } + + Scene& scene = actor.getScene(); + + PxU32 nbInteractions = actor.getActorInteractionCount(); + Interaction** interactions = actor.getActorInteractions(); + while(nbInteractions--) + updateInteraction(scene, *interactions++, isDynamic, isAsleep); +} + +void ShapeSim::createSqBounds() +{ + if(mSqBoundsId!=PX_INVALID_U32) + return; + + BodySim* bodySim = getBodySim(); + PX_ASSERT(bodySim); + + if(bodySim->usingSqKinematicTarget() || bodySim->isFrozen() || !bodySim->isActive() || bodySim->readInternalFlag(BodySim::BF_IS_COMPOUND_RIGID)) + return; + + if(mCore.getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE) + getScene().getSqBoundsManager().addShape(*this); +} + +void ShapeSim::destroySqBounds() +{ + if(mSqBoundsId!=PX_INVALID_U32) + getScene().getSqBoundsManager().removeShape(*this); +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h new file mode 100644 index 000000000..eadc1a2da --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h @@ -0,0 +1,151 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_SHAPESIM +#define PX_PHYSICS_SCP_SHAPESIM + +#include "ScElementSim.h" +#include "ScShapeCore.h" +#include "CmPtrTable.h" +#include "ScRigidSim.h" +#include "PxsShapeSim.h" + +namespace physx +{ + + class PxsTransformCache; +namespace Gu +{ + class TriangleMesh; + class HeightField; +} + +/** Simulation object corresponding to a shape core object. This object is created when + a ShapeCore object is added to the simulation, and destroyed when it is removed +*/ + +struct PxsRigidCore; + +namespace Sc +{ + + class RigidSim; + class ShapeCore; + class Scene; + class BodySim; + class StaticSim; + + class ShapeSim : public ElementSim + { + ShapeSim &operator=(const ShapeSim &); + public: + + // passing in a pointer for the shape to output its bounds allows us not to have to compute them twice. + // A neater way to do this would be to ask the AABB Manager for the bounds after the shape has been + // constructed, but there is currently no spec for what the AABBMgr is allowed to do with the bounds, + // hence better not to assume anything. + + ShapeSim(RigidSim&, const ShapeCore& core); + ~ShapeSim(); + + void reinsertBroadPhase(); + + PX_FORCE_INLINE const ShapeCore& getCore() const { return mCore; } + + // TODO: compile time coupling + + PX_INLINE PxGeometryType::Enum getGeometryType() const { return mCore.getGeometryType(); } + + // This is just for getting a reference for the user, so we cast away const-ness + + PX_INLINE PxShape* getPxShape() const { return const_cast(mCore.getPxShape()); } + + PX_FORCE_INLINE PxReal getRestOffset() const { return mCore.getRestOffset(); } + PX_FORCE_INLINE PxReal getTorsionalPatchRadius() const { return mCore.getTorsionalPatchRadius(); } + PX_FORCE_INLINE PxReal getMinTorsionalPatchRadius() const { return mCore.getMinTorsionalPatchRadius(); } + PX_FORCE_INLINE PxU32 getFlags() const { return mCore.getFlags(); } + PX_FORCE_INLINE PxReal getContactOffset() const { return mCore.getContactOffset(); } + + PX_FORCE_INLINE RigidSim& getRbSim() const { return static_cast(getActor()); } + BodySim* getBodySim() const; + + PxsRigidCore& getPxsRigidCore() const; + + void onFilterDataChange(); + void onRestOffsetChange(); + void onFlagChange(PxShapeFlags oldFlags); + void onResetFiltering(); + void onVolumeOrTransformChange(bool forceBoundsUpdate); + void onMaterialChange(); // remove when material properties are gone from PxcNpWorkUnit + void onContactOffsetChange(); + void markBoundsForUpdate(bool forceBoundsUpdate); + + void getAbsPoseAligned(PxTransform* PX_RESTRICT globalPose) const; + + PX_FORCE_INLINE PxU32 getID() const { return mId; } + PX_FORCE_INLINE PxU32 getTransformCacheID() const { return getElementID(); } + + void removeFromBroadPhase(bool wakeOnLostTouch); + + void createSqBounds(); + void destroySqBounds(); + + PX_FORCE_INLINE PxU32 getSqBoundsId() const { return mSqBoundsId; } + PX_FORCE_INLINE void setSqBoundsId(PxU32 id) { mSqBoundsId = id; } + + void updateCached(PxU32 transformCacheFlags, Cm::BitMapPinned* shapeChangedMap); + void updateCached(PxsTransformCache& transformCache, Bp::BoundsArray& boundsArray); + void updateContactDistance(PxReal* contactDistance, const PxReal inflation, const PxVec3 angVel, const PxReal dt, Bp::BoundsArray& boundsArray); + Ps::IntBool updateSweptBounds(); + void updateBPGroup(); + + PX_FORCE_INLINE PxsShapeSim& getLLShapeSim() { return mLLShape; } + private: + PxsShapeSim mLLShape; + const ShapeCore& mCore; + PxU32 mId; + PxU32 mSqBoundsId; + + PX_FORCE_INLINE void internalAddToBroadPhase(); + PX_FORCE_INLINE void internalRemoveFromBroadPhase(bool wakeOnLostTouch=true); + void initSubsystemsDependingOnElementID(); + Bp::FilterGroup::Enum getBPGroup() const; + }; + +#if !PX_P64_FAMILY +// PX_COMPILE_TIME_ASSERT(32==sizeof(Sc::ShapeSim)); // after removing bounds from shapes +// PX_COMPILE_TIME_ASSERT((sizeof(Sc::ShapeSim) % 16) == 0); // aligned mem bounds are better for prefetching +#endif + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h b/src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h new file mode 100644 index 000000000..97be8b629 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h @@ -0,0 +1,139 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_SIMSTATEDATA +#define PX_SIMSTATEDATA + +#include "foundation/PxMemory.h" +#include "ScBodyCore.h" + +namespace physx +{ +namespace Sc +{ + struct Kinematic : public KinematicTransform + { + // The following members buffer the original body data to restore them when switching back to dynamic body + // (for kinematics the corresponding LowLevel properties are set to predefined values) + PxVec3 backupInverseInertia; // The inverse of the body space inertia tensor + PxReal backupInvMass; // The inverse of the body mass + PxReal backupLinearDamping; // The velocity is scaled by (1.0f - this * dt) inside integrateVelocity() every substep. + PxReal backupAngularDamping; + PxReal backupMaxAngVelSq; // The angular velocity's magnitude is clamped to this maximum value. + PxReal backupMaxLinVelSq; // The angular velocity's magnitude is clamped to this maximum value + }; + PX_COMPILE_TIME_ASSERT(0 == (sizeof(Kinematic) & 0x0f)); + + // Important: Struct is reset in setForcesToDefaults. + + enum VelocityModFlags + { + VMF_GRAVITY_DIRTY = (1 << 0), + VMF_ACC_DIRTY = (1 << 1), + VMF_VEL_DIRTY = (1 << 2) + }; + + struct VelocityMod + { + PxVec3 linearPerSec; // A request to change the linear velocity by this much each second. The velocity is changed by this * dt inside integrateVelocity(). + PxU8 flags; + PxU8 pad0[3]; + PxVec3 angularPerSec; + PxU8 pad1[3]; + PxU8 type; + PxVec3 linearPerStep; // A request to change the linear velocity by this much the next step. The velocity is changed inside updateForces(). + PxU32 pad2; + PxVec3 angularPerStep; + PxU32 pad3; + + PX_FORCE_INLINE void clear() { linearPerSec = angularPerSec = linearPerStep = angularPerStep = PxVec3(0.0f); } + + PX_FORCE_INLINE void clearPerStep() { linearPerStep = angularPerStep = PxVec3(0.0f); } + + PX_FORCE_INLINE const PxVec3& getLinearVelModPerSec() const { return linearPerSec; } + PX_FORCE_INLINE void accumulateLinearVelModPerSec(const PxVec3& v) { linearPerSec += v; } + PX_FORCE_INLINE void setLinearVelModPerSec(const PxVec3& v) { linearPerSec = v; } + PX_FORCE_INLINE void clearLinearVelModPerSec() { linearPerSec = PxVec3(0.0f); } + + PX_FORCE_INLINE const PxVec3& getLinearVelModPerStep() const { return linearPerStep; } + PX_FORCE_INLINE void accumulateLinearVelModPerStep(const PxVec3& v) { linearPerStep += v; } + PX_FORCE_INLINE void clearLinearVelModPerStep() { linearPerStep = PxVec3(0.0f); } + + PX_FORCE_INLINE const PxVec3& getAngularVelModPerSec() const { return angularPerSec; } + PX_FORCE_INLINE void accumulateAngularVelModPerSec(const PxVec3& v) { angularPerSec += v; } + PX_FORCE_INLINE void setAngularVelModPerSec(const PxVec3& v) { angularPerSec = v; } + PX_FORCE_INLINE void clearAngularVelModPerSec() { angularPerSec = PxVec3(0.0f); } + + PX_FORCE_INLINE const PxVec3& getAngularVelModPerStep() const { return angularPerStep; } + PX_FORCE_INLINE void accumulateAngularVelModPerStep(const PxVec3& v) { angularPerStep += v; } + PX_FORCE_INLINE void clearAngularVelModPerStep() { angularPerStep = PxVec3(0.0f); } + + PX_FORCE_INLINE void notifyAddAcceleration() { flags |= VMF_ACC_DIRTY; } + PX_FORCE_INLINE void notifyClearAcceleration() { flags |= VMF_ACC_DIRTY; } + PX_FORCE_INLINE void notifyAddVelocity() { flags |= VMF_VEL_DIRTY; } + PX_FORCE_INLINE void notifyClearVelocity() { flags |= VMF_VEL_DIRTY; } + }; + PX_COMPILE_TIME_ASSERT(sizeof(VelocityMod) == sizeof(Kinematic)); + + + // Structure to store data for kinematics (target pose etc.) + // note: we do not delete this object for kinematics even if no target is set. + struct SimStateData : public Ps::UserAllocated // TODO: may want to optimize the allocation of this further. + { + PxU8 data[sizeof(Kinematic)]; + + enum Enum + { + eVelMod=0, + eKine + }; + + SimStateData(){} + SimStateData(const PxU8 type) + { + PxMemZero(data, sizeof(Kinematic)); + Kinematic* kine = reinterpret_cast(data); + kine->type = type; + } + + PX_FORCE_INLINE PxU32 getType() const { const Kinematic* kine = reinterpret_cast(data); return kine->type;} + PX_FORCE_INLINE bool isKine() const {return eKine == getType();} + PX_FORCE_INLINE bool isVelMod() const {return eVelMod == getType();} + + Kinematic* getKinematicData() { Kinematic* kine = reinterpret_cast(data); PX_ASSERT(eKine == kine->type); return kine;} + VelocityMod* getVelocityModData() { VelocityMod* velmod = reinterpret_cast(data); PX_ASSERT(eVelMod == velmod->type); return velmod;} + const Kinematic* getKinematicData() const { const Kinematic* kine = reinterpret_cast(data); PX_ASSERT(eKine == kine->type); return kine;} + const VelocityMod* getVelocityModData() const { const VelocityMod* velmod = reinterpret_cast(data); PX_ASSERT(eVelMod == velmod->type); return velmod;} + }; + +} // namespace Sc + +} // namespace physx + +#endif //PX_SIMSTATEDATA diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp new file mode 100644 index 000000000..b246dfbbd --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "ScSimStats.h" +#include "PxvSimStats.h" + +using namespace physx; + +Sc::SimStats::SimStats() +{ + numBroadPhaseAdds = numBroadPhaseRemoves = 0; + + clear(); +} + +void Sc::SimStats::clear() +{ +#if PX_ENABLE_SIM_STATS + PxMemZero(const_cast(reinterpret_cast(&numTriggerPairs)), sizeof(TriggerPairCounts)); + numBroadPhaseAddsPending = numBroadPhaseRemovesPending = 0; +#endif +} + +void Sc::SimStats::simStart() +{ +#if PX_ENABLE_SIM_STATS + // pending broadphase adds/removes are now the current ones + numBroadPhaseAdds = numBroadPhaseAddsPending; + numBroadPhaseRemoves = numBroadPhaseRemovesPending; + clear(); +#endif +} + +void Sc::SimStats::readOut(PxSimulationStatistics& s, const PxvSimStats& simStats) const +{ +#if PX_ENABLE_SIM_STATS + s = PxSimulationStatistics(); // clear stats + + for(PxU32 i=0; i < PxGeometryType::eCONVEXMESH+1; i++) + { + for(PxU32 j=0; j < PxGeometryType::eGEOMETRY_COUNT; j++) + { + s.nbTriggerPairs[i][j] += PxU32(numTriggerPairs[i][j]); + if (i != j) + s.nbTriggerPairs[j][i] += PxU32(numTriggerPairs[i][j]); + } + } + + s.nbBroadPhaseAdds = numBroadPhaseAdds; + s.nbBroadPhaseRemoves = numBroadPhaseRemoves; + + for(PxU32 i=0; i < PxGeometryType::eGEOMETRY_COUNT; i++) + { + s.nbDiscreteContactPairs[i][i] = simStats.mNbDiscreteContactPairs[i][i]; + s.nbModifiedContactPairs[i][i] = simStats.mNbModifiedContactPairs[i][i]; + s.nbCCDPairs[i][i] = simStats.mNbCCDPairs[i][i]; + + for(PxU32 j=i+1; j < PxGeometryType::eGEOMETRY_COUNT; j++) + { + PxU32 c = simStats.mNbDiscreteContactPairs[i][j]; + s.nbDiscreteContactPairs[i][j] = c; + s.nbDiscreteContactPairs[j][i] = c; + + c = simStats.mNbModifiedContactPairs[i][j]; + s.nbModifiedContactPairs[i][j] = c; + s.nbModifiedContactPairs[j][i] = c; + + c = simStats.mNbCCDPairs[i][j]; + s.nbCCDPairs[i][j] = c; + s.nbCCDPairs[j][i] = c; + } +#if PX_DEBUG + for(PxU32 j=0; j < i; j++) + { + // PxvSimStats should only use one half of the matrix + PX_ASSERT(simStats.mNbDiscreteContactPairs[i][j] == 0); + PX_ASSERT(simStats.mNbModifiedContactPairs[i][j] == 0); + PX_ASSERT(simStats.mNbCCDPairs[i][j] == 0); + } +#endif + } + + s.nbDiscreteContactPairsTotal = simStats.mNbDiscreteContactPairsTotal; + s.nbDiscreteContactPairsWithCacheHits = simStats.mNbDiscreteContactPairsWithCacheHits; + s.nbDiscreteContactPairsWithContacts = simStats.mNbDiscreteContactPairsWithContacts; + s.nbActiveConstraints = simStats.mNbActiveConstraints; + s.nbActiveDynamicBodies = simStats.mNbActiveDynamicBodies; + s.nbActiveKinematicBodies = simStats.mNbActiveKinematicBodies; + + s.nbAxisSolverConstraints = simStats.mNbAxisSolverConstraints; + + s.peakConstraintMemory = simStats.mPeakConstraintBlockAllocations * 16 * 1024; + s.compressedContactSize = simStats.mTotalCompressedContactSize; + s.requiredContactConstraintMemory = simStats.mTotalConstraintSize; + s.nbNewPairs = simStats.mNbNewPairs; + s.nbLostPairs = simStats.mNbLostPairs; + s.nbNewTouches = simStats.mNbNewTouches; + s.nbLostTouches = simStats.mNbLostTouches; + s.nbPartitions = simStats.mNbPartitions; + +#else + PX_UNUSED(s); + PX_UNUSED(simStats); +#endif +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h new file mode 100644 index 000000000..06356e815 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h @@ -0,0 +1,89 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_SIM_STATS +#define PX_PHYSICS_SCP_SIM_STATS + +#include "PsAtomic.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PxGeometry.h" +#include "PxSimulationStatistics.h" + +namespace physx +{ + +struct PxvSimStats; + +namespace Sc +{ + + /* + Description: contains statistics for the scene. + */ + class SimStats : public Ps::UserAllocated + { + public: + SimStats(); + + void clear(); //set counters to zero + void simStart(); + void readOut(PxSimulationStatistics& dest, const PxvSimStats& simStats) const; + + PX_INLINE void incBroadphaseAdds() + { + numBroadPhaseAddsPending++; + } + + PX_INLINE void incBroadphaseRemoves() + { + numBroadPhaseRemovesPending++; + } + + private: + // Broadphase adds/removes for the current simulation step + PxU32 numBroadPhaseAdds; + PxU32 numBroadPhaseRemoves; + + // Broadphase adds/removes for the next simulation step + PxU32 numBroadPhaseAddsPending; + PxU32 numBroadPhaseRemovesPending; + + public: + typedef PxI32 TriggerPairCountsNonVolatile[PxGeometryType::eCONVEXMESH+1][PxGeometryType::eGEOMETRY_COUNT]; + typedef volatile TriggerPairCountsNonVolatile TriggerPairCounts; + TriggerPairCounts numTriggerPairs; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp new file mode 100644 index 000000000..952436230 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp @@ -0,0 +1,43 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScSimulationController.h" +#include "PsAllocator.h" + +using namespace physx; + +PxsSimulationController* physx::createSimulationController(PxsSimulationControllerCallback* callback) +{ + return PX_PLACEMENT_NEW(PX_ALLOC(sizeof(Sc::SimulationController), PX_DEBUG_EXP("ScSimulationController")), Sc::SimulationController(callback)); +} + +void Sc::SimulationController::udpateScBodyAndShapeSim(PxsTransformCache& /*cache*/, Bp::BoundsArray& /*boundArray*/, PxBaseTask* continuation) +{ + mCallback->updateScBodyAndShapeSim(continuation); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h new file mode 100644 index 000000000..6360c4dbf --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxsSimulationController.h" + +#ifndef SC_SIMULATION_CONTROLLER_H +#define SC_SIMULATION_CONTROLLER_H + + +namespace physx +{ + +class PxsHeapMemoryAllocator; + +namespace Sc +{ + + class SimulationController : public PxsSimulationController + { + PX_NOCOPY(SimulationController) + public: + SimulationController(PxsSimulationControllerCallback* callback): PxsSimulationController(callback) + { + } + + virtual ~SimulationController(){} + virtual void addJoint(const PxU32 /*edgeIndex*/, Dy::Constraint* /*constraint*/, IG::IslandSim& /*islandSim*/, Ps::Array& /*jointIndices*/, + Ps::Array& /*managerIter*/, PxU32 /*uniqueId*/){} + virtual void removeJoint(const PxU32 /*edgeIndex*/, Dy::Constraint* /*constraint*/, Ps::Array& /*jointIndices*/, IG::IslandSim& /*islandSim*/){} + virtual void addShape(PxsShapeSim* /*shapeSim*/, const PxU32 /*index*/){} + virtual void removeShape(const PxU32 /*index*/){} + virtual void addDynamic(PxsRigidBody* /*rigidBody*/, const IG::NodeIndex& /*nodeIndex*/){} + virtual void addDynamics(PxsRigidBody** /*rigidBody*/, const PxU32* /*nodeIndex*/, PxU32 /*nbBodies*/) {} + virtual void addArticulation(Dy::ArticulationV* /*articulation*/, const IG::NodeIndex& /*nodeIndex*/){} + virtual void releaseArticulation(Dy::ArticulationV* /*articulation*/) {} + virtual void releaseDeferredArticulationIds(){} + virtual void updateDynamic(const bool /*isArticulationLink*/, const IG::NodeIndex& /*nodeIndex*/) {} + virtual void updateJoint(const PxU32 /*edgeIndex*/, Dy::Constraint* /*constraint*/){} + virtual void updateBodies(PxsRigidBody** /*rigidBodies*/, PxU32* /*nodeIndices*/, const PxU32 /*nbBodies*/) {} + virtual void updateBody(PxsRigidBody* /*rigidBody*/, const PxU32 /*nodeIndex*/) {} + virtual void updateBodiesAndShapes(PxBaseTask* /*continuation*/){} + virtual void update(const PxU32 /*bitMapWordCounts*/){} + virtual void gpuDmabackData(PxsTransformCache& /*cache*/, Bp::BoundsArray& /*boundArray*/, Cm::BitMapPinned& /*changedAABBMgrHandles*/){} + virtual void udpateScBodyAndShapeSim(PxsTransformCache& cache, Bp::BoundsArray& boundArray, PxBaseTask* continuation); + virtual PxU32* getActiveBodies() { return NULL; } + virtual PxU32* getDeactiveBodies() { return NULL; } + virtual void** getRigidBodies() { return NULL; } + virtual PxU32 getNbBodies() { return 0; } + virtual PxU32 getNbFrozenShapes() { return 0; } + virtual PxU32 getNbUnfrozenShapes() { return 0; } + + virtual PxU32* getUnfrozenShapes() { return NULL; } + virtual PxU32* getFrozenShapes() { return NULL; } + virtual PxsShapeSim** getShapeSims() { return NULL; } + virtual PxU32 getNbShapes() { return 0; } + + virtual void clear() { } + virtual void setBounds(Bp::BoundsArray* /*boundArray*/){} + virtual void reserve(const PxU32 /*nbBodies*/) {} + + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp new file mode 100644 index 000000000..7676fcd57 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp @@ -0,0 +1,122 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScSqBoundsManager.h" +#include "ScBodySim.h" +#include "ScShapeSim.h" +#include "common/PxProfileZone.h" + +using namespace physx; +using namespace Sc; + +SqBoundsManager::SqBoundsManager() : + mShapes (PX_DEBUG_EXP("SqBoundsManager::mShapes")), + mRefs (PX_DEBUG_EXP("SqBoundsManager::mRefs")), + mBoundsIndices (PX_DEBUG_EXP("SqBoundsManager::mBoundsIndices")), + mRefless (PX_DEBUG_EXP("SqBoundsManager::mRefless")) +{ +} + +void SqBoundsManager::addShape(ShapeSim& shape) +{ + PX_ASSERT(shape.getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE); + PX_ASSERT(!shape.getBodySim()->usingSqKinematicTarget()); + PX_ASSERT(!shape.getBodySim()->isFrozen()); + + const PxU32 id = mShapes.size(); + PX_ASSERT(id == mRefs.size()); + PX_ASSERT(id == mBoundsIndices.size()); + + shape.setSqBoundsId(id); + + mShapes.pushBack(&shape); + mRefs.pushBack(PX_INVALID_U32); // PT: TODO: should be INVALID_PRUNERHANDLE but cannot include SqPruner.h + mBoundsIndices.pushBack(shape.getElementID()); + mRefless.pushBack(&shape); +} + +void SqBoundsManager::removeShape(ShapeSim& shape) +{ + const PxU32 id = shape.getSqBoundsId(); + PX_ASSERT(id!=PX_INVALID_U32); + + shape.setSqBoundsId(PX_INVALID_U32); + mShapes[id] = mShapes.back(); + mBoundsIndices[id] = mBoundsIndices.back(); + mRefs[id] = mRefs.back(); + + if(id+1 != mShapes.size()) + mShapes[id]->setSqBoundsId(id); + + mShapes.popBack(); + mRefs.popBack(); + mBoundsIndices.popBack(); +} + +void SqBoundsManager::syncBounds(SqBoundsSync& sync, SqRefFinder& finder, const PxBounds3* bounds, PxU64 contextID, const Cm::BitMap& dirtyShapeSimMap) +{ + PX_PROFILE_ZONE("Sim.sceneQuerySyncBounds", contextID); + PX_UNUSED(contextID); + +#if PX_DEBUG + for(PxU32 i=0;iusingSqKinematicTarget()); + PX_ASSERT(!shape.getBodySim()->isFrozen()); + } +#endif + + ShapeSim*const * shapes = mRefless.begin(); + for(PxU32 i=0, size = mRefless.size();igetSqBoundsId(); + // PT: + // + // If id == PX_INVALID_U32, the shape has been removed and not re-added. Nothing to do in this case, we just ignore it. + // This case didn't previously exist since mRefless only contained valid (added) shapes. But now we left removed shapes in the + // structure, and these have an id == PX_INVALID_U32. + // + // Now if the id is valid but mRefs[id] == PX_INVALID_U32, this is a regular shape that has been added and not processed yet. + // So we process it. + // + // Finally, if both id and mRefs[id] are not PX_INVALID_U32, this is a shape that has been added, removed, and re-added. The + // array contains the same shape twice and we only need to process it once. + if(id!=PX_INVALID_U32) + { + if(mRefs[id] == PX_INVALID_U32) // PT: TODO: should be INVALID_PRUNERHANDLE but cannot include SqPruner.h + mRefs[id] = finder.find(static_cast(shapes[i]->getBodySim()->getPxActor()), shapes[i]->getPxShape()); + } + } + mRefless.clear(); + + sync.sync(mRefs.begin(), mBoundsIndices.begin(), bounds, mShapes.size(), dirtyShapeSimMap); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h new file mode 100644 index 000000000..74d16425b --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_SQ_BOUNDS_MANAGER +#define PX_PHYSICS_SCP_SQ_BOUNDS_MANAGER + +#include "CmPhysXCommon.h" +#include "foundation/PxBounds3.h" +#include "PsArray.h" +#include "PsUserAllocated.h" +#include "PsHashSet.h" +#include "CmBitMap.h" + +namespace physx +{ +namespace Sq +{ +typedef PxU32 PrunerHandle; // PT: we should get this from SqPruner.h but it cannot be included from here +} + +namespace Sc +{ +struct SqBoundsSync; +struct SqRefFinder; +class ShapeSim; + +class SqBoundsManager : public Ps::UserAllocated +{ + PX_NOCOPY(SqBoundsManager) +public: + SqBoundsManager(); + + void addShape(ShapeSim& shape); + void removeShape(ShapeSim& shape); + void syncBounds(SqBoundsSync& sync, SqRefFinder& finder, const PxBounds3* bounds, PxU64 contextID, const Cm::BitMap& dirtyShapeSimMap); + +private: + + Ps::Array mShapes; // + Ps::Array mRefs; // SQ pruner references + Ps::Array mBoundsIndices; // indices into the Sc bounds array + Ps::Array mRefless; // shapesims without references +}; +} +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp new file mode 100644 index 000000000..12ff0e08f --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp @@ -0,0 +1,49 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScStaticCore.h" +#include "ScStaticSim.h" +#include "PxRigidStatic.h" + +using namespace physx; + +Sc::StaticSim* Sc::StaticCore::getSim() const +{ + return static_cast(Sc::ActorCore::getSim()); +} + +void Sc::StaticCore::setActor2World(const PxTransform& actor2World) +{ + mCore.body2World = actor2World; + + StaticSim* sim = getSim(); + if(sim) + sim->notifyShapesOfTransformChange(); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h new file mode 100644 index 000000000..f57cf6369 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_STATIC_SIM +#define PX_PHYSICS_SCP_STATIC_SIM + +#include "ScRigidSim.h" +#include "ScStaticCore.h" + +namespace physx +{ +namespace Sc +{ + class StaticSim : public RigidSim + { + //--------------------------------------------------------------------------------- + // Construction, destruction & initialization + //--------------------------------------------------------------------------------- + public: + StaticSim(Scene& scene, StaticCore& core) : RigidSim(scene, core) {} + ~StaticSim() { getStaticCore().setSim(NULL); } + + PX_FORCE_INLINE StaticCore& getStaticCore() const { return static_cast(getRigidCore()); } + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp new file mode 100644 index 000000000..a4f5ce858 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp @@ -0,0 +1,135 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScTriggerInteraction.h" +#include "ScBodySim.h" +#include "ScNPhaseCore.h" + +using namespace physx; +using namespace Sc; + +TriggerInteraction::TriggerInteraction( ShapeSim& tShape, ShapeSim& oShape) : + ElementSimInteraction(tShape, oShape, InteractionType::eTRIGGER, InteractionFlag::eRB_ELEMENT | InteractionFlag::eFILTERABLE), + mFlags(PROCESS_THIS_FRAME), + mLastFrameHadContacts(false) +{ + // The PxPairFlags eNOTIFY_TOUCH_FOUND and eNOTIFY_TOUCH_LOST get stored and mixed up with internal flags. Make sure any breaking change gets noticed. + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_FOUND < PxPairFlag::eNOTIFY_TOUCH_LOST); + PX_COMPILE_TIME_ASSERT((PAIR_FLAGS_MASK & PxPairFlag::eNOTIFY_TOUCH_FOUND) == PxPairFlag::eNOTIFY_TOUCH_FOUND); + PX_COMPILE_TIME_ASSERT((PAIR_FLAGS_MASK & PxPairFlag::eNOTIFY_TOUCH_LOST) == PxPairFlag::eNOTIFY_TOUCH_LOST); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_FOUND < 0xffff); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_LOST < 0xffff); + PX_COMPILE_TIME_ASSERT(LAST < 0xffff); + + bool active = registerInActors(); + Scene& scene = getScene(); + scene.registerInteraction(this, active); + scene.getNPhaseCore()->registerInteraction(this); + + PX_ASSERT(getTriggerShape().getFlags() & PxShapeFlag::eTRIGGER_SHAPE); + mTriggerCache.state = Gu::TRIGGER_DISJOINT; +} + +TriggerInteraction::~TriggerInteraction() +{ + Scene& scene = getScene(); + scene.unregisterInteraction(this); + scene.getNPhaseCore()->unregisterInteraction(this); + unregisterFromActors(); +} + +static bool isOneActorActive(TriggerInteraction* trigger) +{ + const BodySim* bodySim0 = trigger->getTriggerShape().getBodySim(); + if(bodySim0 && bodySim0->isActive()) + { + PX_ASSERT(!bodySim0->isKinematic() || bodySim0->readInternalFlag(BodySim::BF_KINEMATIC_MOVED) || + bodySim0->readInternalFlag(BodySim::InternalFlags(BodySim::BF_KINEMATIC_SETTLING | BodySim::BF_KINEMATIC_SETTLING_2))); + return true; + } + + const BodySim* bodySim1 = trigger->getOtherShape().getBodySim(); + if(bodySim1 && bodySim1->isActive()) + { + PX_ASSERT(!bodySim1->isKinematic() || bodySim1->readInternalFlag(BodySim::BF_KINEMATIC_MOVED) || + bodySim1->readInternalFlag(BodySim::InternalFlags(BodySim::BF_KINEMATIC_SETTLING | BodySim::BF_KINEMATIC_SETTLING_2))); + return true; + } + + return false; +} + +// +// Some general information about triggers and sleeping +// +// The goal is to avoid running overlap tests if both objects are sleeping. +// This is an optimization for eNOTIFY_TOUCH_LOST events since the overlap state +// can not change if both objects are sleeping. eNOTIFY_TOUCH_FOUND should be sent nonetheless. +// For this to work the following assumptions are made: +// - On creation or if the pose of an actor is set, the pair will always be checked. +// - If the scenario above does not apply, then a trigger pair can only be deactivated, if both actors are sleeping. +// - If an overlapping actor is activated/deactivated, the trigger interaction gets notified +// +bool TriggerInteraction::onActivate_(void*) +{ + // IMPORTANT: this method can get called concurrently from multiple threads -> make sure shared resources + // are protected (note: there are none at the moment but it might change) + + if(!(readFlag(PROCESS_THIS_FRAME))) + { + if(isOneActorActive(this)) + { + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; + } + else + return false; + } + else + { + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; // newly created trigger pairs should always test for overlap, no matter the sleep state + } +} + +bool TriggerInteraction::onDeactivate_() +{ + if(!readFlag(PROCESS_THIS_FRAME)) + { + if(!isOneActorActive(this)) + { + clearInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; + } + else + return false; + } + else + return false; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h new file mode 100644 index 000000000..5de0fee14 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h @@ -0,0 +1,122 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_TRIGGERINTERACTION +#define PX_COLLISION_TRIGGERINTERACTION + +#include "ScElementSimInteraction.h" +#include "ScShapeSim.h" +#include "GuOverlapTests.h" + +namespace physx +{ +namespace Sc +{ + class TriggerInteraction : public ElementSimInteraction + { + public: + enum TriggerFlag + { + PAIR_FLAGS_MASK = ((PxPairFlag::eNOTIFY_TOUCH_LOST << 1) - 1), // Bits where the PxPairFlags eNOTIFY_TOUCH_FOUND and eNOTIFY_TOUCH_LOST get stored + NEXT_FREE = ((PAIR_FLAGS_MASK << 1) & ~PAIR_FLAGS_MASK), + + PROCESS_THIS_FRAME = (NEXT_FREE << 0), // the trigger pair is new or the pose of an actor was set -> initial processing required. + // This is important to cover cases where a static or kinematic + // (non-moving) trigger is created and overlaps with a sleeping + // object. Or for the case where a static/kinematic is teleported to a new + // location. TOUCH_FOUND should still get sent in that case. + LAST = (NEXT_FREE << 1) + }; + + TriggerInteraction(ShapeSim& triggerShape, ShapeSim& otherShape); + ~TriggerInteraction(); + + PX_FORCE_INLINE Gu::TriggerCache& getTriggerCache() { return mTriggerCache; } + PX_FORCE_INLINE ShapeSim& getTriggerShape() const { return static_cast(getElement0()); } + PX_FORCE_INLINE ShapeSim& getOtherShape() const { return static_cast(getElement1()); } + + PX_FORCE_INLINE bool lastFrameHadContacts() const { return mLastFrameHadContacts; } + PX_FORCE_INLINE void updateLastFrameHadContacts(bool hasContact) { mLastFrameHadContacts = hasContact; } + + PX_FORCE_INLINE PxPairFlags getTriggerFlags() const { return PxPairFlags(PxU32(mFlags) & PAIR_FLAGS_MASK); } + PX_FORCE_INLINE void setTriggerFlags(PxPairFlags triggerFlags); + + PX_FORCE_INLINE void raiseFlag(TriggerFlag flag) { mFlags |= flag; } + PX_FORCE_INLINE void clearFlag(TriggerFlag flag) { mFlags &= ~flag; } + PX_FORCE_INLINE Ps::IntBool readFlag(TriggerFlag flag) const { return Ps::IntBool(mFlags & flag); } + + PX_FORCE_INLINE void forceProcessingThisFrame(Sc::Scene& scene); + + bool onActivate_(void*); + bool onDeactivate_(); + + protected: + Gu::TriggerCache mTriggerCache; + PxU16 mFlags; + bool mLastFrameHadContacts; + }; + +} // namespace Sc + + +PX_FORCE_INLINE void Sc::TriggerInteraction::setTriggerFlags(PxPairFlags triggerFlags) +{ + PX_ASSERT(PxU32(triggerFlags) < (PxPairFlag::eDETECT_CCD_CONTACT << 1)); // to find out if a new PxPairFlag has been added in which case PAIR_FLAGS_MASK needs to get adjusted + +#if PX_CHECKED + if (triggerFlags & PxPairFlag::eNOTIFY_TOUCH_PERSISTS) + { + PX_WARN_ONCE("Trigger pairs do not support PxPairFlag::eNOTIFY_TOUCH_PERSISTS events any longer."); + } +#endif + + PxU32 newFlags = mFlags; + PxU32 fl = PxU32(triggerFlags) & PxU32(PxPairFlag::eNOTIFY_TOUCH_FOUND|PxPairFlag::eNOTIFY_TOUCH_LOST); + newFlags &= (~PAIR_FLAGS_MASK); // clear old flags + newFlags |= fl; + + mFlags = PxU16(newFlags); +} + + +PX_FORCE_INLINE void Sc::TriggerInteraction::forceProcessingThisFrame(Sc::Scene& scene) +{ + raiseFlag(PROCESS_THIS_FRAME); + + if (!readInteractionFlag(InteractionFlag::eIS_ACTIVE)) + { + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + scene.notifyInteractionActivated(this); + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h new file mode 100644 index 000000000..dbbbbc819 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_TRIGGER_PAIRS +#define PX_PHYSICS_SCP_TRIGGER_PAIRS + +#include "PsArray.h" +#include "CmPhysXCommon.h" +#include "PxFiltering.h" +#include "PxClient.h" +#include "PxSimulationEventCallback.h" + +namespace physx +{ +class PxShape; + +namespace Sc +{ + struct TriggerPairFlag + { + enum Enum + { + eTEST_FOR_REMOVED_SHAPES = PxTriggerPairFlag::eNEXT_FREE // for cases where the pair got deleted because one of the shape volumes got removed from broadphase. + // This covers scenarios like volume re-insertion into broadphase as well since the shape might get removed + // after such an operation. The scenarios to consider are: + // + // - shape gets removed (this includes raising PxActorFlag::eDISABLE_SIMULATION) + // - shape switches to eSCENE_QUERY_SHAPE only + // - shape switches to eSIMULATION_SHAPE + // - resetFiltering() + // - actor gets removed from an aggregate + }; + }; + + PX_COMPILE_TIME_ASSERT((1 << (8*sizeof(PxTriggerPairFlags::InternalType))) > TriggerPairFlag::eTEST_FOR_REMOVED_SHAPES); + + struct TriggerPairExtraData + { + PX_INLINE TriggerPairExtraData() : + shape0ID(0xffffffff), + shape1ID(0xffffffff), + client0ID(0xff), + client1ID(0xff) + { + } + + PX_INLINE TriggerPairExtraData(PxU32 s0ID, PxU32 s1ID, + PxClientID cl0ID, PxClientID cl1ID) : + shape0ID(s0ID), + shape1ID(s1ID), + client0ID(cl0ID), + client1ID(cl1ID) + { + } + + PxU32 shape0ID; + PxU32 shape1ID; + PxClientID client0ID; + PxClientID client1ID; + }; + + typedef Ps::Array TriggerBufferExtraData; + typedef Ps::Array TriggerBufferAPI; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/task/src/TaskManager.cpp b/src/PhysX/physx/source/task/src/TaskManager.cpp new file mode 100644 index 000000000..e5cd3fdf2 --- /dev/null +++ b/src/PhysX/physx/source/task/src/TaskManager.cpp @@ -0,0 +1,538 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "task/PxTask.h" +#include "task/PxTaskDefine.h" +#include "foundation/PxErrors.h" + +#include "PsThread.h" +#include "PsAtomic.h" +#include "PsMutex.h" +#include "PsHashMap.h" +#include "PsArray.h" +#include "PsAllocator.h" + +#define DOT_LOG 0 + +#define LOCK() shdfnd::Mutex::ScopedLock __lock__(mMutex) + +namespace physx +{ + const int EOL = -1; + typedef shdfnd::HashMap PxTaskNameToIDMap; + + struct PxTaskDepTableRow + { + PxTaskID mTaskID; + int mNextDep; + }; + typedef shdfnd::Array PxTaskDepTable; + + class PxTaskTableRow + { + public: + PxTaskTableRow() : mRefCount( 1 ), mStartDep(EOL), mLastDep(EOL) {} + void addDependency( PxTaskDepTable& depTable, PxTaskID taskID ) + { + int newDep = int(depTable.size()); + PxTaskDepTableRow row; + row.mTaskID = taskID; + row.mNextDep = EOL; + depTable.pushBack( row ); + + if( mLastDep == EOL ) + { + mStartDep = mLastDep = newDep; + } + else + { + depTable[ uint32_t(mLastDep) ].mNextDep = newDep; + mLastDep = newDep; + } + } + + PxTask * mTask; + volatile int mRefCount; + PxTaskType::Enum mType; + int mStartDep; + int mLastDep; + }; + typedef shdfnd::Array PxTaskTable; + + +/* Implementation of PxTaskManager abstract API */ +class PxTaskMgr : public PxTaskManager, public shdfnd::UserAllocated +{ + PX_NOCOPY(PxTaskMgr) +public: + PxTaskMgr(PxErrorCallback& , PxCpuDispatcher*, PxGpuDispatcher*); + ~PxTaskMgr(); + + void setCpuDispatcher( PxCpuDispatcher& ref ) + { + mCpuDispatcher = &ref; + } + + void setGpuDispatcher( PxGpuDispatcher& ref ) + { + mGpuDispatcher = &ref; + } + + PxCpuDispatcher* getCpuDispatcher() const + { + return mCpuDispatcher; + } + + PxGpuDispatcher* getGpuDispatcher() const + { + return mGpuDispatcher; + } + + void resetDependencies(); + void startSimulation(); + void stopSimulation(); + void taskCompleted( PxTask& task ); + + PxTaskID getNamedTask( const char *name ); + PxTaskID submitNamedTask( PxTask *task, const char *name, PxTaskType::Enum type = PxTaskType::TT_CPU ); + PxTaskID submitUnnamedTask( PxTask& task, PxTaskType::Enum type = PxTaskType::TT_CPU ); + PxTask* getTaskFromID( PxTaskID ); + + bool dispatchTask( PxTaskID taskID, bool gpuGroupStart ); + bool resolveRow( PxTaskID taskID, bool gpuGroupStart ); + + void release(); + + void finishBefore( PxTask& task, PxTaskID taskID ); + void startAfter( PxTask& task, PxTaskID taskID ); + + void addReference( PxTaskID taskID ); + void decrReference( PxTaskID taskID ); + int32_t getReference( PxTaskID taskID ) const; + + void decrReference( PxLightCpuTask& lighttask ); + void addReference( PxLightCpuTask& lighttask ); + + PxErrorCallback& mErrorCallback; + PxCpuDispatcher *mCpuDispatcher; + PxGpuDispatcher *mGpuDispatcher; + PxTaskNameToIDMap mName2IDmap; + volatile int mPendingTasks; + shdfnd::Mutex mMutex; + + PxTaskDepTable mDepTable; + PxTaskTable mTaskTable; + + shdfnd::Array mStartDispatch; + }; + +PxTaskManager* PxTaskManager::createTaskManager(PxErrorCallback& errorCallback, PxCpuDispatcher* cpuDispatcher, PxGpuDispatcher* gpuDispatcher) +{ + return PX_NEW(PxTaskMgr)(errorCallback, cpuDispatcher, gpuDispatcher); +} + +PxTaskMgr::PxTaskMgr(PxErrorCallback& errorCallback, PxCpuDispatcher* cpuDispatcher, PxGpuDispatcher* gpuDispatcher) + : mErrorCallback (errorCallback) + , mCpuDispatcher( cpuDispatcher ) + , mGpuDispatcher( gpuDispatcher ) + , mPendingTasks( 0 ) + , mDepTable(PX_DEBUG_EXP("PxTaskDepTable")) + , mTaskTable(PX_DEBUG_EXP("PxTaskTable")) + , mStartDispatch(PX_DEBUG_EXP("StartDispatch")) +{ +} + +PxTaskMgr::~PxTaskMgr() +{ +} + +void PxTaskMgr::release() +{ + PX_DELETE(this); +} + +void PxTaskMgr::decrReference(PxLightCpuTask& lighttask) +{ + /* This does not need a lock! */ + if (!shdfnd::atomicDecrement(&lighttask.mRefCount)) + { + PX_ASSERT(mCpuDispatcher); + if (mCpuDispatcher) + { + mCpuDispatcher->submitTask(lighttask); + } + else + { + lighttask.release(); + } + } +} + +void PxTaskMgr::addReference(PxLightCpuTask& lighttask) +{ + /* This does not need a lock! */ + shdfnd::atomicIncrement(&lighttask.mRefCount); +} + +/* + * Called by the owner (Scene) at the start of every frame, before + * asking for tasks to be submitted. + */ +void PxTaskMgr::resetDependencies() +{ + PX_ASSERT( !mPendingTasks ); // only valid if you don't resubmit named tasks, this is true for the SDK + PX_ASSERT( mCpuDispatcher ); + mTaskTable.clear(); + mDepTable.clear(); + mName2IDmap.clear(); + mPendingTasks = 0; +} + +/* + * Called by the owner (Scene) to start simulating the task graph. + * Dispatch all tasks with refCount == 1 + */ +void PxTaskMgr::startSimulation() +{ + PX_ASSERT( mCpuDispatcher ); + + if( mGpuDispatcher ) + { + mGpuDispatcher->startSimulation(); + } + + /* Handle empty task graph */ + if( mPendingTasks == 0 ) + { + + return; + } + + bool gpuDispatch = false; + for( PxTaskID i = 0 ; i < mTaskTable.size() ; i++ ) + { + if( mTaskTable[ i ].mType == PxTaskType::TT_COMPLETED ) + { + continue; + } + if( !shdfnd::atomicDecrement( &mTaskTable[ i ].mRefCount ) ) + { + mStartDispatch.pushBack(i); + } + } + for( uint32_t i=0; ifinishGroup(); + } +} + +void PxTaskMgr::stopSimulation() +{ + if( mGpuDispatcher ) + { + mGpuDispatcher->stopSimulation(); + } +} + +PxTaskID PxTaskMgr::getNamedTask( const char *name ) +{ + const PxTaskNameToIDMap::Entry *ret; + { + LOCK(); + ret = mName2IDmap.find( name ); + } + if( ret ) + { + return ret->second; + } + else + { + // create named entry in task table, without a task + return submitNamedTask( NULL, name, PxTaskType::TT_NOT_PRESENT ); +} +} + +PxTask* PxTaskMgr::getTaskFromID( PxTaskID id ) +{ + LOCK(); // todo: reader lock necessary? + return mTaskTable[ id ].mTask; +} + + +/* If called at runtime, must be thread-safe */ +PxTaskID PxTaskMgr::submitNamedTask( PxTask *task, const char *name, PxTaskType::Enum type ) +{ + if( task ) + { + task->mTm = this; + task->submitted(); + } + + LOCK(); + + const PxTaskNameToIDMap::Entry *ret = mName2IDmap.find( name ); + if( ret ) + { + PxTaskID prereg = ret->second; + if( task ) + { + /* name was registered for us by a dependent task */ + PX_ASSERT( !mTaskTable[ prereg ].mTask ); + PX_ASSERT( mTaskTable[ prereg ].mType == PxTaskType::TT_NOT_PRESENT ); + mTaskTable[ prereg ].mTask = task; + mTaskTable[ prereg ].mType = type; + task->mTaskID = prereg; + } + return prereg; + } + else + { + shdfnd::atomicIncrement(&mPendingTasks); + PxTaskID id = static_cast(mTaskTable.size()); + mName2IDmap[ name ] = id; + if( task ) + { + task->mTaskID = id; + } + PxTaskTableRow r; + r.mTask = task; + r.mType = type; + mTaskTable.pushBack(r); + return id; + } +} + +/* + * Add an unnamed task to the task table + */ +PxTaskID PxTaskMgr::submitUnnamedTask( PxTask& task, PxTaskType::Enum type ) +{ + shdfnd::atomicIncrement(&mPendingTasks); + + task.mTm = this; + task.submitted(); + + LOCK(); + task.mTaskID = static_cast(mTaskTable.size()); + PxTaskTableRow r; + r.mTask = &task; + r.mType = type; + mTaskTable.pushBack(r); + return task.mTaskID; +} + + +/* Called by worker threads (or cooperating application threads) when a + * PxTask has completed. Propogate depdenencies, decrementing all + * referenced tasks' refCounts. If any of those reach zero, activate + * those tasks. + */ +void PxTaskMgr::taskCompleted( PxTask& task ) +{ + LOCK(); + if( resolveRow( task.mTaskID, false ) ) + { + mGpuDispatcher->finishGroup(); + } +} + +/* ================== Private Functions ======================= */ + +/* + * Add a dependency to force 'task' to complete before the + * referenced 'taskID' is allowed to be dispatched. + */ +void PxTaskMgr::finishBefore( PxTask& task, PxTaskID taskID ) +{ + LOCK(); + PX_ASSERT( mTaskTable[ taskID ].mType != PxTaskType::TT_COMPLETED ); + + mTaskTable[ task.mTaskID ].addDependency( mDepTable, taskID ); + shdfnd::atomicIncrement( &mTaskTable[ taskID ].mRefCount ); +} + + +/* + * Add a dependency to force 'task' to wait for the referenced 'taskID' + * to complete before it is allowed to be dispatched. + */ +void PxTaskMgr::startAfter( PxTask& task, PxTaskID taskID ) +{ + LOCK(); + PX_ASSERT( mTaskTable[ taskID ].mType != PxTaskType::TT_COMPLETED ); + + mTaskTable[ taskID ].addDependency( mDepTable, task.mTaskID ); + shdfnd::atomicIncrement( &mTaskTable[ task.mTaskID ].mRefCount ); +} + + +void PxTaskMgr::addReference( PxTaskID taskID ) +{ + LOCK(); + shdfnd::atomicIncrement( &mTaskTable[ taskID ].mRefCount ); +} + +/* + * Remove one reference count from a task. Intended for use by the + * GPU dispatcher, to remove reference counts when CUDA events are + * resolved. Must be done here to make it thread safe. + */ +void PxTaskMgr::decrReference( PxTaskID taskID ) +{ + LOCK(); + + if( !shdfnd::atomicDecrement( &mTaskTable[ taskID ].mRefCount ) ) + { + if( dispatchTask( taskID, false ) ) + { + mGpuDispatcher->finishGroup(); + } + } +} + +int32_t PxTaskMgr::getReference(PxTaskID taskID) const +{ + return mTaskTable[ taskID ].mRefCount; +} + +/* + * A task has completed, decrement all dependencies and submit tasks + * that are ready to run. Signal simulation end if ther are no more + * pending tasks. + */ +bool PxTaskMgr::resolveRow( PxTaskID taskID, bool gpuGroupStart ) +{ + int depRow = mTaskTable[ taskID ].mStartDep; + + uint32_t streamIndex = 0; + bool syncRequired = false; + if( mTaskTable[ taskID ].mTask ) + { + streamIndex = mTaskTable[ taskID ].mTask->mStreamIndex; + } + + while( depRow != EOL ) + { + PxTaskDepTableRow& row = mDepTable[ uint32_t(depRow) ]; + PxTaskTableRow& dtt = mTaskTable[ row.mTaskID ]; + + // pass stream index to (up to one) dependent GPU task + if( dtt.mTask && dtt.mType == PxTaskType::TT_GPU && streamIndex ) + { + if( dtt.mTask->mStreamIndex ) + { + PX_ASSERT( dtt.mTask->mStreamIndex != streamIndex ); + dtt.mTask->mPreSyncRequired = true; + } + else if( syncRequired ) + { + dtt.mTask->mPreSyncRequired = true; + } + else + { + dtt.mTask->mStreamIndex = streamIndex; + /* only one forward task gets to use this stream */ + syncRequired = true; + } + } + + if( !shdfnd::atomicDecrement( &dtt.mRefCount ) ) + { + gpuGroupStart |= dispatchTask( row.mTaskID, gpuGroupStart ); + } + + depRow = row.mNextDep; + } + + shdfnd::atomicDecrement( &mPendingTasks ); + return gpuGroupStart; +} + +/* + * Submit a ready task to its appropriate dispatcher. + */ +bool PxTaskMgr::dispatchTask( PxTaskID taskID, bool gpuGroupStart ) +{ + LOCK(); // todo: reader lock necessary? + PxTaskTableRow& tt = mTaskTable[ taskID ]; + + // prevent re-submission + if( tt.mType == PxTaskType::TT_COMPLETED ) + { + mErrorCallback.reportError(PxErrorCode::eDEBUG_WARNING, "PxTask dispatched twice", __FILE__, __LINE__); + return false; + } + + switch ( tt.mType ) + { + case PxTaskType::TT_CPU: + mCpuDispatcher->submitTask( *tt.mTask ); + break; + + case PxTaskType::TT_GPU: +#if PX_WINDOWS_FAMILY + if( mGpuDispatcher ) + { + if( !gpuGroupStart ) + { + mGpuDispatcher->startGroup(); + } + mGpuDispatcher->submitTask( *tt.mTask ); + gpuGroupStart = true; + } + else +#endif + { + mErrorCallback.reportError(PxErrorCode::eDEBUG_WARNING, "No GPU dispatcher", __FILE__, __LINE__); + } + break; + + case PxTaskType::TT_NOT_PRESENT: + /* No task registered with this taskID, resolve its dependencies */ + PX_ASSERT(!tt.mTask); + //shdfnd::getFoundation().error(PX_INFO, "unregistered task resolved"); + gpuGroupStart |= resolveRow( taskID, gpuGroupStart ); + break; + case PxTaskType::TT_COMPLETED: + default: + mErrorCallback.reportError(PxErrorCode::eDEBUG_WARNING, "Unknown task type", __FILE__, __LINE__); + gpuGroupStart |= resolveRow( taskID, gpuGroupStart ); + break; + } + + tt.mType = PxTaskType::TT_COMPLETED; + return gpuGroupStart; +} + +}// end physx namespace diff --git a/src/PhysX/physx/tools/binarymetadata/android/android.metaData b/src/PhysX/physx/tools/binarymetadata/android/android.metaData new file mode 100644 index 0000000000000000000000000000000000000000..32e15b966c5b4ca5758c6cc338bd1e66da83675d GIT binary patch literal 44148 zcmd6w51eI3Rp;;I4@L+uAWDRQ-8h7SjBHO5!#EDuGt)Ck!_4Gmx@R&maWilFy_tT= z`eQ)Ib?e^uIyty+ad(zw7m%FjWcQ)D!&4kLhujMtnf$N{ zk0-l|KNa0|iu#vtz_-J9RMdY7>=8soE{@7=3g@c7FPzI}%MV$Z z_%$T4GCmKxsD!_ad~1I*=O)(9W8J>=A10~qDf<`6|4O#+rpUfaI5#o>X?&X@Q+>32 zD<|F|iKX}o`PTnSIX5vq^3N+mrpC|mt(^3K4@oSg|9iC0N>o`GTi!SHY?xkxP+5ug^SpJE35VQxo|O=Wc4_8vW<-Gq{ne3 zmf|la^Q%g@+LwLCt({|2zJK$9Q{DAfM~Stg__#h?l*QKqyez(sY@lE9)eZV%|HuZ3 z_-VLU8Gkuk{w(AF5ng8BIhe+vDAFalX9NC|@YMJ#)+DO`vn1(Djj!5&7fIur;=ci_ z(y#mLM}q!T|34PhPpyv{-%n9Sk{VyFpPz+`X+DIaoO$`DNWaCjh?SFXpAhC+Y@d}C z{~CCif5e{+U+15oQ{4O`9j2$ox$J!o<)%3nMdc(f3VQt2_|y3HaMM3C#(Dpw|JI;G zb*$f3RQ{KL#pM4Y?^E}?NK>%5zWGP?-9_3G>K5yhoxKMBT#l*r?I&SxCvAnN^lN>2 zH+&mhx{LZh0Fw`jB3+V)5={BVKL;zGMUdN z$-gQ8Tn^hoD)XP>Z#P{2OV!u>xE`+9(U{7{SZ;#JM@2FJjo{`#*=S;VHuB#OQvV*3 z^rh_6`ua0)*%otaNBkXd&Hpm~t8mS=6xaBC0RA+%Y&N?&%|+v*XAp|HJyK{{3g_%7;afF3FQPG5=bg^3BeT{QDd#Nk>sE>k3XxzuBy^U#foj z{~B~ipOV$%)X5<-)kaY)>lRL|eT|`s>Dj1%8?`UKLK|iAvj&q5MX{{caANi;hD^Rx z`*i=h4;|8{{#iZK^EQCmD2iqM8Yk9%YJDp5Tz@zb*BGpmh$}|&dR8#v^3l2_AmZx7 zx)6@I*64L6L|kjkh1RYeBd$53`|E@R{t*;7r>%~Hn1^yvBa zvS6RqD9g8UiqCCuu~dAj|2yGZ;qsByx3yvpc7(L8gscCj;9EFO^xqVa--lnuQMye( zr@5@b^$sV+HU4+QXW;U2vHp+1%s;Z<#LCG&J>N(s?DJV}{C*rAVkxfvn|}GwB-Z~^ zl#!(BtNov^(64&G443Unrq|pzO`hUM*D>ePiTHnmYiv_o{&|pOv0KzHC4T}}?8o)3 z{ZGTJ|Ec}E{PWKwNh<#I{PzX8SW3U@Nx%7Dc1m=#^O!prF&lsLkJX9mU$|~@K{v)& zS?M?b74<)vy5h=zl|hcSj)?0SOcHbRv$&q2WM7JFem{dUvSpTJ^*D7h&F2<)jUy(n z;~Jl>a6Ko*+{RDq*NfoC3Y<$P-^=HV;Wu;KLMpbuU%E+au#|pVKVKB;noo69()uI+ zT^%?FCss}u;#YB044CNshsl@1H`4!Tc@1OKf0Nk0viMSYn?D*Kn?K5*A;|~Y->ZL| zW(|D=vG@|V_=>rWleooKiYtEVjGx+4yH+nWc8S^mLsb2 zajwmtw;`u^WqomHe@g#xufK|KR9{>o`z<~!PK*7s@hx&|-^RC$+xUJtT(+71RDE$< zKV@%fd@p5e)jyT(>m$gfa8&$XrkAIP##1_C5x*QRR>o)GQx*K1;WHI{Cw#Voe;d43 z!sVYi`0)}x53~NOjEVJM-+x_48HxI5`7VPG@o$HVmGLHgs)7&TGbLR1zY;#nF&;lI zorvEJxA-gCC;nq_i@y|q6Wrn_#eWfALr+=zTK^VHxZ>+qNoDJg#h>Y4LytthTqC`Y zx;8!^;o5}TjuUa+gCsGxerS9@O6tRplPsT8Cw~^{*BWMG<)+Ed{#TOfzt+D`qeCp_ z@%sAr@QVwaOD9@i{{^o8rs|7dfHGSb%&u7fQ@GG|vOd84CmT#mkJjhQf->!YEZ@rM z`|llau@qPRdAQn6an-*GPKYGA>bK$iOLEo!ZlJ!$PwD?5nDtLSG_ihM0=MT=_22TX z+%zY8ekdECH*TO`i$KN$3<##i=zj3lQ1TRqdG z{{0oa7(X$87+%~z8CU=Q6+Xo=)j#oz`Eqev3D^7eZ-C1WvP~%#@fX6sh0HRpo6}B` z#h|ON3&h6X`e!kqqtdsa+v4M0su0B14{=*RVs3V7eUSef)VqXa`KCwXH;;}x$cM86P^Hlxk!K%hb-w&Ao8N6n>Fd~1AmVdH@k`^EMCsaC?}zn4;fv4S6gA1mR~e;j^u3Ag?4 z@dCH;v-xTEsh<)Zl{)B7iN;ED#|He>8}J{2+xTgHHQ5wS?t{NI=!oOP>{tKa4i}5L z>5=`vLsEI^G`+@k{3x7ZF6Ezq^DoK&0Z?VXxcVqb_3x3W{){i?KFv;X>5!!Olb(=e z&m?KArbt$gQzznP-&Dxc!OH3PQ`3}@XpOOam%+z2nD)2Eid^MhOqpkK6f1Jg-2?Dv zb4<2tLKpZ1g4kr{>qo;k!7d;zRfE*TH8wYJ5yD zr%qJ=K6u&uR{dXq*Ep8de=q!Ic&dHr|0w);1^*j(qk=yIU#{R!#ISY=m;IN*`y9*c zzZ`I9NqyP>BKTSbpNGGyf**t5Rl%3xcUSNc{9Fm2CjURcUt7W72!BHfSN#4W{2q>} z_!a*(_>Y(H_rny!MNuqBG`~_}{12#a^W%Z2pv9@Utq&=#_HBK*m-<%ERDA;eccizG zp*D@%`uYW!d{`8-Z}StY@6S_kvwtd5V(ln?zKJrDxP71FfB*GjxL6rK0GGWfF8j3q zpc~2YQ#;2f^CXV4$?Q_TNgJ+lp5O`wWCVLTX*1_>`{uuC@?Q9<96vU1g^QK(UxJtUNBVymF8`$JzZYiq9jAFd+;KxbD`tr}s)L-M6@{jm3{8bhFEc~t# z{u-EkR21v~eQ@jlY*fhXRDJCaBq{$&_b*XKY=Y|o(f#K);c8DYXnHwyBL4eu`7Xu( z0`_@QF@9|RosR)eCrwd5_Mh=7*o#P8O88#bjijw5e46|Qd|QEY=|tnxh0kz|+xOJ3 zZ)VRhL=4by&0zc@vcIB>)-uw8$b2Y#QgjrcuHi8ME!dxlu6kq{&~1q8Q;v{ z$(|IKYV8j-wiEHewM72gN_~xgs=m1P7iH_Kxb_#9RMgl0q8L9EcOw7j{xQXse5Mr3 zE(|mOZi|4GI|#3Oe$n`7{;O|WNLJ5OO>?5(mlosC_;*C@+n9@)9`%1H=OJbOI|CO> z@jD~^L5wnD#`tPaGFZ=F;m&J$PkG&Pn5GDQp9+>%m#*7MOW$4G?DN$b~|GEL! z{cZD8T+!tHP5hZ~#g9^Cf~%zQc`jV@cZL*}O2_me|89dXbAFsu`yu!goPX==SA6^^e7b~FCHqfywe`*!{AF01s!5_zu-;P!AuZQ1U!LNiLui)3f8zo%h zb0d72Bma`)cM{$z;qucEerpMrf4&RehL`#8_3(ZL|1a=S1%E4iwSxZ&{LTvge)w7i z|3mnzO88&EEIwaL852AGJiO}uqWHD@i{jVnnI7GLp2&|M?*_#FvwO)iVJ{+`E8)9g z)21vsN|)q1xY@7xH!(YIgf~c%RQu9>8(b{rDla(;dpW6i|1kT%A6C`AxNMZ9?0ek? z^~I&5to~0$^=ExC_i6nXmk!CS`}7~i_5SaExYm{#lI2^`_eT0RN6=2iO@E5({lN!< z{s$8Jb$|PP_(wRV?r&P({tEtCj`E+i%c&FbFTi#GNpa2Jr?7dufQqT{orYfqzo>%m zgkN03Uk0=BzZYF56n7$S<9~0+)4{l&|Bg~d{y!JxtBmA3BK^xg%Y9mXanqmTdcNvV zM!uOPSv{wO5Bcvb`~b(4|HSWtA1mQ{K6?Ya0Z;9(?}M3tW+`K0c8Ht(vj#aD--Tne ze#o|MQN9(m@!dg2YJ62r~%0KA1S@yS10nhHQ`Rx3&&P?a!!Q5Nlud zEs-QsB8nzL`3W6D11*Y|f@;3@l5?>5S8EpRTKh~ExZY{&II zWq!is-zhR=pOvdgA@ZGp>)d>zbCs3I{_mlTB-U?b<-hMEiKY0DM*3%^B#3+=5x4I@ zXI+l}u=d6E{iotT#r6A(`zRxOQtj*g?pxtga3zXQ$o(*jzu5?yed4)&5w}xu`Ow*C znNd3%+4o!2{X>%GkIj+Tz-cc25^m!bHQ|cs{`ncWVpsRiBDeU~{I&R&pImv48sCSh zUzBqp<6mIrpMF%x{3CAuiMiP!ZvIK}$J#&Izg+lrzQ42BZ+2N%9<6_#itd!i$CBs4 z&3_;9S?<&PCvN_Wx!Eah@t@)w`S0IRe;28^KHL1&{zChQDXhAeWbJb5#OD74T&3pM z<>c>0|N9D@OD7w}*G<$l|J-SnIGTUN%|9_WJJr8sQrY|$?~^n?Q}%2Bqy5ch_%g}# za_U6u`zrNUxe`n9SHRE0WV6}rHZxqm23FNS@v8p)1biAjMX~tWX#7=Q_s?SgO`rBp zzd{-Hf0k72--EDEk!rB11b+nfgs1!OXH)*w_+JViaV+Ci>yP@rmHP63T%SuPyI|(u zwa9oI2XXUn%&n-ntxqYwk$)fS^;_|A9X8HlaNK{Jzeiv#(p(8w|IU!EE#Vs9m%|Uh zr|>9;37O{V)$leTZr=)tzZTvv;g5EGRsU>!-$5HDHooFEzA?9=+F!qkGV<5GWLm!I zk$>J!`WZmnKl88n1EilT;hJ9`hQF=AO~3qW_D@+RN3%ovZGWjbC>tc!zSbY@FU3^H z>Zy!G^Go|*UZW(}AL-Zr5;r9KH%-3wm)d*8{pZq&xb~NNc1UsQSO3gDD|c+Gudn%G z{Z|_%)(`#nY5oKq67|{gT?QZGpMZ;{_%!+d05{(i>puc}8V#5AU+b6Ir#+U5#h19n zSIn&)Jzrghe%YqJTfXU$ecRz%IHvsft+2hMtpzT95^>pQ-1HoSsn7a;(d?Ig6SMzi zp}z8K205CYE1VypjCAcF=@~p0{r7QhgiBP$>bVR)PQuj3qL?jrz|B6@H!(eW{(iLI z&wL+k$X_Y{%0ByjMT);RvQIH$65FTWZ%9)0W&c~yAy&rKzcTw&U+>S$?$6@yM!$TQ z(*ME8e#M`O*)Og>N>W_<{}f)FAFcu);(rO3id6fb0oeF%u}U1ReQ|3)#ijp?l&Na} zqOUJ+U-h2~w{^$bHB-bdg->xz*?$?#>{lO6OpmzPpW@R0EtDy<|Jx${DgTR`{uGz~ zqZRtaZ-&dZRR2zF(7yO%+5R0-`-*WBTc5;jeT}*Kf1~}^E2+;bxOF05NM0Ycf6iyQ zPtz~1HByq|^6#4|Q{$NOulW7&#R~r0@M9%h{{JZHX1K=R?Bdjk^#3V*m19}^{|vvg zgv&oqc}AA4!L_y*^nUU#1kvmWp-1U(x>L3zS(7-1KtkMEi@!^G9rJT&30*@u$P@so-1S z?<(QCe{F~VK?&FTwhR7n3D^Gc8u-N+zJ)SH`}O>ABYXyM9A5nWj_x0~z|X-`{nPi4 z9k`y~b&o3Q7k>r(T_t=P{v+`B!8NAE`r>bdzrTXN3I3rHuKE8~_(v+Z?*D&K!FB)t z;}Wj-2fvE`Kjp~3b@q$@F8t3b_+P^Ryn_D&{NpA3dCyH;yXP;wg{>Xmae@5`s{byfff9n1tZt)Xy zvtQieC&mA*e);DilJuwg_t%kr-QP{j zPQ5=kPk;Q0#P!XW)8tT&8sybj;ZRT=NY_muEy@?Qa;bF+k=$<#J9l3%J|jrviYO>`{9b+viddn<_dl@{Am^ZR`?|q z{FQK~acTRng-=!RH^aA-@M-cjzFRq_#`j&+-N^r+lw*)G|9lV~Vr5+aeZ?yO|99$F z`TvjLRsR1ZyvqNdgID?gqHn17|5M>p75%#uzNLacA1?ow`A6fkt;lsVL9V2(jnAyh z@gMR0*hd*jnf@AFtc>3TuNt2@310>lHi{{V#lxGhBid^G&J$y|H zNk#q=vX6-~-quI5auGj0#U7@DpMyVC!G9LMnZBj;%fG({pM`Jcyl9{JhvBsnuD_rC z1pGLBHd|kxtnBZnvg}^CbQkq2J~Te}!4+d1ywD%X7pKPK!)Y!yKCq{ZKwFP@T&N~30@Wdr{U(iqJEI;pVp^T|GtL;7Jt%VVtQ2n9(^FG{t4a3M^*oR z4qnzj)tCR*^)LAUehQd^xpxWt>CYR&sFeu z!mq918ovWYejXW!t#4DD%l<9lXk%&dw=LwGpNxN)wr%_$*hHZqcB=mWPs&I(lhj5m z;{OXSR>m*-Mjsz#{8{jsiuya?vn8DDtOh@>BStI5BKunK8pl-o1K8c9w^2`hv+5RK z_rl&u(r>R){5N5LOw#++6xZ{`r{M45$iG}ar(U*<{Xeoijw&nB{oxYINaQ265sUaY z!o^Zt{eM3EILB1~W#4vqA1>WWvAhIk@$p#McLQx4r_ui#`BSCk|eS} z&v!Za7xw)mu`>P>@K2ZUMe^SXzZu1{zo=jFW&T@>;Q5r5i0l23B<5CB{nPiq7r@m& z%Xby{(D%pseQzJ2GR7@__3ZXObc{;)G6iVN!>iig4OiQ#`m$g5uXXoN zHHe7o{$;Vd$>oke#MM8BD7im~*I%`7?bLz_^S_w+U*|J{>-~?$M|pAmWB;GI`OoYx zw*Of1cz(tGH~%UAEq>1hpXgxoPuzZ=6LT9!?T-$G_>fM^H&u$yn@H;KRD$0M)88ko z!epP-mp>$^&(2cjPL44bGr1eS#)axyePcT6`Rf&drTEWK|CMkpGhwO8C;HFB%s=Kc ziskL-{_y~1B(tQ=I*DYO^AEyjIi~CvSNzX$RBV{vZK3&N>dJOZP35k)*~?{nPsiv6TJdZR%^CFXO9ljei+`CH&?J{-5B-EBLR&8x>sd zAC@clXQYU-N(n|+r1jO~9ZWh5#46km&Qu@sm6C*ZbznO^BLk$txANr#hQ-|5J{ zJSZt(der|{2m97IpCZK~{yO*?N2MuJEI$$H-yGT$H~pF)_Xqv`DBpBw{ND=~)A(CG z26ZEzbO zYnN;%;yd8#TUq;i;ktjP_%!)ie=jNF(y#bf_x?AG)j@Qde`JG+*{S+%%1Bb}i=T#z zmGM`?FRI`_059{;8)0VOY$CqI6@!u#*Z9AaGK$S9%2>ZtUZVcLn=}JY#n*!{Ykwxc zd5rHP;`)w45_4-u_I;f6FkJRozN^5;-$wfN4$Z_)#Z7ujo%tv@s#2gAByc1|KZ5KsSxuN&F7K0tuHCA{@MDX{#n1I zL-LkLe>{I}{)ku2Z?$jpTkRM1Yu!ID)Bok{eE0tfxxoDw|2Jj&@0wqj>96M>m+5cB zzsmik^hNH!2k_OI{_6Mfnfu#P_ZPGmXZky{CuaI6arHY|`&bFWhuK z=jUzGZ+$jx(l64pP5RyPrt`G=oqwLzsO&tg1m~Tn{=A1&TRpNPTUxFUIGf+0%k5WZ z!{y#!qzul+S4hyH{7%(dN>XJhYt6?(i2^WoJ0Ke#OB(y_Ifb zcxBdUbz7a4PS&Zfx?^p1aZv9LPxc0#>~^*kwbg_5(Un(bOTG3=r#lml+r#mSY;R}h z&V_z`)T*~>Z`2#?&qlp|)~`2avJ<`0sMpc?_H1{%-CL@|vxVh)qjzRcYjm*Q&pPho zhTfpD7lTGwXI!pRt2Y|0?x`#kt&IkY^%L!8cDmPUWSs>N7ad>kM6cJ*I@g`)4i7dv zy}{Y6vuEYx$>!k5u-TvwwRXMR%(`sBYpZ*E-Qj3Z$8Gc7(+BIr;lffKMeSC%Ss%<~ z_3o*awoZJ;cFWjK)jjoLbAP*jYRLKG*?u!*$%yXKpgC%~%+9#j;pRzapR>DTS5!OL z94`BIhpzQOZ-ftf-3)Ix2RL)S+h{E{hYO>@%F>82c2`+ik@qly-ss9IID(t z?%W3_6KhU4T@!}A(cbgxY1Rpy86(EmbYvZD^$KWHBeH+c>-Y>`;o6mUd#*mJXUleS zs=uxM6?ZcSj5 zchR-Y)~V&u{#LWyFh=uHZniuw5zhT+4ti(&iJve2JSyznR-(xnJ<*P}o?GSu*>bUI z^4eR!&|hv2nt7o?z0q1xmkaqJ8tVPwN_$*-x!ykM&7;QN0`yh(^zyLFRD9p+sM#H< zg_vBkvU8`|aHOlyp6}xRZoPfoiCbAIhJJ_+d+R!;M6p!swa=dFb$tOpbvs9yJUg<^ zp%X`&1KR2wY;~Cl%~e*6-Fx;h12LgFxbCDTbk+$PhO8Z@e2gBhpYi(E=Nvk*uW_nL ztwSf&x*E{b({-+gY3Gg4j0WW`!oInijHR_TR$r{7k^{N&Jy-Hls= z#lB)ooXq+X278iQPJ1hW<|4T ze_d-cR!>}bZ(3(<5@F!j2T>+NG^lfZ$~U9I-Pa$s+6--24p`0o46{Y5L%Yqx&5l<3 z(CH=Tfol%$xn(qH<>kWlx-uun^;Klg(JQZ595kERVSU!>r?>&Mx&%S1OW#`EJ9Ho8 zvcAe?ef5s4KN<{uVcg_yc?3>=TBj&Cfw@+laO2L|u`@Ss2gDIAEVS;Rl|w6?!@b@} z8H$XR&VlCXW}DMRR_^wpUPIaLJQVQBVVI&)693gFrPx5JjYK8x}_r9*A-FDM3F1j6;VzovgfGO z)apyOH5=Dw2e_NJy+>{GWoE&_`l)Vfw9;s1dyZb^hRcPWn?f4UtTR#j(23x_d1|Q* zQ+VBg!P?tvo;;bWCacc<&1G?KAB61!H~CKP7T@al4#IE;GbYwnKS{&tt;MS~+TCsJ zIXl9>aTqsOXC^PT4~W)pzo1WMv3IJE!reG3>bk}aVi-10!;FgqtxjvC41dx+vDa<1 zhS@MICjLgO6>4#LKmhgH4R(0igJ_TD_Hs7s-1aD}ioSOX%rS;Rt7TZ!nWDjba(j)| z$+La~Ra@Qdc5h*KGuLA_)w@g0YX-d)?E?3R;2oH>r{)qAgXn>pk2|DyR5Aa$BYdZB~J7j zXRl!Ih)3A^s^T7U{D^es8zWs^;hN5dz4qzmV4l^nV( zTB{DY{b!WtTbd&bvxTfbj{gw8xnGM^`W`AtKNtVt# zqG&o(zl|=k`)&?`pKge9*iw4faPyZQaW5ZgCn~2+%-B(-f0<(gKo3QG4`(p zHFq{4qXBOEIX`iwEo0{dE+Iz88(Rx>uh*{Jz%|Zx>z&rp{z1zbm!^6u=rqAu}^9uKQB_TOv zztqRIgerH=@KALs?AW!#R(ynQ1luze4#69ryN4QEkQ!|3{qv3GxJ}TwA(p}B;A~`` zCMFLw>Uo?i2iOi~u{Rik>uuYNv0UVNwfbmjIRLkjANq$UZz+)$u3b0nnQi=p(AnMZ zvvf4}_qg4$5cn&!-@Xza4D$Nju6uxVPgCJEZm>4!Y2V0AIh@3B!Oid5pvgufI**GS zZ4FoIZFe(|PBn!74(})6o;@LZo>M-yo51TBJdXIUV%g$!PIj$wI? zvb^tFVM{qTXq|56OVcoGvY*yIxtPx)5xUM`MdgRhe%&u?i@i1r94l59x8@9pGg4Oi z+fTc9CR^s&XI&?pEljG`$&=kCJM2bSmaxe?t2NxcwA57dZjKe5@X{W=l_7dIp0F+_ zbFINBEZ&peI@t3|4!vXruJur`wA^Yp+*w=qPwbHohuNq;IMp20!trP}Xe#ngH{DAQ z&3kEzm-Aa?SC|Kl-pTm}`!Cm=mSks|t;ku!?92LW(>mx~a!zCK;k3A0T4}${wBE4g z9ONy{l@HO$qG=(ahvzms7)H{fA<7z_p{#2=EcA0NmOB;Z^AtYf{lP*$RO39md5r60 z`{)dnts~Bjt|Q~@L>t#H$buWyoIm~c@hNX@+{L2wUaMGADR*^%=*lltE)L2(4%X)? zUORDF7oL+r;;Osga{F}GhlQmRI)7{W&i=KU~m#USZ@K^be4JsqsD54F>bv!R3q>F1P50m6+=8-C4e9+htg_J9`Gb z+nQZz4xWjYwsGZnz#=Q+Y&U3G#~++U`390f7@v10F7kYKquS2$kge?{Gk2lc=6zec ztv&CdKE1O$N*JpsZ}(tO*T}e@Qq$cRc;8WX+qy%2KiZu`^$sq}HThbH`nDa&R(J;S z+m}4+P~SzvoZpaOhjaMBdY4B>9w{fU!fssw!s_bQfKcC}DGsee{rPaua9X@qLj$%2 z4u*%rzSX7n%8(V1M;n*rcl*u}`EWbHV~#s+=aG2m)?z*W=I{IV=mjfoiHCY}25M@B zsn%iV*?!^Vl%1?gtlt)fV0~G^p&GNN2~6?|iy3}+JltDfT5j^4Z#>%;M$;W`=Rcnyj zu!Sw~?ONq!%6ubRxP9l&+F+&2gk~k;E$|BaNH#ykoXAW0OkiX*CBl#nGiRP_W9f<$ z+-0Ko-nG@at3B2&)Q8k$o*p!pn(h`skh!w%f^IT9*sBbA5?kT+Gk(Na@_XC-z7$tu zGBvZatZTd(zQZ44TswTUmo>h)^1>N;d0oA}_eP$sx83G4n3{uHy*0>I z`hBJ$8y!AFU5wBx|IjJQgnW*No2PjmA2ppS@0^J&=Tmpr%vWi5AtW9rso^ z&vX5m%)$#cj#0IVEFZnCu4QWTl%J3CO{rx$HBx)E-`a&$!{ShHfEr;(<6YQxK62;B za9}>5H^vw2LGrCkHs0c7eIHP6%My)<-!kGezaMj>2e+vJ9kqw49F zYX%dX>SosA?UeP`x2l{&OWWgrl}$8cWFHlfj= zOy;>!nas-ew^o~eopW@2iN*!@tm|);tUv66uD&Xp$n?*e-JLtRo3&2z1i6o0)7h-& z--M6!jo1Q|b&ppbys+nM2<=Ll&C1@lI+@)&ajRccU7c`a%WG-hHON)m*6MmCb_2}I z%{R0L9BnSQmfFoZtzq1GeL&(_ZR&ROEY&;3>v!+(-7BM>rXyE69O?7aIMmmK?sBeo zrkiK%T^Z<+L&bO#>z*zA`M&NchH=<$S~a4J$K=J9b_;5NZ$jcMZFVP(9ADnB+;lIJ zCv49PFRai?SNAmErv>+*zzw0ub{6P)K9y2d%pMilT;Dg%cY}iyjy*0_4RgN{`r!$C#t*3 ztJ_&aR)%`-Tw3G`UqtVsL*)A?x{61o5GQ%u5a$+R@(P!VDaHAj`k=8$XPa&Id%Rj9 zTmp5sFW%ME#*fDkRZkPwfW9XfH2uSLD6Qx^(5vgI+VxEuqP&IM<5yBnN!T9im2>Es z>PSail1R^|MR7@pgK>GUD9lnQm^YXLu@~DB^Og zKLk}+|yf8Su>o_~G3_#GkvxpdoExuRIz-^hZ? z*Y#_>e7Ls5Wv(m?d@)uvXnlxe14^yr0)^L0?|{hp7A z51YxT8D5oNC$!=hc2{n?a_Z~nlW|fVBdKqX(5CMYG`9|Mztvu%>kIO2Kd%jKCw-Xr zoILE>cP4sFo(bP29q9FLTj~4XDY=zc&m1&==!ClEFZAsJyD+_Sh>GvZyXrR=D!+4Q z^Wg4dw;bAY%f3U`%pcl!%i?v1uRpTymc2&~AKgdZ;^Fyy3ojY_b}x@AOUt~7T^ZDu z&Q5%F62^I~TQ^g^glCU)TF;fd_Kxns*LCZhktu>&@rJm>xGST ztD?9)#)o7>q9q; J&2#x={2xrOFdP5? literal 0 HcmV?d00001 diff --git a/src/PhysX/physx/tools/binarymetadata/ios/ios64.metaData b/src/PhysX/physx/tools/binarymetadata/ios/ios64.metaData new file mode 100644 index 0000000000000000000000000000000000000000..d2214a55b586efe4c8d4a01e13b78c7220200e69 GIT binary patch literal 52514 zcmdU&3!G$EUFYu%2?mHbK!C^sR)>fqM(jzlFpdysdLEgEc~qt+lL4Zcn(mtEO6t+6 zs-Ee|Mrl_9ao5$r%6_5-odsOkh!PebQDhAQE>Ra z^|@UwWU-|_fA9Z){{Qno=bn43G927{bXQRn7vU4iCHT8**9)$#;P|9LYf)Sr zq-*~edAvlExGxj;)U~QYi}n4h4RTZBzG{NLuh}5CP2Bbg`mU4wA5n~6>I#mVRJXpp ziHts4?q+k>tAWgV+^jf`SB`tBwESIlFgBp4z?A zN^rDZ>-!IhET_-h>&2D%Ywiu=7~`D2ACf%nUqx*^jQ8)sM3&R%eEwN+WODiJ{=HjV zS^TW;{o*)gIeot>dD(&2u>58`6sfrhEH^-w zi;M4XUn!0ZyPjNzv!b|44cm0&{gc%)?!^*3p@eh)?1wqqKVFG`%}S3ssLT56JiQ_5 zkjIYKxHwPe#Fgd!jT^LUeeJ|&gkM+3Mh%58s9?3n_`7XPj3b5zmh2 z^2+z4PlzMKd6`_sON&os?B?dL$Lk4NM|dq`Xnm}+n0t!2vN)T2wzxHMR{K;`TU?>@ zHF>3W>r3;1wr#(PI{n%@rdR3*2j}05#SH{w6>~R-VwcZu7IHZtbOq~%_5Uya7x{o}t%{-BQ2;Bt1oKEGQWp9gYw zKOni|nD&Kr$N3}T#^pXY_Ze~NcyR1p?DwNR9`2vxT!F!yzwU?QJcWi_oS&+N>q;Ft zZn-#bll(dz%f`d=<9cx%kDNZQ2RDh^36Ar|aYHqfn&ghR+kNzS+#xO<588II-i?mO z_ekFxb)*kDKYV}tF>&;PeX>2eP|8%+W!M^j!;>d7JtY-kK z^5x>5ET)XRLflhIIPZs2KlUfatk>_au1fsiJ;ZWf;(2tnI5N3;AmoEJrnzDIK3DZV~5<%Q)ve zc)qwLFqb48>nvXHUo36~Z8?2+1-dA9i@Ou&SK>2n_kiRt*YP}tuJa~WK6IUtWoG+{5 zwt}Od*2g-F*R_8qj&aCw| zxLx1h9u#*1OwR5U+F5>4+#>4hagT^wD&ZWz|0Qm90{0i~E>v6VdKQxQ*%~=VH5z z1LIu5C-{qFE-oj~p5w;*x41!W>7T*j(lznR=_5COKUzT>{n$hy3hOLaD4!e`wo#{x zdC%Xg#8b|AF?MM&w@nBe;C%l$TEaQ+-y?mzA8llQ*zPKL7LLs-KEEVAX&$75>wSbd?-Ow8ND??7 zoL3)~-t)jtjG{Ozia*NOaMl(@@F^!dK^_u?ug z`pi9^#!$B6v!30ji(~wA+_NQ5$Aj-TTpSnQ2d@%OdG2}IzjM1zoS*Zo$9k>rusFte zJ$*HC+$Q8W>+6c+A;-CYuM)TBdro!m^^&J?VGLdDkK>oVN6Nh)avu13noKUQ-n2ox z&cC;bXTNfGx2wZHC9cfwFJ|oS*R?#@U+3Qk6T7Th?q>UOK@B`Ge&$dO#c}yFab@$v z+^5BD#nrj^IWEQHqW-IJ$i>Cnlf^MkInLjkexM??N@IJy^x}K(-{qZ{K@jVY6)A(49YADW^!?OP{TKD5K z&K=M6kK^WI-t)34p7PxM?Mc3>qZ#Y5UUT0qj`tNZWt^`YKf13!AU$4Z%DA_Sn;Hjn zZhgNjE*&?<&&B;5f4-!gxnQ|@+xuPVb-t8w7sUB_XPoo?^kd>GFvPx>aepE1$_dDEqma^WHJvFa6Fh(q^ z8{}TQLGC_r?jLQqZ>-y&`^CK@(VO$v@q17lncVo=--pHd9%y^Cot6)at7{(lyU8~oaZc8OYY|~Gvi#O@xEO=f3HGjobx($MBKB%Ew-cOmHK*@>aR(;o`w4N?3HshA z?&cDGUjIHU?!*M{FT^cO;4Y?y;$pi`5!Wo?9KS2XErDazf=lwO}iBN~-zQo*a zab(uxj)*I}|Jo1lPZ^$EzS#aZOJCW2)m&E`<3k??eo#5e9g^Rr;}*2##$zhbMe$m3 zWzQAoB~Rl_oi4U-eLpIm@?8APy+d4CoX!2TxUx8#`#EupbI$JjBv1WVWhYfO>FE30 zuZw5%Gx4!p*x+|_9~QS2f@+3yUj2zU>dN)+uWXMAfKtJSHplIvDU?gaP2nuZdHy^{ z+!f$*`8Ti|)vo!=zOrnSo-}@(>n`r6^W_`FQ_g&`+`PF1;>hGUkDJ$_3b<8##`QVQ zC#CN^m|UFAwZ*-80{2RBZz85VQDEskT6<31+&Kj>Hims?*Rrw%?#N6wEN_gu+etmAYEH!Jxq zI&LrFrqIrEtGJ!0ua^gXaeF3kuNJp|0{6Y*4wP_jlibgFRdBhuyh~h~FIR$f@%a6^ zIJb`<={t+#@{xqk#l_q|h$FKe_cSI73Vq0NHs|LQ=F~`@@?sRv%jZiU4>^72yzlhy zxg3w6EYatEXW9MD`n>NfTb~_g-_I+sL7U@tXVm|6+@`^~n4c5pJfM8NJn;HQ=1P35 zhu5)8#Y5r$?&&I==i)b?=^u5vSg+%9PCVr~edfMJ9GM*VZ!>li)dPR5@7_e1(`W9D z6YQG1UtC$9`aSI|vlOBsvx9TUg@o;sH=QxPw26?bU~C#*OlZVNd2Fz!bo`M2o!v=VNc6uwH_GfV6` zUtTY+0uKLMpat#~Z{gP2k=lu0Mf$hq#pq z+(Y8-oWT8>xYY^V?}~d>3HLF{(|L3rtc$DvAa2rq-}5cK?{mIckM(+=_!Rz!Gbr}} z;62V9>nu}}zgWk6OStPLpNf~Q`Rn`9jp9;&x!$|j{w?Be)sgaS|8(K);>hIk>Xnk; zr6bptoW6S`pVU8d_J`fb`SJZ5)Mw6m%k;f9qpwO>#ql#|y%gs-fB*3=>8*g9#>aY4 z4ds0qyOjiT)m&;f$N7EY2NJsv!IJevt3}~`$#09h06}hF;`_p%iTfvbt{)S>bZl}1i`}qx$r}_7=Z14c}p_ohaFUR@)%gxeT z!PO7oV?99`{>*3W-eY0Hn@jEHIKQ{(NiXNd@kEbRdpuq#ZV7_ic$oVxad(z*eh=~k z;_e2Qdv3g6@-)s>cM3?V4}=bbE&^M&iAc9mtOjN9!jjo{rgnLF5}{2-dt)o$6fT)c;yxy zX*aiyPszZu#8q&fi?g}siMz6d^ShIq<%;=>rG#c}b!-?S6LoF5nK zBKxrm=Q%%Y*S}ZU4=(4&l=R)M{#9|l-uRvsceI4FU60#NaP-4`bt_I#zAY2K-1yqx z??~d8^TXru-QvjPxF5{erOz(T2XoE`%2SR$v6%Dkhd7UN9Pei=_sc$g&GpaUtGz?q zG`I>rwufpc@5=bGnn3o$T;q%(y!y2Cp=qStqqD!4-1^x3Ktejs=F&K) zocn1mjdPCsLdDtp-6uRPI(Y(zDn9OiY(-4R;!=U&pA+ZHWzYJ@k6tgoR$MwBTawOs zJj|uzk#fGoTsnVq+(yUaU#ovJIx_Eb^W6JR?ntu<0b1`o&jmtxZaJ2m&mcFS(mR6nj8_hrK z^L~i&%jJRh;}6Mx1zZ&$_Y2if9+CVLIxeAgJ3iyw#ZQm?lXQFwPIKey{JTON$74Ni zQl2_)&zC;tUe4bc$1aX;O^^tq=3 z$m<#Vz~c4U&pj2iQHS-Q8j9BqKleskEVGmZLsdFAJxYfCuW zbzCao=!5y=>5`8V|~u3znNV_fc_Va-9AB8*vqIIe#y{JZgT1 zj%D%ldY}4HjhBYMUQf-Xd7pCj$L}q!l3n^RjgRG4Z9iTpZYKmeKXyqztK)ufxjZmu zKg?P0aa_l~_;)wv&=mCX$YOtQkiC@4`MVr1D_$Xx9oE)a^q${{5uc6!_0Btm+|8=JAvc4AO5{4 zk(@sJ`*!IivmWQTl=)$OZ&Yy0{4n=!*(IK{`@xLAe1Gp^f6X~Ql;=3x{X^+x>~eO^ zeOw$5Iqs90{#{AqD(;`Tw0}9yb}#z!s9;k6o+z%Yf7W-oIL0LBhq)`nRZ6(4Bv0p& zLJ&G^f6b--<~ZB^M(Hi{cW=gSZhXz9c5|HV-aNssxs&3`;=H)Q{+auH_ir`RzjQpj zj+;yGTPf%9-RS=QZrSzk@j{X$f0&CbZ^-oTo+t``ZP%RdHx%YLkK5a%w+e2(ynmOt zqZ7Da6?eRZ^Z0&P+|A&af9^l3p}2p4C~gI<>-F!m;_fWr9OtJ$BkEtpA@{x26_Tg( zcPU;H{yGoLIUguYIs0SI`vm4nj`Kd&?{g~Pa{ChR52n@6t>CufGrm9g2Fds9c%np~ z^Thk&#S+eW<+zZ$2Ok#pa~hv5_}Gt0&*#U~uSt2|6lc3IQLOThisNj~{!qLsh6i)L z4?6#&QlnNT;W)=d^$587o41syJ{}Y=NxCd z{v9pznfI>o-`jd0yIKFB2o1MTSaSb-8J_taraH&o+s|X63+XL7m9nN zg!6r2M%*V$I6tQx6n7~cy%OxWzkZ&+Mcg!i6X3?<=lgkG9N&xP;^N=u^u)at9G@M> z?V5XyIKCsyaZ|XK~mS4 zuTZ}x?PIrR#&;FAUF?s!bbVemgrhmHlaJOuc6=T&&bfJRf4x8GgUjs?UYhZjI$b<2 z=F)slIs0oa&F38VFFl_0df-&I5Ck^1$2|dmeZ^(me2Zd@gy=SDf$C5yLi8IEF0$Uh=7Uy5_IBH)O_* z{c>?$So|Wz zdTBQom;bXtyMCW|3IE|CmDFF>Sv($37FT9>ynkiiLp)u2oTs!w*2VF*-L!u>f6cjn zWOCei|H}NB&h+nuJBOqD=k;n|62G00r@6(;ypMR9I5N~>J*b9a?zFgFXkCvxBW|XI zn~G|S*NEGL^ITlaeUG^PC7k^|FYdqu?nlKzK9-{z%I0UsV`_I=G9Ju3e$Nn3S#CVcJx?5& z^|%*_E6Zo=J0PxXUYa{5j(NPE-IL-jo4_rJyLU0e>)HK2ag_<&+r(X2!cC!_ z#d)K^9p6Q)=kMj>Dqz>+t`K+S1n%p^aeUX)=RCM-oYVU9_0p5(!A`I)*5hj8Cgs7a#ZAhC9}>qr;Mk47NAmpe?>i^O?`NcMP5hvn#p}x-i{l!(p1)sG z;Z?)mH95Yqb*RtdwjBb-X8gYJyh^+x9k&O3Gl0l?y^ha_=T#5k!#<|P>%rklY<&m^ ze#SZH%S{ysQC4XPpKglW_M>9OXroQb$*`EaMx6Ur#vKwjY5vy4O`5-Vit|{E+m(0_7p~*E zxO|7eG%nQXV!hV)Mpppj;$rdrs!WQ@e-l?07whvpUlSLN@4E!1ew?rqIBrtidGLr8 zqrH;Ig%w^m{!|&4~bE5c>PRVAFq}ULtO13Fo|D5Vxm|(ipSUaQh|`% zR(u#o>SytInDclz-sV1}ewpL7m4VS=-Z*~$TRgcf_|Rrr%>Au6GV5_qd``T&GJ$)J zxakSpb>eoGaA*$Yn79)-Tf*evCoT4)Bd!Wz&W{zzU#BC#$KiNb9qTOrMe=v*$nR%z z+%HT1`#N4I;r!hF32`3-N1OblWlAT`t4{!647nbKqOpGLmGLyqgT4?Rtk?JRXN#wd z`o{CX+*gYulj9ucuM>9y+?I?!`};z1OJc_D?veZp<;OQYN`Bmy@uTTMfbXt)+`dgb zLu~cZ$0i203oyc0E7R@wg{}9uu_SJ3i;J{@q44 zr<^ZwT>LvN&LPIda;Yo)yD$IuZz}+D^Ud#5eo20;mT*&2?0IxIxQoz+LoAv{ACjKb zU&h77{3peIT1U!r{j=T8Ul}!!$#IXDJheL=FA0Bb*X#Y1cnWj+?8mkVcHO`0#nIQC zK6Bo0@Y!u7e(VT}Iqx^h-m94NcvNsL*EHvoZ=CiwjSKr{`&HCg?5{a89yitd7ZeoqE21bXSt7l zzwx6JxF3_g9|Omwb+Ei@z4~#<(>Qw!Xv_9}pZ*2$lh5Leyl=X!6z+_ z^Y@Eeg^-^LK56;kjNL8j6FJ-UJbzbWcPWuur}OW9;>a-ntjCDoi?hpQ*XzyFf>^e`^>=Ts}bF!DlnSFGzUi;e>Px*R&+$nBSobMKQ$pn4> zQCwM^-z0hJ$IeXNoAW%OA34tX_fF~M9IAkIJa`?8|462Pd`Ir$syTn3 zO?k@MzWw+U>3t>y^uu!N{i}@Koi+x@RdcD`l(SyPEw#(oSZ=#hsyz0qW92Fs%H@kW z=jk*!jir6(QV3qcb9m^bHrFQpeE68^fM&gbVy??aGYf{*2_vzVI}_kOgk z$6Y7x;Sz4#-`^4^HwP~?ZP)%jByKy7jAIoa=GjI#VE^Aa$0hAw&h8hA^LR|^-{*@v zkjZC`r;GE@`LZaU^7ZD?8F7==-B*iaF0ZH0^RjH+{oafpl|eEO=aV_FAM_)~IWFnC z%eXimypHAV8N2Cv<@Lbaq;<{xOV>5_Z`|(Xu)Vo>>SMV=@&60`qVWIpyQ#PUgBKV4 zSMVNN@ZV_rl7jy-*d^iL_j_Eyf92}&1^+FJClve_b1p6T?|pn}!GG1|i3R`7jK^%^ zFIF$w#Q(nO;!Tf3eeD)tkA}ZQe}(ejcCd%8lEL z`Ndinwb|`F-S&c_x7g|S*?>A)uDQBsw8J@S)s-XlTC+IS?9|wFtUiAY{zHOu#U1hF z+TtuKH!KI?OlP^h(7UE+HQJ5Va;s?7R>HBmaSk4=c?6}gSGxO*A(-e z=5nh&t>ZQwuP%1CcI=p2s`VSSChYY)-F-#Bvs5hA7N(1noqoU5Vtre&tJ&<#*B~$E z7HbQgvonqU!P-*M3V&|ubQgA`L;a#P+6C*oJMCV-TSM4p+h-2e zdcC>%8Wc4f?Ru>{UDVpAmz%7_hHY-xhD&E^z52dp?Q{?Iqvw|DYGNna^WA#C9vWLi z>`47o=wIk}%V)UkV7<2(?e>E8Zl{kJcG?A^UGE~8v+ae(e7!fFUc9z`5D#*CHaz|-cUpD@LU zg#8WOX^ln{nI0n?1~IwZCeWuCqW$&8>BauOM!mUU8O$fG#o~}qJ&tL&b2e7utcmpm zj$td&6goX($JPfu3+e1u^l9`w_I_?@vEHo@pl)rUu}qi8+LaBprQULLNH5l!r(*YT z#qKd-ykw>`NV|gg-j#m6-KT|Aa{cm-9qz-8Z6^3^8{u!)nukx`iuIxwhv-P`UCTDhofZBqu9at_gGxTuzEI5w_9L~WOB9! zGY(d<;MvJ|X$-+^n|{=a>z6r;_SLvTqt_!RF`9PPlL#Fg#vp0L5Or%fKON0*aQ80t z8chtXmIJKmafW%RYQb*(NWI1NUQW-42wZ<;=9Yf9G2nE*hB-C7p2*CxYpy=pt=Eeq z{9CP^#%-X{MiMmI@U79lgLgBW)>d#@Te+iH>UVn)Mwr5l4~a9V)_~$xu%}T&y5auW zaxKi;F7gN#<{Edv%Aw`fkxr-224==`>p=ZXy@~2ktl!N;odq_Bx{!DX{d5c&`=S+^ zin-2mcRnm|G1^pm5+Jo`Y3kv|K0Lu)AGZ-k68G_TADvo=na|O9#q>8le=vnwgO)fc z;1-c>YY?`KAXl$J*dk=+7}Zp3^S9L(ZYU1mp5BZR_2etugM+ox?M8ojpG z47a*5vbM%as@0o|SEIkHy)bjGkNyqQxV|zyddl5EvVO+}eY6QXs9Ill;{*+^;Reyu z7OGd^qV%|HL!~ee|DWd-L0QS^k(;9M^^8~t1$P6x???Vm&UA6^^wNu26lu{ z&9gb~jpoB?b)`3yVcP}bWM|>r)!0EIBG>@)!Wp#05oygX^m%r5FkSRI%`^4xELNwm z*$O`Qbhn3Ql0%j8lKJ_CT?=OzgOG0xEL>QHv^|05Mki@ zwfX*$`V#i-!?DA%v@32jP0roUwdz3Fg(hv$(jKG%x5C<-`m}-FS+`gBBCpf9@^gX& z)3dFm|1A7#)+ z+8VumwO;>V=L|cH0Nv)vF^n$?pmB()s=mX zCPbm*XYmL#UGVR=@b~KQ_Zp;R5BsGhgchlaJEtD4PHV@`9X9hr+X!sW2rGHhdU&+) zg0z5beSFGsOV|Vr4PhCqch4o=c4Lwg=836BKj&`~|h-*!6#;{xr+N!nw{GtS5 zBj1aUP_d`TG@S=G{p2=$LTK$;TEfz?u&;yL9TtLk2JN>m>%m}feY+hVBf~S5s;R+h zx5IrSZpx~p!nrWNtKB*_8c97xjx~DAwPv`PCsht%yu-%{IMc1yZgXejJWZPr8ANUg zUMCZ17z8eYd(L5j55s&VR6yN(_9f|e!pDF&<2 z+ciI5r};3)#)I&Z4!mVY_=G&tyMbg+qubZwJsPcpow(${ORT`v4iwBUHku2e*5v&Y z{b3AyMZeZPUGGNeE?7_=uE>>SYtXA=M zg6&7CcRzD!aCQLMH8>%cCgmUx*0wTVTPYjTb2Lf9xGT*NBg?Q5G6_abup>g=4gb zdn6w>w7t-~se0I`XzMa)!vZwOhe0Fu3TqqF`}2UD?alE%&ol}K;{#+|YNE%y!JvEL z5L$){S6Fnl665ml25qot`&6rTYo^<|t=^_)MJ8F=h8L#;mh>WR4ue*-;vw{Cuz|!N z4C}3tlR-OnqukEoA)DJv%-p$p6K~y`P40OQEx|kAQ8ZRb+pcc6#*qo0;>vJe!26C` z*w!6dilf~+RBIt*15NR&LrdNc6w7!9iQAV!)1jr14SV8-1pNpBKUizy(Gic7qi5Q! zGeNMrhBZLfJDbwfI|L2}F88nk;?X8F#oc}g#9+9C0FOE0 zxDAiQy|5PZ@ptgPZ-%c~5tekQM`s|XmZn+@JJ06xN2l!QwaE3`Gz4o|3VSr>rwMc^ zDjaW!!xM$OYx9eBytv?2W~`@Fh^J>fzmEx&fV1tBd^e!8YPio})OnMMeQ6yyppi<^ zJsmulM&ni#x@iUK7uA>x>r%`C<}25ruwl~{_;#-H?8@vyF?aip9o6n~8xtBU5#9wa zV;_ml&v;7`rNKqN43TmkK1#xSqRl|A6H$V&8(ZmooLySWGI085r(3hnr*nuY6wo z?bCg{O^)Z>+1I(iz&v(Dyo=B&-U)fWr`y3dvt*asr{hba-L=+9ygtP!F7$LVUMn4_^~1B!ajcT6zKjbdMi8&4@se}+s8q$b#&`vN zeYbWdyxWW351ypP#eTVT3>)vp60Ta}+kM7oL)tT!hl^LXVGUXC@x5~?a&<)bE?Su% zv*=npDk)C}d4oK6iZLoUWlk~A&(^vNN8xO}iTxg4tsq?_4Yx16tEmnjk0IgZc}Xz9 z?+Lo~_%JP+*>#{(<5P9;jT(}+x!coMQbCEf$9&~1&v*@Wq$u1!#JAtg1|}|rNUet6 zZ`Bvpi13UsPUF2rc!jr?-cxUfm*3n!4LY$457kHTWHD3Ic0Y6$LllnKGY_(4cuf%X z1|u@sg=pPFGaR==C*x(;^7|lc;;^2@2COT?J|LcuCg%yBHir%1Kn1?x!1qUfz;ox@{bQ0(@w(>~k{4+;zW8(jOAoBT5QC1Lx@Z?Khf z!(9{dae*t(8Ug7ewR7`$;~n%yWytf`X5F~0p2bu72ohQhwD#9}u}8WR9gE+2bDD;I zz_2&EuN@prGE7|be{Z)No_}M$#2q3Ma{jh+48{26@kXYQukmZRd<5G%9VpYlk2f>c z?i{{>_h1d;HViF$6c&-)wPtulifP8*_3(vDh_zJ5xDQ4orp;*9^sXJ8XS5C+$4hjCEfva(IL4L=M`oUetZ#gt`%icrR&mP)) z%hAI}Zn$ypExT_#a%?Z!jvkraJ9phMw!87DGQWryvCG}s{JD{@PBhL#-MpFdB|P>x zXZT!+*WSrJ_;3qf|r7p zZVtHhN3(-Sgl$^Phw$)(KM{9gx6T-?YZVttVI-!*}TfqM(jzlFpdysdLEgEc~qt+lL4Zcn(mtEO6t+6 zs-Ee|Mrl_9ao5$r%6_5-odsOkh!PebQDhAQE>Ra z^|@UwWU-|_fA9Z){{Qno=bn43G927{bXQRn7vU4iCHQ;bmKR)G!SP9h)}pvL zNZ0-`@_30RabG6xscTh*7VG<08|0?MeboegU$a4Oo4D;0^j##xLI)=uN?PM$nO1_M^*YYqmdKbdmb=+@zg-Pva(3^MJhgkJ zmEdT-*7qM0Sx%q1*NZFj*W4S#F~&K4KO}kDzlz#;81LVMi7cnj`TVou$mH_b{d>2# zviMow`^9n0a{7K%^0EW3VfoE?C{llO>%o+`Kh}{4t}%`W>n!Fz9!~?aUjP1DoMUHw zY91WCn0_f*9-~hXZjOlU+7GXP_GJqiSieLD?039+_}6jyGI7>N!n#;@KQCA1S#E$V z7Z=~(zET_+c0IWaXGL+98n)@k`zNbq+>0f6LJ8;o*$;EJf4ma?nw1`NP?z=Bd3rHXfVBAy-3 z<(2P8pAbie^D?=NmlmJO*v-vfkJl5lj__K>(E3wzxv) zYw}9%)|ci1ZQFhob^5h+Os~`r4$i+9iyH{WD&}qym)hN#u#S)IE+u-`%L9)u8RmiY zSoIm{`F0&E5YSh1th3mU?-I8aZDh=mNz1+Bz8a0W`^SHk{6QV3!R73FeSWt%J`d#V zen4`^G3^WMj`K&vjmv#*?la=j@!;6I*zZSsJlsFWxdMYZf87tqc?u1=I6qYj*OfYQ z+;VZ=Ci!(bmW_w!$Mxbk9yxtp4{j2-6CCG_PR1Qe)#_OW8&xo`(%5zZ|-NrT{eMxkGRWAIOo9!#Ch(H+qHe?!L!i7OLSSh z>EQf=8a|<81#LNh$H#3exZJpXR(d=xz($J4?J`x;akGpU*q@D#+g4p{y(30(V%^&_ zcImInKh>_s_n`Fhz>oD~ z-2HUBj}jO6s*?J_d~vbfjr!qz$^FvHLvCO4lakxM?fEEi@m^KZ{#6a(=>C;)mK!wZ zaZXglsxC0+d>!cV1!J7!z2^2b#@X+|{4UYw`=R4tY}^m8zZVYxRmSXkzCX5v`=6OO z=kmb$_oqq6SRId#xxW<01M^_q@Bb@#I=)w*HxfT9q*9bK;+~5-ZQ3qnX?e9czF*F9&cD}*T2jVIv+@DLH#*aDZ;(j{LpA}Cz<7~Nk zzo*%(1&z#l{j*(k|>3b_6FSdMBaQ#w9R+#=2ymvPQ{ z@O*JgU@l2G)>*vXzgXM~+H(5t3UpEI7I!Dkuf%8E?g7bPuH$(yIlEp@9cO-zL|q;$ z9{KV5Y;K=896~Ygb8~ypz#Mepd)Bo4Q@M@WeYCknbeiMgGVaHEdC%*d2ljUfZPQZ5 zkg(1e`nlE|k@Nhc;=DJP=KXm?I65E9rFoy@rgZIT!q)X)|@`iw>OJB4ld`fmA+lv&1j&_ zal5|1Jt*!3n4H}yw6px8xJA_0;~o*WRKhua|4ZEJ1nw`y-Ce@De;0oV0F-<1$@R~6 zFB5lf3Fka;K5rH0{Nng*!N>74=Qvj&Z6xLa;P(C>>Y@<#W z^Pay~iKm?LV(ijlZksqV>v1m-SDCm7S^X5*7BeNcNo4CpZZbjU531@%r7Pk{zZhbcQI&pg@aOcJCpTPZy zxC0ZopAuJ{!1?}hw1jiszeoCbKibIru-#SgEF7Cve11uK(mY59*ZT-_-Y4MFktA?F zIIlh|z2||Q7)5bb6n~Vl%Q1GbK98H9-zd+GukRm!F1?INE-u#hDRGyV=<|K+@5NP0 z^qG4+jiGGCXFa=57svSLxMxeAjtAdwxHvAp4_+mn^4#;Zf9H0cI6voEkM&yLVR4M{ zdirYOxJ}4$*4GusLymL*UL|hL_nhkB>m^U)!Wg>PAIC3!kCb~qW{LG;misSNU;>zZS zxlfDRimP++b6kqYMg3Rdkc*4CCyQg8a-6?6{Yr6rz|n_sKg?Y#Ztn!nzYE$2&T|CS zc!I-t{y8qrK_2<}M%hdA)MEtk;C+O-bUjTu`{Q-e<9i-Drtz^H)li%-hh_g^wC=}e zoI9TBAIHtbyys<8JmtCh+mn1%M>E!Az2?4K9PcY+$~a#)eso`dKzh8+lyPqtH#H9E z-1>f5Tsm%ypNsoB{(MO}bHQ@+w)eZz>wGEWE{OB<&N%1&>Bq!XV2FJ$5I{`|HI?h<_LFY7GME02f$*b>EIEoHqYdum>DV2oH+ zH^{wqgWP@M+&|iI-&nUl_ltW+qBrNSx#r%ONN~&)vZy%ospOM(y9@w>!vea_{n_iNuTZYMbBYEIw%;tovE_Y>l(6ZE}L z+|4EWy#9Sy+=&U?Ux-_nz+FryQ5NyZ#l?1?BCc7&Ieu4&TLQqu>N$VSy-D1?C7kEmPl>w^ zTyDOZd#|`3DB*rha?d+pBZYls@wySu*8Vm3JJOfV^M?}L`Cu+xCv%+pm#&lNq0f4( z`lGV@*E&7`LGHe_nKu>`$2*=4|JskIiE|u>M68*6uDI0SN&>r|&iiY`6QK^veTli< z;>fJW9T8V{|Fs|9pE5kTe6js+mcFw4s=2N>#)m!({Gf7_J0!nL$1P~fjmK1=i{iE7 z%APCEOP^ zxGTWr@^4@_s$KJ!eP!7uJ!$+n*InFC=gT*Ur=0m>xp{L3#F5Ey9yhN;6>zKgjO%lp zPfFi;Fu6FJYm0mH1n!mM-crKdE4kwhY@|4T_lry8cRV5OkM((@+ zac&Z zb2%PCS)$MT&a(TP^?Bb}wmv(~zMofMgEq(Q&Zz(CxJ`p|F+V5Hc|iGkdEoVr%$4|9 z53ggHiig7g-P2V#&&6*((?9BTv0lgJoOsG}`pkWcI5Iiz-)8J4st5j9-@S=2r_bCQ zC)hQ2zqqnI^?Uwzi1XU#csM>kFZsy$CXlNa#QD1MBYg_xNv`CN=DaU?TEge{CFZ8Y zk;!q+gRc?C^>i8^{ak0z8IRl5;uaw}k>R%Kimw;P4 z)EQF8mogUXJ0_0*J#{W$rXpV4D(=z}PFQh9+!k>3Vcd^G@^8`cX(ik?DSVZ%pYNt&6<5tU|0&LK9=AuNcNz+ETyA{Lc|E93;Qmhfj!xh%<$t(?0n!vq9Tz>-h4sk0J zxQE2uIf45%ajO%!-xc?&67FM?r}OAMSQl6SLENPKzUNzd-{*X@9_#f!@hSWdXHf0| zz)<`f3c4DmT=cgJ{2!p^Vj#I8^xvma=mx4{aeJ{sw3su{^`Qo#gWP7)hi{x zOGmCPIeqs?KB<4^><_z<^W*zBsL!1Bmg#$IMqib%isNU_dMVCv{{G`#(pv#HjgR%9 z8p``Jb}I?ws=3r|j`RD(4lHV400fOAV#P@|i6Zctgx%+~-&3phr zxdaAtoY$+Ti@OY5F0ZDdn&K*P{0+r=+)i<2_wyShPxJ3#+28@{Lot`;Uyk$pmz$-x zf~z0E$9jS?{F%?#y~o0YH<#MYaei;nlU~k^%l)$4 z&zC=2CxiobOwIF1_^kJd{|E`}e7gUB<=5yt&kFj=Sio@yaba z(r#`YpOS%RiL2l|7iV+N6L)0^=Y7))#cc)0zPtaZhO$TUG_RBzK|ap=xtqeAzHkx# z`1fIC_*)VGUL+(p&i#9ls_Zi+xwst9^pEq>#r?A%4e^xk#D_6Vi{s*dziB6gIX^De zMfPJC&U1d)u79txA6(9lDe1di{j1`9z41LO?q~^TyB@cl;OK|>>Qb*MY!B5?-j(rVHG%Ahxzvx8^CjjyF7zYEZPbrnQU88d$F0yuUu-Wpfj?8Kd|Vvo zP%i(xANr)Y6W}UPHm=Wk;QXtA^Sord*$2kndG%@OL(@pPM`wR8x%IL6frNCN&82Zp zIrr0C8s{AMg^IKHyH9vpbn*laReaq4*ov5r#iatlKPS$Y%bxX*AH80Ft+;eNwj`bN zc$iDaBjtRFxpe;KxQ&j-zgGWdbY$M==DGKs-cL@0dl(=0%lL3*uVv!=pgV=5<7_UCbIREt$E7X3Tc9K7hq*K2E-&G{fA+q(0`4AsY!B5? zd|&-m>3aaJWXibLhmf3 zq&#)po-cjOy_~-@lBeT#A#uXvW-cAKl=CI#(sew?ZFJneQ1?sc>5YodDjdv>$15c7 z=*V|rInHtU7I9_o!<<)liMtp2a(Ois)2eu_xQD^z#@F2UiF>4kd$jLcj?1LHGB+u& z{M`5>@?#79e-OUle2ps7 zkk>QzfyL{ypL;53qYmpqH59KKe(sq$p_F(FgO#*%1`ab8~E?Z5R9Pe{c8?G#->Q7c4h#?xW(!%F|a(Q6R zewee~@W`-jrY*yZe+ z`?xqBa@;2~{kxLJRop*wY5#JZ?OycdQNg7CJyBd)|E%wFag0gM4|7+DtCVn8NuJIl zg&=g;{+dhu&2hH-jnZ4@@7|2v-1wSH?dCY!y?KINb0@`>#d&dq{WJIZ?%!&rf9ZI5 z9XFTWw^GjIyV3pq-LmW7XjKk$htVM%uUKGb6@Ov<#F@)GOtz@l+PuvzH5Vd z^`;5q;e0Wd=2bdw=Dc40g!D46a`|HJ-Qp&$r{c=iQ|t4-lk4evcD?UBQNlTX&gTVi zxqSZ33Gs72o12u+=DyhT+4+*@v-9P1$>%>&oTo4jxx%q#`E+JHE;u1cC-FL5zb$7wufpc-gj;hw~E%>x@+!P;_jQkJx|<&C7kygFBJDk z3FrI5jJQvhaDGlXDDF}?dL`I#fBigti@0e3C%}!z&-e4XIKCIn#l^qR>4|$QI6ga$ z+cozZaePOZ zviFmIpZFfx{V@86|E-Cix!(}?dlR^ii~Icv+~0`%gA(p>9CQ?~8?3MJkBZk*bLo1T za`cJCT)Lj-IPcf~rS3aD9#2wyxR%mK`$=C}%=ta=?Pz1n95-)}z5Y4QT(2%j+2^u8 zU!i_Y+Q)9sjPELJyVxId>H55C2uE{XCm*eS?D#xloOAQs{(67V2bbF)yfou4b-H+5 z%%%C9a`x9;n$J1zUwS@!eAE0}RZu>c{CjyO&Ll`2hj_a5I8SMVtc&AqyJ`P&{+e_D z$mF>3{+0PLo$22RcMeDQ&+FB`Bz`*~Pjicxc^~mIab&2&dQc6;+-Y&U(7GOXM%+vZ zHx<eTzwBEl{yi-2IP7xlIOjr9d!9Hl z>v1mbbo7@6xl;@!jbEpVtSam%gs&um68rCXMfJN#CUL{XKD$#`jOfO&Z_Nh?_LN zPy7l-JHCrp&)>_%Rlu&tT_Ntu3EbC-!l~ngPmYq#^?D<#Z$hX z-DBd&tjE>FP0E8;i<^`OKO~NMz_A;DkL3B`-*--m-_JrkJ^Z94>v&G>!cd6jrYI&Kg6W&n}(dL5q;&#NB7hkZHQ zUU56YF=xj8F!v#G)e_GC9@Iz0odCBpqYq7?e5w+U=fTnDxZSBJ9<1YU2FKjTAr|=0 za*@C^4=O_^3l(MFq=lVLG;jX3wMj5{Q5()_K7n>2s#6z8!Rw=3}=E?mcR zarq8`XC{HZuH?4$Lt&SEv6QDrOI){D!gw|H_}@S)ALnEP9CWY*)J_?&okWdipc zanlpH>%{FW;m{n)F>xnwwuH&SPg?9pM_d)coF6NazfMPfkHhh>I@VeKi{$Urk>Ahc zxL=n1_jSBb!uh%T6XHGwjyCy8%al%>SDygD7;-%dMPvQgE8}UJ2Yn$tSg-Hr&lXP^ z^^NC&xvv&SCdWC>UnlMaxGfod_VE3Y<44nj0N-8pxP6;= zhzgFI@bw2qYL`e(bFzcOkdlj9yQd1`k$UK0M=uGjl1@f7Ct*^g}#?7Dx~i=(eO zedfI1;IrFE{MZo`bKY;1y;m{k@u=Whu4&FE-#G1W8W;A@_N%C~*k5yGJZ`KzmVW=7 za;d)KREF{|h4XmW-?ILFzBtcw$B(f}<5y(^isxm@JrpGIkH^_u`a9&5+oWr}&pwjm z=?Taz7gl&4)x<49FrDF+B=>*Ed>#z_us+sVoTsl8{}#06@;<=$^Ez>FMV-2=&vGCA ze&a_ca6cw}KL(CV>tK1+diCRyr*ZZe(3b7{KK%>gDcgz97FLpCe?KU0CuBK)&3XRr z0hgP7S+DSgcn?jI#j$KxKbE{?b3 z_vEj#q$pNVtqe~?&D=A@t)ev-zbRe!Tyd`|;jC|3+}#s6=ks@tbDD22lAhGxE$Ryo z=m%p=J$a3Rm?r~eA5|7!qU31SCSC$9nt`_G!DARYHIL2hX z{>_TJtc072-5>lr(cZU|*>#?-*(WL|=43C8GyCXbz4o^&p7QnlxKrGuINvSqk_r0$ zqqwp-ze)1ckDZykH|KdmKXRP&@14@iIaC4bcSDgRdfD6 zoAQ*ief#kz()&yZ=!fOj`&SveJ8cY*tL9R>DQCToTWXiFvD|j2RC(-I$I4YOl*<=$ z&eLgd9Ao#ZfNqv2OHVq!7lI`IF>lWMUP>=SB>Z(hozKsa-iIK)1Ru*;XE8S|?)_+6 zkGoFX!zJ9fzrQ6;ZVp~(+OGY5NZfWD8OJI<%(IPf!2Z8;j!W9VoZT-J=kb`-zt0zU zAd}A=PZ#H*^JP&yp#;{O-=MdAPHcT;fz1}`r7 zui!nl;J?xKB?bRwuuH%NJZ5uPt2c9{+q{|n{X9O^l^eGg z^NY1EYO~vUy6pu;Z?V(uvjKIqTyu5NXoqvusw+q8wPtau*{QMVSbhE){D%bRiaX-T zwZ&OfZdeY&na*;1p?6KuYP1`zftP1x&qy8DWLXQ^1KEld|DJNhx$cp$hE4qg@s1@bRo3b?;fq4Y}Si2oyJ1Znu7=@ zt$5wZPN!M44xerJ4%Se9oT#pVMP%Ls?Jc>AQj2SD-fv3$yKVgay z3Huwm(;AH^GCf8(3}SM*O`uOPMEmQF(~JFmje2vzGMGkah*}y(|5CyH5+L73)PW4$+a=yOxzO zm#UrSxzn9?1mXm49m5pbUbGIKJXY_*R_kD+jrmYt!Md?)W(M;RU8r{tpW?JGTGG(N z3UWH;=#kplsBdk{p_6+TPSQt|eb!%?< zN>A6~KsmTiml|v_YE!?Ue#|s;jqp>!R4oAtLN3nzP@3FXwVfAdBZnwY|$>eMe zW*n?y!LyU`(ino-HvOm-*DrGx?W=KxMz2RsVl?fnClNY0j6u?fA?ns}ema`r;O<@O zHJTV&EeBZB;|%js)q>snk$Q{ky_}v85xD-y%q{(HW5DTr4RdOEJ&~DX*Ia$HTdx;K z__ta+joUz@jU;Ha;aj792k&M$t*zj+wsJ?Y)bI8pj4*{89};I!tpUZYU{9llbi@6# z($vF^eRzVqK5iq7B<|zwK037!GoPdJis^59{$L8V1}$+? zz%3%%)*x&dL9SkdutmtsF{-K7=5MPn+)y0AJ-rzt>d9BQ2M24X+l~J6LcN$dc5N80 zkal4TaX^dK$km5VDtxoJiZ-n9a2JEMyHh`PYM>fTHQe7q6YlU`Z5nW^Zw_z(Pu-fv`_91+pS*F(_#{D#9X0{E_RVXo#q1edEAL`rxtc}o^@f9q*XC` zH-|ZfVc=@1MIBRA-A8L@p>gV5+(uPbc7;8h_BeYwm`%0zeEs@vXPJA!-OUCTq1ipf z-29Fm0o~o{){9V^YhV`{{_Hw^x?4Yu=*{lIj;!8|S7GiCb;o+#E{$2C>LZQQ4eSV^ znrCy|8_kE)>Pl}Y!?p{=$q>243rB!?>FCG+zOyB5wc1|i=XSh%nX$GzdPI`*`UepqCx_4Z7o*7I(0Ai}`) zYxDgh^(E}vhhv9jX;<86nw-0xYt@0U3r*Uhr9DUkZiTfu^=SjUvu>~MMP8?I<>v$m zre|AA%gtWB=r!y0rC|?FVd=ah$)>Zl+u$Ph;PtNJbW_rTEoBrgg!v1Pa4+w1Kgytu zv^9GBYQ6r!&KY(X0lLkTV;El!>LA!i84hqg&iRouZW&uA5fbF+aAWI4k9vI;25#Y8 zyVh#V@9Q=~2a8_FoWUt(P9IBB*l3>ua|Gv!1@5QA6GwRH3EOb2$9w=%-L*Kosw?{% zO^8Cr&*Bkgy5Qe!;qTSq?=?ut9`;L12rW_-cTPQ8oz{+>J8b5Mwh`E#5mxf1_3&up z1!)1>`uLRNmaqvL8p1MI@19G#$BBu@8hW0#G63Ge9PM;_5Z9XCjA6MLv{h^U`9%rB zM!pvxp<+*wX*v&X`pIqhgwWcxw1lN&VP6NgJ1hk84BBsB)`P*|`gS`!Muul9Ra1l2 zZio9u+>}*Gg>zwkSG#p=G?IFX9BcHJYt3*oPpTZkc!!S@aHd+~8SK(VI-uN|A!+KJ1siN@ zq2JTMaz_|{Pa{Tni!e7Bs$rXN9>eR=_x@R2Y)3*EgCoP{jEx)S*n^kRT&%u)Sgqpi z1lx~N?|$ae;OqdhYj8p?P0B$YtZik!wo*2v=V+3I%R_R7eR}X=ZvG^X+KJ%|_a3HS zsLkO43=5JDbh6t))Ud2#<-T$I^C0SDEokB8Q@kMV3c|HvuMr<(qbxM=;>J9$3&&^= z_eef&XnUb|Q}wV>(bi?qh6QMl4}(VR71lPU_vZmQ+neKko@o>e#s|o_)I^VYgF*Mg zA+!t^uCVB8CC2694ccJQ_Ni9w)=al^TfI%qicGS!4KGdyEa^qs90sju#Y5=PU;~Lk z7}i@OCxdqEM!B8ELpHaUn7MQHCf>R=o80prT7q}JqiC#>wq4zBjUy90#g*Z{fcG7> zu&q0^6i2&tsMbQr2AbkkhnBn@D3X$aP`6!vJ$PZQ`; zR5;!chbIbm*X9@NcyYn4%vevU5Kqr|ejgJk0cYDM`EEdG)o`D|sPiTh`_ejaKqHl+ zdpdY9jmE7gbkhpdFRC#Y)}@#O%vY{KVZ){^@aQlkkoJnuyXW$3ByYc~D|=oL#k_^alp1a0gXVla+#--< zfej~c6SD(*l^&kNmT~(TK4Q$rz3t$>lwO9(R4>k9UBjEOfGfHM+&}(h{a2HahsLYtae5_?J^))Q;nk`WZZr z_v=Ab49-YXh-tWM4p!-K7B)q(H{twxbnXal3!S1F9{88y9u}#J1cPk2a~n$3t)b{v zZdNj#(EF6IA4+$;$QC{ddY4zWs2bzr&x*!6}BwN zh{P=;VixyfVU*%njE#lFT$J+5;>Iy{BOx$vwT zZispmh8=60>({w? zI@-ZTCVcrDO!|FAv=7|_(?=U!y;eF<>xXBd<5(qCeHj-_j38c7<0a?tQK^b=jqwWl z`flw^c()h5A3RBoi~Vxx7&hLGC0w<{xBHCGhO}oc4;Qa&!y2;O<9p{)Vk zX3@2HR8pP{@&6e`6Z<{9T0y!<8g5^BS5qB69z(**^O9hI z-xGA}@nKptv+F>o#;5Aw8#N?tbGN6jq=FJ{kNL`3p79#$NKv?dh;P4}4NP1Lky;JC z->NUH5#bqOoW^^L@Ct7&y{Fy|FTc5e8gyb89;%Pv$zrCa?SAMihA14dXC7q9@R}g% z4Mt?N3(>lVW;kw#PR7fw<@Z6@#9=*)4Omx(eLy@RP0kZMZ4Mj2feL)Xf$xv}$TQfj zjbv+F((t5?fo6Wpu>oIkVgllUD*RA`5B23zi~|*NN@OD`_Xdk}I1g6f6y6Xq5$g*h zt>NT|ekT#;ztU^0n_PamtjCNwJBP=>pxEtUr+v5^9uyY#H@NmKH~D4qOTzY*-(V}} zhPx)@;{sQnH3HH{YUk$h#yjYZ%8=)=&AM@0J&ULE5hS!2Xzj1{VvlqsIu^h4<}?la zfMIWRUpqLMWSF?<|K4slJpaagi919jt7>e=B){KR5NoN9aUYCGOq0LWG&uB$lu)7M= z71enCU^0%X!$|VmBWUAy2%KAoaKGhVq8&ke+mF|V-bo+9drmy;`a2UoCQs|TqywGK zZOcpX*Ggd}<}(M(A3905;t9V!z%Goh91{FR16Skbg8Ysh^@F>P-*RZ?mc56rpFOnq zmZOJ{+;HRGTXx@gweUHt7;au0ubz1y)Mb^u23t^;fGx)gTD*uz9H z+#GQ0k7fsv2-~!n58>enf!xPCc6^dTP~DpBwFVc*z9^dp`=vBEm^i(D18;KEx9@}N zwT+7kZd|e{-Z)hspdnQTxfNp^x0C2`?8jhRI2^4#c$v-}GGEtTdsDQ7FHYK6mulTP zy#K|?wswmf1DEyJ1#P*{TeS< zb=OL>J~#NLA@PFKM802p6UH1~@wPK&W$c1LCRdJ$kJk9YCWMt)+#HGzbwVp|xY!Z; vW)Rw9`Xb`V<}q2*({LYBxJU1Zn4}|f9zP8kYZ^o`fb#m#O~dYmb~OAy_hnZ( literal 0 HcmV?d00001 diff --git a/src/PhysX/physx/tools/binarymetadata/mac/mac64.metaData b/src/PhysX/physx/tools/binarymetadata/mac/mac64.metaData new file mode 100644 index 0000000000000000000000000000000000000000..77a6123f1d6044f285218552b565ec8302de8422 GIT binary patch literal 52514 zcmdU&3!G$EUFYu%2?mHbK!C^sR)>fqM(jzlFpdysdLEgEc~qt+lL4Zcn(mtEO6t+6 zs-Ee|Mrl_9ao5$r%6_5-odsOkh!PebQDhAQE>Ra z^|@UwWU-|_fA9Z){{Qno=bn43G927{bXQRn7vU4iCHQ;rmKR)G!SP9h)}pvL zNZ0-`@_30RabG6xscTh*7VG<08|0?MeboegU$a4Oo4D;0^j##xLI)=uN?PM$nO1_M^*YYqmdKbdmb=+@zg-Pva(3^MJhgkJ zmEdT-*7qM0Sx%q1*NZFj*W4S#F~&K4KO}kDzlz#;81LVMi7cnj`TVou$mH_b{d>2# zviMow`^9n0a{7K%^0EW3VfoE?C{llO>%o+`Kh}{4t}%`W>n!Fz9!~?aUjP1DoMUHw zY91WCn0_f*9-~hXZjOlU+7GXP_GJqiSieLD?039+_}6jyGI7>N!n#;@KQCA1S#E$V z7Z=~(zET_+c0IWaXGL+98n)@k`zNbq+>0f6LJ8;o*$;EJf4ma?nw1`NP?z=Bd3rHXfVBAy-3 z<(2P8pAbie^D?=NmlmJO*v-vfkJl5lj__K>(E3wzxv) zYw}9%)|ci1ZQFhob^5h+Os~`r4$i+9iyH{WD&}qym)hN#u#S)IE+u-`%L9)u8RmiY zSoIm{`F0&E5YSh1th3mU?-I8aZDh=mNz1+Bz8a0W`^SHk{6QV3!R73FeSWt%J`d#V zen4`^G3^WMj`K&vjmv#*?la=j@!;6I*zZSsJlsFWxdMYZf87tqc?u1=I6qYj*OfYQ z+;VZ=Ci!(bmW_w!$Mxbk9yxtp4{j2-6CCG_PR1Qe)#_OW8&xo`(%5zZ|-NrT{eMxkGRWAIOo9!#Ch(H+qHe?!L!i7OLSSh z>EQf=8a|<81#LNh$H#3exZJpXR(d=xz($J4?J`x;akGpU*q@D#+g4p{y(30(V%^&_ zcImInKh>_s_n`Fhz>oD~ z-2HUBj}jO6s*?J_d~vbfjr!qz$^FvHLvCO4lakxM?fEEi@m^KZ{#6a(=>C;)mK!wZ zaZXglsxC0+d>!cV1!J7!z2^2b#@X+|{4UYw`=R4tY}^m8zZVYxRmSXkzCX5v`=6OO z=kmb$_oqq6SRId#xxW<01M^_q@Bb@#I=)w*HxfT9q*9bK;+~5-ZQ3qnX?e9czF*F9&cD}*T2jVIv+@DLH#*aDZ;(j{LpA}Cz<7~Nk zzo*%(1&z#l{j*(k|>3b_6FSdMBaQ#w9R+#=2ymvPQ{ z@O*JgU@l2G)>*vXzgXM~+H(5t3UpEI7I!Dkuf%8E?g7bPuH$(yIlEp@9cO-zL|q;$ z9{KV5Y;K=896~Ygb8~ypz#Mepd)Bo4Q@M@WeYCknbeiMgGVaHEdC%*d2ljUfZPQZ5 zkg(1e`nlE|k@Nhc;=DJP=KXm?I65E9rFoy@rgZIT!q)X)|@`iw>OJB4ld`fmA+lv&1j&_ zal5|1Jt*!3n4H}yw6px8xJA_0;~o*WRKhua|4ZEJ1nw`y-Ce@De;0oV0F-<1$@R~6 zFB5lf3Fka;K5rH0{Nng*!N>74=Qvj&Z6xLa;P(C>>Y@<#W z^Pay~iKm?LV(ijlZksqV>v1m-SDCm7S^X5*7BeNcNo4CpZZbjU531@%r7Pk{zZhbcQI&pg@aOcJCpTPZy zxC0ZopAuJ{!1?}hw1jiszeoCbKibIru-#SgEF7Cve11uK(mY59*ZT-_-Y4MFktA?F zIIlh|z2||Q7)5bb6n~Vl%Q1GbK98H9-zd+GukRm!F1?INE-u#hDRGyV=<|K+@5NP0 z^qG4+jiGGCXFa=57svSLxMxeAjtAdwxHvAp4_+mn^4#;Zf9H0cI6voEkM&yLVR4M{ zdirYOxJ}4$*4GusLymL*UL|hL_nhkB>m^U)!Wg>PAIC3!kCb~qW{LG;misSNU;>zZS zxlfDRimP++b6kqYMg3Rdkc*4CCyQg8a-6?6{Yr6rz|n_sKg?Y#Ztn!nzYE$2&T|CS zc!I-t{y8qrK_2<}M%hdA)MEtk;C+O-bUjTu`{Q-e<9i-Drtz^H)li%-hh_g^wC=}e zoI9TBAIHtbyys<8JmtCh+mn1%M>E!Az2?4K9PcY+$~a#)eso`dKzh8+lyPqtH#H9E z-1>f5Tsm%ypNsoB{(MO}bHQ@+w)eZz>wGEWE{OB<&N%1&>Bq!XV2FJ$5I{`|HI?h<_LFY7GME02f$*b>EIEoHqYdum>DV2oH+ zH^{wqgWP@M+&|iI-&nUl_ltW+qBrNSx#r%ONN~&)vZy%ospOM(y9@w>!vea_{n_iNuTZYMbBYEIw%;tovE_Y>l(6ZE}L z+|4EWy#9Sy+=&U?Ux-_nz+FryQ5NyZ#l?1?BCc7&Ieu4&TLQqu>N$VSy-D1?C7kEmPl>w^ zTyDOZd#|`3DB*rha?d+pBZYls@wySu*8Vm3JJOfV^M?}L`Cu+xCv%+pm#&lNq0f4( z`lGV@*E&7`LGHe_nKu>`$2*=4|JskIiE|u>M68*6uDI0SN&>r|&iiY`6QK^veTli< z;>fJW9T8V{|Fs|9pE5kTe6js+mcFw4s=2N>#)m!({Gf7_J0!nL$1P~fjmK1=i{iE7 z%APCEOP^ zxGTWr@^4@_s$KJ!eP!7uJ!$+n*InFC=gT*Ur=0m>xp{L3#F5Ey9yhN;6>zKgjO%lp zPfFi;Fu6FJYm0mH1n!mM-crKdE4kwhY@|4T_lry8cRV5OkM((@+ zac&Z zb2%PCS)$MT&a(TP^?Bb}wmv(~zMofMgEq(Q&Zz(CxJ`p|F+V5Hc|iGkdEoVr%$4|9 z53ggHiig7g-P2V#&&6*((?9BTv0lgJoOsG}`pkWcI5Iiz-)8J4st5j9-@S=2r_bCQ zC)hQ2zqqnI^?Uwzi1XU#csM>kFZsy$CXlNa#QD1MBYg_xNv`CN=DaU?TEge{CFZ8Y zk;!q+gRc?C^>i8^{ak0z8IRl5;uaw}k>R%Kimw;P4 z)EQF8mogUXJ0_0*J#{W$rXpV4D(=z}PFQh9+!k>3Vcd^G@^8`cX(ik?DSVZ%pYNt&6<5tU|0&LK9=AuNcNz+ETyA{Lc|E93;Qmhfj!xh%<$t(?0n!vq9Tz>-h4sk0J zxQE2uIf45%ajO%!-xc?&67FM?r}OAMSQl6SLENPKzUNzd-{*X@9_#f!@hSWdXHf0| zz)<`f3c4DmT=cgJ{2!p^Vj#I8^xvma=mx4{aeJ{sw3su{^`Qo#gWP7)hi{x zOGmCPIeqs?KB<4^><_z<^W*zBsL!1Bmg#$IMqib%isNU_dMVCv{{G`#(pv#HjgR%9 z8p``Jb}I?ws=3r|j`RD(4lHV400fOAV#P@|i6Zctgx%+~-&3phr zxdaAtoY$+Ti@OY5F0ZDdn&K*P{0+r=+)i<2_wyShPxJ3#+28@{Lot`;Uyk$pmz$-x zf~z0E$9jS?{F%?#y~o0YH<#MYaei;nlU~k^%l)$4 z&zC=2CxiobOwIF1_^kJd{|E`}e7gUB<=5yt&kFj=Sio@yaba z(r#`YpOS%RiL2l|7iV+N6L)0^=Y7))#cc)0zPtaZhO$TUG_RBzK|ap=xtqeAzHkx# z`1fIC_*)VGUL+(p&i#9ls_Zi+xwst9^pEq>#r?A%4e^xk#D_6Vi{s*dziB6gIX^De zMfPJC&U1d)u79txA6(9lDe1di{j1`9z41LO?q~^TyB@cl;OK|>>Qb*MY!B5?-j(rVHG%Ahxzvx8^CjjyF7zYEZPbrnQU88d$F0yuUu-Wpfj?8Kd|Vvo zP%i(xANr)Y6W}UPHm=Wk;QXtA^Sord*$2kndG%@OL(@pPM`wR8x%IL6frNCN&82Zp zIrr0C8s{AMg^IKHyH9vpbn*laReaq4*ov5r#iatlKPS$Y%bxX*AH80Ft+;eNwj`bN zc$iDaBjtRFxpe;KxQ&j-zgGWdbY$M==DGKs-cL@0dl(=0%lL3*uVv!=pgV=5<7_UCbIREt$E7X3Tc9K7hq*K2E-&G{fA+q(0`4AsY!B5? zd|&-m>3aaJWXibLhmf3 zq&#)po-cjOy_~-@lBeT#A#uXvW-cAKl=CI#(sew?ZFJneQ1?sc>5YodDjdv>$15c7 z=*V|rInHtU7I9_o!<<)liMtp2a(Ois)2eu_xQD^z#@F2UiF>4kd$jLcj?1LHGB+u& z{M`5>@?#79e-OUle2ps7 zkk>QzfyL{ypL;53qYmpqH59KKe(sq$p_F(FgO#*%1`ab8~E?Z5R9Pe{c8?G#->Q7c4h#?xW(!%F|a(Q6R zewee~@W`-jrY*yZe+ z`?xqBa@;2~{kxLJRop*wY5#JZ?OycdQNg7CJyBd)|E%wFag0gM4|7+DtCVn8NuJIl zg&=g;{+dhu&2hH-jnZ4@@7|2v-1wSH?dCY!y?KINb0@`>#d&dq{WJIZ?%!&rf9ZI5 z9XFTWw^GjIyV3pq-LmW7XjKk$htVM%uUKGb6@Ov<#F@)GOtz@l+PuvzH5Vd z^`;5q;e0Wd=2bdw=Dc40g!D46a`|HJ-Qp&$r{c=iQ|t4-lk4evcD?UBQNlTX&gTVi zxqSZ33Gs72o12u+=DyhT+4+*@v-9P1$>%>&oTo4jxx%q#`E+JHE;u1cC-FL5zb$7wufpc-gj;hw~E%>x@+!P;_jQkJx|<&C7kygFBJDk z3FrI5jJQvhaDGlXDDF}?dL`I#fBigti@0e3C%}!z&-e4XIKCIn#l^qR>4|$QI6ga$ z+cozZaePOZ zviFmIpZFfx{V@86|E-Cix!(}?dlR^ii~Icv+~0`%gA(p>9CQ?~8?3MJkBZk*bLo1T za`cJCT)Lj-IPcf~rS3aD9#2wyxR%mK`$=C}%=ta=?Pz1n95-)}z5Y4QT(2%j+2^u8 zU!i_Y+Q)9sjPELJyVxId>H55C2uE{XCm*eS?D#xloOAQs{(67V2bbF)yfou4b-H+5 z%%%C9a`x9;n$J1zUwS@!eAE0}RZu>c{CjyO&Ll`2hj_a5I8SMVtc&AqyJ`P&{+e_D z$mF>3{+0PLo$22RcMeDQ&+FB`Bz`*~Pjicxc^~mIab&2&dQc6;+-Y&U(7GOXM%+vZ zHx<eTzwBEl{yi-2IP7xlIOjr9d!9Hl z>v1mbbo7@6xl;@!jbEpVtSam%gs&um68rCXMfJN#CUL{XKD$#`jOfO&Z_Nh?_LN zPy7l-JHCrp&)>_%Rlu&tT_Ntu3EbC-!l~ngPmYq#^?D<#Z$hX z-DBd&tjE>FP0E8;i<^`OKO~NMz_A;DkL3B`-*--m-_JrkJ^Z94>v&G>!cd6jrYI&Kg6W&n}(dL5q;&#NB7hkZHQ zUU56YF=xj8F!v#G)e_GC9@Iz0odCBpqYq7?e5w+U=fTnDxZSBJ9<1YU2FKjTAr|=0 za*@C^4=O_^3l(MFq=lVLG;jX3wMj5{Q5()_K7n>2s#6z8!Rw=3}=E?mcR zarq8`XC{HZuH?4$Lt&SEv6QDrOI){D!gw|H_}@S)ALnEP9CWY*)J_?&okWdipc zanlpH>%{FW;m{n)F>xnwwuH&SPg?9pM_d)coF6NazfMPfkHhh>I@VeKi{$Urk>Ahc zxL=n1_jSBb!uh%T6XHGwjyCy8%al%>SDygD7;-%dMPvQgE8}UJ2Yn$tSg-Hr&lXP^ z^^NC&xvv&SCdWC>UnlMaxGfod_VE3Y<44nj0N-8pxP6;= zhzgFI@bw2qYL`e(bFzcOkdlj9yQd1`k$UK0M=uGjl1@f7Ct*^g}#?7Dx~i=(eO zedfI1;IrFE{MZo`bKY;1y;m{k@u=Whu4&FE-#G1W8W;A@_N%C~*k5yGJZ`KzmVW=7 za;d)KREF{|h4XmW-?ILFzBtcw$B(f}<5y(^isxm@JrpGIkH^_u`a9&5+oWr}&pwjm z=?Taz7gl&4)x<49FrDF+B=>*Ed>#z_us+sVoTsl8{}#06@;<=$^Ez>FMV-2=&vGCA ze&a_ca6cw}KL(CV>tK1+diCRyr*ZZe(3b7{KK%>gDcgz97FLpCe?KU0CuBK)&3XRr z0hgP7S+DSgcn?jI#j$KxKbE{?b3 z_vEj#q$pNVtqe~?&D=A@t)ev-zbRe!Tyd`|;jC|3+}#s6=ks@tbDD22lAhGxE$Ryo z=m%p=J$a3Rm?r~eA5|7!qU31SCSC$9nt`_G!DARYHIL2hX z{>_TJtc072-5>lr(cZU|*>#?-*(WL|=43C8GyCXbz4o^&p7QnlxKrGuINvSqk_r0$ zqqwp-ze)1ckDZykH|KdmKXRP&@14@iIaC4bcSDgRdfD6 zoAQ*ief#kz()&yZ=!fOj`&SveJ8cY*tL9R>DQCToTWXiFvD|j2RC(-I$I4YOl*<=$ z&eLgd9Ao#ZfNqv2OHVq!7lI`IF>lWMUP>=SB>Z(hozKsa-iIK)1Ru*;XE8S|?)_+6 zkGoFX!zJ9fzrQ6;ZVp~(+OGY5NZfWD8OJI<%(IPf!2Z8;j!W9VoZT-J=kb`-zt0zU zAd}A=PZ#H*^JP&yp#;{O-=MdAPHcT;fz1}`r7 zui!nl;J?xKB?bRwuuH%NJZ5uPt2c9{+q{|n{X9O^l^eGg z^NY1EYO~vUy6pu;Z?V(uvjKIqTyu5NXoqvusw+q8wPtau*{QMVSbhE){D%bRiaX-T zwZ&OfZdeY&na*;1p?6KuYP1`zftP1x&qy8DWLXQ^1KEld|DJNhx$cp$hE4qg@s1@bRo3b?;fq4Y}Si2oyJ1Znu7=@ zt$5wZPN!M44xerJ4%Se9oT#pVMP%Ls?Jc>AQj2SD-fv3$yKVgay z3Huwm(;AH^GCf8(3}SM*O`uOPMEmQF(~JFmje2vzGMGkah*}y(|5CyH5+L73)PW4$+a=yOxzO zm#UrSxzn9?1mXm49m5pbUbGIKJXY_*R_kD+jrmYt!Md?)W(M;RU8r{tpW?JGTGG(N z3UWH;=#kplsBdk{p_6+TPSQt|eb!%?< zN>A6~KsmTiml|v_YE!?Ue#|s;jqp>!R4oAtLN3nzP@3FXwVfAdBZnwY|$>eMe zW*n?y!LyU`(ino-HvOm-*DrGx?W=KxMz2RsVl?fnClNY0j6u?fA?ns}ema`r;O<@O zHJTV&EeBZB;|%js)q>snk$Q{ky_}v85xD-y%q{(HW5DTr4RdOEJ&~DX*Ia$HTdx;K z__ta+joUz@jU;Ha;aj792k&M$t*zj+wsJ?Y)bI8pj4*{89};I!tpUZYU{9llbi@6# z($vF^eRzVqK5iq7B<|zwK037!GoPdJis^59{$L8V1}$+? zz%3%%)*x&dL9SkdutmtsF{-K7=5MPn+)y0AJ-rzt>d9BQ2M24X+l~J6LcN$dc5N80 zkal4TaX^dK$km5VDtxoJiZ-n9a2JEMyHh`PYM>fTHQe7q6YlU`Z5nW^Zw_z(Pu-fv`_91+pS*F(_#{D#9X0{E_RVXo#q1edEAL`rxtc}o^@f9q*XC` zH-|ZfVc=@1MIBRA-A8L@p>gV5+(uPbc7;8h_BeYwm`%0zeEs@vXPJA!-OUCTq1ipf z-29Fm0o~o{){9V^YhV`{{_Hw^x?4Yu=*{lIj;!8|S7GiCb;o+#E{$2C>LZQQ4eSV^ znrCy|8_kE)>Pl}Y!?p{=$q>243rB!?>FCG+zOyB5wc1|i=XSh%nX$GzdPI`*`UepqCx_4Z7o*7I(0Ai}`) zYxDgh^(E}vhhv9jX;<86nw-0xYt@0U3r*Uhr9DUkZiTfu^=SjUvu>~MMP8?I<>v$m zre|AA%gtWB=r!y0rC|?FVd=ah$)>Zl+u$Ph;PtNJbW_rTEoBrgg!v1Pa4+w1Kgytu zv^9GBYQ6r!&KY(X0lLkTV;El!>LA!i84hqg&iRouZW&uA5fbF+aAWI4k9vI;25#Y8 zyVh#V@9Q=~2a8_FoWUt(P9IBB*l3>ua|Gv!1@5QA6GwRH3EOb2$9w=%-L*Kosw?{% zO^8Cr&*Bkgy5Qe!;qTSq?=?ut9`;L12rW_-cTPQ8oz{+>J8b5Mwh`E#5mxf1_3&up z1!)1>`uLRNmaqvL8p1MI@19G#$BBu@8hW0#G63Ge9PM;_5Z9XCjA6MLv{h^U`9%rB zM!pvxp<+*wX*v&X`pIqhgwWcxw1lN&VP6NgJ1hk84BBsB)`P*|`gS`!Muul9Ra1l2 zZio9u+>}*Gg>zwkSG#p=G?IFX9BcHJYt3*oPpTZkc!!S@aHd+~8SK(VI-uN|A!+KJ1siN@ zq2JTMaz_|{Pa{Tni!e7Bs$rXN9>eR=_x@R2Y)3*EgCoP{jEx)S*n^kRT&%u)Sgqpi z1lx~N?|$ae;OqdhYj8p?P0B$YtZik!wo*2v=V+3I%R_R7eR}X=ZvG^X+KJ%|_a3HS zsLkO43=5JDbh6t))Ud2#<-T$I^C0SDEokB8Q@kMV3c|HvuMr<(qbxM=;>J9$3&&^= z_eef&XnUb|Q}wV>(bi?qh6QMl4}(VR71lPU_vZmQ+neKko@o>e#s|o_)I^VYgF*Mg zA+!t^uCVB8CC2694ccJQ_Ni9w)=al^TfI%qicGS!4KGdyEa^qs90sju#Y5=PU;~Lk z7}i@OCxdqEM!B8ELpHaUn7MQHCf>R=o80prT7q}JqiC#>wq4zBjUy90#g*Z{fcG7> zu&q0^6i2&tsMbQr2AbkkhnBn@D3X$aP`6!vJ$PZQ`; zR5;!chbIbm*X9@NcyYn4%vevU5Kqr|ejgJk0cYDM`EEdG)o`D|sPiTh`_ejaKqHl+ zdpdY9jmE7gbkhpdFRC#Y)}@#O%vY{KVZ){^@aQlkkoJnuyXW$3ByYc~D|=oL#k_^alp1a0gXVla+#--< zfej~c6SD(*l^&kNmT~(TK4Q$rz3t$>lwO9(R4>k9UBjEOfGfHM+&}(h{a2HahsLYtae5_?J^))Q;nk`WZZr z_v=Ab49-YXh-tWM4p!-K7B)q(H{twxbnXal3!S1F9{88y9u}#J1cPk2a~n$3t)b{v zZdNj#(EF6IA4+$;$QC{ddY4zWs2bzr&x*!6}BwN zh{P=;VixyfVU*%njE#lFT$J+5;>Iy{BOx$vwT zZispmh8=60>({w? zI@-ZTCVcrDO!|FAv=7|_(?=U!y;eF<>xXBd<5(qCeHj-_j38c7<0a?tQK^b=jqwWl z`flw^c()h5A3RBoi~Vxx7&hLGC0w<{xBHCGhO}oc4;Qa&!y2;O<9p{)Vk zX3@2HR8pP{@&6e`6Z<{9T0y!<8g5^BS5qB69z(**^O9hI z-xGA}@nKptv+F>o#;5Aw8#N?tbGN6jq=FJ{kNL`3p79#$NKv?dh;P4}4NP1Lky;JC z->NUH5#bqOoW^^L@Ct7&y{Fy|FTc5e8gyb89;%Pv$zrCa?SAMihA14dXC7q9@R}g% z4Mt?N3(>lVW;kw#PR7fw<@Z6@#9=*)4Omx(eLy@RP0kZMZ4Mj2feL)Xf$xv}$TQfj zjbv+F((t5?fo6Wpu>oIkVgllUD*RA`5B23zi~|*NN@OD`_Xdk}I1g6f6y6Xq5$g*h zt>NT|ekT#;ztU^0n_PamtjCNwJBP=>pxEtUr+v5^9uyY#H@NmKH~D4qOTzY*-(V}} zhPx)@;{sQnH3HH{YUk$h#yjYZ%8=)=&AM@0J&ULE5hS!2Xzj1{VvlqsIu^h4<}?la zfMIWRUpqLMWSF?<|K4slJpaagi919jt7>e=B){KR5NoN9aUYCGOq0LWG&uB$lu)7M= z71enCU^0%X!$|VmBWUAy2%KAoaKGhVq8&ke+mF|V-bo+9drmy;`a2UoCQs|TqywGK zZOcpX*Ggd}<}(M(A3905;t9V!z%Goh91{FR16Skbg8Ysh^@F>P-*RZ?mc56rpFOnq zmZOJ{+;HRGTXx@gweUHt7;au0ubz1y)Mb^u23t^;fGx)gTD*uz9H z+#GQ0k7fsv2-~!n58>enf!xPCc6^dTP~DpBwFVc*z9^dp`=vBEm^i(D18;KEx9@}N zwT+7kZd|e{-Z)hspdnQTxfNp^x0C2`?8jhRI2^4#c$v-}GGEtTdsDQ7FHYK6mulTP zy#K|?wswmf1DEyJ1#P*{TeS< zb=OL>J~#NLA@PFKM802p6UH1~@wPK&W$c1LCRdJ$kJk9YCWMt)+#HGzbwVp|xY!Z; vW)Rw9`Xb`V<}q2*({LYBxJU1Zn4}|f9zP8kYZ^o`fb#m#O~dYmb~OAy#DZ5k literal 0 HcmV?d00001 diff --git a/src/PhysX/physx/tools/binarymetadata/windows/win32.metaData b/src/PhysX/physx/tools/binarymetadata/windows/win32.metaData new file mode 100644 index 0000000000000000000000000000000000000000..a1426697d6a003b9bee86652351e1cc53525f32f GIT binary patch literal 44148 zcmd6w51eI3Rp;;I4@L+uAWDRQ-8h7SjBHO5!#EDuGt)Ck!_4Gmx@R&maWilFy_tT= z`eQ)Ib?e^uIyty+ad(zw7m%FjtRH7nSgrk#Fs9=G?^Ed92%){=+2oJ!StQ`CrNQ-4xk(3Fju}KaFoQWU7yr zZ{@^0B(W4aCcDd#4pNB((5$kg~*zLk^y?;(k$^nWk;rhg0PCZWjaQ zRA#^U8%gp_s{WhExAvzvH;LPSSIA7&SNyz(B$kRFwg2m+vi__7`$_U`s{V(`Pl?J( z{)lAu%LWs(f0~R>hRj&M=A%UXuSsGt=hBJV{~Sr>rPK6snu`{XuO^)zk^|?tPp4n@ znSPt&n^CCqi%_UO6h-AFPlj9n%x2}A9hY$OtZ)(AZ)LUqJ{K+~ldK-6PPUP;o%A@a z#8UjlWPVi%SNpQhxV3X^%J*+RaH_lh>L{^x6d%`zi?aAyfS1MBkqz`KzPdqw>>t@6 z5kCzVE8{PR%b#WZKf=rGI|tJk6h*ou_iVs_5}q1=#hOI*f0iVDsqt0&?;>e@Q~Wnz zRr+;*{YcQC>i@^0`l*Ej#jzPm_ULfvA0va{E~pUW||zWpTZ?WC>nlzy!* z?}l%KOLtNK2Vn9+QKU=qP=YDn_~+o}zj&W#?L1olseSopiZ)aB%NF@(nq$g8PbTx( zB>6YxpUYu8NM-(0{OyLzf2sPKAJ@YbI~r5j7|TsC`KT!7zY*O0CmT&n&qn_HLF(T_ zlD?FET3>$#F56;m?TEhvuK8cae-*B|mf{+p55S)Wm(6B3r@3f+^bA5VmmCN_`52j> zB58jV>zBO}^RMPs%D?|iUHPym(j|EkC+1(vQ@+``k$;~5^=>yUe5|fTs~U21VmhY zSQo+(*BZUfgota6xzO6RW5hK_bbp8b{)NGKTVt$@j#_^&hKtFER*%zMz6SnOuEbLOQux=DaNR%6zFEqcm>xaf zUKZ@r8fE!bPVu=7E|!W<^?xUPD_lOZ`nFcg!H$r&m2ma{6nqQEiT;}c^84`1I7+wa z=QNi!xZdHUxW@l(_zYY=F4q4MnE6Ncn^-y7r{^2Vgnd5Cjo*)>LoCJBf734?n#B5l ziZYT^eYOAd75Y{0m*KKq$@H50rpZ(M=sM%Rdj2EOv|frQ}cGiv760 zwf||D^*^ zD=YoxzoPypQ&(L1uQJHd))8?%gGpj;eiqj=l*tF?UGu4qN?L#9 zzpDf1;Ka)5Li{R@iUAY7|1kMd_(u94Ew5pW`fn23R~BC?Z}UgvWAjJ(GbH&y`+N0| z)2yM7AQoTZ7GE*9aT2%qN^!+co$*s!YS-$8X54d02VR{2#x;I-z{}>Z=J%`M>RYVe zhR^O#Q}ERKKz8;+)Zf7|RbTP@V#k&2mIl zKF+n-^ETuZudFZb>`&=G?)6vkjp~a_WWU9S#c8pBHoiq}?c4a4aU0(+hs!q8pQ!<8Zjqjz5t@@|3eSHMk6po7j%k=UT(RfNnEaI2L#me{$e5!(fGkm6k?}X1*@Na|H zO1S(p2R~lI=V8`=l`*mY>-(?kC?iq-EZ=4DA^z=fu`=F-PgU>%e5QoU{#U|hImYA1 zr4#YH;TC^I`^0|?Zt<7mZ-QI=r1&qwYv?I!U+dpu30HjmDyeM!vG_CnYv_^4musZ= zQP;-jBV3zs+i@bUdypjN)(?&EM@fD7agyb8>g3NN{aV9JtlTsi+W$&Y{nz^UX>^Fi zJYHY_9)59wbLm9u>%YL&-&B3^3s7e3g4q@8e+n16PSyvQ|73%S>CyUpSx~0^kL6oA zegC}!E|%h|KMz;iDX#iA!3mKhSN%4ee@U+T-wo9F_$mEA1hf9hhbGpKOW^i=s{UKP zm7C^7&ktqe^TrMIYy96#8A(dN?i;@dFVp`Uk$&wlOw3M={|AHq)cDH2kCDXGf2(JD z)W5%i7vm@955tT5C*$hhzrv?Drurv-F<&ljE8%*-{ta;XLAEKyBK|`7w~$%Jb#vND zvKVyrb%EIUTmLKubX58lbX$D9OBI6H`XO%XN6gJmtq<~lgL;>cEZ_8K{N~YdCm9;Q zB0n6puQ4*Qa*FShl#!(3y92v}WX$UM8vHf@_I)JvM=ZsyYp{ zH$AfdcStHPou=2gjvs|H%%%JjaQ-FvKLD!i7grx8ss23@)t~Xj+^5+oE*+8-f6^1O z?3pBu)fCCuhNJAHkUx$@EXUm`tOC`3{SN${U3!Nui$?JZ&dI{;L8>Ki5S){;j;fyc%NgL z{g(soEU7R1Uj$#P;PdcTRq$i*yDIoH{O$@qf}bnl)8zjL_-iZp8{uy#;fmj1gx|w4 z6~E%Y2LJIA{(hKZxG0JRiRM>IjQ;`kZGJou6|^`NxAh^#)xNC{_fp^LnW|5~|Bm!F zGSsGVTVKBblMjnx_HBM*_5FDYZuU<_N~|5l&o@y<61VSj{O`YB3>Pco2jH?d#buxN zA9N!*ero3!WuC-QHkn<@H)+E)&J$e0fQ(=-CvE0DZr}VjP5!Ilwm&cG|1p^PM>qT9Im>l_Wu_4ucWC^w^*O->~Rdc!zFEP8X#6bSW$+=sA1;>S@~`IS6#O`;SYQ6RnfhxSQ~nWOhQF$UpM~F5 z!e0ZEkBVabzYlKxpN$HcovN?>fh6T$>Ha0kh)r-^AiDqjCS2_)22C%gPQ-s7F5jj2 zU%)<3D#nk^zw
            7*&@$Nn=u1$z-`O9|f#yOFfDgin*-fNv{sE}dw6y6_o}ar>V7 z^=%EV=c6+I!|<}_qc_8}Ki*ZSZ~eO;ZsVsunwXzI1W$=s}z`eomJ@Ur;O`?0se8KR{B-vcxM&zMo6tPK4aJSFO@T1zG>94`{cMhrXpN2Q!7ng8w)`xF~%eJC@^6we=*Ol-ZzSHo_;1^Z! zo$!lG_{(56{`aEGgyK%bZT#;Ic{&)^^WRa*$p7b}e3g-WN2Gt*XSq+SFK+r%T+de> z%E&jfB&+9?@FD-5g&*LU@}Kx!@M9%h&u4FdH{hxL^?fk&&n#t3%not0f7T#JZxtX-$(it|0Y&W-1MjT7vYcpTCYFuzpYQxD116x@fCAxM|=w$HA$}f>vs4h zaM`33%S&K3zSgFAT+aS}EmdD-_5HILUzNGU!3PrueYf^f+K}zB{npmusQnq$3u5id zz9o`miXfJ_}tp(1d6Y<;OitV_* zr_4{7{5wU4?6Yz;DMY?AaGje^bgr@z+5bJ1k;M9~to-+VB(W6#(MbQSlmwA4B;xk{ z=d8=|AJ)FOzW-GGr?`H9aUW%5PpW;r-+e253a&))3ArC;@i!Ymvrjy?FXDD8E+0Dk zEHi3nBl~`fx_?O0{INL_8#v9yU&3wNq9$B1-9JAASM2KkS>zVqn!gs`@{=piQRDkC z^^0;YWc&-v{L_yLnSaF1KQT8u#LYh`{#g4*`hB^I*Jqo*+Fxk@Fojk3lB``$o!I<;fUDH} zx}5yI=zm{;75nzvkWEaf* zyA~O5;~;MSjky&SxAiH-H}daey?!e`uEWMz437J6^Y;j>MVc$&>fagCwIy8R`*QdJ z_!J)HFd@@iy&B#I#O+%l@z=upCH&E@uj-$T?>lJ2#Ku?L#y93xRQu~UQAYl{mrTny zJ@U`nNk0RK`)B?Ye}MFJC0z6C!|=BixapUF&HgFNP@V*N*8Pov?o{%id*`?SY0vG@|V z_=>r;qvxy3&@bE6cgr_DvTr+l3&)iIz7@8Yw6(ycPa-b+jGLZgF!fpAFPi<*Z({bp zEYw$i%^*j!bA|IGl##9-Bt3)2qW?b5jc|#|SUs1)$4Qv_SQNA64!GH;`X;7F&)<*s z`gGj`d4P3>g)Y^+5K7k-RPI^ zQu;p_*{}FBG5f{UM@foH|DVE(^TSo(L;Nq{Qju!^GXNXkEmn!6wJ&b%r?~Wgkup{7 zU-b3m?W_J%;kNEryJm{`rSK_^Df=&jnf>aciRlqH`%_%{zlAbo_J3QXKjnXM)1Ttf zf3!ls_|0(Hmg?V$4cZrfEZe^$YF{yKV(XK*t*gDCWojH#{uRF;zF5J38-A>W%l{uG-3-_Gn_Zkbk^Vo0uW~GF|DWM^ zmT>v!DbL8VHMrLHqW()^7XRvliH(oA#z+!#(<82Xkj5^>wLj7OqvHBy_cz^tUVx6L zaoj;F-haLswuf{87Wd!kYwWbYDUOeE8-MY0B#HFQkT#QKU)A$Rjkc@e>m=O#w}vhg z^N+a2*P20&#+sX7QbhAMvXA5&ue#9~)ndPoE@_A7@CoZNv59aiOjjPo9BK~yvJr#Tl z{9Pqn_pj~nKPcf^-*&+tF5%iAUIV`v!?#eTXuqBxZiLSOj>C(;-_iZ!7Wg@Ms(;rhyY5j%{o=2HzpI2#!+!++KDfrTSYP~&@b_2nH^Dzt!ZrWj3jat2*ZuzwD!A_d ze_X=#{@_>9|EC=Jx6Xd?--Z8K1^-L`&c)#4Ube zZuW~?{G|B5b^O@)X#Cemk}qfc7bE{1ceVY8^U@Gm4A+tBwx<>bm`yANkx$k$@jvh{prM~@z=v`{@OE*3flOp?wf=D z)cD*(!MDN1QuFJbk^aq69K`g?{tpEGYTxp$oaq-!>HpB9)Gz-$M3Vkg|Nc7Culu`+ z*{Syj=jo3>k+{D3a+>^$;bJjYdC5lYKZUwBKB@MlKh?fd&c~x|{{>O|$4x0my6r^s zcQ*8Ymh*UwRY3Qr1-O{%SUpaih}Yq}xhms*_?{9zP5vw3a~xCt75_o_wIy8k{}}v0 z1^;Py)%tZmylnka`@aL%-oj#-Qy+H4!`3(PKPO3IE?-GBK7U6#$N6!R z<#X!f3oz3^W0@SSocjN?XM2$HpZFHISQ)<>UN(PJe?MHYTUNgY-(10OhCi)>-wMB^ zg1-{ZG%jubweYD5{$}`=5Zjs=3w6!EZ?i>>vgTi{PrE21d4MDvV$uAWpYrShK#^AbRF>Tfm+qo|#fQe{KDc6xgBSWE`Qp@gd^pX;#z*!&LrM_owtSbt zhxj+at?d-Q7G4$qH^Hmo|1{iuSJV%3{nPrC>fiTJz~WCjOiYjJ-=hyC)jy&8_^9gN z&%w+3r~2~$y8Z?K-%kOvuO=gcYzjw>&x2BeOpz>K*AmUIPr^A&*e`n|pzL#eY~fhe zzenI(OSt^^7KvGr|=bJ@Qo9BnKu{7 zMf`uk#me|a-{|9`j6VxLQ&E2he71y>oz>vSb;M|;SY%%dUgMZ*e*n9i^fv0LZ&uyn z>t5J9N&4+oivK3;k4bvJn&NuC_!Rs-9Ql{)=hVxVvHwSw$5CY^x<6b(8Hs$PHewO~ zMz~mttN+i3ALp3rzwFx%@57~ADVCSOEIuA9`);6(<23qzV_!3}uWe34SAGirPLf3S z=lL!N|H8hXBv!_M0{-a|zDWK%;WwjL_80XlzRZ7X5j>x=5^=pBlEmDKs(3 zXZfxIANu}Szwhk>RK~c)ub$oBhmKJRpQeu1msPmxoJVpJPCiUsvtRu)v35QKf0!h> zfMogBzVv?qE>_0XzRIWCzu>v!_NORgV(n=CdM0HgDgCnla(GqyyWwg(RbTe&{Vg460|Lc4vaJ~Q0_$V)~f9(G=H~*Ra z#r7X79?!41|K>l%zs2vl;1eBe{)yY~b7F4esQuA_5FgTM`KC(oc@s(fol5XqVfy=o zRhaCv`tpYa_1Rg<+{rQKVkURP*SJt!t8Yw4J%7C-uoV9p>c0}MWhN{&`9%MDnEA(i zMzOpd-9H|njAWLyStpT9bN)g2EXS1n;)?${j*1QQyDc<-OkLSf6zP&Y|1$3*e>MIz z{xZ1P-yh@Le(Ao2GLqEzsegJuA(paVyiI+r^JRP$uJJG9uY})R!T%Hdcm@A;c%y>r z{ljtv{|xopCH#NG%zx)7V`6cn{$Kn&pS#9k%Ae++;!lIGaZL4J<39y|RS8%97r^hT z;EJESi=3F3TuoiGf3weWpRxTfrHmwHpW^mLV zmj@*UOpp5i>R{g*=ToFu#9s$r+dBcT>2Fs>)!u{u{wxu^N(yWF*{YiO&Lk5eeu(9 zu`>Qj_(c``2jFG?c_Ym1n@z-*xMEO};u`;VQbw^kMH%at%1hM$cavt|srY&jX6?`9 zH;?gsL|ordNMdg7$i9!09)`<4%Xby{_}fUo-l3V;skrHnx#>~gO~3kR`KD)DC(kFL zFf~5nijNE68Ut%LLzm=Y>RSKrbvgdS%8A?eGch+i6d%u`%sGy6`xkjN?)}*d;IFOV zJK%39;b#9m1#b4Mec5mAZwuvW95<7yIQah=xBkWL$NC>@ZvHE4|FPox89R#~`P;5u2H%^&fq`K|VCeyjbWey#iGW%|FIo$vl%As4v+;{T>h|6TJ7GyV1a<1+n? z_*c2Vl)lLQ_W-^+(_j5QK68It>i&ZE;!J;M_QXto?f0Zif75mTCjD~cf=&8;>4lrl z=lr})`mN8VP5MQ8wn@KR-gKT;zw^)28kL==mEgSd)Svf|YO6Ot!}Hc(#bmYRd=kdE)MG5;mO{hlikjiqPBXlKDzSCY^m2?>2znpaeFvkk?rm5 z+_})Nk6QIM?Tvba{n@D3&-(SoOm?C-8udCl-=6Jmw|h%x|2FYV}5=)jgGkqP5Xrv3{c6%ue@OjjXc(;-cg0o#^%2S?9Vl-QmGz zr#Cp8b@r^BJlPx^88#dAq1LWbPybd-`B~I9yn&qp02LHtU0# ztlmAf($K@PYpR=Jlk)EEE&;V8Z<{um)RK?JKQ|!>~nT^?22j! zo5N+_?$EV9=#B7UubbiR<^X5TcN?vx=5S#&SXmk|#_lTXu5<#BSJ87#v)5^k24~gq z&Yk<T)4JL_@tlTxpL>FW1{Ay?NBwTY$dGo?aeynTqdQ9W}cn zwGfkQR(9?*8;*1p+Vfr9->tW=J8>&3#n2DYVQ*c>lqi;Jz4qBty{<3dr*7vclV?ZP zIdtM^b3j|2gRL$zp}ES6v3t)RW*{as2iKj{gw8ra!;rP(l#kKF^)p`I`kX^2_BBp5 zsdeatT2}*_db-Z_FzvkY*-;jY?h*^kbtg{_o2*z1D<>MQ(<;4C@ATWMn4cUvvAc0= zu-I2@iIZ7h!eCEwi@bsU^Qd3HST|dzx*f`9(d6vpGmchS>a3H#s&~PBSN*7G*Q{t3 z?XPQX#_EX+?@jBhO(F~&`yk3>hz518Px)pvxcmCUR-2&>%K@vopJBF0b!fMFxY^N4 zA3D9{JaEn7J-3Vot-M^gURUPixW0<)IeO(4i-Tq}JFL%I{S-HVR+k`Xb?IBHdx!30 zT-H~)tgqgY^+$uDFN~YqEswy-PwN!rCNS5k6K>o&J9g&g?SMF;QN3w)dz_zRWB*SU=TmjaC}XY|qiF+;F+Db5lqInsp{>A371-H%~3K zVG6GsFj#wg&66i{)nwJVzqu^#?Srsg;3nV6-Qrsv-$5AeV8+DS>L+Pfy|s9?M!UO> zJ!ePQHxA?G>dfS&_5soQ?HBaPEcQ++NORo!cITRnhlufjP!7XtfNBI#V>5Pj0W# zI(gP_plYkT-R>>yZsvN-rh0d&dCj1=qFvzLc8f)5elA;B+PTve-P;>9Gk3PoV*ltq zcAq*mXr97*^KgjvtZEd}E}mD_qmru-87_9L%#ixowu~^Dx})(X5hVmA=Z-Qe$`H zw0z*=t;NE{D(rWKN1NbKEFcHhlG@Y4-Z4qHkO8*cv6Bktux?L_6YiM-Zue|*n@e-{JiP+%ss_Z!=^4JdLyP44&)L>u3UvouEZn zQI_{zD{Lv}2CdW0d}$hHP4?5;Cl~WsBtq92tf>63*{}O$ZL!y8fn&wW;?|r2aYo8Y zfBR|o&Sc9x`>gAPvxQ03I(f3&WQW}d%MvzuXSIgAmzJ7p-p#S16JFY*w=zV}#uL`% zWUe(Bg~fZ)TL*i7$)T65z_lIK32dA2&S~wof22Dl&>85+> zp?NP&@p68v>@e4fHbygyjThiaT>H;-|B zY#*JWvUS9{(RE~;ooM6w1zB*Tn)9dMK0f8Gjk{Qs-fI<0D&?*Y5MB9&%EdvM$HDqs z#cL-n>%wy~NL+OnTyCH4`mnHcLdUQZ-dASDBnOb2;=k4#6_OZZdBV@9KYl>Q);^V0`EKOZd-S#??=0HsNTV4xh7xhP~Wx#*$U4f ze*2PV9qPMinDZMF>~Ic0Snu-a$Rp+CRoJa7Kv-Sf8W8GRG{vEHs6QX>8BUA$YG}Z= zz`^iv*tfdWUKz3i@@V6-{BGYlA|GxCc+7Ff?K~0>-CC^2-~4^w9=%}2E%8uK&OlAA zFx5KjJlijvoU)U3iS^sU5UejNI8Qt2YZzvPhuYjB97n$_sv9g8i1A@v_DjS6(;!MMZR z7J6CRJ@EJa9+prgz+mH@+n5Bi#^_dURuV>p_bF~a6z_P67Cj2uJP&VxTfDf5y5rso z=XtI_lUaDd#xbfkk>#Ve)wN7*p7QfizA3dVr$%b8_FKEKYFHfV4NxQOXuJ#C&PVS2 z7!J$_^v3vtJxIQl$;MlptnUNLZCRob@mofG=J#W6l>Assj${~xJX6oLY@6ILa#TIt za?N0ZQ{Buuyq&WC`c{>5sL2bT+o*?(JYdV|t z{G0HRz7bo1vhMN9gBSLE4WV5rvsu~uRwuK2CvNqNs;d)jY6AUr!|Z_uMbE(t4-Z*o~3%Hc>V7Ey?bTU({$uYha-KS8i)Ft&|S{; z&UEvPy(kr7mYeQH z@`UYq;e{1i>FS>5`?TO56u2QYyQfCHP4?H?**CSo&^$XL-9^+X-3fI)H|X)5tjex* zPx+Tbd+VJOygp@=6s&ettQ+MA9Ry;F?ayQrmg&pnw!5Brvu|iMWbMK0=Jht(z0%69 z-80~IlkF{0eQllBu!X#ZQB#|z#U2|O_qDHU(%x72_R*a;z1SLr*GdQKBlj$Hj8!t6 z-lzqW5#$v$FFD7LN;SSO<`wicgZgRrZqN5Vf08Oc;fv^9bclQ(MOX2t6yhY08{*tTOkUwqF{L;^Qy(-I>1?yjevelx zgiE0A_QkuJ+W7GpqUvel8qoIygQkC&4y6@c2YPiqRlB}PLzK60d;ChuDGA$Sy>bpc zQyuAui_-l=di&jOF>wV6tyXxy)oiRoJR=lK^WMU}!dqW@uGw`jzqNnLjo8gY^7Q_Q;d()+VCWFKIlfGti2U1!eFRClindD)$o&J=B*|#z6`-C6b;D z-OCqgckNoCQ@kNkAT}Elx$fd{_=SX<{~^2zW@`D>vhE}1%mR;rPO;^?o%VGD_n^?Y zwxzXirL8ZMU+T86`UX41+<4bSJT|oQtOF{2xPEqtH{MRKS4N!sJk!l>^$btt6GdE( z73f@BA9{;IO$_tjd25=weZbh7a9=x^PckMh_U{`E-1D!G7r#R!AeU}CD_0b&`x{ws z`MQ3Mmk-x=xXhJ>fiGsu*Jd2wz}sNuejB@17P&=aZ@ukakuuHniypmjalQ_!z2EZ@ z@nJI=HN&g&>x5SP!tTmVS5AHXd@@d|VzRY*51mlA{DrBxW4pH%4c~|}BLgjbv zY#!Ww?3P1&ZrOL}n)yTfZdttU@byRb-Lm({;iLP=TRc3!Z{a0l-|pp6Woelgu`7f6 z(%FfxPQo~kb?auTm+ z0W`w94%Xz#QtXb|!}x;k=D@ArH=BFHZPR=_xQ8bkxnFl||0IP|EjQY0<(0iHA)502 zQtTWi&T!P?O>X@5J+B|OabCfZzBQ#AXE@J`x=_hu%lp`GC$ZSuk#7sfqcz9NbnTG! zy7sCYd^>z`(q&z$4;Fa;%f`%IA84K&5unSXkSSiw^EY4jMWnCfPgT^N<+kqT@r65Q zw`)Jn@eP|_LX%;?rt0Bz_iDSjkblz<^+MAm^!=(E7;}21+fJdCLIqbcs#(B`b-l20 zZdDYw$M{gsH^QcTuCIdt>HQhDSmDHnC zRXx*_jnb?H;;t)!mHk8wIt#e65hW}>qL?)ZxcDSUTwkcOK6tq3;saC`b$`Eee!r?y z)#rA#m<2f>s_(hy|GoeJ`Tx)VoO|xA%5ZSc(Vaz6T!c?3m*DS>H^1PT3XV@6v=+t1 zLAv^nktawriTiSKPg|`jv{>K2S|>Lp?rSFK`?_^UiZx>@dB zC-)X{W#hp)z+!#(iTAkbG(!06{yiW8^D38r?_H-|@0%V@?B?S4I~jes`;7DIqvF}| zTweKp^ht4KI4_gScxmzJjNRP)^>{r|>j1_nY7#^?rYJQyMO#w$sf>h8eGn<*XQ?$E5<~}Pf9S@GZi~WAI$HV<|oGUPx^Vj`woTt!`i}Ta8aBbF+ z`l`v1~j%KduwU@yO}(dT^t-9pE@`95+-$sY&j5yWK~R$L-?M@t|!N>s{}7 ze2?_KSx5Si^TYSI9}`C(*eBbweRDq}?y?Eod&ON|!Z{Bm4zQ6YJiV zu}gnl{;767z6Yh32Y#%_dXGwO`)>D9;-aof8W-M^T&#D!aanKw%>5hLXG~bNJ==ec zAV}9+(H?e*a&|)A8ln>temu_r%0e_D`1^AM87yB908l#Cisx zDqkV)DPqdFE5tpug!6tV^dM3q-a{<+C7wrDi6fJnM~>ePaa`}YCRiWq zEH9D#6*^9%EysC(&=R*9+(`VakV;X`h7z8K&vt)&f?b>VIdKes zZrr8>I-fl^+%M}f_W|iE+vhmWkBDRbR8xNHT3i&D<|JRXr6 zGEb8pbCkc$5RP6~&G~)EIOjN;^ZN_-CDmun>sm$jE8zCwV>zm!OzHSMaSJ$ST*f)) z!Slr}g1IE&SZDEi|6*~=Xv^ulGtfn`OWYke-;B?=-2;-pT*vcZa(2C*I?ntaiMl*i zJo4l9+1y@nID}%}=jL{!fjQ{H_pE98r*iAH`)G3u=rqT}W!#Uo@}Ac@5A5$E+NPz9 zAz_^{^mDB_BIo%>#d&Wo&HM9)aCAPHOY=U*P3hXx%9jex*>CHSc<}zdCvI~I=X_Za zw*?&iv_95ZysrH_ag0Nb8{bFpe#tf3`cNGyZm13LEP>M+<9^PCU8F@?!W}@ zr^Hn!aK3*WE#aK^@0C8@kJd9kY19lEak0Kni@Ur;pYL0LFRoIe&)gGe z3}p*GYuSBj3tE$g)F{CkIZ z_A6(1n>zec;>zs)V#e-1UCV?0b^d)QvCFFEZnhs6)W8GdXAad+9G5>6S2jP)eMa0C zT%C)b<5E06>c0|)TwKgOMI7Ul@|o+GHn6CB3# z&v9`M^2pCO%U+tN9wUec?<35m>uJi_AFq=h-}BHhjgRH1hT?oVEc*|mbss+C-0@8R zIBqWHJujQ$DbLN{p5!Y!nz0`1HTODkyswZc<9yxt(S7{^>G3*K#=TSA)HtAX>-%MK z>9{d|F7D^}^CjiX15g}BWVxX+3!o4?MN zi?}eOF!wIO$NgiS#r?Zf9OrM&506_#+>5|DXQ;=;P*PP#vH%mB=M^*Y3 zCvdM2*Dv90_msHh5^ntYZ3WyV_}E|8S)5lM5Bsqxio;sUdQbM$yz0Ohv8=327nh6q15uPz-*jvWf89@W)=OcIyG;6~bfnD+KGuV3DCSbT70Gxo@87L| zjr38*wbyc(mi(&jyoQ{}v=q{WuTS#d^)zZ_3Xb z!qJ@lyyzF*u9aL(17zWc--n4s?`#8oHgd%w7wO7waC z`-r#`6S%(+H$Q>9m`~2E$+J}aJP%QyM&uUJIi;8`ySMD{+fG>xO+-C&$pivcQ3fyd^7hx zaX(PP{hH*Scfdvp`^w^VBc84PYwmZXFP-NPCAjm!T)Ix?IQK7IC(lEl^;q@CWcROi zydQ$xeQP6cEGUk5JRAPCA5RzOI1Y(eGxuC^slSy3c0Zl>SBob?9hUnNbGyWmS&KU& zuI&D6KfFI>cyjq-``;>kW%pHcU2%*LeHi#b&2BlSDcqT zjWcz+*uM4ssCdeA@iX@>ab81R&v8B}edodC z;%u%h?yVEJSBiUE33rd=jyJH8;`rSsE{)&ugtR}_=Y1lP+_>4^PfIVE5soJ)-XHw3 zIQEBg#P-OrnEP#U9E%+HamoKd#|pUI`to>n@L4)?e&o34O8#OUr%Sl~lHaW3wi0d% z?JT#5+kyI8dC(WPdjj_war-84-z)Aw3HJ`k{hU_?my64T;?jKC4A#Zt_v_-^K7OR{ zERM@Z6FwIgbN?WY%v#*jnJ6gqA;;OApHrAqBYDb;Q9Li7FMT}Z^qKR%)4%6(Jc6=B zpZA?*_c!bFzO!t7cAR}bufPUvj@zA4|I=}s2IpdaR-E&I^0o57>mQlT_*f6GW0{JF z!vEdVl{nAEZ!Xh6>U6PQ$K{-O%5(b6eVaHkIqu(P>?W!Q{#f5Vi7=x=k_J$ro@rSan6IU z6UX&*8Xx^!XV4js+g0KgAUToYw(5#+6!!s~bIy(HGq+RRg%Xb6TeIvJ_qV7sq>e9T zEY^2S9RGXjT)s?2ytqZ&r6ruO;*7XW;ON7+ABE)KrsLB~xUEw7YH?p#V%PcdMsXEz z_}?mjou@x2ZmNW{ANPy9qJ;Yy$+yQX8@ipi5pgMv3JLx+*fxDFd;R4F>3EWqSyJ-UVjp9yB;0}tLFX5aA zH;Y>UhyRu4OH*80T>S@elkWSTZ|QxX^UZp!*Zah$@;{tGxf=lQapqWO znUeg)I^I*lT_^ceylmB9-;Zt(m-@@~-o^HB7I%w|lxO>=3vUxgCYM*Ql>AN|xwho= z-7Wc~{+Y8s>_*Oy?_Z}rbJkm?@9i0VRl+KcpE>KLILG<>j|Zi<0&W@~>p?Y?_h;-@ z63A6^sofms_lX}&>^=lb))TE3h4&@DE$#vYxqXT63x6i=bKr9K1#=tu0Dy7{4CXkm zSI-c48Ms_tO+_`umE!mtinX{M;>zyl*Grz}-@~%O1Js9NF3rCj=l3r+NpA&L-;a;= z1ZDU$m$7@dg$ZviwVUJo-l8YHoEygzJyz}Uc%`^S2y)|L?z_d^QNsB>$Pb9S3taBG z@jl7ZI9J^%933BXslPdHW>dV_>nZ)6PV_|Sa9`rQ+6O@{U(7qN4oH{#Wx1a(e^&k+ zEa84LF;?cu^#vD(;2&ri;H=4soflR(bM9Un{=ez+&Vrb z1J4py!Fevu=AI{Ra|!2t(+kCI0mr_(|EPwtTk~JuB{L31_<=w;kZ&r7GnV^gpT6e$=kL|tC2ksA1s~f( zHIxT4eyk*r{Vy%Hj5qth*gLO2BYkKZDR=Aa?o2?a~=3F1^ zFEN+S-yFBz@%Y#3-;9pT``kSDzSH~3X>bqY<9^wH=jmYm1CZ1Ab-G}`>~cSzn@83+ zDer5UI6vS{;pjMxLrt`@VJ>v$1UZ2iMezg&vEM=w=dTH(s_D=;xVgBW?qB-cQvu}ljD2A7 z`t0YP3fic{dQc6;>xQ3urqG&OpKaIAJzK!#{N?p5e(sq@eXYFmbI&y;ob5U;6>#*y z{Bd>!#q-=8+i2Uxe*51W{sWB%<;(@k&71p}I5IiT{{D@)3b>rV7hfJVKU2rD_<6lg z{iw!E!(Xqb=F+@RIs4=H7FWtHeVE3_a;vr;C_mFQEM*-RKxHKhqd&u)a4dxMhBrdyniA&)NM@#$Ud_cd@_b93RSaobCRh^fGoiyXHP2j)xrg zsZ9Si)3}QJXD;nuj$@yXK3UQSZ?n=qid88184%=UI zslPeScE4GA%lzGwv6~xTbE(}NXS+8|uxsw5xUx7eth0aSzR>+!$@DKB53l3q()(7* zd3@KqzrRa%{d>HSB*`DQ1p?+?W-qjjzReNNmRC7k2@jAusuD>&r7x4J^|bp9^JOTu60 zfjQ>`g(+u$%z2-{T*-0X$NGIv1zc`l;{Cz2`nd(%HhjkS2j3+5J{?b#=yRTUU%XJl zIjv|>k1z9TMM3#|^6Io7ku4UKz&J!h^J)fN~X+Aq&KA(L46UBK76^t(12_S0Jbu2P*TwO@Xf7`PeNIo@+rjbKaon!C*NWpi z!W=h+Ygz6U_damBb;I0S#PNONTHKF_`;`*T>(#r&mA$t$=lx&Vds}nf|CPO;^!vp3 z%I-(dKm2c1{LKA^xZj(=eL~#tPvHJW+#i&1kLRGHc->%qnSWHgo|;S7)0Cr6EauYn zG{G627;={F+KH5+E%3{v%fp0?_W9GPdgY5Osaprn;LCQX#_4x|*YtlY; zTV{M$VB5w1m`m5^6+<|h^E&xx?PJI15#yYj=l0k8gFd+2{@|q|il?jontM}b+}JM{=au!m zBe9#ymwR==Pl_XxTQ}aFvAfAeakO3g`@zI6`)9c?8MjL&XZP11WxI~^N2Ql`b8-1U z>$L0liI?yn9#TpDWu3+2@f2}ocE|fy_C3Tiq{n$m8)RJ^Z`)1#m-E-0`$s0njrXt2 zkLgVRPPlV8x_@4;_9pS$0ePBRyv+NEmx&`o9oBJk6^L1E}Kr zg1J8yPjSlm66e8Ri+c#%34AO^HI$9dj>pvQv}8P(cl@3yp0eC{n0uZ$GHY=!5?7Yb z)^|W$*}OD&OdRugExRYhT{eMR6nFUq?k;g$zSgq)ec~z;xOa%#T*6JEoyB>z1@&BB z{dehE@A$6w|Ih0~(o0|0^4I^rEtAIgx1?{<`2L=_N#pyc;wFvnXT?n#-zR+)qaEKx ztmW_J;woU*;;s<4c>?zh;yAu*>2n@jInHT)`9|qU^I!*9m+^W2Qt_0pW%rmkGHY=) zag*}kHR2}a!4HXJ9&qf&-y?Z``1hTY;`cMsw<>BpZIK%?~SuPTo=0PP$ z;vWtVi~XorG1_R;axyIDt`_IMm2ro}O`5+oag*ln9pXF|<8~z;#D(j4E-v3GFpUd! zx>&FEz1bB2xwu$-pDL5$^54Xj#l`wO&sW7o=%iI}aYQVzgHhxv;|P z#-EBK!#-LM>nv9DSyi^6ZLPRmd_}mdxUz)vcsxxU=j2*;pCgXz-&%IB5%;1A+)Kpm zDB+y<^Wt`waE{B0xP242H;FqifqS>O>IBaDauginw-KLERPp#aUn&ri+ky|{Nc}7x z4|5(5$J^Y8)h~0rwlXj}%p1q=e~Tx#2_M=_i@CoQM`kVVNzaK_S0-@J5jQ=7yH?ze z5)RFw920i}XN#B|{G`Qxbi`F5%=xh_`5ScP_c$C6t7DzzzexUW9r^uCj{9ZFe_zK7 zC7hqTKPm3x;AoSdv`p#5dG$#Ej3L*fP&C$$-5gKTJm?GI!Fqi^f3|qasBb(E%zdpm zGC9t1{swU;z-`Ltv%fDCwp{z^q8Op-|;z*_3t*aIputb!0mz{OYKIOpbekt%@wl<>So-~Q%BA{_QyI#? z6wc#ef6My!h2lKV9Y4k@jbD`wD4v%o_fU|;KOSdu>F!d!E}aOl-&Ov^La4z!}?ffah|?X{M*o$%liQ1&l|+O9d+umKFfXd`;8x+!2Ou? z{TMhdt%K!N>(!4-p2pc@KwGx&`}8k}r)&p4n^;MT{r!-*9gyYxHRt)e8(ePw{=W3M zpNXkZrEZpQcy2seZ#*0RHFv$Z)L)LVi~Y5|x_HVN7t75%E`E5a zwK6;nHFM7tw}RGO{HAo>bH%;7gtNYBad%DNoX_7i&S}2ANP1F#H>oc?jQd*^Pg%|n z=l#pYk;!rPcR?K2ryNH+EcSzQ(mI1ce%z7qW6*hk<9@t8@nZ$@3O;FZoWEb(3WWSr z@JY)LXY6iLpUBy+=lO$)-Ni(1ozB1aizCDQvmSFEw_mY7035e*&T)A}+$I9}jB~#y zd71|m8%y*$E`MjckmbhB+~c=IB_6YJyXKxPt}GACT_w(WP^Rx%ag51Y{o60@vJ!48 zc7O2iM0?*-X4iSTYM-c_n3cUW&g`R$_1fRAc*@uE;|_6?;(V95OD5?1kK)SW{1(Ym zKXzpD-kj$N{m607zjsS7=THT#(vyzwg&>K4%$xJRm(mLn34h&B=ks%<_aR6x!N+peSbFzhh6#ty!FEc4}-oR-d~X{~^J-;`VrQO>q{L>z9IX zrnA(Z?_FKA8tq1Fsa3RU%i&mEKH9Cdd#5_xR&g8tbJgnd!CL?7tBbi#bE(yy)^V$j zR~5Tj+qcgy*7}WF6ZZO@?%tx`Su7T7^V7x2PQTx2vA(s~*=%;^YLFMR3$^*q*_lTF zU~REzg+DiTy7Rlxp?=XCa;<7@e!kH@T?noAyGLs$oAu&Mr!il&W+B2!D_(c9(`gp1 z!)M#QgY{OYd#-5BES)-4@7~a>&%=jmv(~N`?Sl1Pop!I^ts!jt+h-2edcE1X8Wc4f z?Ru>{UDVpAmzu1^hOKVcic4o|z53o}?Q{?IqvsauYGNnabKQEs9vWLi>`47o=wIk} z%V)UkV7<2x?e>E8Zl{kJcG?A^UGE~8``h!4xq5H5-(8yPV~oRD(Ozmvz^L-`f@`4d zx_YNo?|0A9{Pyj8K%o(RI#UmZERJ1|@n0=1T5@S9!YEru(L#@-0$Am^?Co}1u_0ct zZ>ibbUF+A11y@clF74{Hm>e9oT#pVMP%Ls?Jc>AQj2SD-fv3$yKVgay3Huwm(;AH^ zGCf8(3}SM*O`uOPMEmNE(+mB*je2w5GMG zkah*}J%0tbEiA) z2*e58I)*8tEKG1{77du}Z!;;aS!!mRD)Tv$_>(=bj$@#_^(zCVJ zVv`pRDu+()oWDgqju$p4M`LXc9gdPgk75Vo-(ztV!|K^M-EM&`lF8W`%s5!Zf@de= zr7;Bi+w`MWT(`tow713;8oeGliP5yPoP=LSV*PF&>ddn_)P=-D=%-`I*cYwPRLpjkx^rQH zi_xailK`njOH&Uw_TdTc`nZiSlDLnz`{>ks%zTc3U4^h^ z1i5Mz!WJPj$Ec=So4d6>e|>QP_w;6rs3%|H9vrNlZa4Z%^Yvop*fn9eLfVBX!~rc@ zBUc|fsqpQ`RkUG+hr1Z8U7h-=Qv=m#s^R_?nsA5jYSVyQeQOYwXmxl8!Emb^BWr7% zq*}eXcs2Sv+w(K$`sm*>rBNMxE&*M&nJGHQ*^Q;S-B&~|kyIIUJ3ONXK^NmyI;x?+fyff_Kw8z=q!ECCv=jzvWJ4@UP?rJu$2<_ip%+77!9?)H# zZoLS#*#>rz;m^*~r@Qsjh~EC)*pbz{@hZ&yq3&3Z+odrpRDGmzx`7=bRP$_>d!xB< zT3zl9W!QFsIN6y$cNKP!hzK^oyl@6BaYS1C=leXnDwr;Mo#vT(cRyCAu-OVe_jI?1 zW|Bjd@shc@`JMA;7=w^+4J=$(h2!4vSRH%XMn5bv)p~oTQR{iPI1pjr`n9?Kk@_O` z?ZdIdva~a9G)>Ol#Bp$Is#s zX1d_tt>N!g;qTQ*$sYDgiwG@J6?aZOTAkL8ojYvihqe*eo)K2^ruFb>;{|CR+xqyF z_=M2fxwwd>V}5T3w>vBZ@eJB;ThfEU;QDquJVu6RDpga1)ozFTM%EBD_u}&@c#G z1b3gq0?93;q8i(1Ele*UNbOX?D&Q3wZWfUgZ^)|vTd>fzM~=RGy0%lR$rm*&BI=hXgr?7xCJF3F*5UXhD=_peyQHmwD{ zb0KKhdju`vmR9PwyVmJ7LV#jV2g-ZUiAB>X!H4H2b}$%8XG7A|I}0}0)pR@D^cqFjT`f-#mubqwoE*xY&+_Fa}44%^4fl&$0(Eqq$gp`LJ5W+X=QGrQUta zrNP+&WY^$?T$+@FJXqbzd~Ky{NYBwE373cD3j6fn!|dEi9<>w08SXtyzfhaS0~i)0 z9q44YgQ#Iy#mardw&y|A$6Co<5Ckn<_!kj3y07$T)4ub ztCbj+hc{@0Mcb!ZwOcdY&aL$}H7hd7(l)#}9k8SqX>%B~q7@IJM}rL{24PrljhqbH zu^Z)f77y9nUSj6X)|+_i)@*XmduS2f`HrHoO4@dIyETqX@Dx{u`vTr~)WWvz&|)0z z)}dMpAsc9lR~=gPcA!|oGf3RN44MuthHTg!Hzeps2>8KT8;_27q#QldZk-8&)itaE zy58B8rq-dw{kmraEitMx;4QE^uEUO5I|%TY6OLQ)NZbo+ zF&}>i@B3!>niXM5hkA4da%yR+wXpMSK7Vw|j$Vsgzg0u9nx(KuV}6=Im!iV)hB!P? zxT`j|P{)f4Ze_-LN`-iO#`F7_Knb|NeUk46bXE=b8H_q_GO;hM0|zuxDZ0CZ2h(WW zib6N7K>eZ`b75VIIlz478Wc8c+5+FkRi0hhKVQt=wtaiGyVS;n#!7^D!AsajV)HZJ zl0<1R6EHHI5*pH85xN&_%w2U7cbVkvcXfI93!<2}(3n!AZG6z2tA|?zk}R;{1a4w> zV6W1{lh_h&Kf_0ixwy9-+?UeJFq!JbIjn1VGkkkIq~hv1XvJ&(NMOI^8eY~!oNcP1 zZE!7L;>S*&sWrpqQe7Pgs3XIxuJW@Y{=kb0$sEWZE zX$mn7cg?{n9nQk0DE20tUx&^e!EK>aG{Xb`V%)xsiMllu-NMaErW1Of z681ysju+X&M?ueXy#a3E#ZBT)cq=?;3;v8YAqpPHeiB^i;pWkk&4 zek_bq9E;JB)F=!Z`CN-_Qy4}Zc^z-L?l6**ZWb-PowC2tDq9ZK@xo`&eXIaz<9rwr zO8?N%D-;sG`J2PafJBTDL-4BJgeHSB+7?D-w5izJSgyx)E=Y%`&^Q~Ob>poP>koE8 z_=;(yF+OXyw{OSYtZ@oYkbAIeI#+b!oA5rr7n_9?>mFWt;DtTDhTyIgvzg6%mPeaA zCvS<1YPd!>w!u~0cXbCUZf&%q65jv^-2Qp40mteKjk#uhH`g%Sd1FE%vfR||#IscA zG+w{Qc<)^5cQ_q!#={MZcxvqN%ffKFyK}ZZXxO#X5U8Z@FIDn?*n*t30B&?r$wJ;!-E2D2=(^qKHetB zbMEZxTwq`xJ0jji=oIgSJm1~z;G0>pOYPI~CDE>0>m**EVw9MyJ{!L_X_pQpVguWs zQ4?;A%%JbVv)Jt$8}sb#!NdFcHafi08uWIii`PxwTSE1|8eYSWwaxbH+&mraU?UU0 z{0%1kz9QO(?t$r}jjmoR9jNugv(Ry@lB&Lp3noSouc+~obNHxK#kar zi{1~Oq{hX5xpWL0@5UmoTIAb(#%EpHGna>pm$zaKS?ck8|Ja>vQDmZ0MG0)G|y7NcjY`uy79$u{=T_g>+FTAU%4j+#p;pKTrFu?B#y7l-l zEt=VNpi|>hb?}WElD65~(pOSJiMGdl;~hTd=0=U0jF zj4)2)y+wG1x0>EvZ-LEu@eu~NAP4ZQ`2@obQVJtj@UB~vSfHo5cLKlGTMb` z-9s}Rw?il6W!LcgAZ+5Wp2Y^NE5kk@o{%Qz37$5G4d6fpzTv?4M}FiP?AAuIH7;p* zQpZ3uKW5p0uQ)LQ@jw-RsKJN&aw*1v3OOaRk(7IbMLL`ZD{u;Lh?t1=`H|LeazwwA z2=ia*HP%fozg*U1#+;qSV_;D1_OR1F+zk&3^ZOcH`<9ygGWjK8`^s;ym2<;g6Y_DM zE6*wc=_9psb9mz&^hRaK^VnwHxUHVWQ~3xIS`4)I)q1f4A`)`$)^iNS_~r3NrjW1lYq)#_+d3U6)4-26GuG}LzJd2( z4dONoEqfFekzKWBctwh7#^3eug-eLFRL8gvMkJ=qXx8+u8JuUdA}-inh3SfFynZkl zN7Z2@`Rx(3@jC?0twXrqaxc-2AinL#YeVm(kKjEg9(Mhm2_KWE^mPEqskPa;x5ppGIKGzz@6l+pFXr{_=XaV?pcyjNn}d*5oxQ?2fUAiD0-n;MO0_ z4k8h@X)zzd!xI9zmv`*=B!!^5HQ8$oE{=UsHVyVmX>c%cdi@68U!M1QXTD$QwojYW{uD#~QXa`@Mw6QMLy0duyi;Y=0 zKTto_M}jW&)kyhxTfF&(FCybbu}U=527TR$#~0i=+im|iCmMEKWK#PzUasoS#c6d&q@R^D*2BlOK6w8ivA p#FNcqvZkluKBRDu-VrfLN9H_!8Zy>2h++Wc^`RSw-3#q#_ +#include "foundation/PxSimpleTypes.h" +#include "PxPhysXConfig.h" + +struct PropertyOverride +{ + const char* TypeName; + const char* PropName; + const char* OverridePropName; + PropertyOverride( const char* tn, const char* pn, const char* opn ) + : TypeName( tn ) + , PropName( pn ) + , OverridePropName( opn ) + { + } +}; + +//Property overrides will output this exact property name instead of the general +//property name that would be used. The properties need to have no template arguments +//and exactly the same initialization as the classes they are overriding. +struct DisabledPropertyEntry +{ + const char* mTypeName; + const char* mPropertyName; + DisabledPropertyEntry( const char* inTypeName, const char* inValueName ) + : mTypeName( inTypeName ) + , mPropertyName( inValueName ) + { + } +}; + + +struct CustomProperty +{ + const char* mPropertyType; + const char* mTypeName; + const char* mValueStructDefinition; + const char* mValueStructInit; + + CustomProperty( const char* inTypeName, const char* inName, const char* inPropertyType, const char* valueStructDefinition, const char* valueStructInit ) + : mPropertyType( inPropertyType ) + , mTypeName( inTypeName ) + , mValueStructDefinition( valueStructDefinition ) + , mValueStructInit( valueStructInit ) + { + } +}; + +#endif // PX_EXTENSIONS_COMMON_H diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h b/src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h new file mode 100644 index 000000000..88dc86ac6 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h @@ -0,0 +1,186 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_PHYSICS_NXPHYSICSWITHEXTENSIONS_API +#define PX_PHYSICS_NXPHYSICSWITHEXTENSIONS_API + +#include "PxExtensionsCommon.h" + +//Property overrides will output this exact property name instead of the general +//property name that would be used. The properties need to have no template arguments +//and exactly the same initialization as the classes they are overriding. +static PropertyOverride gPropertyOverrides[] = { + PropertyOverride( "PxShape", "Geometry", "PxShapeGeometryProperty" ), + PropertyOverride( "PxShape", "Materials", "PxShapeMaterialsProperty" ), + PropertyOverride( "PxRigidActor", "Shapes", "PxRigidActorShapeCollection" ), + PropertyOverride( "PxArticulationBase", "Links", "PxArticulationLinkCollectionProp" ), +}; + +static DisabledPropertyEntry gDisabledProperties[] = { + DisabledPropertyEntry( "PxSceneLimits", "IsValid" ), + DisabledPropertyEntry( "PxSceneDesc", "TolerancesScale" ), + DisabledPropertyEntry( "PxSceneDesc", "IsValid" ), + DisabledPropertyEntry( "PxShape", "Actor" ), + DisabledPropertyEntry( "PxArticulationLink", "Articulation" ), + DisabledPropertyEntry( "PxArticulationJointBase", "ParentArticulationLink" ), + DisabledPropertyEntry( "PxArticulationJointBase", "ChildArticulationLink" ), + DisabledPropertyEntry( "PxArticulationBase", "Impl" ), + DisabledPropertyEntry( "PxArticulationJointBase", "Impl" ), + DisabledPropertyEntry( "PxRigidActor", "IsRigidActor" ), + DisabledPropertyEntry( "PxRigidActor", "ClassName" ), + DisabledPropertyEntry( "PxRigidStatic", "ClassName" ), + DisabledPropertyEntry( "PxRigidDynamic", "ClassName" ), + DisabledPropertyEntry( "PxRigidBody", "IsRigidBody" ), + DisabledPropertyEntry("PxRigidBody", "InternalIslandNodeIndex"), + DisabledPropertyEntry( "PxActor", "IsRigidStatic" ), + DisabledPropertyEntry( "PxActor", "Type" ), + DisabledPropertyEntry( "PxActor", "ClassName" ), + DisabledPropertyEntry( "PxActor", "IsRigidDynamic" ), + DisabledPropertyEntry( "PxActor", "IsArticulationLink" ), + DisabledPropertyEntry( "PxActor", "IsRigidActor" ), + DisabledPropertyEntry( "PxActor", "IsRigidBody" ), + DisabledPropertyEntry( "PxMeshScale", "Inverse" ), + DisabledPropertyEntry( "PxMeshScale", "IsIdentity" ), + DisabledPropertyEntry( "PxMeshScale", "IsValidForTriangleMesh" ), + DisabledPropertyEntry( "PxMeshScale", "IsValidForConvexMesh" ), + DisabledPropertyEntry( "PxGeometry", "Type" ), + DisabledPropertyEntry( "PxBoxGeometry", "IsValid" ), + DisabledPropertyEntry( "PxSphereGeometry", "IsValid" ), + DisabledPropertyEntry( "PxPlaneGeometry", "IsValid" ), + DisabledPropertyEntry( "PxCapsuleGeometry", "IsValid" ), + DisabledPropertyEntry( "PxConvexMeshGeometry", "IsValid" ), + DisabledPropertyEntry( "PxTriangleMeshGeometry", "IsValid" ), + DisabledPropertyEntry( "PxHeightFieldGeometry", "IsValid" ), + DisabledPropertyEntry( "PxJoint", "ClassName" ), + DisabledPropertyEntry( "PxDistanceJoint", "ClassName" ), + DisabledPropertyEntry( "PxContactJoint", "ClassName"), + DisabledPropertyEntry( "PxFixedJoint", "ClassName" ), + DisabledPropertyEntry( "PxRevoluteJoint", "ClassName" ), + DisabledPropertyEntry( "PxPrismaticJoint", "ClassName" ), + DisabledPropertyEntry( "PxSphericalJoint", "ClassName" ), + DisabledPropertyEntry( "PxD6Joint", "ClassName" ), + DisabledPropertyEntry( "PxJointLimitParameters", "IsValid" ), + DisabledPropertyEntry( "PxJointLimitParameters", "IsSoft" ), + DisabledPropertyEntry( "PxJointLinearLimit", "IsValid" ), + DisabledPropertyEntry( "PxJointLinearLimitPair", "IsValid" ), + DisabledPropertyEntry( "PxJointAngularLimitPair", "IsValid" ), + DisabledPropertyEntry( "PxJointLimitCone", "IsValid" ), + DisabledPropertyEntry( "PxJointLimitPyramid", "IsValid" ), + DisabledPropertyEntry( "PxD6JointDrive", "IsValid" ), + // PT: added this for PVD-315. It's a mystery to me why we don't need to do that here for PxConvexMeshDesc. Maybe because the convex desc is in the cooking lib. + DisabledPropertyEntry( "PxHeightFieldDesc", "IsValid" ), +// DisabledPropertyEntry( "PxConstraint", "IsValid" ), +// DisabledPropertyEntry( "PxTolerancesScale", "IsValid" ), +}; + +//Append these properties to this type. +static CustomProperty gCustomProperties[] = { +#define DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY( propName, propType, fieldName ) CustomProperty("PxSimulationStatistics", #propName, #propType, "PxU32 " #propName "[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT];", "PxMemCopy( "#propName ", inSource->"#fieldName", sizeof( "#propName" ) );" ) + DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY( NbDiscreteContactPairs, NbDiscreteContactPairsProperty, nbDiscreteContactPairs ), + DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY( NbModifiedContactPairs, NbModifiedContactPairsProperty, nbModifiedContactPairs), + DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY( NbCCDPairs, NbCCDPairsProperty, nbCCDPairs), + DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY( NbTriggerPairs, NbTriggerPairsProperty, nbTriggerPairs), +#undef DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY + CustomProperty( "PxSimulationStatistics", "NbShapes", "NbShapesProperty", "PxU32 NbShapes[PxGeometryType::eGEOMETRY_COUNT];", "PxMemCopy( NbShapes, inSource->nbShapes, sizeof( NbShapes ) );" ), + CustomProperty( "PxScene", "SimulationStatistics", "SimulationStatisticsProperty", "PxSimulationStatistics SimulationStatistics;", "inSource->getSimulationStatistics(SimulationStatistics);" ), +}; + +static const char* gImportantPhysXTypes[] = +{ + "PxRigidStatic", + "PxRigidDynamic", + "PxShape", + "PxArticulation", + "PxArticulationLink", + "PxMaterial", + "PxArticulationJointBase", + "PxArticulationJoint", + "PxArticulationJointReducedCoordinate", + "PxScene", + "PxPhysics", + "PxLowLevelArticulationFactory", + "PxHeightFieldDesc", + "PxMeshScale", + "PxConstraint", + "PxTolerancesScale", + "PxSimulationStatistics", + "PxSceneDesc", + "PxSceneLimits", + "PxgDynamicsMemoryConfig", + "PxBroadPhaseDesc", + "PxGeometry", + "PxBoxGeometry", + "PxCapsuleGeometry", + "PxConvexMeshGeometry", + "PxSphereGeometry", + "PxPlaneGeometry", + "PxTriangleMeshGeometry", + "PxHeightFieldGeometry", + "PxAggregate", + "PxPruningStructure", + //The mesh and heightfield buffers will need to be + //handled by hand; they are very unorthodox compared + //to the rest of the objects. +#if PX_SUPPORT_GPU_PHYSX + "PxgDynamicsMemoryConfig", +#endif +}; + +static const char* gExtensionPhysXTypes[] = +{ + "PxD6JointDrive", + "PxJointLinearLimit", + "PxJointLinearLimitPair", + "PxJointAngularLimitPair", + "PxJointLimitCone", + "PxJointLimitPyramid", + "PxD6Joint", + "PxDistanceJoint", + "PxContactJoint", + "PxFixedJoint", + "PxPrismaticJoint", + "PxRevoluteJoint", + "PxSphericalJoint", +}; + +//We absolutely never generate information about these types, even if types +//we do care about are derived from these types. +static const char* gAvoidedPhysXTypes[] = +{ + "PxSerializable", + "PxObservable", + "PxBase", + "PxArticulationReducedCoordinate", + "PxLowLevelArticulationFactory", + "PxBaseFlag::Enum", +}; + +#include "PxPhysicsAPI.h" +#include "extensions/PxExtensionsAPI.h" + +#endif diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h b/src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h new file mode 100644 index 000000000..0e0cfb1b7 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h @@ -0,0 +1,91 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_PHYSICS_NXPHYSICSWITHVEHICLEEXTENSIONS_API +#define PX_PHYSICS_NXPHYSICSWITHVEHICLEEXTENSIONS_API + +#include "PxExtensionsCommon.h" + +static DisabledPropertyEntry gDisabledProperties[] = { + DisabledPropertyEntry( "PxVehicleWheelsDynData", "MTireForceCalculators" ), + DisabledPropertyEntry( "PxVehicleWheelsDynData", "TireForceShaderData" ), + DisabledPropertyEntry( "PxVehicleWheelsDynData", "UserData" ), + DisabledPropertyEntry( "PxVehicleWheels", "MActor" ), +}; + +//Append these properties to this type. +static CustomProperty gCustomProperties[] = { +#define DEFINE_VEHICLETIREDATA_INDEXED_PROPERTY( propName, propType, fieldName ) CustomProperty("PxVehicleTireData", #propName, #propType, "PxReal " #propName "[3][2];", "PxMemCopy( "#propName ", inSource->"#fieldName", sizeof( "#propName" ) );" ) + DEFINE_VEHICLETIREDATA_INDEXED_PROPERTY( MFrictionVsSlipGraph, MFrictionVsSlipGraphProperty, mFrictionVsSlipGraph), +#undef DEFINE_VEHICLETIREDATA_INDEXED_PROPERTY + + CustomProperty( "PxVehicleEngineData", "MTorqueCurve", "MTorqueCurveProperty", "", "" ), +}; + +static const char* gUserPhysXTypes[] = +{ + "PxVehicleWheels", + "PxVehicleWheelsSimData", + "PxVehicleWheelsDynData", + "PxVehicleDrive4W", + "PxVehicleWheels4SimData", + "PxVehicleDriveSimData4W", + "PxVehicleWheelData", + "PxVehicleSuspensionData", + "PxVehicleDriveDynData", + "PxVehicleDifferential4WData", + "PxVehicleDifferentialNWData", + "PxVehicleAckermannGeometryData", + "PxVehicleTireLoadFilterData", + "PxVehicleEngineData", + "PxVehicleGearsData", + "PxVehicleClutchData", + "PxVehicleAutoBoxData", + "PxVehicleTireData", + "PxVehicleChassisData", + "PxTorqueCurvePair", + "PxVehicleDriveTank", + "PxVehicleNoDrive", + "PxVehicleDriveSimDataNW", + "PxVehicleDriveNW", + "PxVehicleAntiRollBarData", +}; + +//We absolutely never generate information about these types, even if types +//we do care about are derived from these types. +static const char* gAvoidedPhysXTypes[] = +{ + "PxSerializable", + "PxObservable", + "PxBase", + "PxBaseFlag::Enum", +}; + +#include "PxPhysicsAPI.h" +#include "PxVehicleSuspWheelTire4.h" +#endif diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat new file mode 100644 index 000000000..651051768 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat @@ -0,0 +1,132 @@ +:: +:: Redistribution and use in source and binary forms, with or without +:: modification, are permitted provided that the following conditions +:: are met: +:: * Redistributions of source code must retain the above copyright +:: notice, this list of conditions and the following disclaimer. +:: * Redistributions in binary form must reproduce the above copyright +:: notice, this list of conditions and the following disclaimer in the +:: documentation and/or other materials provided with the distribution. +:: * Neither the name of NVIDIA CORPORATION nor the names of its +:: contributors may be used to endorse or promote products derived +:: from this software without specific prior written permission. +:: +:: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +:: EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +:: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +:: PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +:: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +:: EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +:: PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +:: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +:: OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +:: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +:: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +:: +:: Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + +@echo off +setlocal EnableDelayedExpansion + +:: batch script that runs generateMetaData.py with either +:: +:: -environment variable PYTHON +:: -p4sw location of python +:: -python.exe in PATH +:: +:: see readme.txt + +cd %~dp0 + +:: Get PxShared path from packman, if packman available: +set PACKMAN_CMD=..\..\buildtools\packman\packman +if not exist !PACKMAN_CMD! goto no_packman + +call "..\..\buildtools\update_packman.cmd" +@if errorlevel 1 @exit /b %errorlevel% + +call !PACKMAN_CMD! pull "%~dp0..\..\dependencies.xml" --include-tag=RequiredForMetaGen +@if errorlevel 1 @exit /b %errorlevel% +:no_packman + +:: look for python in p4 location unless PYTHON is set +if not defined PYTHON ( + call :find_root_path %~p0 "tools\python\3.3" _RESULT + if not !_RESULT!==0 ( + set PYTHON=!_RESULT!\tools\python\3.3\python.exe + ) +) + +:: look for python in PATH unless PYTHON is set +if not defined PYTHON ( + where python.exe > nul 2>&1 + IF !ERRORLEVEL! == 0 ( + set PYTHON=python.exe + ) +) + +if not defined PYTHON ( + goto no_python +) + +echo using: %PYTHON% + +:: visual studio 2015 is required for the meta data generation +:: run vcvarsall.bat to get visual studio developer console environment variables +if not defined VS140COMNTOOLS ( + goto no_vs140 +) + +echo calling: %VS140COMNTOOLS%..\..\VC\vcvarsall.bat +call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" +%PYTHON% generateMetaData.py %1 + +endlocal +exit /b %ERRORLEVEL% + +:no_python +echo no python found, please set PYTHON environment variable to python.exe path, or make sure python.exe is in PATH. +endlocal +exit /b 1 + +:no_vs140 +echo echo make sure vs 2015 is installed. +endlocal +exit /b 1 + + +:: ************************************************************************** +:: functions +:: ************************************************************************** + +:: find a root directory containing a known directory (as a hint) +:find_root_path + setlocal + set START_DIR=%~1 + set CONTAINED_DIR=%~2 + + :: search directory tree + set TMP_DIR=!START_DIR! + set OUT_DIR=0 + :find_root_path_loop + if exist !TMP_DIR!\!CONTAINED_DIR! goto :find_root_path_loop_end + set TMP_DIR=!TMP_DIR!..\ + + :: normalize path + pushd !TMP_DIR! + set OUT_DIR=!CD! + popd + + :: break if we reach the root, by checking the last two charactors + if "!OUT_DIR:~-2!"==":\" ( + set OUT_DIR=0 + goto :find_root_path_loop_end + ) + + goto :find_root_path_loop + :find_root_path_loop_end + + ::echo no idea why we need to use % here + endlocal & set _RESULT=%OUT_DIR% + exit /b 0 + diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py new file mode 100644 index 000000000..7af2f264c --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py @@ -0,0 +1,251 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + +from __future__ import print_function + +import argparse +import os +import stat +import sys +import re +import platform +import shutil +from lib import utils +from lib import compare + +# test mode: create copy of reference files +# update mode: try to open file in p4 if necessary +def setup_targetdir(metaDataDir, isTestMode): + if isTestMode: + targetDir = metaDataDir + "_test" + if os.path.isdir(targetDir): + print("deleting", targetDir) + shutil.rmtree(targetDir) + + def ignore_non_autogen(dir, files): + return filter(lambda f : not (os.path.isdir(os.path.join(dir, f)) + or re.search(r"AutoGenerated", f)), files) + + shutil.copytree(metaDataDir, targetDir, ignore=ignore_non_autogen) + #set write to new files: + for root, dirs, files in os.walk(targetDir): + for file in files: + os.chmod(os.path.join(root, file) , stat.S_IWRITE|stat.S_IREAD) + else: + targetDir = metaDataDir + + if not utils.check_files_writable(utils.list_autogen_files(targetDir)): + utils.try_checkout_files(utils.list_autogen_files(targetDir)) + + if not utils.check_files_writable(utils.list_autogen_files(targetDir)): + print("auto generated meta data files not writable:", targetDir) + print("aborting") + sys.exit(1) + + utils.clear_files(utils.list_autogen_files(targetDir)) + return targetDir + +# test mode: run perl script to compare reference and generated files +def test_targetdir(targetDir, metaDataDir, isTestMode): + + if isTestMode: + print("compare generated meta data files with reference meta data files:") + result = compare.compareMetaDataDirectories(targetDir, metaDataDir) + if not result: + print("failed!") + sys.exit(1) + else: + print("passed.") + +def get_osx_platform_path(): + cmd = "xcodebuild -showsdks" + (stdout, stderr) = utils.run_cmd(cmd) + if stderr != "": + print(stderr) + sys.exit(1) + + match = re.search(r"(-sdk macosx\d+.\d+)", stdout, flags=re.MULTILINE) + if not match: + print("coundn't parse output of:\n", cmd, "\naborting!") + sys.exit(1) + + sdkparam = match.group(0) + cmd = "xcodebuild -version " + sdkparam + " Path" + (sdkPath, stderr) = utils.run_cmd(cmd) + if stderr != "": + print(stderr) + sys.exit(1) + print("using sdk path:", sdkPath.rstrip()) + return sdkPath.rstrip() + +def includeString(path): + return ' -I"' + path + '"' + +########################################################################################################### +# main +########################################################################################################### + +parser = argparse.ArgumentParser(description='Generates meta data source files.') +parser.add_argument('-test', help='enables testing mode, internal only', action='store_true') + +args = parser.parse_args() + +scriptDir = os.path.dirname(os.path.realpath(__file__)) + +try: + os.makedirs("temp") +except: + None + +# find SDK_ROOT, EXTERNALS and PX_SHARED +sdkRoot = utils.find_root_path(scriptDir, "source") + +if os.path.isdir(os.path.join(sdkRoot, "../physx")): + externalsRoot = os.path.join(sdkRoot, "../externals") + pxSharedRoot = os.path.join(sdkRoot, "../pxshared") +else: + externalsRoot = os.path.join(utils.find_root_path(scriptDir, os.path.normpath("externals/clang")), "externals") + pxSharedRoot = os.path.normpath(os.environ['PM_PxShared_PATH']) + +print("testmode:", args.test) +print("root sdk:", sdkRoot) +print("root externals:", externalsRoot) +print("root shared:", pxSharedRoot) + +boilerPlateFile = os.path.join(sdkRoot, os.path.normpath("tools/physxmetadatagenerator/PxBoilerPlate.h")) + +includes = '' +includes += includeString(pxSharedRoot + '/include') +includes += includeString(sdkRoot + '/include') +includes += includeString(sdkRoot + '/source/foundation/include') +includes += includeString(sdkRoot + '/source/common/src') +includes += includeString(sdkRoot + '/source/geomutils/headers') +includes += includeString(sdkRoot + '/source/geomutils/src') +includes += includeString(sdkRoot + '/source/geomutils/Opcode') +includes += includeString(sdkRoot + '/source/physx/src') +includes += includeString(sdkRoot + '/source/physx/src/buffering') +includes += includeString(sdkRoot + '/source/simulationcontroller/src') +includes += includeString(sdkRoot + '/source/simulationcontroller/src/framework') +includes += includeString(sdkRoot + '/source/simulationcontroller/include') +includes += includeString(sdkRoot + '/source/physxcooking/include') +includes += includeString(sdkRoot + '/source/scenequery') +includes += includeString(sdkRoot + '/source/physxmetadata/core/include') +includes += includeString(sdkRoot + '/source/physxgpu/include') +includes += includeString(sdkRoot + '/source/pvd/include') +includes += includeString(sdkRoot + '/source/fastxml/include') +includes += includeString(sdkRoot + '/tools/physxmetadatagenerator') + +print("platform:", platform.system()) + +commonFlags = '-DNDEBUG -DPX_GENERATE_META_DATA -x c++-header -std=c++0x -w -Wno-c++11-narrowing -fms-extensions ' + +if platform.system() == "Windows": + debugFile = open("temp/clangCommandLine_windows.txt", "a") + + # read INCLUDE variable, set by calling batch script + sysIncludes = os.environ['INCLUDE'] + sysIncludes = sysIncludes.rstrip(';') + sysIncludeList = sysIncludes.split(';') + sysIncludeFlags = ' -isystem"' + '" -isystem"'.join(sysIncludeList) + '"' + + # for some reason -cc1 needs to go first in commonFlags + commonFlags = '-cc1 ' + commonFlags + platformFlags = '-DPX_VC=14 -D_WIN32 ' + sysIncludeFlags + clangExe = os.path.join(externalsRoot, os.path.normpath('clang/4.0.0/win32/bin/clang.exe')) + + +elif platform.system() == "Linux": + debugFile = open("temp/clangCommandLine_linux.txt", "a") + + platformFlags = '' + clangExe = os.path.join(externalsRoot, os.path.normpath('clang/4.0.0/linux32/bin/clang')) + +elif platform.system() == "Darwin": + debugFile = open("temp/clangCommandLine_osx.txt", "a") + + platformFlags = ' -isysroot' + get_osx_platform_path() + clangExe = os.path.join(externalsRoot, os.path.normpath('clang/4.0.0/osx/bin/clang')) +else: + print("unsupported platform, aborting!") + sys.exit(1) + +commonFlags += ' -boilerplate-file ' + boilerPlateFile + +#some checks +if not os.path.isfile(clangExe): + print("didn't find,", clangExe, ", aborting!") + sys.exit(1) + +clangExe = '"' + clangExe + '"' + +# required for execution of clang.exe +os.environ["PWD"] = os.path.join(sdkRoot, os.path.normpath("tools/physxmetadatagenerator")) + +############################### +# PxPhysicsWithExtensions # +############################### + +print("PxPhysicsWithExtensions:") + +srcPath = "PxPhysicsWithExtensionsAPI.h" +metaDataDir = os.path.join(sdkRoot, os.path.normpath("source/physxmetadata")) + +targetDir = setup_targetdir(metaDataDir, args.test) + +cmd = " ".join(["", clangExe, commonFlags, "", platformFlags, includes, srcPath, "-o", '"'+targetDir+'"']) +print(cmd, file = debugFile) +(stdout, stderr) = utils.run_cmd(cmd) +if (stderr != "" or stdout != ""): + print(stderr, "\n", stdout) +print("wrote meta data files in", targetDir) + +test_targetdir(targetDir, metaDataDir, args.test) + +############################### +# PxVehicleExtension # +############################### + +print("PxVehicleExtension:") + +srcPath = "PxVehicleExtensionAPI.h" +metaDataDir = os.path.join(sdkRoot, os.path.normpath("source/physxvehicle/src/physxmetadata")) + +includes += includeString(sdkRoot + '/include/vehicle') +includes += includeString(sdkRoot + '/source/physxvehicle/src') +includes += includeString(pxSharedRoot + '/include/foundation') + +targetDir = setup_targetdir(metaDataDir, args.test) + +cmd = " ".join(["", clangExe, commonFlags, "", platformFlags, includes, srcPath, "-o", '"'+targetDir+'"']) +print(cmd, file = debugFile) +(stdout, stderr) = utils.run_cmd(cmd) +if (stderr != "" or stdout != ""): + print(stderr, "\n", stdout) +print("wrote meta data files in", targetDir) + +test_targetdir(targetDir, metaDataDir, args.test) + diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh new file mode 100644 index 000000000..149b523a4 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh @@ -0,0 +1,47 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PHYSX_ROOT_DIR=$SCRIPT_DIR/../.. + +export GEN_META_DATA_PARAMETER=$1 + +if [ -f $PHYSX_ROOT_DIR/buildtools/update_packman.sh ] ; then + source $PHYSX_ROOT_DIR/buildtools/update_packman.sh + $PHYSX_ROOT_DIR/buildtools/packman/packman pull "$PHYSX_ROOT_DIR/dependencies.xml" --platform linux --include-tag=requiredForMetaGen --postscript $SCRIPT_DIR/helper.sh +else + export PM_PxShared_PATH="$PHYSX_ROOT_DIR/../pxshared" + $SCRIPT_DIR/helper.sh +fi + +status=$? +if [ "$status" -ne "0" ]; then + echo "Error $status" + exit 1 +fi diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/helper.sh b/src/PhysX/physx/tools/physxmetadatagenerator/helper.sh new file mode 100644 index 000000000..95b193442 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/helper.sh @@ -0,0 +1,37 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +#!/bin/bash + +set -e + +python generateMetaData.py $GEN_META_DATA_PARAMETER +status=$? +if [ "$status" -ne "0" ]; then + echo "Error $status" + exit 1 +fi diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/lib/__init__.py b/src/PhysX/physx/tools/physxmetadatagenerator/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py b/src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py new file mode 100644 index 000000000..a7657a8c8 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py @@ -0,0 +1,120 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + +# general utility module +from __future__ import print_function +from __future__ import with_statement + +import os +import sys +import re +from . import utils + +def compareMetaDataDirectories(candidateDir, referenceDir): + + print("reference dir:", referenceDir) + print("candidate dir:", candidateDir) + + if not _checkFileExistence(candidateDir, referenceDir): + return False + + referenceFiles = utils.list_autogen_files(referenceDir) + + #get corresponding candidate files without relying on os.walk order + def mapRefToCand(refFile): + return os.path.join(candidateDir, os.path.relpath(refFile, referenceDir)) + candidateFiles = [mapRefToCand(f) for f in referenceFiles] + + for (fileCand, fileRef) in zip(candidateFiles, referenceFiles): + + timeCand = os.path.getmtime(fileCand) + timeRef = os.path.getmtime(fileRef) + + if timeCand <= timeRef: + print("last modified time of candidate is not later than last modified time of reference:") + print("candidate:", fileCand, "\n", "reference:", fileRef) + print("ref:", timeRef) + print("cand:", timeCand) + return False + + #_read_file_content will remove line endings(windows/unix), but not ignore empty lines + candLines = _read_file_content(fileCand) + refLines = _read_file_content(fileRef) + if not (candLines and refLines): + return False + + if len(candLines) != len(refLines): + print("files got different number of lines:") + print("candidate:", fileCand, "\n", "reference:", fileRef) + print("ref:", len(refLines)) + print("cand:", len(candLines)) + return False + + for (i, (lineCand, lineRef)) in enumerate(zip(candLines, refLines)): + if (lineCand != lineRef): + print("candidate line is not equal to refence line:") + print("candidate:", fileCand, "\n", "reference:", fileRef) + print("@line number:", i) + print("ref:", lineRef) + print("cand:", lineCand) + return False + + return True + +############################################################################## +# internal functions +############################################################################## + +#will remove line endings(windows/unix), but not ignore empty lines +def _read_file_content(filePath): + lines = [] + try: + with open(filePath, "r") as file: + for line in file: + lines.append(line.rstrip()) + except: + print("issue with reading file:", filePath) + + return lines + +def _checkFileExistence(candidateDir, referenceDir): + candidateSet = set([os.path.relpath(f, candidateDir) for f in utils.list_autogen_files(candidateDir)]) + referenceSet = set([os.path.relpath(f, referenceDir) for f in utils.list_autogen_files(referenceDir)]) + + missingSet = referenceSet - candidateSet + if missingSet: + print("the following files are missing from the candidates:\n", "\n".join(missingSet)) + return False + + excessSet = candidateSet - referenceSet + if excessSet: + print("too many candidate files:\n", "\n".join(excessSet)) + return False + + return True + + diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py b/src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py new file mode 100644 index 000000000..1d3a786b5 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py @@ -0,0 +1,104 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + +# general utility module +from __future__ import print_function +from __future__ import with_statement + +import os +import sys +import re +import subprocess + +def list_autogen_files(dirPath): + autogenFiles = [] + for (root, subdirs, files) in os.walk(dirPath): + files = filter(lambda f : re.search(r"AutoGenerated", f), files) + autogenFiles.extend([os.path.join(root, f) for f in files]) + return autogenFiles + +#checkout files with p4 if available +def try_checkout_files(files): + + print("checking p4 connection parameter...") + + # checking p4 + cmd = "p4" + (stdout, stderr) = run_cmd(cmd) + + if stderr == "": + print("p4 available.") + else: + print("p4 unavailable.") + return + + cmd = "p4 edit " + " " + " ".join(files) + (stdout, stderr) = run_cmd(cmd) + print(stderr) + print(stdout) + +# check files writability +def check_files_writable(files): + for file in files: + if not os.access(file, os.W_OK): + return False + return True + +# find a root directory containing a known directory (as a hint) +def find_root_path(startDir, containedDir): + + currentDir = startDir + + # search directory tree + mergedDir = os.path.join(currentDir, containedDir) + while not os.path.isdir(mergedDir): + (currentDir, dir) = os.path.split(currentDir) + if not dir: + return None + + mergedDir = os.path.join(currentDir, containedDir) + + return currentDir + +def run_cmd(cmd, stdin = ""): + process = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + (stdoutRaw, stderrRaw) = process.communicate(stdin.encode('utf-8')) + stdout = stdoutRaw.decode(encoding='utf-8') + stderr = stderrRaw.decode(encoding='utf-8') + return (stdout, stderr) + +# clears content of files +def clear_files(files): + for file in files: + open(file, 'w').close() + +############################################################################## +# internal functions +############################################################################## + + diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/readme.txt b/src/PhysX/physx/tools/physxmetadatagenerator/readme.txt new file mode 100644 index 000000000..fa569f54c --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/readme.txt @@ -0,0 +1,54 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +If the Physx API is changed, the API level serialization meta data files need to be updated. +This can be done by running: + +Windows: generateMetaData.bat +It looks for python in + 1. environment variable PYTHON + 2. p4sw location (NVidia only) + 3. PATH + +Linux/MacOS: generateMetaData.sh + +Linux and osx distributions mostly come with Python pre-installed. On windows please download +and install the latest python version from here: + https://www.python.org/downloads +and make sure to add python to your windows PATH (option in python installer) + +The generateMetaData.py tests for meta data files being writable. If not, p4 commands are +used to open the files for edit before the update. If p4 is not available (or not configured for cmd line usage, see https://www.perforce.com/perforce/r15.1/manuals/p4guide/chapter.configuration.html: "Using config files"), +the files need to be made writable manually. + +Requirements: +Windows: Visual Studio 2015 + +Testing: +generateMetaData.py -test runs the meta data generation in test mode. This mode checks that the current +set of meta data files is still in sync with the PhysX API (i.e. already generated meta data files). + diff --git a/src/PhysX/premake4.lua b/src/PhysX/premake4.lua new file mode 100644 index 000000000..a1a693ac0 --- /dev/null +++ b/src/PhysX/premake4.lua @@ -0,0 +1,128 @@ + project "PhysX" + + kind "StaticLib" + if os.is("Linux") then + buildoptions{"-fPIC"} + end + + defines { + "PX_PHYSX_STATIC_LIB", + "PX_COOKING", + "PX_FOUNDATION_DLL=0" + } + + configuration {"x64", "debug"} + defines {"_DEBUG"} + configuration {"x86", "debug"} + defines {"_DEBUG"} + configuration {"x64", "release"} + defines {"NDEBUG"} + configuration {"x86", "release"} + defines {"NDEBUG"} + configuration{} + + includedirs { + "physx/source/common/include", + "physx/source/common/src", + "physx/source/fastxml/include", + "physx/source/filebuf/include", + "physx/source/foundation/include", + "physx/source/geomutils/include", + "physx/source/geomutils/src", + "physx/source/geomutils/src/ccd", + "physx/source/geomutils/src/common", + "physx/source/geomutils/src/contact", + "physx/source/geomutils/src/convex", + "physx/source/geomutils/src/distance", + "physx/source/geomutils/src/gjk", + "physx/source/geomutils/src/hf", + "physx/source/geomutils/src/intersection", + "physx/source/geomutils/src/mesh", + "physx/source/geomutils/src/pcm", + "physx/source/geomutils/src/sweep", + "physx/source/lowlevel/api/include", + "physx/source/lowlevel/common/include", + "physx/source/lowlevel/common/include/collision", + "physx/source/lowlevel/common/include/pipeline", + "physx/source/lowlevel/common/include/utils", + "physx/source/lowlevel/software/include", + "physx/source/lowlevelaabb/include", + "physx/source/lowleveldynamics/include", + "physx/source/physx/src", + "physx/source/physx/src/buffering", + "physx/source/physx/src/device", + "physx/source/physxcooking/src", + "physx/source/physxcooking/src/convex", + "physx/source/physxcooking/src/mesh", + "physx/source/physxextensions/src", + "physx/source/physxextensions/src/serialization/Binary", + "physx/source/physxextensions/src/serialization/File", + "physx/source/physxextensions/src/serialization/Xml", + "physx/source/physxmetadata/core/include", + "physx/source/physxmetadata/extensions/include", + "physx/source/physxvehicle/src", + "physx/source/physxvehicle/src/physxmetadata/include", + "physx/source/pvd/include", + "physx/source/scenequery/include", + "physx/source/simulationcontroller/include", + "physx/source/simulationcontroller/src", +--public + "physx/include", + "physx/include/characterkinematic", + "physx/include/common", + "physx/include/cooking", + "physx/include/extensions", + "physx/include/geometry", + "physx/include/geomutils", + "physx/include/vehicle", + "pxshared/include", + } + + if os.is("Windows") then + files { + "physx/source/common/src/windows/*.cpp", + "physx/source/foundation/src/windows/*.cpp", + "physx/source/physx/src/device/windows/*.cpp", + "physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp", + } + includedirs { + "physx/include/gpu", + "physx/source/physxgpu/include", + } + else +-- files { +-- "physx/source/foundation/src/unix/*.cpp", +-- "physx/source/physx/src/device/linux/*.cpp", +-- } + end + + files { + "physx/source/common/src/*.cpp", + "physx/source/fastxml/src/*.cpp", + "physx/source/foundation/src/*.cpp", + "physx/source/geomutils/src/**.cpp", + "physx/source/immediatemode/src/*.cpp", + "physx/source/lowlevel/api/src/*.cpp", + "physx/source/lowlevel/common/src/**.cpp", + "physx/source/lowlevel/software/src/*.cpp", + "physx/source/lowlevelaabb/src/*.cpp", + "physx/source/lowleveldynamics/src/*.cpp", + "physx/source/physx/src/*.cpp", + "physx/source/physx/src/buffering/*.cpp", + "physx/source/physx/src/gpu/*.cpp", + "physx/source/physxcharacterkinematic/src/*.cpp", + "physx/source/physxcooking/src/*.cpp", + "physx/source/physxcooking/src/convex/*.cpp", + "physx/source/physxcooking/src/mesh/*.cpp", + "physx/source/physxextensions/src/*.cpp", + "physx/source/physxextensions/src/**.cpp", + "physx/source/physxmetadata/core/src/*.cpp", + "physx/source/physxmetadata/extensions/src/*.cpp", + "physx/source/physxvehicle/src/*.cpp", + "physx/source/physxvehicle/src/physxmetadata/src/*.cpp", + "physx/source/pvd/src/*.cpp", + "physx/source/scenequery/src/*.cpp", + "physx/source/simulationcontroller/src/*.cpp", + "physx/source/task/src/*.cpp", + } + \ No newline at end of file diff --git a/src/PhysX/pxshared/include/foundation/Px.h b/src/PhysX/pxshared/include/foundation/Px.h new file mode 100644 index 000000000..28c744853 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/Px.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PX_H +#define PXFOUNDATION_PX_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxSimpleTypes.h" + +/** files to always include */ +#include +#include + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +typedef uint32_t PxU32; + +class PxAllocatorCallback; +class PxErrorCallback; +struct PxErrorCode; +class PxAssertHandler; + +class PxInputStream; +class PxInputData; +class PxOutputStream; + +class PxVec2; +class PxVec3; +class PxVec4; +class PxMat33; +class PxMat44; +class PxPlane; +class PxQuat; +class PxTransform; +class PxBounds3; + +/** enum for empty constructor tag*/ +enum PxEMPTY +{ + PxEmpty +}; + +/** enum for zero constructor tag for vectors and matrices */ +enum PxZERO +{ + PxZero +}; + +/** enum for identity constructor flag for quaternions, transforms, and matrices */ +enum PxIDENTITY +{ + PxIdentity +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PX_H diff --git a/src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h b/src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h new file mode 100644 index 000000000..d9350ce2e --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXALLOCATORCALLBACK_H +#define PXFOUNDATION_PXALLOCATORCALLBACK_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Px.h" +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Abstract base class for an application defined memory allocator that can be used by the Nv library. + +\note The SDK state should not be modified from within any allocation/free function. + +Threading: All methods of this class should be thread safe as it can be called from the user thread +or the physics processing thread(s). +*/ + +class PxAllocatorCallback +{ + public: + /** + \brief destructor + */ + virtual ~PxAllocatorCallback() + { + } + + /** + \brief Allocates size bytes of memory, which must be 16-byte aligned. + + This method should never return NULL. If you run out of memory, then + you should terminate the app or take some other appropriate action. + + Threading: This function should be thread safe as it can be called in the context of the user thread + and physics processing thread(s). + + \param size Number of bytes to allocate. + \param typeName Name of the datatype that is being allocated + \param filename The source file which allocated the memory + \param line The source line which allocated the memory + \return The allocated block of memory. + */ + virtual void* allocate(size_t size, const char* typeName, const char* filename, int line) = 0; + + /** + \brief Frees memory previously allocated by allocate(). + + Threading: This function should be thread safe as it can be called in the context of the user thread + and physics processing thread(s). + + \param ptr Memory to free. + */ + virtual void deallocate(void* ptr) = 0; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXALLOCATORCALLBACK_H diff --git a/src/PhysX/pxshared/include/foundation/PxAssert.h b/src/PhysX/pxshared/include/foundation/PxAssert.h new file mode 100644 index 000000000..227035474 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxAssert.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXASSERT_H +#define PXFOUNDATION_PXASSERT_H + +/** \addtogroup foundation +@{ */ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/* Base class to handle assert failures */ +class PxAssertHandler +{ + public: + virtual ~PxAssertHandler() + { + } + virtual void operator()(const char* exp, const char* file, int line, bool& ignore) = 0; +}; + +PX_FOUNDATION_API PxAssertHandler& PxGetAssertHandler(); +PX_FOUNDATION_API void PxSetAssertHandler(PxAssertHandler& handler); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#if !PX_ENABLE_ASSERTS +#define PX_ASSERT(exp) ((void)0) +#define PX_ALWAYS_ASSERT_MESSAGE(exp) ((void)0) +#define PX_ASSERT_WITH_MESSAGE(condition, message) ((void)0) +#else +#if PX_VC +#define PX_CODE_ANALYSIS_ASSUME(exp) \ + __analysis_assume(!!(exp)) // This macro will be used to get rid of analysis warning messages if a PX_ASSERT is used +// to "guard" illegal mem access, for example. +#else +#define PX_CODE_ANALYSIS_ASSUME(exp) +#endif +#define PX_ASSERT(exp) \ + { \ + static bool _ignore = false; \ + ((void)((!!(exp)) || (!_ignore && (physx::PxGetAssertHandler()(#exp, __FILE__, __LINE__, _ignore), false)))); \ + PX_CODE_ANALYSIS_ASSUME(exp); \ + } +#define PX_ALWAYS_ASSERT_MESSAGE(exp) \ + { \ + static bool _ignore = false; \ + if(!_ignore) \ + physx::PxGetAssertHandler()(exp, __FILE__, __LINE__, _ignore); \ + } +#define PX_ASSERT_WITH_MESSAGE(exp, message) \ + { \ + static bool _ignore = false; \ + ((void)((!!(exp)) || (!_ignore && (physx::PxGetAssertHandler()(message, __FILE__, __LINE__, _ignore), false)))); \ + PX_CODE_ANALYSIS_ASSUME(exp); \ + } +#endif + +#define PX_ALWAYS_ASSERT() PX_ASSERT(0) + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXASSERT_H diff --git a/src/PhysX/pxshared/include/foundation/PxBitAndData.h b/src/PhysX/pxshared/include/foundation/PxBitAndData.h new file mode 100644 index 000000000..be7699e49 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxBitAndData.h @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXBITANDDATA_H +#define PXFOUNDATION_PXBITANDDATA_H + +#include "foundation/Px.h" + +/** \addtogroup foundation + @{ +*/ +#if !PX_DOXYGEN +namespace physx +{ +#endif + +template +class PxBitAndDataT +{ + public: + PX_FORCE_INLINE PxBitAndDataT(const PxEMPTY) + { + } + PX_FORCE_INLINE PxBitAndDataT() : mData(0) + { + } + PX_FORCE_INLINE PxBitAndDataT(storageType data, bool bit = false) + { + mData = bit ? storageType(data | bitMask) : data; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE operator storageType() const + { + return storageType(mData & ~bitMask); + } + PX_CUDA_CALLABLE PX_FORCE_INLINE void setBit() + { + mData |= bitMask; + } + PX_CUDA_CALLABLE PX_FORCE_INLINE void clearBit() + { + mData &= ~bitMask; + } + PX_CUDA_CALLABLE PX_FORCE_INLINE storageType isBitSet() const + { + return storageType(mData & bitMask); + } + + protected: + storageType mData; +}; +typedef PxBitAndDataT PxBitAndByte; +typedef PxBitAndDataT PxBitAndWord; +typedef PxBitAndDataT PxBitAndDword; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PXFOUNDATION_PXBITANDDATA_H diff --git a/src/PhysX/pxshared/include/foundation/PxBounds3.h b/src/PhysX/pxshared/include/foundation/PxBounds3.h new file mode 100644 index 000000000..7a071c3ab --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxBounds3.h @@ -0,0 +1,480 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXBOUNDS3_H +#define PXFOUNDATION_PXBOUNDS3_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +// maximum extents defined such that floating point exceptions are avoided for standard use cases +#define PX_MAX_BOUNDS_EXTENTS (PX_MAX_REAL * 0.25f) + +/** +\brief Class representing 3D range or axis aligned bounding box. + +Stored as minimum and maximum extent corners. Alternate representation +would be center and dimensions. +May be empty or nonempty. For nonempty bounds, minimum <= maximum has to hold for all axes. +Empty bounds have to be represented as minimum = PX_MAX_BOUNDS_EXTENTS and maximum = -PX_MAX_BOUNDS_EXTENTS for all +axes. +All other representations are invalid and the behavior is undefined. +*/ +class PxBounds3 +{ + public: + /** + \brief Default constructor, not performing any initialization for performance reason. + \remark Use empty() function below to construct empty bounds. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3() + { + } + + /** + \brief Construct from two bounding points + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3(const PxVec3& minimum, const PxVec3& maximum); + + /** + \brief Return empty bounds. + */ + static PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 empty(); + + /** + \brief returns the AABB containing v0 and v1. + \param v0 first point included in the AABB. + \param v1 second point included in the AABB. + */ + static PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 boundsOfPoints(const PxVec3& v0, const PxVec3& v1); + + /** + \brief returns the AABB from center and extents vectors. + \param center Center vector + \param extent Extents vector + */ + static PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 centerExtents(const PxVec3& center, const PxVec3& extent); + + /** + \brief Construct from center, extent, and (not necessarily orthogonal) basis + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 + basisExtent(const PxVec3& center, const PxMat33& basis, const PxVec3& extent); + + /** + \brief Construct from pose and extent + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 poseExtent(const PxTransform& pose, const PxVec3& extent); + + /** + \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). + + This version is safe to call for empty bounds. + + \param[in] matrix Transform to apply, can contain scaling as well + \param[in] bounds The bounds to transform. + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 transformSafe(const PxMat33& matrix, const PxBounds3& bounds); + + /** + \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). + + Calling this method for empty bounds leads to undefined behavior. Use #transformSafe() instead. + + \param[in] matrix Transform to apply, can contain scaling as well + \param[in] bounds The bounds to transform. + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 transformFast(const PxMat33& matrix, const PxBounds3& bounds); + + /** + \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). + + This version is safe to call for empty bounds. + + \param[in] transform Transform to apply, can contain scaling as well + \param[in] bounds The bounds to transform. + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 transformSafe(const PxTransform& transform, const PxBounds3& bounds); + + /** + \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). + + Calling this method for empty bounds leads to undefined behavior. Use #transformSafe() instead. + + \param[in] transform Transform to apply, can contain scaling as well + \param[in] bounds The bounds to transform. + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 transformFast(const PxTransform& transform, const PxBounds3& bounds); + + /** + \brief Sets empty to true + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void setEmpty(); + + /** + \brief Sets the bounds to maximum size [-PX_MAX_BOUNDS_EXTENTS, PX_MAX_BOUNDS_EXTENTS]. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void setMaximal(); + + /** + \brief expands the volume to include v + \param v Point to expand to. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void include(const PxVec3& v); + + /** + \brief expands the volume to include b. + \param b Bounds to perform union with. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void include(const PxBounds3& b); + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isEmpty() const; + + /** + \brief indicates whether the intersection of this and b is empty or not. + \param b Bounds to test for intersection. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool intersects(const PxBounds3& b) const; + + /** + \brief computes the 1D-intersection between two AABBs, on a given axis. + \param a the other AABB + \param axis the axis (0, 1, 2) + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool intersects1D(const PxBounds3& a, uint32_t axis) const; + + /** + \brief indicates if these bounds contain v. + \param v Point to test against bounds. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool contains(const PxVec3& v) const; + + /** + \brief checks a box is inside another box. + \param box the other AABB + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isInside(const PxBounds3& box) const; + + /** + \brief returns the center of this axis aligned box. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getCenter() const; + + /** + \brief get component of the box's center along a given axis + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float getCenter(uint32_t axis) const; + + /** + \brief get component of the box's extents along a given axis + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float getExtents(uint32_t axis) const; + + /** + \brief returns the dimensions (width/height/depth) of this axis aligned box. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getDimensions() const; + + /** + \brief returns the extents, which are half of the width/height/depth. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getExtents() const; + + /** + \brief scales the AABB. + + This version is safe to call for empty bounds. + + \param scale Factor to scale AABB by. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void scaleSafe(float scale); + + /** + \brief scales the AABB. + + Calling this method for empty bounds leads to undefined behavior. Use #scaleSafe() instead. + + \param scale Factor to scale AABB by. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void scaleFast(float scale); + + /** + fattens the AABB in all 3 dimensions by the given distance. + + This version is safe to call for empty bounds. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void fattenSafe(float distance); + + /** + fattens the AABB in all 3 dimensions by the given distance. + + Calling this method for empty bounds leads to undefined behavior. Use #fattenSafe() instead. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void fattenFast(float distance); + + /** + checks that the AABB values are not NaN + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite() const; + + /** + checks that the AABB values describe a valid configuration. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isValid() const; + + PxVec3 minimum, maximum; +}; + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3::PxBounds3(const PxVec3& minimum_, const PxVec3& maximum_) +: minimum(minimum_), maximum(maximum_) +{ +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 PxBounds3::empty() +{ + return PxBounds3(PxVec3(PX_MAX_BOUNDS_EXTENTS), PxVec3(-PX_MAX_BOUNDS_EXTENTS)); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isFinite() const +{ + return minimum.isFinite() && maximum.isFinite(); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 PxBounds3::boundsOfPoints(const PxVec3& v0, const PxVec3& v1) +{ + return PxBounds3(v0.minimum(v1), v0.maximum(v1)); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 PxBounds3::centerExtents(const PxVec3& center, const PxVec3& extent) +{ + return PxBounds3(center - extent, center + extent); +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 +PxBounds3::basisExtent(const PxVec3& center, const PxMat33& basis, const PxVec3& extent) +{ + // extended basis vectors + PxVec3 c0 = basis.column0 * extent.x; + PxVec3 c1 = basis.column1 * extent.y; + PxVec3 c2 = basis.column2 * extent.z; + + PxVec3 w; + // find combination of base vectors that produces max. distance for each component = sum of abs() + w.x = PxAbs(c0.x) + PxAbs(c1.x) + PxAbs(c2.x); + w.y = PxAbs(c0.y) + PxAbs(c1.y) + PxAbs(c2.y); + w.z = PxAbs(c0.z) + PxAbs(c1.z) + PxAbs(c2.z); + + return PxBounds3(center - w, center + w); +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::poseExtent(const PxTransform& pose, const PxVec3& extent) +{ + return basisExtent(pose.p, PxMat33(pose.q), extent); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::setEmpty() +{ + minimum = PxVec3(PX_MAX_BOUNDS_EXTENTS); + maximum = PxVec3(-PX_MAX_BOUNDS_EXTENTS); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::setMaximal() +{ + minimum = PxVec3(-PX_MAX_BOUNDS_EXTENTS); + maximum = PxVec3(PX_MAX_BOUNDS_EXTENTS); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::include(const PxVec3& v) +{ + PX_ASSERT(isValid()); + minimum = minimum.minimum(v); + maximum = maximum.maximum(v); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::include(const PxBounds3& b) +{ + PX_ASSERT(isValid()); + minimum = minimum.minimum(b.minimum); + maximum = maximum.maximum(b.maximum); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isEmpty() const +{ + PX_ASSERT(isValid()); + return minimum.x > maximum.x; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::intersects(const PxBounds3& b) const +{ + PX_ASSERT(isValid() && b.isValid()); + return !(b.minimum.x > maximum.x || minimum.x > b.maximum.x || b.minimum.y > maximum.y || minimum.y > b.maximum.y || + b.minimum.z > maximum.z || minimum.z > b.maximum.z); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::intersects1D(const PxBounds3& a, uint32_t axis) const +{ + PX_ASSERT(isValid() && a.isValid()); + return maximum[axis] >= a.minimum[axis] && a.maximum[axis] >= minimum[axis]; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::contains(const PxVec3& v) const +{ + PX_ASSERT(isValid()); + + return !(v.x < minimum.x || v.x > maximum.x || v.y < minimum.y || v.y > maximum.y || v.z < minimum.z || + v.z > maximum.z); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isInside(const PxBounds3& box) const +{ + PX_ASSERT(isValid() && box.isValid()); + if(box.minimum.x > minimum.x) + return false; + if(box.minimum.y > minimum.y) + return false; + if(box.minimum.z > minimum.z) + return false; + if(box.maximum.x < maximum.x) + return false; + if(box.maximum.y < maximum.y) + return false; + if(box.maximum.z < maximum.z) + return false; + return true; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 PxBounds3::getCenter() const +{ + PX_ASSERT(isValid()); + return (minimum + maximum) * 0.5f; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxBounds3::getCenter(uint32_t axis) const +{ + PX_ASSERT(isValid()); + return (minimum[axis] + maximum[axis]) * 0.5f; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxBounds3::getExtents(uint32_t axis) const +{ + PX_ASSERT(isValid()); + return (maximum[axis] - minimum[axis]) * 0.5f; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 PxBounds3::getDimensions() const +{ + PX_ASSERT(isValid()); + return maximum - minimum; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 PxBounds3::getExtents() const +{ + PX_ASSERT(isValid()); + return getDimensions() * 0.5f; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::scaleSafe(float scale) +{ + PX_ASSERT(isValid()); + if(!isEmpty()) + scaleFast(scale); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::scaleFast(float scale) +{ + PX_ASSERT(isValid()); + *this = centerExtents(getCenter(), getExtents() * scale); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::fattenSafe(float distance) +{ + PX_ASSERT(isValid()); + if(!isEmpty()) + fattenFast(distance); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::fattenFast(float distance) +{ + PX_ASSERT(isValid()); + minimum.x -= distance; + minimum.y -= distance; + minimum.z -= distance; + + maximum.x += distance; + maximum.y += distance; + maximum.z += distance; +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformSafe(const PxMat33& matrix, const PxBounds3& bounds) +{ + PX_ASSERT(bounds.isValid()); + return !bounds.isEmpty() ? transformFast(matrix, bounds) : bounds; +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformFast(const PxMat33& matrix, const PxBounds3& bounds) +{ + PX_ASSERT(bounds.isValid()); + return PxBounds3::basisExtent(matrix * bounds.getCenter(), matrix, bounds.getExtents()); +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformSafe(const PxTransform& transform, const PxBounds3& bounds) +{ + PX_ASSERT(bounds.isValid()); + return !bounds.isEmpty() ? transformFast(transform, bounds) : bounds; +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformFast(const PxTransform& transform, const PxBounds3& bounds) +{ + PX_ASSERT(bounds.isValid()); + return PxBounds3::basisExtent(transform.transform(bounds.getCenter()), PxMat33(transform.q), bounds.getExtents()); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isValid() const +{ + return (isFinite() && (((minimum.x <= maximum.x) && (minimum.y <= maximum.y) && (minimum.z <= maximum.z)) || + ((minimum.x == PX_MAX_BOUNDS_EXTENTS) && (minimum.y == PX_MAX_BOUNDS_EXTENTS) && + (minimum.z == PX_MAX_BOUNDS_EXTENTS) && (maximum.x == -PX_MAX_BOUNDS_EXTENTS) && + (maximum.y == -PX_MAX_BOUNDS_EXTENTS) && (maximum.z == -PX_MAX_BOUNDS_EXTENTS)))); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXBOUNDS3_H diff --git a/src/PhysX/pxshared/include/foundation/PxErrorCallback.h b/src/PhysX/pxshared/include/foundation/PxErrorCallback.h new file mode 100644 index 000000000..554c76c8a --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxErrorCallback.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXERRORCALLBACK_H +#define PXFOUNDATION_PXERRORCALLBACK_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxErrors.h" +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief User defined interface class. Used by the library to emit debug information. + +\note The SDK state should not be modified from within any error reporting functions. + +Threading: The SDK sequences its calls to the output stream using a mutex, so the class need not +be implemented in a thread-safe manner if the SDK is the only client. +*/ +class PxErrorCallback +{ + public: + virtual ~PxErrorCallback() + { + } + + /** + \brief Reports an error code. + \param code Error code, see #PxErrorCode + \param message Message to display. + \param file File error occured in. + \param line Line number error occured on. + */ + virtual void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) = 0; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXERRORCALLBACK_H diff --git a/src/PhysX/pxshared/include/foundation/PxErrors.h b/src/PhysX/pxshared/include/foundation/PxErrors.h new file mode 100644 index 000000000..eaedc50fd --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxErrors.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXERRORS_H +#define PXFOUNDATION_PXERRORS_H +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Error codes + +These error codes are passed to #PxErrorCallback + +@see PxErrorCallback +*/ + +struct PxErrorCode +{ + enum Enum + { + eNO_ERROR = 0, + + //! \brief An informational message. + eDEBUG_INFO = 1, + + //! \brief a warning message for the user to help with debugging + eDEBUG_WARNING = 2, + + //! \brief method called with invalid parameter(s) + eINVALID_PARAMETER = 4, + + //! \brief method was called at a time when an operation is not possible + eINVALID_OPERATION = 8, + + //! \brief method failed to allocate some memory + eOUT_OF_MEMORY = 16, + + /** \brief The library failed for some reason. + Possibly you have passed invalid values like NaNs, which are not checked for. + */ + eINTERNAL_ERROR = 32, + + //! \brief An unrecoverable error, execution should be halted and log output flushed + eABORT = 64, + + //! \brief The SDK has determined that an operation may result in poor performance. + ePERF_WARNING = 128, + + //! \brief A bit mask for including all errors + eMASK_ALL = -1 + }; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXERRORS_H diff --git a/src/PhysX/pxshared/include/foundation/PxFlags.h b/src/PhysX/pxshared/include/foundation/PxFlags.h new file mode 100644 index 000000000..58d456cf5 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxFlags.h @@ -0,0 +1,376 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXFLAGS_H +#define PXFOUNDATION_PXFLAGS_H + +/** \addtogroup foundation + @{ +*/ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif +/** +\brief Container for bitfield flag variables associated with a specific enum type. + +This allows for type safe manipulation for bitfields. + +

            Example

            + // enum that defines each bit... + struct MyEnum + { + enum Enum + { + eMAN = 1, + eBEAR = 2, + ePIG = 4, + }; + }; + + // implements some convenient global operators. + PX_FLAGS_OPERATORS(MyEnum::Enum, uint8_t); + + PxFlags myFlags; + myFlags |= MyEnum::eMAN; + myFlags |= MyEnum::eBEAR | MyEnum::ePIG; + if(myFlags & MyEnum::eBEAR) + { + doSomething(); + } +*/ + +template +class PxFlags +{ + public: + typedef storagetype InternalType; + + PX_CUDA_CALLABLE PX_INLINE explicit PxFlags(const PxEMPTY) + { + } + PX_CUDA_CALLABLE PX_INLINE PxFlags(void); + PX_CUDA_CALLABLE PX_INLINE PxFlags(enumtype e); + PX_CUDA_CALLABLE PX_INLINE PxFlags(const PxFlags& f); + PX_CUDA_CALLABLE PX_INLINE explicit PxFlags(storagetype b); + + PX_CUDA_CALLABLE PX_INLINE bool isSet(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE PxFlags& set(enumtype e); + PX_CUDA_CALLABLE PX_INLINE bool operator==(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxFlags& f) const; + PX_CUDA_CALLABLE PX_INLINE bool operator==(bool b) const; + PX_CUDA_CALLABLE PX_INLINE bool operator!=(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE bool operator!=(const PxFlags& f) const; + + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator=(const PxFlags& f); + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator=(enumtype e); + + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator|=(enumtype e); + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator|=(const PxFlags& f); + PX_CUDA_CALLABLE PX_INLINE PxFlags operator|(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE PxFlags operator|(const PxFlags& f) const; + + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator&=(enumtype e); + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator&=(const PxFlags& f); + PX_CUDA_CALLABLE PX_INLINE PxFlags operator&(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE PxFlags operator&(const PxFlags& f) const; + + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator^=(enumtype e); + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator^=(const PxFlags& f); + PX_CUDA_CALLABLE PX_INLINE PxFlags operator^(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE PxFlags operator^(const PxFlags& f) const; + + PX_CUDA_CALLABLE PX_INLINE PxFlags operator~(void) const; + + PX_CUDA_CALLABLE PX_INLINE operator bool(void) const; + PX_CUDA_CALLABLE PX_INLINE operator uint8_t(void) const; + PX_CUDA_CALLABLE PX_INLINE operator uint16_t(void) const; + PX_CUDA_CALLABLE PX_INLINE operator uint32_t(void) const; + + PX_CUDA_CALLABLE PX_INLINE void clear(enumtype e); + + public: + friend PX_INLINE PxFlags operator&(enumtype a, PxFlags& b) + { + PxFlags out; + out.mBits = a & b.mBits; + return out; + } + + private: + storagetype mBits; +}; + +#if !PX_DOXYGEN + +#define PX_FLAGS_OPERATORS(enumtype, storagetype) \ + PX_CUDA_CALLABLE PX_INLINE PxFlags operator|(enumtype a, enumtype b) \ + { \ + PxFlags r(a); \ + r |= b; \ + return r; \ + } \ + PX_CUDA_CALLABLE PX_INLINE PxFlags operator&(enumtype a, enumtype b) \ + { \ + PxFlags r(a); \ + r &= b; \ + return r; \ + } \ + PX_CUDA_CALLABLE PX_INLINE PxFlags operator~(enumtype a) \ + { \ + return ~PxFlags(a); \ + } + +#define PX_FLAGS_TYPEDEF(x, y) \ + typedef PxFlags x##s; \ + PX_FLAGS_OPERATORS(x::Enum, y) + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::PxFlags(void) +{ + mBits = 0; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::PxFlags(enumtype e) +{ + mBits = static_cast(e); +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::PxFlags(const PxFlags& f) +{ + mBits = f.mBits; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::PxFlags(storagetype b) +{ + mBits = b; +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::isSet(enumtype e) const +{ + return (mBits & static_cast(e)) == static_cast(e); +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::set(enumtype e) +{ + mBits = static_cast(e); + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::operator==(enumtype e) const +{ + return mBits == static_cast(e); +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::operator==(const PxFlags& f) const +{ + return mBits == f.mBits; +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::operator==(bool b) const +{ + return bool(*this) == b; +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::operator!=(enumtype e) const +{ + return mBits != static_cast(e); +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::operator!=(const PxFlags& f) const +{ + return mBits != f.mBits; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::operator=(enumtype e) +{ + mBits = static_cast(e); + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::operator=(const PxFlags& f) +{ + mBits = f.mBits; + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::operator|=(enumtype e) +{ + mBits |= static_cast(e); + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags:: +operator|=(const PxFlags& f) +{ + mBits |= f.mBits; + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags::operator|(enumtype e) const +{ + PxFlags out(*this); + out |= e; + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags:: +operator|(const PxFlags& f) const +{ + PxFlags out(*this); + out |= f; + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::operator&=(enumtype e) +{ + mBits &= static_cast(e); + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags:: +operator&=(const PxFlags& f) +{ + mBits &= f.mBits; + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags::operator&(enumtype e) const +{ + PxFlags out = *this; + out.mBits &= static_cast(e); + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags:: +operator&(const PxFlags& f) const +{ + PxFlags out = *this; + out.mBits &= f.mBits; + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::operator^=(enumtype e) +{ + mBits ^= static_cast(e); + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags:: +operator^=(const PxFlags& f) +{ + mBits ^= f.mBits; + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags::operator^(enumtype e) const +{ + PxFlags out = *this; + out.mBits ^= static_cast(e); + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags:: +operator^(const PxFlags& f) const +{ + PxFlags out = *this; + out.mBits ^= f.mBits; + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags::operator~(void) const +{ + PxFlags out; + out.mBits = storagetype(~mBits); + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::operator bool(void) const +{ + return mBits ? true : false; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::operator uint8_t(void) const +{ + return static_cast(mBits); +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::operator uint16_t(void) const +{ + return static_cast(mBits); +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::operator uint32_t(void) const +{ + return static_cast(mBits); +} + +template +PX_CUDA_CALLABLE PX_INLINE void PxFlags::clear(enumtype e) +{ + mBits &= ~static_cast(e); +} + +} // namespace physx +#endif //!PX_DOXYGEN + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXFLAGS_H diff --git a/src/PhysX/pxshared/include/foundation/PxIO.h b/src/PhysX/pxshared/include/foundation/PxIO.h new file mode 100644 index 000000000..4000c9bcd --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxIO.h @@ -0,0 +1,138 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXIO_H +#define PXFOUNDATION_PXIO_H + +/** \addtogroup common + @{ +*/ + +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Input stream class for I/O. + +The user needs to supply a PxInputStream implementation to a number of methods to allow the SDK to read data. +*/ + +class PxInputStream +{ + public: + /** + \brief read from the stream. The number of bytes read may be less than the number requested. + + \param[in] dest the destination address to which the data will be read + \param[in] count the number of bytes requested + + \return the number of bytes read from the stream. + */ + + virtual uint32_t read(void* dest, uint32_t count) = 0; + + virtual ~PxInputStream() + { + } +}; + +/** +\brief Input data class for I/O which provides random read access. + +The user needs to supply a PxInputData implementation to a number of methods to allow the SDK to read data. +*/ + +class PxInputData : public PxInputStream +{ + public: + /** + \brief return the length of the input data + + \return size in bytes of the input data + */ + + virtual uint32_t getLength() const = 0; + + /** + \brief seek to the given offset from the start of the data. + + \param[in] offset the offset to seek to. If greater than the length of the data, this call is equivalent to + seek(length); + */ + + virtual void seek(uint32_t offset) = 0; + + /** + \brief return the current offset from the start of the data + + \return the offset to seek to. + */ + + virtual uint32_t tell() const = 0; + + virtual ~PxInputData() + { + } +}; + +/** +\brief Output stream class for I/O. + +The user needs to supply a PxOutputStream implementation to a number of methods to allow the SDK to write data. +*/ + +class PxOutputStream +{ + public: + /** + \brief write to the stream. The number of bytes written may be less than the number sent. + + \param[in] src the destination address from which the data will be written + \param[in] count the number of bytes to be written + + \return the number of bytes written to the stream by this call. + */ + + virtual uint32_t write(const void* src, uint32_t count) = 0; + + virtual ~PxOutputStream() + { + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXIO_H diff --git a/src/PhysX/pxshared/include/foundation/PxIntrinsics.h b/src/PhysX/pxshared/include/foundation/PxIntrinsics.h new file mode 100644 index 000000000..d4257168d --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxIntrinsics.h @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXINTRINSICS_H +#define PXFOUNDATION_PXINTRINSICS_H + +#include "foundation/PxPreprocessor.h" + +#if PX_WINDOWS_FAMILY +#include "foundation/windows/PxWindowsIntrinsics.h" +#elif(PX_LINUX || PX_ANDROID || PX_APPLE_FAMILY || PX_PS4) +#include "foundation/unix/PxUnixIntrinsics.h" +#elif PX_XBOXONE +#include "foundation/XboxOne/PxXboxOneIntrinsics.h" +#elif PX_SWITCH +#include "foundation/switch/PxSwitchIntrinsics.h" +#else +#error "Platform not supported!" +#endif + +#endif // #ifndef PXFOUNDATION_PXINTRINSICS_H diff --git a/src/PhysX/pxshared/include/foundation/PxMat33.h b/src/PhysX/pxshared/include/foundation/PxMat33.h new file mode 100644 index 000000000..6cf4257f1 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxMat33.h @@ -0,0 +1,396 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMAT33_H +#define PXFOUNDATION_PXMAT33_H +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif +/*! +\brief 3x3 matrix class + +Some clarifications, as there have been much confusion about matrix formats etc in the past. + +Short: +- Matrix have base vectors in columns (vectors are column matrices, 3x1 matrices). +- Matrix is physically stored in column major format +- Matrices are concaternated from left + +Long: +Given three base vectors a, b and c the matrix is stored as + +|a.x b.x c.x| +|a.y b.y c.y| +|a.z b.z c.z| + +Vectors are treated as columns, so the vector v is + +|x| +|y| +|z| + +And matrices are applied _before_ the vector (pre-multiplication) +v' = M*v + +|x'| |a.x b.x c.x| |x| |a.x*x + b.x*y + c.x*z| +|y'| = |a.y b.y c.y| * |y| = |a.y*x + b.y*y + c.y*z| +|z'| |a.z b.z c.z| |z| |a.z*x + b.z*y + c.z*z| + + +Physical storage and indexing: +To be compatible with popular 3d rendering APIs (read D3d and OpenGL) +the physical indexing is + +|0 3 6| +|1 4 7| +|2 5 8| + +index = column*3 + row + +which in C++ translates to M[column][row] + +The mathematical indexing is M_row,column and this is what is used for _-notation +so _12 is 1st row, second column and operator(row, column)! + +*/ +class PxMat33 +{ + public: + //! Default constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33() + { + } + + //! identity constructor + PX_CUDA_CALLABLE PX_INLINE PxMat33(PxIDENTITY r) + : column0(1.0f, 0.0f, 0.0f), column1(0.0f, 1.0f, 0.0f), column2(0.0f, 0.0f, 1.0f) + { + PX_UNUSED(r); + } + + //! zero constructor + PX_CUDA_CALLABLE PX_INLINE PxMat33(PxZERO r) : column0(0.0f), column1(0.0f), column2(0.0f) + { + PX_UNUSED(r); + } + + //! Construct from three base vectors + PX_CUDA_CALLABLE PxMat33(const PxVec3& col0, const PxVec3& col1, const PxVec3& col2) + : column0(col0), column1(col1), column2(col2) + { + } + + //! constructor from a scalar, which generates a multiple of the identity matrix + explicit PX_CUDA_CALLABLE PX_INLINE PxMat33(float r) + : column0(r, 0.0f, 0.0f), column1(0.0f, r, 0.0f), column2(0.0f, 0.0f, r) + { + } + + //! Construct from float[9] + explicit PX_CUDA_CALLABLE PX_INLINE PxMat33(float values[]) + : column0(values[0], values[1], values[2]) + , column1(values[3], values[4], values[5]) + , column2(values[6], values[7], values[8]) + { + } + + //! Construct from a quaternion + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33(const PxQuat& q) + { + const float x = q.x; + const float y = q.y; + const float z = q.z; + const float w = q.w; + + const float x2 = x + x; + const float y2 = y + y; + const float z2 = z + z; + + const float xx = x2 * x; + const float yy = y2 * y; + const float zz = z2 * z; + + const float xy = x2 * y; + const float xz = x2 * z; + const float xw = x2 * w; + + const float yz = y2 * z; + const float yw = y2 * w; + const float zw = z2 * w; + + column0 = PxVec3(1.0f - yy - zz, xy + zw, xz - yw); + column1 = PxVec3(xy - zw, 1.0f - xx - zz, yz + xw); + column2 = PxVec3(xz + yw, yz - xw, 1.0f - xx - yy); + } + + //! Copy constructor + PX_CUDA_CALLABLE PX_INLINE PxMat33(const PxMat33& other) + : column0(other.column0), column1(other.column1), column2(other.column2) + { + } + + //! Assignment operator + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33& operator=(const PxMat33& other) + { + column0 = other.column0; + column1 = other.column1; + column2 = other.column2; + return *this; + } + + //! Construct from diagonal, off-diagonals are zero. + PX_CUDA_CALLABLE PX_INLINE static const PxMat33 createDiagonal(const PxVec3& d) + { + return PxMat33(PxVec3(d.x, 0.0f, 0.0f), PxVec3(0.0f, d.y, 0.0f), PxVec3(0.0f, 0.0f, d.z)); + } + + /** + \brief returns true if the two matrices are exactly equal + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxMat33& m) const + { + return column0 == m.column0 && column1 == m.column1 && column2 == m.column2; + } + + //! Get transposed matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxMat33 getTranspose() const + { + const PxVec3 v0(column0.x, column1.x, column2.x); + const PxVec3 v1(column0.y, column1.y, column2.y); + const PxVec3 v2(column0.z, column1.z, column2.z); + + return PxMat33(v0, v1, v2); + } + + //! Get the real inverse + PX_CUDA_CALLABLE PX_INLINE const PxMat33 getInverse() const + { + const float det = getDeterminant(); + PxMat33 inverse; + + if(det != 0) + { + const float invDet = 1.0f / det; + + inverse.column0.x = invDet * (column1.y * column2.z - column2.y * column1.z); + inverse.column0.y = invDet * -(column0.y * column2.z - column2.y * column0.z); + inverse.column0.z = invDet * (column0.y * column1.z - column0.z * column1.y); + + inverse.column1.x = invDet * -(column1.x * column2.z - column1.z * column2.x); + inverse.column1.y = invDet * (column0.x * column2.z - column0.z * column2.x); + inverse.column1.z = invDet * -(column0.x * column1.z - column0.z * column1.x); + + inverse.column2.x = invDet * (column1.x * column2.y - column1.y * column2.x); + inverse.column2.y = invDet * -(column0.x * column2.y - column0.y * column2.x); + inverse.column2.z = invDet * (column0.x * column1.y - column1.x * column0.y); + + return inverse; + } + else + { + return PxMat33(PxIdentity); + } + } + + //! Get determinant + PX_CUDA_CALLABLE PX_INLINE float getDeterminant() const + { + return column0.dot(column1.cross(column2)); + } + + //! Unary minus + PX_CUDA_CALLABLE PX_INLINE const PxMat33 operator-() const + { + return PxMat33(-column0, -column1, -column2); + } + + //! Add + PX_CUDA_CALLABLE PX_INLINE const PxMat33 operator+(const PxMat33& other) const + { + return PxMat33(column0 + other.column0, column1 + other.column1, column2 + other.column2); + } + + //! Subtract + PX_CUDA_CALLABLE PX_INLINE const PxMat33 operator-(const PxMat33& other) const + { + return PxMat33(column0 - other.column0, column1 - other.column1, column2 - other.column2); + } + + //! Scalar multiplication + PX_CUDA_CALLABLE PX_INLINE const PxMat33 operator*(float scalar) const + { + return PxMat33(column0 * scalar, column1 * scalar, column2 * scalar); + } + + friend PxMat33 operator*(float, const PxMat33&); + + //! Matrix vector multiplication (returns 'this->transform(vec)') + PX_CUDA_CALLABLE PX_INLINE const PxVec3 operator*(const PxVec3& vec) const + { + return transform(vec); + } + + // a = b operators + + //! Matrix multiplication + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxMat33 operator*(const PxMat33& other) const + { + // Rows from this columns from other + // column0 = transform(other.column0) etc + return PxMat33(transform(other.column0), transform(other.column1), transform(other.column2)); + } + + //! Equals-add + PX_CUDA_CALLABLE PX_INLINE PxMat33& operator+=(const PxMat33& other) + { + column0 += other.column0; + column1 += other.column1; + column2 += other.column2; + return *this; + } + + //! Equals-sub + PX_CUDA_CALLABLE PX_INLINE PxMat33& operator-=(const PxMat33& other) + { + column0 -= other.column0; + column1 -= other.column1; + column2 -= other.column2; + return *this; + } + + //! Equals scalar multiplication + PX_CUDA_CALLABLE PX_INLINE PxMat33& operator*=(float scalar) + { + column0 *= scalar; + column1 *= scalar; + column2 *= scalar; + return *this; + } + + //! Equals matrix multiplication + PX_CUDA_CALLABLE PX_INLINE PxMat33& operator*=(const PxMat33& other) + { + *this = *this * other; + return *this; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE float operator()(unsigned int row, unsigned int col) const + { + return (*this)[col][row]; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE float& operator()(unsigned int row, unsigned int col) + { + return (*this)[col][row]; + } + + // Transform etc + + //! Transform vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3 transform(const PxVec3& other) const + { + return column0 * other.x + column1 * other.y + column2 * other.z; + } + + //! Transform vector by matrix transpose, v' = M^t*v + PX_CUDA_CALLABLE PX_INLINE const PxVec3 transformTranspose(const PxVec3& other) const + { + return PxVec3(column0.dot(other), column1.dot(other), column2.dot(other)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const float* front() const + { + return &column0.x; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator[](unsigned int num) + { + return (&column0)[num]; + } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& operator[](unsigned int num) const + { + return (&column0)[num]; + } + + // Data, see above for format! + + PxVec3 column0, column1, column2; // the three base vectors +}; + +// implementation from PxQuat.h +PX_CUDA_CALLABLE PX_INLINE PxQuat::PxQuat(const PxMat33& m) +{ + if(m.column2.z < 0) + { + if(m.column0.x > m.column1.y) + { + float t = 1 + m.column0.x - m.column1.y - m.column2.z; + *this = PxQuat(t, m.column0.y + m.column1.x, m.column2.x + m.column0.z, m.column1.z - m.column2.y) * + (0.5f / PxSqrt(t)); + } + else + { + float t = 1 - m.column0.x + m.column1.y - m.column2.z; + *this = PxQuat(m.column0.y + m.column1.x, t, m.column1.z + m.column2.y, m.column2.x - m.column0.z) * + (0.5f / PxSqrt(t)); + } + } + else + { + if(m.column0.x < -m.column1.y) + { + float t = 1 - m.column0.x - m.column1.y + m.column2.z; + *this = PxQuat(m.column2.x + m.column0.z, m.column1.z + m.column2.y, t, m.column0.y - m.column1.x) * + (0.5f / PxSqrt(t)); + } + else + { + float t = 1 + m.column0.x + m.column1.y + m.column2.z; + *this = PxQuat(m.column1.z - m.column2.y, m.column2.x - m.column0.z, m.column0.y - m.column1.x, t) * + (0.5f / PxSqrt(t)); + } + } +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXMAT33_H diff --git a/src/PhysX/pxshared/include/foundation/PxMat44.h b/src/PhysX/pxshared/include/foundation/PxMat44.h new file mode 100644 index 000000000..91500ae67 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxMat44.h @@ -0,0 +1,376 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMAT44_H +#define PXFOUNDATION_PXMAT44_H +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxQuat.h" +#include "foundation/PxVec4.h" +#include "foundation/PxMat33.h" +#include "foundation/PxTransform.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/*! +\brief 4x4 matrix class + +This class is layout-compatible with D3D and OpenGL matrices. More notes on layout are given in the PxMat33 + +@see PxMat33 PxTransform +*/ + +class PxMat44 +{ + public: + //! Default constructor + PX_CUDA_CALLABLE PX_INLINE PxMat44() + { + } + + //! identity constructor + PX_CUDA_CALLABLE PX_INLINE PxMat44(PxIDENTITY r) + : column0(1.0f, 0.0f, 0.0f, 0.0f) + , column1(0.0f, 1.0f, 0.0f, 0.0f) + , column2(0.0f, 0.0f, 1.0f, 0.0f) + , column3(0.0f, 0.0f, 0.0f, 1.0f) + { + PX_UNUSED(r); + } + + //! zero constructor + PX_CUDA_CALLABLE PX_INLINE PxMat44(PxZERO r) : column0(PxZero), column1(PxZero), column2(PxZero), column3(PxZero) + { + PX_UNUSED(r); + } + + //! Construct from four 4-vectors + PX_CUDA_CALLABLE PxMat44(const PxVec4& col0, const PxVec4& col1, const PxVec4& col2, const PxVec4& col3) + : column0(col0), column1(col1), column2(col2), column3(col3) + { + } + + //! constructor that generates a multiple of the identity matrix + explicit PX_CUDA_CALLABLE PX_INLINE PxMat44(float r) + : column0(r, 0.0f, 0.0f, 0.0f) + , column1(0.0f, r, 0.0f, 0.0f) + , column2(0.0f, 0.0f, r, 0.0f) + , column3(0.0f, 0.0f, 0.0f, r) + { + } + + //! Construct from three base vectors and a translation + PX_CUDA_CALLABLE PxMat44(const PxVec3& col0, const PxVec3& col1, const PxVec3& col2, const PxVec3& col3) + : column0(col0, 0), column1(col1, 0), column2(col2, 0), column3(col3, 1.0f) + { + } + + //! Construct from float[16] + explicit PX_CUDA_CALLABLE PX_INLINE PxMat44(float values[]) + : column0(values[0], values[1], values[2], values[3]) + , column1(values[4], values[5], values[6], values[7]) + , column2(values[8], values[9], values[10], values[11]) + , column3(values[12], values[13], values[14], values[15]) + { + } + + //! Construct from a quaternion + explicit PX_CUDA_CALLABLE PX_INLINE PxMat44(const PxQuat& q) + { + const float x = q.x; + const float y = q.y; + const float z = q.z; + const float w = q.w; + + const float x2 = x + x; + const float y2 = y + y; + const float z2 = z + z; + + const float xx = x2 * x; + const float yy = y2 * y; + const float zz = z2 * z; + + const float xy = x2 * y; + const float xz = x2 * z; + const float xw = x2 * w; + + const float yz = y2 * z; + const float yw = y2 * w; + const float zw = z2 * w; + + column0 = PxVec4(1.0f - yy - zz, xy + zw, xz - yw, 0.0f); + column1 = PxVec4(xy - zw, 1.0f - xx - zz, yz + xw, 0.0f); + column2 = PxVec4(xz + yw, yz - xw, 1.0f - xx - yy, 0.0f); + column3 = PxVec4(0.0f, 0.0f, 0.0f, 1.0f); + } + + //! Construct from a diagonal vector + explicit PX_CUDA_CALLABLE PX_INLINE PxMat44(const PxVec4& diagonal) + : column0(diagonal.x, 0.0f, 0.0f, 0.0f) + , column1(0.0f, diagonal.y, 0.0f, 0.0f) + , column2(0.0f, 0.0f, diagonal.z, 0.0f) + , column3(0.0f, 0.0f, 0.0f, diagonal.w) + { + } + + //! Construct from Mat33 and a translation + PX_CUDA_CALLABLE PxMat44(const PxMat33& axes, const PxVec3& position) + : column0(axes.column0, 0.0f), column1(axes.column1, 0.0f), column2(axes.column2, 0.0f), column3(position, 1.0f) + { + } + + PX_CUDA_CALLABLE PxMat44(const PxTransform& t) + { + *this = PxMat44(PxMat33(t.q), t.p); + } + + /** + \brief returns true if the two matrices are exactly equal + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxMat44& m) const + { + return column0 == m.column0 && column1 == m.column1 && column2 == m.column2 && column3 == m.column3; + } + + //! Copy constructor + PX_CUDA_CALLABLE PX_INLINE PxMat44(const PxMat44& other) + : column0(other.column0), column1(other.column1), column2(other.column2), column3(other.column3) + { + } + + //! Assignment operator + PX_CUDA_CALLABLE PX_INLINE PxMat44& operator=(const PxMat44& other) + { + column0 = other.column0; + column1 = other.column1; + column2 = other.column2; + column3 = other.column3; + return *this; + } + + //! Get transposed matrix + PX_CUDA_CALLABLE PX_INLINE const PxMat44 getTranspose() const + { + return PxMat44( + PxVec4(column0.x, column1.x, column2.x, column3.x), PxVec4(column0.y, column1.y, column2.y, column3.y), + PxVec4(column0.z, column1.z, column2.z, column3.z), PxVec4(column0.w, column1.w, column2.w, column3.w)); + } + + //! Unary minus + PX_CUDA_CALLABLE PX_INLINE const PxMat44 operator-() const + { + return PxMat44(-column0, -column1, -column2, -column3); + } + + //! Add + PX_CUDA_CALLABLE PX_INLINE const PxMat44 operator+(const PxMat44& other) const + { + return PxMat44(column0 + other.column0, column1 + other.column1, column2 + other.column2, + column3 + other.column3); + } + + //! Subtract + PX_CUDA_CALLABLE PX_INLINE const PxMat44 operator-(const PxMat44& other) const + { + return PxMat44(column0 - other.column0, column1 - other.column1, column2 - other.column2, + column3 - other.column3); + } + + //! Scalar multiplication + PX_CUDA_CALLABLE PX_INLINE const PxMat44 operator*(float scalar) const + { + return PxMat44(column0 * scalar, column1 * scalar, column2 * scalar, column3 * scalar); + } + + friend PxMat44 operator*(float, const PxMat44&); + + //! Matrix multiplication + PX_CUDA_CALLABLE PX_INLINE const PxMat44 operator*(const PxMat44& other) const + { + // Rows from this columns from other + // column0 = transform(other.column0) etc + return PxMat44(transform(other.column0), transform(other.column1), transform(other.column2), + transform(other.column3)); + } + + // a = b operators + + //! Equals-add + PX_CUDA_CALLABLE PX_INLINE PxMat44& operator+=(const PxMat44& other) + { + column0 += other.column0; + column1 += other.column1; + column2 += other.column2; + column3 += other.column3; + return *this; + } + + //! Equals-sub + PX_CUDA_CALLABLE PX_INLINE PxMat44& operator-=(const PxMat44& other) + { + column0 -= other.column0; + column1 -= other.column1; + column2 -= other.column2; + column3 -= other.column3; + return *this; + } + + //! Equals scalar multiplication + PX_CUDA_CALLABLE PX_INLINE PxMat44& operator*=(float scalar) + { + column0 *= scalar; + column1 *= scalar; + column2 *= scalar; + column3 *= scalar; + return *this; + } + + //! Equals matrix multiplication + PX_CUDA_CALLABLE PX_INLINE PxMat44& operator*=(const PxMat44& other) + { + *this = *this * other; + return *this; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE float operator()(unsigned int row, unsigned int col) const + { + return (*this)[col][row]; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE float& operator()(unsigned int row, unsigned int col) + { + return (*this)[col][row]; + } + + //! Transform vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_INLINE const PxVec4 transform(const PxVec4& other) const + { + return column0 * other.x + column1 * other.y + column2 * other.z + column3 * other.w; + } + + //! Transform vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_INLINE const PxVec3 transform(const PxVec3& other) const + { + return transform(PxVec4(other, 1.0f)).getXYZ(); + } + + //! Rotate vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_INLINE const PxVec4 rotate(const PxVec4& other) const + { + return column0 * other.x + column1 * other.y + column2 * other.z; // + column3*0; + } + + //! Rotate vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_INLINE const PxVec3 rotate(const PxVec3& other) const + { + return rotate(PxVec4(other, 1.0f)).getXYZ(); + } + + PX_CUDA_CALLABLE PX_INLINE const PxVec3 getBasis(int num) const + { + PX_ASSERT(num >= 0 && num < 3); + return (&column0)[num].getXYZ(); + } + + PX_CUDA_CALLABLE PX_INLINE const PxVec3 getPosition() const + { + return column3.getXYZ(); + } + + PX_CUDA_CALLABLE PX_INLINE void setPosition(const PxVec3& position) + { + column3.x = position.x; + column3.y = position.y; + column3.z = position.z; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const float* front() const + { + return &column0.x; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec4& operator[](unsigned int num) + { + return (&column0)[num]; + } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec4& operator[](unsigned int num) const + { + return (&column0)[num]; + } + + PX_CUDA_CALLABLE PX_INLINE void scale(const PxVec4& p) + { + column0 *= p.x; + column1 *= p.y; + column2 *= p.z; + column3 *= p.w; + } + + PX_CUDA_CALLABLE PX_INLINE const PxMat44 inverseRT(void) const + { + PxVec3 r0(column0.x, column1.x, column2.x), r1(column0.y, column1.y, column2.y), + r2(column0.z, column1.z, column2.z); + + return PxMat44(r0, r1, r2, -(r0 * column3.x + r1 * column3.y + r2 * column3.z)); + } + + PX_CUDA_CALLABLE PX_INLINE bool isFinite() const + { + return column0.isFinite() && column1.isFinite() && column2.isFinite() && column3.isFinite(); + } + + // Data, see above for format! + + PxVec4 column0, column1, column2, column3; // the four base vectors +}; + +// implementation from PxTransform.h +PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform::PxTransform(const PxMat44& m) +{ + PxVec3 column0 = PxVec3(m.column0.x, m.column0.y, m.column0.z); + PxVec3 column1 = PxVec3(m.column1.x, m.column1.y, m.column1.z); + PxVec3 column2 = PxVec3(m.column2.x, m.column2.y, m.column2.z); + + q = PxQuat(PxMat33(column0, column1, column2)); + p = PxVec3(m.column3.x, m.column3.y, m.column3.z); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXMAT44_H diff --git a/src/PhysX/pxshared/include/foundation/PxMath.h b/src/PhysX/pxshared/include/foundation/PxMath.h new file mode 100644 index 000000000..6f4817fcb --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxMath.h @@ -0,0 +1,338 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMATH_H +#define PXFOUNDATION_PXMATH_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxPreprocessor.h" + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4985) // 'symbol name': attributes not present on previous declaration +#endif +#include +#if PX_VC +#pragma warning(pop) +#endif + +#include +#include "foundation/PxIntrinsics.h" +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +// constants +static const float PxPi = float(3.141592653589793); +static const float PxHalfPi = float(1.57079632679489661923); +static const float PxTwoPi = float(6.28318530717958647692); +static const float PxInvPi = float(0.31830988618379067154); +static const float PxInvTwoPi = float(0.15915494309189533577); +static const float PxPiDivTwo = float(1.57079632679489661923); +static const float PxPiDivFour = float(0.78539816339744830962); + +/** +\brief The return value is the greater of the two specified values. +*/ +template +PX_CUDA_CALLABLE PX_FORCE_INLINE T PxMax(T a, T b) +{ + return a < b ? b : a; +} + +//! overload for float to use fsel on xbox +template <> +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxMax(float a, float b) +{ + return intrinsics::selectMax(a, b); +} + +/** +\brief The return value is the lesser of the two specified values. +*/ +template +PX_CUDA_CALLABLE PX_FORCE_INLINE T PxMin(T a, T b) +{ + return a < b ? a : b; +} + +template <> +//! overload for float to use fsel on xbox +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxMin(float a, float b) +{ + return intrinsics::selectMin(a, b); +} + +/* +Many of these are just implemented as PX_CUDA_CALLABLE PX_FORCE_INLINE calls to the C lib right now, +but later we could replace some of them with some approximations or more +clever stuff. +*/ + +/** +\brief abs returns the absolute value of its argument. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAbs(float a) +{ + return intrinsics::abs(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxEquals(float a, float b, float eps) +{ + return (PxAbs(a - b) < eps); +} + +/** +\brief abs returns the absolute value of its argument. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxAbs(double a) +{ + return ::fabs(a); +} + +/** +\brief abs returns the absolute value of its argument. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE int32_t PxAbs(int32_t a) +{ + return ::abs(a); +} + +/** +\brief Clamps v to the range [hi,lo] +*/ +template +PX_CUDA_CALLABLE PX_FORCE_INLINE T PxClamp(T v, T lo, T hi) +{ + PX_ASSERT(lo <= hi); + return PxMin(hi, PxMax(lo, v)); +} + +//! \brief Square root. +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxSqrt(float a) +{ + return intrinsics::sqrt(a); +} + +//! \brief Square root. +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxSqrt(double a) +{ + return ::sqrt(a); +} + +//! \brief reciprocal square root. +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxRecipSqrt(float a) +{ + return intrinsics::recipSqrt(a); +} + +//! \brief reciprocal square root. +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxRecipSqrt(double a) +{ + return 1 / ::sqrt(a); +} + +//! trigonometry -- all angles are in radians. + +//! \brief Sine of an angle ( Unit: Radians ) +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxSin(float a) +{ + return intrinsics::sin(a); +} + +//! \brief Sine of an angle ( Unit: Radians ) +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxSin(double a) +{ + return ::sin(a); +} + +//! \brief Cosine of an angle (Unit: Radians) +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxCos(float a) +{ + return intrinsics::cos(a); +} + +//! \brief Cosine of an angle (Unit: Radians) +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxCos(double a) +{ + return ::cos(a); +} + +/** +\brief Tangent of an angle. +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxTan(float a) +{ + return ::tanf(a); +} + +/** +\brief Tangent of an angle. +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxTan(double a) +{ + return ::tan(a); +} + +/** +\brief Arcsine. +Returns angle between -PI/2 and PI/2 in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAsin(float f) +{ + return ::asinf(PxClamp(f, -1.0f, 1.0f)); +} + +/** +\brief Arcsine. +Returns angle between -PI/2 and PI/2 in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxAsin(double f) +{ + return ::asin(PxClamp(f, -1.0, 1.0)); +} + +/** +\brief Arccosine. +Returns angle between 0 and PI in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAcos(float f) +{ + return ::acosf(PxClamp(f, -1.0f, 1.0f)); +} + +/** +\brief Arccosine. +Returns angle between 0 and PI in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxAcos(double f) +{ + return ::acos(PxClamp(f, -1.0, 1.0)); +} + +/** +\brief ArcTangent. +Returns angle between -PI/2 and PI/2 in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAtan(float a) +{ + return ::atanf(a); +} + +/** +\brief ArcTangent. +Returns angle between -PI/2 and PI/2 in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxAtan(double a) +{ + return ::atan(a); +} + +/** +\brief Arctangent of (x/y) with correct sign. +Returns angle between -PI and PI in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAtan2(float x, float y) +{ + return ::atan2f(x, y); +} + +/** +\brief Arctangent of (x/y) with correct sign. +Returns angle between -PI and PI in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxAtan2(double x, double y) +{ + return ::atan2(x, y); +} + +//! \brief returns true if the passed number is a finite floating point number as opposed to INF, NAN, etc. +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxIsFinite(float f) +{ + return intrinsics::isFinite(f); +} + +//! \brief returns true if the passed number is a finite floating point number as opposed to INF, NAN, etc. +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxIsFinite(double f) +{ + return intrinsics::isFinite(f); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxFloor(float a) +{ + return ::floorf(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxExp(float a) +{ + return ::expf(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxCeil(float a) +{ + return ::ceilf(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxSign(float a) +{ + return physx::intrinsics::sign(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxPow(float x, float y) +{ + return ::powf(x, y); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxLog(float x) +{ + return ::logf(x); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXMATH_H diff --git a/src/PhysX/pxshared/include/foundation/PxMathUtils.h b/src/PhysX/pxshared/include/foundation/PxMathUtils.h new file mode 100644 index 000000000..737ff73d7 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxMathUtils.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMATHUTILS_H +#define PXFOUNDATION_PXMATHUTILS_H + +/** \addtogroup common + @{ +*/ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief finds the shortest rotation between two vectors. + +\param[in] from the vector to start from +\param[in] target the vector to rotate to +\return a rotation about an axis normal to the two vectors which takes one to the other via the shortest path +*/ + +PX_FOUNDATION_API PxQuat PxShortestRotation(const PxVec3& from, const PxVec3& target); + +/* \brief diagonalizes a 3x3 symmetric matrix y + +The returned matrix satisfies M = R * D * R', where R is the rotation matrix for the output quaternion, R' its +transpose, and D the diagonal matrix + +If the matrix is not symmetric, the result is undefined. + +\param[in] m the matrix to diagonalize +\param[out] axes a quaternion rotation which diagonalizes the matrix +\return the vector diagonal of the diagonalized matrix. +*/ + +PX_FOUNDATION_API PxVec3 PxDiagonalize(const PxMat33& m, PxQuat& axes); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/pxshared/include/foundation/PxMemory.h b/src/PhysX/pxshared/include/foundation/PxMemory.h new file mode 100644 index 000000000..68d721def --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxMemory.h @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMEMORY_H +#define PXFOUNDATION_PXMEMORY_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Px.h" +#include "foundation/PxIntrinsics.h" +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Sets the bytes of the provided buffer to zero. + +\param dest Pointer to block of memory to set zero. +\param count Number of bytes to set to zero. + +\return Pointer to memory block (same as input) +*/ +PX_FORCE_INLINE void* PxMemZero(void* dest, PxU32 count) +{ + return physx::intrinsics::memZero(dest, count); +} + +/** +\brief Sets the bytes of the provided buffer to the specified value. + +\param dest Pointer to block of memory to set to the specified value. +\param c Value to set the bytes of the block of memory to. +\param count Number of bytes to set to the specified value. + +\return Pointer to memory block (same as input) +*/ +PX_FORCE_INLINE void* PxMemSet(void* dest, PxI32 c, PxU32 count) +{ + return physx::intrinsics::memSet(dest, c, count); +} + +/** +\brief Copies the bytes of one memory block to another. The memory blocks must not overlap. + +\note Use #PxMemMove if memory blocks overlap. + +\param dest Pointer to block of memory to copy to. +\param src Pointer to block of memory to copy from. +\param count Number of bytes to copy. + +\return Pointer to destination memory block +*/ +PX_FORCE_INLINE void* PxMemCopy(void* dest, const void* src, PxU32 count) +{ + return physx::intrinsics::memCopy(dest, src, count); +} + +/** +\brief Copies the bytes of one memory block to another. The memory blocks can overlap. + +\note Use #PxMemCopy if memory blocks do not overlap. + +\param dest Pointer to block of memory to copy to. +\param src Pointer to block of memory to copy from. +\param count Number of bytes to copy. + +\return Pointer to destination memory block +*/ +PX_FORCE_INLINE void* PxMemMove(void* dest, const void* src, PxU32 count) +{ + return physx::intrinsics::memMove(dest, src, count); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PXFOUNDATION_PXMEMORY_H diff --git a/src/PhysX/pxshared/include/foundation/PxPlane.h b/src/PhysX/pxshared/include/foundation/PxPlane.h new file mode 100644 index 000000000..618d3f077 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxPlane.h @@ -0,0 +1,145 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXPLANE_H +#define PXFOUNDATION_PXPLANE_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxMath.h" +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Representation of a plane. + + Plane equation used: n.dot(v) + d = 0 +*/ +class PxPlane +{ + public: + /** + \brief Constructor + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane() + { + } + + /** + \brief Constructor from a normal and a distance + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane(float nx, float ny, float nz, float distance) : n(nx, ny, nz), d(distance) + { + } + + /** + \brief Constructor from a normal and a distance + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane(const PxVec3& normal, float distance) : n(normal), d(distance) + { + } + + /** + \brief Constructor from a point on the plane and a normal + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane(const PxVec3& point, const PxVec3& normal) + : n(normal), d(-point.dot(n)) // p satisfies normal.dot(p) + d = 0 + { + } + + /** + \brief Constructor from three points + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) + { + n = (p1 - p0).cross(p2 - p0).getNormalized(); + d = -p0.dot(n); + } + + /** + \brief returns true if the two planes are exactly equal + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxPlane& p) const + { + return n == p.n && d == p.d; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE float distance(const PxVec3& p) const + { + return p.dot(n) + d; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool contains(const PxVec3& p) const + { + return PxAbs(distance(p)) < (1.0e-7f); + } + + /** + \brief projects p into the plane + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 project(const PxVec3& p) const + { + return p - n * distance(p); + } + + /** + \brief find an arbitrary point in the plane + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 pointInPlane() const + { + return -n * d; + } + + /** + \brief equivalent plane with unit normal + */ + + PX_CUDA_CALLABLE PX_FORCE_INLINE void normalize() + { + float denom = 1.0f / n.magnitude(); + n *= denom; + d *= denom; + } + + PxVec3 n; //!< The normal to the plane + float d; //!< The distance from the origin +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXPLANE_H diff --git a/src/PhysX/pxshared/include/foundation/PxPreprocessor.h b/src/PhysX/pxshared/include/foundation/PxPreprocessor.h new file mode 100644 index 000000000..13d84d93a --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxPreprocessor.h @@ -0,0 +1,553 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXPREPROCESSOR_H +#define PXFOUNDATION_PXPREPROCESSOR_H + +#include +#if !defined(PX_GENERATE_META_DATA) +#include +#endif +/** \addtogroup foundation + @{ +*/ + +#define PX_STRINGIZE_HELPER(X) #X +#define PX_STRINGIZE(X) PX_STRINGIZE_HELPER(X) + +#define PX_CONCAT_HELPER(X, Y) X##Y +#define PX_CONCAT(X, Y) PX_CONCAT_HELPER(X, Y) + +/* +The following preprocessor identifiers specify compiler, OS, and architecture. +All definitions have a value of 1 or 0, use '#if' instead of '#ifdef'. +*/ + +/** +Compiler defines, see http://sourceforge.net/p/predef/wiki/Compilers/ +*/ +#if defined(_MSC_VER) +#if _MSC_VER >= 1910 +#define PX_VC 15 +#elif _MSC_VER >= 1900 +#define PX_VC 14 +#elif _MSC_VER >= 1800 +#define PX_VC 12 +#elif _MSC_VER >= 1700 +#define PX_VC 11 +#elif _MSC_VER >= 1600 +#define PX_VC 10 +#elif _MSC_VER >= 1500 +#define PX_VC 9 +#else +#error "Unknown VC version" +#endif +#elif defined(__clang__) +#define PX_CLANG 1 +#elif defined(__GNUC__) // note: __clang__ implies __GNUC__ +#define PX_GCC 1 +#else +#error "Unknown compiler" +#endif + +/** +Operating system defines, see http://sourceforge.net/p/predef/wiki/OperatingSystems/ +*/ +#if defined(_XBOX_ONE) +#define PX_XBOXONE 1 +#elif defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_APP +#define PX_UWP 1 +#elif defined(_WIN64) // note: _XBOX_ONE implies _WIN64 +#define PX_WIN64 1 +#elif defined(_WIN32) // note: _M_PPC implies _WIN32 +#define PX_WIN32 1 +#elif defined(__ANDROID__) +#define PX_ANDROID 1 +#elif defined(__linux__) || defined (__EMSCRIPTEN__) // note: __ANDROID__ implies __linux__ +#define PX_LINUX 1 +#elif defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) +#define PX_IOS 1 +#elif defined(__APPLE__) +#define PX_OSX 1 +#elif defined(__ORBIS__) +#define PX_PS4 1 +#elif defined(__NX__) +#define PX_SWITCH 1 +#else +#error "Unknown operating system" +#endif + +/** +Architecture defines, see http://sourceforge.net/p/predef/wiki/Architectures/ +*/ +#if defined(__x86_64__) || defined(_M_X64) // ps4 compiler defines _M_X64 without value +#define PX_X64 1 +#elif defined(__i386__) || defined(_M_IX86) || defined (__EMSCRIPTEN__) +#define PX_X86 1 +#elif defined(__arm64__) || defined(__aarch64__) +#define PX_A64 1 +#elif defined(__arm__) || defined(_M_ARM) +#define PX_ARM 1 +#elif defined(__ppc__) || defined(_M_PPC) || defined(__CELLOS_LV2__) +#define PX_PPC 1 +#else +#error "Unknown architecture" +#endif + +/** +SIMD defines +*/ +#if !defined(PX_SIMD_DISABLED) +#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) || (defined (__EMSCRIPTEN__) && defined(__SSE2__)) +#define PX_SSE2 1 +#endif +#if defined(_M_ARM) || defined(__ARM_NEON__) || defined(__ARM_NEON) +#define PX_NEON 1 +#endif +#if defined(_M_PPC) || defined(__CELLOS_LV2__) +#define PX_VMX 1 +#endif +#endif + +/** +define anything not defined on this platform to 0 +*/ +#ifndef PX_VC +#define PX_VC 0 +#endif +#ifndef PX_CLANG +#define PX_CLANG 0 +#endif +#ifndef PX_GCC +#define PX_GCC 0 +#endif +#ifndef PX_XBOXONE +#define PX_XBOXONE 0 +#endif +#ifndef PX_WIN64 +#define PX_WIN64 0 +#endif +#ifndef PX_WIN32 +#define PX_WIN32 0 +#endif +#ifndef PX_ANDROID +#define PX_ANDROID 0 +#endif +#ifndef PX_LINUX +#define PX_LINUX 0 +#endif +#ifndef PX_IOS +#define PX_IOS 0 +#endif +#ifndef PX_OSX +#define PX_OSX 0 +#endif +#ifndef PX_PS4 +#define PX_PS4 0 +#endif +#ifndef PX_SWITCH +#define PX_SWITCH 0 +#endif +#ifndef PX_UWP +#define PX_UWP 0 +#endif +#ifndef PX_X64 +#define PX_X64 0 +#endif +#ifndef PX_X86 +#define PX_X86 0 +#endif +#ifndef PX_A64 +#define PX_A64 0 +#endif +#ifndef PX_ARM +#define PX_ARM 0 +#endif +#ifndef PX_PPC +#define PX_PPC 0 +#endif +#ifndef PX_SSE2 +#define PX_SSE2 0 +#endif +#ifndef PX_NEON +#define PX_NEON 0 +#endif +#ifndef PX_VMX +#define PX_VMX 0 +#endif + +/* +define anything not defined through the command line to 0 +*/ +#ifndef PX_DEBUG +#define PX_DEBUG 0 +#endif +#ifndef PX_CHECKED +#define PX_CHECKED 0 +#endif +#ifndef PX_PROFILE +#define PX_PROFILE 0 +#endif +#ifndef PX_DEBUG_CRT +#define PX_DEBUG_CRT 0 +#endif +#ifndef PX_NVTX +#define PX_NVTX 0 +#endif +#ifndef PX_DOXYGEN +#define PX_DOXYGEN 0 +#endif + +/** +family shortcuts +*/ +// compiler +#define PX_GCC_FAMILY (PX_CLANG || PX_GCC) +// os +#define PX_WINDOWS_FAMILY (PX_WIN32 || PX_WIN64 || PX_UWP) +#define PX_MICROSOFT_FAMILY (PX_XBOXONE || PX_WINDOWS_FAMILY) +#define PX_LINUX_FAMILY (PX_LINUX || PX_ANDROID) +#define PX_APPLE_FAMILY (PX_IOS || PX_OSX) // equivalent to #if __APPLE__ +#define PX_UNIX_FAMILY (PX_LINUX_FAMILY || PX_APPLE_FAMILY) // shortcut for unix/posix platforms +#if defined(__EMSCRIPTEN__) +#define PX_EMSCRIPTEN 1 +#else +#define PX_EMSCRIPTEN 0 +#endif +// architecture +#define PX_INTEL_FAMILY (PX_X64 || PX_X86) +#define PX_ARM_FAMILY (PX_ARM || PX_A64) +#define PX_P64_FAMILY (PX_X64 || PX_A64) // shortcut for 64-bit architectures + +/** +C++ standard library defines +*/ +#if defined(_LIBCPP_VERSION) || PX_WIN64 || PX_WIN32 || PX_PS4 || PX_XBOXONE || PX_UWP || PX_EMSCRIPTEN +#define PX_LIBCPP 1 +#else +#define PX_LIBCPP 0 +#endif + +// legacy define for PhysX +#define PX_WINDOWS (PX_WINDOWS_FAMILY && !PX_ARM_FAMILY) + +/** +Assert macro +*/ +#ifndef PX_ENABLE_ASSERTS +#if PX_DEBUG && !defined(__CUDACC__) +#define PX_ENABLE_ASSERTS 1 +#else +#define PX_ENABLE_ASSERTS 0 +#endif +#endif + +/** +DLL export macros +*/ +#ifndef PX_C_EXPORT +#if PX_WINDOWS_FAMILY || PX_LINUX +#define PX_C_EXPORT extern "C" +#else +#define PX_C_EXPORT +#endif +#endif + +#if PX_UNIX_FAMILY&& __GNUC__ >= 4 +#define PX_UNIX_EXPORT __attribute__((visibility("default"))) +#else +#define PX_UNIX_EXPORT +#endif + +#if PX_WINDOWS_FAMILY +#define PX_DLL_EXPORT __declspec(dllexport) +#define PX_DLL_IMPORT __declspec(dllimport) +#else +#define PX_DLL_EXPORT PX_UNIX_EXPORT +#define PX_DLL_IMPORT +#endif + +/** +Define API function declaration + +PX_FOUNDATION_DLL=1 - used by the DLL library (PhysXCommon) to export the API +PX_FOUNDATION_DLL=0 - for windows configurations where the PX_FOUNDATION_API is linked through standard static linking +no definition - this will allow DLLs and libraries to use the exported API from PhysXCommon + +*/ + +#if PX_WINDOWS_FAMILY && !PX_ARM_FAMILY +#ifndef PX_FOUNDATION_DLL +#define PX_FOUNDATION_API PX_DLL_IMPORT +#elif PX_FOUNDATION_DLL +#define PX_FOUNDATION_API PX_DLL_EXPORT +#endif +#elif PX_UNIX_FAMILY +#ifdef PX_FOUNDATION_DLL +#define PX_FOUNDATION_API PX_UNIX_EXPORT +#endif +#endif + +#ifndef PX_FOUNDATION_API +#define PX_FOUNDATION_API +#endif + +/** +Calling convention +*/ +#ifndef PX_CALL_CONV +#if PX_MICROSOFT_FAMILY +#define PX_CALL_CONV __cdecl +#else +#define PX_CALL_CONV +#endif +#endif + +/** +Pack macros - disabled on SPU because they are not supported +*/ +#if PX_VC +#define PX_PUSH_PACK_DEFAULT __pragma(pack(push, 8)) +#define PX_POP_PACK __pragma(pack(pop)) +#elif PX_GCC_FAMILY +#define PX_PUSH_PACK_DEFAULT _Pragma("pack(push, 8)") +#define PX_POP_PACK _Pragma("pack(pop)") +#else +#define PX_PUSH_PACK_DEFAULT +#define PX_POP_PACK +#endif + +/** +Inline macro +*/ +#define PX_INLINE inline +#if PX_MICROSOFT_FAMILY +#pragma inline_depth(255) +#endif + +/** +Force inline macro +*/ +#if PX_VC +#define PX_FORCE_INLINE __forceinline +#elif PX_LINUX // Workaround; Fedora Core 3 do not agree with force inline and PxcPool +#define PX_FORCE_INLINE inline +#elif PX_GCC_FAMILY +#define PX_FORCE_INLINE inline __attribute__((always_inline)) +#else +#define PX_FORCE_INLINE inline +#endif + +/** +Noinline macro +*/ +#if PX_MICROSOFT_FAMILY +#define PX_NOINLINE __declspec(noinline) +#elif PX_GCC_FAMILY +#define PX_NOINLINE __attribute__((noinline)) +#else +#define PX_NOINLINE +#endif + +/** +Restrict macro +*/ +#if defined(__CUDACC__) +#define PX_RESTRICT __restrict__ +#else +#define PX_RESTRICT __restrict +#endif + +/** +Noalias macro +*/ +#if PX_MICROSOFT_FAMILY +#define PX_NOALIAS __declspec(noalias) +#else +#define PX_NOALIAS +#endif + +/** +Alignment macros + +PX_ALIGN_PREFIX and PX_ALIGN_SUFFIX can be used for type alignment instead of aligning individual variables as follows: +PX_ALIGN_PREFIX(16) +struct A { +... +} PX_ALIGN_SUFFIX(16); +This declaration style is parsed correctly by Visual Assist. + +*/ +#ifndef PX_ALIGN +#if PX_MICROSOFT_FAMILY +#define PX_ALIGN(alignment, decl) __declspec(align(alignment)) decl +#define PX_ALIGN_PREFIX(alignment) __declspec(align(alignment)) +#define PX_ALIGN_SUFFIX(alignment) +#elif PX_GCC_FAMILY +#define PX_ALIGN(alignment, decl) decl __attribute__((aligned(alignment))) +#define PX_ALIGN_PREFIX(alignment) +#define PX_ALIGN_SUFFIX(alignment) __attribute__((aligned(alignment))) +#elif defined __CUDACC__ +#define PX_ALIGN(alignment, decl) __align__(alignment) decl +#define PX_ALIGN_PREFIX(alignment) +#define PX_ALIGN_SUFFIX(alignment) __align__(alignment)) +#else +#define PX_ALIGN(alignment, decl) +#define PX_ALIGN_PREFIX(alignment) +#define PX_ALIGN_SUFFIX(alignment) +#endif +#endif + +/** +Deprecated macro +- To deprecate a function: Place PX_DEPRECATED at the start of the function header (leftmost word). +- To deprecate a 'typedef', a 'struct' or a 'class': Place PX_DEPRECATED directly after the keywords ('typdef', +'struct', 'class'). + +Use these macro definitions to create warnings for deprecated functions +\#define PX_DEPRECATED __declspec(deprecated) // Microsoft +\#define PX_DEPRECATED __attribute__((deprecated())) // GCC +*/ +#define PX_DEPRECATED + +/** +General defines +*/ + +// static assert +#if(defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || (PX_PS4) || (PX_APPLE_FAMILY) || (PX_SWITCH) || (PX_CLANG && PX_ARM) +#define PX_COMPILE_TIME_ASSERT(exp) typedef char PX_CONCAT(PxCompileTimeAssert_Dummy, __COUNTER__)[(exp) ? 1 : -1] __attribute__((unused)) +#else +#define PX_COMPILE_TIME_ASSERT(exp) typedef char PxCompileTimeAssert_Dummy[(exp) ? 1 : -1] +#endif + +#if PX_GCC_FAMILY +#define PX_OFFSET_OF(X, Y) __builtin_offsetof(X, Y) +#else +#define PX_OFFSET_OF(X, Y) offsetof(X, Y) +#endif + +#define PX_OFFSETOF_BASE 0x100 // casting the null ptr takes a special-case code path, which we don't want +#define PX_OFFSET_OF_RT(Class, Member) \ + (reinterpret_cast(&reinterpret_cast(PX_OFFSETOF_BASE)->Member) - size_t(PX_OFFSETOF_BASE)) + +// check that exactly one of NDEBUG and _DEBUG is defined +#if !defined(NDEBUG) ^ defined(_DEBUG) +#error Exactly one of NDEBUG and _DEBUG needs to be defined! +#endif + +// make sure PX_CHECKED is defined in all _DEBUG configurations as well +#if !PX_CHECKED && PX_DEBUG +#error PX_CHECKED must be defined when PX_DEBUG is defined +#endif + +#ifdef __CUDACC__ +#define PX_CUDA_CALLABLE __host__ __device__ +#else +#define PX_CUDA_CALLABLE +#endif + +// avoid unreferenced parameter warning +// preferred solution: omit the parameter's name from the declaration +template +PX_CUDA_CALLABLE PX_INLINE void PX_UNUSED(T const&) +{ +} + +// Ensure that the application hasn't tweaked the pack value to less than 8, which would break +// matching between the API headers and the binaries +// This assert works on win32/win64, but may need further specialization on other platforms. +// Some GCC compilers need the compiler flag -malign-double to be set. +// Apparently the apple-clang-llvm compiler doesn't support malign-double. +#if PX_PS4 || PX_APPLE_FAMILY || (PX_CLANG && !PX_ARM) +struct PxPackValidation +{ + char _; + long a; +}; +#elif PX_ANDROID || (PX_CLANG && PX_ARM) +struct PxPackValidation +{ + char _; + double a; +}; +#else +struct PxPackValidation +{ + char _; + long long a; +}; +#endif +// clang (as of version 3.9) cannot align doubles on 8 byte boundary when compiling for Intel 32 bit target +#if !PX_APPLE_FAMILY && !PX_EMSCRIPTEN && !(PX_CLANG && PX_X86) +PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(PxPackValidation, a) == 8); +#endif + +// use in a cpp file to suppress LNK4221 +#if PX_VC +#define PX_DUMMY_SYMBOL \ + namespace \ + { \ + char PxDummySymbol; \ + } +#else +#define PX_DUMMY_SYMBOL +#endif + +#if PX_GCC_FAMILY +#define PX_WEAK_SYMBOL __attribute__((weak)) // this is to support SIMD constant merging in template specialization +#else +#define PX_WEAK_SYMBOL +#endif + +// Macro for avoiding default assignment and copy, because doing this by inheritance can increase class size on some +// platforms. +#define PX_NOCOPY(Class) \ + \ +protected: \ + Class(const Class&); \ + Class& operator=(const Class&); + +#ifndef DISABLE_CUDA_PHYSX +//CUDA is currently supported only on windows +#define PX_SUPPORT_GPU_PHYSX ((PX_WINDOWS_FAMILY) || (PX_LINUX && PX_X64)) +#else +#define PX_SUPPORT_GPU_PHYSX 0 +#endif + +#define PX_SUPPORT_COMPUTE_PHYSX 0 + +#ifndef PX_SUPPORT_EXTERN_TEMPLATE +#define PX_SUPPORT_EXTERN_TEMPLATE ((!PX_ANDROID) && (PX_VC != 11)) +#else +#define PX_SUPPORT_EXTERN_TEMPLATE 0 +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXPREPROCESSOR_H diff --git a/src/PhysX/pxshared/include/foundation/PxProfiler.h b/src/PhysX/pxshared/include/foundation/PxProfiler.h new file mode 100644 index 000000000..82d978807 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxProfiler.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXFOUNDATION_PXPROFILER_H +#define PXFOUNDATION_PXPROFILER_H + +#include "foundation/Px.h" + +namespace physx +{ + +/** +\brief The pure virtual callback interface for general purpose instrumentation and profiling of GameWorks modules as +well as applications +*/ +class PxProfilerCallback +{ +protected: + virtual ~PxProfilerCallback() {} + +public: + /************************************************************************************************************************** + Instrumented profiling events + ***************************************************************************************************************************/ + + /** + \brief Mark the beginning of a nested profile block + \param[in] eventName Event name. Must be a persistent const char * + \param[in] detached True for cross thread events + \param[in] contextId the context id of this zone. Zones with the same id belong to the same group. 0 is used for no specific group. + \return Returns implementation-specific profiler data for this event + */ + virtual void* zoneStart(const char* eventName, bool detached, uint64_t contextId) = 0; + + /** + \brief Mark the end of a nested profile block + \param[in] profilerData The data returned by the corresponding zoneStart call (or NULL if not available) + \param[in] eventName The name of the zone ending, must match the corresponding name passed with 'zoneStart'. Must be a persistent const char *. + \param[in] detached True for cross thread events. Should match the value passed to zoneStart. + \param[in] contextId The context of this zone. Should match the value passed to zoneStart. + + \note eventName plus contextId can be used to uniquely match up start and end of a zone. + */ + virtual void zoneEnd(void* profilerData, const char* eventName, bool detached, uint64_t contextId) = 0; +}; + +class PxProfileScoped +{ + public: + PX_FORCE_INLINE PxProfileScoped(PxProfilerCallback* callback, const char* eventName, bool detached, uint64_t contextId) : mCallback(callback), mProfilerData(NULL) + { + if(mCallback) + { + mEventName = eventName; + mContextId = contextId; + mDetached = detached; + mProfilerData = mCallback->zoneStart(eventName, detached, contextId); + } + } + + PX_FORCE_INLINE ~PxProfileScoped() + { + if(mCallback) + mCallback->zoneEnd(mProfilerData, mEventName, mDetached, mContextId); + } + PxProfilerCallback* mCallback; + const char* mEventName; + void* mProfilerData; + uint64_t mContextId; + bool mDetached; +}; + +} // end of physx namespace + +#endif // PXFOUNDATION_PXPROFILER_H diff --git a/src/PhysX/pxshared/include/foundation/PxQuat.h b/src/PhysX/pxshared/include/foundation/PxQuat.h new file mode 100644 index 000000000..7ae30e9cb --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxQuat.h @@ -0,0 +1,403 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXQUAT_H +#define PXFOUNDATION_PXQUAT_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxVec3.h" +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief This is a quaternion class. For more information on quaternion mathematics +consult a mathematics source on complex numbers. + +*/ + +class PxQuat +{ + public: + /** + \brief Default constructor, does not do any initialization. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat() + { + } + + //! identity constructor + PX_CUDA_CALLABLE PX_INLINE PxQuat(PxIDENTITY r) : x(0.0f), y(0.0f), z(0.0f), w(1.0f) + { + PX_UNUSED(r); + } + + /** + \brief Constructor from a scalar: sets the real part w to the scalar value, and the imaginary parts (x,y,z) to zero + */ + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat(float r) : x(0.0f), y(0.0f), z(0.0f), w(r) + { + } + + /** + \brief Constructor. Take note of the order of the elements! + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat(float nx, float ny, float nz, float nw) : x(nx), y(ny), z(nz), w(nw) + { + } + + /** + \brief Creates from angle-axis representation. + + Axis must be normalized! + + Angle is in radians! + + Unit: Radians + */ + PX_CUDA_CALLABLE PX_INLINE PxQuat(float angleRadians, const PxVec3& unitAxis) + { + PX_ASSERT(PxAbs(1.0f - unitAxis.magnitude()) < 1e-3f); + const float a = angleRadians * 0.5f; + const float s = PxSin(a); + w = PxCos(a); + x = unitAxis.x * s; + y = unitAxis.y * s; + z = unitAxis.z * s; + } + + /** + \brief Copy ctor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat(const PxQuat& v) : x(v.x), y(v.y), z(v.z), w(v.w) + { + } + + /** + \brief Creates from orientation matrix. + + \param[in] m Rotation matrix to extract quaternion from. + */ + PX_CUDA_CALLABLE PX_INLINE explicit PxQuat(const PxMat33& m); /* defined in PxMat33.h */ + + /** + \brief returns true if quat is identity + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isIdentity() const + { + return x==0.0f && y==0.0f && z==0.0f && w==1.0f; + } + + /** + \brief returns true if all elements are finite (not NAN or INF, etc.) + */ + PX_CUDA_CALLABLE bool isFinite() const + { + return PxIsFinite(x) && PxIsFinite(y) && PxIsFinite(z) && PxIsFinite(w); + } + + /** + \brief returns true if finite and magnitude is close to unit + */ + PX_CUDA_CALLABLE bool isUnit() const + { + const float unitTolerance = 1e-4f; + return isFinite() && PxAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns true if finite and magnitude is reasonably close to unit to allow for some accumulation of error vs + isValid + */ + PX_CUDA_CALLABLE bool isSane() const + { + const float unitTolerance = 1e-2f; + return isFinite() && PxAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns true if the two quaternions are exactly equal + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxQuat& q) const + { + return x == q.x && y == q.y && z == q.z && w == q.w; + } + + /** + \brief converts this quaternion to angle-axis representation + */ + PX_CUDA_CALLABLE PX_INLINE void toRadiansAndUnitAxis(float& angle, PxVec3& axis) const + { + const float quatEpsilon = 1.0e-8f; + const float s2 = x * x + y * y + z * z; + if(s2 < quatEpsilon * quatEpsilon) // can't extract a sensible axis + { + angle = 0.0f; + axis = PxVec3(1.0f, 0.0f, 0.0f); + } + else + { + const float s = PxRecipSqrt(s2); + axis = PxVec3(x, y, z) * s; + angle = PxAbs(w) < quatEpsilon ? PxPi : PxAtan2(s2 * s, w) * 2.0f; + } + } + + /** + \brief Gets the angle between this quat and the identity quaternion. + + Unit: Radians + */ + PX_CUDA_CALLABLE PX_INLINE float getAngle() const + { + return PxAcos(w) * 2.0f; + } + + /** + \brief Gets the angle between this quat and the argument + + Unit: Radians + */ + PX_CUDA_CALLABLE PX_INLINE float getAngle(const PxQuat& q) const + { + return PxAcos(dot(q)) * 2.0f; + } + + /** + \brief This is the squared 4D vector length, should be 1 for unit quaternions. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float magnitudeSquared() const + { + return x * x + y * y + z * z + w * w; + } + + /** + \brief returns the scalar product of this and other. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float dot(const PxQuat& v) const + { + return x * v.x + y * v.y + z * v.z + w * v.w; + } + + PX_CUDA_CALLABLE PX_INLINE PxQuat getNormalized() const + { + const float s = 1.0f / magnitude(); + return PxQuat(x * s, y * s, z * s, w * s); + } + + PX_CUDA_CALLABLE PX_INLINE float magnitude() const + { + return PxSqrt(magnitudeSquared()); + } + + // modifiers: + /** + \brief maps to the closest unit quaternion. + */ + PX_CUDA_CALLABLE PX_INLINE float normalize() // convert this PxQuat to a unit quaternion + { + const float mag = magnitude(); + if(mag != 0.0f) + { + const float imag = 1.0f / mag; + + x *= imag; + y *= imag; + z *= imag; + w *= imag; + } + return mag; + } + + /* + \brief returns the conjugate. + + \note for unit quaternions, this is the inverse. + */ + PX_CUDA_CALLABLE PX_INLINE PxQuat getConjugate() const + { + return PxQuat(-x, -y, -z, w); + } + + /* + \brief returns imaginary part. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec3 getImaginaryPart() const + { + return PxVec3(x, y, z); + } + + /** brief computes rotation of x-axis */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getBasisVector0() const + { + const float x2 = x * 2.0f; + const float w2 = w * 2.0f; + return PxVec3((w * w2) - 1.0f + x * x2, (z * w2) + y * x2, (-y * w2) + z * x2); + } + + /** brief computes rotation of y-axis */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getBasisVector1() const + { + const float y2 = y * 2.0f; + const float w2 = w * 2.0f; + return PxVec3((-z * w2) + x * y2, (w * w2) - 1.0f + y * y2, (x * w2) + z * y2); + } + + /** brief computes rotation of z-axis */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getBasisVector2() const + { + const float z2 = z * 2.0f; + const float w2 = w * 2.0f; + return PxVec3((y * w2) + x * z2, (-x * w2) + y * z2, (w * w2) - 1.0f + z * z2); + } + + /** + rotates passed vec by this (assumed unitary) + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3 rotate(const PxVec3& v) const + { + const float vx = 2.0f * v.x; + const float vy = 2.0f * v.y; + const float vz = 2.0f * v.z; + const float w2 = w * w - 0.5f; + const float dot2 = (x * vx + y * vy + z * vz); + return PxVec3((vx * w2 + (y * vz - z * vy) * w + x * dot2), (vy * w2 + (z * vx - x * vz) * w + y * dot2), + (vz * w2 + (x * vy - y * vx) * w + z * dot2)); + } + + /** + inverse rotates passed vec by this (assumed unitary) + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3 rotateInv(const PxVec3& v) const + { + const float vx = 2.0f * v.x; + const float vy = 2.0f * v.y; + const float vz = 2.0f * v.z; + const float w2 = w * w - 0.5f; + const float dot2 = (x * vx + y * vy + z * vz); + return PxVec3((vx * w2 - (y * vz - z * vy) * w + x * dot2), (vy * w2 - (z * vx - x * vz) * w + y * dot2), + (vz * w2 - (x * vy - y * vx) * w + z * dot2)); + } + + /** + \brief Assignment operator + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat& operator=(const PxQuat& p) + { + x = p.x; + y = p.y; + z = p.z; + w = p.w; + return *this; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat& operator*=(const PxQuat& q) + { + const float tx = w * q.x + q.w * x + y * q.z - q.y * z; + const float ty = w * q.y + q.w * y + z * q.x - q.z * x; + const float tz = w * q.z + q.w * z + x * q.y - q.x * y; + + w = w * q.w - q.x * x - y * q.y - q.z * z; + x = tx; + y = ty; + z = tz; + + return *this; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat& operator+=(const PxQuat& q) + { + x += q.x; + y += q.y; + z += q.z; + w += q.w; + return *this; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat& operator-=(const PxQuat& q) + { + x -= q.x; + y -= q.y; + z -= q.z; + w -= q.w; + return *this; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat& operator*=(const float s) + { + x *= s; + y *= s; + z *= s; + w *= s; + return *this; + } + + /** quaternion multiplication */ + PX_CUDA_CALLABLE PX_INLINE PxQuat operator*(const PxQuat& q) const + { + return PxQuat(w * q.x + q.w * x + y * q.z - q.y * z, w * q.y + q.w * y + z * q.x - q.z * x, + w * q.z + q.w * z + x * q.y - q.x * y, w * q.w - x * q.x - y * q.y - z * q.z); + } + + /** quaternion addition */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat operator+(const PxQuat& q) const + { + return PxQuat(x + q.x, y + q.y, z + q.z, w + q.w); + } + + /** quaternion subtraction */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat operator-() const + { + return PxQuat(-x, -y, -z, -w); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat operator-(const PxQuat& q) const + { + return PxQuat(x - q.x, y - q.y, z - q.z, w - q.w); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat operator*(float r) const + { + return PxQuat(x * r, y * r, z * r, w * r); + } + + /** the quaternion elements */ + float x, y, z, w; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXQUAT_H diff --git a/src/PhysX/pxshared/include/foundation/PxSimpleTypes.h b/src/PhysX/pxshared/include/foundation/PxSimpleTypes.h new file mode 100644 index 000000000..162b788f5 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxSimpleTypes.h @@ -0,0 +1,112 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXSIMPLETYPES_H +#define PXFOUNDATION_PXSIMPLETYPES_H + +/** \addtogroup foundation + @{ +*/ + +// Platform specific types: +// Design note: Its OK to use int for general loop variables and temps. + +#include "foundation/PxPreprocessor.h" +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4668) // suppressing warning generated by Microsoft Visual Studio when including this standard +// header +#endif + +#if PX_LINUX +#define __STDC_LIMIT_MACROS +#endif + +#include +#if PX_VC +#pragma warning(pop) +#endif + +#if PX_VC // we could use inttypes.h starting with VC12 +#define PX_PRIu64 "I64u" +#else +#if !PX_PS4 && !PX_APPLE_FAMILY +#define __STDC_FORMAT_MACROS +#endif +#include +#define PX_PRIu64 PRIu64 +#endif + +namespace physx +{ +typedef int64_t PxI64; +typedef uint64_t PxU64; +typedef int32_t PxI32; +typedef uint32_t PxU32; +typedef int16_t PxI16; +typedef uint16_t PxU16; +typedef int8_t PxI8; +typedef uint8_t PxU8; +typedef float PxF32; +typedef double PxF64; +typedef float PxReal; +} + +// Type ranges + +// These are here because we sometimes have non-IEEE compliant platforms to deal with. +// Removal is under consideration (issue GWSD-34) + +#define PX_MAX_F32 3.4028234663852885981170418348452e+38F +// maximum possible float value +#define PX_MAX_F64 DBL_MAX // maximum possible double value + +#define PX_EPS_F32 FLT_EPSILON // maximum relative error of float rounding +#define PX_EPS_F64 DBL_EPSILON // maximum relative error of double rounding + +#define PX_MAX_REAL PX_MAX_F32 +#define PX_EPS_REAL PX_EPS_F32 +#define PX_NORMALIZATION_EPSILON float(1e-20f) + +// Legacy type ranges used by PhysX +#define PX_MAX_I8 INT8_MAX +#define PX_MIN_I8 INT8_MIN +#define PX_MAX_U8 UINT8_MAX +#define PX_MIN_U8 UINT8_MIN +#define PX_MAX_I16 INT16_MAX +#define PX_MIN_I16 INT16_MIN +#define PX_MAX_U16 UINT16_MAX +#define PX_MIN_U16 UINT16_MIN +#define PX_MAX_I32 INT32_MAX +#define PX_MIN_I32 INT32_MIN +#define PX_MAX_U32 UINT32_MAX +#define PX_MIN_U32 UINT32_MIN + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXSIMPLETYPES_H diff --git a/src/PhysX/pxshared/include/foundation/PxStrideIterator.h b/src/PhysX/pxshared/include/foundation/PxStrideIterator.h new file mode 100644 index 000000000..786938e3d --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxStrideIterator.h @@ -0,0 +1,353 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXSTRIDEITERATOR_H +#define PXFOUNDATION_PXSTRIDEITERATOR_H + +#include "foundation/Px.h" +#include "foundation/PxAssert.h" + +/** \addtogroup foundation + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Iterator class for iterating over arrays of data that may be interleaved with other data. + +This class is used for iterating over arrays of elements that may have a larger element to element +offset, called the stride, than the size of the element itself (non-contiguous). + +The template parameter T denotes the type of the element accessed. The stride itself +is stored as a member field so multiple instances of a PxStrideIterator class can have +different strides. This is useful for cases were the stride depends on runtime configuration. + +The stride iterator can be used for index based access, e.g.: +\code + PxStrideIterator strideArray(...); + for (unsigned i = 0; i < 10; ++i) + { + PxVec3& vec = strideArray[i]; + ... + } +\endcode +or iteration by increment, e.g.: +\code + PxStrideIterator strideBegin(...); + PxStrideIterator strideEnd(strideBegin + 10); + for (PxStrideIterator it = strideBegin; it < strideEnd; ++it) + { + PxVec3& vec = *it; + ... + } +\endcode + +Two special cases: +- A stride of sizeof(T) represents a regular c array of type T. +- A stride of 0 can be used to describe re-occurrence of the same element multiple times. + +*/ +template +class PxStrideIterator +{ + +#if !PX_DOXYGEN + template + struct StripConst + { + typedef X Type; + }; + + template + struct StripConst + { + typedef X Type; + }; +#endif + + public: + /** + \brief Constructor. + + Optionally takes a pointer to an element and a stride. + + \param[in] ptr pointer to element, defaults to NULL. + \param[in] stride stride for accessing consecutive elements, defaults to the size of one element. + */ + explicit PX_INLINE PxStrideIterator(T* ptr = NULL, PxU32 stride = sizeof(T)) : mPtr(ptr), mStride(stride) + { + PX_ASSERT(mStride == 0 || sizeof(T) <= mStride); + } + + /** + \brief Copy constructor. + + \param[in] strideIterator PxStrideIterator to be copied. + */ + PX_INLINE PxStrideIterator(const PxStrideIterator::Type>& strideIterator) + : mPtr(strideIterator.ptr()), mStride(strideIterator.stride()) + { + PX_ASSERT(mStride == 0 || sizeof(T) <= mStride); + } + + /** + \brief Get pointer to element. + */ + PX_INLINE T* ptr() const + { + return mPtr; + } + + /** + \brief Get stride. + */ + PX_INLINE PxU32 stride() const + { + return mStride; + } + + /** + \brief Indirection operator. + */ + PX_INLINE T& operator*() const + { + return *mPtr; + } + + /** + \brief Dereferencing operator. + */ + PX_INLINE T* operator->() const + { + return mPtr; + } + + /** + \brief Indexing operator. + */ + PX_INLINE T& operator[](unsigned int i) const + { + return *byteAdd(mPtr, i * stride()); + } + + /** + \brief Pre-increment operator. + */ + PX_INLINE PxStrideIterator& operator++() + { + mPtr = byteAdd(mPtr, stride()); + return *this; + } + + /** + \brief Post-increment operator. + */ + PX_INLINE PxStrideIterator operator++(int) + { + PxStrideIterator tmp = *this; + mPtr = byteAdd(mPtr, stride()); + return tmp; + } + + /** + \brief Pre-decrement operator. + */ + PX_INLINE PxStrideIterator& operator--() + { + mPtr = byteSub(mPtr, stride()); + return *this; + } + + /** + \brief Post-decrement operator. + */ + PX_INLINE PxStrideIterator operator--(int) + { + PxStrideIterator tmp = *this; + mPtr = byteSub(mPtr, stride()); + return tmp; + } + + /** + \brief Addition operator. + */ + PX_INLINE PxStrideIterator operator+(unsigned int i) const + { + return PxStrideIterator(byteAdd(mPtr, i * stride()), stride()); + } + + /** + \brief Subtraction operator. + */ + PX_INLINE PxStrideIterator operator-(unsigned int i) const + { + return PxStrideIterator(byteSub(mPtr, i * stride()), stride()); + } + + /** + \brief Addition compound assignment operator. + */ + PX_INLINE PxStrideIterator& operator+=(unsigned int i) + { + mPtr = byteAdd(mPtr, i * stride()); + return *this; + } + + /** + \brief Subtraction compound assignment operator. + */ + PX_INLINE PxStrideIterator& operator-=(unsigned int i) + { + mPtr = byteSub(mPtr, i * stride()); + return *this; + } + + /** + \brief Iterator difference. + */ + PX_INLINE int operator-(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + int byteDiff = static_cast(reinterpret_cast(mPtr) - reinterpret_cast(other.mPtr)); + return byteDiff / static_cast(stride()); + } + + /** + \brief Equality operator. + */ + PX_INLINE bool operator==(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr == other.mPtr; + } + + /** + \brief Inequality operator. + */ + PX_INLINE bool operator!=(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr != other.mPtr; + } + + /** + \brief Less than operator. + */ + PX_INLINE bool operator<(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr < other.mPtr; + } + + /** + \brief Greater than operator. + */ + PX_INLINE bool operator>(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr > other.mPtr; + } + + /** + \brief Less or equal than operator. + */ + PX_INLINE bool operator<=(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr <= other.mPtr; + } + + /** + \brief Greater or equal than operator. + */ + PX_INLINE bool operator>=(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr >= other.mPtr; + } + + private: + PX_INLINE static T* byteAdd(T* ptr, PxU32 bytes) + { + return const_cast(reinterpret_cast(reinterpret_cast(ptr) + bytes)); + } + + PX_INLINE static T* byteSub(T* ptr, PxU32 bytes) + { + return const_cast(reinterpret_cast(reinterpret_cast(ptr) - bytes)); + } + + PX_INLINE bool isCompatible(const PxStrideIterator& other) const + { + int byteDiff = static_cast(reinterpret_cast(mPtr) - reinterpret_cast(other.mPtr)); + return (stride() == other.stride()) && (abs(byteDiff) % stride() == 0); + } + + T* mPtr; + PxU32 mStride; +}; + +/** +\brief Addition operator. +*/ +template +PX_INLINE PxStrideIterator operator+(int i, PxStrideIterator it) +{ + it += i; + return it; +} + +/** +\brief Stride iterator factory function which infers the iterator type. +*/ +template +PX_INLINE PxStrideIterator PxMakeIterator(T* ptr, PxU32 stride = sizeof(T)) +{ + return PxStrideIterator(ptr, stride); +} + +/** +\brief Stride iterator factory function which infers the iterator type. +*/ +template +PX_INLINE PxStrideIterator PxMakeIterator(const T* ptr, PxU32 stride = sizeof(T)) +{ + return PxStrideIterator(ptr, stride); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PXFOUNDATION_PXSTRIDEITERATOR_H diff --git a/src/PhysX/pxshared/include/foundation/PxTransform.h b/src/PhysX/pxshared/include/foundation/PxTransform.h new file mode 100644 index 000000000..8b29584f3 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxTransform.h @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXTRANSFORM_H +#define PXFOUNDATION_PXTRANSFORM_H +/** \addtogroup foundation + @{ +*/ + +#include "foundation/PxQuat.h" +#include "foundation/PxPlane.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/*! +\brief class representing a rigid euclidean transform as a quaternion and a vector +*/ + +class PxTransform +{ + public: + PxQuat q; + PxVec3 p; + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform() + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE explicit PxTransform(const PxVec3& position) : q(PxIdentity), p(position) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE explicit PxTransform(PxIDENTITY r) : q(PxIdentity), p(PxZero) + { + PX_UNUSED(r); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE explicit PxTransform(const PxQuat& orientation) : q(orientation), p(0) + { + PX_ASSERT(orientation.isSane()); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform(float x, float y, float z, PxQuat aQ = PxQuat(PxIdentity)) + : q(aQ), p(x, y, z) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform(const PxVec3& p0, const PxQuat& q0) : q(q0), p(p0) + { + PX_ASSERT(q0.isSane()); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE explicit PxTransform(const PxMat44& m); // defined in PxMat44.h + + /** + \brief returns true if the two transforms are exactly equal + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxTransform& t) const + { + return p == t.p && q == t.q; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform operator*(const PxTransform& x) const + { + PX_ASSERT(x.isSane()); + return transform(x); + } + + //! Equals matrix multiplication + PX_CUDA_CALLABLE PX_INLINE PxTransform& operator*=(PxTransform& other) + { + *this = *this * other; + return *this; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform getInverse() const + { + PX_ASSERT(isFinite()); + return PxTransform(q.rotateInv(-p), q.getConjugate()); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 transform(const PxVec3& input) const + { + PX_ASSERT(isFinite()); + return q.rotate(input) + p; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 transformInv(const PxVec3& input) const + { + PX_ASSERT(isFinite()); + return q.rotateInv(input - p); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 rotate(const PxVec3& input) const + { + PX_ASSERT(isFinite()); + return q.rotate(input); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 rotateInv(const PxVec3& input) const + { + PX_ASSERT(isFinite()); + return q.rotateInv(input); + } + + //! Transform transform to parent (returns compound transform: first src, then *this) + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform transform(const PxTransform& src) const + { + PX_ASSERT(src.isSane()); + PX_ASSERT(isSane()); + // src = [srct, srcr] -> [r*srct + t, r*srcr] + return PxTransform(q.rotate(src.p) + p, q * src.q); + } + + /** + \brief returns true if finite and q is a unit quaternion + */ + + PX_CUDA_CALLABLE bool isValid() const + { + return p.isFinite() && q.isFinite() && q.isUnit(); + } + + /** + \brief returns true if finite and quat magnitude is reasonably close to unit to allow for some accumulation of error + vs isValid + */ + + PX_CUDA_CALLABLE bool isSane() const + { + return isFinite() && q.isSane(); + } + + /** + \brief returns true if all elems are finite (not NAN or INF, etc.) + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite() const + { + return p.isFinite() && q.isFinite(); + } + + //! Transform transform from parent (returns compound transform: first src, then this->inverse) + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform transformInv(const PxTransform& src) const + { + PX_ASSERT(src.isSane()); + PX_ASSERT(isFinite()); + // src = [srct, srcr] -> [r^-1*(srct-t), r^-1*srcr] + PxQuat qinv = q.getConjugate(); + return PxTransform(qinv.rotate(src.p - p), qinv * src.q); + } + + /** + \brief transform plane + */ + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane transform(const PxPlane& plane) const + { + PxVec3 transformedNormal = rotate(plane.n); + return PxPlane(transformedNormal, plane.d - p.dot(transformedNormal)); + } + + /** + \brief inverse-transform plane + */ + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane inverseTransform(const PxPlane& plane) const + { + PxVec3 transformedNormal = rotateInv(plane.n); + return PxPlane(transformedNormal, plane.d + p.dot(plane.n)); + } + + /** + \brief return a normalized transform (i.e. one in which the quaternion has unit magnitude) + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform getNormalized() const + { + return PxTransform(p, q.getNormalized()); + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXTRANSFORM_H diff --git a/src/PhysX/pxshared/include/foundation/PxUnionCast.h b/src/PhysX/pxshared/include/foundation/PxUnionCast.h new file mode 100644 index 000000000..ec35911e0 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxUnionCast.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXUNIONCAST_H +#define PXFOUNDATION_PXUNIONCAST_H + +#include "foundation/Px.h" + +/** \addtogroup foundation +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +template +PX_FORCE_INLINE A PxUnionCast(B b) +{ + union AB + { + AB(B bb) : _b(bb) + { + } + B _b; + A _a; +// needed for clang 7 +#if PX_LINUX && PX_CLANG + } volatile u(b); +#else + } u(b); +#endif + return u._a; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ + +#endif // PXFOUNDATION_PXUNIONCAST_H diff --git a/src/PhysX/pxshared/include/foundation/PxVec2.h b/src/PhysX/pxshared/include/foundation/PxVec2.h new file mode 100644 index 000000000..0736eddb8 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxVec2.h @@ -0,0 +1,347 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXVEC2_H +#define PXFOUNDATION_PXVEC2_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxMath.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief 2 Element vector class. + +This is a 2-dimensional vector class with public data members. +*/ +class PxVec2 +{ + public: + /** + \brief default constructor leaves data uninitialized. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2() + { + } + + /** + \brief zero constructor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2(PxZERO r) : x(0.0f), y(0.0f) + { + PX_UNUSED(r); + } + + /** + \brief Assigns scalar parameter to all elements. + + Useful to initialize to zero or one. + + \param[in] a Value to assign to elements. + */ + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2(float a) : x(a), y(a) + { + } + + /** + \brief Initializes from 2 scalar parameters. + + \param[in] nx Value to initialize X component. + \param[in] ny Value to initialize Y component. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2(float nx, float ny) : x(nx), y(ny) + { + } + + /** + \brief Copy ctor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2(const PxVec2& v) : x(v.x), y(v.y) + { + } + + // Operators + + /** + \brief Assignment operator + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2& operator=(const PxVec2& p) + { + x = p.x; + y = p.y; + return *this; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float& operator[](int index) + { + PX_ASSERT(index >= 0 && index <= 1); + + return reinterpret_cast(this)[index]; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const float& operator[](int index) const + { + PX_ASSERT(index >= 0 && index <= 1); + + return reinterpret_cast(this)[index]; + } + + /** + \brief returns true if the two vectors are exactly equal. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator==(const PxVec2& v) const + { + return x == v.x && y == v.y; + } + + /** + \brief returns true if the two vectors are not exactly equal. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator!=(const PxVec2& v) const + { + return x != v.x || y != v.y; + } + + /** + \brief tests for exact zero vector + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isZero() const + { + return x == 0.0f && y == 0.0f; + } + + /** + \brief returns true if all 2 elems of the vector are finite (not NAN or INF, etc.) + */ + PX_CUDA_CALLABLE PX_INLINE bool isFinite() const + { + return PxIsFinite(x) && PxIsFinite(y); + } + + /** + \brief is normalized - used by API parameter validation + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isNormalized() const + { + const float unitTolerance = 1e-4f; + return isFinite() && PxAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns the squared magnitude + + Avoids calling PxSqrt()! + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float magnitudeSquared() const + { + return x * x + y * y; + } + + /** + \brief returns the magnitude + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float magnitude() const + { + return PxSqrt(magnitudeSquared()); + } + + /** + \brief negation + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 operator-() const + { + return PxVec2(-x, -y); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 operator+(const PxVec2& v) const + { + return PxVec2(x + v.x, y + v.y); + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 operator-(const PxVec2& v) const + { + return PxVec2(x - v.x, y - v.y); + } + + /** + \brief scalar post-multiplication + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 operator*(float f) const + { + return PxVec2(x * f, y * f); + } + + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 operator/(float f) const + { + f = 1.0f / f; // PT: inconsistent notation with operator /= + return PxVec2(x * f, y * f); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2& operator+=(const PxVec2& v) + { + x += v.x; + y += v.y; + return *this; + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2& operator-=(const PxVec2& v) + { + x -= v.x; + y -= v.y; + return *this; + } + + /** + \brief scalar multiplication + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2& operator*=(float f) + { + x *= f; + y *= f; + return *this; + } + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2& operator/=(float f) + { + f = 1.0f / f; // PT: inconsistent notation with operator / + x *= f; + y *= f; + return *this; + } + + /** + \brief returns the scalar product of this and other. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float dot(const PxVec2& v) const + { + return x * v.x + y * v.y; + } + + /** return a unit vector */ + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 getNormalized() const + { + const float m = magnitudeSquared(); + return m > 0.0f ? *this * PxRecipSqrt(m) : PxVec2(0, 0); + } + + /** + \brief normalizes the vector in place + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float normalize() + { + const float m = magnitude(); + if(m > 0.0f) + *this /= m; + return m; + } + + /** + \brief a[i] * b[i], for all i. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 multiply(const PxVec2& a) const + { + return PxVec2(x * a.x, y * a.y); + } + + /** + \brief element-wise minimum + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 minimum(const PxVec2& v) const + { + return PxVec2(PxMin(x, v.x), PxMin(y, v.y)); + } + + /** + \brief returns MIN(x, y); + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float minElement() const + { + return PxMin(x, y); + } + + /** + \brief element-wise maximum + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 maximum(const PxVec2& v) const + { + return PxVec2(PxMax(x, v.x), PxMax(y, v.y)); + } + + /** + \brief returns MAX(x, y); + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float maxElement() const + { + return PxMax(x, y); + } + + float x, y; +}; + +PX_CUDA_CALLABLE static PX_FORCE_INLINE PxVec2 operator*(float f, const PxVec2& v) +{ + return PxVec2(f * v.x, f * v.y); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXVEC2_H diff --git a/src/PhysX/pxshared/include/foundation/PxVec3.h b/src/PhysX/pxshared/include/foundation/PxVec3.h new file mode 100644 index 000000000..9ba01a091 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxVec3.h @@ -0,0 +1,394 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXVEC3_H +#define PXFOUNDATION_PXVEC3_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxMath.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief 3 Element vector class. + +This is a 3-dimensional vector class with public data members. +*/ +class PxVec3 +{ + public: + /** + \brief default constructor leaves data uninitialized. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3() + { + } + + /** + \brief zero constructor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3(PxZERO r) : x(0.0f), y(0.0f), z(0.0f) + { + PX_UNUSED(r); + } + + /** + \brief Assigns scalar parameter to all elements. + + Useful to initialize to zero or one. + + \param[in] a Value to assign to elements. + */ + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3(float a) : x(a), y(a), z(a) + { + } + + /** + \brief Initializes from 3 scalar parameters. + + \param[in] nx Value to initialize X component. + \param[in] ny Value to initialize Y component. + \param[in] nz Value to initialize Z component. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3(float nx, float ny, float nz) : x(nx), y(ny), z(nz) + { + } + + /** + \brief Copy ctor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3(const PxVec3& v) : x(v.x), y(v.y), z(v.z) + { + } + + // Operators + + /** + \brief Assignment operator + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator=(const PxVec3& p) + { + x = p.x; + y = p.y; + z = p.z; + return *this; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float& operator[](unsigned int index) + { + PX_ASSERT(index <= 2); + + return reinterpret_cast(this)[index]; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const float& operator[](unsigned int index) const + { + PX_ASSERT(index <= 2); + + return reinterpret_cast(this)[index]; + } + + /** + \brief returns true if the two vectors are exactly equal. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator==(const PxVec3& v) const + { + return x == v.x && y == v.y && z == v.z; + } + + /** + \brief returns true if the two vectors are not exactly equal. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator!=(const PxVec3& v) const + { + return x != v.x || y != v.y || z != v.z; + } + + /** + \brief tests for exact zero vector + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isZero() const + { + return x == 0.0f && y == 0.0f && z == 0.0f; + } + + /** + \brief returns true if all 3 elems of the vector are finite (not NAN or INF, etc.) + */ + PX_CUDA_CALLABLE PX_INLINE bool isFinite() const + { + return PxIsFinite(x) && PxIsFinite(y) && PxIsFinite(z); + } + + /** + \brief is normalized - used by API parameter validation + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isNormalized() const + { + const float unitTolerance = 1e-4f; + return isFinite() && PxAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns the squared magnitude + + Avoids calling PxSqrt()! + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float magnitudeSquared() const + { + return x * x + y * y + z * z; + } + + /** + \brief returns the magnitude + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float magnitude() const + { + return PxSqrt(magnitudeSquared()); + } + + /** + \brief negation + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 operator-() const + { + return PxVec3(-x, -y, -z); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 operator+(const PxVec3& v) const + { + return PxVec3(x + v.x, y + v.y, z + v.z); + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 operator-(const PxVec3& v) const + { + return PxVec3(x - v.x, y - v.y, z - v.z); + } + + /** + \brief scalar post-multiplication + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 operator*(float f) const + { + return PxVec3(x * f, y * f, z * f); + } + + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 operator/(float f) const + { + f = 1.0f / f; + return PxVec3(x * f, y * f, z * f); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator+=(const PxVec3& v) + { + x += v.x; + y += v.y; + z += v.z; + return *this; + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator-=(const PxVec3& v) + { + x -= v.x; + y -= v.y; + z -= v.z; + return *this; + } + + /** + \brief scalar multiplication + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator*=(float f) + { + x *= f; + y *= f; + z *= f; + return *this; + } + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator/=(float f) + { + f = 1.0f / f; + x *= f; + y *= f; + z *= f; + return *this; + } + + /** + \brief returns the scalar product of this and other. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float dot(const PxVec3& v) const + { + return x * v.x + y * v.y + z * v.z; + } + + /** + \brief cross product + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 cross(const PxVec3& v) const + { + return PxVec3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x); + } + + /** return a unit vector */ + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getNormalized() const + { + const float m = magnitudeSquared(); + return m > 0.0f ? *this * PxRecipSqrt(m) : PxVec3(0, 0, 0); + } + + /** + \brief normalizes the vector in place + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float normalize() + { + const float m = magnitude(); + if(m > 0.0f) + *this /= m; + return m; + } + + /** + \brief normalizes the vector in place. Does nothing if vector magnitude is under PX_NORMALIZATION_EPSILON. + Returns vector magnitude if >= PX_NORMALIZATION_EPSILON and 0.0f otherwise. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float normalizeSafe() + { + const float mag = magnitude(); + if(mag < PX_NORMALIZATION_EPSILON) + return 0.0f; + *this *= 1.0f / mag; + return mag; + } + + /** + \brief normalizes the vector in place. Asserts if vector magnitude is under PX_NORMALIZATION_EPSILON. + returns vector magnitude. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float normalizeFast() + { + const float mag = magnitude(); + PX_ASSERT(mag >= PX_NORMALIZATION_EPSILON); + *this *= 1.0f / mag; + return mag; + } + + /** + \brief a[i] * b[i], for all i. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 multiply(const PxVec3& a) const + { + return PxVec3(x * a.x, y * a.y, z * a.z); + } + + /** + \brief element-wise minimum + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 minimum(const PxVec3& v) const + { + return PxVec3(PxMin(x, v.x), PxMin(y, v.y), PxMin(z, v.z)); + } + + /** + \brief returns MIN(x, y, z); + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float minElement() const + { + return PxMin(x, PxMin(y, z)); + } + + /** + \brief element-wise maximum + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 maximum(const PxVec3& v) const + { + return PxVec3(PxMax(x, v.x), PxMax(y, v.y), PxMax(z, v.z)); + } + + /** + \brief returns MAX(x, y, z); + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float maxElement() const + { + return PxMax(x, PxMax(y, z)); + } + + /** + \brief returns absolute values of components; + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 abs() const + { + return PxVec3(PxAbs(x), PxAbs(y), PxAbs(z)); + } + + float x, y, z; +}; + +PX_CUDA_CALLABLE static PX_FORCE_INLINE PxVec3 operator*(float f, const PxVec3& v) +{ + return PxVec3(f * v.x, f * v.y, f * v.z); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXVEC3_H diff --git a/src/PhysX/pxshared/include/foundation/PxVec4.h b/src/PhysX/pxshared/include/foundation/PxVec4.h new file mode 100644 index 000000000..dd7ebdfa6 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxVec4.h @@ -0,0 +1,376 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXVEC4_H +#define PXFOUNDATION_PXVEC4_H +/** \addtogroup foundation +@{ +*/ +#include "foundation/PxMath.h" +#include "foundation/PxVec3.h" +#include "foundation/PxAssert.h" + +/** +\brief 4 Element vector class. + +This is a 4-dimensional vector class with public data members. +*/ +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxVec4 +{ + public: + /** + \brief default constructor leaves data uninitialized. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4() + { + } + + /** + \brief zero constructor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec4(PxZERO r) : x(0.0f), y(0.0f), z(0.0f), w(0.0f) + { + PX_UNUSED(r); + } + + /** + \brief Assigns scalar parameter to all elements. + + Useful to initialize to zero or one. + + \param[in] a Value to assign to elements. + */ + explicit PX_CUDA_CALLABLE PX_INLINE PxVec4(float a) : x(a), y(a), z(a), w(a) + { + } + + /** + \brief Initializes from 3 scalar parameters. + + \param[in] nx Value to initialize X component. + \param[in] ny Value to initialize Y component. + \param[in] nz Value to initialize Z component. + \param[in] nw Value to initialize W component. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4(float nx, float ny, float nz, float nw) : x(nx), y(ny), z(nz), w(nw) + { + } + + /** + \brief Initializes from 3 scalar parameters. + + \param[in] v Value to initialize the X, Y, and Z components. + \param[in] nw Value to initialize W component. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4(const PxVec3& v, float nw) : x(v.x), y(v.y), z(v.z), w(nw) + { + } + + /** + \brief Initializes from an array of scalar parameters. + + \param[in] v Value to initialize with. + */ + explicit PX_CUDA_CALLABLE PX_INLINE PxVec4(const float v[]) : x(v[0]), y(v[1]), z(v[2]), w(v[3]) + { + } + + /** + \brief Copy ctor. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4(const PxVec4& v) : x(v.x), y(v.y), z(v.z), w(v.w) + { + } + + // Operators + + /** + \brief Assignment operator + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4& operator=(const PxVec4& p) + { + x = p.x; + y = p.y; + z = p.z; + w = p.w; + return *this; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_INLINE float& operator[](unsigned int index) + { + PX_ASSERT(index <= 3); + + return reinterpret_cast(this)[index]; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_INLINE const float& operator[](unsigned int index) const + { + PX_ASSERT(index <= 3); + + return reinterpret_cast(this)[index]; + } + + /** + \brief returns true if the two vectors are exactly equal. + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxVec4& v) const + { + return x == v.x && y == v.y && z == v.z && w == v.w; + } + + /** + \brief returns true if the two vectors are not exactly equal. + */ + PX_CUDA_CALLABLE PX_INLINE bool operator!=(const PxVec4& v) const + { + return x != v.x || y != v.y || z != v.z || w != v.w; + } + + /** + \brief tests for exact zero vector + */ + PX_CUDA_CALLABLE PX_INLINE bool isZero() const + { + return x == 0 && y == 0 && z == 0 && w == 0; + } + + /** + \brief returns true if all 3 elems of the vector are finite (not NAN or INF, etc.) + */ + PX_CUDA_CALLABLE PX_INLINE bool isFinite() const + { + return PxIsFinite(x) && PxIsFinite(y) && PxIsFinite(z) && PxIsFinite(w); + } + + /** + \brief is normalized - used by API parameter validation + */ + PX_CUDA_CALLABLE PX_INLINE bool isNormalized() const + { + const float unitTolerance = 1e-4f; + return isFinite() && PxAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns the squared magnitude + + Avoids calling PxSqrt()! + */ + PX_CUDA_CALLABLE PX_INLINE float magnitudeSquared() const + { + return x * x + y * y + z * z + w * w; + } + + /** + \brief returns the magnitude + */ + PX_CUDA_CALLABLE PX_INLINE float magnitude() const + { + return PxSqrt(magnitudeSquared()); + } + + /** + \brief negation + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 operator-() const + { + return PxVec4(-x, -y, -z, -w); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 operator+(const PxVec4& v) const + { + return PxVec4(x + v.x, y + v.y, z + v.z, w + v.w); + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 operator-(const PxVec4& v) const + { + return PxVec4(x - v.x, y - v.y, z - v.z, w - v.w); + } + + /** + \brief scalar post-multiplication + */ + + PX_CUDA_CALLABLE PX_INLINE PxVec4 operator*(float f) const + { + return PxVec4(x * f, y * f, z * f, w * f); + } + + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 operator/(float f) const + { + f = 1.0f / f; + return PxVec4(x * f, y * f, z * f, w * f); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4& operator+=(const PxVec4& v) + { + x += v.x; + y += v.y; + z += v.z; + w += v.w; + return *this; + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4& operator-=(const PxVec4& v) + { + x -= v.x; + y -= v.y; + z -= v.z; + w -= v.w; + return *this; + } + + /** + \brief scalar multiplication + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4& operator*=(float f) + { + x *= f; + y *= f; + z *= f; + w *= f; + return *this; + } + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4& operator/=(float f) + { + f = 1.0f / f; + x *= f; + y *= f; + z *= f; + w *= f; + return *this; + } + + /** + \brief returns the scalar product of this and other. + */ + PX_CUDA_CALLABLE PX_INLINE float dot(const PxVec4& v) const + { + return x * v.x + y * v.y + z * v.z + w * v.w; + } + + /** return a unit vector */ + + PX_CUDA_CALLABLE PX_INLINE PxVec4 getNormalized() const + { + float m = magnitudeSquared(); + return m > 0.0f ? *this * PxRecipSqrt(m) : PxVec4(0, 0, 0, 0); + } + + /** + \brief normalizes the vector in place + */ + PX_CUDA_CALLABLE PX_INLINE float normalize() + { + float m = magnitude(); + if(m > 0.0f) + *this /= m; + return m; + } + + /** + \brief a[i] * b[i], for all i. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 multiply(const PxVec4& a) const + { + return PxVec4(x * a.x, y * a.y, z * a.z, w * a.w); + } + + /** + \brief element-wise minimum + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 minimum(const PxVec4& v) const + { + return PxVec4(PxMin(x, v.x), PxMin(y, v.y), PxMin(z, v.z), PxMin(w, v.w)); + } + + /** + \brief element-wise maximum + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 maximum(const PxVec4& v) const + { + return PxVec4(PxMax(x, v.x), PxMax(y, v.y), PxMax(z, v.z), PxMax(w, v.w)); + } + + PX_CUDA_CALLABLE PX_INLINE PxVec3 getXYZ() const + { + return PxVec3(x, y, z); + } + + /** + \brief set vector elements to zero + */ + PX_CUDA_CALLABLE PX_INLINE void setZero() + { + x = y = z = w = 0.0f; + } + + float x, y, z, w; +}; + +PX_CUDA_CALLABLE static PX_INLINE PxVec4 operator*(float f, const PxVec4& v) +{ + return PxVec4(f * v.x, f * v.y, f * v.z, f * v.w); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXVEC4_H diff --git a/src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h b/src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h new file mode 100644 index 000000000..64c1367b7 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h @@ -0,0 +1,185 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXUNIXINTRINSICS_H +#define PXFOUNDATION_PXUNIXINTRINSICS_H + +#include "foundation/Px.h" +#include "foundation/PxAssert.h" + +#if !(PX_LINUX || PX_ANDROID || PX_PS4 || PX_APPLE_FAMILY) +#error "This file should only be included by Unix builds!!" +#endif + +#if (PX_LINUX || PX_ANDROID) && !defined(__CUDACC__) && !PX_EMSCRIPTEN + // Linux/android and CUDA compilation does not work with std::isfnite, as it is not marked as CUDA callable + #include + #ifndef isfinite + using std::isfinite; + #endif +#endif + +#include +#include + +namespace physx +{ +namespace intrinsics +{ +//! \brief platform-specific absolute value +PX_CUDA_CALLABLE PX_FORCE_INLINE float abs(float a) +{ + return ::fabsf(a); +} + +//! \brief platform-specific select float +PX_CUDA_CALLABLE PX_FORCE_INLINE float fsel(float a, float b, float c) +{ + return (a >= 0.0f) ? b : c; +} + +//! \brief platform-specific sign +PX_CUDA_CALLABLE PX_FORCE_INLINE float sign(float a) +{ + return (a >= 0.0f) ? 1.0f : -1.0f; +} + +//! \brief platform-specific reciprocal +PX_CUDA_CALLABLE PX_FORCE_INLINE float recip(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific reciprocal estimate +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipFast(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float sqrt(float a) +{ + return ::sqrtf(a); +} + +//! \brief platform-specific reciprocal square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrt(float a) +{ + return 1.0f / ::sqrtf(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrtFast(float a) +{ + return 1.0f / ::sqrtf(a); +} + +//! \brief platform-specific sine +PX_CUDA_CALLABLE PX_FORCE_INLINE float sin(float a) +{ + return ::sinf(a); +} + +//! \brief platform-specific cosine +PX_CUDA_CALLABLE PX_FORCE_INLINE float cos(float a) +{ + return ::cosf(a); +} + +//! \brief platform-specific minimum +PX_CUDA_CALLABLE PX_FORCE_INLINE float selectMin(float a, float b) +{ + return a < b ? a : b; +} + +//! \brief platform-specific maximum +PX_CUDA_CALLABLE PX_FORCE_INLINE float selectMax(float a, float b) +{ + return a > b ? a : b; +} + +//! \brief platform-specific finiteness check (not INF or NAN) +PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite(float a) +{ + //std::isfinite not recommended as of Feb 2017, since it doesn't work with g++/clang's floating point optimization. + union localU { PxU32 i; float f; } floatUnion; + floatUnion.f = a; + return !((floatUnion.i & 0x7fffffff) >= 0x7f800000); +} + +//! \brief platform-specific finiteness check (not INF or NAN) +PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite(double a) +{ + return !!isfinite(a); +} + +/*! +Sets \c count bytes starting at \c dst to zero. +*/ +PX_FORCE_INLINE void* memZero(void* dest, uint32_t count) +{ + return memset(dest, 0, count); +} + +/*! +Sets \c count bytes starting at \c dst to \c c. +*/ +PX_FORCE_INLINE void* memSet(void* dest, int32_t c, uint32_t count) +{ + return memset(dest, c, count); +} + +/*! +Copies \c count bytes from \c src to \c dst. User memMove if regions overlap. +*/ +PX_FORCE_INLINE void* memCopy(void* dest, const void* src, uint32_t count) +{ + return memcpy(dest, src, count); +} + +/*! +Copies \c count bytes from \c src to \c dst. Supports overlapping regions. +*/ +PX_FORCE_INLINE void* memMove(void* dest, const void* src, uint32_t count) +{ + return memmove(dest, src, count); +} + +/*! +Set 128B to zero starting at \c dst+offset. Must be aligned. +*/ +PX_FORCE_INLINE void memZero128(void* dest, uint32_t offset = 0) +{ + PX_ASSERT(((size_t(dest) + offset) & 0x7f) == 0); + memSet(reinterpret_cast(dest) + offset, 0, 128); +} + +} // namespace intrinsics +} // namespace physx + +#endif // #ifndef PXFOUNDATION_PXUNIXINTRINSICS_H diff --git a/src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h b/src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h new file mode 100644 index 000000000..3705b26e9 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h @@ -0,0 +1,188 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXWINDOWSINTRINSICS_H +#define PXFOUNDATION_PXWINDOWSINTRINSICS_H + +#include "foundation/Px.h" +#include "foundation/PxAssert.h" + +#if !PX_WINDOWS_FAMILY +#error "This file should only be included by Windows builds!!" +#endif + +#include +#include + +#if !PX_DOXYGEN +namespace physx +{ +namespace intrinsics +{ +#endif + +//! \brief platform-specific absolute value +PX_CUDA_CALLABLE PX_FORCE_INLINE float abs(float a) +{ + return ::fabsf(a); +} + +//! \brief platform-specific select float +PX_CUDA_CALLABLE PX_FORCE_INLINE float fsel(float a, float b, float c) +{ + return (a >= 0.0f) ? b : c; +} + +//! \brief platform-specific sign +PX_CUDA_CALLABLE PX_FORCE_INLINE float sign(float a) +{ + return (a >= 0.0f) ? 1.0f : -1.0f; +} + +//! \brief platform-specific reciprocal +PX_CUDA_CALLABLE PX_FORCE_INLINE float recip(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific reciprocal estimate +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipFast(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float sqrt(float a) +{ + return ::sqrtf(a); +} + +//! \brief platform-specific reciprocal square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrt(float a) +{ + return 1.0f / ::sqrtf(a); +} + +//! \brief platform-specific reciprocal square root estimate +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrtFast(float a) +{ + return 1.0f / ::sqrtf(a); +} + +//! \brief platform-specific sine +PX_CUDA_CALLABLE PX_FORCE_INLINE float sin(float a) +{ + return ::sinf(a); +} + +//! \brief platform-specific cosine +PX_CUDA_CALLABLE PX_FORCE_INLINE float cos(float a) +{ + return ::cosf(a); +} + +//! \brief platform-specific minimum +PX_CUDA_CALLABLE PX_FORCE_INLINE float selectMin(float a, float b) +{ + return a < b ? a : b; +} + +//! \brief platform-specific maximum +PX_CUDA_CALLABLE PX_FORCE_INLINE float selectMax(float a, float b) +{ + return a > b ? a : b; +} + +//! \brief platform-specific finiteness check (not INF or NAN) +PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite(float a) +{ +#ifdef __CUDACC__ + return !!isfinite(a); +#else + return (0 == ((_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF) & _fpclass(a))); +#endif +} + +//! \brief platform-specific finiteness check (not INF or NAN) +PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite(double a) +{ +#ifdef __CUDACC__ + return !!isfinite(a); +#else + return (0 == ((_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF) & _fpclass(a))); +#endif +} + +/*! +Sets \c count bytes starting at \c dst to zero. +*/ +PX_FORCE_INLINE void* memZero(void* dest, uint32_t count) +{ + return memset(dest, 0, count); +} + +/*! +Sets \c count bytes starting at \c dst to \c c. +*/ +PX_FORCE_INLINE void* memSet(void* dest, int32_t c, uint32_t count) +{ + return memset(dest, c, count); +} + +/*! +Copies \c count bytes from \c src to \c dst. User memMove if regions overlap. +*/ +PX_FORCE_INLINE void* memCopy(void* dest, const void* src, uint32_t count) +{ + return memcpy(dest, src, count); +} + +/*! +Copies \c count bytes from \c src to \c dst. Supports overlapping regions. +*/ +PX_FORCE_INLINE void* memMove(void* dest, const void* src, uint32_t count) +{ + return memmove(dest, src, count); +} + +/*! +Set 128B to zero starting at \c dst+offset. Must be aligned. +*/ +PX_FORCE_INLINE void memZero128(void* dest, uint32_t offset = 0) +{ + PX_ASSERT(((size_t(dest) + offset) & 0x7f) == 0); + memSet(reinterpret_cast(dest) + offset, 0, 128); +} + +#if !PX_DOXYGEN +} // namespace intrinsics +} // namespace physx +#endif + +#endif // #ifndef PXFOUNDATION_PXWINDOWSINTRINSICS_H diff --git a/src/PhysXCharacterAll.cpp b/src/PhysXCharacterAll.cpp new file mode 100644 index 000000000..b4bd479f5 --- /dev/null +++ b/src/PhysXCharacterAll.cpp @@ -0,0 +1,10 @@ +#include "PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp" diff --git a/src/PhysXCommonAll.cpp b/src/PhysXCommonAll.cpp new file mode 100644 index 000000000..90aad96c8 --- /dev/null +++ b/src/PhysXCommonAll.cpp @@ -0,0 +1,8 @@ +#include "PhysX/physx/source/common/src/CmBoxPruning.cpp" +#include "PhysX/physx/source/common/src/CmCollection.cpp" +#include "PhysX/physx/source/common/src/CmMathUtils.cpp" +#include "PhysX/physx/source/common/src/CmPtrTable.cpp" +#include "PhysX/physx/source/common/src/CmRadixSort.cpp" +#include "PhysX/physx/source/common/src/CmRadixSortBuffered.cpp" +#include "PhysX/physx/source/common/src/CmRenderOutput.cpp" +#include "PhysX/physx/source/common/src/CmVisualization.cpp" diff --git a/src/PhysXCookingAll.cpp b/src/PhysXCookingAll.cpp new file mode 100644 index 000000000..67004a9b9 --- /dev/null +++ b/src/PhysXCookingAll.cpp @@ -0,0 +1,18 @@ +#include "PhysX/physx/source/physxcooking/src/Adjacencies.cpp" +#include "PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp" +#include "PhysX/physx/source/physxcooking/src/Cooking.cpp" +#include "PhysX/physx/source/physxcooking/src/CookingUtils.cpp" +#include "PhysX/physx/source/physxcooking/src/EdgeList.cpp" +#include "PhysX/physx/source/physxcooking/src/MeshCleaner.cpp" +#include "PhysX/physx/source/physxcooking/src/Quantizer.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp" +#include "PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp" +#include "PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp" +#include "PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp" +#include "PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp" + diff --git a/src/PhysXExtensionAll.cpp b/src/PhysXExtensionAll.cpp new file mode 100644 index 000000000..5a9d566e1 --- /dev/null +++ b/src/PhysXExtensionAll.cpp @@ -0,0 +1,34 @@ +#include "PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtCollection.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp" + +#include "PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtExtensions.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtMetaData.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtPvd.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtSphericalJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp" diff --git a/src/PhysXFoundationAll.cpp b/src/PhysXFoundationAll.cpp new file mode 100644 index 000000000..3dd9cc720 --- /dev/null +++ b/src/PhysXFoundationAll.cpp @@ -0,0 +1,7 @@ +#include "PhysX/physx/source/foundation/src/PsAllocator.cpp" +#include "PhysX/physx/source/foundation/src/PsAssert.cpp" +#include "PhysX/physx/source/foundation/src/PsFoundation.cpp" +#include "PhysX/physx/source/foundation/src/PsMathUtils.cpp" +#include "PhysX/physx/source/foundation/src/PsString.cpp" +#include "PhysX/physx/source/foundation/src/PsTempAllocator.cpp" +#include "PhysX/physx/source/foundation/src/PsUtilities.cpp" diff --git a/src/PhysXFoundationUnix.cpp b/src/PhysXFoundationUnix.cpp new file mode 100644 index 000000000..bb70a242a --- /dev/null +++ b/src/PhysXFoundationUnix.cpp @@ -0,0 +1,10 @@ +#include "PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp" diff --git a/src/PhysXFoundationWindows.cpp b/src/PhysXFoundationWindows.cpp new file mode 100644 index 000000000..7e600f8f4 --- /dev/null +++ b/src/PhysXFoundationWindows.cpp @@ -0,0 +1,11 @@ +#include "PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp" + diff --git a/src/PhysXGeomUtilsAll.cpp b/src/PhysXGeomUtilsAll.cpp new file mode 100644 index 000000000..264065052 --- /dev/null +++ b/src/PhysXGeomUtilsAll.cpp @@ -0,0 +1,68 @@ +#include "PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp" +#include "PhysX/physx/source/geomutils/src/GuBounds.cpp" +#include "PhysX/physx/source/geomutils/src/GuBox.cpp" +#include "PhysX/physx/source/geomutils/src/GuBVHStructure.cpp" +#include "PhysX/physx/source/geomutils/src/GuCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp" +#include "PhysX/physx/source/geomutils/src/GuGeometryQuery.cpp" +#include "PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp" +#include "PhysX/physx/source/geomutils/src/GuInternal.cpp" +#include "PhysX/physx/source/geomutils/src/GuMeshFactory.cpp" +#include "PhysX/physx/source/geomutils/src/GuMetaData.cpp" +#include "PhysX/physx/source/geomutils/src/GuMTD.cpp" +#include "PhysX/physx/source/geomutils/src/GuOverlapTests.cpp" +#include "PhysX/physx/source/geomutils/src/GuRaycastTests.cpp" +#include "PhysX/physx/source/geomutils/src/GuSerialize.cpp" +#include "PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.cpp" +#include "PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp" +#include "PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp" +#include "PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp" +#include "PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp" +#include "PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp" +#include "PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp" +#include "PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp" +#include "PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp" +#include "PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp" +#include "PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp" +#include "PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp" +#include "PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp" +#include "PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp" + +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp" + diff --git a/src/PhysXLowLevelAll.cpp b/src/PhysXLowLevelAll.cpp new file mode 100644 index 000000000..8890717de --- /dev/null +++ b/src/PhysXLowLevelAll.cpp @@ -0,0 +1,48 @@ +#include "PhysX/physx/source/lowlevel/api/src/px_globals.cpp" +#include "PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactMethodImpl.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialHeightField.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMesh.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsCCD.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsContext.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyConstraintSetupBlock.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverControl.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp" \ No newline at end of file diff --git a/src/PhysXNpSrcAll.cpp b/src/PhysXNpSrcAll.cpp new file mode 100644 index 000000000..c73817ce4 --- /dev/null +++ b/src/PhysXNpSrcAll.cpp @@ -0,0 +1,34 @@ +#include "PhysX/physx/source/physx/src/NpActor.cpp" +#include "PhysX/physx/source/physx/src/NpAggregate.cpp" +#include "PhysX/physx/source/physx/src/NpArticulation.cpp" +#include "PhysX/physx/source/physx/src/NpArticulationJoint.cpp" +#include "PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp" +#include "PhysX/physx/source/physx/src/NpArticulationLink.cpp" +#include "PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp" +#include "PhysX/physx/source/physx/src/NpBatchQuery.cpp" +#include "PhysX/physx/source/physx/src/NpConstraint.cpp" +#include "PhysX/physx/source/physx/src/NpFactory.cpp" +#include "PhysX/physx/source/physx/src/NpMaterial.cpp" +#include "PhysX/physx/source/physx/src/NpMetaData.cpp" +#include "PhysX/physx/source/physx/src/NpPhysics.cpp" +#include "PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp" +#include "PhysX/physx/source/physx/src/NpReadCheck.cpp" +#include "PhysX/physx/source/physx/src/NpRigidDynamic.cpp" +#include "PhysX/physx/source/physx/src/NpRigidStatic.cpp" +#include "PhysX/physx/source/physx/src/NpScene.cpp" +#include "PhysX/physx/source/physx/src/NpSceneQueries.cpp" +#include "PhysX/physx/source/physx/src/NpSerializerAdapter.cpp" +#include "PhysX/physx/source/physx/src/NpShape.cpp" +#include "PhysX/physx/source/physx/src/NpShapeManager.cpp" +#include "PhysX/physx/source/physx/src/NpWriteCheck.cpp" +#include "PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp" +#include "PhysX/physx/source/physx/src/PvdPhysicsClient.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbActor.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbBase.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbScene.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbShape.cpp" +#include "PhysX/physx/source/physx/src/gpu/PxGpu.cpp" +#include "PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp" \ No newline at end of file diff --git a/src/PhysXPvdAll.cpp b/src/PhysXPvdAll.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/PhysXSceneQueryAll.cpp b/src/PhysXSceneQueryAll.cpp new file mode 100644 index 000000000..49e4e580e --- /dev/null +++ b/src/PhysXSceneQueryAll.cpp @@ -0,0 +1,15 @@ +#include "PhysX/physx/source/scenequery/src/SqAABBPruner.cpp" +#include "PhysX/physx/source/scenequery/src/SqAABBTree.cpp" +#include "PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp" +#include "PhysX/physx/source/scenequery/src/SqBounds.cpp" +#include "PhysX/physx/source/scenequery/src/SqBucketPruner.cpp" +#include "PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp" +#include "PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp" +#include "PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp" +#include "PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp" +#include "PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp" +#include "PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp" +#include "PhysX/physx/source/scenequery/src/SqMetaData.cpp" +#include "PhysX/physx/source/scenequery/src/SqPruningPool.cpp" +#include "PhysX/physx/source/scenequery/src/SqPruningStructure.cpp" +#include "PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp" diff --git a/src/PhysXSimulationControllerAll.cpp b/src/PhysXSimulationControllerAll.cpp new file mode 100644 index 000000000..12c5f67e0 --- /dev/null +++ b/src/PhysXSimulationControllerAll.cpp @@ -0,0 +1,31 @@ +#include "PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScIterators.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScScene.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp" \ No newline at end of file diff --git a/src/PhysXVehicleAll.cpp b/src/PhysXVehicleAll.cpp new file mode 100644 index 000000000..53b89d89b --- /dev/null +++ b/src/PhysXVehicleAll.cpp @@ -0,0 +1,18 @@ +#include "PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleDrive4W.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleUpdate.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp" +#include "PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp" +#include "PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp" +#include "PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp" +#include "PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp" +#include "PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp" From d533f19d54ea2dd4fceca2d8e8265132a0a6cada Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Wed, 13 Feb 2019 15:11:34 -0800 Subject: [PATCH 02/10] add missing header --- examples/Importers/ImportSTLDemo/LoadMeshFromSTL.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Importers/ImportSTLDemo/LoadMeshFromSTL.h b/examples/Importers/ImportSTLDemo/LoadMeshFromSTL.h index 53331e147..6e8dbed9e 100644 --- a/examples/Importers/ImportSTLDemo/LoadMeshFromSTL.h +++ b/examples/Importers/ImportSTLDemo/LoadMeshFromSTL.h @@ -5,7 +5,7 @@ #include //fopen #include "Bullet3Common/b3AlignedObjectArray.h" #include "../../CommonInterfaces/CommonFileIOInterface.h" - +#include //memcpy struct MySTLTriangle { float normal[3]; From 43aafbe892bce000cc69f24bc09410db97a990fa Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Wed, 13 Feb 2019 14:57:11 -0800 Subject: [PATCH 03/10] branch with experimental PyBullet support for PhysX see otherPhysicsEngine in examples/pybullet/examples folder for example usage --- setup.py | 1160 +-- src/PhysX/physx/include/PxActor.h | 330 + src/PhysX/physx/include/PxAggregate.h | 213 + src/PhysX/physx/include/PxArticulation.h | 281 + src/PhysX/physx/include/PxArticulationBase.h | 337 + src/PhysX/physx/include/PxArticulationJoint.h | 572 ++ .../PxArticulationJointReducedCoordinate.h | 95 + src/PhysX/physx/include/PxArticulationLink.h | 142 + .../include/PxArticulationReducedCoordinate.h | 409 + src/PhysX/physx/include/PxBatchQuery.h | 224 + src/PhysX/physx/include/PxBatchQueryDesc.h | 303 + src/PhysX/physx/include/PxBroadPhase.h | 174 + src/PhysX/physx/include/PxClient.h | 66 + src/PhysX/physx/include/PxConstraint.h | 280 + src/PhysX/physx/include/PxConstraintDesc.h | 443 + src/PhysX/physx/include/PxContact.h | 583 ++ .../physx/include/PxContactModifyCallback.h | 486 ++ src/PhysX/physx/include/PxDeletionListener.h | 106 + src/PhysX/physx/include/PxFiltering.h | 733 ++ src/PhysX/physx/include/PxForceMode.h | 66 + src/PhysX/physx/include/PxFoundation.h | 158 + src/PhysX/physx/include/PxImmediateMode.h | 228 + src/PhysX/physx/include/PxLockedData.h | 93 + src/PhysX/physx/include/PxMaterial.h | 324 + src/PhysX/physx/include/PxPhysXConfig.h | 78 + src/PhysX/physx/include/PxPhysics.h | 729 ++ src/PhysX/physx/include/PxPhysicsAPI.h | 218 + .../physx/include/PxPhysicsSerialization.h | 74 + src/PhysX/physx/include/PxPhysicsVersion.h | 70 + src/PhysX/physx/include/PxPruningStructure.h | 109 + src/PhysX/physx/include/PxQueryFiltering.h | 276 + src/PhysX/physx/include/PxQueryReport.h | 390 + src/PhysX/physx/include/PxRigidActor.h | 237 + src/PhysX/physx/include/PxRigidBody.h | 701 ++ src/PhysX/physx/include/PxRigidDynamic.h | 388 + src/PhysX/physx/include/PxRigidStatic.h | 77 + src/PhysX/physx/include/PxScene.h | 1649 ++++ src/PhysX/physx/include/PxSceneDesc.h | 1059 +++ src/PhysX/physx/include/PxSceneLock.h | 129 + src/PhysX/physx/include/PxShape.h | 639 ++ .../physx/include/PxSimulationEventCallback.h | 915 ++ .../physx/include/PxSimulationStatistics.h | 330 + .../physx/include/PxVisualizationParameter.h | 254 + .../characterkinematic/PxBoxController.h | 230 + .../characterkinematic/PxCapsuleController.h | 251 + .../include/characterkinematic/PxCharacter.h | 57 + .../include/characterkinematic/PxController.h | 911 ++ .../characterkinematic/PxControllerBehavior.h | 136 + .../characterkinematic/PxControllerManager.h | 301 + .../PxControllerObstacles.h | 192 + .../include/characterkinematic/PxExtended.h | 275 + .../physx/include/collision/PxCollisionDefs.h | 85 + src/PhysX/physx/include/common/PxBase.h | 201 + src/PhysX/physx/include/common/PxCollection.h | 279 + .../physx/include/common/PxCoreUtilityTypes.h | 211 + src/PhysX/physx/include/common/PxMetaData.h | 228 + .../physx/include/common/PxMetaDataFlags.h | 74 + .../include/common/PxPhysXCommonConfig.h | 124 + .../common/PxPhysicsInsertionCallback.h | 84 + .../physx/include/common/PxProfileZone.h | 51 + .../physx/include/common/PxRenderBuffer.h | 157 + .../physx/include/common/PxSerialFramework.h | 406 + src/PhysX/physx/include/common/PxSerializer.h | 257 + .../physx/include/common/PxStringTable.h | 73 + .../physx/include/common/PxTolerancesScale.h | 108 + src/PhysX/physx/include/common/PxTypeInfo.h | 124 + .../common/windows/PxWindowsDelayLoadHook.h | 102 + .../include/cooking/PxBVH33MidphaseDesc.h | 110 + .../include/cooking/PxBVH34MidphaseDesc.h | 90 + .../include/cooking/PxBVHStructureDesc.h | 109 + .../physx/include/cooking/PxConvexMeshDesc.h | 307 + src/PhysX/physx/include/cooking/PxCooking.h | 563 ++ .../physx/include/cooking/PxMidphaseDesc.h | 119 + .../include/cooking/PxTriangleMeshDesc.h | 120 + src/PhysX/physx/include/cooking/Pxc.h | 57 + .../cudamanager/PxCudaContextManager.h | 425 + .../include/cudamanager/PxCudaMemoryManager.h | 281 + .../physx/include/cudamanager/PxGpuCopyDesc.h | 86 + .../include/cudamanager/PxGpuCopyDescQueue.h | 149 + .../include/extensions/PxBinaryConverter.h | 132 + .../include/extensions/PxBroadPhaseExt.h | 75 + .../include/extensions/PxCollectionExt.h | 119 + .../include/extensions/PxConstraintExt.h | 70 + .../physx/include/extensions/PxContactJoint.h | 168 + .../include/extensions/PxConvexMeshExt.h | 73 + .../physx/include/extensions/PxD6Joint.h | 561 ++ .../include/extensions/PxD6JointCreate.h | 255 + .../include/extensions/PxDefaultAllocator.h | 110 + .../extensions/PxDefaultCpuDispatcher.h | 98 + .../extensions/PxDefaultErrorCallback.h | 62 + .../PxDefaultSimulationFilterShader.h | 263 + .../include/extensions/PxDefaultStreams.h | 149 + .../include/extensions/PxDistanceJoint.h | 269 + .../include/extensions/PxExtensionsAPI.h | 88 + .../physx/include/extensions/PxFixedJoint.h | 159 + src/PhysX/physx/include/extensions/PxJoint.h | 419 + .../physx/include/extensions/PxJointLimit.h | 566 ++ .../include/extensions/PxMassProperties.h | 334 + .../include/extensions/PxPrismaticJoint.h | 236 + .../physx/include/extensions/PxRaycastCCD.h | 100 + .../include/extensions/PxRepXSerializer.h | 148 + .../include/extensions/PxRepXSimpleType.h | 106 + .../include/extensions/PxRevoluteJoint.h | 332 + .../include/extensions/PxRigidActorExt.h | 155 + .../physx/include/extensions/PxRigidBodyExt.h | 460 + .../include/extensions/PxSceneQueryExt.h | 309 + .../include/extensions/PxSerialization.h | 284 + .../physx/include/extensions/PxShapeExt.h | 158 + .../include/extensions/PxSimpleFactory.h | 337 + .../include/extensions/PxSmoothNormals.h | 61 + .../include/extensions/PxSphericalJoint.h | 215 + .../include/extensions/PxStringTableExt.h | 57 + .../include/extensions/PxTriangleMeshExt.h | 188 + src/PhysX/physx/include/filebuf/PxFileBuf.h | 339 + .../physx/include/geometry/PxBVHStructure.h | 138 + .../physx/include/geometry/PxBoxGeometry.h | 109 + .../include/geometry/PxCapsuleGeometry.h | 121 + .../physx/include/geometry/PxConvexMesh.h | 197 + .../include/geometry/PxConvexMeshGeometry.h | 151 + src/PhysX/physx/include/geometry/PxGeometry.h | 94 + .../include/geometry/PxGeometryHelpers.h | 214 + .../physx/include/geometry/PxGeometryQuery.h | 225 + .../physx/include/geometry/PxHeightField.h | 262 + .../include/geometry/PxHeightFieldDesc.h | 187 + .../include/geometry/PxHeightFieldFlag.h | 162 + .../include/geometry/PxHeightFieldGeometry.h | 143 + .../include/geometry/PxHeightFieldSample.h | 124 + .../physx/include/geometry/PxMeshQuery.h | 201 + .../physx/include/geometry/PxMeshScale.h | 175 + .../physx/include/geometry/PxPlaneGeometry.h | 107 + .../include/geometry/PxSimpleTriangleMesh.h | 166 + .../physx/include/geometry/PxSphereGeometry.h | 93 + src/PhysX/physx/include/geometry/PxTriangle.h | 149 + .../physx/include/geometry/PxTriangleMesh.h | 317 + .../include/geometry/PxTriangleMeshGeometry.h | 150 + .../physx/include/geomutils/GuContactBuffer.h | 131 + .../physx/include/geomutils/GuContactPoint.h | 109 + src/PhysX/physx/include/gpu/PxGpu.h | 92 + src/PhysX/physx/include/pvd/PxPvd.h | 178 + .../physx/include/pvd/PxPvdSceneClient.h | 142 + src/PhysX/physx/include/pvd/PxPvdTransport.h | 129 + src/PhysX/physx/include/solver/PxSolverDefs.h | 256 + .../physx/include/task/PxCpuDispatcher.h | 79 + .../physx/include/task/PxGpuDispatcher.h | 248 + src/PhysX/physx/include/task/PxGpuTask.h | 118 + src/PhysX/physx/include/task/PxTask.h | 334 + src/PhysX/physx/include/task/PxTaskDefine.h | 37 + src/PhysX/physx/include/task/PxTaskManager.h | 228 + .../include/vehicle/PxVehicleComponents.h | 1359 +++ .../physx/include/vehicle/PxVehicleDrive.h | 566 ++ .../physx/include/vehicle/PxVehicleDrive4W.h | 279 + .../physx/include/vehicle/PxVehicleDriveNW.h | 237 + .../include/vehicle/PxVehicleDriveTank.h | 281 + .../physx/include/vehicle/PxVehicleNoDrive.h | 217 + .../physx/include/vehicle/PxVehicleSDK.h | 237 + .../physx/include/vehicle/PxVehicleShaders.h | 79 + .../include/vehicle/PxVehicleTireFriction.h | 223 + .../physx/include/vehicle/PxVehicleUpdate.h | 579 ++ .../physx/include/vehicle/PxVehicleUtil.h | 64 + .../include/vehicle/PxVehicleUtilControl.h | 651 ++ .../include/vehicle/PxVehicleUtilSetup.h | 160 + .../include/vehicle/PxVehicleUtilTelemetry.h | 410 + .../physx/include/vehicle/PxVehicleWheels.h | 811 ++ src/PhysX/physx/platform_readme.html | 16 + src/PhysX/physx/release_notes.html | 3680 ++++++++ .../include/windows/CmWindowsLoadLibrary.h | 87 + .../windows/CmWindowsModuleUpdateLoader.h | 70 + src/PhysX/physx/source/common/src/CmBitMap.h | 504 ++ .../physx/source/common/src/CmBlockArray.h | 153 + .../physx/source/common/src/CmBoxPruning.cpp | 197 + .../physx/source/common/src/CmBoxPruning.h | 49 + .../physx/source/common/src/CmCollection.cpp | 217 + .../physx/source/common/src/CmCollection.h | 103 + .../source/common/src/CmConeLimitHelper.h | 207 + .../physx/source/common/src/CmFlushPool.h | 157 + src/PhysX/physx/source/common/src/CmIDPool.h | 207 + src/PhysX/physx/source/common/src/CmIO.h | 136 + .../physx/source/common/src/CmMathUtils.cpp | 70 + .../physx/source/common/src/CmMatrix34.h | 286 + .../physx/source/common/src/CmPhysXCommon.h | 87 + src/PhysX/physx/source/common/src/CmPool.h | 292 + .../source/common/src/CmPreallocatingPool.h | 435 + .../physx/source/common/src/CmPriorityQueue.h | 237 + .../physx/source/common/src/CmPtrTable.cpp | 207 + .../physx/source/common/src/CmPtrTable.h | 142 + src/PhysX/physx/source/common/src/CmQueue.h | 152 + .../physx/source/common/src/CmRadixSort.cpp | 460 + .../physx/source/common/src/CmRadixSort.h | 102 + .../source/common/src/CmRadixSortBuffered.cpp | 149 + .../source/common/src/CmRadixSortBuffered.h | 63 + .../physx/source/common/src/CmRefCountable.h | 102 + .../physx/source/common/src/CmRenderBuffer.h | 132 + .../source/common/src/CmRenderOutput.cpp | 301 + .../physx/source/common/src/CmRenderOutput.h | 182 + src/PhysX/physx/source/common/src/CmScaling.h | 227 + .../physx/source/common/src/CmSpatialVector.h | 513 ++ src/PhysX/physx/source/common/src/CmTask.h | 267 + .../physx/source/common/src/CmTaskPool.h | 143 + src/PhysX/physx/source/common/src/CmTmpMem.h | 94 + .../source/common/src/CmTransformUtils.h | 145 + src/PhysX/physx/source/common/src/CmUtils.h | 311 + .../source/common/src/CmVisualization.cpp | 137 + .../physx/source/common/src/CmVisualization.h | 125 + .../src/windows/CmWindowsDelayLoadHook.cpp | 82 + .../windows/CmWindowsModuleUpdateLoader.cpp | 130 + .../source/compiler/cmake/CMakeLists.txt | 142 + .../physx/source/compiler/cmake/FastXml.cmake | 92 + .../source/compiler/cmake/IfTemplate.txt | 32 + .../source/compiler/cmake/LowLevel.cmake | 235 + .../source/compiler/cmake/LowLevelAABB.cmake | 131 + .../compiler/cmake/LowLevelDynamics.cmake | 202 + .../physx/source/compiler/cmake/PhysX.cmake | 382 + .../cmake/PhysXCharacterKinematic.cmake | 152 + .../source/compiler/cmake/PhysXCommon.cmake | 591 ++ .../source/compiler/cmake/PhysXCooking.cmake | 206 + .../compiler/cmake/PhysXExtensions.cmake | 320 + .../compiler/cmake/PhysXFoundation.cmake | 198 + .../source/compiler/cmake/PhysXPvdSDK.cmake | 186 + .../source/compiler/cmake/PhysXTask.cmake | 103 + .../source/compiler/cmake/PhysXVehicle.cmake | 176 + .../source/compiler/cmake/SceneQuery.cmake | 159 + .../compiler/cmake/SimulationController.cmake | 207 + .../compiler/cmake/android/CMakeLists.txt | 94 + .../compiler/cmake/android/FastXml.cmake | 42 + .../compiler/cmake/android/LowLevel.cmake | 53 + .../compiler/cmake/android/LowLevelAABB.cmake | 52 + .../cmake/android/LowLevelDynamics.cmake | 49 + .../source/compiler/cmake/android/PhysX.cmake | 64 + .../android/PhysXCharacterKinematic.cmake | 44 + .../compiler/cmake/android/PhysXCommon.cmake | 51 + .../compiler/cmake/android/PhysXCooking.cmake | 47 + .../cmake/android/PhysXExtensions.cmake | 54 + .../cmake/android/PhysXFoundation.cmake | 87 + .../compiler/cmake/android/PhysXPvdSDK.cmake | 42 + .../compiler/cmake/android/PhysXTask.cmake | 42 + .../compiler/cmake/android/PhysXVehicle.cmake | 43 + .../compiler/cmake/android/SceneQuery.cmake | 47 + .../cmake/android/SimulationController.cmake | 48 + .../source/compiler/cmake/ios/CMakeLists.txt | 76 + .../source/compiler/cmake/ios/FastXml.cmake | 44 + .../source/compiler/cmake/ios/LowLevel.cmake | 51 + .../compiler/cmake/ios/LowLevelAABB.cmake | 53 + .../compiler/cmake/ios/LowLevelDynamics.cmake | 52 + .../source/compiler/cmake/ios/PhysX.cmake | 68 + .../cmake/ios/PhysXCharacterKinematic.cmake | 43 + .../compiler/cmake/ios/PhysXCommon.cmake | 52 + .../compiler/cmake/ios/PhysXCooking.cmake | 52 + .../compiler/cmake/ios/PhysXExtensions.cmake | 55 + .../compiler/cmake/ios/PhysXFoundation.cmake | 91 + .../compiler/cmake/ios/PhysXPvdSDK.cmake | 45 + .../source/compiler/cmake/ios/PhysXTask.cmake | 41 + .../compiler/cmake/ios/PhysXVehicle.cmake | 42 + .../compiler/cmake/ios/SceneQuery.cmake | 48 + .../cmake/ios/SimulationController.cmake | 49 + .../compiler/cmake/linux/CMakeLists.txt | 123 + .../source/compiler/cmake/linux/FastXml.cmake | 43 + .../compiler/cmake/linux/LowLevel.cmake | 67 + .../compiler/cmake/linux/LowLevelAABB.cmake | 53 + .../cmake/linux/LowLevelDynamics.cmake | 53 + .../source/compiler/cmake/linux/PhysX.cmake | 111 + .../cmake/linux/PhysXCharacterKinematic.cmake | 49 + .../compiler/cmake/linux/PhysXCommon.cmake | 65 + .../compiler/cmake/linux/PhysXCooking.cmake | 64 + .../cmake/linux/PhysXExtensions.cmake | 56 + .../cmake/linux/PhysXFoundation.cmake | 110 + .../compiler/cmake/linux/PhysXPvdSDK.cmake | 44 + .../compiler/cmake/linux/PhysXTask.cmake | 45 + .../compiler/cmake/linux/PhysXVehicle.cmake | 47 + .../compiler/cmake/linux/SceneQuery.cmake | 50 + .../cmake/linux/SimulationController.cmake | 51 + .../source/compiler/cmake/mac/CMakeLists.txt | 92 + .../source/compiler/cmake/mac/FastXml.cmake | 45 + .../source/compiler/cmake/mac/LowLevel.cmake | 51 + .../compiler/cmake/mac/LowLevelAABB.cmake | 53 + .../compiler/cmake/mac/LowLevelDynamics.cmake | 50 + .../source/compiler/cmake/mac/PhysX.cmake | 72 + .../cmake/mac/PhysXCharacterKinematic.cmake | 41 + .../compiler/cmake/mac/PhysXCommon.cmake | 70 + .../compiler/cmake/mac/PhysXCooking.cmake | 57 + .../compiler/cmake/mac/PhysXExtensions.cmake | 55 + .../compiler/cmake/mac/PhysXFoundation.cmake | 102 + .../compiler/cmake/mac/PhysXPvdSDK.cmake | 45 + .../source/compiler/cmake/mac/PhysXTask.cmake | 41 + .../compiler/cmake/mac/PhysXVehicle.cmake | 45 + .../compiler/cmake/mac/SceneQuery.cmake | 48 + .../cmake/mac/SimulationController.cmake | 49 + .../compiler/cmake/windows/CMakeLists.txt | 216 + .../compiler/cmake/windows/FastXml.cmake | 55 + .../compiler/cmake/windows/LowLevel.cmake | 82 + .../compiler/cmake/windows/LowLevelAABB.cmake | 84 + .../cmake/windows/LowLevelDynamics.cmake | 83 + .../source/compiler/cmake/windows/PhysX.cmake | 169 + .../windows/PhysXCharacterKinematic.cmake | 65 + .../compiler/cmake/windows/PhysXCommon.cmake | 107 + .../compiler/cmake/windows/PhysXCooking.cmake | 94 + .../cmake/windows/PhysXExtensions.cmake | 77 + .../cmake/windows/PhysXFoundation.cmake | 126 + .../compiler/cmake/windows/PhysXPvdSDK.cmake | 89 + .../compiler/cmake/windows/PhysXTask.cmake | 73 + .../compiler/cmake/windows/PhysXVehicle.cmake | 64 + .../compiler/cmake/windows/SceneQuery.cmake | 81 + .../cmake/windows/SimulationController.cmake | 81 + .../source/compiler/resource_x64/PhysX.rc | 88 + .../compiler/resource_x64/PhysXCommon.rc | 88 + .../compiler/resource_x64/PhysXCooking.rc | 91 + .../compiler/resource_x64/PhysXFoundation.rc | Bin 0 -> 4644 bytes .../source/compiler/resource_x64/resource.h | 44 + .../source/compiler/resource_x86/PhysX.rc | 88 + .../compiler/resource_x86/PhysXCommon.rc | 92 + .../compiler/resource_x86/PhysXCooking.rc | 91 + .../compiler/resource_x86/PhysXFoundation.rc | Bin 0 -> 4642 bytes .../source/compiler/resource_x86/resource.h | 44 + .../physx/source/fastxml/include/PsFastXml.h | 167 + .../physx/source/fastxml/src/PsFastXml.cpp | 833 ++ .../filebuf/include/PsAsciiConversion.h | 99 + .../filebuf/include/PsAsciiConversion.inl | 566 ++ .../source/filebuf/include/PsFileBuffer.h | 250 + .../physx/source/filebuf/include/PsIOStream.h | 137 + .../source/filebuf/include/PsIOStream.inl | 451 + .../source/filebuf/include/PsMemoryBuffer.h | 449 + .../physx/source/foundation/include/Ps.h | 70 + .../foundation/include/PsAlignedMalloc.h | 88 + .../source/foundation/include/PsAlloca.h | 76 + .../source/foundation/include/PsAllocator.h | 367 + .../physx/source/foundation/include/PsAoS.h | 45 + .../physx/source/foundation/include/PsArray.h | 721 ++ .../source/foundation/include/PsAtomic.h | 63 + .../foundation/include/PsBasicTemplates.h | 146 + .../source/foundation/include/PsBitUtils.h | 109 + .../source/foundation/include/PsBroadcast.h | 277 + .../physx/source/foundation/include/PsCpu.h | 47 + .../physx/source/foundation/include/PsFPU.h | 103 + .../source/foundation/include/PsFoundation.h | 223 + .../physx/source/foundation/include/PsHash.h | 162 + .../foundation/include/PsHashInternals.h | 795 ++ .../source/foundation/include/PsHashMap.h | 118 + .../source/foundation/include/PsHashSet.h | 127 + .../foundation/include/PsInlineAllocator.h | 91 + .../source/foundation/include/PsInlineAoS.h | 48 + .../source/foundation/include/PsInlineArray.h | 68 + .../source/foundation/include/PsIntrinsics.h | 47 + .../source/foundation/include/PsMathUtils.h | 701 ++ .../physx/source/foundation/include/PsMutex.h | 182 + .../physx/source/foundation/include/PsPool.h | 298 + .../physx/source/foundation/include/PsSList.h | 140 + .../source/foundation/include/PsSocket.h | 186 + .../physx/source/foundation/include/PsSort.h | 130 + .../foundation/include/PsSortInternals.h | 188 + .../source/foundation/include/PsString.h | 90 + .../physx/source/foundation/include/PsSync.h | 138 + .../foundation/include/PsTempAllocator.h | 62 + .../source/foundation/include/PsThread.h | 384 + .../physx/source/foundation/include/PsTime.h | 95 + .../foundation/include/PsUserAllocated.h | 92 + .../source/foundation/include/PsUtilities.h | 165 + .../source/foundation/include/PsVecMath.h | 1337 +++ .../foundation/include/PsVecMathAoSScalar.h | 250 + .../include/PsVecMathAoSScalarInline.h | 2275 +++++ .../source/foundation/include/PsVecMathSSE.h | 68 + .../foundation/include/PsVecMathUtilities.h | 57 + .../source/foundation/include/PsVecQuat.h | 466 + .../foundation/include/PsVecTransform.h | 283 + .../foundation/include/unix/PsUnixAoS.h | 47 + .../foundation/include/unix/PsUnixFPU.h | 69 + .../foundation/include/unix/PsUnixInlineAoS.h | 45 + .../include/unix/PsUnixIntrinsics.h | 153 + .../include/unix/PsUnixTrigConstants.h | 93 + .../include/unix/neon/PsUnixNeonAoS.h | 140 + .../include/unix/neon/PsUnixNeonInlineAoS.h | 3597 ++++++++ .../include/unix/sse2/PsUnixSse2AoS.h | 191 + .../include/unix/sse2/PsUnixSse2InlineAoS.h | 3239 +++++++ .../foundation/include/windows/PsWindowsAoS.h | 142 + .../foundation/include/windows/PsWindowsFPU.h | 51 + .../include/windows/PsWindowsInclude.h | 104 + .../include/windows/PsWindowsInlineAoS.h | 3132 +++++++ .../include/windows/PsWindowsIntrinsics.h | 193 + .../include/windows/PsWindowsTrigConstants.h | 98 + .../source/foundation/src/PsAllocator.cpp | 124 + .../physx/source/foundation/src/PsAssert.cpp | 90 + .../source/foundation/src/PsFoundation.cpp | 284 + .../source/foundation/src/PsMathUtils.cpp | 212 + .../physx/source/foundation/src/PsString.cpp | 185 + .../source/foundation/src/PsTempAllocator.cpp | 129 + .../source/foundation/src/PsUtilities.cpp | 73 + .../foundation/src/unix/PsUnixAtomic.cpp | 102 + .../source/foundation/src/unix/PsUnixCpu.cpp | 58 + .../source/foundation/src/unix/PsUnixFPU.cpp | 117 + .../foundation/src/unix/PsUnixMutex.cpp | 172 + .../foundation/src/unix/PsUnixPrintString.cpp | 52 + .../foundation/src/unix/PsUnixSList.cpp | 156 + .../foundation/src/unix/PsUnixSocket.cpp | 483 ++ .../source/foundation/src/unix/PsUnixSync.cpp | 164 + .../foundation/src/unix/PsUnixThread.cpp | 504 ++ .../source/foundation/src/unix/PsUnixTime.cpp | 120 + .../src/windows/PsWindowsAtomic.cpp | 96 + .../foundation/src/windows/PsWindowsCpu.cpp | 64 + .../foundation/src/windows/PsWindowsFPU.cpp | 88 + .../foundation/src/windows/PsWindowsMutex.cpp | 162 + .../src/windows/PsWindowsPrintString.cpp | 54 + .../foundation/src/windows/PsWindowsSList.cpp | 77 + .../src/windows/PsWindowsSocket.cpp | 450 + .../foundation/src/windows/PsWindowsSync.cpp | 81 + .../src/windows/PsWindowsThread.cpp | 425 + .../foundation/src/windows/PsWindowsTime.cpp | 101 + .../physx/source/geomutils/include/GuAxes.h | 81 + .../physx/source/geomutils/include/GuBox.h | 246 + .../geomutils/include/GuDistanceSegmentBox.h | 57 + .../include/GuDistanceSegmentSegment.h | 65 + .../geomutils/include/GuIntersectionBoxBox.h | 54 + .../include/GuIntersectionTriangleBox.h | 91 + .../include/GuIntersectionTriangleBoxRef.h | 251 + .../source/geomutils/include/GuRaycastTests.h | 72 + .../source/geomutils/include/GuSIMDHelpers.h | 98 + .../source/geomutils/include/GuSegment.h | 182 + .../source/geomutils/src/GuAABBTreeBuild.cpp | 307 + .../source/geomutils/src/GuAABBTreeBuild.h | 185 + .../source/geomutils/src/GuAABBTreeQuery.h | 239 + .../source/geomutils/src/GuBVHStructure.cpp | 186 + .../source/geomutils/src/GuBVHStructure.h | 200 + .../source/geomutils/src/GuBVHTestsSIMD.h | 259 + .../physx/source/geomutils/src/GuBounds.cpp | 611 ++ .../physx/source/geomutils/src/GuBounds.h | 157 + .../physx/source/geomutils/src/GuBox.cpp | 132 + .../source/geomutils/src/GuCCTSweepTests.cpp | 440 + .../physx/source/geomutils/src/GuCapsule.cpp | 64 + .../physx/source/geomutils/src/GuCapsule.h | 92 + .../source/geomutils/src/GuCenterExtents.h | 131 + .../source/geomutils/src/GuGeometryQuery.cpp | 351 + .../source/geomutils/src/GuGeometryUnion.cpp | 120 + .../source/geomutils/src/GuGeometryUnion.h | 246 + .../physx/source/geomutils/src/GuInternal.cpp | 159 + .../physx/source/geomutils/src/GuInternal.h | 151 + .../physx/source/geomutils/src/GuMTD.cpp | 1457 ++++ src/PhysX/physx/source/geomutils/src/GuMTD.h | 60 + .../source/geomutils/src/GuMeshFactory.cpp | 702 ++ .../source/geomutils/src/GuMeshFactory.h | 142 + .../physx/source/geomutils/src/GuMetaData.cpp | 598 ++ .../source/geomutils/src/GuOverlapTests.cpp | 695 ++ .../source/geomutils/src/GuOverlapTests.h | 117 + .../source/geomutils/src/GuRaycastTests.cpp | 564 ++ .../source/geomutils/src/GuSerialize.cpp | 381 + .../physx/source/geomutils/src/GuSerialize.h | 196 + .../physx/source/geomutils/src/GuSphere.h | 108 + .../physx/source/geomutils/src/GuSweepMTD.cpp | 1218 +++ .../physx/source/geomutils/src/GuSweepMTD.h | 95 + .../geomutils/src/GuSweepSharedTests.cpp | 725 ++ .../source/geomutils/src/GuSweepSharedTests.h | 56 + .../source/geomutils/src/GuSweepTests.cpp | 604 ++ .../physx/source/geomutils/src/GuSweepTests.h | 136 + .../src/ccd/GuCCDSweepConvexMesh.cpp | 728 ++ .../geomutils/src/ccd/GuCCDSweepConvexMesh.h | 178 + .../src/ccd/GuCCDSweepPrimitives.cpp | 286 + .../src/common/GuBarycentricCoordinates.cpp | 89 + .../src/common/GuBarycentricCoordinates.h | 93 + .../geomutils/src/common/GuBoxConversion.h | 121 + .../source/geomutils/src/common/GuEdgeCache.h | 85 + .../geomutils/src/common/GuEdgeListData.h | 153 + .../geomutils/src/common/GuSeparatingAxes.cpp | 64 + .../geomutils/src/common/GuSeparatingAxes.h | 91 + .../geomutils/src/contact/GuContactBoxBox.cpp | 703 ++ .../src/contact/GuContactCapsuleBox.cpp | 457 + .../src/contact/GuContactCapsuleCapsule.cpp | 155 + .../src/contact/GuContactCapsuleConvex.cpp | 587 ++ .../src/contact/GuContactCapsuleMesh.cpp | 635 ++ .../src/contact/GuContactConvexConvex.cpp | 1033 +++ .../src/contact/GuContactConvexMesh.cpp | 1447 ++++ .../src/contact/GuContactMethodImpl.h | 170 + .../src/contact/GuContactPlaneBox.cpp | 128 + .../src/contact/GuContactPlaneCapsule.cpp | 80 + .../src/contact/GuContactPlaneConvex.cpp | 101 + .../src/contact/GuContactPolygonPolygon.cpp | 861 ++ .../src/contact/GuContactPolygonPolygon.h | 69 + .../src/contact/GuContactSphereBox.cpp | 181 + .../src/contact/GuContactSphereCapsule.cpp | 82 + .../src/contact/GuContactSphereMesh.cpp | 614 ++ .../src/contact/GuContactSpherePlane.cpp | 68 + .../src/contact/GuContactSphereSphere.cpp | 68 + .../geomutils/src/contact/GuFeatureCode.cpp | 128 + .../geomutils/src/contact/GuFeatureCode.h | 56 + .../geomutils/src/convex/GuBigConvexData.cpp | 203 + .../geomutils/src/convex/GuBigConvexData.h | 98 + .../geomutils/src/convex/GuBigConvexData2.h | 95 + .../geomutils/src/convex/GuConvexEdgeFlags.h | 59 + .../geomutils/src/convex/GuConvexHelper.cpp | 137 + .../geomutils/src/convex/GuConvexHelper.h | 66 + .../geomutils/src/convex/GuConvexMesh.cpp | 415 + .../geomutils/src/convex/GuConvexMesh.h | 178 + .../geomutils/src/convex/GuConvexMeshData.h | 215 + .../src/convex/GuConvexSupportTable.cpp | 44 + .../src/convex/GuConvexSupportTable.h | 117 + .../src/convex/GuConvexUtilsInternal.cpp | 78 + .../src/convex/GuConvexUtilsInternal.h | 67 + .../source/geomutils/src/convex/GuCubeIndex.h | 155 + .../geomutils/src/convex/GuHillClimbing.cpp | 96 + .../geomutils/src/convex/GuHillClimbing.h | 46 + .../geomutils/src/convex/GuShapeConvex.cpp | 516 ++ .../geomutils/src/convex/GuShapeConvex.h | 100 + .../src/distance/GuDistancePointBox.cpp | 66 + .../src/distance/GuDistancePointBox.h | 70 + .../src/distance/GuDistancePointSegment.h | 90 + .../src/distance/GuDistancePointTriangle.cpp | 353 + .../src/distance/GuDistancePointTriangle.h | 125 + .../distance/GuDistancePointTriangleSIMD.h | 54 + .../src/distance/GuDistanceSegmentBox.cpp | 549 ++ .../src/distance/GuDistanceSegmentSegment.cpp | 576 ++ .../distance/GuDistanceSegmentSegmentSIMD.h | 57 + .../distance/GuDistanceSegmentTriangle.cpp | 541 ++ .../src/distance/GuDistanceSegmentTriangle.h | 63 + .../distance/GuDistanceSegmentTriangleSIMD.h | 54 + .../physx/source/geomutils/src/gjk/GuEPA.cpp | 615 ++ .../physx/source/geomutils/src/gjk/GuEPA.h | 60 + .../source/geomutils/src/gjk/GuEPAFacet.h | 306 + .../physx/source/geomutils/src/gjk/GuGJK.h | 219 + .../geomutils/src/gjk/GuGJKPenetration.h | 320 + .../source/geomutils/src/gjk/GuGJKRaycast.h | 295 + .../source/geomutils/src/gjk/GuGJKSimplex.cpp | 216 + .../source/geomutils/src/gjk/GuGJKSimplex.h | 473 + .../source/geomutils/src/gjk/GuGJKTest.cpp | 70 + .../source/geomutils/src/gjk/GuGJKTest.h | 58 + .../source/geomutils/src/gjk/GuGJKType.h | 177 + .../source/geomutils/src/gjk/GuGJKUtil.h | 76 + .../physx/source/geomutils/src/gjk/GuVecBox.h | 226 + .../source/geomutils/src/gjk/GuVecCapsule.h | 238 + .../source/geomutils/src/gjk/GuVecConvex.h | 179 + .../geomutils/src/gjk/GuVecConvexHull.h | 501 ++ .../src/gjk/GuVecConvexHullNoScale.h | 176 + .../source/geomutils/src/gjk/GuVecPlane.h | 224 + .../source/geomutils/src/gjk/GuVecSphere.h | 243 + .../source/geomutils/src/gjk/GuVecTriangle.h | 268 + .../source/geomutils/src/hf/GuEntityReport.h | 56 + .../source/geomutils/src/hf/GuHeightField.cpp | 711 ++ .../source/geomutils/src/hf/GuHeightField.h | 1266 +++ .../geomutils/src/hf/GuHeightFieldData.h | 87 + .../geomutils/src/hf/GuHeightFieldUtil.cpp | 832 ++ .../geomutils/src/hf/GuHeightFieldUtil.h | 904 ++ .../geomutils/src/hf/GuOverlapTestsHF.cpp | 751 ++ .../source/geomutils/src/hf/GuSweepsHF.cpp | 606 ++ .../src/intersection/GuIntersectionBoxBox.cpp | 139 + .../GuIntersectionCapsuleTriangle.cpp | 61 + .../GuIntersectionCapsuleTriangle.h | 137 + .../intersection/GuIntersectionEdgeEdge.cpp | 84 + .../src/intersection/GuIntersectionEdgeEdge.h | 52 + .../src/intersection/GuIntersectionRay.h | 38 + .../src/intersection/GuIntersectionRayBox.cpp | 449 + .../src/intersection/GuIntersectionRayBox.h | 91 + .../intersection/GuIntersectionRayBoxSIMD.h | 50 + .../intersection/GuIntersectionRayCapsule.cpp | 245 + .../intersection/GuIntersectionRayCapsule.h | 93 + .../src/intersection/GuIntersectionRayPlane.h | 58 + .../intersection/GuIntersectionRaySphere.cpp | 105 + .../intersection/GuIntersectionRaySphere.h | 50 + .../intersection/GuIntersectionRayTriangle.h | 179 + .../intersection/GuIntersectionSphereBox.cpp | 88 + .../intersection/GuIntersectionSphereBox.h | 54 + .../GuIntersectionTriangleBox.cpp | 198 + .../source/geomutils/src/mesh/GuBV32.cpp | 276 + .../physx/source/geomutils/src/mesh/GuBV32.h | 146 + .../source/geomutils/src/mesh/GuBV32Build.cpp | 530 ++ .../source/geomutils/src/mesh/GuBV32Build.h | 50 + .../physx/source/geomutils/src/mesh/GuBV4.cpp | 390 + .../physx/source/geomutils/src/mesh/GuBV4.h | 283 + .../source/geomutils/src/mesh/GuBV4Build.cpp | 1503 ++++ .../source/geomutils/src/mesh/GuBV4Build.h | 125 + .../source/geomutils/src/mesh/GuBV4Settings.h | 40 + .../src/mesh/GuBV4_AABBAABBSweepTest.h | 114 + .../geomutils/src/mesh/GuBV4_AABBSweep.cpp | 39 + .../src/mesh/GuBV4_BoxBoxOverlapTest.h | 201 + .../geomutils/src/mesh/GuBV4_BoxOverlap.cpp | 463 + .../src/mesh/GuBV4_BoxOverlap_Internal.h | 103 + .../src/mesh/GuBV4_BoxSweep_Internal.h | 518 ++ .../src/mesh/GuBV4_BoxSweep_Params.h | 211 + .../geomutils/src/mesh/GuBV4_CapsuleSweep.cpp | 173 + .../src/mesh/GuBV4_CapsuleSweepAA.cpp | 110 + .../src/mesh/GuBV4_CapsuleSweep_Internal.h | 434 + .../source/geomutils/src/mesh/GuBV4_Common.h | 452 + .../geomutils/src/mesh/GuBV4_Internal.h | 318 + .../geomutils/src/mesh/GuBV4_OBBSweep.cpp | 170 + .../mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h | 111 + .../GuBV4_ProcessStreamNoOrder_SegmentAABB.h | 55 + ...rocessStreamNoOrder_SegmentAABB_Inflated.h | 55 + .../GuBV4_ProcessStreamNoOrder_SphereAABB.h | 113 + .../mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h | 111 + .../GuBV4_ProcessStreamOrdered_SegmentAABB.h | 67 + ...rocessStreamOrdered_SegmentAABB_Inflated.h | 67 + .../geomutils/src/mesh/GuBV4_Raycast.cpp | 622 ++ .../source/geomutils/src/mesh/GuBV4_Slabs.h | 193 + .../src/mesh/GuBV4_Slabs_KajiyaNoOrder.h | 309 + .../src/mesh/GuBV4_Slabs_KajiyaOrdered.h | 552 ++ .../src/mesh/GuBV4_Slabs_SwizzledNoOrder.h | 133 + .../src/mesh/GuBV4_Slabs_SwizzledOrdered.h | 158 + .../src/mesh/GuBV4_SphereOverlap.cpp | 326 + .../geomutils/src/mesh/GuBV4_SphereSweep.cpp | 386 + .../source/geomutils/src/mesh/GuBVConstants.h | 44 + .../source/geomutils/src/mesh/GuMeshData.h | 300 + .../source/geomutils/src/mesh/GuMeshQuery.cpp | 304 + .../geomutils/src/mesh/GuMidphaseBV4.cpp | 999 +++ .../geomutils/src/mesh/GuMidphaseInterface.h | 417 + .../geomutils/src/mesh/GuMidphaseRTree.cpp | 887 ++ .../geomutils/src/mesh/GuOverlapTestsMesh.cpp | 241 + .../source/geomutils/src/mesh/GuRTree.cpp | 430 + .../physx/source/geomutils/src/mesh/GuRTree.h | 297 + .../geomutils/src/mesh/GuRTreeQueries.cpp | 573 ++ .../geomutils/src/mesh/GuSweepConvexTri.h | 105 + .../source/geomutils/src/mesh/GuSweepMesh.h | 169 + .../geomutils/src/mesh/GuSweepsMesh.cpp | 603 ++ .../source/geomutils/src/mesh/GuTriangle32.h | 132 + .../geomutils/src/mesh/GuTriangleCache.h | 207 + .../geomutils/src/mesh/GuTriangleMesh.cpp | 232 + .../geomutils/src/mesh/GuTriangleMesh.h | 284 + .../geomutils/src/mesh/GuTriangleMeshBV4.cpp | 76 + .../geomutils/src/mesh/GuTriangleMeshBV4.h | 76 + .../src/mesh/GuTriangleMeshRTree.cpp | 151 + .../geomutils/src/mesh/GuTriangleMeshRTree.h | 81 + .../src/mesh/GuTriangleVertexPointers.h | 65 + .../geomutils/src/pcm/GuPCMContactBoxBox.cpp | 1030 +++ .../src/pcm/GuPCMContactBoxConvex.cpp | 274 + .../src/pcm/GuPCMContactCapsuleBox.cpp | 224 + .../src/pcm/GuPCMContactCapsuleCapsule.cpp | 297 + .../src/pcm/GuPCMContactCapsuleConvex.cpp | 266 + .../pcm/GuPCMContactCapsuleHeightField.cpp | 158 + .../src/pcm/GuPCMContactCapsuleMesh.cpp | 185 + .../src/pcm/GuPCMContactConvexCommon.cpp | 1354 +++ .../src/pcm/GuPCMContactConvexCommon.h | 437 + .../src/pcm/GuPCMContactConvexConvex.cpp | 294 + .../src/pcm/GuPCMContactConvexHeightField.cpp | 267 + .../src/pcm/GuPCMContactConvexMesh.cpp | 259 + .../geomutils/src/pcm/GuPCMContactGen.h | 82 + .../src/pcm/GuPCMContactGenBoxConvex.cpp | 811 ++ .../src/pcm/GuPCMContactGenSphereCapsule.cpp | 444 + .../geomutils/src/pcm/GuPCMContactGenUtil.h | 464 + .../src/pcm/GuPCMContactMeshCallback.h | 208 + .../src/pcm/GuPCMContactPlaneBox.cpp | 219 + .../src/pcm/GuPCMContactPlaneCapsule.cpp | 133 + .../src/pcm/GuPCMContactPlaneConvex.cpp | 159 + .../src/pcm/GuPCMContactSphereBox.cpp | 156 + .../src/pcm/GuPCMContactSphereCapsule.cpp | 121 + .../src/pcm/GuPCMContactSphereConvex.cpp | 284 + .../src/pcm/GuPCMContactSphereHeightField.cpp | 159 + .../src/pcm/GuPCMContactSphereMesh.cpp | 178 + .../src/pcm/GuPCMContactSpherePlane.cpp | 86 + .../src/pcm/GuPCMContactSphereSphere.cpp | 84 + .../geomutils/src/pcm/GuPCMShapeConvex.cpp | 210 + .../geomutils/src/pcm/GuPCMShapeConvex.h | 68 + .../src/pcm/GuPCMTriangleContactGen.cpp | 1254 +++ .../src/pcm/GuPCMTriangleContactGen.h | 69 + .../src/pcm/GuPersistentContactManifold.cpp | 2432 ++++++ .../src/pcm/GuPersistentContactManifold.h | 855 ++ .../geomutils/src/sweep/GuSweepBoxBox.cpp | 272 + .../geomutils/src/sweep/GuSweepBoxBox.h | 49 + .../geomutils/src/sweep/GuSweepBoxSphere.cpp | 158 + .../geomutils/src/sweep/GuSweepBoxSphere.h | 49 + .../sweep/GuSweepBoxTriangle_FeatureBased.cpp | 622 ++ .../sweep/GuSweepBoxTriangle_FeatureBased.h | 67 + .../src/sweep/GuSweepBoxTriangle_SAT.cpp | 39 + .../src/sweep/GuSweepBoxTriangle_SAT.h | 235 + .../geomutils/src/sweep/GuSweepCapsuleBox.cpp | 215 + .../geomutils/src/sweep/GuSweepCapsuleBox.h | 49 + .../src/sweep/GuSweepCapsuleCapsule.cpp | 344 + .../src/sweep/GuSweepCapsuleCapsule.h | 48 + .../src/sweep/GuSweepCapsuleTriangle.cpp | 334 + .../src/sweep/GuSweepCapsuleTriangle.h | 75 + .../src/sweep/GuSweepSphereCapsule.cpp | 103 + .../src/sweep/GuSweepSphereCapsule.h | 50 + .../src/sweep/GuSweepSphereSphere.cpp | 116 + .../geomutils/src/sweep/GuSweepSphereSphere.h | 46 + .../src/sweep/GuSweepSphereTriangle.cpp | 524 ++ .../src/sweep/GuSweepSphereTriangle.h | 156 + .../src/sweep/GuSweepTriangleUtils.cpp | 223 + .../src/sweep/GuSweepTriangleUtils.h | 338 + .../immediatemode/src/NpImmediateMode.cpp | 769 ++ .../lowlevel/api/include/PxsMaterialCore.h | 167 + .../lowlevel/api/include/PxsMaterialManager.h | 149 + .../source/lowlevel/api/include/PxvConfig.h | 47 + .../source/lowlevel/api/include/PxvDynamics.h | 157 + .../source/lowlevel/api/include/PxvGeometry.h | 93 + .../source/lowlevel/api/include/PxvGlobals.h | 116 + .../source/lowlevel/api/include/PxvManager.h | 239 + .../source/lowlevel/api/include/PxvSimStats.h | 114 + .../source/lowlevel/api/src/px_globals.cpp | 117 + .../include/collision/PxcContactMethodImpl.h | 95 + .../include/pipeline/PxcCCDStateStreamPair.h | 29 + .../pipeline/PxcConstraintBlockStream.h | 166 + .../common/include/pipeline/PxcContactCache.h | 64 + .../include/pipeline/PxcMaterialMethodImpl.h | 69 + .../common/include/pipeline/PxcNpBatch.h | 65 + .../common/include/pipeline/PxcNpCache.h | 154 + .../include/pipeline/PxcNpCacheStreamPair.h | 63 + .../include/pipeline/PxcNpContactPrepShared.h | 57 + .../include/pipeline/PxcNpMemBlockPool.h | 119 + .../include/pipeline/PxcNpThreadContext.h | 211 + .../common/include/pipeline/PxcNpWorkUnit.h | 165 + .../common/include/pipeline/PxcRigidBody.h | 118 + .../include/utils/PxcScratchAllocator.h | 138 + .../include/utils/PxcThreadCoherentCache.h | 150 + .../common/src/collision/PxcContact.cpp | 267 + .../common/src/pipeline/PxcContactCache.cpp | 393 + .../src/pipeline/PxcContactMethodImpl.cpp | 279 + .../src/pipeline/PxcMaterialHeightField.cpp | 116 + .../common/src/pipeline/PxcMaterialMesh.cpp | 107 + .../src/pipeline/PxcMaterialMethodImpl.cpp | 140 + .../common/src/pipeline/PxcMaterialShape.cpp | 62 + .../common/src/pipeline/PxcNpBatch.cpp | 445 + .../src/pipeline/PxcNpCacheStreamPair.cpp | 75 + .../src/pipeline/PxcNpContactPrepShared.cpp | 554 ++ .../common/src/pipeline/PxcNpMemBlockPool.cpp | 351 + .../src/pipeline/PxcNpThreadContext.cpp | 93 + .../lowlevel/software/include/PxsBodySim.h | 51 + .../source/lowlevel/software/include/PxsCCD.h | 622 ++ .../software/include/PxsContactManager.h | 178 + .../software/include/PxsContactManagerState.h | 95 + .../lowlevel/software/include/PxsContext.h | 333 + .../include/PxsDefaultMemoryManager.h | 98 + .../software/include/PxsHeapMemoryAllocator.h | 66 + .../PxsIncrementalConstraintPartitioning.h | 40 + .../software/include/PxsIslandManagerTypes.h | 363 + .../software/include/PxsIslandNodeIndex.h | 78 + .../lowlevel/software/include/PxsIslandSim.h | 935 ++ .../software/include/PxsKernelWrangler.h | 49 + .../software/include/PxsMaterialCombiner.h | 143 + .../software/include/PxsMemoryManager.h | 61 + .../include/PxsNphaseImplementationContext.h | 143 + .../lowlevel/software/include/PxsRigidBody.h | 169 + .../lowlevel/software/include/PxsShapeSim.h | 52 + .../software/include/PxsSimpleIslandManager.h | 202 + .../include/PxsSimulationController.h | 139 + .../software/include/PxsTransformCache.h | 144 + .../include/PxvNphaseImplementationContext.h | 217 + .../source/lowlevel/software/src/PxsCCD.cpp | 2060 +++++ .../software/src/PxsContactManager.cpp | 87 + .../lowlevel/software/src/PxsContext.cpp | 623 ++ .../software/src/PxsDefaultMemoryManager.cpp | 74 + .../lowlevel/software/src/PxsIslandSim.cpp | 2323 +++++ .../software/src/PxsMaterialCombiner.cpp | 102 + .../src/PxsNphaseImplementationContext.cpp | 999 +++ .../software/src/PxsSimpleIslandManager.cpp | 377 + .../lowlevelaabb/include/BpAABBManager.h | 610 ++ .../lowlevelaabb/include/BpAABBManagerTasks.h | 109 + .../lowlevelaabb/include/BpBroadPhase.h | 326 + .../lowlevelaabb/include/BpBroadPhaseUpdate.h | 523 ++ .../source/lowlevelaabb/src/BpAABBManager.cpp | 2532 ++++++ .../source/lowlevelaabb/src/BpBroadPhase.cpp | 175 + .../lowlevelaabb/src/BpBroadPhaseABP.cpp | 3402 ++++++++ .../source/lowlevelaabb/src/BpBroadPhaseABP.h | 101 + .../lowlevelaabb/src/BpBroadPhaseMBP.cpp | 3422 ++++++++ .../source/lowlevelaabb/src/BpBroadPhaseMBP.h | 114 + .../lowlevelaabb/src/BpBroadPhaseMBPCommon.h | 199 + .../lowlevelaabb/src/BpBroadPhaseSap.cpp | 1970 +++++ .../source/lowlevelaabb/src/BpBroadPhaseSap.h | 227 + .../lowlevelaabb/src/BpBroadPhaseSapAux.cpp | 964 +++ .../lowlevelaabb/src/BpBroadPhaseSapAux.h | 285 + .../lowlevelaabb/src/BpBroadPhaseShared.cpp | 246 + .../lowlevelaabb/src/BpBroadPhaseShared.h | 243 + .../source/lowlevelaabb/src/BpMBPTasks.cpp | 29 + .../source/lowlevelaabb/src/BpMBPTasks.h | 105 + .../source/lowlevelaabb/src/BpSAPTasks.cpp | 69 + .../source/lowlevelaabb/src/BpSAPTasks.h | 103 + .../lowleveldynamics/include/DyArticulation.h | 284 + .../include/DyArticulationCore.h | 78 + .../include/DyArticulationJointCore.h | 144 + .../lowleveldynamics/include/DyConstraint.h | 89 + .../include/DyConstraintWriteBack.h | 65 + .../lowleveldynamics/include/DyContext.h | 400 + .../include/DyFeatherstoneArticulation.h | 857 ++ .../DyFeatherstoneArticulationJointData.h | 261 + .../include/DyFeatherstoneArticulationUtils.h | 881 ++ .../include/DySleepingConfigulation.h | 42 + .../include/DyThresholdTable.h | 280 + .../include/DyVArticulation.h | 492 ++ .../lowleveldynamics/src/DyArticulation.cpp | 1665 ++++ .../src/DyArticulationContactPrep.cpp | 453 + .../src/DyArticulationContactPrep.h | 97 + .../src/DyArticulationContactPrepPF.cpp | 316 + .../src/DyArticulationFnsDebug.h | 262 + .../src/DyArticulationFnsScalar.h | 397 + .../src/DyArticulationFnsSimd.h | 438 + .../src/DyArticulationHelper.cpp | 524 ++ .../src/DyArticulationHelper.h | 214 + .../src/DyArticulationPImpl.h | 203 + .../src/DyArticulationReference.h | 92 + .../src/DyArticulationSIMD.cpp | 317 + .../src/DyArticulationScalar.cpp | 575 ++ .../src/DyArticulationScalar.h | 101 + .../src/DyArticulationUtils.h | 336 + .../src/DyBodyCoreIntegrator.h | 409 + .../src/DyConstraintPartition.cpp | 917 ++ .../src/DyConstraintPartition.h | 79 + .../lowleveldynamics/src/DyConstraintPrep.h | 101 + .../src/DyConstraintSetup.cpp | 605 ++ .../src/DyConstraintSetupBlock.cpp | 541 ++ .../lowleveldynamics/src/DyContactPrep.cpp | 820 ++ .../lowleveldynamics/src/DyContactPrep.h | 183 + .../lowleveldynamics/src/DyContactPrep4.cpp | 1535 ++++ .../lowleveldynamics/src/DyContactPrep4PF.cpp | 1030 +++ .../lowleveldynamics/src/DyContactPrepPF.cpp | 664 ++ .../src/DyContactPrepShared.h | 308 + .../lowleveldynamics/src/DyContactReduction.h | 409 + .../src/DyCorrelationBuffer.h | 107 + .../lowleveldynamics/src/DyDynamics.cpp | 3145 +++++++ .../source/lowleveldynamics/src/DyDynamics.h | 490 ++ .../src/DyFeatherstoneArticulation.cpp | 4218 +++++++++ .../src/DyFeatherstoneArticulationLink.h | 79 + .../src/DyFeatherstoneForwardDynamic.cpp | 1677 ++++ .../src/DyFeatherstoneInverseDynamic.cpp | 2120 +++++ .../src/DyFrictionCorrelation.cpp | 298 + .../lowleveldynamics/src/DyFrictionPatch.h | 83 + .../src/DyFrictionPatchStreamPair.h | 128 + .../src/DyRigidBodyToSolverBody.cpp | 93 + .../lowleveldynamics/src/DySolverBody.h | 72 + .../src/DySolverConstraint1D.h | 204 + .../src/DySolverConstraint1D4.h | 106 + .../src/DySolverConstraint1DStep.h | 222 + .../src/DySolverConstraintDesc.h | 142 + .../src/DySolverConstraintExtShared.h | 57 + .../src/DySolverConstraintTypes.h | 72 + .../src/DySolverConstraints.cpp | 1261 +++ .../src/DySolverConstraintsBlock.cpp | 1227 +++ .../src/DySolverConstraintsShared.h | 171 + .../lowleveldynamics/src/DySolverContact.h | 228 + .../lowleveldynamics/src/DySolverContact4.h | 179 + .../lowleveldynamics/src/DySolverContactPF.h | 123 + .../lowleveldynamics/src/DySolverContactPF4.h | 155 + .../lowleveldynamics/src/DySolverContext.h | 66 + .../lowleveldynamics/src/DySolverControl.cpp | 770 ++ .../lowleveldynamics/src/DySolverControl.h | 167 + .../src/DySolverControlPF.cpp | 760 ++ .../lowleveldynamics/src/DySolverControlPF.h | 71 + .../lowleveldynamics/src/DySolverCore.h | 251 + .../source/lowleveldynamics/src/DySolverExt.h | 85 + .../src/DySolverPFConstraints.cpp | 879 ++ .../src/DySolverPFConstraintsBlock.cpp | 985 +++ .../source/lowleveldynamics/src/DySpatial.h | 142 + .../lowleveldynamics/src/DyTGSContactPrep.cpp | 3381 ++++++++ .../src/DyTGSContactPrepBlock.cpp | 3658 ++++++++ .../lowleveldynamics/src/DyTGSDynamics.cpp | 3307 +++++++ .../lowleveldynamics/src/DyTGSDynamics.h | 637 ++ .../lowleveldynamics/src/DyTGSPartition.cpp | 961 +++ .../lowleveldynamics/src/DyTGSPartition.h | 79 + .../lowleveldynamics/src/DyThreadContext.cpp | 110 + .../lowleveldynamics/src/DyThreadContext.h | 255 + .../lowleveldynamics/src/DyThresholdTable.cpp | 68 + src/PhysX/physx/source/physx/src/NpActor.cpp | 486 ++ src/PhysX/physx/source/physx/src/NpActor.h | 163 + .../physx/source/physx/src/NpActorTemplate.h | 254 + .../physx/source/physx/src/NpAggregate.cpp | 421 + .../physx/source/physx/src/NpAggregate.h | 100 + .../physx/source/physx/src/NpArticulation.cpp | 247 + .../physx/source/physx/src/NpArticulation.h | 143 + .../source/physx/src/NpArticulationJoint.cpp | 381 + .../source/physx/src/NpArticulationJoint.h | 357 + .../NpArticulationJointReducedCoordinate.cpp | 242 + .../NpArticulationJointReducedCoordinate.h | 108 + .../source/physx/src/NpArticulationLink.cpp | 620 ++ .../source/physx/src/NpArticulationLink.h | 177 + .../src/NpArticulationReducedCoordinate.cpp | 424 + .../src/NpArticulationReducedCoordinate.h | 169 + .../source/physx/src/NpArticulationTemplate.h | 568 ++ .../physx/source/physx/src/NpBatchQuery.cpp | 603 ++ .../physx/source/physx/src/NpBatchQuery.h | 166 + src/PhysX/physx/source/physx/src/NpCast.h | 103 + .../physx/source/physx/src/NpConnector.h | 136 + .../physx/source/physx/src/NpConstraint.cpp | 407 + .../physx/source/physx/src/NpConstraint.h | 131 + .../physx/source/physx/src/NpFactory.cpp | 850 ++ src/PhysX/physx/source/physx/src/NpFactory.h | 266 + .../physx/source/physx/src/NpMaterial.cpp | 205 + src/PhysX/physx/source/physx/src/NpMaterial.h | 122 + .../source/physx/src/NpMaterialManager.h | 164 + .../physx/source/physx/src/NpMetaData.cpp | 546 ++ .../physx/source/physx/src/NpPhysics.cpp | 747 ++ src/PhysX/physx/source/physx/src/NpPhysics.h | 247 + .../physx/src/NpPhysicsInsertionCallback.h | 72 + .../physx/src/NpPtrTableStorageManager.h | 105 + .../physx/src/NpPvdSceneQueryCollector.cpp | 182 + .../physx/src/NpPvdSceneQueryCollector.h | 231 + .../physx/source/physx/src/NpQueryShared.h | 93 + .../physx/source/physx/src/NpReadCheck.cpp | 83 + .../physx/source/physx/src/NpReadCheck.h | 69 + .../source/physx/src/NpRigidActorTemplate.h | 452 + .../physx/src/NpRigidActorTemplateInternal.h | 69 + .../source/physx/src/NpRigidBodyTemplate.h | 631 ++ .../physx/source/physx/src/NpRigidDynamic.cpp | 566 ++ .../physx/source/physx/src/NpRigidDynamic.h | 176 + .../physx/source/physx/src/NpRigidStatic.cpp | 163 + .../physx/source/physx/src/NpRigidStatic.h | 115 + src/PhysX/physx/source/physx/src/NpScene.cpp | 3022 +++++++ src/PhysX/physx/source/physx/src/NpScene.h | 496 ++ .../physx/source/physx/src/NpSceneAccessor.h | 60 + .../physx/source/physx/src/NpSceneQueries.cpp | 850 ++ .../physx/source/physx/src/NpSceneQueries.h | 236 + .../source/physx/src/NpSerializerAdapter.cpp | 182 + src/PhysX/physx/source/physx/src/NpShape.cpp | 823 ++ src/PhysX/physx/source/physx/src/NpShape.h | 219 + .../physx/source/physx/src/NpShapeManager.cpp | 799 ++ .../physx/source/physx/src/NpShapeManager.h | 134 + .../physx/source/physx/src/NpWriteCheck.cpp | 92 + .../physx/source/physx/src/NpWriteCheck.h | 79 + .../source/physx/src/PvdMetaDataBindingData.h | 82 + .../physx/src/PvdMetaDataPvdBinding.cpp | 1635 ++++ .../source/physx/src/PvdMetaDataPvdBinding.h | 151 + .../source/physx/src/PvdPhysicsClient.cpp | 216 + .../physx/source/physx/src/PvdPhysicsClient.h | 95 + .../physx/source/physx/src/PvdTypeNames.h | 174 + .../source/physx/src/buffering/ScbActor.cpp | 53 + .../source/physx/src/buffering/ScbActor.h | 207 + .../physx/src/buffering/ScbAggregate.cpp | 139 + .../source/physx/src/buffering/ScbAggregate.h | 238 + .../physx/src/buffering/ScbArticulation.h | 414 + .../src/buffering/ScbArticulationJoint.h | 578 ++ .../source/physx/src/buffering/ScbBase.cpp | 47 + .../source/physx/src/buffering/ScbBase.h | 318 + .../source/physx/src/buffering/ScbBody.h | 1134 +++ .../physx/src/buffering/ScbConstraint.h | 313 + .../source/physx/src/buffering/ScbDefs.h | 210 + .../physx/src/buffering/ScbMetaData.cpp | 169 + .../source/physx/src/buffering/ScbNpDeps.h | 77 + .../physx/src/buffering/ScbRigidObject.h | 511 ++ .../physx/src/buffering/ScbRigidStatic.h | 163 + .../source/physx/src/buffering/ScbScene.cpp | 1334 +++ .../source/physx/src/buffering/ScbScene.h | 753 ++ .../physx/src/buffering/ScbSceneBuffer.h | 153 + .../physx/src/buffering/ScbScenePvdClient.cpp | 909 ++ .../physx/src/buffering/ScbScenePvdClient.h | 202 + .../source/physx/src/buffering/ScbShape.cpp | 144 + .../source/physx/src/buffering/ScbShape.h | 481 ++ .../source/physx/src/buffering/ScbType.h | 54 + .../source/physx/src/device/PhysXIndicator.h | 56 + .../src/device/linux/PhysXIndicatorLinux.cpp | 51 + .../source/physx/src/device/nvPhysXtoDrv.h | 93 + .../device/windows/PhysXIndicatorWindows.cpp | 131 + .../physx/source/physx/src/gpu/PxGpu.cpp | 93 + .../physx/src/gpu/PxPhysXGpuModuleLoader.cpp | 194 + .../src/windows/NpWindowsDelayLoadHook.cpp | 90 + .../src/CctBoxController.cpp | 197 + .../src/CctBoxController.h | 112 + .../src/CctCapsuleController.cpp | 192 + .../src/CctCapsuleController.h | 111 + .../src/CctCharacterController.cpp | 2552 ++++++ .../src/CctCharacterController.h | 481 ++ .../src/CctCharacterControllerCallbacks.cpp | 1106 +++ .../src/CctCharacterControllerManager.cpp | 661 ++ .../src/CctCharacterControllerManager.h | 147 + .../src/CctController.cpp | 235 + .../src/CctController.h | 130 + .../src/CctInternalStructs.h | 86 + .../src/CctObstacleContext.cpp | 538 ++ .../src/CctObstacleContext.h | 136 + .../src/CctSweptBox.cpp | 50 + .../physxcharacterkinematic/src/CctSweptBox.h | 55 + .../src/CctSweptCapsule.cpp | 48 + .../src/CctSweptCapsule.h | 56 + .../src/CctSweptVolume.cpp | 74 + .../src/CctSweptVolume.h | 76 + .../physxcharacterkinematic/src/CctUtils.h | 214 + .../source/physxcooking/src/Adjacencies.cpp | 712 ++ .../source/physxcooking/src/Adjacencies.h | 234 + .../physxcooking/src/BVHStructureBuilder.cpp | 215 + .../physxcooking/src/BVHStructureBuilder.h | 68 + .../physx/source/physxcooking/src/Cooking.cpp | 554 ++ .../physx/source/physxcooking/src/Cooking.h | 90 + .../source/physxcooking/src/CookingUtils.cpp | 120 + .../source/physxcooking/src/CookingUtils.h | 79 + .../source/physxcooking/src/EdgeList.cpp | 753 ++ .../physx/source/physxcooking/src/EdgeList.h | 110 + .../source/physxcooking/src/MeshCleaner.cpp | 234 + .../source/physxcooking/src/MeshCleaner.h | 55 + .../source/physxcooking/src/Quantizer.cpp | 338 + .../physx/source/physxcooking/src/Quantizer.h | 76 + .../src/convex/BigConvexDataBuilder.cpp | 358 + .../src/convex/BigConvexDataBuilder.h | 100 + .../src/convex/ConvexHullBuilder.cpp | 755 ++ .../src/convex/ConvexHullBuilder.h | 95 + .../physxcooking/src/convex/ConvexHullLib.cpp | 352 + .../physxcooking/src/convex/ConvexHullLib.h | 99 + .../src/convex/ConvexHullUtils.cpp | 925 ++ .../physxcooking/src/convex/ConvexHullUtils.h | 177 + .../src/convex/ConvexMeshBuilder.cpp | 502 ++ .../src/convex/ConvexMeshBuilder.h | 100 + .../src/convex/ConvexPolygonsBuilder.cpp | 1328 +++ .../src/convex/ConvexPolygonsBuilder.h | 64 + .../src/convex/QuickHullConvexHullLib.cpp | 2581 ++++++ .../src/convex/QuickHullConvexHullLib.h | 99 + .../src/convex/VolumeIntegration.cpp | 797 ++ .../src/convex/VolumeIntegration.h | 102 + .../src/mesh/GrbTriangleMeshCooking.cpp | 29 + .../src/mesh/GrbTriangleMeshCooking.h | 337 + .../src/mesh/HeightFieldCooking.cpp | 84 + .../src/mesh/HeightFieldCooking.h | 35 + .../physxcooking/src/mesh/MeshBuilder.cpp | 78 + .../physxcooking/src/mesh/MeshBuilder.h | 45 + .../physxcooking/src/mesh/QuickSelect.h | 114 + .../physxcooking/src/mesh/RTreeCooking.cpp | 894 ++ .../physxcooking/src/mesh/RTreeCooking.h | 51 + .../src/mesh/TriangleMeshBuilder.cpp | 1385 +++ .../src/mesh/TriangleMeshBuilder.h | 123 + .../windows/WindowsCookingDelayLoadHook.cpp | 82 + .../physxextensions/src/ExtBroadPhase.cpp | 74 + .../physxextensions/src/ExtCollection.cpp | 232 + .../physxextensions/src/ExtConstraintHelper.h | 387 + .../physxextensions/src/ExtContactJoint.cpp | 250 + .../physxextensions/src/ExtContactJoint.h | 122 + .../physxextensions/src/ExtConvexMeshExt.cpp | 91 + .../src/ExtCpuWorkerThread.cpp | 106 + .../physxextensions/src/ExtCpuWorkerThread.h | 79 + .../source/physxextensions/src/ExtD6Joint.cpp | 1089 +++ .../source/physxextensions/src/ExtD6Joint.h | 200 + .../physxextensions/src/ExtD6JointCreate.cpp | 289 + .../src/ExtDefaultCpuDispatcher.cpp | 205 + .../src/ExtDefaultCpuDispatcher.h | 122 + .../src/ExtDefaultErrorCallback.cpp | 104 + .../src/ExtDefaultSimulationFilterShader.cpp | 336 + .../physxextensions/src/ExtDefaultStreams.cpp | 194 + .../physxextensions/src/ExtDistanceJoint.cpp | 334 + .../physxextensions/src/ExtDistanceJoint.h | 139 + .../physxextensions/src/ExtExtensions.cpp | 193 + .../physxextensions/src/ExtFixedJoint.cpp | 178 + .../physxextensions/src/ExtFixedJoint.h | 117 + .../physxextensions/src/ExtInertiaTensor.h | 404 + .../source/physxextensions/src/ExtJoint.cpp | 134 + .../source/physxextensions/src/ExtJoint.h | 640 ++ .../src/ExtJointMetaDataExtensions.h | 65 + .../physxextensions/src/ExtMetaData.cpp | 445 + .../source/physxextensions/src/ExtPlatform.h | 39 + .../physxextensions/src/ExtPrismaticJoint.cpp | 236 + .../physxextensions/src/ExtPrismaticJoint.h | 132 + .../source/physxextensions/src/ExtPvd.cpp | 165 + .../physx/source/physxextensions/src/ExtPvd.h | 191 + .../physxextensions/src/ExtPxStringTable.cpp | 99 + .../physxextensions/src/ExtRaycastCCD.cpp | 305 + .../physxextensions/src/ExtRevoluteJoint.cpp | 345 + .../physxextensions/src/ExtRevoluteJoint.h | 153 + .../physxextensions/src/ExtRigidActorExt.cpp | 71 + .../physxextensions/src/ExtRigidBodyExt.cpp | 648 ++ .../physxextensions/src/ExtSceneQueryExt.cpp | 188 + .../physxextensions/src/ExtSerialization.h | 44 + .../src/ExtSharedQueueEntryPool.h | 156 + .../physxextensions/src/ExtSimpleFactory.cpp | 384 + .../physxextensions/src/ExtSmoothNormals.cpp | 145 + .../physxextensions/src/ExtSphericalJoint.cpp | 241 + .../physxextensions/src/ExtSphericalJoint.h | 129 + .../physxextensions/src/ExtTaskQueueHelper.h | 66 + .../src/ExtTriangleMeshExt.cpp | 164 + .../Binary/SnBinaryDeserialization.cpp | 305 + .../Binary/SnBinarySerialization.cpp | 402 + .../src/serialization/Binary/SnConvX.cpp | 166 + .../src/serialization/Binary/SnConvX.h | 183 + .../serialization/Binary/SnConvX_Align.cpp | 63 + .../src/serialization/Binary/SnConvX_Align.h | 44 + .../src/serialization/Binary/SnConvX_Common.h | 43 + .../serialization/Binary/SnConvX_Convert.cpp | 1434 +++ .../serialization/Binary/SnConvX_Error.cpp | 92 + .../serialization/Binary/SnConvX_MetaData.cpp | 960 +++ .../serialization/Binary/SnConvX_MetaData.h | 188 + .../serialization/Binary/SnConvX_Output.cpp | 451 + .../src/serialization/Binary/SnConvX_Output.h | 112 + .../serialization/Binary/SnConvX_Union.cpp | 90 + .../src/serialization/Binary/SnConvX_Union.h | 45 + .../Binary/SnSerializationContext.cpp | 94 + .../Binary/SnSerializationContext.h | 303 + .../src/serialization/File/SnFile.h | 85 + .../src/serialization/SnSerialUtils.cpp | 159 + .../src/serialization/SnSerialUtils.h | 50 + .../src/serialization/SnSerialization.cpp | 425 + .../serialization/SnSerializationRegistry.cpp | 287 + .../serialization/SnSerializationRegistry.h | 91 + .../Xml/SnJointRepXSerializer.cpp | 145 + .../serialization/Xml/SnJointRepXSerializer.h | 73 + .../serialization/Xml/SnPxStreamOperators.h | 134 + .../src/serialization/Xml/SnRepX1_0Defaults.h | 245 + .../src/serialization/Xml/SnRepX3_1Defaults.h | 274 + .../src/serialization/Xml/SnRepX3_2Defaults.h | 313 + .../src/serialization/Xml/SnRepXCollection.h | 173 + .../Xml/SnRepXCoreSerializer.cpp | 553 ++ .../serialization/Xml/SnRepXCoreSerializer.h | 125 + .../serialization/Xml/SnRepXSerializerImpl.h | 90 + .../src/serialization/Xml/SnRepXUpgrader.cpp | 465 + .../src/serialization/Xml/SnRepXUpgrader.h | 54 + .../src/serialization/Xml/SnSimpleXmlWriter.h | 257 + .../src/serialization/Xml/SnXmlDeserializer.h | 197 + .../src/serialization/Xml/SnXmlImpl.h | 243 + .../serialization/Xml/SnXmlMemoryAllocator.h | 129 + .../src/serialization/Xml/SnXmlMemoryPool.h | 373 + .../Xml/SnXmlMemoryPoolStreams.h | 173 + .../src/serialization/Xml/SnXmlReader.h | 130 + .../serialization/Xml/SnXmlSerialization.cpp | 834 ++ .../src/serialization/Xml/SnXmlSerializer.h | 117 + .../serialization/Xml/SnXmlSimpleXmlWriter.h | 215 + .../src/serialization/Xml/SnXmlStringToType.h | 275 + .../serialization/Xml/SnXmlVisitorReader.h | 907 ++ .../serialization/Xml/SnXmlVisitorWriter.h | 801 ++ .../src/serialization/Xml/SnXmlWriter.h | 57 + .../source/physxgpu/include/PxPhysXGpu.h | 189 + .../include/PvdMetaDataDefineProperties.h | 352 + .../core/include/PvdMetaDataExtensions.h | 318 + .../core/include/PvdMetaDataPropertyVisitor.h | 539 ++ .../PxAutoGeneratedMetaDataObjectNames.h | 373 + .../include/PxAutoGeneratedMetaDataObjects.h | 2888 +++++++ .../core/include/PxMetaDataCompare.h | 388 + .../core/include/PxMetaDataCppPrefix.h | 37 + .../core/include/PxMetaDataObjects.h | 662 ++ .../include/RepXMetaDataPropertyVisitor.h | 213 + .../src/PxAutoGeneratedMetaDataObjects.cpp | 1236 +++ .../core/src/PxMetaDataObjects.cpp | 148 + ...xtensionAutoGeneratedMetaDataObjectNames.h | 162 + .../PxExtensionAutoGeneratedMetaDataObjects.h | 1229 +++ .../include/PxExtensionMetaDataObjects.h | 72 + ...xExtensionAutoGeneratedMetaDataObjects.cpp | 488 ++ .../physxvehicle/src/PxVehicleComponents.cpp | 222 + .../physxvehicle/src/PxVehicleDefaults.h | 46 + .../physxvehicle/src/PxVehicleDrive.cpp | 213 + .../physxvehicle/src/PxVehicleDrive4W.cpp | 152 + .../physxvehicle/src/PxVehicleDriveNW.cpp | 149 + .../physxvehicle/src/PxVehicleDriveTank.cpp | 120 + .../physxvehicle/src/PxVehicleLinearMath.h | 433 + .../physxvehicle/src/PxVehicleMetaData.cpp | 541 ++ .../physxvehicle/src/PxVehicleNoDrive.cpp | 157 + .../source/physxvehicle/src/PxVehicleSDK.cpp | 115 + .../src/PxVehicleSerialization.cpp | 203 + .../physxvehicle/src/PxVehicleSerialization.h | 70 + .../src/PxVehicleSuspLimitConstraintShader.h | 317 + .../src/PxVehicleSuspWheelTire4.cpp | 191 + .../src/PxVehicleSuspWheelTire4.h | 393 + .../src/PxVehicleTireFriction.cpp | 132 + .../physxvehicle/src/PxVehicleUpdate.cpp | 7652 +++++++++++++++++ .../physxvehicle/src/PxVehicleWheels.cpp | 857 ++ .../physxvehicle/src/VehicleUtilControl.cpp | 474 + .../physxvehicle/src/VehicleUtilSetup.cpp | 246 + .../physxvehicle/src/VehicleUtilTelemetry.cpp | 577 ++ ...xVehicleAutoGeneratedMetaDataObjectNames.h | 231 + .../PxVehicleAutoGeneratedMetaDataObjects.h | 1797 ++++ .../include/PxVehicleMetaDataObjects.h | 97 + .../PxVehicleAutoGeneratedMetaDataObjects.cpp | 727 ++ .../src/PxVehicleMetaDataObjects.cpp | 86 + src/PhysX/physx/source/pvd/include/PsPvd.h | 83 + .../pvd/include/PxProfileAllocatorWrapper.h | 231 + .../physx/source/pvd/include/PxPvdClient.h | 77 + .../source/pvd/include/PxPvdDataStream.h | 272 + .../pvd/include/PxPvdDataStreamHelpers.h | 120 + .../source/pvd/include/PxPvdErrorCodes.h | 62 + .../pvd/include/PxPvdObjectModelBaseTypes.h | 428 + .../source/pvd/include/PxPvdRenderBuffer.h | 140 + .../source/pvd/include/PxPvdUserRenderer.h | 107 + .../source/pvd/src/PxProfileContextProvider.h | 58 + .../pvd/src/PxProfileContextProviderImpl.h | 52 + .../source/pvd/src/PxProfileDataBuffer.h | 167 + .../source/pvd/src/PxProfileDataParsing.h | 218 + .../source/pvd/src/PxProfileEventBuffer.h | 267 + .../pvd/src/PxProfileEventBufferAtomic.h | 318 + .../pvd/src/PxProfileEventBufferClient.h | 80 + .../src/PxProfileEventBufferClientManager.h | 94 + .../physx/source/pvd/src/PxProfileEventId.h | 64 + .../source/pvd/src/PxProfileEventImpl.cpp | 63 + .../source/pvd/src/PxProfileEventMutex.h | 63 + .../source/pvd/src/PxProfileEventNames.h | 89 + .../source/pvd/src/PxProfileEventSender.h | 111 + .../pvd/src/PxProfileEventSerialization.h | 257 + .../physx/source/pvd/src/PxProfileEvents.h | 705 ++ .../physx/source/pvd/src/PxProfileMemory.h | 92 + .../source/pvd/src/PxProfileMemoryBuffer.h | 192 + .../pvd/src/PxProfileMemoryEventBuffer.h | 157 + .../source/pvd/src/PxProfileMemoryEvents.h | 411 + .../source/pvd/src/PxProfileScopedEvent.h | 107 + .../source/pvd/src/PxProfileScopedMutexLock.h | 64 + .../physx/source/pvd/src/PxProfileZoneImpl.h | 315 + .../source/pvd/src/PxProfileZoneManager.h | 155 + .../source/pvd/src/PxProfileZoneManagerImpl.h | 173 + src/PhysX/physx/source/pvd/src/PxPvd.cpp | 46 + src/PhysX/physx/source/pvd/src/PxPvdBits.h | 132 + .../physx/source/pvd/src/PxPvdByteStreams.h | 126 + .../source/pvd/src/PxPvdCommStreamEventSink.h | 55 + .../source/pvd/src/PxPvdCommStreamEvents.h | 987 +++ .../source/pvd/src/PxPvdCommStreamTypes.h | 229 + .../physx/source/pvd/src/PxPvdDataStream.cpp | 864 ++ .../pvd/src/PxPvdDefaultFileTransport.cpp | 218 + .../pvd/src/PxPvdDefaultFileTransport.h | 77 + .../pvd/src/PxPvdDefaultSocketTransport.cpp | 134 + .../pvd/src/PxPvdDefaultSocketTransport.h | 79 + .../physx/source/pvd/src/PxPvdFoundation.h | 318 + src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp | 399 + src/PhysX/physx/source/pvd/src/PxPvdImpl.h | 221 + .../source/pvd/src/PxPvdInternalByteStreams.h | 99 + .../physx/source/pvd/src/PxPvdMarshalling.h | 220 + .../physx/source/pvd/src/PxPvdMemClient.cpp | 130 + .../physx/source/pvd/src/PxPvdMemClient.h | 85 + .../src/PxPvdObjectModelInternalTypeDefs.h | 32 + .../pvd/src/PxPvdObjectModelInternalTypes.h | 154 + .../pvd/src/PxPvdObjectModelMetaData.cpp | 1503 ++++ .../source/pvd/src/PxPvdObjectModelMetaData.h | 481 ++ .../source/pvd/src/PxPvdObjectRegistrar.cpp | 80 + .../source/pvd/src/PxPvdObjectRegistrar.h | 71 + .../physx/source/pvd/src/PxPvdProfileZone.h | 142 + .../source/pvd/src/PxPvdProfileZoneClient.cpp | 171 + .../source/pvd/src/PxPvdProfileZoneClient.h | 77 + .../source/pvd/src/PxPvdUserRenderImpl.h | 385 + .../source/pvd/src/PxPvdUserRenderTypes.h | 46 + .../source/pvd/src/PxPvdUserRenderer.cpp | 405 + .../source/scenequery/include/SqPruner.h | 397 + .../scenequery/include/SqPrunerMergeData.h | 62 + .../scenequery/include/SqPruningStructure.h | 110 + .../scenequery/include/SqSceneQueryManager.h | 221 + .../source/scenequery/src/SqAABBPruner.cpp | 848 ++ .../source/scenequery/src/SqAABBPruner.h | 269 + .../source/scenequery/src/SqAABBTree.cpp | 920 ++ .../physx/source/scenequery/src/SqAABBTree.h | 259 + .../scenequery/src/SqAABBTreeUpdateMap.cpp | 197 + .../scenequery/src/SqAABBTreeUpdateMap.h | 82 + .../physx/source/scenequery/src/SqBounds.cpp | 75 + .../physx/source/scenequery/src/SqBounds.h | 78 + .../source/scenequery/src/SqBucketPruner.cpp | 2599 ++++++ .../source/scenequery/src/SqBucketPruner.h | 279 + .../scenequery/src/SqCompoundPruner.cpp | 598 ++ .../source/scenequery/src/SqCompoundPruner.h | 98 + .../scenequery/src/SqCompoundPruningPool.cpp | 279 + .../scenequery/src/SqCompoundPruningPool.h | 108 + .../scenequery/src/SqExtendedBucketPruner.cpp | 920 ++ .../scenequery/src/SqExtendedBucketPruner.h | 198 + .../src/SqIncrementalAABBPruner.cpp | 473 + .../scenequery/src/SqIncrementalAABBPruner.h | 99 + .../src/SqIncrementalAABBPrunerCore.cpp | 435 + .../src/SqIncrementalAABBPrunerCore.h | 115 + .../scenequery/src/SqIncrementalAABBTree.cpp | 1077 +++ .../scenequery/src/SqIncrementalAABBTree.h | 215 + .../source/scenequery/src/SqMetaData.cpp | 57 + .../source/scenequery/src/SqPruningPool.cpp | 182 + .../source/scenequery/src/SqPruningPool.h | 120 + .../scenequery/src/SqPruningStructure.cpp | 429 + .../scenequery/src/SqSceneQueryManager.cpp | 604 ++ .../physx/source/scenequery/src/SqTypedef.h | 46 + .../include/ScActorCore.h | 131 + .../include/ScArticulationCore.h | 227 + .../include/ScArticulationJointCore.h | 226 + .../simulationcontroller/include/ScBodyCore.h | 213 + .../include/ScConstraintCore.h | 134 + .../include/ScIterators.h | 104 + .../include/ScMaterialCore.h | 70 + .../simulationcontroller/include/ScPhysics.h | 115 + .../include/ScRigidCore.h | 94 + .../simulationcontroller/include/ScScene.h | 993 +++ .../include/ScShapeCore.h | 137 + .../include/ScStaticCore.h | 78 + .../simulationcontroller/src/ScActorCore.cpp | 87 + .../simulationcontroller/src/ScActorPair.h | 217 + .../simulationcontroller/src/ScActorSim.cpp | 144 + .../simulationcontroller/src/ScActorSim.h | 124 + .../src/ScArticulationCore.cpp | 384 + .../src/ScArticulationJointCore.cpp | 300 + .../src/ScArticulationJointSim.cpp | 92 + .../src/ScArticulationJointSim.h | 71 + .../src/ScArticulationSim.cpp | 813 ++ .../src/ScArticulationSim.h | 213 + .../simulationcontroller/src/ScBodyCore.cpp | 717 ++ .../simulationcontroller/src/ScBodySim.cpp | 973 +++ .../simulationcontroller/src/ScBodySim.h | 333 + .../simulationcontroller/src/ScClient.h | 53 + .../src/ScConstraintCore.cpp | 149 + .../src/ScConstraintGroupNode.cpp | 138 + .../src/ScConstraintGroupNode.h | 178 + .../src/ScConstraintInteraction.cpp | 169 + .../src/ScConstraintInteraction.h | 66 + .../src/ScConstraintProjectionManager.cpp | 505 ++ .../src/ScConstraintProjectionManager.h | 84 + .../src/ScConstraintProjectionTree.cpp | 567 ++ .../src/ScConstraintProjectionTree.h | 75 + .../src/ScConstraintSim.cpp | 490 ++ .../src/ScConstraintSim.h | 156 + .../src/ScContactReportBuffer.h | 174 + .../src/ScContactStream.h | 413 + .../src/ScElementInteractionMarker.cpp | 45 + .../src/ScElementInteractionMarker.h | 68 + .../simulationcontroller/src/ScElementSim.cpp | 133 + .../simulationcontroller/src/ScElementSim.h | 130 + .../src/ScElementSimInteraction.h | 77 + .../src/ScInteraction.cpp | 71 + .../simulationcontroller/src/ScInteraction.h | 210 + .../src/ScInteractionFlags.h | 74 + .../simulationcontroller/src/ScIterators.cpp | 105 + .../simulationcontroller/src/ScMetaData.cpp | 424 + .../simulationcontroller/src/ScNPhaseCore.cpp | 2054 +++++ .../simulationcontroller/src/ScNPhaseCore.h | 390 + .../src/ScObjectIDTracker.h | 99 + .../simulationcontroller/src/ScPhysics.cpp | 61 + .../simulationcontroller/src/ScRigidCore.cpp | 125 + .../simulationcontroller/src/ScRigidSim.cpp | 88 + .../simulationcontroller/src/ScRigidSim.h | 63 + .../simulationcontroller/src/ScScene.cpp | 6223 ++++++++++++++ .../simulationcontroller/src/ScShapeCore.cpp | 372 + .../src/ScShapeInteraction.cpp | 1208 +++ .../src/ScShapeInteraction.h | 412 + .../simulationcontroller/src/ScShapeSim.cpp | 494 ++ .../simulationcontroller/src/ScShapeSim.h | 151 + .../simulationcontroller/src/ScSimStateData.h | 139 + .../simulationcontroller/src/ScSimStats.cpp | 132 + .../simulationcontroller/src/ScSimStats.h | 89 + .../src/ScSimulationController.cpp | 43 + .../src/ScSimulationController.h | 92 + .../src/ScSqBoundsManager.cpp | 122 + .../src/ScSqBoundsManager.h | 73 + .../simulationcontroller/src/ScStaticCore.cpp | 49 + .../simulationcontroller/src/ScStaticSim.h | 57 + .../src/ScTriggerInteraction.cpp | 135 + .../src/ScTriggerInteraction.h | 122 + .../simulationcontroller/src/ScTriggerPairs.h | 95 + .../physx/source/task/src/TaskManager.cpp | 538 ++ .../binarymetadata/android/android.metaData | Bin 0 -> 44148 bytes .../tools/binarymetadata/ios/ios64.metaData | Bin 0 -> 52514 bytes .../binarymetadata/linux/linux64.metaData | Bin 0 -> 52514 bytes .../tools/binarymetadata/mac/mac64.metaData | Bin 0 -> 52514 bytes .../binarymetadata/windows/win32.metaData | Bin 0 -> 44148 bytes .../binarymetadata/windows/win64.metaData | Bin 0 -> 52514 bytes .../physxmetadatagenerator/PxBoilerPlate.h | 26 + .../PxExtensionsCommon.h | 80 + .../PxPhysicsWithExtensionsAPI.h | 186 + .../PxVehicleExtensionAPI.h | 91 + .../generateMetaData.bat | 132 + .../generateMetaData.py | 251 + .../generateMetaData.sh | 47 + .../tools/physxmetadatagenerator/helper.sh | 37 + .../physxmetadatagenerator/lib/__init__.py | 0 .../physxmetadatagenerator/lib/compare.py | 120 + .../tools/physxmetadatagenerator/lib/utils.py | 104 + .../tools/physxmetadatagenerator/readme.txt | 54 + src/PhysX/premake4.lua | 128 + src/PhysX/pxshared/include/foundation/Px.h | 92 + .../include/foundation/PxAllocatorCallback.h | 95 + .../pxshared/include/foundation/PxAssert.h | 95 + .../include/foundation/PxBitAndData.h | 87 + .../pxshared/include/foundation/PxBounds3.h | 480 ++ .../include/foundation/PxErrorCallback.h | 73 + .../pxshared/include/foundation/PxErrors.h | 93 + .../pxshared/include/foundation/PxFlags.h | 376 + src/PhysX/pxshared/include/foundation/PxIO.h | 138 + .../include/foundation/PxIntrinsics.h | 47 + .../pxshared/include/foundation/PxMat33.h | 396 + .../pxshared/include/foundation/PxMat44.h | 376 + .../pxshared/include/foundation/PxMath.h | 338 + .../pxshared/include/foundation/PxMathUtils.h | 73 + .../pxshared/include/foundation/PxMemory.h | 110 + .../pxshared/include/foundation/PxPlane.h | 145 + .../include/foundation/PxPreprocessor.h | 553 ++ .../pxshared/include/foundation/PxProfiler.h | 99 + .../pxshared/include/foundation/PxQuat.h | 403 + .../include/foundation/PxSimpleTypes.h | 112 + .../include/foundation/PxStrideIterator.h | 353 + .../pxshared/include/foundation/PxTransform.h | 215 + .../pxshared/include/foundation/PxUnionCast.h | 69 + .../pxshared/include/foundation/PxVec2.h | 347 + .../pxshared/include/foundation/PxVec3.h | 394 + .../pxshared/include/foundation/PxVec4.h | 376 + .../foundation/unix/PxUnixIntrinsics.h | 185 + .../foundation/windows/PxWindowsIntrinsics.h | 188 + src/PhysXCharacterAll.cpp | 10 + src/PhysXCommonAll.cpp | 8 + src/PhysXCookingAll.cpp | 18 + src/PhysXExtensionAll.cpp | 34 + src/PhysXFoundationAll.cpp | 7 + src/PhysXFoundationUnix.cpp | 10 + src/PhysXFoundationWindows.cpp | 11 + src/PhysXGeomUtilsAll.cpp | 68 + src/PhysXLowLevelAll.cpp | 48 + src/PhysXNpSrcAll.cpp | 34 + src/PhysXPvdAll.cpp | 0 src/PhysXSceneQueryAll.cpp | 15 + src/PhysXSimulationControllerAll.cpp | 31 + src/PhysXVehicleAll.cpp | 18 + 1363 files changed, 413562 insertions(+), 484 deletions(-) create mode 100644 src/PhysX/physx/include/PxActor.h create mode 100644 src/PhysX/physx/include/PxAggregate.h create mode 100644 src/PhysX/physx/include/PxArticulation.h create mode 100644 src/PhysX/physx/include/PxArticulationBase.h create mode 100644 src/PhysX/physx/include/PxArticulationJoint.h create mode 100644 src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h create mode 100644 src/PhysX/physx/include/PxArticulationLink.h create mode 100644 src/PhysX/physx/include/PxArticulationReducedCoordinate.h create mode 100644 src/PhysX/physx/include/PxBatchQuery.h create mode 100644 src/PhysX/physx/include/PxBatchQueryDesc.h create mode 100644 src/PhysX/physx/include/PxBroadPhase.h create mode 100644 src/PhysX/physx/include/PxClient.h create mode 100644 src/PhysX/physx/include/PxConstraint.h create mode 100644 src/PhysX/physx/include/PxConstraintDesc.h create mode 100644 src/PhysX/physx/include/PxContact.h create mode 100644 src/PhysX/physx/include/PxContactModifyCallback.h create mode 100644 src/PhysX/physx/include/PxDeletionListener.h create mode 100644 src/PhysX/physx/include/PxFiltering.h create mode 100644 src/PhysX/physx/include/PxForceMode.h create mode 100644 src/PhysX/physx/include/PxFoundation.h create mode 100644 src/PhysX/physx/include/PxImmediateMode.h create mode 100644 src/PhysX/physx/include/PxLockedData.h create mode 100644 src/PhysX/physx/include/PxMaterial.h create mode 100644 src/PhysX/physx/include/PxPhysXConfig.h create mode 100644 src/PhysX/physx/include/PxPhysics.h create mode 100644 src/PhysX/physx/include/PxPhysicsAPI.h create mode 100644 src/PhysX/physx/include/PxPhysicsSerialization.h create mode 100644 src/PhysX/physx/include/PxPhysicsVersion.h create mode 100644 src/PhysX/physx/include/PxPruningStructure.h create mode 100644 src/PhysX/physx/include/PxQueryFiltering.h create mode 100644 src/PhysX/physx/include/PxQueryReport.h create mode 100644 src/PhysX/physx/include/PxRigidActor.h create mode 100644 src/PhysX/physx/include/PxRigidBody.h create mode 100644 src/PhysX/physx/include/PxRigidDynamic.h create mode 100644 src/PhysX/physx/include/PxRigidStatic.h create mode 100644 src/PhysX/physx/include/PxScene.h create mode 100644 src/PhysX/physx/include/PxSceneDesc.h create mode 100644 src/PhysX/physx/include/PxSceneLock.h create mode 100644 src/PhysX/physx/include/PxShape.h create mode 100644 src/PhysX/physx/include/PxSimulationEventCallback.h create mode 100644 src/PhysX/physx/include/PxSimulationStatistics.h create mode 100644 src/PhysX/physx/include/PxVisualizationParameter.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxBoxController.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxCapsuleController.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxCharacter.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxController.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxControllerManager.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h create mode 100644 src/PhysX/physx/include/characterkinematic/PxExtended.h create mode 100644 src/PhysX/physx/include/collision/PxCollisionDefs.h create mode 100644 src/PhysX/physx/include/common/PxBase.h create mode 100644 src/PhysX/physx/include/common/PxCollection.h create mode 100644 src/PhysX/physx/include/common/PxCoreUtilityTypes.h create mode 100644 src/PhysX/physx/include/common/PxMetaData.h create mode 100644 src/PhysX/physx/include/common/PxMetaDataFlags.h create mode 100644 src/PhysX/physx/include/common/PxPhysXCommonConfig.h create mode 100644 src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h create mode 100644 src/PhysX/physx/include/common/PxProfileZone.h create mode 100644 src/PhysX/physx/include/common/PxRenderBuffer.h create mode 100644 src/PhysX/physx/include/common/PxSerialFramework.h create mode 100644 src/PhysX/physx/include/common/PxSerializer.h create mode 100644 src/PhysX/physx/include/common/PxStringTable.h create mode 100644 src/PhysX/physx/include/common/PxTolerancesScale.h create mode 100644 src/PhysX/physx/include/common/PxTypeInfo.h create mode 100644 src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h create mode 100644 src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h create mode 100644 src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h create mode 100644 src/PhysX/physx/include/cooking/PxBVHStructureDesc.h create mode 100644 src/PhysX/physx/include/cooking/PxConvexMeshDesc.h create mode 100644 src/PhysX/physx/include/cooking/PxCooking.h create mode 100644 src/PhysX/physx/include/cooking/PxMidphaseDesc.h create mode 100644 src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h create mode 100644 src/PhysX/physx/include/cooking/Pxc.h create mode 100644 src/PhysX/physx/include/cudamanager/PxCudaContextManager.h create mode 100644 src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h create mode 100644 src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h create mode 100644 src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h create mode 100644 src/PhysX/physx/include/extensions/PxBinaryConverter.h create mode 100644 src/PhysX/physx/include/extensions/PxBroadPhaseExt.h create mode 100644 src/PhysX/physx/include/extensions/PxCollectionExt.h create mode 100644 src/PhysX/physx/include/extensions/PxConstraintExt.h create mode 100644 src/PhysX/physx/include/extensions/PxContactJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxConvexMeshExt.h create mode 100644 src/PhysX/physx/include/extensions/PxD6Joint.h create mode 100644 src/PhysX/physx/include/extensions/PxD6JointCreate.h create mode 100644 src/PhysX/physx/include/extensions/PxDefaultAllocator.h create mode 100644 src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h create mode 100644 src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h create mode 100644 src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h create mode 100644 src/PhysX/physx/include/extensions/PxDefaultStreams.h create mode 100644 src/PhysX/physx/include/extensions/PxDistanceJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxExtensionsAPI.h create mode 100644 src/PhysX/physx/include/extensions/PxFixedJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxJointLimit.h create mode 100644 src/PhysX/physx/include/extensions/PxMassProperties.h create mode 100644 src/PhysX/physx/include/extensions/PxPrismaticJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxRaycastCCD.h create mode 100644 src/PhysX/physx/include/extensions/PxRepXSerializer.h create mode 100644 src/PhysX/physx/include/extensions/PxRepXSimpleType.h create mode 100644 src/PhysX/physx/include/extensions/PxRevoluteJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxRigidActorExt.h create mode 100644 src/PhysX/physx/include/extensions/PxRigidBodyExt.h create mode 100644 src/PhysX/physx/include/extensions/PxSceneQueryExt.h create mode 100644 src/PhysX/physx/include/extensions/PxSerialization.h create mode 100644 src/PhysX/physx/include/extensions/PxShapeExt.h create mode 100644 src/PhysX/physx/include/extensions/PxSimpleFactory.h create mode 100644 src/PhysX/physx/include/extensions/PxSmoothNormals.h create mode 100644 src/PhysX/physx/include/extensions/PxSphericalJoint.h create mode 100644 src/PhysX/physx/include/extensions/PxStringTableExt.h create mode 100644 src/PhysX/physx/include/extensions/PxTriangleMeshExt.h create mode 100644 src/PhysX/physx/include/filebuf/PxFileBuf.h create mode 100644 src/PhysX/physx/include/geometry/PxBVHStructure.h create mode 100644 src/PhysX/physx/include/geometry/PxBoxGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxCapsuleGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxConvexMesh.h create mode 100644 src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxGeometryHelpers.h create mode 100644 src/PhysX/physx/include/geometry/PxGeometryQuery.h create mode 100644 src/PhysX/physx/include/geometry/PxHeightField.h create mode 100644 src/PhysX/physx/include/geometry/PxHeightFieldDesc.h create mode 100644 src/PhysX/physx/include/geometry/PxHeightFieldFlag.h create mode 100644 src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxHeightFieldSample.h create mode 100644 src/PhysX/physx/include/geometry/PxMeshQuery.h create mode 100644 src/PhysX/physx/include/geometry/PxMeshScale.h create mode 100644 src/PhysX/physx/include/geometry/PxPlaneGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h create mode 100644 src/PhysX/physx/include/geometry/PxSphereGeometry.h create mode 100644 src/PhysX/physx/include/geometry/PxTriangle.h create mode 100644 src/PhysX/physx/include/geometry/PxTriangleMesh.h create mode 100644 src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h create mode 100644 src/PhysX/physx/include/geomutils/GuContactBuffer.h create mode 100644 src/PhysX/physx/include/geomutils/GuContactPoint.h create mode 100644 src/PhysX/physx/include/gpu/PxGpu.h create mode 100644 src/PhysX/physx/include/pvd/PxPvd.h create mode 100644 src/PhysX/physx/include/pvd/PxPvdSceneClient.h create mode 100644 src/PhysX/physx/include/pvd/PxPvdTransport.h create mode 100644 src/PhysX/physx/include/solver/PxSolverDefs.h create mode 100644 src/PhysX/physx/include/task/PxCpuDispatcher.h create mode 100644 src/PhysX/physx/include/task/PxGpuDispatcher.h create mode 100644 src/PhysX/physx/include/task/PxGpuTask.h create mode 100644 src/PhysX/physx/include/task/PxTask.h create mode 100644 src/PhysX/physx/include/task/PxTaskDefine.h create mode 100644 src/PhysX/physx/include/task/PxTaskManager.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleComponents.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleDrive.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleSDK.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleShaders.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleUpdate.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleUtil.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleUtilSetup.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleUtilTelemetry.h create mode 100644 src/PhysX/physx/include/vehicle/PxVehicleWheels.h create mode 100644 src/PhysX/physx/platform_readme.html create mode 100644 src/PhysX/physx/release_notes.html create mode 100644 src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h create mode 100644 src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h create mode 100644 src/PhysX/physx/source/common/src/CmBitMap.h create mode 100644 src/PhysX/physx/source/common/src/CmBlockArray.h create mode 100644 src/PhysX/physx/source/common/src/CmBoxPruning.cpp create mode 100644 src/PhysX/physx/source/common/src/CmBoxPruning.h create mode 100644 src/PhysX/physx/source/common/src/CmCollection.cpp create mode 100644 src/PhysX/physx/source/common/src/CmCollection.h create mode 100644 src/PhysX/physx/source/common/src/CmConeLimitHelper.h create mode 100644 src/PhysX/physx/source/common/src/CmFlushPool.h create mode 100644 src/PhysX/physx/source/common/src/CmIDPool.h create mode 100644 src/PhysX/physx/source/common/src/CmIO.h create mode 100644 src/PhysX/physx/source/common/src/CmMathUtils.cpp create mode 100644 src/PhysX/physx/source/common/src/CmMatrix34.h create mode 100644 src/PhysX/physx/source/common/src/CmPhysXCommon.h create mode 100644 src/PhysX/physx/source/common/src/CmPool.h create mode 100644 src/PhysX/physx/source/common/src/CmPreallocatingPool.h create mode 100644 src/PhysX/physx/source/common/src/CmPriorityQueue.h create mode 100644 src/PhysX/physx/source/common/src/CmPtrTable.cpp create mode 100644 src/PhysX/physx/source/common/src/CmPtrTable.h create mode 100644 src/PhysX/physx/source/common/src/CmQueue.h create mode 100644 src/PhysX/physx/source/common/src/CmRadixSort.cpp create mode 100644 src/PhysX/physx/source/common/src/CmRadixSort.h create mode 100644 src/PhysX/physx/source/common/src/CmRadixSortBuffered.cpp create mode 100644 src/PhysX/physx/source/common/src/CmRadixSortBuffered.h create mode 100644 src/PhysX/physx/source/common/src/CmRefCountable.h create mode 100644 src/PhysX/physx/source/common/src/CmRenderBuffer.h create mode 100644 src/PhysX/physx/source/common/src/CmRenderOutput.cpp create mode 100644 src/PhysX/physx/source/common/src/CmRenderOutput.h create mode 100644 src/PhysX/physx/source/common/src/CmScaling.h create mode 100644 src/PhysX/physx/source/common/src/CmSpatialVector.h create mode 100644 src/PhysX/physx/source/common/src/CmTask.h create mode 100644 src/PhysX/physx/source/common/src/CmTaskPool.h create mode 100644 src/PhysX/physx/source/common/src/CmTmpMem.h create mode 100644 src/PhysX/physx/source/common/src/CmTransformUtils.h create mode 100644 src/PhysX/physx/source/common/src/CmUtils.h create mode 100644 src/PhysX/physx/source/common/src/CmVisualization.cpp create mode 100644 src/PhysX/physx/source/common/src/CmVisualization.h create mode 100644 src/PhysX/physx/source/common/src/windows/CmWindowsDelayLoadHook.cpp create mode 100644 src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp create mode 100644 src/PhysX/physx/source/compiler/cmake/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/IfTemplate.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake create mode 100644 src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake create mode 100644 src/PhysX/physx/source/compiler/resource_x64/PhysX.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x64/PhysXFoundation.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x64/resource.h create mode 100644 src/PhysX/physx/source/compiler/resource_x86/PhysX.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x86/PhysXCommon.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x86/PhysXCooking.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x86/PhysXFoundation.rc create mode 100644 src/PhysX/physx/source/compiler/resource_x86/resource.h create mode 100644 src/PhysX/physx/source/fastxml/include/PsFastXml.h create mode 100644 src/PhysX/physx/source/fastxml/src/PsFastXml.cpp create mode 100644 src/PhysX/physx/source/filebuf/include/PsAsciiConversion.h create mode 100644 src/PhysX/physx/source/filebuf/include/PsAsciiConversion.inl create mode 100644 src/PhysX/physx/source/filebuf/include/PsFileBuffer.h create mode 100644 src/PhysX/physx/source/filebuf/include/PsIOStream.h create mode 100644 src/PhysX/physx/source/filebuf/include/PsIOStream.inl create mode 100644 src/PhysX/physx/source/filebuf/include/PsMemoryBuffer.h create mode 100644 src/PhysX/physx/source/foundation/include/Ps.h create mode 100644 src/PhysX/physx/source/foundation/include/PsAlignedMalloc.h create mode 100644 src/PhysX/physx/source/foundation/include/PsAlloca.h create mode 100644 src/PhysX/physx/source/foundation/include/PsAllocator.h create mode 100644 src/PhysX/physx/source/foundation/include/PsAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/PsArray.h create mode 100644 src/PhysX/physx/source/foundation/include/PsAtomic.h create mode 100644 src/PhysX/physx/source/foundation/include/PsBasicTemplates.h create mode 100644 src/PhysX/physx/source/foundation/include/PsBitUtils.h create mode 100644 src/PhysX/physx/source/foundation/include/PsBroadcast.h create mode 100644 src/PhysX/physx/source/foundation/include/PsCpu.h create mode 100644 src/PhysX/physx/source/foundation/include/PsFPU.h create mode 100644 src/PhysX/physx/source/foundation/include/PsFoundation.h create mode 100644 src/PhysX/physx/source/foundation/include/PsHash.h create mode 100644 src/PhysX/physx/source/foundation/include/PsHashInternals.h create mode 100644 src/PhysX/physx/source/foundation/include/PsHashMap.h create mode 100644 src/PhysX/physx/source/foundation/include/PsHashSet.h create mode 100644 src/PhysX/physx/source/foundation/include/PsInlineAllocator.h create mode 100644 src/PhysX/physx/source/foundation/include/PsInlineAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/PsInlineArray.h create mode 100644 src/PhysX/physx/source/foundation/include/PsIntrinsics.h create mode 100644 src/PhysX/physx/source/foundation/include/PsMathUtils.h create mode 100644 src/PhysX/physx/source/foundation/include/PsMutex.h create mode 100644 src/PhysX/physx/source/foundation/include/PsPool.h create mode 100644 src/PhysX/physx/source/foundation/include/PsSList.h create mode 100644 src/PhysX/physx/source/foundation/include/PsSocket.h create mode 100644 src/PhysX/physx/source/foundation/include/PsSort.h create mode 100644 src/PhysX/physx/source/foundation/include/PsSortInternals.h create mode 100644 src/PhysX/physx/source/foundation/include/PsString.h create mode 100644 src/PhysX/physx/source/foundation/include/PsSync.h create mode 100644 src/PhysX/physx/source/foundation/include/PsTempAllocator.h create mode 100644 src/PhysX/physx/source/foundation/include/PsThread.h create mode 100644 src/PhysX/physx/source/foundation/include/PsTime.h create mode 100644 src/PhysX/physx/source/foundation/include/PsUserAllocated.h create mode 100644 src/PhysX/physx/source/foundation/include/PsUtilities.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecMath.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecMathSSE.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecQuat.h create mode 100644 src/PhysX/physx/source/foundation/include/PsVecTransform.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h create mode 100644 src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h create mode 100644 src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h create mode 100644 src/PhysX/physx/source/foundation/src/PsAllocator.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsAssert.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsFoundation.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsMathUtils.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsString.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp create mode 100644 src/PhysX/physx/source/foundation/src/PsUtilities.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp create mode 100644 src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp create mode 100644 src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp create mode 100644 src/PhysX/physx/source/geomutils/include/GuAxes.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuBox.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuRaycastTests.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h create mode 100644 src/PhysX/physx/source/geomutils/include/GuSegment.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuBVHStructure.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuBounds.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuBounds.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuCapsule.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuCenterExtents.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuGeometryQuery.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuInternal.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuInternal.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuMTD.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuMTD.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuMeshFactory.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuMetaData.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuOverlapTests.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuSerialize.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuSerialize.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuSphere.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepMTD.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/GuSweepTests.h create mode 100644 src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h create mode 100644 src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.h create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactBoxBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecCapsule.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHullNoScale.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h create mode 100644 src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuEntityReport.h create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBoxSIMD.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Raycast.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h create mode 100644 src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp create mode 100644 src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h create mode 100644 src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvManager.h create mode 100644 src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h create mode 100644 src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcCCDStateStreamPair.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h create mode 100644 src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h create mode 100644 src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactMethodImpl.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialHeightField.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMesh.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp create mode 100644 src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsContext.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h create mode 100644 src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsCCD.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp create mode 100644 src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/include/BpAABBManagerTasks.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhase.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp create mode 100644 src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyContext.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetupBlock.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverContact.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverContact4.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF4.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverContext.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h create mode 100644 src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpActor.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpActor.h create mode 100644 src/PhysX/physx/source/physx/src/NpActorTemplate.h create mode 100644 src/PhysX/physx/source/physx/src/NpAggregate.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpAggregate.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulation.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpArticulation.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationJoint.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationLink.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationLink.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h create mode 100644 src/PhysX/physx/source/physx/src/NpArticulationTemplate.h create mode 100644 src/PhysX/physx/source/physx/src/NpBatchQuery.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpBatchQuery.h create mode 100644 src/PhysX/physx/source/physx/src/NpCast.h create mode 100644 src/PhysX/physx/source/physx/src/NpConnector.h create mode 100644 src/PhysX/physx/source/physx/src/NpConstraint.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpConstraint.h create mode 100644 src/PhysX/physx/source/physx/src/NpFactory.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpFactory.h create mode 100644 src/PhysX/physx/source/physx/src/NpMaterial.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpMaterial.h create mode 100644 src/PhysX/physx/source/physx/src/NpMaterialManager.h create mode 100644 src/PhysX/physx/source/physx/src/NpMetaData.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpPhysics.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpPhysics.h create mode 100644 src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h create mode 100644 src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h create mode 100644 src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h create mode 100644 src/PhysX/physx/source/physx/src/NpQueryShared.h create mode 100644 src/PhysX/physx/source/physx/src/NpReadCheck.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpReadCheck.h create mode 100644 src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h create mode 100644 src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h create mode 100644 src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h create mode 100644 src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpRigidDynamic.h create mode 100644 src/PhysX/physx/source/physx/src/NpRigidStatic.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpRigidStatic.h create mode 100644 src/PhysX/physx/source/physx/src/NpScene.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpScene.h create mode 100644 src/PhysX/physx/source/physx/src/NpSceneAccessor.h create mode 100644 src/PhysX/physx/source/physx/src/NpSceneQueries.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpSceneQueries.h create mode 100644 src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpShape.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpShape.h create mode 100644 src/PhysX/physx/source/physx/src/NpShapeManager.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpShapeManager.h create mode 100644 src/PhysX/physx/source/physx/src/NpWriteCheck.cpp create mode 100644 src/PhysX/physx/source/physx/src/NpWriteCheck.h create mode 100644 src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h create mode 100644 src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp create mode 100644 src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h create mode 100644 src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp create mode 100644 src/PhysX/physx/source/physx/src/PvdPhysicsClient.h create mode 100644 src/PhysX/physx/source/physx/src/PvdTypeNames.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbActor.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbBase.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbBody.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbDefs.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbScene.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbShape.h create mode 100644 src/PhysX/physx/source/physx/src/buffering/ScbType.h create mode 100644 src/PhysX/physx/source/physx/src/device/PhysXIndicator.h create mode 100644 src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp create mode 100644 src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h create mode 100644 src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp create mode 100644 src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp create mode 100644 src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp create mode 100644 src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h create mode 100644 src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h create mode 100644 src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/Adjacencies.h create mode 100644 src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/Cooking.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/Cooking.h create mode 100644 src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/CookingUtils.h create mode 100644 src/PhysX/physx/source/physxcooking/src/EdgeList.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/EdgeList.h create mode 100644 src/PhysX/physx/source/physxcooking/src/MeshCleaner.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/MeshCleaner.h create mode 100644 src/PhysX/physx/source/physxcooking/src/Quantizer.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/Quantizer.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp create mode 100644 src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h create mode 100644 src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtCollection.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPlatform.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPvd.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSerialization.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h create mode 100644 src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Common.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Convert.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/SnSerialization.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnSimpleXmlWriter.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h create mode 100644 src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h create mode 100644 src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h create mode 100644 src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp create mode 100644 src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp create mode 100644 src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h create mode 100644 src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive4W.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleUpdate.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjectNames.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h create mode 100644 src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp create mode 100644 src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp create mode 100644 src/PhysX/physx/source/pvd/include/PsPvd.h create mode 100644 src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdClient.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdDataStream.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h create mode 100644 src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventId.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventNames.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventSender.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileEvents.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileMemory.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h create mode 100644 src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvd.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdBits.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdFoundation.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdImpl.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdMemClient.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h create mode 100644 src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp create mode 100644 src/PhysX/physx/source/scenequery/include/SqPruner.h create mode 100644 src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h create mode 100644 src/PhysX/physx/source/scenequery/include/SqPruningStructure.h create mode 100644 src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBPruner.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBTree.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqBounds.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqBounds.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqBucketPruner.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqMetaData.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqPruningPool.h create mode 100644 src/PhysX/physx/source/scenequery/src/SqPruningStructure.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp create mode 100644 src/PhysX/physx/source/scenequery/src/SqTypedef.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScIterators.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScScene.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScClient.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h create mode 100644 src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h create mode 100644 src/PhysX/physx/source/task/src/TaskManager.cpp create mode 100644 src/PhysX/physx/tools/binarymetadata/android/android.metaData create mode 100644 src/PhysX/physx/tools/binarymetadata/ios/ios64.metaData create mode 100644 src/PhysX/physx/tools/binarymetadata/linux/linux64.metaData create mode 100644 src/PhysX/physx/tools/binarymetadata/mac/mac64.metaData create mode 100644 src/PhysX/physx/tools/binarymetadata/windows/win32.metaData create mode 100644 src/PhysX/physx/tools/binarymetadata/windows/win64.metaData create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/PxBoilerPlate.h create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/PxExtensionsCommon.h create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/helper.sh create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/lib/__init__.py create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py create mode 100644 src/PhysX/physx/tools/physxmetadatagenerator/readme.txt create mode 100644 src/PhysX/premake4.lua create mode 100644 src/PhysX/pxshared/include/foundation/Px.h create mode 100644 src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h create mode 100644 src/PhysX/pxshared/include/foundation/PxAssert.h create mode 100644 src/PhysX/pxshared/include/foundation/PxBitAndData.h create mode 100644 src/PhysX/pxshared/include/foundation/PxBounds3.h create mode 100644 src/PhysX/pxshared/include/foundation/PxErrorCallback.h create mode 100644 src/PhysX/pxshared/include/foundation/PxErrors.h create mode 100644 src/PhysX/pxshared/include/foundation/PxFlags.h create mode 100644 src/PhysX/pxshared/include/foundation/PxIO.h create mode 100644 src/PhysX/pxshared/include/foundation/PxIntrinsics.h create mode 100644 src/PhysX/pxshared/include/foundation/PxMat33.h create mode 100644 src/PhysX/pxshared/include/foundation/PxMat44.h create mode 100644 src/PhysX/pxshared/include/foundation/PxMath.h create mode 100644 src/PhysX/pxshared/include/foundation/PxMathUtils.h create mode 100644 src/PhysX/pxshared/include/foundation/PxMemory.h create mode 100644 src/PhysX/pxshared/include/foundation/PxPlane.h create mode 100644 src/PhysX/pxshared/include/foundation/PxPreprocessor.h create mode 100644 src/PhysX/pxshared/include/foundation/PxProfiler.h create mode 100644 src/PhysX/pxshared/include/foundation/PxQuat.h create mode 100644 src/PhysX/pxshared/include/foundation/PxSimpleTypes.h create mode 100644 src/PhysX/pxshared/include/foundation/PxStrideIterator.h create mode 100644 src/PhysX/pxshared/include/foundation/PxTransform.h create mode 100644 src/PhysX/pxshared/include/foundation/PxUnionCast.h create mode 100644 src/PhysX/pxshared/include/foundation/PxVec2.h create mode 100644 src/PhysX/pxshared/include/foundation/PxVec3.h create mode 100644 src/PhysX/pxshared/include/foundation/PxVec4.h create mode 100644 src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h create mode 100644 src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h create mode 100644 src/PhysXCharacterAll.cpp create mode 100644 src/PhysXCommonAll.cpp create mode 100644 src/PhysXCookingAll.cpp create mode 100644 src/PhysXExtensionAll.cpp create mode 100644 src/PhysXFoundationAll.cpp create mode 100644 src/PhysXFoundationUnix.cpp create mode 100644 src/PhysXFoundationWindows.cpp create mode 100644 src/PhysXGeomUtilsAll.cpp create mode 100644 src/PhysXLowLevelAll.cpp create mode 100644 src/PhysXNpSrcAll.cpp create mode 100644 src/PhysXPvdAll.cpp create mode 100644 src/PhysXSceneQueryAll.cpp create mode 100644 src/PhysXSimulationControllerAll.cpp create mode 100644 src/PhysXVehicleAll.cpp diff --git a/setup.py b/setup.py index b630004f9..fe39a57a3 100644 --- a/setup.py +++ b/setup.py @@ -1,484 +1,676 @@ - -from setuptools import find_packages -from sys import platform as _platform -import sys -import glob -import os - - -from distutils.core import setup -from distutils.extension import Extension -from distutils.util import get_platform -from glob import glob - -# monkey-patch for parallel compilation -import multiprocessing -import multiprocessing.pool - - -def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): - # those lines are copied from distutils.ccompiler.CCompiler directly - macros, objects, extra_postargs, pp_opts, build = self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) - cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - # parallel code - N = 2*multiprocessing.cpu_count()# number of parallel compilations - def _single_compile(obj): - try: src, ext = build[obj] - except KeyError: return - newcc_args = cc_args - if _platform == "darwin": - if src.endswith('.cpp'): - newcc_args = cc_args + ["-stdlib=libc++"] - self._compile(obj, src, ext, newcc_args, extra_postargs, pp_opts) - # convert to list, imap is evaluated on-demand - pool = multiprocessing.pool.ThreadPool(N) - list(pool.imap(_single_compile,objects)) - return objects -import distutils.ccompiler -distutils.ccompiler.CCompiler.compile=parallelCCompile - -#see http://stackoverflow.com/a/8719066/295157 -import os - - -platform = get_platform() -print(platform) - -CXX_FLAGS = '' -CXX_FLAGS += '-DGWEN_COMPILE_STATIC ' -CXX_FLAGS += '-DBT_USE_DOUBLE_PRECISION ' -CXX_FLAGS += '-DBT_ENABLE_ENET ' -CXX_FLAGS += '-DBT_ENABLE_CLSOCKET ' -CXX_FLAGS += '-DB3_DUMP_PYTHON_VERSION ' -CXX_FLAGS += '-DEGL_ADD_PYTHON_INIT ' -CXX_FLAGS += '-DB3_ENABLE_FILEIO_PLUGIN ' -CXX_FLAGS += '-DB3_USE_ZIPFILE_FILEIO ' -CXX_FLAGS += '-DBT_THREADSAFE=1 ' -CXX_FLAGS += '-DSTATIC_LINK_SPD_PLUGIN ' - - -EGL_CXX_FLAGS = '' - - - -# libraries += [current_python] - -libraries = [] -include_dirs = [] - -try: - import numpy - NP_DIRS = [numpy.get_include()] -except: - print("numpy is disabled. getCameraImage maybe slower.") -else: - print("numpy is enabled.") - CXX_FLAGS += '-DPYBULLET_USE_NUMPY ' - for d in NP_DIRS: - print("numpy_include_dirs = %s" % d) - include_dirs += NP_DIRS - -sources = ["examples/pybullet/pybullet.c"]\ -+["src/btLinearMathAll.cpp"]\ -+["src/btBulletCollisionAll.cpp"]\ -+["src/btBulletDynamicsAll.cpp"]\ -+["examples/ExampleBrowser/InProcessExampleBrowser.cpp"]\ -+["examples/TinyRenderer/geometry.cpp"]\ -+["examples/TinyRenderer/model.cpp"]\ -+["examples/TinyRenderer/tgaimage.cpp"]\ -+["examples/TinyRenderer/our_gl.cpp"]\ -+["examples/TinyRenderer/TinyRenderer.cpp"]\ -+["examples/SharedMemory/plugins/pdControlPlugin/pdControlPlugin.cpp"]\ -+["examples/SharedMemory/plugins/collisionFilterPlugin/collisionFilterPlugin.cpp"]\ -+["examples/SharedMemory/plugins/fileIOPlugin/fileIOPlugin.cpp"]\ -+["examples/SharedMemory/b3RobotSimulatorClientAPI_NoDirect.cpp"]\ -+["examples/SharedMemory/IKTrajectoryHelper.cpp"]\ -+["examples/SharedMemory/InProcessMemory.cpp"]\ -+["examples/SharedMemory/PhysicsClient.cpp"]\ -+["examples/SharedMemory/PhysicsServer.cpp"]\ -+["examples/SharedMemory/PhysicsServerExample.cpp"]\ -+["examples/SharedMemory/PhysicsServerExampleBullet2.cpp"]\ -+["examples/SharedMemory/SharedMemoryInProcessPhysicsC_API.cpp"]\ -+["examples/SharedMemory/PhysicsServerSharedMemory.cpp"]\ -+["examples/SharedMemory/PhysicsDirect.cpp"]\ -+["examples/SharedMemory/PhysicsDirectC_API.cpp"]\ -+["examples/SharedMemory/PhysicsServerCommandProcessor.cpp"]\ -+["examples/SharedMemory/PhysicsClientSharedMemory.cpp"]\ -+["examples/SharedMemory/PhysicsClientSharedMemory_C_API.cpp"]\ -+["examples/SharedMemory/PhysicsClientC_API.cpp"]\ -+["examples/SharedMemory/Win32SharedMemory.cpp"]\ -+["examples/SharedMemory/PosixSharedMemory.cpp"]\ -+["examples/SharedMemory/plugins/tinyRendererPlugin/TinyRendererVisualShapeConverter.cpp"]\ -+["examples/SharedMemory/plugins/tinyRendererPlugin/tinyRendererPlugin.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/BulletConversion.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/KinTree.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/MathUtil.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/RBDModel.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/RBDUtil.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/Shape.cpp"]\ -+["examples/SharedMemory/plugins/stablePDPlugin/SpAlg.cpp"]\ -+["examples/SharedMemory/PhysicsClientUDP.cpp"]\ -+["examples/SharedMemory/PhysicsClientUDP_C_API.cpp"]\ -+["examples/SharedMemory/PhysicsClientTCP.cpp"]\ -+["examples/SharedMemory/PhysicsClientTCP_C_API.cpp"]\ -+["examples/SharedMemory/b3PluginManager.cpp"]\ -+["examples/Utils/b3ResourcePath.cpp"]\ -+["examples/Utils/RobotLoggingUtil.cpp"]\ -+["examples/Utils/ChromeTraceUtil.cpp"]\ -+["examples/Utils/b3Clock.cpp"]\ -+["examples/Utils/b3Quickprof.cpp"]\ -+["examples/ThirdPartyLibs/tinyxml2/tinyxml2.cpp"]\ -+["examples/ThirdPartyLibs/Wavefront/tiny_obj_loader.cpp"]\ -+["examples/ThirdPartyLibs/stb_image/stb_image.cpp"]\ -+["examples/ThirdPartyLibs/stb_image/stb_image_write.cpp"]\ -+["examples/ThirdPartyLibs/minizip/ioapi.c"]\ -+["examples/ThirdPartyLibs/minizip/unzip.c"]\ -+["examples/ThirdPartyLibs/minizip/zip.c"]\ -+["examples/ThirdPartyLibs/zlib/adler32.c"]\ -+["examples/ThirdPartyLibs/zlib/compress.c"]\ -+["examples/ThirdPartyLibs/zlib/crc32.c"]\ -+["examples/ThirdPartyLibs/zlib/deflate.c"]\ -+["examples/ThirdPartyLibs/zlib/gzclose.c"]\ -+["examples/ThirdPartyLibs/zlib/gzlib.c"]\ -+["examples/ThirdPartyLibs/zlib/gzread.c"]\ -+["examples/ThirdPartyLibs/zlib/gzwrite.c"]\ -+["examples/ThirdPartyLibs/zlib/infback.c"]\ -+["examples/ThirdPartyLibs/zlib/inffast.c"]\ -+["examples/ThirdPartyLibs/zlib/inflate.c"]\ -+["examples/ThirdPartyLibs/zlib/inftrees.c"]\ -+["examples/ThirdPartyLibs/zlib/trees.c"]\ -+["examples/ThirdPartyLibs/zlib/uncompr.c"]\ -+["examples/ThirdPartyLibs/zlib/zutil.c"]\ -+["examples/Importers/ImportColladaDemo/LoadMeshFromCollada.cpp"]\ -+["examples/Importers/ImportObjDemo/LoadMeshFromObj.cpp"]\ -+["examples/Importers/ImportObjDemo/Wavefront2GLInstanceGraphicsShape.cpp"]\ -+["examples/Importers/ImportMJCFDemo/BulletMJCFImporter.cpp"]\ -+["examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp"]\ -+["examples/Importers/ImportURDFDemo/MyMultiBodyCreator.cpp"]\ -+["examples/Importers/ImportURDFDemo/URDF2Bullet.cpp"]\ -+["examples/Importers/ImportURDFDemo/UrdfParser.cpp"]\ -+["examples/Importers/ImportURDFDemo/urdfStringSplit.cpp"]\ -+["examples/Importers/ImportMeshUtility/b3ImportMeshUtility.cpp"]\ -+["examples/MultiThreading/b3PosixThreadSupport.cpp"]\ -+["examples/MultiThreading/b3Win32ThreadSupport.cpp"]\ -+["examples/MultiThreading/b3ThreadSupportInterface.cpp"]\ -+["examples/ThirdPartyLibs/enet/callbacks.c"]\ -+["examples/ThirdPartyLibs/enet/compress.c"]\ -+["examples/ThirdPartyLibs/enet/host.c"]\ -+["examples/ThirdPartyLibs/enet/list.c"]\ -+["examples/ThirdPartyLibs/enet/packet.c"]\ -+["examples/ThirdPartyLibs/enet/peer.c"]\ -+["examples/ThirdPartyLibs/enet/protocol.c"]\ -+["examples/ExampleBrowser/OpenGLGuiHelper.cpp"]\ -+["examples/ExampleBrowser/OpenGLExampleBrowser.cpp"]\ -+["examples/ExampleBrowser/CollisionShape2TriangleMesh.cpp"]\ -+["examples/ExampleBrowser/GL_ShapeDrawer.cpp"]\ -+["examples/OpenGLWindow/SimpleOpenGL2Renderer.cpp"]\ -+["examples/OpenGLWindow/GLInstancingRenderer.cpp"]\ -+["examples/OpenGLWindow/SimpleOpenGL3App.cpp"]\ -+["examples/OpenGLWindow/GLPrimitiveRenderer.cpp"]\ -+["examples/OpenGLWindow/TwFonts.cpp"]\ -+["examples/OpenGLWindow/GLRenderToTexture.cpp"]\ -+["examples/OpenGLWindow/LoadShader.cpp"]\ -+["examples/OpenGLWindow/OpenSans.cpp"]\ -+["examples/OpenGLWindow/SimpleCamera.cpp"]\ -+["examples/OpenGLWindow/fontstash.cpp"]\ -+["examples/OpenGLWindow/SimpleOpenGL2App.cpp"]\ -+["examples/OpenGLWindow/opengl_fontstashcallbacks.cpp"]\ -+["examples/ExampleBrowser/GwenGUISupport/GraphingTexture.cpp"]\ -+["examples/ExampleBrowser/GwenGUISupport/GwenProfileWindow.cpp"]\ -+["examples/ExampleBrowser/GwenGUISupport/gwenUserInterface.cpp"]\ -+["examples/ExampleBrowser/GwenGUISupport/GwenParameterInterface.cpp"]\ -+["examples/ExampleBrowser/GwenGUISupport/GwenTextureWindow.cpp"]\ -+["src/Bullet3Common/b3AlignedAllocator.cpp"]\ -+["src/Bullet3Common/b3Logging.cpp"]\ -+["src/Bullet3Common/b3Vector3.cpp"]\ -+["examples/ThirdPartyLibs/clsocket/src/ActiveSocket.cpp"]\ -+["examples/ThirdPartyLibs/clsocket/src/PassiveSocket.cpp"]\ -+["examples/ThirdPartyLibs/clsocket/src/SimpleSocket.cpp"]\ -+["Extras/Serialize/BulletFileLoader/bChunk.cpp"]\ -+["Extras/Serialize/BulletFileLoader/bDNA.cpp"]\ -+["Extras/Serialize/BulletFileLoader/bFile.cpp"]\ -+["Extras/Serialize/BulletFileLoader/btBulletFile.cpp"]\ -+["Extras/Serialize/BulletWorldImporter/btMultiBodyWorldImporter.cpp"]\ -+["Extras/Serialize/BulletWorldImporter/btBulletWorldImporter.cpp"]\ -+["Extras/Serialize/BulletWorldImporter/btWorldImporter.cpp"]\ -+["Extras/InverseDynamics/CloneTreeCreator.cpp"]\ -+["Extras/InverseDynamics/IDRandomUtil.cpp"]\ -+["Extras/InverseDynamics/MultiBodyTreeDebugGraph.cpp"]\ -+["Extras/InverseDynamics/User2InternalIndex.cpp"]\ -+["Extras/InverseDynamics/CoilCreator.cpp"]\ -+["Extras/InverseDynamics/MultiBodyNameMap.cpp"]\ -+["Extras/InverseDynamics/RandomTreeCreator.cpp"]\ -+["Extras/InverseDynamics/btMultiBodyTreeCreator.cpp"]\ -+["Extras/InverseDynamics/DillCreator.cpp"]\ -+["Extras/InverseDynamics/MultiBodyTreeCreator.cpp"]\ -+["Extras/InverseDynamics/SimpleTreeCreator.cpp"]\ -+["Extras/InverseDynamics/invdyn_bullet_comparison.cpp"]\ -+["src/BulletSoftBody/btDefaultSoftBodySolver.cpp"]\ -+["src/BulletSoftBody/btSoftBodyHelpers.cpp"]\ -+["src/BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp"]\ -+["src/BulletSoftBody/btSoftBody.cpp"]\ -+["src/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp"]\ -+["src/BulletSoftBody/btSoftRigidDynamicsWorld.cpp"]\ -+["src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp"]\ -+["src/BulletSoftBody/btSoftMultiBodyDynamicsWorld.cpp"]\ -+["src/BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp"]\ -+["src/BulletInverseDynamics/IDMath.cpp"]\ -+["src/BulletInverseDynamics/MultiBodyTree.cpp"]\ -+["src/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp"]\ -+["src/BulletInverseDynamics/details/MultiBodyTreeInitCache.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/Jacobian.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/LinearR2.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/LinearR3.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/LinearR4.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/MatrixRmn.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/Misc.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/Node.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/Tree.cpp"]\ -+["examples/ThirdPartyLibs/BussIK/VectorRn.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Anim.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/DragAndDrop.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Hook.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/ToolTip.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/events.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/BaseRender.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Gwen.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Skin.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Utility.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/inputhandler.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Base.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Button.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Canvas.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/CheckBox.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ColorControls.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ColorPicker.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ComboBox.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/CrossSplitter.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/DockBase.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/DockedTabControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Dragger.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/GroupBox.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/HSVColorPicker.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/HorizontalScrollBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ImagePanel.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/HorizontalSlider.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Label.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/LabelClickable.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ListBox.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/MenuItem.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Menu.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/MenuStrip.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/NumericUpDown.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/PanelListPanel.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ProgressBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Properties.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/RadioButton.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/RadioButtonController.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ResizableControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Resizer.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/RichLabel.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ScrollBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ScrollBarBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ScrollBarButton.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/ScrollControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Slider.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/SplitterBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TabButton.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TabControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TabStrip.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Text.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TextBox.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TextBoxNumeric.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TreeControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/TreeNode.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/VerticalScrollBar.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/VerticalSlider.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/WindowControl.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Dialog/FileOpen.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Dialog/FileSave.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Controls/Dialog/Query.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Platforms/Null.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Platforms/Windows.cpp"]\ -+["examples/ThirdPartyLibs/Gwen/Renderers/OpenGL_DebugFont.cpp"]\ - - - -egl_renderer_sources = \ -["examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp"]\ -+["examples/SharedMemory/plugins/eglPlugin/eglRendererPlugin.cpp"]\ -+["examples/Importers/ImportColladaDemo/LoadMeshFromCollada.cpp"]\ -+["examples/Importers/ImportObjDemo/LoadMeshFromObj.cpp"]\ -+["examples/Importers/ImportMeshUtility/b3ImportMeshUtility.cpp"]\ -+["examples/Importers/ImportObjDemo/Wavefront2GLInstanceGraphicsShape.cpp"]\ -+["examples/TinyRenderer/geometry.cpp"]\ -+["examples/TinyRenderer/model.cpp"]\ -+["examples/TinyRenderer/tgaimage.cpp"]\ -+["examples/TinyRenderer/our_gl.cpp"]\ -+["examples/TinyRenderer/TinyRenderer.cpp"]\ -+["examples/ThirdPartyLibs/Wavefront/tiny_obj_loader.cpp"]\ -+["examples/ThirdPartyLibs/stb_image/stb_image.cpp"]\ -+["examples/ThirdPartyLibs/stb_image/stb_image_write.cpp"]\ -+["examples/ThirdPartyLibs/tinyxml2/tinyxml2.cpp"]\ -+["examples/OpenGLWindow/SimpleCamera.cpp"]\ -+["examples/Utils/b3Clock.cpp"]\ -+["examples/Utils/b3ResourcePath.cpp"]\ -+["src/BulletCollision/CollisionShapes/btShapeHull.cpp"]\ -+["src/BulletCollision/CollisionShapes/btConvexHullShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btBoxShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btSphereShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btConvexShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btCollisionShape.cpp"]\ -+["src/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp"]\ -+["src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp"]\ -+["src/Bullet3Common/b3Logging.cpp"]\ -+["src/LinearMath/btAlignedAllocator.cpp"]\ -+["src/LinearMath/btConvexHull.cpp"]\ -+["src/LinearMath/btConvexHullComputer.cpp"] \ -+["src/LinearMath/btGeometryUtil.cpp"]\ -+["src/LinearMath/btQuickprof.cpp"] \ -+["src/LinearMath/btThreads.cpp"] \ -+["src/Bullet3Common/b3AlignedAllocator.cpp"] \ -+["examples/ThirdPartyLibs/glad/gl.c"]\ -+["examples/OpenGLWindow/GLInstancingRenderer.cpp"]\ -+["examples/OpenGLWindow/GLRenderToTexture.cpp"] \ -+["examples/OpenGLWindow/LoadShader.cpp"] - -if 'BT_USE_EGL' in CXX_FLAGS: - sources += ['examples/ThirdPartyLibs/glad/egl.c'] - sources += ['examples/OpenGLWindow/EGLOpenGLWindow.cpp'] - -if _platform == "linux" or _platform == "linux2": - libraries = ['dl','pthread'] - CXX_FLAGS += '-D_LINUX ' - CXX_FLAGS += '-DGLEW_STATIC ' - CXX_FLAGS += '-DGLEW_INIT_OPENGL11_FUNCTIONS=1 ' - CXX_FLAGS += '-DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 ' - CXX_FLAGS += '-DDYNAMIC_LOAD_X11_FUNCTIONS ' - CXX_FLAGS += '-DHAS_SOCKLEN_T ' - CXX_FLAGS += '-fno-inline-functions-called-once ' - EGL_CXX_FLAGS += '-DBT_USE_EGL ' - EGL_CXX_FLAGS += '-fPIC ' # for plugins - - sources = sources + ["examples/ThirdPartyLibs/enet/unix.c"]\ - +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ - +["examples/ThirdPartyLibs/glad/gl.c"]\ - +["examples/ThirdPartyLibs/glad/glx.c"] - include_dirs += ["examples/ThirdPartyLibs/optionalX11"] - - if 'BT_USE_EGL' in EGL_CXX_FLAGS: - egl_renderer_sources = egl_renderer_sources\ - +["examples/OpenGLWindow/EGLOpenGLWindow.cpp"]\ - +['examples/ThirdPartyLibs/glad/egl.c'] - else: - egl_renderer_sources = egl_renderer_sources\ - +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ - +["examples/ThirdPartyLibs/glad/glx.c"] - - -elif _platform == "win32": - print("win32!") - libraries = ['Ws2_32','Winmm','User32','Opengl32','kernel32','glu32','Gdi32','Comdlg32'] - CXX_FLAGS += '-DWIN32 ' - CXX_FLAGS += '-DGLEW_STATIC ' - sources = sources + ["examples/ThirdPartyLibs/enet/win32.c"]\ - +["examples/OpenGLWindow/Win32Window.cpp"]\ - +["examples/OpenGLWindow/Win32OpenGLWindow.cpp"]\ - +["examples/ThirdPartyLibs/glad/gl.c"] -elif _platform == "darwin": - print("darwin!") - os.environ['LDFLAGS'] = '-framework Cocoa -stdlib=libc++ -framework OpenGL' - CXX_FLAGS += '-DB3_NO_PYTHON_FRAMEWORK ' - CXX_FLAGS += '-DHAS_SOCKLEN_T ' - CXX_FLAGS += '-D_DARWIN ' -# CXX_FLAGS += '-framework Cocoa ' - sources = sources + ["examples/ThirdPartyLibs/enet/unix.c"]\ - +["examples/OpenGLWindow/MacOpenGLWindow.cpp"]\ - +["examples/ThirdPartyLibs/glad/gl.c"]\ - +["examples/OpenGLWindow/MacOpenGLWindowObjC.m"] -else: - print("bsd!") - libraries = ['GL','GLEW','pthread'] - os.environ['LDFLAGS'] = '-L/usr/X11R6/lib' - CXX_FLAGS += '-D_BSD ' - CXX_FLAGS += '-I/usr/X11R6/include ' - CXX_FLAGS += '-DHAS_SOCKLEN_T ' - CXX_FLAGS += '-fno-inline-functions-called-once' - sources = ["examples/ThirdPartyLibs/enet/unix.c"]\ - +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ - +["examples/ThirdPartyLibs/glad/gl.c"]\ - + sources - -setup_py_dir = os.path.dirname(os.path.realpath(__file__)) - -need_files = [] -datadir = "examples/pybullet/gym/pybullet_data" - -hh = setup_py_dir + "/" + datadir - -for root, dirs, files in os.walk(hh): - for fn in files: - ext = os.path.splitext(fn)[1][1:] - if ext and ext in 'yaml index meta data-00000-of-00001 png gif jpg urdf sdf obj txt mtl dae off stl STL xml '.split(): - fn = root + "/" + fn - need_files.append(fn[1+len(hh):]) - -print("found resource files: %i" % len(need_files)) -for n in need_files: print("-- %s" % n) -print("packages") -print(find_packages('examples/pybullet/gym')) -print("-----") - -extensions = [] - -pybullet_ext = Extension("pybullet", - sources = sources, - libraries = libraries, - extra_compile_args=CXX_FLAGS.split(), - include_dirs = include_dirs + ["src","examples/ThirdPartyLibs","examples/ThirdPartyLibs/glad", "examples/ThirdPartyLibs/enet/include","examples/ThirdPartyLibs/clsocket/src"] - ) -extensions.append(pybullet_ext) - - -if 'BT_USE_EGL' in EGL_CXX_FLAGS: - - eglRender = Extension("eglRenderer", - sources = egl_renderer_sources, - libraries = libraries, - extra_compile_args=(CXX_FLAGS+EGL_CXX_FLAGS ).split(), - include_dirs = include_dirs + ["src","examples", "examples/ThirdPartyLibs","examples/ThirdPartyLibs/glad", "examples/ThirdPartyLibs/enet/include","examples/ThirdPartyLibs/clsocket/src"]) - - extensions.append(eglRender) - - -setup( - name = 'pybullet', - version='2.4.4', - description='Official Python Interface for the Bullet Physics SDK specialized for Robotics Simulation and Reinforcement Learning', - long_description='pybullet is an easy to use Python module for physics simulation, robotics and deep reinforcement learning based on the Bullet Physics SDK. With pybullet you can load articulated bodies from URDF, SDF and other file formats. pybullet provides forward dynamics simulation, inverse dynamics computation, forward and inverse kinematics and collision detection and ray intersection queries. Aside from physics simulation, pybullet supports to rendering, with a CPU renderer and OpenGL visualization and support for virtual reality headsets.', - url='https://github.com/bulletphysics/bullet3', - author='Erwin Coumans, Yunfei Bai, Jasmine Hsu', - author_email='erwincoumans@google.com', - license='zlib', - platforms='any', - keywords=['game development', 'virtual reality', 'physics simulation', 'robotics', 'collision detection', 'opengl'], - ext_modules = extensions, - classifiers=['Development Status :: 5 - Production/Stable', - 'License :: OSI Approved :: zlib/libpng License', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX :: Linux', - 'Operating System :: MacOS', - 'Intended Audience :: Science/Research', - "Programming Language :: Python", - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Topic :: Games/Entertainment :: Simulation', - 'Topic :: Scientific/Engineering :: Artificial Intelligence', - 'Framework :: Robot Framework'], - package_dir = { '': 'examples/pybullet/gym'}, - packages=[x for x in find_packages('examples/pybullet/gym')], - package_data = { 'pybullet_data': need_files } -) + +from setuptools import find_packages +from sys import platform as _platform +import sys +import glob +import os + + +from distutils.core import setup +from distutils.extension import Extension +from distutils.util import get_platform +from glob import glob + +# monkey-patch for parallel compilation +import multiprocessing +import multiprocessing.pool + + +def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): + # those lines are copied from distutils.ccompiler.CCompiler directly + macros, objects, extra_postargs, pp_opts, build = self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) + cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) + # parallel code + N = 2*multiprocessing.cpu_count()# number of parallel compilations + def _single_compile(obj): + try: src, ext = build[obj] + except KeyError: return + newcc_args = cc_args + if _platform == "darwin": + if src.endswith('.cpp'): + newcc_args = cc_args + ["-stdlib=libc++"] + self._compile(obj, src, ext, newcc_args, extra_postargs, pp_opts) + # convert to list, imap is evaluated on-demand + pool = multiprocessing.pool.ThreadPool(N) + list(pool.imap(_single_compile,objects)) + return objects +import distutils.ccompiler +distutils.ccompiler.CCompiler.compile=parallelCCompile + +#see http://stackoverflow.com/a/8719066/295157 +import os + + +platform = get_platform() +print(platform) + +CXX_FLAGS = '' +CXX_FLAGS += '-DGWEN_COMPILE_STATIC ' +CXX_FLAGS += '-DBT_USE_DOUBLE_PRECISION ' +CXX_FLAGS += '-DBT_ENABLE_ENET ' +CXX_FLAGS += '-DBT_ENABLE_CLSOCKET ' +CXX_FLAGS += '-DB3_DUMP_PYTHON_VERSION ' +CXX_FLAGS += '-DEGL_ADD_PYTHON_INIT ' +CXX_FLAGS += '-DB3_ENABLE_FILEIO_PLUGIN ' +CXX_FLAGS += '-DB3_USE_ZIPFILE_FILEIO ' +CXX_FLAGS += '-DBT_THREADSAFE=1 ' +CXX_FLAGS += '-DSTATIC_LINK_SPD_PLUGIN ' +CXX_FLAGS += '-DPX_PHYSX_STATIC_LIB ' +CXX_FLAGS += '-DBT_ENABLE_PHYSX ' +CXX_FLAGS += '-DPX_FOUNDATION_DLL=0 ' +CXX_FLAGS += '-DPX_COOKING ' +CXX_FLAGS += '-DNDEBUG ' +CXX_FLAGS += '-DDISABLE_CUDA_PHYSX ' + + + +EGL_CXX_FLAGS = '' + + + +# libraries += [current_python] + +libraries = [] +include_dirs = ["examples", + "src/PhysX/physx/source/common/include", + "src/PhysX/physx/source/common/src", + "src/PhysX/physx/source/fastxml/include", + "src/PhysX/physx/source/filebuf/include", + "src/PhysX/physx/source/foundation/include", + "src/PhysX/physx/source/geomutils/include", + "src/PhysX/physx/source/geomutils/src", + "src/PhysX/physx/source/geomutils/src/ccd", + "src/PhysX/physx/source/geomutils/src/common", + "src/PhysX/physx/source/geomutils/src/contact", + "src/PhysX/physx/source/geomutils/src/convex", + "src/PhysX/physx/source/geomutils/src/distance", + "src/PhysX/physx/source/geomutils/src/gjk", + "src/PhysX/physx/source/geomutils/src/hf", + "src/PhysX/physx/source/geomutils/src/intersection", + "src/PhysX/physx/source/geomutils/src/mesh", + "src/PhysX/physx/source/geomutils/src/pcm", + "src/PhysX/physx/source/geomutils/src/sweep", + "src/PhysX/physx/source/lowlevel/api/include", + "src/PhysX/physx/source/lowlevel/common/include", + "src/PhysX/physx/source/lowlevel/common/include/collision", + "src/PhysX/physx/source/lowlevel/common/include/pipeline", + "src/PhysX/physx/source/lowlevel/common/include/utils", + "src/PhysX/physx/source/lowlevel/software/include", + "src/PhysX/physx/source/lowlevelaabb/include", + "src/PhysX/physx/source/lowleveldynamics/include", + "src/PhysX/physx/source/physx/src", + "src/PhysX/physx/source/physx/src/buffering", + "src/PhysX/physx/source/physx/src/device", + "src/PhysX/physx/source/physxcooking/src", + "src/PhysX/physx/source/physxcooking/src/convex", + "src/PhysX/physx/source/physxcooking/src/mesh", + "src/PhysX/physx/source/physxextensions/src", + "src/PhysX/physx/source/physxextensions/src/serialization/Binary", + "src/PhysX/physx/source/physxextensions/src/serialization/File", + "src/PhysX/physx/source/physxextensions/src/serialization/Xml", + "src/PhysX/physx/source/physxmetadata/core/include", + "src/PhysX/physx/source/physxmetadata/extensions/include", + "src/PhysX/physx/source/physxvehicle/src", + "src/PhysX/physx/source/physxvehicle/src/physxmetadata/include", + "src/PhysX/physx/source/pvd/include", + "src/PhysX/physx/source/scenequery/include", + "src/PhysX/physx/source/simulationcontroller/include", + "src/PhysX/physx/source/simulationcontroller/src", + "src/PhysX/physx/include", + "src/PhysX/physx/include/characterkinematic", + "src/PhysX/physx/include/common", + "src/PhysX/physx/include/cooking", + "src/PhysX/physx/include/extensions", + "src/PhysX/physx/include/geometry", + "src/PhysX/physx/include/geomutils", + "src/PhysX/physx/include/vehicle", + "src/PhysX/pxshared/include", + ] + + +try: + import numpy + NP_DIRS = [numpy.get_include()] +except: + print("numpy is disabled. getCameraImage maybe slower.") +else: + print("numpy is enabled.") + CXX_FLAGS += '-DPYBULLET_USE_NUMPY ' + for d in NP_DIRS: + print("numpy_include_dirs = %s" % d) + include_dirs += NP_DIRS + +sources = ["examples/pybullet/pybullet.c"]\ ++["examples/SharedMemory/physx/PhysXC_API.cpp"]\ ++["examples/SharedMemory/physx/PhysXClient.cpp"]\ ++["examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp"]\ ++["examples/SharedMemory/physx/PhysXUrdfImporter.cpp"]\ ++["examples/SharedMemory/physx/URDF2PhysX.cpp"]\ ++["src/btLinearMathAll.cpp"]\ ++["src/PhysXGeomUtilsAll.cpp"]\ ++["examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp"]\ ++["examples/SharedMemory/plugins/eglPlugin/eglRendererPlugin.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactBoxBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Raycast.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp"]\ ++["src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp"]\ ++["src/PhysX/physx/source/fastxml/src/PsFastXml.cpp"]\ ++["src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp"]\ ++["src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp"]\ ++["src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp"]\ ++["src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp"]\ ++["src/PhysX/physx/source/task/src/TaskManager.cpp"]\ ++["src/PhysXVehicleAll.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvd.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp"]\ ++["src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp"]\ ++["src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp"]\ ++["src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp"]\ ++["src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp"]\ ++["src/PhysXCharacterAll.cpp"]\ ++["src/PhysXCommonAll.cpp"]\ ++["src/PhysXCookingAll.cpp"]\ ++["src/PhysXExtensionAll.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/SnSerialization.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Convert.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp"]\ ++["src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp"]\ ++["src/PhysXFoundationAll.cpp"]\ ++["src/PhysXLowLevelAll.cpp"]\ ++["src/PhysXNpSrcAll.cpp"]\ ++["src/PhysXSceneQueryAll.cpp"]\ ++["src/PhysXSimulationControllerAll.cpp"]\ ++["src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp"]\ ++["src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp"]\ ++["src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp"]\ ++["src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp"]\ ++["src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp"]\ ++["src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp"]\ ++["src/btBulletCollisionAll.cpp"]\ ++["src/btBulletDynamicsAll.cpp"]\ ++["examples/ExampleBrowser/InProcessExampleBrowser.cpp"]\ ++["examples/TinyRenderer/geometry.cpp"]\ ++["examples/TinyRenderer/model.cpp"]\ ++["examples/TinyRenderer/tgaimage.cpp"]\ ++["examples/TinyRenderer/our_gl.cpp"]\ ++["examples/TinyRenderer/TinyRenderer.cpp"]\ ++["examples/SharedMemory/plugins/pdControlPlugin/pdControlPlugin.cpp"]\ ++["examples/SharedMemory/plugins/collisionFilterPlugin/collisionFilterPlugin.cpp"]\ ++["examples/SharedMemory/plugins/fileIOPlugin/fileIOPlugin.cpp"]\ ++["examples/SharedMemory/b3RobotSimulatorClientAPI_NoDirect.cpp"]\ ++["examples/SharedMemory/IKTrajectoryHelper.cpp"]\ ++["examples/SharedMemory/InProcessMemory.cpp"]\ ++["examples/SharedMemory/PhysicsClient.cpp"]\ ++["examples/SharedMemory/PhysicsServer.cpp"]\ ++["examples/SharedMemory/PhysicsServerExample.cpp"]\ ++["examples/SharedMemory/PhysicsServerExampleBullet2.cpp"]\ ++["examples/SharedMemory/SharedMemoryInProcessPhysicsC_API.cpp"]\ ++["examples/SharedMemory/PhysicsServerSharedMemory.cpp"]\ ++["examples/SharedMemory/PhysicsDirect.cpp"]\ ++["examples/SharedMemory/PhysicsDirectC_API.cpp"]\ ++["examples/SharedMemory/PhysicsServerCommandProcessor.cpp"]\ ++["examples/SharedMemory/PhysicsClientSharedMemory.cpp"]\ ++["examples/SharedMemory/PhysicsClientSharedMemory_C_API.cpp"]\ ++["examples/SharedMemory/PhysicsClientC_API.cpp"]\ ++["examples/SharedMemory/Win32SharedMemory.cpp"]\ ++["examples/SharedMemory/PosixSharedMemory.cpp"]\ ++["examples/SharedMemory/plugins/tinyRendererPlugin/TinyRendererVisualShapeConverter.cpp"]\ ++["examples/SharedMemory/plugins/tinyRendererPlugin/tinyRendererPlugin.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/BulletConversion.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/KinTree.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/MathUtil.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/RBDModel.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/RBDUtil.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/Shape.cpp"]\ ++["examples/SharedMemory/plugins/stablePDPlugin/SpAlg.cpp"]\ ++["examples/SharedMemory/PhysicsClientUDP.cpp"]\ ++["examples/SharedMemory/PhysicsClientUDP_C_API.cpp"]\ ++["examples/SharedMemory/PhysicsClientTCP.cpp"]\ ++["examples/SharedMemory/PhysicsClientTCP_C_API.cpp"]\ ++["examples/SharedMemory/b3PluginManager.cpp"]\ ++["examples/Utils/b3ResourcePath.cpp"]\ ++["examples/Utils/RobotLoggingUtil.cpp"]\ ++["examples/Utils/ChromeTraceUtil.cpp"]\ ++["examples/Utils/b3Clock.cpp"]\ ++["examples/Utils/b3Quickprof.cpp"]\ ++["examples/ThirdPartyLibs/tinyxml2/tinyxml2.cpp"]\ ++["examples/ThirdPartyLibs/Wavefront/tiny_obj_loader.cpp"]\ ++["examples/ThirdPartyLibs/stb_image/stb_image.cpp"]\ ++["examples/ThirdPartyLibs/stb_image/stb_image_write.cpp"]\ ++["examples/ThirdPartyLibs/minizip/ioapi.c"]\ ++["examples/ThirdPartyLibs/minizip/unzip.c"]\ ++["examples/ThirdPartyLibs/minizip/zip.c"]\ ++["examples/ThirdPartyLibs/zlib/adler32.c"]\ ++["examples/ThirdPartyLibs/zlib/compress.c"]\ ++["examples/ThirdPartyLibs/zlib/crc32.c"]\ ++["examples/ThirdPartyLibs/zlib/deflate.c"]\ ++["examples/ThirdPartyLibs/zlib/gzclose.c"]\ ++["examples/ThirdPartyLibs/zlib/gzlib.c"]\ ++["examples/ThirdPartyLibs/zlib/gzread.c"]\ ++["examples/ThirdPartyLibs/zlib/gzwrite.c"]\ ++["examples/ThirdPartyLibs/zlib/infback.c"]\ ++["examples/ThirdPartyLibs/zlib/inffast.c"]\ ++["examples/ThirdPartyLibs/zlib/inflate.c"]\ ++["examples/ThirdPartyLibs/zlib/inftrees.c"]\ ++["examples/ThirdPartyLibs/zlib/trees.c"]\ ++["examples/ThirdPartyLibs/zlib/uncompr.c"]\ ++["examples/ThirdPartyLibs/zlib/zutil.c"]\ ++["examples/Importers/ImportColladaDemo/LoadMeshFromCollada.cpp"]\ ++["examples/Importers/ImportObjDemo/LoadMeshFromObj.cpp"]\ ++["examples/Importers/ImportObjDemo/Wavefront2GLInstanceGraphicsShape.cpp"]\ ++["examples/Importers/ImportMJCFDemo/BulletMJCFImporter.cpp"]\ ++["examples/Importers/ImportURDFDemo/BulletUrdfImporter.cpp"]\ ++["examples/Importers/ImportURDFDemo/MyMultiBodyCreator.cpp"]\ ++["examples/Importers/ImportURDFDemo/URDF2Bullet.cpp"]\ ++["examples/Importers/ImportURDFDemo/UrdfParser.cpp"]\ ++["examples/Importers/ImportURDFDemo/urdfStringSplit.cpp"]\ ++["examples/Importers/ImportMeshUtility/b3ImportMeshUtility.cpp"]\ ++["examples/MultiThreading/b3PosixThreadSupport.cpp"]\ ++["examples/MultiThreading/b3Win32ThreadSupport.cpp"]\ ++["examples/MultiThreading/b3ThreadSupportInterface.cpp"]\ ++["examples/ThirdPartyLibs/enet/callbacks.c"]\ ++["examples/ThirdPartyLibs/enet/compress.c"]\ ++["examples/ThirdPartyLibs/enet/host.c"]\ ++["examples/ThirdPartyLibs/enet/list.c"]\ ++["examples/ThirdPartyLibs/enet/packet.c"]\ ++["examples/ThirdPartyLibs/enet/peer.c"]\ ++["examples/ThirdPartyLibs/enet/protocol.c"]\ ++["examples/ExampleBrowser/OpenGLGuiHelper.cpp"]\ ++["examples/ExampleBrowser/OpenGLExampleBrowser.cpp"]\ ++["examples/ExampleBrowser/CollisionShape2TriangleMesh.cpp"]\ ++["examples/ExampleBrowser/GL_ShapeDrawer.cpp"]\ ++["examples/OpenGLWindow/SimpleOpenGL2Renderer.cpp"]\ ++["examples/OpenGLWindow/GLInstancingRenderer.cpp"]\ ++["examples/OpenGLWindow/SimpleOpenGL3App.cpp"]\ ++["examples/OpenGLWindow/GLPrimitiveRenderer.cpp"]\ ++["examples/OpenGLWindow/TwFonts.cpp"]\ ++["examples/OpenGLWindow/GLRenderToTexture.cpp"]\ ++["examples/OpenGLWindow/LoadShader.cpp"]\ ++["examples/OpenGLWindow/OpenSans.cpp"]\ ++["examples/OpenGLWindow/SimpleCamera.cpp"]\ ++["examples/OpenGLWindow/fontstash.cpp"]\ ++["examples/OpenGLWindow/SimpleOpenGL2App.cpp"]\ ++["examples/OpenGLWindow/opengl_fontstashcallbacks.cpp"]\ ++["examples/ExampleBrowser/GwenGUISupport/GraphingTexture.cpp"]\ ++["examples/ExampleBrowser/GwenGUISupport/GwenProfileWindow.cpp"]\ ++["examples/ExampleBrowser/GwenGUISupport/gwenUserInterface.cpp"]\ ++["examples/ExampleBrowser/GwenGUISupport/GwenParameterInterface.cpp"]\ ++["examples/ExampleBrowser/GwenGUISupport/GwenTextureWindow.cpp"]\ ++["src/Bullet3Common/b3AlignedAllocator.cpp"]\ ++["src/Bullet3Common/b3Logging.cpp"]\ ++["src/Bullet3Common/b3Vector3.cpp"]\ ++["examples/ThirdPartyLibs/clsocket/src/ActiveSocket.cpp"]\ ++["examples/ThirdPartyLibs/clsocket/src/PassiveSocket.cpp"]\ ++["examples/ThirdPartyLibs/clsocket/src/SimpleSocket.cpp"]\ ++["Extras/Serialize/BulletFileLoader/bChunk.cpp"]\ ++["Extras/Serialize/BulletFileLoader/bDNA.cpp"]\ ++["Extras/Serialize/BulletFileLoader/bFile.cpp"]\ ++["Extras/Serialize/BulletFileLoader/btBulletFile.cpp"]\ ++["Extras/Serialize/BulletWorldImporter/btMultiBodyWorldImporter.cpp"]\ ++["Extras/Serialize/BulletWorldImporter/btBulletWorldImporter.cpp"]\ ++["Extras/Serialize/BulletWorldImporter/btWorldImporter.cpp"]\ ++["Extras/InverseDynamics/CloneTreeCreator.cpp"]\ ++["Extras/InverseDynamics/IDRandomUtil.cpp"]\ ++["Extras/InverseDynamics/MultiBodyTreeDebugGraph.cpp"]\ ++["Extras/InverseDynamics/User2InternalIndex.cpp"]\ ++["Extras/InverseDynamics/CoilCreator.cpp"]\ ++["Extras/InverseDynamics/MultiBodyNameMap.cpp"]\ ++["Extras/InverseDynamics/RandomTreeCreator.cpp"]\ ++["Extras/InverseDynamics/btMultiBodyTreeCreator.cpp"]\ ++["Extras/InverseDynamics/DillCreator.cpp"]\ ++["Extras/InverseDynamics/MultiBodyTreeCreator.cpp"]\ ++["Extras/InverseDynamics/SimpleTreeCreator.cpp"]\ ++["Extras/InverseDynamics/invdyn_bullet_comparison.cpp"]\ ++["src/BulletSoftBody/btDefaultSoftBodySolver.cpp"]\ ++["src/BulletSoftBody/btSoftBodyHelpers.cpp"]\ ++["src/BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp"]\ ++["src/BulletSoftBody/btSoftBody.cpp"]\ ++["src/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp"]\ ++["src/BulletSoftBody/btSoftRigidDynamicsWorld.cpp"]\ ++["src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp"]\ ++["src/BulletSoftBody/btSoftMultiBodyDynamicsWorld.cpp"]\ ++["src/BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp"]\ ++["src/BulletInverseDynamics/IDMath.cpp"]\ ++["src/BulletInverseDynamics/MultiBodyTree.cpp"]\ ++["src/BulletInverseDynamics/details/MultiBodyTreeImpl.cpp"]\ ++["src/BulletInverseDynamics/details/MultiBodyTreeInitCache.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/Jacobian.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/LinearR2.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/LinearR3.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/LinearR4.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/MatrixRmn.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/Misc.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/Node.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/Tree.cpp"]\ ++["examples/ThirdPartyLibs/BussIK/VectorRn.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Anim.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/DragAndDrop.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Hook.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/ToolTip.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/events.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/BaseRender.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Gwen.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Skin.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Utility.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/inputhandler.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Base.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Button.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Canvas.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/CheckBox.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ColorControls.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ColorPicker.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ComboBox.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/CrossSplitter.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/DockBase.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/DockedTabControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Dragger.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/GroupBox.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/HSVColorPicker.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/HorizontalScrollBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ImagePanel.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/HorizontalSlider.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Label.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/LabelClickable.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ListBox.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/MenuItem.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Menu.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/MenuStrip.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/NumericUpDown.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/PanelListPanel.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ProgressBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Properties.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/RadioButton.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/RadioButtonController.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ResizableControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Resizer.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/RichLabel.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ScrollBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ScrollBarBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ScrollBarButton.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/ScrollControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Slider.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/SplitterBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TabButton.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TabControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TabStrip.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Text.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TextBox.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TextBoxNumeric.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TreeControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/TreeNode.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/VerticalScrollBar.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/VerticalSlider.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/WindowControl.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Dialog/FileOpen.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Dialog/FileSave.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Controls/Dialog/Query.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Platforms/Null.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Platforms/Windows.cpp"]\ ++["examples/ThirdPartyLibs/Gwen/Renderers/OpenGL_DebugFont.cpp"] + + + +egl_renderer_sources = ["examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp"]\ ++["examples/SharedMemory/plugins/eglPlugin/eglRendererPlugin.cpp"]\ ++["examples/Importers/ImportColladaDemo/LoadMeshFromCollada.cpp"]\ ++["examples/Importers/ImportObjDemo/LoadMeshFromObj.cpp"]\ ++["examples/Importers/ImportMeshUtility/b3ImportMeshUtility.cpp"]\ ++["examples/Importers/ImportObjDemo/Wavefront2GLInstanceGraphicsShape.cpp"]\ ++["examples/TinyRenderer/geometry.cpp"]\ ++["examples/TinyRenderer/model.cpp"]\ ++["examples/TinyRenderer/tgaimage.cpp"]\ ++["examples/TinyRenderer/our_gl.cpp"]\ ++["examples/TinyRenderer/TinyRenderer.cpp"]\ ++["examples/ThirdPartyLibs/Wavefront/tiny_obj_loader.cpp"]\ ++["examples/ThirdPartyLibs/stb_image/stb_image.cpp"]\ ++["examples/ThirdPartyLibs/stb_image/stb_image_write.cpp"]\ ++["examples/ThirdPartyLibs/tinyxml2/tinyxml2.cpp"]\ ++["examples/OpenGLWindow/SimpleCamera.cpp"]\ ++["examples/Utils/b3Clock.cpp"]\ ++["examples/Utils/b3ResourcePath.cpp"]\ ++["src/BulletCollision/CollisionShapes/btShapeHull.cpp"]\ ++["src/BulletCollision/CollisionShapes/btConvexHullShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btBoxShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btSphereShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btConvexShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btCollisionShape.cpp"]\ ++["src/BulletCollision/CollisionShapes/btConvexPolyhedron.cpp"]\ ++["src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp"]\ ++["src/Bullet3Common/b3Logging.cpp"]\ ++["src/LinearMath/btAlignedAllocator.cpp"]\ ++["src/LinearMath/btConvexHull.cpp"]\ ++["src/LinearMath/btConvexHullComputer.cpp"] \ ++["src/LinearMath/btGeometryUtil.cpp"]\ ++["src/LinearMath/btQuickprof.cpp"] \ ++["src/LinearMath/btThreads.cpp"] \ ++["src/Bullet3Common/b3AlignedAllocator.cpp"] \ ++["examples/ThirdPartyLibs/glad/gl.c"]\ ++["examples/OpenGLWindow/GLInstancingRenderer.cpp"]\ ++["examples/OpenGLWindow/GLRenderToTexture.cpp"] \ ++["examples/OpenGLWindow/LoadShader.cpp"] + +if 'BT_USE_EGL' in CXX_FLAGS: + sources += ['examples/ThirdPartyLibs/glad/egl.c'] + sources += ['examples/OpenGLWindow/EGLOpenGLWindow.cpp'] + +if _platform == "linux" or _platform == "linux2": + libraries = ['dl','pthread'] + CXX_FLAGS += '-D_LINUX ' + CXX_FLAGS += '-DGLEW_STATIC ' + CXX_FLAGS += '-DGLEW_INIT_OPENGL11_FUNCTIONS=1 ' + CXX_FLAGS += '-DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 ' + CXX_FLAGS += '-DDYNAMIC_LOAD_X11_FUNCTIONS ' + CXX_FLAGS += '-DHAS_SOCKLEN_T ' + CXX_FLAGS += '-fno-inline-functions-called-once ' + EGL_CXX_FLAGS += '-DBT_USE_EGL ' + EGL_CXX_FLAGS += '-fPIC ' # for plugins + + sources = sources + ["examples/ThirdPartyLibs/enet/unix.c"]\ + +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ + +["examples/ThirdPartyLibs/glad/gl.c"]\ + +["src/PhysXFoundationUnix.cpp"]\ + +["examples/ThirdPartyLibs/glad/glx.c"] + include_dirs += ["examples/ThirdPartyLibs/optionalX11"] + + if 'BT_USE_EGL' in EGL_CXX_FLAGS: + egl_renderer_sources = egl_renderer_sources\ + +["examples/OpenGLWindow/EGLOpenGLWindow.cpp"]\ + +['examples/ThirdPartyLibs/glad/egl.c'] + else: + egl_renderer_sources = egl_renderer_sources\ + +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ + +["examples/ThirdPartyLibs/glad/glx.c"] + + +elif _platform == "win32": + print("win32!") + libraries = ['Ws2_32','Winmm','User32','Opengl32','kernel32','glu32','Gdi32','Comdlg32'] + CXX_FLAGS += '-DWIN32 ' + CXX_FLAGS += '-DGLEW_STATIC ' + sources = sources + ["examples/ThirdPartyLibs/enet/win32.c"]\ + +["examples/OpenGLWindow/Win32Window.cpp"]\ + +["examples/OpenGLWindow/Win32OpenGLWindow.cpp"]\ + +["examples/ThirdPartyLibs/glad/gl.c"] + sources = sources + +["src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp"]\ + +["src/PhysX/physx/source/common/src/windows/CmWindowsDelayLoadHook.cpp"]\ + +["PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp"]\ + +["src/PhysXFoundationWindows.cpp"]\ + +["src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp"] +elif _platform == "darwin": + print("darwin!") + os.environ['LDFLAGS'] = '-framework Cocoa -stdlib=libc++ -framework OpenGL' + CXX_FLAGS += '-DB3_NO_PYTHON_FRAMEWORK ' + CXX_FLAGS += '-DHAS_SOCKLEN_T ' + CXX_FLAGS += '-D_DARWIN ' +# CXX_FLAGS += '-framework Cocoa ' + sources = sources + ["examples/ThirdPartyLibs/enet/unix.c"]\ + +["src/PhysXFoundationUnix.cpp"]\ + +["examples/OpenGLWindow/MacOpenGLWindow.cpp"]\ + +["examples/ThirdPartyLibs/glad/gl.c"]\ + +["examples/OpenGLWindow/MacOpenGLWindowObjC.m"] +else: + print("bsd!") + libraries = ['GL','GLEW','pthread'] + os.environ['LDFLAGS'] = '-L/usr/X11R6/lib' + CXX_FLAGS += '-D_BSD ' + CXX_FLAGS += '-I/usr/X11R6/include ' + CXX_FLAGS += '-DHAS_SOCKLEN_T ' + CXX_FLAGS += '-fno-inline-functions-called-once' + sources = ["examples/ThirdPartyLibs/enet/unix.c"]\ + +["src/PhysXFoundationUnix.cpp"]\ + +["examples/OpenGLWindow/X11OpenGLWindow.cpp"]\ + +["examples/ThirdPartyLibs/glad/gl.c"]\ + + sources + +setup_py_dir = os.path.dirname(os.path.realpath(__file__)) + +need_files = [] +datadir = "examples/pybullet/gym/pybullet_data" + +hh = setup_py_dir + "/" + datadir + +for root, dirs, files in os.walk(hh): + for fn in files: + ext = os.path.splitext(fn)[1][1:] + if ext and ext in 'yaml index meta data-00000-of-00001 png gif jpg urdf sdf obj txt mtl dae off stl STL xml '.split(): + fn = root + "/" + fn + need_files.append(fn[1+len(hh):]) + +print("found resource files: %i" % len(need_files)) +for n in need_files: print("-- %s" % n) +print("packages") +print(find_packages('examples/pybullet/gym')) +print("-----") + +extensions = [] + +pybullet_ext = Extension("pybullet", + sources = sources, + libraries = libraries, + extra_compile_args=CXX_FLAGS.split(), + include_dirs = include_dirs + ["src","examples/ThirdPartyLibs","examples/ThirdPartyLibs/glad", "examples/ThirdPartyLibs/enet/include","examples/ThirdPartyLibs/clsocket/src"] + ) +extensions.append(pybullet_ext) + + +if 'BT_USE_EGL' in EGL_CXX_FLAGS: + + eglRender = Extension("eglRenderer", + sources = egl_renderer_sources, + libraries = libraries, + extra_compile_args=(CXX_FLAGS+EGL_CXX_FLAGS ).split(), + include_dirs = include_dirs + ["src","examples", "examples/ThirdPartyLibs","examples/ThirdPartyLibs/glad", "examples/ThirdPartyLibs/enet/include","examples/ThirdPartyLibs/clsocket/src"]) + + extensions.append(eglRender) + + +setup( + name = 'pybullet', + version='2.4.3', + description='Official Python Interface for the Bullet Physics SDK specialized for Robotics Simulation and Reinforcement Learning', + long_description='pybullet is an easy to use Python module for physics simulation, robotics and deep reinforcement learning based on the Bullet Physics SDK. With pybullet you can load articulated bodies from URDF, SDF and other file formats. pybullet provides forward dynamics simulation, inverse dynamics computation, forward and inverse kinematics and collision detection and ray intersection queries. Aside from physics simulation, pybullet supports to rendering, with a CPU renderer and OpenGL visualization and support for virtual reality headsets.', + url='https://github.com/bulletphysics/bullet3', + author='Erwin Coumans, Yunfei Bai, Jasmine Hsu', + author_email='erwincoumans@google.com', + license='zlib', + platforms='any', + keywords=['game development', 'virtual reality', 'physics simulation', 'robotics', 'collision detection', 'opengl'], + ext_modules = extensions, + classifiers=['Development Status :: 5 - Production/Stable', + 'License :: OSI Approved :: zlib/libpng License', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX :: Linux', + 'Operating System :: MacOS', + 'Intended Audience :: Science/Research', + "Programming Language :: Python", + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Topic :: Games/Entertainment :: Simulation', + 'Topic :: Scientific/Engineering :: Artificial Intelligence', + 'Framework :: Robot Framework'], + package_dir = { '': 'examples/pybullet/gym'}, + packages=[x for x in find_packages('examples/pybullet/gym')], + package_data = { 'pybullet_data': need_files } +) diff --git a/src/PhysX/physx/include/PxActor.h b/src/PhysX/physx/include/PxActor.h new file mode 100644 index 000000000..6e3306ca9 --- /dev/null +++ b/src/PhysX/physx/include/PxActor.h @@ -0,0 +1,330 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ACTOR +#define PX_PHYSICS_NX_ACTOR + +/** \addtogroup physics + @{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxBounds3.h" +#include "PxClient.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxRigidActor; +class PxRigidBody; +class PxRigidStatic; +class PxRigidDynamic; +class PxArticulation; +class PxArticulationLink; + + +/** Group index which allows to specify 1- or 2-way interaction */ +typedef PxU8 PxDominanceGroup; // Must be < 32, PxU8. + +/** +\brief Flags which control the behavior of an actor. + +@see PxActorFlags PxActor PxActor.setActorFlag() PxActor.getActorFlags() +*/ +struct PxActorFlag +{ + enum Enum + { + /** + \brief Enable debug renderer for this actor + + @see PxScene.getRenderBuffer() PxRenderBuffer PxVisualizationParameter + */ + eVISUALIZATION = (1<<0), + + /** + \brief Disables scene gravity for this actor + */ + eDISABLE_GRAVITY = (1<<1), + + /** + \brief Enables the sending of PxSimulationEventCallback::onWake() and PxSimulationEventCallback::onSleep() notify events + + @see PxSimulationEventCallback::onWake() PxSimulationEventCallback::onSleep() + */ + eSEND_SLEEP_NOTIFIES = (1<<2), + + /** + \brief Disables simulation for the actor. + + \note This is only supported by PxRigidStatic and PxRigidDynamic actors and can be used to reduce the memory footprint when rigid actors are + used for scene queries only. + + \note Setting this flag will remove all constraints attached to the actor from the scene. + + \note If this flag is set, the following calls are forbidden: + \li PxRigidBody: setLinearVelocity(), setAngularVelocity(), addForce(), addTorque(), clearForce(), clearTorque() + \li PxRigidDynamic: setKinematicTarget(), setWakeCounter(), wakeUp(), putToSleep() + + \par Sleeping: + Raising this flag will set all velocities and the wake counter to 0, clear all forces, clear the kinematic target, put the actor + to sleep and wake up all touching actors from the previous frame. + */ + eDISABLE_SIMULATION = (1<<3) + }; +}; + +/** +\brief collection of set bits defined in PxActorFlag. + +@see PxActorFlag +*/ +typedef PxFlags PxActorFlags; +PX_FLAGS_OPERATORS(PxActorFlag::Enum,PxU8) + +/** +\brief Identifies each type of actor. +@see PxActor +*/ +struct PxActorType +{ + enum Enum + { + /** + \brief A static rigid body + @see PxRigidStatic + */ + eRIGID_STATIC, + + /** + \brief A dynamic rigid body + @see PxRigidDynamic + */ + eRIGID_DYNAMIC, + + /** + \brief An articulation link + @see PxArticulationLink + */ + eARTICULATION_LINK, + + //brief internal use only! + eACTOR_COUNT, + + eACTOR_FORCE_DWORD = 0x7fffffff + }; +}; + +/** +\brief PxActor is the base class for the main simulation objects in the physics SDK. + +The actor is owned by and contained in a PxScene. + +*/ +class PxActor : public PxBase +{ +public: + /** + \brief Deletes the actor. + + Do not keep a reference to the deleted instance. + + If the actor belongs to a #PxAggregate object, it is automatically removed from the aggregate. + + @see PxBase.release(), PxAggregate + */ + virtual void release() = 0; + + /** + \brief Retrieves the type of actor. + + \return The actor type of the actor. + + @see PxActorType + */ + virtual PxActorType::Enum getType() const = 0; + + /** + \brief Retrieves the scene which this actor belongs to. + + \return Owner Scene. NULL if not part of a scene. + + @see PxScene + */ + virtual PxScene* getScene() const = 0; + + // Runtime modifications + + /** + \brief Sets a name string for the object that can be retrieved with getName(). + + This is for debugging and is not used by the SDK. The string is not copied by the SDK, + only the pointer is stored. + + \param[in] name String to set the objects name to. + + Default: NULL + + @see getName() + */ + virtual void setName(const char* name) = 0; + + /** + \brief Retrieves the name string set with setName(). + + \return Name string associated with object. + + @see setName() + */ + virtual const char* getName() const = 0; + + /** + \brief Retrieves the axis aligned bounding box enclosing the actor. + + \param[in] inflation Scale factor for computed world bounds. Box extents are multiplied by this value. + + \return The actor's bounding box. + + @see PxBounds3 + */ + virtual PxBounds3 getWorldBounds(float inflation=1.01f) const = 0; + + /** + \brief Raises or clears a particular actor flag. + + See the list of flags #PxActorFlag + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] flag The PxActor flag to raise(set) or clear. See #PxActorFlag. + \param[in] value The boolean value to assign to the flag. + + Default: PxActorFlag::eVISUALIZATION + + @see PxActorFlag getActorFlags() + */ + virtual void setActorFlag(PxActorFlag::Enum flag, bool value) = 0; + /** + \brief sets the actor flags + + See the list of flags #PxActorFlag + @see PxActorFlag setActorFlag() + */ + virtual void setActorFlags( PxActorFlags inFlags ) = 0; + + /** + \brief Reads the PxActor flags. + + See the list of flags #PxActorFlag + + \return The values of the PxActor flags. + + @see PxActorFlag setActorFlag() + */ + virtual PxActorFlags getActorFlags() const = 0; + + /** + \brief Assigns dynamic actors a dominance group identifier. + + PxDominanceGroup is a 5 bit group identifier (legal range from 0 to 31). + + The PxScene::setDominanceGroupPair() lets you set certain behaviors for pairs of dominance groups. + By default every dynamic actor is created in group 0. + + Default: 0 + + Sleeping: Changing the dominance group does NOT wake the actor up automatically. + + \param[in] dominanceGroup The dominance group identifier. Range: [0..31] + + @see getDominanceGroup() PxDominanceGroup PxScene::setDominanceGroupPair() + */ + virtual void setDominanceGroup(PxDominanceGroup dominanceGroup) = 0; + + /** + \brief Retrieves the value set with setDominanceGroup(). + + \return The dominance group of this actor. + + @see setDominanceGroup() PxDominanceGroup PxScene::setDominanceGroupPair() + */ + virtual PxDominanceGroup getDominanceGroup() const = 0; + + + /** + \brief Sets the owner client of an actor. + + This cannot be done once the actor has been placed into a scene. + + Default: PX_DEFAULT_CLIENT + + @see PxClientID PxScene::createClient() + */ + virtual void setOwnerClient( PxClientID inClient ) = 0; + + /** + \brief Returns the owner client that was specified with at creation time. + + This value cannot be changed once the object is placed into the scene. + + @see PxClientID PxScene::createClient() + */ + virtual PxClientID getOwnerClient() const = 0; + + /** + \brief Retrieves the aggregate the actor might be a part of. + + \return The aggregate the actor is a part of, or NULL if the actor does not belong to an aggregate. + + @see PxAggregate + */ + virtual PxAggregate* getAggregate() const = 0; + + //public variables: + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. + +protected: + PX_INLINE PxActor(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags), userData(NULL) {} + PX_INLINE PxActor(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxActor() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxActor", name) || PxBase::isKindOf(name); } + + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxAggregate.h b/src/PhysX/physx/include/PxAggregate.h new file mode 100644 index 000000000..3c0b97700 --- /dev/null +++ b/src/PhysX/physx/include/PxAggregate.h @@ -0,0 +1,213 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_AGGREGATE +#define PX_PHYSICS_NX_AGGREGATE + +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxActor; +class PxBVHStructure; + +/** +\brief Class to aggregate actors into a single broad-phase entry. + +A PxAggregate object is a collection of PxActors, which will exist as a single entry in the +broad-phase structures. This has 3 main benefits: + +1) it reduces "broad phase pollution" by allowing a collection of spatially coherent broad-phase +entries to be replaced by a single aggregated entry (e.g. a ragdoll or a single actor with a +large number of attached shapes). + +2) it reduces broad-phase memory usage + +3) filtering can be optimized a lot if self-collisions within an aggregate are not needed. For + example if you don't need collisions between ragdoll bones, it's faster to simply disable + filtering once and for all, for the aggregate containing the ragdoll, rather than filtering + out each bone-bone collision in the filter shader. + +@see PxActor, PxPhysics.createAggregate +*/ + +class PxAggregate : public PxBase +{ +public: + + /** + \brief Deletes the aggregate object. + + Deleting the PxAggregate object does not delete the aggregated actors. If the PxAggregate object + belongs to a scene, the aggregated actors are automatically re-inserted in that scene. If you intend + to delete both the PxAggregate and its actors, it is best to release the actors first, then release + the PxAggregate when it is empty. + */ + virtual void release() = 0; + + /** + \brief Adds an actor to the aggregate object. + + A warning is output if the total number of actors is reached, or if the incoming actor already belongs + to an aggregate. + + If the aggregate belongs to a scene, adding an actor to the aggregate also adds the actor to that scene. + + If the actor already belongs to a scene, a warning is output and the call is ignored. You need to remove + the actor from the scene first, before adding it to the aggregate. + + \note When BVHStructure is provided the actor shapes are grouped together. + The scene query pruning structure inside PhysX SDK will store/update one + bound per actor. The scene queries against such an actor will query actor + bounds and then make a local space query against the provided BVH structure, which is in + actor's local space. + + \param [in] actor The actor that should be added to the aggregate + \param [in] bvhStructure BVHStructure for actor shapes. + return true if success + */ + virtual bool addActor(PxActor& actor, const PxBVHStructure* bvhStructure = NULL) = 0; + + /** + \brief Removes an actor from the aggregate object. + + A warning is output if the incoming actor does not belong to the aggregate. Otherwise the actor is + removed from the aggregate. If the aggregate belongs to a scene, the actor is reinserted in that + scene. If you intend to delete the actor, it is best to call #PxActor::release() directly. That way + the actor will be automatically removed from its aggregate (if any) and not reinserted in a scene. + + \param [in] actor The actor that should be removed from the aggregate + return true if success + */ + virtual bool removeActor(PxActor& actor) = 0; + + /** + \brief Adds an articulation to the aggregate object. + + A warning is output if the total number of actors is reached (every articulation link counts as an actor), + or if the incoming articulation already belongs to an aggregate. + + If the aggregate belongs to a scene, adding an articulation to the aggregate also adds the articulation to that scene. + + If the articulation already belongs to a scene, a warning is output and the call is ignored. You need to remove + the articulation from the scene first, before adding it to the aggregate. + + \param [in] articulation The articulation that should be added to the aggregate + return true if success + */ + virtual bool addArticulation(PxArticulationBase& articulation) = 0; + + /** + \brief Removes an articulation from the aggregate object. + + A warning is output if the incoming articulation does not belong to the aggregate. Otherwise the articulation is + removed from the aggregate. If the aggregate belongs to a scene, the articulation is reinserted in that + scene. If you intend to delete the articulation, it is best to call #PxArticulation::release() directly. That way + the articulation will be automatically removed from its aggregate (if any) and not reinserted in a scene. + + \param [in] articulation The articulation that should be removed from the aggregate + return true if success + */ + virtual bool removeArticulation(PxArticulationBase& articulation) = 0; + + /** + \brief Returns the number of actors contained in the aggregate. + + You can use #getActors() to retrieve the actor pointers. + + \return Number of actors contained in the aggregate. + + @see PxActor getActors() + */ + virtual PxU32 getNbActors() const = 0; + + /** + \brief Retrieves max amount of actors that can be contained in the aggregate. + + \return Max aggregate size. + + @see PxPhysics::createAggregate() + */ + virtual PxU32 getMaxNbActors() const = 0; + + /** + \brief Retrieve all actors contained in the aggregate. + + You can retrieve the number of actor pointers by calling #getNbActors() + + \param[out] userBuffer The buffer to store the actor pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first actor pointer to be retrieved + \return Number of actor pointers written to the buffer. + + @see PxShape getNbShapes() + */ + virtual PxU32 getActors(PxActor** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Retrieves the scene which this aggregate belongs to. + + \return Owner Scene. NULL if not part of a scene. + + @see PxScene + */ + virtual PxScene* getScene() = 0; + + /** + \brief Retrieves aggregate's self-collision flag. + + \return self-collision flag + */ + virtual bool getSelfCollision() const = 0; + + virtual const char* getConcreteTypeName() const { return "PxAggregate"; } + +protected: + PX_INLINE PxAggregate(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxAggregate(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxAggregate() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxAggregate", name) || PxBase::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulation.h b/src/PhysX/physx/include/PxArticulation.h new file mode 100644 index 000000000..fd4bc3e82 --- /dev/null +++ b/src/PhysX/physx/include/PxArticulation.h @@ -0,0 +1,281 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION +#define PX_PHYSICS_NX_ARTICULATION +/** \addtogroup physics +@{ */ + +#include "PxArticulationBase.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxArticulationLink; + +/** +\brief Articulation drive cache + +This cache is used for making one or more impulse applications to the articulation. + +@see PxArticulation PxArticulation.createDriveCache +*/ +class PxArticulationDriveCache +{ +protected: + PxArticulationDriveCache(); +}; + + +/** +\brief a tree structure of bodies connected by joints that is treated as a unit by the dynamics solver + +Articulations are more expensive to simulate than the equivalent collection of +PxRigidDynamic and PxJoint structures, but because the dynamics solver treats +each articulation as a single object, they are much less prone to separation and +have better support for actuation. An articulation may have at most 64 links. + +@see PxArticulationJoint PxArticulationLink PxPhysics.createArticulation +*/ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4435) +#endif + +class PxArticulation : public PxArticulationBase +{ +public: + + virtual void release() = 0; + + /** + \brief sets maxProjectionIterations. + + This is the maximum number of iterations to run projection on the articulation to bring + the links back together if the separation tolerance is exceeded. + + + \param[in] iterations the maximum number of projection iterations + Default: 4 + + @see getMaxProjectionIterations() + */ + virtual void setMaxProjectionIterations(PxU32 iterations) = 0; + + /** + \brief gets maxProjectionIterations. + + \return the maximum number of projection iterations + + @see setMaxProjectionIterations() + */ + + virtual PxU32 getMaxProjectionIterations() const = 0; + + /** + \brief sets separationTolerance. + + This is the maximum allowed separation of any joint in the articulation before projection is used + + Default: 0.1f, scaled by the tolerance scale + + \param[in] tolerance the separation tolerance for the articulation + + @see getSeparationTolerance() + */ + virtual void setSeparationTolerance(PxReal tolerance) = 0; + + /** + \brief gets separationTolerance. + + \return the separation tolerance + + @see setSeparationTolerance() + */ + + virtual PxReal getSeparationTolerance() const = 0; + + + /** + \brief sets the number of iterations used to compute the drive response to internal forces + + The drive model uses an iterative algorithm to determine the load on each joint of the articulation. + This is the number of iterations to use when computing response of the drive to internal forces. + + \param[in] iterations the number of iterations used to compute the drive response to internal forces. + + Default: 4 + + @see getInternalDriveIterations() + */ + virtual void setInternalDriveIterations(PxU32 iterations) = 0; + + /** + \brief gets internal driveIterations. + + \return the number of iterations used to compute the drive response to internal forces + + @see setInternalDriveIterations() + */ + + virtual PxU32 getInternalDriveIterations() const = 0; + + + /** + \brief sets the number of iterations for drive response to external forces. + + The drive model uses an iterative algorithm to determine the load on each joint of the articulation. + This is the number of iterations to use when computing response of the drive to external forces. + + \param[in] iterations the number of iterations used to compute the drive response to external forces. + + Default: 4 + + @see getExternalDriveIterations() + */ + + virtual void setExternalDriveIterations(PxU32 iterations) = 0; + + /** + \brief gets externalDriveIterations. + + \return the number of iterations used to compute the drive response to external forces + + @see setExternalDriveIterations() + */ + + virtual PxU32 getExternalDriveIterations() const = 0; + + /** + \brief create a drive cache for applying impulses which are propagated to the entire articulation + + \param[in] compliance the compliance value to use at all joints of the articulation. This is equivalent to the external compliance + parameter for articulation joints, as the impulse is treated as an external force + \param[in] driveIterations the number of iterations to use to evaluate the drive strengths + + \return a drive cache + + @see PxArticulationDriveCache updateDriveCache releaseDriveCache applyImpulse computeImpulseResponse + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + + */ + virtual PxArticulationDriveCache* + createDriveCache(PxReal compliance, PxU32 driveIterations) const = 0; + + + /** + \brief update a drive cache + + \param[in] driveCache the drive cache to update + \param[in] compliance the compliance value to use at all joints of the articulation. + \param[in] driveIterations the number of iterations to use to evaluate the drive strengths + + @see releaseDriveCache createDriveCache applyImpulse computeImpulseResponse + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + + */ + virtual void updateDriveCache(PxArticulationDriveCache& driveCache, + PxReal compliance, + PxU32 driveIterations) const = 0; + + /** + \brief release a drive cache + + \param[in] driveCache the drive cache to release + + @see createDriveCache updateDriveCache + */ + virtual void releaseDriveCache(PxArticulationDriveCache& driveCache) const = 0; + + /** + \brief apply an impulse to an entire articulation + + \param[in] link the link to which to apply the impulse + \param[in] driveCache the drive cache + \param[in] linearImpulse the linear impulse to apply + \param[in] angularImpulse the angular impulse to apply + + @see computeImpulseResponse + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + + */ + virtual void applyImpulse(PxArticulationLink* link, + const PxArticulationDriveCache& driveCache, + const PxVec3& linearImpulse, + const PxVec3& angularImpulse) = 0; + + /** + \brief determine the effect of applying an impulse to an entire articulation, without applying the impulse + + \param[in] link the link to which to apply the impulse + \param[out] linearResponse the change in linear velocity of the articulation link + \param[out] angularResponse the change in angular velocity of the articulation link + \param[in] driveCache the drive cache + \param[in] linearImpulse the linear impulse to apply + \param[in] angularImpulse the angular impulse to apply + + @see applyImpulse + + This call will wake up the articulation if it is asleep. + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + */ + + virtual void computeImpulseResponse(PxArticulationLink*link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const PxArticulationDriveCache& driveCache, + const PxVec3& linearImpulse, + const PxVec3& angularImpulse) const = 0; + +protected: + PX_INLINE PxArticulation(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationBase(concreteType, baseFlags){} + PX_INLINE PxArticulation(PxBaseFlags baseFlags) : PxArticulationBase(baseFlags){} + virtual ~PxArticulation() {} + +}; + +#if PX_VC +#pragma warning(pop) +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulationBase.h b/src/PhysX/physx/include/PxArticulationBase.h new file mode 100644 index 000000000..596ee4c5a --- /dev/null +++ b/src/PhysX/physx/include/PxArticulationBase.h @@ -0,0 +1,337 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION_BASE +#define PX_PHYSICS_NX_ARTICULATION_BASE +/** \addtogroup physics +@{ */ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + + class PxArticulationImpl; + + /** + \brief a tree structure of bodies connected by joints that is treated as a unit by the dynamics solver + + Articulations are more expensive to simulate than the equivalent collection of + PxRigidDynamic and PxJoint structures, but because the dynamics solver treats + each articulation as a single object, they are much less prone to separation and + have better support for actuation. An articulation may have at most 64 links. + + @see PxArticulationJoint PxArticulationLink PxPhysics.createArticulation + */ + class PxArticulationBase : public PxBase + { + public: + + enum Enum + { + eReducedCoordinate = 0, + eMaximumCoordinate = 1 + }; + + /** + \brief Retrieves the scene which this articulation belongs to. + + \return Owner Scene. NULL if not part of a scene. + + @see PxScene + */ + virtual PxScene* getScene() const = 0; + + /** + \brief Sets the solver iteration counts for the articulation. + + The solver iteration count determines how accurately joints and contacts are resolved. + If you are having trouble with jointed bodies oscillating and behaving erratically, then + setting a higher position iteration count may improve their stability. + + If intersecting bodies are being depenetrated too violently, increase the number of velocity + iterations. More velocity iterations will drive the relative exit velocity of the intersecting + objects closer to the correct value given the restitution. + + \param[in] minPositionIters Number of position iterations the solver should perform for this articulation. Range: [1,255] + \param[in] minVelocityIters Number of velocity iterations the solver should perform for this articulation. Range: [1,255] + + @see getSolverIterationCounts() + */ + virtual void setSolverIterationCounts(PxU32 minPositionIters, PxU32 minVelocityIters = 1) = 0; + + /** + \brief Retrieves the solver iteration counts. + + @see setSolverIterationCounts() + */ + virtual void getSolverIterationCounts(PxU32 & minPositionIters, PxU32 & minVelocityIters) const = 0; + + /** + \brief Returns true if this articulation is sleeping. + + When an actor does not move for a period of time, it is no longer simulated in order to save time. This state + is called sleeping. However, because the object automatically wakes up when it is either touched by an awake object, + or a sleep-affecting property is changed by the user, the entire sleep mechanism should be transparent to the user. + + An articulation can only go to sleep if all links are ready for sleeping. An articulation is guaranteed to be awake + if at least one of the following holds: + + \li The wake counter is positive (see #setWakeCounter()). + \li The linear or angular velocity of any link is non-zero. + \li A non-zero force or torque has been applied to the articulation or any of its links. + + If an articulation is sleeping, the following state is guaranteed: + + \li The wake counter is zero. + \li The linear and angular velocity of all links is zero. + \li There is no force update pending. + + When an articulation gets inserted into a scene, it will be considered asleep if all the points above hold, else it will + be treated as awake. + + If an articulation is asleep after the call to PxScene::fetchResults() returns, it is guaranteed that the poses of the + links were not changed. You can use this information to avoid updating the transforms of associated of dependent objects. + + \note It is invalid to use this method if the articulation has not been added to a scene already. + + \return True if the articulation is sleeping. + + @see isSleeping() wakeUp() putToSleep() getSleepThreshold() + */ + virtual bool isSleeping() const = 0; + + /** + \brief Sets the mass-normalized energy threshold below which an articulation may go to sleep. + + The articulation will sleep if the energy of each body is below this threshold. + + \param[in] threshold Energy below which an actor may go to sleep. Range: [0, PX_MAX_F32) + + @see isSleeping() getSleepThreshold() wakeUp() putToSleep() + */ + virtual void setSleepThreshold(PxReal threshold) = 0; + + /** + \brief Returns the mass-normalized energy below which an articulation may go to sleep. + + \return The energy threshold for sleeping. + + @see isSleeping() wakeUp() putToSleep() setSleepThreshold() + */ + virtual PxReal getSleepThreshold() const = 0; + + /** + \brief Sets the mass-normalized kinetic energy threshold below which an articulation may participate in stabilization. + + Articulation whose kinetic energy divided by their mass is above this threshold will not participate in stabilization. + + This value has no effect if PxSceneFlag::eENABLE_STABILIZATION was not enabled on the PxSceneDesc. + + Default: 0.01 * PxTolerancesScale::speed * PxTolerancesScale::speed + + \param[in] threshold Energy below which an actor may participate in stabilization. Range: [0,inf) + + @see getStabilizationThreshold() PxSceneFlag::eENABLE_STABILIZATION + */ + virtual void setStabilizationThreshold(PxReal threshold) = 0; + + /** + \brief Returns the mass-normalized kinetic energy below which an articulation may participate in stabilization. + + Articulations whose kinetic energy divided by their mass is above this threshold will not participate in stabilization. + + \return The energy threshold for participating in stabilization. + + @see setStabilizationThreshold() PxSceneFlag::eENABLE_STABILIZATION + */ + virtual PxReal getStabilizationThreshold() const = 0; + + /** + \brief Sets the wake counter for the articulation. + + The wake counter value determines the minimum amount of time until the articulation can be put to sleep. Please note + that an articulation will not be put to sleep if the energy is above the specified threshold (see #setSleepThreshold()) + or if other awake objects are touching it. + + \note Passing in a positive value will wake the articulation up automatically. + + Default: 0.4 (which corresponds to 20 frames for a time step of 0.02) + + \param[in] wakeCounterValue Wake counter value. Range: [0, PX_MAX_F32) + + @see isSleeping() getWakeCounter() + */ + virtual void setWakeCounter(PxReal wakeCounterValue) = 0; + + /** + \brief Returns the wake counter of the articulation. + + \return The wake counter of the articulation. + + @see isSleeping() setWakeCounter() + */ + virtual PxReal getWakeCounter() const = 0; + + /** + \brief Wakes up the articulation if it is sleeping. + + The articulation will get woken up and might cause other touching objects to wake up as well during the next simulation step. + + \note This will set the wake counter of the articulation to the value specified in #PxSceneDesc::wakeCounterResetValue. + + \note It is invalid to use this method if the articulation has not been added to a scene already. + + @see isSleeping() putToSleep() + */ + virtual void wakeUp() = 0; + + /** + \brief Forces the articulation to sleep. + + The articulation will stay asleep during the next simulation step if not touched by another non-sleeping actor. + + \note This will set any applied force, the velocity and the wake counter of all bodies in the articulation to zero. + + \note It is invalid to use this method if the articulation has not been added to a scene already. + + @see isSleeping() wakeUp() + */ + virtual void putToSleep() = 0; + + /** + \brief adds a link to the articulation with default attribute values. + + \param[in] parent the parent link of the articulation. Should be NULL if (and only if) this is the root link + \param[in] pose the initial pose of the new link. Must be a valid transform + + \return the new link, or NULL if the link cannot be created because the articulation has reached + its maximum link count (currently 64). + + @see PxArticulationLink + */ + + virtual PxArticulationLink* createLink(PxArticulationLink* parent, const PxTransform& pose) = 0; + + + /** + \brief returns the number of links in the articulation + */ + + virtual PxU32 getNbLinks() const = 0; + + /** + \brief returns the set of links in the articulation + + \param[in] userBuffer buffer into which to write an array of articulation link pointers + \param[in] bufferSize the size of the buffer. If this is not large enough to contain all the pointers to links, + only as many as will fit are written. + \param[in] startIndex Index of first link pointer to be retrieved + + \return the number of links written into the buffer. + + @see ArticulationLink + */ + + virtual PxU32 getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex = 0) const = 0; + + + /** + \brief Sets a name string for the object that can be retrieved with getName(). + + This is for debugging and is not used by the SDK. The string is not copied by the SDK, + only the pointer is stored. + + \param[in] name String to set the objects name to. + + @see getName() + */ + virtual void setName(const char* name) = 0; + + /** + \brief Retrieves the name string set with setName(). + + \return Name string associated with object. + + @see setName() + */ + virtual const char* getName() const = 0; + + /** + \brief Retrieves the axis aligned bounding box enclosing the articulation. + + \param[in] inflation Scale factor for computed world bounds. Box extents are multiplied by this value. + + \return The articulation's bounding box. + + @see PxBounds3 + */ + virtual PxBounds3 getWorldBounds(float inflation = 1.01f) const = 0; + + /** + \brief Retrieves the aggregate the articulation might be a part of. + + \return The aggregate the articulation is a part of, or NULL if the articulation does not belong to an aggregate. + + @see PxAggregate + */ + virtual PxAggregate* getAggregate() const = 0; + + virtual PxArticulationImpl* getImpl() = 0; + + virtual const PxArticulationImpl* getImpl() const = 0; + + virtual PxArticulationBase::Enum getType() const = 0; + + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. + + virtual ~PxArticulationBase() {} + + protected: + PX_INLINE PxArticulationBase(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationBase(PxBaseFlags baseFlags) : PxBase(baseFlags) {} +public: + virtual PxArticulationJointBase* createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame) = 0; + virtual void releaseArticulationJoint(PxArticulationJointBase* joint) = 0; + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + + /** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulationJoint.h b/src/PhysX/physx/include/PxArticulationJoint.h new file mode 100644 index 000000000..295c19fbe --- /dev/null +++ b/src/PhysX/physx/include/PxArticulationJoint.h @@ -0,0 +1,572 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION_JOINT +#define PX_PHYSICS_NX_ARTICULATION_JOINT +/** \addtogroup physics +@{ */ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxArticulationJointImpl; + +/** +\brief The type of joint drive to use for the articulation joint. + +Two drive models are currently supported. in the TARGET model, the drive spring displacement will be determined +as the rotation vector from the relative quaternion beetween child and parent, and the target quaternion. + +In the ERROR model, the drive spring displacement will be taken directly from the imaginary part of the relative +quaternion. This drive model requires more computation on the part of the application, but allows driving the joint +with a spring displacement that is more than a complete rotation. + +@see PxArticulationJoint +*/ + +struct PxArticulationJointDriveType +{ + enum Enum + { + eTARGET = 0, // use the quaternion as the drive target + eERROR = 1 // use the vector part of the quaternion as the drive error. + }; +}; + +struct PxArticulationAxis +{ + enum Enum + { + eTWIST = 0, + eSWING1 = 1, + eSWING2 = 2, + eX = 3, + eY = 4, + eZ = 5, + eCOUNT = 6 + }; +}; + +PX_FLAGS_OPERATORS(PxArticulationAxis::Enum, PxU8) + +struct PxArticulationMotion +{ + enum Enum + { + eLOCKED = 0, + eLIMITED = 1, + eFREE = 2 + }; +}; + +typedef PxFlags PxArticulationMotions; +PX_FLAGS_OPERATORS(PxArticulationMotion::Enum, PxU8) + +struct PxArticulationJointType +{ + enum Enum + { + ePRISMATIC = 0, + eREVOLUTE = 1, + eSPHERICAL = 2, + eFIX = 3, + eUNDEFINED = 4 + }; +}; + + +class PxArticulationJointBase : public PxBase +{ +public: + /** + \brief get the parent articulation link to which this articulation joint belongs + + \return the articulation link to which this joint belongs + */ + virtual PxArticulationLink& getParentArticulationLink() const = 0; + + /** + \brief set the joint pose in the parent frame + + \param[in] pose the joint pose in the parent frame + Default: the identity matrix + + @see getParentPose() + */ + + virtual void setParentPose(const PxTransform& pose) = 0; + + /** + \brief get the joint pose in the parent frame + + \return the joint pose in the parent frame + + @see setParentPose() + */ + + virtual PxTransform getParentPose() const = 0; + + /** + \brief get the child articulation link to which this articulation joint belongs + + \return the articulation link to which this joint belongs + */ + virtual PxArticulationLink& getChildArticulationLink() const = 0; + + + /** + \brief set the joint pose in the child frame + + \param[in] pose the joint pose in the child frame + Default: the identity matrix + + @see getChildPose() + */ + + virtual void setChildPose(const PxTransform& pose) = 0; + + /** + \brief get the joint pose in the child frame + + \return the joint pose in the child frame + + @see setChildPose() + */ + virtual PxTransform getChildPose() const = 0; + + virtual PxArticulationJointImpl* getImpl() = 0; + virtual const PxArticulationJointImpl* getImpl() const = 0; + + virtual ~PxArticulationJointBase() {} + +private: +protected: + PX_INLINE PxArticulationJointBase(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationJointBase(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationJointBase", name) || PxBase::isKindOf(name); } +}; + + + +/** +\brief a joint between two links in an articulation. + +The joint model is very similar to a PxSphericalJoint with swing and twist limits, +and an implicit drive model. + +@see PxArticulation PxArticulationLink +*/ + +class PxArticulationJoint : public PxArticulationJointBase +{ +public: + + /** + \brief set the target drive + + This is the target position for the joint drive, measured in the parent constraint frame. + + \param[in] orientation the target orientation for the joint + Range: a unit quaternion + Default: the identity quaternion + + @see getTargetOrientation() + */ + + virtual void setTargetOrientation(const PxQuat& orientation) = 0; + + /** + \brief get the target drive position + + \return the joint drive target position + + @see setTargetOrientation() + */ + virtual PxQuat getTargetOrientation() const = 0; + + /** + \brief set the target drive velocity + + This is the target velocity for the joint drive, measured in the parent constraint frame + + \param[in] velocity the target velocity for the joint + Default: the zero vector + + @see getTargetVelocity() + */ + virtual void setTargetVelocity(const PxVec3& velocity) = 0; + + /** + \brief get the target drive velocity + + \return the target velocity for the joint + + @see setTargetVelocity() + */ + virtual PxVec3 getTargetVelocity() const = 0; + + + /** + \brief set the drive type + + \param[in] driveType the drive type for the joint + Default: PxArticulationJointDriveType::eTARGET + + @see getDriveType() + */ + virtual void setDriveType(PxArticulationJointDriveType::Enum driveType) = 0; + + /** + \brief get the drive type + + \return the drive type + + @see setDriveType() + */ + virtual PxArticulationJointDriveType::Enum + getDriveType() const = 0; + + + + /** + \brief set the drive strength of the joint acceleration spring. + + The acceleration generated by the spring drive is proportional to + this value and the angle between the drive target position and the + current position. + + \param[in] spring the spring strength of the joint + Range: [0, PX_MAX_F32)
            + Default: 0.0 + + @see getStiffness() + */ + virtual void setStiffness(PxReal spring) = 0; + + /** + \brief get the drive strength of the joint acceleration spring + + \return the spring strength of the joint + + @see setStiffness() + */ + virtual PxReal getStiffness() const = 0; + + + /** + \brief set the damping of the joint acceleration spring + + The acceleration generated by the spring drive is proportional to + this value and the difference between the angular velocity of the + joint and the target drive velocity. + + \param[in] damping the damping of the joint drive + Range: [0, PX_MAX_F32)
            + Default: 0.0 + + @see getDamping() + */ + virtual void setDamping(PxReal damping) = 0; + + /** + \brief get the damping of the joint acceleration spring + + @see setDamping() + */ + + virtual PxReal getDamping() const = 0; + + /** + \brief set the internal compliance + + Compliance determines the extent to which the joint resists acceleration. + + There are separate values for resistance to accelerations caused by external + forces such as gravity and contact forces, and internal forces generated from + other joints. + + A low compliance means that forces have little effect, a compliance of 1 means + the joint does not resist such forces at all. + + \param[in] compliance the compliance to internal forces + Range: (0, 1] + Default: 0.0 + + @see getInternalCompliance() + */ + + virtual void setInternalCompliance(PxReal compliance) = 0; + + + /** + \brief get the internal compliance + + \return the compliance to internal forces + + @see setInternalCompliance() + */ + virtual PxReal getInternalCompliance() const = 0; + + /** + \brief get the drive external compliance + + Compliance determines the extent to which the joint resists acceleration. + + There are separate values for resistance to accelerations caused by external + forces such as gravity and contact forces, and internal forces generated from + other joints. + + A low compliance means that forces have little effect, a compliance of 1 means + the joint does not resist such forces at all. + + \param[in] compliance the compliance to external forces + Range: (0, 1] + Default: 0.0 + + @see getExternalCompliance() + */ + + virtual void setExternalCompliance(PxReal compliance) = 0; + + /** + \brief get the drive external compliance + + \return the compliance to external forces + + @see setExternalCompliance() + */ + virtual PxReal getExternalCompliance() const = 0; + + + + /** + \brief set the extents of the cone limit. The extents are measured in the frame + of the parent. + + Note that very small or highly elliptical limit cones may result in jitter. + + \param[in] zLimit the allowed extent of rotation around the z-axis + \param[in] yLimit the allowed extent of rotation around the y-axis + Range: ( (0, Pi), (0, Pi) ) + Default: (Pi/4, Pi/4) + + \note Please note the order of zLimit and yLimit. + */ + virtual void setSwingLimit(PxReal zLimit, PxReal yLimit) = 0; + + + /** + \brief get the extents for the swing limit cone + + \param[out] zLimit the allowed extent of rotation around the z-axis + \param[out] yLimit the allowed extent of rotation around the y-axis + + \note Please note the order of zLimit and yLimit. + + @see setSwingLimit() + */ + virtual void getSwingLimit(PxReal& zLimit, PxReal& yLimit) const = 0; + + + + /** + \brief set the tangential spring for the limit cone + Range: ([0, PX_MAX_F32), [0, PX_MAX_F32)) + Default: (0.0, 0.0) + */ + + virtual void setTangentialStiffness(PxReal spring) = 0; + + + /** + \brief get the tangential spring for the swing limit cone + + \return the tangential spring + + @see setTangentialStiffness() + */ + virtual PxReal getTangentialStiffness() const = 0; + + + /** + \brief set the tangential damping for the limit cone + Range: ([0, PX_MAX_F32), [0, PX_MAX_F32)) + Default: (0.0, 0.0) + */ + + virtual void setTangentialDamping(PxReal damping) = 0; + + + /** + \brief get the tangential damping for the swing limit cone + + \return the tangential damping + + @see setTangentialDamping() + */ + virtual PxReal getTangentialDamping() const = 0; + + + /** + \brief set the contact distance for the swing limit + + The contact distance should be less than either limit angle. + + Range: [0, Pi] + Default: 0.05 radians + + @see getSwingLimitContactDistance() + */ + + virtual void setSwingLimitContactDistance(PxReal contactDistance) = 0; + + + /** + \brief get the contact distance for the swing limit + + \return the contact distance for the swing limit cone + + @see setSwingLimitContactDistance() + */ + virtual PxReal getSwingLimitContactDistance() const = 0; + + + + /** + \brief set the flag which enables the swing limit + + \param[in] enabled whether the limit is enabled + Default: false + + @see getSwingLimitEnabled() + */ + virtual void setSwingLimitEnabled(bool enabled) = 0; + + /** + \brief get the flag which enables the swing limit + + \return whether the swing limit is enabled + + @see setSwingLimitEnabled() + */ + + virtual bool getSwingLimitEnabled() const = 0; + + + /** + \brief set the bounds of the twistLimit + + \param[in] lower the lower extent of the twist limit + \param[in] upper the upper extent of the twist limit + Range: (-Pi, Pi) + Default: (-Pi/4, Pi/4) + + The lower limit value must be less than the upper limit if the limit is enabled + + @see getTwistLimit() + */ + virtual void setTwistLimit(PxReal lower, PxReal upper) = 0; + + /** + \brief get the bounds of the twistLimit + + \param[out] lower the lower extent of the twist limit + \param[out] upper the upper extent of the twist limit + + @see setTwistLimit() + */ + + virtual void getTwistLimit(PxReal &lower, PxReal &upper) const = 0; + + /** + \brief set the flag which enables the twist limit + + \param[in] enabled whether the twist limit is enabled + Default: false + + @see getTwistLimitEnabled() + */ + virtual void setTwistLimitEnabled(bool enabled) = 0; + + /** + \brief get the twistLimitEnabled flag + + \return whether the twist limit is enabled + + @see setTwistLimitEnabled() + */ + + virtual bool getTwistLimitEnabled() const = 0; + + + /** + \brief set the contact distance for the swing limit + + The contact distance should be less than half the distance between the upper and lower limits. + + Range: [0, Pi) + Default: 0.05 radians + + @see getTwistLimitContactDistance() + */ + + virtual void setTwistLimitContactDistance(PxReal contactDistance) = 0; + + + /** + \brief get the contact distance for the swing limit + + \return the contact distance for the twist limit + + @see setTwistLimitContactDistance() + */ + virtual PxReal getTwistLimitContactDistance() const = 0; + + virtual const char* getConcreteTypeName() const { return "PxArticulationJoint"; } + +protected: + PX_INLINE PxArticulationJoint(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationJointBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationJoint(PxBaseFlags baseFlags) : PxArticulationJointBase(baseFlags) {} + virtual ~PxArticulationJoint() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationJoint", name) || PxArticulationJointBase::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h b/src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h new file mode 100644 index 000000000..2397da8d5 --- /dev/null +++ b/src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION_JOINT_RC +#define PX_PHYSICS_NX_ARTICULATION_JOINT_RC +/** \addtogroup physics +@{ */ + +#if 1 +#include "PxPhysXConfig.h" +#include "common/PxBase.h" +#include "PxArticulationJoint.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief a joint between two links in an articulation. + + The joint model is very similar to a PxSphericalJoint with swing and twist limits, + and an implicit drive model. + + @see PxArticulation PxArticulationLink + */ + + class PxArticulationJointReducedCoordinate : public PxArticulationJointBase + { + public: + + virtual void setJointType(PxArticulationJointType::Enum jointType) = 0; + virtual PxArticulationJointType::Enum getJointType() const = 0; + + virtual void setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) = 0; + virtual PxArticulationMotion::Enum getMotion(PxArticulationAxis::Enum axis) const = 0; + + virtual void setLimit(PxArticulationAxis::Enum axis, const PxReal lowLimit, const PxReal highLimit) = 0; + virtual void getLimit(PxArticulationAxis::Enum axis, PxReal& lowLimit, PxReal& highLimit) = 0; + virtual void setDrive(PxArticulationAxis::Enum axis, const PxReal stiffness, const PxReal damping, const PxReal maxForce, bool isAccelerationDrive = false) = 0; + virtual void getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration) = 0; + virtual void setDriveTarget(PxArticulationAxis::Enum axis, const PxReal target) = 0; + virtual void setDriveVelocity(PxArticulationAxis::Enum axis, const PxReal targetVel) = 0; + virtual PxReal getDriveTarget(PxArticulationAxis::Enum axis) = 0; + virtual PxReal getDriveVelocity(PxArticulationAxis::Enum axis) = 0; + + virtual void setFrictionCoefficient(const PxReal coefficient) = 0; + virtual PxReal getFrictionCoefficient() const = 0; + virtual const char* getConcreteTypeName() const { return "PxArticulationJointReducedCoordinate"; } + + virtual void setMaxJointVelocity(const PxReal maxJointV) = 0; + virtual PxReal getMaxJointVelocity() const = 0; + + protected: + PX_INLINE PxArticulationJointReducedCoordinate(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationJointBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationJointReducedCoordinate(PxBaseFlags baseFlags) : PxArticulationJointBase(baseFlags) {} + virtual ~PxArticulationJointReducedCoordinate() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationJointReducedCoordinate", name) || PxBase::isKindOf(name); } + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif + + /** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulationLink.h b/src/PhysX/physx/include/PxArticulationLink.h new file mode 100644 index 000000000..9b3faae68 --- /dev/null +++ b/src/PhysX/physx/include/PxArticulationLink.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION_LINK +#define PX_PHYSICS_NX_ARTICULATION_LINK +/** \addtogroup physics +@{ */ + +#include "PxPhysXConfig.h" +#include "PxArticulationJoint.h" +#include "PxRigidBody.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxArticulationBase; + +/** +\brief a component of an articulation that represents a rigid body + +A limited subset of the properties of PxRigidDynamic are supported. In particular, sleep properties +are attributes of the articulation rather than each individual body, damping and velocity limits +are not supported, and links may not be kinematic. + +@see PxArticulation PxArticulation.createLink PxArticulationJoint PxRigidBody +*/ + +class PxArticulationLink : public PxRigidBody +{ +public: + /** + \brief Deletes the articulation link. + + \note Only a leaf articulation link can be released + + Do not keep a reference to the deleted instance. + + @see PxArticulation::createLink() + */ + virtual void release() = 0; + + + /** + \brief get the articulation to which this articulation link belongs. This returns the base class. The application should + establish which articulation implementation this actually is and upcast to that type to access non-common functionality + + \return the articulation to which this link belongs + */ + virtual PxArticulationBase& getArticulation() const = 0; + + + /** + \brief Get the joint which connects this link to its parent. + + \return The joint connecting the link to the parent. NULL for the root link. + + @see PxArticulationJoint + */ + virtual PxArticulationJointBase* getInboundJoint() const = 0; + + /** + \brief Get the degree of freedom of the joint which connects this link to its parent. + + \return The degree of freeedom of the joint connecting the link to the parent. 0xffffffff for the root link. + + @see PxArticulationJoint + */ + virtual PxU32 getInboundJointDof() const = 0; + + /** + \brief Get number of child links. + + \return the number of child links + + @see getChildren() + */ + virtual PxU32 getNbChildren() const = 0; + + + /** + \brief Get low-level link index + + \return low-level index + */ + virtual PxU32 getLinkIndex() const = 0; + + /** + \brief Retrieve all the child links. + + \param[out] userBuffer The buffer to receive articulation link pointers. + \param[in] bufferSize Size of provided user buffer. + \return Number of articulation links written to the buffer. + \param[in] startIndex Index of first child pointer to be retrieved + + @see getNbChildren() + */ + virtual PxU32 getChildren(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + virtual const char* getConcreteTypeName() const { return "PxArticulationLink"; } + +protected: + PX_INLINE PxArticulationLink(PxType concreteType, PxBaseFlags baseFlags) : PxRigidBody(concreteType, baseFlags) {} + PX_INLINE PxArticulationLink(PxBaseFlags baseFlags) : PxRigidBody(baseFlags) {} + virtual ~PxArticulationLink() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationLink", name) || PxRigidBody::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxArticulationReducedCoordinate.h b/src/PhysX/physx/include/PxArticulationReducedCoordinate.h new file mode 100644 index 000000000..6314c0953 --- /dev/null +++ b/src/PhysX/physx/include/PxArticulationReducedCoordinate.h @@ -0,0 +1,409 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_ARTICULATION_RC +#define PX_PHYSICS_NX_ARTICULATION_RC +/** \addtogroup physics +@{ */ + +#include "PxArticulationBase.h" +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + namespace Cm + { + class SpatialVector; + } + + class PxContactJoint; + + struct PxArticulationFlag + { + enum Enum + { + eFIX_BASE = (1 << 1) + }; + }; + + + class PxJoint; + + typedef PxFlags PxArticulationFlags; + PX_FLAGS_OPERATORS(PxArticulationFlag::Enum, PxU8) + + //PxKinematicJacobian is in world space 6x6 matrix + class PxKinematicJacobian + { + public: + //in each single column, top part is angular term and bottom is linear term + PxReal j[6][6];//[column][row] + + PxU32 nbColumns; + }; + + struct PxArticulationRootLinkData + { + + PxTransform transform; + PxVec3 linVel; + PxVec3 angVel; + PxVec3 linAcel; + PxVec3 angAcel; + }; + + class PxArticulationCache + { + public: + enum Enum + { + eVELOCITY = (1 << 0), //!< The joint velocities this frame. Note, this is the accumulated joint velocities, not change in joint velocity. + eACCELERATION = (1 << 1), //!< The joint accelerations this frame. Delta velocity can be computed from acceleration * dt. + ePOSITION = (1 << 2), //!< The joint positions this frame. Note, this is the accumulated joint positions over frames, not change in joint position. + eFORCE = (1 << 3), //!< The joint forces this frame. Note, the application should provide these values for the forward dynamic. If the application is using inverse dynamic, this is the joint force returned. + eROOT = (1 << 4), //!< Root link transform, velocity and acceleration. Note, when the application call applyCache with eROOT flag, it won't apply root link's acceleration to the simulation + eALL = (eVELOCITY | eACCELERATION | ePOSITION| eROOT) + }; + PxArticulationCache() : coefficentMatrix(NULL), lambda(NULL) + {} + + Cm::SpatialVector* externalForces; // N total number of links + PxKinematicJacobian* jacobian; //this store jacobian matrix + PxReal* massMatrix; //N X N (dof X dof) + PxReal* jointVelocity; //N total Dofs + PxReal* jointAcceleration; //N total Dofs + PxReal* jointPosition; //N total Dofs + PxReal* jointForce; //N total Dofs + + //application need to allocate those memory and assign them to the cache + PxReal* coefficentMatrix; + PxReal* lambda; + + //root link data + PxArticulationRootLinkData rootLinkData; + + //These three members won't be set to zero when zeroCache get called + void* scratchMemory; //this is used for internal calculation + void* scratchAllocator; + PxU32 version; //cache version. If the articulation configulation change, the cache is invalid + + + }; + + typedef PxFlags PxArticulationCacheFlags; + PX_FLAGS_OPERATORS(PxArticulationCache::Enum, PxU8) + + + /** + \brief a tree structure of bodies connected by joints that is treated as a unit by the dynamics solver + + Articulations are more expensive to simulate than the equivalent collection of + PxRigidDynamic and PxJoint structures, but because the dynamics solver treats + each articulation as a single object, they are much less prone to separation and + have better support for actuation. An articulation may have at most 64 links. + + @see PxArticulationJoint PxArticulationLink PxPhysics.createArticulation + */ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4435) +#endif + + class PxArticulationReducedCoordinate : public PxArticulationBase + { + public: + + virtual void release() = 0; + + + /** + \brief Sets flags on the articulation + + \param[in] flags Articulation flags + + */ + virtual void setArticulationFlags(PxArticulationFlags flags) = 0; + + /** + \brief Raises or clears a flag on the articulation + + \param[in] flag The articulation flag + \param[in] value true/false indicating whether to raise or clear the flag + + */ + virtual void setArticulationFlag(PxArticulationFlag::Enum flag, bool value) = 0; + + /** + \brief return PxArticulationFlags + */ + virtual PxArticulationFlags getArticulationFlags() const = 0; + + /** + \brief returns the total Dofs of the articulation + */ + virtual PxU32 getDofs() const = 0; + + /** + \brief create an articulation cache + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + */ + virtual PxArticulationCache* createCache() const = 0; + + /** + \brief Get the size of the articulation cache + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + */ + virtual PxU32 getCacheDataSize() const = 0; + + /** + \brief zero all data in the articulation cache beside the cache version + + \note this call may only be made on articulations that are in a scene, and may not be made during simulation + */ + virtual void zeroCache(PxArticulationCache& cache) = 0; + + + /** + \brief apply the user defined data in the cache to the articulation system + + \param[in] cache articulation data. + \param[in] flag The mode to use when determine which value in the cache will be applied to the articulation + \param[in] autowake Specify if the call should wake up the articulation if it is currently asleep. If true and the current wake counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see createCache copyInternalStateToCache + */ + virtual void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake = true) = 0; + + /** + \brief copy the internal data of the articulation to the cache + + \param[in] cache articulation data + \param[in] flag this indicates what kind of data the articulation system need to copy to the cache + + @see createCache applyCache + */ + virtual void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const = 0; + + + /** + \brief release an articulation cache + + \param[in] cache the cache to release + + @see createCache applyCache copyInternalStateToCache + */ + virtual void releaseCache(PxArticulationCache& cache) const = 0; + + + /** + \brief reduce the maximum data format to the reduced internal data + \param[in] maximum joint data format + \param[out] reduced joint data format + */ + virtual void packJointData(const PxReal* maximum, PxReal* reduced) const = 0; + + /** + \brief turn the reduced internal data to maximum joint data format + \param[in] reduced joint data format + \param[out] maximum joint data format + */ + virtual void unpackJointData(const PxReal* reduced, PxReal* maximum) const = 0; + + /** + \brief initialize all the common data for inverse dynamic + */ + virtual void commonInit() const = 0; + + + /** + \brief determine the statically balance of the joint force of gravity for entire articulation. External force, joint velocity and joint acceleration + are set to zero, the joint force returned will be purely determined by gravity. + + \param[out] cache return joint forces which can counteract gravity force + + @see commonInit + */ + virtual void computeGeneralizedGravityForce(PxArticulationCache& cache) const = 0; + + /** + \brief determine coriolise and centrifugal force. External force, gravity and joint acceleration + are set to zero, the joint force return will be coriolise and centrifugal force for each joint. + + \param[in] cache data + + @see commonInit + */ + virtual void computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const = 0; + + /** + \brief determine joint force change caused by external force. Gravity, joint acceleration and joint velocity + are all set to zero. + + \param[in] cache data + + @see commonInit + */ + virtual void computeGeneralizedExternalForce(PxArticulationCache& cache) const = 0; + /** + \brief determine the joint acceleration for each joint + This is purely calculates the change in joint acceleration due to change in the joint force + + \param[in] cache articulation data + + @see commonInit + */ + virtual void computeJointAcceleration(PxArticulationCache& cache) const = 0; + + + /** + \brief determine the joint force + This is purely calculates the change in joint force due to change in the joint acceleration + This means gravity and joint velocity will be zero + + \param[in] cache return joint force + + @see commonInit + */ + virtual void computeJointForce(PxArticulationCache& cache) const = 0; + + /** + \brief compute the kinematic jacobian for each joint from end effector to the root in world space + \param[in] linkID is the end effector id + \param[in] cache return jacobian matrix + + @see commonInit + */ + virtual void computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const = 0; + + + /** + \brief compute the coefficent matrix for contact force. PxContactJoint is the contact point + \param[out] cache returs the coefficent matrix. Each column is the joint force effected by a contact based on impulse strength 1 + @see commonInit + */ + virtual void computeCoefficentMatrix(PxArticulationCache& cache) const = 0; + + + /** + \brief compute the lambda value when the test impulse is 1 + \param[in] initialState the initial state of the articulation system + \param[in] jointTorque M(q)*qddot + C(q,qdot) + g(q) + \param[in] maxIter maximum number of solver iterations to run. If the system converges, fewer iterations may be used. + \param[out] cache returns the coefficent matrix. Each column is the joint force effected by a contact based on impulse strength 1 + @see commonInit + */ + virtual bool computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxU32 maxIter) const = 0; + + /** + \brief compute the joint-space inertia matrix + \param[in] cache articulation data + + @see commonInit + */ + virtual void computeGeneralizedMassMatrix(PxArticulationCache& cache) const = 0; + + /** + \brief add loop joint to the articulation system for inverse dynamic + \param[in] joint required to add loop joint + + @see commonInit + */ + virtual void addLoopJoint(PxJoint* joint) = 0; + + /** + \brief remove loop joint from the articulation system + \param[in] joint required to remove loop joint + + @see commonInit + */ + virtual void removeLoopJoint(PxJoint* joint) = 0; + + /** + \brief returns the number of loop joints in the articulation + \return number of loop joints + */ + + virtual PxU32 getNbLoopJoints() const = 0; + + /** + \brief returns the set of loop constraints in the articulation + + \param[in] userBuffer buffer into which to write an array of constraints pointers + \param[in] bufferSize the size of the buffer. If this is not large enough to contain all the pointers to links, + only as many as will fit are written. + \param[in] startIndex Index of first link pointer to be retrieved + + \return the number of links written into the buffer. + + @see ArticulationLink + */ + + virtual PxU32 getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex = 0) const = 0; + + /** + \brief returns the required size of coeffient matrix in the articulation. The coefficient matrix is number of constraint(loop joints) by total dofs. Constraint Torque = transpose(K) * lambda(). Lambda is a vector of number of constraints + \return bite size of the coefficient matrix(nc * n) + */ + + virtual PxU32 getCoefficentMatrixSize() const = 0; + + /** + \brief teleport root link to a new location + \param[in] pose the new location of the root link + \param[in] autowake wake up the articulation system + + @see commonInit + */ + virtual void teleportRootLink(const PxTransform& pose, bool autowake) = 0; + + protected: + PX_INLINE PxArticulationReducedCoordinate(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationReducedCoordinate(PxBaseFlags baseFlags) : PxArticulationBase(baseFlags) {} + virtual ~PxArticulationReducedCoordinate() {} + }; + +#if PX_VC +#pragma warning(pop) +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + + + /** @} */ +#endif diff --git a/src/PhysX/physx/include/PxBatchQuery.h b/src/PhysX/physx/include/PxBatchQuery.h new file mode 100644 index 000000000..63f1e7209 --- /dev/null +++ b/src/PhysX/physx/include/PxBatchQuery.h @@ -0,0 +1,224 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENEQUERY +#define PX_PHYSICS_NX_SCENEQUERY +/** \addtogroup scenequery +@{ */ + +#include "PxPhysXConfig.h" +#include "PxShape.h" +#include "PxBatchQueryDesc.h" +#include "PxQueryFiltering.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxBoxGeometry; +class PxSphereGeometry; +struct PxQueryCache; + +/** +\brief Batched queries object. This is used to perform several queries at the same time. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 + +@see PxScene, PxScene.createBatchQuery +*/ +class PX_DEPRECATED PxBatchQuery +{ + public: + + /** + \brief Executes batched queries. + */ + virtual void execute() = 0; + + /** + \brief Gets the prefilter shader in use for this scene query. + + \return Prefilter shader. + + @see PxBatchQueryDesc.preFilterShade PxBatchQueryPreFilterShader + */ + virtual PxBatchQueryPreFilterShader getPreFilterShader() const = 0; + + /** + \brief Gets the postfilter shader in use for this scene query. + + \return Postfilter shader. + + @see PxBatchQueryDesc.preFilterShade PxBatchQueryPostFilterShader + */ + virtual PxBatchQueryPostFilterShader getPostFilterShader() const = 0; + + + /** + \brief Gets the shared global filter data in use for this scene query. + + \return Shared filter data for filter shader. + + @see getFilterShaderDataSize() PxBatchQueryDesc.filterShaderData PxBatchQueryPreFilterShader, PxBatchQueryPostFilterShader + */ + virtual const void* getFilterShaderData() const = 0; + + /** + \brief Gets the size of the shared global filter data (#PxSceneDesc.filterShaderData) + + \return Size of shared filter data [bytes]. + + @see getFilterShaderData() PxBatchQueryDesc.filterShaderDataSize PxBatchQueryPreFilterShader, PxBatchQueryPostFilterShader + */ + virtual PxU32 getFilterShaderDataSize() const = 0; + + /** + \brief Sets new user memory pointers. + + It is not possible to change the memory during query execute. + + @see PxBatchQueryDesc + */ + virtual void setUserMemory(const PxBatchQueryMemory&) = 0; + + /** + \brief Gets the user memory pointers. + + @see PxBatchQueryDesc + */ + virtual const PxBatchQueryMemory& getUserMemory() = 0; + + /** + \brief Releases PxBatchQuery from PxScene + + @see PxScene, PxScene.createBatchQuery + */ + virtual void release() = 0; + + /** + \brief Performs a raycast against objects in the scene, returns results in PxBatchQueryMemory::userRaycastResultBuffer + specified at PxBatchQuery creation time or via PxBatchQuery::setUserMemory call. + + \note Touching hits are not ordered. + \note Shooting a ray from within an object leads to different results depending on the shape type. Please check the details in article SceneQuery. User can ignore such objects by using one of the provided filter mechanisms. + + \param[in] origin Origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] distance Length of the ray. Needs to be larger than 0. + \param[in] maxTouchHits Maximum number of hits to record in the touch buffer for this query. Default=0 reports a single blocking hit. If maxTouchHits is set to 0 all hits are treated as blocking by default. + \param[in] hitFlags Specifies which properties per hit should be computed and returned in hit array and blocking hit. + \param[in] filterData Filtering data passed to the filer shader. See #PxQueryFilterData #PxBatchQueryPreFilterShader, #PxBatchQueryPostFilterShader + \param[in] userData User can pass any value in this argument, usually to identify this particular query + \param[in] cache Cached hit shape (optional). Query is tested against cached shape first. If no hit is found the ray gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + + \note This query call writes to a list associated with the query object and is NOT thread safe (for performance reasons there is no lock + and overlapping writes from different threads may result in undefined behavior). + + @see PxQueryFilterData PxBatchQueryPreFilterShader PxBatchQueryPostFilterShader PxRaycastHit PxScene::raycast + */ + virtual void raycast( + const PxVec3& origin, const PxVec3& unitDir, PxReal distance = PX_MAX_F32, PxU16 maxTouchHits = 0, + PxHitFlags hitFlags = PxHitFlag::eDEFAULT, + const PxQueryFilterData& filterData = PxQueryFilterData(), + void* userData = NULL, const PxQueryCache* cache = NULL) = 0; + + + /** + \brief Performs an overlap test of a given geometry against objects in the scene, returns results in PxBatchQueryMemory::userOverlapResultBuffer + specified at PxBatchQuery creation time or via PxBatchQuery::setUserMemory call. + + \note Filtering: returning eBLOCK from user filter for overlap queries will cause a warning (see #PxQueryHitType). + + \param[in] geometry Geometry of object to check for overlap (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the object. + \param[in] maxTouchHits Maximum number of hits to record in the touch buffer for this query. Default=0 reports a single blocking hit. If maxTouchHits is set to 0 all hits are treated as blocking by default. + \param[in] filterData Filtering data and simple logic. See #PxQueryFilterData #PxBatchQueryPreFilterShader, #PxBatchQueryPostFilterShader + \param[in] userData User can pass any value in this argument, usually to identify this particular query + \param[in] cache Cached hit shape (optional). Query is tested against cached shape first. If no hit is found the ray gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + + \note eBLOCK should not be returned from user filters for overlap(). Doing so will result in undefined behavior, and a warning will be issued. + \note If the PxQueryFlag::eNO_BLOCK flag is set, the eBLOCK will instead be automatically converted to an eTOUCH and the warning suppressed. + \note This query call writes to a list associated with the query object and is NOT thread safe (for performance reasons there is no lock + and overlapping writes from different threads may result in undefined behavior). + + @see PxQueryFilterData PxBatchQueryPreFilterShader PxBatchQueryPostFilterShader + */ + virtual void overlap( + const PxGeometry& geometry, const PxTransform& pose, PxU16 maxTouchHits = 0, + const PxQueryFilterData& filterData = PxQueryFilterData(), void* userData=NULL, const PxQueryCache* cache = NULL) = 0; + + /** + \brief Performs a sweep test against objects in the scene, returns results in PxBatchQueryMemory::userSweepResultBuffer + specified at PxBatchQuery creation time or via PxBatchQuery::setUserMemory call. + + \note Touching hits are not ordered. + \note If a shape from the scene is already overlapping with the query shape in its starting position, + the hit is returned unless eASSUME_NO_INITIAL_OVERLAP was specified. + + \param[in] geometry Geometry of object to sweep (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the sweep object. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. Will be clamped to PX_MAX_SWEEP_DISTANCE. + \param[in] maxTouchHits Maximum number of hits to record in the touch buffer for this query. Default=0 reports a single blocking hit. If maxTouchHits is set to 0 all hits are treated as blocking by default. + \param[in] hitFlags Specifies which properties per hit should be computed and returned in hit array and blocking hit. + \param[in] filterData Filtering data and simple logic. See #PxQueryFilterData #PxBatchQueryPreFilterShader, #PxBatchQueryPostFilterShader + \param[in] userData User can pass any value in this argument, usually to identify this particular query + \param[in] cache Cached hit shape (optional). Query is tested against cached shape first. If no hit is found the ray gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + Note: ePRECISE_SWEEP doesn't support inflation. Therefore the sweep will be performed with zero inflation. + + \note This query call writes to a list associated with the query object and is NOT thread safe (for performance reasons there is no lock + and overlapping writes from different threads may result in undefined behavior). + + @see PxHitFlags PxQueryFilterData PxBatchQueryPreFilterShader PxBatchQueryPostFilterShader PxSweepHit + */ + virtual void sweep( + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxU16 maxTouchHits = 0, PxHitFlags hitFlags = PxHitFlag::eDEFAULT, + const PxQueryFilterData& filterData = PxQueryFilterData(), void* userData=NULL, const PxQueryCache* cache = NULL, + const PxReal inflation = 0.f) = 0; + +protected: + virtual ~PxBatchQuery() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxBatchQueryDesc.h b/src/PhysX/physx/include/PxBatchQueryDesc.h new file mode 100644 index 000000000..185ca89a7 --- /dev/null +++ b/src/PhysX/physx/include/PxBatchQueryDesc.h @@ -0,0 +1,303 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENEQUERYDESC +#define PX_PHYSICS_NX_SCENEQUERYDESC +/** \addtogroup physics +@{ */ + +#include "PxPhysXConfig.h" +#include "PxClient.h" +#include "PxFiltering.h" +#include "PxQueryFiltering.h" +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxSweepHit; +struct PxRaycastHit; + +/** +\brief Batched query status. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 +*/ +struct PX_DEPRECATED PxBatchQueryStatus +{ + enum Enum + { + /** + \brief This is the initial state before a query starts. + */ + ePENDING = 0, + + /** + \brief The query is finished; results have been written into the result and hit buffers. + */ + eSUCCESS, + + /** + \brief The query results were incomplete due to touch hit buffer overflow. Blocking hit is still correct. + */ + eOVERFLOW + }; +}; + +/** +\brief Generic struct for receiving results of single query in a batch. Gets templated on hit type PxRaycastHit, PxSweepHit or PxOverlapHit. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 +*/ +template +struct PX_DEPRECATED PxBatchQueryResult +{ + HitType block; //!< Holds the closest blocking hit for a single query in a batch. Only valid if hasBlock is true. + HitType* touches; //!< This pointer will either be set to NULL for 0 nbTouches or will point + //!< into the user provided batch query results buffer specified in PxBatchQueryDesc. + PxU32 nbTouches; //!< Number of touching hits returned by this query, works in tandem with touches pointer. + void* userData; //!< Copy of the userData pointer specified in the corresponding query. + PxU8 queryStatus; //!< Takes on values from PxBatchQueryStatus::Enum. + bool hasBlock; //!< True if there was a blocking hit. + PxU16 pad; //!< pads the struct to 16 bytes. + + /** \brief Computes the number of any hits in this result, blocking or touching. */ + PX_INLINE PxU32 getNbAnyHits() const { return nbTouches + (hasBlock ? 1 : 0); } + + /** \brief Convenience iterator used to access any hits in this result, blocking or touching. */ + PX_INLINE const HitType& getAnyHit(const PxU32 index) const { PX_ASSERT(index < nbTouches + (hasBlock ? 1 : 0)); + return index < nbTouches ? touches[index] : block; } +}; + +/** \brief Convenience typedef for the result of a batched raycast query. */ +typedef PX_DEPRECATED PxBatchQueryResult PxRaycastQueryResult; + +/** \brief Convenience typedef for the result of a batched sweep query. */ +typedef PX_DEPRECATED PxBatchQueryResult PxSweepQueryResult; + +/** \brief Convenience typedef for the result of a batched overlap query. */ +typedef PX_DEPRECATED PxBatchQueryResult PxOverlapQueryResult; + +/** +\brief Struct for #PxBatchQuery memory pointers. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 + +@see PxBatchQuery PxBatchQueryDesc +*/ +struct PX_DEPRECATED PxBatchQueryMemory + { + /** + \brief The pointer to the user-allocated buffer for results of raycast queries in corresponding order of issue + + \note The size should be large enough to fit the number of expected raycast queries. + + @see PxRaycastQueryResult + */ + PxRaycastQueryResult* userRaycastResultBuffer; + + /** + \brief The pointer to the user-allocated buffer for raycast touch hits. + \note The size of this buffer should be large enough to store PxRaycastHit. + If the buffer is too small to store hits, the related PxRaycastQueryResult.queryStatus will be set to eOVERFLOW + + */ + PxRaycastHit* userRaycastTouchBuffer; + + /** + \brief The pointer to the user-allocated buffer for results of sweep queries in corresponding order of issue + + \note The size should be large enough to fit the number of expected sweep queries. + + @see PxRaycastQueryResult + */ + PxSweepQueryResult* userSweepResultBuffer; + + /** + \brief The pointer to the user-allocated buffer for sweep hits. + \note The size of this buffer should be large enough to store PxSweepHit. + If the buffer is too small to store hits, the related PxSweepQueryResult.queryStatus will be set to eOVERFLOW + + */ + PxSweepHit* userSweepTouchBuffer; + + /** + \brief The pointer to the user-allocated buffer for results of overlap queries in corresponding order of issue + + \note The size should be large enough to fit the number of expected overlap queries. + + @see PxRaycastQueryResult + */ + PxOverlapQueryResult* userOverlapResultBuffer; + + /** + \brief The pointer to the user-allocated buffer for overlap hits. + \note The size of this buffer should be large enough to store the hits returned. + If the buffer is too small to store hits, the related PxOverlapQueryResult.queryStatus will be set to eABORTED + + */ + PxOverlapHit* userOverlapTouchBuffer; + + /** \brief Capacity of the user-allocated userRaycastTouchBuffer in elements */ + PxU32 raycastTouchBufferSize; + + /** \brief Capacity of the user-allocated userSweepTouchBuffer in elements */ + PxU32 sweepTouchBufferSize; + + /** \brief Capacity of the user-allocated userOverlapTouchBuffer in elements */ + PxU32 overlapTouchBufferSize; + + /** \return Capacity of the user-allocated userRaycastResultBuffer in elements (max number of raycast() calls before execute() call) */ + PX_FORCE_INLINE PxU32 getMaxRaycastsPerExecute() const { return raycastResultBufferSize; } + + /** \return Capacity of the user-allocated userSweepResultBuffer in elements (max number of sweep() calls before execute() call) */ + PX_FORCE_INLINE PxU32 getMaxSweepsPerExecute() const { return sweepResultBufferSize; } + + /** \return Capacity of the user-allocated userOverlapResultBuffer in elements (max number of overlap() calls before execute() call) */ + PX_FORCE_INLINE PxU32 getMaxOverlapsPerExecute() const { return overlapResultBufferSize; } + + PxBatchQueryMemory(PxU32 raycastResultBufferSize_, PxU32 sweepResultBufferSize_, PxU32 overlapResultBufferSize_) : + userRaycastResultBuffer (NULL), + userRaycastTouchBuffer (NULL), + userSweepResultBuffer (NULL), + userSweepTouchBuffer (NULL), + userOverlapResultBuffer (NULL), + userOverlapTouchBuffer (NULL), + raycastTouchBufferSize (0), + sweepTouchBufferSize (0), + overlapTouchBufferSize (0), + raycastResultBufferSize (raycastResultBufferSize_), + sweepResultBufferSize (sweepResultBufferSize_), + overlapResultBufferSize (overlapResultBufferSize_) + { + } + +protected: + PxU32 raycastResultBufferSize; + PxU32 sweepResultBufferSize; + PxU32 overlapResultBufferSize; +}; + +/** +\brief Descriptor class for #PxBatchQuery. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 + +@see PxBatchQuery PxSceneQueryExecuteMode +*/ +class PX_DEPRECATED PxBatchQueryDesc +{ +public: + + /** + \brief Shared global filter data which will get passed into the filter shader. + + \note The provided data will get copied to internal buffers and this copy will be used for filtering calls. + + Default: NULL + + @see PxSimulationFilterShader + */ + void* filterShaderData; + + /** + \brief Size (in bytes) of the shared global filter data #filterShaderData. + + Default: 0 + + @see PxSimulationFilterShader filterShaderData + */ + PxU32 filterShaderDataSize; + + /** + \brief The custom preFilter shader to use for filtering. + + @see PxBatchQueryPreFilterShader PxDefaultPreFilterShader + */ + PxBatchQueryPreFilterShader preFilterShader; + + /** + \brief The custom postFilter shader to use for filtering. + + @see PxBatchQueryPostFilterShader PxDefaultPostFilterShader + */ + PxBatchQueryPostFilterShader postFilterShader; + + /** + \brief User memory buffers for the query. + + @see PxBatchQueryMemory + */ + PxBatchQueryMemory queryMemory; + + /** + \brief Construct a batch query with specified maximum number of queries per batch. + + If the number of raycasts/sweeps/overlaps per execute exceeds the limit, the query will be discarded with a warning. + + \param maxRaycastsPerExecute Maximum number of raycast() calls allowed before execute() call. + This has to match the amount of memory allocated for PxBatchQueryMemory::userRaycastResultBuffer. + \param maxSweepsPerExecute Maximum number of sweep() calls allowed before execute() call. + This has to match the amount of memory allocated for PxBatchQueryMemory::userSweepResultBuffer. + \param maxOverlapsPerExecute Maximum number of overlap() calls allowed before execute() call. + This has to match the amount of memory allocated for PxBatchQueryMemory::userOverlapResultBuffer. + */ + PX_INLINE PxBatchQueryDesc(PxU32 maxRaycastsPerExecute, PxU32 maxSweepsPerExecute, PxU32 maxOverlapsPerExecute); + PX_INLINE bool isValid() const; +}; + + +PX_INLINE PxBatchQueryDesc::PxBatchQueryDesc(PxU32 maxRaycastsPerExecute, PxU32 maxSweepsPerExecute, PxU32 maxOverlapsPerExecute) : + filterShaderData (NULL), + filterShaderDataSize (0), + preFilterShader (NULL), + postFilterShader (NULL), + queryMemory (maxRaycastsPerExecute, maxSweepsPerExecute, maxOverlapsPerExecute) +{ +} + + +PX_INLINE bool PxBatchQueryDesc::isValid() const +{ + if ( ((filterShaderDataSize == 0) && (filterShaderData != NULL)) || + ((filterShaderDataSize > 0) && (filterShaderData == NULL)) ) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxBroadPhase.h b/src/PhysX/physx/include/PxBroadPhase.h new file mode 100644 index 000000000..39d3c4305 --- /dev/null +++ b/src/PhysX/physx/include/PxBroadPhase.h @@ -0,0 +1,174 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_BROAD_PHASE_H +#define PX_PHYSICS_BROAD_PHASE_H +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxBounds3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxActor; + + /** + \brief Broad phase algorithm used in the simulation + + eSAP is a good generic choice with great performance when many objects are sleeping. Performance + can degrade significantly though, when all objects are moving, or when large numbers of objects + are added to or removed from the broad phase. This algorithm does not need world bounds to be + defined in order to work. + + eMBP is an alternative broad phase algorithm that does not suffer from the same performance + issues as eSAP when all objects are moving or when inserting large numbers of objects. However + its generic performance when many objects are sleeping might be inferior to eSAP, and it requires + users to define world bounds in order to work. + + eABP is a revisited implementation of MBP, which automatically manages broad-phase regions. + It offers the convenience of eSAP (no need to define world bounds or regions) and the performance + of eMBP when a lot of objects are moving. While eSAP can remain faster when most objects are + sleeping and eMBP can remain faster when it uses a large number of properly-defined regions, + eABP often gives the best performance on average and the best memory usage. + */ + struct PxBroadPhaseType + { + enum Enum + { + eSAP, //!< 3-axes sweep-and-prune + eMBP, //!< Multi box pruning + eABP, //!< Automatic box pruning + eGPU, + + eLAST + }; + }; + + /** + \brief Broad-phase callback to receive broad-phase related events. + + Each broadphase callback object is associated with a PxClientID. It is possible to register different + callbacks for different clients. The callback functions are called this way: + - for shapes/actors, the callback assigned to the actors' clients are used + - for aggregates, the callbacks assigned to clients from aggregated actors are used + + \note SDK state should not be modified from within the callbacks. In particular objects should not + be created or destroyed. If state modification is needed then the changes should be stored to a buffer + and performed after the simulation step. + + Threading: It is not necessary to make this class thread safe as it will only be called in the context of the + user thread. + + @see PxSceneDesc PxScene.setBroadPhaseCallback() PxScene.getBroadPhaseCallback() + */ + class PxBroadPhaseCallback + { + public: + virtual ~PxBroadPhaseCallback() {} + + /** + \brief Out-of-bounds notification. + + This function is called when an object leaves the broad-phase. + + \param[in] shape Shape that left the broad-phase bounds + \param[in] actor Owner actor + */ + virtual void onObjectOutOfBounds(PxShape& shape, PxActor& actor) = 0; + + /** + \brief Out-of-bounds notification. + + This function is called when an aggregate leaves the broad-phase. + + \param[in] aggregate Aggregate that left the broad-phase bounds + */ + virtual void onObjectOutOfBounds(PxAggregate& aggregate) = 0; + }; + + /** + \brief "Region of interest" for the broad-phase. + + This is currently only used for the PxBroadPhaseType::eMBP broad-phase, which requires zones or regions to be defined + when the simulation starts in order to work. Regions can overlap and be added or removed at runtime, but at least one + region needs to be defined when the scene is created. + + If objects that do no overlap any region are inserted into the scene, they will not be added to the broad-phase and + thus collisions will be disabled for them. A PxBroadPhaseCallback out-of-bounds notification will be sent for each one + of those objects. + + The total number of regions is limited by PxBroadPhaseCaps::maxNbRegions. + + The number of regions has a direct impact on performance and memory usage, so it is recommended to experiment with + various settings to find the best combination for your game. A good default setup is to start with global bounds + around the whole world, and subdivide these bounds into 4*4 regions. The PxBroadPhaseExt::createRegionsFromWorldBounds + function can do that for you. + + @see PxBroadPhaseCallback PxBroadPhaseExt.createRegionsFromWorldBounds + */ + struct PxBroadPhaseRegion + { + PxBounds3 bounds; //!< Region's bounds + void* userData; //!< Region's user-provided data + }; + + /** + \brief Information & stats structure for a region + */ + struct PxBroadPhaseRegionInfo + { + PxBroadPhaseRegion region; //!< User-provided region data + PxU32 nbStaticObjects; //!< Number of static objects in the region + PxU32 nbDynamicObjects; //!< Number of dynamic objects in the region + bool active; //!< True if region is currently used, i.e. it has not been removed + bool overlap; //!< True if region overlaps other regions (regions that are just touching are not considering overlapping) + }; + + /** + \brief Caps class for broad phase. + */ + struct PxBroadPhaseCaps + { + PxU32 maxNbRegions; //!< Max number of regions supported by the broad-phase + PxU32 maxNbObjects; //!< Max number of objects supported by the broad-phase + bool needsPredefinedBounds; //!< If true, broad-phase needs 'regions' to work + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxClient.h b/src/PhysX/physx/include/PxClient.h new file mode 100644 index 000000000..4124add51 --- /dev/null +++ b/src/PhysX/physx/include/PxClient.h @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_CLIENT +#define PX_PHYSICS_NX_CLIENT + +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief An ID to identify different clients for multiclient support. + +@see PxScene::createClient() +*/ +typedef PxU8 PxClientID; + +/** +\brief The predefined default PxClientID value. + +@see PxClientID PxScene::createClient() +*/ +static const PxClientID PX_DEFAULT_CLIENT = 0; + +/** +\brief The maximum number of clients we support. + +@see PxClientID PxScene::createClient() +*/ +static const PxClientID PX_MAX_CLIENTS = 128; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif diff --git a/src/PhysX/physx/include/PxConstraint.h b/src/PhysX/physx/include/PxConstraint.h new file mode 100644 index 000000000..f0fb733c0 --- /dev/null +++ b/src/PhysX/physx/include/PxConstraint.h @@ -0,0 +1,280 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NX_CONSTRAINT +#define PX_PHYSICS_NX_CONSTRAINT + +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxConstraintDesc.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxRigidActor; +class PxScene; +class PxConstraintConnector; + +/** +\brief a table of function pointers for a constraint + +@see PxConstraint +*/ + +/** +\brief constraint flags + +\note eBROKEN is a read only flag +*/ + +struct PxConstraintFlag +{ + enum Enum + { + eBROKEN = 1<<0, //!< whether the constraint is broken + ePROJECT_TO_ACTOR0 = 1<<1, //!< whether actor1 should get projected to actor0 for this constraint (note: projection of a static/kinematic actor to a dynamic actor will be ignored) + ePROJECT_TO_ACTOR1 = 1<<2, //!< whether actor0 should get projected to actor1 for this constraint (note: projection of a static/kinematic actor to a dynamic actor will be ignored) + ePROJECTION = ePROJECT_TO_ACTOR0 | ePROJECT_TO_ACTOR1, //!< whether the actors should get projected for this constraint (the direction will be chosen by PhysX) + eCOLLISION_ENABLED = 1<<3, //!< whether contacts should be generated between the objects this constraint constrains + eVISUALIZATION = 1<<4, //!< whether this constraint should be visualized, if constraint visualization is turned on + eDRIVE_LIMITS_ARE_FORCES = 1<<5, //!< limits for drive strength are forces rather than impulses + eIMPROVED_SLERP = 1<<7, //!< perform preprocessing for improved accuracy on D6 Slerp Drive (this flag will be removed in a future release when preprocessing is no longer required) + eDISABLE_PREPROCESSING = 1<<8, //!< suppress constraint preprocessing, intended for use with rowResponseThreshold. May result in worse solver accuracy for ill-conditioned constraints. + eENABLE_EXTENDED_LIMITS = 1<<9, //!< enables extended limit ranges for angular limits (e.g. limit values > PxPi or < -PxPi) + eGPU_COMPATIBLE = 1<<10 //!< the constraint type is supported by gpu dynamic + }; +}; + +/** +\brief constraint flags +@see PxConstraintFlag +*/ + +typedef PxFlags PxConstraintFlags; +PX_FLAGS_OPERATORS(PxConstraintFlag::Enum, PxU16) + +struct PxConstraintShaderTable +{ + enum + { + eMAX_SOLVERPRPEP_DATASIZE=400 + }; + + PxConstraintSolverPrep solverPrep; //!< solver constraint generation function + PxConstraintProject project; //!< constraint projection function + PxConstraintVisualize visualize; //!< constraint visualization function + PxConstraintFlag::Enum flag; //!< gpu constraint +}; + + +/** +\brief A plugin class for implementing constraints + +@see PxPhysics.createConstraint +*/ + +class PxConstraint : public PxBase +{ +public: + + /** + \brief Releases a PxConstraint instance. + + \note This call does not wake up the connected rigid bodies. + + @see PxPhysics.createConstraint, PxBase.release() + */ + virtual void release() = 0; + + /** + \brief Retrieves the scene which this constraint belongs to. + + \return Owner Scene. NULL if not part of a scene. + + @see PxScene + */ + virtual PxScene* getScene() const = 0; + + /** + \brief Retrieves the actors for this constraint. + + \param[out] actor0 a reference to the pointer for the first actor + \param[out] actor1 a reference to the pointer for the second actor + + @see PxActor + */ + virtual void getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const = 0; + + /** + \brief Sets the actors for this constraint. + + \param[in] actor0 a reference to the pointer for the first actor + \param[in] actor1 a reference to the pointer for the second actor + + @see PxActor + */ + virtual void setActors(PxRigidActor* actor0, PxRigidActor* actor1) = 0; + + /** + \brief Notify the scene that the constraint shader data has been updated by the application + */ + virtual void markDirty() = 0; + + /** + \brief Set the flags for this constraint + + \param[in] flags the new constraint flags + + default: PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES + + @see PxConstraintFlags + */ + virtual void setFlags(PxConstraintFlags flags) = 0; + + /** + \brief Retrieve the flags for this constraint + + \return the constraint flags + @see PxConstraintFlags + */ + virtual PxConstraintFlags getFlags() const = 0; + + /** + \brief Set a flag for this constraint + + \param[in] flag the constraint flag + \param[in] value the new value of the flag + + @see PxConstraintFlags + */ + virtual void setFlag(PxConstraintFlag::Enum flag, bool value) = 0; + + /** + \brief Retrieve the constraint force most recently applied to maintain this constraint. + + \param[out] linear the constraint force + \param[out] angular the constraint torque + */ + virtual void getForce(PxVec3& linear, PxVec3& angular) const = 0; + + /** + \brief whether the constraint is valid. + + A constraint is valid if it has at least one dynamic rigid body or articulation link. A constraint that + is not valid may not be inserted into a scene, and therefore a static actor to which an invalid constraint + is attached may not be inserted into a scene. + + Invalid constraints arise only when an actor to which the constraint is attached has been deleted. + */ + virtual bool isValid() const = 0; + + /** + \brief Set the break force and torque thresholds for this constraint. + + If either the force or torque measured at the constraint exceed these thresholds the constraint will break. + + \param[in] linear the linear break threshold + \param[in] angular the angular break threshold + */ + virtual void setBreakForce(PxReal linear, PxReal angular) = 0; + + /** + \brief Retrieve the constraint break force and torque thresholds + + \param[out] linear the linear break threshold + \param[out] angular the angular break threshold + */ + virtual void getBreakForce(PxReal& linear, PxReal& angular) const = 0; + + /** + \brief Set the minimum response threshold for a constraint row + + When using mass modification for a joint or infinite inertia for a jointed body, very stiff solver constraints can be generated which + can destabilize simulation. Setting this value to a small positive value (e.g. 1e-8) will cause constraint rows to be ignored if very + large changes in impulses will generate only small changes in velocity. When setting this value, also set + PxConstraintFlag::eDISABLE_PREPROCESSING. The solver accuracy for this joint may be reduced. + + \param[in] threshold the minimum response threshold + + @see PxConstraintFlag::eDISABLE_PREPROCESSING + */ + virtual void setMinResponseThreshold(PxReal threshold) = 0; + + /** + \brief Retrieve the constraint break force and torque thresholds + + \return the minimum response threshold for a constraint row + */ + virtual PxReal getMinResponseThreshold() const = 0; + + /** + \brief Fetch external owner of the constraint. + + Provides a reference to the external owner of a constraint and a unique owner type ID. + + \param[out] typeID Unique type identifier of the external object. + \return Reference to the external object which owns the constraint. + + @see PxConstraintConnector.getExternalReference() + */ + virtual void* getExternalReference(PxU32& typeID) = 0; + + /** + \brief Set the constraint functions for this constraint + + \param[in] connector the constraint connector object by which the SDK communicates with the constraint. + \param[in] shaders the shader table for the constraint + + @see PxConstraintConnector PxConstraintSolverPrep PxConstraintProject PxConstraintVisualize + */ + virtual void setConstraintFunctions(PxConstraintConnector& connector, + const PxConstraintShaderTable& shaders) = 0; + + virtual const char* getConcreteTypeName() const { return "PxConstraint"; } + +protected: + PX_INLINE PxConstraint(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxConstraint(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxConstraint() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxConstraint", name) || PxBase::isKindOf(name); } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxConstraintDesc.h b/src/PhysX/physx/include/PxConstraintDesc.h new file mode 100644 index 000000000..b469b55b5 --- /dev/null +++ b/src/PhysX/physx/include/PxConstraintDesc.h @@ -0,0 +1,443 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NX_CONSTRAINTDESC +#define PX_PHYSICS_NX_CONSTRAINTDESC + +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxFlags.h" +#include "foundation/PxVec3.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx { namespace pvdsdk { +#endif + class PvdDataStream; +#if !PX_DOXYGEN +}} +#endif + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxConstraintConnector; +class PxRigidActor; +class PxScene; +class PxConstraintConnector; +class PxRenderBuffer; +class PxDeletionListener; + +/** + \brief constraint row flags + + These flags configure the post-processing of constraint rows and the behavior of the solver while solving constraints +*/ +struct Px1DConstraintFlag +{ + PX_CUDA_CALLABLE Px1DConstraintFlag(){} + + enum Type + { + eSPRING = 1<<0, //!< whether the constraint is a spring. Mutually exclusive with eRESTITUTION. If set, eKEEPBIAS is ignored. + eACCELERATION_SPRING = 1<<1, //!< whether the constraint is a force or acceleration spring. Only valid if eSPRING is set. + eRESTITUTION = 1<<2, //!< whether the restitution model should be applied to generate the target velocity. Mutually exclusive with eSPRING. If restitution causes a bounces, eKEEPBIAS is ignored + eKEEPBIAS = 1<<3, //!< whether to keep the error term when solving for velocity. Ignored if restitution generates bounce, or eSPRING is set. + eOUTPUT_FORCE = 1<<4, //!< whether to accumulate the force value from this constraint in the force total that is reported for the constraint and tested for breakage + eHAS_DRIVE_LIMIT = 1<<5, //!< whether the constraint has a drive force limit (which will be scaled by dt unless PxConstraintFlag::eLIMITS_ARE_FORCES is set) + eANGULAR_CONSTRAINT = 1 << 6,//!< Whether this is an angular or linear constraint + eDRIVE_ROW = 1 << 7 + }; +}; + +typedef PxFlags Px1DConstraintFlags; +PX_FLAGS_OPERATORS(Px1DConstraintFlag::Type, PxU16) + +/** +\brief constraint type hints which the solver uses to optimize constraint handling +*/ +struct PxConstraintSolveHint +{ + enum Enum + { + eNONE = 0, //!< no special properties + eACCELERATION1 = 256, //!< a group of acceleration drive constraints with the same stiffness and drive parameters + eSLERP_SPRING = 258, //!< temporary special value to identify SLERP drive rows + eACCELERATION2 = 512, //!< a group of acceleration drive constraints with the same stiffness and drive parameters + eACCELERATION3 = 768, //!< a group of acceleration drive constraints with the same stiffness and drive parameters + eROTATIONAL_EQUALITY = 1024, //!< rotational equality constraints with no force limit and no velocity target + eROTATIONAL_INEQUALITY = 1025, //!< rotational inequality constraints with (0, PX_MAX_FLT) force limits + eEQUALITY = 2048, //!< equality constraints with no force limit and no velocity target + eINEQUALITY = 2049 //!< inequality constraints with (0, PX_MAX_FLT) force limits + }; +}; + +/** +\brief A constraint + +A constraint is expressed as a set of 1-dimensional constraint rows which define the required constraint +on the objects' velocities. + +Each constraint is either a hard constraint or a spring. We define the velocity at the constraint to be +the quantity + + v = body0vel.dot(lin0,ang0) - body1vel.dot(lin1, ang1) + +For a hard constraint, the solver attempts to generate + +1. a set of velocities for the objects which, when integrated, respect the constraint errors: + + v + (geometricError / timestep) = velocityTarget + +2. a set of velocities for the objects which respect the constraints: + + v = velocityTarget + +Hard constraints support restitution: if the impact velocity exceeds the bounce threshold, then the target velocity +of the constraint will be set to restitution * -v + +Alternatively, the solver can attempt to resolve the velocity constraint as an implicit spring: + + F = stiffness * -geometricError + damping * (velocityTarget - v) + +where F is the constraint force or acceleration. Springs are fully implicit: that is, the force or acceleration +is a function of the position and velocity after the solve. + +All constraints support limits on the minimum or maximum impulse applied. +*/ + +PX_ALIGN_PREFIX(16) +struct Px1DConstraint +{ + PxVec3 linear0; //!< linear component of velocity jacobian in world space + PxReal geometricError; //!< geometric error of the constraint along this axis + PxVec3 angular0; //!< angular component of velocity jacobian in world space + PxReal velocityTarget; //!< velocity target for the constraint along this axis + + PxVec3 linear1; //!< linear component of velocity jacobian in world space + PxReal minImpulse; //!< minimum impulse the solver may apply to enforce this constraint + PxVec3 angular1; //!< angular component of velocity jacobian in world space + PxReal maxImpulse; //!< maximum impulse the solver may apply to enforce this constraint + + union + { + struct SpringModifiers + { + PxReal stiffness; //!< spring parameter, for spring constraints + PxReal damping; //!< damping parameter, for spring constraints + } spring; + struct RestitutionModifiers + { + PxReal restitution; //!< restitution parameter for determining additional "bounce" + PxReal velocityThreshold; //!< minimum impact velocity for bounce + } bounce; + } mods; + + PxReal forInternalUse; //!< for internal use only + PxU16 flags; //!< a set of Px1DConstraintFlags + PxU16 solveHint; //!< constraint optimization hint, should be an element of PxConstraintSolveHint +} +PX_ALIGN_SUFFIX(16); + + +/** +\brief Flags for determining which components of the constraint should be visualized. + +@see PxConstraintVisualize +*/ +struct PxConstraintVisualizationFlag +{ + enum Enum + { + eLOCAL_FRAMES = 1, //!< visualize constraint frames + eLIMITS = 2 //!< visualize constraint limits + }; +}; + +PX_ALIGN_PREFIX(16) +struct PxConstraintInvMassScale +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PxReal linear0; //!< multiplier for inverse mass of body0 + PxReal angular0; //!< multiplier for inverse MoI of body0 + PxReal linear1; //!< multiplier for inverse mass of body1 + PxReal angular1; //!< multiplier for inverse MoI of body1 + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxConstraintInvMassScale(){} + PX_CUDA_CALLABLE PX_FORCE_INLINE PxConstraintInvMassScale(PxReal lin0, PxReal ang0, PxReal lin1, PxReal ang1) : linear0(lin0), angular0(ang0), linear1(lin1), angular1(ang1){} +} +PX_ALIGN_SUFFIX(16); + +/** solver constraint generation shader + +This function is called by the constraint solver framework. The function must be reentrant, since it may be called simultaneously +from multiple threads, and should access only the arguments passed into it. + +Developers writing custom constraints are encouraged to read the documentation in the user guide and the implementation code in PhysXExtensions. + +\param[out] constraints An array of solver constraint rows to be filled in +\param[out] bodyAWorldOffset The origin point (offset from the position vector of bodyA's center of mass) at which the constraint is resolved. This value does not affect how constraints are solved, only the constraint force reported. +\param[in] maxConstraints The size of the constraint buffer. At most this many constraints rows may be written +\param[out] invMassScale The inverse mass and inertia scales for the constraint +\param[in] constantBlock The constant data block +\param[in] bodyAToWorld The center of mass frame of the first constrained body (the identity transform if the first actor is static, or if a NULL actor pointer was provided for it) +\param[in] bodyBToWorld The center of mass frame of the second constrained body (the identity transform if the second actor is static, or if a NULL actor pointer was provided for it) +\param[in] useExtendedLimits Enables limit ranges outside of (-PI, PI) +\param[out] cAtW The world space location of body A's joint frame (position only) +\param[out] cBtW The world space location of body B's joint frame (position only) + +\return the number of constraint rows written. +*/ +typedef PxU32 (*PxConstraintSolverPrep)(Px1DConstraint* constraints, + PxVec3& bodyAWorldOffset, + PxU32 maxConstraints, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bodyAToWorld, + const PxTransform& bodyBToWorld, + bool useExtendedLimits, + PxVec3& cAtW, + PxVec3& cBtW); + +/** solver constraint projection shader + +This function is called by the constraint post-solver framework. The function must be reentrant, since it may be called simultaneously +from multiple threads and should access only the arguments passed into it. + +\param[in] constantBlock The constant data block +\param[out] bodyAToWorld The center of mass frame of the first constrained body (the identity if the actor is static or a NULL pointer was provided for it) +\param[out] bodyBToWorld The center of mass frame of the second constrained body (the identity if the actor is static or a NULL pointer was provided for it) +\param[in] projectToA True if the constraint should be projected by moving the second body towards the first, false if the converse +*/ +typedef void (*PxConstraintProject)(const void* constantBlock, + PxTransform& bodyAToWorld, + PxTransform& bodyBToWorld, + bool projectToA); + +/** + API used to visualize details about a constraint. +*/ +class PxConstraintVisualizer +{ +protected: + virtual ~PxConstraintVisualizer(){} +public: + /** Visualize joint frames + + \param[in] parent Parent transformation + \param[in] child Child transformation + */ + virtual void visualizeJointFrames(const PxTransform& parent, const PxTransform& child) = 0; + + /** Visualize joint linear limit + + \param[in] t0 Base transformation + \param[in] t1 End transformation + \param[in] value Distance + \param[in] active State of the joint - active/inactive + */ + virtual void visualizeLinearLimit(const PxTransform& t0, const PxTransform& t1, PxReal value, bool active) = 0; + + /** Visualize joint angular limit + + \param[in] t0 Transformation for the visualization + \param[in] lower Lower limit angle + \param[in] upper Upper limit angle + \param[in] active State of the joint - active/inactive + */ + virtual void visualizeAngularLimit(const PxTransform& t0, PxReal lower, PxReal upper, bool active) = 0; + + /** Visualize limit cone + + \param[in] t Transformation for the visualization + \param[in] tanQSwingY Tangent of the quarter Y angle + \param[in] tanQSwingZ Tangent of the quarter Z angle + \param[in] active State of the joint - active/inactive + */ + virtual void visualizeLimitCone(const PxTransform& t, PxReal tanQSwingY, PxReal tanQSwingZ, bool active) = 0; + + /** Visualize joint double cone + + \param[in] t Transformation for the visualization + \param[in] angle Limit angle + \param[in] active State of the joint - active/inactive + */ + virtual void visualizeDoubleCone(const PxTransform& t, PxReal angle, bool active) = 0; + + /** Visualize line + + \param[in] p0 Start position + \param[in] p1 End postion + \param[in] color Color + */ + virtual void visualizeLine(const PxVec3& p0, const PxVec3& p1, PxU32 color) = 0; +}; + +/** solver constraint visualization function + +This function is called by the constraint post-solver framework to visualize the constraint + +\param[out] visualizer The render buffer to render to +\param[in] constantBlock The constant data block +\param[in] body0Transform The center of mass frame of the first constrained body (the identity if the actor is static, or a NULL pointer was provided for it) +\param[in] body1Transform The center of mass frame of the second constrained body (the identity if the actor is static, or a NULL pointer was provided for it) +\param[in] flags The visualization flags (PxConstraintVisualizationFlag) + +@see PxRenderBuffer +*/ +typedef void (*PxConstraintVisualize)(PxConstraintVisualizer& visualizer, + const void* constantBlock, + const PxTransform& body0Transform, + const PxTransform& body1Transform, + PxU32 flags); + + +struct PxPvdUpdateType +{ + enum Enum + { + CREATE_INSTANCE, + RELEASE_INSTANCE, + UPDATE_ALL_PROPERTIES, + UPDATE_SIM_PROPERTIES + }; +}; + +/** + +\brief This class connects a custom constraint to the SDK + +This class connects a custom constraint to the SDK, and functions are called by the SDK +to query the custom implementation for specific information to pass on to the application +or inform the constraint when the application makes calls into the SDK which will update +the custom constraint's internal implementation +*/ +class PxConstraintConnector +{ +public: + /** + when the constraint is marked dirty, this function is called at the start of the simulation + step for the SDK to copy the constraint data block. + */ + virtual void* prepareData() = 0; + + /** + this function is called by the SDK to update PVD's view of it + */ + virtual bool updatePvdProperties(physx::pvdsdk::PvdDataStream& pvdConnection, + const PxConstraint* c, + PxPvdUpdateType::Enum updateType) const = 0; + + /** + When the SDK deletes a PxConstraint object this function is called by the SDK. In general + custom constraints should not be deleted directly by applications: rather, the constraint + should respond to a release() request by calling PxConstraint::release(), then wait for + this call to release its own resources, so that even if the release() call occurs during + a simulation step, the deletion of the constraint is buffered until that step completes. + + This function is also called when a PxConstraint object is deleted on cleanup due to + destruction of the PxPhysics object. + */ + virtual void onConstraintRelease() = 0; + + /** + This function is called by the SDK when the CoM of one of the actors is moved. Since the + API specifies constraint positions relative to actors, and the constraint shader functions + are supplied with coordinates relative to bodies, some synchronization is usually required + when the application moves an object's center of mass. + */ + virtual void onComShift(PxU32 actor) = 0; + + /** + This function is called by the SDK when the scene origin gets shifted and allows to adjust + custom data which contains world space transforms. + + \note If the adjustments affect constraint shader data, it is necessary to call PxConstraint::markDirty() + to make sure that the data gets synced at the beginning of the next simulation step. + + \param[in] shift Translation vector the origin is shifted by. + + @see PxScene.shiftOrigin() + */ + virtual void onOriginShift(const PxVec3& shift) = 0; + + /** + \brief Fetches external data for a constraint. + + This function is used by the SDK to acquire a reference to the owner of a constraint and a unique + owner type ID. This information will be passed on when a breakable constraint breaks or when + #PxConstraint::getExternalReference() is called. + + \param[out] typeID Unique type identifier of the external object. The value 0xffffffff is reserved and should not be used. Furthermore, if the PhysX extensions library is used, some other IDs are reserved already (see PxConstraintExtIDs) + \return Reference to the external object which owns the constraint. + + @see PxConstraintInfo PxSimulationEventCallback.onConstraintBreak() + */ + virtual void* getExternalReference(PxU32& typeID) = 0; + + /** + \brief Obtain a reference to a PxBase interface if the constraint has one. + + If the constraint does not implement the PxBase interface, it should return NULL. + */ + virtual PxBase* getSerializable() = 0; + + /** + \brief Obtain the shader function pointer used to prep rows for this constraint + */ + virtual PxConstraintSolverPrep getPrep() const = 0; + + /** + \brief Obtain the pointer to the constraint's constant data + */ + virtual const void* getConstantBlock() const = 0; + + /** + \brief virtual destructor + */ + virtual ~PxConstraintConnector() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxContact.h b/src/PhysX/physx/include/PxContact.h new file mode 100644 index 000000000..0fab1333d --- /dev/null +++ b/src/PhysX/physx/include/PxContact.h @@ -0,0 +1,583 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONTACT_H +#define PX_CONTACT_H + +#include "foundation/PxVec3.h" +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +#define PXC_CONTACT_NO_FACE_INDEX 0xffffffff + +PX_ALIGN_PREFIX(16) +struct PxMassModificationProps +{ + PxReal mInvMassScale0; + PxReal mInvInertiaScale0; + PxReal mInvMassScale1; + PxReal mInvInertiaScale1; +} +PX_ALIGN_SUFFIX(16); + +/** +\brief Header for contact patch where all points share same material and normal +*/ + +PX_ALIGN_PREFIX(16) +struct PxContactPatch +{ + enum PxContactPatchFlags + { + eHAS_FACE_INDICES = 1, //!< Indicates this contact stream has face indices. + eMODIFIABLE = 2, //!< Indicates this contact stream is modifiable. + eFORCE_NO_RESPONSE = 4, //!< Indicates this contact stream is notify-only (no contact response). + eHAS_MODIFIED_MASS_RATIOS = 8, //!< Indicates this contact stream has modified mass ratios + eHAS_TARGET_VELOCITY = 16, //!< Indicates this contact stream has target velocities set + eHAS_MAX_IMPULSE = 32, //!< Indicates this contact stream has max impulses set + eREGENERATE_PATCHES = 64, //!< Indicates this contact stream needs patches re-generated. + //!< This is required if the application modified either the contact normal or the material properties + eCOMPRESSED_MODIFIED_CONTACT = 128 + }; + + PX_ALIGN(16, PxMassModificationProps mMassModification); //16 + /** + \brief Contact normal + */ + PX_ALIGN(16, PxVec3 normal); //28 + /** + \brief Restitution coefficient + */ + PxReal restitution; //32 + + PxReal dynamicFriction; //36 + PxReal staticFriction; //40 + PxU8 startContactIndex; //41 + PxU8 nbContacts; //42 //Can be a U8 + + PxU8 materialFlags; //43 //Can be a U16 + PxU8 internalFlags; //44 //Can be a U16 + PxU16 materialIndex0; //46 //Can be a U16 + PxU16 materialIndex1; //48 //Can be a U16 + + +} +PX_ALIGN_SUFFIX(16); + +/** +\brief Contact point data including face (feature) indices +*/ + +PX_ALIGN_PREFIX(16) +struct PxContact +{ + /** + \brief Contact point in world space + */ + PxVec3 contact; //12 + /** + \brief Separation value (negative implies penetration). + */ + PxReal separation; //16 +} +PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct PxExtendedContact : public PxContact +{ + /** + \brief Target velocity + */ + PX_ALIGN(16, PxVec3 targetVelocity); //28 + /** + \brief Maximum impulse + */ + PxReal maxImpulse; //32 +} +PX_ALIGN_SUFFIX(16); + +/** +\brief A modifiable contact point. This has additional fields per-contact to permit modification by user. +\note Not all fields are currently exposed to the user. +*/ +PX_ALIGN_PREFIX(16) +struct PxModifiableContact : public PxExtendedContact +{ + /** + \brief Contact normal + */ + PX_ALIGN(16, PxVec3 normal); //44 + /** + \brief Restitution coefficient + */ + PxReal restitution; //48 + + /** + \brief Material Flags + */ + PxU32 materialFlags; //52 + + /** + \brief Shape A's material index + */ + PxU16 materialIndex0; //54 + /** + \brief Shape B's material index + */ + PxU16 materialIndex1; //56 + /** + \brief static friction coefficient + */ + PxReal staticFriction; //60 + /** + \brief dynamic friction coefficient + */ + PxReal dynamicFriction; //64 +} +PX_ALIGN_SUFFIX(16); + +/** +\brief A class to iterate over a compressed contact stream. This supports read-only access to the various contact formats. +*/ +struct PxContactStreamIterator +{ + enum StreamFormat + { + eSIMPLE_STREAM, + eMODIFIABLE_STREAM, + eCOMPRESSED_MODIFIABLE_STREAM + }; + /** + \brief Utility zero vector to optimize functions returning zero vectors when a certain flag isn't set. + \note This allows us to return by reference instead of having to return by value. Returning by value will go via memory (registers -> stack -> registers), which can + cause performance issues on certain platforms. + */ + PxVec3 zero; + /** + \brief The patch headers. + */ + const PxContactPatch* patch; + + /** + \brief The contacts + */ + const PxContact* contact; + + /** + \brief The contact triangle face index + */ + const PxU32* faceIndice; + + + /** + \brief The total number of patches in this contact stream + */ + PxU32 totalPatches; + + /** + \brief The total number of contact points in this stream + */ + PxU32 totalContacts; + + /** + \brief The current contact index + */ + PxU32 nextContactIndex; + + /** + \brief The current patch Index + */ + PxU32 nextPatchIndex; + + /* + \brief Size of contact patch header + \note This varies whether the patch is modifiable or not. + */ + PxU32 contactPatchHeaderSize; + /** + \brief Contact point size + \note This varies whether the patch has feature indices or is modifiable. + */ + PxU32 contactPointSize; + /** + \brief The stream format + */ + StreamFormat mStreamFormat; + /** + \brief Indicates whether this stream is notify-only or not. + */ + PxU32 forceNoResponse; + + bool pointStepped; + + PxU32 hasFaceIndices; + + /** + \brief Constructor + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxContactStreamIterator(const PxU8* contactPatches, const PxU8* contactPoints, const PxU32* contactFaceIndices, PxU32 nbPatches, PxU32 nbContacts) + : zero(0.f) + { + bool modify = false; + bool compressedModify = false; + bool response = false; + bool indices = false; + + PxU32 pointSize = 0; + PxU32 patchHeaderSize = sizeof(PxContactPatch); + + const PxContactPatch* patches = reinterpret_cast(contactPatches); + + if(patches) + { + modify = (patches->internalFlags & PxContactPatch::eMODIFIABLE) != 0; + compressedModify = (patches->internalFlags & PxContactPatch::eCOMPRESSED_MODIFIED_CONTACT) != 0; + indices = (patches->internalFlags & PxContactPatch::eHAS_FACE_INDICES) != 0; + + patch = patches; + + contact = reinterpret_cast(contactPoints); + + faceIndice = contactFaceIndices; + + pointSize = compressedModify ? sizeof(PxExtendedContact) : modify ? sizeof(PxModifiableContact) : sizeof(PxContact); + + response = (patch->internalFlags & PxContactPatch::eFORCE_NO_RESPONSE) == 0; + } + + + mStreamFormat = compressedModify ? eCOMPRESSED_MODIFIABLE_STREAM : modify ? eMODIFIABLE_STREAM : eSIMPLE_STREAM; + hasFaceIndices = PxU32(indices); + forceNoResponse = PxU32(!response); + + contactPatchHeaderSize = patchHeaderSize; + contactPointSize = pointSize; + nextPatchIndex = 0; + nextContactIndex = 0; + totalContacts = nbContacts; + totalPatches = nbPatches; + + pointStepped = false; + } + + /** + \brief Returns whether there are more patches in this stream. + \return Whether there are more patches in this stream. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool hasNextPatch() const + { + return nextPatchIndex < totalPatches; + } + + /** + \brief Returns the total contact count. + \return Total contact count. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getTotalContactCount() const + { + return totalContacts; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getTotalPatchCount() const + { + return totalPatches; + } + + /** + \brief Advances iterator to next contact patch. + */ + PX_CUDA_CALLABLE PX_INLINE void nextPatch() + { + PX_ASSERT(nextPatchIndex < totalPatches); + if(nextPatchIndex) + { + if(nextContactIndex < patch->nbContacts) + { + PxU32 nbToStep = patch->nbContacts - this->nextContactIndex; + contact = reinterpret_cast(reinterpret_cast(contact) + contactPointSize * nbToStep); + } + patch = reinterpret_cast(reinterpret_cast(patch) + contactPatchHeaderSize); + } + nextPatchIndex++; + nextContactIndex = 0; + } + + /** + \brief Returns if the current patch has more contacts. + \return If there are more contacts in the current patch. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool hasNextContact() const + { + return nextContactIndex < (patch->nbContacts); + } + + /** + \brief Advances to the next contact in the patch. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void nextContact() + { + PX_ASSERT(nextContactIndex < patch->nbContacts); + if(pointStepped) + { + contact = reinterpret_cast(reinterpret_cast(contact) + contactPointSize); + faceIndice++; + } + nextContactIndex++; + pointStepped = true; + } + + + /** + \brief Gets the current contact's normal + \return The current contact's normal. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& getContactNormal() const + { + return getContactPatch().normal; + } + + /** + \brief Gets the inverse mass scale for body 0. + \return The inverse mass scale for body 0. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getInvMassScale0() const + { + return patch->mMassModification.mInvMassScale0; + } + + /** + \brief Gets the inverse mass scale for body 1. + \return The inverse mass scale for body 1. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getInvMassScale1() const + { + return patch->mMassModification.mInvMassScale1; + } + + /** + \brief Gets the inverse inertia scale for body 0. + \return The inverse inertia scale for body 0. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getInvInertiaScale0() const + { + return patch->mMassModification.mInvInertiaScale0; + } + + /** + \brief Gets the inverse inertia scale for body 1. + \return The inverse inertia scale for body 1. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getInvInertiaScale1() const + { + return patch->mMassModification.mInvInertiaScale1; + } + + /** + \brief Gets the contact's max impulse. + \return The contact's max impulse. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getMaxImpulse() const + { + return mStreamFormat != eSIMPLE_STREAM ? getExtendedContact().maxImpulse : PX_MAX_REAL; + } + + /** + \brief Gets the contact's target velocity. + \return The contact's target velocity. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& getTargetVel() const + { + return mStreamFormat != eSIMPLE_STREAM ? getExtendedContact().targetVelocity : zero; + } + + /** + \brief Gets the contact's contact point. + \return The contact's contact point. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& getContactPoint() const + { + return contact->contact; + } + + /** + \brief Gets the contact's separation. + \return The contact's separation. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getSeparation() const + { + return contact->separation; + } + + /** + \brief Gets the contact's face index for shape 0. + \return The contact's face index for shape 0. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getFaceIndex0() const + { + return PXC_CONTACT_NO_FACE_INDEX; + } + + /** + \brief Gets the contact's face index for shape 1. + \return The contact's face index for shape 1. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getFaceIndex1() const + { + return hasFaceIndices ? *faceIndice : PXC_CONTACT_NO_FACE_INDEX; + } + + /** + \brief Gets the contact's static friction coefficient. + \return The contact's static friction coefficient. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getStaticFriction() const + { + return getContactPatch().staticFriction; + } + + /** + \brief Gets the contact's static dynamic coefficient. + \return The contact's static dynamic coefficient. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getDynamicFriction() const + { + return getContactPatch().dynamicFriction; + } + + /** + \brief Gets the contact's restitution coefficient. + \return The contact's restitution coefficient. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getRestitution() const + { + return getContactPatch().restitution; + } + + /** + \brief Gets the contact's material flags. + \return The contact's material flags. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaterialFlags() const + { + return getContactPatch().materialFlags; + } + + /** + \brief Gets the contact's material index for shape 0. + \return The contact's material index for shape 0. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex0() const + { + return PxU16(getContactPatch().materialIndex0); + } + + /** + \brief Gets the contact's material index for shape 1. + \return The contact's material index for shape 1. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex1() const + { + return PxU16(getContactPatch().materialIndex1); + } + + /** + \brief Advances the contact stream iterator to a specific contact index. + */ + bool advanceToIndex(const PxU32 initialIndex) + { + PX_ASSERT(this->nextPatchIndex == 0 && this->nextContactIndex == 0); + + PxU32 numToAdvance = initialIndex; + + if(numToAdvance == 0) + { + PX_ASSERT(hasNextPatch()); + nextPatch(); + return true; + } + + while(numToAdvance) + { + while(hasNextPatch()) + { + nextPatch(); + PxU32 patchSize = patch->nbContacts; + if(numToAdvance <= patchSize) + { + contact = reinterpret_cast(reinterpret_cast(contact) + contactPointSize * numToAdvance); + nextContactIndex += numToAdvance; + return true; + } + else + { + numToAdvance -= patchSize; + } + } + } + return false; + } + +private: + + /** + \brief Internal helper + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxContactPatch& getContactPatch() const + { + return *static_cast(patch); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxExtendedContact& getExtendedContact() const + { + PX_ASSERT(mStreamFormat == eMODIFIABLE_STREAM || mStreamFormat == eCOMPRESSED_MODIFIABLE_STREAM); + return *static_cast(contact); + } + +}; + + +#if PX_VC +#pragma warning(pop) +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif diff --git a/src/PhysX/physx/include/PxContactModifyCallback.h b/src/PhysX/physx/include/PxContactModifyCallback.h new file mode 100644 index 000000000..adca7a60a --- /dev/null +++ b/src/PhysX/physx/include/PxContactModifyCallback.h @@ -0,0 +1,486 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONTACT_MODIFY_CALLBACK +#define PX_CONTACT_MODIFY_CALLBACK +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxShape.h" +#include "PxContact.h" +#include "foundation/PxTransform.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxShape; + +/** +\brief An array of contact points, as passed to contact modification. + +The word 'set' in the name does not imply that duplicates are filtered in any +way. This initial set of contacts does potentially get reduced to a smaller +set before being passed to the solver. + +You can use the accessors to read and write contact properties. The number of +contacts is immutable, other than being able to disable contacts using ignore(). + +@see PxContactModifyCallback, PxModifiableContact +*/ +class PxContactSet +{ +public: + /** + \brief Get the position of a specific contact point in the set. + + @see PxModifiableContact.point + */ + PX_FORCE_INLINE const PxVec3& getPoint(PxU32 i) const { return mContacts[i].contact; } + + /** + \brief Alter the position of a specific contact point in the set. + + @see PxModifiableContact.point + */ + PX_FORCE_INLINE void setPoint(PxU32 i, const PxVec3& p) { mContacts[i].contact = p; } + + /** + \brief Get the contact normal of a specific contact point in the set. + + @see PxModifiableContact.normal + */ + PX_FORCE_INLINE const PxVec3& getNormal(PxU32 i) const { return mContacts[i].normal; } + + /** + \brief Alter the contact normal of a specific contact point in the set. + + \note Changing the normal can cause contact points to be ignored. + + @see PxModifiableContact.normal + */ + PX_FORCE_INLINE void setNormal(PxU32 i, const PxVec3& n) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eREGENERATE_PATCHES; + mContacts[i].normal = n; + } + + /** + \brief Get the separation of a specific contact point in the set. + + @see PxModifiableContact.separation + */ + PX_FORCE_INLINE PxReal getSeparation(PxU32 i) const { return mContacts[i].separation; } + + /** + \brief Alter the separation of a specific contact point in the set. + + @see PxModifiableContact.separation + */ + PX_FORCE_INLINE void setSeparation(PxU32 i, PxReal s) { mContacts[i].separation = s; } + + /** + \brief Get the target velocity of a specific contact point in the set. + + @see PxModifiableContact.targetVelocity + + */ + PX_FORCE_INLINE const PxVec3& getTargetVelocity(PxU32 i) const { return mContacts[i].targetVelocity; } + + /** + \brief Alter the target velocity of a specific contact point in the set. + + @see PxModifiableContact.targetVelocity + */ + PX_FORCE_INLINE void setTargetVelocity(PxU32 i, const PxVec3& v) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eHAS_TARGET_VELOCITY; + mContacts[i].targetVelocity = v; + } + + /** + \brief Get the face index with respect to the first shape of the pair for a specific contact point in the set. + + @see PxModifiableContact.internalFaceIndex0 + */ + PX_FORCE_INLINE PxU32 getInternalFaceIndex0(PxU32 i) { PX_UNUSED(i); return PXC_CONTACT_NO_FACE_INDEX; } + + /** + \brief Get the face index with respect to the second shape of the pair for a specific contact point in the set. + + @see PxModifiableContact.internalFaceIndex1 + */ + PX_FORCE_INLINE PxU32 getInternalFaceIndex1(PxU32 i) + { + PxContactPatch* patch = getPatch(); + if (patch->internalFlags & PxContactPatch::eHAS_FACE_INDICES) + { + return reinterpret_cast(mContacts + mCount)[mCount + i]; + } + return PXC_CONTACT_NO_FACE_INDEX; + } + + /** + \brief Get the maximum impulse for a specific contact point in the set. + + @see PxModifiableContact.maxImpulse + */ + PX_FORCE_INLINE PxReal getMaxImpulse(PxU32 i) const { return mContacts[i].maxImpulse; } + + /** + \brief Alter the maximum impulse for a specific contact point in the set. + + \note Must be nonnegative. If set to zero, the contact point will be ignored + + @see PxModifiableContact.maxImpulse + */ + PX_FORCE_INLINE void setMaxImpulse(PxU32 i, PxReal s) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eHAS_MAX_IMPULSE; + mContacts[i].maxImpulse = s; + } + + /** + \brief Get the restitution coefficient for a specific contact point in the set. + + @see PxModifiableContact.restitution + */ + PX_FORCE_INLINE PxReal getRestitution(PxU32 i) const { return mContacts[i].restitution; } + + /** + \brief Alter the restitution coefficient for a specific contact point in the set. + + \note Valid ranges [0,1] + + @see PxModifiableContact.restitution + */ + PX_FORCE_INLINE void setRestitution(PxU32 i, PxReal r) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eREGENERATE_PATCHES; + mContacts[i].restitution = r; + } + + /** + \brief Get the static friction coefficient for a specific contact point in the set. + + @see PxModifiableContact.staticFriction + */ + PX_FORCE_INLINE PxReal getStaticFriction(PxU32 i) const { return mContacts[i].staticFriction; } + + /** + \brief Alter the static friction coefficient for a specific contact point in the set. + + @see PxModifiableContact.staticFriction + */ + PX_FORCE_INLINE void setStaticFriction(PxU32 i, PxReal f) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eREGENERATE_PATCHES; + mContacts[i].staticFriction = f; + } + + /** + \brief Get the static friction coefficient for a specific contact point in the set. + + @see PxModifiableContact.dynamicFriction + */ + PX_FORCE_INLINE PxReal getDynamicFriction(PxU32 i) const { return mContacts[i].dynamicFriction; } + + /** + \brief Alter the static dynamic coefficient for a specific contact point in the set. + + @see PxModifiableContact.dynamic + */ + PX_FORCE_INLINE void setDynamicFriction(PxU32 i, PxReal f) + { + PxContactPatch* patch = getPatch(); + patch->internalFlags |= PxContactPatch::eREGENERATE_PATCHES; + mContacts[i].dynamicFriction = f; + } + + /** + \brief Ignore the contact point. + + If a contact point is ignored then no force will get applied at this point. This can be used to disable collision in certain areas of a shape, for example. + */ + PX_FORCE_INLINE void ignore(PxU32 i) { mContacts[i].maxImpulse = 0.f; } + + /** + \brief The number of contact points in the set. + */ + PX_FORCE_INLINE PxU32 size() const { return mCount; } + + /** + \brief Returns the invMassScale of body 0 + + A value < 1.0 makes this contact treat the body as if it had larger mass. A value of 0.f makes this contact + treat the body as if it had infinite mass. Any value > 1.f makes this contact treat the body as if it had smaller mass. + */ + PX_FORCE_INLINE PxReal getInvMassScale0() const + { + PxContactPatch* patch = getPatch(); + return patch->mMassModification.mInvMassScale0; + } + + /** + \brief Returns the invMassScale of body 1 + + A value < 1.0 makes this contact treat the body as if it had larger mass. A value of 0.f makes this contact + treat the body as if it had infinite mass. Any value > 1.f makes this contact treat the body as if it had smaller mass. + */ + PX_FORCE_INLINE PxReal getInvMassScale1() const + { + PxContactPatch* patch = getPatch(); + return patch->mMassModification.mInvMassScale1; + } + + /** + \brief Returns the invInertiaScale of body 0 + + A value < 1.0 makes this contact treat the body as if it had larger inertia. A value of 0.f makes this contact + treat the body as if it had infinite inertia. Any value > 1.f makes this contact treat the body as if it had smaller inertia. + */ + PX_FORCE_INLINE PxReal getInvInertiaScale0() const + { + PxContactPatch* patch = getPatch(); + return patch->mMassModification.mInvInertiaScale0; + } + + /** + \brief Returns the invInertiaScale of body 1 + + A value < 1.0 makes this contact treat the body as if it had larger inertia. A value of 0.f makes this contact + treat the body as if it had infinite inertia. Any value > 1.f makes this contact treat the body as if it had smaller inertia. + */ + PX_FORCE_INLINE PxReal getInvInertiaScale1() const + { + PxContactPatch* patch = getPatch(); + return patch->mMassModification.mInvInertiaScale1; + } + + /** + \brief Sets the invMassScale of body 0 + + This can be set to any value in the range [0, PX_MAX_F32). A value < 1.0 makes this contact treat the body as if it had larger mass. A value of 0.f makes this contact + treat the body as if it had infinite mass. Any value > 1.f makes this contact treat the body as if it had smaller mass. + */ + PX_FORCE_INLINE void setInvMassScale0(const PxReal scale) + { + PxContactPatch* patch = getPatch(); + patch->mMassModification.mInvMassScale0 = scale; + patch->internalFlags |= PxContactPatch::eHAS_MODIFIED_MASS_RATIOS; + } + + /** + \brief Sets the invMassScale of body 1 + + This can be set to any value in the range [0, PX_MAX_F32). A value < 1.0 makes this contact treat the body as if it had larger mass. A value of 0.f makes this contact + treat the body as if it had infinite mass. Any value > 1.f makes this contact treat the body as if it had smaller mass. + */ + PX_FORCE_INLINE void setInvMassScale1(const PxReal scale) + { + PxContactPatch* patch = getPatch(); + patch->mMassModification.mInvMassScale1 = scale; + patch->internalFlags |= PxContactPatch::eHAS_MODIFIED_MASS_RATIOS; + } + + /** + \brief Sets the invInertiaScale of body 0 + + This can be set to any value in the range [0, PX_MAX_F32). A value < 1.0 makes this contact treat the body as if it had larger inertia. A value of 0.f makes this contact + treat the body as if it had infinite inertia. Any value > 1.f makes this contact treat the body as if it had smaller inertia. + */ + PX_FORCE_INLINE void setInvInertiaScale0(const PxReal scale) + { + PxContactPatch* patch = getPatch(); + patch->mMassModification.mInvInertiaScale0 = scale; + patch->internalFlags |= PxContactPatch::eHAS_MODIFIED_MASS_RATIOS; + } + + /** + \brief Sets the invInertiaScale of body 1 + + This can be set to any value in the range [0, PX_MAX_F32). A value < 1.0 makes this contact treat the body as if it had larger inertia. A value of 0.f makes this contact + treat the body as if it had infinite inertia. Any value > 1.f makes this contact treat the body as if it had smaller inertia. + */ + PX_FORCE_INLINE void setInvInertiaScale1(const PxReal scale) + { + PxContactPatch* patch = getPatch(); + patch->mMassModification.mInvInertiaScale1 = scale; + patch->internalFlags |= PxContactPatch::eHAS_MODIFIED_MASS_RATIOS; + } + +protected: + + PX_FORCE_INLINE PxContactPatch* getPatch() const + { + const size_t headerOffset = sizeof(PxContactPatch)*mCount; + return reinterpret_cast(reinterpret_cast(mContacts) - headerOffset); + } + + PxU32 mCount; //!< Number of contact points in the set + PxModifiableContact* mContacts; //!< The contact points of the set +}; + + + +/** +\brief An array of instances of this class is passed to PxContactModifyCallback::onContactModify(). + +@see PxContactModifyCallback +*/ + +class PxContactModifyPair +{ +public: + + /** + \brief The actors which make up the pair in contact. + + Note that these are the actors as seen by the simulation, and may have been deleted since the simulation step started. + */ + + const PxRigidActor* actor[2]; + /** + \brief The shapes which make up the pair in contact. + + Note that these are the shapes as seen by the simulation, and may have been deleted since the simulation step started. + */ + + const PxShape* shape[2]; + + /** + \brief The shape to world transforms of the two shapes. + + These are the transforms as the simulation engine sees them, and may have been modified by the application + since the simulation step started. + + */ + + PxTransform transform[2]; + + /** + \brief An array of contact points between these two shapes. + */ + + PxContactSet contacts; +}; + + +/** +\brief An interface class that the user can implement in order to modify contact constraints. + +Threading: It is necessary to make this class thread safe as it will be called in the context of the +simulation thread. It might also be necessary to make it reentrant, since some calls can be made by multi-threaded +parts of the physics engine. + +You can enable the use of this contact modification callback by raising the flag PxPairFlag::eMODIFY_CONTACTS in +the filter shader/callback (see #PxSimulationFilterShader) for a pair of rigid body objects. + +Please note: ++ Raising the contact modification flag will not wake the actors up automatically. ++ It is not possible to turn off the performance degradation by simply removing the callback from the scene, the + filter shader/callback has to be used to clear the contact modification flag. ++ The contacts will only be reported as long as the actors are awake. There will be no callbacks while the actors are sleeping. + +@see PxScene.setContactModifyCallback() PxScene.getContactModifyCallback() +*/ +class PxContactModifyCallback +{ +public: + + /** + \brief Passes modifiable arrays of contacts to the application. + + The initial contacts are as determined fresh each frame by collision detection. + + The number of contacts can not be changed, so you cannot add your own contacts. You may however + disable contacts using PxContactSet::ignore(). + + @see PxContactModifyPair + */ + virtual void onContactModify(PxContactModifyPair* const pairs, PxU32 count) = 0; + +protected: + virtual ~PxContactModifyCallback(){} +}; + +/** +\brief An interface class that the user can implement in order to modify CCD contact constraints. + +Threading: It is necessary to make this class thread safe as it will be called in the context of the +simulation thread. It might also be necessary to make it reentrant, since some calls can be made by multi-threaded +parts of the physics engine. + +You can enable the use of this contact modification callback by raising the flag PxPairFlag::eMODIFY_CONTACTS in +the filter shader/callback (see #PxSimulationFilterShader) for a pair of rigid body objects. + +Please note: ++ Raising the contact modification flag will not wake the actors up automatically. ++ It is not possible to turn off the performance degradation by simply removing the callback from the scene, the + filter shader/callback has to be used to clear the contact modification flag. ++ The contacts will only be reported as long as the actors are awake. There will be no callbacks while the actors are sleeping. + +@see PxScene.setContactModifyCallback() PxScene.getContactModifyCallback() +*/ +class PxCCDContactModifyCallback +{ +public: + + /** + \brief Passes modifiable arrays of contacts to the application. + + The initial contacts are as determined fresh each frame by collision detection. + + The number of contacts can not be changed, so you cannot add your own contacts. You may however + disable contacts using PxContactSet::ignore(). + + @see PxContactModifyPair + */ + virtual void onCCDContactModify(PxContactModifyPair* const pairs, PxU32 count) = 0; + +protected: + virtual ~PxCCDContactModifyCallback(){} +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxDeletionListener.h b/src/PhysX/physx/include/PxDeletionListener.h new file mode 100644 index 000000000..0ca180360 --- /dev/null +++ b/src/PhysX/physx/include/PxDeletionListener.h @@ -0,0 +1,106 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_DELETIONLISTENER +#define PX_PHYSICS_NX_DELETIONLISTENER +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + +/** +\brief Flags specifying deletion event types. + +@see PxDeletionListener::onRelease PxPhysics.registerDeletionListener() +*/ +struct PxDeletionEventFlag +{ + enum Enum + { + eUSER_RELEASE = (1<<0), //!< The user has called release on an object. + eMEMORY_RELEASE = (1<<1) //!< The destructor of an object has been called and the memory has been released. + }; +}; + +/** +\brief Collection of set bits defined in PxDeletionEventFlag. + +@see PxDeletionEventFlag +*/ +typedef PxFlags PxDeletionEventFlags; +PX_FLAGS_OPERATORS(PxDeletionEventFlag::Enum,PxU8) + + +/** +\brief interface to get notification on object deletion + +*/ +class PxDeletionListener +{ +public: + /** + \brief Notification if an object or its memory gets released + + If release() gets called on a PxBase object, an eUSER_RELEASE event will get fired immediately. The object state can be queried in the callback but + it is not allowed to change the state. Furthermore, when reading from the object it is the user's responsibility to make sure that no other thread + is writing at the same time to the object (this includes the simulation itself, i.e., #PxScene::fetchResults() must not get called at the same time). + + Calling release() on a PxBase object does not necessarily trigger its destructor immediately. For example, the object can be shared and might still + be referenced by other objects or the simulation might still be running and accessing the object state. In such cases the destructor will be called + as soon as it is safe to do so. After the destruction of the object and its memory, an eMEMORY_RELEASE event will get fired. In this case it is not + allowed to dereference the object pointer in the callback. + + \param[in] observed The object for which the deletion event gets fired. + \param[in] userData The user data pointer of the object for which the deletion event gets fired. Not available for all object types in which case it will be set to 0. + \param[in] deletionEvent The type of deletion event. Do not dereference the object pointer argument if the event is eMEMORY_RELEASE. + + */ + virtual void onRelease(const PxBase* observed, void* userData, PxDeletionEventFlag::Enum deletionEvent) = 0; + +protected: + PxDeletionListener() {} + virtual ~PxDeletionListener() {} +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxFiltering.h b/src/PhysX/physx/include/PxFiltering.h new file mode 100644 index 000000000..b3a7f5289 --- /dev/null +++ b/src/PhysX/physx/include/PxFiltering.h @@ -0,0 +1,733 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_FILTERING +#define PX_PHYSICS_NX_FILTERING +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxActor; +class PxShape; + +static const PxU32 INVALID_FILTER_PAIR_INDEX = 0xffffffff; + +/** +\brief Collection of flags describing the actions to take for a collision pair. + +@see PxPairFlags PxSimulationFilterShader.filter() PxSimulationFilterCallback +*/ +struct PxPairFlag +{ + enum Enum + { + /** + \brief Process the contacts of this collision pair in the dynamics solver. + + \note Only takes effect if the colliding actors are rigid bodies. + */ + eSOLVE_CONTACT = (1<<0), + + /** + \brief Call contact modification callback for this collision pair + + \note Only takes effect if the colliding actors are rigid bodies. + + @see PxContactModifyCallback + */ + eMODIFY_CONTACTS = (1<<1), + + /** + \brief Call contact report callback or trigger callback when this collision pair starts to be in contact. + + If one of the two collision objects is a trigger shape (see #PxShapeFlag::eTRIGGER_SHAPE) + then the trigger callback will get called as soon as the other object enters the trigger volume. + If none of the two collision objects is a trigger shape then the contact report callback will get + called when the actors of this collision pair start to be in contact. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() PxSimulationEventCallback.onTrigger() + */ + eNOTIFY_TOUCH_FOUND = (1<<2), + + /** + \brief Call contact report callback while this collision pair is in contact + + If none of the two collision objects is a trigger shape then the contact report callback will get + called while the actors of this collision pair are in contact. + + \note Triggers do not support this event. Persistent trigger contacts need to be tracked separately by observing eNOTIFY_TOUCH_FOUND/eNOTIFY_TOUCH_LOST events. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note No report will get sent if the objects in contact are sleeping. + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + \note If this flag gets enabled while a pair is in touch already, there will be no eNOTIFY_TOUCH_PERSISTS events until the pair loses and regains touch. + + @see PxSimulationEventCallback.onContact() PxSimulationEventCallback.onTrigger() + */ + eNOTIFY_TOUCH_PERSISTS = (1<<3), + + /** + \brief Call contact report callback or trigger callback when this collision pair stops to be in contact + + If one of the two collision objects is a trigger shape (see #PxShapeFlag::eTRIGGER_SHAPE) + then the trigger callback will get called as soon as the other object leaves the trigger volume. + If none of the two collision objects is a trigger shape then the contact report callback will get + called when the actors of this collision pair stop to be in contact. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note This event will also get triggered if one of the colliding objects gets deleted. + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() PxSimulationEventCallback.onTrigger() + */ + eNOTIFY_TOUCH_LOST = (1<<4), + + /** + \brief Call contact report callback when this collision pair is in contact during CCD passes. + + If CCD with multiple passes is enabled, then a fast moving object might bounce on and off the same + object multiple times. Hence, the same pair might be in contact multiple times during a simulation step. + This flag will make sure that all the detected collision during CCD will get reported. For performance + reasons, the system can not always tell whether the contact pair lost touch in one of the previous CCD + passes and thus can also not always tell whether the contact is new or has persisted. eNOTIFY_TOUCH_CCD + just reports when the two collision objects were detected as being in contact during a CCD pass. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note Trigger shapes are not supported. + + \note Only takes effect if eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() PxSimulationEventCallback.onTrigger() + */ + eNOTIFY_TOUCH_CCD = (1<<5), + + /** + \brief Call contact report callback when the contact force between the actors of this collision pair exceeds one of the actor-defined force thresholds. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() + */ + eNOTIFY_THRESHOLD_FORCE_FOUND = (1<<6), + + /** + \brief Call contact report callback when the contact force between the actors of this collision pair continues to exceed one of the actor-defined force thresholds. + + \note Only takes effect if the colliding actors are rigid bodies. + + \note If a pair gets re-filtered and this flag has previously been disabled, then the report will not get fired in the same frame even if the force threshold has been reached in the + previous one (unless #eNOTIFY_THRESHOLD_FORCE_FOUND has been set in the previous frame). + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() + */ + eNOTIFY_THRESHOLD_FORCE_PERSISTS = (1<<7), + + /** + \brief Call contact report callback when the contact force between the actors of this collision pair falls below one of the actor-defined force thresholds (includes the case where this collision pair stops being in contact). + + \note Only takes effect if the colliding actors are rigid bodies. + + \note If a pair gets re-filtered and this flag has previously been disabled, then the report will not get fired in the same frame even if the force threshold has been reached in the + previous one (unless #eNOTIFY_THRESHOLD_FORCE_FOUND or #eNOTIFY_THRESHOLD_FORCE_PERSISTS has been set in the previous frame). + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() + */ + eNOTIFY_THRESHOLD_FORCE_LOST = (1<<8), + + /** + \brief Provide contact points in contact reports for this collision pair. + + \note Only takes effect if the colliding actors are rigid bodies and if used in combination with the flags eNOTIFY_TOUCH_... or eNOTIFY_THRESHOLD_FORCE_... + + \note Only takes effect if eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT is raised + + @see PxSimulationEventCallback.onContact() PxContactPair PxContactPair.extractContacts() + */ + eNOTIFY_CONTACT_POINTS = (1<<9), + + /** + \brief This flag is used to indicate whether this pair generates discrete collision detection contacts. + + \note Contacts are only responded to if eSOLVE_CONTACT is enabled. + */ + eDETECT_DISCRETE_CONTACT = (1<<10), + + /** + \brief This flag is used to indicate whether this pair generates CCD contacts. + + \note The contacts will only be responded to if eSOLVE_CONTACT is enabled on this pair. + \note The scene must have PxSceneFlag::eENABLE_CCD enabled to use this feature. + \note Non-static bodies of the pair should have PxRigidBodyFlag::eENABLE_CCD specified for this feature to work correctly. + \note This flag is not supported with trigger shapes. However, CCD trigger events can be emulated using non-trigger shapes + and requesting eNOTIFY_TOUCH_FOUND and eNOTIFY_TOUCH_LOST and not raising eSOLVE_CONTACT on the pair. + + @see PxRigidBodyFlag::eENABLE_CCD + @see PxSceneFlag::eENABLE_CCD + */ + eDETECT_CCD_CONTACT = (1<<11), + + /** + \brief Provide pre solver velocities in contact reports for this collision pair. + + If the collision pair has contact reports enabled, the velocities of the rigid bodies before contacts have been solved + will be provided in the contact report callback unless the pair lost touch in which case no data will be provided. + + \note Usually it is not necessary to request these velocities as they will be available by querying the velocity from the provided + PxRigidActor object directly. However, it might be the case that the velocity of a rigid body gets set while the simulation is running + in which case the PxRigidActor would return this new velocity in the contact report callback and not the velocity the simulation used. + + @see PxSimulationEventCallback.onContact(), PxContactPairVelocity, PxContactPairHeader.extraDataStream + */ + ePRE_SOLVER_VELOCITY = (1<<12), + + /** + \brief Provide post solver velocities in contact reports for this collision pair. + + If the collision pair has contact reports enabled, the velocities of the rigid bodies after contacts have been solved + will be provided in the contact report callback unless the pair lost touch in which case no data will be provided. + + @see PxSimulationEventCallback.onContact(), PxContactPairVelocity, PxContactPairHeader.extraDataStream + */ + ePOST_SOLVER_VELOCITY = (1<<13), + + /** + \brief Provide rigid body poses in contact reports for this collision pair. + + If the collision pair has contact reports enabled, the rigid body poses at the contact event will be provided + in the contact report callback unless the pair lost touch in which case no data will be provided. + + \note Usually it is not necessary to request these poses as they will be available by querying the pose from the provided + PxRigidActor object directly. However, it might be the case that the pose of a rigid body gets set while the simulation is running + in which case the PxRigidActor would return this new pose in the contact report callback and not the pose the simulation used. + Another use case is related to CCD with multiple passes enabled, A fast moving object might bounce on and off the same + object multiple times. This flag can be used to request the rigid body poses at the time of impact for each such collision event. + + @see PxSimulationEventCallback.onContact(), PxContactPairPose, PxContactPairHeader.extraDataStream + */ + eCONTACT_EVENT_POSE = (1<<14), + + eNEXT_FREE = (1<<15), //!< For internal use only. + + /** + \brief Provided default flag to do simple contact processing for this collision pair. + */ + eCONTACT_DEFAULT = eSOLVE_CONTACT | eDETECT_DISCRETE_CONTACT, + + /** + \brief Provided default flag to get commonly used trigger behavior for this collision pair. + */ + eTRIGGER_DEFAULT = eNOTIFY_TOUCH_FOUND | eNOTIFY_TOUCH_LOST | eDETECT_DISCRETE_CONTACT + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxPairFlag. + +@see PxPairFlag +*/ +typedef PxFlags PxPairFlags; +PX_FLAGS_OPERATORS(PxPairFlag::Enum, PxU16) + + + +/** +\brief Collection of flags describing the filter actions to take for a collision pair. + +@see PxFilterFlags PxSimulationFilterShader PxSimulationFilterCallback +*/ +struct PxFilterFlag +{ + enum Enum + { + /** + \brief Ignore the collision pair as long as the bounding volumes of the pair objects overlap. + + Killed pairs will be ignored by the simulation and won't run through the filter again until one + of the following occurs: + + \li The bounding volumes of the two objects overlap again (after being separated) + \li The user enforces a re-filtering (see #PxScene::resetFiltering()) + + @see PxScene::resetFiltering() + */ + eKILL = (1<<0), + + /** + \brief Ignore the collision pair as long as the bounding volumes of the pair objects overlap or until filtering relevant data changes for one of the collision objects. + + Suppressed pairs will be ignored by the simulation and won't make another filter request until one + of the following occurs: + + \li Same conditions as for killed pairs (see #eKILL) + \li The filter data or the filter object attributes change for one of the collision objects + + @see PxFilterData PxFilterObjectAttributes + */ + eSUPPRESS = (1<<1), + + /** + \brief Invoke the filter callback (#PxSimulationFilterCallback::pairFound()) for this collision pair. + + @see PxSimulationFilterCallback + */ + eCALLBACK = (1<<2), + + /** + \brief Track this collision pair with the filter callback mechanism. + + When the bounding volumes of the collision pair lose contact, the filter callback #PxSimulationFilterCallback::pairLost() + will be invoked. Furthermore, the filter status of the collision pair can be adjusted through #PxSimulationFilterCallback::statusChange() + once per frame (until a pairLost() notification occurs). + + @see PxSimulationFilterCallback + */ + eNOTIFY = (1<<3) | eCALLBACK, + + /** + \brief Provided default to get standard behavior: + + The application configure the pair's collision properties once when bounding volume overlap is found and + doesn't get asked again about that pair until overlap status or filter properties changes, or re-filtering is requested. + + No notification is provided when bounding volume overlap is lost + + The pair will not be killed or suppressed, so collision detection will be processed + */ + + eDEFAULT = 0 + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxFilterFlag. + +@see PxFilterFlag +*/ +typedef PxFlags PxFilterFlags; +PX_FLAGS_OPERATORS(PxFilterFlag::Enum, PxU16) + + +/** +\brief PxFilterData is user-definable data which gets passed into the collision filtering shader and/or callback. + +@see PxShape.setSimulationFilterData() PxShape.getSimulationFilterData() PxSimulationFilterShader PxSimulationFilterCallback +*/ +struct PxFilterData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PX_INLINE PxFilterData(const PxEMPTY) + { + } + + /** + \brief Default constructor. + */ + PX_INLINE PxFilterData() + { + word0 = word1 = word2 = word3 = 0; + } + + /** + \brief Copy constructor. + */ + PX_INLINE PxFilterData(const PxFilterData& fd) : word0(fd.word0), word1(fd.word1), word2(fd.word2), word3(fd.word3) {} + + /** + \brief Constructor to set filter data initially. + */ + PX_INLINE PxFilterData(PxU32 w0, PxU32 w1, PxU32 w2, PxU32 w3) : word0(w0), word1(w1), word2(w2), word3(w3) {} + + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE void setToDefault() + { + *this = PxFilterData(); + } + + /** + \brief Assignment operator + */ + PX_INLINE void operator = (const PxFilterData& fd) + { + word0 = fd.word0; + word1 = fd.word1; + word2 = fd.word2; + word3 = fd.word3; + } + + /** + \brief Comparison operator to allow use in Array. + */ + PX_INLINE bool operator == (const PxFilterData& a) const + { + return a.word0 == word0 && a.word1 == word1 && a.word2 == word2 && a.word3 == word3; + } + + /** + \brief Comparison operator to allow use in Array. + */ + PX_INLINE bool operator != (const PxFilterData& a) const + { + return !(a == *this); + } + + PxU32 word0; + PxU32 word1; + PxU32 word2; + PxU32 word3; +}; + + +/** +\brief Identifies each type of filter object. + +@see PxGetFilterObjectType() +*/ +struct PxFilterObjectType +{ + enum Enum + { + /** + \brief A static rigid body + @see PxRigidStatic + */ + eRIGID_STATIC, + + /** + \brief A dynamic rigid body + @see PxRigidDynamic + */ + eRIGID_DYNAMIC, + + /** + \brief An articulation + @see PxArticulation + */ + eARTICULATION, + + //brief internal use only! + eMAX_TYPE_COUNT = 16, + + //brief internal use only! + eUNDEFINED = eMAX_TYPE_COUNT-1 + }; +}; + + +// For internal use only +struct PxFilterObjectFlag +{ + enum Enum + { + eKINEMATIC = (1<<4), + eTRIGGER = (1<<5) + }; +}; + + +/** +\brief Structure which gets passed into the collision filtering shader and/or callback providing additional information on objects of a collision pair + +@see PxSimulationFilterShader PxSimulationFilterCallback getActorType() PxFilterObjectIsKinematic() PxFilterObjectIsTrigger() +*/ +typedef PxU32 PxFilterObjectAttributes; + + +/** +\brief Extract filter object type from the filter attributes of a collision pair object + +\param[in] attr The filter attribute of a collision pair object +\return The type of the collision pair object. + +@see PxFilterObjectType +*/ +PX_INLINE PxFilterObjectType::Enum PxGetFilterObjectType(PxFilterObjectAttributes attr) +{ + return PxFilterObjectType::Enum(attr & (PxFilterObjectType::eMAX_TYPE_COUNT-1)); +} + + +/** +\brief Specifies whether the collision object belongs to a kinematic rigid body + +\param[in] attr The filter attribute of a collision pair object +\return True if the object belongs to a kinematic rigid body, else false + +@see PxRigidBodyFlag::eKINEMATIC +*/ +PX_INLINE bool PxFilterObjectIsKinematic(PxFilterObjectAttributes attr) +{ + return (attr & PxFilterObjectFlag::eKINEMATIC) != 0; +} + + +/** +\brief Specifies whether the collision object is a trigger shape + +\param[in] attr The filter attribute of a collision pair object +\return True if the object is a trigger shape, else false + +@see PxShapeFlag::eTRIGGER_SHAPE +*/ +PX_INLINE bool PxFilterObjectIsTrigger(PxFilterObjectAttributes attr) +{ + return (attr & PxFilterObjectFlag::eTRIGGER) != 0; +} + + +/** +\brief Filter shader to specify handling of collision pairs. + +Collision filtering is a mechanism to specify how a pair of potentially colliding objects should be processed by the +simulation. A pair of objects is potentially colliding if the bounding volumes of the two objects overlap. +In short, a collision filter decides whether a collision pair should get processed, temporarily ignored or discarded. +If a collision pair should get processed, the filter can additionally specify how it should get processed, for instance, +whether contacts should get resolved, which callbacks should get invoked or which reports should be sent etc. + +\note A default implementation of a filter shader is provided in the PhysX extensions library, see #PxDefaultSimulationFilterShader. + +@see PxSceneDesc.filterShader PxSimulationFilterCallback +*/ + +/** +\brief Filter method to specify how a pair of potentially colliding objects should be processed. + +Return the PxFilterFlag flags and set the PxPairFlag flags to define what the simulation should do with the given collision pair. + +This methods gets called when: +\li The bounding volumes of two objects start to overlap. +\li The bounding volumes of two objects overlap and the filter data or filter attributes of one of the objects changed +\li A re-filtering was forced through resetFiltering() (see #PxScene::resetFiltering()) +\li Filtering is requested in scene queries + +\note Certain pairs of objects are always ignored and this method does not get called. This is the case for the +following pairs: + +\li Pair of static rigid actors +\li A static rigid actor and a kinematic actor (unless one is a trigger or if explicitly enabled through PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS) +\li Two kinematic actors (unless one is a trigger or if explicitly enabled through PxSceneFlag::eENABLE_KINEMATIC_PAIRS) +\li Two jointed rigid bodies and the joint was defined to disable collision +\li Two articulation links if connected through an articulation joint + +\note This is a performance critical method and should be stateless. You should neither access external objects +from within this method nor should you call external methods that are not inlined. If you need a more complex +logic to filter a collision pair then use the filter callback mechanism for this pair (see #PxSimulationFilterCallback, +#PxFilterFlag::eCALLBACK, #PxFilterFlag::eNOTIFY). + +\param[in] attributes0 The filter attribute of the first object +\param[in] filterData0 The custom filter data of the first object +\param[in] attributes1 The filter attribute of the second object +\param[in] filterData1 The custom filter data of the second object +\param[out] pairFlags Flags giving additional information on how an accepted pair should get processed +\param[in] constantBlock The constant global filter data (see #PxSceneDesc.filterShaderData) +\param[in] constantBlockSize Size of the global filter data (see #PxSceneDesc.filterShaderDataSize) +\return Filter flags defining whether the pair should be discarded, temporarily ignored, processed and whether the +filter callback should get invoked for this pair. + +@see PxSimulationFilterCallback PxFilterData PxFilterObjectAttributes PxFilterFlag PxFilterFlags PxPairFlag PxPairFlags +*/ + +typedef PxFilterFlags (*PxSimulationFilterShader) + (PxFilterObjectAttributes attributes0, PxFilterData filterData0, + PxFilterObjectAttributes attributes1, PxFilterData filterData1, + PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize); + + + +/** +\brief Filter callback to specify handling of collision pairs. + +This class is provided to implement more complex and flexible collision pair filtering logic, for instance, taking +the state of the user application into account. Filter callbacks also give the user the opportunity to track collision +pairs and update their filter state. + +You might want to check the documentation on #PxSimulationFilterShader as well since it includes more general information +on filtering. + +\note SDK state should not be modified from within the callbacks. In particular objects should not +be created or destroyed. If state modification is needed then the changes should be stored to a buffer +and performed after the simulation step. + +\note The callbacks may execute in user threads or simulation threads, possibly simultaneously. The corresponding objects +may have been deleted by the application earlier in the frame. It is the application's responsibility to prevent race conditions +arising from using the SDK API in the callback while an application thread is making write calls to the scene, and to ensure that +the callbacks are thread-safe. Return values which depend on when the callback is called during the frame will introduce nondeterminism +into the simulation. + +@see PxSceneDesc.filterCallback PxSimulationFilterShader +*/ +class PxSimulationFilterCallback +{ +public: + + /** + \brief Filter method to specify how a pair of potentially colliding objects should be processed. + + This method gets called when the filter flags returned by the filter shader (see #PxSimulationFilterShader) + indicate that the filter callback should be invoked (#PxFilterFlag::eCALLBACK or #PxFilterFlag::eNOTIFY set). + Return the PxFilterFlag flags and set the PxPairFlag flags to define what the simulation should do with the given + collision pair. + + \param[in] pairID Unique ID of the collision pair used to issue filter status changes for the pair (see #statusChange()) + \param[in] attributes0 The filter attribute of the first object + \param[in] filterData0 The custom filter data of the first object + \param[in] a0 Actor pointer of the first object + \param[in] s0 Shape pointer of the first object (NULL if the object has no shapes) + \param[in] attributes1 The filter attribute of the second object + \param[in] filterData1 The custom filter data of the second object + \param[in] a1 Actor pointer of the second object + \param[in] s1 Shape pointer of the second object (NULL if the object has no shapes) + \param[in,out] pairFlags In: Pair flags returned by the filter shader. Out: Additional information on how an accepted pair should get processed + \return Filter flags defining whether the pair should be discarded, temporarily ignored or processed and whether the pair + should be tracked and send a report on pair deletion through the filter callback + + @see PxSimulationFilterShader PxFilterData PxFilterObjectAttributes PxFilterFlag PxPairFlag + */ + virtual PxFilterFlags pairFound( PxU32 pairID, + PxFilterObjectAttributes attributes0, PxFilterData filterData0, const PxActor* a0, const PxShape* s0, + PxFilterObjectAttributes attributes1, PxFilterData filterData1, const PxActor* a1, const PxShape* s1, + PxPairFlags& pairFlags) = 0; + + /** + \brief Callback to inform that a tracked collision pair is gone. + + This method gets called when a collision pair disappears or gets re-filtered. Only applies to + collision pairs which have been marked as filter callback pairs (#PxFilterFlag::eNOTIFY set in #pairFound()). + + \param[in] pairID Unique ID of the collision pair that disappeared + \param[in] attributes0 The filter attribute of the first object + \param[in] filterData0 The custom filter data of the first object + \param[in] attributes1 The filter attribute of the second object + \param[in] filterData1 The custom filter data of the second object + \param[in] objectRemoved True if the pair was lost because one of the objects got removed from the scene + + @see pairFound() PxSimulationFilterShader PxFilterData PxFilterObjectAttributes + */ + virtual void pairLost( PxU32 pairID, + PxFilterObjectAttributes attributes0, + PxFilterData filterData0, + PxFilterObjectAttributes attributes1, + PxFilterData filterData1, + bool objectRemoved) = 0; + + /** + \brief Callback to give the opportunity to change the filter state of a tracked collision pair. + + This method gets called once per simulation step to let the application change the filter and pair + flags of a collision pair that has been reported in #pairFound() and requested callbacks by + setting #PxFilterFlag::eNOTIFY. To request a change of filter status, the target pair has to be + specified by its ID, the new filter and pair flags have to be provided and the method should return true. + + \note If this method changes the filter status of a collision pair and the pair should keep being tracked + by the filter callbacks then #PxFilterFlag::eNOTIFY has to be set. + + \note The application is responsible to ensure that this method does not get called for pairs that have been + reported as lost, see #pairLost(). + + \param[out] pairID ID of the collision pair for which the filter status should be changed + \param[out] pairFlags The new pairFlags to apply to the collision pair + \param[out] filterFlags The new filterFlags to apply to the collision pair + \return True if the changes should be applied. In this case the method will get called again. False if + no more status changes should be done in the current simulation step. In that case the provided flags will be discarded. + + @see pairFound() pairLost() PxFilterFlag PxPairFlag + */ + virtual bool statusChange(PxU32& pairID, PxPairFlags& pairFlags, PxFilterFlags& filterFlags) = 0; + +protected: + virtual ~PxSimulationFilterCallback() {} +}; + +struct PxPairFilteringMode +{ + enum Enum + { + /** + Output pair from BP, potentially send to user callbacks, create regular interaction object. + Similar to enabling PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS / PxSceneFlag::eENABLE_KINEMATIC_PAIRS. + */ + eKEEP, + + /** + Output pair from BP, create interaction marker. Can be later switched to regular interaction. + Similar to disabling PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS / PxSceneFlag::eENABLE_KINEMATIC_PAIRS. + */ + eSUPPRESS, + + /** + Don't output pair from BP. Cannot be later switched to regular interaction, needs "resetFiltering" call. + */ + eKILL, + + /** + Default is to ignore the mode and use PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS and PxSceneFlag::eENABLE_KINEMATIC_PAIRS instead (compatibility). + */ + eDEFAULT + }; +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxForceMode.h b/src/PhysX/physx/include/PxForceMode.h new file mode 100644 index 000000000..bac6253ec --- /dev/null +++ b/src/PhysX/physx/include/PxForceMode.h @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_FORCE_MODE +#define PX_PHYSICS_NX_FORCE_MODE + +#include "foundation/PxPreprocessor.h" + +/** \addtogroup physics +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Parameter to addForce() and addTorque() calls, determines the exact operation that is carried out. + +@see PxRigidBody.addForce() PxRigidBody.addTorque() +*/ +struct PxForceMode +{ + enum Enum + { + eFORCE, //!< parameter has unit of mass * distance/ time^2, i.e. a force + eIMPULSE, //!< parameter has unit of mass * distance /time + eVELOCITY_CHANGE, //!< parameter has unit of distance / time, i.e. the effect is mass independent: a velocity change. + eACCELERATION //!< parameter has unit of distance/ time^2, i.e. an acceleration. It gets treated just like a force except the mass is not divided out before integration. + }; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxFoundation.h b/src/PhysX/physx/include/PxFoundation.h new file mode 100644 index 000000000..8a1104c5b --- /dev/null +++ b/src/PhysX/physx/include/PxFoundation.h @@ -0,0 +1,158 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_FOUNDATION_PX_FOUNDATION_H +#define PX_FOUNDATION_PX_FOUNDATION_H + +/** \addtogroup foundation + @{ +*/ + +#include "foundation/Px.h" +#include "foundation/PxErrors.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Foundation SDK singleton class. + +You need to have an instance of this class to instance the higher level SDKs. +*/ +class PX_FOUNDATION_API PxFoundation +{ + public: + /** + \brief Destroys the instance it is called on. + + The operation will fail, if there are still modules referencing the foundation object. Release all dependent modules + prior + to calling this method. + + @see PxCreateFoundation() + */ + virtual void release() = 0; + + /** + retrieves error callback + */ + virtual PxErrorCallback& getErrorCallback() = 0; + + /** + Sets mask of errors to report. + */ + virtual void setErrorLevel(PxErrorCode::Enum mask = PxErrorCode::eMASK_ALL) = 0; + + /** + Retrieves mask of errors to be reported. + */ + virtual PxErrorCode::Enum getErrorLevel() const = 0; + + /** + Retrieves the allocator this object was created with. + */ + virtual PxAllocatorCallback& getAllocatorCallback() = 0; + + /** + Retrieves if allocation names are being passed to allocator callback. + */ + virtual bool getReportAllocationNames() const = 0; + + /** + Set if allocation names are being passed to allocator callback. + \details Enabled by default in debug and checked build, disabled by default in profile and release build. + */ + virtual void setReportAllocationNames(bool value) = 0; + + protected: + virtual ~PxFoundation() + { + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** +\brief Creates an instance of the foundation class + +The foundation class is needed to initialize higher level SDKs. There may be only one instance per process. +Calling this method after an instance has been created already will result in an error message and NULL will be +returned. + +\param version Version number we are expecting (should be #PX_PHYSICS_VERSION) +\param allocator User supplied interface for allocating memory(see #PxAllocatorCallback) +\param errorCallback User supplied interface for reporting errors and displaying messages(see #PxErrorCallback) +\return Foundation instance on success, NULL if operation failed + +@see PxFoundation +*/ + +PX_C_EXPORT PX_FOUNDATION_API physx::PxFoundation* PX_CALL_CONV +PxCreateFoundation(physx::PxU32 version, physx::PxAllocatorCallback& allocator, physx::PxErrorCallback& errorCallback); +/** +\brief Retrieves the Foundation SDK after it has been created. + +\note The behavior of this method is undefined if the foundation instance has not been created already. + +@see PxCreateFoundation() +*/ +#if PX_CLANG +#if PX_LINUX +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#endif // PX_LINUX +#endif // PX_CLANG +PX_C_EXPORT PX_FOUNDATION_API physx::PxFoundation& PX_CALL_CONV PxGetFoundation(); +#if PX_CLANG +#if PX_LINUX +#pragma clang diagnostic pop +#endif // PX_LINUX +#endif // PX_CLANG + +namespace physx +{ +class PxProfilerCallback; +} + +/** +\brief Get the callback that will be used for all profiling. +*/ +PX_C_EXPORT PX_FOUNDATION_API physx::PxProfilerCallback* PX_CALL_CONV PxGetProfilerCallback(); + +/** +\brief Set the callback that will be used for all profiling. +*/ +PX_C_EXPORT PX_FOUNDATION_API void PX_CALL_CONV PxSetProfilerCallback(physx::PxProfilerCallback* profiler); + +/** @} */ +#endif // PX_FOUNDATION_PX_FOUNDATION_H diff --git a/src/PhysX/physx/include/PxImmediateMode.h b/src/PhysX/physx/include/PxImmediateMode.h new file mode 100644 index 000000000..827d2a3ec --- /dev/null +++ b/src/PhysX/physx/include/PxImmediateMode.h @@ -0,0 +1,228 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_IMMEDIATE_MODE +#define PX_PHYSICS_IMMEDIATE_MODE +/** \addtogroup immediatemode +@{ */ + +#include "PxPhysXConfig.h" +#include "solver/PxSolverDefs.h" +#include "collision/PxCollisionDefs.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#if !PX_DOXYGEN +namespace immediate +{ +#endif + + /** + \brief Structure to store rigid body properties + */ + struct PxRigidBodyData + { + PX_ALIGN(16, PxVec3 linearVelocity); //!< 12 Linear velocity + PxReal invMass; //!< 16 Inverse mass + PxVec3 angularVelocity; //!< 28 Angular velocity + PxReal maxDepenetrationVelocity; //!< 32 Maximum de-penetration velocity + PxVec3 invInertia; //!< 44 Mass-space inverse interia diagonal vector + PxReal maxContactImpulse; //!< 48 Maximum permissable contact impulse + PxTransform body2World; //!< 76 World space transform + PxReal linearDamping; //!< 80 Linear damping coefficient + PxReal angularDamping; //!< 84 Angular damping coefficient + PxReal maxLinearVelocitySq; //!< 88 Squared maximum linear velocity + PxReal maxAngularVelocitySq; //!< 92 Squared maximum angular velocity + PxU32 pad; //!< 96 Padding for 16-byte alignment + }; + + /** + \brief Callback class to record contact points produced by immediate::PxGenerateContacts + */ + class PxContactRecorder + { + public: + /** + \brief Method to record new contacts + \param[in] contactPoints The contact points produced + \param[in] nbContacts The number of contact points produced + \param[in] index The index of this pair. This is an index from 0-N-1 identifying which pair this relates to from within the array of pairs passed to PxGenerateContacts + \return a boolean to indicate if this callback successfully stored the contacts or not. + */ + virtual bool recordContacts(const Gu::ContactPoint* contactPoints, const PxU32 nbContacts, const PxU32 index) = 0; + + virtual ~PxContactRecorder(){} + }; + + /** + \brief Constructs a PxSolverBodyData structure based on rigid body properties. Applies gravity, damping and clamps maximum velocity. + \param[in] inRigidData The array rigid body properties + \param[out] outSolverBodyData The array of solverBodyData produced to repreent these bodies + \param[in] nbBodies The total number of solver bodies to create + \param[in] gravity The gravity vector + \param[in] dt The timestep + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PxConstructSolverBodies(const PxRigidBodyData* inRigidData, PxSolverBodyData* outSolverBodyData, const PxU32 nbBodies, const PxVec3& gravity, const PxReal dt); + + /** + \brief Constructs a PxSolverBodyData structure for a static body at a given pose. + \param[in] globalPose The pose of this static actor + \param[out] solverBodyData The solver body representation of this static actor + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PxConstructStaticSolverBody(const PxTransform& globalPose,PxSolverBodyData& solverBodyData); + + /** + \brief Groups together sets of independent PxSolverConstraintDesc objects to be solved using SIMD SOA approach. + \param[in] solverConstraintDescs the set of solver constraint descs to batch + \param[in] nbConstraints The number of constraints to batch + \param[in,out] solverBodies The array of solver bodies that the constraints reference. Some fields in these structures are written to as scratch memory for the batching. + \param[in] nbBodies The number of bodies + \param[out] outBatchHeaders The batch headers produced by this batching process. This array must have at least 1 entry per input constraint + \param[out] outOrderedConstraintDescs A reordered copy of the constraint descs. This array is referenced by the constraint batches. This array must have at least 1 entry per input constraint. + \return The total number of batches produced. This should be less than or equal to nbConstraints. + + \note This method considers all bodies within the range [0, nbBodies-1] to be valid dynamic bodies. A given dynamic body can only be referenced in a batch once. Static or kinematic bodies can be + referenced multiple times within a batch safely because constraints do not affect their velocities. The batching will implicitly consider any bodies outside of the range [0, nbBodies-1] to be + infinite mass (static or kinematic). This means that either appending static/kinematic to the end of the array of bodies or placing static/kinematic bodies at before the start body pointer + will ensure that the minimum number of batches are produced. + */ + PX_C_EXPORT PX_PHYSX_CORE_API PxU32 PxBatchConstraints(PxSolverConstraintDesc* solverConstraintDescs, const PxU32 nbConstraints, PxSolverBody* solverBodies, PxU32 nbBodies, PxConstraintBatchHeader* outBatchHeaders, + PxSolverConstraintDesc* outOrderedConstraintDescs); + + /** + + \brief Creates a set of contact constraint blocks. Note that, depending the results of PxBatchConstraints, each batchHeader may refer to up to 4 solverConstraintDescs. + This function will allocate both constraint and friction patch data via the PxConstraintAllocator provided. Constraint data is only valid until PxSolveConstraints has completed. + Friction data is to be retained and provided by the application for friction correlation. + + \param[in] batchHeader Array of batch headers to process + \param[in] nbHeaders The total number of headers + \param[in] contactDescs An array of contact descs defining the pair and contact properties of each respective contacting pair + \param[in] allocator An allocator callback to allocate constraint and friction memory + \param[in] invDt The inverse timestep + \param[in] bounceThreshold The bounce threshold. Relative velocities below this will be solved by bias only. Relative velocities above this will be solved by restitution. If restitution is zero + then these pairs will always be solved by bias. + \param[in] frictionOffsetThreshold The friction offset threshold. Contacts whose separations are below this threshold can generate friction constraints. + \param[in] correlationDistance The correlation distance used by friction correlation to identify whether a friction patch is broken on the grounds of relation separation. + + \return a boolean to define if this method was successful or not. + */ + PX_C_EXPORT PX_PHYSX_CORE_API bool PxCreateContactConstraints(PxConstraintBatchHeader* batchHeader, const PxU32 nbHeaders, PxSolverContactDesc* contactDescs, + PxConstraintAllocator& allocator, PxReal invDt, PxReal bounceThreshold, PxReal frictionOffsetThreshold, PxReal correlationDistance); + + /** + \brief Creates a set of joint constraint blocks. Note that, depending the results of PxBatchConstraints, the batchHeader may refer to up to 4 solverConstraintDescs + \param[in] batchHeader The array of batch headers to be processed + \param[in] nbHeaders The total number of batch headers to process + \param[in] jointDescs An array of constraint prep descs defining the properties of the constraints being created + \param[in] allocator An allocator callback to allocate constraint data + \param[in] dt The timestep + \param[in] invDt The inverse timestep + \return a boolean indicating if this method was successful or not. + */ + PX_C_EXPORT PX_PHYSX_CORE_API bool PxCreateJointConstraints(PxConstraintBatchHeader* batchHeader, const PxU32 nbHeaders, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, PxReal dt, PxReal invDt); + + /** + \brief Creates a set of joint constraint blocks. This function runs joint shaders defined inside PxConstraint** param, fills in joint row information in jointDescs and then calls PxCreateJointConstraints. + \param[in] batchHeader The set of batchHeaders to be processed + \param[in] nbBatchHeaders The number of batch headers to process. + \param[in] constraints The set of constraints to be used to produce constraint rows + \param[in,out] jointDescs An array of constraint prep descs defining the properties of the constraints being created + \param[in] allocator An allocator callback to allocate constraint data + \param[in] dt The timestep + \param[in] invDt The inverse timestep + \return a boolean indicating if this method was successful or not. + */ + PX_C_EXPORT PX_PHYSX_CORE_API bool PxCreateJointConstraintsWithShaders(PxConstraintBatchHeader* batchHeader, const PxU32 nbBatchHeaders, PxConstraint** constraints, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, PxReal dt, PxReal invDt); + + /** + \brief Iteratively solves the set of constraints defined by the provided PxConstraintBatchHeader and PxSolverConstraintDesc structures. Updates deltaVelocities inside the PxSolverBody structures. Produces resulting linear and angular motion velocities. + \param[in] batchHeaders The set of batch headers to be solved + \param[in] nbBatchHeaders The total number of batch headers to be solved + \param[in] solverConstraintDescs The reordererd set of solver constraint descs referenced by the batch headers + \param[in,out] solverBodies The set of solver bodies the bodies reference + \param[out] linearMotionVelocity The resulting linear motion velocity + \param[out] angularMotionVelocity The resulting angular motion velocity. + \param[in] nbSolverBodies The total number of solver bodies + \param[in] nbPositionIterations The number of position iterations to run + \param[in] nbVelocityIterations The number of velocity iterations to run + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PxSolveConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbBatchHeaders, PxSolverConstraintDesc* solverConstraintDescs, PxSolverBody* solverBodies, + PxVec3* linearMotionVelocity, PxVec3* angularMotionVelocity, const PxU32 nbSolverBodies, const PxU32 nbPositionIterations, const PxU32 nbVelocityIterations); + + /** + \brief Integrates a rigid body, returning the new velocities and transforms. After this function has been called, solverBodyData stores all the body's velocity data. + + \param[in,out] solverBodyData The array of solver body data to be integrated + \param[in] solverBody The bodies' linear and angular velocities + \param[in] linearMotionVelocity The bodies' linear motion velocity array + \param[in] angularMotionState The bodies' angular motion velocity array + \param[in] nbBodiesToIntegrate The total number of bodies to integrate + \param[in] dt The timestep + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PxIntegrateSolverBodies(PxSolverBodyData* solverBodyData, PxSolverBody* solverBody, const PxVec3* linearMotionVelocity, const PxVec3* angularMotionState, const PxU32 nbBodiesToIntegrate, PxReal dt); + + /** + abrief Performs contact generation for a given pair of geometries at the specified poses. Produced contacts are stored in the provided Gu::ContactBuffer. Information is cached in PxCache structure + to accelerate future contact generation between pairs. This cache data is valid only as long as the memory provided by PxCacheAllocator has not been released/re-used. Recommendation is to + retain that data for a single simulation frame, discarding cached data after 2 frames. If the cached memory has been released/re-used prior to the corresponding pair having contact generation + performed again, it is the application's responsibility to reset the PxCache. + + \param[in] geom0 Array of geometries to perform collision detection on. + \param[in] geom1 Array of geometries to perform collision detection on + \param[in] pose0 Array of poses associated with the corresponding entry in the geom0 array + \param[in] pose1 Array of poses associated with the corresponding entry in the geom1 array + \param[in,out] contactCache Array of contact caches associated with each pair geom0[i] + geom1[i] + \param[in] nbPairs The total number of pairs to process + \param[in] contactRecorder A callback that is called to record contacts for each pair that detects contacts + \param[in] contactDistance The distance at which contacts begin to be generated between the pairs + \param[in] meshContactMargin The mesh contact margin. + \param[in] toleranceLength The toleranceLength. Used for scaling distance-based thresholds internally to produce appropriate results given simulations in different units + \param[in] allocator A callback to allocate memory for the contact cache + + \return a boolean indicating if the function was successful or not. + */ + PX_C_EXPORT PX_PHYSX_CORE_API bool PxGenerateContacts(const PxGeometry* const * geom0, const PxGeometry* const * geom1, const PxTransform* pose0, const PxTransform* pose1, PxCache* contactCache, const PxU32 nbPairs, PxContactRecorder& contactRecorder, + const PxReal contactDistance, const PxReal meshContactMargin, const PxReal toleranceLength, PxCacheAllocator& allocator); + +#if !PX_DOXYGEN +} +#endif + + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif + diff --git a/src/PhysX/physx/include/PxLockedData.h b/src/PhysX/physx/include/PxLockedData.h new file mode 100644 index 000000000..11b0b829e --- /dev/null +++ b/src/PhysX/physx/include/PxLockedData.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_LOCKED_DATA +#define PX_PHYSICS_NX_LOCKED_DATA +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxDataAccessFlag +{ + enum Enum + { + eREADABLE = (1 << 0), + eWRITABLE = (1 << 1), + eDEVICE = (1 << 2) + }; +}; + +/** +\brief collection of set bits defined in PxDataAccessFlag. + +@see PxDataAccessFlag +*/ +typedef PxFlags PxDataAccessFlags; +PX_FLAGS_OPERATORS(PxDataAccessFlag::Enum,PxU8) + + +/** +\brief Parent class for bulk data that is shared between the SDK and the application. +*/ +class PxLockedData +{ +public: + + /** + \brief Any combination of PxDataAccessFlag::eREADABLE and PxDataAccessFlag::eWRITABLE + @see PxDataAccessFlag + */ + virtual PxDataAccessFlags getDataAccessFlags() = 0; + + /** + \brief Unlocks the bulk data. + */ + virtual void unlock() = 0; + + /** + \brief virtual destructor + */ + virtual ~PxLockedData() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxMaterial.h b/src/PhysX/physx/include/PxMaterial.h new file mode 100644 index 000000000..24dc74a3d --- /dev/null +++ b/src/PhysX/physx/include/PxMaterial.h @@ -0,0 +1,324 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NXMATERIAL +#define PX_PHYSICS_NXMATERIAL +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxScene; + +/** +\brief Flags which control the behavior of a material. + +@see PxMaterial +*/ +struct PxMaterialFlag +{ + enum Enum + { + + /** + If this flag is set, friction computations are always skipped between shapes with this material and any other shape. + */ + eDISABLE_FRICTION = 1 << 0, + + /** + The difference between "normal" and "strong" friction is that the strong friction feature + remembers the "friction error" between simulation steps. The friction is a force trying to + hold objects in place (or slow them down) and this is handled in the solver. But since the + solver is only an approximation, the result of the friction calculation can include a small + "error" - e.g. a box resting on a slope should not move at all if the static friction is in + action, but could slowly glide down the slope because of a small friction error in each + simulation step. The strong friction counter-acts this by remembering the small error and + taking it to account during the next simulation step. + + However, in some cases the strong friction could cause problems, and this is why it is + possible to disable the strong friction feature by setting this flag. One example is + raycast vehicles, that are sliding fast across the surface, but still need a precise + steering behavior. It may be a good idea to reenable the strong friction when objects + are coming to a rest, to prevent them from slowly creeping down inclines. + + Note: This flag only has an effect if the PxMaterialFlag::eDISABLE_FRICTION bit is 0. + */ + eDISABLE_STRONG_FRICTION = 1 << 1 + }; +}; + +/** +\brief collection of set bits defined in PxMaterialFlag. + +@see PxMaterialFlag +*/ +typedef PxFlags PxMaterialFlags; +PX_FLAGS_OPERATORS(PxMaterialFlag::Enum,PxU16) + + +/** +\brief enumeration that determines the way in which two material properties will be combined to yield a friction or restitution coefficient for a collision. + +When two actors come in contact with each other, they each have materials with various coefficients, but we only need a single set of coefficients for the pair. + +Physics doesn't have any inherent combinations because the coefficients are determined empirically on a case by case +basis. However, simulating this with a pairwise lookup table is often impractical. + +For this reason the following combine behaviors are available: + +eAVERAGE +eMIN +eMULTIPLY +eMAX + +The effective combine mode for the pair is maximum(material0.combineMode, material1.combineMode). + +@see PxMaterial.setFrictionCombineMode() PxMaterial.getFrictionCombineMode() PxMaterial.setRestitutionCombineMode() PxMaterial.getFrictionCombineMode() +*/ +struct PxCombineMode +{ + enum Enum + { + eAVERAGE = 0, //!< Average: (a + b)/2 + eMIN = 1, //!< Minimum: minimum(a,b) + eMULTIPLY = 2, //!< Multiply: a*b + eMAX = 3, //!< Maximum: maximum(a,b) + eN_VALUES = 4, //!< This is not a valid combine mode, it is a sentinel to denote the number of possible values. We assert that the variable's value is smaller than this. + ePAD_32 = 0x7fffffff //!< This is not a valid combine mode, it is to assure that the size of the enum type is big enough. + }; +}; + +/** +\brief Material class to represent a set of surface properties. + +@see PxPhysics.createMaterial +*/ +class PxMaterial : public PxBase +{ +public: + + /** + \brief Decrements the reference count of a material and releases it if the new reference count is zero. + + @see PxPhysics.createMaterial() + */ + virtual void release() = 0; + + /** + \brief Returns the reference count of the material. + + At creation, the reference count of the material is 1. Every shape referencing this material increments the + count by 1. When the reference count reaches 0, and only then, the material gets destroyed automatically. + + \return the current reference count. + */ + virtual PxU32 getReferenceCount() const = 0; + + /** + \brief Acquires a counted reference to a material. + + This method increases the reference count of the material by 1. Decrement the reference count by calling release() + */ + virtual void acquireReference() = 0; + + /** + \brief Sets the coefficient of dynamic friction. + + The coefficient of dynamic friction should be in [0, PX_MAX_F32). If set to greater than staticFriction, the effective value of staticFriction will be increased to match. + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] coef Coefficient of dynamic friction. Range: [0, PX_MAX_F32) + + @see getDynamicFriction() + */ + virtual void setDynamicFriction(PxReal coef) = 0; + + /** + \brief Retrieves the DynamicFriction value. + + \return The coefficient of dynamic friction. + + @see setDynamicFriction + */ + virtual PxReal getDynamicFriction() const = 0; + + /** + \brief Sets the coefficient of static friction + + The coefficient of static friction should be in the range [0, PX_MAX_F32) + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] coef Coefficient of static friction. Range: [0, PX_MAX_F32) + + @see getStaticFriction() + */ + virtual void setStaticFriction(PxReal coef) = 0; + + /** + \brief Retrieves the coefficient of static friction. + \return The coefficient of static friction. + + @see setStaticFriction + */ + virtual PxReal getStaticFriction() const = 0; + + /** + \brief Sets the coefficient of restitution + + A coefficient of 0 makes the object bounce as little as possible, higher values up to 1.0 result in more bounce. + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] rest Coefficient of restitution. Range: [0,1] + + @see getRestitution() + */ + virtual void setRestitution(PxReal rest) = 0; + + /** + \brief Retrieves the coefficient of restitution. + + See #setRestitution. + + \return The coefficient of restitution. + + @see setRestitution() + */ + virtual PxReal getRestitution() const = 0; + + /** + \brief Raises or clears a particular material flag. + + See the list of flags #PxMaterialFlag + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] flag The PxMaterial flag to raise(set) or clear. + + @see getFlags() PxMaterialFlag + */ + virtual void setFlag(PxMaterialFlag::Enum flag, bool) = 0; + + + /** + \brief sets all the material flags. + + See the list of flags #PxMaterialFlag + + Sleeping: Does NOT wake any actors which may be affected. + + */ + virtual void setFlags( PxMaterialFlags inFlags ) = 0; + + /** + \brief Retrieves the flags. See #PxMaterialFlag. + + \return The material flags. + + @see PxMaterialFlag setFlags() + */ + virtual PxMaterialFlags getFlags() const = 0; + + /** + \brief Sets the friction combine mode. + + See the enum ::PxCombineMode . + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] combMode Friction combine mode to set for this material. See #PxCombineMode. + + @see PxCombineMode getFrictionCombineMode setStaticFriction() setDynamicFriction() + */ + virtual void setFrictionCombineMode(PxCombineMode::Enum combMode) = 0; + + /** + \brief Retrieves the friction combine mode. + + See #setFrictionCombineMode. + + \return The friction combine mode for this material. + + @see PxCombineMode setFrictionCombineMode() + */ + virtual PxCombineMode::Enum getFrictionCombineMode() const = 0; + + /** + \brief Sets the restitution combine mode. + + See the enum ::PxCombineMode . + + Sleeping: Does NOT wake any actors which may be affected. + + \param[in] combMode Restitution combine mode for this material. See #PxCombineMode. + + @see PxCombineMode getRestitutionCombineMode() setRestitution() + */ + virtual void setRestitutionCombineMode(PxCombineMode::Enum combMode) = 0; + + /** + \brief Retrieves the restitution combine mode. + + See #setRestitutionCombineMode. + + \return The coefficient of restitution combine mode for this material. + + @see PxCombineMode setRestitutionCombineMode getRestitution() + */ + virtual PxCombineMode::Enum getRestitutionCombineMode() const = 0; + + //public variables: + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. + + virtual const char* getConcreteTypeName() const { return "PxMaterial"; } + +protected: + PX_INLINE PxMaterial(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags), userData(NULL) {} + PX_INLINE PxMaterial(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxMaterial() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxMaterial", name) || PxBase::isKindOf(name); } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxPhysXConfig.h b/src/PhysX/physx/include/PxPhysXConfig.h new file mode 100644 index 000000000..9f532199b --- /dev/null +++ b/src/PhysX/physx/include/PxPhysXConfig.h @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX +#define PX_PHYSICS_NX + +/** Configuration include file for PhysX SDK */ + +/** \addtogroup physics +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysics; +class PxShape; + +class PxActor; +class PxRigidActor; +class PxRigidStatic; +class PxRigidDynamic; +class PxConstraint; +class PxConstraintDesc; + +class PxArticulation; +class PxArticulationReducedCoordinate; +class PxArticulationBase; +class PxArticulationLink; +class PxArticulationJoint; +class PxArticulationJointReducedCoordinate; +class PxArticulationJointBase; + +class PxMaterial; + +class PxScene; +class PxSceneDesc; +class PxTolerancesScale; + +class PxAggregate; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxPhysics.h b/src/PhysX/physx/include/PxPhysics.h new file mode 100644 index 000000000..e05dbc0c6 --- /dev/null +++ b/src/PhysX/physx/include/PxPhysics.h @@ -0,0 +1,729 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NX_PHYSICS +#define PX_PHYSICS_NX_PHYSICS + +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxDeletionListener.h" +#include "foundation/PxTransform.h" +#include "PxShape.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPvd; +class PxPhysicsInsertionCallback; + +class PxRigidActor; +class PxConstraintConnector; +struct PxConstraintShaderTable; + +class PxGeometry; +class PxFoundation; +class PxSerializationRegistry; + +class PxPruningStructure; +class PxBVHStructure; + +/** +\brief Abstract singleton factory class used for instancing objects in the Physics SDK. + +In addition you can use PxPhysics to set global parameters which will effect all scenes and create +objects that can be shared across multiple scenes. + +You can get an instance of this class by calling PxCreateBasePhysics() or PxCreatePhysics() with pre-registered modules. + +@see PxCreatePhysics() PxCreateBasePhysics() PxScene PxVisualizationParameter +*/ +class PxPhysics +{ +public: + + /** @name Basics + */ + //@{ + + virtual ~PxPhysics() {} + + /** + \brief Destroys the instance it is called on. + + Use this release method to destroy an instance of this class. Be sure + to not keep a reference to this object after calling release. + Avoid release calls while a scene is simulating (in between simulate() and fetchResults() calls). + + Note that this must be called once for each prior call to PxCreatePhysics, as + there is a reference counter. Also note that you mustn't destroy the allocator or the error callback (if available) until after the + reference count reaches 0 and the SDK is actually removed. + + Releasing an SDK will also release any scenes, triangle meshes, convex meshes, heightfields and shapes + created through it, provided the user hasn't already done so. + + \note This function is required to be called to release foundation usage. + + @see PxCreatePhysics() + */ + virtual void release() = 0; + + /** + \brief Retrieves the Foundation instance. + \return A reference to the Foundation object. + */ + virtual PxFoundation& getFoundation() = 0; + + /** + \brief Creates an aggregate with the specified maximum size and selfCollision property. + + \param [in] maxSize The maximum number of actors that may be placed in the aggregate. + \param [in] enableSelfCollision Whether the aggregate supports self-collision + \return The new aggregate. + + @see PxAggregate + */ + virtual PxAggregate* createAggregate(PxU32 maxSize, bool enableSelfCollision) = 0; + + /** + \brief Returns the simulation tolerance parameters. + \return The current simulation tolerance parameters. + */ + virtual const PxTolerancesScale& getTolerancesScale() const = 0; + + //@} + /** @name Meshes + */ + //@{ + + /** + \brief Creates a triangle mesh object. + + This can then be instanced into #PxShape objects. + + \param [in] stream The triangle mesh stream. + \return The new triangle mesh. + + @see PxTriangleMesh PxMeshPreprocessingFlag PxTriangleMesh.release() PxInputStream PxTriangleMeshFlag + */ + virtual PxTriangleMesh* createTriangleMesh(PxInputStream& stream) = 0; + + /** + \brief Return the number of triangle meshes that currently exist. + + \return Number of triangle meshes. + + @see getTriangleMeshes() + */ + virtual PxU32 getNbTriangleMeshes() const = 0; + + /** + \brief Writes the array of triangle mesh pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the triangle meshes in the array is not specified. + + \param [out] userBuffer The buffer to receive triangle mesh pointers. + \param [in] bufferSize The number of triangle mesh pointers which can be stored in the buffer. + \param [in] startIndex Index of first mesh pointer to be retrieved + \return The number of triangle mesh pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbTriangleMeshes() PxTriangleMesh + */ + virtual PxU32 getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Creates a heightfield object from previously cooked stream. + + This can then be instanced into #PxShape objects. + + \param [in] stream The heightfield mesh stream. + \return The new heightfield. + + @see PxHeightField PxHeightField.release() PxInputStream PxRegisterHeightFields + */ + virtual PxHeightField* createHeightField(PxInputStream& stream) = 0; + + /** + \brief Return the number of heightfields that currently exist. + + \return Number of heightfields. + + @see getHeightFields() + */ + virtual PxU32 getNbHeightFields() const = 0; + + /** + \brief Writes the array of heightfield pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the heightfields in the array is not specified. + + \param [out] userBuffer The buffer to receive heightfield pointers. + \param [in] bufferSize The number of heightfield pointers which can be stored in the buffer. + \param [in] startIndex Index of first heightfield pointer to be retrieved + \return The number of heightfield pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbHeightFields() PxHeightField + */ + virtual PxU32 getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Creates a convex mesh object. + + This can then be instanced into #PxShape objects. + + \param [in] stream The stream to load the convex mesh from. + \return The new convex mesh. + + @see PxConvexMesh PxConvexMesh.release() PxInputStream createTriangleMesh() PxConvexMeshGeometry PxShape + */ + virtual PxConvexMesh* createConvexMesh(PxInputStream &stream) = 0; + + /** + \brief Return the number of convex meshes that currently exist. + + \return Number of convex meshes. + + @see getConvexMeshes() + */ + virtual PxU32 getNbConvexMeshes() const = 0; + + /** + \brief Writes the array of convex mesh pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the convex meshes in the array is not specified. + + \param [out] userBuffer The buffer to receive convex mesh pointers. + \param [in] bufferSize The number of convex mesh pointers which can be stored in the buffer. + \param [in] startIndex Index of first convex mesh pointer to be retrieved + \return The number of convex mesh pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbConvexMeshes() PxConvexMesh + */ + virtual PxU32 getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Creates a bounding volume hierarchy structure. + + \param [in] stream The stream to load the BVH structure from. + \return The new BVH structure. + + @see PxBVHStructure PxInputStream + */ + virtual PxBVHStructure* createBVHStructure(PxInputStream &stream) = 0; + + /** + \brief Return the number of bounding volume hierarchy structures that currently exist. + + \return Number of bounding volume hierarchy structures. + + @see getBVHStructures() + */ + virtual PxU32 getNbBVHStructures() const = 0; + + /** + \brief Writes the array of bounding volume hierarchy structure pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the BVH structures in the array is not specified. + + \param [out] userBuffer The buffer to receive BVH structure pointers. + \param [in] bufferSize The number of BVH structure pointers which can be stored in the buffer. + \param [in] startIndex Index of first BVH structure pointer to be retrieved + \return The number of BVH structure pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbBVHStructures() PxBVHStructure + */ + virtual PxU32 getBVHStructures(PxBVHStructure** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + //@} + /** @name Scenes + */ + //@{ + + /** + \brief Creates a scene. + + \note Every scene uses a Thread Local Storage slot. This imposes a platform specific limit on the + number of scenes that can be created. + + \param [in] sceneDesc Scene descriptor. See #PxSceneDesc + \return The new scene object. + + @see PxScene PxScene.release() PxSceneDesc + */ + virtual PxScene* createScene(const PxSceneDesc& sceneDesc) = 0; + + /** + \brief Gets number of created scenes. + + \return The number of scenes created. + + @see getScene() + */ + virtual PxU32 getNbScenes() const = 0; + + /** + \brief Writes the array of scene pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the scene pointers in the array is not specified. + + \param [out] userBuffer The buffer to receive scene pointers. + \param [in] bufferSize The number of scene pointers which can be stored in the buffer. + \param [in] startIndex Index of first scene pointer to be retrieved + \return The number of scene pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbScenes() PxScene + */ + virtual PxU32 getScenes(PxScene** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + //@} + /** @name Actors + */ + //@{ + + /** + \brief Creates a static rigid actor with the specified pose and all other fields initialized + to their default values. + + \param [in] pose The initial pose of the actor. Must be a valid transform + + @see PxRigidStatic + */ + virtual PxRigidStatic* createRigidStatic(const PxTransform& pose) = 0; + + /** + \brief Creates a dynamic rigid actor with the specified pose and all other fields initialized + to their default values. + + \param [in] pose The initial pose of the actor. Must be a valid transform + + @see PxRigidDynamic + */ + virtual PxRigidDynamic* createRigidDynamic(const PxTransform& pose) = 0; + + /** + \brief Creates a pruning structure from actors. + + \note Every provided actor needs at least one shape with the eSCENE_QUERY_SHAPE flag set. + \note Both static and dynamic actors can be provided. + \note It is not allowed to pass in actors which are already part of a scene. + \note Articulation links cannot be provided. + + \param [in] actors Array of actors to add to the pruning structure. Must be non NULL. + \param [in] nbActors Number of actors in the array. Must be >0. + \return Pruning structure created from given actors, or NULL if any of the actors did not comply with the above requirements. + @see PxActor PxPruningStructure + */ + virtual PxPruningStructure* createPruningStructure(PxRigidActor*const* actors, PxU32 nbActors) = 0; + + //@} + /** @name Shapes + */ + //@{ + + /** + \brief Creates a shape which may be attached to multiple actors + + The shape will be created with a reference count of 1. + + \param [in] geometry The geometry for the shape + \param [in] material The material for the shape + \param [in] isExclusive Whether this shape is exclusive to a single actor or maybe be shared + \param [in] shapeFlags The PxShapeFlags to be set + + Shared shapes are not mutable when they are attached to an actor + + @see PxShape + */ + PX_FORCE_INLINE PxShape* createShape( const PxGeometry& geometry, + const PxMaterial& material, + bool isExclusive = false, + PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) + { + PxMaterial* materialPtr = const_cast(&material); + return createShape(geometry, &materialPtr, 1, isExclusive, shapeFlags); + } + + /** + \brief Creates a shape which may be attached to multiple actors + + The shape will be created with a reference count of 1. + + \param [in] geometry The geometry for the shape + \param [in] materials The materials for the shape + \param [in] materialCount The number of materials + \param [in] isExclusive Whether this shape is exclusive to a single actor or may be shared + \param [in] shapeFlags The PxShapeFlags to be set + + Shared shapes are not mutable when they are attached to an actor + + @see PxShape + */ + virtual PxShape* createShape(const PxGeometry& geometry, + PxMaterial*const * materials, + PxU16 materialCount, + bool isExclusive = false, + PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) = 0; + + /** + \brief Return the number of shapes that currently exist. + + \return Number of shapes. + + @see getShapes() + */ + virtual PxU32 getNbShapes() const = 0; + + /** + \brief Writes the array of shape pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the shapes in the array is not specified. + + \param [out] userBuffer The buffer to receive shape pointers. + \param [in] bufferSize The number of shape pointers which can be stored in the buffer. + \param [in] startIndex Index of first shape pointer to be retrieved + \return The number of shape pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbShapes() PxShape + */ + virtual PxU32 getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + //@} + /** @name Constraints and Articulations + */ + //@{ + + /** + \brief Creates a constraint shader. + + \note A constraint shader will get added automatically to the scene the two linked actors belong to. Either, but not both, of actor0 and actor1 may + be NULL to denote attachment to the world. + + \param [in] actor0 The first actor + \param [in] actor1 The second actor + \param [in] connector The connector object, which the SDK uses to communicate with the infrastructure for the constraint + \param [in] shaders The shader functions for the constraint + \param [in] dataSize The size of the data block for the shader + + \return The new shader. + + @see PxConstraint + */ + virtual PxConstraint* createConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) = 0; + + /** + \brief Creates an articulation with all fields initialized to their default values. + + \return the new articulation + + @see PxArticulation, PxRegisterArticulations + */ + virtual PxArticulation* createArticulation() = 0; + + /** + \brief Creates a reduced coordinate articulation with all fields initialized to their default values. + + \return the new articulation + + @see PxArticulationReducedCoordinate, PxRegisterArticulationsReducedCoordinate + */ + virtual PxArticulationReducedCoordinate* createArticulationReducedCoordinate() = 0; + + + //@} + /** @name Materials + */ + //@{ + + /** + \brief Creates a new material with default properties. + + \return The new material. + + \param [in] staticFriction The coefficient of static friction + \param [in] dynamicFriction The coefficient of dynamic friction + \param [in] restitution The coefficient of restitution + + @see PxMaterial + */ + virtual PxMaterial* createMaterial(PxReal staticFriction, PxReal dynamicFriction, PxReal restitution) = 0; + + + /** + \brief Return the number of materials that currently exist. + + \return Number of materials. + + @see getMaterials() + */ + virtual PxU32 getNbMaterials() const = 0; + + /** + \brief Writes the array of material pointers to a user buffer. + + Returns the number of pointers written. + + The ordering of the materials in the array is not specified. + + \param [out] userBuffer The buffer to receive material pointers. + \param [in] bufferSize The number of material pointers which can be stored in the buffer. + \param [in] startIndex Index of first material pointer to be retrieved + \return The number of material pointers written to userBuffer, this should be less or equal to bufferSize. + + @see getNbMaterials() PxMaterial + */ + virtual PxU32 getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + //@} + /** @name Deletion Listeners + */ + //@{ + + /** + \brief Register a deletion listener. Listeners will be called whenever an object is deleted. + + It is illegal to register or unregister a deletion listener while deletions are being processed. + + \note By default a registered listener will receive events from all objects. Set the restrictedObjectSet parameter to true on registration and use #registerDeletionListenerObjects to restrict the received events to specific objects. + + \note The deletion events are only supported on core PhysX objects. In general, objects in extension modules do not provide this functionality, however, in the case of PxJoint objects, the underlying PxConstraint will send the events. + + \param [in] observer Observer object to send notifications to. + \param [in] deletionEvents The deletion event types to get notified of. + \param [in] restrictedObjectSet If false, the deletion listener will get events from all objects, else the objects to receive events from have to be specified explicitly through #registerDeletionListenerObjects. + + @see PxDeletionListener unregisterDeletionListener + */ + virtual void registerDeletionListener(PxDeletionListener& observer, const PxDeletionEventFlags& deletionEvents, bool restrictedObjectSet = false) = 0; + + /** + \brief Unregister a deletion listener. + + It is illegal to register or unregister a deletion listener while deletions are being processed. + + \param [in] observer Observer object to send notifications to + + @see PxDeletionListener registerDeletionListener + */ + virtual void unregisterDeletionListener(PxDeletionListener& observer) = 0; + + /** + \brief Register specific objects for deletion events. + + This method allows for a deletion listener to limit deletion events to specific objects only. + + \note It is illegal to register or unregister objects while deletions are being processed. + + \note The deletion listener has to be registered through #registerDeletionListener() and configured to support restricted objects sets prior to this method being used. + + \param [in] observer Observer object to send notifications to. + \param [in] observables List of objects for which to receive deletion events. Only PhysX core objects are supported. In the case of PxJoint objects, the underlying PxConstraint can be used to get the events. + \param [in] observableCount Size of the observables list. + + @see PxDeletionListener unregisterDeletionListenerObjects + */ + virtual void registerDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount) = 0; + + /** + \brief Unregister specific objects for deletion events. + + This method allows to clear previously registered objects for a deletion listener (see #registerDeletionListenerObjects()). + + \note It is illegal to register or unregister objects while deletions are being processed. + + \note The deletion listener has to be registered through #registerDeletionListener() and configured to support restricted objects sets prior to this method being used. + + \param [in] observer Observer object to stop sending notifications to. + \param [in] observables List of objects for which to not receive deletion events anymore. + \param [in] observableCount Size of the observables list. + + @see PxDeletionListener registerDeletionListenerObjects + */ + virtual void unregisterDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount) = 0; + + /** + \brief Gets PxPhysics object insertion interface. + + The insertion interface is needed ie. for PxCooking::createTriangleMesh, this allows runtime mesh creation. This is not advised to do, please + use offline cooking if possible. + + @see PxCooking::createTriangleMesh PxCooking::createHeightfield + */ + virtual PxPhysicsInsertionCallback& getPhysicsInsertionCallback() = 0; + + //@} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** +\brief Enables the usage of the articulations feature. This function is called automatically inside PxCreatePhysics(). +On resource constrained platforms, it is possible to call PxCreateBasePhysics() and then NOT call this function +to save on code memory if your application does not use articulations. In this case the linker should strip out +the relevant implementation code from the library. If you need to use articulations but not some other optional +component, you shoud call PxCreateBasePhysics() followed by this call. +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxRegisterArticulations(physx::PxPhysics& physics); + + +/** +\brief Enables the usage of the reduced coordinate articulations feature. This function is called automatically inside PxCreatePhysics(). +On resource constrained platforms, it is possible to call PxCreateBasePhysics() and then NOT call this function +to save on code memory if your application does not use articulations. In this case the linker should strip out +the relevant implementation code from the library. If you need to use articulations but not some other optional +component, you shoud call PxCreateBasePhysics() followed by this call. +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxRegisterArticulationsReducedCoordinate(physx::PxPhysics& physics); + +/** +\brief Enables the usage of the heightfield feature. + +This call will link the default 'unified' implementation of heightfields which is identical to the narrow phase of triangle meshes. +This function is called automatically inside PxCreatePhysics(). + +On resource constrained platforms, it is possible to call PxCreateBasePhysics() and then NOT call this function +to save on code memory if your application does not use heightfields. In this case the linker should strip out +the relevant implementation code from the library. If you need to use heightfield but not some other optional +component, you shoud call PxCreateBasePhysics() followed by this call. + +You must call this function at a time where no ::PxScene instance exists, typically before calling PxPhysics::createScene(). +This is to prevent a change to the heightfield implementation code at runtime which would have undefined results. + +Calling PxCreateBasePhysics() and then attempting to create a heightfield shape without first calling +::PxRegisterHeightFields(), will result in an error. +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxRegisterHeightFields(physx::PxPhysics& physics); + +/** +\brief Creates an instance of the physics SDK with minimal additional components registered + +Creates an instance of this class. May not be a class member to avoid name mangling. +Pass the constant #PX_PHYSICS_VERSION as the argument. +There may be only one instance of this class per process. Calling this method after an instance +has been created already will result in an error message and NULL will be returned. + +\param version Version number we are expecting(should be #PX_PHYSICS_VERSION) +\param foundation Foundation instance (see PxFoundation) +\param scale values used to determine default tolerances for objects at creation time +\param trackOutstandingAllocations true if you want to track memory allocations + so a debugger connection partway through your physics simulation will get + an accurate map of everything that has been allocated so far. This could have a memory + and performance impact on your simulation hence it defaults to off. +\param pvd When pvd points to a valid PxPvd instance (PhysX Visual Debugger), a connection to the specified PxPvd instance is created. + If pvd is NULL no connection will be attempted. +\return PxPhysics instance on success, NULL if operation failed + +@see PxPhysics, PxFoundation, PxTolerancesScale, PxPvd +*/ +PX_C_EXPORT PX_PHYSX_CORE_API physx::PxPhysics* PX_CALL_CONV PxCreateBasePhysics(physx::PxU32 version, + physx::PxFoundation& foundation, + const physx::PxTolerancesScale& scale, + bool trackOutstandingAllocations = false, + physx::PxPvd* pvd = NULL); + +/** +\brief Creates an instance of the physics SDK. + +Creates an instance of this class. May not be a class member to avoid name mangling. +Pass the constant #PX_PHYSICS_VERSION as the argument. +There may be only one instance of this class per process. Calling this method after an instance +has been created already will result in an error message and NULL will be returned. + +Calling this will register all optional code modules (Articulations and HeightFields), preparing them for use. +If you do not need some of these modules, consider calling PxCreateBasePhysics() instead and registering needed +modules manually. + +\param version Version number we are expecting(should be #PX_PHYSICS_VERSION) +\param foundation Foundation instance (see PxFoundation) +\param scale values used to determine default tolerances for objects at creation time +\param trackOutstandingAllocations true if you want to track memory allocations + so a debugger connection partway through your physics simulation will get + an accurate map of everything that has been allocated so far. This could have a memory + and performance impact on your simulation hence it defaults to off. +\param pvd When pvd points to a valid PxPvd instance (PhysX Visual Debugger), a connection to the specified PxPvd instance is created. + If pvd is NULL no connection will be attempted. +\return PxPhysics instance on success, NULL if operation failed + +@see PxPhysics, PxCreateBasePhysics, PxRegisterArticulations, PxRegisterArticulationsReducedCoordinate, PxRegisterHeightFields +*/ +PX_INLINE physx::PxPhysics* PxCreatePhysics(physx::PxU32 version, + physx::PxFoundation& foundation, + const physx::PxTolerancesScale& scale, + bool trackOutstandingAllocations = false, + physx::PxPvd* pvd = NULL ) +{ + physx::PxPhysics* physics = PxCreateBasePhysics(version, foundation, scale, trackOutstandingAllocations, pvd); + if(!physics) + return NULL; + + PxRegisterArticulations(*physics); + PxRegisterArticulationsReducedCoordinate(*physics); + PxRegisterHeightFields(*physics); + + return physics; +} + + +/** +\brief Retrieves the Physics SDK after it has been created. + +Before using this function the user must call #PxCreatePhysics(). + +\note The behavior of this method is undefined if the Physics SDK instance has not been created already. +*/ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#endif + +PX_C_EXPORT PX_PHYSX_CORE_API physx::PxPhysics& PX_CALL_CONV PxGetPhysics(); + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxPhysicsAPI.h b/src/PhysX/physx/include/PxPhysicsAPI.h new file mode 100644 index 000000000..727774540 --- /dev/null +++ b/src/PhysX/physx/include/PxPhysicsAPI.h @@ -0,0 +1,218 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NXPHYSICS_API +#define PX_PHYSICS_NXPHYSICS_API +/** \addtogroup physics +@{ +*/ + +/** +This is the main include header for the Physics SDK, for users who +want to use a single #include file. + +Alternatively, one can instead directly #include a subset of the below files. +*/ + +// Foundation SDK +#include "foundation/Px.h" +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxAssert.h" +#include "foundation/PxBitAndData.h" +#include "foundation/PxBounds3.h" +#include "foundation/PxErrorCallback.h" +#include "foundation/PxErrors.h" +#include "foundation/PxFlags.h" +#include "foundation/PxIntrinsics.h" +#include "foundation/PxIO.h" +#include "foundation/PxMat33.h" +#include "foundation/PxMat44.h" +#include "foundation/PxMath.h" +#include "foundation/PxMathUtils.h" +#include "foundation/PxPlane.h" +#include "foundation/PxPreprocessor.h" +#include "foundation/PxQuat.h" +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxStrideIterator.h" +#include "foundation/PxTransform.h" +#include "foundation/PxUnionCast.h" +#include "foundation/PxVec2.h" +#include "foundation/PxVec3.h" +#include "foundation/PxVec4.h" + +//Not physics specific utilities and common code +#include "common/PxCoreUtilityTypes.h" +#include "common/PxPhysXCommonConfig.h" +#include "common/PxRenderBuffer.h" +#include "common/PxBase.h" +#include "common/PxTolerancesScale.h" +#include "common/PxTypeInfo.h" +#include "common/PxStringTable.h" +#include "common/PxSerializer.h" +#include "common/PxMetaData.h" +#include "common/PxMetaDataFlags.h" +#include "common/PxSerialFramework.h" +#include "common/PxPhysicsInsertionCallback.h" + +//Task Manager +#include "task/PxTask.h" + +// Cuda Mananger +#if PX_SUPPORT_GPU_PHYSX +#include "gpu/PxGpu.h" +#endif + +//Geometry Library +#include "geometry/PxBoxGeometry.h" +#include "geometry/PxBVHStructure.h" +#include "geometry/PxCapsuleGeometry.h" +#include "geometry/PxConvexMesh.h" +#include "geometry/PxConvexMeshGeometry.h" +#include "geometry/PxGeometry.h" +#include "geometry/PxGeometryHelpers.h" +#include "geometry/PxGeometryQuery.h" +#include "geometry/PxHeightField.h" +#include "geometry/PxHeightFieldDesc.h" +#include "geometry/PxHeightFieldFlag.h" +#include "geometry/PxHeightFieldGeometry.h" +#include "geometry/PxHeightFieldSample.h" +#include "geometry/PxMeshQuery.h" +#include "geometry/PxMeshScale.h" +#include "geometry/PxPlaneGeometry.h" +#include "geometry/PxSimpleTriangleMesh.h" +#include "geometry/PxSphereGeometry.h" +#include "geometry/PxTriangle.h" +#include "geometry/PxTriangleMesh.h" +#include "geometry/PxTriangleMeshGeometry.h" + + +// PhysX Core SDK +#include "PxActor.h" +#include "PxAggregate.h" +#include "PxArticulation.h" +#include "PxArticulationReducedCoordinate.h" +#include "PxArticulationJoint.h" +#include "PxArticulationJointReducedCoordinate.h" +#include "PxArticulationLink.h" +#include "PxBatchQuery.h" +#include "PxBatchQueryDesc.h" +#include "PxClient.h" +#include "PxConstraint.h" +#include "PxConstraintDesc.h" +#include "PxContact.h" +#include "PxContactModifyCallback.h" +#include "PxDeletionListener.h" +#include "PxFiltering.h" +#include "PxForceMode.h" +#include "PxFoundation.h" +#include "PxLockedData.h" +#include "PxMaterial.h" +#include "PxPhysics.h" +#include "PxPhysicsVersion.h" +#include "PxPhysXConfig.h" +#include "PxQueryFiltering.h" +#include "PxQueryReport.h" +#include "PxRigidActor.h" +#include "PxRigidBody.h" +#include "PxRigidDynamic.h" +#include "PxRigidStatic.h" +#include "PxScene.h" +#include "PxSceneDesc.h" +#include "PxSceneLock.h" +#include "PxShape.h" +#include "PxSimulationEventCallback.h" +#include "PxSimulationStatistics.h" +#include "PxVisualizationParameter.h" +#include "PxPruningStructure.h" + +//Character Controller +#include "characterkinematic/PxBoxController.h" +#include "characterkinematic/PxCapsuleController.h" +#include "characterkinematic/PxCharacter.h" +#include "characterkinematic/PxController.h" +#include "characterkinematic/PxControllerBehavior.h" +#include "characterkinematic/PxControllerManager.h" +#include "characterkinematic/PxControllerObstacles.h" +#include "characterkinematic/PxExtended.h" + +//Cooking (data preprocessing) +#include "cooking/Pxc.h" +#include "cooking/PxConvexMeshDesc.h" +#include "cooking/PxCooking.h" +#include "cooking/PxTriangleMeshDesc.h" +#include "cooking/PxBVH33MidphaseDesc.h" +#include "cooking/PxBVH34MidphaseDesc.h" +#include "cooking/PxMidphaseDesc.h" + +//Extensions to the SDK +#include "extensions/PxDefaultStreams.h" +#include "extensions/PxDistanceJoint.h" +#include "extensions/PxExtensionsAPI.h" +#include "extensions/PxFixedJoint.h" +#include "extensions/PxJoint.h" +#include "extensions/PxJointLimit.h" +#include "extensions/PxPrismaticJoint.h" +#include "extensions/PxRevoluteJoint.h" +#include "extensions/PxRigidBodyExt.h" +#include "extensions/PxShapeExt.h" +#include "extensions/PxSimpleFactory.h" +#include "extensions/PxSmoothNormals.h" +#include "extensions/PxSphericalJoint.h" +#include "extensions/PxStringTableExt.h" +#include "extensions/PxTriangleMeshExt.h" +#include "extensions/PxConvexMeshExt.h" + +//Serialization +#include "extensions/PxSerialization.h" +#include "extensions/PxBinaryConverter.h" +#include "extensions/PxRepXSerializer.h" + +//Vehicle Simulation +#include "vehicle/PxVehicleComponents.h" +#include "vehicle/PxVehicleDrive.h" +#include "vehicle/PxVehicleDrive4W.h" +#include "vehicle/PxVehicleDriveTank.h" +#include "vehicle/PxVehicleSDK.h" +#include "vehicle/PxVehicleShaders.h" +#include "vehicle/PxVehicleTireFriction.h" +#include "vehicle/PxVehicleUpdate.h" +#include "vehicle/PxVehicleUtilControl.h" +#include "vehicle/PxVehicleUtilSetup.h" +#include "vehicle/PxVehicleUtilTelemetry.h" +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleNoDrive.h" +#include "vehicle/PxVehicleDriveNW.h" + +//Connecting the SDK to Visual Debugger +#include "pvd/PxPvdSceneClient.h" +#include "pvd/PxPvd.h" +#include "pvd/PxPvdTransport.h" +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxPhysicsSerialization.h b/src/PhysX/physx/include/PxPhysicsSerialization.h new file mode 100644 index 000000000..c4cad9d29 --- /dev/null +++ b/src/PhysX/physx/include/PxPhysicsSerialization.h @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_PX_PHYSICS_SERIALIZATION +#define PX_PHYSICS_PX_PHYSICS_SERIALIZATION + +#include "PxPhysXConfig.h" +#include "common/PxSerialFramework.h" + +#if !PX_DOXYGEN +/** +\brief Retrieves the PhysX SDK metadata. +This function is used to implement PxSerialization.dumpBinaryMetaData() and is not intended to be needed otherwise. +@see PxSerialization.dumpBinaryMetaData() +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxGetPhysicsBinaryMetaData(physx::PxOutputStream& stream); + +/** +\brief Registers physics classes for serialization. +This function is used to implement PxSerialization.createSerializationRegistry() and is not intended to be needed otherwise. +@see PxSerializationRegistry +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxRegisterPhysicsSerializers(physx::PxSerializationRegistry& sr); + +/** +\brief Unregisters physics classes for serialization. +This function is used in the release implementation of PxSerializationRegistry and in not intended to be used otherwise. +@see PxSerializationRegistry +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxUnregisterPhysicsSerializers(physx::PxSerializationRegistry& sr); + + +/** +\brief Adds collected objects to PxPhysics. + +This function adds all objects contained in the input collection to the PxPhysics instance. This is used after deserializing +the collection, to populate the physics with inplace deserialized objects. This function is used in the implementation of +PxSerialization.createCollectionFromBinary and is not intended to be needed otherwise. +\param[in] collection Objects to add to the PxPhysics instance. + +@see PxCollection, PxSerialization.createCollectionFromBinary +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxAddCollectionToPhysics(const physx::PxCollection& collection); + +#endif // !PX_DOXYGEN + +#endif // PX_PHYSICS_PX_PHYSICS_SERIALIZATION diff --git a/src/PhysX/physx/include/PxPhysicsVersion.h b/src/PhysX/physx/include/PxPhysicsVersion.h new file mode 100644 index 000000000..9dd9de053 --- /dev/null +++ b/src/PhysX/physx/include/PxPhysicsVersion.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_VERSION_NUMBER_H +#define PX_PHYSICS_VERSION_NUMBER_H + +/* +VersionNumbers: The combination of these +numbers uniquely identifies the API, and should +be incremented when the SDK API changes. This may +include changes to file formats. + +This header is included in the main SDK header files +so that the entire SDK and everything that builds on it +is completely rebuilt when this file changes. Thus, +this file is not to include a frequently changing +build number. See BuildNumber.h for that. + +Each of these three values should stay below 255 because +sometimes they are stored in a byte. +*/ +/** \addtogroup foundation + @{ +*/ + +// +// Important: if you adjust the versions below, don't forget to adjust the compatibility list in +// sBinaryCompatibleVersions as well. +// + +#define PX_PHYSICS_VERSION_MAJOR 4 +#define PX_PHYSICS_VERSION_MINOR 0 +#define PX_PHYSICS_VERSION_BUGFIX 0 + +/** +The constant PX_PHYSICS_VERSION is used when creating certain PhysX module objects. +This is to ensure that the application is using the same header version as the library was built with. +*/ +#define PX_PHYSICS_VERSION ((PX_PHYSICS_VERSION_MAJOR<<24) + (PX_PHYSICS_VERSION_MINOR<<16) + (PX_PHYSICS_VERSION_BUGFIX<<8) + 0) + + +#endif // PX_PHYSICS_VERSION_NUMBER_H + + /** @} */ diff --git a/src/PhysX/physx/include/PxPruningStructure.h b/src/PhysX/physx/include/PxPruningStructure.h new file mode 100644 index 000000000..beeb1d7ac --- /dev/null +++ b/src/PhysX/physx/include/PxPruningStructure.h @@ -0,0 +1,109 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_PRUNING_STRUCTURE +#define PX_PHYSICS_NX_PRUNING_STRUCTURE +/** \addtogroup physics +@{ */ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + +/** +\brief A precomputed pruning structure to accelerate scene queries against newly added actors. + +The pruning structure can be provided to #PxScene:: addActors() in which case it will get merged +directly into the scene query optimization AABB tree, thus leading to improved performance when +doing queries against the newly added actors. This applies to both static and dynamic actors. + +\note PxPruningStructure objects can be added to a collection and get serialized. +\note Adding a PxPruningStructure object to a collection will also add the actors that were used to build the pruning structure. + +\note PxPruningStructure must be released before its rigid actors. +\note PxRigidBody objects can be in one PxPruningStructure only. +\note Changing the bounds of PxRigidBody objects assigned to a pruning structure that has not been added to a scene yet will +invalidate the pruning structure. Same happens if shape scene query flags change or shape gets removed from an actor. + +@see PxScene::addActors PxCollection +*/ +class PxPruningStructure : public PxBase +{ +public: + /** + \brief Release this object. + */ + virtual void release() = 0; + + /** + \brief Retrieve rigid actors in the pruning structure. + + You can retrieve the number of rigid actor pointers by calling #getNbRigidActors() + + \param[out] userBuffer The buffer to store the actor pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first actor pointer to be retrieved + \return Number of rigid actor pointers written to the buffer. + + @see PxRigidActor + */ + virtual PxU32 getRigidActors(PxRigidActor** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Returns the number of rigid actors in the pruning structure. + + You can use #getRigidActors() to retrieve the rigid actor pointers. + + \return Number of rigid actors in the pruning structure. + + @see PxRigidActor + */ + virtual PxU32 getNbRigidActors() const = 0; + + virtual const char* getConcreteTypeName() const { return "PxPruningStructure"; } +protected: + PX_INLINE PxPruningStructure(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxPruningStructure(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxPruningStructure() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxPruningStructure", name) || PxBase::isKindOf(name); } +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PX_PHYSICS_NX_PRUNING_STRUCTURE diff --git a/src/PhysX/physx/include/PxQueryFiltering.h b/src/PhysX/physx/include/PxQueryFiltering.h new file mode 100644 index 000000000..7991246f6 --- /dev/null +++ b/src/PhysX/physx/include/PxQueryFiltering.h @@ -0,0 +1,276 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENE_QUERY_FILTERING +#define PX_PHYSICS_NX_SCENE_QUERY_FILTERING +/** \addtogroup scenequery +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxFiltering.h" +#include "PxQueryReport.h" +#include "PxClient.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxShape; +class PxRigidActor; +struct PxQueryHit; + + +/** +\brief Filtering flags for scene queries. + +@see PxQueryFilterData.flags +*/ +struct PxQueryFlag +{ + enum Enum + { + eSTATIC = (1<<0), //!< Traverse static shapes + + eDYNAMIC = (1<<1), //!< Traverse dynamic shapes + + ePREFILTER = (1<<2), //!< Run the pre-intersection-test filter (see #PxQueryFilterCallback::preFilter()) + + ePOSTFILTER = (1<<3), //!< Run the post-intersection-test filter (see #PxQueryFilterCallback::postFilter()) + + eANY_HIT = (1<<4), //!< Abort traversal as soon as any hit is found and return it via callback.block. + //!< Helps query performance. Both eTOUCH and eBLOCK hitTypes are considered hits with this flag. + + eNO_BLOCK = (1<<5), //!< All hits are reported as touching. Overrides eBLOCK returned from user filters with eTOUCH. + //!< This is also an optimization hint that may improve query performance. + + eRESERVED = (1<<15) //!< Reserved for internal use + }; +}; +PX_COMPILE_TIME_ASSERT(PxQueryFlag::eSTATIC==(1<<0)); +PX_COMPILE_TIME_ASSERT(PxQueryFlag::eDYNAMIC==(1<<1)); + +/** +\brief Flags typedef for the set of bits defined in PxQueryFlag. + +*/ +typedef PxFlags PxQueryFlags; +PX_FLAGS_OPERATORS(PxQueryFlag::Enum,PxU16) + +/** +\brief Classification of scene query hits (intersections). + + - eNONE: Returning this hit type means that the hit should not be reported. + - eBLOCK: For all raycast, sweep and overlap queries the nearest eBLOCK type hit will always be returned in PxHitCallback::block member. + - eTOUCH: Whenever a raycast, sweep or overlap query was called with non-zero PxHitCallback::nbTouches and PxHitCallback::touches + parameters, eTOUCH type hits that are closer or same distance (touchDistance <= blockDistance condition) + as the globally nearest eBLOCK type hit, will be reported. + - For example, to record all hits from a raycast query, always return eTOUCH. + +All hits in overlap() queries are treated as if the intersection distance were zero. +This means the hits are unsorted and all eTOUCH hits are recorded by the callback even if an eBLOCK overlap hit was encountered. +Even though all overlap() blocking hits have zero length, only one (arbitrary) eBLOCK overlap hit is recorded in PxHitCallback::block. +All overlap() eTOUCH type hits are reported (zero touchDistance <= zero blockDistance condition). + +For raycast/sweep/overlap calls with zero touch buffer or PxHitCallback::nbTouches member, +only the closest hit of type eBLOCK is returned. All eTOUCH hits are discarded. + +@see PxQueryFilterCallback.preFilter PxQueryFilterCallback.postFilter PxScene.raycast PxScene.sweep PxScene.overlap +*/ +struct PxQueryHitType +{ + enum Enum + { + eNONE = 0, //!< the query should ignore this shape + eTOUCH = 1, //!< a hit on the shape touches the intersection geometry of the query but does not block it + eBLOCK = 2 //!< a hit on the shape blocks the query (does not block overlap queries) + }; +}; + +/** +\brief Scene query filtering data. + +Whenever the scene query intersects a shape, filtering is performed in the following order: + +\li For non-batched queries only:
            If the data field is non-zero, and the bitwise-AND value of data AND the shape's +queryFilterData is zero, the shape is skipped +\li If filter callbacks are enabled in flags field (see #PxQueryFlags) they will get invoked accordingly. +\li If neither #PxQueryFlag::ePREFILTER or #PxQueryFlag::ePOSTFILTER is set, the hit defaults +to type #PxQueryHitType::eBLOCK when the value of PxHitCallback::nbTouches provided with the query is zero and to type +#PxQueryHitType::eTOUCH when PxHitCallback::nbTouches is positive. + +@see PxScene.raycast PxScene.sweep PxScene.overlap PxBatchQuery.raycast PxBatchQuery.sweep PxBatchQuery.overlap PxQueryFlag::eANY_HIT +*/ +struct PxQueryFilterData +{ + /** \brief default constructor */ + explicit PX_INLINE PxQueryFilterData() : flags(PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC) {} + + /** \brief constructor to set both filter data and filter flags */ + explicit PX_INLINE PxQueryFilterData(const PxFilterData& fd, PxQueryFlags f) : data(fd), flags(f) {} + + /** \brief constructor to set filter flags only */ + explicit PX_INLINE PxQueryFilterData(PxQueryFlags f) : flags(f) {} + + PxFilterData data; //!< Filter data associated with the scene query + PxQueryFlags flags; //!< Filter flags (see #PxQueryFlags) +}; + +/** +\brief Scene query filtering callbacks. + +Custom filtering logic for scene query intersection candidates. If an intersection candidate object passes the data based filter +(see #PxQueryFilterData), filtering callbacks are executed if requested (see #PxQueryFilterData.flags) + +\li If #PxQueryFlag::ePREFILTER is set, the preFilter function runs before exact intersection tests. +If this function returns #PxQueryHitType::eTOUCH or #PxQueryHitType::eBLOCK, exact testing is performed to +determine the intersection location. + +The preFilter function may overwrite the copy of queryFlags it receives as an argument to specify any of #PxHitFlag::eMODIFIABLE_FLAGS +on a per-shape basis. Changes apply only to the shape being filtered, and changes to other flags are ignored. + +\li If #PxQueryFlag::ePREFILTER is not set, precise intersection testing is performed using the original query's filterData.flags. + +\li If #PxQueryFlag::ePOSTFILTER is set, the postFilter function is called for each intersection to determine the touch/block status. +This overrides any touch/block status previously returned from the preFilter function for this shape. + +Filtering calls are not guaranteed to be sorted along the ray or sweep direction. + +@see PxScene.raycast PxScene.sweep PxScene.overlap PxQueryFlags PxHitFlags +*/ +class PxQueryFilterCallback +{ +public: + + /** + \brief This filter callback is executed before the exact intersection test if PxQueryFlag::ePREFILTER flag was set. + + \param[in] filterData custom filter data specified as the query's filterData.data parameter. + \param[in] shape A shape that has not yet passed the exact intersection test. + \param[in] actor The shape's actor. + \param[in,out] queryFlags scene query flags from the query's function call (only flags from PxHitFlag::eMODIFIABLE_FLAGS bitmask can be modified) + \return the updated type for this hit (see #PxQueryHitType) + */ + virtual PxQueryHitType::Enum preFilter( + const PxFilterData& filterData, const PxShape* shape, const PxRigidActor* actor, PxHitFlags& queryFlags) = 0; + + /** + \brief This filter callback is executed if the exact intersection test returned true and PxQueryFlag::ePOSTFILTER flag was set. + + \param[in] filterData custom filter data of the query + \param[in] hit Scene query hit information. faceIndex member is not valid for overlap queries. For sweep and raycast queries the hit information can be cast to #PxSweepHit and #PxRaycastHit respectively. + \return the updated hit type for this hit (see #PxQueryHitType) + */ + virtual PxQueryHitType::Enum postFilter(const PxFilterData& filterData, const PxQueryHit& hit) = 0; + + /** + \brief virtual destructor + */ + virtual ~PxQueryFilterCallback() {} +}; + +/** +\brief Batched query pre-filter shader. + +Custom filtering logic for batched query intersection candidates. If an intersection candidate object passes the data based filter (see #PxQueryFilterData), +filtering shader runs if specified in filtering flags (see #PxQueryFilterData.flags) + +\li If #PxQueryFlag::ePREFILTER is set, the preFilter shader runs before exact intersection tests. +If the shader returns #PxQueryHitType::eTOUCH or #PxQueryHitType::eBLOCK, exact testing is performed to +determine the intersection location. + +The preFilter shader may overwrite the copy of queryFlags it receives as an argument to specify any of #PxHitFlag::eMODIFIABLE_FLAGS +on a per-shape basis. Changes apply only to the shape being filtered, and changes to other flags are ignored. + +\li If #PxQueryFlag::ePREFILTER is not set, precise intersection testing is performed using the original query's filterData.flags. + +Filtering calls are not guaranteed to be sorted along the ray or sweep direction. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 + +@see PxBatchQueryDesc.preFilterShader PxQueryFilterCallback.preFilter PxBatchQueryPostFilterShader + +*/ + +/** +\param[in] queryFilterData Query filter data +\param[in] objectFilterData Object filter data +\param[in] constantBlock Global constant filter data (see #PxBatchQuery) +\param[in] constantBlockSize Size of global filter data (see #PxBatchQuery) +\param[in,out] hitFlags Per-object modifiable hit flags (only flags from PxHitFlag::eMODIFIABLE_FLAGS mask can be modified) +\return the updated hit type for this hit (see #PxQueryHitType) + +@see PxBatchQueryPostFilterShader +*/ +typedef PX_DEPRECATED PxQueryHitType::Enum (*PxBatchQueryPreFilterShader)( + PxFilterData queryFilterData, PxFilterData objectFilterData, + const void* constantBlock, PxU32 constantBlockSize, + PxHitFlags& hitFlags); + +/** +\brief Batched query post-filter shader. + +Custom filtering logic for batched query intersection candidates. If an intersection candidate object passes the data based filter (see #PxQueryFilterData), +the filtering shader run on request (see #PxQueryFilterData.flags) + +\li If #PxQueryFlag::ePOSTFILTER is set, the postFilter shader is called for each intersection to determine the touch/block status. +This overrides any touch/block status previously returned from the preFilter function for this shape. + +Filtering shaders are not in order along the query direction, rather they are processed in the order in which +candidate shapes for testing are found by PhysX' scene traversal algorithms. + +\deprecated The batched query feature has been deprecated in PhysX version 3.4 + +@see PxBatchQueryDesc.postFilterShader PxQueryFilterCallback.postFilter PxBatchQueryPreFilterShader +*/ + +/** +\param[in] queryFilterData Query filter data +\param[in] objectFilterData Object filter data +\param[in] constantBlock Global constant filter data (see #PxBatchQuery) +\param[in] constantBlockSize Size of global filter data (see #PxBatchQuery) +\param[in] hit Hit data from the prior exact intersection test. +\return the new hit type for this hit (see #PxQueryHitType) + +@see PxBatchQueryPreFilterShader +*/ + +typedef PX_DEPRECATED PxQueryHitType::Enum (*PxBatchQueryPostFilterShader)( + PxFilterData queryFilterData, PxFilterData objectFilterData, + const void* constantBlock, PxU32 constantBlockSize, + const PxQueryHit& hit); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxQueryReport.h b/src/PhysX/physx/include/PxQueryReport.h new file mode 100644 index 000000000..f50322906 --- /dev/null +++ b/src/PhysX/physx/include/PxQueryReport.h @@ -0,0 +1,390 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENEQUERYREPORT +#define PX_PHYSICS_NX_SCENEQUERYREPORT +/** \addtogroup scenequery +@{ +*/ +#include "PxPhysXConfig.h" +#include "foundation/PxVec3.h" +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxShape; +class PxRigidActor; + +/** +\brief Scene query and geometry query behavior flags. + +PxHitFlags are used for 3 different purposes: + +1) To request hit fields to be filled in by scene queries (such as hit position, normal, face index or UVs). +2) Once query is completed, to indicate which fields are valid (note that a query may produce more valid fields than requested). +3) To specify additional options for the narrow phase and mid-phase intersection routines. + +All these flags apply to both scene queries and geometry queries (PxGeometryQuery). + +@see PxRaycastHit PxSweepHit PxOverlapHit PxScene.raycast PxScene.sweep PxScene.overlap PxGeometryQuery PxFindFaceIndex +*/ +struct PxHitFlag +{ + enum Enum + { + ePOSITION = (1<<0), //!< "position" member of #PxQueryHit is valid + eNORMAL = (1<<1), //!< "normal" member of #PxQueryHit is valid + eUV = (1<<3), //!< "u" and "v" barycentric coordinates of #PxQueryHit are valid. Not applicable to sweep queries. + eASSUME_NO_INITIAL_OVERLAP = (1<<4), //!< Performance hint flag for sweeps when it is known upfront there's no initial overlap. + //!< NOTE: using this flag may cause undefined results if shapes are initially overlapping. + eMESH_MULTIPLE = (1<<5), //!< Report all hits for meshes rather than just the first. Not applicable to sweep queries. + eMESH_ANY = (1<<6), //!< Report any first hit for meshes. If neither eMESH_MULTIPLE nor eMESH_ANY is specified, + //!< a single closest hit will be reported for meshes. + eMESH_BOTH_SIDES = (1<<7), //!< Report hits with back faces of mesh triangles. Also report hits for raycast + //!< originating on mesh surface and facing away from the surface normal. Not applicable to sweep queries. + //!< Please refer to the user guide for heightfield-specific differences. + ePRECISE_SWEEP = (1<<8), //!< Use more accurate but slower narrow phase sweep tests. + //!< May provide better compatibility with PhysX 3.2 sweep behavior. + eMTD = (1<<9), //!< Report the minimum translation depth, normal and contact point. + eFACE_INDEX = (1<<10), //!< "face index" member of #PxQueryHit is valid + + eDEFAULT = ePOSITION|eNORMAL|eFACE_INDEX, + + /** \brief Only this subset of flags can be modified by pre-filter. Other modifications will be discarded. */ + eMODIFIABLE_FLAGS = eMESH_MULTIPLE|eMESH_BOTH_SIDES|eASSUME_NO_INITIAL_OVERLAP|ePRECISE_SWEEP + }; +}; + + +/** +\brief collection of set bits defined in PxHitFlag. + +@see PxHitFlag +*/ +PX_FLAGS_TYPEDEF(PxHitFlag, PxU16) + +/** +\brief Combines a shape pointer and the actor the shape belongs to into one memory location. + +Serves as a base class for PxQueryHit. + +@see PxQueryHit +*/ +struct PxActorShape +{ + PX_INLINE PxActorShape() : actor(NULL), shape(NULL) {} + PX_INLINE PxActorShape(PxRigidActor* a, PxShape* s) : actor(a), shape(s) {} + + PxRigidActor* actor; + PxShape* shape; +}; + + +/** +\brief Scene query hit information. +*/ +struct PxQueryHit : public PxActorShape +{ + PX_INLINE PxQueryHit() : faceIndex(0xFFFFffff) {} + + /** + Face index of touched triangle, for triangle meshes, convex meshes and height fields. + + \note This index will default to 0xFFFFffff value for overlap queries. + \note Please refer to the user guide for more details for sweep queries. + \note This index is remapped by mesh cooking. Use #PxTriangleMesh::getTrianglesRemap() to convert to original mesh index. + \note For convex meshes use #PxConvexMesh::getPolygonData() to retrieve touched polygon data. + */ + PxU32 faceIndex; +}; + +/** +\brief Scene query hit information for raycasts and sweeps returning hit position and normal information. + +::PxHitFlag flags can be passed to scene query functions, as an optimization, to cause the SDK to +only generate specific members of this structure. +*/ +struct PxLocationHit : public PxQueryHit +{ + PX_INLINE PxLocationHit() : flags(0), position(PxVec3(0)), normal(PxVec3(0)), distance(PX_MAX_REAL) {} + + /** + \note For raycast hits: true for shapes overlapping with raycast origin. + \note For sweep hits: true for shapes overlapping at zero sweep distance. + + @see PxRaycastHit PxSweepHit + */ + PX_INLINE bool hadInitialOverlap() const { return (distance <= 0.0f); } + + // the following fields are set in accordance with the #PxHitFlags + PxHitFlags flags; //!< Hit flags specifying which members contain valid values. + PxVec3 position; //!< World-space hit position (flag: #PxHitFlag::ePOSITION) + PxVec3 normal; //!< World-space hit normal (flag: #PxHitFlag::eNORMAL) + + /** + \brief Distance to hit. + \note If the eMTD flag is used, distance will be a negative value if shapes are overlapping indicating the penetration depth. + \note Otherwise, this value will be >= 0 */ + PxF32 distance; +}; + + +/** +\brief Stores results of raycast queries. + +::PxHitFlag flags can be passed to raycast function, as an optimization, to cause the SDK to only compute specified members of this +structure. + +Some members like barycentric coordinates are currently only computed for triangle meshes and height fields, but next versions +might provide them in other cases. The client code should check #flags to make sure returned values are valid. + +@see PxScene.raycast PxBatchQuery.raycast +*/ +struct PxRaycastHit : public PxLocationHit +{ + PX_INLINE PxRaycastHit() : u(0.0f), v(0.0f) {} + + // the following fields are set in accordance with the #PxHitFlags + + PxReal u, v; //!< barycentric coordinates of hit point, for triangle mesh and height field (flag: #PxHitFlag::eUV) +#if !PX_P64_FAMILY + PxU32 padTo16Bytes[3]; +#endif +}; + + +/** +\brief Stores results of overlap queries. + +@see PxScene.overlap PxBatchQuery.overlap +*/ +struct PxOverlapHit: public PxQueryHit { PxU32 padTo16Bytes; }; + + +/** +\brief Stores results of sweep queries. + +@see PxScene.sweep PxBatchQuery.sweep +*/ +struct PxSweepHit : public PxLocationHit +{ + PX_INLINE PxSweepHit() {} + + PxU32 padTo16Bytes; +}; + + +/** +\brief Describes query behavior after returning a partial query result via a callback. + +If callback returns true, traversal will continue and callback can be issued again. +If callback returns false, traversal will stop, callback will not be issued again. + +@see PxHitCallback +*/ +typedef bool PxAgain; + + +/** +\brief This callback class facilitates reporting scene query hits (intersections) to the user. + +User overrides the virtual processTouches function to receive hits in (possibly multiple) fixed size blocks. + +\note PxHitBuffer derives from this class and is used to receive touching hits in a fixed size buffer. +\note Since the compiler doesn't look in template dependent base classes when looking for non-dependent names +\note with some compilers it will be necessary to use "this->hasBlock" notation to access a parent variable +\note in a child callback class. +\note Pre-made typedef shorthands, such as ::PxRaycastCallback can be used for raycast, overlap and sweep queries. + +@see PxHitBuffer PxRaycastHit PxSweepHit PxOverlapHit PxRaycastCallback PxOverlapCallback PxSweepCallback +*/ +template +struct PxHitCallback +{ + HitType block; //!< Holds the closest blocking hit result for the query. Invalid if hasBlock is false. + bool hasBlock; //!< Set to true if there was a blocking hit during query. + + HitType* touches; //!< User specified buffer for touching hits. + + /** + \brief Size of the user specified touching hits buffer. + \note If set to 0 all hits will default to PxQueryHitType::eBLOCK, otherwise to PxQueryHitType::eTOUCH + \note Hit type returned from pre-filter overrides this default */ + PxU32 maxNbTouches; + + /** + \brief Number of touching hits returned by the query. Used with PxHitBuffer. + \note If true (PxAgain) is returned from the callback, nbTouches will be reset to 0. */ + PxU32 nbTouches; + + /** + \brief Initializes the class with user provided buffer. + + \param[in] aTouches Optional buffer for recording PxQueryHitType::eTOUCH type hits. + \param[in] aMaxNbTouches Size of touch buffer. + + \note if aTouches is NULL and aMaxNbTouches is 0, only the closest blocking hit will be recorded by the query. + \note If PxQueryFlag::eANY_HIT flag is used as a query parameter, hasBlock will be set to true and blockingHit will be used to receive the result. + \note Both eTOUCH and eBLOCK hits will be registered as hasBlock=true and stored in PxHitCallback.block when eANY_HIT flag is used. + + @see PxHitCallback.hasBlock PxHitCallback.block */ + PxHitCallback(HitType* aTouches, PxU32 aMaxNbTouches) + : hasBlock(false), touches(aTouches), maxNbTouches(aMaxNbTouches), nbTouches(0) + {} + + /** + \brief virtual callback function used to communicate query results to the user. + + This callback will always be invoked with #touches as a buffer if #touches was specified as non-NULL. + All reported touch hits are guaranteed to be closer than the closest blocking hit. + + \param[in] buffer Callback will report touch hits to the user in this buffer. This pointer will be the same as #touches. + \param[in] nbHits Number of touch hits reported in buffer. This number will not exceed #maxNbTouches. + + \note There is a significant performance penalty in case multiple touch callbacks are issued (up to 2x) + \note to avoid the penalty use a bigger buffer so that all touching hits can be reported in a single buffer. + \note If true (again) is returned from the callback, nbTouches will be reset to 0, + \note If false is returned, nbTouched will remain unchanged. + \note By the time processTouches is first called, the globally closest blocking hit is already determined, + \note values of hasBlock and block are final and all touch hits are guaranteed to be closer than the blocking hit. + \note touches and maxNbTouches can be modified inside of processTouches callback. + + \return true to continue receiving callbacks in case there are more hits or false to stop. + + @see PxAgain PxRaycastHit PxSweepHit PxOverlapHit */ + virtual PxAgain processTouches(const HitType* buffer, PxU32 nbHits) = 0; + + virtual void finalizeQuery() {} //!< Query finalization callback, called after the last processTouches callback. + + virtual ~PxHitCallback() {} + + /** \brief Returns true if any blocking or touching hits were encountered during a query. */ + PX_FORCE_INLINE bool hasAnyHits() { return (hasBlock || (nbTouches > 0)); } +}; + + +/** +\brief Returns scene query hits (intersections) to the user in a preallocated buffer. + +Will clip touch hits to maximum buffer capacity. When clipped, an arbitrary subset of touching hits will be discarded. +Overflow does not trigger warnings or errors. block and hasBlock will be valid in finalizeQuery callback and after query completion. +Touching hits are guaranteed to have closer or same distance ( <= condition) as the globally nearest blocking hit at the time any processTouches() +callback is issued. + +\note Pre-made typedef shorthands, such as ::PxRaycastBuffer can be used for raycast, overlap and sweep queries. + +@see PxHitCallback +@see PxRaycastBuffer PxOverlapBuffer PxSweepBuffer PxRaycastBufferN PxOverlapBufferN PxSweepBufferN +*/ +template +struct PxHitBuffer : public PxHitCallback +{ + /** + \brief Initializes the buffer with user memory. + + The buffer is initialized with 0 touch hits by default => query will only report a single closest blocking hit. + Use PxQueryFlag::eANY_HIT to tell the query to abort and return any first hit encoutered as blocking. + + \param[in] aTouches Optional buffer for recording PxQueryHitType::eTOUCH type hits. + \param[in] aMaxNbTouches Size of touch buffer. + + @see PxHitCallback */ + PxHitBuffer(HitType* aTouches = NULL, PxU32 aMaxNbTouches = 0) : PxHitCallback(aTouches, aMaxNbTouches) {} + + /** \brief Computes the number of any hits in this result, blocking or touching. */ + PX_INLINE PxU32 getNbAnyHits() const { return getNbTouches() + PxU32(this->hasBlock); } + /** \brief Convenience iterator used to access any hits in this result, blocking or touching. */ + PX_INLINE const HitType& getAnyHit(const PxU32 index) const { PX_ASSERT(index < getNbTouches() + PxU32(this->hasBlock)); + return index < getNbTouches() ? getTouches()[index] : this->block; } + + PX_INLINE PxU32 getNbTouches() const { return this->nbTouches; } + PX_INLINE const HitType* getTouches() const { return this->touches; } + PX_INLINE const HitType& getTouch(const PxU32 index) const { PX_ASSERT(index < getNbTouches()); return getTouches()[index]; } + PX_INLINE PxU32 getMaxNbTouches() const { return this->maxNbTouches; } + + virtual ~PxHitBuffer() {} + +protected: + // stops after the first callback + virtual PxAgain processTouches(const HitType* buffer, PxU32 nbHits) { PX_UNUSED(buffer); PX_UNUSED(nbHits); return false; } +}; + + +/** \brief Raycast query callback. */ +typedef PxHitCallback PxRaycastCallback; + +/** \brief Overlap query callback. */ +typedef PxHitCallback PxOverlapCallback; + +/** \brief Sweep query callback. */ +typedef PxHitCallback PxSweepCallback; + +/** \brief Raycast query buffer. */ +typedef PxHitBuffer PxRaycastBuffer; + +/** \brief Overlap query buffer. */ +typedef PxHitBuffer PxOverlapBuffer; + +/** \brief Sweep query buffer. */ +typedef PxHitBuffer PxSweepBuffer; + +/** \brief Returns touching raycast hits to the user in a fixed size array embedded in the buffer class. **/ +template +struct PxRaycastBufferN : public PxHitBuffer +{ + PxRaycastHit hits[N]; + PxRaycastBufferN() : PxHitBuffer(hits, N) {} +}; + +/** \brief Returns touching overlap hits to the user in a fixed size array embedded in the buffer class. **/ +template +struct PxOverlapBufferN : public PxHitBuffer +{ + PxOverlapHit hits[N]; + PxOverlapBufferN() : PxHitBuffer(hits, N) {} +}; + +/** \brief Returns touching sweep hits to the user in a fixed size array embedded in the buffer class. **/ +template +struct PxSweepBufferN : public PxHitBuffer +{ + PxSweepHit hits[N]; + PxSweepBufferN() : PxHitBuffer(hits, N) {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxRigidActor.h b/src/PhysX/physx/include/PxRigidActor.h new file mode 100644 index 000000000..950f35e59 --- /dev/null +++ b/src/PhysX/physx/include/PxRigidActor.h @@ -0,0 +1,237 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_RIGIDACTOR +#define PX_PHYSICS_NX_RIGIDACTOR +/** \addtogroup physics +@{ +*/ + +#include "PxActor.h" +#include "PxShape.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxConstraint; +class PxMaterial; +class PxGeometry; +class PxBVHStructure; + +/** +\brief PxRigidActor represents a base class shared between dynamic and static rigid bodies in the physics SDK. + +PxRigidActor objects specify the geometry of the object by defining a set of attached shapes (see #PxShape). + +@see PxActor +*/ + +class PxRigidActor : public PxActor +{ +public: + /** + \brief Deletes the rigid actor object. + + Also releases any shapes associated with the actor. + + Releasing an actor will affect any objects that are connected to the actor (constraint shaders like joints etc.). + Such connected objects will be deleted upon scene deletion, or explicitly by the user by calling release() + on these objects. It is recommended to always remove all objects that reference actors before the actors + themselves are removed. It is not possible to retrieve list of dead connected objects. + + Sleeping: This call will awaken any sleeping actors contacting the deleted actor (directly or indirectly). + + Calls #PxActor::release() so you might want to check the documentation of that method as well. + + @see PxActor::release() + */ + virtual void release() = 0; + + +/************************************************************************************************/ +/** @name Global Pose Manipulation +*/ + + /** + \brief Retrieves the actors world space transform. + + The getGlobalPose() method retrieves the actor's current actor space to world space transformation. + + \return Global pose of object. + + @see PxRigidDynamic.setGlobalPose() PxRigidStatic.setGlobalPose() + */ + virtual PxTransform getGlobalPose() const = 0; + + /** + \brief Method for setting an actor's pose in the world. + + This method instantaneously changes the actor space to world space transformation. + + This method is mainly for dynamic rigid bodies (see #PxRigidDynamic). Calling this method on static actors is + likely to result in a performance penalty, since internal optimization structures for static actors may need to be + recomputed. In addition, moving static actors will not interact correctly with dynamic actors or joints. + + To directly control an actor's position and have it correctly interact with dynamic bodies and joints, create a dynamic + body with the PxRigidBodyFlag::eKINEMATIC flag, then use the setKinematicTarget() commands to define its path. + + Even when moving dynamic actors, exercise restraint in making use of this method. Where possible, avoid: + + \li moving actors into other actors, thus causing overlap (an invalid physical state) + + \li moving an actor that is connected by a joint to another away from the other (thus causing joint error) + + Sleeping: This call wakes dynamic actors if they are sleeping and the autowake parameter is true (default). + + \param[in] pose Transformation from the actors local frame to the global frame. Range: rigid body transform. + \param[in] autowake whether to wake the object if it is dynamic. This parameter has no effect for static or kinematic actors. If true and the current wake counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see getGlobalPose() + */ + virtual void setGlobalPose(const PxTransform& pose, bool autowake = true) = 0; + + +/************************************************************************************************/ +/** @name Shapes +*/ + + /** attach a shared shape to an actor + + This call will increment the reference count of the shape. + + \note Mass properties of dynamic rigid actors will not automatically be recomputed + to reflect the new mass distribution implied by the shape. Follow this call with a call to + the PhysX extensions method #PxRigidBodyExt::updateMassAndInertia() to do that. + + Attaching a triangle mesh, heightfield or plane geometry shape configured as eSIMULATION_SHAPE is not supported for + non-kinematic PxRigidDynamic instances. + + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] shape the shape to attach. + + \return True if success. + */ + virtual bool attachShape(PxShape& shape) = 0; + + + /** detach a shape from an actor. + + This will also decrement the reference count of the PxShape, and if the reference count is zero, will cause it to be deleted. + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] shape the shape to detach. + \param[in] wakeOnLostTouch Specifies whether touching objects from the previous frame should get woken up in the next frame. Only applies to PxArticulation and PxRigidActor types. + + */ + virtual void detachShape(PxShape& shape, bool wakeOnLostTouch = true) = 0; + + + /** + \brief Returns the number of shapes assigned to the actor. + + You can use #getShapes() to retrieve the shape pointers. + + \return Number of shapes associated with this actor. + + @see PxShape getShapes() + */ + virtual PxU32 getNbShapes() const = 0; + + + /** + \brief Retrieve all the shape pointers belonging to the actor. + + These are the shapes used by the actor for collision detection. + + You can retrieve the number of shape pointers by calling #getNbShapes() + + Note: Removing shapes with #PxShape::release() will invalidate the pointer of the released shape. + + \param[out] userBuffer The buffer to store the shape pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first shape pointer to be retrieved + \return Number of shape pointers written to the buffer. + + @see PxShape getNbShapes() PxShape::release() + */ + virtual PxU32 getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + +/************************************************************************************************/ +/** @name Constraints +*/ + + /** + \brief Returns the number of constraint shaders attached to the actor. + + You can use #getConstraints() to retrieve the constraint shader pointers. + + \return Number of constraint shaders attached to this actor. + + @see PxConstraint getConstraints() + */ + virtual PxU32 getNbConstraints() const = 0; + + + /** + \brief Retrieve all the constraint shader pointers belonging to the actor. + + You can retrieve the number of constraint shader pointers by calling #getNbConstraints() + + Note: Removing constraint shaders with #PxConstraint::release() will invalidate the pointer of the released constraint. + + \param[out] userBuffer The buffer to store the constraint shader pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first constraint pointer to be retrieved + \return Number of constraint shader pointers written to the buffer. + + @see PxConstraint getNbConstraints() PxConstraint::release() + */ + virtual PxU32 getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + +protected: + PX_INLINE PxRigidActor(PxType concreteType, PxBaseFlags baseFlags) : PxActor(concreteType, baseFlags) {} + PX_INLINE PxRigidActor(PxBaseFlags baseFlags) : PxActor(baseFlags) {} + virtual ~PxRigidActor() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxRigidActor", name) || PxActor::isKindOf(name); } +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxRigidBody.h b/src/PhysX/physx/include/PxRigidBody.h new file mode 100644 index 000000000..f25628f3c --- /dev/null +++ b/src/PhysX/physx/include/PxRigidBody.h @@ -0,0 +1,701 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_RIGIDBODY +#define PX_PHYSICS_NX_RIGIDBODY +/** \addtogroup physics +@{ +*/ + +#include "PxRigidActor.h" +#include "PxForceMode.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + +/** +\brief Collection of flags describing the behavior of a rigid body. + +@see PxRigidBody.setRigidBodyFlag(), PxRigidBody.getRigidBodyFlags() +*/ + +struct PxRigidBodyFlag +{ + enum Enum + { + + /** + \brief Enables kinematic mode for the actor. + + Kinematic actors are special dynamic actors that are not + influenced by forces (such as gravity), and have no momentum. They are considered to have infinite + mass and can be moved around the world using the setKinematicTarget() method. They will push + regular dynamic actors out of the way. Kinematics will not collide with static or other kinematic objects. + + Kinematic actors are great for moving platforms or characters, where direct motion control is desired. + + You can not connect Reduced joints to kinematic actors. Lagrange joints work ok if the platform + is moving with a relatively low, uniform velocity. + + Sleeping: + \li Setting this flag on a dynamic actor will put the actor to sleep and set the velocities to 0. + \li If this flag gets cleared, the current sleep state of the actor will be kept. + + \note kinematic actors are incompatible with CCD so raising this flag will automatically clear eENABLE_CCD + + @see PxRigidDynamic.setKinematicTarget() + */ + eKINEMATIC = (1<<0), //!< Enable kinematic mode for the body. + + /** + \brief Use the kinematic target transform for scene queries. + + If this flag is raised, then scene queries will treat the kinematic target transform as the current pose + of the body (instead of using the actual pose). Without this flag, the kinematic target will only take + effect with respect to scene queries after a simulation step. + + @see PxRigidDynamic.setKinematicTarget() + */ + eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES = (1<<1), + + /** + \brief Enables swept integration for the actor. + + If this flag is raised and CCD is enabled on the scene, then this body will be simulated by the CCD system to ensure that collisions are not missed due to + high-speed motion. Note individual shape pairs still need to enable PxPairFlag::eDETECT_CCD_CONTACT in the collision filtering to enable the CCD to respond to + individual interactions. + + \note kinematic actors are incompatible with CCD so this flag will be cleared automatically when raised on a kinematic actor + + */ + eENABLE_CCD = (1<<2), //!< Enable CCD for the body. + + /** + \brief Enabled CCD in swept integration for the actor. + + If this flag is raised and CCD is enabled, CCD interactions will simulate friction. By default, friction is disabled in CCD interactions because + CCD friction has been observed to introduce some simulation artifacts. CCD friction was enabled in previous versions of the SDK. Raising this flag will result in behavior + that is a closer match for previous versions of the SDK. + + \note This flag requires PxRigidBodyFlag::eENABLE_CCD to be raised to have any effect. + */ + eENABLE_CCD_FRICTION = (1<<3), + + /** + \brief Register a rigid body for reporting pose changes by the simulation at an early stage. + + Sometimes it might be advantageous to get access to the new pose of a rigid body as early as possible and + not wait until the call to fetchResults() returns. Setting this flag will schedule the rigid body to get reported + in #PxSimulationEventCallback::onAdvance(). Please refer to the documentation of that callback to understand + the behavior and limitations of this functionality. + + @see PxSimulationEventCallback::onAdvance() + */ + eENABLE_POSE_INTEGRATION_PREVIEW = (1 << 4), + + /** + \brief Register a rigid body to dynamicly adjust contact offset based on velocity. This can be used to achieve a CCD effect. + */ + eENABLE_SPECULATIVE_CCD = (1 << 5), + + /** + \brief Permit CCD to limit maxContactImpulse. This is useful for use-cases like a destruction system but can cause visual artefacts so is not enabled by default. + */ + eENABLE_CCD_MAX_CONTACT_IMPULSE = (1 << 6), + + /** + \brief Carries over forces/accelerations between frames, rather than clearning them + */ + eRETAIN_ACCELERATIONS = (1<<7) + }; +}; + +/** +\brief collection of set bits defined in PxRigidBodyFlag. + +@see PxRigidBodyFlag +*/ +typedef PxFlags PxRigidBodyFlags; +PX_FLAGS_OPERATORS(PxRigidBodyFlag::Enum,PxU8) + +/** +\brief PxRigidBody is a base class shared between dynamic rigid body objects. + +@see PxRigidActor +*/ + +class PxRigidBody : public PxRigidActor +{ +public: + // Runtime modifications + +/************************************************************************************************/ +/** @name Mass Manipulation +*/ + + /** + \brief Sets the pose of the center of mass relative to the actor. + + \note Changing this transform will not move the actor in the world! + + \note Setting an unrealistic center of mass which is a long way from the body can make it difficult for + the SDK to solve constraints. Perhaps leading to instability and jittering bodies. + + Default: the identity transform + + \param[in] pose Mass frame offset transform relative to the actor frame. Range: rigid body transform. + + @see getCMassLocalPose() PxRigidBodyDesc.massLocalPose + */ + virtual void setCMassLocalPose(const PxTransform& pose) = 0; + + + /** + \brief Retrieves the center of mass pose relative to the actor frame. + + \return The center of mass pose relative to the actor frame. + + @see setCMassLocalPose() PxRigidBodyDesc.massLocalPose + */ + virtual PxTransform getCMassLocalPose() const = 0; + + + /** + \brief Sets the mass of a dynamic actor. + + The mass must be non-negative. + + setMass() does not update the inertial properties of the body, to change the inertia tensor + use setMassSpaceInertiaTensor() or the PhysX extensions method #PxRigidBodyExt::updateMassAndInertia(). + + \note A value of 0 is interpreted as infinite mass. + \note Values of 0 are not permitted for instances of PxArticulationLink but are permitted for instances of PxRigidDynamic. + + Default: 1.0 + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] mass New mass value for the actor. Range: [0, PX_MAX_F32) + + @see getMass() PxRigidBodyDesc.mass setMassSpaceInertiaTensor() + */ + virtual void setMass(PxReal mass) = 0; + + /** + \brief Retrieves the mass of the actor. + + \note A value of 0 is interpreted as infinite mass. + + \return The mass of this actor. + + @see setMass() PxRigidBodyDesc.mass setMassSpaceInertiaTensor() + */ + virtual PxReal getMass() const = 0; + + /** + \brief Retrieves the inverse mass of the actor. + + \return The inverse mass of this actor. + + @see setMass() PxRigidBodyDesc.mass setMassSpaceInertiaTensor() + */ + virtual PxReal getInvMass() const = 0; + + /** + \brief Sets the inertia tensor, using a parameter specified in mass space coordinates. + + Note that such matrices are diagonal -- the passed vector is the diagonal. + + If you have a non diagonal world/actor space inertia tensor(3x3 matrix). Then you need to + diagonalize it and set an appropriate mass space transform. See #setCMassLocalPose(). + + The inertia tensor elements must be non-negative. + + \note A value of 0 in an element is interpreted as infinite inertia along that axis. + \note Values of 0 are not permitted for instances of PxArticulationLink but are permitted for instances of PxRigidDynamic. + + Default: (1.0, 1.0, 1.0) + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] m New mass space inertia tensor for the actor. + + @see PxRigidBodyDesc.massSpaceInertia getMassSpaceInertia() setMass() setCMassLocalPose() + */ + virtual void setMassSpaceInertiaTensor(const PxVec3& m) = 0; + + /** + \brief Retrieves the diagonal inertia tensor of the actor relative to the mass coordinate frame. + + This method retrieves a mass frame inertia vector. + + \return The mass space inertia tensor of this actor. + + \note A value of 0 in an element is interpreted as infinite inertia along that axis. + + @see PxRigidBodyDesc.massSpaceInertia setMassSpaceInertiaTensor() setMass() setCMassLocalPose() + */ + virtual PxVec3 getMassSpaceInertiaTensor() const = 0; + + /** + \brief Retrieves the diagonal inverse inertia tensor of the actor relative to the mass coordinate frame. + + This method retrieves a mass frame inverse inertia vector. + + \note A value of 0 in an element is interpreted as infinite inertia along that axis. + + \return The mass space inverse inertia tensor of this actor. + + @see PxRigidBodyDesc.massSpaceInertia setMassSpaceInertiaTensor() setMass() setCMassLocalPose() + */ + virtual PxVec3 getMassSpaceInvInertiaTensor() const = 0; + + /************************************************************************************************/ + /** @name Damping + */ + + /** + \brief Sets the linear damping coefficient. + + Zero represents no damping. The damping coefficient must be nonnegative. + + Default: 0.0 + + \param[in] linDamp Linear damping coefficient. Range: [0, PX_MAX_F32) + + @see getLinearDamping() setAngularDamping() + */ + virtual void setLinearDamping(PxReal linDamp) = 0; + + /** + \brief Retrieves the linear damping coefficient. + + \return The linear damping coefficient associated with this actor. + + @see setLinearDamping() getAngularDamping() + */ + virtual PxReal getLinearDamping() const = 0; + + /** + \brief Sets the angular damping coefficient. + + Zero represents no damping. + + The angular damping coefficient must be nonnegative. + + Default: 0.05 + + \param[in] angDamp Angular damping coefficient. Range: [0, PX_MAX_F32) + + @see getAngularDamping() setLinearDamping() + */ + virtual void setAngularDamping(PxReal angDamp) = 0; + + /** + \brief Retrieves the angular damping coefficient. + + \return The angular damping coefficient associated with this actor. + + @see setAngularDamping() getLinearDamping() + */ + virtual PxReal getAngularDamping() const = 0; + + +/************************************************************************************************/ +/** @name Velocity +*/ + + + /** + \brief Retrieves the linear velocity of an actor. + + \return The linear velocity of the actor. + + @see PxRigidDynamic.setLinearVelocity() getAngularVelocity() + */ + virtual PxVec3 getLinearVelocity() const = 0; + + /** + \brief Sets the linear velocity of the actor. + + Note that if you continuously set the velocity of an actor yourself, + forces such as gravity or friction will not be able to manifest themselves, because forces directly + influence only the velocity/momentum of an actor. + + Default: (0.0, 0.0, 0.0) + + Sleeping: This call wakes the actor if it is sleeping, the autowake parameter is true (default) or the + new velocity is non-zero + + \note It is invalid to use this method if PxActorFlag::eDISABLE_SIMULATION is set. + + \param[in] linVel New linear velocity of actor. Range: velocity vector + \param[in] autowake Whether to wake the object up if it is asleep and the velocity is non-zero. If true and the current wake counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see getLinearVelocity() setAngularVelocity() + */ + virtual void setLinearVelocity(const PxVec3& linVel, bool autowake = true ) = 0; + + + + /** + \brief Retrieves the angular velocity of the actor. + + \return The angular velocity of the actor. + + @see PxRigidDynamic.setAngularVelocity() getLinearVelocity() + */ + virtual PxVec3 getAngularVelocity() const = 0; + + + /** + \brief Sets the angular velocity of the actor. + + Note that if you continuously set the angular velocity of an actor yourself, + forces such as friction will not be able to rotate the actor, because forces directly influence only the velocity/momentum. + + Default: (0.0, 0.0, 0.0) + + Sleeping: This call wakes the actor if it is sleeping, the autowake parameter is true (default) or the + new velocity is non-zero + + \note It is invalid to use this method if PxActorFlag::eDISABLE_SIMULATION is set. + + \param[in] angVel New angular velocity of actor. Range: angular velocity vector + \param[in] autowake Whether to wake the object up if it is asleep and the velocity is non-zero. If true and the current wake + counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see getAngularVelocity() setLinearVelocity() + */ + virtual void setAngularVelocity(const PxVec3& angVel, bool autowake = true ) = 0; + + /** + \brief Lets you set the maximum angular velocity permitted for this actor. + + For various internal computations, very quickly rotating actors introduce error + into the simulation, which leads to undesired results. + + With this function, you can set the maximum angular velocity permitted for this rigid body. + Higher angular velocities are clamped to this value. + + Note: The angular velocity is clamped to the set value before the solver, which means that + the limit may still be momentarily exceeded. + + Default: 100.0 + + \param[in] maxAngVel Max allowable angular velocity for actor. Range: [0, PX_MAX_F32) + + @see getMaxAngularVelocity() + */ + virtual void setMaxAngularVelocity(PxReal maxAngVel) = 0; + + /** + \brief Retrieves the maximum angular velocity permitted for this actor. + + \return The maximum allowed angular velocity for this actor. + + @see setMaxAngularVelocity + */ + virtual PxReal getMaxAngularVelocity() const = 0; + + + /** + \brief Lets you set the maximum linear velocity permitted for this actor. + + With this function, you can set the maximum linear velocity permitted for this rigid body. + Higher angular velocities are clamped to this value. + + Note: The angular velocity is clamped to the set value before the solver, which means that + the limit may still be momentarily exceeded. + + Default: PX_MAX_F32 + + \param[in] maxLinVel Max allowable linear velocity for actor. Range: [0, PX_MAX_F32) + + @see getMaxAngularVelocity() + */ + virtual void setMaxLinearVelocity(PxReal maxLinVel) = 0; + + /** + \brief Retrieves the maximum angular velocity permitted for this actor. + + \return The maximum allowed angular velocity for this actor. + + @see setMaxLinearVelocity + */ + virtual PxReal getMaxLinearVelocity() const = 0; + + +/************************************************************************************************/ +/** @name Forces +*/ + + /** + \brief Applies a force (or impulse) defined in the global coordinate frame to the actor at its center of mass. + + This will not induce a torque. + + ::PxForceMode determines if the force is to be conventional or impulsive. + + Each actor has an acceleration and a velocity change accumulator which are directly modified using the modes PxForceMode::eACCELERATION + and PxForceMode::eVELOCITY_CHANGE respectively. The modes PxForceMode::eFORCE and PxForceMode::eIMPULSE also modify these same + accumulators and are just short hand for multiplying the vector parameter by inverse mass and then using PxForceMode::eACCELERATION and + PxForceMode::eVELOCITY_CHANGE respectively. + + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \note The force modes PxForceMode::eIMPULSE and PxForceMode::eVELOCITY_CHANGE can not be applied to articulation links. + + \note if this is called on an articulation link, only the link is updated, not the entire articulation. + + \note see #PxRigidBodyExt::computeVelocityDeltaFromImpulse for details of how to compute the change in linear velocity that + will arise from the application of an impulsive force, where an impulsive force is applied force multiplied by a timestep. + + Sleeping: This call wakes the actor if it is sleeping and the autowake parameter is true (default) or the force is non-zero. + + \param[in] force Force/Impulse to apply defined in the global frame. + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode) + \param[in] autowake Specify if the call should wake up the actor if it is currently asleep. If true and the current wake counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see PxForceMode addTorque + */ + virtual void addForce(const PxVec3& force, PxForceMode::Enum mode = PxForceMode::eFORCE, bool autowake = true) = 0; + + /** + \brief Applies an impulsive torque defined in the global coordinate frame to the actor. + + ::PxForceMode determines if the torque is to be conventional or impulsive. + + Each actor has an angular acceleration and an angular velocity change accumulator which are directly modified using the modes + PxForceMode::eACCELERATION and PxForceMode::eVELOCITY_CHANGE respectively. The modes PxForceMode::eFORCE and PxForceMode::eIMPULSE + also modify these same accumulators and are just short hand for multiplying the vector parameter by inverse inertia and then + using PxForceMode::eACCELERATION and PxForceMode::eVELOCITY_CHANGE respectively. + + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \note The force modes PxForceMode::eIMPULSE and PxForceMode::eVELOCITY_CHANGE can not be applied to articulation links. + + \note if this called on an articulation link, only the link is updated, not the entire articulation. + + \note see #PxRigidBodyExt::computeVelocityDeltaFromImpulse for details of how to compute the change in angular velocity that + will arise from the application of an impulsive torque, where an impulsive torque is an applied torque multiplied by a timestep. + + Sleeping: This call wakes the actor if it is sleeping and the autowake parameter is true (default) or the torque is non-zero. + + \param[in] torque Torque to apply defined in the global frame. Range: torque vector + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode). + \param[in] autowake whether to wake up the object if it is asleep. If true and the current wake counter value is smaller than #PxSceneDesc::wakeCounterResetValue it will get increased to the reset value. + + @see PxForceMode addForce() + */ + virtual void addTorque(const PxVec3& torque, PxForceMode::Enum mode = PxForceMode::eFORCE, bool autowake = true) = 0; + + /** + \brief Clears the accumulated forces (sets the accumulated force back to zero). + + Each actor has an acceleration and a velocity change accumulator which are directly modified using the modes PxForceMode::eACCELERATION + and PxForceMode::eVELOCITY_CHANGE respectively. The modes PxForceMode::eFORCE and PxForceMode::eIMPULSE also modify these same + accumulators (see PxRigidBody::addForce() for details); therefore the effect of calling clearForce(PxForceMode::eFORCE) is equivalent to calling + clearForce(PxForceMode::eACCELERATION), and the effect of calling clearForce(PxForceMode::eIMPULSE) is equivalent to calling + clearForce(PxForceMode::eVELOCITY_CHANGE). + + ::PxForceMode determines if the cleared force is to be conventional or impulsive. + + \note The force modes PxForceMode::eIMPULSE and PxForceMode::eVELOCITY_CHANGE can not be applied to articulation links. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \param[in] mode The mode to use when clearing the force/impulse(see #PxForceMode) + + @see PxForceMode addForce + */ + virtual void clearForce(PxForceMode::Enum mode = PxForceMode::eFORCE) = 0; + + /** + \brief Clears the impulsive torque defined in the global coordinate frame to the actor. + + ::PxForceMode determines if the cleared torque is to be conventional or impulsive. + + Each actor has an angular acceleration and a velocity change accumulator which are directly modified using the modes PxForceMode::eACCELERATION + and PxForceMode::eVELOCITY_CHANGE respectively. The modes PxForceMode::eFORCE and PxForceMode::eIMPULSE also modify these same + accumulators (see PxRigidBody::addTorque() for details); therefore the effect of calling clearTorque(PxForceMode::eFORCE) is equivalent to calling + clearTorque(PxForceMode::eACCELERATION), and the effect of calling clearTorque(PxForceMode::eIMPULSE) is equivalent to calling + clearTorque(PxForceMode::eVELOCITY_CHANGE). + + \note The force modes PxForceMode::eIMPULSE and PxForceMode::eVELOCITY_CHANGE can not be applied to articulation links. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \param[in] mode The mode to use when clearing the force/impulse(see #PxForceMode). + + @see PxForceMode addTorque + */ + virtual void clearTorque(PxForceMode::Enum mode = PxForceMode::eFORCE) = 0; + + + /** + \brief Sets the impulsive force and torque defined in the global coordinate frame to the actor. + + ::PxForceMode determines if the cleared torque is to be conventional or impulsive. + + \note The force modes PxForceMode::eIMPULSE and PxForceMode::eVELOCITY_CHANGE can not be applied to articulation links. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + @see PxForceMode addTorque + */ + virtual void setForceAndTorque(const PxVec3& force, const PxVec3& torque, PxForceMode::Enum mode = PxForceMode::eFORCE) = 0; + + /** + \brief Raises or clears a particular rigid body flag. + + See the list of flags #PxRigidBodyFlag + + Default: no flags are set + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] flag The PxRigidBody flag to raise(set) or clear. See #PxRigidBodyFlag. + \param[in] value The new boolean value for the flag. + + @see PxRigidBodyFlag getRigidBodyFlags() + */ + + virtual void setRigidBodyFlag(PxRigidBodyFlag::Enum flag, bool value) = 0; + virtual void setRigidBodyFlags(PxRigidBodyFlags inFlags) = 0; + + /** + \brief Reads the PxRigidBody flags. + + See the list of flags #PxRigidBodyFlag + + \return The values of the PxRigidBody flags. + + @see PxRigidBodyFlag setRigidBodyFlag() + */ + virtual PxRigidBodyFlags getRigidBodyFlags() const = 0; + + /** + \brief Sets the CCD minimum advance coefficient. + + The CCD minimum advance coefficient is a value in the range [0, 1] that is used to control the minimum amount of time a body is integrated when + it has a CCD contact. The actual minimum amount of time that is integrated depends on various properties, including the relative speed and collision shapes + of the bodies involved in the contact. From these properties, a numeric value is calculated that determines the maximum distance (and therefore maximum time) + which these bodies could be integrated forwards that would ensure that these bodies did not pass through each-other. This value is then scaled by CCD minimum advance + coefficient to determine the amount of time that will be consumed in the CCD pass. + + Things to consider: + A large value (approaching 1) ensures that the objects will always advance some time. However, larger values increase the chances of objects gently drifting through each-other in + scenes which the constraint solver can't converge, e.g. scenes where an object is being dragged through a wall with a constraint. + A value of 0 ensures that the pair of objects stop at the exact time-of-impact and will not gently drift through each-other. However, with very small/thin objects initially in + contact, this can lead to a large amount of time being dropped and increases the chances of jamming. Jamming occurs when the an object is persistently in contact with an object + such that the time-of-impact is 0, which results in no time being advanced for those objects in that CCD pass. + + The chances of jamming can be reduced by increasing the number of CCD mass @see PxSceneDesc.ccdMaxPasses. However, increasing this number increases the CCD overhead. + + \param[in] advanceCoefficient The CCD min advance coefficient. Range: [0, 1] Default: 0.15 + */ + + virtual void setMinCCDAdvanceCoefficient(PxReal advanceCoefficient) = 0; + + /** + \brief Gets the CCD minimum advance coefficient. + + \return The value of the CCD min advance coefficient. + + @see setMinCCDAdvanceCoefficient + + */ + + virtual PxReal getMinCCDAdvanceCoefficient() const = 0; + + + /** + \brief Sets the maximum depenetration velocity permitted to be introduced by the solver. + This value controls how much velocity the solver can introduce to correct for penetrations in contacts. + \param[in] biasClamp The maximum velocity to de-penetrate by Range: (0, PX_MAX_F32]. + */ + virtual void setMaxDepenetrationVelocity(PxReal biasClamp) = 0; + + /** + \brief Returns the maximum depenetration velocity the solver is permitted to introduced. + This value controls how much velocity the solver can introduce to correct for penetrations in contacts. + \return The maximum penetration bias applied by the solver. + */ + virtual PxReal getMaxDepenetrationVelocity() const = 0; + + + /** + \brief Sets a limit on the impulse that may be applied at a contact. The maximum impulse at a contact between two dynamic or kinematic + bodies will be the minimum of the two limit values. For a collision between a static and a dynamic body, the impulse is limited + by the value for the dynamic body. + + \param[in] maxImpulse the maximum contact impulse. Range: [0, PX_MAX_F32] Default: PX_MAX_F32 + + @see getMaxContactImpulse + */ + virtual void setMaxContactImpulse(PxReal maxImpulse) = 0; + + /** + \brief Returns the maximum impulse that may be applied at a contact. + + \return The maximum impulse that may be applied at a contact + + @see setMaxContactImpulse + */ + virtual PxReal getMaxContactImpulse() const = 0; + + /** + \brief Returns the island node index that only for internal use only + + \return The island node index that only for internal use only + */ + virtual PxU32 getInternalIslandNodeIndex() const = 0; + + +protected: + PX_INLINE PxRigidBody(PxType concreteType, PxBaseFlags baseFlags) : PxRigidActor(concreteType, baseFlags) {} + PX_INLINE PxRigidBody(PxBaseFlags baseFlags) : PxRigidActor(baseFlags) {} + virtual ~PxRigidBody() {} + virtual bool isKindOf(const char* name)const { return !::strcmp("PxRigidBody", name) || PxRigidActor::isKindOf(name); } +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxRigidDynamic.h b/src/PhysX/physx/include/PxRigidDynamic.h new file mode 100644 index 000000000..071e9a9d3 --- /dev/null +++ b/src/PhysX/physx/include/PxRigidDynamic.h @@ -0,0 +1,388 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_RIGIDDYNAMIC +#define PX_PHYSICS_NX_RIGIDDYNAMIC +/** \addtogroup physics +@{ +*/ + +#include "PxRigidBody.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + +/** +\brief Collection of flags providing a mechanism to lock motion along/around a specific axis. + +@see PxRigidDynamic.setRigidDynamicLockFlag(), PxRigidBody.getRigidDynamicLockFlags() +*/ +struct PxRigidDynamicLockFlag +{ + enum Enum + { + eLOCK_LINEAR_X = (1 << 0), + eLOCK_LINEAR_Y = (1 << 1), + eLOCK_LINEAR_Z = (1 << 2), + eLOCK_ANGULAR_X = (1 << 3), + eLOCK_ANGULAR_Y = (1 << 4), + eLOCK_ANGULAR_Z = (1 << 5) + }; +}; + +typedef PxFlags PxRigidDynamicLockFlags; +PX_FLAGS_OPERATORS(PxRigidDynamicLockFlag::Enum, PxU16) + +/** +\brief PxRigidDynamic represents a dynamic rigid simulation object in the physics SDK. + +

            Creation

            +Instances of this class are created by calling #PxPhysics::createRigidDynamic() and deleted with #release(). + + +

            Visualizations

            +\li #PxVisualizationParameter::eACTOR_AXES +\li #PxVisualizationParameter::eBODY_AXES +\li #PxVisualizationParameter::eBODY_MASS_AXES +\li #PxVisualizationParameter::eBODY_LIN_VELOCITY +\li #PxVisualizationParameter::eBODY_ANG_VELOCITY + +@see PxRigidBody PxPhysics.createRigidDynamic() release() +*/ + +class PxRigidDynamic : public PxRigidBody +{ +public: + // Runtime modifications + + +/************************************************************************************************/ +/** @name Kinematic Actors +*/ + + /** + \brief Moves kinematically controlled dynamic actors through the game world. + + You set a dynamic actor to be kinematic using the PxRigidBodyFlag::eKINEMATIC flag + with setRigidBodyFlag(). + + The move command will result in a velocity that will move the body into + the desired pose. After the move is carried out during a single time step, + the velocity is returned to zero. Thus, you must continuously call + this in every time step for kinematic actors so that they move along a path. + + This function simply stores the move destination until the next simulation + step is processed, so consecutive calls will simply overwrite the stored target variable. + + The motion is always fully carried out. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + Sleeping: This call wakes the actor if it is sleeping and will set the wake counter to #PxSceneDesc::wakeCounterResetValue. + + \param[in] destination The desired pose for the kinematic actor, in the global frame. Range: rigid body transform. + + @see getKinematicTarget() PxRigidBodyFlag setRigidBodyFlag() + */ + virtual void setKinematicTarget(const PxTransform& destination) = 0; + + /** + \brief Get target pose of a kinematically controlled dynamic actor. + + \param[out] target Transform to write the target pose to. Only valid if the method returns true. + \return True if the actor is a kinematically controlled dynamic and the target has been set, else False. + + @see setKinematicTarget() PxRigidBodyFlag setRigidBodyFlag() + */ + virtual bool getKinematicTarget(PxTransform& target) const = 0; + + +/************************************************************************************************/ +/** @name Sleeping +*/ + + /** + \brief Returns true if this body is sleeping. + + When an actor does not move for a period of time, it is no longer simulated in order to save time. This state + is called sleeping. However, because the object automatically wakes up when it is either touched by an awake object, + or one of its properties is changed by the user, the entire sleep mechanism should be transparent to the user. + + In general, a dynamic rigid actor is guaranteed to be awake if at least one of the following holds: + + \li The wake counter is positive (see #setWakeCounter()). + \li The linear or angular velocity is non-zero. + \li A non-zero force or torque has been applied. + + If a dynamic rigid actor is sleeping, the following state is guaranteed: + + \li The wake counter is zero. + \li The linear and angular velocity is zero. + \li There is no force update pending. + + When an actor gets inserted into a scene, it will be considered asleep if all the points above hold, else it will be treated as awake. + + If an actor is asleep after the call to PxScene::fetchResults() returns, it is guaranteed that the pose of the actor + was not changed. You can use this information to avoid updating the transforms of associated objects. + + \note A kinematic actor is asleep unless a target pose has been set (in which case it will stay awake until the end of the next + simulation step where no target pose has been set anymore). The wake counter will get set to zero or to the reset value + #PxSceneDesc::wakeCounterResetValue in the case where a target pose has been set to be consistent with the definitions above. + + \note It is invalid to use this method if the actor has not been added to a scene already. + + \return True if the actor is sleeping. + + @see isSleeping() wakeUp() putToSleep() getSleepThreshold() + */ + virtual bool isSleeping() const = 0; + + + /** + \brief Sets the mass-normalized kinetic energy threshold below which an actor may go to sleep. + + Actors whose kinetic energy divided by their mass is below this threshold will be candidates for sleeping. + + Default: 5e-5f * PxTolerancesScale::speed * PxTolerancesScale::speed + + \param[in] threshold Energy below which an actor may go to sleep. Range: [0, PX_MAX_F32) + + @see isSleeping() getSleepThreshold() wakeUp() putToSleep() PxTolerancesScale + */ + virtual void setSleepThreshold(PxReal threshold) = 0; + + /** + \brief Returns the mass-normalized kinetic energy below which an actor may go to sleep. + + \return The energy threshold for sleeping. + + @see isSleeping() wakeUp() putToSleep() setSleepThreshold() + */ + virtual PxReal getSleepThreshold() const = 0; + + /** + \brief Sets the mass-normalized kinetic energy threshold below which an actor may participate in stabilization. + + Actors whose kinetic energy divided by their mass is above this threshold will not participate in stabilization. + + This value has no effect if PxSceneFlag::eENABLE_STABILIZATION was not enabled on the PxSceneDesc. + + Default: 1e-5f * PxTolerancesScale::speed * PxTolerancesScale::speed + + \param[in] threshold Energy below which an actor may participate in stabilization. Range: [0,inf) + + @see getStabilizationThreshold() PxSceneFlag::eENABLE_STABILIZATION + */ + virtual void setStabilizationThreshold(PxReal threshold) = 0; + + /** + \brief Returns the mass-normalized kinetic energy below which an actor may participate in stabilization. + + Actors whose kinetic energy divided by their mass is above this threshold will not participate in stabilization. + + \return The energy threshold for participating in stabilization. + + @see setStabilizationThreshold() PxSceneFlag::eENABLE_STABILIZATION + */ + virtual PxReal getStabilizationThreshold() const = 0; + + + /** + \brief Reads the PxRigidDynamic lock flags. + + See the list of flags #PxRigidDynamicLockFlag + + \return The values of the PxRigidDynamicLock flags. + + @see PxRigidDynamicLockFlag setRigidDynamicLockFlag() + */ + virtual PxRigidDynamicLockFlags getRigidDynamicLockFlags() const = 0; + + /** + \brief Raises or clears a particular rigid dynamic lock flag. + + See the list of flags #PxRigidDynamicLockFlag + + Default: no flags are set + + + \param[in] flag The PxRigidDynamicLockBody flag to raise(set) or clear. See #PxRigidBodyFlag. + \param[in] value The new boolean value for the flag. + + @see PxRigidDynamicLockFlag getRigidDynamicLockFlags() + */ + virtual void setRigidDynamicLockFlag(PxRigidDynamicLockFlag::Enum flag, bool value) = 0; + virtual void setRigidDynamicLockFlags(PxRigidDynamicLockFlags flags) = 0; + + + + /** + \brief Sets the wake counter for the actor. + + The wake counter value determines the minimum amount of time until the body can be put to sleep. Please note + that a body will not be put to sleep if the energy is above the specified threshold (see #setSleepThreshold()) + or if other awake bodies are touching it. + + \note Passing in a positive value will wake the actor up automatically. + + \note It is invalid to use this method for kinematic actors since the wake counter for kinematics is defined + based on whether a target pose has been set (see the comment in #isSleeping()). + + \note It is invalid to use this method if PxActorFlag::eDISABLE_SIMULATION is set. + + Default: 0.4 (which corresponds to 20 frames for a time step of 0.02) + + \param[in] wakeCounterValue Wake counter value. Range: [0, PX_MAX_F32) + + @see isSleeping() getWakeCounter() + */ + virtual void setWakeCounter(PxReal wakeCounterValue) = 0; + + /** + \brief Returns the wake counter of the actor. + + \return The wake counter of the actor. + + @see isSleeping() setWakeCounter() + */ + virtual PxReal getWakeCounter() const = 0; + + /** + \brief Wakes up the actor if it is sleeping. + + The actor will get woken up and might cause other touching actors to wake up as well during the next simulation step. + + \note This will set the wake counter of the actor to the value specified in #PxSceneDesc::wakeCounterResetValue. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \note It is invalid to use this method for kinematic actors since the sleep state for kinematics is defined + based on whether a target pose has been set (see the comment in #isSleeping()). + + @see isSleeping() putToSleep() + */ + virtual void wakeUp() = 0; + + /** + \brief Forces the actor to sleep. + + The actor will stay asleep during the next simulation step if not touched by another non-sleeping actor. + + \note Any applied force will be cleared and the velocity and the wake counter of the actor will be set to 0. + + \note It is invalid to use this method if the actor has not been added to a scene already or if PxActorFlag::eDISABLE_SIMULATION is set. + + \note It is invalid to use this method for kinematic actors since the sleep state for kinematics is defined + based on whether a target pose has been set (see the comment in #isSleeping()). + + @see isSleeping() wakeUp() + */ + virtual void putToSleep() = 0; + +/************************************************************************************************/ + + /** + \brief Sets the solver iteration counts for the body. + + The solver iteration count determines how accurately joints and contacts are resolved. + If you are having trouble with jointed bodies oscillating and behaving erratically, then + setting a higher position iteration count may improve their stability. + + If intersecting bodies are being depenetrated too violently, increase the number of velocity + iterations. More velocity iterations will drive the relative exit velocity of the intersecting + objects closer to the correct value given the restitution. + + Default: 4 position iterations, 1 velocity iteration + + \param[in] minPositionIters Number of position iterations the solver should perform for this body. Range: [1,255] + \param[in] minVelocityIters Number of velocity iterations the solver should perform for this body. Range: [1,255] + + @see getSolverIterationCounts() + */ + virtual void setSolverIterationCounts(PxU32 minPositionIters, PxU32 minVelocityIters = 1) = 0; + + /** + \brief Retrieves the solver iteration counts. + + @see setSolverIterationCounts() + */ + virtual void getSolverIterationCounts(PxU32& minPositionIters, PxU32& minVelocityIters) const = 0; + + /** + \brief Retrieves the force threshold for contact reports. + + The contact report threshold is a force threshold. If the force between + two actors exceeds this threshold for either of the two actors, a contact report + will be generated according to the contact report threshold flags provided by + the filter shader/callback. + See #PxPairFlag. + + The threshold used for a collision between a dynamic actor and the static environment is + the threshold of the dynamic actor, and all contacts with static actors are summed to find + the total normal force. + + Default: PX_MAX_F32 + + \return Force threshold for contact reports. + + @see setContactReportThreshold PxPairFlag PxSimulationFilterShader PxSimulationFilterCallback + */ + virtual PxReal getContactReportThreshold() const = 0; + + /** + \brief Sets the force threshold for contact reports. + + See #getContactReportThreshold(). + + \param[in] threshold Force threshold for contact reports. Range: [0, PX_MAX_F32) + + @see getContactReportThreshold PxPairFlag + */ + virtual void setContactReportThreshold(PxReal threshold) = 0; + + virtual const char* getConcreteTypeName() const { return "PxRigidDynamic"; } + +protected: + PX_INLINE PxRigidDynamic(PxType concreteType, PxBaseFlags baseFlags) : PxRigidBody(concreteType, baseFlags) {} + PX_INLINE PxRigidDynamic(PxBaseFlags baseFlags) : PxRigidBody(baseFlags) {} + virtual ~PxRigidDynamic() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxRigidDynamic", name) || PxRigidBody::isKindOf(name); } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxRigidStatic.h b/src/PhysX/physx/include/PxRigidStatic.h new file mode 100644 index 000000000..9d9281455 --- /dev/null +++ b/src/PhysX/physx/include/PxRigidStatic.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_RIGIDSTATIC +#define PX_PHYSICS_NX_RIGIDSTATIC +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxRigidActor.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief PxRigidStatic represents a static rigid body simulation object in the physics SDK. + +PxRigidStatic objects are static rigid physics entities. They shall be used to define solid objects which are fixed in the world. + +

            Creation

            +Instances of this class are created by calling #PxPhysics::createRigidStatic() and deleted with #release(). + +

            Visualizations

            +\li #PxVisualizationParameter::eACTOR_AXES + +@see PxRigidActor PxPhysics.createRigidStatic() release() +*/ + +class PxRigidStatic : public PxRigidActor +{ +public: + virtual const char* getConcreteTypeName() const { return "PxRigidStatic"; } + +protected: + PX_INLINE PxRigidStatic(PxType concreteType, PxBaseFlags baseFlags) : PxRigidActor(concreteType, baseFlags) {} + PX_INLINE PxRigidStatic(PxBaseFlags baseFlags) : PxRigidActor(baseFlags) {} + virtual ~PxRigidStatic() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxRigidStatic", name) || PxRigidActor::isKindOf(name); } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxScene.h b/src/PhysX/physx/include/PxScene.h new file mode 100644 index 000000000..49ea87f34 --- /dev/null +++ b/src/PhysX/physx/include/PxScene.h @@ -0,0 +1,1649 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENE +#define PX_PHYSICS_NX_SCENE +/** \addtogroup physics +@{ +*/ + +#include "PxVisualizationParameter.h" +#include "PxSceneDesc.h" +#include "PxSimulationStatistics.h" +#include "PxQueryReport.h" +#include "PxQueryFiltering.h" +#include "PxClient.h" +#include "task/PxTask.h" + +#include "pvd/PxPvdSceneClient.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxRigidStatic; +class PxRigidDynamic; +class PxConstraint; +class PxMaterial; +class PxSimulationEventCallback; +class PxPhysics; +class PxBatchQueryDesc; +class PxBatchQuery; +class PxAggregate; +class PxRenderBuffer; + +class PxSphereGeometry; +class PxBoxGeometry; +class PxCapsuleGeometry; + +class PxPruningStructure; +class PxBVHStructure; +struct PxContactPairHeader; + +typedef PxU8 PxDominanceGroup; + +class PxPvdSceneClient; + +/** +\brief Expresses the dominance relationship of a contact. +For the time being only three settings are permitted: + +(1, 1), (0, 1), and (1, 0). + +@see getDominanceGroup() PxDominanceGroup PxScene::setDominanceGroupPair() +*/ +struct PxDominanceGroupPair +{ + PxDominanceGroupPair(PxU8 a, PxU8 b) + : dominance0(a), dominance1(b) {} + PxU8 dominance0; + PxU8 dominance1; +}; + + +/** +\brief Identifies each type of actor for retrieving actors from a scene. + +\note #PxArticulationLink objects are not supported. Use the #PxArticulation object to retrieve all its links. + +@see PxScene::getActors(), PxScene::getNbActors() +*/ +struct PxActorTypeFlag +{ + enum Enum + { + /** + \brief A static rigid body + @see PxRigidStatic + */ + eRIGID_STATIC = (1 << 0), + + /** + \brief A dynamic rigid body + @see PxRigidDynamic + */ + eRIGID_DYNAMIC = (1 << 1) + }; +}; + +/** +\brief Collection of set bits defined in PxActorTypeFlag. + +@see PxActorTypeFlag +*/ +typedef PxFlags PxActorTypeFlags; +PX_FLAGS_OPERATORS(PxActorTypeFlag::Enum,PxU16) + +/** +\brief single hit cache for scene queries. + +If a cache object is supplied to a scene query, the cached actor/shape pair is checked for intersection first. +\note Filters are not executed for the cached shape. +\note If intersection is found, the hit is treated as blocking. +\note Typically actor and shape from the last PxHitCallback.block query result is used as a cached actor/shape pair. +\note Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. +\note Cache is only used if no touch buffer was provided, for single nearest blocking hit queries and queries using eANY_HIT flag. +\note if non-zero touch buffer was provided, cache will be ignored + +\note It is the user's responsibility to ensure that the shape and actor are valid, so care must be taken +when deleting shapes to invalidate cached references. + +The faceIndex field is an additional hint for a mesh or height field which is not currently used. + +@see PxScene.raycast +*/ +struct PxQueryCache +{ + /** + \brief constructor sets to default + */ + PX_INLINE PxQueryCache() : shape(NULL), actor(NULL), faceIndex(0xffffffff) {} + + /** + \brief constructor to set properties + */ + PX_INLINE PxQueryCache(PxShape* s, PxU32 findex) : shape(s), actor(NULL), faceIndex(findex) {} + + PxShape* shape; //!< Shape to test for intersection first + PxRigidActor* actor; //!< Actor to which the shape belongs + PxU32 faceIndex; //!< Triangle index to test first - NOT CURRENTLY SUPPORTED +}; + +/** + \brief A scene is a collection of bodies and constraints which can interact. + + The scene simulates the behavior of these objects over time. Several scenes may exist + at the same time, but each body or constraint is specific to a scene + -- they may not be shared. + + @see PxSceneDesc PxPhysics.createScene() release() +*/ +class PxScene +{ + protected: + + /************************************************************************************************/ + + /** @name Basics + */ + //@{ + + PxScene(): userData(0) {} + virtual ~PxScene() {} + + public: + + /** + \brief Deletes the scene. + + Removes any actors and constraint shaders from this scene + (if the user hasn't already done so). + + Be sure to not keep a reference to this object after calling release. + Avoid release calls while the scene is simulating (in between simulate() and fetchResults() calls). + + @see PxPhysics.createScene() + */ + virtual void release() = 0; + + /** + \brief Sets a scene flag. You can only set one flag at a time. + + \note Not all flags are mutable and changing some will result in an error. Please check #PxSceneFlag to see which flags can be changed. + + @see PxSceneFlag + */ + virtual void setFlag(PxSceneFlag::Enum flag, bool value) = 0; + + /** + \brief Get the scene flags. + + \return The scene flags. See #PxSceneFlag + + @see PxSceneFlag + */ + virtual PxSceneFlags getFlags() const = 0; + + + /** + \brief Set new scene limits. + + \note Increase the maximum capacity of various data structures in the scene. The new capacities will be + at least as large as required to deal with the objects currently in the scene. Further, these values + are for preallocation and do not represent hard limits. + + \param[in] limits Scene limits. + @see PxSceneLimits + */ + virtual void setLimits(const PxSceneLimits& limits) = 0; + + /** + \brief Get current scene limits. + \return Current scene limits. + @see PxSceneLimits + */ + virtual PxSceneLimits getLimits() const = 0; + + + /** + \brief Call this method to retrieve the Physics SDK. + + \return The physics SDK this scene is associated with. + + @see PxPhysics + */ + virtual PxPhysics& getPhysics() = 0; + + /** + \brief Retrieves the scene's internal timestamp, increased each time a simulation step is completed. + + \return scene timestamp + */ + virtual PxU32 getTimestamp() const = 0; + + + //@} + /************************************************************************************************/ + + /** @name Add/Remove Contained Objects + */ + //@{ + /** + \brief Adds an articulation to this scene. + + \note If the articulation is already assigned to a scene (see #PxArticulation::getScene), the call is ignored and an error is issued. + + \param[in] articulation Articulation to add to scene. See #PxArticulation + + @see PxArticulation + */ + virtual void addArticulation(PxArticulationBase& articulation) = 0; + + /** + \brief Removes an articulation from this scene. + + \note If the articulation is not part of this scene (see #PxArticulation::getScene), the call is ignored and an error is issued. + + \note If the articulation is in an aggregate it will be removed from the aggregate. + + \param[in] articulation Articulation to remove from scene. See #PxArticulation + \param[in] wakeOnLostTouch Specifies whether touching objects from the previous frame should get woken up in the next frame. Only applies to PxArticulation and PxRigidActor types. + + @see PxArticulation, PxAggregate + */ + virtual void removeArticulation(PxArticulationBase& articulation, bool wakeOnLostTouch = true) = 0; + + + //@} + /************************************************************************************************/ + + + /** + \brief Adds an actor to this scene. + + \note If the actor is already assigned to a scene (see #PxActor::getScene), the call is ignored and an error is issued. + \note If the actor has an invalid constraint, in checked builds the call is ignored and an error is issued. + + \note You can not add individual articulation links (see #PxArticulationLink) to the scene. Use #addArticulation() instead. + + \note If the actor is a PxRigidActor then each assigned PxConstraint object will get added to the scene automatically if + it connects to another actor that is part of the scene already. + + \note When BVHStructure is provided the actor shapes are grouped together. + The scene query pruning structure inside PhysX SDK will store/update one + bound per actor. The scene queries against such an actor will query actor + bounds and then make a local space query against the provided BVH structure, which is in + actor's local space. + + \param[in] actor Actor to add to scene. + \param[in] bvhStructure BVHStructure for actor shapes. + + @see PxActor, PxConstraint::isValid(), PxBVHStructure + */ + virtual void addActor(PxActor& actor, const PxBVHStructure* bvhStructure = NULL) = 0; + + /** + \brief Adds actors to this scene. + + \note If one of the actors is already assigned to a scene (see #PxActor::getScene), the call is ignored and an error is issued. + + \note You can not add individual articulation links (see #PxArticulationLink) to the scene. Use #addArticulation() instead. + + \note If an actor in the array contains an invalid constraint, in checked builds the call is ignored and an error is issued. + \note If an actor in the array is a PxRigidActor then each assigned PxConstraint object will get added to the scene automatically if + it connects to another actor that is part of the scene already. + + \note this method is optimized for high performance, and does not support buffering. It may not be called during simulation. + + \param[in] actors Array of actors to add to scene. + \param[in] nbActors Number of actors in the array. + + @see PxActor, PxConstraint::isValid() + */ + virtual void addActors(PxActor*const* actors, PxU32 nbActors) = 0; + + /** + \brief Adds a pruning structure together with its actors to this scene. + + \note If an actor in the pruning structure contains an invalid constraint, in checked builds the call is ignored and an error is issued. + \note For all actors in the pruning structure each assigned PxConstraint object will get added to the scene automatically if + it connects to another actor that is part of the scene already. + + \note This method is optimized for high performance, and does not support buffering. It may not be called during simulation. + + \note Merging a PxPruningStructure into an active scene query optimization AABB tree might unbalance the tree. A typical use case for + PxPruningStructure is a large world scenario where blocks of closely positioned actors get streamed in. The merge process finds the + best node in the active scene query optimization AABB tree and inserts the PxPruningStructure. Therefore using PxPruningStructure + for actors scattered throughout the world will result in an unbalanced tree. + + \param[in] pruningStructure Pruning structure for a set of actors. + + @see PxPhysics::createPruningStructure, PxPruningStructure + */ + virtual void addActors(const PxPruningStructure& pruningStructure) = 0; + + /** + \brief Removes an actor from this scene. + + \note If the actor is not part of this scene (see #PxActor::getScene), the call is ignored and an error is issued. + + \note You can not remove individual articulation links (see #PxArticulationLink) from the scene. Use #removeArticulation() instead. + + \note If the actor is a PxRigidActor then all assigned PxConstraint objects will get removed from the scene automatically. + + \note If the actor is in an aggregate it will be removed from the aggregate. + + \param[in] actor Actor to remove from scene. + \param[in] wakeOnLostTouch Specifies whether touching objects from the previous frame should get woken up in the next frame. Only applies to PxArticulation and PxRigidActor types. + + @see PxActor, PxAggregate + */ + virtual void removeActor(PxActor& actor, bool wakeOnLostTouch = true) = 0; + + /** + \brief Removes actors from this scene. + + \note If some actor is not part of this scene (see #PxActor::getScene), the actor remove is ignored and an error is issued. + + \note You can not remove individual articulation links (see #PxArticulationLink) from the scene. Use #removeArticulation() instead. + + \note If the actor is a PxRigidActor then all assigned PxConstraint objects will get removed from the scene automatically. + + \param[in] actors Array of actors to add to scene. + \param[in] nbActors Number of actors in the array. + \param[in] wakeOnLostTouch Specifies whether touching objects from the previous frame should get woken up in the next frame. Only applies to PxArticulation and PxRigidActor types. + + @see PxActor + */ + virtual void removeActors(PxActor*const* actors, PxU32 nbActors, bool wakeOnLostTouch = true) = 0; + + /** + \brief Adds an aggregate to this scene. + + \note If the aggregate is already assigned to a scene (see #PxAggregate::getScene), the call is ignored and an error is issued. + \note If the aggregate contains an actor with an invalid constraint, in checked builds the call is ignored and an error is issued. + + \note If the aggregate already contains actors, those actors are added to the scene as well. + + \param[in] aggregate Aggregate to add to scene. + + @see PxAggregate, PxConstraint::isValid() + */ + virtual void addAggregate(PxAggregate& aggregate) = 0; + + /** + \brief Removes an aggregate from this scene. + + \note If the aggregate is not part of this scene (see #PxAggregate::getScene), the call is ignored and an error is issued. + + \note If the aggregate contains actors, those actors are removed from the scene as well. + + \param[in] aggregate Aggregate to remove from scene. + \param[in] wakeOnLostTouch Specifies whether touching objects from the previous frame should get woken up in the next frame. Only applies to PxArticulation and PxRigidActor types. + + @see PxAggregate + */ + virtual void removeAggregate(PxAggregate& aggregate, bool wakeOnLostTouch = true) = 0; + + /** + \brief Adds objects in the collection to this scene. + + This function adds the following types of objects to this scene: PxActor, PxAggregate, PxArticulation. + This method is typically used after deserializing the collection in order to populate the scene with deserialized objects. + + \note If the collection contains an actor with an invalid constraint, in checked builds the call is ignored and an error is issued. + + \param[in] collection Objects to add to this scene. See #PxCollection + + @see PxCollection, PxConstraint::isValid() + */ + virtual void addCollection(const PxCollection& collection) = 0; + //@} + /************************************************************************************************/ + + /** @name Contained Object Retrieval + */ + //@{ + + /** + \brief Retrieve the number of actors of certain types in the scene. + + \param[in] types Combination of actor types. + \return the number of actors. + + @see getActors() + */ + virtual PxU32 getNbActors(PxActorTypeFlags types) const = 0; + + /** + \brief Retrieve an array of all the actors of certain types in the scene. + + \param[in] types Combination of actor types to retrieve. + \param[out] userBuffer The buffer to receive actor pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first actor pointer to be retrieved + \return Number of actors written to the buffer. + + @see getNbActors() + */ + virtual PxU32 getActors(PxActorTypeFlags types, PxActor** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Queries the PxScene for a list of the PxActors whose transforms have been + updated during the previous simulation step + + \note PxSceneFlag::eENABLE_ACTIVE_ACTORS must be set. + + \note Do not use this method while the simulation is running. Calls to this method while the simulation is running will be ignored and NULL will be returned. + + \param[out] nbActorsOut The number of actors returned. + + \return A pointer to the list of active PxActors generated during the last call to fetchResults(). + + @see PxActor + */ + virtual PxActor** getActiveActors(PxU32& nbActorsOut) = 0; + + /** + \brief Returns the number of articulations in the scene. + + \return the number of articulations in this scene. + + @see getArticulations() + */ + virtual PxU32 getNbArticulations() const = 0; + + /** + \brief Retrieve all the articulations in the scene. + + \param[out] userBuffer The buffer to receive articulations pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first articulations pointer to be retrieved + \return Number of articulations written to the buffer. + + @see getNbArticulations() + */ + virtual PxU32 getArticulations(PxArticulationBase** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Returns the number of constraint shaders in the scene. + + \return the number of constraint shaders in this scene. + + @see getConstraints() + */ + virtual PxU32 getNbConstraints() const = 0; + + /** + \brief Retrieve all the constraint shaders in the scene. + + \param[out] userBuffer The buffer to receive constraint shader pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first constraint pointer to be retrieved + \return Number of constraint shaders written to the buffer. + + @see getNbConstraints() + */ + virtual PxU32 getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + + /** + \brief Returns the number of aggregates in the scene. + + \return the number of aggregates in this scene. + + @see getAggregates() + */ + virtual PxU32 getNbAggregates() const = 0; + + /** + \brief Retrieve all the aggregates in the scene. + + \param[out] userBuffer The buffer to receive aggregates pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first aggregate pointer to be retrieved + \return Number of aggregates written to the buffer. + + @see getNbAggregates() + */ + virtual PxU32 getAggregates(PxAggregate** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + //@} + /************************************************************************************************/ + + /** @name Dominance + */ + //@{ + + /** + \brief Specifies the dominance behavior of contacts between two actors with two certain dominance groups. + + It is possible to assign each actor to a dominance groups using #PxActor::setDominanceGroup(). + + With dominance groups one can have all contacts created between actors act in one direction only. This is useful, for example, if you + want an object to push debris out of its way and be unaffected,while still responding physically to forces and collisions + with non-debris objects. + + Whenever a contact between two actors (a0, a1) needs to be solved, the groups (g0, g1) of both + actors are retrieved. Then the PxDominanceGroupPair setting for this group pair is retrieved with getDominanceGroupPair(g0, g1). + + In the contact, PxDominanceGroupPair::dominance0 becomes the dominance setting for a0, and + PxDominanceGroupPair::dominance1 becomes the dominance setting for a1. A dominanceN setting of 1.0f, the default, + will permit aN to be pushed or pulled by a(1-N) through the contact. A dominanceN setting of 0.0f, will however + prevent aN to be pushed by a(1-N) via the contact. Thus, a PxDominanceGroupPair of (1.0f, 0.0f) makes + the interaction one-way. + + + The matrix sampled by getDominanceGroupPair(g1, g2) is initialised by default such that: + + if g1 == g2, then (1.0f, 1.0f) is returned + if g1 < g2, then (0.0f, 1.0f) is returned + if g1 > g2, then (1.0f, 0.0f) is returned + + In other words, we permit actors in higher groups to be pushed around by actors in lower groups by default. + + These settings should cover most applications, and in fact not overriding these settings may likely result in higher performance. + + It is not possible to make the matrix asymetric, or to change the diagonal. In other words: + + * it is not possible to change (g1, g2) if (g1==g2) + * if you set + + (g1, g2) to X, then (g2, g1) will implicitly and automatically be set to ~X, where: + + ~(1.0f, 1.0f) is (1.0f, 1.0f) + ~(0.0f, 1.0f) is (1.0f, 0.0f) + ~(1.0f, 0.0f) is (0.0f, 1.0f) + + These two restrictions are to make sure that contacts between two actors will always evaluate to the same dominance + setting, regardless of the order of the actors. + + Dominance settings are currently specified as floats 0.0f or 1.0f because in the future we may permit arbitrary + fractional settings to express 'partly-one-way' interactions. + + Sleeping: Does NOT wake actors up automatically. + + @see getDominanceGroupPair() PxDominanceGroup PxDominanceGroupPair PxActor::setDominanceGroup() PxActor::getDominanceGroup() + */ + virtual void setDominanceGroupPair( + PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance) = 0; + + /** + \brief Samples the dominance matrix. + + @see setDominanceGroupPair() PxDominanceGroup PxDominanceGroupPair PxActor::setDominanceGroup() PxActor::getDominanceGroup() + */ + virtual PxDominanceGroupPair getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const = 0; + + //@} + /************************************************************************************************/ + + /** @name Dispatcher + */ + //@{ + + /** + \brief Return the cpu dispatcher that was set in PxSceneDesc::cpuDispatcher when creating the scene with PxPhysics::createScene + + @see PxSceneDesc::cpuDispatcher, PxPhysics::createScene + */ + virtual PxCpuDispatcher* getCpuDispatcher() const = 0; + + /** + \brief Return the gpu dispatcher that was set in PxSceneDesc::gpuDispatcher when creating the scene with PxPhysics::createScene + + Platform specific: Applies to PC GPU only. + + @see PxSceneDesc::gpuDispatcher, PxPhysics::createScene + */ + virtual PxGpuDispatcher* getGpuDispatcher() const = 0; + + //@} + /************************************************************************************************/ + /** @name Multiclient + */ + //@{ + /** + \brief Reserves a new client ID. + + PX_DEFAULT_CLIENT is always available as the default clientID. + Additional clients are returned by this function. Clients cannot be released once created. + An error is reported when more than a supported number of clients (currently 128) are created. + + @see PxClientID + */ + virtual PxClientID createClient() = 0; + + //@} + + /************************************************************************************************/ + + /** @name Callbacks + */ + //@{ + + /** + \brief Sets a user notify object which receives special simulation events when they occur. + + \note Do not set the callback while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[in] callback User notification callback. See #PxSimulationEventCallback. + + @see PxSimulationEventCallback getSimulationEventCallback + */ + virtual void setSimulationEventCallback(PxSimulationEventCallback* callback) = 0; + + /** + \brief Retrieves the simulationEventCallback pointer set with setSimulationEventCallback(). + + \return The current user notify pointer. See #PxSimulationEventCallback. + + @see PxSimulationEventCallback setSimulationEventCallback() + */ + virtual PxSimulationEventCallback* getSimulationEventCallback() const = 0; + + /** + \brief Sets a user callback object, which receives callbacks on all contacts generated for specified actors. + + \note Do not set the callback while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[in] callback Asynchronous user contact modification callback. See #PxContactModifyCallback. + */ + virtual void setContactModifyCallback(PxContactModifyCallback* callback) = 0; + + /** + \brief Sets a user callback object, which receives callbacks on all CCD contacts generated for specified actors. + + \note Do not set the callback while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[in] callback Asynchronous user contact modification callback. See #PxCCDContactModifyCallback. + */ + virtual void setCCDContactModifyCallback(PxCCDContactModifyCallback* callback) = 0; + + /** + \brief Retrieves the PxContactModifyCallback pointer set with setContactModifyCallback(). + + \return The current user contact modify callback pointer. See #PxContactModifyCallback. + + @see PxContactModifyCallback setContactModifyCallback() + */ + virtual PxContactModifyCallback* getContactModifyCallback() const = 0; + + /** + \brief Retrieves the PxCCDContactModifyCallback pointer set with setContactModifyCallback(). + + \return The current user contact modify callback pointer. See #PxContactModifyCallback. + + @see PxContactModifyCallback setContactModifyCallback() + */ + virtual PxCCDContactModifyCallback* getCCDContactModifyCallback() const = 0; + + /** + \brief Sets a broad-phase user callback object. + + \note Do not set the callback while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[in] callback Asynchronous broad-phase callback. See #PxBroadPhaseCallback. + */ + virtual void setBroadPhaseCallback(PxBroadPhaseCallback* callback) = 0; + + /** + \brief Retrieves the PxBroadPhaseCallback pointer set with setBroadPhaseCallback(). + + \return The current broad-phase callback pointer. See #PxBroadPhaseCallback. + + @see PxBroadPhaseCallback setBroadPhaseCallback() + */ + virtual PxBroadPhaseCallback* getBroadPhaseCallback() const = 0; + + //@} + /************************************************************************************************/ + + /** @name Collision Filtering + */ + //@{ + + /** + \brief Sets the shared global filter data which will get passed into the filter shader. + + \note It is the user's responsibility to ensure that changing the shared global filter data does not change the filter output value for existing pairs. + If the filter output for existing pairs does change nonetheless then such a change will not take effect until the pair gets refiltered. + resetFiltering() can be used to explicitly refilter the pairs of specific objects. + + \note The provided data will get copied to internal buffers and this copy will be used for filtering calls. + + \note Do not use this method while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[in] data The shared global filter shader data. + \param[in] dataSize Size of the shared global filter shader data (in bytes). + + @see getFilterShaderData() PxSceneDesc.filterShaderData PxSimulationFilterShader + */ + virtual void setFilterShaderData(const void* data, PxU32 dataSize) = 0; + + /** + \brief Gets the shared global filter data in use for this scene. + + \note The reference points to a copy of the original filter data specified in #PxSceneDesc.filterShaderData or provided by #setFilterShaderData(). + + \return Shared filter data for filter shader. + + @see getFilterShaderDataSize() setFilterShaderData() PxSceneDesc.filterShaderData PxSimulationFilterShader + */ + virtual const void* getFilterShaderData() const = 0; + + /** + \brief Gets the size of the shared global filter data (#PxSceneDesc.filterShaderData) + + \return Size of shared filter data [bytes]. + + @see getFilterShaderData() PxSceneDesc.filterShaderDataSize PxSimulationFilterShader + */ + virtual PxU32 getFilterShaderDataSize() const = 0; + + /** + \brief Gets the custom collision filter shader in use for this scene. + + \return Filter shader class that defines the collision pair filtering. + + @see PxSceneDesc.filterShader PxSimulationFilterShader + */ + virtual PxSimulationFilterShader + getFilterShader() const = 0; + + /** + \brief Gets the custom collision filter callback in use for this scene. + + \return Filter callback class that defines the collision pair filtering. + + @see PxSceneDesc.filterCallback PxSimulationFilterCallback + */ + virtual PxSimulationFilterCallback* + getFilterCallback() const = 0; + + /** + \brief Marks the object to reset interactions and re-run collision filters in the next simulation step. + + This call forces the object to remove all existing collision interactions, to search anew for existing contact + pairs and to run the collision filters again for found collision pairs. + + \note The operation is supported for PxRigidActor objects only. + + \note All persistent state of existing interactions will be lost and can not be retrieved even if the same collison pair + is found again in the next step. This will mean, for example, that you will not get notified about persistent contact + for such an interaction (see #PxPairFlag::eNOTIFY_TOUCH_PERSISTS), the contact pair will be interpreted as newly found instead. + + \note Lost touch contact reports will be sent for every collision pair which includes this shape, if they have + been requested through #PxPairFlag::eNOTIFY_TOUCH_LOST or #PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST. + + \note This is an expensive operation, don't use it if you don't have to. + + \note Can be used to retrieve collision pairs that were killed by the collision filters (see #PxFilterFlag::eKILL) + + \note It is invalid to use this method if the actor has not been added to a scene already. + + \note It is invalid to use this method if PxActorFlag::eDISABLE_SIMULATION is set. + + Sleeping: Does wake up the actor. + + \param[in] actor The actor for which to re-evaluate interactions. + + @see PxSimulationFilterShader PxSimulationFilterCallback + */ + virtual void resetFiltering(PxActor& actor) = 0; + + /** + \brief Marks the object to reset interactions and re-run collision filters for specified shapes in the next simulation step. + + This is a specialization of the resetFiltering(PxActor& actor) method and allows to reset interactions for specific shapes of + a PxRigidActor. + + Sleeping: Does wake up the actor. + + \param[in] actor The actor for which to re-evaluate interactions. + \param[in] shapes The shapes for which to re-evaluate interactions. + \param[in] shapeCount Number of shapes in the list. + + @see PxSimulationFilterShader PxSimulationFilterCallback + */ + virtual void resetFiltering(PxRigidActor& actor, PxShape*const* shapes, PxU32 shapeCount) = 0; + + //@} + /************************************************************************************************/ + + /** @name Simulation + */ + //@{ + /** + \brief Advances the simulation by an elapsedTime time. + + \note Large elapsedTime values can lead to instabilities. In such cases elapsedTime + should be subdivided into smaller time intervals and simulate() should be called + multiple times for each interval. + + Calls to simulate() should pair with calls to fetchResults(): + Each fetchResults() invocation corresponds to exactly one simulate() + invocation; calling simulate() twice without an intervening fetchResults() + or fetchResults() twice without an intervening simulate() causes an error + condition. + + scene->simulate(); + ...do some processing until physics is computed... + scene->fetchResults(); + ...now results of run may be retrieved. + + + \param[in] elapsedTime Amount of time to advance simulation by. The parameter has to be larger than 0, else the resulting behavior will be undefined. Range: (0, PX_MAX_F32) + \param[in] completionTask if non-NULL, this task will have its refcount incremented in simulate(), then + decremented when the scene is ready to have fetchResults called. So the task will not run until the + application also calls removeReference(). + \param[in] scratchMemBlock a memory region for physx to use for temporary data during simulation. This block may be reused by the application + after fetchResults returns. Must be aligned on a 16-byte boundary + \param[in] scratchMemBlockSize the size of the scratch memory block. Must be a multiple of 16K. + \param[in] controlSimulation if true, the scene controls its PxTaskManager simulation state. Leave + true unless the application is calling the PxTaskManager start/stopSimulation() methods itself. + + @see fetchResults() checkResults() + */ + virtual void simulate(PxReal elapsedTime, physx::PxBaseTask* completionTask = NULL, + void* scratchMemBlock = 0, PxU32 scratchMemBlockSize = 0, bool controlSimulation = true) = 0; + + + /** + \brief Performs dynamics phase of the simulation pipeline. + + \note Calls to advance() should follow calls to fetchCollision(). An error message will be issued if this sequence is not followed. + + \param[in] completionTask if non-NULL, this task will have its refcount incremented in advance(), then + decremented when the scene is ready to have fetchResults called. So the task will not run until the + application also calls removeReference(). + + */ + virtual void advance(physx::PxBaseTask* completionTask = 0) = 0; + + /** + \brief Performs collision detection for the scene over elapsedTime + + \note Calls to collide() should be the first method called to simulate a frame. + + + \param[in] elapsedTime Amount of time to advance simulation by. The parameter has to be larger than 0, else the resulting behavior will be undefined. Range: (0, PX_MAX_F32) + \param[in] completionTask if non-NULL, this task will have its refcount incremented in collide(), then + decremented when the scene is ready to have fetchResults called. So the task will not run until the + application also calls removeReference(). + \param[in] scratchMemBlock a memory region for physx to use for temporary data during simulation. This block may be reused by the application + after fetchResults returns. Must be aligned on a 16-byte boundary + \param[in] scratchMemBlockSize the size of the scratch memory block. Must be a multiple of 16K. + \param[in] controlSimulation if true, the scene controls its PxTaskManager simulation state. Leave + true unless the application is calling the PxTaskManager start/stopSimulation() methods itself. + + */ + virtual void collide(PxReal elapsedTime, physx::PxBaseTask* completionTask = 0, void* scratchMemBlock = 0, + PxU32 scratchMemBlockSize = 0, bool controlSimulation = true) = 0; + + /** + \brief This checks to see if the simulation run has completed. + + This does not cause the data available for reading to be updated with the results of the simulation, it is simply a status check. + The bool will allow it to either return immediately or block waiting for the condition to be met so that it can return true + + \param[in] block When set to true will block until the condition is met. + \return True if the results are available. + + @see simulate() fetchResults() + */ + virtual bool checkResults(bool block = false) = 0; + + /** + This method must be called after collide() and before advance(). It will wait for the collision phase to finish. If the user makes an illegal simulation call, the SDK will issue an error + message. + + \param[in] block When set to true will block until the condition is met, which is collision must finish running. + */ + + virtual bool fetchCollision(bool block = false) = 0; + + /** + This is the big brother to checkResults() it basically does the following: + + \code + if ( checkResults(block) ) + { + fire appropriate callbacks + swap buffers + return true + } + else + return false + + \endcode + + \param[in] block When set to true will block until results are available. + \param[out] errorState Used to retrieve hardware error codes. A non zero value indicates an error. + \return True if the results have been fetched. + + @see simulate() checkResults() + */ + virtual bool fetchResults(bool block = false, PxU32* errorState = 0) = 0; + + + /** + This call performs the first section of fetchResults (callbacks fired before swapBuffers), and returns a pointer to a + to the contact streams output by the simulation. It can be used to process contact pairs in parallel, which is often a limiting factor + for fetchResults() performance. + + After calling this function and processing the contact streams, call fetchResultsFinish(). Note that writes to the simulation are not + permitted between the start of fetchResultsStart() and the end of fetchResultsFinish(). + + \param[in] block When set to true will block until results are available. + \param[out] contactPairs an array of pointers to contact pair headers + \param[out] nbContactPairs the number of contact pairs + \return True if the results have been fetched. + + @see simulate() checkResults() fetchResults() fetchResultsFinish() + */ + virtual bool fetchResultsStart(const PxContactPairHeader*& contactPairs, PxU32& nbContactPairs, bool block = false) = 0; + + + /** + This call processes all event callbacks in parallel. It takes a continuation task, which will be executed once all callbacks have been processed. + + This is a utility function to make it easier to process callbacks in parallel using the PhysX task system. It can only be used in conjunction with + fetchResultsStart(...) and fetchResultsFinish(...) + + \param[in] continuation The task that will be executed once all callbacks have been processed. + */ + virtual void processCallbacks(physx::PxBaseTask* continuation) = 0; + + + /** + This call performs the second section of fetchResults: the buffer swap and subsequent callbacks. + + It must be called after fetchResultsStart() returns and contact reports have been processed. + + Note that once fetchResultsFinish() has been called, the contact streams returned in fetchResultsStart() will be invalid. + + \param[out] errorState Used to retrieve hardware error codes. A non zero value indicates an error. + + @see simulate() checkResults() fetchResults() fetchResultsStart() + */ + virtual void fetchResultsFinish(PxU32* errorState = 0) = 0; + + + /** + \brief Clear internal buffers and free memory. + + This method can be used to clear buffers and free internal memory without having to destroy the scene. Can be useful if + the physics data gets streamed in and a checkpoint with a clean state should be created. + + \note It is not allowed to call this method while the simulation is running. The call will fail. + + \param[in] sendPendingReports When set to true pending reports will be sent out before the buffers get cleaned up (for instance lost touch contact/trigger reports due to deleted objects). + */ + virtual void flushSimulation(bool sendPendingReports = false) = 0; + + /** + \brief Sets a constant gravity for the entire scene. + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] vec A new gravity vector(e.g. PxVec3(0.0f,-9.8f,0.0f) ) Range: force vector + + @see PxSceneDesc.gravity getGravity() + */ + virtual void setGravity(const PxVec3& vec) = 0; + + /** + \brief Retrieves the current gravity setting. + + \return The current gravity for the scene. + + @see setGravity() PxSceneDesc.gravity + */ + virtual PxVec3 getGravity() const = 0; + + /** + \brief Set the bounce threshold velocity. Collision speeds below this threshold will not cause a bounce. + + @see PxSceneDesc::bounceThresholdVelocity, getBounceThresholdVelocity + */ + virtual void setBounceThresholdVelocity(const PxReal t) = 0; + + /** + \brief Return the bounce threshold velocity. + + @see PxSceneDesc.bounceThresholdVelocity, setBounceThresholdVelocity + */ + virtual PxReal getBounceThresholdVelocity() const = 0; + + + /** + \brief Sets the maximum number of CCD passes + + \param[in] ccdMaxPasses Maximum number of CCD passes + + @see PxSceneDesc.ccdMaxPasses getCCDMaxPasses() + + */ + virtual void setCCDMaxPasses(PxU32 ccdMaxPasses) = 0; + + /** + \brief Gets the maximum number of CCD passes. + + \return The maximum number of CCD passes. + + @see PxSceneDesc::ccdMaxPasses setCCDMaxPasses() + + */ + virtual PxU32 getCCDMaxPasses() const = 0; + + /** + \brief Return the value of frictionOffsetThreshold that was set in PxSceneDesc when creating the scene with PxPhysics::createScene + + @see PxSceneDesc::frictionOffsetThreshold, PxPhysics::createScene + */ + virtual PxReal getFrictionOffsetThreshold() const = 0; + + /** + \brief Set the friction model. + @see PxFrictionType, PxSceneDesc::frictionType + */ + virtual void setFrictionType(PxFrictionType::Enum frictionType) = 0; + + /** + \brief Return the friction model. + @see PxFrictionType, PxSceneDesc::frictionType + */ + virtual PxFrictionType::Enum getFrictionType() const = 0; + + //@} + /************************************************************************************************/ + + /** @name Visualization and Statistics + */ + //@{ + /** + \brief Function that lets you set debug visualization parameters. + + Returns false if the value passed is out of range for usage specified by the enum. + + \param[in] param Parameter to set. See #PxVisualizationParameter + \param[in] value The value to set, see #PxVisualizationParameter for allowable values. Setting to zero disables visualization for the specified property, setting to a positive value usually enables visualization and defines the scale factor. + \return False if the parameter is out of range. + + @see getVisualizationParameter PxVisualizationParameter getRenderBuffer() + */ + virtual bool setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) = 0; + + /** + \brief Function that lets you query debug visualization parameters. + + \param[in] paramEnum The Parameter to retrieve. + \return The value of the parameter. + + @see setVisualizationParameter PxVisualizationParameter + */ + virtual PxReal getVisualizationParameter(PxVisualizationParameter::Enum paramEnum) const = 0; + + + /** + \brief Defines a box in world space to which visualization geometry will be (conservatively) culled. Use a non-empty culling box to enable the feature, and an empty culling box to disable it. + + \param[in] box the box to which the geometry will be culled. Empty box to disable the feature. + @see setVisualizationParameter getVisualizationCullingBox getRenderBuffer() + */ + virtual void setVisualizationCullingBox(const PxBounds3& box) = 0; + + /** + \brief Retrieves the visualization culling box. + + \return the box to which the geometry will be culled. + @see setVisualizationParameter setVisualizationCullingBox + */ + virtual PxBounds3 getVisualizationCullingBox() const = 0; + + /** + \brief Retrieves the render buffer. + + This will contain the results of any active visualization for this scene. + + \note Do not use this method while the simulation is running. Calls to this method while result in undefined behaviour. + + \return The render buffer. + + @see PxRenderBuffer + */ + virtual const PxRenderBuffer& getRenderBuffer() = 0; + + /** + \brief Call this method to retrieve statistics for the current simulation step. + + \note Do not use this method while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \param[out] stats Used to retrieve statistics for the current simulation step. + + @see PxSimulationStatistics + */ + virtual void getSimulationStatistics(PxSimulationStatistics& stats) const = 0; + + + //@} + /************************************************************************************************/ + + /** @name Scene Query + */ + //@{ + + /** + \brief Return the value of PxSceneDesc::staticStructure that was set when creating the scene with PxPhysics::createScene + + @see PxSceneDesc::staticStructure, PxPhysics::createScene + */ + virtual PxPruningStructureType::Enum getStaticStructure() const = 0; + + /** + \brief Return the value of PxSceneDesc::dynamicStructure that was set when creating the scene with PxPhysics::createScene + + @see PxSceneDesc::dynamicStructure, PxPhysics::createScene + */ + virtual PxPruningStructureType::Enum getDynamicStructure() const = 0; + + /** + \brief Flushes any changes in the simulation to the scene query representation. + + This method updates the state of the scene query representation to match changes in the scene state. + + By default, these changes are buffered until the next query is submitted. Calling this function will not change + the results from scene queries, but can be used to ensure that a query will not perform update work in the course of + its execution. + + A thread performing updates will hold a write lock on the query structure, and thus stall other querying threads. In multithread + scenarios it can be useful to explicitly schedule the period where this lock may be held for a significant period, so that + subsequent queries issued from multiple threads will not block. + + Note that while queries will block during the execution of this method, other read operations on the scene will not. + */ + virtual void flushQueryUpdates() = 0; + + /** + \brief Creates a BatchQuery object. + + Scene queries like raycasts, overlap tests and sweeps are batched in this object and are then executed at once. See #PxBatchQuery. + + \deprecated The batched query feature has been deprecated in PhysX version 3.4 + + \param[in] desc The descriptor of scene query. Scene Queries need to register a callback. See #PxBatchQueryDesc. + + @see PxBatchQuery PxBatchQueryDesc + */ + PX_DEPRECATED virtual PxBatchQuery* createBatchQuery(const PxBatchQueryDesc& desc) = 0; + + /** + \brief Sets the rebuild rate of the dynamic tree pruning structures. + + \param[in] dynamicTreeRebuildRateHint Rebuild rate of the dynamic tree pruning structures. + + @see PxSceneDesc.dynamicTreeRebuildRateHint getDynamicTreeRebuildRateHint() forceDynamicTreeRebuild() + */ + virtual void setDynamicTreeRebuildRateHint(PxU32 dynamicTreeRebuildRateHint) = 0; + + /** + \brief Retrieves the rebuild rate of the dynamic tree pruning structures. + + \return The rebuild rate of the dynamic tree pruning structures. + + @see PxSceneDesc.dynamicTreeRebuildRateHint setDynamicTreeRebuildRateHint() forceDynamicTreeRebuild() + */ + virtual PxU32 getDynamicTreeRebuildRateHint() const = 0; + + /** + \brief Forces dynamic trees to be immediately rebuilt. + + \param[in] rebuildStaticStructure True to rebuild the dynamic tree containing static objects + \param[in] rebuildDynamicStructure True to rebuild the dynamic tree containing dynamic objects + + @see PxSceneDesc.dynamicTreeRebuildRateHint setDynamicTreeRebuildRateHint() getDynamicTreeRebuildRateHint() + */ + virtual void forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure) = 0; + + /** + \brief Sets scene query update mode + + \param[in] updateMode Scene query update mode. + + @see PxSceneQueryUpdateMode::Enum + */ + virtual void setSceneQueryUpdateMode(PxSceneQueryUpdateMode::Enum updateMode) = 0; + + /** + \brief Gets scene query update mode + + \return Current scene query update mode. + + @see PxSceneQueryUpdateMode::Enum + */ + virtual PxSceneQueryUpdateMode::Enum getSceneQueryUpdateMode() const = 0; + + /** + \brief Executes scene queries update tasks. + This function will refit dirty shapes within the pruner and will execute a task to build a new AABB tree, which is + build on a different thread. The new AABB tree is built based on the dynamic tree rebuild hint rate. Once + the new tree is ready it will be commited in next fetchQueries call, which must be called after. + + \note If PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED is used, it is required to update the scene queries + using this function. + + \param[in] completionTask if non-NULL, this task will have its refcount incremented in sceneQueryUpdate(), then + decremented when the scene is ready to have fetchQueries called. So the task will not run until the + application also calls removeReference(). + \param[in] controlSimulation if true, the scene controls its PxTaskManager simulation state. Leave + true unless the application is calling the PxTaskManager start/stopSimulation() methods itself. + + @see PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED + */ + virtual void sceneQueriesUpdate(physx::PxBaseTask* completionTask = NULL, bool controlSimulation = true) = 0; + + /** + \brief This checks to see if the scene queries update has completed. + + This does not cause the data available for reading to be updated with the results of the scene queries update, it is simply a status check. + The bool will allow it to either return immediately or block waiting for the condition to be met so that it can return true + + \param[in] block When set to true will block until the condition is met. + \return True if the results are available. + + @see sceneQueriesUpdate() fetchResults() + */ + virtual bool checkQueries(bool block = false) = 0; + + /** + This method must be called after sceneQueriesUpdate. It will wait for the scene queries update to finish. If the user makes an illegal scene queries update call, + the SDK will issue an error message. + + If a new AABB tree build finished, then during fetchQueries the current tree within the pruning structure is swapped with the new tree. + + \param[in] block When set to true will block until the condition is met, which is tree built task must finish running. + */ + + virtual bool fetchQueries(bool block = false) = 0; + + /** + \brief Performs a raycast against objects in the scene, returns results in a PxRaycastBuffer object + or via a custom user callback implementation inheriting from PxRaycastCallback. + + \note Touching hits are not ordered. + \note Shooting a ray from within an object leads to different results depending on the shape type. Please check the details in user guide article SceneQuery. User can ignore such objects by employing one of the provided filter mechanisms. + + \param[in] origin Origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] distance Length of the ray. Has to be in the [0, inf) range. + \param[out] hitCall Raycast hit buffer or callback object used to report raycast hits. + \param[in] hitFlags Specifies which properties per hit should be computed and returned via the hit callback. + \param[in] filterData Filtering data passed to the filer shader. See #PxQueryFilterData #PxBatchQueryPreFilterShader, #PxBatchQueryPostFilterShader + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxQueryFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first. If no hit is found the ray gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + + \return True if any touching or blocking hits were found or any hit was found in case PxQueryFlag::eANY_HIT was specified. + + @see PxRaycastCallback PxRaycastBuffer PxQueryFilterData PxQueryFilterCallback PxQueryCache PxRaycastHit PxQueryFlag PxQueryFlag::eANY_HIT + */ + virtual bool raycast( + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxRaycastCallback& hitCall, PxHitFlags hitFlags = PxHitFlags(PxHitFlag::eDEFAULT), + const PxQueryFilterData& filterData = PxQueryFilterData(), PxQueryFilterCallback* filterCall = NULL, + const PxQueryCache* cache = NULL) const = 0; + + /** + \brief Performs a sweep test against objects in the scene, returns results in a PxSweepBuffer object + or via a custom user callback implementation inheriting from PxSweepCallback. + + \note Touching hits are not ordered. + \note If a shape from the scene is already overlapping with the query shape in its starting position, + the hit is returned unless eASSUME_NO_INITIAL_OVERLAP was specified. + + \param[in] geometry Geometry of object to sweep (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the sweep object. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be in [0, inf) range and >0 if eASSUME_NO_INITIAL_OVERLAP was specified. Will be clamped to PX_MAX_SWEEP_DISTANCE. + \param[out] hitCall Sweep hit buffer or callback object used to report sweep hits. + \param[in] hitFlags Specifies which properties per hit should be computed and returned via the hit callback. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxQueryFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Sweep is performed against cached shape first. If no hit is found the sweep gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + Note: ePRECISE_SWEEP doesn't support inflation. Therefore the sweep will be performed with zero inflation. + + \return True if any touching or blocking hits were found or any hit was found in case PxQueryFlag::eANY_HIT was specified. + + + @see PxSweepCallback PxSweepBuffer PxQueryFilterData PxQueryFilterCallback PxSweepHit PxQueryCache + */ + virtual bool sweep(const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSweepCallback& hitCall, PxHitFlags hitFlags = PxHitFlags(PxHitFlag::eDEFAULT), + const PxQueryFilterData& filterData = PxQueryFilterData(), PxQueryFilterCallback* filterCall = NULL, + const PxQueryCache* cache = NULL, const PxReal inflation = 0.f) const = 0; + + + /** + \brief Performs an overlap test of a given geometry against objects in the scene, returns results in a PxOverlapBuffer object + or via a custom user callback implementation inheriting from PxOverlapCallback. + + \note Filtering: returning eBLOCK from user filter for overlap queries will cause a warning (see #PxQueryHitType). + + \param[in] geometry Geometry of object to check for overlap (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the object. + \param[out] hitCall Overlap hit buffer or callback object used to report overlap hits. + \param[in] filterData Filtering data and simple logic. See #PxQueryFilterData #PxQueryFilterCallback + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxQueryFlag flags are set. If NULL, all hits are assumed to overlap. + + \return True if any touching or blocking hits were found or any hit was found in case PxQueryFlag::eANY_HIT was specified. + + \note eBLOCK should not be returned from user filters for overlap(). Doing so will result in undefined behavior, and a warning will be issued. + \note If the PxQueryFlag::eNO_BLOCK flag is set, the eBLOCK will instead be automatically converted to an eTOUCH and the warning suppressed. + + @see PxOverlapCallback PxOverlapBuffer PxHitFlags PxQueryFilterData PxQueryFilterCallback + */ + virtual bool overlap(const PxGeometry& geometry, const PxTransform& pose, PxOverlapCallback& hitCall, + const PxQueryFilterData& filterData = PxQueryFilterData(), PxQueryFilterCallback* filterCall = NULL + ) const = 0; + + + /** + \brief Retrieves the scene's internal scene query timestamp, increased each time a change to the + static scene query structure is performed. + + \return scene query static timestamp + */ + virtual PxU32 getSceneQueryStaticTimestamp() const = 0; + //@} + + /************************************************************************************************/ + /** @name Broad-phase + */ + //@{ + + /** + \brief Returns broad-phase type. + + \return Broad-phase type + */ + virtual PxBroadPhaseType::Enum getBroadPhaseType() const = 0; + + /** + \brief Gets broad-phase caps. + + \param[out] caps Broad-phase caps + \return True if success + */ + virtual bool getBroadPhaseCaps(PxBroadPhaseCaps& caps) const = 0; + + /** + \brief Returns number of regions currently registered in the broad-phase. + + \return Number of regions + */ + virtual PxU32 getNbBroadPhaseRegions() const = 0; + + /** + \brief Gets broad-phase regions. + + \param[out] userBuffer Returned broad-phase regions + \param[in] bufferSize Size of userBuffer + \param[in] startIndex Index of first desired region, in [0 ; getNbRegions()[ + \return Number of written out regions + */ + virtual PxU32 getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Adds a new broad-phase region. + + Note that by default, objects already existing in the SDK that might touch this region will not be automatically + added to the region. In other words the newly created region will be empty, and will only be populated with new + objects when they are added to the simulation, or with already existing objects when they are updated. + + It is nonetheless possible to override this default behavior and let the SDK populate the new region automatically + with already existing objects overlapping the incoming region. This has a cost though, and it should only be used + when the game can not guarantee that all objects within the new region will be added to the simulation after the + region itself. + + \param[in] region User-provided region data + \param[in] populateRegion Automatically populate new region with already existing objects overlapping it + \return Handle for newly created region, or 0xffffffff in case of failure. + */ + virtual PxU32 addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion=false) = 0; + + /** + \brief Removes a new broad-phase region. + + If the region still contains objects, and if those objects do not overlap any region any more, they are not + automatically removed from the simulation. Instead, the PxBroadPhaseCallback::onObjectOutOfBounds notification + is used for each object. Users are responsible for removing the objects from the simulation if this is the + desired behavior. + + If the handle is invalid, or if a valid handle is removed twice, an error message is sent to the error stream. + + \param[in] handle Region's handle, as returned by PxScene::addBroadPhaseRegion. + \return True if success + */ + virtual bool removeBroadPhaseRegion(PxU32 handle) = 0; + + //@} + + /************************************************************************************************/ + + /** @name Threads and Memory + */ + //@{ + + /** + \brief Get the task manager associated with this scene + + \return the task manager associated with the scene + */ + virtual PxTaskManager* getTaskManager() const = 0; + + + /** + \brief Lock the scene for reading from the calling thread. + + When the PxSceneFlag::eREQUIRE_RW_LOCK flag is enabled lockRead() must be + called before any read calls are made on the scene. + + Multiple threads may read at the same time, no threads may read while a thread is writing. + If a call to lockRead() is made while another thread is holding a write lock + then the calling thread will be blocked until the writing thread calls unlockWrite(). + + \note Lock upgrading is *not* supported, that means it is an error to + call lockRead() followed by lockWrite(). + + \note Recursive locking is supported but each lockRead() call must be paired with an unlockRead(). + + \param file String representing the calling file, for debug purposes + \param line The source file line number, for debug purposes + */ + virtual void lockRead(const char* file=NULL, PxU32 line=0) = 0; + + /** + \brief Unlock the scene from reading. + + \note Each unlockRead() must be paired with a lockRead() from the same thread. + */ + virtual void unlockRead() = 0; + + /** + \brief Lock the scene for writing from this thread. + + When the PxSceneFlag::eREQUIRE_RW_LOCK flag is enabled lockWrite() must be + called before any write calls are made on the scene. + + Only one thread may write at a time and no threads may read while a thread is writing. + If a call to lockWrite() is made and there are other threads reading then the + calling thread will be blocked until the readers complete. + + Writers have priority. If a thread is blocked waiting to write then subsequent calls to + lockRead() from other threads will be blocked until the writer completes. + + \note If multiple threads are waiting to write then the thread that is first + granted access depends on OS scheduling. + + \note Recursive locking is supported but each lockWrite() call must be paired + with an unlockWrite(). + + \note If a thread has already locked the scene for writing then it may call + lockRead(). + + \param file String representing the calling file, for debug purposes + \param line The source file line number, for debug purposes + */ + virtual void lockWrite(const char* file=NULL, PxU32 line=0) = 0; + + /** + \brief Unlock the scene from writing. + + \note Each unlockWrite() must be paired with a lockWrite() from the same thread. + */ + virtual void unlockWrite() = 0; + + + /** + \brief set the cache blocks that can be used during simulate(). + + Each frame the simulation requires memory to store contact, friction, and contact cache data. This memory is used in blocks of 16K. + Each frame the blocks used by the previous frame are freed, and may be retrieved by the application using PxScene::flushSimulation() + + This call will force allocation of cache blocks if the numBlocks parameter is greater than the currently allocated number + of blocks, and less than the max16KContactDataBlocks parameter specified at scene creation time. + + \param[in] numBlocks The number of blocks to allocate. + + @see PxSceneDesc.nbContactDataBlocks PxSceneDesc.maxNbContactDataBlocks flushSimulation() getNbContactDataBlocksUsed getMaxNbContactDataBlocksUsed + */ + virtual void setNbContactDataBlocks(PxU32 numBlocks) = 0; + + + /** + \brief get the number of cache blocks currently used by the scene + + This function may not be called while the scene is simulating + + \return the number of cache blocks currently used by the scene + + @see PxSceneDesc.nbContactDataBlocks PxSceneDesc.maxNbContactDataBlocks flushSimulation() setNbContactDataBlocks() getMaxNbContactDataBlocksUsed() + */ + virtual PxU32 getNbContactDataBlocksUsed() const = 0; + + /** + \brief get the maximum number of cache blocks used by the scene + + This function may not be called while the scene is simulating + + \return the maximum number of cache blocks everused by the scene + + @see PxSceneDesc.nbContactDataBlocks PxSceneDesc.maxNbContactDataBlocks flushSimulation() setNbContactDataBlocks() getNbContactDataBlocksUsed() + */ + virtual PxU32 getMaxNbContactDataBlocksUsed() const = 0; + + + /** + \brief Return the value of PxSceneDesc::contactReportStreamBufferSize that was set when creating the scene with PxPhysics::createScene + + @see PxSceneDesc::contactReportStreamBufferSize, PxPhysics::createScene + */ + virtual PxU32 getContactReportStreamBufferSize() const = 0; + + + /** + \brief Sets the number of actors required to spawn a separate rigid body solver thread. + + \param[in] solverBatchSize Number of actors required to spawn a separate rigid body solver thread. + + @see PxSceneDesc.solverBatchSize getSolverBatchSize() + */ + virtual void setSolverBatchSize(PxU32 solverBatchSize) = 0; + + /** + \brief Retrieves the number of actors required to spawn a separate rigid body solver thread. + + \return Current number of actors required to spawn a separate rigid body solver thread. + + @see PxSceneDesc.solverBatchSize setSolverBatchSize() + */ + virtual PxU32 getSolverBatchSize() const = 0; + + + //@} + + /** + \brief Returns the wake counter reset value. + + \return Wake counter reset value + + @see PxSceneDesc.wakeCounterResetValue + */ + virtual PxReal getWakeCounterResetValue() const = 0; + + /** + \brief Shift the scene origin by the specified vector. + + The poses of all objects in the scene and the corresponding data structures will get adjusted to reflect the new origin location + (the shift vector will get subtracted from all object positions). + + \note It is the user's responsibility to keep track of the summed total origin shift and adjust all input/output to/from PhysX accordingly. + + \note Do not use this method while the simulation is running. Calls to this method while the simulation is running will be ignored. + + \note Make sure to propagate the origin shift to other dependent modules (for example, the character controller module etc.). + + \note This is an expensive operation and we recommend to use it only in the case where distance related precision issues may arise in areas far from the origin. + + \param[in] shift Translation vector to shift the origin by. + */ + virtual void shiftOrigin(const PxVec3& shift) = 0; + + /** + \brief Returns the Pvd client associated with the scene. + \return the client, NULL if no PVD supported. + */ + virtual PxPvdSceneClient* getScenePvdClient() = 0; + + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxSceneDesc.h b/src/PhysX/physx/include/PxSceneDesc.h new file mode 100644 index 000000000..67c107690 --- /dev/null +++ b/src/PhysX/physx/include/PxSceneDesc.h @@ -0,0 +1,1059 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENEDESC +#define PX_PHYSICS_NX_SCENEDESC +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxFlags.h" +#include "foundation/PxBounds3.h" +#include "PxFiltering.h" +#include "PxBroadPhase.h" +#include "common/PxTolerancesScale.h" +#include "task/PxTask.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Pruning structure used to accelerate scene queries. + +eNONE uses a simple data structure that consumes less memory than the alternatives, +but generally has slower query performance. + +eDYNAMIC_AABB_TREE usually provides the fastest queries. However there is a +constant per-frame management cost associated with this structure. How much work should +be done per frame can be tuned via the #PxSceneDesc::dynamicTreeRebuildRateHint +parameter. + +eSTATIC_AABB_TREE is typically used for static objects. It is the same as the +dynamic AABB tree, without the per-frame overhead. This can be a good choice for static +objects, if no static objects are added, moved or removed after the scene has been +created. If there is no such guarantee (e.g. when streaming parts of the world in and out), +then the dynamic version is a better choice even for static objects. + +*/ +struct PxPruningStructureType +{ + enum Enum + { + eNONE, //!< Using a simple data structure + eDYNAMIC_AABB_TREE, //!< Using a dynamic AABB tree + eSTATIC_AABB_TREE, //!< Using a static AABB tree + + eLAST + }; +}; + +/** +\brief Scene query update mode + +When PxScene::fetchResults is called it does scene query related work, with this enum it is possible to +set what work is done during the fetchResults. + +FetchResults will sync changed bounds during simulation and update the scene query bounds in pruners, this work is mandatory. + +eCOMMIT_ENABLED_BUILD_ENABLED does allow to execute the new AABB tree build step during fetchResults, additionally the pruner commit is +called where any changes are applied. During commit PhysX refits the dynamic scene query tree and if a new tree was built and +the build finished the tree is swapped with current AABB tree. + +eCOMMIT_DISABLED_BUILD_ENABLED does allow to execute the new AABB tree build step during fetchResults. Pruner commit is not called, +this means that refit will then occur during the first scene query following fetchResults, or may be forced by the method PxScene::flushSceneQueryUpdates(). + +eCOMMIT_DISABLED_BUILD_DISABLED no further scene query work is executed. The scene queries update needs to be called manually, see PxScene::sceneQueriesUpdate. +It is recommended to call PxScene::sceneQueriesUpdate right after fetchResults as the pruning structures are not updated. + +*/ +struct PxSceneQueryUpdateMode +{ + enum Enum + { + eBUILD_ENABLED_COMMIT_ENABLED, //!< Both scene query build and commit are executed. + eBUILD_ENABLED_COMMIT_DISABLED, //!< Scene query build only is executed. + eBUILD_DISABLED_COMMIT_DISABLED //!< No work is done, no update of scene queries + }; +}; + + +/** +\brief Enum for selecting the friction algorithm used for simulation. + +#PxFrictionType::ePATCH selects the patch friction model which typically leads to the most stable results at low solver iteration counts and is also quite inexpensive, as it uses only +up to four scalar solver constraints per pair of touching objects. The patch friction model is the same basic strong friction algorithm as PhysX 3.2 and before. + +#PxFrictionType::eONE_DIRECTIONAL is a simplification of the Coulomb friction model, in which the friction for a given point of contact is applied in the alternating tangent directions of +the contact's normal. This simplification allows us to reduce the number of iterations required for convergence but is not as accurate as the two directional model. + +#PxFrictionType::eTWO_DIRECTIONAL is identical to the one directional model, but it applies friction in both tangent directions simultaneously. This hurts convergence a bit so it +requires more solver iterations, but is more accurate. Like the one directional model, it is applied at every contact point, which makes it potentially more expensive +than patch friction for scenarios with many contact points. + +#PxFrictionType::eFRICTION_COUNT is the total numer of friction models supported by the SDK. +*/ +struct PxFrictionType +{ + enum Enum + { + ePATCH, //!< Select default patch-friction model. + eONE_DIRECTIONAL, //!< Select one directional per-contact friction model. + eTWO_DIRECTIONAL, //!< Select two directional per-contact friction model. + eFRICTION_COUNT //!< The total number of friction models supported by the SDK. + }; +}; + + +/** +\brief Enum for selecting the type of solver used for the simulation. + +#PxSolverType::ePGS selects the default iterative sequential impulse solver. This is the same kind of solver used in PhysX 3.4 and earlier releases. + +#PxSolverType::eTGS selects a non linear iterative solver. This kind of solver can lead to improved convergence and handle large mass ratios, long chains and jointed systems better. It is slightly more expensive than the default solver and can introduce more energy to correct joint and contact errors. +*/ +struct PxSolverType +{ + enum Enum + { + ePGS, //!< Default Projected Gauss-Seidel iterative solver + eTGS //!< Temporal Gauss-Seidel solver + }; +}; + + +/** +\brief flags for configuring properties of the scene + +@see PxScene +*/ + +struct PxSceneFlag +{ + enum Enum + { + /** + \brief Enable Active Actors Notification. + + This flag enables the Active Actor Notification feature for a scene. This + feature defaults to disabled. When disabled, the function + PxScene::getActiveActors() will always return a NULL list. + + \note There may be a performance penalty for enabling the Active Actor Notification, hence this flag should + only be enabled if the application intends to use the feature. + + Default: False + */ + eENABLE_ACTIVE_ACTORS = (1<<0), + + /** + \brief Enables a second broad phase check after integration that makes it possible to prevent objects from tunneling through eachother. + + PxPairFlag::eDETECT_CCD_CONTACT requires this flag to be specified. + + \note For this feature to be effective for bodies that can move at a significant velocity, the user should raise the flag PxRigidBodyFlag::eENABLE_CCD for them. + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: False + + @see PxRigidBodyFlag::eENABLE_CCD, PxPairFlag::eDETECT_CCD_CONTACT, eDISABLE_CCD_RESWEEP + */ + eENABLE_CCD = (1<<1), + + /** + \brief Enables a simplified swept integration strategy, which sacrifices some accuracy for improved performance. + + This simplified swept integration approach makes certain assumptions about the motion of objects that are not made when using a full swept integration. + These assumptions usually hold but there are cases where they could result in incorrect behavior between a set of fast-moving rigid bodies. A key issue is that + fast-moving dynamic objects may tunnel through each-other after a rebound. This will not happen if this mode is disabled. However, this approach will be potentially + faster than a full swept integration because it will perform significantly fewer sweeps in non-trivial scenes involving many fast-moving objects. This approach + should successfully resist objects passing through the static environment. + + PxPairFlag::eDETECT_CCD_CONTACT requires this flag to be specified. + + \note This scene flag requires eENABLE_CCD to be enabled as well. If it is not, this scene flag will do nothing. + \note For this feature to be effective for bodies that can move at a significant velocity, the user should raise the flag PxRigidBodyFlag::eENABLE_CCD for them. + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: False + + @see PxRigidBodyFlag::eENABLE_CCD, PxPairFlag::eDETECT_CCD_CONTACT, eENABLE_CCD + */ + eDISABLE_CCD_RESWEEP = (1<<2), + + /** + \brief Enable adaptive forces to accelerate convergence of the solver. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: false + */ + eADAPTIVE_FORCE = (1<<3), + + /** + \brief Enable contact pair filtering between kinematic and static rigid bodies. + + By default contacts between kinematic and static rigid bodies are suppressed (see #PxFilterFlag::eSUPPRESS) and don't get reported to the filter mechanism. + Raise this flag if these pairs should go through the filtering pipeline nonetheless. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: false + */ + eENABLE_KINEMATIC_STATIC_PAIRS PX_DEPRECATED = (1<<4), + + /** + \brief Enable contact pair filtering between kinematic rigid bodies. + + By default contacts between kinematic bodies are suppressed (see #PxFilterFlag::eSUPPRESS) and don't get reported to the filter mechanism. + Raise this flag if these pairs should go through the filtering pipeline nonetheless. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: false + */ + eENABLE_KINEMATIC_PAIRS PX_DEPRECATED = (1<<5), + + /** + \brief Enable GJK-based distance collision detection system. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: true + */ + eENABLE_PCM = (1 << 6), + + /** + \brief Disable contact report buffer resize. Once the contact buffer is full, the rest of the contact reports will + not be buffered and sent. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: false + */ + eDISABLE_CONTACT_REPORT_BUFFER_RESIZE = (1 << 7), + + /** + \brief Disable contact cache. + + Contact caches are used internally to provide faster contact generation. You can disable all contact caches + if memory usage for this feature becomes too high. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + Default: false + */ + eDISABLE_CONTACT_CACHE = (1 << 8), + + /** + \brief Require scene-level locking + + When set to true this requires that threads accessing the PxScene use the + multi-threaded lock methods. + + \note This flag is not mutable, and must be set in PxSceneDesc at scene creation. + + @see PxScene::lockRead + @see PxScene::unlockRead + @see PxScene::lockWrite + @see PxScene::unlockWrite + + Default: false + */ + eREQUIRE_RW_LOCK = (1 << 9), + + /** + \brief Enables additional stabilization pass in solver + + When set to true, this enables additional stabilization processing to improve that stability of complex interactions between large numbers of bodies. + + Note that this flag is not mutable and must be set in PxSceneDesc at scene creation. Also, this is an experimental feature which does result in some loss of momentum. + */ + eENABLE_STABILIZATION = (1 << 10), + + /** + \brief Enables average points in contact manifolds + + When set to true, this enables additional contacts to be generated per manifold to represent the average point in a manifold. This can stabilize stacking when only a small + number of solver iterations is used. + + Note that this flag is not mutable and must be set in PxSceneDesc at scene creation. + */ + eENABLE_AVERAGE_POINT = (1 << 11), + + /** + \brief Do not report kinematics in list of active actors. + + Since the target pose for kinematics is set by the user, an application can track the activity state directly and use + this flag to avoid that kinematics get added to the list of active actors. + + \note This flag has only an effect in combination with eENABLE_ACTIVE_ACTORS. + + @see eENABLE_ACTIVE_ACTORS + + Default: false + */ + eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS = (1 << 12), + + /*\brief Enables the GPU dynamics pipeline + + When set to true, a CUDA ARCH 3.0 or above-enabled NVIDIA GPU is present and the GPU dispatcher has been configured, this will run the GPU dynamics pipelin instead of the CPU dynamics pipeline. + + Note that this flag is not mutable and must be set in PxSceneDesc at scene creation. + */ + eENABLE_GPU_DYNAMICS = (1 << 13), + + /** + \brief Provides improved determinism at the expense of performance. + + By default, PhysX provides limited determinism guarantees. Specifically, PhysX guarantees that the exact scene (same actors created in the same order) and simulated using the same + time-stepping scheme should provide the exact same behaviour. + + However, if additional actors are added to the simulation, this can affect the behaviour of the existing actors in the simulation, even if the set of new actors do not interact with + the existing actors. + + This flag provides an additional level of determinism that guarantees that the simulation will not change if additional actors are added to the simulation, provided those actors do not interfere + with the existing actors in the scene. Determinism is only guaranteed if the actors are inserted in a consistent order each run in a newly-created scene and simulated using a consistent time-stepping + scheme. + + Note that this flag is not mutable and must be set at scene creation. + + Note that enabling this flag can have a negative impact on performance. + + Note that this feature is not currently supported on GPU. + + Default false + */ + eENABLE_ENHANCED_DETERMINISM = (1<<14), + + /** + \brief Controls processing friction in all solver iterations + + By default, PhysX processes friction only in the final 3 position iterations, and all velocity + iterations. This flag enables friction processing in all position and velocity iterations. + + The default behaviour provides a good trade-off between performance and stability and is aimed + primarily at game development. + + When simulating more complex frictional behaviour, such as grasping of complex geometries with + a robotic manipulator, better results can be achieved by enabling friction in all solver iterations. + + \note This flag only has effect with the default solver. The TGS solver always performs friction per-iteration. + */ + eENABLE_FRICTION_EVERY_ITERATION = (1 << 15), + + eMUTABLE_FLAGS = eENABLE_ACTIVE_ACTORS|eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS + }; +}; + + +/** +\brief collection of set bits defined in PxSceneFlag. + +@see PxSceneFlag +*/ +typedef PxFlags PxSceneFlags; +PX_FLAGS_OPERATORS(PxSceneFlag::Enum,PxU32) + + +class PxSimulationEventCallback; +class PxContactModifyCallback; +class PxCCDContactModifyCallback; +class PxSimulationFilterCallback; + +/** +\brief Class used to retrieve limits(e.g. maximum number of bodies) for a scene. The limits +are used as a hint to the size of the scene, not as a hard limit (i.e. it will be possible +to create more objects than specified in the scene limits). + +0 indicates no limit. Using limits allows the SDK to preallocate various arrays, leading to +less re-allocations and faster code at runtime. +*/ +class PxSceneLimits +{ +public: + PxU32 maxNbActors; //!< Expected maximum number of actors + PxU32 maxNbBodies; //!< Expected maximum number of dynamic rigid bodies + PxU32 maxNbStaticShapes; //!< Expected maximum number of static shapes + PxU32 maxNbDynamicShapes; //!< Expected maximum number of dynamic shapes + PxU32 maxNbAggregates; //!< Expected maximum number of aggregates + PxU32 maxNbConstraints; //!< Expected maximum number of constraint shaders + PxU32 maxNbRegions; //!< Expected maximum number of broad-phase regions + PxU32 maxNbBroadPhaseOverlaps; //!< Expected maximum number of broad-phase overlaps + + /** + \brief constructor sets to default + */ + PX_INLINE PxSceneLimits(); + + /** + \brief (re)sets the structure to the default + */ + PX_INLINE void setToDefault(); + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + PX_INLINE bool isValid() const; +}; + +PX_INLINE PxSceneLimits::PxSceneLimits() : //constructor sets to default + maxNbActors (0), + maxNbBodies (0), + maxNbStaticShapes (0), + maxNbDynamicShapes (0), + maxNbAggregates (0), + maxNbConstraints (0), + maxNbRegions (0), + maxNbBroadPhaseOverlaps (0) +{ +} + +PX_INLINE void PxSceneLimits::setToDefault() +{ + *this = PxSceneLimits(); +} + +PX_INLINE bool PxSceneLimits::isValid() const +{ + if(maxNbRegions>256) // max number of regions is currently limited + return false; + + return true; +} + +//#if PX_SUPPORT_GPU_PHYSX +/** +\brief Sizes of pre-allocated buffers use for GPU dynamics +*/ + +struct PxgDynamicsMemoryConfig +{ + PxU32 constraintBufferCapacity; //!< Capacity of constraint buffer allocated in GPU global memory + PxU32 contactBufferCapacity; //!< Capacity of contact buffer allocated in GPU global memory + PxU32 tempBufferCapacity; //!< Capacity of temp buffer allocated in pinned host memory. + PxU32 contactStreamSize; //!< Size of contact stream buffer allocated in pinned host memory. This is double-buffered so total allocation size = 2* contactStreamCapacity * sizeof(PxContact). + PxU32 patchStreamSize; //!< Size of the contact patch stream buffer allocated in pinned host memory. This is double-buffered so total allocation size = 2 * patchStreamCapacity * sizeof(PxContactPatch). + PxU32 forceStreamCapacity; //!< Capacity of force buffer allocated in pinned host memory. + PxU32 heapCapacity; //!< Initial capacity of the GPU and pinned host memory heaps. Additional memory will be allocated if more memory is required. + PxU32 foundLostPairsCapacity; //!< Capacity of found and lost buffers allocated in GPU global memory. This is used for the found/lost pair reports in the BP. + + PxgDynamicsMemoryConfig() : + constraintBufferCapacity(32 * 1024 * 1024), + contactBufferCapacity(24 * 1024 * 1024), + tempBufferCapacity(16 * 1024 * 1024), + contactStreamSize(1024 * 512), + patchStreamSize(1024 * 80), + forceStreamCapacity(1 * 1024 * 1024), + heapCapacity(64 * 1024 * 1024), + foundLostPairsCapacity(256 * 1024) + { + } +}; + +//#endif + +/** +\brief Descriptor class for scenes. See #PxScene. + +This struct must be initialized with the same PxTolerancesScale values used to initialize PxPhysics. + +@see PxScene PxPhysics.createScene PxTolerancesScale +*/ +class PxSceneDesc +{ +public: + + /** + \brief Gravity vector. + + Range: force vector
            + Default: Zero + + @see PxScene.setGravity() + + When setting gravity, you should probably also set bounce threshold. + */ + PxVec3 gravity; + + /** + \brief Possible notification callback. + + Default: NULL + + @see PxSimulationEventCallback PxScene.setSimulationEventCallback() PxScene.getSimulationEventCallback() + */ + PxSimulationEventCallback* simulationEventCallback; + + /** + \brief Possible asynchronous callback for contact modification. + + Default: NULL + + @see PxContactModifyCallback PxScene.setContactModifyCallback() PxScene.getContactModifyCallback() + */ + PxContactModifyCallback* contactModifyCallback; + + /** + \brief Possible asynchronous callback for contact modification. + + Default: NULL + + @see PxContactModifyCallback PxScene.setContactModifyCallback() PxScene.getContactModifyCallback() + */ + PxCCDContactModifyCallback* ccdContactModifyCallback; + + /** + \brief Shared global filter data which will get passed into the filter shader. + + \note The provided data will get copied to internal buffers and this copy will be used for filtering calls. + + Default: NULL + + @see PxSimulationFilterShader PxScene::setFilterShaderData() + */ + const void* filterShaderData; + + /** + \brief Size (in bytes) of the shared global filter data #filterShaderData. + + Default: 0 + + @see PxSimulationFilterShader filterShaderData + */ + PxU32 filterShaderDataSize; + + /** + \brief The custom filter shader to use for collision filtering. + + \note This parameter is compulsory. If you don't want to define your own filter shader you can + use the default shader #PxDefaultSimulationFilterShader which can be found in the PhysX extensions + library. + + @see PxSimulationFilterShader + */ + PxSimulationFilterShader filterShader; + + /** + \brief A custom collision filter callback which can be used to implement more complex filtering operations which need + access to the simulation state, for example. + + Default: NULL + + @see PxSimulationFilterCallback + */ + PxSimulationFilterCallback* filterCallback; + + /** + \brief Filtering mode for kinematic-kinematic pairs in the broadphase. + + Default: PxPairFilteringMode::eDEFAULT + + @see PxPairFilteringMode + */ + PxPairFilteringMode::Enum kineKineFilteringMode; + + /** + \brief Filtering mode for static-kinematic pairs in the broadphase. + + Default: PxPairFilteringMode::eDEFAULT + + @see PxPairFilteringMode + */ + PxPairFilteringMode::Enum staticKineFilteringMode; + + /** + \brief Selects the broad-phase algorithm to use. + + Default: PxBroadPhaseType::eABP + + @see PxBroadPhaseType + */ + PxBroadPhaseType::Enum broadPhaseType; + + /** + \brief Broad-phase callback + + Default: NULL + + @see PxBroadPhaseCallback + */ + PxBroadPhaseCallback* broadPhaseCallback; + + /** + \brief Expected scene limits. + + @see PxSceneLimits + */ + PxSceneLimits limits; + + /** + \brief Selects the friction algorithm to use for simulation. + + \note frictionType cannot be modified after the first call to any of PxScene::simulate, PxScene::solve and PxScene::collide + + @see PxFrictionType + Default: PxFrictionType::ePATCH + + @see PxScene::setFrictionType, PxScene::getFrictionType + */ + PxFrictionType::Enum frictionType; + + /** + \brief Selects the solver algorithm to use. + + Default: PxSolverType::eDEFAULT + + @see PxSolverType + */ + PxSolverType::Enum solverType; + + /** + \brief A contact with a relative velocity below this will not bounce. A typical value for simulation. + stability is about 0.2 * gravity. + + Range: [0, PX_MAX_F32)
            + Default: 0.2 * PxTolerancesScale::speed + + @see PxMaterial + */ + PxReal bounceThresholdVelocity; + + /** + \brief A threshold of contact separation distance used to decide if a contact point will experience friction forces. + + \note If the separation distance of a contact point is greater than the threshold then the contact point will not experience friction forces. + + \note If the aggregated contact offset of a pair of shapes is large it might be desirable to neglect friction + for contact points whose separation distance is sufficiently large that the shape surfaces are clearly separated. + + \note This parameter can be used to tune the separation distance of contact points at which friction starts to have an effect. + + Range: [0, PX_MAX_F32)
            + Default: 0.04 * PxTolerancesScale::length + */ + PxReal frictionOffsetThreshold; + + /** + \brief A threshold for speculative CCD. Used to control whether bias, restitution or a combination of the two are used to resolve the contacts. + + \note This only has any effect on contacting pairs where one of the bodies has PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD raised. + + Range: [0, PX_MAX_F32)
            + Default: 0.04 * PxTolerancesScale::length + */ + + PxReal ccdMaxSeparation; + + /** + \brief A slop value used to zero contact offsets from the body's COM on an axis if the offset along that axis is smaller than this threshold. Can be used to compensate + for small numerical errors in contact generation. + + Range: [0, PX_MAX_F32)
            + Default: 0.0 + */ + + PxReal solverOffsetSlop; + + /** + \brief Flags used to select scene options. + + @see PxSceneFlag PxSceneFlags + */ + PxSceneFlags flags; + + /** + \brief The CPU task dispatcher for the scene. + + See PxCpuDispatcher, PxScene::getCpuDispatcher + */ + PxCpuDispatcher* cpuDispatcher; + + /** + \brief The GPU task dispatcher for the scene. + + Platform specific: Applies to PC GPU only. + + See PxGpuDispatcher, PxScene::getGpuDispatcher + */ + PxGpuDispatcher* gpuDispatcher; + + /** + \brief Defines the structure used to store static objects. + + \note Only PxPruningStructureType::eSTATIC_AABB_TREE and PxPruningStructureType::eDYNAMIC_AABB_TREE are allowed here. + */ + PxPruningStructureType::Enum staticStructure; + + /** + \brief Defines the structure used to store dynamic objects. + */ + PxPruningStructureType::Enum dynamicStructure; + + /** + \brief Hint for how much work should be done per simulation frame to rebuild the pruning structure. + + This parameter gives a hint on the distribution of the workload for rebuilding the dynamic AABB tree + pruning structure #PxPruningStructureType::eDYNAMIC_AABB_TREE. It specifies the desired number of simulation frames + the rebuild process should take. Higher values will decrease the workload per frame but the pruning + structure will get more and more outdated the longer the rebuild takes (which can make + scene queries less efficient). + + \note Only used for #PxPruningStructureType::eDYNAMIC_AABB_TREE pruning structure. + + \note This parameter gives only a hint. The rebuild process might still take more or less time depending on the + number of objects involved. + + Range: [4, PX_MAX_U32)
            + Default: 100 + */ + PxU32 dynamicTreeRebuildRateHint; + + /** + \brief Defines the scene query update mode. + Default: PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED + */ + PxSceneQueryUpdateMode::Enum sceneQueryUpdateMode; + + /** + \brief Will be copied to PxScene::userData. + + Default: NULL + */ + void* userData; + + /** + \brief Defines the number of actors required to spawn a separate rigid body solver island task chain. + + This parameter defines the minimum number of actors required to spawn a separate rigid body solver task chain. Setting a low value + will potentially cause more task chains to be generated. This may result in the overhead of spawning tasks can become a limiting performance factor. + Setting a high value will potentially cause fewer islands to be generated. This may reduce thread scaling (fewer task chains spawned) and may + detrimentally affect performance if some bodies in the scene have large solver iteration counts because all constraints in a given island are solved by the + maximum number of solver iterations requested by any body in the island. + + Default: 128 + + @see PxScene.setSolverBatchSize() PxScene.getSolverBatchSize() + */ + PxU32 solverBatchSize; + + /** + \brief Setting to define the number of 16K blocks that will be initially reserved to store contact, friction, and contact cache data. + This is the number of 16K memory blocks that will be automatically allocated from the user allocator when the scene is instantiated. Further 16k + memory blocks may be allocated during the simulation up to maxNbContactDataBlocks. + + \note This value cannot be larger than maxNbContactDataBlocks because that defines the maximum number of 16k blocks that can be allocated by the SDK. + + Default: 0 + + Range: [0, PX_MAX_U32]
            + + @see PxPhysics::createScene PxScene::setNbContactDataBlocks + */ + PxU32 nbContactDataBlocks; + + /** + \brief Setting to define the maximum number of 16K blocks that can be allocated to store contact, friction, and contact cache data. + As the complexity of a scene increases, the SDK may require to allocate new 16k blocks in addition to the blocks it has already + allocated. This variable controls the maximum number of blocks that the SDK can allocate. + + In the case that the scene is sufficiently complex that all the permitted 16K blocks are used, contacts will be dropped and + a warning passed to the error stream. + + If a warning is reported to the error stream to indicate the number of 16K blocks is insufficient for the scene complexity + then the choices are either (i) re-tune the number of 16K data blocks until a number is found that is sufficient for the scene complexity, + (ii) to simplify the scene or (iii) to opt to not increase the memory requirements of physx and accept some dropped contacts. + + Default: 65536 + + Range: [0, PX_MAX_U32]
            + + @see nbContactDataBlocks PxScene::setNbContactDataBlocks + */ + PxU32 maxNbContactDataBlocks; + + /** + \brief The maximum bias coefficient used in the constraint solver + + When geometric errors are found in the constraint solver, either as a result of shapes penetrating + or joints becoming separated or violating limits, a bias is introduced in the solver position iterations + to correct these errors. This bias is proportional to 1/dt, meaning that the bias becomes increasingly + strong as the time-step passed to PxScene::simulate(...) becomes smaller. This coefficient allows the + application to restrict how large the bias coefficient is, to reduce how violent error corrections are. + This can improve simulation quality in cases where either variable time-steps or extremely small time-steps + are used. + + Default: PX_MAX_F32 + + Range [0, PX_MAX_F32]
            + + */ + PxReal maxBiasCoefficient; + + /** + \brief Size of the contact report stream (in bytes). + + The contact report stream buffer is used during the simulation to store all the contact reports. + If the size is not sufficient, the buffer will grow by a factor of two. + It is possible to disable the buffer growth by setting the flag PxSceneFlag::eDISABLE_CONTACT_REPORT_BUFFER_RESIZE. + In that case the buffer will not grow but contact reports not stored in the buffer will not get sent in the contact report callbacks. + + Default: 8192 + + Range: (0, PX_MAX_U32]
            + + */ + PxU32 contactReportStreamBufferSize; + + /** + \brief Maximum number of CCD passes + + The CCD performs multiple passes, where each pass every object advances to its time of first impact. This value defines how many passes the CCD system should perform. + + \note The CCD system is a multi-pass best-effort conservative advancement approach. After the defined number of passes has been completed, any remaining time is dropped. + \note This defines the maximum number of passes the CCD can perform. It may perform fewer if additional passes are not necessary. + + Default: 1 + Range: [1, PX_MAX_U32]
            + */ + PxU32 ccdMaxPasses; + + /** + \brief The wake counter reset value + + Calling wakeUp() on objects which support sleeping will set their wake counter value to the specified reset value. + + Range: (0, PX_MAX_F32)
            + Default: 0.4 (which corresponds to 20 frames for a time step of 0.02) + + @see PxRigidDynamic::wakeUp() PxArticulationBase::wakeUp() + */ + PxReal wakeCounterResetValue; + + /** + \brief The bounds used to sanity check user-set positions of actors and articulation links + + These bounds are used to check the position values of rigid actors inserted into the scene, and positions set for rigid actors + already within the scene. + + Range: any valid PxBounds3
            + Default: (-PX_MAX_BOUNDS_EXTENTS, PX_MAX_BOUNDS_EXTENTS) on each axis + */ + PxBounds3 sanityBounds; + + /** + \brief The pre-allocations performed in the GPU dynamics pipeline. + */ + PxgDynamicsMemoryConfig gpuDynamicsConfig; + + /** + \brief Limitation for the partitions in the GPU dynamics pipeline. + This variable must be power of 2. + A value greater than 32 is currently not supported. + Range: (1, 32)
            + */ + PxU32 gpuMaxNumPartitions; + + /** + \brief Defines which compute version the GPU dynamics should target. DO NOT MODIFY + */ + PxU32 gpuComputeVersion; + +private: + /** + \cond + */ + // For internal use only + PxTolerancesScale tolerancesScale; + /** + \endcond + */ + + +public: + /** + \brief constructor sets to default. + + \param[in] scale scale values for the tolerances in the scene, these must be the same values passed into + PxCreatePhysics(). The affected tolerances are bounceThresholdVelocity and frictionOffsetThreshold. + + @see PxCreatePhysics() PxTolerancesScale bounceThresholdVelocity frictionOffsetThreshold + */ + PX_INLINE PxSceneDesc(const PxTolerancesScale& scale); + + /** + \brief (re)sets the structure to the default. + + \param[in] scale scale values for the tolerances in the scene, these must be the same values passed into + PxCreatePhysics(). The affected tolerances are bounceThresholdVelocity and frictionOffsetThreshold. + + @see PxCreatePhysics() PxTolerancesScale bounceThresholdVelocity frictionOffsetThreshold + */ + PX_INLINE void setToDefault(const PxTolerancesScale& scale); + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + PX_INLINE bool isValid() const; + + /** + \cond + */ + // For internal use only + PX_INLINE const PxTolerancesScale& getTolerancesScale() const { return tolerancesScale; } + /** + \endcond + */ +}; + +PX_INLINE PxSceneDesc::PxSceneDesc(const PxTolerancesScale& scale): + gravity (PxVec3(0.0f)), + simulationEventCallback (NULL), + contactModifyCallback (NULL), + ccdContactModifyCallback (NULL), + + filterShaderData (NULL), + filterShaderDataSize (0), + filterShader (NULL), + filterCallback (NULL), + + kineKineFilteringMode (PxPairFilteringMode::eDEFAULT), + staticKineFilteringMode (PxPairFilteringMode::eDEFAULT), + + broadPhaseType (PxBroadPhaseType::eABP), + broadPhaseCallback (NULL), + + frictionType (PxFrictionType::ePATCH), + solverType (PxSolverType::ePGS), + bounceThresholdVelocity (0.2f * scale.speed), + frictionOffsetThreshold (0.04f * scale.length), + ccdMaxSeparation (0.04f * scale.length), + solverOffsetSlop (0.0f), + + flags (PxSceneFlag::eENABLE_PCM), + + cpuDispatcher (NULL), + gpuDispatcher (NULL), + + staticStructure (PxPruningStructureType::eDYNAMIC_AABB_TREE), + dynamicStructure (PxPruningStructureType::eDYNAMIC_AABB_TREE), + dynamicTreeRebuildRateHint (100), + sceneQueryUpdateMode (PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED), + + userData (NULL), + + solverBatchSize (128), + + nbContactDataBlocks (0), + maxNbContactDataBlocks (1<<16), + maxBiasCoefficient (PX_MAX_F32), + contactReportStreamBufferSize (8192), + ccdMaxPasses (1), + wakeCounterResetValue (20.0f*0.02f), + sanityBounds (PxBounds3(PxVec3(-PX_MAX_BOUNDS_EXTENTS), PxVec3(PX_MAX_BOUNDS_EXTENTS))), + gpuMaxNumPartitions (8), + gpuComputeVersion (0), + tolerancesScale (scale) +{ +} + +PX_INLINE void PxSceneDesc::setToDefault(const PxTolerancesScale& scale) +{ + *this = PxSceneDesc(scale); +} + +PX_INLINE bool PxSceneDesc::isValid() const +{ + if(!filterShader) + return false; + + if( ((filterShaderDataSize == 0) && (filterShaderData != NULL)) || + ((filterShaderDataSize > 0) && (filterShaderData == NULL)) ) + return false; + + if(!limits.isValid()) + return false; + + if(staticStructure!=PxPruningStructureType::eSTATIC_AABB_TREE && staticStructure!=PxPruningStructureType::eDYNAMIC_AABB_TREE) + return false; + + if(dynamicTreeRebuildRateHint < 4) + return false; + + if(bounceThresholdVelocity < 0.0f) + return false; + if(frictionOffsetThreshold < 0.0f) + return false; + if(ccdMaxSeparation < 0.0f) + return false; + if (solverOffsetSlop < 0.f) + return false; + + if(!cpuDispatcher) + return false; + + if(!contactReportStreamBufferSize) + return false; + + if(maxNbContactDataBlocks < nbContactDataBlocks) + return false; + + if(wakeCounterResetValue <= 0.0f) + return false; + + //Adaptive force and stabilization are incompatible. You can only have one or the other + if((flags & (PxSceneFlag::eADAPTIVE_FORCE | PxSceneFlag::eENABLE_STABILIZATION)) == (PxSceneFlag::eADAPTIVE_FORCE | PxSceneFlag::eENABLE_STABILIZATION)) + return false; + + if(!sanityBounds.isValid()) + return false; + +#if PX_SUPPORT_GPU_PHYSX + //gpuMaxNumPartitions must be power of 2 + if((gpuMaxNumPartitions&(gpuMaxNumPartitions - 1)) != 0) + return false; + if (gpuMaxNumPartitions > 32) + return false; +#endif + + return true; +} + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxSceneLock.h b/src/PhysX/physx/include/PxSceneLock.h new file mode 100644 index 000000000..97ffad0c8 --- /dev/null +++ b/src/PhysX/physx/include/PxSceneLock.h @@ -0,0 +1,129 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SCENELOCK +#define PX_PHYSICS_NX_SCENELOCK +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "PxScene.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief RAII wrapper for the PxScene read lock. + +Use this class as follows to lock the scene for reading by the current thread +for the duration of the enclosing scope: + + PxSceneReadLock lock(sceneRef); + +\see PxScene::lockRead(), PxScene::unlockRead(), PxSceneFlag::eREQUIRE_RW_LOCK +*/ +class PxSceneReadLock +{ + PxSceneReadLock(const PxSceneReadLock&); + PxSceneReadLock& operator=(const PxSceneReadLock&); + +public: + + /** + \brief Constructor + \param scene The scene to lock for reading + \param file Optional string for debugging purposes + \param line Optional line number for debugging purposes + */ + PxSceneReadLock(PxScene& scene, const char* file=NULL, PxU32 line=0) + : mScene(scene) + { + mScene.lockRead(file, line); + } + + ~PxSceneReadLock() + { + mScene.unlockRead(); + } + +private: + + PxScene& mScene; +}; + +/** +\brief RAII wrapper for the PxScene write lock. + +Use this class as follows to lock the scene for writing by the current thread +for the duration of the enclosing scope: + + PxSceneWriteLock lock(sceneRef); + +\see PxScene::lockWrite(), PxScene::unlockWrite(), PxSceneFlag::eREQUIRE_RW_LOCK +*/ +class PxSceneWriteLock +{ + PxSceneWriteLock(const PxSceneWriteLock&); + PxSceneWriteLock& operator=(const PxSceneWriteLock&); + +public: + + /** + \brief Constructor + \param scene The scene to lock for writing + \param file Optional string for debugging purposes + \param line Optional line number for debugging purposes + */ + PxSceneWriteLock(PxScene& scene, const char* file=NULL, PxU32 line=0) + : mScene(scene) + { + mScene.lockWrite(file, line); + } + + ~PxSceneWriteLock() + { + mScene.unlockWrite(); + } + +private: + + PxScene& mScene; +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxShape.h b/src/PhysX/physx/include/PxShape.h new file mode 100644 index 000000000..392006428 --- /dev/null +++ b/src/PhysX/physx/include/PxShape.h @@ -0,0 +1,639 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NX_SHAPE +#define PX_PHYSICS_NX_SHAPE +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" +#include "geometry/PxGeometry.h" +#include "geometry/PxGeometryHelpers.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxBoxGeometry; +class PxSphereGeometry; +class PxCapsuleGeometry; +class PxPlaneGeometry; +class PxConvexMeshGeometry; +class PxTriangleMeshGeometry; +class PxHeightFieldGeometry; +class PxRigidActor; +struct PxFilterData; +struct PxRaycastHit; +struct PxSweepHit; + +/** +\brief Flags which affect the behavior of PxShapes. + +@see PxShape PxShape.setFlag() +*/ +struct PxShapeFlag +{ + enum Enum + { + /** + \brief The shape will partake in collision in the physical simulation. + + \note It is illegal to raise the eSIMULATION_SHAPE and eTRIGGER_SHAPE flags. + In the event that one of these flags is already raised the sdk will reject any + attempt to raise the other. To raise the eSIMULATION_SHAPE first ensure that + eTRIGGER_SHAPE is already lowered. + + \note This flag has no effect if simulation is disabled for the corresponding actor (see #PxActorFlag::eDISABLE_SIMULATION). + + @see PxSimulationEventCallback.onContact() PxScene.setSimulationEventCallback() PxShape.setFlag(), PxShape.setFlags() + */ + eSIMULATION_SHAPE = (1<<0), + + /** + \brief The shape will partake in scene queries (ray casts, overlap tests, sweeps, ...). + */ + eSCENE_QUERY_SHAPE = (1<<1), + + /** + \brief The shape is a trigger which can send reports whenever other shapes enter/leave its volume. + + \note Triangle meshes and heightfields can not be triggers. Shape creation will fail in these cases. + + \note Shapes marked as triggers do not collide with other objects. If an object should act both + as a trigger shape and a collision shape then create a rigid body with two shapes, one being a + trigger shape and the other a collision shape. It is illegal to raise the eTRIGGER_SHAPE and + eSIMULATION_SHAPE flags on a single PxShape instance. In the event that one of these flags is already + raised the sdk will reject any attempt to raise the other. To raise the eTRIGGER_SHAPE flag first + ensure that eSIMULATION_SHAPE flag is already lowered. + + \note Trigger shapes will no longer send notification events for interactions with other trigger shapes. + + \note Shapes marked as triggers are allowed to participate in scene queries, provided the eSCENE_QUERY_SHAPE flag is set. + + \note This flag has no effect if simulation is disabled for the corresponding actor (see #PxActorFlag::eDISABLE_SIMULATION). + + @see PxSimulationEventCallback.onTrigger() PxScene.setSimulationEventCallback() PxShape.setFlag(), PxShape.setFlags() + */ + eTRIGGER_SHAPE = (1<<2), + + /** + \brief Enable debug renderer for this shape + + @see PxScene.getRenderBuffer() PxRenderBuffer PxVisualizationParameter + */ + eVISUALIZATION = (1<<3) + }; +}; + +/** +\brief collection of set bits defined in PxShapeFlag. + +@see PxShapeFlag +*/ +typedef PxFlags PxShapeFlags; +PX_FLAGS_OPERATORS(PxShapeFlag::Enum,PxU8) + + +/** +\brief Abstract class for collision shapes. + +Shapes are shared, reference counted objects. + +An instance can be created by calling the createShape() method of the PxRigidActor class, or +the createShape() method of the PxPhysics class. + +

            Visualizations

            +\li PxVisualizationParameter::eCOLLISION_AABBS +\li PxVisualizationParameter::eCOLLISION_SHAPES +\li PxVisualizationParameter::eCOLLISION_AXES + +@see PxPhysics.createShape() PxRigidActor.createShape() PxBoxGeometry PxSphereGeometry PxCapsuleGeometry PxPlaneGeometry PxConvexMeshGeometry +PxTriangleMeshGeometry PxHeightFieldGeometry +*/ +class PxShape : public PxBase +{ +public: + + /** + \brief Decrements the reference count of a shape and releases it if the new reference count is zero. + + Note that in releases prior to PhysX 3.3 this method did not have reference counting semantics and was used to destroy a shape + created with PxActor::createShape(). In PhysX 3.3 and above, this usage is deprecated, instead, use PxRigidActor::detachShape() to detach + a shape from an actor. If the shape to be detached was created with PxActor::createShape(), the actor holds the only counted reference, + and so when the shape is detached it will also be destroyed. + + @see PxRigidActor::createShape() PxPhysics::createShape() PxRigidActor::attachShape() PxRigidActor::detachShape() + */ + virtual void release() = 0; + + /** + \brief Returns the reference count of the shape. + + At creation, the reference count of the shape is 1. Every actor referencing this shape increments the + count by 1. When the reference count reaches 0, and only then, the shape gets destroyed automatically. + + \return the current reference count. + */ + virtual PxU32 getReferenceCount() const = 0; + + /** + \brief Acquires a counted reference to a shape. + + This method increases the reference count of the shape by 1. Decrement the reference count by calling release() + */ + virtual void acquireReference() = 0; + + /** + \brief Get the geometry type of the shape. + + \return Type of shape geometry. + + @see PxGeometryType + */ + virtual PxGeometryType::Enum getGeometryType() const = 0; + + /** + \brief Adjust the geometry of the shape. + + \note The type of the passed in geometry must match the geometry type of the shape. + \note It is not allowed to change the geometry type of a shape. + \note This function does not guarantee correct/continuous behavior when objects are resting on top of old or new geometry. + + \param[in] geometry New geometry of the shape. + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual void setGeometry(const PxGeometry& geometry) = 0; + + + /** + \brief Retrieve the geometry from the shape in a PxGeometryHolder wrapper class. + + \return a PxGeometryHolder object containing the geometry; + + @see PxGeometry PxGeometryType getGeometryType() setGeometry() + */ + + virtual PxGeometryHolder getGeometry() const = 0; + + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getBoxGeometry(PxBoxGeometry& geometry) const = 0; + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getSphereGeometry(PxSphereGeometry& geometry) const = 0; + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getCapsuleGeometry(PxCapsuleGeometry& geometry) const = 0; + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getPlaneGeometry(PxPlaneGeometry& geometry) const = 0; + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getConvexMeshGeometry(PxConvexMeshGeometry& geometry) const = 0; + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getTriangleMeshGeometry(PxTriangleMeshGeometry& geometry) const = 0; + + + /** + \brief Fetch the geometry of the shape. + + \note If the type of geometry to extract does not match the geometry type of the shape + then the method will return false and the passed in geometry descriptor is not modified. + + \param[in] geometry The descriptor to save the shape's geometry data to. + \return True on success else false + + @see PxGeometry PxGeometryType getGeometryType() + */ + virtual bool getHeightFieldGeometry(PxHeightFieldGeometry& geometry) const = 0; + + /** + \brief Retrieves the actor which this shape is associated with. + + \return The actor this shape is associated with, if it is an exclusive shape, else NULL + + @see PxRigidStatic, PxRigidDynamic, PxArticulationLink + */ + virtual PxRigidActor* getActor() const = 0; + + +/************************************************************************************************/ + +/** @name Pose Manipulation +*/ +//@{ + + /** + \brief Sets the pose of the shape in actor space, i.e. relative to the actors to which they are attached. + + This transformation is identity by default. + + The local pose is an attribute of the shape, and so will apply to all actors to which the shape is attached. + + Sleeping: Does NOT wake the associated actor up automatically. + + Note: Does not automatically update the inertia properties of the owning actor (if applicable); use the + PhysX extensions method #PxRigidBodyExt::updateMassAndInertia() to do this. + + Default: the identity transform + + \param[in] pose The new transform from the actor frame to the shape frame. Range: rigid body transform + + @see getLocalPose() + */ + virtual void setLocalPose(const PxTransform& pose) = 0; + + /** + \brief Retrieves the pose of the shape in actor space, i.e. relative to the actor they are owned by. + + This transformation is identity by default. + + \return Pose of shape relative to the actor's frame. + + @see setLocalPose() + */ + virtual PxTransform getLocalPose() const = 0; + +//@} +/************************************************************************************************/ + +/** @name Collision Filtering +*/ +//@{ + + /** + \brief Sets the user definable collision filter data. + + Sleeping: Does wake up the actor if the filter data change causes a formerly suppressed + collision pair to be enabled. + + Default: (0,0,0,0) + + @see getSimulationFilterData() + */ + virtual void setSimulationFilterData(const PxFilterData& data) = 0; + + /** + \brief Retrieves the shape's collision filter data. + + @see setSimulationFilterData() + */ + virtual PxFilterData getSimulationFilterData() const = 0; + + /** + \brief Sets the user definable query filter data. + + Default: (0,0,0,0) + + @see getQueryFilterData() + */ + virtual void setQueryFilterData(const PxFilterData& data) = 0; + + /** + \brief Retrieves the shape's Query filter data. + + @see setQueryFilterData() + */ + virtual PxFilterData getQueryFilterData() const = 0; + +//@} +/************************************************************************************************/ + + /** + \brief Assigns material(s) to the shape. + + Sleeping: Does NOT wake the associated actor up automatically. + + \param[in] materials List of material pointers to assign to the shape. See #PxMaterial + \param[in] materialCount The number of materials provided. + + @see PxPhysics.createMaterial() getMaterials() + */ + virtual void setMaterials(PxMaterial*const* materials, PxU16 materialCount) = 0; + + /** + \brief Returns the number of materials assigned to the shape. + + You can use #getMaterials() to retrieve the material pointers. + + \return Number of materials associated with this shape. + + @see PxMaterial getMaterials() + */ + virtual PxU16 getNbMaterials() const = 0; + + /** + \brief Retrieve all the material pointers associated with the shape. + + You can retrieve the number of material pointers by calling #getNbMaterials() + + Note: Removing materials with #PxMaterial::release() will invalidate the pointer of the released material. + + \param[out] userBuffer The buffer to store the material pointers. + \param[in] bufferSize Size of provided user buffer. + \param[in] startIndex Index of first material pointer to be retrieved + \return Number of material pointers written to the buffer. + + @see PxMaterial getNbMaterials() PxMaterial::release() + */ + virtual PxU32 getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Retrieve material from given triangle index. + + The input index is the internal triangle index as used inside the SDK. This is the index + returned to users by various SDK functions such as raycasts. + + This function is only useful for triangle meshes or heightfields, which have per-triangle + materials. For other shapes the function returns the single material associated with the + shape, regardless of the index. + + \param[in] faceIndex The internal triangle index whose material you want to retrieve. + \return Material from input triangle + + \note If faceIndex value of 0xFFFFffff is passed as an input for mesh and heightfield shapes, this function will issue a warning and return NULL. + \note Scene queries set the value of PxQueryHit::faceIndex to 0xFFFFffff whenever it is undefined or does not apply. + + @see PxMaterial getNbMaterials() PxMaterial::release() + */ + virtual PxMaterial* getMaterialFromInternalFaceIndex(PxU32 faceIndex) const = 0; + + /** + \brief Sets the contact offset. + + Shapes whose distance is less than the sum of their contactOffset values will generate contacts. The contact offset must be positive and + greater than the rest offset. Having a contactOffset greater than than the restOffset allows the collision detection system to + predictively enforce the contact constraint even when the objects are slightly separated. This prevents jitter that would occur + if the constraint were enforced only when shapes were within the rest distance. + + Default: 0.02f * PxTolerancesScale::length + + Sleeping: Does NOT wake the associated actor up automatically. + + \param[in] contactOffset Range: [maximum(0,restOffset), PX_MAX_F32) + + @see getContactOffset PxTolerancesScale setRestOffset + */ + virtual void setContactOffset(PxReal contactOffset) = 0; + + /** + \brief Retrieves the contact offset. + + \return The contact offset of the shape. + + @see setContactOffset() + */ + virtual PxReal getContactOffset() const = 0; + + /** + \brief Sets the rest offset. + + Two shapes will come to rest at a distance equal to the sum of their restOffset values. If the restOffset is 0, they should converge to touching + exactly. Having a restOffset greater than zero is useful to have objects slide smoothly, so that they do not get hung up on irregularities of + each others' surfaces. + + Default: 0.0f + + Sleeping: Does NOT wake the associated actor up automatically. + + \param[in] restOffset Range: (-PX_MAX_F32, contactOffset) + + @see getRestOffset setContactOffset + */ + virtual void setRestOffset(PxReal restOffset) = 0; + + /** + \brief Retrieves the rest offset. + + \return The rest offset of the shape. + + @see setRestOffset() + */ + virtual PxReal getRestOffset() const = 0; + + + /** + \brief Sets torsional patch radius. + + This defines the radius of the contact patch used to apply torsional friction. If the radius is 0, no torsional friction + will be applied. If the radius is > 0, some torsional friction will be applied. This is proportional to the penetration depth + so, if the shapes are separated or penetration is zero, no torsional friction will be applied. It is used to approximate + rotational friction introduced by the compression of contacting surfaces. + + \param[in] radius Range: (0, PX_MAX_F32) + + */ + virtual void setTorsionalPatchRadius(PxReal radius) = 0; + + /** + \brief Gets torsional patch radius. + + This defines the radius of the contact patch used to apply torsional friction. If the radius is 0, no torsional friction + will be applied. If the radius is > 0, some torsional friction will be applied. This is proportional to the penetration depth + so, if the shapes are separated or penetration is zero, no torsional friction will be applied. It is used to approximate + rotational friction introduced by the compression of contacting surfaces. + + \return The torsional patch radius of the shape. + */ + virtual PxReal getTorsionalPatchRadius() const = 0; + + /** + \brief Sets minimum torsional patch radius. + + This defines the minimum radius of the contact patch used to apply torsional friction. If the radius is 0, the amount of torsional friction + that will be applied will be entirely dependent on the value of torsionalPatchRadius. + + If the radius is > 0, some torsional friction will be applied regardless of the value of torsionalPatchRadius or the amount of penetration. + + \param[in] radius Range: (0, PX_MAX_F32) + + */ + virtual void setMinTorsionalPatchRadius(PxReal radius) = 0; + + /** + \brief Gets minimum torsional patch radius. + + This defines the minimum radius of the contact patch used to apply torsional friction. If the radius is 0, the amount of torsional friction + that will be applied will be entirely dependent on the value of torsionalPatchRadius. + + If the radius is > 0, some torsional friction will be applied regardless of the value of torsionalPatchRadius or the amount of penetration. + + \return The minimum torsional patch radius of the shape. + */ + virtual PxReal getMinTorsionalPatchRadius() const = 0; + + +/************************************************************************************************/ + + /** + \brief Sets shape flags + + Sleeping: Does NOT wake the associated actor up automatically. + + \param[in] flag The shape flag to enable/disable. See #PxShapeFlag. + \param[in] value True to set the flag. False to clear the flag specified in flag. + + Default: PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eSCENE_QUERY_SHAPE + + @see PxShapeFlag getFlags() + */ + virtual void setFlag(PxShapeFlag::Enum flag, bool value) = 0; + + /** + \brief Sets shape flags + + @see PxShapeFlag getFlags() + */ + virtual void setFlags(PxShapeFlags inFlags) = 0; + + /** + \brief Retrieves shape flags. + + \return The values of the shape flags. + + @see PxShapeFlag setFlag() + */ + virtual PxShapeFlags getFlags() const = 0; + + /** + \brief Returns true if the shape is exclusive to an actor. + + @see PxPhysics::createShape() + */ + virtual bool isExclusive() const = 0; + + /** + \brief Sets a name string for the object that can be retrieved with #getName(). + + This is for debugging and is not used by the SDK. + The string is not copied by the SDK, only the pointer is stored. + + Default: NULL + + \param[in] name The name string to set the objects name to. + + @see getName() + */ + virtual void setName(const char* name) = 0; + + + /** + \brief retrieves the name string set with setName(). + \return The name associated with the shape. + + @see setName() + */ + virtual const char* getName() const = 0; + + + virtual const char* getConcreteTypeName() const { return "PxShape"; } + +/************************************************************************************************/ + + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. + +protected: + PX_INLINE PxShape(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + PX_INLINE PxShape(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags), userData(NULL) {} + virtual ~PxShape() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxShape", name) || PxBase::isKindOf(name); } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxSimulationEventCallback.h b/src/PhysX/physx/include/PxSimulationEventCallback.h new file mode 100644 index 000000000..52186d374 --- /dev/null +++ b/src/PhysX/physx/include/PxSimulationEventCallback.h @@ -0,0 +1,915 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_SIMULATION_EVENT_CALLBACK +#define PX_SIMULATION_EVENT_CALLBACK +/** \addtogroup physics +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMemory.h" +#include "PxPhysXConfig.h" +#include "PxFiltering.h" +#include "PxContact.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxShape; +class PxActor; +class PxRigidActor; +class PxRigidBody; +class PxConstraint; + + +/** +\brief Extra data item types for contact pairs. + +@see PxContactPairExtraDataItem.type +*/ +struct PxContactPairExtraDataType +{ + enum Enum + { + ePRE_SOLVER_VELOCITY, //!< see #PxContactPairVelocity + ePOST_SOLVER_VELOCITY, //!< see #PxContactPairVelocity + eCONTACT_EVENT_POSE, //!< see #PxContactPairPose + eCONTACT_PAIR_INDEX //!< see #PxContactPairIndex + }; +}; + + +/** +\brief Base class for items in the extra data stream of contact pairs + +@see PxContactPairHeader.extraDataStream +*/ +struct PxContactPairExtraDataItem +{ +public: + PX_FORCE_INLINE PxContactPairExtraDataItem() {} + + /** + \brief The type of the extra data stream item + */ + PxU8 type; +}; + + +/** +\brief Velocities of the contact pair rigid bodies + +This struct is shared by multiple types of extra data items. The #type field allows to distinguish between them: +\li PxContactPairExtraDataType::ePRE_SOLVER_VELOCITY: see #PxPairFlag::ePRE_SOLVER_VELOCITY +\li PxContactPairExtraDataType::ePOST_SOLVER_VELOCITY: see #PxPairFlag::ePOST_SOLVER_VELOCITY + +\note For static rigid bodies, the velocities will be set to zero. + +@see PxContactPairHeader.extraDataStream +*/ +struct PxContactPairVelocity : public PxContactPairExtraDataItem +{ +public: + PX_FORCE_INLINE PxContactPairVelocity() {} + + /** + \brief The linear velocity of the rigid bodies + */ + PxVec3 linearVelocity[2]; + + /** + \brief The angular velocity of the rigid bodies + */ + PxVec3 angularVelocity[2]; +}; + + +/** +\brief World space actor poses of the contact pair rigid bodies + +@see PxContactPairHeader.extraDataStream PxPairFlag::eCONTACT_EVENT_POSE +*/ +struct PxContactPairPose : public PxContactPairExtraDataItem +{ +public: + PX_FORCE_INLINE PxContactPairPose() {} + + /** + \brief The world space pose of the rigid bodies + */ + PxTransform globalPose[2]; +}; + + +/** +\brief Marker for the beginning of a new item set in the extra data stream. + +If CCD with multiple passes is enabled, then a fast moving object might bounce on and off the same +object multiple times. Also, different shapes of the same actor might gain and lose contact with an other +object over multiple passes. This marker allows to seperate the extra data items for each collision case, as well as +distinguish the shape pair reports of different CCD passes. + +Example: +Let us assume that an actor a0 with shapes s0_0 and s0_1 hits another actor a1 with shape s1. +First s0_0 will hit s1, then a0 will slightly rotate and s0_1 will hit s1 while s0_0 will lose contact with s1. +Furthermore, let us say that contact event pose information is requested as extra data. +The extra data stream will look like this: + +PxContactPairIndexA | PxContactPairPoseA | PxContactPairIndexB | PxContactPairPoseB + +The corresponding array of PxContactPair events (see #PxSimulationEventCallback.onContact()) will look like this: + +PxContactPair(touch_found: s0_0, s1) | PxContactPair(touch_lost: s0_0, s1) | PxContactPair(touch_found: s0_1, s1) + +The #index of PxContactPairIndexA will point to the first entry in the PxContactPair array, for PxContactPairIndexB, +#index will point to the third entry. + +@see PxContactPairHeader.extraDataStream +*/ +struct PxContactPairIndex : public PxContactPairExtraDataItem +{ +public: + PX_FORCE_INLINE PxContactPairIndex() {} + + /** + \brief The next item set in the extra data stream refers to the contact pairs starting at #index in the reported PxContactPair array. + */ + PxU16 index; +}; + + +/** +\brief A class to iterate over a contact pair extra data stream. + +@see PxContactPairHeader.extraDataStream +*/ +struct PxContactPairExtraDataIterator +{ + /** + \brief Constructor + \param[in] stream Pointer to the start of the stream. + \param[in] size Size of the stream in bytes. + */ + PX_FORCE_INLINE PxContactPairExtraDataIterator(const PxU8* stream, PxU32 size) + : currPtr(stream), endPtr(stream + size), contactPairIndex(0) + { + clearDataPtrs(); + } + + /** + \brief Advances the iterator to next set of extra data items. + + The contact pair extra data stream contains sets of items as requested by the corresponding #PxPairFlag flags + #PxPairFlag::ePRE_SOLVER_VELOCITY, #PxPairFlag::ePOST_SOLVER_VELOCITY, #PxPairFlag::eCONTACT_EVENT_POSE. A set can contain one + item of each plus the PxContactPairIndex item. This method parses the stream and points the iterator + member variables to the corresponding items of the current set, if they are available. If CCD is not enabled, + you should only get one set of items. If CCD with multiple passes is enabled, you might get more than one item + set. + + \note Even though contact pair extra data is requested per shape pair, you will not get an item set per shape pair + but one per actor pair. If, for example, an actor has two shapes and both collide with another actor, then + there will only be one item set (since it applies to both shape pairs). + + \return True if there was another set of extra data items in the stream, else false. + + @see PxContactPairVelocity PxContactPairPose PxContactPairIndex + */ + PX_INLINE bool nextItemSet() + { + clearDataPtrs(); + + bool foundEntry = false; + bool endOfItemSet = false; + while ((currPtr < endPtr) && (!endOfItemSet)) + { + const PxContactPairExtraDataItem* edItem = reinterpret_cast(currPtr); + PxU8 type = edItem->type; + + switch(type) + { + case PxContactPairExtraDataType::ePRE_SOLVER_VELOCITY: + { + PX_ASSERT(!preSolverVelocity); + preSolverVelocity = static_cast(edItem); + currPtr += sizeof(PxContactPairVelocity); + foundEntry = true; + } + break; + + case PxContactPairExtraDataType::ePOST_SOLVER_VELOCITY: + { + postSolverVelocity = static_cast(edItem); + currPtr += sizeof(PxContactPairVelocity); + foundEntry = true; + } + break; + + case PxContactPairExtraDataType::eCONTACT_EVENT_POSE: + { + eventPose = static_cast(edItem); + currPtr += sizeof(PxContactPairPose); + foundEntry = true; + } + break; + + case PxContactPairExtraDataType::eCONTACT_PAIR_INDEX: + { + if (!foundEntry) + { + contactPairIndex = static_cast(edItem)->index; + currPtr += sizeof(PxContactPairIndex); + foundEntry = true; + } + else + endOfItemSet = true; + } + break; + + default: + return foundEntry; + } + } + + return foundEntry; + } + +private: + /** + \brief Internal helper + */ + PX_FORCE_INLINE void clearDataPtrs() + { + preSolverVelocity = NULL; + postSolverVelocity = NULL; + eventPose = NULL; + } + +public: + /** + \brief Current pointer in the stream. + */ + const PxU8* currPtr; + + /** + \brief Pointer to the end of the stream. + */ + const PxU8* endPtr; + + /** + \brief Pointer to the current pre solver velocity item in the stream. NULL if there is none. + + @see PxContactPairVelocity + */ + const PxContactPairVelocity* preSolverVelocity; + + /** + \brief Pointer to the current post solver velocity item in the stream. NULL if there is none. + + @see PxContactPairVelocity + */ + const PxContactPairVelocity* postSolverVelocity; + + /** + \brief Pointer to the current contact event pose item in the stream. NULL if there is none. + + @see PxContactPairPose + */ + const PxContactPairPose* eventPose; + + /** + \brief The contact pair index of the current item set in the stream. + + @see PxContactPairIndex + */ + PxU32 contactPairIndex; +}; + + +/** +\brief Collection of flags providing information on contact report pairs. + +@see PxContactPairHeader +*/ +struct PxContactPairHeaderFlag +{ + enum Enum + { + eREMOVED_ACTOR_0 = (1<<0), //!< The actor with index 0 has been removed from the scene. + eREMOVED_ACTOR_1 = (1<<1) //!< The actor with index 1 has been removed from the scene. + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxContactPairHeaderFlag. + +@see PxContactPairHeaderFlag +*/ +typedef PxFlags PxContactPairHeaderFlags; +PX_FLAGS_OPERATORS(PxContactPairHeaderFlag::Enum, PxU16) + + +/** +\brief An Instance of this class is passed to PxSimulationEventCallback.onContact(). + +@see PxSimulationEventCallback.onContact() +*/ +struct PxContactPairHeader +{ + public: + PX_INLINE PxContactPairHeader() {} + + /** + \brief The two actors of the notification shape pairs. + + \note The actor pointers might reference deleted actors. This will be the case if PxPairFlag::eNOTIFY_TOUCH_LOST + or PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST events were requested for the pair and one of the involved actors + gets deleted or removed from the scene. Check the #flags member to see whether that is the case. + Do not dereference a pointer to a deleted actor. The pointer to a deleted actor is only provided + such that user data structures which might depend on the pointer value can be updated. + + @see PxActor + */ + PxRigidActor* actors[2]; + + /** + \brief Stream containing extra data as requested in the PxPairFlag flags of the simulation filter. + + This pointer is only valid if any kind of extra data information has been requested for the contact report pair (see #PxPairFlag::ePOST_SOLVER_VELOCITY etc.), + else it will be NULL. + + @see PxPairFlag + */ + const PxU8* extraDataStream; + + /** + \brief Size of the extra data stream [bytes] + */ + PxU16 extraDataStreamSize; + + /** + \brief Additional information on the contact report pair. + + @see PxContactPairHeaderFlag + */ + PxContactPairHeaderFlags flags; + + /** + \brief pointer to the contact pairs + */ + const struct PxContactPair* pairs; + + /** + \brief number of contact pairs + */ + PxU32 nbPairs; +}; + + +/** +\brief Collection of flags providing information on contact report pairs. + +@see PxContactPair +*/ +struct PxContactPairFlag +{ + enum Enum + { + /** + \brief The shape with index 0 has been removed from the actor/scene. + */ + eREMOVED_SHAPE_0 = (1<<0), + + /** + \brief The shape with index 1 has been removed from the actor/scene. + */ + eREMOVED_SHAPE_1 = (1<<1), + + /** + \brief First actor pair contact. + + The provided shape pair marks the first contact between the two actors, no other shape pair has been touching prior to the current simulation frame. + + \note: This info is only available if #PxPairFlag::eNOTIFY_TOUCH_FOUND has been declared for the pair. + */ + eACTOR_PAIR_HAS_FIRST_TOUCH = (1<<2), + + /** + \brief All contact between the actor pair was lost. + + All contact between the two actors has been lost, no shape pairs remain touching after the current simulation frame. + */ + eACTOR_PAIR_LOST_TOUCH = (1<<3), + + /** + \brief Internal flag, used by #PxContactPair.extractContacts() + + The applied contact impulses are provided for every contact point. + This is the case if #PxPairFlag::eSOLVE_CONTACT has been set for the pair. + */ + eINTERNAL_HAS_IMPULSES = (1<<4), + + /** + \brief Internal flag, used by #PxContactPair.extractContacts() + + The provided contact point information is flipped with regards to the shapes of the contact pair. This mainly concerns the order of the internal triangle indices. + */ + eINTERNAL_CONTACTS_ARE_FLIPPED = (1<<5) + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxContactPairFlag. + +@see PxContactPairFlag +*/ +typedef PxFlags PxContactPairFlags; +PX_FLAGS_OPERATORS(PxContactPairFlag::Enum, PxU16) + + +/** +\brief A contact point as used by contact notification +*/ +struct PxContactPairPoint +{ + /** + \brief The position of the contact point between the shapes, in world space. + */ + PxVec3 position; + + /** + \brief The separation of the shapes at the contact point. A negative separation denotes a penetration. + */ + PxReal separation; + + /** + \brief The normal of the contacting surfaces at the contact point. The normal direction points from the second shape to the first shape. + */ + PxVec3 normal; + + /** + \brief The surface index of shape 0 at the contact point. This is used to identify the surface material. + */ + PxU32 internalFaceIndex0; + + /** + \brief The impulse applied at the contact point, in world space. Divide by the simulation time step to get a force value. + */ + PxVec3 impulse; + + /** + \brief The surface index of shape 1 at the contact point. This is used to identify the surface material. + */ + PxU32 internalFaceIndex1; +}; + + +/** +\brief Contact report pair information. + +Instances of this class are passed to PxSimulationEventCallback.onContact(). If contact reports have been requested for a pair of shapes (see #PxPairFlag), +then the corresponding contact information will be provided through this structure. + +@see PxSimulationEventCallback.onContact() +*/ +struct PxContactPair +{ + public: + PX_INLINE PxContactPair() {} + + /** + \brief The two shapes that make up the pair. + + \note The shape pointers might reference deleted shapes. This will be the case if #PxPairFlag::eNOTIFY_TOUCH_LOST + or #PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST events were requested for the pair and one of the involved shapes + gets deleted. Check the #flags member to see whether that is the case. Do not dereference a pointer to a + deleted shape. The pointer to a deleted shape is only provided such that user data structures which might + depend on the pointer value can be updated. + + @see PxShape + */ + PxShape* shapes[2]; + + /** + \brief Pointer to first patch header in contact stream containing contact patch data + + This pointer is only valid if contact point information has been requested for the contact report pair (see #PxPairFlag::eNOTIFY_CONTACT_POINTS). + Use #extractContacts() as a reference for the data layout of the stream. + */ + const PxU8* contactPatches; + + /** + \brief Pointer to first contact point in contact stream containing contact data + + This pointer is only valid if contact point information has been requested for the contact report pair (see #PxPairFlag::eNOTIFY_CONTACT_POINTS). + Use #extractContacts() as a reference for the data layout of the stream. + */ + const PxU8* contactPoints; + + /** + \brief Buffer containing applied impulse data. + + This pointer is only valid if contact point information has been requested for the contact report pair (see #PxPairFlag::eNOTIFY_CONTACT_POINTS). + Use #extractContacts() as a reference for the data layout of the stream. + */ + const PxReal* contactImpulses; + + /** + \brief Size of the contact stream [bytes] including force buffer + */ + PxU32 requiredBufferSize; + + /** + \brief Number of contact points stored in the contact stream + */ + PxU8 contactCount; + + /** + \brief Number of contact patches stored in the contact stream + */ + + PxU8 patchCount; + + /** + \brief Size of the contact stream [bytes] not including force buffer + */ + + PxU16 contactStreamSize; + + /** + \brief Additional information on the contact report pair. + + @see PxContactPairFlag + */ + PxContactPairFlags flags; + + /** + \brief Flags raised due to the contact. + + The events field is a combination of: + +
              +
            • PxPairFlag::eNOTIFY_TOUCH_FOUND,
            • +
            • PxPairFlag::eNOTIFY_TOUCH_PERSISTS,
            • +
            • PxPairFlag::eNOTIFY_TOUCH_LOST,
            • +
            • PxPairFlag::eNOTIFY_TOUCH_CCD,
            • +
            • PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND,
            • +
            • PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS,
            • +
            • PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST
            • +
            + + See the documentation of #PxPairFlag for an explanation of each. + + \note eNOTIFY_TOUCH_CCD can get raised even if the pair did not request this event. However, in such a case it will only get + raised in combination with one of the other flags to point out that the other event occured during a CCD pass. + + @see PxPairFlag + */ + PxPairFlags events; + + PxU32 internalData[2]; // For internal use only + + /** + \brief Extracts the contact points from the stream and stores them in a convenient format. + + \param[out] userBuffer Array of PxContactPairPoint structures to extract the contact points to. The number of contacts for a pair is defined by #contactCount + \param[in] bufferSize Number of PxContactPairPoint structures the provided buffer can store. + \return Number of contact points written to the buffer. + + @see PxContactPairPoint + */ + PX_INLINE PxU32 extractContacts(PxContactPairPoint* userBuffer, PxU32 bufferSize) const; + + /** + \brief Helper method to clone the contact pair and copy the contact data stream into a user buffer. + + The contact data stream is only accessible during the contact report callback. This helper function provides copy functionality + to buffer the contact stream information such that it can get accessed at a later stage. + + \param[out] newPair The contact pair info will get copied to this instance. The contact data stream pointer of the copy will be redirected to the provided user buffer. Use NULL to skip the contact pair copy operation. + \param[out] bufferMemory Memory block to store the contact data stream to. At most #requiredBufferSize bytes will get written to the buffer. + */ + PX_INLINE void bufferContacts(PxContactPair* newPair, PxU8* bufferMemory) const; + + PX_INLINE const PxU32* getInternalFaceIndices() const; +}; + + +PX_INLINE PxU32 PxContactPair::extractContacts(PxContactPairPoint* userBuffer, PxU32 bufferSize) const +{ + PxU32 nbContacts = 0; + + if(contactCount && bufferSize) + { + PxContactStreamIterator iter(contactPatches, contactPoints, getInternalFaceIndices(), patchCount, contactCount); + + const PxReal* impulses = contactImpulses; + + const PxU32 flippedContacts = (flags & PxContactPairFlag::eINTERNAL_CONTACTS_ARE_FLIPPED); + const PxU32 hasImpulses = (flags & PxContactPairFlag::eINTERNAL_HAS_IMPULSES); + + while(iter.hasNextPatch()) + { + iter.nextPatch(); + while(iter.hasNextContact()) + { + iter.nextContact(); + PxContactPairPoint& dst = userBuffer[nbContacts]; + dst.position = iter.getContactPoint(); + dst.separation = iter.getSeparation(); + dst.normal = iter.getContactNormal(); + if(!flippedContacts) + { + dst.internalFaceIndex0 = iter.getFaceIndex0(); + dst.internalFaceIndex1 = iter.getFaceIndex1(); + } + else + { + dst.internalFaceIndex0 = iter.getFaceIndex1(); + dst.internalFaceIndex1 = iter.getFaceIndex0(); + } + + if(hasImpulses) + { + const PxReal impulse = impulses[nbContacts]; + dst.impulse = dst.normal * impulse; + } + else + dst.impulse = PxVec3(0.0f); + ++nbContacts; + if(nbContacts == bufferSize) + return nbContacts; + } + } + } + + return nbContacts; +} + + +PX_INLINE void PxContactPair::bufferContacts(PxContactPair* newPair, PxU8* bufferMemory) const +{ + PxU8* patches = bufferMemory; + PxU8* contacts = NULL; + if(patches) + { + contacts = bufferMemory + patchCount * sizeof(PxContactPatch); + PxMemCopy(patches, contactPatches, sizeof(PxContactPatch)*patchCount); + PxMemCopy(contacts, contactPoints, contactStreamSize - (sizeof(PxContactPatch)*patchCount)); + } + + if(contactImpulses) + { + PxMemCopy(bufferMemory + ((contactStreamSize + 15) & (~15)), contactImpulses, sizeof(PxReal) * contactCount); + } + + if (newPair) + { + *newPair = *this; + newPair->contactPatches = patches; + newPair->contactPoints = contacts; + } +} + + +PX_INLINE const PxU32* PxContactPair::getInternalFaceIndices() const +{ + return reinterpret_cast(contactImpulses + contactCount); +} + +/** +\brief Collection of flags providing information on trigger report pairs. + +@see PxTriggerPair +*/ +struct PxTriggerPairFlag +{ + enum Enum + { + eREMOVED_SHAPE_TRIGGER = (1<<0), //!< The trigger shape has been removed from the actor/scene. + eREMOVED_SHAPE_OTHER = (1<<1), //!< The shape causing the trigger event has been removed from the actor/scene. + eNEXT_FREE = (1<<2) //!< For internal use only. + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxTriggerPairFlag. + +@see PxTriggerPairFlag +*/ +typedef PxFlags PxTriggerPairFlags; +PX_FLAGS_OPERATORS(PxTriggerPairFlag::Enum, PxU8) + + +/** +\brief Descriptor for a trigger pair. + +An array of these structs gets passed to the PxSimulationEventCallback::onTrigger() report. + +\note The shape pointers might reference deleted shapes. This will be the case if #PxPairFlag::eNOTIFY_TOUCH_LOST + events were requested for the pair and one of the involved shapes gets deleted. Check the #flags member to see + whether that is the case. Do not dereference a pointer to a deleted shape. The pointer to a deleted shape is + only provided such that user data structures which might depend on the pointer value can be updated. + +@see PxSimulationEventCallback.onTrigger() +*/ +struct PxTriggerPair +{ + PX_INLINE PxTriggerPair() {} + + PxShape* triggerShape; //!< The shape that has been marked as a trigger. + PxRigidActor* triggerActor; //!< The actor to which triggerShape is attached + PxShape* otherShape; //!< The shape causing the trigger event. \deprecated (see #PxSimulationEventCallback::onTrigger()) If collision between trigger shapes is enabled, then this member might point to a trigger shape as well. + PxRigidActor* otherActor; //!< The actor to which otherShape is attached + PxPairFlag::Enum status; //!< Type of trigger event (eNOTIFY_TOUCH_FOUND or eNOTIFY_TOUCH_LOST). eNOTIFY_TOUCH_PERSISTS events are not supported. + PxTriggerPairFlags flags; //!< Additional information on the pair (see #PxTriggerPairFlag) +}; + + +/** +\brief Descriptor for a broken constraint. + +An array of these structs gets passed to the PxSimulationEventCallback::onConstraintBreak() report. + +@see PxConstraint PxSimulationEventCallback.onConstraintBreak() +*/ +struct PxConstraintInfo +{ + PX_INLINE PxConstraintInfo() {} + PX_INLINE PxConstraintInfo(PxConstraint* c, void* extRef, PxU32 t) : constraint(c), externalReference(extRef), type(t) {} + + PxConstraint* constraint; //!< The broken constraint. + void* externalReference; //!< The external object which owns the constraint (see #PxConstraintConnector::getExternalReference()) + PxU32 type; //!< Unique type ID of the external object. Allows to cast the provided external reference to the appropriate type +}; + + +/** +\brief An interface class that the user can implement in order to receive simulation events. + +With the exception of onAdvance(), the events get sent during the call to either #PxScene::fetchResults() or +#PxScene::flushSimulation() with sendPendingReports=true. onAdvance() gets called while the simulation +is running (that is between PxScene::simulate(), onAdvance() and PxScene::fetchResults()). + +\note SDK state should not be modified from within the callbacks. In particular objects should not +be created or destroyed. If state modification is needed then the changes should be stored to a buffer +and performed after the simulation step. + +Threading: With the exception of onAdvance(), it is not necessary to make these callbacks thread safe as +they will only be called in the context of the user thread. + +@see PxScene.setSimulationEventCallback() PxScene.getSimulationEventCallback() +*/ +class PxSimulationEventCallback + { + public: + /** + \brief This is called when a breakable constraint breaks. + + \note The user should not release the constraint shader inside this call! + + \note No event will get reported if the constraint breaks but gets deleted while the time step is still being simulated. + + \param[in] constraints - The constraints which have been broken. + \param[in] count - The number of constraints + + @see PxConstraint PxConstraintDesc.linearBreakForce PxConstraintDesc.angularBreakForce + */ + virtual void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count) = 0; + + /** + \brief This is called with the actors which have just been woken up. + + \note Only supported by rigid bodies yet. + \note Only called on actors for which the PxActorFlag eSEND_SLEEP_NOTIFIES has been set. + \note Only the latest sleep state transition happening between fetchResults() of the previous frame and fetchResults() of the current frame + will get reported. For example, let us assume actor A is awake, then A->putToSleep() gets called, then later A->wakeUp() gets called. + At the next simulate/fetchResults() step only an onWake() event will get triggered because that was the last transition. + \note If an actor gets newly added to a scene with properties such that it is awake and the sleep state does not get changed by + the user or simulation, then an onWake() event will get sent at the next simulate/fetchResults() step. + + \param[in] actors - The actors which just woke up. + \param[in] count - The number of actors + + @see PxScene.setSimulationEventCallback() PxSceneDesc.simulationEventCallback PxActorFlag PxActor.setActorFlag() + */ + virtual void onWake(PxActor** actors, PxU32 count) = 0; + + /** + \brief This is called with the actors which have just been put to sleep. + + \note Only supported by rigid bodies yet. + \note Only called on actors for which the PxActorFlag eSEND_SLEEP_NOTIFIES has been set. + \note Only the latest sleep state transition happening between fetchResults() of the previous frame and fetchResults() of the current frame + will get reported. For example, let us assume actor A is asleep, then A->wakeUp() gets called, then later A->putToSleep() gets called. + At the next simulate/fetchResults() step only an onSleep() event will get triggered because that was the last transition (assuming the simulation + does not wake the actor up). + \note If an actor gets newly added to a scene with properties such that it is asleep and the sleep state does not get changed by + the user or simulation, then an onSleep() event will get sent at the next simulate/fetchResults() step. + + \param[in] actors - The actors which have just been put to sleep. + \param[in] count - The number of actors + + @see PxScene.setSimulationEventCallback() PxSceneDesc.simulationEventCallback PxActorFlag PxActor.setActorFlag() + */ + virtual void onSleep(PxActor** actors, PxU32 count) = 0; + + /** + \brief This is called when certain contact events occur. + + The method will be called for a pair of actors if one of the colliding shape pairs requested contact notification. + You request which events are reported using the filter shader/callback mechanism (see #PxSimulationFilterShader, + #PxSimulationFilterCallback, #PxPairFlag). + + Do not keep references to the passed objects, as they will be + invalid after this function returns. + + \param[in] pairHeader Information on the two actors whose shapes triggered a contact report. + \param[in] pairs The contact pairs of two actors for which contact reports have been requested. See #PxContactPair. + \param[in] nbPairs The number of provided contact pairs. + + @see PxScene.setSimulationEventCallback() PxSceneDesc.simulationEventCallback PxContactPair PxPairFlag PxSimulationFilterShader PxSimulationFilterCallback + */ + virtual void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 nbPairs) = 0; + + /** + \brief This is called with the current trigger pair events. + + Shapes which have been marked as triggers using PxShapeFlag::eTRIGGER_SHAPE will send events + according to the pair flag specification in the filter shader (see #PxPairFlag, #PxSimulationFilterShader). + + \note Trigger shapes will no longer send notification events for interactions with other trigger shapes. + + \param[in] pairs - The trigger pair events. + \param[in] count - The number of trigger pair events. + + @see PxScene.setSimulationEventCallback() PxSceneDesc.simulationEventCallback PxPairFlag PxSimulationFilterShader PxShapeFlag PxShape.setFlag() + */ + virtual void onTrigger(PxTriggerPair* pairs, PxU32 count) = 0; + + /** + \brief Provides early access to the new pose of moving rigid bodies. + + When this call occurs, rigid bodies having the #PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW + flag set, were moved by the simulation and their new poses can be accessed through the provided buffers. + + \note The provided buffers are valid and can be read until the next call to #PxScene::simulate() or #PxScene::collide(). + + \note Buffered user changes to the rigid body pose will not yet be reflected in the provided data. More important, + the provided data might contain bodies that have been deleted while the simulation was running. It is the user's + responsibility to detect and avoid dereferencing such bodies. + + \note This callback gets triggered while the simulation is running. If the provided rigid body references are used to + read properties of the object, then the callback has to guarantee no other thread is writing to the same body at the same + time. + + \note The code in this callback should be lightweight as it can block the simulation, that is, the + #PxScene::fetchResults() call. + + \param[in] bodyBuffer The rigid bodies that moved and requested early pose reporting. + \param[in] poseBuffer The integrated rigid body poses of the bodies listed in bodyBuffer. + \param[in] count The number of entries in the provided buffers. + + @see PxScene.setSimulationEventCallback() PxSceneDesc.simulationEventCallback PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW + */ + virtual void onAdvance(const PxRigidBody*const* bodyBuffer, const PxTransform* poseBuffer, const PxU32 count) = 0; + + virtual ~PxSimulationEventCallback() {} + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxSimulationStatistics.h b/src/PhysX/physx/include/PxSimulationStatistics.h new file mode 100644 index 000000000..6aa3e74a7 --- /dev/null +++ b/src/PhysX/physx/include/PxSimulationStatistics.h @@ -0,0 +1,330 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_SIMULATION_STATISTICS +#define PX_SIMULATION_STATISTICS +/** \addtogroup physics +@{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxAssert.h" +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Class used to retrieve statistics for a simulation step. + +@see PxScene::getSimulationStatistics() +*/ +class PxSimulationStatistics +{ +public: + + /** + \brief Different types of rigid body collision pair statistics. + @see getRbPairStats + */ + enum RbPairStatsType + { + /** + \brief Shape pairs processed as discrete contact pairs for the current simulation step. + */ + eDISCRETE_CONTACT_PAIRS, + + /** + \brief Shape pairs processed as swept integration pairs for the current simulation step. + + \note Counts the pairs for which special CCD (continuous collision detection) work was actually done and NOT the number of pairs which were configured for CCD. + Furthermore, there can be multiple CCD passes and all processed pairs of all passes are summed up, hence the number can be larger than the amount of pairs which have been configured for CCD. + + @see PxPairFlag::eDETECT_CCD_CONTACT, + */ + eCCD_PAIRS, + + /** + \brief Shape pairs processed with user contact modification enabled for the current simulation step. + + @see PxContactModifyCallback + */ + eMODIFIED_CONTACT_PAIRS, + + /** + \brief Trigger shape pairs processed for the current simulation step. + + @see PxShapeFlag::eTRIGGER_SHAPE + */ + eTRIGGER_PAIRS + }; + + +//objects: + /** + \brief Number of active PxConstraint objects (joints etc.) for the current simulation step. + */ + PxU32 nbActiveConstraints; + + /** + \brief Number of active dynamic bodies for the current simulation step. + + \note Does not include active kinematic bodies + */ + PxU32 nbActiveDynamicBodies; + + /** + \brief Number of active kinematic bodies for the current simulation step. + + \note Kinematic deactivation occurs at the end of the frame after the last call to PxRigidDynamic::setKinematicTarget() was called so kinematics that are + deactivated in a given frame will be included by this counter. + */ + PxU32 nbActiveKinematicBodies; + + /** + \brief Number of static bodies for the current simulation step. + */ + PxU32 nbStaticBodies; + + /** + \brief Number of dynamic bodies for the current simulation step. + + \note Includes inactive and kinematic bodies, and articulation links + */ + PxU32 nbDynamicBodies; + + /** + \brief Number of shapes of each geometry type. + */ + + PxU32 nbShapes[PxGeometryType::eGEOMETRY_COUNT]; + + /** + \brief Number of aggregates in the scene. + */ + PxU32 nbAggregates; + + /** + \brief Number of articulations in the scene. + */ + PxU32 nbArticulations; + +//solver: + /** + \brief The number of 1D axis constraints(joints+contact) present in the current simulation step. + */ + PxU32 nbAxisSolverConstraints; + + /** + \brief The size (in bytes) of the compressed contact stream in the current simulation step + */ + PxU32 compressedContactSize; + + /** + \brief The total required size (in bytes) of the contact constraints in the current simulation step + */ + PxU32 requiredContactConstraintMemory; + + /** + \brief The peak amount of memory (in bytes) that was allocated for constraints (this includes joints) in the current simulation step + */ + PxU32 peakConstraintMemory; + +//broadphase: + /** + \brief Get number of broadphase volumes added for the current simulation step. + + \return Number of broadphase volumes added. + */ + PX_FORCE_INLINE PxU32 getNbBroadPhaseAdds() const + { + return nbBroadPhaseAdds; + } + + /** + \brief Get number of broadphase volumes removed for the current simulation step. + + \return Number of broadphase volumes removed. + */ + PX_FORCE_INLINE PxU32 getNbBroadPhaseRemoves() const + { + return nbBroadPhaseRemoves; + } + +//collisions: + /** + \brief Get number of shape collision pairs of a certain type processed for the current simulation step. + + There is an entry for each geometry pair type. + + \note entry[i][j] = entry[j][i], hence, if you want the sum of all pair + types, you need to discard the symmetric entries + + \param[in] pairType The type of pair for which to get information + \param[in] g0 The geometry type of one pair object + \param[in] g1 The geometry type of the other pair object + \return Number of processed pairs of the specified geometry types. + */ + PxU32 getRbPairStats(RbPairStatsType pairType, PxGeometryType::Enum g0, PxGeometryType::Enum g1) const + { + PX_ASSERT_WITH_MESSAGE( (pairType >= eDISCRETE_CONTACT_PAIRS) && + (pairType <= eTRIGGER_PAIRS), + "Invalid pairType in PxSimulationStatistics::getRbPairStats"); + + if (g0 >= PxGeometryType::eGEOMETRY_COUNT || g1 >= PxGeometryType::eGEOMETRY_COUNT) + { + PX_ASSERT(false); + return 0; + } + + PxU32 nbPairs = 0; + switch(pairType) + { + case eDISCRETE_CONTACT_PAIRS: + nbPairs = nbDiscreteContactPairs[g0][g1]; + break; + case eCCD_PAIRS: + nbPairs = nbCCDPairs[g0][g1]; + break; + case eMODIFIED_CONTACT_PAIRS: + nbPairs = nbModifiedContactPairs[g0][g1]; + break; + case eTRIGGER_PAIRS: + nbPairs = nbTriggerPairs[g0][g1]; + break; + } + return nbPairs; + } + + /** + \brief Total number of (non CCD) pairs reaching narrow phase + */ + PxU32 nbDiscreteContactPairsTotal; + + /** + \brief Total number of (non CCD) pairs for which contacts are successfully cached (<=nbDiscreteContactPairsTotal) + \note This includes pairs for which no contacts are generated, it still counts as a cache hit. + */ + PxU32 nbDiscreteContactPairsWithCacheHits; + + /** + \brief Total number of (non CCD) pairs for which at least 1 contact was generated (<=nbDiscreteContactPairsTotal) + */ + PxU32 nbDiscreteContactPairsWithContacts; + + /** + \brief Number of new pairs found by BP this frame + */ + PxU32 nbNewPairs; + + /** + \brief Number of lost pairs from BP this frame + */ + PxU32 nbLostPairs; + + /** + \brief Number of new touches found by NP this frame + */ + PxU32 nbNewTouches; + + /** + \brief Number of lost touches from NP this frame + */ + PxU32 nbLostTouches; + + /** + \brief Number of partitions used by the solver this frame + */ + PxU32 nbPartitions; + + PxSimulationStatistics() : + nbActiveConstraints (0), + nbActiveDynamicBodies (0), + nbActiveKinematicBodies (0), + nbStaticBodies (0), + nbDynamicBodies (0), + nbAggregates (0), + nbArticulations (0), + nbAxisSolverConstraints (0), + compressedContactSize (0), + requiredContactConstraintMemory (0), + peakConstraintMemory (0), + nbDiscreteContactPairsTotal (0), + nbDiscreteContactPairsWithCacheHits (0), + nbDiscreteContactPairsWithContacts (0), + nbNewPairs (0), + nbLostPairs (0), + nbNewTouches (0), + nbLostTouches (0), + nbPartitions (0) + { + nbBroadPhaseAdds = 0; + nbBroadPhaseRemoves = 0; + + for(PxU32 i=0; i < PxGeometryType::eGEOMETRY_COUNT; i++) + { + for(PxU32 j=0; j < PxGeometryType::eGEOMETRY_COUNT; j++) + { + nbDiscreteContactPairs[i][j] = 0; + nbModifiedContactPairs[i][j] = 0; + nbCCDPairs[i][j] = 0; + nbTriggerPairs[i][j] = 0; + } + } + + for(PxU32 i=0; i < PxGeometryType::eGEOMETRY_COUNT; i++) + { + nbShapes[i] = 0; + } + } + + + // + // We advise to not access these members directly. Use the provided accessor methods instead. + // +//broadphase: + PxU32 nbBroadPhaseAdds; + PxU32 nbBroadPhaseRemoves; + +//collisions: + PxU32 nbDiscreteContactPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 nbCCDPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 nbModifiedContactPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 nbTriggerPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/PxVisualizationParameter.h b/src/PhysX/physx/include/PxVisualizationParameter.h new file mode 100644 index 000000000..eac8a3f00 --- /dev/null +++ b/src/PhysX/physx/include/PxVisualizationParameter.h @@ -0,0 +1,254 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_DEBUG_VISUALIZATION_PARAMETER +#define PX_PHYSICS_NX_DEBUG_VISUALIZATION_PARAMETER + +#include "foundation/PxPreprocessor.h" + +/** \addtogroup physics +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/* +NOTE: Parameters should NOT be conditionally compiled out. Even if a particular feature is not available. +Otherwise the parameter values get shifted about and the numeric values change per platform. This causes problems +when trying to serialize parameters. + +New parameters should also be added to the end of the list for this reason. Also make sure to update +eNUM_VALUES, which should be one higher than the maximum value in the enum. +*/ + +/** +\brief Debug visualization parameters. + +#PxVisualizationParameter::eSCALE is the master switch for enabling visualization, please read the corresponding documentation +for further details. + +@see PxScene.setVisualizationParameter() PxScene.getVisualizationParameter() PxScene.getRenderBuffer() +*/ +struct PxVisualizationParameter +{ + enum Enum + { + /* RigidBody-related parameters */ + + /** + \brief This overall visualization scale gets multiplied with the individual scales. Setting to zero ignores all visualizations. Default is 0. + + The below settings permit the debug visualization of various simulation properties. + The setting is either zero, in which case the property is not drawn. Otherwise it is a scaling factor + that determines the size of the visualization widgets. + + Only objects for which visualization is turned on using setFlag(eVISUALIZATION) are visualized (see #PxActorFlag::eVISUALIZATION, #PxShapeFlag::eVISUALIZATION, ...). + Contacts are visualized if they involve a body which is being visualized. + Default is 0. + + Notes: + - to see any visualization, you have to set PxVisualizationParameter::eSCALE to nonzero first. + - the scale factor has been introduced because it's difficult (if not impossible) to come up with a + good scale for 3D vectors. Normals are normalized and their length is always 1. But it doesn't mean + we should render a line of length 1. Depending on your objects/scene, this might be completely invisible + or extremely huge. That's why the scale factor is here, to let you tune the length until it's ok in + your scene. + - however, things like collision shapes aren't ambiguous. They are clearly defined for example by the + triangles & polygons themselves, and there's no point in scaling that. So the visualization widgets + are only scaled when it makes sense. + + Range: [0, PX_MAX_F32)
            + Default: 0 + */ + eSCALE, + + + /** + \brief Visualize the world axes. + */ + eWORLD_AXES, + + /* Body visualizations */ + + /** + \brief Visualize a bodies axes. + + @see PxActor.globalPose PxActor + */ + eBODY_AXES, + + /** + \brief Visualize a body's mass axes. + + This visualization is also useful for visualizing the sleep state of bodies. Sleeping bodies are drawn in + black, while awake bodies are drawn in white. If the body is sleeping and part of a sleeping group, it is + drawn in red. + + @see PxBodyDesc.massLocalPose PxActor + */ + eBODY_MASS_AXES, + + /** + \brief Visualize the bodies linear velocity. + + @see PxBodyDesc.linearVelocity PxActor + */ + eBODY_LIN_VELOCITY, + + /** + \brief Visualize the bodies angular velocity. + + @see PxBodyDesc.angularVelocity PxActor + */ + eBODY_ANG_VELOCITY, + + + /* Contact visualisations */ + + /** + \brief Visualize contact points. Will enable contact information. + */ + eCONTACT_POINT, + + /** + \brief Visualize contact normals. Will enable contact information. + */ + eCONTACT_NORMAL, + + /** + \brief Visualize contact errors. Will enable contact information. + */ + eCONTACT_ERROR, + + /** + \brief Visualize Contact forces. Will enable contact information. + */ + eCONTACT_FORCE, + + + /** + \brief Visualize actor axes. + + @see PxRigidStatic PxRigidDynamic PxArticulationLink + */ + eACTOR_AXES, + + + /** + \brief Visualize bounds (AABBs in world space) + */ + eCOLLISION_AABBS, + + /** + \brief Shape visualization + + @see PxShape + */ + eCOLLISION_SHAPES, + + /** + \brief Shape axis visualization + + @see PxShape + */ + eCOLLISION_AXES, + + /** + \brief Compound visualization (compound AABBs in world space) + */ + eCOLLISION_COMPOUNDS, + + /** + \brief Mesh & convex face normals + + @see PxTriangleMesh PxConvexMesh + */ + eCOLLISION_FNORMALS, + + /** + \brief Active edges for meshes + + @see PxTriangleMesh + */ + eCOLLISION_EDGES, + + /** + \brief Static pruning structures + */ + eCOLLISION_STATIC, + + /** + \brief Dynamic pruning structures + */ + eCOLLISION_DYNAMIC, + + /** + \brief Visualizes pairwise state. + */ + eDEPRECATED_COLLISION_PAIRS, + + /** + \brief Joint local axes + */ + eJOINT_LOCAL_FRAMES, + + /** + \brief Joint limits + */ + eJOINT_LIMITS, + + /** + \brief Visualize culling box + */ + eCULL_BOX, + + /** + \brief MBP regions + */ + eMBP_REGIONS, + + /** + \brief This is not a parameter, it just records the current number of parameters (as maximum(PxVisualizationParameter)+1) for use in loops. + */ + eNUM_VALUES, + + eFORCE_DWORD = 0x7fffffff + }; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxBoxController.h b/src/PhysX/physx/include/characterkinematic/PxBoxController.h new file mode 100644 index 000000000..ecb7d3baa --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxBoxController.h @@ -0,0 +1,230 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CCT_BOX_CONTROLLER +#define PX_PHYSICS_CCT_BOX_CONTROLLER +/** \addtogroup character + @{ +*/ + +#include "characterkinematic/PxCharacter.h" +#include "characterkinematic/PxController.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Descriptor for a box character controller. + +@see PxBoxController PxControllerDesc +*/ +class PxBoxControllerDesc : public PxControllerDesc +{ +public: + /** + \brief constructor sets to default. + */ + PX_INLINE PxBoxControllerDesc(); + PX_INLINE virtual ~PxBoxControllerDesc() {} + + /** + \brief copy constructor. + */ + PX_INLINE PxBoxControllerDesc(const PxBoxControllerDesc&); + + /** + \brief assignment operator. + */ + PX_INLINE PxBoxControllerDesc& operator=(const PxBoxControllerDesc&); + + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE virtual void setToDefault(); + + /** + \brief returns true if the current settings are valid + + \return True if the descriptor is valid. + */ + PX_INLINE virtual bool isValid() const; + + /** + \brief Half height + + Default: 1.0 + */ + PxF32 halfHeight; // Half-height in the "up" direction + + /** + \brief Half side extent + + Default: 0.5 + */ + PxF32 halfSideExtent; // Half-extent in the "side" direction + + /** + \brief Half forward extent + + Default: 0.5 + */ + PxF32 halfForwardExtent; // Half-extent in the "forward" direction + +protected: + PX_INLINE void copy(const PxBoxControllerDesc&); +}; + +PX_INLINE PxBoxControllerDesc::PxBoxControllerDesc() : + PxControllerDesc (PxControllerShapeType::eBOX), + halfHeight (1.0f), + halfSideExtent (0.5f), + halfForwardExtent (0.5f) +{ +} + +PX_INLINE PxBoxControllerDesc::PxBoxControllerDesc(const PxBoxControllerDesc& other) : PxControllerDesc(other) +{ + copy(other); +} + +PX_INLINE PxBoxControllerDesc& PxBoxControllerDesc::operator=(const PxBoxControllerDesc& other) +{ + PxControllerDesc::operator=(other); + copy(other); + return *this; +} + +PX_INLINE void PxBoxControllerDesc::copy(const PxBoxControllerDesc& other) +{ + halfHeight = other.halfHeight; + halfSideExtent = other.halfSideExtent; + halfForwardExtent = other.halfForwardExtent; +} + +PX_INLINE void PxBoxControllerDesc::setToDefault() +{ + *this = PxBoxControllerDesc(); +} + +PX_INLINE bool PxBoxControllerDesc::isValid() const +{ + if(!PxControllerDesc::isValid()) return false; + if(halfHeight<=0.0f) return false; + if(halfSideExtent<=0.0f) return false; + if(halfForwardExtent<=0.0f) return false; + if(stepOffset>2.0f*halfHeight) return false; // Prevents obvious mistakes + return true; +} + +/** +\brief Box character controller. + +@see PxBoxControllerDesc PxController +*/ +class PxBoxController : public PxController +{ +public: + + /** + \brief Gets controller's half height. + + \return The half height of the controller. + + @see PxBoxControllerDesc.halfHeight setHalfHeight() + */ + virtual PxF32 getHalfHeight() const = 0; + + /** + \brief Gets controller's half side extent. + + \return The half side extent of the controller. + + @see PxBoxControllerDesc.halfSideExtent setHalfSideExtent() + */ + virtual PxF32 getHalfSideExtent() const = 0; + + /** + \brief Gets controller's half forward extent. + + \return The half forward extent of the controller. + + @see PxBoxControllerDesc.halfForwardExtent setHalfForwardExtent() + */ + virtual PxF32 getHalfForwardExtent() const = 0; + + /** + \brief Sets controller's half height. + + \warning this doesn't check for collisions. + + \param[in] halfHeight The new half height for the controller. + \return Currently always true. + + @see PxBoxControllerDesc.halfHeight getHalfHeight() + */ + virtual bool setHalfHeight(PxF32 halfHeight) = 0; + + /** + \brief Sets controller's half side extent. + + \warning this doesn't check for collisions. + + \param[in] halfSideExtent The new half side extent for the controller. + \return Currently always true. + + @see PxBoxControllerDesc.halfSideExtent getHalfSideExtent() + */ + virtual bool setHalfSideExtent(PxF32 halfSideExtent) = 0; + + /** + \brief Sets controller's half forward extent. + + \warning this doesn't check for collisions. + + \param[in] halfForwardExtent The new half forward extent for the controller. + \return Currently always true. + + @see PxBoxControllerDesc.halfForwardExtent getHalfForwardExtent() + */ + virtual bool setHalfForwardExtent(PxF32 halfForwardExtent) = 0; + +protected: + PX_INLINE PxBoxController() {} + virtual ~PxBoxController() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxCapsuleController.h b/src/PhysX/physx/include/characterkinematic/PxCapsuleController.h new file mode 100644 index 000000000..c8cf8a67f --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxCapsuleController.h @@ -0,0 +1,251 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CCT_CAPSULE_CONTROLLER +#define PX_PHYSICS_CCT_CAPSULE_CONTROLLER +/** \addtogroup character + @{ +*/ + +#include "characterkinematic/PxCharacter.h" +#include "characterkinematic/PxController.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxCapsuleClimbingMode +{ + enum Enum + { + eEASY, //!< Standard mode, let the capsule climb over surfaces according to impact normal + eCONSTRAINED, //!< Constrained mode, try to limit climbing according to the step offset + + eLAST + }; +}; + +/** +\brief A descriptor for a capsule character controller. + +@see PxCapsuleController PxControllerDesc +*/ +class PxCapsuleControllerDesc : public PxControllerDesc +{ +public: + /** + \brief constructor sets to default. + */ + PX_INLINE PxCapsuleControllerDesc (); + PX_INLINE virtual ~PxCapsuleControllerDesc () {} + + /** + \brief copy constructor. + */ + PX_INLINE PxCapsuleControllerDesc(const PxCapsuleControllerDesc&); + + /** + \brief assignment operator. + */ + PX_INLINE PxCapsuleControllerDesc& operator=(const PxCapsuleControllerDesc&); + + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE virtual void setToDefault(); + /** + \brief returns true if the current settings are valid + + \return True if the descriptor is valid. + */ + PX_INLINE virtual bool isValid() const; + + /** + \brief The radius of the capsule + + Default: 0.0 + + @see PxCapsuleController + */ + PxF32 radius; + + /** + \brief The height of the controller + + Default: 0.0 + + @see PxCapsuleController + */ + PxF32 height; + + /** + \brief The climbing mode + + Default: PxCapsuleClimbingMode::eEASY + + @see PxCapsuleController + */ + PxCapsuleClimbingMode::Enum climbingMode; + +protected: + PX_INLINE void copy(const PxCapsuleControllerDesc&); +}; + +PX_INLINE PxCapsuleControllerDesc::PxCapsuleControllerDesc () : PxControllerDesc(PxControllerShapeType::eCAPSULE) +{ + radius = height = 0.0f; + climbingMode = PxCapsuleClimbingMode::eEASY; +} + +PX_INLINE PxCapsuleControllerDesc::PxCapsuleControllerDesc(const PxCapsuleControllerDesc& other) : PxControllerDesc(other) +{ + copy(other); +} + +PX_INLINE PxCapsuleControllerDesc& PxCapsuleControllerDesc::operator=(const PxCapsuleControllerDesc& other) +{ + PxControllerDesc::operator=(other); + copy(other); + return *this; +} + +PX_INLINE void PxCapsuleControllerDesc::copy(const PxCapsuleControllerDesc& other) +{ + radius = other.radius; + height = other.height; + climbingMode = other.climbingMode; +} + +PX_INLINE void PxCapsuleControllerDesc::setToDefault() +{ + *this = PxCapsuleControllerDesc(); +} + +PX_INLINE bool PxCapsuleControllerDesc::isValid() const +{ + if(!PxControllerDesc::isValid()) return false; + if(radius<=0.0f) return false; + if(height<=0.0f) return false; + if(stepOffset>height+radius*2.0f) return false; // Prevents obvious mistakes + return true; +} +/** +\brief A capsule character controller. + +The capsule is defined as a position, a vertical height, and a radius. +The height is the distance between the two sphere centers at the end of the capsule. +In other words: + +p = pos (returned by controller)
            +h = height
            +r = radius
            + +p = center of capsule
            +top sphere center = p.y + h*0.5
            +bottom sphere center = p.y - h*0.5
            +top capsule point = p.y + h*0.5 + r
            +bottom capsule point = p.y - h*0.5 - r
            +*/ +class PxCapsuleController : public PxController +{ +public: + + /** + \brief Gets controller's radius. + + \return The radius of the controller. + + @see PxCapsuleControllerDesc.radius setRadius() + */ + virtual PxF32 getRadius() const = 0; + + /** + \brief Sets controller's radius. + + \warning this doesn't check for collisions. + + \param[in] radius The new radius for the controller. + \return Currently always true. + + @see PxCapsuleControllerDesc.radius getRadius() + */ + virtual bool setRadius(PxF32 radius) = 0; + + /** + \brief Gets controller's height. + + \return The height of the capsule controller. + + @see PxCapsuleControllerDesc.height setHeight() + */ + virtual PxF32 getHeight() const = 0; + + /** + \brief Resets controller's height. + + \warning this doesn't check for collisions. + + \param[in] height The new height for the controller. + \return Currently always true. + + @see PxCapsuleControllerDesc.height getHeight() + */ + virtual bool setHeight(PxF32 height) = 0; + + /** + \brief Gets controller's climbing mode. + + \return The capsule controller's climbing mode. + + @see PxCapsuleControllerDesc.climbingMode setClimbingMode() + */ + virtual PxCapsuleClimbingMode::Enum getClimbingMode() const = 0; + + /** + \brief Sets controller's climbing mode. + + \param[in] mode The capsule controller's climbing mode. + + @see PxCapsuleControllerDesc.climbingMode getClimbingMode() + */ + virtual bool setClimbingMode(PxCapsuleClimbingMode::Enum mode) = 0; + +protected: + PX_INLINE PxCapsuleController() {} + virtual ~PxCapsuleController() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxCharacter.h b/src/PhysX/physx/include/characterkinematic/PxCharacter.h new file mode 100644 index 000000000..a0f34fea7 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxCharacter.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CHARACTER_H +#define PX_CHARACTER_H +/** \addtogroup character + @{ +*/ + +#include "foundation/Px.h" + +// define API function declaration +#if defined PX_PHYSX_STATIC_LIB || defined PX_PHYSX_CHARACTER_STATIC_LIB + #define PX_PHYSX_CHARACTER_API +#else + #if PX_WINDOWS + #if defined PX_PHYSX_CHARACTER_EXPORTS + #define PX_PHYSX_CHARACTER_API __declspec(dllexport) + #else + #define PX_PHYSX_CHARACTER_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_PHYSX_CHARACTER_API PX_UNIX_EXPORT + #else + #define PX_PHYSX_CHARACTER_API + #endif +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxController.h b/src/PhysX/physx/include/characterkinematic/PxController.h new file mode 100644 index 000000000..a0be05740 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxController.h @@ -0,0 +1,911 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PX_PHYSICS_CCT_CONTROLLER +#define PX_PHYSICS_CCT_CONTROLLER +/** \addtogroup character + @{ +*/ + +#include "characterkinematic/PxCharacter.h" +#include "characterkinematic/PxExtended.h" +#include "characterkinematic/PxControllerObstacles.h" +#include "PxQueryFiltering.h" +#include "foundation/PxErrorCallback.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief The type of controller, eg box, sphere or capsule. +*/ +struct PxControllerShapeType +{ + enum Enum + { + /** + \brief A box controller. + + @see PxBoxController PxBoxControllerDesc + */ + eBOX, + + /** + \brief A capsule controller + + @see PxCapsuleController PxCapsuleControllerDesc + */ + eCAPSULE, + + eFORCE_DWORD = 0x7fffffff + }; +}; + +class PxShape; +class PxScene; +class PxController; +class PxRigidDynamic; +class PxMaterial; +struct PxFilterData; +class PxQueryFilterCallback; +class PxControllerBehaviorCallback; +class PxObstacleContext; +class PxObstacle; + +/** +\brief specifies how a CCT interacts with non-walkable parts. + +This is only used when slopeLimit is non zero. It is currently enabled for static actors only, and not supported for spheres or capsules. +*/ +struct PxControllerNonWalkableMode +{ + enum Enum + { + ePREVENT_CLIMBING, //!< Stops character from climbing up non-walkable slopes, but doesn't move it otherwise + ePREVENT_CLIMBING_AND_FORCE_SLIDING //!< Stops character from climbing up non-walkable slopes, and forces it to slide down those slopes + }; +}; + +/** +\brief specifies which sides a character is colliding with. +*/ +struct PxControllerCollisionFlag +{ + enum Enum + { + eCOLLISION_SIDES = (1<<0), //!< Character is colliding to the sides. + eCOLLISION_UP = (1<<1), //!< Character has collision above. + eCOLLISION_DOWN = (1<<2) //!< Character has collision below. + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxControllerCollisionFlag. + +@see PxControllerCollisionFlag +*/ +typedef PxFlags PxControllerCollisionFlags; +PX_FLAGS_OPERATORS(PxControllerCollisionFlag::Enum, PxU8) + + +/** +\brief Describes a controller's internal state. +*/ +struct PxControllerState +{ + PxVec3 deltaXP; //!< delta position vector for the object the CCT is standing/riding on. Not always match the CCT delta when variable timesteps are used. + PxShape* touchedShape; //!< Shape on which the CCT is standing + PxRigidActor* touchedActor; //!< Actor owning 'touchedShape' + ObstacleHandle touchedObstacleHandle; // Obstacle on which the CCT is standing + PxU32 collisionFlags; //!< Last known collision flags (PxControllerCollisionFlag) + bool standOnAnotherCCT; //!< Are we standing on another CCT? + bool standOnObstacle; //!< Are we standing on a user-defined obstacle? + bool isMovingUp; //!< is CCT moving up or not? (i.e. explicit jumping) +}; + +/** +\brief Describes a controller's internal statistics. +*/ +struct PxControllerStats +{ + PxU16 nbIterations; + PxU16 nbFullUpdates; + PxU16 nbPartialUpdates; + PxU16 nbTessellation; +}; + +/** +\brief Describes a generic CCT hit. +*/ +struct PxControllerHit +{ + PxController* controller; //!< Current controller + PxExtendedVec3 worldPos; //!< Contact position in world space + PxVec3 worldNormal; //!< Contact normal in world space + PxVec3 dir; //!< Motion direction + PxF32 length; //!< Motion length +}; + +/** +\brief Describes a hit between a CCT and a shape. Passed to onShapeHit() + +@see PxUserControllerHitReport.onShapeHit() +*/ +struct PxControllerShapeHit : public PxControllerHit +{ + PxShape* shape; //!< Touched shape + PxRigidActor* actor; //!< Touched actor + PxU32 triangleIndex; //!< touched triangle index (only for meshes/heightfields) +}; + +/** +\brief Describes a hit between a CCT and another CCT. Passed to onControllerHit(). + +@see PxUserControllerHitReport.onControllerHit() +*/ +struct PxControllersHit : public PxControllerHit +{ + PxController* other; //!< Touched controller +}; + +/** +\brief Describes a hit between a CCT and a user-defined obstacle. Passed to onObstacleHit(). + +@see PxUserControllerHitReport.onObstacleHit() PxObstacleContext +*/ +struct PxControllerObstacleHit : public PxControllerHit +{ + const void* userData; +}; + +/** +\brief User callback class for character controller events. + +\note Character controller hit reports are only generated when move is called. + +@see PxControllerDesc.callback +*/ +class PxUserControllerHitReport +{ +public: + + /** + \brief Called when current controller hits a shape. + + This is called when the CCT moves and hits a shape. This will not be called when a moving shape hits a non-moving CCT. + + \param[in] hit Provides information about the hit. + + @see PxControllerShapeHit + */ + virtual void onShapeHit(const PxControllerShapeHit& hit) = 0; + + /** + \brief Called when current controller hits another controller. + + \param[in] hit Provides information about the hit. + + @see PxControllersHit + */ + virtual void onControllerHit(const PxControllersHit& hit) = 0; + + /** + \brief Called when current controller hits a user-defined obstacle. + + \param[in] hit Provides information about the hit. + + @see PxControllerObstacleHit PxObstacleContext + */ + virtual void onObstacleHit(const PxControllerObstacleHit& hit) = 0; + +protected: + virtual ~PxUserControllerHitReport(){} +}; + + +/** +\brief Dedicated filtering callback for CCT vs CCT. + +This controls collisions between CCTs (one CCT vs anoter CCT). + +To make each CCT collide against all other CCTs, just return true - or simply avoid defining a callback. +To make each CCT freely go through all other CCTs, just return false. +Otherwise create a custom filtering logic in this callback. + +@see PxControllerFilters +*/ +class PxControllerFilterCallback +{ +public: + virtual ~PxControllerFilterCallback(){} + + /** + \brief Filtering method for CCT-vs-CCT. + + \param[in] a First CCT + \param[in] b Second CCT + \return true to keep the pair, false to filter it out + */ + virtual bool filter(const PxController& a, const PxController& b) = 0; +}; + +/** +\brief Filtering data for "move" call. + +This class contains all filtering-related parameters for the PxController::move() call. + +Collisions between a CCT and the world are filtered using the mFilterData, mFilterCallback and mFilterFlags +members. These parameters are internally passed to PxScene::overlap() to find objects touched by the CCT. +Please refer to the PxScene::overlap() documentation for details. + +Collisions between a CCT and another CCT are filtered using the mCCTFilterCallback member. If this filter +callback is not defined, none of the CCT-vs-CCT collisions are filtered, and each CCT will collide against +all other CCTs. + +\note PxQueryFlag::eANY_HIT and PxQueryFlag::eNO_BLOCK are ignored in mFilterFlags. + +@see PxController.move() PxControllerFilterCallback +*/ +class PxControllerFilters +{ + public: + + PX_INLINE PxControllerFilters(const PxFilterData* filterData=NULL, PxQueryFilterCallback* cb=NULL, PxControllerFilterCallback* cctFilterCb=NULL) : + mFilterData (filterData), + mFilterCallback (cb), + mFilterFlags (PxQueryFlag::eSTATIC|PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER), + mCCTFilterCallback (cctFilterCb) + {} + + // CCT-vs-shapes: + const PxFilterData* mFilterData; //!< Data for internal PxQueryFilterData structure. Passed to PxScene::overlap() call. + //!< This can be NULL, in which case a default PxFilterData is used. + PxQueryFilterCallback* mFilterCallback; //!< Custom filter logic (can be NULL). Passed to PxScene::overlap() call. + PxQueryFlags mFilterFlags; //!< Flags for internal PxQueryFilterData structure. Passed to PxScene::overlap() call. + // CCT-vs-CCT: + PxControllerFilterCallback* mCCTFilterCallback; //!< CCT-vs-CCT filter callback. If NULL, all CCT-vs-CCT collisions are kept. +}; + +/** +\brief Descriptor class for a character controller. + +@see PxBoxController PxCapsuleController +*/ +class PxControllerDesc +{ +public: + + /** + \brief returns true if the current settings are valid + + \return True if the descriptor is valid. + */ + PX_INLINE virtual bool isValid() const; + + /** + \brief Returns the character controller type + + \return The controllers type. + + @see PxControllerType PxCapsuleControllerDesc PxBoxControllerDesc + */ + PX_INLINE PxControllerShapeType::Enum getType() const { return mType; } + + /** + \brief The position of the character + + \note The character's initial position must be such that it does not overlap the static geometry. + + Default: Zero + */ + PxExtendedVec3 position; + + /** + \brief Specifies the 'up' direction + + In order to provide stepping functionality the SDK must be informed about the up direction. + + Default: (0, 1, 0) + + */ + PxVec3 upDirection; + + /** + \brief The maximum slope which the character can walk up. + + In general it is desirable to limit where the character can walk, in particular it is unrealistic + for the character to be able to climb arbitary slopes. + + The limit is expressed as the cosine of desired limit angle. A value of 0 disables this feature. + + \warning It is currently enabled for static actors only (not for dynamic/kinematic actors), and not supported for spheres or capsules. + + Default: 0.707 + + @see upDirection invisibleWallHeight maxJumpHeight + */ + PxF32 slopeLimit; + + /** + \brief Height of invisible walls created around non-walkable triangles + + The library can automatically create invisible walls around non-walkable triangles defined + by the 'slopeLimit' parameter. This defines the height of those walls. If it is 0.0, then + no extra triangles are created. + + Default: 0.0 + + @see upDirection slopeLimit maxJumpHeight + */ + PxF32 invisibleWallHeight; + + /** + \brief Maximum height a jumping character can reach + + This is only used if invisible walls are created ('invisibleWallHeight' is non zero). + + When a character jumps, the non-walkable triangles he might fly over are not found + by the collision queries (since the character's bounding volume does not touch them). + Thus those non-walkable triangles do not create invisible walls, and it is possible + for a jumping character to land on a non-walkable triangle, while he wouldn't have + reached that place by just walking. + + The 'maxJumpHeight' variable is used to extend the size of the collision volume + downward. This way, all the non-walkable triangles are properly found by the collision + queries and it becomes impossible to 'jump over' invisible walls. + + If the character in your game can not jump, it is safe to use 0.0 here. Otherwise it + is best to keep this value as small as possible, since a larger collision volume + means more triangles to process. + + Default: 0.0 + + @see upDirection slopeLimit invisibleWallHeight + */ + PxF32 maxJumpHeight; + + /** + \brief The contact offset used by the controller. + + Specifies a skin around the object within which contacts will be generated. + Use it to avoid numerical precision issues. + + This is dependant on the scale of the users world, but should be a small, positive + non zero value. + + Default: 0.1 + */ + PxF32 contactOffset; + + /** + \brief Defines the maximum height of an obstacle which the character can climb. + + A small value will mean that the character gets stuck and cannot walk up stairs etc, + a value which is too large will mean that the character can climb over unrealistically + high obstacles. + + Default: 0.5 + + @see upDirection + */ + PxF32 stepOffset; + + /** + \brief Density of underlying kinematic actor + + The CCT creates a PhysX's kinematic actor under the hood. This controls its density. + + Default: 10.0 + */ + PxF32 density; + + /** + \brief Scale coefficient for underlying kinematic actor + + The CCT creates a PhysX's kinematic actor under the hood. This controls its scale factor. + This should be a number a bit smaller than 1.0. + + Default: 0.8 + */ + PxF32 scaleCoeff; + + /** + \brief Cached volume growth + + Amount of space around the controller we cache to improve performance. This is a scale factor + that should be higher than 1.0f but not too big, ideally lower than 2.0f. + + Default: 1.5 + */ + PxF32 volumeGrowth; + + /** + \brief Specifies a user report callback. + + This report callback is called when the character collides with shapes and other characters. + + Setting this to NULL disables the callback. + + Default: NULL + + @see PxUserControllerHitReport + */ + PxUserControllerHitReport* reportCallback; + + /** + \brief Specifies a user behavior callback. + + This behavior callback is called to customize the controller's behavior w.r.t. touched shapes. + + Setting this to NULL disables the callback. + + Default: NULL + + @see PxControllerBehaviorCallback + */ + PxControllerBehaviorCallback* behaviorCallback; + + /** + \brief The non-walkable mode controls if a character controller slides or not on a non-walkable part. + + This is only used when slopeLimit is non zero. + + Default: PxControllerNonWalkableMode::ePREVENT_CLIMBING + + @see PxControllerNonWalkableMode + */ + PxControllerNonWalkableMode::Enum nonWalkableMode; + + /** + \brief The material for the actor associated with the controller. + + The controller internally creates a rigid body actor. This parameter specifies the material of the actor. + + Default: NULL + + @see PxMaterial + */ + PxMaterial* material; + + /** + \brief Use a deletion listener to get informed about released objects and clear internal caches if needed. + + If a character controller registers a deletion listener, it will get informed about released objects. That allows the + controller to invalidate cached data that connects to a released object. If a deletion listener is not + registered, PxController::invalidateCache has to be called manually after objects have been released. + + @see PxController::invalidateCache + + Default: true + */ + bool registerDeletionListener; + + /** + \brief User specified data associated with the controller. + + Default: NULL + */ + void* userData; + +protected: + const PxControllerShapeType::Enum mType; //!< The type of the controller. This gets set by the derived class' ctor, the user should not have to change it. + + /** + \brief constructor sets to default. + */ + PX_INLINE PxControllerDesc(PxControllerShapeType::Enum); + PX_INLINE virtual ~PxControllerDesc(); + + /** + \brief copy constructor. + */ + PX_INLINE PxControllerDesc(const PxControllerDesc&); + + /** + \brief assignment operator. + */ + PX_INLINE PxControllerDesc& operator=(const PxControllerDesc&); + + PX_INLINE void copy(const PxControllerDesc&); +}; + +PX_INLINE PxControllerDesc::PxControllerDesc(PxControllerShapeType::Enum t) : mType(t) +{ + upDirection = PxVec3(0.0f, 1.0f, 0.0f); + slopeLimit = 0.707f; + contactOffset = 0.1f; + stepOffset = 0.5f; + density = 10.0f; + scaleCoeff = 0.8f; + volumeGrowth = 1.5f; + reportCallback = NULL; + behaviorCallback = NULL; + userData = NULL; + nonWalkableMode = PxControllerNonWalkableMode::ePREVENT_CLIMBING; + position.x = PxExtended(0.0); + position.y = PxExtended(0.0); + position.z = PxExtended(0.0); + material = NULL; + invisibleWallHeight = 0.0f; + maxJumpHeight = 0.0f; + registerDeletionListener = true; +} + +PX_INLINE PxControllerDesc::PxControllerDesc(const PxControllerDesc& other) : mType(other.mType) +{ + copy(other); +} + +PX_INLINE PxControllerDesc& PxControllerDesc::operator=(const PxControllerDesc& other) +{ + copy(other); + return *this; +} + +PX_INLINE void PxControllerDesc::copy(const PxControllerDesc& other) +{ + upDirection = other.upDirection; + slopeLimit = other.slopeLimit; + contactOffset = other.contactOffset; + stepOffset = other.stepOffset; + density = other.density; + scaleCoeff = other.scaleCoeff; + volumeGrowth = other.volumeGrowth; + reportCallback = other.reportCallback; + behaviorCallback = other.behaviorCallback; + userData = other.userData; + nonWalkableMode = other.nonWalkableMode; + position.x = other.position.x; + position.y = other.position.y; + position.z = other.position.z; + material = other.material; + invisibleWallHeight = other.invisibleWallHeight; + maxJumpHeight = other.maxJumpHeight; + registerDeletionListener = other.registerDeletionListener; +} + +PX_INLINE PxControllerDesc::~PxControllerDesc() +{ +} + +PX_INLINE bool PxControllerDesc::isValid() const +{ + if( mType!=PxControllerShapeType::eBOX + && mType!=PxControllerShapeType::eCAPSULE) + return false; + if(scaleCoeff<0.0f) return false; + if(volumeGrowth<1.0f) return false; + if(density<0.0f) return false; + if(slopeLimit<0.0f) return false; + if(stepOffset<0.0f) return false; + if(contactOffset<=0.0f) return false; + if(!material) return false; + + return true; +} + + +/** +\brief Base class for character controllers. + +@see PxCapsuleController PxBoxController +*/ +class PxController +{ +public: + //********************************************************************* + // DEPRECATED FUNCTIONS: + // + // PX_DEPRECATED virtual void setInteraction(PxCCTInteractionMode::Enum flag) = 0; + // PX_DEPRECATED virtual PxCCTInteractionMode::Enum getInteraction() const = 0; + // PX_DEPRECATED virtual void setGroupsBitmask(PxU32 bitmask) = 0; + // PX_DEPRECATED virtual PxU32 getGroupsBitmask() const = 0; + // + // => replaced with: + // + // PxControllerFilters::mCCTFilterCallback. Please define a PxControllerFilterCallback object and emulate the old interaction mode there. + // + //********************************************************************* + + /** + \brief Return the type of controller + + @see PxControllerType + */ + virtual PxControllerShapeType::Enum getType() const = 0; + + /** + \brief Releases the controller. + */ + virtual void release() = 0; + + /** + \brief Moves the character using a "collide-and-slide" algorithm. + + \param[in] disp Displacement vector + \param[in] minDist The minimum travelled distance to consider. If travelled distance is smaller, the character doesn't move. + This is used to stop the recursive motion algorithm when remaining distance to travel is small. + \param[in] elapsedTime Time elapsed since last call + \param[in] filters User-defined filters for this move + \param[in] obstacles Potential additional obstacles the CCT should collide with. + \return Collision flags, collection of ::PxControllerCollisionFlags + */ + virtual PxControllerCollisionFlags move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles=NULL) = 0; + + /** + \brief Sets controller's position. + + The position controlled by this function is the center of the collision shape. + + \warning This is a 'teleport' function, it doesn't check for collisions. + \warning The character's position must be such that it does not overlap the static geometry. + + To move the character under normal conditions use the #move() function. + + \param[in] position The new (center) positon for the controller. + \return Currently always returns true. + + @see PxControllerDesc.position getPosition() getFootPosition() setFootPosition() move() + */ + virtual bool setPosition(const PxExtendedVec3& position) = 0; + + /** + \brief Retrieve the raw position of the controller. + + The position retrieved by this function is the center of the collision shape. To retrieve the bottom position of the shape, + a.k.a. the foot position, use the getFootPosition() function. + + The position is updated by calls to move(). Calling this method without calling + move() will return the last position or the initial position of the controller. + + \return The controller's center position + + @see PxControllerDesc.position setPosition() getFootPosition() setFootPosition() move() + */ + virtual const PxExtendedVec3& getPosition() const = 0; + + /** + \brief Set controller's foot position. + + The position controlled by this function is the bottom of the collision shape, a.k.a. the foot position. + + \note The foot position takes the contact offset into account + + \warning This is a 'teleport' function, it doesn't check for collisions. + + To move the character under normal conditions use the #move() function. + + \param[in] position The new (bottom) positon for the controller. + \return Currently always returns true. + + @see PxControllerDesc.position setPosition() getPosition() getFootPosition() move() + */ + virtual bool setFootPosition(const PxExtendedVec3& position) = 0; + + /** + \brief Retrieve the "foot" position of the controller, i.e. the position of the bottom of the CCT's shape. + + \note The foot position takes the contact offset into account + + \return The controller's foot position + + @see PxControllerDesc.position setPosition() getPosition() setFootPosition() move() + */ + virtual PxExtendedVec3 getFootPosition() const = 0; + + /** + \brief Get the rigid body actor associated with this controller (see PhysX documentation). + The behavior upon manually altering this actor is undefined, you should primarily + use it for reading const properties. + + \return the actor associated with the controller. + */ + virtual PxRigidDynamic* getActor() const = 0; + + /** + \brief The step height. + + \param[in] offset The new step offset for the controller. + + @see PxControllerDesc.stepOffset + */ + virtual void setStepOffset(const PxF32 offset) =0; + + /** + \brief Retrieve the step height. + + \return The step offset for the controller. + + @see setStepOffset() + */ + virtual PxF32 getStepOffset() const =0; + + /** + \brief Sets the non-walkable mode for the CCT. + + \param[in] flag The new value of the non-walkable mode. + + \see PxControllerNonWalkableMode + */ + virtual void setNonWalkableMode(PxControllerNonWalkableMode::Enum flag) = 0; + + /** + \brief Retrieves the non-walkable mode for the CCT. + + \return The current non-walkable mode. + + \see PxControllerNonWalkableMode + */ + virtual PxControllerNonWalkableMode::Enum getNonWalkableMode() const = 0; + + /** + \brief Retrieve the contact offset. + + \return The contact offset for the controller. + + @see PxControllerDesc.contactOffset + */ + virtual PxF32 getContactOffset() const =0; + + /** + \brief Sets the contact offset. + + \param[in] offset The contact offset for the controller. + + @see PxControllerDesc.contactOffset + */ + virtual void setContactOffset(PxF32 offset) =0; + + /** + \brief Retrieve the 'up' direction. + + \return The up direction for the controller. + + @see PxControllerDesc.upDirection + */ + virtual PxVec3 getUpDirection() const =0; + + /** + \brief Sets the 'up' direction. + + \param[in] up The up direction for the controller. + + @see PxControllerDesc.upDirection + */ + virtual void setUpDirection(const PxVec3& up) =0; + + /** + \brief Retrieve the slope limit. + + \return The slope limit for the controller. + + @see PxControllerDesc.slopeLimit + */ + virtual PxF32 getSlopeLimit() const =0; + + /** + \brief Sets the slope limit. + + \note This feature can not be enabled at runtime, i.e. if the slope limit is zero when creating the CCT + (which disables the feature) then changing the slope limit at runtime will not have any effect, and the call + will be ignored. + + \param[in] slopeLimit The slope limit for the controller. + + @see PxControllerDesc.slopeLimit + */ + virtual void setSlopeLimit(PxF32 slopeLimit) =0; + + /** + \brief Flushes internal geometry cache. + + The character controller uses caching in order to speed up collision testing. The cache is + automatically flushed when a change to static objects is detected in the scene. For example when a + static shape is added, updated, or removed from the scene, the cache is automatically invalidated. + + However there may be situations that cannot be automatically detected, and those require manual + invalidation of the cache. Currently the user must call this when the filtering behavior changes (the + PxControllerFilters parameter of the PxController::move call). While the controller in principle + could detect a change in these parameters, it cannot detect a change in the behavior of the filtering + function. + + @see PxController.move + */ + virtual void invalidateCache() = 0; + + /** + \brief Retrieve the scene associated with the controller. + + \return The physics scene + */ + virtual PxScene* getScene() = 0; + + /** + \brief Returns the user data associated with this controller. + + \return The user pointer associated with the controller. + + @see PxControllerDesc.userData + */ + virtual void* getUserData() const = 0; + + /** + \brief Sets the user data associated with this controller. + + \param[in] userData The user pointer associated with the controller. + + @see PxControllerDesc.userData + */ + virtual void setUserData(void* userData) = 0; + + /** + \brief Returns information about the controller's internal state. + + \param[out] state The controller's internal state + + @see PxControllerState + */ + virtual void getState(PxControllerState& state) const = 0; + + /** + \brief Returns the controller's internal statistics. + + \param[out] stats The controller's internal statistics + + @see PxControllerStats + */ + virtual void getStats(PxControllerStats& stats) const = 0; + + /** + \brief Resizes the controller. + + This function attempts to resize the controller to a given size, while making sure the bottom + position of the controller remains constant. In other words the function modifies both the + height and the (center) position of the controller. This is a helper function that can be used + to implement a 'crouch' functionality for example. + + \param[in] height Desired controller's height + */ + virtual void resize(PxReal height) = 0; + +protected: + PX_INLINE PxController() {} + virtual ~PxController() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h b/src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h new file mode 100644 index 000000000..e3b3f74c9 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h @@ -0,0 +1,136 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_CCT_BEHAVIOR +#define PX_PHYSICS_CCT_BEHAVIOR +/** \addtogroup character + @{ +*/ + +#include "PxFiltering.h" +#include "characterkinematic/PxCharacter.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxShape; + class PxObstacle; + class PxController; + + /** + \brief specifies controller behavior + */ + struct PxControllerBehaviorFlag + { + enum Enum + { + eCCT_CAN_RIDE_ON_OBJECT = (1<<0), //!< Controller can ride on touched object (i.e. when this touched object is moving horizontally). \note The CCT vs. CCT case is not supported. + eCCT_SLIDE = (1<<1), //!< Controller should slide on touched object + eCCT_USER_DEFINED_RIDE = (1<<2) //!< Disable all code dealing with controllers riding on objects, let users define it outside of the SDK. + }; + }; + + /** + \brief Bitfield that contains a set of raised flags defined in PxControllerBehaviorFlag. + + @see PxControllerBehaviorFlag + */ + typedef PxFlags PxControllerBehaviorFlags; + PX_FLAGS_OPERATORS(PxControllerBehaviorFlag::Enum, PxU8) + + /** + \brief User behavior callback. + + This behavior callback is called to customize the controller's behavior w.r.t. touched shapes. + */ + class PxControllerBehaviorCallback + { + public: + /** + \brief Retrieve behavior flags for a shape. + + When the CCT touches a shape, the CCT's behavior w.r.t. this shape can be customized by users. + This function retrieves the desired PxControllerBehaviorFlag flags capturing the desired behavior. + + \note See comments about deprecated functions at the start of this class + + \param[in] shape The shape the CCT is currently touching + \param[in] actor The actor owning the shape + + \return Desired behavior flags for the given shape + + @see PxControllerBehaviorFlag + */ + virtual PxControllerBehaviorFlags getBehaviorFlags(const PxShape& shape, const PxActor& actor) = 0; + + /** + \brief Retrieve behavior flags for a controller. + + When the CCT touches a controller, the CCT's behavior w.r.t. this controller can be customized by users. + This function retrieves the desired PxControllerBehaviorFlag flags capturing the desired behavior. + + \note The flag PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT is not supported. + \note See comments about deprecated functions at the start of this class + + \param[in] controller The controller the CCT is currently touching + + \return Desired behavior flags for the given controller + + @see PxControllerBehaviorFlag + */ + virtual PxControllerBehaviorFlags getBehaviorFlags(const PxController& controller) = 0; + + /** + \brief Retrieve behavior flags for an obstacle. + + When the CCT touches an obstacle, the CCT's behavior w.r.t. this obstacle can be customized by users. + This function retrieves the desired PxControllerBehaviorFlag flags capturing the desired behavior. + + \note See comments about deprecated functions at the start of this class + + \param[in] obstacle The obstacle the CCT is currently touching + + \return Desired behavior flags for the given obstacle + + @see PxControllerBehaviorFlag + */ + virtual PxControllerBehaviorFlags getBehaviorFlags(const PxObstacle& obstacle) = 0; + + protected: + virtual ~PxControllerBehaviorCallback(){} + }; + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxControllerManager.h b/src/PhysX/physx/include/characterkinematic/PxControllerManager.h new file mode 100644 index 000000000..924133ca3 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxControllerManager.h @@ -0,0 +1,301 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CCT_MANAGER +#define PX_PHYSICS_CCT_MANAGER +/** \addtogroup character + @{ +*/ + +#include "characterkinematic/PxCharacter.h" + +#include "PxPhysXConfig.h" +#include "foundation/PxFlags.h" +#include "foundation/PxErrorCallback.h" +#include "common/PxRenderBuffer.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysics; +class PxScene; +class PxController; +class PxControllerDesc; +class PxObstacleContext; +class PxControllerFilterCallback; + +/** +\brief specifies debug-rendering flags +*/ +struct PxControllerDebugRenderFlag +{ + enum Enum + { + eTEMPORAL_BV = (1<<0), //!< Temporal bounding volume around controllers + eCACHED_BV = (1<<1), //!< Cached bounding volume around controllers + eOBSTACLES = (1<<2), //!< User-defined obstacles + + eNONE = 0, + eALL = 0xffffffff + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxControllerDebugRenderFlag. + +@see PxControllerDebugRenderFlag +*/ +typedef PxFlags PxControllerDebugRenderFlags; +PX_FLAGS_OPERATORS(PxControllerDebugRenderFlag::Enum, PxU32) + + +/** +\brief Manages an array of character controllers. + +@see PxController PxBoxController PxCapsuleController +*/ +class PX_PHYSX_CHARACTER_API PxControllerManager +{ +public: + /** + \brief Releases the controller manager. + + \note This will release all associated controllers and obstacle contexts. + + \note This function is required to be called to release foundation usage. + + */ + virtual void release() = 0; + + /** + \brief Returns the scene the manager is adding the controllers to. + + \return The associated physics scene. + */ + virtual PxScene& getScene() const = 0; + + /** + \brief Returns the number of controllers that are being managed. + + \return The number of controllers. + */ + virtual PxU32 getNbControllers() const = 0; + + /** + \brief Retrieve one of the controllers in the manager. + + \param index the index of the controller to return + \return The controller with the specified index. + */ + virtual PxController* getController(PxU32 index) = 0; + + /** + \brief Creates a new character controller. + + \param[in] desc The controllers descriptor + \return The new controller + + @see PxController PxController.release() PxControllerDesc + */ + virtual PxController* createController(const PxControllerDesc& desc) = 0; + + /** + \brief Releases all the controllers that are being managed. + */ + virtual void purgeControllers() = 0; + + /** + \brief Retrieves debug data. + + \return The render buffer filled with debug-render data + + @see PxControllerManager.setDebugRenderingFlags() + */ + virtual PxRenderBuffer& getRenderBuffer() = 0; + + /** + \brief Sets debug rendering flags + + \param[in] flags The debug rendering flags (combination of PxControllerDebugRenderFlags) + + @see PxControllerManager.getRenderBuffer() PxControllerDebugRenderFlags + */ + virtual void setDebugRenderingFlags(PxControllerDebugRenderFlags flags) = 0; + + /** + \brief Returns the number of obstacle contexts that are being managed. + + \return The number of obstacle contexts. + */ + virtual PxU32 getNbObstacleContexts() const = 0; + + /** + \brief Retrieve one of the obstacle contexts in the manager. + + \param index The index of the obstacle context to retrieve. + \return The obstacle context with the specified index. + */ + virtual PxObstacleContext* getObstacleContext(PxU32 index) = 0; + + /** + \brief Creates an obstacle context. + + \return New obstacle context + + @see PxObstacleContext + */ + virtual PxObstacleContext* createObstacleContext() = 0; + + /** + \brief Computes character-character interactions. + + This function is an optional helper to properly resolve interactions between characters, in case they overlap (which can happen for gameplay reasons, etc). + + You should call this once per frame, before your PxController::move() calls. The function will not move the characters directly, but it will + compute overlap information for each character that will be used in the next move() call. + + You need to provide a proper time value here so that interactions are resolved in a way that do not depend on the framerate. + + If you only have one character in the scene, or if you can guarantee your characters will never overlap, then you do not need to call this function. + + \note Releasing the manager will automatically release all the associated obstacle contexts. + + \param[in] elapsedTime Elapsed time since last call + \param[in] cctFilterCb Filtering callback for CCT-vs-CCT interactions + */ + virtual void computeInteractions(PxF32 elapsedTime, PxControllerFilterCallback* cctFilterCb=NULL) = 0; + + /** + \brief Enables or disables runtime tessellation. + + Large triangles can create accuracy issues in the sweep code, which in turn can lead to characters not sliding smoothly + against geometries, or even penetrating them. This feature allows one to reduce those issues by tessellating large + triangles at runtime, before performing sweeps against them. The amount of tessellation is controlled by the 'maxEdgeLength' parameter. + Any triangle with at least one edge length greater than the maxEdgeLength will get recursively tessellated, until resulting triangles are small enough. + + This features only applies to triangle meshes, convex meshes, heightfields and boxes. + + \param[in] flag True/false to enable/disable runtime tessellation. + \param[in] maxEdgeLength Max edge length allowed before tessellation kicks in. + */ + virtual void setTessellation(bool flag, float maxEdgeLength) = 0; + + /** + \brief Enables or disables the overlap recovery module. + + The overlap recovery module can be used to depenetrate CCTs from static objects when an overlap is detected. This can happen + in three main cases: + - when the CCT is directly spawned or teleported in another object + - when the CCT algorithm fails due to limited FPU accuracy + - when the "up vector" is modified, making the rotated CCT shape overlap surrounding objects + + When activated, the CCT module will automatically try to resolve the penetration, and move the CCT to a safe place where it does + not overlap other objects anymore. This only concerns static objects, dynamic objects are ignored by the recovery module. + + When the recovery module is not activated, it is possible for the CCTs to go through static objects. By default, the recovery + module is enabled. + + The recovery module currently works with all geometries except heightfields. + + \param[in] flag True/false to enable/disable overlap recovery module. + */ + virtual void setOverlapRecoveryModule(bool flag) = 0; + + /** + \brief Enables or disables the precise sweeps. + + Precise sweeps are more accurate, but also potentially slower than regular sweeps. + + By default, precise sweeps are enabled. + + \param[in] flag True/false to enable/disable precise sweeps. + */ + virtual void setPreciseSweeps(bool flag) = 0; + + /** + \brief Enables or disables vertical sliding against ceilings. + + Geometry is seen as "ceilings" when the following condition is met: + + dot product(contact normal, up direction)<0.0f + + This flag controls whether characters should slide vertically along the geometry in that case. + + By default, sliding is allowed. + + \param[in] flag True/false to enable/disable sliding. + */ + virtual void setPreventVerticalSlidingAgainstCeiling(bool flag) = 0; + + /** + \brief Shift the origin of the character controllers and obstacle objects by the specified vector. + + The positions of all character controllers, obstacle objects and the corresponding data structures will get adjusted to reflect the shifted origin location + (the shift vector will get subtracted from all character controller and obstacle object positions). + + \note It is the user's responsibility to keep track of the summed total origin shift and adjust all input/output to/from PhysXCharacterKinematic accordingly. + + \note This call will not automatically shift the PhysX scene and its objects. You need to call PxScene::shiftOrigin() seperately to keep the systems in sync. + + \param[in] shift Translation vector to shift the origin by. + */ + virtual void shiftOrigin(const PxVec3& shift) = 0; + +protected: + PxControllerManager() {} + virtual ~PxControllerManager() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + + /** + \brief Creates the controller manager. + + \param[in] scene PhysX scene. + \param[in] lockingEnabled Enables/disables internal locking. + + The character controller is informed by #PxDeletionListener::onRelease() when actors or shapes are released, and updates its internal + caches accordingly. If character controller movement or a call to #PxControllerManager::shiftOrigin() may overlap with actor/shape releases, + internal data structures must be guarded against concurrent access. + + Locking guarantees thread safety in such scenarios. + + \note locking may result in significant slowdown for release of actors or shapes. + + By default, locking is disabled. + */ +PX_C_EXPORT PX_PHYSX_CHARACTER_API physx::PxControllerManager* PX_CALL_CONV PxCreateControllerManager(physx::PxScene& scene, bool lockingEnabled = false); + +/** @} */ +#endif //PX_PHYSICS_CCT_MANAGER diff --git a/src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h b/src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h new file mode 100644 index 000000000..ae1ef5231 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h @@ -0,0 +1,192 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_CCT_OBSTACLES +#define PX_PHYSICS_CCT_OBSTACLES +/** \addtogroup character + @{ +*/ + +#include "characterkinematic/PxCharacter.h" +#include "characterkinematic/PxExtended.h" +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxControllerManager; + + #define INVALID_OBSTACLE_HANDLE 0xffffffff + + /** + \brief Base class for obstacles. + + @see PxBoxObstacle PxCapsuleObstacle PxObstacleContext + */ + class PxObstacle + { + protected: + PxObstacle() : + mType (PxGeometryType::eINVALID), + mUserData (NULL), + mPos (0.0, 0.0, 0.0), + mRot (PxQuat(PxIdentity)) + {} + + PxGeometryType::Enum mType; + public: + + PX_FORCE_INLINE PxGeometryType::Enum getType() const { return mType; } + + void* mUserData; + PxExtendedVec3 mPos; + PxQuat mRot; + }; + + /** + \brief A box obstacle. + + @see PxObstacle PxCapsuleObstacle PxObstacleContext + */ + class PxBoxObstacle : public PxObstacle + { + public: + PxBoxObstacle() : + mHalfExtents(0.0f) + { mType = PxGeometryType::eBOX; } + + PxVec3 mHalfExtents; + }; + + /** + \brief A capsule obstacle. + + @see PxBoxObstacle PxObstacle PxObstacleContext + */ + class PxCapsuleObstacle : public PxObstacle + { + public: + PxCapsuleObstacle() : + mHalfHeight (0.0f), + mRadius (0.0f) + { mType = PxGeometryType::eCAPSULE; } + + PxReal mHalfHeight; + PxReal mRadius; + }; + + typedef PxU32 ObstacleHandle; + + /** + \brief Context class for obstacles. + + An obstacle context class contains and manages a set of user-defined obstacles. + + @see PxBoxObstacle PxCapsuleObstacle PxObstacle + */ + class PxObstacleContext + { + public: + PxObstacleContext() {} + virtual ~PxObstacleContext() {} + + /** + \brief Releases the context. + */ + virtual void release() = 0; + + /** + \brief Retrieves the controller manager associated with this context. + + \return The associated controller manager + */ + virtual PxControllerManager& getControllerManager() const = 0; + + /** + \brief Adds an obstacle to the context. + + \param [in] obstacle Obstacle data for the new obstacle. The data gets copied. + + \return Handle for newly-added obstacle + */ + virtual ObstacleHandle addObstacle(const PxObstacle& obstacle) = 0; + + /** + \brief Removes an obstacle from the context. + + \param [in] handle Handle for the obstacle object that needs to be removed. + + \return True if success + */ + virtual bool removeObstacle(ObstacleHandle handle) = 0; + + /** + \brief Updates data for an existing obstacle. + + \param [in] handle Handle for the obstacle object that needs to be updated. + \param [in] obstacle New obstacle data + + \return True if success + */ + virtual bool updateObstacle(ObstacleHandle handle, const PxObstacle& obstacle) = 0; + + /** + \brief Retrieves number of obstacles in the context. + + \return Number of obstacles in the context + */ + virtual PxU32 getNbObstacles() const = 0; + + /** + \brief Retrieves desired obstacle. + + \param [in] i Obstacle index + + \return Desired obstacle + */ + virtual const PxObstacle* getObstacle(PxU32 i) const = 0; + + /** + \brief Retrieves desired obstacle by given handle. + + \param [in] handle Obstacle handle + + \return Desired obstacle + */ + virtual const PxObstacle* getObstacleByHandle(ObstacleHandle handle) const = 0; + }; + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/characterkinematic/PxExtended.h b/src/PhysX/physx/include/characterkinematic/PxExtended.h new file mode 100644 index 000000000..1adb4b9f4 --- /dev/null +++ b/src/PhysX/physx/include/characterkinematic/PxExtended.h @@ -0,0 +1,275 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CCT_EXTENDED +#define PX_PHYSICS_CCT_EXTENDED +/** \addtogroup character + @{ +*/ + +// This needs to be included in Foundation just for the debug renderer + +#include "PxPhysXConfig.h" +#include "foundation/PxTransform.h" +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +// This has to be done here since it also changes the top-level "Px" and "Np" APIs +#define PX_BIG_WORLDS + +#ifdef PX_BIG_WORLDS +typedef double PxExtended; +#define PX_MAX_EXTENDED PX_MAX_F64 +#define PxExtendedAbs(x) fabs(x) + +struct PxExtendedVec3 +{ + PX_INLINE PxExtendedVec3() {} + PX_INLINE PxExtendedVec3(PxExtended _x, PxExtended _y, PxExtended _z) : x(_x), y(_y), z(_z) {} + + PX_INLINE bool isZero() const + { + if(x!=0.0 || y!=0.0 || z!=0.0) return false; + return true; + } + + PX_INLINE PxExtended dot(const PxVec3& v) const + { + return x * PxExtended(v.x) + y * PxExtended(v.y) + z * PxExtended(v.z); + } + + PX_INLINE PxExtended distanceSquared(const PxExtendedVec3& v) const + { + PxExtended dx = x - v.x; + PxExtended dy = y - v.y; + PxExtended dz = z - v.z; + return dx * dx + dy * dy + dz * dz; + } + + PX_INLINE PxExtended magnitudeSquared() const + { + return x * x + y * y + z * z; + } + + PX_INLINE PxExtended magnitude() const + { + return PxSqrt(x * x + y * y + z * z); + } + + PX_INLINE PxExtended normalize() + { + PxExtended m = magnitude(); + if (m != 0.0) + { + const PxExtended il = PxExtended(1.0) / m; + x *= il; + y *= il; + z *= il; + } + return m; + } + + PX_INLINE bool isFinite() const + { + return PxIsFinite(x) && PxIsFinite(y) && PxIsFinite(z); + } + + PX_INLINE void maximum(const PxExtendedVec3& v) + { + if (x < v.x) x = v.x; + if (y < v.y) y = v.y; + if (z < v.z) z = v.z; + } + + + PX_INLINE void minimum(const PxExtendedVec3& v) + { + if (x > v.x) x = v.x; + if (y > v.y) y = v.y; + if (z > v.z) z = v.z; + } + + PX_INLINE void set(PxExtended x_, PxExtended y_, PxExtended z_) + { + this->x = x_; + this->y = y_; + this->z = z_; + } + + PX_INLINE void setPlusInfinity() + { + x = y = z = PX_MAX_EXTENDED; + } + + PX_INLINE void setMinusInfinity() + { + x = y = z = -PX_MAX_EXTENDED; + } + + PX_INLINE void cross(const PxExtendedVec3& left, const PxVec3& right) + { + // temps needed in case left or right is this. + PxExtended a = (left.y * PxExtended(right.z)) - (left.z * PxExtended(right.y)); + PxExtended b = (left.z * PxExtended(right.x)) - (left.x * PxExtended(right.z)); + PxExtended c = (left.x * PxExtended(right.y)) - (left.y * PxExtended(right.x)); + + x = a; + y = b; + z = c; + } + + PX_INLINE void cross(const PxExtendedVec3& left, const PxExtendedVec3& right) + { + // temps needed in case left or right is this. + PxExtended a = (left.y * right.z) - (left.z * right.y); + PxExtended b = (left.z * right.x) - (left.x * right.z); + PxExtended c = (left.x * right.y) - (left.y * right.x); + + x = a; + y = b; + z = c; + } + + PX_INLINE PxExtendedVec3 cross(const PxExtendedVec3& v) const + { + PxExtendedVec3 temp; + temp.cross(*this,v); + return temp; + } + + PX_INLINE void cross(const PxVec3& left, const PxExtendedVec3& right) + { + // temps needed in case left or right is this. + PxExtended a = (PxExtended(left.y) * right.z) - (PxExtended(left.z) * right.y); + PxExtended b = (PxExtended(left.z) * right.x) - (PxExtended(left.x) * right.z); + PxExtended c = (PxExtended(left.x) * right.y) - (PxExtended(left.y) * right.x); + + x = a; + y = b; + z = c; + } + + PX_INLINE PxExtendedVec3 operator-() const + { + return PxExtendedVec3(-x, -y, -z); + } + + PX_INLINE PxExtendedVec3& operator+=(const PxExtendedVec3& v) + { + x += v.x; + y += v.y; + z += v.z; + return *this; + } + + PX_INLINE PxExtendedVec3& operator-=(const PxExtendedVec3& v) + { + x -= v.x; + y -= v.y; + z -= v.z; + return *this; + } + + PX_INLINE PxExtendedVec3& operator+=(const PxVec3& v) + { + x += PxExtended(v.x); + y += PxExtended(v.y); + z += PxExtended(v.z); + return *this; + } + + PX_INLINE PxExtendedVec3& operator-=(const PxVec3& v) + { + x -= PxExtended(v.x); + y -= PxExtended(v.y); + z -= PxExtended(v.z); + return *this; + } + + PX_INLINE PxExtendedVec3& operator*=(const PxReal& s) + { + x *= PxExtended(s); + y *= PxExtended(s); + z *= PxExtended(s); + return *this; + } + + PX_INLINE PxExtendedVec3 operator+(const PxExtendedVec3& v) const + { + return PxExtendedVec3(x + v.x, y + v.y, z + v.z); + } + + PX_INLINE PxVec3 operator-(const PxExtendedVec3& v) const + { + return PxVec3(PxReal(x - v.x), PxReal(y - v.y), PxReal(z - v.z)); + } + + PX_INLINE PxExtended& operator[](int index) + { + PX_ASSERT(index>=0 && index<=2); + + return reinterpret_cast(this)[index]; + } + + + PX_INLINE PxExtended operator[](int index) const + { + PX_ASSERT(index>=0 && index<=2); + + return reinterpret_cast(this)[index]; + } + + PxExtended x,y,z; +}; + + PX_FORCE_INLINE PxVec3 toVec3(const PxExtendedVec3& v) + { + return PxVec3(float(v.x), float(v.y), float(v.z)); + } + +#else +// Big worlds not defined + +typedef PxVec3 PxExtendedVec3; +typedef PxReal PxExtended; +#define PX_MAX_EXTENDED PX_MAX_F32 +#define PxExtendedAbs(x) fabsf(x) +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/collision/PxCollisionDefs.h b/src/PhysX/physx/include/collision/PxCollisionDefs.h new file mode 100644 index 000000000..276954912 --- /dev/null +++ b/src/PhysX/physx/include/collision/PxCollisionDefs.h @@ -0,0 +1,85 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_DEFS_H +#define PX_COLLISION_DEFS_H + +#include "PxPhysXConfig.h" +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "geomutils/GuContactPoint.h" +#include "geomutils/GuContactBuffer.h" +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief A structure to cache contact information produced by LL contact gen functions. + */ + struct PxCache + { + PxU8* mCachedData; //!< Cached data pointer. Allocated via PxCacheAllocator + PxU16 mCachedSize; //!< The total size of the cached data + PxU8 mPairData; //!< Pair data information used and cached internally by some contact gen functions to accelerate performance. + PxU8 mManifoldFlags; //!< Manifold flags used to identify the format the cached data is stored in. + + PxCache() : mCachedData(NULL), mCachedSize(0), mPairData(0), mManifoldFlags(0) + { + } + }; + + + /** + A callback class to allocate memory to cache information used in contact generation. + */ + class PxCacheAllocator + { + public: + /** + \brief Allocates cache data for contact generation. This data is stored inside PxCache objects. The application can retain and provide this information for future contact generation passes + for a given pair to improve contact generation performance. It is the application's responsibility to release this memory appropriately. If the memory is released, the application must ensure that + this memory is no longer referenced by any PxCache objects passed to PxGenerateContacts. + \param byteSize The size of the allocation in bytes + \return the newly-allocated memory. The returned address must be 16-byte aligned. + */ + virtual PxU8* allocateCacheData(const PxU32 byteSize) = 0; + + virtual ~PxCacheAllocator() {} + }; + +#if !PX_DOXYGEN +} +#endif + +#endif + diff --git a/src/PhysX/physx/include/common/PxBase.h b/src/PhysX/physx/include/common/PxBase.h new file mode 100644 index 000000000..848fe91cb --- /dev/null +++ b/src/PhysX/physx/include/common/PxBase.h @@ -0,0 +1,201 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_PX_BASE +#define PX_PHYSICS_PX_BASE + +/** \addtogroup common +@{ +*/ + +#include "PxSerialFramework.h" +#include "PxCollection.h" +#include "common/PxTypeInfo.h" +#include "foundation/PxFlags.h" +#include // For strcmp + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +typedef PxU16 PxType; + +/** +\brief Flags for PxBase. +*/ +struct PxBaseFlag +{ + enum Enum + { + eOWNS_MEMORY = (1<<0), + eIS_RELEASABLE = (1<<1) + }; +}; + +typedef PxFlags PxBaseFlags; +PX_FLAGS_OPERATORS(PxBaseFlag::Enum, PxU16) + +/** +\brief Base class for objects that can be members of a PxCollection. + +All PxBase sub-classes can be serialized. + +@see PxCollection +*/ +class PxBase +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief Releases the PxBase instance, please check documentation of release in derived class. + */ + virtual void release() = 0; + + /** + \brief Returns string name of dynamic type. + \return Class name of most derived type of this object. + */ + virtual const char* getConcreteTypeName() const = 0; + + /* brief Implements dynamic cast functionality. + + Example use: + + if(actor->is()) {...} + + \return A pointer to the specified type if object matches, otherwise NULL + */ + template T* is() { return typeMatch() ? static_cast(this) : NULL; } + + /* brief Implements dynamic cast functionality for const objects. + + Example use: + + if(actor->is()) {...} + + \return A pointer to the specified type if object matches, otherwise NULL + */ + template const T* is() const { return typeMatch() ? static_cast(this) : NULL; } + + /** + \brief Returns concrete type of object. + \return PxConcreteType::Enum of serialized object + + @see PxConcreteType + */ + PX_FORCE_INLINE PxType getConcreteType() const { return mConcreteType; } + + /** + \brief Set PxBaseFlag + + \param[in] flag The flag to be set + \param[in] value The flags new value + */ + PX_FORCE_INLINE void setBaseFlag(PxBaseFlag::Enum flag, bool value) { mBaseFlags = value ? mBaseFlags|flag : mBaseFlags&~flag; } + + /** + \brief Set PxBaseFlags + + \param[in] inFlags The flags to be set + + @see PxBaseFlags + */ + PX_FORCE_INLINE void setBaseFlags(PxBaseFlags inFlags) { mBaseFlags = inFlags; } + + /** + \brief Returns PxBaseFlags + + \return PxBaseFlags + + @see PxBaseFlags + */ + PX_FORCE_INLINE PxBaseFlags getBaseFlags() const { return mBaseFlags; } + + /** + \brief Whether the object is subordinate. + + A class is subordinate, if it can only be instantiated in the context of another class. + + \return Whether the class is subordinate + + @see PxSerialization::isSerializable + */ + virtual bool isReleasable() const { return mBaseFlags & PxBaseFlag::eIS_RELEASABLE; } + +protected: + /** + \brief Constructor setting concrete type and base flags. + */ + PX_INLINE PxBase(PxType concreteType, PxBaseFlags baseFlags) + : mConcreteType(concreteType), mBaseFlags(baseFlags) {} + + /** + \brief Deserialization constructor setting base flags. + */ + PX_INLINE PxBase(PxBaseFlags baseFlags) : mBaseFlags(baseFlags) {} + + /** + \brief Destructor. + */ + virtual ~PxBase() {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* superClass) const { return !::strcmp(superClass, "PxBase"); } + + template bool typeMatch() const + { + return PxU32(PxTypeInfo::eFastTypeId)!=PxU32(PxConcreteType::eUNDEFINED) ? + PxU32(getConcreteType()) == PxU32(PxTypeInfo::eFastTypeId) : isKindOf(PxTypeInfo::name()); + } + + +private: + friend void getBinaryMetaData_PxBase(PxOutputStream& stream); + +protected: + PxType mConcreteType; // concrete type identifier - see PxConcreteType. + PxBaseFlags mBaseFlags; // internal flags + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxCollection.h b/src/PhysX/physx/include/common/PxCollection.h new file mode 100644 index 000000000..904055445 --- /dev/null +++ b/src/PhysX/physx/include/common/PxCollection.h @@ -0,0 +1,279 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_PX_COLLECTION +#define PX_PHYSICS_PX_COLLECTION + +#include "PxSerialFramework.h" + +/** \addtogroup common +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxBase; + +/** +\brief Collection class for serialization. + +A collection is a set of PxBase objects. PxBase objects can be added to the collection +regardless of other objects they depend on. Objects may be named using PxSerialObjectId values in order +to resolve dependencies between objects of different collections. + +Serialization and deserialization only work through collections. + +A scene is typically serialized using the following steps: + + -# create a serialization registry + -# create a collection for scene objects + -# complete the scene objects (adds all dependent objects, e.g. meshes) + -# serialize collection + -# release collection + -# release serialization registry + +For example the code may look like this: + +\code + PxPhysics* physics; // The physics + PxScene* scene; // The physics scene + SerialStream s; // The user-defined stream doing the actual write to disk + + PxSerializationRegistry* registry = PxSerialization::createSerializationRegistry(*physics); // step 1) + PxCollection* collection = PxSerialization::createCollection(*scene); // step 2) + PxSerialization::complete(*collection, *registry); // step 3) + PxSerialization::serializeCollectionToBinary(s, *collection, *registry); // step 4) + collection->release(); // step 5) + registry->release(); // step 6) +\endcode + +A scene is typically deserialized using the following steps: + + -# load a serialized collection into memory + -# create a serialization registry + -# create a collection by passing the serialized memory block + -# add collected objects to scene + -# release collection + -# release serialization registry + +For example the code may look like this: + +\code + PxPhysics* physics; // The physics + PxScene* scene; // The physics scene + void* memory128; // a 128-byte aligned buffer previously loaded from disk by the user - step 1) + + PxSerializationRegistry* registry = PxSerialization::createSerializationRegistry(*physics); // step 2) + PxCollection* collection = PxSerialization::createCollectionFromBinary(memory128, *registry); // step 3) + scene->addCollection(*collection); // step 4) + collection->release(); // step 5) + registry->release(); // step 6) +\endcode + +@see PxBase, PxCreateCollection() +*/ +class PxCollection +{ +public: + + /** + \brief Adds a PxBase object to the collection. + + Adds a PxBase object to the collection. Optionally a PxSerialObjectId can be provided + in order to resolve dependencies between collections. A PxSerialObjectId value of PX_SERIAL_OBJECT_ID_INVALID + means the object remains without id. Objects can be added regardless of other objects they require. If the object + is already in the collection, the ID will be set if it was PX_SERIAL_OBJECT_ID_INVALID previously, otherwise the + operation fails. + + + \param[in] object Object to be added to the collection + \param[in] id Optional PxSerialObjectId id + */ + virtual void add(PxBase& object, PxSerialObjectId id = PX_SERIAL_OBJECT_ID_INVALID) = 0; + + /** + \brief Removes a PxBase member object from the collection. + + Object needs to be contained by the collection. + + \param[in] object PxBase object to be removed + */ + virtual void remove(PxBase& object) = 0; + + /** + \brief Returns whether the collection contains a certain PxBase object. + + \param[in] object PxBase object + \return Whether object is contained. + */ + virtual bool contains(PxBase& object) const = 0; + + /** + \brief Adds an id to a member PxBase object. + + If the object is already associated with an id within the collection, the id is replaced. + May only be called for objects that are members of the collection. The id needs to be unique + within the collection. + + \param[in] object Member PxBase object + \param[in] id PxSerialObjectId id to be given to the object + */ + virtual void addId(PxBase& object, PxSerialObjectId id) = 0; + + /** + \brief Removes id from a contained PxBase object. + + May only be called for ids that are associated with an object in the collection. + + \param[in] id PxSerialObjectId value + */ + virtual void removeId(PxSerialObjectId id) = 0; + + /** + \brief Adds all PxBase objects and their ids of collection to this collection. + + PxBase objects already in this collection are ignored. Object ids need to be conflict + free, i.e. the same object may not have two different ids within the two collections. + + \param[in] collection Collection to be added + */ + virtual void add(PxCollection& collection) = 0; + + /** + \brief Removes all PxBase objects of collection from this collection. + + PxBase objects not present in this collection are ignored. Ids of objects + which are removed are also removed. + + \param[in] collection Collection to be removed + */ + virtual void remove(PxCollection& collection) = 0; + + /** + \brief Gets number of PxBase objects in this collection. + + \return Number of objects in this collection + */ + virtual PxU32 getNbObjects() const = 0; + + /** + \brief Gets the PxBase object of this collection given its index. + + \param[in] index PxBase index in [0, getNbObjects()) + \return PxBase object at index index + */ + virtual PxBase& getObject(PxU32 index) const = 0; + + /** + \brief Copies member PxBase pointers to a user specified buffer. + + \param[out] userBuffer Array of PxBase pointers + \param[in] bufferSize Capacity of userBuffer + \param[in] startIndex Offset into list of member PxBase objects + \return number of members PxBase objects that have been written to the userBuffer + */ + virtual PxU32 getObjects(PxBase** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Looks for a PxBase object given a PxSerialObjectId value. + + If there is no PxBase object in the collection with the given id, NULL is returned. + + \param[in] id PxSerialObjectId value to look for + \return PxBase object with the given id value or NULL + */ + virtual PxBase* find(PxSerialObjectId id) const = 0; + + /** + \brief Gets number of PxSerialObjectId names in this collection. + + \return Number of PxSerialObjectId names in this collection + */ + virtual PxU32 getNbIds() const = 0; + + /** + \brief Copies member PxSerialObjectId values to a user specified buffer. + + \param[out] userBuffer Array of PxSerialObjectId values + \param[in] bufferSize Capacity of userBuffer + \param[in] startIndex Offset into list of member PxSerialObjectId values + \return number of members PxSerialObjectId values that have been written to the userBuffer + */ + virtual PxU32 getIds(PxSerialObjectId* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + + /** + \brief Gets the PxSerialObjectId name of a PxBase object within the collection. + + The PxBase object needs to be a member of the collection. + + \param[in] object PxBase object to get id for + \return PxSerialObjectId name of the object or PX_SERIAL_OBJECT_ID_INVALID if the object is unnamed + */ + virtual PxSerialObjectId getId(const PxBase& object) const = 0; + + /** + \brief Deletes a collection object. + + This function only deletes the collection object, i.e. the container class. It doesn't delete objects + that are part of the collection. + + @see PxCreateCollection() + */ + + virtual void release() = 0; + +protected: + PxCollection() {} + virtual ~PxCollection() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** +\brief Creates a collection object. + +Objects can only be serialized or deserialized through a collection. +For serialization, users must add objects to the collection and serialize the collection as a whole. +For deserialization, the system gives back a collection of deserialized objects to users. + +\return The new collection object. + +@see PxCollection, PxCollection::release() +*/ +PX_PHYSX_COMMON_API physx::PxCollection* PX_CALL_CONV PxCreateCollection(); + + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxCoreUtilityTypes.h b/src/PhysX/physx/include/common/PxCoreUtilityTypes.h new file mode 100644 index 000000000..57a475aa9 --- /dev/null +++ b/src/PhysX/physx/include/common/PxCoreUtilityTypes.h @@ -0,0 +1,211 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CORE_UTILTY_TYPES_H +#define PX_CORE_UTILTY_TYPES_H +/** \addtogroup common +@{ +*/ + +#include "foundation/PxAssert.h" +#include "foundation/PxMemory.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + +struct PxStridedData +{ + /** + \brief The offset in bytes between consecutive samples in the data. + + Default: 0 + */ + PxU32 stride; + const void* data; + + PxStridedData() : stride( 0 ), data( NULL ) {} + + template + PX_INLINE const TDataType& at( PxU32 idx ) const + { + PxU32 theStride( stride ); + if ( theStride == 0 ) + theStride = sizeof( TDataType ); + PxU32 offset( theStride * idx ); + return *(reinterpret_cast( reinterpret_cast< const PxU8* >( data ) + offset )); + } +}; + +template +struct PxTypedStridedData +{ + PxU32 stride; + const TDataType* data; + + PxTypedStridedData() + : stride( 0 ) + , data( NULL ) + { + } + +}; + +struct PxBoundedData : public PxStridedData +{ + PxU32 count; + PxBoundedData() : count( 0 ) {} +}; + +template +struct PxPadding +{ + PxU8 mPadding[TNumBytes]; + PxPadding() + { + for ( PxU8 idx =0; idx < TNumBytes; ++idx ) + mPadding[idx] = 0; + } +}; + +template class PxFixedSizeLookupTable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + PxFixedSizeLookupTable() + : mNbDataPairs(0) + { + } + + PxFixedSizeLookupTable(const PxEMPTY) {} + + PxFixedSizeLookupTable(const PxReal* dataPairs, const PxU32 numDataPairs) + { + PxMemCopy(mDataPairs,dataPairs,sizeof(PxReal)*2*numDataPairs); + mNbDataPairs=numDataPairs; + } + + PxFixedSizeLookupTable(const PxFixedSizeLookupTable& src) + { + PxMemCopy(mDataPairs,src.mDataPairs,sizeof(PxReal)*2*src.mNbDataPairs); + mNbDataPairs=src.mNbDataPairs; + } + + ~PxFixedSizeLookupTable() + { + } + + PxFixedSizeLookupTable& operator=(const PxFixedSizeLookupTable& src) + { + PxMemCopy(mDataPairs,src.mDataPairs,sizeof(PxReal)*2*src.mNbDataPairs); + mNbDataPairs=src.mNbDataPairs; + return *this; + } + + PX_FORCE_INLINE void addPair(const PxReal x, const PxReal y) + { + PX_ASSERT(mNbDataPairs=x0)&&(x=getX(mNbDataPairs-1)); + return getY(mNbDataPairs-1); + } + + PxU32 getNbDataPairs() const {return mNbDataPairs;} + + void clear() + { + memset(mDataPairs, 0, NB_ELEMENTS*2*sizeof(PxReal)); + mNbDataPairs = 0; + } + + PX_FORCE_INLINE PxReal getX(const PxU32 i) const + { + return mDataPairs[2*i]; + } + PX_FORCE_INLINE PxReal getY(const PxU32 i) const + { + return mDataPairs[2*i+1]; + } + + PxReal mDataPairs[2*NB_ELEMENTS]; + PxU32 mNbDataPairs; + PxU32 mPad[3]; + + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxMetaData.h b/src/PhysX/physx/include/common/PxMetaData.h new file mode 100644 index 000000000..c1a050fc4 --- /dev/null +++ b/src/PhysX/physx/include/common/PxMetaData.h @@ -0,0 +1,228 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_METADATA_H +#define PX_PHYSICS_METADATA_H +/** \addtogroup physics +@{ +*/ + +#include "foundation/Px.h" +#include "foundation/PxIO.h" +#include "PxMetaDataFlags.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief Struct to store meta data definitions. + + Note: The individual fields have different meaning depending on the meta data entry configuration. + */ + struct PxMetaDataEntry + { + const char* type; //!< Field type (bool, byte, quaternion, etc) + const char* name; //!< Field name (appears exactly as in the source file) + PxU32 offset; //!< Offset from the start of the class (ie from "this", field is located at "this"+Offset) + PxU32 size; //!< sizeof(Type) + PxU32 count; //!< Number of items of type Type (0 for dynamic sizes) + PxU32 offsetSize; //!< Offset of dynamic size param, for dynamic arrays + PxU32 flags; //!< Field parameters + PxU32 alignment; //!< Explicit alignment + }; + + #define PX_STORE_METADATA(stream, metaData) stream.write(&metaData, sizeof(PxMetaDataEntry)) + + #define PX_SIZE_OF(Class, Member) sizeof((reinterpret_cast(0))->Member) + + /** + \brief specifies a binary metadata entry for a member variable of a class + */ + #define PX_DEF_BIN_METADATA_ITEM(stream, Class, type, name, flags) \ + { \ + PxMetaDataEntry tmp = { #type, #name, PxU32(PX_OFFSET_OF_RT(Class, name)), PX_SIZE_OF(Class, name), \ + 1, 0, flags, 0}; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a member array variable of a class + \details similar to PX_DEF_BIN_METADATA_ITEMS_AUTO but for cases with mismatch between specified type and array type + */ + #define PX_DEF_BIN_METADATA_ITEMS(stream, Class, type, name, flags, count) \ + { \ + PxMetaDataEntry tmp = { #type, #name, PxU32(PX_OFFSET_OF_RT(Class, name)), PX_SIZE_OF(Class, name), \ + count, 0, flags, 0}; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a member array variable of a class + \details similar to PX_DEF_BIN_METADATA_ITEMS but automatically detects the array length, which only works when the specified + type matches the type of the array - does not support PxMetaDataFlag::ePTR + */ + #define PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Class, type, name, flags) \ + { \ + PxMetaDataEntry tmp = { #type, #name, PxU32(PX_OFFSET_OF_RT(Class, name)), PX_SIZE_OF(Class, name), \ + sizeof((reinterpret_cast(0))->name)/sizeof(type), 0, flags, 0}; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a class + */ + #define PX_DEF_BIN_METADATA_CLASS(stream, Class) \ + { \ + PxMetaDataEntry tmp = { #Class, 0, 0, sizeof(Class), 0, 0, PxMetaDataFlag::eCLASS, 0 }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a virtual class + */ + #define PX_DEF_BIN_METADATA_VCLASS(stream, Class) \ + { \ + PxMetaDataEntry tmp = { #Class, 0, 0, sizeof(Class), 0, 0, PxMetaDataFlag::eCLASS|PxMetaDataFlag::eVIRTUAL, 0}; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a typedef + */ + #define PX_DEF_BIN_METADATA_TYPEDEF(stream, newType, oldType) \ + { \ + PxMetaDataEntry tmp = { #newType, #oldType, 0, 0, 0, 0, PxMetaDataFlag::eTYPEDEF, 0 }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for declaring a base class + */ + #define PX_DEF_BIN_METADATA_BASE_CLASS(stream, Class, BaseClass) \ + { \ + Class* myClass = reinterpret_cast(42); \ + BaseClass* s = static_cast(myClass); \ + const PxU32 offset = PxU32(size_t(s) - size_t(myClass)); \ + PxMetaDataEntry tmp = { #Class, #BaseClass, offset, sizeof(Class), 0, 0, PxMetaDataFlag::eCLASS, 0 }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a union + */ + #define PX_DEF_BIN_METADATA_UNION(stream, Class, name) \ + { \ + PxMetaDataEntry tmp = { #Class, 0, PxU32(PX_OFFSET_OF_RT(Class, name)), PX_SIZE_OF(Class, name), \ + 1, 0, PxMetaDataFlag::eUNION, 0 }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for a particular member type of a union + */ + #define PX_DEF_BIN_METADATA_UNION_TYPE(stream, Class, type, enumValue) \ + { \ + PxMetaDataEntry tmp = { #Class, #type, enumValue, 0, 0, 0, PxMetaDataFlag::eUNION, 0 }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for extra data + */ + #define PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, Class, type, control, align) \ + { \ + PxMetaDataEntry tmp = { #type, 0, PxU32(PX_OFFSET_OF_RT(Class, control)), sizeof(type), 0, PxU32(PX_SIZE_OF(Class, control)), \ + PxMetaDataFlag::eEXTRA_DATA|PxMetaDataFlag::eEXTRA_ITEM, align }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for an array of extra data + */ + #define PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, Class, type, control, count, flags, align) \ + { \ + PxMetaDataEntry tmp = { #type, 0, PxU32(PX_OFFSET_OF_RT(Class, control)), PxU32(PX_SIZE_OF(Class, control)), \ + PxU32(PX_OFFSET_OF_RT(Class, count)), PxU32(PX_SIZE_OF(Class, count)), \ + PxMetaDataFlag::eEXTRA_DATA|PxMetaDataFlag::eEXTRA_ITEMS|flags, align }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for an array of extra data + additional to PX_DEF_BIN_METADATA_EXTRA_ITEMS a mask can be specified to interpret the control value + @see PxMetaDataFlag::eCONTROL_MASK + */ + #define PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, Class, type, control, controlMask ,count, flags, align) \ + { \ + PxMetaDataEntry tmp = { #type, 0, PxU32(PX_OFFSET_OF_RT(Class, control)), PxU32(PX_SIZE_OF(Class, control)), \ + PxU32(PX_OFFSET_OF_RT(Class, count)), PxU32(PX_SIZE_OF(Class, count)), \ + PxMetaDataFlag::eCONTROL_MASK|PxMetaDataFlag::eEXTRA_DATA|PxMetaDataFlag::eEXTRA_ITEMS|flags|(controlMask & PxMetaDataFlag::eCONTROL_MASK_RANGE) << 16, \ + align}; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for an array of extra data + \details similar to PX_DEF_BIN_METADATA_EXTRA_ITEMS, but supporting no control - PxMetaDataFlag::ePTR is also not supported + */ + #define PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Class, type, dyn_count, align, flags) \ + { \ + PxMetaDataEntry tmp = { #type, 0, PxU32(PX_OFFSET_OF_RT(Class, dyn_count)), PX_SIZE_OF(Class, dyn_count), align, 0, \ + PxMetaDataFlag::eEXTRA_DATA|flags, align }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry for an string of extra data + */ + #define PX_DEF_BIN_METADATA_EXTRA_NAME(stream, Class, control, align) \ + { \ + PxMetaDataEntry tmp = { "char", "string", 0, 0, 0, 0, PxMetaDataFlag::eEXTRA_DATA|PxMetaDataFlag::eEXTRA_NAME, align }; \ + PX_STORE_METADATA(stream, tmp); \ + } + + /** + \brief specifies a binary metadata entry declaring an extra data alignment for a class + */ + #define PX_DEF_BIN_METADATA_EXTRA_ALIGN(stream, Class, align) \ + { \ + PxMetaDataEntry tmp = { "PxU8", "Alignment", 0, 0, 0, 0, PxMetaDataFlag::eEXTRA_DATA|PxMetaDataFlag::eALIGNMENT, align}; \ + PX_STORE_METADATA(stream, tmp); \ + } + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxMetaDataFlags.h b/src/PhysX/physx/include/common/PxMetaDataFlags.h new file mode 100644 index 000000000..7c890f9e0 --- /dev/null +++ b/src/PhysX/physx/include/common/PxMetaDataFlags.h @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_METADATA_FLAGS +#define PX_PHYSICS_METADATA_FLAGS + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief Flags used to configure binary meta data entries, typically set through PX_DEF_BIN_METADATA defines. + + @see PxMetaDataEntry + */ + struct PxMetaDataFlag + { + enum Enum + { + eCLASS = (1<<0), //!< declares a class + eVIRTUAL = (1<<1), //!< declares class to be virtual + eTYPEDEF = (1<<2), //!< declares a typedef + ePTR = (1<<3), //!< declares a pointer + eEXTRA_DATA = (1<<4), //!< declares extra data exported with PxSerializer::exportExtraData + eEXTRA_ITEM = (1<<5), //!< specifies one element of extra data + eEXTRA_ITEMS = (1<<6), //!< specifies an array of extra data + eEXTRA_NAME = (1<<7), //!< specifies a name of extra data + eUNION = (1<<8), //!< declares a union + ePADDING = (1<<9), //!< declares explicit padding data + eALIGNMENT = (1<<10), //!< declares aligned data + eCOUNT_MASK_MSB = (1<<11), //!< specifies that the count value's most significant bit needs to be masked out + eCOUNT_SKIP_IF_ONE = (1<<12), //!< specifies that the count value is treated as zero for a variable value of one - special case for single triangle meshes + eCONTROL_FLIP = (1<<13), //!< specifies that the control value is the negate of the variable value + eCONTROL_MASK = (1<<14), //!< specifies that the control value is masked - mask bits are assumed to be within eCONTROL_MASK_RANGE + eCONTROL_MASK_RANGE = 0x000000FF, //!< mask range allowed for eCONTROL_MASK + eFORCE_DWORD = 0x7fffffff + }; + }; + +#if !PX_DOXYGEN +} +#endif + +#endif diff --git a/src/PhysX/physx/include/common/PxPhysXCommonConfig.h b/src/PhysX/physx/include/common/PxPhysXCommonConfig.h new file mode 100644 index 000000000..61e2d8754 --- /dev/null +++ b/src/PhysX/physx/include/common/PxPhysXCommonConfig.h @@ -0,0 +1,124 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_NX +#define PX_PHYSICS_COMMON_NX + +/** \addtogroup common +@{ */ + +#include "foundation/Px.h" + +/*Disable support for VS2017 prior version 15.5.1 for windows platform, because of a compiler bug: +https://developercommunity.visualstudio.com/content/problem/66047/possible-compiler-bug.html +*/ +#if (PX_VC == 15) && PX_WINDOWS && (_MSC_FULL_VER < 191225830) +#error Visual studio 2017 prior to 15.5.1 is not supported because of a compiler bug. +#endif + +// define API function declaration (public API only needed because of extensions) +#if defined PX_PHYSX_STATIC_LIB || defined PX_PHYSX_CORE_STATIC_LIB + #define PX_PHYSX_CORE_API +#else + #if PX_WINDOWS + #if defined PX_PHYSX_CORE_EXPORTS + #define PX_PHYSX_CORE_API __declspec(dllexport) + #else + #define PX_PHYSX_CORE_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_PHYSX_CORE_API PX_UNIX_EXPORT + #else + #define PX_PHYSX_CORE_API + #endif +#endif + +#if PX_SUPPORT_GPU_PHYSX +// define API function declaration +#if defined PX_PHYSX_GPU_STATIC + #define PX_PHYSX_GPU_API +#else + #if PX_WINDOWS + #if defined PX_PHYSX_GPU_EXPORTS + #define PX_PHYSX_GPU_API __declspec(dllexport) + #else + #define PX_PHYSX_GPU_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_PHYSX_GPU_API PX_UNIX_EXPORT + #else + #define PX_PHYSX_GPU_API + #endif +#endif + +#else // PX_SUPPORT_GPU_PHYSX +#define PX_PHYSX_GPU_API +#endif // PX_SUPPORT_GPU_PHYSX + +#if defined PX_PHYSX_STATIC_LIB || defined PX_PHYSX_CORE_STATIC_LIB + #define PX_PHYSX_COMMON_API +#else + #if PX_WINDOWS && !defined(__CUDACC__) + #if defined PX_PHYSX_COMMON_EXPORTS + #define PX_PHYSX_COMMON_API __declspec(dllexport) + #else + #define PX_PHYSX_COMMON_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_PHYSX_COMMON_API PX_UNIX_EXPORT + #else + #define PX_PHYSX_COMMON_API + #endif +#endif + +// Changing these parameters requires recompilation of the SDK + +#if !PX_DOXYGEN +namespace physx +{ +#endif + class PxCollection; + class PxBase; + + class PxHeightField; + class PxHeightFieldDesc; + + class PxTriangleMesh; + class PxConvexMesh; + + typedef PxU32 PxTriangleID; + typedef PxU16 PxMaterialTableIndex; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h b/src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h new file mode 100644 index 000000000..414695259 --- /dev/null +++ b/src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h @@ -0,0 +1,84 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_PX_PHYSICS_INSERTION_CALLBACK +#define PX_PHYSICS_PX_PHYSICS_INSERTION_CALLBACK + +#include "PxBase.h" + +/** \addtogroup common +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + + \brief Callback interface that permits PxCooking to insert a + TriangleMesh, HeightfieldMesh or ConvexMesh directly into PxPhysics without the need to store + the cooking results into a stream. + + + Using this is advised only if real-time cooking is required; using "offline" cooking and + streams is otherwise preferred. + + The default PxPhysicsInsertionCallback implementation must be used. The PxPhysics + default callback can be obtained using the PxPhysics::getPhysicsInsertionCallback(). + + @see PxCooking PxPhysics + */ + class PxPhysicsInsertionCallback + { + public: + PxPhysicsInsertionCallback() {} + + /** + \brief Builds object (TriangleMesh, HeightfieldMesh or ConvexMesh) from given data in PxPhysics. + + \param type Object type to build. + \param data Object data + \return PxBase Created object in PxPhysics. + */ + virtual PxBase* buildObjectFromData(PxConcreteType::Enum type, void* data) = 0; + + protected: + virtual ~PxPhysicsInsertionCallback() {} + }; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxProfileZone.h b/src/PhysX/physx/include/common/PxProfileZone.h new file mode 100644 index 000000000..0fda159f1 --- /dev/null +++ b/src/PhysX/physx/include/common/PxProfileZone.h @@ -0,0 +1,51 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXFOUNDATION_PXPROFILEZONE_H +#define PXFOUNDATION_PXPROFILEZONE_H + +#include "foundation/PxProfiler.h" +#include "PxFoundation.h" + +#if PX_DEBUG || PX_CHECKED || PX_PROFILE + #define PX_PROFILE_ZONE(x, y) \ + physx::PxProfileScoped PX_CONCAT(_scoped, __LINE__)(PxGetProfilerCallback(), x, false, y) + #define PX_PROFILE_START_CROSSTHREAD(x, y) \ + if(PxGetProfilerCallback()) \ + PxGetProfilerCallback()->zoneStart(x, true, y) + #define PX_PROFILE_STOP_CROSSTHREAD(x, y) \ + if(PxGetProfilerCallback()) \ + PxGetProfilerCallback()->zoneEnd(NULL, x, true, y) +#else + #define PX_PROFILE_ZONE(x, y) + #define PX_PROFILE_START_CROSSTHREAD(x, y) + #define PX_PROFILE_STOP_CROSSTHREAD(x, y) +#endif + +#define PX_PROFILE_POINTER_TO_U64(pointer) static_cast(reinterpret_cast(pointer)) + +#endif // PXFOUNDATION_PXPROFILEZONE_H diff --git a/src/PhysX/physx/include/common/PxRenderBuffer.h b/src/PhysX/physx/include/common/PxRenderBuffer.h new file mode 100644 index 000000000..8fe0b0fa1 --- /dev/null +++ b/src/PhysX/physx/include/common/PxRenderBuffer.h @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_FOUNDATION_PXRENDERBUFFER_H +#define PX_FOUNDATION_PXRENDERBUFFER_H + +/** \addtogroup common +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "foundation/PxBounds3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Default color values used for debug rendering. +*/ +struct PxDebugColor +{ + enum Enum + { + eARGB_BLACK = 0xff000000, + eARGB_RED = 0xffff0000, + eARGB_GREEN = 0xff00ff00, + eARGB_BLUE = 0xff0000ff, + eARGB_YELLOW = 0xffffff00, + eARGB_MAGENTA = 0xffff00ff, + eARGB_CYAN = 0xff00ffff, + eARGB_WHITE = 0xffffffff, + eARGB_GREY = 0xff808080, + eARGB_DARKRED = 0x88880000, + eARGB_DARKGREEN = 0x88008800, + eARGB_DARKBLUE = 0x88000088 + }; +}; + +/** +\brief Used to store a single point and colour for debug rendering. +*/ +struct PxDebugPoint +{ + PxDebugPoint(const PxVec3& p, const PxU32& c) + : pos(p), color(c) {} + + PxVec3 pos; + PxU32 color; +}; + +/** +\brief Used to store a single line and colour for debug rendering. +*/ +struct PxDebugLine +{ + PxDebugLine(const PxVec3& p0, const PxVec3& p1, const PxU32& c) + : pos0(p0), color0(c), pos1(p1), color1(c) {} + + PxVec3 pos0; + PxU32 color0; + PxVec3 pos1; + PxU32 color1; +}; + +/** +\brief Used to store a single triangle and colour for debug rendering. +*/ +struct PxDebugTriangle +{ + PxDebugTriangle(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxU32& c) + : pos0(p0), color0(c), pos1(p1), color1(c), pos2(p2), color2(c) {} + + PxVec3 pos0; + PxU32 color0; + PxVec3 pos1; + PxU32 color1; + PxVec3 pos2; + PxU32 color2; +}; + +/** +\brief Used to store a text for debug rendering. Doesn't own 'string' array. +*/ +struct PxDebugText +{ + PxDebugText() : string(0) {} + + PxDebugText(const PxVec3& p, const PxReal& s, const PxU32& c, const char* str) + : position(p), size(s), color(c), string(str) {} + + PxVec3 position; + PxReal size; + PxU32 color; + const char* string; +}; + +/** +\brief Interface for points, lines, triangles, and text buffer. +*/ +class PxRenderBuffer +{ +public: + virtual ~PxRenderBuffer() {} + + virtual PxU32 getNbPoints() const = 0; + virtual const PxDebugPoint* getPoints() const = 0; + + virtual PxU32 getNbLines() const = 0; + virtual const PxDebugLine* getLines() const = 0; + + virtual PxU32 getNbTriangles() const = 0; + virtual const PxDebugTriangle* getTriangles() const = 0; + + virtual PxU32 getNbTexts() const = 0; + virtual const PxDebugText* getTexts() const = 0; + + virtual void append(const PxRenderBuffer& other) = 0; + virtual void clear() = 0; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxSerialFramework.h b/src/PhysX/physx/include/common/PxSerialFramework.h new file mode 100644 index 000000000..ac2565a7b --- /dev/null +++ b/src/PhysX/physx/include/common/PxSerialFramework.h @@ -0,0 +1,406 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_NX_SERIAL_FRAMEWORK +#define PX_PHYSICS_COMMON_NX_SERIAL_FRAMEWORK + +/** \addtogroup common +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +typedef PxU16 PxType; +class PxBase; +class PxSerializationContext; +class PxRepXSerializer; +class PxSerializer; +class PxPhysics; + +//! Default serialization alignment +#define PX_SERIAL_ALIGN 16 + +//! Serialized input data must be aligned to this value +#define PX_SERIAL_FILE_ALIGN 128 + +//! PxSerialObjectId value for objects that do not have an ID +#define PX_SERIAL_OBJECT_ID_INVALID 0 + +//! ID type for PxBase objects in a PxCollection +typedef PxU64 PxSerialObjectId; + +//! Bit to mark pointer type references, @see PxDeserializationContext +#define PX_SERIAL_REF_KIND_PTR_TYPE_BIT (1u<<31) + +//! Reference kind value for PxBase objects +#define PX_SERIAL_REF_KIND_PXBASE (0 | PX_SERIAL_REF_KIND_PTR_TYPE_BIT) + +//! Reference kind value for material indices +#define PX_SERIAL_REF_KIND_MATERIAL_IDX (1) + +//! Used to fix multi-byte characters warning from gcc for situations like: PxU32 foo = 'CCTS'; +#define PX_MAKE_FOURCC(a, b, c, d) ( (a) | ((b)<<8) | ((c)<<16) | ((d)<<24) ) + +/** +\brief Callback class used to process PxBase objects. + +@see PxSerializer::requires +*/ +class PxProcessPxBaseCallback +{ +public: + virtual ~PxProcessPxBaseCallback() {} + virtual void process(PxBase&) = 0; +}; + + +/** +\brief Binary serialization context class. + +This class is used to register reference values and write object +and object extra data during serialization. +It is mainly used by the serialization framework. Except for custom +serializable types, users should not have to worry about it. + +@see PxDeserializationContext +*/ +class PxSerializationContext +{ +public: + + /** + \brief Registers a reference value corresponding to a PxBase object. + + This method is assumed to be called in the implementation of PxSerializer::registerReferences for serialized + references that need to be resolved on deserialization. + + A reference needs to be associated with exactly one PxBase object in either the collection or the + external references collection. + + Different kinds of references are supported and need to be specified. In the most common case + (PX_SERIAL_REF_KIND_PXBASE) the PxBase object matches the reference value (which is the pointer + to the PxBase object). Integer references maybe registered as well (used for internal material + indices with PX_SERIAL_REF_KIND_MATERIAL_IDX). Other kinds could be added with the restriction that + for pointer types the kind value needs to be marked with the PX_SERIAL_REF_KIND_PTR_TYPE_BIT. + + \param[in] base PxBase object associated with the reference + \param[in] kind What kind of reference this is (PX_SERIAL_REF_KIND_PXBASE, PX_SERIAL_REF_KIND_MATERIAL_IDX or custom kind) + \param[in] reference Value of reference + + @see PxDeserializationContext::resolveReference, PX_SERIAL_REF_KIND_PXBASE, PX_SERIAL_REF_KIND_MATERIAL_IDX, PxSerializer::registerReferences + */ + virtual void registerReference(PxBase& base, PxU32 kind, size_t reference) = 0; + + /** + \brief Returns the collection that is being serialized. + */ + virtual const PxCollection& getCollection() const = 0; + + /** + \brief Serializes object data and object extra data. + + This function is assumed to be called within the implementation of PxSerializer::exportData and PxSerializer::exportExtraData. + + @see PxSerializer::exportData, PxSerializer::exportExtraData, PxSerializer::createObject, PxDeserializationContext::readExtraData + */ + virtual void writeData(const void* data, PxU32 size) = 0; + + /** + \brief Aligns the serialized data. + + This function is assumed to be called within the implementation of PxSerializer::exportData and PxSerializer::exportExtraData. + + @see PxSerializer::exportData, PxSerializer::exportExtraData, PxDeserializationContext::alignExtraData + */ + virtual void alignData(PxU32 alignment = PX_SERIAL_ALIGN) = 0; + + /** + \brief Helper function to write a name to the extraData if serialization is configured to save names. + + This function is assumed to be called within the implementation of PxSerializer::exportExtraData. + + @see PxSerialization::serializeCollectionToBinary, PxDeserializationContext::readName + */ + virtual void writeName(const char* name) = 0; + +protected: + + PxSerializationContext() {} + virtual ~PxSerializationContext() {} +}; + + +/** +\brief Binary deserialization context class. + +This class is used to resolve references and access extra data during deserialization. +It is mainly used by the serialization framework. Except for custom +serializable types, users should not have to worry about it. + +@see PxSerializationContext +*/ +class PxDeserializationContext +{ +public: + + /** + \brief Retrieves a pointer to a deserialized PxBase object given a corresponding deserialized reference value + + This method is assumed to be called in the implementation of PxSerializer::createObject in order + to update reference values on deserialization. + + To update a PxBase reference the corresponding deserialized pointer value needs to be provided in order to retrieve + the location of the corresponding deserialized PxBase object. (PxDeserializationContext::translatePxBase simplifies + this common case). + + For other kinds of references the reverence values need to be updated by deduction given the corresponding PxBase instance. + + \param[in] kind What kind of reference this is (PX_SERIAL_REF_KIND_PXBASE, PX_SERIAL_REF_KIND_MATERIAL_IDX or custom kind) + \param[in] reference Deserialized reference value + \return PxBase object associated with the reference value + + @see PxSerializationContext::registerReference, PX_SERIAL_REF_KIND_PXBASE, PX_SERIAL_REF_KIND_MATERIAL_IDX, translatePxBase + */ + virtual PxBase* resolveReference(PxU32 kind, size_t reference) const = 0; + + /** + \brief Helper function to update PxBase pointer on deserialization + + @see resolveReference, PX_SERIAL_REF_KIND_PXBASE + */ + template + void translatePxBase(T*& base) { if (base) { base = static_cast(resolveReference(PX_SERIAL_REF_KIND_PXBASE, size_t(base))); } } + + /** + \brief Helper function to read a name from the extra data during deserialization. + + This function is assumed to be called within the implementation of PxSerializer::createObject. + + @see PxSerializationContext::writeName + */ + PX_INLINE void readName(const char*& name) + { + PxU32 len = *reinterpret_cast(mExtraDataAddress); + mExtraDataAddress += sizeof(len); + name = len ? reinterpret_cast(mExtraDataAddress) : NULL; + mExtraDataAddress += len; + } + + /** + \brief Function to read extra data during deserialization. + + This function is assumed to be called within the implementation of PxSerializer::createObject. + + @see PxSerializationContext::writeData, PxSerializer::createObject + */ + template + PX_INLINE T* readExtraData(PxU32 count=1) + { + T* data = reinterpret_cast(mExtraDataAddress); + mExtraDataAddress += sizeof(T)*count; + return data; + } + + /** + \brief Function to read extra data during deserialization optionally aligning the extra data stream before reading. + + This function is assumed to be called within the implementation of PxSerializer::createObject. + + @see PxSerializationContext::writeData, PxDeserializationContext::alignExtraData, PxSerializer::createObject + */ + template + PX_INLINE T* readExtraData(PxU32 count=1) + { + alignExtraData(alignment); + return readExtraData(count); + } + + /** + \brief Function to align the extra data stream to a power of 2 alignment + + This function is assumed to be called within the implementation of PxSerializer::createObject. + + @see PxSerializationContext::alignData, PxSerializer::createObject + */ + PX_INLINE void alignExtraData(PxU32 alignment = PX_SERIAL_ALIGN) + { + size_t addr = reinterpret_cast(mExtraDataAddress); + addr = (addr+alignment-1)&~size_t(alignment-1); + mExtraDataAddress = reinterpret_cast(addr); + } + + + /** + \brief Function to return the PX_PHYSX_VERSION value with which the data was originally serialized + */ + + virtual PxU32 getPhysXVersion() const = 0; + +protected: + + PxDeserializationContext() {} + virtual ~PxDeserializationContext() {} + + PxU8* mExtraDataAddress; +}; + +/** +\brief Callback type for exporting binary meta data for a serializable type. +@see PxSerializationRegistry::registerBinaryMetaDataCallback + +\param stream Stream to store binary meta data. +*/ +typedef void (*PxBinaryMetaDataCallback)(PxOutputStream& stream); + +/** +\brief Class serving as a registry for XML (RepX) and binary serializable types. + +In order to serialize and deserialize objects the application needs +to maintain an instance of this class. It can be created with +PxSerialization::createSerializationRegistry() and released with +PxSerializationRegistry::release(). + +@see PxSerialization::createSerializationRegistry +*/ +class PxSerializationRegistry +{ +public: + /************************************************************************************************/ + + /** @name Binary Serialization Functionality + */ + //@{ + + /** + \brief Register a serializer for a concrete type + + \param type PxConcreteType corresponding to the serializer + \param serializer The PxSerializer to be registered + + @see PxConcreteType, PxSerializer, PxSerializationRegistry::unregisterSerializer + */ + virtual void registerSerializer(PxType type, PxSerializer& serializer) = 0; + + /** + \brief Unregister a serializer for a concrete type, and retrieves the corresponding serializer object. + + \param type PxConcreteType for which the serializer should be unregistered + \return Unregistered serializer corresponding to type, NULL for types for which no serializer has been registered. + + @see PxConcreteType, PxSerializationRegistry::registerSerializer, PxSerializationRegistry::release + */ + virtual PxSerializer* unregisterSerializer(PxType type) = 0; + + /** + \brief Register binary meta data callback + + The callback is executed when calling PxSerialization::dumpBinaryMetaData. + + \param callback PxBinaryMetaDataCallback to be registered. + + @see PxBinaryMetaDataCallback, PxSerialization::dumpBinaryMetaData + */ + virtual void registerBinaryMetaDataCallback(PxBinaryMetaDataCallback callback) = 0; + + /** + \brief Returns PxSerializer corresponding to type + + \param type PxConcreteType of the serializer requested. + \return Registered PxSerializer object corresponding to type + + @see PxConcreteType + */ + virtual const PxSerializer* getSerializer(PxType type) const = 0; + + //@} + /************************************************************************************************/ + + /** @name RepX (XML) Serialization Functionality + */ + //@{ + + /** + \brief Register a RepX serializer for a concrete type + + \param type PxConcreteType corresponding to the RepX serializer + \param serializer The PxRepXSerializer to be registered + + @see PxConcreteType, PxRepXSerializer + */ + virtual void registerRepXSerializer(PxType type, PxRepXSerializer& serializer) = 0; + + /** + \brief Unregister a RepX serializer for a concrete type, and retrieves the corresponding serializer object. + + \param type PxConcreteType for which the RepX serializer should be unregistered + \return Unregistered PxRepxSerializer corresponding to type, NULL for types for which no RepX serializer has been registered. + + @see PxConcreteType, PxSerializationRegistry::registerRepXSerializer, PxSerializationRegistry::release + */ + virtual PxRepXSerializer* unregisterRepXSerializer(PxType type) = 0; + + /** + \brief Returns RepX serializer given the corresponding type name + + \param typeName Name of the type + \return Registered PxRepXSerializer object corresponding to type name + + @see PxRepXSerializer, PxTypeInfo, PX_DEFINE_TYPEINFO + */ + virtual PxRepXSerializer* getRepXSerializer(const char* typeName) const = 0; + + //@} + /************************************************************************************************/ + + /** + \brief Releases PxSerializationRegistry instance. + + This unregisters all PhysX and PhysXExtension serializers. Make sure to unregister all custom type + serializers before releasing the PxSerializationRegistry. + + @see PxSerializationRegistry::unregisterSerializer, PxSerializationRegistry::unregisterRepXSerializer + */ + virtual void release() = 0; + +protected: + virtual ~PxSerializationRegistry(){} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxSerializer.h b/src/PhysX/physx/include/common/PxSerializer.h new file mode 100644 index 000000000..d3c1327fc --- /dev/null +++ b/src/PhysX/physx/include/common/PxSerializer.h @@ -0,0 +1,257 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_SERIALIZER_H +#define PX_SERIALIZER_H +/** \addtogroup extensions +@{ +*/ + +#include "PxSerialFramework.h" +#include "PxCollection.h" +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** + \brief Serialization interface class. + + PxSerializer is used to extend serializable PxBase classes with serialization functionality. The + interface is structured such that per-class adapter instances can be used as opposed to per-object + adapter instances, avoiding per object allocations. Hence the methods take a reference to PxBase as a parameter. + + The PxSerializer interface needs to be implemented for binary or RepX serialization to work on custom + types. If only RepX serialization is needed, some methods can be left empty, as they are only needed + for binary serialization. + + A default implementation is available as a template adapter (PxSerializerDefaultAdapter). + + @see PxSerializerDefaultAdapter, PX_NEW_SERIALIZER_ADAPTER, PxSerializationRegistry::registerSerializer +*/ +class PxSerializer +{ +public: + + /**********************************************************************************************************************/ + + /** @name Basics needed for Binary- and RepX-Serialization + */ + //@{ + + /** + \brief Returns string name of dynamic type. + + \return Class name of most derived type of this object. + */ + virtual const char* getConcreteTypeName() const = 0; + + /** + \brief Adds required objects to the collection. + + This method does not add the required objects recursively, e.g. objects required by required objects. + + @see PxCollection, PxSerialization::complete + */ + virtual void requiresObjects(PxBase&, PxProcessPxBaseCallback&) const = 0; + + /** + \brief Whether the object is subordinate. + + A class is subordinate, if it can only be instantiated in the context of another class. + + \return Whether the class is subordinate + + @see PxSerialization::isSerializable + */ + virtual bool isSubordinate() const = 0; + + //@} + /**********************************************************************************************************************/ + + /**********************************************************************************************************************/ + + /** @name Functionality needed for Binary Serialization only + */ + //@{ + + /** + \brief Exports object's extra data to stream. + */ + virtual void exportExtraData(PxBase&, PxSerializationContext&) const = 0; + + /** + \brief Exports object's data to stream. + */ + virtual void exportData(PxBase&, PxSerializationContext&) const = 0; + + /** + \brief Register references that the object maintains to other objects. + */ + virtual void registerReferences(PxBase& obj, PxSerializationContext& s) const = 0; + + /** + \brief Returns size needed to create the class instance. + + \return sizeof class instance. + */ + virtual size_t getClassSize() const = 0; + + /** + \brief Create object at a given address, resolve references and import extra data. + + \param address Location at which object is created. Address is increased by the size of the created object. + \param context Context for reading external data and resolving references. + \return Created PxBase pointer (needs to be identical to address before increment). + */ + virtual PxBase* createObject(PxU8*& address, PxDeserializationContext& context) const = 0; + + //@} + /**********************************************************************************************************************/ + virtual ~PxSerializer() {} +}; + + +/** + \brief Default PxSerializer implementation. +*/ +template +class PxSerializerDefaultAdapter : public PxSerializer +{ +public: + + /************************************************************************************************/ + + /** @name Basics needed for Binary- and RepX-Serialization + */ + //@{ + + PxSerializerDefaultAdapter(const char* name) : mTypeName(name){} + + virtual const char* getConcreteTypeName() const + { + return mTypeName; + } + + virtual void requiresObjects(PxBase& obj, PxProcessPxBaseCallback& c) const + { + T& t = static_cast(obj); + t.requiresObjects(c); + } + + virtual bool isSubordinate() const + { + return false; + } + + //@} + /************************************************************************************************/ + + /** @name Functionality needed for Binary Serialization only + */ + //@{ + + // object methods + + virtual void exportExtraData(PxBase& obj, PxSerializationContext& s) const + { + T& t = static_cast(obj); + t.exportExtraData(s); + } + + virtual void exportData(PxBase& obj, PxSerializationContext& s) const + { + s.writeData(&obj, sizeof(T)); + } + + virtual void registerReferences(PxBase& obj, PxSerializationContext& s) const + { + T& t = static_cast(obj); + + s.registerReference(obj, PX_SERIAL_REF_KIND_PXBASE, size_t(&obj)); + + struct RequiresCallback : public PxProcessPxBaseCallback + { + RequiresCallback(PxSerializationContext& c) : context(c) {} + RequiresCallback& operator=(RequiresCallback&) { PX_ASSERT(0); return *this; } + void process(physx::PxBase& base) + { + context.registerReference(base, PX_SERIAL_REF_KIND_PXBASE, size_t(&base)); + } + PxSerializationContext& context; + }; + + RequiresCallback callback(s); + t.requiresObjects(callback); + } + + // class methods + + virtual size_t getClassSize() const + { + return sizeof(T); + } + + virtual PxBase* createObject(PxU8*& address, PxDeserializationContext& context) const + { + return T::createObject(address, context); + } + + + //@} + /************************************************************************************************/ + +private: + const char* mTypeName; +}; + +/** + \brief Preprocessor Macro to simplify adapter creation. + + Note: that the allocator used for creation needs to match with the one used in PX_DELETE_SERIALIZER_ADAPTER. +*/ +#define PX_NEW_SERIALIZER_ADAPTER(x) \ + *new( PxGetFoundation().getAllocatorCallback().allocate(sizeof(PxSerializerDefaultAdapter), \ + "PxSerializerDefaultAdapter", __FILE__, __LINE__ )) PxSerializerDefaultAdapter(#x) + +/** + \brief Preprocessor Macro to simplify adapter deletion. +*/ +#define PX_DELETE_SERIALIZER_ADAPTER(x) \ + { PxSerializer* s = x; if (s) { s->~PxSerializer(); PxGetFoundation().getAllocatorCallback().deallocate(s); } } + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxStringTable.h b/src/PhysX/physx/include/common/PxStringTable.h new file mode 100644 index 000000000..5564045eb --- /dev/null +++ b/src/PhysX/physx/include/common/PxStringTable.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_STRING_TABLE_H +#define PX_STRING_TABLE_H + +#include "foundation/PxPreprocessor.h" + +/** \addtogroup physics +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** + * \brief a table to manage strings. Strings allocated through this object are expected to be owned by this object. + */ +class PxStringTable +{ +protected: + virtual ~PxStringTable(){} +public: + /** + * \brief Allocate a new string. + * + * \param[in] inSrc Source string, null terminated or null. + * + * \return *Always* a valid null terminated string. "" is returned if "" or null is passed in. + */ + virtual const char* allocateStr( const char* inSrc ) = 0; + + /** + * Release the string table and all the strings associated with it. + */ + virtual void release() = 0; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxTolerancesScale.h b/src/PhysX/physx/include/common/PxTolerancesScale.h new file mode 100644 index 000000000..f966de3a4 --- /dev/null +++ b/src/PhysX/physx/include/common/PxTolerancesScale.h @@ -0,0 +1,108 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_SCALE_H +#define PX_SCALE_H + +/** \addtogroup common + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysics; + +/** +\brief Class to define the scale at which simulation runs. Most simulation tolerances are +calculated in terms of the values here. + +\note if you change the simulation scale, you will probablly also wish to change the scene's +default value of gravity, and stable simulation will probably require changes to the scene's +bounceThreshold also. +*/ + +class PxTolerancesScale +{ +public: + + /** brief + The approximate size of objects in the simulation. + + For simulating roughly human-sized in metric units, 1 is a good choice. + If simulation is done in centimetres, use 100 instead. This is used to + estimate certain length-related tolerances. + */ + PxReal length; + + /** brief + The typical magnitude of velocities of objects in simulation. This is used to estimate + whether a contact should be treated as bouncing or resting based on its impact velocity, + and a kinetic energy threshold below which the simulation may put objects to sleep. + + For normal physical environments, a good choice is the approximate speed of an object falling + under gravity for one second. + */ + PxReal speed; + + /** + \brief constructor sets to default + */ + PX_INLINE PxTolerancesScale(); + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid (returns always true). + */ + PX_INLINE bool isValid() const; + +}; + +PX_INLINE PxTolerancesScale::PxTolerancesScale(): + length(1.0f), + speed(10.0f) + { + } + +PX_INLINE bool PxTolerancesScale::isValid() const +{ + return length>0.0f; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/PxTypeInfo.h b/src/PhysX/physx/include/common/PxTypeInfo.h new file mode 100644 index 000000000..5e74ea331 --- /dev/null +++ b/src/PhysX/physx/include/common/PxTypeInfo.h @@ -0,0 +1,124 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_COMMON_PX_TYPEINFO +#define PX_PHYSICS_COMMON_PX_TYPEINFO + +/** \addtogroup common +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief an enumeration of concrete classes inheriting from PxBase + +Enumeration space is reserved for future PhysX core types, PhysXExtensions, +PhysXVehicle and Custom application types. + +@see PxBase, PxTypeInfo +*/ + +struct PxConcreteType +{ + enum Enum + { + eUNDEFINED, + + eHEIGHTFIELD, + eCONVEX_MESH, + eTRIANGLE_MESH_BVH33, + eTRIANGLE_MESH_BVH34, + + eRIGID_DYNAMIC, + eRIGID_STATIC, + eSHAPE, + eMATERIAL, + eCONSTRAINT, + eAGGREGATE, + eARTICULATION, + eARTICULATION_LINK, + eARTICULATION_JOINT, + ePRUNING_STRUCTURE, + eBVH_STRUCTURE, + + ePHYSX_CORE_COUNT, + eFIRST_PHYSX_EXTENSION = 256, + eFIRST_VEHICLE_EXTENSION = 512, + eFIRST_USER_EXTENSION = 1024 + }; +}; + +/** +\brief a structure containing per-type information for types inheriting from PxBase + +@see PxBase, PxConcreteType +*/ + +template struct PxTypeInfo {}; + +#define PX_DEFINE_TYPEINFO(_name, _fastType) \ + class _name; \ + template <> struct PxTypeInfo<_name> { static const char* name() { return #_name; } enum { eFastTypeId = _fastType }; }; + +/* the semantics of the fastType are as follows: an object A can be cast to a type B if B's fastType is defined, and A has the same fastType. + * This implies that B has no concrete subclasses or superclasses. + */ + +PX_DEFINE_TYPEINFO(PxBase, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxMaterial, PxConcreteType::eMATERIAL) +PX_DEFINE_TYPEINFO(PxConvexMesh, PxConcreteType::eCONVEX_MESH) +PX_DEFINE_TYPEINFO(PxTriangleMesh, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxBVH33TriangleMesh, PxConcreteType::eTRIANGLE_MESH_BVH33) +PX_DEFINE_TYPEINFO(PxBVH34TriangleMesh, PxConcreteType::eTRIANGLE_MESH_BVH34) +PX_DEFINE_TYPEINFO(PxHeightField, PxConcreteType::eHEIGHTFIELD) +PX_DEFINE_TYPEINFO(PxActor, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxRigidActor, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxRigidBody, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxRigidDynamic, PxConcreteType::eRIGID_DYNAMIC) +PX_DEFINE_TYPEINFO(PxRigidStatic, PxConcreteType::eRIGID_STATIC) +PX_DEFINE_TYPEINFO(PxArticulationLink, PxConcreteType::eARTICULATION_LINK) +PX_DEFINE_TYPEINFO(PxArticulationJoint, PxConcreteType::eARTICULATION_JOINT) +PX_DEFINE_TYPEINFO(PxArticulation, PxConcreteType::eARTICULATION) +PX_DEFINE_TYPEINFO(PxAggregate, PxConcreteType::eAGGREGATE) +PX_DEFINE_TYPEINFO(PxConstraint, PxConcreteType::eCONSTRAINT) +PX_DEFINE_TYPEINFO(PxShape, PxConcreteType::eSHAPE) +PX_DEFINE_TYPEINFO(PxPruningStructure, PxConcreteType::ePRUNING_STRUCTURE) + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h b/src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h new file mode 100644 index 000000000..7f3e45bae --- /dev/null +++ b/src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h @@ -0,0 +1,102 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_DELAY_LOAD_HOOK +#define PX_PHYSICS_DELAY_LOAD_HOOK + +#include "foundation/PxPreprocessor.h" +#include "common/PxPhysXCommonConfig.h" + +/** \addtogroup foundation +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + /** + \brief PxDelayLoadHook + + This is a helper class for delay loading the PhysXCommon dll and PhysXFoundation dll. + If a PhysXCommon dll or PhysXFoundation dll with a non-default file name needs to be loaded, + PxDelayLoadHook can be sub-classed to provide the custom filenames. + + Once the names are set, the instance must be set for use by PhysX.dll using PxSetPhysXDelayLoadHook(), + PhysXCooking.dll using PxSetPhysXCookingDelayLoadHook() or by PhysXCommon.dll using PxSetPhysXCommonDelayLoadHook(). + + @see PxSetPhysXDelayLoadHook(), PxSetPhysXCookingDelayLoadHook(), PxSetPhysXCommonDelayLoadHook() + */ + class PxDelayLoadHook + { + public: + PxDelayLoadHook() {} + virtual ~PxDelayLoadHook() {} + + virtual const char* getPhysXFoundationDllName() const = 0; + + virtual const char* getPhysXCommonDllName() const = 0; + + protected: + private: + }; + + /** + \brief Sets delay load hook instance for PhysX dll. + + \param[in] hook Delay load hook. + + @see PxDelayLoadHook + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxSetPhysXDelayLoadHook(const physx::PxDelayLoadHook* hook); + + /** + \brief Sets delay load hook instance for PhysXCooking dll. + + \param[in] hook Delay load hook. + + @see PxDelayLoadHook + */ + PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxSetPhysXCookingDelayLoadHook(const physx::PxDelayLoadHook* hook); + + /** + \brief Sets delay load hook instance for PhysXCommon dll. + + \param[in] hook Delay load hook. + + @see PxDelayLoadHook + */ + PX_C_EXPORT PX_PHYSX_COMMON_API void PX_CALL_CONV PxSetPhysXCommonDelayLoadHook(const physx::PxDelayLoadHook* hook); + +#if !PX_DOXYGEN +} // namespace physx +#endif +/** @} */ +#endif diff --git a/src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h b/src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h new file mode 100644 index 000000000..dd0168922 --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_BVH_33_MIDPHASE_DESC_H +#define PX_BVH_33_MIDPHASE_DESC_H +/** \addtogroup cooking +@{ +*/ + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** \brief Enumeration for mesh cooking hints. */ +struct PxMeshCookingHint +{ + enum Enum + { + eSIM_PERFORMANCE = 0, //!< Default value. Favors higher quality hierarchy with higher runtime performance over cooking speed. + eCOOKING_PERFORMANCE = 1 //!< Enables fast cooking path at the expense of somewhat lower quality hierarchy construction. + }; +}; + +/** + +\brief Structure describing parameters affecting BVH33 midphase mesh structure. + +@see PxCookingParams, PxMidphaseDesc +*/ +struct PxBVH33MidphaseDesc +{ + /** + \brief Controls the trade-off between mesh size and runtime performance. + + Using a value of 1.0 will produce a larger cooked mesh with generally higher runtime performance, + using 0.0 will produce a smaller cooked mesh, with generally lower runtime performance. + + Values outside of [0,1] range will be clamped and cause a warning when any mesh gets cooked. + + Default value: 0.55 + Range: [0.0f, 1.0f] + */ + PxF32 meshSizePerformanceTradeOff; + + /** + \brief Mesh cooking hint. Used to specify mesh hierarchy construction preference. + + Default value: PxMeshCookingHint::eSIM_PERFORMANCE + */ + PxMeshCookingHint::Enum meshCookingHint; + + /** + \brief Desc initialization to default value. + */ + void setToDefault() + { + meshSizePerformanceTradeOff = 0.55f; + meshCookingHint = PxMeshCookingHint::eSIM_PERFORMANCE; + } + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + bool isValid() const + { + if(meshSizePerformanceTradeOff < 0.0f || meshSizePerformanceTradeOff > 1.0f) + return false; + return true; + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + + + /** @} */ +#endif // PX_BVH_33_MIDPHASE_DESC_H diff --git a/src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h b/src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h new file mode 100644 index 000000000..aec2fe218 --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_BVH_34_MIDPHASE_DESC_H +#define PX_BVH_34_MIDPHASE_DESC_H +/** \addtogroup cooking +@{ +*/ + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** + +\brief Structure describing parameters affecting BVH34 midphase mesh structure. + +@see PxCookingParams, PxMidphaseDesc +*/ +struct PxBVH34MidphaseDesc +{ + /** + \brief Mesh cooking hint for max primitives per leaf limit. + Less primitives per leaf produces larger meshes with better runtime performance + and worse cooking performance. More triangles per leaf results in faster cooking speed and + smaller mesh sizes, but with worse runtime performance. + + Default value: 4 + Range: <4, 15> + */ + PxU32 numPrimsPerLeaf; + + /** + \brief Desc initialization to default value. + */ + void setToDefault() + { + numPrimsPerLeaf = 4; + } + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + bool isValid() const + { + if(numPrimsPerLeaf < 4 || numPrimsPerLeaf > 15) + return false; + return true; + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + + + /** @} */ +#endif // PX_BVH_34_MIDPHASE_DESC_H diff --git a/src/PhysX/physx/include/cooking/PxBVHStructureDesc.h b/src/PhysX/physx/include/cooking/PxBVHStructureDesc.h new file mode 100644 index 000000000..6c9a8544a --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxBVHStructureDesc.h @@ -0,0 +1,109 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_BVH_STRUCTURE_DESC_H +#define PX_BVH_STRUCTURE_DESC_H +/** \addtogroup cooking +@{ +*/ + +#include "common/PxCoreUtilityTypes.h" +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxTransform.h" +#include "foundation/PxBounds3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** + +\brief Descriptor class for #PxBVHStructure. + +@see PxBVHStructure +*/ +class PxBVHStructureDesc +{ +public: + PX_INLINE PxBVHStructureDesc(); + + /** + \brief Pointer to first bounding box. + */ + PxBoundedData bounds; + + /** + \brief Initialize the BVH structure descriptor + */ + PX_INLINE void setToDefault(); + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + PX_INLINE bool isValid() const; + + +protected: +}; + + + +PX_INLINE PxBVHStructureDesc::PxBVHStructureDesc() +{ +} + +PX_INLINE void PxBVHStructureDesc::setToDefault() +{ + *this = PxBVHStructureDesc(); +} + +PX_INLINE bool PxBVHStructureDesc::isValid() const +{ + // Check BVH desc data + if(!bounds.data) + return false; + if(bounds.stride < sizeof(PxBounds3)) //should be at least one point's worth of data + return false; + + if(bounds.count == 0) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + + + /** @} */ +#endif // PX_BVH_STRUCTURE_DESC_H diff --git a/src/PhysX/physx/include/cooking/PxConvexMeshDesc.h b/src/PhysX/physx/include/cooking/PxConvexMeshDesc.h new file mode 100644 index 000000000..20d0ee1e6 --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxConvexMeshDesc.h @@ -0,0 +1,307 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_NXCONVEXMESHDESC +#define PX_COLLISION_NXCONVEXMESHDESC +/** \addtogroup cooking +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxFlags.h" +#include "common/PxCoreUtilityTypes.h" +#include "geometry/PxConvexMesh.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Flags which describe the format and behavior of a convex mesh. +*/ +struct PxConvexFlag +{ + enum Enum + { + /** + Denotes the use of 16-bit vertex indices in PxConvexMeshDesc::triangles or PxConvexMeshDesc::polygons. + (otherwise, 32-bit indices are assumed) + @see #PxConvexMeshDesc.indices + */ + e16_BIT_INDICES = (1<<0), + + /** + Automatically recomputes the hull from the vertices. If this flag is not set, you must provide the entire geometry manually. + + \note There are two different algorithms for hull computation, please see PxConvexMeshCookingType. + + @see PxConvexMeshCookingType + */ + eCOMPUTE_CONVEX = (1<<1), + + /** + \brief Checks and removes almost zero-area triangles during convex hull computation. + The rejected area size is specified in PxCookingParams::areaTestEpsilon + + \note This flag is only used in combination with eCOMPUTE_CONVEX. + + @see PxCookingParams PxCookingParams::areaTestEpsilon + */ + eCHECK_ZERO_AREA_TRIANGLES = (1<<2), + + /** + \brief Quantizes the input vertices using the k-means clustering + + \note The input vertices are quantized to PxConvexMeshDesc::quantizedCount + see http://en.wikipedia.org/wiki/K-means_clustering + + */ + eQUANTIZE_INPUT = (1 << 3), + + /** + \brief Disables the convex mesh validation to speed-up hull creation. Please use separate validation + function in checked/debug builds. Creating a convex mesh with invalid input data without prior validation + may result in undefined behavior. + + @see PxCooking::validateConvexMesh + */ + eDISABLE_MESH_VALIDATION = (1 << 4), + + /** + \brief Enables plane shifting vertex limit algorithm. + + Plane shifting is an alternative algorithm for the case when the computed hull has more vertices + than the specified vertex limit. + + The default algorithm computes the full hull, and an OBB around the input vertices. This OBB is then sliced + with the hull planes until the vertex limit is reached.The default algorithm requires the vertex limit + to be set to at least 8, and typically produces results that are much better quality than are produced + by plane shifting. + + When plane shifting is enabled, the hull computation stops when vertex limit is reached. The hull planes + are then shifted to contain all input vertices, and the new plane intersection points are then used to + generate the final hull with the given vertex limit.Plane shifting may produce sharp edges to vertices + very far away from the input cloud, and does not guarantee that all input vertices are inside the resulting + hull.However, it can be used with a vertex limit as low as 4. + */ + ePLANE_SHIFTING = (1 << 5), + + /** + \brief Inertia tensor computation is faster using SIMD code, but the precision is lower, which may result + in incorrect inertia for very thin hulls. + */ + eFAST_INERTIA_COMPUTATION = (1 << 6), + + /** + \brief Convex hulls are created with respect to GPU simulation limitations. Vertex limit is set to 64 and + vertex limit per face is internally set to 32. + \note Can be used only with eCOMPUTE_CONVEX flag. + */ + eGPU_COMPATIBLE = (1 << 7), + + /** + \brief Convex hull input vertices are shifted to be around origin to provide better computation stability. + It is recommended to provide input vertices around the origin, otherwise use this flag to improve + numerical stability. + \note Is used only with eCOMPUTE_CONVEX flag. + */ + eSHIFT_VERTICES = (1 << 8) + }; +}; + +/** +\brief collection of set bits defined in PxConvexFlag. + +@see PxConvexFlag +*/ +typedef PxFlags PxConvexFlags; +PX_FLAGS_OPERATORS(PxConvexFlag::Enum,PxU16) + +/** +\brief Descriptor class for #PxConvexMesh. +\note The number of vertices and the number of convex polygons in a cooked convex mesh is limited to 256. + +@see PxConvexMesh PxConvexMeshGeometry PxShape PxPhysics.createConvexMesh() + +*/ +class PxConvexMeshDesc +{ +public: + + /** + \brief Vertex positions data in PxBoundedData format. + + Default: NULL + */ + PxBoundedData points; + + /** + \brief Polygons data in PxBoundedData format. +

            Pointer to first polygon.

            + + Default: NULL + + @see PxHullPolygon + */ + PxBoundedData polygons; + + /** + \brief Polygon indices data in PxBoundedData format. +

            Pointer to first index.

            + + Default: NULL + +

            This is declared as a void pointer because it is actually either an PxU16 or a PxU32 pointer.

            + + @see PxHullPolygon PxConvexFlag::e16_BIT_INDICES + */ + PxBoundedData indices; + + /** + \brief Flags bits, combined from values of the enum ::PxConvexFlag + + Default: 0 + */ + PxConvexFlags flags; + + /** + \brief Limits the number of vertices of the result convex mesh. Hard maximum limit is 256 + and minimum limit is 4 if PxConvexFlag::ePLANE_SHIFTING is used, otherwise the minimum + limit is 8. + + \note Vertex limit is only used when PxConvexFlag::eCOMPUTE_CONVEX is specified. + \note The please see PxConvexFlag::ePLANE_SHIFTING for algorithm explanation + + @see PxConvexFlag::ePLANE_SHIFTING + + Range: [4, 255]
            + Default: 255 + */ + PxU16 vertexLimit; + + /** + \brief Maximum number of vertices after quantization. The quantization is done during the vertex cleaning phase. + The quantization is applied when PxConvexFlag::eQUANTIZE_INPUT is specified. + + @see PxConvexFlag::eQUANTIZE_INPUT + + Range: [4, 65535]
            + Default: 255 + */ + PxU16 quantizedCount; + + /** + \brief constructor sets to default. + */ + PX_INLINE PxConvexMeshDesc(); + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE void setToDefault(); + /** + \brief Returns true if the descriptor is valid. + + \return True if the current settings are valid + */ + PX_INLINE bool isValid() const; +}; + +PX_INLINE PxConvexMeshDesc::PxConvexMeshDesc() //constructor sets to default +: vertexLimit(255), quantizedCount(255) +{ +} + +PX_INLINE void PxConvexMeshDesc::setToDefault() +{ + *this = PxConvexMeshDesc(); +} + +PX_INLINE bool PxConvexMeshDesc::isValid() const +{ + // Check geometry + if(points.count < 3 || //at least 1 trig's worth of points + (points.count > 0xffff && flags & PxConvexFlag::e16_BIT_INDICES)) + return false; + if(!points.data) + return false; + if(points.stride < sizeof(PxVec3)) //should be at least one point's worth of data + return false; + if (quantizedCount < 4) + return false; + + // Check topology + if(polygons.data) + { + if(polygons.count < 4) // we require 2 neighbors for each vertex - 4 polygons at least + return false; + + if(!indices.data) // indices must be provided together with polygons + return false; + + PxU32 limit = (flags & PxConvexFlag::e16_BIT_INDICES) ? sizeof(PxU16) : sizeof(PxU32); + if(indices.stride < limit) + return false; + + limit = sizeof(PxHullPolygon); + if(polygons.stride < limit) + return false; + } + else + { + // We can compute the hull from the vertices + if(!(flags & PxConvexFlag::eCOMPUTE_CONVEX)) + return false; // If the mesh is convex and we're not allowed to compute the hull, + // you have to provide it completely (geometry & topology). + } + + if((flags & PxConvexFlag::ePLANE_SHIFTING) && vertexLimit < 4) + { + return false; + } + + if (!(flags & PxConvexFlag::ePLANE_SHIFTING) && vertexLimit < 8) + { + return false; + } + + if(vertexLimit > 256) + { + return false; + } + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/cooking/PxCooking.h b/src/PhysX/physx/include/cooking/PxCooking.h new file mode 100644 index 000000000..f1a8a257e --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxCooking.h @@ -0,0 +1,563 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COOKING_H +#define PX_COOKING_H +/** \addtogroup cooking +@{ +*/ +#include "common/PxPhysXCommonConfig.h" +#include "common/PxTolerancesScale.h" +#include "cooking/Pxc.h" + +#include "cooking/PxConvexMeshDesc.h" +#include "cooking/PxTriangleMeshDesc.h" +#include "cooking/PxMidphaseDesc.h" +#include "cooking/PxBVHStructureDesc.h" +#include "geometry/PxTriangleMesh.h" +#include "geometry/PxBVHStructure.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysicsInsertionCallback; +class PxFoundation; + +/** +\brief Result from convex cooking. +*/ +struct PxConvexMeshCookingResult +{ + enum Enum + { + /** + \brief Convex mesh cooking succeeded. + */ + eSUCCESS, + + /** + \brief Convex mesh cooking failed, algorithm couldn't find 4 initial vertices without a small triangle. + + @see PxCookingParams::areaTestEpsilon PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES + */ + eZERO_AREA_TEST_FAILED, + + /** + \brief Convex mesh cooking succeeded, but the algorithm has reached the 255 polygons limit. + The produced hull does not contain all input vertices. Try to simplify the input vertices + or try to use the eINFLATE_CONVEX or the eQUANTIZE_INPUT flags. + + @see PxConvexFlag::eINFLATE_CONVEX PxConvexFlag::eQUANTIZE_INPUT + */ + ePOLYGONS_LIMIT_REACHED, + + /** + \brief Something unrecoverable happened. Check the error stream to find out what. + */ + eFAILURE + }; +}; + +/** \brief Enumeration for convex mesh cooking algorithms. */ +struct PxConvexMeshCookingType +{ + enum Enum + { + /** + \brief The Quickhull algorithm constructs the hull from the given input points. The resulting hull + will only contain a subset of the input points. + + */ + eQUICKHULL + }; +}; + +/** +\brief Result from triangle mesh cooking +*/ +struct PxTriangleMeshCookingResult +{ + enum Enum + { + /** + \brief Everything is A-OK. + */ + eSUCCESS = 0, + + /** + \brief a triangle is too large for well-conditioned results. Tessellate the mesh for better behavior, see the user guide section on cooking for more details. + */ + eLARGE_TRIANGLE, + + /** + \brief Something unrecoverable happened. Check the error stream to find out what. + */ + eFAILURE + }; +}; + +/** + +\brief Enum for the set of mesh pre-processing parameters. + +*/ + +struct PxMeshPreprocessingFlag +{ + enum Enum + { + /** + \brief When set, mesh welding is performed. See PxCookingParams::meshWeldTolerance. Clean mesh must be enabled. + */ + eWELD_VERTICES = 1 << 0, + + /** + \brief When set, mesh cleaning is disabled. This makes cooking faster. + + When clean mesh is not performed, mesh welding is also not performed. + + It is recommended to use only meshes that passed during validateTriangleMesh. + + */ + eDISABLE_CLEAN_MESH = 1 << 1, + + /** + \brief When set, active edges are set for each triangle edge. This makes cooking faster but slow up contact generation. + */ + eDISABLE_ACTIVE_EDGES_PRECOMPUTE = 1 << 2, + + /** + \brief When set, 32-bit indices will always be created regardless of triangle count. + + \note By default mesh will be created with 16-bit indices for triangle count <= 0xFFFF and 32-bit otherwise. + */ + eFORCE_32BIT_INDICES = 1 << 3 + }; +}; + +typedef PxFlags PxMeshPreprocessingFlags; + +/** + +\brief Structure describing parameters affecting mesh cooking. + +@see PxSetCookingParams() PxGetCookingParams() +*/ +struct PxCookingParams +{ + /** + \brief Zero-size area epsilon used in convex hull computation. + + If the area of a triangle of the hull is below this value, the triangle will be rejected. This test + is done only if PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES is used. + + @see PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES + + Default value: 0.06f*PxTolerancesScale.length*PxTolerancesScale.length + + Range: (0.0f, PX_MAX_F32) + */ + float areaTestEpsilon; + + /** + \brief Plane tolerance used in convex hull computation. + + The value is used during hull construction. When a new point is about to be added to the hull it + gets dropped when the point is closer to the hull than the planeTolerance. The planeTolerance + is increased according to the hull size. + + If 0.0f is set all points are accepted when the convex hull is created. This may lead to edge cases + where the new points may be merged into an existing polygon and the polygons plane equation might + slightly change therefore. This might lead to failures during polygon merging phase in the hull computation. + + It is recommended to use the default value, however if it is required that all points needs to be + accepted or huge thin convexes are created, it might be required to lower the default value. + + \note The plane tolerance is used only within PxConvexMeshCookingType::eQUICKHULL algorithm. + + Default value: 0.0007f + + Range: <0.0f, PX_MAX_F32) + */ + float planeTolerance; + + /** + \brief Convex hull creation algorithm. + + Default value: PxConvexMeshCookingType::eQUICKHULL + + @see PxConvexMeshCookingType + */ + PxConvexMeshCookingType::Enum convexMeshCookingType; + + /** + \brief When true, the face remap table is not created. This saves a significant amount of memory, but the SDK will + not be able to provide the remap information for internal mesh triangles returned by collisions, + sweeps or raycasts hits. + + Default value: false + */ + bool suppressTriangleMeshRemapTable; + + /** + \brief When true, the triangle adjacency information is created. You can get the adjacency triangles + for a given triangle from getTriangle. + + Default value: false + */ + bool buildTriangleAdjacencies; + + /** + \brief When true, addigional information required for GPU-accelerated rigid body simulation is created. This can increase memory usage and cooking times for convex meshes and triangle meshes. + + Default value: false + */ + bool buildGPUData; + + /** + \brief Tolerance scale is used to check if cooked triangles are not too huge. This check will help with simulation stability. + + \note The PxTolerancesScale values have to match the values used when creating a PxPhysics or PxScene instance. + + @see PxTolerancesScale + */ + PxTolerancesScale scale; + + /** + \brief Mesh pre-processing parameters. Used to control options like whether the mesh cooking performs vertex welding before cooking. + + Default value: 0 + */ + PxMeshPreprocessingFlags meshPreprocessParams; + + /** + \brief Mesh weld tolerance. If mesh welding is enabled, this controls the distance at which vertices are welded. + If mesh welding is not enabled, this value defines the acceptance distance for mesh validation. Provided no two vertices are within this distance, the mesh is considered to be + clean. If not, a warning will be emitted. Having a clean, welded mesh is required to achieve the best possible performance. + + The default vertex welding uses a snap-to-grid approach. This approach effectively truncates each vertex to integer values using meshWeldTolerance. + Once these snapped vertices are produced, all vertices that snap to a given vertex on the grid are remapped to reference a single vertex. Following this, + all triangles' indices are remapped to reference this subset of clean vertices. It should be noted that the vertices that we do not alter the + position of the vertices; the snap-to-grid is only performed to identify nearby vertices. + + The mesh validation approach also uses the same snap-to-grid approach to identify nearby vertices. If more than one vertex snaps to a given grid coordinate, + we ensure that the distance between the vertices is at least meshWeldTolerance. If this is not the case, a warning is emitted. + + Default value: 0.0 + */ + PxReal meshWeldTolerance; + + /** + \brief Controls the desired midphase desc structure for triangle meshes. + + @see PxBVH33MidphaseDesc, PxBVH34MidphaseDesc, PxMidphaseDesc + + Default value: PxMeshMidPhase::eBVH33 + */ + PxMidphaseDesc midphaseDesc; + + /** + \brief Vertex limit beyond which additional acceleration structures are computed for each convex mesh. Increase that limit to reduce memory usage. + Computing the extra structures all the time does not guarantee optimal performance. There is a per-platform break-even point below which the + extra structures actually hurt performance. + + Default value: 32 + */ + PxU32 gaussMapLimit; + + PxCookingParams(const PxTolerancesScale& sc): + areaTestEpsilon (0.06f*sc.length*sc.length), + planeTolerance (0.0007f), + convexMeshCookingType (PxConvexMeshCookingType::eQUICKHULL), + suppressTriangleMeshRemapTable (false), + buildTriangleAdjacencies (false), + buildGPUData (false), + scale (sc), + meshPreprocessParams (0), + meshWeldTolerance (0.f), + gaussMapLimit (32) + { + } +}; + +class PxCooking +{ +public: + /** + \brief Closes this instance of the interface. + + This function should be called to cleanly shut down the Cooking library before application exit. + + \note This function is required to be called to release foundation usage. + + */ + virtual void release() = 0; + + /** + \brief Sets cooking parameters + + \param[in] params Cooking parameters + + @see getParams() + */ + virtual void setParams(const PxCookingParams& params) = 0; + + /** + \brief Gets cooking parameters + + \return Current cooking parameters. + + @see PxCookingParams setParams() + */ + virtual const PxCookingParams& getParams() const = 0; + + /** + \brief Checks endianness is the same between cooking & target platforms + + \return True if there is and endian mismatch. + */ + virtual bool platformMismatch() const = 0; + + /** + \brief Cooks a triangle mesh. The results are written to the stream. + + To create a triangle mesh object it is necessary to first 'cook' the mesh data into + a form which allows the SDK to perform efficient collision detection. + + cookTriangleMesh() allows a mesh description to be cooked into a binary stream + suitable for loading and performing collision detection at runtime. + + \param[in] desc The triangle mesh descriptor to read the mesh from. + \param[in] stream User stream to output the cooked data. + \param[out] condition Result from triangle mesh cooking. + \return true on success + + @see cookConvexMesh() setParams() PxPhysics.createTriangleMesh() PxTriangleMeshCookingResult::Enum + */ + virtual bool cookTriangleMesh(const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition = NULL) const = 0; + + + /** + \brief Cooks and creates a triangle mesh and inserts it into PxPhysics. + + \note PxPhysicsInsertionCallback can be obtained through PxPhysics::getPhysicsInsertionCallback(). + + \param[in] desc The triangle mesh descriptor to read the mesh from. + \param[in] insertionCallback The insertion interface from PxPhysics. + \param[out] condition Result from triangle mesh cooking. + \return PxTriangleMesh pointer on success. + + @see cookTriangleMesh() setParams() PxPhysics.createTriangleMesh() PxPhysicsInsertionCallback + */ + virtual PxTriangleMesh* createTriangleMesh(const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition = NULL) const = 0; + + /** + \brief Verifies if the triangle mesh is valid. Prints an error message for each inconsistency found. + + The following conditions are true for a valid triangle mesh: + 1. There are no duplicate vertices (within specified vertexWeldTolerance. See PxCookingParams::meshWeldTolerance) + 2. There are no large triangles (within specified PxTolerancesScale.) + + \param[in] desc The triangle mesh descriptor to read the mesh from. + + \return true if all the validity conditions hold, false otherwise. + + @see cookTriangleMesh() + */ + virtual bool validateTriangleMesh(const PxTriangleMeshDesc& desc) const = 0; + + + /** + \brief Cooks a convex mesh. The results are written to the stream. + + To create a triangle mesh object it is necessary to first 'cook' the mesh data into + a form which allows the SDK to perform efficient collision detection. + + cookConvexMesh() allows a mesh description to be cooked into a binary stream + suitable for loading and performing collision detection at runtime. + + \note The number of vertices and the number of convex polygons in a cooked convex mesh is limited to 255. + \note If those limits are exceeded in either the user-provided data or the final cooked mesh, an error is reported. + + \param[in] desc The convex mesh descriptor to read the mesh from. + \param[in] stream User stream to output the cooked data. + \param[out] condition Result from convex mesh cooking. + \return true on success. + + @see cookTriangleMesh() setParams() PxConvexMeshCookingResult::Enum + */ + virtual bool cookConvexMesh(const PxConvexMeshDesc& desc, PxOutputStream& stream, PxConvexMeshCookingResult::Enum* condition = NULL) const = 0; + + /** + \brief Cooks and creates a convex mesh and inserts it into PxPhysics. + + \note This method does the same as cookConvexMesh, but the produced convex mesh is not stored + into a stream but is directly inserted in PxPhysics. Use this method if you are unable to cook offline. + + \note PxPhysicsInsertionCallback can be obtained through PxPhysics::getPhysicsInsertionCallback(). + + \param[in] desc The convex mesh descriptor to read the mesh from. + \param[in] insertionCallback The insertion interface from PxPhysics. + \param[out] condition Result from convex mesh cooking. + \return PxConvexMesh pointer on success + + @see cookConvexMesh() setParams() PxPhysicsInsertionCallback + */ + virtual PxConvexMesh* createConvexMesh(const PxConvexMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxConvexMeshCookingResult::Enum* condition = NULL) const = 0; + + /** + \brief Verifies if the convex mesh is valid. Prints an error message for each inconsistency found. + + The convex mesh descriptor must contain an already created convex mesh - the vertices, indices and polygons must be provided. + + \note This function should be used if PxConvexFlag::eDISABLE_MESH_VALIDATION is planned to be used in release builds. + + \param[in] desc The convex mesh descriptor to read the mesh from. + + \return true if all the validity conditions hold, false otherwise. + + @see cookConvexMesh() + */ + virtual bool validateConvexMesh(const PxConvexMeshDesc& desc) const = 0; + + + /** + \brief Computed hull polygons from given vertices and triangles. Polygons are needed for PxConvexMeshDesc rather than triangles. + + Please note that the resulting polygons may have different number of vertices. Some vertices may be removed. + The output vertices, indices and polygons must be used to construct a hull. + + The provided PxAllocatorCallback does allocate the out array's. It is the user responsibility to deallocated those + array's. + + \param[in] mesh Simple triangle mesh containing vertices and triangles used to compute polygons. + \param[in] inCallback Memory allocator for out array allocations. + \param[out] nbVerts Number of vertices used by polygons. + \param[out] vertices Vertices array used by polygons. + \param[out] nbIndices Number of indices used by polygons. + \param[out] indices Indices array used by polygons. + \param[out] nbPolygons Number of created polygons. + \param[out] hullPolygons Polygons array. + \return true on success + + @see cookConvexMesh() PxConvexFlags PxConvexMeshDesc PxSimpleTriangleMesh + */ + virtual bool computeHullPolygons(const PxSimpleTriangleMesh& mesh, PxAllocatorCallback& inCallback, PxU32& nbVerts, PxVec3*& vertices, + PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& hullPolygons) const = 0; + + /** + \brief Cooks a heightfield. The results are written to the stream. + + To create a heightfield object there is an option to precompute some of calculations done while loading the heightfield data. + + cookHeightField() allows a heightfield description to be cooked into a binary stream + suitable for loading and performing collision detection at runtime. + + \param[in] desc The heightfield descriptor to read the HF from. + \param[in] stream User stream to output the cooked data. + \return true on success + + @see PxPhysics.createHeightField() + */ + virtual bool cookHeightField(const PxHeightFieldDesc& desc, PxOutputStream& stream) const = 0; + + /** + \brief Cooks and creates a heightfield mesh and inserts it into PxPhysics. + + \param[in] desc The heightfield descriptor to read the HF from. + \param[in] insertionCallback The insertion interface from PxPhysics. + \return PxHeightField pointer on success + + @see cookConvexMesh() setParams() PxPhysics.createTriangleMesh() PxPhysicsInsertionCallback + */ + virtual PxHeightField* createHeightField(const PxHeightFieldDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const = 0; + + /** + \brief Cooks a bounding volume hierarchy structure. The results are written to the stream. + + cookBVHStructure() allows a BVH structure description to be cooked into a binary stream + suitable for loading and performing BVH detection at runtime. + + \param[in] desc The BVH structure descriptor. + \param[in] stream User stream to output the cooked data. + \return true on success. + + @see PxBVHStructure PxRigidActorExt::getRigidActorShapeLocalBoundsList + */ + virtual bool cookBVHStructure(const PxBVHStructureDesc& desc, PxOutputStream& stream) const = 0; + + /** + \brief Cooks and creates a bounding volume hierarchy structure and inserts it into PxPhysics. + + \note This method does the same as cookBVHStructure, but the produced BVH structure is not stored + into a stream but is directly inserted in PxPhysics. Use this method if you are unable to cook offline. + + \note PxPhysicsInsertionCallback can be obtained through PxPhysics::getPhysicsInsertionCallback(). + + \param[in] desc The BVH structure descriptor. + \param[in] insertionCallback The insertion interface from PxPhysics. + \return PxBVHStructure pointer on success + + @see cookBVHStructure() PxPhysicsInsertionCallback + */ + virtual PxBVHStructure* createBVHStructure(const PxBVHStructureDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const = 0; +protected: + virtual ~PxCooking(){} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** +\brief Create an instance of the cooking interface. + +Note that the foundation object is handled as an application-wide singleton in statically linked executables +and a DLL-wide singleton in dynamically linked executables. Therefore, if you are using the runtime SDK in the +same executable as cooking, you should pass the Physics's copy of foundation (acquired with +PxPhysics::getFoundation()) to the cooker. This will also ensure correct handling of memory for objects +passed from the cooker to the SDK. + +To use cooking in standalone mode, create an instance of the Foundation object with PxCreateCookingFoundation. +You should pass the same foundation object to all instances of the cooking interface. + +\param[in] version the SDK version number +\param[in] foundation the foundation object associated with this instance of the cooking interface. +\param[in] params the parameters for this instance of the cooking interface +\return true on success. +*/ +PX_C_EXPORT PX_PHYSX_COOKING_API physx::PxCooking* PX_CALL_CONV PxCreateCooking(physx::PxU32 version, + physx::PxFoundation& foundation, + const physx::PxCookingParams& params); + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/cooking/PxMidphaseDesc.h b/src/PhysX/physx/include/cooking/PxMidphaseDesc.h new file mode 100644 index 000000000..731926451 --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxMidphaseDesc.h @@ -0,0 +1,119 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_MIDPHASE_DESC_H +#define PX_MIDPHASE_DESC_H +/** \addtogroup cooking +@{ +*/ + +#include "geometry/PxTriangleMesh.h" +#include "cooking/PxBVH33MidphaseDesc.h" +#include "cooking/PxBVH34MidphaseDesc.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** + +\brief Structure describing parameters affecting midphase mesh structure. + +@see PxCookingParams, PxBVH33MidphaseDesc, PxBVH34MidphaseDesc +*/ +class PxMidphaseDesc +{ +public: + PX_FORCE_INLINE PxMidphaseDesc() { setToDefault(PxMeshMidPhase::eBVH33); } + + /** + \brief Returns type of midphase mesh structure. + \return PxMeshMidPhase::Enum + + @see PxMeshMidPhase::Enum + */ + PX_FORCE_INLINE PxMeshMidPhase::Enum getType() const { return mType; } + + /** + \brief Midphase descriptors union + + @see PxBV33MidphaseDesc, PxBV34MidphaseDesc + */ + union { + PxBVH33MidphaseDesc mBVH33Desc; + PxBVH34MidphaseDesc mBVH34Desc; + }; + + /** + \brief Initialize the midphase mesh structure descriptor + \param[in] type Midphase mesh structure descriptor + + @see PxBV33MidphaseDesc, PxBV34MidphaseDesc + */ + void setToDefault(PxMeshMidPhase::Enum type) + { + mType = type; + if(type==PxMeshMidPhase::eBVH33) + mBVH33Desc.setToDefault(); + else if(type==PxMeshMidPhase::eBVH34) + mBVH34Desc.setToDefault(); + } + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid. + */ + bool isValid() const + { + if(mType==PxMeshMidPhase::eBVH33) + return mBVH33Desc.isValid(); + else if(mType==PxMeshMidPhase::eBVH34) + return mBVH34Desc.isValid(); + return false; + } + + PX_FORCE_INLINE PxMidphaseDesc& operator=(PxMeshMidPhase::Enum descType) + { + setToDefault(descType); + return *this; + } + +protected: + PxMeshMidPhase::Enum mType; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + + + /** @} */ +#endif // PX_MIDPHASE_DESC_UNION_H diff --git a/src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h b/src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h new file mode 100644 index 000000000..9aa422fc3 --- /dev/null +++ b/src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_NXTRIANGLEMESHDESC +#define PX_COLLISION_NXTRIANGLEMESHDESC +/** \addtogroup cooking +@{ +*/ + +#include "PxPhysXConfig.h" +#include "geometry/PxSimpleTriangleMesh.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Descriptor class for #PxTriangleMesh. + +Note that this class is derived from PxSimpleTriangleMesh which contains the members that describe the basic mesh. +The mesh data is *copied* when an PxTriangleMesh object is created from this descriptor. After the call the +user may discard the triangle data. + +@see PxTriangleMesh PxTriangleMeshGeometry PxShape +*/ +class PxTriangleMeshDesc : public PxSimpleTriangleMesh +{ +public: + + /** + Optional pointer to first material index, or NULL. There are PxSimpleTriangleMesh::numTriangles indices in total. + Caller may add materialIndexStride bytes to the pointer to access the next triangle. + + When a triangle mesh collides with another object, a material is required at the collision point. + If materialIndices is NULL, then the material of the PxShape instance is used. + Otherwise, if the point of contact is on a triangle with index i, then the material index is determined as: + PxMaterialTableIndex index = *(PxMaterialTableIndex *)(((PxU8*)materialIndices) + materialIndexStride * i); + + If the contact point falls on a vertex or an edge, a triangle adjacent to the vertex or edge is selected, and its index + used to look up a material. The selection is arbitrary but consistent over time. + + Default: NULL + + @see materialIndexStride + */ + PxTypedStridedData materialIndices; + + /** + \brief Constructor sets to default. + */ + PX_INLINE PxTriangleMeshDesc(); + + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE void setToDefault(); + + /** + \brief Returns true if the descriptor is valid. + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const; +}; + +PX_INLINE PxTriangleMeshDesc::PxTriangleMeshDesc() //constructor sets to default +{ + PxSimpleTriangleMesh::setToDefault(); +} + +PX_INLINE void PxTriangleMeshDesc::setToDefault() +{ + *this = PxTriangleMeshDesc(); +} + +PX_INLINE bool PxTriangleMeshDesc::isValid() const +{ + if(points.count < 3) //at least 1 trig's worth of points + return false; + if ((!triangles.data) && (points.count%3)) // Non-indexed mesh => we must ensure the geometry defines an implicit number of triangles // i.e. numVertices can't be divided by 3 + return false; + //add more validity checks here + if (materialIndices.data && materialIndices.stride < sizeof(PxMaterialTableIndex)) + return false; + return PxSimpleTriangleMesh::isValid(); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/cooking/Pxc.h b/src/PhysX/physx/include/cooking/Pxc.h new file mode 100644 index 000000000..a279db6cd --- /dev/null +++ b/src/PhysX/physx/include/cooking/Pxc.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COOKING_NX +#define PX_COOKING_NX + +#include "foundation/Px.h" + +// define API function declaration +#if !defined PX_PHYSX_STATIC_LIB + #if PX_WINDOWS + #if defined PX_PHYSX_COOKING_EXPORTS + #define PX_PHYSX_COOKING_API __declspec(dllexport) + #else + #define PX_PHYSX_COOKING_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_PHYSX_COOKING_API PX_UNIX_EXPORT + #endif +#endif + +#if !defined(PX_PHYSX_COOKING_API) + #define PX_PHYSX_COOKING_API +#endif + +#ifndef PX_C_EXPORT + #define PX_C_EXPORT extern "C" +#endif + +#endif diff --git a/src/PhysX/physx/include/cudamanager/PxCudaContextManager.h b/src/PhysX/physx/include/cudamanager/PxCudaContextManager.h new file mode 100644 index 000000000..1bcaf2409 --- /dev/null +++ b/src/PhysX/physx/include/cudamanager/PxCudaContextManager.h @@ -0,0 +1,425 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef PXCUDACONTEXTMANAGER_PXCUDACONTEXTMANAGER_H +#define PXCUDACONTEXTMANAGER_PXCUDACONTEXTMANAGER_H + +#include "foundation/PxPreprocessor.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxErrorCallback.h" +#include "foundation/PxFlags.h" +#include "task/PxTaskDefine.h" +#include "cudamanager/PxCudaMemoryManager.h" + +/* Forward decl to avoid inclusion of cuda.h */ +typedef struct CUctx_st *CUcontext; +typedef struct CUgraphicsResource_st *CUgraphicsResource; +typedef int CUdevice; + +namespace physx +{ + +class PxGpuDispatcher; + + +/** \brief Possible graphic/CUDA interoperability modes for context */ +struct PxCudaInteropMode +{ + /** + * \brief Possible graphic/CUDA interoperability modes for context + */ + enum Enum + { + NO_INTEROP = 0, + D3D10_INTEROP, + D3D11_INTEROP, + OGL_INTEROP, + + COUNT + }; +}; + +struct PxCudaInteropRegisterFlag +{ + enum Enum + { + eNONE = 0x00, + eREAD_ONLY = 0x01, + eWRITE_DISCARD = 0x02, + eSURFACE_LDST = 0x04, + eTEXTURE_GATHER = 0x08 + }; +}; + +/** +\brief collection of set bits defined in NxCudaInteropRegisterFlag. + +@see NxCudaInteropRegisterFlag +*/ +typedef PxFlags PxCudaInteropRegisterFlags; +PX_FLAGS_OPERATORS(PxCudaInteropRegisterFlag::Enum, uint32_t) + +//! \brief Descriptor used to create a PxCudaContextManager +class PxCudaContextManagerDesc +{ +public: + /** + * \brief The CUDA context to manage + * + * If left NULL, the PxCudaContextManager will create a new context. If + * graphicsDevice is also not NULL, this new CUDA context will be bound to + * that graphics device, enabling the use of CUDA/Graphics interop features. + * + * If ctx is not NULL, the specified context must be applied to the thread + * that is allocating the PxCudaContextManager at creation time (aka, it + * cannot be popped). The PxCudaContextManager will take ownership of the + * context until the manager is released. All access to the context must be + * gated by lock acquisition. + * + * If the user provides a context for the PxCudaContextManager, the context + * _must_ have either been created on the GPU ordinal returned by + * PxGetSuggestedCudaDeviceOrdinal() or on your graphics device. + * + * It is perfectly acceptable to allocate device or host pinned memory from + * the context outside the scope of the PxCudaMemoryManager, so long as you + * manage its eventual cleanup. + */ + CUcontext *ctx; + + /** + * \brief D3D device pointer or OpenGl context handle + * + * Only applicable when ctx is NULL, thus forcing a new context to be + * created. In that case, the created context will be bound to this + * graphics device. + */ + void *graphicsDevice; + +#if PX_SUPPORT_GPU_PHYSX + /** + * \brief Application-specific GUID + * + * If your application employs PhysX modules that use CUDA you need to use a GUID + * so that patches for new architectures can be released for your game.You can obtain a GUID for your + * application from Nvidia. + */ + const char* appGUID; +#endif + /** + * \brief The CUDA/Graphics interop mode of this context + * + * If ctx is NULL, this value describes the nature of the graphicsDevice + * pointer provided by the user. Else it describes the nature of the + * context provided by the user. + */ + PxCudaInteropMode::Enum interopMode; + + + /** + * \brief Size of persistent memory + * + * This memory is allocated up front and stays allocated until the + * PxCudaContextManager is released. Size is in bytes, has to be power of two + * and bigger than the page size. Set to 0 to only use dynamic pages. + * + * Note: On Vista O/S and above, there is a per-memory allocation overhead + * to every CUDA work submission, so we recommend that you carefully tune + * this initial base memory size to closely approximate the amount of + * memory your application will consume. + + Note: This is currently not used by PxSceneFlag::eENABLE_GPU_DYNAMICS. Memory allocation properties are configured + for GPU rigid bodies using PxSceneDesc::gpuDynamicsConfig. + */ + uint32_t memoryBaseSize[PxCudaBufferMemorySpace::COUNT]; + + /** + * \brief Size of memory pages + * + * The memory manager will dynamically grow and shrink in blocks multiple of + * this page size. Size has to be power of two and bigger than 0. + + Note: This is currently not used by PxSceneFlag::eENABLE_GPU_DYNAMICS. Memory allocation properties are configured + for GPU rigid bodies using PxSceneDesc::gpuDynamicsConfig. + */ + uint32_t memoryPageSize[PxCudaBufferMemorySpace::COUNT]; + + /** + * \brief Maximum size of memory that the memory manager will allocate + + Note: This is currently not used by PxSceneFlag::eENABLE_GPU_DYNAMICS. Memory allocation properties are configured + for GPU rigid bodies using PxSceneDesc::gpuDynamicsConfig. + */ + uint32_t maxMemorySize[PxCudaBufferMemorySpace::COUNT]; + + PX_INLINE PxCudaContextManagerDesc() + { + ctx = NULL; + interopMode = PxCudaInteropMode::NO_INTEROP; + graphicsDevice = 0; +#if PX_SUPPORT_GPU_PHYSX + appGUID = NULL; +#endif + for(uint32_t i = 0; i < PxCudaBufferMemorySpace::COUNT; i++) + { + memoryBaseSize[i] = 0; + memoryPageSize[i] = 2 * 1024*1024; + maxMemorySize[i] = UINT32_MAX; + } + } +}; + + +/** + * \brief Manages memory, thread locks, and task scheduling for a CUDA context + * + * A PxCudaContextManager manages access to a single CUDA context, allowing it to + * be shared between multiple scenes. Memory allocations are dynamic: starting + * with an initial heap size and growing on demand by a configurable page size. + * The context must be acquired from the manager before using any CUDA APIs. + * + * The PxCudaContextManager is based on the CUDA driver API and explictly does not + * support the CUDA runtime API (aka, CUDART). + * + * To enable CUDA use by an APEX scene, a PxCudaContextManager must be created + * (supplying your own CUDA context, or allowing a new context to be allocated + * for you), the PxGpuDispatcher for that context is retrieved via the + * getGpuDispatcher() method, and this is assigned to the TaskManager that is + * given to the scene via its NxApexSceneDesc. + */ +class PxCudaContextManager +{ +public: + /** + * \brief Acquire the CUDA context for the current thread + * + * Acquisitions are allowed to be recursive within a single thread. + * You can acquire the context multiple times so long as you release + * it the same count. + * + * The context must be acquired before using most CUDA functions. + * + * It is not necessary to acquire the CUDA context inside GpuTask + * launch functions, because the PxGpuDispatcher will have already + * acquired the context for its worker thread. However it is not + * harmfull to (re)acquire the context in code that is shared between + * GpuTasks and non-task functions. + */ + virtual void acquireContext() = 0; + + /** + * \brief Release the CUDA context from the current thread + * + * The CUDA context should be released as soon as practically + * possible, to allow other CPU threads (including the + * PxGpuDispatcher) to work efficiently. + */ + virtual void releaseContext() = 0; + + /** + * \brief Return the CUcontext + */ + virtual CUcontext getContext() = 0; + + /** + * \brief Return the PxCudaMemoryManager instance associated with this + * CUDA context + * Note: This is currently not used by PxSceneFlag::eENABLE_GPU_DYNAMICS. Memory allocation properties are configured + * for GPU rigid bodies using PxSceneDesc::gpuDynamicsConfig. + */ + virtual PxCudaMemoryManager *getMemoryManager() = 0; + + /** + * \brief Return the PxGpuDispatcher instance associated with this + * CUDA context + */ + virtual class physx::PxGpuDispatcher *getGpuDispatcher() = 0; + + /** + * \brief Context manager has a valid CUDA context + * + * This method should be called after creating a PxCudaContextManager, + * especially if the manager was responsible for allocating its own + * CUDA context (desc.ctx == NULL). If it returns false, there is + * no point in assigning this manager's PxGpuDispatcher to a + * TaskManager as it will be unable to execute GpuTasks. + */ + virtual bool contextIsValid() const = 0; + + /* Query CUDA context and device properties, without acquiring context */ + + virtual bool supportsArchSM10() const = 0; //!< G80 + virtual bool supportsArchSM11() const = 0; //!< G92 + virtual bool supportsArchSM12() const = 0; //!< GT200 + virtual bool supportsArchSM13() const = 0; //!< GT260 + virtual bool supportsArchSM20() const = 0; //!< GF100 + virtual bool supportsArchSM30() const = 0; //!< GK100 + virtual bool supportsArchSM35() const = 0; //!< GK110 + virtual bool supportsArchSM50() const = 0; //!< GM100 + virtual bool supportsArchSM52() const = 0; //!< GM200 + virtual bool supportsArchSM60() const = 0; //!< GP100 + virtual bool isIntegrated() const = 0; //!< true if GPU is an integrated (MCP) part + virtual bool canMapHostMemory() const = 0; //!< true if GPU map host memory to GPU (0-copy) + virtual int getDriverVersion() const = 0; //!< returns cached value of cuGetDriverVersion() + virtual size_t getDeviceTotalMemBytes() const = 0; //!< returns cached value of device memory size + virtual int getMultiprocessorCount() const = 0; //!< returns cache value of SM unit count + virtual unsigned int getClockRate() const = 0; //!< returns cached value of SM clock frequency + virtual int getSharedMemPerBlock() const = 0; //!< returns total amount of shared memory available per block in bytes + virtual int getSharedMemPerMultiprocessor() const = 0; //!< returns total amount of shared memory available per multiprocessor in bytes + virtual unsigned int getMaxThreadsPerBlock() const = 0; //!< returns the maximum number of threads per block + virtual const char *getDeviceName() const = 0; //!< returns device name retrieved from driver + virtual CUdevice getDevice() const = 0; //!< returns device handle retrieved from driver + virtual PxCudaInteropMode::Enum getInteropMode() const = 0; //!< interop mode the context was created with + + virtual void setUsingConcurrentStreams(bool) = 0; //!< turn on/off using concurrent streams for GPU work + virtual bool getUsingConcurrentStreams() const = 0; //!< true if GPU work can run in concurrent streams + /* End query methods that don't require context to be acquired */ + + /** + * \brief Register a rendering resource with CUDA + * + * This function is called to register render resources (allocated + * from OpenGL) with CUDA so that the memory may be shared + * between the two systems. This is only required for render + * resources that are designed for interop use. In APEX, each + * render resource descriptor that could support interop has a + * 'registerInCUDA' boolean variable. + * + * The function must be called again any time your graphics device + * is reset, to re-register the resource. + * + * Returns true if the registration succeeded. A registered + * resource must be unregistered before it can be released. + * + * \param resource [OUT] the handle to the resource that can be used with CUDA + * \param buffer [IN] GLuint buffer index to be mapped to cuda + * \param flags [IN] cuda interop registration flags + */ + virtual bool registerResourceInCudaGL(CUgraphicsResource &resource, uint32_t buffer, PxCudaInteropRegisterFlags flags = PxCudaInteropRegisterFlags()) = 0; + + /** + * \brief Register a rendering resource with CUDA + * + * This function is called to register render resources (allocated + * from Direct3D) with CUDA so that the memory may be shared + * between the two systems. This is only required for render + * resources that are designed for interop use. In APEX, each + * render resource descriptor that could support interop has a + * 'registerInCUDA' boolean variable. + * + * The function must be called again any time your graphics device + * is reset, to re-register the resource. + * + * Returns true if the registration succeeded. A registered + * resource must be unregistered before it can be released. + * + * \param resource [OUT] the handle to the resource that can be used with CUDA + * \param resourcePointer [IN] A pointer to either IDirect3DResource9, or ID3D10Device, or ID3D11Resource to be registered. + * \param flags [IN] cuda interop registration flags + */ + virtual bool registerResourceInCudaD3D(CUgraphicsResource &resource, void *resourcePointer, PxCudaInteropRegisterFlags flags = PxCudaInteropRegisterFlags()) = 0; + + /** + * \brief Unregister a rendering resource with CUDA + * + * If a render resource was successfully registered with CUDA using + * the registerResourceInCuda***() methods, this function must be called + * to unregister the resource before the it can be released. + */ + virtual bool unregisterResourceInCuda(CUgraphicsResource resource) = 0; + + /** + * \brief Determine if the user has configured a dedicated PhysX GPU in the NV Control Panel + * \note If using CUDA Interop, this will always return false + * \returns 1 if there is a dedicated GPU + * 0 if there is NOT a dedicated GPU + * -1 if the routine is not implemented + */ + virtual int usingDedicatedGPU() const = 0; + + /** + * \brief Release the PxCudaContextManager + * + * When the manager instance is released, it also releases its + * PxGpuDispatcher instance and PxCudaMemoryManager. Before the memory + * manager is released, it frees all allocated memory pages. If the + * PxCudaContextManager created the CUDA context it was responsible + * for, it also frees that context. + * + * Do not release the PxCudaContextManager if there are any scenes + * using its PxGpuDispatcher. Those scenes must be released first + * since there is no safe way to remove a PxGpuDispatcher from a + * TaskManager once the TaskManager has been given to a scene. + * + */ + virtual void release() = 0; + +protected: + + /** + * \brief protected destructor, use release() method + */ + virtual ~PxCudaContextManager() {} +}; + +/** + * \brief Convenience class for holding CUDA lock within a scope + */ +class PxScopedCudaLock +{ +public: + /** + * \brief ScopedCudaLock constructor + */ + PxScopedCudaLock(PxCudaContextManager& ctx) : mCtx(&ctx) + { + mCtx->acquireContext(); + } + + /** + * \brief ScopedCudaLock destructor + */ + ~PxScopedCudaLock() + { + mCtx->releaseContext(); + } + +protected: + + /** + * \brief CUDA context manager pointer (initialized in the constructor) + */ + PxCudaContextManager* mCtx; +}; + +} // end physx namespace + +#endif // PX_SUPPORT_GPU_PHYSX +#endif // PXCUDACONTEXTMANAGER_PXCUDACONTEXTMANAGER_H diff --git a/src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h b/src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h new file mode 100644 index 000000000..9d20feb61 --- /dev/null +++ b/src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h @@ -0,0 +1,281 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXCUDACONTEXTMANAGER_PXCUDAMEMORYMANAGER_H +#define PXCUDACONTEXTMANAGER_PXCUDAMEMORYMANAGER_H + +#include "foundation/PxPreprocessor.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "task/PxTaskDefine.h" + +// some macros to keep the source code more readable +#define PX_ALLOC_INFO(name, ID) __FILE__, __LINE__, name, physx::PxAllocId::ID +#define PX_ALLOC_INFO_PARAMS_DECL(p0, p1, p2, p3) const char* file = p0, int line = p1, const char* allocName = p2, physx::PxAllocId::Enum allocId = physx::PxAllocId::p3 +#define PX_ALLOC_INFO_PARAMS_DEF() const char* file, int line, const char* allocName, physx::PxAllocId::Enum allocId +#define PX_ALLOC_INFO_PARAMS_INPUT() file, line, allocName, allocId +#define PX_ALLOC_INFO_PARAMS_INPUT_INFO(info) info.getFileName(), info.getLine(), info.getAllocName(), info.getAllocId() + +#ifndef NULL // don't want to include +#define NULL 0 +#endif + +namespace physx +{ + +PX_PUSH_PACK_DEFAULT + +/** \brief ID of the Feature which owns/allocated memory from the heap + * + * Maximum of 64k IDs allowed. + */ +struct PxAllocId +{ + /** + * \brief ID of the Feature which owns/allocated memory from the heap + */ + enum Enum + { + UNASSIGNED, //!< default + APEX, //!< APEX stuff not further classified + PARTICLES, //!< all particle related + GPU_UTIL, //!< e.g. RadixSort (used in SPH and deformable self collision) + CLOTH, //!< all cloth related + NUM_IDS //!< number of IDs, be aware that ApexHeapStats contains PxAllocIdStats[NUM_IDS] + }; +}; + +/// \brief memory type managed by a heap +struct PxCudaBufferMemorySpace +{ + /** + * \brief memory type managed by a heap + */ + enum Enum + { + T_GPU, + T_PINNED_HOST, + T_WRITE_COMBINED, + T_HOST, + COUNT + }; +}; + +/// \brief class to track allocation statistics, see PxgMirrored +class PxAllocInfo +{ +public: + /** + * \brief AllocInfo default constructor + */ + PxAllocInfo() {} + + /** + * \brief AllocInfo constructor that initializes all of the members + */ + PxAllocInfo(const char* file, int line, const char* allocName, PxAllocId::Enum allocId) + : mFileName(file) + , mLine(line) + , mAllocName(allocName) + , mAllocId(allocId) + {} + + /// \brief get the allocation file name + inline const char* getFileName() const + { + return mFileName; + } + + /// \brief get the allocation line + inline int getLine() const + { + return mLine; + } + + /// \brief get the allocation name + inline const char* getAllocName() const + { + return mAllocName; + } + + /// \brief get the allocation ID + inline PxAllocId::Enum getAllocId() const + { + return mAllocId; + } + +private: + const char* mFileName; + int mLine; + const char* mAllocName; + PxAllocId::Enum mAllocId; +}; + +/// \brief statistics collected per AllocationId by HeapManager. +struct PxAllocIdStats +{ + size_t size; //!< currently allocated memory by this ID + size_t maxSize; //!< max allocated memory by this ID + size_t elements; //!< number of current allocations by this ID + size_t maxElements; //!< max number of allocations by this ID +}; + +class PxCudaMemoryManager; +typedef size_t PxCudaBufferPtr; + +/// \brief Hint flag to tell how the buffer will be used +struct PxCudaBufferFlags +{ +/// \brief Enumerations for the hint flag to tell how the buffer will be used + enum Enum + { + F_READ = (1 << 0), + F_WRITE = (1 << 1), + F_READ_WRITE = F_READ | F_WRITE + }; +}; + + +/// \brief Memory statistics struct returned by CudaMemMgr::getStats() +struct PxCudaMemoryManagerStats +{ + + size_t heapSize; //!< Size of all pages allocated for this memory type (allocated + free). + size_t totalAllocated; //!< Size occupied by the current allocations. + size_t maxAllocated; //!< High water mark of allocations since the SDK was created. + PxAllocIdStats allocIdStats[PxAllocId::NUM_IDS]; //!< Stats for each allocation ID, see PxAllocIdStats +}; + + +/// \brief Buffer type: made of hint flags and the memory space (Device Memory, Pinned Host Memory, ...) +struct PxCudaBufferType +{ + /// \brief PxCudaBufferType copy constructor + PX_INLINE PxCudaBufferType(const PxCudaBufferType& t) + : memorySpace(t.memorySpace) + , flags(t.flags) + {} + + /// \brief PxCudaBufferType constructor to explicitely assign members + PX_INLINE PxCudaBufferType(PxCudaBufferMemorySpace::Enum _memSpace, PxCudaBufferFlags::Enum _flags) + : memorySpace(_memSpace) + , flags(_flags) + {} + + PxCudaBufferMemorySpace::Enum memorySpace; //!< specifies which memory space for the buffer + PxCudaBufferFlags::Enum flags; //!< specifies the usage flags for the buffer +}; + + +/// \brief Buffer which keeps informations about allocated piece of memory. +class PxCudaBuffer +{ +public: + /// Retrieves the manager over which the buffer was allocated. + virtual PxCudaMemoryManager* getCudaMemoryManager() const = 0; + + /// Releases the buffer and the memory it used, returns true if successful. + virtual bool free() = 0; + + /// Realloc memory. Use to shrink or resize the allocated chunk of memory of this buffer. + /// Returns true if successful. Fails if the operation would change the address and need a memcopy. + /// In that case the user has to allocate, copy and free the memory with separate steps. + /// Realloc to size 0 always returns false and doesn't change the state. + virtual bool realloc(size_t size, PX_ALLOC_INFO_PARAMS_DECL(NULL, 0, NULL, UNASSIGNED)) = 0; + + /// Returns the type of the allocated memory. + virtual const PxCudaBufferType& getType() const = 0; + + /// Returns the pointer to the allocated memory. + virtual PxCudaBufferPtr getPtr() const = 0; + + /// Returns the size of the allocated memory. + virtual size_t getSize() const = 0; + +protected: + /// \brief protected destructor + virtual ~PxCudaBuffer() {} +}; + + +/// \brief Allocator class for different kinds of CUDA related memory. +class PxCudaMemoryManager +{ +public: + /// Allocate memory of given type and size. Returns a CudaBuffer if successful. Returns NULL if failed. + virtual PxCudaBuffer* alloc(const PxCudaBufferType& type, size_t size, PX_ALLOC_INFO_PARAMS_DECL(NULL, 0, NULL, UNASSIGNED)) = 0; + + /// Basic heap allocator without PxCudaBuffer + virtual PxCudaBufferPtr alloc(PxCudaBufferMemorySpace::Enum memorySpace, size_t size, PX_ALLOC_INFO_PARAMS_DECL(NULL, 0, NULL, UNASSIGNED)) = 0; + + /// Basic heap deallocator without PxCudaBuffer + virtual bool free(PxCudaBufferMemorySpace::Enum memorySpace, PxCudaBufferPtr addr) = 0; + + /// Basic heap realloc without PxCudaBuffer + virtual bool realloc(PxCudaBufferMemorySpace::Enum memorySpace, PxCudaBufferPtr addr, size_t size, PX_ALLOC_INFO_PARAMS_DECL(NULL, 0, NULL, UNASSIGNED)) = 0; + + /// Retrieve stats for the memory of given type. See PxCudaMemoryManagerStats. + virtual void getStats(const PxCudaBufferType& type, PxCudaMemoryManagerStats& outStats) = 0; + + /// Ensure that a given amount of free memory is available. Triggers CUDA allocations in size of (2^n * pageSize) if necessary. + /// Returns false if page allocations failed. + virtual bool reserve(const PxCudaBufferType& type, size_t size) = 0; + + /// Set the page size. The managed memory grows by blocks 2^n * pageSize. Page allocations trigger CUDA driver allocations, + /// so the page size should be reasonably big. Returns false if input size was invalid, i.e. not power of two. + /// Default is 2 MB. + virtual bool setPageSize(const PxCudaBufferType& type, size_t size) = 0; + + /// Set the upper limit until which pages of a given memory type can be allocated. + /// Reducing the max when it is already hit does not shrink the memory until it is deallocated by releasing the buffers which own the memory. + virtual bool setMaxMemorySize(const PxCudaBufferType& type, size_t size) = 0; + + /// Returns the base size. The base memory block stays persistently allocated over the SDKs life time. + virtual size_t getBaseSize(const PxCudaBufferType& type) = 0; + + /// Returns the currently set page size. The memory grows and shrinks in blocks of size (2^n pageSize) + virtual size_t getPageSize(const PxCudaBufferType& type) = 0; + + /// Returns the upper limit until which the manager is allowed to allocate additional pages from the CUDA driver. + virtual size_t getMaxMemorySize(const PxCudaBufferType& type) = 0; + + /// Get device mapped pinned host mem ptr. Operation only valid for memory space PxCudaBufferMemorySpace::T_PINNED_HOST. + virtual PxCudaBufferPtr getMappedPinnedPtr(PxCudaBufferPtr hostPtr) = 0; + +protected: + /// \brief protected destructor + virtual ~PxCudaMemoryManager() {} +}; + +PX_POP_PACK + + +} // end physx namespace + +#endif // PX_SUPPORT_GPU_PHYSX +#endif // PXCUDACONTEXTMANAGER_PXCUDAMEMORYMANAGER_H diff --git a/src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h b/src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h new file mode 100644 index 000000000..6429a7b1b --- /dev/null +++ b/src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h @@ -0,0 +1,86 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXCUDACONTEXTMANAGER_PXGPUCOPYDESC_H +#define PXCUDACONTEXTMANAGER_PXGPUCOPYDESC_H + +#include "foundation/PxPreprocessor.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "task/PxTaskDefine.h" + +namespace physx +{ + +PX_PUSH_PACK_DEFAULT + +/** + * \brief Input descriptor for the GpuDispatcher's built-in copy kernel + * + * All host memory involved in copy transactions must be page-locked. + * If more than one descriptor is passed to the copy kernel in one launch, + * the descriptors themselves must be in page-locked memory. + */ +struct PxGpuCopyDesc +{ + /** + * \brief Input descriptor for the GpuDispatcher's built-in copy kernel + */ + enum CopyType + { + HostToDevice, + DeviceToHost, + DeviceToDevice, + DeviceMemset32 + }; + + size_t dest; //!< the destination + size_t source; //!< the source (32bit value when type == DeviceMemset) + size_t bytes; //!< the size in bytes + CopyType type; //!< the memory transaction type + + /** + * \brief Copy is optimally performed as 64bit words, requires 64bit alignment. But it can + * gracefully degrade to 32bit copies if necessary + */ + PX_INLINE bool isValid() + { + bool ok = true; + ok &= ((dest & 0x3) == 0); + ok &= ((type == DeviceMemset32) || (source & 0x3) == 0); + ok &= ((bytes & 0x3) == 0); + return ok; + } +}; + +PX_POP_PACK + +} // end physx namespace + +#endif // PX_SUPPORT_GPU_PHYSX +#endif // PXCUDACONTEXTMANAGER_PXGPUCOPYDESC_H diff --git a/src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h b/src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h new file mode 100644 index 000000000..64d190b03 --- /dev/null +++ b/src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXCUDACONTEXTMANAGER_PXGPUCOPYDESCQUEUE_H +#define PXCUDACONTEXTMANAGER_PXGPUCOPYDESCQUEUE_H + +#include "foundation/PxPreprocessor.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "foundation/PxAssert.h" +#include "task/PxTaskDefine.h" +#include "task/PxGpuDispatcher.h" +#include "cudamanager/PxGpuCopyDesc.h" +#include "cudamanager/PxCudaContextManager.h" + +/* forward decl to avoid including */ +typedef struct CUstream_st* CUstream; + +namespace physx +{ + +PX_PUSH_PACK_DEFAULT + +/// \brief Container class for queueing PxGpuCopyDesc instances in pinned (non-pageable) CPU memory +class PxGpuCopyDescQueue +{ +public: + /// \brief PxGpuCopyDescQueue constructor + PxGpuCopyDescQueue(PxGpuDispatcher& d) + : mDispatcher(d) + , mBuffer(0) + , mStream(0) + , mReserved(0) + , mOccupancy(0) + , mFlushed(0) + { + } + + /// \brief PxGpuCopyDescQueue destructor + ~PxGpuCopyDescQueue() + { + if (mBuffer) + { + mDispatcher.getCudaContextManager()->getMemoryManager()->free(PxCudaBufferMemorySpace::T_PINNED_HOST, (size_t) mBuffer); + } + } + + /// \brief Reset the enqueued copy descriptor list + /// + /// Must be called at least once before any copies are enqueued, and each time the launched + /// copies are known to have been completed. The recommended use case is to call this at the + /// start of each simulation step. + void reset(CUstream stream, uint32_t reserveSize) + { + if (reserveSize > mReserved) + { + if (mBuffer) + { + mDispatcher.getCudaContextManager()->getMemoryManager()->free( + PxCudaBufferMemorySpace::T_PINNED_HOST, + (size_t) mBuffer); + mReserved = 0; + } + mBuffer = (PxGpuCopyDesc*) mDispatcher.getCudaContextManager()->getMemoryManager()->alloc( + PxCudaBufferMemorySpace::T_PINNED_HOST, + reserveSize * sizeof(PxGpuCopyDesc), + PX_ALLOC_INFO("PxGpuCopyDescQueue", GPU_UTIL)); + if (mBuffer) + { + mReserved = reserveSize; + } + } + + mOccupancy = 0; + mFlushed = 0; + mStream = stream; + } + + /// \brief Enqueue the specified copy descriptor, or launch immediately if no room is available + void enqueue(PxGpuCopyDesc& desc) + { + PX_ASSERT(desc.isValid()); + if (desc.bytes == 0) + { + return; + } + + if (mOccupancy < mReserved) + { + mBuffer[ mOccupancy++ ] = desc; + } + else + { + mDispatcher.launchCopyKernel(&desc, 1, mStream); + } + } + + /// \brief Launch all copies queued since the last flush or reset + void flushEnqueued() + { + if (mOccupancy > mFlushed) + { + mDispatcher.launchCopyKernel(mBuffer + mFlushed, mOccupancy - mFlushed, mStream); + mFlushed = mOccupancy; + } + } + +private: + PxGpuDispatcher& mDispatcher; + PxGpuCopyDesc* mBuffer; + CUstream mStream; + uint32_t mReserved; + uint32_t mOccupancy; + uint32_t mFlushed; + + void operator=(const PxGpuCopyDescQueue&); // prevent a warning... +}; + +PX_POP_PACK + +} // end physx namespace + +#endif // PX_SUPPORT_GPU_PHYSX +#endif // PXCUDACONTEXTMANAGER_PXGPUCOPYDESCQUEUE_H diff --git a/src/PhysX/physx/include/extensions/PxBinaryConverter.h b/src/PhysX/physx/include/extensions/PxBinaryConverter.h new file mode 100644 index 000000000..1a985fda7 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxBinaryConverter.h @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_BINARY_CONVERTER_H +#define PX_BINARY_CONVERTER_H +/** \addtogroup extensions +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxConverterReportMode +{ + enum Enum + { + eNONE, //!< Silent mode. If enabled, no information is sent to the error stream. + eNORMAL, //!< Normal mode. If enabled, only important information is sent to the error stream. + eVERBOSE //!< Verbose mode. If enabled, detailed information is sent to the error stream. + }; +}; + + +/** +\brief Binary converter for serialized streams. + +The binary converter class is targeted at converting binary streams from authoring platforms, +such as windows, osx or linux to any game runtime platform supported by PhysX. Particularly +it is currently not supported to run the converter on a platforms that has an endian mismatch +with the platform corresponding to the source binary file and source meta data. + +If you want to use multiple threads for batch conversions, please create one instance +of this class for each thread. + +@see PxSerialization.createBinaryConverter +*/ +class PxBinaryConverter +{ +public: + + /** + \brief Releases binary converter + */ + virtual void release() = 0; + + /** + \brief Sets desired report mode. + + \param[in] mode Report mode + */ + virtual void setReportMode(PxConverterReportMode::Enum mode) = 0; + + /** + \brief Setups source and target meta-data streams + + The source meta data provided needs to have the same endianness as the platform the converter is run on. + The meta data needs to be set before calling the conversion method. + + \param[in] srcMetaData Source platform's meta-data stream + \param[in] dstMetaData Target platform's meta-data stream + + \return True if success + */ + virtual bool setMetaData(PxInputStream& srcMetaData, PxInputStream& dstMetaData) = 0; + + /** + \brief Test utility function to compare two sets of meta data. + + The meta data needs to be set before calling the compareMetaData method. + This method will issue PxErrorCode::eDEBUG_INFO messages if mismatches are encountered. + + \return True if meta data is equivalend + */ + virtual bool compareMetaData() const = 0; + + /** + \brief Converts binary stream from source platform to target platform + + The converter needs to be configured with source and destination meta data before calling the conversion method. + The source meta data needs to correspond to the same platform as the source binary data. + + \param[in] srcStream Source stream + \param[in] srcSize Number of bytes to convert + \param[in] targetStream Target stream + + \return True if success + */ + virtual bool convert(PxInputStream& srcStream, PxU32 srcSize, PxOutputStream& targetStream) = 0; + + +protected: + PxBinaryConverter() {} + virtual ~PxBinaryConverter() {} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxBroadPhaseExt.h b/src/PhysX/physx/include/extensions/PxBroadPhaseExt.h new file mode 100644 index 000000000..8347bcc10 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxBroadPhaseExt.h @@ -0,0 +1,75 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_BROAD_PHASE_H +#define PX_PHYSICS_EXTENSIONS_BROAD_PHASE_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxBroadPhaseExt +{ +public: + + /** + \brief Creates regions for PxSceneDesc, from a global box. + + This helper simply subdivides the given global box into a 2D grid of smaller boxes. Each one of those smaller boxes + is a region of interest for the broadphase. There are nbSubdiv*nbSubdiv regions in the 2D grid. The function does not + subdivide along the given up axis. + + This is the simplest setup one can use with PxBroadPhaseType::eMBP. A more sophisticated setup would try to cover + the game world with a non-uniform set of regions (i.e. not just a grid). + + \param[out] regions Regions computed from the input global box + \param[in] globalBounds World-space box covering the game world + \param[in] nbSubdiv Grid subdivision level. The function will create nbSubdiv*nbSubdiv regions. + \param[in] upAxis Up axis (0 for X, 1 for Y, 2 for Z). + \return number of regions written out to the 'regions' array + + @see PxSceneDesc PxBroadPhaseType + */ + static PxU32 createRegionsFromWorldBounds(PxBounds3* regions, const PxBounds3& globalBounds, PxU32 nbSubdiv, PxU32 upAxis=1); +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxCollectionExt.h b/src/PhysX/physx/include/extensions/PxCollectionExt.h new file mode 100644 index 000000000..157c28108 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxCollectionExt.h @@ -0,0 +1,119 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLECTION_EXT_H +#define PX_COLLECTION_EXT_H +/** \addtogroup extensions +@{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxCollection.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxCollectionExt + { + public: + /** + \brief Removes and releases all object from a collection. + + The Collection itself is not released. + + If the releaseExclusiveShapes flag is not set to true, release() will not be called on exclusive shapes. + + It is assumed that the application holds a reference to each of the objects in the collection, with the exception of objects that are not releasable + (PxBase::isReleasable()). In general, objects that violate this assumption need to be removed from the collection prior to calling releaseObjects. + + \note when a shape is created with PxRigidActor::createShape() or PxRigidActorExt::createExclusiveShape(), the only counted reference is held by the actor. + If such a shape and its actor are present in the collection, the reference count will be decremented once when the actor is released, and once when the + shape is released, resulting in undefined behavior. Shape reference counts can be incremented with PxShape::acquireReference(). + + \param[in] collection to remove and release all object from. + \param[in] releaseExclusiveShapes if this parameter is set to false, release() will not be called on exclusive shapes. + */ + static void releaseObjects(PxCollection& collection, bool releaseExclusiveShapes = true); + + /** + \brief Removes objects of a given type from a collection, potentially adding them to another collection. + + \param[in,out] collection Collection from which objects are removed + \param[in] concreteType PxConcreteType of sdk objects that should be removed + \param[in,out] to Optional collection to which the removed objects are added + + @see PxCollection, PxConcreteType + */ + static void remove(PxCollection& collection, PxType concreteType, PxCollection* to = NULL); + + + /** + \brief Collects all objects in PxPhysics that are shareable across multiple scenes. + + This function creates a new collection from all objects that are shareable across multiple + scenes. Instances of the following types are included: PxConvexMesh, PxTriangleMesh, + PxHeightField, PxShape and PxMaterial. + + This is a helper function to ease the creation of collections for serialization. + + \param[in] physics The physics SDK instance from which objects are collected. See #PxPhysics + \return Collection to which objects are added. See #PxCollection + + @see PxCollection, PxPhysics + */ + static PxCollection* createCollection(PxPhysics& physics); + + /** + \brief Collects all objects from a PxScene. + + This function creates a new collection from all objects that where added to the specified + PxScene. Instances of the following types are included: PxActor, PxAggregate, + PxArticulation and PxJoint (other PxConstraint types are not included). + + This is a helper function to ease the creation of collections for serialization. + The function PxSerialization.complete() can be used to complete the collection with required objects prior to + serialization. + + \param[in] scene The PxScene instance from which objects are collected. See #PxScene + \return Collection to which objects are added. See #PxCollection + + @see PxCollection, PxScene, PxSerialization.complete() + */ + static PxCollection* createCollection(PxScene& scene); + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxConstraintExt.h b/src/PhysX/physx/include/extensions/PxConstraintExt.h new file mode 100644 index 000000000..62ca07814 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxConstraintExt.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_CONSTRAINT_H +#define PX_PHYSICS_EXTENSIONS_CONSTRAINT_H + +#include "foundation/PxPreprocessor.h" + +/** \addtogroup extensions + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Unique identifiers for extensions classes which implement a constraint based on PxConstraint. + +\note Users which want to create their own custom constraint types should choose an ID larger or equal to eNEXT_FREE_ID +and not eINVALID_ID. + +@see PxConstraint PxSimulationEventCallback.onConstraintBreak() +*/ +struct PxConstraintExtIDs +{ + enum Enum + { + eJOINT, + eVEHICLE_SUSP_LIMIT, + eVEHICLE_STICKY_TYRE, + eNEXT_FREE_ID, + eINVALID_ID = 0x7fffffff + }; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxContactJoint.h b/src/PhysX/physx/include/extensions/PxContactJoint.h new file mode 100644 index 000000000..00f84a5e3 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxContactJoint.h @@ -0,0 +1,168 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_CONTACTJOINT_H +#define PX_CONTACTJOINT_H + +#include "extensions/PxJoint.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxContactJoint; + + /** + \brief Create a distance Joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + + @see PxContactJoint + */ + PxContactJoint* PxContactJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + + /** + \brief a joint that maintains an upper or lower bound (or both) on the distance between two points on different objects + + @see PxContactJointCreate PxJoint + */ + + struct PxJacobianRow + { + PxVec3 linear0; + PxVec3 linear1; + PxVec3 angular0; + PxVec3 angular1; + + PxJacobianRow(){} + + PxJacobianRow(const PxVec3& lin0, const PxVec3& lin1, const PxVec3& ang0, const PxVec3& ang1) : + linear0(lin0), linear1(lin1), angular0(ang0), angular1(ang1) + { + + } + + void operator *= (const PxReal scale) + { + linear0 *= scale; + linear1 *= scale; + angular0 *= scale; + angular1 *= scale; + } + + PxJacobianRow operator * (const PxReal scale) const + { + return PxJacobianRow(linear0*scale, linear1*scale, angular0*scale, angular1*scale); + } + }; + + /** + \brief a joint that maintains an upper or lower bound (or both) on the distance between two points on different objects + + @see PxContactJointCreate PxJoint + */ + class PxContactJoint : public PxJoint + { + public: + + /** + \brief Set the current contact of the joint + */ + virtual void setContact(const PxVec3& contact) = 0; + + /** + \brief Set the current contact normal of the joint + */ + virtual void setContactNormal(const PxVec3& contactNormal) = 0; + + /** + \brief Set the current penetration of the joint + */ + virtual void setPenetration(const PxReal penetration) = 0; + + /** + \brief Return the current contact of the joint + */ + virtual PxVec3 getContact() const = 0; + + /** + \brief Return the current contact normal of the joint + */ + virtual PxVec3 getContactNormal() const = 0; + + /** + \brief Return the current penetration value of the joint + */ + virtual PxReal getPenetration() const = 0; + + virtual PxReal getResititution() const = 0; + virtual void setResititution(const PxReal resititution) = 0; + virtual PxReal getBounceThreshold() const = 0; + virtual void setBounceThreshold(const PxReal bounceThreshold) = 0; + + /** + \brief Returns string name of PxContactJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxContactJoint"; } + + virtual void computeJacobians(PxJacobianRow* jacobian) const = 0; + virtual PxU32 getNbJacobianRows() const = 0; + + protected: + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxContactJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxContactJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxContactJoint", name) || PxJoint::isKindOf(name); } + + //~serialization + }; + +#if !PX_DOXYGEN +} +#endif + + +#endif diff --git a/src/PhysX/physx/include/extensions/PxConvexMeshExt.h b/src/PhysX/physx/include/extensions/PxConvexMeshExt.h new file mode 100644 index 000000000..39b150f42 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxConvexMeshExt.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_CONVEX_MESH_H +#define PX_PHYSICS_EXTENSIONS_CONVEX_MESH_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxConvexMeshGeometry; + + /** + \brief Computes closest polygon of the convex hull geometry for a given impact point + and impact direction. When doing sweeps against a scene, one might want to delay + the rather expensive computation of the hit face index for convexes until it is clear + the information is really needed and then use this method to get the corresponding + face index. + + \param[in] convexGeom The convex mesh geometry. + \param[in] geomPose Pose for the geometry object. + \param[in] impactPos Impact position. + \param[in] unitDir Normalized impact direction. + + \return Closest face index of the convex geometry. + + @see PxTransform PxConvexMeshGeometry + */ + PxU32 PxFindFaceIndex(const PxConvexMeshGeometry& convexGeom, + const PxTransform& geomPose, + const PxVec3& impactPos, + const PxVec3& unitDir); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxD6Joint.h b/src/PhysX/physx/include/extensions/PxD6Joint.h new file mode 100644 index 000000000..7fe722d8d --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxD6Joint.h @@ -0,0 +1,561 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_D6JOINT_H +#define PX_D6JOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" +#include "extensions/PxJointLimit.h" +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxD6Joint; + +/** +\brief Create a D6 joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxD6Joint +*/ +PxD6Joint* PxD6JointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + +/** +\brief Used to specify one of the degrees of freedom of a D6 joint. + +@see PxD6Joint +*/ +struct PxD6Axis +{ + enum Enum + { + eX = 0, //!< motion along the X axis + eY = 1, //!< motion along the Y axis + eZ = 2, //!< motion along the Z axis + eTWIST = 3, //!< motion around the X axis + eSWING1 = 4, //!< motion around the Y axis + eSWING2 = 5, //!< motion around the Z axis + eCOUNT = 6 + }; +}; + + +/** +\brief Used to specify the range of motions allowed for a degree of freedom in a D6 joint. + +@see PxD6Joint +*/ +struct PxD6Motion +{ + enum Enum + { + eLOCKED, //!< The DOF is locked, it does not allow relative motion. + eLIMITED, //!< The DOF is limited, it only allows motion within a specific range. + eFREE //!< The DOF is free and has its full range of motion. + }; +}; + + +/** +\brief Used to specify which axes of a D6 joint are driven. + +Each drive is an implicit force-limited damped spring: + +force = spring * (target position - position) + damping * (targetVelocity - velocity) + +Alternatively, the spring may be configured to generate a specified acceleration instead of a force. + +A linear axis is affected by drive only if the corresponding drive flag is set. There are two possible models +for angular drive: swing/twist, which may be used to drive one or more angular degrees of freedom, or slerp, +which may only be used to drive all three angular degrees simultaneously. + +@see PxD6Joint +*/ +struct PxD6Drive +{ + enum Enum + { + eX = 0, //!< drive along the X-axis + eY = 1, //!< drive along the Y-axis + eZ = 2, //!< drive along the Z-axis + eSWING = 3, //!< drive of displacement from the X-axis + eTWIST = 4, //!< drive of the displacement around the X-axis + eSLERP = 5, //!< drive of all three angular degrees along a SLERP-path + eCOUNT = 6 + }; +}; + +/** +\brief flags for configuring the drive model of a PxD6Joint + +@see PxD6JointDrive PxD6Joint +*/ +struct PxD6JointDriveFlag +{ + enum Enum + { + eACCELERATION = 1 //!< drive spring is for the acceleration at the joint (rather than the force) + }; +}; +typedef PxFlags PxD6JointDriveFlags; +PX_FLAGS_OPERATORS(PxD6JointDriveFlag::Enum, PxU32) + +/** +\brief parameters for configuring the drive model of a PxD6Joint + +@see PxD6Joint +*/ +class PxD6JointDrive : public PxSpring +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + +public: + PxReal forceLimit; //!< the force limit of the drive - may be an impulse or a force depending on PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES + PxD6JointDriveFlags flags; //!< the joint drive flags + + /** + \brief default constructor for PxD6JointDrive. + */ + PxD6JointDrive(): PxSpring(0,0), forceLimit(PX_MAX_F32), flags(0) {} + + /** + \brief constructor a PxD6JointDrive. + + \param[in] driveStiffness The stiffness of the drive spring. + \param[in] driveDamping The damping of the drive spring + \param[in] driveForceLimit The maximum impulse or force that can be exerted by the drive + \param[in] isAcceleration Whether the drive is an acceleration drive or a force drive + */ + PxD6JointDrive(PxReal driveStiffness, PxReal driveDamping, PxReal driveForceLimit, bool isAcceleration = false) + : PxSpring(driveStiffness, driveDamping) + , forceLimit(driveForceLimit) + , flags(isAcceleration?PxU32(PxD6JointDriveFlag::eACCELERATION) : 0) + {} + + /** + \brief returns true if the drive is valid + */ + bool isValid() const + { + return PxIsFinite(stiffness) && stiffness>=0 && + PxIsFinite(damping) && damping >=0 && + PxIsFinite(forceLimit) && forceLimit >=0; + } +}; + + +/** + \brief A D6 joint is a general constraint between two actors. + + It allows the application to individually define the linear and rotational degrees of freedom, + and also to configure a variety of limits and driven degrees of freedom. + + By default all degrees of freedom are locked. So to create a prismatic joint with free motion + along the x-axis: + + \code + ... + joint->setMotion(PxD6Axis::eX, PxD6JointMotion::eFREE); + ... + \endcode + + Or a Revolute joint with motion free allowed around the x-axis: + + \code + ... + joint->setMotion(PxD6Axis::eTWIST, PxD6JointMotion::eFREE); + ... + \endcode + + Degrees of freedom may also be set to limited instead of locked. + + There are two different kinds of linear limits available. The first kind is a single limit value + for all linear degrees of freedom, which may act as a linear, circular, or spherical limit depending + on which degrees of freedom are limited. This is similar to a distance limit. Then, the second kind + supports a pair of limit values for each linear axis, which can be used to implement a traditional + prismatic joint for example. + + If the twist degree of freedom is limited, is supports upper and lower limits. The two swing degrees + of freedom are limited with a cone limit. +@see PxD6JointCreate() PxJoint +*/ +class PxD6Joint : public PxJoint +{ +public: + + /** + \brief Set the motion type around the specified axis. + + Each axis may independently specify that the degree of freedom is locked (blocking relative movement + along or around this axis), limited by the corresponding limit, or free. + + \param[in] axis the axis around which motion is specified + \param[in] type the motion type around the specified axis + + Default: all degrees of freedom are locked + + @see getMotion() PxD6Axis PxD6Motion + */ + virtual void setMotion(PxD6Axis::Enum axis, PxD6Motion::Enum type) = 0; + + /** + \brief Get the motion type around the specified axis. + + @see setMotion() PxD6Axis PxD6Motion + + \param[in] axis the degree of freedom around which the motion type is specified + \return the motion type around the specified axis + */ + virtual PxD6Motion::Enum getMotion(PxD6Axis::Enum axis) const = 0; + + /** + \brief get the twist angle of the joint, in the range (-2*Pi, 2*Pi] + */ + virtual PxReal getTwistAngle() const = 0; + + /** + \brief get the twist angle of the joint + + \deprecated Use getTwistAngle instead. Deprecated since PhysX version 4.0 + */ + PX_DEPRECATED PX_FORCE_INLINE PxReal getTwist() const { return getTwistAngle(); } + + /** + \brief get the swing angle of the joint from the Y axis + */ + virtual PxReal getSwingYAngle() const = 0; + + /** + \brief get the swing angle of the joint from the Z axis + */ + virtual PxReal getSwingZAngle() const = 0; + + /** + \brief Set the distance limit for the joint. + + A single limit constraints all linear limited degrees of freedom, forming a linear, circular + or spherical constraint on motion depending on the number of limited degrees. This is similar + to a distance limit. + + \param[in] limit the distance limit structure + + @see getDistanceLimit() PxJointLinearLimit + */ + virtual void setDistanceLimit(const PxJointLinearLimit& limit) = 0; + + /** + \brief Get the distance limit for the joint. + + \return the distance limit structure + + @see setDistanceLimit() PxJointLinearLimit + */ + virtual PxJointLinearLimit getDistanceLimit() const = 0; + + /** + \deprecated Use setDistanceLimit instead. Deprecated since PhysX version 4.0 + */ + PX_DEPRECATED PX_FORCE_INLINE void setLinearLimit(const PxJointLinearLimit& limit) { setDistanceLimit(limit); } + + /** + \deprecated Use getDistanceLimit instead. Deprecated since PhysX version 4.0 + */ + PX_DEPRECATED PX_FORCE_INLINE PxJointLinearLimit getLinearLimit() const { return getDistanceLimit(); } + + /** + \brief Set the linear limit for a given linear axis. + + This function extends the previous setDistanceLimit call with the following features: + - there can be a different limit for each linear axis + - each limit is defined by two values, i.e. it can now be asymmetric + + This can be used to create prismatic joints similar to PxPrismaticJoint, or point-in-quad joints, + or point-in-box joints. + + \param[in] axis The limited linear axis (must be PxD6Axis::eX, PxD6Axis::eY or PxD6Axis::eZ) + \param[in] limit The linear limit pair structure + + @see getLinearLimit() + */ + virtual void setLinearLimit(PxD6Axis::Enum axis, const PxJointLinearLimitPair& limit) = 0; + + /** + \brief Get the linear limit for a given linear axis. + + \param[in] axis The limited linear axis (must be PxD6Axis::eX, PxD6Axis::eY or PxD6Axis::eZ) + + \return the linear limit pair structure from desired axis + + @see setLinearLimit() PxJointLinearLimit + */ + virtual PxJointLinearLimitPair getLinearLimit(PxD6Axis::Enum axis) const = 0; + + /** + \brief Set the twist limit for the joint. + + The twist limit controls the range of motion around the twist axis. + + The limit angle range is (-2*Pi, 2*Pi). + + \param[in] limit the twist limit structure + + @see getTwistLimit() PxJointAngularLimitPair + */ + virtual void setTwistLimit(const PxJointAngularLimitPair& limit) = 0; + + /** + \brief Get the twist limit for the joint. + + \return the twist limit structure + + @see setTwistLimit() PxJointAngularLimitPair + */ + virtual PxJointAngularLimitPair getTwistLimit() const = 0; + + /** + \brief Set the swing cone limit for the joint. + + The cone limit is used if either or both swing axes are limited. The extents are + symmetrical and measured in the frame of the parent. If only one swing degree of freedom + is limited, the corresponding value from the cone limit defines the limit range. + + \param[in] limit the cone limit structure + + @see getLimitCone() PxJointLimitCone + */ + virtual void setSwingLimit(const PxJointLimitCone& limit) = 0; + + /** + \brief Get the cone limit for the joint. + + \return the swing limit structure + + @see setLimitCone() PxJointLimitCone + */ + virtual PxJointLimitCone getSwingLimit() const = 0; + + /** + \brief Set a pyramidal swing limit for the joint. + + The pyramid limits will only be used in the following cases: + - both swing Y and Z are limited. The limit shape is then a pyramid. + - Y is limited and Z is locked, or vice versa. The limit shape is an asymmetric angular section, similar to + what is supported for the twist axis. + The remaining cases (Y limited and Z is free, or vice versa) are not supported. + + \param[in] limit the cone limit structure + + @see getLimitCone() PxJointLimitPyramid + */ + virtual void setPyramidSwingLimit(const PxJointLimitPyramid& limit) = 0; + + /** + \brief Get the pyramidal swing limit for the joint. + + \return the swing limit structure + + @see setLimitCone() PxJointLimitPyramid + */ + virtual PxJointLimitPyramid getPyramidSwingLimit() const = 0; + + /** + \brief Set the drive parameters for the specified drive type. + + \param[in] index the type of drive being specified + \param[in] drive the drive parameters + + @see getDrive() PxD6JointDrive + + Default The default drive spring and damping values are zero, the force limit is zero, and no flags are set. + */ + virtual void setDrive(PxD6Drive::Enum index, const PxD6JointDrive& drive) = 0; + + /** + \brief Get the drive parameters for the specified drive type. + + \param[in] index the specified drive type + + @see setDrive() PxD6JointDrive + */ + virtual PxD6JointDrive getDrive(PxD6Drive::Enum index) const = 0; + + /** + \brief Set the drive goal pose + + The goal is relative to the constraint frame of actor[0] + + Default the identity transform + + \param[in] pose The goal drive pose if positional drive is in use. + \param[in] autowake Whether to wake the joint rigids up if it is asleep. + + @see setDrivePosition() + */ + virtual void setDrivePosition(const PxTransform& pose, bool autowake = true) = 0; + + /** + \brief Get the drive goal pose. + + @see getDrivePosition() + */ + virtual PxTransform getDrivePosition() const = 0; + + /** + \brief Set the target goal velocity for drive. + + The velocity is measured in the constraint frame of actor[0] + + \param[in] linear The goal velocity for linear drive + \param[in] angular The goal velocity for angular drive + \param[in] autowake Whether to wake the joint rigids up if it is asleep. + + @see getDriveVelocity() + */ + virtual void setDriveVelocity(const PxVec3& linear, const PxVec3& angular, bool autowake = true) = 0; + + /** + \brief Get the target goal velocity for joint drive. + + \param[in] linear The goal velocity for linear drive + \param[in] angular The goal velocity for angular drive + + @see setDriveVelocity() + */ + virtual void getDriveVelocity(PxVec3& linear, PxVec3& angular) const = 0; + + /** + \brief Set the linear tolerance threshold for projection. Projection is enabled if PxConstraintFlag::ePROJECTION + is set for the joint. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0, PX_MAX_F32)
            + Default: 1e10f + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() PxJoint::setConstraintFlags() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionLinearTolerance(PxReal tolerance) = 0; + + /** + \brief Get the linear tolerance threshold for projection. + + \return the linear tolerance threshold + + @see setProjectionLinearTolerance() + */ + virtual PxReal getProjectionLinearTolerance() const = 0; + + /** + \brief Set the angular tolerance threshold for projection. Projection is enabled if + PxConstraintFlag::ePROJECTION is set for the joint. + + If the joint deviates by more than this angle around its locked angular degrees of freedom, + the solver will move the bodies to close the angle. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0,Pi]
            + Default: Pi + + \param[in] tolerance the angular tolerance threshold in radians + + \note + Angular projection is implemented only for the case of two or three locked angular degrees of freedom. + + @see getProjectionAngularTolerance() PxJoint::setConstraintFlag() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionAngularTolerance(PxReal tolerance) = 0; + + /** + \brief Get the angular tolerance threshold for projection. + + \return tolerance the angular tolerance threshold in radians + + @see setProjectionAngularTolerance() + */ + virtual PxReal getProjectionAngularTolerance() const = 0; + + /** + \brief Returns string name of PxD6Joint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxD6Joint"; } + +protected: + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxD6Joint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxD6Joint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxD6Joint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxD6JointCreate.h b/src/PhysX/physx/include/extensions/PxD6JointCreate.h new file mode 100644 index 000000000..0e2d1964a --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxD6JointCreate.h @@ -0,0 +1,255 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_D6JOINT_CREATE_H +#define PX_D6JOINT_CREATE_H + +#include "common/PxPhysXCommonConfig.h" + +/** \addtogroup extensions + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysics; +class PxRigidActor; +class PxJoint; + +/** + \brief Helper function to create a fixed joint, using either a PxD6Joint or PxFixedJoint. + + For fixed joints it is important that the joint frames have the same orientation. This helper function uses an identity rotation for both. + It is also important that the joint frames have an equivalent position in world space. The function does not check this, so it is up to users + to ensure that this is the case. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] useD6 True to use a PxD6Joint, false to use a PxFixedJoint; + + \return The created joint. + + @see PxD6Joint PxFixedJoint +*/ +PxJoint* PxD6JointCreate_Fixed(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, bool useD6); + +/** + \brief Helper function to create a distance joint, using either a PxD6Joint or PxDistanceJoint. + + This helper function only supports a maximum distance constraint, because PxD6Joint does not support a minimum distance constraint (contrary + to PxDistanceJoint). + + The distance is computed between the joint frames' world-space positions. The joint frames' orientations are irrelevant here so the function + sets them to identity. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] maxDist The maximum allowed distance + \param[in] useD6 True to use a PxD6Joint, false to use a PxDistanceJoint; + + \return The created joint. + + @see PxD6Joint PxDistanceJoint +*/ +PxJoint* PxD6JointCreate_Distance(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, float maxDist, bool useD6); + +/** + \brief Helper function to create a prismatic joint, using either a PxD6Joint or PxPrismaticJoint. + + This function enforces that the joint frames have the same orientation, which is a local frame whose X is the desired translation axis. + This orientation is computed by the function, so users only have to define the desired translation axis (typically 1;0;0 or 0;1;0 or 0;0;1). + + The translation can be limited. Limits are enforced if minLimitmaxLimit the + limits are not enforced and the axis is free. The limit values are computed relative to the position of actor0's joint frame. + + The function creates hard limits, and uses PhysX's default contact distance parameter. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] axis The axis along which objects are allowed to move, expressed in the actors' local space + \param[in] minLimit The minimum allowed position along the axis + \param[in] maxLimit The maximum allowed position along the axis + \param[in] useD6 True to use a PxD6Joint, false to use a PxPrismaticJoint; + + \return The created joint. + + @see PxD6Joint PxPrismaticJoint +*/ +PxJoint* PxD6JointCreate_Prismatic(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float minLimit, float maxLimit, bool useD6); + +/** + \brief Helper function to create a revolute joint, using either a PxD6Joint or PxRevoluteJoint. + + This function enforces that the joint frames have the same orientation, which is a local frame whose X is the desired rotation axis. + This orientation is computed by the function, so users only have to define the desired rotation axis (typically 1;0;0 or 0;1;0 or 0;0;1). + + The rotation can be limited. Limits are enforced if minLimitmaxLimit the + limits are not enforced and the axis is free. The limit values are computed relative to the rotation of actor0's joint frame. + + The function creates hard limits, and uses PhysX's default contact distance parameter. + + Limits are expressed in radians. Allowed range is ]-2*PI;+2*PI[ + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] axis The axis around which objects are allowed to move, expressed in the actors' local space + \param[in] minLimit The minimum allowed rotation along the axis + \param[in] maxLimit The maximum allowed rotation along the axis + \param[in] useD6 True to use a PxD6Joint, false to use a PxRevoluteJoint; + + \return The created joint. + + @see PxD6Joint PxRevoluteJoint +*/ +PxJoint* PxD6JointCreate_Revolute(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float minLimit, float maxLimit, bool useD6); + +/** + \brief Helper function to create a spherical joint, using either a PxD6Joint or PxSphericalJoint. + + This function supports a cone limit shape, defined by a cone axis and two angular limit values. + + This function enforces that the joint frames have the same orientation, which is a local frame whose X is the desired cone axis. + This orientation is computed by the function, so users only have to define the desired cone axis (typically 1;0;0 or 0;1;0 or 0;0;1). + + The rotations can be limited. Limits are enforced if limit1>0 and limit2>0. Otherwise the motion is free. The limit values define an ellipse, + which is the cross-section of the cone limit shape. + + The function creates hard limits, and uses PhysX's default contact distance parameter. + + Limits are expressed in radians. Allowed range is ]0;PI[. Limits are symmetric around the cone axis. + + The cone axis is equivalent to the twist axis for the D6 joint. The twist motion is not limited. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] axis The cone axis, expressed in the actors' local space + \param[in] limit1 Max angular limit for the ellipse along the joint frame's second axis (first axis = cone axis) + \param[in] limit2 Max angular limit for the ellipse along the joint frame's third axis (first axis = cone axis) + \param[in] useD6 True to use a PxD6Joint, false to use a PxSphericalJoint; + + \return The created joint. + + @see PxD6Joint PxSphericalJoint +*/ +PxJoint* PxD6JointCreate_Spherical(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float limit1, float limit2, bool useD6); + +/** + \brief Helper function to create a spherical joint, using either a PxD6Joint or PxSphericalJoint. + + This function supports a cone limit shape, defined by two pairs of angular limit values. This can be used to create an asymmetric cone. If the + angular limit values are symmetric (i.e. minLimit1=-maxLimit1 and minLimit2=-maxLimit2) then the cone axis is the X axis in actor0's space. + If the limits are not symmetric, the function rotates the cone axis accordingly so that limits remain symmetric for PhysX. If this happens, + the initial joint frames will be different for both actors. By default minLimit1/maxLimit1 are limits around the joint's Y axis, and + minLimit2/maxLimit2 are limits around the joint's Z axis. + + The function creates hard limits, and uses PhysX's default contact distance parameter. + + Limits are expressed in radians. Allowed range is ]-PI;PI[. + + The cone axis is equivalent to the twist axis for the D6 joint. The twist motion is not limited. + + The returned apiroty and apirotz values can later be added to retrieved Y and Z swing angle values (from the joint), to remap + angle values to the given input range. + + \param[out] apiroty Amount of rotation around Y used to setup actor0's joint frame + \param[out] apirotz Amount of rotation around Z used to setup actor0's joint frame + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] minLimit1 Min angular limit along the joint frame's second axis (first axis = cone axis) + \param[in] maxLimit1 Max angular limit along the joint frame's second axis (first axis = cone axis) + \param[in] minLimit2 Min angular limit along the joint frame's third axis (first axis = cone axis) + \param[in] maxLimit2 Max angular limit along the joint frame's third axis (first axis = cone axis) + \param[in] useD6 True to use a PxD6Joint, false to use a PxSphericalJoint; + + \return The created joint. + + @see PxD6Joint PxSphericalJoint +*/ +PxJoint* PxD6JointCreate_GenericCone(float& apiroty, float& apirotz, PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, float minLimit1, float maxLimit1, float minLimit2, float maxLimit2, bool useD6); + + +/** + \brief Helper function to create a D6 joint with pyramidal swing limits. + + This function supports a pyramid limit shape, defined by two pairs of angular limit values. This can be used to create an asymmetric pyramid. If the + angular limit values are symmetric (i.e. minLimit1=-maxLimit1 and minLimit2=-maxLimit2) then the pyramid axis is the X axis in actor0's space. + By default minLimit1/maxLimit1 are limits around the joint's Y axis, and minLimit2/maxLimit2 are limits around the joint's Z axis. + + The function creates hard limits, and uses PhysX's default contact distance parameter. + + Limits are expressed in radians. Allowed range is ]-PI;PI[. + + The pyramid axis is equivalent to the twist axis for the D6 joint. The twist motion is not limited. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos0 The position of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localPos1 The position of the joint relative to actor1 + \param[in] axis The pyramid axis, expressed in the actors' local space + \param[in] minLimit1 Min angular limit along the joint frame's second axis (first axis = pyramid axis) + \param[in] maxLimit1 Max angular limit along the joint frame's second axis (first axis = pyramid axis) + \param[in] minLimit2 Min angular limit along the joint frame's third axis (first axis = pyramid axis) + \param[in] maxLimit2 Max angular limit along the joint frame's third axis (first axis = pyramid axis) + + \return The created joint. + + @see PxD6Joint +*/ +PxJoint* PxD6JointCreate_Pyramid(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, + float minLimit1, float maxLimit1, float minLimit2, float maxLimit2); + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxDefaultAllocator.h b/src/PhysX/physx/include/extensions/PxDefaultAllocator.h new file mode 100644 index 000000000..86405a44b --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDefaultAllocator.h @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_DEFAULT_ALLOCATOR_H +#define PX_DEFAULT_ALLOCATOR_H +/** \addtogroup extensions + @{ +*/ + +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxAssert.h" +#include "common/PxPhysXCommonConfig.h" + +#include + +#if PX_WINDOWS || PX_LINUX_FAMILY || PX_SWITCH +#include +#endif + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#if PX_WINDOWS +// on win32 we only have 8-byte alignment guaranteed, but the CRT provides special aligned allocation fns +PX_FORCE_INLINE void* platformAlignedAlloc(size_t size) +{ + return _aligned_malloc(size, 16); +} + +PX_FORCE_INLINE void platformAlignedFree(void* ptr) +{ + _aligned_free(ptr); +} +#elif PX_LINUX_FAMILY || PX_SWITCH +PX_FORCE_INLINE void* platformAlignedAlloc(size_t size) +{ + return ::memalign(16, size); +} + +PX_FORCE_INLINE void platformAlignedFree(void* ptr) +{ + ::free(ptr); +} +#else +// on all other platforms we get 16-byte alignment by default +PX_FORCE_INLINE void* platformAlignedAlloc(size_t size) +{ + return ::malloc(size); +} + +PX_FORCE_INLINE void platformAlignedFree(void* ptr) +{ + ::free(ptr); +} +#endif + +/** +\brief default implementation of the allocator interface required by the SDK +*/ +class PxDefaultAllocator : public PxAllocatorCallback +{ +public: + void* allocate(size_t size, const char*, const char*, int) + { + void* ptr = platformAlignedAlloc(size); + PX_ASSERT((reinterpret_cast(ptr) & 15)==0); + return ptr; + } + + void deallocate(void* ptr) + { + platformAlignedFree(ptr); + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h b/src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h new file mode 100644 index 000000000..458676b32 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_DEFAULT_CPU_DISPATCHER_H +#define PX_PHYSICS_EXTENSIONS_DEFAULT_CPU_DISPATCHER_H +/** \addtogroup extensions + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "task/PxCpuDispatcher.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief A default implementation for a CPU task dispatcher. + +@see PxDefaultCpuDispatcherCreate() PxCpuDispatcher +*/ +class PxDefaultCpuDispatcher: public PxCpuDispatcher +{ +public: + /** + \brief Deletes the dispatcher. + + Do not keep a reference to the deleted instance. + + @see PxDefaultCpuDispatcherCreate() + */ + virtual void release() = 0; + + /** + \brief Enables profiling at task level. + + \note By default enabled only in profiling builds. + + \param[in] runProfiled True if tasks should be profiled. + */ + virtual void setRunProfiled(bool runProfiled) = 0; + + /** + \brief Checks if profiling is enabled at task level. + + \return True if tasks should be profiled. + */ + virtual bool getRunProfiled() const = 0; +}; + + +/** +\brief Create default dispatcher, extensions SDK needs to be initialized first. + +\param[in] numThreads Number of worker threads the dispatcher should use. +\param[in] affinityMasks Array with affinity mask for each thread. If not defined, default masks will be used. + +\note numThreads may be zero in which case no worker thread are initialized and +simulation tasks will be executed on the thread that calls PxScene::simulate() + +@see PxDefaultCpuDispatcher +*/ +PxDefaultCpuDispatcher* PxDefaultCpuDispatcherCreate(PxU32 numThreads, PxU32* affinityMasks = NULL); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h b/src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h new file mode 100644 index 000000000..7880c12ed --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h @@ -0,0 +1,62 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_EXTENSIONS_DEFAULT_ERROR_CALLBACK_H +#define PX_PHYSICS_EXTENSIONS_DEFAULT_ERROR_CALLBACK_H + +#include "foundation/PxErrorCallback.h" +#include "PxPhysXConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief default implementation of the error callback + + This class is provided in order to enable the SDK to be started with the minimum of user code. Typically an application + will use its own error callback, and log the error to file or otherwise make it visible. Warnings and error messages from + the SDK are usually indicative that changes are required in order for PhysX to function correctly, and should not be ignored. + */ + + class PxDefaultErrorCallback : public PxErrorCallback + { + public: + PxDefaultErrorCallback(); + ~PxDefaultErrorCallback(); + + virtual void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line); + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif diff --git a/src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h b/src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h new file mode 100644 index 000000000..c4edb5e11 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h @@ -0,0 +1,263 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_DEFAULTSIMULATIONFILTERSHADER_H +#define PX_PHYSICS_EXTENSIONS_DEFAULTSIMULATIONFILTERSHADER_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" + +#include "PxFiltering.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxActor; + +/** +\brief 64-bit mask used for collision filtering. + +The collision filtering equation for 2 objects o0 and o1 is: + +
             (G0 op0 K0) op2 (G1 op1 K1) == b 
            + +with + +
              +
            • G0 = PxGroupsMask for object o0. See PxSetGroupsMask
            • +
            • G1 = PxGroupsMask for object o1. See PxSetGroupsMask
            • +
            • K0 = filtering constant 0. See PxSetFilterConstants
            • +
            • K1 = filtering constant 1. See PxSetFilterConstants
            • +
            • b = filtering boolean. See PxSetFilterBool
            • +
            • op0, op1, op2 = filtering operations. See PxSetFilterOps
            • +
            + +If the filtering equation is true, collision detection is enabled. + +@see PxSetFilterOps() +*/ +class PxGroupsMask +{ +public: + PX_INLINE PxGroupsMask():bits0(0),bits1(0),bits2(0),bits3(0) {} + PX_INLINE ~PxGroupsMask() {} + + PxU16 bits0, bits1, bits2, bits3; +}; + +/** +\brief Collision filtering operations. + +@see PxGroupsMask +*/ +struct PxFilterOp +{ + enum Enum + { + PX_FILTEROP_AND, + PX_FILTEROP_OR, + PX_FILTEROP_XOR, + PX_FILTEROP_NAND, + PX_FILTEROP_NOR, + PX_FILTEROP_NXOR, + PX_FILTEROP_SWAP_AND + }; +}; + +/** +\brief Implementation of a simple filter shader that emulates PhysX 2.8.x filtering + +This shader provides the following logic: +\li If one of the two filter objects is a trigger, the pair is acccepted and #PxPairFlag::eTRIGGER_DEFAULT will be used for trigger reports +\li Else, if the filter mask logic (see further below) discards the pair it will be suppressed (#PxFilterFlag::eSUPPRESS) +\li Else, the pair gets accepted and collision response gets enabled (#PxPairFlag::eCONTACT_DEFAULT) + +Filter mask logic: +Given the two #PxFilterData structures fd0 and fd1 of two collision objects, the pair passes the filter if the following +conditions are met: + + 1) Collision groups of the pair are enabled + 2) Collision filtering equation is satisfied + +@see PxSimulationFilterShader +*/ + +PxFilterFlags PxDefaultSimulationFilterShader( + PxFilterObjectAttributes attributes0, + PxFilterData filterData0, + PxFilterObjectAttributes attributes1, + PxFilterData filterData1, + PxPairFlags& pairFlags, + const void* constantBlock, + PxU32 constantBlockSize); + +/** + \brief Determines if collision detection is performed between a pair of groups + + \note Collision group is an integer between 0 and 31. + + \param[in] group1 First Group + \param[in] group2 Second Group + + \return True if the groups could collide + + @see PxSetGroupCollisionFlag +*/ +bool PxGetGroupCollisionFlag(const PxU16 group1, const PxU16 group2); + +/** + \brief Specifies if collision should be performed by a pair of groups + + \note Collision group is an integer between 0 and 31. + + \param[in] group1 First Group + \param[in] group2 Second Group + \param[in] enable True to enable collision between the groups + + @see PxGetGroupCollisionFlag +*/ +void PxSetGroupCollisionFlag(const PxU16 group1, const PxU16 group2, const bool enable); + +/** + \brief Retrieves the value set with PxSetGroup() + + \note Collision group is an integer between 0 and 31. + + \param[in] actor The actor + + \return The collision group this actor belongs to + + @see PxSetGroup +*/ +PxU16 PxGetGroup(const PxActor& actor); + +/** + \brief Sets which collision group this actor is part of + + \note Collision group is an integer between 0 and 31. + + \param[in] actor The actor + \param[in] collisionGroup Collision group this actor belongs to + + @see PxGetGroup +*/ +void PxSetGroup(PxActor& actor, const PxU16 collisionGroup); + +/** +\brief Retrieves filtering operation. See comments for PxGroupsMask + +\param[out] op0 First filter operator. +\param[out] op1 Second filter operator. +\param[out] op2 Third filter operator. + +@see PxSetFilterOps PxSetFilterBool PxSetFilterConstants +*/ +void PxGetFilterOps(PxFilterOp::Enum& op0, PxFilterOp::Enum& op1, PxFilterOp::Enum& op2); + +/** +\brief Setups filtering operations. See comments for PxGroupsMask + +\param[in] op0 Filter op 0. +\param[in] op1 Filter op 1. +\param[in] op2 Filter op 2. + +@see PxSetFilterBool PxSetFilterConstants +*/ +void PxSetFilterOps(const PxFilterOp::Enum& op0, const PxFilterOp::Enum& op1, const PxFilterOp::Enum& op2); + +/** +\brief Retrieves filtering's boolean value. See comments for PxGroupsMask + +\return flag Boolean value for filter. + +@see PxSetFilterBool PxSetFilterConstants +*/ +bool PxGetFilterBool(); + +/** +\brief Setups filtering's boolean value. See comments for PxGroupsMask + +\param[in] enable Boolean value for filter. + +@see PxSetFilterOps PxSsetFilterConstants +*/ +void PxSetFilterBool(const bool enable); + +/** +\brief Gets filtering constant K0 and K1. See comments for PxGroupsMask + +\param[out] c0 the filtering constants, as a mask. See #PxGroupsMask. +\param[out] c1 the filtering constants, as a mask. See #PxGroupsMask. + +@see PxSetFilterOps PxSetFilterBool PxSetFilterConstants +*/ +void PxGetFilterConstants(PxGroupsMask& c0, PxGroupsMask& c1); + +/** +\brief Setups filtering's K0 and K1 value. See comments for PxGroupsMask + +\param[in] c0 The new group mask. See #PxGroupsMask. +\param[in] c1 The new group mask. See #PxGroupsMask. + +@see PxSetFilterOps PxSetFilterBool PxGetFilterConstants +*/ +void PxSetFilterConstants(const PxGroupsMask& c0, const PxGroupsMask& c1); + +/** +\brief Gets 64-bit mask used for collision filtering. See comments for PxGroupsMask + +\param[in] actor The actor + +\return The group mask for the actor. + +@see PxSetGroupsMask() +*/ +PxGroupsMask PxGetGroupsMask(const PxActor& actor); + +/** +\brief Sets 64-bit mask used for collision filtering. See comments for PxGroupsMask + +\param[in] actor The actor +\param[in] mask The group mask to set for the actor. + +@see PxGetGroupsMask() +*/ +void PxSetGroupsMask(PxActor& actor, const PxGroupsMask& mask); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxDefaultStreams.h b/src/PhysX/physx/include/extensions/PxDefaultStreams.h new file mode 100644 index 000000000..9eddeda5e --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDefaultStreams.h @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_DEFAULT_STREAMS_H +#define PX_PHYSICS_EXTENSIONS_DEFAULT_STREAMS_H +/** \addtogroup extensions + @{ +*/ + +#include +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxIO.h" +#include "PxFoundation.h" + +typedef FILE* PxFileHandle; + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief default implementation of a memory write stream + +@see PxOutputStream +*/ + +class PxDefaultMemoryOutputStream: public PxOutputStream +{ +public: + PxDefaultMemoryOutputStream(PxAllocatorCallback &allocator = PxGetFoundation().getAllocatorCallback()); + virtual ~PxDefaultMemoryOutputStream(); + + virtual PxU32 write(const void* src, PxU32 count); + + virtual PxU32 getSize() const { return mSize; } + virtual PxU8* getData() const { return mData; } + +private: + PxDefaultMemoryOutputStream(const PxDefaultMemoryOutputStream&); + PxDefaultMemoryOutputStream& operator=(const PxDefaultMemoryOutputStream&); + + PxAllocatorCallback& mAllocator; + PxU8* mData; + PxU32 mSize; + PxU32 mCapacity; +}; + +/** +\brief default implementation of a memory read stream + +@see PxInputData +*/ + +class PxDefaultMemoryInputData: public PxInputData +{ +public: + PxDefaultMemoryInputData(PxU8* data, PxU32 length); + + virtual PxU32 read(void* dest, PxU32 count); + virtual PxU32 getLength() const; + virtual void seek(PxU32 pos); + virtual PxU32 tell() const; + +private: + PxU32 mSize; + const PxU8* mData; + PxU32 mPos; +}; + + + +/** +\brief default implementation of a file write stream + +@see PxOutputStream +*/ + +class PxDefaultFileOutputStream: public PxOutputStream +{ +public: + PxDefaultFileOutputStream(const char* name); + virtual ~PxDefaultFileOutputStream(); + + virtual PxU32 write(const void* src, PxU32 count); + virtual bool isValid(); +private: + PxFileHandle mFile; +}; + + +/** +\brief default implementation of a file read stream + +@see PxInputData +*/ + +class PxDefaultFileInputData: public PxInputData +{ +public: + PxDefaultFileInputData(const char* name); + virtual ~PxDefaultFileInputData(); + + virtual PxU32 read(void* dest, PxU32 count); + virtual void seek(PxU32 pos); + virtual PxU32 tell() const; + virtual PxU32 getLength() const; + + bool isValid() const; +private: + PxFileHandle mFile; + PxU32 mLength; +}; + +#if !PX_DOXYGEN +} +#endif + +/** @} */ + +#endif + diff --git a/src/PhysX/physx/include/extensions/PxDistanceJoint.h b/src/PhysX/physx/include/extensions/PxDistanceJoint.h new file mode 100644 index 000000000..bd752d25d --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxDistanceJoint.h @@ -0,0 +1,269 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_DISTANCEJOINT_H +#define PX_DISTANCEJOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxDistanceJoint; + +/** +\brief Create a distance Joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxDistanceJoint +*/ +PxDistanceJoint* PxDistanceJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + + +/** +\brief flags for configuring the drive of a PxDistanceJoint + +@see PxDistanceJoint +*/ +struct PxDistanceJointFlag +{ + enum Enum + { + eMAX_DISTANCE_ENABLED = 1<<1, + eMIN_DISTANCE_ENABLED = 1<<2, + eSPRING_ENABLED = 1<<3 + }; +}; + +typedef PxFlags PxDistanceJointFlags; +PX_FLAGS_OPERATORS(PxDistanceJointFlag::Enum, PxU16) + +/** +\brief a joint that maintains an upper or lower bound (or both) on the distance between two points on different objects + +@see PxDistanceJointCreate PxJoint +*/ +class PxDistanceJoint : public PxJoint +{ +public: + + /** + \brief Return the current distance of the joint + */ + virtual PxReal getDistance() const = 0; + + /** + \brief Set the allowed minimum distance for the joint. + + The minimum distance must be no more than the maximum distance + + Default 0.0f + Range [0, PX_MAX_F32) + + \param[in] distance the minimum distance + + @see PxDistanceJoint::minDistance, PxDistanceJointFlag::eMIN_DISTANCE_ENABLED getMinDistance() + */ + virtual void setMinDistance(PxReal distance) = 0; + + /** + \brief Get the allowed minimum distance for the joint. + + \return the allowed minimum distance + + @see PxDistanceJoint::minDistance, PxDistanceJointFlag::eMIN_DISTANCE_ENABLED setMinDistance() + */ + virtual PxReal getMinDistance() const = 0; + + /** + \brief Set the allowed maximum distance for the joint. + + The maximum distance must be no less than the minimum distance. + + Default 0.0f + Range [0, PX_MAX_F32) + + \param[in] distance the maximum distance + + @see PxDistanceJoint::maxDistance, PxDistanceJointFlag::eMAX_DISTANCE_ENABLED getMinDistance() + */ + virtual void setMaxDistance(PxReal distance) = 0; + + /** + \brief Get the allowed maximum distance for the joint. + + \return the allowed maximum distance + + @see PxDistanceJoint::maxDistance, PxDistanceJointFlag::eMAX_DISTANCE_ENABLED setMaxDistance() + */ + virtual PxReal getMaxDistance() const = 0; + + /** + \brief Set the error tolerance of the joint. + + \param[in] tolerance the distance beyond the allowed range at which the joint becomes active + + @see PxDistanceJoint::tolerance, getTolerance() + */ + virtual void setTolerance(PxReal tolerance) = 0; + + /** + \brief Get the error tolerance of the joint. + + the distance beyond the joint's [min, max] range before the joint becomes active. + + Default 0.25f * PxTolerancesScale::length + Range (0, PX_MAX_F32) + + This value should be used to ensure that if the minimum distance is zero and the + spring function is in use, the rest length of the spring is non-zero. + + @see PxDistanceJoint::tolerance, setTolerance() + */ + virtual PxReal getTolerance() const = 0; + + /** + \brief Set the strength of the joint spring. + + The spring is used if enabled, and the distance exceeds the range [min-error, max+error]. + + Default 0.0f + Range [0, PX_MAX_F32) + + \param[in] stiffness the spring strength of the joint + + @see PxDistanceJointFlag::eSPRING_ENABLED getStiffness() + */ + virtual void setStiffness(PxReal stiffness) = 0; + + /** + \brief Get the strength of the joint spring. + + \return stiffness the spring strength of the joint + + @see PxDistanceJointFlag::eSPRING_ENABLED setStiffness() + */ + virtual PxReal getStiffness() const = 0; + + /** + \brief Set the damping of the joint spring. + + The spring is used if enabled, and the distance exceeds the range [min-error, max+error]. + + Default 0.0f + Range [0, PX_MAX_F32) + + \param[in] damping the degree of damping of the joint spring of the joint + + @see PxDistanceJointFlag::eSPRING_ENABLED setDamping() + */ + virtual void setDamping(PxReal damping) = 0; + + /** + \brief Get the damping of the joint spring. + + \return the degree of damping of the joint spring of the joint + + @see PxDistanceJointFlag::eSPRING_ENABLED setDamping() + */ + virtual PxReal getDamping() const = 0; + + /** + \brief Set the flags specific to the Distance Joint. + + Default PxDistanceJointFlag::eMAX_DISTANCE_ENABLED + + \param[in] flags The joint flags. + + @see PxDistanceJointFlag setFlag() getFlags() + */ + virtual void setDistanceJointFlags(PxDistanceJointFlags flags) = 0; + + /** + \brief Set a single flag specific to a Distance Joint to true or false. + + \param[in] flag The flag to set or clear. + \param[in] value the value to which to set the flag + + @see PxDistanceJointFlag, getFlags() setFlags() + */ + virtual void setDistanceJointFlag(PxDistanceJointFlag::Enum flag, bool value) = 0; + + /** + \brief Get the flags specific to the Distance Joint. + + \return the joint flags + + @see PxDistanceJoint::flags, PxDistanceJointFlag setFlag() setFlags() + */ + virtual PxDistanceJointFlags getDistanceJointFlags() const = 0; + + /** + \brief Returns string name of PxDistanceJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxDistanceJoint"; } + +protected: + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxDistanceJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxDistanceJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxDistanceJoint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxExtensionsAPI.h b/src/PhysX/physx/include/extensions/PxExtensionsAPI.h new file mode 100644 index 000000000..6647b3cc2 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxExtensionsAPI.h @@ -0,0 +1,88 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_EXTENSIONS_API_H +#define PX_EXTENSIONS_API_H +/** \addtogroup extensions + @{ +*/ + +#include "foundation/PxErrorCallback.h" +#include "extensions/PxDefaultAllocator.h" +#include "extensions/PxConstraintExt.h" +#include "extensions/PxDistanceJoint.h" +#include "extensions/PxContactJoint.h" +#include "extensions/PxFixedJoint.h" +#include "extensions/PxPrismaticJoint.h" +#include "extensions/PxRevoluteJoint.h" +#include "extensions/PxSphericalJoint.h" +#include "extensions/PxD6Joint.h" +#include "extensions/PxDefaultSimulationFilterShader.h" +#include "extensions/PxDefaultErrorCallback.h" +#include "extensions/PxDefaultStreams.h" +#include "extensions/PxRigidActorExt.h" +#include "extensions/PxRigidBodyExt.h" +#include "extensions/PxShapeExt.h" +#include "extensions/PxTriangleMeshExt.h" +#include "extensions/PxSerialization.h" +#include "extensions/PxDefaultCpuDispatcher.h" +#include "extensions/PxSmoothNormals.h" +#include "extensions/PxSimpleFactory.h" +#include "extensions/PxStringTableExt.h" +#include "extensions/PxBroadPhaseExt.h" +#include "extensions/PxMassProperties.h" +#include "extensions/PxSceneQueryExt.h" + +/** \brief Initialize the PhysXExtensions library. + +This should be called before calling any functions or methods in extensions which may require allocation. +\note This function does not need to be called before creating a PxDefaultAllocator object. + +\param physics a PxPhysics object +\param pvd an PxPvd (PhysX Visual Debugger) object + +@see PxCloseExtensions PxFoundation PxPhysics +*/ + +PX_C_EXPORT bool PX_CALL_CONV PxInitExtensions(physx::PxPhysics& physics, physx::PxPvd* pvd); + +/** \brief Shut down the PhysXExtensions library. + +This function should be called to cleanly shut down the PhysXExtensions library before application exit. + +\note This function is required to be called to release foundation usage. + +@see PxInitExtensions +*/ + +PX_C_EXPORT void PX_CALL_CONV PxCloseExtensions(); + +/** @} */ +#endif // PX_EXTENSIONS_API_H diff --git a/src/PhysX/physx/include/extensions/PxFixedJoint.h b/src/PhysX/physx/include/extensions/PxFixedJoint.h new file mode 100644 index 000000000..fff4b84e9 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxFixedJoint.h @@ -0,0 +1,159 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_FIXEDJOINT_H +#define PX_FIXEDJOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxFixedJoint; + +/** +\brief Create a fixed joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxFixedJoint +*/ +PxFixedJoint* PxFixedJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + +/** + \brief A fixed joint permits no relative movement between two bodies. ie the bodies are glued together. + + \image html fixedJoint.png + + @see PxFixedJointCreate() PxJoint +*/ +class PxFixedJoint : public PxJoint +{ +public: + + /** + \brief Set the linear tolerance threshold for projection. Projection is enabled if PxConstraintFlag::ePROJECTION + is set for the joint. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0, PX_MAX_F32)
            + Default: 1e10f + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() PxJoint::setConstraintFlags() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionLinearTolerance(PxReal tolerance) = 0; + + /** + \brief Get the linear tolerance threshold for projection. + + \return the linear tolerance threshold + + @see setProjectionLinearTolerance() PxJoint::setConstraintFlag() + */ + virtual PxReal getProjectionLinearTolerance() const = 0; + + /** + \brief Set the angular tolerance threshold for projection. Projection is enabled if + PxConstraintFlag::ePROJECTION is set for the joint. + + If the joint deviates by more than this angle around its locked angular degrees of freedom, + the solver will move the bodies to close the angle. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0,Pi]
            + Default: Pi + + \param[in] tolerance the angular tolerance threshold in radians + + @see getProjectionAngularTolerance() PxJoint::setConstraintFlag() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionAngularTolerance(PxReal tolerance) = 0; + + /** + \brief Get the angular tolerance threshold for projection. + + \return the angular tolerance threshold in radians + + @see setProjectionAngularTolerance() + */ + virtual PxReal getProjectionAngularTolerance() const = 0; + + /** + \brief Returns string name of PxFixedJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxFixedJoint"; } + +protected: + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxFixedJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxFixedJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxFixedJoint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxJoint.h b/src/PhysX/physx/include/extensions/PxJoint.h new file mode 100644 index 000000000..14898d6be --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxJoint.h @@ -0,0 +1,419 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_JOINTCONSTRAINT_H +#define PX_JOINTCONSTRAINT_H +/** \addtogroup extensions + @{ +*/ + +#include "foundation/PxTransform.h" +#include "PxRigidActor.h" +#include "PxConstraint.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxRigidActor; +class PxScene; +class PxPhysics; +class PxConstraint; + +/** +\brief an enumeration of PhysX' built-in joint types + +@see PxJoint +*/ +struct PxJointConcreteType +{ + enum Enum + { + eSPHERICAL = PxConcreteType::eFIRST_PHYSX_EXTENSION, + eREVOLUTE, + ePRISMATIC, + eFIXED, + eDISTANCE, + eD6, + eCONTACT, + eLast + }; +}; + +PX_DEFINE_TYPEINFO(PxJoint, PxConcreteType::eUNDEFINED) +PX_DEFINE_TYPEINFO(PxD6Joint, PxJointConcreteType::eD6) +PX_DEFINE_TYPEINFO(PxDistanceJoint, PxJointConcreteType::eDISTANCE) +PX_DEFINE_TYPEINFO(PxContactJoint, PxJointConcreteType::eCONTACT) +PX_DEFINE_TYPEINFO(PxFixedJoint, PxJointConcreteType::eFIXED) +PX_DEFINE_TYPEINFO(PxPrismaticJoint, PxJointConcreteType::ePRISMATIC) +PX_DEFINE_TYPEINFO(PxRevoluteJoint, PxJointConcreteType::eREVOLUTE) +PX_DEFINE_TYPEINFO(PxSphericalJoint, PxJointConcreteType::eSPHERICAL) + + +/** +\brief an enumeration for specifying one or other of the actors referenced by a joint + +@see PxJoint +*/ + +struct PxJointActorIndex +{ + enum Enum + { + eACTOR0, + eACTOR1, + COUNT + }; +}; + +/** +\brief a base interface providing common functionality for PhysX joints +*/ + +class PxJoint : public PxBase +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + /** + \brief Set the actors for this joint. + + An actor may be NULL to indicate the world frame. At most one of the actors may be NULL. + + \param[in] actor0 the first actor. + \param[in] actor1 the second actor + + @see getActors() + */ + virtual void setActors(PxRigidActor* actor0, PxRigidActor* actor1) = 0; + + /** + \brief Get the actors for this joint. + + \param[out] actor0 the first actor. + \param[out] actor1 the second actor + + @see setActors() + */ + virtual void getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const = 0; + + /** + \brief Set the joint local pose for an actor. + + This is the relative pose which locates the joint frame relative to the actor. + + \param[in] actor 0 for the first actor, 1 for the second actor. + \param[in] localPose the local pose for the actor this joint + + @see getLocalPose() + */ + virtual void setLocalPose(PxJointActorIndex::Enum actor, const PxTransform& localPose) = 0; + + /** + \brief get the joint local pose for an actor. + + \param[in] actor 0 for the first actor, 1 for the second actor. + + return the local pose for this joint + + @see setLocalPose() + */ + virtual PxTransform getLocalPose(PxJointActorIndex::Enum actor) const = 0; + + /** + \brief get the relative pose for this joint + + This function returns the pose of the joint frame of actor1 relative to actor0 + + */ + virtual PxTransform getRelativeTransform() const = 0; + + /** + \brief get the relative linear velocity of the joint + + This function returns the linear velocity of the origin of the constraint frame of actor1, relative to the origin of the constraint + frame of actor0. The value is returned in the constraint frame of actor0 + */ + virtual PxVec3 getRelativeLinearVelocity() const = 0; + + /** + \brief get the relative angular velocity of the joint + + This function returns the angular velocity of actor1 relative to actor0. The value is returned in the constraint frame of actor0 + */ + virtual PxVec3 getRelativeAngularVelocity() const = 0; + + /** + \brief set the break force for this joint. + + if the constraint force or torque on the joint exceeds the specified values, the joint will break, + at which point it will not constrain the two actors and the flag PxConstraintFlag::eBROKEN will be set. The + force and torque are measured in the joint frame of the first actor + + \param[in] force the maximum force the joint can apply before breaking + \param[in] torque the maximum torque the joint can apply before breaking + */ + virtual void setBreakForce(PxReal force, PxReal torque) = 0; + + /** + \brief get the break force for this joint. + + \param[out] force the maximum force the joint can apply before breaking + \param[out] torque the maximum torque the joint can apply before breaking + + @see setBreakForce() + */ + virtual void getBreakForce(PxReal& force, PxReal& torque) const = 0; + + /** + \brief set the constraint flags for this joint. + + \param[in] flags the constraint flags + + @see PxConstraintFlag + */ + virtual void setConstraintFlags(PxConstraintFlags flags) = 0; + + /** + \brief set a constraint flags for this joint to a specified value. + + \param[in] flag the constraint flag + \param[in] value the value to which to set the flag + + @see PxConstraintFlag + */ + virtual void setConstraintFlag(PxConstraintFlag::Enum flag, bool value) = 0; + + /** + \brief get the constraint flags for this joint. + + \return the constraint flags + + @see PxConstraintFlag + */ + virtual PxConstraintFlags getConstraintFlags() const = 0; + + /** + \brief set the inverse mass scale for actor0. + + \param[in] invMassScale the scale to apply to the inverse mass of actor 0 for resolving this constraint + + @see getInvMassScale0 + */ + virtual void setInvMassScale0(PxReal invMassScale) = 0; + + /** + \brief get the inverse mass scale for actor0. + + \return inverse mass scale for actor0 + + @see setInvMassScale0 + */ + virtual PxReal getInvMassScale0() const = 0; + + /** + \brief set the inverse inertia scale for actor0. + + \param[in] invInertiaScale the scale to apply to the inverse inertia of actor0 for resolving this constraint + + @see getInvMassScale0 + */ + virtual void setInvInertiaScale0(PxReal invInertiaScale) = 0; + + /** + \brief get the inverse inertia scale for actor0. + + \return inverse inertia scale for actor0 + + @see setInvInertiaScale0 + */ + virtual PxReal getInvInertiaScale0() const = 0; + + /** + \brief set the inverse mass scale for actor1. + + \param[in] invMassScale the scale to apply to the inverse mass of actor 1 for resolving this constraint + + @see getInvMassScale1 + */ + virtual void setInvMassScale1(PxReal invMassScale) = 0; + + /** + \brief get the inverse mass scale for actor1. + + \return inverse mass scale for actor1 + + @see setInvMassScale1 + */ + virtual PxReal getInvMassScale1() const = 0; + + /** + \brief set the inverse inertia scale for actor1. + + \param[in] invInertiaScale the scale to apply to the inverse inertia of actor1 for resolving this constraint + + @see getInvInertiaScale1 + */ + virtual void setInvInertiaScale1(PxReal invInertiaScale) = 0; + + /** + \brief get the inverse inertia scale for actor1. + + \return inverse inertia scale for actor1 + + @see setInvInertiaScale1 + */ + virtual PxReal getInvInertiaScale1() const = 0; + + /** + \brief Retrieves the PxConstraint corresponding to this joint. + + This can be used to determine, among other things, the force applied at the joint. + + \return the constraint + */ + virtual PxConstraint* getConstraint() const = 0; + + /** + \brief Sets a name string for the object that can be retrieved with getName(). + + This is for debugging and is not used by the SDK. The string is not copied by the SDK, + only the pointer is stored. + + \param[in] name String to set the objects name to. + + @see getName() + */ + virtual void setName(const char* name) = 0; + + /** + \brief Retrieves the name string set with setName(). + + \return Name string associated with object. + + @see setName() + */ + virtual const char* getName() const = 0; + + /** + \brief Deletes the joint. + + \note This call does not wake up the connected rigid bodies. + */ + virtual void release() = 0; + + /** + \brief Retrieves the scene which this joint belongs to. + + \return Owner Scene. NULL if not part of a scene. + + @see PxScene + */ + virtual PxScene* getScene() const = 0; + + void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. + + //serialization + + /** + \brief Put class meta data in stream, used for serialization + */ + static void getBinaryMetaData(PxOutputStream& stream); + + //~serialization + +protected: + virtual ~PxJoint() {} + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxJoint(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags), userData(NULL) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxJoint(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxJoint", name) || PxBase::isKindOf(name); } + + //~serialization +}; + +class PxSpring +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + PxReal stiffness; //!< the spring strength of the drive: that is, the force proportional to the position error + PxReal damping; //!< the damping strength of the drive: that is, the force proportional to the velocity error + + PxSpring(PxReal stiffness_, PxReal damping_): stiffness(stiffness_), damping(damping_) {} +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** \brief Helper function to setup a joint's global frame + + This replaces the following functions from previous SDK versions: + + void NxJointDesc::setGlobalAnchor(const NxVec3& wsAnchor); + void NxJointDesc::setGlobalAxis(const NxVec3& wsAxis); + + The function sets the joint's localPose using world-space input parameters. + + \param[in] wsAnchor Global frame anchor point. Range: position vector + \param[in] wsAxis Global frame axis. Range: direction vector + \param[in,out] joint Joint having its global frame set. +*/ + +PX_C_EXPORT void PX_CALL_CONV PxSetJointGlobalFrame(physx::PxJoint& joint, const physx::PxVec3* wsAnchor, const physx::PxVec3* wsAxis); + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxJointLimit.h b/src/PhysX/physx/include/extensions/PxJointLimit.h new file mode 100644 index 000000000..85a4ec82f --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxJointLimit.h @@ -0,0 +1,566 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_EXTENSIONS_JOINT_LIMIT +#define PX_EXTENSIONS_JOINT_LIMIT +/** \addtogroup extensions + @{ +*/ + +#include "foundation/PxMath.h" +#include "PxPhysXConfig.h" +#include "common/PxTolerancesScale.h" +#include "PxJoint.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Describes the parameters for a joint limit. + +Limits are enabled or disabled by setting flags or other configuration parameters joints, see the +documentation for specific joint types for details. +*/ +class PxJointLimitParameters +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief Controls the amount of bounce when the joint hits a limit. + + A restitution value of 1.0 causes the joint to bounce back with the velocity which it hit the limit. + A value of zero causes the joint to stop dead. + + In situations where the joint has many locked DOFs (e.g. 5) the restitution may not be applied + correctly. This is due to a limitation in the solver which causes the restitution velocity to become zero + as the solver enforces constraints on the other DOFs. + + This limitation applies to both angular and linear limits, however it is generally most apparent with limited + angular DOFs. Disabling joint projection and increasing the solver iteration count may improve this behavior + to some extent. + + Also, combining soft joint limits with joint drives driving against those limits may affect stability. + + Range: [0,1]
            + Default: 0.0 + */ + PxReal restitution; + + /** + determines the minimum impact velocity which will cause the joint to bounce + */ + PxReal bounceThreshold; + + /** + \brief if greater than zero, the limit is soft, i.e. a spring pulls the joint back to the limit + + Range: [0, PX_MAX_F32)
            + Default: 0.0 + */ + PxReal stiffness; + + /** + \brief if spring is greater than zero, this is the damping of the limit spring + + Range: [0, PX_MAX_F32)
            + Default: 0.0 + */ + PxReal damping; + + /** + \brief the distance inside the limit value at which the limit will be considered to be active by the + solver. As this value is made larger, the limit becomes active more quickly. It thus becomes less + likely to violate the extents of the limit, but more expensive. + + The contact distance should be less than the limit angle or distance, and in the case of a pair limit, + less than half the distance between the upper and lower bounds. Exceeding this value will result in + the limit being active all the time. + + Making this value too small can result in jitter around the limit. + + Default: depends on the joint + + @see PxPhysics::getTolerancesScale() + */ + PxReal contactDistance; + + PxJointLimitParameters() : + restitution (0.0f), + bounceThreshold (0.0f), + stiffness (0.0f), + damping (0.0f), + contactDistance (0.0f) + { + } + + PxJointLimitParameters(const PxJointLimitParameters& p) : + restitution (p.restitution), + bounceThreshold (p.bounceThreshold), + stiffness (p.stiffness), + damping (p.damping), + contactDistance (p.contactDistance) + { + } + + /** + \brief Returns true if the current settings are valid. + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxIsFinite(restitution) && restitution >= 0 && restitution <= 1 && + PxIsFinite(stiffness) && stiffness >= 0 && + PxIsFinite(damping) && damping >= 0 && + PxIsFinite(bounceThreshold) && bounceThreshold >= 0 && + PxIsFinite(contactDistance) && contactDistance >= 0; + } + + PX_INLINE bool isSoft() const + { + return damping>0 || stiffness>0; + } + +protected: + ~PxJointLimitParameters() {} +}; + + +/** +\brief Describes a one-sided linear limit. +*/ +class PxJointLinearLimit : public PxJointLimitParameters +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief the extent of the limit. + + Range: (0, PX_MAX_F32)
            + Default: PX_MAX_F32 + */ + PxReal value; + + /** + \brief construct a linear hard limit + + \param[in] scale A PxTolerancesScale struct. Should be the same as used when creating the PxPhysics object. + \param[in] extent The extent of the limit + \param[in] contactDist The distance from the limit at which it becomes active. Default is 0.01f scaled by the tolerance length scale + + @see PxJointLimitParameters PxTolerancesScale + */ + PxJointLinearLimit(const PxTolerancesScale& scale, PxReal extent, PxReal contactDist = -1.0f) + : value(extent) + { + PxJointLimitParameters::contactDistance = contactDist == -1.0f ? 0.01f*scale.length : contactDist; + } + + /** + \brief construct a linear soft limit + + \param[in] extent the extent of the limit + \param[in] spring the stiffness and damping parameters for the limit spring + + @see PxJointLimitParameters PxTolerancesScale + */ + PxJointLinearLimit(PxReal extent, const PxSpring& spring) : value(extent) + { + stiffness = spring.stiffness; + damping = spring.damping; + } + + /** + \brief Returns true if the limit is valid + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxJointLimitParameters::isValid() && + PxIsFinite(value) && + value > 0.0f; + } +}; + + +/** +\brief Describes a two-sided limit. +*/ +class PxJointLinearLimitPair : public PxJointLimitParameters +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief the range of the limit. The upper limit must be no lower than the + lower limit, and if they are equal the limited degree of freedom will be treated as locked. + + Range: See the joint on which the limit is used for details
            + Default: lower = -PX_MAX_F32/3, upper = PX_MAX_F32/3 + */ + PxReal upper, lower; + + /** + \brief Construct a linear hard limit pair. The lower distance value must be less than the upper distance value. + + \param[in] scale A PxTolerancesScale struct. Should be the same as used when creating the PxPhysics object. + \param[in] lowerLimit The lower distance of the limit + \param[in] upperLimit The upper distance of the limit + \param[in] contactDist The distance from the limit at which it becomes active. Default is the lesser of 0.01f scaled by the tolerance length scale, and 0.49 * (upperLimit - lowerLimit) + + @see PxJointLimitParameters PxTolerancesScale + */ + PxJointLinearLimitPair(const PxTolerancesScale& scale, PxReal lowerLimit = -PX_MAX_F32/3.0f, PxReal upperLimit = PX_MAX_F32/3.0f, PxReal contactDist = -1.0f) : + upper(upperLimit), + lower(lowerLimit) + { + PxJointLimitParameters::contactDistance = contactDist == -1.0f ? PxMin(scale.length * 0.01f, (upperLimit*0.49f-lowerLimit*0.49f)) : contactDist; + bounceThreshold = 2.0f*scale.length; + } + + /** + \brief construct a linear soft limit pair + + \param[in] lowerLimit The lower distance of the limit + \param[in] upperLimit The upper distance of the limit + \param[in] spring The stiffness and damping parameters of the limit spring + + @see PxJointLimitParameters PxTolerancesScale + */ + PxJointLinearLimitPair(PxReal lowerLimit, PxReal upperLimit, const PxSpring& spring) : + upper(upperLimit), + lower(lowerLimit) + { + stiffness = spring.stiffness; + damping = spring.damping; + } + + /** + \brief Returns true if the limit is valid. + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxJointLimitParameters::isValid() && + PxIsFinite(upper) && PxIsFinite(lower) && upper >= lower && + PxIsFinite(upper - lower); + } +}; + + +class PxJointAngularLimitPair : public PxJointLimitParameters +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief the range of the limit. The upper limit must be no lower than the lower limit. + + Unit: Angular: Radians + Range: See the joint on which the limit is used for details
            + Default: lower = -PI/2, upper = PI/2 + */ + PxReal upper, lower; + + /** + \brief construct an angular hard limit pair. + + The lower value must be less than the upper value. + + \param[in] lowerLimit The lower angle of the limit + \param[in] upperLimit The upper angle of the limit + \param[in] contactDist The distance from the limit at which it becomes active. Default is the lesser of 0.1 radians, and 0.49 * (upperLimit - lowerLimit) + + @see PxJointLimitParameters + */ + PxJointAngularLimitPair(PxReal lowerLimit, PxReal upperLimit, PxReal contactDist = -1.0f) : + upper(upperLimit), + lower(lowerLimit) + { + PxJointLimitParameters::contactDistance = contactDist ==-1.0f ? PxMin(0.1f, 0.49f*(upperLimit-lowerLimit)) : contactDist; + bounceThreshold = 0.5f; + } + + /** + \brief construct an angular soft limit pair. + + The lower value must be less than the upper value. + + \param[in] lowerLimit The lower angle of the limit + \param[in] upperLimit The upper angle of the limit + \param[in] spring The stiffness and damping of the limit spring + + @see PxJointLimitParameters + */ + PxJointAngularLimitPair(PxReal lowerLimit, PxReal upperLimit, const PxSpring& spring) : + upper(upperLimit), + lower(lowerLimit) + { + stiffness = spring.stiffness; + damping = spring.damping; + } + + /** + \brief Returns true if the limit is valid. + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxJointLimitParameters::isValid() && + PxIsFinite(upper) && PxIsFinite(lower) && upper >= lower; + } +}; + +/** +\brief Describes an elliptical conical joint limit. Note that very small or highly elliptical limit cones may +result in jitter. + +@see PxD6Joint PxSphericalJoint +*/ +class PxJointLimitCone : public PxJointLimitParameters +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief the maximum angle from the Y axis of the constraint frame. + + Unit: Angular: Radians + Range: Angular: (0,PI)
            + Default: PI/2 + */ + PxReal yAngle; + + /** + \brief the maximum angle from the Z-axis of the constraint frame. + + Unit: Angular: Radians + Range: Angular: (0,PI)
            + Default: PI/2 + */ + PxReal zAngle; + + /** + \brief Construct a cone hard limit. + + \param[in] yLimitAngle The limit angle from the Y-axis of the constraint frame + \param[in] zLimitAngle The limit angle from the Z-axis of the constraint frame + \param[in] contactDist The distance from the limit at which it becomes active. Default is the lesser of 0.1 radians, and 0.49 * the lower of the limit angles + + @see PxJointLimitParameters + */ + PxJointLimitCone(PxReal yLimitAngle, PxReal zLimitAngle, PxReal contactDist = -1.0f) : + yAngle(yLimitAngle), + zAngle(zLimitAngle) + { + PxJointLimitParameters::contactDistance = contactDist == -1.0f ? PxMin(0.1f, PxMin(yLimitAngle, zLimitAngle)*0.49f) : contactDist; + bounceThreshold = 0.5f; + } + + /** + \brief Construct a cone soft limit. + + \param[in] yLimitAngle The limit angle from the Y-axis of the constraint frame + \param[in] zLimitAngle The limit angle from the Z-axis of the constraint frame + \param[in] spring The stiffness and damping of the limit spring + + @see PxJointLimitParameters + */ + PxJointLimitCone(PxReal yLimitAngle, PxReal zLimitAngle, const PxSpring& spring) : + yAngle(yLimitAngle), + zAngle(zLimitAngle) + { + stiffness = spring.stiffness; + damping = spring.damping; + } + + /** + \brief Returns true if the limit is valid. + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxJointLimitParameters::isValid() && + PxIsFinite(yAngle) && yAngle>0 && yAngle0 && zAngleUnit:
            Angular: Radians + Range: Angular: (-PI,PI)
            + Default: -PI/2 + */ + PxReal yAngleMin; + + /** + \brief the maximum angle from the Y axis of the constraint frame. + + Unit: Angular: Radians + Range: Angular: (-PI,PI)
            + Default: PI/2 + */ + PxReal yAngleMax; + + /** + \brief the minimum angle from the Z-axis of the constraint frame. + + Unit: Angular: Radians + Range: Angular: (-PI,PI)
            + Default: -PI/2 + */ + PxReal zAngleMin; + + /** + \brief the maximum angle from the Z-axis of the constraint frame. + + Unit: Angular: Radians + Range: Angular: (-PI,PI)
            + Default: PI/2 + */ + PxReal zAngleMax; + + /** + \brief Construct a pyramid hard limit. + + \param[in] yLimitAngleMin The minimum limit angle from the Y-axis of the constraint frame + \param[in] yLimitAngleMax The maximum limit angle from the Y-axis of the constraint frame + \param[in] zLimitAngleMin The minimum limit angle from the Z-axis of the constraint frame + \param[in] zLimitAngleMax The maximum limit angle from the Z-axis of the constraint frame + \param[in] contactDist The distance from the limit at which it becomes active. Default is the lesser of 0.1 radians, and 0.49 * the lower of the limit angles + + @see PxJointLimitParameters + */ + PxJointLimitPyramid(PxReal yLimitAngleMin, PxReal yLimitAngleMax, PxReal zLimitAngleMin, PxReal zLimitAngleMax, PxReal contactDist = -1.0f) : + yAngleMin(yLimitAngleMin), + yAngleMax(yLimitAngleMax), + zAngleMin(zLimitAngleMin), + zAngleMax(zLimitAngleMax) + { + if(contactDist == -1.0f) + { + const PxReal contactDistY = PxMin(0.1f, 0.49f*(yLimitAngleMax - yLimitAngleMin)); + const PxReal contactDistZ = PxMin(0.1f, 0.49f*(zLimitAngleMax - zLimitAngleMin)); + PxJointLimitParameters::contactDistance = contactDist == PxMin(contactDistY, contactDistZ); + } + else + { + PxJointLimitParameters::contactDistance = contactDist; + } + + bounceThreshold = 0.5f; + } + + /** + \brief Construct a pyramid soft limit. + + \param[in] yLimitAngleMin The minimum limit angle from the Y-axis of the constraint frame + \param[in] yLimitAngleMax The maximum limit angle from the Y-axis of the constraint frame + \param[in] zLimitAngleMin The minimum limit angle from the Z-axis of the constraint frame + \param[in] zLimitAngleMax The maximum limit angle from the Z-axis of the constraint frame + \param[in] spring The stiffness and damping of the limit spring + + @see PxJointLimitParameters + */ + PxJointLimitPyramid(PxReal yLimitAngleMin, PxReal yLimitAngleMax, PxReal zLimitAngleMin, PxReal zLimitAngleMax, const PxSpring& spring) : + yAngleMin(yLimitAngleMin), + yAngleMax(yLimitAngleMax), + zAngleMin(zLimitAngleMin), + zAngleMax(zLimitAngleMax) + { + stiffness = spring.stiffness; + damping = spring.damping; + } + + /** + \brief Returns true if the limit is valid. + + \return true if the current settings are valid + */ + PX_INLINE bool isValid() const + { + return PxJointLimitParameters::isValid() && + PxIsFinite(yAngleMin) && yAngleMin>-PxPi && yAngleMin-PxPi && yAngleMax-PxPi && zAngleMin-PxPi && zAngleMax=yAngleMin && zAngleMax>=zAngleMin; + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxMassProperties.h b/src/PhysX/physx/include/extensions/PxMassProperties.h new file mode 100644 index 000000000..a5bc057a5 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxMassProperties.h @@ -0,0 +1,334 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_MASS_PROPERTIES_H +#define PX_PHYSICS_EXTENSIONS_MASS_PROPERTIES_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "foundation/PxMath.h" +#include "foundation/PxMathUtils.h" +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "geometry/PxGeometry.h" +#include "geometry/PxBoxGeometry.h" +#include "geometry/PxSphereGeometry.h" +#include "geometry/PxCapsuleGeometry.h" +#include "geometry/PxConvexMeshGeometry.h" +#include "geometry/PxConvexMesh.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Utility class to compute and manipulate mass and inertia tensor properties. + +In most cases #PxRigidBodyExt::updateMassAndInertia(), #PxRigidBodyExt::setMassAndUpdateInertia() should be enough to +setup the mass properties of a rigid body. This utility class targets users that need to customize the mass properties +computation. +*/ +class PxMassProperties +{ +public: + /** + \brief Default constructor. + */ + PX_FORCE_INLINE PxMassProperties() : inertiaTensor(PxIdentity), centerOfMass(0.0f), mass(1.0f) {} + + /** + \brief Construct from individual elements. + */ + PX_FORCE_INLINE PxMassProperties(const PxReal m, const PxMat33& inertiaT, const PxVec3& com) : inertiaTensor(inertiaT), centerOfMass(com), mass(m) {} + + /** + \brief Compute mass properties based on a provided geometry structure. + + This constructor assumes the geometry has a density of 1. Mass and inertia tensor scale linearly with density. + + \param[in] geometry The geometry to compute the mass properties for. Supported geometry types are: sphere, box, capsule and convex mesh. + */ + PxMassProperties(const PxGeometry& geometry) + { + switch (geometry.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& s = static_cast(geometry); + mass = (4.0f / 3.0f) * PxPi * s.radius * s.radius * s.radius; + inertiaTensor = PxMat33::createDiagonal(PxVec3(2.0f / 5.0f * mass * s.radius * s.radius)); + centerOfMass = PxVec3(0.0f); + } + break; + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& b = static_cast(geometry); + mass = b.halfExtents.x * b.halfExtents.y * b.halfExtents.z * 8.0f; + PxVec3 d2 = b.halfExtents.multiply(b.halfExtents); + inertiaTensor = PxMat33::createDiagonal(PxVec3(d2.y + d2.z, d2.x + d2.z, d2.x + d2.y)) * (mass * 1.0f / 3.0f); + centerOfMass = PxVec3(0.0f); + } + break; + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& c = static_cast(geometry); + PxReal r = c.radius, h = c.halfHeight; + mass = ((4.0f / 3.0f) * r + 2 * c.halfHeight) * PxPi * r * r; + + PxReal a = r*r*r * (8.0f / 15.0f) + h*r*r * (3.0f / 2.0f) + h*h*r * (4.0f / 3.0f) + h*h*h * (2.0f / 3.0f); + PxReal b = r*r*r * (8.0f / 15.0f) + h*r*r; + inertiaTensor = PxMat33::createDiagonal(PxVec3(b, a, a) * PxPi * r * r); + centerOfMass = PxVec3(0.0f); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& c = static_cast(geometry); + PxVec3 unscaledCoM; + PxMat33 unscaledInertiaTensorNonCOM; // inertia tensor of convex mesh in mesh local space + PxMat33 unscaledInertiaTensorCOM; + PxReal unscaledMass; + c.convexMesh->getMassInformation(unscaledMass, unscaledInertiaTensorNonCOM, unscaledCoM); + + // inertia tensor relative to center of mass + unscaledInertiaTensorCOM[0][0] = unscaledInertiaTensorNonCOM[0][0] - unscaledMass*PxReal((unscaledCoM.y*unscaledCoM.y+unscaledCoM.z*unscaledCoM.z)); + unscaledInertiaTensorCOM[1][1] = unscaledInertiaTensorNonCOM[1][1] - unscaledMass*PxReal((unscaledCoM.z*unscaledCoM.z+unscaledCoM.x*unscaledCoM.x)); + unscaledInertiaTensorCOM[2][2] = unscaledInertiaTensorNonCOM[2][2] - unscaledMass*PxReal((unscaledCoM.x*unscaledCoM.x+unscaledCoM.y*unscaledCoM.y)); + unscaledInertiaTensorCOM[0][1] = unscaledInertiaTensorCOM[1][0] = (unscaledInertiaTensorNonCOM[0][1] + unscaledMass*PxReal(unscaledCoM.x*unscaledCoM.y)); + unscaledInertiaTensorCOM[1][2] = unscaledInertiaTensorCOM[2][1] = (unscaledInertiaTensorNonCOM[1][2] + unscaledMass*PxReal(unscaledCoM.y*unscaledCoM.z)); + unscaledInertiaTensorCOM[0][2] = unscaledInertiaTensorCOM[2][0] = (unscaledInertiaTensorNonCOM[0][2] + unscaledMass*PxReal(unscaledCoM.z*unscaledCoM.x)); + + const PxMeshScale& s = c.scale; + mass = unscaledMass * s.scale.x * s.scale.y * s.scale.z; + centerOfMass = s.rotation.rotate(s.scale.multiply(s.rotation.rotateInv(unscaledCoM))); + inertiaTensor = scaleInertia(unscaledInertiaTensorCOM, s.rotation, s.scale); + } + break; + + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eINVALID: + case PxGeometryType::eGEOMETRY_COUNT: + { + *this = PxMassProperties(); + } + break; + } + + PX_ASSERT(inertiaTensor.column0.isFinite() && inertiaTensor.column1.isFinite() && inertiaTensor.column2.isFinite()); + PX_ASSERT(centerOfMass.isFinite()); + PX_ASSERT(PxIsFinite(mass)); + } + + /** + \brief Scale mass properties. + + \param[in] scale The linear scaling factor to apply to the mass properties. + \return The scaled mass properties. + */ + PX_FORCE_INLINE PxMassProperties operator*(const PxReal scale) const + { + PX_ASSERT(PxIsFinite(scale)); + + return PxMassProperties(mass * scale, inertiaTensor * scale, centerOfMass); + } + + /** + \brief Translate the center of mass by a given vector and adjust the inertia tensor accordingly. + + \param[in] t The translation vector for the center of mass. + */ + PX_FORCE_INLINE void translate(const PxVec3& t) + { + PX_ASSERT(t.isFinite()); + + inertiaTensor = translateInertia(inertiaTensor, mass, t); + centerOfMass += t; + + PX_ASSERT(inertiaTensor.column0.isFinite() && inertiaTensor.column1.isFinite() && inertiaTensor.column2.isFinite()); + PX_ASSERT(centerOfMass.isFinite()); + } + + /** + \brief Get the entries of the diagonalized inertia tensor and the corresponding reference rotation. + + \param[in] inertia The inertia tensor to diagonalize. + \param[out] massFrame The frame the diagonalized tensor refers to. + \return The entries of the diagonalized inertia tensor. + */ + PX_FORCE_INLINE static PxVec3 getMassSpaceInertia(const PxMat33& inertia, PxQuat& massFrame) + { + PX_ASSERT(inertia.column0.isFinite() && inertia.column1.isFinite() && inertia.column2.isFinite()); + + PxVec3 diagT = PxDiagonalize(inertia, massFrame); + PX_ASSERT(diagT.isFinite()); + PX_ASSERT(massFrame.isFinite()); + return diagT; + } + + /** + \brief Translate an inertia tensor using the parallel axis theorem + + \param[in] inertia The inertia tensor to translate. + \param[in] mass The mass of the object. + \param[in] t The relative frame to translate the inertia tensor to. + \return The translated inertia tensor. + */ + PX_FORCE_INLINE static PxMat33 translateInertia(const PxMat33& inertia, const PxReal mass, const PxVec3& t) + { + PX_ASSERT(inertia.column0.isFinite() && inertia.column1.isFinite() && inertia.column2.isFinite()); + PX_ASSERT(PxIsFinite(mass)); + PX_ASSERT(t.isFinite()); + + PxMat33 s( PxVec3(0,t.z,-t.y), + PxVec3(-t.z,0,t.x), + PxVec3(t.y,-t.x,0) ); + + PxMat33 translatedIT = s.getTranspose() * s * mass + inertia; + PX_ASSERT(translatedIT.column0.isFinite() && translatedIT.column1.isFinite() && translatedIT.column2.isFinite()); + return translatedIT; + } + + /** + \brief Rotate an inertia tensor around the center of mass + + \param[in] inertia The inertia tensor to rotate. + \param[in] q The rotation to apply to the inertia tensor. + \return The rotated inertia tensor. + */ + PX_FORCE_INLINE static PxMat33 rotateInertia(const PxMat33& inertia, const PxQuat& q) + { + PX_ASSERT(inertia.column0.isFinite() && inertia.column1.isFinite() && inertia.column2.isFinite()); + PX_ASSERT(q.isUnit()); + + PxMat33 m(q); + PxMat33 rotatedIT = m * inertia * m.getTranspose(); + PX_ASSERT(rotatedIT.column0.isFinite() && rotatedIT.column1.isFinite() && rotatedIT.column2.isFinite()); + return rotatedIT; + } + + /** + \brief Non-uniform scaling of the inertia tensor + + \param[in] inertia The inertia tensor to scale. + \param[in] scaleRotation The frame of the provided scaling factors. + \param[in] scale The scaling factor for each axis (relative to the frame specified in scaleRotation). + \return The scaled inertia tensor. + */ + static PxMat33 scaleInertia(const PxMat33& inertia, const PxQuat& scaleRotation, const PxVec3& scale) + { + PX_ASSERT(inertia.column0.isFinite() && inertia.column1.isFinite() && inertia.column2.isFinite()); + PX_ASSERT(scaleRotation.isUnit()); + PX_ASSERT(scale.isFinite()); + + PxMat33 localInertiaT = rotateInertia(inertia, scaleRotation); // rotate inertia into scaling frame + PxVec3 diagonal(localInertiaT[0][0], localInertiaT[1][1], localInertiaT[2][2]); + + PxVec3 xyz2 = PxVec3(diagonal.dot(PxVec3(0.5f))) - diagonal; // original x^2, y^2, z^2 + PxVec3 scaledxyz2 = xyz2.multiply(scale).multiply(scale); + + PxReal xx = scaledxyz2.y + scaledxyz2.z, + yy = scaledxyz2.z + scaledxyz2.x, + zz = scaledxyz2.x + scaledxyz2.y; + + PxReal xy = localInertiaT[0][1] * scale.x * scale.y, + xz = localInertiaT[0][2] * scale.x * scale.z, + yz = localInertiaT[1][2] * scale.y * scale.z; + + PxMat33 scaledInertia( PxVec3(xx, xy, xz), + PxVec3(xy, yy, yz), + PxVec3(xz, yz, zz)); + + PxMat33 scaledIT = rotateInertia(scaledInertia * (scale.x * scale.y * scale.z), scaleRotation.getConjugate()); + PX_ASSERT(scaledIT.column0.isFinite() && scaledIT.column1.isFinite() && scaledIT.column2.isFinite()); + return scaledIT; + } + + /** + \brief Sum up individual mass properties. + + \param[in] props Array of mass properties to sum up. + \param[in] transforms Reference transforms for each mass properties entry. + \param[in] count The number of mass properties to sum up. + \return The summed up mass properties. + */ + static PxMassProperties sum(const PxMassProperties* props, const PxTransform* transforms, const PxU32 count) + { + PxReal combinedMass = 0.0f; + PxVec3 combinedCoM(0.0f); + PxMat33 combinedInertiaT = PxMat33(PxZero); + + for(PxU32 i = 0; i < count; i++) + { + PX_ASSERT(props[i].inertiaTensor.column0.isFinite() && props[i].inertiaTensor.column1.isFinite() && props[i].inertiaTensor.column2.isFinite()); + PX_ASSERT(props[i].centerOfMass.isFinite()); + PX_ASSERT(PxIsFinite(props[i].mass)); + + combinedMass += props[i].mass; + const PxVec3 comTm = transforms[i].transform(props[i].centerOfMass); + combinedCoM += comTm * props[i].mass; + } + + combinedCoM /= combinedMass; + + for(PxU32 i = 0; i < count; i++) + { + const PxVec3 comTm = transforms[i].transform(props[i].centerOfMass); + combinedInertiaT += translateInertia(rotateInertia(props[i].inertiaTensor, transforms[i].q), props[i].mass, combinedCoM - comTm); + } + + PX_ASSERT(combinedInertiaT.column0.isFinite() && combinedInertiaT.column1.isFinite() && combinedInertiaT.column2.isFinite()); + PX_ASSERT(combinedCoM.isFinite()); + PX_ASSERT(PxIsFinite(combinedMass)); + + return PxMassProperties(combinedMass, combinedInertiaT, combinedCoM); + } + + + PxMat33 inertiaTensor; //!< The inertia tensor of the object. + PxVec3 centerOfMass; //!< The center of mass of the object. + PxReal mass; //!< The mass of the object. +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxPrismaticJoint.h b/src/PhysX/physx/include/extensions/PxPrismaticJoint.h new file mode 100644 index 000000000..b82ac2c8a --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxPrismaticJoint.h @@ -0,0 +1,236 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PRISMATICJOINT_H +#define PX_PRISMATICJOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" +#include "extensions/PxJointLimit.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPrismaticJoint; + +/** +\brief Create a prismatic joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxPrismaticJoint +*/ +PxPrismaticJoint* PxPrismaticJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + + +/** +\brief Flags specific to the prismatic joint. + +@see PxPrismaticJoint +*/ +struct PxPrismaticJointFlag +{ + enum Enum + { + eLIMIT_ENABLED = 1<<1 + }; +}; + +typedef PxFlags PxPrismaticJointFlags; +PX_FLAGS_OPERATORS(PxPrismaticJointFlag::Enum, PxU16) + +/** + \brief A prismatic joint permits relative translational movement between two bodies along + an axis, but no relative rotational movement. + + the axis on each body is defined as the line containing the origin of the joint frame and + extending along the x-axis of that frame + + \image html prismJoint.png + + @see PxPrismaticJointCreate() PxJoint +*/ +class PxPrismaticJoint : public PxJoint +{ +public: + + /** + \brief returns the displacement of the joint along its axis. + */ + virtual PxReal getPosition() const = 0; + + /** + \brief returns the velocity of the joint along its axis + */ + virtual PxReal getVelocity() const = 0; + + /** + \brief sets the joint limit parameters. + + The limit range is [-PX_MAX_F32, PX_MAX_F32], but note that the width of the limit (upper-lower) must also be + a valid float. + + @see PxJointLinearLimitPair getLimit() + */ + virtual void setLimit(const PxJointLinearLimitPair&) = 0; + + /** + \brief gets the joint limit parameters. + + @see PxJointLinearLimit getLimit() + */ + virtual PxJointLinearLimitPair getLimit() const = 0; + + /** + \brief Set the flags specific to the Prismatic Joint. + + Default PxPrismaticJointFlags(0) + + \param[in] flags The joint flags. + + @see PxPrismaticJointFlag setFlag() getFlags() + */ + virtual void setPrismaticJointFlags(PxPrismaticJointFlags flags) = 0; + + /** + \brief Set a single flag specific to a Prismatic Joint to true or false. + + \param[in] flag The flag to set or clear. + \param[in] value The value to which to set the flag + + @see PxPrismaticJointFlag, getFlags() setFlags() + */ + virtual void setPrismaticJointFlag(PxPrismaticJointFlag::Enum flag, bool value) = 0; + + /** + \brief Get the flags specific to the Prismatic Joint. + + \return the joint flags + + @see PxPrismaticJoint::flags, PxPrismaticJointFlag setFlag() setFlags() + */ + virtual PxPrismaticJointFlags getPrismaticJointFlags() const = 0; + + /** + \brief Set the linear tolerance threshold for projection. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + This value must be nonnegative. + + Range: [0, PX_MAX_F32)
            + Default: 1e10f + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() + */ + virtual void setProjectionLinearTolerance(PxReal tolerance) = 0; + + /** + \brief Get the linear tolerance threshold for projection. + + \return the linear tolerance threshold in radians + + @see setProjectionLinearTolerance() + */ + virtual PxReal getProjectionLinearTolerance() const = 0; + + /** + \brief Set the angular tolerance threshold for projection. Projection is enabled if PxConstraintFlag::ePROJECTION + is set for the joint. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0, PX_MAX_F32)
            + Default: Pi + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() PxJoint::setConstraintFlags() + */ + virtual void setProjectionAngularTolerance(PxReal tolerance) = 0; + + /** + \brief Get the angular tolerance threshold for projection. + + @see getProjectionAngularTolerance() + */ + virtual PxReal getProjectionAngularTolerance() const = 0; + + /** + \brief Returns string name of PxPrismaticJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxPrismaticJoint"; } + +protected: + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxPrismaticJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxPrismaticJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxPrismaticJoint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxRaycastCCD.h b/src/PhysX/physx/include/extensions/PxRaycastCCD.h new file mode 100644 index 000000000..ed8f84ba0 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRaycastCCD.h @@ -0,0 +1,100 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_RAYCAST_CCD_H +#define PX_RAYCAST_CCD_H +/** \addtogroup extensions +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxScene; + class PxShape; + class PxRigidDynamic; + class RaycastCCDManagerInternal; + + /** + \brief Raycast-CCD manager. + + Raycast-CCD is a simple and potentially cheaper alternative to the SDK's built-in continuous collision detection algorithm. + + This implementation has some limitations: + - it is only implemented for PxRigidDynamic objects (not for PxArticulationLink) + - it is only implemented for simple actors with 1 shape (not for "compounds") + + Also, since it is raycast-based, the solution is not perfect. In particular: + - small dynamic objects can still go through the static world if the ray goes through a crack between edges, or a small + hole in the world (like the keyhole from a door). + - dynamic-vs-dynamic CCD is very approximate. It only works well for fast-moving dynamic objects colliding against + slow-moving dynamic objects. + + Finally, since it is using the SDK's scene queries under the hood, it only works provided the simulation shapes also have + scene-query shapes associated with them. That is, if the objects in the scene only use PxShapeFlag::eSIMULATION_SHAPE + (and no PxShapeFlag::eSCENE_QUERY_SHAPE), then the raycast-CCD system will not work. + */ + class RaycastCCDManager + { + public: + RaycastCCDManager(PxScene* scene); + ~RaycastCCDManager(); + + /** + \brief Register dynamic object for raycast CCD. + + \param[in] actor object's actor + \param[in] shape object's shape + + \return True if success + */ + bool registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape); + + /** + \brief Perform raycast CCD. Call this after your simulate/fetchResults calls. + + \param[in] doDynamicDynamicCCD True to enable dynamic-vs-dynamic CCD (more expensive, not always needed) + */ + void doRaycastCCD(bool doDynamicDynamicCCD); + + private: + RaycastCCDManagerInternal* mImpl; + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxRepXSerializer.h b/src/PhysX/physx/include/extensions/PxRepXSerializer.h new file mode 100644 index 000000000..dae0ef604 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRepXSerializer.h @@ -0,0 +1,148 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_REPX_SERIALIZER_H +#define PX_REPX_SERIALIZER_H +/** \addtogroup Serializers + @{ +*/ + +#include "common/PxBase.h" +#include "extensions/PxRepXSimpleType.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class XmlMemoryAllocator; + class XmlWriter; + class XmlReader; + class MemoryBuffer; + + /** + \brief Serializer interface for RepX (Xml) serialization. + + In order to serialize a class to RepX both a PxSerializer and + a PxRepXSerializer implementation are needed. + + A repx Serializer provides the ability to capture a live + object to a descriptor or static state and the ability to + write that state out to a file. Objects allocated + by the Serializer using the allocator are freed when the + collection itself is freed. + SnRepXCoreSerializers.cpp implements a set of Serializers + for the core PhysX types. + + \note Implementing a PxRepXSerializer is currently not practical without including the internal PhysXExtension header "SnRepXSerializerImpl.h". + + @see PxSerializer, PX_NEW_REPX_SERIALIZER, PxSerializationRegistry::registerRepXSerializer + */ + class PxRepXSerializer + { + protected: + virtual ~PxRepXSerializer(){} + public: + + /** + \brief The type this Serializer is meant to operate on. + @see PxRepXObject::typeName + */ + virtual const char* getTypeName() = 0; + + /** + \brief Convert from a RepX object to a key-value pair hierarchy + + \param[in] inLiveObject The object to convert to the passed in descriptor. + \param[in] inCollection The collection to use to find ids of references of this object. + \param[in] inWriter Interface to write data to. + \param[in] inTempBuffer used to for temporary allocations. + \param[in] inArgs The arguments used in create resources and objects. + */ + virtual void objectToFile( const PxRepXObject& inLiveObject, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs ) = 0; + + /** + \brief Convert from a descriptor to a live object. Must be an object of this Serializer type. + + \param[in] inReader The inverse of the writer, a key-value pair database. + \param[in] inAllocator An allocator to use for temporary allocations. These will be freed after instantiation completes. + \param[in] inArgs The arguments used in create resources and objects. + \param[in] inCollection The collection used to find references. + + \return The new live object. It can be an invalid object if the instantiation cannot take place. + */ + virtual PxRepXObject fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) = 0; + + }; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** +\brief Inline helper template function to create PxRepXObject from TDataType type supporting PxTypeInfo::name. +*/ +template +PX_INLINE physx::PxRepXObject PxCreateRepXObject(const TDataType* inType, const physx::PxSerialObjectId inId) +{ + return physx::PxRepXObject(physx::PxTypeInfo::name(), inType, inId); +} + +/** +\brief Inline helper function to create PxRepXObject from a PxBase instance. +*/ +PX_INLINE physx::PxRepXObject PxCreateRepXObject(const physx::PxBase* inType, const physx::PxSerialObjectId inId) +{ + PX_ASSERT(inType); + return physx::PxRepXObject(inType->getConcreteTypeName(), inType, inId); +} + +/** +\brief Inline helper template function to create PxRepXObject form TDataType type using inType pointer as a PxSerialObjectId id. +*/ +template +PX_INLINE physx::PxRepXObject PxCreateRepXObject(const TDataType* inType) +{ + return PxCreateRepXObject(inType, static_cast(reinterpret_cast(inType))); +} + +/** +\brief Preprocessor macro for RepX serializer creation. +*/ +#define PX_NEW_REPX_SERIALIZER(T) \ + *PX_PLACEMENT_NEW(PxGetFoundation().getAllocatorCallback().allocate(sizeof(T), "PxRepXSerializer", __FILE__, __LINE__ ), T)(PxGetFoundation().getAllocatorCallback()) + +/** +\brief Preprocessor Macro to simplify RepX serializer delete. +*/ +#define PX_DELETE_REPX_SERIALIZER(x) \ + { PxRepXSerializer* s = x; if (s) { PxGetFoundation().getAllocatorCallback().deallocate(s); } } + + +/** @} */ +#endif // PX_REPX_SERIALIZER_H diff --git a/src/PhysX/physx/include/extensions/PxRepXSimpleType.h b/src/PhysX/physx/include/extensions/PxRepXSimpleType.h new file mode 100644 index 000000000..bd6023e92 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRepXSimpleType.h @@ -0,0 +1,106 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PX_REPX_SIMPLE_TYPE_H +#define PX_REPX_SIMPLE_TYPE_H + +/** \addtogroup extensions + @{ +*/ + +#include "foundation/PxSimpleTypes.h" +#include "cooking/PxCooking.h" +#include "common/PxStringTable.h" +#include "common/PxSerialFramework.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + /** + \brief Helper class containing the mapping of id to object, and type name. + */ + struct PxRepXObject + { + /** + \brief Identifies the extension meant to handle this object. + @see PxTypeInfo, PX_DEFINE_TYPEINFO, PxRepXSerializer + */ + const char* typeName; + + /** + \brief Pointer to the serializable this was created from + */ + const void* serializable; + + /** + \brief Id given to this object at some point + */ + PxSerialObjectId id; + PxRepXObject( const char* inTypeName = "", const void* inSerializable = NULL, const PxSerialObjectId inId = 0 ) + : typeName( inTypeName ) + , serializable( inSerializable ) + , id( inId ) + { + } + bool isValid() const { return serializable != NULL; } + }; + + /** + \brief Arguments required to instantiate a serializable object from RepX. + + Extra arguments can be added to the object map under special ids. + + @see PxRepXSerializer::objectToFile, PxRepXSerializer::fileToObject + */ + struct PxRepXInstantiationArgs + { + PxPhysics& physics; + PxCooking* cooker; + PxStringTable* stringTable; + PxRepXInstantiationArgs( PxPhysics& inPhysics, PxCooking* inCooking = NULL , PxStringTable* inStringTable = NULL ) + : physics( inPhysics ) + , cooker( inCooking ) + , stringTable( inStringTable ) + { + } + + PxRepXInstantiationArgs& operator=(const PxRepXInstantiationArgs&); + }; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxRevoluteJoint.h b/src/PhysX/physx/include/extensions/PxRevoluteJoint.h new file mode 100644 index 000000000..d7d1c685c --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRevoluteJoint.h @@ -0,0 +1,332 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_REVOLUTEJOINT_H +#define PX_REVOLUTEJOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" +#include "extensions/PxJointLimit.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxRevoluteJoint; + +/** +\brief Create a revolute joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxRevoluteJoint +*/ +PxRevoluteJoint* PxRevoluteJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + +/** +\brief Flags specific to the Revolute Joint. + +@see PxRevoluteJoint +*/ +struct PxRevoluteJointFlag +{ + enum Enum + { + eLIMIT_ENABLED = 1<<0, //!< enable the limit + eDRIVE_ENABLED = 1<<1, //!< enable the drive + eDRIVE_FREESPIN = 1<<2 //!< if the existing velocity is beyond the drive velocity, do not add force + }; +}; + +typedef PxFlags PxRevoluteJointFlags; +PX_FLAGS_OPERATORS(PxRevoluteJointFlag::Enum, PxU16) + +/** + +\brief A joint which behaves in a similar way to a hinge or axle. + + A hinge joint removes all but a single rotational degree of freedom from two objects. + The axis along which the two bodies may rotate is specified with a point and a direction + vector. + + The position of the hinge on each body is specified by the origin of the body's joint frame. + The axis of the hinge is specified as the direction of the x-axis in the body's joint frame. + + \image html revoluteJoint.png + + A revolute joint can be given a motor, so that it can apply a force to rotate the attached actors. + It may also be given a limit, to restrict the revolute motion to within a certain range. In + addition, the bodies may be projected together if the distance or angle between them exceeds + a given threshold. + + Projection, drive and limits are activated by setting the appropriate flags on the joint. + + @see PxRevoluteJointCreate() PxJoint +*/ +class PxRevoluteJoint : public PxJoint +{ +public: + + /** + \brief return the angle of the joint, in the range (-2*Pi, 2*Pi] + */ + virtual PxReal getAngle() const = 0; + + /** + \brief return the velocity of the joint + */ + virtual PxReal getVelocity() const = 0; + + /** + \brief set the joint limit parameters. + + The limit is activated using the flag PxRevoluteJointFlag::eLIMIT_ENABLED + + The limit angle range is (-2*Pi, 2*Pi). + + \param[in] limits The joint limit parameters. + + @see PxJointAngularLimitPair getLimit() + */ + virtual void setLimit(const PxJointAngularLimitPair& limits) = 0; + + /** + \brief get the joint limit parameters. + + \return the joint limit parameters + + @see PxJointAngularLimitPair setLimit() + */ + virtual PxJointAngularLimitPair getLimit() const = 0; + + /** + \brief set the target velocity for the drive model. + + The motor will only be able to reach this velocity if the maxForce is sufficiently large. + If the joint is spinning faster than this velocity, the motor will actually try to brake + (see PxRevoluteJointFlag::eDRIVE_FREESPIN.) + + If you set this to infinity then the motor will keep speeding up, unless there is some sort + of resistance on the attached bodies. The sign of this variable determines the rotation direction, + with positive values going the same way as positive joint angles. + + \param[in] velocity the drive target velocity + \param[in] autowake Whether to wake the joint rigids up if it is asleep. + + Range: [0, PX_MAX_F32)
            + Default: 0.0 + + @see PxRevoluteFlags::eDRIVE_FREESPIN + */ + virtual void setDriveVelocity(PxReal velocity, bool autowake = true) = 0; + + /** + \brief gets the target velocity for the drive model. + + \return the drive target velocity + + @see setDriveVelocity() + */ + virtual PxReal getDriveVelocity() const = 0; + + /** + \brief sets the maximum torque the drive can exert. + + Setting this to a very large value if velTarget is also very large may cause unexpected results. + + The value set here may be used either as an impulse limit or a force limit, depending on the flag PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES + + Range: [0, PX_MAX_F32)
            + Default: PX_MAX_F32 + + @see setDriveVelocity() + */ + virtual void setDriveForceLimit(PxReal limit) = 0; + + /** + \brief gets the maximum torque the drive can exert. + + \return the torque limit + + @see setDriveVelocity() + */ + virtual PxReal getDriveForceLimit() const = 0; + + /** + \brief sets the gear ratio for the drive. + + When setting up the drive constraint, the velocity of the first actor is scaled by this value, and its response to drive torque is scaled down. + So if the drive target velocity is zero, the second actor will be driven to the velocity of the first scaled by the gear ratio + + Range: [0, PX_MAX_F32)
            + Default: 1.0 + + \param[in] ratio the drive gear ratio + + @see getDriveGearRatio() + */ + virtual void setDriveGearRatio(PxReal ratio) = 0; + + /** + \brief gets the gear ratio. + + \return the drive gear ratio + + @see setDriveGearRatio() + */ + virtual PxReal getDriveGearRatio() const = 0; + + /** + \brief sets the flags specific to the Revolute Joint. + + Default PxRevoluteJointFlags(0) + + \param[in] flags The joint flags. + + @see PxRevoluteJointFlag setFlag() getFlags() + */ + virtual void setRevoluteJointFlags(PxRevoluteJointFlags flags) = 0; + + /** + \brief sets a single flag specific to a Revolute Joint. + + \param[in] flag The flag to set or clear. + \param[in] value the value to which to set the flag + + @see PxRevoluteJointFlag, getFlags() setFlags() + */ + virtual void setRevoluteJointFlag(PxRevoluteJointFlag::Enum flag, bool value) = 0; + + /** + \brief gets the flags specific to the Revolute Joint. + + \return the joint flags + + @see PxRevoluteJoint::flags, PxRevoluteJointFlag setFlag() setFlags() + */ + virtual PxRevoluteJointFlags getRevoluteJointFlags() const = 0; + + /** + \brief Set the linear tolerance threshold for projection. Projection is enabled if PxConstraintFlag::ePROJECTION + is set for the joint. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0, PX_MAX_F32)
            + Default: 1e10f + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() PxJoint::setConstraintFlags() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionLinearTolerance(PxReal tolerance) = 0; + + /** + \brief Get the linear tolerance threshold for projection. + + \return the linear tolerance threshold + + @see setProjectionLinearTolerance() + */ + virtual PxReal getProjectionLinearTolerance() const = 0; + + /** + \brief Set the angular tolerance threshold for projection. Projection is enabled if + PxConstraintFlag::ePROJECTION is set for the joint. + + If the joint deviates by more than this angle around its locked angular degrees of freedom, + the solver will move the bodies to close the angle. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0,Pi]
            + Default: Pi + + \param[in] tolerance the angular tolerance threshold in radians + + @see getProjectionAngularTolerance() PxJoint::setConstraintFlag() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionAngularTolerance(PxReal tolerance) = 0; + + /** + \brief gets the angular tolerance threshold for projection. + + \return the angular tolerance threshold in radians + + @see setProjectionAngularTolerance() + */ + virtual PxReal getProjectionAngularTolerance() const = 0; + + /** + \brief Returns string name of PxRevoluteJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxRevoluteJoint"; } + +protected: + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxRevoluteJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxRevoluteJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxRevoluteJoint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxRigidActorExt.h b/src/PhysX/physx/include/extensions/PxRigidActorExt.h new file mode 100644 index 000000000..2a437be7f --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRigidActorExt.h @@ -0,0 +1,155 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_RIGIDACTOR_H +#define PX_PHYSICS_EXTENSIONS_RIGIDACTOR_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "PxPhysics.h" +#include "PxRigidActor.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief utility functions for use with PxRigidActor and subclasses + +@see PxRigidActor PxRigidStatic PxRigidBody PxRigidDynamic PxArticulationLink +*/ + +class PxRigidActorExt +{ +public: + + /** + \brief Creates a new shape with default properties and a list of materials and adds it to the list of shapes of this actor. + + This is equivalent to the following + + PxShape* shape(...) = PxGetPhysics().createShape(...); // reference count is 1 + actor->attachShape(shape); // increments reference count + shape->release(); // releases user reference, leaving reference count at 1 + + As a consequence, detachShape() will result in the release of the last reference, and the shape will be deleted. + + \note The default shape flags to be set are: eVISUALIZATION, eSIMULATION_SHAPE, eSCENE_QUERY_SHAPE (see #PxShapeFlag). + Triangle mesh, heightfield or plane geometry shapes configured as eSIMULATION_SHAPE are not supported for + non-kinematic PxRigidDynamic instances. + + \note Creating compounds with a very large number of shapes may adversely affect performance and stability. + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] actor the actor to which to attach the shape + \param[in] geometry the geometry of the shape + \param[in] materials a pointer to an array of material pointers + \param[in] materialCount the count of materials + \param[in] shapeFlags optional PxShapeFlags + + \return The newly created shape. + + @see PxShape PxShape::release(), PxPhysics::createShape(), PxRigidActor::attachShape() + */ + + static PxShape* createExclusiveShape(PxRigidActor& actor, const PxGeometry& geometry, PxMaterial*const* materials, PxU16 materialCount, + PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) + { + PxShape* shape = PxGetPhysics().createShape(geometry, materials, materialCount, true, shapeFlags); + if(shape) + { + bool status = actor.attachShape(*shape); // attach can fail, if e.g. we try and attach a trimesh simulation shape to a dynamic actor + shape->release(); // if attach fails, we hold the only counted reference, and so this cleans up properly + if(!status) + shape = NULL; + } + return shape; + } + + /** + \brief Creates a new shape with default properties and a single material adds it to the list of shapes of this actor. + + This is equivalent to the following + + PxShape* shape(...) = PxGetPhysics().createShape(...); // reference count is 1 + actor->attachShape(shape); // increments reference count + shape->release(); // releases user reference, leaving reference count at 1 + + As a consequence, detachShape() will result in the release of the last reference, and the shape will be deleted. + + \note The default shape flags to be set are: eVISUALIZATION, eSIMULATION_SHAPE, eSCENE_QUERY_SHAPE (see #PxShapeFlag). + Triangle mesh, heightfield or plane geometry shapes configured as eSIMULATION_SHAPE are not supported for + non-kinematic PxRigidDynamic instances. + + \note Creating compounds with a very large number of shapes may adversely affect performance and stability. + + Sleeping: Does NOT wake the actor up automatically. + + \param[in] actor the actor to which to attach the shape + \param[in] geometry the geometry of the shape + \param[in] material the material for the shape + \param[in] shapeFlags optional PxShapeFlags + + \return The newly created shape. + + @see PxShape PxShape::release(), PxPhysics::createShape(), PxRigidActor::attachShape() + */ + + static PX_FORCE_INLINE PxShape* createExclusiveShape(PxRigidActor& actor, const PxGeometry& geometry, const PxMaterial& material, + PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) + { + PxMaterial* materialPtr = const_cast(&material); + return createExclusiveShape(actor, geometry, &materialPtr, 1, shapeFlags); + } + + + /** + \brief Gets a list of bounds based on shapes in rigid actor. This list can be used to cook/create + bounding volume hierarchy though PxCooking API. + + \param[in] actor The actor from which the bounds list is retrieved. + \param[out] numBounds Number of bounds in returned list. + + @see PxShape PxBVHStructure PxCooking::createBVHStructure PxCooking::cookBVHStructure + */ + static PxBounds3* getRigidActorShapeLocalBoundsList(const PxRigidActor& actor, PxU32& numBounds); + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxRigidBodyExt.h b/src/PhysX/physx/include/extensions/PxRigidBodyExt.h new file mode 100644 index 000000000..f7c8f53ed --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxRigidBodyExt.h @@ -0,0 +1,460 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_RIGIDBODY_H +#define PX_PHYSICS_EXTENSIONS_RIGIDBODY_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "PxRigidBody.h" +#include "PxQueryReport.h" +#include "PxQueryFiltering.h" +#include "extensions/PxMassProperties.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxScene; +struct PxQueryCache; +class PxShape; + +/** +\brief utility functions for use with PxRigidBody and subclasses + +@see PxRigidBody PxRigidDynamic PxArticulationLink +*/ + +class PxRigidBodyExt +{ +public: + /** + \brief Computation of mass properties for a rigid body actor + + To simulate a dynamic rigid actor, the SDK needs a mass and an inertia tensor. + + This method offers functionality to compute the necessary mass and inertia properties based on the shapes declared in + the PxRigidBody descriptor and some additionally specified parameters. For each shape, the shape geometry, + the shape positioning within the actor and the specified shape density are used to compute the body's mass and + inertia properties. + +
              +
            • Shapes without PxShapeFlag::eSIMULATION_SHAPE set are ignored unless includeNonSimShapes is true.
            • +
            • Shapes with plane, triangle mesh or heightfield geometry and PxShapeFlag::eSIMULATION_SHAPE set are not allowed for PxRigidBody collision.
            • +
            + + This method will set the mass, center of mass, and inertia tensor + + if no collision shapes are found, the inertia tensor is set to (1,1,1) and the mass to 1 + + if massLocalPose is non-NULL, the rigid body's center of mass parameter will be set + to the user provided value (massLocalPose) and the inertia tensor will be resolved at that point. + + \note If all shapes of the actor have the same density then the overloaded method updateMassAndInertia() with a single density parameter can be used instead. + + \param[in,out] body The rigid body. + \param[in] shapeDensities The per shape densities. There must be one entry for each shape which has the PxShapeFlag::eSIMULATION_SHAPE set (or for all shapes if includeNonSimShapes is set to true). Other shapes are ignored. The density values must be greater than 0. + \param[in] shapeDensityCount The number of provided density values. + \param[in] massLocalPose The center of mass relative to the actor frame. If set to null then (0,0,0) is assumed. + \param[in] includeNonSimShapes True if all kind of shapes (PxShapeFlag::eSCENE_QUERY_SHAPE, PxShapeFlag::eTRIGGER_SHAPE) should be taken into account. + \return Boolean. True on success else false. + + @see PxRigidBody::setMassLocalPose PxRigidBody::setMassSpaceInertiaTensor PxRigidBody::setMass + */ + static bool updateMassAndInertia(PxRigidBody& body, const PxReal* shapeDensities, PxU32 shapeDensityCount, const PxVec3* massLocalPose = NULL, bool includeNonSimShapes = false); + + + /** + \brief Computation of mass properties for a rigid body actor + + See previous method for details. + + \param[in,out] body The rigid body. + \param[in] density The density of the body. Used to compute the mass of the body. The density must be greater than 0. + \param[in] massLocalPose The center of mass relative to the actor frame. If set to null then (0,0,0) is assumed. + \param[in] includeNonSimShapes True if all kind of shapes (PxShapeFlag::eSCENE_QUERY_SHAPE, PxShapeFlag::eTRIGGER_SHAPE) should be taken into account. + \return Boolean. True on success else false. + + @see PxRigidBody::setMassLocalPose PxRigidBody::setMassSpaceInertiaTensor PxRigidBody::setMass + */ + static bool updateMassAndInertia(PxRigidBody& body, PxReal density, const PxVec3* massLocalPose = NULL, bool includeNonSimShapes = false); + + + /** + \brief Computation of mass properties for a rigid body actor + + This method sets the mass, inertia and center of mass of a rigid body. The mass is set to the sum of all user-supplied + shape mass values, and the inertia and center of mass are computed according to the rigid body's shapes and the per shape mass input values. + + If no collision shapes are found, the inertia tensor is set to (1,1,1) + + \note If a single mass value should be used for the actor as a whole then the overloaded method setMassAndUpdateInertia() with a single mass parameter can be used instead. + + @see updateMassAndInertia for more details. + + \param[in,out] body The rigid body for which to set the mass and centre of mass local pose properties. + \param[in] shapeMasses The per shape mass values. There must be one entry for each shape which has the PxShapeFlag::eSIMULATION_SHAPE set. Other shapes are ignored. The mass values must be greater than 0. + \param[in] shapeMassCount The number of provided mass values. + \param[in] massLocalPose The center of mass relative to the actor frame. If set to null then (0,0,0) is assumed. + \param[in] includeNonSimShapes True if all kind of shapes (PxShapeFlag::eSCENE_QUERY_SHAPE, PxShapeFlag::eTRIGGER_SHAPE) should be taken into account. + \return Boolean. True on success else false. + + @see PxRigidBody::setCMassLocalPose PxRigidBody::setMassSpaceInertiaTensor PxRigidBody::setMass + */ + static bool setMassAndUpdateInertia(PxRigidBody& body, const PxReal* shapeMasses, PxU32 shapeMassCount, const PxVec3* massLocalPose = NULL, bool includeNonSimShapes = false); + + + /** + \brief Computation of mass properties for a rigid body actor + + This method sets the mass, inertia and center of mass of a rigid body. The mass is set to the user-supplied + value, and the inertia and center of mass are computed according to the rigid body's shapes and the input mass. + + If no collision shapes are found, the inertia tensor is set to (1,1,1) + + @see updateMassAndInertia for more details. + + \param[in,out] body The rigid body for which to set the mass and centre of mass local pose properties. + \param[in] mass The mass of the body. Must be greater than 0. + \param[in] massLocalPose The center of mass relative to the actor frame. If set to null then (0,0,0) is assumed. + \param[in] includeNonSimShapes True if all kind of shapes (PxShapeFlag::eSCENE_QUERY_SHAPE, PxShapeFlag::eTRIGGER_SHAPE) should be taken into account. + \return Boolean. True on success else false. + + @see PxRigidBody::setCMassLocalPose PxRigidBody::setMassSpaceInertiaTensor PxRigidBody::setMass + */ + static bool setMassAndUpdateInertia(PxRigidBody& body, PxReal mass, const PxVec3* massLocalPose = NULL, bool includeNonSimShapes = false); + + + /** + \brief Compute the mass, inertia tensor and center of mass from a list of shapes. + + \param[in] shapes The shapes to compute the mass properties from. + \param[in] shapeCount The number of provided shapes. + \return The mass properties from the combined shapes. + + @see PxRigidBody::setCMassLocalPose PxRigidBody::setMassSpaceInertiaTensor PxRigidBody::setMass + */ + static PxMassProperties computeMassPropertiesFromShapes(const PxShape* const* shapes, PxU32 shapeCount); + + + /** + \brief Applies a force (or impulse) defined in the global coordinate frame, acting at a particular + point in global coordinates, to the actor. + + Note that if the force does not act along the center of mass of the actor, this + will also add the corresponding torque. Because forces are reset at the end of every timestep, + you can maintain a total external force on an object by calling this once every frame. + + \note if this call is used to apply a force or impulse to an articulation link, only the link is updated, not the entire + articulation + + ::PxForceMode determines if the force is to be conventional or impulsive. Only eFORCE and eIMPULSE are supported, as the + force required to produce a given velocity change or acceleration is underdetermined given only the desired change at a + given point. + + Sleeping: This call wakes the actor if it is sleeping and the wakeup parameter is true (default). + + \param[in] body The rigid body to apply the force to. + \param[in] force Force/impulse to add, defined in the global frame. Range: force vector + \param[in] pos Position in the global frame to add the force at. Range: position vector + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode). + \param[in] wakeup Specify if the call should wake up the actor. + + @see PxForceMode + @see addForceAtLocalPos() addLocalForceAtPos() addLocalForceAtLocalPos() + */ + static void addForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode = PxForceMode::eFORCE, bool wakeup = true); + + /** + \brief Applies a force (or impulse) defined in the global coordinate frame, acting at a particular + point in local coordinates, to the actor. + + Note that if the force does not act along the center of mass of the actor, this + will also add the corresponding torque. Because forces are reset at the end of every timestep, you can maintain a + total external force on an object by calling this once every frame. + + \note if this call is used to apply a force or impulse to an articulation link, only the link is updated, not the entire + articulation + + ::PxForceMode determines if the force is to be conventional or impulsive. Only eFORCE and eIMPULSE are supported, as the + force required to produce a given velocity change or acceleration is underdetermined given only the desired change at a + given point. + + Sleeping: This call wakes the actor if it is sleeping and the wakeup parameter is true (default). + + \param[in] body The rigid body to apply the force to. + \param[in] force Force/impulse to add, defined in the global frame. Range: force vector + \param[in] pos Position in the local frame to add the force at. Range: position vector + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode). + \param[in] wakeup Specify if the call should wake up the actor. + + @see PxForceMode + @see addForceAtPos() addLocalForceAtPos() addLocalForceAtLocalPos() + */ + static void addForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode = PxForceMode::eFORCE, bool wakeup = true); + + /** + \brief Applies a force (or impulse) defined in the actor local coordinate frame, acting at a + particular point in global coordinates, to the actor. + + Note that if the force does not act along the center of mass of the actor, this + will also add the corresponding torque. Because forces are reset at the end of every timestep, you can maintain a + total external force on an object by calling this once every frame. + + \note if this call is used to apply a force or impulse to an articulation link, only the link is updated, not the entire + articulation + + ::PxForceMode determines if the force is to be conventional or impulsive. Only eFORCE and eIMPULSE are supported, as the + force required to produce a given velocity change or acceleration is underdetermined given only the desired change at a + given point. + + Sleeping: This call wakes the actor if it is sleeping and the wakeup parameter is true (default). + + \param[in] body The rigid body to apply the force to. + \param[in] force Force/impulse to add, defined in the local frame. Range: force vector + \param[in] pos Position in the global frame to add the force at. Range: position vector + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode). + \param[in] wakeup Specify if the call should wake up the actor. + + @see PxForceMode + @see addForceAtPos() addForceAtLocalPos() addLocalForceAtLocalPos() + */ + static void addLocalForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode = PxForceMode::eFORCE, bool wakeup = true); + + /** + \brief Applies a force (or impulse) defined in the actor local coordinate frame, acting at a + particular point in local coordinates, to the actor. + + Note that if the force does not act along the center of mass of the actor, this + will also add the corresponding torque. Because forces are reset at the end of every timestep, you can maintain a + total external force on an object by calling this once every frame. + + \note if this call is used to apply a force or impulse to an articulation link, only the link is updated, not the entire + articulation + + ::PxForceMode determines if the force is to be conventional or impulsive. Only eFORCE and eIMPULSE are supported, as the + force required to produce a given velocity change or acceleration is underdetermined given only the desired change at a + given point. + + Sleeping: This call wakes the actor if it is sleeping and the wakeup parameter is true (default). + + \param[in] body The rigid body to apply the force to. + \param[in] force Force/impulse to add, defined in the local frame. Range: force vector + \param[in] pos Position in the local frame to add the force at. Range: position vector + \param[in] mode The mode to use when applying the force/impulse(see #PxForceMode). + \param[in] wakeup Specify if the call should wake up the actor. + + @see PxForceMode + @see addForceAtPos() addForceAtLocalPos() addLocalForceAtPos() + */ + static void addLocalForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode = PxForceMode::eFORCE, bool wakeup = true); + + /** + \brief Computes the velocity of a point given in world coordinates if it were attached to the + specified body and moving with it. + + \param[in] body The rigid body the point is attached to. + \param[in] pos Position we wish to determine the velocity for, defined in the global frame. Range: position vector + \return The velocity of point in the global frame. + + @see getLocalPointVelocity() + */ + static PxVec3 getVelocityAtPos(const PxRigidBody& body, const PxVec3& pos); + + /** + \brief Computes the velocity of a point given in local coordinates if it were attached to the + specified body and moving with it. + + \param[in] body The rigid body the point is attached to. + \param[in] pos Position we wish to determine the velocity for, defined in the local frame. Range: position vector + \return The velocity of point in the local frame. + + @see getLocalPointVelocity() + */ + static PxVec3 getLocalVelocityAtLocalPos(const PxRigidBody& body, const PxVec3& pos); + + /** + \brief Computes the velocity of a point (offset from the origin of the body) given in world coordinates if it were attached to the + specified body and moving with it. + + \param[in] body The rigid body the point is attached to. + \param[in] pos Position (offset from the origin of the body) we wish to determine the velocity for, defined in the global frame. Range: position vector + \return The velocity of point (offset from the origin of the body) in the global frame. + + @see getLocalPointVelocity() + */ + static PxVec3 getVelocityAtOffset(const PxRigidBody& body, const PxVec3& pos); + + + /** + \brief Performs a linear sweep through space with the body's geometry objects. + + \note Supported geometries are: box, sphere, capsule, convex. Other geometry types will be ignored. + \note If eTOUCH is returned from the filter callback, it will trigger an error and the hit will be discarded. + + The function sweeps all shapes attached to a given rigid body through space and reports the nearest + object in the scene which intersects any of of the shapes swept paths. + Information about the closest intersection is written to a #PxSweepHit structure. + + \param[in] body The rigid body to sweep. + \param[in] scene The scene object to process the query. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. + \param[in] outputFlags Specifies which properties should be written to the hit information. + \param[out] closestHit Closest hit result. + \param[out] shapeIndex Index of the body shape that caused the closest hit. + \param[in] filterData If any word in filterData.data is non-zero then filterData.data will be used for filtering, + otherwise shape->getQueryFilterData() will be used instead. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxQueryFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + + \return True if a blocking hit was found. + + @see PxScene PxQueryFlags PxFilterData PxBatchQueryPreFilterShader PxBatchQueryPostFilterShader PxSweepHit + */ + static bool linearSweepSingle( + PxRigidBody& body, PxScene& scene, const PxVec3& unitDir, const PxReal distance, + PxHitFlags outputFlags, + PxSweepHit& closestHit, PxU32& shapeIndex, + const PxQueryFilterData& filterData = PxQueryFilterData(), + PxQueryFilterCallback* filterCall = NULL, + const PxQueryCache* cache = NULL, + const PxReal inflation=0.0f); + + /** + \brief Performs a linear sweep through space with the body's geometry objects, returning all overlaps. + + \note Supported geometries are: box, sphere, capsule, convex. Other geometry types will be ignored. + + This function sweeps all shapes attached to a given rigid body through space and reports all + objects in the scene that intersect any of the shapes' swept paths until there are no more objects to report + or a blocking hit is encountered. + + \param[in] body The rigid body to sweep. + \param[in] scene The scene object to process the query. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. + \param[in] outputFlags Specifies which properties should be written to the hit information. + \param[out] touchHitBuffer Raycast hit information buffer. If the buffer overflows, an arbitrary subset of touch hits + is returned (typically the query should be restarted with a larger buffer). + \param[out] touchHitShapeIndices After the query is completed, touchHitShapeIndices[i] will contain the body index that caused the hit stored in hitBuffer[i] + \param[in] touchHitBufferSize Size of both touch hit buffers in elements. + \param[out] block Closest blocking hit is returned via this reference. + \param[out] blockingShapeIndex Set to -1 if if a blocking hit was not found, otherwise set to closest blocking hit shape index. The touching hits are reported separately in hitBuffer. + \param[out] overflow Set to true if touchHitBuffer didn't have enough space for all results. Touch hits will be incomplete if overflow occurred. Possible solution is to restart the query with a larger buffer. + \param[in] filterData If any word in filterData.data is non-zero then filterData.data will be used for filtering, + otherwise shape->getQueryFilterData() will be used instead. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxQueryFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + + \return the number of touching hits. If overflow is set to true, the results are incomplete. In case of overflow there are also no guarantees that all touching hits returned are closer than the blocking hit. + + @see PxScene PxQueryFlags PxFilterData PxBatchQueryPreFilterShader PxBatchQueryPostFilterShader PxSweepHit + */ + static PxU32 linearSweepMultiple( + PxRigidBody& body, PxScene& scene, const PxVec3& unitDir, const PxReal distance, + PxHitFlags outputFlags, + PxSweepHit* touchHitBuffer, PxU32* touchHitShapeIndices, PxU32 touchHitBufferSize, + PxSweepHit& block, PxI32& blockingShapeIndex, bool& overflow, + const PxQueryFilterData& filterData = PxQueryFilterData(), + PxQueryFilterCallback* filterCall = NULL, + const PxQueryCache* cache = NULL, const PxReal inflation = 0.0f); + + + /** + \brief Compute the change to linear and angular velocity that would occur if an impulsive force and torque were to be applied to a specified rigid body. + + The rigid body is left unaffected unless a subsequent independent call is executed that actually applies the computed changes to velocity and angular velocity. + + \note if this call is used to determine the velocity delta for an articulation link, only the mass properties of the link are taken into account. + + @see PxRigidBody::getLinearVelocity, PxRigidBody::setLinearVelocity, PxRigidBody::getAngularVelocity, PxRigidBody::setAngularVelocity + + \param[in] body The body under consideration. + \param[in] impulsiveForce The impulsive force that would be applied to the specified rigid body. + \param[in] impulsiveTorque The impulsive torque that would be applied to the specified rigid body. + \param[out] deltaLinearVelocity The change in linear velocity that would arise if impulsiveForce was to be applied to the specified rigid body. + \param[out] deltaAngularVelocity The change in angular velocity that would arise if impulsiveTorque was to be applied to the specified rigid body. + */ + static void computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxVec3& impulsiveForce, const PxVec3& impulsiveTorque, PxVec3& deltaLinearVelocity, PxVec3& deltaAngularVelocity); + + /** + \brief Computes the linear and angular velocity change vectors for a given impulse at a world space position taking a mass and inertia scale into account + + This function is useful for extracting the respective linear and angular velocity changes from a contact or joint when the mass/inertia ratios have been adjusted. + + \note if this call is used to determine the velocity delta for an articulation link, only the mass properties of the link are taken into account. + + \param[in] body The rigid body + \param[in] globalPose The body's world space transform + \param[in] point The point in world space where the impulse is applied + \param[in] impulse The impulse vector in world space + \param[in] invMassScale The inverse mass scale + \param[in] invInertiaScale The inverse inertia scale + \param[out] deltaLinearVelocity The linear velocity change + \param[out] deltaAngularVelocity The angular velocity change + */ + + static void computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale, + const PxReal invInertiaScale, PxVec3& deltaLinearVelocity, PxVec3& deltaAngularVelocity); + + /** + \brief Computes the linear and angular impulse vectors for a given impulse at a world space position taking a mass and inertia scale into account + + This function is useful for extracting the respective linear and angular impulses from a contact or joint when the mass/inertia ratios have been adjusted. + + \param[in] body The rigid body + \param[in] globalPose The body's world space transform + \param[in] point The point in world space where the impulse is applied + \param[in] impulse The impulse vector in world space + \param[in] invMassScale The inverse mass scale + \param[in] invInertiaScale The inverse inertia scale + \param[out] linearImpulse The linear impulse + \param[out] angularImpulse The angular impulse + */ + static void computeLinearAngularImpulse(const PxRigidBody& body, const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale, + const PxReal invInertiaScale, PxVec3& linearImpulse, PxVec3& angularImpulse); + + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxSceneQueryExt.h b/src/PhysX/physx/include/extensions/PxSceneQueryExt.h new file mode 100644 index 000000000..1d4b86c01 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxSceneQueryExt.h @@ -0,0 +1,309 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_SCENE_QUERY_H +#define PX_PHYSICS_EXTENSIONS_SCENE_QUERY_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" + +#include "PxScene.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +// These types have been deprecated (removed) in PhysX 3.4. We typedef them to the new types here for easy migration from 3.3 to 3.4. +typedef PxQueryHit PxSceneQueryHit; +typedef PxQueryFilterData PxSceneQueryFilterData; +typedef PxQueryFilterCallback PxSceneQueryFilterCallback; +typedef PxQueryCache PxSceneQueryCache; +typedef PxHitFlag PxSceneQueryFlag; +typedef PxHitFlags PxSceneQueryFlags; + +/** +\brief utility functions for use with PxScene, related to scene queries. + +Some of these functions have been deprecated (removed) in PhysX 3.4. We re-implement them here for easy migration from 3.3 to 3.4. + +@see PxShape +*/ + +class PxSceneQueryExt +{ +public: + + /** + \brief Raycast returning any blocking hit, not necessarily the closest. + + Returns whether any rigid actor is hit along the ray. + + \note Shooting a ray from within an object leads to different results depending on the shape type. Please check the details in article SceneQuery. User can ignore such objects by using one of the provided filter mechanisms. + + \param[in] scene The scene + \param[in] origin Origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] distance Length of the ray. Needs to be larger than 0. + \param[out] hit Raycast hit information. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first. If no hit is found the ray gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \return True if a blocking hit was found. + + @see PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryCache PxSceneQueryHit + */ + static bool raycastAny( const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryHit& hit, const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, const PxSceneQueryCache* cache = NULL); + + /** + \brief Raycast returning a single result. + + Returns the first rigid actor that is hit along the ray. Data for a blocking hit will be returned as specified by the outputFlags field. Touching hits will be ignored. + + \note Shooting a ray from within an object leads to different results depending on the shape type. Please check the details in article SceneQuery. User can ignore such objects by using one of the provided filter mechanisms. + + \param[in] scene The scene + \param[in] origin Origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] distance Length of the ray. Needs to be larger than 0. + \param[in] outputFlags Specifies which properties should be written to the hit information + \param[out] hit Raycast hit information. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \return True if a blocking hit was found. + + @see PxSceneQueryFlags PxRaycastHit PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryCache + */ + static bool raycastSingle( const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, PxRaycastHit& hit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, const PxSceneQueryCache* cache = NULL); + + /** + \brief Raycast returning multiple results. + + Find all rigid actors that get hit along the ray. Each result contains data as specified by the outputFlags field. + + \note Touching hits are not ordered. + + \note Shooting a ray from within an object leads to different results depending on the shape type. Please check the details in article SceneQuery. User can ignore such objects by using one of the provided filter mechanisms. + + \param[in] scene The scene + \param[in] origin Origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] distance Length of the ray. Needs to be larger than 0. + \param[in] outputFlags Specifies which properties should be written to the hit information + \param[out] hitBuffer Raycast hit information buffer. If the buffer overflows, the blocking hit is returned as the last entry together with an arbitrary subset + of the nearer touching hits (typically the query should be restarted with a larger buffer). + \param[in] hitBufferSize Size of the hit buffer. + \param[out] blockingHit True if a blocking hit was found. If found, it is the last in the buffer, preceded by any touching hits which are closer. Otherwise the touching hits are listed. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be touching. + \param[in] cache Cached hit shape (optional). Ray is tested against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \return Number of hits in the buffer, or -1 if the buffer overflowed. + + @see PxSceneQueryFlags PxRaycastHit PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryCache + */ + static PxI32 raycastMultiple( const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, + PxRaycastHit* hitBuffer, PxU32 hitBufferSize, bool& blockingHit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, const PxSceneQueryCache* cache = NULL); + + /** + \brief Sweep returning any blocking hit, not necessarily the closest. + + Returns whether any rigid actor is hit along the sweep path. + + \note If a shape from the scene is already overlapping with the query shape in its starting position, behavior is controlled by the PxSceneQueryFlag::eINITIAL_OVERLAP flag. + + \param[in] scene The scene + \param[in] geometry Geometry of object to sweep (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the sweep object. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. Will be clamped to PX_MAX_SWEEP_DISTANCE. + \param[in] queryFlags Combination of PxSceneQueryFlag defining the query behavior + \param[out] hit Sweep hit information. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Sweep is performed against cached shape first. If no hit is found the sweep gets queried against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + \return True if a blocking hit was found. + + @see PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryHit PxSceneQueryCache + */ + static bool sweepAny( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags queryFlags, + PxSceneQueryHit& hit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, + const PxSceneQueryCache* cache = NULL, + PxReal inflation = 0.0f); + + /** + \brief Sweep returning a single result. + + Returns the first rigid actor that is hit along the ray. Data for a blocking hit will be returned as specified by the outputFlags field. Touching hits will be ignored. + + \note If a shape from the scene is already overlapping with the query shape in its starting position, behavior is controlled by the PxSceneQueryFlag::eINITIAL_OVERLAP flag. + + \param[in] scene The scene + \param[in] geometry Geometry of object to sweep (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the sweep object. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. Will be clamped to PX_MAX_SWEEP_DISTANCE. + \param[in] outputFlags Specifies which properties should be written to the hit information. + \param[out] hit Sweep hit information. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be blocking. + \param[in] cache Cached hit shape (optional). Sweep is performed against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + \return True if a blocking hit was found. + + @see PxSceneQueryFlags PxSweepHit PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryCache + */ + static bool sweepSingle(const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, + PxSweepHit& hit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, + const PxSceneQueryCache* cache = NULL, + PxReal inflation=0.0f); + + /** + \brief Sweep returning multiple results. + + Find all rigid actors that get hit along the sweep. Each result contains data as specified by the outputFlags field. + + \note Touching hits are not ordered. + + \note If a shape from the scene is already overlapping with the query shape in its starting position, behavior is controlled by the PxSceneQueryFlag::eINITIAL_OVERLAP flag. + + \param[in] scene The scene + \param[in] geometry Geometry of object to sweep (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the sweep object. + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. Will be clamped to PX_MAX_SWEEP_DISTANCE. + \param[in] outputFlags Specifies which properties should be written to the hit information. + \param[out] hitBuffer Sweep hit information buffer. If the buffer overflows, the blocking hit is returned as the last entry together with an arbitrary subset + of the nearer touching hits (typically the query should be restarted with a larger buffer). + \param[in] hitBufferSize Size of the hit buffer. + \param[out] blockingHit True if a blocking hit was found. If found, it is the last in the buffer, preceded by any touching hits which are closer. Otherwise the touching hits are listed. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to be touching. + \param[in] cache Cached hit shape (optional). Sweep is performed against cached shape first then against the scene. + Note: Filtering is not executed for a cached shape if supplied; instead, if a hit is found, it is assumed to be a blocking hit. + Note: Using past touching hits as cache will produce incorrect behavior since the cached hit will always be treated as blocking. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + \return Number of hits in the buffer, or -1 if the buffer overflowed. + + @see PxSceneQueryFlags PxSweepHit PxSceneQueryFilterData PxSceneQueryFilterCallback PxSceneQueryCache + */ + static PxI32 sweepMultiple( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, PxSweepHit* hitBuffer, PxU32 hitBufferSize, bool& blockingHit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL, const PxSceneQueryCache* cache = NULL, + PxReal inflation = 0.0f); + + /** + \brief Test overlap between a geometry and objects in the scene. + + \note Filtering: Overlap tests do not distinguish between touching and blocking hit types. Both get written to the hit buffer. + + \note PxHitFlag::eMESH_MULTIPLE and PxHitFlag::eMESH_BOTH_SIDES have no effect in this case + + \param[in] scene The scene + \param[in] geometry Geometry of object to check for overlap (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the object. + \param[out] hitBuffer Buffer to store the overlapping objects to. If the buffer overflows, an arbitrary subset of overlapping objects is stored (typically the query should be restarted with a larger buffer). + \param[in] hitBufferSize Size of the hit buffer. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to overlap. + \return Number of hits in the buffer, or -1 if the buffer overflowed. + + @see PxSceneQueryFlags PxSceneQueryFilterData PxSceneQueryFilterCallback + */ + static PxI32 overlapMultiple( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, + PxOverlapHit* hitBuffer, PxU32 hitBufferSize, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL); + + /** + \brief Test returning, for a given geometry, any overlapping object in the scene. + + \note Filtering: Overlap tests do not distinguish between touching and blocking hit types. Both trigger a hit. + + \note PxHitFlag::eMESH_MULTIPLE and PxHitFlag::eMESH_BOTH_SIDES have no effect in this case + + \param[in] scene The scene + \param[in] geometry Geometry of object to check for overlap (supported types are: box, sphere, capsule, convex). + \param[in] pose Pose of the object. + \param[out] hit Pointer to store the overlapping object to. + \param[in] filterData Filtering data and simple logic. + \param[in] filterCall Custom filtering logic (optional). Only used if the corresponding #PxHitFlag flags are set. If NULL, all hits are assumed to overlap. + \return True if an overlap was found. + + @see PxSceneQueryFlags PxSceneQueryFilterData PxSceneQueryFilterCallback + */ + static bool overlapAny( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, + PxOverlapHit& hit, + const PxSceneQueryFilterData& filterData = PxSceneQueryFilterData(), + PxSceneQueryFilterCallback* filterCall = NULL); +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxSerialization.h b/src/PhysX/physx/include/extensions/PxSerialization.h new file mode 100644 index 000000000..2d111ca78 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxSerialization.h @@ -0,0 +1,284 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_SERIALIZATION_H +#define PX_SERIALIZATION_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxBase.h" +#include "cooking/PxCooking.h" +#include "foundation/PxIO.h" +#include "common/PxTolerancesScale.h" +#include "common/PxTypeInfo.h" +#include "common/PxStringTable.h" + +// +// Important: if you adjust the comment about binary compatible versions below, don't forget to adjust the compatibility list in +// sBinaryCompatibleVersionsbinary as well +// +/** +PX_BINARY_SERIAL_VERSION is used to specify the binary data format compatibility additionally to the physics sdk version. +The binary format version is defined as "PX_PHYSICS_VERSION_MAJOR.PX_PHYSICS_VERSION_MINOR.PX_PHYSICS_VERSION_BUGFIX-PX_BINARY_SERIAL_VERSION". +The following binary format versions are compatible with the current physics version: + (no compatible versions) + +The PX_BINARY_SERIAL_VERSION for a given PhysX release is typically 0. If incompatible modifications are made to a customer specific branch the +number should be increased. +*/ +#define PX_BINARY_SERIAL_VERSION 0 + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxBinaryConverter; + +/** +\brief Utility functions for serialization + +@see PxCollection, PxSerializationRegistry +*/ +class PxSerialization +{ +public: + /** + \brief Additional PxScene and PxPhysics options stored in XML serialized data. + + The PxXmlMiscParameter parameter can be serialized and deserialized along with PxCollection instances (XML only). + This is for application use only and has no impact on how objects are serialized or deserialized. + @see PxSerialization::createCollectionFromXml, PxSerialization::serializeCollectionToXml + */ + struct PxXmlMiscParameter + { + /** + \brief Up vector for the scene reference coordinate system. + */ + PxVec3 upVector; + + /** + \brief Tolerances scale to be used for the scene. + */ + PxTolerancesScale scale; + + PxXmlMiscParameter() : upVector(0) {} + PxXmlMiscParameter(PxVec3& inUpVector, PxTolerancesScale inScale) : upVector(inUpVector), scale(inScale) {} + }; + + /** + \brief Returns whether the collection is serializable with the externalReferences collection. + + Some definitions to explain whether a collection can be serialized or not: + + For definitions of requires and complete see #PxSerialization::complete + + A serializable object is subordinate if it cannot be serialized on its own + The following objects are subordinate: + - articulation links + - articulation joints + - joints + + A collection C can be serialized with external references collection D iff + - C is complete relative to D (no dangling references) + - Every object in D required by an object in C has a valid ID (no unnamed references) + - Every subordinate object in C is required by another object in C (no orphans) + + \param[in] collection Collection to be checked + \param[in] sr PxSerializationRegistry instance with information about registered classes. + \param[in] externalReferences the external References collection + \return Whether the collection is serializable + @see PxSerialization::complete, PxSerialization::serializeCollectionToBinary, PxSerialization::serializeCollectionToXml, PxSerializationRegistry + */ + static bool isSerializable(PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* externalReferences = NULL); + + /** + \brief Adds to a collection all objects such that it can be successfully serialized. + + A collection C is complete relative to an other collection D if every object required by C is either in C or D. + This function adds objects to a collection, such that it becomes complete with respect to the exceptFor collection. + Completeness is needed for serialization. See #PxSerialization::serializeCollectionToBinary, + #PxSerialization::serializeCollectionToXml. + + Sdk objects require other sdk object according to the following rules: + - joints require their actors and constraint + - rigid actors require their shapes + - shapes require their material(s) and mesh (triangle mesh, convex mesh or height field), if any + - articulations require their links and joints + - aggregates require their actors + + If followJoints is specified another rule is added: + - actors require their joints + + Specifying followJoints will make whole jointed actor chains being added to the collection. Following chains + is interrupted whenever a object in exceptFor is encountered. + + \param[in,out] collection Collection which is completed + \param[in] sr PxSerializationRegistry instance with information about registered classes. + \param[in] exceptFor Optional exemption collection + \param[in] followJoints Specifies whether joints should be added for jointed actors + @see PxCollection, PxSerialization::serializeCollectionToBinary, PxSerialization::serializeCollectionToXml, PxSerializationRegistry + */ + static void complete(PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* exceptFor = NULL, bool followJoints = false); + + /** + \brief Creates PxSerialObjectId values for unnamed objects in a collection. + + Creates PxSerialObjectId names for unnamed objects in a collection starting at a base value and incrementing, + skipping values that are already assigned to objects in the collection. + + \param[in,out] collection Collection for which names are created + \param[in] base Start address for PxSerialObjectId names + @see PxCollection + */ + static void createSerialObjectIds(PxCollection& collection, const PxSerialObjectId base); + + /** + \brief Creates a PxCollection from XML data. + + \param inputData The input data containing the XML collection. + \param cooking PxCooking instance used for sdk object instantiation. + \param sr PxSerializationRegistry instance with information about registered classes. + \param externalRefs PxCollection used to resolve external references. + \param stringTable PxStringTable instance used for storing object names. + \param outArgs Optional parameters of physics and scene deserialized from XML. See #PxSerialization::PxXmlMiscParameter + \return a pointer to a PxCollection if successful or NULL if it failed. + + @see PxCollection, PxSerializationRegistry, PxInputData, PxStringTable, PxCooking, PxSerialization::PxXmlMiscParameter + */ + static PxCollection* createCollectionFromXml(PxInputData& inputData, PxCooking& cooking, PxSerializationRegistry& sr, const PxCollection* externalRefs = NULL, PxStringTable* stringTable = NULL, PxXmlMiscParameter* outArgs = NULL); + + /** + \brief Deserializes a PxCollection from memory. + + Creates a collection from memory. If the collection has external dependencies another collection + can be provided to resolve these. + + The memory block provided has to be 128 bytes aligned and contain a contiguous serialized collection as written + by PxSerialization::serializeCollectionToBinary. The contained binary data needs to be compatible with the current binary format version + which is defined by "PX_PHYSICS_VERSION_MAJOR.PX_PHYSICS_VERSION_MINOR.PX_PHYSICS_VERSION_BUGFIX-PX_BINARY_SERIAL_VERSION". + For a list of compatible sdk releases refer to the documentation of PX_BINARY_SERIAL_VERSION. + + \param[in] memBlock Pointer to memory block containing the serialized collection + \param[in] sr PxSerializationRegistry instance with information about registered classes. + \param[in] externalRefs Collection to resolve external dependencies + + @see PxCollection, PxSerialization::complete, PxSerialization::serializeCollectionToBinary, PxSerializationRegistry, PX_BINARY_SERIAL_VERSION + */ + static PxCollection* createCollectionFromBinary(void* memBlock, PxSerializationRegistry& sr, const PxCollection* externalRefs = NULL); + + /** + \brief Serializes a physics collection to an XML output stream. + + The collection to be serialized needs to be complete @see PxSerialization.complete. + Optionally the XML may contain meshes in binary cooked format for fast loading. It does this when providing a valid non-null PxCooking pointer. + + \note Serialization of objects in a scene that is simultaneously being simulated is not supported and leads to undefined behavior. + + \param outputStream Stream to save collection to. + \param collection PxCollection instance which is serialized. The collection needs to be complete with respect to the externalRefs collection. + \param sr PxSerializationRegistry instance with information about registered classes. + \param cooking Optional pointer to cooking instance. If provided, cooked mesh data is cached for fast loading. + \param externalRefs Collection containing external references. + \param inArgs Optional parameters of physics and scene serialized to XML along with the collection. See #PxSerialization::PxXmlMiscParameter + \return true if the collection is successfully serialized. + + @see PxCollection, PxOutputStream, PxSerializationRegistry, PxCooking, PxSerialization::PxXmlMiscParameter + */ + static bool serializeCollectionToXml(PxOutputStream& outputStream, PxCollection& collection, PxSerializationRegistry& sr, PxCooking* cooking = NULL, const PxCollection* externalRefs = NULL, PxXmlMiscParameter* inArgs = NULL); + + /** + \brief Serializes a collection to a binary stream. + + Serializes a collection to a stream. In order to resolve external dependencies the externalReferences collection has to be provided. + Optionally names of objects that where set for example with #PxActor::setName are serialized along with the objects. + + The collection can be successfully serialized if isSerializable(collection) returns true. See #isSerializable. + + The implementation of the output stream needs to fulfill the requirements on the memory block input taken by + PxSerialization::createCollectionFromBinary. + + \note Serialization of objects in a scene that is simultaneously being simulated is not supported and leads to undefined behavior. + + \param[out] outputStream into which the collection is serialized + \param[in] collection Collection to be serialized + \param[in] sr PxSerializationRegistry instance with information about registered classes. + \param[in] externalRefs Collection used to resolve external dependencies + \param[in] exportNames Specifies whether object names are serialized + \return Whether serialization was successful + + @see PxCollection, PxOutputStream, PxSerialization::complete, PxSerialization::createCollectionFromBinary, PxSerializationRegistry + */ + static bool serializeCollectionToBinary(PxOutputStream& outputStream, PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* externalRefs = NULL, bool exportNames = false ); + + /** + \brief Dumps the binary meta-data to a stream. + + A meta-data file contains information about the SDK's internal classes and about custom user types ready + for serialization. Such a file is needed to convert binary-serialized data from one platform to another (re-targeting). + The converter needs meta-data files for the source and target platforms to perform conversions. + + Custom user types can be supported with PxSerializationRegistry::registerBinaryMetaDataCallback (see the guide for more information). + + \param[out] outputStream Stream to write meta data to + \param[in] sr PxSerializationRegistry instance with information about registered classes used for conversion. + + @see PxOutputStream, PxSerializationRegistry + */ + static void dumpBinaryMetaData(PxOutputStream& outputStream, PxSerializationRegistry& sr); + + /** + \brief Creates binary converter for re-targeting binary-serialized data. + + \return Binary converter instance. + */ + static PxBinaryConverter* createBinaryConverter(); + + /** + \brief Creates an application managed registry for serialization. + + \param[in] physics Physics SDK to generate create serialization registry + + \return PxSerializationRegistry instance. + + @see PxSerializationRegistry + */ + static PxSerializationRegistry* createSerializationRegistry(PxPhysics& physics); +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxShapeExt.h b/src/PhysX/physx/include/extensions/PxShapeExt.h new file mode 100644 index 000000000..02228d5e4 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxShapeExt.h @@ -0,0 +1,158 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_SHAPE_H +#define PX_PHYSICS_EXTENSIONS_SHAPE_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" + +#include "PxShape.h" +#include "PxRigidActor.h" +#include "geometry/PxGeometryQuery.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief utility functions for use with PxShape + +@see PxShape +*/ + +class PxShapeExt +{ +public: + /** + \brief Retrieves the world space pose of the shape. + + \param[in] shape The shape for which to get the global pose. + \param[in] actor The actor to which the shape is attached + + \return Global pose of shape. + */ + static PX_INLINE PxTransform getGlobalPose(const PxShape& shape, const PxRigidActor& actor) + { + return actor.getGlobalPose() * shape.getLocalPose(); + } + + /** + \brief Raycast test against the shape. + + \param[in] shape the shape + \param[in] actor the actor to which the shape is attached + \param[in] rayOrigin The origin of the ray to test the geometry object against + \param[in] rayDir The direction of the ray to test the geometry object against + \param[in] maxDist Maximum ray length + \param[in] hitFlags Specify which properties per hit should be computed and written to result hit array. Combination of #PxHitFlag flags + \param[in] maxHits max number of returned hits = size of 'rayHits' buffer + \param[out] rayHits Raycast hits information + \return Number of hits between the ray and the shape + + @see PxRaycastHit PxTransform + */ + static PX_INLINE PxU32 raycast(const PxShape& shape, const PxRigidActor& actor, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, PxHitFlags hitFlags, + PxU32 maxHits, PxRaycastHit* rayHits) + { + return PxGeometryQuery::raycast( + rayOrigin, rayDir, shape.getGeometry().any(), getGlobalPose(shape, actor), maxDist, hitFlags, maxHits, rayHits); + } + + /** + \brief Test overlap between the shape and a geometry object + + \param[in] shape the shape + \param[in] actor the actor to which the shape is attached + \param[in] otherGeom The other geometry object to test overlap with + \param[in] otherGeomPose Pose of the other geometry object + \return True if the shape overlaps the geometry object + + @see PxGeometry PxTransform + */ + static PX_INLINE bool overlap(const PxShape& shape, const PxRigidActor& actor, + const PxGeometry& otherGeom, const PxTransform& otherGeomPose) + { + return PxGeometryQuery::overlap(shape.getGeometry().any(), getGlobalPose(shape, actor), otherGeom, otherGeomPose); + } + + /** + \brief Sweep a geometry object against the shape. + + Currently only box, sphere, capsule and convex mesh shapes are supported, i.e. the swept geometry object must be one of those types. + + \param[in] shape the shape + \param[in] actor the actor to which the shape is attached + \param[in] unitDir Normalized direction along which the geometry object should be swept. + \param[in] distance Sweep distance. Needs to be larger than 0. + \param[in] otherGeom The geometry object to sweep against the shape + \param[in] otherGeomPose Pose of the geometry object + \param[out] sweepHit The sweep hit information. Only valid if this method returns true. + \param[in] hitFlags Specify which properties per hit should be computed and written to result hit array. Combination of #PxHitFlag flags + \return True if the swept geometry object hits the shape + + @see PxGeometry PxTransform PxSweepHit + */ + static PX_INLINE bool sweep(const PxShape& shape, const PxRigidActor& actor, + const PxVec3& unitDir, const PxReal distance, const PxGeometry& otherGeom, const PxTransform& otherGeomPose, + PxSweepHit& sweepHit, PxHitFlags hitFlags) + { + return PxGeometryQuery::sweep(unitDir, distance, otherGeom, otherGeomPose, shape.getGeometry().any(), getGlobalPose(shape, actor), sweepHit, hitFlags); + } + + + /** + \brief Retrieves the axis aligned bounding box enclosing the shape. + + \return The shape's bounding box. + + \param[in] shape the shape + \param[in] actor the actor to which the shape is attached + \param[in] inflation Scale factor for computed world bounds. Box extents are multiplied by this value. + + @see PxBounds3 + */ + static PX_INLINE PxBounds3 getWorldBounds(const PxShape& shape, const PxRigidActor& actor, float inflation=1.01f) + { + return PxGeometryQuery::getWorldBounds(shape.getGeometry().any(), getGlobalPose(shape, actor), inflation); + } + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxSimpleFactory.h b/src/PhysX/physx/include/extensions/PxSimpleFactory.h new file mode 100644 index 000000000..354fe2080 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxSimpleFactory.h @@ -0,0 +1,337 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_SIMPLE_FACTORY_H +#define PX_PHYSICS_EXTENSIONS_SIMPLE_FACTORY_H +/** \addtogroup extensions + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxTransform.h" +#include "foundation/PxPlane.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxPhysics; + class PxMaterial; + class PxRigidActor; + class PxRigidDynamic; + class PxRigidStatic; + class PxGeometry; + class PxShape; + + +/** \brief simple method to create a PxRigidDynamic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] geometry the geometry of the new object's shape, which must be a sphere, capsule, box or convex + \param[in] material the material for the new object's shape + \param[in] density the density of the new object. Must be greater than zero. + \param[in] shapeOffset an optional offset for the new shape, defaults to identity + + \return a new dynamic actor with the PxRigidBodyFlag, or NULL if it could + not be constructed + + @see PxRigidDynamic PxShapeFlag +*/ + +PxRigidDynamic* PxCreateDynamic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + PxReal density, + const PxTransform& shapeOffset = PxTransform(PxIdentity)); + + +/** \brief simple method to create a PxRigidDynamic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the transform of the new object + \param[in] shape the shape of the new object + \param[in] density the density of the new object. Must be greater than zero. + + \return a new dynamic actor with the PxRigidBodyFlag, or NULL if it could + not be constructed + + @see PxRigidDynamic PxShapeFlag +*/ + +PxRigidDynamic* PxCreateDynamic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape, + PxReal density); + + +/** \brief simple method to create a kinematic PxRigidDynamic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] geometry the geometry of the new object's shape + \param[in] material the material for the new object's shape + \param[in] density the density of the new object. Must be greater than zero if the object is to participate in simulation. + \param[in] shapeOffset an optional offset for the new shape, defaults to identity + + \note unlike PxCreateDynamic, the geometry is not restricted to box, capsule, sphere or convex. However, + kinematics of other geometry types may not participate in simulation collision and may be used only for + triggers or scene queries of moving objects under animation control. In this case the density parameter + will be ignored and the created shape will be set up as a scene query only shape (see #PxShapeFlag::eSCENE_QUERY_SHAPE) + + \return a new dynamic actor with the PxRigidBodyFlag::eKINEMATIC set, or NULL if it could + not be constructed + + @see PxRigidDynamic PxShapeFlag +*/ + +PxRigidDynamic* PxCreateKinematic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + PxReal density, + const PxTransform& shapeOffset = PxTransform(PxIdentity)); + + +/** \brief simple method to create a kinematic PxRigidDynamic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] density the density of the new object. Must be greater than zero if the object is to participate in simulation. + \param[in] shape the shape of the new object + + \note unlike PxCreateDynamic, the geometry is not restricted to box, capsule, sphere or convex. However, + kinematics of other geometry types may not participate in simulation collision and may be used only for + triggers or scene queries of moving objects under animation control. In this case the density parameter + will be ignored and the created shape will be set up as a scene query only shape (see #PxShapeFlag::eSCENE_QUERY_SHAPE) + + \return a new dynamic actor with the PxRigidBodyFlag::eKINEMATIC set, or NULL if it could + not be constructed + + @see PxRigidDynamic PxShapeFlag +*/ + +PxRigidDynamic* PxCreateKinematic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape, + PxReal density); + + +/** \brief simple method to create a PxRigidStatic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] geometry the geometry of the new object's shape + \param[in] material the material for the new object's shape + \param[in] shapeOffset an optional offset for the new shape, defaults to identity + + \return a new static actor, or NULL if it could not be constructed + + @see PxRigidStatic +*/ + +PxRigidStatic* PxCreateStatic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + const PxTransform& shapeOffset = PxTransform(PxIdentity)); + + +/** \brief simple method to create a PxRigidStatic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] shape the new object's shape + + \return a new static actor, or NULL if it could not be constructed + + @see PxRigidStatic +*/ + +PxRigidStatic* PxCreateStatic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape); + + +/** \brief simple method to create a PxRigidStatic actor with a single PxShape. + + \param[in] sdk the PxPhysics object + \param[in] transform the global pose of the new object + \param[in] shape the new object's shape + + \return a new static actor, or NULL if it could not be constructed + + @see PxRigidStatic +*/ + +PxRigidStatic* PxCreateStatic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape); + + +/** +\brief create a shape by copying attributes from another shape + +The function clones a PxShape. The following properties are copied: +- geometry +- flags +- materials +- actor-local pose +- contact offset +- rest offset +- simulation filter data +- query filter data + +The following are not copied and retain their default values: +- name +- user data + +\param[in] physicsSDK - the physics SDK used to allocate the shape +\param[in] shape the shape from which to take the attributes. +\param[in] isExclusive whether the new shape should be an exclusive or shared shape. + +\return the newly-created rigid static + +*/ + +PxShape* PxCloneShape(PxPhysics& physicsSDK, + const PxShape& shape, + bool isExclusive); + + + +/** +\brief create a static body by copying attributes from another rigid actor + +The function clones a PxRigidDynamic or PxRigidStatic as a PxRigidStatic. A uniform scale is applied. The following properties are copied: +- shapes +- actor flags +- owner client and client behavior bits + +The following are not copied and retain their default values: +- name +- joints or observers +- aggregate or scene membership +- user data + +\note Transforms are not copied with bit-exact accuracy. + +\param[in] physicsSDK - the physics SDK used to allocate the rigid static +\param[in] actor the rigid actor from which to take the attributes. +\param[in] transform the transform of the new static. + +\return the newly-created rigid static + +*/ + +PxRigidStatic* PxCloneStatic(PxPhysics& physicsSDK, + const PxTransform& transform, + const PxRigidActor& actor); + + +/** +\brief create a dynamic body by copying attributes from an existing body + +The following properties are copied: +- shapes +- actor flags and rigidDynamic flags +- mass, moment of inertia, and center of mass frame +- linear and angular velocity +- linear and angular damping +- maximum angular velocity +- position and velocity solver iterations +- maximum depenetration velocity +- sleep threshold +- contact report threshold +- dominance group +- owner client and client behavior bits +- name pointer + +The following are not copied and retain their default values: +- name +- joints or observers +- aggregate or scene membership +- sleep timer +- user data + +\note Transforms are not copied with bit-exact accuracy. + +\param[in] physicsSDK PxPhysics - the physics SDK used to allocate the rigid static +\param[in] body the rigid dynamic to clone. +\param[in] transform the transform of the new dynamic + +\return the newly-created rigid static + +*/ + +PxRigidDynamic* PxCloneDynamic(PxPhysics& physicsSDK, + const PxTransform& transform, + const PxRigidDynamic& body); + + +/** \brief create a plane actor. The plane equation is n.x + d = 0 + + \param[in] sdk the PxPhysics object + \param[in] plane a plane of the form n.x + d = 0 + \param[in] material the material for the new object's shape + + \return a new static actor, or NULL if it could not be constructed + + @see PxRigidStatic +*/ + +PxRigidStatic* PxCreatePlane(PxPhysics& sdk, + const PxPlane& plane, + PxMaterial& material); + + +/** +\brief scale a rigid actor by a uniform scale + +The geometry and relative positions of the actor are multiplied by the given scale value. If the actor is a rigid body or an +articulation link and the scaleMassProps value is true, the mass properties are scaled assuming the density is constant: the +center of mass is linearly scaled, the mass is multiplied by the cube of the scale, and the inertia tensor by the fifth power of the scale. + +\param[in] actor a rigid actor +\param[in] scale the scale by which to multiply the actor. Must be >0. +\param[in] scaleMassProps whether to scale the mass properties +*/ + +void PxScaleRigidActor(PxRigidActor& actor, PxReal scale, bool scaleMassProps = true); + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxSmoothNormals.h b/src/PhysX/physx/include/extensions/PxSmoothNormals.h new file mode 100644 index 000000000..0aa373b44 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxSmoothNormals.h @@ -0,0 +1,61 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_SMOOTH_NORMALS_H +#define PX_PHYSICS_EXTENSIONS_SMOOTH_NORMALS_H +/** \addtogroup extensions + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" + +/** +\brief Builds smooth vertex normals over a mesh. + +- "smooth" because smoothing groups are not supported here +- takes angles into account for correct cube normals computation + +To use 32bit indices pass a pointer in dFaces and set wFaces to zero. Alternatively pass a pointer to +wFaces and set dFaces to zero. + +\param[in] nbTris Number of triangles +\param[in] nbVerts Number of vertices +\param[in] verts Array of vertices +\param[in] dFaces Array of dword triangle indices, or null +\param[in] wFaces Array of word triangle indices, or null +\param[out] normals Array of computed normals (assumes nbVerts vectors) +\param[in] flip Flips the normals or not +\return True on success. +*/ +PX_C_EXPORT bool PX_CALL_CONV PxBuildSmoothNormals(physx::PxU32 nbTris, physx::PxU32 nbVerts, const physx::PxVec3* verts, + const physx::PxU32* dFaces, const physx::PxU16* wFaces, physx::PxVec3* normals, bool flip); + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxSphericalJoint.h b/src/PhysX/physx/include/extensions/PxSphericalJoint.h new file mode 100644 index 000000000..80e513193 --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxSphericalJoint.h @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_SPHERICALJOINT_H +#define PX_SPHERICALJOINT_H +/** \addtogroup extensions + @{ +*/ + +#include "extensions/PxJoint.h" +#include "extensions/PxJointLimit.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxSphericalJoint; + +/** +\brief Create a spherical joint. + + \param[in] physics The physics SDK + \param[in] actor0 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame0 The position and orientation of the joint relative to actor0 + \param[in] actor1 An actor to which the joint is attached. NULL may be used to attach the joint to a specific point in the world frame + \param[in] localFrame1 The position and orientation of the joint relative to actor1 + +@see PxSphericalJoint +*/ +PxSphericalJoint* PxSphericalJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + + +/** +\brief Flags specific to the spherical joint. + +@see PxSphericalJoint +*/ +struct PxSphericalJointFlag +{ + enum Enum + { + eLIMIT_ENABLED = 1<<1 //!< the cone limit for the spherical joint is enabled + }; +}; +typedef PxFlags PxSphericalJointFlags; +PX_FLAGS_OPERATORS(PxSphericalJointFlag::Enum, PxU16) + +/** +\brief A joint which behaves in a similar way to a ball and socket. + + A spherical joint removes all linear degrees of freedom from two objects. + + The position of the joint on each actor is specified by the origin of the body's joint frame. + + A spherical joint may have a cone limit, to restrict the motion to within a certain range. In + addition, the bodies may be projected together if the distance between them exceeds a given threshold. + + Projection, drive and limits are activated by setting the appropriate flags on the joint. + + @see PxRevoluteJointCreate() PxJoint +*/ +class PxSphericalJoint : public PxJoint +{ +public: + + /** + \brief Set the limit cone. + + If enabled, the limit cone will constrain the angular movement of the joint to lie + within an elliptical cone. + + \return the limit cone + + @see PxJointLimitCone setLimit() + */ + virtual PxJointLimitCone getLimitCone() const = 0; + + /** + \brief Get the limit cone. + + \param[in] limit the limit cone + + @see PxJointLimitCone getLimit() + */ + virtual void setLimitCone(const PxJointLimitCone& limit) = 0; + + /** + \brief get the swing angle of the joint from the Y axis + */ + virtual PxReal getSwingYAngle() const = 0; + + /** + \brief get the swing angle of the joint from the Z axis + */ + virtual PxReal getSwingZAngle() const = 0; + + /** + \brief Set the flags specific to the Spherical Joint. + + Default PxSphericalJointFlags(0) + + \param[in] flags The joint flags. + + @see PxSphericalJointFlag setFlag() getFlags() + */ + virtual void setSphericalJointFlags(PxSphericalJointFlags flags) = 0; + + /** + \brief Set a single flag specific to a Spherical Joint to true or false. + + \param[in] flag The flag to set or clear. + \param[in] value the value to which to set the flag + + @see PxSphericalJointFlag, getFlags() setFlags() + */ + virtual void setSphericalJointFlag(PxSphericalJointFlag::Enum flag, bool value) = 0; + + /** + \brief Get the flags specific to the Spherical Joint. + + \return the joint flags + + @see PxSphericalJoint::flags, PxSphericalJointFlag setFlag() setFlags() + */ + virtual PxSphericalJointFlags getSphericalJointFlags() const = 0; + + /** + \brief Set the linear tolerance threshold for projection. Projection is enabled if PxConstraintFlag::ePROJECTION + is set for the joint. + + If the joint separates by more than this distance along its locked degrees of freedom, the solver + will move the bodies to close the distance. + + Setting a very small tolerance may result in simulation jitter or other artifacts. + + Sometimes it is not possible to project (for example when the joints form a cycle). + + Range: [0, PX_MAX_F32)
            + Default: 1e10f + + \param[in] tolerance the linear tolerance threshold + + @see getProjectionLinearTolerance() PxJoint::setConstraintFlags() PxConstraintFlag::ePROJECTION + */ + virtual void setProjectionLinearTolerance(PxReal tolerance) = 0; + + /** + \brief Get the linear tolerance threshold for projection. + + \return the linear tolerance threshold + + @see setProjectionLinearTolerance() + */ + virtual PxReal getProjectionLinearTolerance() const = 0; + + /** + \brief Returns string name of PxSphericalJoint, used for serialization + */ + virtual const char* getConcreteTypeName() const { return "PxSphericalJoint"; } + +protected: + + //serialization + + /** + \brief Constructor + */ + PX_INLINE PxSphericalJoint(PxType concreteType, PxBaseFlags baseFlags) : PxJoint(concreteType, baseFlags) {} + + /** + \brief Deserialization constructor + */ + PX_INLINE PxSphericalJoint(PxBaseFlags baseFlags) : PxJoint(baseFlags) {} + + /** + \brief Returns whether a given type name matches with the type of this instance + */ + virtual bool isKindOf(const char* name) const { return !::strcmp("PxSphericalJoint", name) || PxJoint::isKindOf(name); } + + //~serialization +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/extensions/PxStringTableExt.h b/src/PhysX/physx/include/extensions/PxStringTableExt.h new file mode 100644 index 000000000..3585983ea --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxStringTableExt.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_STRING_TABLE_EXT_H +#define PX_STRING_TABLE_EXT_H +#include "foundation/Px.h" +#include "common/PxStringTable.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief a factory class for creating PxStringTable with a specific allocator. + +@see PxStringTable +*/ + +class PxStringTableExt +{ +public: + static PxStringTable& createStringTable( physx::PxAllocatorCallback& inAllocator ); +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif diff --git a/src/PhysX/physx/include/extensions/PxTriangleMeshExt.h b/src/PhysX/physx/include/extensions/PxTriangleMeshExt.h new file mode 100644 index 000000000..43dc29e2d --- /dev/null +++ b/src/PhysX/physx/include/extensions/PxTriangleMeshExt.h @@ -0,0 +1,188 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_TRIANGLE_MESH_H +#define PX_PHYSICS_EXTENSIONS_TRIANGLE_MESH_H +/** \addtogroup extensions + @{ +*/ + +#include "PxPhysXConfig.h" +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxGeometry; +class PxTriangleMeshGeometry; +class PxHeightFieldGeometry; + + /** + \brief Utility class to find mesh triangles touched by a specified geometry object. + + This class is a helper calling PxMeshQuery::findOverlapTriangleMesh or PxMeshQuery::findOverlapHeightField under the hood, + while taking care of necessary memory management issues. + + PxMeshQuery::findOverlapTriangleMesh and PxMeshQuery::findOverlapHeightField are the "raw" functions operating on user-provided fixed-size + buffers. These functions abort with an error code in case of buffer overflow. PxMeshOverlapUtil is a convenient helper function checking + this error code, and resizing buffers appropriately, until the desired call succeeds. + + Returned triangle indices are stored within the class, and can be used with PxMeshQuery::getTriangle() to retrieve the triangle properties. + */ + class PxMeshOverlapUtil + { + public: + PxMeshOverlapUtil(); + ~PxMeshOverlapUtil(); + /** + \brief Find the mesh triangles which touch the specified geometry object. + + \param[in] geom The geometry object to test for mesh triangle overlaps. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry and #PxBoxGeometry + \param[in] geomPose Pose of the geometry object + \param[in] meshGeom The triangle mesh geometry to check overlap against + \param[in] meshPose Pose of the triangle mesh + \return Number of overlaps found. Triangle indices can then be accessed through the #getResults() function. + + @see PxGeometry PxTransform PxTriangleMeshGeometry PxMeshQuery::findOverlapTriangleMesh + */ + PxU32 findOverlap(const PxGeometry& geom, const PxTransform& geomPose, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose); + + /** + \brief Find the height field triangles which touch the specified geometry object. + + \param[in] geom The geometry object to test for height field overlaps. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry and #PxBoxGeometry. The sphere and capsule queries are currently conservative estimates. + \param[in] geomPose Pose of the geometry object + \param[in] hfGeom The height field geometry to check overlap against + \param[in] hfPose Pose of the height field + \return Number of overlaps found. Triangle indices can then be accessed through the #getResults() function. + + @see PxGeometry PxTransform PxHeightFieldGeometry PxMeshQuery::findOverlapHeightField + */ + PxU32 findOverlap(const PxGeometry& geom, const PxTransform& geomPose, const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose); + + /** + \brief Retrieves array of triangle indices after a findOverlap call. + \return Indices of touched triangles + */ + PX_FORCE_INLINE const PxU32* getResults() const { return mResultsMemory; } + + /** + \brief Retrieves number of triangle indices after a findOverlap call. + \return Number of touched triangles + */ + PX_FORCE_INLINE PxU32 getNbResults() const { return mNbResults; } + + private: + PxU32* mResultsMemory; + PxU32 mResults[256]; + PxU32 mNbResults; + PxU32 mMaxNbResults; + }; + + /** + \brief Computes an approximate minimum translational distance (MTD) between a geometry object and a mesh. + + This iterative function computes an approximate vector that can be used to depenetrate a geom object + from a triangle mesh. Returned depenetration vector should be applied to 'geom', to get out of the mesh. + + The function works best when the amount of overlap between the geom object and the mesh is small. If the + geom object's center goes inside the mesh, backface culling usually kicks in, no overlap is detected, + and the function does not compute an MTD vector. + + The function early exits if no overlap is detected after a depenetration attempt. This means that if + maxIter = N, the code will attempt at most N iterations but it might exit earlier if depenetration has + been successful. Usually N = 4 gives good results. + + \param[out] direction Computed MTD unit direction + \param[out] depth Penetration depth. Always positive or zero. + \param[in] geom The geometry object + \param[in] geomPose Pose for the geometry object + \param[in] meshGeom The mesh geometry + \param[in] meshPose Pose for the mesh + \param[in] maxIter Max number of iterations before returning. + \param[out] usedIter Number of depenetrations attempts performed during the call. Will not be returned if the pointer is NULL. + + \return True if the MTD has successfully been computed, i.e. if objects do overlap. + + @see PxGeometry PxTransform PxTriangleMeshGeometry + */ + bool PxComputeTriangleMeshPenetration(PxVec3& direction, + PxReal& depth, + const PxGeometry& geom, + const PxTransform& geomPose, + const PxTriangleMeshGeometry& meshGeom, + const PxTransform& meshPose, + PxU32 maxIter, + PxU32* usedIter = NULL); + + /** + \brief Computes an approximate minimum translational distance (MTD) between a geometry object and a heightfield. + + This iterative function computes an approximate vector that can be used to depenetrate a geom object + from a heightfield. Returned depenetration vector should be applied to 'geom', to get out of the heightfield. + + The function works best when the amount of overlap between the geom object and the mesh is small. If the + geom object's center goes inside the heightfield, backface culling usually kicks in, no overlap is detected, + and the function does not compute an MTD vector. + + The function early exits if no overlap is detected after a depenetration attempt. This means that if + maxIter = N, the code will attempt at most N iterations but it might exit earlier if depenetration has + been successful. Usually N = 4 gives good results. + + \param[out] direction Computed MTD unit direction + \param[out] depth Penetration depth. Always positive or zero. + \param[in] geom The geometry object + \param[in] geomPose Pose for the geometry object + \param[in] heightFieldGeom The heightfield geometry + \param[in] heightFieldPose Pose for the heightfield + \param[in] maxIter Max number of iterations before returning. + \param[out] usedIter Number of depenetrations attempts performed during the call. Will not be returned if the pointer is NULL. + + \return True if the MTD has successfully been computed, i.e. if objects do overlap. + + @see PxGeometry PxTransform PxHeightFieldGeometry + */ + bool PxComputeHeightFieldPenetration(PxVec3& direction, + PxReal& depth, + const PxGeometry& geom, + const PxTransform& geomPose, + const PxHeightFieldGeometry& heightFieldGeom, + const PxTransform& heightFieldPose, + PxU32 maxIter, + PxU32* usedIter = NULL); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/filebuf/PxFileBuf.h b/src/PhysX/physx/include/filebuf/PxFileBuf.h new file mode 100644 index 000000000..b54f09b2a --- /dev/null +++ b/src/PhysX/physx/include/filebuf/PxFileBuf.h @@ -0,0 +1,339 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PSFILEBUFFER_PXFILEBUF_H +#define PSFILEBUFFER_PXFILEBUF_H + +#include "foundation/PxSimpleTypes.h" + +/** \addtogroup foundation + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ + +namespace general_PxIOStream2 +{ +#endif + +PX_PUSH_PACK_DEFAULT + +/** +\brief Callback class for data serialization. + +The user needs to supply an PxFileBuf implementation to a number of methods to allow the SDK to read or write +chunks of binary data. This allows flexibility for the source/destination of the data. For example the PxFileBuf +could store data in a file, memory buffer or custom file format. + +\note It is the users responsibility to ensure that the data is written to the appropriate offset. + +*/ +class PxFileBuf +{ +public: + + enum EndianMode + { + ENDIAN_NONE = 0, // do no conversion for endian mode + ENDIAN_BIG = 1, // always read/write data as natively big endian (Power PC, etc.) + ENDIAN_LITTLE = 2 // always read/write data as natively little endian (Intel, etc.) Default Behavior! + }; + + PxFileBuf(EndianMode mode=ENDIAN_LITTLE) + { + setEndianMode(mode); + } + + virtual ~PxFileBuf(void) + { + + } + + /** + \brief Declares a constant to seek to the end of the stream. + * + * Does not support streams longer than 32 bits + */ + static const uint32_t STREAM_SEEK_END=0xFFFFFFFF; + + enum OpenMode + { + OPEN_FILE_NOT_FOUND, + OPEN_READ_ONLY, // open file buffer stream for read only access + OPEN_WRITE_ONLY, // open file buffer stream for write only access + OPEN_READ_WRITE_NEW, // open a new file for both read/write access + OPEN_READ_WRITE_EXISTING // open an existing file for both read/write access + }; + + virtual OpenMode getOpenMode(void) const = 0; + + bool isOpen(void) const + { + return getOpenMode()!=OPEN_FILE_NOT_FOUND; + } + + enum SeekType + { + SEEKABLE_NO = 0, + SEEKABLE_READ = 0x1, + SEEKABLE_WRITE = 0x2, + SEEKABLE_READWRITE = 0x3 + }; + + virtual SeekType isSeekable(void) const = 0; + + void setEndianMode(EndianMode e) + { + mEndianMode = e; + if ( (e==ENDIAN_BIG && !isBigEndian() ) || + (e==ENDIAN_LITTLE && isBigEndian() ) ) + { + mEndianSwap = true; + } + else + { + mEndianSwap = false; + } + } + + EndianMode getEndianMode(void) const + { + return mEndianMode; + } + + virtual uint32_t getFileLength(void) const = 0; + + /** + \brief Seeks the stream to a particular location for reading + * + * If the location passed exceeds the length of the stream, then it will seek to the end. + * Returns the location it ended up at (useful if you seek to the end) to get the file position + */ + virtual uint32_t seekRead(uint32_t loc) = 0; + + /** + \brief Seeks the stream to a particular location for writing + * + * If the location passed exceeds the length of the stream, then it will seek to the end. + * Returns the location it ended up at (useful if you seek to the end) to get the file position + */ + virtual uint32_t seekWrite(uint32_t loc) = 0; + + /** + \brief Reads from the stream into a buffer. + + \param[out] mem The buffer to read the stream into. + \param[in] len The number of bytes to stream into the buffer + + \return Returns the actual number of bytes read. If not equal to the length requested, then reached end of stream. + */ + virtual uint32_t read(void *mem,uint32_t len) = 0; + + + /** + \brief Reads from the stream into a buffer but does not advance the read location. + + \param[out] mem The buffer to read the stream into. + \param[in] len The number of bytes to stream into the buffer + + \return Returns the actual number of bytes read. If not equal to the length requested, then reached end of stream. + */ + virtual uint32_t peek(void *mem,uint32_t len) = 0; + + /** + \brief Writes a buffer of memory to the stream + + \param[in] mem The address of a buffer of memory to send to the stream. + \param[in] len The number of bytes to send to the stream. + + \return Returns the actual number of bytes sent to the stream. If not equal to the length specific, then the stream is full or unable to write for some reason. + */ + virtual uint32_t write(const void *mem,uint32_t len) = 0; + + /** + \brief Reports the current stream location read aqccess. + + \return Returns the current stream read location. + */ + virtual uint32_t tellRead(void) const = 0; + + /** + \brief Reports the current stream location for write access. + + \return Returns the current stream write location. + */ + virtual uint32_t tellWrite(void) const = 0; + + /** + \brief Causes any temporarily cached data to be flushed to the stream. + */ + virtual void flush(void) = 0; + + /** + \brief Close the stream. + */ + virtual void close(void) {} + + void release(void) + { + delete this; + } + + static PX_INLINE bool isBigEndian() + { + int32_t i = 1; + return *(reinterpret_cast(&i))==0; + } + + PX_INLINE void swap2Bytes(void* _data) const + { + char *data = static_cast(_data); + char one_byte; + one_byte = data[0]; data[0] = data[1]; data[1] = one_byte; + } + + PX_INLINE void swap4Bytes(void* _data) const + { + char *data = static_cast(_data); + char one_byte; + one_byte = data[0]; data[0] = data[3]; data[3] = one_byte; + one_byte = data[1]; data[1] = data[2]; data[2] = one_byte; + } + + PX_INLINE void swap8Bytes(void *_data) const + { + char *data = static_cast(_data); + char one_byte; + one_byte = data[0]; data[0] = data[7]; data[7] = one_byte; + one_byte = data[1]; data[1] = data[6]; data[6] = one_byte; + one_byte = data[2]; data[2] = data[5]; data[5] = one_byte; + one_byte = data[3]; data[3] = data[4]; data[4] = one_byte; + } + + + PX_INLINE void storeDword(uint32_t v) + { + if ( mEndianSwap ) + swap4Bytes(&v); + + write(&v,sizeof(v)); + } + + PX_INLINE void storeFloat(float v) + { + if ( mEndianSwap ) + swap4Bytes(&v); + write(&v,sizeof(v)); + } + + PX_INLINE void storeDouble(double v) + { + if ( mEndianSwap ) + swap8Bytes(&v); + write(&v,sizeof(v)); + } + + PX_INLINE void storeByte(uint8_t b) + { + write(&b,sizeof(b)); + } + + PX_INLINE void storeWord(uint16_t w) + { + if ( mEndianSwap ) + swap2Bytes(&w); + write(&w,sizeof(w)); + } + + uint8_t readByte(void) + { + uint8_t v=0; + read(&v,sizeof(v)); + return v; + } + + uint16_t readWord(void) + { + uint16_t v=0; + read(&v,sizeof(v)); + if ( mEndianSwap ) + swap2Bytes(&v); + return v; + } + + uint32_t readDword(void) + { + uint32_t v=0; + read(&v,sizeof(v)); + if ( mEndianSwap ) + swap4Bytes(&v); + return v; + } + + float readFloat(void) + { + float v=0; + read(&v,sizeof(v)); + if ( mEndianSwap ) + swap4Bytes(&v); + return v; + } + + double readDouble(void) + { + double v=0; + read(&v,sizeof(v)); + if ( mEndianSwap ) + swap8Bytes(&v); + return v; + } + +private: + bool mEndianSwap; // whether or not the endian should be swapped on the current platform + EndianMode mEndianMode; // the current endian mode behavior for the stream +}; + +PX_POP_PACK + +#if !PX_DOXYGEN +} // end of namespace + +using namespace general_PxIOStream2; + +namespace general_PxIOStream = general_PxIOStream2; + +} // end of namespace +#endif + +/** @} */ + +#endif // PSFILEBUFFER_PXFILEBUF_H diff --git a/src/PhysX/physx/include/geometry/PxBVHStructure.h b/src/PhysX/physx/include/geometry/PxBVHStructure.h new file mode 100644 index 000000000..86050b13e --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxBVHStructure.h @@ -0,0 +1,138 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_BVH_STRUCTURE +#define PX_PHYSICS_BVH_STRUCTURE +/** \addtogroup geomutils +@{ +*/ + +#include "common/PxBase.h" +#include "foundation/PxTransform.h" +#include "foundation/PxBounds3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Class representing the bounding volume hierarchy structure. + +PxBVHStructure can be provided to PxScene::addActor. In this case the scene query +pruning structure inside PhysX SDK will store/update one bound per actor. +The scene queries against such an actor will query actor bounds and then +make a local space query against the provided BVH structure, which is in +actor's local space. + +@see PxScene::addActor +*/ +class PxBVHStructure: public PxBase +{ +public: + + /** + \brief Raycast test against a BVH structure. + + \param[in] origin The origin of the ray. + \param[in] unitDir Normalized direction of the ray. + \param[in] maxDist Maximum ray length, has to be in the [0, inf) range + \param[in] maxHits Max number of returned hits = size of 'rayHits' buffer + \param[out] rayHits Raycast hits information, bounds indices + \return Number of hits + */ + virtual PxU32 raycast(const PxVec3& origin, + const PxVec3& unitDir, + PxReal maxDist, + PxU32 maxHits, + PxU32* PX_RESTRICT rayHits) const = 0; + + /** + \brief Sweep test against a BVH structure. + + \param[in] aabb The axis aligned bounding box to sweep + \param[in] unitDir Normalized direction of the sweep. + \param[in] maxDist Maximum sweep length, has to be in the [0, inf) range + \param[in] maxHits Max number of returned hits = size of 'sweepHits' buffer + \param[out] sweepHits Sweep hits information, bounds indices + \return Number of hits + */ + virtual PxU32 sweep(const PxBounds3& aabb, + const PxVec3& unitDir, + PxReal maxDist, + PxU32 maxHits, + PxU32* PX_RESTRICT sweepHits) const = 0; + + /** + \brief AABB overlap test against a BVH structure. + + \param[in] aabb The axis aligned bounding box + \param[in] maxHits Max number of returned hits = size of 'overlapHits' buffer + \param[out] overlapHits Overlap hits information, bounds indices + \return Number of hits + */ + virtual PxU32 overlap(const PxBounds3& aabb, + PxU32 maxHits, + PxU32* PX_RESTRICT overlapHits) const = 0; + + /** + \brief Retrieve the bounds in the BVH. + + @see PxBounds3 + */ + virtual const PxBounds3* getBounds() const = 0; + + /** + \brief Returns the number of bounds in the BVH. + + You can use #getBounds() to retrieve the bounds. + + \return Number of bounds in the BVH. + + */ + virtual PxU32 getNbBounds() const = 0; + + virtual const char* getConcreteTypeName() const { return "PxBVHStructure"; } +protected: + PX_INLINE PxBVHStructure(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxBVHStructure(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxBVHStructure() {} + + virtual bool isKindOf(const char* name) const { return !::strcmp("PxBVHStructure", name) || PxBase::isKindOf(name); } + +}; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxBoxGeometry.h b/src/PhysX/physx/include/geometry/PxBoxGeometry.h new file mode 100644 index 000000000..a0fa7d0c6 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxBoxGeometry.h @@ -0,0 +1,109 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_BOX_GEOMETRY +#define PX_PHYSICS_NX_BOX_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxGeometry.h" +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Class representing the geometry of a box. + +The geometry of a box can be fully specified by its half extents. This is the half of its width, height, and depth. +\note The scaling of the box is expected to be baked into these values, there is no additional scaling parameter. +*/ +class PxBoxGeometry : public PxGeometry +{ +public: + /** + \brief Default constructor, initializes to a box with zero dimensions. + */ + PX_INLINE PxBoxGeometry() : PxGeometry(PxGeometryType::eBOX), halfExtents(0,0,0) {} + + /** + \brief Constructor to initialize half extents from scalar parameters. + \param hx Initial half extents' x component. + \param hy Initial half extents' y component. + \param hz Initial half extents' z component. + */ + PX_INLINE PxBoxGeometry(PxReal hx, PxReal hy, PxReal hz) : PxGeometry(PxGeometryType::eBOX), halfExtents(hx, hy, hz) {} + + /** + \brief Constructor to initialize half extents from vector parameter. + \param halfExtents_ Initial half extents. + */ + PX_INLINE PxBoxGeometry(PxVec3 halfExtents_) : PxGeometry(PxGeometryType::eBOX), halfExtents(halfExtents_) {} + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid + + \note A valid box has a positive extent in each direction (halfExtents.x > 0, halfExtents.y > 0, halfExtents.z > 0). + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a box that has zero extent in any direction. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + /** + \brief Half of the width, height, and depth of the box. + */ + PxVec3 halfExtents; +}; + + +PX_INLINE bool PxBoxGeometry::isValid() const +{ + if (mType != PxGeometryType::eBOX) + return false; + if (!halfExtents.isFinite()) + return false; + if (halfExtents.x <= 0.0f || halfExtents.y <= 0.0f || halfExtents.z <= 0.0f) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxCapsuleGeometry.h b/src/PhysX/physx/include/geometry/PxCapsuleGeometry.h new file mode 100644 index 000000000..4600f4da4 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxCapsuleGeometry.h @@ -0,0 +1,121 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_CAPSULE_GEOMETRY +#define PX_PHYSICS_NX_CAPSULE_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Class representing the geometry of a capsule. + +Capsules are shaped as the union of a cylinder of length 2 * halfHeight and with the +given radius centered at the origin and extending along the x axis, and two hemispherical ends. +\note The scaling of the capsule is expected to be baked into these values, there is no additional scaling parameter. + +The function PxTransformFromSegment is a helper for generating an appropriate transform for the capsule from the capsule's interior line segment. + +@see PxTransformFromSegment +*/ +class PxCapsuleGeometry : public PxGeometry +{ +public: + /** + \brief Default constructor, initializes to a capsule with zero height and radius. + */ + PX_INLINE PxCapsuleGeometry() : PxGeometry(PxGeometryType::eCAPSULE), radius(0), halfHeight(0) {} + + /** + \brief Constructor, initializes to a capsule with passed radius and half height. + */ + PX_INLINE PxCapsuleGeometry(PxReal radius_, PxReal halfHeight_) : PxGeometry(PxGeometryType::eCAPSULE), radius(radius_), halfHeight(halfHeight_) {} + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid. + + \note A valid capsule has radius > 0, halfHeight > 0. + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a capsule that has zero radius or height. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + /** + \brief The radius of the capsule. + */ + PxReal radius; + + /** + \brief half of the capsule's height, measured between the centers of the hemispherical ends. + */ + PxReal halfHeight; +}; + + +PX_INLINE bool PxCapsuleGeometry::isValid() const +{ + if (mType != PxGeometryType::eCAPSULE) + return false; + if (!PxIsFinite(radius) || !PxIsFinite(halfHeight)) + return false; + if (radius <= 0.0f || halfHeight <= 0.0f) + return false; + + return true; +} + + +/** \brief creates a transform from the endpoints of a segment, suitable for an actor transform for a PxCapsuleGeometry + +\param[in] p0 one end of major axis of the capsule +\param[in] p1 the other end of the axis of the capsule +\param[out] halfHeight the halfHeight of the capsule. This parameter is optional. +\return A PxTransform which will transform the vector (1,0,0) to the capsule axis shrunk by the halfHeight +*/ + +PX_FOUNDATION_API PxTransform PxTransformFromSegment(const PxVec3& p0, const PxVec3& p1, PxReal* halfHeight = NULL); + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxConvexMesh.h b/src/PhysX/physx/include/geometry/PxConvexMesh.h new file mode 100644 index 000000000..b331a4223 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxConvexMesh.h @@ -0,0 +1,197 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_NX_CONVEXMESH +#define PX_PHYSICS_GEOMUTILS_NX_CONVEXMESH +/** \addtogroup geomutils + @{ +*/ + +#include "foundation/Px.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Polygon data + +Plane format: (mPlane[0],mPlane[1],mPlane[2]).dot(x) + mPlane[3] = 0 +With the normal outward-facing from the hull. +*/ +struct PxHullPolygon +{ + PxReal mPlane[4]; //!< Plane equation for this polygon + PxU16 mNbVerts; //!< Number of vertices/edges in the polygon + PxU16 mIndexBase; //!< Offset in index buffer +}; + +/** +\brief A convex mesh. + +Internally represented as a list of convex polygons. The number +of polygons is limited to 256. + +To avoid duplicating data when you have several instances of a particular +mesh positioned differently, you do not use this class to represent a +convex object directly. Instead, you create an instance of this mesh via +the PxConvexMeshGeometry and PxShape classes. + +

            Creation

            + +To create an instance of this class call PxPhysics::createConvexMesh(), +and PxConvexMesh::release() to delete it. This is only possible +once you have released all of its #PxShape instances. + +

            Visualizations:

            +\li #PxVisualizationParameter::eCOLLISION_AABBS +\li #PxVisualizationParameter::eCOLLISION_SHAPES +\li #PxVisualizationParameter::eCOLLISION_AXES +\li #PxVisualizationParameter::eCOLLISION_FNORMALS +\li #PxVisualizationParameter::eCOLLISION_EDGES + +@see PxConvexMeshDesc PxPhysics.createConvexMesh() +*/ +class PxConvexMesh : public PxBase +{ +public: + + /** + \brief Returns the number of vertices. + \return Number of vertices. + @see getVertices() + */ + PX_PHYSX_COMMON_API virtual PxU32 getNbVertices() const = 0; + + /** + \brief Returns the vertices. + \return Array of vertices. + @see getNbVertices() + */ + PX_PHYSX_COMMON_API virtual const PxVec3* getVertices() const = 0; + + /** + \brief Returns the index buffer. + \return Index buffer. + @see getNbPolygons() getPolygonData() + */ + PX_PHYSX_COMMON_API virtual const PxU8* getIndexBuffer() const = 0; + + /** + \brief Returns the number of polygons. + \return Number of polygons. + @see getIndexBuffer() getPolygonData() + */ + PX_PHYSX_COMMON_API virtual PxU32 getNbPolygons() const = 0; + + /** + \brief Returns the polygon data. + \param[in] index Polygon index in [0 ; getNbPolygons()[. + \param[out] data Polygon data. + \return True if success. + @see getIndexBuffer() getNbPolygons() + */ + PX_PHYSX_COMMON_API virtual bool getPolygonData(PxU32 index, PxHullPolygon& data) const = 0; + + /** + \brief Decrements the reference count of a convex mesh and releases it if the new reference count is zero. + + @see PxPhysics.createConvexMesh() PxConvexMeshGeometry PxShape + */ + PX_PHYSX_COMMON_API virtual void release() = 0; + + /** + \brief Returns the reference count of a convex mesh. + + At creation, the reference count of the convex mesh is 1. Every shape referencing this convex mesh increments the + count by 1. When the reference count reaches 0, and only then, the convex mesh gets destroyed automatically. + + \return the current reference count. + */ + PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const = 0; + + /** + \brief Acquires a counted reference to a convex mesh. + + This method increases the reference count of the convex mesh by 1. Decrement the reference count by calling release() + */ + PX_PHYSX_COMMON_API virtual void acquireReference() = 0; + + /** + \brief Returns the mass properties of the mesh assuming unit density. + + The following relationship holds between mass and volume: + + mass = volume * density + + The mass of a unit density mesh is equal to its volume, so this function returns the volume of the mesh. + + Similarly, to obtain the localInertia of an identically shaped object with a uniform density of d, simply multiply the + localInertia of the unit density mesh by d. + + \param[out] mass The mass of the mesh assuming unit density. + \param[out] localInertia The inertia tensor in mesh local space assuming unit density. + \param[out] localCenterOfMass Position of center of mass (or centroid) in mesh local space. + */ + PX_PHYSX_COMMON_API virtual void getMassInformation(PxReal& mass, PxMat33& localInertia, PxVec3& localCenterOfMass) const = 0; + + /** + \brief Returns the local-space (vertex space) AABB from the convex mesh. + + \return local-space bounds + */ + PX_PHYSX_COMMON_API virtual PxBounds3 getLocalBounds() const = 0; + + PX_PHYSX_COMMON_API virtual const char* getConcreteTypeName() const { return "PxConvexMesh"; } + + /** + \brief This method decides whether a convex mesh is gpu compatible. If the total number of vertices are more than 64 or any number of vertices in a polygon is more than 32, or + convex hull data was not cooked with GPU data enabled during cooking or was loaded from a serialized collection, the convex hull is incompatible with GPU collision detection. Otherwise + it is compatible. + + \return True if the convex hull is gpu compatible + */ + PX_PHYSX_COMMON_API virtual bool isGpuCompatible() const = 0; + +protected: + PX_INLINE PxConvexMesh(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxConvexMesh(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + PX_PHYSX_COMMON_API virtual ~PxConvexMesh() {} + PX_PHYSX_COMMON_API virtual bool isKindOf(const char* name) const { return !::strcmp("PxConvexMesh", name) || PxBase::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h b/src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h new file mode 100644 index 000000000..dad042c8c --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h @@ -0,0 +1,151 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_CONVEXMESH_GEOMETRY +#define PX_PHYSICS_NX_CONVEXMESH_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxGeometry.h" +#include "geometry/PxMeshScale.h" +#include "common/PxCoreUtilityTypes.h" +#include "geometry/PxConvexMesh.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxConvexMesh; + +/** +\brief Flags controlling the simulated behavior of the convex mesh geometry. + +Used in ::PxConvexMeshGeometryFlags. +*/ +struct PxConvexMeshGeometryFlag +{ + enum Enum + { + eTIGHT_BOUNDS = (1<<0) //!< Use tighter (but more expensive to compute) bounds around the convex geometry. + }; +}; + +/** +\brief collection of set bits defined in PxConvexMeshGeometryFlag. + +@see PxConvexMeshGeometryFlag +*/ +typedef PxFlags PxConvexMeshGeometryFlags; +PX_FLAGS_OPERATORS(PxConvexMeshGeometryFlag::Enum,PxU8) + +/** +\brief Convex mesh geometry class. + +This class unifies a convex mesh object with a scaling transform, and +lets the combined object be used anywhere a PxGeometry is needed. + +The scaling is a transform along arbitrary axes contained in the scale object. +The vertices of the mesh in geometry (or shape) space is the +PxMeshScale::toMat33() transform, multiplied by the vertex space vertices +in the PxConvexMesh object. +*/ +class PxConvexMeshGeometry : public PxGeometry +{ +public: + /** + \brief Default constructor. + + Creates an empty object with a NULL mesh and identity scale. + */ + PX_INLINE PxConvexMeshGeometry() : + PxGeometry (PxGeometryType::eCONVEXMESH), + scale (PxMeshScale(1.0f)), + convexMesh (NULL), + meshFlags (PxConvexMeshGeometryFlag::eTIGHT_BOUNDS) + {} + + /** + \brief Constructor. + \param[in] mesh Mesh pointer. May be NULL, though this will not make the object valid for shape construction. + \param[in] scaling Scale factor. + \param[in] flags Mesh flags. + \ + */ + PX_INLINE PxConvexMeshGeometry( PxConvexMesh* mesh, + const PxMeshScale& scaling = PxMeshScale(), + PxConvexMeshGeometryFlags flags = PxConvexMeshGeometryFlag::eTIGHT_BOUNDS) : + PxGeometry (PxGeometryType::eCONVEXMESH), + scale (scaling), + convexMesh (mesh), + meshFlags (flags) + { + } + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid for shape creation. + + \note A valid convex mesh has a positive scale value in each direction (scale.x > 0, scale.y > 0, scale.z > 0). + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a convex that has zero extent in any direction. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + PxMeshScale scale; //!< The scaling transformation (from vertex space to shape space). + PxConvexMesh* convexMesh; //!< A reference to the convex mesh object. + PxConvexMeshGeometryFlags meshFlags; //!< Mesh flags. + PxPadding<3> paddingFromFlags; //!< padding for mesh flags +}; + + +PX_INLINE bool PxConvexMeshGeometry::isValid() const +{ + if(mType != PxGeometryType::eCONVEXMESH) + return false; + if(!scale.scale.isFinite() || !scale.rotation.isUnit()) + return false; + if(!scale.isValidForConvexMesh()) + return false; + if(!convexMesh) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxGeometry.h b/src/PhysX/physx/include/geometry/PxGeometry.h new file mode 100644 index 000000000..5c8133918 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxGeometry.h @@ -0,0 +1,94 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_GEOMETRY +#define PX_PHYSICS_NX_GEOMETRY +/** \addtogroup geomutils +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxFlags.h" +#include "foundation/PxMath.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief A geometry type. + +Used to distinguish the type of a ::PxGeometry object. +*/ +struct PxGeometryType +{ + enum Enum + { + eSPHERE, + ePLANE, + eCAPSULE, + eBOX, + eCONVEXMESH, + eTRIANGLEMESH, + eHEIGHTFIELD, + eGEOMETRY_COUNT, //!< internal use only! + eINVALID = -1 //!< internal use only! + }; +}; + +/** +\brief A geometry object. + +A geometry object defines the characteristics of a spatial object, but without any information +about its placement in the world. + +\note This is an abstract class. You cannot create instances directly. Create an instance of one of the derived classes instead. +*/ +class PxGeometry +{ +public: + /** + \brief Returns the type of the geometry. + \return The type of the object. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxGeometryType::Enum getType() const { return mType; } + +protected: + PX_CUDA_CALLABLE PX_FORCE_INLINE PxGeometry(PxGeometryType::Enum type) : mType(type) {} + PxGeometryType::Enum mType; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxGeometryHelpers.h b/src/PhysX/physx/include/geometry/PxGeometryHelpers.h new file mode 100644 index 000000000..e4637df70 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxGeometryHelpers.h @@ -0,0 +1,214 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMETRYHELPERS +#define PX_PHYSICS_GEOMETRYHELPERS +/** \addtogroup geomutils +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "PxGeometry.h" +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "foundation/PxPlane.h" +#include "foundation/PxTransform.h" +#include "foundation/PxUnionCast.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Geometry holder class + +This class contains enough space to hold a value of any PxGeometry subtype. + +Its principal use is as a convenience class to allow geometries to be returned polymorphically +from functions. See PxShape::getGeometry(); +*/ + +PX_ALIGN_PREFIX(4) +class PxGeometryHolder +{ +public: + PX_FORCE_INLINE PxGeometryType::Enum getType() const + { + return any().getType(); + } + + PX_FORCE_INLINE PxGeometry& any() + { + return *PxUnionCast(&bytes.geometry); + } + + PX_FORCE_INLINE const PxGeometry& any() const + { + return *PxUnionCast(&bytes.geometry); + } + + PX_FORCE_INLINE PxSphereGeometry& sphere() + { + return get(); + } + + PX_FORCE_INLINE const PxSphereGeometry& sphere() const + { + return get(); + } + + PX_FORCE_INLINE PxPlaneGeometry& plane() + { + return get(); + } + + PX_FORCE_INLINE const PxPlaneGeometry& plane() const + { + return get(); + } + + PX_FORCE_INLINE PxCapsuleGeometry& capsule() + { + return get(); + } + + PX_FORCE_INLINE const PxCapsuleGeometry& capsule() const + { + return get(); + } + + PX_FORCE_INLINE PxBoxGeometry& box() + { + return get(); + } + + PX_FORCE_INLINE const PxBoxGeometry& box() const + { + return get(); + } + + PX_FORCE_INLINE PxConvexMeshGeometry& convexMesh() + { + return get(); + } + + PX_FORCE_INLINE const PxConvexMeshGeometry& convexMesh() const + { + return get(); + } + + PX_FORCE_INLINE PxTriangleMeshGeometry& triangleMesh() + { + return get(); + } + + PX_FORCE_INLINE const PxTriangleMeshGeometry& triangleMesh() const + { + return get(); + } + + PX_FORCE_INLINE PxHeightFieldGeometry& heightField() + { + return get(); + } + + PX_FORCE_INLINE const PxHeightFieldGeometry& heightField() const + { + return get(); + } + + PX_FORCE_INLINE void storeAny(const PxGeometry& geometry) + { + PX_ASSERT_WITH_MESSAGE( (geometry.getType() >= PxGeometryType::eSPHERE) && + (geometry.getType() < PxGeometryType::eGEOMETRY_COUNT), + "Unexpected GeometryType in PxGeometryHolder::storeAny"); + + switch(geometry.getType()) + { + case PxGeometryType::eSPHERE: put(geometry); break; + case PxGeometryType::ePLANE: put(geometry); break; + case PxGeometryType::eCAPSULE: put(geometry); break; + case PxGeometryType::eBOX: put(geometry); break; + case PxGeometryType::eCONVEXMESH: put(geometry); break; + case PxGeometryType::eTRIANGLEMESH: put(geometry); break; + case PxGeometryType::eHEIGHTFIELD: put(geometry); break; + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: break; + } + } + + PX_FORCE_INLINE PxGeometryHolder() {} + PX_FORCE_INLINE PxGeometryHolder(const PxGeometry& geometry){ storeAny(geometry); } + + private: + template void put(const PxGeometry& geometry) + { + static_cast(any()) = static_cast(geometry); + } + + template T& get() + { + PX_ASSERT(getType() == type); + return static_cast(any()); + } + + template T& get() const + { + PX_ASSERT(getType() == type); + return static_cast(any()); + } + + union { + PxU8 geometry[sizeof(PxGeometry)]; + PxU8 box[sizeof(PxBoxGeometry)]; + PxU8 sphere[sizeof(PxSphereGeometry)]; + PxU8 capsule[sizeof(PxCapsuleGeometry)]; + PxU8 plane[sizeof(PxPlaneGeometry)]; + PxU8 convex[sizeof(PxConvexMeshGeometry)]; + PxU8 mesh[sizeof(PxTriangleMeshGeometry)]; + PxU8 heightfield[sizeof(PxHeightFieldGeometry)]; + } bytes; +} +PX_ALIGN_SUFFIX(4); + + + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxGeometryQuery.h b/src/PhysX/physx/include/geometry/PxGeometryQuery.h new file mode 100644 index 000000000..95274b34f --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxGeometryQuery.h @@ -0,0 +1,225 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_PX_GEOMETRY_QUERY +#define PX_PHYSICS_GEOMUTILS_PX_GEOMETRY_QUERY + +/** +\brief Maximum sweep distance for scene sweeps. The distance parameter for sweep functions will be clamped to this value. +The reason for this is GJK support cannot be evaluated near infinity. A viable alternative can be a sweep followed by an infinite raycast. + +@see PxScene +*/ +#define PX_MAX_SWEEP_DISTANCE 1e8f + +/** \addtogroup geomutils + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "PxQueryReport.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxGeometry; +struct PxSweepHit; +struct PxRaycastHit; + +class PxTriangle; + +/** +\brief Collection of geometry object queries (sweeps, raycasts, overlaps, ...). +*/ +class PxGeometryQuery +{ +public: + + /** + \brief Sweep a specified geometry object in space and test for collision with a given object. + + The following combinations are supported. + + \li PxSphereGeometry vs. {PxSphereGeometry, PxPlaneGeometry, PxCapsuleGeometry, PxBoxGeometry, PxConvexMeshGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry} + \li PxCapsuleGeometry vs. {PxSphereGeometry, PxPlaneGeometry, PxCapsuleGeometry, PxBoxGeometry, PxConvexMeshGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry} + \li PxBoxGeometry vs. {PxSphereGeometry, PxPlaneGeometry, PxCapsuleGeometry, PxBoxGeometry, PxConvexMeshGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry} + \li PxConvexMeshGeometry vs. {PxSphereGeometry, PxPlaneGeometry, PxCapsuleGeometry, PxBoxGeometry, PxConvexMeshGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry} + + \param[in] unitDir Normalized direction along which object geom0 should be swept + \param[in] maxDist Maximum sweep distance, has to be in the [0, inf) range + \param[in] geom0 The geometry object to sweep. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry, #PxBoxGeometry and #PxConvexMeshGeometry + \param[in] pose0 Pose of the geometry object to sweep + \param[in] geom1 The geometry object to test the sweep against + \param[in] pose1 Pose of the geometry object to sweep against + \param[out] sweepHit The sweep hit information. Only valid if this method returns true. + \param[in] hitFlags Specify which properties per hit should be computed and written to result hit array. Combination of #PxHitFlag flags + \param[in] inflation Surface of the swept shape is additively extruded in the normal direction, rounding corners and edges. + + \return True if the swept geometry object geom0 hits the object geom1 + + @see PxSweepHit PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static bool sweep(const PxVec3& unitDir, + const PxReal maxDist, + const PxGeometry& geom0, + const PxTransform& pose0, + const PxGeometry& geom1, + const PxTransform& pose1, + PxSweepHit& sweepHit, + PxHitFlags hitFlags = PxHitFlag::eDEFAULT, + const PxReal inflation = 0.f); + + + /** + \brief Overlap test for two geometry objects. + + All combinations are supported except: + \li PxPlaneGeometry vs. {PxPlaneGeometry, PxTriangleMeshGeometry, PxHeightFieldGeometry} + \li PxTriangleMeshGeometry vs. {PxTriangleMeshGeometry, PxHeightFieldGeometry} + \li PxHeightFieldGeometry vs. {PxHeightFieldGeometry} + + \param[in] geom0 The first geometry object + \param[in] pose0 Pose of the first geometry object + \param[in] geom1 The second geometry object + \param[in] pose1 Pose of the second geometry object + \return True if the two geometry objects overlap + + @see PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static bool overlap(const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1); + + + /** + \brief Raycast test against a geometry object. + + \param[in] origin The origin of the ray to test the geometry object against + \param[in] unitDir Normalized direction of the ray to test the geometry object against + \param[in] geom The geometry object to test the ray against + \param[in] pose Pose of the geometry object + \param[in] maxDist Maximum ray length, has to be in the [0, inf) range + \param[in] hitFlags Specification of the kind of information to retrieve on hit. Combination of #PxHitFlag flags + \param[in] maxHits max number of returned hits = size of 'rayHits' buffer + \param[out] rayHits Raycast hits information + \return Number of hits between the ray and the geometry object + + @see PxRaycastHit PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static PxU32 raycast(const PxVec3& origin, + const PxVec3& unitDir, + const PxGeometry& geom, + const PxTransform& pose, + PxReal maxDist, + PxHitFlags hitFlags, + PxU32 maxHits, + PxRaycastHit* PX_RESTRICT rayHits); + + /** + \brief Compute minimum translational distance (MTD) between two geometry objects. + + All combinations of geom objects are supported except: + - plane/plane + - plane/mesh + - plane/heightfield + - mesh/mesh + - mesh/heightfield + - heightfield/heightfield + + The function returns a unit vector ('direction') and a penetration depth ('depth'). + + The depenetration vector D = direction * depth should be applied to the first object, to + get out of the second object. + + Returned depth should always be positive or null. + + If objects do not overlap, the function can not compute the MTD and returns false. + + \param[out] direction Computed MTD unit direction + \param[out] depth Penetration depth. Always positive or null. + \param[in] geom0 The first geometry object + \param[in] pose0 Pose of the first geometry object + \param[in] geom1 The second geometry object + \param[in] pose1 Pose of the second geometry object + \return True if the MTD has successfully been computed, i.e. if objects do overlap. + + @see PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static bool computePenetration(PxVec3& direction, PxF32& depth, + const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1); + + /** + \brief Computes distance between a point and a geometry object. + + Currently supported geometry objects: box, sphere, capsule, convex. + + \param[in] point The point P + \param[in] geom The geometry object + \param[in] pose Pose of the geometry object + \param[out] closestPoint Optionally returned closest point to P on the geom object. Only valid when returned distance is strictly positive. + \return Square distance between the point and the geom object, or 0.0 if the point is inside the object, or -1.0 if the geometry type is not supported. + + @see PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static PxReal pointDistance(const PxVec3& point, const PxGeometry& geom, const PxTransform& pose, PxVec3* closestPoint=NULL); + + + /** + \brief get the bounds for a geometry object + + \param[in] geom The geometry object + \param[in] pose Pose of the geometry object + \param[in] inflation Scale factor for computed world bounds. Box extents are multiplied by this value. + \return The bounds of the object + + @see PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static PxBounds3 getWorldBounds(const PxGeometry& geom, const PxTransform& pose, float inflation=1.01f); + + /** + \brief Checks if provided geometry is valid. + + \param[in] geom The geometry object. + \return True if geometry is valid. + + @see PxGeometry PxSphereGeometry, PxCapsuleGeometry, PxBoxGeometry, PxConvexGeometry + */ + PX_PHYSX_COMMON_API static bool isValid(const PxGeometry& geom); +}; + + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxHeightField.h b/src/PhysX/physx/include/geometry/PxHeightField.h new file mode 100644 index 000000000..5cc07ae71 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxHeightField.h @@ -0,0 +1,262 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_NX_HEIGHTFIELD +#define PX_PHYSICS_GEOMUTILS_NX_HEIGHTFIELD +/** \addtogroup geomutils + @{ +*/ + +#include "geometry/PxHeightFieldFlag.h" +#include "geometry/PxHeightFieldSample.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxHeightFieldDesc; + +/** +\brief A height field class. + +Height fields work in a similar way as triangle meshes specified to act as +height fields, with some important differences: + +Triangle meshes can be made of nonuniform geometry, while height fields are +regular, rectangular grids. This means that with PxHeightField, you sacrifice +flexibility in return for improved performance and decreased memory consumption. + +In local space rows extend in X direction, columns in Z direction and height in Y direction. + +Like Convexes and TriangleMeshes, HeightFields are referenced by shape instances +(see #PxHeightFieldGeometry, #PxShape). + +To avoid duplicating data when you have several instances of a particular +height field differently, you do not use this class to represent a +height field object directly. Instead, you create an instance of this height field +via the PxHeightFieldGeometry and PxShape classes. + +

            Creation

            + +To create an instance of this class call PxPhysics::createHeightField() or +PxCooking::createHeightField(const PxHeightFieldDesc&, PxPhysicsInsertionCallback&). +To delete it call release(). This is only possible +once you have released all of its PxHeightFiedShape instances. + +

            Visualizations:

            +\li #PxVisualizationParameter::eCOLLISION_AABBS +\li #PxVisualizationParameter::eCOLLISION_SHAPES +\li #PxVisualizationParameter::eCOLLISION_AXES +\li #PxVisualizationParameter::eCOLLISION_FNORMALS +\li #PxVisualizationParameter::eCOLLISION_EDGES + +@see PxHeightFieldDesc PxHeightFieldGeometry PxShape PxPhysics.createHeightField() PxCooking.createHeightField() +*/ + +class PxHeightField : public PxBase +{ + public: + /** + \brief Decrements the reference count of a height field and releases it if the new reference count is zero. + + @see PxPhysics.createHeightField() PxHeightFieldDesc PxHeightFieldGeometry PxShape + */ + PX_PHYSX_COMMON_API virtual void release() = 0; + + /** + \brief Writes out the sample data array. + + The user provides destBufferSize bytes storage at destBuffer. + The data is formatted and arranged as PxHeightFieldDesc.samples. + + \param[out] destBuffer The destination buffer for the sample data. + \param[in] destBufferSize The size of the destination buffer. + \return The number of bytes written. + + @see PxHeightFieldDesc.samples + */ + PX_PHYSX_COMMON_API virtual PxU32 saveCells(void* destBuffer, PxU32 destBufferSize) const = 0; + + /** + \brief Replaces a rectangular subfield in the sample data array. + + The user provides the description of a rectangular subfield in subfieldDesc. + The data is formatted and arranged as PxHeightFieldDesc.samples. + + \param[in] startCol First cell in the destination heightfield to be modified. Can be negative. + \param[in] startRow First row in the destination heightfield to be modified. Can be negative. + \param[in] subfieldDesc Description of the source subfield to read the samples from. + \param[in] shrinkBounds If left as false, the bounds will never shrink but only grow. If set to true the bounds will be recomputed from all HF samples at O(nbColums*nbRows) perf cost. + \return True on success, false on failure. Failure can occur due to format mismatch. + + \note Modified samples are constrained to the same height quantization range as the original heightfield. + Source samples that are out of range of target heightfield will be clipped with no error. + PhysX does not keep a mapping from the heightfield to heightfield shapes that reference it. + Call PxShape::setGeometry on each shape which references the height field, to ensure that internal data structures are updated to reflect the new geometry. + Please note that PxShape::setGeometry does not guarantee correct/continuous behavior when objects are resting on top of old or new geometry. + + @see PxHeightFieldDesc.samples PxShape.setGeometry + */ + PX_PHYSX_COMMON_API virtual bool modifySamples(PxI32 startCol, PxI32 startRow, const PxHeightFieldDesc& subfieldDesc, bool shrinkBounds = false) = 0; + + /** + \brief Retrieves the number of sample rows in the samples array. + + \return The number of sample rows in the samples array. + + @see PxHeightFieldDesc.nbRows + */ + PX_PHYSX_COMMON_API virtual PxU32 getNbRows() const = 0; + + /** + \brief Retrieves the number of sample columns in the samples array. + + \return The number of sample columns in the samples array. + + @see PxHeightFieldDesc.nbColumns + */ + PX_PHYSX_COMMON_API virtual PxU32 getNbColumns() const = 0; + + /** + \brief Retrieves the format of the sample data. + + \return The format of the sample data. + + @see PxHeightFieldDesc.format PxHeightFieldFormat + */ + PX_PHYSX_COMMON_API virtual PxHeightFieldFormat::Enum getFormat() const = 0; + + /** + \brief Retrieves the offset in bytes between consecutive samples in the array. + + \return The offset in bytes between consecutive samples in the array. + + @see PxHeightFieldDesc.sampleStride + */ + PX_PHYSX_COMMON_API virtual PxU32 getSampleStride() const = 0; + + /** + \brief Retrieves the convex edge threshold. + + \return The convex edge threshold. + + @see PxHeightFieldDesc.convexEdgeThreshold + */ + PX_PHYSX_COMMON_API virtual PxReal getConvexEdgeThreshold() const = 0; + + /** + \brief Retrieves the flags bits, combined from values of the enum ::PxHeightFieldFlag. + + \return The flags bits, combined from values of the enum ::PxHeightFieldFlag. + + @see PxHeightFieldDesc.flags PxHeightFieldFlag + */ + PX_PHYSX_COMMON_API virtual PxHeightFieldFlags getFlags() const = 0; + + /** + \brief Retrieves the height at the given coordinates in grid space. + + \return The height at the given coordinates or 0 if the coordinates are out of range. + */ + PX_PHYSX_COMMON_API virtual PxReal getHeight(PxReal x, PxReal z) const = 0; + + /** + \brief Returns the reference count for shared heightfields. + + At creation, the reference count of the heightfield is 1. Every shape referencing this heightfield increments the + count by 1. When the reference count reaches 0, and only then, the heightfield gets destroyed automatically. + + \return the current reference count. + */ + PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const = 0; + + /** + \brief Acquires a counted reference to a heightfield. + + This method increases the reference count of the heightfield by 1. Decrement the reference count by calling release() + */ + PX_PHYSX_COMMON_API virtual void acquireReference() = 0; + + /** + \brief Returns material table index of given triangle + + \note This function takes a post cooking triangle index. + + \param[in] triangleIndex (internal) index of desired triangle + \return Material table index, or 0xffff if no per-triangle materials are used + */ + PX_PHYSX_COMMON_API virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const = 0; + + /** + \brief Returns a triangle face normal for a given triangle index + + \note This function takes a post cooking triangle index. + + \param[in] triangleIndex (internal) index of desired triangle + \return Triangle normal for a given triangle index + */ + PX_PHYSX_COMMON_API virtual PxVec3 getTriangleNormal(PxTriangleID triangleIndex) const = 0; + + /** + \brief Returns heightfield sample of given row and column + + \param[in] row Given heightfield row + \param[in] column Given heightfield column + \return Heightfield sample + */ + PX_PHYSX_COMMON_API virtual const PxHeightFieldSample& getSample(PxU32 row, PxU32 column) const = 0; + + /** + \brief Returns the number of times the heightfield data has been modified + + This method returns the number of times modifySamples has been called on this heightfield, so that code that has + retained state that depends on the heightfield can efficiently determine whether it has been modified. + + \return the number of times the heightfield sample data has been modified. + */ + PX_PHYSX_COMMON_API virtual PxU32 getTimestamp() const = 0; + + PX_PHYSX_COMMON_API virtual const char* getConcreteTypeName() const { return "PxHeightField"; } + +protected: + PX_INLINE PxHeightField(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxHeightField(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + PX_PHYSX_COMMON_API virtual ~PxHeightField() {} + PX_PHYSX_COMMON_API virtual bool isKindOf(const char* name) const { return !::strcmp("PxHeightField", name) || PxBase::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldDesc.h b/src/PhysX/physx/include/geometry/PxHeightFieldDesc.h new file mode 100644 index 000000000..eac748ccc --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxHeightFieldDesc.h @@ -0,0 +1,187 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_NXHEIGHTFIELDDESC +#define PX_COLLISION_NXHEIGHTFIELDDESC +/** \addtogroup geomutils +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "geometry/PxHeightFieldFlag.h" +#include "common/PxCoreUtilityTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Descriptor class for #PxHeightField. + +\note The heightfield data is *copied* when a PxHeightField object is created from this descriptor. After the call the +user may discard the height data. + +@see PxHeightField PxHeightFieldGeometry PxShape PxPhysics.createHeightField() PxCooking.createHeightField() +*/ +class PxHeightFieldDesc +{ +public: + + /** + \brief Number of sample rows in the height field samples array. + + \note Local space X-axis corresponds to rows. + + Range: >1
            + Default: 0 + */ + PxU32 nbRows; + + /** + \brief Number of sample columns in the height field samples array. + + \note Local space Z-axis corresponds to columns. + + Range: >1
            + Default: 0 + */ + PxU32 nbColumns; + + /** + \brief Format of the sample data. + + Currently the only supported format is PxHeightFieldFormat::eS16_TM: + + Default: PxHeightFieldFormat::eS16_TM + + @see PxHeightFormat PxHeightFieldDesc.samples + */ + PxHeightFieldFormat::Enum format; + + /** + \brief The samples array. + + It is copied to the SDK's storage at creation time. + + There are nbRows * nbColumn samples in the array, + which define nbRows * nbColumn vertices and cells, + of which (nbRows - 1) * (nbColumns - 1) cells are actually used. + + The array index of sample(row, column) = row * nbColumns + column. + The byte offset of sample(row, column) = sampleStride * (row * nbColumns + column). + The sample data follows at the offset and spans the number of bytes defined by the format. + Then there are zero or more unused bytes depending on sampleStride before the next sample. + + Default: NULL + + @see PxHeightFormat + */ + PxStridedData samples; + + /** + This threshold is used by the collision detection to determine if a height field edge is convex + and can generate contact points. + Usually the convexity of an edge is determined from the angle (or cosine of the angle) between + the normals of the faces sharing that edge. + The height field allows a more efficient approach by comparing height values of neighboring vertices. + This parameter offsets the comparison. Smaller changes than 0.5 will not alter the set of convex edges. + The rule of thumb is that larger values will result in fewer edge contacts. + + This parameter is ignored in contact generation with sphere and capsule primitives. + + Range: [0, PX_MAX_F32)
            + Default: 0 + */ + PxReal convexEdgeThreshold; + + /** + \brief Flags bits, combined from values of the enum ::PxHeightFieldFlag. + + Default: 0 + + @see PxHeightFieldFlag PxHeightFieldFlags + */ + PxHeightFieldFlags flags; + + /** + \brief Constructor sets to default. + */ + PX_INLINE PxHeightFieldDesc(); + + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE void setToDefault(); + + /** + \brief Returns true if the descriptor is valid. + \return True if the current settings are valid. + */ + PX_INLINE bool isValid() const; +}; + +PX_INLINE PxHeightFieldDesc::PxHeightFieldDesc() //constructor sets to default +{ + nbColumns = 0; + nbRows = 0; + format = PxHeightFieldFormat::eS16_TM; + convexEdgeThreshold = 0.0f; + flags = PxHeightFieldFlags(); +} + +PX_INLINE void PxHeightFieldDesc::setToDefault() +{ + *this = PxHeightFieldDesc(); +} + +PX_INLINE bool PxHeightFieldDesc::isValid() const +{ + if (nbColumns < 2) + return false; + if (nbRows < 2) + return false; + if(format != PxHeightFieldFormat::eS16_TM) + return false; + if (samples.stride < 4) + return false; + if (convexEdgeThreshold < 0) + return false; + if ((flags & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) != flags) + return false; + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldFlag.h b/src/PhysX/physx/include/geometry/PxHeightFieldFlag.h new file mode 100644 index 000000000..0f0e407df --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxHeightFieldFlag.h @@ -0,0 +1,162 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_NXHEIGHTFIELDFLAG +#define PX_COLLISION_NXHEIGHTFIELDFLAG +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxFlags.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Describes the format of height field samples. +@see PxHeightFieldDesc.format PxHeightFieldDesc.samples +*/ +struct PxHeightFieldFormat +{ + enum Enum + { + /** + \brief Height field height data is 16 bit signed integers, followed by triangle materials. + + Each sample is 32 bits wide arranged as follows: + + \image html heightFieldFormat_S16_TM.png + + 1) First there is a 16 bit height value. + 2) Next, two one byte material indices, with the high bit of each byte reserved for special use. + (so the material index is only 7 bits). + The high bit of material0 is the tess-flag. + The high bit of material1 is reserved for future use. + + There are zero or more unused bytes before the next sample depending on PxHeightFieldDesc.sampleStride, + where the application may eventually keep its own data. + + This is the only format supported at the moment. + + @see PxHeightFieldDesc.format PxHeightFieldDesc.samples + */ + eS16_TM = (1 << 0) + }; +}; + +/** +\brief Determines the tessellation of height field cells. +@see PxHeightFieldDesc.format PxHeightFieldDesc.samples +*/ +struct PxHeightFieldTessFlag +{ + enum Enum + { + /** + \brief This flag determines which way each quad cell is subdivided. + + The flag lowered indicates subdivision like this: (the 0th vertex is referenced by only one triangle) + + \image html heightfieldTriMat2.PNG + +
            +		+--+--+--+---> column
            +		| /| /| /|
            +		|/ |/ |/ |
            +		+--+--+--+
            +		| /| /| /|
            +		|/ |/ |/ |
            +		+--+--+--+
            +		|
            +		|
            +		V row
            +		
            + + The flag raised indicates subdivision like this: (the 0th vertex is shared by two triangles) + + \image html heightfieldTriMat1.PNG + +
            +		+--+--+--+---> column
            +		|\ |\ |\ |
            +		| \| \| \|
            +		+--+--+--+
            +		|\ |\ |\ |
            +		| \| \| \|
            +		+--+--+--+
            +		|
            +		|
            +		V row
            +		
            + + @see PxHeightFieldDesc.format PxHeightFieldDesc.samples + */ + e0TH_VERTEX_SHARED = (1 << 0) + }; +}; + + +/** +\brief Enum with flag values to be used in PxHeightFieldDesc.flags. +*/ +struct PxHeightFieldFlag +{ + enum Enum + { + /** + \brief Disable collisions with height field with boundary edges. + + Raise this flag if several terrain patches are going to be placed adjacent to each other, + to avoid a bump when sliding across. + + This flag is ignored in contact generation with sphere and capsule shapes. + + @see PxHeightFieldDesc.flags + */ + eNO_BOUNDARY_EDGES = (1 << 0) + }; +}; + +/** +\brief collection of set bits defined in PxHeightFieldFlag. + +@see PxHeightFieldFlag +*/ +typedef PxFlags PxHeightFieldFlags; +PX_FLAGS_OPERATORS(PxHeightFieldFlag::Enum,PxU16) + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h b/src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h new file mode 100644 index 000000000..b0dc61102 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h @@ -0,0 +1,143 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_HEIGHTFIELD_GEOMETRY +#define PX_PHYSICS_NX_HEIGHTFIELD_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxTriangleMeshGeometry.h" +#include "common/PxCoreUtilityTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#define PX_MIN_HEIGHTFIELD_XZ_SCALE 1e-8f +#define PX_MIN_HEIGHTFIELD_Y_SCALE (0.0001f / PxReal(0xFFFF)) + +class PxHeightField; + +/** +\brief Height field geometry class. + +This class allows to create a scaled height field geometry instance. + +There is a minimum allowed value for Y and XZ scaling - PX_MIN_HEIGHTFIELD_XZ_SCALE, heightfield creation will fail if XZ value is below this value. +*/ +class PxHeightFieldGeometry : public PxGeometry +{ +public: + PX_INLINE PxHeightFieldGeometry() : + PxGeometry (PxGeometryType::eHEIGHTFIELD), + heightField (NULL), + heightScale (1.0f), + rowScale (1.0f), + columnScale (1.0f), + heightFieldFlags(0) + {} + + PX_INLINE PxHeightFieldGeometry(PxHeightField* hf, + PxMeshGeometryFlags flags, + PxReal heightScale_, + PxReal rowScale_, + PxReal columnScale_) : + PxGeometry (PxGeometryType::eHEIGHTFIELD), + heightField (hf) , + heightScale (heightScale_), + rowScale (rowScale_), + columnScale (columnScale_), + heightFieldFlags (flags) + { + } + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid + + \note A valid height field has a positive scale value in each direction (heightScale > 0, rowScale > 0, columnScale > 0). + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a height field that has zero extents in any direction. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + /** + \brief The height field data. + */ + PxHeightField* heightField; + + /** + \brief The scaling factor for the height field in vertical direction (y direction in local space). + */ + PxReal heightScale; + + /** + \brief The scaling factor for the height field in the row direction (x direction in local space). + */ + PxReal rowScale; + + /** + \brief The scaling factor for the height field in the column direction (z direction in local space). + */ + PxReal columnScale; + + /** + \brief Flags to specify some collision properties for the height field. + */ + PxMeshGeometryFlags heightFieldFlags; + + PxPadding<3> paddingFromFlags; //!< padding for mesh flags. +}; + + +PX_INLINE bool PxHeightFieldGeometry::isValid() const +{ + if (mType != PxGeometryType::eHEIGHTFIELD) + return false; + if (!PxIsFinite(heightScale) || !PxIsFinite(rowScale) || !PxIsFinite(columnScale)) + return false; + if (rowScale < PX_MIN_HEIGHTFIELD_XZ_SCALE || columnScale < PX_MIN_HEIGHTFIELD_XZ_SCALE || heightScale < PX_MIN_HEIGHTFIELD_Y_SCALE) + return false; + if (!heightField) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldSample.h b/src/PhysX/physx/include/geometry/PxHeightFieldSample.h new file mode 100644 index 000000000..4f2170556 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxHeightFieldSample.h @@ -0,0 +1,124 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NXHEIGHTFIELDSAMPLE +#define PX_PHYSICS_NXHEIGHTFIELDSAMPLE +/** \addtogroup geomutils +@{ */ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxBitAndData.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Special material index values for height field samples. + +@see PxHeightFieldSample.materialIndex0 PxHeightFieldSample.materialIndex1 +*/ +struct PxHeightFieldMaterial +{ + enum Enum + { + eHOLE = 127 //!< A material indicating that the triangle should be treated as a hole in the mesh. + }; +}; + +/** +\brief Heightfield sample format. + +This format corresponds to the #PxHeightFieldFormat member PxHeightFieldFormat::eS16_TM. + +An array of heightfield samples are used when creating a PxHeightField to specify +the elevation of the heightfield points. In addition the material and tessellation of the adjacent +triangles are specified. + +@see PxHeightField PxHeightFieldDesc PxHeightFieldDesc.samples +*/ +struct PxHeightFieldSample +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + /** + \brief The height of the heightfield sample + + This value is scaled by PxHeightFieldGeometry::heightScale. + + @see PxHeightFieldGeometry + */ + PxI16 height; + + /** + \brief The triangle material index of the quad's lower triangle + tesselation flag + + An index pointing into the material table of the shape which instantiates the heightfield. + This index determines the material of the lower of the quad's two triangles (i.e. the quad whose + upper-left corner is this sample, see the Guide for illustrations). + + Special values of the 7 data bits are defined by PxHeightFieldMaterial + + The tesselation flag specifies which way the quad is split whose upper left corner is this sample. + If the flag is set, the diagonal of the quad will run from this sample to the opposite vertex; if not, + it will run between the other two vertices (see the Guide for illustrations). + + @see PxHeightFieldGeometry materialIndex1 PxShape.setmaterials() PxShape.getMaterials() + */ + PxBitAndByte materialIndex0; + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU8 tessFlag() const { return PxU8(materialIndex0.isBitSet() ? 1 : 0); } // PT: explicit conversion to make sure we don't break the code + PX_CUDA_CALLABLE PX_FORCE_INLINE void setTessFlag() { materialIndex0.setBit(); } + PX_CUDA_CALLABLE PX_FORCE_INLINE void clearTessFlag() { materialIndex0.clearBit(); } + + /** + \brief The triangle material index of the quad's upper triangle + reserved flag + + An index pointing into the material table of the shape which instantiates the heightfield. + This index determines the material of the upper of the quad's two triangles (i.e. the quad whose + upper-left corner is this sample, see the Guide for illustrations). + + @see PxHeightFieldGeometry materialIndex0 PxShape.setmaterials() PxShape.getMaterials() + */ + PxBitAndByte materialIndex1; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxMeshQuery.h b/src/PhysX/physx/include/geometry/PxMeshQuery.h new file mode 100644 index 000000000..0c381ea7e --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxMeshQuery.h @@ -0,0 +1,201 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_PX_MESH_QUERY +#define PX_PHYSICS_GEOMUTILS_PX_MESH_QUERY + +/** \addtogroup geomutils + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "PxQueryReport.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxGeometry; +class PxConvexMeshGeometry; +class PxTriangleMeshGeometry; +class PxHeightFieldGeometry; + +class PxTriangle; + +class PxMeshQuery +{ +public: + + /** + \brief Retrieves triangle data from a triangle ID. + + This function can be used together with #findOverlapTriangleMesh() to retrieve triangle properties. + + \param[in] triGeom Geometry of the triangle mesh to extract the triangle from. + \param[in] transform Transform for the triangle mesh + \param[in] triangleIndex The index of the triangle to retrieve. + \param[out] triangle Triangle points in world space. + \param[out] vertexIndices Returned vertex indices for given triangle + \param[out] adjacencyIndices Returned 3 triangle adjacency internal face indices (0xFFFFFFFF if no adjacency). The mesh must be cooked with cooking param buildTriangleAdjacencies enabled. + + \note This function will flip the triangle normal whenever triGeom.scale.hasNegativeDeterminant() is true. + + @see PxTriangle PxTriangleFlags PxTriangleID findOverlapTriangleMesh() + */ + PX_PHYSX_COMMON_API static void getTriangle(const PxTriangleMeshGeometry& triGeom, const PxTransform& transform, PxTriangleID triangleIndex, PxTriangle& triangle, PxU32* vertexIndices=NULL, PxU32* adjacencyIndices=NULL); + + + /** + \brief Retrieves triangle data from a triangle ID. + + This function can be used together with #findOverlapHeightField() to retrieve triangle properties. + + \param[in] hfGeom Geometry of the height field to extract the triangle from. + \param[in] transform Transform for the height field. + \param[in] triangleIndex The index of the triangle to retrieve. + \param[out] triangle Triangle points in world space. + \param[out] vertexIndices Returned vertex indices for given triangle + \param[out] adjacencyIndices Returned 3 triangle adjacency triangle indices (0xFFFFFFFF if no adjacency). + + \note This function will flip the triangle normal whenever triGeom.scale.hasNegativeDeterminant() is true. + \note TriangleIndex is an index used in internal format, which does have an index out of the bounds in last row. + To traverse all tri indices in the HF, the following code can be applied: + for (PxU32 row = 0; row < (nbRows - 1); row++) + { + for (PxU32 col = 0; col < (nbCols - 1); col++) + { + for (PxU32 k = 0; k < 2; k++) + { + const PxU32 triIndex = 2 * (row*nbCols + col) + k; + .... + } + } + } + @see PxTriangle PxTriangleFlags PxTriangleID findOverlapHeightField() + */ + PX_PHYSX_COMMON_API static void getTriangle(const PxHeightFieldGeometry& hfGeom, const PxTransform& transform, PxTriangleID triangleIndex, PxTriangle& triangle, PxU32* vertexIndices=NULL, PxU32* adjacencyIndices=NULL); + + + /** + \brief Find the mesh triangles which touch the specified geometry object. + + Returned triangle indices can be used with #getTriangle() to retrieve the triangle properties. + + \param[in] geom The geometry object to test for mesh triangle overlaps. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry and #PxBoxGeometry + \param[in] geomPose Pose of the geometry object + \param[in] meshGeom The triangle mesh geometry to check overlap against + \param[in] meshPose Pose of the triangle mesh + \param[out] results Indices of overlapping triangles + \param[in] maxResults Size of 'results' buffer + \param[in] startIndex Index of first result to be retrieved. Previous indices are skipped. + \param[out] overflow True if a buffer overflow occurred + \return Number of overlaps found, i.e. number of elements written to the results buffer + + @see PxTriangleMeshGeometry getTriangle() + */ + PX_PHYSX_COMMON_API static PxU32 findOverlapTriangleMesh( const PxGeometry& geom, const PxTransform& geomPose, + const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose, + PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow); + + /** + \brief Find the height field triangles which touch the specified geometry object. + + Returned triangle indices can be used with #getTriangle() to retrieve the triangle properties. + + \param[in] geom The geometry object to test for height field overlaps. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry and #PxBoxGeometry. The sphere and capsule queries are currently conservative estimates. + \param[in] geomPose Pose of the geometry object + \param[in] hfGeom The height field geometry to check overlap against + \param[in] hfPose Pose of the height field + \param[out] results Indices of overlapping triangles + \param[in] maxResults Size of 'results' buffer + \param[in] startIndex Index of first result to be retrieved. Previous indices are skipped. + \param[out] overflow True if a buffer overflow occurred + \return Number of overlaps found, i.e. number of elements written to the results buffer + + @see PxHeightFieldGeometry getTriangle() + */ + PX_PHYSX_COMMON_API static PxU32 findOverlapHeightField(const PxGeometry& geom, const PxTransform& geomPose, + const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose, + PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow); + + + /** + \brief Sweep a specified geometry object in space and test for collision with a set of given triangles. + + This function simply sweeps input geometry against each input triangle, in the order they are given. + This is an O(N) operation with N = number of input triangles. It does not use any particular acceleration structure. + + \param[in] unitDir Normalized direction of the sweep. + \param[in] distance Sweep distance. Needs to be larger than 0. Clamped to PX_MAX_SWEEP_DISTANCE. + \param[in] geom The geometry object to sweep. Supported geometries are #PxSphereGeometry, #PxCapsuleGeometry and #PxBoxGeometry + \param[in] pose Pose of the geometry object to sweep. + \param[in] triangleCount Number of specified triangles + \param[in] triangles Array of triangles to sweep against + \param[out] sweepHit The sweep hit information. See the notes below for limitations about returned results. + \param[in] hitFlags Specification of the kind of information to retrieve on hit. Combination of #PxHitFlag flags. See the notes below for limitations about supported flags. + \param[in] cachedIndex Cached triangle index for subsequent calls. Cached triangle is tested first. Optional parameter. + \param[in] inflation This parameter creates a skin around the swept geometry which increases its extents for sweeping. The sweep will register a hit as soon as the skin touches a shape, and will return the corresponding distance and normal. + \param[in] doubleSided Counterpart of PxMeshGeometryFlag::eDOUBLE_SIDED for input triangles. + \return True if the swept geometry object hits the specified triangles + + \note Only the following geometry types are currently supported: PxSphereGeometry, PxCapsuleGeometry, PxBoxGeometry + \note If a shape from the scene is already overlapping with the query shape in its starting position, the hit is returned unless eASSUME_NO_INITIAL_OVERLAP was specified. + \note This function returns a single closest hit across all the input triangles. Multiple hits are not supported. + \note Supported hitFlags are PxHitFlag::eDEFAULT, PxHitFlag::eASSUME_NO_INITIAL_OVERLAP, PxHitFlag::ePRECISE_SWEEP, PxHitFlag::eMESH_BOTH_SIDES, PxHitFlag::eMESH_ANY. + \note ePOSITION is only defined when there is no initial overlap (sweepHit.hadInitialOverlap() == false) + \note The returned normal for initially overlapping sweeps is set to -unitDir. + \note Otherwise the returned normal is the front normal of the triangle even if PxHitFlag::eMESH_BOTH_SIDES is set. + \note The returned PxSweepHit::faceIndex parameter will hold the index of the hit triangle in input array, i.e. the range is [0; triangleCount). For initially overlapping sweeps, this is the index of overlapping triangle. + \note The returned PxSweepHit::actor and PxSweepHit::shape pointers are not filled. + \note The inflation parameter is not compatible with PxHitFlag::ePRECISE_SWEEP. + + @see PxTriangle PxSweepHit PxGeometry PxTransform + */ + PX_PHYSX_COMMON_API static bool sweep(const PxVec3& unitDir, + const PxReal distance, + const PxGeometry& geom, + const PxTransform& pose, + PxU32 triangleCount, + const PxTriangle* triangles, + PxSweepHit& sweepHit, + PxHitFlags hitFlags = PxHitFlag::eDEFAULT, + const PxU32* cachedIndex = NULL, + const PxReal inflation = 0.0f, + bool doubleSided = false); +}; + + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxMeshScale.h b/src/PhysX/physx/include/geometry/PxMeshScale.h new file mode 100644 index 000000000..7771a7e0e --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxMeshScale.h @@ -0,0 +1,175 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_MESHSCALE +#define PX_PHYSICS_NX_MESHSCALE +/** \addtogroup geomutils +@{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxMat33.h" +#include "foundation/PxAssert.h" + +/** \brief Minimum allowed absolute magnitude for each of mesh scale's components (x,y,z). + \note Only positive scale values are allowed for convex meshes. */ +#define PX_MESH_SCALE_MIN 1e-6f + +/** \brief Maximum allowed absolute magnitude for each of mesh scale's components (x,y,z). + \note Only positive scale values are allowed for convex meshes. */ +#define PX_MESH_SCALE_MAX 1e6f + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief A class expressing a nonuniform scaling transformation. + +The scaling is along arbitrary axes that are specified by PxMeshScale::rotation. + +\note Negative scale values are supported for PxTriangleMeshGeometry + with absolute values for each component within [PX_MIN_ABS_MESH_SCALE, PX_MAX_ABS_MESH_SCALE] range. + Negative scale causes a reflection around the specified axis, in addition PhysX will flip the normals + for mesh triangles when scale.x*scale.y*scale.z < 0. +\note Only positive scale values are supported for PxConvexMeshGeometry + with values for each component within [PX_MIN_ABS_MESH_SCALE, PX_MAX_ABS_MESH_SCALE] range). + +@see PxConvexMeshGeometry PxTriangleMeshGeometry +*/ +class PxMeshScale +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + /** + \brief Constructor initializes to identity scale. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMeshScale(): scale(1.0f), rotation(PxIdentity) + { + } + + /** + \brief Constructor from scalar. + */ + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE PxMeshScale(PxReal r): scale(r), rotation(PxIdentity) + { + } + + /** + \brief Constructor to initialize to arbitrary scale and identity scale rotation. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMeshScale(const PxVec3& s) + { + scale = s; + rotation = PxQuat(PxIdentity); + } + + /** + \brief Constructor to initialize to arbitrary scaling. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMeshScale(const PxVec3& s, const PxQuat& r) + { + PX_ASSERT(r.isUnit()); + scale = s; + rotation = r; + } + + + /** + \brief Returns true if the scaling is an identity transformation. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isIdentity() const + { + return (scale.x == 1.0f && scale.y == 1.0f && scale.z == 1.0f); + } + + /** + \brief Returns the inverse of this scaling transformation. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMeshScale getInverse() const + { + return PxMeshScale(PxVec3(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z), rotation); + } + + /** + \brief Converts this transformation to a 3x3 matrix representation. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33 toMat33() const + { + PxMat33 rot(rotation); + PxMat33 trans = rot.getTranspose(); + trans.column0 *= scale[0]; + trans.column1 *= scale[1]; + trans.column2 *= scale[2]; + return trans * rot; + } + + /** + \brief Returns true if combination of negative scale components will cause the triangle normal to flip. The SDK will flip the normals internally. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool hasNegativeDeterminant() const + { + return (scale.x * scale.y * scale.z < 0.0f); + } + + PxVec3 transform(const PxVec3& v) const + { + return rotation.rotateInv(scale.multiply(rotation.rotate(v))); + } + + bool isValidForTriangleMesh() const + { + PxVec3 absXYZ = scale.abs(); + return (absXYZ.maxElement() <= PX_MESH_SCALE_MAX) && (absXYZ.minElement() >= PX_MESH_SCALE_MIN); + } + + bool isValidForConvexMesh() const + { + return (scale.maxElement() <= PX_MESH_SCALE_MAX) && (scale.minElement() >= PX_MESH_SCALE_MIN); + } + + PxVec3 scale; //!< A nonuniform scaling + PxQuat rotation; //!< The orientation of the scaling axes + + +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxPlaneGeometry.h b/src/PhysX/physx/include/geometry/PxPlaneGeometry.h new file mode 100644 index 000000000..002c72715 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxPlaneGeometry.h @@ -0,0 +1,107 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_PLANE_GEOMETRY +#define PX_PHYSICS_NX_PLANE_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "foundation/PxPlane.h" +#include "foundation/PxTransform.h" +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Class describing a plane geometry. + +The plane geometry specifies the half-space volume x<=0. As with other geometry types, +when used in a PxShape the collision volume is obtained by transforming the halfspace +by the shape local pose and the actor global pose. + +To generate a PxPlane from a PxTransform, transform PxPlane(1,0,0,0). + +To generate a PxTransform from a PxPlane, use PxTransformFromPlaneEquation. + +@see PxShape.setGeometry() PxShape.getPlaneGeometry() PxTransformFromPlaneEquation +*/ +class PxPlaneGeometry : public PxGeometry +{ +public: + PX_INLINE PxPlaneGeometry() : PxGeometry(PxGeometryType::ePLANE) {} + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid + */ + PX_INLINE bool isValid() const; +}; + + +PX_INLINE bool PxPlaneGeometry::isValid() const +{ + if (mType != PxGeometryType::ePLANE) + return false; + + return true; +} + + +/** \brief creates a transform from a plane equation, suitable for an actor transform for a PxPlaneGeometry + +\param[in] plane the desired plane equation +\return a PxTransform which will transform the plane PxPlane(1,0,0,0) to the specified plane +*/ + +PX_FOUNDATION_API PxTransform PxTransformFromPlaneEquation(const PxPlane& plane); + +/** \brief creates a plane equation from a transform, such as the actor transform for a PxPlaneGeometry + +\param[in] transform the transform +\return the plane +*/ + + +PX_INLINE PxPlane PxPlaneEquationFromTransform(const PxTransform& transform) +{ + return transform.transform(PxPlane(1.f,0.f,0.f,0.f)); +} + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h b/src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h new file mode 100644 index 000000000..eeaea9858 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h @@ -0,0 +1,166 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_NX_SIMPLETRIANGLEMESH +#define PX_PHYSICS_GEOMUTILS_NX_SIMPLETRIANGLEMESH +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxFlags.h" +#include "common/PxCoreUtilityTypes.h" +#include "common/PxPhysXCommonConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Enum with flag values to be used in PxSimpleTriangleMesh::flags. +*/ +struct PxMeshFlag +{ + enum Enum + { + /** + \brief Specifies if the SDK should flip normals. + + The PhysX libraries assume that the face normal of a triangle with vertices [a,b,c] can be computed as: + edge1 = b-a + edge2 = c-a + face_normal = edge1 x edge2. + + Note: This is the same as a counterclockwise winding in a right handed coordinate system or + alternatively a clockwise winding order in a left handed coordinate system. + + If this does not match the winding order for your triangles, raise the below flag. + */ + eFLIPNORMALS = (1<<0), + e16_BIT_INDICES = (1<<1) //!< Denotes the use of 16-bit vertex indices + }; +}; + +/** +\brief collection of set bits defined in PxMeshFlag. + +@see PxMeshFlag +*/ +typedef PxFlags PxMeshFlags; +PX_FLAGS_OPERATORS(PxMeshFlag::Enum,PxU16) + + +/** +\brief A structure describing a triangle mesh. +*/ +class PxSimpleTriangleMesh +{ +public: + + /** + \brief Pointer to first vertex point. + */ + PxBoundedData points; + + /** + \brief Pointer to first triangle. + + Caller may add triangleStrideBytes bytes to the pointer to access the next triangle. + + These are triplets of 0 based indices: + vert0 vert1 vert2 + vert0 vert1 vert2 + vert0 vert1 vert2 + ... + + where vertex is either a 32 or 16 bit unsigned integer. There are numTriangles*3 indices. + + This is declared as a void pointer because it is actually either an PxU16 or a PxU32 pointer. + */ + PxBoundedData triangles; + + /** + \brief Flags bits, combined from values of the enum ::PxMeshFlag + */ + PxMeshFlags flags; + + /** + \brief constructor sets to default. + */ + PX_INLINE PxSimpleTriangleMesh(); + /** + \brief (re)sets the structure to the default. + */ + PX_INLINE void setToDefault(); + /** + \brief returns true if the current settings are valid + */ + PX_INLINE bool isValid() const; +}; + + +PX_INLINE PxSimpleTriangleMesh::PxSimpleTriangleMesh() +{ +} + +PX_INLINE void PxSimpleTriangleMesh::setToDefault() +{ + *this = PxSimpleTriangleMesh(); +} + +PX_INLINE bool PxSimpleTriangleMesh::isValid() const +{ + // Check geometry + if(points.count > 0xffff && flags & PxMeshFlag::e16_BIT_INDICES) + return false; + if(!points.data) + return false; + if(points.stride < sizeof(PxVec3)) //should be at least one point's worth of data + return false; + + // Check topology + // The triangles pointer is not mandatory + if(triangles.data) + { + // Indexed mesh + PxU32 limit = (flags & PxMeshFlag::e16_BIT_INDICES) ? sizeof(PxU16)*3 : sizeof(PxU32)*3; + if(triangles.stride < limit) + return false; + } + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxSphereGeometry.h b/src/PhysX/physx/include/geometry/PxSphereGeometry.h new file mode 100644 index 000000000..05ea69c76 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxSphereGeometry.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_SPHERE_GEOMETRY +#define PX_PHYSICS_NX_SPHERE_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxGeometry.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief A class representing the geometry of a sphere. + +Spheres are defined by their radius. +\note The scaling of the sphere is expected to be baked into this value, there is no additional scaling parameter. +*/ +class PxSphereGeometry : public PxGeometry +{ +public: + PX_INLINE PxSphereGeometry() : PxGeometry(PxGeometryType::eSPHERE), radius(0) {} + PX_INLINE PxSphereGeometry(PxReal ir) : PxGeometry(PxGeometryType::eSPHERE), radius(ir) {} + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid + + \note A valid sphere has radius > 0. + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a sphere that has zero radius. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + + /** + \brief The radius of the sphere. + */ + PxReal radius; +}; + + +PX_INLINE bool PxSphereGeometry::isValid() const +{ + if (mType != PxGeometryType::eSPHERE) + return false; + if (!PxIsFinite(radius)) + return false; + if (radius <= 0.0f) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxTriangle.h b/src/PhysX/physx/include/geometry/PxTriangle.h new file mode 100644 index 000000000..68d227464 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxTriangle.h @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_PX_TRIANGLE +#define PX_PHYSICS_GEOMUTILS_PX_TRIANGLE +/** \addtogroup geomutils + @{ +*/ + +#include "common/PxPhysXCommonConfig.h" +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Triangle class. +*/ +class PxTriangle +{ + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE PxTriangle() {} + + /** + \brief Constructor + + \param[in] p0 Point 0 + \param[in] p1 Point 1 + \param[in] p2 Point 2 + */ + PX_FORCE_INLINE PxTriangle(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) + { + verts[0] = p0; + verts[1] = p1; + verts[2] = p2; + } + + /** + \brief Copy constructor + + \param[in] triangle Tri to copy + */ + PX_FORCE_INLINE PxTriangle(const PxTriangle& triangle) + { + verts[0] = triangle.verts[0]; + verts[1] = triangle.verts[1]; + verts[2] = triangle.verts[2]; + } + + /** + \brief Destructor + */ + PX_FORCE_INLINE ~PxTriangle() {} + + /** + \brief Assignment operator + */ + PX_FORCE_INLINE void operator=(const PxTriangle& triangle) + { + verts[0] = triangle.verts[0]; + verts[1] = triangle.verts[1]; + verts[2] = triangle.verts[2]; + } + + /** + \brief Compute the normal of the Triangle. + + \param[out] _normal Triangle normal. + */ + PX_FORCE_INLINE void normal(PxVec3& _normal) const + { + _normal = (verts[1]-verts[0]).cross(verts[2]-verts[0]); + _normal.normalize(); + } + + /** + \brief Compute the unnormalized normal of the triangle. + + \param[out] _normal Triangle normal (not normalized). + */ + PX_FORCE_INLINE void denormalizedNormal(PxVec3& _normal) const + { + _normal = (verts[1]-verts[0]).cross(verts[2]-verts[0]); + } + + /** + \brief Compute the area of the triangle. + + \return Area of the triangle. + */ + PX_FORCE_INLINE PxReal area() const + { + const PxVec3& p0 = verts[0]; + const PxVec3& p1 = verts[1]; + const PxVec3& p2 = verts[2]; + return ((p0 - p1).cross(p0 - p2)).magnitude() * 0.5f; + } + + /** + \return Computes a point on the triangle from u and v barycentric coordinates. + */ + PxVec3 pointFromUV(PxReal u, PxReal v) const { return (1.0f-u-v)*verts[0] + u*verts[1] + v*verts[2]; } + + /** + \brief Array of Vertices. + */ + PxVec3 verts[3]; + +}; + + +#if !PX_DOXYGEN +} +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxTriangleMesh.h b/src/PhysX/physx/include/geometry/PxTriangleMesh.h new file mode 100644 index 000000000..e6fc1582e --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxTriangleMesh.h @@ -0,0 +1,317 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_GEOMUTILS_NX_TRIANGLEMESH +#define PX_PHYSICS_GEOMUTILS_NX_TRIANGLEMESH +/** \addtogroup geomutils +@{ */ + +#include "foundation/PxVec3.h" +#include "foundation/PxBounds3.h" +#include "common/PxPhysXCommonConfig.h" +#include "common/PxBase.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Enables the dynamic rtree mesh feature. It is recommended to use this feature for scene queries only. +@see PxTriangleMesh::getVerticesForModification +@see PxTriangleMesh::refitBVH +*/ +#define PX_ENABLE_DYNAMIC_MESH_RTREE 1 + +/** +\brief Mesh midphase structure. This enum is used to select the desired acceleration structure for midphase queries + (i.e. raycasts, overlaps, sweeps vs triangle meshes). + + The PxMeshMidPhase::eBVH33 structure is the one used in recent PhysX versions (up to PhysX 3.3). It has great performance and is + supported on all platforms. + + The PxMeshMidPhase::eBVH34 structure is a revisited implementation introduced in PhysX 3.4. It can be significantly faster both + in terms of cooking performance and runtime performance, but it is currently only available on platforms supporting the + SSE2 instuction set. +*/ +struct PxMeshMidPhase +{ + enum Enum + { + eBVH33 = 0, //!< Default midphase mesh structure, as used up to PhysX 3.3 + eBVH34 = 1, //!< New midphase mesh structure, introduced in PhysX 3.4 + + eLAST + }; +}; + +/** +\brief Flags for the mesh geometry properties. + +Used in ::PxTriangleMeshFlags. +*/ +struct PxTriangleMeshFlag +{ + enum Enum + { + e16_BIT_INDICES = (1<<1), //!< The triangle mesh has 16bits vertex indices. + eADJACENCY_INFO = (1<<2) //!< The triangle mesh has adjacency information build. + }; +}; + +/** +\brief collection of set bits defined in PxTriangleMeshFlag. + +@see PxTriangleMeshFlag +*/ +typedef PxFlags PxTriangleMeshFlags; +PX_FLAGS_OPERATORS(PxTriangleMeshFlag::Enum,PxU8) + +/** + +\brief A triangle mesh, also called a 'polygon soup'. + +It is represented as an indexed triangle list. There are no restrictions on the +triangle data. + +To avoid duplicating data when you have several instances of a particular +mesh positioned differently, you do not use this class to represent a +mesh object directly. Instead, you create an instance of this mesh via +the PxTriangleMeshGeometry and PxShape classes. + +

            Creation

            + +To create an instance of this class call PxPhysics::createTriangleMesh(), +and release() to delete it. This is only possible +once you have released all of its PxShape instances. + + +

            Visualizations:

            +\li #PxVisualizationParameter::eCOLLISION_AABBS +\li #PxVisualizationParameter::eCOLLISION_SHAPES +\li #PxVisualizationParameter::eCOLLISION_AXES +\li #PxVisualizationParameter::eCOLLISION_FNORMALS +\li #PxVisualizationParameter::eCOLLISION_EDGES + +@see PxTriangleMeshDesc PxTriangleMeshGeometry PxShape PxPhysics.createTriangleMesh() +*/ + +class PxTriangleMesh : public PxBase +{ + public: + /** + \brief Returns the number of vertices. + \return number of vertices + @see getVertices() + */ + virtual PxU32 getNbVertices() const = 0; + + /** + \brief Returns the vertices. + \return array of vertices + @see getNbVertices() + */ + virtual const PxVec3* getVertices() const = 0; + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + /** + \brief Returns all mesh vertices for modification. + + This function will return the vertices of the mesh so that their positions can be changed in place. + After modifying the vertices you must call refitBVH for the refitting to actually take place. + This function maintains the old mesh topology (triangle indices). + + \return inplace vertex coordinates for each existing mesh vertex. + + \note works only for PxMeshMidPhase::eBVH33 + \note Size of array returned is equal to the number returned by getNbVertices(). + \note This function operates on cooked vertex indices. + \note This means the index mapping and vertex count can be different from what was provided as an input to the cooking routine. + \note To achieve unchanged 1-to-1 index mapping with orignal mesh data (before cooking) please use the following cooking flags: + \note eWELD_VERTICES = 0, eDISABLE_CLEAN_MESH = 1. + \note It is also recommended to make sure that a call to validateTriangleMesh returns true if mesh cleaning is disabled. + @see getNbVertices() + @see refitBVH() + */ + virtual PxVec3* getVerticesForModification() = 0; + + /** + \brief Refits BVH for mesh vertices. + + This function will refit the mesh BVH to correctly enclose the new positions updated by getVerticesForModification. + Mesh BVH will not be reoptimized by this function so significantly different new positions will cause significantly reduced performance. + + \return New bounds for the entire mesh. + + \note works only for PxMeshMidPhase::eBVH33 + \note PhysX does not keep a mapping from the mesh to mesh shapes that reference it. + \note Call PxShape::setGeometry on each shape which references the mesh, to ensure that internal data structures are updated to reflect the new geometry. + \note PxShape::setGeometry does not guarantee correct/continuous behavior when objects are resting on top of old or new geometry. + \note It is also recommended to make sure that a call to validateTriangleMesh returns true if mesh cleaning is disabled. + \note Active edges information will be lost during refit, the rigid body mesh contact generation might not perform as expected. + @see getNbVertices() + @see getVerticesForModification() + */ + virtual PxBounds3 refitBVH() = 0; +#endif // PX_ENABLE_DYNAMIC_MESH_RTREE + + /** + \brief Returns the number of triangles. + \return number of triangles + @see getTriangles() getTrianglesRemap() + */ + virtual PxU32 getNbTriangles() const = 0; + + /** + \brief Returns the triangle indices. + + The indices can be 16 or 32bit depending on the number of triangles in the mesh. + Call getTriangleMeshFlags() to know if the indices are 16 or 32 bits. + + The number of indices is the number of triangles * 3. + + \return array of triangles + @see getNbTriangles() getTriangleMeshFlags() getTrianglesRemap() + */ + virtual const void* getTriangles() const = 0; + + /** + \brief Reads the PxTriangleMesh flags. + + See the list of flags #PxTriangleMeshFlag + + \return The values of the PxTriangleMesh flags. + + @see PxTriangleMesh + */ + virtual PxTriangleMeshFlags getTriangleMeshFlags() const = 0; + + /** + \brief Returns the triangle remapping table. + + The triangles are internally sorted according to various criteria. Hence the internal triangle order + does not always match the original (user-defined) order. The remapping table helps finding the old + indices knowing the new ones: + + remapTable[ internalTriangleIndex ] = originalTriangleIndex + + \return the remapping table (or NULL if 'PxCookingParams::suppressTriangleMeshRemapTable' has been used) + @see getNbTriangles() getTriangles() PxCookingParams::suppressTriangleMeshRemapTable + */ + virtual const PxU32* getTrianglesRemap() const = 0; + + + /** + \brief Decrements the reference count of a triangle mesh and releases it if the new reference count is zero. + + @see PxPhysics.createTriangleMesh() + */ + virtual void release() = 0; + + /** + \brief Returns material table index of given triangle + + This function takes a post cooking triangle index. + + \param[in] triangleIndex (internal) index of desired triangle + \return Material table index, or 0xffff if no per-triangle materials are used + */ + virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const = 0; + + /** + \brief Returns the local-space (vertex space) AABB from the triangle mesh. + + \return local-space bounds + */ + virtual PxBounds3 getLocalBounds() const = 0; + + /** + \brief Returns the reference count for shared meshes. + + At creation, the reference count of the mesh is 1. Every shape referencing this mesh increments the + count by 1. When the reference count reaches 0, and only then, the mesh gets destroyed automatically. + + \return the current reference count. + */ + virtual PxU32 getReferenceCount() const = 0; + + /** + \brief Acquires a counted reference to a triangle mesh. + + This method increases the reference count of the triangle mesh by 1. Decrement the reference count by calling release() + */ + virtual void acquireReference() = 0; + +protected: + PX_INLINE PxTriangleMesh(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxTriangleMesh(PxBaseFlags baseFlags) : PxBase(baseFlags) {} + virtual ~PxTriangleMesh() {} + + virtual bool isKindOf(const char* name) const { return !::strcmp("PxTriangleMesh", name) || PxBase::isKindOf(name); } +}; + +/** + +\brief A triangle mesh containing the PxMeshMidPhase::eBVH33 structure. + +@see PxMeshMidPhase +*/ +class PxBVH33TriangleMesh : public PxTriangleMesh +{ + public: +protected: + PX_INLINE PxBVH33TriangleMesh(PxType concreteType, PxBaseFlags baseFlags) : PxTriangleMesh(concreteType, baseFlags) {} + PX_INLINE PxBVH33TriangleMesh(PxBaseFlags baseFlags) : PxTriangleMesh(baseFlags) {} + virtual ~PxBVH33TriangleMesh() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxBVH33TriangleMesh", name) || PxTriangleMesh::isKindOf(name); } +}; + +/** + +\brief A triangle mesh containing the PxMeshMidPhase::eBVH34 structure. + +@see PxMeshMidPhase +*/ +class PxBVH34TriangleMesh : public PxTriangleMesh +{ + public: +protected: + PX_INLINE PxBVH34TriangleMesh(PxType concreteType, PxBaseFlags baseFlags) : PxTriangleMesh(concreteType, baseFlags) {} + PX_INLINE PxBVH34TriangleMesh(PxBaseFlags baseFlags) : PxTriangleMesh(baseFlags) {} + virtual ~PxBVH34TriangleMesh() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxBVH34TriangleMesh", name) || PxTriangleMesh::isKindOf(name); } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h b/src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h new file mode 100644 index 000000000..f2a8bdbd8 --- /dev/null +++ b/src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h @@ -0,0 +1,150 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NX_TRIANGLEMESH_GEOMETRY +#define PX_PHYSICS_NX_TRIANGLEMESH_GEOMETRY +/** \addtogroup geomutils +@{ +*/ +#include "geometry/PxGeometry.h" +#include "geometry/PxMeshScale.h" +#include "common/PxCoreUtilityTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxTriangleMesh; + + +/** +\brief Flags controlling the simulated behavior of the triangle mesh geometry. + +Used in ::PxMeshGeometryFlags. +*/ +struct PxMeshGeometryFlag +{ + enum Enum + { + eDOUBLE_SIDED = (1<<1) //!< Meshes with this flag set are treated as double-sided. + //!< This flag is currently only used for raycasts and sweeps (it is ignored for overlap queries). + //!< For detailed specifications of this flag for meshes and heightfields please refer to the Geometry Query section of the user guide. + }; +}; + +/** +\brief collection of set bits defined in PxMeshGeometryFlag. + +@see PxMeshGeometryFlag +*/ +typedef PxFlags PxMeshGeometryFlags; +PX_FLAGS_OPERATORS(PxMeshGeometryFlag::Enum,PxU8) + +/** +\brief Triangle mesh geometry class. + +This class unifies a mesh object with a scaling transform, and +lets the combined object be used anywhere a PxGeometry is needed. + +The scaling is a transform along arbitrary axes contained in the scale object. +The vertices of the mesh in geometry (or shape) space is the +PxMeshScale::toMat33() transform, multiplied by the vertex space vertices +in the PxConvexMesh object. +*/ +class PxTriangleMeshGeometry : public PxGeometry +{ +public: + /** + \brief Default constructor. + + Creates an empty object with a NULL mesh and identity scale. + */ + PX_INLINE PxTriangleMeshGeometry() : + PxGeometry (PxGeometryType::eTRIANGLEMESH), + triangleMesh(NULL) + {} + + /** + \brief Constructor. + \param[in] mesh Mesh pointer. May be NULL, though this will not make the object valid for shape construction. + \param[in] scaling Scale factor. + \param[in] flags Mesh flags. + \ + */ + PX_INLINE PxTriangleMeshGeometry( PxTriangleMesh* mesh, + const PxMeshScale& scaling = PxMeshScale(), + PxMeshGeometryFlags flags = PxMeshGeometryFlags()) : + PxGeometry (PxGeometryType::eTRIANGLEMESH), + scale (scaling), + meshFlags (flags), + triangleMesh(mesh) + {} + + /** + \brief Returns true if the geometry is valid. + + \return True if the current settings are valid for shape creation. + + \note A valid triangle mesh has a positive scale value in each direction (scale.scale.x > 0, scale.scale.y > 0, scale.scale.z > 0). + It is illegal to call PxRigidActor::createShape and PxPhysics::createShape with a triangle mesh that has zero extents in any direction. + + @see PxRigidActor::createShape, PxPhysics::createShape + */ + PX_INLINE bool isValid() const; + +public: + PxMeshScale scale; //!< The scaling transformation. + PxMeshGeometryFlags meshFlags; //!< Mesh flags. + PxPadding<3> paddingFromFlags; //!< padding for mesh flags + PxTriangleMesh* triangleMesh; //!< A reference to the mesh object. +}; + + +PX_INLINE bool PxTriangleMeshGeometry::isValid() const +{ + if(mType != PxGeometryType::eTRIANGLEMESH) + return false; + if(!scale.scale.isFinite() || !scale.rotation.isUnit()) + return false; + if(!scale.isValidForTriangleMesh()) + return false; + if(!triangleMesh) + return false; + + return true; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geomutils/GuContactBuffer.h b/src/PhysX/physx/include/geomutils/GuContactBuffer.h new file mode 100644 index 000000000..06568fe62 --- /dev/null +++ b/src/PhysX/physx/include/geomutils/GuContactBuffer.h @@ -0,0 +1,131 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONTACTBUFFER_H +#define GU_CONTACTBUFFER_H + +#include "PxPhysXConfig.h" +#include "PxContact.h" +#include "GuContactPoint.h" + +namespace physx +{ +namespace Gu +{ + + struct NarrowPhaseParams + { + PX_FORCE_INLINE NarrowPhaseParams(PxReal contactDistance, PxReal meshContactMargin, PxReal toleranceLength) : + mContactDistance(contactDistance), + mMeshContactMargin(meshContactMargin), + mToleranceLength(toleranceLength) {} + + PxReal mContactDistance; + PxReal mMeshContactMargin; // PT: Margin used to generate mesh contacts. Temp & unclear, should be removed once GJK is default path. + PxReal mToleranceLength; // PT: copy of PxTolerancesScale::length + }; + +//sizeof(SavedContactData)/sizeof(PxU32) = 17, 1088/17 = 64 triangles in the local array +#define LOCAL_CONTACTS_SIZE 1088 + +class ContactBuffer +{ +public: + + static const PxU32 MAX_CONTACTS = 64; + + Gu::ContactPoint contacts[MAX_CONTACTS]; + PxU32 count; + PxU32 pad; + + PX_FORCE_INLINE void reset() + { + count = 0; + } + + PX_FORCE_INLINE bool contact(const PxVec3& worldPoint, + const PxVec3& worldNormalIn, + PxReal separation, + PxU32 faceIndex1 = PXC_CONTACT_NO_FACE_INDEX + ) + { + PX_ASSERT(PxAbs(worldNormalIn.magnitude()-1)<1e-3f); + + if(count>=MAX_CONTACTS) + return false; + + Gu::ContactPoint& p = contacts[count++]; + p.normal = worldNormalIn; + p.point = worldPoint; + p.separation = separation; + p.internalFaceIndex1= faceIndex1; + return true; + } + + PX_FORCE_INLINE bool contact(const PxVec3& worldPoint, + const PxVec3& worldNormalIn, + PxReal separation, + PxU16 internalUsage, + PxU32 faceIndex1 = PXC_CONTACT_NO_FACE_INDEX + ) + { + PX_ASSERT(PxAbs(worldNormalIn.magnitude() - 1)<1e-3f); + + if (count >= MAX_CONTACTS) + return false; + + Gu::ContactPoint& p = contacts[count++]; + p.normal = worldNormalIn; + p.point = worldPoint; + p.separation = separation; + p.internalFaceIndex1 = faceIndex1; + p.forInternalUse = internalUsage; + return true; + } + + PX_FORCE_INLINE bool contact(const Gu::ContactPoint & pt) + { + if(count>=MAX_CONTACTS) + return false; + contacts[count++] = pt; + return true; + } + + PX_FORCE_INLINE Gu::ContactPoint* contact() + { + if(count>=MAX_CONTACTS) + return NULL; + return &contacts[count++]; + } +}; + +} +} + +#endif diff --git a/src/PhysX/physx/include/geomutils/GuContactPoint.h b/src/PhysX/physx/include/geomutils/GuContactPoint.h new file mode 100644 index 000000000..5f2bcb623 --- /dev/null +++ b/src/PhysX/physx/include/geomutils/GuContactPoint.h @@ -0,0 +1,109 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONTACT_POINT_H +#define GU_CONTACT_POINT_H + +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" + +namespace physx +{ +namespace Gu +{ + +struct ContactPoint +{ + /** + \brief The normal of the contacting surfaces at the contact point. + + For two shapes s0 and s1, the normal points in the direction that s0 needs to move in to resolve the contact with s1. + */ + PX_ALIGN(16, PxVec3 normal); + /** + \brief The separation of the shapes at the contact point. A negative separation denotes a penetration. + */ + PxReal separation; + + /** + \brief The point of contact between the shapes, in world space. + */ + PX_ALIGN(16, PxVec3 point); + + /** + \brief The max impulse permitted at this point + */ + PxReal maxImpulse; + + PX_ALIGN(16, PxVec3 targetVel); + + /** + \brief The static friction coefficient + */ + PxReal staticFriction; + + /** + \brief Material flags for this contact (eDISABLE_FRICTION, eDISABLE_STRONG_FRICTION). @see PxMaterialFlag + */ + PxU8 materialFlags; + + /** + \brief internal structure used for internal use only + */ + PxU16 forInternalUse; + + /** + \brief The surface index of shape 1 at the contact point. This is used to identify the surface material. + + \note This field is only supported by triangle meshes and heightfields, else it will be set to PXC_CONTACT_NO_FACE_INDEX. + \note This value must be directly after internalFaceIndex0 in memory + */ + + PxU32 internalFaceIndex1; + + /** + \brief The dynamic friction coefficient + */ + PxReal dynamicFriction; + /** + \brief The restitution coefficient + */ + PxReal restitution; + +}; + +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/gpu/PxGpu.h b/src/PhysX/physx/include/gpu/PxGpu.h new file mode 100644 index 000000000..5f495bcaa --- /dev/null +++ b/src/PhysX/physx/include/gpu/PxGpu.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PX_GPU_H +#define PX_GPU_H + +#include "PxPhysXConfig.h" + + +#if PX_SUPPORT_GPU_PHYSX + +#include "cudamanager/PxCudaContextManager.h" +#include "cudamanager/PxCudaMemoryManager.h" +#include "cudamanager/PxGpuCopyDesc.h" +#include "foundation/Px.h" +#include "foundation/PxPreprocessor.h" +#include "common/PxPhysXCommonConfig.h" +#include "PxFoundation.h" + +/** +\brief PxGpuLoadHook + +This is a helper class for loading the PhysXGpu dll. +If a PhysXGpu dll with a non-default file name needs to be loaded, +PxGpuLoadHook can be sub-classed to provide the custom filenames. + +Once the names are set, the instance must be set for use by PhysX.dll using PxSetPhysXGpuLoadHook(), + +@see PxSetPhysXGpuLoadHook() +*/ +class PxGpuLoadHook +{ +public: + PxGpuLoadHook() {} + virtual ~PxGpuLoadHook() {} + + virtual const char* getPhysXGpuDllName() const = 0; + +protected: +private: +}; + +/** +\brief Sets GPU load hook instance for PhysX dll. + +\param[in] hook GPU load hook. + +@see PxGpuLoadHook +*/ +PX_C_EXPORT PX_PHYSX_CORE_API void PX_CALL_CONV PxSetPhysXGpuLoadHook(const PxGpuLoadHook* hook); + +/** + * \brief Ask the NVIDIA control panel which GPU has been selected for use by + * PhysX. Returns -1 if no PhysX capable GPU is found or GPU PhysX has + * been disabled. + */ +PX_C_EXPORT PX_PHYSX_CORE_API int PX_CALL_CONV PxGetSuggestedCudaDeviceOrdinal(physx::PxErrorCallback& errc); + +/** + * \brief Allocate a CUDA Context manager, complete with heaps and task dispatcher. + * You only need one CUDA context manager per GPU device you intend to use for + * CUDA tasks. + */ +PX_C_EXPORT PX_PHYSX_CORE_API physx::PxCudaContextManager* PX_CALL_CONV PxCreateCudaContextManager(physx::PxFoundation& foundation, const physx::PxCudaContextManagerDesc& desc); + +#endif // PX_SUPPORT_GPU_PHYSX + +#endif // PX_GPU_H diff --git a/src/PhysX/physx/include/pvd/PxPvd.h b/src/PhysX/physx/include/pvd/PxPvd.h new file mode 100644 index 000000000..df4cbc63d --- /dev/null +++ b/src/PhysX/physx/include/pvd/PxPvd.h @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVD_H +#define PXPVDSDK_PXPVD_H + +/** \addtogroup pvd +@{ +*/ +#include "foundation/PxFlags.h" +#include "foundation/PxProfiler.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxFoundation; +class PxPvdTransport; + +/** +\brief types of instrumentation that PVD can do. +*/ +struct PxPvdInstrumentationFlag +{ + enum Enum + { + /** + \brief Send debugging information to PVD. + + This information is the actual object data of the rigid statics, shapes, + articulations, etc. Sending this information has a noticeable impact on + performance and thus this flag should not be set if you want an accurate + performance profile. + */ + eDEBUG = 1 << 0, + + /** + \brief Send profile information to PVD. + + This information populates PVD's profile view. It has (at this time) negligible + cost compared to Debug information and makes PVD *much* more useful so it is quite + highly recommended. + + This flag works together with a PxCreatePhysics parameter. + Using it allows the SDK to send profile events to PVD. + */ + ePROFILE = 1 << 1, + + /** + \brief Send memory information to PVD. + + The PVD sdk side hooks into the Foundation memory controller and listens to + allocation/deallocation events. This has a noticable hit on the first frame, + however, this data is somewhat compressed and the PhysX SDK doesn't allocate much + once it hits a steady state. This information also has a fairly negligible + impact and thus is also highly recommended. + + This flag works together with a PxCreatePhysics parameter, + trackOutstandingAllocations. Using both of them together allows users to have + an accurate view of the overall memory usage of the simulation at the cost of + a hashtable lookup per allocation/deallocation. Again, PhysX makes a best effort + attempt not to allocate or deallocate during simulation so this hashtable lookup + tends to have no effect past the first frame. + + Sending memory information without tracking outstanding allocations means that + PVD will accurate information about the state of the memory system before the + actual connection happened. + */ + eMEMORY = 1 << 2, + + eALL = (eDEBUG | ePROFILE | eMEMORY) + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxPvdInstrumentationFlag. + +@see PxPvdInstrumentationFlag +*/ +typedef PxFlags PxPvdInstrumentationFlags; +PX_FLAGS_OPERATORS(PxPvdInstrumentationFlag::Enum, uint8_t) + +/** +\brief PxPvd is the top-level class for the PVD framework, and the main customer interface for PVD +configuration.It is a singleton class, instantiated and owned by the application. +*/ +class PxPvd : public physx::PxProfilerCallback +{ + public: + /** + Connects the SDK to the PhysX Visual Debugger application. + \param transport transport for pvd captured data. + \param flags Flags to set. + return True if success + */ + virtual bool connect(PxPvdTransport& transport, PxPvdInstrumentationFlags flags) = 0; + + /** + Disconnects the SDK from the PhysX Visual Debugger application. + If we are still connected, this will kill the entire debugger connection. + */ + virtual void disconnect() = 0; + + /** + * Return if connection to PVD is created. + \param useCachedStatus + 1> When useCachedStaus is false, isConnected() checks the lowlevel network status. + This can be slow because it needs to lock the lowlevel network stream. If isConnected() is + called frequently, the expense of locking can be significant. + 2> When useCachedStatus is true, isConnected() checks the highlevel cached status with atomic access. + It is faster than locking, but the status may be different from the lowlevel network with latency of up to + one frame. + The reason for this is that the cached status is changed inside socket listener, which is not + called immediately when the lowlevel connection status changes. + */ + virtual bool isConnected(bool useCachedStatus = true) = 0; + + /** + returns the PVD data transport + returns NULL if no transport is present. + */ + virtual PxPvdTransport* getTransport() = 0; + + /** + Retrieves the PVD flags. See PxPvdInstrumentationFlags. + */ + virtual PxPvdInstrumentationFlags getInstrumentationFlags() = 0; + + /** + \brief Releases the pvd instance. + */ + virtual void release() = 0; + + protected: + virtual ~PxPvd() + { + } +}; + +/** + \brief Create a pvd instance. + \param foundation is the foundation instance that stores the allocator and error callbacks. +*/ +PX_C_EXPORT PxPvd* PX_CALL_CONV PxCreatePvd(PxFoundation& foundation); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVD_H diff --git a/src/PhysX/physx/include/pvd/PxPvdSceneClient.h b/src/PhysX/physx/include/pvd/PxPvdSceneClient.h new file mode 100644 index 000000000..86c5eb040 --- /dev/null +++ b/src/PhysX/physx/include/pvd/PxPvdSceneClient.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PVD_SCENE_CLIENT_H +#define PX_PVD_SCENE_CLIENT_H + +/** \addtogroup pvd +@{ +*/ + +#include "foundation/PxFlags.h" + +namespace physx +{ + namespace pvdsdk + { + class PvdClient; + struct PvdDebugPoint; + struct PvdDebugLine; + struct PvdDebugTriangle; + struct PvdDebugText; + } +} + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief PVD scene Flags. They are disabled by default, and only works if PxPvdInstrumentationFlag::eDEBUG is set. +*/ +struct PxPvdSceneFlag +{ + enum Enum + { + eTRANSMIT_CONTACTS = (1 << 0), //! Transmits contact stream to PVD. + eTRANSMIT_SCENEQUERIES = (1 << 1), //! Transmits scene query stream to PVD. + eTRANSMIT_CONSTRAINTS = (1 << 2) //! Transmits constraints visualize stream to PVD. + }; +}; + +/** +\brief Bitfield that contains a set of raised flags defined in PxPvdSceneFlag. + +@see PxPvdSceneFlag +*/ +typedef PxFlags PxPvdSceneFlags; +PX_FLAGS_OPERATORS(PxPvdSceneFlag::Enum, PxU8) + +/** +\brief Special client for PxScene. +It provides access to the PxPvdSceneFlag. +It also provides simple user debug services that associated scene position such as immediate rendering and camera updates. +*/ +class PxPvdSceneClient +{ + public: + /** + Sets the PVD flag. See PxPvdSceneFlag. + \param flag Flag to set. + \param value value the flag gets set to. + */ + virtual void setScenePvdFlag(PxPvdSceneFlag::Enum flag, bool value) = 0; + + /** + Sets the PVD flags. See PxPvdSceneFlags. + \param flags Flags to set. + */ + virtual void setScenePvdFlags(PxPvdSceneFlags flags) = 0; + + /** + Retrieves the PVD flags. See PxPvdSceneFlags. + */ + virtual PxPvdSceneFlags getScenePvdFlags() const = 0; + + /** + update camera on PVD application's render window + */ + virtual void updateCamera(const char* name, const PxVec3& origin, const PxVec3& up, const PxVec3& target) = 0; + + /** + draw points on PVD application's render window + */ + virtual void drawPoints(const physx::pvdsdk::PvdDebugPoint* points, PxU32 count) = 0; + + /** + draw lines on PVD application's render window + */ + virtual void drawLines(const physx::pvdsdk::PvdDebugLine* lines, PxU32 count) = 0; + + /** + draw triangles on PVD application's render window + */ + virtual void drawTriangles(const physx::pvdsdk::PvdDebugTriangle* triangles, PxU32 count) = 0; + + /** + draw text on PVD application's render window + */ + virtual void drawText(const physx::pvdsdk::PvdDebugText& text) = 0; + + /** + get the underlying client, for advanced users + */ + virtual physx::pvdsdk::PvdClient* getClientInternal() = 0; + +protected: + virtual ~PxPvdSceneClient(){} +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PX_PVD_SCENE_CLIENT_H diff --git a/src/PhysX/physx/include/pvd/PxPvdTransport.h b/src/PhysX/physx/include/pvd/PxPvdTransport.h new file mode 100644 index 000000000..098fbb5c1 --- /dev/null +++ b/src/PhysX/physx/include/pvd/PxPvdTransport.h @@ -0,0 +1,129 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDTRANSPORT_H +#define PXPVDSDK_PXPVDTRANSPORT_H + +/** \addtogroup pvd +@{ +*/ +#include "foundation/PxErrors.h" +#include "foundation/PxFlags.h" +#include "pvd/PxPvd.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief PxPvdTransport is an interface representing the data transport mechanism. +This class defines all services associated with the transport: configuration, connection, reading, writing etc. +It is owned by the application, and can be realized as a file or a socket (using one-line PxDefault<...> methods in +PhysXExtensions) or in a custom implementation. This is a class that is intended for use by PVD, not by the +application, the application entry points are PxPvd and PvdClient. +*/ + +class PxPvdTransport +{ + public: + // connect, isConnected, disconnect, read, write, flush + + /** + Connects to the Visual Debugger application. + return True if success + */ + virtual bool connect() = 0; + + /** + Disconnects from the Visual Debugger application. + If we are still connected, this will kill the entire debugger connection. + */ + virtual void disconnect() = 0; + + /** + * Return if connection to PVD is created. + */ + virtual bool isConnected() = 0; + + /** + * write bytes to the other endpoint of the connection. should lock before witre. If an error occurs + * this connection will assume to be dead. + */ + virtual bool write(const uint8_t* inBytes, uint32_t inLength) = 0; + + /* + lock this transport and return it + */ + virtual PxPvdTransport& lock() = 0; + + /* + unlock this transport + */ + virtual void unlock() = 0; + + /** + * send any data and block until we know it is at least on the wire. + */ + virtual void flush() = 0; + + /** + * Return size of written data. + */ + virtual uint64_t getWrittenDataSize() = 0; + + virtual void release() = 0; + + protected: + virtual ~PxPvdTransport() + { + } +}; + +/** + \brief Create a default socket transport. + \param host host address of the pvd application. + \param port ip port used for pvd, should same as the port setting in pvd application. + \param timeoutInMilliseconds timeout when connect to pvd host. +*/ +PX_C_EXPORT PxPvdTransport* PX_CALL_CONV +PxDefaultPvdSocketTransportCreate(const char* host, int port, unsigned int timeoutInMilliseconds); + +/** + \brief Create a default file transport. + \param name full path filename used save captured pvd data, or NULL for a fake/test file transport. +*/ +PX_C_EXPORT PxPvdTransport* PX_CALL_CONV PxDefaultPvdFileTransportCreate(const char* name); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDTRANSPORT_H diff --git a/src/PhysX/physx/include/solver/PxSolverDefs.h b/src/PhysX/physx/include/solver/PxSolverDefs.h new file mode 100644 index 000000000..6fad51007 --- /dev/null +++ b/src/PhysX/physx/include/solver/PxSolverDefs.h @@ -0,0 +1,256 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_SOLVER_DEFS_H +#define PX_SOLVER_DEFS_H + + +#include "PxPhysXConfig.h" +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "foundation/PxTransform.h" +#include "PxConstraintDesc.h" +#include "geomutils/GuContactPoint.h" + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4324) // structure was padded due to alignment +#endif + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +namespace Dy +{ + //struct FsData; + class ArticulationV; +} + +namespace Sc +{ + class ShapeInteraction; +} + +struct PxSolverBody +{ + PX_ALIGN(16, PxVec3) linearVelocity; //!< Delta linear velocity computed by the solver + PxU16 maxSolverNormalProgress; //!< Progress counter used by constraint batching and parallel island solver. + PxU16 maxSolverFrictionProgress; //!< Progress counter used by constraint batching and parallel island solver. + + PxVec3 angularState; //!< Delta angular velocity state computed by the solver. + + PxU32 solverProgress; //!< Progress counter used by constraint batching and parallel island solver + + PxSolverBody() : linearVelocity(0.f), maxSolverNormalProgress(0), maxSolverFrictionProgress(0), angularState(0), solverProgress(0) + { + } +}; + +PX_COMPILE_TIME_ASSERT(sizeof(PxSolverBody) == 32); + +struct PxSolverBodyData +{ + PX_ALIGN(16, PxVec3 linearVelocity); //!< 12 Pre-solver linear velocity + PxReal invMass; //!< 16 inverse mass + PxVec3 angularVelocity; //!< 28 Pre-solver angular velocity + PxReal reportThreshold; //!< 32 contact force threshold + PxMat33 sqrtInvInertia; //!< 68 inverse inertia in world space + PxReal penBiasClamp; //!< 72 the penetration bias clamp + PxU32 nodeIndex; //!< 76 the node idx of this solverBodyData. Used by solver to reference between solver bodies and island bodies. Not required by immediate mode + PxReal maxContactImpulse; //!< 80 the max contact impulse + PxTransform body2World; //!< 108 the body's transform + PxU16 lockFlags; //!< 110 lock flags + PxU16 pad; //!< 112 pad + + PX_FORCE_INLINE PxReal projectVelocity(const PxVec3& lin, const PxVec3& ang) const + { + return linearVelocity.dot(lin) + angularVelocity.dot(ang); + } +}; + +//---------------------------------- +/* +* A header that defines the size of a specific batch of constraints (of same type and without dependencies) +*/ +struct PxConstraintBatchHeader +{ + PxU32 mStartIndex; //!< Start index for this batch + PxU16 mStride; //!< Number of constraints in this batch (range: 1-4) + PxU16 mConstraintType; //!< The type of constraint this batch references +}; + + +struct PxSolverConstraintDesc +{ + static const PxU16 NO_LINK = 0xffff; + + enum ConstraintType + { + eCONTACT_CONSTRAINT, //!< Defines this pair is a contact constraint + eJOINT_CONSTRAINT //!< Defines this pair is a joint constraint + }; + + union + { + PxSolverBody* bodyA; //!< bodyA pointer + Dy::ArticulationV* articulationA; //!< Articulation pointer for body A + }; + + union + { + PxSolverBody* bodyB; //!< BodyB pointer + Dy::ArticulationV* articulationB; //!< Articulation pointer for body B + }; + PxU16 linkIndexA; //!< Link index defining which link in Articulation A this constraint affects. If not an articulation, must be NO_LINK + PxU16 linkIndexB; //!< Link index defining which link in Articulation B this constraint affects. If not an articulation, must be NO_LINK. + union + { + PxU16 articulationALength; //!< The total length of articulation A in multiples of 16 bytes + PxU32 bodyADataIndex; //!< Body A's index into the SolverBodyData array + }; + + union + { + PxU16 articulationBLength; //!< The total lengh of articulation B in multiples of 16 bytes. + PxU32 bodyBDataIndex; //!< Body B's index into the SolverBodyData array + }; + + PxU16 writeBackLengthOver4; //!< writeBackLength/4, max writeback length is 256K, allows PxSolverConstraintDesc to fit in 32 bytes + PxU16 constraintLengthOver16; //!< constraintLength/16, max constraint length is 1MB, allows PxSolverConstraintDesc to fit in 32 bytes + + PxU8* constraint; //!< Pointer to the constraint rows to be solved + void* writeBack; //!< Pointer to the writeback structure results for this given constraint are to be written to +}; + +struct PxSolverConstraintPrepDescBase +{ + enum BodyState + { + eDYNAMIC_BODY = 1 << 0, + eSTATIC_BODY = 1 << 1, + eKINEMATIC_BODY = 1 << 2, + eARTICULATION = 1 << 3 + }; + + PxConstraintInvMassScale mInvMassScales; //!< In: The local mass scaling for this pair. + + PxSolverConstraintDesc* desc; //!< Output: The PxSolverConstraintDesc filled in by contact prep + + const PxSolverBody* body0; //!< In: The first body. Stores velocity information. Unused unless contact involves articulations. + const PxSolverBody* body1; //!< In: The second body. Stores velocity information. Unused unless contact involves articulations. + + const PxSolverBodyData* data0; //!< In: The first PxSolverBodyData. Stores mass and miscellaneous information for the first body. + const PxSolverBodyData* data1; //!< In: The second PxSolverBodyData. Stores mass and miscellaneous information for the second body + + PxTransform bodyFrame0; //!< In: The world-space transform of the first body. + PxTransform bodyFrame1; //!< In: The world-space transform of the second body. + + BodyState bodyState0; //!< In: Defines what kind of actor the first body is + BodyState bodyState1; //!< In: Defines what kind of actor the second body is + +}; + +struct PxSolverConstraintPrepDesc : public PxSolverConstraintPrepDescBase +{ + PX_ALIGN(16, Px1DConstraint* rows); //!< The start of the constraint rows + PxU32 numRows; //!< The number of rows + + PxReal linBreakForce, angBreakForce; //!< Break forces + PxReal minResponseThreshold; //!< The minimum response threshold + void* writeback; //!< Pointer to constraint writeback structure. Reports back joint breaking. If not required, set to NULL. + bool disablePreprocessing; //!< Disable joint pre-processing. Pre-processing can improve stability but under certain circumstances, e.g. when some invInertia rows are zero/almost zero, can cause instabilities. + bool improvedSlerp; //!< Use improved slerp model + bool driveLimitsAreForces; //!< Indicates whether drive limits are forces + bool extendedLimits; //!< Indicates whether we want to use extended limits + + PxVec3 body0WorldOffset; //!< Body0 world offset +}; + + +struct PxSolverContactDesc : public PxSolverConstraintPrepDescBase +{ + + PX_ALIGN(16, Sc::ShapeInteraction* shapeInteraction); //!< Pointer to share interaction. Used for force threshold reports in solver. Set to NULL if using immediate mode. + Gu::ContactPoint* contacts; //!< The start of the contacts for this pair + PxU32 numContacts; //!< The total number of contacs this pair references. + + bool hasMaxImpulse; //!< Defines whether this pairs has maxImpulses clamping enabled + bool disableStrongFriction; //!< Defines whether this pair disables strong friction (sticky friction correlation) + bool hasForceThresholds; //!< Defines whether this pair requires force thresholds + + PxReal restDistance; //!< A distance at which the solver should aim to hold the bodies separated. Default is 0 + PxReal maxCCDSeparation; //!< A distance used to configure speculative CCD behavior. Default is PX_MAX_F32. Set internally in PhysX for bodies with eENABLE_SPECULATIVE_CCD on. Do not set directly! + + PxU8* frictionPtr; //!< InOut: Friction patch correlation data. Set each frame by solver. Can be retained for improved behaviour or discarded each frame. + PxU8 frictionCount; //!< The total number of friction patches in this pair + + PxReal* contactForces; //!< Out: A buffer for the solver to write applied contact forces to. + + PxU32 startFrictionPatchIndex; //!< Start index of friction patch in the correlation buffer. Set by friction correlation + PxU32 numFrictionPatches; //!< Total number of friction patches in this pair. Set by friction correlation + + PxU32 startContactPatchIndex; //!< The start index of this pair's contact patches in the correlation buffer. For internal use only + PxU16 numContactPatches; //!< Total number of contact patches. + PxU16 axisConstraintCount; //!< Axis constraint count. Defines how many constraint rows this pair has produced. Useful for statistical purposes. + + PxU8 pad[16 - sizeof(void*)]; +}; + +class PxConstraintAllocator +{ +public: + /** + \brief Allocates constraint data. It is the application's responsibility to release this memory after PxSolveConstraints has completed. + \param[in] byteSize Allocation size in bytes + \return the allocated memory. This address must be 16-byte aligned. + */ + virtual PxU8* reserveConstraintData(const PxU32 byteSize) = 0; + /** + \brief Allocates friction data. Friction data can be retained by the application for a given pair and provided as an input to PxSolverContactDesc to improve simulation stability. + It is the application's responsibility to release this memory. If this memory is released, the application should ensure it does not pass pointers to this memory to PxSolverContactDesc. + \param[in] byteSize Allocation size in bytes + \return the allocated memory. This address must be 4-byte aligned. + */ + virtual PxU8* reserveFrictionData(const PxU32 byteSize) = 0; + + virtual ~PxConstraintAllocator() {} +}; + +#if !PX_DOXYGEN +} +#endif + +#if PX_VC +#pragma warning(pop) +#endif + +#endif + diff --git a/src/PhysX/physx/include/task/PxCpuDispatcher.h b/src/PhysX/physx/include/task/PxCpuDispatcher.h new file mode 100644 index 000000000..523c69af4 --- /dev/null +++ b/src/PhysX/physx/include/task/PxCpuDispatcher.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXCPUDISPATCHER_H +#define PXTASK_PXCPUDISPATCHER_H + +#include "task/PxTaskDefine.h" +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + +class PxBaseTask; + +/** + \brief A CpuDispatcher is responsible for scheduling the execution of tasks passed to it by the SDK. + + A typical implementation would for example use a thread pool with the dispatcher + pushing tasks onto worker thread queues or a global queue. + + @see PxBaseTask + @see PxTask + @see PxTaskManager +*/ +class PxCpuDispatcher +{ +public: + /** + \brief Called by the TaskManager when a task is to be queued for execution. + + Upon receiving a task, the dispatcher should schedule the task + to run when resource is available. After the task has been run, + it should call the release() method and discard it's pointer. + + \param[in] task The task to be run. + + @see PxBaseTask + */ + virtual void submitTask( PxBaseTask& task ) = 0; + + /** + \brief Returns the number of available worker threads for this dispatcher. + + The SDK will use this count to control how many tasks are submitted. By + matching the number of tasks with the number of execution units task + overhead can be reduced. + */ + virtual uint32_t getWorkerCount() const = 0; + + virtual ~PxCpuDispatcher() {} +}; + +} // end physx namespace + +#endif // PXTASK_PXCPUDISPATCHER_H diff --git a/src/PhysX/physx/include/task/PxGpuDispatcher.h b/src/PhysX/physx/include/task/PxGpuDispatcher.h new file mode 100644 index 000000000..ae53ab1a1 --- /dev/null +++ b/src/PhysX/physx/include/task/PxGpuDispatcher.h @@ -0,0 +1,248 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXGPUDISPATCHER_H +#define PXTASK_PXGPUDISPATCHER_H + +#include "task/PxTaskDefine.h" +#include "task/PxTask.h" + +/* forward decl to avoid including */ +typedef struct CUstream_st* CUstream; + +namespace physx +{ + +struct PxGpuCopyDesc; +class PxCudaContextManager; + +PX_PUSH_PACK_DEFAULT + +class PxTaskManager; + +/** \brief A GpuTask dispatcher + * + * A PxGpuDispatcher executes GpuTasks submitted by one or more TaskManagers (one + * or more scenes). It maintains a CPU worker thread which waits on GpuTask + * "groups" to be submitted. The submission API is explicitly sessioned so that + * GpuTasks are dispatched together as a group whenever possible to improve + * parallelism on the GPU. + * + * A PxGpuDispatcher cannot be allocated ad-hoc, they are created as a result of + * creating a PxCudaContextManager. Every PxCudaContextManager has a PxGpuDispatcher + * instance that can be queried. In this way, each PxGpuDispatcher is tied to + * exactly one CUDA context. + * + * A scene will use CPU fallback Tasks for GpuTasks if the PxTaskManager provided + * to it does not have a PxGpuDispatcher. For this reason, the PxGpuDispatcher must + * be assigned to the PxTaskManager before the PxTaskManager is given to a scene. + * + * Multiple TaskManagers may safely share a single PxGpuDispatcher instance, thus + * enabling scenes to share a CUDA context. + * + * Only failureDetected() is intended for use by the user. The rest of the + * nvGpuDispatcher public methods are reserved for internal use by only both + * TaskManagers and GpuTasks. + */ +class PxGpuDispatcher +{ +public: + /** \brief Record the start of a simulation step + * + * A PxTaskManager calls this function to record the beginning of a simulation + * step. The PxGpuDispatcher uses this notification to initialize the + * profiler state. + */ + virtual void startSimulation() = 0; + + /** \brief Record the start of a GpuTask batch submission + * + * A PxTaskManager calls this function to notify the PxGpuDispatcher that one or + * more GpuTasks are about to be submitted for execution. The PxGpuDispatcher + * will not read the incoming task queue until it receives one finishGroup() + * call for each startGroup() call. This is to ensure as many GpuTasks as + * possible are executed together as a group, generating optimal parallelism + * on the GPU. + */ + virtual void startGroup() = 0; + + /** \brief Submit a GpuTask for execution + * + * Submitted tasks are pushed onto an incoming queue. The PxGpuDispatcher + * will take the contents of this queue every time the pending group count + * reaches 0 and run the group of submitted GpuTasks as an interleaved + * group. + */ + virtual void submitTask(PxTask& task) = 0; + + /** \brief Record the end of a GpuTask batch submission + * + * A PxTaskManager calls this function to notify the PxGpuDispatcher that it is + * done submitting a group of GpuTasks (GpuTasks which were all make ready + * to run by the same prerequisite dependency becoming resolved). If no + * other group submissions are in progress, the PxGpuDispatcher will execute + * the set of ready tasks. + */ + virtual void finishGroup() = 0; + + /** \brief Add a CUDA completion prerequisite dependency to a task + * + * A GpuTask calls this function to add a prerequisite dependency on another + * task (usually a CpuTask) preventing that task from starting until all of + * the CUDA kernels and copies already launched have been completed. The + * PxGpuDispatcher will increment that task's reference count, blocking its + * execution, until the CUDA work is complete. + * + * This is generally only required when a CPU task is expecting the results + * of the CUDA kernels to have been copied into host memory. + * + * This mechanism is not at all not required to ensure CUDA kernels and + * copies are issued in the correct order. Kernel issue order is determined + * by normal task dependencies. The rule of thumb is to only use a blocking + * completion prerequisite if the task in question depends on a completed + * GPU->Host DMA. + * + * The PxGpuDispatcher issues a blocking event record to CUDA for the purposes + * of tracking the already submitted CUDA work. When this event is + * resolved, the PxGpuDispatcher manually decrements the reference count of + * the specified task, allowing it to execute (assuming it does not have + * other pending prerequisites). + */ + virtual void addCompletionPrereq(PxBaseTask& task) = 0; + + /** \brief Retrieve the PxCudaContextManager associated with this + * PxGpuDispatcher + * + * Every PxCudaContextManager has one PxGpuDispatcher, and every PxGpuDispatcher + * has one PxCudaContextManager. + */ + virtual PxCudaContextManager* getCudaContextManager() = 0; + + /** \brief Record the end of a simulation frame + * + * A PxTaskManager calls this function to record the completion of its + * dependency graph. If profiling is enabled, the PxGpuDispatcher will + * trigger the retrieval of profiling data from the GPU at this point. + */ + virtual void stopSimulation() = 0; + + /** \brief Returns true if a CUDA call has returned a non-recoverable error + * + * A return value of true indicates a fatal error has occurred. To protect + * itself, the PxGpuDispatcher enters a fall through mode that allows GpuTasks + * to complete without being executed. This allows simulations to continue + * but leaves GPU content static or corrupted. + * + * The user may try to recover from these failures by deleting GPU content + * so the visual artifacts are minimized. But there is no way to recover + * the state of the GPU actors before the failure. Once a CUDA context is + * in this state, the only recourse is to create a new CUDA context, a new + * scene, and start over. + * + * This is our "Best Effort" attempt to not turn a soft failure into a hard + * failure because continued use of a CUDA context after it has returned an + * error will usually result in a driver reset. However if the initial + * failure was serious enough, a reset may have already occurred by the time + * we learn of it. + */ + virtual bool failureDetected() const = 0; + + /** \brief Force the PxGpuDispatcher into failure mode + * + * This API should be used if user code detects a non-recoverable CUDA + * error. This ensures the PxGpuDispatcher does not launch any further + * CUDA work. Subsequent calls to failureDetected() will return true. + */ + virtual void forceFailureMode() = 0; + + /** \brief Launch a copy kernel with arbitrary number of copy commands + * + * This method is intended to be called from Kernel GpuTasks, but it can + * function outside of that context as well. + * + * If count is 1, the descriptor is passed to the kernel as arguments, so it + * may be declared on the stack. + * + * If count is greater than 1, the kernel will read the descriptors out of + * host memory. Because of this, the descriptor array must be located in + * page locked (pinned) memory. The provided descriptors may be modified by + * this method (converting host pointers to their GPU mapped equivalents) + * and should be considered *owned* by CUDA until the current batch of work + * has completed, so descriptor arrays should not be freed or modified until + * you have received a completion notification. + * + * If your GPU does not support mapping of page locked memory (SM>=1.1), + * this function degrades to calling CUDA copy methods. + */ + virtual void launchCopyKernel(PxGpuCopyDesc* desc, uint32_t count, CUstream stream) = 0; + + /** \brief Query pre launch task that runs before launching gpu kernels. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Do *not* set the continuation on the returned task, but use addPreLaunchDependent(). + */ + virtual PxBaseTask& getPreLaunchTask() = 0; + + /** \brief Adds a gpu launch task that gets executed after the pre launch task. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Each call adds a reference to the pre-launch task. + */ + virtual void addPreLaunchDependent(PxBaseTask& dependent) = 0; + + /** \brief Query post launch task that runs after the gpu is done. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Do *not* set the continuation on the returned task, but use addPostLaunchDependent(). + */ + virtual PxBaseTask& getPostLaunchTask() = 0; + + /** \brief Adds a task that gets executed after the post launch task. + * + * This is part of an optional feature to schedule multiple gpu features + * at the same time to get kernels to run in parallel. + * \note Each call adds a reference to the pre-launch task. + */ + virtual void addPostLaunchDependent(PxBaseTask& dependent) = 0; + +protected: + /** \brief protected destructor + * + * GpuDispatchers are allocated and freed by their PxCudaContextManager. + */ + virtual ~PxGpuDispatcher() {} +}; + +PX_POP_PACK + +} // end physx namespace + + +#endif // PXTASK_PXGPUDISPATCHER_H diff --git a/src/PhysX/physx/include/task/PxGpuTask.h b/src/PhysX/physx/include/task/PxGpuTask.h new file mode 100644 index 000000000..e9d88ac52 --- /dev/null +++ b/src/PhysX/physx/include/task/PxGpuTask.h @@ -0,0 +1,118 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef PXTASK_PXGPUTASK_H +#define PXTASK_PXGPUTASK_H + +#include "task/PxTaskDefine.h" +#include "task/PxTask.h" +#include "task/PxGpuDispatcher.h" + +namespace physx +{ + +PX_PUSH_PACK_DEFAULT + +/** \brief Define the 'flavor' of a PxGpuTask + * + * Each PxGpuTask should have a specific function; either copying data to the + * device, running kernels on that data, or copying data from the device. + * + * For optimal performance, the dispatcher should run all available HtoD tasks + * before running all Kernel tasks, and all Kernel tasks before running any DtoH + * tasks. This provides maximal kernel overlap and the least number of CUDA + * flushes. + */ +struct PxGpuTaskHint +{ + /// \brief Enums for the type of GPU task + enum Enum + { + HostToDevice, + Kernel, + DeviceToHost, + + NUM_GPU_TASK_HINTS + }; +}; + +/** + * \brief PxTask implementation for launching CUDA work + */ +class PxGpuTask : public PxTask +{ +public: + PxGpuTask() : mComp(NULL) {} + + /** + * \brief iterative "run" function for a PxGpuTask + * + * The GpuDispatcher acquires the CUDA context for the duration of this + * function call, and it is highly recommended that the PxGpuTask use the + * provided CUstream for all kernels. + * + * kernelIndex will be 0 for the initial call and incremented before each + * subsequent call. Once launchInstance() returns false, its PxGpuTask is + * considered completed and is released. + */ + virtual bool launchInstance(CUstream stream, int kernelIndex) = 0; + + /** + * \brief Returns a hint indicating the function of this task + */ + virtual PxGpuTaskHint::Enum getTaskHint() const = 0; + + /** + * \brief Specify a task that will have its reference count decremented + * when this task is released + */ + void setCompletionTask(PxBaseTask& task) + { + mComp = &task; + } + + void release() + { + if (mComp) + { + mComp->removeReference(); + mComp = NULL; + } + PxTask::release(); + } + +protected: + /// \brief A pointer to the completion task + PxBaseTask* mComp; +}; + +PX_POP_PACK + +} // end physx namespace + +#endif // PXTASK_PXGPUTASK_H diff --git a/src/PhysX/physx/include/task/PxTask.h b/src/PhysX/physx/include/task/PxTask.h new file mode 100644 index 000000000..b51e6bc58 --- /dev/null +++ b/src/PhysX/physx/include/task/PxTask.h @@ -0,0 +1,334 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXTASK_H +#define PXTASK_PXTASK_H + +#include "task/PxTaskDefine.h" +#include "task/PxTaskManager.h" +#include "task/PxCpuDispatcher.h" +#include "task/PxGpuDispatcher.h" +#include "foundation/PxAssert.h" + +namespace physx +{ + +/** + * \brief Base class of all task types + * + * PxBaseTask defines a runnable reference counted task with built-in profiling. + */ +class PxBaseTask +{ +public: + PxBaseTask() : mContextID(0), mTm(NULL) {} + virtual ~PxBaseTask() {} + + /** + * \brief The user-implemented run method where the task's work should be performed + * + * run() methods must be thread safe, stack friendly (no alloca, etc), and + * must never block. + */ + virtual void run() = 0; + + /** + * \brief Return a user-provided task name for profiling purposes. + * + * It does not have to be unique, but unique names are helpful. + * + * \return The name of this task + */ + virtual const char* getName() const = 0; + + //! \brief Implemented by derived implementation classes + virtual void addReference() = 0; + //! \brief Implemented by derived implementation classes + virtual void removeReference() = 0; + //! \brief Implemented by derived implementation classes + virtual int32_t getReference() const = 0; + + /** \brief Implemented by derived implementation classes + * + * A task may assume in its release() method that the task system no longer holds + * references to it - so it may safely run its destructor, recycle itself, etc. + * provided no additional user references to the task exist + */ + virtual void release() = 0; + + /** + * \brief Return PxTaskManager to which this task was submitted + * + * Note, can return NULL if task was not submitted, or has been + * completed. + */ + PX_FORCE_INLINE PxTaskManager* getTaskManager() const + { + return mTm; + } + + PX_FORCE_INLINE void setContextId(PxU64 id) { mContextID = id; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + +protected: + PxU64 mContextID; //!< Context ID for profiler interface + PxTaskManager* mTm; //!< Owning PxTaskManager instance + + friend class PxTaskMgr; +}; + + +/** + * \brief A PxBaseTask implementation with deferred execution and full dependencies + * + * A PxTask must be submitted to a PxTaskManager to to be executed, Tasks may + * optionally be named when they are submitted. + */ +class PxTask : public PxBaseTask +{ +public: + PxTask() : mTaskID(0) {} + virtual ~PxTask() {} + + //! \brief Release method implementation + virtual void release() + { + PX_ASSERT(mTm); + + // clear mTm before calling taskCompleted() for safety + PxTaskManager* save = mTm; + mTm = NULL; + save->taskCompleted( *this ); + } + + //! \brief Inform the PxTaskManager this task must finish before the given + // task is allowed to start. + PX_INLINE void finishBefore( PxTaskID taskID ) + { + PX_ASSERT(mTm); + mTm->finishBefore( *this, taskID); + } + + //! \brief Inform the PxTaskManager this task cannot start until the given + // task has completed. + PX_INLINE void startAfter( PxTaskID taskID ) + { + PX_ASSERT(mTm); + mTm->startAfter( *this, taskID ); + } + + /** + * \brief Manually increment this task's reference count. The task will + * not be allowed to run until removeReference() is called. + */ + PX_INLINE void addReference() + { + PX_ASSERT(mTm); + mTm->addReference( mTaskID ); + } + + /** + * \brief Manually decrement this task's reference count. If the reference + * count reaches zero, the task will be dispatched. + */ + PX_INLINE void removeReference() + { + PX_ASSERT(mTm); + mTm->decrReference( mTaskID ); + } + + /** + * \brief Return the ref-count for this task + */ + PX_INLINE int32_t getReference() const + { + return mTm->getReference( mTaskID ); + } + + /** + * \brief Return the unique ID for this task + */ + PX_INLINE PxTaskID getTaskID() const + { + return mTaskID; + } + + /** + * \brief Called by PxTaskManager at submission time for initialization + * + * Perform simulation step initialization here. + */ + virtual void submitted() + { + mStreamIndex = 0; + mPreSyncRequired = false; + } + + /** + * \brief Specify that the GpuTask sync flag be set + */ + PX_INLINE void requestSyncPoint() + { + mPreSyncRequired = true; + } + +protected: + PxTaskID mTaskID; //!< ID assigned at submission + uint32_t mStreamIndex; //!< GpuTask CUDA stream index + bool mPreSyncRequired; //!< GpuTask sync flag + + friend class PxTaskMgr; + friend class PxGpuWorkerThread; +}; + + +/** + * \brief A PxBaseTask implementation with immediate execution and simple dependencies + * + * A PxLightCpuTask bypasses the PxTaskManager launch dependencies and will be + * submitted directly to your scene's CpuDispatcher. When the run() function + * completes, it will decrement the reference count of the specified + * continuation task. + * + * You must use a full-blown PxTask if you want your task to be resolved + * by another PxTask, or you need more than a single dependency to be + * resolved when your task completes, or your task will not run on the + * CpuDispatcher. + */ +class PxLightCpuTask : public PxBaseTask +{ +public: + PxLightCpuTask() + : mCont( NULL ) + , mRefCount( 0 ) + { + } + virtual ~PxLightCpuTask() + { + mTm = NULL; + } + + /** + * \brief Initialize this task and specify the task that will have its ref count decremented on completion. + * + * Submission is deferred until the task's mRefCount is decremented to zero. + * Note that we only use the PxTaskManager to query the appropriate dispatcher. + * + * \param[in] tm The PxTaskManager this task is managed by + * \param[in] c The task to be executed when this task has finished running + */ + PX_INLINE void setContinuation(PxTaskManager& tm, PxBaseTask* c) + { + PX_ASSERT( mRefCount == 0 ); + mRefCount = 1; + mCont = c; + mTm = &tm; + if( mCont ) + { + mCont->addReference(); + } + } + + /** + * \brief Initialize this task and specify the task that will have its ref count decremented on completion. + * + * This overload of setContinuation() queries the PxTaskManager from the continuation + * task, which cannot be NULL. + * \param[in] c The task to be executed after this task has finished running + */ + PX_INLINE void setContinuation( PxBaseTask* c ) + { + PX_ASSERT( c ); + PX_ASSERT( mRefCount == 0 ); + mRefCount = 1; + mCont = c; + if( mCont ) + { + mCont->addReference(); + mTm = mCont->getTaskManager(); + PX_ASSERT( mTm ); + } + } + + /** + * \brief Retrieves continuation task + */ + PX_INLINE PxBaseTask* getContinuation() const + { + return mCont; + } + + /** + * \brief Manually decrement this task's reference count. If the reference + * count reaches zero, the task will be dispatched. + */ + PX_INLINE void removeReference() + { + mTm->decrReference(*this); + } + + /** \brief Return the ref-count for this task */ + PX_INLINE int32_t getReference() const + { + return mRefCount; + } + + /** + * \brief Manually increment this task's reference count. The task will + * not be allowed to run until removeReference() is called. + */ + PX_INLINE void addReference() + { + mTm->addReference(*this); + } + + /** + * \brief called by CpuDispatcher after run method has completed + * + * Decrements the continuation task's reference count, if specified. + */ + PX_INLINE void release() + { + if( mCont ) + { + mCont->removeReference(); + } + } + +protected: + + PxBaseTask* mCont; //!< Continuation task, can be NULL + volatile int32_t mRefCount; //!< PxTask is dispatched when reaches 0 + + friend class PxTaskMgr; +}; + + +}// end physx namespace + + +#endif // PXTASK_PXTASK_H diff --git a/src/PhysX/physx/include/task/PxTaskDefine.h b/src/PhysX/physx/include/task/PxTaskDefine.h new file mode 100644 index 000000000..935089046 --- /dev/null +++ b/src/PhysX/physx/include/task/PxTaskDefine.h @@ -0,0 +1,37 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXTASKDEFINE_H +#define PXTASK_PXTASKDEFINE_H + +#include "foundation/PxPreprocessor.h" + +#ifndef PX_SUPPORT_PXTASK_PROFILING +#define PX_SUPPORT_PXTASK_PROFILING 1 +#endif + +#endif // PXTASK_PXTASKDEFINE_H diff --git a/src/PhysX/physx/include/task/PxTaskManager.h b/src/PhysX/physx/include/task/PxTaskManager.h new file mode 100644 index 000000000..d1738ef0b --- /dev/null +++ b/src/PhysX/physx/include/task/PxTaskManager.h @@ -0,0 +1,228 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXTASK_PXTASKMANAGER_H +#define PXTASK_PXTASKMANAGER_H + +#include "task/PxTaskDefine.h" +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxErrorCallback.h" + +namespace physx +{ +PX_PUSH_PACK_DEFAULT + +class PxBaseTask; +class PxTask; +class PxLightCpuTask; +typedef unsigned int PxTaskID; + +/** +\brief Identifies the type of each heavyweight PxTask object + +\note This enum type is only used by PxTask and GpuTask objects, LightCpuTasks do not use this enum. + +@see PxTask +@see PxLightCpuTask +*/ +struct PxTaskType +{ + /** + * \brief Identifies the type of each heavyweight PxTask object + */ + enum Enum + { + TT_CPU, //!< PxTask will be run on the CPU + TT_GPU, //!< PxTask will be run on the GPU + TT_NOT_PRESENT, //!< Return code when attempting to find a task that does not exist + TT_COMPLETED //!< PxTask execution has been completed + }; +}; + +class PxCpuDispatcher; +class PxGpuDispatcher; + +/** + \brief The PxTaskManager interface + + A PxTaskManager instance holds references to user-provided dispatcher objects, when tasks are + submitted the PxTaskManager routes them to the appropriate dispatcher and handles task profiling if enabled. + Users should not implement the PxTaskManager interface, the SDK creates its own concrete PxTaskManager object + per-scene which users can configure by passing dispatcher objects into the PxSceneDesc. + + @see CpuDispatcher + @see PxGpuDispatcher + +*/ +class PxTaskManager +{ +public: + + /** + \brief Set the user-provided dispatcher object for CPU tasks + + \param[in] ref The dispatcher object. + + @see CpuDispatcher + */ + virtual void setCpuDispatcher(PxCpuDispatcher& ref) = 0; + + /** + \brief Set the user-provided dispatcher object for GPU tasks + + \param[in] ref The dispatcher object. + + @see PxGpuDispatcher + */ + virtual void setGpuDispatcher(PxGpuDispatcher& ref) = 0; + + /** + \brief Get the user-provided dispatcher object for CPU tasks + + \return The CPU dispatcher object. + + @see CpuDispatcher + */ + virtual PxCpuDispatcher* getCpuDispatcher() const = 0; + + /** + \brief Get the user-provided dispatcher object for GPU tasks + + \return The GPU dispatcher object. + + @see PxGpuDispatcher + */ + virtual PxGpuDispatcher* getGpuDispatcher() const = 0; + + /** + \brief Reset any dependencies between Tasks + + \note Will be called at the start of every frame before tasks are submitted. + + @see PxTask + */ + virtual void resetDependencies() = 0; + + /** + \brief Called by the owning scene to start the task graph. + + \note All tasks with with ref count of 1 will be dispatched. + + @see PxTask + */ + virtual void startSimulation() = 0; + + /** + \brief Called by the owning scene at the end of a simulation step to synchronize the PxGpuDispatcher + + @see PxGpuDispatcher + */ + virtual void stopSimulation() = 0; + + /** + \brief Called by the worker threads to inform the PxTaskManager that a task has completed processing + + \param[in] task The task which has been completed + */ + virtual void taskCompleted(PxTask& task) = 0; + + /** + \brief Retrieve a task by name + + \param[in] name The unique name of a task + \return The ID of the task with that name, or TT_NOT_PRESENT if not found + */ + virtual PxTaskID getNamedTask(const char* name) = 0; + + /** + \brief Submit a task with a unique name. + + \param[in] task The task to be executed + \param[in] name The unique name of a task + \param[in] type The type of the task (default TT_CPU) + \return The ID of the task with that name, or TT_NOT_PRESENT if not found + */ + virtual PxTaskID submitNamedTask(PxTask* task, const char* name, PxTaskType::Enum type = PxTaskType::TT_CPU) = 0; + + /** + \brief Submit an unnamed task. + + \param[in] task The task to be executed + \param[in] type The type of the task (default TT_CPU) + + \return The ID of the task with that name, or TT_NOT_PRESENT if not found + */ + virtual PxTaskID submitUnnamedTask(PxTask& task, PxTaskType::Enum type = PxTaskType::TT_CPU) = 0; + + /** + \brief Retrieve a task given a task ID + + \param[in] id The ID of the task to return, a valid ID must be passed or results are undefined + + \return The task associated with the ID + */ + virtual PxTask* getTaskFromID(PxTaskID id) = 0; + + /** + \brief Release the PxTaskManager object, referenced dispatchers will not be released + */ + virtual void release() = 0; + + /** + \brief Construct a new PxTaskManager instance with the given [optional] dispatchers + */ + static PxTaskManager* createTaskManager(PxErrorCallback& errorCallback, PxCpuDispatcher* = 0, PxGpuDispatcher* = 0); + +protected: + virtual ~PxTaskManager() {} + + /*! \cond PRIVATE */ + + virtual void finishBefore(PxTask& task, PxTaskID taskID) = 0; + virtual void startAfter(PxTask& task, PxTaskID taskID) = 0; + + virtual void addReference(PxTaskID taskID) = 0; + virtual void decrReference(PxTaskID taskID) = 0; + virtual int32_t getReference(PxTaskID taskID) const = 0; + + virtual void decrReference(PxLightCpuTask&) = 0; + virtual void addReference(PxLightCpuTask&) = 0; + + /*! \endcond */ + + friend class PxBaseTask; + friend class PxTask; + friend class PxLightCpuTask; + friend class PxGpuWorkerThread; +}; + +PX_POP_PACK + +} // end physx namespace + + +#endif // PXTASK_PXTASKMANAGER_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleComponents.h b/src/PhysX/physx/include/vehicle/PxVehicleComponents.h new file mode 100644 index 000000000..3a85fcbd5 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleComponents.h @@ -0,0 +1,1359 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_CORE_COMPONENTS_H +#define PX_VEHICLE_CORE_COMPONENTS_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxMemory.h" +#include "foundation/PxVec3.h" +#include "common/PxCoreUtilityTypes.h" +#include "PxVehicleSDK.h" +#include "common/PxTypeInfo.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxVehicleChassisData +{ +public: + + friend class PxVehicleDriveSimData4W; + + PxVehicleChassisData() + : mMOI(PxVec3(0,0,0)), + mMass(1500), + mCMOffset(PxVec3(0,0,0)) + { + } + + /** + \brief Moment of inertia of vehicle rigid body actor. + + \note Specified in kilograms metres-squared (kg m^2). + */ + PxVec3 mMOI; + + /** + \brief Mass of vehicle rigid body actor. + + \note Specified in kilograms (kg). + */ + PxReal mMass; + + /** + \brief Center of mass offset of vehicle rigid body actor. + + \note Specified in metres (m). + */ + PxVec3 mCMOffset; + +private: + + PxReal pad; + + bool isValid() const; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleChassisData)& 0x0f)); + +class PxVehicleEngineData +{ +public: + + friend class PxVehicleDriveSimData; + + enum + { + eMAX_NB_ENGINE_TORQUE_CURVE_ENTRIES = 8 + }; + + PxVehicleEngineData() + : mMOI(1.0f), + mPeakTorque(500.0f), + mMaxOmega(600.0f), + mDampingRateFullThrottle(0.15f), + mDampingRateZeroThrottleClutchEngaged(2.0f), + mDampingRateZeroThrottleClutchDisengaged(0.35f) + { + mTorqueCurve.addPair(0.0f, 0.8f); + mTorqueCurve.addPair(0.33f, 1.0f); + mTorqueCurve.addPair(1.0f, 0.8f); + + mRecipMOI=1.0f/mMOI; + mRecipMaxOmega=1.0f/mMaxOmega; + } + + /** + \brief Graph of normalized torque (torque/mPeakTorque) against normalized engine speed ( engineRotationSpeed / mMaxOmega ). + + \note The normalized engine speed is the x-axis of the graph, while the normalized torque is the y-axis of the graph. + */ + PxFixedSizeLookupTable mTorqueCurve; + + /** + \brief Moment of inertia of the engine around the axis of rotation. + + \note Specified in kilograms metres-squared (kg m^2) + */ + PxReal mMOI; + + /** + \brief Maximum torque available to apply to the engine when the accelerator pedal is at maximum. + + \note The torque available is the value of the accelerator pedal (in range [0, 1]) multiplied by the normalized torque as computed from mTorqueCurve multiplied by mPeakTorque. + + \note Specified in kilograms metres-squared per second-squared (kg m^2 s^-2). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mPeakTorque; + + /** + \brief Maximum rotation speed of the engine. + + \note Specified in radians per second (s^-1). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mMaxOmega; + + /** + \brief Damping rate of engine when full throttle is applied. + + \note If the clutch is engaged (any gear except neutral) then the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchEngaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchEngaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchEngaged)*acceleratorPedal; + + \note If the clutch is disengaged (in neutral gear) the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchDisengaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchDisengaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchDisengaged)*acceleratorPedal; + + \note Specified in kilograms metres-squared per second (kg m^2 s^-1). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mDampingRateFullThrottle; + + + /** + \brief Damping rate of engine when full throttle is applied. + + \note If the clutch is engaged (any gear except neutral) then the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchEngaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchEngaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchEngaged)*acceleratorPedal; + + \note If the clutch is disengaged (in neutral gear) the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchDisengaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchDisengaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchDisengaged)*acceleratorPedal; + + \note Specified in kilograms metres-squared per second (kg m^2 s^-1). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mDampingRateZeroThrottleClutchEngaged; + + /** + \brief Damping rate of engine when full throttle is applied. + + \note If the clutch is engaged (any gear except neutral) then the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchEngaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchEngaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchEngaged)*acceleratorPedal; + + \note If the clutch is disengaged (in neutral gear) the damping rate applied at run-time is an interpolation + between mDampingRateZeroThrottleClutchDisengaged and mDampingRateFullThrottle: + mDampingRateZeroThrottleClutchDisengaged + (mDampingRateFullThrottle-mDampingRateZeroThrottleClutchDisengaged)*acceleratorPedal; + + \note Specified in kilograms metres-squared per second (kg m^2 s^-1). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mDampingRateZeroThrottleClutchDisengaged; + + /** + \brief Return value of mRecipMOI(=1.0f/mMOI) that is automatically set by PxVehicleDriveSimData::setEngineData + */ + PX_FORCE_INLINE PxReal getRecipMOI() const {return mRecipMOI;} + + /** + \brief Return value of mRecipMaxOmega( = 1.0f / mMaxOmega ) that is automatically set by PxVehicleDriveSimData::setEngineData + */ + PX_FORCE_INLINE PxReal getRecipMaxOmega() const {return mRecipMaxOmega;} + +private: + + /** + \brief Reciprocal of the engine moment of inertia. + + \note Not necessary to set this value because it is set by PxVehicleDriveSimData::setEngineData + + Range: [0, PX_MAX_F32)
            + */ + PxReal mRecipMOI; + + /** + \brief Reciprocal of the maximum rotation speed of the engine. + + \note Not necessary to set this value because it is set by PxVehicleDriveSimData::setEngineData + + Range: [0, PX_MAX_F32)
            + */ + PxReal mRecipMaxOmega; + + bool isValid() const; + + +//serialization +public: + PxVehicleEngineData(const PxEMPTY) : mTorqueCurve(PxEmpty) {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleEngineData)& 0x0f)); + +class PxVehicleGearsData +{ +public: + + friend class PxVehicleDriveSimData; + + enum Enum + { + eREVERSE=0, + eNEUTRAL, + eFIRST, + eSECOND, + eTHIRD, + eFOURTH, + eFIFTH, + eSIXTH, + eSEVENTH, + eEIGHTH, + eNINTH, + eTENTH, + eELEVENTH, + eTWELFTH, + eTHIRTEENTH, + eFOURTEENTH, + eFIFTEENTH, + eSIXTEENTH, + eSEVENTEENTH, + eEIGHTEENTH, + eNINETEENTH, + eTWENTIETH, + eTWENTYFIRST, + eTWENTYSECOND, + eTWENTYTHIRD, + eTWENTYFOURTH, + eTWENTYFIFTH, + eTWENTYSIXTH, + eTWENTYSEVENTH, + eTWENTYEIGHTH, + eTWENTYNINTH, + eTHIRTIETH, + eGEARSRATIO_COUNT + }; + + PxVehicleGearsData() + : mFinalRatio(4.0f), + mNbRatios(7), + mSwitchTime(0.5f) + { + mRatios[PxVehicleGearsData::eREVERSE]=-4.0f; + mRatios[PxVehicleGearsData::eNEUTRAL]=0.0f; + mRatios[PxVehicleGearsData::eFIRST]=4.0f; + mRatios[PxVehicleGearsData::eSECOND]=2.0f; + mRatios[PxVehicleGearsData::eTHIRD]=1.5f; + mRatios[PxVehicleGearsData::eFOURTH]=1.1f; + mRatios[PxVehicleGearsData::eFIFTH]=1.0f; + + for(PxU32 i = PxVehicleGearsData::eSIXTH; i < PxVehicleGearsData::eGEARSRATIO_COUNT; ++i) + mRatios[i]=0.f; + } + + /** + \brief Gear ratios + + Range: [0, PX_MAX_F32)
            + */ + PxReal mRatios[PxVehicleGearsData::eGEARSRATIO_COUNT]; + + /** + \brief Gear ratio applied is mRatios[currentGear]*finalRatio + + Range: [0, PX_MAX_F32)
            + */ + PxReal mFinalRatio; + + /** + \brief Number of gears (including reverse and neutral). + + Range: (0, MAX_NB_GEAR_RATIOS)
            + */ + PxU32 mNbRatios; + + /** + \brief Time it takes to switch gear. + + \note Specified in seconds (s). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mSwitchTime; + +private: + + PxReal mPad; + + bool isValid() const; + +//serialization +public: + PxVehicleGearsData(const PxEMPTY) {} + PxReal getGearRatio(PxVehicleGearsData::Enum a) const {return mRatios[a];} + void setGearRatio(PxVehicleGearsData::Enum a, PxReal ratio) { mRatios[a] = ratio;} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleGearsData)& 0x0f)); + +class PxVehicleAutoBoxData +{ +public: + + friend class PxVehicleDriveSimData; + + PxVehicleAutoBoxData() + { + for(PxU32 i=0;i mUpRatios[currentGear] the autobox will begin + a transition to currentGear+1 unless currentGear is the highest possible gear or neutral or reverse. + + Range: [0, 1]
            + */ + PxReal mUpRatios[PxVehicleGearsData::eGEARSRATIO_COUNT]; + + /** + \brief Value of engineRevs/maxEngineRevs that is low enough to decrement gear. + + \note When ( engineRotationSpeed / PxVehicleEngineData::mMaxOmega ) < mDownRatios[currentGear] the autobox will begin + a transition to currentGear-1 unless currentGear is first gear or neutral or reverse. + + Range: [0, 1]
            + */ + PxReal mDownRatios[PxVehicleGearsData::eGEARSRATIO_COUNT]; + + /** + \brief Set the latency time of the autobox. + + \note Latency time is the minimum time that must pass between each gear change that is initiated by the autobox. + The auto-box will only attempt to initiate another gear change up or down if the simulation time that has passed since the most recent + automated gear change is greater than the specified latency. + + \note Specified in seconds (s). + + @see getLatency + */ + void setLatency(const PxReal latency) + { + mDownRatios[PxVehicleGearsData::eREVERSE]=latency; + } + + /** + \brief Get the latency time of the autobox. + + \note Specified in seconds (s). + + @see setLatency + */ + PxReal getLatency() const + { + return mDownRatios[PxVehicleGearsData::eREVERSE]; + } + +private: + bool isValid() const; + +//serialization +public: + PxVehicleAutoBoxData(const PxEMPTY) {} + + PxReal getUpRatios(PxVehicleGearsData::Enum a) const {return mUpRatios[a];} + void setUpRatios(PxVehicleGearsData::Enum a, PxReal ratio) { mUpRatios[a] = ratio;} + + PxReal getDownRatios(PxVehicleGearsData::Enum a) const {return mDownRatios[a];} + void setDownRatios(PxVehicleGearsData::Enum a, PxReal ratio) { mDownRatios[a] = ratio;} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleAutoBoxData)& 0x0f)); + +class PxVehicleDifferential4WData +{ +public: + + friend class PxVehicleDriveSimData4W; + + enum Enum + { + eDIFF_TYPE_LS_4WD, //limited slip differential for car with 4 driven wheels + eDIFF_TYPE_LS_FRONTWD, //limited slip differential for car with front-wheel drive + eDIFF_TYPE_LS_REARWD, //limited slip differential for car with rear-wheel drive + eDIFF_TYPE_OPEN_4WD, //open differential for car with 4 driven wheels + eDIFF_TYPE_OPEN_FRONTWD, //open differential for car with front-wheel drive + eDIFF_TYPE_OPEN_REARWD, //open differential for car with rear-wheel drive + eMAX_NB_DIFF_TYPES + }; + + PxVehicleDifferential4WData() + : mFrontRearSplit(0.45f), + mFrontLeftRightSplit(0.5f), + mRearLeftRightSplit(0.5f), + mCentreBias(1.3f), + mFrontBias(1.3f), + mRearBias(1.3f), + mType(PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD) + { + } + + /** + \brief Ratio of torque split between front and rear (>0.5 means more to front, <0.5 means more to rear). + + \note Only applied to DIFF_TYPE_LS_4WD and eDIFF_TYPE_OPEN_4WD + + Range: [0, 1]
            + */ + PxReal mFrontRearSplit; + + /** + \brief Ratio of torque split between front-left and front-right (>0.5 means more to front-left, <0.5 means more to front-right). + + \note Only applied to DIFF_TYPE_LS_4WD and eDIFF_TYPE_OPEN_4WD and eDIFF_TYPE_LS_FRONTWD + + Range: [0, 1]
            + */ + PxReal mFrontLeftRightSplit; + + /** + \brief Ratio of torque split between rear-left and rear-right (>0.5 means more to rear-left, <0.5 means more to rear-right). + + \note Only applied to DIFF_TYPE_LS_4WD and eDIFF_TYPE_OPEN_4WD and eDIFF_TYPE_LS_REARWD + + Range: [0, 1]
            + */ + PxReal mRearLeftRightSplit; + + /** + \brief Maximum allowed ratio of average front wheel rotation speed and rear wheel rotation speeds + The differential will divert more torque to the slower wheels when the bias is exceeded. + + \note Only applied to DIFF_TYPE_LS_4WD + + Range: [1, PX_MAX_F32)
            + */ + PxReal mCentreBias; + + /** + \brief Maximum allowed ratio of front-left and front-right wheel rotation speeds. + The differential will divert more torque to the slower wheel when the bias is exceeded. + + \note Only applied to DIFF_TYPE_LS_4WD and DIFF_TYPE_LS_FRONTWD + + Range: [1, PX_MAX_F32)
            + */ + PxReal mFrontBias; + + /** + \brief Maximum allowed ratio of rear-left and rear-right wheel rotation speeds. + The differential will divert more torque to the slower wheel when the bias is exceeded. + + \note Only applied to DIFF_TYPE_LS_4WD and DIFF_TYPE_LS_REARWD + + Range: [1, PX_MAX_F32)
            + */ + PxReal mRearBias; + + /** + \brief Type of differential. + + Range: [DIFF_TYPE_LS_4WD, DIFF_TYPE_OPEN_FRONTWD]
            + */ + PxVehicleDifferential4WData::Enum mType; + +private: + + PxReal mPad[1]; + + bool isValid() const; + +//serialization +public: + PxVehicleDifferential4WData(const PxEMPTY) {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDifferential4WData)& 0x0f)); + +class PxVehicleDifferentialNWData +{ +public: + + friend class PxVehicleDriveSimDataNW; + friend class PxVehicleUpdate; + + PxVehicleDifferentialNWData() + { + PxMemSet(mBitmapBuffer, 0, sizeof(PxU32) * (((PX_MAX_NB_WHEELS + 31) & ~31) >> 5)); + mNbDrivenWheels=0; + mInvNbDrivenWheels=0.0f; + } + + /** + \brief Set a specific wheel to be driven or non-driven by the differential. + + \note The available drive torque will be split equally between all driven wheels. + Zero torque will be applied to non-driven wheels. + The default state of each wheel is to be uncoupled to the differential. + */ + void setDrivenWheel(const PxU32 wheelId, const bool drivenState); + + /** + \brief Test if a specific wheel has been configured as a driven or non-driven wheel. + */ + bool getIsDrivenWheel(const PxU32 wheelId) const; + +private: + + PxU32 mBitmapBuffer[((PX_MAX_NB_WHEELS + 31) & ~31) >> 5]; + PxU32 mNbDrivenWheels; + PxReal mInvNbDrivenWheels; + PxU32 mPad; + + bool isValid() const; + +//serialization +public: + PxVehicleDifferentialNWData(const PxEMPTY) {} + PxU32 getDrivenWheelStatus() const; + void setDrivenWheelStatus(PxU32 status); +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDifferentialNWData)& 0x0f)); + + +class PxVehicleAckermannGeometryData +{ +public: + + friend class PxVehicleDriveSimData4W; + + PxVehicleAckermannGeometryData() + : mAccuracy(1.0f), + mFrontWidth(0.0f), //Must be filled out + mRearWidth(0.0f), //Must be filled out + mAxleSeparation(0.0f) //Must be filled out + { + } + + /** + \brief Accuracy of Ackermann steer calculation. + + \note Accuracy with value 0.0 results in no Ackermann steer-correction, while + accuracy with value 1.0 results in perfect Ackermann steer-correction. + + \note Perfect Ackermann steer correction modifies the steer angles applied to the front-left and + front-right wheels so that the perpendiculars to the wheels' longitudinal directions cross the + extended vector of the rear axle at the same point. It is also applied to any steer angle applied + to the rear wheels but instead using the extended vector of the front axle. + + \note In general, more steer correction produces better cornering behavior. + + Range: [0, 1]
            + */ + PxReal mAccuracy; + + /** + \brief Distance between center-point of the two front wheels. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mFrontWidth; + + /** + \brief Distance between center-point of the two rear wheels. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mRearWidth; + + /** + \brief Distance between center of front axle and center of rear axle. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mAxleSeparation; + +private: + + bool isValid() const; + +//serialization +public: + PxVehicleAckermannGeometryData(const PxEMPTY) {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleAckermannGeometryData)& 0x0f)); + +/** +\brief Choose between a potentially more expensive but more accurate solution to the clutch model or a potentially cheaper but less accurate solution. +@see PxVehicleClutchData +*/ +struct PxVehicleClutchAccuracyMode +{ + enum Enum + { + eESTIMATE = 0, + eBEST_POSSIBLE + }; +}; + +class PxVehicleClutchData +{ +public: + + friend class PxVehicleDriveSimData; + + PxVehicleClutchData() + : mStrength(10.0f), + mAccuracyMode(PxVehicleClutchAccuracyMode::eBEST_POSSIBLE), + mEstimateIterations(5) + { + } + + /** + \brief Strength of clutch. + + \note The clutch is the mechanism that couples the engine to the wheels. + A stronger clutch more strongly couples the engine to the wheels, while a + clutch of strength zero completely decouples the engine from the wheels. + Stronger clutches more quickly bring the wheels and engine into equilibrium, while weaker + clutches take longer, resulting in periods of clutch slip and delays in power transmission + from the engine to the wheels. + The torque generated by the clutch is proportional to the clutch strength and + the velocity difference between the engine's rotational speed and the rotational speed of the + driven wheels after accounting for the gear ratio. + The torque at the clutch is applied negatively to the engine and positively to the driven wheels. + + \note Specified in kilograms metres-squared per second (kg m^2 s^-1) + + Range: [0,PX_MAX_F32)
            + */ + PxReal mStrength; + + /** + \brief The engine and wheel rotation speeds that are coupled through the clutch can be updated by choosing + one of two modes: eESTIMATE and eBEST_POSSIBLE. + + \note If eESTIMATE is chosen the vehicle sdk will update the wheel and engine rotation speeds + with estimated values to the implemented clutch model. + + \note If eBEST_POSSIBLE is chosen the vehicle sdk will compute the best possible + solution (within floating point tolerance) to the implemented clutch model. + This is the recommended mode. + + \note The clutch model remains the same if either eESTIMATE or eBEST_POSSIBLE is chosen but the accuracy and + computational cost of the solution to the model can be tuned as required. + */ + PxVehicleClutchAccuracyMode::Enum mAccuracyMode; + + /** + \brief Tune the mathematical accuracy and computational cost of the computed estimate to the wheel and + engine rotation speeds if eESTIMATE is chosen. + + \note As mEstimateIterations increases the computational cost of the clutch also increases and the solution + approaches the solution that would be computed if eBEST_POSSIBLE was chosen instead. + + \note This has no effect if eBEST_POSSIBLE is chosen as the accuracy mode. + + \note A value of zero is not allowed if eESTIMATE is chosen as the accuracy mode. + */ + PxU32 mEstimateIterations; + +private: + + PxU8 mPad[4]; + + bool isValid() const; + +//serialization +public: + PxVehicleClutchData(const PxEMPTY) {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleClutchData)& 0x0f)); + + +/** +\brief Tire load variation can be strongly dependent on the time-step so it is a good idea to filter it +to give less jerky handling behavior. + +\note The x-axis of the graph is normalized tire load, while the y-axis is the filtered normalized tire load. + +\note The normalized load is the force acting downwards on the tire divided by the force experienced by the tire when the car is at rest on the ground. + +\note The rest load is approximately the product of the value of gravitational acceleration and PxVehicleSuspensionData::mSprungMass. + +\note The minimum possible normalized load is zero. + +\note There are two points on the graph: (mMinNormalisedLoad, mMinNormalisedFilteredLoad) and (mMaxNormalisedLoad, mMaxFilteredNormalisedLoad). + +\note Normalized loads less than mMinNormalisedLoad have filtered normalized load = mMinNormalisedFilteredLoad. + +\note Normalized loads greater than mMaxNormalisedLoad have filtered normalized load = mMaxFilteredNormalisedLoad. + +\note Normalized loads in-between are linearly interpolated between mMinNormalisedFilteredLoad and mMaxFilteredNormalisedLoad. + +\note The tire load applied as input to the tire force computation is the filtered normalized load multiplied by the rest load. +*/ +class PxVehicleTireLoadFilterData +{ +public: + + friend class PxVehicleWheelsSimData; + + PxVehicleTireLoadFilterData() + : mMinNormalisedLoad(0), + mMinFilteredNormalisedLoad(0.2308f), + mMaxNormalisedLoad(3.0f), + mMaxFilteredNormalisedLoad(3.0f) + { + mDenominator=1.0f/(mMaxNormalisedLoad - mMinNormalisedLoad); + } + + /** + \brief Graph point (mMinNormalisedLoad,mMinFilteredNormalisedLoad) + */ + PxReal mMinNormalisedLoad; + + /** + \brief Graph point (mMinNormalisedLoad,mMinFilteredNormalisedLoad) + */ + PxReal mMinFilteredNormalisedLoad; + + /** + \brief Graph point (mMaxNormalisedLoad,mMaxFilteredNormalisedLoad) + */ + PxReal mMaxNormalisedLoad; + + /** + \brief Graph point (mMaxNormalisedLoad,mMaxFilteredNormalisedLoad) + */ + PxReal mMaxFilteredNormalisedLoad; + + PX_FORCE_INLINE PxReal getDenominator() const {return mDenominator;} + +private: + + /** + \brief Not necessary to set this value. + */ + //1.0f/(mMaxNormalisedLoad-mMinNormalisedLoad) for quick calculations + PxReal mDenominator; + + PxU32 mPad[3]; + + bool isValid() const; + +//serialization +public: + PxVehicleTireLoadFilterData(const PxEMPTY) {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleTireLoadFilterData)& 0x0f)); + +class PxVehicleWheelData +{ +public: + + friend class PxVehicleWheels4SimData; + + PxVehicleWheelData() + : mRadius(0.0f), //Must be filled out + mWidth(0.0f), + mMass(20.0f), + mMOI(0.0f), //Must be filled out + mDampingRate(0.25f), + mMaxBrakeTorque(1500.0f), + mMaxHandBrakeTorque(0.0f), + mMaxSteer(0.0f), + mToeAngle(0.0f), + mRecipRadius(0.0f), //Must be filled out + mRecipMOI(0.0f) //Must be filled out + { + } + + /** + \brief Radius of unit that includes metal wheel plus rubber tire. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mRadius; + + /** + \brief Maximum width of unit that includes wheel plus tire. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mWidth; + + /** + \brief Mass of unit that includes wheel plus tire. + + \note Specified in kilograms (kg). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mMass; + + /** + \brief Moment of inertia of unit that includes wheel plus tire about the rolling axis. + + \note Specified in kilograms metres-squared (kg m^2). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mMOI; + + /** + \brief Damping rate applied to wheel. + + \note Specified in kilograms metres-squared per second (kg m^2 s^-1). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mDampingRate; + + /** + \brief Max brake torque that can be applied to wheel. + + \note Specified in kilograms metres-squared per second-squared (kg m^2 s^-2) + + Range: [0, PX_MAX_F32)
            + */ + PxReal mMaxBrakeTorque; + + /** + \brief Max handbrake torque that can be applied to wheel. + + \note Specified in kilograms metres-squared per second-squared (kg m^2 s^-2) + + Range: [0, PX_MAX_F32)
            + */ + PxReal mMaxHandBrakeTorque; + + /** + \brief Max steer angle that can be achieved by the wheel. + + \note Specified in radians. + + Range: [0, PX_MAX_F32)
            + */ + PxReal mMaxSteer; + + /** + \brief Wheel toe angle. This value is ignored by PxVehicleDriveTank and PxVehicleNoDrive. + + \note Specified in radians. + + Range: [0, Pi/2]
            + */ + PxReal mToeAngle;//in radians + + /** + \brief Return value equal to 1.0f/mRadius + + @see PxVehicleWheelsSimData::setWheelData + */ + PX_FORCE_INLINE PxReal getRecipRadius() const {return mRecipRadius;} + + /** + \brief Return value equal to 1.0f/mRecipMOI + + @see PxVehicleWheelsSimData::setWheelData + */ + PX_FORCE_INLINE PxReal getRecipMOI() const {return mRecipMOI;} + +private: + + /** + \brief Reciprocal of radius of unit that includes metal wheel plus rubber tire. + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setWheelData + + Range: [0, PX_MAX_F32)
            + */ + PxReal mRecipRadius; + + /** + \brief Reciprocal of moment of inertia of unit that includes wheel plus tire about single allowed axis of rotation. + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setWheelData + + Range: [0, PX_MAX_F32)
            + */ + PxReal mRecipMOI; + + PxReal mPad[1]; + + bool isValid() const; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleWheelData)& 0x0f)); + +class PxVehicleSuspensionData +{ +public: + + friend class PxVehicleWheels4SimData; + + PxVehicleSuspensionData() + : mSpringStrength(0.0f), + mSpringDamperRate(0.0f), + mMaxCompression(0.3f), + mMaxDroop(0.1f), + mSprungMass(0.0f), + mCamberAtRest(0.0f), + mCamberAtMaxCompression(0.0f), + mCamberAtMaxDroop(0.0f), + mRecipMaxCompression(1.0f), + mRecipMaxDroop(1.0f) + { + } + + /** + \brief Spring strength of suspension unit. + + \note Specified in kilograms per second-squared (kg s^-2). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mSpringStrength; + + /** + \brief Spring damper rate of suspension unit. + + \note Specified in kilograms per second (kg s^-1). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mSpringDamperRate; + + /** + \brief Maximum compression allowed by suspension spring. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mMaxCompression; + + /** + \brief Maximum elongation allowed by suspension spring. + + \note Specified in metres (m). + + Range: [0, PX_MAX_F32)
            + */ + PxReal mMaxDroop; + + /** + \brief Mass of vehicle that is supported by suspension spring. + + \note Specified in kilograms (kg). + + \note Each suspension is guaranteed to generate an upwards force of |gravity|*mSprungMass along the suspension direction when the wheel is perfectly + at rest and sitting at the rest pose defined by the wheel centre offset. + + \note The sum of the sprung masses of all suspensions of a vehicle should match the mass of the PxRigidDynamic associated with the vehicle. + When this condition is satisfied for a vehicle on a horizontal plane the wheels of the vehicle are guaranteed to sit at the rest pose + defined by the wheel centre offset. The mass matching condition is not enforced. + + \note As the wheel compresses or elongates along the suspension direction the force generated by the spring is + F = |gravity|*mSprungMass + deltaX*mSpringStrength + deltaXDot*mSpringDamperRate + where deltaX is the deviation from the defined rest pose and deltaXDot is the velocity of the sprung mass along the suspension direction. + In practice, deltaXDot is computed by comparing the current and previous deviation from the rest pose and dividing the difference + by the simulation timestep. + + \note If a single suspension spring is hanging in the air and generates zero force the remaining springs of the vehicle will necessarily + sit in a compressed configuration. In summary, the sum of the remaining suspension forces cannot balance the downwards gravitational force + acting on the vehicle without extra force arising from the deltaX*mSpringStrength force term. + + \note Theoretically, a suspension spring should generate zero force at maximum elongation and increase linearly as the suspension approaches the rest pose. + PxVehicleSuspensionData will only enforce this physical law if the spring is configured so that |gravity|*mSprungMass == mMaxDroop*mSpringStrength. + To help decouple vehicle handling from visual wheel positioning this condition is not enforced. + In practice, the value of |gravity|*mSprungMass + deltaX*mSpringStrength is clamped at zero to ensure it never falls negative. + + @see PxVehicleComputeSprungMasses, PxVehicleWheelsSimData::setWheelCentreOffset, PxVehicleSuspensionData::mSpringStrength, PxVehicleSuspensionData::mSpringDamperRate, PxVehicleSuspensionData::mMaxDroop + + Range: [0, PX_MAX_F32)
            + */ + PxReal mSprungMass; + + /** + \brief Camber angle (in radians) of wheel when the suspension is at its rest position. + + \note Specified in radians. + + Range: [-pi/2, pi/2]
            + + */ + PxReal mCamberAtRest; + + /** + \brief Camber angle (in radians) of wheel when the suspension is at maximum compression. + + \note For compressed suspensions the camber angle is a linear interpolation of + mCamberAngleAtRest and mCamberAtMaxCompression + + \note Specified in radians. + + Range: [-pi/2, pi/2]
            + */ + PxReal mCamberAtMaxCompression; + + /** + \brief Camber angle (in radians) of wheel when the suspension is at maximum droop. + + \note For extended suspensions the camber angle is linearly interpolation of + mCamberAngleAtRest and mCamberAtMaxDroop + + \note Specified in radians. + + Range: [-pi/2, pi/2]
            + */ + PxReal mCamberAtMaxDroop; + + /** + \brief Reciprocal of maximum compression. + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setSuspensionData + + Range: [0, PX_MAX_F32)
            + */ + PX_FORCE_INLINE PxReal getRecipMaxCompression() const {return mRecipMaxCompression;} + + /** + \brief Reciprocal of maximum droop. + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setSuspensionData + + Range: [0, PX_MAX_F32)
            + */ + PX_FORCE_INLINE PxReal getRecipMaxDroop() const {return mRecipMaxDroop;} + + /** + \brief Set a new sprung mass for the suspension and modify the spring strength so that the natural frequency + of the spring is preserved. + \param[in] newSprungMass is the new mass that the suspension spring will support. + */ + void setMassAndPreserveNaturalFrequency(const PxReal newSprungMass) + { + const PxF32 oldStrength = mSpringStrength; + const PxF32 oldSprungMass = mSprungMass; + const PxF32 newStrength = oldStrength * (newSprungMass / oldSprungMass); + mSpringStrength = newStrength; + mSprungMass = newSprungMass; + } + +private: + + /** + \brief Cached value of 1.0f/mMaxCompression + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setSuspensionData + */ + PxReal mRecipMaxCompression; + + /** + \brief Cached value of 1.0f/mMaxDroop + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setSuspensionData + */ + PxReal mRecipMaxDroop; + + //padding + PxReal mPad[2]; + + bool isValid() const; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleSuspensionData)& 0x0f)); + +class PxVehicleAntiRollBarData +{ +public: + + friend class PxVehicleWheelsSimData; + + PxVehicleAntiRollBarData() + : mWheel0(0xffffffff), + mWheel1(0xffffffff), + mStiffness(0.0f) + { + } + + /* + \brief The anti-roll bar connects two wheels with indices mWheel0 and mWheel1 + */ + PxU32 mWheel0; + + /* + \brief The anti-roll bar connects two wheels with indices mWheel0 and mWheel1 + */ + PxU32 mWheel1; + + /* + \brief The stiffness of the anti-roll bar. + + \note Specified in kilograms per second-squared (kg s^-2). + + Range: [0, PX_MAX_F32)
            + */ + PxF32 mStiffness; + +private: + + PxF32 mPad[1]; + + bool isValid() const; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleAntiRollBarData)& 0x0f)); + +class PxVehicleTireData +{ +public: + friend class PxVehicleWheels4SimData; + + PxVehicleTireData() + : mLatStiffX(2.0f), + mLatStiffY(0.3125f*(180.0f / PxPi)), + mLongitudinalStiffnessPerUnitGravity(1000.0f), + mCamberStiffnessPerUnitGravity(0.1f*(180.0f / PxPi)), + mType(0) + { + mFrictionVsSlipGraph[0][0]=0.0f; + mFrictionVsSlipGraph[0][1]=1.0f; + mFrictionVsSlipGraph[1][0]=0.1f; + mFrictionVsSlipGraph[1][1]=1.0f; + mFrictionVsSlipGraph[2][0]=1.0f; + mFrictionVsSlipGraph[2][1]=1.0f; + + mRecipLongitudinalStiffnessPerUnitGravity=1.0f/mLongitudinalStiffnessPerUnitGravity; + + mFrictionVsSlipGraphRecipx1Minusx0=1.0f/(mFrictionVsSlipGraph[1][0]-mFrictionVsSlipGraph[0][0]); + mFrictionVsSlipGraphRecipx2Minusx1=1.0f/(mFrictionVsSlipGraph[2][0]-mFrictionVsSlipGraph[1][0]); + } + + /** + \brief Tire lateral stiffness is a graph of tire load that has linear behavior near zero load and + flattens at large loads. mLatStiffX describes the minimum normalized load (load/restLoad) that gives a + flat lateral stiffness response to load. + + Range: [0, PX_MAX_F32)
            + */ + PxReal mLatStiffX; + + /** + \brief Tire lateral stiffness is a graph of tire load that has linear behavior near zero load and + flattens at large loads. mLatStiffY describes the maximum possible value of lateralStiffness/restLoad that occurs + when (load/restLoad)>= mLatStiffX. + + \note If load/restLoad is greater than mLatStiffX then the lateral stiffness is mLatStiffY*restLoad. + + \note If load/restLoad is less than mLatStiffX then the lateral stiffness is mLastStiffY*(load/mLatStiffX) + + \note Lateral force can be approximated as lateralStiffness * lateralSlip. + + \note Specified in per radian. + + Range: [0, PX_MAX_F32)
            + */ + PxReal mLatStiffY; + + /** + \brief Tire Longitudinal stiffness per unit gravitational acceleration. + + \note Longitudinal stiffness of the tire is calculated as gravitationalAcceleration*mLongitudinalStiffnessPerUnitGravity. + + \note Longitudinal force can be approximated as gravitationalAcceleration*mLongitudinalStiffnessPerUnitGravity*longitudinalSlip. + + \note Specified in kilograms per radian. + + Range: [0, PX_MAX_F32)
            + */ + PxReal mLongitudinalStiffnessPerUnitGravity; + + /** + \brief tire Tire camber stiffness per unity gravitational acceleration. + + \note Camber stiffness of the tire is calculated as gravitationalAcceleration*mCamberStiffnessPerUnitGravity + + \note Camber force can be approximated as gravitationalAcceleration*mCamberStiffnessPerUnitGravity*camberAngle. + + \note Specified in kilograms per radian. + + Range: [0, PX_MAX_F32)
            + */ + PxReal mCamberStiffnessPerUnitGravity; + + /** + \brief Graph of friction vs longitudinal slip with 3 points. + + \note mFrictionVsSlipGraph[0][0] is always zero. + + \note mFrictionVsSlipGraph[0][1] is the friction available at zero longitudinal slip. + + \note mFrictionVsSlipGraph[1][0] is the value of longitudinal slip with maximum friction. + + \note mFrictionVsSlipGraph[1][1] is the maximum friction. + + \note mFrictionVsSlipGraph[2][0] is the end point of the graph. + + \note mFrictionVsSlipGraph[2][1] is the value of friction for slips greater than mFrictionVsSlipGraph[2][0]. + + \note The friction value computed from the friction vs longitudinal slip graph is used to scale the friction + value for the combination of material and tire type (PxVehicleDrivableSurfaceToTireFrictionPairs). + + \note mFrictionVsSlipGraph[2][0] > mFrictionVsSlipGraph[1][0] > mFrictionVsSlipGraph[0][0] + + \note mFrictionVsSlipGraph[1][1] is typically greater than mFrictionVsSlipGraph[0][1] + + \note mFrictionVsSlipGraph[2][1] is typically smaller than mFrictionVsSlipGraph[1][1] + + \note longitudinal slips > mFrictionVsSlipGraph[2][0] use friction multiplier mFrictionVsSlipGraph[2][1] + + \note The final friction value used by the tire model is the value returned by PxVehicleDrivableSurfaceToTireFrictionPairs + multiplied by the value computed from mFrictionVsSlipGraph. + + @see PxVehicleDrivableSurfaceToTireFrictionPairs, PxVehicleComputeTireForce + + Range: [0, PX_MAX_F32)
            + */ + PxReal mFrictionVsSlipGraph[3][2]; + + /** + \brief Tire type denoting slicks, wets, snow, winter, summer, all-terrain, mud etc. + + @see PxVehicleDrivableSurfaceToTireFrictionPairs + + Range: [0, PX_MAX_F32)
            + */ + PxU32 mType; + + /** + \brief Return Cached value of 1.0/mLongitudinalStiffnessPerUnitGravity + + @see PxVehicleWheelsSimData::setTireData + */ + PX_FORCE_INLINE PxReal getRecipLongitudinalStiffnessPerUnitGravity() const {return mRecipLongitudinalStiffnessPerUnitGravity;} + + /** + \brief Return Cached value of 1.0f/(mFrictionVsSlipGraph[1][0]-mFrictionVsSlipGraph[0][0]) + + @see PxVehicleWheelsSimData::setTireData + */ + PX_FORCE_INLINE PxReal getFrictionVsSlipGraphRecipx1Minusx0() const {return mFrictionVsSlipGraphRecipx1Minusx0;} + + /** + \brief Return Cached value of 1.0f/(mFrictionVsSlipGraph[2][0]-mFrictionVsSlipGraph[1][0]) + + @see PxVehicleWheelsSimData::setTireData + */ + PX_FORCE_INLINE PxReal getFrictionVsSlipGraphRecipx2Minusx1() const {return mFrictionVsSlipGraphRecipx2Minusx1;} + +private: + + /** + \brief Cached value of 1.0/mLongitudinalStiffnessPerUnitGravity. + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setTireData + + @see PxVehicleWheelsSimData::setTireData + */ + PxReal mRecipLongitudinalStiffnessPerUnitGravity; + + /** + \brief Cached value of 1.0f/(mFrictionVsSlipGraph[1][0]-mFrictionVsSlipGraph[0][0]) + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setTireData + + @see PxVehicleWheelsSimData::setTireData + */ + PxReal mFrictionVsSlipGraphRecipx1Minusx0; + + /** + \brief Cached value of 1.0f/(mFrictionVsSlipGraph[2][0]-mFrictionVsSlipGraph[1][0]) + + \note Not necessary to set this value because it is set by PxVehicleWheelsSimData::setTireData + + @see PxVehicleWheelsSimData::setTireData + */ + PxReal mFrictionVsSlipGraphRecipx2Minusx1; + + PxReal mPad[2]; + + bool isValid() const; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleTireData)& 0x0f)); +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_CORE_COMPONENTS_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDrive.h b/src/PhysX/physx/include/vehicle/PxVehicleDrive.h new file mode 100644 index 000000000..6358c7f84 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleDrive.h @@ -0,0 +1,566 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_DRIVE_H +#define PX_VEHICLE_DRIVE_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleComponents.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxFilterData; +class PxGeometry; +class PxPhysics; +class PxBatchQuery; +class PxVehicleDrivableSurfaceToTireFrictionPairs; +class PxShape; +class PxMaterial; +class PxRigidDynamic; + +/** +\brief Data structure describing non-wheel configuration data of a vehicle that has engine, gears, clutch, and auto-box. +@see PxVehicleWheelsSimData for wheels configuration data. +*/ +class PxVehicleDriveSimData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleDriveTank; + + /** + \brief Return the engine data + */ + PX_FORCE_INLINE const PxVehicleEngineData& getEngineData() const + { + return mEngine; + } + + /** + \brief Set the engine data + \param[in] engine - the data stored in engine is copied to the vehicle's engine. + */ + void setEngineData(const PxVehicleEngineData& engine); + + /** + \brief Return the gears data + */ + PX_FORCE_INLINE const PxVehicleGearsData& getGearsData() const + { + return mGears; + } + + /** + \brief Set the gears data + \param[in] gears - the data stored in gears is copied to the vehicle's gears. + */ + void setGearsData(const PxVehicleGearsData& gears); + + /** + \brief Return the clutch data + */ + PX_FORCE_INLINE const PxVehicleClutchData& getClutchData() const + { + return mClutch; + } + + /** + \brief Set the clutch data + \param[in] clutch - the data stored in clutch is copied to the vehicle's clutch. + */ + void setClutchData(const PxVehicleClutchData& clutch); + + /** + \brief Return the autobox data + */ + PX_FORCE_INLINE const PxVehicleAutoBoxData& getAutoBoxData() const + { + return mAutoBox; + } + + /** + \brief Set the autobox data + \param[in] autobox - the data stored in autobox is copied to the vehicle's autobox. + */ + void setAutoBoxData(const PxVehicleAutoBoxData& autobox); + +protected: + /* + \brief Engine simulation data + @see setEngineData, getEngineData + */ + PxVehicleEngineData mEngine; + + /* + \brief Gear simulation data + @see setGearsData, getGearsData + */ + PxVehicleGearsData mGears; + + /* + \brief Clutch simulation data + @see setClutchData, getClutchData + */ + PxVehicleClutchData mClutch; + + /* + \brief Autobox simulation data + @see setAutoboxData, getAutoboxData + */ + PxVehicleAutoBoxData mAutoBox; + + /** + \brief Test that a PxVehicleDriveSimData instance has been configured with legal data. + Call only after setting all components with setEngineData,setGearsData,setClutchData,setAutoBoxData + @see PxVehicleDrive4W::setup, PxVehicleDriveTank::setup + */ + bool isValid() const; + + +//serialization +public: + PxVehicleDriveSimData() {} + PxVehicleDriveSimData(const PxEMPTY) : mEngine(PxEmpty), mGears(PxEmpty), mClutch(PxEmpty), mAutoBox(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveSimData) & 15)); + + +/** +\brief Data structure with instanced dynamics data for vehicle with engine, clutch, gears, autobox +@see PxVehicleWheelsDynData for wheels dynamics data. +*/ +class PxVehicleDriveDynData +{ +public: + + enum + { + eMAX_NB_ANALOG_INPUTS=16 + }; + + friend class PxVehicleDrive; + + /** + \brief Set all dynamics data to zero to bring the vehicle to rest. + */ + void setToRestState(); + + /** + \brief Set an analog control value to drive the vehicle. + \param[in] type describes the type of analog control being modified + \param[in] analogVal is the new value of the specific analog control. + @see PxVehicleDrive4WControl, PxVehicleDriveNWControl, PxVehicleDriveTankControl + */ + void setAnalogInput(const PxU32 type, const PxReal analogVal); + + /** + \brief Get the analog control value that has been applied to the vehicle. + \return The value of the specified analog control value. + @see PxVehicleDrive4WControl, PxVehicleDriveNWControl, PxVehicleDriveTankControl + */ + PxReal getAnalogInput(const PxU32 type) const; + + /** + \brief Inform the vehicle that the gear-up button has been pressed. + + \param[in] digitalVal is the state of the gear-up button. + + \note If digitalVal is true the vehicle will attempt to initiate a gear change at the next call to PxVehicleUpdates. + + \note The value of mGearUpPressed is not reset by PxVehicleUpdates + */ + void setGearUp(const bool digitalVal) + { + mGearUpPressed = digitalVal; + } + + /** + \brief Set that the gear-down button has been pressed. + + \param[in] digitalVal is the state of the gear-down button. + + \note If digitalVal is true the vehicle will attempt to initiate a gear change at the next call to PxVehicleUpdates. + + \note The value of mGearDownPressed is not reset by PxVehicleUpdates + */ + void setGearDown(const bool digitalVal) + { + mGearDownPressed = digitalVal; + } + + /** + \brief Check if the gear-up button has been pressed + \return The state of the gear-up button. + */ + bool getGearUp() const + { + return mGearUpPressed; + } + + /** + \brief Check if the gear-down button has been pressed + \return The state of the gear-down button. + */ + bool getGearDown() const + { + return mGearDownPressed; + } + + /** + \brief Set the flag that will be used to select auto-gears + If useAutoGears is true the auto-box will be active. + \param[in] useAutoGears is the active state of the auto-box. + */ + PX_FORCE_INLINE void setUseAutoGears(const bool useAutoGears) + { + mUseAutoGears=useAutoGears; + } + + /** + \brief Get the flag status that is used to select auto-gears + \return The active status of the auto-box. + */ + PX_FORCE_INLINE bool getUseAutoGears() const + { + return mUseAutoGears; + } + + /** + \brief Toggle the auto-gears flag + If useAutoGears is true the auto-box will be active. + */ + PX_FORCE_INLINE void toggleAutoGears() + { + mUseAutoGears = !mUseAutoGears; + } + + /** + \brief Set the current gear. + + \param[in] currentGear is the vehicle's gear. + + \note If the target gear is different from the current gear the vehicle will + attempt to start a gear change from the current gear that has just been set + towards the target gear at the next call to PxVehicleUpdates. + + @see setTargetGear, PxVehicleGearsData + */ + PX_FORCE_INLINE void setCurrentGear(PxU32 currentGear) + { + mCurrentGear = currentGear; + } + + /** + \brief Get the current gear. + + \return The vehicle's current gear. + + @see getTargetGear, PxVehicleGearsData + */ + PX_FORCE_INLINE PxU32 getCurrentGear() const + { + return mCurrentGear; + } + + /** + \brief Set the target gear. + + \param[in] targetGear is the vehicle's target gear. + + \note If the target gear is different from the current gear the vehicle will + attempt to start a gear change towards the target gear at the next call to + PxVehicleUpdates. + + @see PxVehicleGearsData + */ + PX_FORCE_INLINE void setTargetGear(PxU32 targetGear) + { + mTargetGear = targetGear; + } + + /** + \brief Get the target gear. + + \return The vehicle's target gear. + + @see setTargetGear, PxVehicleGearsData + */ + PX_FORCE_INLINE PxU32 getTargetGear() const + { + return mTargetGear; + } + + /** + \brief Start a gear change to a target gear. + + \param[in] targetGear is the gear the vehicle will begin a transition towards. + + \note The gear change will begin at the next call to PxVehicleUpadates. + + @see PxVehicleGearsData + */ + PX_FORCE_INLINE void startGearChange(const PxU32 targetGear) + { + mTargetGear=targetGear; + } + + /** + \brief Force an immediate gear change to a target gear + + \param[in] targetGear is the gear the vehicle will be given immediately. + + @see PxVehicleGearsData + */ + PX_FORCE_INLINE void forceGearChange(const PxU32 targetGear) + { + mTargetGear=targetGear; + mCurrentGear=targetGear; + } + + /** + \brief Set the rotation speed of the engine (radians per second) + + \param[in] speed is the rotational speed (radians per second) to apply to the engine. + */ + PX_FORCE_INLINE void setEngineRotationSpeed(const PxF32 speed) + { + mEnginespeed = speed; + } + + /** + \brief Return the rotation speed of the engine (radians per second) + + \return The rotational speed (radians per second) of the engine. + */ + PX_FORCE_INLINE PxReal getEngineRotationSpeed() const + { + return mEnginespeed; + } + + /** + \brief Return the time that has passed since the current gear change was initiated. + + \return The time that has passed since the current gear change was initiated. + + \note If no gear change is in process the gear switch time will be zero. + + @see PxVehicleGearsData.mSwitchTime + */ + PX_FORCE_INLINE PxReal getGearSwitchTime() const + { + return mGearSwitchTime; + } + + /** + \brief Return the time that has passed since the autobox last initiated a gear change. + + \return The time that has passed since the autobox last initiated a gear change. + + @see PxVehicleAutoBoxData::setLatency, PxVehicleAutoBoxData::getLatency + */ + PX_FORCE_INLINE PxReal getAutoBoxSwitchTime() const + { + return mAutoBoxSwitchTime; + } + + /** + \brief All dynamic data values are public for fast access. + */ + + + /** + \brief Analog control values used by vehicle simulation. + @see setAnalogInput, getAnalogInput, PxVehicleDrive4WControl, PxVehicleDriveNWControl, PxVehicleDriveTankControl + */ + PxReal mControlAnalogVals[eMAX_NB_ANALOG_INPUTS]; + + /** + \brief Auto-gear flag used by vehicle simulation. Set true to enable the autobox, false to disable the autobox. + @see setUseAutoGears, setUseAutoGears, toggleAutoGears, PxVehicleAutoBoxData + */ + bool mUseAutoGears; + + /** + \brief Gear-up digital control value used by vehicle simulation. + + \note If true a gear change will be initiated towards currentGear+1 (or to first gear if in reverse). + + @see setDigitalInput, getDigitalInput + */ + bool mGearUpPressed; + + /** + \brief Gear-down digital control value used by vehicle simulation. + + \note If true a gear change will be initiated towards currentGear-1 (or to reverse if in first). + + @see setDigitalInput, getDigitalInput + */ + bool mGearDownPressed; + + /** + \brief Current gear + @see startGearChange, forceGearChange, getCurrentGear, PxVehicleGearsData + */ + PxU32 mCurrentGear; + + /** + \brief Target gear (different from current gear if a gear change is underway) + @see startGearChange, forceGearChange, getTargetGear, PxVehicleGearsData + */ + PxU32 mTargetGear; + + /** + \brief Rotation speed of engine + @see setToRestState, getEngineRotationSpeed + */ + PxReal mEnginespeed; + + /** + \brief Reported time that has passed since gear change started. + @see setToRestState, startGearChange, PxVehicleGearsData::mSwitchTime + */ + PxReal mGearSwitchTime; + + /** + \brief Reported time that has passed since last autobox gearup/geardown decision. + @see setToRestState, PxVehicleAutoBoxData::setLatency + */ + PxReal mAutoBoxSwitchTime; + +private: + PxU32 mPad[2]; + + /** + \brief Test that a PxVehicleDriveDynData instance has legal values. + @see setToRestState + */ + bool isValid() const; + +//serialization +public: + PxVehicleDriveDynData(); + PxVehicleDriveDynData(const PxEMPTY) {} + PxU32 getNbAnalogInput() const { return eMAX_NB_ANALOG_INPUTS; } + PX_FORCE_INLINE void setGearChange(const PxU32 gearChange) { mTargetGear= gearChange; } + PX_FORCE_INLINE PxU32 getGearChange() const { return mTargetGear; } + PX_FORCE_INLINE void setGearSwitchTime(const PxReal switchTime) { mGearSwitchTime = switchTime; } + PX_FORCE_INLINE void setAutoBoxSwitchTime(const PxReal autoBoxSwitchTime) { mAutoBoxSwitchTime = autoBoxSwitchTime; } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveDynData) & 15)); + +/** +\brief A complete vehicle with instance dynamics data and configuration data for wheels and engine,clutch,gears,autobox. +@see PxVehicleDrive4W, PxVehicleDriveTank +*/ +class PxVehicleDrive : public PxVehicleWheels +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + /** + \brief Dynamics data of vehicle instance. + @see setup + */ + PxVehicleDriveDynData mDriveDynData; + +protected: + + /** + \brief Test that all instanced dynamics data and configuration data have legal values. + */ + bool isValid() const; + + /** + \brief Set vehicle to rest. + */ + void setToRestState(); + + /** + @see PxVehicleDrive4W::allocate, PxVehicleDriveTank::allocate + */ + static PxU32 computeByteSize(const PxU32 numWheels); + static PxU8* patchupPointers(const PxU32 nbWheels, PxVehicleDrive* vehDrive, PxU8* ptr); + virtual void init(const PxU32 numWheels); + + /** + \brief Deallocate a PxVehicle4WDrive instance. + @see PxVehicleDrive4W::free, PxVehicleDriveTank::free + */ + void free(); + + /** + @see PxVehicleDrive4W::setup, PxVehicleDriveTank::setup + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, + const PxU32 nbDrivenWheels, const PxU32 nbNonDrivenWheels); + +//serialization +public: + static void getBinaryMetaData(PxOutputStream& stream); + PxVehicleDrive(PxBaseFlags baseFlags) : PxVehicleWheels(baseFlags), mDriveDynData(PxEmpty) {} + virtual const char* getConcreteTypeName() const { return "PxVehicleDrive"; } +protected: + PxVehicleDrive(PxType concreteType, PxBaseFlags baseFlags) : PxVehicleWheels(concreteType, baseFlags) {} + ~PxVehicleDrive() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleDrive", name) || PxBase::isKindOf(name); } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDrive) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_DRIVE_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h b/src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h new file mode 100644 index 000000000..ec91e7b49 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h @@ -0,0 +1,279 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_4WDRIVE_H +#define PX_VEHICLE_4WDRIVE_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleDrive.h" +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleComponents.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxFilterData; +class PxGeometry; +class PxPhysics; +class PxBatchQuery; +class PxVehicleDrivableSurfaceToTireFrictionPairs; +class PxShape; +class PxMaterial; +class PxRigidDynamic; + +/** +\brief Data structure describing the drive model components of a vehicle with up to 4 driven wheels and up to 16 un-driven wheels. +The drive model incorporates engine, clutch, gears, autobox, differential, and Ackermann steer correction. +@see PxVehicleDriveSimData +*/ +class PxVehicleDriveSimData4W : public PxVehicleDriveSimData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleDrive4W; + + PxVehicleDriveSimData4W() + : PxVehicleDriveSimData() + { + } + + /** + \brief Return the data describing the differential. + @see PxVehicleDifferential4WData + */ + PX_FORCE_INLINE const PxVehicleDifferential4WData& getDiffData() const + { + return mDiff; + } + + /** + \brief Return the data describing the Ackermann steer-correction. + @see PxVehicleAckermannGeometryData + */ + PX_FORCE_INLINE const PxVehicleAckermannGeometryData& getAckermannGeometryData() const + { + return mAckermannGeometry; + } + + /** + \brief Set the data describing the differential. + @see PxVehicleDifferential4WData + */ + void setDiffData(const PxVehicleDifferential4WData& diff); + + /** + \brief Set the data describing the Ackermann steer-correction. + @see PxVehicleAckermannGeometryData + */ + void setAckermannGeometryData(const PxVehicleAckermannGeometryData& ackermannData); + +private: + + /** + \brief Differential simulation data + @see setDiffData, getDiffData + */ + PxVehicleDifferential4WData mDiff; + + /** + \brief Data for ackermann steer angle computation. + @see setAckermannGeometryData, getAckermannGeometryData + */ + PxVehicleAckermannGeometryData mAckermannGeometry; + + /** + \brief Test if the 4W-drive simulation data has been setup with legal data. + \note Call only after setting all components. + @see setEnginedata, setClutchData, setGearsData, setAutoboxData, setDiffData, setAckermannGeometryData + */ + bool isValid() const; + +//serialization +public: + PxVehicleDriveSimData4W(const PxEMPTY) : PxVehicleDriveSimData(PxEmpty), mDiff(PxEmpty), mAckermannGeometry(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveSimData4W) & 15)); + + + +/** +\brief The ordering of the driven and steered wheels of a PxVehicleDrive4W. + +@see PxVehicleWheelsSimData, PxVehicleWheelsDynData +*/ + +struct PxVehicleDrive4WWheelOrder +{ + enum Enum + { + eFRONT_LEFT=0, + eFRONT_RIGHT, + eREAR_LEFT, + eREAR_RIGHT + }; +}; + +/** +\brief The control inputs for a PxVehicleDrive4W. + +@see PxVehicleDriveDynData::setAnalogInput, PxVehicleDriveDynData::getAnalogInput +*/ + +struct PxVehicleDrive4WControl +{ + enum Enum + { + eANALOG_INPUT_ACCEL=0, + eANALOG_INPUT_BRAKE, + eANALOG_INPUT_HANDBRAKE, + eANALOG_INPUT_STEER_LEFT, + eANALOG_INPUT_STEER_RIGHT, + eMAX_NB_DRIVE4W_ANALOG_INPUTS + }; +}; + +/** +\brief Data structure with instanced dynamics data and configuration data of a vehicle with up to 4 driven wheels and up to 16 non-driven wheels. +*/ +class PxVehicleDrive4W : public PxVehicleDrive +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + friend class PxVehicleUpdate; + + /** + \brief Allocate a PxVehicleDrive4W instance for a 4WDrive vehicle with nbWheels (= 4 + number of un-driven wheels) + + \param[in] nbWheels is the number of vehicle wheels (= 4 + number of un-driven wheels) + + \return The instantiated vehicle. + + @see free, setup + */ + static PxVehicleDrive4W* allocate(const PxU32 nbWheels); + + /** + \brief Deallocate a PxVehicleDrive4W instance. + @see allocate + */ + void free(); + + /** + \brief Set up a vehicle using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \param[in] driveData describes the properties of the vehicle's drive model (gears/engine/clutch/differential/autobox). The vehicle instance takes a copy of this data. + \param[in] nbNonDrivenWheels is the number of wheels on the vehicle that cannot be connected to the differential (= numWheels - 4). + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + \note wheelsData must contain data for at least 4 wheels. Unwanted wheels can be disabled with PxVehicleWheelsSimData::disableWheel after calling setup. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, + const PxU32 nbNonDrivenWheels); + + /** + \brief Allocate and set up a vehicle using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \param[in] driveData describes the properties of the vehicle's drive model (gears/engine/clutch/differential/autobox). The vehicle instance takes a copy of this data. + \param[in] nbNonDrivenWheels is the number of wheels on the vehicle that cannot be connected to the differential (= numWheels - 4). + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + \note wheelsData must contain data for at least 4 wheels. Unwanted wheels can be disabled with PxVehicleWheelsSimData::disableWheel after calling setup. + \return The instantiated vehicle. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + */ + static PxVehicleDrive4W* create + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, + const PxU32 nbNonDrivenWheels); + + /** + \brief Set a vehicle to its rest state. Aside from the rigid body transform, this will set the vehicle and rigid body + to the state they were in immediately after setup or create. + \note Calling setToRestState invalidates the cached raycast hit planes under each wheel meaning that suspension line + raycasts need to be performed at least once with PxVehicleSuspensionRaycasts before calling PxVehicleUpdates. + @see setup, create, PxVehicleSuspensionRaycasts, PxVehicleUpdates + */ + void setToRestState(); + + /** + \brief Simulation data that describes the configuration of the vehicle's drive model. + @see setup, create + */ + PxVehicleDriveSimData4W mDriveSimData; + +private: + + /** + \brief Test if the instanced dynamics and configuration data has legal values. + */ + bool isValid() const; + +//serialization +protected: + PxVehicleDrive4W(); + ~PxVehicleDrive4W(){} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleDrive4W", name) || PxBase::isKindOf(name); } +public: + static PxVehicleDrive4W* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + PxVehicleDrive4W(PxBaseFlags baseFlags) : PxVehicleDrive(baseFlags), mDriveSimData(PxEmpty) {} + virtual const char* getConcreteTypeName() const { return "PxVehicleDrive4W"; } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDrive4W) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_4WDRIVE_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h b/src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h new file mode 100644 index 000000000..f4a0f84ea --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h @@ -0,0 +1,237 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_NWDRIVE_H +#define PX_VEHICLE_NWDRIVE_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleDrive.h" +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleComponents.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxFilterData; +class PxGeometry; +class PxPhysics; +class PxBatchQuery; +class PxVehicleDrivableSurfaceToTireFrictionPairs; +class PxShape; +class PxMaterial; +class PxRigidDynamic; + +/** +\brief Data structure describing configuration data of a vehicle with up to PX_MAX_NB_WHEELS driven equally through the differential. The vehicle has an +engine, clutch, gears, autobox, differential. +@see PxVehicleDriveSimData +*/ +class PxVehicleDriveSimDataNW : public PxVehicleDriveSimData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleDriveNW; + + PxVehicleDriveSimDataNW() + : PxVehicleDriveSimData() + { + } + + /** + \brief Return the data describing the differential of a vehicle with up to PX_MAX_NB_WHEELS driven wheels. + */ + const PxVehicleDifferentialNWData& getDiffData() const + { + return mDiff; + } + + /** + \brief Set the data describing the differential of a vehicle with up to PX_MAX_NB_WHEELS driven wheels. + The differential data describes the set of wheels that are driven by the differential. + */ + void setDiffData(const PxVehicleDifferentialNWData& diff); + +private: + + /** + \brief Differential simulation data + @see setDiffData, getDiffData + */ + PxVehicleDifferentialNWData mDiff; + + /** + \brief Test if the NW-drive simulation data has been setup with legal data. + Call only after setting all components. + @see setEngineData, setClutchData, setGearsData, setAutoboxData, setDiffData, setAckermannGeometryData + */ + bool isValid() const; + +//serialization +public: + PxVehicleDriveSimDataNW(const PxEMPTY) : PxVehicleDriveSimData(PxEmpty), mDiff(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveSimDataNW) & 15)); + + +/** +\brief The control inputs for a PxVehicleDriveNW. + +@see PxVehicleDriveDynData::setAnalogInput, PxVehicleDriveDynData::getAnalogInput +*/ +struct PxVehicleDriveNWControl +{ + enum Enum + { + eANALOG_INPUT_ACCEL=0, + eANALOG_INPUT_BRAKE, + eANALOG_INPUT_HANDBRAKE, + eANALOG_INPUT_STEER_LEFT, + eANALOG_INPUT_STEER_RIGHT, + eMAX_NB_DRIVENW_ANALOG_INPUTS + }; +}; + +/** +\brief Data structure with instanced dynamics data and configuration data of a vehicle with up to PX_MAX_NB_WHEELS driven wheels. +*/ +class PxVehicleDriveNW : public PxVehicleDrive +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + /** + \brief Allocate a PxVehicleDriveNW instance for a NWDrive vehicle with nbWheels + + \param[in] nbWheels is the number of wheels on the vehicle. + + \return The instantiated vehicle. + + @see free, setup + */ + static PxVehicleDriveNW* allocate(const PxU32 nbWheels); + + /** + \brief Deallocate a PxVehicleDriveNW instance. + @see allocate + */ + void free(); + + /** + \brief Set up a vehicle using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \param[in] driveData describes the properties of the vehicle's drive model (gears/engine/clutch/differential/autobox). The vehicle instance takes a copy of this data. + \param[in] nbWheels is the number of wheels on the vehicle. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimDataNW& driveData, + const PxU32 nbWheels); + + /** + \brief Allocate and set up a vehicle using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \param[in] driveData describes the properties of the vehicle's drive model (gears/engine/clutch/differential/autobox). The vehicle instance takes a copy of this data. + \param[in] nbWheels is the number of wheels on the vehicle. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + \return The instantiated vehicle. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + */ + static PxVehicleDriveNW* create + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimDataNW& driveData, + const PxU32 nbWheels); + + /** + \brief Set a vehicle to its rest state. Aside from the rigid body transform, this will set the vehicle and rigid body + to the state they were in immediately after setup or create. + \note Calling setToRestState invalidates the cached raycast hit planes under each wheel meaning that suspension line + raycasts need to be performed at least once with PxVehicleSuspensionRaycasts before calling PxVehicleUpdates. + @see setup, create, PxVehicleSuspensionRaycasts, PxVehicleUpdates + */ + void setToRestState(); + + /** + \brief Simulation data that describes the configuration of the vehicle's drive model. + @see setup, create + */ + PxVehicleDriveSimDataNW mDriveSimData; + +private: + + /** + \brief Test if the instanced dynamics and configuration data has legal values. + */ + bool isValid() const; + +//serialization +public: + PxVehicleDriveNW(PxBaseFlags baseFlags) : PxVehicleDrive(baseFlags), mDriveSimData(PxEmpty) {} + PxVehicleDriveNW(); + ~PxVehicleDriveNW(){} + static PxVehicleDriveNW* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + virtual const char* getConcreteTypeName() const { return "PxVehicleDriveNW"; } + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleDriveNW", name) || PxBase::isKindOf(name); } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveNW) & 15)); + + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_NWDRIVE_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h b/src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h new file mode 100644 index 000000000..3e176f124 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h @@ -0,0 +1,281 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_DRIVE_TANK_H +#define PX_VEHICLE_DRIVE_TANK_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleDrive.h" +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleComponents.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxFilterData; +class PxGeometry; +class PxPhysics; +class PxBatchQuery; +class PxVehicleDrivableSurfaceToTireFrictionPairs; +class PxShape; +class PxMaterial; +class PxRigidDynamic; + +/** +\brief The ordering of the wheels of a PxVehicleDriveTank. + +@see PxVehicleWheelsSimData, PxVehicleWheelsDynData +*/ +struct PxVehicleDriveTankWheelOrder +{ + enum Enum + { + eFRONT_LEFT=0, + eFRONT_RIGHT, + e1ST_FROM_FRONT_LEFT, + e1ST_FROM_FRONT_RIGHT, + e2ND_FROM_FRONT_LEFT, + e2ND_FROM_FRONT_RIGHT, + e3RD_FROM_FRONT_LEFT, + e3RD_FROM_FRONT_RIGHT, + e4TH_FROM_FRONT_LEFT, + e4TH_FROM_FRONT_RIGHT, + e5TH_FROM_FRONT_LEFT, + e5TH_FROM_FRONT_RIGHT, + e6TH_FROM_FRONT_LEFT, + e6TH_FROM_FRONT_RIGHT, + e7TH_FROM_FRONT_LEFT, + e7TH_FROM_FRONT_RIGHT, + e8TH_FROM_FRONT_LEFT, + e8TH_FROM_FRONT_RIGHT, + e9TH_FROM_FRONT_LEFT, + e9TH_FROM_FRONT_RIGHT + }; +}; + + +/** +\brief The control inputs for a PxVehicleDriveTank. + +\note The values of eANALOG_INPUT_THRUST_LEFT and eANALOG_INPUT_THRUST_RIGHT determine how much +of the total available drive torque is diverted to the left and right wheels. These entries in the +enumerated list represent the state of the left and right control sticks of a tank. The total available +drive torque available is controlled by eANALOG_INPUT_ACCEL, which represents the state of the acceleration +pedal and controls how much torque will be applied to the engine. + +\note To accelerate forwards eANALOG_INPUT_ACCEL must be greater than zero so that torque is applied to drive the +engine, while eANALOG_INPUT_THRUST_LEFT and eANALOG_INPUT_THRUST_RIGHT must also be greater than zero +to divert the available drive torque to the left and wheels. If eANALOG_INPUT_THRUST_LEFT > eANALOG_INPUT_THRUST_RIGHT +the tank will turn to the right. If eANALOG_INPUT_THRUST_RIGHT > eANALOG_INPUT_THRUST_LEFT +the tank will turn to the left. + +@see PxVehicleDriveDynData::setAnalogInput, PxVehicleDriveDynData::getAnalogInput +*/ + +struct PxVehicleDriveTankControl +{ + enum Enum + { + eANALOG_INPUT_ACCEL=0, + eANALOG_INPUT_BRAKE_LEFT, + eANALOG_INPUT_BRAKE_RIGHT, + eANALOG_INPUT_THRUST_LEFT, + eANALOG_INPUT_THRUST_RIGHT, + eMAX_NB_DRIVETANK_ANALOG_INPUTS + }; +}; + +/** +\brief Two driving models are supported. + +\note If eSTANDARD is chosen the left and right wheels are always driven in the same direction. If the tank is in +a forward gear the left and right wheels will all be driven forwards, while in reverse gear the left and right wheels +will all be driven backwards. With eSTANDARD the legal range of left and right thrust is (0,1). + +\note If eSPECIAL is chosen it is possible to drive the left and right wheels in different directions. +With eSPECIAL the legal range of left and right thrust is (-1,1). In forward(reverse) gear negative thrust values drive the wheels +backwards(forwards), while positive thrust values drives the wheels forwards(backwards). + +\note A sharp left turn can be achieved in eSTANDARD mode by braking with the left wheels and thrusting forward with the +right wheels. A smaller turning circle can theoretically be achieved in eSPECIAL mode by applying negative thrust to the left wheels and positive +thrust to the right wheels. + +\note In both modes the legal ranges of acceleration and left/right brake are all (0,1). + +@see PxVehicleDriveTank::setDriveModel +*/ +struct PxVehicleDriveTankControlModel +{ + enum Enum + { + eSTANDARD=0, + eSPECIAL + }; +}; + + +/** +\brief Data structure with instanced dynamics data and configuration data of a tank. +*/ +class PxVehicleDriveTank : public PxVehicleDrive +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + + /** + \brief Allocate a PxVehicleTankDrive instance for a tank with nbWheels + + \param[in] nbWheels is the number of wheels on the vehicle. + + \note It is assumed that all wheels are driven wheels. + + \return The instantiated vehicle. + + @see free, setup + */ + static PxVehicleDriveTank* allocate(const PxU32 nbWheels); + + /** + \brief Deallocate a PxVehicleDriveTank instance. + @see allocate + */ + void free(); + + /** + \brief Set up a tank using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the tank in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the tank. The tank instance takes a copy of this data. + \param[in] driveData describes the properties of the tank's drive model (gears/engine/clutch/autobox). The tank instance takes a copy of this data. + \param[in] nbDrivenWheels is the number of wheels on the tank. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + \note nbDrivenWheels must be an even number + \note The wheels must be arranged according to PxVehicleDriveTankWheelOrder; that is, + the even wheels are on the left side of the tank and the odd wheels are on the right side of the tank. + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData& driveData, + const PxU32 nbDrivenWheels); + + /** + \brief Allocate and set up a tank using simulation data for the wheels and drive model. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the tank. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the tank in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the tank. The tank instance takes a copy of this data. + \param[in] driveData describes the properties of the tank's drive model (gears/engine/clutch/differential/autobox). The tank instance takes a copy of this data. + \param[in] nbDrivenWheels is the number of wheels on the tank. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheelsSimData::setWheelShapeMapping. + \return The instantiated vehicle. + @see allocate, free, setToRestState, PxVehicleWheelsSimData::setWheelShapeMapping + */ + static PxVehicleDriveTank* create + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData& driveData, + const PxU32 nbDrivenWheels); + + /** + \brief Set the control model used by the tank. + \note eDRIVE_MODEL_STANDARD: turning achieved by braking on one side, accelerating on the other side. + \note eDRIVE_MODEL_SPECIAL: turning achieved by accelerating forwards on one side, accelerating backwards on the other side. + \note The default value is eDRIVE_MODEL_STANDARD + */ + void setDriveModel(const PxVehicleDriveTankControlModel::Enum driveModel) + { + mDriveModel=driveModel; + } + + /** + \brief Return the control model used by the tank. + */ + PxVehicleDriveTankControlModel::Enum getDriveModel() const {return mDriveModel;} + + /** + \brief Set a vehicle to its rest state. Aside from the rigid body transform, this will set the vehicle and rigid body + to the state they were in immediately after setup or create. + \note Calling setToRestState invalidates the cached raycast hit planes under each wheel meaning that suspension line + raycasts need to be performed at least once with PxVehicleSuspensionRaycasts before calling PxVehicleUpdates. + @see setup, create, PxVehicleSuspensionRaycasts, PxVehicleUpdates + */ + void setToRestState(); + + /** + \brief Simulation data that models vehicle components + @see setup, create + */ + PxVehicleDriveSimData mDriveSimData; + +private: + /** + \brief Test if the instanced dynamics and configuration data has legal values. + */ + bool isValid() const; + + /** + \brief Drive model + @see setDriveModel, getDriveModel, PxVehicleDriveTankControlModel + */ + PxVehicleDriveTankControlModel::Enum mDriveModel; + + PxU32 mPad[3]; + +//serialization +public: + PxVehicleDriveTank(PxBaseFlags baseFlags) : PxVehicleDrive(baseFlags) {} + static PxVehicleDriveTank* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + virtual const char* getConcreteTypeName() const { return "PxVehicleDriveTank"; } + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleDriveTank", name) || PxBase::isKindOf(name); } +protected: + PxVehicleDriveTank(); + ~PxVehicleDriveTank(){} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDriveTank) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_DRIVE_TANK_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h b/src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h new file mode 100644 index 000000000..a92a082e4 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h @@ -0,0 +1,217 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_NO_DRIVE_H +#define PX_VEHICLE_NO_DRIVE_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleWheels.h" +#include "vehicle/PxVehicleComponents.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxFilterData; +class PxGeometry; +class PxPhysics; +class PxBatchQuery; +class PxVehicleDrivableSurfaceToTireFrictionPairs; +class PxShape; +class PxMaterial; +class PxRigidDynamic; + +/** +\brief Data structure with instanced dynamics data and configuration data of a vehicle with no drive model. +*/ +class PxVehicleNoDrive : public PxVehicleWheels +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + /** + \brief Allocate a PxVehicleNoDrive instance for a vehicle without drive model and with nbWheels + + \param[in] nbWheels is the number of wheels on the vehicle. + + \return The instantiated vehicle. + + @see free, setup + */ + static PxVehicleNoDrive* allocate(const PxU32 nbWheels); + + /** + \brief Deallocate a PxVehicleNoDrive instance. + @see allocate + */ + void free(); + + /** + \brief Set up a vehicle using simulation data for the wheels. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheels::setWheelShapeMapping. + @see allocate, free, setToRestState, PxVehicleWheels::setWheelShapeMapping + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, const PxVehicleWheelsSimData& wheelsData); + + /** + \brief Allocate and set up a vehicle using simulation data for the wheels. + \param[in] physics is a PxPhysics instance that is needed to create special vehicle constraints that are maintained by the vehicle. + \param[in] vehActor is a PxRigidDynamic instance that is used to represent the vehicle in the PhysX SDK. + \param[in] wheelsData describes the configuration of all suspension/tires/wheels of the vehicle. The vehicle instance takes a copy of this data. + \note It is assumed that the first shapes of the actor are the wheel shapes, followed by the chassis shapes. To break this assumption use PxVehicleWheels::setWheelShapeMapping. + \return The instantiated vehicle. + @see allocate, free, setToRestState, PxVehicleWheels::setWheelShapeMapping + */ + static PxVehicleNoDrive* create + (PxPhysics* physics, PxRigidDynamic* vehActor, const PxVehicleWheelsSimData& wheelsData); + + /** + \brief Set a vehicle to its rest state. Aside from the rigid body transform, this will set the vehicle and rigid body + to the state they were in immediately after setup or create. + \note Calling setToRestState invalidates the cached raycast hit planes under each wheel meaning that suspension line + raycasts need to be performed at least once with PxVehicleSuspensionRaycasts before calling PxVehicleUpdates. + @see setup, create, PxVehicleSuspensionRaycasts, PxVehicleUpdates + */ + void setToRestState(); + + /** + \brief Set the brake torque to be applied to a specific wheel + + \note The applied brakeTorque persists until the next call to setBrakeTorque + + \note The brake torque is specified in Newton metres. + + \param[in] id is the wheel being given the brake torque + \param[in] brakeTorque is the value of the brake torque + */ + void setBrakeTorque(const PxU32 id, const PxReal brakeTorque); + + /** + \brief Set the drive torque to be applied to a specific wheel + + \note The applied driveTorque persists until the next call to setDriveTorque + + \note The brake torque is specified in Newton metres. + + \param[in] id is the wheel being given the brake torque + \param[in] driveTorque is the value of the brake torque + */ + void setDriveTorque(const PxU32 id, const PxReal driveTorque); + + /** + \brief Set the steer angle to be applied to a specific wheel + + \note The applied steerAngle persists until the next call to setSteerAngle + + \note The steer angle is specified in radians. + + \param[in] id is the wheel being given the steer angle + \param[in] steerAngle is the value of the steer angle in radians. + */ + void setSteerAngle(const PxU32 id, const PxReal steerAngle); + + /** + \brief Get the brake torque that has been applied to a specific wheel + \param[in] id is the wheel being queried for its brake torque + \return The brake torque applied to the queried wheel. + */ + PxReal getBrakeTorque(const PxU32 id) const; + + /** + \brief Get the drive torque that has been applied to a specific wheel + \param[in] id is the wheel being queried for its drive torque + \return The drive torque applied to the queried wheel. + */ + PxReal getDriveTorque(const PxU32 id) const; + + /** + \brief Get the steer angle that has been applied to a specific wheel + \param[in] id is the wheel being queried for its steer angle + \return The steer angle (in radians) applied to the queried wheel. + */ + PxReal getSteerAngle(const PxU32 id) const; + +private: + + PxReal* mSteerAngles; + PxReal* mDriveTorques; + PxReal* mBrakeTorques; + +#if PX_P64_FAMILY + PxU32 mPad[2]; +#else + PxU32 mPad[1]; +#endif + + /** + \brief Test if the instanced dynamics and configuration data has legal values. + */ + bool isValid() const; + + +//serialization +public: + PxVehicleNoDrive(PxBaseFlags baseFlags) : PxVehicleWheels(baseFlags) {} + virtual void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext&); + static PxVehicleNoDrive* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + virtual const char* getConcreteTypeName() const { return "PxVehicleNoDrive"; } + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleNoDrive", name) || PxBase::isKindOf(name); } + PxU32 getNbSteerAngle() const { return mWheelsSimData.getNbWheels(); } + PxU32 getNbDriveTorque() const { return mWheelsSimData.getNbWheels(); } + PxU32 getNbBrakeTorque() const { return mWheelsSimData.getNbWheels(); } +protected: + PxVehicleNoDrive(); + ~PxVehicleNoDrive() {} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleNoDrive) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_NO_DRIVE_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleSDK.h b/src/PhysX/physx/include/vehicle/PxVehicleSDK.h new file mode 100644 index 000000000..01924b203 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleSDK.h @@ -0,0 +1,237 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_SDK_H +#define PX_VEHICLE_SDK_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/Px.h" +#include "common/PxTypeInfo.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPhysics; +class PxSerializationRegistry; + +/** +\brief Initialize the PhysXVehicle library. + +Call this before using any of the vehicle functions. + +\param physics The PxPhysics instance. +\param serializationRegistry PxSerializationRegistry instance, if NULL vehicle serialization is not supported. + +\note This function must be called after PxFoundation and PxPhysics instances have been created. +\note If a PxSerializationRegistry instance is specified then PhysXVehicle is also dependent on PhysXExtensions. + +@see PxCloseVehicleSDK +*/ +PX_C_EXPORT bool PX_CALL_CONV PxInitVehicleSDK(PxPhysics& physics, PxSerializationRegistry* serializationRegistry = NULL); + + +/** +\brief Shut down the PhysXVehicle library. + +Call this function as part of the physx shutdown process. + +\param serializationRegistry PxSerializationRegistry instance, if non-NULL must be the same as passed into PxInitVehicleSDK. + +\note This function must be called prior to shutdown of PxFoundation and PxPhysics. +\note If the PxSerializationRegistry instance is specified this function must additionally be called prior to shutdown of PhysXExtensions. + +@see PxInitVehicleSDK +*/ +PX_C_EXPORT void PX_CALL_CONV PxCloseVehicleSDK(PxSerializationRegistry* serializationRegistry = NULL); + + +/** +\brief This number is the maximum number of wheels allowed for a vehicle. +*/ +#define PX_MAX_NB_WHEELS (20) + + +/** +\brief Compiler setting to enable recording of telemetry data + +@see PxVehicleUpdateSingleVehicleAndStoreTelemetryData, PxVehicleTelemetryData +*/ +#define PX_DEBUG_VEHICLE_ON (1) + + +/** +@see PxVehicleDrive4W, PxVehicleDriveTank, PxVehicleDriveNW, PxVehicleNoDrive, PxVehicleWheels::getVehicleType +*/ +struct PxVehicleTypes +{ + enum Enum + { + eDRIVE4W=0, + eDRIVENW, + eDRIVETANK, + eNODRIVE, + eUSER1, + eUSER2, + eUSER3, + eMAX_NB_VEHICLE_TYPES + }; +}; + + +/** +\brief An enumeration of concrete vehicle classes inheriting from PxBase. +\note This enum can be used to identify a vehicle object stored in a PxCollection. +@see PxBase, PxTypeInfo, PxBase::getConcreteType +*/ +struct PxVehicleConcreteType +{ + enum Enum + { + eVehicleNoDrive = PxConcreteType::eFIRST_VEHICLE_EXTENSION, + eVehicleDrive4W, + eVehicleDriveNW, + eVehicleDriveTank + }; +}; + + +/** +\brief Set the basis vectors of the vehicle simulation + +Default values PxVec3(0,1,0), PxVec3(0,0,1) + +Call this function before using PxVehicleUpdates unless the default values are correct. +*/ +void PxVehicleSetBasisVectors(const PxVec3& up, const PxVec3& forward); + + +/** +@see PxVehicleSetUpdateMode +*/ +struct PxVehicleUpdateMode +{ + enum Enum + { + eVELOCITY_CHANGE, + eACCELERATION + }; +}; + + +/** +\brief Set the effect of PxVehicleUpdates to be either to modify each vehicle's rigid body actor + +with an acceleration to be applied in the next PhysX SDK update or as an immediate velocity modification. + +Default behavior is immediate velocity modification. + +Call this function before using PxVehicleUpdates for the first time if the default is not the desired behavior. + +@see PxVehicleUpdates +*/ +void PxVehicleSetUpdateMode(PxVehicleUpdateMode::Enum vehicleUpdateMode); + +/** + +\brief Set threshold angles that are used to determine if a wheel hit is to be resolved by vehicle suspension or by rigid body collision. + + +\note ^ + N ___ + |** + ** + ** + %%% %%% ** + %%% %%% ** / + / + %%% %%% / + / + %%% %%% / + C / + %%% | ** %%% / + | ** / + %%% | **%%%/ + | X** + %%% | %%% / **_| ^ + | / D + %%% | %%% / + | / + | / + | / + | + ^ | + S \|/ + +The diagram above depicts a wheel centered at "C" that has hit an inclined plane at point "X". +The inclined plane has unit normal "N", while the suspension direction has unit vector "S". +The unit vector from the wheel center to the hit point is "D". +Hit points are analyzed by comparing the unit vectors D and N with the suspension direction S. +This analysis is performed in the contact modification callback PxVehicleModifyWheelContacts (when enabled) and in +PxVehicleUpdates (when non-blocking sweeps are enabled). +If the angle between D and S is less than pointRejectAngle the hit is accepted by the suspension in PxVehicleUpdates and rejected +by the contact modification callback PxVehicleModifyWheelContacts. +If the angle between -N and S is less than normalRejectAngle the hit is accepted by the suspension in PxVehicleUpdates and rejected +by the contact modification callback PxVehicleModifyWheelContacts. + +\param pointRejectAngle is the threshold angle used when comparing the angle between D and S. + +\param normalRejectAngle is the threshold angle used when comparing the angle between -N and S. + +\note PxVehicleUpdates ignores the rejection angles for raycasts and for sweeps that return blocking hits. + +\note Both angles have default values of Pi/4. + +@see PxVehicleSuspensionSweeps, PxVehicleModifyWheelContacts +*/ +void PxVehicleSetSweepHitRejectionAngles(const PxF32 pointRejectAngle, const PxF32 normalRejectAngle); + + +/** +\brief Determine the maximum acceleration experienced by PxRigidDynamic instances that are found to be in contact +with a wheel. + +\note Newton's Third Law states that every force has an equal and opposite force. As a consequence, forces applied to +the suspension must be applied to dynamic objects that lie under the wheel. This can lead to instabilities, particularly +when a heavy wheel is driving on a light object. The value of maxHitActorAcceleration clamps the applied force so that it never +generates an acceleration greater than the specified value. + +\note Default value of maxHitActorAcceleration is PX_MAX_REAL +*/ +void PxVehicleSetMaxHitActorAcceleration(const PxF32 maxHitActorAcceleration); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_SDK_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleShaders.h b/src/PhysX/physx/include/vehicle/PxVehicleShaders.h new file mode 100644 index 000000000..915ab0af5 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleShaders.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_SHADERS_H +#define PX_VEHICLE_SHADERS_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Prototype of shader function that is used to compute wheel torque and tire forces. +\param[in] shaderData is the shader data for the tire being processed. The shader data describes the tire data in the format required by the tire model that is implemented by the shader function. +\param[in] tireFriction is the value of friction for the contact between the tire and the ground. +\param[in] longSlip is the value of longitudinal slip experienced by the tire. +\param[in] latSlip is the value of lateral slip experienced by the tire. +\param[in] camber is the camber angle of the tire in radians. +\param[in] wheelOmega is the rotational speed of the wheel. +\param[in] wheelRadius is the distance from the tire surface to the center of the wheel. +\param[in] recipWheelRadius is the reciprocal of wheelRadius. +\param[in] restTireLoad is the load force experienced by the tire when the vehicle is at rest. +\param[in] normalisedTireLoad is a pre-computed value equal to the load force on the tire divided by restTireLoad. +\param[in] tireLoad is the load force currently experienced by the tire (= restTireLoad*normalisedTireLoad) +\param[in] gravity is the magnitude of gravitational acceleration. +\param[in] recipGravity is the reciprocal of the magnitude of gravitational acceleration. +\param[out] wheelTorque is the torque that is to be applied to the wheel around the wheel's axle. +\param[out] tireLongForceMag is the magnitude of the longitudinal tire force to be applied to the vehicle's rigid body. +\param[out] tireLatForceMag is the magnitude of the lateral tire force to be applied to the vehicle's rigid body. +\param[out] tireAlignMoment is the aligning moment of the tire that is to be applied to the vehicle's rigid body (not currently used). +@see PxVehicleWheelsDynData::setTireForceShaderFunction, PxVehicleWheelsDynData::setTireForceShaderData +*/ +typedef void (*PxVehicleComputeTireForce) +(const void* shaderData, + const PxF32 tireFriction, + const PxF32 longSlip, const PxF32 latSlip, const PxF32 camber, + const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 recipWheelRadius, + const PxF32 restTireLoad, const PxF32 normalisedTireLoad, const PxF32 tireLoad, + const PxF32 gravity, const PxF32 recipGravity, + PxF32& wheelTorque, PxF32& tireLongForceMag, PxF32& tireLatForceMag, PxF32& tireAlignMoment); + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_SHADERS_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h b/src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h new file mode 100644 index 000000000..dd4f05c30 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h @@ -0,0 +1,223 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_TIREFRICTION_H +#define PX_VEHICLE_TIREFRICTION_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxMaterial; + +/** +\brief Driving surface type. Each PxMaterial is associated with a corresponding PxVehicleDrivableSurfaceType. +@see PxMaterial, PxVehicleDrivableSurfaceToTireFrictionPairs +*/ +struct PxVehicleDrivableSurfaceType +{ + enum + { + eSURFACE_TYPE_UNKNOWN=0xffffffff + }; + PxU32 mType; +}; + +/** +\brief Friction for each combination of driving surface type and tire type. +@see PxVehicleDrivableSurfaceType, PxVehicleTireData::mType +*/ +class PxVehicleDrivableSurfaceToTireFrictionPairs +{ +public: + + friend class VehicleSurfaceTypeHashTable; + + enum + { + eMAX_NB_SURFACE_TYPES=256 + }; + + /** + \brief Allocate the memory for a PxVehicleDrivableSurfaceToTireFrictionPairs instance + that can hold data for combinations of tire type and surface type with up to maxNbTireTypes types of tire and maxNbSurfaceTypes types of surface. + + \param[in] maxNbTireTypes is the maximum number of allowed tire types. + \param[in] maxNbSurfaceTypes is the maximum number of allowed surface types. Must be less than or equal to eMAX_NB_SURFACE_TYPES + + \return a PxVehicleDrivableSurfaceToTireFrictionPairs instance that can be reused later with new type and friction data. + + @see setup + */ + static PxVehicleDrivableSurfaceToTireFrictionPairs* allocate + (const PxU32 maxNbTireTypes, const PxU32 maxNbSurfaceTypes); + + /** + \brief Set up a PxVehicleDrivableSurfaceToTireFrictionPairs instance for combinations of nbTireTypes tire types and nbSurfaceTypes surface types. + + \param[in] nbTireTypes is the number of different types of tire. This value must be less than or equal to maxNbTireTypes specified in allocate(). + \param[in] nbSurfaceTypes is the number of different types of surface. This value must be less than or equal to maxNbSurfaceTypes specified in allocate(). + \param[in] drivableSurfaceMaterials is an array of PxMaterial pointers of length nbSurfaceTypes. + \param[in] drivableSurfaceTypes is an array of PxVehicleDrivableSurfaceType instances of length nbSurfaceTypes. + + \note If the pointer to the PxMaterial that touches the tire is found in drivableSurfaceMaterials[x] then the surface type is drivableSurfaceTypes[x].mType + and the friction is the value that is set with setTypePairFriction(drivableSurfaceTypes[x].mType, PxVehicleTireData::mType, frictionValue). + + \note A friction value of 1.0 will be assigned as default to each combination of tire and surface type. To override this use setTypePairFriction. + @see release, setTypePairFriction, getTypePairFriction, PxVehicleTireData.mType + */ + void setup + (const PxU32 nbTireTypes, const PxU32 nbSurfaceTypes, + const PxMaterial** drivableSurfaceMaterials, const PxVehicleDrivableSurfaceType* drivableSurfaceTypes); + + /** + \brief Deallocate a PxVehicleDrivableSurfaceToTireFrictionPairs instance + */ + void release(); + + /** + \brief Set the friction for a specified pair of tire type and drivable surface type. + + \param[in] surfaceType describes the surface type + \param[in] tireType describes the tire type. + \param[in] value describes the friction coefficient for the combination of surface type and tire type. + */ + void setTypePairFriction(const PxU32 surfaceType, const PxU32 tireType, const PxReal value); + + /** + \brief Return the friction for a specified combination of surface type and tire type. + \return The friction for a specified combination of surface type and tire type. + \note The final friction value used by the tire model is the value returned by getTypePairFriction + multiplied by the value computed from PxVehicleTireData::mFrictionVsSlipGraph + @see PxVehicleTireData::mFrictionVsSlipGraph + */ + PxReal getTypePairFriction(const PxU32 surfaceType, const PxU32 tireType) const; + + /** + \brief Return the maximum number of surface types + \return The maximum number of surface types + @see allocate + */ + PxU32 getMaxNbSurfaceTypes() const {return mMaxNbSurfaceTypes;} + + /** + \brief Return the maximum number of tire types + \return The maximum number of tire types + @see allocate + */ + PxU32 getMaxNbTireTypes() const {return mMaxNbTireTypes;} + +private: + + /** + \brief Ptr to base address of a 2d PxReal array with dimensions [mNbSurfaceTypes][mNbTireTypes] + + \note Each element of the array describes the maximum friction provided by a surface type-tire type combination. + eg the friction corresponding to a combination of surface type x and tire type y is mPairs[x][y] + */ + PxReal* mPairs; + + /** + \brief Ptr to 1d array of material ptrs that is of length mNbSurfaceTypes. + + \note If the PxMaterial that touches the tire corresponds to mDrivableSurfaceMaterials[x] then the drivable surface + type is mDrivableSurfaceTypes[x].mType and the friction for that contact is mPairs[mDrivableSurfaceTypes[x].mType][y], + assuming a tire type y. + + \note If the PxMaterial that touches the tire is not found in mDrivableSurfaceMaterials then the friction is + mPairs[0][y], assuming a tire type y. + */ + const PxMaterial** mDrivableSurfaceMaterials; + + /** + \brief Ptr to 1d array of PxVehicleDrivableSurfaceType that is of length mNbSurfaceTypes. + + \note If the PxMaterial that touches the tire is found in mDrivableSurfaceMaterials[x] then the drivable surface + type is mDrivableSurfaceTypes[x].mType and the friction for that contact is mPairs[mDrivableSurfaceTypes[x].mType][y], + assuming a tire type y. + + \note If the PxMaterial that touches the tire is not found in mDrivableSurfaceMaterials then the friction is + mPairs[0][y], assuming a tire type y. + */ + PxVehicleDrivableSurfaceType* mDrivableSurfaceTypes; + + /** + \brief Number of different driving surface types. + + \note mDrivableSurfaceMaterials and mDrivableSurfaceTypes are both 1d arrays of length mMaxNbSurfaceTypes. + + \note mNbSurfaceTypes must be less than or equal to mMaxNbSurfaceTypes. + */ + PxU32 mNbSurfaceTypes; + + /** + \brief Maximum number of different driving surface types. + + \note mMaxNbSurfaceTypes must be less than or equal to eMAX_NB_SURFACE_TYPES. + */ + PxU32 mMaxNbSurfaceTypes; + + /** + \brief Number of different tire types. + + \note Tire types stored in PxVehicleTireData.mType + */ + PxU32 mNbTireTypes; + + /** + \brief Maximum number of different tire types. + + \note Tire types stored in PxVehicleTireData.mType + */ + PxU32 mMaxNbTireTypes; + + +#if !PX_P64_FAMILY + PxU32 mPad[1]; +#else + PxU32 mPad[2]; +#endif + + PxVehicleDrivableSurfaceToTireFrictionPairs(){} + ~PxVehicleDrivableSurfaceToTireFrictionPairs(){} +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleDrivableSurfaceToTireFrictionPairs) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_TIREFRICTION_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUpdate.h b/src/PhysX/physx/include/vehicle/PxVehicleUpdate.h new file mode 100644 index 000000000..381a01ead --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleUpdate.h @@ -0,0 +1,579 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_UPDATE_H +#define PX_VEHICLE_UPDATE_H +/** \addtogroup vehicle + @{ +*/ + +#include "vehicle/PxVehicleSDK.h" +#include "vehicle/PxVehicleTireFriction.h" +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxMemory.h" +#include "foundation/PxTransform.h" +#include "PxBatchQueryDesc.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class PxBatchQuery; + class PxContactModifyPair; + class PxVehicleWheels; + class PxVehicleDrivableSurfaceToTireFrictionPairs; + class PxVehicleTelemetryData; + + /** + \brief Structure containing data describing the non-persistent state of each suspension/wheel/tire unit. + This structure is filled out in PxVehicleUpdates and PxVehicleUpdateSingleVehicleAndStoreTelemetryData + @see PxVehicleUpdates, PxVehicleUpdateSingleVehicleAndStoreTelemetryData + */ + struct PxWheelQueryResult + { + PxWheelQueryResult() + { + PxMemZero(this, sizeof(PxWheelQueryResult)); + isInAir=true; + tireSurfaceType = PxU32(PxVehicleDrivableSurfaceType::eSURFACE_TYPE_UNKNOWN); + localPose = PxTransform(PxIdentity); + } + + /** + \brief Start point of suspension line raycast/sweep used in the raycast/sweep completed immediately before PxVehicleUpdates. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then (0,0,0) is stored. + @see PxVehicleSuspensionRaycasts, PxVehicleSuspensionRaycasts + */ + PxVec3 suspLineStart; + + /** + \brief Directions of suspension line raycast/sweep used in the raycast/sweep completed immediately before PxVehicleUpdates. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then (0,0,0) is stored. + @see PxVehicleSuspensionRaycasts, PxVehicleSuspensionRaycasts + */ + PxVec3 suspLineDir; + + /** + \brief Lengths of suspension line raycast/sweep used in raycast/sweep completed immediately before PxVehicleUpdates. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then 0 is stored. + @see PxVehicleSuspensionRaycasts, PxVehicleSuspensionRaycasts + */ + PxReal suspLineLength; + + /** + \brief If suspension travel limits forbid the wheel from touching the drivable surface then isInAir is true. + \note If the wheel can be placed on the contact plane of the most recent suspension line raycast/sweep then isInAir is false. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then isInAir + is computed using the contact plane that was hit by the most recent suspension line raycast/sweep. + */ + bool isInAir; + + /** + \brief PxActor instance of the driving surface under the corresponding vehicle wheel. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireContactActor is NULL. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then NULL is stored. + */ + PxActor* tireContactActor; + + /** + \brief PxShape instance of the driving surface under the corresponding vehicle wheel. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireContactShape is NULL. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then NULL is stored. + */ + PxShape* tireContactShape; + + /** + \brief PxMaterial instance of the driving surface under the corresponding vehicle wheel. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireSurfaceMaterial is NULL. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then NULL is stored. + */ + const PxMaterial* tireSurfaceMaterial; + + /** + \brief Surface type integer that corresponds to the mapping between tireSurfaceMaterial and integer as + described in PxVehicleDrivableSurfaceToTireFrictionPairs. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireSurfaceType is + PxVehicleDrivableSurfaceType::eSURFACE_TYPE_UNKNOWN. + \note If no raycast/sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then + PxVehicleDrivableSurfaceType::eSURFACE_TYPE_UNKNOWN is stored. + @see PxVehicleDrivableSurfaceToTireFrictionPairs + */ + PxU32 tireSurfaceType; + + /** + \brief Point on the drivable surface hit by the most recent suspension raycast or sweep. + \note If suspension travel limits forbid the wheel from touching the drivable surface then the contact point is (0,0,0). + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then (0,0,0) is stored. + */ + PxVec3 tireContactPoint; + + /** + \brief Normal on the drivable surface at the hit point of the most recent suspension raycast or sweep. + \note If suspension travel limits forbid the wheel from touching the drivable surface then the contact normal is (0,0,0). + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then (0,0,0) is stored. + */ + PxVec3 tireContactNormal; + + /** + \brief Friction experienced by the tire for the combination of tire type and surface type after accounting + for the friction vs slip graph. + \note If suspension travel limits forbid the wheel from touching the drivable surface then the tire friction is 0. + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + stored tire friction is the value computed in PxVehicleUpdates that immediately followed the last raycast or sweep. + @see PxVehicleDrivableSurfaceToTireFrictionPairs, PxVehicleTireData + */ + PxReal tireFriction; + + /** + \brief Compression of the suspension spring. + \note If suspension travel limits forbid the wheel from touching the drivable surface then the jounce is -PxVehicleSuspensionData.mMaxDroop + The jounce can never exceed PxVehicleSuspensionData.mMaxCompression. Positive values result when the suspension is compressed from + the rest position, while negative values mean the suspension is elongated from the rest position. + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + suspension compression is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + */ + PxReal suspJounce; + + /** + \brief Magnitude of force applied by the suspension spring along the direction of suspension travel. + \note If suspension travel limits forbid the wheel from touching the drivable surface then the force is 0 + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + suspension spring force is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + @see PxVehicleWheelsSimData::getSuspTravelDirection + */ + PxReal suspSpringForce; + + /** + \brief Forward direction of the wheel/tire accounting for steer/toe/camber angle projected on to the contact plane of the drivable surface. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireLongitudinalDir is (0,0,0) + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + tire longitudinal direction is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + */ + PxVec3 tireLongitudinalDir; + + /** + \brief Lateral direction of the wheel/tire accounting for steer/toe/camber angle projected on to the contact plan of the drivable surface. + \note If suspension travel limits forbid the wheel from touching the drivable surface then tireLateralDir is (0,0,0) + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + tire lateral direction is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + */ + PxVec3 tireLateralDir; + + /** + \brief Longitudinal slip of the tire. + \note If suspension travel limits forbid the wheel from touching the drivable surface then longitudinalSlip is 0.0 + \note The longitudinal slip is approximately (w*r - vz) / PxAbs(vz) where w is the angular speed of the wheel, r is the radius of the wheel, and + vz component of rigid body velocity computed at the wheel base along the longitudinal direction of the tire. + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + tire longitudinal slip is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + */ + PxReal longitudinalSlip; + + /** + \brief Lateral slip of the tire. + \note If suspension travel limits forbid the wheel from touching the drivable surface then lateralSlip is 0.0 + \note The lateral slip angle is approximately PxAtan(vx / PxAbs(vz)) where vx and vz are the components of rigid body velocity at the wheel base + along the wheel's lateral and longitudinal directions, respectively. + \note If no raycast or sweep for the corresponding suspension was performed immediately prior to PxVehicleUpdates then the + tire lateral slip is computed using the contact plane that was hit by the most recent suspension line raycast or sweep. + */ + PxReal lateralSlip; + + /** + \brief Steer angle of the wheel about the "up" vector accounting for input steer and toe and, if applicable, Ackermann steer correction. + @see PxVehicleWheelData::mToeAngle + */ + PxReal steerAngle; + + /** + \brief Local pose of the wheel. + */ + PxTransform localPose; + }; + + struct PxVehicleWheelQueryResult + { + /** + \brief Pointer to an PxWheelQueryResult buffer of length nbWheelQueryResults + The wheelQueryResults buffer must persist until the end of PxVehicleUpdates + A NULL pointer is permitted. + The wheelQueryResults buffer is left unmodified in PxVehicleUpdates for vehicles with sleeping rigid bodies + whose control inputs indicate they should remain inert. + @see PxVehicleUpdates + */ + PxWheelQueryResult* wheelQueryResults; + + /** + \brief The length of the wheelQueryResults buffer. This value corresponds to the + number of wheels in the associated vehicle in PxVehicleUpdates. + */ + PxU32 nbWheelQueryResults; + }; + + /** + \brief Structure containing data that is computed for a wheel during concurrent calls to PxVehicleUpdates + but which cannot be safely concurrently applied. + + @see PxVehicleUpdates, PxVehiclePostUpdates, PxVehicleConcurrentUpdate + */ + struct PxVehicleWheelConcurrentUpdateData + { + friend class PxVehicleUpdate; + + PxVehicleWheelConcurrentUpdateData() + : localPose(PxTransform(PxIdentity)), + hitActor(NULL), + hitActorForce(PxVec3(0,0,0)), + hitActorForcePosition(PxVec3(0,0,0)) + { + } + + private: + + PxTransform localPose; + PxRigidDynamic* hitActor; + PxVec3 hitActorForce; + PxVec3 hitActorForcePosition; + }; + + /** + \brief Structure containing data that is computed for a vehicle and its wheels during concurrent calls to PxVehicleUpdates + but which cannot be safely concurrently applied. + + @see PxVehicleUpdates, PxVehiclePostUpdates, PxVehicleWheelConcurrentUpdateData + */ + + struct PxVehicleConcurrentUpdateData + { + friend class PxVehicleUpdate; + + PxVehicleConcurrentUpdateData() + : concurrentWheelUpdates(NULL), + nbConcurrentWheelUpdates(0), + linearMomentumChange(PxVec3(0,0,0)), + angularMomentumChange(PxVec3(0,0,0)), + staySleeping(false), + wakeup(false) + { + } + + /** + \brief Pointer to an PxVehicleWheelConcurrentUpdate buffer of length nbConcurrentWheelUpdates + The concurrentWheelUpdates buffer must persist until the end of PxVehiclePostUpdates + A NULL pointer is not permitted. + @see PxVehicleUpdates, PxVehiclePostUpdates + */ + PxVehicleWheelConcurrentUpdateData* concurrentWheelUpdates; + + /** + \brief The length of the concurrentWheelUpdates buffer. This value corresponds to the + number of wheels in the associated vehicle passed to PxVehicleUpdates. + */ + PxU32 nbConcurrentWheelUpdates; + + private: + + PxVec3 linearMomentumChange; + PxVec3 angularMomentumChange; + bool staySleeping; + bool wakeup; + }; + + /** + \brief Perform raycasts for all suspension lines for all vehicles. + + \param[in] batchQuery is a PxBatchQuery instance used to specify shader data and functions for the raycast scene queries. + + \param[in] nbVehicles is the number of vehicles in the vehicles array. + + \param[in] vehicles is an array of all vehicles that are to have a raycast issued from each wheel. + + \param[in] nbSceneQueryResults must be greater than or equal to the total number of wheels of all the vehicles in the vehicles array; that is, + sceneQueryResults must have dimensions large enough for one raycast hit result per wheel for all the vehicles in the vehicles array. + + \param[in] sceneQueryResults must persist without being overwritten until the end of the next PxVehicleUpdates call. + + \param[in] vehiclesToRaycast is an array of bools of length nbVehicles that is used to decide if raycasts will be performed for the corresponding vehicle + in the vehicles array. If vehiclesToRaycast[i] is true then suspension line raycasts will be performed for vehicles[i]. If vehiclesToRaycast[i] is + false then suspension line raycasts will not be performed for vehicles[i]. + + \note If vehiclesToRaycast is NULL then raycasts are performed for all vehicles in the vehicles array. + + \note If vehiclesToRaycast[i] is false then the vehicle stored in vehicles[i] will automatically use the raycast or sweep hit planes recorded by the most recent + suspension sweeps or raycasts for that vehicle. For vehicles far from the camera or not visible on the screen it can be + optimal to only perform suspension line raycasts every Nth update rather than every single update. The accuracy of the cached contact plane + naturally diminishes as N increase, meaning that wheels might start to hover or intersect the ground for large values of N or even with values close to 1 in + conjunction with large vehicle speeds and/or geometry that has low spatial coherence. + + \note Calling setToRestState invalidates any cached hit planes. Prior to calling PxVehicleUpdates each vehicle needs to perform suspension line raycasts + or sweeps at least once after instantiation and at least once after calling setToRestState. + + \note Each raycast casts along the suspension travel direction from the position of the top of the wheel at maximum suspension compression + to the position of the base of the wheel at maximum droop. Raycasts that start inside a PxShape are subsequently ignored by the + corresponding vehicle. + + \note Only blocking hits are supported (PxQueryHitType::eBLOCK). + + @see PxVehicleDrive4W::setToRestState, PxVehicleDriveNW::setToRestState, PxVehicleDriveTank::setToRestState, PxVehicleNoDrive::setToRestState + */ + void PxVehicleSuspensionRaycasts + (PxBatchQuery* batchQuery, + const PxU32 nbVehicles, PxVehicleWheels** vehicles, + const PxU32 nbSceneQueryResults, PxRaycastQueryResult* sceneQueryResults, + const bool* vehiclesToRaycast = NULL); + + + /** + \brief Perform sweeps for all suspension lines for all vehicles. + + \param[in] batchQuery is a PxBatchQuery instance used to specify shader data and functions for the sweep scene queries. + + \param[in] nbVehicles is the number of vehicles in the vehicles array. + + \param[in] vehicles is an array of all vehicles that are to have a sweep issued from each wheel. + + \param[in] nbSceneQueryResults must be greater than or equal to the total number of wheels of all the vehicles in the vehicles array; that is, + sceneQueryResults must have dimensions large enough for one sweep hit result per wheel for all the vehicles in the vehicles array. + + \param[in] sceneQueryResults must persist without being overwritten until the end of the next PxVehicleUpdates call. + + \param[in] nbHitsPerQuery is the maximum numbers of hits that will be returned for each query. + + \param[in] vehiclesToSweep is an array of bools of length nbVehicles that is used to decide if sweeps will be performed for the corresponding vehicle + in the vehicles array. If vehiclesToSweep[i] is true then suspension sweeps will be performed for vehicles[i]. If vehiclesToSweep[i] is + false then suspension sweeps will not be performed for vehicles[i]. + + \param[in] sweepWidthScale scales the geometry of the wheel used in the sweep. Values < 1 result in a thinner swept wheel, while values > 1 result in a fatter swept wheel. + + \param[in] sweepRadiusScale scales the geometry of the wheel used in the sweep. Values < 1 result in a larger swept wheel, while values > 1 result in a smaller swept wheel. + + \note If vehiclesToSweep is NULL then sweeps are performed for all vehicles in the vehicles array. + + \note If vehiclesToSweep[i] is false then the vehicle stored in vehicles[i] will automatically use the most recent sweep or raycast hit planes + recorded by the most recent suspension sweeps or raycasts for that vehicle. For vehicles far from the camera or not visible on the screen it can be + optimal to only perform suspension queries every Nth update rather than every single update. The accuracy of the cached contact plane + naturally diminishes as N increase, meaning that wheels might start to hover or intersect the ground for large values of N or even with values close to 1 in + conjunction with large vehicle speeds and/or geometry that has low spatial coherence. + + \note Calling setToRestState invalidates any cached hit planes. Prior to calling PxVehicleUpdates each vehicle needs to perform suspension raycasts + or sweeps at least once after instantiation and at least once after calling setToRestState. + + \note Each sweep casts the wheel's shape along the suspension travel direction from the position of the top of the wheel at maximum suspension compression + to the position of the base of the wheel at maximum droop. Sweeps that start inside a PxShape are subsequently ignored by the + corresponding vehicle. + + \note A scale can be applied to the shape so that a modified shape is swept through the scene. The parameters sweepWidthScale and sweepRadiusScale scale the + swept wheel shape in the width and radial directions. It is sometimes a good idea to sweep a thinner wheel to allow contact with other dynamic actors to be resolved + first before attempting to drive on them. + + \note Blocking hits (PxQueryHitType::eBLOCK) and non-blocking hits (PxQueryHitType::TOUCH) are supported. If the pre-and post-filter functions of the PxBatchQuery + instance are set up to return blocking hits it is recommended to set nbHitsPerQuery = 1. If the filter functions returns touch hits then it is recommended to + set nbHitsPerQuery > 1. The exact value depends on the expected complexity of the geometry that lies under the wheel. For complex geometry, especially with dynamic + objects, it is recommended to use non-blocking hits. The vehicle update function will analyze all returned hits and choose the most appropriate using the thresholds + set in PxVehicleSetSweepHitRejectionAngles. + + @see PxVehicleDrive4W::setToRestState, PxVehicleDriveNW::setToRestState, PxVehicleDriveTank::setToRestState, PxVehicleNoDrive::setToRestState + + @see PxBatchQuery::sweep + + @see PxVehicleSetSweepHitRejectionAngles + */ + void PxVehicleSuspensionSweeps + (PxBatchQuery* batchQuery, + const PxU32 nbVehicles, PxVehicleWheels** vehicles, + const PxU32 nbSceneQueryResults, PxSweepQueryResult* sceneQueryResults, const PxU16 nbHitsPerQuery, + const bool* vehiclesToSweep = NULL, + const PxF32 sweepWidthScale = 1.0f, const PxF32 sweepRadiusScale = 1.0f); + + /** + \brief A function called from PxContactModifyCallback::onContactModify. The function determines if rigid body contact points + recorded for the wheel's PxShape are likely to be duplicated and resolved by the wheel's suspension raycast. Contact points that will be + resolved by the suspension are ignored. Contact points that are accepted (rather than ignored) are modified to account for the effect of the + suspension geometry and the angular speed of the wheel. + + \param[in] vehicle is a reference to the PxVehicleWheels instance that owns the wheel + + \param[in] wheelId is the id of the wheel + + \param[in] wheelTangentVelocityMultiplier determines the amount of wheel angular velocity that is used to modify the target relative velocity of the contact. + The target relative velocity is modified by adding a vector equal to the tangent velocity of the rotating wheel at the contact point and scaled by + wheelTangentVelocityMultiplier. The value of wheelTangentVelocityMultiplier is limited to the range (0,1). Higher values mimic higher values of friction + and tire load, while lower values mimic lower values of friction and tire load. + + \param[in] maxImpulse determines the maximum impulse strength that the contacts can apply when a wheel is in contact with a PxRigidDynamic. This value is ignored for + contacts with PxRigidStatic instances. + + \param[in,out] contactModifyPair describes the set of contacts involving the PxShape of the specified wheel and one other shape. The contacts in the contact set are + ignored or modified as required. + + \note[in] Contact points are accepted or rejected using the threshold angles specified in the function PxVehicleSetSweepHitRejectionAngles. + + \note If a contact point is not rejected it is modified to account for the wheel rotation speed. + + \note Set maxImpulse to PX_MAX_F32 to allow any impulse value to be applied. + + \note Reduce maxImpulse if the wheels are frequently colliding with light objects with mass much less than the vehicle's mass. + Reducing this value encourages numerical stability. + + @see PxContactModifyCallback::onContactModify, PxVehicleSetSweepHitRejectionAngles + */ + PxU32 PxVehicleModifyWheelContacts + (const PxVehicleWheels& vehicle, const PxU32 wheelId, + const PxF32 wheelTangentVelocityMultiplier, const PxReal maxImpulse, + PxContactModifyPair& contactModifyPair); + + + /** + \brief Update an array of vehicles by either applying an acceleration to the rigid body actor associated with + each vehicle or by an immediate update of the velocity of the actor. + + \note The update mode (acceleration or velocity change) can be selected with PxVehicleSetUpdateMode. + + \param[in] timestep is the timestep of the update + + \param[in] gravity is the value of gravitational acceleration + + \param[in] vehicleDrivableSurfaceToTireFrictionPairs describes the mapping between each PxMaterial ptr and an integer representing a + surface type. It also stores the friction value for each combination of surface and tire type. + + \param[in] nbVehicles is the number of vehicles pointers in the vehicles array + + \param[in,out] vehicles is an array of length nbVehicles containing all vehicles to be updated by the specified timestep + + \param[out] vehicleWheelQueryResults is an array of length nbVehicles storing the wheel query results of each corresponding vehicle and wheel in the + vehicles array. A NULL pointer is permitted. + + \param[out] vehicleConcurrentUpdates is an array of length nbVehicles. It is only necessary to specify vehicleConcurrentUpdates if PxVehicleUpdates is + called concurrently. The element vehicleWheelQueryResults[i] of the array stores data that is computed for vehicle[i] during PxVehicleUpdates but which + cannot be safely written when concurrently called. The data computed and stored in vehicleConcurrentUpdates must be passed to PxVehiclePostUpdates, where + it is applied to all relevant actors in sequence. A NULL pointer is permitted. + + \note The vehicleWheelQueryResults buffer must persist until the end of PxVehicleUpdates. + + \note The vehicleWheelQueryResults buffer is left unmodified for vehicles with sleeping rigid bodies whose control inputs indicate they should remain inert. + + \note If PxVehicleUpdates is called concurrently then vehicleConcurrentUpdates must be specified. Do not specify vehicleConcurrentUpdates is PxVehicleUpdates + is not called concurrently. + + \note The vehicleConcurrentUpdates buffer must persist until the end of PxVehiclePostUpdate. + + \note If any vehicle has one or more disabled wheels (PxVehicleWheelsSimData::disableWheel) then the disabled wheels must not be associated + with a PxShape (PxVehicleWheelsSimData::setWheelShapeMapping); the differential of the vehicle must be configured so that no drive torque + is delivered to a disabled wheel; and the wheel must have zero rotation speed (PxVehicleWheelsDynData::setWheelRotationSpeed) + + \note PxVehicleUpdates may be called concurrently provided all concurrent calls to PxVehicleUpdates involve only vehicles in the scene specified by PxVehicleUpdateSetScene. + PxVehicleUpdates must never run concurrently with PxVehicleUpdateSingleVehicleAndStoreTelemetryData. + + @see PxVehicleSetUpdateMode, PxVehicleWheelsSimData::disableWheel, PxVehicleWheelsSimData::setWheelShapeMapping, PxVehicleWheelsDynData::setWheelRotationSpeed, + PxVehiclePostUpdates + */ + void PxVehicleUpdates( + const PxReal timestep, const PxVec3& gravity, + const PxVehicleDrivableSurfaceToTireFrictionPairs& vehicleDrivableSurfaceToTireFrictionPairs, + const PxU32 nbVehicles, PxVehicleWheels** vehicles, PxVehicleWheelQueryResult* vehicleWheelQueryResults, PxVehicleConcurrentUpdateData* vehicleConcurrentUpdates = NULL); + + + /** + \brief Apply actor changes that were computed in concurrent calls to PxVehicleUpdates but which could not be safely applied due to the concurrency. + + \param[in] vehicleConcurrentUpdates is an array of length nbVehicles where vehicleConcurrentUpdates[i] contains data describing actor changes that + were computed for vehicles[i] during concurrent calls to PxVehicleUpdates. + + \param[in] nbVehicles is the number of vehicles pointers in the vehicles array + + \param[in,out] vehicles is an array of length nbVehicles containing all vehicles that were partially updated in concurrent calls to PxVehicleUpdates. + + @see PxVehicleUpdates + */ + void PxVehiclePostUpdates( + const PxVehicleConcurrentUpdateData* vehicleConcurrentUpdates, const PxU32 nbVehicles, PxVehicleWheels** vehicles); + + + /** + \brief Shift the origin of vehicles by the specified vector. + + Call this method to adjust the internal data structures of vehicles to reflect the shifted origin location + (the shift vector will get subtracted from all world space spatial data). + + \note It is the user's responsibility to keep track of the summed total origin shift and adjust all input/output to/from PhysXVehicle accordingly. + + \note This call will not automatically shift the PhysX scene and its objects. You need to call PxScene::shiftOrigin() seperately to keep the systems in sync. + + \param[in] shift is the translation vector to shift the origin by. + + \param[in] nbVehicles is the number of vehicles in the vehicles array. + + \param[in,out] vehicles is an array of all vehicles that should be updated to map to the new scene origin. + */ + void PxVehicleShiftOrigin(const PxVec3& shift, const PxU32 nbVehicles, PxVehicleWheels** vehicles); + +#if PX_DEBUG_VEHICLE_ON + /** + \brief Update an single vehicle by either applying an acceleration to the rigid body actor associated with + each vehicle or by an immediate update of the velocity of the actor. Also record telemetry data from the + vehicle so that it may be visualized or queried. + + \note The update mode (acceleration or velocity change) can be selected with PxVehicleSetUpdateMode. + + \param[in] timestep is the timestep of the update + + \param[in] gravity is the value of gravitational acceleration + + \param[in] vehicleDrivableSurfaceToTireFrictionPairs describes the mapping between each PxMaterial ptr and an integer representing a + surface type. It also stores the friction value for each combination of surface and tire type. + + \param[in,out] focusVehicle is the vehicle to be updated and have its telemetry data recorded + + \param[out] vehicleWheelQueryResults is an array of length 1 storing the wheel query results of each wheel of the vehicle/ + A NULL pointer is permitted. + + \param[out] telemetryData is the data structure used to record telemetry data during the update for later query or visualization + + \note The vehicleWheelQueryResults buffer must persist until the end of PxVehicleUpdates + + \note The vehicleWheelQueryResults buffer is left unmodified for vehicles with sleeping rigid bodies whose control inputs indicate they should remain inert. + + \note PxVehicleUpdateSingleVehicleAndStoreTelemetryData is not thread-safe. As a consequence, it must run sequentially and never concurrently with PxVehicleUpdates + + @see PxVehicleSetUpdateMode, PxVehicleTelemetryData + */ + void PxVehicleUpdateSingleVehicleAndStoreTelemetryData + (const PxReal timestep, const PxVec3& gravity, + const PxVehicleDrivableSurfaceToTireFrictionPairs& vehicleDrivableSurfaceToTireFrictionPairs, + PxVehicleWheels* focusVehicle, PxVehicleWheelQueryResult* vehicleWheelQueryResults, + PxVehicleTelemetryData& telemetryData); +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_UPDATE_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUtil.h b/src/PhysX/physx/include/vehicle/PxVehicleUtil.h new file mode 100644 index 000000000..8c67751bc --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleUtil.h @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_UTILHELPER_H +#define PX_VEHICLE_UTILHELPER_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +struct PxVehicleWheelQueryResult; + +/** +\brief Test if all wheels of a vehicle are in the air by querying the wheel query data +stored in the last call to PxVehicleUpdates. If all wheels are in the air then true is returned. + +\note False is returned if any wheel can reach to the ground. + +\note If vehWheelQueryResults.wheelQueryResults is NULL or vehWheelQueryResults.nbWheelQueryResults is 0 then true is returned. +This function does not account for wheels that have been disabled since the last execution of PxVehicleUpdates so it is possible +that wheels disabled more recently than the last call to PxVehicleUpdates report are treated as touching the ground. + +\return True if the vehicle is in the air, false if any wheel is touching the ground. +*/ +bool PxVehicleIsInAir(const PxVehicleWheelQueryResult& vehWheelQueryResults); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_UTILHELPER_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h b/src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h new file mode 100644 index 000000000..3daf06453 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h @@ -0,0 +1,651 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_CONTROL_H +#define PX_VEHICLE_CONTROL_H +/** \addtogroup vehicle + @{ +*/ +#include "vehicle/PxVehicleSDK.h" +#include "vehicle/PxVehicleDrive4W.h" +#include "vehicle/PxVehicleDriveNW.h" +#include "vehicle/PxVehicleDriveTank.h" + + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#if PX_CHECKED + void testValidAnalogValue(const PxF32 actualValue, const PxF32 minVal, const PxF32 maxVal, const char* errorString); +#endif + +/** +\brief Used to produce smooth vehicle driving control values from key inputs. +@see PxVehicle4WSmoothDigitalRawInputsAndSetAnalogInputs, PxVehicle4WSmoothAnalogRawInputsAndSetAnalogInputs +*/ +struct PxVehicleKeySmoothingData +{ +public: + + /** + \brief Rise rate of each analog value if digital value is 1 + */ + PxReal mRiseRates[PxVehicleDriveDynData::eMAX_NB_ANALOG_INPUTS]; + + /** + \brief Fall rate of each analog value if digital value is 0 + */ + PxReal mFallRates[PxVehicleDriveDynData::eMAX_NB_ANALOG_INPUTS]; +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleKeySmoothingData)& 0x0f)); + +/** +\brief Used to produce smooth analog vehicle control values from analog inputs. +@see PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs, PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs +*/ +struct PxVehiclePadSmoothingData +{ +public: + + /** + \brief Rise rate of each analog value from previous value towards target if target>previous + */ + PxReal mRiseRates[PxVehicleDriveDynData::eMAX_NB_ANALOG_INPUTS]; + + /** + \brief Rise rate of each analog value from previous value towards target if target& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDrive4W& focusVehicle); + +/** +\brief Used to smooth and set analog vehicle control values from analog inputs (gamepad). +Also used to set boolean gearup, geardown values. +\param[in] padSmoothing describes how quickly the control values applied to the vehicle blend from the current vehicle values towards the raw analog values from the gamepad. +\param[in] steerVsForwardSpeedTable is a table of maximum allowed steer versus forward vehicle speed. +\param[in] rawInputData is the state of all gamepad analog inputs that will be used control the vehicle. +\param[in] timestep is the time that has passed since the last call to PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs +\param[in] isVehicleInAir describes if the vehicle is in the air or on the ground and is used to decide whether or not to apply steerVsForwardSpeedTable. +\param[in] focusVehicle is the vehicle that will be given analog control values arising from the gamepad inputs. +*/ +void PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs + (const PxVehiclePadSmoothingData& padSmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDrive4W& focusVehicle); + + +/** +\brief Used to produce smooth vehicle driving control values from analog and digital inputs. +@see PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs, PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs +*/ +class PxVehicleDriveNWRawInputData : public PxVehicleDrive4WRawInputData +{ +public: + + PxVehicleDriveNWRawInputData() : PxVehicleDrive4WRawInputData(){} + ~PxVehicleDriveNWRawInputData(){} +}; + +/** +\brief Used to smooth and set analog vehicle control values (accel,brake,handbrake,steer) from digital inputs (keyboard). + Also used to set boolean gearup, geardown values. + \param[in] keySmoothing describes the rise and fall rates of the corresponding analog values when keys are pressed on and off. + \param[in] steerVsForwardSpeedTable is a table of maximum allowed steer versus forward vehicle speed. + \param[in] rawInputData is the state of all digital inputs that control the vehicle. + \param[in] timestep is the time that has passed since the last call to PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs + \param[in] isVehicleInAir describes if the vehicle is in the air or on the ground and is used to decide whether or not to apply steerVsForwardSpeedTable. + \param[in] focusVehicle is the vehicle that will be given analog and gearup/geardown control values arising from the digital inputs. +*/ +void PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs + (const PxVehicleKeySmoothingData& keySmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDriveNWRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDriveNW& focusVehicle); + +/** +\brief Used to smooth and set analog vehicle control values from analog inputs (gamepad). +Also used to set boolean gearup, geardown values. +\param[in] padSmoothing describes how quickly the control values applied to the vehicle blend from the current vehicle values towards the raw analog values from the gamepad. +\param[in] steerVsForwardSpeedTable is a table of maximum allowed steer versus forward vehicle speed. +\param[in] rawInputData is the state of all gamepad analog inputs that will be used control the vehicle. +\param[in] timestep is the time that has passed since the last call to PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs +\param[in] isVehicleInAir describes if the vehicle is in the air or on the ground and is used to decide whether or not to apply steerVsForwardSpeedTable. +\param[in] focusVehicle is the vehicle that will be given analog control values arising from the gamepad inputs. +*/ +void PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs + (const PxVehiclePadSmoothingData& padSmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDriveNWRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDriveNW& focusVehicle); + + +/** +\brief Used to produce smooth analog tank control values from analog and digital inputs. +@see PxVehicleDriveTankSmoothDigitalRawInputsAndSetAnalogInputs, PxVehicleDriveTankSmoothAnalogRawInputsAndSetAnalogInputs +*/ +class PxVehicleDriveTankRawInputData +{ +public: + + PxVehicleDriveTankRawInputData(const PxVehicleDriveTankControlModel::Enum mode) + : mMode(mode) + { + for(PxU32 i=0;iRange: (0,1)
            + */ + PxReal mPosX; + + /** + \brief y-coord of graph centre. + Range: (0,1)
            + */ + PxReal mPosY; + + /** + \brief x-extents of graph (from mPosX-0.5f*mSizeX to mPosX+0.5f*mSizeX). + Range: (0,1)
            + */ + PxReal mSizeX; + + /** + \brief y-extents of graph (from mPosY-0.5f*mSizeY to mPosY+0.5f*mSizeY). + Range: (0,1)
            + */ + PxReal mSizeY; + + /** + \brief Background color of graph. + */ + PxVec3 mBackgroundColor; + + /** + \brief Alpha value of background color. + */ + PxReal mAlpha; + +private: + + bool isValid() const; +}; + +struct PxVehicleGraphChannelDesc +{ +public: + + friend class PxVehicleGraph; + + PxVehicleGraphChannelDesc(); + + /** + \brief Data values less than mMinY will be clamped at mMinY. + */ + PxReal mMinY; + + /** + \brief Data values greater than mMaxY will be clamped at mMaxY. + */ + PxReal mMaxY; + + /** + \brief Data values greater than mMidY will be drawn with color mColorHigh. + Data values less than mMidY will be drawn with color mColorLow. + */ + PxReal mMidY; + + /** + \brief Color used to render data values lower than mMidY. + */ + PxVec3 mColorLow; + + /** + \brief Color used to render data values greater than mMidY. + */ + PxVec3 mColorHigh; + + /** + \brief String to describe data channel. + */ + char* mTitle; + +private: + + bool isValid() const; +}; + +struct PxVehicleWheelGraphChannel +{ + enum Enum + { + eJOUNCE=0, + eSUSPFORCE, + eTIRELOAD, + eNORMALIZED_TIRELOAD, + eWHEEL_OMEGA, + eTIRE_FRICTION, + eTIRE_LONG_SLIP, + eNORM_TIRE_LONG_FORCE, + eTIRE_LAT_SLIP, + eNORM_TIRE_LAT_FORCE, + eNORM_TIRE_ALIGNING_MOMENT, + eMAX_NB_WHEEL_CHANNELS + }; +}; + +struct PxVehicleDriveGraphChannel +{ + enum Enum + { + eENGINE_REVS=0, + eENGINE_DRIVE_TORQUE, + eCLUTCH_SLIP, + eACCEL_CONTROL, //TANK_ACCEL + eBRAKE_CONTROL, //TANK_BRAKE_LEFT + eHANDBRAKE_CONTROL, //TANK_BRAKE_RIGHT + eSTEER_LEFT_CONTROL, //TANK_THRUST_LEFT + eSTEER_RIGHT_CONTROL, //TANK_THRUST_RIGHT + eGEAR_RATIO, + eMAX_NB_DRIVE_CHANNELS + }; +}; + +struct PxVehicleGraphType +{ + enum Enum + { + eWHEEL=0, + eDRIVE + }; +}; + + +class PxVehicleGraph +{ +public: + + friend class PxVehicleTelemetryData; + friend class PxVehicleUpdate; + + enum + { + eMAX_NB_SAMPLES=256 + }; + + enum + { + eMAX_NB_TITLE_CHARS=256 + }; + + enum + { + eMAX_NB_CHANNELS=12 + }; + + /** + \brief Setup a graph from a descriptor. + */ + void setup(const PxVehicleGraphDesc& desc, const PxVehicleGraphType::Enum graphType); + + /** + \brief Clear all data recorded in a graph. + */ + void clearRecordedChannelData(); + + /** + \brief Get the color of the graph background. Used for rendering a graph. + */ + const PxVec3& getBackgroundColor() const {return mBackgroundColor;} + + /** + \brief Get the alpha transparency of the color of the graph background. Used for rendering a graph. + */ + PxReal getBackgroundAlpha() const {return mBackgroundAlpha;} + + /** + \brief Get the coordinates of the graph background. Used for rendering a graph + + \param[out] xMin is the x-coord of the lower-left corner + \param[out] yMin is the y-coord of the lower-left corner + \param[out] xMax is the x-coord of the upper-right corner + \param[out] yMax is the y-coord of the upper-right corner + */ + void getBackgroundCoords(PxReal& xMin, PxReal& yMin, PxReal& xMax, PxReal& yMax) const {xMin = mBackgroundMinX;xMax = mBackgroundMaxX;yMin = mBackgroundMinY;yMax = mBackgroundMaxY;} + + /** + \brief Compute the coordinates of the graph data of a specific graph channel. + + \param[out] xy is an array of graph sample coordinates stored in order x0,y0,x1,y1,x2,y2...xn,yn. + \param[out] colors stores the color of each point on the graph. + \param[out] title is the title of the graph. + */ + void computeGraphChannel(const PxU32 channel, PxReal* xy, PxVec3* colors, char* title) const; + + /** + \brief Return the latest value stored in the specified graph channel + */ + PxF32 getLatestValue(const PxU32 channel) const ; + +private: + + //Min and max of each sample. + PxReal mChannelMinY[eMAX_NB_CHANNELS]; + PxReal mChannelMaxY[eMAX_NB_CHANNELS]; + //Discriminate between high and low values with different colors. + PxReal mChannelMidY[eMAX_NB_CHANNELS]; + //Different colors for values than midY and less than midY. + PxVec3 mChannelColorLow[eMAX_NB_CHANNELS]; + PxVec3 mChannelColorHigh[eMAX_NB_CHANNELS]; + //Title of graph + char mChannelTitle[eMAX_NB_CHANNELS][eMAX_NB_TITLE_CHARS]; + //Graph data. + PxReal mChannelSamples[eMAX_NB_CHANNELS][eMAX_NB_SAMPLES]; + + //Background color,alpha,coords + PxVec3 mBackgroundColor; + PxReal mBackgroundAlpha; + PxReal mBackgroundMinX; + PxReal mBackgroundMaxX; + PxReal mBackgroundMinY; + PxReal mBackgroundMaxY; + + PxU32 mSampleTide; + + PxU32 mNbChannels; + + PxU32 mPad[2]; + + + void setup + (const PxF32 graphSizeX, const PxF32 graphSizeY, + const PxF32 engineGraphPosX, const PxF32 engineGraphPosY, + const PxF32* const wheelGraphPosX, const PxF32* const wheelGraphPosY, + const PxVec3& backgroundColor, const PxVec3& lineColorHigh, const PxVec3& lineColorLow); + + void updateTimeSlice(const PxReal* const samples); + + void setChannel(PxVehicleGraphChannelDesc& desc, const PxU32 channel); + + void setupEngineGraph + (const PxF32 sizeX, const PxF32 sizeY, const PxF32 posX, const PxF32 posY, + const PxVec3& backgoundColor, const PxVec3& lineColorHigh, const PxVec3& lineColorLow); + + void setupWheelGraph + (const PxF32 sizeX, const PxF32 sizeY, const PxF32 posX, const PxF32 posY, + const PxVec3& backgoundColor, const PxVec3& lineColorHigh, const PxVec3& lineColorLow); + + PxVehicleGraph(); + ~PxVehicleGraph(); +}; +PX_COMPILE_TIME_ASSERT(PxU32(PxVehicleGraph::eMAX_NB_CHANNELS) >= PxU32(PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS) && PxU32(PxVehicleGraph::eMAX_NB_CHANNELS) >= PxU32(PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS)); +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleGraph) & 15)); + +class PxVehicleTelemetryData +{ +public: + + friend class PxVehicleUpdate; + + /** + \brief Allocate a PxVehicleNWTelemetryData instance for a vehicle with nbWheels + @see PxVehicleNWTelemetryDataFree + */ + static PxVehicleTelemetryData* allocate(const PxU32 nbWheels); + + /** + \brief Free a PxVehicleNWTelemetryData instance for a vehicle. + @see PxVehicleNWTelemetryDataAllocate + */ + void free(); + + /** + \brief Set up all the graphs so that they are ready to record data. + */ + void setup + (const PxReal graphSizeX, const PxReal graphSizeY, + const PxReal engineGraphPosX, const PxReal engineGraphPosY, + const PxReal* const wheelGraphPosX, const PxReal* const wheelGraphPosY, + const PxVec3& backGroundColor, const PxVec3& lineColorHigh, const PxVec3& lineColorLow); + + /** + \brief Clear the graphs of recorded data. + */ + void clear(); + + /** + \brief Get the graph data for the engine + */ + const PxVehicleGraph& getEngineGraph() const {return *mEngineGraph;} + + /** + \brief Get the number of wheel graphs + */ + PxU32 getNbWheelGraphs() const {return mNbActiveWheels;} + + /** + \brief Get the graph data for the kth wheel + */ + const PxVehicleGraph& getWheelGraph(const PxU32 k) const {return mWheelGraphs[k];} + + /** + \brief Get the array of tire force application points so they can be rendered + */ + const PxVec3* getTireforceAppPoints() const {return mTireforceAppPoints;} + + /** + \brief Get the array of susp force application points so they can be rendered + */ + const PxVec3* getSuspforceAppPoints() const {return mSuspforceAppPoints;} + +private: + + /** + \brief Graph data for engine. + Used for storing single timeslices of debug data for engine graph. + @see PxVehicleGraph + */ + PxVehicleGraph* mEngineGraph; + + /** + \brief Graph data for each wheel. + Used for storing single timeslices of debug data for wheel graphs. + @see PxVehicleGraph + */ + PxVehicleGraph* mWheelGraphs; + + /** + \brief Application point of tire forces. + */ + PxVec3* mTireforceAppPoints; + + /** + \brief Application point of susp forces. + */ + PxVec3* mSuspforceAppPoints; + + /** + \brief Total number of active wheels + */ + PxU32 mNbActiveWheels; + + PxU32 mPad[3]; + +private: + + PxVehicleTelemetryData(){} + ~PxVehicleTelemetryData(){} +}; + +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleTelemetryData) & 15)); + +#endif //PX_DEBUG_VEHICLE_ON + +//#endif // PX_DEBUG_VEHICLE_ON + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_UTILSTELEMETRY_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleWheels.h b/src/PhysX/physx/include/vehicle/PxVehicleWheels.h new file mode 100644 index 000000000..cd36131b4 --- /dev/null +++ b/src/PhysX/physx/include/vehicle/PxVehicleWheels.h @@ -0,0 +1,811 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_VEHICLE_WHEELS_H +#define PX_VEHICLE_WHEELS_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxSimpleTypes.h" +#include "vehicle/PxVehicleShaders.h" +#include "vehicle/PxVehicleComponents.h" +#include "common/PxBase.h" +#include "PxRigidDynamic.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxVehicleWheels4SimData; +class PxVehicleWheels4DynData; +class PxVehicleTireForceCalculator; +class PxShape; +class PxPhysics; +class PxMaterial; + +/** +\brief Data structure describing configuration data of a vehicle with up to 20 wheels. +*/ + +class PxVehicleWheelsSimData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleWheels; + friend class PxVehicleNoDrive; + friend class PxVehicleDrive4W; + friend class PxVehicleDriveTank; + friend class PxVehicleUpdate; + + /** + \brief Allocate a PxVehicleWheelsSimData instance for with nbWheels. + @see free + */ + static PxVehicleWheelsSimData* allocate(const PxU32 nbWheels); + + /** + \brief Setup with mass information that can be applied to the default values of the suspensions, wheels, and tires + set in their respective constructors. + + \param chassisMass is the mass of the chassis. + + \note This function assumes that the suspensions equally share the load of the chassis mass. It also + assumes that the suspension will have a particular natural frequency and damping ratio that is typical + of a standard car. If either of these assumptions is broken then each suspension will need to + be individually configured with custom strength, damping rate, and sprung mass. + + @see allocate + */ + void setChassisMass(const PxF32 chassisMass); + + /** + \brief Free a PxVehicleWheelsSimData instance + @see allocate + */ + void free(); + + /** + \brief Copy wheel simulation data. + \note The number of wheels on both instances of PxVehicleWheelsSimData must match. + */ + PxVehicleWheelsSimData& operator=(const PxVehicleWheelsSimData& src); + + /** + \brief Copy the data of a single wheel unit (wheel, suspension, tire) from srcWheel of src to trgWheel. + \param[in] src is the data to be copied. + \param[in] srcWheel is the wheel whose data will be copied from src. + \param[in] trgWheel is the wheel that will be assigned the copied data. + */ + void copy(const PxVehicleWheelsSimData& src, const PxU32 srcWheel, const PxU32 trgWheel); + + /** + \brief Return the number of wheels + @see allocate + */ + PxU32 getNbWheels() const {return mNbActiveWheels;} + + /** + \brief Return the suspension data of the idth wheel + */ + const PxVehicleSuspensionData& getSuspensionData(const PxU32 id) const; + + /** + \brief Return the wheel data of the idth wheel + */ + const PxVehicleWheelData& getWheelData(const PxU32 id) const; + + /** + \brief Return the tire data of the idth wheel + */ + const PxVehicleTireData& getTireData(const PxU32 id) const; + + /** + \brief Return the direction of travel of the suspension of the idth wheel + */ + const PxVec3& getSuspTravelDirection(const PxU32 id) const; + + /** + \brief Return the application point of the suspension force of the suspension of the idth wheel as an offset from the rigid body center of mass. + \note Specified relative to the center of mass of the rigid body + */ + const PxVec3& getSuspForceAppPointOffset(const PxU32 id) const; + + /** + \brief Return the application point of the tire force of the tire of the idth wheel as an offset from the rigid body center of mass. + \note Specified relative to the centre of mass of the rigid body + */ + const PxVec3& getTireForceAppPointOffset(const PxU32 id) const; + + /** + \brief Return the offset from the rigid body centre of mass to the centre of the idth wheel. + */ + const PxVec3& getWheelCentreOffset(const PxU32 id) const; + + /** + \brief Return the wheel mapping for the ith wheel. + + \note The return value is the element in the array of + shapes of the vehicle's PxRigidDynamic that corresponds to the ith wheel. A return value of -1 means + that the wheel is not mapped to a PxShape. + + @see PxRigidActor.getShapes + */ + PxI32 getWheelShapeMapping(const PxU32 wheelId) const; + + /** + \brief Return the scene query filter data used by the specified suspension line + */ + const PxFilterData& getSceneQueryFilterData(const PxU32 suspId) const; + + /** + \brief Return the number of unique anti-roll bars that have been added with addAntiRollBarData + @see PxVehicleWheelsSimData::addAntiRollBarData + */ + PxU32 getNbAntiRollBars() const + { + return mNbActiveAntiRollBars; + } + + /** + \brief Return a specific anti-roll bar. + \param antiRollId is the unique id of the anti-roll bar + \note The return value of addAntiRollBarData is a unique id for that specific anti-roll bar + and can be used as input parameter for getAntiRollBarData in order to query the same anti-roll bar. + Alternatively, it is possible to iterate over all anti-roll bars by choosing antiRollId + in range (0, getNbAntiRollBars()). + */ + const PxVehicleAntiRollBarData& getAntiRollBarData(const PxU32 antiRollId) const; + + /** + \brief Return the data that describes the filtering of the tire load to produce smoother handling at large time-steps. + */ + PX_FORCE_INLINE const PxVehicleTireLoadFilterData& getTireLoadFilterData() const + { + return mNormalisedLoadFilter; + } + + /** + \brief Set the suspension data of the idth wheel + \param[in] id is the wheel index. + \param[in] susp is the suspension data to be applied. + */ + void setSuspensionData(const PxU32 id, const PxVehicleSuspensionData& susp); + + /** + \brief Set the wheel data of the idth wheel + \param[in] id is the wheel index. + \param[in] wheel is the wheel data to be applied. + */ + void setWheelData(const PxU32 id, const PxVehicleWheelData& wheel); + + /** + \brief Set the tire data of the idth wheel + \param[in] id is the wheel index. + \param[in] tire is the tire data to be applied. + */ + void setTireData(const PxU32 id, const PxVehicleTireData& tire); + + /** + \brief Set the direction of travel of the suspension of the idth wheel + \param[in] id is the wheel index + \param[in] dir is the suspension travel direction to be applied. + */ + void setSuspTravelDirection(const PxU32 id, const PxVec3& dir); + + /** + \brief Set the application point of the suspension force of the suspension of the idth wheel. + \param[in] id is the wheel index + \param[in] offset is the offset from the rigid body center of mass to the application point of the suspension force. + \note Specified relative to the centre of mass of the rigid body + */ + void setSuspForceAppPointOffset(const PxU32 id, const PxVec3& offset); + + /** + \brief Set the application point of the tire force of the tire of the idth wheel. + \param[in] id is the wheel index + \param[in] offset is the offset from the rigid body center of mass to the application point of the tire force. + \note Specified relative to the centre of mass of the rigid body + */ + void setTireForceAppPointOffset(const PxU32 id, const PxVec3& offset); + + /** + \brief Set the offset from the rigid body centre of mass to the centre of the idth wheel. + \param[in] id is the wheel index + \param[in] offset is the offset from the rigid body center of mass to the center of the wheel at rest. + \note Specified relative to the centre of mass of the rigid body + */ + void setWheelCentreOffset(const PxU32 id, const PxVec3& offset); + + /** + \brief Set mapping between wheel id and position of corresponding wheel shape in the list of actor shapes. + + \note This mapping is used to pose the correct wheel shapes with the latest wheel rotation angle, steer angle, and suspension travel + while allowing arbitrary ordering of the wheel shapes in the actor's list of shapes. + + \note Use setWheelShapeMapping(i,-1) to register that there is no wheel shape corresponding to the ith wheel + + \note Set setWheelShapeMapping(i,k) to register that the ith wheel corresponds to the kth shape in the actor's list of shapes. + + \note The default values correspond to setWheelShapeMapping(i,i) for all wheels. + + \note Calling this function will also pose the relevant PxShape at the rest position of the wheel. + + \param wheelId is the wheel index + + \param shapeId is the shape index. + + @see PxVehicleUpdates, PxVehicleDrive4W::setup, PxVehicleDriveTank::setup, PxVehicleNoDrive::setup, setSceneQueryFilterData, PxRigidActor::getShapes + */ + void setWheelShapeMapping(const PxU32 wheelId, const PxI32 shapeId); + + /** + \brief Set the scene query filter data that will be used for raycasts along the travel + direction of the specified suspension. The default value is PxFilterData(0,0,0,0) + \param suspId is the wheel index + \param sqFilterData is the raycast filter data for the suspension raycast. + @see setWheelShapeMapping + */ + void setSceneQueryFilterData(const PxU32 suspId, const PxFilterData& sqFilterData); + + /** + \brief Set the data that describes the filtering of the tire load to produce smoother handling at large timesteps. + \param tireLoadFilter is the smoothing function data. + */ + void setTireLoadFilterData(const PxVehicleTireLoadFilterData& tireLoadFilter); + + /** + \brief Set the anti-roll suspension for a pair of wheels. + + \param antiRoll is the anti-roll suspension. + + \note If an anti-roll bar has already been set for the same logical wheel pair + (independent of wheel index order specified by PxVehicleAntiRollBar.mWheel0 and PxVehicleAntiRollBar.mWheel0) + then the existing anti-roll bar is updated with a new stiffness parameter antiRoll.mStiffness. + + \note If the wheel pair specified by antiRoll does not yet have an anti-roll bar then antiRoll is added to + a list of anti-roll bars for the vehicle. + + \return If antiRoll represents a new wheel pair then a unique id is assigned to the anti-roll bar and returned. + If antiRoll represents an existing wheel pair then the unique id of the existing anti-roll bar is returned. + The return value is always in range (0, getNbAntiRollBars()). + + \note The return value can be used to query the anti-roll bar with getAntiRollBarData(id). + + \note The number of possible anti-roll bars is limited to half the wheel count. + + \note An existing anti-roll bar can be disabled by calling antiRoll.mStiffness to zero. + + @see PxVehicleWheelsSimData::getAntiRollBarData, PxVehicleAntiRollBarData + */ + PxU32 addAntiRollBarData(const PxVehicleAntiRollBarData& antiRoll); + + /** + \brief Disable a wheel so that zero suspension forces and zero tire forces are applied to the rigid body from this wheel. + + \note If the vehicle has a differential (PxVehicleNW/PxVehicle4W) then the differential (PxVehicleDifferentialNWData/PxVehicleDifferential4WData) + needs to be configured so that no drive torque is delivered to the disabled wheel. + + \note If the vehicle is of type PxVehicleNoDrive then zero drive torque must be applied to the disabled wheel. + + \note For tanks (PxVehicleDriveTank) any drive torque that could be delivered to the wheel through the tank differential will be + re-directed to the remaining enabled wheels. + + @see enableWheel + @see PxVehicleDifferentialNWData::setDrivenWheel + @see PxVehicleDifferential4WData::mFrontLeftRightSplit, PxVehicleDifferential4WData::mRearLeftRightSplit, PxVehicleDifferential4WData::mType + @see PxVehicleNoDrive::setDriveTorque + @see PxVehicle4WEnable3WTadpoleMode, PxVehicle4WEnable3WDeltaMode + + \note If a PxShape is associated with the disabled wheel then the association must be broken by calling setWheelShapeMapping(wheelId, -1). + @see setWheelShapeMapping + + \note A wheel that is disabled must also simultaneously be given zero wheel rotation speed. + @see PxVehicleWheelsDynData::setWheelRotationSpeed + + \note Care must be taken with the sprung mass supported by the remaining enabled wheels. Depending on the desired effect, the mass of the rigid body + might need to be distributed among the remaining enabled wheels and suspensions. + + \param[in] wheel is the wheel index. + */ + void disableWheel(const PxU32 wheel); + + /** + \brief Enable a wheel so that suspension forces and tire forces are applied to the rigid body. + All wheels are enabled by default and remain enabled until they are disabled. + \param[in] wheel is the wheel index. + @see disableWheel + */ + void enableWheel(const PxU32 wheel); + + /** + \brief Test if a wheel has been disabled. + \param[in] wheel is the wheel index. + */ + bool getIsWheelDisabled(const PxU32 wheel) const; + + /** + \brief Set the number of vehicle sub-steps that will be performed when the vehicle's longitudinal + speed is below and above a threshold longitudinal speed. + + \note More sub-steps provides better stability but with greater computational cost. + + \note Typically, vehicles require more sub-steps at very low forward speeds. + + \note The threshold longitudinal speed has a default value that is the equivalent of 5 metres per second after accounting for + the length scale set in PxTolerancesScale. + + \note The sub-step count below the threshold longitudinal speed has a default of 3. + + \note The sub-step count above the threshold longitudinal speed has a default of 1. + + \note Each sub-step has time advancement equal to the time-step passed to PxVehicleUpdates divided by the number of required sub-steps. + + \note The contact planes of the most recent suspension line raycast are reused across all sub-steps. + + \note Each sub-step computes tire and suspension forces and then advances a velocity, angular velocity and transform. + + \note At the end of all sub-steps the vehicle actor is given the velocity and angular velocity that would move the actor from its start transform prior + to the first sub-step to the transform computed at the end of the last substep, assuming it doesn't collide with anything along the way in the next PhysX SDK update. + + \note The global pose of the actor is left unchanged throughout the sub-steps. + + \param[in] thresholdLongitudinalSpeed is a threshold speed that is used to categorize vehicle speed as low speed or high speed. + \param[in] lowForwardSpeedSubStepCount is the number of sub-steps performed in PxVehicleUpates for vehicles that have longitudinal speed lower than thresholdLongitudinalSpeed. + \param[in] highForwardSpeedSubStepCount is the number of sub-steps performed in PxVehicleUpdates for vehicles that have longitudinal speed graeter than thresholdLongitudinalSpeed. + */ + void setSubStepCount(const PxReal thresholdLongitudinalSpeed, const PxU32 lowForwardSpeedSubStepCount, const PxU32 highForwardSpeedSubStepCount); + + /** + \brief Set the minimum denominator used in the longitudinal slip calculation. + + \note The longitudinal slip has a theoretical value of (w*r - vz)/|vz|, where w is the angular speed of the wheel; r is the radius of the wheel; + and vz is the component of rigid body velocity (computed at the wheel base) that lies along the longitudinal wheel direction. The term |vz| + normalizes the slip, while preserving the sign of the longitudinal tire slip. The difficulty here is that when |vz| approaches zero the + longitudinal slip approaches infinity. A solution to this problem is to replace the denominator (|vz|) with a value that never falls below a chosen threshold. + The longitudinal slip is then calculated with (w*r - vz)/PxMax(|vz|, minLongSlipDenominator). + + \note The default value is the equivalent of 4 metres per second after accounting for the length scale set in PxTolerancesScale. + + \note Adjust this value upwards if a vehicle has difficulty coming to rest. + + \note Decreasing the timestep (or increasing the number of sub-steps at low longitudinal speed with setSubStepCount) should allow stable stable + behavior with smaller values of minLongSlipDenominator. + */ + void setMinLongSlipDenominator(const PxReal minLongSlipDenominator); + +private: + + /** + \brief Graph to filter normalised load + @see setTireLoadFilterData, getTireLoadFilterData + */ + PxVehicleTireLoadFilterData mNormalisedLoadFilter; + + /** + \brief Wheels data organised in blocks of 4 wheels. + */ + PxVehicleWheels4SimData* mWheels4SimData; + + /** + \brief Number of blocks of 4 wheels. + */ + PxU32 mNbWheels4; + + /** + \brief Number of actual wheels (<=(mNbWheels4*4)) + */ + PxU32 mNbActiveWheels; + + /** + \brief Anti-roll bars + */ + PxVehicleAntiRollBarData* mAntiRollBars; + + /** + \brief 2 anti-rollbars allocated for each block of 4 wheels. + */ + PxU32 mNbAntiRollBars4; + + /** + \brief Number of active anti-roll bars. + */ + PxU32 mNbActiveAntiRollBars; + + /** + \brief Which of the mNbActiveWheels are active or disabled? + The default is that all mNbActiveWheels wheels are active. + */ + PxU32 mActiveWheelsBitmapBuffer[((PX_MAX_NB_WHEELS + 31) & ~31) >> 5]; + + /** + \brief Threshold longitudinal speed used to decide whether to use + mLowForwardSpeedSubStepCount or mHighForwardSpeedSubStepCount as the + number of sub-steps that will be peformed. + */ + PxF32 mThresholdLongitudinalSpeed; + + /** + \brief Number of sub-steps that will be performed if the longitudinal speed + of the vehicle is smaller than mThresholdLongitudinalSpeed. + */ + PxU32 mLowForwardSpeedSubStepCount; + + /** + \brief Number of sub-steps that will be performed if the longitudinal speed + of the vehicle is greater than or equal to mThresholdLongitudinalSpeed. + */ + PxU32 mHighForwardSpeedSubStepCount; + + /** + \brief Minimum long slip denominator + */ + PxF32 mMinLongSlipDenominator; + +#if PX_P64_FAMILY + PxU32 mPad[2]; +#else + PxU32 mPad[1]; +#endif + + /** + \brief Test if wheel simulation data has been setup with legal values. + */ + bool isValid() const; + + /** + \brief see PxVehicleWheels::allocate + */ + static PxU32 computeByteSize(const PxU32 numWheels); + static PxU8* patchUpPointers(const PxU32 numWheels, PxVehicleWheelsSimData* simData, PxU8* ptrIn); + PxVehicleWheelsSimData(const PxU32 numWheels); + +//serialization +public: + PxVehicleWheelsSimData(const PxEMPTY) : mNormalisedLoadFilter(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); + PxU32 getNbWheels4() const { return mNbWheels4; } + PxU32 getNbSuspensionData() const { return mNbActiveWheels; } + PxU32 getNbWheelData() const { return mNbActiveWheels; } + PxU32 getNbSuspTravelDirection() const { return mNbActiveWheels; } + PxU32 getNbTireData() const { return mNbActiveWheels; } + PxU32 getNbSuspForceAppPointOffset() const { return mNbActiveWheels; } + PxU32 getNbTireForceAppPointOffset() const { return mNbActiveWheels; } + PxU32 getNbWheelCentreOffset() const { return mNbActiveWheels; } + PxU32 getNbWheelShapeMapping() const { return mNbActiveWheels; } + PxU32 getNbSceneQueryFilterData() const { return mNbActiveWheels; } + PxF32 getMinLongSlipDenominator() const {return mMinLongSlipDenominator;} + void setThresholdLongSpeed(const PxF32 f) {mThresholdLongitudinalSpeed = f;} + PxF32 getThresholdLongSpeed() const {return mThresholdLongitudinalSpeed;} + void setLowForwardSpeedSubStepCount(const PxU32 f) {mLowForwardSpeedSubStepCount = f;} + PxU32 getLowForwardSpeedSubStepCount() const {return mLowForwardSpeedSubStepCount;} + void setHighForwardSpeedSubStepCount(const PxU32 f) {mHighForwardSpeedSubStepCount = f;} + PxU32 getHighForwardSpeedSubStepCount() const {return mHighForwardSpeedSubStepCount;} + void setWheelEnabledState(const PxU32 wheel, const bool state) {if(state) {enableWheel(wheel);} else {disableWheel(wheel);}} + bool getWheelEnabledState(const PxU32 wheel) const {return !getIsWheelDisabled(wheel);} + PxU32 getNbWheelEnabledState() const {return mNbActiveWheels;} + PxU32 getNbAntiRollBars4() const { return mNbAntiRollBars4; } + PxU32 getNbAntiRollBarData() const {return mNbActiveAntiRollBars;} + void setAntiRollBarData(const PxU32 id, const PxVehicleAntiRollBarData& antiRoll); + PxVehicleWheelsSimData(){} + ~PxVehicleWheelsSimData(){} +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleWheelsSimData) & 15)); + +/** +\brief Data structure with instanced dynamics data for wheels +*/ +class PxVehicleWheelsDynData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleWheels; + friend class PxVehicleDrive4W; + friend class PxVehicleDriveTank; + friend class PxVehicleUpdate; + + PxVehicleWheelsDynData(){} + ~PxVehicleWheelsDynData(){} + + /** + \brief Set all wheels to their rest state. + @see setup + */ + void setToRestState(); + + /** + \brief Set the tire force shader function + \param[in] tireForceShaderFn is the shader function that will be used to compute tire forces. + */ + void setTireForceShaderFunction(PxVehicleComputeTireForce tireForceShaderFn); + + /** + \brief Set the tire force shader data for a specific tire + \param[in] tireId is the wheel index + \param[in] tireForceShaderData is the data describing the tire. + */ + void setTireForceShaderData(const PxU32 tireId, const void* tireForceShaderData); + + /** + \brief Get the tire force shader data for a specific tire + */ + const void* getTireForceShaderData(const PxU32 tireId) const; + + /** + \brief Set the wheel rotation speed (radians per second) about the rolling axis for the specified wheel. + \param[in] wheelIdx is the wheel index + \param[in] speed is the rotation speed to be applied to the wheel. + */ + void setWheelRotationSpeed(const PxU32 wheelIdx, const PxReal speed); + + /** + \brief Return the rotation speed about the rolling axis of a specified wheel . + */ + PxReal getWheelRotationSpeed(const PxU32 wheelIdx) const; + + /** + \brief Set the wheel rotation angle (radians) about the rolling axis of the specified wheel. + \param[in] wheelIdx is the wheel index + \param[in] angle is the rotation angle to be applied to the wheel. + */ + void setWheelRotationAngle(const PxU32 wheelIdx, const PxReal angle); + + /** + \brief Return the rotation angle about the rolling axis for the specified wheel. + */ + PxReal getWheelRotationAngle(const PxU32 wheelIdx) const; + + /** + \brief Set the user data pointer for the specified wheel + It has a default value of NULL. + \param[in] tireIdx is the wheel index + \param[in] userData is the data to be associated with the wheel. + */ + void setUserData(const PxU32 tireIdx, void* userData); + + /** + \brief Get the user data pointer that was set for the specified wheel + */ + void* getUserData(const PxU32 tireIdx) const; + + /** + \brief Copy the dynamics data of a single wheel unit (wheel, suspension, tire) from srcWheel of src to trgWheel. + \param[in] src is the data to be copied. + \param[in] srcWheel is the wheel whose data will be copied from src. + \param[in] trgWheel is the wheel that will be assigned the copied data. + */ + void copy(const PxVehicleWheelsDynData& src, const PxU32 srcWheel, const PxU32 trgWheel); + +private: + + /** + \brief Dynamics data arranged in blocks of 4 wheels. + */ + PxVehicleWheels4DynData* mWheels4DynData; + + /** + \brief Test if wheel dynamics data have legal values. + */ + bool isValid() const; + + /** + \brief Shader data and function for tire force calculations. + */ + PxVehicleTireForceCalculator* mTireForceCalculators; + + /** + \brief A userData pointer can be stored for each wheel. + @see setUserData, getUserData + */ + void** mUserDatas; + + /** + \brief Number of blocks of 4 wheels. + */ + PxU32 mNbWheels4; + + /** + \brief Number of wheels (mNbActiveWheels <= (mNbWheels4*4)) + */ + PxU32 mNbActiveWheels; + + PxU32 mPad[3]; + + /** + \brief see PxVehicleWheels::allocate + */ + static PxU32 computeByteSize(const PxU32 numWheels); + static PxU8* patchUpPointers(const PxU32 numWheels, PxVehicleWheelsDynData* dynData, PxU8* ptr); + PxVehicleWheelsDynData(const PxU32 numWheels); + +//serialization +public: + static void getBinaryMetaData(PxOutputStream& stream); + PxU32 getNbWheelRotationSpeed() const { return mNbActiveWheels; } + PxU32 getNbWheelRotationAngle() const { return mNbActiveWheels; } + PxVehicleWheels4DynData* getWheel4DynData() const { return mWheels4DynData; } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleWheelsDynData) & 15)); + +/** +\brief Data structure with instanced dynamics data and configuration data of a vehicle with just wheels +@see PxVehicleDrive, PxVehicleDrive4W, PxVehicleDriveTank +*/ +class PxVehicleWheels : public PxBase +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + friend class PxVehicleConstraintShader; + + /** + \brief Return the type of vehicle + @see PxVehicleTypes + */ + PX_FORCE_INLINE PxU32 getVehicleType() const {return mType;} + + /** + \brief Get non-const ptr to PxRigidDynamic instance that is the vehicle's physx representation + */ + PX_FORCE_INLINE PxRigidDynamic* getRigidDynamicActor() {return mActor;} + + /** + \brief Get const ptr to PxRigidDynamic instance that is the vehicle's physx representation + */ + PX_FORCE_INLINE const PxRigidDynamic* getRigidDynamicActor() const {return mActor;} + + /** + \brief Compute the rigid body velocity component along the forward vector of the rigid body transform. + @see PxVehicleSetBasisVectors + */ + PxReal computeForwardSpeed() const; + + /** + \brief Compute the rigid body velocity component along the right vector of the rigid body transform. + @see PxVehicleSetBasisVectors + */ + PxReal computeSidewaysSpeed() const; + + /** + \brief Data describing the setup of all the wheels/suspensions/tires. + */ + PxVehicleWheelsSimData mWheelsSimData; + + /** + \brief Data describing the dynamic state of all wheels/suspension/tires. + */ + PxVehicleWheelsDynData mWheelsDynData; + +protected: + + /** + \brief Set all wheels to their rest state + */ + void setToRestState(); + + /** + \brief Test that all configuration and instanced dynamics data is valid. + */ + bool isValid() const; + + /** + @see PxVehicleDrive4W::allocate, PxVehicleDriveTank::allocate + */ + static PxU32 computeByteSize(const PxU32 nbWheels); + static PxU8* patchupPointers(const PxU32 nbWheels, PxVehicleWheels* vehWheels, PxU8* ptr); + virtual void init(const PxU32 numWheels); + + /** + \brief Deallocate a PxVehicleWheels instance. + @see PxVehicleDrive4W::free, PxVehicleDriveTank::free + */ + void free(); + + /** + @see PxVehicleDrive4W::setup, PxVehicleDriveTank::setup + */ + void setup + (PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, + const PxU32 nbDrivenWheels, const PxU32 nbNonDrivenWheels); + + /** + \brief The rigid body actor that represents the vehicle in the PhysX SDK. + */ + PxRigidDynamic* mActor; + +private: + + /** + \brief Count the number of constraint connectors that have hit their callback when deleting a vehicle. + Can only delete the vehicle's memory when all constraint connectors have hit their callback. + */ + PxU32 mNbNonDrivenWheels; + + PxU8 mOnConstraintReleaseCounter; + +protected: + + /** + \brief Vehicle type (eVehicleDriveTypes) + */ + PxU8 mType; + +#if PX_P64_FAMILY + PxU8 mPad0[14]; +#else + PxU8 mPad0[14]; +#endif + +//serialization +public: + virtual void requiresObjects(PxProcessPxBaseCallback& c); + virtual const char* getConcreteTypeName() const { return "PxVehicleWheels"; } + virtual bool isKindOf(const char* name) const { return !::strcmp("PxVehicleWheels", name) || PxBase::isKindOf(name); } + virtual void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext&); + void resolveReferences(PxDeserializationContext&); + static void getBinaryMetaData(PxOutputStream& stream); + PX_FORCE_INLINE PxU32 getNbNonDrivenWheels() const { return mNbNonDrivenWheels; } + PxVehicleWheels(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PxVehicleWheels(PxBaseFlags baseFlags) : PxBase(baseFlags), mWheelsSimData(PxEmpty) {} + virtual ~PxVehicleWheels() {} + virtual void release() { free(); } +//~serialization +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleWheels) & 15)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_WHEELS_H diff --git a/src/PhysX/physx/platform_readme.html b/src/PhysX/physx/platform_readme.html new file mode 100644 index 000000000..8d9c05553 --- /dev/null +++ b/src/PhysX/physx/platform_readme.html @@ -0,0 +1,16 @@ + + + + +Platform Specific Readme Files + + + + + + +


            +Copyright (C) 2008-2018 NVIDIA Corporation, 2701 San Thomas Expressway, Santa Clara, CA 95050 U.S.A. All rights reserved.
            www.nvidia.com +

            + + diff --git a/src/PhysX/physx/release_notes.html b/src/PhysX/physx/release_notes.html new file mode 100644 index 000000000..ea4e7b40f --- /dev/null +++ b/src/PhysX/physx/release_notes.html @@ -0,0 +1,3680 @@ + + + + + Release Notes - NVIDIA PhysX SDK 4.0 + + + + + + + +

            Release Notes - NVIDIA® PhysX® SDK 4.0

            +

            December 2018

            + +

            Supported Platforms

            + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
            + Runtime + Development
            + Apple iOS (tested on 12.1) + Xcode (tested with 10.1)
            + Apple macOS (tested on 10.13) + Xcode (tested with 10.1)
            + Google Android ARM (tested with API Level 19 - KITKAT) + NDK r13b
            + Linux (tested on Ubuntu 16.04), GPU acceleration: display driver and GPU supporting CUDA 10 / CUDA ARCH 3.0 + Clang (tested with 3.8)
            + Microsoft Windows, GPU acceleration: display driver and GPU supporting CUDA 10 / CUDA ARCH 3.0 + Microsoft Visual Studio 2013, 2015, 2017
            + Microsoft XBox One* +
            + Nintendo Switch* +
            + Sony Playstation 4* +
            + + * Console code subject to platform NDA not available on GitHub. Developers licensed by respective platform owners please contact NVIDIA for access. + +

            Changes and Resolved Issues

            +
            + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
            + +

            General

            +
              +
            • Added:
            • +
                +
              • New Temporal Gauss Seidel (TGS) solver offering a new level of simulation accuracy.
              • +
              • New Reduced Coordinate Articulation feature with no relative positional error and realistic actuation.
              • +
              • New automatic multi-broadphase (ABP) providing better out of the box performance for many use cases.
              • +
              • New BVH structure supporting better performance for actors with many shapes.
              • +
              +
            • Removed:
            • +
                +
              • PhysX Particle feature.
              • +
              • PhysX Cloth feature.
              • +
              • The deprecated active transforms feature has been removed. Please use active actors instead.
              • +
              • The deprecated multi client behavior feature has been removed.
              • +
              • The deprecated legacy heightfields have been removed.
              • +
              +
            • Changed:
            • +
                +
              • The PhysX SDK build system is now based on CMake generated build configuration files. For more details, please refer to the PhysX SDK 4.0 Migration Guide.
              • +
              • The Linux build has been changed to produce static as opposed to shared libraries. The compiler was switched from GCC to Clang.
              • +
              • The PxShared library contains functionality shared beyond the PhysX SDK. It has been streamlined to a minimal set of headers. The PxFoundation singleton has been moved back to the PhysX SDK, as well as the task manager, CUDA context manager and PhysX Visual Debugger (PVD) functionality.
              • +
              • PhysXDelayLoadHook and PhysXGpuLoadHook have been simplified, and PxFoundationDelayLoadHook has been removed.
              • +
              +
            + +

            Rigid Bodies

            +
              +
            • Added:
            • +
                +
              • A new broadphase implementation has been added. See PxBroadPhaseType::eABP for details. This is now the default broadphase implementation.
              • +
              • TGS: A new rigid body solver, which can produce improved convergence compared to the default rigid body solver.
              • +
              • Optional torsional friction model to simulate rotational friction when there is just a single point of contact.
              • +
              • A flag to enable friction constraints to be processed every frame has been added
              • +
              • PxContactJoint added to represent contacts in inverse dynamics. Not intended for use in simulation.
              • +
              • A missing PxShape::getReferenceCount() function has been added.
              • +
              +
            • Removed:
            • +
                +
              • PxVisualizationParameter::eDEPRECATED_BODY_JOINT_GROUPS has been removed.
              • +
              • PxSceneDesc::maxNbObjectsPerRegion has been removed.
              • +
              • PxRigidActor::createShape() has been removed. Please use PxPhysics::createShape() or PxRigidActorExt::createExclusiveShape() instead
              • +
              • The deprecated mass parameter in PxTolerancesScale has been removed.
              • +
              • PxSceneFlag::eDEPRECATED_TRIGGER_TRIGGER_REPORTS has been removed.
              • +
              +
            • Changed:
            • +
                +
              • Aggregates can now contain more than 128 actors.
              • +
              • Switching a kinematic object to dynamic does not automatically wake up the object anymore. Explicit calls to PxRigidDynamic::wakeUp() are now needed.
              • +
              • Switching a kinematic object to dynamic re-inserts the object into the broadphase, producing PxPairFlag::eNOTIFY_TOUCH_FOUND events instead of PxPairFlag::eNOTIFY_TOUCH_PERSISTS events.
              • +
              • PxConvexMeshGeometryFlag::eTIGHT_BOUNDS is now enabled by default for PxConvexMeshGeometry.
              • +
              • The default max angular velocity for rigid bodies has been changed, from 7 to 100.
              • + +
              +
            + + +

            Extensions

            +
              +
            • Added:
            • +
                +
              • PxD6Joint now supports per-axis linear limit pairs.
              • +
              • Added PxSphericalJoint::getSwingYAngle and PxSphericalJoint::getSwingZAngle.
              • +
              • Added PxD6Joint distance limit debug visualization.
              • +
              • Added PxD6JointCreate.h file with helper functions to setup the D6 joint in various common configurations.
              • +
              • Added pyramidal swing limits to the D6 joint.
              • +
              + +
            • Removed:
            • +
                +
              • PxComputeHeightFieldPenetration has a new signature.
              • +
              • PxComputeMeshPenetration has been removed. Use PxComputeTriangleMeshPenetration instead.
              • +
              + +
            • Changed:
            • +
                +
              • PxRevoluteJoint now properly supports a -PI*2 to +PI*2 range for its limits, and the accuracy of limits has been improved. In order to use extended limit ranges, PxConstraintFlag::eENABLE_EXTENDED_LIMITS must be raised on the constraint.
              • +
              • PxD6Joint now properly supports a -PI*2 to +PI*2 range for its twist limit, and the accuracy of the twist limit has been improved. In order to use extended limit ranges, PxConstraintFlag::eENABLE_EXTENDED_LIMITS must be raised on the constraint.
              • +
              • The accuracy of the D6 joint swing limits has been improved.
              • +
              • PxDistanceJoint does now always insert constraint row, this change does increase the limit precision.
              • +
              • PxDistanceJoint::getDistance does not anymore return squared distance.
              • +
              • PxD6Joint::setDriveVelocity, PxD6Joint::setDrivePosition and PxRevoluteJoint::setDriveVelocity have now additional parameter autowake, which will wake the joint rigids up if true (default behavior).
              • +
              • Joint shaders now take a bool to define whether to use extended joint limits or not.
              • +
              • Joint shaders must now provide the cA2w and cB2w vectors, defining the world-space location of the joint anchors for both bodies.
              • +
              + +
            • Deprecated:
            • +
                +
              • PxD6Joint::getTwist() has been deprecated. Please use PxD6Joint::getTwistAngle() now.
              • +
              • The previous PxD6Joint::setLinearLimit() and PxD6Joint::getLinearLimit() functions (supporting a single linear limit value) have been deprecated. Please use PxD6Joint::setDistanceLimit() and PxD6Joint::getDistanceLimit() instead. Or you can also use the new PxD6Joint::setLinearLimit() and PxD6Joint::getLinearLimit() functions, which now support pairs of linear limit values.
              • +
              +
            + +

            Articulations

            +
              +
            • Added:
            • +
                +
              • New reduced coordinate articulation implementation, supporting a wider range of joint types, more accurate drive model, inverse dynamics and joint torque control.
              • +
              • PxArticulationJoint::getParentArticulationLink and PxArticulationJoint::getChildArticulationLink has been added.
              • +
              +
            + +

            Scene queries

            +
              +
            • Removed:
            • +
                +
              • PxHitFlag::eDISTANCE has been removed.
              • +
              • The PxVolumeCache feature has been removed.
              • +
              • The PxSpatialIndex feature has been removed.
              • +
              • The deprecated PxSceneFlag::eSUPPRESS_EAGER_SCENE_QUERY_REFIT has been removed.
              • +
              +
            + +

            Cooking

            +
              +
            • Added:
            • +
                +
              • PxBVHStructure added, it computes and stores BVH structure for given bounds. The structure can be used for actors with large amount of shapes to perform scene queries actor centric rather than shape centric. For more information please see guide or snippets.
              • +
              +
            • Removed:
            • +
                +
              • PxPlatform enum has been removed. PhysX supported platforms all share the same endianness
              • +
              • The deprecated PxCookingParams::meshCookingHint and PxCookingParams::meshSizePerformanceTradeOff parameters have been removed.
              • +
              • The deprecated PxGetGaussMapVertexLimitForPlatform has been removed, use PxCookingParams::gaussMapLimit instead.
              • +
              • The deprecated PxConvexMeshCookingType::eINFLATION_INCREMENTAL_HULL and PxCookingParams::skinWidth have been removed.
              • +
              • PxBVH34MidphaseDesc::numTrisPerLeaf has been renamed to PxBVH34MidphaseDesc::numPrimsPerLeaf
              • +
              +
            + + +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.4.2.25354359

            +

            December 2018

            + +

            General

            +
              +
            • Changed:
            • +
                +
              • Changed GitHub distribution to BSD license.
              • +
              +
            + +

            Supported Platforms

            + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
            + Runtime + Development
            + Apple iOS (tested on 12.1) + Xcode (tested with 10.1)
            + Apple macOS (tested on 10.13) + Xcode (tested with 10.1)
            + Google Android ARM (tested with API Level 16, Android 4.1 - JELLY_BEAN) + NDK r13b-win32
            + Linux (tested on Ubuntu 16.04, GPU acceleration: NVIDIA Driver version R361+ and CUDA ARCH 3.0) + GCC (tested with 4.8)
            + Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration) + Microsoft Visual Studio 2012, 2013, 2015
            + Microsoft XBox One* +
            + Nintendo Switch* +
            + Sony Playstation 4* +
            + + * Console code subject to platform NDA not available on GitHub. Developers licensed by respective platform owners please contact NVIDIA for access. + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.4.2.25256367

            +

            November 2018

            + +

            General

            +
              +
            • Changed:
            • +
                +
              • A couple of serialization write functions have been optimized.
              • +
              • Rtree cooking has been slightly optimized.
              • +
              • Renamed PxSerializer::requires to PxSerializer::requiresObjects due to an erroneous clash with C++20 keyword with apple-clang.
              • +
              +
            • Fixed:
            • +
            • Moved external vector math includes out of PhysX namespaces.
            • +
            + + +

            Rigid Bodies

            +
              +
            • Fixed:
            • +
                +
              • Fixed an incorrect trigger behavior when a trigger was removed and inserted within the same frame.
              • +
              +
            + +

            Scene query

            +
              +
            • Fixed:
            • +
                +
              • Fixed a bug in BVH34. Raycasts could fail on binary deserialized BVH34 triangle meshes.
              • +
              +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.4.2.24990349

            +

            September 2018

            + +

            General

            +
              +
            • Fixed:
            • +
                +
              • PxMeshQuery::getTriangle adjacency information for heightfield geometry fixed.
              • +
              • Removed PxSetPhysXGpuDelayLoadHook from API. Delay loaded dynamically linked library names are now provided through PxSetPhysXDelayLoadHook.
              • +
              • Fixed a source of non-determinism with GPU rigid bodies.
              • +
              +
            + +

            Rigid Bodies

            +
              +
            • Fixed:
            • +
                +
              • Fixed a divide by zero bug when gjk is trying to calculate the barycentric coordinate for two identical/nearly identical points.
              • +
              • Fixed an incorrect mesh index reported in contact buffer when stabilization flag was used.
              • +
              +
            + + + +

            Release Notes - NVIDIA® PhysX® SDK 3.4.2.24698370

            +

            August 2018

            + +

            Rigid Bodies

            +
              +
            • Fixed:
            • +
                +
              • Fixed a crash bug when EPA's edge buffer overflow.
              • +
              • GPU rigid bodies fixed for Volta GPUs.
              • +
              +
            • Added:
            • +
                +
              • Aggregate broad phase now runs in parallel
              • +
              +
            + +

            Cooking

            +
              +
            • Fixed:
            • +
                +
              • Added:
              • +
                  +
                • PxHeightField::getSample has been added.
                • +
                +
              + +

              Character controller

              +
                +
              • Fixed:
              • +
                  +
                • Capsule controller with a very small height could degenerate to spheres (with a height of exactly zero) far away from the origin, which would then triggers errors in Debug/Checked builds. This has been fixed.
                • +
                • The triangle array growing strategy has been changed again to prevent performance issues when tessellation is used. Memory usage may increase in these cases.
                • +
                • Some internal debug visualization code has been disabled and a crash in it has been fixed.
                • +
                +
              + +

              Scene query

              +
                +
              • Fixed:
              • +
                  +
                • Fixed possible buffer overrun when PxPruningStructure was used.
                • +
                • Raycasts against a heightfield may have missed if a large distance was used.
                • +
                • Sq raycasts against heightfield or triangle mesh could return a mildly negative values, this has been fixed.
                • +
                +
              + + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.2.24214033

              +

              May 2018

              + +

              General

              +
                +
              • Fixed:
              • +
                  +
                • Fixed clang 7 unionCast issues.
                • +
                • Fixed the binary meta data in PhysX_3.4/Tools/BinaryMetaData for conversion of vehicles.
                • +
                +
              + +

              Rigid Bodies

              +
                +
              • Deprecated:
              • +
                  +
                • PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS and PxSceneDesc::eENABLE_KINEMATIC_PAIRS have been deprecated. Use the new PxPairFilteringMode parameters in PxSceneDesc instead.
                • +
                +
              • Fixed:
              • +
                  +
                • A sequence of shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, false) / shape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, true) without simulation calls inbetween could produce errors in the broadphase. This has been fixed.
                • +
                • Fixed a bug in the broadphase (SAP) that got recently introduced in SDK 3.4.1.23933511. It should only have generated a few false positives (more overlaps than stricly necessary).
                • +
                • Fixed a bug in PxShape::checkMaterialSetup.
                • +
                • Fixed intermittent crash with GPU rigid bodies when materials were destroyed.
                • +
                • Fixed bug where setting maxImpulse to 0 on CCD contact modification meant contact was not reported in the frame's contact reports.
                • +
                +
              + +

              Cooking

              +
                +
              • Fixed:
              • +
                  +
                • Big convex mesh serialization used together with insertion callback stored incorrect memory for big convex data. This has been fixed.
                • +
                +
              + +

              Scene query

              +
                +
              • Fixed:
              • +
                  +
                • Fixed a bug in extended bucket pruner, when a pruning structure was added and immediatelly released.
                • +
                +
              + + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.1.23933511

              +

              April 2018

              + +

              General

              +
                +
              • Added:
              • +
                  +
                • Added snippet for deformable meshes, added section in the guide for them.
                • +
                +
              + +

              Rigid Bodies

              +
                +
              • Fixed:
              • +
                  +
                • PxTriangleMesh::refitBVH() was crashing with PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE. This has been fixed.
                • +
                • SQ-only shapes contained in aggregates could result in crashes or memory corruption when removed from the aggregate. This has been fixed.
                • +
                • Assert no longer fired if a dynamic body contacting a static is converted to kinematic with kinematic-static pairs enabled.
                • +
                • Assert reporting broad phase as being "inconsistent" could fire when using speculative CCD when sleeping objects were activated.
                • +
                +
              + +

              Cooking

              +
                +
              • Fixed:
              • +
                  +
                • Convex hull cooking could have produced hulls with vertices too far from convex hull planes, this has been fixed.
                • +
                +
              • Changed:
              • +
                  +
                • PxCooking::createTriangleMesh does now take additional output parameter PxTriangleMeshCookingResult::Enum.
                • +
                • PxCooking::createConvexMesh does now take additional output parameter PxConvexMeshCookingResult::Enum.
                • +
                +
              + +

              Scene query

              +
                +
              • Changed:
              • +
                  +
                • PxSceneFlag::eSUPPRESS_EAGER_SCENE_QUERY_REFIT has been marked as deprecated. Was replaced with PxSceneQueryUpdateMode enum, if this new enum is set the flag gets ignored.
                • +
                +
              • Added:
              • +
                  +
                • PxSceneQueryUpdateMode was added to control work done during fetchResults.
                • +
                • PxScene::sceneQueriesUpdate, PxScene::checkQueries and PxScene::fetchQueries were added to run separate scene query update, see manual for more details.
                • +
                +
              + + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.1.23584284

              +

              February 2018

              + +

              General

              +
                +
              • Fixed:
              • +
                  +
                • PhysX sometimes froze in a spinlock after certain sequences of read & write locks. This has been fixed.
                • +
                +
              + +

              Scene queries

              +
                +
              • Fixed:
              • +
                  +
                • Raycasts against heightfields were sometimes missing hits for vertical rays were located exactly at the heightfield's boundaries. This has been fixed.
                • +
                +
              + +

              Rigid Bodies

              +
                +
              • Fixed:
              • +
                  +
                • Avoid edge-face collisions on boundary edges when eNO_BOUNDARY_EDGES flag is raised on heightfield when using PCM and unified HF collision.
                • +
                +
              + + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.1.23472123

              +

              January 2018

              + +

              General

              +
                +
              • Added:
              • +
                  +
                • Visual Studio 2017 15.5.1 and newer is now supported. Samples are currently not supported with Visual Studio 2017.
                • +
                + +
              • Removed:
              • +
                  +
                • Visual Studio 2012 support is discontinued.
                • +
                +
              + +

              Cooking

              +
                +
              • Fixed:
              • +
                  +
                • Cooked mesh structures contained a mix of little-endian and big-endian data (the midphase structures were always saved as big-endian). This made loading of cooked files slower than necessary. This has been fixed.
                • +
                +
              + +

              Scene queries

              +
                +
              • Fixed:
              • +
                  +
                • Buffered moves were sometimes not properly taken into account by scene queries, leading to invalid results for one frame. This has been fixed.
                • +
                • Pruning structure failed to build when actor had more shapes. This has been fixed.
                • +
                +
              + + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.1.23173160

              +

              November 2017

              + +

              Extensions

              +
                +
              • Fixed:
              • +
                  +
                • An issue with CCD sweeps against meshes that could potentially lead to the earliest impact not being detected has been fixed.
                • +
                +
              + + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.1.23131702

              +

              November 2017

              + +

              General

              +
                +
              • Fixed:
              • +
                  +
                • A bug in the management of internal interaction objects has been fixed.
                • +
                +
              + +

              Extensions

              +
                +
              • Fixed:
              • +
                  +
                • A regression in prismatic constraint stability introduced in PhysX 3.4.1 has been fixed.
                • +
                • PxRevoluteJoint::getAngle(), PxD6Joint::getTwist(), PxD6Joint::getSwingYAngle() and PxD6Joint::getSwingZAngle() did not always return the correct angle. This problem has been fixed.
                • +
                • The "double cone" case of the D6 joint had errors both in the debug visualization and the code dealing with limits. This has been fixed.
                • +
                • The debug visualization of the D6 joint in the twist case did not properly color-code the angular limits. This has been fixed.
                • +
                • The debug visualization of distance joints has been fixed.
                • +
                • The debug visualization of prismatic joints has been fixed.
                • +
                • The debug visualization of revolute joints has been fixed. It now renders active limits properly (in red when the limit is active, grey otherwise).
                • +
                • Proper visualization flags are now passed to the PxConstraintVisualize function. Previously all available flags were active, even if PxVisualizationParameter::eJOINT_LOCAL_FRAMES and/or PxVisualizationParameter::eJOINT_LIMITS were set to zero.
                • +
                • PxRevoluteJoint::getVelocity has been fixed.
                • +
                +
              + +

              Scene queries

              +
                +
              • Fixed:
              • +
                  +
                • Sweep queries using the eMTD flag could generate incorrect normals in sphere/sphere, sphere/capsule or capsule/capsule cases, when the objects were exactly overlapping each-other. This has been fixed.
                • +
                • Sweep convex mesh vs heightfield queries using the eMTD flag did not fill correctly returned faceIndex. This has been fixed.
                • +
                +
              + + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.1

              +

              September 2017

              + +

              Changes and Resolved Issues

              +
              + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
              + +

              General

              +
                +
              • Deprecated:
              • +
                  +
                • The PhysX cloth feature has been deprecated.
                • +
                +
              • Changed:
              • +
                  +
                • The meaning of PxVisualizationParameter::eCULL_BOX has changed. It is now used to visualize the current culling box, while it was previously used to enable or disable the feature. Please simply use PxScene::setVisualizationCullingBox() to enable the feature from now on.
                • +
                • PxVisualizationParameter::eBODY_JOINT_GROUPS has been deprecated.
                • +
                • Performance of setCMassLocalPose function has been improved.
                • +
                +
              • Added:
              • +
                  +
                • PhysXGpu: Added warnings for the case when the dynamic gpu library fails to load.
                • +
                +
              + +

              Rigid Bodies

              +
                +
              • Fixed:
              • +
                  +
                • A potential crash when calling detachShape has been fixed.
                • +
                • Fixed assert that fired if application switched a body from dynamic to kinematic, then queried kinematic target without setting one. This assert only fired if the modifications overlapped simulation so were buffered.
                • +
                • PxArticulation::getStabilizationThreshold() and PxArticulation::setStabilizationThreshold() were accessing the sleep threshold instead of the stabilization threshold. This has been fixed.
                • +
                • Fixed an internal edge bug in PCM sphere vs mesh code
                • +
                • Make sure sphere vs sphere and sphere vs box in PCM contact gen generate contacts consistently on the second body when the sphere center is contained in the other shape
                • +
                +
              • Changed:
              • +
                  +
                • Improved convex vs mesh contact generation when using GPU rigid bodies. Requires the mesh to be recooked.
                • +
                • Improved convex vs convex contact generation when using GPU rigid bodies.
                • +
                • Reduced memory footprint of GPU triangle meshes significantly. Requires the mesh to be recooked.
                • +
                +
              • Added:
              • +
                  +
                • Support for modifying friction and restitution coefficients has been added to contact modification.
                • +
                • Added PxRigidBodyFlag::eENABLE_CCD_MAX_CONTACT_IMPULSE to enable maxContactImpulse member of PxRigidBody to be used in CCD. This is disabled by default. It is useful in some circumstances, e.g. shooting a small ball through a plate glass window and triggering it to break, but it can also result in behavioral artefacts so it is disabled by default.
                • +
                • Added PxSceneDesc::solverOffsetSlop. This is defaulted to a value of 0. A positive, non-zero value defines a tolerance used in the solver below which a contacts' offset from the COM of the body is negligible and therefore snapped to zero. This clamping occurs in a space tangential to the contact or friction direction. This is aimed at pool or golf simulations, where small numerical imprecision in either contact points or normals can lead to balls curving slightly when there are relatively high angular velocities involved.
                • +
                • Added PxConvexMeshGeometry::maxMargin. This allows the application to tune the maximum amount by which PCM collision detection will shrink convex shapes in contact generation. This shrinking approach leads to some noticeable clipping around edges and vertices, but should not lead to clipping with face collisions. By default, the mesh is shrunk by an amount that is automatically computed based on the shape's properties. This allows you to limit by how much the shape will be shrunk. If the maxMargin is set to 0, then the original shape will be used for collision detection.
                • +
                + +
              + +

              Scene queries

              +
                +
              • Fixed:
              • +
                  +
                • A rare invalid memory read that could lead to incorrect sweep results in case of an initial overlap has been fixed.
                • +
                +
              + +

              Serialization

              +
                +
              • Fixed:
              • +
                  +
                • Binary serialization didn't preserve PxConstraintFlags, e.g. projection flags.
                • +
                • Xml serialization failed if a shape referencing a PxTriangleMesh was added to another (dependent) collection.
                • +
                +
              + + +
              + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.0.22387197

              +

              June 2017

              + +

              Changes and Resolved Issues

              +
              + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
              + +

              Cooking

              +
                +
              • Fixed:
              • +
                  +
                • Fixed issue when PxConvexFlag::e16_BIT_INDICES was used together with PxConvexFlag::eCOMPUTE_CONVEX.
                • +
                • Fixed issue in convex hull cooking when postHullMerge was not executed.
                • +
                • Fixed crash in CCD when using bodies with 0 mass.
                • +
                +
              + +

              Rigid Bodies

              +
                +
              • Fixed:
              • +
                  +
                • Fixed behavioral differences when comparing the results of a given scene to the results when simulating a subset of the islands in that scene. In order for this case to be deterministic, it is necessary to raise PxSceneFlag::eENABLE_ENHANCED_DETERMINISM.
                • +
                + +
              • Added:
              • +
                  +
                • Introduced maxBiasCoefficient in PxSceneDesc to be able to limit the coefficient used to scale error to produce the bias used in the constraint solver to correct geometric error. The default value is PX_MAX_F32 and, therefore, a value of 1/dt will be used. This value can be useful to reduce/remove jitter in scenes with variable or very small time-steps. +
                +
              + + +
              + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.0.22121272

              +

              May

              + +

              Changes and Resolved Issues

              +
              + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
              + +

              Rigid Bodies

              +
                +
              • Fixed:
              • +
                  +
                • Fixed a bug in convex vs convex PCM contact gen code. There were cases in which full contact gen should have been triggered but was not.
                • +
                • Fixed a jittering bug on both GPU/CPU codepath in PCM contact gen code. This is due to the contact recycling condition not considering the toleranceLength.
                • +
                • Fixed a bug causing simulation to behave incorrectly when greater than 65536 bodies were simulated.
                • +
                +
              + +

              Scene queries

              +
                +
              • Fixed:
              • +
                  +
                • A rare crash that could happen with sphere overlap calls has been fixed.
                • +
                • Fixed a stack corruption in CCD contact modification callback.
                • +
                • Fixed a bug where external forces were not cleared correctly with PxArticulations.
                • +
                +
              + +

              Cooking

              +
                +
              • Changed:
              • +
                  +
                • Convex hull cooking now reuses edge information, perf optimization.
                • +
                • PxCooking API is now const if possible.
                • +
                +
              + + +
              + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.0.22017166

              +

              April 2017

              + +

              Changes and Resolved Issues

              +
              + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
              + +

              General

              +
                +
              • Added:
              • +
                  +
                • PxConvexMeshGeometry::maxMargin has been added. This will let the application limit how much the shape is shrunk in GJK when using by PCM contact gen
                • +
                +
              + +

              Rigid Bodies

              +
                +
              • Fixed:
              • +
                  +
                • Fixed a bug with joint breaking where sometimes joints would not break as expected.
                • +
                • Fix a race condition between cloth/particle/trigger interactions and the parallel filtering of rigid body interaction.
                • +
                • GPU rigid body feature now issues a performance warning in checked build if feature is enabled but PCM contact gen is not.
                • +
                • Fixed a bug with applying external force/torque to a body in buffered insertion stage.
                • +
                • Fixed a bug with CCD involving rotated static mesh actors.
                • +
                • Fixed a memory leak in CCD.
                • +
                +
              • Changed:
              • +
                  +
                • Optimizations for GPU rigid body feature, including reduced memory footprint and improvements to performance spikes when many objects are woken in a single frame.
                • +
                +
              + +

              Midphase

              +
                +
              • Fixed:
              • +
                  +
                • Fix Crash in BV4 code (serialization bug).
                • +
                +
              + +

              Cooking

              +
                +
              • Fixed:
              • +
                  +
                • Fix endless loop in convex hull cooking.
                • +
                +
              + + +
              + + +

              Release Notes - NVIDIA® PhysX® SDK 3.4.0.21821222

              +

              March 2017

              + +

              Supported Platforms

              +
              +

              Runtime

              +
                +
              • Apple iOS
              • +
              • Apple Mac OS X
              • +
              • Google Android ARM (version 2.2 or later required for SDK, 2.3 or later required for snippets)
              • +
              • Linux (tested on Ubuntu)
              • +
              • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
              • +
              • Microsoft XBox One
              • +
              • Nintendo Switch
              • +
              • Sony Playstation 4
              • +
              +

              Development

              +
                +
              • Microsoft Windows XP or later
              • +
              • Microsoft Visual Studio 2012, 2013, 2015
              • +
              • Xcode 8.2
              • +
              +
              +

              Known Issues

              +
              +
                +
                +

                Changes and Resolved Issues

                +
                + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +
                + +

                General

                +
                  +
                • Fixed:
                • +
                    +
                  • A rare crash happening in the debug visualization code has been fixed.
                  • +
                  +
                + +

                Rigid Bodies

                +
                  +
                • Fixed:
                • +
                    +
                  • Fixed a bug in PCM capsule vs plane contact gen.
                  • +
                  • A crash happening with more than 64K interactions has been fixed.
                  • +
                  • Fixed an island management issue in CCD when using multiple CCD passes.
                  • +
                  • Fixed a bug with GPU rigid bodies with non-simulation scene query-only shapes.
                  • +
                  • Fixed a bug in convex vs convex PCM contact gen code. There were cases in which full contact gen should have been triggered but was not.
                  • +
                  • Fixed a jittering bug on both GPU/CPU codepath in PCM contact gen code. This is due to the contact recycling condition not considering the toleranceLength.
                  • +
                  +
                • Added:
                • +
                    +
                  • getFrozenActors has been added to allow application queries the frozen actors
                  • +
                  • PxRigidDynamic::setKinematicSurfaceVelocity has been added, permitting the user to set a persistent velocity on a kinematic actor which behaves like a conveyor belt
                  • +
                  • PxSceneDesc::solverOffsetSlop added. This defines a threshold distance from a body's COM under which a contact will be snapped to the COM of the body inside the solver along any principal component axis
                  • +
                  +
                + +

                Scene queries

                +
                  +
                • Fixed:
                • +
                    +
                  • A bug in the BVH34 overlap code sometimes made PxMeshOverlapUtil::findOverlap assert (reporting an incorrect buffer overflow). This has been fixed.
                  • +
                  • Fix a bug in the case of two primitives just touching in sweep with eMTD flag on.
                  • +
                  +
                + + +
                + + +

                Release Notes - NVIDIA® PhysX® SDK 3.4

                +

                February 2017

                + +

                Supported Platforms

                +
                +

                Runtime

                +
                  +
                • Apple iOS
                • +
                • Apple Mac OS X
                • +
                • Google Android ARM (version 2.2 or later required for SDK, 2.3 or later required for snippets)
                • +
                • Linux (tested on Ubuntu)
                • +
                • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
                • +
                • Microsoft XBox One
                • +
                • Nintendo Switch
                • +
                • Sony Playstation 4
                • +
                +

                Development

                +
                  +
                • Microsoft Windows XP or later
                • +
                • Microsoft Visual Studio 2012, 2013, 2015
                • +
                • Xcode 8.2
                • +
                +
                +

                Known Issues

                +
                +
                  +
                  +

                  Changes and Resolved Issues

                  +
                  + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

                  General

                  +
                    +
                  • Added:
                  • +
                      +
                    • nbAggregates, nbArticulations, nbDiscreteContactPairsTotal, nbDiscreteContactPairsWithCacheHits and nbDiscreteContactPairsWithContacts have been added to PxSimulationStatistics.
                    • +
                    • PxSceneLimits::maxNbBroadPhaseOverlaps has been added.
                    • +
                    • A new midphase has been added. See PxMeshMidPhase enum for details.
                    • +
                    • Midphase descriptor has been added. See PxMidphaseDesc for details.
                    • +
                    • PxHeightField::getTimestamp() has been added
                    • +
                    • PxCloneShape has been added to enable cloning of shapes.
                    • +
                    • acquireReference() has been added to increment the reference count of shapes, materials, triangle meshes, convex meshes heightfields and cloth fabrics.
                    • +
                    • The global filter shader data block can now be set through PxScene::setFilterShaderData().
                    • +
                    • PxGpuLoadHook and PxSetPhysXGpuLoadHook has been added to support loading of GPU dll with a different name than the default.
                    • +
                    • A new profile zone has been added for debug visualization.
                    • +
                    +
                  • Changed:
                  • +
                      +
                    • PxMathUtils.h has moved from include/common to include/foundation
                    • +
                    • GPU support requires SM2.x (Fermi architecture, GeForce 400 series) or later hardware. SM1.x is no longer supported.
                    • +
                    • Windows XP 64-bit and Windows Vista no longer support GPU acceleration. Windows XP 32-bit still supports GPU acceleration.
                    • +
                    • PxTaskManager::createTaskManager requires error callback and does not accept SPU task manager.
                    • +
                    • PxCreatePhysics and PxCreateBasePhysics now take an optional pointer to an physx::PxPvd instance.
                    • +
                    • PxConstraintConnector::updatePvdProperties now takes an optional pointer to an physx::pvdsdk::PvdDataStream instance.
                    • +
                    • PxInitExtensions now takes an optional pointer to an physx::PxPvd instance.
                    • +
                    • Shared objects (triangle mesh, convex mesh, heightfield, material, shape, cloth fabric) no longer issue an eUSER_RELEASE event when their release() method is called
                    • +
                    • + PxBase::isReleasable() is now a property only of the type of an object, not the object state. In particular, + isReleasable() returns true for PxShape objects whose only counted reference belongs to their owning actor. +
                    • +
                    • PxCollection::releaseObjects() now calls release() even on shapes whose only counted reference belongs to their owning actor. An optional parameter releaseExclusiveShapes, which defaults to true, has been added to this method to assist with common scenarios in which all shapes are created with the deprecated method PxRigidActor::createShape() or its replacement PxRigidActorExt::createExclusiveShape()
                    • +
                    • Negative mesh scale is now supported for PxTriangleMeshGeometry. Negative scale corresponds to reflection and scale along the corresponding axis. In addition to reflection PhysX will flip the triangle normals.
                    • +
                    • PxDelayLoadHook is now inherited from PxFoundationDelayLoadHook. PxFoundation dll and PxPvdSDK dll are now delay loaded inside the SDK, their names can be provided through the delay load hook.
                    • +
                    +
                  • Removed:
                  • +
                      +
                    • Sony Playstation 3 is not supported any longer. Any related APIs have been removed.
                    • +
                    • Microsoft XBox 360 is not supported any longer. Any related APIs have been removed.
                    • +
                    • Nintendo Wii U is not supported any longer. Any related APIs have been removed.
                    • +
                    • Sony Playstation Vita is not supported any longer. Any related APIs have been removed.
                    • +
                    • Visual Studio 2010 is not supported any longer.
                    • +
                    • Microsoft Windows RT is not supported any longer.
                    • +
                    • Google Android X86 is not supported any longer.
                    • +
                    • PhysX Samples are not supported anymore except on Microsoft Windows.
                    • +
                    • Linux 32-bit no longer support GPU acceleration. Linux 64-bit still supports GPU acceleration.
                    • +
                    +
                  • Fixed:
                  • +
                      +
                    • PxScene::setFlag() does now properly send error messages in CHECKED builds if a non-mutable scene flag gets passed in.
                    • +
                    • Fixed a bug in force threshold based contact reports, which caused events to be lost.
                    • +
                    • Fixed a bug in aggregates that led to a crash when rigid bodies are added to an aggregate after removing all rigid bodies from an aggregate. This only occurred with aggregates that were added to the scene and with rigid bodies that had shapes attached.
                    • +
                    • Fixed a bug where non-breakable joints could break, leading to a crash.
                    • +
                    • Fixed RepX load of kinematic rigid bodies with mesh shapes.
                    • +
                    • Fixed a divide by zero bug in SIMD distanceSegmentTriangle function.
                    • +
                    • Debug visualization for compound bounds (PxVisualizationParameter::eCOLLISION_COMPOUNDS) now works even when all the compounds' shapes have their debug viz flag (PxShapeFlag::eVISUALIZATION) disabled.
                    • +
                    • Double-buffering now works properly for the debug visualization culling box.
                    • +
                    • The default debug visualization culling box now works correctly with heightfields.
                    • +
                    • Rare crashes in PxShape::setGeometry() and PxShape::getMaterials() have been fixed.
                    • +
                    • A crash happening when doing an origin shift on a scene containing an empty aggregate has been fixed.
                    • +
                    +
                  • Deprecated:
                  • +
                      +
                    • PxSceneLimits::maxNbObjectsPerRegion has been deprecated. It is currently not used.
                    • +
                    • PxComputeHeightFieldPenetration has a new signature, and the old one has been deprecated
                    • +
                    • PxComputeMeshPenetration has been deprecated. Use PxComputeTriangleMeshPenetration.
                    • +
                    • The PhysX particle feature has been deprecated.
                    • +
                    • PxTolerancesScale::mass has been deprecated. It is currently not used.
                    • +
                    • PxActorClientBehaviorFlag has been marked as deprecated and will be removed in future releases.
                    • +
                    +
                  • Removed deprecated API:
                  • +
                      +
                    • PxPairFlag::eCCD_LINEAR removed. Use PxPairFlag::eDETECT_CCD_CONTACT | PxPairFlag::eSOLVE_CONTACT instead.
                    • +
                    • PxPairFlag::eRESOLVE_CONTACTS removed. Use PxPairFlag::eDETECT_DISCRETE_CONTACT | PxPairFlag::eSOLVE_CONTACT instead.
                    • +
                    • PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICE removed. Use PxTriangleMeshFlag::e16_BIT_INDICES instead.
                    • +
                    • PxTriangleMeshFlag::eHAS_ADJACENCY_INFO removed. Use PxTriangleMeshFlag::eADJACENCY_INFO instead.
                    • +
                    • PxTriangleMeshDesc::convexEdgeThreshold removed.
                    • +
                    • PxSceneQueryFlag renamed to PxHitFlag.
                    • +
                    • PxHitFlag::eDIRECT_SWEEP renamed to PxHitFlag::ePRECISE_SWEEP.
                    • +
                    • PxHitFlag::eIMPACT renamed to PxHitFlag::ePOSITION.
                    • +
                    • PxSceneQueryHitType renamed to PxQueryHitType.
                    • +
                    • PxSceneQueryCache renamed to PxQueryCache.
                    • +
                    • PxSceneQueryFilterFlag renamed to PxQueryFlag.
                    • +
                    • PxSceneQueryFilterFlags renamed to PxQueryFlags.
                    • +
                    • PxSceneQueryFilterData renamed to PxQueryFilterData.
                    • +
                    • PxSceneQueryFilterCallback renamed to PxQueryFilterCallback.
                    • +
                    • PxScene::raycastAll,PxScene::raycastSingle,PxScene::raycastAny replaced by PxScene::raycast.
                    • +
                    • PxScene::overlapAll,PxScene::overlapAny replaced by PxScene::overlap.
                    • +
                    • PxScene::sweepAll,PxScene::sweepSingle,PxScene::sweepAny replaced by PxScene::sweep.
                    • +
                    • PxQuat, PxTranform, PxMat33, PxMat44 createIdentity and createZero removed. Use PxIdentity, PxZero in constructor.
                    • +
                    • PxJointType::Enum, PxJoint::getType() removed. Use PxJointConcreteType instead.
                    • +
                    • PxVisualDebugger removed. Use PxPvd instead.
                    • +
                    • PxControllerFlag renamed to PxControllerCollisionFlag.
                    • +
                    • PxCCTHit renamed to PxControllerHit.
                    • +
                    • PxCCTNonWalkableMode renamed to PxControllerNonWalkableMode.
                    • +
                    • PxControllerNonWalkableMode::eFORCE_SLIDING changed to PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING.
                    • +
                    • PxControllerDesc::interactionMode, groupsBitmask, callback removed.
                    • +
                    • PxController::setInteraction, getInteraction, setGroupsBitmask, getGroupsBitmask removed.
                    • +
                    • PxControllerManager::createController no longer needs PxPhysics and PxScene.
                    • +
                    • PxControllerFilters::mActiveGroups replaced with PxControllerFilters::mCCTFilterCallback.
                    • +
                    • PxSerialization::createBinaryConverter(PxSerializationRegistry&) changed to PxSerialization::createBinaryConverter().
                    • +
                    • PxConstraintDominance renamed to PxDominanceGroupPair.
                    • +
                    • PxScene::flush renamed to PxScene::flushSimulation.
                    • +
                    • PxClothFabric::getPhaseType removed.
                    • +
                    • PxCollection::addRequired removed.
                    • +
                    • PxRigidActor::createShape discontinued support for initial transform.
                    • +
                    • PxShape::resetFiltering removed.
                    • +
                    • PxParticleBase::resetFiltering removed.
                    • +
                    • PxSceneDesc::meshContactMargin removed.
                    • +
                    • PxSceneDesc::contactCorrelationDistance removed.
                    • +
                    • Indexing operators taking signed integers in PxVec3, PxVec4, PxMat33, PxMat44, PxStrideIterator have been removed.
                    • +
                    • PxHitFlag::ePOSITION, PxHitFlag::eDISTANCE and PxHitFlag::eNORMAL are now supported in PxMeshQuery::sweep function.
                    • +
                    • PxClothFlag::eGPU renamed to PxClothFlag::eCUDA.
                    • +
                    • PxActorTypeSelectionFlag/PxActorTypeSelectionFlags. Use PxActorTypeFlag/PxActorTypeFlags instead.
                    • +
                    • PxConstraintFlag::eDEPRECATED_32_COMPATIBILITY flag removed.
                    • +
                    +
                  + +

                  PxShared

                  +
                    + APEX 1.4 can now be used independently of PhysX. In order to achieve that a new shared code base was created called "PxShared". PhysX functionality such as common types, PxFoundation, the task infrastructure are now part of PxShared. +
                  + +

                  Rigid Bodies

                  +
                    +
                  • Added:
                  • +
                      +
                    • An alternative simulation API has been introduced. This makes use of the following new functions: PsScene:collide(), PxScene::fetchCollision() and PxScene::advance(). Expected usage of these functions is illustrated in a new snippet SnippetSplitSim. This feature is also described in the manual in Section Simulation->SplitSim.
                    • +
                    • PxSceneFlag::eDEPRECATED_TRIGGER_TRIGGER_REPORTS has been introduced to re-enable the legacy behavior of trigger shape pairs sending reports. This flag and the corresponding legacy behavior will be removed in version 4.
                    • +
                    • The active actors feature has been added. See PxSceneFlag::eENABLE_ACTIVE_ACTORS.
                    • +
                    • Functionality to compute and manipulate mass, inertia tensor and center of mass of objects has been exposed in the new class PxMassProperties.
                    • +
                    • The option to exclude kinematics from the active actors/transforms list has been added. See PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS for details.
                    • +
                    • The integrated pose of dynamic rigid bodies can be accessed earlier in the pipeline through a new callback (see the API documentation for PxSimulationEventCallback::onAdvance() and PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW for details).
                    • +
                    • PxConvexMeshGeometryFlag::eTIGHT_BOUNDS has been added. See the user manual for details.
                    • +
                    • New split fetchResults methods introduced, see PxScene::fetchResultsBegin(), PxScene::fetchResultsFinish() and PxScene::processCallbacks(). This is intended to permit the application to parallelize the event notification callbacks.
                    • +
                    • New flag introduced to suppress updating scene query pruner trees inside fetchResults, see PxSceneFlag::eSUPPRESS_EAGER_SCENE_QUERY_REFIT. Instead, pruners will be updated during the next query.
                    • +
                    • Introduced GPU rigid body simulation support, see PxSceneFlag::eENABLE_GPU_DYNAMICS. GPU rigid body support requires SM3.0 or later.
                    • +
                    • Introduced a new GPU-accelerated broad phase. See PxBroadPhaseType::eGPU. GPU broad phase support requires SM3.0 or later.
                    • +
                    • Introduced a new enhanced determinism mode. See PxSceneFlag::eENABLE_ENHANCED_DETERMINISM. This provides additional levels of rigid body simulation determinism at the cost of some performance.
                    • +
                    • Introduced a new speculative contacts CCD approach. See PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD. This is a slightly cheaper, less robust solution to PxRigidBodyFlag::eENABLE_CCD. There is no need to turn CCD on the scene using PxSceneFlag::eENABLE_CCD or enable PxPairFlag::eDETECT_CCD_CONTACT with this CCD mode as it functions as an extension to the discrete time-stepping scheme. This form of CCD can be enabled on kinematic actors.
                    • +
                    • New "immediate mode" API has been added. This exposes access to the low-level contact generation and constraint solver, which allows the application to use these PhysX low-level components to perform its own simulations without needing to populate and simulate a PxScene.
                    • +
                    • RigidDynamic lock flags added which permit the application to disallow rotation/translation of PxRigidDynamics around specific axes.
                    • +
                    +
                  • Changed:
                  • +
                      +
                    • PxRigidStatic objects can now have zero shapes while being part of a scene.
                    • +
                    • PxContactPairFlag::eINTERNAL_HAS_FACE_INDICES is obsolete and has been removed.
                    • +
                    • PxConstraintFlag::eDEPRECATED_32_COMPATIBILITY was previously only implemented for spring constraints. It is now correctly implemented for equality constraints.
                    • +
                    • PxSceneFlag::eENABLE_PCM is enabled by default. This means PhysX uses PCM distance-based collision detection by default.
                    • +
                    • Calls to PxRigidDynamic::setWakeCounter() following PxScene::collide() do now explicitly get taken into account in the subsequent call to PxScene::advance().
                    • +
                    • Calls to contact modification callbacks can be made from multiple threads simultaneously. Therefore, modification callbacks should must be thread-safe.
                    • +
                    • Unified heightfield contact generation is now the default heightfield contact generation approach. This approach offers similar performance and behavior to contact generation with triangle meshes. Unified heightfields have no thickness because contact genreation operates on triangles so objects may tunnel if CCD is not enabled.
                    • +
                    • When unified heightfield contact generation is in use, the bounds of heightfield shapes are no longer extruded by "thickness".
                    • +
                    • PxArticulationJoint::setTwistLimit and PxArticulationJoint::getTwistLimit were incorrectly documented with zLimit and yLimit in the wrong order. The behavior of both functions remains unchanged but now they are correctly documented with zLimit and yLimit in the correct order. This is simply a clarification of the existing function behavior.
                    • +
                    +
                  • Removed:
                  • +
                      +
                    • The deprecated class PxFindOverlapTriangleMeshUtil has been removed. Please use PxMeshOverlapUtil instead.
                    • +
                    • The deprecated flag PxConstraintFlag::eREPORTING has been removed. Force reports are now always generated.
                    • +
                    • The following deprecated simulation event flags have been removed: PxContactPairHeaderFlag::eDELETED_ACTOR_0, ::eDELETED_ACTOR_1, PxContactPairFlag::eDELETED_SHAPE_0, ::eDELETED_SHAPE_1, PxTriggerPairFlag::eDELETED_SHAPE_TRIGGER, ::eDELETED_SHAPE_OTHER. Please use the following flags instead: PxContactPairHeaderFlag::eREMOVED_ACTOR_0, ::eREMOVED_ACTOR_1, PxContactPairFlag::eREMOVED_SHAPE_0, ::eREMOVED_SHAPE_1, PxTriggerPairFlag::eREMOVED_SHAPE_TRIGGER, ::REMOVED_SHAPE_OTHER.
                    • +
                    • The deprecated method PxPhysics::createHeightField(const PxHeightFieldDesc&) has been removed. Please use PxCooking::createHeightField(const PxHeightFieldDesc&, PxPhysicsInsertionCallback&) instead. The insertion callback can be obtained through PxPhysics::getPhysicsInsertionCallback().
                    • +
                    +
                  • Deprecated:
                  • +
                      +
                    • PxRigidActor::createShape() has been deprecated in favor of PxRigidActorExt::createExclusiveShape()
                    • +
                    • Trigger notification events for trigger-trigger pairs have been deprecated and will be omitted by default. See the 3.4 migration guide for more information.
                    • +
                    • The active transforms feature (PxSceneFlag::eENABLE_ACTIVETRANSFORMS) has been deprecated. Please use PxSceneFlag::eENABLE_ACTIVE_ACTORS instead.
                    • +
                    • PxRegisterHeightFields has been modified to register unified heightfields, which are now the default implementation. PxRegisterLegacyHeightFields() has been added to register the legacy (deprecated) heightfield contact gen approach.
                    • +
                    • PxHeightFieldDesc::thickness has been deprecated, as the new unified height field (see PxRegisterUnifiedHeightFields()) does not support thickness any longer.
                    • +
                    +
                  • Fixed:
                  • +
                      +
                    • The capsule-vs-heightfield contact generation had a rare bug where a vertical capsule standing exactly on a shared edge could fall through a mesh. This has been fixed.
                    • +
                    • The bounding box of a shape was not always properly updated when the contact offset changed.
                    • +
                    • Fixed a bug in the GJK sweep caused by lost precision in the math
                    • +
                    • Calls to PxScene::shiftOrigin() can crash when PxRigidDynamic actors with PxActorFlag::eDISABLE_SIMULATION are present.
                    • +
                    • Fixed a bug when PxShape::setMaterials was called with less materials than shape had before.
                    • +
                    • Fixed a bug in CCD that could lead to a hang in the simulation.
                    • +
                    • Fixed a bug in PCM mesh edge-edge check for the parallel case.
                    • +
                    • Fixed a bug in CCD where contact modify callbacks could be called when the CCD did not detect a contact.
                    • +
                    • Fixed a bug with applying external force/torque to a body in buffered insertion stage.
                    • +
                    • A rare capsule-vs-mesh contact generation bug has been fixed.
                    • +
                    • A rare crash due to an invalid assert in the MBP broad-phase has been fixed. This error only affected debug & checked builds; release & profile builds were unaffected.
                    • +
                    +
                  + +

                  Particles

                  +
                    +
                  • Gpu: Maxwell Optimizations
                  • +
                  • The PhysX particle feature has been deprecated.
                  • +
                  • Fixed particle collision issue with PxParticleBaseFlag::ePER_PARTICLE_COLLISION_CACHE_HINT (on by default). When particles collided against very dense triangle mesh areas an assert would be triggered or particles would leak through the triangle mesh. A workaround was to disable PxParticleBaseFlag::ePER_PARTICLE_COLLISION_CACHE_HINT.
                  • +
                  + +

                  Cloth

                  +
                    +
                  • Continuous collision (PxClothFlag::eSWEPT_CONTACT) behavior has been optimized to reduce cloth sticking to collision shape.
                  • +
                  • Added air resistance feature (see PxCloth::setWindVelocity(PxVec3), PxCloth::setWindDrag(PxReal), PxCloth::setWindLift(PxReal), PxClothFabricDesc::nbTriangles, PxClothFabricDesc::triangles.
                  • +
                  + +

                  Serialization

                  +
                    +
                  • Fixed:
                  • +
                      +
                    • PxTriangleMesh instances with adjacency information were not correctly initialized when created with cooking.createTriangleMesh. This caused a crash when converting the binary serialized triangle mesh data.
                    • +
                    +
                  + +

                  Character controller

                  +
                    +
                  • Added:
                  • +
                      +
                    • Profile zones have been added for the character controller.
                    • +
                    • Added PxControllerDesc::registerDeletionListener boolean defining if deletion listener for CCT should be registered.
                    • +
                    +
                  • Fixed:
                  • +
                      +
                    • Character controllers cannot stand on dynamic triggers anymore.
                    • +
                    • Fixed: the capsule-vs-sphere sweep now returns a normal in the correct direction.
                    • +
                    • Fixed a bug where CCT shapes initially overlapping static geometry would be moved down by an incorrect amount (the length of the step offset).
                    • +
                    • The overlap recovery module now works against kinematic objects.
                    • +
                    +
                  + +

                  Vehicles

                  +
                    +
                  • Added:
                  • +
                      +
                    • + Anti-roll suspension has been added. The class PxVehicleAntiRollBar and the functions PxVehicleWheelsSimData::setAntiRollBar, + PxVehicleWheelsSimData::getAntiRollBar, PxVehicleWheelsSimData::getNbAntiRollBars allow anti-roll bars to be configured and queried. +
                    • +
                    • + A new function PxVehicleSuspensionSweeps has been introduced. This sweeps the PxShape that represents the wheel along the suspension direction. The hit planes resulting + from the sweep are used as driving surfaces similar to those found by PxVehicleSuspensionRaycasts. +
                    • +
                    • + A new snippet SnippetVehicleContactMod has been added. This snippet demonstrates how to use sweeps and contact modification to allow the wheel's volume to fully interact + with the environment. +
                    • +
                    • A new function PxVehicleModifyWheelContacts has been introduced. This function analyses contact in the contact modification callback and rejects contact points that represent drivable surfaces.
                    • +
                    • A new function PxVehicleSetMaxHitActorAcceleration has been introduced. This function sets the maximum acceleration experienced by a PxRigidDynamic that finds itself under the wheel of a vehicle.
                    • +
                    +
                  • Changed:
                  • +
                      +
                    • + In checked build the functions PxVehicleDrive4W::allocate, PxVehicleDriveNW::allocate, PxVehicleDriveTank::allocate, PxVehicleNoDrive::allocate all return NULL and issue a warning if called before + PxInitVehicleSDK. +
                    • +
                    • Tire width is no longer accounted for when computing the suspension compression from raycasts (PxVehicleSuspensionRaycasts). Instead, tire width is incorporated into the suspension compression arising from swept wheels (PxVehicleSuspensionSweeps). It is recommended to use PxVehicleSuspensionSweeps if there is a strict requirement that the inside and outside of the wheel don't visibly penetrate geometry.
                    • +
                    +
                  • Fixed:
                  • +
                      +
                    • Suspension force calculation now applies an extra force perpendicular to the spring travel direction. This force is calculated to satisfy the constraint that the sprung mass only has motion along the spring travel direction. This change mostly affects vehicles with suspension travel directions that are not vertical.
                    • +
                    • PxVehicleWheelsSimData::mThresholdLongitudinalSpeed and PxVehicleWheelsSimData::mMinLongSlipDenominator are now given default values that reflect the length scale set in PxTolerancesScale.
                    • +
                    • Unphysically large constraint forces were generated to resolve the suspension compression beyond its limit when the suspension direction and the hit normal under the wheel approach perpendicularity. This has been fixed so that the constraint force approaches zero as the angle between the hit normal and suspension direction approaches a right angle.
                    • +
                    +
                  + +

                  Scene queries

                  +
                    +
                  • Added:
                  • +
                      +
                    • PxPruningStructure was introduced as an optimization structure to accelerate scene queries against large sets of newly added actors.
                    • +
                    • PxScene::addActors(PxPruningStructure& ) has been added.
                    • +
                    • PxMeshQuery::sweep now supports PxHitFlag::eMESH_ANY.
                    • +
                    • PxHitFlag::eFACE_INDEX was introduced to reduce the perf cost for convex hull face index computation. In order to receive face index for sweeps against a convex hull, the flag PxHitFlag::eFACE_INDEX has to be set. Note that the face index can also be computed externally using the newly introduced method PxFindFaceIndex from the extensions library.
                    • +
                    • PxGeometryQuery::isValid was added to check provided geometry validity.
                    • +
                    +
                  • Changed:
                  • +
                      +
                    • Raycasts against triangle meshes with PxHitFlag::eMESH_MULTIPLE flag now return all hits, code for discarding hits close to each other has been removed.
                    • +
                    • PxPruningStructure enum has been renamed to PxPruningStructureType
                    • +
                    +
                  • Deprecated:
                  • +
                      +
                    • PxHitFlag::eDISTANCE has been deprecated.
                    • +
                    • The batched query feature has been deprecated.
                    • +
                    • Volume cache feature has been deprecated.
                    • +
                    • Spatial index feature has been deprecated.
                    • +
                    +
                  • Fixed:
                  • +
                      +
                    • PxScene::sweep now properly implements PxHitFlag::eMESH_BOTH_SIDES (returned normal follows the same convention as for raycasts).
                    • +
                    • Raycasts against heightfields now correctly return multiple hits when PxHitFlag::eMESH_MULTIPLE flag is used.
                    • +
                    • PxSweepHit.faceIndex was computed incorrectly for sweep tests initially overlapping convex objects. The face index is now set to 0xffffffff in these cases.
                    • +
                    • Convex vs convex sweeps in PxGeometryQuery::sweep() do now correctly return the face index of the convex mesh that gets passed in as parameter geom1 (and not the one from geom0).
                    • +
                    • PxMeshQuery::sweep now supports PxHitFlag::eMESH_ANY.
                    • +
                    • Deprecated definition PxSceneQueryHit has been removed. Please use PxQueryHit instead.
                    • +
                    • PxGeometryQuery::computePenetration with convex geometries.
                    • +
                    • On Android platforms, the eDYNAMIC_AABB_TREE pruning structure could pass already released objects into the scene query filter callback.
                    • +
                    +
                  + + +

                  Cooking

                  +
                    +
                  • Added:
                  • +
                      +
                    • PxTriangleMeshCookingResult added, cookTriangleMesh now does return additional PxTriangleMeshCookingResult. Please see the manual for more information.
                    • +
                    • New convex hull generator added. It is now possible to switch between a new quickhull implementation and the legacy inflation based hull. Quickhull is the default algorithm.
                    • +
                    • Convex hulls can now be directly inserted in PxPhysics as triangle meshes and height fields.
                    • +
                    • A separate convex hull validation function has been added, it is now possible to create hulls without validation.
                    • +
                    • Convex hull generator vertex limit has two different algorithms - plane shifting and OBB slicing.
                    • +
                    • PxConvexFlag::eFAST_INERTIA_COMPUTATION added. When enabled, the inertia tensor is computed faster but with less precision.
                    • +
                    • PxConvexFlag::eGPU_COMPATIBLE added. When enabled convex hulls are created with vertex limit set to 64 and vertex limit per face is 32.
                    • +
                    • PxConvexFlag::eSHIFT_VERTICES added. When enabled input points are shifted to be around origin to improve computation stability.
                    • +
                    • PxCookingParams::gaussMapLimit has been added. The limit can now be fully user-defined. Please refer to the migration guide and best practices sections of the manual.
                    • +
                    +
                  • Changed:
                  • +
                      +
                    • The performance of convex creation from polygons has been improved.
                    • +
                    +
                  • Deprecated:
                  • +
                      +
                    • The PxPlatform enum and the PxGetGaussMapVertexLimitForPlatform function have been deprecated.
                    • +
                    +
                  • Removed:
                  • +
                      +
                    • The deprecated flags PxMeshPreprocessingFlag::eREMOVE_UNREFERENCED_VERTICES and ::eREMOVE_DUPLICATED_TRIANGLES have been removed. Meshes get cleaned up by default unless PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH is set.
                    • +
                    +
                  • Fixed:
                  • +
                      +
                    • Mesh cooking was sometimes crashing for meshes with less than 4 triangles. This has been fixed.
                    • +
                    • Cooking convex mesh from a flat input mesh produced incorrect large mesh.
                    • +
                    +
                  + +

                  Extensions

                  +
                    +
                  • Added:
                  • +
                      +
                    • PxRaycastCCD has been added, to improve the visibility of the raycast-based CCD solution, which was previously only available in the Sample framework. This is a simpler and potentially cheaper alternative to the SDK's built-in continuous collision detection algorithm.
                    • +
                    • PxFindFaceIndex has been added. The function computes the closest polygon on a convex mesh from a given hit point and direction.
                    • +
                    +
                  • Changed:
                  • +
                      +
                    • Memory churn of PxDefaultMemoryOutputStream has been reduced.
                    • +
                    • The signatures for the PxComputeMeshPenetration and PxComputeHeightFieldPenetration functions have changed.
                    • +
                    +
                  + +

                  Profiling

                  +
                    +
                  • Changed:
                  • +
                      +
                    • Profiling information is now available only in debug, checked and profile configuration.
                    • +
                    • PxProfileZoneManager::createProfileZoneManager now takes PxAllocatorCallback as input parameter instead of PxFoundation.
                    • +
                    +
                  + +

                  Physx Visual Debugger

                  +
                    +
                  • PhysXVisualDebuggerSDK, PvdRuntime projects replaced with PxPvdSDK.
                  • +
                  • PxPvdSceneClient::drawPoints now takes physx::pvdsdk::PvdDebugPoint as input parameter instead of PxDebugPoint. drawLines, drawTriangles, drawText and so on.
                  • +
                  • The SDK's Debug visualization data is not sent to PVD anymore in ePROFILE mode.
                  • +
                  • PxPvdSceneFlag::eTRANSMIT_CONTACTS (instead of PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS) was sometimes incorrectly used to control the transmission of constraint-related data. This has been fixed. In addition, the PxPvdSceneFlag flags are now consistently ignored when PxPvdInstrumentationFlag::eDEBUG is not set.
                  • +
                  + +

                  Aggregates

                  +
                    + +

                    Snippets

                    +
                      +
                    • Snippet profile zone has been removed.
                    • +
                    +
                    + + +
                    + + +

                    Release Notes - NVIDIA® PhysX® SDK 3.3.4

                    +

                    October 2015

                    + +

                    Supported Platforms

                    +
                    +

                    Runtime

                    +
                      +
                    • Apple iOS
                    • +
                    • Apple Mac OS X
                    • +
                    • Google Android ARM & x86 (version 2.2 or later required for SDK, 2.3 or later required for samples)
                    • +
                    • Linux (tested on Ubuntu)
                    • +
                    • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
                    • +
                    • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
                    • +
                    • Microsoft XBox One (SDK only, no samples)
                    • +
                    • Microsoft XBox 360
                    • +
                    • Nintendo Wii U
                    • +
                    • Sony Playstation 3
                    • +
                    • Sony Playstation 4 (SDK only, no samples)
                    • +
                    • Sony Playstation Vita
                    • +
                    +

                    Development

                    +
                      +
                    • Microsoft Windows XP or later
                    • +
                    • Microsoft Visual Studio 2012, 2013 and 2015
                    • +
                    • Xcode 6.3
                    • +
                    +
                    +

                    Changes and Resolved Issues

                    +
                    + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

                    General

                    +
                      +
                    • Added support for Microsoft Visual Studio 2015 for Windows builds. Note that the GPU features are not currently supported with Visual Studio 2015.
                    • +
                    • Removed support for Microsoft Visual Studio 2010
                    • +
                    • Added support for Android platform API Level 16 and removed support for platform API Level 8 and 9.
                    • +
                    • Fixed:
                    • +
                        +
                      • Fixed a bug in aggregates that led to a crash when rigid bodies are added to an aggregate after removing all rigid bodies from an aggregate. This only occurred with aggregates that were added to the scene and with rigid bodies that had shapes attached.
                      • +
                      +
                    + +

                    Rigid Bodies

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • Creating a PxConstraint or using PxConstraint::setActors() could cause a crash in situations where one of the two newly connected actors was part of a previously simulated graph of connected constraints (with some of them having projection enabled, i.e., PxConstraintFlag::ePROJECT_TO_ACTOR0 or ::ePROJECT_TO_ACTOR1 set).
                      • +
                      • PxD6Joint::getTwist(), getSwingY(), getSwingZ() returned incorrect angle values when the corresponding components of the quaternion were negative
                      • +
                      • The thickness of a heightfield was incorrectly applied when the heightfield transform had a non-identity quaternion.
                      • +
                      • PxD6Joint angular projection now functions correctly when there is one free axis and it is not the twist axis.
                      • +
                      • The bounding box of a shape was not always properly updated when the contact offset changed.
                      • +
                      • Fixed an edge case bug in PCM contact gen that could result in a QNAN reported as the contact point.
                      • +
                      • Fixed an uninitialized variable bug in the GJK algorithm resulting in unintialized closest points reported.
                      • +
                      • Fixed an edge case in which the constraint solver could access invalid memory in constraint partitioning.
                      • +
                      • Fixed a bug in capsule vs heightfield contact generation that could produce incorrect penetration depths.
                      • +
                      • Fixed a bug in Unified MTD code path which transformed the normal twice for the polygon index calculation.
                      • +
                      • Fixed a crash in height fields when a capsule collided with an edge whose shared triangles had a hole material.
                      • +
                      +
                    + +

                    Serialization

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • PxTriangleMesh instances with adjacency information were not correctly initialized when created with cooking.createTriangleMesh. This caused a crash when converting the binary serialized triangle mesh data.
                      • +
                      +
                    + +

                    Scene Queries

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • Sweeps against scaled meshes.
                      • +
                      +
                    + +

                    Cooking

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • Mesh cooking was sometimes crashing for meshes with less than 4 triangles. This has been fixed.
                      • +
                      +
                    + +
                    + + +
                    + + +

                    Release Notes - NVIDIA® PhysX® SDK 3.3.3

                    +

                    January 2015

                    + +

                    Supported Platforms

                    +
                    +

                    Runtime

                    +
                      +
                    • Apple iOS
                    • +
                    • Apple Mac OS X
                    • +
                    • Google Android ARM & x86 (version 2.2 or later required for SDK, 2.3 or later required for samples)
                    • +
                    • Linux (tested on Ubuntu)
                    • +
                    • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
                    • +
                    • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
                    • +
                    • Microsoft XBox One (SDK only, no samples)
                    • +
                    • Microsoft XBox 360
                    • +
                    • Nintendo Wii U
                    • +
                    • Sony Playstation 3
                    • +
                    • Sony Playstation 4 (SDK only, no samples)
                    • +
                    • Sony Playstation Vita
                    • +
                    +

                    Development

                    +
                      +
                    • Microsoft Windows XP or later
                    • +
                    • Microsoft Visual Studio 2010, 2012 and 2013
                    • +
                    • Xcode 6.2
                    • +
                    +
                    +

                    Known Issues

                    +
                    +
                      +
                    • The combination of releasing an actor and reassigning the actors of any affected joint so that the joint no longer references the released actor will lead to a crash if these operations are performed as buffered calls ie after simulate but before fetchResults.
                    • +
                    +
                    +

                    Changes and Resolved Issues

                    +
                    + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

                    General

                    +
                      +
                    • Added support for Microsoft Visual Studio 2013 and removed support for Microsoft Visual Studio 2008.
                    • +
                    • Where applicable, mutexes on Unix flavored platforms now inherit priority to avoid priority inversion. This is a default behavior of Windows mutexes.
                    • +
                    • Added arm64 support for the iOS version of the PhysX SDK.
                    • +
                    • Removed samples from the iOS version of the PhysX SDK.
                    • +
                    • Fixed:
                    • +
                        +
                      • x64 samples running on Windows 8.
                      • +
                      • Concurrent calls to PxPhysics::(un)registerDeletionListener() and PxPhysics::(un)registerDeletionListenerObjects() were not safe.
                      • +
                      • PxGeometryQuery::pointDistance() could sometimes read uninitialized memory. This has been fixed.
                      • +
                      • The SDK will not crash anymore if an object is involved in more than 65535 interactions. Instead, it will emit an error message and the additional interactions will be ignored.
                      • +
                      • The PxPhysics::getMaterials() function did not work with a non-zero 'startIndex' parameter. This has been fixed.
                      • +
                      • The following static Android libs are now packed into a single library called PhysX3: LowLevel, LowLevelCloth, PhysX3, PhysXProfileSDK, PhysXVisualDebuggerSDK, PvdRuntime, PxTask, SceneQuery and SimulationController. This fixes cyclic dependencies between these libraries.
                      • +
                      • FSqrt(0), V4Sqrt(0), V4Length(0) and V3Length(0) will return 0 instead of QNan in Android and iOS.
                      • +
                      +
                    + +

                    Rigid Bodies

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • The Prismatic joint limit now acts correctly when its frame is not the identity.
                      • +
                      • Calling PxRigidDynamic::setGlobalPose() with the autowake parameter set to true could result in an assert when the rigid body got released and had the PxActorFlag::eDISABLE_SIMULATION flag set.
                      • +
                      • Added errors on misuse of PxRegister[Unified]Heightfields() function, and documented it.
                      • +
                      • PxConstraint has a eDISABLE_PREPROCESSING flag and minResponseThreshold attribute to assist in stabilizing stiffness caused by infinite inertias or mass modification.
                      • +
                      • Island manager performance in the presence of large numbers of kinematic actors has been improved.
                      • +
                      • Using PxConstraint::setActors() or PxJoint::setActors() could cause a crash if the new actors resulted in the constraint/joint being removed from the scene.
                      • +
                      • The functions PxRigidActor::setGlobalPose and PxShape::setLocalPose failed to update cached contact data internal to PhysX. This led to contacts being generated with transforms that were no longer valid. Similarly, contacts could be missed due to transforms being invalid. This defect affected the classes PxRigidStatic and PxRigidDynamic, though it was more immediately noticeable with the PxRigidStatic class.
                      • +
                      • The sphere-vs-mesh contact generation code has been improved. It could previously generate wrong contacts. This has been fixed.
                      • +
                      • The capsule-vs-convex contact generation had a bug that could lead to rare invalid contacts. This has been fixed.
                      • +
                      • The mesh contact generation had a bug on PS3 that could lead to invalid contacts. This has been fixed.
                      • +
                      • The PxRigidBody::clearForce() and PxRigidBody::clearTorque() were not properly decoupled - they both cleared the force and the torque. This has been fixed.
                      • +
                      • Repeatedly calling PxRigidActor::attachShape and PxRigidActor::detachShape in between calls to simulate resulted in a memory leak. This has been fixed.
                      • +
                      +
                    • Added:
                    • +
                        +
                      • Enabling CCD on kinematic actors is now disallowed. When the kinematic flags are raised on a CCD-enabled actor, CCD is automatically disabled.
                      • +
                      +
                    + +

                    Particles

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • + Consistency between GPU and CPU particles has been improved in the case of a spatial date structure overflow. The positions and velocities of particles that have the PxParticleFlag::eSPATIAL_DATA_STRUCTURE_OVERFLOW set + are now updated also for CPU particles. +
                      • +
                      • Fixed potential deadlocks from occurring in the GPU particle kernels running on GM204 and above GPUs.
                      • +
                      • Fixed fluid simulation crash on Titan X.
                      • +
                      +
                    + +

                    Cloth

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • A bug related to hitting the sphere or plane limit while world collision is enabled has been fixed.
                      • +
                      • PxCloth::getParticleAccelerations() implementation was fixed for GPU cloth.
                      • +
                      +
                    + +

                    Character controller

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • Character controllers cannot stand on dynamic triggers anymore.
                      • +
                      +
                    • Added:
                    • +
                        +
                      • added lockingEnabled parameter to PxCreateControllerManager(), to support thread-safe release of objects while the character controller's move() routine is executing.
                      • +
                      +
                    + +

                    Scene Queries

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • Raycasts against triangle meshes with large scales potentionaly failed to register a hit.
                      • +
                      • Overlaps against height field meshes with the flag eNO_BOUNDARY_EDGES potentionaly failed to register a hit.
                      • +
                      • Sweeps using shapes modelled around a space significantly offset from their geometric center could fail to register a hit.
                      • +
                      +
                    + +

                    Vehicles

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • Sticky tire friction was unreliable with more than one substep but is now fixed. This defect led to vehicles sliding down slopes where the sticky friction should have held firm.
                      • +
                      • An error in the jounce speed calculation that led to lift force at high forward speeds has been fixed. This defect led to instabilities at high speed.
                      • +
                      • Improved documentation for PxVehicleSuspsensionData::mSprungMass.
                      • +
                      +
                    + +

                    Cooking

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • Convex meshes generated from PhysX 3.2 were not able to load inside PhysX 3.3.
                      • +
                      +
                    + +
                    + + +
                    + + +

                    Release Notes - NVIDIA® PhysX® SDK 3.3.2

                    +

                    September 2014

                    + +

                    Supported Platforms

                    +
                    +

                    Runtime

                    +
                      +
                    • Apple iOS
                    • +
                    • Apple Mac OS X
                    • +
                    • Google Android ARM & x86 (version 2.2 or later required for SDK, 2.3 or later required for samples)
                    • +
                    • Linux (tested on Ubuntu)
                    • +
                    • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
                    • +
                    • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
                    • +
                    • Microsoft XBox One (SDK only, no samples)
                    • +
                    • Microsoft XBox 360
                    • +
                    • Nintendo Wii U
                    • +
                    • Sony Playstation 3
                    • +
                    • Sony Playstation 4 (SDK only, no samples)
                    • +
                    • Sony Playstation Vita
                    • +
                    +

                    Development

                    +
                      +
                    • Microsoft Windows XP or later
                    • +
                    • Microsoft Visual Studio 2008, 2010, 2012 (Windows RT only)
                    • +
                    • Xcode 4.6|5.0|5.0.1
                    • +
                    +
                    +

                    Known Issues

                    +
                    +
                      +
                    • The combination of releasing an actor and reassigning the actors of any affected joint so that the joint no longer references the released actor will lead to a crash if these operations are performed as buffered calls ie after simulate but before fetchResults.
                    • +
                    +
                    +

                    Changes and Resolved Issues

                    +
                    + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

                    General

                    +
                      +
                    • Added:
                    • +
                        +
                      • The PhysXCommon/64.dll, nvcuda.dll and PhysXUpdateLoader/64.dll are loaded and checked for the NVIDIA Corporation digital signature. The signature is expected on all NVIDIA Corporation provided dlls. The application will exit if the signature check fails.
                      • +
                      • Added the PxDefaultBufferedProfiler extension for simplified SDK profile events extraction.
                      • +
                      • PxSceneDesc::sanityBounds allows a bounding box to be set for validating the position coordinates of inserted or updated rigid actors and articulations.
                      • +
                      • Linux: Now supports GPU PhysX.
                      • +
                      • Added set/getRunProfiled() for PxDefaultCpuDispatcher to control profiling at task level.
                      • +
                      • Android: Support for x86 based devices was added.
                      • +
                      • PxProfileEventHandler::durationToNanoseconds() added. Translates event duration in timestamp (cycles) into nanoseconds.
                      • +
                      • Added SnippetProfileZone to show how to retrieve profiling information.
                      • +
                      • Added SnippetCustomJoint to better illustrate custom joint implementation, and removed SnippetExtension.
                      • +
                      • Added SnippetStepper to demonstrate kinematic updates while substepping with tasks.
                      • +
                      +
                    • Fixed:
                    • +
                        +
                      • PxTask::runProfiled() now takes threadId as a parameter.
                      • +
                      • The static pruner now issues a performance warning in debug and checked configurations when a tree rebuild occurs and the tree is not empty.
                      • +
                      • PxSceneDesc::staticStructure now defaults to PxPruningStructure::eDYNAMIC_AABB_TREE.
                      • +
                      • Linux: Switched to shared libraries.
                      • +
                      • Profile zone event names changed to match function calls.
                      • +
                      • Overlapping read/write errors will now issue a PxErrorCode::eINVALID_OPERATION rather than PxErrorCode::eDEBUG_INFO.
                      • +
                      • Improved SnippetToleranceScale to better demonstrate the intended use case.
                      • +
                      • Increased 126 characters limit for warnings on unix platforms, 1k limit on all platforms.
                      • +
                      • PhysXCommon dll load within PhysX dll now respects dll name. Please see the manual's PhysXCommon DLL load section.
                      • +
                      • Significant revision of the user's guide. Both structure and most content have been modified.
                      • +
                      • Fixed search function of user's guide.
                      • +
                      • Foundation math classes now have in-place arithmetic operators (+= etc).
                      • +
                      • PxScene::checkResults() no longer counts as a read API method. Hence it is now possible to call this method in blocking mode without causing all writes to block until it returns.
                      • +
                      +
                    • Deprecated:
                    • +
                        +
                      • Indexing operators taking signed integers in PxVec3, PxVec4, PxMat33, PxMat44, PxStrideIterator have been deprecated.
                      • +
                      +
                    + +

                    Rigid Bodies

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • A minor bug in contact generation between capsules and triangle meshes has been fixed, reducing the amount of tunneling cases when CCD is not used.
                      • +
                      • Discrete contact reports are no longer produced for pairs without PxPairFlag::eDETECT_DISCRETE_CONTACT raised in the filter shader. Previously, discrete contact generation would always have been performed regardless of the presence of the PxPairFlag::eDETECT_DISCRETE_CONTACT flag. This change potentially improves performance when using specific shapes for CCD-only collision, which would have previously generated discrtete contacts and then ignored them in the solver.
                      • +
                      • Trigger reports are no longer produced for pairs without PxPairFlag::eDETECT_DISCRETE_CONTACT raised in the filter shader. PxPairFlag::eTRIGGER_DEFAULT has been modified to include the PxPairFlag::eDETECT_DISCRETE_CONTACT flag.
                      • +
                      • An incorrect PX_DEPRECATED annotation on the default constructor for PxD6JointDrive has been removed.
                      • +
                      • PxRigidDynamic::getKinematicTarget() returned a wrong transform if the actor center of mass pose was different from the actor global pose.
                      • +
                      • Switching a PxRigidDynamic from dynamic to kinematic did not properly suppress existing pairs which turned into kinematic-kinematic or kinematic-static pairs.
                      • +
                      • PxRigidDynamic::isSleeping() did not return the correct value on the frame the object got inserted if PxScene::addActors() was used and if the object was awake.
                      • +
                      • PxSceneFlag::eDISABLE_CONTACT_CACHE now correctly works on PS3/SPU.
                      • +
                      • If an object was added to the scene, put asleep and had overlap with another sleeping object then contact points for that pair might not have been reported once the object woke up.
                      • +
                      • Potential crash when calling PxScene::resetFiltering() mulitple times for the same actor while the simulation was running has been fixed.
                      • +
                      • Potential crash when using PxScene::resetFiltering() with shapes that were just added while the simulation was running has been fixed.
                      • +
                      • A crash in MBP when deleting an object that just went out of broad-phase bounds has been fixed.
                      • +
                      • A new drive mode has been added to drive articulation joints using rotation vectors.
                      • +
                      • In contact and trigger reports, the shape references in PxTriggerPair and PxContactPair might not have been properly marked as removed shapes if the removal took place while the simulation was running.
                      • +
                      • PxPairFlag::eSOLVE_CONTACT is now properly observed if the flag is not set on a contacting pair. A consequence of this fix is that sleeping actors will no longer be woken up due to contact or lost contact with awake actors if PxPairFlag::eSOLVE_CONTACT is not set for the pair. This also affects kinematic-kinematic pairs if one kinematic of the pair moves out of contact with the other. Behaviour is unaffected for any pair that has PxPairFlag::eSOLVE_CONTACT set.
                      • +
                      • A memory leak with buffered shape and actor removal has been fixed. The memory leak occurred when the release of an actor's shapes was followed by the release of the actor, all in-between simulate() and fetchResults().
                      • +
                      • A bug was fixed which caused incorrect force reports to sometimes be reported.
                      • +
                      • Fixed a bug where incorrect normals were reported when using PCM contact gen.
                      • +
                      • Fixed some issues with scaled convex hulls in the PCM contact gen code path.
                      • +
                      • The accuracy of capsule collision code has been improved.
                      • +
                      • An isValid() method has been added to constraints, that is satisfied if and only if at least one actor is a dynamic body or articulation link
                      • +
                      • A check has been added to prevent constraint construction or modification that would leave the constraint invalid.
                      • +
                      • In checked builds the PxScene methods addActor(), addActors(), addAggregate() and addCollection() will warn and return if an invalid constraint would be added to the scene
                      • +
                      +
                    • Deprecated:
                    • +
                        +
                      • The following flags have been renamed and deprecated because the name did not properly reflect the root cause.
                      • +
                          +
                        • PxContactPairHeaderFlag
                        • +
                            +
                          • eDELETED_ACTOR_0 (use eREMOVED_ACTOR_0 instead)
                          • +
                          • eDELETED_ACTOR_1 (use eREMOVED_ACTOR_1 instead)
                          • +
                          +
                        • PxContactPairFlag
                        • +
                            +
                          • eDELETED_SHAPE_0 (use eREMOVED_SHAPE_0 instead)
                          • +
                          • eDELETED_SHAPE_1 (use eREMOVED_SHAPE_1 instead)
                          • +
                          +
                        • PxTriggerPairFlag
                        • +
                            +
                          • eDELETED_SHAPE_TRIGGER (use eREMOVED_SHAPE_TRIGGER instead)
                          • +
                          • eDELETED_SHAPE_OTHER (use eREMOVED_SHAPE_OTHER instead)
                          • +
                          +
                        +
                      +
                    + +

                    Vehicles

                    +
                      +
                    • Added:
                    • +
                        +
                      • In profile config the functions PxVehicleUpdates, PxVehiclePostUpdates and PxVehicleSuspensionRaycasts are now tracked with profile events (provided that a PxProfileZoneManager instance was passed to PxCreatePhysics). These profile events can be viewed in profile view in pvd, where the names of the profile events match the names of the tracked vehicle functions.
                      • +
                      +
                    • Fixed:
                    • +
                        +
                      • In checked config PxVehicleDriveTank::allocate enforces the rule that only tanks with even numbers of wheels are legal and warns when this rule is broken.
                      • +
                      • In checked config PxVehicleDrive4W::allocate enforces the rule that only vehicles with 4 wheels or more are legal and warns when this rule is broken.
                      • +
                      • PxWheelQueryResult::localPose was incorrectly only set when the vehicle had a corresponding PxShape, as described by PxVehicleWheelsSimData::setWheelShapeMapping. The localPose is now properly set independent of the mapping between wheel and shape.
                      • +
                      • Wheels resting on moving actors now properly observe the relative speed of the two actors when their relative speed is small. This fixes a bug where at small relative speeds the velocity of the other actor was assumed to be zero.
                      • +
                      • Repx serialization failed to serialize PxVehicleWheelsSimData::setMinLongSlipDenominator, PxVehicleWheelsSimData::setSubStepCount, PxVehicleWheelsSimData::disableWheel, PxVehicleWheelsSimData::enableWheel and the number of entries in the engine torque curve. These have now been fixed.
                      • +
                      • PxVehicleConcreteType used for vehicle serialization is now in the public API and has been moved to PxVehicleSDK.h.
                      • +
                      • Very small longitudinal and lateral slip angles could lead to numerical instabilities in some circumstances. A threshold has been introduced to reject very small slip angles by setting them to zero when they are below the threshold.
                      • +
                      • Vehicles now account for rigid bodies that have been given a zero inertia component in order to lock rotation around the corresponding axis.
                      • +
                      • Fixed a bug where the sticky wheel constraints sometimes didn't function correctly.
                      • +
                      +
                    + +

                    Cloth

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • The version number written to the fabric stream changed from PX_PHYSICS_VERSION to 1. A fabric can be created from streams written with version 3.3.0 and later until the stream format changes. Previously, the version of the producer and the consumer of the stream needed to match.
                      • +
                      • GPU cloth friction against convexes has been fixed.
                      • +
                      • A crash resulting from deleting a shape in proximity of a cloth with scene collision enabled has been fixed.
                      • +
                      +
                    + +

                    Scene Queries

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • PxMeshQuery::sweep now respects PxHitFlag::eMESH_BOTH_SIDES, and supports double-sided input triangles.
                      • +
                      • PxRigidBodyExt::linearSweepSingle and PxRigidBodyExt::linearSweepMultiple now correctly use query filter data instead of simulation filter data if filter data is not provided.
                      • +
                      • The spec for raycasts whose origin is located inside a solid object (sphere, box, capsule, convex) has changed back to what it was in 3.3.0. It was accidentally changed in 3.3.1. See the manual for details.
                      • +
                      • Convex sweeps against heightfields worked only when the heightfield had the identity transform. This has now been fixed to support arbitrary transforms again.
                      • +
                      +
                    + +

                    Cooking

                    +
                      +
                    • Added:
                    • +
                        +
                      • Using PxMeshPreprocessingFlag::eFORCE_32BIT_INDICES will always cook meshes with 32-bit triangle indices.
                      • +
                      +
                    • Fixed:
                    • +
                        +
                      • Convex hull cooking fix. Some input points could be ignored during cooking, fixed.
                      • +
                      • Inserted triangle meshes now respect 16 bit indices flag.
                      • + + +
                      +
                    + +

                    Geometry

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • PxHeightFieldDesc::thickness is now limited to [-PX_MAX_BOUNDS_EXTENTS, PX_MAX_BOUNDS_EXTENTS range]. (Previously unbounded).
                      • +
                      +
                    + +

                    Particles

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • Setting PxParticleReadDataFlag::eREST_OFFSET_BUFFER on a PxParticleBase instance that was not created with the per particle rest offset option (see PxPhysics::createParticleSystem, PxPhysics::createParticleFluid and PxParticleBaseFlag::ePER_PARTICLE_REST_OFFSET) is not supported. The unsupported configuration may have resulted in crashes. The SDK now rejects this configuration on calling PxParticleBase::setParticleBaseFlag and issues an appropriate warning to the error stream.
                      • +
                      • Performance improvements on Kepler and above GPUs running SPH.
                      • +
                      • In rare cases particle systems could access released memory when all interactions with a rigid body shape were lost.
                      • +
                      +
                    + +

                    Serialization

                    +
                      +
                    • Fixed:
                    • +
                        +
                      • PxBinaryConverter::convert potentially failed in checked mode with allocators that don't set 0xcd pattern. This has been fixed now.
                      • +
                      +
                    + +
                    + + + +
                    + + +

                    Release Notes - NVIDIA® PhysX® SDK 3.3.1

                    +

                    December 2013

                    + +

                    Supported Platforms

                    +
                    +

                    Runtime

                    +
                      +
                    • Apple iOS
                    • +
                    • Apple Mac OS X
                    • +
                    • Google Android (version 2.2 or later for SDK, 2.3 or later required for samples)
                    • +
                    • Linux (tested on Ubuntu)
                    • +
                    • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
                    • +
                    • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
                    • +
                    • Microsoft XBox One
                    • +
                    • Microsoft XBox 360
                    • +
                    • Nintendo Wii U
                    • +
                    • Sony Playstation 3
                    • +
                    • Sony Playstation 4 (SDK only, no samples)
                    • +
                    • Sony Playstation Vita
                    • +
                    +

                    Development

                    +
                      +
                    • Microsoft Windows XP or later
                    • +
                    • Microsoft Visual Studio 2008, 2010, 2012 (Windows RT only)
                    • +
                    • Xcode 4.6|5.0|5.0.1
                    • +
                    +
                    +

                    Known Issues

                    +
                    +
                      +
                      +

                      Changes and Resolved Issues

                      +
                      + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

                      General

                      +
                        +
                      • Added:
                      • +
                          +
                        • The friction model can now be changed after scene instantiation with PxScene::setFrictionType. The friction model can also be queried with PxScene::getFrictionType.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • PxDefaultSimulationFilterShader now supports particles and cloth as well.
                        • +
                        • PxSimulationFilterCallback: the provided actor and shape pointers are now defined as const. Note: this is no behavior change, it was never allowed to write to those objects from within the callback.
                        • +
                        • The PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICES and PxTriangleMeshFlag::eHAS_ADJACENCY_INFO enums have been deprecated. Please use PxTriangleMeshFlag::e16_BIT_INDICES and PxTriangleMeshFlag::eADJACENCY_INFO instead.
                        • +
                        • Removed following functions from the API for platforms which do not support CUDA: PxGetSuggestedCudaDeviceOrdinal, PxCreateCudaContextManager, PxLoadPhysxGPUModule.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Fixed concurrency issue on windows. Calling PxScene::simulate on multiple scenes concurrently may have caused a deadlock. This only happened if the scenes shared a single PxCpuDispatcher and the dispatcher was configured to use one worker thread only.
                        • +
                        +
                      + +

                      Rigid Bodies

                      +
                        +
                      • Added:
                      • +
                          +
                        • The projection direction for constraints can now be specified through the flags PxConstraintFlag::ePROJECT_TO_ACTOR0, ::ePROJECT_TO_ACTOR1.
                        • +
                        • A parameter has been added to PxRigidBodyExt::updateMassAndInertia() and ::setMassAndUpdateInertia() to optionally take non-simulation shapes into account for computing the mass and the inertia tensor of a rigid body.
                        • +
                        • It is now possible to retrieve additional information in contact reports. See the API documentation of PxContactPairHeader.extraDataStream, PxPairFlag::ePRE_SOLVER_VELOCITY, ::ePOST_SOLVER_VELOCITY, ::eCONTACT_EVENT_POSE for details.
                        • +
                        • The contact report system has been extended to support multiple notification events if the same two objects collide multiple times in multipass CCD scenarios. See the API documentation of PxPairFlag::eNOTIFY_TOUCH_CCD for details.
                        • +
                        + +
                      • Changed:
                      • +
                          +
                        • If touching objects were added to the scene asleep and one of them got woken up, then all contact pairs of the touching objects which contained a static rigid body were resolved with a delay of one simulation step. Now these pairs get resolved without delay in the next simulation step.
                        • +
                        • If touching objects were added to the scene asleep, eNOTIFY_TOUCH_FOUND contact reports were sent out for pairs of dynamic rigid bodies if requested. These reports will not be sent at the end of the simulation step after insertion anymore but rather at the end of the simulation step after the touching objects have been woken up.
                        • +
                        • Rigid bodies now permit zeroes in passed to setMass and setMassSpaceInertiaTensor. Zeroes are interpreted to indicate infinite mass or infinite moment of inertia around a given principal axis of inertia. Previously, zeroes were illegal values to these methods. Note that zeroes are still illegal for instances of PxArticulationLink.
                        • +
                        + +
                      • Fixed:
                      • +
                          +
                        • Reading back the kinematic target in the PxSimulationEventCallback::onContact() callback through PxRigidDynamic::getKinematicTarget() will now work.
                        • +
                        • Contact reports are no longer generated for contact pairs involving two sleeping kinematic actors or for pairs involving a sleeping kinematic actor in contact with a static actor. This fixes a bug that was introduced in 3.3.0.
                        • +
                        • No PxPairFlag::eNOTIFY_TOUCH_LOST event was sent in contact reports if a pair of sleeping rigid bodies got woken up after setting the pose on one of them (with autowake parameter set to false) and if the bounding boxes of the two objects still overlapped.
                        • +
                        • No PxPairFlag::eNOTIFY_TOUCH_PERSISTS event was sent in contact reports during the first simulation step after a pair of sleeping rigid bodies got woken up.
                        • +
                        • The inertia tensor computation for convex meshes has been adjusted to be more stable in certain cases where floating point precision issues arise. Furthermore, a fallback routine has been added to use an approximation if the diagonalized inertia tensor still ends up with invalid entries.
                        • +
                        • PxRigidBody::clearForce() and ::clearTorque() did not properly clear the respective properties if used with force mode PxForceMode::eIMPULES or PxForceMode::eVELOCITY_CHANGE.
                        • +
                        • Setting PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS also enabled PxSceneFlag::eENABLE_KINEMATIC_PAIRS internally and vice versa.
                        • +
                        • Missing validation checks for some joint set() methods have been added. Similarly to other API calls, when validation fails in the checked build PhysX will report an error and return without updating the joint.
                        • +
                        • Switching a kinematic rigid body to dynamic could lead to a crash in a subsequent simulation step, if the kinematic was moved and connected to another kinematic through a breakable PxConstraint/PxJoint.
                        • +
                        • Deleting a breakable PxConstraint/PxJoint while the simulation is running could lead to a crash if the PxConstraint/PxJoint broke in the same simulation step.
                        • +
                        • A bug in the PxScene::addBroadPhaseRegion() function, that could lead to a crash when using 'populateRegion=true', has been fixed.
                        • +
                        +
                      + +

                      Particles

                      +
                        +
                      • Added:
                      • +
                          +
                        • Added triangle mesh cache statistics for GPU particles. Triangle mesh cache statistics are also captured by PVD as part of simulation statistics.
                        • +
                        • Added new option to query approximate particle velocities relative to colliding rigid actors. This can be used for debris rotation on moving objects. Enable with PxParticleReadDataFlag::eCOLLISION_VELOCITY_BUFFER and read from PxParticleReadData::collisionVelocityBuffer.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Fixed a bug which might lead to GPU particle pipeline failures on low end GPUs.
                        • +
                        • Enabled warning when a spatial data structure overflow occured for GPU particles (see the guide for more information).
                        • +
                        +
                      + +

                      Cloth

                      +
                        +
                      • Fixed:
                      • +
                          +
                        • PxFilterFlag::eSUPPRESS was ignored for collision pairs that involved a PxCloth object. This does work now, however, please note that PxFilterFlag::eSUPPRESS is treated as PxFilterFlag::eKILL for pairs with a PxCloth object.
                        • +
                        +
                      + +

                      Serialization

                      +
                        +
                      • Added:
                      • +
                          +
                        • Support for binary compatibility between different sdk releases and patches has been added (PX_BINARY_SERIAL_VERSION). The current sdk version can load binary data of the older sdk versions listed in the documentation of PX_BINARY_SERIAL_VERSION.
                        • +
                        • SnippetLoadCollection has been added. It illustrates loading repx or binary serialized collections and instatiating the objects in a scene. It only compiles and runs on authoring platforms (windows, osx and linux).
                        • +
                        • SnippetConvert has been added. It illustrates how to convert PhysX 3 serialized binary files from one platform to another. It only compiles and runs on authoring platforms (windows, osx and linux).
                        • +
                        +
                      • Deprecated:
                      • +
                          +
                        • Method PxCollection::addRequire is deprecated, use PxCollection::add and PxCollection::contains instead.
                        • +
                        • Method PxCollection::createBinaryConverter(PxSerializationRegistry&) is deprecated, use PxCollection::createBinaryConverter() instead.
                        • +
                        +
                      + +

                      Character controller

                      +
                        +
                      • Added:
                      • +
                          +
                        • PxControllerManager::setPreventVerticalSlidingAgainstCeiling() has been added, to control the behaviour of characters against ceilings.
                        • +
                        +
                      + + +

                      Vehicles

                      +
                        +
                      • Added:
                      • +
                          +
                        • Vehicles may now be updated concurrently through the addition of a new function PxVehiclePostUpdates and passing a PxVehicleConcurrentUpdateData array to PxVehicleupdates.
                        • +
                        • A new snippet SnippetVehicleMultiThreading has been added to show the operation of concurrent vehicle updates.
                        • +
                        • PxVehicleDriveTankControl and PxVehicleDriveTankControlModel now have improved doxy comments.
                        • +
                        • A new function PxVehicleUpdateCMassLocalPose has been added to help update a vehicle after the center of mass pose of the vehicle's actor has been modified.
                        • +
                        • PxWheelQueryResult now records the local pose of the wheel.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • PxVehcicleDrive4W::setup now tests that at least 4 wheels are specified and returns wtih an error message if numWheels < 4. It is only possible to create a PxVehicleDrive4W instance with less than 4 active wheels by disabling wheels after instantiating a 4-wheeled car.
                        • +
                        • In debug and checked build configurations PxVehicleComputeSprungMasses now reports whether the sprung masses were successfully computed. Warnings are passed to the error stream in checked configuration if the function does not complete successfully. Apart from error checking the operation of the function is unchanged.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • The doxy comment describing the default setting for PxVehicleWheelsSimData::setWheelShapeMapping was incorrect. This now correctly documents the default mapping as PxVehicleWheelsSimData::setWheelShapeMapping(i,i).
                        • +
                        • Suspensions raycasts that start inside geometry are ignored for all geometry types. Prior to this release this was true for all geometry types except for heightfields and triangle meshes. This inconsistency has now been fixed so that all geometry types obey the rule that suspension raycasts starting inside geometry are neglected.
                        • +
                        +
                      + +

                      Scene queries

                      +
                        +
                      • Added:
                      • +
                          +
                        • Added eMTD flag. If an initial overlap is detected, this flag triggers the sweep to compute the MTD (Minimum Translation Direction), which can be used to de-penetrate the query shape from the shape with which an initial overlap was found. In this case, the distance reported will be negative. This negative distance can be used to scale the reported normal to generate the translation vector required to de-penetrate the query shape.
                        • +
                        • Added PxTriangle::pointFromUV.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • A rare ray-capsule intersection bug has been fixed, when the capsule's height is close to zero.
                        • +
                        • A capsule-capsule distance bug has been fixed, when the tested capsules are large and parallel.
                        • +
                        • Raycasts against heightfields now correctly return triangle UVs.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • + PxBatchQuery::raycast, overlap and sweep previously had an incorrect const modifier indicating that these methods were safe to call from multiple threads simultaneously. This has been removed. + Multiple batch queries can still be executed (via PxBatchQuery::execute()) in parallel. +
                        • +
                        +
                      + +

                      Cooking

                      +
                        +
                      • Added:
                      • +
                          +
                        • PxCookingParams::meshSizePerformanceTradeOff parameter can be used to make the mesh smaller at the expense of reduced simulation and scene query performance (or the other way around).
                        • +
                        • PxCookingParams::meshCookingHint parameter can be used to specify mesh hierarchy construction preference (cooking speed or simulation speed).
                        • +
                        • PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH disables mesh clean proces. Vertices duplicities are not searched, huge triangles test is not done. Vertices welding is not done. Does speed up the cooking.
                        • +
                        • PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE disables vertex edge precomputation. Makes cooking faster but slow up contact generation.
                        • +
                        • PxPhysicsInsertionCallback adds the support for inserting cooked triangle mesh or height field directly into PxPhysics without the stream serialization.
                        • +
                        • PxCooking::createTriangleMesh creates triangle mesh and inserts it to PxPhysics without using the stream serialization.
                        • +
                        • PxCooking::createHeightField creates height field and inserts it to PxPhysics without using the stream serialization.
                        • +
                        • PxCooking::validateTriangleMesh validates mesh in separate function before it can be cooked without the mesh cleaning.
                        • +
                        • PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES checks and removes almost zero-area triangles during the computation of the convex hull.
                        • +
                        • PxCookingParams::areaTestEpsilon triangle area size was added. This epsilon is used for the zero-area test in the computation of the convex hull.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • PxCooking::computeHullPolygons does now return also vertices used by the polygons. Redundant vertices are removed.
                        • +
                        • PxCooking::cookConvexMesh now returns a PxConvexMeshCookingResult::Enum with additional error information.
                        • +
                        +
                      + +

                      Aggregates

                      +
                        +
                      • Added:
                      • +
                          +
                        • PxSceneLimits has a new member variable maxNbAggregates. Setting this value to a good approximation of the peak number of aggregates will avoid the need for internal run-time allocations that can occur when aggregates are added to the scene.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • PxScene::removeActor will auotmatically remove that actor from all PxAggregate instances that contain the removed actor. Likewise, PxScene::removeArticulation will automatically remove all articulation links from all relevant aggregates. This fix upholds the rule that all actors of an aggregate must be in same scene.
                        • +
                        • The condition of an internal assert that triggered after calling PxScene::addAggregate has been corrected. This assert triggered when an aggregate was added to the scene after removal of all aggregates from the scene. The operation of the function PxScene::addAggregate is unchanged apart from the asserted condtition.
                        • +
                        +
                      + +

                      Samples

                      +
                        +
                      • Changed:
                      • +
                          +
                        • Starting with Release 302 drivers, application developers can direct the Optimus driver at runtime to use the High Performance Graphics to render any application - even those applications for which there is no existing application profile. The samples now make use of this feature to enable High Performance Graphics by default.
                        • +
                        +
                      +
                      + + +
                      + + +

                      Release Notes - NVIDIA® PhysX® SDK 3.3

                      +

                      September 2013

                      + +

                      Release Highlights

                      +
                      +
                        +
                      • Added PhysXDevice/64.dll to the PC packages. See the Windows readme for details.
                      • +
                      • Added support for the NVIDIA Kepler GPU architecture.
                      • +
                      • Added support for the Nintendo Wii U console.
                      • +
                      • Added support for Windows 8 Modern UI applications (ARM and x86).
                      • +
                      • Ported our SIMD library to the ARM NEON architecture.
                      • +
                      • Multi Box Pruning (MBP) is offered as an alternative broad phase algorithm to Sweep And Prune (SAP). MBP shows improved performance when most objects are moving or when inserting large numbers of objects. SAP can be faster in scenarios with few moving (many sleeping) objects.
                      • +
                      • Significant performance and stability optimizations for rigid body solver.
                      • +
                      • New function to compute the minimum translational distance and direction to separate two overlapping geometry objects.
                      • +
                      • New 'PCM' contact generation mode which is often faster and more robust than the still available legacy path.
                      • +
                      • Improved performance of scene queries and contact reports.
                      • +
                      • Improved behavior and performance of Continuous Collision Detection (CCD).
                      • +
                      • Reduced memory footprint of rigid body classes.
                      • +
                      • Added support for sharing shapes among rigid bodies.
                      • +
                      • Significantly improved cloth behavior and GPU performance.
                      • +
                      • Added support for cloth colliding against itself, other cloth instances, and scene geometry.
                      • +
                      • Improved useability of binary and xml serialization.
                      • +
                      • Memory can be saved for objects that do not participate in the simulation and are used for scene queries only. For details see the new flag PxActorFlag::eDISABLE_SIMULATION.
                      • +
                      +
                      + +

                      Supported Platforms

                      +
                      +

                      Runtime

                      +
                        +
                      • Apple iOS
                      • +
                      • Apple Mac OS X
                      • +
                      • Google Android (version 2.2 or later for SDK, 2.3 or later required for samples)
                      • +
                      • Linux (tested on Ubuntu)
                      • +
                      • Microsoft Windows XP or later (NVIDIA Driver version R304 or later is required for GPU acceleration)
                      • +
                      • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
                      • +
                      • Microsoft XBox One
                      • +
                      • Microsoft XBox 360
                      • +
                      • Nintendo Wii U
                      • +
                      • Sony Playstation 3
                      • +
                      • Sony Playstation 4 (SDK only, no samples yet)
                      • +
                      • Sony Playstation Vita
                      • +
                      +

                      Development

                      +
                        +
                      • Microsoft Windows XP or later
                      • +
                      • Microsoft Visual Studio 2008, 2010, 2012 (Windows RT/PS4/XboxOne only)
                      • +
                      • Xcode 4.6
                      • +
                      +
                      + +

                      Known Issues

                      +
                      +
                        +
                      • PxSimulationOrder::eSOLVE_COLLIDE feature is not implemented in this release. Calls to PxScene::solve() and PxScene::collide() will be ignored with a warning added to the error stream
                      • +
                      • Reading back the kinematic target through PxRigidDynamic::getKinematicTarget() in the PxSimulationEventCallback::onContact() callback will fail.
                      • +
                      • Cloth self-collision without rest position is not supported on GPUs with SM capability lower than 2.0.
                      • +
                      +
                      +

                      + Changes and Resolved Issues

                      +
                      + + Note: Platform specific issues and changes can be found in the readme file of the corresponding platform. + +

                      General

                      +
                        +
                      • Added:
                      • +
                          +
                        • PxScene::setLimits: Hint to preallocate capacities of various data structures. See PxSceneLimits.
                        • +
                        • Scalar constructors PxQuat(r), PxMat33(r), PxMat33(r).
                        • +
                        • identity constructors for PxMat33, PxMat44, PxQuat, PxTransform - e.g. PxTransform(PxIdentity).
                        • +
                        • zero constructors for PxMat33, PxMat44, PxVec3 - e.g. PxMat33(PxZero).
                        • +
                        • PxTransform(x, y, z) constructor was added as a shortcut for PxTransform(PxVec3(x,y,z)).
                        • +
                        • The GPU code uses CUDA version 5.0 and supports Kepler GPUs (SM version 3.0 and 3.5). +
                        • Helper method PxContactPair::bufferContacts() has been added to copy the contact pair data stream into a user buffer.
                        • +
                        • PxGeometryQuery::computePenetration() has been added, to compute the Minimum Translational Distance between geometry objects.
                        • +
                        • Ability for APEX and other PhysX extensions to change the PhysX Visual Indicator.
                        • +
                        • Reporting allocation names can now be enabled or disabled (see PxFoundation::setReportAllocationNames). When enabled, some platforms allocate memory through 'malloc'.
                        • +
                        • The scene origin can now be shifted to better support big world scenarios. See PxScene::shiftOrigin() for details.
                        • +
                        • PxAssertHandler got extended with a boolean parameter to support ignoring specific asserts. See windows implementation of DefaultAssertHandler.
                        • +
                        • Added new PxPairFlags - PxPairFlag::eSOLVE_CONTACT and PxPairFlag::eDETECT_DISCRETE_CONTACT.
                        • +
                        +
                      • Removed:
                      • +
                          +
                        • The obsolete PxConvexFlag::eUSE_UNCOMPRESSED_NORMALS flag has been removed.
                        • +
                        • The PxSceneFlag::eDISABLE_SSE was obsolete, and has now been removed.
                        • +
                        • The obsolte PxPtrArray has been removed
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • Mesh BVH construction was significantly improved for meshes with a mix of large and small triangles. Mesh sizes are now slightly increased, but traversals are substantially faster. As a side effect, cooked mesh format has changed. This requires meshes to be recooked!
                        • +
                        • The specification for valid PxBounds3 representations has changed. See the API documentation for details (especially the newly introduced PxBounds3::isValid() method).
                        • +
                        • PxBounds3::transform(), ::scale() and ::fatten() have been split into a fast and a safe version to avoid unnecessary checks and improve stability for empty bounds respectively.
                        • +
                        • PxBounds3::setInfinite() has been renamed to PxBounds3::setMaximal() to better fit the actual behavior.
                        • +
                        • PxTask: PVD profile events for tasks are now only emitted in profile builds (all platforms).
                        • +
                        • Platform mutex implementations now verify that lock/unlock come from correct thread in debug builds.
                        • +
                        • PxWindowsDelayLoadHook.h has been moved from Include/foundation/windows to Include/common/windows.
                        • +
                        • API instances of 'Num' as in 'maxNum' and 'getNum' have changed uniformly to 'Nb'.
                        • +
                        • + The following classes have been renamed: +
                            +
                          • PxSceneQueryHit to PxQueryHit
                          • +
                          • PxSceneQueryFlags to PxHitFlags
                          • +
                          • PxSceneQueryHitType to PxQueryHitType
                          • +
                          • PxSceneQueryFilterData to PxQueryFilterData
                          • +
                          • PxSceneQueryFilterCallback to PxQueryFilterCallback
                          • +
                          • PxSceneQueryFilterFlags to PxQueryFlags
                          • +
                          • PxSceneQueryCache to PxQueryCache
                          • +
                          • PxCCTNonWalkableMode to PxControllerNonWalkableMode
                          • +
                          • PxControllerFlags to PxControllerCollisionFlags
                          • +
                          • PxCCTHit to PxControllerHit
                          • +
                          • PxConstraintDominance to PxDominanceGroupPair
                          • +
                          • PxActorTypeSelectionFlags to PxActorTypeFlags
                          • +
                          • PxFindOverlapTriangleMeshUtil to PxMeshOverlapUtil
                          • +
                          + The previous names have been retained for compatibility but are deprecated. +
                        • +
                        • PX_SLEEP_INTERVAL has been replaced with the new parameter PxSceneDesc::wakeCounterResetValue to specify the wake counter value to set when wakeUp() gets called on dynamic objects.
                        • +
                        • PxClientBehaviorBit has been renamed PxClientBehaviorFlag, PxActorClientBehaviorBit has been renamed PxActorClientBehaviorFlag. Names of related functions have also changed.
                        • +
                        • queryClient parameter in raycast(), sweep(), overlap() functions was moved inside of PxQueryFilterData struct.
                        • +
                        • The PxObserver/PxObservable system has been replaced by the PxDeletionListener API. The supported object types have been extended from PxActor to all core objects inheriting from PxBase. Furthermore, two kinds of deletion events are now distinguished: user release and memory release. Please read the API documentation for details.
                        • +
                        • Deprecated PxPairFlag::eRESOLVE_CONTACT. Use PxPairFlag::eDETECT_DISCRETE_CONTACT and PxPairFlag::eSOLVE_CONTACT instead.
                        • +
                        • Number of materials per shape is now PxU16 instead of PxU32, contact material information also now returns PxU16.
                        • +
                        • Maximum number of touching hits in batched queries is now PxU16 instead of PxU32.
                        • +
                        • SweepEpsilonDistance has been replaced by meshContactMargin and marked as deprecated. Please read the API documentation for details.
                        • +
                        • PxShape::resetFiltering() and PxParticleBase::resetFiltering() have been deprecated. Please use one of the new overloaded methods PxScene::resetFiltering() instead.
                        • +
                        • The pxtask namespace has been removed and it's types have been added to the physx namespace with a Px* prefix
                        • +
                        • The delay load hook PxDelayLoadHook::setPhysXInstance has been renamed to PxSetPhysXDelayLoadHook and PxDelayLoadHook::setPhysXCookingInstance has been renamed to PxSetPhysXCookingDelayLoadHook
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Calling Thread::setAffinityMask() before the thread has been started is now supported.
                        • +
                        • PxDefaultSimulationFilterShader ignored the value of the second filter constant in the collision filtering equation and used the value of the first filter constant instead.
                        • +
                        +
                      • Deprecated:
                      • +
                          +
                        • The PxScene::flush() method has been deprecated, please use PxScene::flushSimulation().
                        • +
                        • PxRigidDynamicFlag has been deprecated and replaced with PxRigidBodyFlag to allow flags to be shared between PxArticulationLink and PxRigidDynamic.
                        • +
                        • PxTransform::createIdentity(), PxQuat::createIdentity(), PxMat33::createIdentity(), PxMat44::createIdentity(), PxMat33::createZero(), PxMat44::createZero()
                        • +
                        +
                      + +

                      Character controller

                      +
                        +
                      • Added:
                      • +
                          +
                        • The PxControllerBehaviorFlag::eCCT_USER_DEFINED_RIDE flag has been added.
                        • +
                        • A new helper function PxController::resize() has been added to facilitate character controller resizing.
                        • +
                        • A new runtime tessellation feature has been added that can help reducing FPU accuracy issues in the sweep tests.
                        • +
                        • PxControllerFilterCallback has been added to make CCT-vs-CCT filtering more flexible.
                        • +
                        • An overlap recovery module has been added. See PxControllerManager::setOverlapRecoveryModule
                        • +
                        • PxObstacle notifications has been added to handle touched obstacles.
                        • +
                        • PxObstacleContext::getObstacleByHandle has been added.
                        • +
                        • The origin of character controllers and obstacles can now be shifted to stay in sync when the origin of the underlying PxScene is shifted. See PxControllerManager::shiftOrigin() for details.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • The PxControllerManager is now tightly coupled to a PxScene which has to be provided on creation. See PxCreateControllerManager().
                        • +
                        • The PxObstacleContext instances of a PxControllerManager will now get released automatically when the manager is released.
                        • +
                        • PxController::reportSceneChanged() has been renamed to PxController::invalidateCache().
                        • +
                        • PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT is not the default behavior anymore.
                        • +
                        • PxCCTNonWalkableMode::eFORCE_SLIDING has been renamed to PxCCTNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING.
                        • +
                        • PxCCTInteractionMode has been renamed PxControllerInteractionMode
                        • +
                        • PxCCTNonWalkableMode has been renamed PxControllerNonWalkableMode
                        • +
                        • PxCCTHit has been renamed PxControllerHit
                        • +
                        • Touched PxObstacle has been replaced by touched ObstacleHandle.
                        • +
                        • PxControllerInteractionMode and PxControllerFilters::mActiveGroups have been removed. Please use the new PxControllerFilterCallback instead.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Bugs with respect to deleting shapes from a touching actor have been fixed.
                        • +
                        • Touched actor's scene is checked in CCT::move before rideOnTouchedObject is called to ensure that the touched shape is valid.
                        • +
                        • Touched shape's scene query flag is checked in CCT::move before rideOnTouchedObject is called to ensure that the touched shape is valid.
                        • +
                        • Touched shape's user CCT filtering is triggered in CCT::move before rideOnTouchedObject is called to ensure that the touched shape is valid.
                        • +
                        • The PxControllerBehaviorCallback was not called when a PxUserControllerHitReport was not defined.
                        • +
                        • It is not possible anymore to create a CCT whose contact offset is zero. The doc has always said a non-zero value was expected, but corresponding check was missing.
                        • +
                        • Box & capsule controllers' resize function now properly take the up direction into account
                        • +
                        +
                      + +

                      CCD

                      +
                        +
                      • Added:
                      • +
                          +
                        • Introduced PxRigidBodyFlag::eENABLE_CCD_FRICTION to control whether friction is applied inside CCD on a given body. This is disabled by default. In general, disabling friction in CCD improves the behavior of high-speed collisions.
                        • +
                        • Introduced PxSceneDesc::ccdMaxPasses field, which controls the maximum number of CCD passes. Each CCD pass will advance each object to its next TOI. By default, we use 1 pass. Increasing the number of passes can reduced the likelihood of time being dropped inside the CCD system at the cost of extra CCD procesing overhead.
                        • +
                        • Introduced per-body value to control how far past the initial time of impact the CCD advances the simulation. Advancing further can improve fluidity at the increased risk of tunneling.
                        • +
                        • CCD now has limited support contact modification. It supports disabling response to contacts by setting max impulse to 0. It does not yet support target velocity, non-zero max impulse values or scaling mass or inertia.
                        • +
                        • Introduced new PxPairFlag eDETECT_CCD_CONTACT. This flags is used to control whether CCD performs sweep tests for a give pair. Decision over whether any collisions are responded to is made by the presence of the flag eSOLVE_CONTACT.
                        • +
                        +
                      • Removed:
                      • +
                          +
                        • CCD is now enabled per-body instead of per-shape. As a result, PxShapeFlag::eUSE_SWEPT_BOUNDS has been removed and replaced with PxRigidBodyFlag::eENABLE_CCD.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • API attributes previously named SWEPT_INTEGRATION have now been renamed 'ccd': specifically PxSceneFlag::eENABLE_SWEPT_INTEGRATION, PxSceneFlag::eSWEPT_INTEGRATION_LINEAR, PxSceneDesc::sweptIntegrationLinearSpeedFactor, PxSceneDesc::sweptIntegrationAngularSpeedFactor, PxPairFlag::eENABLE_SWEPT_INTEGRATION
                        • +
                        • PxPairFlag::eCCD_LINEAR has been deprecated. Use (PxPairFlag::eDETECT_CCD_CONTACT | PxPairFlag::eSOLVE_CONTACT) instead.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Updated CCD algorithm which improves the fluidity of motion of the CCD system while reducing the processing overhead significantly.
                        • +
                        • Contact notification is now reliable in CCD. CCD contacts are appended to the end of the contact list for a given pair so that both discrete and continuous contacts are reported
                        • +
                        • CCD contact notification now reports applied forces. These contacts will be correctly filtered by force thresholds.
                        • +
                        +
                      + +

                      Serialization

                      +
                        +
                      • Added:
                      • +
                          +
                        • Added extension PxSerialization::isSerializable method to query whether a collection is serializable.
                        • +
                        • Added extension PxSerialization::complete which allows to prepare a collection for serialization.
                        • +
                        • Added extension PxSerialization::createNames which adds reference names to serializables.
                        • +
                        • Added extension PxCollectionExt::remove which removes all serializables of a certain type and optionally adds them to another collection.
                        • +
                        • Added extension function PxCollectionExt::releaseObjects to remove and release objects from a collection
                        • +
                        • Added class PxSerializationRegistry for handling custom serializable types.
                        • +
                        • Added PxSerialization::serializeCollectionToXml and PxSerialization::createCollectionFromXml
                        • +
                        • Included pre-built binary meta data with SDK at [path to installed PhysX SDK]/Tools/BinaryMetaData
                        • +
                        • Added class PxSerializer to provide serialization specific functionality to serializable classes.
                        • +
                        • Added classes PxSerializationContext and PxDeserializationContext to provide functionality for serialization and deserialization operations.
                        • +
                        +
                      • Removed:
                      • +
                          +
                        • Removed PxUserReferences class and PxPhysics::createUserReferences.
                        • +
                        • Removed RepX.h and unified serialization interfaces for xml and binary serialization.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • PxCollection was reworked to improve reference management rendering PxUserReferences obsolete. PxCollection was also decoupled more from Serialization/Deserialization functionality. Serialization of incomplete collections fails now early at serialization as opposed to late at deserialization.
                        • +
                        • Replaced PxPhysics::createCollection with PxCreateCollection. +
                        • +
                        • Replaced RepXCollection with PxCollection, and unified corresponding interfaces.
                        • +
                        • Replaced PxCollection::serialize with PxSerialization::serializeCollectionToBinary.
                        • +
                        • Replaced PxCollection::deserialize with PxSerialization::createCollectionFromBinary.
                        • +
                        • Replaced PxSerializable::collectForExport(PxCollection& c) with PxSerializer::requires. The new method works in a non-recursive way.
                        • +
                        • Replaced PxDumpMetaData with PxSerialization::dumpMetaData.
                        • +
                        • Replaced PxCollectForExportSDK with PxCollectionExt::createCollection(PxPhysics& sdk).
                        • +
                        • Replaced PxCollectForExportScene with PxCollectionExt::createCollection(PxScene& scene).
                        • +
                        • Moved PxCooking::createBinaryConverter to PxSerialization
                        • +
                        • Changed PxShape release semantics for shapes. As a consequence deserialized shapes are never autmatically released, but need to be released by the application. Exception: PxPhysics::release.
                        • +
                        +
                      + +

                      Cloth

                      +
                        +
                      • Added:
                      • +
                          +
                        • Improved GPU cloth performance significantly with new parallel solver.
                        • +
                        • Added tether constraints, which allow minimum amount of cloth stretching even for large gravity scales. See PxClothFabric.
                        • +
                        • Added support for dynamic addition and deletion of collision primitives. See PxCloth::addCollision* and PxCloth::removeCollision*.
                        • +
                        • Added triangle mesh collider support. See PxCloth::setCollisionTriangles.
                        • +
                        • Added support for self collision and inter-cloth collision. See PxCloth::setSelfCollision*() and PxScene::setClothInterCollision*().
                        • +
                        • Added direct access to CUDA particle data for graphics interoperability, see PxCloth::lockParticleData()
                        • +
                        • Added PxRegisterCloth to reduce binary size by stripping unused code on platforms where static linking is used.
                        • +
                        • Added methods setWakeCounter/getWakeCounter() (see the corresponding API documentation for details).
                        • +
                            +
                          • It is illegal to call wakeUp/putToSleep/isSleeping() on a PxCloth that has not been added to a scene.
                          • +
                          +
                        +
                      • Changed:
                      • +
                          +
                        • Cloth solver does not use fibers any more. See PxClothFabric for changes in fabric API.
                        • +
                        • Moved PxCooking.cookClothFabric() to extensions. See PxClothFabricCooker and PxClothFabricCreate.
                        • +
                        • PxClothMeshDesc has been moved to extensions and now supports both triangle and quad representations. See PxClothMeshDesc.
                        • +
                        • The scaling of damping and stiffness coefficients has been separated from the solver frequency and can now be set indepedently using PxCloth::setStiffnessFrequency().
                        • +
                        • PxCloth::setInertiaScale() has been split into linear, angular, and centrifugal components. See PxCloth::set*IntertiaScale.
                        • +
                        • Drag coefficient has been split into linear and angular coefficient. See PxCloth::setLinearDragCoefficient and PxCloth::setAngularDragCoefficient.
                        • +
                        • Renamed PxCloth::lockClothReadData() to lockParticleData(). Added support for update operations on the returned particle arrays (as an alternative to setParticles()).
                        • +
                        • PxCloth::wakeUp() does not have a parameter anymore. Use setWakeCounter() instead to set a specific value.
                        • +
                        • PxCloth::getNbCollisionSpherePairs() has been renamed to PxCloth::getNbCollisionCapsules()
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Fixed a crash bug in the clothing collision code appearing on iOS.
                        • +
                        +
                      + +

                      Rigid Bodies

                      +
                        +
                      • Added:
                      • +
                          +
                        • The contact distance parameter for a limit is automatically estimated if not supplied in the constructor for the limit structure.
                        • +
                        • The new callback PxConstraintConnector::onOriginShift() has been introduced. It gets called for all constraints of a scene, when its origin is shifted.
                        • +
                        • New helper function PxRigidBodyExt::computeVelocityDeltaFromImpulse has been added.
                        • +
                        • Shapes may be declared as shared on creation, and then attached to multiple actors, see the user manual for restrictions.
                        • +
                        • + Since a shape is no longer necessarily associated with a unique actor, references to shapes in callbacks from the engine are accompanied by the + a reference to the associated actor +
                        • +
                        • Joints and contact modification now support tuning relative mass and inertia for the bodies on a per-contact basis. Inertia and mass can be tuned independently.
                        • +
                        +
                      • Removed:
                      • +
                          +
                        • PxShape::overlap(), PxShape::sweep() and PxShape::raycast() have been removed. Equivalent functionality is provided in PxGeometryQuery.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • It is illegal to call resetFiltering() on a PxShape or PxParticleBase if they have not been added to a scene.
                        • +
                        • It is illegal to call addForce/addTorque/clearForce/clearTorque() on a PxRigidBody that has not been added to a scene.
                        • +
                        • + The sleep behavior of dynamic rigid bodies has changed significantly (for details on the current behavior see the API documentation of isSleeping(), wakeUp(), putToSleep(), setKinematicTarget(), PxRigidBodyFlag::eKINEMATIC, ...). Among the changes are: +
                            +
                          • The methods setWakeCounter/getWakeCounter() have been added for PxRigidDynamic and PxArticulation objects (see the corresponding API documentation for details).
                          • +
                          • The wakeUp() method of PxRigidDynamic and PxArticulation has lost the wake counter parameter. Use setWakeCounter() instead to set a specific value.
                          • +
                          • It is illegal to call wakeUp/putToSleep/isSleeping() on a PxRigidDynamic or PxArticulation that has not been added to a scene.
                          • +
                          • Putting a dynamic rigid actor to sleep will clear any pending force updates.
                          • +
                          • Switching a dynamic actor to kinematic will put the actor to sleep immediately.
                          • +
                          • Switching a kinematic actor back to dynamic will not affect the sleep state (previously the actor was woken up).
                          • +
                          • Calling wakeUp/putToSleep() on a kinematically controlled dynamic actor is not valid any longer. The sleep state of a kinematic actor is solely defined based on whether a target pose has been set (see API documentation of isSleeping() for details).
                          • +
                          • A call to PxRigidBody::setCMassLocalPose() does not wake up the actor anymore. Note: this also affects related methods in PhysXExtensions like PxRigidBodyExt::updateMassAndInertia() etc.
                          • +
                          • If a non-zero velocity or force is set through PxRigidBody::setLinearVelocity(), ::setAngularVelocity(), ::addForce() or ::addTorque(), the actor will get woken up automatically even if the autowake parameter is false.
                          • +
                          • PxRigidBody::clearForce() and ::clearTorque() do not have the autowake parameter, to optionally wake the actor up, anymore. These methods will not change the sleep state any longer. Call ::wakeUp() subsequently to get the old default behavior.
                          • +
                          • Adding or removing a PxConstraint/PxJoint to/from the scene does not wake the connected actors up automatically anymore.
                          • +
                          • It is now possible to avoid automatic wake up of previously touching objects on scene removal. See the additional parameter wakeOnLostTouch in PxScene::removeActor(), ::removeArticulation(), ::removeAggregte(), PxRigidActor::detachShape().
                          • +
                          +
                        • +
                        • PxJointLimit and PxJointLimitPair are now PxJointLinearLimit, PxJointLinearLimitPair, PxJointAngularLimitPair, depending on whether the limit is linear or angular.
                        • +
                        • Joints now solve for the entire position error rather than a ratio of 0.7 of it. The flag PxConstraintFlag::eDEPRECATED_32_COMPATIBILITY can be used to restore this behavior
                        • +
                        • PxConstraintFlag::Type has been renamed to PxConstraintFlag::Enum +
                        • +
                        • The spring constant parameter in joints and articulations that was previously 'spring' is now 'stiffness'.
                        • +
                        • The tangential spring constant parameter in articulations that was previously 'tangentialSpring' is now 'tangentialStiffness'.
                        • +
                        • Constraints do not respect PxDominanceGroup settings. Use PxJoint::setInvMassScale and setInvInertiaScale
                        • +
                        • Shapes are reference counted. PxShape::release() now decrements the reference count on a shape, and its use is deprecated for detaching a shape from its actor - use detachShape() instead.
                        • +
                        • Shape creation methods do not take a local transform parameter anymore. Instead PxShapeFlags can be specified. Triangle meshes, height fields and plane geometry shapes cannot be combined with non-kinematic PxRigidDynmic actors if PxShapeFlag::eSIMULATION_SHAPE is specified. Corresponding calls to PxRigidActor::createShape() or PxRigidActor::attachShape() are not supported.
                        • +
                        • PxShape::getActor() now returns a pointer, which is NULL if the shape is shareable.
                        • +
                        • PxShape::getWorldBounds() has been replaced with PxShapeExt::getWorldBounds().
                        • +
                        • PxContactPoint has been renamed PxFeatureContact.
                        • +
                        • The internal format for contact storage has been modified; applications directly accessing the internal contact representation rather than PxContactPair::extractContacts should be modified accordingly.
                        • +
                        • Friction mode flags eENABLE_ONE_DIRECTIONAL_FRICTION and eENABLE_TWO_DIRECTIONAL_FRICTION have been replaced by PxFrictionType::Enum PxSceneDesc::frictionType.
                        • +
                        • PxSceneDesc::contactCorrelationDistance has been deprecated.
                        • +
                        • PxSceneDesc::contactCorrelationDistance no longer has an influence on how many friction anchors are created in a single frame, only on when they are removed in later frames. This may cause a very minor change in friction behavior.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Rigid bodies now properly accumulate the forces/torques that are applied with addForce/addTorque while scene insertion is still pending. This affects bodies added to the scene while the scene is simulating and then given forces/torques with addForce/addTorque while the scene is simulating. These accumulated forces and torques are applied during the next simulate() call. Prior to this fix the forces/torques that accumulated while scene insertion was pending were lost and never applied.
                        • +
                        • Its now possible to serialize collections with jointed actors without including the corresponding joints in the collection. The deserialized actors will not be jointed anymore.
                        • +
                        • Joint drive force limits are actual force limits rather than impulse limits. Set the flag PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES to false to support legacy behavior
                        • +
                        • Angular drive constraints for revolute joints were wrongly computed, resulting in a negative angular velocity when a positive drive target was set.
                        • +
                        • Scene addActors will now correctly remove all actors that were passed to add, if some insert failed.
                        • +
                        • Contact modification now enforces max impulse field correctly. Previously, it only enforced it if max impulse was set to 0.
                        • +
                        • Contact modification now supports target velocity in all directions. Previously, it only enforced the target velocity components that were perpendicular to the contact normal.
                        • +
                        • Jittering of small spheres & capsules on very large triangles has been fixed.
                        • +
                        • Setting sleep threshold to 0 now guarantees that bodies won't fall asleep even if their kinetic energy reaches exactly 0.
                        • +
                        +
                      • Deprecated:
                      • +
                          +
                        • PxShape::release() now decrements the reference count on a shape, and its use is deprecated for detaching a shape from its actor - use detachShape() instead.
                        • +
                        • PxJoint::getType() is deprecated - use getConcreteType() instead.
                        • +
                        • PxConstraintFlag::eREPORTING is deprecated - constraints always generate force reports
                        • +
                        • PxConstraintDominance is deprecated - use PxDominanceGroupPair instead.
                        • +
                        +
                      + +

                      Scene queries

                      +
                        +
                      • Added:
                      • +
                          +
                        • PxVolumeCache, a volumetric cache for local collision geometry.
                        • +
                        • Parameter inflation was added to some PxGeometryQuery functions.
                        • +
                        • Flag eMESH_BOTH_SIDES can be now used to control triangle mesh culling for raycasts and sweeps.
                        • +
                        • Added PxBatchQueryMemory as a part of PxBatchQueryDesc, to allow memory buffers to be set before execution.
                        • +
                        • PxBatchQueryDesc() constructor now requires 3 parameters at initialization - see migration guide for more details.
                        • +
                        • PxBatchQuery::raycast, sweep, overlap calls will now issue a warning and discard the query when over maximum allowed queries.
                        • +
                        • There is a new flag to allow manually updating the scene query representation, see: PxSceneFlags::eENABLE_MANUAL_SQ_UPDATE and PxScene::flushQueryChanges().
                        • +
                        • Added PxScene::forceDynamicTreeRebuild() function to immediately rebuild the scene query structures.
                        • +
                        • Added bool PxSweepHit::hadInitialOverlap() returning true if a sweep hit occurred early due to initial overlap at distance=0.
                        • +
                        +
                      • Removed:
                      • +
                          +
                        • PxBatchQuery::linearCompoundGeometrySweepSingle and PxBatchQuery::linearCompoundGeometrySweepMultiple functions are no longer supported.
                        • +
                        • Globals PxPS3ConfigParam::eSPU_OVERLAP, eSPU_RAYCAST, eSPU_SWEEP that were previous set via setSceneParamInt call are replaced with PxBatchQueryDesc::runOnSpu. See migration guide for more details.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • Scene Query raycastAny/Single/Multiple APIs were merged into a single raycast() call (same for overlaps and sweeps). Please refer to user and migration guides for details.
                        • +
                        • Scene Query raycast() API now uses a PxRaycastBuffer or a PxRaycastCallback parameter for reporting hits. Blocking hits are now reported separately from toching and PxRaycastCallback class supports reporting an unbounded number of results (same for overlaps and sweeps).
                        • +
                        • A const templated PxRaycastBufferN object was added to allow convenient creation of fixed size scene query touch buffers. Same for overlaps and sweeps.
                        • +
                        • Support for compound sweeps was moved out from core SDK to extensions.
                        • +
                        • Support for compound sweeps was moved from PxScene to extensions (see PxRigidBodyExt::linearSweepSingle, PxRigidBodyExt::linearSweepMultiple).
                        • +
                        • PxQueryFilterCallback::preFilter now passes an actor pointer as well as a shape pointer.
                        • +
                        • PxSceneQueryFlag::eINITIAL_OVERLAP and PxSceneQueryFlag::eINITIAL_OVERLAP_KEEP have been replaced with PxHitFlag::eINITIAL_OVERLAP_DISABLE and PxLocationHit::hadInitialOverlap(). Note that checking for initial overlap is now the defaut for sweeps.
                        • +
                        • Sweeps in 3.3 execute using a new faster code path, in some cases with reduced precision. If you encounter precision issues not previously experienced in earlier versions of PhysX, use ePRECISE_SWEEP flag to enable the backwards compatible more accurate sweep code.
                        • +
                        • The default behavior for overlap queries with query filters returning eBLOCK has changed to only return one of eBLOCK hits. Please refer to the migration guide for details.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Scene Query performance was significantly improved in a variety of scenarios.
                        • +
                        • Fixed a bug in capsule/mesh overlap code that occasionally caused unreported and misreported faces.
                        • +
                        • Fixed a crash in raycastMultiple when a convex was hit by the ray and eMESH_MULTIPLE flag was specified as a query flag.
                        • +
                        • Fixed a rare crash in heightfield raycast code.
                        • +
                        • Internal limit of 65536 results has been removed.
                        • +
                        • Accuracy in sphere/capsule-vs-triangle overlap and sweep tests has been improved.
                        • +
                        +
                      + +

                      Cooking

                      +
                        +
                      • Added:
                      • +
                          +
                        • Added support for convex hull creation with limited output vertices count.
                        • +
                        • Added support for convex hull creation directly from polygons rather than triangles.
                        • +
                        • Added support function computeHullPolygons in cooking, that creates hull polygons from given vertices and triangles. The resulting polygons can be used to create the convex hull directly.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • Changed convex hull volume integrals.
                        • +
                        • PxCookingParams constructor requires now PxTolerancesScale as an additional parameter. This enables us to perform further checks on the triangles during cooking. A warning will be emitted to the error stream if too huge triangles were found. This will ensure better simulation stability.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Optimized heightfield load code for no-endian conversion case.
                        • +
                        +
                      + +

                      Triangle meshes

                      +
                        +
                      • Added:
                      • +
                          +
                        • Added PxTriangleMeshFlag::eHAS_ADJACENCY_INFO flag for adjacency information checks.
                        • +
                        +
                      • Removed:
                      • +
                          +
                        • Removed has16BitTriangleIndices(), replaced by triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICES.
                        • +
                        +
                      + +

                      Particles

                      +
                        +
                      • Added:
                      • +
                          +
                        • Direct read access to CUDA particle data has been added for graphics interoperability, see PxParticleBase::lockParticleReadData(PxDataAccessFlags flags) and PxParticleFluid::lockParticleFluidReadData(PxDataAccessFlags flags)
                        • +
                        • Added PxRegisterParticles to reduce binary size by stipping unused code on platforms where static linking is used.
                        • +
                        • Added setExplicitCudaFlushCountHint to allow early flushing of the cuda push buffer.
                        • +
                        • Added caching of triangle meshes. setTriangleMeshCacheSizeHint is supported on Kepler and above GPUs.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Creating and immediately setting the position of particles failed and possibly crashed with GPU acceleration enabled. This would only happen after one or more simulation updates.
                        • +
                        • Creating many spread out particles might have crashed the GPU pipeline.
                        • +
                        +
                      + +

                      Broad Phase

                      +
                        +
                      • Added:
                      • +
                          +
                        • The SDK now supports multiple broad-phase algorithms (SAP & MBP). See Release Highlights, PxBroadPhaseType and PxBroadPhaseDesc.
                        • +
                        • PxVisualizationParameter::eMBP_REGIONS has been added to visualize MBP regions
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • The sap broadphase now gracefully handles PxShape instances whose axis-aligned bounding boxes have min/max limits with value +/- PX_MAX_F32 or QNAN or INF. Such bounds values can occur if a PxShape is given a global transform that is either illegal or close to the upper limit of the floating point range. Prior to this release, the sap broadphase could crash when the axis-aligned bounds of shapes had values that weren't within the floating point range. This has now been fixed. The overlap pairs reported by the broadphase for bounds with such values is undefined.
                        • +
                        +
                      + +

                      Vehicles

                      +
                        +
                      • Added:
                      • +
                          +
                        • Vehicles now support serialization. A PxSerializationRegistry instance may be passed into PxInitVehicleSdk and PxCloseVehicleSdk in order to enable support.
                        • +
                        • Vehicle telemetry graphs can now be queried per channel for the most recent value with the function PxVehicleGraph::getLatestValue.
                        • +
                        • New vehicle PxVehicleDriveNW type has been introduced. This class makes use of a new differential type PxVehicleDifferentialNW, which allows specified wheels to be equally coupled to the differential, and allows all for some or all of the N wheels to be driven.
                        • +
                        • Support for camber angles has been added to the PxVehicleSuspensionData class.
                        • +
                        • Moment of inertia parameter has been added to the PxVehicleEngineData class. Prior to this a value of 1.0 was assumed internally. A default value of 1.0 has been used in the constructor for backwards compatability.
                        • +
                        • Vehicle manual contains new section describing the conversion of default vehicle parameter values from SI units to any system of units with particular reference to the use of centimetres instead of metres .
                        • +
                        • The SI units of each parameter in PxVehicleComponents.h has been documented.
                        • +
                        • Vehicle manual contains updated troubleshooting section.
                        • +
                        • The requirements for disabled wheels (PxVehicleWheelsSimData::disableWheel) have been documented to make it clear that disabled wheels must be no longer associated with a PxShape, must have zero rotation speed, and must be decoupled from the differential. This is also now discussed in the guide.
                        • +
                        • Wheel raycasts documentation has been improved to clarify the start and end points of each raycast.
                        • +
                        • Suspension line raycasts do not need to be performed for each vehicle prior to update with PxVehicleUpdates. This feature is implemented with a boolean array passed as an extra function argument to PxVehicleSuspensionRaycasts. This feature is useful for vehicles that require only low level of detail.
                        • +
                        • The clutch model now supports two accuracy modes (PxVehicleClutchAccuracyMode::eESTIMATE and PxVehicleClutchAccuracyMode::eBEST_POSSIBLE). If the estimate mode is chosen the computational cost and accuracy of the clutch can be tuned with PxVehicleClutchData::mEstimateIterations.
                        • +
                        • PxVehicleSuspensionData now has a function setMassAndPreserveNaturalFrequency. This modifies the mass and stiffness of the spring in a way that preserves the spring natural frequency.
                        • +
                        • A new helper function PxVehicleCopyDynamicsData has been added that allows dynamics data such as engine rotation speed, wheel rotation speed, gear etc to be copied from one vehicle to another of the same type. This is particularly useful if a vehicle has a number of different versions where each represents a different level of detail.
                        • +
                        • A new function PxVehicleWheelsSimData::copy has been added to allow per wheel dynamics data to be copied from one vehicle to another.
                        • +
                        • The vehicle manual contains a new section "Level of Detail" describing the available options for vehicles that require only a low level of detail.
                        • +
                        • PxVehicleTireLoadFilterData now requires that mMinNormalisedLoad is greater than or equal to zero.
                        • +
                        • PxVehicleTireLoadFilterData now has a new member variable mMinFilteredNormalisedLoad. This value describes the filtered normalised load that occurs when the normalised is less than or equal to mMinNormalisedLoad.
                        • +
                        • PxVehicleWheelsSimData now has a new function setMinLongSlipDenominator. This can be used to tune stability issues that can arise when the vehicle slows down in the absence of brake and drive torques.
                        • +
                        • A new section "PxVehicleAutoBoxData" has been added to the vehicle tuning guide to describe operation of the automatic gearbox.
                        • +
                        • A new section "The Vehicle Under-steers Then Over-steers" has been added to the vehicle troubleshooting guide to describe steps to avoid twitchy handling on bumpy surfaces.
                        • + A new section "The Vehicle Never Goes Beyond First Gear" has been added to the vehicle troubleshooting guide to describe a common scenario that occurs when the automatic gearbox is given a latency time that is shorter than the time taken to complete a gear change. +
                        • A new section "The Vehicle Slows Down Unnaturally" has been added to the vehicle troubleshooting guide to describe the steps that can be taken to help the vehicle slow down more smoothly.
                        • +
                        • A number of vehicle snippets have been added.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • Minor api change for consistency: PxVehicleDrive4WWheelOrder has been introduced to replace the enum beginning with PxVehicleDrive4W::eFRONT_LEFT_WHEEL.
                        • +
                        • Minor api change for consistency: PxVehicleDrive4WControl has been introduced to replace the enum beginning with PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL.
                        • +
                        • Minor api change for consistency: PxVehicleDriveTankWheelOrder has been introduced to replace the enum beginning with PxVehicleDriveTankWheelOrder::eFRONT_LEFT.
                        • +
                        • Minor api change for consistency: PxVehicleDriveTankControl has been introduced to replace the enum beginning with PxVehicleDriveTank::eANALOG_INPUT_ACCEL.
                        • +
                        • Minor api change for consistency: PxVehicleDriveTankControlModel has been introduced to replace the enum beginning with PxVehicleDriveTank::eDRIVE_MODEL_STANDARD.
                        • +
                        • Minor api change for consistency: PxVehicleTypes has been introduced to replace the enum beginning with eVEHICLE_TYPE_DRIVE4W.
                        • +
                        • Minor api change for consistency: PxVehicleWheelGraphChannel has been introduced to replace the enum beginning with PxVehicleGraph::eCHANNEL_JOUNCE.
                        • +
                        • Minor api change for consistency: PxVehicleDriveGraphChannel has been introduced to replace the enum beginning with PxVehicleGraph::eCHANNEL_ENGINE_REVS.
                        • +
                        • Minor api change for consistency: PxVehicleGraphType has been introduced to replace the enum beginning with PxVehicleGraph::eGRAPH_TYPE_WHEEL.
                        • +
                        • PxVehicleUpdates now checks in checked and debug config that the wheel positioning of the driven wheels in a PxVehicleDrive4W obey the wheel ordering specified in PxVehicleDrive4WWheelOrder. Vehicles that are back-to-front or right-to-left are also allowed. A warning is issued if the check fails.
                        • +
                        • PxVehicleUpdates now checks in checked and debug config that the odd wheels of a PxVehicleDriveTank are either all to the left or all to the right of their even-wheeled complement, as specified in PxVehicleDriveTankWheelOrder. A warning is issued if the check fails.
                        • +
                        • To improve api consistency the arguments of the function PxVehicleDriveDynData::setAnalogInput(const PxReal analogVal, const PxU32 type) have been swapped so that it is now of the form PxVehicleDriveDynData::setAnalogInput(const PxU32 type, const PxReal analogVal).
                        • +
                        • Non-persistent wheel dynamics data (slip, friction, suspension force, hit data etc) has been moved out of the PxVehicleWheelsDynData class and is now recorded in a PxWheelQueryResult buffer that is passed as a function argument to the PxVehicleUpdates function.
                        • +
                        • PxVehicleWheels::isInAir() has been replaced with PxVehicleIsInAir(const PxVehicleWheelQueryResult& vehWheelQueryResults) to reflect the change to non-persistent data storage.
                        • +
                        • The origin of vehicles can now be shifted to stay in sync when the origin of the underlying PxScene is shifted. See PxVehicleShiftOrigin() for details.
                        • +
                        • PxVehicleWheels::setWheelShapeMapping and PxVehicleWheels::getWheelShapeMapping have been moved to PxVehicleWheelsSimData::setWheelShapeMapping and PxVehicleWheelsSimData::getWheelShapeMapping
                        • +
                        • PxVehicleWheels::setSceneQueryFilterData and PxVehicleWheels::getSceneQueryFilterData have been moved to PxVehicleWheelsSimData::setSceneQueryFilterData and PxVehicleWheelsSimData::getSceneQueryFilterData
                        • +
                        • PxVehicle4WEnable3WTadpoleMode and PxVehicle4WEnable3WDeltaMode now take an extra function argument: a non-const reference to a PxVehicleWheelsDynData.
                        • +
                        • The section "SI Units" in the vehicle guide has been updated to include the new functon PxVehicleWheelsSimData::setMinLongSlipDenominator.
                        • +
                        • PxVehicleTireData::mCamberStiffness has been replaced with PxVehicleTireData::mCamberStiffnessPerUnitGravity. PxVehicleTireData::mCamberStiffnessPerUnitGravity should be set so that it is equivalent to the old value of PxVehicleTireData::mCamberStiffness divided by the magnitude of gravitational acceleration.
                        • +
                        • PxVehicleComputeTireForceDefault has been removed from the public vehicle api. Custom tire shaders that call PxVehicleComputeTireForceDefault are best implemented by taking a copy of PxVehicleComputeTireForceDefault and calling the copy instead.
                        • +
                        +
                      • Fixed:
                      • +
                          +
                        • Sticky tire friction is now activated in the tire's lateral direction at the tire force application point when the velocity at the base of the tire has low longitudinal and low lateral components. Longitudinal sticky tire friction is unaffected and is still activated when the vehicle has low forward speed. This fixes a minor bug where vehicles positioned on a slope perpendicular to the slope's downwards direction can slowly drift down the slope.
                        • +
                        • Bugs in the suspension force and tire load computation have been fixed that affected handling when the car was upside down.
                        • +
                        • The tire load passed to the tire force computation is now clamped so that it never falls below zero.
                        • +
                        • A bug in the tank damping forces has now been fixed. Tanks now slow down more aggressively from engine and wheel damping forces.
                        • +
                        +
                      + + +
                      + + + +

                      Release Notes - NVIDIA® PhysX® SDK 3.2.4

                      + +

                      April 2013

                      + +

                      What's New In NVIDIA PhysX 3.2.4

                      + +
                      +

                      General

                      +
                        +
                      • Note: PS3 platform specific changes can be found in the PS3 readme file.
                      • +
                      • Fixed a bug which caused actors to return wrong world bounds if the bounds minimum was above 10000 on any axis.
                      • +
                      • Reporting allocation names can now be enabled or disabled (see PxFoundation::setReportAllocationNames). When enabled, some platforms allocate memory through 'malloc'.
                      • +
                      • eEXCEPTION_ON_STARTUP is removed from PxErrorCode and it is no longer needed.
                      • +
                      • Added boilerplate.txt to the Tools folder. SpuShaderDump.exe and clang.exe require it.
                      • +
                      • PxWindowsDelayLoadHook.h has been moved from Include/foundation/windows to Include/common/windows.
                      • +
                      • PxScene::saveToDesc now reports the bounceThresholdVelocity value.
                      • +
                      • Fixed a bug in PxDefaultSimulationFilterShader: the value of the second filter constant in the collision filtering equation was ignored and instead the value of the first filter constant was used.
                      • +
                      • Fixed a crash bug in PCM collision.
                      • +
                      + + +

                      Rigid Bodies

                      +
                        +
                      • Forces applied to bodies (with PxRigidBody::addForce) that go to sleep in the subsequent update now have their applied forces cleared when the body is set to sleep to avoid them being applied in a later update when the body is once more awake. This bug broke the rule that forces applied with PxRigidBody::addForce do not persist beyond the next scene update.
                      • +
                      • Jittering of small spheres & capsules on very large triangles has been fixed.
                      • +
                      + +

                      Cooking

                      +
                        +
                      • PxCookingParams constructor is now marked as deprecated. PxToleranceScale is needed for PxCookingParams in order to perform additional triangle check during cooking. This triangle check will trigger warning if too huge triangles are used. This check will ensure better simulation stability.
                      • +
                      + +

                      Scene Queries

                      +
                        +
                      • Added PxScene::forceDynamicTreeRebuild() function to immediately rebuild the scene query structures.
                      • +
                      • Accuracy in sphere/capsule-vs-triangle overlap and sweep tests has been improved.
                      • +
                      + +

                      Broad Phase

                      +
                        +
                      • Fixed assert in debug mode that wrongly asserted when an overlap pair was removed with perfect equality between the min of one bound and the max of the other along at least one axis.
                      • +
                      + +

                      Character controller

                      +
                        +
                      • PxControllerFilterCallback has been added, to make CCT-vs-CCT filtering more flexible.
                      • +
                      • Fixed a bug where PxControllerBehaviorCallback was not called when a PxUserControllerHitReport was not defined.
                      • +
                      • PxObstacle notifications has been added to handle touched obstacles.
                      • +
                      • Touched PxObstacle has been replaced by touched ObstacleHandle.
                      • +
                      • PxObstacleContext::getObstacleByHandle has been added.
                      • +
                      • Touched actors scene is checked before ride on shape, to ensure valid touched shape.
                      • +
                      • Touched shape scene query flag is checked before ride on shape, to ensure valid touched shape.
                      • +
                      • Touched shape user CCT filtering is triggered before ride on shape, to ensure valid touched shape.
                      • +
                      • Box & capsule controllers' resize function now properly take the up direction into account
                      • +
                      + +

                      Vehicles

                      +
                        +
                      • Documented potential problem with PxVehicleWheelsDynData::getTireDrivableSurfaceType() and PxVehicleWheelsDynData::getTireDrivableSurfaceShape() whereby the pointer returned may reference a PxShape or PxMaterial that has been released in-between storing the pointer in PxVehicleUpdates and any subsequent query.
                      • +
                      • PxVehicleWheelsSimData::disableWheel has been documented in more detail. PxVehicleWheelsSimData::enableWheel has been added.
                      • +
                      • Fixed a bug where the denominator of the longitudinal slip calculation didn't take into account the value of PxTolerancesScale::length. This will only have an impact if PxTolerancesScale::length != 1.0f.
                      • +
                      • Fixed a bug where the engine torque would be incorrectly applied if PxTolerancesScale::length != 1.0f. This will only have an impact if PxTolerancesScale::length != 1.0f.
                      • +
                      + +

                      Particles

                      +
                        +
                      • Creating and immediately setting the position of particles failed and possibly crashed with GPU acceleration enabled. This would only happen after one or more simulation updates.
                      • +
                      + +
                      + +


                      Supported Platforms

                      + +
                      + Unchanged from from 3.2.3 except: +

                      Development

                      +
                        +
                      • Upgraded to Xcode 4.6
                      • +
                      +
                      + +


                      Known Issues And Limitations

                      + +
                      + Unchanged from from 3.2.3. +
                      +
                      + + + +
                      + + + +

                      Release Notes - NVIDIA® PhysX® SDK 3.2.3

                      + +

                      November 2012

                      + +

                      What's New In NVIDIA PhysX 3.2.3

                      + +
                      +

                      General

                      +
                        +
                      • Note: PS3 platform specific changes can be found in the PS3 readme file.
                      • +
                      • Quaternions passed through the API are now considered valid if their magnitude is between 0.99 and 1.01.
                      • +
                      • Fixed crash when running out of memory on creation of a triangle mesh.
                      • +
                      • For applications using floating point exceptions, the SDK will now mask or avoid exceptions arising from invalid floating point operations (inexact and underflow exceptions may still be generated).
                      • +
                      • Fixed a bug with recursive use of the PxScene read/write lock.
                      • +
                      • Fixed a shutdown bug with very short lived threads on Linux platforms.
                      • +
                      • PhysX version number in error messages now printed in hex for easier reading.
                      • +
                      • Fixed memory barrier and prefetch implementation for all posix based platforms (android, ios, osx, linux).
                      • +
                      + +

                      Broad Phase

                      +
                        +
                      • Fixed a broad phase crash bug that occurred when deleting shapes with bounds very far from the origin.
                      • +
                      + + +

                      Collision Detection

                      +
                        +
                      • Documentation of limits of PxShape counts has been added for affected platforms.
                      • +
                      • Made kinematics interact better with CCD.
                      • +
                      • Adding support for disabled contact response in CCD by respecting the dominance setting. In this case, CCD will emit events but will not alter the motion of the bodies.
                      • +
                      • Fixed potential crash in eENABLE_PCM codepath.
                      • +
                      + + +

                      Rigid Bodies

                      +
                        +
                      • Fixed bug in force based contact reports. An assert could occur when PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS was set and PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND was not set.
                      • +
                      • Twist Limit range is documented for revolute and D6 joints, and validated.
                      • +
                      • Reduced the number of SDK allocations when using CCD.
                      • +
                      + + +

                      Scene Queries

                      +
                        +
                      • Raycasts against heighfields now return correct actual mesh index, which can be used for getTriangle().
                      • +
                      • Fixed bug that caused scene queries to miss hits for static rigid bodies that got moved around (after having been added to the scene).
                      • +
                      • Fixed rebuilding the dynamic structure properly when used for static rigid bodies.
                      • +
                      • Fixed a rare crash in heightfield raycast code.
                      • +
                      + +

                      Character controller

                      +
                        +
                      • A new runtime tessellation feature has been added, that can help reducing FPU accuracy issues in the sweep tests.
                      • +
                      + +

                      Convex hulls

                      +
                        +
                      • Zero-sized convex hull data double delete detection fix.
                      • +
                      + +

                      Vehicles

                      +
                        +
                      • Vehicles with rigid body actors that are asleep and also have no acceleration or steer inputs are treated as though they are asleep too; that is, they are bypassed completely in the PxVehicleUpdates function. Vehicles with sleeping rigid body actors but with non-zero acceleration or steer inputs are processed as normal in PxVehicleUpdates and will automatically have their rigid body actor woken up.
                      • +
                      • New function PxVehicleSetUpdateMode to allow PxVehicleUpdates to select between applying accelerations to vehicle rigid bodies or immediate updating of their velocity.
                      • +
                      + +

                      Particles

                      +
                        +
                      • Fixed a non-deterministic crash appearing with rigid bodies using CCD and gpu particles in the same scene.
                      • +
                      + +

                      Physx Visual Debugger

                      +
                        +
                      • Material release events are now correctly sent to PVD.
                      • +
                      + +

                      RepX

                      +
                        +
                      • Add more RepX class information in PhysXAPI document.
                      • +
                      + +
                      + +


                      Supported Platforms

                      + +
                      +

                      Runtime

                      +
                        +
                      • Apple iOS
                      • +
                      • Apple Mac OS X
                      • +
                      • Google Android (version 2.2 or later for SDK, 2.3 or later required for samples)
                      • +
                      • Linux (tested on Ubuntu)
                      • +
                      • Microsoft Windows XP or later (NVIDIA Driver ver 295.73 or later is required for GPU acceleration)
                      • +
                      • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
                      • +
                      • Microsoft XBox 360
                      • +
                      • Sony Playstation 3
                      • +
                      +

                      Development

                      +
                        +
                      • Microsoft Windows XP or later
                      • +
                      • Microsoft Visual Studio 2008, 2010
                      • +
                      • Xcode 4.5
                      • +
                      +
                      + +


                      Known Issues And Limitations

                      + +
                      + Unchanged from from 3.2.2. +
                      +
                      + + + +
                      + + + +

                      Release Notes - NVIDIA® PhysX® SDK 3.2.2

                      + +

                      October 2012

                      + +

                      What's New In NVIDIA PhysX 3.2.2

                      + +
                      +

                      General

                      +
                        +
                      • Note: PS3 platform specific changes can be found in the PS3 readme file.
                      • + +
                      • Added Microsoft Windows RT (formerly known as Windows on ARM) support.
                      • +
                      • Suspended Sony Playstation Vita support.
                      • +
                      • PxScene now exposes methods to make multithreaded sharing of scenes easier, see PxSceneFlags::eREQUIRE_RW_LOCK for details.
                      • +
                      • Enabled Win64 DEBUG build to use SIMD enabled code path.
                      • +
                      • Fixed bug in quaternion to axis/angle routine which failed on negative w values.
                      • +
                      • Fixed crash when using ConvX on a PxCollection with external references.
                      • +
                      • Fixed a spurious overlapping read/write error report when using simulation call backs in checked builds.
                      • +
                      • The bounce threshold velocity can be set at run-time with PxScene::setBounceThresholdVelocity. Likewise, it can be queried with PxScene::getBounceThresholdVelocity
                      • +
                      • Fixed return value of Ps::atomicExchange for POSIX based platforms: Linux, Android, IOS and OSX
                      • +
                      • PxGeometryQuery::computePenetration() has been added, to compute the Minimum Translational Distance between geometry objects.
                      • +
                      + +

                      Broad Phase

                      +
                        +
                      • Fixed a broad phase crash bug.
                      • +
                      + +

                      Collision Detection

                      +
                        +
                      • Collision detection now more robust when confronted with ill-conditioned scenarios.
                      • +
                      • Fixed crash when SDK is unable to create more contact pairs. Now a warning is emitted and the contacts are ignored.
                      • +
                      + +

                      Rigid Bodies

                      +
                        +
                      • Improved the numerical stability of articulations.
                      • +
                      • The target pose of a kinematically controlled dynamic actor can now get extracted through PxRigidDynamic::getKinematicTarget().
                      • +
                      • The new flag PxRigidDynamicFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES makes scene queries use the target pose of a kinematically controlled dynamic actor instead of the actual pose.
                      • +
                      • Fixed PxConstraintFlag::eVISUALIZATION having no effect.
                      • +
                      • Fixed bug that caused sleep/wake-up notification events to get lost.
                      • +
                      • Fixed a bug where sleeping objects were not waking up properly.
                      • +
                      • Fixed potential assert when switching a rigid body between kinematic and dynamic with contact reports enabled.
                      • +
                      • Fixed a bug where CCD didn't consider the scaling transform for triangle meshes.
                      • +
                      • Fixed a potential crash bug when PxConstraintFlag::ePROJECTION gets raised after a joint (that connects to other projecting joints) has been created.
                      • +
                      • Fixed a bug that resulted in joint breakage being reported every frame for any broken joint
                      • +
                      • Added PxArticulationDriveCache for applying an impulse to an entire articulation
                      • +
                      • fixed various crash bugs when sleeping articulations were removed from the scene and re-added +
                      • +
                      + +

                      GPU Physics

                      +
                        +
                      • Fixed a possible out of bounds array access in GPU particle collision code.
                      • +
                      • Updated the GPU PhysX Visual Indicator to allow APEX to hook into it.
                      • +
                      • Fixed sample particles crash when there is a cuda kernel error.
                      • +
                      • Upgraded GPU tech to CUDA 4.2.
                      • +
                      + +

                      Scene Queries

                      +
                        +
                      • PxSceneQueryHit::faceIndex is now filled in for sweeps against convex meshes.
                      • +
                      • Added support for sphere and capsule shaped heightfield overlap queries.
                      • +
                      • Added an inflation parameter to all sweep queries in PxGeometryQuery, PxBatchQuery, and PxScene. This can be used to maintain a minimum distance between objects when moving them using sweeps.
                      • +
                      • Made sure that raycast multiple will include the closest triangle in the results even if the output cannot hold all the triangles that are hit.
                      • +
                      • Fixed swept sphere against capsule not properly testing for initial overlap.
                      • +
                      • Fixed the normal vector returned from sweeps being sometimes negated.
                      • +
                      • Fixed a scenario where raycasting could miss an actor after the user has moved it using setGlobalPose().
                      • +
                      • Fixed swept convex against plane sometimes reporting false hits
                      • +
                      • Fixed swept/overlap/raycast with convexes that don't have identity scale rotation.
                      • +
                      • Fixed a culling bug for box-triangle mesh sweep.
                      • +
                      + +

                      Convex hulls

                      +
                        +
                      • Convex hull is rejected if it has less than 4 polygons.
                      • +
                      • Additional convex hull check has been added to detect open volumes.
                      • +
                      + +

                      Triangle meshes

                      +
                        +
                      • Added triangle mesh flags for 16bit indices and adjacency information.
                      • +
                      • Fixed adjacency information order for getTriangle with triangle meshes to respect the vertex order.
                      • +
                      + +

                      HeightFields

                      +
                        +
                      • Fixed bug where capsules would bounce as they roll across heightfield edges.
                      • +
                      • Fixed bug where spheres would bounce as they roll across heightfield vertices.
                      • +
                      • Added adjacency information for getTriangle with height fields.
                      • +
                      + +

                      Particles

                      +
                        +
                      • Fixed triangle mesh shapes with ePARTICLE_DRAIN colliding with particles.
                      • +
                      • Fixed crash with GPU particles when a lot of grid cells get occupied and vacated within one time step.
                      • +
                      + +

                      Character Controller

                      +
                        +
                      • The PxControllerBehaviorFlag::eCCT_USER_DEFINED_RIDE flag has been added.
                      • +
                      • Fixed character controller not walking up steps properly if they are not exactly 90 degrees vertical.
                      • +
                      • Fixed a bug where the character controller was not able to move up slopes when using a small step offset setting.
                      • +
                      • Fixed a bug where the character controller could rise off the ground when blocked by a step.
                      • +
                      • Fixed a bug where the character controller could rise off the ground when hitting another character controller.
                      • +
                      • Fixed a bug where releasing a shape of a touching actor and then releasing the character controller would crash. Releasing shapes of actors in touch may still lead to crashes in other situations. PxController::invalidateCache() can be used to work around these situations.
                      • +
                      +

                      CCD

                      +
                        +
                      • Fixed culling bug in CCD sweeps with meshes with transforms that caused contacts to be missed
                      • +
                      + +

                      Vehicles

                      +
                        +
                      • The vehicle sdk used to make the assumption that wheels 0 and 1 (the front wheels) of a PxVehicleDrive4W responded positively to the input steering wheel angle, while wheels 2 and 3 (the rear wheels) responded in the opposite direction to that of the input steering wheel angle. A consequence of this assumed behaviour was the restriction that PxVehicleWheelData::mMaxSteer was limited to positive values. This restriction has now been relaxed with the caveat that PxVehicleWheelData::mMaxSteer must be in range (-Pi/2, Pi/2). It is now possible to turn each wheel positively or negatively relative to the input steering wheel angle by choosing positive or negative values for PxVehicleWheelData::mMaxSteer. Ackermann steer correction might result in the front and rear wheels competing against each other if the rear and front all steer in the same direction relative to the input steering wheel angle. If this is the case it will be necessary to set the Ackermann accuracy to zero.
                      • +
                      • It is now possible to set the engine rotation speed (PxVehicleDriveDynData::setEngineRotationSpeed), the rotation speed of each wheel (PxVehicleWheelsDynData::setWheelRotationSpeed) and the rotation angle of each wheel (PxVehicleWheelsDynData::setWheelRotationAngle). The default values for each of these properties remains zero.
                      • +
                      • Wheel contact reporting has been improved with the addition of a number of query functions to the PxVehicleWheelsDynData class. These are getTireDrivableSurfaceContactPoint, getTireDrivableSurfaceContactNormal, getTireLongitudinalDir, getTireLateralDir, getSuspensionForce, getTireDrivableSurfaceShape.
                      • +
                      • It is now possible to store a userData pointer per wheel. This allows, for example, each wheel to be associated with a game object. The relevant functions are PxVehicleWheelsDynData::setUserData and PxVehicleWheelsDynData::getUserData.
                      • +
                      • The default behavior of PxVehicleWheels::setWheelShapeMapping has changed. Previously, default values were automatically assigned to each wheel at construction so that the ith wheel was mapped to the ith body shape. This, however, made the assumption that there was a wheel shape for each wheel, which is not always true. As a consequence, the default value is now -1, meaning any mapping between body shape and wheel has to be explictily made by calling setWheelShapeMapping.
                      • +
                      • It is now possible to query the mapping between wheel and body shape with PxVehicleWheels::getWheelShapeMapping.
                      • +
                      • It is now possible to query the tire shader data that has been applied to each wheel with PxVehicleWheelsDynData::getTireForceShaderData.
                      • +
                      • The helper function PxVehicleComputeSprungMasses has been added to aid the setup of the suspension sprung masses from the rigid body centre of mass and wheel center coordinates.
                      • +
                      • The scene query filter data applied to the suspension raycasts was previously taken from the filter data of the associated body shape. This makes the assumption of a body shape per wheel, which is not always true. As a consequence, the filter data must be explictly set per wheel by calling PxVehicleWheels::setSceneQueryFilterData. The filter data can be queried with PxVehicleWheels::getSceneQueryFilterData.
                      • +
                      • Sub-stepping of the vehicle update can now be applied per vehicle with PxVehicleWheelsSimData::setSubStepCount.
                      • +
                      • PxVehicleDrivableSurfaceToTireFrictionPairs has been modified so that the dictionary of material pointers can be updated without the necessity of further allocation. The create function has been replaced with separate allocate and setup functions.
                      • +
                      • A new vehicle type PxVehicleNoDrive has been added to provide a close approximation to backwards compatibility with the api of the 2.8.x NxWheelShape.
                      • +
                      + + +

                      Visual Remote Debugger

                      +
                        +
                      • Added PVD compatible profile zones for batched queries.
                      • +
                      • Added the ability to capture and inspect scene queries in PVD.
                      • +
                      • SDK will now flush the pvd connection stream immediately after cloth or cloth fabric is created or released.
                      • +
                      • Fixed the PVD support for articulations.
                      • +
                      • Fixed PVD rendering wrong constraint limits.
                      • +
                      + + +

                      Documentation

                      +
                        +
                      • Wrong statement in PxRigidStatic::release() has been corrected. Static rigid bodies do wake up touching dynamic rigid bodies on release.
                      • +
                      • Wrong statement in PxShape::setFlag() has been corrected. It is a valid operation to clear all flags.
                      • +
                      • Retroactively added more detail about changes to 3.2.1 release notes below.
                      • +
                      +
                      + +


                      Supported Platforms

                      + +
                      +

                      Runtime

                      +
                        +
                      • Apple iOS
                      • +
                      • Apple Mac OS X
                      • +
                      • Google Android (version 2.2 or later for SDK, 2.3 or later required for samples)
                      • +
                      • Linux (tested on Ubuntu)
                      • +
                      • Microsoft Windows XP or later (NVIDIA Driver ver 295.73 or later is required for GPU acceleration)
                      • +
                      • Microsoft Windows RT (formerly known as Windows on ARM) (SDK only, no samples yet)
                      • +
                      • Microsoft XBox 360
                      • +
                      • Sony Playstation 3
                      • +
                      +

                      Development

                      +
                        +
                      • Microsoft Windows XP or later
                      • +
                      • Microsoft Visual Studio 2008, 2010
                      • +
                      • Xcode 4.2
                      • +
                      +
                      + + + + +


                      Known Issues And Limitations

                      + +
                      +

                      General

                      +
                        +
                      • Memory leaks might get reported when using the debug versions of malloc and free together with the debug version of PhysX on Microsoft Windows platforms with Visual Studio 2010. This is caused by a bug in the Visual Studio 2010 runtime libraries. If such a leak appears immediately after releasing PhysX and all its components, please contact us for information about workarounds.
                      • +
                      • Use of articulations may require an increase of 64K in the stack size for threads making API calls and engine worker threads
                      • +
                      +
                      + +
                      + Please also see the previous lists from 3.2.1 and earlier. +
                      + + + +
                      + + + +

                      Release Notes - NVIDIA® PhysX® SDK 3.2.1

                      + +

                      June 2012

                      + +

                      What's New In NVIDIA PhysX 3.2.1

                      + +
                      +

                      General

                      +
                        +
                      • Note: PS3 platform specific changes can be found in the PS3 readme file.
                      • +
                      • Added GRB hooks for APEX 1.2.1.
                      • +
                      • Some incorrect usages of __restrict have been fixed.
                      • +
                      • A crash when the user's code had FPU exceptions enabled has been fixed.
                      • +
                      • A rare crash on Win64 has been fixed.
                      • +
                      • Removed no longer needed RapidXML library from distribution.
                      • +
                      • Binary serialization can now save the names of actors and shapes.
                      • +
                      • Removed a RepXUtility.h reinterpret_cast compile warning that happened on some platforms.
                      • +
                      • Fixed a spurious overlapping read/write error report when using simulation call backs in checked builds.
                      • +
                      • Fixed a bug in PxBinaryConverter when processing PxConvexMesh and PxMaterial assets.
                      • +
                      • Fixed bug in implementations of array accessors (PxPhysics::getScenes(), GuMeshFactory::getTriangleMeshes(), GuMeshFactory::getConvexMeshes(), GuMeshFactory::getHeightFields(), PxAggregate::getActors(), PxScene::getAggregates(), PxScene::getArticulations(), PxScene::getConstraints() ) when startIndex > bufferSize.
                      • +
                      • Reduced the number of libraries provided for game consoles. The following core libraries are included in the PhysX3 library and are not available separately anymore: LowLevel, LowLevelCloth, PhysX3Common, PhysXProfileSDK, PhysXVisualDebuggerSDK, PvdRuntime, PxTask, SceneQuery, SimulationController.
                      • +
                      + +

                      Documentation

                      +
                        +
                      • Clarified documentation regarding use of eSEND_SLEEP_NOTIFIES flag.
                      • +
                      • Clarified documentation regarding using different CRT libraries.
                      • +
                      • Removed some confusing statements about meta data from the documentation.
                      • +
                      • Updated PsPool, PsSort so they can use the user allocator.
                      • +
                      + +

                      Mesh Cooking

                      +
                        +
                      • A warning message about negative volumes in the convex mesh cooker could cause a crash.
                      • +
                      • Fixed failure to create valid convex hull in cooking when using PxConvexFlag::eINFLATE_CONVEX.
                      • +
                      • The convex mesh cooking has been made more robust and properly outputs some error messages that were previously missing.
                      • +
                      • Fixed crash bug in x64 version of ClothEdgeQuadifier.
                      • +
                      • Adjacency information option for TriangleMeshes. This is controlled using the PxCookingParams::buildTriangleAdjacencies flag, and returned if available by PxMeshQuery::getTriangle().
                      • +
                      + +

                      Broad Phase

                      +
                        +
                      • The sdk gracefully handles the case of more than 65536 broadphase pairs and reports a warning that some contacts will be dropped in the event that this limit is exceeded. This affects all platforms except win32/win64/linux/linux64, which support a maximum number of 4294967296 pairs.
                      • +
                      + +

                      Collision Detection

                      +
                        +
                      • Fixed a memory corruption bug in heightfield code.
                      • +
                      • Fixed a bug in sphere vs mesh contact generation that could result in bad normals.
                      • +
                      • Added a flag to enable or disable contact caching, to permit users to make a performance vs memory tradeoff.
                      • +
                      • Fixed a crash bug in ContactReport cleanup code.
                      • +
                      + +

                      Rigid Bodies

                      +
                        +
                      • The simultaneous raising of both the PxShapeFlag::eSIMULATION_SHAPE and PxShapeFlag::eTRIGGER_SHAPE flags is now explicitly forbidden by the sdk. If any of the two is raised then any attempt to raise the other is rejected by the sdk and an error is passed to the error stream.
                      • +
                      + +

                      Articulations

                      +
                        +
                      • The API stubbed in 3.2.0 for applying an impulse to an entire articulation is now implemented.
                      • +
                      + +

                      GPU Physics

                      +
                        +
                      • Much more specific error messages for CUDA compute capability related failures.
                      • +
                      • Added SM35 support for GK110 GPUs.
                      • +
                      • The CUDA contexts provided by the gpu dispatcher can now be shared across scenes.
                      • +
                      • Fixed a possible out of bounds array access in GPU particle collision code.
                      • +
                      + + +

                      Scene Queries

                      +
                        +
                      • Resolved poor GJK sweep convergence.
                      • +
                      • Batched queries accept sphere geometry for sweeps.
                      • +
                      • Optimized performance of raycasts.
                      • +
                      • An internal limit of 65536 objects in the scene-query subsytem has been lifted.
                      • +
                      • Fixed a bug where raycasts that sliced through two adjoining heightfields did not return correct results.
                      • +
                      • Fixed a crash bug in PxFindOverlapTriangleMeshUtil when doing a sphere overlap query against a heightfield.
                      • +
                      + + +

                      Cloth

                      +
                        +
                      • PxCloth::setMotionConstraints() now works with NULL parameter.
                      • +
                      + +

                      Character Controller

                      +
                        +
                      • PhysX CCT code no longer sets PxShape::userData.
                      • +
                      • Intersection of pairs of CCTs now uses the supplied filter data and the supplied callback prefilter. The callback postfilter is not yet hooked up.
                      • +
                      • A bug has been fixed whereby the filterData was ignored in one of the scene queries initiated by the PhysX CCT code.
                      • +
                      • Introduced a more automatic mechanism for invelidating the character controller's scene cache. As part of this, PxController::reportSceneChanged() was replaced with PxController::invalidateCache().
                      • +
                      • Added helper functions PxController::get/setFootPosition() to let user specify the bottom point of the character controller, rather than the center.
                      • +
                      • A new helper function, PxController::resize(), has been added to facilitate character controller resizing.
                      • +
                      • PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT is not the default behavior anymore.
                      • +
                      • The slope limit is only observed when walking on static convex meshes, static triangle meshes, static boxes and static heightfields. The slope limit is not observed when walking on dynamic or kinematic rigid bodies or static capsules or static spheres. This partially fixes a bug where the slope limit was inadvertently considered for shapes attached to dynamic rigid bodies and inadvertently ignored for boxes attached to static shapes.
                      • +
                      + +

                      Vehicles

                      +
                        +
                      • The vehicle sdk now reports an error to the error stream and exits from PxVehicleUpates if PxInitVehicleSdk has not been called in advance. This avoids a divide-by-zero error that can arise if the vehicle sdk has not been initialised correctly.
                      • +
                      + +

                      Visual Debugger

                      +
                        +
                      • Releasing of cloth fabrics is reported to the VRD.
                      • +
                      • Ext::Joint::setActors() call now reported to PVD.
                      • +
                      • Fixed crash bug when removing an aggregate containing a PxArticulation, while PVD is running.
                      • +
                      +
                      + +


                      Supported Platforms

                      + +
                      + Unchanged from from 3.2. +
                      + +


                      Known Issues And Limitations

                      + +
                      + Unchanged from from 3.2. +
                      + + + +
                      + + + +

                      Release Notes - NVIDIA® PhysX® SDK 3.2

                      + +

                      December 2011

                      + +

                      What's New In NVIDIA PhysX 3.2

                      + +
                      +

                      General

                      +
                        +
                      • Three new sample applications: SampleCharacterCloth (character with cloth cape and cloth flags), SampleBridges (character controller walking on dynamic bridges and moving platforms), SampleCustomGravity (character controller with arbitrary up vector).
                      • +
                      • On Windows, the PxFoundation instance is now a process wide singleton and part of the new PhysX3Common.dll library
                      • +
                      • PxCreatePhysics() does not create a PxFoundation instance any longer. The PxFoundation instance has to be created in advance through PxCreateFoundation().
                      • +
                      • Calls to PxCreatePhysics() are not valid anymore if a PxPhysics instance already exists.
                      • +
                      • If profiling information should be sent to the PhysX Visual Debugger, a PxProfileZoneManager instance has to be provided when creating the PxPhysics instance.
                      • +
                      • The version number constant PX_PUBLIC_FOUNDATION_VERSION has been replaced with PX_PHYSICS_VERSION. Both PxFoundation and PxPhysics use the same version number now.
                      • +
                      • The API now distinguishes between input and output stream types.
                      • +
                      • Added mechanism to reduce code size by not linking optional components. See PxCreateBasePhysics() and the PxRegister*() functions.
                      • +
                      • Added getConcreteTypeName() to API classes to provide run time type information.
                      • +
                      • Added PxScene::getTimestamp() to retrieve the simulation counter.
                      • +
                      • PxGetFoundation has been moved to PxGetFoundation.h
                      • +
                      • Changed the functions PxPhysics::releaseUserReferences(), releaseCollection(), addCollection() and releaseCollected() to now take a reference rather than a pointer.
                      • +
                      • The signature of PxCreatePhysics has changed: The Foundation SDK instance must be passed in explicitly. One can also hook profiling information by passing a PxProfileZoneManager.
                      • +
                      • Platform conversion for serialized data has been moved from the ConvX command line tool to the PxBinaryConverter interface in the cooking library
                      • +
                      • contact data block allocation now provides statistics on usage and max usage
                      • +
                      • On all platforms except PS3, contact data blocks can be progressively allocated
                      • +
                      • PxExtensionVisualDebugger has been renamed to PxVisualDebuggerExt, PxExtensionsConnectionType renamed to PxVisualDebuggerConnectionFlag
                      • +
                      • Default implementations of memory and file streams added in PxDefaultStreams.h
                      • +
                      • Renamed PxPhysics::getMetaData() to ::PxGetSDKMetaData().
                      • +
                      • Scene::simulate() now takes a memory block which is used for allocation of temporary data during simulation
                      • +
                      • On Windows, CudaContextManagerDesc support appGUID now. It only works on release build. If your application employs PhysX modules that use CUDA you need to use a GUID so that patches for new architectures can be released for your game.You can obtain a GUID for your application from Nvidia.
                      • +
                      + +

                      Rigid Bodies

                      +
                        +
                      • Introduced a new contact generation mode, see eENABLE_PCM. Note that this is an experimental feature that still shows simulation artifacts in some scenarios.
                      • +
                      • Introduced two new friction simulation modes, see eENABLE_ONE_DIRECTIONAL_FRICTION and eENABLE_TWO_DIRECTIONAL_FRICTION.
                      • +
                      • Introduced a new scene query flag PxSceneQueryFlag::eINITIAL_OVERLAP_KEEP to control how initial overhaps are treated in scene queries.
                      • +
                      • Per-triangle materials have been implemented.
                      • +
                      • Changes to material properties are automatically reflected in contact resolution.
                      • +
                      • New helper methods to compute mass properties for a dynamic rigid body taking per shape density/mass values into account (see documentation on PxRigidBodyExt for details).
                      • +
                      • A new set of methods for overlap, sweep and raycast tests based on PxGeometry objects has been introduced. See documentation on PxMeshQuery and PxGeometryQuery for details).
                      • +
                      • + The contact report API has changed (for details see the documentation on PxSimulationEventCallback::onContact()). Among the changes are: +
                          +
                        • Reports only get sent for shape pairs which request them. Previously, reports were sent for an actor pair even if the requested shape pair event was not triggered (for example because other shapes of the same actors started colliding etc.)
                        • +
                        • The following PxPairFlags have been removed eNOTIFY_CONTACT_FORCES, eNOTIFY_CONTACT_FORCE_PER_POINT, eNOTIFY_CONTACT_FEATURE_INDICES_PER_POINT. Forces and feature indices are now always provided if applicable.
                        • +
                        • It is much easier now to skip shape pairs or contact point information when traversing the contact report data.
                        • +
                        • The memory footprint of contact reports has been reduced.
                        • +
                        +
                      • +
                      • The members featureIndex0/1 of PxContactPoint have been renamed to internalFaceIndex0/1 for consistency.
                      • +
                      • For trigger reports, the eNOTIFY_TOUCH_PERSISTS event has been deprecated and will be removed in the next release. For performance and flexibility reasons it is recommended to use eNOTIFY_TOUCH_FOUND and eNOTIFY_TOUCH_LOST only and manage the persistent state separately.
                      • +
                      • Added PxConstraintVisualizer interface and code to visualize joint frames and limits.
                      • +
                      • Improved PxBatchQuery API.
                      • +
                      • PxPhysics::getProfileZoneManager() now returns a pointer rather than a reference.
                      • +
                      • PxRigidDynamic::moveKinematic() has been renamed to setKinematicTarget() to underline its precise semantics.
                      • +
                      • Added new function PxShape::getGeometry and class PxGeometryHolder to improve Geometry APIs.
                      • +
                      • PxCreatePlane now takes a PxPlane equation as a parameter. Note that the interpretation of the distance value is negated relative to 3.1
                      • +
                      • Added new actor creation helpers PxCloneStatic, PxCloneDynamic, PxScaleActor.
                      • +
                      • Added new functions PxTransformFromSegment, PxTransformFromPlaneEquation to simplify creation of planes and capsules.
                      • +
                      • added PxJoint::getConstraint() to access the underlying constraint object, from which the constraint force can be read
                      • +
                      • + Some methods of PxAggregate have been renamed for consistency or replaced for extended functionality. +
                          +
                        • getMaxSize() is now called getMaxNbActors().
                        • +
                        • getCurrentSize() is now called getNbActors().
                        • +
                        • getActor() has been replaced by getActors() which copies the actor pointers to a user buffer.
                        • +
                        +
                      • +
                      • Added support for kinematic triangle meshes, planes and heighfields.
                      • +
                      + +

                      Scene queries

                      +
                        +
                      • Dynamic AABBTree has been set as the default dynamic pruning structure.
                      • +
                      + +

                      Particles

                      +
                        +
                      • Removed descriptors from particle API: The properties maxParticles and PxParticleBaseFlag::ePER_PARTICLE_REST_OFFSET need to be specified when calling PxPhysics::createParticleSystem() and createParticleFluid(). All other properties can be adjusted after creation through set methods.
                      • +
                      + +

                      Cloth

                      +
                        +
                      • Added convex collision shapes, see PxCloth::addCollisionConvex()
                      • +
                      • Added friction support, see PxCloth::setFrictionCoefficient()
                      • +
                      • Added angle based bending constraints, see PxClothPhaseSolverConfig::SolverType::eBENDING
                      • +
                      • Added separation constraints, a spherical volume that particles should stay outside of, see PxCloth::setSeparationConstraints()
                      • +
                      • Added drag, see PxCloth::setDragCoefficient()
                      • +
                      • Added inertia scaling, controls how much movement due to PxCloth::setTargetPose() will affect the cloth
                      • +
                      • Added support for setting particle previous positions, see PxCloth::setParticles()
                      • +
                      • Added controls for scaling particle mass during collision, this can help reduce edge stretching around joints on characters, see PxCloth::setCollisionMassScale()
                      • +
                      • Particle data is now copied asynchronously from the GPU after simulate (rather than on demand)
                      • +
                      • Improved fabric layout, you can now share fabric data across multiple phases to reduce memory usage, see PxClothFabric
                      • +
                      • Fixed bug in collision when capsules are tapered at a slope > 1
                      • +
                      + +

                      Vehicles

                      +
                        +
                      • Added PxVehicleDriveTank, a vehicle class that enables tank behaviors.
                      • +
                      • Support for vehicles with more than 4 wheels, see PxVehicleDrive4W, PxVehicleDriveTank.
                      • +
                      • Significant refactor of vehicle api to allow further types of vehicle to be added.
                      • +
                      • Americal English spelling used in vehicle api.
                      • +
                      • PxVehicle4W replaced with PxVehicleDrive4W, PxVehicle4WSimulationData replaced with PxVehicleDriveSimData4W.
                      • +
                      • Removal of scene query helper functions and structs: PxVehicle4WSceneQueryData, PxVehicle4WSetUpSceneQuery, PxWheelRaycastPreFilter, PxSetupDrivableShapeQueryFilterData, PxSetupNonDrivableShapeQueryFilterData, PxSetupVehicleShapeQueryFilterData. See SampleVehicle_SceneQuery.h for their implementation in SampleVehicle.
                      • +
                      • PxVehicle4WSimpleSetup and PxCreateVehicle4WSimulationData have been removed and replaced with default values in vehicle components, see PxVehicleComponents.h.
                      • +
                      • PxVehicle4WTelemetryData has been replaced with PxVehicleTelemetryData, a class that supports vehicles with any number of wheels, see PxVehicleTelemetryData
                      • +
                      • PxVehicleDrivableSurfaceType no longer stored in PxMaterial::userData. A hash table of PxMaterial pointers is instead used to associate each PxMaterial with a PxVehicleDrivableSurfaceType, see PxVehicleDrivableSurfaceToTireFrictionPairs.
                      • +
                      • PxVehicleTyreData::mLongitudinalStiffness has been replaced with PxVehicleTireData::mLongitudinalStiffnessPerUnitGravity, see PxVehicleTireData.
                      • +
                      • Tire forces now computed from a shader to allow user-specified tire force functions, see PxVehicleTireForceCalculator.
                      • +
                      • Added helper functions to quickly configure 3-wheeled cars, see PxVehicle4WEnable3WTadpoleMode, PxVehicle4WEnable3WDeltaMode.
                      • +
                      + +

                      Serialization

                      +
                        +
                      • Changed the functions PxPhysics::releaseUserReferences(), releaseCollection(), addCollection() and releaseCollected() to now take a reference rather than a pointer.
                      • +
                      • Platform conversion for serialized data has been moved from the ConvX command line tool to the PxBinaryConverter interface in the cooking library.
                      • +
                      • Changed some functions in RepXUtility.h and RepX.h to take a reference rather than a pointer.
                      • +
                      + + +

                      What we removed:

                      +
                        +
                      • Deformables have been removed. Use the optimized solution for clothing simulation instead (see documentation on PxCloth for details).
                      • +
                      • PxSweepCache was replaced with PxVolumeCache.
                      • +
                      • PVD is no longer enabled in the release build.
                      • +
                      • Removed anisotropic friction.
                      • +
                      • Removed the CCD mode eSWEPT_CONTACT_PAIRS.
                      • +
                      • PxActorDesc has been removed.
                      • +
                      • The ConvX tool has been removed.
                      • +
                      • Removed empty default implementations of functions in PxSimulationEventCallback for consistency and because it can create bugs in user code if function prototypes change between releases. Users must now supply (eventually blank) implementations for all functions.
                      • +
                      • Octree and quadtree pruning structures have been removed.
                      • +
                      + +

                      Fixed Bugs

                      +
                        +
                      • PxScene::getActors() might not work properly when the startIndex parameter is used.
                      • +
                      • Improved the doc-comment of PxConvexMesh::getMassInformation().
                      • +
                      • RepX instantiation can lose all internal references when addOriginalIdsToObjectMap is false.
                      • +
                      • PxSetGroup crashed when used on a compound.
                      • +
                      • PhysXCommon.dll can be delay loaded.
                      • +
                      • ContactReportStream can now handle huge report numbers and size (resize-able flag) can be set in PxSceneDesc.h.
                      • +
                      • Fixed assert in sweep tests.
                      • +
                      • Concurrent read/write operations during a PxScene::fetchResults() call were not detected properly and no warning message got sent in checked builds. Forbidden write operations during callbacks triggered by PxScene::fetchResults() (contact/trigger reports etc.) were not covered either.
                      • +
                      • Fixed crash bug that occurred during collision detection when more than 16K of contact data was generated. Contacts that generate more than 16K of contact data are now fully supported.
                      • +
                      • Fixed crash bug when PhysX Visual Debugger is connected and an object gets modified and then released while the simulation is running.
                      • +
                      + +
                      + +


                      Supported Platforms

                      + +
                      +

                      Runtime

                      +
                        +
                      • Apple iOS
                      • +
                      • Apple Mac OS X
                      • +
                      • Google Android (version 2.2 or later for SDK, 2.3 or later required for samples)
                      • +
                      • Linux (tested on Ubuntu)
                      • +
                      • Microsoft Windows XP or later (NVIDIA Driver ver 295.73 or later is required for GPU acceleration)
                      • +
                      • Microsoft XBox 360
                      • +
                      • Sony Playstation 3
                      • +
                      • Sony Playstation Vita
                      • +
                      +

                      Development

                      +
                        +
                      • Microsoft Windows XP or later
                      • +
                      • Microsoft Visual Studio 2008, 2010
                      • +
                      • Xcode 4.2
                      • +
                      +
                      + +


                      Known Issues And Limitations

                      + +
                      + +

                      Binary Serialization

                      +
                        +
                      • Meta data generation for PxParticleFluid and PxParticleSystem serializables currently fails.
                      • +
                      • For collections that contain jointed rigid bodies all corresponding joints need to be added as well, otherwise deserialization will fail. +
                      +

                      Rigid Body Simulation

                      +
                        +
                      • Capsules and spheres can struggle to come to rest on even perfectly flat surfaces. To ensure these objects come to rest, it is necessary to increase angular damping on rigid bodies made of these shapes. In addition, flagging the capsule/sphere's material with physx::PxMaterialFlag::eDISABLE_STRONG_FRICTION can help bring these shapes to rest.
                      • +
                      +

                      Character Cloth Sample

                      +
                        +
                      • An NVIDIA GPU with Compute Capability 2.0 or higher is required for GPU accelerated simulation in the SampleCharacterCloth sample, if no such device is present then simulation will be performed on the CPU.
                      • +
                      • Note that this is not a general SDK requirement, the clothing SDK supports cards with Compute Capability < 2.0 but with limitations on mesh size.
                      • +
                      +

                      Character Controller

                      +
                        +
                      • Releasing shapes of actors that are in touch with a character controller may lead to crashes. Releasing whole actors doesn't lead to the same problems. PxController::invalidateCache() can be used to work around these issues.
                      • +
                      + +
                      + +
                      + Please also see the previous lists from 3.1.1 and earlier. +
                      + + + +
                      + + + +

                      Release Notes - NVIDIA® PhysX® SDK 3.1.2

                      + +

                      December 2011

                      + +

                      What's New In NVIDIA PhysX 3.1.2

                      + +
                      +

                      General

                      +
                        +
                      • Fixed wrong write/read clash checks.
                      • +
                      • Removed some compiler warnings from public header files.
                      • +
                      • Fixed PxScene::getActors() returning wrong actors when a start index is specified.
                      • +
                      + + +

                      Rigid Bodies

                      +
                        +
                      • Fixed broken joint projection in connection with kinematics.
                      • +
                      • Fixed inaccurate normals returned from height field scene queries.
                      • +
                      • Fixed a crash when the geometry of a shape changes and then the actor gets removed from the scene while the simulation is running.
                      • +
                      • Fixed a crash when re-adding scene-query shape actors to scene.
                      • +
                      + + +

                      Particles

                      +
                        +
                      • Fixed crash bug in particle simulation code on GPU.
                      • +
                      + + +

                      Cloth

                      +
                        +
                      • Fixed a crash when GPU fabrics are shared between cloths.
                      • +
                      • Fixed a hang in cloth fiber cooker when handed non-manifold geometry.
                      • +
                      + +

                      Samples

                      +
                        +
                      • Fixed SampleVehicles doing an invalid write.
                      • +
                      • Fixed SampleVehicle jitter in profile build.
                      • +
                      + +
                      + +


                      Supported Platforms (available in separate packages)

                      + +
                      + Unchanged from from 3.1.1. +
                      + +


                      Known Issues And Limitations

                      + +
                      + Unchanged from from 3.1. +
                      + + + +
                      + + + +

                      Release Notes - NVIDIA® PhysX® SDK 3.1.1

                      + +

                      November 2011

                      + +

                      What's New In NVIDIA PhysX 3.1.1

                      + +
                      +

                      General

                      +
                        +
                      • Ported samples to Linux.
                      • +
                      • Fixed crash bug in ConvX.
                      • +
                      • Fixed crash bug in the allocator code of PXC_NP_MEM_BLOCK_EXTENSIBLE.
                      • +
                      • Fixed crash bug when connected to PVD on various platforms.
                      • +
                      • Fixed bogus asserts due to overly strict validation of quaternions.
                      • +
                      • Fixed one frame lag in PVD scene statistics.
                      • +
                      • Fixed a number of OSX PVD sockets issues.
                      • +
                      • Fixed SampleSubmarine code that violated concurrent read/writes restriction.
                      • +
                      • Added warnings about read/write hazards to the checked build.
                      • +
                      • Fixed RepX not reading joint properties.
                      • +
                      • Fixed support for concurrent scene queries.
                      • +
                      • Fixed PhysX GPU Visual Indicator support.
                      • +
                      • Made it more clear in documentation that simulate(0) is not allowed.
                      • +
                      + +

                      Rigid Bodies

                      +
                        +
                      • eNOTIFY_TOUCH_LOST trigger events do now get reported if one of the objects in contact gets deleted (see documentation of PxTriggerPair for details).
                      • +
                      • Dynamic rigid bodies with trigger shapes only do not wake up other touching bodies anymore.
                      • +
                      • Added lost touch events for trigger reports when objects get deleted.
                      • +
                      • Fixed dynamic triggers waking up actors they are triggered by.
                      • +
                      • Removed an inapropriate assert from articulation code.
                      • +
                      • Fixed problem with the angular momentum conservation of articulations.
                      • +
                      • Fixed articulation sleep problems.
                      • +
                      • Fixed a linear velocity related bug in CCD.
                      • +
                      • Fixed crash bug CCD.
                      • +
                      • Optimized performance of joint information being sent to PVD.
                      • +
                      + +
                      + +


                      Supported Platforms (available in separate packages)

                      + +
                      +

                      Runtime

                      +
                        +
                      • Microsoft Windows XP or later
                      • +
                      • Microsoft XBox 360
                      • +
                      • Sony Playstation 3
                      • +
                      • Android 2.2 or later for SDK, 2.3 or later required for samples
                      • +
                      • Linux (tested on Ubuntu)
                      • +
                      • Mac OS X
                      • +
                      +

                      Development

                      +
                        +
                      • Microsoft Windows XP or later
                      • +
                      • Microsoft Visual Studio 2008, 2010
                      • +
                      • Xcode 3
                      • +
                      +
                      + +


                      Known Issues And Limitations

                      + +
                      + Unchanged from from 3.1. +
                      + + + +
                      + + + +

                      Release Notes - NVIDIA® PhysX® SDK 3.1

                      + +

                      September 2011

                      + +

                      What's New In NVIDIA PhysX 3.1

                      + +
                      +

                      General

                      +
                        +
                      • VC10 support has been introduced.
                      • +
                      • VC8 support has been discontinued.
                      • +
                      • Namespaces cleaned up.
                      • +
                      • Extensions, Character Controller and Vehicle source code made available in binary distribution.
                      • +
                      • Added x86,x64 suffix to PxTaskCUDA.dll
                      • +
                      • Removed boolean return value from PxScene::addActor(...), and similar API calls.
                      • +
                      • Added MacOS, Android and Linux to the list of supported platforms. See Supported Platforms below for details.
                      • +
                      • Upgraded GPU tech to CUDA 4.
                      • +
                      • Cleaned up a large number of warnings at C++ warning level 4, and set SDK to compile with warnings as errors.
                      • +
                      • Removed individual sample executables in favor of one Samples executable from PC and console builds.
                      • +
                      • Fixed alpha blending in samples.
                      • +
                      • Simplified some code in samples.
                      • +
                      • Improved ambient lighting in samples.
                      • +
                      • Made samples work with older graphics cards.
                      • +
                      • Improved and added more content the user's guide.
                      • +
                      • No longer passing NULL pointers to user allocator to deallocate.
                      • +
                      • Various improvements to Foundation and classes shared with APEX.
                      • +
                      +

                      Rigid Bodies

                      +
                        +
                      • Rigid Body: High performance alternative convex narrow phase code available to source licensees. See PERSISTENT_CONTACT_MANIFOLD in the code.
                      • +
                      • Significant advancements in the continuous collision detection algorithm.
                      • +
                      • Optimizations and robustness improvements for articulations.
                      • +
                      • Added some helper code to the API.
                      • +
                      • Added sleep code for articulations.
                      • +
                      • Added support for vehicles with more than one chassis shape.
                      • +
                      • Solver iteration count for articulations.
                      • +
                      • Articulation limit padding configurable.
                      • +
                      • The reference count of meshes does now take the application's reference into acount as well and thus has increased by 1 (it used to count the number of objects referencing the mesh only). Note that a mesh does only get destroyed and removed from the list of meshes once the reference count reaches 0.
                      • +
                      • Fixed autowake parameter sometimes being ignored.
                      • +
                      • Constraint solver optimizations.
                      • +
                      • Improved behavior of character controller on steep slopes.
                      • +
                      • Binary serialization now saves names.
                      • +
                      • Removed some descriptors from API.
                      • +
                      • Removed the angular velocity term in the joint positional drive error formula.
                      • +
                      • Fixed bug in capsule sweep versus mesh.
                      • +
                      • Fixed a crash bug in the tire model.
                      • +
                      • Fixed crashing of single link articulations.
                      • +
                      • Fixed bug related to removing elements of an aggregate.
                      • +
                      • Fixed swapped wheel graphs in sample vehicle.
                      • +
                      • Fixed some slow moving bodies falling asleep in midair.
                      • +
                      • Fixed missing collisions after a call to resetFiltering.
                      • +
                      • Fixed broken autowake option in setAngularVelocity.
                      • +
                      • Fixed D6 joint linear limits being uninitialized.
                      • +
                      • A large number of misc. bug fixes and optimizations.
                      • +
                      • Improved documentation and error messages associated with running out of narrow phase buffer blocks.
                      • +
                      • Added articulation documentation.
                      • +
                      • Expanded manual sections on joints.
                      • +
                      • Improved reference doc for PxSceneQueryHitType.
                      • +
                      • Added reference doc for PxSerializable.
                      • +
                      + +

                      Particles

                      +
                        +
                      • Particle index allocation removed from SDK. Added index allocation pool to extensions.
                      • +
                      • Replaced GPU specific side band API PxPhysicsGpu and PxPhysics::getPhysicsGpu() with PxParticleGpu.
                      • +
                      • + Memory optimizations on all platforms and options to reduce memory usage according to use case with new per particle system flags: +
                          +
                        • PxParticleBaseFlag::eCOLLISION_WITH_DYNAMIC_ACTORS
                        • +
                        • PxParticleBaseFlag::ePER_PARTICLE_COLLISION_CACHE_HINT
                        • +
                        +
                      • +
                      • Fixed rare crash appearing with multi-threaded non-GPU particle systems and rigid bodies.
                      • +
                      • Fixed particles leaking through triangle mesh geometry on GPU.
                      • +
                      • Fixed fast particles tunneling through scene geometry in some cases.
                      • +
                      • Fixed erroneous collision of particles with teleporting rigid shapes (setGlobalPose).
                      • +
                      • Fixed particle sample behavior with some older GPU models.
                      • +
                      • Fixed a GPU particle crash bug.
                      • +
                      + + +

                      Cloth

                      +
                        +
                      • A new solution for simulating cloth and clothing.
                      • +
                      + + +

                      Deformables

                      +
                        +
                      • Deformables are deprecated and will be removed in the next release. There is a new optimized solution for clothing simulation (see documentation on PxCloth for details).
                      • +
                      +
                      + +


                      Supported Platforms (available in separate packages)

                      + +
                      +

                      Runtime

                      +
                        +
                      • Microsoft Windows XP or later
                      • +
                      • Microsoft XBox 360
                      • +
                      • Sony Playstation 3
                      • +
                      • Android 2.2 or later for SDK, 2.3 or later required for samples
                      • +
                      • Linux (SDK tested on Ubuntu, samples not yet ported)
                      • +
                      • Mac OS X
                      • +
                      +

                      Development

                      +
                        +
                      • Microsoft Windows XP or later
                      • +
                      • Microsoft Visual Studio 2008, 2010
                      • +
                      • Xcode 3
                      • +
                      +
                      + +


                      Known Issues And Limitations

                      + +
                      +

                      General

                      +
                        +
                      • Under VC10, you may get warnings due to conflicting build configuration flags. Workaround: Clear the "Inherit from parent or project defaults" flag for all projects in Project->Properties->C/C++->Command Line. We plan to fix this for the 3.1 general availability release.
                      • +
                      +

                      Scene Query

                      +
                        +
                      • Querying the scene (e.g. using raycastSingle()) from multiple threads simultaneously is not safe.
                      • +
                      +

                      Cloth

                      +
                        +
                      • Even simple parameters of a PxCloth can not be set or accessed while the simulation is running.
                      • +
                      +

                      RepX

                      +
                        +
                      • RepX fails to load elements of aggregate joint parameters (PxD6JointDrive etc.)
                      • +
                      +
                      + +
                      + Please also see the previous lists from 3.0. +
                      + + + +
                      + + + +

                      Release Notes - NVIDIA® PhysX® SDK 3.0

                      + +

                      February 14th2011

                      + +

                      What's New In NVIDIA PhysX 3.0

                      + +
                      +

                      General

                      + + This third major version of the SDK is a significant rewrite of the entire technology. We did away with a large amount of legacy clutter and replaced them with a wealth of new features and improvements. + Because even the API changes are so significant, it is easier to see it as a whole new product rather than a long list of changes. + +

                      What we removed:

                      +
                        +
                      • The dedicated NVIDIA PhysX PPU hardware is not supported any more.
                      • +
                      • Scene compartments are not supported anymore. All created physical objects are now part of one and the same compartment.
                      • +
                      • Force fields are not part of the NVIDIA PhysX SDK anymore.
                      • +
                      • Splitting a simulation timestep into multiple substeps is not a functionality of the NVIDIA PhysX SDK any longer and has to be implemented above the SDK.
                      • +
                      + +

                      Key new features:

                      +
                        +
                      • Articulations: A way to create very stiff joint assemblies.
                      • +
                      • Serialization: Save objects in a binary format and load them back quickly!
                      • +
                      • Broad Phase Clustering: Put objects that belong together into a single broadphase volume.
                      • +
                      • Driverless Model: No need to worry about system software on PC anymore.
                      • +
                      • Dynamic Character Controller: A character controller that can robustly walk on dynamic objects.
                      • +
                      • Vehicle Library: A toolkit to make vehicles, including an all new tire model.
                      • +
                      • Non-Simulation Objects: A staging are outside of the simulation from where you can add things into the simulation at high speed.
                      • +
                      • Simulation Task Manager: Take control of the management of simulation tasks.
                      • +
                      • Stable Depenetration: Large penetrations can be gracefully recovered from.
                      • +
                      • Double Buffering: You can read from and write to the simulated objects while the simulation is running on another thread.
                      • +
                      • Mesh Scaling: Create different nonuniformly scaled instances of your meshes and convexes without duplicating the memory.
                      • +
                      • Distance Based Collision Detection: Have the simulation create contacts before objects touch, and do away with unsightly overlaps.
                      • +
                      • Fast Continuous Collision Detection: Have small and high speed objects collide correctly without sacrificing performance.
                      • +
                      • Significantly increased performance and memory footprint, especially on consoles.
                      • +
                      • Unified solver for deformables and rigid bodies for great interaction.
                      • +
                      • Triangle collision detection with deformables.
                      • +
                      • Support for our new Physics Visual Debugger, including integrated profiling.
                      • +
                      + + +

                      Math classes

                      +
                        +
                      • Matrix based transforms have been replaced by quaternions.
                      • +
                      • + All angles are now expressed in radians. IN PARTICULAR the PxQuat constructor from + axis and angle as well as the getAngleAxis and fromAngleAxis methods now use radians rather than degrees. +
                      • +
                      + +

                      Rigid Bodies

                      +
                        +
                      • Capsules are now defined to extend along the x rather than the y axis.
                      • +
                      • Triangle meshes do not support heightfield functionality anymore. Use the dedicated PxHeightField class instead.
                      • +
                      • Dynamic triangle mesh actors are not supported any longer. However, you can decompose your mesh into convex parts and create a dynamic actor consisting of these convex parts.
                      • +
                      • The deprecated heightfield property NxHeightFieldDesc::verticalExtent is not supported any longer. Please use the PxHeightFieldDesc::thickness parameter instead.
                      • +
                      • NxSpringAndDamperEffector is not supported anymore. Use PxDistanceJoint instead.
                      • +
                      • Joints are now part of the PhysX extensions library (PhysXExtensions).
                      • +
                      • Wheel shapes have been replaced by the more flexible entity PxWheel. A default wheel implementation, encapsulating most of the old wheel functionality, can be found in the PhysX extensions library (see PxDefaultWheel).
                      • +
                      • The NxUtilLib library has been removed. Sweep/overlap/raycast tests and other helper methods can be found in the new GeomUtils library.
                      • +
                      • Materials can no longer be accessed through indices. Per triangle material meshes need a material table which can be specified per shape (see PxShape::setMaterials() for details).
                      • +
                      • The default material is not available anymore.
                      • +
                      + +

                      Particle Systems, Particle Fluids

                      +
                        +
                      • + The NxFluid class has been replaced with two classes for separation of functionality and ease of use. +
                          +
                        • PxParticleSystem: Particles colliding against the scene.
                        • +
                        • PxParticleFluid: Particles simulating a fluid (sph).
                        • +
                        +
                      • +
                      • + Simplified parameterization for particle systems. +
                          +
                        • Using absolute distances instead of relative multipliers to rest spacing
                        • +
                        • Simplified sph parameters
                        • +
                        • Unified collision parameters with deformable and rigid body features
                        • +
                        +
                      • +
                      • + Creating and releasing particles is now fully controlled by the application. +
                          +
                        • Particle lifetime management isn't provided through the SDK anymore.
                        • +
                        • Emitters have been removed from the SDK.
                        • +
                        • Drain shapes don't cause particles to be deleted directly, but to be flagged instead.
                        • +
                        • Initial particle creation from the particle system descriptor isn't supported anymore.
                        • +
                        +
                      • +
                      • Particle data buffer handling has been moved to the SDK.
                      • +
                      • Per particle collision rest offset.
                      • +
                      • + GPU accelerated particle systems. +
                          +
                        • Application controllable mesh mirroring to device memory.
                        • +
                        • Runtime switching between software and GPU accelerated implementation.
                        • +
                        +
                      • +
                      +
                      + +


                      Supported Platforms (available in separate packages)

                      + +
                      +

                      Runtime

                      +
                        +
                      • Microsoft Windows XP or and later
                      • +
                      • Microsoft XBox360
                      • +
                      • Sony Playstation 3
                      • +
                      +

                      Development

                      +
                        +
                      • Microsoft Windows XP or and later
                      • +
                      • Microsoft Visual Studio 2008
                      • +
                      +
                      + +


                      Known Issues And Limitations

                      + +
                      +

                      Rigid Bodies

                      +
                        +
                      • Adding or removing a PxAggregate object to the scene is not possible while the simulation is running.
                      • +
                      +

                      Particle Systems

                      +
                        +
                      • + Releasing the Physics SDK may result in a crash when using GPU accelerated particle systems.
                        + This can be avoided by doing the following before releasing the Physics SDK: +
                          +
                        • Releasing the PxScene objects that contain the GPU accelerated particle systems.
                        • +
                        • Releasing application mirrored meshes by calling PxPhysicsGpu::releaseTriangleMeshMirror(...), PxPhysicsGpu::releaseHeightFieldMirror(...) or PxPhysicsGpu::releaseConvexMeshMirror(...).
                        • +
                        +
                      • +
                      +
                      + +

                      +
                      + Copyright (C) 2008-2018 NVIDIA Corporation, 2701 San Thomas Expressway, Santa Clara, CA 95050 U.S.A. All rights reserved. www.nvidia.com +

                      + + diff --git a/src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h b/src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h new file mode 100644 index 000000000..6a99075be --- /dev/null +++ b/src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef CM_WINDOWS_LOADLIBRARY_H +#define CM_WINDOWS_LOADLIBRARY_H + +#include "foundation/PxPreprocessor.h" +#include "windows/PxWindowsDelayLoadHook.h" +#include "windows/PsWindowsInclude.h" + +#ifdef PX_SECURE_LOAD_LIBRARY +#include "nvSecureLoadLibrary.h" +#endif + + +namespace physx +{ +namespace Cm +{ + EXTERN_C IMAGE_DOS_HEADER __ImageBase; + + PX_INLINE HMODULE WINAPI loadLibrary(const char* name) + { +#ifdef PX_SECURE_LOAD_LIBRARY + HMODULE retVal = nvLoadSignedLibrary(name,true); + if(!retVal) + { + exit(1); + } + return retVal; +#else + return ::LoadLibraryA( name ); +#endif + }; + + PX_INLINE FARPROC WINAPI physXCommonDliNotePreLoadLibrary(const char* libraryName, const physx::PxDelayLoadHook* delayLoadHook) + { + if(!delayLoadHook) + { + return (FARPROC)loadLibrary(libraryName); + } + else + { + if(strstr(libraryName, "PhysXFoundation")) + { + return (FARPROC)Cm::loadLibrary(delayLoadHook->getPhysXFoundationDllName()); + } + + if(strstr(libraryName, "PhysXCommon")) + { + return (FARPROC)Cm::loadLibrary(delayLoadHook->getPhysXCommonDllName()); + } + } + return NULL; + } +} // namespace Cm +} // namespace physx + + +#endif // CM_WINDOWS_LOADLIBRARY_H diff --git a/src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h b/src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h new file mode 100644 index 000000000..3afd86db2 --- /dev/null +++ b/src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef CM_WINDOWS_MODULEUPDATELOADER_H +#define CM_WINDOWS_MODULEUPDATELOADER_H + +#include "foundation/PxPreprocessor.h" +#include "common/PxPhysXCommonConfig.h" + +#include "windows/PsWindowsInclude.h" + +namespace physx +{ +namespace Cm +{ + +#if PX_X64 +#define UPDATE_LOADER_DLL_NAME "PhysXUpdateLoader64.dll" +#else +#define UPDATE_LOADER_DLL_NAME "PhysXUpdateLoader.dll" +#endif + +class PX_PHYSX_COMMON_API CmModuleUpdateLoader +{ +public: + CmModuleUpdateLoader(const char* updateLoaderDllName); + + ~CmModuleUpdateLoader(); + + // Loads the given module through the update loader. Loads it from the path if + // the update loader doesn't find the requested module. Returns NULL if no + // module found. + HMODULE LoadModule(const char* moduleName, const char* appGUID); + +protected: + HMODULE mUpdateLoaderDllHandle; + FARPROC mGetUpdatedModuleFunc; +}; +} // namespace Cm +} // namespace physx + + +#endif // CM_WINDOWS_MODULEUPDATELOADER_H diff --git a/src/PhysX/physx/source/common/src/CmBitMap.h b/src/PhysX/physx/source/common/src/CmBitMap.h new file mode 100644 index 000000000..31c62853b --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmBitMap.h @@ -0,0 +1,504 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_BITMAP +#define PX_PHYSICS_COMMON_BITMAP + +#include "foundation/PxAssert.h" +#include "foundation/PxMath.h" +#include "foundation/PxMemory.h" +#include "PsAllocator.h" +#include "PsUserAllocated.h" +#include "PsIntrinsics.h" +#include "PsMathUtils.h" +#include "CmPhysXCommon.h" +#include "PsBitUtils.h" +// PX_SERIALIZATION +#include "PxSerialFramework.h" +//~PX_SERIALIZATION + +namespace physx +{ +namespace Cm +{ + + /*! + Hold a bitmap with operations to set,reset or test given bit. + + We inhibit copy to prevent unintentional copies. If a copy is desired copy() should be used or + alternatively a copy constructor implemented. + */ + template + class BitMapBase : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_NOCOPY(BitMapBase) + + public: + + // PX_SERIALIZATION + /* todo: explicit */ BitMapBase(const PxEMPTY) + { + if(mMap) + mWordCount |= PX_SIGN_BITMASK; + } + + void exportExtraData(PxSerializationContext& stream, void*) + { + if(mMap && getWordCount()) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mMap, getWordCount()*sizeof(PxU32)); + } + } + void importExtraData(PxDeserializationContext& context) + { + if(mMap && getWordCount()) + mMap = context.readExtraData(getWordCount()); + } + //~PX_SERIALIZATION + + //sschirm: function for placement new. Almost the same as importExtraData above, but lets you set word count and map after default construction + void importData(PxU32 worldCount, PxU32* words) + { + PX_ASSERT(mWordCount == 0 && !mMap); + mMap = words; + mWordCount = worldCount | PX_SIGN_BITMASK; + } + + PX_INLINE BitMapBase(Allocator& allocator) : mMap(0), mWordCount(0), mAllocator(allocator) {} + + PX_INLINE BitMapBase() : mMap(0), mWordCount(0) {} + + PX_INLINE ~BitMapBase() + { + if(mMap && !isInUserMemory()) + mAllocator.deallocate(mMap); + mMap = NULL; + } + + PX_INLINE Allocator& getAllocator() { return mAllocator; } + + PX_INLINE void growAndSet(PxU32 index) + { + extend(index+1); + mMap[index>>5] |= 1<<(index&31); + } + + PX_INLINE void growAndReset(PxU32 index) + { + extend(index+1); + mMap[index>>5] &= ~(1<<(index&31)); + } + + PX_INLINE Ps::IntBool boundedTest(PxU32 index) const + { + return Ps::IntBool(index>>5 >= getWordCount() ? Ps::IntFalse : (mMap[index>>5]&(1<<(index&31)))); + } + + // Special optimized versions, when you _know_ your index is in range + PX_INLINE void set(PxU32 index) + { + PX_ASSERT(index>5] |= 1<<(index&31); + } + + PX_INLINE void reset(PxU32 index) + { + PX_ASSERT(index>5] &= ~(1<<(index&31)); + } + + PX_INLINE Ps::IntBool test(PxU32 index) const + { + PX_ASSERT(index>5]&(1<<(index&31))); + } + + // nibble == 4 bits + PX_INLINE PxU32 getNibbleFast(PxU32 nibIndex) const + { + PxU32 bitIndex = nibIndex << 2; + PX_ASSERT(bitIndex < getWordCount()*32); + return (mMap[bitIndex >> 5] >> (bitIndex & 31)) & 0xf; + } + + PX_INLINE void andNibbleFast(PxU32 nibIndex, PxU32 mask) + { + //TODO: there has to be a faster way... + PxU32 bitIndex = nibIndex << 2; + PxU32 shift = (bitIndex & 31); + PxU32 nibMask = 0xf << shift; + + PX_ASSERT(bitIndex < getWordCount()*32); + + mMap[bitIndex >> 5] &= ((mask << shift) | ~nibMask); + } + + PX_INLINE void orNibbleFast(PxU32 nibIndex, PxU32 mask) + { + PX_ASSERT(!(mask & ~0xf)); //check extra bits are not set + + PxU32 bitIndex = nibIndex << 2; + PxU32 shift = bitIndex & 31; + + PX_ASSERT(bitIndex < getWordCount()*32); + + mMap[bitIndex >> 5] |= (mask << shift); + } + + void clear() + { + PxMemSet(mMap, 0, getWordCount()*sizeof(PxU32)); + } + + void resizeAndClear(PxU32 newBitCount) + { + extendUninitialized(newBitCount); + PxMemSet(mMap, 0, getWordCount()*sizeof(PxU32)); + } + + void setEmpty() + { + mMap=NULL; + mWordCount=0; + } + + void setWords(PxU32* map, PxU32 wordCount) + { + mMap=map; + mWordCount=wordCount; + mWordCount |= PX_SIGN_BITMASK; + } + + // !!! only sets /last/ bit to value + void resize(PxU32 newBitCount, bool value = false) + { + PX_ASSERT(!value); // only new class supports this + PX_UNUSED(value); + extend(newBitCount); + } + PxU32 size() const { return getWordCount()*32; } + + void copy(const BitMapBase& a) + { + extendUninitialized(a.getWordCount()<<5); + PxMemCopy(mMap, a.mMap, a.getWordCount() * sizeof(PxU32)); + if(getWordCount() > a.getWordCount()) + PxMemSet(mMap + a.getWordCount(), 0, (getWordCount() - a.getWordCount()) * sizeof(PxU32)); + } + + PX_INLINE PxU32 count() const + { + // NOTE: we can probably do this faster, since the last steps in PxcBitCount32 can be defered to + // the end of the seq. + 64/128bits at a time + native bit counting instructions(360 is fast non micro code). + PxU32 count = 0; + PxU32 wordCount = getWordCount(); + for(PxU32 i=0; i 0;) + { + if(mMap[i]) + return (i<<5)+Ps::highestSetBit(mMap[i]); + } + return PxU32(0); + } + + + + // the obvious combiners and some used in the SDK + + struct OR { PX_INLINE PxU32 operator()(PxU32 a, PxU32 b) { return a|b; } }; + struct AND { PX_INLINE PxU32 operator()(PxU32 a, PxU32 b) { return a&b; } }; + struct XOR { PX_INLINE PxU32 operator()(PxU32 a, PxU32 b) { return a^b; } }; + + // we use auxiliary functions here so as not to generate combiners for every combination + // of allocators + + template + PX_INLINE void combineInPlace(const BitMapBase<_>& b) + { + combine1(b.mMap,b.getWordCount()); + } + + template + PX_INLINE void combine(const BitMapBase<_1>& a, const BitMapBase<_2>& b) + { + combine2(a.mMap,a.getWordCount(),b.mMap,b.getWordCount()); + } + + PX_FORCE_INLINE const PxU32* getWords() const { return mMap; } + PX_FORCE_INLINE PxU32* getWords() { return mMap; } + + // PX_SERIALIZATION + PX_FORCE_INLINE PxU32 getWordCount() const { return mWordCount & ~PX_SIGN_BITMASK; } + + // We need one bit to mark arrays that have been deserialized from a user-provided memory block. + PX_FORCE_INLINE PxU32 isInUserMemory() const { return mWordCount & PX_SIGN_BITMASK; } + //~PX_SERIALIZATION + + /*! + Iterate over indices in a bitmap + + This iterator is good because it finds the set bit without looping over the cached bits upto 31 times. + However it does require a variable shift. + */ + + class Iterator + { + public: + static const PxU32 DONE = 0xffffffff; + + PX_INLINE Iterator(const BitMapBase &map) : mBitMap(map) + { + reset(); + } + + PX_INLINE Iterator& operator=(const Iterator& other) + { + PX_ASSERT(&mBitMap == &other.mBitMap); + mBlock = other.mBlock; + mIndex = other.mIndex; + return *this; + } + + PX_INLINE PxU32 getNext() + { + if(mBlock) + { + PxU32 bitIndex = mIndex<<5 | Ps::lowestSetBit(mBlock); + mBlock &= mBlock-1; + PxU32 wordCount = mBitMap.getWordCount(); + while(!mBlock && ++mIndex < wordCount) + mBlock = mBitMap.mMap[mIndex]; + return bitIndex; + } + return DONE; + } + + PX_INLINE void reset() + { + mIndex = mBlock = 0; + PxU32 wordCount = mBitMap.getWordCount(); + while(mIndex < wordCount && ((mBlock = mBitMap.mMap[mIndex]) == 0)) + ++mIndex; + } + private: + PxU32 mBlock, mIndex; + const BitMapBase& mBitMap; + }; + + // DS: faster but less general: hasBits() must be true or getNext() is illegal so it is the calling code's responsibility to ensure that getNext() is not called illegally. + class LoopIterator + { + PX_NOCOPY(LoopIterator) + + public: + PX_FORCE_INLINE LoopIterator(const BitMapBase &map) : mMap(map.getWords()), mBlock(0), mIndex(-1), mWordCount(PxI32(map.getWordCount())) {} + + PX_FORCE_INLINE bool hasBits() + { + PX_ASSERT(mIndex>5; + if(newWordCount > getWordCount()) + { + PxU32* newMap = reinterpret_cast(mAllocator.allocate(newWordCount*sizeof(PxU32), __FILE__, __LINE__)); + if(mMap) + { + PxMemCopy(newMap, mMap, getWordCount()*sizeof(PxU32)); + if (!isInUserMemory()) + mAllocator.deallocate(mMap); + } + PxMemSet(newMap+getWordCount(), 0, (newWordCount-getWordCount())*sizeof(PxU32)); + mMap = newMap; + // also resets the isInUserMemory bit + mWordCount = newWordCount; + } + } + + void extendUninitialized(PxU32 size) + { + PxU32 newWordCount = (size+31)>>5; + if(newWordCount > getWordCount()) + { + if(mMap && !isInUserMemory()) + mAllocator.deallocate(mMap); + // also resets the isInUserMemory bit + mWordCount = newWordCount; + mMap = reinterpret_cast(mAllocator.allocate(mWordCount*sizeof(PxU32), __FILE__, __LINE__)); + } + } + + template + void combine1(const PxU32* words, PxU32 length) + { + extend(length<<5); + PxU32 combineLength = PxMin(getWordCount(), length); + for(PxU32 i=0;i + void combine2(const PxU32* words1, PxU32 length1, + const PxU32* words2, PxU32 length2) + { + extendUninitialized(PxMax(length1,length2)<<5); + + PxU32 commonSize = PxMin(length1,length2); + + for(PxU32 i=0;i BitMap; + typedef BitMapBase BitMapPinned; + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmBlockArray.h b/src/PhysX/physx/source/common/src/CmBlockArray.h new file mode 100644 index 000000000..2e80a9b47 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmBlockArray.h @@ -0,0 +1,153 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CM_BLOCK_ARRAY_H +#define CM_BLOCK_ARRAY_H + +#include "foundation/PxAssert.h" +#include "foundation/PxMath.h" +#include "foundation/PxMemory.h" +#include "PsAllocator.h" +#include "PsUserAllocated.h" +#include "PsIntrinsics.h" +#include "PsMathUtils.h" +#include "CmPhysXCommon.h" +#include "PsArray.h" + +namespace physx +{ +namespace Cm +{ + +template +class BlockArray +{ + Ps::Array mBlocks; + PxU32 mSize; + PxU32 mCapacity; + PxU32 mSlabSize; + +public: + + BlockArray(PxU32 slabSize = 2048) : mSize(0), mCapacity(0), mSlabSize(slabSize) + { + PX_ASSERT(slabSize > 0); + } + + ~BlockArray() + { + for (PxU32 a = 0; a < mBlocks.size(); ++a) + { + PX_FREE(mBlocks[a]); + } + mBlocks.resize(0); + } + + void reserve(PxU32 capacity) + { + if (capacity > mCapacity) + { + PxU32 nbSlabsRequired = (capacity + mSlabSize - 1) / mSlabSize; + + PxU32 nbSlabsToAllocate = nbSlabsRequired - mBlocks.size(); + + mCapacity += nbSlabsToAllocate * mSlabSize; + + for (PxU32 a = 0; a < nbSlabsToAllocate; ++a) + { + mBlocks.pushBack(reinterpret_cast(PX_ALLOC(sizeof(T) * mSlabSize, PX_DEBUG_EXP("BlockArray")))); + } + } + } + + void resize(PxU32 size) + { + reserve(size); + for (PxU32 a = mSize; a < size; ++a) + { + mBlocks[a / mSlabSize][a%mSlabSize] = T(); + } + mSize = size; + } + + void forceSize_Unsafe(PxU32 size) + { + PX_ASSERT(size <= mCapacity); + mSize = size; + } + + void remove(PxU32 idx) + { + PX_ASSERT(idx < mSize); + for (PxU32 a = idx; a < mSize; ++a) + { + mBlocks[a / mSlabSize][a%mSlabSize] = mBlocks[(a + 1) / mSlabSize][(a + 1) % mSlabSize]; + } + + mSize--; + } + + void replaceWithLast(PxU32 idx) + { + PX_ASSERT(idx < mSize); + --mSize; + mBlocks[idx / mSlabSize][idx%mSlabSize] = mBlocks[mSize / mSlabSize][mSize%mSlabSize]; + } + + T& operator [] (const PxU32 idx) + { + PX_ASSERT(idx < mSize); + + return mBlocks[idx / mSlabSize][idx%mSlabSize]; + } + + const T& operator [] (const PxU32 idx) const + { + PX_ASSERT(idx < mSize); + + return mBlocks[idx / mSlabSize][idx%mSlabSize]; + } + + void pushBack(const T& item) + { + reserve(mSize + 1); + mBlocks[mSize / mSlabSize][mSize%mSlabSize] = item; + mSize++; + } + + PxU32 capacity() const { return mCapacity; } + + PxU32 size() const { return mSize; } +}; + +} +} + +#endif + diff --git a/src/PhysX/physx/source/common/src/CmBoxPruning.cpp b/src/PhysX/physx/source/common/src/CmBoxPruning.cpp new file mode 100644 index 000000000..9622ffdbc --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmBoxPruning.cpp @@ -0,0 +1,197 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CmBoxPruning.h" +#include "CmRadixSortBuffered.h" +#include "PsAllocator.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. + * \param nb0 [in] number of boxes in the first set + * \param bounds0 [in] list of boxes for the first set + * \param nb1 [in] number of boxes in the second set + * \param bounds1 [in] list of boxes for the second set + * \param pairs [out] list of overlapping pairs + * \param axes [in] projection order (0,2,1 is often best) + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Cm::BipartiteBoxPruning(const PxBounds3* bounds0, PxU32 nb0, const PxBounds3* bounds1, PxU32 nb1, Ps::Array& pairs, const Axes& axes) +{ + pairs.clear(); + // Checkings + if(nb0 == 0 || nb1 == 0) + return false; + + // Catch axes + PxU32 Axis0 = axes.mAxis0; + PxU32 Axis1 = axes.mAxis1; + PxU32 Axis2 = axes.mAxis2; + + PX_UNUSED(Axis1); + PX_UNUSED(Axis2); + + // Allocate some temporary data + float* MinPosBounds0 = reinterpret_cast(PX_ALLOC_TEMP(sizeof(float)*nb0, "Gu::BipartiteBoxPruning")); + float* MinPosBounds1 = reinterpret_cast(PX_ALLOC_TEMP(sizeof(float)*nb1, "Gu::BipartiteBoxPruning")); + + // 1) Build main lists using the primary axis + for(PxU32 i=0;i& pairs, const Axes& axes) +{ + pairs.clear(); + + // Checkings + if(!nb) + return false; + + // Catch axes + const PxU32 Axis0 = axes.mAxis0; + const PxU32 Axis1 = axes.mAxis1; + const PxU32 Axis2 = axes.mAxis2; + + PX_UNUSED(Axis1); + PX_UNUSED(Axis2); + + // Allocate some temporary data + float* PosList = reinterpret_cast(PX_ALLOC_TEMP(sizeof(float)*nb, "Cm::CompleteBoxPruning")); + + // 1) Build main list using the primary axis + for(PxU32 i=0;i& pairs, const Gu::Axes& axes); + PX_PHYSX_COMMON_API bool BipartiteBoxPruning(const PxBounds3* bounds0, PxU32 nb0, const PxBounds3* bounds1, PxU32 nb1, Ps::Array& pairs, const Gu::Axes& axes); +} +} + +#endif // CM_BOXPRUNING_H diff --git a/src/PhysX/physx/source/common/src/CmCollection.cpp b/src/PhysX/physx/source/common/src/CmCollection.cpp new file mode 100644 index 000000000..87e9e1ccb --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmCollection.cpp @@ -0,0 +1,217 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CmCollection.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Cm; + +void Collection::add(PxBase& object, PxSerialObjectId id) +{ + PxSerialObjectId originId = getId(object); + if( originId != PX_SERIAL_OBJECT_ID_INVALID) + { + if( originId != id) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxCollection::add called for an object that has an associated id already present in the collection!"); + } + return; + } + + if(id != PX_SERIAL_OBJECT_ID_INVALID) + { + if(!mIds.insert(id, &object)) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxCollection::add called with an id which is already used in the collection"); + return; + } + } + + mObjects[&object] = id; +} + +void Collection::remove(PxBase& object) +{ + PX_CHECK_AND_RETURN(contains(object), "PxCollection::remove called for an object not contained in the collection!"); + + const ObjectToIdMap::Entry* e = mObjects.find(&object); + if(e) + { + mIds.erase(e->second); + mObjects.erase(&object); + } +} + +bool Collection::contains(PxBase& object) const +{ + return mObjects.find(&object) != NULL; +} + +void Collection::addId(PxBase& object, PxSerialObjectId id) +{ + PX_CHECK_AND_RETURN(contains(object), "PxCollection::addId called for object that is not contained in the collection!"); + PX_CHECK_AND_RETURN(id != PX_SERIAL_OBJECT_ID_INVALID, "PxCollection::addId called with PxSerialObjectId being set to PX_SERIAL_OBJECT_ID_INVALID!"); + PX_CHECK_AND_RETURN(mIds.find(id) == NULL, "PxCollection::addId called with an id which is already used in the collection!"); + + const ObjectToIdMap::Entry* e = mObjects.find(&object); + if(e && e->second != PX_SERIAL_OBJECT_ID_INVALID) + mIds.erase(e->second); + + mIds.insert(id, &object); + mObjects[&object] = id; +} + +void Collection::removeId(PxSerialObjectId id) +{ + PX_CHECK_AND_RETURN(id != PX_SERIAL_OBJECT_ID_INVALID, "PxCollection::removeId called with PxSerialObjectId being set to PX_SERIAL_OBJECT_ID_INVALID!"); + PX_CHECK_AND_RETURN(mIds.find(id), "PxCollection::removeId called with PxSerialObjectId not contained in the collection!"); + const IdToObjectMap::Entry* e = mIds.find(id); + if(e) + { + mObjects[e->second] = PX_SERIAL_OBJECT_ID_INVALID; + mIds.erase(id); + } +} + +PxBase* Collection::find(PxSerialObjectId id) const +{ + PX_CHECK_AND_RETURN_NULL(id != PX_SERIAL_OBJECT_ID_INVALID, "PxCollection::find called with PxSerialObjectId being set to PX_SERIAL_OBJECT_ID_INVALID!"); + const IdToObjectMap::Entry* e = mIds.find(id); + return e ? static_cast(e->second) : NULL; +} + +void Collection::add(PxCollection& _collection) +{ + Collection& collection = static_cast(_collection); + PX_CHECK_AND_RETURN(this != &collection, "PxCollection::add(PxCollection&) called with itself!"); + + mObjects.reserve(mObjects.capacity() + collection.mObjects.size()); + const ObjectToIdMap::Entry* e = collection.mObjects.getEntries(); + for (PxU32 i = 0; i < collection.mObjects.size(); ++i) + { + PxSerialObjectId id = e[i].second; + if( id != PX_SERIAL_OBJECT_ID_INVALID) + { + if(!mIds.insert(id, e[i].first)) + { + if(mIds[id] != e[i].first) + { + PX_CHECK_MSG( false, "PxCollection::add(PxCollection&) called with conflicting id!"); + mObjects.insert(e[i].first, PX_SERIAL_OBJECT_ID_INVALID); + } + } + else + mObjects[ e[i].first ] = id; + } + else + mObjects.insert(e[i].first, PX_SERIAL_OBJECT_ID_INVALID); + } +} + +void Collection::remove(PxCollection& _collection) +{ + Collection& collection = static_cast(_collection); + PX_CHECK_AND_RETURN(this != &collection, "PxCollection::remove(PxCollection&) called with itself!"); + + const ObjectToIdMap::Entry* e = collection.mObjects.getEntries(); + for (PxU32 i = 0; i < collection.mObjects.size(); ++i) + { + const ObjectToIdMap::Entry* e1 = mObjects.find(e[i].first); + if(e1) + { + mIds.erase(e1->second); + mObjects.erase(e1->first); + } + } +} + +PxU32 Collection::getNbObjects() const +{ + return mObjects.size(); +} + +PxBase& Collection::getObject(PxU32 index) const +{ + PX_ASSERT(index < mObjects.size()); + return *mObjects.getEntries()[index].first; +} + +PxU32 Collection::getObjects(PxBase** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + PX_CHECK_AND_RETURN_NULL(userBuffer != NULL, "PxCollection::getObjects called with userBuffer NULL!"); + PX_CHECK_AND_RETURN_NULL(bufferSize != 0, "PxCollection::getObjects called with bufferSize 0!"); + PxU32 dstIndex = 0; + const ObjectToIdMap::Entry* e = mObjects.getEntries(); + for (PxU32 srcIndex = startIndex; srcIndex < mObjects.size() && dstIndex < bufferSize; ++srcIndex) + userBuffer[dstIndex++] = e[srcIndex].first; + + return dstIndex; +} + +PxU32 Collection::getNbIds() const +{ + return mIds.size(); +} + +PxSerialObjectId Collection::getId(const PxBase& object) const +{ + const ObjectToIdMap::Entry* e = mObjects.find(const_cast(&object)); + return e ? e->second : PX_SERIAL_OBJECT_ID_INVALID; +} + +PxU32 Collection::getIds(PxSerialObjectId* userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + PX_CHECK_AND_RETURN_NULL(userBuffer != NULL, "PxCollection::getIds called with userBuffer NULL!"); + PX_CHECK_AND_RETURN_NULL(bufferSize != 0, "PxCollection::getIds called with bufferSize 0!"); + PxU32 dstIndex = 0; + + IdToObjectMap::Iterator srcIt = (const_cast(mIds)).getIterator(); + + while (!srcIt.done() && dstIndex < bufferSize) + { + if(srcIt->first != PX_SERIAL_OBJECT_ID_INVALID) + { + if(startIndex > 0) + startIndex--; + else + userBuffer[dstIndex++] = srcIt->first; + } + srcIt++; + } + + return dstIndex; +} + +PxCollection* PxCreateCollection() +{ + return PX_NEW(Collection); +} diff --git a/src/PhysX/physx/source/common/src/CmCollection.h b/src/PhysX/physx/source/common/src/CmCollection.h new file mode 100644 index 000000000..b80ec256b --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmCollection.h @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CM_COLLECTION +#define PX_PHYSICS_CM_COLLECTION + +#include "CmPhysXCommon.h" +#include "PxCollection.h" +#include "PsHashMap.h" +#include "PsUserAllocated.h" +#include "PsAllocator.h" + +namespace physx +{ +namespace Cm +{ + template , + class Allocator = Ps::NonTrackingAllocator > + class CollectionHashMap : public Ps::CoalescedHashMap< Key, Value, HashFn, Allocator> + { + typedef physx::shdfnd::internal::HashMapBase< Key, Value, HashFn, Allocator> MapBase; + typedef Ps::Pair EntryData; + + public: + CollectionHashMap(PxU32 initialTableSize = 64, float loadFactor = 0.75f): + Ps::CoalescedHashMap< Key, Value, HashFn, Allocator>(initialTableSize,loadFactor) {} + + void insertUnique(const Key& k, const Value& v) + { + PX_PLACEMENT_NEW(MapBase::mBase.insertUnique(k), EntryData)(k,v); + } + }; + + + + class Collection : public PxCollection, public Ps::UserAllocated + { + public: + typedef CollectionHashMap ObjectToIdMap; + typedef CollectionHashMap IdToObjectMap; + + virtual void add(PxBase& object, PxSerialObjectId ref); + virtual void remove(PxBase& object); + virtual bool contains(PxBase& object) const; + virtual void addId(PxBase& object, PxSerialObjectId id); + virtual void removeId(PxSerialObjectId id); + virtual PxBase* find(PxSerialObjectId ref) const; + virtual void add(PxCollection& collection); + virtual void remove(PxCollection& collection); + virtual PxU32 getNbObjects() const; + virtual PxBase& getObject(PxU32 index) const; + virtual PxU32 getObjects(PxBase** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxU32 getNbIds() const; + virtual PxSerialObjectId getId(const PxBase& object) const; + virtual PxU32 getIds(PxSerialObjectId* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + void release() { PX_DELETE(this); } + + + // Only for internal use. Bypasses virtual calls, specialized behaviour. + PX_INLINE void internalAdd(PxBase* s, PxSerialObjectId id = PX_SERIAL_OBJECT_ID_INVALID) { mObjects.insertUnique(s, id); } + PX_INLINE PxU32 internalGetNbObjects() const { return mObjects.size(); } + PX_INLINE PxBase* internalGetObject(PxU32 i) const { PX_ASSERT(i1e-6f); + return (tan1+tan2)/(1-tan1*tan2); + } + + PX_FORCE_INLINE float computeAxisAndError(const PxVec3& r, const PxVec3& d, const PxVec3& twistAxis, PxVec3& axis) + { + // the point on the cone defined by the tanQ swing vector r + // this code is equal to quatFromTanQVector(r).rotate(PxVec3(1.0f, 0.0f, 0.0f); + PxVec3 p(1.f,0,0); + PxReal r2 = r.dot(r), a = 1-r2, b = 1/(1+r2), b2 = b*b; + PxReal v1 = 2*a*b2; + PxVec3 v2(a, 2*r.z, -2*r.y); // a*p + 2*r.cross(p); + PxVec3 coneLine = v1 * v2 - p; // already normalized + + // the derivative of coneLine in the direction d + PxReal rd = r.dot(d); + PxReal dv1 = -4*rd*(3-r2)*b2*b; + PxVec3 dv2(-2*rd, 2*d.z, -2*d.y); + + PxVec3 coneNormal = v1 * dv2 + dv1 * v2; + + axis = coneLine.cross(coneNormal)/coneNormal.magnitude(); + return coneLine.cross(axis).dot(twistAxis); + } + + // this is here because it's used in both LL and Extensions. However, it + // should STAY IN THE SDK CODE BASE because it's SDK-specific + + class ConeLimitHelper + { + public: + ConeLimitHelper(PxReal tanQSwingY, PxReal tanQSwingZ, PxReal tanQPadding) + : mTanQYMax(tanQSwingY), mTanQZMax(tanQSwingZ), mTanQPadding(tanQPadding) {} + + // whether the point is inside the (inwardly) padded cone - if it is, there's no limit + // constraint + + PX_FORCE_INLINE bool contains(const PxVec3& tanQSwing) const + { + PxReal tanQSwingYPadded = tanAdd(PxAbs(tanQSwing.y),mTanQPadding); + PxReal tanQSwingZPadded = tanAdd(PxAbs(tanQSwing.z),mTanQPadding); + return Ps::sqr(tanQSwingYPadded/mTanQYMax)+Ps::sqr(tanQSwingZPadded/mTanQZMax) <= 1; + } + + PX_FORCE_INLINE PxVec3 clamp(const PxVec3& tanQSwing, PxVec3& normal) const + { + PxVec3 p = Ps::ellipseClamp(tanQSwing, PxVec3(0,mTanQYMax,mTanQZMax)); + normal = PxVec3(0, p.y/Ps::sqr(mTanQYMax), p.z/Ps::sqr(mTanQZMax)); +#ifdef PX_PARANOIA_ELLIPSE_CHECK + PxReal err = PxAbs(Ps::sqr(p.y/mTanQYMax) + Ps::sqr(p.z/mTanQZMax) - 1); + PX_ASSERT(err<1e-3); +#endif + return p; + } + + // input is a swing quat, such that swing.x = twist.y = twist.z = 0, q = swing * twist + // The routine is agnostic to the sign of q.w (i.e. we don't need the minimal-rotation swing) + + // output is an axis such that positive rotation increases the angle outward from the + // limit (i.e. the image of the x axis), the error is the sine of the angular difference, + // positive if the twist axis is inside the cone + + bool getLimit(const PxQuat& swing, PxVec3& axis, PxReal& error) const + { + PX_ASSERT(swing.w>0); + PxVec3 twistAxis = swing.getBasisVector0(); + PxVec3 tanQSwing = PxVec3(0, Ps::tanHalf(swing.z,swing.w), -Ps::tanHalf(swing.y,swing.w)); + if(contains(tanQSwing)) + return false; + + PxVec3 normal, clamped = clamp(tanQSwing, normal); + + // rotation vector and ellipse normal + PxVec3 r(0,-clamped.z,clamped.y), d(0, -normal.z, normal.y); + + error = computeAxisAndError(r, d, twistAxis, axis); + + PX_ASSERT(PxAbs(axis.magnitude()-1)<1e-5f); + +#ifdef PX_PARANOIA_ELLIPSE_CHECK + bool inside = Ps::sqr(tanQSwing.y/mTanQYMax) + Ps::sqr(tanQSwing.z/mTanQZMax) <= 1; + PX_ASSERT(inside && error>-1e-4f || !inside && error<1e-4f); +#endif + return true; + } + + private: + + + PxReal mTanQYMax, mTanQZMax, mTanQPadding; + }; + + class ConeLimitHelperTanLess + { + public: + ConeLimitHelperTanLess(PxReal swingY, PxReal swingZ, PxReal padding) + : mYMax(swingY), mZMax(swingZ), mPadding(padding) {} + + // whether the point is inside the (inwardly) padded cone - if it is, there's no limit + // constraint + PX_FORCE_INLINE bool contains(const PxVec3& swing) const + { + // padded current swing angles + PxReal swingYPadded = PxAbs(swing.y) + mPadding; + PxReal swingZPadded = PxAbs(swing.z) + mPadding; + // if angle is within ellipse defined by mYMax/mZMax + return Ps::sqr(swingYPadded/mYMax)+Ps::sqr(swingZPadded/mZMax) <= 1; + } + + PX_FORCE_INLINE PxVec3 clamp(const PxVec3& swing, PxVec3& normal) const + { + // finds the closest point on the ellipse to a given point + PxVec3 p = Ps::ellipseClamp(swing, PxVec3(0,mYMax,mZMax)); + // normal to the point on ellipse + normal = PxVec3(0, p.y/Ps::sqr(mYMax), p.z/Ps::sqr(mZMax)); +#ifdef PX_PARANOIA_ELLIPSE_CHECK + PxReal err = PxAbs(Ps::sqr(p.y/mYMax) + Ps::sqr(p.z/mZMax) - 1); + PX_ASSERT(err<1e-3); +#endif + return p; + } + + // input is a swing quat, such that swing.x = twist.y = twist.z = 0, q = swing * twist + // The routine is agnostic to the sign of q.w (i.e. we don't need the minimal-rotation swing) + + // output is an axis such that positive rotation increases the angle outward from the + // limit (i.e. the image of the x axis), the error is the sine of the angular difference, + // positive if the twist axis is inside the cone + + bool getLimit(const PxQuat& swing, PxVec3& axis, PxReal& error) const + { + PX_ASSERT(swing.w>0); + PxVec3 twistAxis = swing.getBasisVector0(); + // get the angles from the swing quaternion + PxVec3 swingAngle(0.0f, 4 * PxAtan2(swing.y, 1 + swing.w), 4 * PxAtan2(swing.z, 1 + swing.w)); + if(contains(swingAngle)) + return false; + + PxVec3 normal, clamped = clamp(swingAngle, normal); + + // rotation vector and ellipse normal + PxVec3 r(0,PxTan(clamped.y/4),PxTan(clamped.z/4)), d(0, normal.y, normal.z); + + error = computeAxisAndError(r, d, twistAxis, axis); + + PX_ASSERT(PxAbs(axis.magnitude()-1)<1e-5f); + + return true; + } + + private: + PxReal mYMax, mZMax, mPadding; + }; + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmFlushPool.h b/src/PhysX/physx/source/common/src/CmFlushPool.h new file mode 100644 index 000000000..e9e9ebe47 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmFlushPool.h @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_FLUSHPOOL +#define PX_PHYSICS_COMMON_FLUSHPOOL + +#include "foundation/Px.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsMutex.h" +#include "PsArray.h" +#include "PsBitUtils.h" + +/* +Pool used to allocate variable sized tasks. It's intended to be cleared after a short period (time step). +*/ + +namespace physx +{ +namespace Cm +{ + static const PxU32 sSpareChunkCount = 2; + + class FlushPool + { + PX_NOCOPY(FlushPool) + public: + FlushPool(PxU32 chunkSize) : mChunks(PX_DEBUG_EXP("FlushPoolChunk")), mChunkIndex(0), mOffset(0), mChunkSize(chunkSize) + { + mChunks.pushBack(static_cast(PX_ALLOC(mChunkSize, "PxU8"))); + } + + ~FlushPool() + { + for (PxU32 i = 0; i < mChunks.size(); ++i) + PX_FREE(mChunks[i]); + } + + // alignment must be a power of two + void* allocate(PxU32 size, PxU32 alignment=16) + { + Ps::Mutex::ScopedLock lock(mMutex); + return allocateNotThreadSafe(size, alignment); + } + + // alignment must be a power of two + void* allocateNotThreadSafe(PxU32 size, PxU32 alignment=16) + { + PX_ASSERT(shdfnd::isPowerOfTwo(alignment)); + PX_ASSERT(size <= mChunkSize && !mChunks.empty()); + + // padding for alignment + size_t unalignedStart = reinterpret_cast(mChunks[mChunkIndex]+mOffset); + PxU32 pad = PxU32(((unalignedStart+alignment-1)&~(size_t(alignment)-1)) - unalignedStart); + + if (mOffset + size + pad > mChunkSize) + { + mChunkIndex++; + mOffset = 0; + if (mChunkIndex >= mChunks.size()) + mChunks.pushBack(static_cast(PX_ALLOC(mChunkSize, "PxU8"))); + + // update padding to ensure new alloc is aligned + unalignedStart = reinterpret_cast(mChunks[mChunkIndex]); + pad = PxU32(((unalignedStart+alignment-1)&~(size_t(alignment)-1)) - unalignedStart); + } + + void* ptr = mChunks[mChunkIndex] + mOffset + pad; + PX_ASSERT((reinterpret_cast(ptr)&(size_t(alignment)-1)) == 0); + mOffset += size + pad; + return ptr; + } + + void clear(PxU32 spareChunkCount = sSpareChunkCount) + { + Ps::Mutex::ScopedLock lock(mMutex); + + clearNotThreadSafe(spareChunkCount); + } + + void clearNotThreadSafe(PxU32 spareChunkCount = sSpareChunkCount) + { + PX_UNUSED(spareChunkCount); + + //release memory not used previously + PxU32 targetSize = mChunkIndex+sSpareChunkCount; + while (mChunks.size() > targetSize) + PX_FREE(mChunks.popBack()); + + mChunkIndex = 0; + mOffset = 0; + } + + void resetNotThreadSafe() + { + PxU8* firstChunk = mChunks[0]; + + for (PxU32 i = 1; i < mChunks.size(); ++i) + PX_FREE(mChunks[i]); + + mChunks.clear(); + mChunks.pushBack(firstChunk); + mChunkIndex = 0; + mOffset = 0; + } + + void lock() + { + mMutex.lock(); + } + + void unlock() + { + mMutex.unlock(); + } + + private: + Ps::Mutex mMutex; + Ps::Array mChunks; + PxU32 mChunkIndex; + PxU32 mOffset; + PxU32 mChunkSize; + }; + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmIDPool.h b/src/PhysX/physx/source/common/src/CmIDPool.h new file mode 100644 index 000000000..ea848face --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmIDPool.h @@ -0,0 +1,207 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_ID_POOL +#define PX_PHYSICS_COMMON_ID_POOL + +#include "foundation/Px.h" +#include "CmPhysXCommon.h" +#include "PsArray.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Cm +{ + template + class IDPoolBase : public Ps::UserAllocated + { + protected: + PxU32 mCurrentID; + FreeBuffer mFreeIDs; + public: + IDPoolBase() : mCurrentID(0) {} + + void freeID(PxU32 id) + { + // Allocate on first call + // Add released ID to the array of free IDs + if(id == (mCurrentID - 1)) + --mCurrentID; + else + mFreeIDs.pushBack(id); + } + + void freeAll() + { + mCurrentID = 0; + mFreeIDs.clear(); + } + + PxU32 getNewID() + { + // If recycled IDs are available, use them + const PxU32 size = mFreeIDs.size(); + if(size) + { + // Recycle last ID + return mFreeIDs.popBack(); + } + // Else create a new ID + return mCurrentID++; + } + + PxU32 getNumUsedID() const + { + return mCurrentID - mFreeIDs.size(); + } + + PxU32 getMaxID() const + { + return mCurrentID; + } + + }; + + //This class extends IDPoolBase. This is mainly used for when it is unsafe for the application to free the id immediately so that it can + //defer the free process until it is safe to do so + template + class DeferredIDPoolBase : public IDPoolBase + { + FreeBuffer mDeferredFreeIDs; + public: + //release an index into the deferred list + void deferredFreeID(PxU32 id) + { + mDeferredFreeIDs.pushBack(id); + } + + //release the deferred indices into the free list + void processDeferredIds() + { + const PxU32 deferredFreeIDCount = mDeferredFreeIDs.size(); + for(PxU32 a = 0; a < deferredFreeIDCount;++a) + { + IDPoolBase::freeID(mDeferredFreeIDs[a]); + } + mDeferredFreeIDs.clear(); + } + + //release all indices + void freeAll() + { + mDeferredFreeIDs.clear(); + IDPoolBase::freeAll(); + } + + PxU32 getNumUsedID() const + { + return IDPoolBase::getNumUsedID() - mDeferredFreeIDs.size(); + } + + FreeBuffer& getDeferredFreeIDs() { return mDeferredFreeIDs; } + }; + + //This is spu friendly fixed size array + template + class InlineFixedArray + { + T mArr[N]; + PxU32 mSize; + public: + + InlineFixedArray() : mSize(0) + { + } + + ~InlineFixedArray(){} + + void pushBack(const T& t) + { + PX_ASSERT(mSize < N); + mArr[mSize++] = t; + } + + T popBack() + { + PX_ASSERT(mSize > 0); + return mArr[--mSize]; + } + + void clear() { mSize = 0; } + + T& operator [] (PxU32 index) { PX_ASSERT(index < N); return mArr[index]; } + + const T& operator [] (PxU32 index) const { PX_ASSERT(index < N); return mArr[index]; } + + PxU32 size() const { return mSize; } + }; + + //Fix size IDPool + template + class InlineIDPool : public IDPoolBase > + { + public: + PxU32 getNumRemainingIDs() + { + return Capacity - this->getNumUsedID(); + } + }; + + //Dynamic resize IDPool + class IDPool : public IDPoolBase > + { + }; + + + //This class is used to recycle indices. It supports deferred release, so that until processDeferredIds is called, + //released indices will not be reallocated. This class will fail if the calling code request more id than the InlineDeferredIDPoll + //has. It is the calling code's responsibility to ensure that this does not happen. + template + class InlineDeferredIDPool : public DeferredIDPoolBase > + { + public: + PxU32 getNumRemainingIDs() + { + return Capacity - IDPoolBase< InlineFixedArray >::getNumUsedID(); + } + }; + + //Dynamic resize DeferredIDPool + class DeferredIDPool : public DeferredIDPoolBase > + { + + }; + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmIO.h b/src/PhysX/physx/source/common/src/CmIO.h new file mode 100644 index 000000000..5d290a0fb --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmIO.h @@ -0,0 +1,136 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_IO +#define PX_PHYSICS_COMMON_IO + +#include "foundation/PxIO.h" +#include "foundation/PxAssert.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + + // wrappers for IO classes so that we can add extra functionality (byte counting, buffering etc) + +namespace Cm +{ + +class InputStreamReader +{ +public: + + InputStreamReader(PxInputStream& stream) : mStream(stream) { } + PxU32 read(void* dest, PxU32 count) + { + PxU32 readLength = mStream.read(dest, count); + + // zero the buffer if we didn't get all the data + if(readLength(dest)+readLength, count-readLength); + + return readLength; + } + + template T get() + { + T val; + PxU32 length = mStream.read(&val, sizeof(T)); + PX_ASSERT(length == sizeof(T)); + PX_UNUSED(length); + return val; + } + + +protected: + PxInputStream &mStream; +private: + InputStreamReader& operator=(const InputStreamReader&); +}; + + +class InputDataReader : public InputStreamReader +{ +public: + InputDataReader(PxInputData& data) : InputStreamReader(data) {} + InputDataReader &operator=(const InputDataReader &); + + PxU32 length() const { return getData().getLength(); } + void seek(PxU32 offset) { getData().seek(offset); } + PxU32 tell() { return getData().tell(); } + +private: + PxInputData& getData() { return static_cast(mStream); } + const PxInputData& getData() const { return static_cast(mStream); } +}; + + +class OutputStreamWriter +{ +public: + + PX_INLINE OutputStreamWriter(PxOutputStream& stream) + : mStream(stream) + , mCount(0) + {} + + PX_INLINE PxU32 write(const void* src, PxU32 offset) + { + PxU32 count = mStream.write(src, offset); + mCount += count; + return count; + } + + PX_INLINE PxU32 getStoredSize() + { + return mCount; + } + + template void put(const T& val) + { + PxU32 length = write(&val, sizeof(T)); + PX_ASSERT(length == sizeof(T)); + PX_UNUSED(length); + } + +private: + + OutputStreamWriter& operator=(const OutputStreamWriter&); + PxOutputStream& mStream; + PxU32 mCount; +}; + + + +} +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmMathUtils.cpp b/src/PhysX/physx/source/common/src/CmMathUtils.cpp new file mode 100644 index 000000000..70c71326e --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmMathUtils.cpp @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMathUtils.h" +#include "common/PxPhysXCommonConfig.h" + +namespace physx +{ + + +PX_PHYSX_COMMON_API PxTransform PxTransformFromPlaneEquation(const PxPlane& plane) +{ + PxPlane p = plane; + p.normalize(); + + // special case handling for axis aligned planes + const PxReal halfsqrt2 = 0.707106781f; + PxQuat q; + if(2 == (p.n.x == 0.0f) + (p.n.y == 0.0f) + (p.n.z == 0.0f)) // special handling for axis aligned planes + { + if(p.n.x > 0) q = PxQuat(PxIdentity); + else if(p.n.x < 0) q = PxQuat(0, 0, 1.0f, 0); + else q = PxQuat(0.0f, -p.n.z, p.n.y, 1.0f) * halfsqrt2; + } + else q = PxShortestRotation(PxVec3(1.f,0,0), p.n); + + return PxTransform(-p.n * p.d, q); + +} + +PX_PHYSX_COMMON_API PxTransform PxTransformFromSegment(const PxVec3& p0, const PxVec3& p1, PxReal* halfHeight) +{ + const PxVec3 axis = p1-p0; + const PxReal height = axis.magnitude(); + if(halfHeight) + *halfHeight = height/2; + + return PxTransform((p1+p0) * 0.5f, + height<1e-6f ? PxQuat(PxIdentity) : PxShortestRotation(PxVec3(1.f,0,0), axis/height)); +} + +} diff --git a/src/PhysX/physx/source/common/src/CmMatrix34.h b/src/PhysX/physx/source/common/src/CmMatrix34.h new file mode 100644 index 000000000..01d8f34ea --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmMatrix34.h @@ -0,0 +1,286 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_MATRIX34 +#define PX_PHYSICS_COMMON_MATRIX34 + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Cm +{ + +/*! +Basic mathematical 3x4 matrix, implemented as a 3x3 rotation matrix and a translation + +See PxMat33 for the format of the rotation matrix. + +*/ + +class Matrix34 +{ +public: + //! Default constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34() + {} + + //! Construct from four base vectors + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const PxVec3& b0, const PxVec3& b1, const PxVec3& b2, const PxVec3& b3) + : m(b0, b1, b2), p(b3) + {} + + //! Construct from float[12] + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(PxReal values[]): + m(values), p(values[9], values[10], values[11]) + { + } + + //! Construct from a 3x3 matrix + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const PxMat33& other) + : m(other), p(PxZero) + { + } + + //! Construct from a 3x3 matrix and a translation vector + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const PxMat33& other, const PxVec3& t) + : m(other), p(t) + {} + + //! Construct from a PxTransform + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const PxTransform& other): + m(other.q), p(other.p) + { + } + + //! Construct from a quaternion + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const PxQuat& q): + m(q), p(PxZero) + { + } + + //! Copy constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34(const Matrix34& other): + m(other.m), p(other.p) + { + } + + //! Assignment operator + PX_CUDA_CALLABLE PX_FORCE_INLINE const Matrix34& operator=(const Matrix34& other) + { + m = other.m; + p = other.p; + return *this; + } + + //! Set to identity matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE void setIdentity() + { + m = PxMat33(PxIdentity); + p = PxVec3(0); + } + + // Simpler operators + //! Equality operator + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator==(const Matrix34& other) const + { + return m == other.m && p == other.p; + } + + //! Inequality operator + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator!=(const Matrix34& other) const + { + return !operator==(other); + } + + //! Unary minus + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator-() const + { + return Matrix34(-m, -p); + } + + //! Add + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator+(const Matrix34& other) const + { + return Matrix34(m + other.m, p + other.p); + } + + //! Subtract + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator-(const Matrix34& other) const + { + return Matrix34(m - other.m, p - other.p); + } + + //! Scalar multiplication + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator*(PxReal scalar) const + { + return Matrix34(m*scalar, p*scalar); + } + + friend Matrix34 operator*(PxReal, const Matrix34&); + + //! Matrix multiplication + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator*(const Matrix34& other) const + { + //Rows from this columns from other + //base0 = rotate(other.m.column0) etc + return Matrix34(m*other.m, m*other.p + p); + } + + //! Matrix multiplication, extend the second matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 operator*(const PxMat33& other) const + { + //Rows from this columns from other + //base0 = transform(other.m.column0) etc + return Matrix34(m*other, p); + } + + friend Matrix34 operator*(const PxMat33& a, const Matrix34& b); + + // a = b operators + + //! Equals-add + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34& operator+=(const Matrix34& other) + { + m += other.m; + p += other.p; + return *this; + } + + //! Equals-sub + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34& operator-=(const Matrix34& other) + { + m -= other.m; + p -= other.p; + return *this; + } + + //! Equals scalar multiplication + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34& operator*=(PxReal scalar) + { + m *= scalar; + p *= scalar; + + return *this; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal operator()(PxU32 row, PxU32 col) const + { + return (*this)[col][row]; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal& operator()(PxU32 row, PxU32 col) + { + return (*this)[col][row]; + } + + // Transform etc + + //! Transform vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 rotate(const PxVec3& other) const + { + return m*other; + } + + //! Transform vector by transpose of matrix, equal to v' = M^t*v + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 rotateTranspose(const PxVec3& other) const + { + return m.transformTranspose(other); + } + + //! Transform point by matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 transform(const PxVec3& other) const + { + return m*other + p; + } + + //! Transform point by transposed matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 transformTranspose(const PxVec3& other) const + { + return m.transformTranspose(other - p); + } + + //! Transform point by transposed matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::Matrix34 transformTranspose(const Cm::Matrix34& other) const + { + return Cm::Matrix34(m.transformTranspose(other.m.column0), + m.transformTranspose(other.m.column1), + m.transformTranspose(other.m.column2), + m.transformTranspose(other.p - p)); + } + + + //! Invert matrix treating it as a rotation+translation matrix only + PX_CUDA_CALLABLE PX_FORCE_INLINE Matrix34 getInverseRT() const + { + return Matrix34(m.getTranspose(), m.transformTranspose(-p)); + } + + + // Conversion + //! Set matrix from quaternion + PX_CUDA_CALLABLE PX_FORCE_INLINE void set(const PxQuat& q) + { + m = PxMat33(q); + p = PxVec3(PxZero); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator[](unsigned int num){return (&m.column0)[num];} + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator[](int num) { return (&m.column0)[num]; } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& operator[](unsigned int num) const { return (&m.column0)[num]; } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& operator[](int num) const { return (&m.column0)[num]; } + + //Data, see above for format! + + PxMat33 m; + PxVec3 p; + +}; + + +//! Multiply a*b, a is extended +PX_INLINE Matrix34 operator*(const PxMat33& a, const Matrix34& b) +{ + return Matrix34(a * b.m, a * b.p); +} + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmPhysXCommon.h b/src/PhysX/physx/source/common/src/CmPhysXCommon.h new file mode 100644 index 000000000..6ff0387c6 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPhysXCommon.h @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON +#define PX_PHYSICS_COMMON + +//! \file Top level internal include file for PhysX SDK + +#include "Ps.h" + +// Enable debug visualization +#define PX_ENABLE_DEBUG_VISUALIZATION 1 + +// Enable simulation statistics generation +#define PX_ENABLE_SIM_STATS 1 + +// PT: typical "invalid" value in various CD algorithms +#define PX_INVALID_U32 0xffffffff +#define PX_INVALID_U16 0xffff + +// PT: this used to be replicated everywhere in the code, causing bugs to sometimes reappear (e.g. TTP 3587). +// It is better to define it in a header and use the same constant everywhere. The original value (1e-05f) +// caused troubles (e.g. TTP 1705, TTP 306). +#define PX_PARALLEL_TOLERANCE 1e-02f + +namespace physx +{ + // alias shared foundation to something usable + namespace Ps = shdfnd; +} + +#if PX_CHECKED + #define PX_CHECK_MSG(exp, msg) (!!(exp) || (physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, msg), 0) ) + #define PX_CHECK(exp) PX_CHECK_MSG(exp, #exp) + #define PX_CHECK_AND_RETURN(exp,msg) { if(!(exp)) { PX_CHECK_MSG(exp, msg); return; } } + #define PX_CHECK_AND_RETURN_NULL(exp,msg) { if(!(exp)) { PX_CHECK_MSG(exp, msg); return 0; } } + #define PX_CHECK_AND_RETURN_VAL(exp,msg,r) { if(!(exp)) { PX_CHECK_MSG(exp, msg); return r; } } +#else + #define PX_CHECK_MSG(exp, msg) + #define PX_CHECK(exp) + #define PX_CHECK_AND_RETURN(exp,msg) + #define PX_CHECK_AND_RETURN_NULL(exp,msg) + #define PX_CHECK_AND_RETURN_VAL(exp,msg,r) +#endif + +#if PX_VC + // VC compiler defines __FUNCTION__ as a string literal so it is possible to concatenate it with another string + // Example: #define PX_CHECK_VALID(x) PX_CHECK_MSG(physx::shdfnd::checkValid(x), __FUNCTION__ ": parameter invalid!") + #define PX_CHECK_VALID(x) PX_CHECK_MSG(physx::shdfnd::checkValid(x), __FUNCTION__) +#elif PX_GCC_FAMILY + // GCC compiler defines __FUNCTION__ as a variable, hence, it is NOT possible concatenate an additional string to it + // In GCC, __FUNCTION__ only returns the function name, using __PRETTY_FUNCTION__ will return the full function definition + #define PX_CHECK_VALID(x) PX_CHECK_MSG(physx::shdfnd::checkValid(x), __PRETTY_FUNCTION__) +#else + // Generic macro for other compilers + #define PX_CHECK_VALID(x) PX_CHECK_MSG(physx::shdfnd::checkValid(x), __FUNCTION__) +#endif + + +#endif diff --git a/src/PhysX/physx/source/common/src/CmPool.h b/src/PhysX/physx/source/common/src/CmPool.h new file mode 100644 index 000000000..a28ea0307 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPool.h @@ -0,0 +1,292 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef CM_POOL_H +#define CM_POOL_H + +#include "PsSort.h" +#include "PsMutex.h" +#include "PsBasicTemplates.h" + +#include "CmBitMap.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Cm +{ + +/*! +Allocator for pools of data structures +Also decodes indices (which can be computed from handles) into objects. To make this +faster, the EltsPerSlab must be a power of two +*/ +template +class PoolList : public Ps::AllocatorTraits::Type +{ + typedef typename Ps::AllocatorTraits::Type Alloc; + PX_NOCOPY(PoolList) +public: + PX_INLINE PoolList(const Alloc& alloc, ArgumentType* argument, PxU32 eltsPerSlab, PxU32 maxSlabs) + : Alloc(alloc), + mEltsPerSlab(eltsPerSlab), + mMaxSlabs(maxSlabs), + mSlabCount(0), + mFreeList(0), + mFreeCount(0), + mSlabs(reinterpret_cast(Alloc::allocate(maxSlabs * sizeof(T*), __FILE__, __LINE__))), + mArgument(argument) + { + PX_ASSERT(mEltsPerSlab>0); + // either maxSlabs = 1 (non-resizable pool), or elts per slab must be a power of two + PX_ASSERT((maxSlabs==1) || ((maxSlabs < 8192) && (mEltsPerSlab & (mEltsPerSlab-1))) == 0); + mLog2EltsPerSlab = 0; + + if(mMaxSlabs>1) + { + for(mLog2EltsPerSlab=0; mEltsPerSlab!=PxU32(1< mFreeCount ? nbRequired - mFreeCount : 0; + + PxU32 nbElements = nbRequired - nbToAllocate; + + PxMemCopy(elements, mFreeList + (mFreeCount - nbElements), sizeof(T*) * nbElements); + //PxU32 originalFreeCount = mFreeCount; + mFreeCount -= nbElements; + + if (nbToAllocate) + { + PX_ASSERT(mFreeCount == 0); + + PxU32 nbSlabs = (nbToAllocate + mEltsPerSlab - 1) / mEltsPerSlab; //The number of slabs we need to allocate... + + if (mSlabCount + nbSlabs >= mMaxSlabs) + return nbElements; //Return only nbFromFree because we're not going to allocate any slabs. Seriously, we need to nuke this "maxSlabs" stuff ASAP! + + //allocate our slabs... + + PxU32 freeCount = mFreeCount; + + for (PxU32 i = 0; i < nbSlabs; ++i) + { + + //KS - would be great to allocate this using a single allocation but it will make releasing slabs fail later :( + T * mAddr = reinterpret_cast(Alloc::allocate(mEltsPerSlab * sizeof(T), __FILE__, __LINE__)); + if (!mAddr) + return nbElements; //Allocation failed so only return the set of elements we could allocate from the free list + + mSlabs[mSlabCount++] = mAddr; + + // Make sure the usage bitmap is up-to-size + if (mUseBitmap.size() < mSlabCount*mEltsPerSlab) + { + mUseBitmap.resize(2 * mSlabCount*mEltsPerSlab); //set last element as not used + if (mFreeList) + PX_FREE(mFreeList); + mFreeList = reinterpret_cast(Alloc::allocate(2 * mSlabCount * mEltsPerSlab * sizeof(T*), __FILE__, __LINE__)); + } + + PxU32 baseIndex = (mSlabCount-1) * mEltsPerSlab; + + //Now add all these to the mFreeList and elements... + PxI32 idx = PxI32(mEltsPerSlab - 1); + + for (; idx >= PxI32(nbToAllocate); --idx) + { + mFreeList[freeCount++] = new(mAddr + idx) T(mArgument, baseIndex + idx); + } + + PxU32 origElements = nbElements; + T** writeIdx = elements + nbElements; + for (; idx >= 0; --idx) + { + writeIdx[idx] = new(mAddr + idx) T(mArgument, baseIndex + idx); + nbElements++; + } + + nbToAllocate -= (nbElements - origElements); + } + + mFreeCount = freeCount; + } + + PX_ASSERT(nbElements == nbRequired); + + for (PxU32 a = 0; a < nbElements; ++a) + { + mUseBitmap.set(elements[a]->getIndex()); + } + + return nbRequired; + } + + // TODO: would be nice to add templated construct/destroy methods like ObjectPool + + PX_INLINE T* get() + { + if(mFreeCount == 0 && !extend()) + return 0; + T* element = mFreeList[--mFreeCount]; + mUseBitmap.set(element->getIndex()); + return element; + } + + PX_INLINE void put(T* element) + { + PxU32 i = element->getIndex(); + mUseBitmap.reset(i); + mFreeList[mFreeCount++] = element; + } + + /* + WARNING: Unlike findByIndexFast below, this method is NOT safe to use if another thread + is concurrently updating the pool (e.g. through put/get/extend/getIterator), since the + safety boundedTest uses mSlabCount and mUseBitmap. + */ + PX_FORCE_INLINE T* findByIndex(PxU32 index) const + { + if(index>=mSlabCount*mEltsPerSlab || !(mUseBitmap.boundedTest(index))) + return 0; + return mMaxSlabs==1 ? mSlabs[0]+index : mSlabs[index>>mLog2EltsPerSlab] + (index&(mEltsPerSlab-1)); + } + + /* + This call is safe to do while other threads update the pool. + */ + PX_FORCE_INLINE T* findByIndexFast(PxU32 index) const + { + PX_ASSERT(mMaxSlabs != 1); + return mSlabs[index>>mLog2EltsPerSlab] + (index&(mEltsPerSlab-1)); + } + + bool extend() + { + if(mSlabCount == mMaxSlabs) + return false; + T * mAddr = reinterpret_cast(Alloc::allocate(mEltsPerSlab * sizeof(T), __FILE__, __LINE__)); + if(!mAddr) + return false; + mSlabs[mSlabCount++] = mAddr; + + + + // Make sure the usage bitmap is up-to-size + if(mUseBitmap.size() < mSlabCount*mEltsPerSlab) + { + mUseBitmap.resize(2*mSlabCount*mEltsPerSlab); //set last element as not used + if(mFreeList) + PX_FREE(mFreeList); + mFreeList = reinterpret_cast(Alloc::allocate(2*mSlabCount * mEltsPerSlab * sizeof(T*), __FILE__, __LINE__)); + } + + // Add to free list in descending order so that lowest indices get allocated first - + // the FW context code currently *relies* on this behavior to grab the zero-index volume + // which can't be allocated to the user. TODO: fix this + + PxU32 baseIndex = (mSlabCount-1) * mEltsPerSlab; + PxU32 freeCount = mFreeCount; + for(PxI32 i=PxI32(mEltsPerSlab-1);i>=0;i--) + mFreeList[freeCount++] = new(mAddr+i) T(mArgument, baseIndex+ i); + + mFreeCount = freeCount; + + return true; + } + + PX_INLINE PxU32 getMaxUsedIndex() const + { + return mUseBitmap.findLast(); + } + + PX_INLINE BitMap::Iterator getIterator() const + { + return BitMap::Iterator(mUseBitmap); + } + +private: + const PxU32 mEltsPerSlab; + const PxU32 mMaxSlabs; + PxU32 mSlabCount; + PxU32 mLog2EltsPerSlab; + T** mFreeList; + PxU32 mFreeCount; + T** mSlabs; + ArgumentType* mArgument; + BitMap mUseBitmap; +}; + + +} +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmPreallocatingPool.h b/src/PhysX/physx/source/common/src/CmPreallocatingPool.h new file mode 100644 index 000000000..e3b9ece83 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPreallocatingPool.h @@ -0,0 +1,435 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_PREALLOCATINGPOOL +#define PX_PHYSICS_COMMON_PREALLOCATINGPOOL + +#include "foundation/Px.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsSort.h" +#include "PsArray.h" + +/* +Pool used to allocate variable sized tasks. It's intended to be cleared after a short period (time step). +*/ + +namespace physx +{ +namespace Cm +{ + +class PreallocatingRegion +{ +public: + PX_FORCE_INLINE PreallocatingRegion() : mMemory(NULL), mFirstFree(NULL), mNbElements(0) {} + + void init(PxU32 maxElements, PxU32 elementSize, const char* typeName) + { + mFirstFree = NULL; + mNbElements = 0; + PX_ASSERT(typeName); + PX_UNUSED(typeName); + mMemory = reinterpret_cast(PX_ALLOC(sizeof(PxU8)*elementSize*maxElements, typeName?typeName:"SceneSim Pool")); // ### addActor alloc + PX_ASSERT(elementSize*maxElements>=sizeof(void*)); + } + + void reset() + { + PX_FREE_AND_RESET(mMemory); + } + + PX_FORCE_INLINE PxU8* allocateMemory(PxU32 maxElements, PxU32 elementSize) + { + if(mFirstFree) + { + PxU8* recycled = reinterpret_cast(mFirstFree); + + void** recycled32 = reinterpret_cast(recycled); + mFirstFree = *recycled32; + + return recycled; + } + else + { + if(mNbElements==maxElements) + return NULL; // Out of memory + + const PxU32 freeIndex = mNbElements++; + return mMemory + freeIndex * elementSize; + } + } + + void deallocateMemory(PxU32 maxElements, PxU32 elementSize, PxU8* element) + { + PX_ASSERT(element); + PX_ASSERT(element>=mMemory && element(element); + *recycled32 = mFirstFree; + + mFirstFree = element; + } + + PX_FORCE_INLINE bool operator < (const PreallocatingRegion& p) const + { + return mMemory < p.mMemory; + } + + PX_FORCE_INLINE bool operator > (const PreallocatingRegion& p) const + { + return mMemory > p.mMemory; + } + + PxU8* mMemory; + void* mFirstFree; + PxU32 mNbElements; +}; + + + +class PreallocatingRegionManager +{ + public: + PreallocatingRegionManager(PxU32 maxElements, PxU32 elementSize, const char* typeName) + : mMaxElements (maxElements) + , mElementSize (elementSize) + , mActivePoolIndex (0) + , mPools(PX_DEBUG_EXP("MyPoolManagerPools")) + , mNeedsSorting (true) + , mTypeName (typeName) + { + PreallocatingRegion tmp; + tmp.init(maxElements, elementSize, mTypeName); + mPools.pushBack(tmp); + } + + ~PreallocatingRegionManager() + { + const PxU32 nbPools = mPools.size(); + for(PxU32 i=0;iavailableSpace) + { + PreallocatingRegion tmp; + tmp.init(maxElements, elementSize, mTypeName); + mPools.pushBack(tmp); + + availableSpace += maxElements; + } + } + + PX_FORCE_INLINE PxU8* allocateMemory() + { + PX_ASSERT(mActivePoolIndex>1; + + PreallocatingRegion& candidate = mPools[PxU32(mid)]; + if(contains(candidate.mMemory, slabSize, element)) + { + candidate.deallocateMemory(maxElements, elementSize, element); + + // when we sorted earlier we trashed the active index, but at least this region has a free element + if(mNeedsSorting) + mActivePoolIndex = PxU32(mid); + + mNeedsSorting = false; + return; + } + + if(candidate.mMemory=memory && element mPools; + bool mNeedsSorting; + const char* mTypeName; +}; + +template +class PreallocatingPool : public Ps::UserAllocated +{ + PreallocatingPool& operator=(const PreallocatingPool&); + +public: + PreallocatingPool(PxU32 maxElements, const char* typeName) : mPool(maxElements, sizeof(T), typeName) + { + } + + ~PreallocatingPool() + { + } + + PX_FORCE_INLINE void preAllocate(PxU32 n) + { + mPool.preAllocate(n); + } + + PX_INLINE T* allocate() + { + return reinterpret_cast(mPool.allocateMemory()); + } + + PX_FORCE_INLINE T* allocateAndPrefetch() + { + T* t = reinterpret_cast(mPool.allocateMemory()); + Ps::prefetch(t, sizeof(T)); + return t; + } + + PX_INLINE T* construct() + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T() : 0; + } + + template + PX_INLINE T* construct(A1& a) + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T(a) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b) + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T(a,b) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c) + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T(a,b,c) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c, A4& d) + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T(a,b,c,d) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c, A4& d, A5& e) + { + T* t = reinterpret_cast(mPool.allocateMemory()); + return t ? new (t) T(a,b,c,d,e) : 0; + } + + //// + + PX_INLINE T* construct(T* t) + { + PX_ASSERT(t); + return new (t) T(); + } + + template + PX_INLINE T* construct(T* t, A1& a) + { + PX_ASSERT(t); + return new (t) T(a); + } + + template + PX_INLINE T* construct(T* t, A1& a, A2& b) + { + PX_ASSERT(t); + return new (t) T(a,b); + } + + template + PX_INLINE T* construct(T* t, A1& a, A2& b, A3& c) + { + PX_ASSERT(t); + return new (t) T(a,b,c); + } + + template + PX_INLINE T* construct(T* t, A1& a, A2& b, A3& c, A4& d) + { + PX_ASSERT(t); + return new (t) T(a,b,c,d); + } + + template + PX_INLINE T* construct(T* t, A1& a, A2& b, A3& c, A4& d, A5& e) + { + PX_ASSERT(t); + return new (t) T(a,b,c,d,e); + } + + PX_INLINE void destroy(T* const p) + { + if(p) + { + p->~T(); + mPool.deallocateMemory(reinterpret_cast(p)); + } + } + + PX_INLINE void releasePreallocated(T* const p) + { + if(p) + mPool.deallocateMemory(reinterpret_cast(p)); + } +protected: + PreallocatingRegionManager mPool; +}; + +template +class BufferedPreallocatingPool : public PreallocatingPool +{ + Ps::Array mDeletedElems; + PX_NOCOPY(BufferedPreallocatingPool) +public: + BufferedPreallocatingPool(PxU32 maxElements, const char* typeName) : PreallocatingPool(maxElements, typeName) + { + } + + PX_INLINE void destroy(T* const p) + { + if (p) + { + p->~T(); + mDeletedElems.pushBack(p); + } + } + + void processPendingDeletedElems() + { + for (PxU32 i = 0; i < mDeletedElems.size(); ++i) + this->mPool.deallocateMemory(reinterpret_cast(mDeletedElems[i])); + mDeletedElems.clear(); + } + + +}; + + + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmPriorityQueue.h b/src/PhysX/physx/source/common/src/CmPriorityQueue.h new file mode 100644 index 000000000..a1eeef523 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPriorityQueue.h @@ -0,0 +1,237 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_PRIORITYQUEUE +#define PX_PHYSICS_COMMON_PRIORITYQUEUE + +#include "PsBasicTemplates.h" +#include "CmPhysXCommon.h" +#include "PsAllocator.h" +#include "foundation/PxMemory.h" + +namespace physx +{ +namespace Cm +{ + template > + class PriorityQueueBase : protected Comparator // inherit so that stateless comparators take no space + { + public: + PriorityQueueBase(const Comparator& less, Element* elements) : Comparator(less), mHeapSize(0), mDataPtr(elements) + { + } + + ~PriorityQueueBase() + { + } + + //! Get the element with the highest priority + PX_FORCE_INLINE const Element top() const + { + return mDataPtr[0]; + } + + //! Get the element with the highest priority + PX_FORCE_INLINE Element top() + { + return mDataPtr[0]; + } + + //! Check to whether the priority queue is empty + PX_FORCE_INLINE bool empty() const + { + return (mHeapSize == 0); + } + + //! Empty the priority queue + PX_FORCE_INLINE void clear() + { + mHeapSize = 0; + } + + //! Insert a new element into the priority queue. Only valid when size() is less than Capacity + PX_FORCE_INLINE void push(const Element& value) + { + PxU32 newIndex; + PxU32 parentIndex = parent(mHeapSize); + + for (newIndex = mHeapSize; newIndex > 0 && compare(value, mDataPtr[parentIndex]); newIndex = parentIndex, parentIndex= parent(newIndex)) + { + mDataPtr[ newIndex ] = mDataPtr[parentIndex]; + } + mDataPtr[newIndex] = value; + mHeapSize++; + PX_ASSERT(valid()); + } + + //! Delete the highest priority element. Only valid when non-empty. + PX_FORCE_INLINE Element pop() + { + PX_ASSERT(mHeapSize > 0); + PxU32 i, child; + //try to avoid LHS + PxU32 tempHs = mHeapSize-1; + mHeapSize = tempHs; + Element min = mDataPtr[0]; + Element last = mDataPtr[tempHs]; + + for (i = 0; (child = left(i)) < tempHs; i = child) + { + /* Find highest priority child */ + const PxU32 rightChild = child + 1; + + child += ((rightChild < tempHs) & compare((mDataPtr[rightChild]), (mDataPtr[child]))) ? 1 : 0; + + if(compare(last, mDataPtr[child])) + break; + + mDataPtr[i] = mDataPtr[child]; + } + mDataPtr[ i ] = last; + + PX_ASSERT(valid()); + return min; + } + + //! Make sure the priority queue sort all elements correctly + bool valid() const + { + const Element& min = mDataPtr[0]; + for(PxU32 i=1; i> 1; + } + private: + PriorityQueueBase& operator = (const PriorityQueueBase); + }; + + template + class InlinePriorityQueue : public PriorityQueueBase + { + Element mData[Capacity]; + public: + InlinePriorityQueue(const Comparator& less = Comparator()) : PriorityQueueBase(less, mData) + { + } + + PX_FORCE_INLINE void push(Element& elem) + { + PX_ASSERT(this->mHeapSize < Capacity); + PriorityQueueBase::push(elem); + } + private: + InlinePriorityQueue& operator = (const InlinePriorityQueue); + }; + + template ::Type> + class PriorityQueue : public PriorityQueueBase, protected Alloc + { + PxU32 mCapacity; + public: + PriorityQueue(const Comparator& less = Comparator(), PxU32 initialCapacity = 0, Alloc alloc = Alloc()) + : PriorityQueueBase(less, NULL), Alloc(alloc), mCapacity(initialCapacity) + { + if(initialCapacity > 0) + this->mDataPtr = reinterpret_cast(Alloc::allocate(sizeof(Element)*initialCapacity, __FILE__, __LINE__)); + } + + ~PriorityQueue() + { + if(this->mDataPtr) + this->deallocate(this->mDataPtr); + } + + PX_FORCE_INLINE void push(Element& elem) + { + if(this->mHeapSize == mCapacity) + { + reserve((this->mHeapSize+1)*2); + } + PriorityQueueBase::push(elem); + } + + PX_FORCE_INLINE PxU32 capacity() + { + return mCapacity; + } + + PX_FORCE_INLINE void reserve(const PxU32 newCapacity) + { + if(newCapacity > mCapacity) + { + Element* newElems = reinterpret_cast(Alloc::allocate(sizeof(Element)*newCapacity, __FILE__, __LINE__)); + if(this->mDataPtr) + { + physx::PxMemCopy(newElems, this->mDataPtr, sizeof(Element) * this->mHeapSize); + Alloc::deallocate(this->mDataPtr); + } + this->mDataPtr = newElems; + mCapacity = newCapacity; + } + } + + private: + PriorityQueue& operator = (const PriorityQueue); + }; + +} +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmPtrTable.cpp b/src/PhysX/physx/source/common/src/CmPtrTable.cpp new file mode 100644 index 000000000..2341f0a3b --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPtrTable.cpp @@ -0,0 +1,207 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" +#include "foundation/PxMemory.h" +#include "CmPtrTable.h" +#include "CmUtils.h" +#include "PxMetaData.h" +#include "PsBitUtils.h" + +using namespace physx; +using namespace Cm; + +PtrTable::PtrTable() +: mList(NULL) +, mCount(0) +, mOwnsMemory(true) +, mBufferUsed(false) +{ +} + +PtrTable::~PtrTable() +{ + PX_ASSERT(mOwnsMemory); + PX_ASSERT(mCount == 0); + PX_ASSERT(mList == NULL); +} + +void PtrTable::clear(PtrTableStorageManager& sm) +{ + if(mOwnsMemory && mCount>1) + { + PxU32 implicitCapacity = Ps::nextPowerOfTwo(PxU32(mCount)-1); + sm.deallocate(mList, sizeof(void*)*implicitCapacity); + } + + mList = NULL; + mOwnsMemory = true; + mCount = 0; +} + +PxU32 PtrTable::find(const void* ptr) const +{ + const PxU32 nbPtrs = mCount; + void*const * PX_RESTRICT ptrs = getPtrs(); + + for(PxU32 i=0; i1) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mList, sizeof(void*)*mCount); + } +} + +void PtrTable::importExtraData(PxDeserializationContext& context) +{ + if(mCount>1) + mList = context.readExtraData(mCount); +} + +void PtrTable::realloc(PxU32 oldCapacity, PxU32 newCapacity, PtrTableStorageManager& sm) +{ + PX_ASSERT((mOwnsMemory && oldCapacity) || (!mOwnsMemory && oldCapacity == 0)); + PX_ASSERT(newCapacity); + + if(mOwnsMemory && sm.canReuse(oldCapacity, newCapacity)) + return; + + void** newMem = sm.allocate(newCapacity * sizeof(void*)); + PxMemCopy(newMem, mList, mCount * sizeof(void*)); + + if(mOwnsMemory) + sm.deallocate(mList, oldCapacity*sizeof(void*)); + + mList = newMem; + mOwnsMemory = true; +} + +void PtrTable::add(void* ptr, PtrTableStorageManager& sm) +{ + if(mCount == 0) // 0 -> 1, easy case + { + PX_ASSERT(mOwnsMemory); + PX_ASSERT(mList == NULL); + PX_ASSERT(!mBufferUsed); + mSingle = ptr; + mCount = 1; + mBufferUsed = true; + return; + } + + if(mCount == 1) // 1 -> 2, easy case + { + PX_ASSERT(mOwnsMemory); + PX_ASSERT(mBufferUsed); + + void* single = mSingle; + mList = sm.allocate(2*sizeof(void*)); + mList[0] = single; + mBufferUsed = false; + mOwnsMemory = true; + } + else + { + PX_ASSERT(!mBufferUsed); + + if(!mOwnsMemory) // don't own the memory, must always alloc + realloc(0, Ps::nextPowerOfTwo(mCount), sm); // we're guaranteed nextPowerOfTwo(x) > x + + else if(Ps::isPowerOfTwo(mCount)) // count is at implicit capacity, so realloc + realloc(mCount, PxU32(mCount)*2, sm); // ... to next higher power of 2 + + PX_ASSERT(mOwnsMemory); + } + + mList[mCount++] = ptr; +} + +void PtrTable::replaceWithLast(PxU32 index, PtrTableStorageManager& sm) +{ + PX_ASSERT(mCount!=0); + + if(mCount == 1) // 1 -> 0 easy case + { + PX_ASSERT(mOwnsMemory); + PX_ASSERT(mBufferUsed); + + mList = NULL; + mCount = 0; + mBufferUsed = false; + } + else if(mCount == 2) // 2 -> 1 easy case + { + PX_ASSERT(!mBufferUsed); + void* ptr = mList[1-index]; + if(mOwnsMemory) + sm.deallocate(mList, 2*sizeof(void*)); + mSingle = ptr; + mCount = 1; + mBufferUsed = true; + mOwnsMemory = true; + } + else + { + PX_ASSERT(!mBufferUsed); + + mList[index] = mList[--mCount]; // remove before adjusting memory + + if(!mOwnsMemory) // don't own the memory, must alloc + realloc(0, Ps::nextPowerOfTwo(PxU32(mCount)-1), sm); // if currently a power of 2, don't jump to the next one + + else if(Ps::isPowerOfTwo(mCount)) // own the memory, and implicit capacity requires that we downsize + realloc(PxU32(mCount)*2, PxU32(mCount), sm); // ... from the next power of 2, which was the old implicit capacity + + PX_ASSERT(mOwnsMemory); + } +} + +void Cm::PtrTable::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PtrTable) + + PX_DEF_BIN_METADATA_ITEM(stream, PtrTable, void, mSingle, PxMetaDataFlag::ePTR) // PT: this is actually a union, beware + PX_DEF_BIN_METADATA_ITEM(stream, PtrTable, PxU16, mCount, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PtrTable, bool, mOwnsMemory, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PtrTable, bool, mBufferUsed, 0) + + //------ Extra-data ------ + + // mList + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PtrTable, void, mBufferUsed, mCount, PxMetaDataFlag::eCONTROL_FLIP|PxMetaDataFlag::ePTR, PX_SERIAL_ALIGN) +} diff --git a/src/PhysX/physx/source/common/src/CmPtrTable.h b/src/PhysX/physx/source/common/src/CmPtrTable.h new file mode 100644 index 000000000..cc5041d4d --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmPtrTable.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_PTR_TABLE +#define PX_PHYSICS_COMMON_PTR_TABLE + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +class PxSerializationContext; +class PxDeserializationContext; + + +namespace Cm +{ + +class PtrTableStorageManager +{ + // This will typically be backed by a MultiPool implementation with fallback to the user + // allocator. For MultiPool, when deallocating we want to know what the previously requested size was + // so we can release into the right pool + +public: + + // capacity is in bytes + + virtual void** allocate(PxU32 capacity) = 0; + virtual void deallocate(void** addr, PxU32 originalCapacity) = 0; + + // whether memory allocated at one capacity can (and should) be safely reused at a different capacity + // allows realloc-style reuse by clients. + + virtual bool canReuse(PxU32 originalCapacity, PxU32 newCapacity) = 0; +protected: + virtual ~PtrTableStorageManager() {} +}; + + + +// specialized class to hold an array of pointers with extrinsic storage management, +// serialization-compatible with 3.3.1 PtrTable +// +// note that extrinsic storage implies you *must* clear the table before the destructor runs +// +// capacity is implicit: +// if the memory is not owned (i.e. came from deserialization) then the capacity is exactly mCount +// else if mCount==0, capacity is 0 +// else the capacity is the power of 2 >= mCount +// +// one implication of this is that if we want to add or remove a pointer from unowned memory, we always realloc + +struct PX_PHYSX_COMMON_API PtrTable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PtrTable(); + ~PtrTable(); + + void add(void* ptr, PtrTableStorageManager& sm); + void replaceWithLast(PxU32 index, PtrTableStorageManager& sm); + void clear(PtrTableStorageManager& sm); + + PxU32 find(const void* ptr) const; + + PX_FORCE_INLINE PxU32 getCount() const { return mCount; } + PX_FORCE_INLINE void*const* getPtrs() const { return mCount == 1 ? &mSingle : mList; } + PX_FORCE_INLINE void** getPtrs() { return mCount == 1 ? &mSingle : mList; } + + + // SERIALIZATION + + // 3.3.1 compatibility fixup: this implementation ALWAYS sets 'ownsMemory' if the size is 0 or 1 + PtrTable(const PxEMPTY) + { + mOwnsMemory = mCount<2; + if(mCount == 0) + mList = NULL; + } + + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + + static void getBinaryMetaData(physx::PxOutputStream& stream); + +private: + void realloc(PxU32 oldCapacity, PxU32 newCapacity, PtrTableStorageManager& sm); + + union + { + void* mSingle; + void** mList; + }; + + PxU16 mCount; + bool mOwnsMemory; + bool mBufferUsed; // dark magic in serialization requires this, otherwise redundant because it's logically equivalent to mCount == 1. + +}; + +} // namespace Cm +#if !PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(sizeof(Cm::PtrTable)==8); +#endif + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmQueue.h b/src/PhysX/physx/source/common/src/CmQueue.h new file mode 100644 index 000000000..334a530b5 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmQueue.h @@ -0,0 +1,152 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_QUEUE +#define PX_PHYSICS_COMMON_QUEUE + +#include "foundation/PxAssert.h" +#include "PsAllocator.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Cm +{ + + template + class Queue: public Ps::UserAllocated + { + public: + Queue(PxU32 maxEntries); + ~Queue(); + + T popFront(); + T front(); + T popBack(); + T back(); + bool pushBack(const T& element); + bool empty() const; + PxU32 size() const; + + private: + T* mJobQueue; + PxU32 mNum; + PxU32 mHead; + PxU32 mTail; + PxU32 mMaxEntries; + AllocType mAllocator; + }; + + template + Queue::Queue(PxU32 maxEntries): + mNum(0), + mHead(0), + mTail(0), + mMaxEntries(maxEntries) + { + mJobQueue = reinterpret_cast(mAllocator.allocate(sizeof(T)*mMaxEntries, __FILE__, __LINE__)); + } + + template + Queue::~Queue() + { + if(mJobQueue) + mAllocator.deallocate(mJobQueue); + } + + template + T Queue::popFront() + { + PX_ASSERT(mNum>0); + + mNum--; + T& element = mJobQueue[mTail]; + mTail = (mTail+1) % (mMaxEntries); + return element; + } + + template + T Queue::front() + { + PX_ASSERT(mNum>0); + + return mJobQueue[mTail]; + } + + template + T Queue::popBack() + { + PX_ASSERT(mNum>0); + + mNum--; + mHead = (mHead-1) % (mMaxEntries); + return mJobQueue[mHead]; + } + + template + T Queue::back() + { + PX_ASSERT(mNum>0); + + PxU32 headAccess = (mHead-1) % (mMaxEntries); + return mJobQueue[headAccess]; + } + + template + bool Queue::pushBack(const T& element) + { + if (mNum == mMaxEntries) return false; + mJobQueue[mHead] = element; + + mNum++; + mHead = (mHead+1) % (mMaxEntries); + + return true; + } + + template + bool Queue::empty() const + { + return mNum == 0; + } + + template + PxU32 Queue::size() const + { + return mNum; + } + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmRadixSort.cpp b/src/PhysX/physx/source/common/src/CmRadixSort.cpp new file mode 100644 index 000000000..bb0adf40e --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmRadixSort.cpp @@ -0,0 +1,460 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "foundation/PxMemory.h" +#include "foundation/PxAssert.h" +#include "CmRadixSort.h" + +using namespace physx; +using namespace Cm; + +#if defined(__BIG_ENDIAN__) || defined(_XBOX) + #define H0_OFFSET 768 + #define H1_OFFSET 512 + #define H2_OFFSET 256 + #define H3_OFFSET 0 + #define BYTES_INC (3-j) +#else + #define H0_OFFSET 0 + #define H1_OFFSET 256 + #define H2_OFFSET 512 + #define H3_OFFSET 768 + #define BYTES_INC j +#endif + +#define CREATE_HISTOGRAMS(type, buffer) \ + /* Clear counters/histograms */ \ + PxMemZero(mHistogram1024, 256*4*sizeof(PxU32)); \ + \ + /* Prepare to count */ \ + const PxU8* PX_RESTRICT p = reinterpret_cast(input); \ + const PxU8* PX_RESTRICT pe = &p[nb*4]; \ + PxU32* PX_RESTRICT h0= &mHistogram1024[H0_OFFSET]; /* Histogram for first pass (LSB)*/ \ + PxU32* PX_RESTRICT h1= &mHistogram1024[H1_OFFSET]; /* Histogram for second pass */ \ + PxU32* PX_RESTRICT h2= &mHistogram1024[H2_OFFSET]; /* Histogram for third pass */ \ + PxU32* PX_RESTRICT h3= &mHistogram1024[H3_OFFSET]; /* Histogram for last pass (MSB)*/ \ + \ + bool AlreadySorted = true; /* Optimism... */ \ + \ + if(INVALID_RANKS) \ + { \ + /* Prepare for temporal coherence */ \ + const type* PX_RESTRICT Running = reinterpret_cast(buffer); \ + type PrevVal = *Running; \ + \ + while(p!=pe) \ + { \ + /* Read input buffer in previous sorted order */ \ + const type Val = *Running++; \ + /* Check whether already sorted or not */ \ + if(Val(input))+pass); + + // Check that byte's counter + if(CurCount[UniqueVal]==nb) + return NULL; + + return CurCount; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +RadixSort::RadixSort() : mCurrentSize(0), mRanks(NULL), mRanks2(NULL), mHistogram1024(0), mLinks256(0), mTotalCalls(0), mNbHits(0), mDeleteRanks(true) +{ + // Initialize indices + INVALIDATE_RANKS; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +RadixSort::~RadixSort() +{ +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main sort routine. + * This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data. + * \param input [in] a list of integer values to sort + * \param nb [in] number of values to sort, must be < 2^31 + * \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +RadixSort& RadixSort::Sort(const PxU32* input, PxU32 nb, RadixHint hint) +{ + PX_ASSERT(mHistogram1024); + PX_ASSERT(mLinks256); + PX_ASSERT(mRanks); + PX_ASSERT(mRanks2); + + // Checkings + if(!input || !nb || nb&0x80000000) return *this; + + // Stats + mTotalCalls++; + + // Create histograms (counters). Counters for all passes are created in one run. + // Pros: read input buffer once instead of four times + // Cons: mHistogram1024 is 4Kb instead of 1Kb + // We must take care of signed/unsigned values for temporal coherence.... I just + // have 2 code paths even if just a single opcode changes. Self-modifying code, someone? + if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(PxU32, input); } + else { CREATE_HISTOGRAMS(PxI32, input); } + + // Compute #negative values involved if needed + PxU32 NbNegativeValues = 0; + if(hint==RADIX_SIGNED) + { + // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128 + // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte, + // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers. + PxU32* PX_RESTRICT h3= &mHistogram1024[768]; + for(PxU32 i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part + } + + // Radix sort, j is the pass number (0=LSB, 3=MSB) + for(PxU32 j=0;j<4;j++) + { +// CHECK_PASS_VALIDITY(j); + PxU8 UniqueVal; + const PxU32* PX_RESTRICT CurCount = CheckPassValidity(j, mHistogram1024, nb, input, UniqueVal); + + // Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is + // not a problem, numbers are correctly sorted anyway. + if(CurCount) + { + PxU32** PX_RESTRICT Links256 = mLinks256; + + // Should we care about negative values? + if(j!=3 || hint==RADIX_UNSIGNED) + { + // Here we deal with positive values only + + // Create offsets + Links256[0] = mRanks2; + for(PxU32 i=1;i<256;i++) + Links256[i] = Links256[i-1] + CurCount[i-1]; + } + else + { + // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place. + + // Create biased offsets, in order for negative numbers to be sorted as well + Links256[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones + for(PxU32 i=1;i<128;i++) + Links256[i] = Links256[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + + // Fixing the wrong place for negative values + Links256[128] = mRanks2; + for(PxU32 i=129;i<256;i++) + Links256[i] = Links256[i-1] + CurCount[i-1]; + } + + // Perform Radix Sort + const PxU8* PX_RESTRICT InputBytes = reinterpret_cast(input); + InputBytes += BYTES_INC; + if(INVALID_RANKS) + { + for(PxU32 i=0;i(input2); + + // Allocate histograms & offsets on the stack + //PxU32 mHistogram1024[256*4]; + //PxU32* mLinks256[256]; + + // Create histograms (counters). Counters for all passes are created in one run. + // Pros: read input buffer once instead of four times + // Cons: mHistogram1024 is 4Kb instead of 1Kb + // Floating-point values are always supposed to be signed values, so there's only one code path there. + // Please note the floating point comparison needed for temporal coherence! Although the resulting asm code + // is dreadful, this is surprisingly not such a performance hit - well, I suppose that's a big one on first + // generation Pentiums....We can't make comparison on integer representations because, as Chris said, it just + // wouldn't work with mixed positive/negative values.... + { CREATE_HISTOGRAMS(float, input2); } + + // Compute #negative values involved if needed + PxU32 NbNegativeValues = 0; + // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128 + // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte, + // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers. + // ### is that ok on Apple ?! + PxU32* PX_RESTRICT h3= &mHistogram1024[768]; + for(PxU32 i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part + + // Radix sort, j is the pass number (0=LSB, 3=MSB) + for(PxU32 j=0;j<4;j++) + { + PxU8 UniqueVal; + const PxU32* PX_RESTRICT CurCount = CheckPassValidity(j, mHistogram1024, nb, input, UniqueVal); + // Should we care about negative values? + if(j!=3) + { + // Here we deal with positive values only +// CHECK_PASS_VALIDITY(j); +// const bool PerformPass = CheckPassValidity(j, mHistogram1024, nb, input); + + if(CurCount) + { + PxU32** PX_RESTRICT Links256 = mLinks256; + + // Create offsets + Links256[0] = mRanks2; + for(PxU32 i=1;i<256;i++) + Links256[i] = Links256[i-1] + CurCount[i-1]; + + // Perform Radix Sort + const PxU8* PX_RESTRICT InputBytes = reinterpret_cast(input); + InputBytes += BYTES_INC; + if(INVALID_RANKS) + { + for(PxU32 i=0;i>24; // Radix byte, same as above. AND is useless here (PxU32). + // ### cmp to be killed. Not good. Later. + if(Radix<128) *Links256[Radix]++ = i; // Number is positive, same as above + else *(--Links256[Radix]) = i; // Number is negative, flip the sorting order + } + VALIDATE_RANKS; + } + else + { + const PxU32* PX_RESTRICT Ranks = mRanks; + for(PxU32 i=0;i>24; // Radix byte, same as above. AND is useless here (PxU32). + // ### cmp to be killed. Not good. Later. + if(Radix<128) *Links256[Radix]++ = Ranks[i]; // Number is positive, same as above + else *(--Links256[Radix]) = Ranks[i]; // Number is negative, flip the sorting order + } + } + // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap. + PxU32* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp; + } + else + { + // The pass is useless, yet we still have to reverse the order of current list if all values are negative. + if(UniqueVal>=128) + { + if(INVALID_RANKS) + { + // ###Possible? + for(PxU32 i=0;i(PX_ALLOC(sizeof(PxU32)*nb, "RadixSortBuffered:mRanks")); + mRanks2 = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nb, "RadixSortBuffered:mRanks2")); + } + + return true; +} + +PX_INLINE void RadixSortBuffered::CheckResize(PxU32 nb) +{ + PxU32 CurSize = CURRENT_SIZE; + if(nb!=CurSize) + { + if(nb>CurSize) Resize(nb); + mCurrentSize = nb; + INVALIDATE_RANKS; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main sort routine. + * This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data. + * \param input [in] a list of integer values to sort + * \param nb [in] number of values to sort, must be < 2^31 + * \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RadixSortBuffered& RadixSortBuffered::Sort(const PxU32* input, PxU32 nb, RadixHint hint) +{ + // Checkings + if(!input || !nb || nb&0x80000000) return *this; + + // Resize lists if needed + CheckResize(nb); + + //Set histogram buffers. + PxU32 histogram[1024]; + PxU32* links[256]; + mHistogram1024=histogram; + mLinks256=links; + + RadixSort::Sort(input,nb,hint); + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main sort routine. + * This one is for floating-point values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data. + * \param input2 [in] a list of floating-point values to sort + * \param nb [in] number of values to sort, must be < 2^31 + * \return Self-Reference + * \warning only sorts IEEE floating-point values + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RadixSortBuffered& RadixSortBuffered::Sort(const float* input2, PxU32 nb) +{ + // Checkings + if(!input2 || !nb || nb&0x80000000) return *this; + + // Resize lists if needed + CheckResize(nb); + + //Set histogram buffers. + PxU32 histogram[1024]; + PxU32* links[256]; + mHistogram1024=histogram; + mLinks256=links; + + RadixSort::Sort(input2,nb); + return *this; +} + diff --git a/src/PhysX/physx/source/common/src/CmRadixSortBuffered.h b/src/PhysX/physx/source/common/src/CmRadixSortBuffered.h new file mode 100644 index 000000000..9e6bcc034 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmRadixSortBuffered.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CM_RADIX_SORT_BUFFERED_H +#define CM_RADIX_SORT_BUFFERED_H + +#include "CmPhysXCommon.h" +#include "CmRadixSort.h" + +namespace physx +{ +namespace Cm +{ + class PX_PHYSX_COMMON_API RadixSortBuffered : public RadixSort + { + public: + RadixSortBuffered(); + ~RadixSortBuffered(); + + void reset(); + + RadixSortBuffered& Sort(const PxU32* input, PxU32 nb, RadixHint hint=RADIX_SIGNED); + RadixSortBuffered& Sort(const float* input, PxU32 nb); + + private: + RadixSortBuffered(const RadixSortBuffered& object); + RadixSortBuffered& operator=(const RadixSortBuffered& object); + + // Internal methods + void CheckResize(PxU32 nb); + bool Resize(PxU32 nb); + }; +} + +} + +#endif // CM_RADIX_SORT_BUFFERED_H diff --git a/src/PhysX/physx/source/common/src/CmRefCountable.h b/src/PhysX/physx/source/common/src/CmRefCountable.h new file mode 100644 index 000000000..361437628 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmRefCountable.h @@ -0,0 +1,102 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_REFCOUNTABLE +#define PX_PHYSICS_COMMON_REFCOUNTABLE + +#include "foundation/PxAssert.h" +#include "PsAtomic.h" + +namespace physx +{ +namespace Cm +{ + + // simple thread-safe reference count + // when the ref count is zero, the object is in an undefined state (pending delete) + + class RefCountable + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + RefCountable(const PxEMPTY) : mRefCount(1) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + explicit RefCountable(PxU32 initialCount = 1) + : mRefCount(PxI32(initialCount)) + { + PX_ASSERT(mRefCount!=0); + } + + virtual ~RefCountable() {} + + /** + Calls 'delete this;'. It needs to be overloaded for classes also deriving from + PxBase and call 'Cm::deletePxBase(this);' instead. + */ + virtual void onRefCountZero() + { + delete this; + } + + void incRefCount() + { + physx::shdfnd::atomicIncrement(&mRefCount); + // value better be greater than 1, or we've created a ref to an undefined object + PX_ASSERT(mRefCount>1); + } + + void decRefCount() + { + PX_ASSERT(mRefCount>0); + if(physx::shdfnd::atomicDecrement(&mRefCount) == 0) + onRefCountZero(); + } + + PX_FORCE_INLINE PxU32 getRefCount() const + { + return PxU32(mRefCount); + } + private: + volatile PxI32 mRefCount; + }; + + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmRenderBuffer.h b/src/PhysX/physx/source/common/src/CmRenderBuffer.h new file mode 100644 index 000000000..062bd0e34 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmRenderBuffer.h @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_FOUNDATION_PSRENDERBUFFER_H +#define PX_FOUNDATION_PSRENDERBUFFER_H + +#include "common/PxRenderBuffer.h" +#include "CmPhysXCommon.h" +#include "PsArray.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Cm +{ + /** + Implementation of PxRenderBuffer. + */ + class RenderBuffer : public PxRenderBuffer, public Ps::UserAllocated + { + + template + void append(Ps::Array& dst, const T* src, PxU32 count) + { + dst.reserve(dst.size() + count); + for(const T* end=src+count; src mPoints; + Ps::Array mLines; + Ps::Array mTriangles; + Ps::Array mTexts; + Ps::Array mCharBuf; + }; + +} // Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmRenderOutput.cpp b/src/PhysX/physx/source/common/src/CmRenderOutput.cpp new file mode 100644 index 000000000..bc5b78241 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmRenderOutput.cpp @@ -0,0 +1,301 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxMat44.h" +#include "CmRenderOutput.h" +#include "PsMathUtils.h" +#include "PsString.h" +#include + +#if PX_VC +#pragma warning(disable: 4342 ) // behavior change: 'function' called, but a member operator was called in previous versions +#pragma warning(disable: 4996 ) // intentionally suppressing this warning message +#endif + +using namespace physx; +using namespace Cm; + + +namespace physx +{ +namespace Cm +{ + RenderOutput& RenderOutput::operator<<(Primitive prim) + { + mPrim = prim; mVertexCount = 0; return *this; + } + + RenderOutput& RenderOutput::operator<<(PxU32 color) + { + mColor = color; return *this; + } + + RenderOutput& RenderOutput::operator<<(const PxMat44& transform) + { + mTransform = transform; return *this; + } + + RenderOutput& RenderOutput::operator<<(const PxTransform&t) + { + mTransform = PxMat44(t); + return *this; + } + + RenderOutput& RenderOutput::operator<<(PxVec3 vertex) + { + // apply transformation + vertex = mTransform.transform(vertex); + ++mVertexCount; + + // add primitive to render buffer + switch(mPrim) + { + case POINTS: + mBuffer.mPoints.pushBack(PxDebugPoint(vertex, mColor)); break; + case LINES: + if(mVertexCount == 2) + { + mBuffer.mLines.pushBack(PxDebugLine(mVertex0, vertex, mColor)); + mVertexCount = 0; + } + break; + case LINESTRIP: + if(mVertexCount >= 2) + mBuffer.mLines.pushBack(PxDebugLine(mVertex0, vertex, mColor)); + break; + case TRIANGLES: + if(mVertexCount == 3) + { + mBuffer.mTriangles.pushBack(PxDebugTriangle(mVertex1, mVertex0, vertex, mColor)); + mVertexCount = 0; + } + break; + case TRIANGLESTRIP: + if(mVertexCount >= 3) + mBuffer.mTriangles.pushBack(PxDebugTriangle( + (mVertexCount & 0x1) ? mVertex0 : mVertex1, + (mVertexCount & 0x1) ? mVertex1 : mVertex0, vertex, mColor)); + break; + case TEXT: + break; + } + + // cache the last 2 vertices (for strips) + if(1 < mVertexCount) + { + mVertex1 = mVertex0; + mVertex0 = vertex; + } else { + mVertex0 = vertex; + } + return *this; + } + + DebugText::DebugText(const PxVec3& position_, PxReal size_, const char* string, ...) + : position(position_), size(size_) + { + va_list argList; + va_start(argList, string); + if(0 >= Ps::vsnprintf(buffer, sBufferSize-1, string, argList)) + buffer[sBufferSize-1] = 0; // terminate string + va_end(argList); + } + + RenderOutput& RenderOutput::operator<<(const DebugText& text) + { + const PxU32 n = PxU32(strlen(text.buffer)); + const PxU32 newCharBufSize = mBuffer.mCharBuf.size()+n+1; + if(mBuffer.mCharBuf.capacity() < newCharBufSize) + { + char* oldBuf = mBuffer.mCharBuf.begin(); + mBuffer.mCharBuf.reserve(newCharBufSize); + intptr_t diff = mBuffer.mCharBuf.begin() - oldBuf; + for (PxU32 i = 0; i < mBuffer.mTexts.size(); ++i) + mBuffer.mTexts[i].string += diff; + } + mBuffer.mTexts.pushBack(PxDebugText(mTransform.transform(text.position), text.size, mColor, mBuffer.mCharBuf.end())); + for(size_t i=0; i<=n; ++i) + mBuffer.mCharBuf.pushBack(text.buffer[i]); + return *this; + } + + RenderOutput& operator<<(RenderOutput& out, const DebugBox& box) + { + if(box.wireframe) + { + out << RenderOutput::LINESTRIP; + out << PxVec3(box.minimum.x, box.minimum.y, box.minimum.z); + out << PxVec3(box.maximum.x, box.minimum.y, box.minimum.z); + out << PxVec3(box.maximum.x, box.maximum.y, box.minimum.z); + out << PxVec3(box.minimum.x, box.maximum.y, box.minimum.z); + out << PxVec3(box.minimum.x, box.minimum.y, box.minimum.z); + out << PxVec3(box.minimum.x, box.minimum.y, box.maximum.z); + out << PxVec3(box.maximum.x, box.minimum.y, box.maximum.z); + out << PxVec3(box.maximum.x, box.maximum.y, box.maximum.z); + out << PxVec3(box.minimum.x, box.maximum.y, box.maximum.z); + out << PxVec3(box.minimum.x, box.minimum.y, box.maximum.z); + out << RenderOutput::LINES; + out << PxVec3(box.maximum.x, box.minimum.y, box.minimum.z); + out << PxVec3(box.maximum.x, box.minimum.y, box.maximum.z); + out << PxVec3(box.maximum.x, box.maximum.y, box.minimum.z); + out << PxVec3(box.maximum.x, box.maximum.y, box.maximum.z); + out << PxVec3(box.minimum.x, box.maximum.y, box.minimum.z); + out << PxVec3(box.minimum.x, box.maximum.y, box.maximum.z); + } + else + { + out << RenderOutput::TRIANGLESTRIP; + out << PxVec3(box.minimum.x, box.minimum.y, box.minimum.z); // 0 + out << PxVec3(box.minimum.x, box.maximum.y, box.minimum.z); // 2 + out << PxVec3(box.maximum.x, box.minimum.y, box.minimum.z); // 1 + out << PxVec3(box.maximum.x, box.maximum.y, box.minimum.z); // 3 + out << PxVec3(box.maximum.x, box.maximum.y, box.maximum.z); // 7 + out << PxVec3(box.minimum.x, box.maximum.y, box.minimum.z); // 2 + out << PxVec3(box.minimum.x, box.maximum.y, box.maximum.z); // 6 + out << PxVec3(box.minimum.x, box.minimum.y, box.minimum.z); // 0 + out << PxVec3(box.minimum.x, box.minimum.y, box.maximum.z); // 4 + out << PxVec3(box.maximum.x, box.minimum.y, box.minimum.z); // 1 + out << PxVec3(box.maximum.x, box.minimum.y, box.maximum.z); // 5 + out << PxVec3(box.maximum.x, box.maximum.y, box.maximum.z); // 7 + out << PxVec3(box.minimum.x, box.minimum.y, box.maximum.z); // 4 + out << PxVec3(box.minimum.x, box.maximum.y, box.maximum.z); // 6 + } + return out; + } + + RenderOutput& operator<<(RenderOutput& out, const DebugArrow& arrow) + { + PxVec3 t0 = arrow.tip - arrow.base, t1, t2; + + t0.normalize(); + Ps::normalToTangents(t0, t1, t2); + + const PxReal tipAngle = 0.25f; + t1 *= arrow.headLength * tipAngle; + t2 *= arrow.headLength * tipAngle * PxSqrt(3.0f); + PxVec3 headBase = arrow.tip - t0 * arrow.headLength; + + out << RenderOutput::LINES; + out << arrow.base << arrow.tip; + + out << RenderOutput::TRIANGLESTRIP; + out << arrow.tip; + out << headBase + t1 + t1; + out << headBase - t1 - t2; + out << headBase - t1 + t2; + out << arrow.tip; + out << headBase + t1 + t1; + return out; + } + + RenderOutput& operator<<(RenderOutput& out, const DebugBasis& basis) + { + const PxReal headLength = basis.extends.magnitude() * 0.15f; + out << basis.colorX << DebugArrow(PxVec3(0.0f), PxVec3(basis.extends.x, 0, 0), headLength); + out << basis.colorY << DebugArrow(PxVec3(0.0f), PxVec3(0, basis.extends.y, 0), headLength); + out << basis.colorZ << DebugArrow(PxVec3(0.0f), PxVec3(0, 0, basis.extends.z), headLength); + return out; + } + + RenderOutput& operator<<(RenderOutput& out, const DebugCircle& circle) + { + const PxF32 step = PxTwoPi/circle.nSegments; + PxF32 angle = 0; + out << RenderOutput::LINESTRIP; + for(PxU32 i=0; ipos0 = v0; + segment->pos1 = v1; + segment->color0 = segment->color1 = mColor; + } + + RenderOutput& outputCapsule(PxF32 radius, PxF32 halfHeight, const PxMat44& absPose); + + private: + + RenderOutput& operator=(const RenderOutput&); + + Primitive mPrim; + PxU32 mColor; + PxVec3 mVertex0, mVertex1; + PxU32 mVertexCount; + PxMat44 mTransform; + RenderBuffer& mBuffer; + }; + + /** debug render helper types */ + struct PX_PHYSX_COMMON_API DebugText + { + DebugText(const PxVec3& position, PxReal size, const char* string, ...); + static const int sBufferSize = 1008; // sizeof(DebugText)==1kB + char buffer[sBufferSize]; + PxVec3 position; + PxReal size; + }; + + struct DebugBox + { + explicit DebugBox(const PxVec3& extents, bool wireframe_ = true) + : minimum(-extents), maximum(extents), wireframe(wireframe_) {} + + explicit DebugBox(const PxVec3& pos, const PxVec3& extents, bool wireframe_ = true) + : minimum(pos-extents), maximum(pos+extents), wireframe(wireframe_) {} + + explicit DebugBox(const PxBounds3& bounds, bool wireframe_ = true) + : minimum(bounds.minimum), maximum(bounds.maximum), wireframe(wireframe_) {} + + PxVec3 minimum, maximum; + bool wireframe; + }; + PX_PHYSX_COMMON_API RenderOutput& operator<<(RenderOutput& out, const DebugBox& box); + + struct DebugArrow + { + DebugArrow(const PxVec3& pos, const PxVec3& vec) + : base(pos), tip(pos+vec), headLength(vec.magnitude()*0.15f) {} + + DebugArrow(const PxVec3& pos, const PxVec3& vec, PxReal headLength_) + : base(pos), tip(pos+vec), headLength(headLength_) {} + + PxVec3 base, tip; + PxReal headLength; + }; + PX_PHYSX_COMMON_API RenderOutput& operator<<(RenderOutput& out, const DebugArrow& arrow); + + struct DebugBasis + { + DebugBasis(const PxVec3& ext, PxU32 cX = PxDebugColor::eARGB_RED, + PxU32 cY = PxDebugColor::eARGB_GREEN, PxU32 cZ = PxDebugColor::eARGB_BLUE) + : extends(ext), colorX(cX), colorY(cY), colorZ(cZ) {} + PxVec3 extends; + PxU32 colorX, colorY, colorZ; + }; + PX_PHYSX_COMMON_API RenderOutput& operator<<(RenderOutput& out, const DebugBasis& basis); + +#if PX_VC + #pragma warning(pop) +#endif + + struct DebugCircle + { + DebugCircle(PxU32 s, PxReal r) + : nSegments(s), radius(r) {} + PxU32 nSegments; + PxReal radius; + }; + PX_PHYSX_COMMON_API RenderOutput& operator<<(RenderOutput& out, const DebugCircle& circle); + + struct DebugArc + { + DebugArc(PxU32 s, PxReal r, PxReal minAng, PxReal maxAng) + : nSegments(s), radius(r), minAngle(minAng), maxAngle(maxAng) {} + PxU32 nSegments; + PxReal radius; + PxReal minAngle, maxAngle; + }; + PX_PHYSX_COMMON_API RenderOutput& operator<<(RenderOutput& out, const DebugArc& arc); + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmScaling.h b/src/PhysX/physx/source/common/src/CmScaling.h new file mode 100644 index 000000000..53ba22345 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmScaling.h @@ -0,0 +1,227 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_SCALING +#define PX_PHYSICS_COMMON_SCALING + +#include "foundation/PxBounds3.h" +#include "foundation/PxMat33.h" +#include "geometry/PxMeshScale.h" +#include "CmMatrix34.h" +#include "CmUtils.h" +#include "PsMathUtils.h" + +namespace physx +{ +namespace Cm +{ + // class that can perform scaling fast. Relatively large size, generated from PxMeshScale on demand. + // CS: I've removed most usages of this class, because most of the time only one-way transform is needed. + // If you only need a temporary FastVertex2ShapeScaling, setup your transform as PxMat34Legacy and use + // normal matrix multiplication or a transform() overload to convert points and bounds between spaces. + class FastVertex2ShapeScaling + { + public: + PX_INLINE FastVertex2ShapeScaling() + { + //no scaling by default: + vertex2ShapeSkew = PxMat33(PxIdentity); + shape2VertexSkew = PxMat33(PxIdentity); + mFlipNormal = false; + } + + PX_INLINE explicit FastVertex2ShapeScaling(const PxMeshScale& scale) + { + init(scale); + } + + PX_INLINE FastVertex2ShapeScaling(const PxVec3& scale, const PxQuat& rotation) + { + init(scale, rotation); + } + + PX_INLINE void init(const PxMeshScale& scale) + { + init(scale.scale, scale.rotation); + } + + PX_INLINE void setIdentity() + { + vertex2ShapeSkew = PxMat33(PxIdentity); + shape2VertexSkew = PxMat33(PxIdentity); + mFlipNormal = false; + } + + PX_INLINE void init(const PxVec3& scale, const PxQuat& rotation) + { + // TODO: may want to optimize this for cases where we have uniform or axis aligned scaling! + // That would introduce branches and it's unclear to me whether that's faster than just doing the math. + // Lazy computation would be another option, at the cost of introducing even more branches. + + const PxMat33 R(rotation); + vertex2ShapeSkew = R.getTranspose(); + const PxMat33 diagonal = PxMat33::createDiagonal(scale); + vertex2ShapeSkew = vertex2ShapeSkew * diagonal; + vertex2ShapeSkew = vertex2ShapeSkew * R; + + /* + The inverse, is, explicitly: + shape2VertexSkew.setTransposed(R); + shape2VertexSkew.multiplyDiagonal(PxVec3(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z)); + shape2VertexSkew *= R; + + It may be competitive to compute the inverse -- though this has a branch in it: + */ + + shape2VertexSkew = vertex2ShapeSkew.getInverse(); + + mFlipNormal = ((scale.x * scale.y * scale.z) < 0.0f); + } + + PX_FORCE_INLINE void flipNormal(PxVec3& v1, PxVec3& v2) const + { + if (mFlipNormal) + { + PxVec3 tmp = v1; v1 = v2; v2 = tmp; + } + } + + PX_FORCE_INLINE PxVec3 operator* (const PxVec3& src) const + { + return vertex2ShapeSkew * src; + } + PX_FORCE_INLINE PxVec3 operator% (const PxVec3& src) const + { + return shape2VertexSkew * src; + } + + PX_FORCE_INLINE const PxMat33& getVertex2ShapeSkew() const + { + return vertex2ShapeSkew; + } + + PX_FORCE_INLINE const PxMat33& getShape2VertexSkew() const + { + return shape2VertexSkew; + } + + PX_INLINE Cm::Matrix34 getVertex2WorldSkew(const Cm::Matrix34& shape2world) const + { + const Cm::Matrix34 vertex2worldSkew = shape2world * getVertex2ShapeSkew(); + //vertex2worldSkew = shape2world * [vertex2shapeSkew, 0] + //[aR at] * [bR bt] = [aR * bR aR * bt + at] NOTE: order of operations important so it works when this ?= left ?= right. + return vertex2worldSkew; + } + + PX_INLINE Cm::Matrix34 getWorld2VertexSkew(const Cm::Matrix34& shape2world) const + { + //world2vertexSkew = shape2vertex * invPQ(shape2world) + //[aR 0] * [bR' -bR'*bt] = [aR * bR' -aR * bR' * bt + 0] + + const PxMat33 rotate( shape2world[0], shape2world[1], shape2world[2] ); + const PxMat33 M = getShape2VertexSkew() * rotate.getTranspose(); + return Cm::Matrix34(M[0], M[1], M[2], -M * shape2world[3]); + } + + //! Transforms a shape space OBB to a vertex space OBB. All 3 params are in and out. + void transformQueryBounds(PxVec3& center, PxVec3& extents, PxMat33& basis) const + { + basis.column0 = shape2VertexSkew * (basis.column0 * extents.x); + basis.column1 = shape2VertexSkew * (basis.column1 * extents.y); + basis.column2 = shape2VertexSkew * (basis.column2 * extents.z); + + center = shape2VertexSkew * center; + extents = Ps::optimizeBoundingBox(basis); + } + + void transformPlaneToShapeSpace(const PxVec3& nIn, const PxReal dIn, PxVec3& nOut, PxReal& dOut) const + { + const PxVec3 tmp = shape2VertexSkew.transformTranspose(nIn); + const PxReal denom = 1.0f / tmp.magnitude(); + nOut = tmp * denom; + dOut = dIn * denom; + } + + PX_FORCE_INLINE bool flipsNormal() const { return mFlipNormal; } + + private: + PxMat33 vertex2ShapeSkew; + PxMat33 shape2VertexSkew; + bool mFlipNormal; + }; + + PX_FORCE_INLINE void getScaledVertices(PxVec3* v, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, bool idtMeshScale, const Cm::FastVertex2ShapeScaling& scaling) + { + if(idtMeshScale) + { + v[0] = v0; + v[1] = v1; + v[2] = v2; + } + else + { + const PxI32 winding = scaling.flipsNormal() ? 1 : 0; + v[0] = scaling * v0; + v[1+winding] = scaling * v1; + v[2-winding] = scaling * v2; + } + } + +} // namespace Cm + + +PX_INLINE Cm::Matrix34 operator*(const PxTransform& transform, const PxMeshScale& scale) +{ + return Cm::Matrix34(PxMat33(transform.q) * scale.toMat33(), transform.p); +} + +PX_INLINE Cm::Matrix34 operator*(const PxMeshScale& scale, const PxTransform& transform) +{ + const PxMat33 scaleMat = scale.toMat33(); + const PxMat33 t = PxMat33(transform.q); + const PxMat33 r = scaleMat * t; + const PxVec3 p = scaleMat * transform.p; + return Cm::Matrix34(r, p); +} + +PX_INLINE Cm::Matrix34 operator*(const Cm::Matrix34& transform, const PxMeshScale& scale) +{ + return Cm::Matrix34(transform.m * scale.toMat33(), transform.p); +} + +PX_INLINE Cm::Matrix34 operator*(const PxMeshScale& scale, const Cm::Matrix34& transform) +{ + const PxMat33 scaleMat = scale.toMat33(); + return Cm::Matrix34(scaleMat * transform.m, scaleMat * transform.p); +} + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmSpatialVector.h b/src/PhysX/physx/source/common/src/CmSpatialVector.h new file mode 100644 index 000000000..05e7ef57c --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmSpatialVector.h @@ -0,0 +1,513 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_VECTOR +#define PX_PHYSICS_COMMON_VECTOR + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" +#include "foundation/PxTransform.h" + +/*! +Combination of two R3 vectors. +*/ + +namespace physx +{ +namespace Cm +{ +PX_ALIGN_PREFIX(16) +class SpatialVector +{ +public: + //! Default constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector() + {} + + //! Construct from two PxcVectors + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector(const PxVec3& lin, const PxVec3& ang) + : linear(lin), pad0(0.0f), angular(ang), pad1(0.0f) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE ~SpatialVector() + {} + + + + // PT: this one is very important. Without it, the Xbox compiler generates weird "float-to-int" and "int-to-float" LHS + // each time we copy a SpatialVector (see for example PIX on "solveSimpleGroupA" without this operator). + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator = (const SpatialVector& v) + { + linear = v.linear; + pad0 = 0.0f; + angular = v.angular; + pad1 = 0.0f; + } + + + static PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector zero() { return SpatialVector(PxVec3(0),PxVec3(0)); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector operator+(const SpatialVector& v) const + { + return SpatialVector(linear+v.linear,angular+v.angular); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector operator-(const SpatialVector& v) const + { + return SpatialVector(linear-v.linear,angular-v.angular); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector operator-() const + { + return SpatialVector(-linear,-angular); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVector operator *(PxReal s) const + { + return SpatialVector(linear*s,angular*s); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator+=(const SpatialVector& v) + { + linear+=v.linear; + angular+=v.angular; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator-=(const SpatialVector& v) + { + linear-=v.linear; + angular-=v.angular; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal magnitude() const + { + return angular.magnitude() + linear.magnitude(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal dot(const SpatialVector& v) const + { + return linear.dot(v.linear) + angular.dot(v.angular); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite() const + { + return linear.isFinite() && angular.isFinite(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVector scale(PxReal l, PxReal a) const + { + return Cm::SpatialVector(linear*l, angular*a); + } + + PxVec3 linear; + PxReal pad0; + PxVec3 angular; + PxReal pad1; +} +PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct SpatialVectorF +{ +public: + //! Default constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF() + {} + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF(const PxReal* v) + : pad0(0.0f), pad1(0.0f) + { + top.x = v[0]; top.y = v[1]; top.z = v[2]; + bottom.x = v[3]; bottom.y = v[4]; bottom.z = v[5]; + } + //! Construct from two PxcVectors + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF(const PxVec3& top_, const PxVec3& bottom_) + : top(top_), pad0(0.0f), bottom(bottom_), pad1(0.0f) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE ~SpatialVectorF() + {} + + + + // PT: this one is very important. Without it, the Xbox compiler generates weird "float-to-int" and "int-to-float" LHS + // each time we copy a SpatialVector (see for example PIX on "solveSimpleGroupA" without this operator). + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator = (const SpatialVectorF& v) + { + top = v.top; + pad0 = 0.0f; + bottom = v.bottom; + pad1 = 0.0f; + } + + + static PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF Zero() { return SpatialVectorF(PxVec3(0), PxVec3(0)); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF operator+(const SpatialVectorF& v) const + { + return SpatialVectorF(top + v.top, bottom + v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF operator-(const SpatialVectorF& v) const + { + return SpatialVectorF(top - v.top, bottom - v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF operator-() const + { + return SpatialVectorF(-top, -bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF operator *(PxReal s) const + { + return SpatialVectorF(top*s, bottom*s); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator *= (const PxReal s) + { + top *= s; + bottom *= s; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator += (const SpatialVectorF& v) + { + top += v.top; + bottom += v.bottom; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator -= (const SpatialVectorF& v) + { + top -= v.top; + bottom -= v.bottom; + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal magnitude() const + { + return top.magnitude() + bottom.magnitude(); + } + + PX_FORCE_INLINE PxReal magnitudeSquared() const + { + return top.magnitudeSquared() + bottom.magnitudeSquared(); + } + + //This is inner product + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal innerProduct(const SpatialVectorF& v) const + { + return bottom.dot(v.top) + top.dot(v.bottom); + /*PxVec3 p0 = bottom.multiply(v.top); + PxVec3 p1 = top.multiply(v.bottom); + + PxReal result = (((p1.y + p1.z) + (p0.z + p1.x)) + (p0.x + p0.y)); + return result;*/ + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal dot(const SpatialVectorF& v) const + { + return top.dot(v.top) + bottom.dot(v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal dot(const SpatialVector& v) const + { + return bottom.dot(v.angular) + top.dot(v.linear); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF cross(const SpatialVectorF& v) const + { + SpatialVectorF a; + a.top = top.cross(v.top); + a.bottom = top.cross(v.bottom) + bottom.cross(v.top); + return a; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF abs() const + { + return SpatialVectorF(top.abs(), bottom.abs()); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF rotate(const PxTransform& rot) const + { + return SpatialVectorF(rot.rotate(top), rot.rotate(bottom)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialVectorF rotateInv(const PxTransform& rot) const + { + return SpatialVectorF(rot.rotateInv(top), rot.rotateInv(bottom)); + } + + + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite() const + { + return top.isFinite() && bottom.isFinite(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isValid(const PxReal maxV) const + { + const bool tValid = ((PxAbs(top.x) <= maxV) && (PxAbs(top.y) <= maxV) && (PxAbs(top.z) <= maxV)); + const bool bValid = ((PxAbs(bottom.x) <= maxV) && (PxAbs(bottom.y) <= maxV) && (PxAbs(bottom.z) <= maxV)); + + return tValid && bValid; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF scale(PxReal l, PxReal a) const + { + return Cm::SpatialVectorF(top*l, bottom*a); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void assignTo(PxReal* val) const + { + val[0] = top.x; val[1] = top.y; val[2] = top.z; + val[3] = bottom.x; val[4] = bottom.y; val[5] = bottom.z; + } + + PxVec3 top; + PxReal pad0; + PxVec3 bottom; + PxReal pad1; +} PX_ALIGN_SUFFIX(16); + + +struct UnAlignedSpatialVector +{ +public: + //! Default constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector() + {} + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector(const PxReal* v) + { + top.x = v[0]; top.y = v[1]; top.z = v[2]; + bottom.x = v[3]; bottom.y = v[4]; bottom.z = v[5]; + } + //! Construct from two PxcVectors + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector(const PxVec3& top_, const PxVec3& bottom_) + : top(top_), bottom(bottom_) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE ~UnAlignedSpatialVector() + {} + + + + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator = (const SpatialVectorF& v) + { + top = v.top; + bottom = v.bottom; + } + + + static PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector Zero() { return UnAlignedSpatialVector(PxVec3(0), PxVec3(0)); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector operator+(const UnAlignedSpatialVector& v) const + { + return UnAlignedSpatialVector(top + v.top, bottom + v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector operator-(const UnAlignedSpatialVector& v) const + { + return UnAlignedSpatialVector(top - v.top, bottom - v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector operator-() const + { + return UnAlignedSpatialVector(-top, -bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector operator *(PxReal s) const + { + return UnAlignedSpatialVector(top*s, bottom*s); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator *= (const PxReal s) + { + top *= s; + bottom *= s; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator += (const UnAlignedSpatialVector& v) + { + top += v.top; + bottom += v.bottom; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator += (const SpatialVectorF& v) + { + top += v.top; + bottom += v.bottom; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator -= (const UnAlignedSpatialVector& v) + { + top -= v.top; + bottom -= v.bottom; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator -= (const SpatialVectorF& v) + { + top -= v.top; + bottom -= v.bottom; + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal magnitude() const + { + return top.magnitude() + bottom.magnitude(); + } + + PX_FORCE_INLINE PxReal magnitudeSquared() const + { + return top.magnitudeSquared() + bottom.magnitudeSquared(); + } + + //This is inner product + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal innerProduct(const UnAlignedSpatialVector& v) const + { + return bottom.dot(v.top) + top.dot(v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal innerProduct(const SpatialVectorF& v) const + { + return bottom.dot(v.top) + top.dot(v.bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal dot(const UnAlignedSpatialVector& v) const + { + return top.dot(v.top) + bottom.dot(v.bottom); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector cross(const UnAlignedSpatialVector& v) const + { + UnAlignedSpatialVector a; + a.top = top.cross(v.top); + a.bottom = top.cross(v.bottom) + bottom.cross(v.top); + return a; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector abs() const + { + return UnAlignedSpatialVector(top.abs(), bottom.abs()); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector rotate(const PxTransform& rot) const + { + return UnAlignedSpatialVector(rot.rotate(top), rot.rotate(bottom)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE UnAlignedSpatialVector rotateInv(const PxTransform& rot) const + { + return UnAlignedSpatialVector(rot.rotateInv(top), rot.rotateInv(bottom)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite() const + { + return top.isFinite() && bottom.isFinite(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isValid(const PxReal maxV) const + { + const bool tValid = ((top.x <= maxV) && (top.y <= maxV) && (top.z <= maxV)); + const bool bValid = ((bottom.x <= maxV) && (bottom.y <= maxV) && (bottom.z <= maxV)); + + return tValid && bValid; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector scale(PxReal l, PxReal a) const + { + return Cm::UnAlignedSpatialVector(top*l, bottom*a); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void assignTo(PxReal* val) const + { + val[0] = top.x; val[1] = top.y; val[2] = top.z; + val[3] = bottom.x; val[4] = bottom.y; val[5] = bottom.z; + } + + PxVec3 top; //12 12 + PxVec3 bottom; //12 24 +}; + + +PX_ALIGN_PREFIX(16) +struct SpatialVectorV +{ + Ps::aos::Vec3V linear; + Ps::aos::Vec3V angular; + + PX_FORCE_INLINE SpatialVectorV() {} + PX_FORCE_INLINE SpatialVectorV(PxZERO): linear(Ps::aos::V3Zero()), angular(Ps::aos::V3Zero()) {} + PX_FORCE_INLINE SpatialVectorV(const Cm::SpatialVector& v): linear(Ps::aos::V3LoadU(v.linear)), angular(Ps::aos::V3LoadU(v.angular)) {} + PX_FORCE_INLINE SpatialVectorV(const Ps::aos::Vec3VArg l, const Ps::aos::Vec3VArg a): linear(l), angular(a) {} + PX_FORCE_INLINE SpatialVectorV(const SpatialVectorV& other): linear(other.linear), angular(other.angular) {} + PX_FORCE_INLINE SpatialVectorV& operator=(const SpatialVectorV& other) { linear = other.linear; angular = other.angular; return *this; } + + PX_FORCE_INLINE SpatialVectorV operator+(const SpatialVectorV& other) const { return SpatialVectorV(Ps::aos::V3Add(linear,other.linear), + Ps::aos::V3Add(angular, other.angular)); } + + PX_FORCE_INLINE SpatialVectorV& operator+=(const SpatialVectorV& other) { linear = Ps::aos::V3Add(linear,other.linear); + angular = Ps::aos::V3Add(angular, other.angular); + return *this; + } + + PX_FORCE_INLINE SpatialVectorV operator-(const SpatialVectorV& other) const { return SpatialVectorV(Ps::aos::V3Sub(linear,other.linear), + Ps::aos::V3Sub(angular, other.angular)); } + + PX_FORCE_INLINE SpatialVectorV operator-() const { return SpatialVectorV(Ps::aos::V3Neg(linear), Ps::aos::V3Neg(angular)); } + + PX_FORCE_INLINE SpatialVectorV operator*(const Ps::aos::FloatVArg r) const { return SpatialVectorV(Ps::aos::V3Scale(linear,r), Ps::aos::V3Scale(angular,r)); } + + PX_FORCE_INLINE SpatialVectorV& operator-=(const SpatialVectorV& other) { linear = Ps::aos::V3Sub(linear,other.linear); + angular = Ps::aos::V3Sub(angular, other.angular); + return *this; + } + + PX_FORCE_INLINE Ps::aos::FloatV dot(const SpatialVectorV& other) const { return Ps::aos::FAdd(Ps::aos::V3Dot(linear, other.linear), Ps::aos::V3Dot(angular, other.angular)); } + + +}PX_ALIGN_SUFFIX(16); + +} // namespace Cm + +PX_COMPILE_TIME_ASSERT(sizeof(Cm::SpatialVector) == 32); +PX_COMPILE_TIME_ASSERT(sizeof(Cm::SpatialVectorV) == 32); + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmTask.h b/src/PhysX/physx/source/common/src/CmTask.h new file mode 100644 index 000000000..cf28c7da5 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmTask.h @@ -0,0 +1,267 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_TASK +#define PX_PHYSICS_COMMON_TASK + +#include "task/PxTask.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PsAtomic.h" +#include "PsMutex.h" +#include "PsInlineArray.h" +#include "PsFPU.h" + +namespace physx +{ +namespace Cm +{ + // wrapper around the public PxLightCpuTask + // internal SDK tasks should be inherited from + // this and override the runInternal() method + // to ensure that the correct floating point + // state is set / reset during execution + class Task : public physx::PxLightCpuTask + { + public: + Task(PxU64 contextId) + { + mContextID = contextId; + } + + virtual void run() + { +#if PX_SWITCH // special case because default rounding mode is not nearest + PX_FPU_GUARD; +#else + PX_SIMD_GUARD; +#endif + runInternal(); + } + + virtual void runInternal()=0; + }; + + // same as Cm::Task but inheriting from physx::PxBaseTask + // instead of PxLightCpuTask + class BaseTask : public physx::PxBaseTask + { + public: + + virtual void run() + { +#if PX_SWITCH // special case because default rounding mode is not nearest + PX_FPU_GUARD; +#else + PX_SIMD_GUARD; +#endif + runInternal(); + } + + virtual void runInternal()=0; + }; + + template + class DelegateTask : public Cm::Task, public shdfnd::UserAllocated + { + public: + + DelegateTask(PxU64 contextID, T* obj, const char* name) : Cm::Task(contextID), mObj(obj), mName(name) {} + + virtual void runInternal() + { + (mObj->*Fn)(mCont); + } + + virtual const char* getName() const + { + return mName; + } + + void setObject(T* obj) { mObj = obj; } + + private: + T* mObj; + const char* mName; + }; + + + /** + \brief A task that maintains a list of dependent tasks. + + This task maintains a list of dependent tasks that have their reference counts + reduced on completion of the task. + + The refcount is incremented every time a dependent task is added. + */ + class FanoutTask : public Cm::BaseTask + { + PX_NOCOPY(FanoutTask) + public: + FanoutTask(PxU64 contextID, const char* name) : Cm::BaseTask(), mRefCount(0), mName(name), mNotifySubmission(false) { mContextID = contextID; } + + virtual void runInternal() {} + + virtual const char* getName() const { return mName; } + + /** + Swap mDependents with mReferencesToRemove when refcount goes to 0. + */ + virtual void removeReference() + { + shdfnd::Mutex::ScopedLock lock(mMutex); + if (!physx::shdfnd::atomicDecrement(&mRefCount)) + { + // prevents access to mReferencesToRemove until release + physx::shdfnd::atomicIncrement(&mRefCount); + mNotifySubmission = false; + PX_ASSERT(mReferencesToRemove.empty()); + for (PxU32 i = 0; i < mDependents.size(); i++) + mReferencesToRemove.pushBack(mDependents[i]); + mDependents.clear(); + mTm->getCpuDispatcher()->submitTask(*this); + } + } + + /** + \brief Increases reference count + */ + virtual void addReference() + { + shdfnd::Mutex::ScopedLock lock(mMutex); + physx::shdfnd::atomicIncrement(&mRefCount); + mNotifySubmission = true; + } + + /** + \brief Return the ref-count for this task + */ + PX_INLINE PxI32 getReference() const + { + return mRefCount; + } + + /** + Sets the task manager. Doesn't increase the reference count. + */ + PX_INLINE void setTaskManager(physx::PxTaskManager& tm) + { + mTm = &tm; + } + + /** + Adds a dependent task. It also sets the task manager querying it from the dependent task. + The refcount is incremented every time a dependent task is added. + */ + PX_INLINE void addDependent(physx::PxBaseTask& dependent) + { + shdfnd::Mutex::ScopedLock lock(mMutex); + physx::shdfnd::atomicIncrement(&mRefCount); + mTm = dependent.getTaskManager(); + mDependents.pushBack(&dependent); + dependent.addReference(); + mNotifySubmission = true; + } + + /** + Reduces reference counts of the continuation task and the dependent tasks, also + clearing the copy of continuation and dependents task list. + */ + virtual void release() + { + Ps::InlineArray referencesToRemove; + + { + shdfnd::Mutex::ScopedLock lock(mMutex); + + const PxU32 contCount = mReferencesToRemove.size(); + referencesToRemove.reserve(contCount); + for (PxU32 i=0; i < contCount; ++i) + referencesToRemove.pushBack(mReferencesToRemove[i]); + + mReferencesToRemove.clear(); + // allow access to mReferencesToRemove again + if (mNotifySubmission) + { + removeReference(); + } + else + { + physx::shdfnd::atomicDecrement(&mRefCount); + } + + // the scoped lock needs to get freed before the continuation tasks get (potentially) submitted because + // those continuation tasks might trigger events that delete this task and corrupt the memory of the + // mutex (for example, assume this task is a member of the scene then the submitted tasks cause the simulation + // to finish and then the scene gets released which in turn will delete this task. When this task then finally + // continues the heap memory will be corrupted. + } + + for (PxU32 i=0; i < referencesToRemove.size(); ++i) + referencesToRemove[i]->removeReference(); + } + + protected: + volatile PxI32 mRefCount; + const char* mName; + Ps::InlineArray mDependents; + Ps::InlineArray mReferencesToRemove; + bool mNotifySubmission; + Ps::Mutex mMutex; // guarding mDependents and mNotifySubmission + }; + + + /** + \brief Specialization of FanoutTask class in order to provide the delegation mechanism. + */ + template + class DelegateFanoutTask : public FanoutTask, public shdfnd::UserAllocated + { + public: + DelegateFanoutTask(PxU64 contextID, T* obj, const char* name) : + FanoutTask(contextID, name), mObj(obj) { } + + virtual void runInternal() + { + physx::PxBaseTask* continuation = mReferencesToRemove.empty() ? NULL : mReferencesToRemove[0]; + (mObj->*Fn)(continuation); + } + + void setObject(T* obj) { mObj = obj; } + + private: + T* mObj; + }; + +} // namespace Cm + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmTaskPool.h b/src/PhysX/physx/source/common/src/CmTaskPool.h new file mode 100644 index 000000000..8bb36a71b --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmTaskPool.h @@ -0,0 +1,143 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_TASKPOOL +#define PX_PHYSICS_COMMON_TASKPOOL + +#include "foundation/Px.h" +#include "PsMutex.h" +#include "PsSList.h" +#include "PsAllocator.h" +#include "PsArray.h" + +class PxTask; + +/* +Implimentation of a thread safe task pool. (PxTask derived classes). + +T is the actual type of the task(currently NphaseTask or GroupSolveTask). +*/ + +namespace Cm +{ + template class TaskPool : public Ps::AlignedAllocator<16> + { + const static PxU32 TaskPoolSlabSize=64; + + public: + + typedef Ps::SListEntry TaskPoolItem; + + PX_INLINE TaskPool() : slabArray(PX_DEBUG_EXP("taskPoolSlabArray")) + { + //we have to ensure that the list header is 16byte aligned for win64. + freeTasks = (Ps::SList*)allocate(sizeof(Ps::SList), __FILE__, __LINE__); + PX_PLACEMENT_NEW(freeTasks, Ps::SList)(); + + slabArray.reserve(16); + } + + ~TaskPool() + { + Ps::Mutex::ScopedLock lock(slabAllocMutex); + + freeTasks->flush(); + + for(PxU32 i=0;i~SList(); + deallocate(freeTasks); + freeTasks = NULL; + } + } + + T *allocTask() + { + T *rv = static_cast(freeTasks->pop()); + if(rv == NULL) + return static_cast(allocateSlab()); + else + return rv; + } + void freeTask(T *task) + { + freeTasks->push(*task); + } + + private: + + T *allocateSlab() + { + //ack, convoluted memory macros. + + //T *newSlab=new T[TaskPoolSlabSize]; + + // we must align this memory. + T *newSlab=(T *)allocate(sizeof(T)*TaskPoolSlabSize, __FILE__, __LINE__); + + new (newSlab) T(); + + //we keep one for the caller + // and build a list of tasks and insert in the free list + for(PxU32 i=1;ipush(newSlab[i]); + } + + Ps::Mutex::ScopedLock lock(slabAllocMutex); + slabArray.pushBack(newSlab); + + return newSlab; + } + + Ps::Mutex slabAllocMutex; + Ps::Array slabArray; + + Ps::SList *freeTasks; + + }; + + +} // namespace Cm + + +#endif diff --git a/src/PhysX/physx/source/common/src/CmTmpMem.h b/src/PhysX/physx/source/common/src/CmTmpMem.h new file mode 100644 index 000000000..9f30304d8 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmTmpMem.h @@ -0,0 +1,94 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_TMPMEM +#define PX_PHYSICS_COMMON_TMPMEM + +#include "CmPhysXCommon.h" +#include "PsAllocator.h" + +namespace physx +{ +namespace Cm +{ + // dsequeira: we should be able to use PX_ALLOCA or Ps::InlineArray for this, but both seem slightly flawed: + // + // PX_ALLOCA has non-configurable fallback threshold and uses _alloca, which means the allocation is necessarily + // function-scope rather than block-scope (sometimes useful, mostly not.) + // + // Ps::InlineArray touches all memory on resize (a general flaw in the array class which badly needs fixing) + // + // Todo: fix both the above issues. + + template + class TmpMem + { + + public: + PX_FORCE_INLINE TmpMem(PxU32 size): + mPtr(size<=stackLimit?mStackBuf : reinterpret_cast(PX_ALLOC(size*sizeof(T), "char"))) + { + } + + PX_FORCE_INLINE ~TmpMem() + { + if(mPtr!=mStackBuf) + PX_FREE(mPtr); + } + + PX_FORCE_INLINE T& operator*() const + { + return *mPtr; + } + + PX_FORCE_INLINE T* operator->() const + { + return mPtr; + } + + PX_FORCE_INLINE T& operator[](PxU32 index) + { + return mPtr[index]; + } + + T* getBase() + { + return mPtr; + } + + private: + T mStackBuf[stackLimit]; + T* mPtr; + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmTransformUtils.h b/src/PhysX/physx/source/common/src/CmTransformUtils.h new file mode 100644 index 000000000..fe70469b6 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmTransformUtils.h @@ -0,0 +1,145 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_TRANSFORMUTILS +#define PX_PHYSICS_COMMON_TRANSFORMUTILS + +#include "PsVecMath.h" + +namespace +{ + +using namespace physx::shdfnd::aos; + +// V3PrepareCross would help here, but it's not on all platforms yet... + +PX_FORCE_INLINE void transformFast(const FloatVArg wa, const Vec3VArg va, const Vec3VArg pa, + const FloatVArg wb, const Vec3VArg vb, const Vec3VArg pb, + FloatV& wo, Vec3V& vo, Vec3V& po) +{ + wo = FSub(FMul(wa, wb), V3Dot(va, vb)); + vo = V3ScaleAdd(va, wb, V3ScaleAdd(vb, wa, V3Cross(va, vb))); + + const Vec3V t1 = V3Scale(pb, FScaleAdd(wa, wa, FLoad(-0.5f))); + const Vec3V t2 = V3ScaleAdd(V3Cross(va, pb), wa, t1); + const Vec3V t3 = V3ScaleAdd(va, V3Dot(va, pb), t2); + + po = V3ScaleAdd(t3, FLoad(2.f), pa); +} + +PX_FORCE_INLINE void transformInvFast(const FloatVArg wa, const Vec3VArg va, const Vec3VArg pa, + const FloatVArg wb, const Vec3VArg vb, const Vec3VArg pb, + FloatV& wo, Vec3V& vo, Vec3V& po) +{ + wo = FScaleAdd(wa, wb, V3Dot(va, vb)); + vo = V3NegScaleSub(va, wb, V3ScaleAdd(vb, wa, V3Cross(vb, va))); + + const Vec3V pt = V3Sub(pb, pa); + const Vec3V t1 = V3Scale(pt, FScaleAdd(wa, wa, FLoad(-0.5f))); + const Vec3V t2 = V3ScaleAdd(V3Cross(pt, va), wa, t1); + const Vec3V t3 = V3ScaleAdd(va, V3Dot(va, pt), t2); + po = V3Add(t3,t3); +} + +} + + + + + +namespace physx +{ +namespace Cm +{ + +PX_FORCE_INLINE void getStaticGlobalPoseAligned(const PxTransform& actor2World, const PxTransform& shape2Actor, PxTransform& outTransform) +{ + using namespace shdfnd::aos; + + PX_ASSERT((size_t(&actor2World)&15) == 0); + PX_ASSERT((size_t(&shape2Actor)&15) == 0); + PX_ASSERT((size_t(&outTransform)&15) == 0); + + const Vec3V actor2WorldPos = V3LoadA(actor2World.p); + const QuatV actor2WorldRot = QuatVLoadA(&actor2World.q.x); + + const Vec3V shape2ActorPos = V3LoadA(shape2Actor.p); + const QuatV shape2ActorRot = QuatVLoadA(&shape2Actor.q.x); + + Vec3V v,p; + FloatV w; + + transformFast(V4GetW(actor2WorldRot), Vec3V_From_Vec4V(actor2WorldRot), actor2WorldPos, + V4GetW(shape2ActorRot), Vec3V_From_Vec4V(shape2ActorRot), shape2ActorPos, + w, v, p); + + V3StoreA(p, outTransform.p); + V4StoreA(V4SetW(v,w), &outTransform.q.x); +} + + +PX_FORCE_INLINE void getDynamicGlobalPoseAligned(const PxTransform& body2World, const PxTransform& shape2Actor, const PxTransform& body2Actor, PxTransform& outTransform) +{ + PX_ASSERT((size_t(&body2World)&15) == 0); + PX_ASSERT((size_t(&shape2Actor)&15) == 0); + PX_ASSERT((size_t(&body2Actor)&15) == 0); + PX_ASSERT((size_t(&outTransform)&15) == 0); + + using namespace shdfnd::aos; + + const Vec3V shape2ActorPos = V3LoadA(shape2Actor.p); + const QuatV shape2ActorRot = QuatVLoadA(&shape2Actor.q.x); + + const Vec3V body2ActorPos = V3LoadA(body2Actor.p); + const QuatV body2ActorRot = QuatVLoadA(&body2Actor.q.x); + + const Vec3V body2WorldPos = V3LoadA(body2World.p); + const QuatV body2WorldRot = QuatVLoadA(&body2World.q.x); + + Vec3V v1, p1, v2, p2; + FloatV w1, w2; + + transformInvFast(V4GetW(body2ActorRot), Vec3V_From_Vec4V(body2ActorRot), body2ActorPos, + V4GetW(shape2ActorRot), Vec3V_From_Vec4V(shape2ActorRot), shape2ActorPos, + w1, v1, p1); + + transformFast(V4GetW(body2WorldRot), Vec3V_From_Vec4V(body2WorldRot), body2WorldPos, + w1, v1, p1, + w2, v2, p2); + + V3StoreA(p2, outTransform.p); + V4StoreA(V4SetW(v2, w2), &outTransform.q.x); +} + + +} +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmUtils.h b/src/PhysX/physx/source/common/src/CmUtils.h new file mode 100644 index 000000000..c9480bef7 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmUtils.h @@ -0,0 +1,311 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_COMMON_UTILS +#define PX_PHYSICS_COMMON_UTILS + + +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "foundation/PxBounds3.h" +#include "CmPhysXCommon.h" +#include "PxBase.h" +#include "PsInlineArray.h" +#include "PsArray.h" +#include "PsAllocator.h" + +namespace physx +{ +namespace Cm +{ + +template +PX_FORCE_INLINE PxU32 getArrayOfPointers(DstType** PX_RESTRICT userBuffer, PxU32 bufferSize, PxU32 startIndex, SrcType*const* PX_RESTRICT src, PxU32 size) +{ + const PxU32 remainder = PxU32(PxMax(PxI32(size - startIndex), 0)); + const PxU32 writeCount = PxMin(remainder, bufferSize); + src += startIndex; + for(PxU32 i=0;i(src[i]); + return writeCount; +} + +PX_INLINE void transformInertiaTensor(const PxVec3& invD, const PxMat33& M, PxMat33& mIInv) +{ + const float axx = invD.x*M(0,0), axy = invD.x*M(1,0), axz = invD.x*M(2,0); + const float byx = invD.y*M(0,1), byy = invD.y*M(1,1), byz = invD.y*M(2,1); + const float czx = invD.z*M(0,2), czy = invD.z*M(1,2), czz = invD.z*M(2,2); + + mIInv(0,0) = axx*M(0,0) + byx*M(0,1) + czx*M(0,2); + mIInv(1,1) = axy*M(1,0) + byy*M(1,1) + czy*M(1,2); + mIInv(2,2) = axz*M(2,0) + byz*M(2,1) + czz*M(2,2); + + mIInv(0,1) = mIInv(1,0) = axx*M(1,0) + byx*M(1,1) + czx*M(1,2); + mIInv(0,2) = mIInv(2,0) = axx*M(2,0) + byx*M(2,1) + czx*M(2,2); + mIInv(1,2) = mIInv(2,1) = axy*M(2,0) + byy*M(2,1) + czy*M(2,2); +} + +// PT: TODO: refactor this with PxBounds3 header +PX_FORCE_INLINE PxVec3 basisExtent(const PxVec3& basis0, const PxVec3& basis1, const PxVec3& basis2, const PxVec3& extent) +{ + // extended basis vectors + const PxVec3 c0 = basis0 * extent.x; + const PxVec3 c1 = basis1 * extent.y; + const PxVec3 c2 = basis2 * extent.z; + + // find combination of base vectors that produces max. distance for each component = sum of abs() + return PxVec3 ( PxAbs(c0.x) + PxAbs(c1.x) + PxAbs(c2.x), + PxAbs(c0.y) + PxAbs(c1.y) + PxAbs(c2.y), + PxAbs(c0.z) + PxAbs(c1.z) + PxAbs(c2.z)); +} + +PX_FORCE_INLINE PxBounds3 basisExtent(const PxVec3& center, const PxVec3& basis0, const PxVec3& basis1, const PxVec3& basis2, const PxVec3& extent) +{ + const PxVec3 w = basisExtent(basis0, basis1, basis2, extent); + return PxBounds3(center - w, center + w); +} + +PX_FORCE_INLINE bool isValid(const PxVec3& c, const PxVec3& e) +{ + return (c.isFinite() && e.isFinite() && (((e.x >= 0.0f) && (e.y >= 0.0f) && (e.z >= 0.0f)) || + ((e.x == -PX_MAX_BOUNDS_EXTENTS) && + (e.y == -PX_MAX_BOUNDS_EXTENTS) && + (e.z == -PX_MAX_BOUNDS_EXTENTS)))); +} + +PX_FORCE_INLINE bool isEmpty(const PxVec3& c, const PxVec3& e) +{ + PX_UNUSED(c); + PX_ASSERT(isValid(c, e)); + return e.x<0.0f; +} + +// Array with externally managed storage. +// Allocation and resize policy are managed by the owner, +// Very minimal functionality right now, just POD types + +template +class OwnedArray +{ +public: + OwnedArray() + : mData(0) + , mCapacity(0) + , mSize(0) + {} + + ~OwnedArray() // owner must call releaseMem before destruction + { + PX_ASSERT(mCapacity==0); + } + + void pushBack(T& element, Owner& owner) + { + // there's a failure case if here if we push an existing element which causes a resize - + // a rare case not worth coding around; if you need it, copy the element then push it. + + PX_ASSERT(&element=mData+mSize); + if(mSize==mCapacity) + (owner.*realloc)(mData, mCapacity, mSize, IndexType(mSize+1)); + + PX_ASSERT(mData && mSize=mCapacity) + (owner.*realloc)(mData, mCapacity, mSize, capacity); + } + + void releaseMem(Owner &owner) + { + mSize = 0; + (owner.*realloc)(mData, mCapacity, 0, 0); + } + +private: + T* mData; + IndexType mCapacity; + IndexType mSize; + + // just in case someone tries to use a non-POD in here + union FailIfNonPod + { + T t; + int x; + }; +}; + +/** +Any object deriving from PxBase needs to call this function instead of 'delete object;'. + +We don't want implement 'operator delete' in PxBase because that would impose how +memory of derived classes is allocated. Even though most or all of the time derived classes will +be user allocated, we don't want to put UserAllocatable into the API and derive from that. +*/ +template +PX_INLINE void deletePxBase(T* object) +{ + if(object->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + PX_DELETE(object); + else + object->~T(); +} + +#if PX_CHECKED +/** +Mark a specified amount of memory with 0xcd pattern. This is used to check that the meta data +definition for serialized classes is complete in checked builds. +*/ +PX_INLINE void markSerializedMem(void* ptr, PxU32 byteSize) +{ + for (PxU32 i = 0; i < byteSize; ++i) + reinterpret_cast(ptr)[i] = 0xcd; +} + +/** +Macro to instantiate a type for serialization testing. +Note: Only use PX_NEW_SERIALIZED once in a scope. +*/ +#define PX_NEW_SERIALIZED(v,T) \ + void* _buf = physx::shdfnd::ReflectionAllocator().allocate(sizeof(T),__FILE__,__LINE__); \ + Cm::markSerializedMem(_buf, sizeof(T)); \ + v = PX_PLACEMENT_NEW(_buf, T) + +#else +PX_INLINE void markSerializedMem(void*, PxU32){} + +#define PX_NEW_SERIALIZED(v,T) v = PX_NEW(T) +#endif + +template +struct ArrayAccess: public Ps::Array +{ + void store(PxSerializationContext& context) const + { + if(this->mData && (this->mSize || this->capacity())) + context.writeData(this->mData, this->capacity()*sizeof(T)); + } + + void load(PxDeserializationContext& context) + { + if(this->mData && (this->mSize || this->capacity())) + this->mData = context.readExtraData(this->capacity()); + } +}; + +template +void exportArray(const Ps::Array& a, PxSerializationContext& context) +{ + static_cast&>(a).store(context); +} + +template +void importArray(Ps::Array& a, PxDeserializationContext& context) +{ + static_cast&>(a).load(context); +} + +template +void exportInlineArray(const Ps::InlineArray& a, PxSerializationContext& context) +{ + if(!a.isInlined()) + Cm::exportArray(a, context); +} + +template +void importInlineArray(Ps::InlineArray& a, PxDeserializationContext& context) +{ + if(!a.isInlined()) + Cm::importArray(a, context); +} + +template +static PX_INLINE T* reserveContainerMemory(Ps::Array& container, PxU32 nb) +{ + const PxU32 maxNbEntries = container.capacity(); + const PxU32 requiredSize = container.size() + nb; + + if(requiredSize>maxNbEntries) + { + const PxU32 naturalGrowthSize = maxNbEntries ? maxNbEntries*2 : 2; + const PxU32 newSize = PxMax(requiredSize, naturalGrowthSize); + container.reserve(newSize); + } + + T* buf = container.end(); + container.forceSize_Unsafe(requiredSize); + return buf; +} + +} // namespace Cm + + + +} + +#endif diff --git a/src/PhysX/physx/source/common/src/CmVisualization.cpp b/src/PhysX/physx/source/common/src/CmVisualization.cpp new file mode 100644 index 000000000..70a54e905 --- /dev/null +++ b/src/PhysX/physx/source/common/src/CmVisualization.cpp @@ -0,0 +1,137 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxTransform.h" +#include "CmPhysXCommon.h" +#include "CmRenderOutput.h" +#include "CmVisualization.h" + +using namespace physx; +using namespace Cm; + +void Cm::visualizeJointFrames(RenderOutput& out, PxReal scale, const PxTransform& parent, const PxTransform& child) +{ + if(scale==0.0f) + return; + + out << parent << Cm::DebugBasis(PxVec3(scale, scale, scale) * 1.5f, + PxU32(PxDebugColor::eARGB_DARKRED), PxU32(PxDebugColor::eARGB_DARKGREEN), PxU32(PxDebugColor::eARGB_DARKBLUE)); + out << child << Cm::DebugBasis(PxVec3(scale, scale, scale)); +} + +void Cm::visualizeLinearLimit(RenderOutput& out, PxReal scale, const PxTransform& t0, const PxTransform& /*t1*/, PxReal value, bool active) +{ + if(scale==0.0f) + return; + + // debug circle is around z-axis, and we want it around x-axis + PxTransform r(t0.p+value*t0.q.getBasisVector0(), t0.q*PxQuat(PxPi/2,PxVec3(0,1.f,0))); + out << (active ? PxDebugColor::eARGB_RED : PxDebugColor::eARGB_GREY); + out << PxTransform(PxIdentity); + out << Cm::DebugArrow(t0.p,r.p-t0.p); + + out << r << Cm::DebugCircle(20, scale*0.3f); +} + +void Cm::visualizeAngularLimit(RenderOutput& out, PxReal scale, const PxTransform& t, PxReal lower, PxReal upper, bool active) +{ + if(scale==0.0f) + return; + + out << t << (active ? PxDebugColor::eARGB_RED : PxDebugColor::eARGB_GREY); + + out << Cm::RenderOutput::LINES + << PxVec3(0) << PxVec3(0, PxCos(lower), PxSin(lower)) * scale + << PxVec3(0) << PxVec3(0, PxCos(upper), PxSin(upper)) * scale; + + out << Cm::RenderOutput::LINESTRIP; + PxReal angle = lower, step = (upper-lower)/20; + + for(PxU32 i=0; i<=20; i++, angle += step) + out << PxVec3(0, PxCos(angle), PxSin(angle)) * scale; +} + +void Cm::visualizeLimitCone(RenderOutput& out, PxReal scale, const PxTransform& t, PxReal tanQSwingY, PxReal tanQSwingZ, bool active) +{ + if(scale==0.0f) + return; + + out << t << (active ? PxDebugColor::eARGB_RED : PxDebugColor::eARGB_GREY); + out << Cm::RenderOutput::LINES; + + PxVec3 prev(0,0,0); + + const PxU32 LINES = 32; + + for(PxU32 i=0;i<=LINES;i++) + { + PxReal angle = 2*PxPi/LINES*i; + PxReal c = PxCos(angle), s = PxSin(angle); + PxVec3 rv(0,-tanQSwingZ*s, tanQSwingY*c); + PxReal rv2 = rv.magnitudeSquared(); + PxQuat q = PxQuat(0,2*rv.y,2*rv.z,1-rv2) * (1/(1+rv2)); + PxVec3 a = q.rotate(PxVec3(1.0f,0,0)) * scale; + + out << prev << a << PxVec3(0) << a; + prev = a; + } +} + +void Cm::visualizeDoubleCone(Cm::RenderOutput& out, PxReal scale, const PxTransform& t, PxReal angle, bool active) +{ + if(scale==0.0f) + return; + + out << t << (active ? PxDebugColor::eARGB_RED : PxDebugColor::eARGB_GREY); + + const PxReal height = PxTan(angle); + + const PxU32 LINES = 32; + + out << Cm::RenderOutput::LINESTRIP; + + const PxReal step = PxPi*2/LINES; + + for(PxU32 i=0; i<=LINES; i++) + out << PxVec3(height, PxCos(step * i), PxSin(step * i)) * scale; + + angle = 0; + out << Cm::RenderOutput::LINESTRIP; + for(PxU32 i=0; i<=LINES; i++, angle += PxPi*2/LINES) + out << PxVec3(-height, PxCos(step * i), PxSin(step * i)) * scale; + + angle = 0; + out << Cm::RenderOutput::LINES; + for(PxU32 i=0;i + +static const physx::PxDelayLoadHook* gDelayLoadHook = NULL; + +void physx::PxSetPhysXCommonDelayLoadHook(const physx::PxDelayLoadHook* hook) +{ + gDelayLoadHook = hook; +} + +using namespace physx; + +#pragma comment(lib, "delayimp") + +FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli) +{ + switch (dliNotify) { + case dliStartProcessing : + break; + + case dliNotePreLoadLibrary : + { + return Cm::physXCommonDliNotePreLoadLibrary(pdli->szDll,gDelayLoadHook); + } + break; + + case dliNotePreGetProcAddress : + break; + + case dliFailLoadLib : + break; + + case dliFailGetProc : + break; + + case dliNoteEndProcessing : + break; + + default : + + return NULL; + } + + return NULL; +} + +PfnDliHook __pfnDliNotifyHook2 = delayHook; diff --git a/src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp b/src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp new file mode 100644 index 000000000..b10cba8d8 --- /dev/null +++ b/src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsFoundation.h" + +#ifdef SUPPORT_UPDATE_LOADER_LOGGING +#if PX_X86 +#define NX_USE_SDK_DLLS +#include "PhysXUpdateLoader.h" +#endif +#endif /* SUPPORT_UPDATE_LOADER_LOGGING */ + +#include "windows/CmWindowsModuleUpdateLoader.h" +#include "windows/CmWindowsLoadLibrary.h" + + +namespace physx { namespace Cm { + +#if PX_VC +#pragma warning(disable: 4191) //'operator/operation' : unsafe conversion from 'type of expression' to 'type required' +#endif + + +typedef HMODULE (*GetUpdatedModule_FUNC)(const char*, const char*); + +#ifdef SUPPORT_UPDATE_LOADER_LOGGING +#if PX_X86 +typedef void (*setLogging_FUNC)(PXUL_ErrorCode, pt2LogFunc); + +static void LogMessage(PXUL_ErrorCode messageType, char* message) +{ + switch(messageType) + { + case PXUL_ERROR_MESSAGES: + getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PhysX Update Loader Error: %s.", message); + break; + case PXUL_WARNING_MESSAGES: + getFoundation().error(PX_WARN, "PhysX Update Loader Warning: %s.", message); + break; + case PXUL_INFO_MESSAGES: + getFoundation().error(PX_INFO, "PhysX Update Loader Information: %s.", message); + break; + default: + getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Unknown message type from update loader."); + break; + } +} +#endif +#endif /* SUPPORT_UPDATE_LOADER_LOGGING */ + +CmModuleUpdateLoader::CmModuleUpdateLoader(const char* updateLoaderDllName) + : mGetUpdatedModuleFunc(NULL) +{ + mUpdateLoaderDllHandle = loadLibrary(updateLoaderDllName); + + if (mUpdateLoaderDllHandle != NULL) + { + mGetUpdatedModuleFunc = GetProcAddress(mUpdateLoaderDllHandle, "GetUpdatedModule"); + +#ifdef SUPPORT_UPDATE_LOADER_LOGGING +#if PX_X86 + setLogging_FUNC setLoggingFunc; + setLoggingFunc = (setLogging_FUNC)GetProcAddress(mUpdateLoaderDllHandle, "setLoggingFunction"); + if(setLoggingFunc != NULL) + { + setLoggingFunc(PXUL_ERROR_MESSAGES, LogMessage); + } +#endif +#endif /* SUPPORT_UPDATE_LOADER_LOGGING */ + } +} + +CmModuleUpdateLoader::~CmModuleUpdateLoader() +{ + if (mUpdateLoaderDllHandle != NULL) + { + FreeLibrary(mUpdateLoaderDllHandle); + mUpdateLoaderDllHandle = NULL; + } +} + +HMODULE CmModuleUpdateLoader::LoadModule(const char* moduleName, const char* appGUID) +{ + HMODULE result = NULL; + + if (mGetUpdatedModuleFunc != NULL) + { + // Try to get the module through PhysXUpdateLoader + GetUpdatedModule_FUNC getUpdatedModuleFunc = (GetUpdatedModule_FUNC)mGetUpdatedModuleFunc; + result = getUpdatedModuleFunc(moduleName, appGUID); + } + else + { + // If no PhysXUpdateLoader, just load the DLL directly + result = loadLibrary(moduleName); + } + + return result; +} + +}; // end of namespace +}; // end of namespace diff --git a/src/PhysX/physx/source/compiler/cmake/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/CMakeLists.txt new file mode 100644 index 000000000..f8747b862 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/CMakeLists.txt @@ -0,0 +1,142 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +cmake_minimum_required(VERSION 3.7) + +project(PhysX C CXX) + +CMAKE_POLICY(SET CMP0057 NEW) # Enable IN_LIST + +OPTION(PX_GENERATE_GPU_PROJECTS "Generate the GPU projects, if possible." OFF) +OPTION(PX_GENERATE_GPU_PROJECTS_ONLY "Generate ONLY the GPU projects, if possible." OFF) +OPTION(PX_GENERATE_GPU_STATIC_LIBRARIES "Generate PhysXGPU static libraries" OFF) +OPTION(PX_SCALAR_MATH "Disable SIMD math" OFF) +OPTION(PX_GENERATE_STATIC_LIBRARIES "Generate static libraries" OFF) +OPTION(PX_EXPORT_LOWLEVEL_PDB "Export low level pdb's" OFF) + +IF(NOT DEFINED PHYSX_ROOT_DIR) + + STRING(REPLACE "\\" "/" BRD_TEMP $ENV{PHYSX_ROOT_DIR}) + + # This env variable is set by GenerateProjects.bat, and is no longer available when CMake rebuilds, so this stores it in the cache + SET(PHYSX_ROOT_DIR ${BRD_TEMP} CACHE INTERNAL "Root of the PhysX source tree") + +ENDIF() + +MESSAGE("PHYSX ROOT ${PHYSX_ROOT_DIR}") + +IF(NOT EXISTS ${PHYSX_ROOT_DIR}) + MESSAGE(FATAL_ERROR "PHYSX_ROOT_DIR environment variable wasn't set or was invalid.") +ENDIF() + +IF(NOT DEFINED CMAKEMODULES_VERSION) + SET(CMAKEMODULES_PATH $ENV{PM_CMakeModules_PATH} CACHE INTERNAL "Path to CMakeModules") + SET(CMAKEMODULES_NAME $ENV{PM_CMakeModules_NAME} CACHE INTERNAL "CMakeModules name") + SET(CMAKEMODULES_VERSION $ENV{PM_CMakeModules_VERSION} CACHE INTERNAL "CMakeModules version from generation batch") +ENDIF() + +#TODO: More elegance +IF(NOT EXISTS ${CMAKEMODULES_PATH}) + MESSAGE(FATAL_ERROR "Could not find ${CMAKEMODULES_PATH}") +ENDIF() + +SET(CMAKE_MODULE_PATH ${CMAKEMODULES_PATH}) + + +MESSAGE("PhysX Build Platform: " ${TARGET_BUILD_PLATFORM}) +MESSAGE("Using CXX Compiler: " ${CMAKE_CXX_COMPILER}) + +INCLUDE(NvidiaBuildOptions) + +IF (NOT EXISTS ${PXSHARED_PATH}) + IF (NOT EXISTS $ENV{PM_PxShared_PATH}) + MESSAGE(FATAL_ERROR "Trying to set PxShared path, but PM_PxShared_PATH is not valid: '$ENV{PM_PxShared_PATH}'.") + ENDIF() + SET(PXSHARED_PATH_TMP $ENV{PM_PxShared_PATH}) + + STRING(REPLACE "\\" "/" PXSHARED_PATH_TMP ${PXSHARED_PATH_TMP}) + SET(PXSHARED_PATH ${PXSHARED_PATH_TMP} CACHE INTERNAL "Path to PxShared") + + STRING(REGEX REPLACE "/PhysX$" "/PxShared" PXSHARED_INSTALL_PREFIX_TMP ${CMAKE_INSTALL_PREFIX}) + SET(PXSHARED_INSTALL_PREFIX ${PXSHARED_INSTALL_PREFIX_TMP} CACHE INTERNAL "Path to PxShared") +ENDIF() + +IF(CMAKE_CONFIGURATION_TYPES) + SET(CMAKE_CONFIGURATION_TYPES debug checked profile release) + SET(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING + "Reset config to what we need" + FORCE) + + SET(CMAKE_SHARED_LINKER_FLAGS_CHECKED "") + SET(CMAKE_SHARED_LINKER_FLAGS_PROFILE "") + + # Build PDBs for all configurations + SET(CMAKE_SHARED_LINKER_FLAGS "/DEBUG") + +ENDIF() + +# Prevent failure due to command line limitations +IF(USE_RESPONSE_FILES) + SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1) + SET(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1) + SET(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES 1) + SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1) + SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1) + SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_LIBRARIES 1) +ENDIF() + +IF($ENV{PHYSX_AUTOBUILD}) + IF($ENV{PHYSX_AUTOBUILD} STREQUAL "1") + SET(PHYSX_AUTOBUILD "PHYSX_AUTOBUILD") + ENDIF() +ENDIF() + +IF($ENV{AQUA_BUILD_CHANGELIST}) + SET(PHYSX_AUTOBUILDNUMBER PX_BUILD_NUMBER=$ENV{AQUA_BUILD_CHANGELIST}) +ELSE() + SET(PHYSX_AUTOBUILDNUMBER PX_BUILD_NUMBER=0) +ENDIF() + +SET(PROJECT_CMAKE_FILES_DIR source/compiler/cmake) +SET(PLATFORM_CMAKELISTS ${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/CMakeLists.txt) + +IF(NOT EXISTS ${PLATFORM_CMAKELISTS}) + MESSAGE(FATAL_ERROR "Unable to find platform CMakeLists.txt for ${TARGET_BUILD_PLATFORM} at ${PLATFORM_CMAKELISTS}") +ENDIF() + +SET(CMAKE_POSITION_INDEPENDENT_CODE ON) + +SET(SOURCE_DISTRO_FILE_LIST "") + +# Include the platform specific CMakeLists +INCLUDE(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/CMakeLists.txt) + +IF(PX_GENERATE_SOURCE_DISTRO) + FOREACH(FILE_NAME ${SOURCE_DISTRO_FILE_LIST}) + FILE(APPEND "${CMAKE_CURRENT_BINARY_DIR}/source_distro_list.txt" "${FILE_NAME}\n") + ENDFOREACH() +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/FastXml.cmake new file mode 100644 index 000000000..4ac700275 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/FastXml.cmake @@ -0,0 +1,92 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml common +# + + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/fastxml) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/FastXml.cmake) + + +SET(FASTXML_HEADERS + ${LL_SOURCE_DIR}/include/PsFastXml.h +) +SOURCE_GROUP(include FILES ${FASTXML_HEADERS}) + +SET(FASTXML_SOURCE + ${LL_SOURCE_DIR}/src/PsFastXml.cpp +) +SOURCE_GROUP(src FILES ${FASTXML_SOURCE}) + +ADD_LIBRARY(FastXml ${FASTXML_LIBTYPE} + ${FASTXML_HEADERS} + ${FASTXML_SOURCE} +) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(FastXml + PRIVATE ${PLATFORM_INCLUDES} + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + PRIVATE ${LL_SOURCE_DIR}/include +) + +TARGET_COMPILE_DEFINITIONS(FastXml + PRIVATE ${FASTXML_COMPILE_DEFS} +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND FASTXML_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(FastXml PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "FastXml_static" + ARCHIVE_OUTPUT_NAME_CHECKED "FastXml_static" + ARCHIVE_OUTPUT_NAME_PROFILE "FastXml_static" + ARCHIVE_OUTPUT_NAME_RELEASE "FastXml_static" + ) +ENDIF() + +IF(FASTXML_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(FastXml PROPERTIES + COMPILE_PDB_NAME_DEBUG "${FASTXML_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${FASTXML_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${FASTXML_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${FASTXML_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${FASTXML_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${FASTXML_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(FastXml PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + diff --git a/src/PhysX/physx/source/compiler/cmake/IfTemplate.txt b/src/PhysX/physx/source/compiler/cmake/IfTemplate.txt new file mode 100644 index 000000000..5561e9ae7 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/IfTemplate.txt @@ -0,0 +1,32 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +IF(TARGET_BUILD_PLATFORM STREQUAL "Windows") +ELSEIF(TARGET_BUILD_PLATFORM STREQUAL "PS4") +ELSEIF(TARGET_BUILD_PLATFORM STREQUAL "XboxOne") +ELSEIF(TARGET_BUILD_PLATFORM STREQUAL "Unix") +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/LowLevel.cmake new file mode 100644 index 000000000..8b97e6b98 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/LowLevel.cmake @@ -0,0 +1,235 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/lowlevel) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/LowLevel.cmake) + + +SET(LL_API_DIR ${LL_SOURCE_DIR}/api/) +SET(LL_API_HEADERS + ${LL_API_DIR}/include/PxsMaterialCore.h + ${LL_API_DIR}/include/PxsMaterialManager.h + ${LL_API_DIR}/include/PxvConfig.h + ${LL_API_DIR}/include/PxvDynamics.h + ${LL_API_DIR}/include/PxvGeometry.h + ${LL_API_DIR}/include/PxvGlobals.h + ${LL_API_DIR}/include/PxvManager.h + ${LL_API_DIR}/include/PxvSimStats.h +) +SOURCE_GROUP("API Includes" FILES ${LL_API_HEADERS}) + +SET(LL_API_SOURCE + ${LL_API_DIR}/src/px_globals.cpp +) +SOURCE_GROUP("API Source" FILES ${LL_API_SOURCE}) + +SET(LL_COMMON_DIR ${LL_SOURCE_DIR}/common/) +SET(LL_COMMON_COLLISION_HEADERS + ${LL_COMMON_DIR}/include/collision/PxcContactMethodImpl.h +) +SOURCE_GROUP("Common Includes\\collision" FILES ${LL_COMMON_COLLISION_HEADERS}) +SET(LL_COMMON_PIPELINE_HEADERS + ${LL_COMMON_DIR}/include/pipeline/PxcCCDStateStreamPair.h + ${LL_COMMON_DIR}/include/pipeline/PxcConstraintBlockStream.h + ${LL_COMMON_DIR}/include/pipeline/PxcContactCache.h + ${LL_COMMON_DIR}/include/pipeline/PxcMaterialMethodImpl.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpBatch.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpCache.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpCacheStreamPair.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpContactPrepShared.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpMemBlockPool.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpThreadContext.h + ${LL_COMMON_DIR}/include/pipeline/PxcNpWorkUnit.h + ${LL_COMMON_DIR}/include/pipeline/PxcRigidBody.h +) +SOURCE_GROUP("Common Includes\\pipeline" FILES ${LL_COMMON_PIPELINE_HEADERS}) +SET(LL_COMMON_UTILS_HEADERS + ${LL_COMMON_DIR}/include/utils/PxcScratchAllocator.h + ${LL_COMMON_DIR}/include/utils/PxcThreadCoherentCache.h +) +SOURCE_GROUP("Common Includes\\utils" FILES ${LL_COMMON_UTILS_HEADERS}) + +SET(LL_COMMON_COLLISION_SOURCE + ${LL_COMMON_DIR}/src/collision/PxcContact.cpp +) +SOURCE_GROUP("Common Source\\collision" FILES ${LL_COMMON_COLLISION_SOURCE}) +SET(LL_COMMON_PIPELINE_SOURCE + ${LL_COMMON_DIR}/src/pipeline/PxcContactCache.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcContactMethodImpl.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcMaterialHeightField.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcMaterialMesh.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcMaterialMethodImpl.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcMaterialShape.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcNpBatch.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcNpCacheStreamPair.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcNpContactPrepShared.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcNpMemBlockPool.cpp + ${LL_COMMON_DIR}/src/pipeline/PxcNpThreadContext.cpp +) +SOURCE_GROUP("Common Source\\pipeline" FILES ${LL_COMMON_PIPELINE_SOURCE}) + +SET(LL_SOFTWARE_DIR ${LL_SOURCE_DIR}/software/) +SET(LL_SOFTWARE_HEADERS + ${LL_SOFTWARE_DIR}/include/PxsBodySim.h + ${LL_SOFTWARE_DIR}/include/PxsCCD.h + ${LL_SOFTWARE_DIR}/include/PxsContactManager.h + ${LL_SOFTWARE_DIR}/include/PxsContactManagerState.h + ${LL_SOFTWARE_DIR}/include/PxsContext.h + ${LL_SOFTWARE_DIR}/include/PxsDefaultMemoryManager.h + ${LL_SOFTWARE_DIR}/include/PxsHeapMemoryAllocator.h + ${LL_SOFTWARE_DIR}/include/PxsIncrementalConstraintPartitioning.h + ${LL_SOFTWARE_DIR}/include/PxsIslandManagerTypes.h + ${LL_SOFTWARE_DIR}/include/PxsIslandSim.h + ${LL_SOFTWARE_DIR}/include/PxsIslandNodeIndex.h + ${LL_SOFTWARE_DIR}/include/PxsKernelWrangler.h + ${LL_SOFTWARE_DIR}/include/PxsMaterialCombiner.h + ${LL_SOFTWARE_DIR}/include/PxsMemoryManager.h + ${LL_SOFTWARE_DIR}/include/PxsNphaseImplementationContext.h + ${LL_SOFTWARE_DIR}/include/PxsRigidBody.h + ${LL_SOFTWARE_DIR}/include/PxsShapeSim.h + ${LL_SOFTWARE_DIR}/include/PxsSimpleIslandManager.h + ${LL_SOFTWARE_DIR}/include/PxsSimulationController.h + ${LL_SOFTWARE_DIR}/include/PxsTransformCache.h + ${LL_SOFTWARE_DIR}/include/PxvNphaseImplementationContext.h +) +SOURCE_GROUP("Software Includes" FILES ${LL_SOFTWARE_HEADERS}) +SET(LL_SOFTWARE_SOURCE + ${LL_SOFTWARE_DIR}/src/PxsCCD.cpp + ${LL_SOFTWARE_DIR}/src/PxsContactManager.cpp + ${LL_SOFTWARE_DIR}/src/PxsContext.cpp + ${LL_SOFTWARE_DIR}/src/PxsDefaultMemoryManager.cpp + ${LL_SOFTWARE_DIR}/src/PxsIslandSim.cpp + ${LL_SOFTWARE_DIR}/src/PxsMaterialCombiner.cpp + ${LL_SOFTWARE_DIR}/src/PxsNphaseImplementationContext.cpp + ${LL_SOFTWARE_DIR}/src/PxsSimpleIslandManager.cpp +) +SOURCE_GROUP("Software Source" FILES ${LL_SOFTWARE_SOURCE}) + +ADD_LIBRARY(LowLevel ${LOWLEVEL_LIBTYPE} + ${LL_API_HEADERS} + ${LL_API_SOURCE} + + ${LL_COMMON_COLLISION_HEADERS} + ${LL_COMMON_COLLISION_SOURCE} + + ${LL_COMMON_PIPELINE_HEADERS} + ${LL_COMMON_PIPELINE_SOURCE} + + ${LL_COMMON_UTILS_HEADERS} + + ${LL_SOFTWARE_HEADERS} + ${LL_SOFTWARE_SOURCE} +) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(LowLevel + PRIVATE ${LOWLEVEL_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/collision + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/utils + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/software/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowleveldynamics/include + +) + +TARGET_COMPILE_DEFINITIONS(LowLevel + PRIVATE ${LOWLEVEL_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(LowLevel PROPERTIES + LINK_FLAGS ${LOWLEVEL_PLATFORM_LINK_FLAGS} +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) + SET_TARGET_PROPERTIES(LowLevel PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "LowLevel_static" + ARCHIVE_OUTPUT_NAME_CHECKED "LowLevel_static" + ARCHIVE_OUTPUT_NAME_PROFILE "LowLevel_static" + ARCHIVE_OUTPUT_NAME_RELEASE "LowLevel_static" + ) +ENDIF() + +IF(LOWLEVEL_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(LowLevel PROPERTIES + COMPILE_PDB_NAME_DEBUG "${LOWLEVEL_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${LOWLEVEL_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${LOWLEVEL_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${LOWLEVEL_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_SOFTWARE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_SOFTWARE_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_API_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_API_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_COMMON_COLLISION_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_COMMON_COLLISION_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_COMMON_PIPELINE_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_COMMON_PIPELINE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LL_COMMON_UTILS_HEADERS}) +ENDIF() + +SET_TARGET_PROPERTIES(LowLevel PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake new file mode 100644 index 000000000..1a655d664 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake @@ -0,0 +1,131 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LLAABB_DIR ${PHYSX_SOURCE_DIR}/lowlevelaabb) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/LowLevelAABB.cmake) + + +SET(LLAABB_HEADERS + ${LLAABB_DIR}/include/BpAABBManager.h + ${LLAABB_DIR}/include/BpAABBManagerTasks.h + ${LLAABB_DIR}/include/BpBroadPhase.h + ${LLAABB_DIR}/include/BpBroadPhaseUpdate.h +) +SOURCE_GROUP("includes" FILES ${LLAABB_HEADERS}) + +SET(LLAABB_SOURCE + ${LLAABB_DIR}/src/BpAABBManager.cpp + ${LLAABB_DIR}/src/BpBroadPhase.cpp + ${LLAABB_DIR}/src/BpBroadPhaseABP.cpp + ${LLAABB_DIR}/src/BpBroadPhaseABP.h + ${LLAABB_DIR}/src/BpBroadPhaseMBP.cpp + ${LLAABB_DIR}/src/BpBroadPhaseMBP.h + ${LLAABB_DIR}/src/BpBroadPhaseMBPCommon.h + ${LLAABB_DIR}/src/BpBroadPhaseSap.cpp + ${LLAABB_DIR}/src/BpBroadPhaseSap.h + ${LLAABB_DIR}/src/BpBroadPhaseSapAux.cpp + ${LLAABB_DIR}/src/BpBroadPhaseSapAux.h + ${LLAABB_DIR}/src/BpBroadPhaseShared.cpp + ${LLAABB_DIR}/src/BpBroadPhaseShared.h + ${LLAABB_DIR}/src/BpMBPTasks.cpp + ${LLAABB_DIR}/src/BpMBPTasks.h + ${LLAABB_DIR}/src/BpSAPTasks.cpp + ${LLAABB_DIR}/src/BpSAPTasks.h +) +SOURCE_GROUP("src" FILES ${LLAABB_SOURCE}) + +ADD_LIBRARY(LowLevelAABB ${LOWLEVELAABB_LIBTYPE} + ${LLAABB_HEADERS} + ${LLAABB_SOURCE} +) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(LowLevelAABB + PRIVATE ${LOWLEVELAABB_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/utils + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevelaabb/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevelaabb/src +) + +TARGET_COMPILE_DEFINITIONS(LowLevelAABB + PRIVATE ${LOWLEVELAABB_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(LowLevelAABB PROPERTIES + LINK_FLAGS ${LOWLEVELAABB_PLATFORM_LINK_FLAGS} +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) + SET_TARGET_PROPERTIES(LowLevelAABB PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "LowLevelAABB_static" + ARCHIVE_OUTPUT_NAME_CHECKED "LowLevelAABB_static" + ARCHIVE_OUTPUT_NAME_PROFILE "LowLevelAABB_static" + ARCHIVE_OUTPUT_NAME_RELEASE "LowLevelAABB_static" + ) +ENDIF() + +IF(LOWLEVELAABB_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(LowLevelAABB PROPERTIES + COMPILE_PDB_NAME_DEBUG "${LOWLEVELAABB_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${LOWLEVELAABB_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${LOWLEVELAABB_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${LOWLEVELAABB_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LLAABB_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LLAABB_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(LowLevelAABB PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake new file mode 100644 index 000000000..0069c413f --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake @@ -0,0 +1,202 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/lowleveldynamics/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/LowLevelDynamics.cmake) + + +SET(LLDYNAMICS_BASE_DIR ${PHYSX_ROOT_DIR}/source/lowleveldynamics) +SET(LLDYNAMICS_INCLUDES + ${LLDYNAMICS_BASE_DIR}/include/DyArticulationCore.h + ${LLDYNAMICS_BASE_DIR}/include/DyVArticulation.h + ${LLDYNAMICS_BASE_DIR}/include/DyArticulation.h + ${LLDYNAMICS_BASE_DIR}/include/DyFeatherstoneArticulation.h + ${LLDYNAMICS_BASE_DIR}/include/DyFeatherstoneArticulationJointData.h + ${LLDYNAMICS_BASE_DIR}/include/DyFeatherstoneArticulationUtils.h + ${LLDYNAMICS_BASE_DIR}/include/DyConstraint.h + ${LLDYNAMICS_BASE_DIR}/include/DyConstraintWriteBack.h + ${LLDYNAMICS_BASE_DIR}/include/DyContext.h + ${LLDYNAMICS_BASE_DIR}/include/DySleepingConfigulation.h + ${LLDYNAMICS_BASE_DIR}/include/DyThresholdTable.h + ${LLDYNAMICS_BASE_DIR}/include/DyArticulationJointCore.h +) +SOURCE_GROUP("Dynamics Includes" FILES ${LLDYNAMICS_INCLUDES}) + +SET(LLDYNAMICS_SOURCE + ${LLDYNAMICS_BASE_DIR}/src/DyArticulation.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationContactPrep.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationContactPrepPF.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationHelper.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationScalar.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationSIMD.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyFeatherstoneArticulation.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyFeatherstoneForwardDynamic.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyFeatherstoneInverseDynamic.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyConstraintPartition.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyConstraintSetup.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyConstraintSetupBlock.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrep.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrep4.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrep4PF.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrepPF.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyDynamics.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyFrictionCorrelation.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyRigidBodyToSolverBody.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraints.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraintsBlock.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverControl.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverControlPF.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverPFConstraints.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverPFConstraintsBlock.cpp + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraint1DStep.h + ${LLDYNAMICS_BASE_DIR}/src/DyThreadContext.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyThresholdTable.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyTGSDynamics.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyTGSPartition.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyTGSContactPrep.cpp + ${LLDYNAMICS_BASE_DIR}/src/DyTGSContactPrepBlock.cpp +) +SOURCE_GROUP("Dynamics Source" FILES ${LLDYNAMICS_SOURCE}) + +SET(LLDYNAMICS_INTERNAL_INCLUDES + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationContactPrep.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationFnsDebug.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationFnsScalar.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationFnsSimd.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationHelper.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationPImpl.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationReference.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationScalar.h + ${LLDYNAMICS_BASE_DIR}/src/DyArticulationUtils.h + ${LLDYNAMICS_BASE_DIR}/src/DyFeatherstoneArticulationLink.h + ${LLDYNAMICS_BASE_DIR}/src/DyBodyCoreIntegrator.h + ${LLDYNAMICS_BASE_DIR}/src/DyConstraintPartition.h + ${LLDYNAMICS_BASE_DIR}/src/DyConstraintPrep.h + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrep.h + ${LLDYNAMICS_BASE_DIR}/src/DyContactPrepShared.h + ${LLDYNAMICS_BASE_DIR}/src/DyContactReduction.h + ${LLDYNAMICS_BASE_DIR}/src/DyCorrelationBuffer.h + ${LLDYNAMICS_BASE_DIR}/src/DyDynamics.h + ${LLDYNAMICS_BASE_DIR}/src/DyFrictionPatch.h + ${LLDYNAMICS_BASE_DIR}/src/DyFrictionPatchStreamPair.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverBody.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraint1D.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraint1D4.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraintDesc.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraintExtShared.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraintsShared.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverConstraintTypes.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverContact.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverContact4.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverContactPF.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverContactPF4.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverContext.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverControl.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverControlPF.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverCore.h + ${LLDYNAMICS_BASE_DIR}/src/DySolverExt.h + ${LLDYNAMICS_BASE_DIR}/src/DySpatial.h + ${LLDYNAMICS_BASE_DIR}/src/DyThreadContext.h + ${LLDYNAMICS_BASE_DIR}/src/DyTGSPartition.h + ${LLDYNAMICS_BASE_DIR}/src/DyTGSDynamics.h +) +SOURCE_GROUP("Dynamics Internal Includes" FILES ${LLDYNAMICS_INTERNAL_INCLUDES}) + +ADD_LIBRARY(LowLevelDynamics ${LOWLEVELDYNAMICS_LIBTYPE} + ${LLDYNAMICS_INCLUDES} + ${LLDYNAMICS_SOURCE} + ${LLDYNAMICS_INTERNAL_INCLUDES} +) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(LowLevelDynamics + PRIVATE ${LOWLEVELDYNAMICS_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/utils + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/software/include + + PRIVATE ${PHYSX_SOURCE_DIR}/lowleveldynamics/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowleveldynamics/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include +) + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(LowLevelDynamics + + # Common to all configurations + PRIVATE ${LOWLEVELDYNAMICS_COMPILE_DEFS} +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) + SET_TARGET_PROPERTIES(LowLevelDynamics PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "LowLevelDynamics_static" + ARCHIVE_OUTPUT_NAME_CHECKED "LowLevelDynamics_static" + ARCHIVE_OUTPUT_NAME_PROFILE "LowLevelDynamics_static" + ARCHIVE_OUTPUT_NAME_RELEASE "LowLevelDynamics_static" + ) +ENDIF() + +IF(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(LowLevelDynamics PROPERTIES + COMPILE_PDB_NAME_DEBUG "${LOWLEVELDYNAMICS_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${LOWLEVELDYNAMICS_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${LOWLEVELDYNAMICS_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${LOWLEVELDYNAMICS_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LLDYNAMICS_INCLUDES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LLDYNAMICS_INTERNAL_INCLUDES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${LLDYNAMICS_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(LowLevelDynamics PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/PhysX.cmake new file mode 100644 index 000000000..a4628160e --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysX.cmake @@ -0,0 +1,382 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(PX_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physx/src) +SET(MD_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physxmetadata) + +SET(PHYSX_PLATFORM_LINK_FLAGS " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_DEBUG " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_CHECKED " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_PROFILE " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_RELEASE " ") + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysX.cmake) + +SET(PHYSX_HEADERS + ${PHYSX_ROOT_DIR}/include/PxActor.h + ${PHYSX_ROOT_DIR}/include/PxAggregate.h + ${PHYSX_ROOT_DIR}/include/PxArticulationReducedCoordinate.h + ${PHYSX_ROOT_DIR}/include/PxArticulationBase.h + ${PHYSX_ROOT_DIR}/include/PxArticulation.h + ${PHYSX_ROOT_DIR}/include/PxArticulationJoint.h + ${PHYSX_ROOT_DIR}/include/PxArticulationJointReducedCoordinate.h + ${PHYSX_ROOT_DIR}/include/PxArticulationLink.h + ${PHYSX_ROOT_DIR}/include/PxBatchQuery.h + ${PHYSX_ROOT_DIR}/include/PxBatchQueryDesc.h + ${PHYSX_ROOT_DIR}/include/PxBroadPhase.h + ${PHYSX_ROOT_DIR}/include/PxClient.h + ${PHYSX_ROOT_DIR}/include/PxConstraint.h + ${PHYSX_ROOT_DIR}/include/PxConstraintDesc.h + ${PHYSX_ROOT_DIR}/include/PxContact.h + ${PHYSX_ROOT_DIR}/include/PxContactModifyCallback.h + ${PHYSX_ROOT_DIR}/include/PxDeletionListener.h + ${PHYSX_ROOT_DIR}/include/PxFiltering.h + ${PHYSX_ROOT_DIR}/include/PxForceMode.h + ${PHYSX_ROOT_DIR}/include/PxImmediateMode.h + ${PHYSX_ROOT_DIR}/include/PxLockedData.h + ${PHYSX_ROOT_DIR}/include/PxMaterial.h + ${PHYSX_ROOT_DIR}/include/PxPhysics.h + ${PHYSX_ROOT_DIR}/include/PxPhysicsAPI.h + ${PHYSX_ROOT_DIR}/include/PxPhysicsSerialization.h + ${PHYSX_ROOT_DIR}/include/PxPhysicsVersion.h + ${PHYSX_ROOT_DIR}/include/PxPhysXConfig.h + ${PHYSX_ROOT_DIR}/include/PxPruningStructure.h + ${PHYSX_ROOT_DIR}/include/PxQueryFiltering.h + ${PHYSX_ROOT_DIR}/include/PxQueryReport.h + ${PHYSX_ROOT_DIR}/include/PxRigidActor.h + ${PHYSX_ROOT_DIR}/include/PxRigidBody.h + ${PHYSX_ROOT_DIR}/include/PxRigidDynamic.h + ${PHYSX_ROOT_DIR}/include/PxRigidStatic.h + ${PHYSX_ROOT_DIR}/include/PxScene.h + ${PHYSX_ROOT_DIR}/include/PxSceneDesc.h + ${PHYSX_ROOT_DIR}/include/PxSceneLock.h + ${PHYSX_ROOT_DIR}/include/PxShape.h + ${PHYSX_ROOT_DIR}/include/PxSimulationEventCallback.h + ${PHYSX_ROOT_DIR}/include/PxSimulationStatistics.h + ${PHYSX_ROOT_DIR}/include/PxVisualizationParameter.h +) +SOURCE_GROUP(include FILES ${PHYSX_HEADERS}) + +SET(PHYSX_COMMON_HEADERS + ${PHYSX_ROOT_DIR}/include/common/PxBase.h + ${PHYSX_ROOT_DIR}/include/common/PxCollection.h + ${PHYSX_ROOT_DIR}/include/common/PxCoreUtilityTypes.h + ${PHYSX_ROOT_DIR}/include/common/PxMetaData.h + ${PHYSX_ROOT_DIR}/include/common/PxMetaDataFlags.h + ${PHYSX_ROOT_DIR}/include/common/PxPhysicsInsertionCallback.h + ${PHYSX_ROOT_DIR}/include/common/PxPhysXCommonConfig.h + ${PHYSX_ROOT_DIR}/include/common/PxRenderBuffer.h + ${PHYSX_ROOT_DIR}/include/common/PxSerialFramework.h + ${PHYSX_ROOT_DIR}/include/common/PxSerializer.h + ${PHYSX_ROOT_DIR}/include/common/PxStringTable.h + ${PHYSX_ROOT_DIR}/include/common/PxTolerancesScale.h + ${PHYSX_ROOT_DIR}/include/common/PxTypeInfo.h + ${PHYSX_ROOT_DIR}/include/common/PxProfileZone.h +) +SOURCE_GROUP(include\\common FILES ${PHYSX_COMMON_HEADERS}) + +SET(PHYSX_PVD_HEADERS + ${PHYSX_ROOT_DIR}/include/pvd/PxPvdSceneClient.h + ${PHYSX_ROOT_DIR}/include/pvd/PxPvd.h + ${PHYSX_ROOT_DIR}/include/pvd/PxPvdTransport.h +) +SOURCE_GROUP(include\\pvd FILES ${PHYSX_PVD_HEADERS}) + +SET(PHYSX_COLLISION_HEADERS + ${PHYSX_ROOT_DIR}/include/collision/PxCollisionDefs.h +) +SOURCE_GROUP(include\\collision FILES ${PHYSX_COLLISION_HEADERS}) + +SET(PHYSX_SOLVER_HEADERS + ${PHYSX_ROOT_DIR}/include/solver/PxSolverDefs.h +) +SOURCE_GROUP(include\\collision FILES ${PHYSX_SOLVER_HEADERS}) + +SET(PHYSX_METADATA_HEADERS + ${MD_SOURCE_DIR}/core/include/PvdMetaDataDefineProperties.h + ${MD_SOURCE_DIR}/core/include/PvdMetaDataExtensions.h + ${MD_SOURCE_DIR}/core/include/PvdMetaDataPropertyVisitor.h + ${MD_SOURCE_DIR}/core/include/PxAutoGeneratedMetaDataObjectNames.h + ${MD_SOURCE_DIR}/core/include/PxAutoGeneratedMetaDataObjects.h + ${MD_SOURCE_DIR}/core/include/PxMetaDataCompare.h + ${MD_SOURCE_DIR}/core/include/PxMetaDataCppPrefix.h + ${MD_SOURCE_DIR}/core/include/PxMetaDataObjects.h + ${MD_SOURCE_DIR}/core/include/RepXMetaDataPropertyVisitor.h +) +SOURCE_GROUP(metadata\\include FILES ${PHYSX_METADATA_HEADERS}) + +SET(PHYSX_METADATA_SOURCE + ${MD_SOURCE_DIR}/core/src/PxAutoGeneratedMetaDataObjects.cpp + ${MD_SOURCE_DIR}/core/src/PxMetaDataObjects.cpp +) +SOURCE_GROUP(metadata\\src FILES ${PHYSX_METADATA_SOURCE}) + +SET(PHYSX_IMMEDIATEMODE_SOURCE + ${PHYSX_ROOT_DIR}/source/immediatemode/src/NpImmediateMode.cpp +) +SOURCE_GROUP(src\\immediatemode FILES ${PHYSX_IMMEDIATEMODE_SOURCE}) + +SET(PHYSX_BUFFERING_SOURCE + ${PX_SOURCE_DIR}/buffering/ScbActor.cpp + ${PX_SOURCE_DIR}/buffering/ScbAggregate.cpp + ${PX_SOURCE_DIR}/buffering/ScbBase.cpp + ${PX_SOURCE_DIR}/buffering/ScbMetaData.cpp + ${PX_SOURCE_DIR}/buffering/ScbScene.cpp + ${PX_SOURCE_DIR}/buffering/ScbScenePvdClient.cpp + ${PX_SOURCE_DIR}/buffering/ScbShape.cpp + ${PX_SOURCE_DIR}/buffering/ScbActor.h + ${PX_SOURCE_DIR}/buffering/ScbAggregate.h + ${PX_SOURCE_DIR}/buffering/ScbArticulation.h + ${PX_SOURCE_DIR}/buffering/ScbArticulationJoint.h + ${PX_SOURCE_DIR}/buffering/ScbBase.h + ${PX_SOURCE_DIR}/buffering/ScbBody.h + ${PX_SOURCE_DIR}/buffering/ScbConstraint.h + ${PX_SOURCE_DIR}/buffering/ScbDefs.h + ${PX_SOURCE_DIR}/buffering/ScbNpDeps.h + ${PX_SOURCE_DIR}/buffering/ScbRigidObject.h + ${PX_SOURCE_DIR}/buffering/ScbRigidStatic.h + ${PX_SOURCE_DIR}/buffering/ScbScene.h + ${PX_SOURCE_DIR}/buffering/ScbSceneBuffer.h + ${PX_SOURCE_DIR}/buffering/ScbScenePvdClient.h + ${PX_SOURCE_DIR}/buffering/ScbShape.h + ${PX_SOURCE_DIR}/buffering/ScbType.h +) +SOURCE_GROUP(src\\buffering FILES ${PHYSX_BUFFERING_SOURCE}) + +SET(PHYSX_CORE_SOURCE + ${PX_SOURCE_DIR}/NpActor.cpp + ${PX_SOURCE_DIR}/NpAggregate.cpp + ${PX_SOURCE_DIR}/NpArticulationReducedCoordinate.cpp + ${PX_SOURCE_DIR}/NpArticulation.cpp + ${PX_SOURCE_DIR}/NpArticulationJoint.cpp + ${PX_SOURCE_DIR}/NpArticulationJointReducedCoordinate.cpp + ${PX_SOURCE_DIR}/NpArticulationLink.cpp + ${PX_SOURCE_DIR}/NpBatchQuery.cpp + ${PX_SOURCE_DIR}/NpConstraint.cpp + ${PX_SOURCE_DIR}/NpFactory.cpp + ${PX_SOURCE_DIR}/NpMaterial.cpp + ${PX_SOURCE_DIR}/NpMetaData.cpp + ${PX_SOURCE_DIR}/NpPhysics.cpp + ${PX_SOURCE_DIR}/NpPvdSceneQueryCollector.cpp + ${PX_SOURCE_DIR}/NpReadCheck.cpp + ${PX_SOURCE_DIR}/NpRigidDynamic.cpp + ${PX_SOURCE_DIR}/NpRigidStatic.cpp + ${PX_SOURCE_DIR}/NpScene.cpp + ${PX_SOURCE_DIR}/NpSceneQueries.cpp + ${PX_SOURCE_DIR}/NpSerializerAdapter.cpp + ${PX_SOURCE_DIR}/NpShape.cpp + ${PX_SOURCE_DIR}/NpShapeManager.cpp + ${PX_SOURCE_DIR}/NpWriteCheck.cpp + ${PX_SOURCE_DIR}/PvdMetaDataPvdBinding.cpp + ${PX_SOURCE_DIR}/PvdPhysicsClient.cpp + ${PX_SOURCE_DIR}/NpActor.h + ${PX_SOURCE_DIR}/NpActorTemplate.h + ${PX_SOURCE_DIR}/NpAggregate.h + ${PX_SOURCE_DIR}/NpArticulationReducedCoordinate.h + ${PX_SOURCE_DIR}/NpArticulation.h + ${PX_SOURCE_DIR}/NpArticulationJoint.h + ${PX_SOURCE_DIR}/NpArticulationJointReducedCoordinate.h + ${PX_SOURCE_DIR}/NpArticulationLink.h + ${PX_SOURCE_DIR}/NpArticulationTemplate.h + ${PX_SOURCE_DIR}/NpBatchQuery.h + ${PX_SOURCE_DIR}/NpCast.h + ${PX_SOURCE_DIR}/NpConnector.h + ${PX_SOURCE_DIR}/NpConstraint.h + ${PX_SOURCE_DIR}/NpFactory.h + ${PX_SOURCE_DIR}/NpMaterial.h + ${PX_SOURCE_DIR}/NpMaterialManager.h + ${PX_SOURCE_DIR}/NpPhysics.h + ${PX_SOURCE_DIR}/NpPhysicsInsertionCallback.h + ${PX_SOURCE_DIR}/NpPtrTableStorageManager.h + ${PX_SOURCE_DIR}/NpPvdSceneQueryCollector.h + ${PX_SOURCE_DIR}/NpQueryShared.h + ${PX_SOURCE_DIR}/NpReadCheck.h + ${PX_SOURCE_DIR}/NpRigidActorTemplate.h + ${PX_SOURCE_DIR}/NpRigidActorTemplateInternal.h + ${PX_SOURCE_DIR}/NpRigidBodyTemplate.h + ${PX_SOURCE_DIR}/NpRigidDynamic.h + ${PX_SOURCE_DIR}/NpRigidStatic.h + ${PX_SOURCE_DIR}/NpScene.h + ${PX_SOURCE_DIR}/NpSceneQueries.h + ${PX_SOURCE_DIR}/NpSceneAccessor.h + ${PX_SOURCE_DIR}/NpShape.h + ${PX_SOURCE_DIR}/NpShapeManager.h + ${PX_SOURCE_DIR}/NpWriteCheck.h + ${PX_SOURCE_DIR}/PvdMetaDataBindingData.h + ${PX_SOURCE_DIR}/PvdMetaDataPvdBinding.h + ${PX_SOURCE_DIR}/PvdPhysicsClient.h + ${PX_SOURCE_DIR}/PvdTypeNames.h +) +SOURCE_GROUP(src FILES ${PHYSX_CORE_SOURCE}) + +ADD_LIBRARY(PhysX ${PHYSX_LIBTYPE} + ${PHYSX_HEADERS} + ${PHYSX_COMMON_HEADERS} + ${PHYSX_PVD_HEADERS} + ${PHYSX_SOLVER_HEADERS} + + ${PHYSX_METADATA_HEADERS} + ${PHYSX_METADATA_SOURCE} + + ${PHYSX_CORE_SOURCE} + ${PHYSX_BUFFERING_SOURCE} + ${PHYSX_IMMEDIATEMODE_SOURCE} + + ${PHYSX_PLATFORM_SRC_FILES} +) + +# Add the headers to the install +INSTALL(FILES ${PHYSX_HEADERS} DESTINATION include) +INSTALL(FILES ${PHYSX_COMMON_HEADERS} DESTINATION include/common) +INSTALL(FILES ${PHYSX_PVD_HEADERS} DESTINATION include/pvd) +INSTALL(FILES ${PHYSX_COLLISION_HEADERS} DESTINATION include/collision) +INSTALL(FILES ${PHYSX_SOLVER_HEADERS} DESTINATION include/solver) + +TARGET_INCLUDE_DIRECTORIES(PhysX + PRIVATE ${PHYSX_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/gpu + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/pvd + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src/device + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src/buffering + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/software/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevelaabb/include + + PRIVATE ${PHYSX_SOURCE_DIR}/lowleveldynamics/include + + PRIVATE ${PHYSX_SOURCE_DIR}/simulationcontroller/include + PRIVATE ${PHYSX_SOURCE_DIR}/simulationcontroller/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src/convex + + PRIVATE ${PHYSX_SOURCE_DIR}/scenequery/include + + PRIVATE ${PHYSX_SOURCE_DIR}/physxmetadata/core/include + + PRIVATE ${PHYSX_SOURCE_DIR}/immediatemode/include + + PRIVATE ${PHYSX_SOURCE_DIR}/pvd/include +) + +TARGET_COMPILE_DEFINITIONS(PhysX + + # Common to all configurations + PRIVATE ${PHYSX_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysX PROPERTIES + OUTPUT_NAME PhysX + +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSX_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysX PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysX_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysX_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysX_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysX_static" + ) +ENDIF() + +IF(PHYSX_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysX PROPERTIES + COMPILE_PDB_NAME_DEBUG "${PHYSX_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${PHYSX_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${PHYSX_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${PHYSX_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +TARGET_LINK_LIBRARIES(PhysX + PRIVATE ${PHYSX_PRIVATE_PLATFORM_LINKED_LIBS} + PRIVATE PhysXPvdSDK + PUBLIC PhysXCommon + PUBLIC PhysXFoundation + PUBLIC ${PHYSX_PLATFORM_LINKED_LIBS} +) + +SET_TARGET_PROPERTIES(PhysX PROPERTIES + LINK_FLAGS "${PHYSX_PLATFORM_LINK_FLAGS}" + LINK_FLAGS_DEBUG "${PHYSX_PLATFORM_LINK_FLAGS_DEBUG}" + LINK_FLAGS_CHECKED "${PHYSX_PLATFORM_LINK_FLAGS_CHECKED}" + LINK_FLAGS_PROFILE "${PHYSX_PLATFORM_LINK_FLAGS_PROFILE}" + LINK_FLAGS_RELEASE "${PHYSX_PLATFORM_LINK_FLAGS_RELEASE}" +) + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COMMON_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_PVD_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_METADATA_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_METADATA_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_CORE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_BUFFERING_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_IMMEDIATEMODE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_PLATFORM_SRC_FILES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_SOLVER_HEADERS}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysX PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..cd9ee9dd6 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake @@ -0,0 +1,152 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physxcharacterkinematic/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXCharacterKinematic.cmake) + + +SET(PHYSXCCT_HEADERS + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxBoxController.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxCapsuleController.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxCharacter.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxController.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxControllerBehavior.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxControllerManager.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxControllerObstacles.h + ${PHYSX_ROOT_DIR}/include/characterkinematic/PxExtended.h +) +SOURCE_GROUP(include FILES ${PHYSXCCT_HEADERS}) + +SET(PHYSXCCT_SOURCE + ${LL_SOURCE_DIR}/CctBoxController.cpp + ${LL_SOURCE_DIR}/CctCapsuleController.cpp + ${LL_SOURCE_DIR}/CctCharacterController.cpp + ${LL_SOURCE_DIR}/CctCharacterControllerCallbacks.cpp + ${LL_SOURCE_DIR}/CctCharacterControllerManager.cpp + ${LL_SOURCE_DIR}/CctController.cpp + ${LL_SOURCE_DIR}/CctObstacleContext.cpp + ${LL_SOURCE_DIR}/CctSweptBox.cpp + ${LL_SOURCE_DIR}/CctSweptCapsule.cpp + ${LL_SOURCE_DIR}/CctSweptVolume.cpp + ${LL_SOURCE_DIR}/CctBoxController.h + ${LL_SOURCE_DIR}/CctCapsuleController.h + ${LL_SOURCE_DIR}/CctCharacterController.h + ${LL_SOURCE_DIR}/CctCharacterControllerManager.h + ${LL_SOURCE_DIR}/CctController.h + ${LL_SOURCE_DIR}/CctInternalStructs.h + ${LL_SOURCE_DIR}/CctObstacleContext.h + ${LL_SOURCE_DIR}/CctSweptBox.h + ${LL_SOURCE_DIR}/CctSweptCapsule.h + ${LL_SOURCE_DIR}/CctSweptVolume.h + ${LL_SOURCE_DIR}/CctUtils.h +) +SOURCE_GROUP(src FILES ${PHYSXCCT_SOURCE}) + +ADD_LIBRARY(PhysXCharacterKinematic ${PHYSXCHARACTERKINEMATIC_LIBTYPE} + ${PHYSXCCT_HEADERS} + ${PHYSXCCT_SOURCE} +) + +INSTALL(FILES ${PHYSXCCT_HEADERS} DESTINATION include/characterkinematic) + +TARGET_INCLUDE_DIRECTORIES(PhysXCharacterKinematic + + PRIVATE ${PHYSXCHARACTERKINEMATICS_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/characterkinematic + PRIVATE ${PHYSX_ROOT_DIR}/include/extensions + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm +) + +TARGET_COMPILE_DEFINITIONS(PhysXCharacterKinematic + + # Common to all configurations + PRIVATE ${PHYSXCHARACTERKINEMATICS_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXCharacterKinematic PROPERTIES + OUTPUT_NAME PhysXCharacterKinematic +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCHARACTERKINEMATIC_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXCharacterKinematic PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXCharacterKinematic_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXCharacterKinematic_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXCharacterKinematic_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXCharacterKinematic_static" + ) +ENDIF() + +IF(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXCharacterKinematic PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +TARGET_LINK_LIBRARIES(PhysXCharacterKinematic + PUBLIC ${PHYSXCHARACTERKINEMATICS_PLATFORM_LINKED_LIBS} + PUBLIC PhysXFoundation +) + + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCCT_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCCT_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXCharacterKinematic PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake new file mode 100644 index 000000000..5bcbf8222 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake @@ -0,0 +1,591 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(COMMON_SRC_DIR ${PHYSX_SOURCE_DIR}/common/src) +SET(GU_SOURCE_DIR ${PHYSX_SOURCE_DIR}/geomutils) + +SET(PXCOMMON_PLATFORM_LINK_FLAGS_DEBUG " ") +SET(PXCOMMON_PLATFORM_LINK_FLAGS_CHECKED " ") +SET(PXCOMMON_PLATFORM_LINK_FLAGS_PROFILE " ") +SET(PXCOMMON_PLATFORM_LINK_FLAGS_RELEASE " ") + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXCommon.cmake) + + +SET(PHYSX_COMMON_SOURCE + ${COMMON_SRC_DIR}/CmBoxPruning.cpp + ${COMMON_SRC_DIR}/CmCollection.cpp + ${COMMON_SRC_DIR}/CmMathUtils.cpp + ${COMMON_SRC_DIR}/CmPtrTable.cpp + ${COMMON_SRC_DIR}/CmRadixSort.cpp + ${COMMON_SRC_DIR}/CmRadixSortBuffered.cpp + ${COMMON_SRC_DIR}/CmRenderOutput.cpp + ${COMMON_SRC_DIR}/CmVisualization.cpp + ${COMMON_SRC_DIR}/CmBitMap.h + ${COMMON_SRC_DIR}/CmBlockArray.h + ${COMMON_SRC_DIR}/CmBoxPruning.h + ${COMMON_SRC_DIR}/CmCollection.h + ${COMMON_SRC_DIR}/CmConeLimitHelper.h + ${COMMON_SRC_DIR}/CmFlushPool.h + ${COMMON_SRC_DIR}/CmIDPool.h + ${COMMON_SRC_DIR}/CmIO.h + ${COMMON_SRC_DIR}/CmMatrix34.h + ${COMMON_SRC_DIR}/CmPhysXCommon.h + ${COMMON_SRC_DIR}/CmPool.h + ${COMMON_SRC_DIR}/CmPreallocatingPool.h + ${COMMON_SRC_DIR}/CmPriorityQueue.h + ${COMMON_SRC_DIR}/CmPtrTable.h + ${COMMON_SRC_DIR}/CmQueue.h + ${COMMON_SRC_DIR}/CmRadixSort.h + ${COMMON_SRC_DIR}/CmRadixSortBuffered.h + ${COMMON_SRC_DIR}/CmRefCountable.h + ${COMMON_SRC_DIR}/CmRenderBuffer.h + ${COMMON_SRC_DIR}/CmRenderOutput.h + ${COMMON_SRC_DIR}/CmScaling.h + ${COMMON_SRC_DIR}/CmSpatialVector.h + ${COMMON_SRC_DIR}/CmTask.h + ${COMMON_SRC_DIR}/CmTaskPool.h + ${COMMON_SRC_DIR}/CmTmpMem.h + ${COMMON_SRC_DIR}/CmTransformUtils.h + ${COMMON_SRC_DIR}/CmUtils.h + ${COMMON_SRC_DIR}/CmVisualization.h +) +SOURCE_GROUP(common\\src FILES ${PHYSX_COMMON_SOURCE}) + +SET(PHYSXCOMMON_COMMON_HEADERS + ${PHYSX_ROOT_DIR}/include/common/PxBase.h + ${PHYSX_ROOT_DIR}/include/common/PxCollection.h + ${PHYSX_ROOT_DIR}/include/common/PxCoreUtilityTypes.h + ${PHYSX_ROOT_DIR}/include/common/PxMetaData.h + ${PHYSX_ROOT_DIR}/include/common/PxMetaDataFlags.h + ${PHYSX_ROOT_DIR}/include/common/PxPhysicsInsertionCallback.h + ${PHYSX_ROOT_DIR}/include/common/PxPhysXCommonConfig.h + ${PHYSX_ROOT_DIR}/include/common/PxRenderBuffer.h + ${PHYSX_ROOT_DIR}/include/common/PxSerialFramework.h + ${PHYSX_ROOT_DIR}/include/common/PxSerializer.h + ${PHYSX_ROOT_DIR}/include/common/PxStringTable.h + ${PHYSX_ROOT_DIR}/include/common/PxTolerancesScale.h + ${PHYSX_ROOT_DIR}/include/common/PxTypeInfo.h + ${PHYSX_ROOT_DIR}/include/common/PxProfileZone.h +) +SOURCE_GROUP(include\\common FILES ${PHYSXCOMMON_COMMON_HEADERS}) + +SET(PHYSXCOMMON_GEOMETRY_HEADERS + ${PHYSX_ROOT_DIR}/include/geometry/PxBoxGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxCapsuleGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxConvexMesh.h + ${PHYSX_ROOT_DIR}/include/geometry/PxConvexMeshGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxGeometryHelpers.h + ${PHYSX_ROOT_DIR}/include/geometry/PxGeometryQuery.h + ${PHYSX_ROOT_DIR}/include/geometry/PxHeightField.h + ${PHYSX_ROOT_DIR}/include/geometry/PxHeightFieldDesc.h + ${PHYSX_ROOT_DIR}/include/geometry/PxHeightFieldFlag.h + ${PHYSX_ROOT_DIR}/include/geometry/PxHeightFieldGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxHeightFieldSample.h + ${PHYSX_ROOT_DIR}/include/geometry/PxMeshQuery.h + ${PHYSX_ROOT_DIR}/include/geometry/PxMeshScale.h + ${PHYSX_ROOT_DIR}/include/geometry/PxPlaneGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxSimpleTriangleMesh.h + ${PHYSX_ROOT_DIR}/include/geometry/PxSphereGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxTriangle.h + ${PHYSX_ROOT_DIR}/include/geometry/PxTriangleMesh.h + ${PHYSX_ROOT_DIR}/include/geometry/PxTriangleMeshGeometry.h + ${PHYSX_ROOT_DIR}/include/geometry/PxBVHStructure.h +) +SOURCE_GROUP(include\\geometry FILES ${PHYSXCOMMON_GEOMETRY_HEADERS}) + +SET(PHYSXCOMMON_GEOMUTILS_HEADERS + ${PHYSX_ROOT_DIR}/include/geomutils/GuContactBuffer.h + ${PHYSX_ROOT_DIR}/include/geomutils/GuContactPoint.h +) +SOURCE_GROUP(include\\geomutils FILES ${PHYSXCOMMON_GEOMUTILS_HEADERS}) + +SET(PHYSXCOMMON_COLLISION_HEADERS + ${PHYSX_ROOT_DIR}/include/collision/PxCollisionDefs.h +) +SOURCE_GROUP(include\\collision FILES ${PHYSXCOMMON_COLLISION_HEADERS}) + +SET(PHYSXCOMMON_GU_HEADERS + ${GU_SOURCE_DIR}/include/GuAxes.h + ${GU_SOURCE_DIR}/include/GuBox.h + ${GU_SOURCE_DIR}/include/GuDistanceSegmentBox.h + ${GU_SOURCE_DIR}/include/GuDistanceSegmentSegment.h + ${GU_SOURCE_DIR}/include/GuIntersectionBoxBox.h + ${GU_SOURCE_DIR}/include/GuIntersectionTriangleBox.h + ${GU_SOURCE_DIR}/include/GuIntersectionTriangleBoxRef.h + ${GU_SOURCE_DIR}/include/GuRaycastTests.h + ${GU_SOURCE_DIR}/include/GuSegment.h + ${GU_SOURCE_DIR}/include/GuSIMDHelpers.h +) +SOURCE_GROUP(geomutils\\headers FILES ${PHYSXCOMMON_GU_HEADERS}) + +SET(PHYSXCOMMON_GU_PXHEADERS + ${PHYSX_ROOT_DIR}/include/geomutils/GuContactBuffer.h + ${PHYSX_ROOT_DIR}/include/geomutils/GuContactPoint.h +) +SOURCE_GROUP(geomutils\\include FILES ${PHYSXCOMMON_GU_PXHEADERS}) + +SET(PHYSXCOMMON_GU_SOURCE + ${GU_SOURCE_DIR}/src/GuBounds.cpp + ${GU_SOURCE_DIR}/src/GuBox.cpp + ${GU_SOURCE_DIR}/src/GuCapsule.cpp + ${GU_SOURCE_DIR}/src/GuCCTSweepTests.cpp + ${GU_SOURCE_DIR}/src/GuGeometryQuery.cpp + ${GU_SOURCE_DIR}/src/GuGeometryUnion.cpp + ${GU_SOURCE_DIR}/src/GuInternal.cpp + ${GU_SOURCE_DIR}/src/GuMeshFactory.cpp + ${GU_SOURCE_DIR}/src/GuMetaData.cpp + ${GU_SOURCE_DIR}/src/GuMTD.cpp + ${GU_SOURCE_DIR}/src/GuOverlapTests.cpp + ${GU_SOURCE_DIR}/src/GuRaycastTests.cpp + ${GU_SOURCE_DIR}/src/GuSerialize.cpp + ${GU_SOURCE_DIR}/src/GuSweepMTD.cpp + ${GU_SOURCE_DIR}/src/GuSweepSharedTests.cpp + ${GU_SOURCE_DIR}/src/GuSweepTests.cpp + ${GU_SOURCE_DIR}/src/GuBounds.h + ${GU_SOURCE_DIR}/src/GuCapsule.h + ${GU_SOURCE_DIR}/src/GuCenterExtents.h + ${GU_SOURCE_DIR}/src/GuGeometryUnion.h + ${GU_SOURCE_DIR}/src/GuInternal.h + ${GU_SOURCE_DIR}/src/GuMeshFactory.h + ${GU_SOURCE_DIR}/src/GuMTD.h + ${GU_SOURCE_DIR}/src/GuOverlapTests.h + ${GU_SOURCE_DIR}/src/GuSerialize.h + ${GU_SOURCE_DIR}/src/GuSphere.h + ${GU_SOURCE_DIR}/src/GuSweepMTD.h + ${GU_SOURCE_DIR}/src/GuSweepSharedTests.h + ${GU_SOURCE_DIR}/src/GuSweepTests.h + ${GU_SOURCE_DIR}/src/GuAABBTreeBuild.cpp + ${GU_SOURCE_DIR}/src/GuAABBTreeBuild.h + ${GU_SOURCE_DIR}/src/GuAABBTreeQuery.h + ${GU_SOURCE_DIR}/src/GuBVHStructure.cpp + ${GU_SOURCE_DIR}/src/GuBVHStructure.h + ${GU_SOURCE_DIR}/src/GuBVHTestsSIMD.h +) +SOURCE_GROUP(geomutils\\src FILES ${PHYSXCOMMON_GU_SOURCE}) + +SET(PHYSXCOMMON_GU_CCD_SOURCE + ${GU_SOURCE_DIR}/src/ccd/GuCCDSweepConvexMesh.cpp + ${GU_SOURCE_DIR}/src/ccd/GuCCDSweepPrimitives.cpp + ${GU_SOURCE_DIR}/src/ccd/GuCCDSweepConvexMesh.h +) +SOURCE_GROUP(geomutils\\src\\ccd FILES ${PHYSXCOMMON_GU_CCD_SOURCE}) + +SET(PHYSXCOMMON_GU_COMMON_SOURCE + ${GU_SOURCE_DIR}/src/common/GuBarycentricCoordinates.cpp + ${GU_SOURCE_DIR}/src/common/GuSeparatingAxes.cpp + ${GU_SOURCE_DIR}/src/common/GuBarycentricCoordinates.h + ${GU_SOURCE_DIR}/src/common/GuBoxConversion.h + ${GU_SOURCE_DIR}/src/common/GuEdgeCache.h + ${GU_SOURCE_DIR}/src/common/GuEdgeListData.h + ${GU_SOURCE_DIR}/src/common/GuSeparatingAxes.h +) +SOURCE_GROUP(geomutils\\src\\common FILES ${PHYSXCOMMON_GU_COMMON_SOURCE}) + +SET(PHYSXCOMMON_GU_CONTACT_SOURCE + ${GU_SOURCE_DIR}/src/contact/GuContactBoxBox.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactCapsuleBox.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactCapsuleCapsule.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactCapsuleConvex.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactCapsuleMesh.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactConvexConvex.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactConvexMesh.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactPlaneBox.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactPlaneCapsule.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactPlaneConvex.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactPolygonPolygon.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactSphereBox.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactSphereCapsule.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactSphereMesh.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactSpherePlane.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactSphereSphere.cpp + ${GU_SOURCE_DIR}/src/contact/GuFeatureCode.cpp + ${GU_SOURCE_DIR}/src/contact/GuContactMethodImpl.h + ${GU_SOURCE_DIR}/src/contact/GuContactPolygonPolygon.h + ${GU_SOURCE_DIR}/src/contact/GuFeatureCode.h +) +SOURCE_GROUP(geomutils\\src\\contact FILES ${PHYSXCOMMON_GU_CONTACT_SOURCE}) + +SET(PHYSXCOMMON_GU_CONVEX_SOURCE + ${GU_SOURCE_DIR}/src/convex/GuBigConvexData.cpp + ${GU_SOURCE_DIR}/src/convex/GuConvexHelper.cpp + ${GU_SOURCE_DIR}/src/convex/GuConvexMesh.cpp + ${GU_SOURCE_DIR}/src/convex/GuConvexSupportTable.cpp + ${GU_SOURCE_DIR}/src/convex/GuConvexUtilsInternal.cpp + ${GU_SOURCE_DIR}/src/convex/GuHillClimbing.cpp + ${GU_SOURCE_DIR}/src/convex/GuShapeConvex.cpp + ${GU_SOURCE_DIR}/src/convex/GuBigConvexData.h + ${GU_SOURCE_DIR}/src/convex/GuBigConvexData2.h + ${GU_SOURCE_DIR}/src/convex/GuConvexEdgeFlags.h + ${GU_SOURCE_DIR}/src/convex/GuConvexHelper.h + ${GU_SOURCE_DIR}/src/convex/GuConvexMesh.h + ${GU_SOURCE_DIR}/src/convex/GuConvexMeshData.h + ${GU_SOURCE_DIR}/src/convex/GuConvexSupportTable.h + ${GU_SOURCE_DIR}/src/convex/GuConvexUtilsInternal.h + ${GU_SOURCE_DIR}/src/convex/GuCubeIndex.h + ${GU_SOURCE_DIR}/src/convex/GuHillClimbing.h + ${GU_SOURCE_DIR}/src/convex/GuShapeConvex.h +) +SOURCE_GROUP(geomutils\\src\\convex FILES ${PHYSXCOMMON_GU_CONVEX_SOURCE}) + +SET(PHYSXCOMMON_GU_DISTANCE_SOURCE + ${GU_SOURCE_DIR}/src/distance/GuDistancePointBox.cpp + ${GU_SOURCE_DIR}/src/distance/GuDistancePointTriangle.cpp + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentBox.cpp + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentSegment.cpp + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentTriangle.cpp + ${GU_SOURCE_DIR}/src/distance/GuDistancePointBox.h + ${GU_SOURCE_DIR}/src/distance/GuDistancePointSegment.h + ${GU_SOURCE_DIR}/src/distance/GuDistancePointTriangle.h + ${GU_SOURCE_DIR}/src/distance/GuDistancePointTriangleSIMD.h + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentSegmentSIMD.h + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentTriangle.h + ${GU_SOURCE_DIR}/src/distance/GuDistanceSegmentTriangleSIMD.h +) +SOURCE_GROUP(geomutils\\src\\distance FILES ${PHYSXCOMMON_GU_DISTANCE_SOURCE}) + +SET(PHYSXCOMMON_GU_GJK_SOURCE + ${GU_SOURCE_DIR}/src/gjk/GuEPA.cpp + ${GU_SOURCE_DIR}/src/gjk/GuGJKSimplex.cpp + ${GU_SOURCE_DIR}/src/gjk/GuGJKTest.cpp + ${GU_SOURCE_DIR}/src/gjk/GuEPA.h + ${GU_SOURCE_DIR}/src/gjk/GuEPAFacet.h + ${GU_SOURCE_DIR}/src/gjk/GuGJK.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKPenetration.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKRaycast.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKSimplex.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKTest.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKType.h + ${GU_SOURCE_DIR}/src/gjk/GuGJKUtil.h + ${GU_SOURCE_DIR}/src/gjk/GuVecBox.h + ${GU_SOURCE_DIR}/src/gjk/GuVecCapsule.h + ${GU_SOURCE_DIR}/src/gjk/GuVecConvex.h + ${GU_SOURCE_DIR}/src/gjk/GuVecConvexHull.h + ${GU_SOURCE_DIR}/src/gjk/GuVecConvexHullNoScale.h + ${GU_SOURCE_DIR}/src/gjk/GuVecPlane.h + ${GU_SOURCE_DIR}/src/gjk/GuVecSphere.h + ${GU_SOURCE_DIR}/src/gjk/GuVecTriangle.h +) +SOURCE_GROUP(geomutils\\src\\gjk FILES ${PHYSXCOMMON_GU_GJK_SOURCE}) + +SET(PHYSXCOMMON_GU_HF_SOURCE + ${GU_SOURCE_DIR}/src/hf/GuHeightField.cpp + ${GU_SOURCE_DIR}/src/hf/GuHeightFieldUtil.cpp + ${GU_SOURCE_DIR}/src/hf/GuOverlapTestsHF.cpp + ${GU_SOURCE_DIR}/src/hf/GuSweepsHF.cpp + ${GU_SOURCE_DIR}/src/hf/GuEntityReport.h + ${GU_SOURCE_DIR}/src/hf/GuHeightField.h + ${GU_SOURCE_DIR}/src/hf/GuHeightFieldData.h + ${GU_SOURCE_DIR}/src/hf/GuHeightFieldUtil.h +) +SOURCE_GROUP(geomutils\\src\\hf FILES ${PHYSXCOMMON_GU_HF_SOURCE}) + +SET(PHYSXCOMMON_GU_INTERSECTION_SOURCE + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionBoxBox.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionCapsuleTriangle.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionEdgeEdge.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayBox.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayCapsule.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRaySphere.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionSphereBox.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionTriangleBox.cpp + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionCapsuleTriangle.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionEdgeEdge.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRay.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayBox.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayBoxSIMD.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayCapsule.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayPlane.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRaySphere.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionRayTriangle.h + ${GU_SOURCE_DIR}/src/intersection/GuIntersectionSphereBox.h +) +SOURCE_GROUP(geomutils\\src\\intersection FILES ${PHYSXCOMMON_GU_INTERSECTION_SOURCE}) + +SET(PXCOMMON_BVH4_FILES + ${GU_SOURCE_DIR}/src/mesh/GuBV4_AABBSweep.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_BoxOverlap.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_CapsuleSweep.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_CapsuleSweepAA.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_OBBSweep.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Raycast.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_SphereOverlap.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4_SphereSweep.cpp + ${GU_SOURCE_DIR}/src/mesh/GuMidphaseBV4.cpp +) + +SET(PHYSXCOMMON_GU_MESH_SOURCE + ${GU_SOURCE_DIR}/src/mesh/GuBV4.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV4Build.cpp + + ${PXCOMMON_BVH4_FILES} + + ${GU_SOURCE_DIR}/src/mesh/GuMeshQuery.cpp + ${GU_SOURCE_DIR}/src/mesh/GuMidphaseRTree.cpp + ${GU_SOURCE_DIR}/src/mesh/GuOverlapTestsMesh.cpp + ${GU_SOURCE_DIR}/src/mesh/GuRTree.cpp + ${GU_SOURCE_DIR}/src/mesh/GuRTreeQueries.cpp + ${GU_SOURCE_DIR}/src/mesh/GuSweepsMesh.cpp + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMesh.cpp + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMeshBV4.cpp + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMeshRTree.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV32.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV32Build.cpp + ${GU_SOURCE_DIR}/src/mesh/GuBV32.h + ${GU_SOURCE_DIR}/src/mesh/GuBV32Build.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4Build.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4Settings.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_AABBAABBSweepTest.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_BoxBoxOverlapTest.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_BoxOverlap_Internal.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_BoxSweep_Internal.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_BoxSweep_Params.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_CapsuleSweep_Internal.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Common.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Internal.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Slabs.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Slabs_KajiyaOrdered.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h + ${GU_SOURCE_DIR}/src/mesh/GuBV4_Slabs_SwizzledOrdered.h + ${GU_SOURCE_DIR}/src/mesh/GuBVConstants.h + ${GU_SOURCE_DIR}/src/mesh/GuMeshData.h + ${GU_SOURCE_DIR}/src/mesh/GuMidphaseInterface.h + ${GU_SOURCE_DIR}/src/mesh/GuRTree.h + ${GU_SOURCE_DIR}/src/mesh/GuSweepConvexTri.h + ${GU_SOURCE_DIR}/src/mesh/GuSweepMesh.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangle32.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangleCache.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMesh.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMeshBV4.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangleMeshRTree.h + ${GU_SOURCE_DIR}/src/mesh/GuTriangleVertexPointers.h +) +SOURCE_GROUP(geomutils\\src\\mesh FILES ${PHYSXCOMMON_GU_MESH_SOURCE}) + +SET(PHYSXCOMMON_GU_PCM_SOURCE + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactBoxBox.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactBoxConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactCapsuleBox.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactCapsuleCapsule.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactCapsuleConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactCapsuleHeightField.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactCapsuleMesh.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactConvexCommon.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactConvexConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactConvexHeightField.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactConvexMesh.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactGenBoxConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactGenSphereCapsule.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactPlaneBox.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactPlaneCapsule.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactPlaneConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereBox.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereCapsule.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereHeightField.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereMesh.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSpherePlane.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactSphereSphere.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMShapeConvex.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMTriangleContactGen.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPersistentContactManifold.cpp + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactConvexCommon.h + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactGen.h + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactGenUtil.h + ${GU_SOURCE_DIR}/src/pcm/GuPCMContactMeshCallback.h + ${GU_SOURCE_DIR}/src/pcm/GuPCMShapeConvex.h + ${GU_SOURCE_DIR}/src/pcm/GuPCMTriangleContactGen.h + ${GU_SOURCE_DIR}/src/pcm/GuPersistentContactManifold.h +) +SOURCE_GROUP(geomutils\\src\\pcm FILES ${PHYSXCOMMON_GU_PCM_SOURCE}) + +SET(PHYSXCOMMON_GU_SWEEP_SOURCE + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxBox.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxSphere.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxTriangle_SAT.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleBox.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleCapsule.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleTriangle.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereCapsule.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereSphere.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereTriangle.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepTriangleUtils.cpp + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxBox.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxSphere.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxTriangle_FeatureBased.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepBoxTriangle_SAT.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleBox.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleCapsule.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepCapsuleTriangle.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereCapsule.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereSphere.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepSphereTriangle.h + ${GU_SOURCE_DIR}/src/sweep/GuSweepTriangleUtils.h +) +SOURCE_GROUP(geomutils\\src\\sweep FILES ${PHYSXCOMMON_GU_SWEEP_SOURCE}) + +ADD_LIBRARY(PhysXCommon ${PHYSXCOMMON_LIBTYPE} + ${PHYSX_COMMON_SOURCE} + + ${PHYSXCOMMON_COMMON_HEADERS} + ${PHYSXCOMMON_GEOMETRY_HEADERS} + ${PHYSXCOMMON_GEOMUTILS_HEADERS} + ${PHYSXCOMMON_COLLISION_HEADERS} + + ${PXCOMMON_PLATFORM_SRC_FILES} + + ${PHYSXCOMMON_GU_HEADERS} + ${PHYSXCOMMON_GU_PXHEADERS} + + ${PHYSXCOMMON_GU_SOURCE} + ${PHYSXCOMMON_GU_CCD_SOURCE} + ${PHYSXCOMMON_GU_COMMON_SOURCE} + ${PHYSXCOMMON_GU_CONTACT_SOURCE} + ${PHYSXCOMMON_GU_CONVEX_SOURCE} + ${PHYSXCOMMON_GU_DISTANCE_SOURCE} + ${PHYSXCOMMON_GU_GJK_SOURCE} + ${PHYSXCOMMON_GU_HF_SOURCE} + ${PHYSXCOMMON_GU_INTERSECTION_SOURCE} + ${PHYSXCOMMON_GU_MESH_SOURCE} + ${PHYSXCOMMON_GU_PCM_SOURCE} + ${PHYSXCOMMON_GU_SWEEP_SOURCE} +) + +INSTALL(FILES ${PHYSXCOMMON_GEOMETRY_HEADERS} DESTINATION include/geometry) +INSTALL(FILES ${PHYSXCOMMON_GEOMUTILS_HEADERS} DESTINATION include/geomutils) + +TARGET_INCLUDE_DIRECTORIES(PhysXCommon + PRIVATE ${PXCOMMON_PLATFORM_INCLUDES} + + PUBLIC ${PHYSX_ROOT_DIR}/include + PUBLIC ${PHYSX_ROOT_DIR}/include/common + PUBLIC ${PHYSX_ROOT_DIR}/include/geometry + PUBLIC ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include +) + +TARGET_COMPILE_DEFINITIONS(PhysXCommon + PRIVATE ${PXCOMMON_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXCommon PROPERTIES + OUTPUT_NAME PhysXCommon +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCOMMON_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXCommon PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXCommon_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXCommon_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXCommon_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXCommon_static" + ) +ENDIF() + +IF(PHYSXCOMMON_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXCommon PROPERTIES + COMPILE_PDB_NAME_DEBUG "${PHYSXCOMMON_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${PHYSXCOMMON_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${PHYSXCOMMON_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${PHYSXCOMMON_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +SET_TARGET_PROPERTIES(PhysXCommon PROPERTIES + LINK_FLAGS "${PXCOMMON_PLATFORM_LINK_FLAGS}" + LINK_FLAGS_DEBUG "${PXCOMMON_PLATFORM_LINK_FLAGS_DEBUG}" + LINK_FLAGS_CHECKED "${PXCOMMON_PLATFORM_LINK_FLAGS_CHECKED}" + LINK_FLAGS_PROFILE "${PXCOMMON_PLATFORM_LINK_FLAGS_PROFILE}" + LINK_FLAGS_RELEASE "${PXCOMMON_PLATFORM_LINK_FLAGS_RELEASE}" +) + +TARGET_LINK_LIBRARIES(PhysXCommon + PUBLIC ${PXCOMMON_PLATFORM_LINKED_LIBS} + PUBLIC PhysXFoundation +) + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COMMON_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_COMMON_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GEOMETRY_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GEOMUTILS_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PXCOMMON_PLATFORM_SRC_FILES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_PXHEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_CCD_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_COMMON_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_CONTACT_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_CONVEX_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_DISTANCE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_GJK_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_HF_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_INTERSECTION_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_MESH_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_PCM_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_GU_SWEEP_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOMMON_COLLISION_HEADERS}) +ENDIF() + + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXCommon PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake new file mode 100644 index 000000000..4a7719cc9 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake @@ -0,0 +1,206 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking common +# +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physxcooking/src) + +SET(PHYSXCOOKING_LINK_FLAGS_DEBUG " ") +SET(PHYSXCOOKING_LINK_FLAGS_CHECKED " ") +SET(PHYSXCOOKING_LINK_FLAGS_PROFILE " ") +SET(PHYSXCOOKING_LINK_FLAGS_RELEASE " ") + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXCooking.cmake) + + +SET(PHYSX_COOKING_HEADERS + ${PHYSX_ROOT_DIR}/include/cooking/PxBVH33MidphaseDesc.h + ${PHYSX_ROOT_DIR}/include/cooking/PxBVH34MidphaseDesc.h + ${PHYSX_ROOT_DIR}/include/cooking/Pxc.h + ${PHYSX_ROOT_DIR}/include/cooking/PxConvexMeshDesc.h + ${PHYSX_ROOT_DIR}/include/cooking/PxCooking.h + ${PHYSX_ROOT_DIR}/include/cooking/PxMidphaseDesc.h + ${PHYSX_ROOT_DIR}/include/cooking/PxTriangleMeshDesc.h + ${PHYSX_ROOT_DIR}/include/cooking/PxBVHStructureDesc.h +) +SOURCE_GROUP(include FILES ${PHYSX_COOKING_HEADERS}) + +SET(PHYSX_COOKING_SOURCE + ${LL_SOURCE_DIR}/Adjacencies.cpp + ${LL_SOURCE_DIR}/Cooking.cpp + ${LL_SOURCE_DIR}/CookingUtils.cpp + ${LL_SOURCE_DIR}/EdgeList.cpp + ${LL_SOURCE_DIR}/MeshCleaner.cpp + ${LL_SOURCE_DIR}/Quantizer.cpp + ${LL_SOURCE_DIR}/Adjacencies.h + ${LL_SOURCE_DIR}/Cooking.h + ${LL_SOURCE_DIR}/CookingUtils.h + ${LL_SOURCE_DIR}/EdgeList.h + ${LL_SOURCE_DIR}/MeshCleaner.h + ${LL_SOURCE_DIR}/Quantizer.h + ${LL_SOURCE_DIR}/BVHStructureBuilder.cpp + ${LL_SOURCE_DIR}/BVHStructureBuilder.h +) +SOURCE_GROUP(src FILES ${PHYSX_COOKING_SOURCE}) + +SET(PHYSX_COOKING_MESH_SOURCE + ${LL_SOURCE_DIR}/mesh/HeightFieldCooking.cpp + ${LL_SOURCE_DIR}/mesh/RTreeCooking.cpp + ${LL_SOURCE_DIR}/mesh/MeshBuilder.cpp + ${LL_SOURCE_DIR}/mesh/TriangleMeshBuilder.cpp + ${LL_SOURCE_DIR}/mesh/GrbTriangleMeshCooking.cpp + ${LL_SOURCE_DIR}/mesh/GrbTriangleMeshCooking.h + ${LL_SOURCE_DIR}/mesh/HeightFieldCooking.h + ${LL_SOURCE_DIR}/mesh/QuickSelect.h + ${LL_SOURCE_DIR}/mesh/RTreeCooking.h + ${LL_SOURCE_DIR}/mesh/MeshBuilder.h + ${LL_SOURCE_DIR}/mesh/TriangleMeshBuilder.h +) +SOURCE_GROUP(src\\mesh FILES ${PHYSX_COOKING_MESH_SOURCE}) + + +SET(PHYSX_COOKING_CONVEX_SOURCE + ${LL_SOURCE_DIR}/convex/BigConvexDataBuilder.cpp + ${LL_SOURCE_DIR}/convex/ConvexHullBuilder.cpp + ${LL_SOURCE_DIR}/convex/ConvexHullLib.cpp + ${LL_SOURCE_DIR}/convex/ConvexHullUtils.cpp + ${LL_SOURCE_DIR}/convex/ConvexMeshBuilder.cpp + ${LL_SOURCE_DIR}/convex/ConvexPolygonsBuilder.cpp + ${LL_SOURCE_DIR}/convex/QuickHullConvexHullLib.cpp + ${LL_SOURCE_DIR}/convex/VolumeIntegration.cpp + ${LL_SOURCE_DIR}/convex/BigConvexDataBuilder.h + ${LL_SOURCE_DIR}/convex/ConvexHullBuilder.h + ${LL_SOURCE_DIR}/convex/ConvexHullLib.h + ${LL_SOURCE_DIR}/convex/ConvexHullUtils.h + ${LL_SOURCE_DIR}/convex/ConvexMeshBuilder.h + ${LL_SOURCE_DIR}/convex/ConvexPolygonsBuilder.h + ${LL_SOURCE_DIR}/convex/QuickHullConvexHullLib.h + ${LL_SOURCE_DIR}/convex/VolumeIntegration.h +) +SOURCE_GROUP(src\\convex FILES ${PHYSX_COOKING_CONVEX_SOURCE}) + +ADD_LIBRARY(PhysXCooking ${PHYSXCOOKING_LIBTYPE} + ${PHYSX_COOKING_HEADERS} + + ${PHYSX_COOKING_SOURCE} + ${PHYSX_COOKING_MESH_SOURCE} + ${PHYSX_COOKING_CONVEX_SOURCE} + + ${PHYSXCOOKING_PLATFORM_SRC_FILES} +) + +INSTALL(FILES ${PHYSX_COOKING_HEADERS} DESTINATION include/cooking) + +# Target specific compile options + +TARGET_INCLUDE_DIRECTORIES(PhysXCooking + PRIVATE ${PHYSXCOOKING_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/cooking + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/include + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/common + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PUBLIC ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/physxcooking/src/convex + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include + +) + +TARGET_LINK_LIBRARIES(PhysXCooking PUBLIC PhysXCommon PhysXFoundation) + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(PhysXCooking + PRIVATE ${PHYSXCOOKING_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXCooking PROPERTIES + OUTPUT_NAME PhysXCooking +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCOOKING_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXCooking PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXCooking_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXCooking_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXCooking_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXCooking_static" + ) +ENDIF() + +IF(PHYSXCOOKING_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXCooking PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXCOOKING_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXCOOKING_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXCOOKING_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXCOOKING_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +SET_TARGET_PROPERTIES(PhysXCooking PROPERTIES + LINK_FLAGS ${PHYSXCOOKING_LINK_FLAGS} + LINK_FLAGS_DEBUG ${PHYSXCOOKING_LINK_FLAGS_DEBUG} + LINK_FLAGS_CHECKED ${PHYSXCOOKING_LINK_FLAGS_CHECKED} + LINK_FLAGS_PROFILE ${PHYSXCOOKING_LINK_FLAGS_PROFILE} + LINK_FLAGS_RELEASE ${PHYSXCOOKING_LINK_FLAGS_RELEASE} +) + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COOKING_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COOKING_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COOKING_MESH_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_COOKING_CONVEX_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXCOOKING_PLATFORM_SRC_FILES}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXCooking PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + + diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake new file mode 100644 index 000000000..aeb868943 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake @@ -0,0 +1,320 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physxextensions/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXExtensions.cmake) + + + +SET(PHYSX_EXTENSIONS_SOURCE + ${LL_SOURCE_DIR}/ExtBroadPhase.cpp + ${LL_SOURCE_DIR}/ExtCollection.cpp + ${LL_SOURCE_DIR}/ExtConvexMeshExt.cpp + ${LL_SOURCE_DIR}/ExtCpuWorkerThread.cpp + ${LL_SOURCE_DIR}/ExtD6Joint.cpp + ${LL_SOURCE_DIR}/ExtD6JointCreate.cpp + ${LL_SOURCE_DIR}/ExtDefaultCpuDispatcher.cpp + ${LL_SOURCE_DIR}/ExtDefaultErrorCallback.cpp + ${LL_SOURCE_DIR}/ExtDefaultSimulationFilterShader.cpp + ${LL_SOURCE_DIR}/ExtDefaultStreams.cpp + ${LL_SOURCE_DIR}/ExtDistanceJoint.cpp + ${LL_SOURCE_DIR}/ExtContactJoint.cpp + ${LL_SOURCE_DIR}/ExtExtensions.cpp + ${LL_SOURCE_DIR}/ExtFixedJoint.cpp + ${LL_SOURCE_DIR}/ExtJoint.cpp + ${LL_SOURCE_DIR}/ExtMetaData.cpp + ${LL_SOURCE_DIR}/ExtPrismaticJoint.cpp + ${LL_SOURCE_DIR}/ExtPvd.cpp + ${LL_SOURCE_DIR}/ExtPxStringTable.cpp + ${LL_SOURCE_DIR}/ExtRaycastCCD.cpp + ${LL_SOURCE_DIR}/ExtRevoluteJoint.cpp + ${LL_SOURCE_DIR}/ExtRigidBodyExt.cpp + ${LL_SOURCE_DIR}/ExtRigidActorExt.cpp + ${LL_SOURCE_DIR}/ExtSceneQueryExt.cpp + ${LL_SOURCE_DIR}/ExtSimpleFactory.cpp + ${LL_SOURCE_DIR}/ExtSmoothNormals.cpp + ${LL_SOURCE_DIR}/ExtSphericalJoint.cpp + ${LL_SOURCE_DIR}/ExtTriangleMeshExt.cpp + ${LL_SOURCE_DIR}/ExtConstraintHelper.h + ${LL_SOURCE_DIR}/ExtCpuWorkerThread.h + ${LL_SOURCE_DIR}/ExtD6Joint.h + ${LL_SOURCE_DIR}/ExtDefaultCpuDispatcher.h + ${LL_SOURCE_DIR}/ExtDistanceJoint.h + ${LL_SOURCE_DIR}/ExtContactJoint.h + ${LL_SOURCE_DIR}/ExtFixedJoint.h + ${LL_SOURCE_DIR}/ExtInertiaTensor.h + ${LL_SOURCE_DIR}/ExtJoint.h + ${LL_SOURCE_DIR}/ExtJointMetaDataExtensions.h + ${LL_SOURCE_DIR}/ExtPlatform.h + ${LL_SOURCE_DIR}/ExtPrismaticJoint.h + ${LL_SOURCE_DIR}/ExtPvd.h + ${LL_SOURCE_DIR}/ExtRevoluteJoint.h + ${LL_SOURCE_DIR}/ExtSerialization.h + ${LL_SOURCE_DIR}/ExtSharedQueueEntryPool.h + ${LL_SOURCE_DIR}/ExtSphericalJoint.h + ${LL_SOURCE_DIR}/ExtTaskQueueHelper.h +) +SOURCE_GROUP(src FILES ${PHYSX_EXTENSIONS_SOURCE}) + +SET(PHYSX_EXTENSIONS_METADATA_SOURCE + ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp + ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h + ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h + ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h +) +SOURCE_GROUP(src\\metadata FILES ${PHYSX_EXTENSIONS_METADATA_SOURCE}) + +SET(PHYSX_EXTENSIONS_HEADERS + ${PHYSX_ROOT_DIR}/include/extensions/PxBinaryConverter.h + ${PHYSX_ROOT_DIR}/include/extensions/PxBroadPhaseExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxCollectionExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxConstraintExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxContactJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxConvexMeshExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxD6Joint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxD6JointCreate.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDefaultAllocator.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDefaultCpuDispatcher.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDefaultErrorCallback.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDefaultSimulationFilterShader.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDefaultStreams.h + ${PHYSX_ROOT_DIR}/include/extensions/PxDistanceJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxContactJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxExtensionsAPI.h + ${PHYSX_ROOT_DIR}/include/extensions/PxFixedJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxJointLimit.h +# ${PHYSX_ROOT_DIR}/include/extensions/PxJointRepXSerializer.h + ${PHYSX_ROOT_DIR}/include/extensions/PxMassProperties.h + ${PHYSX_ROOT_DIR}/include/extensions/PxPrismaticJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRaycastCCD.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRepXSerializer.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRepXSimpleType.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRevoluteJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRigidActorExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxRigidBodyExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxSceneQueryExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxSerialization.h + ${PHYSX_ROOT_DIR}/include/extensions/PxShapeExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxSimpleFactory.h + ${PHYSX_ROOT_DIR}/include/extensions/PxSmoothNormals.h + ${PHYSX_ROOT_DIR}/include/extensions/PxSphericalJoint.h + ${PHYSX_ROOT_DIR}/include/extensions/PxStringTableExt.h + ${PHYSX_ROOT_DIR}/include/extensions/PxTriangleMeshExt.h +) +SOURCE_GROUP(include FILES ${PHYSX_EXTENSIONS_HEADERS}) + +SET(PHYSX_FILEBUF_HEADERS + ${PHYSX_ROOT_DIR}/include/filebuf/PxFileBuf.h +) +SOURCE_GROUP(include FILES ${PHYSX_FILEBUF_HEADERS}) + +SET(PHYSX_EXTENSIONS_SERIALIZATION_SOURCE + ${LL_SOURCE_DIR}/serialization/SnSerialization.cpp + ${LL_SOURCE_DIR}/serialization/SnSerializationRegistry.cpp + ${LL_SOURCE_DIR}/serialization/SnSerializationRegistry.h + ${LL_SOURCE_DIR}/serialization/SnSerialUtils.cpp + ${LL_SOURCE_DIR}/serialization/SnSerialUtils.h +) +SOURCE_GROUP(serialization FILES ${PHYSX_EXTENSIONS_SERIALIZATION_SOURCE}) + +SET(PHYSX_EXTENSIONS_SERIALIZATION_XML_SOURCE + ${LL_SOURCE_DIR}/serialization/Xml/SnJointRepXSerializer.cpp + ${LL_SOURCE_DIR}/serialization/Xml/SnJointRepXSerializer.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXCoreSerializer.cpp + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXUpgrader.cpp + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlSerialization.cpp + ${LL_SOURCE_DIR}/serialization/Xml/SnPxStreamOperators.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepX1_0Defaults.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepX3_1Defaults.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepX3_2Defaults.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXCollection.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXCoreSerializer.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXSerializerImpl.h + ${LL_SOURCE_DIR}/serialization/Xml/SnRepXUpgrader.h + ${LL_SOURCE_DIR}/serialization/Xml/SnSimpleXmlWriter.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlDeserializer.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlImpl.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlMemoryAllocator.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlMemoryPool.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlMemoryPoolStreams.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlReader.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlSerializer.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlSimpleXmlWriter.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlStringToType.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlVisitorReader.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlVisitorWriter.h + ${LL_SOURCE_DIR}/serialization/Xml/SnXmlWriter.h +) +SOURCE_GROUP(serialization\\xml FILES ${PHYSX_EXTENSIONS_SERIALIZATION_XML_SOURCE}) + +SET(PHYSX_EXTENSIONS_SERIALIZATION_FILE_SOURCE + ${LL_SOURCE_DIR}/serialization/File/SnFile.h +) +SOURCE_GROUP(serialization\\file FILES ${PHYSX_EXTENSIONS_SERIALIZATION_FILE_SOURCE}) + +SET(PHYSX_EXTENSIONS_SERIALIZATION_BINARY_SOURCE + ${LL_SOURCE_DIR}/serialization/Binary/SnBinaryDeserialization.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnBinarySerialization.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Align.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Convert.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Error.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_MetaData.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Output.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Union.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnSerializationContext.cpp + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX.h + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Align.h + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Common.h + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_MetaData.h + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Output.h + ${LL_SOURCE_DIR}/serialization/Binary/SnConvX_Union.h + ${LL_SOURCE_DIR}/serialization/Binary/SnSerializationContext.h +) +SOURCE_GROUP(serialization\\binary FILES ${PHYSX_EXTENSIONS_SERIALIZATION_BINARY_SOURCE}) + +ADD_LIBRARY(PhysXExtensions ${PHYSXEXTENSIONS_LIBTYPE} + ${PHYSXEXTENSIONS_PLATFORM_SRC_FILES} + + ${PHYSX_EXTENSIONS_SOURCE} + ${PHYSX_EXTENSIONS_METADATA_SOURCE} + + ${PHYSX_EXTENSIONS_HEADERS} + ${PHYSX_FILEBUF_HEADERS} + + ${PHYSX_EXTENSIONS_SERIALIZATION_SOURCE} + ${PHYSX_EXTENSIONS_SERIALIZATION_XML_SOURCE} + ${PHYSX_EXTENSIONS_SERIALIZATION_FILE_SOURCE} + ${PHYSX_EXTENSIONS_SERIALIZATION_BINARY_SOURCE} +) + +INSTALL(FILES ${PHYSX_EXTENSIONS_HEADERS} DESTINATION include/extensions) +INSTALL(FILES ${PHYSX_FILEBUF_HEADERS} DESTINATION include/filebuf) + +TARGET_INCLUDE_DIRECTORIES(PhysXExtensions + + PRIVATE ${PHYSXEXTENSIONS_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/pvd + PRIVATE ${PHYSX_ROOT_DIR}/include/cooking + PRIVATE ${PHYSX_ROOT_DIR}/include/extensions + PRIVATE ${PHYSX_ROOT_DIR}/include/vehicle + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + + PRIVATE ${PHYSX_SOURCE_DIR}/physxmetadata/core/include + PRIVATE ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/include + + PRIVATE ${PHYSX_SOURCE_DIR}/physxextensions/src + PRIVATE ${PHYSX_SOURCE_DIR}/physxextensions/src/serialization/Xml + PRIVATE ${PHYSX_SOURCE_DIR}/physxextensions/src/serialization/Binary + PRIVATE ${PHYSX_SOURCE_DIR}/physxextensions/src/serialization/File + + PRIVATE ${PHYSX_SOURCE_DIR}/pvdsdk/src + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src + + PRIVATE ${PHYSX_SOURCE_DIR}/pvd/include + + PRIVATE ${PHYSX_SOURCE_DIR}/fastxml/include +) + +TARGET_LINK_LIBRARIES(PhysXExtensions + PRIVATE ${PHYSXEXTENSIONS_PRIVATE_PLATFORM_LINKED_LIBS} + PUBLIC PhysXFoundation + PUBLIC PhysXPvdSDK + PUBLIC PhysX +) + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(PhysXExtensions + PRIVATE ${PHYSXEXTENSIONS_COMPILE_DEFS} +) + + +SET_TARGET_PROPERTIES(PhysXExtensions PROPERTIES + OUTPUT_NAME PhysXExtensions +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXEXTENSIONS_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXExtensions PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXExtensions_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXExtensions_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXExtensions_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXExtensions_static" + ) +ENDIF() + +IF(PHYSXEXTENSIONS_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXExtensions PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXEXTENSIONS_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXEXTENSIONS_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXEXTENSIONS_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXEXTENSIONS_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXEXTENSIONS_PLATFORM_SRC_FILES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_METADATA_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_FILEBUF_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_SERIALIZATION_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_SERIALIZATION_XML_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_SERIALIZATION_FILE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_EXTENSIONS_SERIALIZATION_BINARY_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXExtensions PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake new file mode 100644 index 000000000..fb93058f3 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake @@ -0,0 +1,198 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/foundation) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXFoundation.cmake) + +SET(PHYSXFOUNDATION_HEADERS + ${PHYSX_ROOT_DIR}/include/PxFoundation.h +) +SOURCE_GROUP(include FILES ${PHYSXFOUNDATION_HEADERS}) + +SET(PXSHARED_HEADERS + ${PXSHARED_PATH}/include/foundation/Px.h + ${PXSHARED_PATH}/include/foundation/PxAllocatorCallback.h + ${PXSHARED_PATH}/include/foundation/PxProfiler.h + ${PXSHARED_PATH}/include/foundation/PxAssert.h + ${PXSHARED_PATH}/include/foundation/PxBitAndData.h + ${PXSHARED_PATH}/include/foundation/PxBounds3.h + ${PXSHARED_PATH}/include/foundation/PxErrorCallback.h + ${PXSHARED_PATH}/include/foundation/PxErrors.h + ${PXSHARED_PATH}/include/foundation/PxFlags.h + ${PXSHARED_PATH}/include/foundation/PxIntrinsics.h + ${PXSHARED_PATH}/include/foundation/PxIO.h + ${PXSHARED_PATH}/include/foundation/PxMat33.h + ${PXSHARED_PATH}/include/foundation/PxMat44.h + ${PXSHARED_PATH}/include/foundation/PxMath.h + ${PXSHARED_PATH}/include/foundation/PxMathUtils.h + ${PXSHARED_PATH}/include/foundation/PxMemory.h + ${PXSHARED_PATH}/include/foundation/PxPlane.h + ${PXSHARED_PATH}/include/foundation/PxPreprocessor.h + ${PXSHARED_PATH}/include/foundation/PxQuat.h + ${PXSHARED_PATH}/include/foundation/PxSimpleTypes.h + ${PXSHARED_PATH}/include/foundation/PxStrideIterator.h + ${PXSHARED_PATH}/include/foundation/PxTransform.h + ${PXSHARED_PATH}/include/foundation/PxUnionCast.h + ${PXSHARED_PATH}/include/foundation/PxVec2.h + ${PXSHARED_PATH}/include/foundation/PxVec3.h + ${PXSHARED_PATH}/include/foundation/PxVec4.h +) +SOURCE_GROUP(shared\\include FILES ${PXSHARED_HEADERS}) + +SET(PHYSXFOUNDATION_SOURCE + ${LL_SOURCE_DIR}/src/PsAllocator.cpp + ${LL_SOURCE_DIR}/src/PsAssert.cpp + ${LL_SOURCE_DIR}/src/PsFoundation.cpp + ${LL_SOURCE_DIR}/src/PsMathUtils.cpp + ${LL_SOURCE_DIR}/src/PsString.cpp + ${LL_SOURCE_DIR}/src/PsTempAllocator.cpp + ${LL_SOURCE_DIR}/src/PsUtilities.cpp +) +SOURCE_GROUP(src\\src FILES ${PHYSXFOUNDATION_SOURCE}) + +SET(PHYSXFOUNDATION_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/Ps.h + ${LL_SOURCE_DIR}/include/PsAlignedMalloc.h + ${LL_SOURCE_DIR}/include/PsAlloca.h + ${LL_SOURCE_DIR}/include/PsAllocator.h + ${LL_SOURCE_DIR}/include/PsAoS.h + ${LL_SOURCE_DIR}/include/PsArray.h + ${LL_SOURCE_DIR}/include/PsAtomic.h + ${LL_SOURCE_DIR}/include/PsBasicTemplates.h + ${LL_SOURCE_DIR}/include/PsBitUtils.h + ${LL_SOURCE_DIR}/include/PsBroadcast.h + ${LL_SOURCE_DIR}/include/PsCpu.h + ${LL_SOURCE_DIR}/include/PsFoundation.h + ${LL_SOURCE_DIR}/include/PsFPU.h + ${LL_SOURCE_DIR}/include/PsHash.h + ${LL_SOURCE_DIR}/include/PsHashInternals.h + ${LL_SOURCE_DIR}/include/PsHashMap.h + ${LL_SOURCE_DIR}/include/PsHashSet.h + ${LL_SOURCE_DIR}/include/PsInlineAllocator.h + ${LL_SOURCE_DIR}/include/PsInlineAoS.h + ${LL_SOURCE_DIR}/include/PsInlineArray.h + ${LL_SOURCE_DIR}/include/PsIntrinsics.h + ${LL_SOURCE_DIR}/include/PsMathUtils.h + ${LL_SOURCE_DIR}/include/PsMutex.h + ${LL_SOURCE_DIR}/include/PsPool.h + ${LL_SOURCE_DIR}/include/PsSList.h + ${LL_SOURCE_DIR}/include/PsSocket.h + ${LL_SOURCE_DIR}/include/PsSort.h + ${LL_SOURCE_DIR}/include/PsSortInternals.h + ${LL_SOURCE_DIR}/include/PsString.h + ${LL_SOURCE_DIR}/include/PsSync.h + ${LL_SOURCE_DIR}/include/PsTempAllocator.h + ${LL_SOURCE_DIR}/include/PsThread.h + ${LL_SOURCE_DIR}/include/PsTime.h + ${LL_SOURCE_DIR}/include/PsUserAllocated.h + ${LL_SOURCE_DIR}/include/PsUtilities.h + ${LL_SOURCE_DIR}/include/PsVecMath.h + ${LL_SOURCE_DIR}/include/PsVecMathAoSScalar.h + ${LL_SOURCE_DIR}/include/PsVecMathAoSScalarInline.h + ${LL_SOURCE_DIR}/include/PsVecMathSSE.h + ${LL_SOURCE_DIR}/include/PsVecMathUtilities.h + ${LL_SOURCE_DIR}/include/PsVecQuat.h + ${LL_SOURCE_DIR}/include/PsVecTransform.h +) +SOURCE_GROUP("src\\include" FILES ${PHYSXFOUNDATION_SOURCE_HEADERS}) + +ADD_LIBRARY(PhysXFoundation ${PHYSXFOUNDATION_LIBTYPE} + ${PHYSXFOUNDATION_SOURCE} + ${PHYSXFOUNDATION_SOURCE_HEADERS} + ${PHYSXFOUNDATION_HEADERS} + ${PHYSXFOUNDATION_PLATFORM_FILES} + ${PXSHARED_HEADERS} + ${PXSHARED_PLATFORM_HEADERS} +) + +# Add the headers to the install +INSTALL(FILES ${PHYSXFOUNDATION_HEADERS} DESTINATION include) +INSTALL(FILES ${PHYSXFOUNDATION_SOURCE_HEADERS} DESTINATION source/foundation/include) +INSTALL(FILES ${PXSHARED_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation) + +TARGET_INCLUDE_DIRECTORIES(PhysXFoundation + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PXSHARED_PATH}/include + PRIVATE ${LL_SOURCE_DIR}/include + PRIVATE ${PHYSXFOUNDATION_PLATFORM_INCLUDES} + + INTERFACE $$ + INTERFACE $$ + + # FIXME: This is really terrible! Don't export src directories + INTERFACE $$ +) + +TARGET_COMPILE_DEFINITIONS(PhysXFoundation + PRIVATE ${PHYSXFOUNDATION_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXFoundation PROPERTIES + OUTPUT_NAME PhysXFoundation +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXFOUNDATION_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXFoundation PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXFoundation_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXFoundation_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXFoundation_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXFoundation_static" + ) +ENDIF() + +IF(PHYSXFOUNDATION_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXFoundation PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXFOUNDATION_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXFOUNDATION_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXFOUNDATION_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXFOUNDATION_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +# Add linked libraries +TARGET_LINK_LIBRARIES(PhysXFoundation + PRIVATE ${PHYSXFOUNDATION_PLATFORM_LINKED_LIBS} +) + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_SOURCE_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_PLATFORM_FILES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PXSHARED_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PXSHARED_PLATFORM_HEADERS}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXFoundation PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake new file mode 100644 index 000000000..aad0e7143 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake @@ -0,0 +1,186 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/pvd) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXPvdSDK.cmake) + + +SET(PHYSXPVDSDK_HEADERS + ${PHYSX_ROOT_DIR}/include/pvd/PxPvd.h + ${PHYSX_ROOT_DIR}/include/pvd/PxPvdTransport.h +) +SOURCE_GROUP(include FILES ${PHYSXPVDSDK_HEADERS}) + +SET(PHYSXPVDSDK_SOURCE + ${LL_SOURCE_DIR}/src/PxProfileContextProvider.h + ${LL_SOURCE_DIR}/src/PxProfileContextProviderImpl.h + ${LL_SOURCE_DIR}/src/PxProfileDataBuffer.h + ${LL_SOURCE_DIR}/src/PxProfileDataParsing.h + ${LL_SOURCE_DIR}/src/PxProfileEventBuffer.h + ${LL_SOURCE_DIR}/src/PxProfileEventBufferAtomic.h + ${LL_SOURCE_DIR}/src/PxProfileEventBufferClient.h + ${LL_SOURCE_DIR}/src/PxProfileEventBufferClientManager.h + ${LL_SOURCE_DIR}/src/PxProfileEventId.h + ${LL_SOURCE_DIR}/src/PxProfileEventImpl.cpp + ${LL_SOURCE_DIR}/src/PxProfileEventMutex.h + ${LL_SOURCE_DIR}/src/PxProfileEventNames.h + ${LL_SOURCE_DIR}/src/PxProfileEvents.h + ${LL_SOURCE_DIR}/src/PxProfileEventSender.h + ${LL_SOURCE_DIR}/src/PxProfileEventSerialization.h + ${LL_SOURCE_DIR}/src/PxProfileMemory.h + ${LL_SOURCE_DIR}/src/PxProfileMemoryBuffer.h + ${LL_SOURCE_DIR}/src/PxProfileMemoryEventBuffer.h + ${LL_SOURCE_DIR}/src/PxProfileMemoryEvents.h + ${LL_SOURCE_DIR}/src/PxProfileScopedEvent.h + ${LL_SOURCE_DIR}/src/PxProfileScopedMutexLock.h + ${LL_SOURCE_DIR}/src/PxPvdProfileZone.h + ${LL_SOURCE_DIR}/src/PxProfileZoneImpl.h + ${LL_SOURCE_DIR}/src/PxProfileZoneManager.h + ${LL_SOURCE_DIR}/src/PxProfileZoneManagerImpl.h + ${LL_SOURCE_DIR}/src/PxPvd.cpp + ${LL_SOURCE_DIR}/src/PxPvdBits.h + ${LL_SOURCE_DIR}/src/PxPvdByteStreams.h + ${LL_SOURCE_DIR}/src/PxPvdCommStreamEvents.h + ${LL_SOURCE_DIR}/src/PxPvdCommStreamEventSink.h + ${LL_SOURCE_DIR}/src/PxPvdCommStreamTypes.h + ${LL_SOURCE_DIR}/src/PxPvdDataStream.cpp + ${LL_SOURCE_DIR}/src/PxPvdDefaultFileTransport.cpp + ${LL_SOURCE_DIR}/src/PxPvdDefaultFileTransport.h + ${LL_SOURCE_DIR}/src/PxPvdDefaultSocketTransport.cpp + ${LL_SOURCE_DIR}/src/PxPvdDefaultSocketTransport.h + ${LL_SOURCE_DIR}/src/PxPvdFoundation.h + ${LL_SOURCE_DIR}/src/PxPvdImpl.cpp + ${LL_SOURCE_DIR}/src/PxPvdImpl.h + ${LL_SOURCE_DIR}/src/PxPvdInternalByteStreams.h + ${LL_SOURCE_DIR}/src/PxPvdMarshalling.h + ${LL_SOURCE_DIR}/src/PxPvdMemClient.cpp + ${LL_SOURCE_DIR}/src/PxPvdMemClient.h + ${LL_SOURCE_DIR}/src/PxPvdObjectModelInternalTypeDefs.h + ${LL_SOURCE_DIR}/src/PxPvdObjectModelInternalTypes.h + ${LL_SOURCE_DIR}/src/PxPvdObjectModelMetaData.cpp + ${LL_SOURCE_DIR}/src/PxPvdObjectModelMetaData.h + ${LL_SOURCE_DIR}/src/PxPvdObjectRegistrar.cpp + ${LL_SOURCE_DIR}/src/PxPvdObjectRegistrar.h + ${LL_SOURCE_DIR}/src/PxPvdProfileZoneClient.cpp + ${LL_SOURCE_DIR}/src/PxPvdProfileZoneClient.h + ${LL_SOURCE_DIR}/src/PxPvdUserRenderer.cpp + ${LL_SOURCE_DIR}/src/PxPvdUserRenderImpl.h + ${LL_SOURCE_DIR}/src/PxPvdUserRenderTypes.h +) +SOURCE_GROUP(src\\src FILES ${PHYSXPVDSDK_SOURCE}) + +SET(PHYSXPVDSDK_INTERNAL_HEADERS + ${LL_SOURCE_DIR}/include/PsPvd.h + ${LL_SOURCE_DIR}/include/PxProfileAllocatorWrapper.h + ${LL_SOURCE_DIR}/include/PxPvdClient.h + ${LL_SOURCE_DIR}/include/PxPvdDataStream.h + ${LL_SOURCE_DIR}/include/PxPvdDataStreamHelpers.h + ${LL_SOURCE_DIR}/include/PxPvdErrorCodes.h + ${LL_SOURCE_DIR}/include/PxPvdObjectModelBaseTypes.h + ${LL_SOURCE_DIR}/include/PxPvdRenderBuffer.h + ${LL_SOURCE_DIR}/include/PxPvdUserRenderer.h +) +SOURCE_GROUP(src\\include FILES ${PHYSXPVDSDK_INTERNAL_HEADERS}) + +SET(PHYSXPVDSDK_FILEBUF_FILES + ${PHYSX_SOURCE_DIR}/filebuf/include/PsAsciiConversion.h + ${PHYSX_SOURCE_DIR}/filebuf/include/PsAsciiConversion.inl + ${PHYSX_SOURCE_DIR}/filebuf/include/PsFileBuffer.h + ${PHYSX_SOURCE_DIR}/filebuf/include/PsIOStream.h + ${PHYSX_SOURCE_DIR}/filebuf/include/PsIOStream.inl + ${PHYSX_SOURCE_DIR}/filebuf/include/PsMemoryBuffer.h +) +SOURCE_GROUP(filebuf\\include FILES ${PHYSXPVDSDK_FILEBUF_FILES}) + +ADD_LIBRARY(PhysXPvdSDK ${PHYSXPVDSDK_LIBTYPE} + ${PHYSXPVDSDK_HEADERS} + ${PHYSXPVDSDK_FILEBUF_FILES} + + ${PHYSXPVDSDK_INTERNAL_HEADERS} + ${PHYSXPVDSDK_SOURCE} + + ${PHYSXPVDSDK_PLATFORM_FILES} +) + +TARGET_INCLUDE_DIRECTORIES(PhysXPvdSDK + PRIVATE ${PHYSXPVDSDK_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${LL_SOURCE_DIR}/include + PRIVATE ${PHYSX_SOURCE_DIR}/filebuf/include + +) + +TARGET_COMPILE_DEFINITIONS(PhysXPvdSDK + PRIVATE ${PHYSXPVDSDK_COMPILE_DEFS} +) + +# Add linked libraries +TARGET_LINK_LIBRARIES(PhysXPvdSDK + PUBLIC ${PHYSXPVDSDK_PLATFORM_LINKED_LIBS} + PUBLIC PhysXFoundation +) + +SET_TARGET_PROPERTIES(PhysXPvdSDK PROPERTIES + OUTPUT_NAME PhysXPvdSDK +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXPVDSDK_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXPvdSDK PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXPvdSDK_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXPvdSDK_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXPvdSDK_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXPvdSDK_static" + ) +ENDIF() + +IF(PHYSXPVDSDK_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXPvdSDK PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXPVDSDK_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXPVDSDK_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXPVDSDK_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXPVDSDK_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXPVDSDK_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXPVDSDK_INTERNAL_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXPVDSDK_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXPVDSDK_PLATFORM_FILES}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXPVDSDK_FILEBUF_FILES}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXPvdSDK PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake new file mode 100644 index 000000000..61596f3d9 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake @@ -0,0 +1,103 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/task) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXTask.cmake) + +SET(PHYSXTASK_HEADERS + ${PHYSX_ROOT_DIR}/include/task/PxCpuDispatcher.h + ${PHYSX_ROOT_DIR}/include/task/PxGpuDispatcher.h + ${PHYSX_ROOT_DIR}/include/task/PxGpuTask.h + ${PHYSX_ROOT_DIR}/include/task/PxTask.h + ${PHYSX_ROOT_DIR}/include/task/PxTaskDefine.h + ${PHYSX_ROOT_DIR}/include/task/PxTaskManager.h +) +SOURCE_GROUP(include FILES ${PHYSXTASK_HEADERS}) + +SET(PHYSXTASK_SOURCE + ${LL_SOURCE_DIR}/src/TaskManager.cpp +) +SOURCE_GROUP(src FILES ${PHYSXTASK_SOURCE}) + +ADD_LIBRARY(PhysXTask ${PHYSXTASK_LIBTYPE} + ${PHYSXTASK_HEADERS} + ${PHYSXTASK_SOURCE} +) + +INSTALL(FILES ${PHYSXTASK_HEADERS} DESTINATION include/task) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(PhysXTask + PRIVATE ${PHYSXTASK_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_SOURCE_DIR}/cudamanager/include +) + +TARGET_COMPILE_DEFINITIONS(PhysXTask + PRIVATE ${PHYSXTASK_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXTask PROPERTIES + OUTPUT_NAME PhysXTask +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXTASK_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXTask PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXTask_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXTask_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXTask_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXTask_static" + ) +ENDIF() + +IF(PHYSXTASK_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXTask PROPERTIES + COMPILE_PDB_NAME_DEBUG "${PHYSXTASK_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${PHYSXTASK_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${PHYSXTASK_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${PHYSXTASK_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXTASK_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXTASK_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXTask PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake new file mode 100644 index 000000000..64ac6ff85 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake @@ -0,0 +1,176 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/physxvehicle/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/PhysXVehicle.cmake) + + + +SET(PHYSX_VEHICLE_HEADERS + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleComponents.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleDrive.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleDrive4W.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleDriveNW.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleDriveTank.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleNoDrive.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleSDK.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleShaders.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleTireFriction.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleUpdate.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleUtil.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleUtilControl.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleUtilSetup.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleUtilTelemetry.h + ${PHYSX_ROOT_DIR}/include/vehicle/PxVehicleWheels.h +) +SOURCE_GROUP(include FILES ${PHYSX_VEHICLE_HEADERS}) + +SET(PHYSX_VEHICLE_SOURCE + ${LL_SOURCE_DIR}/PxVehicleComponents.cpp + ${LL_SOURCE_DIR}/PxVehicleDrive.cpp + ${LL_SOURCE_DIR}/PxVehicleDrive4W.cpp + ${LL_SOURCE_DIR}/PxVehicleDriveNW.cpp + ${LL_SOURCE_DIR}/PxVehicleDriveTank.cpp + ${LL_SOURCE_DIR}/PxVehicleMetaData.cpp + ${LL_SOURCE_DIR}/PxVehicleNoDrive.cpp + ${LL_SOURCE_DIR}/PxVehicleSDK.cpp + ${LL_SOURCE_DIR}/PxVehicleSerialization.cpp + ${LL_SOURCE_DIR}/PxVehicleSuspWheelTire4.cpp + ${LL_SOURCE_DIR}/PxVehicleTireFriction.cpp + ${LL_SOURCE_DIR}/PxVehicleUpdate.cpp + ${LL_SOURCE_DIR}/PxVehicleWheels.cpp + ${LL_SOURCE_DIR}/VehicleUtilControl.cpp + ${LL_SOURCE_DIR}/VehicleUtilSetup.cpp + ${LL_SOURCE_DIR}/VehicleUtilTelemetry.cpp + ${LL_SOURCE_DIR}/PxVehicleDefaults.h + ${LL_SOURCE_DIR}/PxVehicleLinearMath.h + ${LL_SOURCE_DIR}/PxVehicleSerialization.h + ${LL_SOURCE_DIR}/PxVehicleSuspLimitConstraintShader.h + ${LL_SOURCE_DIR}/PxVehicleSuspWheelTire4.h +) +SOURCE_GROUP(src FILES ${PHYSX_VEHICLE_SOURCE}) + +SET(PHYSX_VEHICLE_METADATA_HEADERS + ${LL_SOURCE_DIR}/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjectNames.h + ${LL_SOURCE_DIR}/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h + ${LL_SOURCE_DIR}/physxmetadata/include/PxVehicleMetaDataObjects.h +) +SOURCE_GROUP(metadata\\include FILES ${PHYSX_VEHICLE_METADATA_HEADERS}) + +SET(PHYSX_VEHICLE_METADATA_SOURCE + ${LL_SOURCE_DIR}/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp + ${LL_SOURCE_DIR}/physxmetadata/src/PxVehicleMetaDataObjects.cpp +) +SOURCE_GROUP(metadata\\src FILES ${PHYSX_VEHICLE_METADATA_SOURCE}) + +ADD_LIBRARY(PhysXVehicle ${PHYSXVEHICLE_LIBTYPE} + ${PHYSX_VEHICLE_SOURCE} + ${PHYSX_VEHICLE_HEADERS} + ${PHYSX_VEHICLE_METADATA_HEADERS} + ${PHYSX_VEHICLE_METADATA_SOURCE} +) + +INSTALL(FILES ${PHYSX_VEHICLE_HEADERS} DESTINATION include/vehicle) + +TARGET_INCLUDE_DIRECTORIES(PhysXVehicle + PRIVATE ${PHYSXVEHICLE_PLATFORM_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/vehicle + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/extensions + PRIVATE ${PHYSX_ROOT_DIR}/include/pvd + PRIVATE ${PHYSX_ROOT_DIR}/include/physxprofilesdk + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physxvehicle/src + PRIVATE ${PHYSX_SOURCE_DIR}/physxvehicle/src/physxmetadata/include + + PRIVATE ${PHYSX_SOURCE_DIR}/physxmetadata/extensions/include + PRIVATE ${PHYSX_SOURCE_DIR}/physxmetadata/core/include + + PRIVATE ${PHYSX_SOURCE_DIR}/physxextensions/src/serialization/Xml + + PRIVATE ${PHYSX_SOURCE_DIR}/pvdsdk/src + + PRIVATE ${PHYSX_SOURCE_DIR}/pvd/include +) + +# No linked libraries + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(PhysXVehicle + PRIVATE ${PHYSXVEHICLE_COMPILE_DEFS} +) + +SET_TARGET_PROPERTIES(PhysXVehicle PROPERTIES + OUTPUT_NAME PhysXVehicle +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXVEHICLE_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(PhysXVehicle PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "PhysXVehicle_static" + ARCHIVE_OUTPUT_NAME_CHECKED "PhysXVehicle_static" + ARCHIVE_OUTPUT_NAME_PROFILE "PhysXVehicle_static" + ARCHIVE_OUTPUT_NAME_RELEASE "PhysXVehicle_static" + ) +ENDIF() + +IF(PHYSXVEHICLE_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(PhysXVehicle PROPERTIES + COMPILE_PDB_NAME_DEBUG ${PHYSXVEHICLE_COMPILE_PDB_NAME_DEBUG} + COMPILE_PDB_NAME_CHECKED ${PHYSXVEHICLE_COMPILE_PDB_NAME_CHECKED} + COMPILE_PDB_NAME_PROFILE ${PHYSXVEHICLE_COMPILE_PDB_NAME_PROFILE} + COMPILE_PDB_NAME_RELEASE ${PHYSXVEHICLE_COMPILE_PDB_NAME_RELEASE} + ) +ENDIF() + +TARGET_LINK_LIBRARIES(PhysXVehicle + PUBLIC ${PHYSXVEHICLE_PLATFORM_LINKED_LIBS} PhysXFoundation PhysXPvdSDK +) + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_VEHICLE_SOURCE}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_VEHICLE_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_VEHICLE_METADATA_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSX_VEHICLE_METADATA_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(PhysXVehicle PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake new file mode 100644 index 000000000..c73b20b24 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake @@ -0,0 +1,159 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/scenequery/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/SceneQuery.cmake) + + +SET(SCENEQUERY_BASE_DIR ${PHYSX_ROOT_DIR}/source/scenequery) +SET(SCENEQUERY_HEADERS + ${SCENEQUERY_BASE_DIR}/include/SqPruner.h + ${SCENEQUERY_BASE_DIR}/include/SqPrunerMergeData.h + ${SCENEQUERY_BASE_DIR}/include/SqPruningStructure.h + ${SCENEQUERY_BASE_DIR}/include/SqSceneQueryManager.h +) +SOURCE_GROUP(include FILES ${SCENEQUERY_HEADERS}) + +SET(SCENEQUERY_SOURCE + ${SCENEQUERY_BASE_DIR}/src/SqAABBPruner.cpp + ${SCENEQUERY_BASE_DIR}/src/SqAABBPruner.h + ${SCENEQUERY_BASE_DIR}/src/SqAABBTree.cpp + ${SCENEQUERY_BASE_DIR}/src/SqAABBTree.h + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBPruner.cpp + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBPruner.h + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBPrunerCore.cpp + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBPrunerCore.h + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBTree.cpp + ${SCENEQUERY_BASE_DIR}/src/SqIncrementalAABBTree.h + ${SCENEQUERY_BASE_DIR}/src/SqAABBTreeUpdateMap.cpp + ${SCENEQUERY_BASE_DIR}/src/SqAABBTreeUpdateMap.h + ${SCENEQUERY_BASE_DIR}/src/SqBounds.cpp + ${SCENEQUERY_BASE_DIR}/src/SqBounds.h + ${SCENEQUERY_BASE_DIR}/src/SqCompoundPruner.cpp + ${SCENEQUERY_BASE_DIR}/src/SqCompoundPruner.h + ${SCENEQUERY_BASE_DIR}/src/SqCompoundPruningPool.cpp + ${SCENEQUERY_BASE_DIR}/src/SqCompoundPruningPool.h + ${SCENEQUERY_BASE_DIR}/src/SqBucketPruner.cpp + ${SCENEQUERY_BASE_DIR}/src/SqBucketPruner.h + ${SCENEQUERY_BASE_DIR}/src/SqExtendedBucketPruner.cpp + ${SCENEQUERY_BASE_DIR}/src/SqExtendedBucketPruner.h + ${SCENEQUERY_BASE_DIR}/src/SqMetaData.cpp + ${SCENEQUERY_BASE_DIR}/src/SqPruningPool.cpp + ${SCENEQUERY_BASE_DIR}/src/SqPruningPool.h + ${SCENEQUERY_BASE_DIR}/src/SqPruningStructure.cpp + ${SCENEQUERY_BASE_DIR}/src/SqSceneQueryManager.cpp + ${SCENEQUERY_BASE_DIR}/src/SqTypedef.h +) +SOURCE_GROUP(src FILES ${SCENEQUERY_SOURCE}) + +ADD_LIBRARY(SceneQuery ${SCENEQUERY_LIBTYPE} + ${SCENEQUERY_HEADERS} + ${SCENEQUERY_SOURCE} +) + +# Target specific compile options + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(SceneQuery + PRIVATE ${SCENEQUERY_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/pvd + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + + PRIVATE ${PHYSX_SOURCE_DIR}/scenequery/include + PRIVATE ${PHYSX_SOURCE_DIR}/simulationcontroller/include + + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src + PRIVATE ${PHYSX_SOURCE_DIR}/physx/src/buffering + + PRIVATE ${PHYSX_SOURCE_DIR}/pvd/include +) + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(SceneQuery + + # Common to all configurations + PRIVATE ${SCENEQUERY_COMPILE_DEFS} +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND SCENEQUERY_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(SceneQuery PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "SceneQuery_static" + ARCHIVE_OUTPUT_NAME_CHECKED "SceneQuery_static" + ARCHIVE_OUTPUT_NAME_PROFILE "SceneQuery_static" + ARCHIVE_OUTPUT_NAME_RELEASE "SceneQuery_static" + ) +ENDIF() + +IF(SCENEQUERY_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(SceneQuery PROPERTIES + COMPILE_PDB_NAME_DEBUG "${SCENEQUERY_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${SCENEQUERY_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${SCENEQUERY_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${SCENEQUERY_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${SCENEQUERY_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${SCENEQUERY_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(SceneQuery PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/SimulationController.cmake new file mode 100644 index 000000000..577a75d38 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/SimulationController.cmake @@ -0,0 +1,207 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController common +# + +SET(PHYSX_SOURCE_DIR ${PHYSX_ROOT_DIR}/source) +SET(LL_SOURCE_DIR ${PHYSX_SOURCE_DIR}/simulationcontroller/src) + +# Include here after the directories are defined so that the platform specific file can use the variables. +include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/SimulationController.cmake) + + +SET(SIMULATIONCONTROLLER_BASE_DIR ${PHYSX_ROOT_DIR}/source/simulationcontroller) +SET(SIMULATIONCONTROLLER_HEADERS + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScActorCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScArticulationCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScArticulationJointCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScBodyCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScConstraintCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScIterators.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScMaterialCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScPhysics.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScRigidCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScScene.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScShapeCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/include/ScStaticCore.h +) +SOURCE_GROUP(include FILES ${SIMULATIONCONTROLLER_HEADERS}) + +SET(SIMULATIONCONTROLLER_SOURCE + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScActorCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScActorPair.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScActorSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScActorSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationJointCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationJointSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationJointSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScArticulationSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScBodyCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScBodySim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScBodySim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScClient.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintGroupNode.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintGroupNode.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintInteraction.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintInteraction.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintProjectionManager.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintProjectionManager.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintProjectionTree.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintProjectionTree.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScConstraintSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScContactReportBuffer.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScContactStream.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScElementInteractionMarker.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScElementInteractionMarker.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScElementSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScElementSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScElementSimInteraction.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScInteraction.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScInteraction.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScInteractionFlags.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScIterators.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScMetaData.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScNPhaseCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScNPhaseCore.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScObjectIDTracker.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScPhysics.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScRigidCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScRigidSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScRigidSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScScene.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScShapeCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScShapeInteraction.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScShapeInteraction.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScShapeSim.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScShapeSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSimStateData.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSimStats.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSimStats.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSimulationController.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSimulationController.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSqBoundsManager.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScSqBoundsManager.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScStaticCore.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScStaticSim.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScTriggerInteraction.cpp + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScTriggerInteraction.h + ${SIMULATIONCONTROLLER_BASE_DIR}/src/ScTriggerPairs.h +) +SOURCE_GROUP(src FILES ${SIMULATIONCONTROLLER_SOURCE}) + +ADD_LIBRARY(SimulationController ${SIMULATIONCONTROLLER_LIBTYPE} + ${SIMULATIONCONTROLLER_HEADERS} + ${SIMULATIONCONTROLLER_SOURCE} +) + +GET_TARGET_PROPERTY(PHYSXFOUNDATION_INCLUDES PhysXFoundation INTERFACE_INCLUDE_DIRECTORIES) + +TARGET_INCLUDE_DIRECTORIES(SimulationController + PRIVATE ${SIMULATIONCONTROLLER_PLATFORM_INCLUDES} + + PRIVATE ${PHYSXFOUNDATION_INCLUDES} + + PRIVATE ${PHYSX_ROOT_DIR}/include + PRIVATE ${PHYSX_ROOT_DIR}/include/common + PRIVATE ${PHYSX_ROOT_DIR}/include/geometry + PRIVATE ${PHYSX_ROOT_DIR}/include/pvd + PRIVATE ${PHYSX_ROOT_DIR}/include/geomutils + + PRIVATE ${PHYSX_SOURCE_DIR}/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/common/src + + PRIVATE ${PHYSX_SOURCE_DIR}/physxgpu/include + + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/include + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/contact + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/common + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/convex + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/distance + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/sweep + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/gjk + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/intersection + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/mesh + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/hf + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/pcm + PRIVATE ${PHYSX_SOURCE_DIR}/geomutils/src/ccd + + PRIVATE ${PHYSX_SOURCE_DIR}/simulationcontroller/include + PRIVATE ${PHYSX_SOURCE_DIR}/simulationcontroller/src + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/api/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/collision + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/common/include/utils + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevel/software/include + + PRIVATE ${PHYSX_SOURCE_DIR}/lowleveldynamics/include + + PRIVATE ${PHYSX_SOURCE_DIR}/lowlevelaabb/include + +) + +# Use generator expressions to set config specific preprocessor definitions +TARGET_COMPILE_DEFINITIONS(SimulationController + + # Common to all configurations + PRIVATE ${SIMULATIONCONTROLLER_COMPILE_DEFS} +) + + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND SIMULATIONCONTROLLER_LIBTYPE STREQUAL "STATIC") + SET_TARGET_PROPERTIES(SimulationController PROPERTIES + ARCHIVE_OUTPUT_NAME_DEBUG "SimulationController_static" + ARCHIVE_OUTPUT_NAME_CHECKED "SimulationController_static" + ARCHIVE_OUTPUT_NAME_PROFILE "SimulationController_static" + ARCHIVE_OUTPUT_NAME_RELEASE "SimulationController_static" + ) +ENDIF() + +IF(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_DEBUG) + SET_TARGET_PROPERTIES(SimulationController PROPERTIES + COMPILE_PDB_NAME_DEBUG "${SIMULATIONCONTROLLER_COMPILE_PDB_NAME_DEBUG}" + COMPILE_PDB_NAME_CHECKED "${SIMULATIONCONTROLLER_COMPILE_PDB_NAME_CHECKED}" + COMPILE_PDB_NAME_PROFILE "${SIMULATIONCONTROLLER_COMPILE_PDB_NAME_PROFILE}" + COMPILE_PDB_NAME_RELEASE "${SIMULATIONCONTROLLER_COMPILE_PDB_NAME_RELEASE}" + ) +ENDIF() + +IF(PX_GENERATE_SOURCE_DISTRO) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${SIMULATIONCONTROLLER_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${SIMULATIONCONTROLLER_SOURCE}) +ENDIF() + +# enable -fPIC so we can link static libs with the editor +SET_TARGET_PROPERTIES(SimulationController PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt new file mode 100644 index 000000000..b53ca716b --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt @@ -0,0 +1,94 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# We define the CXX flags for this CMakeLists and all others that are included afterwards. This is a GLOBAL setting. +# If/when the solutions go standalone (say, samples and visual tests) - those CMakeLists will need to be fixed. + +STRING(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWERCASE) + +SET(PHYSX_WARNING_DISABLES "-Wno-invalid-offsetof -Wno-maybe-uninitialized -Wno-unused-variable -Wno-variadic-macros -Wno-array-bounds") + +IF(${ANDROID_ABI} STREQUAL "armeabi-v7a") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wpedantic -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -funwind-tables -fomit-frame-pointer -funswitch-loops -finline-limit=300 -fno-strict-aliasing -fstack-protector ${PHYSX_WARNING_DISABLES}" CACHE INTERAL "PhysX CXX") +ELSEIF(${ANDROID_ABI} STREQUAL "armeabi-v7a with NEON") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wpedantic -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -funwind-tables -fomit-frame-pointer -funswitch-loops -finline-limit=300 -fno-strict-aliasing -fstack-protector ${PHYSX_WARNING_DISABLES}" CACHE INTERAL "PhysX CXX") +ELSEIF(${ANDROID_ABI} STREQUAL "arm64-v8a") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wpedantic -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections ${PHYSX_WARNING_DISABLES} " CACHE INTERAL "PhysX CXX") +ELSEIF(${ANDROID_ABI} STREQUAL "x86") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wpedantic -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections ${PHYSX_WARNING_DISABLES} -fpack-struct=8 -malign-double " CACHE INTERAL "PhysX CXX") +ELSEIF(${ANDROID_ABI} STREQUAL "x86_64") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wpedantic -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections ${PHYSX_WARNING_DISABLES} -mstackrealign -msse3 " CACHE INTERAL "PhysX CXX") +ENDIF() + +# Build debug info for all configurations +SET(PHYSX_CXX_FLAGS_DEBUG "-O0 -g" CACHE INTERAL "PhysX Debug CXX Flags") +SET(PHYSX_CXX_FLAGS_CHECKED "-O3 -g" CACHE INTERAL "PhysX Checked CXX Flags") +SET(PHYSX_CXX_FLAGS_PROFILE "-O3 -g" CACHE INTERAL "PhysX Profile CXX Flags") +SET(PHYSX_CXX_FLAGS_RELEASE "-O3 -g" CACHE INTERAL "PhysX Release CXX Flags") + + +# These flags are local to the directory the CMakeLists.txt is in +SET(CMAKE_CXX_FLAGS ${PHYSX_CXX_FLAGS}) + +SET(CMAKE_CXX_FLAGS_DEBUG ${PHYSX_CXX_FLAGS_DEBUG}) +SET(CMAKE_CXX_FLAGS_CHECKED ${PHYSX_CXX_FLAGS_CHECKED}) +SET(CMAKE_CXX_FLAGS_PROFILE ${PHYSX_CXX_FLAGS_PROFILE}) +SET(CMAKE_CXX_FLAGS_RELEASE ${PHYSX_CXX_FLAGS_RELEASE}) + +# Controls PX_NVTX for all projects +SET(NVTX_FLAG "PX_NVTX=0") + +# Disable cuda and dx for all projects on Android +SET(PHYSX_ANDROID_COMPILE_DEFS "__STDC_LIMIT_MACROS;${PHYSX_AUTOBUILD};${PHYSX_AUTOBUILDNUMBER}" CACHE INTERNAL "Base PhysX preprocessor definitions") +SET(PHYSX_ANDROID_DEBUG_COMPILE_DEFS "_DEBUG;PX_DEBUG=1;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Debug PhysX preprocessor definitions") +SET(PHYSX_ANDROID_CHECKED_COMPILE_DEFS "NDEBUG;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Checked PhysX preprocessor definitions") +SET(PHYSX_ANDROID_PROFILE_COMPILE_DEFS "NDEBUG;PX_PROFILE=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Profile PhysX preprocessor definitions") +SET(PHYSX_ANDROID_RELEASE_COMPILE_DEFS "NDEBUG;PX_SUPPORT_PVD=0" CACHE INTERNAL "Release PhysX preprocessor definitions") + +SET(CMAKE_DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}") +SET(CMAKE_PROFILE_POSTFIX "${CMAKE_PROFILE_POSTFIX}") +SET(CMAKE_CHECKED_POSTFIX "${CMAKE_CHECKED_POSTFIX}") +SET(CMAKE_RELEASE_POSTFIX "${CMAKE_RELEASE_POSTFIX}") + + +# Include all of the projects +INCLUDE(PhysXFoundation.cmake) +INCLUDE(LowLevel.cmake) +INCLUDE(LowLevelAABB.cmake) +INCLUDE(LowLevelDynamics.cmake) +INCLUDE(PhysX.cmake) +INCLUDE(PhysXCharacterKinematic.cmake) +INCLUDE(PhysXCommon.cmake) +INCLUDE(PhysXCooking.cmake) +INCLUDE(PhysXExtensions.cmake) +INCLUDE(PhysXVehicle.cmake) +INCLUDE(PhysXPvdSDK.cmake) +INCLUDE(PhysXTask.cmake) +INCLUDE(SceneQuery.cmake) +INCLUDE(SimulationController.cmake) +INCLUDE(FastXml.cmake) + diff --git a/src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake new file mode 100644 index 000000000..7ba5b42da --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake @@ -0,0 +1,42 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml +# + +SET(FASTXML_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(FASTXML_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake new file mode 100644 index 000000000..8190dcae3 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake @@ -0,0 +1,53 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel +# + +SET(LOWLEVEL_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/linux + ${PHYSX_SOURCE_DIR}/LowLevel/software/include/linux + ${PHYSX_SOURCE_DIR}/LowLevelDynamics/include/linux + ${PHYSX_SOURCE_DIR}/LowLevel/common/include/pipeline/linux +) + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVEL_LIBTYPE OBJECT) + +SET(LOWLEVEL_PLATFORM_LINK_FLAGS "/MAP") + + diff --git a/src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake new file mode 100644 index 000000000..8438d7a2a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake @@ -0,0 +1,52 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB +# + + +SET(LOWLEVELAABB_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/linux + ${PHYSX_SOURCE_DIR}/LowLevelAABB/linux/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/src +) + +SET(LOWLEVELAABB_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELAABB_LIBTYPE OBJECT) + +SET(LOWLEVELAABB_PLATFORM_LINK_FLAGS "/MAP") diff --git a/src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake new file mode 100644 index 000000000..3c0478908 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake @@ -0,0 +1,49 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics +# + +SET(LOWLEVELDYNAMICS_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/linux + ${PHYSX_SOURCE_DIR}/lowlevel/software/include/linux + ${PHYSX_SOURCE_DIR}/lowleveldynamics/include/linux + ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline/linux +) + +SET(LOWLEVELDYNAMICS_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELDYNAMICS_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake new file mode 100644 index 000000000..be1c03fa1 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake @@ -0,0 +1,64 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) +# + +SET(PHYSX_PLATFORM_INCLUDES + ${NVTOOLSEXT_INCLUDE_DIRS} +) + +SET(PHYSX_PLATFORM_OBJECT_FILES + $ + $ + $ + $ + $ + $ +) + +SET(PHYSX_PLATFORM_SRC_FILES + ${PX_SOURCE_DIR}/device/linux/PhysXIndicatorLinux.cpp + ${PX_SOURCE_DIR}/gpu/PxGpu.cpp + ${PX_SOURCE_DIR}/gpu/PxPhysXGpuModuleLoader.cpp + + ${PHYSX_PLATFORM_OBJECT_FILES} +) + +SET(PHYSX_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSX_LIBTYPE STATIC) + diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..083c4a81c --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake @@ -0,0 +1,44 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic +# + +SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + + +SET(PHYSXCHARACTERKINEMATIC_LIBTYPE STATIC) + diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake new file mode 100644 index 000000000..33846420f --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake @@ -0,0 +1,51 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon +# + +SET(PHYSXCOMMON_LIBTYPE STATIC) + +SET(PXCOMMON_PLATFORM_SRC_FILES +) + +SET(PXCOMMON_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/linux +) + +SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PXCOMMON_PLATFORM_LINK_FLAGS "/MAP") \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake new file mode 100644 index 000000000..ea13629c4 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake @@ -0,0 +1,47 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking +# + +SET(PHYSXCOOKING_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB;PX_COOKING; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXCOOKING_PLATFORM_SRC_FILES +) + +SET(PHYSXCOOKING_LIBTYPE STATIC) + +SET(PHYSXCOOKING_LINK_FLAGS "/MAP") \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake new file mode 100644 index 000000000..a75161f1b --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake @@ -0,0 +1,54 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions +# + +SET(PHYSXEXTENSIONS_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/linux +) + +SET(PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES + $ +) + +SET(PHYSXEXTENSIONS_PLATFORM_SRC_FILES + ${PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES} +) + +SET(PHYSXEXTENSIONS_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + + diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake new file mode 100644 index 000000000..3a403b252 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake @@ -0,0 +1,87 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation +# + +SET(PHYSXFOUNDATION_LIBTYPE STATIC) + +SET(PXSHARED_PLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/unix/PxUnixIntrinsics.h +) +SOURCE_GROUP(shared\\include\\unix FILES ${PXSHARED_PLATFORM_HEADERS}) + +SET(PHYSXFOUNDATION_PLATFORM_FILES + ${LL_SOURCE_DIR}/src/unix/PsUnixAtomic.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixCpu.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixFPU.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixMutex.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixPrintString.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSList.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSocket.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSync.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixThread.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixTime.cpp + ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c +) +SOURCE_GROUP("src\\src\\unix" FILES ${PHYSXFOUNDATION_PLATFORM_FILES}) + +SET(PHYSXFOUNDATION_PLATFORM_INCLUDES + ${LL_SOURCE_DIR}/include/linux + ${ANDROID_NDK}/sources/android/cpufeatures +) + +SET(PHYSXFOUNDATION_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS}; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/unix/PsUnixInlineAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixTrigConstants.h + ${LL_SOURCE_DIR}/include/unix/PsUnixAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixIntrinsics.h + ${LL_SOURCE_DIR}/include/unix/PsUnixFPU.h +) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS_3 + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonAoS.h + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonInlineAoS.h +) + +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} DESTINATION source/foundation/include/unix) +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS_3} DESTINATION source/foundation/include/unix/neon) +INSTALL(FILES ${PXSHARED_PLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/unix) + + +SET(PHYSXFOUNDATION_PLATFORM_LINKED_LIBS log) diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake new file mode 100644 index 000000000..a38d1580c --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake @@ -0,0 +1,42 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK +# + +SET(PHYSXPVDSDK_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS}; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXPVDSDK_LIBTYPE STATIC) diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake new file mode 100644 index 000000000..350cf7961 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake @@ -0,0 +1,42 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask +# + +SET(PHYSXTASK_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS}; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXTASK_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake new file mode 100644 index 000000000..c3e9bee77 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake @@ -0,0 +1,43 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle +# + +SET(PHYSXVEHICLE_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXVEHICLE_LIBTYPE STATIC) + diff --git a/src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake new file mode 100644 index 000000000..03e10a690 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake @@ -0,0 +1,47 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery +# + +SET(SCENEQUERY_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/linux +) + +SET(SCENEQUERY_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(SCENEQUERY_LIBTYPE OBJECT) + diff --git a/src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake new file mode 100644 index 000000000..36d435d58 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake @@ -0,0 +1,48 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController +# + +SET(SIMULATIONCONTROLLER_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/linux + ${PHYSX_SOURCE_DIR}/LowLevel/linux/include +) + +SET(SIMULATIONCONTROLLER_COMPILE_DEFS + # Common to all configurations + ${PHYSX_ANDROID_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_ANDROID_RELEASE_COMPILE_DEFS};> +) + +SET(SIMULATIONCONTROLLER_LIBTYPE OBJECT) + diff --git a/src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt new file mode 100644 index 000000000..bcbaf2284 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt @@ -0,0 +1,76 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + + +SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -ferror-limit=0 -Wall -Wextra -Werror -fstrict-aliasing -Wstrict-aliasing=2 -Weverything -Wno-unknown-warning-option -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-undefined-reinterpret-cast -Wno-invalid-offsetof -Wno-zero-as-null-pointer-constant -gdwarf-2" CACHE INTERAL "PhysX CXX") + +SET(CMAKE_SHARED_LINKER_FLAGS_CHECKED "") +SET(CMAKE_SHARED_LINKER_FLAGS_PROFILE "") +SET(CMAKE_SHARED_LINKER_FLAGS "") + +# Build debug info for all configurations +SET(PHYSX_CXX_FLAGS_DEBUG "-O0 -g" CACHE INTERAL "PhysX Debug CXX Flags") +SET(PHYSX_CXX_FLAGS_CHECKED "-O3 -g" CACHE INTERAL "PhysX Checked CXX Flags") +SET(PHYSX_CXX_FLAGS_PROFILE "-O3 -g" CACHE INTERAL "PhysX Profile CXX Flags") +SET(PHYSX_CXX_FLAGS_RELEASE "-O3 -g" CACHE INTERAL "PhysX Release CXX Flags") + +# These flags are local to the directory the CMakeLists.txt is in +SET(CMAKE_CXX_FLAGS ${PHYSX_CXX_FLAGS}) + +SET(CMAKE_CXX_FLAGS_DEBUG ${PHYSX_CXX_FLAGS_DEBUG}) +SET(CMAKE_CXX_FLAGS_CHECKED ${PHYSX_CXX_FLAGS_CHECKED}) +SET(CMAKE_CXX_FLAGS_PROFILE ${PHYSX_CXX_FLAGS_PROFILE}) +SET(CMAKE_CXX_FLAGS_RELEASE ${PHYSX_CXX_FLAGS_RELEASE}) + +# Controls PX_NVTX for all projects +SET(NVTX_FLAG "PX_NVTX=0") + +# Disable cuda and dx for all projects on iOS +SET(PHYSX_IOS_COMPILE_DEFS "DISABLE_CUDA_PHYSX;${PHYSX_AUTOBUILD};${PHYSX_AUTOBUILDNUMBER}" CACHE INTERNAL "Base PhysX preprocessor definitions") +SET(PHYSX_IOS_DEBUG_COMPILE_DEFS "_DEBUG;PX_DEBUG=1;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Debug PhysX preprocessor definitions") +SET(PHYSX_IOS_CHECKED_COMPILE_DEFS "NDEBUG;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Checked PhysX preprocessor definitions") +SET(PHYSX_IOS_PROFILE_COMPILE_DEFS "NDEBUG;PX_PROFILE=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Profile PhysX preprocessor definitions") +SET(PHYSX_IOS_RELEASE_COMPILE_DEFS "NDEBUG;PX_SUPPORT_PVD=0" CACHE INTERNAL "Release PhysX preprocessor definitions") + + +# Include all of the projects +INCLUDE(PhysXFoundation.cmake) +INCLUDE(LowLevel.cmake) +INCLUDE(LowLevelAABB.cmake) +INCLUDE(LowLevelDynamics.cmake) +INCLUDE(PhysX.cmake) +INCLUDE(PhysXCharacterKinematic.cmake) +INCLUDE(PhysXCommon.cmake) +INCLUDE(PhysXCooking.cmake) +INCLUDE(PhysXExtensions.cmake) +INCLUDE(PhysXVehicle.cmake) +INCLUDE(PhysXPvdSDK.cmake) +INCLUDE(PhysXTask.cmake) +INCLUDE(SceneQuery.cmake) +INCLUDE(SimulationController.cmake) +INCLUDE(FastXml.cmake) + diff --git a/src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake new file mode 100644 index 000000000..299dec64b --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake @@ -0,0 +1,44 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml +# + +# Use generator expressions to set config specific preprocessor definitions +SET(FASTXML_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(FASTXML_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake new file mode 100644 index 000000000..9c0d07f0a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake @@ -0,0 +1,51 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel +# + +SET(LOWLEVEL_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/ios + ${PHYSX_SOURCE_DIR}/LowLevel/software/include/ios + ${PHYSX_SOURCE_DIR}/LowLevelDynamics/include/ios + ${PHYSX_SOURCE_DIR}/LowLevel/common/include/pipeline/ios +) + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVEL_PLATFORM_LINK_FLAGS " ") + +SET(LOWLEVEL_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake new file mode 100644 index 000000000..5ba12ba7a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake @@ -0,0 +1,53 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB +# + +SET(LOWLEVELAABB_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/ios + ${PHYSX_SOURCE_DIR}/LowLevelAABB/ios/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/src +) + + +SET(LOWLEVELAABB_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELAABB_PLATFORM_LINK_FLAGS " ") + +SET(LOWLEVELAABB_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake new file mode 100644 index 000000000..f2747d0e0 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake @@ -0,0 +1,52 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics +# + + +SET(LOWLEVELDYNAMICS_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/ios + ${PHYSX_SOURCE_DIR}/lowlevel/software/include/ios + ${PHYSX_SOURCE_DIR}/lowleveldynamics/include/ios + ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline/ios +) + +# Use generator expressions to set config specific preprocessor definitions +SET(LOWLEVELDYNAMICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELDYNAMICS_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake new file mode 100644 index 000000000..fe635d5ec --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake @@ -0,0 +1,68 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) +# + +SET(PHYSX_PLATFORM_INCLUDES +) + +SET(PHYSX_PLATFORM_OBJECT_FILES + $ + $ + $ + $ + $ + $ +) + +SET(PHYSX_PLATFORM_SRC_FILES + ${PX_SOURCE_DIR}/gpu/PxGpu.cpp + ${PX_SOURCE_DIR}/gpu/PxPhysXGpuModuleLoader.cpp + + ${PHYSX_PLATFORM_OBJECT_FILES} +) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSX_COMPILE_DEFS + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_CORE_EXPORTS + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSX_LIBTYPE STATIC) + +SET(PHYSX_PLATFORM_LINK_FLAGS " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_DEBUG " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_CHECKED " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_PROFILE " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_RELEASE " ") diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..af2d60b58 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake @@ -0,0 +1,43 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic +# + + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_CHARACTER_EXPORTS;PX_PHYSX_CORE_EXPORTS;PX_FOUNDATION_DLL=1; + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake new file mode 100644 index 000000000..b19f22570 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake @@ -0,0 +1,52 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon +# + +SET(PHYSXCOMMON_LIBTYPE STATIC) + +SET(PXCOMMON_PLATFORM_SRC_FILES +) + +SET(PXCOMMON_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/ios +) + +SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS; + + # Switch platforms here? + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS}> +) + +SET(PXCOMMON_PLATFORM_LINK_FLAGS " ") \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake new file mode 100644 index 000000000..73e6d26b2 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake @@ -0,0 +1,52 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking +# + +SET(PHYSXCOOKING_COMPILE_DEFS + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_CORE_EXPORTS;PX_COOKING; + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> + +) + +SET(PHYSXCOOKING_PLATFORM_SRC_FILES +) + +SET(PHYSXCOOKING_LIBTYPE STATIC) + +SET(PHYSXCOOKING_LINK_FLAGS " ") +SET(PHYSXCOOKING_LINK_FLAGS_DEBUG " ") +SET(PHYSXCOOKING_LINK_FLAGS_CHECKED " ") +SET(PHYSXCOOKING_LINK_FLAGS_PROFILE " ") +SET(PHYSXCOOKING_LINK_FLAGS_RELEASE " ") \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake new file mode 100644 index 000000000..773941eea --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake @@ -0,0 +1,55 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions +# + +SET(PHYSXEXTENSIONS_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/ios +) + +SET(PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES + $ +) + +SET(PHYSXEXTENSIONS_PLATFORM_SRC_FILES + ${PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES} +) + + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXEXTENSIONS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake new file mode 100644 index 000000000..d047086fa --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake @@ -0,0 +1,91 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation +# + +SET(PHYSXFOUNDATION_LIBTYPE STATIC) + +SET(PXSHARED_PLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/unix/PxUnixIntrinsics.h +) +SOURCE_GROUP(shared\\include\\unix FILES ${PXSHARED_PLATFORM_HEADERS}) + +SET(PHYSXFOUNDATION_PLATFORM_INCLUDES + ${LL_SOURCE_DIR}/include/ios +) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE + ${LL_SOURCE_DIR}/src/unix/PsUnixAtomic.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixCpu.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixFPU.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixMutex.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixPrintString.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSList.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSocket.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSync.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixThread.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixTime.cpp +) + +SET(PHYSXFOUNDATION_NEON_FILES + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonAoS.h + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonInlineAoS.h +) + + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/unix/PsUnixAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixFPU.h + ${LL_SOURCE_DIR}/include/unix/PsUnixInlineAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixIntrinsics.h + ${LL_SOURCE_DIR}/include/unix/PsUnixTrigConstants.h +) +SOURCE_GROUP("src\\include\\unix" FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS}) + +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} DESTINATION source/foundation/include/unix) +INSTALL(FILES ${PHYSXFOUNDATION_NEON_FILES} DESTINATION source/foundation/include/unix/neon) +INSTALL(FILES ${PXSHARED_PLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/unix) + +SET(PHYSXFOUNDATION_PLATFORM_FILES + ${PHYSXFOUNDATION_PLATFORM_SOURCE} + ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} + ${PHYSXFOUNDATION_NEON_FILES} + ${PHYSXFOUNDATION_RESOURCE_FILE} +) + +SET(PHYSXFOUNDATION_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS} + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS}> +) diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake new file mode 100644 index 000000000..ba98d234e --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake @@ -0,0 +1,45 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK +# + +SET(PHYSXPVDSDK_LIBTYPE STATIC) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXPVDSDK_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS} + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS}> +) + diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake new file mode 100644 index 000000000..687e798ac --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake @@ -0,0 +1,41 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask +# + + +SET(PHYSXTASK_COMPILE_DEFS + ${PHYSX_IOS_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXTASK_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake new file mode 100644 index 000000000..e5e5a477e --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake @@ -0,0 +1,42 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle +# + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXVEHICLE_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) diff --git a/src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake new file mode 100644 index 000000000..f00560aa4 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake @@ -0,0 +1,48 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery +# + +SET(SCENEQUERY_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/ios +) + +# Use generator expressions to set config specific preprocessor definitions +SET(SCENEQUERY_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(SCENEQUERY_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake new file mode 100644 index 000000000..bf3309e65 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake @@ -0,0 +1,49 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController +# + +SET(SIMULATIONCONTROLLER_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/ios + ${PHYSX_SOURCE_DIR}/LowLevel/mac/include +) + +# Use generator expressions to set config specific preprocessor definitions +SET(SIMULATIONCONTROLLER_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_IOS_RELEASE_COMPILE_DEFS};> +) + +SET(SIMULATIONCONTROLLER_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt new file mode 100644 index 000000000..65ba87305 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt @@ -0,0 +1,123 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +STRING(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWERCASE) + +#TODO: Fix warnings +SET(CLANG_WARNINGS "-ferror-limit=0 -Wall -Wextra -Werror -Wstrict-aliasing=2 -Weverything -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-gnu-anonymous-struct -Wno-undef -Wno-unused-function -Wno-nested-anon-types -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-format-nonliteral -Wno-implicit-fallthrough -Wno-undefined-reinterpret-cast -Wno-disabled-macro-expansion -Wno-zero-as-null-pointer-constant -Wno-shadow -Wno-unknown-warning-option") +SET(GCC_WARNINGS "-Wall -Werror -Wno-invalid-offsetof -Wno-uninitialized") + +IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # using Clang + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -fstrict-aliasing ${CLANG_WARNINGS}" CACHE INTERAL "PhysX CXX") +ELSEIF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -fno-strict-aliasing ${GCC_WARNINGS}" CACHE INTERAL "PhysX CXX") +ENDIF() + +# Build debug info for all configurations +SET(PHYSX_CXX_FLAGS_DEBUG "-O0 -g3 -gdwarf-2" CACHE INTERNAL "PhysX Debug CXX Flags") +SET(PHYSX_CXX_FLAGS_CHECKED "-O3 -g3 -gdwarf-2" CACHE INTERNAL "PhysX Checked CXX Flags") +SET(PHYSX_CXX_FLAGS_PROFILE "-O3" CACHE INTERNAL "PhysX Profile CXX Flags") +SET(PHYSX_CXX_FLAGS_RELEASE "-O3" CACHE INTERNAL "PhysX Release CXX Flags") + +# These flags are local to the directory the CMakeLists.txt is in, so don't get carried over to OTHER CMakeLists.txt (thus the CACHE variables above) +SET(CMAKE_CXX_FLAGS ${PHYSX_CXX_FLAGS}) + +SET(CMAKE_CXX_FLAGS_DEBUG ${PHYSX_CXX_FLAGS_DEBUG}) +SET(CMAKE_CXX_FLAGS_CHECKED ${PHYSX_CXX_FLAGS_CHECKED}) +SET(CMAKE_CXX_FLAGS_PROFILE ${PHYSX_CXX_FLAGS_PROFILE}) +SET(CMAKE_CXX_FLAGS_RELEASE ${PHYSX_CXX_FLAGS_RELEASE}) + + +# Controls PX_NVTX for all projects +SET(NVTX_FLAG "PX_NVTX=0") + +IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + SET(CUDA_FLAG "DISABLE_CUDA_PHYSX") +ENDIF() + +SET(PHYSX_LINUX_COMPILE_DEFS "${CUDA_FLAG};${PHYSX_AUTOBUILD};${PHYSX_AUTOBUILDNUMBER}" CACHE INTERNAL "Base PhysX preprocessor definitions") + +SET(PHYSX_LINUX_DEBUG_COMPILE_DEFS "NDEBUG;PX_DEBUG=1;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Debug PhysX preprocessor definitions") +SET(PHYSX_LINUX_CHECKED_COMPILE_DEFS "NDEBUG;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Checked PhysX preprocessor definitions") +SET(PHYSX_LINUX_PROFILE_COMPILE_DEFS "NDEBUG;PX_PROFILE=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Profile PhysX preprocessor definitions") +SET(PHYSX_LINUX_RELEASE_COMPILE_DEFS "NDEBUG;PX_SUPPORT_PVD=0" CACHE INTERNAL "Release PhysX preprocessor definitions") + +SET(GENERATED_GPU_CUDA_FILES "") + +# Include all of the projects +IF(PX_GENERATE_GPU_PROJECTS_ONLY) + INCLUDE(PhysXCommon.cmake) + INCLUDE(PhysXBroadphaseGpu.cmake) + INCLUDE(PhysXCommonGpu.cmake) + INCLUDE(PhysXNarrowphaseGpu.cmake) + INCLUDE(PhysXSimulationControllerGpu.cmake) + INCLUDE(PhysXSolverGpu.cmake) + INCLUDE(CloudGpu.cmake) + INCLUDE(PhysXCudaContextManager.cmake) + INCLUDE(PhysXArticulationGpu.cmake) + INCLUDE(PhysXGpu.cmake) # must be last +ELSE() + INCLUDE(PhysXFoundation.cmake) + INCLUDE(LowLevel.cmake) + INCLUDE(LowLevelAABB.cmake) + INCLUDE(LowLevelDynamics.cmake) + INCLUDE(PhysX.cmake) + INCLUDE(PhysXCharacterKinematic.cmake) + INCLUDE(PhysXCommon.cmake) + INCLUDE(PhysXCooking.cmake) + INCLUDE(PhysXExtensions.cmake) + INCLUDE(PhysXVehicle.cmake) + INCLUDE(SceneQuery.cmake) + INCLUDE(SimulationController.cmake) + INCLUDE(FastXml.cmake) + INCLUDE(PhysXPvdSDK.cmake) + INCLUDE(PhysXTask.cmake) + + SET(PHYSXDISTRO_LIBS PhysXFoundation PhysX PhysXCharacterKinematic PhysXPvdSDK PhysXCommon PhysXCooking PhysXExtensions PhysXVehicle) + + IF(PX_GENERATE_GPU_PROJECTS) + INCLUDE(PhysXBroadphaseGpu.cmake) + INCLUDE(PhysXCommonGpu.cmake) + INCLUDE(PhysXNarrowphaseGpu.cmake) + INCLUDE(PhysXSimulationControllerGpu.cmake) + INCLUDE(PhysXSolverGpu.cmake) + INCLUDE(CloudGpu.cmake) + INCLUDE(PhysXCudaContextManager.cmake) + INCLUDE(PhysXArticulationGpu.cmake) + INCLUDE(PhysXGpu.cmake) # must be last + + LIST(APPEND PHYSXDISTRO_LIBS PhysXGpu) + ENDIF() + + INSTALL( + TARGETS ${PHYSXDISTRO_LIBS} + EXPORT PhysXSDK + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> + ) + +ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake new file mode 100644 index 000000000..e2377741e --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake @@ -0,0 +1,43 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml +# + +# Use generator expressions to set config specific preprocessor definitions +SET(FASTXML_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS}; + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(FASTXML_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake new file mode 100644 index 000000000..20b7eddf9 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake @@ -0,0 +1,67 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel +# + + +SET(LOWLEVEL_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/linux + ${PHYSX_SOURCE_DIR}/LowLevel/software/include/linux + ${PHYSX_SOURCE_DIR}/LowLevelDynamics/include/linux + ${PHYSX_SOURCE_DIR}/LowLevel/common/include/pipeline/linux +) + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB +) + +IF(PX_GENERATE_GPU_STATIC_LIBRARIES) + SET(LOWLEVEL_GPU_LIBTYPE_DEFS + PX_PHYSX_GPU_STATIC; + ) +ENDIF() + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB;${LOWLEVEL_GPU_LIBTYPE_DEFS} + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVEL_PLATFORM_LINK_FLAGS " ") + +SET(LOWLEVEL_LIBTYPE OBJECT) + +# NOTE: Is this a UE4 specific change? +# enable -fPIC so we can link static libs with the editor +#SET_TARGET_PROPERTIES(LowLevel PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake new file mode 100644 index 000000000..5ff3a9592 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake @@ -0,0 +1,53 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB +# + +SET(LOWLEVELAABB_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/linux + ${PHYSX_SOURCE_DIR}/LowLevelAABB/linux/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/src +) + + +SET(LOWLEVELAABB_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELAABB_LIBTYPE OBJECT) + +SET(LOWLEVELAABB_PLATFORM_LINK_FLAGS " ") diff --git a/src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake new file mode 100644 index 000000000..e7cdc1d63 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake @@ -0,0 +1,53 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics +# + + +SET(LOWLEVELDYNAMICS_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/linux + ${PHYSX_SOURCE_DIR}/lowlevel/software/include/linux + ${PHYSX_SOURCE_DIR}/lowleveldynamics/include/linux + ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline/linux +) + +# Use generator expressions to set config specific preprocessor definitions +SET(LOWLEVELDYNAMICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELDYNAMICS_LIBTYPE OBJECT) + diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake new file mode 100644 index 000000000..5ab23e0d2 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake @@ -0,0 +1,111 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) +# + +SET(PHYSX_GPU_HEADERS + ${PHYSX_ROOT_DIR}/include/gpu/PxGpu.h +) +SOURCE_GROUP(include\\gpu FILES ${PHYSX_GPU_HEADERS}) + + +SET(PHYSX_PLATFORM_INCLUDES + ${NVTOOLSEXT_INCLUDE_DIRS} +) + +SET(PHYSX_PLATFORM_OBJECT_FILES + $ + $ + $ + $ + $ + $ +) + +SET(PHYSX_PLATFORM_SRC_FILES + ${PX_SOURCE_DIR}/device/linux/PhysXIndicatorLinux.cpp + ${PX_SOURCE_DIR}/gpu/PxGpu.cpp + ${PX_SOURCE_DIR}/gpu/PxPhysXGpuModuleLoader.cpp + + ${PHYSX_PLATFORM_OBJECT_FILES} +) + +INSTALL(FILES ${PHYSX_GPU_HEADERS} DESTINATION include/gpu) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_LIBTYPE STATIC) + SET(PXPHYSX_LIBTYPE_DEFS + PX_PHYSX_STATIC_LIB; + ) +ELSE() + SET(PHYSX_LIBTYPE SHARED) + SET(PXPHYSX_LIBTYPE_DEFS + PX_PHYSX_CORE_EXPORTS; + ) +ENDIF() + +IF(PX_GENERATE_GPU_STATIC_LIBRARIES) + SET(PXPHYSX_GPU_DEFS PX_PHYSX_GPU_STATIC) +ENDIF() + +# Set default PhysXGpu shared library name +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(BITNESS_STRING "64") + ELSE() + SET(BITNESS_STRING "32") + ENDIF() + SET(PHYSX_GPU_SHARED_LIB_NAME_DEF PX_PHYSX_GPU_SHARED_LIB_NAME=libPhysXGpu_${BITNESS_STRING}.so) +ELSE() + SET(CONFIG_STRING $<$:DEBUG>$<$:CHECKED>$<$:PROFILE>) + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(BITNESS_STRING "x64") + ELSE() + SET(BITNESS_STRING "x86") + ENDIF() + SET(PHYSX_GPU_SHARED_LIB_NAME_DEF PX_PHYSX_GPU_SHARED_LIB_NAME=libPhysXGpu${CONFIG_STRING}_${BITNESS_STRING}.so) +ENDIF() + +SET(PHYSX_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};${PXPHYSX_LIBTYPE_DEFS};${PHYSX_GPU_SHARED_LIB_NAME_DEF};${PXPHYSX_GPU_DEFS} + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSX_PLATFORM_LINK_FLAGS " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_DEBUG " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_CHECKED " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_PROFILE " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_RELEASE " ") + +SET(PHYSX_PLATFORM_LINKED_LIBS dl) diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..daa63e5cd --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake @@ -0,0 +1,49 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic +# + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_CHARACTER_STATIC_LIB;PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXCHARACTERKINEMATIC_LIBTYPE STATIC) + + +# enable -fPIC so we can link static libs with the editor +#SET_TARGET_PROPERTIES(PhysXCharacterKinematic PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake new file mode 100644 index 000000000..6471c8b81 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake @@ -0,0 +1,65 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon +# + +SET(PXCOMMON_PLATFORM_SRC_FILES +) + +SET(PXCOMMON_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/linux +) + + + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PXCOMMON_LIBTYPE_DEFS + PX_PHYSX_STATIC_LIB; + ) + SET(PHYSXCOMMON_LIBTYPE STATIC) +ELSE() + SET(PXCOMMON_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS; + ) + SET(PHYSXCOMMON_LIBTYPE SHARED) +ENDIF() + + +SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};${PXCOMMON_LIBTYPE_DEFS} + + # Switch platforms here? + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS}> +) + +SET(PXCOMMON_PLATFORM_LINK_FLAGS " ") diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake new file mode 100644 index 000000000..5f11a3f5d --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake @@ -0,0 +1,64 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking +# + + +SET(PHYSXCOOKING_PLATFORM_SRC_FILES +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXCOOKING_LIBTYPE STATIC) + SET(PXCOOKING_LIBTYPE_DEFS + PX_PHYSX_STATIC_LIB; + ) +ELSE() + SET(PHYSXCOOKING_LIBTYPE SHARED) + SET(PXCOOKING_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_CORE_EXPORTS + ) +ENDIF() + +SET(PHYSXCOOKING_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_COOKING;${PXCOOKING_LIBTYPE_DEFS} + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> + +) + + +SET(PHYSXCOOKING_LINK_FLAGS " ") +SET(PHYSXCOOKING_LINK_FLAGS_DEBUG " ") +SET(PHYSXCOOKING_LINK_FLAGS_CHECKED " ") +SET(PHYSXCOOKING_LINK_FLAGS_PROFILE " ") +SET(PHYSXCOOKING_LINK_FLAGS_RELEASE " ") diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake new file mode 100644 index 000000000..8e5831592 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake @@ -0,0 +1,56 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions +# + +SET(PHYSXEXTENSIONS_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/linux +) + +SET(PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES + $ +) + +SET(PHYSXEXTENSIONS_PLATFORM_SRC_FILES + ${PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES} +) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXEXTENSIONS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXEXTENSIONS_LIBTYPE STATIC) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake new file mode 100644 index 000000000..b4520e7fd --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake @@ -0,0 +1,110 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation +# + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXFOUNDATION_LIBTYPE STATIC) +ELSE() + SET(PHYSXFOUNDATION_LIBTYPE SHARED) + SET(PHYSXFOUNDATION_PLATFORM_LINKED_LIBS rt) + SET(PXFOUNDATION_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1; + ) +ENDIF() + +SET(PXSHARED_PLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/unix/PxUnixIntrinsics.h +) +SOURCE_GROUP(shared\\include\\unix FILES ${PXSHARED_PLATFORM_HEADERS}) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE + ${LL_SOURCE_DIR}/src/unix/PsUnixAtomic.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixCpu.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixFPU.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixMutex.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixPrintString.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSList.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSocket.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSync.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixThread.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixTime.cpp +) +SOURCE_GROUP("src\\src\\unix" FILES ${PHYSXFOUNDATION_PLATFORM_FILES}) + +SET(PHYSXFOUNDATION_PLATFORM_INCLUDES + ${LL_SOURCE_DIR}/include/linux +) + +SET(PHYSXFOUNDATION_NEON_FILES + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonAoS.h + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonInlineAoS.h +) + +SET(PHYSXFOUNDATION_SSE2_FILES + ${LL_SOURCE_DIR}/include/unix/sse2/PsUnixSse2AoS.h + ${LL_SOURCE_DIR}/include/unix/sse2/PsUnixSse2InlineAoS.h +) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/unix/PsUnixAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixFPU.h + ${LL_SOURCE_DIR}/include/unix/PsUnixInlineAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixIntrinsics.h + ${LL_SOURCE_DIR}/include/unix/PsUnixTrigConstants.h +) +SOURCE_GROUP("src\\include\\unix" FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS}) + +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} DESTINATION source/foundation/include/unix) +INSTALL(FILES ${PHYSXFOUNDATION_NEON_FILES} DESTINATION source/foundation/include/unix/neon) +INSTALL(FILES ${PHYSXFOUNDATION_SSE2_FILES} DESTINATION source/foundation/include/unix/sse2) +INSTALL(FILES ${PXSHARED_PLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/unix) + +SET(PHYSXFOUNDATION_PLATFORM_FILES + ${PHYSXFOUNDATION_PLATFORM_SOURCE} + ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} + ${PHYSXFOUNDATION_NEON_FILES} + ${PHYSXFOUNDATION_SSE2_FILES} + ${PHYSXFOUNDATION_RESOURCE_FILE} +) + + + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXFOUNDATION_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};${PXFOUNDATION_LIBTYPE_DEFS} + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(PXFOUNDATION_PLATFORM_LINK_FLAGS "-m64") diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake new file mode 100644 index 000000000..164ca1f83 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake @@ -0,0 +1,44 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK +# + +SET(PHYSXPVDSDK_LIBTYPE STATIC) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXPVDSDK_COMPILE_DEFS + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake new file mode 100644 index 000000000..a896acfb4 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake @@ -0,0 +1,45 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask +# + +SET(PHYSXTASK_COMPILE_DEFS + ${PHYSX_LINUX_COMPILE_DEFS}; +) + +SET(PHYSXTASK_COMPILE_DEFS + ${PHYSX_LINUX_COMPILE_DEFS};_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXTASK_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake new file mode 100644 index 000000000..048d2ea02 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake @@ -0,0 +1,47 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle +# + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXVEHICLE_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + + +SET(PHYSXVEHICLE_LIBTYPE STATIC) + + diff --git a/src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake new file mode 100644 index 000000000..6a9540b6a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake @@ -0,0 +1,50 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery +# + +SET(SCENEQUERY_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/linux +) + + +# Use generator expressions to set config specific preprocessor definitions +SET(SCENEQUERY_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(SCENEQUERY_LIBTYPE OBJECT) + diff --git a/src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake new file mode 100644 index 000000000..a15ea443a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake @@ -0,0 +1,51 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController +# + +SET(SIMULATIONCONTROLLER_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/linux + ${PHYSX_SOURCE_DIR}/lowlevel/linux/include +) + +# Use generator expressions to set config specific preprocessor definitions +SET(SIMULATIONCONTROLLER_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_LINUX_RELEASE_COMPILE_DEFS};> +) + +SET(SIMULATIONCONTROLLER_LIBTYPE OBJECT) + + diff --git a/src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt new file mode 100644 index 000000000..9e9618c62 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt @@ -0,0 +1,92 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +OPTION(PX_32BIT "Specifies 32bit compilation flags." OFF) +OPTION(PX_64BIT "Specifies 64bit compilation flags." OFF) + +IF (PX_32BIT) + SET(OSX_BITNESS "-arch i386") +ELSE() + SET(OSX_BITNESS "-arch x86_64") + SET(CMAKE_OSX_ARCHITECTURES "x86_64") +ENDIF() + + +SET(PHYSX_CXX_FLAGS "${OSX_BITNESS} -msse2 -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -Werror -ferror-limit=0 -Wall -Wextra -fstrict-aliasing -Wstrict-aliasing=2 -Weverything -Wno-unknown-warning-option -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-undefined-reinterpret-cast -Wno-invalid-offsetof -Wno-zero-as-null-pointer-constant -gdwarf-2" CACHE INTERAL "PhysX CXX") + +SET(CMAKE_SHARED_LINKER_FLAGS_CHECKED "") +SET(CMAKE_SHARED_LINKER_FLAGS_PROFILE "") +SET(CMAKE_SHARED_LINKER_FLAGS "") + +# Build debug info for all configurations +SET(PHYSX_CXX_FLAGS_DEBUG "-O0 -g" CACHE INTERAL "PhysX Debug CXX Flags") +SET(PHYSX_CXX_FLAGS_CHECKED "-O3 -g" CACHE INTERAL "PhysX Checked CXX Flags") +SET(PHYSX_CXX_FLAGS_PROFILE "-O3 -g" CACHE INTERAL "PhysX Profile CXX Flags") +SET(PHYSX_CXX_FLAGS_RELEASE "-O3 -g" CACHE INTERAL "PhysX Release CXX Flags") + +# These flags are local to the directory the CMakeLists.txt is in +SET(CMAKE_CXX_FLAGS ${PHYSX_CXX_FLAGS}) + +SET(CMAKE_CXX_FLAGS_DEBUG ${PHYSX_CXX_FLAGS_DEBUG}) +SET(CMAKE_CXX_FLAGS_CHECKED ${PHYSX_CXX_FLAGS_CHECKED}) +SET(CMAKE_CXX_FLAGS_PROFILE ${PHYSX_CXX_FLAGS_PROFILE}) +SET(CMAKE_CXX_FLAGS_RELEASE ${PHYSX_CXX_FLAGS_RELEASE}) + +# Build libs compatible with OS X 10.9 +SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.9") + +#set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym") + +# Controls PX_NVTX for all projects on Mac +SET(NVTX_FLAG "PX_NVTX=0") + +# Disable cuda for all projects +SET(PHYSX_MAC_COMPILE_DEFS "DISABLE_CUDA_PHYSX;${PHYSX_AUTOBUILD};${PHYSX_AUTOBUILDNUMBER}" CACHE INTERNAL "Base PhysX preprocessor definitions") +SET(PHYSX_MAC_DEBUG_COMPILE_DEFS "_DEBUG;PX_DEBUG=1;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Debug PhysX preprocessor definitions") +SET(PHYSX_MAC_CHECKED_COMPILE_DEFS "NDEBUG;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Checked PhysX preprocessor definitions") +SET(PHYSX_MAC_PROFILE_COMPILE_DEFS "NDEBUG;PX_PROFILE=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Profile PhysX preprocessor definitions") +SET(PHYSX_MAC_RELEASE_COMPILE_DEFS "NDEBUG;PX_SUPPORT_PVD=0" CACHE INTERNAL "Release PhysX preprocessor definitions") + + +# Include all of the projects +INCLUDE(PhysXFoundation.cmake) +INCLUDE(LowLevel.cmake) +INCLUDE(LowLevelAABB.cmake) +INCLUDE(LowLevelDynamics.cmake) +INCLUDE(PhysX.cmake) +INCLUDE(PhysXCharacterKinematic.cmake) +INCLUDE(PhysXCommon.cmake) +INCLUDE(PhysXCooking.cmake) +INCLUDE(PhysXExtensions.cmake) +INCLUDE(PhysXVehicle.cmake) +INCLUDE(PhysXPvdSDK.cmake) +INCLUDE(PhysXTask.cmake) +INCLUDE(SceneQuery.cmake) +INCLUDE(SimulationController.cmake) +INCLUDE(FastXml.cmake) + + diff --git a/src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake new file mode 100644 index 000000000..ac4c4d53e --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake @@ -0,0 +1,45 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml +# + + +# Use generator expressions to set config specific preprocessor definitions +SET(FASTXML_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(FASTXML_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake new file mode 100644 index 000000000..b314a2f0c --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake @@ -0,0 +1,51 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel +# +SET(LOWLEVEL_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/mac + ${PHYSX_SOURCE_DIR}/LowLevel/software/include/mac + ${PHYSX_SOURCE_DIR}/LowLevelDynamics/include/mac + ${PHYSX_SOURCE_DIR}/LowLevel/common/include/pipeline/mac +) + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVEL_PLATFORM_LINK_FLAGS " ") + +SET(LOWLEVEL_LIBTYPE OBJECT) + diff --git a/src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake new file mode 100644 index 000000000..fbec014b1 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake @@ -0,0 +1,53 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB +# + +SET(LOWLEVELAABB_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/mac + ${PHYSX_SOURCE_DIR}/LowLevelAABB/mac/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/src +) + + +SET(LOWLEVELAABB_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELAABB_PLATFORM_LINK_FLAGS " ") + +SET(LOWLEVELAABB_LIBTYPE OBJECT) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake new file mode 100644 index 000000000..9069816b2 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake @@ -0,0 +1,50 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics +# +SET(LOWLEVELDYNAMICS_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/mac + ${PHYSX_SOURCE_DIR}/lowlevel/software/include/mac + ${PHYSX_SOURCE_DIR}/lowleveldynamics/include/mac + ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline/mac +) + +# Use generator expressions to set config specific preprocessor definitions +SET(LOWLEVELDYNAMICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(LOWLEVELDYNAMICS_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake new file mode 100644 index 000000000..731c18224 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake @@ -0,0 +1,72 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) +# + +SET(PHYSX_PLATFORM_INCLUDES +) + +SET(PHYSX_PLATFORM_OBJECT_FILES + $ + $ + $ + $ + $ + $ +) + +SET(PHYSX_PLATFORM_SRC_FILES + ${PX_SOURCE_DIR}/gpu/PxGpu.cpp + ${PX_SOURCE_DIR}/gpu/PxPhysXGpuModuleLoader.cpp + + ${PHYSX_PLATFORM_OBJECT_FILES} +) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSX_COMPILE_DEFS + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_CORE_EXPORTS + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_LIBTYPE STATIC) +ELSE() + SET(PHYSX_LIBTYPE SHARED) +ENDIF() + +SET(PHYSX_PLATFORM_LINK_FLAGS " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_DEBUG " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_CHECKED " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_PROFILE " ") +SET(PHYSX_PLATFORM_LINK_FLAGS_RELEASE " ") diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..055eb7911 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake @@ -0,0 +1,41 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic +# +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_CHARACTER_EXPORTS;PX_PHYSX_CORE_EXPORTS; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake new file mode 100644 index 000000000..739565130 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake @@ -0,0 +1,70 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon +# + + +SET(PXCOMMON_PLATFORM_SRC_FILES +) + +SET(PXCOMMON_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/mac +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS}> + ) +ELSE() + SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_COMMON_EXPORTS; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS}> + ) +ENDIF() + + + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXCOMMON_LIBTYPE STATIC) +ELSE() + SET(PHYSXCOMMON_LIBTYPE SHARED) +ENDIF() + +SET(PXCOMMON_PLATFORM_LINK_FLAGS " ") diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake new file mode 100644 index 000000000..9e882ef83 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake @@ -0,0 +1,57 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking +# + +SET(PHYSXCOOKING_COMPILE_DEFS + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_CORE_EXPORTS;PX_COOKING; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> + +) + +SET(PHYSXCOOKING_PLATFORM_SRC_FILES +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXCOOKING_LIBTYPE STATIC) +ELSE() + SET(PHYSXCOOKING_LIBTYPE SHARED) +ENDIF() + + +SET(PHYSXCOOKING_LINK_FLAGS " ") +SET(PHYSXCOOKING_LINK_FLAGS_DEBUG " ") +SET(PHYSXCOOKING_LINK_FLAGS_CHECKED " ") +SET(PHYSXCOOKING_LINK_FLAGS_PROFILE " ") +SET(PHYSXCOOKING_LINK_FLAGS_RELEASE " ") diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake new file mode 100644 index 000000000..b4c15ac27 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake @@ -0,0 +1,55 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions +# + +SET(PHYSXEXTENSIONS_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/mac +) + +SET(PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES + $ +) + +SET(PHYSXEXTENSIONS_PLATFORM_SRC_FILES + ${PHYSXEXTENSIONS_PLATFORM_OBJECT_FILES} +) + + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXEXTENSIONS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB; + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake new file mode 100644 index 000000000..256293a02 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake @@ -0,0 +1,102 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation +# + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXFOUNDATION_LIBTYPE STATIC) +ELSE() + SET(PHYSXFOUNDATION_LIBTYPE SHARED) + SET(PHYSXFOUNDATION_SHARED_LIBRARY_DEFS PX_FOUNDATION_DLL=1;) +ENDIF() + +SET(PXSHARED_PLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/unix/PxUnixIntrinsics.h +) +SOURCE_GROUP(shared\\include\\unix FILES ${PXSHARED_PLATFORM_HEADERS}) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE + ${LL_SOURCE_DIR}/src/unix/PsUnixAtomic.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixCpu.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixFPU.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixMutex.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixPrintString.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSList.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSocket.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixSync.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixThread.cpp + ${LL_SOURCE_DIR}/src/unix/PsUnixTime.cpp +) + +SET(PHYSXFOUNDATION_PLATFORM_INCLUDES + ${LL_SOURCE_DIR}/include/linux +) + +SET(PHYSXFOUNDATION_NEON_FILES + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonAoS.h + ${LL_SOURCE_DIR}/include/unix/neon/PsUnixNeonInlineAoS.h +) + +SET(PHYSXFOUNDATION_SSE2_FILES + ${LL_SOURCE_DIR}/include/unix/sse2/PsUnixSse2AoS.h + ${LL_SOURCE_DIR}/include/unix/sse2/PsUnixSse2InlineAoS.h +) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/unix/PsUnixAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixFPU.h + ${LL_SOURCE_DIR}/include/unix/PsUnixInlineAoS.h + ${LL_SOURCE_DIR}/include/unix/PsUnixIntrinsics.h + ${LL_SOURCE_DIR}/include/unix/PsUnixTrigConstants.h +) +SOURCE_GROUP("src\\include\\unix" FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS}) + +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} DESTINATION source/foundation/include/unix) +INSTALL(FILES ${PHYSXFOUNDATION_NEON_FILES} DESTINATION source/foundation/include/unix/neon) +INSTALL(FILES ${PHYSXFOUNDATION_SSE2_FILES} DESTINATION source/foundation/include/unix/sse2) +INSTALL(FILES ${PXSHARED_PLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/unix) + +SET(PHYSXFOUNDATION_PLATFORM_FILES + ${PHYSXFOUNDATION_PLATFORM_SOURCE} + ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} + ${PHYSXFOUNDATION_NEON_FILES} + ${PHYSXFOUNDATION_SSE2_FILES} + ${PHYSXFOUNDATION_RESOURCE_FILE} +) + +SET(PHYSXFOUNDATION_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS} + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS}> +) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake new file mode 100644 index 000000000..1f0506c85 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake @@ -0,0 +1,45 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK +# + +SET(PHYSXPVDSDK_LIBTYPE STATIC) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXPVDSDK_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS} + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS}> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS}> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS}> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS}> +) + diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake new file mode 100644 index 000000000..ec71f0c54 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake @@ -0,0 +1,41 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask +# + + +SET(PHYSXTASK_COMPILE_DEFS + ${PHYSX_MAC_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXTASK_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake new file mode 100644 index 000000000..c81d171e3 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake @@ -0,0 +1,45 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle +# + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXVEHICLE_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + + +SET(PHYSXVEHICLE_LIBTYPE STATIC) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake new file mode 100644 index 000000000..6222fe3a0 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake @@ -0,0 +1,48 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery +# + +SET(SCENEQUERY_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/mac +) + +# Use generator expressions to set config specific preprocessor definitions +SET(SCENEQUERY_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(SCENEQUERY_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake new file mode 100644 index 000000000..6c811d569 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake @@ -0,0 +1,49 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController +# + +SET(SIMULATIONCONTROLLER_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/mac + ${PHYSX_SOURCE_DIR}/LowLevel/mac/include +) + +# Use generator expressions to set config specific preprocessor definitions +SET(SIMULATIONCONTROLLER_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_MAC_COMPILE_DEFS};PX_PHYSX_STATIC_LIB + + $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_MAC_RELEASE_COMPILE_DEFS};> +) + +SET(SIMULATIONCONTROLLER_LIBTYPE OBJECT) \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt new file mode 100644 index 000000000..db4d0457d --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt @@ -0,0 +1,216 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +OPTION(PX_COPY_EXTERNAL_DLL "Copy external dlls into SDK bin directory" OFF) +OPTION(PX_FLOAT_POINT_PRECISE_MATH "Float point precise math" OFF) +OPTION(PX_USE_NVTX "Enabled NVTX profiling" OFF) + +# We define the CXX flags for this CMakeLists and all others that are included afterwards. This is a GLOBAL setting. +# If/when the solutions go standalone (say, samples and visual tests) - those CMakeLists will need to be fixed. + +SET(PHYSX_WARNING_DISABLES "/wd4514 /wd4820 /wd4127 /wd4710 /wd4711 /wd4577 /wd4996") + +# Cache the CXX flags so the other CMakeLists.txt can use them if needed +IF(PX_FLOAT_POINT_PRECISE_MATH) + SET(PHYSX_FP_MODE "/fp:precise") +ELSE() + SET(PHYSX_FP_MODE "/fp:fast") +ENDIF() +IF(CMAKE_CL_64) + SET(PHYSX_CXX_FLAGS "/Wall /d2Zi+ /MP /WX /W4 /GF /GS- /GR- /Gd ${PHYSX_FP_MODE} /Oy ${PHYSX_WARNING_DISABLES}" CACHE INTERNAL "PhysX CXX") +ELSE() + SET(PHYSX_CXX_FLAGS "/Wall /arch:SSE2 /d2Zi+ /MP /WX /W4 /GF /GS- /GR- /Gd ${PHYSX_FP_MODE} /Oy ${PHYSX_WARNING_DISABLES}" CACHE INTERNAL "PhysX CXX") +ENDIF() + +SET(PHYSX_CXX_FLAGS_DEBUG "/Od ${WINCRT_DEBUG} /RTCu /Zi" CACHE INTERNAL "PhysX Debug CXX Flags") +SET(PHYSX_CXX_FLAGS_CHECKED "/Ox ${WINCRT_NDEBUG} /Zi" CACHE INTERNAL "PhysX Checked CXX Flags") +SET(PHYSX_CXX_FLAGS_PROFILE "/Ox ${WINCRT_NDEBUG} /Zi" CACHE INTERNAL "PhysX Profile CXX Flags") +SET(PHYSX_CXX_FLAGS_RELEASE "/Ox ${WINCRT_NDEBUG} /Zi" CACHE INTERNAL "PhysX Release CXX Flags") + +# cache lib type defs +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_LIBTYPE_DEFS "PX_PHYSX_STATIC_LIB;PX_FOUNDATION_DLL=0;" CACHE INTERNAL "PhysX lib type defs") +ENDIF() +IF(PX_GENERATE_GPU_STATIC_LIBRARIES) + SET(PHYSXGPU_LIBTYPE_DEFS "PX_PHYSX_GPU_STATIC;" CACHE INTERNAL "PhysX GPU lib type defs") +ENDIF() + +SET(CUDA_SUPPRESS_WARNINGS "-Xptxas -w -Wno-deprecated-declarations -ftemplate-backtrace-limit=2") + +# These flags are local to the directory the CMakeLists.txt is in, so don't get carried over to OTHER CMakeLists.txt (thus the CACHE variables above) +SET(CMAKE_CXX_FLAGS ${PHYSX_CXX_FLAGS}) + +SET(CMAKE_CXX_FLAGS_DEBUG ${PHYSX_CXX_FLAGS_DEBUG}) +SET(CMAKE_CXX_FLAGS_CHECKED ${PHYSX_CXX_FLAGS_CHECKED}) +SET(CMAKE_CXX_FLAGS_PROFILE ${PHYSX_CXX_FLAGS_PROFILE}) +SET(CMAKE_CXX_FLAGS_RELEASE ${PHYSX_CXX_FLAGS_RELEASE}) + +# Build PDBs for all configurations +SET(CMAKE_SHARED_LINKER_FLAGS "/DEBUG /INCREMENTAL:NO") +SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG "/DEBUG /INCREMENTAL:NO") +SET(CMAKE_SHARED_LINKER_FLAGS_CHECKED "/DEBUG /INCREMENTAL:NO") +SET(CMAKE_SHARED_LINKER_FLAGS_PROFILE "/DEBUG /INCREMENTAL:NO") +SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "/DEBUG /INCREMENTAL:NO") + +IF(CMAKE_CL_64) + SET(WIN64_FLAG "WIN64") +ENDIF(CMAKE_CL_64) + +IF(PX_SCALAR_MATH) + SET(SCALAR_MATH_FLAG "PX_SIMD_DISABLED") +ENDIF() + +# Controls PX_NVTX for all projects +IF(PX_USE_NVTX) + SET(NVTX_FLAG "PX_NVTX=1") +ELSE() + SET(NVTX_FLAG "PX_NVTX=0") +ENDIF() + +SET(PHYSX_WINDOWS_COMPILE_DEFS "WIN32;${WIN64_FLAG};${SCALAR_MATH_FLAG};${CUDA_FLAG};_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;${PHYSX_AUTOBUILD};${PHYSX_AUTOBUILDNUMBER}" CACHE INTERNAL "Base PhysX preprocessor definitions") + +SET(PHYSX_WINDOWS_DEBUG_COMPILE_DEFS "PX_DEBUG=1;PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Debug PhysX preprocessor definitions") +SET(PHYSX_WINDOWS_CHECKED_COMPILE_DEFS "PX_CHECKED=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Checked PhysX preprocessor definitions") +SET(PHYSX_WINDOWS_PROFILE_COMPILE_DEFS "PX_PROFILE=1;${NVTX_FLAG};PX_SUPPORT_PVD=1" CACHE INTERNAL "Profile PhysX preprocessor definitions") +SET(PHYSX_WINDOWS_RELEASE_COMPILE_DEFS "PX_SUPPORT_PVD=0" CACHE INTERNAL "Release PhysX preprocessor definitions") + +# copy the external dlls +IF(PX_COPY_EXTERNAL_DLL) + IF(NOT PHYSX_SLN_PHYSXDEVICE_PATH) + SET(PHYSX_SLN_PHYSXDEVICE_PATH "$ENV{PM_PhysXDevice_PATH}/bin/x86/" CACHE INTERNAL "PhysX device copy path") + ENDIF() + + IF(NOT PHYSX_SLN_GLUT32_PATH) + SET(PHYSX_SLN_GLUT32_PATH "$ENV{PM_glut32_PATH}/bin/" CACHE INTERNAL "PhysX glut32 copy path") + ENDIF() + + IF(CMAKE_CL_64) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x64/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x64/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x64/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x64/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + ELSE() + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + FILE(COPY ${PHYSX_SLN_PHYSXDEVICE_PATH}/PhysXDevice.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x86/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x86/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x86/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + FILE(COPY ${PHYSX_SLN_GLUT32_PATH}/x86/glut32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + ENDIF() +ENDIF() +# for public release we copy the dlls for GPU other platforms so we dont keep duplicates around the repository +IF(PUBLIC_RELEASE) + IF(NOT GPU_DLL_COPIED) + SET(GPU_DLL_COPIED 1 CACHE INTERNAL "PhysX GPU dlls copied") + IF(CMAKE_CL_64) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_64.vc140.mt/debug/PhysXGpu_64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_64.vc140.mt/checked/PhysXGpu_64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_64.vc140.mt/profile/PhysXGpu_64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_64.vc140.mt/release/PhysXGpu_64.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + ELSE() + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_32.vc120.mt/debug/PhysXGpu_32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_DEBUG}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_32.vc120.mt/checked/PhysXGpu_32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_CHECKED}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_32.vc120.mt/profile/PhysXGpu_32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_PROFILE}) + FILE(COPY ${PHYSX_ROOT_DIR}/bin/win.x86_32.vc120.mt/release/PhysXGpu_32.dll DESTINATION ${PX_EXE_OUTPUT_DIRECTORY_RELEASE}) + ENDIF() + ENDIF() +ENDIF() + +SET(GENERATED_GPU_CUDA_FILES "") + +# Include all of the projects +IF(PX_GENERATE_GPU_PROJECTS_ONLY) + INCLUDE(PhysXFoundation.cmake) + INCLUDE(PhysXCommon.cmake) + INCLUDE(PhysXBroadphaseGpu.cmake) + INCLUDE(PhysXCommonGpu.cmake) + INCLUDE(PhysXNarrowphaseGpu.cmake) + INCLUDE(PhysXSimulationControllerGpu.cmake) + INCLUDE(PhysXSolverGpu.cmake) + INCLUDE(CloudGpu.cmake) + INCLUDE(PhysXCudaContextManager.cmake) + INCLUDE(PhysXArticulationGpu.cmake) + INCLUDE(PhysXGpu.cmake) # must be the last PhysXGPU + + SET(PHYSXDISTRO_LIBS PhysXGpu) + INSTALL( + TARGETS ${PHYSXDISTRO_LIBS} + EXPORT PhysXSDK + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> + ) +ELSE() + INCLUDE(PhysXFoundation.cmake) + INCLUDE(LowLevel.cmake) + INCLUDE(LowLevelAABB.cmake) + INCLUDE(LowLevelDynamics.cmake) + INCLUDE(PhysX.cmake) + INCLUDE(PhysXCharacterKinematic.cmake) + INCLUDE(PhysXCommon.cmake) + INCLUDE(PhysXCooking.cmake) + INCLUDE(PhysXExtensions.cmake) + INCLUDE(PhysXVehicle.cmake) + INCLUDE(SceneQuery.cmake) + INCLUDE(SimulationController.cmake) + INCLUDE(FastXml.cmake) + INCLUDE(PhysXPvdSDK.cmake) + INCLUDE(PhysXTask.cmake) + + IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXDISTRO_LIBS PhysXFoundation PhysX PhysXCharacterKinematic PhysXPvdSDK PhysXCommon PhysXCooking PhysXExtensions PhysXVehicle) + ELSE() + SET(PHYSXDISTRO_LIBS PhysXFoundation PhysX PhysXCharacterKinematic PhysXPvdSDK PhysXCommon PhysXCooking PhysXExtensions PhysXVehicle PhysXTask) + ENDIF() + + IF(PX_GENERATE_GPU_PROJECTS) + INCLUDE(PhysXBroadphaseGpu.cmake) + INCLUDE(PhysXCommonGpu.cmake) + INCLUDE(PhysXNarrowphaseGpu.cmake) + INCLUDE(PhysXSimulationControllerGpu.cmake) + INCLUDE(PhysXSolverGpu.cmake) + INCLUDE(CloudGpu.cmake) + INCLUDE(PhysXCudaContextManager.cmake) + INCLUDE(PhysXArticulationGpu.cmake) + INCLUDE(PhysXGpu.cmake) # must be the last PhysXGPU + + LIST(APPEND PHYSXDISTRO_LIBS PhysXGpu) + ENDIF() + + INSTALL( + TARGETS ${PHYSXDISTRO_LIBS} + EXPORT PhysXSDK + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> + ) + +ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake new file mode 100644 index 000000000..da1153c3c --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake @@ -0,0 +1,55 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build FastXml +# + +# Use generator expressions to set config specific preprocessor definitions +SET(FASTXML_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +SET(FASTXML_LIBTYPE STATIC) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND FASTXML_LIBTYPE STREQUAL "STATIC") + SET(FASTXML_COMPILE_PDB_NAME_DEBUG "FastXml_static${CMAKE_DEBUG_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_CHECKED "FastXml_static${CMAKE_CHECKED_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_PROFILE "FastXml_static${CMAKE_PROFILE_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_RELEASE "FastXml_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(FASTXML_COMPILE_PDB_NAME_DEBUG "FastXml${CMAKE_DEBUG_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_CHECKED "FastXml${CMAKE_CHECKED_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_PROFILE "FastXml${CMAKE_PROFILE_POSTFIX}") + SET(FASTXML_COMPILE_PDB_NAME_RELEASE "FastXml${CMAKE_RELEASE_POSTFIX}") +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake new file mode 100644 index 000000000..7b037d3a6 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake @@ -0,0 +1,82 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevel +# + +SET(LOWLEVEL_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/windows + ${PHYSX_SOURCE_DIR}/LowLevel/software/include/windows + ${PHYSX_SOURCE_DIR}/LowLevelDynamics/include/windows + ${PHYSX_SOURCE_DIR}/LowLevel/common/include/pipeline/windows +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(LOWLEVEL_LIBTYPE OBJECT) +ELSE() + SET(LOWLEVEL_LIBTYPE STATIC) +ENDIF() + +SET(LOWLEVEL_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND LOWLEVEL_LIBTYPE STREQUAL "STATIC") + SET(LL_COMPILE_PDB_NAME_DEBUG "LowLevel_static${CMAKE_DEBUG_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_CHECKED "LowLevel_static${CMAKE_CHECKED_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_PROFILE "LowLevel_static${CMAKE_PROFILE_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_RELEASE "LowLevel_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(LL_COMPILE_PDB_NAME_DEBUG "LowLevel${CMAKE_DEBUG_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_CHECKED "LowLevel${CMAKE_CHECKED_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_PROFILE "LowLevel${CMAKE_PROFILE_POSTFIX}") + SET(LL_COMPILE_PDB_NAME_RELEASE "LowLevel${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(LOWLEVEL_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${LL_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVEL_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${LL_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVEL_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${LL_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVEL_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${LL_COMPILE_PDB_NAME_RELEASE}") + + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${LL_COMPILE_PDB_NAME_DEBUG}>$<$:${LL_COMPILE_PDB_NAME_CHECKED}>$<$:${LL_COMPILE_PDB_NAME_PROFILE}>$<$:${LL_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + SET(LOWLEVEL_COMPILE_PDB_NAME_DEBUG "${LL_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVEL_COMPILE_PDB_NAME_CHECKED "${LL_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVEL_COMPILE_PDB_NAME_PROFILE "${LL_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVEL_COMPILE_PDB_NAME_RELEASE "${LL_COMPILE_PDB_NAME_RELEASE}") +ENDIF() + +SET(LOWLEVEL_PLATFORM_LINK_FLAGS "/MAP") diff --git a/src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake new file mode 100644 index 000000000..2efd43b8b --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake @@ -0,0 +1,84 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelAABB +# + +SET(LOWLEVELAABB_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/windows + ${PHYSX_SOURCE_DIR}/LowLevelAABB/windows/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/include + ${PHYSX_SOURCE_DIR}/GpuBroadPhase/src +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(LOWLEVELAABB_LIBTYPE OBJECT) +ELSE() + SET(LOWLEVELAABB_LIBTYPE STATIC) +ENDIF() + +SET(LOWLEVELAABB_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND LOWLEVELAABB_LIBTYPE STREQUAL "STATIC") + SET(LLAABB_COMPILE_PDB_NAME_DEBUG "LowLevelAABB_static${CMAKE_DEBUG_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_CHECKED "LowLevelAABB_static${CMAKE_CHECKED_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_PROFILE "LowLevelAABB_static${CMAKE_PROFILE_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_RELEASE "LowLevelAABB_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(LLAABB_COMPILE_PDB_NAME_DEBUG "LowLevelAABB${CMAKE_DEBUG_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_CHECKED "LowLevelAABB${CMAKE_CHECKED_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_PROFILE "LowLevelAABB${CMAKE_PROFILE_POSTFIX}") + SET(LLAABB_COMPILE_PDB_NAME_RELEASE "LowLevelAABB${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(LOWLEVELAABB_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${LLAABB_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${LLAABB_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${LLAABB_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${LLAABB_COMPILE_PDB_NAME_RELEASE}") + + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${LLAABB_COMPILE_PDB_NAME_DEBUG}>$<$:${LLAABB_COMPILE_PDB_NAME_CHECKED}>$<$:${LLAABB_COMPILE_PDB_NAME_PROFILE}>$<$:${LLAABB_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + SET(LOWLEVELAABB_COMPILE_PDB_NAME_DEBUG "${LLAABB_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_CHECKED "${LLAABB_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_PROFILE "${LLAABB_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVELAABB_COMPILE_PDB_NAME_RELEASE "${LLAABB_COMPILE_PDB_NAME_RELEASE}") +ENDIF() + +SET(LOWLEVELAABB_PLATFORM_LINK_FLAGS "/MAP") + diff --git a/src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake new file mode 100644 index 000000000..facc9890d --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake @@ -0,0 +1,83 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build LowLevelDynamics +# + +SET(LOWLEVELDYNAMICS_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/windows + ${PHYSX_SOURCE_DIR}/lowlevel/software/include/windows + ${PHYSX_SOURCE_DIR}/lowleveldynamics/include/windows + ${PHYSX_SOURCE_DIR}/lowlevel/common/include/pipeline/windows +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(LOWLEVELDYNAMICS_LIBTYPE OBJECT) +ELSE() + SET(LOWLEVELDYNAMICS_LIBTYPE STATIC) +ENDIF() + +# Use generator expressions to set config specific preprocessor definitions +SET(LOWLEVELDYNAMICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND LOWLEVELDYNAMICS_LIBTYPE STREQUAL "STATIC") + SET(LLDYNAMICS_COMPILE_PDB_NAME_DEBUG "LowLevelDynamics_static${CMAKE_DEBUG_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_CHECKED "LowLevelDynamics_static${CMAKE_CHECKED_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_PROFILE "LowLevelDynamics_static${CMAKE_PROFILE_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_RELEASE "LowLevelDynamics_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(LLDYNAMICS_COMPILE_PDB_NAME_DEBUG "LowLevelDynamics${CMAKE_DEBUG_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_CHECKED "LowLevelDynamics${CMAKE_CHECKED_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_PROFILE "LowLevelDynamics${CMAKE_PROFILE_POSTFIX}") + SET(LLDYNAMICS_COMPILE_PDB_NAME_RELEASE "LowLevelDynamics${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${LLDYNAMICS_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${LLDYNAMICS_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${LLDYNAMICS_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${LLDYNAMICS_COMPILE_PDB_NAME_RELEASE}") + + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${LLDYNAMICS_COMPILE_PDB_NAME_DEBUG}>$<$:${LLDYNAMICS_COMPILE_PDB_NAME_CHECKED}>$<$:${LLDYNAMICS_COMPILE_PDB_NAME_PROFILE}>$<$:${LLDYNAMICS_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_DEBUG "${LLDYNAMICS_COMPILE_PDB_NAME_DEBUG}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_CHECKED "${LLDYNAMICS_COMPILE_PDB_NAME_CHECKED}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_PROFILE "${LLDYNAMICS_COMPILE_PDB_NAME_PROFILE}") + SET(LOWLEVELDYNAMICS_COMPILE_PDB_NAME_RELEASE "${LLDYNAMICS_COMPILE_PDB_NAME_RELEASE}") +ENDIF() + diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake new file mode 100644 index 000000000..aada387ab --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake @@ -0,0 +1,169 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysX (PROJECT not SOLUTION) +# + +IF(PX_USE_NVTX) + FIND_PACKAGE(nvToolsExt $ENV{PM_nvToolsExt_VERSION} REQUIRED) + MESSAGE("Using nvtx lib: ${NVTOOLSEXT_LIB} path: ${NVTOOLSEXTSDK_PATH}") + SET(NV_TOOLS_EXT_LIB ${NVTOOLSEXT_LIB}) +ENDIF() + +SET(PHYSX_PLATFORM_INCLUDES + ${NVTOOLSEXT_INCLUDE_DIRS} +) + +SET(PHYSX_GPU_HEADERS + ${PHYSX_ROOT_DIR}/include/gpu/PxGpu.h +) +SOURCE_GROUP(include\\gpu FILES ${PHYSX_GPU_HEADERS}) + +SET(PHYSX_COMMON_WINDOWS_HEADERS + ${PHYSX_ROOT_DIR}/include/common/windows/PxWindowsDelayLoadHook.h +) +SOURCE_GROUP(include\\gpu FILES ${PHYSX_GPU_HEADERS}) + +SET(PHYSX_RESOURCE + ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/PhysX.rc + ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/resource.h +) +SOURCE_GROUP(resource FILES ${PHYSX_RESOURCE}) + +SET(PHYSX_DEVICE_SOURCE + ${PX_SOURCE_DIR}/device/nvPhysXtoDrv.h + ${PX_SOURCE_DIR}/device/PhysXIndicator.h + ${PX_SOURCE_DIR}/device/windows/PhysXIndicatorWindows.cpp +) +SOURCE_GROUP(src\\device FILES ${PHYSX_DEVICE_SOURCE}) + +SET(PHYSX_GPU_SOURCE + ${PX_SOURCE_DIR}/gpu/PxGpu.cpp + ${PX_SOURCE_DIR}/gpu/PxPhysXGpuModuleLoader.cpp +) +SOURCE_GROUP(src\\gpu FILES ${PHYSX_GPU_SOURCE}) + +SET(PHYSX_WINDOWS_SOURCE + ${PX_SOURCE_DIR}/windows/NpWindowsDelayLoadHook.cpp +) +SOURCE_GROUP(src\\windows FILES ${PHYSX_WINDOWS_SOURCE}) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_PLATFORM_OBJECT_FILES + $ + $ + $ + $ + $ + $ + ) +ENDIF() + +SET(PHYSX_PLATFORM_SRC_FILES + ${PHYSX_GPU_HEADERS} + ${PHYSX_RESOURCE} + ${PHYSX_COMMON_WINDOWS_HEADERS} + + ${PHYSX_DEVICE_SOURCE} + ${PHYSX_GPU_SOURCE} + + ${PHYSX_WINDOWS_SOURCE} + + ${PHYSX_PLATFORM_OBJECT_FILES} +) + +INSTALL(FILES ${PHYSX_GPU_HEADERS} DESTINATION include/gpu) +INSTALL(FILES ${PHYSX_COMMON_WINDOWS_HEADERS} DESTINATION include/common/windows) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) + SET(BITNESS_STRING $<$:64>$<$:32>) + SET(PHYSX_GPU_SHARED_LIB_NAME_DEF PX_PHYSX_GPU_SHARED_LIB_NAME=PhysXGpu_${BITNESS_STRING}.dll) +ELSE() + SET(CONFIG_STRING $<$:DEBUG>$<$:CHECKED>$<$:PROFILE>) + SET(BITNESS_STRING $<$:x64>$<$:x86>) + SET(PHYSX_GPU_SHARED_LIB_NAME_DEF PX_PHYSX_GPU_SHARED_LIB_NAME=PhysXGpu${CONFIG_STRING}_${BITNESS_STRING}.dll) +ENDIF() + +IF(NOT PX_GENERATE_STATIC_LIBRARIES) + SET(PXPHYSX_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1;PX_PHYSX_CORE_EXPORTS; + ) +ENDIF() + +SET(PHYSX_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PXPHYSX_LIBTYPE_DEFS};${PHYSX_GPU_SHARED_LIB_NAME_DEF};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_LIBTYPE STATIC) +ELSE() + SET(PHYSX_LIBTYPE SHARED) +ENDIF() + +IF(PX_GENERATE_GPU_PROJECTS) + SET(PHYSX_PLATFORM_LINK_FLAGS "/DELAYLOAD:nvcuda.dll") +ENDIF() + +IF(NOT PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSX_PRIVATE_PLATFORM_LINKED_LIBS + LowLevel LowLevelAABB LowLevelDynamics PhysXTask SceneQuery SimulationController ${NV_TOOLS_EXT_LIB} + ) +ENDIF() + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSX_LIBTYPE STREQUAL "STATIC") + SET(PHYSX_COMPILE_PDB_NAME_DEBUG "PhysX_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_CHECKED "PhysX_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_PROFILE "PhysX_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_RELEASE "PhysX_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSX_COMPILE_PDB_NAME_DEBUG "PhysX${CMAKE_DEBUG_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_CHECKED "PhysX${CMAKE_CHECKED_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_PROFILE "PhysX${CMAKE_PROFILE_POSTFIX}") + SET(PHYSX_COMPILE_PDB_NAME_RELEASE "PhysX${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +SET(PHYSX_PLATFORM_LINK_FLAGS "/MAP") + +IF(PHYSX_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) + + SET(PHYSX_PLATFORM_LINK_FLAGS_DEBUG "/DELAYLOAD:PhysXFoundation${CMAKE_DEBUG_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_DEBUG_POSTFIX}.dll") + SET(PHYSX_PLATFORM_LINK_FLAGS_CHECKED "/DELAYLOAD:PhysXFoundation${CMAKE_CHECKED_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_CHECKED_POSTFIX}.dll") + SET(PHYSX_PLATFORM_LINK_FLAGS_PROFILE "/DELAYLOAD:PhysXFoundation${CMAKE_PROFILE_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_PROFILE_POSTFIX}.dll") + SET(PHYSX_PLATFORM_LINK_FLAGS_RELEASE "/DELAYLOAD:PhysXFoundation${CMAKE_RELEASE_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_RELEASE_POSTFIX}.dll") +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSX_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSX_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSX_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSX_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake new file mode 100644 index 000000000..0519de2d7 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake @@ -0,0 +1,65 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCharacterKinematic +# + +SET(PHYSXCHARACTERKINEMATIC_LIBTYPE STATIC) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};PX_PHYSX_CHARACTER_STATIC_LIB;${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCHARACTERKINEMATIC_LIBTYPE STREQUAL "STATIC") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_DEBUG "PhysXCharacterKinematic_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_CHECKED "PhysXCharacterKinematic_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_PROFILE "PhysXCharacterKinematic_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_RELEASE "PhysXCharacterKinematic_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_DEBUG "PhysXCharacterKinematic${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_CHECKED "PhysXCharacterKinematic${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_PROFILE "PhysXCharacterKinematic${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_RELEASE "PhysXCharacterKinematic${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXCHARACTERKINEMATIC_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXCHARACTERKINEMATIC_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() + diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake new file mode 100644 index 000000000..572f0db76 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake @@ -0,0 +1,107 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCommon +# + +SET(PHYSXCOMMON_WINDOWS_HEADERS + ${PHYSX_SOURCE_DIR}/common/include/windows/CmWindowsLoadLibrary.h + ${PHYSX_SOURCE_DIR}/common/include/windows/CmWindowsModuleUpdateLoader.h +) +SOURCE_GROUP(common\\include\\windows FILES ${PHYSXCOMMON_WINDOWS_HEADERS}) + +SET(PHYSXCOMMON_WINDOWS_SOURCE + ${COMMON_SRC_DIR}/windows/CmWindowsModuleUpdateLoader.cpp + ${COMMON_SRC_DIR}/windows/CmWindowsDelayLoadHook.cpp +) +SOURCE_GROUP(common\\src\\windows FILES ${PHYSXCOMMON_WINDOWS_SOURCE}) + +SET(PHYSXCOMMON_RESOURCE + ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/PhysXCommon.rc) +SOURCE_GROUP(resource FILES ${PHYSXCOMMON_RESOURCE}) + +SET(PXCOMMON_PLATFORM_SRC_FILES + ${PHYSXCOMMON_WINDOWS_HEADERS} + ${PHYSXCOMMON_WINDOWS_SOURCE} + + ${PHYSXCOMMON_RESOURCE} +) + +SET(PXCOMMON_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/common/src/windows +) + +IF(NOT PX_GENERATE_STATIC_LIBRARIES) + SET(PXCOMMON_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS; + ) +ENDIF() + +SET(PXCOMMON_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PXCOMMON_LIBTYPE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + # Switch platforms here? + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS}> +) + +SET(PXCOMMON_PLATFORM_LINK_FLAGS "/MAP") + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXCOMMON_LIBTYPE STATIC) +ELSE() + SET(PHYSXCOMMON_LIBTYPE SHARED) +ENDIF() + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCOMMON_LIBTYPE STREQUAL "STATIC") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_DEBUG "PhysXCommon_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_CHECKED "PhysXCommon_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_PROFILE "PhysXCommon_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_RELEASE "PhysXCommon_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXCOMMON_COMPILE_PDB_NAME_DEBUG "PhysXCommon${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_CHECKED "PhysXCommon${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_PROFILE "PhysXCommon${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCOMMON_COMPILE_PDB_NAME_RELEASE "PhysXCommon${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXCOMMON_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) + + SET(PXCOMMON_PLATFORM_LINK_FLAGS_DEBUG "/DELAYLOAD:PhysXFoundation${CMAKE_DEBUG_POSTFIX}.dll") + SET(PXCOMMON_PLATFORM_LINK_FLAGS_CHECKED "/DELAYLOAD:PhysXFoundation${CMAKE_CHECKED_POSTFIX}.dll") + SET(PXCOMMON_PLATFORM_LINK_FLAGS_PROFILE "/DELAYLOAD:PhysXFoundation${CMAKE_PROFILE_POSTFIX}.dll") + SET(PXCOMMON_PLATFORM_LINK_FLAGS_RELEASE "/DELAYLOAD:PhysXFoundation${CMAKE_RELEASE_POSTFIX}.dll") +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXCOMMON_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXCOMMON_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXCOMMON_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXCOMMON_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake new file mode 100644 index 000000000..13c254ac4 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake @@ -0,0 +1,94 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXCooking +# + +IF(NOT PX_GENERATE_STATIC_LIBRARIES) + SET(PXCOOKING_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_CORE_EXPORTS + ) +ENDIF() + +SET(PHYSXCOOKING_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};PX_COOKING;${PXCOOKING_LIBTYPE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> + +) + +SET(PHYSX_COOKING_WINDOWS_SOURCE + ${LL_SOURCE_DIR}/windows/WindowsCookingDelayLoadHook.cpp +) +SOURCE_GROUP(src\\windows FILES ${PHYSX_COOKING_WINDOWS_SOURCE}) + +SET(PHYSX_COOKING_RESOURCE + ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/PhysXCooking.rc +) +SOURCE_GROUP(resource FILES ${PHYSX_COOKING_RESOURCE}) + +SET(PHYSXCOOKING_PLATFORM_SRC_FILES + ${PHYSX_COOKING_WINDOWS_SOURCE} + ${PHYSX_COOKING_RESOURCE} +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXCOOKING_LIBTYPE STATIC) +ELSE() + SET(PHYSXCOOKING_LIBTYPE SHARED) +ENDIF() + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXCOOKING_LIBTYPE STREQUAL "STATIC") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_DEBUG "PhysXCooking_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_CHECKED "PhysXCooking_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_PROFILE "PhysXCooking_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_RELEASE "PhysXCooking_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXCOOKING_COMPILE_PDB_NAME_DEBUG "PhysXCooking${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_CHECKED "PhysXCooking${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_PROFILE "PhysXCooking${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXCOOKING_COMPILE_PDB_NAME_RELEASE "PhysXCooking${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXCOOKING_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXCOOKING_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXCOOKING_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXCOOKING_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXCOOKING_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() + +SET(PHYSXCOOKING_LINK_FLAGS "/MAP") +SET(PHYSXCOOKING_LINK_FLAGS_DEBUG "/DELAYLOAD:PhysXFoundation${CMAKE_DEBUG_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_DEBUG_POSTFIX}.dll") +SET(PHYSXCOOKING_LINK_FLAGS_CHECKED "/DELAYLOAD:PhysXFoundation${CMAKE_CHECKED_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_CHECKED_POSTFIX}.dll") +SET(PHYSXCOOKING_LINK_FLAGS_PROFILE "/DELAYLOAD:PhysXFoundation${CMAKE_PROFILE_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_PROFILE_POSTFIX}.dll") +SET(PHYSXCOOKING_LINK_FLAGS_RELEASE "/DELAYLOAD:PhysXFoundation${CMAKE_RELEASE_POSTFIX}.dll /DELAYLOAD:PhysXCommon${CMAKE_RELEASE_POSTFIX}.dll") diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake new file mode 100644 index 000000000..6fec964d7 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake @@ -0,0 +1,77 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXExtensions +# + + +SET(PHYSXEXTENSIONS_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/windows +) + +SET(PHYSXEXTENSIONS_PLATFORM_SRC_FILES + +) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXEXTENSIONS_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXEXTENSIONS_PRIVATE_PLATFORM_LINKED_LIBS + FastXml +) + +SET(PHYSXEXTENSIONS_LIBTYPE STATIC) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXEXTENSIONS_LIBTYPE STREQUAL "STATIC") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_DEBUG "PhysXExtensions_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_CHECKED "PhysXExtensions_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_PROFILE "PhysXExtensions_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_RELEASE "PhysXExtensions_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_DEBUG "PhysXExtensions${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_CHECKED "PhysXExtensions${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_PROFILE "PhysXExtensions${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXEXTENSIONS_COMPILE_PDB_NAME_RELEASE "PhysXExtensions${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXEXTENSIONS_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXEXTENSIONS_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXEXTENSIONS_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXEXTENSIONS_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXEXTENSIONS_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake new file mode 100644 index 000000000..a4ca7b522 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake @@ -0,0 +1,126 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXFoundation +# + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXFOUNDATION_LIBTYPE STATIC) +ELSE() + SET(PHYSXFOUNDATION_LIBTYPE SHARED) + SET(PHYSXFOUNDATION_SHARED_LIBRARY_DEFS PX_FOUNDATION_DLL=1;) +ENDIF() + +SET(PXSHARED_PLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/windows/PxWindowsIntrinsics.h +) +SOURCE_GROUP(shared\\include\\windows FILES ${PXSHARED_PLATFORM_HEADERS}) +SET(PXSHARED_UNIXPLATFORM_HEADERS + ${PXSHARED_PATH}/include/foundation/unix/PxUnixIntrinsics.h +) +SOURCE_GROUP(shared\\include\\unix FILES ${PXSHARED_UNIXPLATFORM_HEADERS}) + +SET(PHYSXFOUNDATION_RESOURCE_FILE + ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/PhysXFoundation.rc +) +SOURCE_GROUP(resource FILES ${PHYSXFOUNDATION_RESOURCE_FILE}) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE + ${LL_SOURCE_DIR}/src/windows/PsWindowsAtomic.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsCpu.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsFPU.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsMutex.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsPrintString.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsSList.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsSocket.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsSync.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsThread.cpp + ${LL_SOURCE_DIR}/src/windows/PsWindowsTime.cpp +) +SOURCE_GROUP("src\\src\\windows" FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE}) + +SET(PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS + ${LL_SOURCE_DIR}/include/windows/PsWindowsAoS.h + ${LL_SOURCE_DIR}/include/windows/PsWindowsFPU.h + ${LL_SOURCE_DIR}/include/windows/PsWindowsInclude.h + ${LL_SOURCE_DIR}/include/windows/PsWindowsInlineAoS.h + ${LL_SOURCE_DIR}/include/windows/PsWindowsIntrinsics.h + ${LL_SOURCE_DIR}/include/windows/PsWindowsTrigConstants.h +) +SOURCE_GROUP("src\\include\\windows" FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS}) + +INSTALL(FILES ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} DESTINATION source/foundation/include/windows) +INSTALL(FILES ${PXSHARED_PLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/windows) +INSTALL(FILES ${PXSHARED_UNIXPLATFORM_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation/unix) + +SET(PHYSXFOUNDATION_PLATFORM_FILES + ${PHYSXFOUNDATION_PLATFORM_HEADERS} + ${PHYSXFOUNDATION_PLATFORM_SOURCE} + ${PHYSXFOUNDATION_PLATFORM_SOURCE_HEADERS} + ${PHYSXFOUNDATION_RESOURCE_FILE} +) + +SET(PHYSXFOUNDATION_PLATFORM_INCLUDES + ${LL_SOURCE_DIR}/include/windows +) + +IF(NOT PX_GENERATE_STATIC_LIBRARIES) + SET(PXFOUNDATION_LIBTYPE_DEFS + PX_FOUNDATION_DLL=1; + ) +ENDIF() + +SET(PHYSXFOUNDATION_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};_WINSOCK_DEPRECATED_NO_WARNINGS;${PXFOUNDATION_LIBTYPE_DEFS};${PHYSX_LIBTYPE_DEFS}; + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXFOUNDATION_LIBTYPE STREQUAL "STATIC") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_DEBUG "PhysXFoundation_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_CHECKED "PhysXFoundation_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_PROFILE "PhysXFoundation_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_RELEASE "PhysXFoundation_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_DEBUG "PhysXFoundation${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_CHECKED "PhysXFoundation${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_PROFILE "PhysXFoundation${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXFOUNDATION_COMPILE_PDB_NAME_RELEASE "PhysXFoundation${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXFOUNDATION_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXFOUNDATION_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXFOUNDATION_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXFOUNDATION_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXFOUNDATION_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake new file mode 100644 index 000000000..06cb95478 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake @@ -0,0 +1,89 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXPvdSDK +# + +IF(PX_USE_NVTX) + FIND_PACKAGE(nvToolsExt $ENV{PM_nvToolsExt_VERSION} REQUIRED) + MESSAGE("Using nvtx lib: ${NVTOOLSEXT_LIB} path: ${NVTOOLSEXTSDK_PATH}") + SET(NV_TOOLS_EXT_LIB ${NVTOOLSEXT_LIB}) +ENDIF() + +SOURCE_GROUP(resource FILES ${PHYSXPVDSDK_RESOURCE_FILE}) + +SOURCE_GROUP(include\\windows FILES ${PHYSXPVDSDK_PLATFORM_HEADERS}) + +SOURCE_GROUP(src\\src\\windows FILES ${PHYSXPVDSDK_PLATFORM_SOURCE}) + +SET(PHYSXPVDSDK_PLATFORM_FILES + ${PHYSXPVDSDK_RESOURCE_FILE} + ${PHYSXPVDSDK_PLATFORM_HEADERS} + ${PHYSXPVDSDK_PLATFORM_SOURCE} +) + +SET(PHYSXPVDSDK_PLATFORM_INCLUDES + ${NVTOOLSEXT_INCLUDE_DIRS} +) + +SET(PHYSXPVDSDK_LIBTYPE STATIC) + +SET(PHYSXPVDSDK_PLATFORM_LINKED_LIBS ${NV_TOOLS_EXT_LIB}) + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXPVDSDK_COMPILE_DEFS + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB;${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXPVDSDK_LIBTYPE STREQUAL "STATIC") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_DEBUG "PhysXPvdSDK_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_CHECKED "PhysXPvdSDK_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_PROFILE "PhysXPvdSDK_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_RELEASE "PhysXPvdSDK_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_DEBUG "PhysXPvdSDK${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_CHECKED "PhysXPvdSDK${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_PROFILE "PhysXPvdSDK${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXPVDSDK_COMPILE_PDB_NAME_RELEASE "PhysXPvdSDK${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXPVDSDK_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXPVDSDK_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXPVDSDK_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXPVDSDK_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXPVDSDK_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() + + diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake new file mode 100644 index 000000000..dcd1013e5 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake @@ -0,0 +1,73 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXTask +# + + +SET(PHYSXTASK_COMPILE_DEFS + ${PHYSX_WINDOWS_COMPILE_DEFS};_LIB;${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(PHYSXTASK_LIBTYPE OBJECT) +ELSE() + SET(PHYSXTASK_LIBTYPE STATIC) +ENDIF() + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXTASK_LIBTYPE STREQUAL "STATIC") + SET(PT_COMPILE_PDB_NAME_DEBUG "PhysXTask_static${CMAKE_DEBUG_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_CHECKED "PhysXTask_static${CMAKE_CHECKED_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_PROFILE "PhysXTask_static${CMAKE_PROFILE_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_RELEASE "PhysXTask_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PT_COMPILE_PDB_NAME_DEBUG "PhysXTask${CMAKE_DEBUG_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_CHECKED "PhysXTask${CMAKE_CHECKED_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_PROFILE "PhysXTask${CMAKE_PROFILE_POSTFIX}") + SET(PT_COMPILE_PDB_NAME_RELEASE "PhysXTask${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(PHYSXTASK_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${PT_COMPILE_PDB_NAME_DEBUG}") + SET(PHYSXTASK_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${PT_COMPILE_PDB_NAME_CHECKED}") + SET(PHYSXTASK_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${PT_COMPILE_PDB_NAME_PROFILE}") + SET(PHYSXTASK_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${PT_COMPILE_PDB_NAME_RELEASE}") +ELSE() + SET(PHYSXTASK_COMPILE_PDB_NAME_DEBUG "${PT_COMPILE_PDB_NAME_DEBUG}") + SET(PHYSXTASK_COMPILE_PDB_NAME_CHECKED "${PT_COMPILE_PDB_NAME_CHECKED}") + SET(PHYSXTASK_COMPILE_PDB_NAME_PROFILE "${PT_COMPILE_PDB_NAME_PROFILE}") + SET(PHYSXTASK_COMPILE_PDB_NAME_RELEASE "${PT_COMPILE_PDB_NAME_RELEASE}") +ENDIF() + +INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PT_COMPILE_PDB_NAME_DEBUG}>$<$:${PT_COMPILE_PDB_NAME_CHECKED}>$<$:${PT_COMPILE_PDB_NAME_PROFILE}>$<$:${PT_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake new file mode 100644 index 000000000..3c8eec283 --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake @@ -0,0 +1,64 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build PhysXVehicle +# + +# Use generator expressions to set config specific preprocessor definitions +SET(PHYSXVEHICLE_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};PX_PHYSX_STATIC_LIB;${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +SET(PHYSXVEHICLE_LIBTYPE STATIC) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND PHYSXVEHICLE_LIBTYPE STREQUAL "STATIC") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_DEBUG "PhysXVehicle_static${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_CHECKED "PhysXVehicle_static${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_PROFILE "PhysXVehicle_static${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_RELEASE "PhysXVehicle_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_DEBUG "PhysXVehicle${CMAKE_DEBUG_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_CHECKED "PhysXVehicle${CMAKE_CHECKED_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_PROFILE "PhysXVehicle${CMAKE_PROFILE_POSTFIX}") + SET(PHYSXVEHICLE_COMPILE_PDB_NAME_RELEASE "PhysXVehicle${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PHYSXVEHICLE_LIBTYPE STREQUAL "SHARED") + INSTALL(FILES $ + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${PHYSXVEHICLE_COMPILE_PDB_NAME_DEBUG}>$<$:${PHYSXVEHICLE_COMPILE_PDB_NAME_CHECKED}>$<$:${PHYSXVEHICLE_COMPILE_PDB_NAME_PROFILE}>$<$:${PHYSXVEHICLE_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake new file mode 100644 index 000000000..76604a4dc --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake @@ -0,0 +1,81 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SceneQuery +# + + +SET(SCENEQUERY_PLATFORM_INCLUDES + PRIVATE ${PHYSX_SOURCE_DIR}/Common/src/windows +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(SCENEQUERY_LIBTYPE OBJECT) +ELSE() + SET(SCENEQUERY_LIBTYPE STATIC) +ENDIF() + + +# Use generator expressions to set config specific preprocessor definitions +SET(SCENEQUERY_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND SCENEQUERY_LIBTYPE STREQUAL "STATIC") + SET(SQ_COMPILE_PDB_NAME_DEBUG "SceneQuery_static${CMAKE_DEBUG_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_CHECKED "SceneQuery_static${CMAKE_CHECKED_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_PROFILE "SceneQuery_static${CMAKE_PROFILE_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_RELEASE "SceneQuery_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(SQ_COMPILE_PDB_NAME_DEBUG "SceneQuery${CMAKE_DEBUG_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_CHECKED "SceneQuery${CMAKE_CHECKED_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_PROFILE "SceneQuery${CMAKE_PROFILE_POSTFIX}") + SET(SQ_COMPILE_PDB_NAME_RELEASE "SceneQuery${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(SCENEQUERY_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${SQ_COMPILE_PDB_NAME_DEBUG}") + SET(SCENEQUERY_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${SQ_COMPILE_PDB_NAME_CHECKED}") + SET(SCENEQUERY_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${SQ_COMPILE_PDB_NAME_PROFILE}") + SET(SCENEQUERY_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${SQ_COMPILE_PDB_NAME_RELEASE}") + + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${SQ_COMPILE_PDB_NAME_DEBUG}>$<$:${SQ_COMPILE_PDB_NAME_CHECKED}>$<$:${SQ_COMPILE_PDB_NAME_PROFILE}>$<$:${SQ_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + SET(SCENEQUERY_COMPILE_PDB_NAME_DEBUG "${SQ_COMPILE_PDB_NAME_DEBUG}") + SET(SCENEQUERY_COMPILE_PDB_NAME_CHECKED "${SQ_COMPILE_PDB_NAME_CHECKED}") + SET(SCENEQUERY_COMPILE_PDB_NAME_PROFILE "${SQ_COMPILE_PDB_NAME_PROFILE}") + SET(SCENEQUERY_COMPILE_PDB_NAME_RELEASE "${SQ_COMPILE_PDB_NAME_RELEASE}") +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake new file mode 100644 index 000000000..856d9a23a --- /dev/null +++ b/src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake @@ -0,0 +1,81 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +# +# Build SimulationController +# + +SET(SIMULATIONCONTROLLER_PLATFORM_INCLUDES + ${PHYSX_SOURCE_DIR}/Common/src/windows + ${PHYSX_SOURCE_DIR}/LowLevel/windows/include +) + +IF(PX_GENERATE_STATIC_LIBRARIES) + SET(SIMULATIONCONTROLLER_LIBTYPE OBJECT) +ELSE() + SET(SIMULATIONCONTROLLER_LIBTYPE STATIC) +ENDIF() + + +# Use generator expressions to set config specific preprocessor definitions +SET(SIMULATIONCONTROLLER_COMPILE_DEFS + + # Common to all configurations + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + + $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_PROFILE_COMPILE_DEFS};> + $<$:${PHYSX_WINDOWS_RELEASE_COMPILE_DEFS};> +) + +IF(NV_USE_GAMEWORKS_OUTPUT_DIRS AND SIMULATIONCONTROLLER_LIBTYPE STREQUAL "STATIC") + SET(SC_COMPILE_PDB_NAME_DEBUG "SimulationController_static${CMAKE_DEBUG_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_CHECKED "SimulationController_static${CMAKE_CHECKED_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_PROFILE "SimulationController_static${CMAKE_PROFILE_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_RELEASE "SimulationController_static${CMAKE_RELEASE_POSTFIX}") +ELSE() + SET(SC_COMPILE_PDB_NAME_DEBUG "SimulationController${CMAKE_DEBUG_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_CHECKED "SimulationController${CMAKE_CHECKED_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_PROFILE "SimulationController${CMAKE_PROFILE_POSTFIX}") + SET(SC_COMPILE_PDB_NAME_RELEASE "SimulationController${CMAKE_RELEASE_POSTFIX}") +ENDIF() + +IF(PX_EXPORT_LOWLEVEL_PDB) + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_DEBUG "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/debug/${SC_COMPILE_PDB_NAME_DEBUG}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_CHECKED "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/checked/${SC_COMPILE_PDB_NAME_CHECKED}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_PROFILE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/profile/${SC_COMPILE_PDB_NAME_PROFILE}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_RELEASE "${PHYSX_ROOT_DIR}/${PX_ROOT_LIB_DIR}/release/${SC_COMPILE_PDB_NAME_RELEASE}") + + INSTALL(FILES ${PHYSX_ROOT_DIR}/$<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile>/$<$:${SC_COMPILE_PDB_NAME_DEBUG}>$<$:${SC_COMPILE_PDB_NAME_CHECKED}>$<$:${SC_COMPILE_PDB_NAME_PROFILE}>$<$:${SC_COMPILE_PDB_NAME_RELEASE}>.pdb + DESTINATION $<$:${PX_ROOT_LIB_DIR}/debug>$<$:${PX_ROOT_LIB_DIR}/release>$<$:${PX_ROOT_LIB_DIR}/checked>$<$:${PX_ROOT_LIB_DIR}/profile> OPTIONAL) +ELSE() + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_DEBUG "${SC_COMPILE_PDB_NAME_DEBUG}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_CHECKED "${SC_COMPILE_PDB_NAME_CHECKED}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_PROFILE "${SC_COMPILE_PDB_NAME_PROFILE}") + SET(SIMULATIONCONTROLLER_COMPILE_PDB_NAME_RELEASE "${SC_COMPILE_PDB_NAME_RELEASE}") +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysX.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysX.rc new file mode 100644 index 000000000..e93c4a969 --- /dev/null +++ b/src/PhysX/physx/source/compiler/resource_x64/PhysX.rc @@ -0,0 +1,88 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""windows.h""\r\r\r\0" +END + +3 TEXTINCLUDE +BEGIN + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,0,0,0 + PRODUCTVERSION 4,0,0,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "NVIDIA Corporation" + VALUE "FileDescription", "PhysX 64bit Dynamic Link Library" + VALUE "FileVersion", "4.0.0.0" + VALUE "InternalName", "PhysX_64" + VALUE "LegalCopyright", "Copyright (C) 2018 NVIDIA Corporation" + VALUE "OriginalFilename", "PhysX_64.dll" + VALUE "ProductName", "PhysX" + VALUE "ProductVersion", "4.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc new file mode 100644 index 000000000..5d8a51c48 --- /dev/null +++ b/src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc @@ -0,0 +1,88 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""windows.h""\r\r\r\0" +END + +3 TEXTINCLUDE +BEGIN + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,0,0,0 + PRODUCTVERSION 4,0,0,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "NVIDIA Corporation" + VALUE "FileDescription", "PhysXCommon 64bit Dynamic Link Library" + VALUE "FileVersion", "4.0.0.0" + VALUE "InternalName", "PhysXCommon_64" + VALUE "LegalCopyright", "Copyright (C) 2018 NVIDIA Corporation" + VALUE "OriginalFilename", "PhysXCommon_64.dll" + VALUE "ProductName", "PhysX" + VALUE "ProductVersion", "4.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc new file mode 100644 index 000000000..577b8060d --- /dev/null +++ b/src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc @@ -0,0 +1,91 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" + + + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""windows.h""\r\r\r\0" +END + +3 TEXTINCLUDE +BEGIN + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,0,0,0 + PRODUCTVERSION 4,0,0,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "NVIDIA Corporation" + VALUE "FileDescription", "PhysXCooking 64bit Dynamic Link Library" + VALUE "FileVersion", "4.0.0.0" + VALUE "InternalName", "PhysXCooking_64" + VALUE "LegalCopyright", "Copyright (C) 2018 NVIDIA Corporation" + VALUE "OriginalFilename", "PhysXCooking_64.dll" + VALUE "ProductName", "PhysX" + VALUE "ProductVersion", "4.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysXFoundation.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysXFoundation.rc new file mode 100644 index 0000000000000000000000000000000000000000..f2e1701b63d2ddabaf25bd782049b201c35e4eae GIT binary patch literal 4644 zcmdUyTW=CU6vxlAiQi$RFE*NX+Y+^p<ofs{*X(uB0-A`yXfAxh$BSAS=QVRzXD z1^a;6%+Bo0oSAd}=eoas9?P+KaxDXSlqVTWKDvs)<-%)XwX z)3Yz6CUt2@Qwma&s#_oRQVgDY-o$Ech0+&^cd!RIy zugE_I9mS!6)VI7#NS#Mc#iPjlDQ#6Ff7Zb_gRiBFj zox5MXb8DCe_Yc zaP$%C!hh$}>E7yetG%`p5Lv%7hvZkr-*vCIc+%tNo%o9ws#>skDV0Wt?Qckn+8Ig4 zjEvk7d+xAG8^1#y*YR_<6F=MDa$q8A2Vc=BVITGKw1|7WHyO0Tw@Q79cPVvr#my_F$Bq&)pt!QflPtdMFPq(4i&(1FPhA1_tLTYek*H07cbvVv}O>~#LG2Pd5oKXc&MI^(` zQ}nF$R3FU@kf@7(Mj$g@@xS!boF~Lj_0gs;=d`Rm@Q!Iv$mYhOZsgNkpg!_>rr=-y|qRr zowI%mf?C0n=d^?Bf)mx1^5^WrYBIFH5RtP~TdOgv_^jO9CIcZosqNYMZ>jS>M!hu&ztLNkGlP_}+l%kdX+-0~Z88;)$&2&1@@ z%vJ%a40GTgh6ejK6~45c3Es= zLj%nPm9^gW?#|B4KZhOv{&g(J;>ooP0-4Lb4Ef5-J04H^(v=Zy z%KVJJ!`zf!>E?_c8GC%`X#<(C=SW`5E4Un+{(#vLt3!5o%-&3b{z$$^#_U$g1GBGZ z==AIhsYzWL(v*Ufq$+K>ks`AN#uX{cXJ+cWuO-J!nX%)qGVI^uq;tI|SKzXWaSxQn z@(ulGprbf6(E65l39a+!sdyBbKc%f&! zCw`hlS02u7ysONk=l1Y&;|3pYLsfzP&~Pm?TVb|Ie`WmiT;6la$%;(tEvbIq zLZVMl7yi4LPLEb+TkW--fXMotIW)gA@veKl#gm>t@5Eo^P_=@^OQ|$FY|kMrYiA^x zGcs~V?zzJ&ZT=2rTqn=jPV#Ka<-lar4zZ$H!af@1X&EEwXNuc?<%Fm-PCD1?&8_!y zfVN{S;pVm1*5XEN^DYvlb#sGO_*Shii7us%uDE%n^w?1%2NYNKc#6h@(EK%nUzu^4|&c6I0IzF(Xr>j5qmRVnOQ<2-g2~Awi@FaJFqwkVO z18~~B-(T|68`=6#oT7?+_U!CpZ-`QZDx`*FcKtN5UPs~_*hF_Zj_I+UtUQS*|s1 z(kIq$K~O7LwQ|VMlQS~r1KhPDF0gZCq}FQLLo>zx>tghPy!W)HHHdANG9I%IlPuM< zziiuRuYRC)!PH1ROrX74@3zr<8-8VY_0fN5EH?oK^~^r#&XyKdo5okPhRK_n;uJkk zE&q~!P+dr(wo>_=Usy|q{`VY2j_{5l&o;=^;6H|Lj?c`tw8QRvcK4^SUT_|p zoQY?QmhpRs>n<56o+lb0ra2-2=`a>;Oz1se^OLO})o=Vo7Ps<-(uN~@XM{Q2X=ZDH zm#M3xck+IXRr_eOx96t6reqDYUm8WFQO)$*_5b?+tQG0z|7V9~REqr?tAy)o-Vymo M@3Z9Ja? +#include +#include +#include + +using namespace physx; + +namespace +{ +#define MIN_CLOSE_COUNT 2 +#define DEFAULT_READ_BUFFER_SIZE (16 * 1024) +#define NUM_ENTITY 5 + +struct Entity +{ + const char* str; + unsigned int strLength; + char chr; +}; + +static const Entity entity[NUM_ENTITY] = { + { "<", 4, '<' }, { "&", 5, '&' }, { ">", 4, '>' }, { """, 6, '\"' }, { "'", 6, '\'' } +}; + +class MyFastXml : public physx::shdfnd::FastXml +{ + public: + enum CharType + { + CT_DATA, + CT_EOF, + CT_SOFT, + CT_END_OF_ELEMENT, // either a forward slash or a greater than symbol + CT_END_OF_LINE + }; + + MyFastXml(Callback* c) + { + mStreamFromMemory = true; + mCallback = c; + memset(mTypes, CT_DATA, sizeof(mTypes)); + mTypes[0] = CT_EOF; + mTypes[uint8_t(' ')] = mTypes[uint8_t('\t')] = CT_SOFT; + mTypes[uint8_t('/')] = mTypes[uint8_t('>')] = mTypes[uint8_t('?')] = CT_END_OF_ELEMENT; + mTypes[uint8_t('\n')] = mTypes[uint8_t('\r')] = CT_END_OF_LINE; + mError = 0; + mStackIndex = 0; + mFileBuf = NULL; + mReadBufferEnd = NULL; + mReadBuffer = NULL; + mReadBufferSize = DEFAULT_READ_BUFFER_SIZE; + mOpenCount = 0; + mLastReadLoc = 0; + for(uint32_t i = 0; i < (MAX_STACK + 1); i++) + { + mStack[i] = NULL; + mStackAllocated[i] = false; + } + } + + char* processClose(char c, const char* element, char* scan, int32_t argc, const char** argv, + FastXml::Callback* iface, bool& isError) + { + AttributePairs attr(argc, argv); + isError = true; // by default, if we return null it's due to an error. + if(c == '/' || c == '?') + { + char* slash = const_cast(static_cast(strchr(element, c))); + if(slash) + *slash = 0; + + if(c == '?' && strcmp(element, "xml") == 0) + { + if(!iface->processXmlDeclaration(attr, 0, mLineNo)) + return NULL; + } + else + { + if(!iface->processElement(element, 0, attr, mLineNo)) + { + mError = "User aborted the parsing process"; + return NULL; + } + + pushElement(element); + + const char* close = popElement(); + + if(!iface->processClose(close, mStackIndex, isError)) + { + return NULL; + } + } + + if(!slash) + ++scan; + } + else + { + scan = skipNextData(scan); + char* data = scan; // this is the data portion of the element, only copies memory if we encounter line feeds + char* dest_data = 0; + while(*scan && *scan != '<') + { + if(getCharType(scan) == CT_END_OF_LINE) + { + if(*scan == '\r') + mLineNo++; + dest_data = scan; + *dest_data++ = ' '; // replace the linefeed with a space... + scan = skipNextData(scan); + while(*scan && *scan != '<') + { + if(getCharType(scan) == CT_END_OF_LINE) + { + if(*scan == '\r') + mLineNo++; + *dest_data++ = ' '; // replace the linefeed with a space... + scan = skipNextData(scan); + } + else + { + *dest_data++ = *scan++; + } + } + break; + } + else if('&' == *scan) + { + dest_data = scan; + while(*scan && *scan != '<') + { + if('&' == *scan) + { + if(*(scan + 1) && *(scan + 1) == '#' && *(scan + 2)) + { + if(*(scan + 2) == 'x') + { + // Hexadecimal. + if(!*(scan + 3)) + break; + + char* q = scan + 3; + q = strchr(q, ';'); + + if(!q || !*q) + PX_ASSERT(0); + + --q; + char ch = char(*q > '9' ? (tolower(*q) - 'a' + 10) : *q - '0'); + if(*(--q) != tolower('x')) + ch |= char(*q > '9' ? (tolower(*q) - 'a' + 10) : *q - '0') << 4; + + *dest_data++ = ch; + } + else + { + // Decimal. + if(!*(scan + 2)) + break; + + const char* q = scan + 2; + q = strchr(q, ';'); + + if(!q || !*q) + PX_ASSERT(0); + + --q; + char ch = *q - '0'; + if(*(--q) != '#') + ch |= (*q - '0') * 10; + + *dest_data++ = ch; + } + + char* start = scan; + char* end = strchr(start, ';'); + if(end) + { + *end = 0; + scan = end + 1; + } + + continue; + } + + for(int i = 0; i < NUM_ENTITY; ++i) + { + if(strncmp(entity[i].str, scan, entity[i].strLength) == 0) + { + *dest_data++ = entity[i].chr; + scan += entity[i].strLength; + break; + } + } + } + else + { + *dest_data++ = *scan++; + } + } + break; + } + else + ++scan; + } + + if(*scan == '<') + { + if(scan[1] != '/') + { + PX_ASSERT(mOpenCount > 0); + mOpenCount--; + } + if(dest_data) + { + *dest_data = 0; + } + else + { + *scan = 0; + } + + scan++; // skip it.. + + if(*data == 0) + data = 0; + + if(!iface->processElement(element, data, attr, mLineNo)) + { + mError = "User aborted the parsing process"; + return 0; + } + + pushElement(element); + + // check for the comment use case... + if(scan[0] == '!' && scan[1] == '-' && scan[2] == '-') + { + scan += 3; + while(*scan && *scan == ' ') + ++scan; + + char* comment = scan; + char* comment_end = strstr(scan, "-->"); + if(comment_end) + { + *comment_end = 0; + scan = comment_end + 3; + if(!iface->processComment(comment)) + { + mError = "User aborted the parsing process"; + return 0; + } + } + } + else if(*scan == '/') + { + scan = processClose(scan, iface, isError); + if(scan == NULL) + { + return NULL; + } + } + } + else + { + mError = "Data portion of an element wasn't terminated properly"; + return NULL; + } + } + + if(mOpenCount < MIN_CLOSE_COUNT) + { + scan = readData(scan); + } + + return scan; + } + + char* processClose(char* scan, FastXml::Callback* iface, bool& isError) + { + const char* start = popElement(), *close = start; + if(scan[1] != '>') + { + scan++; + close = scan; + while(*scan && *scan != '>') + scan++; + *scan = 0; + } + + if(0 != strcmp(start, close)) + { + mError = "Open and closing tags do not match"; + return 0; + } + + if(!iface->processClose(close, mStackIndex, isError)) + { + // we need to set the read pointer! + uint32_t offset = uint32_t(mReadBufferEnd - scan) - 1; + uint32_t readLoc = mLastReadLoc - offset; + mFileBuf->seek(readLoc); + return NULL; + } + ++scan; + + return scan; + } + + virtual bool processXml(physx::PxInputData& fileBuf, bool streamFromMemory) + { + releaseMemory(); + mFileBuf = &fileBuf; + mStreamFromMemory = streamFromMemory; + return processXml(mCallback); + } + + // if we have finished processing the data we had pending.. + char* readData(char* scan) + { + for(uint32_t i = 0; i < (mStackIndex + 1); i++) + { + if(!mStackAllocated[i]) + { + const char* text = mStack[i]; + if(text) + { + uint32_t tlen = uint32_t(strlen(text)); + mStack[i] = static_cast(mCallback->allocate(tlen + 1)); + PxMemCopy(const_cast(static_cast(mStack[i])), text, tlen + 1); + mStackAllocated[i] = true; + } + } + } + + if(!mStreamFromMemory) + { + if(scan == NULL) + { + uint32_t seekLoc = mFileBuf->tell(); + mReadBufferSize = (mFileBuf->getLength() - seekLoc); + } + else + { + return scan; + } + } + + if(mReadBuffer == NULL) + { + mReadBuffer = static_cast(mCallback->allocate(mReadBufferSize + 1)); + } + uint32_t offset = 0; + uint32_t readLen = mReadBufferSize; + + if(scan) + { + offset = uint32_t(scan - mReadBuffer); + uint32_t copyLen = mReadBufferSize - offset; + if(copyLen) + { + PX_ASSERT(scan >= mReadBuffer); + memmove(mReadBuffer, scan, copyLen); + mReadBuffer[copyLen] = 0; + readLen = mReadBufferSize - copyLen; + } + offset = copyLen; + } + + uint32_t readCount = mFileBuf->read(&mReadBuffer[offset], readLen); + + while(readCount > 0) + { + + mReadBuffer[readCount + offset] = 0; // end of string terminator... + mReadBufferEnd = &mReadBuffer[readCount + offset]; + + const char* scan_ = &mReadBuffer[offset]; + while(*scan_) + { + if(*scan_ == '<' && scan_[1] != '/') + { + mOpenCount++; + } + scan_++; + } + + if(mOpenCount < MIN_CLOSE_COUNT) + { + uint32_t oldSize = uint32_t(mReadBufferEnd - mReadBuffer); + mReadBufferSize = mReadBufferSize * 2; + char* oldReadBuffer = mReadBuffer; + mReadBuffer = static_cast(mCallback->allocate(mReadBufferSize + 1)); + PxMemCopy(mReadBuffer, oldReadBuffer, oldSize); + mCallback->deallocate(oldReadBuffer); + offset = oldSize; + uint32_t readSize = mReadBufferSize - oldSize; + readCount = mFileBuf->read(&mReadBuffer[offset], readSize); + if(readCount == 0) + break; + } + else + { + break; + } + } + mLastReadLoc = mFileBuf->tell(); + + return mReadBuffer; + } + + bool processXml(FastXml::Callback* iface) + { + bool ret = true; + + const int MAX_ATTRIBUTE = 2048; // can't imagine having more than 2,048 attributes in a single element right? + + mLineNo = 1; + + char* element, *scan = readData(0); + + while(*scan) + { + + scan = skipNextData(scan); + + if(*scan == 0) + break; + + if(*scan == '<') + { + + if(scan[1] != '/') + { + PX_ASSERT(mOpenCount > 0); + mOpenCount--; + } + scan++; + + if(*scan == '?') // Allow xml declarations + { + scan++; + } + else if(scan[0] == '!' && scan[1] == '-' && scan[2] == '-') + { + scan += 3; + while(*scan && *scan == ' ') + scan++; + char* comment = scan, *comment_end = strstr(scan, "-->"); + if(comment_end) + { + *comment_end = 0; + scan = comment_end + 3; + if(!iface->processComment(comment)) + { + mError = "User aborted the parsing process"; + return false; + } + } + continue; + } + else if(scan[0] == '!') // Allow doctype + { + scan++; + + // DOCTYPE syntax differs from usual XML so we parse it here + + // Read DOCTYPE + const char* tag = "DOCTYPE"; + if(!strstr(scan, tag)) + { + mError = "Invalid DOCTYPE"; + return false; + } + + scan += strlen(tag); + + // Skip whites + while(CT_SOFT == getCharType(scan)) + ++scan; + + // Read rootElement + const char* rootElement = scan; + while(CT_DATA == getCharType(scan)) + ++scan; + + char* endRootElement = scan; + + // TODO: read remaining fields (fpi, uri, etc.) + while(CT_END_OF_ELEMENT != getCharType(scan++)) + ; + + *endRootElement = 0; + + if(!iface->processDoctype(rootElement, 0, 0, 0)) + { + mError = "User aborted the parsing process"; + return false; + } + + continue; // Restart loop + } + } + + if(*scan == '/') + { + bool isError; + scan = processClose(scan, iface, isError); + if(!scan) + { + if(isError) + { + mError = "User aborted the parsing process"; + } + return !isError; + } + } + else + { + if(*scan == '?') + scan++; + element = scan; + int32_t argc = 0; + const char* argv[MAX_ATTRIBUTE]; + bool close; + scan = nextSoftOrClose(scan, close); + if(close) + { + char c = *(scan - 1); + if(c != '?' && c != '/') + { + c = '>'; + } + *scan++ = 0; + bool isError; + scan = processClose(c, element, scan, argc, argv, iface, isError); + if(!scan) + { + if(isError) + { + mError = "User aborted the parsing process"; + } + return !isError; + } + } + else + { + if(*scan == 0) + { + return ret; + } + + *scan = 0; // place a zero byte to indicate the end of the element name... + scan++; + + while(*scan) + { + scan = skipNextData(scan); // advance past any soft seperators (tab or space) + + if(getCharType(scan) == CT_END_OF_ELEMENT) + { + char c = *scan++; + if('?' == c) + { + if('>' != *scan) //?> + { + PX_ASSERT(0); + return false; + } + + scan++; + } + bool isError; + scan = processClose(c, element, scan, argc, argv, iface, isError); + if(!scan) + { + if(isError) + { + mError = "User aborted the parsing process"; + } + return !isError; + } + break; + } + else + { + if(argc >= MAX_ATTRIBUTE) + { + mError = "encountered too many attributes"; + return false; + } + argv[argc] = scan; + scan = nextSep(scan); // scan up to a space, or an equal + if(*scan) + { + if(*scan != '=') + { + *scan = 0; + scan++; + while(*scan && *scan != '=') + scan++; + if(*scan == '=') + scan++; + } + else + { + *scan = 0; + scan++; + } + + if(*scan) // if not eof... + { + scan = skipNextData(scan); + if(*scan == '"') + { + scan++; + argc++; + argv[argc] = scan; + argc++; + while(*scan && *scan != 34) + scan++; + if(*scan == '"') + { + *scan = 0; + scan++; + } + else + { + mError = "Failed to find closing quote for attribute"; + return false; + } + } + else + { + // mError = "Expected quote to begin attribute"; + // return false; + // PH: let's try to have a more graceful fallback + argc--; + while(*scan != '/' && *scan != '>' && *scan != 0) + scan++; + } + } + } // if( *scan ) + } // if ( mTypes[*scan] + } // if( close ) + } // if( *scan == '/' + } // while( *scan ) + } + + if(mStackIndex) + { + mError = "Invalid file format"; + return false; + } + + return ret; + } + + const char* getError(int32_t& lineno) + { + const char* ret = mError; + lineno = mLineNo; + mError = 0; + return ret; + } + + virtual void release(void) + { + Callback* c = mCallback; // get the user allocator interface + MyFastXml* f = this; // cast the this pointer + f->~MyFastXml(); // explicitely invoke the destructor for this class + c->deallocate(f); // now free up the memory associated with it. + } + + private: + virtual ~MyFastXml(void) + { + releaseMemory(); + } + + PX_INLINE void releaseMemory(void) + { + mFileBuf = NULL; + mCallback->deallocate(mReadBuffer); + mReadBuffer = NULL; + mStackIndex = 0; + mReadBufferEnd = NULL; + mOpenCount = 0; + mLastReadLoc = 0; + mError = NULL; + for(uint32_t i = 0; i < (mStackIndex + 1); i++) + { + if(mStackAllocated[i]) + { + mCallback->deallocate(const_cast(static_cast(mStack[i]))); + mStackAllocated[i] = false; + } + mStack[i] = NULL; + } + } + + PX_INLINE CharType getCharType(char* scan) const + { + return mTypes[uint8_t(*scan)]; + } + + PX_INLINE char* nextSoftOrClose(char* scan, bool& close) + { + while(*scan && getCharType(scan) != CT_SOFT && *scan != '>') + scan++; + close = *scan == '>'; + return scan; + } + + PX_INLINE char* nextSep(char* scan) + { + while(*scan && getCharType(scan) != CT_SOFT && *scan != '=') + scan++; + return scan; + } + + PX_INLINE char* skipNextData(char* scan) + { + // while we have data, and we encounter soft seperators or line feeds... + while(*scan && (getCharType(scan) == CT_SOFT || getCharType(scan) == CT_END_OF_LINE)) + { + if(*scan == '\n') + mLineNo++; + scan++; + } + return scan; + } + + void pushElement(const char* element) + { + PX_ASSERT(mStackIndex < uint32_t(MAX_STACK)); + if(mStackIndex < uint32_t(MAX_STACK)) + { + if(mStackAllocated[mStackIndex]) + { + mCallback->deallocate(const_cast(static_cast(mStack[mStackIndex]))); + mStackAllocated[mStackIndex] = false; + } + mStack[mStackIndex++] = element; + } + } + + const char* popElement(void) + { + PX_ASSERT(mStackIndex > 0); + if(mStackAllocated[mStackIndex]) + { + mCallback->deallocate(const_cast(static_cast(mStack[mStackIndex]))); + mStackAllocated[mStackIndex] = false; + } + mStack[mStackIndex] = NULL; + return mStackIndex ? mStack[--mStackIndex] : NULL; + } + + static const int MAX_STACK = 2048; + + CharType mTypes[256]; + + physx::PxInputData* mFileBuf; + + char* mReadBuffer; + char* mReadBufferEnd; + + uint32_t mOpenCount; + uint32_t mReadBufferSize; + uint32_t mLastReadLoc; + + int32_t mLineNo; + const char* mError; + uint32_t mStackIndex; + const char* mStack[MAX_STACK + 1]; + bool mStreamFromMemory; + bool mStackAllocated[MAX_STACK + 1]; + Callback* mCallback; +}; +} + +namespace physx +{ +namespace shdfnd +{ + +FastXml* createFastXml(FastXml::Callback* iface) +{ + MyFastXml* m = static_cast(iface->allocate(sizeof(MyFastXml))); + if(m) + { + new (m) MyFastXml(iface); + } + return static_cast(m); +} +} +} diff --git a/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.h b/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.h new file mode 100644 index 000000000..5f74c07f8 --- /dev/null +++ b/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PSFILEBUFFER_PSASCIICONVERSION_H +#define PSFILEBUFFER_PSASCIICONVERSION_H + +/*! +\file +\brief PxAsciiConversion namespace contains string/value helper functions +*/ + +#include "PxMath.h" +#include "PsString.h" +#include +#include +#include +#include +#include + +namespace physx +{ +namespace general_string_parsing2 +{ +namespace PxAsc +{ + +const uint32_t PxF32StrLen = 24; +const uint32_t PxF64StrLen = 32; +const uint32_t IntStrLen = 32; + +PX_INLINE bool isWhiteSpace(char c); +PX_INLINE const char * skipNonWhiteSpace(const char *scan); +PX_INLINE const char * skipWhiteSpace(const char *scan); + +////////////////////////// +// str to value functions +////////////////////////// +PX_INLINE bool strToBool(const char *str, const char **endptr); +PX_INLINE int8_t strToI8(const char *str, const char **endptr); +PX_INLINE int16_t strToI16(const char *str, const char **endptr); +PX_INLINE int32_t strToI32(const char *str, const char **endptr); +PX_INLINE int64_t strToI64(const char *str, const char **endptr); +PX_INLINE uint8_t strToU8(const char *str, const char **endptr); +PX_INLINE uint16_t strToU16(const char *str, const char **endptr); +PX_INLINE uint32_t strToU32(const char *str, const char **endptr); +PX_INLINE uint64_t strToU64(const char *str, const char **endptr); +PX_INLINE float strToF32(const char *str, const char **endptr); +PX_INLINE double strToF64(const char *str, const char **endptr); +PX_INLINE void strToF32s(float *v,uint32_t count,const char *str, const char**endptr); + + +////////////////////////// +// value to str functions +////////////////////////// +PX_INLINE const char * valueToStr( bool val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( int8_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( int16_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( int32_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( int64_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( uint8_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( uint16_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( uint32_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( uint64_t val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( float val, char *buf, uint32_t n ); +PX_INLINE const char * valueToStr( double val, char *buf, uint32_t n ); + +#include "PsAsciiConversion.inl" + +} // end of namespace +} // end of namespace +using namespace general_string_parsing2; +} // end of namespace + + +#endif // PSFILEBUFFER_PSASCIICONVERSION_H diff --git a/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.inl b/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.inl new file mode 100644 index 000000000..aa84e84a7 --- /dev/null +++ b/src/PhysX/physx/source/filebuf/include/PsAsciiConversion.inl @@ -0,0 +1,566 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +/*! +\file +\brief NvAsciiConversion namespace contains string/value helper functions +*/ + +#include + +PX_INLINE bool isWhiteSpace(char c) +{ + bool ret = false; + if ( c == 32 || c == 9 || c == 13 || c == 10 || c == ',' ) ret = true; + return ret; +} + +PX_INLINE const char * skipNonWhiteSpace(const char *scan) +{ + while ( !isWhiteSpace(*scan) && *scan) scan++; + if ( *scan == 0 ) scan = NULL; + return scan; +} +PX_INLINE const char * skipWhiteSpace(const char *scan) +{ + while ( isWhiteSpace(*scan) && *scan ) scan++; + if ( *scan == 0 ) scan = NULL; + return scan; +} + +static double strtod_fast(const char * pString) +{ + //--- + // Find the start of the string + const char* pNumberStart = skipWhiteSpace(pString); + + //--- + // Find the end of the string + const char* pNumberEnd = pNumberStart; + + // skip optional sign + if( *pNumberEnd == '-' || *pNumberEnd == '+' ) + ++pNumberEnd; + + // skip optional digits + while( isdigit(*pNumberEnd) ) + ++pNumberEnd; + + // skip optional decimal and digits + if( *pNumberEnd == '.' ) + { + ++pNumberEnd; + + while( isdigit(*pNumberEnd) ) + ++pNumberEnd; + } + + // skip optional exponent + if( *pNumberEnd == 'd' + || *pNumberEnd == 'D' + || *pNumberEnd == 'e' + || *pNumberEnd == 'E' ) + { + ++pNumberEnd; + + if( *pNumberEnd == '-' || *pNumberEnd == '+' ) + ++pNumberEnd; + + while( isdigit(*pNumberEnd) ) + ++pNumberEnd; + } + + //--- + // Process the string + const uint32_t numberLen = (const uint32_t)(pNumberEnd-pNumberStart); + char buffer[32]; + if( numberLen+1 < sizeof(buffer)/sizeof(buffer[0]) ) + { + // copy into buffer and terminate with NUL before calling the + // standard function + memcpy( buffer, pNumberStart, numberLen*sizeof(buffer[0]) ); + buffer[numberLen] = '\0'; + const double result = strtod( buffer, NULL ); + + return result; + } + else + { + // buffer was too small so just call the standard function on the + // source input to get a proper result + return strtod( pString, NULL ); + } +} + +static float strtof_fast(const char* pString) +{ + return (float)strtod_fast(pString); +} + + +////////////////////////// +// str to value functions +////////////////////////// +PX_INLINE bool strToBool(const char *str, const char **endptr) +{ + bool ret = false; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + size_t len = (size_t)(end - begin); + if ( physx::shdfnd::strnicmp(begin,"true", len) == 0 || physx::shdfnd::strnicmp(begin,"1", len) == 0 ) + ret = true; + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE int8_t strToI8(const char *str, const char **endptr) +{ + int8_t ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + if( strncmp(begin, "INT8_MIN", (size_t)(end-begin)) == 0) + ret = INT8_MIN; + else if( strncmp(begin, "INT8_MAX", (size_t)(end-begin)) == 0) + ret = INT8_MAX; + else if( strncmp(begin, "PX_MIN_I8", (size_t)(end-begin)) == 0) + ret = INT8_MIN; + else if( strncmp(begin, "PX_MAX_I8", (size_t)(end-begin)) == 0) + ret = INT8_MAX; + else + ret = (int8_t)strtol(begin, 0, 0); //FIXME + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE int16_t strToI16(const char *str, const char **endptr) +{ + int16_t ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + if( strncmp(begin, "INT16_MIN", (size_t)(end-begin)) == 0) + ret = INT16_MIN; + else if( strncmp(begin, "INT16_MAX", (size_t)(end-begin)) == 0) + ret = INT16_MAX; + else if( strncmp(begin, "PX_MIN_I16", (size_t)(end-begin)) == 0) + ret = INT16_MIN; + else if( strncmp(begin, "PX_MAX_I16", (size_t)(end-begin)) == 0) + ret = INT16_MAX; + else + ret = (int16_t)strtol(begin, 0, 0); //FIXME + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE int32_t strToI32(const char *str, const char **endptr) +{ + int32_t ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + if( strncmp(begin, "INT32_MIN", (size_t)(end-begin)) == 0) + ret = INT32_MIN; + else if( strncmp(begin, "INT32_MAX", (size_t)(end-begin)) == 0) + ret = INT32_MAX; + else if( strncmp(begin, "PX_MIN_I32", (size_t)(end-begin)) == 0) + ret = INT32_MIN; + else if( strncmp(begin, "PX_MAX_I32", (size_t)(end-begin)) == 0) + ret = INT32_MAX; + else + ret = (int32_t)strtol(begin, 0, 0); //FIXME + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE int64_t strToI64(const char *str, const char **endptr) +{ + int64_t ret; + const char *begin = skipWhiteSpace(str); + + //FIXME +#ifdef _WIN32 //NV_WINDOWS, NV_XBOX + ret = (int64_t)_strtoi64(begin,0,10); +#else + ret = (int64_t)strtoll(begin,0,10); +#endif + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE uint8_t strToU8(const char *str, const char **endptr) +{ + uint8_t ret; + const char *begin = skipWhiteSpace(str); + + ret = (uint8_t)strtoul(begin, 0, 0); + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE uint16_t strToU16(const char *str, const char **endptr) +{ + uint16_t ret; + const char *end; + const char *begin = skipWhiteSpace(str); + + end = skipNonWhiteSpace(begin); + if( !end ) + end = begin + strlen(str); + + if( strncmp(begin, "UINT16_MAX", (size_t)(end-begin)) == 0) + ret = UINT16_MAX; + else if( strncmp(begin, "PX_MAX_U16", (size_t)(end-begin)) == 0) + ret = UINT16_MAX; + else + ret = (uint16_t)strtoul(begin,0,0); + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE uint32_t strToU32(const char *str, const char **endptr) +{ + uint32_t ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + if( strncmp(begin, "UINT32_MAX", (size_t)(end-begin)) == 0) + ret = UINT32_MAX; + else if( strncmp(begin, "PX_U32_MAX", (size_t)(end-begin)) == 0) + ret = UINT32_MAX; + else + ret = (uint32_t)strtoul(begin,0,0); + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE uint64_t strToU64(const char *str, const char **endptr) +{ + uint64_t ret; + const char *begin; + begin = skipWhiteSpace(str); + + //FIXME +#ifdef _WIN32 //NV_WINDOWS, NV_XBOX + ret = (uint64_t)_strtoui64(begin,0,10); +#else + ret = (uint64_t)strtoull(begin,0,10); +#endif + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +#ifndef DEBUGGING_MISMATCHES +#define DEBUGGING_MISMATCHES 0 +#endif + +PX_INLINE float strToF32(const char *str, const char **endptr) +{ + float ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + const uint32_t len = (uint32_t)(end - begin); + + const char F32_MIN[] = "NV_MIN_F32"; + const char F32_MAX[] = "NV_MAX_F32"; + const char PX_F32_MIN[] = "PX_MIN_F32"; + const char PX_F32_MAX[] = "PX_MAX_F32"; + + if( strncmp(begin, PX_F32_MIN, physx::PxMin(len, (uint32_t)(sizeof(PX_F32_MIN) - 1))) == 0) + ret = -PX_MAX_F32; + else if( strncmp(begin, PX_F32_MAX, physx::PxMin(len, (uint32_t)(sizeof(PX_F32_MAX) - 1))) == 0) + ret = PX_MAX_F32; + else if( strncmp(begin, F32_MIN, physx::PxMin(len, (uint32_t)(sizeof(F32_MIN) - 1))) == 0) + ret = -PX_MAX_F32; + else if( strncmp(begin, F32_MAX, physx::PxMin(len, (uint32_t)(sizeof(F32_MAX) - 1))) == 0) + ret = PX_MAX_F32; + else + { + ret = (float)strtof_fast(begin); + } + +#if DEBUGGING_MISMATCHES + float testRet = (float)atof(begin); + if( ret != testRet ) + { + PX_ASSERT(0 && "Inaccurate float string"); + } +#endif + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + + +PX_INLINE double strToF64(const char *str, const char **endptr) +{ + double ret; + const char *begin = skipWhiteSpace(str); + const char *end = skipNonWhiteSpace(begin); + + end = skipNonWhiteSpace(begin); + + if( !end ) + end = begin + strlen(str); + + const uint32_t len = (const uint32_t)(end - begin); + + const char F64_MIN[] = "PX_MIN_F364"; + const char F64_MAX[] = "PX_MAX_F64"; + const char PX_F64_MIN[] = "PX_MIN_F64"; + const char PX_F64_MAX[] = "PX_MAX_F64"; + + if( strncmp(begin, F64_MIN, physx::PxMin(len, (uint32_t)(sizeof(F64_MIN) - 1))) == 0) + ret = -PX_MAX_F64; + else if( strncmp(begin, F64_MAX, physx::PxMin(len, (uint32_t)(sizeof(F64_MAX) - 1))) == 0) + ret = PX_MAX_F64; + else if( strncmp(begin, PX_F64_MIN, physx::PxMin(len, (uint32_t)(sizeof(PX_F64_MIN) - 1))) == 0) + ret = -PX_MAX_F64; + else if( strncmp(begin, PX_F64_MAX, physx::PxMin(len, (uint32_t)(sizeof(PX_F64_MAX) - 1))) == 0) + ret = PX_MAX_F64; + else + ret = (double)strtod_fast(begin); + + if( endptr ) + *endptr = skipNonWhiteSpace(begin); + + return ret; +} + +PX_INLINE void strToF32s(float *v,uint32_t count,const char *str, const char**endptr) +{ + const char *begin = skipWhiteSpace(str); + + if ( *begin == '(' ) begin++; + for (uint32_t i=0; i + +namespace physx +{ +namespace general_PxIOStream2 +{ + using namespace shdfnd; + +//Use this class if you want to use your own allocator +class PxFileBufferBase : public PxFileBuf +{ +public: + PxFileBufferBase(const char *fileName,OpenMode mode) + { + mOpenMode = mode; + mFph = NULL; + mFileLength = 0; + mSeekRead = 0; + mSeekWrite = 0; + mSeekCurrent = 0; + switch ( mode ) + { + case OPEN_READ_ONLY: + mFph = fopen(fileName,"rb"); + break; + case OPEN_WRITE_ONLY: + mFph = fopen(fileName,"wb"); + break; + case OPEN_READ_WRITE_NEW: + mFph = fopen(fileName,"wb+"); + break; + case OPEN_READ_WRITE_EXISTING: + mFph = fopen(fileName,"rb+"); + break; + case OPEN_FILE_NOT_FOUND: + break; + } + if ( mFph ) + { + fseek(mFph,0L,SEEK_END); + mFileLength = static_cast(ftell(mFph)); + fseek(mFph,0L,SEEK_SET); + } + else + { + mOpenMode = OPEN_FILE_NOT_FOUND; + } + } + + virtual ~PxFileBufferBase() + { + close(); + } + + virtual void close() + { + if( mFph ) + { + fclose(mFph); + mFph = 0; + } + } + + virtual SeekType isSeekable(void) const + { + return mSeekType; + } + + virtual uint32_t read(void* buffer, uint32_t size) + { + uint32_t ret = 0; + if ( mFph ) + { + setSeekRead(); + ret = static_cast(::fread(buffer,1,size,mFph)); + mSeekRead+=ret; + mSeekCurrent+=ret; + } + return ret; + } + + virtual uint32_t peek(void* buffer, uint32_t size) + { + uint32_t ret = 0; + if ( mFph ) + { + uint32_t loc = tellRead(); + setSeekRead(); + ret = static_cast(::fread(buffer,1,size,mFph)); + mSeekCurrent+=ret; + seekRead(loc); + } + return ret; + } + + virtual uint32_t write(const void* buffer, uint32_t size) + { + uint32_t ret = 0; + if ( mFph ) + { + setSeekWrite(); + ret = static_cast(::fwrite(buffer,1,size,mFph)); + mSeekWrite+=ret; + mSeekCurrent+=ret; + if ( mSeekWrite > mFileLength ) + { + mFileLength = mSeekWrite; + } + } + return ret; + } + + virtual uint32_t tellRead(void) const + { + return mSeekRead; + } + + virtual uint32_t tellWrite(void) const + { + return mSeekWrite; + } + + virtual uint32_t seekRead(uint32_t loc) + { + mSeekRead = loc; + if ( mSeekRead > mFileLength ) + { + mSeekRead = mFileLength; + } + return mSeekRead; + } + + virtual uint32_t seekWrite(uint32_t loc) + { + mSeekWrite = loc; + if ( mSeekWrite > mFileLength ) + { + mSeekWrite = mFileLength; + } + return mSeekWrite; + } + + virtual void flush(void) + { + if ( mFph ) + { + ::fflush(mFph); + } + } + + virtual OpenMode getOpenMode(void) const + { + return mOpenMode; + } + + virtual uint32_t getFileLength(void) const + { + return mFileLength; + } + +private: + // Moves the actual file pointer to the current read location + void setSeekRead(void) + { + if ( mSeekRead != mSeekCurrent && mFph ) + { + if ( mSeekRead >= mFileLength ) + { + fseek(mFph,0L,SEEK_END); + } + else + { + fseek(mFph,static_cast(mSeekRead),SEEK_SET); + } + mSeekCurrent = mSeekRead = static_cast(ftell(mFph)); + } + } + // Moves the actual file pointer to the current write location + void setSeekWrite(void) + { + if ( mSeekWrite != mSeekCurrent && mFph ) + { + if ( mSeekWrite >= mFileLength ) + { + fseek(mFph,0L,SEEK_END); + } + else + { + fseek(mFph,static_cast(mSeekWrite),SEEK_SET); + } + mSeekCurrent = mSeekWrite = static_cast(ftell(mFph)); + } + } + + + FILE *mFph; + uint32_t mSeekRead; + uint32_t mSeekWrite; + uint32_t mSeekCurrent; + uint32_t mFileLength; + SeekType mSeekType; + OpenMode mOpenMode; +}; + +//Use this class if you want to use PhysX memory allocator +class PsFileBuffer: public PxFileBufferBase, public UserAllocated +{ +public: + PsFileBuffer(const char *fileName,OpenMode mode): PxFileBufferBase(fileName, mode) {} +}; + +} +using namespace general_PxIOStream2; +} + +#endif // PSFILEBUFFER_PSFILEBUFFER_H diff --git a/src/PhysX/physx/source/filebuf/include/PsIOStream.h b/src/PhysX/physx/source/filebuf/include/PsIOStream.h new file mode 100644 index 000000000..e32330c91 --- /dev/null +++ b/src/PhysX/physx/source/filebuf/include/PsIOStream.h @@ -0,0 +1,137 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PSFILEBUFFER_PSIOSTREAM_H +#define PSFILEBUFFER_PSIOSTREAM_H + +/*! +\file +\brief PsIOStream class +*/ +#include "filebuf/PxFileBuf.h" + +#include "Ps.h" +#include "PsString.h" +#include +#include +#include "PsAsciiConversion.h" + +#define safePrintf physx::shdfnd::snprintf + +PX_PUSH_PACK_DEFAULT + +namespace physx +{ + namespace general_PxIOStream2 + { + +/** +\brief A wrapper class for physx::PxFileBuf that provides both binary and ASCII streaming capabilities +*/ +class PsIOStream +{ + static const uint32_t MAX_STREAM_STRING = 1024; +public: + /** + \param [in] stream the physx::PxFileBuf through which all reads and writes will be performed + \param [in] streamLen the length of the input data stream when de-serializing + */ + PsIOStream(physx::PxFileBuf &stream,uint32_t streamLen) : mBinary(true), mStreamLen(streamLen), mStream(stream) { } + ~PsIOStream(void) { } + + /** + \brief Set the stream to binary or ASCII + + \param [in] state if true, stream is binary, if false, stream is ASCII + + If the stream is binary, stream access is passed straight through to the respecitve + physx::PxFileBuf methods. If the stream is ASCII, all stream reads and writes are converted to + human readable ASCII. + */ + PX_INLINE void setBinary(bool state) { mBinary = state; } + PX_INLINE bool getBinary() { return mBinary; } + + PX_INLINE PsIOStream& operator<<(bool v); + PX_INLINE PsIOStream& operator<<(char c); + PX_INLINE PsIOStream& operator<<(uint8_t v); + PX_INLINE PsIOStream& operator<<(int8_t v); + + PX_INLINE PsIOStream& operator<<(const char *c); + PX_INLINE PsIOStream& operator<<(int64_t v); + PX_INLINE PsIOStream& operator<<(uint64_t v); + PX_INLINE PsIOStream& operator<<(double v); + PX_INLINE PsIOStream& operator<<(float v); + PX_INLINE PsIOStream& operator<<(uint32_t v); + PX_INLINE PsIOStream& operator<<(int32_t v); + PX_INLINE PsIOStream& operator<<(uint16_t v); + PX_INLINE PsIOStream& operator<<(int16_t v); + PX_INLINE PsIOStream& operator<<(const physx::PxVec3 &v); + PX_INLINE PsIOStream& operator<<(const physx::PxQuat &v); + PX_INLINE PsIOStream& operator<<(const physx::PxBounds3 &v); + + PX_INLINE PsIOStream& operator>>(const char *&c); + PX_INLINE PsIOStream& operator>>(bool &v); + PX_INLINE PsIOStream& operator>>(char &c); + PX_INLINE PsIOStream& operator>>(uint8_t &v); + PX_INLINE PsIOStream& operator>>(int8_t &v); + PX_INLINE PsIOStream& operator>>(int64_t &v); + PX_INLINE PsIOStream& operator>>(uint64_t &v); + PX_INLINE PsIOStream& operator>>(double &v); + PX_INLINE PsIOStream& operator>>(float &v); + PX_INLINE PsIOStream& operator>>(uint32_t &v); + PX_INLINE PsIOStream& operator>>(int32_t &v); + PX_INLINE PsIOStream& operator>>(uint16_t &v); + PX_INLINE PsIOStream& operator>>(int16_t &v); + PX_INLINE PsIOStream& operator>>(physx::PxVec3 &v); + PX_INLINE PsIOStream& operator>>(physx::PxQuat &v); + PX_INLINE PsIOStream& operator>>(physx::PxBounds3 &v); + + uint32_t getStreamLen(void) const { return mStreamLen; } + + physx::PxFileBuf& getStream(void) { return mStream; } + + PX_INLINE void storeString(const char *c,bool zeroTerminate=false); + +private: + PsIOStream& operator=( const PsIOStream& ); + + + bool mBinary; // true if we are serializing binary data. Otherwise, everything is assumed converted to ASCII + uint32_t mStreamLen; // the length of the input data stream when de-serializing. + physx::PxFileBuf &mStream; + char mReadString[MAX_STREAM_STRING]; // a temp buffer for streaming strings on input. +}; + +#include "PsIOStream.inl" // inline methods... + + } // end of namespace + using namespace general_PxIOStream2; +} // end of physx namespace + +PX_POP_PACK + +#endif // PSFILEBUFFER_PSIOSTREAM_H diff --git a/src/PhysX/physx/source/filebuf/include/PsIOStream.inl b/src/PhysX/physx/source/filebuf/include/PsIOStream.inl new file mode 100644 index 000000000..74f7e2066 --- /dev/null +++ b/src/PhysX/physx/source/filebuf/include/PsIOStream.inl @@ -0,0 +1,451 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + + +/* + * Copyright 2009-2018 NVIDIA Corporation. All rights reserved. + * + * NOTICE TO USER: + * + * This source code is subject to NVIDIA ownership rights under U.S. and + * international Copyright laws. Users and possessors of this source code + * are hereby granted a nonexclusive, royalty-free license to use this code + * in individual and commercial software. + * + * NVIDIA MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS SOURCE + * CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR + * IMPLIED WARRANTY OF ANY KIND. NVIDIA DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOURCE CODE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. + * IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL, + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE + * OR PERFORMANCE OF THIS SOURCE CODE. + * + * U.S. Government End Users. This source code is a "commercial item" as + * that term is defined at 48 C.F.R. 2.101 (OCT 1995), consisting of + * "commercial computer software" and "commercial computer software + * documentation" as such terms are used in 48 C.F.R. 12.212 (SEPT 1995) + * and is provided to the U.S. Government only as a commercial end item. + * Consistent with 48 C.F.R.12.212 and 48 C.F.R. 227.7202-1 through + * 227.7202-4 (JUNE 1995), all U.S. Government End Users acquire the + * source code with only those rights set forth herein. + * + * Any use of this source code in individual and commercial software must + * include, in the user documentation and internal comments to the code, + * the above Disclaimer and U.S. Government End Users Notice. + */ + +/*! +\file +\brief PsIOStream inline implementation +*/ + +PX_INLINE PsIOStream& PsIOStream::operator<<(bool v) +{ + if ( mBinary ) + { + mStream.storeByte((uint8_t)v); + } + else + { + char scratch[6]; + storeString( physx::PxAsc::valueToStr(v, scratch, 6) ); + } + return *this; +} + + +PX_INLINE PsIOStream& PsIOStream::operator<<(char c) +{ + mStream.storeByte((uint8_t)c); + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(uint8_t c) +{ + if ( mBinary ) + { + mStream.storeByte((uint8_t)c); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(c, scratch, physx::PxAsc::IntStrLen) ); + } + + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(int8_t c) +{ + if ( mBinary ) + { + mStream.storeByte((uint8_t)c); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(c, scratch, physx::PxAsc::IntStrLen) ); + } + + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(const char *c) +{ + if ( mBinary ) + { + c = c ? c : ""; // it it is a null pointer, assign it to an empty string. + uint32_t len = (uint32_t)strlen(c); + PX_ASSERT( len < (MAX_STREAM_STRING-1)); + if ( len > (MAX_STREAM_STRING-1) ) + { + len = MAX_STREAM_STRING-1; + } + mStream.storeDword(len); + if ( len ) + mStream.write(c,len); + } + else + { + storeString(c); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(uint64_t v) +{ + if ( mBinary ) + { + mStream.storeDouble( (double) v ); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(int64_t v) +{ + if ( mBinary ) + { + mStream.storeDouble( (double) v ); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(double v) +{ + if ( mBinary ) + { + mStream.storeDouble( (double) v ); + } + else + { + char scratch[physx::PxAsc::PxF64StrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::PxF64StrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(float v) +{ + if ( mBinary ) + { + mStream.storeFloat(v); + } + else + { + char scratch[physx::PxAsc::PxF32StrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::PxF32StrLen) ); + + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(uint32_t v) +{ + if ( mBinary ) + { + mStream.storeDword(v); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(int32_t v) +{ + if ( mBinary ) + { + mStream.storeDword( (uint32_t) v ); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(uint16_t v) +{ + if ( mBinary ) + { + mStream.storeWord(v); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(int16_t v) +{ + if ( mBinary ) + { + mStream.storeWord( (uint16_t) v ); + } + else + { + char scratch[physx::PxAsc::IntStrLen]; + storeString( physx::PxAsc::valueToStr(v, scratch, physx::PxAsc::IntStrLen) ); + } + return *this; +} + + +PX_INLINE PsIOStream& PsIOStream::operator>>(uint32_t &v) +{ + if ( mBinary ) + { + v = mStream.readDword(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(char &v) +{ + if ( mBinary ) + { + v = (char)mStream.readByte(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(uint8_t &v) +{ + if ( mBinary ) + { + v = mStream.readByte(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(int8_t &v) +{ + if ( mBinary ) + { + v = (int8_t)mStream.readByte(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(int64_t &v) +{ + if ( mBinary ) + { + v = mStream.readDword(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(uint64_t &v) +{ + if ( mBinary ) + { + v = (uint64_t)mStream.readDouble(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(double &v) +{ + if ( mBinary ) + { + v = mStream.readDouble(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(float &v) +{ + if ( mBinary ) + { + v = mStream.readFloat(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(int32_t &v) +{ + if ( mBinary ) + { + v = (int32_t)mStream.readDword(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(uint16_t &v) +{ + if ( mBinary ) + { + v = mStream.readWord(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(int16_t &v) +{ + if ( mBinary ) + { + v = (int16_t)mStream.readWord(); + } + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(bool &v) +{ + int8_t iv; + iv = (int8_t)mStream.readByte(); + v = iv ? true : false; + return *this; +} + +#define NX_IOSTREAM_COMMA_SEPARATOR if(!mBinary) *this << ' '; + +PX_INLINE PsIOStream& PsIOStream::operator<<(const physx::PxVec3 &v) +{ + *this << v.x; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.y; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.z; + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator<<(const physx::PxQuat &v) +{ + *this << v.x; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.y; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.z; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.w; + return *this; +} + + +PX_INLINE PsIOStream& PsIOStream::operator<<(const physx::PxBounds3 &v) +{ + *this << v.minimum; + NX_IOSTREAM_COMMA_SEPARATOR; + *this << v.maximum; + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(physx::PxVec3 &v) +{ + *this >> v.x; + *this >> v.y; + *this >> v.z; + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(physx::PxQuat &v) +{ + *this>>v.x; + *this>>v.y; + *this>>v.z; + *this>>v.w; + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(physx::PxBounds3 &v) +{ + *this >> v.minimum; + *this >> v.maximum; + return *this; +} + +PX_INLINE PsIOStream& PsIOStream::operator>>(const char *&str) +{ + str = NULL; // by default no string streamed... + if ( mBinary ) + { + uint32_t len=0; + *this >> len; + + PX_ASSERT( len < (MAX_STREAM_STRING-1) ); + if ( len < (MAX_STREAM_STRING-1) ) + { + mStream.read(mReadString,len); + mReadString[len] = 0; + str = mReadString; + } + } + return *this; +} + + +PX_INLINE void PsIOStream::storeString(const char *c,bool zeroTerminate) +{ + while ( *c ) + { + mStream.storeByte((uint8_t)*c); + c++; + } + if ( zeroTerminate ) + { + mStream.storeByte(0); + } +} diff --git a/src/PhysX/physx/source/filebuf/include/PsMemoryBuffer.h b/src/PhysX/physx/source/filebuf/include/PsMemoryBuffer.h new file mode 100644 index 000000000..ae0bc8da5 --- /dev/null +++ b/src/PhysX/physx/source/filebuf/include/PsMemoryBuffer.h @@ -0,0 +1,449 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PSFILEBUFFER_PSMEMORYBUFFER_H +#define PSFILEBUFFER_PSMEMORYBUFFER_H + +#include "Ps.h" +#include "PsUserAllocated.h" +#include "PsAlignedMalloc.h" +#include "filebuf/PxFileBuf.h" +#include "foundation/PxAssert.h" + +namespace physx +{ +namespace general_PxIOStream2 +{ + using namespace shdfnd; + + const uint32_t BUFFER_SIZE_DEFAULT = 4096; + +//Use this class if you want to use your own allocator +template +class PxMemoryBufferBase : public PxFileBuf, public Allocator +{ + PX_NOCOPY(PxMemoryBufferBase) + void init(const void *readMem, uint32_t readLen) + { + mAllocator = this; + + mReadBuffer = mReadLoc = static_cast(readMem); + mReadStop = &mReadLoc[readLen]; + + mWriteBuffer = mWriteLoc = mWriteStop = NULL; + mWriteBufferSize = 0; + mDefaultWriteBufferSize = BUFFER_SIZE_DEFAULT; + + mOpenMode = OPEN_READ_ONLY; + mSeekType = SEEKABLE_READ; + } + + void init(uint32_t defaultWriteBufferSize) + { + mAllocator = this; + + mReadBuffer = mReadLoc = mReadStop = NULL; + + mWriteBuffer = mWriteLoc = mWriteStop = NULL; + mWriteBufferSize = 0; + mDefaultWriteBufferSize = defaultWriteBufferSize; + + mOpenMode = OPEN_READ_WRITE_NEW; + mSeekType = SEEKABLE_READWRITE; + } + +public: + PxMemoryBufferBase(const void *readMem,uint32_t readLen) + { + init(readMem, readLen); + } + + PxMemoryBufferBase(const void *readMem,uint32_t readLen, const Allocator &alloc): Allocator(alloc) + { + init(readMem, readLen); + } + + PxMemoryBufferBase(uint32_t defaultWriteBufferSize = BUFFER_SIZE_DEFAULT) + { + init(defaultWriteBufferSize); + } + + PxMemoryBufferBase(uint32_t defaultWriteBufferSize, const Allocator &alloc): Allocator(alloc) + { + init(defaultWriteBufferSize); + } + + virtual ~PxMemoryBufferBase(void) + { + reset(); + } + + void setAllocator(Allocator *allocator) + { + mAllocator = allocator; + } + + void initWriteBuffer(uint32_t size) + { + if ( mWriteBuffer == NULL ) + { + if ( size < mDefaultWriteBufferSize ) size = mDefaultWriteBufferSize; + mWriteBuffer = static_cast(mAllocator->allocate(size)); + PX_ASSERT( mWriteBuffer ); + mWriteLoc = mWriteBuffer; + mWriteStop = &mWriteBuffer[size]; + mWriteBufferSize = size; + mReadBuffer = mWriteBuffer; + mReadStop = &mWriteBuffer[size]; + mReadLoc = mWriteBuffer; + } + } + + void reset(void) + { + mAllocator->deallocate(mWriteBuffer); + mWriteBuffer = NULL; + mWriteBufferSize = 0; + mWriteLoc = NULL; + mWriteStop = NULL; + mReadBuffer = NULL; + mReadStop = NULL; + mReadLoc = NULL; + } + + virtual OpenMode getOpenMode(void) const + { + return mOpenMode; + } + + + SeekType isSeekable(void) const + { + return mSeekType; + } + + virtual uint32_t read(void* buffer, uint32_t size) + { + if ( (mReadLoc+size) > mReadStop ) + { + size = uint32_t(mReadStop - mReadLoc); + } + if ( size != 0 ) + { + memmove(buffer,mReadLoc,size); + mReadLoc+=size; + } + return size; + } + + virtual uint32_t peek(void* buffer, uint32_t size) + { + if ( (mReadLoc+size) > mReadStop ) + { + size = uint32_t(mReadStop - mReadLoc); + } + if ( size != 0 ) + { + memmove(buffer,mReadLoc,size); + } + return size; + } + + virtual uint32_t write(const void* buffer, uint32_t size) + { + PX_ASSERT( mOpenMode == OPEN_READ_WRITE_NEW ); + if ( mOpenMode == OPEN_READ_WRITE_NEW ) + { + if ( (mWriteLoc+size) > mWriteStop ) + growWriteBuffer(size); + memmove(mWriteLoc,buffer,size); + mWriteLoc+=size; + mReadStop = mWriteLoc; + } + else + { + size = 0; + } + return size; + } + + PX_INLINE const uint8_t * getReadLoc(void) const { return mReadLoc; } + PX_INLINE void advanceReadLoc(uint32_t len) + { + PX_ASSERT(mReadBuffer); + if ( mReadBuffer ) + { + mReadLoc+=len; + if ( mReadLoc >= mReadStop ) + { + mReadLoc = mReadStop; + } + } + } + + virtual uint32_t tellRead(void) const + { + uint32_t ret=0; + + if ( mReadBuffer ) + { + ret = uint32_t(mReadLoc-mReadBuffer); + } + return ret; + } + + virtual uint32_t tellWrite(void) const + { + return uint32_t(mWriteLoc-mWriteBuffer); + } + + virtual uint32_t seekRead(uint32_t loc) + { + uint32_t ret = 0; + PX_ASSERT(mReadBuffer); + if ( mReadBuffer ) + { + mReadLoc = &mReadBuffer[loc]; + if ( mReadLoc >= mReadStop ) + { + mReadLoc = mReadStop; + } + ret = uint32_t(mReadLoc-mReadBuffer); + } + return ret; + } + + virtual uint32_t seekWrite(uint32_t loc) + { + uint32_t ret = 0; + PX_ASSERT( mOpenMode == OPEN_READ_WRITE_NEW ); + if ( mWriteBuffer ) + { + if ( loc > mWriteBufferSize ) + { + mWriteLoc = mWriteStop; + growWriteBuffer(loc - mWriteBufferSize); + } + mWriteLoc = &mWriteBuffer[loc]; + ret = uint32_t(mWriteLoc-mWriteBuffer); + } + return ret; + } + + virtual void flush(void) + { + + } + + virtual uint32_t getFileLength(void) const + { + uint32_t ret = 0; + if ( mReadBuffer ) + { + ret = uint32_t(mReadStop-mReadBuffer); + } + else if ( mWriteBuffer ) + { + ret = uint32_t(mWriteLoc-mWriteBuffer); + } + return ret; + } + + uint32_t getWriteBufferSize(void) const + { + return uint32_t(mWriteLoc-mWriteBuffer); + } + + void setWriteLoc(uint8_t *writeLoc) + { + PX_ASSERT(writeLoc >= mWriteBuffer && writeLoc < mWriteStop ); + mWriteLoc = writeLoc; + mReadStop = mWriteLoc; + } + + const uint8_t * getWriteBuffer(void) const + { + return mWriteBuffer; + } + + /** + * Attention: if you use aligned allocator you cannot free memory with PX_FREE macros instead use deallocate method from base + */ + uint8_t * getWriteBufferOwnership(uint32_t &dataLen) // return the write buffer, and zero it out, the caller is taking ownership of the memory + { + uint8_t *ret = mWriteBuffer; + dataLen = uint32_t(mWriteLoc-mWriteBuffer); + mWriteBuffer = NULL; + mWriteLoc = NULL; + mWriteStop = NULL; + mWriteBufferSize = 0; + return ret; + } + + + void alignRead(uint32_t a) + { + uint32_t loc = tellRead(); + uint32_t aloc = ((loc+(a-1))/a)*a; + if ( aloc != loc ) + { + seekRead(aloc); + } + } + + void alignWrite(uint32_t a) + { + uint32_t loc = tellWrite(); + uint32_t aloc = ((loc+(a-1))/a)*a; + if ( aloc != loc ) + { + seekWrite(aloc); + } + } + +private: + + + // double the size of the write buffer or at least as large as the 'size' value passed in. + void growWriteBuffer(uint32_t size) + { + if ( mWriteBuffer == NULL ) + { + if ( size < mDefaultWriteBufferSize ) size = mDefaultWriteBufferSize; + initWriteBuffer(size); + } + else + { + uint32_t oldWriteIndex = uint32_t(mWriteLoc - mWriteBuffer); + uint32_t newSize = mWriteBufferSize*2; + uint32_t avail = newSize-oldWriteIndex; + if ( size >= avail ) newSize = newSize+size; + uint8_t *writeBuffer = static_cast(mAllocator->allocate(newSize)); + PX_ASSERT( writeBuffer ); + memmove(writeBuffer,mWriteBuffer,mWriteBufferSize); + mAllocator->deallocate(mWriteBuffer); + mWriteBuffer = writeBuffer; + mWriteBufferSize = newSize; + mWriteLoc = &mWriteBuffer[oldWriteIndex]; + mWriteStop = &mWriteBuffer[mWriteBufferSize]; + uint32_t oldReadLoc = uint32_t(mReadLoc-mReadBuffer); + mReadBuffer = mWriteBuffer; + mReadStop = mWriteLoc; + mReadLoc = &mReadBuffer[oldReadLoc]; + } + } + + const uint8_t *mReadBuffer; + const uint8_t *mReadLoc; + const uint8_t *mReadStop; + + uint8_t *mWriteBuffer; + uint8_t *mWriteLoc; + uint8_t *mWriteStop; + + uint32_t mWriteBufferSize; + uint32_t mDefaultWriteBufferSize; + Allocator *mAllocator; + OpenMode mOpenMode; + SeekType mSeekType; + +}; + +class PxMemoryBufferAllocator +{ +public: + PxMemoryBufferAllocator(uint32_t a = 0) : alignment(a) {} + + virtual void * allocate(uint32_t size) + { + switch(alignment) + { + case 0: + return PX_ALLOC(size, PX_DEBUG_EXP("PxMemoryBufferAllocator")); + case 16 : + return physx::AlignedAllocator<16>().allocate(size, __FILE__, __LINE__); + case 32 : + return physx::AlignedAllocator<32>().allocate(size, __FILE__, __LINE__); + case 64 : + return physx::AlignedAllocator<64>().allocate(size, __FILE__, __LINE__); + case 128 : + return physx::AlignedAllocator<128>().allocate(size, __FILE__, __LINE__); + default : + PX_ASSERT(0); + } + return NULL; + } + virtual void deallocate(void *mem) + { + switch(alignment) + { + case 0: + PX_FREE(mem); + break; + case 16 : + physx::AlignedAllocator<16>().deallocate(mem); + break; + case 32 : + physx::AlignedAllocator<32>().deallocate(mem); + break; + case 64 : + physx::AlignedAllocator<64>().deallocate(mem); + break; + case 128 : + physx::AlignedAllocator<128>().deallocate(mem); + break; + default : + PX_ASSERT(0); + } + } + virtual ~PxMemoryBufferAllocator(void) {} +private: + PxMemoryBufferAllocator& operator=(const PxMemoryBufferAllocator&); + + const uint32_t alignment; +}; + +//Use this class if you want to use PhysX memory allocator +class PsMemoryBuffer: public PxMemoryBufferBase, public UserAllocated +{ + PX_NOCOPY(PsMemoryBuffer) + typedef PxMemoryBufferBase BaseClass; + +public: + PsMemoryBuffer(const void *readMem,uint32_t readLen): BaseClass(readMem, readLen) {} + PsMemoryBuffer(const void *readMem,uint32_t readLen, uint32_t alignment): BaseClass(readMem, readLen, PxMemoryBufferAllocator(alignment)) {} + + PsMemoryBuffer(uint32_t defaultWriteBufferSize=BUFFER_SIZE_DEFAULT): BaseClass(defaultWriteBufferSize) {} + PsMemoryBuffer(uint32_t defaultWriteBufferSize,uint32_t alignment): BaseClass(defaultWriteBufferSize, PxMemoryBufferAllocator(alignment)) {} +}; + +} +using namespace general_PxIOStream2; +} + +#endif // PSFILEBUFFER_PSMEMORYBUFFER_H + diff --git a/src/PhysX/physx/source/foundation/include/Ps.h b/src/PhysX/physx/source/foundation/include/Ps.h new file mode 100644 index 000000000..7827ca13d --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/Ps.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PS_H +#define PSFOUNDATION_PS_H + +/*! \file top level include file for shared foundation */ + +#include "foundation/Px.h" + +/** +Platform specific defines +*/ +#if PX_WINDOWS_FAMILY || PX_XBOXONE +#pragma intrinsic(memcmp) +#pragma intrinsic(memcpy) +#pragma intrinsic(memset) +#pragma intrinsic(abs) +#pragma intrinsic(labs) +#endif + +// An expression that should expand to nothing in non PX_CHECKED builds. +// We currently use this only for tagging the purpose of containers for memory use tracking. +#if PX_CHECKED +#define PX_DEBUG_EXP(x) (x) +#else +#define PX_DEBUG_EXP(x) +#endif + +#define PX_SIGN_BITMASK 0x80000000 + +namespace physx +{ +namespace shdfnd +{ +// Int-as-bool type - has some uses for efficiency and with SIMD +typedef int IntBool; +static const IntBool IntFalse = 0; +static const IntBool IntTrue = 1; +} + +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PS_H diff --git a/src/PhysX/physx/source/foundation/include/PsAlignedMalloc.h b/src/PhysX/physx/source/foundation/include/PsAlignedMalloc.h new file mode 100644 index 000000000..50fdbe954 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsAlignedMalloc.h @@ -0,0 +1,88 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSALIGNEDMALLOC_H +#define PSFOUNDATION_PSALIGNEDMALLOC_H + +#include "PsUserAllocated.h" + +/*! +Allocate aligned memory. +Alignment must be a power of 2! +-- should be templated by a base allocator +*/ + +namespace physx +{ +namespace shdfnd +{ +/** +Allocator, which is used to access the global PxAllocatorCallback instance +(used for dynamic data types template instantiation), which can align memory +*/ + +// SCS: AlignedMalloc with 3 params not found, seems not used on PC either +// disabled for now to avoid GCC error + +template +class AlignedAllocator : public BaseAllocator +{ + public: + AlignedAllocator(const BaseAllocator& base = BaseAllocator()) : BaseAllocator(base) + { + } + + void* allocate(size_t size, const char* file, int line) + { + size_t pad = N - 1 + sizeof(size_t); // store offset for delete. + uint8_t* base = reinterpret_cast(BaseAllocator::allocate(size + pad, file, line)); + if(!base) + return NULL; + + uint8_t* ptr = reinterpret_cast(size_t(base + pad) & ~(size_t(N) - 1)); // aligned pointer, ensuring N + // is a size_t + // wide mask + reinterpret_cast(ptr)[-1] = size_t(ptr - base); // store offset + + return ptr; + } + void deallocate(void* ptr) + { + if(ptr == NULL) + return; + + uint8_t* base = reinterpret_cast(ptr) - reinterpret_cast(ptr)[-1]; + BaseAllocator::deallocate(base); + } +}; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSALIGNEDMALLOC_H diff --git a/src/PhysX/physx/source/foundation/include/PsAlloca.h b/src/PhysX/physx/source/foundation/include/PsAlloca.h new file mode 100644 index 000000000..a264eb72a --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsAlloca.h @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSALLOCA_H +#define PSFOUNDATION_PSALLOCA_H + +#include "PsTempAllocator.h" + +namespace physx +{ +namespace shdfnd +{ +template +class ScopedPointer : private Alloc +{ + public: + ~ScopedPointer() + { + if(mOwned) + Alloc::deallocate(mPointer); + } + + operator T*() const + { + return mPointer; + } + + T* mPointer; + bool mOwned; +}; + +} // namespace shdfnd +} // namespace physx + +/*! Stack allocation for \c count instances of \c type. Falling back to temp allocator if using more than 1kB. */ +#ifdef __SPU__ +#define PX_ALLOCA(var, type, count) type* var = reinterpret_cast(PxAlloca(sizeof(type) * (count))) +#else +#define PX_ALLOCA(var, type, count) \ + physx::shdfnd::ScopedPointer var; \ + { \ + uint32_t size = sizeof(type) * (count); \ + var.mOwned = size > 1024; \ + if(var.mOwned) \ + var.mPointer = reinterpret_cast(physx::shdfnd::TempAllocator().allocate(size, __FILE__, __LINE__)); \ + else \ + var.mPointer = reinterpret_cast(PxAlloca(size)); \ + } +#endif +#endif // #ifndef PSFOUNDATION_PSALLOCA_H diff --git a/src/PhysX/physx/source/foundation/include/PsAllocator.h b/src/PhysX/physx/source/foundation/include/PsAllocator.h new file mode 100644 index 000000000..9de5d81c0 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsAllocator.h @@ -0,0 +1,367 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSALLOCATOR_H +#define PSFOUNDATION_PSALLOCATOR_H + +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxAssert.h" +#include "PxFoundation.h" +#include "Ps.h" + +#if(PX_WINDOWS_FAMILY || PX_XBOXONE) +#include +#include +#endif +#if(PX_APPLE_FAMILY) +#include +#endif + +#include + +// Allocation macros going through user allocator +#if PX_CHECKED +#define PX_ALLOC(n, name) physx::shdfnd::NamedAllocator(name).allocate(n, __FILE__, __LINE__) +#else +#define PX_ALLOC(n, name) physx::shdfnd::NonTrackingAllocator().allocate(n, __FILE__, __LINE__) +#endif +#define PX_ALLOC_TEMP(n, name) PX_ALLOC(n, name) +#define PX_FREE(x) physx::shdfnd::NonTrackingAllocator().deallocate(x) +#define PX_FREE_AND_RESET(x) \ + { \ + PX_FREE(x); \ + x = 0; \ + } + +// The following macros support plain-old-types and classes derived from UserAllocated. +#define PX_NEW(T) new (physx::shdfnd::ReflectionAllocator(), __FILE__, __LINE__) T +#define PX_NEW_TEMP(T) PX_NEW(T) +#define PX_DELETE(x) delete x +#define PX_DELETE_AND_RESET(x) \ + { \ + PX_DELETE(x); \ + x = 0; \ + } +#define PX_DELETE_POD(x) \ + { \ + PX_FREE(x); \ + x = 0; \ + } +#define PX_DELETE_ARRAY(x) \ + { \ + PX_DELETE([] x); \ + x = 0; \ + } + +// aligned allocation +#define PX_ALIGNED16_ALLOC(n) physx::shdfnd::AlignedAllocator<16>().allocate(n, __FILE__, __LINE__) +#define PX_ALIGNED16_FREE(x) physx::shdfnd::AlignedAllocator<16>().deallocate(x) + +//! placement new macro to make it easy to spot bad use of 'new' +#define PX_PLACEMENT_NEW(p, T) new (p) T + +#if PX_DEBUG || PX_CHECKED +#define PX_USE_NAMED_ALLOCATOR 1 +#else +#define PX_USE_NAMED_ALLOCATOR 0 +#endif + +// Don't use inline for alloca !!! +#if PX_WINDOWS_FAMILY +#include +#define PxAlloca(x) _alloca(x) +#elif PX_LINUX || PX_ANDROID +#include +#define PxAlloca(x) alloca(x) +#elif PX_APPLE_FAMILY +#include +#define PxAlloca(x) alloca(x) +#elif PX_PS4 +#include +#define PxAlloca(x) alloca(x) +#elif PX_XBOXONE +#include +#define PxAlloca(x) alloca(x) +#elif PX_SWITCH +#include +#define PxAlloca(x) alloca(x) +#endif + +#define PxAllocaAligned(x, alignment) ((size_t(PxAlloca(x + alignment)) + (alignment - 1)) & ~size_t(alignment - 1)) + +namespace physx +{ +namespace shdfnd +{ + +PX_FOUNDATION_API PxAllocatorCallback& getAllocator(); + +/** +Allocator used to access the global PxAllocatorCallback instance without providing additional information. +*/ + +class PX_FOUNDATION_API Allocator +{ + public: + Allocator(const char* = 0) + { + } + void* allocate(size_t size, const char* file, int line); + void deallocate(void* ptr); +}; + +/* + * Bootstrap allocator using malloc/free. + * Don't use unless your objects get allocated before foundation is initialized. + */ +class RawAllocator +{ + public: + RawAllocator(const char* = 0) + { + } + void* allocate(size_t size, const char*, int) + { + // malloc returns valid pointer for size==0, no need to check + return ::malloc(size); + } + void deallocate(void* ptr) + { + // free(0) is guaranteed to have no side effect, no need to check + ::free(ptr); + } +}; + +/* + * Allocator that simply calls straight back to the application without tracking. + * This is used by the heap (Foundation::mNamedAllocMap) that tracks allocations + * because it needs to be able to grow as a result of an allocation. + * Making the hash table re-entrant to deal with this may not make sense. + */ +class NonTrackingAllocator +{ + public: + PX_FORCE_INLINE NonTrackingAllocator(const char* = 0) + { + } + PX_FORCE_INLINE void* allocate(size_t size, const char* file, int line) + { + return !size ? 0 : getAllocator().allocate(size, "NonTrackedAlloc", file, line); + } + PX_FORCE_INLINE void deallocate(void* ptr) + { + if(ptr) + getAllocator().deallocate(ptr); + } +}; + +/* +\brief Virtual allocator callback used to provide run-time defined allocators to foundation types like Array or Bitmap. + This is used by VirtualAllocator +*/ +class VirtualAllocatorCallback +{ + public: + VirtualAllocatorCallback() + { + } + virtual ~VirtualAllocatorCallback() + { + } + virtual void* allocate(const size_t size, const char* file, const int line) = 0; + virtual void deallocate(void* ptr) = 0; +}; + +/* +\brief Virtual allocator to be used by foundation types to provide run-time defined allocators. +Due to the fact that Array extends its allocator, rather than contains a reference/pointer to it, the VirtualAllocator +must +be a concrete type containing a pointer to a virtual callback. The callback may not be available at instantiation time, +therefore +methods are provided to set the callback later. +*/ +class VirtualAllocator +{ + public: + VirtualAllocator(VirtualAllocatorCallback* callback = NULL) : mCallback(callback) + { + } + + void* allocate(const size_t size, const char* file, const int line) + { + PX_ASSERT(mCallback); + if(size) + return mCallback->allocate(size, file, line); + return NULL; + } + void deallocate(void* ptr) + { + PX_ASSERT(mCallback); + if(ptr) + mCallback->deallocate(ptr); + } + + void setCallback(VirtualAllocatorCallback* callback) + { + mCallback = callback; + } + VirtualAllocatorCallback* getCallback() + { + return mCallback; + } + + private: + VirtualAllocatorCallback* mCallback; + VirtualAllocator& operator=(const VirtualAllocator&); +}; + +#if PX_USE_NAMED_ALLOCATOR // can be slow, so only use in debug/checked +class PX_FOUNDATION_API NamedAllocator +{ + public: + NamedAllocator(const PxEMPTY); + NamedAllocator(const char* name = 0); // todo: should not have default argument! + NamedAllocator(const NamedAllocator&); + ~NamedAllocator(); + NamedAllocator& operator=(const NamedAllocator&); + void* allocate(size_t size, const char* filename, int line); + void deallocate(void* ptr); +}; +#else +class NamedAllocator; +#endif // PX_DEBUG + +/** +Allocator used to access the global PxAllocatorCallback instance using a static name derived from T. +*/ + +template +class ReflectionAllocator +{ + static const char* getName() + { + if(!PxGetFoundation().getReportAllocationNames()) + return ""; +#if PX_GCC_FAMILY + return __PRETTY_FUNCTION__; +#else + // name() calls malloc(), raw_name() wouldn't + return typeid(T).name(); +#endif + } + + public: + ReflectionAllocator(const PxEMPTY) + { + } + ReflectionAllocator(const char* = 0) + { + } + inline ReflectionAllocator(const ReflectionAllocator&) + { + } + void* allocate(size_t size, const char* filename, int line) + { + return size ? getAllocator().allocate(size, getName(), filename, line) : 0; + } + void deallocate(void* ptr) + { + if(ptr) + getAllocator().deallocate(ptr); + } +}; + +template +struct AllocatorTraits +{ +#if PX_USE_NAMED_ALLOCATOR + typedef NamedAllocator Type; +#else + typedef ReflectionAllocator Type; +#endif +}; + +// if you get a build error here, you are trying to PX_NEW a class +// that is neither plain-old-type nor derived from UserAllocated +template +union EnableIfPod +{ + int i; + T t; + typedef X Type; +}; + +} // namespace shdfnd +} // namespace physx + +// Global placement new for ReflectionAllocator templated by +// plain-old-type. Allows using PX_NEW for pointers and built-in-types. +// +// ATTENTION: You need to use PX_DELETE_POD or PX_FREE to deallocate +// memory, not PX_DELETE. PX_DELETE_POD redirects to PX_FREE. +// +// Rationale: PX_DELETE uses global operator delete(void*), which we dont' want to overload. +// Any other definition of PX_DELETE couldn't support array syntax 'PX_DELETE([]a);'. +// PX_DELETE_POD was preferred over PX_DELETE_ARRAY because it is used +// less often and applies to both single instances and arrays. +template +PX_INLINE void* operator new(size_t size, physx::shdfnd::ReflectionAllocator alloc, const char* fileName, + typename physx::shdfnd::EnableIfPod::Type line) +{ + return alloc.allocate(size, fileName, line); +} + +template +PX_INLINE void* operator new [](size_t size, physx::shdfnd::ReflectionAllocator alloc, const char* fileName, + typename physx::shdfnd::EnableIfPod::Type line) +{ return alloc.allocate(size, fileName, line); } + +// If construction after placement new throws, this placement delete is being called. +template +PX_INLINE void operator delete(void* ptr, physx::shdfnd::ReflectionAllocator alloc, const char* fileName, + typename physx::shdfnd::EnableIfPod::Type line) +{ + PX_UNUSED(fileName); + PX_UNUSED(line); + + alloc.deallocate(ptr); +} + +// If construction after placement new throws, this placement delete is being called. +template +PX_INLINE void operator delete [](void* ptr, physx::shdfnd::ReflectionAllocator alloc, const char* fileName, + typename physx::shdfnd::EnableIfPod::Type line) +{ + PX_UNUSED(fileName); + PX_UNUSED(line); + + alloc.deallocate(ptr); +} + +#endif // #ifndef PSFOUNDATION_PSALLOCATOR_H diff --git a/src/PhysX/physx/source/foundation/include/PsAoS.h b/src/PhysX/physx/source/foundation/include/PsAoS.h new file mode 100644 index 000000000..128e99fb6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsAoS.h @@ -0,0 +1,45 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSAOS_H +#define PSFOUNDATION_PSAOS_H + +#include "foundation/Px.h" + +#if PX_WINDOWS && !PX_NEON +#include "windows/PsWindowsAoS.h" +#elif(PX_UNIX_FAMILY || PX_PS4 || PX_SWITCH) +#include "unix/PsUnixAoS.h" +#elif PX_XBOXONE +#include "XboxOne/PsXboxOneAoS.h" +#else +#error "Platform not supported!" +#endif + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsArray.h b/src/PhysX/physx/source/foundation/include/PsArray.h new file mode 100644 index 000000000..0282899a1 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsArray.h @@ -0,0 +1,721 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSARRAY_H +#define PSFOUNDATION_PSARRAY_H + +#include "foundation/PxAssert.h" +#include "foundation/PxIntrinsics.h" +#include "PsAllocator.h" +#include "PsBasicTemplates.h" + +namespace physx +{ +namespace shdfnd +{ +template +void exportArray(Serializer& stream, const void* data, uint32_t size, uint32_t sizeOfElement, uint32_t capacity); +char* importArray(char* address, void** data, uint32_t size, uint32_t sizeOfElement, uint32_t capacity); + +/*! +An array is a sequential container. + +Implementation note +* entries between 0 and size are valid objects +* we use inheritance to build this because the array is included inline in a lot + of objects and we want the allocator to take no space if it's not stateful, which + aggregation doesn't allow. Also, we want the metadata at the front for the inline + case where the allocator contains some inline storage space +*/ +template ::Type> +class Array : protected Alloc +{ + public: + typedef T* Iterator; + typedef const T* ConstIterator; + + explicit Array(const PxEMPTY v) : Alloc(v) + { + if(mData) + mCapacity |= PX_SIGN_BITMASK; + } + + /*! + Default array constructor. Initialize an empty array + */ + PX_INLINE explicit Array(const Alloc& alloc = Alloc()) : Alloc(alloc), mData(0), mSize(0), mCapacity(0) + { + } + + /*! + Initialize array with given capacity + */ + PX_INLINE explicit Array(uint32_t size, const T& a = T(), const Alloc& alloc = Alloc()) + : Alloc(alloc), mData(0), mSize(0), mCapacity(0) + { + resize(size, a); + } + + /*! + Copy-constructor. Copy all entries from other array + */ + template + PX_INLINE explicit Array(const Array& other, const Alloc& alloc = Alloc()) + : Alloc(alloc) + { + copy(other); + } + + // This is necessary else the basic default copy constructor is used in the case of both arrays being of the same + // template instance + // The C++ standard clearly states that a template constructor is never a copy constructor [2]. In other words, + // the presence of a template constructor does not suppress the implicit declaration of the copy constructor. + // Also never make a copy constructor explicit, or copy-initialization* will no longer work. This is because + // 'binding an rvalue to a const reference requires an accessible copy constructor' (http://gcc.gnu.org/bugs/) + // *http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy-initialization-and-assignment-initializ + PX_INLINE Array(const Array& other, const Alloc& alloc = Alloc()) : Alloc(alloc) + { + copy(other); + } + + /*! + Initialize array with given length + */ + PX_INLINE explicit Array(const T* first, const T* last, const Alloc& alloc = Alloc()) + : Alloc(alloc), mSize(last < first ? 0 : uint32_t(last - first)), mCapacity(mSize) + { + mData = allocate(mSize); + copy(mData, mData + mSize, first); + } + + /*! + Destructor + */ + PX_INLINE ~Array() + { + destroy(mData, mData + mSize); + + if(capacity() && !isInUserMemory()) + deallocate(mData); + } + + /*! + Assignment operator. Copy content (deep-copy) + */ + template + PX_INLINE Array& operator=(const Array& rhs) + { + if(&rhs == this) + return *this; + + clear(); + reserve(rhs.mSize); + copy(mData, mData + rhs.mSize, rhs.mData); + + mSize = rhs.mSize; + return *this; + } + + PX_INLINE Array& operator=(const Array& t) // Needs to be declared, see comment at copy-constructor + { + return operator=(t); + } + + /*! + Array indexing operator. + \param i + The index of the element that will be returned. + \return + The element i in the array. + */ + PX_FORCE_INLINE const T& operator[](uint32_t i) const + { + PX_ASSERT(i < mSize); + return mData[i]; + } + + /*! + Array indexing operator. + \param i + The index of the element that will be returned. + \return + The element i in the array. + */ + PX_FORCE_INLINE T& operator[](uint32_t i) + { + PX_ASSERT(i < mSize); + return mData[i]; + } + + /*! + Returns a pointer to the initial element of the array. + \return + a pointer to the initial element of the array. + */ + PX_FORCE_INLINE ConstIterator begin() const + { + return mData; + } + + PX_FORCE_INLINE Iterator begin() + { + return mData; + } + + /*! + Returns an iterator beyond the last element of the array. Do not dereference. + \return + a pointer to the element beyond the last element of the array. + */ + + PX_FORCE_INLINE ConstIterator end() const + { + return mData + mSize; + } + + PX_FORCE_INLINE Iterator end() + { + return mData + mSize; + } + + /*! + Returns a reference to the first element of the array. Undefined if the array is empty. + \return a reference to the first element of the array + */ + + PX_FORCE_INLINE const T& front() const + { + PX_ASSERT(mSize); + return mData[0]; + } + + PX_FORCE_INLINE T& front() + { + PX_ASSERT(mSize); + return mData[0]; + } + + /*! + Returns a reference to the last element of the array. Undefined if the array is empty + \return a reference to the last element of the array + */ + + PX_FORCE_INLINE const T& back() const + { + PX_ASSERT(mSize); + return mData[mSize - 1]; + } + + PX_FORCE_INLINE T& back() + { + PX_ASSERT(mSize); + return mData[mSize - 1]; + } + + /*! + Returns the number of entries in the array. This can, and probably will, + differ from the array capacity. + \return + The number of of entries in the array. + */ + PX_FORCE_INLINE uint32_t size() const + { + return mSize; + } + + /*! + Clears the array. + */ + PX_INLINE void clear() + { + destroy(mData, mData + mSize); + mSize = 0; + } + + /*! + Returns whether the array is empty (i.e. whether its size is 0). + \return + true if the array is empty + */ + PX_FORCE_INLINE bool empty() const + { + return mSize == 0; + } + + /*! + Finds the first occurrence of an element in the array. + \param a + The element to find. + */ + + PX_INLINE Iterator find(const T& a) + { + uint32_t index; + for(index = 0; index < mSize && mData[index] != a; index++) + ; + return mData + index; + } + + PX_INLINE ConstIterator find(const T& a) const + { + uint32_t index; + for(index = 0; index < mSize && mData[index] != a; index++) + ; + return mData + index; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Adds one element to the end of the array. Operation is O(1). + \param a + The element that will be added to this array. + */ + ///////////////////////////////////////////////////////////////////////// + + PX_FORCE_INLINE T& pushBack(const T& a) + { + if(capacity() <= mSize) + return growAndPushBack(a); + + PX_PLACEMENT_NEW(reinterpret_cast(mData + mSize), T)(a); + + return mData[mSize++]; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Returns the element at the end of the array. Only legal if the array is non-empty. + */ + ///////////////////////////////////////////////////////////////////////// + PX_INLINE T popBack() + { + PX_ASSERT(mSize); + T t = mData[mSize - 1]; + + mData[--mSize].~T(); + + return t; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Construct one element at the end of the array. Operation is O(1). + */ + ///////////////////////////////////////////////////////////////////////// + PX_INLINE T& insert() + { + if(capacity() <= mSize) + grow(capacityIncrement()); + + T* ptr = mData + mSize++; + new (ptr) T; // not 'T()' because PODs should not get default-initialized. + return *ptr; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Subtracts the element on position i from the array and replace it with + the last element. + Operation is O(1) + \param i + The position of the element that will be subtracted from this array. + */ + ///////////////////////////////////////////////////////////////////////// + PX_INLINE void replaceWithLast(uint32_t i) + { + PX_ASSERT(i < mSize); + mData[i] = mData[--mSize]; + + mData[mSize].~T(); + } + + PX_INLINE void replaceWithLast(Iterator i) + { + replaceWithLast(static_cast(i - mData)); + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Replaces the first occurrence of the element a with the last element + Operation is O(n) + \param a + The position of the element that will be subtracted from this array. + \return true if the element has been removed. + */ + ///////////////////////////////////////////////////////////////////////// + + PX_INLINE bool findAndReplaceWithLast(const T& a) + { + uint32_t index = 0; + while(index < mSize && mData[index] != a) + ++index; + if(index == mSize) + return false; + replaceWithLast(index); + return true; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Subtracts the element on position i from the array. Shift the entire + array one step. + Operation is O(n) + \param i + The position of the element that will be subtracted from this array. + */ + ///////////////////////////////////////////////////////////////////////// + PX_INLINE void remove(uint32_t i) + { + PX_ASSERT(i < mSize); + + T* it = mData + i; + it->~T(); + while (++i < mSize) + { + new (it) T(mData[i]); + ++it; + it->~T(); + } + --mSize; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Removes a range from the array. Shifts the array so order is maintained. + Operation is O(n) + \param begin + The starting position of the element that will be subtracted from this array. + \param count + The number of elments that will be subtracted from this array. + */ + ///////////////////////////////////////////////////////////////////////// + PX_INLINE void removeRange(uint32_t begin, uint32_t count) + { + PX_ASSERT(begin < mSize); + PX_ASSERT((begin + count) <= mSize); + + for(uint32_t i = 0; i < count; i++) + mData[begin + i].~T(); // call the destructor on the ones being removed first. + + T* dest = &mData[begin]; // location we are copying the tail end objects to + T* src = &mData[begin + count]; // start of tail objects + uint32_t move_count = mSize - (begin + count); // compute remainder that needs to be copied down + + for(uint32_t i = 0; i < move_count; i++) + { + new (dest) T(*src); // copy the old one to the new location + src->~T(); // call the destructor on the old location + dest++; + src++; + } + mSize -= count; + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Resize array + */ + ////////////////////////////////////////////////////////////////////////// + PX_NOINLINE void resize(const uint32_t size, const T& a = T()); + + PX_NOINLINE void resizeUninitialized(const uint32_t size); + + ////////////////////////////////////////////////////////////////////////// + /*! + Resize array such that only as much memory is allocated to hold the + existing elements + */ + ////////////////////////////////////////////////////////////////////////// + PX_INLINE void shrink() + { + recreate(mSize); + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Deletes all array elements and frees memory. + */ + ////////////////////////////////////////////////////////////////////////// + PX_INLINE void reset() + { + resize(0); + shrink(); + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Ensure that the array has at least size capacity. + */ + ////////////////////////////////////////////////////////////////////////// + PX_INLINE void reserve(const uint32_t capacity) + { + if(capacity > this->capacity()) + grow(capacity); + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Query the capacity(allocated mem) for the array. + */ + ////////////////////////////////////////////////////////////////////////// + PX_FORCE_INLINE uint32_t capacity() const + { + return mCapacity & ~PX_SIGN_BITMASK; + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Unsafe function to force the size of the array + */ + ////////////////////////////////////////////////////////////////////////// + PX_FORCE_INLINE void forceSize_Unsafe(uint32_t size) + { + PX_ASSERT(size <= mCapacity); + mSize = size; + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Swap contents of an array without allocating temporary storage + */ + ////////////////////////////////////////////////////////////////////////// + PX_INLINE void swap(Array& other) + { + shdfnd::swap(mData, other.mData); + shdfnd::swap(mSize, other.mSize); + shdfnd::swap(mCapacity, other.mCapacity); + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Assign a range of values to this vector (resizes to length of range) + */ + ////////////////////////////////////////////////////////////////////////// + PX_INLINE void assign(const T* first, const T* last) + { + resizeUninitialized(uint32_t(last - first)); + copy(begin(), end(), first); + } + + // We need one bit to mark arrays that have been deserialized from a user-provided memory block. + // For alignment & memory saving purpose we store that bit in the rarely used capacity member. + PX_FORCE_INLINE uint32_t isInUserMemory() const + { + return mCapacity & PX_SIGN_BITMASK; + } + + /// return reference to allocator + PX_INLINE Alloc& getAllocator() + { + return *this; + } + + protected: + // constructor for where we don't own the memory + Array(T* memory, uint32_t size, uint32_t capacity, const Alloc& alloc = Alloc()) + : Alloc(alloc), mData(memory), mSize(size), mCapacity(capacity | PX_SIGN_BITMASK) + { + } + + template + PX_NOINLINE void copy(const Array& other); + + PX_INLINE T* allocate(uint32_t size) + { + if(size > 0) + { + T* p = reinterpret_cast(Alloc::allocate(sizeof(T) * size, __FILE__, __LINE__)); +/** +Mark a specified amount of memory with 0xcd pattern. This is used to check that the meta data +definition for serialized classes is complete in checked builds. +*/ +#if PX_CHECKED + if(p) + { + for(uint32_t i = 0; i < (sizeof(T) * size); ++i) + reinterpret_cast(p)[i] = 0xcd; + } +#endif + return p; + } + return 0; + } + + PX_INLINE void deallocate(void* mem) + { + Alloc::deallocate(mem); + } + + static PX_INLINE void create(T* first, T* last, const T& a) + { + for(; first < last; ++first) + ::new (first) T(a); + } + + static PX_INLINE void copy(T* first, T* last, const T* src) + { + if(last <= first) + return; + + for(; first < last; ++first, ++src) + ::new (first) T(*src); + } + + static PX_INLINE void destroy(T* first, T* last) + { + for(; first < last; ++first) + first->~T(); + } + + /*! + Called when pushBack() needs to grow the array. + \param a The element that will be added to this array. + */ + PX_NOINLINE T& growAndPushBack(const T& a); + + /*! + Resizes the available memory for the array. + + \param capacity + The number of entries that the set should be able to hold. + */ + PX_INLINE void grow(uint32_t capacity) + { + PX_ASSERT(this->capacity() < capacity); + recreate(capacity); + } + + /*! + Creates a new memory block, copies all entries to the new block and destroys old entries. + + \param capacity + The number of entries that the set should be able to hold. + */ + PX_NOINLINE void recreate(uint32_t capacity); + + // The idea here is to prevent accidental bugs with pushBack or insert. Unfortunately + // it interacts badly with InlineArrays with smaller inline allocations. + // TODO(dsequeira): policy template arg, this is exactly what they're for. + PX_INLINE uint32_t capacityIncrement() const + { + const uint32_t capacity = this->capacity(); + return capacity == 0 ? 1 : capacity * 2; + } + + T* mData; + uint32_t mSize; + uint32_t mCapacity; +}; + +template +PX_NOINLINE void Array::resize(const uint32_t size, const T& a) +{ + reserve(size); + create(mData + mSize, mData + size, a); + destroy(mData + size, mData + mSize); + mSize = size; +} + +template +template +PX_NOINLINE void Array::copy(const Array& other) +{ + if(!other.empty()) + { + mData = allocate(mSize = mCapacity = other.size()); + copy(mData, mData + mSize, other.begin()); + } + else + { + mData = NULL; + mSize = 0; + mCapacity = 0; + } + + // mData = allocate(other.mSize); + // mSize = other.mSize; + // mCapacity = other.mSize; + // copy(mData, mData + mSize, other.mData); +} + +template +PX_NOINLINE void Array::resizeUninitialized(const uint32_t size) +{ + reserve(size); + mSize = size; +} + +template +PX_NOINLINE T& Array::growAndPushBack(const T& a) +{ + uint32_t capacity = capacityIncrement(); + + T* newData = allocate(capacity); + PX_ASSERT((!capacity) || (newData && (newData != mData))); + copy(newData, newData + mSize, mData); + + // inserting element before destroying old array + // avoids referencing destroyed object when duplicating array element. + PX_PLACEMENT_NEW(reinterpret_cast(newData + mSize), T)(a); + + destroy(mData, mData + mSize); + if(!isInUserMemory()) + deallocate(mData); + + mData = newData; + mCapacity = capacity; + + return mData[mSize++]; +} + +template +PX_NOINLINE void Array::recreate(uint32_t capacity) +{ + T* newData = allocate(capacity); + PX_ASSERT((!capacity) || (newData && (newData != mData))); + + copy(newData, newData + mSize, mData); + destroy(mData, mData + mSize); + if(!isInUserMemory()) + deallocate(mData); + + mData = newData; + mCapacity = capacity; +} + +template +PX_INLINE void swap(Array& x, Array& y) +{ + x.swap(y); +} + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSARRAY_H diff --git a/src/PhysX/physx/source/foundation/include/PsAtomic.h b/src/PhysX/physx/source/foundation/include/PsAtomic.h new file mode 100644 index 000000000..58ca22bd1 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsAtomic.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSATOMIC_H +#define PSFOUNDATION_PSATOMIC_H + +#include "Ps.h" + +namespace physx +{ +namespace shdfnd +{ +/* set *dest equal to val. Return the old value of *dest */ +PX_FOUNDATION_API int32_t atomicExchange(volatile int32_t* dest, int32_t val); + +/* if *dest == comp, replace with exch. Return original value of *dest */ +PX_FOUNDATION_API int32_t atomicCompareExchange(volatile int32_t* dest, int32_t exch, int32_t comp); + +/* if *dest == comp, replace with exch. Return original value of *dest */ +PX_FOUNDATION_API void* atomicCompareExchangePointer(volatile void** dest, void* exch, void* comp); + +/* increment the specified location. Return the incremented value */ +PX_FOUNDATION_API int32_t atomicIncrement(volatile int32_t* val); + +/* decrement the specified location. Return the decremented value */ +PX_FOUNDATION_API int32_t atomicDecrement(volatile int32_t* val); + +/* add delta to *val. Return the new value */ +PX_FOUNDATION_API int32_t atomicAdd(volatile int32_t* val, int32_t delta); + +/* compute the maximum of dest and val. Return the new value */ +PX_FOUNDATION_API int32_t atomicMax(volatile int32_t* val, int32_t val2); + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSATOMIC_H diff --git a/src/PhysX/physx/source/foundation/include/PsBasicTemplates.h b/src/PhysX/physx/source/foundation/include/PsBasicTemplates.h new file mode 100644 index 000000000..bd4f82da9 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsBasicTemplates.h @@ -0,0 +1,146 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSBASICTEMPLATES_H +#define PSFOUNDATION_PSBASICTEMPLATES_H + +#include "Ps.h" + +namespace physx +{ +namespace shdfnd +{ +template +struct Equal +{ + bool operator()(const A& a, const A& b) const + { + return a == b; + } +}; + +template +struct Less +{ + bool operator()(const A& a, const A& b) const + { + return a < b; + } +}; + +template +struct Greater +{ + bool operator()(const A& a, const A& b) const + { + return a > b; + } +}; + +template +class Pair +{ + public: + F first; + S second; + Pair() : first(F()), second(S()) + { + } + Pair(const F& f, const S& s) : first(f), second(s) + { + } + Pair(const Pair& p) : first(p.first), second(p.second) + { + } + // CN - fix for /.../PsBasicTemplates.h(61) : warning C4512: 'physx::shdfnd::Pair' : assignment operator could + // not be generated + Pair& operator=(const Pair& p) + { + first = p.first; + second = p.second; + return *this; + } + bool operator==(const Pair& p) const + { + return first == p.first && second == p.second; + } + bool operator<(const Pair& p) const + { + if(first < p.first) + return true; + else + return !(p.first < first) && (second < p.second); + } +}; + +template +struct LogTwo +{ + static const unsigned int value = LogTwo<(A >> 1)>::value + 1; +}; +template <> +struct LogTwo<1> +{ + static const unsigned int value = 0; +}; + +template +struct UnConst +{ + typedef T Type; +}; +template +struct UnConst +{ + typedef T Type; +}; + +template +T pointerOffset(void* p, ptrdiff_t offset) +{ + return reinterpret_cast(reinterpret_cast(p) + offset); +} +template +T pointerOffset(const void* p, ptrdiff_t offset) +{ + return reinterpret_cast(reinterpret_cast(p) + offset); +} + +template +PX_CUDA_CALLABLE PX_INLINE void swap(T& x, T& y) +{ + const T tmp = x; + x = y; + y = tmp; +} + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSBASICTEMPLATES_H diff --git a/src/PhysX/physx/source/foundation/include/PsBitUtils.h b/src/PhysX/physx/source/foundation/include/PsBitUtils.h new file mode 100644 index 000000000..b01fb532b --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsBitUtils.h @@ -0,0 +1,109 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSBITUTILS_H +#define PSFOUNDATION_PSBITUTILS_H + +#include "foundation/PxIntrinsics.h" +#include "foundation/PxAssert.h" +#include "PsIntrinsics.h" +#include "Ps.h" + +namespace physx +{ +namespace shdfnd +{ +PX_INLINE uint32_t bitCount(uint32_t v) +{ + // from http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + uint32_t const w = v - ((v >> 1) & 0x55555555); + uint32_t const x = (w & 0x33333333) + ((w >> 2) & 0x33333333); + return (((x + (x >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + +PX_INLINE bool isPowerOfTwo(uint32_t x) +{ + return x != 0 && (x & (x - 1)) == 0; +} + +// "Next Largest Power of 2 +// Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm +// that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with +// the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next +// largest power of 2. For a 32-bit value:" +PX_INLINE uint32_t nextPowerOfTwo(uint32_t x) +{ + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return x + 1; +} + +/*! +Return the index of the highest set bit. Not valid for zero arg. +*/ + +PX_INLINE uint32_t lowestSetBit(uint32_t x) +{ + PX_ASSERT(x); + return lowestSetBitUnsafe(x); +} + +/*! +Return the index of the highest set bit. Not valid for zero arg. +*/ + +PX_INLINE uint32_t highestSetBit(uint32_t x) +{ + PX_ASSERT(x); + return highestSetBitUnsafe(x); +} + +// Helper function to approximate log2 of an integer value +// assumes that the input is actually power of two. +// todo: replace 2 usages with 'highestSetBit' +PX_INLINE uint32_t ilog2(uint32_t num) +{ + for(uint32_t i = 0; i < 32; i++) + { + num >>= 1; + if(num == 0) + return i; + } + + PX_ASSERT(0); + return uint32_t(-1); +} + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSBITUTILS_H diff --git a/src/PhysX/physx/source/foundation/include/PsBroadcast.h b/src/PhysX/physx/source/foundation/include/PsBroadcast.h new file mode 100644 index 000000000..242bf81f6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsBroadcast.h @@ -0,0 +1,277 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSBROADCAST_H +#define PSFOUNDATION_PSBROADCAST_H + +#include "Ps.h" +#include "PsInlineArray.h" + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxErrorCallback.h" + +namespace physx +{ +namespace shdfnd +{ + +/** +\brief Abstract listener class that listens to allocation and deallocation events from the + foundation memory system. + +Threading: All methods of this class should be thread safe as it can be called from the user thread +or the physics processing thread(s). +*/ +class AllocationListener +{ + public: + /** + \brief callback when memory is allocated. + \param size Size of the allocation in bytes. + \param typeName Type this data is being allocated for. + \param filename File the allocation came from. + \param line the allocation came from. + \param allocatedMemory memory that will be returned from the allocation. + */ + virtual void onAllocation(size_t size, const char* typeName, const char* filename, int line, + void* allocatedMemory) = 0; + + /** + \brief callback when memory is deallocated. + \param allocatedMemory memory just before allocation. + */ + virtual void onDeallocation(void* allocatedMemory) = 0; + + protected: + virtual ~AllocationListener() + { + } +}; + +/** +\brief Broadcast class implementation, registering listeners. + +Threading: All methods of this class should be thread safe as it can be called from the user thread +or the physics processing thread(s). There is not internal locking +*/ +template +class Broadcast : public Base +{ + public: + static const uint32_t MAX_NB_LISTENERS = 16; + + /** + \brief The default constructor. + */ + Broadcast() + { + } + + /** + \brief Register new listener. + + \note It is NOT SAFE to register and deregister listeners while allocations may be taking place. + moreover, there is no thread safety to registration/deregistration. + + \param listener Listener to register. + */ + void registerListener(Listener& listener) + { + if(mListeners.size() < MAX_NB_LISTENERS) + mListeners.pushBack(&listener); + } + + /** + \brief Deregister an existing listener. + + \note It is NOT SAFE to register and deregister listeners while allocations may be taking place. + moreover, there is no thread safety to registration/deregistration. + + \param listener Listener to deregister. + */ + void deregisterListener(Listener& listener) + { + mListeners.findAndReplaceWithLast(&listener); + } + + /** + \brief Get number of registered listeners. + + \return Number of listeners. + */ + uint32_t getNbListeners() const + { + return mListeners.size(); + } + + /** + \brief Get an existing listener from given index. + + \param index Index of the listener. + \return Listener on given index. + */ + Listener& getListener(uint32_t index) + { + PX_ASSERT(index <= mListeners.size()); + return *mListeners[index]; + } + + protected: + virtual ~Broadcast() + { + } + + physx::shdfnd::InlineArray mListeners; +}; + +/** +\brief Abstract base class for an application defined memory allocator that allows an external listener +to audit the memory allocations. +*/ +class BroadcastingAllocator : public Broadcast +{ + PX_NOCOPY(BroadcastingAllocator) + + public: + /** + \brief The default constructor. + */ + BroadcastingAllocator(PxAllocatorCallback& allocator, PxErrorCallback& error) : mAllocator(allocator), mError(error) + { + mListeners.clear(); + } + + /** + \brief The default constructor. + */ + virtual ~BroadcastingAllocator() + { + mListeners.clear(); + } + + /** + \brief Allocates size bytes of memory, which must be 16-byte aligned. + + This method should never return NULL. If you run out of memory, then + you should terminate the app or take some other appropriate action. + + Threading: This function should be thread safe as it can be called in the context of the user thread + and physics processing thread(s). + + \param size Number of bytes to allocate. + \param typeName Name of the datatype that is being allocated + \param filename The source file which allocated the memory + \param line The source line which allocated the memory + \return The allocated block of memory. + */ + void* allocate(size_t size, const char* typeName, const char* filename, int line) + { + void* mem = mAllocator.allocate(size, typeName, filename, line); + + if(!mem) + { + mError.reportError(PxErrorCode::eABORT, "User allocator returned NULL.", __FILE__, __LINE__); + return NULL; + } + + if((reinterpret_cast(mem) & 15)) + { + mError.reportError(PxErrorCode::eABORT, "Allocations must be 16-byte aligned.", __FILE__, __LINE__); + return NULL; + } + + for(uint32_t i = 0; i < mListeners.size(); i++) + mListeners[i]->onAllocation(size, typeName, filename, line, mem); + + return mem; + } + + /** + \brief Frees memory previously allocated by allocate(). + + Threading: This function should be thread safe as it can be called in the context of the user thread + and physics processing thread(s). + + \param ptr Memory to free. + */ + void deallocate(void* ptr) + { + for(uint32_t i = 0; i < mListeners.size(); i++) + { + mListeners[i]->onDeallocation(ptr); + } + mAllocator.deallocate(ptr); + } + + private: + PxAllocatorCallback& mAllocator; + PxErrorCallback& mError; +}; + +/** +\brief Abstract base class for an application defined error callback that allows an external listener +to report errors. +*/ +class BroadcastingErrorCallback : public Broadcast +{ + PX_NOCOPY(BroadcastingErrorCallback) + public: + /** + \brief The default constructor. + */ + BroadcastingErrorCallback(PxErrorCallback& errorCallback) + { + registerListener(errorCallback); + } + + /** + \brief The default destructor. + */ + virtual ~BroadcastingErrorCallback() + { + mListeners.clear(); + } + + /** + \brief Reports an error code. + \param code Error code, see #PxErrorCode + \param message Message to display. + \param file File error occured in. + \param line Line number error occured on. + */ + void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) + { + for(uint32_t i = 0; i < mListeners.size(); i++) + mListeners[i]->reportError(code, message, file, line); + } +}; +} +} // namespace physx + +#endif // PSFOUNDATION_PXBROADCAST_H diff --git a/src/PhysX/physx/source/foundation/include/PsCpu.h b/src/PhysX/physx/source/foundation/include/PsCpu.h new file mode 100644 index 000000000..11df8cf7e --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsCpu.h @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSCPU_H +#define PSFOUNDATION_PSCPU_H + +#include "Ps.h" + +namespace physx +{ +namespace shdfnd +{ +class Cpu +{ + public: + static uint8_t getCpuId(); +}; +} +} + +#endif // #ifndef PSFOUNDATION_PSCPU_H diff --git a/src/PhysX/physx/source/foundation/include/PsFPU.h b/src/PhysX/physx/source/foundation/include/PsFPU.h new file mode 100644 index 000000000..f599d21fc --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsFPU.h @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSFPU_H +#define PSFOUNDATION_PSFPU_H + +#include "Ps.h" +#include "PsIntrinsics.h" + +#define PX_IR(x) ((uint32_t&)(x)) +#define PX_SIR(x) ((int32_t&)(x)) +#define PX_FR(x) ((float&)(x)) + +// signed integer representation of a floating-point value. + +// Floating-point representation of a integer value. + +#define PX_SIGN_BITMASK 0x80000000 + +#define PX_FPU_GUARD shdfnd::FPUGuard scopedFpGuard; +#define PX_SIMD_GUARD shdfnd::SIMDGuard scopedFpGuard; + +#define PX_SUPPORT_GUARDS (PX_WINDOWS_FAMILY || PX_XBOXONE || (PX_LINUX && (PX_X86 || PX_X64)) || PX_PS4 || PX_OSX) + +namespace physx +{ +namespace shdfnd +{ +// sets the default SDK state for scalar and SIMD units +class PX_FOUNDATION_API FPUGuard +{ + public: + FPUGuard(); // set fpu control word for PhysX + ~FPUGuard(); // restore fpu control word + private: + uint32_t mControlWords[8]; +}; + +// sets default SDK state for simd unit only, lighter weight than FPUGuard +class SIMDGuard +{ + public: + PX_INLINE SIMDGuard(); // set simd control word for PhysX + PX_INLINE ~SIMDGuard(); // restore simd control word + private: +#if PX_SUPPORT_GUARDS + uint32_t mControlWord; +#endif +}; + +/** +\brief Enables floating point exceptions for the scalar and SIMD unit +*/ +PX_FOUNDATION_API void enableFPExceptions(); + +/** +\brief Disables floating point exceptions for the scalar and SIMD unit +*/ +PX_FOUNDATION_API void disableFPExceptions(); + +} // namespace shdfnd +} // namespace physx + +#if PX_WINDOWS_FAMILY || PX_XBOXONE +#include "windows/PsWindowsFPU.h" +#elif (PX_LINUX && PX_SSE2) || PX_PS4 || PX_OSX +#include "unix/PsUnixFPU.h" +#else +PX_INLINE physx::shdfnd::SIMDGuard::SIMDGuard() +{ +} +PX_INLINE physx::shdfnd::SIMDGuard::~SIMDGuard() +{ +} +#endif + +#endif // #ifndef PSFOUNDATION_PSFPU_H diff --git a/src/PhysX/physx/source/foundation/include/PsFoundation.h b/src/PhysX/physx/source/foundation/include/PsFoundation.h new file mode 100644 index 000000000..e95753fbd --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsFoundation.h @@ -0,0 +1,223 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_FOUNDATION_PSFOUNDATION_H +#define PX_FOUNDATION_PSFOUNDATION_H + +#include "foundation/PxErrors.h" +#include "foundation/PxProfiler.h" + +#include "PxFoundation.h" + +#include "PsBroadcast.h" +#include "PsAllocator.h" +#include "PsTempAllocator.h" +#include "PsMutex.h" +#include "PsHashMap.h" +#include "PsUserAllocated.h" + +#include + +namespace physx +{ +namespace shdfnd +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4251) // class needs to have dll-interface to be used by clients of class +#endif + +class PX_FOUNDATION_API Foundation : public PxFoundation, public UserAllocated +{ + PX_NOCOPY(Foundation) + + public: + typedef MutexT Mutex; + + typedef HashMap, NonTrackingAllocator> AllocNameMap; + typedef Array AllocFreeTable; + + public: + // factory + // note, you MUST eventually call release if createInstance returned true! + static Foundation* createInstance(PxU32 version, PxErrorCallback& errc, PxAllocatorCallback& alloc); + static Foundation& getInstance(); + static void setInstance(Foundation& foundation); + void release(); + static void incRefCount(); // this call requires a foundation object to exist already + static void decRefCount(); // this call requires a foundation object to exist already + + // Begin Errors + virtual PxErrorCallback& getErrorCallback() + { + return mErrorCallback; + } // Return the user's error callback + PxErrorCallback& getInternalErrorCallback() + { + return mBroadcastingError; + } // Return the broadcasting error callback + + void registerErrorCallback(PxErrorCallback& listener); + void deregisterErrorCallback(PxErrorCallback& listener); + + virtual void setErrorLevel(PxErrorCode::Enum mask) + { + mErrorMask = mask; + } + virtual PxErrorCode::Enum getErrorLevel() const + { + return mErrorMask; + } + + void error(PxErrorCode::Enum, const char* file, int line, const char* messageFmt, ...); // Report errors with the + // broadcasting + void errorImpl(PxErrorCode::Enum, const char* file, int line, const char* messageFmt, va_list); // error callback + static PxU32 getWarnOnceTimestamp(); + + // End errors + + // Begin Allocations + virtual PxAllocatorCallback& getAllocatorCallback() + { + return mAllocatorCallback; + } // Return the user's allocator callback + PxAllocatorCallback& getAllocator() + { + return mBroadcastingAllocator; + } // Return the broadcasting allocator + + void registerAllocationListener(physx::shdfnd::AllocationListener& listener); + void deregisterAllocationListener(physx::shdfnd::AllocationListener& listener); + + virtual bool getReportAllocationNames() const + { + return mReportAllocationNames; + } + virtual void setReportAllocationNames(bool value) + { + mReportAllocationNames = value; + } + + PX_INLINE AllocNameMap& getNamedAllocMap() + { + return mNamedAllocMap; + } + PX_INLINE Mutex& getNamedAllocMutex() + { + return mNamedAllocMutex; + } + + PX_INLINE AllocFreeTable& getTempAllocFreeTable() + { + return mTempAllocFreeTable; + } + PX_INLINE Mutex& getTempAllocMutex() + { + return mTempAllocMutex; + } + // End allocations + + private: + static void destroyInstance(); + + Foundation(PxErrorCallback& errc, PxAllocatorCallback& alloc); + ~Foundation(); + + // init order is tricky here: the mutexes require the allocator, the allocator may require the error stream + PxAllocatorCallback& mAllocatorCallback; + PxErrorCallback& mErrorCallback; + + BroadcastingAllocator mBroadcastingAllocator; + BroadcastingErrorCallback mBroadcastingError; + + bool mReportAllocationNames; + + PxErrorCode::Enum mErrorMask; + Mutex mErrorMutex; + + AllocNameMap mNamedAllocMap; + Mutex mNamedAllocMutex; + + AllocFreeTable mTempAllocFreeTable; + Mutex mTempAllocMutex; + + Mutex mListenerMutex; + + static Foundation* mInstance; + static PxU32 mRefCount; + static PxU32 mWarnOnceTimestap; +}; +#if PX_VC +#pragma warning(pop) +#endif + +PX_INLINE Foundation& getFoundation() +{ + return Foundation::getInstance(); +} + +PX_INLINE void setFoundationInstance(Foundation& foundation) +{ + Foundation::setInstance(foundation); +} + +} // namespace shdfnd +} // namespace physx + +// shortcut macros: +// usage: Foundation::error(PX_WARN, "static friction %f is is lower than dynamic friction %d", sfr, dfr); +#define PX_WARN ::physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__ +#define PX_INFO ::physx::PxErrorCode::eDEBUG_INFO, __FILE__, __LINE__ + +#if PX_DEBUG || PX_CHECKED +#define PX_WARN_ONCE(string) \ + { \ + static PxU32 timestamp = 0; \ + if(timestamp != Ps::getFoundation().getWarnOnceTimestamp()) \ + { \ + timestamp = Ps::getFoundation().getWarnOnceTimestamp(); \ + Ps::getFoundation().error(PX_WARN, string); \ + } \ + \ +} +#define PX_WARN_ONCE_IF(condition, string) \ + { \ + if(condition) \ + { \ + PX_WARN_ONCE(string) \ + } \ + \ +} +#else +#define PX_WARN_ONCE(string) ((void)0) +#define PX_WARN_ONCE_IF(condition, string) ((void)0) +#endif + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsHash.h b/src/PhysX/physx/source/foundation/include/PsHash.h new file mode 100644 index 000000000..786907033 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsHash.h @@ -0,0 +1,162 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSHASH_H +#define PSFOUNDATION_PSHASH_H + +#include "Ps.h" +#include "PsBasicTemplates.h" + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4302) +#endif + +#if PX_LINUX +#include "foundation/PxSimpleTypes.h" +#endif + +/*! +Central definition of hash functions +*/ + +namespace physx +{ +namespace shdfnd +{ +// Hash functions + +// Thomas Wang's 32 bit mix +// http://www.cris.com/~Ttwang/tech/inthash.htm +PX_FORCE_INLINE uint32_t hash(const uint32_t key) +{ + uint32_t k = key; + k += ~(k << 15); + k ^= (k >> 10); + k += (k << 3); + k ^= (k >> 6); + k += ~(k << 11); + k ^= (k >> 16); + return uint32_t(k); +} + +PX_FORCE_INLINE uint32_t hash(const int32_t key) +{ + return hash(uint32_t(key)); +} + +// Thomas Wang's 64 bit mix +// http://www.cris.com/~Ttwang/tech/inthash.htm +PX_FORCE_INLINE uint32_t hash(const uint64_t key) +{ + uint64_t k = key; + k += ~(k << 32); + k ^= (k >> 22); + k += ~(k << 13); + k ^= (k >> 8); + k += (k << 3); + k ^= (k >> 15); + k += ~(k << 27); + k ^= (k >> 31); + return uint32_t(UINT32_MAX & k); +} + +#if PX_APPLE_FAMILY +// hash for size_t, to make gcc happy +PX_INLINE uint32_t hash(const size_t key) +{ +#if PX_P64_FAMILY + return hash(uint64_t(key)); +#else + return hash(uint32_t(key)); +#endif +} +#endif + +// Hash function for pointers +PX_INLINE uint32_t hash(const void* ptr) +{ +#if PX_P64_FAMILY + return hash(uint64_t(ptr)); +#else + return hash(uint32_t(UINT32_MAX & size_t(ptr))); +#endif +} + +// Hash function for pairs +template +PX_INLINE uint32_t hash(const Pair& p) +{ + uint32_t seed = 0x876543; + uint32_t m = 1000007; + return hash(p.second) ^ (m * (hash(p.first) ^ (m * seed))); +} + +// hash object for hash map template parameter +template +struct Hash +{ + uint32_t operator()(const Key& k) const + { + return hash(k); + } + bool equal(const Key& k0, const Key& k1) const + { + return k0 == k1; + } +}; + +// specialization for strings +template <> +struct Hash +{ + public: + uint32_t operator()(const char* _string) const + { + // "DJB" string hash + const uint8_t* string = reinterpret_cast(_string); + uint32_t h = 5381; + for(const uint8_t* ptr = string; *ptr; ptr++) + h = ((h << 5) + h) ^ uint32_t(*ptr); + return h; + } + bool equal(const char* string0, const char* string1) const + { + return !strcmp(string0, string1); + } +}; + +} // namespace shdfnd +} // namespace physx + +#if PX_VC +#pragma warning(pop) +#endif + +#endif // #ifndef PSFOUNDATION_PSHASH_H diff --git a/src/PhysX/physx/source/foundation/include/PsHashInternals.h b/src/PhysX/physx/source/foundation/include/PsHashInternals.h new file mode 100644 index 000000000..89d0265e6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsHashInternals.h @@ -0,0 +1,795 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSHASHINTERNALS_H +#define PSFOUNDATION_PSHASHINTERNALS_H + +#include "PsBasicTemplates.h" +#include "PsArray.h" +#include "PsBitUtils.h" +#include "PsHash.h" +#include "foundation/PxIntrinsics.h" + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant +#endif +namespace physx +{ +namespace shdfnd +{ +namespace internal +{ +template +class HashBase : private Allocator +{ + void init(uint32_t initialTableSize, float loadFactor) + { + mBuffer = NULL; + mEntries = NULL; + mEntriesNext = NULL; + mHash = NULL; + mEntriesCapacity = 0; + mHashSize = 0; + mLoadFactor = loadFactor; + mFreeList = uint32_t(EOL); + mTimestamp = 0; + mEntriesCount = 0; + + if(initialTableSize) + reserveInternal(initialTableSize); + } + + public: + typedef Entry EntryType; + + HashBase(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : Allocator(PX_DEBUG_EXP("hashBase")) + { + init(initialTableSize, loadFactor); + } + + HashBase(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) : Allocator(alloc) + { + init(initialTableSize, loadFactor); + } + + HashBase(const Allocator& alloc) : Allocator(alloc) + { + init(64, 0.75f); + } + + ~HashBase() + { + destroy(); // No need to clear() + + if(mBuffer) + Allocator::deallocate(mBuffer); + } + + static const uint32_t EOL = 0xffffffff; + + PX_INLINE Entry* create(const Key& k, bool& exists) + { + uint32_t h = 0; + if(mHashSize) + { + h = hash(k); + uint32_t index = mHash[h]; + while(index != EOL && !HashFn().equal(GetKey()(mEntries[index]), k)) + index = mEntriesNext[index]; + exists = index != EOL; + if(exists) + return mEntries + index; + } + else + exists = false; + + if(freeListEmpty()) + { + grow(); + h = hash(k); + } + + uint32_t entryIndex = freeListGetNext(); + + mEntriesNext[entryIndex] = mHash[h]; + mHash[h] = entryIndex; + + mEntriesCount++; + mTimestamp++; + + return mEntries + entryIndex; + } + + PX_INLINE const Entry* find(const Key& k) const + { + if(!mEntriesCount) + return NULL; + + const uint32_t h = hash(k); + uint32_t index = mHash[h]; + while(index != EOL && !HashFn().equal(GetKey()(mEntries[index]), k)) + index = mEntriesNext[index]; + return index != EOL ? mEntries + index : NULL; + } + + PX_INLINE bool erase(const Key& k, Entry& e) + { + if(!mEntriesCount) + return false; + + const uint32_t h = hash(k); + uint32_t* ptr = mHash + h; + while(*ptr != EOL && !HashFn().equal(GetKey()(mEntries[*ptr]), k)) + ptr = mEntriesNext + *ptr; + + if(*ptr == EOL) + return false; + + PX_PLACEMENT_NEW(&e, Entry)(mEntries[*ptr]); + + return eraseInternal(ptr); + } + + PX_INLINE bool erase(const Key& k) + { + if(!mEntriesCount) + return false; + + const uint32_t h = hash(k); + uint32_t* ptr = mHash + h; + while(*ptr != EOL && !HashFn().equal(GetKey()(mEntries[*ptr]), k)) + ptr = mEntriesNext + *ptr; + + if(*ptr == EOL) + return false; + + return eraseInternal(ptr); + } + + PX_INLINE uint32_t size() const + { + return mEntriesCount; + } + + PX_INLINE uint32_t capacity() const + { + return mHashSize; + } + + void clear() + { + if(!mHashSize || mEntriesCount == 0) + return; + + destroy(); + + intrinsics::memSet(mHash, EOL, mHashSize * sizeof(uint32_t)); + + const uint32_t sizeMinus1 = mEntriesCapacity - 1; + for(uint32_t i = 0; i < sizeMinus1; i++) + { + prefetchLine(mEntriesNext + i, 128); + mEntriesNext[i] = i + 1; + } + mEntriesNext[mEntriesCapacity - 1] = uint32_t(EOL); + mFreeList = 0; + mEntriesCount = 0; + } + + void reserve(uint32_t size) + { + if(size > mHashSize) + reserveInternal(size); + } + + PX_INLINE const Entry* getEntries() const + { + return mEntries; + } + + PX_INLINE Entry* insertUnique(const Key& k) + { + PX_ASSERT(find(k) == NULL); + uint32_t h = hash(k); + + uint32_t entryIndex = freeListGetNext(); + + mEntriesNext[entryIndex] = mHash[h]; + mHash[h] = entryIndex; + + mEntriesCount++; + mTimestamp++; + + return mEntries + entryIndex; + } + + private: + void destroy() + { + for(uint32_t i = 0; i < mHashSize; i++) + { + for(uint32_t j = mHash[i]; j != EOL; j = mEntriesNext[j]) + mEntries[j].~Entry(); + } + } + + template + PX_NOINLINE void copy(const HashBase& other); + + // free list management - if we're coalescing, then we use mFreeList to hold + // the top of the free list and it should always be equal to size(). Otherwise, + // we build a free list in the next() pointers. + + PX_INLINE void freeListAdd(uint32_t index) + { + if(compacting) + { + mFreeList--; + PX_ASSERT(mFreeList == mEntriesCount); + } + else + { + mEntriesNext[index] = mFreeList; + mFreeList = index; + } + } + + PX_INLINE void freeListAdd(uint32_t start, uint32_t end) + { + if(!compacting) + { + for(uint32_t i = start; i < end - 1; i++) // add the new entries to the free list + mEntriesNext[i] = i + 1; + + // link in old free list + mEntriesNext[end - 1] = mFreeList; + PX_ASSERT(mFreeList != end - 1); + mFreeList = start; + } + else if(mFreeList == EOL) // don't reset the free ptr for the compacting hash unless it's empty + mFreeList = start; + } + + PX_INLINE uint32_t freeListGetNext() + { + PX_ASSERT(!freeListEmpty()); + if(compacting) + { + PX_ASSERT(mFreeList == mEntriesCount); + return mFreeList++; + } + else + { + uint32_t entryIndex = mFreeList; + mFreeList = mEntriesNext[mFreeList]; + return entryIndex; + } + } + + PX_INLINE bool freeListEmpty() const + { + if(compacting) + return mEntriesCount == mEntriesCapacity; + else + return mFreeList == EOL; + } + + PX_INLINE void replaceWithLast(uint32_t index) + { + PX_PLACEMENT_NEW(mEntries + index, Entry)(mEntries[mEntriesCount]); + mEntries[mEntriesCount].~Entry(); + mEntriesNext[index] = mEntriesNext[mEntriesCount]; + + uint32_t h = hash(GetKey()(mEntries[index])); + uint32_t* ptr; + for(ptr = mHash + h; *ptr != mEntriesCount; ptr = mEntriesNext + *ptr) + PX_ASSERT(*ptr != EOL); + *ptr = index; + } + + PX_INLINE uint32_t hash(const Key& k, uint32_t hashSize) const + { + return HashFn()(k) & (hashSize - 1); + } + + PX_INLINE uint32_t hash(const Key& k) const + { + return hash(k, mHashSize); + } + + PX_INLINE bool eraseInternal(uint32_t* ptr) + { + const uint32_t index = *ptr; + + *ptr = mEntriesNext[index]; + + mEntries[index].~Entry(); + + mEntriesCount--; + mTimestamp++; + + if (compacting && index != mEntriesCount) + replaceWithLast(index); + + freeListAdd(index); + return true; + } + + void reserveInternal(uint32_t size) + { + if(!isPowerOfTwo(size)) + size = nextPowerOfTwo(size); + + PX_ASSERT(!(size & (size - 1))); + + // decide whether iteration can be done on the entries directly + bool resizeCompact = compacting || freeListEmpty(); + + // define new table sizes + uint32_t oldEntriesCapacity = mEntriesCapacity; + uint32_t newEntriesCapacity = uint32_t(float(size) * mLoadFactor); + uint32_t newHashSize = size; + + // allocate new common buffer and setup pointers to new tables + uint8_t* newBuffer; + uint32_t* newHash; + uint32_t* newEntriesNext; + Entry* newEntries; + { + uint32_t newHashByteOffset = 0; + uint32_t newEntriesNextBytesOffset = newHashByteOffset + newHashSize * sizeof(uint32_t); + uint32_t newEntriesByteOffset = newEntriesNextBytesOffset + newEntriesCapacity * sizeof(uint32_t); + newEntriesByteOffset += (16 - (newEntriesByteOffset & 15)) & 15; + uint32_t newBufferByteSize = newEntriesByteOffset + newEntriesCapacity * sizeof(Entry); + + newBuffer = reinterpret_cast(Allocator::allocate(newBufferByteSize, __FILE__, __LINE__)); + PX_ASSERT(newBuffer); + + newHash = reinterpret_cast(newBuffer + newHashByteOffset); + newEntriesNext = reinterpret_cast(newBuffer + newEntriesNextBytesOffset); + newEntries = reinterpret_cast(newBuffer + newEntriesByteOffset); + } + + // initialize new hash table + intrinsics::memSet(newHash, uint32_t(EOL), newHashSize * sizeof(uint32_t)); + + // iterate over old entries, re-hash and create new entries + if(resizeCompact) + { + // check that old free list is empty - we don't need to copy the next entries + PX_ASSERT(compacting || mFreeList == EOL); + + for(uint32_t index = 0; index < mEntriesCount; ++index) + { + uint32_t h = hash(GetKey()(mEntries[index]), newHashSize); + newEntriesNext[index] = newHash[h]; + newHash[h] = index; + + PX_PLACEMENT_NEW(newEntries + index, Entry)(mEntries[index]); + mEntries[index].~Entry(); + } + } + else + { + // copy old free list, only required for non compact resizing + intrinsics::memCopy(newEntriesNext, mEntriesNext, mEntriesCapacity * sizeof(uint32_t)); + + for(uint32_t bucket = 0; bucket < mHashSize; bucket++) + { + uint32_t index = mHash[bucket]; + while(index != EOL) + { + uint32_t h = hash(GetKey()(mEntries[index]), newHashSize); + newEntriesNext[index] = newHash[h]; + PX_ASSERT(index != newHash[h]); + + newHash[h] = index; + + PX_PLACEMENT_NEW(newEntries + index, Entry)(mEntries[index]); + mEntries[index].~Entry(); + + index = mEntriesNext[index]; + } + } + } + + // swap buffer and pointers + Allocator::deallocate(mBuffer); + mBuffer = newBuffer; + mHash = newHash; + mHashSize = newHashSize; + mEntriesNext = newEntriesNext; + mEntries = newEntries; + mEntriesCapacity = newEntriesCapacity; + + freeListAdd(oldEntriesCapacity, newEntriesCapacity); + } + + void grow() + { + PX_ASSERT((mFreeList == EOL) || (compacting && (mEntriesCount == mEntriesCapacity))); + + uint32_t size = mHashSize == 0 ? 16 : mHashSize * 2; + reserve(size); + } + + uint8_t* mBuffer; + Entry* mEntries; + uint32_t* mEntriesNext; // same size as mEntries + uint32_t* mHash; + uint32_t mEntriesCapacity; + uint32_t mHashSize; + float mLoadFactor; + uint32_t mFreeList; + uint32_t mTimestamp; + uint32_t mEntriesCount; // number of entries + + public: + class Iter + { + public: + PX_INLINE Iter(HashBase& b) : mBucket(0), mEntry(uint32_t(b.EOL)), mTimestamp(b.mTimestamp), mBase(b) + { + if(mBase.mEntriesCapacity > 0) + { + mEntry = mBase.mHash[0]; + skip(); + } + } + + PX_INLINE void check() const + { + PX_ASSERT(mTimestamp == mBase.mTimestamp); + } + PX_INLINE const Entry& operator*() const + { + check(); + return mBase.mEntries[mEntry]; + } + PX_INLINE Entry& operator*() + { + check(); + return mBase.mEntries[mEntry]; + } + PX_INLINE const Entry* operator->() const + { + check(); + return mBase.mEntries + mEntry; + } + PX_INLINE Entry* operator->() + { + check(); + return mBase.mEntries + mEntry; + } + PX_INLINE Iter operator++() + { + check(); + advance(); + return *this; + } + PX_INLINE Iter operator++(int) + { + check(); + Iter i = *this; + advance(); + return i; + } + PX_INLINE bool done() const + { + check(); + return mEntry == mBase.EOL; + } + + private: + PX_INLINE void advance() + { + mEntry = mBase.mEntriesNext[mEntry]; + skip(); + } + PX_INLINE void skip() + { + while(mEntry == mBase.EOL) + { + if(++mBucket == mBase.mHashSize) + break; + mEntry = mBase.mHash[mBucket]; + } + } + + Iter& operator=(const Iter&); + + uint32_t mBucket; + uint32_t mEntry; + uint32_t mTimestamp; + HashBase& mBase; + }; + + /*! + Iterate over entries in a hash base and allow entry erase while iterating + */ + class EraseIterator + { + public: + PX_INLINE EraseIterator(HashBase& b): mBase(b) + { + reset(); + } + + PX_INLINE Entry* eraseCurrentGetNext(bool eraseCurrent) + { + if(eraseCurrent && mCurrentEntryIndexPtr) + { + mBase.eraseInternal(mCurrentEntryIndexPtr); + // if next was valid return the same ptr, if next was EOL search new hash entry + if(*mCurrentEntryIndexPtr != mBase.EOL) + return mBase.mEntries + *mCurrentEntryIndexPtr; + else + return traverseHashEntries(); + } + + // traverse mHash to find next entry + if(mCurrentEntryIndexPtr == NULL) + return traverseHashEntries(); + + const uint32_t index = *mCurrentEntryIndexPtr; + if(mBase.mEntriesNext[index] == mBase.EOL) + { + return traverseHashEntries(); + } + else + { + mCurrentEntryIndexPtr = mBase.mEntriesNext + index; + return mBase.mEntries + *mCurrentEntryIndexPtr; + } + } + + PX_INLINE void reset() + { + mCurrentHashIndex = 0; + mCurrentEntryIndexPtr = NULL; + } + + private: + PX_INLINE Entry* traverseHashEntries() + { + mCurrentEntryIndexPtr = NULL; + while (mCurrentEntryIndexPtr == NULL && mCurrentHashIndex < mBase.mHashSize) + { + if (mBase.mHash[mCurrentHashIndex] != mBase.EOL) + { + mCurrentEntryIndexPtr = mBase.mHash + mCurrentHashIndex; + mCurrentHashIndex++; + return mBase.mEntries + *mCurrentEntryIndexPtr; + } + else + { + mCurrentHashIndex++; + } + } + return NULL; + } + + EraseIterator& operator=(const EraseIterator&); + private: + uint32_t* mCurrentEntryIndexPtr; + uint32_t mCurrentHashIndex; + HashBase& mBase; + }; +}; + +template +template +PX_NOINLINE void +HashBase::copy(const HashBase& other) +{ + reserve(other.mEntriesCount); + + for(uint32_t i = 0; i < other.mEntriesCount; i++) + { + for(uint32_t j = other.mHash[i]; j != EOL; j = other.mEntriesNext[j]) + { + const Entry& otherEntry = other.mEntries[j]; + + bool exists; + Entry* newEntry = create(GK()(otherEntry), exists); + PX_ASSERT(!exists); + + PX_PLACEMENT_NEW(newEntry, Entry)(otherEntry); + } + } +} + +template ::Type, bool Coalesced = false> +class HashSetBase +{ + PX_NOCOPY(HashSetBase) + public: + struct GetKey + { + PX_INLINE const Key& operator()(const Key& e) + { + return e; + } + }; + + typedef HashBase BaseMap; + typedef typename BaseMap::Iter Iterator; + + HashSetBase(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) + : mBase(initialTableSize, loadFactor, alloc) + { + } + + HashSetBase(const Allocator& alloc) : mBase(64, 0.75f, alloc) + { + } + + HashSetBase(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : mBase(initialTableSize, loadFactor) + { + } + + bool insert(const Key& k) + { + bool exists; + Key* e = mBase.create(k, exists); + if(!exists) + PX_PLACEMENT_NEW(e, Key)(k); + return !exists; + } + + PX_INLINE bool contains(const Key& k) const + { + return mBase.find(k) != 0; + } + PX_INLINE bool erase(const Key& k) + { + return mBase.erase(k); + } + PX_INLINE uint32_t size() const + { + return mBase.size(); + } + PX_INLINE uint32_t capacity() const + { + return mBase.capacity(); + } + PX_INLINE void reserve(uint32_t size) + { + mBase.reserve(size); + } + PX_INLINE void clear() + { + mBase.clear(); + } + + protected: + BaseMap mBase; +}; + +template >::Type> +class HashMapBase +{ + PX_NOCOPY(HashMapBase) + public: + typedef Pair Entry; + + struct GetKey + { + PX_INLINE const Key& operator()(const Entry& e) + { + return e.first; + } + }; + + typedef HashBase BaseMap; + typedef typename BaseMap::Iter Iterator; + typedef typename BaseMap::EraseIterator EraseIterator; + + HashMapBase(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) + : mBase(initialTableSize, loadFactor, alloc) + { + } + + HashMapBase(const Allocator& alloc) : mBase(64, 0.75f, alloc) + { + } + + HashMapBase(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : mBase(initialTableSize, loadFactor) + { + } + + bool insert(const Key /*&*/ k, const Value /*&*/ v) + { + bool exists; + Entry* e = mBase.create(k, exists); + if(!exists) + PX_PLACEMENT_NEW(e, Entry)(k, v); + return !exists; + } + + Value& operator[](const Key& k) + { + bool exists; + Entry* e = mBase.create(k, exists); + if(!exists) + PX_PLACEMENT_NEW(e, Entry)(k, Value()); + + return e->second; + } + + PX_INLINE const Entry* find(const Key& k) const + { + return mBase.find(k); + } + PX_INLINE bool erase(const Key& k) + { + return mBase.erase(k); + } + PX_INLINE bool erase(const Key& k, Entry& e) + { + return mBase.erase(k, e); + } + PX_INLINE uint32_t size() const + { + return mBase.size(); + } + PX_INLINE uint32_t capacity() const + { + return mBase.capacity(); + } + PX_INLINE Iterator getIterator() + { + return Iterator(mBase); + } + PX_INLINE EraseIterator getEraseIterator() + { + return EraseIterator(mBase); + } + PX_INLINE void reserve(uint32_t size) + { + mBase.reserve(size); + } + PX_INLINE void clear() + { + mBase.clear(); + } + + protected: + BaseMap mBase; +}; +} + +} // namespace shdfnd +} // namespace physx + +#if PX_VC +#pragma warning(pop) +#endif +#endif // #ifndef PSFOUNDATION_PSHASHINTERNALS_H diff --git a/src/PhysX/physx/source/foundation/include/PsHashMap.h b/src/PhysX/physx/source/foundation/include/PsHashMap.h new file mode 100644 index 000000000..f5913fb96 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsHashMap.h @@ -0,0 +1,118 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSHASHMAP_H +#define PSFOUNDATION_PSHASHMAP_H + +#include "PsHashInternals.h" + +// TODO: make this doxy-format +// +// This header defines two hash maps. Hash maps +// * support custom initial table sizes (rounded up internally to power-of-2) +// * support custom static allocator objects +// * auto-resize, based on a load factor (i.e. a 64-entry .75 load factor hash will resize +// when the 49th element is inserted) +// * are based on open hashing +// * have O(1) contains, erase +// +// Maps have STL-like copying semantics, and properly initialize and destruct copies of objects +// +// There are two forms of map: coalesced and uncoalesced. Coalesced maps keep the entries in the +// initial segment of an array, so are fast to iterate over; however deletion is approximately +// twice as expensive. +// +// HashMap: +// bool insert(const Key& k, const Value& v) O(1) amortized (exponential resize policy) +// Value & operator[](const Key& k) O(1) for existing objects, else O(1) amortized +// const Entry * find(const Key& k); O(1) +// bool erase(const T& k); O(1) +// uint32_t size(); constant +// void reserve(uint32_t size); O(MAX(currentOccupancy,size)) +// void clear(); O(currentOccupancy) (with zero constant for objects +// without +// destructors) +// Iterator getIterator(); +// +// operator[] creates an entry if one does not exist, initializing with the default constructor. +// CoalescedHashMap does not support getIterator, but instead supports +// const Key *getEntries(); +// +// Use of iterators: +// +// for(HashMap::Iterator iter = test.getIterator(); !iter.done(); ++iter) +// myFunction(iter->first, iter->second); + +namespace physx +{ +namespace shdfnd +{ +template , class Allocator = NonTrackingAllocator> +class HashMap : public internal::HashMapBase +{ + public: + typedef internal::HashMapBase HashMapBase; + typedef typename HashMapBase::Iterator Iterator; + + HashMap(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : HashMapBase(initialTableSize, loadFactor) + { + } + HashMap(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) + : HashMapBase(initialTableSize, loadFactor, alloc) + { + } + HashMap(const Allocator& alloc) : HashMapBase(64, 0.75f, alloc) + { + } + Iterator getIterator() + { + return Iterator(HashMapBase::mBase); + } +}; + +template , class Allocator = NonTrackingAllocator> +class CoalescedHashMap : public internal::HashMapBase +{ + public: + typedef internal::HashMapBase HashMapBase; + + CoalescedHashMap(uint32_t initialTableSize = 64, float loadFactor = 0.75f) + : HashMapBase(initialTableSize, loadFactor) + { + } + const Pair* getEntries() const + { + return HashMapBase::mBase.getEntries(); + } +}; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSHASHMAP_H diff --git a/src/PhysX/physx/source/foundation/include/PsHashSet.h b/src/PhysX/physx/source/foundation/include/PsHashSet.h new file mode 100644 index 000000000..95eae09a6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsHashSet.h @@ -0,0 +1,127 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSHASHSET_H +#define PSFOUNDATION_PSHASHSET_H + +#include "PsHashInternals.h" + +// TODO: make this doxy-format + +// This header defines two hash sets. Hash sets +// * support custom initial table sizes (rounded up internally to power-of-2) +// * support custom static allocator objects +// * auto-resize, based on a load factor (i.e. a 64-entry .75 load factor hash will resize +// when the 49th element is inserted) +// * are based on open hashing +// +// Sets have STL-like copying semantics, and properly initialize and destruct copies of objects +// +// There are two forms of set: coalesced and uncoalesced. Coalesced sets keep the entries in the +// initial segment of an array, so are fast to iterate over; however deletion is approximately +// twice as expensive. +// +// HashSet: +// bool insert(const T& k) amortized O(1) (exponential resize policy) +// bool contains(const T& k) const; O(1) +// bool erase(const T& k); O(1) +// uint32_t size() const; constant +// void reserve(uint32_t size); O(MAX(size, currentOccupancy)) +// void clear(); O(currentOccupancy) (with zero constant for objects without +// destructors) +// Iterator getIterator(); +// +// Use of iterators: +// +// for(HashSet::Iterator iter = test.getIterator(); !iter.done(); ++iter) +// myFunction(*iter); +// +// CoalescedHashSet does not support getIterator, but instead supports +// const Key *getEntries(); +// +// insertion into a set already containing the element fails returning false, as does +// erasure of an element not in the set +// + +namespace physx +{ +namespace shdfnd +{ +template , class Allocator = NonTrackingAllocator> +class HashSet : public internal::HashSetBase +{ + public: + typedef internal::HashSetBase HashSetBase; + typedef typename HashSetBase::Iterator Iterator; + + HashSet(uint32_t initialTableSize = 64, float loadFactor = 0.75f) : HashSetBase(initialTableSize, loadFactor) + { + } + HashSet(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) + : HashSetBase(initialTableSize, loadFactor, alloc) + { + } + HashSet(const Allocator& alloc) : HashSetBase(64, 0.75f, alloc) + { + } + Iterator getIterator() + { + return Iterator(HashSetBase::mBase); + } +}; + +template , class Allocator = NonTrackingAllocator> +class CoalescedHashSet : public internal::HashSetBase +{ + public: + typedef typename internal::HashSetBase HashSetBase; + + CoalescedHashSet(uint32_t initialTableSize = 64, float loadFactor = 0.75f) + : HashSetBase(initialTableSize, loadFactor) + { + } + + CoalescedHashSet(uint32_t initialTableSize, float loadFactor, const Allocator& alloc) + : HashSetBase(initialTableSize, loadFactor, alloc) + { + } + CoalescedHashSet(const Allocator& alloc) : HashSetBase(64, 0.75f, alloc) + { + } + + const Key* getEntries() const + { + return HashSetBase::mBase.getEntries(); + } +}; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSHASHSET_H diff --git a/src/PhysX/physx/source/foundation/include/PsInlineAllocator.h b/src/PhysX/physx/source/foundation/include/PsInlineAllocator.h new file mode 100644 index 000000000..9acdc9120 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsInlineAllocator.h @@ -0,0 +1,91 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSINLINEALLOCATOR_H +#define PSFOUNDATION_PSINLINEALLOCATOR_H + +#include "PsUserAllocated.h" + +namespace physx +{ +namespace shdfnd +{ +// this is used by the array class to allocate some space for a small number +// of objects along with the metadata +template +class InlineAllocator : private BaseAllocator +{ + public: + InlineAllocator(const PxEMPTY v) : BaseAllocator(v) + { + } + + InlineAllocator(const BaseAllocator& alloc = BaseAllocator()) : BaseAllocator(alloc), mBufferUsed(false) + { + } + + InlineAllocator(const InlineAllocator& aloc) : BaseAllocator(aloc), mBufferUsed(false) + { + } + + void* allocate(uint32_t size, const char* filename, int line) + { + if(!mBufferUsed && size <= N) + { + mBufferUsed = true; + return mBuffer; + } + return BaseAllocator::allocate(size, filename, line); + } + + void deallocate(void* ptr) + { + if(ptr == mBuffer) + mBufferUsed = false; + else + BaseAllocator::deallocate(ptr); + } + + PX_FORCE_INLINE uint8_t* getInlineBuffer() + { + return mBuffer; + } + PX_FORCE_INLINE bool isBufferUsed() const + { + return mBufferUsed; + } + + protected: + uint8_t mBuffer[N]; + bool mBufferUsed; +}; +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSINLINEALLOCATOR_H diff --git a/src/PhysX/physx/source/foundation/include/PsInlineAoS.h b/src/PhysX/physx/source/foundation/include/PsInlineAoS.h new file mode 100644 index 000000000..f08405758 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsInlineAoS.h @@ -0,0 +1,48 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSINLINEAOS_H +#define PSFOUNDATION_PSINLINEAOS_H + +#include "foundation/PxPreprocessor.h" + +#if PX_WINDOWS +#include "windows/PsWindowsTrigConstants.h" +#include "windows/PsWindowsInlineAoS.h" +#elif(PX_UNIX_FAMILY || PX_PS4 || PX_SWITCH || (PX_UWP && PX_NEON)) +#include "unix/PsUnixTrigConstants.h" +#include "unix/PsUnixInlineAoS.h" +#elif PX_XBOXONE +#include "XboxOne/PsXboxOneTrigConstants.h" +#include "XboxOne/PsXboxOneInlineAoS.h" +#else +#error "Platform not supported!" +#endif + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsInlineArray.h b/src/PhysX/physx/source/foundation/include/PsInlineArray.h new file mode 100644 index 000000000..c7fed5eb2 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsInlineArray.h @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSINLINEARRAY_H +#define PSFOUNDATION_PSINLINEARRAY_H + +#include "PsArray.h" +#include "PsInlineAllocator.h" + +namespace physx +{ +namespace shdfnd +{ + +// array that pre-allocates for N elements +template ::Type> +class InlineArray : public Array > +{ + typedef InlineAllocator Allocator; + + public: + InlineArray(const PxEMPTY v) : Array(v) + { + if(isInlined()) + this->mData = reinterpret_cast(Array::getInlineBuffer()); + } + + PX_INLINE bool isInlined() const + { + return Allocator::isBufferUsed(); + } + + PX_INLINE explicit InlineArray(const Alloc& alloc = Alloc()) : Array(alloc) + { + this->mData = this->allocate(N); + this->mCapacity = N; + } +}; +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSINLINEARRAY_H diff --git a/src/PhysX/physx/source/foundation/include/PsIntrinsics.h b/src/PhysX/physx/source/foundation/include/PsIntrinsics.h new file mode 100644 index 000000000..d6b6408c4 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsIntrinsics.h @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSINTRINSICS_H +#define PSFOUNDATION_PSINTRINSICS_H + +#include "foundation/PxPreprocessor.h" + +#if PX_WINDOWS_FAMILY +#include "windows/PsWindowsIntrinsics.h" +#elif(PX_LINUX || PX_ANDROID || PX_APPLE_FAMILY || PX_PS4) +#include "unix/PsUnixIntrinsics.h" +#elif PX_XBOXONE +#include "XboxOne/PsXboxOneIntrinsics.h" +#elif PX_SWITCH +#include "switch/PsSwitchIntrinsics.h" +#else +#error "Platform not supported!" +#endif + +#endif // #ifndef PSFOUNDATION_PSINTRINSICS_H diff --git a/src/PhysX/physx/source/foundation/include/PsMathUtils.h b/src/PhysX/physx/source/foundation/include/PsMathUtils.h new file mode 100644 index 000000000..33200a38f --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsMathUtils.h @@ -0,0 +1,701 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSMATHUTILS_H +#define PSFOUNDATION_PSMATHUTILS_H + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" +#include "Ps.h" +#include "PsIntrinsics.h" + +// General guideline is: if it's an abstract math function, it belongs here. +// If it's a math function where the inputs have specific semantics (e.g. +// separateSwingTwist) it doesn't. + +namespace physx +{ +namespace shdfnd +{ +/** +\brief sign returns the sign of its argument. The sign of zero is undefined. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 sign(const PxF32 a) +{ + return intrinsics::sign(a); +} + +/** +\brief sign returns the sign of its argument. The sign of zero is undefined. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 sign(const PxF64 a) +{ + return (a >= 0.0) ? 1.0 : -1.0; +} + +/** +\brief sign returns the sign of its argument. The sign of zero is undefined. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxI32 sign(const PxI32 a) +{ + return (a >= 0) ? 1 : -1; +} + +/** +\brief Returns true if the two numbers are within eps of each other. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE bool equals(const PxF32 a, const PxF32 b, const PxF32 eps) +{ + return (PxAbs(a - b) < eps); +} + +/** +\brief Returns true if the two numbers are within eps of each other. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE bool equals(const PxF64 a, const PxF64 b, const PxF64 eps) +{ + return (PxAbs(a - b) < eps); +} + +/** +\brief The floor function returns a floating-point value representing the largest integer that is less than or equal to +x. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 floor(const PxF32 a) +{ + return floatFloor(a); +} + +/** +\brief The floor function returns a floating-point value representing the largest integer that is less than or equal to +x. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 floor(const PxF64 a) +{ + return ::floor(a); +} + +/** +\brief The ceil function returns a single value representing the smallest integer that is greater than or equal to x. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 ceil(const PxF32 a) +{ + return ::ceilf(a); +} + +/** +\brief The ceil function returns a double value representing the smallest integer that is greater than or equal to x. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 ceil(const PxF64 a) +{ + return ::ceil(a); +} + +/** +\brief mod returns the floating-point remainder of x / y. + +If the value of y is 0.0, mod returns a quiet NaN. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 mod(const PxF32 x, const PxF32 y) +{ + return PxF32(::fmodf(x, y)); +} + +/** +\brief mod returns the floating-point remainder of x / y. + +If the value of y is 0.0, mod returns a quiet NaN. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 mod(const PxF64 x, const PxF64 y) +{ + return ::fmod(x, y); +} + +/** +\brief Square. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 sqr(const PxF32 a) +{ + return a * a; +} + +/** +\brief Square. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 sqr(const PxF64 a) +{ + return a * a; +} + +/** +\brief Calculates x raised to the power of y. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 pow(const PxF32 x, const PxF32 y) +{ + return ::powf(x, y); +} + +/** +\brief Calculates x raised to the power of y. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 pow(const PxF64 x, const PxF64 y) +{ + return ::pow(x, y); +} + +/** +\brief Calculates e^n +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 exp(const PxF32 a) +{ + return ::expf(a); +} +/** + +\brief Calculates e^n +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 exp(const PxF64 a) +{ + return ::exp(a); +} + +/** +\brief Calculates 2^n +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 exp2(const PxF32 a) +{ + return ::expf(a * 0.693147180559945309417f); +} +/** + +\brief Calculates 2^n +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 exp2(const PxF64 a) +{ + return ::exp(a * 0.693147180559945309417); +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 logE(const PxF32 a) +{ + return ::logf(a); +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 logE(const PxF64 a) +{ + return ::log(a); +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 log2(const PxF32 a) +{ + return ::logf(a) / 0.693147180559945309417f; +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 log2(const PxF64 a) +{ + return ::log(a) / 0.693147180559945309417; +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 log10(const PxF32 a) +{ + return ::log10f(a); +} + +/** +\brief Calculates logarithms. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 log10(const PxF64 a) +{ + return ::log10(a); +} + +/** +\brief Converts degrees to radians. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 degToRad(const PxF32 a) +{ + return 0.01745329251994329547f * a; +} + +/** +\brief Converts degrees to radians. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 degToRad(const PxF64 a) +{ + return 0.01745329251994329547 * a; +} + +/** +\brief Converts radians to degrees. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 radToDeg(const PxF32 a) +{ + return 57.29577951308232286465f * a; +} + +/** +\brief Converts radians to degrees. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF64 radToDeg(const PxF64 a) +{ + return 57.29577951308232286465 * a; +} + +//! \brief compute sine and cosine at the same time. There is a 'fsincos' on PC that we probably want to use here +PX_CUDA_CALLABLE PX_FORCE_INLINE void sincos(const PxF32 radians, PxF32& sin, PxF32& cos) +{ + /* something like: + _asm fld Local + _asm fsincos + _asm fstp LocalCos + _asm fstp LocalSin + */ + sin = PxSin(radians); + cos = PxCos(radians); +} + +/** +\brief uniform random number in [a,b] +*/ +PX_FORCE_INLINE PxI32 rand(const PxI32 a, const PxI32 b) +{ + return a + PxI32(::rand() % (b - a + 1)); +} + +/** +\brief uniform random number in [a,b] +*/ +PX_FORCE_INLINE PxF32 rand(const PxF32 a, const PxF32 b) +{ + return a + (b - a) * ::rand() / RAND_MAX; +} + +//! \brief return angle between two vectors in radians +PX_CUDA_CALLABLE PX_FORCE_INLINE PxF32 angle(const PxVec3& v0, const PxVec3& v1) +{ + const PxF32 cos = v0.dot(v1); // |v0|*|v1|*Cos(Angle) + const PxF32 sin = (v0.cross(v1)).magnitude(); // |v0|*|v1|*Sin(Angle) + return PxAtan2(sin, cos); +} + +//! If possible use instead fsel on the dot product /*fsel(d.dot(p),onething,anotherthing);*/ +//! Compares orientations (more readable, user-friendly function) +PX_CUDA_CALLABLE PX_FORCE_INLINE bool sameDirection(const PxVec3& d, const PxVec3& p) +{ + return d.dot(p) >= 0.0f; +} + +//! Checks 2 values have different signs +PX_CUDA_CALLABLE PX_FORCE_INLINE IntBool differentSign(PxReal f0, PxReal f1) +{ +#if !PX_EMSCRIPTEN + union + { + PxU32 u; + PxReal f; + } u1, u2; + u1.f = f0; + u2.f = f1; + return IntBool((u1.u ^ u2.u) & PX_SIGN_BITMASK); +#else + // javascript floats are 64-bits... + return IntBool( (f0*f1) < 0.0f ); +#endif +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33 star(const PxVec3& v) +{ + return PxMat33(PxVec3(0, v.z, -v.y), PxVec3(-v.z, 0, v.x), PxVec3(v.y, -v.x, 0)); +} + +PX_CUDA_CALLABLE PX_INLINE PxVec3 log(const PxQuat& q) +{ + const PxReal s = q.getImaginaryPart().magnitude(); + if(s < 1e-12f) + return PxVec3(0.0f); + // force the half-angle to have magnitude <= pi/2 + PxReal halfAngle = q.w < 0 ? PxAtan2(-s, -q.w) : PxAtan2(s, q.w); + PX_ASSERT(halfAngle >= -PxPi / 2 && halfAngle <= PxPi / 2); + + return q.getImaginaryPart().getNormalized() * 2.f * halfAngle; +} + +PX_CUDA_CALLABLE PX_INLINE PxQuat exp(const PxVec3& v) +{ + const PxReal m = v.magnitudeSquared(); + return m < 1e-24f ? PxQuat(PxIdentity) : PxQuat(PxSqrt(m), v * PxRecipSqrt(m)); +} + +// quat to rotate v0 t0 v1 +PX_CUDA_CALLABLE PX_INLINE PxQuat rotationArc(const PxVec3& v0, const PxVec3& v1) +{ + const PxVec3 cross = v0.cross(v1); + const PxReal d = v0.dot(v1); + if(d <= -0.99999f) + return (PxAbs(v0.x) < 0.1f ? PxQuat(0.0f, v0.z, -v0.y, 0.0f) : PxQuat(v0.y, -v0.x, 0.0, 0.0)).getNormalized(); + + const PxReal s = PxSqrt((1 + d) * 2), r = 1 / s; + + return PxQuat(cross.x * r, cross.y * r, cross.z * r, s * 0.5f).getNormalized(); +} + +/** +\brief returns largest axis +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 largestAxis(const PxVec3& v) +{ + PxU32 m = PxU32(v.y > v.x ? 1 : 0); + return v.z > v[m] ? 2 : m; +} + +/** +\brief returns indices for the largest axis and 2 other axii +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 largestAxis(const PxVec3& v, PxU32& other1, PxU32& other2) +{ + if(v.x >= PxMax(v.y, v.z)) + { + other1 = 1; + other2 = 2; + return 0; + } + else if(v.y >= v.z) + { + other1 = 0; + other2 = 2; + return 1; + } + else + { + other1 = 0; + other2 = 1; + return 2; + } +} + +/** +\brief returns axis with smallest absolute value +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 closestAxis(const PxVec3& v) +{ + PxU32 m = PxU32(PxAbs(v.y) > PxAbs(v.x) ? 1 : 0); + return PxAbs(v.z) > PxAbs(v[m]) ? 2 : m; +} + +PX_CUDA_CALLABLE PX_INLINE PxU32 closestAxis(const PxVec3& v, PxU32& j, PxU32& k) +{ + // find largest 2D plane projection + const PxF32 absPx = PxAbs(v.x); + const PxF32 absNy = PxAbs(v.y); + const PxF32 absNz = PxAbs(v.z); + + PxU32 m = 0; // x biggest axis + j = 1; + k = 2; + if(absNy > absPx && absNy > absNz) + { + // y biggest + j = 2; + k = 0; + m = 1; + } + else if(absNz > absPx) + { + // z biggest + j = 0; + k = 1; + m = 2; + } + return m; +} + +/*! +Extend an edge along its length by a factor +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE void makeFatEdge(PxVec3& p0, PxVec3& p1, PxReal fatCoeff) +{ + PxVec3 delta = p1 - p0; + + const PxReal m = delta.magnitude(); + if(m > 0.0f) + { + delta *= fatCoeff / m; + p0 -= delta; + p1 += delta; + } +} + +//! Compute point as combination of barycentric coordinates +PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 +computeBarycentricPoint(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxReal u, PxReal v) +{ + // This seems to confuse the compiler... + // return (1.0f - u - v)*p0 + u*p1 + v*p2; + const PxF32 w = 1.0f - u - v; + return PxVec3(w * p0.x + u * p1.x + v * p2.x, w * p0.y + u * p1.y + v * p2.y, w * p0.z + u * p1.z + v * p2.z); +} + +// generates a pair of quaternions (swing, twist) such that in = swing * twist, with +// swing.x = 0 +// twist.y = twist.z = 0, and twist is a unit quat +PX_FORCE_INLINE void separateSwingTwist(const PxQuat& q, PxQuat& swing, PxQuat& twist) +{ + twist = q.x != 0.0f ? PxQuat(q.x, 0, 0, q.w).getNormalized() : PxQuat(PxIdentity); + swing = q * twist.getConjugate(); +} + +// generate two tangent vectors to a given normal +PX_FORCE_INLINE void normalToTangents(const PxVec3& normal, PxVec3& tangent0, PxVec3& tangent1) +{ + tangent0 = PxAbs(normal.x) < 0.70710678f ? PxVec3(0, -normal.z, normal.y) : PxVec3(-normal.y, normal.x, 0); + tangent0.normalize(); + tangent1 = normal.cross(tangent0); +} + +/** +\brief computes a oriented bounding box around the scaled basis. +\param basis Input = skewed basis, Output = (normalized) orthogonal basis. +\return Bounding box extent. +*/ +PX_FOUNDATION_API PxVec3 optimizeBoundingBox(PxMat33& basis); + +PX_FOUNDATION_API PxQuat slerp(const PxReal t, const PxQuat& left, const PxQuat& right); + +PX_CUDA_CALLABLE PX_INLINE PxVec3 ellipseClamp(const PxVec3& point, const PxVec3& radii) +{ + // This function need to be implemented in the header file because + // it is included in a spu shader program. + + // finds the closest point on the ellipse to a given point + + // (p.y, p.z) is the input point + // (e.y, e.z) are the radii of the ellipse + + // lagrange multiplier method with Newton/Halley hybrid root-finder. + // see http://www.geometrictools.com/Documentation/DistancePointToEllipse2.pdf + // for proof of Newton step robustness and initial estimate. + // Halley converges much faster but sometimes overshoots - when that happens we take + // a newton step instead + + // converges in 1-2 iterations where D&C works well, and it's good with 4 iterations + // with any ellipse that isn't completely crazy + + const PxU32 MAX_ITERATIONS = 20; + const PxReal convergenceThreshold = 1e-4f; + + // iteration requires first quadrant but we recover generality later + + PxVec3 q(0, PxAbs(point.y), PxAbs(point.z)); + const PxReal tinyEps = 1e-6f; // very close to minor axis is numerically problematic but trivial + if(radii.y >= radii.z) + { + if(q.z < tinyEps) + return PxVec3(0, point.y > 0 ? radii.y : -radii.y, 0); + } + else + { + if(q.y < tinyEps) + return PxVec3(0, 0, point.z > 0 ? radii.z : -radii.z); + } + + PxVec3 denom, e2 = radii.multiply(radii), eq = radii.multiply(q); + + // we can use any initial guess which is > maximum(-e.y^2,-e.z^2) and for which f(t) is > 0. + // this guess works well near the axes, but is weak along the diagonals. + + PxReal t = PxMax(eq.y - e2.y, eq.z - e2.z); + + for(PxU32 i = 0; i < MAX_ITERATIONS; i++) + { + denom = PxVec3(0, 1 / (t + e2.y), 1 / (t + e2.z)); + PxVec3 denom2 = eq.multiply(denom); + + PxVec3 fv = denom2.multiply(denom2); + PxReal f = fv.y + fv.z - 1; + + // although in exact arithmetic we are guaranteed f>0, we can get here + // on the first iteration via catastrophic cancellation if the point is + // very close to the origin. In that case we just behave as if f=0 + + if(f < convergenceThreshold) + return e2.multiply(point).multiply(denom); + + PxReal df = fv.dot(denom) * -2.0f; + t = t - f / df; + } + + // we didn't converge, so clamp what we have + PxVec3 r = e2.multiply(point).multiply(denom); + return r * PxRecipSqrt(sqr(r.y / radii.y) + sqr(r.z / radii.z)); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal tanHalf(PxReal sin, PxReal cos) +{ + // PT: avoids divide by zero for singularity. We return sqrt(FLT_MAX) instead of FLT_MAX + // to make sure the calling code doesn't generate INF values when manipulating the returned value + // (some joints multiply it by 4, etc). + if(cos==-1.0f) + return sin<0.0f ? -sqrtf(FLT_MAX) : sqrtf(FLT_MAX); + + // PT: half-angle formula: tan(a/2) = sin(a)/(1+cos(a)) + return sin / (1.0f + cos); +} + +PX_INLINE PxQuat quatFromTanQVector(const PxVec3& v) +{ + PxReal v2 = v.dot(v); + if(v2 < 1e-12f) + return PxQuat(PxIdentity); + PxReal d = 1 / (1 + v2); + return PxQuat(v.x * 2, v.y * 2, v.z * 2, 1 - v2) * d; +} + +PX_FORCE_INLINE PxVec3 cross100(const PxVec3& b) +{ + return PxVec3(0.0f, -b.z, b.y); +} +PX_FORCE_INLINE PxVec3 cross010(const PxVec3& b) +{ + return PxVec3(b.z, 0.0f, -b.x); +} +PX_FORCE_INLINE PxVec3 cross001(const PxVec3& b) +{ + return PxVec3(-b.y, b.x, 0.0f); +} + +PX_INLINE void decomposeVector(PxVec3& normalCompo, PxVec3& tangentCompo, const PxVec3& outwardDir, + const PxVec3& outwardNormal) +{ + normalCompo = outwardNormal * (outwardDir.dot(outwardNormal)); + tangentCompo = outwardDir - normalCompo; +} + +//! \brief Return (i+1)%3 +// Avoid variable shift for XBox: +// PX_INLINE PxU32 Ps::getNextIndex3(PxU32 i) { return (1<> 1)) & 3; +} + +PX_INLINE PxMat33 rotFrom2Vectors(const PxVec3& from, const PxVec3& to) +{ + // See bottom of http://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/index.htm + + // Early exit if to = from + if((from - to).magnitudeSquared() < 1e-4f) + return PxMat33(PxIdentity); + + // Early exit if to = -from + if((from + to).magnitudeSquared() < 1e-4f) + return PxMat33::createDiagonal(PxVec3(1.0f, -1.0f, -1.0f)); + + PxVec3 n = from.cross(to); + + PxReal C = from.dot(to), S = PxSqrt(1 - C * C), CC = 1 - C; + + PxReal xx = n.x * n.x, yy = n.y * n.y, zz = n.z * n.z, xy = n.x * n.y, yz = n.y * n.z, xz = n.x * n.z; + + PxMat33 R; + + R(0, 0) = 1 + CC * (xx - 1); + R(0, 1) = -n.z * S + CC * xy; + R(0, 2) = n.y * S + CC * xz; + + R(1, 0) = n.z * S + CC * xy; + R(1, 1) = 1 + CC * (yy - 1); + R(1, 2) = -n.x * S + CC * yz; + + R(2, 0) = -n.y * S + CC * xz; + R(2, 1) = n.x * S + CC * yz; + R(2, 2) = 1 + CC * (zz - 1); + + return R; +} + +PX_FOUNDATION_API void integrateTransform(const PxTransform& curTrans, const PxVec3& linvel, const PxVec3& angvel, + PxReal timeStep, PxTransform& result); + +PX_INLINE void computeBasis(const PxVec3& dir, PxVec3& right, PxVec3& up) +{ + // Derive two remaining vectors + if(PxAbs(dir.y) <= 0.9999f) + { + right = PxVec3(dir.z, 0.0f, -dir.x); + right.normalize(); + + // PT: normalize not needed for 'up' because dir & right are unit vectors, + // and by construction the angle between them is 90 degrees (i.e. sin(angle)=1) + up = PxVec3(dir.y * right.z, dir.z * right.x - dir.x * right.z, -dir.y * right.x); + } + else + { + right = PxVec3(1.0f, 0.0f, 0.0f); + + up = PxVec3(0.0f, dir.z, -dir.y); + up.normalize(); + } +} + +PX_INLINE void computeBasis(const PxVec3& p0, const PxVec3& p1, PxVec3& dir, PxVec3& right, PxVec3& up) +{ + // Compute the new direction vector + dir = p1 - p0; + dir.normalize(); + + // Derive two remaining vectors + computeBasis(dir, right, up); +} + +PX_FORCE_INLINE bool isAlmostZero(const PxVec3& v) +{ + if(PxAbs(v.x) > 1e-6f || PxAbs(v.y) > 1e-6f || PxAbs(v.z) > 1e-6f) + return false; + return true; +} + +} // namespace shdfnd +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsMutex.h b/src/PhysX/physx/source/foundation/include/PsMutex.h new file mode 100644 index 000000000..675f6bf04 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsMutex.h @@ -0,0 +1,182 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSMUTEX_H +#define PSFOUNDATION_PSMUTEX_H + +#include "PsAllocator.h" + +/* + * This inclusion is a best known fix for gcc 4.4.1 error: + * Creating object file for apex/src/PsAllocator.cpp ... + * In file included from apex/include/PsFoundation.h:30, + * from apex/src/PsAllocator.cpp:26: + * apex/include/PsMutex.h: In constructor 'physx::shdfnd::MutexT::MutexT(const Alloc&)': + * apex/include/PsMutex.h:92: error: no matching function for call to 'operator new(unsigned int, + * physx::shdfnd::MutexImpl*&)' + * :0: note: candidates are: void* operator new(unsigned int) + */ +#include + +namespace physx +{ +namespace shdfnd +{ +class PX_FOUNDATION_API MutexImpl +{ + public: + /** + The constructor for Mutex creates a mutex. It is initially unlocked. + */ + MutexImpl(); + + /** + The destructor for Mutex deletes the mutex. + */ + ~MutexImpl(); + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method blocks until the mutex is + unlocked. + */ + void lock(); + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method returns false without blocking. + */ + bool trylock(); + + /** + Release (unlock) the mutex. + */ + void unlock(); + + /** + Size of this class. + */ + static uint32_t getSize(); +}; + +template > +class MutexT : protected Alloc +{ + PX_NOCOPY(MutexT) + public: + class ScopedLock + { + MutexT& mMutex; + PX_NOCOPY(ScopedLock) + public: + PX_INLINE ScopedLock(MutexT& mutex) : mMutex(mutex) + { + mMutex.lock(); + } + PX_INLINE ~ScopedLock() + { + mMutex.unlock(); + } + }; + + /** + The constructor for Mutex creates a mutex. It is initially unlocked. + */ + MutexT(const Alloc& alloc = Alloc()) : Alloc(alloc) + { + mImpl = reinterpret_cast(Alloc::allocate(MutexImpl::getSize(), __FILE__, __LINE__)); + PX_PLACEMENT_NEW(mImpl, MutexImpl)(); + } + + /** + The destructor for Mutex deletes the mutex. + */ + ~MutexT() + { + mImpl->~MutexImpl(); + Alloc::deallocate(mImpl); + } + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method blocks until the mutex is + unlocked. + */ + void lock() const + { + mImpl->lock(); + } + + /** + Acquire (lock) the mutex. If the mutex is already locked + by another thread, this method returns false without blocking, + returns true if lock is successfully acquired + */ + bool trylock() const + { + return mImpl->trylock(); + } + + /** + Release (unlock) the mutex, the calling thread must have + previously called lock() or method will error + */ + void unlock() const + { + mImpl->unlock(); + } + + private: + MutexImpl* mImpl; +}; + +class PX_FOUNDATION_API ReadWriteLock +{ + PX_NOCOPY(ReadWriteLock) + public: + ReadWriteLock(); + ~ReadWriteLock(); + + // "takeLock" can only be false if the thread already holds the mutex, e.g. if it already acquired the write lock + void lockReader(bool takeLock); + void lockWriter(); + + void unlockReader(); + void unlockWriter(); + + private: + class ReadWriteLockImpl* mImpl; +}; + +typedef MutexT<> Mutex; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSMUTEX_H diff --git a/src/PhysX/physx/source/foundation/include/PsPool.h b/src/PhysX/physx/source/foundation/include/PsPool.h new file mode 100644 index 000000000..a6f3a10fb --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsPool.h @@ -0,0 +1,298 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSPOOL_H +#define PSFOUNDATION_PSPOOL_H + +#include "PsArray.h" +#include "PsSort.h" +#include "PsBasicTemplates.h" +#include "PsInlineArray.h" + +namespace physx +{ +namespace shdfnd +{ + +/*! +Simple allocation pool +*/ +template ::Type> +class PoolBase : public UserAllocated, public Alloc +{ + PX_NOCOPY(PoolBase) + protected: + PoolBase(const Alloc& alloc, uint32_t elementsPerSlab, uint32_t slabSize) + : Alloc(alloc), mSlabs(alloc), mElementsPerSlab(elementsPerSlab), mUsed(0), mSlabSize(slabSize), mFreeElement(0) + { + PX_COMPILE_TIME_ASSERT(sizeof(T) >= sizeof(size_t)); + } + + public: + ~PoolBase() + { + if(mUsed) + disposeElements(); + + for(void** slabIt = mSlabs.begin(), *slabEnd = mSlabs.end(); slabIt != slabEnd; ++slabIt) + Alloc::deallocate(*slabIt); + } + + // Allocate space for single object + PX_INLINE T* allocate() + { + if(mFreeElement == 0) + allocateSlab(); + T* p = reinterpret_cast(mFreeElement); + mFreeElement = mFreeElement->mNext; + mUsed++; +/** +Mark a specified amount of memory with 0xcd pattern. This is used to check that the meta data +definition for serialized classes is complete in checked builds. +*/ +#if PX_CHECKED + for(uint32_t i = 0; i < sizeof(T); ++i) + reinterpret_cast(p)[i] = 0xcd; +#endif + return p; + } + + // Put space for a single element back in the lists + PX_INLINE void deallocate(T* p) + { + if(p) + { + PX_ASSERT(mUsed); + mUsed--; + push(reinterpret_cast(p)); + } + } + + PX_INLINE T* construct() + { + T* t = allocate(); + return t ? new (t) T() : 0; + } + + template + PX_INLINE T* construct(A1& a) + { + T* t = allocate(); + return t ? new (t) T(a) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b) + { + T* t = allocate(); + return t ? new (t) T(a, b) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c) + { + T* t = allocate(); + return t ? new (t) T(a, b, c) : 0; + } + + template + PX_INLINE T* construct(A1* a, A2& b, A3& c) + { + T* t = allocate(); + return t ? new (t) T(a, b, c) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c, A4& d) + { + T* t = allocate(); + return t ? new (t) T(a, b, c, d) : 0; + } + + template + PX_INLINE T* construct(A1& a, A2& b, A3& c, A4& d, A5& e) + { + T* t = allocate(); + return t ? new (t) T(a, b, c, d, e) : 0; + } + + PX_INLINE void destroy(T* const p) + { + if(p) + { + p->~T(); + deallocate(p); + } + } + + protected: + struct FreeList + { + FreeList* mNext; + }; + + // All the allocated slabs, sorted by pointer + InlineArray mSlabs; + + uint32_t mElementsPerSlab; + uint32_t mUsed; + uint32_t mSlabSize; + + FreeList* mFreeElement; // Head of free-list + + // Helper function to get bitmap of allocated elements + + void push(FreeList* p) + { + p->mNext = mFreeElement; + mFreeElement = p; + } + + // Allocate a slab and segregate it into the freelist + void allocateSlab() + { + T* slab = reinterpret_cast(Alloc::allocate(mSlabSize, __FILE__, __LINE__)); + + mSlabs.pushBack(slab); + + // Build a chain of nodes for the freelist + T* it = slab + mElementsPerSlab; + while(--it >= slab) + push(reinterpret_cast(it)); + } + + /* + Cleanup method. Go through all active slabs and call destructor for live objects, + then free their memory + */ + void disposeElements() + { + Array freeNodes(*this); + while(mFreeElement) + { + freeNodes.pushBack(mFreeElement); + mFreeElement = mFreeElement->mNext; + } + Alloc& alloc(*this); + sort(freeNodes.begin(), freeNodes.size(), Less(), alloc); + sort(mSlabs.begin(), mSlabs.size(), Less(), alloc); + + typename Array::Iterator slabIt = mSlabs.begin(), slabEnd = mSlabs.end(); + for(typename Array::Iterator freeIt = freeNodes.begin(); slabIt != slabEnd; ++slabIt) + { + for(T* tIt = reinterpret_cast(*slabIt), *tEnd = tIt + mElementsPerSlab; tIt != tEnd; ++tIt) + { + if(freeIt != freeNodes.end() && *freeIt == tIt) + ++freeIt; + else + tIt->~T(); + } + } + } + + /* + Go through all slabs and call destructor if the slab is empty + */ + void releaseEmptySlabs() + { + Array freeNodes(*this); + Array slabNodes(mSlabs, *this); + while(mFreeElement) + { + freeNodes.pushBack(mFreeElement); + mFreeElement = mFreeElement->mNext; + } + + typename Array::Iterator freeIt = freeNodes.begin(), freeEnd = freeNodes.end(), + lastCheck = freeNodes.end() - mElementsPerSlab; + + if(freeNodes.size() > mElementsPerSlab) + { + Alloc& alloc(*this); + sort(freeNodes.begin(), freeNodes.size(), Less(), alloc); + sort(slabNodes.begin(), slabNodes.size(), Less(), alloc); + + mSlabs.clear(); + for(void** slabIt = slabNodes.begin(), *slabEnd = slabNodes.end(); slabIt != slabEnd; ++slabIt) + { + while((freeIt < lastCheck) && (*slabIt > (*freeIt))) + { + push(reinterpret_cast(*freeIt)); + freeIt++; + } + + if(*slabIt == (*freeIt)) // the slab's first element in freeList + { + const size_t endSlabAddress = size_t(*slabIt) + mSlabSize; + const size_t endFreeAddress = size_t(*(freeIt + mElementsPerSlab - 1)); + if(endFreeAddress + sizeof(T) == endSlabAddress) + { // all slab's element in freeList + Alloc::deallocate(*slabIt); + freeIt += mElementsPerSlab; + continue; + } + } + + mSlabs.pushBack(*slabIt); + } + } + + while(freeIt != freeEnd) + { + push(reinterpret_cast(*freeIt)); + ++freeIt; + } + } +}; + +// original pool implementation +template ::Type> +class Pool : public PoolBase +{ + public: + Pool(const Alloc& alloc = Alloc(), uint32_t elementsPerSlab = 32) + : PoolBase(alloc, elementsPerSlab, elementsPerSlab * sizeof(T)) + { + } +}; + +// allows specification of the slab size instead of the occupancy +template ::Type> +class Pool2 : public PoolBase +{ + public: + Pool2(const Alloc& alloc = Alloc()) : PoolBase(alloc, slabSize / sizeof(T), slabSize) + { + } +}; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSPOOL_H diff --git a/src/PhysX/physx/source/foundation/include/PsSList.h b/src/PhysX/physx/source/foundation/include/PsSList.h new file mode 100644 index 000000000..b03bc9b96 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsSList.h @@ -0,0 +1,140 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSLIST_H +#define PSFOUNDATION_PSSLIST_H + +#include "foundation/Px.h" +#include "foundation/PxAssert.h" +#include "PsAlignedMalloc.h" + +#if PX_P64_FAMILY +#define PX_SLIST_ALIGNMENT 16 +#else +#define PX_SLIST_ALIGNMENT 8 +#endif + +namespace physx +{ +namespace shdfnd +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +#if !PX_GCC_FAMILY +__declspec(align(PX_SLIST_ALIGNMENT)) +#endif + class SListEntry +{ + friend struct SListImpl; + + public: + SListEntry() : mNext(NULL) + { + PX_ASSERT((size_t(this) & (PX_SLIST_ALIGNMENT - 1)) == 0); + } + + // Only use on elements returned by SList::flush() + // because the operation is not atomic. + SListEntry* next() + { + return mNext; + } + + private: + SListEntry* mNext; +} +#if PX_GCC_FAMILY +__attribute__((aligned(PX_SLIST_ALIGNMENT))); +#else +; +#endif + +#if PX_VC +#pragma warning(pop) +#endif + +// template-less implementation +struct PX_FOUNDATION_API SListImpl +{ + SListImpl(); + ~SListImpl(); + void push(SListEntry* entry); + SListEntry* pop(); + SListEntry* flush(); + static uint32_t getSize(); +}; + +template > +class SListT : protected Alloc +{ + public: + SListT(const Alloc& alloc = Alloc()) : Alloc(alloc) + { + mImpl = reinterpret_cast(Alloc::allocate(SListImpl::getSize(), __FILE__, __LINE__)); + PX_ASSERT((size_t(mImpl) & (PX_SLIST_ALIGNMENT - 1)) == 0); + PX_PLACEMENT_NEW(mImpl, SListImpl)(); + } + ~SListT() + { + mImpl->~SListImpl(); + Alloc::deallocate(mImpl); + } + + // pushes a new element to the list + void push(SListEntry& entry) + { + mImpl->push(&entry); + } + + // pops an element from the list + SListEntry* pop() + { + return mImpl->pop(); + } + + // removes all items from list, returns pointer to first element + SListEntry* flush() + { + return mImpl->flush(); + } + + private: + SListImpl* mImpl; +}; + +typedef SListT<> SList; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSSLIST_H diff --git a/src/PhysX/physx/source/foundation/include/PsSocket.h b/src/PhysX/physx/source/foundation/include/PsSocket.h new file mode 100644 index 000000000..3b3d68f6b --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsSocket.h @@ -0,0 +1,186 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSOCKET_H +#define PSFOUNDATION_PSSOCKET_H + +#include "PsUserAllocated.h" + +namespace physx +{ +namespace shdfnd +{ +/** +Socket abstraction API +*/ + +class PX_FOUNDATION_API Socket : public UserAllocated +{ + public: + static const uint32_t DEFAULT_BUFFER_SIZE; + + Socket(bool inEnableBuffering = true, bool blocking = true); + + virtual ~Socket(); + + /*! + Opens a network socket for input and/or output + + \param host + Name of the host to connect to. This can be an IP, URL, etc + + \param port + The port to connect to on the remote host + + \param timeout + Timeout in ms until the connection must be established. + + \return + True if the connection was successful, false otherwise + */ + bool connect(const char* host, uint16_t port, uint32_t timeout = 1000); + + /*! + Opens a network socket for input and/or output as a server. Put the connection in listening mode + + \param port + The port on which the socket listens + */ + bool listen(uint16_t port); + + /*! + Accept a connection on a socket that is in listening mode + + \note + This method only supports a single connection client. Additional clients + that connect to the listening port will overwrite the existing socket handle. + + \param block + whether or not the call should block + + \return whether a connection was established + */ + bool accept(bool block); + + /*! + Disconnects an open socket + */ + void disconnect(); + + /*! + Returns whether the socket is currently open (connected) or not. + + \return + True if the socket is connected, false otherwise + */ + bool isConnected() const; + + /*! + Returns the name of the connected host. This is the same as the string + that was supplied to the connect call. + + \return + The name of the connected host + */ + const char* getHost() const; + + /*! + Returns the port of the connected host. This is the same as the port + that was supplied to the connect call. + + \return + The port of the connected host + */ + uint16_t getPort() const; + + /*! + Flushes the output stream. Until the stream is flushed, there is no + guarantee that the written data has actually reached the destination + storage. Flush forces all buffered data to be sent to the output. + + \note flush always blocks. If the socket is in non-blocking mode, this will result + the thread spinning. + + \return + True if the flush was successful, false otherwise + */ + bool flush(); + + /*! + Writes data to the output stream. + + \param data + Pointer to a block of data to write to the stream + + \param length + Amount of data to write, in bytes + + \return + Number of bytes actually written. This could be lower than length if the socket is non-blocking. + */ + + uint32_t write(const uint8_t* data, uint32_t length); + + /*! + Reads data from the output stream. + + \param data + Pointer to a buffer where the read data will be stored. + + \param length + Amount of data to read, in bytes. + + \return + Number of bytes actually read. This could be lower than length if the stream end is + encountered or the socket is non-blocking. + */ + uint32_t read(uint8_t* data, uint32_t length); + + /*! + Sets blocking mode of the socket. + Socket must be connected, otherwise calling this method won't take any effect. + */ + void setBlocking(bool blocking); + + /*! + Returns whether read/write/flush calls to the socket are blocking. + + \return + True if the socket is blocking. + */ + bool isBlocking() const; + + private: + class SocketImpl* mImpl; +}; + +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSSOCKET_H diff --git a/src/PhysX/physx/source/foundation/include/PsSort.h b/src/PhysX/physx/source/foundation/include/PsSort.h new file mode 100644 index 000000000..8beec55ec --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsSort.h @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSORT_H +#define PSFOUNDATION_PSSORT_H + +/** \addtogroup foundation +@{ +*/ + +#include "PsSortInternals.h" +#include "PsAlloca.h" + +#define PX_SORT_PARANOIA PX_DEBUG + +/** +\brief Sorts an array of objects in ascending order, assuming +that the predicate implements the < operator: + +\see Less, Greater +*/ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4706) // disable the warning that we did an assignment within a conditional expression, as +// this was intentional. +#endif + +namespace physx +{ +namespace shdfnd +{ +template +void sort(T* elements, uint32_t count, const Predicate& compare, const Allocator& inAllocator, + const uint32_t initialStackSize = 32) +{ + static const uint32_t SMALL_SORT_CUTOFF = 5; // must be >= 3 since we need 3 for median + + PX_ALLOCA(stackMem, int32_t, initialStackSize); + internal::Stack stack(stackMem, initialStackSize, inAllocator); + + int32_t first = 0, last = int32_t(count - 1); + if(last > first) + { + for(;;) + { + while(last > first) + { + PX_ASSERT(first >= 0 && last < int32_t(count)); + if(uint32_t(last - first) < SMALL_SORT_CUTOFF) + { + internal::smallSort(elements, first, last, compare); + break; + } + else + { + const int32_t partIndex = internal::partition(elements, first, last, compare); + + // push smaller sublist to minimize stack usage + if((partIndex - first) < (last - partIndex)) + { + stack.push(first, partIndex - 1); + first = partIndex + 1; + } + else + { + stack.push(partIndex + 1, last); + last = partIndex - 1; + } + } + } + + if(stack.empty()) + break; + + stack.pop(first, last); + } + } +#if PX_SORT_PARANOIA + for(uint32_t i = 1; i < count; i++) + PX_ASSERT(!compare(elements[i], elements[i - 1])); +#endif +} + +template +void sort(T* elements, uint32_t count, const Predicate& compare) +{ + sort(elements, count, compare, typename shdfnd::AllocatorTraits::Type()); +} + +template +void sort(T* elements, uint32_t count) +{ + sort(elements, count, shdfnd::Less(), typename shdfnd::AllocatorTraits::Type()); +} + +} // namespace shdfnd +} // namespace physx + +#if PX_VC +#pragma warning(pop) +#endif + +#endif // #ifndef PSFOUNDATION_PSSORT_H diff --git a/src/PhysX/physx/source/foundation/include/PsSortInternals.h b/src/PhysX/physx/source/foundation/include/PsSortInternals.h new file mode 100644 index 000000000..54d9b4480 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsSortInternals.h @@ -0,0 +1,188 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSORTINTERNALS_H +#define PSFOUNDATION_PSSORTINTERNALS_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxAssert.h" +#include "foundation/PxIntrinsics.h" +#include "PsBasicTemplates.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace shdfnd +{ +namespace internal +{ +template +PX_INLINE void median3(T* elements, int32_t first, int32_t last, Predicate& compare) +{ + /* + This creates sentinels because we know there is an element at the start minimum(or equal) + than the pivot and an element at the end greater(or equal) than the pivot. Plus the + median of 3 reduces the chance of degenerate behavour. + */ + + int32_t mid = (first + last) / 2; + + if(compare(elements[mid], elements[first])) + swap(elements[first], elements[mid]); + + if(compare(elements[last], elements[first])) + swap(elements[first], elements[last]); + + if(compare(elements[last], elements[mid])) + swap(elements[mid], elements[last]); + + // keep the pivot at last-1 + swap(elements[mid], elements[last - 1]); +} + +template +PX_INLINE int32_t partition(T* elements, int32_t first, int32_t last, Predicate& compare) +{ + median3(elements, first, last, compare); + + /* + WARNING: using the line: + + T partValue = elements[last-1]; + + and changing the scan loops to: + + while(comparator.greater(partValue, elements[++i])); + while(comparator.greater(elements[--j], partValue); + + triggers a compiler optimizer bug on xenon where it stores a double to the stack for partValue + then loads it as a single...:-( + */ + + int32_t i = first; // we know first is less than pivot(but i gets pre incremented) + int32_t j = last - 1; // pivot is in last-1 (but j gets pre decremented) + + for(;;) + { + while(compare(elements[++i], elements[last - 1])) + ; + while(compare(elements[last - 1], elements[--j])) + ; + + if(i >= j) + break; + + PX_ASSERT(i <= last && j >= first); + swap(elements[i], elements[j]); + } + // put the pivot in place + + PX_ASSERT(i <= last && first <= (last - 1)); + swap(elements[i], elements[last - 1]); + + return i; +} + +template +PX_INLINE void smallSort(T* elements, int32_t first, int32_t last, Predicate& compare) +{ + // selection sort - could reduce to fsel on 360 with floats. + + for(int32_t i = first; i < last; i++) + { + int32_t m = i; + for(int32_t j = i + 1; j <= last; j++) + if(compare(elements[j], elements[m])) + m = j; + + if(m != i) + swap(elements[m], elements[i]); + } +} + +template +class Stack +{ + Allocator mAllocator; + uint32_t mSize, mCapacity; + int32_t* mMemory; + bool mRealloc; + + public: + Stack(int32_t* memory, uint32_t capacity, const Allocator& inAllocator) + : mAllocator(inAllocator), mSize(0), mCapacity(capacity), mMemory(memory), mRealloc(false) + { + } + ~Stack() + { + if(mRealloc) + mAllocator.deallocate(mMemory); + } + + void grow() + { + mCapacity *= 2; + int32_t* newMem = + reinterpret_cast(mAllocator.allocate(sizeof(int32_t) * mCapacity, __FILE__, __LINE__)); + intrinsics::memCopy(newMem, mMemory, mSize * sizeof(int32_t)); + if(mRealloc) + mAllocator.deallocate(mMemory); + mRealloc = true; + mMemory = newMem; + } + + PX_INLINE void push(int32_t start, int32_t end) + { + if(mSize >= mCapacity - 1) + grow(); + mMemory[mSize++] = start; + mMemory[mSize++] = end; + } + + PX_INLINE void pop(int32_t& start, int32_t& end) + { + PX_ASSERT(!empty()); + end = mMemory[--mSize]; + start = mMemory[--mSize]; + } + + PX_INLINE bool empty() + { + return mSize == 0; + } +}; +} // namespace internal + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSSORTINTERNALS_H diff --git a/src/PhysX/physx/source/foundation/include/PsString.h b/src/PhysX/physx/source/foundation/include/PsString.h new file mode 100644 index 000000000..eeb1b2444 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsString.h @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSTRING_H +#define PSFOUNDATION_PSSTRING_H + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxSimpleTypes.h" +#include + +namespace physx +{ +namespace shdfnd +{ + +// the following functions have C99 semantics. Note that C99 requires for snprintf and vsnprintf: +// * the resulting string is always NULL-terminated regardless of truncation. +// * in the case of truncation the return value is the number of characters that would have been created. + +PX_FOUNDATION_API int32_t sscanf(const char* buffer, const char* format, ...); +PX_FOUNDATION_API int32_t strcmp(const char* str1, const char* str2); +PX_FOUNDATION_API int32_t strncmp(const char* str1, const char* str2, size_t count); +PX_FOUNDATION_API int32_t snprintf(char* dst, size_t dstSize, const char* format, ...); +PX_FOUNDATION_API int32_t vsnprintf(char* dst, size_t dstSize, const char* src, va_list arg); + +// strlcat and strlcpy have BSD semantics: +// * dstSize is always the size of the destination buffer +// * the resulting string is always NULL-terminated regardless of truncation +// * in the case of truncation the return value is the length of the string that would have been created + +PX_FOUNDATION_API size_t strlcat(char* dst, size_t dstSize, const char* src); +PX_FOUNDATION_API size_t strlcpy(char* dst, size_t dstSize, const char* src); + +// case-insensitive string comparison +PX_FOUNDATION_API int32_t stricmp(const char* str1, const char* str2); +PX_FOUNDATION_API int32_t strnicmp(const char* str1, const char* str2, size_t count); + +// in-place string case conversion +PX_FOUNDATION_API void strlwr(char* str); +PX_FOUNDATION_API void strupr(char* str); + +/** +\brief The maximum supported formatted output string length +(number of characters after replacement). + +@see printFormatted() +*/ +static const size_t MAX_PRINTFORMATTED_LENGTH = 1024; + +/** +\brief Prints the formatted data, trying to make sure it's visible to the app programmer + +@see NS_MAX_PRINTFORMATTED_LENGTH +*/ +PX_FOUNDATION_API void printFormatted(const char*, ...); + +/** +\brief Prints the string literally (does not consume % specifier), trying to make sure it's visible to the app +programmer +*/ +PX_FOUNDATION_API void printString(const char*); +} +} +#endif // #ifndef PSFOUNDATION_PSSTRING_H diff --git a/src/PhysX/physx/source/foundation/include/PsSync.h b/src/PhysX/physx/source/foundation/include/PsSync.h new file mode 100644 index 000000000..f8d73b36f --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsSync.h @@ -0,0 +1,138 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSSYNC_H +#define PSFOUNDATION_PSSYNC_H + +#include "PsAllocator.h" + +namespace physx +{ +namespace shdfnd +{ +/*! +Implementation notes: +* - Calling set() on an already signaled Sync does not change its state. +* - Calling reset() on an already reset Sync does not change its state. +* - Calling set() on a reset Sync wakes all waiting threads (potential for thread contention). +* - Calling wait() on an already signaled Sync will return true immediately. +* - NOTE: be careful when pulsing an event with set() followed by reset(), because a +* thread that is not waiting on the event will miss the signal. +*/ +class PX_FOUNDATION_API SyncImpl +{ + public: + static const uint32_t waitForever = 0xffffffff; + + SyncImpl(); + + ~SyncImpl(); + + /** Wait on the object for at most the given number of ms. Returns + * true if the object is signaled. Sync::waitForever will block forever + * or until the object is signaled. + */ + + bool wait(uint32_t milliseconds = waitForever); + + /** Signal the synchronization object, waking all threads waiting on it */ + + void set(); + + /** Reset the synchronization object */ + + void reset(); + + /** + Size of this class. + */ + static uint32_t getSize(); +}; + +/*! +Implementation notes: +* - Calling set() on an already signaled Sync does not change its state. +* - Calling reset() on an already reset Sync does not change its state. +* - Calling set() on a reset Sync wakes all waiting threads (potential for thread contention). +* - Calling wait() on an already signaled Sync will return true immediately. +* - NOTE: be careful when pulsing an event with set() followed by reset(), because a +* thread that is not waiting on the event will miss the signal. +*/ +template > +class SyncT : protected Alloc +{ + public: + static const uint32_t waitForever = SyncImpl::waitForever; + + SyncT(const Alloc& alloc = Alloc()) : Alloc(alloc) + { + mImpl = reinterpret_cast(Alloc::allocate(SyncImpl::getSize(), __FILE__, __LINE__)); + PX_PLACEMENT_NEW(mImpl, SyncImpl)(); + } + + ~SyncT() + { + mImpl->~SyncImpl(); + Alloc::deallocate(mImpl); + } + + /** Wait on the object for at most the given number of ms. Returns + * true if the object is signaled. Sync::waitForever will block forever + * or until the object is signaled. + */ + + bool wait(uint32_t milliseconds = SyncImpl::waitForever) + { + return mImpl->wait(milliseconds); + } + + /** Signal the synchronization object, waking all threads waiting on it */ + + void set() + { + mImpl->set(); + } + + /** Reset the synchronization object */ + + void reset() + { + mImpl->reset(); + } + + private: + class SyncImpl* mImpl; +}; + +typedef SyncT<> Sync; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSSYNC_H diff --git a/src/PhysX/physx/source/foundation/include/PsTempAllocator.h b/src/PhysX/physx/source/foundation/include/PsTempAllocator.h new file mode 100644 index 000000000..6594bafad --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsTempAllocator.h @@ -0,0 +1,62 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSTEMPALLOCATOR_H +#define PSFOUNDATION_PSTEMPALLOCATOR_H + +#include "PsAllocator.h" + +namespace physx +{ +namespace shdfnd +{ +union TempAllocatorChunk +{ + TempAllocatorChunk() : mNext(0) + { + } + TempAllocatorChunk* mNext; // while chunk is free + uint32_t mIndex; // while chunk is allocated + uint8_t mPad[16]; // 16 byte aligned allocations +}; + +class TempAllocator +{ + public: + PX_FORCE_INLINE TempAllocator(const char* = 0) + { + } + PX_FOUNDATION_API void* allocate(size_t size, const char* file, int line); + PX_FOUNDATION_API void deallocate(void* ptr); +}; + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSTEMPALLOCATOR_H diff --git a/src/PhysX/physx/source/foundation/include/PsThread.h b/src/PhysX/physx/source/foundation/include/PsThread.h new file mode 100644 index 000000000..c9304ca7a --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsThread.h @@ -0,0 +1,384 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSTHREAD_H +#define PSFOUNDATION_PSTHREAD_H + +#include "PsUserAllocated.h" + +// dsequeira: according to existing comment here (David Black would be my guess) +// "This is useful to reduce bus contention on tight spin locks. And it needs +// to be a macro as the xenon compiler often ignores even __forceinline." What's not +// clear is why a pause function needs inlining...? (TODO: check with XBox team) + +// todo: these need to go somewhere else + +#if PX_WINDOWS_FAMILY || PX_XBOXONE +#define PxSpinLockPause() __asm pause +#elif PX_LINUX || PX_ANDROID || PX_PS4 || PX_APPLE_FAMILY || PX_SWITCH +#define PxSpinLockPause() asm("nop") +#else +#error "Platform not supported!" +#endif + +namespace physx +{ +namespace shdfnd +{ +struct ThreadPriority // todo: put in some other header file +{ + enum Enum + { + /** + \brief High priority + */ + eHIGH = 0, + + /** + \brief Above Normal priority + */ + eABOVE_NORMAL = 1, + + /** + \brief Normal/default priority + */ + eNORMAL = 2, + + /** + \brief Below Normal priority + */ + eBELOW_NORMAL = 3, + + /** + \brief Low priority. + */ + eLOW = 4, + eFORCE_DWORD = 0xffFFffFF + }; +}; + +class Runnable +{ + public: + Runnable() + { + } + virtual ~Runnable() + { + } + virtual void execute(void) + { + } +}; + +class PX_FOUNDATION_API ThreadImpl +{ + public: + typedef size_t Id; // space for a pointer or an integer + typedef void* (*ExecuteFn)(void*); + + static uint32_t getDefaultStackSize(); + static Id getId(); + + /** + Construct (but do not start) the thread object. The OS thread object will not be created + until start() is called. Executes in the context + of the spawning thread. + */ + + ThreadImpl(); + + /** + Construct and start the the thread, passing the given arg to the given fn. (pthread style) + */ + + ThreadImpl(ExecuteFn fn, void* arg, const char* name); + + /** + Deallocate all resources associated with the thread. Should be called in the + context of the spawning thread. + */ + + ~ThreadImpl(); + + /** + Create the OS thread and start it running. Called in the context of the spawning thread. + If an affinity mask has previously been set then it will be applied after the + thread has been created. + */ + + void start(uint32_t stackSize, Runnable* r); + + /** + Violently kill the current thread. Blunt instrument, not recommended since + it can leave all kinds of things unreleased (stack, memory, mutexes...) Should + be called in the context of the spawning thread. + */ + + void kill(); + + /** + Stop the thread. Signals the spawned thread that it should stop, so the + thread should check regularly + */ + + void signalQuit(); + + /** + Wait for a thread to stop. Should be called in the context of the spawning + thread. Returns false if the thread has not been started. + */ + + bool waitForQuit(); + + /** + check whether the thread is signalled to quit. Called in the context of the + spawned thread. + */ + + bool quitIsSignalled(); + + /** + Cleanly shut down this thread. Called in the context of the spawned thread. + */ + void quit(); + + /** + Change the affinity mask for this thread. The mask is a platform + specific value. + + On Windows, Linux, PS4, XboxOne and Switch platforms, each set mask bit represents + the index of a logical processor that the OS may schedule thread execution on. + Bits outside the range of valid logical processors may be ignored or cause + the function to return an error. + + On Apple platforms, this function has no effect. + + If the thread has not yet been started then the mask is stored + and applied when the thread is started. + + If the thread has already been started then this method returns the + previous affinity mask on success, otherwise it returns zero. + */ + uint32_t setAffinityMask(uint32_t mask); + + static ThreadPriority::Enum getPriority(Id threadId); + + /** Set thread priority. */ + void setPriority(ThreadPriority::Enum prio); + + /** set the thread's name */ + void setName(const char* name); + + /** Put the current thread to sleep for the given number of milliseconds */ + static void sleep(uint32_t ms); + + /** Yield the current thread's slot on the CPU */ + static void yield(); + + /** Return the number of physical cores (does not include hyper-threaded cores), returns 0 on failure */ + static uint32_t getNbPhysicalCores(); + + /** + Size of this class. + */ + static uint32_t getSize(); +}; + +/** +Thread abstraction API +*/ +template > +class ThreadT : protected Alloc, public UserAllocated, public Runnable +{ + public: + typedef ThreadImpl::Id Id; // space for a pointer or an integer + + /** + Construct (but do not start) the thread object. Executes in the context + of the spawning thread + */ + ThreadT(const Alloc& alloc = Alloc()) : Alloc(alloc) + { + mImpl = reinterpret_cast(Alloc::allocate(ThreadImpl::getSize(), __FILE__, __LINE__)); + PX_PLACEMENT_NEW(mImpl, ThreadImpl)(); + } + + /** + Construct and start the the thread, passing the given arg to the given fn. (pthread style) + */ + ThreadT(ThreadImpl::ExecuteFn fn, void* arg, const char* name, const Alloc& alloc = Alloc()) : Alloc(alloc) + { + mImpl = reinterpret_cast(Alloc::allocate(ThreadImpl::getSize(), __FILE__, __LINE__)); + PX_PLACEMENT_NEW(mImpl, ThreadImpl)(fn, arg, name); + } + + /** + Deallocate all resources associated with the thread. Should be called in the + context of the spawning thread. + */ + virtual ~ThreadT() + { + mImpl->~ThreadImpl(); + Alloc::deallocate(mImpl); + } + + /** + start the thread running. Called in the context of the spawning thread. + */ + + void start(uint32_t stackSize = ThreadImpl::getDefaultStackSize()) + { + mImpl->start(stackSize, this); + } + + /** + Violently kill the current thread. Blunt instrument, not recommended since + it can leave all kinds of things unreleased (stack, memory, mutexes...) Should + be called in the context of the spawning thread. + */ + + void kill() + { + mImpl->kill(); + } + + /** + The virtual execute() method is the user defined function that will + run in the new thread. Called in the context of the spawned thread. + */ + + virtual void execute(void) + { + } + + /** + stop the thread. Signals the spawned thread that it should stop, so the + thread should check regularly + */ + + void signalQuit() + { + mImpl->signalQuit(); + } + + /** + Wait for a thread to stop. Should be called in the context of the spawning + thread. Returns false if the thread has not been started. + */ + + bool waitForQuit() + { + return mImpl->waitForQuit(); + } + + /** + check whether the thread is signalled to quit. Called in the context of the + spawned thread. + */ + + bool quitIsSignalled() + { + return mImpl->quitIsSignalled(); + } + + /** + Cleanly shut down this thread. Called in the context of the spawned thread. + */ + void quit() + { + mImpl->quit(); + } + + uint32_t setAffinityMask(uint32_t mask) + { + return mImpl->setAffinityMask(mask); + } + + static ThreadPriority::Enum getPriority(ThreadImpl::Id threadId) + { + return ThreadImpl::getPriority(threadId); + } + + /** Set thread priority. */ + void setPriority(ThreadPriority::Enum prio) + { + mImpl->setPriority(prio); + } + + /** set the thread's name */ + void setName(const char* name) + { + mImpl->setName(name); + } + + /** Put the current thread to sleep for the given number of milliseconds */ + static void sleep(uint32_t ms) + { + ThreadImpl::sleep(ms); + } + + /** Yield the current thread's slot on the CPU */ + static void yield() + { + ThreadImpl::yield(); + } + + static uint32_t getDefaultStackSize() + { + return ThreadImpl::getDefaultStackSize(); + } + + static ThreadImpl::Id getId() + { + return ThreadImpl::getId(); + } + + static uint32_t getNbPhysicalCores() + { + return ThreadImpl::getNbPhysicalCores(); + } + + private: + class ThreadImpl* mImpl; +}; + +typedef ThreadT<> Thread; + +PX_FOUNDATION_API uint32_t TlsAlloc(); +PX_FOUNDATION_API void TlsFree(uint32_t index); +PX_FOUNDATION_API void* TlsGet(uint32_t index); +PX_FOUNDATION_API size_t TlsGetValue(uint32_t index); +PX_FOUNDATION_API uint32_t TlsSet(uint32_t index, void* value); +PX_FOUNDATION_API uint32_t TlsSetValue(uint32_t index, size_t value); + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSTHREAD_H diff --git a/src/PhysX/physx/source/foundation/include/PsTime.h b/src/PhysX/physx/source/foundation/include/PsTime.h new file mode 100644 index 000000000..078e45365 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsTime.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSTIME_H +#define PSFOUNDATION_PSTIME_H + +#include "Ps.h" + +#if PX_LINUX || PX_ANDROID +#include +#endif + +namespace physx +{ +namespace shdfnd +{ + +struct CounterFrequencyToTensOfNanos +{ + uint64_t mNumerator; + uint64_t mDenominator; + CounterFrequencyToTensOfNanos(uint64_t inNum, uint64_t inDenom) : mNumerator(inNum), mDenominator(inDenom) + { + } + + // quite slow. + uint64_t toTensOfNanos(uint64_t inCounter) const + { + return (inCounter * mNumerator) / mDenominator; + } +}; + +class PX_FOUNDATION_API Time +{ + public: + typedef double Second; + static const uint64_t sNumTensOfNanoSecondsInASecond = 100000000; + // This is supposedly guaranteed to not change after system boot + // regardless of processors, speedstep, etc. + static const CounterFrequencyToTensOfNanos& getBootCounterFrequency(); + + static CounterFrequencyToTensOfNanos getCounterFrequency(); + + static uint64_t getCurrentCounterValue(); + + // SLOW!! + // Thar be a 64 bit divide in thar! + static uint64_t getCurrentTimeInTensOfNanoSeconds() + { + uint64_t ticks = getCurrentCounterValue(); + return getBootCounterFrequency().toTensOfNanos(ticks); + } + + Time(); + Second getElapsedSeconds(); + Second peekElapsedSeconds(); + Second getLastTime() const; + + private: +#if PX_LINUX || PX_ANDROID || PX_APPLE_FAMILY || PX_PS4 + Second mLastTime; +#else + int64_t mTickCount; +#endif +}; +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSTIME_H diff --git a/src/PhysX/physx/source/foundation/include/PsUserAllocated.h b/src/PhysX/physx/source/foundation/include/PsUserAllocated.h new file mode 100644 index 000000000..1ca80f193 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsUserAllocated.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUSERALLOCATED_H +#define PSFOUNDATION_PSUSERALLOCATED_H + +#include "PsAllocator.h" + +namespace physx +{ +namespace shdfnd +{ +/** +Provides new and delete using a UserAllocator. +Guarantees that 'delete x;' uses the UserAllocator too. +*/ +class UserAllocated +{ + public: + // PX_SERIALIZATION + PX_INLINE void* operator new(size_t, void* address) + { + return address; + } + //~PX_SERIALIZATION + // Matching operator delete to the above operator new. Don't ask me + // how this makes any sense - Nuernberger. + PX_INLINE void operator delete(void*, void*) + { + } + + template + PX_INLINE void* operator new(size_t size, Alloc alloc, const char* fileName, int line) + { + return alloc.allocate(size, fileName, line); + } + template + PX_INLINE void* operator new [](size_t size, Alloc alloc, const char* fileName, int line) + { return alloc.allocate(size, fileName, line); } + + // placement delete + template + PX_INLINE void operator delete(void* ptr, Alloc alloc, const char* fileName, int line) + { + PX_UNUSED(fileName); + PX_UNUSED(line); + alloc.deallocate(ptr); + } + template + PX_INLINE void operator delete [](void* ptr, Alloc alloc, const char* fileName, int line) + { + PX_UNUSED(fileName); + PX_UNUSED(line); + alloc.deallocate(ptr); + } PX_INLINE void + operator delete(void* ptr) + { + NonTrackingAllocator().deallocate(ptr); + } + PX_INLINE void operator delete [](void* ptr) + { NonTrackingAllocator().deallocate(ptr); } +}; +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSUSERALLOCATED_H diff --git a/src/PhysX/physx/source/foundation/include/PsUtilities.h b/src/PhysX/physx/source/foundation/include/PsUtilities.h new file mode 100644 index 000000000..d991be1fd --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsUtilities.h @@ -0,0 +1,165 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUTILITIES_H +#define PSFOUNDATION_PSUTILITIES_H + +#include "foundation/PxVec3.h" +#include "foundation/PxAssert.h" +#include "Ps.h" +#include "PsIntrinsics.h" +#include "PsBasicTemplates.h" + +namespace physx +{ +namespace shdfnd +{ +PX_INLINE char littleEndian() +{ + int i = 1; + return *(reinterpret_cast(&i)); +} + +// PT: checked casts +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 to32(PxU64 value) +{ + PX_ASSERT(value <= 0xffffffff); + return PxU32(value); +} +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 to16(PxU32 value) +{ + PX_ASSERT(value <= 0xffff); + return PxU16(value); +} +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU8 to8(PxU16 value) +{ + PX_ASSERT(value <= 0xff); + return PxU8(value); +} +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU8 to8(PxU32 value) +{ + PX_ASSERT(value <= 0xff); + return PxU8(value); +} +PX_CUDA_CALLABLE PX_FORCE_INLINE PxU8 to8(PxI32 value) +{ + PX_ASSERT(value <= 0xff); + PX_ASSERT(value >= 0); + return PxU8(value); +} +PX_CUDA_CALLABLE PX_FORCE_INLINE PxI8 toI8(PxU32 value) +{ + PX_ASSERT(value <= 0x7f); + return PxI8(value); +} + +/*! +Get number of elements in array +*/ +template +char (&ArraySizeHelper(T (&array)[N]))[N]; +#define PX_ARRAY_SIZE(_array) (sizeof(physx::shdfnd::ArraySizeHelper(_array))) + +/*! +Sort two elements using operator< + +On return x will be the smaller of the two +*/ +template +PX_CUDA_CALLABLE PX_FORCE_INLINE void order(T& x, T& y) +{ + if(y < x) + swap(x, y); +} + +// most architectures can do predication on real comparisons, and on VMX, it matters + +PX_CUDA_CALLABLE PX_FORCE_INLINE void order(PxReal& x, PxReal& y) +{ + PxReal newX = PxMin(x, y); + PxReal newY = PxMax(x, y); + x = newX; + y = newY; +} + +/*! +Sort two elements using operator< and also keep order +of any extra data +*/ +template +PX_CUDA_CALLABLE PX_FORCE_INLINE void order(T& x, T& y, E1& xe1, E1& ye1) +{ + if(y < x) + { + swap(x, y); + swap(xe1, ye1); + } +} + +#if PX_GCC_FAMILY && !PX_EMSCRIPTEN && !PX_LINUX +__attribute__((noreturn)) +#endif + PX_INLINE void debugBreak() +{ +#if PX_WINDOWS || PX_XBOXONE + __debugbreak(); +#elif PX_ANDROID + raise(SIGTRAP); // works better than __builtin_trap. Proper call stack and can be continued. +#elif PX_LINUX + asm("int $3"); +#elif PX_GCC_FAMILY + __builtin_trap(); +#else + PX_ASSERT(false); +#endif +} + +bool checkValid(const float&); +bool checkValid(const PxVec3&); +bool checkValid(const PxQuat&); +bool checkValid(const PxMat33&); +bool checkValid(const PxTransform&); +bool checkValid(const char*); + +// equivalent to std::max_element +template +inline const T* maxElement(const T* first, const T* last) +{ + const T* m = first; + for(const T* it = first + 1; it < last; ++it) + if(*m < *it) + m = it; + + return m; +} + +} // namespace shdfnd +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsVecMath.h b/src/PhysX/physx/source/foundation/include/PsVecMath.h new file mode 100644 index 000000000..680c350c6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecMath.h @@ -0,0 +1,1337 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECMATH_H +#define PSFOUNDATION_PSVECMATH_H + +#include "Ps.h" +#include "PsIntrinsics.h" +#include "foundation/PxVec3.h" +#include "foundation/PxVec4.h" +#include "foundation/PxMat33.h" +#include "foundation/PxUnionCast.h" + +// We can opt to use the scalar version of vectorised functions. +// This can catch type safety issues and might even work out more optimal on pc. +// It will also be useful for benchmarking and testing. +// NEVER submit with vector intrinsics deactivated without good reason. +// AM: deactivating SIMD for debug win64 just so autobuild will also exercise +// non-SIMD path, until a dedicated non-SIMD platform sich as Arm comes online. +// TODO: dima: reference all platforms with SIMD support here, +// all unknown/experimental cases should better default to NO SIMD. + +// enable/disable SIMD +#if !defined(PX_SIMD_DISABLED) +#if PX_INTEL_FAMILY && (!defined(__EMSCRIPTEN__) || defined(__SSE2__)) +#define COMPILE_VECTOR_INTRINSICS 1 +#elif PX_ANDROID&& PX_NEON +#define COMPILE_VECTOR_INTRINSICS 1 +#elif PX_IOS&& PX_NEON +#define COMPILE_VECTOR_INTRINSICS 1 +#elif PX_SWITCH +#define COMPILE_VECTOR_INTRINSICS 1 +#else +#define COMPILE_VECTOR_INTRINSICS 0 +#endif +#else +#define COMPILE_VECTOR_INTRINSICS 0 +#endif + +#if COMPILE_VECTOR_INTRINSICS && PX_INTEL_FAMILY&&(PX_UNIX_FAMILY || PX_PS4) +// only SSE2 compatible platforms should reach this +#if PX_EMSCRIPTEN +#include +#endif +#include +#endif + +#if COMPILE_VECTOR_INTRINSICS +#include "PsAoS.h" +#else +#include "PsVecMathAoSScalar.h" +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +// Basic AoS types are +// FloatV - 16-byte aligned representation of float. +// Vec3V - 16-byte aligned representation of PxVec3 stored as (x y z 0). +// Vec4V - 16-byte aligned representation of vector of 4 floats stored as (x y z w). +// BoolV - 16-byte aligned representation of vector of 4 bools stored as (x y z w). +// VecU32V - 16-byte aligned representation of 4 unsigned ints stored as (x y z w). +// VecI32V - 16-byte aligned representation of 4 signed ints stored as (x y z w). +// Mat33V - 16-byte aligned representation of any 3x3 matrix. +// Mat34V - 16-byte aligned representation of transformation matrix (rotation in col1,col2,col3 and translation in +// col4). +// Mat44V - 16-byte aligned representation of any 4x4 matrix. + +////////////////////////////////////////// +// Construct a simd type from a scalar type +////////////////////////////////////////// + +// FloatV +//(f,f,f,f) +PX_FORCE_INLINE FloatV FLoad(const PxF32 f); + +// Vec3V +//(f,f,f,0) +PX_FORCE_INLINE Vec3V V3Load(const PxF32 f); +//(f.x,f.y,f.z,0) +PX_FORCE_INLINE Vec3V V3LoadU(const PxVec3& f); +//(f.x,f.y,f.z,0), f must be 16-byte aligned +PX_FORCE_INLINE Vec3V V3LoadA(const PxVec3& f); +//(f.x,f.y,f.z,w_undefined), f must be 16-byte aligned +PX_FORCE_INLINE Vec3V V3LoadUnsafeA(const PxVec3& f); +//(f.x,f.y,f.z,0) +PX_FORCE_INLINE Vec3V V3LoadU(const PxF32* f); +//(f.x,f.y,f.z,0), f must be 16-byte aligned +PX_FORCE_INLINE Vec3V V3LoadA(const PxF32* f); + +// Vec4V +//(f,f,f,f) +PX_FORCE_INLINE Vec4V V4Load(const PxF32 f); +//(f[0],f[1],f[2],f[3]) +PX_FORCE_INLINE Vec4V V4LoadU(const PxF32* const f); +//(f[0],f[1],f[2],f[3]), f must be 16-byte aligned +PX_FORCE_INLINE Vec4V V4LoadA(const PxF32* const f); +//(x,y,z,w) +PX_FORCE_INLINE Vec4V V4LoadXYZW(const PxF32& x, const PxF32& y, const PxF32& z, const PxF32& w); + +// BoolV +//(f,f,f,f) +PX_FORCE_INLINE BoolV BLoad(const bool f); +//(f[0],f[1],f[2],f[3]) +PX_FORCE_INLINE BoolV BLoad(const bool* const f); + +// VecU32V +//(f,f,f,f) +PX_FORCE_INLINE VecU32V U4Load(const PxU32 f); +//(f[0],f[1],f[2],f[3]) +PX_FORCE_INLINE VecU32V U4LoadU(const PxU32* f); +//(f[0],f[1],f[2],f[3]), f must be 16-byte aligned +PX_FORCE_INLINE VecU32V U4LoadA(const PxU32* f); +//((U32)x, (U32)y, (U32)z, (U32)w) +PX_FORCE_INLINE VecU32V U4LoadXYZW(PxU32 x, PxU32 y, PxU32 z, PxU32 w); + +// VecI32V +//(i,i,i,i) +PX_FORCE_INLINE VecI32V I4Load(const PxI32 i); +//(i,i,i,i) +PX_FORCE_INLINE VecI32V I4LoadU(const PxI32* i); +//(i,i,i,i) +PX_FORCE_INLINE VecI32V I4LoadA(const PxI32* i); + +// QuatV +//(x = v[0], y=v[1], z=v[2], w=v3[3]) and array don't need to aligned +PX_FORCE_INLINE QuatV QuatVLoadU(const PxF32* v); +//(x = v[0], y=v[1], z=v[2], w=v3[3]) and array need to aligned, fast load +PX_FORCE_INLINE QuatV QuatVLoadA(const PxF32* v); +//(x, y, z, w) +PX_FORCE_INLINE QuatV QuatVLoadXYZW(const PxF32 x, const PxF32 y, const PxF32 z, const PxF32 w); + +// not added to public api +Vec4V Vec4V_From_PxVec3_WUndefined(const PxVec3& v); + +/////////////////////////////////////////////////// +// Construct a simd type from a different simd type +/////////////////////////////////////////////////// + +// Vec3V +//(v.x,v.y,v.z,0) +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V v); +//(v.x,v.y,v.z,undefined) - be very careful with w!=0 because many functions require w==0 for correct operation eg V3Dot, V3Length, V3Cross etc etc. +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V_WUndefined(const Vec4V v); + +// Vec4V +//(f.x,f.y,f.z,f.w) +PX_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f); +//((PxF32)f.x, (PxF32)f.y, (PxF32)f.z, (PxF32)f.w) +PX_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a); +//((PxF32)f.x, (PxF32)f.y, (PxF32)f.z, (PxF32)f.w) +PX_FORCE_INLINE Vec4V Vec4V_From_VecI32V(VecI32V a); +//(*(reinterpret_cast(&f.x), (reinterpret_cast(&f.y), (reinterpret_cast(&f.z), +//(reinterpret_cast(&f.w)) +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecU32V(VecU32V a); +//(*(reinterpret_cast(&f.x), (reinterpret_cast(&f.y), (reinterpret_cast(&f.z), +//(reinterpret_cast(&f.w)) +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecI32V(VecI32V a); + +// VecU32V +//(*(reinterpret_cast(&f.x), (reinterpret_cast(&f.y), (reinterpret_cast(&f.z), +//(reinterpret_cast(&f.w)) +PX_FORCE_INLINE VecU32V VecU32V_ReinterpretFrom_Vec4V(Vec4V a); +//(b[0], b[1], b[2], b[3]) +PX_FORCE_INLINE VecU32V VecU32V_From_BoolV(const BoolVArg b); + +// VecI32V +//(*(reinterpret_cast(&f.x), (reinterpret_cast(&f.y), (reinterpret_cast(&f.z), +//(reinterpret_cast(&f.w)) +PX_FORCE_INLINE VecI32V VecI32V_ReinterpretFrom_Vec4V(Vec4V a); +//((I32)a.x, (I32)a.y, (I32)a.z, (I32)a.w) +PX_FORCE_INLINE VecI32V VecI32V_From_Vec4V(Vec4V a); +//((I32)b.x, (I32)b.y, (I32)b.z, (I32)b.w) +PX_FORCE_INLINE VecI32V VecI32V_From_BoolV(const BoolVArg b); + +/////////////////////////////////////////////////// +// Convert from a simd type back to a scalar type +/////////////////////////////////////////////////// + +// FloatV +// a.x +PX_FORCE_INLINE void FStore(const FloatV a, PxF32* PX_RESTRICT f); + +// Vec3V +//(a.x,a.y,a.z) +PX_FORCE_INLINE void V3StoreA(const Vec3V a, PxVec3& f); +//(a.x,a.y,a.z) +PX_FORCE_INLINE void V3StoreU(const Vec3V a, PxVec3& f); + +// Vec4V +PX_FORCE_INLINE void V4StoreA(const Vec4V a, PxF32* f); +PX_FORCE_INLINE void V4StoreU(const Vec4V a, PxF32* f); + +// BoolV +PX_FORCE_INLINE void BStoreA(const BoolV b, PxU32* f); + +// VecU32V +PX_FORCE_INLINE void U4StoreA(const VecU32V uv, PxU32* u); + +// VecI32V +PX_FORCE_INLINE void I4StoreA(const VecI32V iv, PxI32* i); + +////////////////////////////////////////////////////////////////// +// Test that simd types have elements in the floating point range +////////////////////////////////////////////////////////////////// + +// check for each component is valid ie in floating point range +PX_FORCE_INLINE bool isFiniteFloatV(const FloatV a); +// check for each component is valid ie in floating point range +PX_FORCE_INLINE bool isFiniteVec3V(const Vec3V a); +// check for each component is valid ie in floating point range +PX_FORCE_INLINE bool isFiniteVec4V(const Vec4V a); + +// Check that w-component is zero. +PX_FORCE_INLINE bool isValidVec3V(const Vec3V a); + +////////////////////////////////////////////////////////////////// +// Tests that all elements of two 16-byte types are completely equivalent. +// Use these tests for unit testing and asserts only. +////////////////////////////////////////////////////////////////// + +namespace _VecMathTests +{ +PX_FORCE_INLINE Vec3V getInvalidVec3V(); +PX_FORCE_INLINE bool allElementsEqualFloatV(const FloatV a, const FloatV b); +PX_FORCE_INLINE bool allElementsEqualVec3V(const Vec3V a, const Vec3V b); +PX_FORCE_INLINE bool allElementsEqualVec4V(const Vec4V a, const Vec4V b); +PX_FORCE_INLINE bool allElementsEqualBoolV(const BoolV a, const BoolV b); +PX_FORCE_INLINE bool allElementsEqualVecU32V(const VecU32V a, const VecU32V b); +PX_FORCE_INLINE bool allElementsEqualVecI32V(const VecI32V a, const VecI32V b); + +PX_FORCE_INLINE bool allElementsEqualMat33V(const Mat33V& a, const Mat33V& b) +{ + return (allElementsEqualVec3V(a.col0, b.col0) && allElementsEqualVec3V(a.col1, b.col1) && + allElementsEqualVec3V(a.col2, b.col2)); +} +PX_FORCE_INLINE bool allElementsEqualMat34V(const Mat34V& a, const Mat34V& b) +{ + return (allElementsEqualVec3V(a.col0, b.col0) && allElementsEqualVec3V(a.col1, b.col1) && + allElementsEqualVec3V(a.col2, b.col2) && allElementsEqualVec3V(a.col3, b.col3)); +} +PX_FORCE_INLINE bool allElementsEqualMat44V(const Mat44V& a, const Mat44V& b) +{ + return (allElementsEqualVec4V(a.col0, b.col0) && allElementsEqualVec4V(a.col1, b.col1) && + allElementsEqualVec4V(a.col2, b.col2) && allElementsEqualVec4V(a.col3, b.col3)); +} + +PX_FORCE_INLINE bool allElementsNearEqualFloatV(const FloatV a, const FloatV b); +PX_FORCE_INLINE bool allElementsNearEqualVec3V(const Vec3V a, const Vec3V b); +PX_FORCE_INLINE bool allElementsNearEqualVec4V(const Vec4V a, const Vec4V b); +PX_FORCE_INLINE bool allElementsNearEqualMat33V(const Mat33V& a, const Mat33V& b) +{ + return (allElementsNearEqualVec3V(a.col0, b.col0) && allElementsNearEqualVec3V(a.col1, b.col1) && + allElementsNearEqualVec3V(a.col2, b.col2)); +} +PX_FORCE_INLINE bool allElementsNearEqualMat34V(const Mat34V& a, const Mat34V& b) +{ + return (allElementsNearEqualVec3V(a.col0, b.col0) && allElementsNearEqualVec3V(a.col1, b.col1) && + allElementsNearEqualVec3V(a.col2, b.col2) && allElementsNearEqualVec3V(a.col3, b.col3)); +} +PX_FORCE_INLINE bool allElementsNearEqualMat44V(const Mat44V& a, const Mat44V& b) +{ + return (allElementsNearEqualVec4V(a.col0, b.col0) && allElementsNearEqualVec4V(a.col1, b.col1) && + allElementsNearEqualVec4V(a.col2, b.col2) && allElementsNearEqualVec4V(a.col3, b.col3)); +} +} + +////////////////////////////////////////////////////////////////// +// Math operations on FloatV +////////////////////////////////////////////////////////////////// + +//(0,0,0,0) +PX_FORCE_INLINE FloatV FZero(); +//(1,1,1,1) +PX_FORCE_INLINE FloatV FOne(); +//(0.5,0.5,0.5,0.5) +PX_FORCE_INLINE FloatV FHalf(); +//(PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL) +PX_FORCE_INLINE FloatV FEps(); +//(PX_MAX_REAL, PX_MAX_REAL, PX_MAX_REAL PX_MAX_REAL) +PX_FORCE_INLINE FloatV FMax(); +//(-PX_MAX_REAL, -PX_MAX_REAL, -PX_MAX_REAL -PX_MAX_REAL) +PX_FORCE_INLINE FloatV FNegMax(); +//(1e-6f, 1e-6f, 1e-6f, 1e-6f) +PX_FORCE_INLINE FloatV FEps6(); +//((PxF32*)&1, (PxF32*)&1, (PxF32*)&1, (PxF32*)&1) + +//-f (per component) +PX_FORCE_INLINE FloatV FNeg(const FloatV f); +// a+b (per component) +PX_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b); +// a-b (per component) +PX_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b); +// a*b (per component) +PX_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b); +// 1.0f/a +PX_FORCE_INLINE FloatV FRecip(const FloatV a); +// 1.0f/a +PX_FORCE_INLINE FloatV FRecipFast(const FloatV a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE FloatV FRsqrt(const FloatV a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE FloatV FRsqrtFast(const FloatV a); +// sqrt(a) +PX_FORCE_INLINE FloatV FSqrt(const FloatV a); +// a*b+c +PX_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c); +// c-a*b +PX_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c); +// fabs(a) +PX_FORCE_INLINE FloatV FAbs(const FloatV a); +// c ? a : b (per component) +PX_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b); +// a>b (per component) +PX_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b); +// a>=b (per component) +PX_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b); +// a==b (per component) +PX_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b); +// Max(a,b) (per component) +PX_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b); +// Min(a,b) (per component) +PX_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b); +// Clamp(a,b) (per component) +PX_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV); + +// a.x>b.x +PX_FORCE_INLINE PxU32 FAllGrtr(const FloatV a, const FloatV b); +// a.x>=b.x +PX_FORCE_INLINE PxU32 FAllGrtrOrEq(const FloatV a, const FloatV b); +// a.x==b.x +PX_FORCE_INLINE PxU32 FAllEq(const FloatV a, const FloatV b); +// amax +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max); +// a>=min && a<=max +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV min, const FloatV max); +// a<-bounds || a>bounds +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV bounds); +// a>=-bounds && a<=bounds +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV bounds); + +// round float a to the near int +PX_FORCE_INLINE FloatV FRound(const FloatV a); +// calculate the sin of float a +PX_FORCE_INLINE FloatV FSin(const FloatV a); +// calculate the cos of float b +PX_FORCE_INLINE FloatV FCos(const FloatV a); + +////////////////////////////////////////////////////////////////// +// Math operations on Vec3V +////////////////////////////////////////////////////////////////// + +//(f,f,f,f) +PX_FORCE_INLINE Vec3V V3Splat(const FloatV f); + +//(x,y,z) +PX_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z); + +//(1,0,0,0) +PX_FORCE_INLINE Vec3V V3UnitX(); +//(0,1,0,0) +PX_FORCE_INLINE Vec3V V3UnitY(); +//(0,0,1,0) +PX_FORCE_INLINE Vec3V V3UnitZ(); + +//(f.x,f.x,f.x,f.x) +PX_FORCE_INLINE FloatV V3GetX(const Vec3V f); +//(f.y,f.y,f.y,f.y) +PX_FORCE_INLINE FloatV V3GetY(const Vec3V f); +//(f.z,f.z,f.z,f.z) +PX_FORCE_INLINE FloatV V3GetZ(const Vec3V f); + +//(f,v.y,v.z,v.w) +PX_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f); +//(v.x,f,v.z,v.w) +PX_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f); +//(v.x,v.y,f,v.w) +PX_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f); + +// v.x=f +PX_FORCE_INLINE void V3WriteX(Vec3V& v, const PxF32 f); +// v.y=f +PX_FORCE_INLINE void V3WriteY(Vec3V& v, const PxF32 f); +// v.z=f +PX_FORCE_INLINE void V3WriteZ(Vec3V& v, const PxF32 f); +// v.x=f.x, v.y=f.y, v.z=f.z +PX_FORCE_INLINE void V3WriteXYZ(Vec3V& v, const PxVec3& f); +// return v.x +PX_FORCE_INLINE PxF32 V3ReadX(const Vec3V& v); +// return v.y +PX_FORCE_INLINE PxF32 V3ReadY(const Vec3V& v); +// return v.y +PX_FORCE_INLINE PxF32 V3ReadZ(const Vec3V& v); +// return (v.x,v.y,v.z) +PX_FORCE_INLINE const PxVec3& V3ReadXYZ(const Vec3V& v); + +//(a.x, b.x, c.x) +PX_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c); +//(a.y, b.y, c.y) +PX_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c); +//(a.z, b.z, c.z) +PX_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c); + +//(0,0,0,0) +PX_FORCE_INLINE Vec3V V3Zero(); +//(1,1,1,1) +PX_FORCE_INLINE Vec3V V3One(); +//(PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL) +PX_FORCE_INLINE Vec3V V3Eps(); +//-c (per component) +PX_FORCE_INLINE Vec3V V3Neg(const Vec3V c); +// a+b (per component) +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b); +// a-b (per component) +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b); +// a*b (per component) +PX_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b); +// a*b (per component) +PX_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b); +// a/b (per component) +PX_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b); +// a/b (per component) +PX_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b); +// 1.0f/a +PX_FORCE_INLINE Vec3V V3Recip(const Vec3V a); +// 1.0f/a +PX_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a); +// a*b+c +PX_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c); +// c-a*b +PX_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c); +// a*b+c +PX_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c); +// c-a*b +PX_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c); +// fabs(a) +PX_FORCE_INLINE Vec3V V3Abs(const Vec3V a); + +// a.b +// Note: a.w and b.w must have value zero +PX_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b); +// aXb +// Note: a.w and b.w must have value zero +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b); +// |a.a|^1/2 +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3Length(const Vec3V a); +// a.a +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3LengthSq(const Vec3V a); +// a*|a.a|^-1/2 +// Note: a.w must have value zero +PX_FORCE_INLINE Vec3V V3Normalize(const Vec3V a); +// a.a>0 ? a*|a.a|^-1/2 : (0,0,0,0) +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3Length(const Vec3V a); +// a.a>0 ? a*|a.a|^-1/2 : unsafeReturnValue +// Note: a.w must have value zero +PX_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a, const Vec3V unsafeReturnValue); +// a.x + a.y + a.z +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3SumElems(const Vec3V a); + +// c ? a : b (per component) +PX_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b); +// a>b (per component) +PX_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b); +// a>=b (per component) +PX_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b); +// a==b (per component) +PX_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b); +// Max(a,b) (per component) +PX_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b); +// Min(a,b) (per component) +PX_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b); + +// Extract the maximum value from a +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a); + +// Extract the minimum value from a +// Note: a.w must have value zero +PX_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a); + +// Clamp(a,b) (per component) +PX_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV); + +// Extract the sign for each component +PX_FORCE_INLINE Vec3V V3Sign(const Vec3V a); + +// Test all components. +// (a.x>b.x && a.y>b.y && a.z>b.z) +// Note: a.w and b.w must have value zero +PX_FORCE_INLINE PxU32 V3AllGrtr(const Vec3V a, const Vec3V b); +// (a.x>=b.x && a.y>=b.y && a.z>=b.z) +// Note: a.w and b.w must have value zero +PX_FORCE_INLINE PxU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b); +// (a.x==b.x && a.y==b.y && a.z==b.z) +// Note: a.w and b.w must have value zero +PX_FORCE_INLINE PxU32 V3AllEq(const Vec3V a, const Vec3V b); +// a.xmax.x || a.y>max.y || a.z>max.z +// Note: a.w and min.w and max.w must have value zero +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max); +// a.x>=min.x && a.y>=min.y && a.z>=min.z && a.x<=max.x && a.y<=max.y && a.z<=max.z +// Note: a.w and min.w and max.w must have value zero +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max); +// a.x<-bounds.x || a.y<=-bounds.y || a.zbounds.x || a.y>bounds.y || a.z>bounds.z +// Note: a.w and bounds.w must have value zero +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds); +// a.x>=-bounds.x && a.y>=-bounds.y && a.z>=-bounds.z && a.x<=bounds.x && a.y<=bounds.y && a.z<=bounds.z +// Note: a.w and bounds.w must have value zero +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V bounds); + +//(floor(a.x + 0.5f), floor(a.y + 0.5f), floor(a.z + 0.5f)) +PX_FORCE_INLINE Vec3V V3Round(const Vec3V a); + +//(sinf(a.x), sinf(a.y), sinf(a.z)) +PX_FORCE_INLINE Vec3V V3Sin(const Vec3V a); +//(cosf(a.x), cosf(a.y), cosf(a.z)) +PX_FORCE_INLINE Vec3V V3Cos(const Vec3V a); + +//(a.y,a.z,a.z) +PX_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a); +//(a.x,a.y,a.x) +PX_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a); +//(a.y,a.z,a.x) +PX_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a); +//(a.z, a.x, a.y) +PX_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a); +//(a.z,a.z,a.y) +PX_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a); +//(a.y,a.x,a.x) +PX_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a); +//(0, v1.z, v0.y) +PX_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1); +//(v0.z, 0, v1.x) +PX_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1); +//(v1.y, v0.x, 0) +PX_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1); + +// Transpose 3 Vec3Vs inplace. Sets the w component to zero +// [ x0, y0, z0, w0] [ x1, y1, z1, w1] [ x2, y2, z2, w2] -> [x0 x1 x2 0] [y0 y1 y2 0] [z0 z1 z2 0] +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2); + +////////////////////////////////////////////////////////////////// +// Math operations on Vec4V +////////////////////////////////////////////////////////////////// + +//(f,f,f,f) +PX_FORCE_INLINE Vec4V V4Splat(const FloatV f); + +//(f[0],f[1],f[2],f[3]) +PX_FORCE_INLINE Vec4V V4Merge(const FloatV* const f); +//(x,y,z,w) +PX_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w); +//(x.w, y.w, z.w, w.w) +PX_FORCE_INLINE Vec4V V4MergeW(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w); +//(x.z, y.z, z.z, w.z) +PX_FORCE_INLINE Vec4V V4MergeZ(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w); +//(x.y, y.y, z.y, w.y) +PX_FORCE_INLINE Vec4V V4MergeY(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w); +//(x.x, y.x, z.x, w.x) +PX_FORCE_INLINE Vec4V V4MergeX(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w); + +//(a.x, b.x, a.y, b.y) +PX_FORCE_INLINE Vec4V V4UnpackXY(const Vec4VArg a, const Vec4VArg b); +//(a.z, b.z, a.w, b.w) +PX_FORCE_INLINE Vec4V V4UnpackZW(const Vec4VArg a, const Vec4VArg b); + +//(1,0,0,0) +PX_FORCE_INLINE Vec4V V4UnitW(); +//(0,1,0,0) +PX_FORCE_INLINE Vec4V V4UnitY(); +//(0,0,1,0) +PX_FORCE_INLINE Vec4V V4UnitZ(); +//(0,0,0,1) +PX_FORCE_INLINE Vec4V V4UnitW(); + +//(f.x,f.x,f.x,f.x) +PX_FORCE_INLINE FloatV V4GetX(const Vec4V f); +//(f.y,f.y,f.y,f.y) +PX_FORCE_INLINE FloatV V4GetY(const Vec4V f); +//(f.z,f.z,f.z,f.z) +PX_FORCE_INLINE FloatV V4GetZ(const Vec4V f); +//(f.w,f.w,f.w,f.w) +PX_FORCE_INLINE FloatV V4GetW(const Vec4V f); + +//(f,v.y,v.z,v.w) +PX_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f); +//(v.x,f,v.z,v.w) +PX_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f); +//(v.x,v.y,f,v.w) +PX_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f); +//(v.x,v.y,v.z,f) +PX_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f); + +//(v.x,v.y,v.z,0) +PX_FORCE_INLINE Vec4V V4ClearW(const Vec4V v); + +//(a[elementIndex], a[elementIndex], a[elementIndex], a[elementIndex]) +template +PX_FORCE_INLINE Vec4V V4SplatElement(Vec4V a); + +// v.x=f +PX_FORCE_INLINE void V4WriteX(Vec4V& v, const PxF32 f); +// v.y=f +PX_FORCE_INLINE void V4WriteY(Vec4V& v, const PxF32 f); +// v.z=f +PX_FORCE_INLINE void V4WriteZ(Vec4V& v, const PxF32 f); +// v.w=f +PX_FORCE_INLINE void V4WriteW(Vec4V& v, const PxF32 f); +// v.x=f.x, v.y=f.y, v.z=f.z +PX_FORCE_INLINE void V4WriteXYZ(Vec4V& v, const PxVec3& f); +// return v.x +PX_FORCE_INLINE PxF32 V4ReadX(const Vec4V& v); +// return v.y +PX_FORCE_INLINE PxF32 V4ReadY(const Vec4V& v); +// return v.z +PX_FORCE_INLINE PxF32 V4ReadZ(const Vec4V& v); +// return v.w +PX_FORCE_INLINE PxF32 V4ReadW(const Vec4V& v); +// return (v.x,v.y,v.z) +PX_FORCE_INLINE const PxVec3& V4ReadXYZ(const Vec4V& v); + +//(0,0,0,0) +PX_FORCE_INLINE Vec4V V4Zero(); +//(1,1,1,1) +PX_FORCE_INLINE Vec4V V4One(); +//(PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL,PX_EPS_REAL) +PX_FORCE_INLINE Vec4V V4Eps(); + +//-c (per component) +PX_FORCE_INLINE Vec4V V4Neg(const Vec4V c); +// a+b (per component) +PX_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b); +// a-b (per component) +PX_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b); +// a*b (per component) +PX_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b); +// a*b (per component) +PX_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b); +// a/b (per component) +PX_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b); +// a/b (per component) +PX_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b); +// a/b (per component) +PX_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b); +// 1.0f/a +PX_FORCE_INLINE Vec4V V4Recip(const Vec4V a); +// 1.0f/a +PX_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a); +// 1.0f/sqrt(a) +PX_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a); +// a*b+c +PX_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c); +// c-a*b +PX_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c); +// a*b+c +PX_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c); +// c-a*b +PX_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c); + +// fabs(a) +PX_FORCE_INLINE Vec4V V4Abs(const Vec4V a); +// bitwise a & ~b +PX_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b); + +// a.b (W is taken into account) +PX_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b); +// a.b (same computation as V3Dot. W is ignored in input) +PX_FORCE_INLINE FloatV V4Dot3(const Vec4V a, const Vec4V b); +// aXb (same computation as V3Cross. W is ignored in input and undefined in output) +PX_FORCE_INLINE Vec4V V4Cross(const Vec4V a, const Vec4V b); + +//|a.a|^1/2 +PX_FORCE_INLINE FloatV V4Length(const Vec4V a); +// a.a +PX_FORCE_INLINE FloatV V4LengthSq(const Vec4V a); + +// a*|a.a|^-1/2 +PX_FORCE_INLINE Vec4V V4Normalize(const Vec4V a); +// a.a>0 ? a*|a.a|^-1/2 : unsafeReturnValue +PX_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a, const Vec4V unsafeReturnValue); +// a*|a.a|^-1/2 +PX_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a); + +// c ? a : b (per component) +PX_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b); +// a>b (per component) +PX_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b); +// a>=b (per component) +PX_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b); +// a==b (per component) +PX_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b); +// Max(a,b) (per component) +PX_FORCE_INLINE Vec4V V4Max(const Vec4V a, const Vec4V b); +// Min(a,b) (per component) +PX_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b); +// Get the maximum component from a +PX_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a); +// Get the minimum component from a +PX_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a); + +// Clamp(a,b) (per component) +PX_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV); + +// return 1 if all components of a are greater than all components of b. +PX_FORCE_INLINE PxU32 V4AllGrtr(const Vec4V a, const Vec4V b); +// return 1 if all components of a are greater than or equal to all components of b +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b); +// return 1 if XYZ components of a are greater than or equal to XYZ components of b. W is ignored. +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq3(const Vec4V a, const Vec4V b); +// return 1 if all components of a are equal to all components of b +PX_FORCE_INLINE PxU32 V4AllEq(const Vec4V a, const Vec4V b); +// return 1 if any XYZ component of a is greater than the corresponding component of b. W is ignored. +PX_FORCE_INLINE PxU32 V4AnyGrtr3(const Vec4V a, const Vec4V b); + +// round(a)(per component) +PX_FORCE_INLINE Vec4V V4Round(const Vec4V a); +// sin(a) (per component) +PX_FORCE_INLINE Vec4V V4Sin(const Vec4V a); +// cos(a) (per component) +PX_FORCE_INLINE Vec4V V4Cos(const Vec4V a); + +// Permute v into a new vec4v with YXWZ format +PX_FORCE_INLINE Vec4V V4PermYXWZ(const Vec4V v); +// Permute v into a new vec4v with XZXZ format +PX_FORCE_INLINE Vec4V V4PermXZXZ(const Vec4V v); +// Permute v into a new vec4v with YWYW format +PX_FORCE_INLINE Vec4V V4PermYWYW(const Vec4V v); +// Permute v into a new vec4v with YZXW format +PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V v); +// Permute v into a new vec4v with ZWXY format - equivalent to a swap of the two 64bit parts of the vector +PX_FORCE_INLINE Vec4V V4PermZWXY(const Vec4V a); + +// Permute v into a new vec4v with format {a[x], a[y], a[z], a[w]} +// V4Perm<1,3,1,3> is equal to V4PermYWYW +// V4Perm<0,2,0,2> is equal to V4PermXZXZ +// V3Perm<1,0,3,2> is equal to V4PermYXWZ +template +PX_FORCE_INLINE Vec4V V4Perm(const Vec4V a); + +// Transpose 4 Vec4Vs inplace. +// [ x0, y0, z0, w0] [ x1, y1, z1, w1] [ x2, y2, z2, w2] [ x3, y3, z3, w3] -> +// [ x0, x1, x2, x3] [ y0, y1, y2, y3] [ z0, z1, z2, z3] [ w0, w1, w2, w3] +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2); + +// q = cos(a/2) + u*sin(a/2) +PX_FORCE_INLINE QuatV QuatV_From_RotationAxisAngle(const Vec3V u, const FloatV a); +// convert q to a unit quaternion +PX_FORCE_INLINE QuatV QuatNormalize(const QuatV q); +//|q.q|^1/2 +PX_FORCE_INLINE FloatV QuatLength(const QuatV q); +// q.q +PX_FORCE_INLINE FloatV QuatLengthSq(const QuatV q); +// a.b +PX_FORCE_INLINE FloatV QuatDot(const QuatV a, const QuatV b); +//(-q.x, -q.y, -q.z, q.w) +PX_FORCE_INLINE QuatV QuatConjugate(const QuatV q); +//(q.x, q.y, q.z) +PX_FORCE_INLINE Vec3V QuatGetImaginaryPart(const QuatV q); +// convert quaternion to matrix 33 +PX_FORCE_INLINE Mat33V QuatGetMat33V(const QuatVArg q); +// convert quaternion to matrix 33 +PX_FORCE_INLINE void QuatGetMat33V(const QuatVArg q, Vec3V& column0, Vec3V& column1, Vec3V& column2); +// convert matrix 33 to quaternion +PX_FORCE_INLINE QuatV Mat33GetQuatV(const Mat33V& a); +// brief computes rotation of x-axis +PX_FORCE_INLINE Vec3V QuatGetBasisVector0(const QuatV q); +// brief computes rotation of y-axis +PX_FORCE_INLINE Vec3V QuatGetBasisVector1(const QuatV q); +// brief computes rotation of z-axis +PX_FORCE_INLINE Vec3V QuatGetBasisVector2(const QuatV q); +// calculate the rotation vector from q and v +PX_FORCE_INLINE Vec3V QuatRotate(const QuatV q, const Vec3V v); +// calculate the rotation vector from the conjugate quaternion and v +PX_FORCE_INLINE Vec3V QuatRotateInv(const QuatV q, const Vec3V v); +// quaternion multiplication +PX_FORCE_INLINE QuatV QuatMul(const QuatV a, const QuatV b); +// quaternion add +PX_FORCE_INLINE QuatV QuatAdd(const QuatV a, const QuatV b); +// (-q.x, -q.y, -q.z, -q.w) +PX_FORCE_INLINE QuatV QuatNeg(const QuatV q); +// (a.x - b.x, a.y-b.y, a.z-b.z, a.w-b.w ) +PX_FORCE_INLINE QuatV QuatSub(const QuatV a, const QuatV b); +// (a.x*b, a.y*b, a.z*b, a.w*b) +PX_FORCE_INLINE QuatV QuatScale(const QuatV a, const FloatV b); +// (x = v[0], y = v[1], z = v[2], w =v[3]) +PX_FORCE_INLINE QuatV QuatMerge(const FloatV* const v); +// (x = v[0], y = v[1], z = v[2], w =v[3]) +PX_FORCE_INLINE QuatV QuatMerge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w); +// (x = 0.f, y = 0.f, z = 0.f, w = 1.f) +PX_FORCE_INLINE QuatV QuatIdentity(); +// check for each component is valid +PX_FORCE_INLINE bool isFiniteQuatV(const QuatV q); +// check for each component is valid +PX_FORCE_INLINE bool isValidQuatV(const QuatV q); +// check for each component is valid +PX_FORCE_INLINE bool isSaneQuatV(const QuatV q); + +// Math operations on 16-byte aligned booleans. +// x=false y=false z=false w=false +PX_FORCE_INLINE BoolV BFFFF(); +// x=false y=false z=false w=true +PX_FORCE_INLINE BoolV BFFFT(); +// x=false y=false z=true w=false +PX_FORCE_INLINE BoolV BFFTF(); +// x=false y=false z=true w=true +PX_FORCE_INLINE BoolV BFFTT(); +// x=false y=true z=false w=false +PX_FORCE_INLINE BoolV BFTFF(); +// x=false y=true z=false w=true +PX_FORCE_INLINE BoolV BFTFT(); +// x=false y=true z=true w=false +PX_FORCE_INLINE BoolV BFTTF(); +// x=false y=true z=true w=true +PX_FORCE_INLINE BoolV BFTTT(); +// x=true y=false z=false w=false +PX_FORCE_INLINE BoolV BTFFF(); +// x=true y=false z=false w=true +PX_FORCE_INLINE BoolV BTFFT(); +// x=true y=false z=true w=false +PX_FORCE_INLINE BoolV BTFTF(); +// x=true y=false z=true w=true +PX_FORCE_INLINE BoolV BTFTT(); +// x=true y=true z=false w=false +PX_FORCE_INLINE BoolV BTTFF(); +// x=true y=true z=false w=true +PX_FORCE_INLINE BoolV BTTFT(); +// x=true y=true z=true w=false +PX_FORCE_INLINE BoolV BTTTF(); +// x=true y=true z=true w=true +PX_FORCE_INLINE BoolV BTTTT(); + +// x=false y=false z=false w=true +PX_FORCE_INLINE BoolV BWMask(); +// x=true y=false z=false w=false +PX_FORCE_INLINE BoolV BXMask(); +// x=false y=true z=false w=false +PX_FORCE_INLINE BoolV BYMask(); +// x=false y=false z=true w=false +PX_FORCE_INLINE BoolV BZMask(); + +// get x component +PX_FORCE_INLINE BoolV BGetX(const BoolV f); +// get y component +PX_FORCE_INLINE BoolV BGetY(const BoolV f); +// get z component +PX_FORCE_INLINE BoolV BGetZ(const BoolV f); +// get w component +PX_FORCE_INLINE BoolV BGetW(const BoolV f); + +// Use elementIndex to splat xxxx or yyyy or zzzz or wwww +template +PX_FORCE_INLINE BoolV BSplatElement(Vec4V a); + +// component-wise && (AND) +PX_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b); +// component-wise || (OR) +PX_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b); +// component-wise not +PX_FORCE_INLINE BoolV BNot(const BoolV a); + +// if all four components are true, return true, otherwise return false +PX_FORCE_INLINE BoolV BAllTrue4(const BoolV a); + +// if any four components is true, return true, otherwise return false +PX_FORCE_INLINE BoolV BAnyTrue4(const BoolV a); + +// if all three(0, 1, 2) components are true, return true, otherwise return false +PX_FORCE_INLINE BoolV BAllTrue3(const BoolV a); + +// if any three (0, 1, 2) components is true, return true, otherwise return false +PX_FORCE_INLINE BoolV BAnyTrue3(const BoolV a); + +// Return 1 if all components equal, zero otherwise. +PX_FORCE_INLINE PxU32 BAllEq(const BoolV a, const BoolV b); + +// Specialized/faster BAllEq function for b==TTTT +PX_FORCE_INLINE PxU32 BAllEqTTTT(const BoolV a); +// Specialized/faster BAllEq function for b==FFFF +PX_FORCE_INLINE PxU32 BAllEqFFFF(const BoolV a); + +/// Get BoolV as bits set in an PxU32. A bit in the output is set if the element is 'true' in the input. +/// There is a bit for each element in a, with element 0s value held in bit0, element 1 in bit 1s and so forth. +/// If nothing is true in the input it will return 0, and if all are true if will return 0xf. +/// NOTE! That performance of the function varies considerably by platform, thus it is recommended to use +/// where your algorithm really needs a BoolV in an integer variable. +PX_FORCE_INLINE PxU32 BGetBitMask(const BoolV a); + +// VecI32V stuff + +PX_FORCE_INLINE VecI32V VecI32V_Zero(); + +PX_FORCE_INLINE VecI32V VecI32V_One(); + +PX_FORCE_INLINE VecI32V VecI32V_Two(); + +PX_FORCE_INLINE VecI32V VecI32V_MinusOne(); + +// Compute a shift parameter for VecI32V_LeftShift and VecI32V_RightShift +// Each element of shift must be identical ie the vector must have form {count, count, count, count} with count>=0 +PX_FORCE_INLINE VecShiftV VecI32V_PrepareShift(const VecI32VArg shift); + +// Shift each element of a leftwards by the same amount +// Compute shift with VecI32V_PrepareShift +//{a.x<>shift[0], a.y>>shift[0], a.z>>shift[0], a.w>>shift[0]} +PX_FORCE_INLINE VecI32V VecI32V_RightShift(const VecI32VArg a, const VecShiftVArg shift); + +PX_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b); + +PX_FORCE_INLINE VecI32V VecI32V_Or(const VecI32VArg a, const VecI32VArg b); + +PX_FORCE_INLINE VecI32V VecI32V_GetX(const VecI32VArg a); + +PX_FORCE_INLINE VecI32V VecI32V_GetY(const VecI32VArg a); + +PX_FORCE_INLINE VecI32V VecI32V_GetZ(const VecI32VArg a); + +PX_FORCE_INLINE VecI32V VecI32V_GetW(const VecI32VArg a); + +PX_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b); + +PX_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b); + +PX_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b); + +PX_FORCE_INLINE VecI32V V4I32Sel(const BoolV c, const VecI32V a, const VecI32V b); + +// VecU32V stuff + +PX_FORCE_INLINE VecU32V U4Zero(); + +PX_FORCE_INLINE VecU32V U4One(); + +PX_FORCE_INLINE VecU32V U4Two(); + +PX_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b); + +PX_FORCE_INLINE VecU32V V4U32Sel(const BoolV c, const VecU32V a, const VecU32V b); + +PX_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b); + +PX_FORCE_INLINE VecU32V V4U32xor(VecU32V a, VecU32V b); + +PX_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b); + +PX_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b); + +// VecU32 - why does this not return a bool? +PX_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b); + +// Math operations on 16-byte aligned Mat33s (represents any 3x3 matrix) +// a*b +PX_FORCE_INLINE Vec3V M33MulV3(const Mat33V& a, const Vec3V b); +// A*x + b +PX_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V& A, const Vec3V b, const Vec3V c); +// transpose(a) * b +PX_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V& a, const Vec3V b); +// a*b +PX_FORCE_INLINE Mat33V M33MulM33(const Mat33V& a, const Mat33V& b); +// a+b +PX_FORCE_INLINE Mat33V M33Add(const Mat33V& a, const Mat33V& b); +// a+b +PX_FORCE_INLINE Mat33V M33Sub(const Mat33V& a, const Mat33V& b); +//-a +PX_FORCE_INLINE Mat33V M33Neg(const Mat33V& a); +// absolute value of the matrix +PX_FORCE_INLINE Mat33V M33Abs(const Mat33V& a); +// inverse mat +PX_FORCE_INLINE Mat33V M33Inverse(const Mat33V& a); +// transpose(a) +PX_FORCE_INLINE Mat33V M33Trnsps(const Mat33V& a); +// create an identity matrix +PX_FORCE_INLINE Mat33V M33Identity(); + +// create a vec3 to store the diagonal element of the M33 +PX_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg); + +// Not implemented +// return 1 if all components of a are equal to all components of b +// PX_FORCE_INLINE PxU32 V4U32AllEq(const VecU32V a, const VecU32V b); +// v.w=f +// PX_FORCE_INLINE void V3WriteW(Vec3V& v, const PxF32 f); +// PX_FORCE_INLINE PxF32 V3ReadW(const Vec3V& v); + +// Not used +// PX_FORCE_INLINE Vec4V V4LoadAligned(Vec4V* addr); +// PX_FORCE_INLINE Vec4V V4LoadUnaligned(Vec4V* addr); +// floor(a)(per component) +// PX_FORCE_INLINE Vec4V V4Floor(Vec4V a); +// ceil(a) (per component) +// PX_FORCE_INLINE Vec4V V4Ceil(Vec4V a); +// PX_FORCE_INLINE VecU32V V4ConvertToU32VSaturate(const Vec4V a, PxU32 power); + +// Math operations on 16-byte aligned Mat34s (represents transformation matrix - rotation and translation). +// namespace _Mat34V +//{ +// //a*b +// PX_FORCE_INLINE Vec3V multiplyV(const Mat34V& a, const Vec3V b); +// //a_rotation * b +// PX_FORCE_INLINE Vec3V multiply3X3V(const Mat34V& a, const Vec3V b); +// //transpose(a_rotation)*b +// PX_FORCE_INLINE Vec3V multiplyTranspose3X3V(const Mat34V& a, const Vec3V b); +// //a*b +// PX_FORCE_INLINE Mat34V multiplyV(const Mat34V& a, const Mat34V& b); +// //a_rotation*b +// PX_FORCE_INLINE Mat33V multiply3X3V(const Mat34V& a, const Mat33V& b); +// //a_rotation*b_rotation +// PX_FORCE_INLINE Mat33V multiply3X3V(const Mat34V& a, const Mat34V& b); +// //a+b +// PX_FORCE_INLINE Mat34V addV(const Mat34V& a, const Mat34V& b); +// //a^-1 +// PX_FORCE_INLINE Mat34V getInverseV(const Mat34V& a); +// //transpose(a_rotation) +// PX_FORCE_INLINE Mat33V getTranspose3X3(const Mat34V& a); +//}; //namespace _Mat34V + +// a*b +//#define M34MulV3(a,b) (M34MulV3(a,b)) +////a_rotation * b +//#define M34Mul33V3(a,b) (M34Mul33V3(a,b)) +////transpose(a_rotation)*b +//#define M34TrnspsMul33V3(a,b) (M34TrnspsMul33V3(a,b)) +////a*b +//#define M34MulM34(a,b) (_Mat34V::multiplyV(a,b)) +// a_rotation*b +//#define M34MulM33(a,b) (M34MulM33(a,b)) +// a_rotation*b_rotation +//#define M34Mul33MM34(a,b) (M34MulM33(a,b)) +// a+b +//#define M34Add(a,b) (M34Add(a,b)) +////a^-1 +//#define M34Inverse(a,b) (M34Inverse(a)) +// transpose(a_rotation) +//#define M34Trnsps33(a) (M33Trnsps3X3(a)) + +// Math operations on 16-byte aligned Mat44s (represents any 4x4 matrix) +// namespace _Mat44V +//{ +// //a*b +// PX_FORCE_INLINE Vec4V multiplyV(const Mat44V& a, const Vec4V b); +// //transpose(a)*b +// PX_FORCE_INLINE Vec4V multiplyTransposeV(const Mat44V& a, const Vec4V b); +// //a*b +// PX_FORCE_INLINE Mat44V multiplyV(const Mat44V& a, const Mat44V& b); +// //a+b +// PX_FORCE_INLINE Mat44V addV(const Mat44V& a, const Mat44V& b); +// //a&-1 +// PX_FORCE_INLINE Mat44V getInverseV(const Mat44V& a); +// //transpose(a) +// PX_FORCE_INLINE Mat44V getTransposeV(const Mat44V& a); +//}; //namespace _Mat44V + +// namespace _VecU32V +//{ +// // pack 8 U32s to 8 U16s with saturation +// PX_FORCE_INLINE VecU16V pack2U32VToU16VSaturate(VecU32V a, VecU32V b); +// PX_FORCE_INLINE VecU32V orV(VecU32V a, VecU32V b); +// PX_FORCE_INLINE VecU32V andV(VecU32V a, VecU32V b); +// PX_FORCE_INLINE VecU32V andcV(VecU32V a, VecU32V b); +// // conversion from integer to float +// PX_FORCE_INLINE Vec4V convertToVec4V(VecU32V a); +// // splat a[elementIndex] into all fields of a +// template +// PX_FORCE_INLINE VecU32V splatElement(VecU32V a); +// PX_FORCE_INLINE void storeAligned(VecU32V a, VecU32V* address); +//}; + +// namespace _VecI32V +//{ +// template PX_FORCE_INLINE VecI32V splatI32(); +//}; +// +// namespace _VecU16V +//{ +// PX_FORCE_INLINE VecU16V orV(VecU16V a, VecU16V b); +// PX_FORCE_INLINE VecU16V andV(VecU16V a, VecU16V b); +// PX_FORCE_INLINE VecU16V andcV(VecU16V a, VecU16V b); +// PX_FORCE_INLINE void storeAligned(VecU16V val, VecU16V *address); +// PX_FORCE_INLINE VecU16V loadAligned(VecU16V* addr); +// PX_FORCE_INLINE VecU16V loadUnaligned(VecU16V* addr); +// PX_FORCE_INLINE VecU16V compareGt(VecU16V a, VecU16V b); +// template +// PX_FORCE_INLINE VecU16V splatElement(VecU16V a); +// PX_FORCE_INLINE VecU16V subtractModulo(VecU16V a, VecU16V b); +// PX_FORCE_INLINE VecU16V addModulo(VecU16V a, VecU16V b); +// PX_FORCE_INLINE VecU32V getLo16(VecU16V a); // [0,2,4,6] 16-bit values to [0,1,2,3] 32-bit vector +// PX_FORCE_INLINE VecU32V getHi16(VecU16V a); // [1,3,5,7] 16-bit values to [0,1,2,3] 32-bit vector +//}; +// +// namespace _VecI16V +//{ +// template PX_FORCE_INLINE VecI16V splatImmediate(); +//}; +// +// namespace _VecU8V +//{ +//}; + +// a*b +//#define M44MulV4(a,b) (M44MulV4(a,b)) +////transpose(a)*b +//#define M44TrnspsMulV4(a,b) (M44TrnspsMulV4(a,b)) +////a*b +//#define M44MulM44(a,b) (M44MulM44(a,b)) +////a+b +//#define M44Add(a,b) (M44Add(a,b)) +////a&-1 +//#define M44Inverse(a) (M44Inverse(a)) +////transpose(a) +//#define M44Trnsps(a) (M44Trnsps(a)) + +// dsequeira: these used to be assert'd out in SIMD builds, but they're necessary if +// we want to be able to write some scalar functions which run using SIMD data structures + +PX_FORCE_INLINE void V3WriteX(Vec3V& v, const PxF32 f) +{ + reinterpret_cast(v).x = f; +} + +PX_FORCE_INLINE void V3WriteY(Vec3V& v, const PxF32 f) +{ + reinterpret_cast(v).y = f; +} + +PX_FORCE_INLINE void V3WriteZ(Vec3V& v, const PxF32 f) +{ + reinterpret_cast(v).z = f; +} + +PX_FORCE_INLINE void V3WriteXYZ(Vec3V& v, const PxVec3& f) +{ + reinterpret_cast(v) = f; +} + +PX_FORCE_INLINE PxF32 V3ReadX(const Vec3V& v) +{ + return reinterpret_cast(v).x; +} + +PX_FORCE_INLINE PxF32 V3ReadY(const Vec3V& v) +{ + return reinterpret_cast(v).y; +} + +PX_FORCE_INLINE PxF32 V3ReadZ(const Vec3V& v) +{ + return reinterpret_cast(v).z; +} + +PX_FORCE_INLINE const PxVec3& V3ReadXYZ(const Vec3V& v) +{ + return reinterpret_cast(v); +} + +PX_FORCE_INLINE void V4WriteX(Vec4V& v, const PxF32 f) +{ + reinterpret_cast(v).x = f; +} + +PX_FORCE_INLINE void V4WriteY(Vec4V& v, const PxF32 f) +{ + reinterpret_cast(v).y = f; +} + +PX_FORCE_INLINE void V4WriteZ(Vec4V& v, const PxF32 f) +{ + reinterpret_cast(v).z = f; +} + +PX_FORCE_INLINE void V4WriteW(Vec4V& v, const PxF32 f) +{ + reinterpret_cast(v).w = f; +} + +PX_FORCE_INLINE void V4WriteXYZ(Vec4V& v, const PxVec3& f) +{ + reinterpret_cast(v) = f; +} + +PX_FORCE_INLINE PxF32 V4ReadX(const Vec4V& v) +{ + return reinterpret_cast(v).x; +} + +PX_FORCE_INLINE PxF32 V4ReadY(const Vec4V& v) +{ + return reinterpret_cast(v).y; +} + +PX_FORCE_INLINE PxF32 V4ReadZ(const Vec4V& v) +{ + return reinterpret_cast(v).z; +} + +PX_FORCE_INLINE PxF32 V4ReadW(const Vec4V& v) +{ + return reinterpret_cast(v).w; +} + +PX_FORCE_INLINE const PxVec3& V4ReadXYZ(const Vec4V& v) +{ + return reinterpret_cast(v); +} + +// this macro transposes 4 Vec4V into 3 Vec4V (assuming that the W component can be ignored +#define PX_TRANSPOSE_44_34(inA, inB, inC, inD, outA, outB, outC) \ + \ +outA = V4UnpackXY(inA, inC); \ + \ +inA = V4UnpackZW(inA, inC); \ + \ +inC = V4UnpackXY(inB, inD); \ + \ +inB = V4UnpackZW(inB, inD); \ + \ +outB = V4UnpackZW(outA, inC); \ + \ +outA = V4UnpackXY(outA, inC); \ + \ +outC = V4UnpackXY(inA, inB); + +// this macro transposes 3 Vec4V into 4 Vec4V (with W components as garbage!) +#define PX_TRANSPOSE_34_44(inA, inB, inC, outA, outB, outC, outD) \ + outA = V4UnpackXY(inA, inC); \ + inA = V4UnpackZW(inA, inC); \ + outC = V4UnpackXY(inB, inB); \ + inC = V4UnpackZW(inB, inB); \ + outB = V4UnpackZW(outA, outC); \ + outA = V4UnpackXY(outA, outC); \ + outC = V4UnpackXY(inA, inC); \ + outD = V4UnpackZW(inA, inC); + +#define PX_TRANSPOSE_44(inA, inB, inC, inD, outA, outB, outC, outD) \ + outA = V4UnpackXY(inA, inC); \ + inA = V4UnpackZW(inA, inC); \ + inC = V4UnpackXY(inB, inD); \ + inB = V4UnpackZW(inB, inD); \ + outB = V4UnpackZW(outA, inC); \ + outA = V4UnpackXY(outA, inC); \ + outC = V4UnpackXY(inA, inB); \ + outD = V4UnpackZW(inA, inB); + +// This function returns a Vec4V, where each element is the dot product of one pair of Vec3Vs. On PC, each element in +// the result should be identical to the results if V3Dot was performed +// for each pair of Vec3V. +// However, on other platforms, the result might diverge by some small margin due to differences in FP rounding, e.g. if +// _mm_dp_ps was used or some other approximate dot product or fused madd operations +// were used. +// Where there does not exist a hw-accelerated dot-product operation, this approach should be the fastest way to compute +// the dot product of 4 vectors. +PX_FORCE_INLINE Vec4V V3Dot4(const Vec3VArg a0, const Vec3VArg b0, const Vec3VArg a1, const Vec3VArg b1, + const Vec3VArg a2, const Vec3VArg b2, const Vec3VArg a3, const Vec3VArg b3) +{ + Vec4V a0b0 = Vec4V_From_Vec3V(V3Mul(a0, b0)); + Vec4V a1b1 = Vec4V_From_Vec3V(V3Mul(a1, b1)); + Vec4V a2b2 = Vec4V_From_Vec3V(V3Mul(a2, b2)); + Vec4V a3b3 = Vec4V_From_Vec3V(V3Mul(a3, b3)); + + Vec4V aTrnsps, bTrnsps, cTrnsps; + + PX_TRANSPOSE_44_34(a0b0, a1b1, a2b2, a3b3, aTrnsps, bTrnsps, cTrnsps); + + return V4Add(V4Add(aTrnsps, bTrnsps), cTrnsps); +} + +//(f.x,f.y,f.z,0) - Alternative/faster V3LoadU implementation when it is safe to read "W", i.e. the 32bits after the PxVec3. +PX_FORCE_INLINE Vec3V V3LoadU_SafeReadW(const PxVec3& f) +{ + return Vec3V_From_Vec4V(V4LoadU(&f.x)); +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +// Now for the cross-platform implementations of the 16-byte aligned maths functions (win32/360/ppu/spu etc). +#if COMPILE_VECTOR_INTRINSICS +#include "PsInlineAoS.h" +#else // #if COMPILE_VECTOR_INTRINSICS +#include "PsVecMathAoSScalarInline.h" +#endif // #if !COMPILE_VECTOR_INTRINSICS +#include "PsVecQuat.h" + +#endif // PSFOUNDATION_PSVECMATH_H diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h new file mode 100644 index 000000000..8607565c8 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h @@ -0,0 +1,250 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECMATHAOSSCALAR_H +#define PSFOUNDATION_PSVECMATHAOSSCALAR_H + +#if COMPILE_VECTOR_INTRINSICS +#error Scalar version should not be included when using vector intrinsics. +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +struct VecI16V; +struct VecU16V; +struct VecI32V; +struct VecU32V; +struct Vec4V; +typedef Vec4V QuatV; + +PX_ALIGN_PREFIX(16) +struct FloatV +{ + PxF32 x; + PxF32 pad[3]; + FloatV() + { + } + FloatV(const PxF32 _x) : x(_x) + { + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Vec4V +{ + PxF32 x, y, z, w; + Vec4V() + { + } + Vec4V(const PxF32 _x, const PxF32 _y, const PxF32 _z, const PxF32 _w) : x(_x), y(_y), z(_z), w(_w) + { + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Vec3V +{ + PxF32 x, y, z; + PxF32 pad; + Vec3V() + { + } + Vec3V(const PxF32 _x, const PxF32 _y, const PxF32 _z) : x(_x), y(_y), z(_z), pad(0.0f) + { + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct BoolV +{ + PxU32 ux, uy, uz, uw; + BoolV() + { + } + BoolV(const PxU32 _x, const PxU32 _y, const PxU32 _z, const PxU32 _w) : ux(_x), uy(_y), uz(_z), uw(_w) + { + } +} PX_ALIGN_SUFFIX(16); + +struct Mat33V +{ + Mat33V() + { + } + Mat33V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec3V col0; + Vec3V col1; + Vec3V col2; +}; + +struct Mat34V +{ + Mat34V() + { + } + Mat34V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2, const Vec3V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec3V col0; + Vec3V col1; + Vec3V col2; + Vec3V col3; +}; + +struct Mat43V +{ + Mat43V() + { + } + Mat43V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec4V col0; + Vec4V col1; + Vec4V col2; +}; + +struct Mat44V +{ + Mat44V() + { + } + Mat44V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2, const Vec4V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec4V col0; + Vec4V col1; + Vec4V col2; + Vec4V col3; +}; + +PX_ALIGN_PREFIX(16) +struct VecU32V +{ + PxU32 u32[4]; + PX_FORCE_INLINE VecU32V() + { + } + PX_FORCE_INLINE VecU32V(PxU32 a, PxU32 b, PxU32 c, PxU32 d) + { + u32[0] = a; + u32[1] = b; + u32[2] = c; + u32[3] = d; + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct VecI32V +{ + PxI32 i32[4]; + PX_FORCE_INLINE VecI32V() + { + } + PX_FORCE_INLINE VecI32V(PxI32 a, PxI32 b, PxI32 c, PxI32 d) + { + i32[0] = a; + i32[1] = b; + i32[2] = c; + i32[3] = d; + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct VecI16V +{ + PxI16 i16[8]; + PX_FORCE_INLINE VecI16V() + { + } + PX_FORCE_INLINE VecI16V(PxI16 a, PxI16 b, PxI16 c, PxI16 d, PxI16 e, PxI16 f, PxI16 g, PxI16 h) + { + i16[0] = a; + i16[1] = b; + i16[2] = c; + i16[3] = d; + i16[4] = e; + i16[5] = f; + i16[6] = g; + i16[7] = h; + } +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct VecU16V +{ + union + { + PxU16 u16[8]; + PxI16 i16[8]; + }; + PX_FORCE_INLINE VecU16V() + { + } + PX_FORCE_INLINE VecU16V(PxU16 a, PxU16 b, PxU16 c, PxU16 d, PxU16 e, PxU16 f, PxU16 g, PxU16 h) + { + u16[0] = a; + u16[1] = b; + u16[2] = c; + u16[3] = d; + u16[4] = e; + u16[5] = f; + u16[6] = g; + u16[7] = h; + } +} PX_ALIGN_SUFFIX(16); + +#define FloatVArg FloatV & +#define Vec3VArg Vec3V & +#define Vec4VArg Vec4V & +#define BoolVArg BoolV & +#define VecU32VArg VecU32V & +#define VecI32VArg VecI32V & +#define VecU16VArg VecU16V & +#define VecI16VArg VecI16V & +#define QuatVArg QuatV & + +#define VecCrossV Vec3V + +typedef VecI32V VecShiftV; +#define VecShiftVArg VecShiftV & + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PX_PHYSICS_COMMON_VECMATH_INLINE_SCALAR diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h new file mode 100644 index 000000000..e88f79c9b --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h @@ -0,0 +1,2275 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECMATHAOSSCALARINLINE_H +#define PSFOUNDATION_PSVECMATHAOSSCALARINLINE_H + +#if COMPILE_VECTOR_INTRINSICS +#error Scalar version should not be included when using vector intrinsics. +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +#define BOOL_TO_U32(b) (PxU32)(- PxI32(b)) +#define TRUE_TO_U32 (PxU32)(-1) +#define FALSE_TO_U32 (PxU32)(0) + +#define BOOL_TO_U16(b) (PxU16)(- PxI32(b)) + +#define PX_VECMATH_ASSERT_ENABLED 0 + +#if PX_VECMATH_ASSERT_ENABLED +#define VECMATHAOS_ASSERT(x) { PX_ASSERT(x); } +#else +#define VECMATHAOS_ASSERT(x) +#endif + +///////////////////////////////////////////////////////////////////// +////INTERNAL USE ONLY AND TESTS +///////////////////////////////////////////////////////////////////// + +namespace internalScalarSimd +{ +PX_FORCE_INLINE PxF32 FStore(const FloatV a) +{ + return a.x; +} + +PX_FORCE_INLINE bool hasZeroElementInFloatV(const FloatV a) +{ + return (0 == a.x); +} + +PX_FORCE_INLINE bool hasZeroElementInVec3V(const Vec3V a) +{ + return (0 == a.x || 0 == a.y || 0 == a.z); +} + +PX_FORCE_INLINE bool hasZeroElementInVec4V(const Vec4V a) +{ + return (0 == a.x || 0 == a.y || 0 == a.z || 0 == a.w); +} +} + +namespace _VecMathTests +{ +// PT: this function returns an invalid Vec3V (W!=0.0f) just for unit-testing 'isValidVec3V' +PX_FORCE_INLINE Vec3V getInvalidVec3V() +{ + Vec3V tmp; + tmp.x = tmp.y = tmp.z = 0.0f; + tmp.pad = 1.0f; + return tmp; +} + +PX_FORCE_INLINE bool allElementsEqualFloatV(const FloatV a, const FloatV b) +{ + return (a.x == b.x); +} + +PX_FORCE_INLINE bool allElementsEqualVec3V(const Vec3V a, const Vec3V b) +{ + return (a.x == b.x && a.y == b.y && a.z == b.z); +} + +PX_FORCE_INLINE bool allElementsEqualVec4V(const Vec4V a, const Vec4V b) +{ + return (a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w); +} + +PX_FORCE_INLINE bool allElementsEqualBoolV(const BoolV a, const BoolV b) +{ + return (a.ux == b.ux && a.uy == b.uy && a.uz == b.uz && a.uw == b.uw); +} + +PX_FORCE_INLINE bool allElementsEqualVecU32V(const VecU32V a, const VecU32V b) +{ + return (a.u32[0] == b.u32[0] && a.u32[1] == b.u32[1] && a.u32[2] == b.u32[2] && a.u32[3] == b.u32[3]); +} + +PX_FORCE_INLINE bool allElementsEqualVecI32V(const VecI32V a, const VecI32V b) +{ + return (a.i32[0] == b.i32[0] && a.i32[1] == b.i32[1] && a.i32[2] == b.i32[2] && a.i32[3] == b.i32[3]); +} + +#define VECMATH_AOS_EPSILON (1e-3f) + +PX_FORCE_INLINE bool allElementsNearEqualFloatV(const FloatV a, const FloatV b) +{ + const PxF32 cx = a.x - b.x; + return (cx > -VECMATH_AOS_EPSILON && cx < VECMATH_AOS_EPSILON); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec3V(const Vec3V a, const Vec3V b) +{ + const PxF32 cx = a.x - b.x; + const PxF32 cy = a.y - b.y; + const PxF32 cz = a.z - b.z; + return (cx > -VECMATH_AOS_EPSILON && cx < VECMATH_AOS_EPSILON && cy > -VECMATH_AOS_EPSILON && + cy < VECMATH_AOS_EPSILON && cz > -VECMATH_AOS_EPSILON && cz < VECMATH_AOS_EPSILON); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec4V(const Vec4V a, const Vec4V b) +{ + const PxF32 cx = a.x - b.x; + const PxF32 cy = a.y - b.y; + const PxF32 cz = a.z - b.z; + const PxF32 cw = a.w - b.w; + return (cx > -VECMATH_AOS_EPSILON && cx < VECMATH_AOS_EPSILON && cy > -VECMATH_AOS_EPSILON && + cy < VECMATH_AOS_EPSILON && cz > -VECMATH_AOS_EPSILON && cz < VECMATH_AOS_EPSILON && + cw > -VECMATH_AOS_EPSILON && cw < VECMATH_AOS_EPSILON); +} +} + +/////////////////////////////////////////////////////// + +PX_FORCE_INLINE bool isValidVec3V(const Vec3V a) +{ + return a.pad == 0.f; +} + +PX_FORCE_INLINE bool isFiniteFloatV(const FloatV a) +{ + return PxIsFinite(a.x); +} + +PX_FORCE_INLINE bool isFiniteVec3V(const Vec3V a) +{ + return PxIsFinite(a.x) && PxIsFinite(a.y) && PxIsFinite(a.z); +} + +PX_FORCE_INLINE bool isFiniteVec4V(const Vec4V a) +{ + return PxIsFinite(a.x) && PxIsFinite(a.y) && PxIsFinite(a.z) && PxIsFinite(a.w); +} + +///////////////////////////////////////////////////////////////////// +////VECTORISED FUNCTION IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE FloatV FLoad(const PxF32 f) +{ + return FloatV(f); +} + +PX_FORCE_INLINE Vec3V V3Load(const PxF32 f) +{ + return Vec3V(f, f, f); +} + +PX_FORCE_INLINE Vec4V V4Load(const PxF32 f) +{ + return Vec4V(f, f, f, f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool f) +{ +#if PX_ARM + // SD: Android ARM builds fail if this is done with a cast. + // Might also fail because of something else but the select + // operator here seems to fix everything that failed in release builds. + return f ? BTTTT() : BFFFF(); +#else + return BoolV(BOOL_TO_U32(f), BOOL_TO_U32(f), BOOL_TO_U32(f), BOOL_TO_U32(f)); +#endif +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxVec3& f) +{ + return Vec3V(f.x, f.y, f.z); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxVec3& f) +{ + return Vec3V(f.x, f.y, f.z); +} + +PX_FORCE_INLINE Vec3V V3LoadUnsafeA(const PxVec3& f) +{ + return Vec3V(f.x, f.y, f.z); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxF32* const f) +{ + return Vec3V(f[0], f[1], f[2]); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxF32* const f) +{ + return Vec3V(f[0], f[1], f[2]); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V f) +{ + return Vec3V(f.x, f.y, f.z); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V_WUndefined(const Vec4V v) +{ + return Vec3V(v.x, v.y, v.z); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f) +{ + return Vec4V(f.x, f.y, f.z, 0.0f); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_FloatV(FloatV f) +{ + return Vec4V(f.x, f.x, f.x, f.x); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV(FloatV f) +{ + return Vec3V(f.x, f.x, f.x); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV_WUndefined(FloatV f) +{ + return Vec3V(f.x, f.x, f.x); +} + +PX_FORCE_INLINE Vec4V V4LoadA(const PxF32* const f) +{ + return Vec4V(f[0], f[1], f[2], f[3]); +} + +PX_FORCE_INLINE void V4StoreA(const Vec4V a, PxF32* f) +{ + *reinterpret_cast(f) = a; +} + +PX_FORCE_INLINE void V4StoreU(const Vec4V a, PxF32* f) +{ + *reinterpret_cast(f) = *reinterpret_cast(&a.x); +} + +PX_FORCE_INLINE void BStoreA(const BoolV a, PxU32* f) +{ + *reinterpret_cast(f) = a; +} + +PX_FORCE_INLINE void U4StoreA(const VecU32V uv, PxU32* u) +{ + *reinterpret_cast(u) = uv; +} + +PX_FORCE_INLINE void I4StoreA(const VecI32V iv, PxI32* i) +{ + *reinterpret_cast(i) = iv; +} + +PX_FORCE_INLINE Vec4V V4LoadU(const PxF32* const f) +{ + return Vec4V(f[0], f[1], f[2], f[3]); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_PxVec3_WUndefined(const PxVec3& f) +{ + return Vec4V(f[0], f[1], f[2], 0.0f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool* const f) +{ + return BoolV(BOOL_TO_U32(f[0]), BOOL_TO_U32(f[1]), BOOL_TO_U32(f[2]), BOOL_TO_U32(f[3])); +} + +PX_FORCE_INLINE void FStore(const FloatV a, PxF32* PX_RESTRICT f) +{ + *f = a.x; +} + +PX_FORCE_INLINE void V3StoreA(const Vec3V a, PxVec3& f) +{ + f = PxVec3(a.x, a.y, a.z); +} + +PX_FORCE_INLINE void V3StoreU(const Vec3V a, PxVec3& f) +{ + f = PxVec3(a.x, a.y, a.z); +} + +PX_FORCE_INLINE void Store_From_BoolV(const BoolV b, PxU32* b2) +{ + *b2 = b.ux; +} + +////////////////////////// +// FLOATV +////////////////////////// + +PX_FORCE_INLINE FloatV FZero() +{ + return FLoad(0.0f); +} + +PX_FORCE_INLINE FloatV FOne() +{ + return FLoad(1.0f); +} + +PX_FORCE_INLINE FloatV FHalf() +{ + return FLoad(0.5f); +} + +PX_FORCE_INLINE FloatV FEps() +{ + return FLoad(PX_EPS_REAL); +} + +PX_FORCE_INLINE FloatV FEps6() +{ + return FLoad(1e-6f); +} + +PX_FORCE_INLINE FloatV FMax() +{ + return FLoad(PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV FNegMax() +{ + return FLoad(-PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV FNeg(const FloatV f) +{ + return FloatV(-f.x); +} + +PX_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b) +{ + return FloatV(a.x + b.x); +} + +PX_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b) +{ + return FloatV(a.x - b.x); +} + +PX_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b) +{ + return FloatV(a.x * b.x); +} + +PX_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(b.x != 0.0f); + return FloatV(a.x / b.x); +} + +PX_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b) +{ + VECMATHAOS_ASSERT(b.x != 0.0f); + return FloatV(a.x / b.x); +} + +PX_FORCE_INLINE FloatV FRecip(const FloatV a) +{ + VECMATHAOS_ASSERT(a.x != 0.0f); + return 1.0f / a.x; +} + +PX_FORCE_INLINE FloatV FRecipFast(const FloatV a) +{ + VECMATHAOS_ASSERT(a.x != 0.0f); + return 1.0f / a.x; +} + +PX_FORCE_INLINE FloatV FRsqrt(const FloatV a) +{ + VECMATHAOS_ASSERT(a.x != 0.0f); + return PxRecipSqrt(a.x); +} + +PX_FORCE_INLINE FloatV FSqrt(const FloatV a) +{ + return PxSqrt(a.x); +} + +PX_FORCE_INLINE FloatV FRsqrtFast(const FloatV a) +{ + VECMATHAOS_ASSERT(a.x != 0.0f); + return PxRecipSqrt(a.x); +} + +PX_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c) +{ + return FAdd(FMul(a, b), c); +} + +PX_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c) +{ + return FSub(c, FMul(a, b)); +} + +PX_FORCE_INLINE FloatV FAbs(const FloatV a) +{ + return FloatV(PxAbs(a.x)); +} + +PX_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b) +{ + return FloatV(c.ux ? a.x : b.x); +} + +PX_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b) +{ + return BLoad(a.x > b.x); +} + +PX_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b) +{ + return BLoad(a.x >= b.x); +} + +PX_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b) +{ + return BLoad(a.x == b.x); +} + +PX_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b) +{ + return (a.x > b.x ? FloatV(a.x) : FloatV(b.x)); +} + +PX_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b) +{ + return (a.x > b.x ? FloatV(b.x) : FloatV(a.x)); +} + +PX_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV) +{ + return FMax(FMin(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 FAllGrtr(const FloatV a, const FloatV b) +{ + return BOOL_TO_U32(a.x > b.x); +} + +PX_FORCE_INLINE PxU32 FAllGrtrOrEq(const FloatV a, const FloatV b) +{ + return BOOL_TO_U32(a.x >= b.x); +} +PX_FORCE_INLINE PxU32 FAllEq(const FloatV a, const FloatV b) +{ + return BOOL_TO_U32(a.x == b.x); +} + +PX_FORCE_INLINE FloatV FRound(const FloatV a) +{ + return floorf(a.x + 0.5f); +} + +PX_FORCE_INLINE FloatV FSin(const FloatV a) +{ + return sinf(a.x); +} + +PX_FORCE_INLINE FloatV FCos(const FloatV a) +{ + return cosf(a.x); +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max) +{ + return BOOL_TO_U32(a.x > max.x || a.x < min.x); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV min, const FloatV max) +{ + return BOOL_TO_U32(a.x >= min.x && a.x <= max.x); +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV bounds) +{ + return FOutOfBounds(a, FNeg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV bounds) +{ + return FInBounds(a, FNeg(bounds), bounds); +} + +///////////////////// +// VEC3V +///////////////////// + +PX_FORCE_INLINE Vec3V V3Splat(const FloatV f) +{ + return Vec3V(f.x, f.x, f.x); +} + +PX_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z) +{ + return Vec3V(x.x, y.x, z.x); +} + +PX_FORCE_INLINE Vec3V V3UnitX() +{ + return Vec3V(1.0f, 0.0f, 0.0f); +} + +PX_FORCE_INLINE Vec3V V3UnitY() +{ + return Vec3V(0.0f, 1.0f, 0.0f); +} + +PX_FORCE_INLINE Vec3V V3UnitZ() +{ + return Vec3V(0.0f, 0.0f, 1.0f); +} + +PX_FORCE_INLINE FloatV V3GetX(const Vec3V f) +{ + return FloatV(f.x); +} + +PX_FORCE_INLINE FloatV V3GetY(const Vec3V f) +{ + return FloatV(f.y); +} + +PX_FORCE_INLINE FloatV V3GetZ(const Vec3V f) +{ + return FloatV(f.z); +} + +PX_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f) +{ + return Vec3V(f.x, v.y, v.z); +} + +PX_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f) +{ + return Vec3V(v.x, f.x, v.z); +} + +PX_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f) +{ + return Vec3V(v.x, v.y, f.x); +} + +PX_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c) +{ + return Vec3V(a.x, b.x, c.x); +} + +PX_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c) +{ + return Vec3V(a.y, b.y, c.y); +} + +PX_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c) +{ + return Vec3V(a.z, b.z, c.z); +} + +PX_FORCE_INLINE Vec3V V3Zero() +{ + return V3Load(0.0f); +} + +PX_FORCE_INLINE Vec3V V3One() +{ + return V3Load(1.0f); +} + +PX_FORCE_INLINE Vec3V V3Eps() +{ + return V3Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec3V V3Neg(const Vec3V c) +{ + return Vec3V(-c.x, -c.y, -c.z); +} + +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x + b.x, a.y + b.y, a.z + b.z); +} + +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x - b.x, a.y - b.y, a.z - b.z); +} + +PX_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b) +{ + return Vec3V(a.x * b.x, a.y * b.x, a.z * b.x); +} + +PX_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x * b.x, a.y * b.y, a.z * b.z); +} + +PX_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b) +{ + const PxF32 bInv = 1.0f / b.x; + return Vec3V(a.x * bInv, a.y * bInv, a.z * bInv); +} + +PX_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x / b.x, a.y / b.y, a.z / b.z); +} + +PX_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b) +{ + const PxF32 bInv = 1.0f / b.x; + return Vec3V(a.x * bInv, a.y * bInv, a.z * bInv); +} + +PX_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x / b.x, a.y / b.y, a.z / b.z); +} + +PX_FORCE_INLINE Vec3V V3Recip(const Vec3V a) +{ + return Vec3V(1.0f / a.x, 1.0f / a.y, 1.0f / a.z); +} + +PX_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a) +{ + return Vec3V(1.0f / a.x, 1.0f / a.y, 1.0f / a.z); +} + +PX_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a) +{ + return Vec3V(PxRecipSqrt(a.x), PxRecipSqrt(a.y), PxRecipSqrt(a.z)); +} + +PX_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a) +{ + return Vec3V(PxRecipSqrt(a.x), PxRecipSqrt(a.y), PxRecipSqrt(a.z)); +} + +PX_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c) +{ + return V3Add(V3Scale(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c) +{ + return V3Sub(c, V3Scale(a, b)); +} + +PX_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c) +{ + return V3Add(V3Mul(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c) +{ + return V3Sub(c, V3Mul(a, b)); +} + +PX_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b) +{ + return FloatV(a.x * b.x + a.y * b.y + a.z * b.z); +} + +PX_FORCE_INLINE VecCrossV V3PrepareCross(const Vec3VArg normal) +{ + return normal; +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); +} + +PX_FORCE_INLINE FloatV V3Length(const Vec3V a) +{ + return FloatV(PxSqrt(a.x * a.x + a.y * a.y + a.z * a.z)); +} + +PX_FORCE_INLINE FloatV V3LengthSq(const Vec3V a) +{ + return FloatV(a.x * a.x + a.y * a.y + a.z * a.z); +} + +PX_FORCE_INLINE Vec3V V3Normalize(const Vec3V a) +{ + VECMATHAOS_ASSERT(a.x != 0 || a.y != 0 || a.z != 0); + const PxF32 lengthInv = 1.0f / PxSqrt(a.x * a.x + a.y * a.y + a.z * a.z); + return Vec3V(a.x * lengthInv, a.y * lengthInv, a.z * lengthInv); +} + +PX_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a, const Vec3V unsafeReturnValue) +{ + const PxF32 length = PxSqrt(a.x * a.x + a.y * a.y + a.z * a.z); + if(PX_EPS_REAL >= length) + { + return unsafeReturnValue; + } + else + { + const PxF32 lengthInv = 1.0f / length; + return Vec3V(a.x * lengthInv, a.y * lengthInv, a.z * lengthInv); + } +} + +PX_FORCE_INLINE Vec3V V3NormalizeFast(const Vec3V a) +{ + VECMATHAOS_ASSERT(a.x != 0 || a.y != 0 || a.z != 0); + const PxF32 lengthInv = 1.0f / PxSqrt(a.x * a.x + a.y * a.y + a.z * a.z); + return Vec3V(a.x * lengthInv, a.y * lengthInv, a.z * lengthInv); +} + +PX_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b) +{ + return Vec3V(c.ux ? a.x : b.x, c.uy ? a.y : b.y, c.uz ? a.z : b.z); +} + +PX_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b) +{ + return BoolV(BOOL_TO_U32(a.x > b.x), BOOL_TO_U32(a.y > b.y), BOOL_TO_U32(a.z > b.z), FALSE_TO_U32); +} + +PX_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b) +{ + return BoolV(BOOL_TO_U32(a.x >= b.x), BOOL_TO_U32(a.y >= b.y), BOOL_TO_U32(a.z >= b.z), TRUE_TO_U32); +} + +PX_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b) +{ + return BoolV(BOOL_TO_U32(a.x == b.x), BOOL_TO_U32(a.y == b.y), BOOL_TO_U32(a.z == b.z), TRUE_TO_U32); +} + +PX_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x > b.x ? a.x : b.x, a.y > b.y ? a.y : b.y, a.z > b.z ? a.z : b.z); +} + +PX_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b) +{ + return Vec3V(a.x < b.x ? a.x : b.x, a.y < b.y ? a.y : b.y, a.z < b.z ? a.z : b.z); +} + +PX_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a) +{ + const PxF32 t0 = (a.x >= a.y) ? a.x : a.y; + return t0 >= a.z ? t0 : a.z; +} + +PX_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a) +{ + const PxF32 t0 = (a.x <= a.y) ? a.x : a.y; + return t0 <= a.z ? t0 : a.z; +} + +// return (a >= 0.0f) ? 1.0f : -1.0f; +PX_FORCE_INLINE Vec3V V3Sign(const Vec3V a) +{ + return Vec3V((a.x >= 0.f ? 1.f : -1.f), (a.y >= 0.f ? 1.f : -1.f), (a.z >= 0.f ? 1.f : -1.f)); +} + +PX_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV) +{ + return V3Max(V3Min(a, maxV), minV); +} + +PX_FORCE_INLINE Vec3V V3Abs(const Vec3V a) +{ + return V3Max(a, V3Neg(a)); +} + +PX_FORCE_INLINE PxU32 V3AllGrtr(const Vec3V a, const Vec3V b) +{ + return BOOL_TO_U32((a.x > b.x) & (a.y > b.y) & (a.z > b.z)); +} + +PX_FORCE_INLINE PxU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b) +{ + return BOOL_TO_U32((a.x >= b.x) & (a.y >= b.y) & (a.z >= b.z)); +} + +PX_FORCE_INLINE PxU32 V3AllEq(const Vec3V a, const Vec3V b) +{ + return BOOL_TO_U32((a.x == b.x) & (a.y == b.y) & (a.z == b.z)); +} + +PX_FORCE_INLINE Vec3V V3Round(const Vec3V a) +{ + return Vec3V(floorf(a.x + 0.5f), floorf(a.y + 0.5f), floorf(a.z + 0.5f)); +} + +PX_FORCE_INLINE Vec3V V3Sin(const Vec3V a) +{ + return Vec3V(sinf(a.x), sinf(a.y), sinf(a.z)); +} + +PX_FORCE_INLINE Vec3V V3Cos(const Vec3V a) +{ + return Vec3V(cosf(a.x), cosf(a.y), cosf(a.z)); +} + +PX_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a) +{ + return Vec3V(a.y, a.z, a.z); +} + +PX_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a) +{ + return Vec3V(a.x, a.y, a.x); +} + +PX_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a) +{ + return Vec3V(a.y, a.z, a.x); +} + +PX_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a) +{ + return Vec3V(a.z, a.x, a.y); +} + +PX_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a) +{ + return Vec3V(a.z, a.z, a.y); +} + +PX_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a) +{ + return Vec3V(a.y, a.x, a.x); +} + +PX_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1) +{ + return Vec3V(0.0f, v1.z, v0.y); +} + +PX_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1) +{ + return Vec3V(v0.z, 0.0f, v1.x); +} + +PX_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1) +{ + return Vec3V(v1.y, v0.x, 0.0f); +} + +PX_FORCE_INLINE FloatV V3SumElems(const Vec3V a) +{ + return FloatV(a.x + a.y + a.z); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + return BOOL_TO_U32(a.x > max.x || a.y > max.y || a.z > max.z || a.x < min.x || a.y < min.y || a.z < min.z); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + return BOOL_TO_U32(a.x <= max.x && a.y <= max.y && a.z <= max.z && a.x >= min.x && a.y >= min.y && a.z >= min.z); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds) +{ + return V3OutOfBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V bounds) +{ + return V3InBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2) +{ + const PxF32 t01 = col0.y, t02 = col0.z, t12 = col1.z; + col0.y = col1.x; + col0.z = col2.x; + col1.z = col2.y; + col1.x = t01; + col2.x = t02; + col2.y = t12; +} + +///////////////////////// +// VEC4V +///////////////////////// + +PX_FORCE_INLINE Vec4V V4Splat(const FloatV f) +{ + return Vec4V(f.x, f.x, f.x, f.x); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatV* const floatVArray) +{ + return Vec4V(floatVArray[0].x, floatVArray[1].x, floatVArray[2].x, floatVArray[3].x); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w) +{ + return Vec4V(x.x, y.x, z.x, w.x); +} + +PX_FORCE_INLINE Vec4V V4MergeW(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + return Vec4V(x.w, y.w, z.w, w.w); +} + +PX_FORCE_INLINE Vec4V V4MergeZ(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + return Vec4V(x.z, y.z, z.z, w.z); +} + +PX_FORCE_INLINE Vec4V V4MergeY(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + return Vec4V(x.y, y.y, z.y, w.y); +} + +PX_FORCE_INLINE Vec4V V4MergeX(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + return Vec4V(x.x, y.x, z.x, w.x); +} + +PX_FORCE_INLINE Vec4V V4UnpackXY(const Vec4VArg a, const Vec4VArg b) +{ + return Vec4V(a.x, b.x, a.y, b.y); +} + +PX_FORCE_INLINE Vec4V V4UnpackZW(const Vec4VArg a, const Vec4VArg b) +{ + return Vec4V(a.z, b.z, a.w, b.w); +} + +PX_FORCE_INLINE Vec4V V4UnitX() +{ + return Vec4V(1.0f, 0.0f, 0.0f, 0.0f); +} + +PX_FORCE_INLINE Vec4V V4UnitY() +{ + return Vec4V(0.0f, 1.0f, 0.0f, 0.0f); +} + +PX_FORCE_INLINE Vec4V V4UnitZ() +{ + return Vec4V(0.0f, 0.0f, 1.0f, 0.0f); +} + +PX_FORCE_INLINE Vec4V V4UnitW() +{ + return Vec4V(0.0f, 0.0f, 0.0f, 1.0f); +} + +PX_FORCE_INLINE FloatV V4GetX(const Vec4V f) +{ + return FloatV(f.x); +} + +PX_FORCE_INLINE FloatV V4GetY(const Vec4V f) +{ + return FloatV(f.y); +} + +PX_FORCE_INLINE FloatV V4GetZ(const Vec4V f) +{ + return FloatV(f.z); +} + +PX_FORCE_INLINE FloatV V4GetW(const Vec4V f) +{ + return FloatV(f.w); +} + +PX_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f) +{ + return Vec4V(f.x, v.y, v.z, v.w); +} + +PX_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f) +{ + return Vec4V(v.x, f.x, v.z, v.w); +} + +PX_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f) +{ + return Vec4V(v.x, v.y, f.x, v.w); +} + +PX_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f) +{ + return Vec4V(v.x, v.y, v.z, f.x); +} + +PX_FORCE_INLINE Vec4V V4SetW(const Vec3V v, const FloatV f) +{ + return Vec4V(v.x, v.y, v.z, f.x); +} + +PX_FORCE_INLINE Vec4V V4ClearW(const Vec4V v) +{ + return Vec4V(v.x, v.y, v.z, 0.0f); +} + +PX_FORCE_INLINE Vec4V V4PermYXWZ(const Vec4V v) +{ + return Vec4V(v.y, v.x, v.w, v.z); +} + +PX_FORCE_INLINE Vec4V V4PermXZXZ(const Vec4V v) +{ + return Vec4V(v.x, v.z, v.x, v.z); +} + +PX_FORCE_INLINE Vec4V V4PermYWYW(const Vec4V v) +{ + return Vec4V(v.y, v.w, v.y, v.w); +} + +PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V v) +{ + return Vec4V(v.y, v.z, v.x, v.w); +} + +PX_FORCE_INLINE Vec4V V4PermZWXY(const Vec4V v) +{ + return Vec4V(v.z, v.w, v.x, v.y); +} + +template +PX_FORCE_INLINE Vec4V V4Perm(const Vec4V v) +{ + const PxF32 f[4] = { v.x, v.y, v.z, v.w }; + return Vec4V(f[_x], f[_y], f[_z], f[_w]); +} + +PX_FORCE_INLINE Vec4V V4Zero() +{ + return V4Load(0.0f); +} + +PX_FORCE_INLINE Vec4V V4One() +{ + return V4Load(1.0f); +} + +PX_FORCE_INLINE Vec4V V4Eps() +{ + return V4Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec4V V4Neg(const Vec4V c) +{ + return Vec4V(-c.x, -c.y, -c.z, -c.w); +} + +PX_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); +} + +PX_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); +} + +PX_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b) +{ + return Vec4V(a.x * b.x, a.y * b.x, a.z * b.x, a.w * b.x); +} + +PX_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); +} + +PX_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b) +{ + const PxF32 bInv = 1.0f / b.x; + return Vec4V(a.x * bInv, a.y * bInv, a.z * bInv, a.w * bInv); +} + +PX_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b) +{ + VECMATHAOS_ASSERT(b.x != 0 && b.y != 0 && b.z != 0 && b.w != 0); + return Vec4V(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); +} + +PX_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b) +{ + const PxF32 bInv = 1.0f / b.x; + return Vec4V(a.x * bInv, a.y * bInv, a.z * bInv, a.w * bInv); +} + +PX_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); +} + +PX_FORCE_INLINE Vec4V V4Recip(const Vec4V a) +{ + return Vec4V(1.0f / a.x, 1.0f / a.y, 1.0f / a.z, 1.0f / a.w); +} + +PX_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a) +{ + return Vec4V(1.0f / a.x, 1.0f / a.y, 1.0f / a.z, 1.0f / a.w); +} + +PX_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a) +{ + return Vec4V(PxRecipSqrt(a.x), PxRecipSqrt(a.y), PxRecipSqrt(a.z), PxRecipSqrt(a.w)); +} + +PX_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a) +{ + return Vec4V(PxRecipSqrt(a.x), PxRecipSqrt(a.y), PxRecipSqrt(a.z), PxRecipSqrt(a.w)); +} + +PX_FORCE_INLINE Vec4V V4Sqrt(const Vec4V a) +{ + return Vec4V(PxSqrt(a.x), PxSqrt(a.y), PxSqrt(a.z), PxSqrt(a.w)); +} + +PX_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c) +{ + return V4Add(V4Scale(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c) +{ + return V4Sub(c, V4Scale(a, b)); +} + +PX_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Add(V4Mul(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Sub(c, V4Mul(a, b)); +} + +PX_FORCE_INLINE FloatV V4SumElements(const Vec4V a) +{ + return FloatV(a.x + a.y + a.z + a.w); +} + +PX_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b) +{ + return FloatV(a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w); +} + +PX_FORCE_INLINE FloatV V4Dot3(const Vec4V a, const Vec4V b) +{ + return FloatV(a.x * b.x + a.y * b.y + a.z * b.z); +} + +PX_FORCE_INLINE Vec4V V4Cross(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x, 0.0f); +} + +PX_FORCE_INLINE FloatV V4Length(const Vec4V a) +{ + return FloatV(PxSqrt(a.x * a.x + a.y * a.y + a.z * a.z + a.w * a.w)); +} + +PX_FORCE_INLINE FloatV V4LengthSq(const Vec4V a) +{ + return V4Dot(a, a); +} + +PX_FORCE_INLINE Vec4V V4Normalize(const Vec4V a) +{ + VECMATHAOS_ASSERT(0 != a.x || 0 != a.y || 0 != a.z || 0 != a.w); + const FloatV length = FloatV(V4Length(a)); + return V4ScaleInv(a, length); +} + +PX_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a, const Vec4V unsafeReturnValue) +{ + const FloatV length = FloatV(V4Length(a)); + if(PX_EPS_REAL >= length.x) + { + return unsafeReturnValue; + } + else + { + return V4ScaleInv(a, length); + } +} +PX_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a) +{ + VECMATHAOS_ASSERT(0 != a.x || 0 != a.y || 0 != a.z || 0 != a.w); + const FloatV length = FloatV(V4Length(a)); + return V4ScaleInv(a, length); +} + +PX_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b) +{ + return Vec4V(c.ux ? a.x : b.x, c.uy ? a.y : b.y, c.uz ? a.z : b.z, c.uw ? a.w : b.w); +} + +PX_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b) +{ + return BoolV(BOOL_TO_U32(a.x > b.x), BOOL_TO_U32(a.y > b.y), BOOL_TO_U32(a.z > b.z), BOOL_TO_U32(a.w > b.w)); +} + +PX_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return BoolV(BOOL_TO_U32(a.x >= b.x), BOOL_TO_U32(a.y >= b.y), BOOL_TO_U32(a.z >= b.z), BOOL_TO_U32(a.w >= b.w)); +} + +PX_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b) +{ + return BoolV(BOOL_TO_U32(a.x == b.x), BOOL_TO_U32(a.y == b.y), BOOL_TO_U32(a.z == b.z), BOOL_TO_U32(a.w == b.w)); +} + +PX_FORCE_INLINE Vec4V V4Max(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x > b.x ? a.x : b.x, a.y > b.y ? a.y : b.y, a.z > b.z ? a.z : b.z, a.w > b.w ? a.w : b.w); +} + +PX_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b) +{ + return Vec4V(a.x < b.x ? a.x : b.x, a.y < b.y ? a.y : b.y, a.z < b.z ? a.z : b.z, a.w < b.w ? a.w : b.w); +} + +PX_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a) +{ + const PxF32 t0 = (a.x >= a.y) ? a.x : a.y; + const PxF32 t1 = (a.z >= a.w) ? a.x : a.w; + return t0 >= t1 ? t0 : t1; +} + +PX_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a) +{ + const PxF32 t0 = (a.x <= a.y) ? a.x : a.y; + const PxF32 t1 = (a.z <= a.w) ? a.x : a.w; + return t0 <= t1 ? t0 : t1; +} + +PX_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV) +{ + return V4Max(V4Min(a, maxV), minV); +} + +PX_FORCE_INLINE Vec4V V4Round(const Vec4V a) +{ + return Vec4V(floorf(a.x + 0.5f), floorf(a.y + 0.5f), floorf(a.z + 0.5f), floorf(a.w + 0.5f)); +} + +PX_FORCE_INLINE Vec4V V4Sin(const Vec4V a) +{ + return Vec4V(sinf(a.x), sinf(a.y), sinf(a.z), sinf(a.w)); +} + +PX_FORCE_INLINE Vec4V V4Cos(const Vec4V a) +{ + return Vec4V(cosf(a.x), cosf(a.y), cosf(a.z), cosf(a.w)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtr(const Vec4V a, const Vec4V b) +{ + return BOOL_TO_U32((a.x > b.x) & (a.y > b.y) & (a.z > b.z) & (a.w > b.w)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return BOOL_TO_U32((a.x >= b.x) & (a.y >= b.y) & (a.z >= b.z) & (a.w >= b.w)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq3(const Vec4V a, const Vec4V b) +{ + return BOOL_TO_U32((a.x >= b.x) & (a.y >= b.y) & (a.z >= b.z)); +} + +PX_FORCE_INLINE PxU32 V4AllEq(const Vec4V a, const Vec4V b) +{ + return BOOL_TO_U32((a.x == b.x) & (a.y == b.y) & (a.z == b.z) & (a.w == b.w)); +} + +PX_FORCE_INLINE PxU32 V4AnyGrtr3(const Vec4V a, const Vec4V b) +{ + return BOOL_TO_U32((a.x > b.x) | (a.y > b.y) | (a.z > b.z)); +} + +PX_FORCE_INLINE void V4Transpose(Vec4V& col0, Vec4V& col1, Vec4V& col2, Vec4V& col3) +{ + const PxF32 t01 = col0.y, t02 = col0.z, t03 = col0.w; + const PxF32 t12 = col1.z, t13 = col1.w; + const PxF32 t23 = col2.w; + col0.y = col1.x; + col0.z = col2.x; + col0.w = col3.x; + col1.z = col2.y; + col1.w = col3.y; + col2.w = col3.z; + col1.x = t01; + col2.x = t02; + col3.x = t03; + col2.y = t12; + col3.y = t13; + col3.z = t23; +} + +PX_FORCE_INLINE BoolV BFFFF() +{ + return BoolV(FALSE_TO_U32, FALSE_TO_U32, FALSE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BFFFT() +{ + return BoolV(FALSE_TO_U32, FALSE_TO_U32, FALSE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BFFTF() +{ + return BoolV(FALSE_TO_U32, FALSE_TO_U32, TRUE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BFFTT() +{ + return BoolV(FALSE_TO_U32, FALSE_TO_U32, TRUE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BFTFF() +{ + return BoolV(FALSE_TO_U32, TRUE_TO_U32, FALSE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BFTFT() +{ + return BoolV(FALSE_TO_U32, TRUE_TO_U32, FALSE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BFTTF() +{ + return BoolV(FALSE_TO_U32, TRUE_TO_U32, TRUE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BFTTT() +{ + return BoolV(FALSE_TO_U32, TRUE_TO_U32, TRUE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BTFFF() +{ + return BoolV(TRUE_TO_U32, FALSE_TO_U32, FALSE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BTFFT() +{ + return BoolV(TRUE_TO_U32, FALSE_TO_U32, FALSE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BTFTF() +{ + return BoolV(TRUE_TO_U32, FALSE_TO_U32, TRUE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BTFTT() +{ + return BoolV(TRUE_TO_U32, FALSE_TO_U32, TRUE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BTTFF() +{ + return BoolV(TRUE_TO_U32, TRUE_TO_U32, FALSE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BTTFT() +{ + return BoolV(TRUE_TO_U32, TRUE_TO_U32, FALSE_TO_U32, TRUE_TO_U32); +} +PX_FORCE_INLINE BoolV BTTTF() +{ + return BoolV(TRUE_TO_U32, TRUE_TO_U32, TRUE_TO_U32, FALSE_TO_U32); +} +PX_FORCE_INLINE BoolV BTTTT() +{ + return BoolV(TRUE_TO_U32, TRUE_TO_U32, TRUE_TO_U32, TRUE_TO_U32); +} + +PX_FORCE_INLINE BoolV BXMask() +{ + return BTFFF(); +} +PX_FORCE_INLINE BoolV BYMask() +{ + return BFTFF(); +} +PX_FORCE_INLINE BoolV BZMask() +{ + return BFFTF(); +} +PX_FORCE_INLINE BoolV BWMask() +{ + return BFFFT(); +} + +PX_FORCE_INLINE BoolV BGetX(const BoolV a) +{ + return BoolV(a.ux, a.ux, a.ux, a.ux); +} + +PX_FORCE_INLINE BoolV BGetY(const BoolV a) +{ + return BoolV(a.uy, a.uy, a.uy, a.uy); +} + +PX_FORCE_INLINE BoolV BGetZ(const BoolV a) +{ + return BoolV(a.uz, a.uz, a.uz, a.uz); +} + +PX_FORCE_INLINE BoolV BGetW(const BoolV a) +{ + return BoolV(a.uw, a.uw, a.uw, a.uw); +} + +PX_FORCE_INLINE BoolV BSetX(const BoolV v, const BoolV f) +{ + return BoolV(f.ux, v.uy, v.uz, v.uw); +} + +PX_FORCE_INLINE BoolV BSetY(const BoolV v, const BoolV f) +{ + return BoolV(v.ux, f.uy, v.uz, v.uw); +} + +PX_FORCE_INLINE BoolV BSetZ(const BoolV v, const BoolV f) +{ + return BoolV(v.ux, v.uy, f.uz, v.uw); +} + +PX_FORCE_INLINE BoolV BSetW(const BoolV v, const BoolV f) +{ + return BoolV(v.ux, v.uy, v.uz, f.uw); +} + +template +BoolV BSplatElement(BoolV a) +{ + PxU32* b = (PxU32*)&a; + return BoolV(b[index], b[index], b[index], b[index]); +} + +PX_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b) +{ + return BoolV(BOOL_TO_U32(a.ux && b.ux), BOOL_TO_U32(a.uy && b.uy), BOOL_TO_U32(a.uz && b.uz), BOOL_TO_U32(a.uw && b.uw)); +} + +PX_FORCE_INLINE BoolV BAndNot(const BoolV a, const BoolV b) +{ + return BoolV(a.ux & ~b.ux, a.uy & ~b.uy, a.uz & ~b.uz, a.uw & ~b.uw); +} + +PX_FORCE_INLINE BoolV BNot(const BoolV a) +{ + return BoolV(~a.ux, ~a.uy, ~a.uz, ~a.uw); +} + +PX_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b) +{ + return BoolV(BOOL_TO_U32(a.ux || b.ux), BOOL_TO_U32(a.uy || b.uy), BOOL_TO_U32(a.uz || b.uz), BOOL_TO_U32(a.uw || b.uw)); +} + +PX_FORCE_INLINE PxU32 BAllEq(const BoolV a, const BoolV b) +{ + return (a.ux == b.ux && a.uy == b.uy && a.uz == b.uz && a.uw == b.uw ? 1 : 0); +} + +PX_FORCE_INLINE PxU32 BAllEqTTTT(const BoolV a) +{ + return BAllEq(a, BTTTT()); +} + +PX_FORCE_INLINE PxU32 BAllEqFFFF(const BoolV a) +{ + return BAllEq(a, BFFFF()); +} + +PX_FORCE_INLINE BoolV BAllTrue4(const BoolV a) +{ + return (a.ux & a.uy & a.uz & a.uw) ? BTTTT() : BFFFF(); +} + +PX_FORCE_INLINE BoolV BAnyTrue4(const BoolV a) +{ + return (a.ux | a.uy | a.uz | a.uw) ? BTTTT() : BFFFF(); +} + +PX_FORCE_INLINE BoolV BAllTrue3(const BoolV a) +{ + return (a.ux & a.uy & a.uz) ? BTTTT() : BFFFF(); +} + +PX_FORCE_INLINE BoolV BAnyTrue3(const BoolV a) +{ + return (a.ux | a.uy | a.uz) ? BTTTT() : BFFFF(); +} + +PX_FORCE_INLINE PxU32 BGetBitMask(const BoolV a) +{ + return (a.ux & 1) | (a.uy & 2) | (a.uz & 4) | (a.uw & 8); +} + +////////////////////////////////// +// MAT33V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M33MulV3(const Mat33V& a, const Vec3V b) +{ + return Vec3V(a.col0.x * b.x + a.col1.x * b.y + a.col2.x * b.z, a.col0.y * b.x + a.col1.y * b.y + a.col2.y * b.z, + a.col0.z * b.x + a.col1.z * b.y + a.col2.z * b.z); +} + +PX_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V& a, const Vec3V b) +{ + return Vec3V(a.col0.x * b.x + a.col0.y * b.y + a.col0.z * b.z, a.col1.x * b.x + a.col1.y * b.y + a.col1.z * b.z, + a.col2.x * b.x + a.col2.y * b.y + a.col2.z * b.z); +} + +PX_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V& A, const Vec3V b, const Vec3V c) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + Vec3V result = V3ScaleAdd(A.col0, x, c); + result = V3ScaleAdd(A.col1, y, result); + return V3ScaleAdd(A.col2, z, result); +} + +PX_FORCE_INLINE Mat33V M33MulM33(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(M33MulV3(a, b.col0), M33MulV3(a, b.col1), M33MulV3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Add(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Scale(const Mat33V& a, const FloatV& b) +{ + return Mat33V(V3Scale(a.col0, b), V3Scale(a.col1, b), V3Scale(a.col2, b)); +} + +PX_FORCE_INLINE Mat33V M33Sub(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Sub(a.col0, b.col0), V3Sub(a.col1, b.col1), V3Sub(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Neg(const Mat33V& a) +{ + return Mat33V(V3Neg(a.col0), V3Neg(a.col1), V3Neg(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Abs(const Mat33V& a) +{ + return Mat33V(V3Abs(a.col0), V3Abs(a.col1), V3Abs(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg d) +{ + const Vec3V x = V3Mul(V3UnitX(), d); + const Vec3V y = V3Mul(V3UnitY(), d); + const Vec3V z = V3Mul(V3UnitZ(), d); + return Mat33V(x, y, z); +} + +PX_FORCE_INLINE Mat33V M33Inverse(const Mat33V& a) +{ + const PxF32 det = a.col0.x * (a.col1.y * a.col2.z - a.col1.z * a.col2.y) - + a.col1.x * (a.col0.y * a.col2.z - a.col2.y * a.col0.z) + + a.col2.x * (a.col0.y * a.col1.z - a.col1.y * a.col0.z); + + const PxF32 invDet = 1.0f / det; + + Mat33V ret; + ret.col0.x = invDet * (a.col1.y * a.col2.z - a.col2.y * a.col1.z); + ret.col0.y = invDet * (a.col2.y * a.col0.z - a.col0.y * a.col2.z); + ret.col0.z = invDet * (a.col0.y * a.col1.z - a.col1.y * a.col0.z); + + ret.col1.x = invDet * (a.col2.x * a.col1.z - a.col1.x * a.col2.z); + ret.col1.y = invDet * (a.col0.x * a.col2.z - a.col2.x * a.col0.z); + ret.col1.z = invDet * (a.col1.x * a.col0.z - a.col0.x * a.col1.z); + + ret.col2.x = invDet * (a.col1.x * a.col2.y - a.col2.x * a.col1.y); + ret.col2.y = invDet * (a.col2.x * a.col0.y - a.col0.x * a.col2.y); + ret.col2.z = invDet * (a.col0.x * a.col1.y - a.col1.x * a.col0.y); + + return ret; +} + +PX_FORCE_INLINE Mat33V Mat33V_From_PxMat33(const PxMat33& m) +{ + return Mat33V(V3LoadU(m.column0), V3LoadU(m.column1), V3LoadU(m.column2)); +} + +PX_FORCE_INLINE void PxMat33_From_Mat33V(const Mat33V& m, PxMat33& out) +{ + PX_ASSERT((size_t(&out) & 15) == 0); + V3StoreU(m.col0, out.column0); + V3StoreU(m.col1, out.column1); + V3StoreU(m.col2, out.column2); +} + +PX_FORCE_INLINE Mat33V M33Trnsps(const Mat33V& a) +{ + return Mat33V(Vec3V(a.col0.x, a.col1.x, a.col2.x), Vec3V(a.col0.y, a.col1.y, a.col2.y), + Vec3V(a.col0.z, a.col1.z, a.col2.z)); +} + +PX_FORCE_INLINE Mat33V M33Identity() +{ + return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ()); +} + +////////////////////////////////// +// MAT34V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M34MulV3(const Mat34V& a, const Vec3V b) +{ + return Vec3V(a.col0.x * b.x + a.col1.x * b.y + a.col2.x * b.z + a.col3.x, + a.col0.y * b.x + a.col1.y * b.y + a.col2.y * b.z + a.col3.y, + a.col0.z * b.x + a.col1.z * b.y + a.col2.z * b.z + a.col3.z); +} + +PX_FORCE_INLINE Vec3V M34Mul33V3(const Mat34V& a, const Vec3V b) +{ + return Vec3V(a.col0.x * b.x + a.col1.x * b.y + a.col2.x * b.z, a.col0.y * b.x + a.col1.y * b.y + a.col2.y * b.z, + a.col0.z * b.x + a.col1.z * b.y + a.col2.z * b.z); +} + +PX_FORCE_INLINE Vec3V M34TrnspsMul33V3(const Mat34V& a, const Vec3V b) +{ + return Vec3V(a.col0.x * b.x + a.col0.y * b.y + a.col0.z * b.z, a.col1.x * b.x + a.col1.y * b.y + a.col1.z * b.z, + a.col2.x * b.x + a.col2.y * b.y + a.col2.z * b.z); +} + +PX_FORCE_INLINE Mat34V M34MulM34(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2), M34MulV3(a, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34MulM33(const Mat34V& a, const Mat33V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M34Mul33V3(const Mat34V& a, const Mat33V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M34Mul33MM34(const Mat34V& a, const Mat34V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat34V M34Add(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2), V3Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34Trnsps33(const Mat34V& a) +{ + return Mat33V(Vec3V(a.col0.x, a.col1.x, a.col2.x), Vec3V(a.col0.y, a.col1.y, a.col2.y), + Vec3V(a.col0.z, a.col1.z, a.col2.z)); +} + +////////////////////////////////// +// MAT44V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V M44MulV4(const Mat44V& a, const Vec4V b) +{ + return Vec4V(a.col0.x * b.x + a.col1.x * b.y + a.col2.x * b.z + a.col3.x * b.w, + a.col0.y * b.x + a.col1.y * b.y + a.col2.y * b.z + a.col3.y * b.w, + a.col0.z * b.x + a.col1.z * b.y + a.col2.z * b.z + a.col3.z * b.w, + a.col0.w * b.x + a.col1.w * b.y + a.col2.w * b.z + a.col3.w * b.w); +} + +PX_FORCE_INLINE Vec4V M44TrnspsMulV4(const Mat44V& a, const Vec4V b) +{ + return Vec4V(a.col0.x * b.x + a.col0.y * b.y + a.col0.z * b.z + a.col0.w * b.w, + a.col1.x * b.x + a.col1.y * b.y + a.col1.z * b.z + a.col1.w * b.w, + a.col2.x * b.x + a.col2.y * b.y + a.col2.z * b.z + a.col2.w * b.w, + a.col3.x * b.x + a.col3.y * b.y + a.col3.z * b.z + a.col3.w * b.w); +} + +PX_FORCE_INLINE Mat44V M44MulM44(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(M44MulV4(a, b.col0), M44MulV4(a, b.col1), M44MulV4(a, b.col2), M44MulV4(a, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Add(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(V4Add(a.col0, b.col0), V4Add(a.col1, b.col1), V4Add(a.col2, b.col2), V4Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Inverse(const Mat44V& a) +{ + PxF32 tmp[12]; + PxF32 dst[16]; + PxF32 det; + + const PxF32 src[16] = { a.col0.x, a.col0.y, a.col0.z, a.col0.w, a.col1.x, a.col1.y, a.col1.z, a.col1.w, + a.col2.x, a.col2.y, a.col2.z, a.col2.w, a.col3.x, a.col3.y, a.col3.z, a.col3.w }; + + tmp[0] = src[10] * src[15]; + tmp[1] = src[11] * src[14]; + tmp[2] = src[9] * src[15]; + tmp[3] = src[11] * src[13]; + tmp[4] = src[9] * src[14]; + tmp[5] = src[10] * src[13]; + tmp[6] = src[8] * src[15]; + tmp[7] = src[11] * src[12]; + tmp[8] = src[8] * src[14]; + tmp[9] = src[10] * src[12]; + tmp[10] = src[8] * src[13]; + tmp[11] = src[9] * src[12]; + + dst[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7]; + dst[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7]; + dst[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7]; + dst[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7]; + dst[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7]; + dst[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7]; + dst[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6]; + dst[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6]; + dst[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3]; + dst[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3]; + dst[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3]; + dst[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3]; + dst[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3]; + dst[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3]; + dst[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2]; + dst[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2]; + + tmp[0] = src[2] * src[7]; + tmp[1] = src[3] * src[6]; + tmp[2] = src[1] * src[7]; + tmp[3] = src[3] * src[5]; + tmp[4] = src[1] * src[6]; + tmp[5] = src[2] * src[5]; + tmp[6] = src[0] * src[7]; + tmp[7] = src[3] * src[4]; + tmp[8] = src[0] * src[6]; + tmp[9] = src[2] * src[4]; + tmp[10] = src[0] * src[5]; + tmp[11] = src[1] * src[4]; + + dst[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15]; + dst[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15]; + dst[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15]; + dst[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15]; + dst[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15]; + dst[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15]; + dst[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14]; + dst[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14]; + dst[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9]; + dst[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10]; + dst[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10]; + dst[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8]; + dst[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8]; + dst[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9]; + dst[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9]; + dst[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8]; + + det = src[0] * dst[0] + src[1] * dst[1] + src[2] * dst[2] + src[3] * dst[3]; + + det = 1.0f / det; + for(PxU32 j = 0; j < 16; j++) + { + dst[j] *= det; + } + + return Mat44V(Vec4V(dst[0], dst[4], dst[8], dst[12]), Vec4V(dst[1], dst[5], dst[9], dst[13]), + Vec4V(dst[2], dst[6], dst[10], dst[14]), Vec4V(dst[3], dst[7], dst[11], dst[15])); +} + +PX_FORCE_INLINE Mat44V M44Trnsps(const Mat44V& a) +{ + return Mat44V(Vec4V(a.col0.x, a.col1.x, a.col2.x, a.col3.x), Vec4V(a.col0.y, a.col1.y, a.col2.y, a.col3.y), + Vec4V(a.col0.z, a.col1.z, a.col2.z, a.col3.z), Vec4V(a.col0.w, a.col1.w, a.col2.w, a.col3.w)); +} + +PX_FORCE_INLINE Vec4V V4LoadXYZW(const PxF32& x, const PxF32& y, const PxF32& z, const PxF32& w) +{ + return Vec4V(x, y, z, w); +} + +/* +PX_FORCE_INLINE VecU16V V4U32PK(VecU32V a, VecU32V b) +{ + return VecU16V( + PxU16(PxClamp((a).u32[0], 0, 0xFFFF)), + PxU16(PxClamp((a).u32[1], 0, 0xFFFF)), + PxU16(PxClamp((a).u32[2], 0, 0xFFFF)), + PxU16(PxClamp((a).u32[3], 0, 0xFFFF)), + PxU16(PxClamp((b).u32[0], 0, 0xFFFF)), + PxU16(PxClamp((b).u32[1], 0, 0xFFFF)), + PxU16(PxClamp((b).u32[2], 0, 0xFFFF)), + PxU16(PxClamp((b).u32[3], 0, 0xFFFF))); +} +*/ + +PX_FORCE_INLINE VecU32V V4U32Sel(const BoolV c, const VecU32V a, const VecU32V b) +{ + return VecU32V(c.ux ? a.u32[0] : b.u32[0], c.uy ? a.u32[1] : b.u32[1], c.uz ? a.u32[2] : b.u32[2], + c.uw ? a.u32[3] : b.u32[3]); +} + +PX_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b) +{ + return VecU32V((a).u32[0] | (b).u32[0], (a).u32[1] | (b).u32[1], (a).u32[2] | (b).u32[2], (a).u32[3] | (b).u32[3]); +} + +PX_FORCE_INLINE VecU32V V4U32xor(VecU32V a, VecU32V b) +{ + return VecU32V((a).u32[0] ^ (b).u32[0], (a).u32[1] ^ (b).u32[1], (a).u32[2] ^ (b).u32[2], (a).u32[3] ^ (b).u32[3]); +} + +PX_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b) +{ + return VecU32V((a).u32[0] & (b).u32[0], (a).u32[1] & (b).u32[1], (a).u32[2] & (b).u32[2], (a).u32[3] & (b).u32[3]); +} + +PX_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b) +{ + return VecU32V((a).u32[0] & ~(b).u32[0], (a).u32[1] & ~(b).u32[1], (a).u32[2] & ~(b).u32[2], + (a).u32[3] & ~(b).u32[3]); +} + +/* +PX_FORCE_INLINE VecU16V V4U16Or(VecU16V a, VecU16V b) +{ + return VecU16V( + (a).u16[0]|(b).u16[0], (a).u16[1]|(b).u16[1], (a).u16[2]|(b).u16[2], (a).u16[3]|(b).u16[3], + (a).u16[4]|(b).u16[4], (a).u16[5]|(b).u16[5], (a).u16[6]|(b).u16[6], (a).u16[7]|(b).u16[7]); +} +*/ + +/* +PX_FORCE_INLINE VecU16V V4U16And(VecU16V a, VecU16V b) +{ + return VecU16V( + (a).u16[0]&(b).u16[0], (a).u16[1]&(b).u16[1], (a).u16[2]&(b).u16[2], (a).u16[3]&(b).u16[3], + (a).u16[4]&(b).u16[4], (a).u16[5]&(b).u16[5], (a).u16[6]&(b).u16[6], (a).u16[7]&(b).u16[7]); +} +*/ + +/* +PX_FORCE_INLINE VecU16V V4U16Andc(VecU16V a, VecU16V b) +{ + return VecU16V( + (a).u16[0]&~(b).u16[0], (a).u16[1]&~(b).u16[1], (a).u16[2]&~(b).u16[2], (a).u16[3]&~(b).u16[3], + (a).u16[4]&~(b).u16[4], (a).u16[5]&~(b).u16[5], (a).u16[6]&~(b).u16[6], (a).u16[7]&~(b).u16[7]); +} +*/ + +/* +template PX_FORCE_INLINE VecI32V V4ISplat() +{ + return VecI32V(a, a, a, a); +} + +template PX_FORCE_INLINE VecU32V V4USplat() +{ + return VecU32V(a, a, a, a); +} +*/ + +/* +PX_FORCE_INLINE void V4U16StoreAligned(VecU16V val, VecU16V* address) +{ + *address = val; +} +*/ + +PX_FORCE_INLINE void V4U32StoreAligned(VecU32V val, VecU32V* address) +{ + *address = val; +} + +PX_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b) +{ + VecU32V r = V4U32Andc(*reinterpret_cast(&a), b); + return (*reinterpret_cast(&r)); +} + +PX_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b) +{ + return VecU32V(a.x > b.x ? 0xFFFFffff : 0, a.y > b.y ? 0xFFFFffff : 0, a.z > b.z ? 0xFFFFffff : 0, + a.w > b.w ? 0xFFFFffff : 0); +} + +PX_FORCE_INLINE VecU16V V4U16LoadAligned(VecU16V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE VecU16V V4U16LoadUnaligned(VecU16V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE VecU16V V4U16CompareGt(VecU16V a, VecU16V b) +{ + return VecU16V + ( + BOOL_TO_U16(a.u16[0] > b.u16[0]), BOOL_TO_U16(a.u16[1] > b.u16[1]), BOOL_TO_U16(a.u16[2] > b.u16[2]), BOOL_TO_U16(a.u16[3] > b.u16[3]), + BOOL_TO_U16(a.u16[4] > b.u16[4]), BOOL_TO_U16(a.u16[5] > b.u16[5]), BOOL_TO_U16(a.u16[6] > b.u16[6]), BOOL_TO_U16(a.u16[7] > b.u16[7]) + ); +} + +PX_FORCE_INLINE VecU16V V4I16CompareGt(VecU16V a, VecU16V b) +{ + return VecU16V + ( + BOOL_TO_U16(a.i16[0] > b.i16[0]), BOOL_TO_U16(a.i16[1] > b.i16[1]), BOOL_TO_U16(a.i16[2] > b.i16[2]), BOOL_TO_U16(a.i16[3] > b.i16[3]), + BOOL_TO_U16(a.i16[4] > b.i16[4]), BOOL_TO_U16(a.i16[5] > b.i16[5]), BOOL_TO_U16(a.i16[6] > b.i16[6]), BOOL_TO_U16(a.i16[7] > b.i16[7]) + ); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a) +{ + return Vec4V(PxF32((a).u32[0]), PxF32((a).u32[1]), PxF32((a).u32[2]), PxF32((a).u32[3])); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecI32V(VecI32V a) +{ + return Vec4V(PxF32((a).i32[0]), PxF32((a).i32[1]), PxF32((a).i32[2]), PxF32((a).i32[3])); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_Vec4V(Vec4V a) +{ + float* data = (float*)&a; + return VecI32V(PxI32(data[0]), PxI32(data[1]), PxI32(data[2]), PxI32(data[3])); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecU32V(VecU32V a) +{ + Vec4V b = *reinterpret_cast(&a); + return b; +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecI32V(VecI32V a) +{ + Vec4V b = *reinterpret_cast(&a); + return b; +} + +PX_FORCE_INLINE VecU32V VecU32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + VecU32V b = *reinterpret_cast(&a); + return b; +} + +PX_FORCE_INLINE VecI32V VecI32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + VecI32V b = *reinterpret_cast(&a); + return b; +} + +template +PX_FORCE_INLINE VecU32V V4U32SplatElement(VecU32V a) +{ + return VecU32V((a).u32[index], (a).u32[index], (a).u32[index], (a).u32[index]); +} + +template +PX_FORCE_INLINE VecU32V V4U32SplatElement(BoolV a) +{ + const PxU32 u = (&a.ux)[index]; + return VecU32V(u, u, u, u); +} + +template +PX_FORCE_INLINE Vec4V V4SplatElement(Vec4V a) +{ + float* data = (float*)&a; + return Vec4V(data[index], data[index], data[index], data[index]); +} + +PX_FORCE_INLINE VecU32V U4LoadXYZW(PxU32 x, PxU32 y, PxU32 z, PxU32 w) +{ + return VecU32V(x, y, z, w); +} + +PX_FORCE_INLINE Vec4V V4Abs(const Vec4V a) +{ + return V4Max(a, V4Neg(a)); +} + +PX_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b) +{ + return BoolV(BOOL_TO_U32(a.u32[0] == b.u32[0]), BOOL_TO_U32(a.u32[1] == b.u32[1]), BOOL_TO_U32(a.u32[2] == b.u32[2]), BOOL_TO_U32(a.u32[3] == b.u32[3])); +} + +PX_FORCE_INLINE VecU32V U4Load(const PxU32 i) +{ + return VecU32V(i, i, i, i); +} + +PX_FORCE_INLINE VecU32V U4LoadU(const PxU32* i) +{ + return VecU32V(i[0], i[1], i[2], i[3]); +} + +PX_FORCE_INLINE VecU32V U4LoadA(const PxU32* i) +{ + return VecU32V(i[0], i[1], i[2], i[3]); +} + +PX_FORCE_INLINE VecI32V I4Load(const PxI32 i) +{ + return VecI32V(i, i, i, i); +} + +PX_FORCE_INLINE VecI32V I4LoadU(const PxI32* i) +{ + return VecI32V(i[0], i[1], i[2], i[3]); +} + +PX_FORCE_INLINE VecI32V I4LoadA(const PxI32* i) +{ + return VecI32V(i[0], i[1], i[2], i[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b) +{ + return VecI32V(a.i32[0] + b.i32[0], a.i32[1] + b.i32[1], a.i32[2] + b.i32[2], a.i32[3] + b.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b) +{ + return VecI32V(a.i32[0] - b.i32[0], a.i32[1] - b.i32[1], a.i32[2] - b.i32[2], a.i32[3] - b.i32[3]); +} + +PX_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b) +{ + return BoolV(BOOL_TO_U32(a.i32[0] > b.i32[0]), BOOL_TO_U32(a.i32[1] > b.i32[1]), BOOL_TO_U32(a.i32[2] > b.i32[2]), BOOL_TO_U32(a.i32[3] > b.i32[3])); +} + +PX_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b) +{ + return BoolV(BOOL_TO_U32(a.i32[0] == b.i32[0]), BOOL_TO_U32(a.i32[1] == b.i32[1]), BOOL_TO_U32(a.i32[2] == b.i32[2]), BOOL_TO_U32(a.i32[3] == b.i32[3])); +} + +PX_FORCE_INLINE VecI32V V4I32Sel(const BoolV c, const VecI32V a, const VecI32V b) +{ + return VecI32V(c.ux ? a.i32[0] : b.i32[0], c.uy ? a.i32[1] : b.i32[1], c.uz ? a.i32[2] : b.i32[2], + c.uw ? a.i32[3] : b.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Zero() +{ + return VecI32V(0, 0, 0, 0); +} + +PX_FORCE_INLINE VecI32V VecI32V_One() +{ + return VecI32V(1, 1, 1, 1); +} + +PX_FORCE_INLINE VecI32V VecI32V_Two() +{ + return VecI32V(2, 2, 2, 2); +} + +PX_FORCE_INLINE VecI32V VecI32V_MinusOne() +{ + return VecI32V(-1, -1, -1, -1); +} + +PX_FORCE_INLINE VecU32V U4Zero() +{ + return VecU32V(0, 0, 0, 0); +} + +PX_FORCE_INLINE VecU32V U4One() +{ + return VecU32V(1, 1, 1, 1); +} + +PX_FORCE_INLINE VecU32V U4Two() +{ + return VecU32V(2, 2, 2, 2); +} + +PX_FORCE_INLINE VecShiftV VecI32V_PrepareShift(const VecI32VArg shift) +{ + return shift; +} + +PX_FORCE_INLINE VecI32V VecI32V_LeftShift(const VecI32VArg a, const VecShiftVArg count) +{ + return VecI32V(a.i32[0] << count.i32[0], a.i32[1] << count.i32[1], a.i32[2] << count.i32[2], a.i32[3] + << count.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_RightShift(const VecI32VArg a, const VecShiftVArg count) +{ + return VecI32V(a.i32[0] >> count.i32[0], a.i32[1] >> count.i32[1], a.i32[2] >> count.i32[2], + a.i32[3] >> count.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_And(const VecI32VArg a, const VecI32VArg b) +{ + return VecI32V(a.i32[0] & b.i32[0], a.i32[1] & b.i32[1], a.i32[2] & b.i32[2], a.i32[3] & b.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Or(const VecI32VArg a, const VecI32VArg b) +{ + return VecI32V(a.i32[0] | b.i32[0], a.i32[1] | b.i32[1], a.i32[2] | b.i32[2], a.i32[3] | b.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetX(const VecI32VArg a) +{ + return VecI32V(a.i32[0], a.i32[0], a.i32[0], a.i32[0]); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetY(const VecI32VArg a) +{ + return VecI32V(a.i32[1], a.i32[1], a.i32[1], a.i32[1]); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetZ(const VecI32VArg a) +{ + return VecI32V(a.i32[2], a.i32[2], a.i32[2], a.i32[2]); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetW(const VecI32VArg a) +{ + return VecI32V(a.i32[3], a.i32[3], a.i32[3], a.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sel(const BoolV c, const VecI32VArg a, const VecI32VArg b) +{ + return VecI32V(c.ux ? a.i32[0] : b.i32[0], c.uy ? a.i32[1] : b.i32[1], c.uz ? a.i32[2] : b.i32[2], + c.uw ? a.i32[3] : b.i32[3]); +} + +PX_FORCE_INLINE VecI32V VecI32V_Merge(const VecI32VArg a, const VecI32VArg b, const VecI32VArg c, const VecI32VArg d) +{ + return VecI32V(a.i32[0], b.i32[0], c.i32[0], d.i32[0]); +} + +PX_FORCE_INLINE void PxI32_From_VecI32V(const VecI32VArg a, PxI32* i) +{ + *i = a.i32[0]; +} + +PX_FORCE_INLINE VecI32V VecI32V_From_BoolV(const BoolVArg b) +{ + return VecI32V(PxI32(b.ux), PxI32(b.uy), PxI32(b.uz), PxI32(b.uw)); +} + +PX_FORCE_INLINE VecU32V VecU32V_From_BoolV(const BoolVArg b) +{ + return VecU32V(b.ux, b.uy, b.uz, b.uw); +} + +PX_FORCE_INLINE void QuatGetMat33V(const QuatVArg q, Vec3V& column0, Vec3V& column1, Vec3V& column2) +{ + const FloatV one = FOne(); + const FloatV x = V4GetX(q); + const FloatV y = V4GetY(q); + const FloatV z = V4GetZ(q); + const FloatV w = V4GetW(q); + + const FloatV x2 = FAdd(x, x); + const FloatV y2 = FAdd(y, y); + const FloatV z2 = FAdd(z, z); + + const FloatV xx = FMul(x2, x); + const FloatV yy = FMul(y2, y); + const FloatV zz = FMul(z2, z); + + const FloatV xy = FMul(x2, y); + const FloatV xz = FMul(x2, z); + const FloatV xw = FMul(x2, w); + + const FloatV yz = FMul(y2, z); + const FloatV yw = FMul(y2, w); + const FloatV zw = FMul(z2, w); + + const FloatV v = FSub(one, xx); + + column0 = V3Merge(FSub(FSub(one, yy), zz), FAdd(xy, zw), FSub(xz, yw)); + column1 = V3Merge(FSub(xy, zw), FSub(v, zz), FAdd(yz, xw)); + column2 = V3Merge(FAdd(xz, yw), FSub(yz, xw), FSub(v, yy)); +} + + +// not used + +/* +PX_FORCE_INLINE Vec4V V4LoadAligned(Vec4V* addr) +{ + return *addr; +} +*/ + +/* +PX_FORCE_INLINE Vec4V V4LoadUnaligned(Vec4V* addr) +{ + return *addr; +} +*/ + +/* +PX_FORCE_INLINE Vec4V V4Ceil(const Vec4V a) +{ + return Vec4V(PxCeil(a.x), PxCeil(a.y), PxCeil(a.z), PxCeil(a.w)); +} + +PX_FORCE_INLINE Vec4V V4Floor(const Vec4V a) +{ + return Vec4V(PxFloor(a.x), PxFloor(a.y), PxFloor(a.z), PxFloor(a.w)); +} +*/ + +/* +PX_FORCE_INLINE VecU32V V4ConvertToU32VSaturate(const Vec4V a, PxU32 power) +{ + PX_ASSERT(power == 0 && "Non-zero power not supported in convertToU32VSaturate"); + PX_UNUSED(power); // prevent warning in release builds + PxF32 ffffFFFFasFloat = PxF32(0xFFFF0000); + return VecU32V( + PxU32(PxClamp((a).x, 0.0f, ffffFFFFasFloat)), + PxU32(PxClamp((a).y, 0.0f, ffffFFFFasFloat)), + PxU32(PxClamp((a).z, 0.0f, ffffFFFFasFloat)), + PxU32(PxClamp((a).w, 0.0f, ffffFFFFasFloat))); +} +*/ + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSVECMATHAOSSCALARINLINE_H diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathSSE.h b/src/PhysX/physx/source/foundation/include/PsVecMathSSE.h new file mode 100644 index 000000000..cc292caf8 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecMathSSE.h @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECMATHSSE_H +#define PSFOUNDATION_PSVECMATHSSE_H + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +namespace +{ + const PX_ALIGN(16, PxF32) minus1w[4] = { 0.0f, 0.0f, 0.0f, -1.0f }; +} + +PX_FORCE_INLINE void QuatGetMat33V(const QuatVArg q, Vec3V& column0, Vec3V& column1, Vec3V& column2) +{ + const __m128 q2 = V4Add(q, q); + const __m128 qw2 = V4MulAdd(q2, V4GetW(q), _mm_load_ps(minus1w)); // (2wx, 2wy, 2wz, 2ww-1) + const __m128 nw2 = Vec3V_From_Vec4V(V4Neg(qw2)); // (-2wx, -2wy, -2wz, 0) + const __m128 v = Vec3V_From_Vec4V(q); + + const __m128 a0 = _mm_shuffle_ps(qw2, nw2, _MM_SHUFFLE(3, 1, 2, 3)); // (2ww-1, 2wz, -2wy, 0) + column0 = V4MulAdd(v, V4GetX(q2), a0); + + const __m128 a1 = _mm_shuffle_ps(qw2, nw2, _MM_SHUFFLE(3, 2, 0, 3)); // (2ww-1, 2wx, -2wz, 0) + column1 = V4MulAdd(v, V4GetY(q2), _mm_shuffle_ps(a1, a1, _MM_SHUFFLE(3, 1, 0, 2))); + + const __m128 a2 = _mm_shuffle_ps(qw2, nw2, _MM_SHUFFLE(3, 0, 1, 3)); // (2ww-1, 2wy, -2wx, 0) + column2 = V4MulAdd(v, V4GetZ(q2), _mm_shuffle_ps(a2, a2, _MM_SHUFFLE(3, 0, 2, 1))); +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + + +#endif // PSFOUNDATION_PSVECMATHSSE_H + diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h b/src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h new file mode 100644 index 000000000..a00a944d6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECMATHUTILITIES_H +#define PSFOUNDATION_PSVECMATHUTILITIES_H + +#include "PsVecMath.h" + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ +/*! + Extend an edge along its length by a factor + */ +PX_FORCE_INLINE void makeFatEdge(Vec3V& p0, Vec3V& p1, const FloatVArg fatCoeff) +{ + const Vec3V delta = V3Sub(p1, p0); + const FloatV m = V3Length(delta); + const BoolV con = FIsGrtr(m, FZero()); + const Vec3V fatDelta = V3Scale(V3ScaleInv(delta, m), fatCoeff); + p0 = V3Sel(con, V3Sub(p0, fatDelta), p0); + p1 = V3Sel(con, V3Add(p1, fatDelta), p1); +} +} +} +} + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsVecQuat.h b/src/PhysX/physx/source/foundation/include/PsVecQuat.h new file mode 100644 index 000000000..8eb3ea487 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecQuat.h @@ -0,0 +1,466 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECQUAT_H +#define PSFOUNDATION_PSVECQUAT_H + +//#include "PsInlineAoS.h" + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +#ifndef PX_PIDIV2 +#define PX_PIDIV2 1.570796327f +#endif + +////////////////////////////////// +// QuatV +////////////////////////////////// +PX_FORCE_INLINE QuatV QuatVLoadXYZW(const PxF32 x, const PxF32 y, const PxF32 z, const PxF32 w) +{ + return V4LoadXYZW(x, y, z, w); +} + +PX_FORCE_INLINE QuatV QuatVLoadU(const PxF32* v) +{ + return V4LoadU(v); +} + +PX_FORCE_INLINE QuatV QuatVLoadA(const PxF32* v) +{ + return V4LoadA(v); +} + +PX_FORCE_INLINE QuatV QuatV_From_RotationAxisAngle(const Vec3V u, const FloatV a) +{ + // q = cos(a/2) + u*sin(a/2) + const FloatV half = FLoad(0.5f); + const FloatV hangle = FMul(a, half); + const FloatV piByTwo(FLoad(PX_PIDIV2)); + const FloatV PiByTwoMinHangle(FSub(piByTwo, hangle)); + const Vec4V hangle2(Vec4V_From_Vec3V(V3Merge(hangle, PiByTwoMinHangle, hangle))); + + /*const FloatV sina = FSin(hangle); + const FloatV cosa = FCos(hangle);*/ + + const Vec4V _sina = V4Sin(hangle2); + const FloatV sina = V4GetX(_sina); + const FloatV cosa = V4GetY(_sina); + + const Vec3V v = V3Scale(u, sina); + // return V4Sel(BTTTF(), Vec4V_From_Vec3V(v), V4Splat(cosa)); + return V4SetW(Vec4V_From_Vec3V(v), cosa); +} + +// Normalize +PX_FORCE_INLINE QuatV QuatNormalize(const QuatV q) +{ + return V4Normalize(q); +} + +PX_FORCE_INLINE FloatV QuatLength(const QuatV q) +{ + return V4Length(q); +} + +PX_FORCE_INLINE FloatV QuatLengthSq(const QuatV q) +{ + return V4LengthSq(q); +} + +PX_FORCE_INLINE FloatV QuatDot(const QuatV a, const QuatV b) // convert this PxQuat to a unit quaternion +{ + return V4Dot(a, b); +} + +PX_FORCE_INLINE QuatV QuatConjugate(const QuatV q) +{ + return V4SetW(V4Neg(q), V4GetW(q)); +} + +PX_FORCE_INLINE Vec3V QuatGetImaginaryPart(const QuatV q) +{ + return Vec3V_From_Vec4V(q); +} + +/** brief computes rotation of x-axis */ +PX_FORCE_INLINE Vec3V QuatGetBasisVector0(const QuatV q) +{ + /*const PxF32 x2 = x*2.0f; + const PxF32 w2 = w*2.0f; + return PxVec3( (w * w2) - 1.0f + x*x2, + (z * w2) + y*x2, + (-y * w2) + z*x2);*/ + + const FloatV two = FLoad(2.f); + const FloatV w = V4GetW(q); + const Vec3V u = Vec3V_From_Vec4V(q); + + const FloatV x2 = FMul(V3GetX(u), two); + const FloatV w2 = FMul(w, two); + + const Vec3V a = V3Scale(u, x2); + const Vec3V tmp = V3Merge(w, V3GetZ(u), FNeg(V3GetY(u))); + // const Vec3V b = V3Scale(tmp, w2); + // const Vec3V ab = V3Add(a, b); + const Vec3V ab = V3ScaleAdd(tmp, w2, a); + return V3SetX(ab, FSub(V3GetX(ab), FOne())); +} + +/** brief computes rotation of y-axis */ +PX_FORCE_INLINE Vec3V QuatGetBasisVector1(const QuatV q) +{ + /*const PxF32 y2 = y*2.0f; + const PxF32 w2 = w*2.0f; + return PxVec3( (-z * w2) + x*y2, + (w * w2) - 1.0f + y*y2, + (x * w2) + z*y2);*/ + + const FloatV two = FLoad(2.f); + const FloatV w = V4GetW(q); + const Vec3V u = Vec3V_From_Vec4V(q); + + const FloatV y2 = FMul(V3GetY(u), two); + const FloatV w2 = FMul(w, two); + + const Vec3V a = V3Scale(u, y2); + const Vec3V tmp = V3Merge(FNeg(V3GetZ(u)), w, V3GetX(u)); + // const Vec3V b = V3Scale(tmp, w2); + // const Vec3V ab = V3Add(a, b); + const Vec3V ab = V3ScaleAdd(tmp, w2, a); + return V3SetY(ab, FSub(V3GetY(ab), FOne())); +} + +/** brief computes rotation of z-axis */ +PX_FORCE_INLINE Vec3V QuatGetBasisVector2(const QuatV q) +{ + /*const PxF32 z2 = z*2.0f; + const PxF32 w2 = w*2.0f; + return PxVec3( (y * w2) + x*z2, + (-x * w2) + y*z2, + (w * w2) - 1.0f + z*z2);*/ + + const FloatV two = FLoad(2.f); + const FloatV w = V4GetW(q); + const Vec3V u = Vec3V_From_Vec4V(q); + + const FloatV z2 = FMul(V3GetZ(u), two); + const FloatV w2 = FMul(w, two); + + const Vec3V a = V3Scale(u, z2); + const Vec3V tmp = V3Merge(V3GetY(u), FNeg(V3GetX(u)), w); + /*const Vec3V b = V3Scale(tmp, w2); + const Vec3V ab = V3Add(a, b);*/ + const Vec3V ab = V3ScaleAdd(tmp, w2, a); + return V3SetZ(ab, FSub(V3GetZ(ab), FOne())); +} + +PX_FORCE_INLINE Vec3V QuatRotate(const QuatV q, const Vec3V v) +{ + /* + const PxVec3 qv(x,y,z); + return (v*(w*w-0.5f) + (qv.cross(v))*w + qv*(qv.dot(v)))*2; + */ + + const FloatV two = FLoad(2.f); + // const FloatV half = FloatV_From_F32(0.5f); + const FloatV nhalf = FLoad(-0.5f); + const Vec3V u = Vec3V_From_Vec4V(q); + const FloatV w = V4GetW(q); + // const FloatV w2 = FSub(FMul(w, w), half); + const FloatV w2 = FScaleAdd(w, w, nhalf); + const Vec3V a = V3Scale(v, w2); + // const Vec3V b = V3Scale(V3Cross(u, v), w); + // const Vec3V c = V3Scale(u, V3Dot(u, v)); + // return V3Scale(V3Add(V3Add(a, b), c), two); + const Vec3V temp = V3ScaleAdd(V3Cross(u, v), w, a); + return V3Scale(V3ScaleAdd(u, V3Dot(u, v), temp), two); +} + +PX_FORCE_INLINE Vec3V QuatTransform(const QuatV q, const Vec3V p, const Vec3V v) +{ + // p + q.rotate(v) + const FloatV two = FLoad(2.f); + // const FloatV half = FloatV_From_F32(0.5f); + const FloatV nhalf = FLoad(-0.5f); + const Vec3V u = Vec3V_From_Vec4V(q); + const FloatV w = V4GetW(q); + // const FloatV w2 = FSub(FMul(w, w), half); + const FloatV w2 = FScaleAdd(w, w, nhalf); + const Vec3V a = V3Scale(v, w2); + /*const Vec3V b = V3Scale(V3Cross(u, v), w); + const Vec3V c = V3Scale(u, V3Dot(u, v)); + return V3ScaleAdd(V3Add(V3Add(a, b), c), two, p);*/ + const Vec3V temp = V3ScaleAdd(V3Cross(u, v), w, a); + const Vec3V z = V3ScaleAdd(u, V3Dot(u, v), temp); + return V3ScaleAdd(z, two, p); +} + +PX_FORCE_INLINE Vec3V QuatRotateInv(const QuatV q, const Vec3V v) +{ + + // const PxVec3 qv(x,y,z); + // return (v*(w*w-0.5f) - (qv.cross(v))*w + qv*(qv.dot(v)))*2; + + const FloatV two = FLoad(2.f); + const FloatV nhalf = FLoad(-0.5f); + const Vec3V u = Vec3V_From_Vec4V(q); + const FloatV w = V4GetW(q); + const FloatV w2 = FScaleAdd(w, w, nhalf); + const Vec3V a = V3Scale(v, w2); + /*const Vec3V b = V3Scale(V3Cross(u, v), w); + const Vec3V c = V3Scale(u, V3Dot(u, v)); + return V3Scale(V3Add(V3Sub(a, b), c), two);*/ + const Vec3V temp = V3NegScaleSub(V3Cross(u, v), w, a); + return V3Scale(V3ScaleAdd(u, V3Dot(u, v), temp), two); +} + +PX_FORCE_INLINE QuatV QuatMul(const QuatV a, const QuatV b) +{ + const Vec3V imagA = Vec3V_From_Vec4V(a); + const Vec3V imagB = Vec3V_From_Vec4V(b); + const FloatV rA = V4GetW(a); + const FloatV rB = V4GetW(b); + + const FloatV real = FSub(FMul(rA, rB), V3Dot(imagA, imagB)); + const Vec3V v0 = V3Scale(imagA, rB); + const Vec3V v1 = V3Scale(imagB, rA); + const Vec3V v2 = V3Cross(imagA, imagB); + const Vec3V imag = V3Add(V3Add(v0, v1), v2); + + return V4SetW(Vec4V_From_Vec3V(imag), real); +} + +PX_FORCE_INLINE QuatV QuatAdd(const QuatV a, const QuatV b) +{ + return V4Add(a, b); +} + +PX_FORCE_INLINE QuatV QuatNeg(const QuatV q) +{ + return V4Neg(q); +} + +PX_FORCE_INLINE QuatV QuatSub(const QuatV a, const QuatV b) +{ + return V4Sub(a, b); +} + +PX_FORCE_INLINE QuatV QuatScale(const QuatV a, const FloatV b) +{ + return V4Scale(a, b); +} + +PX_FORCE_INLINE QuatV QuatMerge(const FloatV* const floatVArray) +{ + return V4Merge(floatVArray); +} + +PX_FORCE_INLINE QuatV QuatMerge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w) +{ + return V4Merge(x, y, z, w); +} + +PX_FORCE_INLINE QuatV QuatIdentity() +{ + return V4SetW(V4Zero(), FOne()); +} + +PX_FORCE_INLINE bool isFiniteQuatV(const QuatV q) +{ + return isFiniteVec4V(q); +} + +PX_FORCE_INLINE bool isValidQuatV(const QuatV q) +{ + const FloatV unitTolerance = FLoad(1e-4f); + const FloatV tmp = FAbs(FSub(QuatLength(q), FOne())); + const BoolV con = FIsGrtr(unitTolerance, tmp); + return isFiniteVec4V(q) & (BAllEqTTTT(con) == 1); +} + +PX_FORCE_INLINE bool isSaneQuatV(const QuatV q) +{ + const FloatV unitTolerance = FLoad(1e-2f); + const FloatV tmp = FAbs(FSub(QuatLength(q), FOne())); + const BoolV con = FIsGrtr(unitTolerance, tmp); + return isFiniteVec4V(q) & (BAllEqTTTT(con) == 1); +} + +PX_FORCE_INLINE Mat33V QuatGetMat33V(const QuatVArg q) +{ + // const FloatV two = FloatV_From_F32(2.f); + // const FloatV one = FOne(); + + // const FloatV x = V4GetX(q); + // const FloatV y = V4GetY(q); + // const FloatV z = V4GetZ(q); + // const Vec4V _q = V4Mul(q, two); + // + ////const FloatV w = V4GetW(q); + + // const Vec4V t0 = V4Mul(_q, x); // 2xx, 2xy, 2xz, 2xw + // const Vec4V t1 = V4Mul(_q, y); // 2xy, 2yy, 2yz, 2yw + // const Vec4V t2 = V4Mul(_q, z); // 2xz, 2yz, 2zz, 2zw + ////const Vec4V t3 = V4Mul(_q, w); // 2xw, 2yw, 2zw, 2ww + + // const FloatV xx2 = V4GetX(t0); + // const FloatV xy2 = V4GetY(t0); + // const FloatV xz2 = V4GetZ(t0); + // const FloatV xw2 = V4GetW(t0); + + // const FloatV yy2 = V4GetY(t1); + // const FloatV yz2 = V4GetZ(t1); + // const FloatV yw2 = V4GetW(t1); + + // const FloatV zz2 = V4GetZ(t2); + // const FloatV zw2 = V4GetW(t2); + + ////const FloatV ww2 = V4GetW(t3); + + // const FloatV c00 = FSub(one, FAdd(yy2, zz2)); + // const FloatV c01 = FSub(xy2, zw2); + // const FloatV c02 = FAdd(xz2, yw2); + + // const FloatV c10 = FAdd(xy2, zw2); + // const FloatV c11 = FSub(one, FAdd(xx2, zz2)); + // const FloatV c12 = FSub(yz2, xw2); + + // const FloatV c20 = FSub(xz2, yw2); + // const FloatV c21 = FAdd(yz2, xw2); + // const FloatV c22 = FSub(one, FAdd(xx2, yy2)); + + // const Vec3V c0 = V3Merge(c00, c10, c20); + // const Vec3V c1 = V3Merge(c01, c11, c21); + // const Vec3V c2 = V3Merge(c02, c12, c22); + + // return Mat33V(c0, c1, c2); + + const FloatV one = FOne(); + const FloatV x = V4GetX(q); + const FloatV y = V4GetY(q); + const FloatV z = V4GetZ(q); + const FloatV w = V4GetW(q); + + const FloatV x2 = FAdd(x, x); + const FloatV y2 = FAdd(y, y); + const FloatV z2 = FAdd(z, z); + + const FloatV xx = FMul(x2, x); + const FloatV yy = FMul(y2, y); + const FloatV zz = FMul(z2, z); + + const FloatV xy = FMul(x2, y); + const FloatV xz = FMul(x2, z); + const FloatV xw = FMul(x2, w); + + const FloatV yz = FMul(y2, z); + const FloatV yw = FMul(y2, w); + const FloatV zw = FMul(z2, w); + + const FloatV v = FSub(one, xx); + + const Vec3V column0 = V3Merge(FSub(FSub(one, yy), zz), FAdd(xy, zw), FSub(xz, yw)); + const Vec3V column1 = V3Merge(FSub(xy, zw), FSub(v, zz), FAdd(yz, xw)); + const Vec3V column2 = V3Merge(FAdd(xz, yw), FSub(yz, xw), FSub(v, yy)); + return Mat33V(column0, column1, column2); +} + +PX_FORCE_INLINE QuatV Mat33GetQuatV(const Mat33V& a) +{ + const FloatV one = FOne(); + const FloatV zero = FZero(); + const FloatV half = FLoad(0.5f); + const FloatV two = FLoad(2.f); + const FloatV scale = FLoad(0.25f); + const FloatV a00 = V3GetX(a.col0); + const FloatV a11 = V3GetY(a.col1); + const FloatV a22 = V3GetZ(a.col2); + + const FloatV a21 = V3GetZ(a.col1); // row=2, col=1; + const FloatV a12 = V3GetY(a.col2); // row=1, col=2; + const FloatV a02 = V3GetX(a.col2); // row=0, col=2; + const FloatV a20 = V3GetZ(a.col0); // row=2, col=0; + const FloatV a10 = V3GetY(a.col0); // row=1, col=0; + const FloatV a01 = V3GetX(a.col1); // row=0, col=1; + + const Vec3V vec0 = V3Merge(a21, a02, a10); + const Vec3V vec1 = V3Merge(a12, a20, a01); + const Vec3V v = V3Sub(vec0, vec1); + const Vec3V g = V3Add(vec0, vec1); + + const FloatV trace = FAdd(a00, FAdd(a11, a22)); + + if(FAllGrtrOrEq(trace, zero)) + { + const FloatV h = FSqrt(FAdd(trace, one)); + const FloatV w = FMul(half, h); + const FloatV s = FMul(half, FRecip(h)); + const Vec3V u = V3Scale(v, s); + return V4SetW(Vec4V_From_Vec3V(u), w); + } + else + { + const FloatV ntrace = FNeg(trace); + const Vec3V d = V3Merge(a00, a11, a22); + const BoolV con0 = BAllTrue3(V3IsGrtrOrEq(V3Splat(a00), d)); + const BoolV con1 = BAllTrue3(V3IsGrtrOrEq(V3Splat(a11), d)); + + const FloatV t0 = FAdd(one, FScaleAdd(a00, two, ntrace)); + const FloatV t1 = FAdd(one, FScaleAdd(a11, two, ntrace)); + const FloatV t2 = FAdd(one, FScaleAdd(a22, two, ntrace)); + + const FloatV t = FSel(con0, t0, FSel(con1, t1, t2)); + + const FloatV h = FMul(two, FSqrt(t)); + const FloatV s = FRecip(h); + const FloatV g0 = FMul(scale, h); + const Vec3V vs = V3Scale(v, s); + const Vec3V gs = V3Scale(g, s); + const FloatV gsx = V3GetX(gs); + const FloatV gsy = V3GetY(gs); + const FloatV gsz = V3GetZ(gs); + // vs.x= (a21 - a12)*s; vs.y=(a02 - a20)*s; vs.z=(a10 - a01)*s; + // gs.x= (a21 + a12)*s; gs.y=(a02 + a20)*s; gs.z=(a10 + a01)*s; + const Vec4V v0 = V4Merge(g0, gsz, gsy, V3GetX(vs)); + const Vec4V v1 = V4Merge(gsz, g0, gsx, V3GetY(vs)); + const Vec4V v2 = V4Merge(gsy, gsx, g0, V3GetZ(vs)); + return V4Sel(con0, v0, V4Sel(con1, v1, v2)); + } +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/foundation/include/PsVecTransform.h b/src/PhysX/physx/source/foundation/include/PsVecTransform.h new file mode 100644 index 000000000..d9da8d0e4 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/PsVecTransform.h @@ -0,0 +1,283 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSVECTRANSFORM_H +#define PSFOUNDATION_PSVECTRANSFORM_H + +#include "PsVecMath.h" +#include "foundation/PxTransform.h" + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +class PsTransformV +{ + public: + QuatV q; + Vec3V p; + + PX_FORCE_INLINE PsTransformV(const PxTransform& orientation) + { + // const PxQuat oq = orientation.q; + // const PxF32 f[4] = {oq.x, oq.y, oq.z, oq.w}; + q = QuatVLoadXYZW(orientation.q.x, orientation.q.y, orientation.q.z, orientation.q.w); + // q = QuatV_From_F32Array(&oq.x); + p = V3LoadU(orientation.p); + } + + PX_FORCE_INLINE PsTransformV(const Vec3VArg p0 = V3Zero(), const QuatVArg q0 = QuatIdentity()) : q(q0), p(p0) + { + PX_ASSERT(isSaneQuatV(q0)); + } + + PX_FORCE_INLINE PsTransformV operator*(const PsTransformV& x) const + { + PX_ASSERT(x.isSane()); + return transform(x); + } + + PX_FORCE_INLINE PsTransformV getInverse() const + { + PX_ASSERT(isFinite()); + // return PxTransform(q.rotateInv(-p),q.getConjugate()); + return PsTransformV(QuatRotateInv(q, V3Neg(p)), QuatConjugate(q)); + } + + PX_FORCE_INLINE void normalize() + { + p = V3Zero(); + q = QuatIdentity(); + } + + PX_FORCE_INLINE void Invalidate() + { + p = V3Splat(FMax()); + q = QuatIdentity(); + } + + PX_FORCE_INLINE Vec3V transform(const Vec3VArg input) const + { + PX_ASSERT(isFinite()); + // return q.rotate(input) + p; + return QuatTransform(q, p, input); + } + + PX_FORCE_INLINE Vec3V transformInv(const Vec3VArg input) const + { + PX_ASSERT(isFinite()); + // return q.rotateInv(input-p); + return QuatRotateInv(q, V3Sub(input, p)); + } + + PX_FORCE_INLINE Vec3V rotate(const Vec3VArg input) const + { + PX_ASSERT(isFinite()); + // return q.rotate(input); + return QuatRotate(q, input); + } + + PX_FORCE_INLINE Vec3V rotateInv(const Vec3VArg input) const + { + PX_ASSERT(isFinite()); + // return q.rotateInv(input); + return QuatRotateInv(q, input); + } + + //! Transform transform to parent (returns compound transform: first src, then *this) + PX_FORCE_INLINE PsTransformV transform(const PsTransformV& src) const + { + PX_ASSERT(src.isSane()); + PX_ASSERT(isSane()); + // src = [srct, srcr] -> [r*srct + t, r*srcr] + // return PxTransform(q.rotate(src.p) + p, q*src.q); + return PsTransformV(V3Add(QuatRotate(q, src.p), p), QuatMul(q, src.q)); + } + + /** + \brief returns true if finite and q is a unit quaternion + */ + + PX_FORCE_INLINE bool isValid() const + { + // return p.isFinite() && q.isFinite() && q.isValid(); + return isFiniteVec3V(p) & isFiniteQuatV(q) & isValidQuatV(q); + } + + /** + \brief returns true if finite and quat magnitude is reasonably close to unit to allow for some accumulation of error + vs isValid + */ + + PX_FORCE_INLINE bool isSane() const + { + // return isFinite() && q.isSane(); + return isFinite() & isSaneQuatV(q); + } + + /** + \brief returns true if all elems are finite (not NAN or INF, etc.) + */ + PX_FORCE_INLINE bool isFinite() const + { + // return p.isFinite() && q.isFinite(); + return isFiniteVec3V(p) & isFiniteQuatV(q); + } + + //! Transform transform from parent (returns compound transform: first src, then this->inverse) + PX_FORCE_INLINE PsTransformV transformInv(const PsTransformV& src) const + { + PX_ASSERT(src.isSane()); + PX_ASSERT(isFinite()); + // src = [srct, srcr] -> [r^-1*(srct-t), r^-1*srcr] + /*PxQuat qinv = q.getConjugate(); + return PxTransform(qinv.rotate(src.p - p), qinv*src.q);*/ + const QuatV qinv = QuatConjugate(q); + const Vec3V v = QuatRotate(qinv, V3Sub(src.p, p)); + const QuatV rot = QuatMul(qinv, src.q); + return PsTransformV(v, rot); + } + + static PX_FORCE_INLINE PsTransformV createIdentity() + { + return PsTransformV(V3Zero()); + } +}; + +PX_FORCE_INLINE PsTransformV loadTransformA(const PxTransform& transform) +{ + const QuatV q0 = QuatVLoadA(&transform.q.x); + const Vec3V p0 = V3LoadA(&transform.p.x); + + return PsTransformV(p0, q0); +} + +PX_FORCE_INLINE PsTransformV loadTransformU(const PxTransform& transform) +{ + const QuatV q0 = QuatVLoadU(&transform.q.x); + const Vec3V p0 = V3LoadU(&transform.p.x); + + return PsTransformV(p0, q0); +} + +class PsMatTransformV +{ + public: + Mat33V rot; + Vec3V p; + + PX_FORCE_INLINE PsMatTransformV() + { + p = V3Zero(); + rot = M33Identity(); + } + PX_FORCE_INLINE PsMatTransformV(const Vec3VArg _p, const Mat33V& _rot) + { + p = _p; + rot = _rot; + } + + PX_FORCE_INLINE PsMatTransformV(const PsTransformV& other) + { + p = other.p; + QuatGetMat33V(other.q, rot.col0, rot.col1, rot.col2); + } + + PX_FORCE_INLINE PsMatTransformV(const Vec3VArg _p, const QuatV& quat) + { + p = _p; + QuatGetMat33V(quat, rot.col0, rot.col1, rot.col2); + } + + PX_FORCE_INLINE Vec3V getCol0() const + { + return rot.col0; + } + + PX_FORCE_INLINE Vec3V getCol1() const + { + return rot.col1; + } + + PX_FORCE_INLINE Vec3V getCol2() const + { + return rot.col2; + } + + PX_FORCE_INLINE void setCol0(const Vec3VArg col0) + { + rot.col0 = col0; + } + + PX_FORCE_INLINE void setCol1(const Vec3VArg col1) + { + rot.col1 = col1; + } + + PX_FORCE_INLINE void setCol2(const Vec3VArg col2) + { + rot.col2 = col2; + } + + PX_FORCE_INLINE Vec3V transform(const Vec3VArg input) const + { + return V3Add(p, M33MulV3(rot, input)); + } + + PX_FORCE_INLINE Vec3V transformInv(const Vec3VArg input) const + { + return M33TrnspsMulV3(rot, V3Sub(input, p)); // QuatRotateInv(q, V3Sub(input, p)); + } + + PX_FORCE_INLINE Vec3V rotate(const Vec3VArg input) const + { + return M33MulV3(rot, input); + } + + PX_FORCE_INLINE Vec3V rotateInv(const Vec3VArg input) const + { + return M33TrnspsMulV3(rot, input); + } + + PX_FORCE_INLINE PsMatTransformV transformInv(const PsMatTransformV& src) const + { + + const Vec3V v = M33TrnspsMulV3(rot, V3Sub(src.p, p)); + const Mat33V mat = M33MulM33(M33Trnsps(rot), src.rot); + return PsMatTransformV(v, mat); + } +}; +} +} +} + +#endif diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h new file mode 100644 index 000000000..02fa34a25 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXAOS_H +#define PSFOUNDATION_PSUNIXAOS_H + +// no includes here! this file should be included from PxcVecMath.h only!!! + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +#if PX_INTEL_FAMILY +#include "sse2/PsUnixSse2AoS.h" +#elif PX_NEON +#include "neon/PsUnixNeonAoS.h" +#else +#error No SIMD implementation for this unix platform. +#endif + +#endif // PSFOUNDATION_PSUNIXAOS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h new file mode 100644 index 000000000..3a2138fea --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXFPU_H +#define PSFOUNDATION_PSUNIXFPU_H + +#include "foundation/PxPreprocessor.h" + +#if PX_LINUX || PX_PS4 || PX_OSX + +#if PX_X86 || PX_X64 +#if PX_EMSCRIPTEN +#include +#endif +#include +#elif PX_NEON +#include +#endif + + +PX_INLINE physx::shdfnd::SIMDGuard::SIMDGuard() +{ +#if !PX_EMSCRIPTEN && (PX_X86 || PX_X64) + mControlWord = _mm_getcsr(); + // set default (disable exceptions: _MM_MASK_MASK) and FTZ (_MM_FLUSH_ZERO_ON), DAZ (_MM_DENORMALS_ZERO_ON: (1<<6)) + _mm_setcsr(_MM_MASK_MASK | _MM_FLUSH_ZERO_ON | (1 << 6)); +#endif +} + +PX_INLINE physx::shdfnd::SIMDGuard::~SIMDGuard() +{ +#if !PX_EMSCRIPTEN && (PX_X86 || PX_X64) + // restore control word and clear exception flags + // (setting exception state flags cause exceptions on the first following fp operation) + _mm_setcsr(mControlWord & ~_MM_EXCEPT_MASK); +#endif +} + +#else +#error No SIMD implementation for this unix platform. +#endif // PX_LINUX || PX_PS4 || PX_OSX + +#endif // #ifndef PSFOUNDATION_PSUNIXFPU_H diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h new file mode 100644 index 000000000..9c14ea9f8 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h @@ -0,0 +1,45 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXINLINEAOS_H +#define PSFOUNDATION_PSUNIXINLINEAOS_H + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +#if PX_INTEL_FAMILY +#include "sse2/PsUnixSse2InlineAoS.h" +#elif PX_NEON +#include "neon/PsUnixNeonInlineAoS.h" +#else +#error No SIMD implementation for this unix platform. +#endif + +#endif // PSFOUNDATION_PSUNIXINLINEAOS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h new file mode 100644 index 000000000..b69b57c54 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h @@ -0,0 +1,153 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXINTRINSICS_H +#define PSFOUNDATION_PSUNIXINTRINSICS_H + +#include "Ps.h" +#include "foundation/PxAssert.h" +#include + +#if PX_ANDROID +#include // for Ns::debugBreak() { raise(SIGTRAP); } +#endif + +#if 0 +#include +#endif + +// this file is for internal intrinsics - that is, intrinsics that are used in +// cross platform code but do not appear in the API + +#if !(PX_LINUX || PX_ANDROID || PX_PS4 || PX_APPLE_FAMILY) +#error "This file should only be included by unix builds!!" +#endif + +namespace physx +{ +namespace shdfnd +{ + +PX_FORCE_INLINE void memoryBarrier() +{ + __sync_synchronize(); +} + +/*! +Return the index of the highest set bit. Undefined for zero arg. +*/ +PX_INLINE uint32_t highestSetBitUnsafe(uint32_t v) +{ + + return 31 - __builtin_clz(v); +} + +/*! +Return the index of the highest set bit. Undefined for zero arg. +*/ +PX_INLINE int32_t lowestSetBitUnsafe(uint32_t v) +{ + return __builtin_ctz(v); +} + +/*! +Returns the index of the highest set bit. Returns 32 for v=0. +*/ +PX_INLINE uint32_t countLeadingZeros(uint32_t v) +{ + if(v) + return __builtin_clz(v); + else + return 32; +} + +/*! +Prefetch aligned 64B x86, 32b ARM around \c ptr+offset. +*/ +PX_FORCE_INLINE void prefetchLine(const void* ptr, uint32_t offset = 0) +{ + __builtin_prefetch(reinterpret_cast(ptr) + offset, 0, 3); +} + +/*! +Prefetch \c count bytes starting at \c ptr. +*/ +#if PX_ANDROID || PX_IOS +PX_FORCE_INLINE void prefetch(const void* ptr, uint32_t count = 1) +{ + const char* cp = static_cast(ptr); + size_t p = reinterpret_cast(ptr); + uint32_t startLine = uint32_t(p >> 5), endLine = uint32_t((p + count - 1) >> 5); + uint32_t lines = endLine - startLine + 1; + do + { + prefetchLine(cp); + cp += 32; + } while(--lines); +} +#else +PX_FORCE_INLINE void prefetch(const void* ptr, uint32_t count = 1) +{ + const char* cp = reinterpret_cast(ptr); + uint64_t p = size_t(ptr); + uint64_t startLine = p >> 6, endLine = (p + count - 1) >> 6; + uint64_t lines = endLine - startLine + 1; + do + { + prefetchLine(cp); + cp += 64; + } while(--lines); +} +#endif + +//! \brief platform-specific reciprocal +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipFast(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific fast reciprocal square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrtFast(float a) +{ + return 1.0f / ::sqrtf(a); +} + +//! \brief platform-specific floor +PX_CUDA_CALLABLE PX_FORCE_INLINE float floatFloor(float x) +{ + return ::floorf(x); +} + +#define NS_EXPECT_TRUE(x) x +#define NS_EXPECT_FALSE(x) x + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSUNIXINTRINSICS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h new file mode 100644 index 000000000..8753a24b8 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXTRIGCONSTANTS_H +#define PSFOUNDATION_PSUNIXTRIGCONSTANTS_H + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +//#define PX_GLOBALCONST extern const __declspec(selectany) +#define PX_GLOBALCONST extern const __attribute__((weak)) + +PX_ALIGN_PREFIX(16) +struct PX_VECTORF32 +{ + float f[4]; +} PX_ALIGN_SUFFIX(16); + +PX_GLOBALCONST PX_VECTORF32 g_PXSinCoefficients0 = { { 1.0f, -0.166666667f, 8.333333333e-3f, -1.984126984e-4f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinCoefficients1 = { { 2.755731922e-6f, -2.505210839e-8f, 1.605904384e-10f, -7.647163732e-13f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinCoefficients2 = { { 2.811457254e-15f, -8.220635247e-18f, 1.957294106e-20f, -3.868170171e-23f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXCosCoefficients0 = { { 1.0f, -0.5f, 4.166666667e-2f, -1.388888889e-3f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosCoefficients1 = { { 2.480158730e-5f, -2.755731922e-7f, 2.087675699e-9f, -1.147074560e-11f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosCoefficients2 = { { 4.779477332e-14f, -1.561920697e-16f, 4.110317623e-19f, -8.896791392e-22f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTanCoefficients0 = { { 1.0f, 0.333333333f, 0.133333333f, 5.396825397e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXTanCoefficients1 = { { 2.186948854e-2f, 8.863235530e-3f, 3.592128167e-3f, 1.455834485e-3f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXTanCoefficients2 = { { 5.900274264e-4f, 2.391290764e-4f, 9.691537707e-5f, 3.927832950e-5f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients0 = { { -0.05806367563904f, -0.41861972469416f, 0.22480114791621f, 2.17337241360606f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients1 = { { 0.61657275907170f, 4.29696498283455f, -1.18942822255452f, -6.53784832094831f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients2 = { { -1.36926553863413f, -4.48179294237210f, 1.41810672941833f, 5.48179257935713f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXATanCoefficients0 = { { 1.0f, 0.333333334f, 0.2f, 0.142857143f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanCoefficients1 = { { 1.111111111e-1f, 9.090909091e-2f, 7.692307692e-2f, 6.666666667e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanCoefficients2 = { { 5.882352941e-2f, 5.263157895e-2f, 4.761904762e-2f, 4.347826087e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinEstCoefficients = { { 1.0f, -1.66521856991541e-1f, 8.199913018755e-3f, -1.61475937228e-4f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosEstCoefficients = { { 1.0f, -4.95348008918096e-1f, 3.878259962881e-2f, -9.24587976263e-4f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTanEstCoefficients = { { 2.484f, -1.954923183e-1f, 2.467401101f, PxInvPi } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanEstCoefficients = { { 7.689891418951e-1f, 1.104742493348f, 8.661844266006e-1f, PxPiDivTwo } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinEstCoefficients = { { -1.36178272886711f, 2.37949493464538f, -8.08228565650486e-1f, 2.78440142746736e-1f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXASinEstConstants = { { 1.00000011921f, PxPiDivTwo, 0.0f, 0.0f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXPiConstants0 = { { PxPi, PxTwoPi, PxInvPi, PxInvTwoPi } }; +PX_GLOBALCONST PX_VECTORF32 g_PXReciprocalTwoPi = { { PxInvTwoPi, PxInvTwoPi, PxInvTwoPi, PxInvTwoPi } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTwoPi = { { PxTwoPi, PxTwoPi, PxTwoPi, PxTwoPi } }; + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h new file mode 100644 index 000000000..fc90c5235 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h @@ -0,0 +1,140 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXNEONAOS_H +#define PSFOUNDATION_PSUNIXNEONAOS_H + +// no includes here! this file should be included from PxcVecMath.h only!!! + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +// only ARM NEON compatible platforms should reach this +#include + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +typedef float32x2_t FloatV; +typedef float32x4_t Vec3V; +typedef float32x4_t Vec4V; +typedef uint32x4_t BoolV; +typedef float32x4_t QuatV; + +typedef uint32x4_t VecU32V; +typedef int32x4_t VecI32V; +typedef uint16x8_t VecU16V; +typedef int16x8_t VecI16V; +typedef uint8x16_t VecU8V; + +#define FloatVArg FloatV & +#define Vec3VArg Vec3V & +#define Vec4VArg Vec4V & +#define BoolVArg BoolV & +#define VecU32VArg VecU32V & +#define VecI32VArg VecI32V & +#define VecU16VArg VecU16V & +#define VecI16VArg VecI16V & +#define VecU8VArg VecU8V & +#define QuatVArg QuatV & + +// KS - TODO - make an actual VecCrossV type for NEON +#define VecCrossV Vec3V + +typedef VecI32V VecShiftV; +#define VecShiftVArg VecShiftV & + +PX_ALIGN_PREFIX(16) +struct Mat33V +{ + Mat33V() + { + } + Mat33V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat34V +{ + Mat34V() + { + } + Mat34V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2, const Vec3V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); + Vec3V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat43V +{ + Mat43V() + { + } + Mat43V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat44V +{ + Mat44V() + { + } + Mat44V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2, const Vec4V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); + Vec4V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSUNIXNEONAOS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h new file mode 100644 index 000000000..2db302e93 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h @@ -0,0 +1,3597 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXNEONINLINEAOS_H +#define PSFOUNDATION_PSUNIXNEONINLINEAOS_H + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +// improved estimates +#define VRECIPEQ recipq_newton<1> +#define VRECIPE recip_newton<1> +#define VRECIPSQRTEQ rsqrtq_newton<1> +#define VRECIPSQRTE rsqrt_newton<1> + +// "exact" +#define VRECIPQ recipq_newton<4> +#if PX_SWITCH +// StabilizationTests.AveragePoint needs more precision to succeed. +#define VRECIP recip_newton<5> +#else +#define VRECIP recip_newton<4> +#endif +#define VRECIPSQRTQ rsqrtq_newton<4> +#define VRECIPSQRT rsqrt_newton<4> + +#define VECMATH_AOS_EPSILON (1e-3f) + +////////////////////////////////////////////////////////////////////// +//Test that Vec3V and FloatV are legal +////////////////////////////////// + +#define FLOAT_COMPONENTS_EQUAL_THRESHOLD 0.01f +PX_FORCE_INLINE bool isValidFloatV(const FloatV a) +{ + /* + PX_ALIGN(16, PxF32) data[4]; + vst1_f32(reinterpret_cast(data), a); + return + PxU32* intData = reinterpret_cast(data); + return intData[0] == intData[1]; + */ + PX_ALIGN(16, PxF32) data[4]; + vst1_f32(reinterpret_cast(data), a); + const float32_t x = data[0]; + const float32_t y = data[1]; + + if (PxAbs(x - y) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + { + return true; + } + + if (PxAbs((x - y) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + { + return true; + } + + return false; +} + +PX_FORCE_INLINE bool isValidVec3V(const Vec3V a) +{ + const float32_t w = vgetq_lane_f32(a, 3); + return (0.0f == w); + //const PxU32* intData = reinterpret_cast(&w); + //return *intData == 0; +} + +PX_FORCE_INLINE bool isAligned16(const void* a) +{ + return(0 == (size_t(a) & 0x0f)); +} + +#if PX_DEBUG +#define ASSERT_ISVALIDVEC3V(a) PX_ASSERT(isValidVec3V(a)) +#define ASSERT_ISVALIDFLOATV(a) PX_ASSERT(isValidFloatV(a)) +#define ASSERT_ISALIGNED16(a) PX_ASSERT(isAligned16(static_cast(a))) +#else +#define ASSERT_ISVALIDVEC3V(a) +#define ASSERT_ISVALIDFLOATV(a) +#define ASSERT_ISALIGNED16(a) +#endif + +namespace internalUnitNeonSimd +{ +PX_FORCE_INLINE PxU32 BAllTrue4_R(const BoolV a) +{ + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + const uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + return PxU32(vget_lane_u32(finalReduce, 0) == 0xffffFFFF); +} + +PX_FORCE_INLINE PxU32 BAllTrue3_R(const BoolV a) +{ + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + const uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + return PxU32((vget_lane_u32(finalReduce, 0) & 0xffFFff) == 0xffFFff); +} + +PX_FORCE_INLINE PxU32 BAnyTrue4_R(const BoolV a) +{ + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + const uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + return PxU32(vget_lane_u32(finalReduce, 0) != 0x0); +} + +PX_FORCE_INLINE PxU32 BAnyTrue3_R(const BoolV a) +{ + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + const uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + return PxU32((vget_lane_u32(finalReduce, 0) & 0xffFFff) != 0); +} +} + +namespace _VecMathTests +{ +// PT: this function returns an invalid Vec3V (W!=0.0f) just for unit-testing 'isValidVec3V' +PX_FORCE_INLINE Vec3V getInvalidVec3V() +{ + PX_ALIGN(16, PxF32) data[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE bool allElementsEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vget_lane_u32(vceq_f32(a, b), 0) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec3V(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return V3AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec4V(const Vec4V a, const Vec4V b) +{ + return V4AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualBoolV(const BoolV a, const BoolV b) +{ + return internalUnitNeonSimd::BAllTrue4_R(vceqq_u32(a, b)) != 0; +} + +PX_FORCE_INLINE PxU32 V4U32AllEq(const VecU32V a, const VecU32V b) +{ + return internalUnitNeonSimd::BAllTrue4_R(V4IsEqU32(a, b)); +} + +PX_FORCE_INLINE bool allElementsEqualVecU32V(const VecU32V a, const VecU32V b) +{ + return V4U32AllEq(a, b) != 0; +} + +PX_FORCE_INLINE BoolV V4IsEqI32(const VecI32V a, const VecI32V b) +{ + return vceqq_s32(a, b); +} + +PX_FORCE_INLINE PxU32 V4I32AllEq(const VecI32V a, const VecI32V b) +{ + return internalUnitNeonSimd::BAllTrue4_R(V4IsEqI32(a, b)); +} + +PX_FORCE_INLINE bool allElementsEqualVecI32V(const VecI32V a, const VecI32V b) +{ + return V4I32AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsNearEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + + const float32x2_t c = vsub_f32(a, b); + const float32x2_t error = vdup_n_f32(VECMATH_AOS_EPSILON); +// absolute compare abs(error) > abs(c) + const uint32x2_t greater = vcagt_f32(error, c); + const uint32x2_t min = vpmin_u32(greater, greater); + return vget_lane_u32(min, 0) != 0x0; +} + +PX_FORCE_INLINE bool allElementsNearEqualVec3V(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + const float32x4_t c = vsubq_f32(a, b); + const float32x4_t error = vdupq_n_f32(VECMATH_AOS_EPSILON); +// absolute compare abs(error) > abs(c) + const uint32x4_t greater = vcagtq_f32(error, c); + return internalUnitNeonSimd::BAllTrue3_R(greater) != 0; +} + +PX_FORCE_INLINE bool allElementsNearEqualVec4V(const Vec4V a, const Vec4V b) +{ + const float32x4_t c = vsubq_f32(a, b); + const float32x4_t error = vdupq_n_f32(VECMATH_AOS_EPSILON); +// absolute compare abs(error) > abs(c) + const uint32x4_t greater = vcagtq_f32(error, c); + return internalUnitNeonSimd::BAllTrue4_R(greater) != 0x0; +} +} + +#if 0 // debugging printfs +#include +PX_FORCE_INLINE void printVec(const float32x4_t& v, const char* name) +{ + PX_ALIGN(16, float32_t) data[4]; + vst1q_f32(data, v); + printf("%s: (%f, %f, %f, %f)\n", name, data[0], data[1], data[2], data[3]); +} + +PX_FORCE_INLINE void printVec(const float32x2_t& v, const char* name) +{ + PX_ALIGN(16, float32_t) data[2]; + vst1_f32(data, v); + printf("%s: (%f, %f)\n", name, data[0], data[1]); +} + +PX_FORCE_INLINE void printVec(const uint32x4_t& v, const char* name) +{ + PX_ALIGN(16, uint32_t) data[4]; + vst1q_u32(data, v); + printf("%s: (0x%x, 0x%x, 0x%x, 0x%x)\n", name, data[0], data[1], data[2], data[3]); +} + +PX_FORCE_INLINE void printVec(const uint16x8_t& v, const char* name) +{ + PX_ALIGN(16, uint16_t) data[8]; + vst1q_u16(data, v); + printf("%s: (0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", name, data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7]); +} + +PX_FORCE_INLINE void printVec(const int32x4_t& v, const char* name) +{ + PX_ALIGN(16, int32_t) data[4]; + vst1q_s32(data, v); + printf("%s: (0x%x, 0x%x, 0x%x, 0x%x)\n", name, data[0], data[1], data[2], data[3]); +} + +PX_FORCE_INLINE void printVec(const int16x8_t& v, const char* name) +{ + PX_ALIGN(16, int16_t) data[8]; + vst1q_s16(data, v); + printf("%s: (0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", name, data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7]); +} + +PX_FORCE_INLINE void printVec(const uint16x4_t& v, const char* name) +{ + PX_ALIGN(16, uint16_t) data[4]; + vst1_u16(data, v); + printf("%s: (0x%x, 0x%x, 0x%x, 0x%x)\n", name, data[0], data[1], data[2], data[3]); +} + +PX_FORCE_INLINE void printVec(const uint32x2_t& v, const char* name) +{ + PX_ALIGN(16, uint32_t) data[2]; + vst1_u32(data, v); + printf("%s: (0x%x, 0x%x)\n", name, data[0], data[1]); +} + +PX_FORCE_INLINE void printVar(const PxU32 v, const char* name) +{ + printf("%s: 0x%x\n", name, v); +} + +PX_FORCE_INLINE void printVar(const PxF32 v, const char* name) +{ + printf("%s: %f\n", name, v); +} + +#define PRINT_VAR(X) printVar((X), #X) +#define PRINT_VEC(X) printVec((X), #X) +#define PRINT_VEC_TITLE(TITLE, X) printVec((X), TITLE #X) +#endif // debugging printf + +///////////////////////////////////////////////////////////////////// +////FUNCTIONS USED ONLY FOR ASSERTS IN VECTORISED IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE bool isFiniteFloatV(const FloatV a) +{ + PX_ALIGN(16, PxF32) data[4]; + vst1_f32(reinterpret_cast(data), a); + return PxIsFinite(data[0]) && PxIsFinite(data[1]); +} + +PX_FORCE_INLINE bool isFiniteVec3V(const Vec3V a) +{ + PX_ALIGN(16, PxF32) data[4]; + vst1q_f32(reinterpret_cast(data), a); + return PxIsFinite(data[0]) && PxIsFinite(data[1]) && PxIsFinite(data[2]); +} + +PX_FORCE_INLINE bool isFiniteVec4V(const Vec4V a) +{ + PX_ALIGN(16, PxF32) data[4]; + vst1q_f32(reinterpret_cast(data), a); + return PxIsFinite(data[0]) && PxIsFinite(data[1]) && PxIsFinite(data[2]) && PxIsFinite(data[3]); +} + +PX_FORCE_INLINE bool hasZeroElementinFloatV(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return vget_lane_u32(vreinterpret_u32_f32(a), 0) == 0; +} + +PX_FORCE_INLINE bool hasZeroElementInVec3V(const Vec3V a) +{ + const uint32x2_t dLow = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t dMin = vpmin_u32(dLow, dLow); + + return vget_lane_u32(dMin, 0) == 0 || vgetq_lane_u32(vreinterpretq_u32_f32(a), 2) == 0; +} + +PX_FORCE_INLINE bool hasZeroElementInVec4V(const Vec4V a) +{ + const uint32x2_t dHigh = vget_high_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t dLow = vget_low_u32(vreinterpretq_u32_f32(a)); + + const uint32x2_t dMin = vmin_u32(dHigh, dLow); + const uint32x2_t pairMin = vpmin_u32(dMin, dMin); + return vget_lane_u32(pairMin, 0) == 0; +} + +///////////////////////////////////////////////////////////////////// +////VECTORISED FUNCTION IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE FloatV FLoad(const PxF32 f) +{ + return vdup_n_f32(reinterpret_cast(f)); +} + +PX_FORCE_INLINE FloatV FLoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(f); + return vld1_f32(reinterpret_cast(f)); +} + +PX_FORCE_INLINE Vec3V V3Load(const PxF32 f) +{ + PX_ALIGN(16, PxF32) data[4] = { f, f, f, 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec4V V4Load(const PxF32 f) +{ + return vdupq_n_f32(reinterpret_cast(f)); +} + +PX_FORCE_INLINE BoolV BLoad(const bool f) +{ + const PxU32 i = static_cast(-(static_cast(f))); + return vdupq_n_u32(i); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + PX_ALIGN(16, PxF32) data[4] = { f.x, f.y, f.z, 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxVec3& f) +{ + PX_ALIGN(16, PxF32) data[4] = { f.x, f.y, f.z, 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec3V V3LoadUnsafeA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + PX_ALIGN(16, PxF32) data[4] = { f.x, f.y, f.z, 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxF32* f) +{ + ASSERT_ISALIGNED16(f); + PX_ALIGN(16, PxF32) data[4] = { f[0], f[1], f[2], 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxF32* f) +{ + PX_ALIGN(16, PxF32) data[4] = { f[0], f[1], f[2], 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V v) +{ + return vsetq_lane_f32(0.0f, v, 3); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V_WUndefined(Vec4V v) +{ + return v; +} + +PX_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f) +{ + return f; // ok if it is implemented as the same type. +} + +PX_FORCE_INLINE Vec4V Vec4V_From_FloatV(FloatV f) +{ + return vcombine_f32(f, f); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV(FloatV f) +{ + return Vec3V_From_Vec4V(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV_WUndefined(FloatV f) +{ + return Vec3V_From_Vec4V_WUndefined(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_PxVec3_WUndefined(const PxVec3& f) +{ + PX_ALIGN(16, PxF32) data[4] = { f.x, f.y, f.z, 0.0f }; + return V4LoadA(data); +} + +PX_FORCE_INLINE Mat33V Mat33V_From_PxMat33(const PxMat33& m) +{ + return Mat33V(V3LoadU(m.column0), V3LoadU(m.column1), V3LoadU(m.column2)); +} + +PX_FORCE_INLINE void PxMat33_From_Mat33V(const Mat33V& m, PxMat33& out) +{ + V3StoreU(m.col0, out.column0); + V3StoreU(m.col1, out.column1); + V3StoreU(m.col2, out.column2); +} + +PX_FORCE_INLINE Vec4V V4LoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(f); + return vld1q_f32(reinterpret_cast(f)); +} + +PX_FORCE_INLINE void V4StoreA(Vec4V a, PxF32* f) +{ + ASSERT_ISALIGNED16(f); + vst1q_f32(reinterpret_cast(f), a); +} + +PX_FORCE_INLINE void V4StoreU(const Vec4V a, PxF32* f) +{ + PX_ALIGN(16, PxF32) f2[4]; + vst1q_f32(reinterpret_cast(f2), a); + f[0] = f2[0]; + f[1] = f2[1]; + f[2] = f2[2]; + f[3] = f2[3]; +} + +PX_FORCE_INLINE void BStoreA(const BoolV a, PxU32* u) +{ + ASSERT_ISALIGNED16(u); + vst1q_u32(reinterpret_cast(u), a); +} + +PX_FORCE_INLINE void U4StoreA(const VecU32V uv, PxU32* u) +{ + ASSERT_ISALIGNED16(u); + vst1q_u32(reinterpret_cast(u), uv); +} + +PX_FORCE_INLINE void I4StoreA(const VecI32V iv, PxI32* i) +{ + ASSERT_ISALIGNED16(i); + vst1q_s32(reinterpret_cast(i), iv); +} + +PX_FORCE_INLINE Vec4V V4LoadU(const PxF32* const f) +{ + return vld1q_f32(reinterpret_cast(f)); +} + +PX_FORCE_INLINE BoolV BLoad(const bool* const f) +{ + const PX_ALIGN(16, PxU32) b[4] = { static_cast(-static_cast(f[0])), + static_cast(-static_cast(f[1])), + static_cast(-static_cast(f[2])), + static_cast(-static_cast(f[3])) }; + return vld1q_u32(b); +} + +PX_FORCE_INLINE void FStore(const FloatV a, PxF32* PX_RESTRICT f) +{ + ASSERT_ISVALIDFLOATV(a); + // vst1q_lane_f32(f, a, 0); // causes vst1 alignment bug + *f = vget_lane_f32(a, 0); +} + +PX_FORCE_INLINE void Store_From_BoolV(const BoolV a, PxU32* PX_RESTRICT f) +{ + *f = vget_lane_u32(vget_low_u32(a), 0); +} + +PX_FORCE_INLINE void V3StoreA(const Vec3V a, PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + PX_ALIGN(16, PxF32) f2[4]; + vst1q_f32(reinterpret_cast(f2), a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +PX_FORCE_INLINE void V3StoreU(const Vec3V a, PxVec3& f) +{ + PX_ALIGN(16, PxF32) f2[4]; + vst1q_f32(reinterpret_cast(f2), a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +////////////////////////////////// +// FLOATV +////////////////////////////////// + +PX_FORCE_INLINE FloatV FZero() +{ + return FLoad(0.0f); +} + +PX_FORCE_INLINE FloatV FOne() +{ + return FLoad(1.0f); +} + +PX_FORCE_INLINE FloatV FHalf() +{ + return FLoad(0.5f); +} + +PX_FORCE_INLINE FloatV FEps() +{ + return FLoad(PX_EPS_REAL); +} + +PX_FORCE_INLINE FloatV FEps6() +{ + return FLoad(1e-6f); +} + +PX_FORCE_INLINE FloatV FMax() +{ + return FLoad(PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV FNegMax() +{ + return FLoad(-PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV IZero() +{ + return vreinterpret_f32_u32(vdup_n_u32(0)); +} + +PX_FORCE_INLINE FloatV IOne() +{ + return vreinterpret_f32_u32(vdup_n_u32(1)); +} + +PX_FORCE_INLINE FloatV ITwo() +{ + return vreinterpret_f32_u32(vdup_n_u32(2)); +} + +PX_FORCE_INLINE FloatV IThree() +{ + return vreinterpret_f32_u32(vdup_n_u32(3)); +} + +PX_FORCE_INLINE FloatV IFour() +{ + return vreinterpret_f32_u32(vdup_n_u32(4)); +} + +PX_FORCE_INLINE FloatV FNeg(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return vneg_f32(f); +} + +PX_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vadd_f32(a, b); +} + +PX_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vsub_f32(a, b); +} + +PX_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vmul_f32(a, b); +} + +template +PX_FORCE_INLINE float32x2_t recip_newton(const float32x2_t& in) +{ + float32x2_t recip = vrecpe_f32(in); + for(int i = 0; i < n; ++i) + recip = vmul_f32(recip, vrecps_f32(in, recip)); + return recip; +} + +template +PX_FORCE_INLINE float32x4_t recipq_newton(const float32x4_t& in) +{ + float32x4_t recip = vrecpeq_f32(in); + for(int i = 0; i < n; ++i) + recip = vmulq_f32(recip, vrecpsq_f32(recip, in)); + return recip; +} + +template +PX_FORCE_INLINE float32x2_t rsqrt_newton(const float32x2_t& in) +{ + float32x2_t rsqrt = vrsqrte_f32(in); + for(int i = 0; i < n; ++i) + rsqrt = vmul_f32(rsqrt, vrsqrts_f32(vmul_f32(rsqrt, rsqrt), in)); + return rsqrt; +} + +template +PX_FORCE_INLINE float32x4_t rsqrtq_newton(const float32x4_t& in) +{ + float32x4_t rsqrt = vrsqrteq_f32(in); + for(int i = 0; i < n; ++i) + rsqrt = vmulq_f32(rsqrt, vrsqrtsq_f32(vmulq_f32(rsqrt, rsqrt), in)); + return rsqrt; +} + +PX_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vmul_f32(a, VRECIP(b)); +} + +PX_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vmul_f32(a, VRECIPE(b)); +} + +PX_FORCE_INLINE FloatV FRecip(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return VRECIP(a); +} + +PX_FORCE_INLINE FloatV FRecipFast(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return VRECIPE(a); +} + +PX_FORCE_INLINE FloatV FRsqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return VRECIPSQRT(a); +} + +PX_FORCE_INLINE FloatV FSqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return FSel(FIsEq(a, FZero()), a, vmul_f32(a, VRECIPSQRT(a))); +} + +PX_FORCE_INLINE FloatV FRsqrtFast(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return VRECIPSQRTE(a); +} + +PX_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return vmla_f32(c, a, b); +} + +PX_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return vmls_f32(c, a, b); +} + +PX_FORCE_INLINE FloatV FAbs(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return vabs_f32(a); +} + +PX_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b) +{ + PX_ASSERT( _VecMathTests::allElementsEqualBoolV(c, BTTTT()) || + _VecMathTests::allElementsEqualBoolV(c, BFFFF())); + ASSERT_ISVALIDFLOATV(vbsl_f32(vget_low_u32(c), a, b)); + return vbsl_f32(vget_low_u32(c), a, b); +} + +PX_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vdupq_lane_u32(vcgt_f32(a, b), 0); +} + +PX_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vdupq_lane_u32(vcge_f32(a, b), 0); +} + +PX_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vdupq_lane_u32(vceq_f32(a, b), 0); +} + +PX_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b) +{ + //ASSERT_ISVALIDFLOATV(a); + //ASSERT_ISVALIDFLOATV(b); + return vmax_f32(a, b); +} + +PX_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b) +{ + //ASSERT_ISVALIDFLOATV(a); + //ASSERT_ISVALIDFLOATV(b); + return vmin_f32(a, b); +} + +PX_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV) +{ + ASSERT_ISVALIDFLOATV(minV); + ASSERT_ISVALIDFLOATV(maxV); + return vmax_f32(vmin_f32(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 FAllGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vget_lane_u32(vcgt_f32(a, b), 0); +} + +PX_FORCE_INLINE PxU32 FAllGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vget_lane_u32(vcge_f32(a, b), 0); +} + +PX_FORCE_INLINE PxU32 FAllEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return vget_lane_u32(vceq_f32(a, b), 0); +} + +PX_FORCE_INLINE FloatV FRound(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // truncate(a + (0.5f - sign(a))) + const float32x2_t half = vdup_n_f32(0.5f); + const float32x2_t sign = vcvt_f32_u32((vshr_n_u32(vreinterpret_u32_f32(a), 31))); + const float32x2_t aPlusHalf = vadd_f32(a, half); + const float32x2_t aRound = vsub_f32(aPlusHalf, sign); + int32x2_t tmp = vcvt_s32_f32(aRound); + return vcvt_f32_s32(tmp); +} + +PX_FORCE_INLINE FloatV FSin(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = FLoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = FLoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V3 = FMul(V2, V1); + const FloatV V5 = FMul(V3, V2); + const FloatV V7 = FMul(V5, V2); + const FloatV V9 = FMul(V7, V2); + const FloatV V11 = FMul(V9, V2); + const FloatV V13 = FMul(V11, V2); + const FloatV V15 = FMul(V13, V2); + const FloatV V17 = FMul(V15, V2); + const FloatV V19 = FMul(V17, V2); + const FloatV V21 = FMul(V19, V2); + const FloatV V23 = FMul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + FloatV Result; + Result = FScaleAdd(S1, V3, V1); + Result = FScaleAdd(S2, V5, Result); + Result = FScaleAdd(S3, V7, Result); + Result = FScaleAdd(S4, V9, Result); + Result = FScaleAdd(S5, V11, Result); + Result = FScaleAdd(S6, V13, Result); + Result = FScaleAdd(S7, V15, Result); + Result = FScaleAdd(S8, V17, Result); + Result = FScaleAdd(S9, V19, Result); + Result = FScaleAdd(S10, V21, Result); + Result = FScaleAdd(S11, V23, Result); + + return Result; +} + +PX_FORCE_INLINE FloatV FCos(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = FLoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = FLoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V4 = FMul(V2, V2); + const FloatV V6 = FMul(V4, V2); + const FloatV V8 = FMul(V4, V4); + const FloatV V10 = FMul(V6, V4); + const FloatV V12 = FMul(V6, V6); + const FloatV V14 = FMul(V8, V6); + const FloatV V16 = FMul(V8, V8); + const FloatV V18 = FMul(V10, V8); + const FloatV V20 = FMul(V10, V10); + const FloatV V22 = FMul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + FloatV Result; + Result = FScaleAdd(C1, V2, FOne()); + Result = FScaleAdd(C2, V4, Result); + Result = FScaleAdd(C3, V6, Result); + Result = FScaleAdd(C4, V8, Result); + Result = FScaleAdd(C5, V10, Result); + Result = FScaleAdd(C6, V12, Result); + Result = FScaleAdd(C7, V14, Result); + Result = FScaleAdd(C8, V16, Result); + Result = FScaleAdd(C9, V18, Result); + Result = FScaleAdd(C10, V20, Result); + Result = FScaleAdd(C11, V22, Result); + + return Result; +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max); + + const BoolV c = BOr(FIsGrtr(a, max), FIsGrtr(min, a)); + return PxU32(!BAllEqFFFF(c)); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max); + + const BoolV c = BAnd(FIsGrtrOrEq(a, min), FIsGrtrOrEq(max, a)); + return PxU32(BAllEqTTTT(c)); +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + const uint32x2_t greater = vcagt_f32(a, bounds); + return vget_lane_u32(greater, 0); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + const uint32x2_t geq = vcage_f32(bounds, a); + return vget_lane_u32(geq, 0); +} + +////////////////////////////////// +// VEC3V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V V3Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t uHigh = vreinterpret_u32_f32(f); + const float32x2_t dHigh = vreinterpret_f32_u32(vand_u32(uHigh, mask)); + + return vcombine_f32(f, dHigh); +} + +PX_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t dHigh = vand_u32(vreinterpret_u32_f32(z), mask); + const uint32x2_t dLow = vext_u32(vreinterpret_u32_f32(x), vreinterpret_u32_f32(y), 1); + return vreinterpretq_f32_u32(vcombine_u32(dLow, dHigh)); +} + +PX_FORCE_INLINE Vec3V V3UnitX() +{ + const float32x4_t x = { 1.0f, 0.0f, 0.0f, 0.0f }; + return x; +} + +PX_FORCE_INLINE Vec3V V3UnitY() +{ + const float32x4_t y = { 0, 1.0f, 0, 0 }; + return y; +} + +PX_FORCE_INLINE Vec3V V3UnitZ() +{ + const float32x4_t z = { 0, 0, 1.0f, 0 }; + return z; +} + +PX_FORCE_INLINE FloatV V3GetX(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + const float32x2_t fLow = vget_low_f32(f); + return vdup_lane_f32(fLow, 0); +} + +PX_FORCE_INLINE FloatV V3GetY(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + const float32x2_t fLow = vget_low_f32(f); + return vdup_lane_f32(fLow, 1); +} + +PX_FORCE_INLINE FloatV V3GetZ(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + const float32x2_t fhigh = vget_high_f32(f); + return vdup_lane_f32(fhigh, 0); +} + +PX_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + + const float32x2_t aLow = vget_low_f32(a); + const float32x2_t bLow = vget_low_f32(b); + const float32x2_t cLow = vget_low_f32(c); + const float32x2_t zero = vdup_n_f32(0.0f); + + const float32x2x2_t zipL = vzip_f32(aLow, bLow); + const float32x2x2_t zipH = vzip_f32(cLow, zero); + + return vcombine_f32(zipL.val[0], zipH.val[0]); +} + +PX_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + + const float32x2_t aLow = vget_low_f32(a); + const float32x2_t bLow = vget_low_f32(b); + const float32x2_t cLow = vget_low_f32(c); + const float32x2_t zero = vdup_n_f32(0.0f); + + const float32x2x2_t zipL = vzip_f32(aLow, bLow); + const float32x2x2_t zipH = vzip_f32(cLow, zero); + + return vcombine_f32(zipL.val[1], zipH.val[1]); +} + +PX_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + + const float32x2_t aHi = vget_high_f32(a); + const float32x2_t bHi = vget_high_f32(b); + const float32x2_t cHi = vget_high_f32(c); + + const float32x2x2_t zipL = vzip_f32(aHi, bHi); + + return vcombine_f32(zipL.val[0], cHi); +} + +PX_FORCE_INLINE Vec3V V3Zero() +{ + return vdupq_n_f32(0.0f); +} + +PX_FORCE_INLINE Vec3V V3Eps() +{ + return V3Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec3V V3One() +{ + return V3Load(1.0f); +} + +PX_FORCE_INLINE Vec3V V3Neg(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + const float32x4_t tmp = vnegq_f32(f); + return vsetq_lane_f32(0.0f, tmp, 3); +} + +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vaddq_f32(a, b); +} + +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return vaddq_f32(a, Vec3V_From_FloatV(b)); +} + +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vsubq_f32(a, b); +} + +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return vsubq_f32(a, Vec3V_From_FloatV(b)); +} + +PX_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + const float32x4_t tmp = vmulq_lane_f32(a, b, 0); + return vsetq_lane_f32(0.0f, tmp, 3); +} + +PX_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vmulq_f32(a, b); +} + +PX_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + const float32x2_t invB = VRECIP(b); + const float32x4_t tmp = vmulq_lane_f32(a, invB, 0); + return vsetq_lane_f32(0.0f, tmp, 3); +} + +PX_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + float32x4_t invB = VRECIPQ(b); + invB = vsetq_lane_f32(0.0f, invB, 3); + return vmulq_f32(a, invB); +} + +PX_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + const float32x2_t invB = VRECIPE(b); + const float32x4_t tmp = vmulq_lane_f32(a, invB, 0); + return vsetq_lane_f32(0.0f, tmp, 3); +} + +PX_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + float32x4_t invB = VRECIPEQ(b); + invB = vsetq_lane_f32(0.0f, invB, 3); + return vmulq_f32(a, invB); +} + +PX_FORCE_INLINE Vec3V V3Recip(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const float32x4_t recipA = VRECIPQ(a); + return vsetq_lane_f32(0.0f, recipA, 3); +} + +PX_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const float32x4_t recipA = VRECIPEQ(a); + return vsetq_lane_f32(0.0f, recipA, 3); +} + +PX_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const float32x4_t rSqrA = VRECIPSQRTQ(a); + return vsetq_lane_f32(0.0f, rSqrA, 3); +} + +PX_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const float32x4_t rSqrA = VRECIPSQRTEQ(a); + return vsetq_lane_f32(0.0f, rSqrA, 3); +} + +PX_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + + float32x4_t tmp = vmlaq_lane_f32(c, a, b, 0); + // using vsetq_lane_f32 resulted in failures, + // probably related to a compiler bug on + // ndk r9d-win32, gcc 4.8, cardhu/shield + + // code with issue + // return vsetq_lane_f32(0.0f, tmp, 3); + + // workaround + float32x2_t w_z = vget_high_f32(tmp); + float32x2_t y_x = vget_low_f32(tmp); + w_z = vset_lane_f32(0.0f, w_z, 1); + return vcombine_f32(y_x, w_z); +} + +PX_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + + float32x4_t tmp = vmlsq_lane_f32(c, a, b, 0); + // using vsetq_lane_f32 resulted in failures, + // probably related to a compiler bug on + // ndk r9d-win32, gcc 4.8, cardhu/shield + + // code with issue + // return vsetq_lane_f32(0.0f, tmp, 3); + + // workaround + float32x2_t w_z = vget_high_f32(tmp); + float32x2_t y_x = vget_low_f32(tmp); + w_z = vset_lane_f32(0.0f, w_z, 1); + return vcombine_f32(y_x, w_z); +} + +PX_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return vmlaq_f32(c, a, b); +} + +PX_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return vmlsq_f32(c, a, b); +} + +PX_FORCE_INLINE Vec3V V3Abs(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return vabsq_f32(a); +} + +PX_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + + // const uint32x2_t mask = {0xffffFFFF, 0x0}; + const float32x4_t tmp = vmulq_f32(a, b); + + const float32x2_t low = vget_low_f32(tmp); + const float32x2_t high = vget_high_f32(tmp); + // const float32x2_t high = vreinterpret_f32_u32(vand_u32(vreinterpret_u32_f32(high_), mask)); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {0+z, x+y} + const float32x2_t sum0ZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z, x+y+z} + + return sum0ZYX; +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + + const uint32x2_t TF = { 0xffffFFFF, 0x0 }; + const float32x2_t ay_ax = vget_low_f32(a); // d2 + const float32x2_t aw_az = vget_high_f32(a); // d3 + const float32x2_t by_bx = vget_low_f32(b); // d4 + const float32x2_t bw_bz = vget_high_f32(b); // d5 + // Hi, Lo + const float32x2_t bz_by = vext_f32(by_bx, bw_bz, 1); // bz, by + const float32x2_t az_ay = vext_f32(ay_ax, aw_az, 1); // az, ay + + const float32x2_t azbx = vmul_f32(aw_az, by_bx); // 0, az*bx + const float32x2_t aybz_axby = vmul_f32(ay_ax, bz_by); // ay*bz, ax*by + + const float32x2_t azbxSUBaxbz = vmls_f32(azbx, bw_bz, ay_ax); // 0, az*bx-ax*bz + const float32x2_t aybzSUBazby_axbySUBaybx = vmls_f32(aybz_axby, by_bx, az_ay); // ay*bz-az*by, ax*by-ay*bx + + const float32x2_t retLow = vext_f32(aybzSUBazby_axbySUBaybx, azbxSUBaxbz, 1); // az*bx-ax*bz, ay*bz-az*by + const uint32x2_t retHigh = vand_u32(TF, vreinterpret_u32_f32(aybzSUBazby_axbySUBaybx)); // 0, ax*by-ay*bx + + return vcombine_f32(retLow, vreinterpret_f32_u32(retHigh)); +} + +PX_FORCE_INLINE VecCrossV V3PrepareCross(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return a; +} + +PX_FORCE_INLINE FloatV V3Length(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // const uint32x2_t mask = {0xffffFFFF, 0x0}; + + const float32x4_t tmp = vmulq_f32(a, a); + const float32x2_t low = vget_low_f32(tmp); + const float32x2_t high = vget_high_f32(tmp); + // const float32x2_t high = vreinterpret_f32_u32(vand_u32(vreinterpret_u32_f32(high_), mask)); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {0+z, x+y} + const float32x2_t sum0ZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z, x+y+z} + + return FSqrt(sum0ZYX); +} + +PX_FORCE_INLINE FloatV V3LengthSq(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return V3Dot(a, a); +} + +PX_FORCE_INLINE Vec3V V3Normalize(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + //PX_ASSERT(!FAllEq(V4LengthSq(a), FZero())); + return V3ScaleInv(a, V3Length(a)); +} + +PX_FORCE_INLINE Vec3V V3NormalizeFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + //PX_ASSERT(!FAllEq(V4LengthSq(a), FZero())); + return V3Scale(a, VRECIPSQRTE(V3Dot(a, a))); +} + +PX_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a, const Vec3V unsafeReturnValue) +{ + ASSERT_ISVALIDVEC3V(a); + const FloatV zero = vdup_n_f32(0.0f); + const FloatV length = V3Length(a); + const uint32x4_t isGreaterThanZero = FIsGrtr(length, zero); + return V3Sel(isGreaterThanZero, V3ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V( vbslq_f32(c, a, b)); + return vbslq_f32(c, a, b); +} + +PX_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vcgtq_f32(a, b); +} + +PX_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vcgeq_f32(a, b); +} + +PX_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vceqq_f32(a, b); +} + +PX_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vmaxq_f32(a, b); +} + +PX_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return vminq_f32(a, b); +} + +PX_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + + const float32x2_t zz = vdup_lane_f32(high, 0); + const float32x2_t max0 = vpmax_f32(zz, low); + const float32x2_t max1 = vpmax_f32(max0, max0); + + return max1; +} + +PX_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + + const float32x2_t zz = vdup_lane_f32(high, 0); + const float32x2_t min0 = vpmin_f32(zz, low); + const float32x2_t min1 = vpmin_f32(min0, min0); + + return min1; +} + +// return (a >= 0.0f) ? 1.0f : -1.0f; +PX_FORCE_INLINE Vec3V V3Sign(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const Vec3V zero = V3Zero(); + const Vec3V one = V3One(); + const Vec3V none = V3Neg(one); + return V3Sel(V3IsGrtrOrEq(a, zero), one, none); +} + +PX_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV) +{ + ASSERT_ISVALIDVEC3V(minV); + ASSERT_ISVALIDVEC3V(maxV); + return V3Max(V3Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V3AllGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitNeonSimd::BAllTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitNeonSimd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitNeonSimd::BAllTrue3_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE Vec3V V3Round(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + // truncate(a + (0.5f - sign(a))) + const Vec3V half = V3Load(0.5f); + const float32x4_t sign = vcvtq_f32_u32((vshrq_n_u32(vreinterpretq_u32_f32(a), 31))); + const Vec3V aPlusHalf = V3Add(a, half); + const Vec3V aRound = V3Sub(aPlusHalf, sign); + return vcvtq_f32_s32(vcvtq_s32_f32(aRound)); +} + +PX_FORCE_INLINE Vec3V V3Sin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V4Mul(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V4NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V3 = V3Mul(V2, V1); + const Vec3V V5 = V3Mul(V3, V2); + const Vec3V V7 = V3Mul(V5, V2); + const Vec3V V9 = V3Mul(V7, V2); + const Vec3V V11 = V3Mul(V9, V2); + const Vec3V V13 = V3Mul(V11, V2); + const Vec3V V15 = V3Mul(V13, V2); + const Vec3V V17 = V3Mul(V15, V2); + const Vec3V V19 = V3Mul(V17, V2); + const Vec3V V21 = V3Mul(V19, V2); + const Vec3V V23 = V3Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec3V Result; + Result = V4ScaleAdd(V3, S1, V1); + Result = V4ScaleAdd(V5, S2, Result); + Result = V4ScaleAdd(V7, S3, Result); + Result = V4ScaleAdd(V9, S4, Result); + Result = V4ScaleAdd(V11, S5, Result); + Result = V4ScaleAdd(V13, S6, Result); + Result = V4ScaleAdd(V15, S7, Result); + Result = V4ScaleAdd(V17, S8, Result); + Result = V4ScaleAdd(V19, S9, Result); + Result = V4ScaleAdd(V21, S10, Result); + Result = V4ScaleAdd(V23, S11, Result); + + return Result; +} + +PX_FORCE_INLINE Vec3V V3Cos(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V4Mul(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V4NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V4 = V3Mul(V2, V2); + const Vec3V V6 = V3Mul(V4, V2); + const Vec3V V8 = V3Mul(V4, V4); + const Vec3V V10 = V3Mul(V6, V4); + const Vec3V V12 = V3Mul(V6, V6); + const Vec3V V14 = V3Mul(V8, V6); + const Vec3V V16 = V3Mul(V8, V8); + const Vec3V V18 = V3Mul(V10, V8); + const Vec3V V20 = V3Mul(V10, V10); + const Vec3V V22 = V3Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec3V Result; + Result = V4ScaleAdd(V2, C1, V4One()); + Result = V4ScaleAdd(V4, C2, Result); + Result = V4ScaleAdd(V6, C3, Result); + Result = V4ScaleAdd(V8, C4, Result); + Result = V4ScaleAdd(V10, C5, Result); + Result = V4ScaleAdd(V12, C6, Result); + Result = V4ScaleAdd(V14, C7, Result); + Result = V4ScaleAdd(V16, C8, Result); + Result = V4ScaleAdd(V18, C9, Result); + Result = V4ScaleAdd(V20, C10, Result); + Result = V4ScaleAdd(V22, C11, Result); + + return V4ClearW(Result); +} + +PX_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const float32x2_t xy = vget_low_f32(a); + const float32x2_t zw = vget_high_f32(a); + const float32x2_t yz = vext_f32(xy, zw, 1); + return vcombine_f32(yz, zw); +} + +PX_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t xw = vand_u32(xy, mask); + return vreinterpretq_f32_u32(vcombine_u32(xy, xw)); +} + +PX_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t yz = vext_u32(xy, zw, 1); + const uint32x2_t xw = vand_u32(xy, mask); + return vreinterpretq_f32_u32(vcombine_u32(yz, xw)); +} + +PX_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t wz = vrev64_u32(zw); + + const uint32x2_t zx = vext_u32(wz, xy, 1); + const uint32x2_t yw = vext_u32(xy, wz, 1); + + return vreinterpretq_f32_u32(vcombine_u32(zx, yw)); +} + +PX_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(a)); + + const uint32x2_t wz = vrev64_u32(zw); + const uint32x2_t yw = vext_u32(xy, wz, 1); + const uint32x2_t zz = vdup_lane_u32(wz, 1); + + return vreinterpretq_f32_u32(vcombine_u32(zz, yw)); +} + +PX_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t yx = vrev64_u32(xy); + const uint32x2_t xw = vand_u32(xy, mask); + return vreinterpretq_f32_u32(vcombine_u32(yx, xw)); +} + +PX_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(v0)); + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(v1)); + const uint32x2_t wz = vrev64_u32(zw); + const uint32x2_t yw = vext_u32(xy, wz, 1); + + return vreinterpretq_f32_u32(vcombine_u32(wz, yw)); +} + +PX_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + + const uint32x2_t mask = { 0xffffFFFF, 0x0 }; + + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(v0)); + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(v1)); + const uint32x2_t xw = vand_u32(xy, mask); + + return vreinterpretq_f32_u32(vcombine_u32(zw, xw)); +} + +PX_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + + const uint32x2_t axy = vget_low_u32(vreinterpretq_u32_f32(v0)); + const uint32x2_t bxy = vget_low_u32(vreinterpretq_u32_f32(v1)); + const uint32x2_t byax = vext_u32(bxy, axy, 1); + const uint32x2_t ww = vdup_n_u32(0); + + return vreinterpretq_f32_u32(vcombine_u32(byax, ww)); +} + +PX_FORCE_INLINE FloatV V3SumElems(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // const uint32x2_t mask = {0xffffFFFF, 0x0}; + + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + // const float32x2_t high = vreinterpret_f32_u32(vand_u32(vreinterpret_u32_f32(high_), mask)); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {0+z, x+y} + const float32x2_t sum0ZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z, x+y+z} + + return sum0ZYX; +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + + const BoolV c = BOr(V3IsGrtr(a, max), V3IsGrtr(min, a)); + return internalUnitNeonSimd::BAnyTrue3_R(c); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + + const BoolV c = BAnd(V3IsGrtrOrEq(a, min), V3IsGrtrOrEq(max, a)); + return internalUnitNeonSimd::BAllTrue4_R(c); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds); + + const BoolV greater = V3IsGrtr(V3Abs(a), bounds); + return internalUnitNeonSimd::BAnyTrue3_R(greater); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds); + + const BoolV greaterOrEq = V3IsGrtrOrEq(bounds, V3Abs(a)); + return internalUnitNeonSimd::BAllTrue4_R(greaterOrEq); +} + +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2) +{ + ASSERT_ISVALIDVEC3V(col0); + ASSERT_ISVALIDVEC3V(col1); + ASSERT_ISVALIDVEC3V(col2); + + Vec3V col3 = V3Zero(); + const float32x4x2_t v0v1 = vzipq_f32(col0, col2); + const float32x4x2_t v2v3 = vzipq_f32(col1, col3); + const float32x4x2_t zip0 = vzipq_f32(v0v1.val[0], v2v3.val[0]); + const float32x4x2_t zip1 = vzipq_f32(v0v1.val[1], v2v3.val[1]); + col0 = zip0.val[0]; + col1 = zip0.val[1]; + col2 = zip1.val[0]; + // col3 = zip1.val[1]; +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V V4Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return vcombine_f32(f, f); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatV* const floatVArray) +{ + ASSERT_ISVALIDFLOATV(floatVArray[0]); + ASSERT_ISVALIDFLOATV(floatVArray[1]); + ASSERT_ISVALIDFLOATV(floatVArray[2]); + ASSERT_ISVALIDFLOATV(floatVArray[3]); + + const uint32x2_t xLow = vreinterpret_u32_f32(floatVArray[0]); + const uint32x2_t yLow = vreinterpret_u32_f32(floatVArray[1]); + const uint32x2_t zLow = vreinterpret_u32_f32(floatVArray[2]); + const uint32x2_t wLow = vreinterpret_u32_f32(floatVArray[3]); + + const uint32x2_t dLow = vext_u32(xLow, yLow, 1); + const uint32x2_t dHigh = vext_u32(zLow, wLow, 1); + + return vreinterpretq_f32_u32(vcombine_u32(dLow, dHigh)); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + ASSERT_ISVALIDFLOATV(w); + + const uint32x2_t xLow = vreinterpret_u32_f32(x); + const uint32x2_t yLow = vreinterpret_u32_f32(y); + const uint32x2_t zLow = vreinterpret_u32_f32(z); + const uint32x2_t wLow = vreinterpret_u32_f32(w); + + const uint32x2_t dLow = vext_u32(xLow, yLow, 1); + const uint32x2_t dHigh = vext_u32(zLow, wLow, 1); + + return vreinterpretq_f32_u32(vcombine_u32(dLow, dHigh)); +} + +PX_FORCE_INLINE Vec4V V4MergeW(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const float32x2_t xx = vget_high_f32(x); + const float32x2_t yy = vget_high_f32(y); + const float32x2_t zz = vget_high_f32(z); + const float32x2_t ww = vget_high_f32(w); + + const float32x2x2_t zipL = vzip_f32(xx, yy); + const float32x2x2_t zipH = vzip_f32(zz, ww); + + return vcombine_f32(zipL.val[1], zipH.val[1]); +} + +PX_FORCE_INLINE Vec4V V4MergeZ(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const float32x2_t xx = vget_high_f32(x); + const float32x2_t yy = vget_high_f32(y); + const float32x2_t zz = vget_high_f32(z); + const float32x2_t ww = vget_high_f32(w); + + const float32x2x2_t zipL = vzip_f32(xx, yy); + const float32x2x2_t zipH = vzip_f32(zz, ww); + + return vcombine_f32(zipL.val[0], zipH.val[0]); +} + +PX_FORCE_INLINE Vec4V V4MergeY(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const float32x2_t xx = vget_low_f32(x); + const float32x2_t yy = vget_low_f32(y); + const float32x2_t zz = vget_low_f32(z); + const float32x2_t ww = vget_low_f32(w); + + const float32x2x2_t zipL = vzip_f32(xx, yy); + const float32x2x2_t zipH = vzip_f32(zz, ww); + + return vcombine_f32(zipL.val[1], zipH.val[1]); +} + +PX_FORCE_INLINE Vec4V V4MergeX(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const float32x2_t xx = vget_low_f32(x); + const float32x2_t yy = vget_low_f32(y); + const float32x2_t zz = vget_low_f32(z); + const float32x2_t ww = vget_low_f32(w); + + const float32x2x2_t zipL = vzip_f32(xx, yy); + const float32x2x2_t zipH = vzip_f32(zz, ww); + + return vcombine_f32(zipL.val[0], zipH.val[0]); +} + +PX_FORCE_INLINE Vec4V V4UnpackXY(const Vec4VArg a, const Vec4VArg b) +{ + return vzipq_f32(a, b).val[0]; +} + +PX_FORCE_INLINE Vec4V V4UnpackZW(const Vec4VArg a, const Vec4VArg b) +{ + return vzipq_f32(a, b).val[1]; +} + +PX_FORCE_INLINE Vec4V V4UnitW() +{ + const float32x2_t zeros = vreinterpret_f32_u32(vmov_n_u32(0)); + const float32x2_t ones = vmov_n_f32(1.0f); + const float32x2_t zo = vext_f32(zeros, ones, 1); + return vcombine_f32(zeros, zo); +} + +PX_FORCE_INLINE Vec4V V4UnitX() +{ + const float32x2_t zeros = vreinterpret_f32_u32(vmov_n_u32(0)); + const float32x2_t ones = vmov_n_f32(1.0f); + const float32x2_t oz = vext_f32(ones, zeros, 1); + return vcombine_f32(oz, zeros); +} + +PX_FORCE_INLINE Vec4V V4UnitY() +{ + const float32x2_t zeros = vreinterpret_f32_u32(vmov_n_u32(0)); + const float32x2_t ones = vmov_n_f32(1.0f); + const float32x2_t zo = vext_f32(zeros, ones, 1); + return vcombine_f32(zo, zeros); +} + +PX_FORCE_INLINE Vec4V V4UnitZ() +{ + const float32x2_t zeros = vreinterpret_f32_u32(vmov_n_u32(0)); + const float32x2_t ones = vmov_n_f32(1.0f); + const float32x2_t oz = vext_f32(ones, zeros, 1); + return vcombine_f32(zeros, oz); +} + +PX_FORCE_INLINE FloatV V4GetW(const Vec4V f) +{ + const float32x2_t fhigh = vget_high_f32(f); + return vdup_lane_f32(fhigh, 1); +} + +PX_FORCE_INLINE FloatV V4GetX(const Vec4V f) +{ + const float32x2_t fLow = vget_low_f32(f); + return vdup_lane_f32(fLow, 0); +} + +PX_FORCE_INLINE FloatV V4GetY(const Vec4V f) +{ + const float32x2_t fLow = vget_low_f32(f); + return vdup_lane_f32(fLow, 1); +} + +PX_FORCE_INLINE FloatV V4GetZ(const Vec4V f) +{ + const float32x2_t fhigh = vget_high_f32(f); + return vdup_lane_f32(fhigh, 0); +} + +PX_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTTF(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, vcombine_f32(f, f)); +} + +PX_FORCE_INLINE Vec4V V4ClearW(const Vec4V v) +{ + return V4Sel(BTTTF(), v, V4Zero()); +} + +PX_FORCE_INLINE Vec4V V4PermYXWZ(const Vec4V a) +{ + const float32x2_t xy = vget_low_f32(a); + const float32x2_t zw = vget_high_f32(a); + const float32x2_t yx = vext_f32(xy, xy, 1); + const float32x2_t wz = vext_f32(zw, zw, 1); + return vcombine_f32(yx, wz); +} + +PX_FORCE_INLINE Vec4V V4PermXZXZ(const Vec4V a) +{ + const float32x2_t xy = vget_low_f32(a); + const float32x2_t zw = vget_high_f32(a); + const float32x2x2_t xzyw = vzip_f32(xy, zw); + return vcombine_f32(xzyw.val[0], xzyw.val[0]); +} + +PX_FORCE_INLINE Vec4V V4PermYWYW(const Vec4V a) +{ + const float32x2_t xy = vget_low_f32(a); + const float32x2_t zw = vget_high_f32(a); + const float32x2x2_t xzyw = vzip_f32(xy, zw); + return vcombine_f32(xzyw.val[1], xzyw.val[1]); +} + +PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V a) +{ + const uint32x2_t xy = vget_low_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t zw = vget_high_u32(vreinterpretq_u32_f32(a)); + const uint32x2_t yz = vext_u32(xy, zw, 1); + const uint32x2_t xw = vrev64_u32(vext_u32(zw, xy, 1)); + return vreinterpretq_f32_u32(vcombine_u32(yz, xw)); +} + +PX_FORCE_INLINE Vec4V V4PermZWXY(const Vec4V a) +{ + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + return vcombine_f32(high, low); +} + +template +PX_FORCE_INLINE Vec4V V4Perm(const Vec4V V) +{ + static const uint32_t ControlElement[4] = + { +#if 1 + 0x03020100, // XM_SWIZZLE_X + 0x07060504, // XM_SWIZZLE_Y + 0x0B0A0908, // XM_SWIZZLE_Z + 0x0F0E0D0C, // XM_SWIZZLE_W +#else + 0x00010203, // XM_SWIZZLE_X + 0x04050607, // XM_SWIZZLE_Y + 0x08090A0B, // XM_SWIZZLE_Z + 0x0C0D0E0F, // XM_SWIZZLE_W +#endif + }; + + uint8x8x2_t tbl; + tbl.val[0] = vreinterpret_u8_f32(vget_low_f32(V)); + tbl.val[1] = vreinterpret_u8_f32(vget_high_f32(V)); + + uint8x8_t idx = + vcreate_u8(static_cast(ControlElement[E0]) | (static_cast(ControlElement[E1]) << 32)); + const uint8x8_t rL = vtbl2_u8(tbl, idx); + idx = vcreate_u8(static_cast(ControlElement[E2]) | (static_cast(ControlElement[E3]) << 32)); + const uint8x8_t rH = vtbl2_u8(tbl, idx); + return vreinterpretq_f32_u8(vcombine_u8(rL, rH)); +} + +// PT: this seems measurably slower than the hardcoded version +/*PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V a) +{ + return V4Perm<1, 2, 0, 3>(a); +}*/ + +PX_FORCE_INLINE Vec4V V4Zero() +{ + return vreinterpretq_f32_u32(vmovq_n_u32(0)); + // return vmovq_n_f32(0.0f); +} + +PX_FORCE_INLINE Vec4V V4One() +{ + return vmovq_n_f32(1.0f); +} + +PX_FORCE_INLINE Vec4V V4Eps() +{ + // return vmovq_n_f32(PX_EPS_REAL); + return V4Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec4V V4Neg(const Vec4V f) +{ + return vnegq_f32(f); +} + +PX_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b) +{ + return vaddq_f32(a, b); +} + +PX_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b) +{ + return vsubq_f32(a, b); +} + +PX_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b) +{ + return vmulq_lane_f32(a, b, 0); +} + +PX_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b) +{ + return vmulq_f32(a, b); +} + +PX_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + const float32x2_t invB = VRECIP(b); + return vmulq_lane_f32(a, invB, 0); +} + +PX_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b) +{ + const float32x4_t invB = VRECIPQ(b); + return vmulq_f32(a, invB); +} + +PX_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + const float32x2_t invB = VRECIPE(b); + return vmulq_lane_f32(a, invB, 0); +} + +PX_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b) +{ + const float32x4_t invB = VRECIPEQ(b); + return vmulq_f32(a, invB); +} + +PX_FORCE_INLINE Vec4V V4Recip(const Vec4V a) +{ + return VRECIPQ(a); +} + +PX_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a) +{ + return VRECIPEQ(a); +} + +PX_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a) +{ + return VRECIPSQRTQ(a); +} + +PX_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a) +{ + return VRECIPSQRTEQ(a); +} + +PX_FORCE_INLINE Vec4V V4Sqrt(const Vec4V a) +{ + return V4Sel(V4IsEq(a, V4Zero()), a, V4Mul(a, VRECIPSQRTQ(a))); +} + +PX_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return vmlaq_lane_f32(c, a, b, 0); +} + +PX_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return vmlsq_lane_f32(c, a, b, 0); +} + +PX_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return vmlaq_f32(c, a, b); +} + +PX_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return vmlsq_f32(c, a, b); +} + +PX_FORCE_INLINE Vec4V V4Abs(const Vec4V a) +{ + return vabsq_f32(a); +} + +PX_FORCE_INLINE FloatV V4SumElements(const Vec4V a) +{ + const Vec4V xy = V4UnpackXY(a, a); // x,x,y,y + const Vec4V zw = V4UnpackZW(a, a); // z,z,w,w + const Vec4V xz_yw = V4Add(xy, zw); // x+z,x+z,y+w,y+w + const FloatV xz = V4GetX(xz_yw); // x+z + const FloatV yw = V4GetZ(xz_yw); // y+w + return FAdd(xz, yw); // sum +} + +PX_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b) +{ + const float32x4_t tmp = vmulq_f32(a, b); + const float32x2_t low = vget_low_f32(tmp); + const float32x2_t high = vget_high_f32(tmp); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {z+w, x+y} + const float32x2_t sumWZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z+w, x+y+z+w} + return sumWZYX; +} + +PX_FORCE_INLINE FloatV V4Dot3(const Vec4V aa, const Vec4V bb) +{ + // PT: the V3Dot code relies on the fact that W=0 so we can't reuse it as-is, we need to clear W first. + // TODO: find a better implementation that does not need to clear W. + const Vec4V a = V4ClearW(aa); + const Vec4V b = V4ClearW(bb); + + const float32x4_t tmp = vmulq_f32(a, b); + const float32x2_t low = vget_low_f32(tmp); + const float32x2_t high = vget_high_f32(tmp); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {0+z, x+y} + const float32x2_t sum0ZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z, x+y+z} + return sum0ZYX; +} + +PX_FORCE_INLINE Vec4V V4Cross(const Vec4V a, const Vec4V b) +{ + const uint32x2_t TF = { 0xffffFFFF, 0x0 }; + const float32x2_t ay_ax = vget_low_f32(a); // d2 + const float32x2_t aw_az = vget_high_f32(a); // d3 + const float32x2_t by_bx = vget_low_f32(b); // d4 + const float32x2_t bw_bz = vget_high_f32(b); // d5 + // Hi, Lo + const float32x2_t bz_by = vext_f32(by_bx, bw_bz, 1); // bz, by + const float32x2_t az_ay = vext_f32(ay_ax, aw_az, 1); // az, ay + + const float32x2_t azbx = vmul_f32(aw_az, by_bx); // 0, az*bx + const float32x2_t aybz_axby = vmul_f32(ay_ax, bz_by); // ay*bz, ax*by + + const float32x2_t azbxSUBaxbz = vmls_f32(azbx, bw_bz, ay_ax); // 0, az*bx-ax*bz + const float32x2_t aybzSUBazby_axbySUBaybx = vmls_f32(aybz_axby, by_bx, az_ay); // ay*bz-az*by, ax*by-ay*bx + + const float32x2_t retLow = vext_f32(aybzSUBazby_axbySUBaybx, azbxSUBaxbz, 1); // az*bx-ax*bz, ay*bz-az*by + const uint32x2_t retHigh = vand_u32(TF, vreinterpret_u32_f32(aybzSUBazby_axbySUBaybx)); // 0, ax*by-ay*bx + + return vcombine_f32(retLow, vreinterpret_f32_u32(retHigh)); +} + +PX_FORCE_INLINE FloatV V4Length(const Vec4V a) +{ + const float32x4_t tmp = vmulq_f32(a, a); + const float32x2_t low = vget_low_f32(tmp); + const float32x2_t high = vget_high_f32(tmp); + + const float32x2_t sumTmp = vpadd_f32(low, high); // = {0+z, x+y} + const float32x2_t sumWZYX = vpadd_f32(sumTmp, sumTmp); // = {x+y+z, x+y+z} + return FSqrt(sumWZYX); +} + +PX_FORCE_INLINE FloatV V4LengthSq(const Vec4V a) +{ + return V4Dot(a, a); +} + +PX_FORCE_INLINE Vec4V V4Normalize(const Vec4V a) +{ + //PX_ASSERT(!FAllEq(V4LengthSq(a), FZero())); + return V4ScaleInv(a, V4Length(a)); +} + +PX_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a) +{ + //PX_ASSERT(!FAllEq(V4LengthSq(a), FZero())); + return V4Scale(a, FRsqrtFast(V4Dot(a, a))); +} + +PX_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a, const Vec4V unsafeReturnValue) +{ + const FloatV zero = FZero(); + const FloatV length = V4Length(a); + const uint32x4_t isGreaterThanZero = FIsGrtr(length, zero); + return V4Sel(isGreaterThanZero, V4ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b) +{ + return vceqq_u32(a, b); +} + +PX_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b) +{ + return vbslq_f32(c, a, b); +} + +PX_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b) +{ + return vcgtq_f32(a, b); +} + +PX_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return vcgeq_f32(a, b); +} + +PX_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b) +{ + return vceqq_f32(a, b); +} + +PX_FORCE_INLINE Vec4V V4Max(const Vec4V a, const Vec4V b) +{ + return vmaxq_f32(a, b); +} + +PX_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b) +{ + return vminq_f32(a, b); +} + +PX_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a) +{ + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + + const float32x2_t max0 = vpmax_f32(high, low); + const float32x2_t max1 = vpmax_f32(max0, max0); + + return max1; +} + +PX_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a) +{ + const float32x2_t low = vget_low_f32(a); + const float32x2_t high = vget_high_f32(a); + + const float32x2_t min0 = vpmin_f32(high, low); + const float32x2_t min1 = vpmin_f32(min0, min0); + + return min1; +} + +PX_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV) +{ + return V4Max(V4Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V4AllGrtr(const Vec4V a, const Vec4V b) +{ + return internalUnitNeonSimd::BAllTrue4_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return internalUnitNeonSimd::BAllTrue4_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq3(const Vec4V a, const Vec4V b) +{ + return internalUnitNeonSimd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllEq(const Vec4V a, const Vec4V b) +{ + return internalUnitNeonSimd::BAllTrue4_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AnyGrtr3(const Vec4V a, const Vec4V b) +{ + return internalUnitNeonSimd::BAnyTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE Vec4V V4Round(const Vec4V a) +{ + // truncate(a + (0.5f - sign(a))) + const Vec4V half = V4Load(0.5f); + const float32x4_t sign = vcvtq_f32_u32((vshrq_n_u32(vreinterpretq_u32_f32(a), 31))); + const Vec4V aPlusHalf = V4Add(a, half); + const Vec4V aRound = V4Sub(aPlusHalf, sign); + return vcvtq_f32_s32(vcvtq_s32_f32(aRound)); +} + +PX_FORCE_INLINE Vec4V V4Sin(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V3 = V4Mul(V2, V1); + const Vec4V V5 = V4Mul(V3, V2); + const Vec4V V7 = V4Mul(V5, V2); + const Vec4V V9 = V4Mul(V7, V2); + const Vec4V V11 = V4Mul(V9, V2); + const Vec4V V13 = V4Mul(V11, V2); + const Vec4V V15 = V4Mul(V13, V2); + const Vec4V V17 = V4Mul(V15, V2); + const Vec4V V19 = V4Mul(V17, V2); + const Vec4V V21 = V4Mul(V19, V2); + const Vec4V V23 = V4Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec4V Result; + Result = V4ScaleAdd(V3, S1, V1); + Result = V4ScaleAdd(V5, S2, Result); + Result = V4ScaleAdd(V7, S3, Result); + Result = V4ScaleAdd(V9, S4, Result); + Result = V4ScaleAdd(V11, S5, Result); + Result = V4ScaleAdd(V13, S6, Result); + Result = V4ScaleAdd(V15, S7, Result); + Result = V4ScaleAdd(V17, S8, Result); + Result = V4ScaleAdd(V19, S9, Result); + Result = V4ScaleAdd(V21, S10, Result); + Result = V4ScaleAdd(V23, S11, Result); + + return Result; +} + +PX_FORCE_INLINE Vec4V V4Cos(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V4 = V4Mul(V2, V2); + const Vec4V V6 = V4Mul(V4, V2); + const Vec4V V8 = V4Mul(V4, V4); + const Vec4V V10 = V4Mul(V6, V4); + const Vec4V V12 = V4Mul(V6, V6); + const Vec4V V14 = V4Mul(V8, V6); + const Vec4V V16 = V4Mul(V8, V8); + const Vec4V V18 = V4Mul(V10, V8); + const Vec4V V20 = V4Mul(V10, V10); + const Vec4V V22 = V4Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec4V Result; + Result = V4ScaleAdd(V2, C1, V4One()); + Result = V4ScaleAdd(V4, C2, Result); + Result = V4ScaleAdd(V6, C3, Result); + Result = V4ScaleAdd(V8, C4, Result); + Result = V4ScaleAdd(V10, C5, Result); + Result = V4ScaleAdd(V12, C6, Result); + Result = V4ScaleAdd(V14, C7, Result); + Result = V4ScaleAdd(V16, C8, Result); + Result = V4ScaleAdd(V18, C9, Result); + Result = V4ScaleAdd(V20, C10, Result); + Result = V4ScaleAdd(V22, C11, Result); + + return Result; +} + +PX_FORCE_INLINE void V4Transpose(Vec4V& col0, Vec4V& col1, Vec4V& col2, Vec4V& col3) +{ + const float32x4x2_t v0v1 = vzipq_f32(col0, col2); + const float32x4x2_t v2v3 = vzipq_f32(col1, col3); + const float32x4x2_t zip0 = vzipq_f32(v0v1.val[0], v2v3.val[0]); + const float32x4x2_t zip1 = vzipq_f32(v0v1.val[1], v2v3.val[1]); + col0 = zip0.val[0]; + col1 = zip0.val[1]; + col2 = zip1.val[0]; + col3 = zip1.val[1]; +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +PX_FORCE_INLINE BoolV BFFFF() +{ + return vmovq_n_u32(0); +} + +PX_FORCE_INLINE BoolV BFFFT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + return vcombine_u32(zeros, zo); +} + +PX_FORCE_INLINE BoolV BFFTF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(zeros, oz); +} + +PX_FORCE_INLINE BoolV BFFTT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + return vcombine_u32(zeros, ones); +} + +PX_FORCE_INLINE BoolV BFTFF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + return vcombine_u32(zo, zeros); +} + +PX_FORCE_INLINE BoolV BFTFT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + return vcombine_u32(zo, zo); +} + +PX_FORCE_INLINE BoolV BFTTF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(zo, oz); +} + +PX_FORCE_INLINE BoolV BFTTT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + return vcombine_u32(zo, ones); +} + +PX_FORCE_INLINE BoolV BTFFF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + // const uint32x2_t zo = vext_u32(zeros, ones, 1); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(oz, zeros); +} + +PX_FORCE_INLINE BoolV BTFFT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(oz, zo); +} + +PX_FORCE_INLINE BoolV BTFTF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(oz, oz); +} + +PX_FORCE_INLINE BoolV BTFTT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(oz, ones); +} + +PX_FORCE_INLINE BoolV BTTFF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + return vcombine_u32(ones, zeros); +} + +PX_FORCE_INLINE BoolV BTTFT() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t zo = vext_u32(zeros, ones, 1); + return vcombine_u32(ones, zo); +} + +PX_FORCE_INLINE BoolV BTTTF() +{ + const uint32x2_t zeros = vmov_n_u32(0); + const uint32x2_t ones = vmov_n_u32(0xffffFFFF); + const uint32x2_t oz = vext_u32(ones, zeros, 1); + return vcombine_u32(ones, oz); +} + +PX_FORCE_INLINE BoolV BTTTT() +{ + return vmovq_n_u32(0xffffFFFF); +} + +PX_FORCE_INLINE BoolV BXMask() +{ + return BTFFF(); +} + +PX_FORCE_INLINE BoolV BYMask() +{ + return BFTFF(); +} + +PX_FORCE_INLINE BoolV BZMask() +{ + return BFFTF(); +} + +PX_FORCE_INLINE BoolV BWMask() +{ + return BFFFT(); +} + +PX_FORCE_INLINE BoolV BGetX(const BoolV f) +{ + const uint32x2_t fLow = vget_low_u32(f); + return vdupq_lane_u32(fLow, 0); +} + +PX_FORCE_INLINE BoolV BGetY(const BoolV f) +{ + const uint32x2_t fLow = vget_low_u32(f); + return vdupq_lane_u32(fLow, 1); +} + +PX_FORCE_INLINE BoolV BGetZ(const BoolV f) +{ + const uint32x2_t fHigh = vget_high_u32(f); + return vdupq_lane_u32(fHigh, 0); +} + +PX_FORCE_INLINE BoolV BGetW(const BoolV f) +{ + const uint32x2_t fHigh = vget_high_u32(f); + return vdupq_lane_u32(fHigh, 1); +} + +PX_FORCE_INLINE BoolV BSetX(const BoolV v, const BoolV f) +{ + return vbslq_u32(BFTTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetY(const BoolV v, const BoolV f) +{ + return vbslq_u32(BTFTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetZ(const BoolV v, const BoolV f) +{ + return vbslq_u32(BTTFT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetW(const BoolV v, const BoolV f) +{ + return vbslq_u32(BTTTF(), v, f); +} + +PX_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b) +{ + return vandq_u32(a, b); +} + +PX_FORCE_INLINE BoolV BNot(const BoolV a) +{ + return vmvnq_u32(a); +} + +PX_FORCE_INLINE BoolV BAndNot(const BoolV a, const BoolV b) +{ + // return vbicq_u32(a, b); + return vandq_u32(a, vmvnq_u32(b)); +} + +PX_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b) +{ + return vorrq_u32(a, b); +} + +PX_FORCE_INLINE BoolV BAllTrue4(const BoolV a) +{ + const uint32x2_t allTrue = vmov_n_u32(0xffffFFFF); + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + const uint32x2_t result = vceq_u32(finalReduce, allTrue); + return vdupq_lane_u32(result, 0); +} + +PX_FORCE_INLINE BoolV BAnyTrue4(const BoolV a) +{ + const uint32x2_t allTrue = vmov_n_u32(0xffffFFFF); + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + const uint32x2_t result = vtst_u32(finalReduce, allTrue); + return vdupq_lane_u32(result, 0); +} + +PX_FORCE_INLINE BoolV BAllTrue3(const BoolV a) +{ + const uint32x2_t allTrue3 = vmov_n_u32(0x00ffFFFF); + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + const uint32x2_t result = vceq_u32(vand_u32(finalReduce, allTrue3), allTrue3); + return vdupq_lane_u32(result, 0); +} + +PX_FORCE_INLINE BoolV BAnyTrue3(const BoolV a) +{ + const uint32x2_t allTrue3 = vmov_n_u32(0x00ffFFFF); + const uint16x4_t dHigh = vget_high_u16(vreinterpretq_u16_u32(a)); + const uint16x4_t dLow = vmovn_u32(a); + uint16x8_t combined = vcombine_u16(dLow, dHigh); + const uint32x2_t finalReduce = vreinterpret_u32_u8(vmovn_u16(combined)); + const uint32x2_t result = vtst_u32(vand_u32(finalReduce, allTrue3), allTrue3); + return vdupq_lane_u32(result, 0); +} + +PX_FORCE_INLINE PxU32 BAllEq(const BoolV a, const BoolV b) +{ + const BoolV bTest = vceqq_u32(a, b); + return internalUnitNeonSimd::BAllTrue4_R(bTest); +} + +PX_FORCE_INLINE PxU32 BAllEqTTTT(const BoolV a) +{ + return BAllEq(a, BTTTT()); +} + +PX_FORCE_INLINE PxU32 BAllEqFFFF(const BoolV a) +{ + return BAllEq(a, BFFFF()); +} + +PX_FORCE_INLINE PxU32 BGetBitMask(const BoolV a) +{ + static PX_ALIGN(16, const PxU32) bitMaskData[4] = { 1, 2, 4, 8 }; + const uint32x4_t bitMask = *(reinterpret_cast(bitMaskData)); + const uint32x4_t t0 = vandq_u32(a, bitMask); + const uint32x2_t t1 = vpadd_u32(vget_low_u32(t0), vget_high_u32(t0)); // Pairwise add (0 + 1), (2 + 3) + return PxU32(vget_lane_u32(vpadd_u32(t1, t1), 0)); +} + +////////////////////////////////// +// MAT33V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M33MulV3(const Mat33V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V& a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +PX_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V& A, const Vec3V b, const Vec3V c) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + Vec3V result = V3ScaleAdd(A.col0, x, c); + result = V3ScaleAdd(A.col1, y, result); + return V3ScaleAdd(A.col2, z, result); +} + +PX_FORCE_INLINE Mat33V M33MulM33(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(M33MulV3(a, b.col0), M33MulV3(a, b.col1), M33MulV3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Add(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Scale(const Mat33V& a, const FloatV& b) +{ + return Mat33V(V3Scale(a.col0, b), V3Scale(a.col1, b), V3Scale(a.col2, b)); +} + +PX_FORCE_INLINE Mat33V M33Inverse(const Mat33V& a) +{ + const float32x2_t zeros = vreinterpret_f32_u32(vmov_n_u32(0)); + const BoolV btttf = BTTTF(); + + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = FRecipFast(dot); + + const float32x4x2_t merge = vzipq_f32(cross12, cross01); + const float32x4_t mergeh = merge.val[0]; + const float32x4_t mergel = merge.val[1]; + + // const Vec3V colInv0 = XMVectorPermute(mergeh,cross20,PxPermuteControl(0,4,1,7)); + const float32x4_t colInv0_xxyy = vzipq_f32(mergeh, cross20).val[0]; + const float32x4_t colInv0 = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(colInv0_xxyy), btttf)); + + // const Vec3V colInv1 = XMVectorPermute(mergeh,cross20,PxPermuteControl(2,5,3,7)); + const float32x2_t zw0 = vget_high_f32(mergeh); + const float32x2_t xy1 = vget_low_f32(cross20); + const float32x2_t yzero1 = vext_f32(xy1, zeros, 1); + const float32x2x2_t merge1 = vzip_f32(zw0, yzero1); + const float32x4_t colInv1 = vcombine_f32(merge1.val[0], merge1.val[1]); + + // const Vec3V colInv2 = XMVectorPermute(mergel,cross20,PxPermuteControl(0,6,1,7)); + const float32x2_t x0y0 = vget_low_f32(mergel); + const float32x2_t z1w1 = vget_high_f32(cross20); + const float32x2x2_t merge2 = vzip_f32(x0y0, z1w1); + const float32x4_t colInv2 = vcombine_f32(merge2.val[0], merge2.val[1]); + + return Mat33V(vmulq_lane_f32(colInv0, invDet, 0), vmulq_lane_f32(colInv1, invDet, 0), + vmulq_lane_f32(colInv2, invDet, 0)); +} + +PX_FORCE_INLINE Mat33V M33Trnsps(const Mat33V& a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +PX_FORCE_INLINE Mat33V M33Identity() +{ + return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ()); +} + +PX_FORCE_INLINE Mat33V M33Sub(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Sub(a.col0, b.col0), V3Sub(a.col1, b.col1), V3Sub(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Neg(const Mat33V& a) +{ + return Mat33V(V3Neg(a.col0), V3Neg(a.col1), V3Neg(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Abs(const Mat33V& a) +{ + return Mat33V(V3Abs(a.col0), V3Abs(a.col1), V3Abs(a.col2)); +} + +PX_FORCE_INLINE Mat33V PromoteVec3V(const Vec3V v) +{ + const BoolV bTFFF = BTFFF(); + const BoolV bFTFF = BFTFF(); + const BoolV bFFTF = BTFTF(); + + const Vec3V zero = V3Zero(); + + return Mat33V(V3Sel(bTFFF, v, zero), V3Sel(bFTFF, v, zero), V3Sel(bFFTF, v, zero)); +} + +PX_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg d) +{ + const Vec3V x = V3Mul(V3UnitX(), d); + const Vec3V y = V3Mul(V3UnitY(), d); + const Vec3V z = V3Mul(V3UnitZ(), d); + return Mat33V(x, y, z); +} + +////////////////////////////////// +// MAT34V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M34MulV3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + const Vec3V v0PlusV1Plusv2 = V3Add(v0PlusV1, v2); + return V3Add(v0PlusV1Plusv2, a.col3); +} + +PX_FORCE_INLINE Vec3V M34Mul33V3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M34TrnspsMul33V3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +PX_FORCE_INLINE Mat34V M34MulM34(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2), M34MulV3(a, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34MulM33(const Mat34V& a, const Mat33V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M34Mul33MM34(const Mat34V& a, const Mat34V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat34V M34Add(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2), V3Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34Trnsps33(const Mat34V& a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +////////////////////////////////// +// MAT44V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V M44MulV4(const Mat44V& a, const Vec4V b) +{ + const FloatV x = V4GetX(b); + const FloatV y = V4GetY(b); + const FloatV z = V4GetZ(b); + const FloatV w = V4GetW(b); + + const Vec4V v0 = V4Scale(a.col0, x); + const Vec4V v1 = V4Scale(a.col1, y); + const Vec4V v2 = V4Scale(a.col2, z); + const Vec4V v3 = V4Scale(a.col3, w); + const Vec4V v0PlusV1 = V4Add(v0, v1); + const Vec4V v0PlusV1Plusv2 = V4Add(v0PlusV1, v2); + return V4Add(v0PlusV1Plusv2, v3); +} + +PX_FORCE_INLINE Vec4V M44TrnspsMulV4(const Mat44V& a, const Vec4V b) +{ + return V4Merge(V4Dot(a.col0, b), V4Dot(a.col1, b), V4Dot(a.col2, b), V4Dot(a.col3, b)); +} + +PX_FORCE_INLINE Mat44V M44MulM44(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(M44MulV4(a, b.col0), M44MulV4(a, b.col1), M44MulV4(a, b.col2), M44MulV4(a, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Add(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(V4Add(a.col0, b.col0), V4Add(a.col1, b.col1), V4Add(a.col2, b.col2), V4Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Trnsps(const Mat44V& a) +{ + // asm volatile( + // "vzip.f32 %q0, %q2 \n\t" + // "vzip.f32 %q1, %q3 \n\t" + // "vzip.f32 %q0, %q1 \n\t" + // "vzip.f32 %q2, %q3 \n\t" + // : "+w" (a.col0), "+w" (a.col1), "+w" (a.col2), "+w" a.col3)); + + const float32x4x2_t v0v1 = vzipq_f32(a.col0, a.col2); + const float32x4x2_t v2v3 = vzipq_f32(a.col1, a.col3); + const float32x4x2_t zip0 = vzipq_f32(v0v1.val[0], v2v3.val[0]); + const float32x4x2_t zip1 = vzipq_f32(v0v1.val[1], v2v3.val[1]); + + return Mat44V(zip0.val[0], zip0.val[1], zip1.val[0], zip1.val[1]); +} + +PX_FORCE_INLINE Mat44V M44Inverse(const Mat44V& a) +{ + float32x4_t minor0, minor1, minor2, minor3; + float32x4_t row0, row1, row2, row3; + float32x4_t det, tmp1; + + tmp1 = vmovq_n_f32(0.0f); + row1 = vmovq_n_f32(0.0f); + row3 = vmovq_n_f32(0.0f); + + row0 = a.col0; + row1 = vextq_f32(a.col1, a.col1, 2); + row2 = a.col2; + row3 = vextq_f32(a.col3, a.col3, 2); + + tmp1 = vmulq_f32(row2, row3); + tmp1 = vrev64q_f32(tmp1); + minor0 = vmulq_f32(row1, tmp1); + minor1 = vmulq_f32(row0, tmp1); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor0 = vsubq_f32(vmulq_f32(row1, tmp1), minor0); + minor1 = vsubq_f32(vmulq_f32(row0, tmp1), minor1); + minor1 = vextq_f32(minor1, minor1, 2); + + tmp1 = vmulq_f32(row1, row2); + tmp1 = vrev64q_f32(tmp1); + minor0 = vaddq_f32(vmulq_f32(row3, tmp1), minor0); + minor3 = vmulq_f32(row0, tmp1); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor0 = vsubq_f32(minor0, vmulq_f32(row3, tmp1)); + minor3 = vsubq_f32(vmulq_f32(row0, tmp1), minor3); + minor3 = vextq_f32(minor3, minor3, 2); + + tmp1 = vmulq_f32(vextq_f32(row1, row1, 2), row3); + tmp1 = vrev64q_f32(tmp1); + row2 = vextq_f32(row2, row2, 2); + minor0 = vaddq_f32(vmulq_f32(row2, tmp1), minor0); + minor2 = vmulq_f32(row0, tmp1); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor0 = vsubq_f32(minor0, vmulq_f32(row2, tmp1)); + minor2 = vsubq_f32(vmulq_f32(row0, tmp1), minor2); + minor2 = vextq_f32(minor2, minor2, 2); + + tmp1 = vmulq_f32(row0, row1); + tmp1 = vrev64q_f32(tmp1); + minor2 = vaddq_f32(vmulq_f32(row3, tmp1), minor2); + minor3 = vsubq_f32(vmulq_f32(row2, tmp1), minor3); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor2 = vsubq_f32(vmulq_f32(row3, tmp1), minor2); + minor3 = vsubq_f32(minor3, vmulq_f32(row2, tmp1)); + + tmp1 = vmulq_f32(row0, row3); + tmp1 = vrev64q_f32(tmp1); + minor1 = vsubq_f32(minor1, vmulq_f32(row2, tmp1)); + minor2 = vaddq_f32(vmulq_f32(row1, tmp1), minor2); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor1 = vaddq_f32(vmulq_f32(row2, tmp1), minor1); + minor2 = vsubq_f32(minor2, vmulq_f32(row1, tmp1)); + + tmp1 = vmulq_f32(row0, row2); + tmp1 = vrev64q_f32(tmp1); + minor1 = vaddq_f32(vmulq_f32(row3, tmp1), minor1); + minor3 = vsubq_f32(minor3, vmulq_f32(row1, tmp1)); + tmp1 = vextq_f32(tmp1, tmp1, 2); + minor1 = vsubq_f32(minor1, vmulq_f32(row3, tmp1)); + minor3 = vaddq_f32(vmulq_f32(row1, tmp1), minor3); + + det = vmulq_f32(row0, minor0); + det = vaddq_f32(vextq_f32(det, det, 2), det); + det = vaddq_f32(vrev64q_f32(det), det); + det = vdupq_lane_f32(VRECIPE(vget_low_f32(det)), 0); + + minor0 = vmulq_f32(det, minor0); + minor1 = vmulq_f32(det, minor1); + minor2 = vmulq_f32(det, minor2); + minor3 = vmulq_f32(det, minor3); + Mat44V invTrans(minor0, minor1, minor2, minor3); + return M44Trnsps(invTrans); +} + +PX_FORCE_INLINE Vec4V V4LoadXYZW(const PxF32& x, const PxF32& y, const PxF32& z, const PxF32& w) +{ + const float32x4_t ret = { x, y, z, w }; + return ret; +} + +/* +PX_FORCE_INLINE VecU16V V4U32PK(VecU32V a, VecU32V b) +{ + return vcombine_u16(vqmovn_u32(a), vqmovn_u32(b)); +} +*/ + +PX_FORCE_INLINE VecU32V V4U32Sel(const BoolV c, const VecU32V a, const VecU32V b) +{ + return vbslq_u32(c, a, b); +} + +PX_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b) +{ + return vorrq_u32(a, b); +} + +PX_FORCE_INLINE VecU32V V4U32xor(VecU32V a, VecU32V b) +{ + return veorq_u32(a, b); +} + +PX_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b) +{ + return vandq_u32(a, b); +} + +PX_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b) +{ + // return vbicq_u32(a, b); // creates gcc compiler bug in RTreeQueries.cpp + return vandq_u32(a, vmvnq_u32(b)); +} + +/* +PX_FORCE_INLINE VecU16V V4U16Or(VecU16V a, VecU16V b) +{ + return vorrq_u16(a, b); +} +*/ + +/* +PX_FORCE_INLINE VecU16V V4U16And(VecU16V a, VecU16V b) +{ + return vandq_u16(a, b); +} +*/ +/* +PX_FORCE_INLINE VecU16V V4U16Andc(VecU16V a, VecU16V b) +{ + return vbicq_u16(a, b); +} +*/ + +PX_FORCE_INLINE VecI32V I4Load(const PxI32 i) +{ + return vdupq_n_s32(i); +} + +PX_FORCE_INLINE VecI32V I4LoadU(const PxI32* i) +{ + return vld1q_s32(i); +} + +PX_FORCE_INLINE VecI32V I4LoadA(const PxI32* i) +{ + return vld1q_s32(i); +} + +PX_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b) +{ + return vaddq_s32(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b) +{ + return vsubq_s32(a, b); +} + +PX_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b) +{ + return vcgtq_s32(a, b); +} + +PX_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b) +{ + return vceqq_s32(a, b); +} + +PX_FORCE_INLINE VecI32V V4I32Sel(const BoolV c, const VecI32V a, const VecI32V b) +{ + return vbslq_s32(c, a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Zero() +{ + return vdupq_n_s32(0); +} + +PX_FORCE_INLINE VecI32V VecI32V_One() +{ + return vdupq_n_s32(1); +} + +PX_FORCE_INLINE VecI32V VecI32V_Two() +{ + return vdupq_n_s32(2); +} + +PX_FORCE_INLINE VecI32V VecI32V_MinusOne() +{ + return vdupq_n_s32(-1); +} + +PX_FORCE_INLINE VecU32V U4Zero() +{ + return U4Load(0); +} + +PX_FORCE_INLINE VecU32V U4One() +{ + return U4Load(1); +} + +PX_FORCE_INLINE VecU32V U4Two() +{ + return U4Load(2); +} + +PX_FORCE_INLINE VecShiftV VecI32V_PrepareShift(const VecI32VArg shift) +{ + return shift; +} + +PX_FORCE_INLINE VecI32V VecI32V_LeftShift(const VecI32VArg a, const VecShiftVArg count) +{ + return vshlq_s32(a, count); +} + +PX_FORCE_INLINE VecI32V VecI32V_RightShift(const VecI32VArg a, const VecShiftVArg count) +{ + return vshlq_s32(a, VecI32V_Sub(I4Load(0), count)); +} + +PX_FORCE_INLINE VecI32V VecI32V_And(const VecI32VArg a, const VecI32VArg b) +{ + return vandq_s32(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Or(const VecI32VArg a, const VecI32VArg b) +{ + return vorrq_s32(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetX(const VecI32VArg f) +{ + const int32x2_t fLow = vget_low_s32(f); + return vdupq_lane_s32(fLow, 0); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetY(const VecI32VArg f) +{ + const int32x2_t fLow = vget_low_s32(f); + return vdupq_lane_s32(fLow, 1); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetZ(const VecI32VArg f) +{ + const int32x2_t fHigh = vget_high_s32(f); + return vdupq_lane_s32(fHigh, 0); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetW(const VecI32VArg f) +{ + const int32x2_t fHigh = vget_high_s32(f); + return vdupq_lane_s32(fHigh, 1); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sel(const BoolV c, const VecI32VArg a, const VecI32VArg b) +{ + return vbslq_s32(c, a, b); +} + +PX_FORCE_INLINE void PxI32_From_VecI32V(const VecI32VArg a, PxI32* i) +{ + *i = vgetq_lane_s32(a, 0); +} + +PX_FORCE_INLINE VecI32V VecI32V_Merge(const VecI32VArg a, const VecI32VArg b, const VecI32VArg c, const VecI32VArg d) +{ + const int32x2_t aLow = vget_low_s32(a); + const int32x2_t bLow = vget_low_s32(b); + const int32x2_t cLow = vget_low_s32(c); + const int32x2_t dLow = vget_low_s32(d); + + const int32x2_t low = vext_s32(aLow, bLow, 1); + const int32x2_t high = vext_s32(cLow, dLow, 1); + + return vcombine_s32(low, high); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_BoolV(const BoolVArg a) +{ + return vreinterpretq_s32_u32(a); +} + +PX_FORCE_INLINE VecU32V VecU32V_From_BoolV(const BoolVArg a) +{ + return a; +} + +/* +template PX_FORCE_INLINE VecI32V V4ISplat() +{ + return vdupq_n_s32(a); +} + +template PX_FORCE_INLINE VecU32V V4USplat() +{ + return vdupq_n_u32(a); +} +*/ + +/* +PX_FORCE_INLINE void V4U16StoreAligned(VecU16V val, VecU16V* address) +{ + vst1q_u16((uint16_t*)address, val); +} +*/ + +PX_FORCE_INLINE void V4U32StoreAligned(VecU32V val, VecU32V* address) +{ + vst1q_u32(reinterpret_cast(address), val); +} + +PX_FORCE_INLINE Vec4V V4LoadAligned(Vec4V* addr) +{ + return vld1q_f32(reinterpret_cast(addr)); +} + +PX_FORCE_INLINE Vec4V V4LoadUnaligned(Vec4V* addr) +{ + return vld1q_f32(reinterpret_cast(addr)); +} + +PX_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b) +{ + return vreinterpretq_f32_u32(V4U32Andc(vreinterpretq_u32_f32(a), b)); +} + +PX_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b) +{ + return V4IsGrtr(a, b); +} + +PX_FORCE_INLINE VecU16V V4U16LoadAligned(VecU16V* addr) +{ + return vld1q_u16(reinterpret_cast(addr)); +} + +PX_FORCE_INLINE VecU16V V4U16LoadUnaligned(VecU16V* addr) +{ + return vld1q_u16(reinterpret_cast(addr)); +} + +PX_FORCE_INLINE VecU16V V4U16CompareGt(VecU16V a, VecU16V b) +{ + return vcgtq_u16(a, b); +} + +PX_FORCE_INLINE VecU16V V4I16CompareGt(VecI16V a, VecI16V b) +{ + return vcgtq_s16(a, b); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a) +{ + return vcvtq_f32_u32(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecI32V(VecI32V a) +{ + return vcvtq_f32_s32(a); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_Vec4V(Vec4V a) +{ + return vcvtq_s32_f32(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecU32V(VecU32V a) +{ + return vreinterpretq_f32_u32(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecI32V(VecI32V a) +{ + return vreinterpretq_f32_s32(a); +} + +PX_FORCE_INLINE VecU32V VecU32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return vreinterpretq_u32_f32(a); +} + +PX_FORCE_INLINE VecI32V VecI32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return vreinterpretq_s32_f32(a); +} + +template +PX_FORCE_INLINE BoolV BSplatElement(BoolV a) +{ + if(index < 2) + { + return vdupq_lane_u32(vget_low_u32(a), index); + } + else if(index == 2) + { + return vdupq_lane_u32(vget_high_u32(a), 0); + } + else if(index == 3) + { + return vdupq_lane_u32(vget_high_u32(a), 1); + } +} + +template +PX_FORCE_INLINE VecU32V V4U32SplatElement(VecU32V a) +{ + if(index < 2) + { + return vdupq_lane_u32(vget_low_u32(a), index); + } + else if(index == 2) + { + return vdupq_lane_u32(vget_high_u32(a), 0); + } + else if(index == 3) + { + return vdupq_lane_u32(vget_high_u32(a), 1); + } +} + +template +PX_FORCE_INLINE Vec4V V4SplatElement(Vec4V a) +{ + if(index < 2) + { + return vdupq_lane_f32(vget_low_f32(a), index); + } + else if(index == 2) + { + return vdupq_lane_f32(vget_high_f32(a), 0); + } + else if(index == 3) + { + return vdupq_lane_f32(vget_high_f32(a), 1); + } +} + +PX_FORCE_INLINE VecU32V U4LoadXYZW(PxU32 x, PxU32 y, PxU32 z, PxU32 w) +{ + const uint32x4_t ret = { x, y, z, w }; + return ret; +} + +PX_FORCE_INLINE VecU32V U4Load(const PxU32 i) +{ + return vdupq_n_u32(i); +} + +PX_FORCE_INLINE VecU32V U4LoadU(const PxU32* i) +{ + return vld1q_u32(i); +} + +PX_FORCE_INLINE VecU32V U4LoadA(const PxU32* i) +{ + return vld1q_u32(i); +} + +PX_FORCE_INLINE Vec4V V4Ceil(const Vec4V in) +{ + const float32x4_t ones = vdupq_n_f32(1.0f); + const float32x4_t rdToZero = vcvtq_f32_s32(vcvtq_s32_f32(in)); + const float32x4_t rdToZeroPlusOne = vaddq_f32(rdToZero, ones); + const uint32x4_t gt = vcgtq_f32(in, rdToZero); + return vbslq_f32(gt, rdToZeroPlusOne, rdToZero); +} + +PX_FORCE_INLINE Vec4V V4Floor(const Vec4V in) +{ + const float32x4_t ones = vdupq_n_f32(1.0f); + const float32x4_t rdToZero = vcvtq_f32_s32(vcvtq_s32_f32(in)); + const float32x4_t rdToZeroMinusOne = vsubq_f32(rdToZero, ones); + const uint32x4_t lt = vcltq_f32(in, rdToZero); + return vbslq_f32(lt, rdToZeroMinusOne, rdToZero); +} + +PX_FORCE_INLINE VecU32V V4ConvertToU32VSaturate(const Vec4V in, PxU32 power) +{ + PX_ASSERT(power == 0 && "Non-zero power not supported in convertToU32VSaturate"); + PX_UNUSED(power); // prevent warning in release builds + + return vcvtq_u32_f32(in); +} + +PX_FORCE_INLINE void QuatGetMat33V(const QuatVArg q, Vec3V& column0, Vec3V& column1, Vec3V& column2) +{ + const FloatV one = FOne(); + const FloatV x = V4GetX(q); + const FloatV y = V4GetY(q); + const FloatV z = V4GetZ(q); + const FloatV w = V4GetW(q); + + const FloatV x2 = FAdd(x, x); + const FloatV y2 = FAdd(y, y); + const FloatV z2 = FAdd(z, z); + + const FloatV xx = FMul(x2, x); + const FloatV yy = FMul(y2, y); + const FloatV zz = FMul(z2, z); + + const FloatV xy = FMul(x2, y); + const FloatV xz = FMul(x2, z); + const FloatV xw = FMul(x2, w); + + const FloatV yz = FMul(y2, z); + const FloatV yw = FMul(y2, w); + const FloatV zw = FMul(z2, w); + + const FloatV v = FSub(one, xx); + + column0 = V3Merge(FSub(FSub(one, yy), zz), FAdd(xy, zw), FSub(xz, yw)); + column1 = V3Merge(FSub(xy, zw), FSub(v, zz), FAdd(yz, xw)); + column2 = V3Merge(FAdd(xz, yw), FSub(yz, xw), FSub(v, yy)); +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSUNIXNEONINLINEAOS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h new file mode 100644 index 000000000..102746281 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h @@ -0,0 +1,191 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXSSE2AOS_H +#define PSFOUNDATION_PSUNIXSSE2AOS_H + +// no includes here! this file should be included from PxcVecMath.h only!!! + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +#if PX_EMSCRIPTEN +typedef int8_t __int8_t; +typedef int16_t __int16_t; +typedef int32_t __int32_t; +typedef int64_t __int64_t; +typedef uint16_t __uint16_t; +typedef uint32_t __uint32_t; +typedef uint64_t __uint64_t; +#endif + +typedef union UnionM128 +{ + UnionM128() + { + } + UnionM128(__m128 in) + { + m128 = in; + } + + UnionM128(__m128i in) + { + m128i = in; + } + + operator __m128() + { + return m128; + } + + operator const __m128() const + { + return m128; + } + + float m128_f32[4]; + __int8_t m128_i8[16]; + __int16_t m128_i16[8]; + __int32_t m128_i32[4]; + __int64_t m128_i64[2]; + __uint16_t m128_u16[8]; + __uint32_t m128_u32[4]; + __uint64_t m128_u64[2]; + __m128 m128; + __m128i m128i; +} UnionM128; + +typedef __m128 FloatV; +typedef __m128 Vec3V; +typedef __m128 Vec4V; +typedef __m128 BoolV; +typedef __m128 QuatV; +typedef __m128i VecI32V; +typedef UnionM128 VecU32V; +typedef UnionM128 VecU16V; +typedef UnionM128 VecI16V; +typedef UnionM128 VecU8V; + +#define FloatVArg FloatV & +#define Vec3VArg Vec3V & +#define Vec4VArg Vec4V & +#define BoolVArg BoolV & +#define VecU32VArg VecU32V & +#define VecI32VArg VecI32V & +#define VecU16VArg VecU16V & +#define VecI16VArg VecI16V & +#define VecU8VArg VecU8V & +#define QuatVArg QuatV & + +// Optimization for situations in which you cross product multiple vectors with the same vector. +// Avoids 2X shuffles per product +struct VecCrossV +{ + Vec3V mL1; + Vec3V mR1; +}; + +struct VecShiftV +{ + VecI32V shift; +}; +#define VecShiftVArg VecShiftV & + +PX_ALIGN_PREFIX(16) +struct Mat33V +{ + Mat33V() + { + } + Mat33V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat34V +{ + Mat34V() + { + } + Mat34V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2, const Vec3V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); + Vec3V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat43V +{ + Mat43V() + { + } + Mat43V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat44V +{ + Mat44V() + { + } + Mat44V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2, const Vec4V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); + Vec4V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSUNIXSSE2AOS_H diff --git a/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h new file mode 100644 index 000000000..9899af581 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h @@ -0,0 +1,3239 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSUNIXSSE2INLINEAOS_H +#define PSFOUNDATION_PSUNIXSSE2INLINEAOS_H + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +#ifdef __SSE4_2__ +#include "smmintrin.h" +#endif + +#include "../../PsVecMathSSE.h" + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +#define PX_FPCLASS_SNAN 0x0001 /* signaling NaN */ +#define PX_FPCLASS_QNAN 0x0002 /* quiet NaN */ +#define PX_FPCLASS_NINF 0x0004 /* negative infinity */ +#define PX_FPCLASS_PINF 0x0200 /* positive infinity */ + +PX_FORCE_INLINE __m128 m128_I2F(__m128i n) +{ + return _mm_castsi128_ps(n); +} +PX_FORCE_INLINE __m128i m128_F2I(__m128 n) +{ + return _mm_castps_si128(n); +} + +////////////////////////////////////////////////////////////////////// +//Test that Vec3V and FloatV are legal +////////////////////////////////////////////////////////////////////// + +#define FLOAT_COMPONENTS_EQUAL_THRESHOLD 0.01f +PX_FORCE_INLINE static bool isValidFloatV(const FloatV a) +{ + const PxF32 x = V4ReadX(a); + const PxF32 y = V4ReadY(a); + const PxF32 z = V4ReadZ(a); + const PxF32 w = V4ReadW(a); + + if ( + (PxAbs(x - y) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs(x - z) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs(x - w) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + ) + { + return true; + } + + if ( + (PxAbs((x - y) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs((x - z) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs((x - w) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + ) + { + return true; + } + + return false; +} + +PX_FORCE_INLINE bool isValidVec3V(const Vec3V a) +{ + PX_ALIGN(16, PxF32 f[4]); + V4StoreA(a, f); + return (f[3] == 0.0f); +} + +PX_FORCE_INLINE bool isFiniteLength(const Vec3V a) +{ + return !FAllEq(V4LengthSq(a), FZero()); +} + +PX_FORCE_INLINE bool isAligned16(void* a) +{ + return(0 == (size_t(a) & 0x0f)); +} + +//ASSERT_FINITELENGTH is deactivated because there is a lot of code that calls a simd normalisation function with zero length but then ignores the result. + +#if PX_DEBUG +#define ASSERT_ISVALIDVEC3V(a) PX_ASSERT(isValidVec3V(a)) +#define ASSERT_ISVALIDFLOATV(a) PX_ASSERT(isValidFloatV(a)) +#define ASSERT_ISALIGNED16(a) PX_ASSERT(isAligned16(reinterpret_cast(a))) +#define ASSERT_ISFINITELENGTH(a) //PX_ASSERT(isFiniteLength(a)) +#else +#define ASSERT_ISVALIDVEC3V(a) +#define ASSERT_ISVALIDFLOATV(a) +#define ASSERT_ISALIGNED16(a) +#define ASSERT_ISFINITELENGTH(a) +#endif + + +namespace internalUnitSSE2Simd +{ +PX_FORCE_INLINE PxU32 BAllTrue4_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32(moveMask == 0xf); +} + +PX_FORCE_INLINE PxU32 BAllTrue3_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32((moveMask & 0x7) == 0x7); +} + +PX_FORCE_INLINE PxU32 BAnyTrue4_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32(moveMask != 0x0); +} + +PX_FORCE_INLINE PxU32 BAnyTrue3_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32((moveMask & 0x7) != 0x0); +} + +PX_FORCE_INLINE PxU32 FiniteTestEq(const Vec4V a, const Vec4V b) +{ + // This is a bit of a bodge. + //_mm_comieq_ss returns 1 if either value is nan so we need to re-cast a and b with true encoded as a non-nan + // number. + // There must be a better way of doing this in sse. + const BoolV one = FOne(); + const BoolV zero = FZero(); + const BoolV a1 = V4Sel(a, one, zero); + const BoolV b1 = V4Sel(b, one, zero); + return ( + _mm_comieq_ss(a1, b1) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(1, 1, 1, 1)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(1, 1, 1, 1))) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(2, 2, 2, 2)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(2, 2, 2, 2))) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(3, 3, 3, 3)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(3, 3, 3, 3)))); +} + +#if !PX_EMSCRIPTEN +const PX_ALIGN(16, PxF32 gMaskXYZ[4]) = { physx::PxUnionCast(0xffffffff), physx::PxUnionCast(0xffffffff), + physx::PxUnionCast(0xffffffff), 0 }; +} +#else +// emscripten doesn't like the PxUnionCast data structure +// the following is what windows and xbox does -- using these for emscripten +const PX_ALIGN(16, PxU32 gMaskXYZ[4]) = { 0xffffffff, 0xffffffff, 0xffffffff, 0 }; } +#endif + +namespace _VecMathTests +{ +// PT: this function returns an invalid Vec3V (W!=0.0f) just for unit-testing 'isValidVec3V' +PX_FORCE_INLINE Vec3V getInvalidVec3V() +{ + const float f = 1.0f; + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE bool allElementsEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_comieq_ss(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec3V(const Vec3V a, const Vec3V b) +{ + return V3AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec4V(const Vec4V a, const Vec4V b) +{ + return V4AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualBoolV(const BoolV a, const BoolV b) +{ + return internalUnitSSE2Simd::BAllTrue4_R(VecI32V_IsEq(m128_F2I(a), m128_F2I(b))) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVecU32V(const VecU32V a, const VecU32V b) +{ + return internalUnitSSE2Simd::BAllTrue4_R(V4IsEqU32(a, b)) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVecI32V(const VecI32V a, const VecI32V b) +{ + BoolV c = m128_I2F(_mm_cmpeq_epi32(a, b)); + return internalUnitSSE2Simd::BAllTrue4_R(c) != 0; +} + +#define VECMATH_AOS_EPSILON (1e-3f) + +PX_FORCE_INLINE bool allElementsNearEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + const FloatV c = FSub(a, b); + const FloatV minError = FLoad(-VECMATH_AOS_EPSILON); + const FloatV maxError = FLoad(VECMATH_AOS_EPSILON); + return _mm_comigt_ss(c, minError) && _mm_comilt_ss(c, maxError); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec3V(const Vec3V a, const Vec3V b) +{ + const Vec3V c = V3Sub(a, b); + const Vec3V minError = V3Load(-VECMATH_AOS_EPSILON); + const Vec3V maxError = V3Load(VECMATH_AOS_EPSILON); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxError) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxError) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxError)); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec4V(const Vec4V a, const Vec4V b) +{ + const Vec4V c = V4Sub(a, b); + const Vec4V minError = V4Load(-VECMATH_AOS_EPSILON); + const Vec4V maxError = V4Load(VECMATH_AOS_EPSILON); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxError) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxError) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxError) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), minError) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), maxError)); +} +} + +///////////////////////////////////////////////////////////////////// +////FUNCTIONS USED ONLY FOR ASSERTS IN VECTORISED IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE bool isFiniteFloatV(const FloatV a) +{ + PxF32 badNumber = + physx::PxUnionCast(PX_FPCLASS_SNAN | PX_FPCLASS_QNAN | PX_FPCLASS_NINF | PX_FPCLASS_PINF); + const FloatV vBadNum = FLoad(badNumber); + const BoolV vMask = BAnd(vBadNum, a); + return internalUnitSSE2Simd::FiniteTestEq(vMask, BFFFF()) == 1; +} + +PX_FORCE_INLINE bool isFiniteVec3V(const Vec3V a) +{ + PxF32 badNumber = + physx::PxUnionCast(PX_FPCLASS_SNAN | PX_FPCLASS_QNAN | PX_FPCLASS_NINF | PX_FPCLASS_PINF); + const Vec3V vBadNum = V3Load(badNumber); + const BoolV vMask = BAnd(BAnd(vBadNum, a), BTTTF()); + return internalUnitSSE2Simd::FiniteTestEq(vMask, BFFFF()) == 1; +} + +PX_FORCE_INLINE bool isFiniteVec4V(const Vec4V a) +{ + /*Vec4V a; + PX_ALIGN(16, PxF32 f[4]); + F32Array_Aligned_From_Vec4V(a, f); + return PxIsFinite(f[0]) + && PxIsFinite(f[1]) + && PxIsFinite(f[2]) + && PxIsFinite(f[3]);*/ + + PxF32 badNumber = + physx::PxUnionCast(PX_FPCLASS_SNAN | PX_FPCLASS_QNAN | PX_FPCLASS_NINF | PX_FPCLASS_PINF); + const Vec4V vBadNum = V4Load(badNumber); + const BoolV vMask = BAnd(vBadNum, a); + + return internalUnitSSE2Simd::FiniteTestEq(vMask, BFFFF()) == 1; +} + +PX_FORCE_INLINE bool hasZeroElementinFloatV(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) ? true : false; +} + +PX_FORCE_INLINE bool hasZeroElementInVec3V(const Vec3V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FZero())); +} + +PX_FORCE_INLINE bool hasZeroElementInVec4V(const Vec4V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)), FZero())); +} + +///////////////////////////////////////////////////////////////////// +////VECTORISED FUNCTION IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE FloatV FLoad(const PxF32 f) +{ + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE Vec3V V3Load(const PxF32 f) +{ + return _mm_set_ps(0.0f, f, f, f); +} + +PX_FORCE_INLINE Vec4V V4Load(const PxF32 f) +{ + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool f) +{ + const PxU32 i = -PxI32(f); + return _mm_load1_ps(reinterpret_cast(&i)); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(const_cast(&f)); +#if !PX_EMSCRIPTEN + return _mm_and_ps(reinterpret_cast(f), V4LoadA(internalUnitSSE2Simd::gMaskXYZ)); +#else + return _mm_and_ps((Vec3V&)f, (VecI32V&)internalUnitSSE2Simd::gMaskXYZ); +#endif +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxVec3& f) +{ + return _mm_set_ps(0.0f, f.z, f.y, f.x); +} + +PX_FORCE_INLINE Vec3V V3LoadUnsafeA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(const_cast(&f)); + return _mm_set_ps(0.0f, f.z, f.y, f.x); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(const_cast(f)); +#if !PX_EMSCRIPTEN + return _mm_and_ps(V4LoadA(f), V4LoadA(internalUnitSSE2Simd::gMaskXYZ)); +#else + return _mm_and_ps((Vec3V&)*f, (VecI32V&)internalUnitSSE2Simd::gMaskXYZ); +#endif +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxF32* const i) +{ + return _mm_set_ps(0.0f, i[2], i[1], i[0]); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V v) +{ + return V4ClearW(v); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V_WUndefined(const Vec4V v) +{ + return v; +} + +PX_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return f; // ok if it is implemented as the same type. +} + +PX_FORCE_INLINE Vec4V Vec4V_From_PxVec3_WUndefined(const PxVec3& f) +{ + return _mm_set_ps(0.0f, f.z, f.y, f.x); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_FloatV(FloatV f) +{ + return f; +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV(FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return Vec3V_From_Vec4V(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV_WUndefined(FloatV f) +{ + ASSERT_ISVALIDVEC3V(f); + return Vec3V_From_Vec4V_WUndefined(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Mat33V Mat33V_From_PxMat33(const PxMat33& m) +{ + return Mat33V(V3LoadU(m.column0), V3LoadU(m.column1), V3LoadU(m.column2)); +} + +PX_FORCE_INLINE void PxMat33_From_Mat33V(const Mat33V& m, PxMat33& out) +{ + V3StoreU(m.col0, out.column0); + V3StoreU(m.col1, out.column1); + V3StoreU(m.col2, out.column2); +} + +PX_FORCE_INLINE Vec4V V4LoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(const_cast(f)); + return _mm_load_ps(f); +} + +PX_FORCE_INLINE void V4StoreA(Vec4V a, PxF32* f) +{ + ASSERT_ISALIGNED16(f); + _mm_store_ps(f, a); +} + +PX_FORCE_INLINE void V4StoreU(const Vec4V a, PxF32* f) +{ + _mm_storeu_ps(f, a); +} + +PX_FORCE_INLINE void BStoreA(const BoolV a, PxU32* f) +{ + ASSERT_ISALIGNED16(f); + _mm_store_ps(reinterpret_cast(f), a); +} + +PX_FORCE_INLINE void U4StoreA(const VecU32V uv, PxU32* u) +{ + ASSERT_ISALIGNED16(u); + _mm_store_ps(reinterpret_cast(u), uv); +} + +PX_FORCE_INLINE void I4StoreA(const VecI32V iv, PxI32* i) +{ + ASSERT_ISALIGNED16(i); + _mm_store_ps(reinterpret_cast(i), m128_I2F(iv)); +} + +PX_FORCE_INLINE Vec4V V4LoadU(const PxF32* const f) +{ + return _mm_loadu_ps(f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool* const f) +{ + const PX_ALIGN(16, PxI32) b[4] = { -PxI32(f[0]), -PxI32(f[1]), -PxI32(f[2]), -PxI32(f[3]) }; + return _mm_load_ps(reinterpret_cast(&b)); +} + +PX_FORCE_INLINE void FStore(const FloatV a, PxF32* PX_RESTRICT f) +{ + ASSERT_ISVALIDFLOATV(a); + _mm_store_ss(f, a); +} + +PX_FORCE_INLINE void V3StoreA(const Vec3V a, PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + PX_ALIGN(16, PxF32) f2[4]; + _mm_store_ps(f2, a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +PX_FORCE_INLINE void V3StoreU(const Vec3V a, PxVec3& f) +{ + PX_ALIGN(16, PxF32) f2[4]; + _mm_store_ps(f2, a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +PX_FORCE_INLINE void Store_From_BoolV(const BoolV b, PxU32* b2) +{ + _mm_store_ss(reinterpret_cast(b2), b); +} + +PX_FORCE_INLINE VecU32V U4Load(const PxU32 i) +{ + return _mm_load1_ps(reinterpret_cast(&i)); +} + +PX_FORCE_INLINE VecU32V U4LoadU(const PxU32* i) +{ + return _mm_loadu_ps(reinterpret_cast(i)); +} + +PX_FORCE_INLINE VecU32V U4LoadA(const PxU32* i) +{ + ASSERT_ISALIGNED16(const_cast(i)); + return _mm_load_ps(reinterpret_cast(i)); +} + +////////////////////////////////// +// FLOATV +////////////////////////////////// + +PX_FORCE_INLINE FloatV FZero() +{ + return FLoad(0.0f); +} + +PX_FORCE_INLINE FloatV FOne() +{ + return FLoad(1.0f); +} + +PX_FORCE_INLINE FloatV FHalf() +{ + return FLoad(0.5f); +} + +PX_FORCE_INLINE FloatV FEps() +{ + return FLoad(PX_EPS_REAL); +} + +PX_FORCE_INLINE FloatV FEps6() +{ + return FLoad(1e-6f); +} + +PX_FORCE_INLINE FloatV FMax() +{ + return FLoad(PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV FNegMax() +{ + return FLoad(-PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV IZero() +{ + const PxU32 zero = 0; + return _mm_load1_ps(reinterpret_cast(&zero)); +} + +PX_FORCE_INLINE FloatV IOne() +{ + const PxU32 one = 1; + return _mm_load1_ps(reinterpret_cast(&one)); +} + +PX_FORCE_INLINE FloatV ITwo() +{ + const PxU32 two = 2; + return _mm_load1_ps(reinterpret_cast(&two)); +} + +PX_FORCE_INLINE FloatV IThree() +{ + const PxU32 three = 3; + return _mm_load1_ps(reinterpret_cast(&three)); +} + +PX_FORCE_INLINE FloatV IFour() +{ + PxU32 four = 4; + return _mm_load1_ps(reinterpret_cast(&four)); +} + +PX_FORCE_INLINE FloatV FNeg(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); +/* + if(!isValidFloatV(a)) + { +assert(false); + } + if(!isValidFloatV(b)) + { +assert(false); + } +*/ + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE FloatV FRecip(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_div_ps(FOne(), a); +} + +PX_FORCE_INLINE FloatV FRecipFast(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_rcp_ps(a); +} + +PX_FORCE_INLINE FloatV FRsqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_div_ps(FOne(), _mm_sqrt_ps(a)); +} + +PX_FORCE_INLINE FloatV FSqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_sqrt_ps(a); +} + +PX_FORCE_INLINE FloatV FRsqrtFast(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_rsqrt_ps(a); +} + +PX_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return FAdd(FMul(a, b), c); +} + +PX_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return FSub(c, FMul(a, b)); +} + +PX_FORCE_INLINE FloatV FAbs(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + PX_ALIGN(16, const PxU32) absMask[4] = { 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF }; + return _mm_and_ps(a, _mm_load_ps(reinterpret_cast(absMask))); +} + +PX_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b) +{ + PX_ASSERT(_VecMathTests::allElementsEqualBoolV(c,BTTTT()) || + _VecMathTests::allElementsEqualBoolV(c,BFFFF())); + ASSERT_ISVALIDFLOATV(_mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a))); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV) +{ + ASSERT_ISVALIDFLOATV(minV); + ASSERT_ISVALIDFLOATV(maxV); + return _mm_max_ps(_mm_min_ps(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 FAllGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_comigt_ss(a, b); +} + +PX_FORCE_INLINE PxU32 FAllGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_comige_ss(a, b); +} + +PX_FORCE_INLINE PxU32 FAllEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_comieq_ss(a, b); +} + +PX_FORCE_INLINE FloatV FRound(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); +#ifdef __SSE4_2__ + return _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); +#else + // return _mm_round_ps(a, 0x0); + const FloatV half = FLoad(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const FloatV aRound = FSub(FAdd(a, half), signBit); + __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +#endif +} + +PX_FORCE_INLINE FloatV FSin(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = V4LoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V3 = FMul(V2, V1); + const FloatV V5 = FMul(V3, V2); + const FloatV V7 = FMul(V5, V2); + const FloatV V9 = FMul(V7, V2); + const FloatV V11 = FMul(V9, V2); + const FloatV V13 = FMul(V11, V2); + const FloatV V15 = FMul(V13, V2); + const FloatV V17 = FMul(V15, V2); + const FloatV V19 = FMul(V17, V2); + const FloatV V21 = FMul(V19, V2); + const FloatV V23 = FMul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + FloatV Result; + Result = FScaleAdd(S1, V3, V1); + Result = FScaleAdd(S2, V5, Result); + Result = FScaleAdd(S3, V7, Result); + Result = FScaleAdd(S4, V9, Result); + Result = FScaleAdd(S5, V11, Result); + Result = FScaleAdd(S6, V13, Result); + Result = FScaleAdd(S7, V15, Result); + Result = FScaleAdd(S8, V17, Result); + Result = FScaleAdd(S9, V19, Result); + Result = FScaleAdd(S10, V21, Result); + Result = FScaleAdd(S11, V23, Result); + + return Result; +} + +PX_FORCE_INLINE FloatV FCos(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = V4LoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V4 = FMul(V2, V2); + const FloatV V6 = FMul(V4, V2); + const FloatV V8 = FMul(V4, V4); + const FloatV V10 = FMul(V6, V4); + const FloatV V12 = FMul(V6, V6); + const FloatV V14 = FMul(V8, V6); + const FloatV V16 = FMul(V8, V8); + const FloatV V18 = FMul(V10, V8); + const FloatV V20 = FMul(V10, V10); + const FloatV V22 = FMul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + FloatV Result; + Result = FScaleAdd(C1, V2, V4One()); + Result = FScaleAdd(C2, V4, Result); + Result = FScaleAdd(C3, V6, Result); + Result = FScaleAdd(C4, V8, Result); + Result = FScaleAdd(C5, V10, Result); + Result = FScaleAdd(C6, V12, Result); + Result = FScaleAdd(C7, V14, Result); + Result = FScaleAdd(C8, V16, Result); + Result = FScaleAdd(C9, V18, Result); + Result = FScaleAdd(C10, V20, Result); + Result = FScaleAdd(C11, V22, Result); + + return Result; +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max); + const BoolV c = BOr(FIsGrtr(a, max), FIsGrtr(min, a)); + return !BAllEqFFFF(c); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max) + const BoolV c = BAnd(FIsGrtrOrEq(a, min), FIsGrtrOrEq(max, a)); + return BAllEqTTTT(c); +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + return FOutOfBounds(a, FNeg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + return FInBounds(a, FNeg(bounds), bounds); +} + +////////////////////////////////// +// VEC3V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V V3Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + const __m128 zero = FZero(); + const __m128 fff0 = _mm_move_ss(f, zero); + return _mm_shuffle_ps(fff0, fff0, _MM_SHUFFLE(0, 1, 2, 3)); +} + +PX_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + // static on zero causes compiler crash on x64 debug_opt + const __m128 zero = FZero(); + const __m128 xy = _mm_move_ss(x, y); + const __m128 z0 = _mm_move_ss(zero, z); + + return _mm_shuffle_ps(xy, z0, _MM_SHUFFLE(1, 0, 0, 1)); +} + +PX_FORCE_INLINE Vec3V V3UnitX() +{ + const PX_ALIGN(16, PxF32) x[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +PX_FORCE_INLINE Vec3V V3UnitY() +{ + const PX_ALIGN(16, PxF32) y[4] = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +PX_FORCE_INLINE Vec3V V3UnitZ() +{ + const PX_ALIGN(16, PxF32) z[4] = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +PX_FORCE_INLINE FloatV V3GetX(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE FloatV V3GetY(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f) + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE FloatV V3GetZ(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 0, 3, 0)); + return V3SetY(r, V3GetX(b)); +} + +PX_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c) + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 1, 3, 1)); + return V3SetY(r, V3GetY(b)); +} + +PX_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 2, 3, 2)); + return V3SetY(r, V3GetZ(b)); +} + +PX_FORCE_INLINE Vec3V V3Zero() +{ + return V3Load(0.0f); +} + +PX_FORCE_INLINE Vec3V V3Eps() +{ + return V3Load(PX_EPS_REAL); +} +PX_FORCE_INLINE Vec3V V3One() +{ + return V3Load(1.0f); +} + +PX_FORCE_INLINE Vec3V V3Neg(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return V4ClearW(_mm_div_ps(a, b)); +} + +PX_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return V4ClearW(_mm_mul_ps(a, _mm_rcp_ps(b))); +} + +PX_FORCE_INLINE Vec3V V3Recip(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_div_ps(V3One(), a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rcp_ps(a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_div_ps(V3One(), _mm_sqrt_ps(a)); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rsqrt_ps(a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + return V3Add(V3Scale(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + return V3Sub(c, V3Scale(a, b)); +} + +PX_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return V3Add(V3Mul(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return V3Sub(c, V3Mul(a, b)); +} + +PX_FORCE_INLINE Vec3V V3Abs(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return V3Max(a, V3Neg(a)); +} + +PX_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); +#ifdef __SSE4_2__ + return _mm_dp_ps(a, b, 0x7f); +#else + const __m128 t0 = _mm_mul_ps(a, b); // aw*bw | az*bz | ay*by | ax*bx + const __m128 t1 = _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(1,0,3,2)); // ay*by | ax*bx | aw*bw | az*bz + const __m128 t2 = _mm_add_ps(t0, t1); // ay*by + aw*bw | ax*bx + az*bz | aw*bw + ay*by | az*bz + ax*bx + const __m128 t3 = _mm_shuffle_ps(t2, t2, _MM_SHUFFLE(2,3,0,1)); // ax*bx + az*bz | ay*by + aw*bw | az*bz + ax*bx | aw*bw + ay*by + return _mm_add_ps(t3, t2); // ax*bx + az*bz + ay*by + aw*bw + // ay*by + aw*bw + ax*bx + az*bz + // az*bz + ax*bx + aw*bw + ay*by + // aw*bw + ay*by + az*bz + ax*bx +#endif +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + const __m128 r1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(l1, l2), _mm_mul_ps(r1, r2)); +} + +PX_FORCE_INLINE VecCrossV V3PrepareCross(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + VecCrossV v; + v.mR1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + v.mL1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + return v; +} + +PX_FORCE_INLINE Vec3V V3Cross(const VecCrossV& a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(b); + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(a.mL1, l2), _mm_mul_ps(a.mR1, r2)); +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const VecCrossV& b) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 r2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(b.mR1, r2), _mm_mul_ps(b.mL1, l2)); +} + +PX_FORCE_INLINE Vec3V V3Cross(const VecCrossV& a, const VecCrossV& b) +{ + return _mm_sub_ps(_mm_mul_ps(a.mL1, b.mR1), _mm_mul_ps(a.mR1, b.mL1)); +} + +PX_FORCE_INLINE FloatV V3Length(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_sqrt_ps(V3Dot(a, a)); +} + +PX_FORCE_INLINE FloatV V3LengthSq(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return V3Dot(a, a); +} + +PX_FORCE_INLINE Vec3V V3Normalize(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISFINITELENGTH(a); + return V3ScaleInv(a, _mm_sqrt_ps(V3Dot(a, a))); +} + +PX_FORCE_INLINE Vec3V V3NormalizeFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISFINITELENGTH(a); + return V3Scale(a, _mm_rsqrt_ps(V3Dot(a, a))); +} + +PX_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a, const Vec3V unsafeReturnValue) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 eps = V3Eps(); + const __m128 length = V3Length(a); + const __m128 isGreaterThanZero = FIsGrtr(length, eps); + return V3Sel(isGreaterThanZero, V3ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(_mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a))); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + + return _mm_max_ps(_mm_max_ps(shuf1, shuf2), shuf3); +} + +PX_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + + return _mm_min_ps(_mm_min_ps(shuf1, shuf2), shuf3); +} + +// return (a >= 0.0f) ? 1.0f : -1.0f; +PX_FORCE_INLINE Vec3V V3Sign(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 one = V3One(); + const __m128 none = V3Neg(one); + return V3Sel(V3IsGrtrOrEq(a, zero), one, none); +} + +PX_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV) +{ + ASSERT_ISVALIDVEC3V(maxV); + ASSERT_ISVALIDVEC3V(minV); + return V3Max(V3Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V3AllGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitSSE2Simd::BAllTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitSSE2Simd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalUnitSSE2Simd::BAllTrue3_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE Vec3V V3Round(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); +#ifdef __SSE4_2__ + return _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); +#else + // return _mm_round_ps(a, 0x0); + const Vec3V half = V3Load(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const Vec3V aRound = V3Sub(V3Add(a, half), signBit); + __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +#endif +} + +PX_FORCE_INLINE Vec3V V3Sin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V3Scale(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegScaleSub(b, twoPi, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V3 = V3Mul(V2, V1); + const Vec3V V5 = V3Mul(V3, V2); + const Vec3V V7 = V3Mul(V5, V2); + const Vec3V V9 = V3Mul(V7, V2); + const Vec3V V11 = V3Mul(V9, V2); + const Vec3V V13 = V3Mul(V11, V2); + const Vec3V V15 = V3Mul(V13, V2); + const Vec3V V17 = V3Mul(V15, V2); + const Vec3V V19 = V3Mul(V17, V2); + const Vec3V V21 = V3Mul(V19, V2); + const Vec3V V23 = V3Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec3V Result; + Result = V3ScaleAdd(V3, S1, V1); + Result = V3ScaleAdd(V5, S2, Result); + Result = V3ScaleAdd(V7, S3, Result); + Result = V3ScaleAdd(V9, S4, Result); + Result = V3ScaleAdd(V11, S5, Result); + Result = V3ScaleAdd(V13, S6, Result); + Result = V3ScaleAdd(V15, S7, Result); + Result = V3ScaleAdd(V17, S8, Result); + Result = V3ScaleAdd(V19, S9, Result); + Result = V3ScaleAdd(V21, S10, Result); + Result = V3ScaleAdd(V23, S11, Result); + + ASSERT_ISVALIDVEC3V(Result); + return Result; +} + +PX_FORCE_INLINE Vec3V V3Cos(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V3Scale(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegScaleSub(b, twoPi, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V4 = V3Mul(V2, V2); + const Vec3V V6 = V3Mul(V4, V2); + const Vec3V V8 = V3Mul(V4, V4); + const Vec3V V10 = V3Mul(V6, V4); + const Vec3V V12 = V3Mul(V6, V6); + const Vec3V V14 = V3Mul(V8, V6); + const Vec3V V16 = V3Mul(V8, V8); + const Vec3V V18 = V3Mul(V10, V8); + const Vec3V V20 = V3Mul(V10, V10); + const Vec3V V22 = V3Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec3V Result; + Result = V3ScaleAdd(V2, C1, V3One()); + Result = V3ScaleAdd(V4, C2, Result); + Result = V3ScaleAdd(V6, C3, Result); + Result = V3ScaleAdd(V8, C4, Result); + Result = V3ScaleAdd(V10, C5, Result); + Result = V3ScaleAdd(V12, C6, Result); + Result = V3ScaleAdd(V14, C7, Result); + Result = V3ScaleAdd(V16, C8, Result); + Result = V3ScaleAdd(V18, C9, Result); + Result = V3ScaleAdd(V20, C10, Result); + Result = V3ScaleAdd(V22, C11, Result); + + ASSERT_ISVALIDVEC3V(Result); + return Result; +} + +PX_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 2, 1)); +} + +PX_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 1, 0)); +} + +PX_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); +} + +PX_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); +} + +PX_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 2, 2)); +} + +PX_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 0, 1)); +} + +PX_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + return _mm_shuffle_ps(v1, v0, _MM_SHUFFLE(3, 1, 2, 3)); +} + +PX_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + return _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(3, 0, 3, 2)); +} + +PX_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + // There must be a better way to do this. + Vec3V v2 = V3Zero(); + FloatV y1 = V3GetY(v1); + FloatV x0 = V3GetX(v0); + v2 = V3SetX(v2, y1); + return V3SetY(v2, x0); +} + +PX_FORCE_INLINE FloatV V3SumElems(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); +#ifdef __SSE4_2__ + Vec3V r = _mm_hadd_ps(a, a); + r = _mm_hadd_ps(r, r); + return r; +#else + __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); // z,y,x,w + __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); // y,x,w,z + __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); // x,w,z,y + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); +#endif +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + const BoolV c = BOr(V3IsGrtr(a, max), V3IsGrtr(min, a)); + return !BAllEqFFFF(c); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + const BoolV c = BAnd(V3IsGrtrOrEq(a, min), V3IsGrtrOrEq(max, a)); + return BAllEqTTTT(c); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds); + return V3OutOfBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds) + return V3InBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2) +{ + ASSERT_ISVALIDVEC3V(col0); + ASSERT_ISVALIDVEC3V(col1); + ASSERT_ISVALIDVEC3V(col2); + + const Vec3V col3 = _mm_setzero_ps(); + Vec3V tmp0 = _mm_unpacklo_ps(col0, col1); + Vec3V tmp2 = _mm_unpacklo_ps(col2, col3); + Vec3V tmp1 = _mm_unpackhi_ps(col0, col1); + Vec3V tmp3 = _mm_unpackhi_ps(col2, col3); + col0 = _mm_movelh_ps(tmp0, tmp2); + col1 = _mm_movehl_ps(tmp2, tmp0); + col2 = _mm_movelh_ps(tmp1, tmp3); +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V V4Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + // return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0,0,0,0)); + return f; +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatV* const floatVArray) +{ + ASSERT_ISVALIDFLOATV(floatVArray[0]); + ASSERT_ISVALIDFLOATV(floatVArray[1]); + ASSERT_ISVALIDFLOATV(floatVArray[2]); + ASSERT_ISVALIDFLOATV(floatVArray[3]); + const __m128 xw = _mm_move_ss(floatVArray[1], floatVArray[0]); // y, y, y, x + const __m128 yz = _mm_move_ss(floatVArray[2], floatVArray[3]); // z, z, z, w + return _mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0)); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + ASSERT_ISVALIDFLOATV(w); + const __m128 xw = _mm_move_ss(y, x); // y, y, y, x + const __m128 yz = _mm_move_ss(z, w); // z, z, z, w + return _mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0)); +} + +PX_FORCE_INLINE Vec4V V4MergeW(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpackhi_ps(x, z); + const Vec4V yw = _mm_unpackhi_ps(y, w); + return _mm_unpackhi_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeZ(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpackhi_ps(x, z); + const Vec4V yw = _mm_unpackhi_ps(y, w); + return _mm_unpacklo_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeY(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpacklo_ps(x, z); + const Vec4V yw = _mm_unpacklo_ps(y, w); + return _mm_unpackhi_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeX(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpacklo_ps(x, z); + const Vec4V yw = _mm_unpacklo_ps(y, w); + return _mm_unpacklo_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4UnpackXY(const Vec4VArg a, const Vec4VArg b) +{ + return _mm_unpacklo_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4UnpackZW(const Vec4VArg a, const Vec4VArg b) +{ + return _mm_unpackhi_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4UnitW() +{ + const PX_ALIGN(16, PxF32) w[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; + const __m128 w128 = _mm_load_ps(w); + return w128; +} + +PX_FORCE_INLINE Vec4V V4UnitX() +{ + const PX_ALIGN(16, PxF32) x[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +PX_FORCE_INLINE Vec4V V4UnitY() +{ + const PX_ALIGN(16, PxF32) y[4] = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +PX_FORCE_INLINE Vec4V V4UnitZ() +{ + const PX_ALIGN(16, PxF32) z[4] = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +PX_FORCE_INLINE FloatV V4GetW(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +PX_FORCE_INLINE FloatV V4GetX(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE FloatV V4GetY(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE FloatV V4GetZ(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTTF(), v, f); +} + +PX_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4ClearW(const Vec4V v) +{ +#if !PX_EMSCRIPTEN + return _mm_and_ps(v, V4LoadA(internalUnitSSE2Simd::gMaskXYZ)); +#else + return _mm_and_ps(v, (VecI32V&)internalUnitSSE2Simd::gMaskXYZ); +#endif +} + +PX_FORCE_INLINE Vec4V V4PermYXWZ(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 0, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermXZXZ(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 0, 2, 0)); +} + +PX_FORCE_INLINE Vec4V V4PermYWYW(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 3, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermZWXY(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); +} + +template +PX_FORCE_INLINE Vec4V V4Perm(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(w, z, y, x)); +} + +PX_FORCE_INLINE Vec4V V4Zero() +{ + return V4Load(0.0f); +} + +PX_FORCE_INLINE Vec4V V4One() +{ + return V4Load(1.0f); +} + +PX_FORCE_INLINE Vec4V V4Eps() +{ + return V4Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec4V V4Neg(const Vec4V f) +{ + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b) +{ + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b) +{ + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b) +{ + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b) +{ + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec4V V4Recip(const Vec4V a) +{ + return _mm_div_ps(V4One(), a); +} + +PX_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a) +{ + return _mm_rcp_ps(a); +} + +PX_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a) +{ + return _mm_div_ps(V4One(), _mm_sqrt_ps(a)); +} + +PX_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a) +{ + return _mm_rsqrt_ps(a); +} + +PX_FORCE_INLINE Vec4V V4Sqrt(const Vec4V a) +{ + return _mm_sqrt_ps(a); +} + +PX_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return V4Add(V4Scale(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return V4Sub(c, V4Scale(a, b)); +} + +PX_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Add(V4Mul(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Sub(c, V4Mul(a, b)); +} + +PX_FORCE_INLINE Vec4V V4Abs(const Vec4V a) +{ + return V4Max(a, V4Neg(a)); +} + +PX_FORCE_INLINE FloatV V4SumElements(const Vec4V a) +{ +#ifdef __SSE4_2__ + Vec4V r = _mm_hadd_ps(a, a); + r = _mm_hadd_ps(r, r); + return r; +#else + const Vec4V xy = V4UnpackXY(a, a); // x,x,y,y + const Vec4V zw = V4UnpackZW(a, a); // z,z,w,w + const Vec4V xz_yw = V4Add(xy, zw); // x+z,x+z,y+w,y+w + const FloatV xz = V4GetX(xz_yw); // x+z + const FloatV yw = V4GetZ(xz_yw); // y+w + return FAdd(xz, yw); // sum +#endif +} + +PX_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b) +{ +#ifdef __SSE4_2__ + return _mm_dp_ps(a, b, 0xff); +#else + const __m128 dot1 = _mm_mul_ps(a, b); // x,y,z,w + const __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 1, 0, 3)); // w,x,y,z + const __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 0, 3, 2)); // z,w,x,y + const __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 3, 2, 1)); // y,z,w,x + return _mm_add_ps(_mm_add_ps(shuf2, shuf3), _mm_add_ps(dot1, shuf1)); +#endif +} + +PX_FORCE_INLINE FloatV V4Dot3(const Vec4V a, const Vec4V b) +{ +#ifdef __SSE4_2__ + return _mm_dp_ps(a, b, 0x7f); +#else + const __m128 dot1 = _mm_mul_ps(a, b); // w,z,y,x + const __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 0, 0, 0)); // z,y,x,w + const __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 1, 1, 1)); // y,x,w,z + const __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 2, 2, 2)); // x,w,z,y + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); +#endif +} + +PX_FORCE_INLINE Vec4V V4Cross(const Vec4V a, const Vec4V b) +{ + const __m128 r1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(l1, l2), _mm_mul_ps(r1, r2)); +} + +PX_FORCE_INLINE FloatV V4Length(const Vec4V a) +{ + return _mm_sqrt_ps(V4Dot(a, a)); +} + +PX_FORCE_INLINE FloatV V4LengthSq(const Vec4V a) +{ + return V4Dot(a, a); +} + +PX_FORCE_INLINE Vec4V V4Normalize(const Vec4V a) +{ + ASSERT_ISFINITELENGTH(a); + return V4ScaleInv(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +PX_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a) +{ + ASSERT_ISFINITELENGTH(a); + return V4ScaleInvFast(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +PX_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a, const Vec3V unsafeReturnValue) +{ + const __m128 eps = V3Eps(); + const __m128 length = V4Length(a); + const __m128 isGreaterThanZero = V4IsGrtr(length, eps); + return V4Sel(isGreaterThanZero, V4ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b) +{ + return m128_I2F(_mm_cmpeq_epi32(m128_F2I(a), m128_F2I(b))); +} + +PX_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b) +{ + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b) +{ + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Max(const Vec4V a, const Vec4V b) +{ + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b) +{ + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_max_ps(_mm_max_ps(a, shuf1), _mm_max_ps(shuf2, shuf3)); +} + +PX_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_min_ps(_mm_min_ps(a, shuf1), _mm_min_ps(shuf2, shuf3)); +} + +PX_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV) +{ + return V4Max(V4Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V4AllGrtr(const Vec4V a, const Vec4V b) +{ + return internalUnitSSE2Simd::BAllTrue4_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return internalUnitSSE2Simd::BAllTrue4_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq3(const Vec4V a, const Vec4V b) +{ + return internalUnitSSE2Simd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllEq(const Vec4V a, const Vec4V b) +{ + return internalUnitSSE2Simd::BAllTrue4_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AnyGrtr3(const Vec4V a, const Vec4V b) +{ + return internalUnitSSE2Simd::BAnyTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE Vec4V V4Round(const Vec4V a) +{ +#ifdef __SSE4_2__ + return _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); +#else + // return _mm_round_ps(a, 0x0); + const Vec4V half = V4Load(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const Vec4V aRound = V4Sub(V4Add(a, half), signBit); + __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +#endif +} + +PX_FORCE_INLINE Vec4V V4Sin(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V3 = V4Mul(V2, V1); + const Vec4V V5 = V4Mul(V3, V2); + const Vec4V V7 = V4Mul(V5, V2); + const Vec4V V9 = V4Mul(V7, V2); + const Vec4V V11 = V4Mul(V9, V2); + const Vec4V V13 = V4Mul(V11, V2); + const Vec4V V15 = V4Mul(V13, V2); + const Vec4V V17 = V4Mul(V15, V2); + const Vec4V V19 = V4Mul(V17, V2); + const Vec4V V21 = V4Mul(V19, V2); + const Vec4V V23 = V4Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec4V Result; + Result = V4MulAdd(S1, V3, V1); + Result = V4MulAdd(S2, V5, Result); + Result = V4MulAdd(S3, V7, Result); + Result = V4MulAdd(S4, V9, Result); + Result = V4MulAdd(S5, V11, Result); + Result = V4MulAdd(S6, V13, Result); + Result = V4MulAdd(S7, V15, Result); + Result = V4MulAdd(S8, V17, Result); + Result = V4MulAdd(S9, V19, Result); + Result = V4MulAdd(S10, V21, Result); + Result = V4MulAdd(S11, V23, Result); + + return Result; +} + +PX_FORCE_INLINE Vec4V V4Cos(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V4 = V4Mul(V2, V2); + const Vec4V V6 = V4Mul(V4, V2); + const Vec4V V8 = V4Mul(V4, V4); + const Vec4V V10 = V4Mul(V6, V4); + const Vec4V V12 = V4Mul(V6, V6); + const Vec4V V14 = V4Mul(V8, V6); + const Vec4V V16 = V4Mul(V8, V8); + const Vec4V V18 = V4Mul(V10, V8); + const Vec4V V20 = V4Mul(V10, V10); + const Vec4V V22 = V4Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec4V Result; + Result = V4MulAdd(C1, V2, V4One()); + Result = V4MulAdd(C2, V4, Result); + Result = V4MulAdd(C3, V6, Result); + Result = V4MulAdd(C4, V8, Result); + Result = V4MulAdd(C5, V10, Result); + Result = V4MulAdd(C6, V12, Result); + Result = V4MulAdd(C7, V14, Result); + Result = V4MulAdd(C8, V16, Result); + Result = V4MulAdd(C9, V18, Result); + Result = V4MulAdd(C10, V20, Result); + Result = V4MulAdd(C11, V22, Result); + + return Result; +} + +PX_FORCE_INLINE void V4Transpose(Vec4V& col0, Vec4V& col1, Vec4V& col2, Vec4V& col3) +{ + Vec4V tmp0 = _mm_unpacklo_ps(col0, col1); + Vec4V tmp2 = _mm_unpacklo_ps(col2, col3); + Vec4V tmp1 = _mm_unpackhi_ps(col0, col1); + Vec4V tmp3 = _mm_unpackhi_ps(col2, col3); + col0 = _mm_movelh_ps(tmp0, tmp2); + col1 = _mm_movehl_ps(tmp2, tmp0); + col2 = _mm_movelh_ps(tmp1, tmp3); + col3 = _mm_movehl_ps(tmp3, tmp1); +} + +////////////////////////////////// +// BoolV +////////////////////////////////// + +PX_FORCE_INLINE BoolV BFFFF() +{ + return _mm_setzero_ps(); +} + +PX_FORCE_INLINE BoolV BFFFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0,0xFFFFFFFF}; + const __m128 ffft=_mm_load_ps((float*)&f); + return ffft;*/ + return m128_I2F(_mm_set_epi32(-1, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFFTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0}; + const __m128 fftf=_mm_load_ps((float*)&f); + return fftf;*/ + return m128_I2F(_mm_set_epi32(0, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFFTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 fftt=_mm_load_ps((float*)&f); + return fftt;*/ + return m128_I2F(_mm_set_epi32(-1, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFTFF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0}; + const __m128 ftff=_mm_load_ps((float*)&f); + return ftff;*/ + return m128_I2F(_mm_set_epi32(0, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0xFFFFFFFF}; + const __m128 ftft=_mm_load_ps((float*)&f); + return ftft;*/ + return m128_I2F(_mm_set_epi32(-1, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0xFFFFFFFF,0}; + const __m128 fttf=_mm_load_ps((float*)&f); + return fttf;*/ + return m128_I2F(_mm_set_epi32(0, -1, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 fttt=_mm_load_ps((float*)&f); + return fttt;*/ + return m128_I2F(_mm_set_epi32(-1, -1, -1, 0)); +} + +PX_FORCE_INLINE BoolV BTFFF() +{ + // const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0}; + // const __m128 tfff=_mm_load_ps((float*)&f); + // return tfff; + return m128_I2F(_mm_set_epi32(0, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0xFFFFFFFF}; + const __m128 tfft=_mm_load_ps((float*)&f); + return tfft;*/ + return m128_I2F(_mm_set_epi32(-1, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0xFFFFFFFF,0}; + const __m128 tftf=_mm_load_ps((float*)&f); + return tftf;*/ + return m128_I2F(_mm_set_epi32(0, -1, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 tftt=_mm_load_ps((float*)&f); + return tftt;*/ + return m128_I2F(_mm_set_epi32(-1, -1, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTTFF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0,0}; + const __m128 ttff=_mm_load_ps((float*)&f); + return ttff;*/ + return m128_I2F(_mm_set_epi32(0, 0, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0,0xFFFFFFFF}; + const __m128 ttft=_mm_load_ps((float*)&f); + return ttft;*/ + return m128_I2F(_mm_set_epi32(-1, 0, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0}; + const __m128 tttf=_mm_load_ps((float*)&f); + return tttf;*/ + return m128_I2F(_mm_set_epi32(0, -1, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 tttt=_mm_load_ps((float*)&f); + return tttt;*/ + return m128_I2F(_mm_set_epi32(-1, -1, -1, -1)); +} + +PX_FORCE_INLINE BoolV BXMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0}; + const __m128 tfff=_mm_load_ps((float*)&f); + return tfff;*/ + return m128_I2F(_mm_set_epi32(0, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BYMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0}; + const __m128 ftff=_mm_load_ps((float*)&f); + return ftff;*/ + return m128_I2F(_mm_set_epi32(0, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BZMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0}; + const __m128 fftf=_mm_load_ps((float*)&f); + return fftf;*/ + return m128_I2F(_mm_set_epi32(0, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BWMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0,0xFFFFFFFF}; + const __m128 ffft=_mm_load_ps((float*)&f); + return ffft;*/ + return m128_I2F(_mm_set_epi32(-1, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BGetX(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BGetY(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE BoolV BGetZ(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE BoolV BGetW(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +PX_FORCE_INLINE BoolV BSetX(const BoolV v, const BoolV f) +{ + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetY(const BoolV v, const BoolV f) +{ + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetZ(const BoolV v, const BoolV f) +{ + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetW(const BoolV v, const BoolV f) +{ + return V4Sel(BTTTF(), v, f); +} + +PX_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b) +{ + return _mm_and_ps(a, b); +} + +PX_FORCE_INLINE BoolV BNot(const BoolV a) +{ + const BoolV bAllTrue(BTTTT()); + return _mm_xor_ps(a, bAllTrue); +} + +PX_FORCE_INLINE BoolV BAndNot(const BoolV a, const BoolV b) +{ + return _mm_andnot_ps(b, a); +} + +PX_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b) +{ + return _mm_or_ps(a, b); +} + +PX_FORCE_INLINE BoolV BAllTrue4(const BoolV a) +{ + const BoolV bTmp = + _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAnyTrue4(const BoolV a) +{ + const BoolV bTmp = + _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAllTrue3(const BoolV a) +{ + const BoolV bTmp = + _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAnyTrue3(const BoolV a) +{ + const BoolV bTmp = + _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE PxU32 BAllEq(const BoolV a, const BoolV b) +{ + const BoolV bTest = m128_I2F(_mm_cmpeq_epi32(m128_F2I(a), m128_F2I(b))); + return internalUnitSSE2Simd::BAllTrue4_R(bTest); +} + +PX_FORCE_INLINE PxU32 BAllEqTTTT(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)==15); +} + +PX_FORCE_INLINE PxU32 BAllEqFFFF(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)==0); +} + +PX_FORCE_INLINE PxU32 BGetBitMask(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)); +} + +////////////////////////////////// +// MAT33V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M33MulV3(const Mat33V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V& a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +PX_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V& A, const Vec3V b, const Vec3V c) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + Vec3V result = V3ScaleAdd(A.col0, x, c); + result = V3ScaleAdd(A.col1, y, result); + return V3ScaleAdd(A.col2, z, result); +} + +PX_FORCE_INLINE Mat33V M33MulM33(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(M33MulV3(a, b.col0), M33MulV3(a, b.col1), M33MulV3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Add(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Scale(const Mat33V& a, const FloatV& b) +{ + return Mat33V(V3Scale(a.col0, b), V3Scale(a.col1, b), V3Scale(a.col2, b)); +} + +PX_FORCE_INLINE Mat33V M33Inverse(const Mat33V& a) +{ + const BoolV tfft = BTFFT(); + const BoolV tttf = BTTTF(); + const FloatV zero = FZero(); + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = _mm_rcp_ps(dot); + const Vec3V mergeh = _mm_unpacklo_ps(cross12, cross01); + const Vec3V mergel = _mm_unpackhi_ps(cross12, cross01); + Vec3V colInv0 = _mm_unpacklo_ps(mergeh, cross20); + colInv0 = _mm_or_ps(_mm_andnot_ps(tttf, zero), _mm_and_ps(tttf, colInv0)); + const Vec3V zppd = _mm_shuffle_ps(mergeh, cross20, _MM_SHUFFLE(3, 0, 0, 2)); + const Vec3V pbwp = _mm_shuffle_ps(cross20, mergeh, _MM_SHUFFLE(3, 3, 1, 0)); + const Vec3V colInv1 = _mm_or_ps(_mm_andnot_ps(BTFFT(), pbwp), _mm_and_ps(BTFFT(), zppd)); + const Vec3V xppd = _mm_shuffle_ps(mergel, cross20, _MM_SHUFFLE(3, 0, 0, 0)); + const Vec3V pcyp = _mm_shuffle_ps(cross20, mergel, _MM_SHUFFLE(3, 1, 2, 0)); + const Vec3V colInv2 = _mm_or_ps(_mm_andnot_ps(tfft, pcyp), _mm_and_ps(tfft, xppd)); + + return Mat33V(_mm_mul_ps(colInv0, invDet), _mm_mul_ps(colInv1, invDet), _mm_mul_ps(colInv2, invDet)); +} + +PX_FORCE_INLINE Mat33V M33Trnsps(const Mat33V& a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +PX_FORCE_INLINE Mat33V M33Identity() +{ + return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ()); +} + +PX_FORCE_INLINE Mat33V M33Sub(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Sub(a.col0, b.col0), V3Sub(a.col1, b.col1), V3Sub(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Neg(const Mat33V& a) +{ + return Mat33V(V3Neg(a.col0), V3Neg(a.col1), V3Neg(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Abs(const Mat33V& a) +{ + return Mat33V(V3Abs(a.col0), V3Abs(a.col1), V3Abs(a.col2)); +} + +PX_FORCE_INLINE Mat33V PromoteVec3V(const Vec3V v) +{ + const BoolV bTFFF = BTFFF(); + const BoolV bFTFF = BFTFF(); + const BoolV bFFTF = BTFTF(); + + const Vec3V zero = V3Zero(); + + return Mat33V(V3Sel(bTFFF, v, zero), V3Sel(bFTFF, v, zero), V3Sel(bFFTF, v, zero)); +} + +PX_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg d) +{ + const FloatV x = V3Mul(V3UnitX(), d); + const FloatV y = V3Mul(V3UnitY(), d); + const FloatV z = V3Mul(V3UnitZ(), d); + return Mat33V(x, y, z); +} + +////////////////////////////////// +// MAT34V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M34MulV3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + const Vec3V v0PlusV1Plusv2 = V3Add(v0PlusV1, v2); + return V3Add(v0PlusV1Plusv2, a.col3); +} + +PX_FORCE_INLINE Vec3V M34Mul33V3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M34TrnspsMul33V3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3Dot(a.col0, b); + const FloatV y = V3Dot(a.col1, b); + const FloatV z = V3Dot(a.col2, b); + return V3Merge(x, y, z); +} + +PX_FORCE_INLINE Mat34V M34MulM34(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2), M34MulV3(a, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34MulM33(const Mat34V& a, const Mat33V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M34Mul33MM34(const Mat34V& a, const Mat34V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat34V M34Add(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2), V3Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34Trnsps33(const Mat34V& a) +{ + return Mat33V(V3Merge(V3GetX(a.col0), V3GetX(a.col1), V3GetX(a.col2)), + V3Merge(V3GetY(a.col0), V3GetY(a.col1), V3GetY(a.col2)), + V3Merge(V3GetZ(a.col0), V3GetZ(a.col1), V3GetZ(a.col2))); +} + +////////////////////////////////// +// MAT44V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V M44MulV4(const Mat44V& a, const Vec4V b) +{ + const FloatV x = V4GetX(b); + const FloatV y = V4GetY(b); + const FloatV z = V4GetZ(b); + const FloatV w = V4GetW(b); + + const Vec4V v0 = V4Scale(a.col0, x); + const Vec4V v1 = V4Scale(a.col1, y); + const Vec4V v2 = V4Scale(a.col2, z); + const Vec4V v3 = V4Scale(a.col3, w); + const Vec4V v0PlusV1 = V4Add(v0, v1); + const Vec4V v0PlusV1Plusv2 = V4Add(v0PlusV1, v2); + return V4Add(v0PlusV1Plusv2, v3); +} + +PX_FORCE_INLINE Vec4V M44TrnspsMulV4(const Mat44V& a, const Vec4V b) +{ + PX_ALIGN(16, FloatV) dotProdArray[4] = { V4Dot(a.col0, b), V4Dot(a.col1, b), V4Dot(a.col2, b), V4Dot(a.col3, b) }; + return V4Merge(dotProdArray); +} + +PX_FORCE_INLINE Mat44V M44MulM44(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(M44MulV4(a, b.col0), M44MulV4(a, b.col1), M44MulV4(a, b.col2), M44MulV4(a, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Add(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(V4Add(a.col0, b.col0), V4Add(a.col1, b.col1), V4Add(a.col2, b.col2), V4Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Trnsps(const Mat44V& a) +{ + const Vec4V v0 = _mm_unpacklo_ps(a.col0, a.col2); + const Vec4V v1 = _mm_unpackhi_ps(a.col0, a.col2); + const Vec4V v2 = _mm_unpacklo_ps(a.col1, a.col3); + const Vec4V v3 = _mm_unpackhi_ps(a.col1, a.col3); + return Mat44V(_mm_unpacklo_ps(v0, v2), _mm_unpackhi_ps(v0, v2), _mm_unpacklo_ps(v1, v3), _mm_unpackhi_ps(v1, v3)); +} + +PX_FORCE_INLINE Mat44V M44Inverse(const Mat44V& a) +{ + __m128 minor0, minor1, minor2, minor3; + __m128 row0, row1, row2, row3; + __m128 det, tmp1; + + tmp1 = V4Zero(); + row1 = V4Zero(); + row3 = V4Zero(); + + row0 = a.col0; + row1 = _mm_shuffle_ps(a.col1, a.col1, _MM_SHUFFLE(1, 0, 3, 2)); + row2 = a.col2; + row3 = _mm_shuffle_ps(a.col3, a.col3, _MM_SHUFFLE(1, 0, 3, 2)); + + tmp1 = _mm_mul_ps(row2, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_mul_ps(row1, tmp1); + minor1 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0); + minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1); + minor1 = _mm_shuffle_ps(minor1, minor1, 0x4E); + + tmp1 = _mm_mul_ps(row1, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0); + minor3 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3); + minor3 = _mm_shuffle_ps(minor3, minor3, 0x4E); + + tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, 0x4E), row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + row2 = _mm_shuffle_ps(row2, row2, 0x4E); + minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0); + minor2 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2); + minor2 = _mm_shuffle_ps(minor2, minor2, 0x4E); + + tmp1 = _mm_mul_ps(row0, row1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1)); + + tmp1 = _mm_mul_ps(row0, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1); + minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1)); + + tmp1 = _mm_mul_ps(row0, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1)); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3); + + det = _mm_mul_ps(row0, minor0); + det = _mm_add_ps(_mm_shuffle_ps(det, det, 0x4E), det); + det = _mm_add_ss(_mm_shuffle_ps(det, det, 0xB1), det); + tmp1 = _mm_rcp_ss(det); +#if 0 + det = _mm_sub_ss(_mm_add_ss(tmp1, tmp1), _mm_mul_ss(det, _mm_mul_ss(tmp1, tmp1))); + det = _mm_shuffle_ps(det, det, 0x00); +#else + det = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(0, 0, 0, 0)); +#endif + + minor0 = _mm_mul_ps(det, minor0); + minor1 = _mm_mul_ps(det, minor1); + minor2 = _mm_mul_ps(det, minor2); + minor3 = _mm_mul_ps(det, minor3); + Mat44V invTrans(minor0, minor1, minor2, minor3); + return M44Trnsps(invTrans); +} + +PX_FORCE_INLINE Vec4V V4LoadXYZW(const PxF32& x, const PxF32& y, const PxF32& z, const PxF32& w) +{ + return _mm_set_ps(w, z, y, x); +} + +/* +// AP: work in progress - use proper SSE intrinsics where possible +PX_FORCE_INLINE VecU16V V4U32PK(VecU32V a, VecU32V b) +{ + VecU16V result; + result.m128_u16[0] = PxU16(PxClamp((a).m128_u32[0], 0, 0xFFFF)); + result.m128_u16[1] = PxU16(PxClamp((a).m128_u32[1], 0, 0xFFFF)); + result.m128_u16[2] = PxU16(PxClamp((a).m128_u32[2], 0, 0xFFFF)); + result.m128_u16[3] = PxU16(PxClamp((a).m128_u32[3], 0, 0xFFFF)); + result.m128_u16[4] = PxU16(PxClamp((b).m128_u32[0], 0, 0xFFFF)); + result.m128_u16[5] = PxU16(PxClamp((b).m128_u32[1], 0, 0xFFFF)); + result.m128_u16[6] = PxU16(PxClamp((b).m128_u32[2], 0, 0xFFFF)); + result.m128_u16[7] = PxU16(PxClamp((b).m128_u32[3], 0, 0xFFFF)); + return result; +} +*/ + +PX_FORCE_INLINE VecU32V V4U32Sel(const BoolV c, const VecU32V a, const VecU32V b) +{ + return m128_I2F(_mm_or_si128(_mm_andnot_si128(m128_F2I(c), m128_F2I(b)), _mm_and_si128(m128_F2I(c), m128_F2I(a)))); +} + +PX_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_or_si128(m128_F2I(a), m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32xor(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_xor_si128(m128_F2I(a), m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_and_si128(m128_F2I(a), m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b) +{ + return m128_I2F(_mm_andnot_si128(m128_F2I(b), m128_F2I(a))); +} + +/* +PX_FORCE_INLINE VecU16V V4U16Or(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_or_si128(m128_F2I(a), m128_F2I(b))); +} +*/ + +/* +PX_FORCE_INLINE VecU16V V4U16And(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_and_si128(m128_F2I(a), m128_F2I(b))); +} +*/ + +/* +PX_FORCE_INLINE VecU16V V4U16Andc(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_andnot_si128(m128_F2I(b), m128_F2I(a))); +} +*/ + +PX_FORCE_INLINE VecI32V I4Load(const PxI32 i) +{ + return m128_F2I(_mm_load1_ps(reinterpret_cast(&i))); +} + +PX_FORCE_INLINE VecI32V I4LoadU(const PxI32* i) +{ + return m128_F2I(_mm_loadu_ps(reinterpret_cast(i))); +} + +PX_FORCE_INLINE VecI32V I4LoadA(const PxI32* i) +{ + return m128_F2I(_mm_load_ps(reinterpret_cast(i))); +} + +PX_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_add_epi32(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_sub_epi32(a, b); +} + +PX_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_cmpgt_epi32(a, b)); +} + +PX_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b) +{ + return m128_I2F(_mm_cmpeq_epi32(a, b)); +} + +PX_FORCE_INLINE VecI32V V4I32Sel(const BoolV c, const VecI32V a, const VecI32V b) +{ + return _mm_or_si128(_mm_andnot_si128(m128_F2I(c), b), _mm_and_si128(m128_F2I(c), a)); +} + +PX_FORCE_INLINE VecI32V VecI32V_Zero() +{ + return _mm_setzero_si128(); +} + +PX_FORCE_INLINE VecI32V VecI32V_One() +{ + return I4Load(1); +} + +PX_FORCE_INLINE VecI32V VecI32V_Two() +{ + return I4Load(2); +} + +PX_FORCE_INLINE VecI32V VecI32V_MinusOne() +{ + return I4Load(-1); +} + +PX_FORCE_INLINE VecU32V U4Zero() +{ + return U4Load(0); +} + +PX_FORCE_INLINE VecU32V U4One() +{ + return U4Load(1); +} + +PX_FORCE_INLINE VecU32V U4Two() +{ + return U4Load(2); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sel(const BoolV c, const VecI32VArg a, const VecI32VArg b) +{ + return _mm_or_si128(_mm_andnot_si128(m128_F2I(c), b), _mm_and_si128(m128_F2I(c), a)); +} + +PX_FORCE_INLINE VecShiftV VecI32V_PrepareShift(const VecI32VArg shift) +{ + VecShiftV s; + s.shift = VecI32V_Sel(BTFFF(), shift, VecI32V_Zero()); + return s; +} + +PX_FORCE_INLINE VecI32V VecI32V_LeftShift(const VecI32VArg a, const VecShiftVArg count) +{ + return _mm_sll_epi32(a, count.shift); +} + +PX_FORCE_INLINE VecI32V VecI32V_RightShift(const VecI32VArg a, const VecShiftVArg count) +{ + return _mm_srl_epi32(a, count.shift); +} + +PX_FORCE_INLINE VecI32V VecI32V_And(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_and_si128(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Or(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_or_si128(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetX(const VecI32VArg a) +{ + return m128_F2I(_mm_shuffle_ps(m128_I2F(a), m128_I2F(a), _MM_SHUFFLE(0, 0, 0, 0))); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetY(const VecI32VArg a) +{ + return m128_F2I(_mm_shuffle_ps(m128_I2F(a), m128_I2F(a), _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetZ(const VecI32VArg a) +{ + return m128_F2I(_mm_shuffle_ps(m128_I2F(a), m128_I2F(a), _MM_SHUFFLE(2, 2, 2, 2))); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetW(const VecI32VArg a) +{ + return m128_F2I(_mm_shuffle_ps(m128_I2F(a), m128_I2F(a), _MM_SHUFFLE(3, 3, 3, 3))); +} + +PX_FORCE_INLINE void PxI32_From_VecI32V(const VecI32VArg a, PxI32* i) +{ + _mm_store_ss(reinterpret_cast(i), m128_I2F(a)); +} + +PX_FORCE_INLINE VecI32V VecI32V_Merge(const VecI32VArg x, const VecI32VArg y, const VecI32VArg z, const VecI32VArg w) +{ + const __m128 xw = _mm_move_ss(m128_I2F(y), m128_I2F(x)); // y, y, y, x + const __m128 yz = _mm_move_ss(m128_I2F(z), m128_I2F(w)); // z, z, z, w + return m128_F2I(_mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0))); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_BoolV(const BoolVArg a) +{ + return m128_F2I(a); +} + +PX_FORCE_INLINE VecU32V VecU32V_From_BoolV(const BoolVArg a) +{ + return a; +} + +/* +template PX_FORCE_INLINE VecI32V V4ISplat() +{ + VecI32V result; + result.m128_i32[0] = a; + result.m128_i32[1] = a; + result.m128_i32[2] = a; + result.m128_i32[3] = a; + return result; +} + +template PX_FORCE_INLINE VecU32V V4USplat() +{ + VecU32V result; + result.m128_u32[0] = a; + result.m128_u32[1] = a; + result.m128_u32[2] = a; + result.m128_u32[3] = a; + return result; +} +*/ + +/* +PX_FORCE_INLINE void V4U16StoreAligned(VecU16V val, VecU16V* address) +{ + *address = val; +} +*/ + +PX_FORCE_INLINE void V4U32StoreAligned(VecU32V val, VecU32V* address) +{ + *address = val; +} + +PX_FORCE_INLINE Vec4V V4LoadAligned(Vec4V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE Vec4V V4LoadUnaligned(Vec4V* addr) +{ + return V4LoadU(reinterpret_cast(addr)); +} + +PX_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b) +{ + VecU32V result32(a); + result32 = V4U32Andc(result32, b); + return Vec4V(result32); +} + +PX_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b) +{ + return V4IsGrtr(a, b); +} + +PX_FORCE_INLINE VecU16V V4U16LoadAligned(VecU16V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE VecU16V V4U16LoadUnaligned(VecU16V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE VecU16V V4U16CompareGt(VecU16V a, VecU16V b) +{ + // _mm_cmpgt_epi16 doesn't work for unsigned values unfortunately + // return m128_I2F(_mm_cmpgt_epi16(m128_F2I(a), m128_F2I(b))); + VecU16V result; + result.m128_u16[0] = (a).m128_u16[0] > (b).m128_u16[0]; + result.m128_u16[1] = (a).m128_u16[1] > (b).m128_u16[1]; + result.m128_u16[2] = (a).m128_u16[2] > (b).m128_u16[2]; + result.m128_u16[3] = (a).m128_u16[3] > (b).m128_u16[3]; + result.m128_u16[4] = (a).m128_u16[4] > (b).m128_u16[4]; + result.m128_u16[5] = (a).m128_u16[5] > (b).m128_u16[5]; + result.m128_u16[6] = (a).m128_u16[6] > (b).m128_u16[6]; + result.m128_u16[7] = (a).m128_u16[7] > (b).m128_u16[7]; + return result; +} + +PX_FORCE_INLINE VecU16V V4I16CompareGt(VecU16V a, VecU16V b) +{ + return m128_I2F(_mm_cmpgt_epi16(m128_F2I(a), m128_F2I(b))); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a) +{ + Vec4V result = V4LoadXYZW(PxF32(a.m128_u32[0]), PxF32(a.m128_u32[1]), PxF32(a.m128_u32[2]), PxF32(a.m128_u32[3])); + return result; +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecI32V(VecI32V in) +{ + return _mm_cvtepi32_ps(in); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_Vec4V(Vec4V a) +{ + return _mm_cvttps_epi32(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecU32V(VecU32V a) +{ + return Vec4V(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecI32V(VecI32V a) +{ + return m128_I2F(a); +} + +PX_FORCE_INLINE VecU32V VecU32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return VecU32V(a); +} + +PX_FORCE_INLINE VecI32V VecI32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return m128_F2I(a); +} + +/* +template PX_FORCE_INLINE BoolV BSplatElement(BoolV a) +{ + BoolV result; + result[0] = result[1] = result[2] = result[3] = a[index]; + return result; +} +*/ + +template +BoolV BSplatElement(BoolV a) +{ + float* data = reinterpret_cast(&a); + return V4Load(data[index]); +} + +template +PX_FORCE_INLINE VecU32V V4U32SplatElement(VecU32V a) +{ + VecU32V result; + result.m128_u32[0] = result.m128_u32[1] = result.m128_u32[2] = result.m128_u32[3] = a.m128_u32[index]; + return result; +} + +template +PX_FORCE_INLINE Vec4V V4SplatElement(Vec4V a) +{ + float* data = reinterpret_cast(&a); + return V4Load(data[index]); +} + +PX_FORCE_INLINE VecU32V U4LoadXYZW(PxU32 x, PxU32 y, PxU32 z, PxU32 w) +{ + VecU32V result; + result.m128_u32[0] = x; + result.m128_u32[1] = y; + result.m128_u32[2] = z; + result.m128_u32[3] = w; + return result; +} + +PX_FORCE_INLINE Vec4V V4Ceil(const Vec4V in) +{ + UnionM128 a(in); + return V4LoadXYZW(PxCeil(a.m128_f32[0]), PxCeil(a.m128_f32[1]), PxCeil(a.m128_f32[2]), PxCeil(a.m128_f32[3])); +} + +PX_FORCE_INLINE Vec4V V4Floor(const Vec4V in) +{ + UnionM128 a(in); + return V4LoadXYZW(PxFloor(a.m128_f32[0]), PxFloor(a.m128_f32[1]), PxFloor(a.m128_f32[2]), PxFloor(a.m128_f32[3])); +} + +PX_FORCE_INLINE VecU32V V4ConvertToU32VSaturate(const Vec4V in, PxU32 power) +{ + PX_ASSERT(power == 0 && "Non-zero power not supported in convertToU32VSaturate"); + PX_UNUSED(power); // prevent warning in release builds + PxF32 ffffFFFFasFloat = PxF32(0xFFFF0000); + UnionM128 a(in); + VecU32V result; + result.m128_u32[0] = PxU32(PxClamp((a).m128_f32[0], 0.0f, ffffFFFFasFloat)); + result.m128_u32[1] = PxU32(PxClamp((a).m128_f32[1], 0.0f, ffffFFFFasFloat)); + result.m128_u32[2] = PxU32(PxClamp((a).m128_f32[2], 0.0f, ffffFFFFasFloat)); + result.m128_u32[3] = PxU32(PxClamp((a).m128_f32[3], 0.0f, ffffFFFFasFloat)); + return result; +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSUNIXSSE2INLINEAOS_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h new file mode 100644 index 000000000..2a2d71063 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSAOS_H +#define PSFOUNDATION_PSWINDOWSAOS_H + +// no includes here! this file should be included from PxcVecMath.h only!!! + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +typedef __m128 FloatV; +typedef __m128 Vec3V; +typedef __m128 Vec4V; +typedef __m128 BoolV; +typedef __m128 VecU32V; +typedef __m128 VecI32V; +typedef __m128 VecU16V; +typedef __m128 VecI16V; +typedef __m128 QuatV; + +#define FloatVArg FloatV & +#define Vec3VArg Vec3V & +#define Vec4VArg Vec4V & +#define BoolVArg BoolV & +#define VecU32VArg VecU32V & +#define VecI32VArg VecI32V & +#define VecU16VArg VecU16V & +#define VecI16VArg VecI16V & +#define QuatVArg QuatV & + +// Optimization for situations in which you cross product multiple vectors with the same vector. +// Avoids 2X shuffles per product +struct VecCrossV +{ + Vec3V mL1; + Vec3V mR1; +}; + +struct VecShiftV +{ + VecI32V shift; +}; +#define VecShiftVArg VecShiftV & + +PX_ALIGN_PREFIX(16) +struct Mat33V +{ + Mat33V() + { + } + Mat33V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat34V +{ + Mat34V() + { + } + Mat34V(const Vec3V& c0, const Vec3V& c1, const Vec3V& c2, const Vec3V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec3V PX_ALIGN(16, col0); + Vec3V PX_ALIGN(16, col1); + Vec3V PX_ALIGN(16, col2); + Vec3V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat43V +{ + Mat43V() + { + } + Mat43V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2) : col0(c0), col1(c1), col2(c2) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct Mat44V +{ + Mat44V() + { + } + Mat44V(const Vec4V& c0, const Vec4V& c1, const Vec4V& c2, const Vec4V& c3) : col0(c0), col1(c1), col2(c2), col3(c3) + { + } + Vec4V PX_ALIGN(16, col0); + Vec4V PX_ALIGN(16, col1); + Vec4V PX_ALIGN(16, col2); + Vec4V PX_ALIGN(16, col3); +} PX_ALIGN_SUFFIX(16); + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSWINDOWSAOS_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h new file mode 100644 index 000000000..019bfc9a0 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h @@ -0,0 +1,51 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSFPU_H +#define PSFOUNDATION_PSWINDOWSFPU_H + +PX_INLINE physx::shdfnd::SIMDGuard::SIMDGuard() +{ +#if !PX_ARM + mControlWord = _mm_getcsr(); + // set default (disable exceptions: _MM_MASK_MASK) and FTZ (_MM_FLUSH_ZERO_ON), DAZ (_MM_DENORMALS_ZERO_ON: (1<<6)) + _mm_setcsr(_MM_MASK_MASK | _MM_FLUSH_ZERO_ON | (1 << 6)); +#endif +} + +PX_INLINE physx::shdfnd::SIMDGuard::~SIMDGuard() +{ +#if !PX_ARM + // restore control word and clear any exception flags + // (setting exception state flags cause exceptions on the first following fp operation) + _mm_setcsr(mControlWord & ~_MM_EXCEPT_MASK); +#endif +} + +#endif // #ifndef PSFOUNDATION_PSWINDOWSFPU_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h new file mode 100644 index 000000000..4dab60145 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h @@ -0,0 +1,104 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSINCLUDE_H +#define PSFOUNDATION_PSWINDOWSINCLUDE_H + +#include "Ps.h" + +#ifndef _WIN32 +#error "This file should only be included by Windows builds!!" +#endif + +#ifdef _WINDOWS_ // windows already included +#error "Only include windows.h through this file!!" +#endif + +// We only support >= Windows XP, and we need this for critical section and +// Setting this hides some important APIs (e.g. LoadPackagedLibrary), so don't do it +#if !PX_UWP +#define _WIN32_WINNT 0x0501 +#else +#define _WIN32_WINNT 0x0602 +#endif + +// turn off as much as we can for windows. All we really need is the thread functions(critical sections/Interlocked* +// etc) +#define NOGDICAPMASKS +#define NOVIRTUALKEYCODES +#define NOWINMESSAGES +#define NOWINSTYLES +#define NOSYSMETRICS +#define NOMENUS +#define NOICONS +#define NOKEYSTATES +#define NOSYSCOMMANDS +#define NORASTEROPS +#define NOSHOWWINDOW +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCTLMGR +#define NODRAWTEXT +#define NOGDI +#define NOMB +#define NOMEMMGR +#define NOMETAFILE +#define NOMINMAX +#define NOOPENFILE +#define NOSCROLL +#define NOSERVICE +#define NOSOUND +#define NOTEXTMETRIC +#define NOWH +#define NOWINOFFSETS +#define NOCOMM +#define NOKANJI +#define NOHELP +#define NOPROFILER +#define NODEFERWINDOWPOS +#define NOMCX +#define WIN32_LEAN_AND_MEAN +// We need a slightly wider API surface for e.g. MultiByteToWideChar +#if !PX_UWP +#define NOUSER +#define NONLS +#define NOMSG +#endif + +#pragma warning(push) +#pragma warning(disable : 4668) //'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#include +#pragma warning(pop) + +#if PX_SSE2 +#include +#endif + +#endif // #ifndef PSFOUNDATION_PSWINDOWSINCLUDE_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h new file mode 100644 index 000000000..887535d4a --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h @@ -0,0 +1,3132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSINLINEAOS_H +#define PSFOUNDATION_PSWINDOWSINLINEAOS_H + +#if !COMPILE_VECTOR_INTRINSICS +#error Vector intrinsics should not be included when using scalar implementation. +#endif + +#include "../PsVecMathSSE.h" + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +////////////////////////////////////////////////////////////////////// +//Test that Vec3V and FloatV are legal +////////////////////////////////////////////////////////////////////// + +#define FLOAT_COMPONENTS_EQUAL_THRESHOLD 0.01f +PX_FORCE_INLINE bool isValidFloatV(const FloatV a) +{ + const PxF32 x = V4ReadX(a); + const PxF32 y = V4ReadY(a); + const PxF32 z = V4ReadZ(a); + const PxF32 w = V4ReadW(a); + + if ( + (PxAbs(x - y) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs(x - z) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs(x - w) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + ) + { + return true; + } + + if ( + (PxAbs((x - y) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs((x - z) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) && + (PxAbs((x - w) / x) < FLOAT_COMPONENTS_EQUAL_THRESHOLD) + ) + { + return true; + } + return false; +} + +PX_FORCE_INLINE bool isValidVec3V(const Vec3V a) +{ + //using _mm_comieq_ss to do the comparison doesn't work for NaN. + PX_ALIGN(16, PxF32 f[4]); + V4StoreA((const Vec4V&)a, f); + return f[3] == 0.0f; +} + +PX_FORCE_INLINE bool isFiniteLength(const Vec3V a) +{ + return !FAllEq(V4LengthSq(a), FZero()); +} + +PX_FORCE_INLINE bool isAligned16(void* a) +{ + return(0 == ((size_t)a & 0x0f)); +} + +//ASSERT_FINITELENGTH is deactivated because there is a lot of code that calls a simd normalisation function with zero length but then ignores the result. + +#if PX_DEBUG +#define ASSERT_ISVALIDVEC3V(a) PX_ASSERT(isValidVec3V(a)) +#define ASSERT_ISVALIDFLOATV(a) PX_ASSERT(isValidFloatV(a)) +#define ASSERT_ISALIGNED16(a) PX_ASSERT(isAligned16((void*)a)) +#define ASSERT_ISFINITELENGTH(a) //PX_ASSERT(isFiniteLength(a)) +#else +#define ASSERT_ISVALIDVEC3V(a) +#define ASSERT_ISVALIDFLOATV(a) +#define ASSERT_ISALIGNED16(a) +#define ASSERT_ISFINITELENGTH(a) +#endif +///////////////////////////////////////////////////////////////////// +////FUNCTIONS USED ONLY FOR ASSERTS IN VECTORISED IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// USED ONLY INTERNALLY +////////////////////////////////////////////////////////////////////// + +namespace internalWindowsSimd +{ +PX_FORCE_INLINE __m128 m128_I2F(__m128i n) +{ + return _mm_castsi128_ps(n); +} + +PX_FORCE_INLINE __m128i m128_F2I(__m128 n) +{ + return _mm_castps_si128(n); +} + +PX_FORCE_INLINE PxU32 BAllTrue4_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32(moveMask == 0xf); +} + +PX_FORCE_INLINE PxU32 BAllTrue3_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32((moveMask & 0x7) == 0x7); +} + +PX_FORCE_INLINE PxU32 BAnyTrue4_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32(moveMask != 0x0); +} + +PX_FORCE_INLINE PxU32 BAnyTrue3_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32(((moveMask & 0x7) != 0x0)); +} + +PX_FORCE_INLINE PxU32 FiniteTestEq(const Vec4V a, const Vec4V b) +{ + // This is a bit of a bodge. + //_mm_comieq_ss returns 1 if either value is nan so we need to re-cast a and b with true encoded as a non-nan + // number. + // There must be a better way of doing this in sse. + const BoolV one = FOne(); + const BoolV zero = FZero(); + const BoolV a1 = V4Sel(a, one, zero); + const BoolV b1 = V4Sel(b, one, zero); + return (PxU32( + _mm_comieq_ss(a1, b1) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(1, 1, 1, 1)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(1, 1, 1, 1))) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(2, 2, 2, 2)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(2, 2, 2, 2))) && + _mm_comieq_ss(_mm_shuffle_ps(a1, a1, _MM_SHUFFLE(3, 3, 3, 3)), _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(3, 3, 3, 3))))); +} + +PX_FORCE_INLINE bool hasZeroElementinFloatV(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) ? true : false; +} + +PX_FORCE_INLINE bool hasZeroElementInVec3V(const Vec3V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FZero())); +} + +PX_FORCE_INLINE bool hasZeroElementInVec4V(const Vec4V a) +{ + return (_mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)), FZero()) || + _mm_comieq_ss(_mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)), FZero())); +} + +const PX_ALIGN(16, PxU32 gMaskXYZ[4]) = { 0xffffffff, 0xffffffff, 0xffffffff, 0 }; +} //internalWindowsSimd + +namespace _VecMathTests +{ +// PT: this function returns an invalid Vec3V (W!=0.0f) just for unit-testing 'isValidVec3V' +PX_FORCE_INLINE Vec3V getInvalidVec3V() +{ + const float f = 1.0f; + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE bool allElementsEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_comieq_ss(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec3V(const Vec3V a, const Vec3V b) +{ + return V3AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVec4V(const Vec4V a, const Vec4V b) +{ + return V4AllEq(a, b) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualBoolV(const BoolV a, const BoolV b) +{ + return internalWindowsSimd::BAllTrue4_R(VecI32V_IsEq(a, b)) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVecU32V(const VecU32V a, const VecU32V b) +{ + return internalWindowsSimd::BAllTrue4_R(V4IsEqU32(a, b)) != 0; +} + +PX_FORCE_INLINE bool allElementsEqualVecI32V(const VecI32V a, const VecI32V b) +{ + BoolV c = internalWindowsSimd::m128_I2F( + _mm_cmpeq_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); + return internalWindowsSimd::BAllTrue4_R(c) != 0; +} + +#define VECMATH_AOS_EPSILON (1e-3f) +static const FloatV minFError = FLoad(-VECMATH_AOS_EPSILON); +static const FloatV maxFError = FLoad(VECMATH_AOS_EPSILON); +static const Vec3V minV3Error = V3Load(-VECMATH_AOS_EPSILON); +static const Vec3V maxV3Error = V3Load(VECMATH_AOS_EPSILON); +static const Vec4V minV4Error = V4Load(-VECMATH_AOS_EPSILON); +static const Vec4V maxV4Error = V4Load(VECMATH_AOS_EPSILON); + +PX_FORCE_INLINE bool allElementsNearEqualFloatV(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + const FloatV c = FSub(a, b); + return _mm_comigt_ss(c, minFError) && _mm_comilt_ss(c, maxFError); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec3V(const Vec3V a, const Vec3V b) +{ + const Vec3V c = V3Sub(a, b); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minV3Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxV3Error) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minV3Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxV3Error) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minV3Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxV3Error)); +} + +PX_FORCE_INLINE bool allElementsNearEqualVec4V(const Vec4V a, const Vec4V b) +{ + const Vec4V c = V4Sub(a, b); + return (_mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), minV4Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0)), maxV4Error) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), minV4Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1)), maxV4Error) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), minV4Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2)), maxV4Error) && + _mm_comigt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), minV4Error) && + _mm_comilt_ss(_mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)), maxV4Error)); +} +} //_VecMathTests + +PX_FORCE_INLINE bool isFiniteFloatV(const FloatV a) +{ + PxF32 f; + FStore(a, &f); + return PxIsFinite(f); + /* + const PxU32 badNumber = (_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF); + const FloatV vBadNum = FloatV_From_F32((PxF32&)badNumber); + const BoolV vMask = BAnd(vBadNum, a); + return FiniteTestEq(vMask, BFFFF()) == 1; + */ +} + +PX_FORCE_INLINE bool isFiniteVec3V(const Vec3V a) +{ + PX_ALIGN(16, PxF32 f[4]); + V4StoreA((Vec4V&)a, f); + return PxIsFinite(f[0]) && PxIsFinite(f[1]) && PxIsFinite(f[2]); + + /* + const PxU32 badNumber = (_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF); + const Vec3V vBadNum = Vec3V_From_F32((PxF32&)badNumber); + const BoolV vMask = BAnd(BAnd(vBadNum, a), BTTTF()); + return FiniteTestEq(vMask, BFFFF()) == 1; + */ +} + +PX_FORCE_INLINE bool isFiniteVec4V(const Vec4V a) +{ + PX_ALIGN(16, PxF32 f[4]); + V4StoreA(a, f); + return PxIsFinite(f[0]) && PxIsFinite(f[1]) && PxIsFinite(f[2]) && PxIsFinite(f[3]); + + /* + const PxU32 badNumber = (_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF); + const Vec4V vBadNum = Vec4V_From_U32((PxF32&)badNumber); + const BoolV vMask = BAnd(vBadNum, a); + + return FiniteTestEq(vMask, BFFFF()) == 1; + */ +} + +///////////////////////////////////////////////////////////////////// +////VECTORISED FUNCTION IMPLEMENTATIONS +///////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE FloatV FLoad(const PxF32 f) +{ + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE Vec3V V3Load(const PxF32 f) +{ + return _mm_set_ps(0.0f, f, f, f); +} + +PX_FORCE_INLINE Vec4V V4Load(const PxF32 f) +{ + return _mm_load1_ps(&f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool f) +{ + const PxU32 i = PxU32(-(PxI32)f); + return _mm_load1_ps((float*)&i); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + return _mm_and_ps(_mm_load_ps(&f.x), reinterpret_cast(internalWindowsSimd::gMaskXYZ)); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxVec3& f) +{ + return _mm_set_ps(0.0f, f.z, f.y, f.x); +} + +// w component of result is undefined +PX_FORCE_INLINE Vec3V V3LoadUnsafeA(const PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + return _mm_load_ps(&f.x); +} + +PX_FORCE_INLINE Vec3V V3LoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(f); + return V4ClearW(_mm_load_ps(f)); +} + +PX_FORCE_INLINE Vec3V V3LoadU(const PxF32* const i) +{ + return _mm_set_ps(0.0f, i[2], i[1], i[0]); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V(Vec4V v) +{ + return V4ClearW(v); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_Vec4V_WUndefined(const Vec4V v) +{ + return v; +} + +PX_FORCE_INLINE Vec4V Vec4V_From_Vec3V(Vec3V f) +{ + return f; // ok if it is implemented as the same type. +} + +PX_FORCE_INLINE Vec4V Vec4V_From_FloatV(FloatV f) +{ + return f; +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV(FloatV f) +{ + return Vec3V_From_Vec4V(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Vec3V Vec3V_From_FloatV_WUndefined(FloatV f) +{ + return Vec3V_From_Vec4V_WUndefined(Vec4V_From_FloatV(f)); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_PxVec3_WUndefined(const PxVec3& f) +{ + return _mm_set_ps(0.0f, f.z, f.y, f.x); +} + +PX_FORCE_INLINE Vec4V V4LoadA(const PxF32* const f) +{ + ASSERT_ISALIGNED16(f); + return _mm_load_ps(f); +} + +PX_FORCE_INLINE void V4StoreA(const Vec4V a, PxF32* f) +{ + ASSERT_ISALIGNED16(f); + _mm_store_ps(f, a); +} + +PX_FORCE_INLINE void V4StoreU(const Vec4V a, PxF32* f) +{ + _mm_storeu_ps(f, a); +} + +PX_FORCE_INLINE void BStoreA(const BoolV a, PxU32* f) +{ + ASSERT_ISALIGNED16(f); + _mm_store_ps((PxF32*)f, a); +} + +PX_FORCE_INLINE void U4StoreA(const VecU32V uv, PxU32* u) +{ + ASSERT_ISALIGNED16(u); + _mm_store_ps((PxF32*)u, uv); +} + +PX_FORCE_INLINE void I4StoreA(const VecI32V iv, PxI32* i) +{ + ASSERT_ISALIGNED16(i); + _mm_store_ps((PxF32*)i, iv); +} + +PX_FORCE_INLINE Vec4V V4LoadU(const PxF32* const f) +{ + return _mm_loadu_ps(f); +} + +PX_FORCE_INLINE BoolV BLoad(const bool* const f) +{ + const PX_ALIGN(16, PxU32 b[4]) = { PxU32(-(PxI32)f[0]), PxU32(-(PxI32)f[1]), + PxU32(-(PxI32)f[2]), PxU32(-(PxI32)f[3]) }; + return _mm_load_ps((float*)&b); +} + +PX_FORCE_INLINE void FStore(const FloatV a, PxF32* PX_RESTRICT f) +{ + ASSERT_ISVALIDFLOATV(a); + _mm_store_ss(f, a); +} + +PX_FORCE_INLINE void V3StoreA(const Vec3V a, PxVec3& f) +{ + ASSERT_ISALIGNED16(&f); + PX_ALIGN(16, PxF32 f2[4]); + _mm_store_ps(f2, a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +PX_FORCE_INLINE void Store_From_BoolV(const BoolV b, PxU32* b2) +{ + _mm_store_ss((PxF32*)b2, b); +} + +PX_FORCE_INLINE void V3StoreU(const Vec3V a, PxVec3& f) +{ + PX_ALIGN(16, PxF32 f2[4]); + _mm_store_ps(f2, a); + f = PxVec3(f2[0], f2[1], f2[2]); +} + +PX_FORCE_INLINE Mat33V Mat33V_From_PxMat33(const PxMat33& m) +{ + return Mat33V(V3LoadU(m.column0), V3LoadU(m.column1), V3LoadU(m.column2)); +} + +PX_FORCE_INLINE void PxMat33_From_Mat33V(const Mat33V& m, PxMat33& out) +{ + ASSERT_ISALIGNED16(&out); + V3StoreU(m.col0, out.column0); + V3StoreU(m.col1, out.column1); + V3StoreU(m.col2, out.column2); +} + +////////////////////////////////// +// FLOATV +////////////////////////////////// + +PX_FORCE_INLINE FloatV FZero() +{ + return _mm_setzero_ps(); +} + +PX_FORCE_INLINE FloatV FOne() +{ + return FLoad(1.0f); +} + +PX_FORCE_INLINE FloatV FHalf() +{ + return FLoad(0.5f); +} + +PX_FORCE_INLINE FloatV FEps() +{ + return FLoad(PX_EPS_REAL); +} + +PX_FORCE_INLINE FloatV FEps6() +{ + return FLoad(1e-6f); +} + +PX_FORCE_INLINE FloatV FMax() +{ + return FLoad(PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV FNegMax() +{ + return FLoad(-PX_MAX_REAL); +} + +PX_FORCE_INLINE FloatV IZero() +{ + const PxU32 zero = 0; + return _mm_load1_ps((PxF32*)&zero); +} + +PX_FORCE_INLINE FloatV IOne() +{ + const PxU32 one = 1; + return _mm_load1_ps((PxF32*)&one); +} + +PX_FORCE_INLINE FloatV ITwo() +{ + const PxU32 two = 2; + return _mm_load1_ps((PxF32*)&two); +} + +PX_FORCE_INLINE FloatV IThree() +{ + const PxU32 three = 3; + return _mm_load1_ps((PxF32*)&three); +} + +PX_FORCE_INLINE FloatV IFour() +{ + const PxU32 four = 4; + return _mm_load1_ps((PxF32*)&four); +} + +PX_FORCE_INLINE FloatV FNeg(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE FloatV FAdd(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE FloatV FSub(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMul(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE FloatV FDiv(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE FloatV FDivFast(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE FloatV FRecip(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_div_ps(FOne(), a); +} + +PX_FORCE_INLINE FloatV FRecipFast(const FloatV a) +{ + return _mm_rcp_ps(a); +} + +PX_FORCE_INLINE FloatV FRsqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_div_ps(FOne(), _mm_sqrt_ps(a)); +} + +PX_FORCE_INLINE FloatV FSqrt(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_sqrt_ps(a); +} + +PX_FORCE_INLINE FloatV FRsqrtFast(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + return _mm_rsqrt_ps(a); +} + +PX_FORCE_INLINE FloatV FScaleAdd(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return FAdd(FMul(a, b), c); +} + +PX_FORCE_INLINE FloatV FNegScaleSub(const FloatV a, const FloatV b, const FloatV c) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDFLOATV(c); + return FSub(c, FMul(a, b)); +} + +PX_FORCE_INLINE FloatV FAbs(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + PX_ALIGN(16, const static PxU32 absMask[4]) = { 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF, 0x7fFFffFF }; + return _mm_and_ps(a, _mm_load_ps((PxF32*)absMask)); +} + +PX_FORCE_INLINE FloatV FSel(const BoolV c, const FloatV a, const FloatV b) +{ + PX_ASSERT(_VecMathTests::allElementsEqualBoolV(c, BTTTT()) || + _VecMathTests::allElementsEqualBoolV(c, BFFFF())); + ASSERT_ISVALIDFLOATV(_mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a))); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV FIsGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV FIsGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV FIsEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMax(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE FloatV FMin(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV FClamp(const FloatV a, const FloatV minV, const FloatV maxV) +{ + ASSERT_ISVALIDFLOATV(minV); + ASSERT_ISVALIDFLOATV(maxV); + return _mm_max_ps(_mm_min_ps(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 FAllGrtr(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return PxU32(_mm_comigt_ss(a, b)); +} + +PX_FORCE_INLINE PxU32 FAllGrtrOrEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return PxU32(_mm_comige_ss(a, b)); +} + +PX_FORCE_INLINE PxU32 FAllEq(const FloatV a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(b); + return PxU32(_mm_comieq_ss(a, b)); +} + +PX_FORCE_INLINE FloatV FRound(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + // return _mm_round_ps(a, 0x0); + const FloatV half = FLoad(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const FloatV aRound = FSub(FAdd(a, half), signBit); + __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +} + +PX_FORCE_INLINE FloatV FSin(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = V4LoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V3 = FMul(V2, V1); + const FloatV V5 = FMul(V3, V2); + const FloatV V7 = FMul(V5, V2); + const FloatV V9 = FMul(V7, V2); + const FloatV V11 = FMul(V9, V2); + const FloatV V13 = FMul(V11, V2); + const FloatV V15 = FMul(V13, V2); + const FloatV V17 = FMul(V15, V2); + const FloatV V19 = FMul(V17, V2); + const FloatV V21 = FMul(V19, V2); + const FloatV V23 = FMul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + FloatV Result; + Result = FScaleAdd(S1, V3, V1); + Result = FScaleAdd(S2, V5, Result); + Result = FScaleAdd(S3, V7, Result); + Result = FScaleAdd(S4, V9, Result); + Result = FScaleAdd(S5, V11, Result); + Result = FScaleAdd(S6, V13, Result); + Result = FScaleAdd(S7, V15, Result); + Result = FScaleAdd(S8, V17, Result); + Result = FScaleAdd(S9, V19, Result); + Result = FScaleAdd(S10, V21, Result); + Result = FScaleAdd(S11, V23, Result); + + return Result; +} + +PX_FORCE_INLINE FloatV FCos(const FloatV a) +{ + ASSERT_ISVALIDFLOATV(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const FloatV recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = V4LoadA(g_PXTwoPi.f); + const FloatV tmp = FMul(a, recipTwoPi); + const FloatV b = FRound(tmp); + const FloatV V1 = FNegScaleSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const FloatV V2 = FMul(V1, V1); + const FloatV V4 = FMul(V2, V2); + const FloatV V6 = FMul(V4, V2); + const FloatV V8 = FMul(V4, V4); + const FloatV V10 = FMul(V6, V4); + const FloatV V12 = FMul(V6, V6); + const FloatV V14 = FMul(V8, V6); + const FloatV V16 = FMul(V8, V8); + const FloatV V18 = FMul(V10, V8); + const FloatV V20 = FMul(V10, V10); + const FloatV V22 = FMul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + FloatV Result; + Result = FScaleAdd(C1, V2, V4One()); + Result = FScaleAdd(C2, V4, Result); + Result = FScaleAdd(C3, V6, Result); + Result = FScaleAdd(C4, V8, Result); + Result = FScaleAdd(C5, V10, Result); + Result = FScaleAdd(C6, V12, Result); + Result = FScaleAdd(C7, V14, Result); + Result = FScaleAdd(C8, V16, Result); + Result = FScaleAdd(C9, V18, Result); + Result = FScaleAdd(C10, V20, Result); + Result = FScaleAdd(C11, V22, Result); + + return Result; +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max); + const BoolV c = BOr(FIsGrtr(a, max), FIsGrtr(min, a)); + return PxU32(!BAllEqFFFF(c)); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV min, const FloatV max) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(min); + ASSERT_ISVALIDFLOATV(max); + const BoolV c = BAnd(FIsGrtrOrEq(a, min), FIsGrtrOrEq(max, a)); + return BAllEqTTTT(c); +} + +PX_FORCE_INLINE PxU32 FOutOfBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + return FOutOfBounds(a, FNeg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 FInBounds(const FloatV a, const FloatV bounds) +{ + ASSERT_ISVALIDFLOATV(a); + ASSERT_ISVALIDFLOATV(bounds); + return FInBounds(a, FNeg(bounds), bounds); +} + +////////////////////////////////// +// VEC3V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V V3Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + const __m128 zero = V3Zero(); + const __m128 fff0 = _mm_move_ss(f, zero); + return _mm_shuffle_ps(fff0, fff0, _MM_SHUFFLE(0, 1, 2, 3)); +} + +PX_FORCE_INLINE Vec3V V3Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + // static on zero causes compiler crash on x64 debug_opt + const __m128 zero = V3Zero(); + const __m128 xy = _mm_move_ss(x, y); + const __m128 z0 = _mm_move_ss(zero, z); + + return _mm_shuffle_ps(xy, z0, _MM_SHUFFLE(1, 0, 0, 1)); +} + +PX_FORCE_INLINE Vec3V V3UnitX() +{ + const PX_ALIGN(16, PxF32 x[4]) = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +PX_FORCE_INLINE Vec3V V3UnitY() +{ + const PX_ALIGN(16, PxF32 y[4]) = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +PX_FORCE_INLINE Vec3V V3UnitZ() +{ + const PX_ALIGN(16, PxF32 z[4]) = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +PX_FORCE_INLINE FloatV V3GetX(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE FloatV V3GetY(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE FloatV V3GetZ(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE Vec3V V3SetX(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3SetY(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3SetZ(const Vec3V v, const FloatV f) +{ + ASSERT_ISVALIDVEC3V(v); + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE Vec3V V3ColX(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 0, 3, 0)); + return V3SetY(r, V3GetX(b)); +} + +PX_FORCE_INLINE Vec3V V3ColY(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 1, 3, 1)); + return V3SetY(r, V3GetY(b)); +} + +PX_FORCE_INLINE Vec3V V3ColZ(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + Vec3V r = _mm_shuffle_ps(a, c, _MM_SHUFFLE(3, 2, 3, 2)); + return V3SetY(r, V3GetZ(b)); +} + +PX_FORCE_INLINE Vec3V V3Zero() +{ + return _mm_setzero_ps(); +} + +PX_FORCE_INLINE Vec3V V3One() +{ + return V3Load(1.0f); +} + +PX_FORCE_INLINE Vec3V V3Eps() +{ + return V3Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec3V V3Neg(const Vec3V f) +{ + ASSERT_ISVALIDVEC3V(f); + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE Vec3V V3Add(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Sub(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Scale(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Mul(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3ScaleInv(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Div(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return V4ClearW(_mm_div_ps(a, b)); +} + +PX_FORCE_INLINE Vec3V V3ScaleInvFast(const Vec3V a, const FloatV b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec3V V3DivFast(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return V4ClearW(_mm_mul_ps(a, _mm_rcp_ps(b))); +} + +PX_FORCE_INLINE Vec3V V3Recip(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_div_ps(V3One(), a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3RecipFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rcp_ps(a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3Rsqrt(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_div_ps(V3One(), _mm_sqrt_ps(a)); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3RsqrtFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 tttf = BTTTF(); + const __m128 recipA = _mm_rsqrt_ps(a); + return V4Sel(tttf, recipA, zero); +} + +PX_FORCE_INLINE Vec3V V3ScaleAdd(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + return V3Add(V3Scale(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegScaleSub(const Vec3V a, const FloatV b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDFLOATV(b); + ASSERT_ISVALIDVEC3V(c); + return V3Sub(c, V3Scale(a, b)); +} + +PX_FORCE_INLINE Vec3V V3MulAdd(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return V3Add(V3Mul(a, b), c); +} + +PX_FORCE_INLINE Vec3V V3NegMulSub(const Vec3V a, const Vec3V b, const Vec3V c) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + ASSERT_ISVALIDVEC3V(c); + return V3Sub(c, V3Mul(a, b)); +} + +PX_FORCE_INLINE Vec3V V3Abs(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return V3Max(a, V3Neg(a)); +} + +PX_FORCE_INLINE FloatV V3Dot(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + + const __m128 t0 = _mm_mul_ps(a, b); // aw*bw | az*bz | ay*by | ax*bx + const __m128 t1 = _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(1,0,3,2)); // ay*by | ax*bx | aw*bw | az*bz + const __m128 t2 = _mm_add_ps(t0, t1); // ay*by + aw*bw | ax*bx + az*bz | aw*bw + ay*by | az*bz + ax*bx + const __m128 t3 = _mm_shuffle_ps(t2, t2, _MM_SHUFFLE(2,3,0,1)); // ax*bx + az*bz | ay*by + aw*bw | az*bz + ax*bx | aw*bw + ay*by + return _mm_add_ps(t3, t2); // ax*bx + az*bz + ay*by + aw*bw + // ay*by + aw*bw + ax*bx + az*bz + // az*bz + ax*bx + aw*bw + ay*by + // aw*bw + ay*by + az*bz + ax*bx +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + const __m128 r1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(l1, l2), _mm_mul_ps(r1, r2)); +} + +PX_FORCE_INLINE VecCrossV V3PrepareCross(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + VecCrossV v; + v.mR1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + v.mL1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + return v; +} + +PX_FORCE_INLINE Vec3V V3Cross(const VecCrossV& a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(b); + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(a.mL1, l2), _mm_mul_ps(a.mR1, r2)); +} + +PX_FORCE_INLINE Vec3V V3Cross(const Vec3V a, const VecCrossV& b) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 r2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(b.mR1, r2), _mm_mul_ps(b.mL1, l2)); +} + +PX_FORCE_INLINE Vec3V V3Cross(const VecCrossV& a, const VecCrossV& b) +{ + return _mm_sub_ps(_mm_mul_ps(a.mL1, b.mR1), _mm_mul_ps(a.mR1, b.mL1)); +} + +PX_FORCE_INLINE FloatV V3Length(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_sqrt_ps(V3Dot(a, a)); +} + +PX_FORCE_INLINE FloatV V3LengthSq(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return V3Dot(a, a); +} + +PX_FORCE_INLINE Vec3V V3Normalize(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISFINITELENGTH(a); + return V3ScaleInv(a, _mm_sqrt_ps(V3Dot(a, a))); +} + +PX_FORCE_INLINE Vec3V V3NormalizeFast(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISFINITELENGTH(a); + return V3Scale(a, _mm_rsqrt_ps(V3Dot(a, a))); +} + +PX_FORCE_INLINE Vec3V V3NormalizeSafe(const Vec3V a, const Vec3V unsafeReturnValue) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 eps = FEps(); + const __m128 length = V3Length(a); + const __m128 isGreaterThanZero = FIsGrtr(length, eps); + return V3Sel(isGreaterThanZero, V3ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE Vec3V V3Sel(const BoolV c, const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(_mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a))); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV V3IsGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV V3IsGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV V3IsEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Max(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE Vec3V V3Min(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV V3ExtractMax(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + return _mm_max_ps(_mm_max_ps(shuf1, shuf2), shuf3); +} + +PX_FORCE_INLINE FloatV V3ExtractMin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); + return _mm_min_ps(_mm_min_ps(shuf1, shuf2), shuf3); +} + +//// if(a > 0.0f) return 1.0f; else if a == 0.f return 0.f, else return -1.f; +// PX_FORCE_INLINE Vec3V V3MathSign(const Vec3V a) +//{ +// VECMATHAOS_ASSERT(isValidVec3V(a)); +// +// const __m128i ai = _mm_cvtps_epi32(a); +// const __m128i bi = _mm_cvtps_epi32(V3Neg(a)); +// const __m128 aa = _mm_cvtepi32_ps(_mm_srai_epi32(ai, 31)); +// const __m128 bb = _mm_cvtepi32_ps(_mm_srai_epi32(bi, 31)); +// return _mm_or_ps(aa, bb); +//} + +// return (a >= 0.0f) ? 1.0f : -1.0f; +PX_FORCE_INLINE Vec3V V3Sign(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 zero = V3Zero(); + const __m128 one = V3One(); + const __m128 none = V3Neg(one); + return V3Sel(V3IsGrtrOrEq(a, zero), one, none); +} + +PX_FORCE_INLINE Vec3V V3Clamp(const Vec3V a, const Vec3V minV, const Vec3V maxV) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(minV); + ASSERT_ISVALIDVEC3V(maxV); + return V3Max(V3Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V3AllGrtr(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalWindowsSimd::BAllTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllGrtrOrEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalWindowsSimd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V3AllEq(const Vec3V a, const Vec3V b) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(b); + return internalWindowsSimd::BAllTrue3_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE Vec3V V3Round(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // return _mm_round_ps(a, 0x0); + const Vec3V half = V3Load(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const Vec3V aRound = V3Sub(V3Add(a, half), signBit); + __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +} + +PX_FORCE_INLINE Vec3V V3Sin(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V3Scale(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegScaleSub(b, twoPi, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V3 = V3Mul(V2, V1); + const Vec3V V5 = V3Mul(V3, V2); + const Vec3V V7 = V3Mul(V5, V2); + const Vec3V V9 = V3Mul(V7, V2); + const Vec3V V11 = V3Mul(V9, V2); + const Vec3V V13 = V3Mul(V11, V2); + const Vec3V V15 = V3Mul(V13, V2); + const Vec3V V17 = V3Mul(V15, V2); + const Vec3V V19 = V3Mul(V17, V2); + const Vec3V V21 = V3Mul(V19, V2); + const Vec3V V23 = V3Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec3V Result; + Result = V3ScaleAdd(V3, S1, V1); + Result = V3ScaleAdd(V5, S2, Result); + Result = V3ScaleAdd(V7, S3, Result); + Result = V3ScaleAdd(V9, S4, Result); + Result = V3ScaleAdd(V11, S5, Result); + Result = V3ScaleAdd(V13, S6, Result); + Result = V3ScaleAdd(V15, S7, Result); + Result = V3ScaleAdd(V17, S8, Result); + Result = V3ScaleAdd(V19, S9, Result); + Result = V3ScaleAdd(V21, S10, Result); + Result = V3ScaleAdd(V23, S11, Result); + + ASSERT_ISVALIDVEC3V(Result); + return Result; +} + +PX_FORCE_INLINE Vec3V V3Cos(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + + // Modulo the range of the given angles such that -XM_2PI <= Angles < XM_2PI + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec3V tmp = V3Scale(a, recipTwoPi); + const Vec3V b = V3Round(tmp); + const Vec3V V1 = V3NegScaleSub(b, twoPi, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec3V V2 = V3Mul(V1, V1); + const Vec3V V4 = V3Mul(V2, V2); + const Vec3V V6 = V3Mul(V4, V2); + const Vec3V V8 = V3Mul(V4, V4); + const Vec3V V10 = V3Mul(V6, V4); + const Vec3V V12 = V3Mul(V6, V6); + const Vec3V V14 = V3Mul(V8, V6); + const Vec3V V16 = V3Mul(V8, V8); + const Vec3V V18 = V3Mul(V10, V8); + const Vec3V V20 = V3Mul(V10, V10); + const Vec3V V22 = V3Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec3V Result; + Result = V3ScaleAdd(V2, C1, V3One()); + Result = V3ScaleAdd(V4, C2, Result); + Result = V3ScaleAdd(V6, C3, Result); + Result = V3ScaleAdd(V8, C4, Result); + Result = V3ScaleAdd(V10, C5, Result); + Result = V3ScaleAdd(V12, C6, Result); + Result = V3ScaleAdd(V14, C7, Result); + Result = V3ScaleAdd(V16, C8, Result); + Result = V3ScaleAdd(V18, C9, Result); + Result = V3ScaleAdd(V20, C10, Result); + Result = V3ScaleAdd(V22, C11, Result); + + ASSERT_ISVALIDVEC3V(Result); + return Result; +} + +PX_FORCE_INLINE Vec3V V3PermYZZ(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 2, 1)); +} + +PX_FORCE_INLINE Vec3V V3PermXYX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 1, 0)); +} + +PX_FORCE_INLINE Vec3V V3PermYZX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); +} + +PX_FORCE_INLINE Vec3V V3PermZXY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); +} + +PX_FORCE_INLINE Vec3V V3PermZZY(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 2, 2)); +} + +PX_FORCE_INLINE Vec3V V3PermYXX(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 0, 1)); +} + +PX_FORCE_INLINE Vec3V V3Perm_Zero_1Z_0Y(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + return _mm_shuffle_ps(v1, v0, _MM_SHUFFLE(3, 1, 2, 3)); +} + +PX_FORCE_INLINE Vec3V V3Perm_0Z_Zero_1X(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + return _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(3, 0, 3, 2)); +} + +PX_FORCE_INLINE Vec3V V3Perm_1Y_0X_Zero(const Vec3V v0, const Vec3V v1) +{ + ASSERT_ISVALIDVEC3V(v0); + ASSERT_ISVALIDVEC3V(v1); + // There must be a better way to do this. + Vec3V v2 = V3Zero(); + FloatV y1 = V3GetY(v1); + FloatV x0 = V3GetX(v0); + v2 = V3SetX(v2, y1); + return V3SetY(v2, x0); +} + +PX_FORCE_INLINE FloatV V3SumElems(const Vec3V a) +{ + ASSERT_ISVALIDVEC3V(a); + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); // z,y,x,w + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); // y,x,w,z + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); // x,w,z,y + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + const BoolV c = BOr(V3IsGrtr(a, max), V3IsGrtr(min, a)); + return PxU32(!BAllEqFFFF(c)); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V min, const Vec3V max) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(min); + ASSERT_ISVALIDVEC3V(max); + const BoolV c = BAnd(V3IsGrtrOrEq(a, min), V3IsGrtrOrEq(max, a)); + return BAllEqTTTT(c); +} + +PX_FORCE_INLINE PxU32 V3OutOfBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds); + return V3OutOfBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE PxU32 V3InBounds(const Vec3V a, const Vec3V bounds) +{ + ASSERT_ISVALIDVEC3V(a); + ASSERT_ISVALIDVEC3V(bounds); + return V3InBounds(a, V3Neg(bounds), bounds); +} + +PX_FORCE_INLINE void V3Transpose(Vec3V& col0, Vec3V& col1, Vec3V& col2) +{ + ASSERT_ISVALIDVEC3V(col0); + ASSERT_ISVALIDVEC3V(col1); + ASSERT_ISVALIDVEC3V(col2); + const Vec3V col3 = _mm_setzero_ps(); + Vec3V tmp0 = _mm_unpacklo_ps(col0, col1); + Vec3V tmp2 = _mm_unpacklo_ps(col2, col3); + Vec3V tmp1 = _mm_unpackhi_ps(col0, col1); + Vec3V tmp3 = _mm_unpackhi_ps(col2, col3); + col0 = _mm_movelh_ps(tmp0, tmp2); + col1 = _mm_movehl_ps(tmp2, tmp0); + col2 = _mm_movelh_ps(tmp1, tmp3); +} + +////////////////////////////////// +// VEC4V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V V4Splat(const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + // return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0,0,0,0)); + return f; +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatV* const floatVArray) +{ + ASSERT_ISVALIDFLOATV(floatVArray[0]); + ASSERT_ISVALIDFLOATV(floatVArray[1]); + ASSERT_ISVALIDFLOATV(floatVArray[2]); + ASSERT_ISVALIDFLOATV(floatVArray[3]); + const __m128 xw = _mm_move_ss(floatVArray[1], floatVArray[0]); // y, y, y, x + const __m128 yz = _mm_move_ss(floatVArray[2], floatVArray[3]); // z, z, z, w + return _mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0)); +} + +PX_FORCE_INLINE Vec4V V4Merge(const FloatVArg x, const FloatVArg y, const FloatVArg z, const FloatVArg w) +{ + ASSERT_ISVALIDFLOATV(x); + ASSERT_ISVALIDFLOATV(y); + ASSERT_ISVALIDFLOATV(z); + ASSERT_ISVALIDFLOATV(w); + const __m128 xw = _mm_move_ss(y, x); // y, y, y, x + const __m128 yz = _mm_move_ss(z, w); // z, z, z, w + return _mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0)); +} + +PX_FORCE_INLINE Vec4V V4MergeW(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpackhi_ps(x, z); + const Vec4V yw = _mm_unpackhi_ps(y, w); + return _mm_unpackhi_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeZ(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpackhi_ps(x, z); + const Vec4V yw = _mm_unpackhi_ps(y, w); + return _mm_unpacklo_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeY(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpacklo_ps(x, z); + const Vec4V yw = _mm_unpacklo_ps(y, w); + return _mm_unpackhi_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4MergeX(const Vec4VArg x, const Vec4VArg y, const Vec4VArg z, const Vec4VArg w) +{ + const Vec4V xz = _mm_unpacklo_ps(x, z); + const Vec4V yw = _mm_unpacklo_ps(y, w); + return _mm_unpacklo_ps(xz, yw); +} + +PX_FORCE_INLINE Vec4V V4UnpackXY(const Vec4VArg a, const Vec4VArg b) +{ + return _mm_unpacklo_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4UnpackZW(const Vec4VArg a, const Vec4VArg b) +{ + return _mm_unpackhi_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4PermYXWZ(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 0, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermXZXZ(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 0, 2, 0)); +} + +PX_FORCE_INLINE Vec4V V4PermYWYW(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 3, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermYZXW(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); +} + +PX_FORCE_INLINE Vec4V V4PermZWXY(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); +} + +template +PX_FORCE_INLINE Vec4V V4Perm(const Vec4V a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(w, z, y, x)); +} + +PX_FORCE_INLINE Vec4V V4UnitW() +{ + const PX_ALIGN(16, PxF32 w[4]) = { 0.0f, 0.0f, 0.0f, 1.0f }; + const __m128 w128 = _mm_load_ps(w); + return w128; +} + +PX_FORCE_INLINE Vec4V V4UnitX() +{ + const PX_ALIGN(16, PxF32 x[4]) = { 1.0f, 0.0f, 0.0f, 0.0f }; + const __m128 x128 = _mm_load_ps(x); + return x128; +} + +PX_FORCE_INLINE Vec4V V4UnitY() +{ + const PX_ALIGN(16, PxF32 y[4]) = { 0.0f, 1.0f, 0.0f, 0.0f }; + const __m128 y128 = _mm_load_ps(y); + return y128; +} + +PX_FORCE_INLINE Vec4V V4UnitZ() +{ + const PX_ALIGN(16, PxF32 z[4]) = { 0.0f, 0.0f, 1.0f, 0.0f }; + const __m128 z128 = _mm_load_ps(z); + return z128; +} + +PX_FORCE_INLINE FloatV V4GetW(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +PX_FORCE_INLINE FloatV V4GetX(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE FloatV V4GetY(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE FloatV V4GetZ(const Vec4V f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE Vec4V V4SetW(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTTF(), v, f); +} + +PX_FORCE_INLINE Vec4V V4ClearW(const Vec4V v) +{ + return _mm_and_ps(v, (VecI32V&)internalWindowsSimd::gMaskXYZ); +} + +PX_FORCE_INLINE Vec4V V4SetX(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4SetY(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4SetZ(const Vec4V v, const FloatV f) +{ + ASSERT_ISVALIDFLOATV(f); + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE Vec4V V4Zero() +{ + return _mm_setzero_ps(); +} + +PX_FORCE_INLINE Vec4V V4One() +{ + return V4Load(1.0f); +} + +PX_FORCE_INLINE Vec4V V4Eps() +{ + return V4Load(PX_EPS_REAL); +} + +PX_FORCE_INLINE Vec4V V4Neg(const Vec4V f) +{ + return _mm_sub_ps(_mm_setzero_ps(), f); +} + +PX_FORCE_INLINE Vec4V V4Add(const Vec4V a, const Vec4V b) +{ + return _mm_add_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Sub(const Vec4V a, const Vec4V b) +{ + return _mm_sub_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Scale(const Vec4V a, const FloatV b) +{ + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Mul(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4ScaleInv(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Div(const Vec4V a, const Vec4V b) +{ + return _mm_div_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4ScaleInvFast(const Vec4V a, const FloatV b) +{ + ASSERT_ISVALIDFLOATV(b); + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec4V V4DivFast(const Vec4V a, const Vec4V b) +{ + return _mm_mul_ps(a, _mm_rcp_ps(b)); +} + +PX_FORCE_INLINE Vec4V V4Recip(const Vec4V a) +{ + return _mm_div_ps(V4One(), a); +} + +PX_FORCE_INLINE Vec4V V4RecipFast(const Vec4V a) +{ + return _mm_rcp_ps(a); +} + +PX_FORCE_INLINE Vec4V V4Rsqrt(const Vec4V a) +{ + return _mm_div_ps(V4One(), _mm_sqrt_ps(a)); +} + +PX_FORCE_INLINE Vec4V V4RsqrtFast(const Vec4V a) +{ + return _mm_rsqrt_ps(a); +} + +PX_FORCE_INLINE Vec4V V4Sqrt(const Vec4V a) +{ + return _mm_sqrt_ps(a); +} + +PX_FORCE_INLINE Vec4V V4ScaleAdd(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return V4Add(V4Scale(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegScaleSub(const Vec4V a, const FloatV b, const Vec4V c) +{ + ASSERT_ISVALIDFLOATV(b); + return V4Sub(c, V4Scale(a, b)); +} + +PX_FORCE_INLINE Vec4V V4MulAdd(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Add(V4Mul(a, b), c); +} + +PX_FORCE_INLINE Vec4V V4NegMulSub(const Vec4V a, const Vec4V b, const Vec4V c) +{ + return V4Sub(c, V4Mul(a, b)); +} + +PX_FORCE_INLINE Vec4V V4Abs(const Vec4V a) +{ + return V4Max(a, V4Neg(a)); +} + +PX_FORCE_INLINE FloatV V4SumElements(const Vec4V a) +{ + const Vec4V xy = V4UnpackXY(a, a); // x,x,y,y + const Vec4V zw = V4UnpackZW(a, a); // z,z,w,w + const Vec4V xz_yw = V4Add(xy, zw); // x+z,x+z,y+w,y+w + const FloatV xz = V4GetX(xz_yw); // x+z + const FloatV yw = V4GetZ(xz_yw); // y+w + return FAdd(xz, yw); // sum +} + +PX_FORCE_INLINE FloatV V4Dot(const Vec4V a, const Vec4V b) +{ + const __m128 dot1 = _mm_mul_ps(a, b); // x,y,z,w + const __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 1, 0, 3)); // w,x,y,z + const __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 0, 3, 2)); // z,w,x,y + const __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 3, 2, 1)); // y,z,w,x + return _mm_add_ps(_mm_add_ps(shuf2, shuf3), _mm_add_ps(dot1, shuf1)); + + // PT: this version has two less instructions but we should check its accuracy + // aw*bw | az*bz | ay*by | ax*bx + // const __m128 t0 = _mm_mul_ps(a, b); + // ay*by | ax*bx | aw*bw | az*bz + // const __m128 t1 = _mm_shuffle_ps(t0, t0, _MM_SHUFFLE(1,0,3,2)); + // ay*by + aw*bw | ax*bx + az*bz | aw*bw + ay*by | az*bz + ax*bx + // const __m128 t2 = _mm_add_ps(t0, t1); + // ax*bx + az*bz | ay*by + aw*bw | az*bz + ax*bx | aw*bw + ay*by + // const __m128 t3 = _mm_shuffle_ps(t2, t2, _MM_SHUFFLE(2,3,0,1)); + // ax*bx + az*bz + ay*by + aw*bw + // return _mm_add_ps(t3, t2); + // ay*by + aw*bw + ax*bx + az*bz + // az*bz + ax*bx + aw*bw + ay*by + // aw*bw + ay*by + az*bz + ax*bx +} + +PX_FORCE_INLINE FloatV V4Dot3(const Vec4V a, const Vec4V b) +{ + const __m128 dot1 = _mm_mul_ps(a, b); // aw*bw | az*bz | ay*by | ax*bx + const __m128 shuf1 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(0, 0, 0, 0)); // ax*bx | ax*bx | ax*bx | ax*bx + const __m128 shuf2 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(1, 1, 1, 1)); // ay*by | ay*by | ay*by | ay*by + const __m128 shuf3 = _mm_shuffle_ps(dot1, dot1, _MM_SHUFFLE(2, 2, 2, 2)); // az*bz | az*bz | az*bz | az*bz + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); // ax*bx + ay*by + az*bz in each component +} + +PX_FORCE_INLINE Vec4V V4Cross(const Vec4V a, const Vec4V b) +{ + const __m128 r1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + const __m128 r2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1)); // y,z,x,w + const __m128 l2 = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 1, 0, 2)); // z,x,y,w + return _mm_sub_ps(_mm_mul_ps(l1, l2), _mm_mul_ps(r1, r2)); +} + +PX_FORCE_INLINE FloatV V4Length(const Vec4V a) +{ + return _mm_sqrt_ps(V4Dot(a, a)); +} + +PX_FORCE_INLINE FloatV V4LengthSq(const Vec4V a) +{ + return V4Dot(a, a); +} + +PX_FORCE_INLINE Vec4V V4Normalize(const Vec4V a) +{ + ASSERT_ISFINITELENGTH(a); + return V4ScaleInv(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +PX_FORCE_INLINE Vec4V V4NormalizeFast(const Vec4V a) +{ + ASSERT_ISFINITELENGTH(a); + return V4ScaleInvFast(a, _mm_sqrt_ps(V4Dot(a, a))); +} + +PX_FORCE_INLINE Vec4V V4NormalizeSafe(const Vec4V a, const Vec4V unsafeReturnValue) +{ + const __m128 eps = V3Eps(); + const __m128 length = V4Length(a); + const __m128 isGreaterThanZero = V4IsGrtr(length, eps); + return V4Sel(isGreaterThanZero, V4ScaleInv(a, length), unsafeReturnValue); +} + +PX_FORCE_INLINE Vec4V V4Sel(const BoolV c, const Vec4V a, const Vec4V b) +{ + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE BoolV V4IsGrtr(const Vec4V a, const Vec4V b) +{ + return _mm_cmpgt_ps(a, b); +} + +PX_FORCE_INLINE BoolV V4IsGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpge_ps(a, b); +} + +PX_FORCE_INLINE BoolV V4IsEq(const Vec4V a, const Vec4V b) +{ + return _mm_cmpeq_ps(a, b); +} + +PX_FORCE_INLINE BoolV V4IsEqU32(const VecU32V a, const VecU32V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_cmpeq_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE Vec4V V4Max(const Vec4V a, const Vec4V b) +{ + return _mm_max_ps(a, b); +} + +PX_FORCE_INLINE Vec4V V4Min(const Vec4V a, const Vec4V b) +{ + return _mm_min_ps(a, b); +} + +PX_FORCE_INLINE FloatV V4ExtractMax(const Vec4V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_max_ps(_mm_max_ps(a, shuf1), _mm_max_ps(shuf2, shuf3)); +} + +PX_FORCE_INLINE FloatV V4ExtractMin(const Vec4V a) +{ + const __m128 shuf1 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 1, 0, 3)); + const __m128 shuf2 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 shuf3 = _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 3, 2, 1)); + + return _mm_min_ps(_mm_min_ps(a, shuf1), _mm_min_ps(shuf2, shuf3)); +} + +PX_FORCE_INLINE Vec4V V4Clamp(const Vec4V a, const Vec4V minV, const Vec4V maxV) +{ + return V4Max(V4Min(a, maxV), minV); +} + +PX_FORCE_INLINE PxU32 V4AllGrtr(const Vec4V a, const Vec4V b) +{ + return internalWindowsSimd::BAllTrue4_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq(const Vec4V a, const Vec4V b) +{ + return internalWindowsSimd::BAllTrue4_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllGrtrOrEq3(const Vec4V a, const Vec4V b) +{ + return internalWindowsSimd::BAllTrue3_R(V4IsGrtrOrEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AllEq(const Vec4V a, const Vec4V b) +{ + return internalWindowsSimd::BAllTrue4_R(V4IsEq(a, b)); +} + +PX_FORCE_INLINE PxU32 V4AnyGrtr3(const Vec4V a, const Vec4V b) +{ + return internalWindowsSimd::BAnyTrue3_R(V4IsGrtr(a, b)); +} + +PX_FORCE_INLINE Vec4V V4Round(const Vec4V a) +{ + // return _mm_round_ps(a, 0x0); + const Vec4V half = V4Load(0.5f); + const __m128 signBit = _mm_cvtepi32_ps(_mm_srli_epi32(_mm_cvtps_epi32(a), 31)); + const Vec4V aRound = V4Sub(V4Add(a, half), signBit); + const __m128i tmp = _mm_cvttps_epi32(aRound); + return _mm_cvtepi32_ps(tmp); +} + +PX_FORCE_INLINE Vec4V V4Sin(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const Vec4V twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // sin(V) ~= V - V^3 / 3! + V^5 / 5! - V^7 / 7! + V^9 / 9! - V^11 / 11! + V^13 / 13! - + // V^15 / 15! + V^17 / 17! - V^19 / 19! + V^21 / 21! - V^23 / 23! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V3 = V4Mul(V2, V1); + const Vec4V V5 = V4Mul(V3, V2); + const Vec4V V7 = V4Mul(V5, V2); + const Vec4V V9 = V4Mul(V7, V2); + const Vec4V V11 = V4Mul(V9, V2); + const Vec4V V13 = V4Mul(V11, V2); + const Vec4V V15 = V4Mul(V13, V2); + const Vec4V V17 = V4Mul(V15, V2); + const Vec4V V19 = V4Mul(V17, V2); + const Vec4V V21 = V4Mul(V19, V2); + const Vec4V V23 = V4Mul(V21, V2); + + const Vec4V sinCoefficients0 = V4LoadA(g_PXSinCoefficients0.f); + const Vec4V sinCoefficients1 = V4LoadA(g_PXSinCoefficients1.f); + const Vec4V sinCoefficients2 = V4LoadA(g_PXSinCoefficients2.f); + + const FloatV S1 = V4GetY(sinCoefficients0); + const FloatV S2 = V4GetZ(sinCoefficients0); + const FloatV S3 = V4GetW(sinCoefficients0); + const FloatV S4 = V4GetX(sinCoefficients1); + const FloatV S5 = V4GetY(sinCoefficients1); + const FloatV S6 = V4GetZ(sinCoefficients1); + const FloatV S7 = V4GetW(sinCoefficients1); + const FloatV S8 = V4GetX(sinCoefficients2); + const FloatV S9 = V4GetY(sinCoefficients2); + const FloatV S10 = V4GetZ(sinCoefficients2); + const FloatV S11 = V4GetW(sinCoefficients2); + + Vec4V Result; + Result = V4MulAdd(S1, V3, V1); + Result = V4MulAdd(S2, V5, Result); + Result = V4MulAdd(S3, V7, Result); + Result = V4MulAdd(S4, V9, Result); + Result = V4MulAdd(S5, V11, Result); + Result = V4MulAdd(S6, V13, Result); + Result = V4MulAdd(S7, V15, Result); + Result = V4MulAdd(S8, V17, Result); + Result = V4MulAdd(S9, V19, Result); + Result = V4MulAdd(S10, V21, Result); + Result = V4MulAdd(S11, V23, Result); + + return Result; +} + +PX_FORCE_INLINE Vec4V V4Cos(const Vec4V a) +{ + const Vec4V recipTwoPi = V4LoadA(g_PXReciprocalTwoPi.f); + const FloatV twoPi = V4LoadA(g_PXTwoPi.f); + const Vec4V tmp = V4Mul(a, recipTwoPi); + const Vec4V b = V4Round(tmp); + const Vec4V V1 = V4NegMulSub(twoPi, b, a); + + // cos(V) ~= 1 - V^2 / 2! + V^4 / 4! - V^6 / 6! + V^8 / 8! - V^10 / 10! + V^12 / 12! - + // V^14 / 14! + V^16 / 16! - V^18 / 18! + V^20 / 20! - V^22 / 22! (for -PI <= V < PI) + const Vec4V V2 = V4Mul(V1, V1); + const Vec4V V4 = V4Mul(V2, V2); + const Vec4V V6 = V4Mul(V4, V2); + const Vec4V V8 = V4Mul(V4, V4); + const Vec4V V10 = V4Mul(V6, V4); + const Vec4V V12 = V4Mul(V6, V6); + const Vec4V V14 = V4Mul(V8, V6); + const Vec4V V16 = V4Mul(V8, V8); + const Vec4V V18 = V4Mul(V10, V8); + const Vec4V V20 = V4Mul(V10, V10); + const Vec4V V22 = V4Mul(V12, V10); + + const Vec4V cosCoefficients0 = V4LoadA(g_PXCosCoefficients0.f); + const Vec4V cosCoefficients1 = V4LoadA(g_PXCosCoefficients1.f); + const Vec4V cosCoefficients2 = V4LoadA(g_PXCosCoefficients2.f); + + const FloatV C1 = V4GetY(cosCoefficients0); + const FloatV C2 = V4GetZ(cosCoefficients0); + const FloatV C3 = V4GetW(cosCoefficients0); + const FloatV C4 = V4GetX(cosCoefficients1); + const FloatV C5 = V4GetY(cosCoefficients1); + const FloatV C6 = V4GetZ(cosCoefficients1); + const FloatV C7 = V4GetW(cosCoefficients1); + const FloatV C8 = V4GetX(cosCoefficients2); + const FloatV C9 = V4GetY(cosCoefficients2); + const FloatV C10 = V4GetZ(cosCoefficients2); + const FloatV C11 = V4GetW(cosCoefficients2); + + Vec4V Result; + Result = V4MulAdd(C1, V2, V4One()); + Result = V4MulAdd(C2, V4, Result); + Result = V4MulAdd(C3, V6, Result); + Result = V4MulAdd(C4, V8, Result); + Result = V4MulAdd(C5, V10, Result); + Result = V4MulAdd(C6, V12, Result); + Result = V4MulAdd(C7, V14, Result); + Result = V4MulAdd(C8, V16, Result); + Result = V4MulAdd(C9, V18, Result); + Result = V4MulAdd(C10, V20, Result); + Result = V4MulAdd(C11, V22, Result); + + return Result; +} + +PX_FORCE_INLINE void V4Transpose(Vec4V& col0, Vec4V& col1, Vec4V& col2, Vec4V& col3) +{ + Vec4V tmp0 = _mm_unpacklo_ps(col0, col1); + Vec4V tmp2 = _mm_unpacklo_ps(col2, col3); + Vec4V tmp1 = _mm_unpackhi_ps(col0, col1); + Vec4V tmp3 = _mm_unpackhi_ps(col2, col3); + col0 = _mm_movelh_ps(tmp0, tmp2); + col1 = _mm_movehl_ps(tmp2, tmp0); + col2 = _mm_movelh_ps(tmp1, tmp3); + col3 = _mm_movehl_ps(tmp3, tmp1); +} + +////////////////////////////////// +// BoolV +////////////////////////////////// + +PX_FORCE_INLINE BoolV BFFFF() +{ + return _mm_setzero_ps(); +} + +PX_FORCE_INLINE BoolV BFFFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0,0xFFFFFFFF}; + const __m128 ffft=_mm_load_ps((float*)&f); + return ffft;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFFTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0}; + const __m128 fftf=_mm_load_ps((float*)&f); + return fftf;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFFTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 fftt=_mm_load_ps((float*)&f); + return fftt;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BFTFF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0}; + const __m128 ftff=_mm_load_ps((float*)&f); + return ftff;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0xFFFFFFFF}; + const __m128 ftft=_mm_load_ps((float*)&f); + return ftft;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0xFFFFFFFF,0}; + const __m128 fttf=_mm_load_ps((float*)&f); + return fttf;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, -1, -1, 0)); +} + +PX_FORCE_INLINE BoolV BFTTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 fttt=_mm_load_ps((float*)&f); + return fttt;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, -1, -1, 0)); +} + +PX_FORCE_INLINE BoolV BTFFF() +{ + // const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0}; + // const __m128 tfff=_mm_load_ps((float*)&f); + // return tfff; + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0xFFFFFFFF}; + const __m128 tfft=_mm_load_ps((float*)&f); + return tfft;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0xFFFFFFFF,0}; + const __m128 tftf=_mm_load_ps((float*)&f); + return tftf;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, -1, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTFTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 tftt=_mm_load_ps((float*)&f); + return tftt;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, -1, 0, -1)); +} + +PX_FORCE_INLINE BoolV BTTFF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0,0}; + const __m128 ttff=_mm_load_ps((float*)&f); + return ttff;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, 0, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTFT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0,0xFFFFFFFF}; + const __m128 ttft=_mm_load_ps((float*)&f); + return ttft;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, 0, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTTF() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0}; + const __m128 tttf=_mm_load_ps((float*)&f); + return tttf;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, -1, -1, -1)); +} + +PX_FORCE_INLINE BoolV BTTTT() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF}; + const __m128 tttt=_mm_load_ps((float*)&f); + return tttt;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, -1, -1, -1)); +} + +PX_FORCE_INLINE BoolV BXMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0xFFFFFFFF,0,0,0}; + const __m128 tfff=_mm_load_ps((float*)&f); + return tfff;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, 0, 0, -1)); +} + +PX_FORCE_INLINE BoolV BYMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0xFFFFFFFF,0,0}; + const __m128 ftff=_mm_load_ps((float*)&f); + return ftff;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, 0, -1, 0)); +} + +PX_FORCE_INLINE BoolV BZMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0xFFFFFFFF,0}; + const __m128 fftf=_mm_load_ps((float*)&f); + return fftf;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(0, -1, 0, 0)); +} + +PX_FORCE_INLINE BoolV BWMask() +{ + /*const PX_ALIGN(16, PxU32 f[4])={0,0,0,0xFFFFFFFF}; + const __m128 ffft=_mm_load_ps((float*)&f); + return ffft;*/ + return internalWindowsSimd::m128_I2F(_mm_set_epi32(-1, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BGetX(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE BoolV BGetY(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE BoolV BGetZ(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE BoolV BGetW(const BoolV f) +{ + return _mm_shuffle_ps(f, f, _MM_SHUFFLE(3, 3, 3, 3)); +} + +PX_FORCE_INLINE BoolV BSetX(const BoolV v, const BoolV f) +{ + return V4Sel(BFTTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetY(const BoolV v, const BoolV f) +{ + return V4Sel(BTFTT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetZ(const BoolV v, const BoolV f) +{ + return V4Sel(BTTFT(), v, f); +} + +PX_FORCE_INLINE BoolV BSetW(const BoolV v, const BoolV f) +{ + return V4Sel(BTTTF(), v, f); +} + +template +BoolV BSplatElement(BoolV a) +{ + return internalWindowsSimd::m128_I2F( + _mm_shuffle_epi32(internalWindowsSimd::m128_F2I(a), _MM_SHUFFLE(index, index, index, index))); +} + +PX_FORCE_INLINE BoolV BAnd(const BoolV a, const BoolV b) +{ + return _mm_and_ps(a, b); +} + +PX_FORCE_INLINE BoolV BNot(const BoolV a) +{ + const BoolV bAllTrue(BTTTT()); + return _mm_xor_ps(a, bAllTrue); +} + +PX_FORCE_INLINE BoolV BAndNot(const BoolV a, const BoolV b) +{ + return _mm_andnot_ps(b, a); +} + +PX_FORCE_INLINE BoolV BOr(const BoolV a, const BoolV b) +{ + return _mm_or_ps(a, b); +} + +PX_FORCE_INLINE BoolV BAllTrue4(const BoolV a) +{ + const BoolV bTmp = + _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAnyTrue4(const BoolV a) +{ + const BoolV bTmp = + _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 2, 3))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAllTrue3(const BoolV a) +{ + const BoolV bTmp = + _mm_and_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_and_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE BoolV BAnyTrue3(const BoolV a) +{ + const BoolV bTmp = + _mm_or_ps(_mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 0, 1)), _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); + return _mm_or_ps(_mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_shuffle_ps(bTmp, bTmp, _MM_SHUFFLE(1, 1, 1, 1))); +} + +PX_FORCE_INLINE PxU32 BAllEq(const BoolV a, const BoolV b) +{ + const BoolV bTest = internalWindowsSimd::m128_I2F( + _mm_cmpeq_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); + return internalWindowsSimd::BAllTrue4_R(bTest); +} + +PX_FORCE_INLINE PxU32 BAllEqTTTT(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)==15); +} + +PX_FORCE_INLINE PxU32 BAllEqFFFF(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)==0); +} + +PX_FORCE_INLINE PxU32 BGetBitMask(const BoolV a) +{ + return PxU32(_mm_movemask_ps(a)); +} + +////////////////////////////////// +// MAT33V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M33MulV3(const Mat33V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M33TrnspsMulV3(const Mat33V& a, const Vec3V b) +{ + Vec3V v0 = V3Mul(a.col0, b); + Vec3V v1 = V3Mul(a.col1, b); + Vec3V v2 = V3Mul(a.col2, b); + V3Transpose(v0, v1, v2); + return V3Add(V3Add(v0, v1), v2); +} + +PX_FORCE_INLINE Vec3V M33MulV3AddV3(const Mat33V& A, const Vec3V b, const Vec3V c) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + Vec3V result = V3ScaleAdd(A.col0, x, c); + result = V3ScaleAdd(A.col1, y, result); + return V3ScaleAdd(A.col2, z, result); +} + +PX_FORCE_INLINE Mat33V M33MulM33(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(M33MulV3(a, b.col0), M33MulV3(a, b.col1), M33MulV3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Add(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Scale(const Mat33V& a, const FloatV& b) +{ + return Mat33V(V3Scale(a.col0, b), V3Scale(a.col1, b), V3Scale(a.col2, b)); +} + +PX_FORCE_INLINE Mat33V M33Sub(const Mat33V& a, const Mat33V& b) +{ + return Mat33V(V3Sub(a.col0, b.col0), V3Sub(a.col1, b.col1), V3Sub(a.col2, b.col2)); +} + +PX_FORCE_INLINE Mat33V M33Neg(const Mat33V& a) +{ + return Mat33V(V3Neg(a.col0), V3Neg(a.col1), V3Neg(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Abs(const Mat33V& a) +{ + return Mat33V(V3Abs(a.col0), V3Abs(a.col1), V3Abs(a.col2)); +} + +PX_FORCE_INLINE Mat33V M33Inverse(const Mat33V& a) +{ + const BoolV tfft = BTFFT(); + const BoolV tttf = BTTTF(); + const FloatV zero = V3Zero(); + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = _mm_rcp_ps(dot); + const Vec3V mergeh = _mm_unpacklo_ps(cross12, cross01); + const Vec3V mergel = _mm_unpackhi_ps(cross12, cross01); + Vec3V colInv0 = _mm_unpacklo_ps(mergeh, cross20); + colInv0 = _mm_or_ps(_mm_andnot_ps(tttf, zero), _mm_and_ps(tttf, colInv0)); + const Vec3V zppd = _mm_shuffle_ps(mergeh, cross20, _MM_SHUFFLE(3, 0, 0, 2)); + const Vec3V pbwp = _mm_shuffle_ps(cross20, mergeh, _MM_SHUFFLE(3, 3, 1, 0)); + const Vec3V colInv1 = _mm_or_ps(_mm_andnot_ps(BTFFT(), pbwp), _mm_and_ps(BTFFT(), zppd)); + const Vec3V xppd = _mm_shuffle_ps(mergel, cross20, _MM_SHUFFLE(3, 0, 0, 0)); + const Vec3V pcyp = _mm_shuffle_ps(cross20, mergel, _MM_SHUFFLE(3, 1, 2, 0)); + const Vec3V colInv2 = _mm_or_ps(_mm_andnot_ps(tfft, pcyp), _mm_and_ps(tfft, xppd)); + + return Mat33V(_mm_mul_ps(colInv0, invDet), _mm_mul_ps(colInv1, invDet), _mm_mul_ps(colInv2, invDet)); +} + +PX_FORCE_INLINE Mat33V M33Trnsps(const Mat33V& a) +{ + Vec3V col0 = a.col0, col1 = a.col1, col2 = a.col2; + V3Transpose(col0, col1, col2); + return Mat33V(col0, col1, col2); +} + +PX_FORCE_INLINE Mat33V M33Identity() +{ + return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ()); +} + +PX_FORCE_INLINE Mat33V M33Diagonal(const Vec3VArg d) +{ + const FloatV x = V3Mul(V3UnitX(), d); + const FloatV y = V3Mul(V3UnitY(), d); + const FloatV z = V3Mul(V3UnitZ(), d); + return Mat33V(x, y, z); +} + +////////////////////////////////// +// MAT34V +////////////////////////////////// + +PX_FORCE_INLINE Vec3V M34MulV3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + const Vec3V v0PlusV1Plusv2 = V3Add(v0PlusV1, v2); + return V3Add(v0PlusV1Plusv2, a.col3); +} + +PX_FORCE_INLINE Vec3V M34Mul33V3(const Mat34V& a, const Vec3V b) +{ + const FloatV x = V3GetX(b); + const FloatV y = V3GetY(b); + const FloatV z = V3GetZ(b); + const Vec3V v0 = V3Scale(a.col0, x); + const Vec3V v1 = V3Scale(a.col1, y); + const Vec3V v2 = V3Scale(a.col2, z); + const Vec3V v0PlusV1 = V3Add(v0, v1); + return V3Add(v0PlusV1, v2); +} + +PX_FORCE_INLINE Vec3V M34TrnspsMul33V3(const Mat34V& a, const Vec3V b) +{ + Vec3V v0 = V3Mul(a.col0, b); + Vec3V v1 = V3Mul(a.col1, b); + Vec3V v2 = V3Mul(a.col2, b); + V3Transpose(v0, v1, v2); + return V3Add(V3Add(v0, v1), v2); +} + +PX_FORCE_INLINE Mat34V M34MulM34(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2), M34MulV3(a, b.col3)); +} + +PX_FORCE_INLINE Mat33V M34MulM33(const Mat34V& a, const Mat33V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat33V M34Mul33MM34(const Mat34V& a, const Mat34V& b) +{ + return Mat33V(M34Mul33V3(a, b.col0), M34Mul33V3(a, b.col1), M34Mul33V3(a, b.col2)); +} + +PX_FORCE_INLINE Mat34V M34Add(const Mat34V& a, const Mat34V& b) +{ + return Mat34V(V3Add(a.col0, b.col0), V3Add(a.col1, b.col1), V3Add(a.col2, b.col2), V3Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat34V M34Inverse(const Mat34V& a) +{ + Mat34V aInv; + const BoolV tfft = BTFFT(); + const BoolV tttf = BTTTF(); + const FloatV zero = V3Zero(); + const Vec3V cross01 = V3Cross(a.col0, a.col1); + const Vec3V cross12 = V3Cross(a.col1, a.col2); + const Vec3V cross20 = V3Cross(a.col2, a.col0); + const FloatV dot = V3Dot(cross01, a.col2); + const FloatV invDet = _mm_rcp_ps(dot); + const Vec3V mergeh = _mm_unpacklo_ps(cross12, cross01); + const Vec3V mergel = _mm_unpackhi_ps(cross12, cross01); + Vec3V colInv0 = _mm_unpacklo_ps(mergeh, cross20); + colInv0 = _mm_or_ps(_mm_andnot_ps(tttf, zero), _mm_and_ps(tttf, colInv0)); + const Vec3V zppd = _mm_shuffle_ps(mergeh, cross20, _MM_SHUFFLE(3, 0, 0, 2)); + const Vec3V pbwp = _mm_shuffle_ps(cross20, mergeh, _MM_SHUFFLE(3, 3, 1, 0)); + const Vec3V colInv1 = _mm_or_ps(_mm_andnot_ps(BTFFT(), pbwp), _mm_and_ps(BTFFT(), zppd)); + const Vec3V xppd = _mm_shuffle_ps(mergel, cross20, _MM_SHUFFLE(3, 0, 0, 0)); + const Vec3V pcyp = _mm_shuffle_ps(cross20, mergel, _MM_SHUFFLE(3, 1, 2, 0)); + const Vec3V colInv2 = _mm_or_ps(_mm_andnot_ps(tfft, pcyp), _mm_and_ps(tfft, xppd)); + aInv.col0 = _mm_mul_ps(colInv0, invDet); + aInv.col1 = _mm_mul_ps(colInv1, invDet); + aInv.col2 = _mm_mul_ps(colInv2, invDet); + aInv.col3 = M34Mul33V3(aInv, V3Neg(a.col3)); + return aInv; +} + +PX_FORCE_INLINE Mat33V M34Trnsps33(const Mat34V& a) +{ + Vec3V col0 = a.col0, col1 = a.col1, col2 = a.col2; + V3Transpose(col0, col1, col2); + return Mat33V(col0, col1, col2); +} + +////////////////////////////////// +// MAT44V +////////////////////////////////// + +PX_FORCE_INLINE Vec4V M44MulV4(const Mat44V& a, const Vec4V b) +{ + const FloatV x = V4GetX(b); + const FloatV y = V4GetY(b); + const FloatV z = V4GetZ(b); + const FloatV w = V4GetW(b); + + const Vec4V v0 = V4Scale(a.col0, x); + const Vec4V v1 = V4Scale(a.col1, y); + const Vec4V v2 = V4Scale(a.col2, z); + const Vec4V v3 = V4Scale(a.col3, w); + const Vec4V v0PlusV1 = V4Add(v0, v1); + const Vec4V v0PlusV1Plusv2 = V4Add(v0PlusV1, v2); + return V4Add(v0PlusV1Plusv2, v3); +} + +PX_FORCE_INLINE Vec4V M44TrnspsMulV4(const Mat44V& a, const Vec4V b) +{ + Vec4V v0 = V4Mul(a.col0, b); + Vec4V v1 = V4Mul(a.col1, b); + Vec4V v2 = V4Mul(a.col2, b); + Vec4V v3 = V4Mul(a.col3, b); + V4Transpose(v0, v1, v2, v3); + return V4Add(V4Add(v0, v1), V4Add(v2, v3)); +} + +PX_FORCE_INLINE Mat44V M44MulM44(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(M44MulV4(a, b.col0), M44MulV4(a, b.col1), M44MulV4(a, b.col2), M44MulV4(a, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Add(const Mat44V& a, const Mat44V& b) +{ + return Mat44V(V4Add(a.col0, b.col0), V4Add(a.col1, b.col1), V4Add(a.col2, b.col2), V4Add(a.col3, b.col3)); +} + +PX_FORCE_INLINE Mat44V M44Trnsps(const Mat44V& a) +{ + Vec4V col0 = a.col0, col1 = a.col1, col2 = a.col2, col3 = a.col3; + V4Transpose(col0, col1, col2, col3); + return Mat44V(col0, col1, col2, col3); +} + +PX_FORCE_INLINE Mat44V M44Inverse(const Mat44V& a) +{ + __m128 minor0, minor1, minor2, minor3; + __m128 row0, row1, row2, row3; + __m128 det, tmp1; + + tmp1 = V4Zero(); + row1 = V4Zero(); + row3 = V4Zero(); + + row0 = a.col0; + row1 = _mm_shuffle_ps(a.col1, a.col1, _MM_SHUFFLE(1, 0, 3, 2)); + row2 = a.col2; + row3 = _mm_shuffle_ps(a.col3, a.col3, _MM_SHUFFLE(1, 0, 3, 2)); + + tmp1 = _mm_mul_ps(row2, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_mul_ps(row1, tmp1); + minor1 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0); + minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1); + minor1 = _mm_shuffle_ps(minor1, minor1, 0x4E); + + tmp1 = _mm_mul_ps(row1, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0); + minor3 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3); + minor3 = _mm_shuffle_ps(minor3, minor3, 0x4E); + + tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, 0x4E), row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + row2 = _mm_shuffle_ps(row2, row2, 0x4E); + minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0); + minor2 = _mm_mul_ps(row0, tmp1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2); + minor2 = _mm_shuffle_ps(minor2, minor2, 0x4E); + + tmp1 = _mm_mul_ps(row0, row1); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1)); + + tmp1 = _mm_mul_ps(row0, row3); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1)); + minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1); + minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1)); + + tmp1 = _mm_mul_ps(row0, row2); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1); + minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1); + minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1)); + tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E); + minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1)); + minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3); + + det = _mm_mul_ps(row0, minor0); + det = _mm_add_ps(_mm_shuffle_ps(det, det, 0x4E), det); + det = _mm_add_ss(_mm_shuffle_ps(det, det, 0xB1), det); + tmp1 = _mm_rcp_ss(det); +#if 0 + det = _mm_sub_ss(_mm_add_ss(tmp1, tmp1), _mm_mul_ss(det, _mm_mul_ss(tmp1, tmp1))); + det = _mm_shuffle_ps(det, det, 0x00); +#else + det = _mm_shuffle_ps(tmp1, tmp1, _MM_SHUFFLE(0, 0, 0, 0)); +#endif + + minor0 = _mm_mul_ps(det, minor0); + minor1 = _mm_mul_ps(det, minor1); + minor2 = _mm_mul_ps(det, minor2); + minor3 = _mm_mul_ps(det, minor3); + Mat44V invTrans(minor0, minor1, minor2, minor3); + return M44Trnsps(invTrans); +} + +PX_FORCE_INLINE Vec4V V4LoadXYZW(const PxF32& x, const PxF32& y, const PxF32& z, const PxF32& w) +{ + return _mm_set_ps(w, z, y, x); +} + +PX_FORCE_INLINE VecU32V V4U32Sel(const BoolV c, const VecU32V a, const VecU32V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_or_si128(_mm_andnot_si128(internalWindowsSimd::m128_F2I(c), internalWindowsSimd::m128_F2I(b)), + _mm_and_si128(internalWindowsSimd::m128_F2I(c), internalWindowsSimd::m128_F2I(a)))); +} + +PX_FORCE_INLINE VecU32V V4U32or(VecU32V a, VecU32V b) +{ + return internalWindowsSimd::m128_I2F(_mm_or_si128(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32xor(VecU32V a, VecU32V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_xor_si128(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32and(VecU32V a, VecU32V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_and_si128(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE VecU32V V4U32Andc(VecU32V a, VecU32V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_andnot_si128(internalWindowsSimd::m128_F2I(b), internalWindowsSimd::m128_F2I(a))); +} + +PX_FORCE_INLINE VecI32V U4Load(const PxU32 i) +{ + return _mm_load1_ps((PxF32*)&i); +} + +PX_FORCE_INLINE VecU32V U4LoadU(const PxU32* i) +{ + return _mm_loadu_ps((PxF32*)i); +} + +PX_FORCE_INLINE VecU32V U4LoadA(const PxU32* i) +{ + ASSERT_ISALIGNED16(i); + return _mm_load_ps((PxF32*)i); +} + +PX_FORCE_INLINE VecI32V I4Load(const PxI32 i) +{ + return _mm_load1_ps((PxF32*)&i); +} + +PX_FORCE_INLINE VecI32V I4LoadU(const PxI32* i) +{ + return _mm_loadu_ps((PxF32*)i); +} + +PX_FORCE_INLINE VecI32V I4LoadA(const PxI32* i) +{ + ASSERT_ISALIGNED16(i); + return _mm_load_ps((PxF32*)i); +} + +PX_FORCE_INLINE VecI32V VecI32V_Add(const VecI32VArg a, const VecI32VArg b) +{ + return internalWindowsSimd::m128_I2F( + _mm_add_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sub(const VecI32VArg a, const VecI32VArg b) +{ + return internalWindowsSimd::m128_I2F( + _mm_sub_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE BoolV VecI32V_IsGrtr(const VecI32VArg a, const VecI32VArg b) +{ + return internalWindowsSimd::m128_I2F( + _mm_cmpgt_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE BoolV VecI32V_IsEq(const VecI32VArg a, const VecI32VArg b) +{ + return internalWindowsSimd::m128_I2F( + _mm_cmpeq_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE VecI32V V4I32Sel(const BoolV c, const VecI32V a, const VecI32V b) +{ + return V4U32Sel(c, a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Zero() +{ + return V4Zero(); +} + +PX_FORCE_INLINE VecI32V VecI32V_One() +{ + return I4Load(1); +} + +PX_FORCE_INLINE VecI32V VecI32V_Two() +{ + return I4Load(2); +} + +PX_FORCE_INLINE VecI32V VecI32V_MinusOne() +{ + return I4Load(-1); +} + +PX_FORCE_INLINE VecU32V U4Zero() +{ + return U4Load(0); +} + +PX_FORCE_INLINE VecU32V U4One() +{ + return U4Load(1); +} + +PX_FORCE_INLINE VecU32V U4Two() +{ + return U4Load(2); +} + +PX_FORCE_INLINE VecI32V VecI32V_Sel(const BoolV c, const VecI32VArg a, const VecI32VArg b) +{ + PX_ASSERT(_VecMathTests::allElementsEqualBoolV(c, BTTTT()) || + _VecMathTests::allElementsEqualBoolV(c, BFFFF())); + return _mm_or_ps(_mm_andnot_ps(c, b), _mm_and_ps(c, a)); +} + +PX_FORCE_INLINE VecShiftV VecI32V_PrepareShift(const VecI32VArg shift) +{ + VecShiftV preparedShift; + preparedShift.shift = _mm_or_ps(_mm_andnot_ps(BTFFF(), VecI32V_Zero()), _mm_and_ps(BTFFF(), shift)); + return preparedShift; +} + +PX_FORCE_INLINE VecI32V VecI32V_LeftShift(const VecI32VArg a, const VecShiftVArg count) +{ + return internalWindowsSimd::m128_I2F( + _mm_sll_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(count.shift))); +} + +PX_FORCE_INLINE VecI32V VecI32V_RightShift(const VecI32VArg a, const VecShiftVArg count) +{ + return internalWindowsSimd::m128_I2F( + _mm_srl_epi32(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(count.shift))); +} + +PX_FORCE_INLINE VecI32V VecI32V_And(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_and_ps(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_Or(const VecI32VArg a, const VecI32VArg b) +{ + return _mm_or_ps(a, b); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetX(const VecI32VArg a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetY(const VecI32VArg a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetZ(const VecI32VArg a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)); +} + +PX_FORCE_INLINE VecI32V VecI32V_GetW(const VecI32VArg a) +{ + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)); +} + +PX_FORCE_INLINE void PxI32_From_VecI32V(const VecI32VArg a, PxI32* i) +{ + _mm_store_ss((PxF32*)i, a); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_BoolV(const BoolVArg a) +{ + return a; +} + +PX_FORCE_INLINE VecU32V VecU32V_From_BoolV(const BoolVArg a) +{ + return a; +} + +PX_FORCE_INLINE VecI32V VecI32V_Merge(const VecI32VArg a, const VecI32VArg b, const VecI32VArg c, const VecI32VArg d) +{ + const __m128 xw = _mm_move_ss(b, a); // y, y, y, x + const __m128 yz = _mm_move_ss(c, d); // z, z, z, w + return _mm_shuffle_ps(xw, yz, _MM_SHUFFLE(0, 2, 1, 0)); +} + +PX_FORCE_INLINE void V4U32StoreAligned(VecU32V val, VecU32V* address) +{ + *address = val; +} + +PX_FORCE_INLINE Vec4V V4Andc(const Vec4V a, const VecU32V b) +{ + VecU32V result32(a); + result32 = V4U32Andc(result32, b); + return Vec4V(result32); +} + +PX_FORCE_INLINE VecU32V V4IsGrtrV32u(const Vec4V a, const Vec4V b) +{ + return V4IsGrtr(a, b); +} + +PX_FORCE_INLINE VecU16V V4U16LoadAligned(VecU16V* addr) +{ + return *addr; +} + +PX_FORCE_INLINE VecU16V V4U16LoadUnaligned(VecU16V* addr) +{ + return *addr; +} + +// unsigned compares are not supported on x86 +PX_FORCE_INLINE VecU16V V4U16CompareGt(VecU16V a, VecU16V b) +{ + // _mm_cmpgt_epi16 doesn't work for unsigned values unfortunately + // return m128_I2F(_mm_cmpgt_epi16(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); + VecU16V result; + result.m128_u16[0] = PxU16((a).m128_u16[0] > (b).m128_u16[0]); + result.m128_u16[1] = PxU16((a).m128_u16[1] > (b).m128_u16[1]); + result.m128_u16[2] = PxU16((a).m128_u16[2] > (b).m128_u16[2]); + result.m128_u16[3] = PxU16((a).m128_u16[3] > (b).m128_u16[3]); + result.m128_u16[4] = PxU16((a).m128_u16[4] > (b).m128_u16[4]); + result.m128_u16[5] = PxU16((a).m128_u16[5] > (b).m128_u16[5]); + result.m128_u16[6] = PxU16((a).m128_u16[6] > (b).m128_u16[6]); + result.m128_u16[7] = PxU16((a).m128_u16[7] > (b).m128_u16[7]); + return result; +} + +PX_FORCE_INLINE VecU16V V4I16CompareGt(VecU16V a, VecU16V b) +{ + return internalWindowsSimd::m128_I2F( + _mm_cmpgt_epi16(internalWindowsSimd::m128_F2I(a), internalWindowsSimd::m128_F2I(b))); +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecU32V(VecU32V a) +{ + Vec4V result = V4LoadXYZW(PxF32(a.m128_u32[0]), PxF32(a.m128_u32[1]), PxF32(a.m128_u32[2]), PxF32(a.m128_u32[3])); + return result; +} + +PX_FORCE_INLINE Vec4V Vec4V_From_VecI32V(VecI32V a) +{ + return _mm_cvtepi32_ps(internalWindowsSimd::m128_F2I(a)); +} + +PX_FORCE_INLINE VecI32V VecI32V_From_Vec4V(Vec4V a) +{ + return internalWindowsSimd::m128_I2F(_mm_cvttps_epi32(a)); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecU32V(VecU32V a) +{ + return Vec4V(a); +} + +PX_FORCE_INLINE Vec4V Vec4V_ReinterpretFrom_VecI32V(VecI32V a) +{ + return Vec4V(a); +} + +PX_FORCE_INLINE VecU32V VecU32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return VecU32V(a); +} + +PX_FORCE_INLINE VecI32V VecI32V_ReinterpretFrom_Vec4V(Vec4V a) +{ + return VecI32V(a); +} + +template +PX_FORCE_INLINE VecU32V V4U32SplatElement(VecU32V a) +{ + return internalWindowsSimd::m128_I2F( + _mm_shuffle_epi32(internalWindowsSimd::m128_F2I(a), _MM_SHUFFLE(index, index, index, index))); +} + +template +PX_FORCE_INLINE Vec4V V4SplatElement(Vec4V a) +{ + return internalWindowsSimd::m128_I2F( + _mm_shuffle_epi32(internalWindowsSimd::m128_F2I(a), _MM_SHUFFLE(index, index, index, index))); +} + +PX_FORCE_INLINE VecU32V U4LoadXYZW(PxU32 x, PxU32 y, PxU32 z, PxU32 w) +{ + VecU32V result; + result.m128_u32[0] = x; + result.m128_u32[1] = y; + result.m128_u32[2] = z; + result.m128_u32[3] = w; + return result; +} + +PX_FORCE_INLINE Vec4V V4ConvertFromI32V(const VecI32V in) +{ + return _mm_cvtepi32_ps(internalWindowsSimd::m128_F2I(in)); +} + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif // PSFOUNDATION_PSWINDOWSINLINEAOS_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h new file mode 100644 index 000000000..b8c837d9c --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h @@ -0,0 +1,193 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSINTRINSICS_H +#define PSFOUNDATION_PSWINDOWSINTRINSICS_H + +#include "Ps.h" +#include "foundation/PxAssert.h" + +// this file is for internal intrinsics - that is, intrinsics that are used in +// cross platform code but do not appear in the API + +#if !PX_WINDOWS_FAMILY +#error "This file should only be included by Windows builds!!" +#endif + +#pragma warning(push) +//'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#pragma warning(disable : 4668) +#if PX_VC == 10 +#pragma warning(disable : 4987) // nonstandard extension used: 'throw (...)' +#endif +#include +#pragma warning(pop) + +#pragma warning(push) +#pragma warning(disable : 4985) // 'symbol name': attributes not present on previous declaration +#include +#pragma warning(pop) + +#include +// do not include for ARM target +#if !PX_ARM +#include +#endif + +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) + +namespace physx +{ +namespace shdfnd +{ + +/* +* Implements a memory barrier +*/ +PX_FORCE_INLINE void memoryBarrier() +{ + _ReadWriteBarrier(); + /* long Barrier; + __asm { + xchg Barrier, eax + }*/ +} + +/*! +Returns the index of the highest set bit. Not valid for zero arg. +*/ +PX_FORCE_INLINE uint32_t highestSetBitUnsafe(uint32_t v) +{ + unsigned long retval; + _BitScanReverse(&retval, v); + return retval; +} + +/*! +Returns the index of the highest set bit. Undefined for zero arg. +*/ +PX_FORCE_INLINE uint32_t lowestSetBitUnsafe(uint32_t v) +{ + unsigned long retval; + _BitScanForward(&retval, v); + return retval; +} + +/*! +Returns the number of leading zeros in v. Returns 32 for v=0. +*/ +PX_FORCE_INLINE uint32_t countLeadingZeros(uint32_t v) +{ + if(v) + { + unsigned long bsr = (unsigned long)-1; + _BitScanReverse(&bsr, v); + return 31 - bsr; + } + else + return 32; +} + +/*! +Prefetch aligned cache size around \c ptr+offset. +*/ +#if !PX_ARM +PX_FORCE_INLINE void prefetchLine(const void* ptr, uint32_t offset = 0) +{ + // cache line on X86/X64 is 64-bytes so a 128-byte prefetch would require 2 prefetches. + // However, we can only dispatch a limited number of prefetch instructions so we opt to prefetch just 1 cache line + /*_mm_prefetch(((const char*)ptr + offset), _MM_HINT_T0);*/ + // We get slightly better performance prefetching to non-temporal addresses instead of all cache levels + _mm_prefetch(((const char*)ptr + offset), _MM_HINT_NTA); +} +#else +PX_FORCE_INLINE void prefetchLine(const void* ptr, uint32_t offset = 0) +{ + // arm does have 32b cache line size + __prefetch(((const char*)ptr + offset)); +} +#endif + +/*! +Prefetch \c count bytes starting at \c ptr. +*/ +#if !PX_ARM +PX_FORCE_INLINE void prefetch(const void* ptr, uint32_t count = 1) +{ + const char* cp = (char*)ptr; + uint64_t p = size_t(ptr); + uint64_t startLine = p >> 6, endLine = (p + count - 1) >> 6; + uint64_t lines = endLine - startLine + 1; + do + { + prefetchLine(cp); + cp += 64; + } while(--lines); +} +#else +PX_FORCE_INLINE void prefetch(const void* ptr, uint32_t count = 1) +{ + const char* cp = (char*)ptr; + uint32_t p = size_t(ptr); + uint32_t startLine = p >> 5, endLine = (p + count - 1) >> 5; + uint32_t lines = endLine - startLine + 1; + do + { + prefetchLine(cp); + cp += 32; + } while(--lines); +} +#endif + +//! \brief platform-specific reciprocal +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipFast(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific fast reciprocal square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrtFast(float a) +{ + return 1.0f / ::sqrtf(a); +} + +//! \brief platform-specific floor +PX_CUDA_CALLABLE PX_FORCE_INLINE float floatFloor(float x) +{ + return ::floorf(x); +} + +#define NS_EXPECT_TRUE(x) x +#define NS_EXPECT_FALSE(x) x + +} // namespace shdfnd +} // namespace physx + +#endif // #ifndef PSFOUNDATION_PSWINDOWSINTRINSICS_H diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h new file mode 100644 index 000000000..f622cfe41 --- /dev/null +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PSFOUNDATION_PSWINDOWSTRIGCONSTANTS_H +#define PSFOUNDATION_PSWINDOWSTRIGCONSTANTS_H + +namespace physx +{ +namespace shdfnd +{ +namespace aos +{ + +#define PX_GLOBALCONST extern const __declspec(selectany) + +__declspec(align(16)) struct PX_VECTORF32 +{ + float f[4]; +}; + +//#define PX_PI 3.141592654f +//#define PX_2PI 6.283185307f +//#define PX_1DIVPI 0.318309886f +//#define PX_1DIV2PI 0.159154943f +//#define PX_PIDIV2 1.570796327f +//#define PX_PIDIV4 0.785398163f + +PX_GLOBALCONST PX_VECTORF32 g_PXSinCoefficients0 = { { 1.0f, -0.166666667f, 8.333333333e-3f, -1.984126984e-4f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinCoefficients1 = { { 2.755731922e-6f, -2.505210839e-8f, 1.605904384e-10f, -7.647163732e-13f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinCoefficients2 = { { 2.811457254e-15f, -8.220635247e-18f, 1.957294106e-20f, -3.868170171e-23f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXCosCoefficients0 = { { 1.0f, -0.5f, 4.166666667e-2f, -1.388888889e-3f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosCoefficients1 = { { 2.480158730e-5f, -2.755731922e-7f, 2.087675699e-9f, -1.147074560e-11f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosCoefficients2 = { { 4.779477332e-14f, -1.561920697e-16f, 4.110317623e-19f, -8.896791392e-22f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTanCoefficients0 = { { 1.0f, 0.333333333f, 0.133333333f, 5.396825397e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXTanCoefficients1 = { { 2.186948854e-2f, 8.863235530e-3f, 3.592128167e-3f, 1.455834485e-3f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXTanCoefficients2 = { { 5.900274264e-4f, 2.391290764e-4f, 9.691537707e-5f, 3.927832950e-5f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients0 = { { -0.05806367563904f, -0.41861972469416f, 0.22480114791621f, 2.17337241360606f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients1 = { { 0.61657275907170f, 4.29696498283455f, -1.18942822255452f, -6.53784832094831f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinCoefficients2 = { { -1.36926553863413f, -4.48179294237210f, 1.41810672941833f, 5.48179257935713f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXATanCoefficients0 = { { 1.0f, 0.333333334f, 0.2f, 0.142857143f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanCoefficients1 = { { 1.111111111e-1f, 9.090909091e-2f, 7.692307692e-2f, 6.666666667e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanCoefficients2 = { { 5.882352941e-2f, 5.263157895e-2f, 4.761904762e-2f, 4.347826087e-2f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXSinEstCoefficients = { { 1.0f, -1.66521856991541e-1f, 8.199913018755e-3f, -1.61475937228e-4f } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXCosEstCoefficients = { { 1.0f, -4.95348008918096e-1f, 3.878259962881e-2f, -9.24587976263e-4f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTanEstCoefficients = { { 2.484f, -1.954923183e-1f, 2.467401101f, PxInvPi } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXATanEstCoefficients = { { 7.689891418951e-1f, 1.104742493348f, 8.661844266006e-1f, PxPiDivTwo } }; +PX_GLOBALCONST PX_VECTORF32 +g_PXASinEstCoefficients = { { -1.36178272886711f, 2.37949493464538f, -8.08228565650486e-1f, 2.78440142746736e-1f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXASinEstConstants = { { 1.00000011921f, PxPiDivTwo, 0.0f, 0.0f } }; +PX_GLOBALCONST PX_VECTORF32 g_PXPiConstants0 = { { PxPi, PxTwoPi, PxInvPi, PxInvTwoPi } }; +PX_GLOBALCONST PX_VECTORF32 g_PXReciprocalTwoPi = { { PxInvTwoPi, PxInvTwoPi, PxInvTwoPi, PxInvTwoPi } }; +PX_GLOBALCONST PX_VECTORF32 g_PXTwoPi = { { PxTwoPi, PxTwoPi, PxTwoPi, PxTwoPi } }; + +} // namespace aos +} // namespace shdfnd +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/foundation/src/PsAllocator.cpp b/src/PhysX/physx/source/foundation/src/PsAllocator.cpp new file mode 100644 index 000000000..8ffff92ba --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsAllocator.cpp @@ -0,0 +1,124 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsFoundation.h" +#include "PsAllocator.h" +#include "PsHashMap.h" +#include "PsArray.h" +#include "PsMutex.h" + +namespace physx +{ +namespace shdfnd +{ + +#if PX_USE_NAMED_ALLOCATOR +namespace +{ +typedef HashMap, NonTrackingAllocator> AllocNameMap; +PX_INLINE AllocNameMap& getMap() +{ + return getFoundation().getNamedAllocMap(); +} +PX_INLINE Foundation::Mutex& getMutex() +{ + return getFoundation().getNamedAllocMutex(); +} +} + +NamedAllocator::NamedAllocator(const PxEMPTY) +{ + Foundation::Mutex::ScopedLock lock(getMutex()); + getMap().insert(this, 0); +} + +NamedAllocator::NamedAllocator(const char* name) +{ + Foundation::Mutex::ScopedLock lock(getMutex()); + getMap().insert(this, name); +} + +NamedAllocator::NamedAllocator(const NamedAllocator& other) +{ + Foundation::Mutex::ScopedLock lock(getMutex()); + const AllocNameMap::Entry* e = getMap().find(&other); + PX_ASSERT(e); + const char* name = e->second; // The copy is important because insert might invalidate the referenced hash entry + getMap().insert(this, name); +} + +NamedAllocator::~NamedAllocator() +{ + Foundation::Mutex::ScopedLock lock(getMutex()); + bool erased = getMap().erase(this); + PX_UNUSED(erased); + PX_ASSERT(erased); +} + +NamedAllocator& NamedAllocator::operator=(const NamedAllocator& other) +{ + Foundation::Mutex::ScopedLock lock(getMutex()); + const AllocNameMap::Entry* e = getMap().find(&other); + PX_ASSERT(e); + getMap()[this] = e->second; + return *this; +} + +void* NamedAllocator::allocate(size_t size, const char* filename, int line) +{ + if(!size) + return 0; + Foundation::Mutex::ScopedLock lock(getMutex()); + const AllocNameMap::Entry* e = getMap().find(this); + PX_ASSERT(e); + return getAllocator().allocate(size, e->second, filename, line); +} + +void NamedAllocator::deallocate(void* ptr) +{ + if(ptr) + getAllocator().deallocate(ptr); +} + +#endif // PX_DEBUG + +void* Allocator::allocate(size_t size, const char* file, int line) +{ + if(!size) + return 0; + return getAllocator().allocate(size, "", file, line); +} +void Allocator::deallocate(void* ptr) +{ + if(ptr) + getAllocator().deallocate(ptr); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/PsAssert.cpp b/src/PhysX/physx/source/foundation/src/PsAssert.cpp new file mode 100644 index 000000000..a6e5a6143 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsAssert.cpp @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" + +#include +#include "PsString.h" + +#if PX_WINDOWS_FAMILY +#include +#elif PX_SWITCH +#include "switch/PsSwitchAbort.h" +#endif + +namespace +{ +class DefaultAssertHandler : public physx::PxAssertHandler +{ + virtual void operator()(const char* expr, const char* file, int line, bool& ignore) + { + PX_UNUSED(ignore); // is used only in debug windows config + char buffer[1024]; +#if PX_WINDOWS_FAMILY + sprintf_s(buffer, "%s(%d) : Assertion failed: %s\n", file, line, expr); +#else + sprintf(buffer, "%s(%d) : Assertion failed: %s\n", file, line, expr); +#endif + physx::shdfnd::printString(buffer); +#if PX_WINDOWS_FAMILY&& PX_DEBUG && PX_DEBUG_CRT + // _CrtDbgReport returns -1 on error, 1 on 'retry', 0 otherwise including 'ignore'. + // Hitting 'abort' will terminate the process immediately. + int result = _CrtDbgReport(_CRT_ASSERT, file, line, NULL, "%s", buffer); + int mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_REPORT_MODE); + ignore = _CRTDBG_MODE_WNDW == mode && result == 0; + if(ignore) + return; + __debugbreak(); +#elif PX_WINDOWS_FAMILY&& PX_CHECKED + __debugbreak(); +#elif PX_SWITCH + abort(buffer); +#else + abort(); +#endif + } +}; + +DefaultAssertHandler sAssertHandler; +physx::PxAssertHandler* sAssertHandlerPtr = &sAssertHandler; +} + +namespace physx +{ + +PxAssertHandler& PxGetAssertHandler() +{ + return *sAssertHandlerPtr; +} + +void PxSetAssertHandler(PxAssertHandler& handler) +{ + sAssertHandlerPtr = &handler; +} +} // end of physx namespace diff --git a/src/PhysX/physx/source/foundation/src/PsFoundation.cpp b/src/PhysX/physx/source/foundation/src/PsFoundation.cpp new file mode 100644 index 000000000..f2edf3f87 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsFoundation.cpp @@ -0,0 +1,284 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxProfiler.h" +#include "foundation/PxErrorCallback.h" +#include "PxPhysicsVersion.h" +#include "PsFoundation.h" +#include "PsString.h" +#include "PsAllocator.h" + +namespace physx +{ +namespace shdfnd +{ + +Foundation::Foundation(PxErrorCallback& errc, PxAllocatorCallback& alloc) +: mAllocatorCallback(alloc) +, mErrorCallback(errc) +, mBroadcastingAllocator(alloc, errc) +, mBroadcastingError(errc) +, +#if PX_CHECKED + mReportAllocationNames(true) +, +#else + mReportAllocationNames(false) +, +#endif + mErrorMask(PxErrorCode::Enum(~0)) +, mErrorMutex(PX_DEBUG_EXP("Foundation::mErrorMutex")) +, mNamedAllocMutex(PX_DEBUG_EXP("Foundation::mNamedAllocMutex")) +, mTempAllocMutex(PX_DEBUG_EXP("Foundation::mTempAllocMutex")) +{ +} + +Foundation::~Foundation() +{ + // deallocate temp buffer allocations + Allocator alloc; + for(PxU32 i = 0; i < mTempAllocFreeTable.size(); ++i) + { + for(TempAllocatorChunk* ptr = mTempAllocFreeTable[i]; ptr;) + { + TempAllocatorChunk* next = ptr->mNext; + alloc.deallocate(ptr); + ptr = next; + } + } + mTempAllocFreeTable.reset(); +} + +Foundation& Foundation::getInstance() +{ + PX_ASSERT(mInstance); + return *mInstance; +} + +void Foundation::setInstance(Foundation& foundation) +{ + PX_ASSERT(mInstance == NULL || &foundation == mInstance); + mInstance = &foundation; +} + +PxU32 Foundation::getWarnOnceTimestamp() +{ + PX_ASSERT(mInstance != NULL); + return mWarnOnceTimestap; +} + +void Foundation::error(PxErrorCode::Enum c, const char* file, int line, const char* messageFmt, ...) +{ + va_list va; + va_start(va, messageFmt); + errorImpl(c, file, line, messageFmt, va); + va_end(va); +} + +void Foundation::errorImpl(PxErrorCode::Enum e, const char* file, int line, const char* messageFmt, va_list va) +{ + PX_ASSERT(messageFmt); + if(e & mErrorMask) + { + // this function is reentrant but user's error callback may not be, so... + Mutex::ScopedLock lock(mErrorMutex); + + // using a static fixed size buffer here because: + // 1. vsnprintf return values differ between platforms + // 2. va_start is only usable in functions with ellipses + // 3. ellipses (...) cannot be passed to called function + // which would be necessary to dynamically grow the buffer here + + static const size_t bufSize = 1024; + char stringBuffer[bufSize]; + shdfnd::vsnprintf(stringBuffer, bufSize, messageFmt, va); + + mBroadcastingError.reportError(e, stringBuffer, file, line); + } +} + +Foundation* Foundation::createInstance(PxU32 version, PxErrorCallback& errc, PxAllocatorCallback& alloc) +{ + if(version != PX_PHYSICS_VERSION) + { + char* buffer = new char[256]; + physx::shdfnd::snprintf(buffer, 256, "Wrong version: physics version is 0x%08x, tried to create 0x%08x", + PX_PHYSICS_VERSION, version); + errc.reportError(PxErrorCode::eINVALID_PARAMETER, buffer, __FILE__, __LINE__); + return 0; + } + + if(!mInstance) + { + // if we don't assign this here, the Foundation object can't create member + // subobjects which require the allocator + + mInstance = reinterpret_cast(alloc.allocate(sizeof(Foundation), "Foundation", __FILE__, __LINE__)); + + if(mInstance) + { + PX_PLACEMENT_NEW(mInstance, Foundation)(errc, alloc); + + PX_ASSERT(mRefCount == 0); + mRefCount = 1; + + // skip 0 which marks uninitialized timestaps in PX_WARN_ONCE + mWarnOnceTimestap = (mWarnOnceTimestap == PX_MAX_U32) ? 1 : mWarnOnceTimestap + 1; + + return mInstance; + } + else + { + errc.reportError(PxErrorCode::eINTERNAL_ERROR, "Memory allocation for foundation object failed.", __FILE__, + __LINE__); + } + } + else + { + errc.reportError(PxErrorCode::eINVALID_OPERATION, + "Foundation object exists already. Only one instance per process can be created.", __FILE__, + __LINE__); + } + + return 0; +} + +void Foundation::destroyInstance() +{ + PX_ASSERT(mInstance != NULL); + + if(mRefCount == 1) + { + PxAllocatorCallback& alloc = mInstance->getAllocatorCallback(); + mInstance->~Foundation(); + alloc.deallocate(mInstance); + mInstance = 0; + mRefCount = 0; + } + else + { + mInstance->error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Foundation destruction failed due to pending module references. Close/release all depending " + "modules first."); + } +} + +void Foundation::incRefCount() +{ + PX_ASSERT(mInstance != NULL); + + if(mRefCount > 0) + { + mRefCount++; + } + else + { + mInstance->error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Foundation: Invalid registration detected."); + } +} + +void Foundation::decRefCount() +{ + PX_ASSERT(mInstance != NULL); + + if(mRefCount > 0) + { + mRefCount--; + } + else + { + mInstance->error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Foundation: Invalid deregistration detected."); + } +} + +void Foundation::release() +{ + Foundation::destroyInstance(); +} + +PxAllocatorCallback& getAllocator() +{ + return getFoundation().getAllocator(); +} + +Foundation* Foundation::mInstance = NULL; +PxU32 Foundation::mRefCount = 0; +PxU32 Foundation::mWarnOnceTimestap = 0; + +void Foundation::registerAllocationListener(physx::shdfnd::AllocationListener& listener) +{ + Mutex::ScopedLock lock(mListenerMutex); + mBroadcastingAllocator.registerListener(listener); +} + +void Foundation::deregisterAllocationListener(physx::shdfnd::AllocationListener& listener) +{ + Mutex::ScopedLock lock(mListenerMutex); + mBroadcastingAllocator.deregisterListener(listener); +} + +void Foundation::registerErrorCallback(PxErrorCallback& callback) +{ + Mutex::ScopedLock lock(mListenerMutex); + mBroadcastingError.registerListener(callback); +} + +void Foundation::deregisterErrorCallback(PxErrorCallback& callback) +{ + Mutex::ScopedLock lock(mListenerMutex); + mBroadcastingError.deregisterListener(callback); +} + +physx::PxProfilerCallback* gProfilerCallback = NULL; + +} // namespace shdfnd +} // namespace physx + +physx::PxFoundation* PxCreateFoundation(physx::PxU32 version, physx::PxAllocatorCallback& allocator, + physx::PxErrorCallback& errorCallback) +{ + return physx::shdfnd::Foundation::createInstance(version, errorCallback, allocator); +} + +physx::PxFoundation& PxGetFoundation() +{ + return physx::shdfnd::Foundation::getInstance(); +} + +physx::PxProfilerCallback* PxGetProfilerCallback() +{ + return physx::shdfnd::gProfilerCallback; +} + +void PxSetProfilerCallback(physx::PxProfilerCallback* profiler) +{ + physx::shdfnd::gProfilerCallback = profiler; +} diff --git a/src/PhysX/physx/source/foundation/src/PsMathUtils.cpp b/src/PhysX/physx/source/foundation/src/PsMathUtils.cpp new file mode 100644 index 000000000..9aa30690b --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsMathUtils.cpp @@ -0,0 +1,212 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMat33.h" +#include "foundation/PxMathUtils.h" +#include "foundation/PxVec4.h" +#include "foundation/PxAssert.h" +#include "PsMathUtils.h" +#include "PsUtilities.h" +#include "PsBasicTemplates.h" + +using namespace physx; +using namespace physx::shdfnd; +using namespace physx::intrinsics; + +PX_FOUNDATION_API PxQuat physx::PxShortestRotation(const PxVec3& v0, const PxVec3& v1) +{ + const PxReal d = v0.dot(v1); + const PxVec3 cross = v0.cross(v1); + + PxQuat q = d > -1 ? PxQuat(cross.x, cross.y, cross.z, 1 + d) : PxAbs(v0.x) < 0.1f ? PxQuat(0.0f, v0.z, -v0.y, 0.0f) + : PxQuat(v0.y, -v0.x, 0.0f, 0.0f); + + return q.getNormalized(); +} + +namespace +{ +// indexed rotation around axis, with sine and cosine of half-angle +PxQuat indexedRotation(PxU32 axis, PxReal s, PxReal c) +{ + PxReal v[3] = { 0, 0, 0 }; + v[axis] = s; + return PxQuat(v[0], v[1], v[2], c); +} +} + +PX_FOUNDATION_API PxVec3 physx::PxDiagonalize(const PxMat33& m, PxQuat& massFrame) +{ + // jacobi rotation using quaternions (from an idea of Stan Melax, with fix for precision issues) + + const PxU32 MAX_ITERS = 24; + + PxQuat q = PxQuat(PxIdentity); + + PxMat33 d; + for(PxU32 i = 0; i < MAX_ITERS; i++) + { + PxMat33 axes(q); + d = axes.getTranspose() * m * axes; + + PxReal d0 = PxAbs(d[1][2]), d1 = PxAbs(d[0][2]), d2 = PxAbs(d[0][1]); + PxU32 a = PxU32(d0 > d1 && d0 > d2 ? 0 : d1 > d2 ? 1 : 2); // rotation axis index, from largest off-diagonal + // element + + PxU32 a1 = shdfnd::getNextIndex3(a), a2 = shdfnd::getNextIndex3(a1); + if(d[a1][a2] == 0.0f || PxAbs(d[a1][a1] - d[a2][a2]) > 2e6f * PxAbs(2.0f * d[a1][a2])) + break; + + PxReal w = (d[a1][a1] - d[a2][a2]) / (2.0f * d[a1][a2]); // cot(2 * phi), where phi is the rotation angle + PxReal absw = PxAbs(w); + + PxQuat r; + if(absw > 1000) + r = indexedRotation(a, 1 / (4 * w), 1.f); // h will be very close to 1, so use small angle approx instead + else + { + PxReal t = 1 / (absw + PxSqrt(w * w + 1)); // absolute value of tan phi + PxReal h = 1 / PxSqrt(t * t + 1); // absolute value of cos phi + + PX_ASSERT(h != 1); // |w|<1000 guarantees this with typical IEEE754 machine eps (approx 6e-8) + r = indexedRotation(a, PxSqrt((1 - h) / 2) * PxSign(w), PxSqrt((1 + h) / 2)); + } + + q = (q * r).getNormalized(); + } + + massFrame = q; + return PxVec3(d.column0.x, d.column1.y, d.column2.z); +} + +/** +\brief computes a oriented bounding box around the scaled basis. +\param basis Input = skewed basis, Output = (normalized) orthogonal basis. +\return Bounding box extent. +*/ +PxVec3 physx::shdfnd::optimizeBoundingBox(PxMat33& basis) +{ + PxVec3* PX_RESTRICT vec = &basis[0]; // PT: don't copy vectors if not needed... + + // PT: since we store the magnitudes to memory, we can avoid the FCMPs afterwards + PxVec3 magnitude(vec[0].magnitudeSquared(), vec[1].magnitudeSquared(), vec[2].magnitudeSquared()); + + // find indices sorted by magnitude + unsigned int i = magnitude[1] > magnitude[0] ? 1 : 0u; + unsigned int j = magnitude[2] > magnitude[1 - i] ? 2 : 1 - i; + const unsigned int k = 3 - i - j; + + if(magnitude[i] < magnitude[j]) + swap(i, j); + + PX_ASSERT(magnitude[i] >= magnitude[j] && magnitude[i] >= magnitude[k] && magnitude[j] >= magnitude[k]); + + // ortho-normalize basis + + PxReal invSqrt = PxRecipSqrt(magnitude[i]); + magnitude[i] *= invSqrt; + vec[i] *= invSqrt; // normalize the first axis + PxReal dotij = vec[i].dot(vec[j]); + PxReal dotik = vec[i].dot(vec[k]); + magnitude[i] += PxAbs(dotij) + PxAbs(dotik); // elongate the axis by projection of the other two + vec[j] -= vec[i] * dotij; // orthogonize the two remaining axii relative to vec[i] + vec[k] -= vec[i] * dotik; + + magnitude[j] = vec[j].normalize(); + PxReal dotjk = vec[j].dot(vec[k]); + magnitude[j] += PxAbs(dotjk); // elongate the axis by projection of the other one + vec[k] -= vec[j] * dotjk; // orthogonize vec[k] relative to vec[j] + + magnitude[k] = vec[k].normalize(); + + return magnitude; +} + +PxQuat physx::shdfnd::slerp(const PxReal t, const PxQuat& left, const PxQuat& right) +{ + const PxReal quatEpsilon = (PxReal(1.0e-8f)); + + PxReal cosine = left.dot(right); + PxReal sign = PxReal(1); + if(cosine < 0) + { + cosine = -cosine; + sign = PxReal(-1); + } + + PxReal sine = PxReal(1) - cosine * cosine; + + if(sine >= quatEpsilon * quatEpsilon) + { + sine = PxSqrt(sine); + const PxReal angle = PxAtan2(sine, cosine); + const PxReal i_sin_angle = PxReal(1) / sine; + + const PxReal leftw = PxSin(angle * (PxReal(1) - t)) * i_sin_angle; + const PxReal rightw = PxSin(angle * t) * i_sin_angle * sign; + + return left * leftw + right * rightw; + } + + return left; +} + +void physx::shdfnd::integrateTransform(const PxTransform& curTrans, const PxVec3& linvel, const PxVec3& angvel, + PxReal timeStep, PxTransform& result) +{ + result.p = curTrans.p + linvel * timeStep; + + // from void DynamicsContext::integrateAtomPose(PxsRigidBody* atom, Cm::BitMap &shapeChangedMap) const: + // Integrate the rotation using closed form quaternion integrator + PxReal w = angvel.magnitudeSquared(); + + if(w != 0.0f) + { + w = PxSqrt(w); + if(w != 0.0f) + { + const PxReal v = timeStep * w * 0.5f; + const PxReal q = PxCos(v); + const PxReal s = PxSin(v) / w; + + const PxVec3 pqr = angvel * s; + const PxQuat quatVel(pqr.x, pqr.y, pqr.z, 0); + PxQuat out; // need to have temporary, otherwise we may overwrite input if &curTrans == &result. + out = quatVel * curTrans.q; + out.x += curTrans.q.x * q; + out.y += curTrans.q.y * q; + out.z += curTrans.q.z * q; + out.w += curTrans.q.w * q; + result.q = out; + return; + } + } + // orientation stays the same - convert from quat to matrix: + result.q = curTrans.q; +} diff --git a/src/PhysX/physx/source/foundation/src/PsString.cpp b/src/PhysX/physx/source/foundation/src/PsString.cpp new file mode 100644 index 000000000..1916fb0df --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsString.cpp @@ -0,0 +1,185 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsString.h" +#include +#include +#include + +#if PX_WINDOWS_FAMILY +#pragma warning(push) +#pragma warning(disable : 4996) // unsafe string functions +#endif + +#if PX_PS4 || PX_APPLE_FAMILY +#pragma clang diagnostic push +// error : format string is not a string literal +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif + +namespace physx +{ +namespace shdfnd +{ +// cross-platform implementations + +int32_t strcmp(const char* str1, const char* str2) +{ + return ::strcmp(str1, str2); +} + +int32_t strncmp(const char* str1, const char* str2, size_t count) +{ + return ::strncmp(str1, str2, count); +} + +int32_t snprintf(char* dst, size_t dstSize, const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int32_t r = shdfnd::vsnprintf(dst, dstSize, format, arg); + va_end(arg); + return r; +} + +int32_t sscanf(const char* buffer, const char* format, ...) +{ + va_list arg; + va_start(arg, format); +#if (PX_VC < 12) && !PX_LINUX + int32_t r = ::sscanf(buffer, format, arg); +#else + int32_t r = ::vsscanf(buffer, format, arg); +#endif + va_end(arg); + + return r; +} + +size_t strlcpy(char* dst, size_t dstSize, const char* src) +{ + size_t i = 0; + if(dst && dstSize) + { + for(; i + 1 < dstSize && src[i]; i++) // copy up to dstSize-1 bytes + dst[i] = src[i]; + dst[i] = 0; // always null-terminate + } + + while(src[i]) // read any remaining characters in the src string to get the length + i++; + + return i; +} + +size_t strlcat(char* dst, size_t dstSize, const char* src) +{ + size_t i = 0, s = 0; + if(dst && dstSize) + { + s = strlen(dst); + for(; i + s + 1 < dstSize && src[i]; i++) // copy until total is at most dstSize-1 + dst[i + s] = src[i]; + dst[i + s] = 0; // always null-terminate + } + + while(src[i]) // read any remaining characters in the src string to get the length + i++; + + return i + s; +} + +void strlwr(char* str) +{ + for(; *str; str++) + if(*str >= 'A' && *str <= 'Z') + *str += 32; +} + +void strupr(char* str) +{ + for(; *str; str++) + if(*str >= 'a' && *str <= 'z') + *str -= 32; +} + +int32_t vsnprintf(char* dst, size_t dstSize, const char* src, va_list arg) +{ + +#if PX_VC // MSVC is not C99-compliant... + int32_t result = dst ? ::vsnprintf(dst, dstSize, src, arg) : -1; + if(dst && (result == int32_t(dstSize) || result < 0)) + dst[dstSize - 1] = 0; // string was truncated or there wasn't room for the NULL + if(result < 0) + result = _vscprintf(src, arg); // work out how long the answer would have been. +#else + int32_t result = ::vsnprintf(dst, dstSize, src, arg); +#endif + return result; +} + +int32_t stricmp(const char* str, const char* str1) +{ +#if PX_VC + return (::_stricmp(str, str1)); +#else + return (::strcasecmp(str, str1)); +#endif +} + +int32_t strnicmp(const char* str, const char* str1, size_t n) +{ +#if PX_VC + return (::_strnicmp(str, str1, n)); +#else + return (::strncasecmp(str, str1, n)); +#endif +} + +void printFormatted(const char* format, ...) +{ + char buf[MAX_PRINTFORMATTED_LENGTH]; + + va_list arg; + va_start(arg, format); + vsnprintf(buf, MAX_PRINTFORMATTED_LENGTH, format, arg); + va_end(arg); + + printString(buf); +} +} +} + +#if PX_PS4 || PX_APPLE_FAMILY +#pragma clang diagnostic pop +#endif + +#if PX_WINDOWS_FAMILY +#pragma warning(pop) +#endif diff --git a/src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp b/src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp new file mode 100644 index 000000000..f174168ed --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp @@ -0,0 +1,129 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMath.h" + +#include "PsFoundation.h" +#include "PsTempAllocator.h" +#include "PsArray.h" +#include "PsMutex.h" +#include "PsAtomic.h" +#include "PsIntrinsics.h" +#include "PsBitUtils.h" + +#if PX_VC +#pragma warning(disable : 4706) // assignment within conditional expression +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace +{ +typedef TempAllocatorChunk Chunk; +typedef Array AllocFreeTable; + +PX_INLINE Foundation::AllocFreeTable& getFreeTable() +{ + return getFoundation().getTempAllocFreeTable(); +} +PX_INLINE Foundation::Mutex& getMutex() +{ + return getFoundation().getTempAllocMutex(); +} + +const PxU32 sMinIndex = 8; // 256B min +const PxU32 sMaxIndex = 17; // 128kB max +} + +void* TempAllocator::allocate(size_t size, const char* filename, int line) +{ + if(!size) + return 0; + + uint32_t index = PxMax(highestSetBit(uint32_t(size) + sizeof(Chunk) - 1), sMinIndex); + + Chunk* chunk = 0; + if(index < sMaxIndex) + { + Foundation::Mutex::ScopedLock lock(getMutex()); + + // find chunk up to 16x bigger than necessary + Chunk** it = getFreeTable().begin() + index - sMinIndex; + Chunk** end = PxMin(it + 3, getFreeTable().end()); + while(it < end && !(*it)) + ++it; + + if(it < end) + { + // pop top off freelist + chunk = *it; + *it = chunk->mNext; + index = uint32_t(it - getFreeTable().begin() + sMinIndex); + } + else + // create new chunk + chunk = reinterpret_cast(NonTrackingAllocator().allocate(size_t(2 << index), filename, line)); + } + else + { + // too big for temp allocation, forward to base allocator + chunk = reinterpret_cast(NonTrackingAllocator().allocate(size + sizeof(Chunk), filename, line)); + } + + chunk->mIndex = index; + void* ret = chunk + 1; + PX_ASSERT((size_t(ret) & 0xf) == 0); // SDK types require at minimum 16 byte allignment. + return ret; +} + +void TempAllocator::deallocate(void* ptr) +{ + if(!ptr) + return; + + Chunk* chunk = reinterpret_cast(ptr) - 1; + uint32_t index = chunk->mIndex; + + if(index >= sMaxIndex) + return NonTrackingAllocator().deallocate(chunk); + + Foundation::Mutex::ScopedLock lock(getMutex()); + + index -= sMinIndex; + if(getFreeTable().size() <= index) + getFreeTable().resize(index + 1); + + chunk->mNext = getFreeTable()[index]; + getFreeTable()[index] = chunk; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/PsUtilities.cpp b/src/PhysX/physx/source/foundation/src/PsUtilities.cpp new file mode 100644 index 000000000..1d1e174ba --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/PsUtilities.cpp @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMat33.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsUtilities.h" +#include "PsUserAllocated.h" +#include "PsFPU.h" + +namespace physx +{ +namespace shdfnd +{ + +bool checkValid(const float& f) +{ + return PxIsFinite(f); +} +bool checkValid(const PxVec3& v) +{ + return PxIsFinite(v.x) && PxIsFinite(v.y) && PxIsFinite(v.z); +} + +bool checkValid(const PxTransform& t) +{ + return checkValid(t.p) && checkValid(t.q); +} + +bool checkValid(const PxQuat& q) +{ + return PxIsFinite(q.x) && PxIsFinite(q.y) && PxIsFinite(q.z) && PxIsFinite(q.w); +} +bool checkValid(const PxMat33& m) +{ + return PxIsFinite(m(0, 0)) && PxIsFinite(m(1, 0)) && PxIsFinite(m(2, 0)) && PxIsFinite(m(0, 1)) && + PxIsFinite(m(1, 1)) && PxIsFinite(m(2, 1)) && PxIsFinite(m(0, 3)) && PxIsFinite(m(1, 3)) && + PxIsFinite(m(2, 3)); +} +bool checkValid(const char* string) +{ + static const PxU32 maxLength = 4096; + return strnlen(string, maxLength) != maxLength; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp new file mode 100644 index 000000000..9ca9249cf --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp @@ -0,0 +1,102 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#include "Ps.h" +#include "PsAtomic.h" + +#if ! PX_EMSCRIPTEN +#define PAUSE() asm("nop") +#else +#define PAUSE() +#endif + +namespace physx +{ +namespace shdfnd +{ + +void* atomicCompareExchangePointer(volatile void** dest, void* exch, void* comp) +{ + return __sync_val_compare_and_swap(const_cast(dest), comp, exch); +} + +int32_t atomicCompareExchange(volatile int32_t* dest, int32_t exch, int32_t comp) +{ + return __sync_val_compare_and_swap(dest, comp, exch); +} + +int32_t atomicIncrement(volatile int32_t* val) +{ + return __sync_add_and_fetch(val, 1); +} + +int32_t atomicDecrement(volatile int32_t* val) +{ + return __sync_sub_and_fetch(val, 1); +} + +int32_t atomicAdd(volatile int32_t* val, int32_t delta) +{ + return __sync_add_and_fetch(val, delta); +} + +int32_t atomicMax(volatile int32_t* val, int32_t val2) +{ + int32_t oldVal, newVal; + + do + { + PAUSE(); + oldVal = *val; + + if(val2 > oldVal) + newVal = val2; + else + newVal = oldVal; + + } while(atomicCompareExchange(val, newVal, oldVal) != oldVal); + + return *val; +} + +int32_t atomicExchange(volatile int32_t* val, int32_t val2) +{ + int32_t newVal, oldVal; + + do + { + PAUSE(); + oldVal = *val; + newVal = val2; + } while(atomicCompareExchange(val, newVal, oldVal) != oldVal); + + return oldVal; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp new file mode 100644 index 000000000..dda248c39 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp @@ -0,0 +1,58 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxSimpleTypes.h" +#include "PsCpu.h" + +#if PX_X86 && !defined(__EMSCRIPTEN__) +#define cpuid(op, reg) \ + __asm__ __volatile__("pushl %%ebx \n\t" /* save %ebx */ \ + "cpuid \n\t" \ + "movl %%ebx, %1 \n\t" /* save what cpuid just put in %ebx */ \ + "popl %%ebx \n\t" /* restore the old %ebx */ \ + : "=a"(reg[0]), "=r"(reg[1]), "=c"(reg[2]), "=d"(reg[3]) \ + : "a"(op) \ + : "cc") +#else +#define cpuid(op, reg) reg[0] = reg[1] = reg[2] = reg[3] = 0; +#endif + +namespace physx +{ +namespace shdfnd +{ + +uint8_t Cpu::getCpuId() +{ + uint32_t cpuInfo[4]; + cpuid(1, cpuInfo); + return static_cast(cpuInfo[1] >> 24); // APIC Physical ID +} +} +} diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp new file mode 100644 index 000000000..b6fc57c40 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#include "PsFPU.h" + +#if !(defined(__CYGWIN__) || PX_ANDROID || PX_PS4) +#include +PX_COMPILE_TIME_ASSERT(8 * sizeof(uint32_t) >= sizeof(fenv_t)); +#endif + +#if PX_OSX +// osx defines SIMD as standard for floating point operations. +#include +#endif + +physx::shdfnd::FPUGuard::FPUGuard() +{ +#if defined(__CYGWIN__) +#pragma message "FPUGuard::FPUGuard() is not implemented" +#elif PX_ANDROID +// not supported unless ARM_HARD_FLOAT is enabled. +#elif PX_PS4 + // not supported + PX_UNUSED(mControlWords); +#elif PX_OSX + mControlWords[0] = _mm_getcsr(); + // set default (disable exceptions: _MM_MASK_MASK) and FTZ (_MM_FLUSH_ZERO_ON), DAZ (_MM_DENORMALS_ZERO_ON: (1<<6)) + _mm_setcsr(_MM_MASK_MASK | _MM_FLUSH_ZERO_ON | (1 << 6)); +#elif defined(__EMSCRIPTEN__) +// not supported +#else + PX_COMPILE_TIME_ASSERT(sizeof(fenv_t) <= sizeof(mControlWords)); + + fegetenv(reinterpret_cast(mControlWords)); + fesetenv(FE_DFL_ENV); + +#if PX_LINUX + // need to explicitly disable exceptions because fesetenv does not modify + // the sse control word on 32bit linux (64bit is fine, but do it here just be sure) + fedisableexcept(FE_ALL_EXCEPT); +#endif + +#endif +} + +physx::shdfnd::FPUGuard::~FPUGuard() +{ +#if defined(__CYGWIN__) +#pragma message "FPUGuard::~FPUGuard() is not implemented" +#elif PX_ANDROID +// not supported unless ARM_HARD_FLOAT is enabled. +#elif PX_PS4 +// not supported +#elif PX_OSX + // restore control word and clear exception flags + // (setting exception state flags cause exceptions on the first following fp operation) + _mm_setcsr(mControlWords[0] & ~_MM_EXCEPT_MASK); +#elif defined(__EMSCRIPTEN__) +// not supported +#else + fesetenv(reinterpret_cast(mControlWords)); +#endif +} + +PX_FOUNDATION_API void physx::shdfnd::enableFPExceptions() +{ +#if PX_LINUX && !defined(__EMSCRIPTEN__) + feclearexcept(FE_ALL_EXCEPT); + feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW); +#elif PX_OSX + // clear any pending exceptions + // (setting exception state flags cause exceptions on the first following fp operation) + uint32_t control = _mm_getcsr() & ~_MM_EXCEPT_MASK; + + // enable all fp exceptions except inexact and underflow (common, benign) + // note: denorm has to be disabled as well because underflow can create denorms + _mm_setcsr((control & ~_MM_MASK_MASK) | _MM_MASK_INEXACT | _MM_MASK_UNDERFLOW | _MM_MASK_DENORM); + +#endif +} + +PX_FOUNDATION_API void physx::shdfnd::disableFPExceptions() +{ +#if PX_LINUX && !defined(__EMSCRIPTEN__) + fedisableexcept(FE_ALL_EXCEPT); +#elif PX_OSX + // clear any pending exceptions + // (setting exception state flags cause exceptions on the first following fp operation) + uint32_t control = _mm_getcsr() & ~_MM_EXCEPT_MASK; + _mm_setcsr(control | _MM_MASK_MASK); +#endif +} diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp new file mode 100644 index 000000000..194e23ff3 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp @@ -0,0 +1,172 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" +#include "foundation/PxErrorCallback.h" + +#include "Ps.h" +#include "PsFoundation.h" +#include "PsUserAllocated.h" +#include "PsMutex.h" +#include "PsAtomic.h" +#include "PsThread.h" + +#include + +namespace physx +{ +namespace shdfnd +{ + +namespace +{ +struct MutexUnixImpl +{ + pthread_mutex_t lock; + Thread::Id owner; +}; + +MutexUnixImpl* getMutex(MutexImpl* impl) +{ + return reinterpret_cast(impl); +} +} + +MutexImpl::MutexImpl() +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#if !PX_ANDROID + // mimic default windows behavior where applicable + pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); +#endif + pthread_mutex_init(&getMutex(this)->lock, &attr); + pthread_mutexattr_destroy(&attr); +} + +MutexImpl::~MutexImpl() +{ + pthread_mutex_destroy(&getMutex(this)->lock); +} + +void MutexImpl::lock() +{ + int err = pthread_mutex_lock(&getMutex(this)->lock); + PX_ASSERT(!err); + PX_UNUSED(err); + +#if PX_DEBUG + getMutex(this)->owner = Thread::getId(); +#endif +} + +bool MutexImpl::trylock() +{ + bool success = !pthread_mutex_trylock(&getMutex(this)->lock); +#if PX_DEBUG + if(success) + getMutex(this)->owner = Thread::getId(); +#endif + return success; +} + +void MutexImpl::unlock() +{ +#if PX_DEBUG + if(getMutex(this)->owner != Thread::getId()) + { + shdfnd::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Mutex must be unlocked only by thread that has already acquired lock"); + return; + } +#endif + + int err = pthread_mutex_unlock(&getMutex(this)->lock); + PX_ASSERT(!err); + PX_UNUSED(err); +} + +uint32_t MutexImpl::getSize() +{ + return sizeof(MutexUnixImpl); +} + +class ReadWriteLockImpl +{ + public: + Mutex mutex; + volatile int readerCounter; +}; + +ReadWriteLock::ReadWriteLock() +{ + mImpl = reinterpret_cast(PX_ALLOC(sizeof(ReadWriteLockImpl), "ReadWriteLockImpl")); + PX_PLACEMENT_NEW(mImpl, ReadWriteLockImpl); + + mImpl->readerCounter = 0; +} + +ReadWriteLock::~ReadWriteLock() +{ + mImpl->~ReadWriteLockImpl(); + PX_FREE(mImpl); +} + +void ReadWriteLock::lockReader(bool takeLock) +{ + if(takeLock) + mImpl->mutex.lock(); + + atomicIncrement(&mImpl->readerCounter); + + if(takeLock) + mImpl->mutex.unlock(); +} + +void ReadWriteLock::lockWriter() +{ + mImpl->mutex.lock(); + + // spin lock until no readers + while(mImpl->readerCounter); +} + +void ReadWriteLock::unlockReader() +{ + atomicDecrement(&mImpl->readerCounter); +} + +void ReadWriteLock::unlockWriter() +{ + mImpl->mutex.unlock(); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp new file mode 100644 index 000000000..a19eff11e --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp @@ -0,0 +1,52 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsString.h" +#include + +#if PX_ANDROID +#include +#endif + +namespace physx +{ +namespace shdfnd +{ + +void printString(const char* str) +{ +#if PX_ANDROID + __android_log_print(ANDROID_LOG_INFO, "PsPrintString", "%s", str); +#else + puts(str); +#endif +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp new file mode 100644 index 000000000..a3b1e2e68 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp @@ -0,0 +1,156 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsAllocator.h" +#include "PsAtomic.h" +#include "PsSList.h" +#include "PsThread.h" +#include + +#if PX_IOS || PX_EMSCRIPTEN +#define USE_MUTEX +#endif + +namespace physx +{ +namespace shdfnd +{ +namespace +{ +#if defined(USE_MUTEX) +class ScopedMutexLock +{ + pthread_mutex_t& mMutex; + + public: + PX_INLINE ScopedMutexLock(pthread_mutex_t& mutex) : mMutex(mutex) + { + pthread_mutex_lock(&mMutex); + } + + PX_INLINE ~ScopedMutexLock() + { + pthread_mutex_unlock(&mMutex); + } +}; + +typedef ScopedMutexLock ScopedLock; +#else +struct ScopedSpinLock +{ + PX_FORCE_INLINE ScopedSpinLock(volatile int32_t& lock) : mLock(lock) + { + while(__sync_lock_test_and_set(&mLock, 1)) + { + // spinning without atomics is usually + // causing less bus traffic. -> only one + // CPU is modifying the cache line. + while(lock) + PxSpinLockPause(); + } + } + + PX_FORCE_INLINE ~ScopedSpinLock() + { + __sync_lock_release(&mLock); + } + + private: + volatile int32_t& mLock; +}; + +typedef ScopedSpinLock ScopedLock; +#endif + +struct SListDetail +{ + SListEntry* head; +#if defined(USE_MUTEX) + pthread_mutex_t lock; +#else + volatile int32_t lock; +#endif +}; + +template +SListDetail* getDetail(T* impl) +{ + return reinterpret_cast(impl); +} +} + +SListImpl::SListImpl() +{ + getDetail(this)->head = NULL; + +#if defined(USE_MUTEX) + pthread_mutex_init(&getDetail(this)->lock, NULL); +#else + getDetail(this)->lock = 0; // 0 == unlocked +#endif +} + +SListImpl::~SListImpl() +{ +#if defined(USE_MUTEX) + pthread_mutex_destroy(&getDetail(this)->lock); +#endif +} + +void SListImpl::push(SListEntry* entry) +{ + ScopedLock lock(getDetail(this)->lock); + entry->mNext = getDetail(this)->head; + getDetail(this)->head = entry; +} + +SListEntry* SListImpl::pop() +{ + ScopedLock lock(getDetail(this)->lock); + SListEntry* result = getDetail(this)->head; + if(result != NULL) + getDetail(this)->head = result->mNext; + return result; +} + +SListEntry* SListImpl::flush() +{ + ScopedLock lock(getDetail(this)->lock); + SListEntry* result = getDetail(this)->head; + getDetail(this)->head = NULL; + return result; +} + +uint32_t SListImpl::getSize() +{ + return sizeof(SListDetail); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp new file mode 100644 index 000000000..0d273cc9d --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp @@ -0,0 +1,483 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIntrinsics.h" + +#include "PsSocket.h" + +#include +#include +#include +#include +#if !PX_PS4 +#include +#include +#else +#include +#endif +#include +#include +#include + +#define INVALID_SOCKET -1 + +#ifndef SOMAXCONN +#define SOMAXCONN 5 +#endif + +namespace physx +{ +namespace shdfnd +{ + +const uint32_t Socket::DEFAULT_BUFFER_SIZE = 32768; + +class SocketImpl +{ + public: + SocketImpl(bool isBlocking); + virtual ~SocketImpl(); + + bool connect(const char* host, uint16_t port, uint32_t timeout); + bool listen(uint16_t port); + bool accept(bool block); + void disconnect(); + + void setBlocking(bool blocking); + + virtual uint32_t write(const uint8_t* data, uint32_t length); + virtual bool flush(); + uint32_t read(uint8_t* data, uint32_t length); + + PX_FORCE_INLINE bool isBlocking() const + { + return mIsBlocking; + } + PX_FORCE_INLINE bool isConnected() const + { + return mIsConnected; + } + PX_FORCE_INLINE const char* getHost() const + { + return mHost; + } + PX_FORCE_INLINE uint16_t getPort() const + { + return mPort; + } + + protected: + bool nonBlockingTimeout() const; + + int32_t mSocket; + int32_t mListenSocket; + const char* mHost; + uint16_t mPort; + bool mIsConnected; + bool mIsBlocking; + bool mListenMode; +}; + +void socketSetBlockingInternal(int32_t socket, bool blocking); + +SocketImpl::SocketImpl(bool isBlocking) +: mSocket(INVALID_SOCKET) +, mListenSocket(INVALID_SOCKET) +, mHost(NULL) +, mPort(0) +, mIsConnected(false) +, mIsBlocking(isBlocking) +, mListenMode(false) +{ +} + +SocketImpl::~SocketImpl() +{ +} + +bool SocketImpl::connect(const char* host, uint16_t port, uint32_t timeout) +{ + sockaddr_in socketAddress; + intrinsics::memSet(&socketAddress, 0, sizeof(sockaddr_in)); + socketAddress.sin_family = AF_INET; + socketAddress.sin_port = htons(port); + +#if PX_PS4 + socketAddress.sin_addr.s_addr = resolveName(host, timeout); +#else + // get host + hostent* hp = gethostbyname(host); + if(!hp) + { + in_addr a; + a.s_addr = inet_addr(host); + hp = gethostbyaddr(reinterpret_cast(&a), sizeof(in_addr), AF_INET); + if(!hp) + return false; + } + intrinsics::memCopy(&socketAddress.sin_addr, hp->h_addr_list[0], hp->h_length); +#endif + // connect + mSocket = socket(AF_INET, SOCK_STREAM, 0); + if(mSocket == INVALID_SOCKET) + return false; + + socketSetBlockingInternal(mSocket, false); + + int connectRet = ::connect(mSocket, reinterpret_cast(&socketAddress), sizeof(socketAddress)); + if(connectRet < 0) + { + if(errno != EINPROGRESS) + { + disconnect(); + return false; + } + + // Setup select function call to monitor the connect call. + fd_set writefs; + fd_set exceptfs; + FD_ZERO(&writefs); + FD_ZERO(&exceptfs); + FD_SET(mSocket, &writefs); + FD_SET(mSocket, &exceptfs); + timeval timeout_; + timeout_.tv_sec = timeout / 1000; + timeout_.tv_usec = (timeout % 1000) * 1000; + int selret = ::select(mSocket + 1, NULL, &writefs, &exceptfs, &timeout_); + int excepted = FD_ISSET(mSocket, &exceptfs); + int canWrite = FD_ISSET(mSocket, &writefs); + if(selret != 1 || excepted || !canWrite) + { + disconnect(); + return false; + } + + // check if we are really connected, above code seems to return + // true if host is a unix machine even if the connection was + // not accepted. + char buffer; + if(recv(mSocket, &buffer, 0, 0) < 0) + { + if(errno != EWOULDBLOCK) + { + disconnect(); + return false; + } + } + } + + socketSetBlockingInternal(mSocket, mIsBlocking); + +#if PX_APPLE_FAMILY + int noSigPipe = 1; + setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(int)); +#endif + + mIsConnected = true; + mPort = port; + mHost = host; + return true; +} + +bool SocketImpl::listen(uint16_t port) +{ + mListenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if(mListenSocket == INVALID_SOCKET) + return false; + + // enable address reuse: "Address already in use" error message + int yes = 1; + if(setsockopt(mListenSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) + return false; + + mListenMode = true; + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + intrinsics::memSet(addr.sin_zero, '\0', sizeof addr.sin_zero); + + return bind(mListenSocket, reinterpret_cast(&addr), sizeof(addr)) != -1 && + ::listen(mListenSocket, SOMAXCONN) != -1; +} + +bool SocketImpl::accept(bool block) +{ + if(mIsConnected || !mListenMode) + return false; + + // set the listen socket to be non-blocking. + socketSetBlockingInternal(mListenSocket, block); + int32_t clientSocket = ::accept(mListenSocket, 0, 0); + if(clientSocket == INVALID_SOCKET) + return false; + + mSocket = clientSocket; + mIsConnected = true; + socketSetBlockingInternal(mSocket, mIsBlocking); // force the mode to whatever the user set + + return mIsConnected; +} + +void SocketImpl::disconnect() +{ + if(mListenSocket != INVALID_SOCKET) + { + close(mListenSocket); + mListenSocket = INVALID_SOCKET; + } + if(mSocket != INVALID_SOCKET) + { + if(mIsConnected) + { + socketSetBlockingInternal(mSocket, true); + shutdown(mSocket, SHUT_RDWR); + } + close(mSocket); + mSocket = INVALID_SOCKET; + } + mIsConnected = false; + mListenMode = false; + mPort = 0; + mHost = NULL; +} + +bool SocketImpl::nonBlockingTimeout() const +{ + return !mIsBlocking && errno == EWOULDBLOCK; +} + +#if !PX_PS4 +void socketSetBlockingInternal(int32_t socket, bool blocking) +{ + int mode = fcntl(socket, F_GETFL, 0); + if(!blocking) + mode |= O_NONBLOCK; + else + mode &= ~O_NONBLOCK; + fcntl(socket, F_SETFL, mode); +} +#endif + +// should be cross-platform from here down + +void SocketImpl::setBlocking(bool blocking) +{ + if(blocking != mIsBlocking) + { + mIsBlocking = blocking; + if(isConnected()) + socketSetBlockingInternal(mSocket, blocking); + } +} + +bool SocketImpl::flush() +{ + return true; +} + +uint32_t SocketImpl::write(const uint8_t* data, uint32_t length) +{ + if(length == 0) + return 0; + + int sent = send(mSocket, reinterpret_cast(data), int32_t(length), 0); + + if(sent <= 0 && !nonBlockingTimeout()) + disconnect(); + + return uint32_t(sent > 0 ? sent : 0); +} + +uint32_t SocketImpl::read(uint8_t* data, uint32_t length) +{ + if(length == 0) + return 0; + + int32_t received = recv(mSocket, reinterpret_cast(data), int32_t(length), 0); + + if(received <= 0 && !nonBlockingTimeout()) + disconnect(); + + return uint32_t(received > 0 ? received : 0); +} + +class BufferedSocketImpl : public SocketImpl +{ + public: + BufferedSocketImpl(bool isBlocking) : SocketImpl(isBlocking), mBufferPos(0) + { + } + virtual ~BufferedSocketImpl() + { + } + bool flush(); + uint32_t write(const uint8_t* data, uint32_t length); + + private: + uint32_t mBufferPos; + uint8_t mBuffer[Socket::DEFAULT_BUFFER_SIZE]; +}; + +bool BufferedSocketImpl::flush() +{ + uint32_t totalBytesWritten = 0; + + while(totalBytesWritten < mBufferPos && mIsConnected) + totalBytesWritten += int32_t(SocketImpl::write(mBuffer + totalBytesWritten, mBufferPos - totalBytesWritten)); + + bool ret = (totalBytesWritten == mBufferPos); + mBufferPos = 0; + return ret; +} + +uint32_t BufferedSocketImpl::write(const uint8_t* data, uint32_t length) +{ + uint32_t bytesWritten = 0; + while(mBufferPos + length >= Socket::DEFAULT_BUFFER_SIZE) + { + uint32_t currentChunk = Socket::DEFAULT_BUFFER_SIZE - mBufferPos; + intrinsics::memCopy(mBuffer + mBufferPos, data + bytesWritten, currentChunk); + bytesWritten += uint32_t(currentChunk); // for the user, this is consumed even if we fail to shove it down a + // non-blocking socket + + uint32_t sent = SocketImpl::write(mBuffer, Socket::DEFAULT_BUFFER_SIZE); + mBufferPos = Socket::DEFAULT_BUFFER_SIZE - sent; + + if(sent < Socket::DEFAULT_BUFFER_SIZE) // non-blocking or error + { + if(sent) // we can reasonably hope this is rare + intrinsics::memMove(mBuffer, mBuffer + sent, mBufferPos); + + return bytesWritten; + } + length -= currentChunk; + } + + if(length > 0) + { + intrinsics::memCopy(mBuffer + mBufferPos, data + bytesWritten, length); + bytesWritten += length; + mBufferPos += length; + } + + return bytesWritten; +} + +Socket::Socket(bool inIsBuffering, bool isBlocking) +{ + if(inIsBuffering) + { + void* mem = PX_ALLOC(sizeof(BufferedSocketImpl), "BufferedSocketImpl"); + mImpl = PX_PLACEMENT_NEW(mem, BufferedSocketImpl)(isBlocking); + } + else + { + void* mem = PX_ALLOC(sizeof(SocketImpl), "SocketImpl"); + mImpl = PX_PLACEMENT_NEW(mem, SocketImpl)(isBlocking); + } +} + +Socket::~Socket() +{ + mImpl->flush(); + mImpl->disconnect(); + mImpl->~SocketImpl(); + PX_FREE(mImpl); +} + +bool Socket::connect(const char* host, uint16_t port, uint32_t timeout) +{ + return mImpl->connect(host, port, timeout); +} + +bool Socket::listen(uint16_t port) +{ + return mImpl->listen(port); +} + +bool Socket::accept(bool block) +{ + return mImpl->accept(block); +} + +void Socket::disconnect() +{ + mImpl->disconnect(); +} + +bool Socket::isConnected() const +{ + return mImpl->isConnected(); +} + +const char* Socket::getHost() const +{ + return mImpl->getHost(); +} + +uint16_t Socket::getPort() const +{ + return mImpl->getPort(); +} + +bool Socket::flush() +{ + if(!mImpl->isConnected()) + return false; + return mImpl->flush(); +} + +uint32_t Socket::write(const uint8_t* data, uint32_t length) +{ + if(!mImpl->isConnected()) + return 0; + return mImpl->write(data, length); +} + +uint32_t Socket::read(uint8_t* data, uint32_t length) +{ + if(!mImpl->isConnected()) + return 0; + return mImpl->read(data, length); +} + +void Socket::setBlocking(bool blocking) +{ + mImpl->setBlocking(blocking); +} + +bool Socket::isBlocking() const +{ + return mImpl->isBlocking(); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp new file mode 100644 index 000000000..9336fd3e7 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp @@ -0,0 +1,164 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" + +#include "Ps.h" +#include "PsUserAllocated.h" +#include "PsSync.h" + +#include +#include +#include +#include +#include + +namespace physx +{ +namespace shdfnd +{ + +namespace +{ +class _SyncImpl +{ + public: + pthread_mutex_t mutex; + pthread_cond_t cond; + volatile int setCounter; + volatile bool is_set; +}; + +_SyncImpl* getSync(SyncImpl* impl) +{ + return reinterpret_cast<_SyncImpl*>(impl); +} +} + +uint32_t SyncImpl::getSize() +{ + return sizeof(_SyncImpl); +} + +struct PxUnixScopeLock +{ + PxUnixScopeLock(pthread_mutex_t& m) : mMutex(m) + { + pthread_mutex_lock(&mMutex); + } + + ~PxUnixScopeLock() + { + pthread_mutex_unlock(&mMutex); + } + + private: + pthread_mutex_t& mMutex; +}; + +SyncImpl::SyncImpl() +{ + int status = pthread_mutex_init(&getSync(this)->mutex, 0); + PX_ASSERT(!status); + status = pthread_cond_init(&getSync(this)->cond, 0); + PX_ASSERT(!status); + PX_UNUSED(status); + getSync(this)->is_set = false; + getSync(this)->setCounter = 0; +} + +SyncImpl::~SyncImpl() +{ + pthread_cond_destroy(&getSync(this)->cond); + pthread_mutex_destroy(&getSync(this)->mutex); +} + +void SyncImpl::reset() +{ + PxUnixScopeLock lock(getSync(this)->mutex); + getSync(this)->is_set = false; +} + +void SyncImpl::set() +{ + PxUnixScopeLock lock(getSync(this)->mutex); + if(!getSync(this)->is_set) + { + getSync(this)->is_set = true; + getSync(this)->setCounter++; + pthread_cond_broadcast(&getSync(this)->cond); + } +} + +bool SyncImpl::wait(uint32_t ms) +{ + PxUnixScopeLock lock(getSync(this)->mutex); + int lastSetCounter = getSync(this)->setCounter; + if(!getSync(this)->is_set) + { + if(ms == uint32_t(-1)) + { + // have to loop here and check is_set since pthread_cond_wait can return successfully + // even if it was not signaled by pthread_cond_broadcast (OS efficiency design decision) + int status = 0; + while(!status && !getSync(this)->is_set && (lastSetCounter == getSync(this)->setCounter)) + status = pthread_cond_wait(&getSync(this)->cond, &getSync(this)->mutex); + PX_ASSERT((!status && getSync(this)->is_set) || (lastSetCounter != getSync(this)->setCounter)); + } + else + { + timespec ts; + timeval tp; + gettimeofday(&tp, NULL); + uint32_t sec = ms / 1000; + uint32_t usec = (ms - 1000 * sec) * 1000; + + // sschirm: taking into account that us might accumulate to a second + // otherwise the pthread_cond_timedwait complains on osx. + usec = tp.tv_usec + usec; + uint32_t div_sec = usec / 1000000; + uint32_t rem_usec = usec - div_sec * 1000000; + + ts.tv_sec = tp.tv_sec + sec + div_sec; + ts.tv_nsec = rem_usec * 1000; + + // have to loop here and check is_set since pthread_cond_timedwait can return successfully + // even if it was not signaled by pthread_cond_broadcast (OS efficiency design decision) + int status = 0; + while(!status && !getSync(this)->is_set && (lastSetCounter == getSync(this)->setCounter)) + status = pthread_cond_timedwait(&getSync(this)->cond, &getSync(this)->mutex, &ts); + PX_ASSERT((!status && getSync(this)->is_set) || (status == ETIMEDOUT) || + (lastSetCounter != getSync(this)->setCounter)); + } + } + return getSync(this)->is_set || (lastSetCounter != getSync(this)->setCounter); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp new file mode 100644 index 000000000..a868512ad --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp @@ -0,0 +1,504 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" +#include "foundation/PxErrorCallback.h" + +#include "Ps.h" +#include "PsFoundation.h" +#include "PsAtomic.h" +#include "PsThread.h" + +#include +#if !PX_APPLE_FAMILY && !defined(ANDROID) && !defined(__CYGWIN__) && !PX_PS4 && !PX_EMSCRIPTEN +#include // PTHREAD_STACK_MIN +#endif +#include +#include +#include +#if !PX_PS4 +#include +#if !PX_APPLE_FAMILY && !PX_EMSCRIPTEN +#include +#include +#endif +#endif + +#if PX_APPLE_FAMILY +#include +#include +#include +#include +#endif + +// fwd +#if defined(ANDROID) +extern "C" { +int android_getCpuCount(void); +} +#endif + +#define PxSpinLockPause() asm("nop") + +namespace physx +{ +namespace shdfnd +{ + +#if PX_PS4 + uint64_t setAffinityMaskPS4(pthread_t, uint32_t); + int32_t setNamePS4(pthread_t, const char*); + int32_t pthreadCreatePS4(pthread_t *, const pthread_attr_t *, void *(*) (void *), void *, const char*); +#endif + +namespace +{ + +typedef enum +{ + _PxThreadNotStarted, + _PxThreadStarted, + _PxThreadStopped +} PxThreadState; + +class _ThreadImpl +{ + public: + ThreadImpl::ExecuteFn fn; + void* arg; + volatile int32_t quitNow; + volatile int32_t threadStarted; + volatile int32_t state; + + pthread_t thread; + pid_t tid; + + uint32_t affinityMask; + const char* name; +}; + +_ThreadImpl* getThread(ThreadImpl* impl) +{ + return reinterpret_cast<_ThreadImpl*>(impl); +} + +static void setTid(_ThreadImpl& threadImpl) +{ +// query TID +#if PX_PS4 || (defined (TARGET_OS_TV) && TARGET_OS_TV) +// AM: TODO: neither of the below are implemented +#elif PX_APPLE_FAMILY + threadImpl.tid = syscall(SYS_gettid); +#elif PX_EMSCRIPTEN + threadImpl.tid = pthread_self(); +#else + threadImpl.tid = syscall(__NR_gettid); +#endif + + // notify/unblock parent thread + atomicCompareExchange(&(threadImpl.threadStarted), 1, 0); +} + +void* PxThreadStart(void* arg) +{ + _ThreadImpl* impl = getThread(reinterpret_cast(arg)); + impl->state = _PxThreadStarted; + + // run setTid in thread's context + setTid(*impl); + + // then run either the passed in function or execute from the derived class (Runnable). + if(impl->fn) + (*impl->fn)(impl->arg); + else if(impl->arg) + (reinterpret_cast(impl->arg))->execute(); + return 0; +} +} + +uint32_t ThreadImpl::getSize() +{ + return sizeof(_ThreadImpl); +} + +ThreadImpl::Id ThreadImpl::getId() +{ + return Id(pthread_self()); +} + +ThreadImpl::ThreadImpl() +{ + getThread(this)->thread = 0; + getThread(this)->tid = 0; + getThread(this)->state = _PxThreadNotStarted; + getThread(this)->quitNow = 0; + getThread(this)->threadStarted = 0; + getThread(this)->fn = NULL; + getThread(this)->arg = NULL; + getThread(this)->affinityMask = 0; + getThread(this)->name = "set my name before starting me"; +} + +ThreadImpl::ThreadImpl(ThreadImpl::ExecuteFn fn, void* arg, const char* name) +{ + getThread(this)->thread = 0; + getThread(this)->tid = 0; + getThread(this)->state = _PxThreadNotStarted; + getThread(this)->quitNow = 0; + getThread(this)->threadStarted = 0; + getThread(this)->fn = fn; + getThread(this)->arg = arg; + getThread(this)->affinityMask = 0; + getThread(this)->name = name; + + start(0, NULL); +} + +ThreadImpl::~ThreadImpl() +{ + if(getThread(this)->state == _PxThreadStarted) + kill(); +} + +void ThreadImpl::start(uint32_t stackSize, Runnable* runnable) +{ + if(getThread(this)->state != _PxThreadNotStarted) + return; + + if(stackSize == 0) + stackSize = getDefaultStackSize(); + +#if defined(PTHREAD_STACK_MIN) && !defined(ANDROID) + if(stackSize < PTHREAD_STACK_MIN) + { + shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "ThreadImpl::start(): stack size was set below PTHREAD_STACK_MIN"); + stackSize = PTHREAD_STACK_MIN; + } +#endif + + if(runnable && !getThread(this)->arg && !getThread(this)->fn) + getThread(this)->arg = runnable; + + pthread_attr_t attr; + int status = pthread_attr_init(&attr); + PX_ASSERT(!status); + PX_UNUSED(status); + + status = pthread_attr_setstacksize(&attr, stackSize); + PX_ASSERT(!status); +#if PX_PS4 + status = pthreadCreatePS4(&getThread(this)->thread, &attr, PxThreadStart, this, getThread(this)->name); +#else + status = pthread_create(&getThread(this)->thread, &attr, PxThreadStart, this); +#endif + PX_ASSERT(!status); + + // wait for thread to startup and write out TID + // otherwise TID dependent calls like setAffinity will fail. + while(atomicCompareExchange(&(getThread(this)->threadStarted), 1, 1) == 0) + yield(); + + // here we are sure that getThread(this)->state >= _PxThreadStarted + + status = pthread_attr_destroy(&attr); + PX_ASSERT(!status); + + // apply stored affinity mask + if(getThread(this)->affinityMask) + setAffinityMask(getThread(this)->affinityMask); + +#if !PX_PS4 + if (getThread(this)->name) + setName(getThread(this)->name); +#endif +} + +void ThreadImpl::signalQuit() +{ + atomicIncrement(&(getThread(this)->quitNow)); +} + +bool ThreadImpl::waitForQuit() +{ + if(getThread(this)->state == _PxThreadNotStarted) + return false; + + // works also with a stopped/exited thread if the handle is still valid + pthread_join(getThread(this)->thread, NULL); + return true; +} + +bool ThreadImpl::quitIsSignalled() +{ + return atomicCompareExchange(&(getThread(this)->quitNow), 0, 0) != 0; +} + +#if defined(PX_GCC_FAMILY) +__attribute__((noreturn)) +#endif + void ThreadImpl::quit() +{ + getThread(this)->state = _PxThreadStopped; + pthread_exit(0); +} + +void ThreadImpl::kill() +{ +#ifndef ANDROID + if(getThread(this)->state == _PxThreadStarted) + pthread_cancel(getThread(this)->thread); + getThread(this)->state = _PxThreadStopped; +#else + shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "ThreadImpl::kill() called, but is not implemented"); +#endif +} + +void ThreadImpl::sleep(uint32_t ms) +{ + timespec sleepTime; + uint32_t remainder = ms % 1000; + sleepTime.tv_sec = ms - remainder; + sleepTime.tv_nsec = remainder * 1000000L; + + while(nanosleep(&sleepTime, &sleepTime) == -1) + continue; +} + +void ThreadImpl::yield() +{ + sched_yield(); +} + +uint32_t ThreadImpl::setAffinityMask(uint32_t mask) +{ + // Same as windows impl if mask is zero + if(!mask) + return 0; + + getThread(this)->affinityMask = mask; + + uint64_t prevMask = 0; + + if(getThread(this)->state == _PxThreadStarted) + { +#if PX_PS4 + prevMask = setAffinityMaskPS4(getThread(this)->thread, mask); +#elif PX_EMSCRIPTEN + // not supported +#elif !PX_APPLE_FAMILY // Apple doesn't support syscall with getaffinity and setaffinity + int32_t errGet = syscall(__NR_sched_getaffinity, getThread(this)->tid, sizeof(prevMask), &prevMask); + if(errGet < 0) + return 0; + + int32_t errSet = syscall(__NR_sched_setaffinity, getThread(this)->tid, sizeof(mask), &mask); + if(errSet != 0) + return 0; +#endif + } + + return uint32_t(prevMask); +} + +void ThreadImpl::setName(const char* name) +{ + getThread(this)->name = name; + + if (getThread(this)->state == _PxThreadStarted) + { +#if(defined(ANDROID) && (__ANDROID_API__ > 8)) + pthread_setname_np(getThread(this)->thread, name); +#elif PX_PS4 + setNamePS4(getThread(this)->thread, name); +#else + // not implemented because most unix APIs expect setName() + // to be called from the thread's context. Example see next comment: + + // this works only with the current thread and can rename + // the main process if used in the wrong context: + // prctl(PR_SET_NAME, reinterpret_cast(name) ,0,0,0); + PX_UNUSED(name); +#endif + } +} + +#if !PX_APPLE_FAMILY +static ThreadPriority::Enum convertPriorityFromLinux(uint32_t inPrio, int policy) +{ + PX_COMPILE_TIME_ASSERT(ThreadPriority::eLOW > ThreadPriority::eHIGH); + PX_COMPILE_TIME_ASSERT(ThreadPriority::eHIGH == 0); + + int maxL = sched_get_priority_max(policy); + int minL = sched_get_priority_min(policy); + int rangeL = maxL - minL; + int rangeNv = ThreadPriority::eLOW - ThreadPriority::eHIGH; + + // case for default scheduler policy + if(rangeL == 0) + return ThreadPriority::eNORMAL; + + float floatPrio = (float(maxL - inPrio) * float(rangeNv)) / float(rangeL); + + return ThreadPriority::Enum(int(roundf(floatPrio))); +} + +static int convertPriorityToLinux(ThreadPriority::Enum inPrio, int policy) +{ + int maxL = sched_get_priority_max(policy); + int minL = sched_get_priority_min(policy); + int rangeL = maxL - minL; + int rangeNv = ThreadPriority::eLOW - ThreadPriority::eHIGH; + + // case for default scheduler policy + if(rangeL == 0) + return 0; + + float floatPrio = (float(ThreadPriority::eLOW - inPrio) * float(rangeL)) / float(rangeNv); + + return minL + int(roundf(floatPrio)); +} +#endif + +void ThreadImpl::setPriority(ThreadPriority::Enum val) +{ + PX_UNUSED(val); +#if !PX_APPLE_FAMILY + int policy; + sched_param s_param; + pthread_getschedparam(getThread(this)->thread, &policy, &s_param); + s_param.sched_priority = convertPriorityToLinux(val, policy); + pthread_setschedparam(getThread(this)->thread, policy, &s_param); +#endif +} + +ThreadPriority::Enum ThreadImpl::getPriority(Id pthread) +{ + PX_UNUSED(pthread); +#if !PX_APPLE_FAMILY + int policy; + sched_param s_param; + int ret = pthread_getschedparam(pthread_t(pthread), &policy, &s_param); + if(ret == 0) + return convertPriorityFromLinux(s_param.sched_priority, policy); + else + return ThreadPriority::eNORMAL; +#else + return ThreadPriority::eNORMAL; +#endif +} + +uint32_t ThreadImpl::getNbPhysicalCores() +{ +#if PX_APPLE_FAMILY + int count; + size_t size = sizeof(count); + return sysctlbyname("hw.physicalcpu", &count, &size, NULL, 0) ? 0 : count; +#elif defined(ANDROID) + return android_getCpuCount(); +#else + // Linux exposes CPU topology using /sys/devices/system/cpu + // https://www.kernel.org/doc/Documentation/cputopology.txt + if(FILE* f = fopen("/sys/devices/system/cpu/possible", "r")) + { + int minIndex, maxIndex; + int n = fscanf(f, "%d-%d", &minIndex, &maxIndex); + fclose(f); + + if(n == 2) + return (maxIndex - minIndex) + 1; + else if(n == 1) + return minIndex + 1; + } + +#if PX_PS4 + // Reducing to 6 to take into account that the OS appears to use 2 cores at peak currently. + return 6; +#else + // For non-Linux kernels this fallback is possibly the best we can do + // but will report logical (hyper-threaded) counts + int n = sysconf(_SC_NPROCESSORS_CONF); + if(n < 0) + return 0; + else + return n; +#endif +#endif +} + +uint32_t TlsAlloc() +{ + pthread_key_t key; + int status = pthread_key_create(&key, NULL); + PX_ASSERT(!status); + PX_UNUSED(status); + return uint32_t(key); +} + +void TlsFree(uint32_t index) +{ + int status = pthread_key_delete(pthread_key_t(index)); + PX_ASSERT(!status); + PX_UNUSED(status); +} + +void* TlsGet(uint32_t index) +{ + return reinterpret_cast(pthread_getspecific(pthread_key_t(index))); +} + +size_t TlsGetValue(uint32_t index) +{ + return reinterpret_cast(pthread_getspecific(pthread_key_t(index))); +} + +uint32_t TlsSet(uint32_t index, void* value) +{ + int status = pthread_setspecific(pthread_key_t(index), value); + PX_ASSERT(!status); + return !status; +} + +uint32_t TlsSetValue(uint32_t index, size_t value) +{ + int status = pthread_setspecific(pthread_key_t(index), reinterpret_cast(value)); + PX_ASSERT(!status); + return !status; +} + +// DM: On Linux x86-32, without implementation-specific restrictions +// the default stack size for a new thread should be 2 megabytes (kernel.org). +// NOTE: take care of this value on other architectures! +uint32_t ThreadImpl::getDefaultStackSize() +{ + return 1 << 21; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp new file mode 100644 index 000000000..e1fc32c1d --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "Ps.h" +#include "PsTime.h" + +#include +#include + +#if PX_APPLE_FAMILY +#include +#endif + +// Use real-time high-precision timer. +#if !PX_APPLE_FAMILY +#define CLOCKID CLOCK_REALTIME +#endif + +namespace physx +{ +namespace shdfnd +{ + +static const CounterFrequencyToTensOfNanos gCounterFreq = Time::getCounterFrequency(); + +const CounterFrequencyToTensOfNanos& Time::getBootCounterFrequency() +{ + return gCounterFreq; +} + +static Time::Second getTimeSeconds() +{ + static struct timeval _tv; + gettimeofday(&_tv, NULL); + return double(_tv.tv_sec) + double(_tv.tv_usec) * 0.000001; +} + +Time::Time() +{ + mLastTime = getTimeSeconds(); +} + +Time::Second Time::getElapsedSeconds() +{ + Time::Second curTime = getTimeSeconds(); + Time::Second diff = curTime - mLastTime; + mLastTime = curTime; + return diff; +} + +Time::Second Time::peekElapsedSeconds() +{ + Time::Second curTime = getTimeSeconds(); + Time::Second diff = curTime - mLastTime; + return diff; +} + +Time::Second Time::getLastTime() const +{ + return mLastTime; +} + +#if PX_APPLE_FAMILY +CounterFrequencyToTensOfNanos Time::getCounterFrequency() +{ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + // mach_absolute_time * (info.numer/info.denom) is in units of nano seconds + return CounterFrequencyToTensOfNanos(info.numer, info.denom * 10); +} + +uint64_t Time::getCurrentCounterValue() +{ + return mach_absolute_time(); +} + +#else + +CounterFrequencyToTensOfNanos Time::getCounterFrequency() +{ + return CounterFrequencyToTensOfNanos(1, 10); +} + +uint64_t Time::getCurrentCounterValue() +{ + struct timespec mCurrTimeInt; + clock_gettime(CLOCKID, &mCurrTimeInt); + // Convert to nanos as this doesn't cause a large divide here + return (static_cast(mCurrTimeInt.tv_sec) * 1000000000) + (static_cast(mCurrTimeInt.tv_nsec)); +} +#endif + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp new file mode 100644 index 000000000..d8ebca2da --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp @@ -0,0 +1,96 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "windows/PsWindowsInclude.h" +#include "PsAtomic.h" + +namespace physx +{ +namespace shdfnd +{ + +int32_t atomicExchange(volatile int32_t* val, int32_t val2) +{ + return (int32_t)InterlockedExchange((volatile LONG*)val, (LONG)val2); +} + +int32_t atomicCompareExchange(volatile int32_t* dest, int32_t exch, int32_t comp) +{ + return (int32_t)InterlockedCompareExchange((volatile LONG*)dest, exch, comp); +} + +void* atomicCompareExchangePointer(volatile void** dest, void* exch, void* comp) +{ + return InterlockedCompareExchangePointer((volatile PVOID*)dest, exch, comp); +} + +int32_t atomicIncrement(volatile int32_t* val) +{ + return (int32_t)InterlockedIncrement((volatile LONG*)val); +} + +int32_t atomicDecrement(volatile int32_t* val) +{ + return (int32_t)InterlockedDecrement((volatile LONG*)val); +} + +int32_t atomicAdd(volatile int32_t* val, int32_t delta) +{ + LONG newValue, oldValue; + do + { + oldValue = *val; + newValue = oldValue + delta; + } while(InterlockedCompareExchange((volatile LONG*)val, newValue, oldValue) != oldValue); + + return newValue; +} + +int32_t atomicMax(volatile int32_t* val, int32_t val2) +{ + // Could do this more efficiently in asm... + + LONG newValue, oldValue; + + do + { + oldValue = *val; + + if(val2 > oldValue) + newValue = val2; + else + newValue = oldValue; + + } while(InterlockedCompareExchange((volatile LONG*)val, newValue, oldValue) != oldValue); + + return newValue; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp new file mode 100644 index 000000000..75c308333 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsCpu.h" +#pragma warning(push) +//'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#pragma warning(disable : 4668) +#if PX_VC == 10 +#pragma warning(disable : 4987) // nonstandard extension used: 'throw (...)' +#endif +#include +#pragma warning(pop) + +namespace physx +{ +namespace shdfnd +{ + +#if PX_ARM +#define cpuid(reg) reg[0] = reg[1] = reg[2] = reg[3] = 0; + +uint8_t Cpu::getCpuId() +{ + uint32_t cpuInfo[4]; + cpuid(cpuInfo); + return static_cast(cpuInfo[1] >> 24); // APIC Physical ID +} +#else +uint8_t Cpu::getCpuId() +{ + int CPUInfo[4]; + int InfoType = 1; + __cpuid(CPUInfo, InfoType); + return static_cast(CPUInfo[1] >> 24); // APIC Physical ID +} +#endif +} +} diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp new file mode 100644 index 000000000..a8575a062 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp @@ -0,0 +1,88 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#include "PsFPU.h" +#include "float.h" +#include "PsIntrinsics.h" + +#if PX_X64 || PX_ARM +#define _MCW_ALL _MCW_DN | _MCW_EM | _MCW_RC +#else +#define _MCW_ALL _MCW_DN | _MCW_EM | _MCW_IC | _MCW_RC | _MCW_PC +#endif + +physx::shdfnd::FPUGuard::FPUGuard() +{ +// default plus FTZ and DAZ +#if PX_X64 || PX_ARM + // query current control word state + _controlfp_s(mControlWords, 0, 0); + + // set both x87 and sse units to default + DAZ + unsigned int cw; + _controlfp_s(&cw, _CW_DEFAULT | _DN_FLUSH, _MCW_ALL); +#else + // query current control word state + __control87_2(0, 0, mControlWords, mControlWords + 1); + + // set both x87 and sse units to default + DAZ + unsigned int x87, sse; + __control87_2(_CW_DEFAULT | _DN_FLUSH, _MCW_ALL, &x87, &sse); +#endif +} + +physx::shdfnd::FPUGuard::~FPUGuard() +{ + _clearfp(); + +#if PX_X64 || PX_ARM + // reset FP state + unsigned int cw; + _controlfp_s(&cw, *mControlWords, _MCW_ALL); +#else + + // reset FP state + unsigned int x87, sse; + __control87_2(mControlWords[0], _MCW_ALL, &x87, 0); + __control87_2(mControlWords[1], _MCW_ALL, 0, &sse); +#endif +} + +void physx::shdfnd::enableFPExceptions() +{ + // clear any pending exceptions + _clearfp(); + + // enable all fp exceptions except inexact and underflow (common, benign) + _controlfp_s(NULL, uint32_t(~_MCW_EM) | _EM_INEXACT | _EM_UNDERFLOW, _MCW_EM); +} + +void physx::shdfnd::disableFPExceptions() +{ + _controlfp_s(NULL, _MCW_EM, _MCW_EM); +} diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp new file mode 100644 index 000000000..45cbeafea --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp @@ -0,0 +1,162 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "windows/PsWindowsInclude.h" +#include "PsFoundation.h" +#include "PsUserAllocated.h" +#include "PsMutex.h" +#include "PsThread.h" +#include "foundation/PxErrorCallback.h" + +namespace physx +{ +namespace shdfnd +{ + +namespace +{ +struct MutexWinImpl +{ + CRITICAL_SECTION mLock; + Thread::Id mOwner; +}; + +MutexWinImpl* getMutex(MutexImpl* impl) +{ + return reinterpret_cast(impl); +} +} + +MutexImpl::MutexImpl() +{ + InitializeCriticalSection(&getMutex(this)->mLock); + getMutex(this)->mOwner = 0; +} + +MutexImpl::~MutexImpl() +{ + DeleteCriticalSection(&getMutex(this)->mLock); +} + +void MutexImpl::lock() +{ + EnterCriticalSection(&getMutex(this)->mLock); + +#if PX_DEBUG + getMutex(this)->mOwner = Thread::getId(); +#endif +} + +bool MutexImpl::trylock() +{ + bool success = TryEnterCriticalSection(&getMutex(this)->mLock) != 0; +#if PX_DEBUG + if(success) + getMutex(this)->mOwner = Thread::getId(); +#endif + return success; +} + +void MutexImpl::unlock() +{ +#if PX_DEBUG + // ensure we are already holding the lock + if(getMutex(this)->mOwner != Thread::getId()) + { + shdfnd::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Mutex must be unlocked only by thread that has already acquired lock"); + return; + } + +#endif + + LeaveCriticalSection(&getMutex(this)->mLock); +} + +uint32_t MutexImpl::getSize() +{ + return sizeof(MutexWinImpl); +} + +class ReadWriteLockImpl +{ + PX_NOCOPY(ReadWriteLockImpl) + public: + ReadWriteLockImpl() + { + } + Mutex mutex; + volatile LONG readerCount; // handle recursive writer locking +}; + +ReadWriteLock::ReadWriteLock() +{ + mImpl = reinterpret_cast(PX_ALLOC(sizeof(ReadWriteLockImpl), "ReadWriteLockImpl")); + PX_PLACEMENT_NEW(mImpl, ReadWriteLockImpl); + + mImpl->readerCount = 0; +} + +ReadWriteLock::~ReadWriteLock() +{ + mImpl->~ReadWriteLockImpl(); + PX_FREE(mImpl); +} + +void ReadWriteLock::lockReader(bool takeLock) +{ + if(takeLock) + mImpl->mutex.lock(); + + InterlockedIncrement(&mImpl->readerCount); + + if(takeLock) + mImpl->mutex.unlock(); +} + +void ReadWriteLock::lockWriter() +{ + mImpl->mutex.lock(); + + // spin lock until no readers + while(mImpl->readerCount); +} + +void ReadWriteLock::unlockReader() +{ + InterlockedDecrement(&mImpl->readerCount); +} + +void ReadWriteLock::unlockWriter() +{ + mImpl->mutex.unlock(); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp new file mode 100644 index 000000000..50a83eb46 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsString.h" +#include +#pragma warning(push) +#pragma warning(disable : 4668) //'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#include +#pragma warning(pop) + +#include +#include +#include + +namespace physx +{ +namespace shdfnd +{ + +void printString(const char* str) +{ + puts(str); // do not use printf here, since str can contain multiple % signs that will not be printed + OutputDebugStringA(str); + OutputDebugStringA("\n"); +} +} + +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp new file mode 100644 index 000000000..76647cc9c --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "windows/PsWindowsInclude.h" +#include "PsAllocator.h" +#include "PsSList.h" + +namespace physx +{ +namespace shdfnd +{ +namespace +{ +template +SLIST_HEADER* getDetail(T* impl) +{ + return reinterpret_cast(impl); +} +} + +SListImpl::SListImpl() +{ + InitializeSListHead(getDetail(this)); +} + +SListImpl::~SListImpl() +{ +} + +void SListImpl::push(SListEntry* entry) +{ + InterlockedPushEntrySList(getDetail(this), reinterpret_cast(entry)); +} + +SListEntry* SListImpl::pop() +{ + return reinterpret_cast(InterlockedPopEntrySList(getDetail(this))); +} + +SListEntry* SListImpl::flush() +{ + return reinterpret_cast(InterlockedFlushSList(getDetail(this))); +} + +uint32_t SListImpl::getSize() +{ + return sizeof(SLIST_HEADER); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp new file mode 100644 index 000000000..b50afa8e0 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp @@ -0,0 +1,450 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIntrinsics.h" + +#include "windows/PsWindowsInclude.h" +#include "PsSocket.h" +#include "PsThread.h" +#include "PsArray.h" + +#include +#pragma comment(lib, "Ws2_32") + +namespace physx +{ +namespace shdfnd +{ + +const uint32_t Socket::DEFAULT_BUFFER_SIZE = 32768; + +class SocketImpl +{ + public: + SocketImpl(bool isBlocking); + virtual ~SocketImpl(); + + bool connect(const char* host, uint16_t port, uint32_t timeout); + bool listen(uint16_t port); + bool accept(bool block); + void disconnect(); + + void setBlocking(bool blocking); + + virtual uint32_t write(const uint8_t* data, uint32_t length); + virtual bool flush(); + uint32_t read(uint8_t* data, uint32_t length); + + PX_FORCE_INLINE bool isBlocking() const + { + return mIsBlocking; + } + PX_FORCE_INLINE bool isConnected() const + { + return mIsConnected; + } + PX_FORCE_INLINE const char* getHost() const + { + return mHost; + } + PX_FORCE_INLINE uint16_t getPort() const + { + return mPort; + } + + protected: + bool nonBlockingTimeout() const; + void setBlockingInternal(SOCKET socket, bool blocking); + + mutable SOCKET mSocket; + SOCKET mListenSocket; + const char* mHost; + uint16_t mPort; + mutable bool mIsConnected; + bool mIsBlocking; + bool mListenMode; + bool mSocketLayerIntialized; +}; + +SocketImpl::SocketImpl(bool isBlocking) +: mSocket(INVALID_SOCKET) +, mListenSocket(INVALID_SOCKET) +, mPort(0) +, mHost(NULL) +, mIsConnected(false) +, mIsBlocking(isBlocking) +, mListenMode(false) +, mSocketLayerIntialized(false) +{ + WORD vreq; + WSADATA wsaData; + vreq = MAKEWORD(2, 2); + mSocketLayerIntialized = (WSAStartup(vreq, &wsaData) == 0); +} + +SocketImpl::~SocketImpl() +{ + if(mSocketLayerIntialized) + WSACleanup(); +} + +void SocketImpl::setBlockingInternal(SOCKET socket, bool blocking) +{ + uint32_t mode = uint32_t(blocking ? 0 : 1); + ioctlsocket(socket, FIONBIO, (u_long*)&mode); +} + +#ifdef PX_VC11 +#pragma warning(push) +#pragma warning(disable : 4548) // for FD_SET on vc11 only +#endif +bool SocketImpl::connect(const char* host, uint16_t port, uint32_t timeout) +{ + if(!mSocketLayerIntialized) + return false; + + sockaddr_in socketAddress; + hostent* hp; + + intrinsics::memSet(&socketAddress, 0, sizeof(sockaddr_in)); + socketAddress.sin_family = AF_INET; + socketAddress.sin_port = htons(port); + + // get host + hp = gethostbyname(host); + if(!hp) + { + in_addr a; + a.s_addr = inet_addr(host); + hp = gethostbyaddr((const char*)&a, sizeof(in_addr), AF_INET); + if(!hp) + return false; + } + intrinsics::memCopy(&socketAddress.sin_addr, hp->h_addr_list[0], (uint32_t)hp->h_length); + + // connect + mSocket = socket(PF_INET, SOCK_STREAM, 0); + if(mSocket == INVALID_SOCKET) + return false; + + setBlockingInternal(mSocket, false); + + ::connect(mSocket, (sockaddr*)&socketAddress, sizeof(socketAddress)); + // Setup select function call to monitor the connect call. + fd_set writefs; + fd_set exceptfs; + FD_ZERO(&writefs); + FD_ZERO(&exceptfs); +#pragma warning(push) +#pragma warning(disable : 4127 4548) + FD_SET(mSocket, &writefs); + FD_SET(mSocket, &exceptfs); +#pragma warning(pop) + timeval timeout_; + timeout_.tv_sec = long(timeout / 1000); + timeout_.tv_usec = long(((timeout % 1000) * 1000)); + int selret = ::select(1, NULL, &writefs, &exceptfs, &timeout_); + int excepted = FD_ISSET(mSocket, &exceptfs); + int canWrite = FD_ISSET(mSocket, &writefs); + if(selret != 1 || excepted || !canWrite) + { + disconnect(); + return false; + } + + setBlockingInternal(mSocket, mIsBlocking); + + mIsConnected = true; + mPort = port; + mHost = host; + return true; +} +#ifdef PX_VC11 +#pragma warning(pop) +#endif + +bool SocketImpl::listen(uint16_t port) +{ + if(!mSocketLayerIntialized) + return false; + + mListenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if(mListenSocket == INVALID_SOCKET) + return false; + + mListenMode = true; + + sockaddr_in addr = { 0 }; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + return bind(mListenSocket, (sockaddr*)&addr, sizeof(addr)) == 0 && ::listen(mListenSocket, SOMAXCONN) == 0; +} + +bool SocketImpl::accept(bool block) +{ + if(mIsConnected || !mListenMode) + return false; + + // set the listen socket to be non-blocking. + setBlockingInternal(mListenSocket, block); + SOCKET clientSocket = ::accept(mListenSocket, 0, 0); + if(clientSocket == INVALID_SOCKET) + return false; + + mSocket = clientSocket; + mIsConnected = true; + setBlockingInternal(mSocket, mIsBlocking); // force the mode to whatever the user set + + return mIsConnected; +} + +void SocketImpl::disconnect() +{ + if(mListenSocket != INVALID_SOCKET) + { + closesocket(mListenSocket); + mListenSocket = INVALID_SOCKET; + } + if(mSocket != INVALID_SOCKET) + { +#if PX_UWP + shutdown(mSocket, SD_SEND); +#else + WSASendDisconnect(mSocket, NULL); +#endif + closesocket(mSocket); + mSocket = INVALID_SOCKET; + } + mIsConnected = false; + mListenMode = false; + mPort = 0; + mHost = NULL; +} + +bool SocketImpl::nonBlockingTimeout() const +{ + return !mIsBlocking && WSAGetLastError() == WSAEWOULDBLOCK; +} + +// should be cross-platform from here down + +void SocketImpl::setBlocking(bool blocking) +{ + if(blocking != mIsBlocking) + { + mIsBlocking = blocking; + if(isConnected()) + setBlockingInternal(mSocket, blocking); + } +} + +bool SocketImpl::flush() +{ + return true; +} + +uint32_t SocketImpl::write(const uint8_t* data, uint32_t length) +{ + if(length == 0) + return 0; + + int sent = send(mSocket, (const char*)data, (int32_t)length, 0); + + if(sent <= 0 && !nonBlockingTimeout()) + disconnect(); + + return uint32_t(sent > 0 ? sent : 0); +} + +uint32_t SocketImpl::read(uint8_t* data, uint32_t length) +{ + if(length == 0) + return 0; + + int32_t received = recv(mSocket, (char*)data, (int32_t)length, 0); + + if(received <= 0 && !nonBlockingTimeout()) + disconnect(); + + return uint32_t(received > 0 ? received : 0); +} + +class BufferedSocketImpl : public SocketImpl +{ + public: + BufferedSocketImpl(bool isBlocking) : SocketImpl(isBlocking), mBufferPos(0) + { + } + virtual ~BufferedSocketImpl() + { + } + bool flush(); + uint32_t write(const uint8_t* data, uint32_t length); + + private: + uint32_t mBufferPos; + uint8_t mBuffer[Socket::DEFAULT_BUFFER_SIZE]; +}; + +bool BufferedSocketImpl::flush() +{ + uint32_t totalBytesWritten = 0; + + while(totalBytesWritten < mBufferPos && mIsConnected) + totalBytesWritten += (int32_t)SocketImpl::write(mBuffer + totalBytesWritten, mBufferPos - totalBytesWritten); + + bool ret = (totalBytesWritten == mBufferPos); + mBufferPos = 0; + return ret; +} + +uint32_t BufferedSocketImpl::write(const uint8_t* data, uint32_t length) +{ + uint32_t bytesWritten = 0; + while(mBufferPos + length >= Socket::DEFAULT_BUFFER_SIZE) + { + uint32_t currentChunk = Socket::DEFAULT_BUFFER_SIZE - mBufferPos; + intrinsics::memCopy(mBuffer + mBufferPos, data + bytesWritten, currentChunk); + bytesWritten += (uint32_t)currentChunk; // for the user, this is consumed even if we fail to shove it down a + // non-blocking socket + + uint32_t sent = SocketImpl::write(mBuffer, Socket::DEFAULT_BUFFER_SIZE); + mBufferPos = Socket::DEFAULT_BUFFER_SIZE - sent; + + if(sent < Socket::DEFAULT_BUFFER_SIZE) // non-blocking or error + { + if(sent) // we can reasonably hope this is rare + intrinsics::memMove(mBuffer, mBuffer + sent, mBufferPos); + + return bytesWritten; + } + length -= currentChunk; + } + + if(length > 0) + { + intrinsics::memCopy(mBuffer + mBufferPos, data + bytesWritten, length); + bytesWritten += length; + mBufferPos += length; + } + + return bytesWritten; +} + +Socket::Socket(bool inIsBuffering, bool isBlocking) +{ + if(inIsBuffering) + { + void* mem = PX_ALLOC(sizeof(BufferedSocketImpl), "BufferedSocketImpl"); + mImpl = PX_PLACEMENT_NEW(mem, BufferedSocketImpl)(isBlocking); + } + else + { + void* mem = PX_ALLOC(sizeof(SocketImpl), "SocketImpl"); + mImpl = PX_PLACEMENT_NEW(mem, SocketImpl)(isBlocking); + } +} + +Socket::~Socket() +{ + mImpl->flush(); + mImpl->disconnect(); + mImpl->~SocketImpl(); + PX_FREE(mImpl); +} + +bool Socket::connect(const char* host, uint16_t port, uint32_t timeout) +{ + return mImpl->connect(host, port, timeout); +} + +bool Socket::listen(uint16_t port) +{ + return mImpl->listen(port); +} + +bool Socket::accept(bool block) +{ + return mImpl->accept(block); +} + +void Socket::disconnect() +{ + mImpl->disconnect(); +} + +bool Socket::isConnected() const +{ + return mImpl->isConnected(); +} + +const char* Socket::getHost() const +{ + return mImpl->getHost(); +} + +uint16_t Socket::getPort() const +{ + return mImpl->getPort(); +} + +bool Socket::flush() +{ + if(!mImpl->isConnected()) + return false; + return mImpl->flush(); +} + +uint32_t Socket::write(const uint8_t* data, uint32_t length) +{ + if(!mImpl->isConnected()) + return 0; + return mImpl->write(data, length); +} + +uint32_t Socket::read(uint8_t* data, uint32_t length) +{ + if(!mImpl->isConnected()) + return 0; + return mImpl->read(data, length); +} + +void Socket::setBlocking(bool blocking) +{ + mImpl->setBlocking(blocking); +} + +bool Socket::isBlocking() const +{ + return mImpl->isBlocking(); +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp new file mode 100644 index 000000000..b3675dc7a --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp @@ -0,0 +1,81 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "windows/PsWindowsInclude.h" +#include "PsUserAllocated.h" +#include "PsSync.h" + +namespace physx +{ +namespace shdfnd +{ + +namespace +{ +HANDLE& getSync(SyncImpl* impl) +{ + return *reinterpret_cast(impl); +} +} + +uint32_t SyncImpl::getSize() +{ + return sizeof(HANDLE); +} + +SyncImpl::SyncImpl() +{ + getSync(this) = CreateEvent(0, true, false, 0); +} + +SyncImpl::~SyncImpl() +{ + CloseHandle(getSync(this)); +} + +void SyncImpl::reset() +{ + ResetEvent(getSync(this)); +} + +void SyncImpl::set() +{ + SetEvent(getSync(this)); +} + +bool SyncImpl::wait(uint32_t milliseconds) +{ + if(milliseconds == -1) + milliseconds = INFINITE; + + return WaitForSingleObject(getSync(this), milliseconds) == WAIT_OBJECT_0 ? true : false; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp new file mode 100644 index 000000000..243c501dc --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp @@ -0,0 +1,425 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "windows/PsWindowsInclude.h" +#include "PsFoundation.h" +#include "PsThread.h" +#include "foundation/PxErrorCallback.h" +#include "foundation/PxAssert.h" + +// an exception for setting the thread name in Microsoft debuggers +#define NS_MS_VC_EXCEPTION 0x406D1388 + +namespace physx +{ +namespace shdfnd +{ + +namespace +{ + +#if PX_VC +#pragma warning(disable : 4061) // enumerator 'identifier' in switch of enum 'enumeration' is not handled +#pragma warning(disable : 4191) //'operator/operation' : unsafe conversion from 'type of expression' to 'type required' +#endif + +// struct for naming a thread in the debugger +#pragma pack(push, 8) + +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; + +#pragma pack(pop) + +class _ThreadImpl +{ + public: + enum State + { + NotStarted, + Started, + Stopped + }; + + HANDLE thread; + LONG quitNow; // Should be 32bit aligned on SMP systems. + State state; + DWORD threadID; + + ThreadImpl::ExecuteFn fn; + void* arg; + + uint32_t affinityMask; + const char* name; +}; + +_ThreadImpl* getThread(ThreadImpl* impl) +{ + return reinterpret_cast<_ThreadImpl*>(impl); +} + +DWORD WINAPI PxThreadStart(LPVOID arg) +{ + _ThreadImpl* impl = getThread((ThreadImpl*)arg); + + // run either the passed in function or execute from the derived class (Runnable). + if(impl->fn) + (*impl->fn)(impl->arg); + else if(impl->arg) + ((Runnable*)impl->arg)->execute(); + return 0; +} + +// cache physical thread count +uint32_t gPhysicalCoreCount = 0; +} + +uint32_t ThreadImpl::getSize() +{ + return sizeof(_ThreadImpl); +} + +ThreadImpl::Id ThreadImpl::getId() +{ + return static_cast(GetCurrentThreadId()); +} + +// fwd GetLogicalProcessorInformation() +typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); + +uint32_t ThreadImpl::getNbPhysicalCores() +{ + if(!gPhysicalCoreCount) + { + // modified example code from: http://msdn.microsoft.com/en-us/library/ms683194 + LPFN_GLPI glpi; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL; + DWORD returnLength = 0; + DWORD processorCoreCount = 0; + DWORD byteOffset = 0; + + glpi = (LPFN_GLPI)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "GetLogicalProcessorInformation"); + + if(NULL == glpi) + { + // GetLogicalProcessorInformation not supported on OS < XP Service Pack 3 + return 0; + } + + DWORD rc = (DWORD)glpi(NULL, &returnLength); + PX_ASSERT(rc == FALSE); + PX_UNUSED(rc); + + // first query reports required buffer space + if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)PxAlloca(returnLength); + } + else + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Error querying buffer size for number of physical processors"); + return 0; + } + + // retrieve data + rc = (DWORD)glpi(buffer, &returnLength); + if(rc != TRUE) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Error querying number of physical processors"); + return 0; + } + + ptr = buffer; + + while(byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) + { + switch(ptr->Relationship) + { + case RelationProcessorCore: + processorCoreCount++; + break; + default: + break; + } + + byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + ptr++; + } + + gPhysicalCoreCount = processorCoreCount; + } + + return gPhysicalCoreCount; +} + +ThreadImpl::ThreadImpl() +{ + getThread(this)->thread = NULL; + getThread(this)->state = _ThreadImpl::NotStarted; + getThread(this)->quitNow = 0; + getThread(this)->fn = NULL; + getThread(this)->arg = NULL; + getThread(this)->affinityMask = 0; + getThread(this)->name = NULL; +} + +ThreadImpl::ThreadImpl(ExecuteFn fn, void* arg, const char* name) +{ + getThread(this)->thread = NULL; + getThread(this)->state = _ThreadImpl::NotStarted; + getThread(this)->quitNow = 0; + getThread(this)->fn = fn; + getThread(this)->arg = arg; + getThread(this)->affinityMask = 0; + getThread(this)->name = name; + + start(0, NULL); +} + +ThreadImpl::~ThreadImpl() +{ + if(getThread(this)->state == _ThreadImpl::Started) + kill(); + CloseHandle(getThread(this)->thread); +} + +void ThreadImpl::start(uint32_t stackSize, Runnable* runnable) +{ + if(getThread(this)->state != _ThreadImpl::NotStarted) + return; + getThread(this)->state = _ThreadImpl::Started; + + if(runnable && !getThread(this)->arg && !getThread(this)->fn) + getThread(this)->arg = runnable; + + getThread(this)->thread = + CreateThread(NULL, stackSize, PxThreadStart, (LPVOID) this, CREATE_SUSPENDED, &getThread(this)->threadID); + if(!getThread(this)->thread) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PsWindowsThread::start: Failed to create thread."); + getThread(this)->state = _ThreadImpl::NotStarted; + return; + } + + // set affinity, set name and resume + if(getThread(this)->affinityMask) + setAffinityMask(getThread(this)->affinityMask); + + if (getThread(this)->name) + setName(getThread(this)->name); + + DWORD rc = ResumeThread(getThread(this)->thread); + if(rc == DWORD(-1)) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PsWindowsThread::start: Failed to resume thread."); + getThread(this)->state = _ThreadImpl::NotStarted; + return; + } +} + +void ThreadImpl::signalQuit() +{ + InterlockedIncrement(&(getThread(this)->quitNow)); +} + +bool ThreadImpl::waitForQuit() +{ + if(getThread(this)->state == _ThreadImpl::NotStarted) + return false; + + WaitForSingleObject(getThread(this)->thread, INFINITE); + return true; +} + +bool ThreadImpl::quitIsSignalled() +{ + return InterlockedCompareExchange(&(getThread(this)->quitNow), 0, 0) != 0; +} + +void ThreadImpl::quit() +{ + getThread(this)->state = _ThreadImpl::Stopped; + ExitThread(0); +} + +void ThreadImpl::kill() +{ + if(getThread(this)->state == _ThreadImpl::Started) + TerminateThread(getThread(this)->thread, 0); + getThread(this)->state = _ThreadImpl::Stopped; +} + +void ThreadImpl::sleep(uint32_t ms) +{ + Sleep(ms); +} + +void ThreadImpl::yield() +{ + SwitchToThread(); +} + +uint32_t ThreadImpl::setAffinityMask(uint32_t mask) +{ + if(mask) + { + // store affinity + getThread(this)->affinityMask = mask; + + // if thread already started apply immediately + if(getThread(this)->state == _ThreadImpl::Started) + { + uint32_t err = uint32_t(SetThreadAffinityMask(getThread(this)->thread, mask)); + return err; + } + } + + return 0; +} + +void ThreadImpl::setName(const char* name) +{ + getThread(this)->name = name; + + if (getThread(this)->state == _ThreadImpl::Started) + { + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = getThread(this)->threadID; + info.dwFlags = 0; + + // C++ Exceptions are disabled for this project, but SEH is not (and cannot be) + // http://stackoverflow.com/questions/943087/what-exactly-will-happen-if-i-disable-c-exceptions-in-a-project + __try + { + RaiseException(NS_MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + // this runs if not attached to a debugger (thus not really naming the thread) + } + } +} + +void ThreadImpl::setPriority(ThreadPriority::Enum prio) +{ + BOOL rc = false; + switch(prio) + { + case ThreadPriority::eHIGH: + rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_HIGHEST); + break; + case ThreadPriority::eABOVE_NORMAL: + rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_ABOVE_NORMAL); + break; + case ThreadPriority::eNORMAL: + rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_NORMAL); + break; + case ThreadPriority::eBELOW_NORMAL: + rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_BELOW_NORMAL); + break; + case ThreadPriority::eLOW: + rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_LOWEST); + break; + default: + break; + } + if(!rc) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PsWindowsThread::setPriority: Failed to set thread priority."); + } +} + +ThreadPriority::Enum ThreadImpl::getPriority(Id threadId) +{ + ThreadPriority::Enum retval = ThreadPriority::eLOW; + int priority = GetThreadPriority((HANDLE)threadId); + PX_COMPILE_TIME_ASSERT(THREAD_PRIORITY_HIGHEST > THREAD_PRIORITY_ABOVE_NORMAL); + if(priority >= THREAD_PRIORITY_HIGHEST) + retval = ThreadPriority::eHIGH; + else if(priority >= THREAD_PRIORITY_ABOVE_NORMAL) + retval = ThreadPriority::eABOVE_NORMAL; + else if(priority >= THREAD_PRIORITY_NORMAL) + retval = ThreadPriority::eNORMAL; + else if(priority >= THREAD_PRIORITY_BELOW_NORMAL) + retval = ThreadPriority::eBELOW_NORMAL; + return retval; +} + +uint32_t TlsAlloc() +{ + DWORD rv = ::TlsAlloc(); + PX_ASSERT(rv != TLS_OUT_OF_INDEXES); + return (uint32_t)rv; +} + +void TlsFree(uint32_t index) +{ + ::TlsFree(index); +} + +void* TlsGet(uint32_t index) +{ + return ::TlsGetValue(index); +} + +size_t TlsGetValue(uint32_t index) +{ + return reinterpret_cast(::TlsGetValue(index)); +} + +uint32_t TlsSet(uint32_t index, void* value) +{ + return (uint32_t)::TlsSetValue(index, value); +} + +uint32_t TlsSetValue(uint32_t index, size_t value) +{ + return (uint32_t)::TlsSetValue(index, reinterpret_cast(value)); +} + +uint32_t ThreadImpl::getDefaultStackSize() +{ + return 1048576; +}; + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp new file mode 100644 index 000000000..71d5e41c6 --- /dev/null +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp @@ -0,0 +1,101 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsTime.h" +#include "windows/PsWindowsInclude.h" + +namespace +{ +int64_t getTimeTicks() +{ + LARGE_INTEGER a; + QueryPerformanceCounter(&a); + return a.QuadPart; +} + +double getTickDuration() +{ + LARGE_INTEGER a; + QueryPerformanceFrequency(&a); + return 1.0f / double(a.QuadPart); +} + +double sTickDuration = getTickDuration(); +} // namespace + +namespace physx +{ +namespace shdfnd +{ + +static const CounterFrequencyToTensOfNanos gCounterFreq = Time::getCounterFrequency(); + +const CounterFrequencyToTensOfNanos& Time::getBootCounterFrequency() +{ + return gCounterFreq; +} + +CounterFrequencyToTensOfNanos Time::getCounterFrequency() +{ + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + return CounterFrequencyToTensOfNanos(Time::sNumTensOfNanoSecondsInASecond, (uint64_t)freq.QuadPart); +} + +uint64_t Time::getCurrentCounterValue() +{ + LARGE_INTEGER ticks; + QueryPerformanceCounter(&ticks); + return (uint64_t)ticks.QuadPart; +} + +Time::Time() : mTickCount(0) +{ + getElapsedSeconds(); +} + +Time::Second Time::getElapsedSeconds() +{ + int64_t lastTickCount = mTickCount; + mTickCount = getTimeTicks(); + return (mTickCount - lastTickCount) * sTickDuration; +} + +Time::Second Time::peekElapsedSeconds() +{ + return (getTimeTicks() - mTickCount) * sTickDuration; +} + +Time::Second Time::getLastTime() const +{ + return mTickCount * sTickDuration; +} + +} // namespace shdfnd +} // namespace physx diff --git a/src/PhysX/physx/source/geomutils/include/GuAxes.h b/src/PhysX/physx/source/geomutils/include/GuAxes.h new file mode 100644 index 000000000..4daa0a84d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuAxes.h @@ -0,0 +1,81 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_AXES_H +#define GU_AXES_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ +namespace Gu +{ + enum PointComponent + { + X_ = 0, + Y_ = 1, + Z_ = 2, + W_ = 3, + + PC_FORCE_DWORD = 0x7fffffff + }; + + enum AxisOrder + { + AXES_XYZ = (X_)|(Y_<<2)|(Z_<<4), + AXES_XZY = (X_)|(Z_<<2)|(Y_<<4), + AXES_YXZ = (Y_)|(X_<<2)|(Z_<<4), + AXES_YZX = (Y_)|(Z_<<2)|(X_<<4), + AXES_ZXY = (Z_)|(X_<<2)|(Y_<<4), + AXES_ZYX = (Z_)|(Y_<<2)|(X_<<4), + + AXES_FORCE_DWORD = 0x7fffffff + }; + + class Axes + { + public: + + PX_FORCE_INLINE Axes(AxisOrder order) + { + mAxis0 = PxU32((order ) & 3); + mAxis1 = PxU32((order>>2) & 3); + mAxis2 = PxU32((order>>4) & 3); + } + PX_FORCE_INLINE ~Axes() {} + + PxU32 mAxis0; + PxU32 mAxis1; + PxU32 mAxis2; + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuBox.h b/src/PhysX/physx/source/geomutils/include/GuBox.h new file mode 100644 index 000000000..bdd96e3ea --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuBox.h @@ -0,0 +1,246 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BOX_H +#define GU_BOX_H + +/** \addtogroup geomutils +@{ +*/ + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "CmScaling.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" + +namespace physx +{ +namespace Gu +{ + class Capsule; + + PX_PHYSX_COMMON_API void computeOBBPoints(PxVec3* PX_RESTRICT pts, const PxVec3& center, const PxVec3& extents, const PxVec3& base0, const PxVec3& base1, const PxVec3& base2); + + + /** + \brief Represents an oriented bounding box. + + As a center point, extents(radii) and a rotation. i.e. the center of the box is at the center point, + the box is rotated around this point with the rotation and it is 2*extents in width, height and depth. + */ + + /** + Box geometry + + The rot member describes the world space orientation of the box. + The center member gives the world space position of the box. + The extents give the local space coordinates of the box corner in the positive octant. + Dimensions of the box are: 2*extent. + Transformation to world space is: worldPoint = rot * localPoint + center + Transformation to local space is: localPoint = T(rot) * (worldPoint - center) + Where T(M) denotes the transpose of M. + */ +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + class PX_PHYSX_COMMON_API Box + { + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE Box() + { + } + + /** + \brief Constructor + + \param origin Center of the OBB + \param extent Extents/radii of the obb. + \param base rotation to apply to the obb. + */ + //! Construct from center, extent and rotation + PX_FORCE_INLINE Box(const PxVec3& origin, const PxVec3& extent, const PxMat33& base) : rot(base), center(origin), extents(extent) + {} + + //! Copy constructor + PX_FORCE_INLINE Box(const Box& other) : rot(other.rot), center(other.center), extents(other.extents) + {} + + /** + \brief Destructor + */ + PX_FORCE_INLINE ~Box() + { + } + + //! Assignment operator + PX_FORCE_INLINE const Box& operator=(const Box& other) + { + rot = other.rot; + center = other.center; + extents = other.extents; + return *this; + } + + /** + \brief Setups an empty box. + */ + PX_INLINE void setEmpty() + { + center = PxVec3(0); + extents = PxVec3(-PX_MAX_REAL, -PX_MAX_REAL, -PX_MAX_REAL); + rot = PxMat33(PxIdentity); + } + + /** + \brief Checks the box is valid. + + \return true if the box is valid + */ + PX_INLINE bool isValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0.0f + if(extents.x < 0.0f) return false; + if(extents.y < 0.0f) return false; + if(extents.z < 0.0f) return false; + return true; + } + +///////////// + PX_FORCE_INLINE void setAxes(const PxVec3& axis0, const PxVec3& axis1, const PxVec3& axis2) + { + rot.column0 = axis0; + rot.column1 = axis1; + rot.column2 = axis2; + } + + PX_FORCE_INLINE PxVec3 rotate(const PxVec3& src) const + { + return rot * src; + } + + PX_FORCE_INLINE PxVec3 rotateInv(const PxVec3& src) const + { + return rot.transformTranspose(src); + } + + PX_FORCE_INLINE PxVec3 transform(const PxVec3& src) const + { + return rot * src + center; + } + + PX_FORCE_INLINE PxTransform getTransform() const + { + return PxTransform(center, PxQuat(rot)); + } + + PX_INLINE PxVec3 computeAABBExtent() const + { + const PxReal a00 = PxAbs(rot[0][0]); + const PxReal a01 = PxAbs(rot[0][1]); + const PxReal a02 = PxAbs(rot[0][2]); + + const PxReal a10 = PxAbs(rot[1][0]); + const PxReal a11 = PxAbs(rot[1][1]); + const PxReal a12 = PxAbs(rot[1][2]); + + const PxReal a20 = PxAbs(rot[2][0]); + const PxReal a21 = PxAbs(rot[2][1]); + const PxReal a22 = PxAbs(rot[2][2]); + + const PxReal ex = extents.x; + const PxReal ey = extents.y; + const PxReal ez = extents.z; + + return PxVec3( a00 * ex + a10 * ey + a20 * ez, + a01 * ex + a11 * ey + a21 * ez, + a02 * ex + a12 * ey + a22 * ez); + } + + /** + Computes the obb points. + \param pts [out] 8 box points + */ + PX_FORCE_INLINE void computeBoxPoints(PxVec3* PX_RESTRICT pts) const + { + Gu::computeOBBPoints(pts, center, extents, rot.column0, rot.column1, rot.column2); + } + + void create(const Gu::Capsule& capsule); + + PxMat33 rot; + PxVec3 center; + PxVec3 extents; + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::Box) == 60); + + //! A padded version of Gu::Box, to safely load its data using SIMD + class BoxPadded : public Box + { + public: + PX_FORCE_INLINE BoxPadded() {} + PX_FORCE_INLINE ~BoxPadded() {} + PxU32 padding; + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::BoxPadded) == 64); + + //! Transforms a shape space AABB to a vertex space AABB (conservative). + PX_FORCE_INLINE void computeVertexSpaceAABB(Gu::Box& vertexSpaceOBB, const PxBounds3& worldBounds, const PxTransform& world2Shape, const Cm::FastVertex2ShapeScaling& scaling, bool idtScaleMesh) + { + PX_ASSERT(!worldBounds.isEmpty()); + const PxBounds3 boundsInMesh = PxBounds3::transformFast(world2Shape, worldBounds); // transform bounds from world to shape (excluding mesh scale) + + vertexSpaceOBB.rot = PxMat33(PxIdentity); + if(idtScaleMesh) + { + vertexSpaceOBB.center = boundsInMesh.getCenter(); + vertexSpaceOBB.extents = boundsInMesh.getExtents(); + } + else + { + const PxBounds3 bounds = PxBounds3::basisExtent(scaling.getShape2VertexSkew() * boundsInMesh.getCenter(), scaling.getShape2VertexSkew(), boundsInMesh.getExtents()); + vertexSpaceOBB.center = bounds.getCenter(); + vertexSpaceOBB.extents = bounds.getExtents(); + } + } + +#if PX_VC + #pragma warning(pop) +#endif + +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h new file mode 100644 index 000000000..ab28b9268 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_BOX_H +#define GU_DISTANCE_SEGMENT_BOX_H + +#include "foundation/PxMat33.h" +#include "GuSegment.h" +#include "GuBox.h" + +namespace physx +{ +namespace Gu +{ + + //! Compute the smallest distance from the (finite) line segment to the box. + PX_PHYSX_COMMON_API PxReal distanceSegmentBoxSquared( const PxVec3& segmentPoint0, const PxVec3& segmentPoint1, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxReal* segmentParam = NULL, + PxVec3* boxParam = NULL); + + PX_FORCE_INLINE PxReal distanceSegmentBoxSquared(const Gu::Segment& segment, const Gu::Box& box, PxReal* t = NULL, PxVec3* p = NULL) + { + return distanceSegmentBoxSquared(segment.p0, segment.p1, box.center, box.extents, box.rot, t, p); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h new file mode 100644 index 000000000..65202aaee --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h @@ -0,0 +1,65 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_SEGMENT_H +#define GU_DISTANCE_SEGMENT_SEGMENT_H + +#include "common/PxPhysXCommonConfig.h" +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + // This version fixes accuracy issues (e.g. TTP 4617), but needs to do 2 square roots in order + // to find the normalized direction and length of the segments, and then + // a division in order to renormalize the output + + PX_PHYSX_COMMON_API PxReal distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& dir0, PxReal extent0, + const PxVec3& origin1, const PxVec3& dir1, PxReal extent1, + PxReal* s=NULL, PxReal* t=NULL); + + PX_PHYSX_COMMON_API PxReal distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& extent0, + const PxVec3& origin1, const PxVec3& extent1, + PxReal* s=NULL, PxReal* t=NULL); + + PX_FORCE_INLINE PxReal distanceSegmentSegmentSquared( const Gu::Segment& segment0, + const Gu::Segment& segment1, + PxReal* s=NULL, PxReal* t=NULL) + { + return distanceSegmentSegmentSquared( segment0.p0, segment0.computeDirection(), + segment1.p0, segment1.computeDirection(), + s, t); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h b/src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h new file mode 100644 index 000000000..1b1b8eee2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_BOX_BOX_H +#define GU_INTERSECTION_BOX_BOX_H + +#include "foundation/PxMat33.h" +#include "foundation/PxBounds3.h" +#include "GuBox.h" + +namespace physx +{ +namespace Gu +{ + PX_PHYSX_COMMON_API bool intersectOBBOBB(const PxVec3& e0, const PxVec3& c0, const PxMat33& r0, const PxVec3& e1, const PxVec3& c1, const PxMat33& r1, bool full_test); + + PX_FORCE_INLINE bool intersectOBBAABB(const Gu::Box& obb, const PxBounds3& aabb) + { + PxVec3 center = aabb.getCenter(); + PxVec3 extents = aabb.getExtents(); + return intersectOBBOBB(obb.extents, obb.center, obb.rot, extents, center, PxMat33(PxIdentity), true); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h new file mode 100644 index 000000000..160ccd66f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h @@ -0,0 +1,91 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_TRIANGLE_BOX_H +#define GU_INTERSECTION_TRIANGLE_BOX_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "foundation/PxMat33.h" + +namespace physx +{ +namespace Gu +{ + class Box; + class BoxPadded; + + /** + Tests if a triangle overlaps a box (AABB). This is the reference non-SIMD code. + + \param center [in] the box center + \param extents [in] the box extents + \param p0 [in] triangle's first point + \param p1 [in] triangle's second point + \param p2 [in] triangle's third point + \return true if triangle overlaps box + */ + PX_PHYSX_COMMON_API Ps::IntBool intersectTriangleBox_ReferenceCode(const PxVec3& center, const PxVec3& extents, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2); + + /** + Tests if a triangle overlaps a box (AABB). This is the optimized SIMD code. + + WARNING: the function has various SIMD requirements, left to the calling code: + - function will load 4 bytes after 'center'. Make sure it's safe to load from there. + - function will load 4 bytes after 'extents'. Make sure it's safe to load from there. + - function will load 4 bytes after 'p0'. Make sure it's safe to load from there. + - function will load 4 bytes after 'p1'. Make sure it's safe to load from there. + - function will load 4 bytes after 'p2'. Make sure it's safe to load from there. + If you can't guarantee these requirements, please use the non-SIMD reference code instead. + + \param center [in] the box center. + \param extents [in] the box extents + \param p0 [in] triangle's first point + \param p1 [in] triangle's second point + \param p2 [in] triangle's third point + \return true if triangle overlaps box + */ + PX_PHYSX_COMMON_API Ps::IntBool intersectTriangleBox_Unsafe(const PxVec3& center, const PxVec3& extents, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2); + + /** + Tests if a triangle overlaps a box (OBB). + + There are currently no SIMD-related requirements for p0, p1, p2. + + \param box [in] the box + \param p0 [in] triangle's first point + \param p1 [in] triangle's second point + \param p2 [in] triangle's third point + \return true if triangle overlaps box + */ + PX_PHYSX_COMMON_API Ps::IntBool intersectTriangleBox(const BoxPadded& box, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2); +} // namespace Gu +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h new file mode 100644 index 000000000..e136c8ed7 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h @@ -0,0 +1,251 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_TRIANGLE_BOX_REF_H +#define GU_INTERSECTION_TRIANGLE_BOX_REF_H + +#include "CmPhysXCommon.h" +#include "foundation/PxVec3.h" + + +/********************************************************/ +/* AABB-triangle overlap test code */ +/* by Tomas Akenine-M?r */ +/* Function: int triBoxOverlap(float boxcenter[3], */ +/* float boxhalfsize[3],float triverts[3][3]); */ +/* History: */ +/* 2001-03-05: released the code in its first version */ +/* 2001-06-18: changed the order of the tests, faster */ +/* */ +/* Acknowledgement: Many thanks to Pierre Terdiman for */ +/* suggestions and discussions on how to optimize code. */ +/* Thanks to David Hunt for finding a ">="-bug! */ +/********************************************************/ + + +namespace physx +{ + +#define CROSS(dest,v1,v2) \ + dest.x=v1.y*v2.z-v1.z*v2.y; \ + dest.y=v1.z*v2.x-v1.x*v2.z; \ + dest.z=v1.x*v2.y-v1.y*v2.x; + +#define DOT(v1,v2) (v1.x*v2.x+v1.y*v2.y+v1.z*v2.z) + +#define FINDMINMAX(x0, x1, x2, minimum, maximum) \ + minimum = physx::intrinsics::selectMin(x0, x1); \ + maximum = physx::intrinsics::selectMax(x0, x1); \ + minimum = physx::intrinsics::selectMin(minimum, x2); \ + maximum = physx::intrinsics::selectMax(maximum, x2); + + static PX_CUDA_CALLABLE PX_FORCE_INLINE Ps::IntBool planeBoxOverlap(const PxVec3& normal, PxReal d, const PxVec3& maxbox) + { + PxVec3 vmin, vmax; + + if (normal.x>0.0f) + { + vmin.x = -maxbox.x; + vmax.x = maxbox.x; + } + else + { + vmin.x = maxbox.x; + vmax.x = -maxbox.x; + } + + if (normal.y>0.0f) + { + vmin.y = -maxbox.y; + vmax.y = maxbox.y; + } + else + { + vmin.y = maxbox.y; + vmax.y = -maxbox.y; + } + + if (normal.z>0.0f) + { + vmin.z = -maxbox.z; + vmax.z = maxbox.z; + } + else + { + vmin.z = maxbox.z; + vmax.z = -maxbox.z; + } + + if (normal.dot(vmin) + d > 0.0f) return Ps::IntFalse; + if (normal.dot(vmax) + d >= 0.0f) return Ps::IntTrue; + return Ps::IntFalse; + } + + /*======================== X-tests ========================*/ +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p2 = a*v2.y - b*v2.z; \ + minimum = physx::intrinsics::selectMin(p0, p2); \ + maximum = physx::intrinsics::selectMax(p0, p2); \ + rad = fa * extents.y + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p1 = a*v1.y - b*v1.z; \ + minimum = physx::intrinsics::selectMin(p0, p1); \ + maximum = physx::intrinsics::selectMax(p0, p1); \ + rad = fa * extents.y + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + + /*======================== Y-tests ========================*/ +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p2 = -a*v2.x + b*v2.z; \ + minimum = physx::intrinsics::selectMin(p0, p2); \ + maximum = physx::intrinsics::selectMax(p0, p2); \ + rad = fa * extents.x + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p1 = -a*v1.x + b*v1.z; \ + minimum = physx::intrinsics::selectMin(p0, p1); \ + maximum = physx::intrinsics::selectMax(p0, p1); \ + rad = fa * extents.x + fb * extents.z; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + + /*======================== Z-tests ========================*/ +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1.x - b*v1.y; \ + p2 = a*v2.x - b*v2.y; \ + minimum = physx::intrinsics::selectMin(p1, p2); \ + maximum = physx::intrinsics::selectMax(p1, p2); \ + rad = fa * extents.x + fb * extents.y; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0.x - b*v0.y; \ + p1 = a*v1.x - b*v1.y; \ + minimum = physx::intrinsics::selectMin(p0, p1); \ + maximum = physx::intrinsics::selectMax(p0, p1); \ + rad = fa * extents.x + fb * extents.y; \ + if(minimum>rad || maximum<-rad) return Ps::IntFalse; + + namespace Gu + { + + static PX_CUDA_CALLABLE PX_FORCE_INLINE Ps::IntBool intersectTriangleBox_RefImpl(const PxVec3& boxcenter, const PxVec3& extents, const PxVec3& tp0, const PxVec3& tp1, const PxVec3& tp2) + { + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + + // This is the fastest branch on Sun - move everything so that the boxcenter is in (0,0,0) + const PxVec3 v0 = tp0 - boxcenter; + const PxVec3 v1 = tp1 - boxcenter; + const PxVec3 v2 = tp2 - boxcenter; + + // compute triangle edges + const PxVec3 e0 = v1 - v0; // tri edge 0 + const PxVec3 e1 = v2 - v1; // tri edge 1 + const PxVec3 e2 = v0 - v2; // tri edge 2 + + float minimum, maximum, rad, p0, p1, p2; + + // Bullet 3: test the 9 tests first (this was faster) + float fex = PxAbs(e0.x); + float fey = PxAbs(e0.y); + float fez = PxAbs(e0.z); + AXISTEST_X01(e0.z, e0.y, fez, fey); + AXISTEST_Y02(e0.z, e0.x, fez, fex); + AXISTEST_Z12(e0.y, e0.x, fey, fex); + + fex = PxAbs(e1.x); + fey = PxAbs(e1.y); + fez = PxAbs(e1.z); + AXISTEST_X01(e1.z, e1.y, fez, fey); + AXISTEST_Y02(e1.z, e1.x, fez, fex); + AXISTEST_Z0(e1.y, e1.x, fey, fex); + + fex = PxAbs(e2.x); + fey = PxAbs(e2.y); + fez = PxAbs(e2.z); + AXISTEST_X2(e2.z, e2.y, fez, fey); + AXISTEST_Y1(e2.z, e2.x, fez, fex); + AXISTEST_Z12(e2.y, e2.x, fey, fex); + + // Bullet 1: + // first test overlap in the {x,y,z}-directions + // find minimum, maximum of the triangle each direction, and test for overlap in + // that direction -- this is equivalent to testing a minimal AABB around + // the triangle against the AABB + + // test in X-direction + FINDMINMAX(v0.x, v1.x, v2.x, minimum, maximum); + if (minimum>extents.x || maximum<-extents.x) return Ps::IntFalse; + + // test in Y-direction + FINDMINMAX(v0.y, v1.y, v2.y, minimum, maximum); + if (minimum>extents.y || maximum<-extents.y) return Ps::IntFalse; + + // test in Z-direction + FINDMINMAX(v0.z, v1.z, v2.z, minimum, maximum); + if (minimum>extents.z || maximum<-extents.z) return Ps::IntFalse; + + // Bullet 2: + // test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + PxVec3 normal; + CROSS(normal, e0, e1); + const float d = -DOT(normal, v0); // plane eq: normal.x+d=0 + if (!planeBoxOverlap(normal, d, extents)) return Ps::IntFalse; + + return Ps::IntTrue; // box and triangle overlaps + } + } + +#undef CROSS +#undef DOT +#undef FINDMINMAX +#undef AXISTEST_X01 +#undef AXISTEST_X2 +#undef AXISTEST_Y02 +#undef AXISTEST_Y1 +#undef AXISTEST_Z12 +#undef AXISTEST_Z0 + +} + +#endif + diff --git a/src/PhysX/physx/source/geomutils/include/GuRaycastTests.h b/src/PhysX/physx/source/geomutils/include/GuRaycastTests.h new file mode 100644 index 000000000..e54e65b71 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuRaycastTests.h @@ -0,0 +1,72 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_RAYCAST_TESTS_H +#define GU_RAYCAST_TESTS_H + +#include "CmPhysXCommon.h" +#include "foundation/PxSimpleTypes.h" +#include "PxQueryReport.h" +#include "PxGeometry.h" + +namespace physx +{ + // PT: TODO: why is PxHitFlag::eMESH_MULTIPLE used in the ray-vs-hf function, but not in the ray-vs-mesh function? + + // PT: we use a define to be able to quickly change the signature of all raycast functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] geom geometry object to raycast against + // \param[in] pose pose of geometry object + // \param[in] rayOrigin ray's origin + // \param[in] rayDir ray's unit dir + // \param[in] maxDist ray's length/max distance + // \param[in] hitFlags query behavior flags + // \param[in] maxHits max number of hits = size of 'hits' buffer + // \param[out] hits result buffer where to write raycast hits + #define GU_RAY_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, \ + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits + namespace Gu + { + // PT: function pointer for Geom-indexed raycast functions + // See GU_RAY_FUNC_PARAMS for function parameters details. + // \return number of hits written to 'hits' result buffer + // \note there's no mechanism to report overflow. Returned number of hits is just clamped to maxHits. + typedef PxU32 (*RaycastFunc) (GU_RAY_FUNC_PARAMS); + + // PT: typedef for a bundle of all raycast functions, i.e. the function table itself (indexed by geom-type). + typedef RaycastFunc GeomRaycastTable[PxGeometryType::eGEOMETRY_COUNT]; + + // PT: retrieves the raycast function table (for access by external non-Gu modules) + PX_PHYSX_COMMON_API const GeomRaycastTable& getRaycastFuncTable(); + + } // namespace Gu +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h b/src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h new file mode 100644 index 000000000..a28f39f82 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SIMD_HELPERS_H +#define GU_SIMD_HELPERS_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "geometry/PxTriangle.h" +#include "foundation/PxMat33.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + //! A padded version of PxTriangle, to safely load its data using SIMD + class TrianglePadded : public PxTriangle + { + public: + PX_FORCE_INLINE TrianglePadded() {} + PX_FORCE_INLINE ~TrianglePadded() {} + PxU32 padding; + }; + + // PT: wrapper helper class to make sure we can safely load a PxVec3 using SIMD loads + // PT: TODO: refactor with PxVec3Pad + class Vec3p : public PxVec3 + { + public: + PX_FORCE_INLINE Vec3p() {} + PX_FORCE_INLINE ~Vec3p() {} + PX_FORCE_INLINE Vec3p(const PxVec3& p) : PxVec3(p) {} + PX_FORCE_INLINE Vec3p(float f) : PxVec3(f) {} + PxU32 padding; + }; + PX_COMPILE_TIME_ASSERT(sizeof(Vec3p) == 16); + + //! A padded version of PxMat33, to safely load its data using SIMD + class PxMat33Padded : public PxMat33 + { + public: + explicit PX_FORCE_INLINE PxMat33Padded(const PxQuat& q) + { + using namespace Ps::aos; + const QuatV qV = V4LoadU(&q.x); + Vec3V column0V, column1V, column2V; + QuatGetMat33V(qV, column0V, column1V, column2V); +#if defined(PX_SIMD_DISABLED) || PX_ANDROID || (PX_LINUX && (PX_ARM || PX_A64)) + V3StoreU(column0V, column0); + V3StoreU(column1V, column1); + V3StoreU(column2V, column2); +#else + V4StoreU(column0V, &column0.x); + V4StoreU(column1V, &column1.x); + V4StoreU(column2V, &column2.x); +#endif + } + PX_FORCE_INLINE ~PxMat33Padded() {} + PX_FORCE_INLINE void operator=(const PxMat33& other) + { + column0 = other.column0; + column1 = other.column1; + column2 = other.column2; + } + PxU32 padding; + }; + +} // namespace Gu +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/include/GuSegment.h b/src/PhysX/physx/source/geomutils/include/GuSegment.h new file mode 100644 index 000000000..bcee652c5 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/include/GuSegment.h @@ -0,0 +1,182 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SEGMENT_H +#define GU_SEGMENT_H +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "Ps.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + + /** + \brief Represents a line segment. + + Line segment geometry + In some cases this structure will be used to represent the infinite line that passes point0 and point1. + */ + class Segment + { + public: + /** + \brief Constructor + */ + PX_INLINE Segment() + { + } + + /** + \brief Constructor + */ + PX_INLINE Segment(const PxVec3& _p0, const PxVec3& _p1) : p0(_p0), p1(_p1) + { + } + + /** + \brief Copy constructor + */ + PX_INLINE Segment(const Segment& seg) : p0(seg.p0), p1(seg.p1) + { + } + + /** + \brief Destructor + */ + PX_INLINE ~Segment() + { + } + + //! Assignment operator + PX_INLINE Segment& operator=(const Segment& other) + { + p0 = other.p0; + p1 = other.p1; + return *this; + } + + //! Equality operator + PX_INLINE bool operator==(const Segment& other) const + { + return (p0==other.p0 && p1==other.p1); + } + + //! Inequality operator + PX_INLINE bool operator!=(const Segment& other) const + { + return (p0!=other.p0 || p1!=other.p1); + } + + PX_INLINE const PxVec3& getOrigin() const + { + return p0; + } + + //! Return the vector from point0 to point1 + PX_INLINE PxVec3 computeDirection() const + { + return p1 - p0; + } + + //! Return the vector from point0 to point1 + PX_INLINE void computeDirection(PxVec3& dir) const + { + dir = p1 - p0; + } + + //! Return the center of the segment segment + PX_INLINE PxVec3 computeCenter() const + { + return (p0 + p1)*0.5f; + } + + PX_INLINE PxF32 computeLength() const + { + return (p1-p0).magnitude(); + } + + PX_INLINE PxF32 computeSquareLength() const + { + return (p1-p0).magnitudeSquared(); + } + + // PT: TODO: remove this one + //! Return the square of the length of vector from point0 to point1 + PX_INLINE PxReal lengthSquared() const + { + return ((p1 - p0).magnitudeSquared()); + } + + // PT: TODO: remove this one + //! Return the length of vector from point0 to point1 + PX_INLINE PxReal length() const + { + return ((p1 - p0).magnitude()); + } + + /* PX_INLINE void setOriginDirection(const PxVec3& origin, const PxVec3& direction) + { + p0 = p1 = origin; + p1 += direction; + }*/ + + /** + \brief Computes a point on the segment + + \param[out] pt point on segment + \param[in] t point's parameter [t=0 => pt = mP0, t=1 => pt = mP1] + */ + PX_INLINE void computePoint(PxVec3& pt, PxF32 t) const + { + pt = p0 + t * (p1 - p0); + } + + // PT: TODO: remove this one + //! Return the point at parameter t along the line: point0 + t*(point1-point0) + PX_INLINE PxVec3 getPointAt(PxReal t) const + { + return (p1 - p0)*t + p0; + } + + PxVec3 p0; //!< Start of segment + PxVec3 p1; //!< End of segment + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::Segment) == 24); +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp new file mode 100644 index 000000000..1d9ae9dcc --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp @@ -0,0 +1,307 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuAABBTreeBuild.h" + +#include "PsMathUtils.h" +#include "PsFoundation.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; + +NodeAllocator::NodeAllocator() : mPool(NULL), mCurrentSlabIndex(0), mTotalNbNodes(0) +{ +} + +NodeAllocator::~NodeAllocator() +{ + release(); +} + +void NodeAllocator::release() +{ + const PxU32 nbSlabs = mSlabs.size(); + for (PxU32 i = 0; imNodeIndex = 0; + mPool->mNbPrimitives = nbPrimitives; + + mSlabs.pushBack(Slab(mPool, 1, estimatedFinalSize)); + mCurrentSlabIndex = 0; + mTotalNbNodes = 1; +} + +// PT: TODO: inline this? +AABBTreeBuildNode* NodeAllocator::getBiNode() +{ + mTotalNbNodes += 2; + Slab& currentSlab = mSlabs[mCurrentSlabIndex]; + if (currentSlab.mNbUsedNodes + 2 <= currentSlab.mMaxNbNodes) + { + AABBTreeBuildNode* biNode = currentSlab.mPool + currentSlab.mNbUsedNodes; + currentSlab.mNbUsedNodes += 2; + return biNode; + } + else + { + // Allocate new slab + const PxU32 size = 1024; + AABBTreeBuildNode* pool = PX_NEW(AABBTreeBuildNode)[size]; + PxMemZero(pool, sizeof(AABBTreeBuildNode)*size); + + mSlabs.pushBack(Slab(pool, 2, size)); + mCurrentSlabIndex++; + return pool; + } +} + +static PX_FORCE_INLINE float getSplittingValue(const PxBounds3& global_box, PxU32 axis) +{ + // Default split value = middle of the axis (using only the box) + return global_box.getCenter(axis); +} + +static PxU32 split(const PxBounds3& box, PxU32 nb, PxU32* const PX_RESTRICT prims, PxU32 axis, const AABBTreeBuildParams& params) +{ + // Get node split value + const float splitValue = getSplittingValue(box, axis); + + PxU32 nbPos = 0; + // Loop through all node-related primitives. Their indices range from "mNodePrimitives[0]" to "mNodePrimitives[mNbPrimitives-1]", + // with mNodePrimitives = mIndices + mNodeIndex (i.e. those indices map the global list in the tree params). + + // PT: to avoid calling the unsafe [] operator + const size_t ptrValue = size_t(params.mCache) + axis * sizeof(float); + const PxVec3* /*PX_RESTRICT*/ cache = reinterpret_cast(ptrValue); + + for (PxU32 i = 0; i splitValue) + { + // Swap entries + prims[i] = prims[nbPos]; + prims[nbPos] = index; + // Count primitives assigned to positive space + nbPos++; + } + } + return nbPos; +} + +void AABBTreeBuildNode::subdivide(const AABBTreeBuildParams& params, BuildStats& stats, NodeAllocator& allocator, PxU32* const indices) +{ + PxU32* const PX_RESTRICT primitives = indices + mNodeIndex; + const PxU32 nbPrims = mNbPrimitives; + + // Compute global box & means for current node. The box is stored in mBV. + Vec4V meansV; + { + const PxBounds3* PX_RESTRICT boxes = params.mAABBArray; + PX_ASSERT(boxes); + PX_ASSERT(primitives); + PX_ASSERT(nbPrims); + + Vec4V minV = V4LoadU(&boxes[primitives[0]].minimum.x); + Vec4V maxV = V4LoadU(&boxes[primitives[0]].maximum.x); + + meansV = V4LoadU(¶ms.mCache[primitives[0]].x); + + for (PxU32 i = 1; iparams.mLimit) + { + nbPos = nbPrims >> 1; + } + else return; + } + + // Now create children and assign their pointers. + mPos = allocator.getBiNode(); + + stats.increaseCount(2); + + // Assign children + PX_ASSERT(!isLeaf()); + AABBTreeBuildNode* Pos = const_cast(mPos); + AABBTreeBuildNode* Neg = Pos + 1; + Pos->mNodeIndex = mNodeIndex; + Pos->mNbPrimitives = nbPos; + Neg->mNodeIndex = mNodeIndex + nbPos; + Neg->mNbPrimitives = mNbPrimitives - nbPos; +} + +void AABBTreeBuildNode::_buildHierarchy(AABBTreeBuildParams& params, BuildStats& stats, NodeAllocator& nodeBase, PxU32* const indices) +{ + // Subdivide current node + subdivide(params, stats, nodeBase, indices); + + // Recurse + if (!isLeaf()) + { + AABBTreeBuildNode* Pos = const_cast(getPos()); + PX_ASSERT(Pos); + AABBTreeBuildNode* Neg = Pos + 1; + Pos->_buildHierarchy(params, stats, nodeBase, indices); + Neg->_buildHierarchy(params, stats, nodeBase, indices); + } + + stats.mTotalPrims += mNbPrimitives; +} + +bool Gu::initAABBTreeBuild(AABBTreeBuildParams& params, NodeAllocator& nodeAllocator, BuildStats& stats, PxU32*& indices) +{ + const PxU32 numPrimitives = params.mNbPrimitives; + + if(numPrimitives == 0) + return false; + + // indices already initialized! + if(indices) + return false; + + // Init stats + stats.setCount(1); + + // Initialize indices. This list will be modified during build. + indices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*numPrimitives, "AABB tree indices")); + // Identity permutation + for(PxU32 i=0;i(PX_ALLOC(sizeof(PxVec3)*(numPrimitives+1), "cache")); + const float half = 0.5f; + const FloatV halfV = FLoad(half); + for(PxU32 i=0;i_buildHierarchy(params, stats, nodeAllocator, indices); + + return true; +} + diff --git a/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h new file mode 100644 index 000000000..93dfb7f1a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h @@ -0,0 +1,185 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SQ_AABBTREE_BUILD_H +#define SQ_AABBTREE_BUILD_H + +#include "foundation/PxMemory.h" +#include "foundation/PxBounds3.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PsVecMath.h" +#include "PsArray.h" + +namespace physx +{ + + using namespace shdfnd::aos; + + namespace Gu + { +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + //! Contains AABB-tree build statistics + struct PX_PHYSX_COMMON_API BuildStats + { + BuildStats() : mCount(0), mTotalPrims(0) {} + + PxU32 mCount; //!< Number of nodes created + PxU32 mTotalPrims; //!< Total accumulated number of primitives. Should be much higher than the source + //!< number of prims, since it accumulates all prims covered by each node (i.e. internal + //!< nodes too, not just leaf ones) + + PX_FORCE_INLINE void reset() { mCount = mTotalPrims = 0; } + + PX_FORCE_INLINE void setCount(PxU32 nb) { mCount = nb; } + PX_FORCE_INLINE void increaseCount(PxU32 nb) { mCount += nb; } + PX_FORCE_INLINE PxU32 getCount() const { return mCount; } + }; + + //! Contains AABB-tree build parameters + class PX_PHYSX_COMMON_API AABBTreeBuildParams : public Ps::UserAllocated + { + public: + AABBTreeBuildParams(PxU32 limit = 1, PxU32 nb_prims = 0, const PxBounds3* boxes = NULL) : + mLimit(limit), mNbPrimitives(nb_prims), mAABBArray(boxes), mCache(NULL) {} + ~AABBTreeBuildParams() + { + reset(); + } + + PX_FORCE_INLINE void reset() + { + mLimit = mNbPrimitives = 0; + mAABBArray = NULL; + PX_FREE_AND_RESET(mCache); + } + + PxU32 mLimit; //!< Limit number of primitives / node. If limit is 1, build a complete tree (2*N-1 nodes) + PxU32 mNbPrimitives; //!< Number of (source) primitives. + const PxBounds3* mAABBArray; //!< Shortcut to an app-controlled array of AABBs. + PxVec3* mCache; //!< Cache for AABB centers - managed by build code. + }; + + class NodeAllocator; + + //! AABB tree node used for building + class PX_PHYSX_COMMON_API AABBTreeBuildNode : public Ps::UserAllocated + { + public: + PX_FORCE_INLINE AABBTreeBuildNode() {} + PX_FORCE_INLINE ~AABBTreeBuildNode() {} + + PX_FORCE_INLINE const PxBounds3& getAABB() const { return mBV; } + PX_FORCE_INLINE const AABBTreeBuildNode* getPos() const { return mPos; } + PX_FORCE_INLINE const AABBTreeBuildNode* getNeg() const { const AABBTreeBuildNode* P = mPos; return P ? P + 1 : NULL; } + + PX_FORCE_INLINE bool isLeaf() const { return !getPos(); } + + PxBounds3 mBV; //!< Global bounding-volume enclosing all the node-related primitives + const AABBTreeBuildNode* mPos; //!< "Positive" & "Negative" children + + PxU32 mNodeIndex; //!< Index of node-related primitives (in the tree's mIndices array) + PxU32 mNbPrimitives; //!< Number of primitives for this node + + // Data access + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mNbPrimitives; } + + PX_FORCE_INLINE PxU32 getNbRuntimePrimitives() const { return mNbPrimitives; } + PX_FORCE_INLINE void setNbRunTimePrimitives(PxU32 val) { mNbPrimitives = val; } + PX_FORCE_INLINE const PxU32* getPrimitives(const PxU32* base) const { return base + mNodeIndex; } + PX_FORCE_INLINE PxU32* getPrimitives(PxU32* base) { return base + mNodeIndex; } + + // Internal methods + void subdivide(const AABBTreeBuildParams& params, BuildStats& stats, NodeAllocator& allocator, PxU32* const indices); + void _buildHierarchy(AABBTreeBuildParams& params, BuildStats& stats, NodeAllocator& allocator, PxU32* const indices); + }; + + //! For complete trees we can predict the final number of nodes and preallocate them. For incomplete trees we can't. + //! But we don't want to allocate nodes one by one (which would be quite slow), so we use this helper class to + //! allocate N nodes at once, while minimizing the amount of nodes allocated for nothing. An initial amount of + //! nodes is estimated using the max number for a complete tree, and the user-defined number of primitives per leaf. + //! In ideal cases this estimated number will be quite close to the final number of nodes. When that number is not + //! enough though, slabs of N=1024 extra nodes are allocated until the build is complete. + class PX_PHYSX_COMMON_API NodeAllocator : public Ps::UserAllocated + { + public: + NodeAllocator(); + ~NodeAllocator(); + + void release(); + void init(PxU32 nbPrimitives, PxU32 limit); + AABBTreeBuildNode* getBiNode(); + + AABBTreeBuildNode* mPool; + + struct Slab + { + PX_FORCE_INLINE Slab() {} + PX_FORCE_INLINE Slab(AABBTreeBuildNode* pool, PxU32 nbUsedNodes, PxU32 maxNbNodes) : mPool(pool), mNbUsedNodes(nbUsedNodes), mMaxNbNodes(maxNbNodes) {} + AABBTreeBuildNode* mPool; + PxU32 mNbUsedNodes; + PxU32 mMaxNbNodes; + }; + Ps::Array mSlabs; + PxU32 mCurrentSlabIndex; + PxU32 mTotalNbNodes; + }; + + /* + * \brief Initialize AABBtree build from given parameters. + * \param params [in] AABBTree build params + * \param nodeAllocator [in] Node allocator + * \param stats [out] Statistics + * \param indices [out] Indices buffer allocated during build + */ + bool PX_PHYSX_COMMON_API initAABBTreeBuild(AABBTreeBuildParams& params, NodeAllocator& nodeAllocator, BuildStats& stats, PxU32*& indices); + + /* + * \brief Builds AABBtree from given parameters. + * \note Initialize will be called! + * \param params [in] AABBTree build params + * \param nodeAllocator [in] Node allocator + * \param stats [out] Statistics + * \param indices [out] Indices buffer allocated during build + */ + bool PX_PHYSX_COMMON_API buildAABBTree(AABBTreeBuildParams& params, NodeAllocator& nodeAllocator, BuildStats& stats, PxU32*& indices); +#if PX_VC + #pragma warning(pop) +#endif + + } // namespace Sq + +} + +#endif // SQ_AABBTREE_H diff --git a/src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h b/src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h new file mode 100644 index 000000000..2fbc91c95 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h @@ -0,0 +1,239 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef GU_AABBTREEQUERY_H +#define GU_AABBTREEQUERY_H + +#include "GuBVHTestsSIMD.h" +#include "PsInlineArray.h" + +namespace physx +{ + namespace Gu + { + #define RAW_TRAVERSAL_STACK_SIZE 256 + + ////////////////////////////////////////////////////////////////////////// + + static PX_FORCE_INLINE void getBoundsTimesTwo(Vec4V& center, Vec4V& extents, const PxBounds3* boxes, PxU32 poolIndex) + { + const PxBounds3* objectBounds = boxes + poolIndex; + + const Vec4V minV = V4LoadU(&objectBounds->minimum.x); + const Vec4V maxV = V4LoadU(&objectBounds->maximum.x); + + center = V4Add(maxV, minV); + extents = V4Sub(maxV, minV); + } + + ////////////////////////////////////////////////////////////////////////// + + template + class AABBTreeOverlap + { + public: + bool operator()(const Payload* objects, const PxBounds3* boxes, const Tree& tree, const Test& test, QueryCallback& visitor) + { + using namespace Cm; + + Ps::InlineArray stack; + stack.forceSize_Unsafe(RAW_TRAVERSAL_STACK_SIZE); + const Node* const nodeBase = tree.getNodes(); + stack[0] = nodeBase; + PxU32 stackIndex = 1; + + while (stackIndex > 0) + { + const Node* node = stack[--stackIndex]; + Vec3V center, extents; + node->getAABBCenterExtentsV(¢er, &extents); + while (test(center, extents)) + { + if (node->isLeaf()) + { + PxU32 nbPrims = node->getNbPrimitives(); + const bool doBoxTest = nbPrims > 1; + const PxU32* prims = node->getPrimitives(tree.getIndices()); + while (nbPrims--) + { + const PxU32* prunableIndex = prims; + prims++; + + const PxU32 poolIndex = *prunableIndex; + if (doBoxTest) + { + Vec4V center2, extents2; + getBoundsTimesTwo(center2, extents2, boxes, poolIndex); + + const float half = 0.5f; + const FloatV halfV = FLoad(half); + + const Vec4V extents_ = V4Scale(extents2, halfV); + const Vec4V center_ = V4Scale(center2, halfV); + + if (!test(Vec3V_From_Vec4V(center_), Vec3V_From_Vec4V(extents_))) + continue; + } + + PxReal unusedDistance; + if (!visitor.invoke(unusedDistance, objects[poolIndex])) + return false; + } + break; + } + + const Node* children = node->getPos(nodeBase); + + node = children; + stack[stackIndex++] = children + 1; + if(stackIndex == stack.capacity()) + stack.resizeUninitialized(stack.capacity() * 2); + node->getAABBCenterExtentsV(¢er, &extents); + } + } + return true; + } + }; + + ////////////////////////////////////////////////////////////////////////// + + template // use inflate=true for sweeps, inflate=false for raycasts + static PX_FORCE_INLINE bool doLeafTest(const Node* node, Gu::RayAABBTest& test, PxReal& md, PxReal oldMaxDist, + const Payload* objects, const PxBounds3* boxes, const Tree& tree, + PxReal& maxDist, QueryCallback& pcb) + { + PxU32 nbPrims = node->getNbPrimitives(); + const bool doBoxTest = nbPrims > 1; + const PxU32* prims = node->getPrimitives(tree.getIndices()); + while (nbPrims--) + { + const PxU32* prunableIndex = prims; + prims++; + + const PxU32 poolIndex = *prunableIndex; + if (doBoxTest) + { + Vec4V center_, extents_; + getBoundsTimesTwo(center_, extents_, boxes, poolIndex); + + if (!test.check(Vec3V_From_Vec4V(center_), Vec3V_From_Vec4V(extents_))) + continue; + } + + if (!pcb.invoke(md, objects[poolIndex])) + return false; + + if (md < oldMaxDist) + { + maxDist = md; + test.setDistance(md); + } + } + return true; + } + + ////////////////////////////////////////////////////////////////////////// + + template // use inflate=true for sweeps, inflate=false for raycasts + class AABBTreeRaycast + { + public: + bool operator()( + const Payload* objects, const PxBounds3* boxes, const Tree& tree, + const PxVec3& origin, const PxVec3& unitDir, PxReal& maxDist, const PxVec3& inflation, + QueryCallback& pcb) + { + using namespace Cm; + + // PT: we will pass center*2 and extents*2 to the ray-box code, to save some work per-box + // So we initialize the test with values multiplied by 2 as well, to get correct results + Gu::RayAABBTest test(origin*2.0f, unitDir*2.0f, maxDist, inflation*2.0f); + + Ps::InlineArray stack; + stack.forceSize_Unsafe(RAW_TRAVERSAL_STACK_SIZE); + const Node* const nodeBase = tree.getNodes(); + stack[0] = nodeBase; + PxU32 stackIndex = 1; + + PxReal oldMaxDist; + while (stackIndex--) + { + const Node* node = stack[stackIndex]; + Vec3V center, extents; + node->getAABBCenterExtentsV2(¢er, &extents); + if (test.check(center, extents)) // TODO: try timestamp ray shortening to skip this + { + PxReal md = maxDist; // has to be before the goto below to avoid compile error + while (!node->isLeaf()) + { + const Node* children = node->getPos(nodeBase); + + Vec3V c0, e0; + children[0].getAABBCenterExtentsV2(&c0, &e0); + const PxU32 b0 = test.check(c0, e0); + + Vec3V c1, e1; + children[1].getAABBCenterExtentsV2(&c1, &e1); + const PxU32 b1 = test.check(c1, e1); + + if (b0 && b1) // if both intersect, push the one with the further center on the stack for later + { + // & 1 because FAllGrtr behavior differs across platforms + const PxU32 bit = FAllGrtr(V3Dot(V3Sub(c1, c0), test.mDir), FZero()) & 1; + stack[stackIndex++] = children + bit; + node = children + (1 - bit); + if (stackIndex == stack.capacity()) + stack.resizeUninitialized(stack.capacity() * 2); + } + else if (b0) + node = children; + else if (b1) + node = children + 1; + else + goto skip_leaf_code; + } + + oldMaxDist = maxDist; // we copy since maxDist can be updated in the callback and md(node, test, md, oldMaxDist, + objects, boxes, tree, + maxDist, + pcb)) + return false; + skip_leaf_code:; + } + } + return true; + } + }; + } +} + +#endif // SQ_AABBTREEQUERY_H diff --git a/src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp new file mode 100644 index 000000000..b565da2b6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp @@ -0,0 +1,186 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuBVHStructure.h" +#include "GuAABBTreeBuild.h" +#include "GuAABBTreeQuery.h" +#include "GuSerialize.h" +#include "GuBounds.h" +#include "PsFoundation.h" +#include "CmUtils.h" + +using namespace physx; +using namespace Gu; + +BVHStructure::BVHStructure(GuMeshFactory* factory): + PxBVHStructure(PxType(PxConcreteType::eBVH_STRUCTURE), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE), + mMeshFactory(factory), + mNumVolumes(0), + mNumNodes(0), + mBounds(NULL), + mIndices(NULL), + mVolumes(NULL), + mNodes(NULL) +{ +} + +BVHStructure::BVHStructure(GuMeshFactory* factory, BVHStructureData& bvhData): + PxBVHStructure(PxType(PxConcreteType::eBVH_STRUCTURE), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE), + mMeshFactory(factory), + mNumVolumes(bvhData.mNumVolumes), + mNumNodes(bvhData.mNumNodes), + mBounds(bvhData.mBounds), + mIndices(bvhData.mIndices), + mVolumes(NULL), + mNodes(bvhData.mNodes) +{ +} + +BVHStructure::~BVHStructure() +{ +} + +bool BVHStructure::load(PxInputStream& stream) +{ + // Import header + PxU32 version; + bool mismatch; + if(!readHeader('B', 'V', 'H', 'S', version, mismatch, stream)) + return false; + + // read numVolumes, numNodes together + ReadDwordBuffer(&mNumVolumes, 2, mismatch, stream); + + // read indices + mIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mNumVolumes, "BVH indices")); + ReadDwordBuffer(mIndices, mNumVolumes, mismatch, stream); + + // read bounds + mBounds = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(mNumVolumes + 1), "BVH bounds")); + readFloatBuffer(&mBounds[0].minimum.x, mNumVolumes*(3 + 3), mismatch, stream); + + // read nodes + mNodes = reinterpret_cast(PX_ALLOC(sizeof(BVHNode)*mNumNodes, "BVH nodes")); + for(PxU32 i = 0; i < mNumNodes; i++) + { + ReadDwordBuffer(&mNodes[i].mData, 1, mismatch, stream); + + readFloatBuffer(&mNodes[i].mBV.minimum.x, 3 + 3, mismatch, stream); + } + return true; +} + +void BVHStructure::release() +{ + decRefCount(); +} + +void BVHStructure::onRefCountZero() +{ + PX_FREE_AND_RESET(mBounds); + PX_FREE_AND_RESET(mIndices); + PX_FREE_AND_RESET(mNodes); + PX_FREE_AND_RESET(mVolumes); + + mNumNodes = 0; + mNumVolumes = 0; + + if(mMeshFactory->removeBVHStructure(*this)) + { + const PxType type = getConcreteType(); + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, type); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::BVHStructure::release: double deletion detected!"); +} + +void BVHStructure::createVolumes() const +{ + if(!mVolumes) + { + mVolumes = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mNumVolumes, "BVH volume list")); + for(PxU32 i = 0; i < mNumVolumes; i++) + { + mVolumes[i] = i; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Query Implementation + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +PxU32 BVHStructure::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal maxDist, PxU32 maxHits, PxU32* PX_RESTRICT rayHits) const +{ + createVolumes(); + + BVHCallback cbk(rayHits, maxHits); + BVHTree tree(mNodes, mIndices); + AABBTreeRaycast()(mVolumes, mBounds, tree, origin, unitDir, maxDist, PxVec3(0.0f), cbk); + + return cbk.mCurrentHitsCount; +} + +PxU32 BVHStructure::sweep(const PxBounds3& aabb, const PxVec3& unitDir, PxReal maxDist, PxU32 maxHits, PxU32* PX_RESTRICT sweepHits) const +{ + createVolumes(); + + const PxVec3 extents = aabb.getExtents(); + + BVHCallback cbk(sweepHits, maxHits); + BVHTree tree(mNodes, mIndices); + + AABBTreeRaycast()(mVolumes, mBounds, tree, aabb.getCenter(), unitDir, maxDist, extents, cbk); + + return cbk.mCurrentHitsCount; +} + + +PxU32 BVHStructure::overlap(const PxBounds3& aabb, PxU32 maxHits, PxU32* PX_RESTRICT overlapHits) const +{ + createVolumes(); + + BVHCallback cbk(overlapHits, maxHits); + BVHTree tree(mNodes, mIndices); + + const Gu::AABBAABBTest test(aabb); + AABBTreeOverlap()(mVolumes, mBounds, tree, test, cbk); + + return cbk.mCurrentHitsCount; +} + diff --git a/src/PhysX/physx/source/geomutils/src/GuBVHStructure.h b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.h new file mode 100644 index 000000000..cf8b94706 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.h @@ -0,0 +1,200 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef GU_BVH_STRUCTURE_H +#define GU_BVH_STRUCTURE_H + +/** \addtogroup geomutils +@{ +*/ + +#include "PsUserAllocated.h" +#include "CmRefCountable.h" +#include "CmPhysXCommon.h" +#include "GuMeshFactory.h" +#include "PsVecMath.h" +#include "PxBVHStructure.h" + +namespace physx +{ +namespace Gu +{ + struct BVHNode + { + public: + PX_FORCE_INLINE PxU32 isLeaf() const { return mData&1; } + + PX_FORCE_INLINE const PxU32* getPrimitives(const PxU32* base) const { return base + (mData>>5); } + PX_FORCE_INLINE PxU32* getPrimitives(PxU32* base) { return base + (mData>>5); } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return (mData>>1)&15; } + + PX_FORCE_INLINE PxU32 getPosIndex() const { return mData>>1; } + PX_FORCE_INLINE PxU32 getNegIndex() const { return (mData>>1) + 1; } + PX_FORCE_INLINE const BVHNode* getPos(const BVHNode* base) const { return base + (mData>>1); } + PX_FORCE_INLINE const BVHNode* getNeg(const BVHNode* base) const { const BVHNode* P = getPos(base); return P ? P+1 : NULL;} + + PX_FORCE_INLINE BVHNode* getPos(BVHNode* base) { return base + (mData >> 1); } + PX_FORCE_INLINE BVHNode* getNeg(BVHNode* base) { BVHNode* P = getPos(base); return P ? P + 1 : NULL; } + + + PX_FORCE_INLINE void getAABBCenterExtentsV(shdfnd::aos::Vec3V* center, shdfnd::aos::Vec3V* extents) const + { + const shdfnd::aos::Vec4V minV = shdfnd::aos::V4LoadU(&mBV.minimum.x); + const shdfnd::aos::Vec4V maxV = shdfnd::aos::V4LoadU(&mBV.maximum.x); + + const float half = 0.5f; + const shdfnd::aos::FloatV halfV = shdfnd::aos::FLoad(half); + + *extents = shdfnd::aos::Vec3V_From_Vec4V(shdfnd::aos::V4Scale(shdfnd::aos::V4Sub(maxV, minV), halfV)); + *center = shdfnd::aos::Vec3V_From_Vec4V(shdfnd::aos::V4Scale(shdfnd::aos::V4Add(maxV, minV), halfV)); + } + + PX_FORCE_INLINE void getAABBCenterExtentsV2(shdfnd::aos::Vec3V* center, shdfnd::aos::Vec3V* extents) const + { + const shdfnd::aos::Vec4V minV = shdfnd::aos::V4LoadU(&mBV.minimum.x); + const shdfnd::aos::Vec4V maxV = shdfnd::aos::V4LoadU(&mBV.maximum.x); + + *extents = shdfnd::aos::Vec3V_From_Vec4V(shdfnd::aos::V4Sub(maxV, minV)); + *center = shdfnd::aos::Vec3V_From_Vec4V(shdfnd::aos::V4Add(maxV, minV)); + } + + PX_FORCE_INLINE void getAABBMinMaxV(shdfnd::aos::Vec4V* minV, shdfnd::aos::Vec4V* maxV) const + { + *minV = shdfnd::aos::V4LoadU(&mBV.minimum.x); + *maxV = shdfnd::aos::V4LoadU(&mBV.maximum.x); + } + + PxBounds3 mBV; // Global bounding-volume enclosing all the node-related primitives + PxU32 mData; // 27 bits node or prim index|4 bits #prims|1 bit leaf + }; + + struct BVHTree + { + BVHTree(const BVHNode* node, const PxU32* indices): + mRootNode(node), + mIndices(indices) + { + } + + + const BVHNode* getNodes() const { return mRootNode; } + const PxU32* getIndices() const { return mIndices; } + + const BVHNode* mRootNode; + const PxU32* mIndices; + }; + + struct BVHCallback + { + BVHCallback(PxU32* hits, PxU32 numMaxHits): + mHits(hits), + mNbMaxHits(numMaxHits), + mCurrentHitsCount(0) + { + } + + bool invoke(PxReal& , PxU32 payload) + { + mHits[mCurrentHitsCount++] = payload; + if(mCurrentHitsCount == mNbMaxHits) + return false; + + return true; + } + + PxU32* mHits; + PxU32 mNbMaxHits; + PxU32 mCurrentHitsCount; + }; + + struct BVHStructureData + { + PxU32 mNumVolumes; + PxU32 mNumNodes; + PxBounds3* mBounds; + PxU32* mIndices; + BVHNode* mNodes; + }; +/** +\brief Represents a BVH. +*/ + class BVHStructure: public PxBVHStructure, public Ps::UserAllocated, public Cm::RefCountable + { + public: + /** + \brief Constructor + */ + BVHStructure(GuMeshFactory* factory); + BVHStructure(GuMeshFactory* factory, BVHStructureData& data); + + /** + \brief Destructor + */ + ~BVHStructure(); + + + bool load(PxInputStream& desc); + + void release(); + +// PxBVHStructure + PxU32 getNbBounds() const { return mNumVolumes; } + const PxBounds3* getBounds() const { return mBounds; } + PxU32 raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal maxDist, PxU32 maxHits, PxU32* PX_RESTRICT rayHits) const; + PxU32 sweep(const PxBounds3& aabb, const PxVec3& unitDir, PxReal maxDist, PxU32 maxHits, PxU32* PX_RESTRICT sweepHits) const; + PxU32 overlap(const PxBounds3& aabb, PxU32 maxHits, PxU32* PX_RESTRICT overlapHits) const; +// ~PxBVHStructure + +// Cm::RefCountable + virtual void onRefCountZero(); +//~Cm::RefCountable + + const BVHNode* getNodes() const { return mNodes; } + const PxU32* getIndices() const { return mIndices; } + + private: + void createVolumes() const; + + private: + GuMeshFactory* mMeshFactory; + + PxU32 mNumVolumes; + PxU32 mNumNodes; + PxBounds3* mBounds; + PxU32* mIndices; + mutable PxU32* mVolumes; // used just for queries + BVHNode* mNodes; + }; +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h b/src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h new file mode 100644 index 000000000..903b4416a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h @@ -0,0 +1,259 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef GU_RAWQUERY_TESTS_SIMD_H +#define GU_RAWQUERY_TESTS_SIMD_H + +#include "foundation/PxTransform.h" +#include "foundation/PxBounds3.h" +#include "CmPhysXCommon.h" +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + +struct RayAABBTest +{ + PX_FORCE_INLINE RayAABBTest(const PxVec3& origin_, const PxVec3& unitDir_, const PxReal maxDist, const PxVec3& inflation_) + : mOrigin(V3LoadU(origin_)) + , mDir(V3LoadU(unitDir_)) + , mDirYZX(V3PermYZX(mDir)) + , mInflation(V3LoadU(inflation_)) + , mAbsDir(V3Abs(mDir)) + , mAbsDirYZX(V3PermYZX(mAbsDir)) + { + const PxVec3 ext = maxDist >= PX_MAX_F32 ? PxVec3( unitDir_.x == 0 ? origin_.x : PxSign(unitDir_.x)*PX_MAX_F32, + unitDir_.y == 0 ? origin_.y : PxSign(unitDir_.y)*PX_MAX_F32, + unitDir_.z == 0 ? origin_.z : PxSign(unitDir_.z)*PX_MAX_F32) + : origin_ + unitDir_ * maxDist; + mRayMin = V3Min(mOrigin, V3LoadU(ext)); + mRayMax = V3Max(mOrigin, V3LoadU(ext)); + } + + PX_FORCE_INLINE void setDistance(PxReal distance) + { + const Vec3V ext = V3ScaleAdd(mDir, FLoad(distance), mOrigin); + mRayMin = V3Min(mOrigin, ext); + mRayMax = V3Max(mOrigin, ext); + } + + template + PX_FORCE_INLINE PxU32 check(const Vec3V center, const Vec3V extents) const + { + const Vec3V iExt = TInflate ? V3Add(extents, mInflation) : extents; + + // coordinate axes + const Vec3V nodeMax = V3Add(center, iExt); + const Vec3V nodeMin = V3Sub(center, iExt); + + // cross axes + const Vec3V offset = V3Sub(mOrigin, center); + const Vec3V offsetYZX = V3PermYZX(offset); + const Vec3V iExtYZX = V3PermYZX(iExt); + + const Vec3V f = V3NegMulSub(mDirYZX, offset, V3Mul(mDir, offsetYZX)); + const Vec3V g = V3MulAdd(iExt, mAbsDirYZX, V3Mul(iExtYZX, mAbsDir)); + + const BoolV + maskA = V3IsGrtrOrEq(nodeMax, mRayMin), + maskB = V3IsGrtrOrEq(mRayMax, nodeMin), + maskC = V3IsGrtrOrEq(g, V3Abs(f)); + const BoolV andABCMasks = BAnd(BAnd(maskA, maskB), maskC); + + return BAllEqTTTT(andABCMasks); + } + + const Vec3V mOrigin, mDir, mDirYZX, mInflation, mAbsDir, mAbsDirYZX; + Vec3V mRayMin, mRayMax; +protected: + RayAABBTest& operator=(const RayAABBTest&); +}; + +// probably not worth having a SIMD version of this unless the traversal passes Vec3Vs +struct AABBAABBTest +{ + PX_FORCE_INLINE AABBAABBTest(const PxTransform&t, const PxBoxGeometry&b) + : mCenter(V3LoadU(t.p)) + , mExtents(V3LoadU(b.halfExtents)) + { } + + PX_FORCE_INLINE AABBAABBTest(const PxBounds3& b) + : mCenter(V3LoadU(b.getCenter())) + , mExtents(V3LoadU(b.getExtents())) + { } + + PX_FORCE_INLINE Ps::IntBool operator()(const Vec3V center, const Vec3V extents) const + { + //PxVec3 c; PxVec3_From_Vec3V(center, c); + //PxVec3 e; PxVec3_From_Vec3V(extents, e); + //if(PxAbs(c.x - mCenter.x) > mExtents.x + e.x) return Ps::IntFalse; + //if(PxAbs(c.y - mCenter.y) > mExtents.y + e.y) return Ps::IntFalse; + //if(PxAbs(c.z - mCenter.z) > mExtents.z + e.z) return Ps::IntFalse; + //return Ps::IntTrue; + return Ps::IntBool(V3AllGrtrOrEq(V3Add(mExtents, extents), V3Abs(V3Sub(center, mCenter)))); + } + +private: + AABBAABBTest& operator=(const AABBAABBTest&); + const Vec3V mCenter, mExtents; +}; + +struct SphereAABBTest +{ + PX_FORCE_INLINE SphereAABBTest(const PxTransform& t, const PxSphereGeometry& s) + : mCenter(V3LoadU(t.p)) + , mRadius2(FLoad(s.radius * s.radius)) + {} + + PX_FORCE_INLINE SphereAABBTest(const PxVec3& center, PxF32 radius) + : mCenter(V3LoadU(center)) + , mRadius2(FLoad(radius * radius)) + {} + + PX_FORCE_INLINE Ps::IntBool operator()(const Vec3V boxCenter, const Vec3V boxExtents) const + { + const Vec3V offset = V3Sub(mCenter, boxCenter); + const Vec3V closest = V3Clamp(offset, V3Neg(boxExtents), boxExtents); + const Vec3V d = V3Sub(offset, closest); + return Ps::IntBool(BAllEqTTTT(FIsGrtrOrEq(mRadius2, V3Dot(d, d)))); + } + +private: + SphereAABBTest& operator=(const SphereAABBTest&); + const Vec3V mCenter; + const FloatV mRadius2; +}; + +// The Opcode capsule-AABB traversal test seems to be *exactly* the same as the ray-box test inflated by the capsule radius (so not a true capsule/box test) +// and the code for the ray-box test is better. TODO: check the zero length case and use the sphere traversal if this one fails. +// (OTOH it's not that hard to adapt the Ray-AABB test to a capsule test) + +struct CapsuleAABBTest: private RayAABBTest +{ + PX_FORCE_INLINE CapsuleAABBTest(const PxVec3& origin, const PxVec3& unitDir, const PxReal length, const PxVec3& inflation) + : RayAABBTest(origin, unitDir, length, inflation) + {} + + PX_FORCE_INLINE Ps::IntBool operator()(const Vec3VArg center, const Vec3VArg extents) const + { + return Ps::IntBool(RayAABBTest::check(center, extents)); + } +}; + +template +struct OBBAABBTests +{ + OBBAABBTests(const PxVec3& pos, const PxMat33& rot, const PxVec3& halfExtentsInflated) + { + const Vec3V eps = V3Load(1e-6f); + + mT = V3LoadU(pos); + mExtents = V3LoadU(halfExtentsInflated); + + // storing the transpose matrices yields a simpler SIMD test + mRT = Mat33V_From_PxMat33(rot.getTranspose()); + mART = Mat33V(V3Add(V3Abs(mRT.col0), eps), V3Add(V3Abs(mRT.col1), eps), V3Add(V3Abs(mRT.col2), eps)); + mBB_xyz = M33TrnspsMulV3(mART, mExtents); + + if(fullTest) + { + const Vec3V eYZX = V3PermYZX(mExtents), eZXY = V3PermZXY(mExtents); + + mBB_123 = V3MulAdd(eYZX, V3PermZXY(mART.col0), V3Mul(eZXY, V3PermYZX(mART.col0))); + mBB_456 = V3MulAdd(eYZX, V3PermZXY(mART.col1), V3Mul(eZXY, V3PermYZX(mART.col1))); + mBB_789 = V3MulAdd(eYZX, V3PermZXY(mART.col2), V3Mul(eZXY, V3PermYZX(mART.col2))); + } + } + + // TODO: force inline it? + Ps::IntBool operator()(const Vec3V center, const Vec3V extents) const + { + const Vec3V t = V3Sub(mT, center); + + // class I - axes of AABB + if(V3OutOfBounds(t, V3Add(extents, mBB_xyz))) + return Ps::IntFalse; + + const Vec3V rX = mRT.col0, rY = mRT.col1, rZ = mRT.col2; + const Vec3V arX = mART.col0, arY = mART.col1, arZ = mART.col2; + + const FloatV eX = V3GetX(extents), eY = V3GetY(extents), eZ = V3GetZ(extents); + const FloatV tX = V3GetX(t), tY = V3GetY(t), tZ = V3GetZ(t); + + // class II - axes of OBB + { + const Vec3V v = V3ScaleAdd(rZ, tZ, V3ScaleAdd(rY, tY, V3Scale(rX, tX))); + const Vec3V v2 = V3ScaleAdd(arZ, eZ, V3ScaleAdd(arY, eY, V3ScaleAdd(arX, eX, mExtents))); + if(V3OutOfBounds(v, v2)) + return Ps::IntFalse; + } + + if(!fullTest) + return Ps::IntTrue; + + // class III - edge cross products. Almost all OBB tests early-out with type I or type II, + // so early-outs here probably aren't useful (TODO: profile) + + const Vec3V va = V3NegScaleSub(rZ, tY, V3Scale(rY, tZ)); + const Vec3V va2 = V3ScaleAdd(arY, eZ, V3ScaleAdd(arZ, eY, mBB_123)); + const BoolV ba = BOr(V3IsGrtr(va, va2), V3IsGrtr(V3Neg(va2), va)); + + const Vec3V vb = V3NegScaleSub(rX, tZ, V3Scale(rZ, tX)); + const Vec3V vb2 = V3ScaleAdd(arX, eZ, V3ScaleAdd(arZ, eX, mBB_456)); + const BoolV bb = BOr(V3IsGrtr(vb, vb2), V3IsGrtr(V3Neg(vb2), vb)); + + const Vec3V vc = V3NegScaleSub(rY, tX, V3Scale(rX, tY)); + const Vec3V vc2 = V3ScaleAdd(arX, eY, V3ScaleAdd(arY, eX, mBB_789)); + const BoolV bc = BOr(V3IsGrtr(vc, vc2), V3IsGrtr(V3Neg(vc2), vc)); + + return Ps::IntBool(BAllEqFFFF(BOr(ba, BOr(bb,bc)))); + } + + Vec3V mExtents; // extents of OBB + Vec3V mT; // translation of OBB + Mat33V mRT; // transpose of rotation matrix of OBB + Mat33V mART; // transpose of mRT, padded by epsilon + + Vec3V mBB_xyz; // extents of OBB along coordinate axes + Vec3V mBB_123; // projections of extents onto edge-cross axes + Vec3V mBB_456; + Vec3V mBB_789; +}; + +typedef OBBAABBTests OBBAABBTest; + +} +} +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuBounds.cpp b/src/PhysX/physx/source/geomutils/src/GuBounds.cpp new file mode 100644 index 000000000..38ee4c779 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBounds.cpp @@ -0,0 +1,611 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBounds.h" +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxTriangleMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "GuInternal.h" +#include "CmUtils.h" +#include "GuConvexMesh.h" +#include "GuConvexMeshData.h" +#include "GuTriangleMesh.h" +#include "GuHeightFieldData.h" +#include "GuHeightField.h" +#include "PsFoundation.h" +#include "GuConvexUtilsInternal.h" +#include "GuBoxConversion.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxMat33& rot, const PxVec3& pos, const CenterExtentsPadded& bounds) +{ + c = rot.transform(bounds.mCenter) + pos; + ext = Cm::basisExtent(rot.column0, rot.column1, rot.column2, bounds.mExtents); +} + +// PT: this one may have duplicates in GuBV4_BoxSweep_Internal.h & GuBV4_Raycast.cpp +static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33Padded& mat_Padded) +{ + Vec4V ResV = V4Scale(V4LoadU(&mat_Padded.column0.x), V4GetX(p)); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat_Padded.column1.x), V4GetY(p))); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat_Padded.column2.x), V4GetZ(p))); + return ResV; +} + +static PX_FORCE_INLINE void transformNoEmptyTestV(Vec3p& c, Vec3p& ext, const PxMat33Padded& rot, const PxVec3& pos, const CenterExtentsPadded& bounds) +{ + const Vec4V boundsCenterV = V4LoadU(&bounds.mCenter.x); // PT: this load is safe since extents follow center in the class + + // PT: unfortunately we can't V4LoadU 'pos' directly (it can come directly from users!). So we have to live with this for now: + const Vec4V posV = Vec4V_From_Vec3V(V3LoadU(&pos.x)); + // PT: but eventually we'd like to use the "unsafe" version (e.g. by switching p&q in PxTransform), which would save 6 instructions on Win32 + const Vec4V cV = V4Add(multiply3x3V(boundsCenterV, rot), posV); +// const Vec4V cV = V4Add(multiply3x3V(boundsCenterV, rot), V4LoadU(&pos.x)); // ### unsafe + V4StoreU(cV, &c.x); + + // extended basis vectors + const Vec4V boundsExtentsV = V4LoadU(&bounds.mExtents.x); // PT: this load is safe since bounds are padded + const Vec4V c0V = V4Scale(V4LoadU(&rot.column0.x), V4GetX(boundsExtentsV)); + const Vec4V c1V = V4Scale(V4LoadU(&rot.column1.x), V4GetY(boundsExtentsV)); + const Vec4V c2V = V4Scale(V4LoadU(&rot.column2.x), V4GetZ(boundsExtentsV)); + + // find combination of base vectors that produces max. distance for each component = sum of abs() + Vec4V extentsV = V4Add(V4Abs(c0V), V4Abs(c1V)); + extentsV = V4Add(extentsV, V4Abs(c2V)); + V4StoreU(extentsV, &ext.x); +} + +static PX_FORCE_INLINE PxU32 isNonIdentity(const PxVec3& scale) +{ + #define IEEE_1_0 0x3f800000 //!< integer representation of 1.0 + const PxU32* binary = reinterpret_cast(&scale.x); + return (binary[0] - IEEE_1_0)|(binary[1] - IEEE_1_0)|(binary[2] - IEEE_1_0); +} + +// PT: please don't inline this one - 300+ lines of rarely used code +static void computeScaledMatrix(PxMat33Padded& rot, const PxMeshScale& scale) +{ + rot = rot * scale.toMat33(); +} + +static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxTransform& transform, const PxMeshScale& scale, const CenterExtentsPadded& bounds) +{ + PxMat33Padded rot(transform.q); + + if(isNonIdentity(scale.scale)) + computeScaledMatrix(rot, scale); + + transformNoEmptyTestV(c, ext, rot, transform.p, bounds); +} + +static PX_FORCE_INLINE void transformNoEmptyTest(Vec3p& c, Vec3p& ext, const PxVec3& pos, const PxMat33Padded& rot, const PxMeshScale& scale, const CenterExtentsPadded& bounds) +{ + if(scale.isIdentity()) + transformNoEmptyTest(c, ext, rot, pos, bounds); + else + transformNoEmptyTest(c, ext, rot * scale.toMat33(), pos, bounds); +} + +static void computeMeshBounds(const PxTransform& pose, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, const PxMeshScale& meshScale, Vec3p& origin, Vec3p& extent) +{ + transformNoEmptyTest(origin, extent, pose, meshScale, *localSpaceBounds); +} + +static void computePlaneBounds(PxBounds3& bounds, const PxTransform& pose, float contactOffset, float inflation) +{ + // PT: A plane is infinite, so usually the bounding box covers the whole world. + // Now, in particular cases when the plane is axis-aligned, we can take + // advantage of this to compute a smaller bounding box anyway. + + // PT: we use PX_MAX_BOUNDS_EXTENTS to be compatible with PxBounds3::setMaximal, + // and to make sure that the value doesn't collide with the BP's sentinels. + const PxF32 bigValue = PX_MAX_BOUNDS_EXTENTS; +// const PxF32 bigValue = 1000000.0f; + PxVec3 minPt = PxVec3(-bigValue, -bigValue, -bigValue); + PxVec3 maxPt = PxVec3(bigValue, bigValue, bigValue); + + const PxVec3 planeNormal = pose.q.getBasisVector0(); + const PxPlane plane(pose.p, planeNormal); + + const float nx = PxAbs(planeNormal.x); + const float ny = PxAbs(planeNormal.y); + const float nz = PxAbs(planeNormal.z); + const float epsilon = 1e-6f; + const float oneMinusEpsilon = 1.0f - epsilon; + if(nx>oneMinusEpsilon && ny0.0f) maxPt.x = -plane.d + contactOffset; + else minPt.x = plane.d - contactOffset; + } + else if(nxoneMinusEpsilon && nz0.0f) maxPt.y = -plane.d + contactOffset; + else minPt.y = plane.d - contactOffset; + } + else if(nxoneMinusEpsilon) + { + if(planeNormal.z>0.0f) maxPt.z = -plane.d + contactOffset; + else minPt.z = plane.d - contactOffset; + } + + // PT: it is important to compute the min/max form directly without going through the + // center/extents intermediate form. With PX_MAX_BOUNDS_EXTENTS, those back-and-forth + // computations destroy accuracy. + + // PT: inflation actually destroys the bounds really. We keep it to please UTs but this is broken (DE10595). + // (e.g. for SQ 1% of PX_MAX_BOUNDS_EXTENTS is still a huge number, effectively making the AABB infinite and defeating the point of the above computation) + if(inflation!=1.0f) + { + const PxVec3 c = (maxPt + minPt)*0.5f; + const PxVec3 e = (maxPt - minPt)*0.5f*inflation; + minPt = c - e; + maxPt = c + e; + } + + bounds.minimum = minPt; + bounds.maximum = maxPt; +} + +static PX_FORCE_INLINE void inflateBounds(PxBounds3& bounds, const Vec3p& origin, const Vec3p& extents, float contactOffset, float inflation) +{ + Vec4V extentsV = V4LoadU(&extents.x); + extentsV = V4Add(extentsV, V4Load(contactOffset)); + extentsV = V4Scale(extentsV, FLoad(inflation)); + + const Vec4V originV = V4LoadU(&origin.x); + const Vec4V minV = V4Sub(originV, extentsV); + const Vec4V maxV = V4Add(originV, extentsV); + + StoreBounds(bounds, minV, maxV); +} + +static PX_FORCE_INLINE Vec4V basisExtentV(const PxMat33Padded& basis, const PxVec3& extent, float offset, float inflation) +{ + // extended basis vectors + const Vec4V c0V = V4Scale(V4LoadU(&basis.column0.x), FLoad(extent.x)); + const Vec4V c1V = V4Scale(V4LoadU(&basis.column1.x), FLoad(extent.y)); + const Vec4V c2V = V4Scale(V4LoadU(&basis.column2.x), FLoad(extent.z)); + + // find combination of base vectors that produces max. distance for each component = sum of abs() + Vec4V extentsV = V4Add(V4Abs(c0V), V4Abs(c1V)); + extentsV = V4Add(extentsV, V4Abs(c2V)); + extentsV = V4Add(extentsV, V4Load(offset)); + extentsV = V4Scale(extentsV, FLoad(inflation)); + return extentsV; +} + +void Gu::computeBounds(PxBounds3& bounds, const PxGeometry& geometry, const PxTransform& pose, float contactOffset, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, float inflation) +{ + PX_ASSERT(contactOffset==0.0f || inflation==1.0f); + + // Box, Convex, Mesh and HeightField will compute local bounds and pose to world space. + // Sphere, Capsule & Plane will compute world space bounds directly. + + switch(geometry.getType()) + { + case PxGeometryType::eSPHERE: + { + PX_ASSERT(!localSpaceBounds); + + const PxSphereGeometry& shape = static_cast(geometry); + const PxVec3 extents((shape.radius+contactOffset)*inflation); + bounds.minimum = pose.p - extents; + bounds.maximum = pose.p + extents; + } + break; + + case PxGeometryType::ePLANE: + { + PX_ASSERT(!localSpaceBounds); + + computePlaneBounds(bounds, pose, contactOffset, inflation); + } + break; + + case PxGeometryType::eCAPSULE: + { + PX_ASSERT(!localSpaceBounds); + + const PxCapsuleGeometry& shape = static_cast(geometry); + const PxVec3 d = pose.q.getBasisVector0(); + PxVec3 extents; + for(PxU32 ax = 0; ax<3; ax++) + extents[ax] = (PxAbs(d[ax]) * shape.halfHeight + shape.radius + contactOffset)*inflation; + bounds.minimum = pose.p - extents; + bounds.maximum = pose.p + extents; + } + break; + + case PxGeometryType::eBOX: + { + PX_ASSERT(!localSpaceBounds); + + const PxBoxGeometry& shape = static_cast(geometry); + + const Vec3p origin(pose.p); + + const PxMat33Padded basis(pose.q); + + const Vec4V extentsV = basisExtentV(basis, shape.halfExtents, contactOffset, inflation); + + const Vec4V originV = V4LoadU(&origin.x); + const Vec4V minV = V4Sub(originV, extentsV); + const Vec4V maxV = V4Add(originV, extentsV); + + StoreBounds(bounds, minV, maxV); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& shape = static_cast(geometry); + const Gu::ConvexHullData& hullData = static_cast(shape.convexMesh)->getHull(); + + const bool useTightBounds = shape.meshFlags & PxConvexMeshGeometryFlag::eTIGHT_BOUNDS; + if(useTightBounds) + { + PxMat33Padded rot(pose.q); + + if(isNonIdentity(shape.scale.scale)) + computeScaledMatrix(rot, shape.scale); + + PxU32 nb = hullData.mNbHullVertices; + const PxVec3* v = hullData.getHullVertices(); + Vec4V minV; + Vec4V maxV; + + { + const Vec4V vertexV = multiply3x3V(V4LoadU(&v->x), rot); + v++; + + minV = vertexV; + maxV = vertexV; + nb--; + } + + while(nb--) + { + const Vec4V vertexV = multiply3x3V(V4LoadU(&v->x), rot); + v++; + + minV = V4Min(minV, vertexV); + maxV = V4Max(maxV, vertexV); + } + + const Vec4V offsetV = V4Load(contactOffset); + minV = V4Sub(minV, offsetV); + maxV = V4Add(maxV, offsetV); + + const Vec4V posV = Vec4V_From_Vec3V(V3LoadU(&pose.p.x)); + maxV = V4Add(maxV, posV); + minV = V4Add(minV, posV); + + // Inflation + { + const Vec4V centerV = V4Scale(V4Add(maxV, minV), FLoad(0.5f)); + const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), FLoad(0.5f*inflation)); + maxV = V4Add(centerV, extentsV); + minV = V4Sub(centerV, extentsV); + } + + StoreBounds(bounds, minV, maxV); + } + else + { + Vec3p origin, extents; + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &hullData.getPaddedBounds(), shape.scale, origin, extents); + + inflateBounds(bounds, origin, extents, contactOffset, inflation); + } + } + break; + + case PxGeometryType::eTRIANGLEMESH: + { + Vec3p origin, extents; + const PxTriangleMeshGeometry& shape = static_cast(geometry); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &static_cast(shape.triangleMesh)->getPaddedBounds(), shape.scale, origin, extents); + + inflateBounds(bounds, origin, extents, contactOffset, inflation); + } + break; + + case PxGeometryType::eHEIGHTFIELD: + { + const PxHeightFieldGeometry& shape = static_cast(geometry); + const PxMeshScale scale(PxVec3(shape.rowScale, shape.heightScale, shape.columnScale), PxQuat(PxIdentity)); + + if(!localSpaceBounds) + localSpaceBounds = &static_cast(shape.heightField)->getData().getPaddedBounds(); + + //Compute and inflate the bounds from the pose, scale and center/extents. + Vec3p origin, extents; + computeMeshBounds(pose, localSpaceBounds, scale, origin, extents); + inflateBounds(bounds, origin, extents, contactOffset, inflation); + } + break; + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + PX_ASSERT(0); + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::GeometryUnion::computeBounds: Unknown shape type."); + } + } +} + +// PT: TODO: refactor this with regular function +PxF32 Gu::computeBoundsWithCCDThreshold(Vec3p& origin, Vec3p& extent, const PxGeometry& geometry, const PxTransform& pose, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds) +{ + // Box, Convex, Mesh and HeightField will compute local bounds and pose to world space. + // Sphere, Capsule & Plane will compute world space bounds directly. + + const PxReal inSphereRatio = 0.75f; + + //The CCD thresholds are as follows: + //(1) sphere = inSphereRatio * radius + //(2) plane = inf (we never need CCD against this shape) + //(3) capsule = inSphereRatio * radius + //(4) box = inSphereRatio * (box minimum extent axis) + //(5) convex = inSphereRatio * convex in-sphere * min scale + //(6) triangle mesh = 0.f (polygons have 0 thickness) + //(7) heightfields = 0.f (polygons have 0 thickness) + + //The decision to enter CCD depends on the sum of the shapes' CCD thresholds. One of the 2 shapes must be a + //sphere/capsule/box/convex so the sum of the CCD thresholds will be non-zero. + + switch (geometry.getType()) + { + case PxGeometryType::eSPHERE: + { + PX_ASSERT(!localSpaceBounds); + + const PxSphereGeometry& shape = static_cast(geometry); + origin = pose.p; + extent = PxVec3(shape.radius, shape.radius, shape.radius); + return shape.radius*inSphereRatio; + } + case PxGeometryType::ePLANE: + { + PX_ASSERT(!localSpaceBounds); + + PxBounds3 bounds; + computePlaneBounds(bounds, pose, 0.0f, 1.0f); + origin = bounds.getCenter(); + extent = bounds.getExtents(); + return PX_MAX_REAL; + } + case PxGeometryType::eCAPSULE: + { + PX_ASSERT(!localSpaceBounds); + + const PxCapsuleGeometry& shape = static_cast(geometry); + origin = pose.p; + const PxVec3 d = pose.q.getBasisVector0(); + for(PxU32 ax = 0; ax<3; ax++) + extent[ax] = PxAbs(d[ax]) * shape.halfHeight + shape.radius; + return shape.radius * inSphereRatio; + } + + case PxGeometryType::eBOX: + { + PX_ASSERT(!localSpaceBounds); + + const PxBoxGeometry& shape = static_cast(geometry); + + const PxMat33 rot(pose.q); + extent = Cm::basisExtent(rot.column0, rot.column1, rot.column2, shape.halfExtents); + + origin = pose.p; + + return PxMin(PxMin(shape.halfExtents.x, shape.halfExtents.y), shape.halfExtents.z)*inSphereRatio; + } + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& shape = static_cast(geometry); + const Gu::ConvexHullData& hullData = static_cast(shape.convexMesh)->getHull(); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &hullData.getPaddedBounds(), shape.scale, origin, extent); + return PxMin(shape.scale.scale.z, PxMin(shape.scale.scale.x, shape.scale.scale.y)) * hullData.mInternal.mRadius * inSphereRatio; + } + + case PxGeometryType::eTRIANGLEMESH: + { + const PxTriangleMeshGeometry& shape = static_cast(geometry); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &static_cast(shape.triangleMesh)->getPaddedBounds(), shape.scale, origin, extent); + return 0.0f; + } + + case PxGeometryType::eHEIGHTFIELD: + { + const PxHeightFieldGeometry& shape = static_cast(geometry); + const PxMeshScale scale(PxVec3(shape.rowScale, shape.heightScale, shape.columnScale), PxQuat(PxIdentity)); + const Gu::HeightFieldData& data = static_cast(shape.heightField)->getData(); + computeMeshBounds(pose, localSpaceBounds ? localSpaceBounds : &data.getPaddedBounds(), scale, origin, extent); + return 0.f; + } + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + PX_ASSERT(0); + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::GeometryUnion::computeBounds: Unknown shape type."); + } + } + return PX_MAX_REAL; +} + + +static PX_FORCE_INLINE void computeBoxExtentsAroundCapsule(PxVec3& extents, const PxCapsuleGeometry& capsuleGeom, float inflation) +{ + extents.x = (capsuleGeom.radius + capsuleGeom.halfHeight) * inflation; + extents.y = capsuleGeom.radius * inflation; + extents.z = capsuleGeom.radius * inflation; +} + +static const PxReal SQ_PRUNER_INFLATION = 1.01f; // pruner test shape inflation (not narrow phase shape) + +static void computeMeshBounds(const PxVec3& pos, const PxMat33Padded& rot, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, const PxMeshScale& meshScale, Vec3p& origin, Vec3p& extent) +{ + Ps::prefetchLine(localSpaceBounds); // PT: this one helps reducing L2 misses in transformNoEmptyTest + transformNoEmptyTest(origin, extent, pos, rot, meshScale, *localSpaceBounds); +} + +// PT: warning: this writes 4 bytes after the end of 'bounds'. Calling code must ensure it is safe to do so. +static PX_FORCE_INLINE void computeMinMaxBounds(PxBounds3* PX_RESTRICT bounds, const Vec3p& c, const Vec3p& e, float prunerInflation, float offset) +{ + const Vec4V extentsV = V4Scale(V4Add(V4LoadU(&e.x), V4Load(offset)), FLoad(prunerInflation)); + const Vec4V centerV = V4LoadU(&c.x); + const Vec4V minV = V4Sub(centerV, extentsV); + const Vec4V maxV = V4Add(centerV, extentsV); + V4StoreU(minV, &bounds->minimum.x); + V4StoreU(maxV, &bounds->maximum.x); +} + +ShapeData::ShapeData(const PxGeometry& g, const PxTransform& t, PxReal inflation) +{ + using namespace physx::shdfnd::aos; + + // PT: this cast to matrix is already done in GeometryUnion::computeBounds (e.g. for boxes). So we do it first, + // then we'll pass the matrix directly to computeBoundsShapeData, to avoid the double conversion. + const bool isOBB = PxAbs(t.q.w) < 0.999999f; + if(isOBB) + { + // PT: writes 4 bytes after 'rot' but it's safe since we then write 'center' just afterwards + buildFrom(mGuBox, t.q); + } + else + { + mGuBox.rot = PxMat33(PxIdentity); + } + + // PT: can't use V4Load here since there's no guarantee on 't.p' + // PT: must store 'center' after 'rot' now + mGuBox.center = t.p; + + // Compute AABB, used by the BucketPruner as cullBox + switch(g.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& shape = static_cast(g); + computeMinMaxBounds(&mPrunerInflatedAABB, mGuBox.center, PxVec3(0.0f), SQ_PRUNER_INFLATION, shape.radius+inflation); + + // + + reinterpret_cast(mGuSphere) = Sphere(t.p, shape.radius); + } + break; + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& shape = static_cast(g); + const Vec3p extents = mGuBox.rot.column0.abs() * shape.halfHeight; + computeMinMaxBounds(&mPrunerInflatedAABB, mGuBox.center, extents, SQ_PRUNER_INFLATION, shape.radius+inflation); + + // + + Capsule& dstWorldCapsule = reinterpret_cast(mGuCapsule); // store a narrow phase version copy + getCapsule(dstWorldCapsule, shape, t); + + mGuBox.extents.x = shape.halfHeight; + + // compute PxBoxGeometry pruner geom around input capsule geom; transform remains unchanged + + computeBoxExtentsAroundCapsule(mPrunerBoxGeomExtents, shape, SQ_PRUNER_INFLATION); + } + break; + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& shape = static_cast(g); + // PT: cast is safe because 'rot' followed by other members + Vec4V extentsV = basisExtentV(static_cast(mGuBox.rot), shape.halfExtents, inflation, SQ_PRUNER_INFLATION); + + // PT: c/e-to-m/M conversion + const Vec4V centerV = V4LoadU(&mGuBox.center.x); + const Vec4V minV = V4Sub(centerV, extentsV); + const Vec4V maxV = V4Add(centerV, extentsV); + V4StoreU(minV, &mPrunerInflatedAABB.minimum.x); + V4StoreU(maxV, &mPrunerInflatedAABB.maximum.x); // PT: WARNING: writes past end of class + + // + + mGuBox.extents = shape.halfExtents; // PT: TODO: use SIMD + mPrunerBoxGeomExtents = shape.halfExtents*SQ_PRUNER_INFLATION; + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& shape = static_cast(g); + + const ConvexMesh* cm = static_cast(shape.convexMesh); + const ConvexHullData* hullData = &cm->getHull(); + + // PT: cast is safe since 'rot' is followed by other members of the box + Vec3p center, extents; + computeMeshBounds(mGuBox.center, static_cast(mGuBox.rot), &hullData->getPaddedBounds(), shape.scale, center, extents); + + computeMinMaxBounds(&mPrunerInflatedAABB, center, extents, SQ_PRUNER_INFLATION, inflation); + + // + + Box prunerBox; + computeOBBAroundConvex(prunerBox, shape, cm, t); + mGuBox.rot = prunerBox.rot; // PT: TODO: optimize this copy + + // AP: pruners are now responsible for growing the OBB by 1% for overlap/sweep/GJK accuracy + mPrunerBoxGeomExtents = prunerBox.extents*SQ_PRUNER_INFLATION; + mGuBox.center = prunerBox.center; + } + break; + + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("PhysX internal error: Invalid shape in ShapeData contructor."); + } + + // PT: WARNING: these writes must stay after the above code + mIsOBB = PxU32(isOBB); + mType = PxU16(g.getType()); +} + + + + diff --git a/src/PhysX/physx/source/geomutils/src/GuBounds.h b/src/PhysX/physx/source/geomutils/src/GuBounds.h new file mode 100644 index 000000000..6e8293f05 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBounds.h @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BOUNDS_H +#define GU_BOUNDS_H + +#include "foundation/PxBounds3.h" +#include "foundation/PxFlags.h" +#include "GuSIMDHelpers.h" +#include +#include "PxGeometry.h" +#include "GuBox.h" +#include "GuCenterExtents.h" +#include "GuSphere.h" +#include "GuCapsule.h" + +namespace physx +{ +class PxGeometry; + +namespace Gu +{ + +//For spheres, planes, capsules and boxes just set localSpaceBounds to NULL. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to the relevant pointer if it has already been pre-fetched. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to NULL if it has not already been pre-fetched. computeBounds will synchronously +//prefetch the local space bounds if localSpaceBounds is NULL. +//'contactOffset' and 'inflation' should not be used at the same time, i.e. either contactOffset==0.0f, or inflation==1.0f +PX_PHYSX_COMMON_API void computeBounds(PxBounds3& bounds, const PxGeometry& geometry, const PxTransform& transform, float contactOffset, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds, float inflation); //AABB in world space. + +//For spheres, planes, capsules and boxes just set localSpaceBounds to NULL. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to the relevant pointer if it has not already been pre-fetched. +//For convex meshes, triangle meshes, and heightfields set localSpaceBounds to NULL if it has not already been pre-fetched. computeBounds will synchronously +//prefetch the local space bounds if localSpaceBounds is NULL. +PX_PHYSX_COMMON_API PxF32 computeBoundsWithCCDThreshold(Vec3p& origin, Vec3p& extent, const PxGeometry& geometry, const PxTransform& transform, const CenterExtentsPadded* PX_RESTRICT localSpaceBounds); //AABB in world space. + + +PX_FORCE_INLINE PxBounds3 computeBounds(const PxGeometry& geometry, const PxTransform& pose) +{ + PxBounds3 bounds; + computeBounds(bounds, geometry, pose, 0.0f, NULL, 1.0f); + return bounds; +} + +class ShapeData +{ +public: + + PX_PHYSX_COMMON_API ShapeData(const PxGeometry& g, const PxTransform& t, PxReal inflation); + + // PT: used by overlaps (box, capsule, convex) + PX_FORCE_INLINE const PxVec3& getPrunerBoxGeomExtentsInflated() const { return mPrunerBoxGeomExtents; } + + // PT: used by overlaps (box, capsule, convex) + PX_FORCE_INLINE const PxVec3& getPrunerWorldPos() const { return mGuBox.center; } + + PX_FORCE_INLINE const PxBounds3& getPrunerInflatedWorldAABB() const { return mPrunerInflatedAABB; } + + // PT: used by overlaps (box, capsule, convex) + PX_FORCE_INLINE const PxMat33& getPrunerWorldRot33() const { return mGuBox.rot; } + + // PT: this one only used by overlaps so far (for sphere shape, pruner level) + PX_FORCE_INLINE const Gu::Sphere& getGuSphere() const + { + PX_ASSERT(mType == PxGeometryType::eSPHERE); + return reinterpret_cast(mGuSphere); + } + + // PT: this one only used by sweeps so far (for box shape, NP level) + PX_FORCE_INLINE const Gu::Box& getGuBox() const + { + PX_ASSERT(mType == PxGeometryType::eBOX); + return mGuBox; + } + + // PT: this one used by sweeps (NP level) and overlaps (pruner level) - for capsule shape + PX_FORCE_INLINE const Gu::Capsule& getGuCapsule() const + { + PX_ASSERT(mType == PxGeometryType::eCAPSULE); + return reinterpret_cast(mGuCapsule); + } + + PX_FORCE_INLINE float getCapsuleHalfHeight() const + { + PX_ASSERT(mType == PxGeometryType::eCAPSULE); + return mGuBox.extents.x; + } + + PX_FORCE_INLINE PxU32 isOBB() const { return PxU32(mIsOBB); } + PX_FORCE_INLINE PxGeometryType::Enum getType() const { return PxGeometryType::Enum(mType); } + + PX_NOCOPY(ShapeData) +private: + + // PT: box: pre-inflated box extents + // capsule: pre-inflated extents of box-around-capsule + // convex: pre-inflated extents of box-around-convex + // sphere: not used + PxVec3 mPrunerBoxGeomExtents; // used for pruners. This volume encloses but can differ from the original shape + + // PT: + // + // box center = unchanged copy of initial shape's position, except for convex (position of box around convex) + // SIMD code will load it as a V4 (safe because member is not last of Gu structure) + // + // box rot = precomputed PxMat33 version of initial shape's rotation, except for convex (rotation of box around convex) + // SIMD code will load it as V4s (safe because member is not last of Gu structure) + // + // box extents = non-inflated initial box extents for box shape, half-height for capsule, otherwise not used + Gu::Box mGuBox; + + PxBounds3 mPrunerInflatedAABB; // precomputed AABB for the pruner shape + PxU16 mIsOBB; // true for OBB, false for AABB. Also used as padding for mPrunerInflatedAABB, don't move. + PxU16 mType; // shape's type + + // these union Gu shapes are only precomputed for narrow phase (not pruners), can be different from mPrunerVolume + // so need separate storage + union + { + PxU8 mGuCapsule[sizeof(Gu::Capsule)]; // 28 + PxU8 mGuSphere[sizeof(Gu::Sphere)]; // 16 + }; +}; + +// PT: please make sure it fits in "one" cache line +PX_COMPILE_TIME_ASSERT(sizeof(ShapeData)==128); + +} // namespace Gu + +} +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuBox.cpp b/src/PhysX/physx/source/geomutils/src/GuBox.cpp new file mode 100644 index 000000000..53c173708 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuBox.cpp @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsIntrinsics.h" +#include "GuBoxConversion.h" +#include "GuCapsule.h" +#include "GuInternal.h" +#include "CmMatrix34.h" +#include "PsMathUtils.h" + +using namespace physx; + +void Gu::Box::create(const Gu::Capsule& capsule) +{ + // Box center = center of the two LSS's endpoints + center = capsule.computeCenter(); + + // Box orientation + const PxVec3 dir = capsule.p1 - capsule.p0; + const float d = dir.magnitude(); + if(d!=0.0f) + { + rot.column0 = dir / d; + Ps::computeBasis(rot.column0, rot.column1, rot.column2); + } + else + rot = PxMat33(PxIdentity); + + // Box extents + extents.x = capsule.radius + (d * 0.5f); + extents.y = capsule.radius; + extents.z = capsule.radius; +} + + +/** +Returns edges. +\return 24 indices (12 edges) indexing the list returned by ComputePoints() +*/ +const PxU8* Gu::getBoxEdges() +{ + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + static PxU8 Indices[] = { + 0, 1, 1, 2, 2, 3, 3, 0, + 7, 6, 6, 5, 5, 4, 4, 7, + 1, 5, 6, 2, + 3, 7, 4, 0 + }; + return Indices; +} + + +void Gu::computeOBBPoints(PxVec3* PX_RESTRICT pts, const PxVec3& center, const PxVec3& extents, const PxVec3& base0, const PxVec3& base1, const PxVec3& base2) +{ + PX_ASSERT(pts); + + // "Rotated extents" + const PxVec3 axis0 = base0 * extents.x; + const PxVec3 axis1 = base1 * extents.y; + const PxVec3 axis2 = base2 * extents.z; + + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + // Original code: 24 vector ops + /* pts[0] = box.center - Axis0 - Axis1 - Axis2; + pts[1] = box.center + Axis0 - Axis1 - Axis2; + pts[2] = box.center + Axis0 + Axis1 - Axis2; + pts[3] = box.center - Axis0 + Axis1 - Axis2; + pts[4] = box.center - Axis0 - Axis1 + Axis2; + pts[5] = box.center + Axis0 - Axis1 + Axis2; + pts[6] = box.center + Axis0 + Axis1 + Axis2; + pts[7] = box.center - Axis0 + Axis1 + Axis2;*/ + + // Rewritten: 12 vector ops + pts[0] = pts[3] = pts[4] = pts[7] = center - axis0; + pts[1] = pts[2] = pts[5] = pts[6] = center + axis0; + + PxVec3 tmp = axis1 + axis2; + pts[0] -= tmp; + pts[1] -= tmp; + pts[6] += tmp; + pts[7] += tmp; + + tmp = axis1 - axis2; + pts[2] += tmp; + pts[3] += tmp; + pts[4] -= tmp; + pts[5] -= tmp; +} + diff --git a/src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp b/src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp new file mode 100644 index 000000000..08751a946 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp @@ -0,0 +1,440 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTests.h" +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "PxSphereGeometry.h" +#include "GuDistanceSegmentBox.h" +#include "GuDistancePointBox.h" +#include "GuSweepBoxSphere.h" +#include "GuSweepCapsuleBox.h" +#include "GuSweepBoxBox.h" +#include "GuSweepBoxTriangle_SAT.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" +#include "PsVecMath.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace Ps::aos; + +static const bool gValidateBoxRadiusComputation = false; + +/////////////////////////////////////////// + +bool sweepCapsule_BoxGeom_Precise(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_UNUSED(inflation); + PX_UNUSED(capsulePose_); + PX_UNUSED(capsuleGeom_); + + const PxBoxGeometry& boxGeom = static_cast(geom); + + if (lss.p0 == lss.p1) // The capsule is actually a sphere + { + //TODO: Check if this is really faster than using a "sphere-aware" version of sweepCapsuleBox + + Box box; buildFrom(box, pose.p, boxGeom.halfExtents, pose.q); + if(!sweepBoxSphere(box, lss.radius, lss.p0, unitDir, distance, sweepHit.distance, sweepHit.normal, hitFlags)) + return false; + + sweepHit.normal = -sweepHit.normal; + sweepHit.flags = PxHitFlag::eNORMAL; + + if(hitFlags & PxHitFlag::ePOSITION && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + const PxVec3 newSphereCenter = lss.p0 + unitDir * sweepHit.distance; + PxVec3 closest; + const PxReal d = distancePointBoxSquared(newSphereCenter, box.center, box.extents, box.rot, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + closest = box.rotate(closest); + sweepHit.position = closest + box.center; + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + else + { + if(!sweepCapsuleBox(lss, pose, boxGeom.halfExtents, unitDir, distance, sweepHit.position, sweepHit.distance, sweepHit.normal, hitFlags)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + + if((hitFlags & PxHitFlag::ePOSITION) && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + Capsule movedCaps = lss; + movedCaps.p0 += unitDir * sweepHit.distance; + movedCaps.p1 += unitDir * sweepHit.distance; + + Box box; + buildFrom(box, pose.p, boxGeom.halfExtents, pose.q); + + PxVec3 closest; + const PxReal d = distanceSegmentBoxSquared(movedCaps, box, NULL, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + closest = pose.q.rotate(closest); + sweepHit.position = closest + pose.p; + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sweepBox_SphereGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + const PxSphereGeometry& sphereGeom = static_cast(geom); + + // PT: move to relative space + const Box relBox(box.center - pose.p, box.extents, box.rot); + + const PxReal sphereRadius = sphereGeom.radius + inflation; + + if(!sweepBoxSphere(relBox, sphereRadius, PxVec3(0), -unitDir, distance, sweepHit.distance, sweepHit.normal, hitFlags)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + + if((hitFlags & PxHitFlag::ePOSITION) && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + const PxVec3 motion = sweepHit.distance * unitDir; + const PxVec3 newSphereCenter = - motion; + PxVec3 closest; + const PxReal d = distancePointBoxSquared(newSphereCenter, relBox.center, relBox.extents, relBox.rot, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + sweepHit.position = relBox.rotate(closest) + box.center + motion; // PT: undo move to local space here + sweepHit.flags |= PxHitFlag::ePOSITION; + } + return true; +} + +bool sweepBox_CapsuleGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + PX_UNUSED(inflation); + PX_UNUSED(boxGeom_); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + // PT: move to relative space + const PxVec3 delta = box.center - pose.p; + Box relBox(delta, box.extents, box.rot); + + Capsule capsule; + const PxVec3 halfHeightVector = getCapsuleHalfHeightVector(pose, capsuleGeom); + capsule.p0 = halfHeightVector; + capsule.p1 = -halfHeightVector; + capsule.radius = capsuleGeom.radius; + + // PT: TODO: remove this. We convert to PxTansform here but inside sweepCapsuleBox we convert back to a matrix. + const PxTransform boxWorldPose(delta, boxPose_.q); + + PxVec3 n; + if(!sweepCapsuleBox(capsule, boxWorldPose, relBox.extents, -unitDir, distance, sweepHit.position, sweepHit.distance, n, hitFlags)) + return false; + + sweepHit.normal = -n; + sweepHit.flags = PxHitFlag::eNORMAL; + + if((hitFlags & PxHitFlag::ePOSITION) && sweepHit.distance!=0.0f) + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + relBox.center += (unitDir * sweepHit.distance); + PxVec3 closest; + const PxReal d = distanceSegmentBoxSquared(capsule, relBox, NULL, &closest); + PX_UNUSED(d); + // Compute point on the box, after sweep + sweepHit.position = relBox.transform(closest) + pose.p; // PT: undo move to local space here + sweepHit.flags |= PxHitFlag::ePOSITION; + } + return true; +} + +bool sweepBox_BoxGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_UNUSED(inflation); + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + + const PxBoxGeometry& boxGeom = static_cast(geom); + + // PT: move to local space + const Box relBox(box.center - pose.p, box.extents, box.rot); + Box staticBox; buildFrom(staticBox, PxVec3(0), boxGeom.halfExtents, pose.q); + + if(!sweepBoxBox(relBox, staticBox, unitDir, distance, hitFlags, sweepHit)) + return false; + + if(sweepHit.distance!=0.0f) + sweepHit.position += pose.p; // PT: undo move to local space + return true; +} + +// PT: test: new version for CCT, based on code for general sweeps. Just to check it works or not with rotations +// TODO: refactor this and the similar code in sweptBox for box-vs-mesh. Not so easy though. +static bool sweepBoxVsTriangles(PxU32 nbTris, const PxTriangle* triangles, const Box& box, const PxVec3& unitDir, const PxReal distance, PxSweepHit& sweepHit, + PxHitFlags hitFlags, bool isDoubleSided, const PxU32* cachedIndex) +{ + if(!nbTris) + return false; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localMotion = localDir * distance; + + bool status = false; + sweepHit.distance = distance; //was PX_MAX_F32, but that may trigger an assert in the caller! + + const PxVec3 oneOverMotion( + localDir.x!=0.0f ? 1.0f/localMotion.x : 0.0f, + localDir.y!=0.0f ? 1.0f/localMotion.y : 0.0f, + localDir.z!=0.0f ? 1.0f/localMotion.z : 0.0f); + +// PT: experimental code, don't clean up before I test it more and validate it + +// Project box +/*float boxRadius0 = + PxAbs(dir.x) * box.extents.x + + PxAbs(dir.y) * box.extents.y + + PxAbs(dir.z) * box.extents.z;*/ + +float boxRadius = + PxAbs(localDir.x) * box.extents.x + + PxAbs(localDir.y) * box.extents.y + + PxAbs(localDir.z) * box.extents.z; + +if(gValidateBoxRadiusComputation) // PT: run this to check the box radius is correctly computed +{ + PxVec3 boxVertices2[8]; + box.computeBoxPoints(boxVertices2); + float dpmin = FLT_MAX; + float dpmax = -FLT_MAX; + for(int i=0;i<8;i++) + { + const float dp = boxVertices2[i].dot(unitDir); + if(dpdpmax) dpmax = dp; + } + const float goodRadius = (dpmax-dpmin)/2.0f; + PX_UNUSED(goodRadius); +} + +const float dpc0 = box.center.dot(unitDir); +float localMinDist = 1.0f; +#if PX_DEBUG + PxU32 totalTestsExpected = nbTris; + PxU32 totalTestsReal = 0; + PX_UNUSED(totalTestsExpected); + PX_UNUSED(totalTestsReal); +#endif + + const PxU32 idx = cachedIndex ? *cachedIndex : 0; + + PxVec3 bestTriNormal(0.0f); + + for(PxU32 ii=0;ii(geom); + + // Compute swept box + Box sweptBox; + computeSweptBox(sweptBox, box.extents, box.center, box.rot, unitDir, distance); + + //### Temp hack until we can directly collide the OBB against the HF + const PxTransform sweptBoxTR = sweptBox.getTransform(); + const PxBounds3 bounds = PxBounds3::poseExtent(sweptBoxTR, sweptBox.extents); + + sweepHit.distance = PX_MAX_F32; + + struct LocalReport : EntityReport + { + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + for(PxU32 i=0; igetTriangle(*mPose, currentTriangle, NULL, NULL, triangleIndex, true, true); + + PxSweepHit sweepHit_; + const bool b = sweepBoxVsTriangles(1, ¤tTriangle, mBox, mDir, mDist, sweepHit_, mHitFlags, mIsDoubleSided, NULL); + if(b && sweepHit_.distancedistance) + { + *mHit = sweepHit_; + mHit->faceIndex = triangleIndex; + mStatus = true; + } + } + return true; + } + + const HeightFieldUtil* mHFUtil; + const PxTransform* mPose; + PxSweepHit* mHit; + bool mStatus; + Box mBox; + PxVec3 mDir; + float mDist; + PxHitFlags mHitFlags; + bool mIsDoubleSided; + } myReport; + + HeightFieldUtil hfUtil(heightFieldGeom); + + myReport.mBox = box; + myReport.mDir = unitDir; + myReport.mDist = distance; + myReport.mHitFlags = hitFlags; + myReport.mHFUtil = &hfUtil; + myReport.mStatus = false; + myReport.mPose = &pose; + myReport.mHit = &sweepHit; + const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + myReport.mIsDoubleSided = (heightFieldGeom.heightFieldFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) || meshBothSides; + + hfUtil.overlapAABBTriangles(pose, bounds, GuHfQueryFlags::eWORLD_SPACE, &myReport); + + return myReport.mStatus; +} + +bool Gu::sweepBoxTriangles_Precise(GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxBoxGeometry)) +{ + PX_UNUSED(inflation); + + Box box; + buildFrom(box, pose.p, geom.halfExtents, pose.q); + + return sweepBoxVsTriangles(nbTris, triangles, box, unitDir, distance, hit, hitFlags, doubleSided, cachedIndex); +} diff --git a/src/PhysX/physx/source/geomutils/src/GuCapsule.cpp b/src/PhysX/physx/source/geomutils/src/GuCapsule.cpp new file mode 100644 index 000000000..f0572a9ea --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuCapsule.cpp @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "PsMathUtils.h" +#include "GuInternal.h" +#include "GuBox.h" +#include "GuCapsule.h" + +using namespace physx; + +/** +* Computes an OBB surrounding the capsule. +* \param box [out] the OBB +*/ +void Gu::computeBoxAroundCapsule(const Gu::Capsule& capsule, Gu::Box& box) +{ + // Box center = center of the two capsule's endpoints + box.center = capsule.computeCenter(); + + // Box extents + const PxF32 d = (capsule.p0 - capsule.p1).magnitude(); + box.extents.x = capsule.radius + (d * 0.5f); + box.extents.y = capsule.radius; + box.extents.z = capsule.radius; + + // Box orientation + if(d==0.0f) + { + box.rot = PxMat33(PxIdentity); + } + else + { + PxVec3 dir, right, up; + Ps::computeBasis(capsule.p0, capsule.p1, dir, right, up); + box.setAxes(dir, right, up); + } +} diff --git a/src/PhysX/physx/source/geomutils/src/GuCapsule.h b/src/PhysX/physx/source/geomutils/src/GuCapsule.h new file mode 100644 index 000000000..157bb9984 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuCapsule.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CAPSULE_H +#define GU_CAPSULE_H + +/** \addtogroup geomutils +@{ +*/ + +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + +/** +\brief Represents a capsule. +*/ + class Capsule : public Segment + { + public: + /** + \brief Constructor + */ + PX_INLINE Capsule() + { + } + + /** + \brief Constructor + + \param seg Line segment to create capsule from. + \param _radius Radius of the capsule. + */ + PX_INLINE Capsule(const Segment& seg, PxF32 _radius) : Segment(seg), radius(_radius) + { + } + + /** + \brief Constructor + + \param _p0 First segment point + \param _p1 Second segment point + \param _radius Radius of the capsule. + */ + PX_INLINE Capsule(const PxVec3& _p0, const PxVec3& _p1, PxF32 _radius) : Segment(_p0, _p1), radius(_radius) + { + } + + /** + \brief Destructor + */ + PX_INLINE ~Capsule() + { + } + + PxF32 radius; + }; +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuCenterExtents.h b/src/PhysX/physx/source/geomutils/src/GuCenterExtents.h new file mode 100644 index 000000000..f81e0a278 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuCenterExtents.h @@ -0,0 +1,131 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CENTER_EXTENTS_H +#define GU_CENTER_EXTENTS_H + +/** \addtogroup geomutils +@{ +*/ + +#include "CmMatrix34.h" +#include "CmUtils.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Gu +{ + class CenterExtents : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE CenterExtents() {} + PX_FORCE_INLINE CenterExtents(const PxBounds3& b) { mCenter = b.getCenter(); mExtents = b.getExtents(); } + PX_FORCE_INLINE ~CenterExtents() {} + + PX_FORCE_INLINE void getMin(PxVec3& min) const { min = mCenter - mExtents; } + PX_FORCE_INLINE void getMax(PxVec3& max) const { max = mCenter + mExtents; } + + PX_FORCE_INLINE float getMin(PxU32 axis) const { return mCenter[axis] - mExtents[axis]; } + PX_FORCE_INLINE float getMax(PxU32 axis) const { return mCenter[axis] + mExtents[axis]; } + + PX_FORCE_INLINE PxVec3 getMin() const { return mCenter - mExtents; } + PX_FORCE_INLINE PxVec3 getMax() const { return mCenter + mExtents; } + + PX_FORCE_INLINE void setMinMax(const PxVec3& min, const PxVec3& max) + { + mCenter = (max + min)*0.5f; + mExtents = (max - min)*0.5f; + } + + PX_FORCE_INLINE PxU32 isInside(const CenterExtents& box) const + { + if(box.getMin(0)>getMin(0)) return 0; + if(box.getMin(1)>getMin(1)) return 0; + if(box.getMin(2)>getMin(2)) return 0; + if(box.getMax(0)(geom); + if(!sphereGeom.isValid()) + return false; + break; + } + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + if(!capsuleGeom.isValid()) + return false; + break; + } + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast(geom); + if(!boxGeom.isValid()) + return false; + break; + } + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast(geom); + if(!convexGeom.isValid()) + return false; + break; + } + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + return true; +} + +bool PxGeometryQuery::sweep(const PxVec3& unitDir, const PxReal distance, + const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1, + PxSweepHit& sweepHit, PxHitFlags hitFlags, + const PxReal inflation) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "PxGeometryQuery::sweep(): pose0 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "PxGeometryQuery::sweep(): pose1 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "PxGeometryQuery::sweep(): unitDir is not valid.", false); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(distance), "PxGeometryQuery::sweep(): distance is not valid.", false); + PX_CHECK_AND_RETURN_VAL((distance >= 0.0f && !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) || distance > 0.0f, + "PxGeometryQuery::sweep(): sweep distance must be >=0 or >0 with eASSUME_NO_INITIAL_OVERLAP.", 0); +#if PX_CHECKED + if(!PxGeometryQuery::isValid(geom0)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry 0 is not valid"); + return false; + } + if(!PxGeometryQuery::isValid(geom1)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry 1 is not valid"); + return false; + } +#endif // PX_CHECKED + + const GeomSweepFuncs& sf = gGeomSweepFuncs; + + switch(geom0.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast(geom0); + + // PT: TODO: technically this capsule with 0.0 half-height is invalid ("isValid" returns false) + const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f); + + const Capsule worldCapsule(pose0.p, pose0.p, sphereGeom.radius); + + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()]; + + return func(geom1, pose1, capsuleGeom, pose0, worldCapsule, unitDir, distance, sweepHit, hitFlags, inflation); + } + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + + Capsule worldCapsule; + getCapsule(worldCapsule, capsuleGeom, pose0); + + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()]; + + return func(geom1, pose1, capsuleGeom, pose0, worldCapsule, unitDir, distance, sweepHit, hitFlags, inflation); + } + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast(geom0); + + Box box; + buildFrom(box, pose0.p, boxGeom.halfExtents, pose0.q); + + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepBoxFunc func = precise ? sf.preciseBoxMap[geom1.getType()] : sf.boxMap[geom1.getType()]; + + return func(geom1, pose1, boxGeom, pose0, box, unitDir, distance, sweepHit, hitFlags, inflation); + } + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + + const SweepConvexFunc func = sf.convexMap[geom1.getType()]; + + return func(geom1, pose1, convexGeom, pose0, unitDir, distance, sweepHit, hitFlags, inflation); + } + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_CHECK_MSG(false, "PxGeometryQuery::sweep(): first geometry object parameter must be sphere, capsule, box or convex geometry."); + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PxGeometryQuery::overlap( const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1) +{ + PX_SIMD_GUARD; + return Gu::overlap(geom0, pose0, geom1, pose1, gGeomOverlapMethodTable); +} + +/////////////////////////////////////////////////////////////////////////////// +PxU32 PxGeometryQuery::raycast( const PxVec3& rayOrigin, const PxVec3& rayDir, + const PxGeometry& geom, const PxTransform& pose, + PxReal maxDist, PxHitFlags hitFlags, + PxU32 maxHits, PxRaycastHit* PX_RESTRICT rayHits) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(rayDir.isFinite(), "PxGeometryQuery::raycast(): rayDir is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(rayOrigin.isFinite(), "PxGeometryQuery::raycast(): rayOrigin is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxGeometryQuery::raycast(): pose is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(maxDist >= 0.0f, "PxGeometryQuery::raycast(): maxDist is negative.", false); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(maxDist), "PxGeometryQuery::raycast(): maxDist is not valid.", false); + PX_CHECK_AND_RETURN_VAL(PxAbs(rayDir.magnitudeSquared()-1)<1e-4f, "PxGeometryQuery::raycast(): ray direction must be unit vector.", false); + + const RaycastFunc func = gRaycastMap[geom.getType()]; + return func(geom, pose, rayOrigin, rayDir, maxDist, hitFlags, maxHits, rayHits); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool pointConvexDistance(PxVec3& normal_, PxVec3& closestPoint_, PxReal& sqDistance, const PxVec3& pt, const ConvexMesh* convexMesh, const PxMeshScale& meshScale, const PxTransform& convexPose); + +PxReal PxGeometryQuery::pointDistance(const PxVec3& point, const PxGeometry& geom, const PxTransform& pose, PxVec3* closestPoint) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxGeometryQuery::pointDistance(): pose is not valid.", false); + + switch(geom.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast(geom); + + const PxReal r = sphereGeom.radius; + + PxVec3 delta = point - pose.p; + const PxReal d = delta.magnitude(); + if(d<=r) + return 0.0f; + + if(closestPoint) + { + delta /= d; + *closestPoint = pose.p + delta * r; + } + + return (d - r)*(d - r); + } + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsGeom = static_cast(geom); + + Capsule capsule; + getCapsule(capsule, capsGeom, pose); + + const PxReal r = capsGeom.radius; + + PxReal param; + const PxReal sqDistance = distancePointSegmentSquared(capsule, point, ¶m); + if(sqDistance<=r*r) + return 0.0f; + + const PxReal d = physx::intrinsics::sqrt(sqDistance); + + if(closestPoint) + { + const PxVec3 cp = capsule.getPointAt(param); + + PxVec3 delta = point - cp; + delta.normalize(); + + *closestPoint = cp + delta * r; + } + return (d - r)*(d - r); + } + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast(geom); + + Box obb; + buildFrom(obb, pose.p, boxGeom.halfExtents, pose.q); + + PxVec3 boxParam; + const PxReal sqDistance = distancePointBoxSquared(point, obb, &boxParam); + if(closestPoint && sqDistance!=0.0f) + { + *closestPoint = obb.transform(boxParam); + } + return sqDistance; + } + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast(geom); + + PxVec3 normal, cp; + PxReal sqDistance; + const bool intersect = pointConvexDistance(normal, cp, sqDistance, point, static_cast(convexGeom.convexMesh), convexGeom.scale, pose); + if(!intersect && closestPoint) + *closestPoint = cp; + return sqDistance; + } + case PxGeometryType::ePLANE: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_CHECK_MSG(false, "PxGeometryQuery::pointDistance(): geometry object parameter must be sphere, capsule box or convex geometry."); + break; + } + return -1.0f; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxBounds3 PxGeometryQuery::getWorldBounds(const PxGeometry& geom, const PxTransform& pose, float inflation) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxGeometryQuery::getWorldBounds(): pose is not valid.", PxBounds3::empty()); + + PxBounds3 bounds; + Gu::computeBounds(bounds, geom, pose, 0.0f, NULL, inflation); + PX_ASSERT(bounds.isValid()); + return bounds; +} + +/////////////////////////////////////////////////////////////////////////////// + +extern GeomMTDFunc gGeomMTDMethodTable[][PxGeometryType::eGEOMETRY_COUNT]; + +bool PxGeometryQuery::computePenetration( PxVec3& mtd, PxF32& depth, + const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "PxGeometryQuery::computePenetration(): pose0 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "PxGeometryQuery::computePenetration(): pose1 is not valid.", false); + + if(geom0.getType() > geom1.getType()) + { + GeomMTDFunc mtdFunc = gGeomMTDMethodTable[geom1.getType()][geom0.getType()]; + PX_ASSERT(mtdFunc); + if(!mtdFunc(mtd, depth, geom1, pose1, geom0, pose0)) + return false; + mtd = -mtd; + return true; + } + else + { + GeomMTDFunc mtdFunc = gGeomMTDMethodTable[geom0.getType()][geom1.getType()]; + PX_ASSERT(mtdFunc); + return mtdFunc(mtd, depth, geom0, pose0, geom1, pose1); + } +} diff --git a/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp new file mode 100644 index 000000000..69b74f74f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" + +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "GuHeightField.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +static PX_FORCE_INLINE Gu::ConvexMesh& getConvexMesh(PxConvexMesh* pxcm) +{ + return *static_cast(pxcm); +} + +static PX_FORCE_INLINE Gu::TriangleMesh& getTriangleMesh(PxTriangleMesh* pxtm) +{ + return *static_cast(pxtm); +} + +static PX_FORCE_INLINE Gu::HeightField& getHeightField(PxHeightField* pxhf) +{ + return *static_cast(pxhf); +} + +// PT: TODO: optimize all these data copies +void Gu::GeometryUnion::set(const PxGeometry& g) +{ + switch(g.getType()) + { + case PxGeometryType::eBOX: + { + reinterpret_cast(mGeometry) = static_cast(g); + } + break; + + case PxGeometryType::eCAPSULE: + { + reinterpret_cast(mGeometry) = static_cast(g); + } + break; + + case PxGeometryType::eSPHERE: + { + reinterpret_cast(mGeometry) = static_cast(g); + reinterpret_cast(mGeometry).halfHeight = 0.0f; //AM: make sphere geometry also castable as a zero height capsule. + } + break; + + case PxGeometryType::ePLANE: + { + reinterpret_cast(mGeometry) = static_cast(g); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + reinterpret_cast(mGeometry) = static_cast(g); + reinterpret_cast(mGeometry).hullData = &(::getConvexMesh(get().convexMesh).getHull()); + reinterpret_cast(mGeometry).gpuCompatible = ::getConvexMesh(get().convexMesh).isGpuCompatible(); + } + break; + + case PxGeometryType::eTRIANGLEMESH: + { + reinterpret_cast(mGeometry) = static_cast(g); + reinterpret_cast(mGeometry).meshData = &(::getTriangleMesh(get().triangleMesh)); + reinterpret_cast(mGeometry).materialIndices = (::getTriangleMesh(get().triangleMesh).getMaterials()); + reinterpret_cast(mGeometry).materials = MaterialIndicesStruct(); + } + break; + + case PxGeometryType::eHEIGHTFIELD: + { + reinterpret_cast(mGeometry) = static_cast(g); + reinterpret_cast(mGeometry).heightFieldData = &::getHeightField(get().heightField).getData(); + reinterpret_cast(mGeometry).materials = MaterialIndicesStruct(); + } + break; + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("geometry type not handled"); + break; + } +} + + + + diff --git a/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h new file mode 100644 index 000000000..621a2f8d1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h @@ -0,0 +1,246 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GEOMETRY_UNION_H +#define GU_GEOMETRY_UNION_H + +#include "foundation/PxBounds3.h" +#include "GuSIMDHelpers.h" +#include +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxTriangleMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "PsAllocator.h" +#include "GuBox.h" +#include "GuCenterExtents.h" +#include "GuSphere.h" +#include "GuCapsule.h" + +namespace physx +{ + +namespace Gu +{ + struct ConvexHullData; + class TriangleMesh; + struct HeightFieldData; + class GeometryUnion; +} + + +// +// Summary of our material approach: +// +// On the API level, materials are accessed via pointer. Internally we store indices into the material table. +// The material table is stored in the SDK and the materials are shared among scenes. To make this threadsafe, +// we have the following approach: +// +// - Every scene has a copy of the SDK master material table +// - At the beginning of a simulation step, the scene material table gets synced to the master material table. +// - While the simulation is running, the scene table does not get touched. +// - Each shape stores the indices of its material(s). When the simulation is not running and a user requests the +// materials of the shape, the indices are used to fetch the material from the master material table. When the +// the simulation is running then the same indices are used internally to fetch the materials from the scene +// material table. If a user changes the materials of a shape while the simulation is running, the index list +// will not get touched, instead the new materials get buffered and synced at the end of the simulation. +// - This whole scheme only works as long as the position of a material in the material table does not change +// when other materials get deleted/inserted. The data structure of the material table makes sure that is the case. +// + +struct PX_PHYSX_COMMON_API MaterialIndicesStruct +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +// PX_SERIALIZATION + MaterialIndicesStruct(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + MaterialIndicesStruct() : indices(NULL), numIndices(0) + { + } + + ~MaterialIndicesStruct() + { + } + + void allocate(PxU16 size) + { + indices = reinterpret_cast(PX_ALLOC(sizeof(PxU16) * size, "MaterialIndicesStruct::allocate")); + numIndices = size; + } + + void deallocate() + { + PX_FREE(indices); + numIndices = 0; + } + PxU16* indices; // the remap table for material index + PxU16 numIndices; // the size of the remap table + PxU16 pad; // pad for serialization +#if PX_P64_FAMILY + PxU32 pad64; // pad for serialization +#endif +}; + +struct PxConvexMeshGeometryLL: public PxConvexMeshGeometry +{ + const Gu::ConvexHullData* hullData; + bool gpuCompatible; +}; + +struct PxTriangleMeshGeometryLL: public PxTriangleMeshGeometry +{ + const Gu::TriangleMesh* meshData; + const PxU16* materialIndices; + MaterialIndicesStruct materials; +}; + +struct PxHeightFieldGeometryLL : public PxHeightFieldGeometry +{ + const Gu::HeightFieldData* heightFieldData; + MaterialIndicesStruct materials; +}; + +// We sometimes overload capsule code for spheres, so every sphere should have +// valid capsule data (height = 0). This is preferable to a typedef so that we +// can maintain traits separately for a sphere, but some care is required to deal +// with the fact that when a reference to a capsule is extracted, it may have its +// type field set to eSPHERE + +template +struct PxcGeometryTraits +{ + enum {TypeID = PxGeometryType::eINVALID }; +}; +template struct PxcGeometryTraits { enum { TypeID = PxcGeometryTraits::TypeID }; }; + +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eBOX }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eSPHERE }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eCAPSULE }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::ePLANE }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eCONVEXMESH }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eTRIANGLEMESH }; }; +template <> struct PxcGeometryTraits { enum { TypeID = PxGeometryType::eHEIGHTFIELD }; }; +template PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType(const Gu::GeometryUnion& geometry); +template<> PX_CUDA_CALLABLE PX_INLINE void checkType(const Gu::GeometryUnion& geometry); +template<> PX_CUDA_CALLABLE PX_INLINE void checkType(const Gu::GeometryUnion& geometry); + + +namespace Gu +{ + +class InvalidGeometry : public PxGeometry +{ +public: + PX_CUDA_CALLABLE PX_FORCE_INLINE InvalidGeometry() : PxGeometry(PxGeometryType::eINVALID) {} +}; + +class PX_PHYSX_COMMON_API GeometryUnion +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + GeometryUnion(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PX_CUDA_CALLABLE PX_FORCE_INLINE GeometryUnion() { reinterpret_cast(mGeometry) = InvalidGeometry(); } + PX_CUDA_CALLABLE PX_FORCE_INLINE GeometryUnion(const PxGeometry& g) { set(g); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxGeometry& getGeometry() const { return reinterpret_cast(mGeometry); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxGeometryType::Enum getType() const { return reinterpret_cast(mGeometry).getType(); } + + PX_CUDA_CALLABLE void set(const PxGeometry& g); + + template PX_CUDA_CALLABLE PX_FORCE_INLINE Geom& get() + { + checkType(*this); + return reinterpret_cast(mGeometry); + } + + template PX_CUDA_CALLABLE PX_FORCE_INLINE const Geom& get() const + { + checkType(*this); + return reinterpret_cast(mGeometry); + } + +private: + + union { + void* alignment; // PT: Makes sure the class is at least aligned to pointer size. See DE6803. + PxU8 box[sizeof(PxBoxGeometry)]; + PxU8 sphere[sizeof(PxSphereGeometry)]; + PxU8 capsule[sizeof(PxCapsuleGeometry)]; + PxU8 plane[sizeof(PxPlaneGeometry)]; + PxU8 convex[sizeof(PxConvexMeshGeometryLL)]; + PxU8 mesh[sizeof(PxTriangleMeshGeometryLL)]; + PxU8 heightfield[sizeof(PxHeightFieldGeometryLL)]; + PxU8 invalid[sizeof(InvalidGeometry)]; + } mGeometry; +}; +} + + +template PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType(const Gu::GeometryUnion& geometry) +{ + PX_ASSERT(PxU32(geometry.getType()) == PxU32(PxcGeometryTraits::TypeID)); + PX_UNUSED(geometry); +} + +template<> PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType(const Gu::GeometryUnion& geometry) +{ + PX_ASSERT(geometry.getType() == PxGeometryType::eCAPSULE || geometry.getType() == PxGeometryType::eSPHERE); + PX_UNUSED(geometry); +} + +template<> PX_CUDA_CALLABLE PX_FORCE_INLINE void checkType(const Gu::GeometryUnion& geometry) +{ + PX_ASSERT(geometry.getType()== PxGeometryType::eCAPSULE || geometry.getType() == PxGeometryType::eSPHERE); + PX_UNUSED(geometry); +} + +// the shape structure relies on punning capsules and spheres +PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(PxCapsuleGeometry, radius) == PX_OFFSET_OF(PxSphereGeometry, radius)); +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuInternal.cpp b/src/PhysX/physx/source/geomutils/src/GuInternal.cpp new file mode 100644 index 000000000..8cfca77c1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuInternal.cpp @@ -0,0 +1,159 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxBounds3.h" +#include "PsIntrinsics.h" +#include "GuInternal.h" +#include "GuBox.h" +#include "GuVecPlane.h" +#include "PsMathUtils.h" +#include "PxCapsuleGeometry.h" +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +using namespace physx; + +/** +Computes the aabb points. +\param pts [out] 8 box points +*/ +void Gu::computeBoxPoints(const PxBounds3& bounds, PxVec3* PX_RESTRICT pts) +{ + PX_ASSERT(pts); + + // Get box corners + const PxVec3& minimum = bounds.minimum; + const PxVec3& maximum = bounds.maximum; + + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + // Generate 8 corners of the bbox + pts[0] = PxVec3(minimum.x, minimum.y, minimum.z); + pts[1] = PxVec3(maximum.x, minimum.y, minimum.z); + pts[2] = PxVec3(maximum.x, maximum.y, minimum.z); + pts[3] = PxVec3(minimum.x, maximum.y, minimum.z); + pts[4] = PxVec3(minimum.x, minimum.y, maximum.z); + pts[5] = PxVec3(maximum.x, minimum.y, maximum.z); + pts[6] = PxVec3(maximum.x, maximum.y, maximum.z); + pts[7] = PxVec3(minimum.x, maximum.y, maximum.z); +} + +PxPlane Gu::getPlane(const PxTransform& pose) +{ + const PxVec3 n = pose.q.getBasisVector0(); + return PxPlane(n, -pose.p.dot(n)); +} + +void Gu::computeBoundsAroundVertices(PxBounds3& bounds, PxU32 nbVerts, const PxVec3* PX_RESTRICT verts) +{ + // PT: we can safely V4LoadU the first N-1 vertices. We must V3LoadU the last vertex, to make sure we don't read + // invalid memory. Since we have to special-case that last vertex anyway, we reuse that code to also initialize + // the minV/maxV values (bypassing the need for a 'setEmpty()' initialization). + + if(!nbVerts) + { + bounds.setEmpty(); + return; + } + + PxU32 nbSafe = nbVerts-1; + + // PT: read last (unsafe) vertex using V3LoadU, initialize minV/maxV + const Vec4V lastVertexV = Vec4V_From_Vec3V(V3LoadU(&verts[nbSafe].x)); + Vec4V minV = lastVertexV; + Vec4V maxV = lastVertexV; + + // PT: read N-1 first (safe) vertices using V4LoadU + while(nbSafe--) + { + const Vec4V vertexV = V4LoadU(&verts->x); + verts++; + + minV = V4Min(minV, vertexV); + maxV = V4Max(maxV, vertexV); + } + + StoreBounds(bounds, minV, maxV); +} + +void Gu::computeSweptBox(Gu::Box& dest, const PxVec3& extents, const PxVec3& center, const PxMat33& rot, const PxVec3& unitDir, const PxReal distance) +{ + PxVec3 R1, R2; + Ps::computeBasis(unitDir, R1, R2); + + PxReal dd[3]; + dd[0] = PxAbs(rot.column0.dot(unitDir)); + dd[1] = PxAbs(rot.column1.dot(unitDir)); + dd[2] = PxAbs(rot.column2.dot(unitDir)); + PxReal dmax = dd[0]; + PxU32 ax0=1; + PxU32 ax1=2; + if(dd[1]>dmax) + { + dmax=dd[1]; + ax0=0; + ax1=2; + } + if(dd[2]>dmax) + { + dmax=dd[2]; + ax0=0; + ax1=1; + } + if(dd[ax1] maxDistance) + return false; + + // PT: make it a relative epsilon to make sure it still works with large distances + distEpsilon *= PxMax(1.0f, PxMax(triImpactDistance, bestImpactDistance)); + + // If new distance is more than epsilon closer than old distance + if(triImpactDistance < bestImpactDistance - distEpsilon) + return true; + + // If new distance is no more than epsilon farther than oldDistance and "face is more opposing than previous" + if(triImpactDistance < bestImpactDistance+distEpsilon && triAlignmentValue < bestAlignmentValue) + return true; + + // If alignment value is the same, but the new triangle is closer than the best distance + if(triAlignmentValue == bestAlignmentValue && triImpactDistance < bestImpactDistance) + return true; + + // If initial overlap happens, keep the triangle + if(triImpactDistance == 0.0f) + return true; + + return false; + } + + #define StoreBounds(bounds, minV, maxV) \ + V4StoreU(minV, &bounds.minimum.x); \ + PX_ALIGN(16, PxVec4) max4; \ + V4StoreA(maxV, &max4.x); \ + bounds.maximum = PxVec3(max4.x, max4.y, max4.z); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuMTD.cpp b/src/PhysX/physx/source/geomutils/src/GuMTD.cpp new file mode 100644 index 000000000..599254609 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuMTD.cpp @@ -0,0 +1,1457 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuMTD.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentBox.h" + + +#include "GuVecBox.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuInternal.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuBoxConversion.h" +#include "GuGeometryUnion.h" +#include "GuShapeConvex.h" +#include "GuPCMShapeConvex.h" +#include "GuPCMContactGen.h" +#include "GuConvexMesh.h" +#include "GuGJK.h" + +#include "PsUtilities.h" +#include "PsVecTransform.h" +#include "PsMathUtils.h" +#include "PxMeshScale.h" +#include "PxConvexMeshGeometry.h" + +using namespace physx; +using namespace Gu; + +static PX_FORCE_INLINE PxF32 manualNormalize(PxVec3& mtd, const PxVec3& normal, PxReal lenSq) +{ + const PxF32 len = PxSqrt(lenSq); + + // We do a *manual* normalization to check for singularity condition + if(lenSq < 1e-6f) + mtd = PxVec3(1.0f, 0.0f, 0.0f); // PT: zero normal => pick up random one + else + mtd = normal * 1.0f / len; + + return len; +} + +static PX_FORCE_INLINE float validateDepth(float depth) +{ + // PT: penetration depth must always be positive or null, but FPU accuracy being what it is, we sometimes + // end up with very small, epsilon-sized negative depths. We clamp those to zero, since they don't indicate + // real bugs in the MTD functions. However anything larger than epsilon is wrong, and caught with an assert. + const float epsilon = 1.e-3f; + + //ML: because we are shrunking the shape in this moment, so the depth might be larger than eps, this condition is no longer valid + //PX_ASSERT(depth>=-epsilon); + PX_UNUSED(epsilon); + return PxMax(depth, 0.0f); +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: the function names should follow the order in which the PxGeometryTypes are listed, +// i.e. computeMTD_Type0Type1 with Type0<=Type1. This is to guarantee that the proper results +// (following the desired convention) are returned from the PxGeometryQuery-level call. + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_SphereSphere(PxVec3& mtd, PxF32& depth, const Sphere& sphere0, const Sphere& sphere1) +{ + const PxVec3 delta = sphere0.center - sphere1.center; + const PxReal d2 = delta.magnitudeSquared(); + const PxReal radiusSum = sphere0.radius + sphere1.radius; + + if(d2 > radiusSum*radiusSum) + return false; + + const PxF32 d = manualNormalize(mtd, delta, d2); + + depth = validateDepth(radiusSum - d); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_SphereCapsule(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const Capsule& capsule) +{ + const PxReal radiusSum = sphere.radius + capsule.radius; + + PxReal u; + const PxReal d2 = distancePointSegmentSquared(capsule, sphere.center, &u); + + if(d2 > radiusSum*radiusSum) + return false; + + const PxVec3 normal = sphere.center - capsule.getPointAt(u); + + const PxReal lenSq = normal.magnitudeSquared(); + const PxF32 d = manualNormalize(mtd, normal, lenSq); + + depth = validateDepth(radiusSum - d); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + + +//This version is ported 1:1 from novodex +static PX_FORCE_INLINE bool ContactSphereBox(const PxVec3& sphereOrigin, + PxReal sphereRadius, + const PxVec3& boxExtents, +// const PxcCachedTransforms& boxCacheTransform, + const PxTransform& boxTransform, + PxVec3& point, + PxVec3& normal, + PxReal& separation, + PxReal contactDistance) +{ + + //returns true on contact + const PxVec3 delta = sphereOrigin - boxTransform.p; // s1.center - s2.center; + PxVec3 dRot = boxTransform.rotateInv(delta); //transform delta into OBB body coords. + + //check if delta is outside ABB - and clip the vector to the ABB. + bool outside = false; + + if (dRot.x < -boxExtents.x) + { + outside = true; + dRot.x = -boxExtents.x; + } + else if (dRot.x > boxExtents.x) + { + outside = true; + dRot.x = boxExtents.x; + } + + if (dRot.y < -boxExtents.y) + { + outside = true; + dRot.y = -boxExtents.y; + } + else if (dRot.y > boxExtents.y) + { + outside = true; + dRot.y = boxExtents.y; + } + + if (dRot.z < -boxExtents.z) + { + outside = true; + dRot.z =-boxExtents.z; + } + else if (dRot.z > boxExtents.z) + { + outside = true; + dRot.z = boxExtents.z; + } + + if (outside) //if clipping was done, sphere center is outside of box. + { + point = boxTransform.rotate(dRot); //get clipped delta back in world coords. + normal = delta - point; //what we clipped away. + const PxReal lenSquared = normal.magnitudeSquared(); + const PxReal inflatedDist = sphereRadius + contactDistance; + if (lenSquared > inflatedDist * inflatedDist) + return false; //disjoint + + //normalize to make it into the normal: + separation = PxRecipSqrt(lenSquared); + normal *= separation; + separation *= lenSquared; + //any plane that touches the sphere is tangential, so a vector from contact point to sphere center defines normal. + //we could also use point here, which has same direction. + //this is either a faceFace or a vertexFace contact depending on whether the box's face or vertex collides, but we did not distinguish. + //We'll just use vertex face for now, this info isn't really being used anyway. + //contact point is point on surface of cube closest to sphere center. + point += boxTransform.p; + separation -= sphereRadius; + return true; + } + else + { + //center is in box, we definitely have a contact. + PxVec3 locNorm; //local coords contact normal + + PxVec3 absdRot; + absdRot = PxVec3(PxAbs(dRot.x), PxAbs(dRot.y), PxAbs(dRot.z)); + PxVec3 distToSurface = boxExtents - absdRot; //dist from embedded center to box surface along 3 dimensions. + + //find smallest element of distToSurface + if (distToSurface.y < distToSurface.x) + { + if (distToSurface.y < distToSurface.z) + { + //y + locNorm = PxVec3(0.0f, dRot.y > 0.0f ? 1.0f : -1.0f, 0.0f); + separation = -distToSurface.y; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + else + { + if (distToSurface.x < distToSurface.z) + { + //x + locNorm = PxVec3(dRot.x > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f); + separation = -distToSurface.x; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + //separation so far is just the embedding of the center point; we still have to push out all of the radius. + point = sphereOrigin; + normal = boxTransform.rotate(locNorm); + separation -= sphereRadius; + return true; + } +} + +static bool computeMTD_SphereBox(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const Box& box) +{ + PxVec3 point; + if(!ContactSphereBox( sphere.center, sphere.radius, + box.extents, PxTransform(box.center, PxQuat(box.rot)), + point, mtd, depth, 0.0f)) + return false; + depth = validateDepth(-depth); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_CapsuleCapsule(PxVec3& mtd, PxF32& depth, const Capsule& capsule0, const Capsule& capsule1) +{ + PxReal s,t; + const PxReal d2 = distanceSegmentSegmentSquared(capsule0, capsule1, &s, &t); + + const PxReal radiusSum = capsule0.radius + capsule1.radius; + + if(d2 > radiusSum*radiusSum) + return false; + + const PxVec3 normal = capsule0.getPointAt(s) - capsule1.getPointAt(t); + + const PxReal lenSq = normal.magnitudeSquared(); + const PxF32 d = manualNormalize(mtd, normal, lenSq); + + depth = validateDepth(radiusSum - d); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + + +static PX_FORCE_INLINE void reorderMTD(PxVec3& mtd, const PxVec3& center0, const PxVec3& center1) +{ + const PxVec3 witness = center0 - center1; + if(mtd.dot(witness) < 0.0f) + mtd = -mtd; +} + +static PX_FORCE_INLINE void projectBox(PxReal& min, PxReal& max, const PxVec3& axis, const Box& box) +{ + const PxReal boxCen = box.center.dot(axis); + const PxReal boxExt = + PxAbs(box.rot.column0.dot(axis)) * box.extents.x + + PxAbs(box.rot.column1.dot(axis)) * box.extents.y + + PxAbs(box.rot.column2.dot(axis)) * box.extents.z; + + min = boxCen - boxExt; + max = boxCen + boxExt; +} + +static bool PxcTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, const Box& box, PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project box + PxReal Min1, Max1; + projectBox(Min1, Max1, axis, box); + + // Test projections + if(max0=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +static bool PxcCapsuleOBBOverlap3(const Segment& segment, PxReal radius, const Box& box, PxReal* t=NULL, PxVec3* pp=NULL) +{ + PxVec3 Sep(0.0f); + PxReal PenDepth = PX_MAX_REAL; + + // Test normals + for(PxU32 i=0;i<3;i++) + { + PxReal d; + if(!PxcTestAxis(box.rot[i], segment, radius, box, d)) + return false; + + if(d capsule.radius*capsule.radius) + return false; + + if(d2 != 0.0f) + { + // PT: the capsule segment doesn't intersect the box => distance-based version + const PxVec3 onSegment = capsule.getPointAt(t); + onBox = box.center + box.rot.transform(onBox); + + PxVec3 normal = onSegment - onBox; + PxReal normalLen = normal.magnitude(); + + if(normalLen != 0.0f) + { + normal *= 1.0f/normalLen; + + mtd = normal; + depth = validateDepth(capsule.radius - PxSqrt(d2)); + return true; + } + } + + // PT: the capsule segment intersects the box => penetration-based version + return PxcCapsuleOBBOverlap3(capsule, capsule.radius, box, &depth, &mtd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool PxcTestAxis(const PxVec3& axis, const Box& box0, const Box& box1, PxReal& depth) +{ + // Project box + PxReal min0, max0; + projectBox(min0, max0, axis, box0); + + // Project box + PxReal Min1, Max1; + projectBox(Min1, Max1, axis, box1); + + // Test projections + if(max0=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +static PX_FORCE_INLINE bool testBoxBoxAxis(PxVec3& mtd, PxF32& depth, const PxVec3& axis, const Box& box0, const Box& box1) +{ + PxF32 d; + if(!PxcTestAxis(axis, box0, box1, d)) + return false; + if(dgetHull(); + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + const ConvexHullV convexHull_(hullData, zeroV, vScale, vQuat, meshScale.isIdentity()); + + const PsMatTransformV aToB(convexPose.transformInv(transform0)); + + //const CapsuleV capsule(zeroV, zeroV, FZero());//this is a point + const CapsuleV capsule_(aToB.p, FZero());//this is a point + LocalConvex capsule(capsule_); + LocalConvex convexHull(convexHull_); + + status = gjk, LocalConvex >(capsule, convexHull, aToB.p, FMax(), closA, closB, normalV, dist); + } + + bool intersect = status == GJK_CONTACT; + if(intersect) + { + sqDistance = 0.0f; + } + else + { + const FloatV sqDist = FMul(dist, dist); + FStore(sqDist, &sqDistance); + V3StoreU(normalV, normal_); + V3StoreU(closB, closestPoint_); + + normal_ = convexPose.rotate(normal_); + closestPoint_ = convexPose.transform(closestPoint_); + } + + return intersect; +} + +static bool computeMTD_SphereConvex(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + PxReal d2; + const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + PxVec3 dummy; + if(!pointConvexDistance(mtd, dummy, d2, sphere.center, convexMesh, convexGeom.scale, convexPose)) + { + if(d2 > sphere.radius*sphere.radius) + return false; + + depth = validateDepth(sphere.radius - PxSqrt(d2)); + mtd = -mtd; + return true; + } + + // PT: if we reach this place, the sphere center touched the convex => switch to penetration-based code + PxU32 nbPolygons = convexMesh->getNbPolygonsFast(); + const HullPolygonData* polygons = convexMesh->getPolygons(); + const PxVec3 localSphereCenter = convexPose.transformInv(sphere.center); + PxReal dmax = -PX_MAX_F32; + while(nbPolygons--) + { + const HullPolygonData& polygon = *polygons++; + const PxF32 d = polygon.mPlane.distance(localSphereCenter); + if(d>dmax) + { + dmax = d; + mtd = convexPose.rotate(polygon.mPlane.n); + } + } + depth = validateDepth(sphere.radius - dmax); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +//ML : capsule will be in the local space of convexHullV +static bool internalComputeMTD_CapsuleConvex(const CapsuleV& capsule, const bool idtScale, ConvexHullV& convexHullV, const Ps::aos::PsTransformV& transf1, + Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) +{ + PolygonalData polyData; + getPCMConvexData(convexHullV, idtScale, polyData); + + PxU8 buff[sizeof(SupportLocalImpl)]; + + SupportLocal* map = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(static_cast(convexHullV), transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale)) : + static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(convexHullV, transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale))); + + return computeMTD(capsule, polyData, map, penetrationDepth, normal); +} + +static bool computeMTD_CapsuleConvex(PxVec3& mtd, PxF32& depth, const Capsule& capsule, const PxTransform& capsulePose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + const FloatV capsuleHalfHeight = FLoad(capsule.length()*0.5f); + const FloatV capsuleRadius = FLoad(capsule.radius); + + const Vec3V zeroV = V3Zero(); + // Convex mesh + const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + const ConvexHullData* hull = &convexMesh->getHull(); + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHullV(hull, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + //~Convex mesh + + + const QuatV q0 = QuatVLoadU(&capsulePose.q.x); + const Vec3V p0 = V3LoadU(&capsulePose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + Vec3V normal = zeroV; + FloatV penetrationDepth = FZero(); + + CapsuleV capsuleV(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + const bool idtScale = convexGeom.scale.isIdentity(); + bool hasContacts = internalComputeMTD_CapsuleConvex(capsuleV, idtScale, convexHullV, transf1, penetrationDepth, normal); + if(hasContacts) + { + FStore(penetrationDepth, &depth); + depth = validateDepth(depth); + V3StoreU(normal, mtd); + } + + return hasContacts; +} + +/////////////////////////////////////////////////////////////////////////////// +static bool internalComputeMTD_BoxConvex(const PxVec3 halfExtents, const BoxV& box, const bool idtScale, ConvexHullV& convexHullV, const Ps::aos::PsTransformV& transf0, const Ps::aos::PsTransformV& transf1, + Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) +{ + PolygonalData polyData0; + PCMPolygonalBox polyBox0(halfExtents); + polyBox0.getPolygonalData(&polyData0); + polyData0.mPolygonVertexRefs = gPCMBoxPolygonData; + + PolygonalData polyData1; + getPCMConvexData(convexHullV, idtScale, polyData1); + + Mat33V identity = M33Identity(); + SupportLocalImpl map0(box, transf0, identity, identity, true); + + PxU8 buff[sizeof(SupportLocalImpl)]; + + SupportLocal* map1 = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(static_cast(convexHullV), transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale)) : + static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(convexHullV, transf1, convexHullV.vertex2Shape, convexHullV.shape2Vertex, idtScale))); + + return computeMTD(polyData0, polyData1, &map0, map1, penetrationDepth, normal); + +} + +static bool computeMTD_BoxConvex(PxVec3& mtd, PxF32& depth, const Box& box, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + const Vec3V zeroV = V3Zero(); + const PxTransform boxPose = box.getTransform(); + const Vec3V boxExtents = V3LoadU(box.extents); + BoxV boxV(zeroV, boxExtents); + + // Convex mesh + const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + const ConvexHullData* hull = &convexMesh->getHull(); + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHullV(hull, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + //~Convex mesh + + + const QuatV q0 = QuatVLoadU(&boxPose.q.x); + const Vec3V p0 = V3LoadU(&boxPose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + + Vec3V normal=zeroV; + FloatV penetrationDepth=FZero(); + + const bool idtScale = convexGeom.scale.isIdentity(); + bool hasContacts = internalComputeMTD_BoxConvex(box.extents, boxV, idtScale, convexHullV, transf0, transf1, penetrationDepth, normal); + if(hasContacts) + { + FStore(penetrationDepth, &depth); + depth = validateDepth(depth); + V3StoreU(normal, mtd); + } + + return hasContacts; + +} + + +static bool internalComputeMTD_ConvexConvex(const bool idtScale0, const bool idtScale1, ConvexHullV& convexHullV0, ConvexHullV& convexHullV1, const Ps::aos::PsTransformV& transf0, const Ps::aos::PsTransformV& transf1, + Ps::aos::FloatV& penetrationDepth, Ps::aos::Vec3V& normal) +{ + PolygonalData polyData0, polyData1; + getPCMConvexData(convexHullV0, idtScale0, polyData0); + getPCMConvexData(convexHullV1, idtScale1, polyData1); + + PxU8 buff0[sizeof(SupportLocalImpl)]; + PxU8 buff1[sizeof(SupportLocalImpl)]; + + SupportLocal* map0 = (idtScale0 ? static_cast(PX_PLACEMENT_NEW(buff0, SupportLocalImpl)(static_cast(convexHullV0), transf0, convexHullV0.vertex2Shape, convexHullV0.shape2Vertex, idtScale0)) : + static_cast(PX_PLACEMENT_NEW(buff0, SupportLocalImpl)(convexHullV0, transf0, convexHullV0.vertex2Shape, convexHullV0.shape2Vertex, idtScale0))); + + SupportLocal* map1 = (idtScale1 ? static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(static_cast(convexHullV1), transf1, convexHullV1.vertex2Shape, convexHullV1.shape2Vertex, idtScale1)) : + static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(convexHullV1, transf1, convexHullV1.vertex2Shape, convexHullV1.shape2Vertex, idtScale1))); + + return computeMTD(polyData0, polyData1, map0, map1, penetrationDepth, normal); +} + +/////////////////////////////////////////////////////////////////////////////// +static bool computeMTD_ConvexConvex(PxVec3& mtd, PxF32& depth, const PxConvexMeshGeometry& convexGeom0, const PxTransform& convexPose0, const PxConvexMeshGeometry& convexGeom1, const PxTransform& convexPose1) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + // Convex mesh + const ConvexMesh* convexMesh0 = static_cast(convexGeom0.convexMesh); + const ConvexHullData* hull0 = &convexMesh0->getHull(); + const Vec3V vScale0 = V3LoadU_SafeReadW(convexGeom0.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat0 = QuatVLoadU(&convexGeom0.scale.rotation.x); + ConvexHullV convexHullV0(hull0, zeroV, vScale0, vQuat0, convexGeom0.scale.isIdentity()); + //~Convex mesh + + // Convex mesh + const ConvexMesh* convexMesh1 = static_cast(convexGeom1.convexMesh); + const ConvexHullData* hull1 = &convexMesh1->getHull(); + const Vec3V vScale1 = V3LoadU_SafeReadW(convexGeom1.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat1 = QuatVLoadU(&convexGeom1.scale.rotation.x); + ConvexHullV convexHullV1(hull1, zeroV, vScale1, vQuat1, convexGeom1.scale.isIdentity()); + //~Convex mesh + + const QuatV q0 = QuatVLoadU(&convexPose0.q.x); + const Vec3V p0 = V3LoadU(&convexPose0.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose1.q.x); + const Vec3V p1 = V3LoadU(&convexPose1.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + + Vec3V normal = zeroV; + FloatV penetrationDepth = FZero(); + + + const bool idtScale0 = convexGeom0.scale.isIdentity(); + const bool idtScale1 = convexGeom1.scale.isIdentity(); + + bool hasContacts = internalComputeMTD_ConvexConvex(idtScale0, idtScale1, convexHullV0, convexHullV1, transf0, transf1, penetrationDepth, normal); + + if(hasContacts) + { + FStore(penetrationDepth, &depth); + depth = validateDepth(depth); + V3StoreU(normal, mtd); + } + return hasContacts; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD_SpherePlane(PxVec3& mtd, PxF32& depth, const Sphere& sphere, const PxPlane& plane) +{ + const PxReal d = plane.distance(sphere.center); + if(d>sphere.radius) + return false; + + mtd = plane.n; + depth = validateDepth(sphere.radius - d); + return true; +} + +static bool computeMTD_PlaneBox(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const Box& box) +{ + PxVec3 pts[8]; + box.computeBoxPoints(pts); + + PxReal dmin = plane.distance(pts[0]); + for(PxU32 i=1;i<8;i++) + { + const PxReal d = plane.distance(pts[i]); + dmin = physx::intrinsics::selectMin(dmin, d); + } + if(dmin>0.0f) + return false; + + mtd = -plane.n; + depth = validateDepth(-dmin); + return true; +} + +static bool computeMTD_PlaneCapsule(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const Capsule& capsule) +{ + const PxReal d0 = plane.distance(capsule.p0); + const PxReal d1 = plane.distance(capsule.p1); + const PxReal dmin = physx::intrinsics::selectMin(d0, d1) - capsule.radius; + if(dmin>0.0f) + return false; + + mtd = -plane.n; + depth = validateDepth(-dmin); + return true; +} + +static bool computeMTD_PlaneConvex(PxVec3& mtd, PxF32& depth, const PxPlane& plane, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose) +{ + const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + PxU32 nbVerts = convexMesh->getNbVerts(); + const PxVec3* PX_RESTRICT verts = convexMesh->getVerts(); + + PxReal dmin = plane.distance(convexPose.transform(verts[0])); + for(PxU32 i=1;i0.0f) + return false; + + mtd = -plane.n; + depth = validateDepth(-dmin); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool processContacts(PxVec3& mtd, PxF32& depth, PxU32 nbContacts, const ContactPoint* contacts) +{ + if(nbContacts) + { + PxVec3 mn(0.0f), mx(0.0f); + for(PxU32 i=0; i(geom0); + const PxSphereGeometry& sphereGeom1 = static_cast(geom1); + + return computeMTD_SphereSphere(mtd, depth, Sphere(pose0.p, sphereGeom0.radius), Sphere(pose1.p, sphereGeom1.radius)); +} + +static bool GeomMTDCallback_SpherePlane(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::ePLANE); + PX_UNUSED(geom1); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + return computeMTD_SpherePlane(mtd, depth, Sphere(pose0.p, sphereGeom.radius), getPlane(pose1)); +} + +static bool GeomMTDCallback_SphereCapsule(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxCapsuleGeometry& capsuleGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose1, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_SphereCapsule(mtd, depth, Sphere(pose0.p, sphereGeom.radius), capsule); +} + +static bool GeomMTDCallback_SphereBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxBoxGeometry& boxGeom = static_cast(geom1); + + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return computeMTD_SphereBox(mtd, depth, Sphere(pose0.p, sphereGeom.radius), obb); +} + +static bool GeomMTDCallback_SphereConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + return computeMTD_SphereConvex(mtd, depth, Sphere(pose0.p, sphereGeom.radius), convexGeom, pose1); +} + +static bool GeomMTDCallback_SphereMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + return computeMTD_SphereMesh(mtd, depth, Sphere(pose0.p, sphereGeom.radius), meshGeom, pose1); +} + +static bool GeomMTDCallback_PlaneCapsule(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(geom0); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose1, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_PlaneCapsule(mtd, depth, getPlane(pose0), capsule); +} + +static bool GeomMTDCallback_PlaneBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(geom0); + + const PxBoxGeometry& boxGeom = static_cast(geom1); + + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return computeMTD_PlaneBox(mtd, depth, getPlane(pose0), obb); +} + +static bool GeomMTDCallback_PlaneConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + PX_UNUSED(geom0); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + return computeMTD_PlaneConvex(mtd, depth, getPlane(pose0), convexGeom, pose1); +} + +static bool GeomMTDCallback_CapsuleCapsule(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + + const PxCapsuleGeometry& capsuleGeom0 = static_cast(geom0); + const PxCapsuleGeometry& capsuleGeom1 = static_cast(geom1); + + Capsule capsule0; + getCapsuleSegment(pose0, capsuleGeom0, capsule0); + capsule0.radius = capsuleGeom0.radius; + + Capsule capsule1; + getCapsuleSegment(pose1, capsuleGeom1, capsule1); + capsule1.radius = capsuleGeom1.radius; + + return computeMTD_CapsuleCapsule(mtd, depth, capsule0, capsule1); +} + +static bool GeomMTDCallback_CapsuleBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxBoxGeometry& boxGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return computeMTD_CapsuleBox(mtd, depth, capsule, obb); +} + +static bool GeomMTDCallback_CapsuleConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_CapsuleConvex(mtd, depth, capsule, pose0, convexGeom, pose1); +} + +static bool GeomMTDCallback_CapsuleMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_CapsuleMesh(mtd, depth, capsule, meshGeom, pose1); +} + +static bool GeomMTDCallback_BoxBox(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + + const PxBoxGeometry& boxGeom0 = static_cast(geom0); + const PxBoxGeometry& boxGeom1 = static_cast(geom1); + + Box obb0; + buildFrom(obb0, pose0.p, boxGeom0.halfExtents, pose0.q); + + Box obb1; + buildFrom(obb1, pose1.p, boxGeom1.halfExtents, pose1.q); + + return computeMTD_BoxBox(mtd, depth, obb0, obb1); +} + +static bool GeomMTDCallback_BoxConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + Box obb; + buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); + + return computeMTD_BoxConvex(mtd, depth, obb, convexGeom, pose1); +} + +static bool GeomMTDCallback_BoxMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + Box obb; + buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); + + return computeMTD_BoxMesh(mtd, depth, obb, meshGeom, pose1); +} + +static bool GeomMTDCallback_ConvexConvex(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxConvexMeshGeometry& convexGeom0 = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom1 = static_cast(geom1); + + return computeMTD_ConvexConvex(mtd, depth, convexGeom0, pose0, convexGeom1, pose1); +} + +static bool GeomMTDCallback_ConvexMesh(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + return computeMTD_ConvexMesh(mtd, depth, convexGeom, pose0, meshGeom, pose1); +} + +static bool GeomMTDCallback_SphereHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast(geom1); + + const Sphere sphere(pose0.p, sphereGeom.radius); + + return computeMTD_SphereHeightField(mtd, depth, sphere, meshGeom, pose1); +} + +static bool GeomMTDCallback_CapsuleHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast(geom1); + + Capsule capsule; + getCapsuleSegment(pose0, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + return computeMTD_CapsuleHeightField(mtd, depth, capsule, meshGeom, pose1); +} + +static bool GeomMTDCallback_BoxHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast(geom1); + + Box obb; + buildFrom(obb, pose0.p, boxGeom.halfExtents, pose0.q); + + return computeMTD_BoxHeightField(mtd, depth, obb, meshGeom, pose1); +} + +static bool GeomMTDCallback_ConvexHeightField(GU_MTD_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + const PxHeightFieldGeometry& meshGeom = static_cast(geom1); + + return computeMTD_ConvexHeightField(mtd, depth, convexGeom, pose0, meshGeom, pose1); +} + +Gu::GeomMTDFunc gGeomMTDMethodTable[][PxGeometryType::eGEOMETRY_COUNT] = +{ + //PxGeometryType::eSPHERE + { + GeomMTDCallback_SphereSphere, //PxGeometryType::eSPHERE + GeomMTDCallback_SpherePlane, //PxGeometryType::ePLANE + GeomMTDCallback_SphereCapsule, //PxGeometryType::eCAPSULE + GeomMTDCallback_SphereBox, //PxGeometryType::eBOX + GeomMTDCallback_SphereConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_SphereMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_SphereHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + GeomMTDCallback_NotSupported, //PxGeometryType::ePLANE + GeomMTDCallback_PlaneCapsule, //PxGeometryType::eCAPSULE + GeomMTDCallback_PlaneBox, //PxGeometryType::eBOX + GeomMTDCallback_PlaneConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + GeomMTDCallback_CapsuleCapsule, //PxGeometryType::eCAPSULE + GeomMTDCallback_CapsuleBox, //PxGeometryType::eBOX + GeomMTDCallback_CapsuleConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_CapsuleMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_CapsuleHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + GeomMTDCallback_BoxBox, //PxGeometryType::eBOX + GeomMTDCallback_BoxConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_BoxMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_BoxHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + GeomMTDCallback_ConvexConvex, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_ConvexMesh, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_ConvexHeightField, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + GeomMTDCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, +}; diff --git a/src/PhysX/physx/source/geomutils/src/GuMTD.h b/src/PhysX/physx/source/geomutils/src/GuMTD.h new file mode 100644 index 000000000..572ca9437 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuMTD.h @@ -0,0 +1,60 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MTD_H +#define GU_MTD_H + +#include "PxGeometry.h" + +namespace physx +{ +namespace Gu +{ + // PT: we use a define to be able to quickly change the signature of all MTD functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[out] mtd computed depenetration dir + // \param[out] depth computed depenetration depth + // \param[in] geom0 first geometry object + // \param[in] pose0 pose of first geometry object + // \param[in] geom1 second geometry object + // \param[in] pose1 pose of second geometry object + // \param[in] cache optional cached data for triggers + #define GU_MTD_FUNC_PARAMS PxVec3& mtd, PxF32& depth, \ + const PxGeometry& geom0, const PxTransform& pose0, \ + const PxGeometry& geom1, const PxTransform& pose1 + + // PT: function pointer for Geom-indexed MTD functions + // See GU_MTD_FUNC_PARAMS for function parameters details. + // \return true if an overlap was found, false otherwise + // \note depenetration vector D is equal to mtd * depth. It should be applied to the 1st object, to get out of the 2nd object. + typedef bool (*GeomMTDFunc) (GU_MTD_FUNC_PARAMS); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp new file mode 100644 index 000000000..bd422fc6c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp @@ -0,0 +1,702 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "GuMeshFactory.h" +#include "PxHeightFieldDesc.h" +#include "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" +#include "GuTriangleMeshRTree.h" +#include "GuConvexMesh.h" +#include "GuBVHStructure.h" +#include "GuHeightField.h" +#include "GuConvexMeshData.h" +#include "CmUtils.h" +#include "GuMeshData.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + +// PT: TODO: refactor all this with a dedicated container + +GuMeshFactory::GuMeshFactory() : + mTriangleMeshes (PX_DEBUG_EXP("mesh factory triangle mesh hash")), + mConvexMeshes (PX_DEBUG_EXP("mesh factory convex mesh hash")), + mHeightFields (PX_DEBUG_EXP("mesh factory height field hash")), + mBVHStructures (PX_DEBUG_EXP("BVH structure factory hash")), + mFactoryListeners (PX_DEBUG_EXP("FactoryListeners")) +{ +} + +GuMeshFactory::~GuMeshFactory() +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +template +static void releaseObjects(Ps::CoalescedHashSet& objects) +{ + while(objects.size()) + { + T* object = objects.getEntries()[0]; + PX_ASSERT(object->getRefCount()==1); + object->release(); + } +} + +void GuMeshFactory::release() +{ + // Release all objects in case the user didn't do it + releaseObjects(mTriangleMeshes); + releaseObjects(mConvexMeshes); + releaseObjects(mHeightFields); + releaseObjects(mBVHStructures); + + PX_DELETE(this); +} + +template +static void addToHash(Ps::CoalescedHashSet& hash, T* element, Ps::Mutex* mutex) +{ + if(!element) + return; + + if(mutex) + mutex->lock(); + + hash.insert(element); + + if(mutex) + mutex->unlock(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addTriangleMesh(TriangleMesh* np, bool lock) +{ + addToHash(mTriangleMeshes, np, lock ? &mTrackingMutex : NULL); +} + +PxTriangleMesh* GuMeshFactory::createTriangleMesh(TriangleMeshData& data) +{ + TriangleMesh* np; + + if(data.mType==PxMeshMidPhase::eBVH33) + { + PX_NEW_SERIALIZED(np, RTreeTriangleMesh)(*this, data); + } + else if(data.mType==PxMeshMidPhase::eBVH34) + { + PX_NEW_SERIALIZED(np, BV4TriangleMesh)(*this, data); + } + else return NULL; + + if(np) + addTriangleMesh(np); + + return np; +} + +// data injected by cooking lib for runtime cooking +PxTriangleMesh* GuMeshFactory::createTriangleMesh(void* data) +{ + return createTriangleMesh(*reinterpret_cast(data)); +} + +static TriangleMeshData* loadMeshData(PxInputStream& stream) +{ + // Import header + PxU32 version; + bool mismatch; + if(!readHeader('M', 'E', 'S', 'H', version, mismatch, stream)) + return NULL; + + PxU32 midphaseID = PxMeshMidPhase::eBVH33; // Default before version 14 + if(version>=14) // this refers to PX_MESH_VERSION + { + midphaseID = readDword(mismatch, stream); + } + + // Check if old (incompatible) mesh format is loaded + if (version <= 9) // this refers to PX_MESH_VERSION + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Loading triangle mesh failed: " + "Deprecated mesh cooking format. Please recook your mesh in a new cooking format."); + PX_ALWAYS_ASSERT_MESSAGE("Obsolete cooked mesh found. Mesh version has been updated, please recook your meshes."); + return NULL; + } + + // Import serialization flags + const PxU32 serialFlags = readDword(mismatch, stream); + + // Import misc values + if (version <= 12) // this refers to PX_MESH_VERSION + { + // convexEdgeThreshold was removed in 3.4.0 + readFloat(mismatch, stream); + } + + TriangleMeshData* data; + if(midphaseID==PxMeshMidPhase::eBVH33) + data = PX_NEW(RTreeTriangleData); + else if(midphaseID==PxMeshMidPhase::eBVH34) + data = PX_NEW(BV4TriangleData); + else return NULL; + + // Import mesh + PxVec3* verts = data->allocateVertices(readDword(mismatch, stream)); + const PxU32 nbTris = readDword(mismatch, stream); + bool force32 = (serialFlags & (IMSF_8BIT_INDICES|IMSF_16BIT_INDICES)) == 0; + + //ML: this will allocate CPU triangle indices and GPU triangle indices if we have GRB data built + void* tris = data->allocateTriangles(nbTris, force32, serialFlags & IMSF_GRB_DATA); + + stream.read(verts, sizeof(PxVec3)*data->mNbVertices); + if(mismatch) + { + for(PxU32 i=0;imNbVertices;i++) + { + flip(verts[i].x); + flip(verts[i].y); + flip(verts[i].z); + } + } + //TODO: stop support for format conversion on load!! + const PxU32 nbIndices = 3*data->mNbTriangles; + if(serialFlags & IMSF_8BIT_INDICES) + { + PxU8 x; + if(data->has16BitIndices()) + { + PxU16* tris16 = reinterpret_cast(tris); + for(PxU32 i=0;i(tris); + for(PxU32 i=0;ihas16BitIndices()) + { + PxU16* tris16 = reinterpret_cast(tris); + stream.read(tris16, nbIndices*sizeof(PxU16)); + if(mismatch) + { + for(PxU32 i=0;i(tris); + PxU16 x; + for(PxU32 i=0;ihas16BitIndices()) + { + PxU32 x; + PxU16* tris16 = reinterpret_cast(tris); + for(PxU32 i=0;i(tris); + stream.read(tris32, nbIndices*sizeof(PxU32)); + + if(mismatch) + { + for(PxU32 i=0;iallocateMaterials(); + stream.read(materials, sizeof(PxU16)*data->mNbTriangles); + if(mismatch) + { + for(PxU32 i=0;imNbTriangles;i++) + flip(materials[i]); + } + } + if(serialFlags & IMSF_FACE_REMAP) + { + PxU32* remap = data->allocateFaceRemap(); + readIndices(readDword(mismatch, stream), data->mNbTriangles, remap, stream, mismatch); + } + + if(serialFlags & IMSF_ADJACENCIES) + { + PxU32* adj = data->allocateAdjacencies(); + stream.read(adj, sizeof(PxU32)*data->mNbTriangles*3); + if(mismatch) + { + for(PxU32 i=0;imNbTriangles*3;i++) + flip(adj[i]); + } + } + + // PT: TODO better + if(midphaseID==PxMeshMidPhase::eBVH33) + { + if(!static_cast(data)->mRTree.load(stream, version, mismatch)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "RTree binary image load error."); + PX_DELETE(data); + return NULL; + } + } + else if(midphaseID==PxMeshMidPhase::eBVH34) + { + BV4TriangleData* bv4data = static_cast(data); + if(!bv4data->mBV4Tree.load(stream, mismatch)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV4 binary image load error."); + PX_DELETE(data); + return NULL; + } + + bv4data->mMeshInterface.setNbTriangles(nbTris); + bv4data->mMeshInterface.setNbVertices(data->mNbVertices); + if(data->has16BitIndices()) + bv4data->mMeshInterface.setPointers(NULL, reinterpret_cast(tris), verts); + else + bv4data->mMeshInterface.setPointers(reinterpret_cast(tris), NULL, verts); + bv4data->mBV4Tree.mMeshInterface = &bv4data->mMeshInterface; + } + else PX_ASSERT(0); + + // Import local bounds + data->mGeomEpsilon = readFloat(mismatch, stream); + readFloatBuffer(&data->mAABB.minimum.x, 6, mismatch, stream); + + PxU32 nb = readDword(mismatch, stream); + if(nb) + { + PX_ASSERT(nb==data->mNbTriangles); + data->allocateExtraTrigData(); + // No need to convert those bytes + stream.read(data->mExtraTrigData, nb*sizeof(PxU8)); + } + + if(serialFlags & IMSF_GRB_DATA) + { + PxU32 GRB_meshAdjVerticiesTotal = 0; + if(version < 15) + GRB_meshAdjVerticiesTotal = readDword(mismatch, stream); + + //read grb triangle indices + PX_ASSERT(data->mGRB_primIndices); + + if (serialFlags & IMSF_8BIT_INDICES) + { + PxU8 x; + if (data->has16BitIndices()) + { + PxU16* tris16 = reinterpret_cast(data->mGRB_primIndices); + for (PxU32 i = 0; i(data->mGRB_primIndices); + for (PxU32 i = 0; ihas16BitIndices()) + { + PxU16* tris16 = reinterpret_cast(data->mGRB_primIndices); + stream.read(tris16, nbIndices*sizeof(PxU16)); + if (mismatch) + { + for (PxU32 i = 0; i(data->mGRB_primIndices); + PxU16 x; + for (PxU32 i = 0; ihas16BitIndices()) + { + PxU32 x; + PxU16* tris16 = reinterpret_cast(data->mGRB_primIndices); + for (PxU32 i = 0; i(data->mGRB_primIndices); + stream.read(tris32, nbIndices*sizeof(PxU32)); + + if (mismatch) + { + for (PxU32 i = 0; imGRB_primAdjacencies = static_cast(PX_NEW(PxU32)[data->mNbTriangles*4]); + data->mGRB_faceRemap = PX_NEW(PxU32)[data->mNbTriangles]; + + stream.read(data->mGRB_primAdjacencies, sizeof(PxU32)*data->mNbTriangles*4); + if (version < 15) + { + //stream.read(data->mGRB_vertValency, sizeof(PxU32)*data->mNbVertices); + for (PxU32 i = 0; i < data->mNbVertices; ++i) + readDword(mismatch, stream); + //stream.read(data->mGRB_adjVertStart, sizeof(PxU32)*data->mNbVertices); + for (PxU32 i = 0; i < data->mNbVertices; ++i) + readDword(mismatch, stream); + //stream.read(data->mGRB_adjVertices, sizeof(PxU32)*GRB_meshAdjVerticiesTotal); + for (PxU32 i = 0; i < GRB_meshAdjVerticiesTotal; ++i) + readDword(mismatch, stream); + } + stream.read(data->mGRB_faceRemap, sizeof(PxU32)*data->mNbTriangles); + + if(mismatch) + { + for(PxU32 i=0;imNbTriangles*4;i++) + flip(reinterpret_cast(data->mGRB_primIndices)[i]); + + for(PxU32 i=0;imNbTriangles*4;i++) + flip(reinterpret_cast(data->mGRB_primAdjacencies)[i]); + } + + //read BV32 + data->mGRB_BV32Tree = PX_NEW(BV32Tree); + BV32Tree* bv32Tree = static_cast(data->mGRB_BV32Tree); + if (!bv32Tree->load(stream, mismatch)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV32 binary image load error."); + PX_DELETE(data); + return NULL; + } + } + + return data; +} + +PxTriangleMesh* GuMeshFactory::createTriangleMesh(PxInputStream& desc) +{ + TriangleMeshData* data = ::loadMeshData(desc); + if(!data) + return NULL; + PxTriangleMesh* m = createTriangleMesh(*data); + PX_DELETE(data); + return m; +} + +bool GuMeshFactory::removeTriangleMesh(PxTriangleMesh& m) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + TriangleMesh* gu = static_cast(&m); + bool found = mTriangleMeshes.erase(gu); + return found; +} + +PxU32 GuMeshFactory::getNbTriangleMeshes() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mTriangleMeshes.size(); +} + +PxU32 GuMeshFactory::getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mTriangleMeshes.getEntries(), mTriangleMeshes.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addConvexMesh(ConvexMesh* np, bool lock) +{ + addToHash(mConvexMeshes, np, lock ? &mTrackingMutex : NULL); +} + +// data injected by cooking lib for runtime cooking +PxConvexMesh* GuMeshFactory::createConvexMesh(void* data) +{ + return createConvexMesh(*reinterpret_cast(data)); +} + +PxConvexMesh* GuMeshFactory::createConvexMesh(Gu::ConvexHullData& data) +{ + Gu::ConvexMesh *np; + PX_NEW_SERIALIZED(np, Gu::ConvexMesh)(*this, data); + if (np) + addConvexMesh(np); + + return np; +} + +PxConvexMesh* GuMeshFactory::createConvexMesh(PxInputStream& desc) +{ + ConvexMesh* np; + PX_NEW_SERIALIZED(np, ConvexMesh); + if(!np) + return NULL; + + np->setMeshFactory(this); + + if(!np->load(desc)) + { + np->decRefCount(); + return NULL; + } + + addConvexMesh(np); + return np; +} + +bool GuMeshFactory::removeConvexMesh(PxConvexMesh& m) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + ConvexMesh* gu = static_cast(&m); + bool found = mConvexMeshes.erase(gu); + return found; +} + +PxU32 GuMeshFactory::getNbConvexMeshes() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mConvexMeshes.size(); +} + +PxU32 GuMeshFactory::getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mConvexMeshes.getEntries(), mConvexMeshes.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addHeightField(HeightField* np, bool lock) +{ + addToHash(mHeightFields, np, lock ? &mTrackingMutex : NULL); +} + +PxHeightField* GuMeshFactory::createHeightField(void* heightFieldMeshData) +{ + HeightField* np; + PX_NEW_SERIALIZED(np, HeightField)(*this, *reinterpret_cast(heightFieldMeshData)); + if(np) + addHeightField(np); + + return np; +} + +PxHeightField* GuMeshFactory::createHeightField(PxInputStream& stream) +{ + HeightField* np; + PX_NEW_SERIALIZED(np, HeightField)(this); + if(!np) + return NULL; + + if(!np->load(stream)) + { + np->decRefCount(); + return NULL; + } + + addHeightField(np); + return np; +} + +bool GuMeshFactory::removeHeightField(PxHeightField& hf) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + HeightField* gu = static_cast(&hf); + bool found = mHeightFields.erase(gu); + return found; +} + +PxU32 GuMeshFactory::getNbHeightFields() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mHeightFields.size(); +} + +PxU32 GuMeshFactory::getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mHeightFields.getEntries(), mHeightFields.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addFactoryListener( GuMeshFactoryListener& listener ) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mFactoryListeners.pushBack( &listener ); +} + +void GuMeshFactory::removeFactoryListener( GuMeshFactoryListener& listener ) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + for ( PxU32 idx = 0; idx < mFactoryListeners.size(); ++idx ) + { + if ( mFactoryListeners[idx] == &listener ) + { + mFactoryListeners.replaceWithLast( idx ); + --idx; + } + } +} + +void GuMeshFactory::notifyFactoryListener(const PxBase* base, PxType typeID) +{ + const PxU32 nbListeners = mFactoryListeners.size(); + for(PxU32 i=0; ionGuMeshFactoryBufferRelease(base, typeID); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GuMeshFactory::addBVHStructure(BVHStructure* np, bool lock) +{ + addToHash(mBVHStructures, np, lock ? &mTrackingMutex : NULL); +} + +// data injected by cooking lib for runtime cooking +PxBVHStructure* GuMeshFactory::createBVHStructure(void* data) +{ + return createBVHStructure(*reinterpret_cast(data)); +} + +PxBVHStructure* GuMeshFactory::createBVHStructure(Gu::BVHStructureData& data) +{ + Gu::BVHStructure *np; + PX_NEW_SERIALIZED(np, Gu::BVHStructure)(this, data); + if (np) + addBVHStructure(np); + + return np; +} + +PxBVHStructure* GuMeshFactory::createBVHStructure(PxInputStream& desc) +{ + BVHStructure* np; + PX_NEW_SERIALIZED(np, BVHStructure)(this); + if(!np) + return NULL; + + if(!np->load(desc)) + { + np->decRefCount(); + return NULL; + } + + addBVHStructure(np); + return np; +} + +bool GuMeshFactory::removeBVHStructure(PxBVHStructure& m) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + BVHStructure* gu = static_cast(&m); + bool found = mBVHStructures.erase(gu); + return found; +} + +PxU32 GuMeshFactory::getNbBVHStructures() const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return mBVHStructures.size(); +} + +PxU32 GuMeshFactory::getBVHStructures(PxBVHStructure** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mBVHStructures.getEntries(), mBVHStructures.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + diff --git a/src/PhysX/physx/source/geomutils/src/GuMeshFactory.h b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.h new file mode 100644 index 000000000..e17ca335f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MESH_FACTORY_H +#define GU_MESH_FACTORY_H + +#include "foundation/PxIO.h" +#include "PxTriangleMesh.h" +#include "PxConvexMesh.h" +#include "PxHeightField.h" +#include "PxBVHStructure.h" + +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" +#include "PsMutex.h" +#include "PsArray.h" + +#include "PsUserAllocated.h" +#include "PsHashSet.h" + +namespace physx +{ + +class PxHeightFieldDesc; + +namespace Gu +{ + class ConvexMesh; + class HeightField; + class TriangleMesh; + class TriangleMeshData; + class BVHStructure; + struct ConvexHullData; + struct BVHStructureData; +} + +class GuMeshFactoryListener +{ +protected: + virtual ~GuMeshFactoryListener(){} +public: + virtual void onGuMeshFactoryBufferRelease(const PxBase* object, PxType type) = 0; +}; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif +class PX_PHYSX_COMMON_API GuMeshFactory : public Ps::UserAllocated +{ + PX_NOCOPY(GuMeshFactory) +public: + GuMeshFactory(); +protected: + virtual ~GuMeshFactory(); + +public: + void release(); + + // Triangle meshes + void addTriangleMesh(Gu::TriangleMesh* np, bool lock=true); + PxTriangleMesh* createTriangleMesh(PxInputStream& stream); + PxTriangleMesh* createTriangleMesh(void* triangleMeshData); + bool removeTriangleMesh(PxTriangleMesh&); + PxU32 getNbTriangleMeshes() const; + PxU32 getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Convexes + void addConvexMesh(Gu::ConvexMesh* np, bool lock=true); + PxConvexMesh* createConvexMesh(PxInputStream&); + PxConvexMesh* createConvexMesh(void* convexMeshData); + bool removeConvexMesh(PxConvexMesh&); + PxU32 getNbConvexMeshes() const; + PxU32 getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Heightfields + void addHeightField(Gu::HeightField* np, bool lock=true); + PxHeightField* createHeightField(void* heightFieldMeshData); + PxHeightField* createHeightField(PxInputStream&); + bool removeHeightField(PxHeightField&); + PxU32 getNbHeightFields() const; + PxU32 getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // BVHStructure + void addBVHStructure(Gu::BVHStructure* np, bool lock=true); + PxBVHStructure* createBVHStructure(PxInputStream&); + PxBVHStructure* createBVHStructure(void* bvhData); + bool removeBVHStructure(PxBVHStructure&); + PxU32 getNbBVHStructures() const; + PxU32 getBVHStructures(PxBVHStructure** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + void addFactoryListener( GuMeshFactoryListener& listener ); + void removeFactoryListener( GuMeshFactoryListener& listener ); + void notifyFactoryListener(const PxBase*, PxType typeID); + +protected: + + PxTriangleMesh* createTriangleMesh(Gu::TriangleMeshData& data); + PxConvexMesh* createConvexMesh(Gu::ConvexHullData& data); + PxBVHStructure* createBVHStructure(Gu::BVHStructureData& data); + + mutable Ps::Mutex mTrackingMutex; +private: + Ps::CoalescedHashSet mTriangleMeshes; + Ps::CoalescedHashSet mConvexMeshes; + Ps::CoalescedHashSet mHeightFields; + Ps::CoalescedHashSet mBVHStructures; + + Ps::Array mFactoryListeners; +}; +#if PX_VC + #pragma warning(pop) +#endif +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuMetaData.cpp b/src/PhysX/physx/source/geomutils/src/GuMetaData.cpp new file mode 100644 index 000000000..2b312630e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuMetaData.cpp @@ -0,0 +1,598 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "GuHeightField.h" +#include "GuConvexMeshData.h" +#include "GuBigConvexData2.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" +#include "GuTriangleMeshRTree.h" +#include "GuGeometryUnion.h" +#include "PsIntrinsics.h" +#include "CmPhysXCommon.h" +#include "PxMetaData.h" + +using namespace physx; +using namespace Ps; +using namespace Cm; +using namespace Gu; + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_Valency(PxOutputStream& stream) +{ +// 4 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Valency) + PX_DEF_BIN_METADATA_ITEM(stream, Valency, PxU16, mCount, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Valency, PxU16, mOffset, 0) +} + +static void getBinaryMetaData_BigConvexRawData(PxOutputStream& stream) +{ +// 24 bytes + PX_DEF_BIN_METADATA_CLASS(stream, BigConvexRawData) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU16, mSubdiv, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU16, mNbSamples, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU8, mSamples, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU32, mNbVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU32, mNbAdjVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, Valency, mValencies, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexRawData, PxU8, mAdjacentVerts, PxMetaDataFlag::ePTR) +} + +void BigConvexData::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_Valency(stream); + getBinaryMetaData_BigConvexRawData(stream); + +// 28 bytes + PX_DEF_BIN_METADATA_CLASS(stream, BigConvexData) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexData, BigConvexRawData, mData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BigConvexData, void, mVBuffer, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mData.mSamples + // PT: can't use one array of PxU16 since we don't want to flip those bytes during conversion. + // PT: We only align the first array for DE1340, but the second one shouldn't be aligned since + // both are written as one unique block of memory. + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, PxU8, mData.mNbSamples, PX_SERIAL_ALIGN, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, PxU8, mData.mNbSamples, 0, 0) + + // mData.mValencies + // PT: same here, we must only align the first array + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, Valency, mData.mNbVerts, PX_SERIAL_ALIGN, 0) + PX_DEF_BIN_METADATA_EXTRA_ALIGN(stream, BigConvexData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BigConvexData, PxU8, mData.mNbAdjVerts, 0, 0) +} + +static void getBinaryMetaData_InternalObjectsData(PxOutputStream& stream) +{ +// 16 bytes + PX_DEF_BIN_METADATA_CLASS(stream, InternalObjectsData) + PX_DEF_BIN_METADATA_ITEM(stream, InternalObjectsData, PxReal, mRadius, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, InternalObjectsData, PxReal, mExtents, 0) +} + +static void getBinaryMetaData_HullPolygonData(PxOutputStream& stream) +{ +// 20 bytes + PX_DEF_BIN_METADATA_CLASS(stream, HullPolygonData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, HullPolygonData, PxReal, mPlane, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HullPolygonData, PxU16, mVRef8, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HullPolygonData, PxU8, mNbVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HullPolygonData, PxU8, mMinIndex, 0) +} + +static void getBinaryMetaData_ConvexHullData(PxOutputStream& stream) +{ +// 64 bytes + PX_DEF_BIN_METADATA_CLASS(stream, ConvexHullData) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxBounds3, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxVec3, mCenterOfMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, HullPolygonData, mPolygons, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, BigConvexRawData, mBigConvexRawData, PxMetaDataFlag::ePTR) + //ML: the most significant bit of mNbEdges is used to indicate whether we have grb data or not. However, we don't support grb data + //in serialization so we have to mask the most significant bit and force the contact gen run on CPU code path + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxU16, mNbEdges, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxU8, mNbHullVertices, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, PxU8, mNbPolygons, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexHullData, InternalObjectsData, mInternal, 0) +} + +void Gu::ConvexMesh::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_InternalObjectsData(stream); + getBinaryMetaData_HullPolygonData(stream); + getBinaryMetaData_ConvexHullData(stream); + BigConvexData::getBinaryMetaData(stream); + +// 136 bytes + PX_DEF_BIN_METADATA_VCLASS(stream,ConvexMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream,ConvexMesh, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream,ConvexMesh, RefCountable) + + // + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, ConvexHullData, mHullData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, PxU32, mNb, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, BigConvexData, mBigConvexData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, PxReal, mMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, PxMat33, mInertia, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConvexMesh, GuMeshFactory, mMeshFactory, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mHullData.mPolygons (Gu::HullPolygonData, PxVec3, PxU8*2, PxU8) + // PT: we only align the first array since the other ones are contained within it + + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, HullPolygonData, mHullData.mNbPolygons, PX_SERIAL_ALIGN, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxVec3, mHullData.mNbHullVertices, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbEdges, 0, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbEdges, 0, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbHullVertices, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbHullVertices, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mHullData.mNbHullVertices, 0, 0) + + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, Gu::ConvexMesh, PxU8, mNb, 0, PxMetaDataFlag::eCOUNT_MASK_MSB) + PX_DEF_BIN_METADATA_EXTRA_ALIGN(stream, ConvexMesh, 4) + // mBigConvexData + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, Gu::ConvexMesh, BigConvexData, mBigConvexData, PX_SERIAL_ALIGN) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PxHeightFieldSample(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxHeightFieldSample) + PX_DEF_BIN_METADATA_ITEM(stream, PxHeightFieldSample, PxI16, height, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxHeightFieldSample, PxBitAndByte, materialIndex0, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxHeightFieldSample, PxBitAndByte, materialIndex1, 0) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxBitAndByte, PxU8) +} + +static void getBinaryMetaData_HeightFieldData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxHeightFieldFlags, PxU16) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxHeightFieldFormat::Enum, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, HeightFieldData) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxBounds3, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxU32, rows, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxU32, columns, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, rowLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, colLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, nbColumns, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxHeightFieldSample, samples, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxReal, convexEdgeThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxHeightFieldFlags, flags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxU16, paddAfterFlags, PxMetaDataFlag::ePADDING) +#endif + PX_DEF_BIN_METADATA_ITEM(stream, HeightFieldData, PxHeightFieldFormat::Enum, format, 0) +} + +void Gu::HeightField::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PxHeightFieldSample(stream); + getBinaryMetaData_HeightFieldData(stream); + + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxMaterialTableIndex, PxU16) + + PX_DEF_BIN_METADATA_VCLASS(stream, HeightField) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, HeightField, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, HeightField, RefCountable) + + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, HeightFieldData, mData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxU32, mSampleStride, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxU32, mNbSamples, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxReal, mMinHeight, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxReal, mMaxHeight, 0) + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, PxU32, mModifyCount, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, HeightField, GuMeshFactory, mMeshFactory, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mData.samples + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, HeightField, PxHeightFieldSample, mNbSamples, PX_SERIAL_ALIGN, 0) // PT: ### try to remove mNbSamples later +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_RTreePage(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, RTreePage) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, minx, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, miny, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, minz, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, maxx, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, maxy, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxReal, maxz, 0, RTREE_N) + PX_DEF_BIN_METADATA_ITEMS(stream, RTreePage, PxU32, ptrs, 0, RTREE_N) +} + +void RTree::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_RTreePage(stream); + +// 96 bytes + PX_DEF_BIN_METADATA_CLASS(stream, RTree) + + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mBoundsMin, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mBoundsMax, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mInvDiagonal, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxVec4, mDiagonalScaler, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mPageSize, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mNumRootPages, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mNumLevels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mTotalNodes, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mTotalPages, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, PxU32, mFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RTree, RTreePage, mPages, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mPages + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream,RTree, RTreePage, mTotalPages, 128, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void SourceMesh::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, SourceMesh) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxU32, mNbVerts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxVec3, mVerts, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxU32, mNbTris, 0) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, void, mTriangles32, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, void, mTriangles16, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SourceMesh, PxU32, mRemap, PxMetaDataFlag::ePTR) +} + +static void getBinaryMetaData_BVDataPackedQ(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, QuantizedAABB) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxU16, mData[0].mExtents, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxI16, mData[0].mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxU16, mData[1].mExtents, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxI16, mData[1].mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxU16, mData[2].mExtents, 0) + PX_DEF_BIN_METADATA_ITEM(stream, QuantizedAABB, PxI16, mData[2].mCenter, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, BVDataPackedQ) + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPackedQ, QuantizedAABB, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPackedQ, PxU32, mData, 0) +} + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE +static void getBinaryMetaData_BVDataPackedNQ(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, CenterExtents) + PX_DEF_BIN_METADATA_ITEM(stream, CenterExtents, PxVec3, mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, CenterExtents, PxVec3, mExtents, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, BVDataPackedNQ) + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPackedNQ, CenterExtents, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BVDataPackedNQ, PxU32, mData, 0) +} +#endif + +void BV4Tree::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_BVDataPackedQ(stream); +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + getBinaryMetaData_BVDataPackedNQ(stream); +#endif + PX_DEF_BIN_METADATA_CLASS(stream, LocalBounds) + PX_DEF_BIN_METADATA_ITEM(stream, LocalBounds, PxVec3, mCenter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, LocalBounds, float, mExtentsMagnitude, 0) + +// 96 bytes + PX_DEF_BIN_METADATA_CLASS(stream, BV4Tree) + + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, void, mMeshInterface, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, LocalBounds, mLocalBounds, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxU32, mNbNodes, 0) + //PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, void, mNodes, PxMetaDataFlag::eEXTRA_DATA) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxU32, mInitData, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxVec3, mCenterOrMinCoeff, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, PxVec3, mExtentsOrMaxCoeff, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, bool, mUserAllocated, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4Tree, bool, mQuantized, 0) + PX_DEF_BIN_METADATA_ITEMS(stream, BV4Tree, bool, mPadding, PxMetaDataFlag::ePADDING, 2) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ARRAY(stream, BV4Tree, BVDataPackedQ, mNbNodes, 16, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Gu::TriangleMesh::getBinaryMetaData(PxOutputStream& stream) +{ +// 320 => 304 => 272 => 256 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, TriangleMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, TriangleMesh, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, TriangleMesh, RefCountable) + +// 224 => 208 => 192 bytes + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mNbVertices, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mNbTriangles, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxVec3, mVertices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mTriangles, PxMetaDataFlag::ePTR) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxBounds3, mAABB, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU8, mExtraTrigData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxReal, mGeomEpsilon, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU8, mFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU16, mMaterialIndices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mFaceRemap, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mAdjacencies, PxMetaDataFlag::ePTR) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mGRB_triIndices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mGRB_triAdjacencies, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, PxU32, mGRB_faceRemap, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, void, mGRB_BV32Tree, PxMetaDataFlag::ePTR) + + + //------ Extra-data ------ + + // mVertices + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxVec3, mVertices, mNbVertices, 0, PX_SERIAL_ALIGN) + + // mTriangles + // PT: quite tricky here: we exported either an array of PxU16s or an array of PxU32s. We trick the converter by + // pretending we exported both, with the same control variable (m16BitIndices) but opposed control flags. Also there's + // no way to capture "mNumTriangles*3" using the macros, so we just pretend we exported 3 buffers instead of 1. + // But since in reality it's all the same buffer, only the first one is declared as aligned. + + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU16, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, 0, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU16, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU16, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU32, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, PxMetaDataFlag::eCONTROL_FLIP, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU32, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, PxMetaDataFlag::eCONTROL_FLIP, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS_MASKED_CONTROL(stream, TriangleMesh, PxU32, mFlags, PxTriangleMeshFlag::e16_BIT_INDICES, mNbTriangles, PxMetaDataFlag::eCONTROL_FLIP, 0) + + // mExtraTrigData + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU8, mExtraTrigData, mNbTriangles, 0, PX_SERIAL_ALIGN) + + // mMaterialIndices + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU16, mMaterialIndices, mNbTriangles, 0, PX_SERIAL_ALIGN) + + // mFaceRemap + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mFaceRemap, mNbTriangles, 0, PX_SERIAL_ALIGN) + + // mAdjacencies + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mAdjacencies, mNbTriangles, 0, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mAdjacencies, mNbTriangles, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, TriangleMesh, PxU32, mAdjacencies, mNbTriangles, 0, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, TriangleMesh, GuMeshFactory, mMeshFactory, PxMetaDataFlag::ePTR) + + +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, TriangleMesh, PxU32, mPaddingFromInternalMesh, PxMetaDataFlag::ePADDING) +#endif +} + +void Gu::RTreeTriangleMesh::getBinaryMetaData(PxOutputStream& stream) +{ + RTree::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, RTreeTriangleMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, RTreeTriangleMesh, TriangleMesh) + + PX_DEF_BIN_METADATA_ITEM(stream, RTreeTriangleMesh, RTree, mRTree, 0) +} + +void Gu::BV4TriangleMesh::getBinaryMetaData(PxOutputStream& stream) +{ + SourceMesh::getBinaryMetaData(stream); + BV4Tree::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, BV4TriangleMesh) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, BV4TriangleMesh, TriangleMesh) + + PX_DEF_BIN_METADATA_ITEM(stream, BV4TriangleMesh, SourceMesh, mMeshInterface, 0) + PX_DEF_BIN_METADATA_ITEM(stream, BV4TriangleMesh, BV4Tree, mBV4Tree, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void MaterialIndicesStruct::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, MaterialIndicesStruct) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU16, indices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU16, numIndices, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU16, pad, PxMetaDataFlag::ePADDING) +#if PX_P64_FAMILY + PX_DEF_BIN_METADATA_ITEM(stream, MaterialIndicesStruct, PxU32, pad64, PxMetaDataFlag::ePADDING) +#endif + + //------ Extra-data ------ + // indices + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, MaterialIndicesStruct, PxU16, indices, numIndices, 0, PX_SERIAL_ALIGN) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Gu::GeometryUnion::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxGeometryType::Enum, PxU32) + + // The various PxGeometry classes are all public, so I can't really put the meta-data function in there. And then + // I can't access their protected members. So we use the same trick as for the ShapeContainer + class ShadowConvexMeshGeometry : public PxConvexMeshGeometryLL + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_TYPEDEF(stream_, PxConvexMeshGeometryFlags, PxU8) + + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowConvexMeshGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxMeshScale, scale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxConvexMesh, convexMesh, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, PxConvexMeshGeometryFlags, meshFlags, 0) + PX_DEF_BIN_METADATA_ITEMS(stream_, ShadowConvexMeshGeometry, PxU8, paddingFromFlags, PxMetaDataFlag::ePADDING, 3) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, ConvexHullData, hullData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowConvexMeshGeometry, bool, gpuCompatible, 0) + } + }; + ShadowConvexMeshGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxConvexMeshGeometryLL, ShadowConvexMeshGeometry) + + ///////////////// + + class ShadowTriangleMeshGeometry : public PxTriangleMeshGeometryLL + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_TYPEDEF(stream_, PxMeshGeometryFlags, PxU8) + + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowTriangleMeshGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxMeshScale, scale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxMeshGeometryFlags, meshFlags, 0) + PX_DEF_BIN_METADATA_ITEMS(stream_, ShadowTriangleMeshGeometry, PxU8, paddingFromFlags, PxMetaDataFlag::ePADDING, 3) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxTriangleMesh, triangleMesh, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, TriangleMesh, meshData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, PxU16, materialIndices, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowTriangleMeshGeometry, MaterialIndicesStruct, materials, 0) + } + }; + ShadowTriangleMeshGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream,PxTriangleMeshGeometryLL, ShadowTriangleMeshGeometry) + + ///////////////// + + class ShadowHeightFieldGeometry : public PxHeightFieldGeometryLL + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowHeightFieldGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxHeightField, heightField, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxReal, heightScale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxReal, rowScale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxReal, columnScale, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, PxMeshGeometryFlags, heightFieldFlags, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream_, ShadowHeightFieldGeometry, PxU8, paddingFromFlags, PxMetaDataFlag::ePADDING) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, HeightField, heightFieldData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowHeightFieldGeometry, MaterialIndicesStruct, materials, 0) + } + }; + ShadowHeightFieldGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream,PxHeightFieldGeometryLL, ShadowHeightFieldGeometry) + + ///////////////// + + class ShadowPlaneGeometry : public PxPlaneGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowPlaneGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowPlaneGeometry, PxGeometryType::Enum, mType, 0) + } + }; + ShadowPlaneGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream,PxPlaneGeometry, ShadowPlaneGeometry) + + ///////////////// + + class ShadowSphereGeometry : public PxSphereGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowSphereGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowSphereGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowSphereGeometry, PxReal, radius, 0) + } + }; + ShadowSphereGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxSphereGeometry, ShadowSphereGeometry) + + ///////////////// + + class ShadowCapsuleGeometry : public PxCapsuleGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowCapsuleGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowCapsuleGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowCapsuleGeometry, PxReal, radius, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowCapsuleGeometry, PxReal, halfHeight, 0) + } + }; + ShadowCapsuleGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxCapsuleGeometry, ShadowCapsuleGeometry) + + ///////////////// + + class ShadowBoxGeometry : public PxBoxGeometry + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowBoxGeometry) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowBoxGeometry, PxGeometryType::Enum, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, ShadowBoxGeometry, PxVec3, halfExtents,0) + } + }; + ShadowBoxGeometry::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxBoxGeometry, ShadowBoxGeometry) + + /* + - geom union offset & size + - control type offset & size + - type-to-class mapping + */ + +// 44 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Gu::GeometryUnion) + + PX_DEF_BIN_METADATA_UNION(stream, Gu::GeometryUnion, mGeometry) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxSphereGeometry, PxGeometryType::eSPHERE) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxPlaneGeometry, PxGeometryType::ePLANE) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxCapsuleGeometry, PxGeometryType::eCAPSULE) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxBoxGeometry, PxGeometryType::eBOX) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxConvexMeshGeometryLL, PxGeometryType::eCONVEXMESH) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxTriangleMeshGeometryLL,PxGeometryType::eTRIANGLEMESH) + PX_DEF_BIN_METADATA_UNION_TYPE(stream, Gu::GeometryUnion, PxHeightFieldGeometryLL, PxGeometryType::eHEIGHTFIELD) +} diff --git a/src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp new file mode 100644 index 000000000..80fab6271 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp @@ -0,0 +1,695 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuOverlapTests.h" +#include "GuIntersectionBoxBox.h" +#include "GuIntersectionSphereBox.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentBox.h" +#include "GuDistanceSegmentSegment.h" +#include "GuSphere.h" +#include "GuBoxConversion.h" +#include "GuInternal.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecBox.h" +#include "GuConvexMesh.h" +#include "GuHillClimbing.h" +#include "GuGJK.h" + +using namespace physx; +using namespace Cm; +using namespace Gu; + +// PT: TODO: why don't we use ShapeData for overlaps? + +//returns the maximal vertex in shape space +// PT: this function should be removed. We already have 2 different project hull functions in PxcShapeConvex & GuGJKObjectSupport, this one looks like a weird mix of both! +static PxVec3 projectHull_( const ConvexHullData& hull, + float& minimum, float& maximum, + const PxVec3& localDir, // expected to be normalized + const PxMat33& vert2ShapeSkew) +{ + PX_ASSERT(localDir.isNormalized()); + + //use property that x|My == Mx|y for symmetric M to avoid having to transform vertices. + const PxVec3 vertexSpaceDir = vert2ShapeSkew * localDir; + + const PxVec3* Verts = hull.getHullVertices(); + const PxVec3* bestVert = NULL; + + if(!hull.mBigConvexRawData) // Brute-force, local space. Experiments show break-even point is around 32 verts. + { + PxU32 NbVerts = hull.mNbHullVertices; + float min_ = PX_MAX_F32; + float max_ = -PX_MAX_F32; + while(NbVerts--) + { + const float dp = (*Verts).dot(vertexSpaceDir); + min_ = physx::intrinsics::selectMin(min_, dp); + if(dp > max_) { max_ = dp; bestVert = Verts; } + + Verts++; + } + minimum = min_; + maximum = max_; + + PX_ASSERT(bestVert != NULL); + + return vert2ShapeSkew * *bestVert; + } + else //*/if(1) // This version is better for objects with a lot of vertices + { + const PxU32 Offset = ComputeCubemapNearestOffset(vertexSpaceDir, hull.mBigConvexRawData->mSubdiv); + PxU32 MinID = hull.mBigConvexRawData->mSamples[Offset]; + PxU32 MaxID = hull.mBigConvexRawData->getSamples2()[Offset]; + + localSearch(MinID, -vertexSpaceDir, Verts, hull.mBigConvexRawData); + localSearch(MaxID, vertexSpaceDir, Verts, hull.mBigConvexRawData); + + minimum = (Verts[MinID].dot(vertexSpaceDir)); + maximum = (Verts[MaxID].dot(vertexSpaceDir)); + + PX_ASSERT(maximum >= minimum); + + return vert2ShapeSkew * Verts[MaxID]; + } +} + +static bool intersectSphereConvex(const PxTransform& sphereTransform, float radius, const ConvexMesh& mesh, const PxMeshScale& meshScale, const PxTransform& convexGlobalPose, + PxVec3*) +{ + using namespace Ps::aos; + const Vec3V zeroV = V3Zero(); + const ConvexHullData* hullData = &mesh.getHull(); + const FloatV sphereRadius = FLoad(radius); + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + + const PsMatTransformV aToB(convexGlobalPose.transformInv(sphereTransform)); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, meshScale.isIdentity()); + CapsuleV capsule(aToB.p, sphereRadius); + + Vec3V contactA, contactB, normal; + FloatV dist; + LocalConvex convexA(capsule); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + + GjkStatus status = gjk(convexA, convexB, initialSearchDir, FZero(), contactA, contactB, normal, dist); + + return status == GJK_CONTACT; +} + +static bool intersectCapsuleConvex( const PxCapsuleGeometry& capsGeom, const PxTransform& capsGlobalPose, + const ConvexMesh& mesh, const PxMeshScale& meshScale, const PxTransform& convexGlobalPose, + PxVec3*) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + const ConvexHullData* hull = &mesh.getHull(); + + const FloatV capsuleHalfHeight = FLoad(capsGeom.halfHeight); + const FloatV capsuleRadius = FLoad(capsGeom.radius); + + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + + const PsMatTransformV aToB(convexGlobalPose.transformInv(capsGlobalPose)); + + ConvexHullV convexHull(hull, zeroV, vScale, vQuat, meshScale.isIdentity()); + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + Vec3V contactA, contactB, normal; + FloatV dist; + LocalConvex convexA(capsule); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + + GjkStatus status = gjk(convexA, convexB, initialSearchDir, FZero(), contactA, contactB, normal, dist); + + return status == GJK_CONTACT; +} + +static bool intersectBoxConvex(const PxBoxGeometry& boxGeom, const PxTransform& boxGlobalPose, + const ConvexMesh& mesh, const PxMeshScale& meshScale, const PxTransform& convexGlobalPose, + PxVec3*) +{ + // AP: see archived non-GJK version in //sw/physx/dev/pterdiman/graveyard/contactConvexBox.cpp + using namespace Ps::aos; + const Vec3V zeroV = V3Zero(); + const ConvexHullData* hull = &mesh.getHull(); + + const Vec3V vScale = V3LoadU_SafeReadW(meshScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&meshScale.rotation.x); + const Vec3V boxExtents = V3LoadU(boxGeom.halfExtents); + const PsMatTransformV aToB(convexGlobalPose.transformInv(boxGlobalPose)); + + ConvexHullV convexHull(hull, zeroV, vScale, vQuat, meshScale.isIdentity()); + BoxV box(zeroV, boxExtents); + + Vec3V contactA, contactB, normal; + FloatV dist; + RelativeConvex convexA(box, aToB); + LocalConvex convexB(convexHull); + + GjkStatus status = gjk(convexA, convexB, aToB.p, FZero(), contactA, contactB, normal, dist); + + //PX_PRINTF("BOX status = %i, overlap = %i, PxVec3(%f, %f, %f)\n", status, overlap, boxGlobalPose.p.x, boxGlobalPose.p.y, boxGlobalPose.p.z); + + return status == GJK_CONTACT; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxVec3* getCachedAxis(TriggerCache* cache) +{ + if(cache && cache->state==TRIGGER_OVERLAP) + return &cache->dir; + else + return NULL; +} + +static PX_FORCE_INLINE bool updateTriggerCache(bool overlap, TriggerCache* cache) +{ + if(cache) + { + if(overlap) + cache->state = TRIGGER_OVERLAP; + else + cache->state = TRIGGER_DISJOINT; + } + return overlap; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Sphere-vs-shape + +static bool GeomOverlapCallback_SphereSphere(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eSPHERE); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom0 = static_cast(geom0); + const PxSphereGeometry& sphereGeom1 = static_cast(geom1); + + const PxVec3 delta = pose1.p - pose0.p; + const PxReal r = sphereGeom0.radius + sphereGeom1.radius; + return delta.magnitudeSquared() <= r*r; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_SpherePlane(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::ePLANE); + PX_UNUSED(cache); + PX_UNUSED(geom1); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + + return getPlane(pose1).distance(pose0.p) <= sphereGeom.radius; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_SphereCapsule(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxCapsuleGeometry& capsuleGeom = static_cast(geom1); + + // PT: TODO: remove this useless conversion + const PxVec3 capsuleHalfHeightVector = getCapsuleHalfHeightVector(pose1, capsuleGeom); + const PxReal r = sphereGeom.radius + capsuleGeom.radius; + + return distancePointSegmentSquared(capsuleHalfHeightVector, -capsuleHalfHeightVector, pose0.p - pose1.p) <= r*r; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_SphereBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxBoxGeometry& boxGeom = static_cast(geom1); + + // PT: TODO: remove this useless conversion + Box obb; + buildFrom(obb, pose1.p, boxGeom.halfExtents, pose1.q); + + return intersectSphereBox(Sphere(pose0.p, sphereGeom.radius), obb); +} + +static bool GeomOverlapCallback_SphereConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + PxVec3 cachedSepAxis; + PxVec3* tmp = getCachedAxis(cache); + if(tmp) + cachedSepAxis = *tmp; + else + cachedSepAxis = PxVec3(0,0,1.f); + + const bool overlap = intersectSphereConvex(pose0, sphereGeom.radius, + *cm, + convexGeom.scale, pose1, + &cachedSepAxis); + + if(cache && overlap) + cache->dir = cachedSepAxis; + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Plane-vs-shape + +static bool GeomOverlapCallback_PlaneCapsule(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(cache); + PX_UNUSED(geom0); + +// const PxPlaneGeometry& planeGeom = static_cast(geom0); + const PxCapsuleGeometry& capsuleGeom = static_cast(geom1); + + // PT: TODO: remove this useless conversion + Capsule capsule; + getCapsule(capsule, capsuleGeom, pose1); + + const PxPlane plane = getPlane(pose0); + + // We handle the capsule-plane collision with 2 sphere-plane collisions. + // Seems ok so far, since plane is infinite. + + if(plane.distance(capsule.p0) <= capsule.radius) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + + if(plane.distance(capsule.p1) <= capsule.radius) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + + return false; +} + +/*static bool intersectPlaneBox(const PxPlane& plane, const Box& box) +{ + PxVec3 pts[8]; + box.computeBoxPoints(pts); + + for(PxU32 i=0;i<8;i++) + { + if(plane.distance(pts[i]) <= 0.0f) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + } + return false; +}*/ + +static bool GeomOverlapCallback_PlaneBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + PX_UNUSED(geom0); + +// const PxPlaneGeometry& planeGeom = static_cast(geom0); + const PxBoxGeometry& boxGeom = static_cast(geom1); + + // I currently use the same code as for contact generation but maybe we could do something faster (in theory testing + // only 2 pts is enough). + + const Matrix34 absPose(pose1); + const PxPlane worldPlane = getPlane(pose0); + + for(int vx=-1; vx<=1; vx+=2) + for(int vy=-1; vy<=1; vy+=2) + for(int vz=-1; vz<=1; vz+=2) + { + const PxVec3 v = absPose.transform(PxVec3(PxReal(vx),PxReal(vy),PxReal(vz)).multiply(boxGeom.halfExtents)); + + if(worldPlane.distance(v) <= 0.0f) // PT: objects are defined as closed, so we return 'true' in case of equality + return true; + } + return false; +} + +static bool GeomOverlapCallback_PlaneConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::ePLANE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + PX_UNUSED(cache); + PX_UNUSED(geom0); + +// const PxPlaneGeometry& planeGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + //find plane normal in shape space of convex: + const PxTransform plane2convex = pose1.getInverse().transform(pose0); + + const PxPlane shapeSpacePlane = getPlane(plane2convex); + + PxReal minimum, maximum; + projectHull_(cm->getHull(), minimum, maximum, shapeSpacePlane.n, convexGeom.scale.toMat33()); + + return (minimum <= -shapeSpacePlane.d); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Capsule-vs-shape + +static bool GeomOverlapCallback_CapsuleCapsule(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCAPSULE); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom0 = static_cast(geom0); + const PxCapsuleGeometry& capsuleGeom1 = static_cast(geom1); + + // PT: move computation to local space for improved accuracy + const PxVec3 delta = pose1.p - pose0.p; + + // PT: TODO: remove this useless conversion + const PxVec3 capsuleHalfHeightVector0 = getCapsuleHalfHeightVector(pose0, capsuleGeom0); + const PxVec3 capsuleHalfHeightVector1 = getCapsuleHalfHeightVector(pose1, capsuleGeom1); + + const PxReal squareDist = distanceSegmentSegmentSquared(-capsuleHalfHeightVector0, capsuleHalfHeightVector0*2.0f, + delta-capsuleHalfHeightVector1, capsuleHalfHeightVector1*2.0f); + const PxReal r = capsuleGeom0.radius + capsuleGeom1.radius; + return squareDist <= r*r; // PT: objects are defined as closed, so we return 'true' in case of equality +} + +static bool GeomOverlapCallback_CapsuleBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxBoxGeometry& boxGeom = static_cast(geom1); + + // PT: move computation to local space for improved accuracy + const PxVec3 delta = pose1.p - pose0.p; + + // PT: TODO: remove this useless conversion + const PxVec3 capsuleHalfHeightVector = getCapsuleHalfHeightVector(pose0, capsuleGeom); + + // PT: TODO: remove this useless conversion + const PxMat33 obbRot(pose1.q); + + // PT: objects are defined as closed, so we return 'true' in case of equality + return distanceSegmentBoxSquared(capsuleHalfHeightVector, -capsuleHalfHeightVector, delta, boxGeom.halfExtents, obbRot) <= capsuleGeom.radius*capsuleGeom.radius; +} + +static bool GeomOverlapCallback_CapsuleConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + + PxVec3 cachedSepAxis; + PxVec3* tmp = getCachedAxis(cache); + if(tmp) + cachedSepAxis = *tmp; + else + cachedSepAxis = PxVec3(0,0,1.0f); + + const bool overlap = intersectCapsuleConvex(capsuleGeom, pose0, *cm, convexGeom.scale, pose1, &cachedSepAxis); + + if(cache && overlap) + cache->dir = cachedSepAxis; + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Box-vs-shape + +static bool GeomOverlapCallback_BoxBox(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eBOX); + PX_UNUSED(cache); + + const PxBoxGeometry& boxGeom0 = static_cast(geom0); + const PxBoxGeometry& boxGeom1 = static_cast(geom1); + + // PT: TODO: remove this useless conversion + return intersectOBBOBB( boxGeom0.halfExtents, pose0.p, PxMat33Padded(pose0.q), + boxGeom1.halfExtents, pose1.p, PxMat33Padded(pose1.q), true); +} + +static bool GeomOverlapCallback_BoxConvex(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + PxVec3 cachedSepAxis; + PxVec3* tmp = getCachedAxis(cache); + if(tmp) + cachedSepAxis = *tmp; + else + cachedSepAxis = PxVec3(0.0f, 0.0f, 1.0f); + + const bool overlap = intersectBoxConvex(boxGeom, pose0, *cm, convexGeom.scale, pose1, &cachedSepAxis); + + if(cache && overlap) + cache->dir = cachedSepAxis; + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Convex-vs-shape +static bool GeomOverlapCallback_ConvexConvex(GU_OVERLAP_FUNC_PARAMS) +{ + using namespace Ps::aos; + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eCONVEXMESH); + + const Vec3V zeroV = V3Zero(); + const PxConvexMeshGeometry& convexGeom0 = static_cast(geom0); + const PxConvexMeshGeometry& convexGeom1 = static_cast(geom1); + const ConvexMesh* cm0 = static_cast(convexGeom0.convexMesh); + const ConvexMesh* cm1 = static_cast(convexGeom1.convexMesh); + + bool overlap; + { + const ConvexHullData* hullData0 = &cm0->getHull(); + const ConvexHullData* hullData1 = &cm1->getHull(); + + const Vec3V vScale0 = V3LoadU_SafeReadW(convexGeom0.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat0 = QuatVLoadU(&convexGeom0.scale.rotation.x); + const Vec3V vScale1 = V3LoadU_SafeReadW(convexGeom1.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat1 = QuatVLoadU(&convexGeom1.scale.rotation.x); + + const QuatV q0 = QuatVLoadU(&pose0.q.x); + const Vec3V p0 = V3LoadU(&pose0.p.x); + + const QuatV q1 = QuatVLoadU(&pose1.q.x); + const Vec3V p1 = V3LoadU(&pose1.p.x); + + const PsTransformV transf0(p0, q0); + const PsTransformV transf1(p1, q1); + + const PsMatTransformV aToB(transf1.transformInv(transf0)); + + ConvexHullV convexHull0(hullData0, zeroV, vScale0, vQuat0, convexGeom0.scale.isIdentity()); + ConvexHullV convexHull1(hullData1, zeroV, vScale1, vQuat1, convexGeom1.scale.isIdentity()); + + Vec3V contactA, contactB, normal; + FloatV dist; + RelativeConvex convexA(convexHull0, aToB); + LocalConvex convexB(convexHull1); + + GjkStatus status = gjk(convexA, convexB, aToB.p, FZero(), contactA, contactB, normal, dist); + overlap = (status == GJK_CONTACT); + } + + return updateTriggerCache(overlap, cache); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool GeomOverlapCallback_NotSupported(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ALWAYS_ASSERT_MESSAGE("NOT SUPPORTED"); + PX_UNUSED(cache); + PX_UNUSED(pose0); + PX_UNUSED(pose1); + PX_UNUSED(geom0); + PX_UNUSED(geom1); + return false; +} + +static bool GeomOverlapCallback_HeightfieldUnregistered(GU_OVERLAP_FUNC_PARAMS) +{ + PX_UNUSED(cache); + PX_UNUSED(geom0); + PX_UNUSED(geom1); + PX_UNUSED(pose0); + PX_UNUSED(pose1); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Overlap test called with height fields unregistered "); + return false; +} + +bool GeomOverlapCallback_SphereMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_CapsuleMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_BoxMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_ConvexMesh (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_SphereHeightfield (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_CapsuleHeightfield (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_BoxHeightfield (GU_OVERLAP_FUNC_PARAMS); +bool GeomOverlapCallback_ConvexHeightfield (GU_OVERLAP_FUNC_PARAMS); + +GeomOverlapTable gGeomOverlapMethodTable[] = +{ + //PxGeometryType::eSPHERE + { + GeomOverlapCallback_SphereSphere, //PxGeometryType::eSPHERE + GeomOverlapCallback_SpherePlane, //PxGeometryType::ePLANE + GeomOverlapCallback_SphereCapsule, //PxGeometryType::eCAPSULE + GeomOverlapCallback_SphereBox, //PxGeometryType::eBOX + GeomOverlapCallback_SphereConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_SphereMesh, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + GeomOverlapCallback_NotSupported, //PxGeometryType::ePLANE + GeomOverlapCallback_PlaneCapsule, //PxGeometryType::eCAPSULE + GeomOverlapCallback_PlaneBox, //PxGeometryType::eBOX + GeomOverlapCallback_PlaneConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + GeomOverlapCallback_CapsuleCapsule, //PxGeometryType::eCAPSULE + GeomOverlapCallback_CapsuleBox, //PxGeometryType::eBOX + GeomOverlapCallback_CapsuleConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_CapsuleMesh, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + GeomOverlapCallback_BoxBox, //PxGeometryType::eBOX + GeomOverlapCallback_BoxConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_BoxMesh, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + GeomOverlapCallback_ConvexConvex, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_ConvexMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + GeomOverlapCallback_HeightfieldUnregistered, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + GeomOverlapCallback_NotSupported, //PxGeometryType::eHEIGHTFIELD + }, +}; + +const GeomOverlapTable* Gu::getOverlapFuncTable() +{ + return gGeomOverlapMethodTable; +} + +void registerHeightFields_Raycasts(); +void registerHeightFields_Sweeps(); +void Gu::registerHeightFields() +{ + registerHeightFields_Raycasts(); + registerHeightFields_Sweeps(); + + gGeomOverlapMethodTable[PxGeometryType::eSPHERE][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_SphereHeightfield; + gGeomOverlapMethodTable[PxGeometryType::eCAPSULE][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_CapsuleHeightfield; + gGeomOverlapMethodTable[PxGeometryType::eBOX][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_BoxHeightfield; + gGeomOverlapMethodTable[PxGeometryType::eCONVEXMESH][PxGeometryType::eHEIGHTFIELD] = GeomOverlapCallback_ConvexHeightfield; +} diff --git a/src/PhysX/physx/source/geomutils/src/GuOverlapTests.h b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.h new file mode 100644 index 000000000..dd59a44c2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.h @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_OVERLAP_TESTS_H +#define GU_OVERLAP_TESTS_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxAssert.h" +#include "CmPhysXCommon.h" +#include "PxGeometry.h" +#include "PsFoundation.h" + +namespace physx +{ +namespace Gu +{ + class Capsule; + class Sphere; + + PX_PHYSX_COMMON_API bool checkOverlapAABB_triangleGeom (const PxGeometry& triGeom, const PxTransform& pose, const PxBounds3& box); + PX_PHYSX_COMMON_API bool checkOverlapAABB_heightFieldGeom (const PxGeometry& hfGeom, const PxTransform& pose, const PxBounds3& box); + + // PT: this is just a shadow of what it used to be. We currently don't use TRIGGER_INSIDE anymore, but I leave it for now, + // since I really want to put this back the way it was before. + enum TriggerStatus + { + TRIGGER_DISJOINT, + TRIGGER_INSIDE, + TRIGGER_OVERLAP + }; + + // PT: currently only used for convex triggers + struct TriggerCache + { + PxVec3 dir; + PxU16 state; + PxU16 gjkState; //gjk succeed or fail + }; + + // PT: we use a define to be able to quickly change the signature of all overlap functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] geom0 first geometry object + // \param[in] pose0 pose of first geometry object + // \param[in] geom1 second geometry object + // \param[in] pose1 pose of second geometry object + // \param[in] cache optional cached data for triggers + #define GU_OVERLAP_FUNC_PARAMS const PxGeometry& geom0, const PxTransform& pose0, \ + const PxGeometry& geom1, const PxTransform& pose1, \ + Gu::TriggerCache* cache + + // PT: function pointer for Geom-indexed overlap functions + // See GU_OVERLAP_FUNC_PARAMS for function parameters details. + // \return true if an overlap was found, false otherwise + typedef bool (*GeomOverlapFunc) (GU_OVERLAP_FUNC_PARAMS); + + // PT: typedef for a bundle of all overlap functions, i.e. the function table itself (indexed by geom-type). + typedef GeomOverlapFunc GeomOverlapTable[PxGeometryType::eGEOMETRY_COUNT]; + + // PT: retrieves the overlap function table (for access by external non-Gu modules) + PX_PHYSX_COMMON_API const GeomOverlapTable* getOverlapFuncTable(); + + // dynamic registration of height fields + PX_PHYSX_COMMON_API void registerHeightFields(); + + PX_FORCE_INLINE bool overlap( const PxGeometry& geom0, const PxTransform& pose0, + const PxGeometry& geom1, const PxTransform& pose1, + const GeomOverlapTable* PX_RESTRICT overlapFuncs) + { + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "Gu::overlap(): pose0 is not valid.", false); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "Gu::overlap(): pose1 is not valid.", false); + + if(geom0.getType() > geom1.getType()) + { + GeomOverlapFunc overlapFunc = overlapFuncs[geom1.getType()][geom0.getType()]; + PX_ASSERT(overlapFunc); + return overlapFunc(geom1, pose1, geom0, pose0, NULL); + } + else + { + GeomOverlapFunc overlapFunc = overlapFuncs[geom0.getType()][geom1.getType()]; + PX_ASSERT(overlapFunc); + return overlapFunc(geom0, pose0, geom1, pose1, NULL); + } + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp b/src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp new file mode 100644 index 000000000..aba0a6b91 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp @@ -0,0 +1,564 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuMidphaseInterface.h" +#include "GuInternal.h" +#include "PxSphereGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "GuIntersectionRayCapsule.h" +#include "GuIntersectionRaySphere.h" +#include "GuIntersectionRayPlane.h" +#include "GuHeightFieldUtil.h" +#include "GuDistancePointSegment.h" +#include "GuConvexMesh.h" +#include "CmScaling.h" + +using namespace physx; +using namespace Gu; + +////////////////////////////////////////////////// raycasts ////////////////////////////////////////////////////////////////// +PxU32 raycast_box(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_ASSERT(maxHits && hits); + const PxBoxGeometry& boxGeom = static_cast(geom); + + const PxTransform& absPose = pose; + + PxVec3 localOrigin = rayOrigin - absPose.p; + localOrigin = absPose.q.rotateInv(localOrigin); + + const PxVec3 localDir = absPose.q.rotateInv(rayDir); + + PxVec3 localImpact; + PxReal t; + PxU32 rval = rayAABBIntersect2(-boxGeom.halfExtents, boxGeom.halfExtents, localOrigin, localDir, localImpact, t); + if(!rval) + return 0; + + if(t>maxDist) + return 0; + + hits->distance = t; //worldRay.orig.distance(hit.worldImpact); //should be the same, assuming ray dir was normalized!! + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + + PxHitFlags outFlags = PxHitFlags(0); + if((hitFlags & PxHitFlag::ePOSITION)) + { + outFlags |= PxHitFlag::ePOSITION; + if(t!=0.0f) + hits->position = absPose.transform(localImpact); + else + hits->position = rayOrigin; + } + + // Compute additional information if needed + if(hitFlags & PxHitFlag::eNORMAL) + { + outFlags |= PxHitFlag::eNORMAL; + + //Because rayAABBIntersect2 set t = 0 if start point inside shape + if(t == 0) + { + hits->normal = -rayDir; + } + else + { + //local space normal is: + rval--; + PxVec3 n(0.0f); + n[rval] = PxReal((localImpact[rval] > 0.0f) ? 1.0f : -1.0f); + hits->normal = absPose.q.rotate(n); + } + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = outFlags; + return 1; +} + +PxU32 raycast_sphere(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + PX_ASSERT(maxHits && hits); + + const PxSphereGeometry& sphereGeom = static_cast(geom); + + if(!intersectRaySphere(rayOrigin, rayDir, maxDist, pose.p, sphereGeom.radius, hits->distance, &hits->position)) + return 0; + + /* // PT: should be useless now + hit.distance = worldRay.orig.distance(hit.worldImpact); + if(hit.distance>maxDist) + return false; + */ + // PT: we can't avoid computing the position here since it's needed to compute the normal anyway + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + + // Compute additional information if needed + PxHitFlags outFlags = PxHitFlag::ePOSITION; + if(hitFlags & PxHitFlag::eNORMAL) + { + // User requested impact normal + //Because intersectRaySphere set distance = 0 if start point inside shape + if(hits->distance == 0.0f) + { + hits->normal = -rayDir; + } + else + { + hits->normal = hits->position - pose.p; + hits->normal.normalize(); + } + outFlags |= PxHitFlag::eNORMAL; + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = outFlags; + + return 1; +} + +PxU32 raycast_capsule(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + PX_ASSERT(maxHits && hits); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + // TODO: PT: could we simplify this ? + Capsule capsule; + getCapsuleSegment(pose, capsuleGeom, capsule); + capsule.radius = capsuleGeom.radius; + + PxReal t = 0.0f; + if(!intersectRayCapsule(rayOrigin, rayDir, capsule, t)) + return 0; + + if(t<0.0f || t>maxDist) + return 0; + + // PT: we can't avoid computing the position here since it's needed to compute the normal anyway + hits->position = rayOrigin + rayDir*t; // PT: will be rayOrigin for t=0.0f (i.e. what the spec wants) + hits->distance = t; + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + + // Compute additional information if needed + PxHitFlags outFlags = PxHitFlag::ePOSITION; + if(hitFlags & PxHitFlag::eNORMAL) + { + outFlags |= PxHitFlag::eNORMAL; + + if(t==0.0f) + { + hits->normal = -rayDir; + } + else + { + PxReal capsuleT; + distancePointSegmentSquared(capsule, hits->position, &capsuleT); + capsule.computePoint(hits->normal, capsuleT); + hits->normal = hits->position - hits->normal; //this should never be zero. It should have a magnitude of the capsule radius. + hits->normal.normalize(); + } + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = outFlags; + + return 1; +} + +PxU32 raycast_plane(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(hitFlags); + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_ASSERT(maxHits && hits); + PX_UNUSED(geom); +// const PxPlaneGeometry& planeGeom = static_cast(geom); + + // Perform backface culling so that we can pick objects beyond planes + const PxPlane plane = getPlane(pose); + if(rayDir.dot(plane.n)>=0.0f) + return false; + + PxReal distanceAlongLine; + if(!intersectRayPlane(rayOrigin, rayDir, plane, distanceAlongLine, &hits->position)) + return 0; + + /* + PxReal test = worldRay.orig.distance(hit.worldImpact); + + PxReal dd; + PxVec3 pp; + PxSegmentPlaneIntersect(worldRay.orig, worldRay.orig+worldRay.dir*1000.0f, plane, dd, pp); + */ + + if(distanceAlongLine<0.0f) + return 0; + + if(distanceAlongLine>maxDist) + return 0; + + hits->distance = distanceAlongLine; + hits->faceIndex = 0xffffffff; + hits->u = 0.0f; + hits->v = 0.0f; + hits->flags = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL; + hits->normal = plane.n; + return 1; +} + +PxU32 raycast_convexMesh(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(maxHits); + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + PX_ASSERT(maxHits && hits); + PX_ASSERT(PxAbs(rayDir.magnitudeSquared()-1)<1e-4f); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + + PxRaycastHit& hit = *hits; + + //scaling: transform the ray to vertex space + const Cm::Matrix34 world2vertexSkew = convexGeom.scale.getInverse() * pose.getInverse(); + + //ConvexMesh* cmesh = static_cast(convexGeom.convexMesh); + const PxU32 nPolys = convexMesh->getNbPolygonsFast(); + const HullPolygonData* PX_RESTRICT polysEA = convexMesh->getPolygons(); + const HullPolygonData* polys = polysEA; + + const PxVec3 vrayOrig = world2vertexSkew.transform(rayOrigin); + const PxVec3 vrayDir = world2vertexSkew.rotate(rayDir); + + /* + Purely convex planes based algorithm + Iterate all planes of convex, with following rules: + * determine of ray origin is inside them all or not. + * planes parallel to ray direction are immediate early out if we're on the outside side (plane normal is sep axis) + * else + - for all planes the ray direction "enters" from the front side, track the one furthest along the ray direction (A) + - for all planes the ray direction "exits" from the back side, track the one furthest along the negative ray direction (B) + if the ray origin is outside the convex and if along the ray, A comes before B, the directed line stabs the convex at A + */ + bool originInsideAllPlanes = true; + PxReal latestEntry = -FLT_MAX; + PxReal earliestExit = FLT_MAX; +// PxU32 bestPolygonIndex = 0; + hit.faceIndex = 0xffffffff; + + for(PxU32 i=0;i 0.0f) + originInsideAllPlanes = false; //origin not behind plane == ray starts outside the convex. + + if(dn > 1E-7f) //the ray direction "exits" from the back side + { + earliestExit = physx::intrinsics::selectMin(earliestExit, distAlongRay); + } + else if(dn < -1E-7f) //the ray direction "enters" from the front side + { + if(distAlongRay > latestEntry) + { + latestEntry = distAlongRay; + hit.faceIndex = i; + } + } + else + { + //plane normal and ray dir are orthogonal + if(distToPlane > 0.0f) + return 0; //a plane is parallel with ray -- and we're outside the ray -- we definitely miss the entire convex! + } + } + + if(originInsideAllPlanes) //ray starts inside convex + { + hit.distance = 0.0f; + hit.faceIndex = 0xffffffff; + hit.u = 0.0f; + hit.v = 0.0f; + hit.position = rayOrigin; + hit.normal = -rayDir; + hit.flags = PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + return 1; + } + + // AP: changed to latestEntry < maxDist-1e-5f so that we have a conservatively negative result near end of ray + if(latestEntry < earliestExit && latestEntry > 0.0f && latestEntry < maxDist-1e-5f) + { + PxHitFlags outFlags = PxHitFlag::eFACE_INDEX; + if(hitFlags & PxHitFlag::ePOSITION) + { + outFlags |= PxHitFlag::ePOSITION; + const PxVec3 pointOnPlane = vrayOrig + latestEntry * vrayDir; + hit.position = pose.transform(convexGeom.scale.toMat33() * pointOnPlane); + } + hit.distance = latestEntry; + hit.u = 0.0f; + hit.v = 0.0f; + hit.normal = PxVec3(0.0f); + + // Compute additional information if needed + if(hitFlags & PxHitFlag::eNORMAL) + { + outFlags |= PxHitFlag::eNORMAL; + //when we have nonuniform scaling we actually have to transform by the transpose of the inverse of vertex2worldSkew.M == transpose of world2vertexSkew: + hit.normal = world2vertexSkew.rotateTranspose(polys[hit.faceIndex].mPlane.n); + hit.normal.normalize(); + } + hit.flags = outFlags; + return 1; + } + return 0; +} + +PxU32 raycast_triangleMesh(GU_RAY_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + PX_ASSERT(PxAbs(rayDir.magnitudeSquared()-1)<1e-4f); + + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + return Midphase::raycastTriangleMesh(meshData, meshGeom, pose, rayOrigin, rayDir, maxDist, hitFlags, maxHits, hits); +} + +namespace +{ + struct HFTraceSegmentCallback + { + PX_NOCOPY(HFTraceSegmentCallback) + public: + PxRaycastHit* mHits; + const PxU32 mMaxHits; + PxU32 mNbHits; + const HeightFieldUtil& mUtil; + const PxTransform& mPose; + const PxVec3& mRayDir; + const PxVec3& mLocalRayDir; + const PxVec3& mLocalRayOrig; + const PxHitFlags mHitFlags; + const bool mIsDoubleSided; + + HFTraceSegmentCallback( PxRaycastHit* hits, PxU32 maxHits, const PxHitFlags hitFlags, const HeightFieldUtil& hfUtil, const PxTransform& pose, + const PxVec3& rayDir, const PxVec3& localRayDir, const PxVec3& localRayOrig, + bool isDoubleSided) : + mHits (hits), + mMaxHits (maxHits), + mNbHits (0), + mUtil (hfUtil), + mPose (pose), + mRayDir (rayDir), + mLocalRayDir (localRayDir), + mLocalRayOrig (localRayOrig), + mHitFlags (hitFlags), + mIsDoubleSided (isDoubleSided) + { + PX_ASSERT(maxHits > 0); + } + + PX_FORCE_INLINE bool onEvent(PxU32 , PxU32*) + { + return true; + } + + PX_FORCE_INLINE bool underFaceHit(const HeightFieldUtil&, const PxVec3&, const PxVec3&, PxF32, PxF32, PxF32, PxU32) + { + return true; // true means continue traversal + } + + PxAgain faceHit(const HeightFieldUtil&, const PxVec3& aHitPoint, PxU32 aTriangleIndex, PxReal u, PxReal v) + { + // traversal is strictly sorted so there's no need to sort hits + if(mNbHits >= mMaxHits) + return false; // false = stop traversal + + PxRaycastHit& hit = mHits[mNbHits++]; + hit.position = aHitPoint; + hit.faceIndex = aTriangleIndex; + hit.u = u; + hit.v = v; + hit.flags = PxHitFlag::eUV | PxHitFlag::eFACE_INDEX; // UVs and face index are always set + + if(mHitFlags & PxHitFlag::eNORMAL) + { + // We need the normal for the dot product. + PxVec3 normal = mPose.q.rotate(mUtil.getNormalAtShapePoint(hit.position.x, hit.position.z)); + normal.normalize(); + if(mIsDoubleSided && normal.dot(mRayDir) > 0.0f) // comply with normal spec for double sided (should always face opposite rayDir) + hit.normal = -normal; + else + hit.normal = normal; + hit.flags |= PxHitFlag::eNORMAL; + } + + hit.distance = physx::intrinsics::selectMax(0.f, (hit.position - mLocalRayOrig).dot(mLocalRayDir)); + + if(mHitFlags & PxHitFlag::ePOSITION) + { + hit.position = mPose.transform(hit.position); + hit.flags |= PxHitFlag::ePOSITION; + } + return (mNbHits < mMaxHits); // true = continue traversal, false = stop traversal + } + }; +} + +PxU32 raycast_heightField(GU_RAY_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + PX_ASSERT(maxHits && hits); + PX_UNUSED(maxHits); + + const PxHeightFieldGeometry& hfGeom = static_cast(geom); + + const PxTransform invAbsPose = pose.getInverse(); + const PxVec3 localRayOrig = invAbsPose.transform(rayOrigin); + const PxVec3 localRayDir = invAbsPose.rotate(rayDir); + + const bool isDoubleSided = hfGeom.heightFieldFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED); + const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES); + + const HeightFieldTraceUtil hfUtil(hfGeom); + + PxVec3 normRayDir = localRayDir; + normRayDir.normalizeSafe(); // nothing will happen if length is < PX_NORMALIZATION_EPSILON + + // pretest if we intersect HF bounds. If no early exit, if yes move the origin and shorten the maxDist + // to deal with precision issues with large maxDist + PxBounds3 hfLocalBounds; + hfUtil.computeLocalBounds(hfLocalBounds); + + // PT: inflate the bounds like we do in the scene-tree (see PX-1179) + const PxVec3 center = hfLocalBounds.getCenter(); + const PxVec3 extents = hfLocalBounds.getExtents() * 1.01f; //SQ_PRUNER_INFLATION; + hfLocalBounds.minimum = center - extents; + hfLocalBounds.maximum = center + extents; + + PxVec3 localImpact; + PxReal t; // closest intersection, t==0 hit inside + PxU32 rval = rayAABBIntersect2(hfLocalBounds.minimum, hfLocalBounds.maximum, localRayOrig, localRayDir, localImpact, t); + // early exit we miss the AABB + if (!rval) + return 0; + if (t > maxDist) + return 0; + + // PT: if eMESH_ANY is used then eMESH_MULTIPLE won't be, and we'll stop the query after 1 hit is found. There is no difference + // between 'any hit' and 'closest hit' for HFs since hits are reported in order. + HFTraceSegmentCallback callback(hits, hitFlags.isSet(PxHitFlag::eMESH_MULTIPLE) ? maxHits : 1, hitFlags, hfUtil, pose, + rayDir, localRayDir, localRayOrig, + isDoubleSided); // make sure we return only 1 hit without eMESH_MULTIPLE + + PxReal offset = 0.0f; + PxReal maxDistOffset = maxDist; + PxVec3 localRayOrigOffset = localRayOrig; + + // if we don't start inside the AABB box, offset the start pos, because of precision issues with large maxDist + if(t > 0.0f) + { + offset = t - GU_RAY_SURFACE_OFFSET; + // move the rayOrig to offset start pos + localRayOrigOffset = localRayOrig + normRayDir*offset; + } + + // shorten the maxDist of the offset that was cut off and clip it + // we pick either the original maxDist, if maxDist is huge we clip it + maxDistOffset = PxMin(maxDist - offset, GU_RAY_SURFACE_OFFSET + 2.0f * PxMax(hfLocalBounds.maximum.x - hfLocalBounds.minimum.x, PxMax(hfLocalBounds.maximum.y - hfLocalBounds.minimum.y, hfLocalBounds.maximum.z - hfLocalBounds.minimum.z))); + + hfUtil.traceSegment(localRayOrigOffset, normRayDir, maxDistOffset, + &callback, hfLocalBounds, !bothSides); + return callback.mNbHits; +} + +static PxU32 raycast_heightField_unregistered(GU_RAY_FUNC_PARAMS) +{ + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(rayOrigin); + PX_UNUSED(rayDir); + PX_UNUSED(maxDist); + PX_UNUSED(hitFlags); + PX_UNUSED(maxHits); + PX_UNUSED(hits); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Raycast test called with height fields unregistered "); + return 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// PT: table is not static because it's accessed as 'extern' within Gu (bypassing the function call). +RaycastFunc gRaycastMap[PxGeometryType::eGEOMETRY_COUNT] = +{ + raycast_sphere, + raycast_plane, + raycast_capsule, + raycast_box, + raycast_convexMesh, + raycast_triangleMesh, + raycast_heightField_unregistered +}; + +// PT: the function is used by external modules (Np, CCT, Sq) +const Gu::GeomRaycastTable& Gu::getRaycastFuncTable() +{ + return gRaycastMap; +} + +void registerHeightFields_Raycasts() +{ + gRaycastMap[PxGeometryType::eHEIGHTFIELD] = raycast_heightField; +} diff --git a/src/PhysX/physx/source/geomutils/src/GuSerialize.cpp b/src/PhysX/physx/source/geomutils/src/GuSerialize.cpp new file mode 100644 index 000000000..83205d8a2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSerialize.cpp @@ -0,0 +1,381 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "PsUtilities.h" +#include "GuSerialize.h" +#include "PsUserAllocated.h" +#include "PsAllocator.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +void physx::readChunk(PxI8& a, PxI8& b, PxI8& c, PxI8& d, PxInputStream& stream) +{ + stream.read(&a, sizeof(PxI8)); + stream.read(&b, sizeof(PxI8)); + stream.read(&c, sizeof(PxI8)); + stream.read(&d, sizeof(PxI8)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxU16 physx::readWord(bool mismatch, PxInputStream& stream) +{ + PxU16 d; + stream.read(&d, sizeof(PxU16)); + + if(mismatch) + flip(d); + return d; +} + +PxU32 physx::readDword(bool mismatch, PxInputStream& stream) +{ + PxU32 d; + stream.read(&d, sizeof(PxU32)); + + if(mismatch) + flip(d); + return d; +} + +PxF32 physx::readFloat(bool mismatch, PxInputStream& stream) +{ + union + { + PxU32 d; + PxF32 f; + } u; + + stream.read(&u.d, sizeof(PxU32)); + + if(mismatch) + flip(u.d); + return u.f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void physx::writeWord(PxU16 value, bool mismatch, PxOutputStream& stream) +{ + if(mismatch) + flip(value); + stream.write(&value, sizeof(PxU16)); +} + +void physx::writeDword(PxU32 value, bool mismatch, PxOutputStream& stream) +{ + if(mismatch) + flip(value); + stream.write(&value, sizeof(PxU32)); +} + +void physx::writeFloat(PxF32 value, bool mismatch, PxOutputStream& stream) +{ + if(mismatch) + flip(value); + stream.write(&value, sizeof(PxF32)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool physx::readFloatBuffer(PxF32* dest, PxU32 nbFloats, bool mismatch, PxInputStream& stream) +{ + stream.read(dest, sizeof(PxF32)*nbFloats); + if(mismatch) + { + for(PxU32 i=0;imaxIndex) + maxIndex = currentIndex; + } + return maxIndex; +} +PxU16 physx::computeMaxIndex(const PxU16* indices, PxU32 nbIndices) +{ + PxU16 maxIndex=0; + while(nbIndices--) + { + PxU16 currentIndex = *indices++; + if(currentIndex>maxIndex) + maxIndex = currentIndex; + } + return maxIndex; +} + +void physx::storeIndices(PxU32 maxIndex, PxU32 nbIndices, const PxU32* indices, PxOutputStream& stream, bool platformMismatch) +{ + if(maxIndex<=0xff) + { + for(PxU32 i=0;i(PxAlloca(nbIndices*sizeof(PxU8))); + stream.read(tmp, nbIndices*sizeof(PxU8)); + for(PxU32 i=0;i(PxAlloca(nbIndices*sizeof(PxU16))); + readWordBuffer(tmp, nbIndices, platformMismatch, stream); + for(PxU32 i=0;i(PxAlloca(nbIndices*sizeof(PxU8))); + stream.read(tmp, nbIndices*sizeof(PxU8)); + for(PxU32 i=0;i(&v); + PxU8 temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + PX_INLINE void flip(PxI16& v) + { + PxI8* b = reinterpret_cast(&v); + PxI8 temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + PX_INLINE void flip(PxU32& v) + { + PxU8* b = reinterpret_cast(&v); + + PxU8 temp = b[0]; + b[0] = b[3]; + b[3] = temp; + temp = b[1]; + b[1] = b[2]; + b[2] = temp; + } + + // MS: It is important to modify the value directly and not use a temporary variable or a return + // value. The reason for this is that a flipped float might have a bit pattern which indicates + // an invalid float. If such a float is assigned to another float, the bit pattern + // can change again (maybe to map invalid floats to a common invalid pattern?). + // When reading the float and flipping again, the changed bit pattern will result in a different + // float than the original one. + PX_INLINE void flip(PxF32& v) + { + PxU8* b = reinterpret_cast(&v); + + PxU8 temp = b[0]; + b[0] = b[3]; + b[3] = temp; + temp = b[1]; + b[1] = b[2]; + b[2] = temp; + } + + PX_INLINE void writeChunk(PxI8 a, PxI8 b, PxI8 c, PxI8 d, PxOutputStream& stream) + { + stream.write(&a, sizeof(PxI8)); + stream.write(&b, sizeof(PxI8)); + stream.write(&c, sizeof(PxI8)); + stream.write(&d, sizeof(PxI8)); + } + + void readChunk(PxI8& a, PxI8& b, PxI8& c, PxI8& d, PxInputStream& stream); + + PxU16 readWord(bool mismatch, PxInputStream& stream); +PX_PHYSX_COMMON_API PxU32 readDword(bool mismatch, PxInputStream& stream); + PxF32 readFloat(bool mismatch, PxInputStream& stream); + +PX_PHYSX_COMMON_API void writeWord(PxU16 value, bool mismatch, PxOutputStream& stream); +PX_PHYSX_COMMON_API void writeDword(PxU32 value, bool mismatch, PxOutputStream& stream); +PX_PHYSX_COMMON_API void writeFloat(PxF32 value, bool mismatch, PxOutputStream& stream); + + bool readFloatBuffer(PxF32* dest, PxU32 nbFloats, bool mismatch, PxInputStream& stream); +PX_PHYSX_COMMON_API void writeFloatBuffer(const PxF32* src, PxU32 nb, bool mismatch, PxOutputStream& stream); +PX_PHYSX_COMMON_API void writeWordBuffer(const PxU16* src, PxU32 nb, bool mismatch, PxOutputStream& stream); + void readWordBuffer(PxU16* dest, PxU32 nb, bool mismatch, PxInputStream& stream); + +PX_PHYSX_COMMON_API bool writeHeader(PxI8 a, PxI8 b, PxI8 c, PxI8 d, PxU32 version, bool mismatch, PxOutputStream& stream); + bool readHeader(PxI8 a, PxI8 b, PxI8 c, PxI8 d, PxU32& version, bool& mismatch, PxInputStream& stream); + +PX_INLINE bool readIntBuffer(PxU32* dest, PxU32 nbInts, bool mismatch, PxInputStream& stream) +{ + return readFloatBuffer(reinterpret_cast(dest), nbInts, mismatch, stream); +} + +PX_INLINE void writeIntBuffer(const PxU32* src, PxU32 nb, bool mismatch, PxOutputStream& stream) +{ + writeFloatBuffer(reinterpret_cast(src), nb, mismatch, stream); +} + +PX_INLINE bool ReadDwordBuffer(PxU32* dest, PxU32 nb, bool mismatch, PxInputStream& stream) +{ + return readFloatBuffer(reinterpret_cast(dest), nb, mismatch, stream); +} + +PX_INLINE void WriteDwordBuffer(const PxU32* src, PxU32 nb, bool mismatch, PxOutputStream& stream) +{ + writeFloatBuffer(reinterpret_cast(src), nb, mismatch, stream); +} + +PX_PHYSX_COMMON_API PxU32 computeMaxIndex(const PxU32* indices, PxU32 nbIndices); +PX_PHYSX_COMMON_API PxU16 computeMaxIndex(const PxU16* indices, PxU32 nbIndices); +PX_PHYSX_COMMON_API void storeIndices(PxU32 maxIndex, PxU32 nbIndices, const PxU32* indices, PxOutputStream& stream, bool platformMismatch); +PX_PHYSX_COMMON_API void readIndices(PxU32 maxIndex, PxU32 nbIndices, PxU32* indices, PxInputStream& stream, bool platformMismatch); + + // PT: see PX-1163 + PX_FORCE_INLINE bool readBigEndianVersionNumber(PxInputStream& stream, bool mismatch_, PxU32& fileVersion, bool& mismatch) + { + // PT: allright this is going to be subtle: + // - in version 1 the data was always saved in big-endian format + // - *including the version number*! + // - so we cannot just read the version "as usual" using the passed mismatch param + + // PT: mismatch value for version 1 + mismatch = (shdfnd::littleEndian() == 1); + + const PxU32 rawFileVersion = readDword(false, stream); + if(rawFileVersion==1) + { + // PT: this is a version-1 file with no flip + fileVersion = 1; + PX_ASSERT(!mismatch); + } + else + { + PxU32 fileVersionFlipped = rawFileVersion; + flip(fileVersionFlipped); + if(fileVersionFlipped==1) + { + // PT: this is a version-1 file with flip + fileVersion = 1; + PX_ASSERT(mismatch); + } + else + { + // PT: this is at least version 2 so we can process it "as usual" + mismatch = mismatch_; + fileVersion = mismatch_ ? fileVersionFlipped : rawFileVersion; + } + } + + PX_ASSERT(fileVersion<=3); + if(fileVersion>3) + return false; + return true; + } + +// PT: TODO: copied from IceSerialize.h, still needs to be refactored/cleaned up. +namespace Gu +{ + PX_PHYSX_COMMON_API bool WriteHeader(PxU8 a, PxU8 b, PxU8 c, PxU8 d, PxU32 version, bool mismatch, PxOutputStream& stream); + PX_PHYSX_COMMON_API bool ReadHeader(PxU8 a_, PxU8 b_, PxU8 c_, PxU8 d_, PxU32& version, bool& mismatch, PxInputStream& stream); + + PX_PHYSX_COMMON_API void StoreIndices(PxU32 maxIndex, PxU32 nbIndices, const PxU32* indices, PxOutputStream& stream, bool platformMismatch); + void ReadIndices(PxU32 maxIndex, PxU32 nbIndices, PxU32* indices, PxInputStream& stream, bool platformMismatch); + + PX_PHYSX_COMMON_API void StoreIndices(PxU16 maxIndex, PxU32 nbIndices, const PxU16* indices, PxOutputStream& stream, bool platformMismatch); + void ReadIndices(PxU16 maxIndex, PxU32 nbIndices, PxU16* indices, PxInputStream& stream, bool platformMismatch); +} + + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuSphere.h b/src/PhysX/physx/source/geomutils/src/GuSphere.h new file mode 100644 index 000000000..c249f4e21 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSphere.h @@ -0,0 +1,108 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SPHERE_H +#define GU_SPHERE_H +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +/** +\brief Represents a sphere defined by its center point and radius. +*/ +namespace Gu +{ + class Sphere + { + public: + /** + \brief Constructor + */ + PX_INLINE Sphere() + { + } + + /** + \brief Constructor + */ + PX_INLINE Sphere(const PxVec3& _center, PxF32 _radius) : center(_center), radius(_radius) + { + } + /** + \brief Copy constructor + */ + PX_INLINE Sphere(const Sphere& sphere) : center(sphere.center), radius(sphere.radius) + { + } + /** + \brief Destructor + */ + PX_INLINE ~Sphere() + { + } + + PX_INLINE void set(const PxVec3& _center, float _radius) { center = _center; radius = _radius; } + + /** + \brief Checks the sphere is valid. + + \return true if the sphere is valid + */ + PX_INLINE bool isValid() const + { + // Consistency condition for spheres: Radius >= 0.0f + return radius >= 0.0f; + } + + /** + \brief Tests if a point is contained within the sphere. + + \param[in] p the point to test + \return true if inside the sphere + */ + PX_INLINE bool contains(const PxVec3& p) const + { + return (center-p).magnitudeSquared() <= radius*radius; + } + + PxVec3 center; //!< Sphere's center + PxF32 radius; //!< Sphere's radius + }; +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp new file mode 100644 index 000000000..d2e8cb917 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp @@ -0,0 +1,1218 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "PxConvexMeshGeometry.h" +#include "GuConvexMesh.h" +#include "GuSweepSharedTests.h" +#include "GuConvexUtilsInternal.h" +#include "GuTriangleMesh.h" +#include "GuVecBox.h" +#include "GuVecTriangle.h" +#include "GuVecConvexHullNoScale.h" +#include "GuMidphaseInterface.h" +#include "GuPCMContactConvexCommon.h" +#include "GuSweepMTD.h" +#include "GuPCMShapeConvex.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistancePointSegment.h" +#include "GuInternal.h" +#include "GuConvexEdgeFlags.h" + +using namespace physx; +using namespace Gu; + +// PT: TODO: refactor with GuMTD.cpp versions +static PX_FORCE_INLINE PxF32 manualNormalize(PxVec3& mtd, const PxVec3& normal, PxReal lenSq) +{ + const PxF32 len = PxSqrt(lenSq); + + // We do a *manual* normalization to check for singularity condition + if(lenSq < 1e-6f) + mtd = PxVec3(1.0f, 0.0f, 0.0f); // PT: zero normal => pick up random one + else + mtd = normal * 1.0f / len; + + return len; +} + +static PX_FORCE_INLINE void getScaledTriangle(const PxTriangleMeshGeometry& triGeom, const Cm::Matrix34& vertex2worldSkew, bool flipsNormal, PxTriangle& triangle, PxTriangleID triangleIndex) +{ + TriangleMesh* tm = static_cast(triGeom.triangleMesh); + tm->computeWorldTriangle(triangle, triangleIndex, vertex2worldSkew, flipsNormal); +} + +#define BATCH_TRIANGLE_NUMBER 32u + +struct MTDTriangle : public PxTriangle +{ +public: + PxU8 extraTriData;//active edge flag data +}; + +struct MeshMTDGenerationCallback : MeshHitCallback +{ +public: + + Ps::Array& container; + + MeshMTDGenerationCallback(Ps::Array& tempContainer) + : MeshHitCallback(CallbackMode::eMULTIPLE), container(tempContainer) + { + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3&, const PxVec3&, const PxVec3&, PxReal&, const PxU32*) + { + container.pushBack(hit.faceIndex); + + return true; + } + + void operator=(const MeshMTDGenerationCallback&) {} +}; + +static bool getMTDPerTriangle(const MeshPersistentContact* manifoldContacts, const PxU32 numContacts, const PxU32 triangleIndex, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, PxU32& faceIndex, Ps::aos::FloatV& deepestPen) +{ + using namespace Ps::aos; + + FloatV deepest = V4GetW(manifoldContacts[0].mLocalNormalPen); + PxU32 index = 0; + for(PxU32 k=1; k& tempContainer) +{ + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + Box vertexSpaceBox; + computeVertexSpaceOBB(vertexSpaceBox, bound, pose, meshGeom.scale); + + MeshMTDGenerationCallback callback(tempContainer); + Midphase::intersectOBB(meshData, vertexSpaceBox, callback, true); +} + +// PT: TODO: refactor with EntityReportContainerCallback +struct MidPhaseQueryLocalReport : EntityReport +{ + MidPhaseQueryLocalReport(Ps::Array& _container) : container(_container) + { + + } + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + for(PxU32 i=0; i& container; + +private: + MidPhaseQueryLocalReport operator=(MidPhaseQueryLocalReport& report); +} ; + +static void midPhaseQuery(const HeightFieldUtil& hfUtil, const PxTransform& pose, const PxBounds3& bounds, Ps::Array& tempContainer, PxU32 flags) +{ + MidPhaseQueryLocalReport localReport(tempContainer); + hfUtil.overlapAABBTriangles(pose, bounds, flags, &localReport); +} + +static bool calculateMTD( const CapsuleV& capsuleV, const Ps::aos::FloatVArg inflatedRadiusV, const bool isDoubleSide, const MTDTriangle* triangles, const PxU32 nbTriangles, const PxU32 startIndex, MeshPersistentContact* manifoldContacts, + PxU32& numContacts, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, PxU32& faceIndex, Ps::aos::FloatV& mtd) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + bool hadContacts = false; + FloatV deepestPen = mtd; + + for(PxU32 j=0; j(triMeshGeom.triangleMesh); + const PxU8* extraTrigData = triMesh->getExtraTrigData(); + const bool flipsNormal = triMeshGeom.scale.hasNegativeDeterminant(); + + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + //inflated the capsule by 15% in case of some disagreement between sweep and mtd calculation. If sweep said initial overlap, but mtd has a positive separation, + //we are still be able to return a valid normal but we should zero the distance. + const FloatV inflatedRadiusV = FLoad(inflatedRadius*1.15f); + + bool foundInitial = false; + const PxU32 iterations = 4; + + const Cm::Matrix34 vertexToWorldSkew = pose * triMeshGeom.scale; + + Ps::Array tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i tempContainer; + tempContainer.reserve(128); + + const HeightFieldUtil hfUtil(heightFieldGeom); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i(triMeshGeom.triangleMesh); + const PxU8* extraTrigData = triMesh->getExtraTrigData(); + + const Vec3V zeroV = V3Zero(); + + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + + bool foundInitial = false; + const PxU32 iterations = 4; + + Ps::Array tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + Box box = _box; + + const QuatV q0 = QuatVLoadU(&boxTransform.q.x); + const Vec3V p0 = V3LoadU(&boxTransform.p.x); + + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV minMargin = CalculateMTDBoxMargin(boxExtents); + const FloatV inflationV = FAdd(FLoad(inflation), minMargin); + PxReal boundInflation; + FStore(inflationV, &boundInflation); + + box.extents += PxVec3(boundInflation); + const BoxV boxV(zeroV, boxExtents); + + Vec3V boxCenter = V3LoadU(box.center); + + //create the polyData based on the original data + PolygonalData polyData; + const PCMPolygonalBox polyBox(_box.extents); + polyBox.getPolygonalData(&polyData); + + const Mat33V identity = M33Identity(); + + const Cm::Matrix34 meshToWorldSkew = pose * triMeshGeom.scale; + + PsTransformV boxTransformV(p0, q0);//box + + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i boxMap(boxV, boxTransformV, identity, identity, true); + + boxMap.setShapeSpaceCenterofMass(zeroV); + // Move to AABB space + Cm::Matrix34 WorldToBox; + computeWorldToBoxMatrix(WorldToBox, box); + const Cm::Matrix34 meshToBox = WorldToBox*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToBox.m.column0), V3LoadU(meshToBox.m.column1), V3LoadU(meshToBox.m.column2)); + const Ps::aos::PsMatTransformV meshToConvex(V3LoadU(meshToBox.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER - 1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; kgetLocalTriangle(triangles[k], triangleIndex1, triMeshGeom.scale.hasNegativeDeterminant()); + triangles[k].extraTriData = getConvexEdgeFlags(extraTrigData, triangleIndex1); + } + + //ML: mtd has back face culling, so if the capsule's center is below the triangle, we won't generate any contacts + hadContacts = calculateMTD(polyData, &boxMap, boxTransformV, meshToConvex, isDoubleSided, inflationV, triangles, nbTrigs, startIndex, manifoldContacts, numContacts, normal, closestA, closestB, triangleIndex, mtd) || hadContacts; + } + + if(!hadContacts) + break; + + triangleIndex = tempContainer[triangleIndex]; + + foundInitial = true; + + distV = mtd; + worldNormal = boxTransformV.rotate(normal); + worldContactA = boxTransformV.transform(closestA); + if(FAllGrtrOrEq(FZero(), distV)) + { + const Vec3V t = V3Scale(worldNormal, mtd); + translation = V3Sub(translation, t); + boxCenter = V3Sub(boxCenter, t); + V3StoreU(boxCenter, box.center); + } + else + { + if(i == 0) + { + //First iteration so keep this normal + hit.distance = 0.f; + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + return true; + } + break; + } + } + + const FloatV translationF = V3Length(translation); + distV = FNeg(translationF); + + const BoolV con = FIsGrtr(translationF, FZero()); + worldNormal = V3Sel(con, V3ScaleInv(translation, translationF), zeroV); + + if(foundInitial) + { + //transform closestA to world space + FStore(distV, &hit.distance); + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + } + return foundInitial; +} + +bool physx::Gu::computeBox_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const Box& _box, const PxTransform& boxTransform, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit) +{ + using namespace Ps::aos; + + const Vec3V zeroV = V3Zero(); + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + bool foundInitial = false; + const PxU32 iterations = 4; + + Ps::Array tempContainer; + tempContainer.reserve(128); + const HeightFieldUtil hfUtil(heightFieldGeom); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + Box box = _box; + + const QuatV q0 = QuatVLoadU(&boxTransform.q.x); + const Vec3V p0 = V3LoadU(&boxTransform.p.x); + + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV minMargin = CalculateMTDBoxMargin(boxExtents); + const FloatV inflationV = FAdd(FLoad(inflation), minMargin); + //const FloatV inflationV = FLoad(inflation); + + PxReal boundInflation; + FStore(inflationV, &boundInflation); + box.extents += PxVec3(boundInflation); + + const BoxV boxV(zeroV, boxExtents); + + Vec3V boxCenter = V3LoadU(box.center); + + //create the polyData based on the original box + PolygonalData polyData; + const PCMPolygonalBox polyBox(_box.extents); + polyBox.getPolygonalData(&polyData); + + const Mat33V identity = M33Identity(); + + const Cm::Matrix34 meshToWorldSkew(pose); + + PsTransformV boxTransformV(p0, q0);//box + + FloatV mtd; + FloatV distV; + for(PxU32 i=0; i boxMap(boxV, boxTransformV, identity, identity, true); + boxMap.setShapeSpaceCenterofMass(zeroV); + // Move to AABB space + Cm::Matrix34 WorldToBox; + computeWorldToBoxMatrix(WorldToBox, box); + const Cm::Matrix34 meshToBox = WorldToBox*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToBox.m.column0), V3LoadU(meshToBox.m.column1), V3LoadU(meshToBox.m.column2)); + const Ps::aos::PsMatTransformV meshToConvex(V3LoadU(meshToBox.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER-1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; k(triMeshGeom.triangleMesh); + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + const PxU8* extraTrigData = triMesh->getExtraTrigData(); + + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + + bool foundInitial = false; + const PxU32 iterations = 2; + + ConvexHullData* hullData = &cm->getHull(); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + const PxVec3 _shapeSpaceCenterOfMass = convexScaling * hullData->mCenterOfMass; + const Vec3V shapeSpaceCenterOfMass = V3LoadU(_shapeSpaceCenterOfMass); + + const QuatV q0 = QuatVLoadU(&convexPose.q.x); + const Vec3V p0 = V3LoadU(&convexPose.p.x); + PsTransformV convexTransformV(p0, q0); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHull(hullData, V3Zero(), vScale, vQuat, idtScaleConvex); + PX_ALIGN(16, PxU8 convexBuff[sizeof(SupportLocalImpl)]); + + const FloatV convexMargin = CalculateMTDConvexMargin(hullData, vScale); + const FloatV inflationV = FAdd(FLoad(inflation), convexMargin); + PxReal boundInflation; + FStore(inflationV, &boundInflation); + + Ps::Array tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + const Cm::Matrix34 meshToWorldSkew = pose * triMeshGeom.scale; + + PolygonalData polyData; + getPCMConvexData(convexHull, idtScaleConvex, polyData); + + FloatV mtd; + FloatV distV; + Vec3V center = p0; + PxTransform tempConvexPose = convexPose; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + + for(PxU32 i=0; i(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl)(static_cast(convexHull), convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex)) : + static_cast(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl)(convexHull, convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex))); + + convexMap->setShapeSpaceCenterofMass(shapeSpaceCenterOfMass); + + Box hullOBB; + computeOBBAroundConvex(hullOBB, convexGeom, cm, tempConvexPose); + + hullOBB.extents += PxVec3(boundInflation); + + midPhaseQuery(triMeshGeom, pose, hullOBB, tempContainer); + + // Get results + const PxU32 nbTriangles = tempContainer.size(); + if(!nbTriangles) + break; + + // Move to AABB space + const Cm::Matrix34 worldToConvex(tempConvexPose.getInverse()); + const Cm::Matrix34 meshToConvex = worldToConvex*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToConvex.m.column0), V3LoadU(meshToConvex.m.column1), V3LoadU(meshToConvex.m.column2)); + const Ps::aos::PsMatTransformV meshToConvexV(V3LoadU(meshToConvex.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER-1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; kgetLocalTriangle(triangles[k], triangleIndex1, triMeshGeom.scale.hasNegativeDeterminant()); + triangles[k].extraTriData = getConvexEdgeFlags(extraTrigData, triangleIndex1); + } + + //ML: mtd has back face culling, so if the capsule's center is below the triangle, we won't generate any contacts + hadContacts = calculateMTD(polyData, convexMap, convexTransformV, meshToConvexV, isDoubleSided, inflationV, triangles, nbTrigs, startIndex, manifoldContacts, numContacts, normal, closestA, closestB, triangleIndex, mtd) || hadContacts; + } + + if(!hadContacts) + break; + + triangleIndex = tempContainer[triangleIndex]; + + foundInitial = true; + + distV = mtd; + worldNormal = convexTransformV.rotate(normal); + worldContactA = convexTransformV.transform(closestA); + if(FAllGrtrOrEq(FZero(), distV)) + { + const Vec3V t = V3Scale(worldNormal, mtd); + translation = V3Sub(translation, t); + center = V3Sub(center, t); + } + else + { + if(i == 0) + { + //First iteration so keep this normal + hit.distance = 0.f; + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + return true; + } + break; + } + } + + + const FloatV translationF = V3Length(translation); + distV = FNeg(translationF); + + const BoolV con = FIsGrtr(translationF, FZero()); + worldNormal = V3Sel(con, V3ScaleInv(translation, translationF), zeroV); + + if(foundInitial) + { + //transform closestA to world space + FStore(distV, &hit.distance); + V3StoreU(worldContactA, hit.position); + V3StoreU(worldNormal, hit.normal); + hit.faceIndex = triangleIndex; + } + return foundInitial; +} + +bool physx::Gu::computeConvex_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit) +{ + using namespace Ps::aos; + + const HeightFieldUtil hfUtil(heightFieldGeom); + + const Vec3V zeroV = V3Zero(); + MeshPersistentContact manifoldContacts[64]; + PxU32 numContacts = 0; + + bool foundInitial = false; + const PxU32 iterations = 2; + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + ConvexHullData* hullData = &cm->getHull(); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + const PxVec3 _shapeSpaceCenterOfMass = convexScaling * hullData->mCenterOfMass; + const Vec3V shapeSpaceCenterOfMass = V3LoadU(_shapeSpaceCenterOfMass); + + const QuatV q0 = QuatVLoadU(&convexPose.q.x); + const Vec3V p0 = V3LoadU(&convexPose.p.x); + PsTransformV convexTransformV(p0, q0); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, idtScaleConvex); + PX_ALIGN(16, PxU8 convexBuff[sizeof(SupportLocalImpl)]); + + const FloatV convexMargin = CalculateMTDConvexMargin(hullData, vScale); + const FloatV inflationV = FAdd(FLoad(inflation), convexMargin); + PxReal boundInflation; + FStore(inflationV, &boundInflation); + + Ps::Array tempContainer; + tempContainer.reserve(128); + + Vec3V closestA = zeroV, closestB = zeroV, normal = zeroV; + Vec3V worldNormal = zeroV, worldContactA = zeroV;//, worldContactB = zeroV; + PxU32 triangleIndex = 0xfffffff; + + Vec3V translation = zeroV; + + PolygonalData polyData; + getPCMConvexData(convexHull, idtScaleConvex, polyData); + + FloatV mtd; + FloatV distV; + Vec3V center = p0; + PxTransform tempConvexPose = convexPose; + const Cm::Matrix34 meshToWorldSkew(pose); + + for(PxU32 i=0; i(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl)(static_cast(convexHull), convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex)) : + static_cast(PX_PLACEMENT_NEW(convexBuff, SupportLocalImpl)(convexHull, convexTransformV, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex))); + + convexMap->setShapeSpaceCenterofMass(shapeSpaceCenterOfMass); + + Box hullOBB; + computeOBBAroundConvex(hullOBB, convexGeom, cm, tempConvexPose); + hullOBB.extents += PxVec3(boundInflation); + + const PxBounds3 bounds = PxBounds3::basisExtent(hullOBB.center, hullOBB.rot, hullOBB.extents); + + midPhaseQuery(hfUtil, pose, bounds, tempContainer, flags); + + // Get results + const PxU32 nbTriangles = tempContainer.size(); + + if(!nbTriangles) + break; + + // Move to AABB space + const Cm::Matrix34 worldToConvex(tempConvexPose.getInverse()); + const Cm::Matrix34 meshToConvex = worldToConvex*meshToWorldSkew; + + const Ps::aos::Mat33V rot(V3LoadU(meshToConvex.m.column0), V3LoadU(meshToConvex.m.column1), V3LoadU(meshToConvex.m.column2)); + const Ps::aos::PsMatTransformV meshToConvexV(V3LoadU(meshToConvex.p), rot); + + bool hadContacts = false; + + const PxU32 nbBatches = (nbTriangles + BATCH_TRIANGLE_NUMBER-1)/BATCH_TRIANGLE_NUMBER; + mtd = FMax(); + MTDTriangle triangles[BATCH_TRIANGLE_NUMBER]; + for(PxU32 a = 0; a < nbBatches; ++a) + { + const PxU32 startIndex = a * BATCH_TRIANGLE_NUMBER; + const PxU32 nbTrigs = PxMin(nbTriangles - startIndex, BATCH_TRIANGLE_NUMBER); + for(PxU32 k=0; k d) + { + index = i; + dmin = d; + } + } + hit.normal = plane.n; + hit.distance = dmin; + hit.position = pts[index] - plane.n*dmin; + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool physx::Gu::computePlane_ConvexMTD(const PxPlane& plane, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, PxSweepHit& hit) +{ + const ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + const Cm::FastVertex2ShapeScaling convexScaling(convexGeom.scale); + PxU32 nbVerts = convexMesh->getNbVerts(); + const PxVec3* PX_RESTRICT verts = convexMesh->getVerts(); + + PxVec3 worldPointMin = convexPose.transform(convexScaling * verts[0]); + PxReal dmin = plane.distance(worldPointMin); + for(PxU32 i=1;i d) + { + dmin = d; + worldPointMin = worldPoint; + } + } + + hit.normal = plane.n; + hit.distance = dmin; + hit.position = worldPointMin - plane.n * dmin; + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepMTD.h b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.h new file mode 100644 index 000000000..9c63c9c2f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_MTD_H +#define GU_SWEEP_MTD_H + +namespace physx +{ + class PxConvexMeshGeometry; + class PxTriangleMeshGeometry; + class PxGeometry; + class PxHeightFieldGeometry; + +namespace Gu +{ + class Sphere; + class Capsule; + + bool computeCapsule_TriangleMeshMTD(const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, Gu::CapsuleV& capsuleV, PxReal inflatedRadius, bool isDoubleSided, PxSweepHit& hit); + + bool computeCapsule_HeightFieldMTD(const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, Gu::CapsuleV& capsuleV, PxReal inflatedRadius, bool isDoubleSided, const PxU32 flags, PxSweepHit& hit); + + bool computeBox_TriangleMeshMTD(const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, const Gu::Box& box, const PxTransform& boxTransform, PxReal inflation, + bool isDoubleSided, PxSweepHit& hit); + + bool computeBox_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const Gu::Box& box, const PxTransform& boxTransform, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit); + + bool computeConvex_TriangleMeshMTD( const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexTransform, PxReal inflation, + bool isDoubleSided, PxSweepHit& hit); + + bool computeConvex_HeightFieldMTD( const PxHeightFieldGeometry& heightFieldGeom, const PxTransform& pose, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexTransform, PxReal inflation, + bool isDoubleSided, const PxU32 flags, PxSweepHit& hit); + + bool computeSphere_SphereMTD(const Sphere& sphere0, const Sphere& sphere1, PxSweepHit& hit); + bool computeSphere_CapsuleMTD(const Sphere& sphere, const Capsule& capsule, PxSweepHit& hit); + + bool computeCapsule_CapsuleMTD(const Capsule& capsule0, const Capsule& capsule1, PxSweepHit& hit); + + bool computePlane_CapsuleMTD(const PxPlane& plane, const Capsule& capsule, PxSweepHit& hit); + bool computePlane_BoxMTD(const PxPlane& plane, const Box& box, PxSweepHit& hit); + bool computePlane_ConvexMTD(const PxPlane& plane, const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, PxSweepHit& hit); + + // PT: wrapper just to avoid duplicating these lines. + PX_FORCE_INLINE void setupSweepHitForMTD(PxSweepHit& sweepHit, bool hasContacts, const PxVec3& unitDir) + { + sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + if(!hasContacts) + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + else + { + //ML: touching contact. We need to overwrite the normal to the negative of sweep direction + if (sweepHit.distance == 0.0f) + { + sweepHit.normal = -unitDir; + } + sweepHit.flags |= PxHitFlag::ePOSITION; + + } + } +} + +} + + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp new file mode 100644 index 000000000..f5b3ab00c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp @@ -0,0 +1,725 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTests.h" +#include "GuHeightFieldUtil.h" +#include "CmScaling.h" +#include "GuConvexMesh.h" +#include "GuIntersectionRayPlane.h" +#include "GuVecBox.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuSweepMTD.h" +#include "PxConvexMeshGeometry.h" +#include "PxSphereGeometry.h" +#include "GuSweepSphereCapsule.h" +#include "GuSweepCapsuleCapsule.h" +#include "GuSweepTriangleUtils.h" +#include "GuSweepCapsuleTriangle.h" +#include "GuInternal.h" +#include "GuGJKRaycast.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +static const PxReal gEpsilon = .01f; + +static PxU32 computeSweepConvexPlane( + const PxConvexMeshGeometry& convexGeom, ConvexHullData* hullData, const PxU32& nbPolys, const PxTransform& pose, + const PxVec3& impact_, const PxVec3& unitDir) +{ + PX_ASSERT(nbPolys); + + const PxVec3 impact = impact_ - unitDir * gEpsilon; + + const PxVec3 localPoint = pose.transformInv(impact); + const PxVec3 localDir = pose.rotateInv(unitDir); + + const FastVertex2ShapeScaling scaling(convexGeom.scale); + + PxU32 minIndex = 0; + PxReal minD = PX_MAX_REAL; + for(PxU32 j=0; jmPolygons[j].mPlane; + + PxPlane plane; + scaling.transformPlaneToShapeSpace(pl.n, pl.d, plane.n, plane.d); + + PxReal d = plane.distance(localPoint); + if(d<0.0f) + continue; + + const PxReal tweak = plane.n.dot(localDir) * gEpsilon; + d += tweak; + + if(dmNbPolygons, pose, sweepHit.position, unitDir); + sweepHit.flags |= PxHitFlag::eFACE_INDEX; + } + return true; +} + +static PX_FORCE_INLINE bool hasInitialOverlap(PxSweepHit& sweepHit, const PxVec3& unitDir, + const FloatVArg toi, + const Vec3VArg normal, const Vec3VArg closestA, + const PsTransformV& convexPose, + const bool isMtd, const bool impactPointOnTheOtherShape) +{ + sweepHit.flags = PxHitFlag::eNORMAL; + + const FloatV zero = FZero(); + if(FAllGrtrOrEq(zero, toi)) + { + //ML: initial overlap + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const FloatV length = toi; + const Vec3V worldPointA = convexPose.transform(closestA); + const Vec3V worldNormal = V3Normalize(convexPose.rotate(normal)); + if(impactPointOnTheOtherShape) + { + const Vec3V destWorldPointA = V3NegScaleSub(worldNormal, length, worldPointA); + V3StoreU(worldNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + } + else + { + const Vec3V destNormal = V3Neg(worldNormal); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(worldPointA, sweepHit.position); + } + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + sweepHit.faceIndex = 0xffffffff; + return true; + } + return false; +} + +///////////////////////////////////////////////// sweepCapsule/Sphere ////////////////////////////////////////////////////// +bool sweepCapsule_SphereGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + const PxSphereGeometry& sphereGeom = static_cast(geom); + + const Sphere sphere(pose.p, sphereGeom.radius+inflation); + + if(!sweepSphereCapsule(sphere, lss, -unitDir, distance, sweepHit.distance, sweepHit.position, sweepHit.normal, hitFlags)) + return false; + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + if(isMtd) + { + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + + if(sweepHit.distance == 0.f) + { + //intialOverlap + if(lss.p0 == lss.p1) + { + //sphere + return computeSphere_SphereMTD(sphere, Sphere(lss.p0, lss.radius), sweepHit); + } + else + { + //capsule + return computeSphere_CapsuleMTD(sphere, lss, sweepHit); + } + } + } + else + { + if(sweepHit.distance!=0.0f) + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + else + sweepHit.flags = PxHitFlag::eNORMAL; + } + return true; +} + +bool sweepCapsule_PlaneGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_UNUSED(geom); +// const PxPlaneGeometry& planeGeom = static_cast(geom); + + const PxPlane& worldPlane = getPlane(pose); + + const PxF32 capsuleRadius = lss.radius + inflation; + + PxU32 index = 0; + PxVec3 pts[2]; + + PxReal minDp = PX_MAX_REAL; + + sweepHit.faceIndex = 0xFFFFffff; // spec says face index is undefined for planes + + // Find extreme point on the capsule + // AP: removed if (lss.p0 == lss.p1 clause because it wasn't properly computing minDp) + pts[0] = lss.p0; + pts[1] = lss.p1; + for(PxU32 i=0; i<2; i++) + { + const PxReal dp = pts[i].dot(worldPlane.n); + if(dp 0 && sweepHit.distance <= distance) + { + sweepHit.normal = worldPlane.n; + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + return true; + } + return false; +} + +bool sweepCapsule_CapsuleGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + Capsule staticCapsule; + getCapsule(staticCapsule, capsuleGeom, pose); + staticCapsule.radius +=inflation; + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + PxU16 outFlags; + if(!sweepCapsuleCapsule(lss, staticCapsule, -unitDir, distance, sweepHit.distance, sweepHit.position, sweepHit.normal, hitFlags, outFlags)) + return false; + + sweepHit.flags = PxHitFlags(outFlags); + if(sweepHit.distance == 0.0f) + { + //initial overlap + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + return computeCapsule_CapsuleMTD(lss, staticCapsule, sweepHit); + } + } + return true; +} + +bool sweepCapsule_ConvexGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + + using namespace Ps::aos; + + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + const PxConvexMeshGeometry& convexGeom = static_cast(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + const FloatV dist = FLoad(distance); + const Vec3V worldDir = V3LoadU(unitDir); + + const PsTransformV capPose = loadTransformU(capsulePose_); + const PsTransformV convexPose = loadTransformU(pose); + + const PsMatTransformV aToB(convexPose.transformInv(capPose)); + + const FloatV capsuleHalfHeight = FLoad(capsuleGeom_.halfHeight); + const FloatV capsuleRadius = FLoad(lss.radius); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + CapsuleV capsule(aToB.p, aToB.rotate( V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + + const Vec3V dir = convexPose.rotateInv(V3Neg(V3Scale(worldDir, dist))); + + bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of convex hull + LocalConvex convexA(capsule); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(!gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, lss.radius + inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, true)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = convexPose.transform(closestA); + const FloatV length = FMul(dist, toi); + const Vec3V destNormal = V3Normalize(convexPose.rotate(normal)); + const Vec3V destWorldPointA = V3ScaleAdd(worldDir, length, worldPointA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + + return computeFaceIndex(sweepHit, hitFlags, convexGeom, hullData, pose, unitDir); +} + +///////////////////////////////////////////////// sweepBox ////////////////////////////////////////////////////// + +bool sweepBox_PlaneGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_UNUSED(geom); + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + +// const PxPlaneGeometry& planeGeom = static_cast(geom); + + sweepHit.faceIndex = 0xFFFFffff; // spec says face index is undefined for planes + + PxPlane worldPlane = getPlane(pose); + worldPlane.d -=inflation; + + // Find extreme point on the box + PxVec3 boxPts[8]; + box.computeBoxPoints(boxPts); + PxU32 index = 0; + PxReal minDp = PX_MAX_REAL; + for(PxU32 i=0;i<8;i++) + { + const PxReal dp = boxPts[i].dot(worldPlane.n); + + if(dp 0 && sweepHit.distance <= distance) + { + sweepHit.normal = worldPlane.n; + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + return true; + } + return false; +} + +bool sweepBox_ConvexGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(boxGeom_); + + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + const PxConvexMeshGeometry& convexGeom = static_cast(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + + const PsTransformV boxPose = loadTransformU(boxPose_); + const PsTransformV convexPose = loadTransformU(pose); + + const PsMatTransformV aToB(convexPose.transformInv(boxPose)); + + const Vec3V boxExtents = V3LoadU(box.extents); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + BoxV boxV(zeroV, boxExtents); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const Vec3V dir = convexPose.rotateInv(V3Neg(V3Scale(worldDir, dist))); + + bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal; + RelativeConvex convexA(boxV, aToB); + LocalConvex convexB(convexHull); + if(!gjkRaycastPenetration< RelativeConvex,LocalConvex >(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, true)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destNormal = V3Normalize(convexPose.rotate(normal)); + const FloatV length = FMul(dist, toi); + const Vec3V worldPointA = convexPose.transform(closestA); + const Vec3V destWorldPointA = V3ScaleAdd(worldDir, length, worldPointA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + + return computeFaceIndex(sweepHit, hitFlags, convexGeom, hullData, pose, unitDir); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Gu::sweepCapsuleTriangles(GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxCapsuleGeometry)) +{ + Capsule capsule; + getCapsule(capsule, geom, pose); + capsule.radius +=inflation; + + // Compute swept box + Box capsuleBox; + computeBoxAroundCapsule(capsule, capsuleBox); + + BoxPadded sweptBounds; + computeSweptBox(sweptBounds, capsuleBox.extents, capsuleBox.center, capsuleBox.rot, unitDir, distance); + + PxVec3 triNormal; + return sweepCapsuleTriangles_Precise(nbTris, triangles, capsule, unitDir, distance, cachedIndex, hit, triNormal, hitFlags, doubleSided, &sweptBounds); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sweepConvex_SphereGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + const PxSphereGeometry& sphereGeom = static_cast(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero= FZero(); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + const FloatV sphereRadius = FLoad(sphereGeom.radius); + + const PsTransformV sphereTransf = loadTransformU(pose); + const PsTransformV convexTransf = loadTransformU(convexPose); + + const PsMatTransformV aToB(convexTransf.transformInv(sphereTransf)); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const Vec3V dir = convexTransf.rotateInv(V3Scale(worldDir, dist)); + + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + //CapsuleV capsule(zeroV, sphereRadius); + CapsuleV capsule(aToB.p, sphereRadius); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal; + LocalConvex convexA(capsule); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(!gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, sphereGeom.radius+inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, false)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destNormal = V3Neg(V3Normalize(convexTransf.rotate(normal))); + const FloatV length = FMul(dist, toi); + const Vec3V destWorldPointA = convexTransf.transform(closestA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + sweepHit.faceIndex = 0xffffffff; + return true; +} + +bool sweepConvex_PlaneGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::ePLANE); + PX_UNUSED(hitFlags); + PX_UNUSED(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + sweepHit.faceIndex = 0xFFFFffff; // spec says face index is undefined for planes + + const PxVec3* PX_RESTRICT hullVertices = hullData->getHullVertices(); + PxU32 numHullVertices = hullData->mNbHullVertices; + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + const FastVertex2ShapeScaling convexScaling(convexGeom.scale); + + PxPlane plane = getPlane(pose); + plane.d -=inflation; + + sweepHit.distance = distance; + bool status = false; + bool initialOverlap = false; + while(numHullVertices--) + { + const PxVec3& vertex = *hullVertices++; + const PxVec3 worldPt = convexPose.transform(convexScaling * vertex); + float t; + PxVec3 pointOnPlane; + if(intersectRayPlane(worldPt, unitDir, plane, t, &pointOnPlane)) + { + if(plane.distance(worldPt) <= 0.0f) + { + initialOverlap = true; + break; + //// Convex touches plane + //sweepHit.distance = 0.0f; + //sweepHit.flags = PxHitFlag::eNORMAL; + //sweepHit.normal = -unitDir; + //return true; + } + + if(t > 0.0f && t <= sweepHit.distance) + { + sweepHit.distance = t; + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + sweepHit.position = pointOnPlane; + sweepHit.normal = plane.n; + status = true; + } + } + } + + if(initialOverlap) + { + if(isMtd) + { + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL; + return computePlane_ConvexMTD(plane, convexGeom, convexPose, sweepHit); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.flags = PxHitFlag::eNORMAL; + sweepHit.normal = -unitDir; + return true; + } + } + return status; +} + +bool sweepConvex_CapsuleGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + Capsule capsule; + getCapsule(capsule, capsuleGeom, pose); + + // remove PxHitFlag::eFACE_INDEX, not neeeded to compute. + PxHitFlags tempHitFlags = hitFlags; + tempHitFlags &= ~PxHitFlag::eFACE_INDEX; + + if(!sweepCapsule_ConvexGeom(convexGeom, convexPose, capsuleGeom, pose, capsule, -unitDir, distance, sweepHit, tempHitFlags, inflation)) + return false; + + if(sweepHit.flags & PxHitFlag::ePOSITION) + sweepHit.position += unitDir * sweepHit.distance; + + sweepHit.normal = -sweepHit.normal; + sweepHit.faceIndex = 0xffffffff; + return true; +} + +bool sweepConvex_BoxGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + const PxBoxGeometry& boxGeom = static_cast(geom); + + Box box; + buildFrom(box, pose.p, boxGeom.halfExtents, pose.q); + + // remove PxHitFlag::eFACE_INDEX, not neeeded to compute. + PxHitFlags tempHitFlags = hitFlags; + tempHitFlags &= ~PxHitFlag::eFACE_INDEX; + + if(!sweepBox_ConvexGeom(convexGeom, convexPose, boxGeom, pose, box, -unitDir, distance, sweepHit, tempHitFlags, inflation)) + return false; + + if(sweepHit.flags & PxHitFlag::ePOSITION) + sweepHit.position += unitDir * sweepHit.distance; + + sweepHit.normal = -sweepHit.normal; + sweepHit.faceIndex = 0xffffffff; + return true; +} + +bool sweepConvex_ConvexGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eCONVEXMESH); + const PxConvexMeshGeometry& otherConvexGeom = static_cast(geom); + ConvexMesh& otherConvexMesh = *static_cast(otherConvexGeom.convexMesh); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + ConvexHullData* hullData = &convexMesh->getHull(); + + ConvexHullData* otherHullData = &otherConvexMesh.getHull(); + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + + const Vec3V otherVScale = V3LoadU_SafeReadW(otherConvexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV otherVQuat = QuatVLoadU(&otherConvexGeom.scale.rotation.x); + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexGeom.scale.rotation.x); + + const PsTransformV otherTransf = loadTransformU(pose); + const PsTransformV convexTransf = loadTransformU(convexPose); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const Vec3V dir = convexTransf.rotateInv(V3Scale(worldDir, dist)); + + const PsMatTransformV aToB(convexTransf.transformInv(otherTransf)); + + ConvexHullV otherConvexHull(otherHullData, zeroV, otherVScale, otherVQuat, otherConvexGeom.scale.isIdentity()); + ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, convexGeom.scale.isIdentity()); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + + FloatV toi; + Vec3V closestA, normal; + RelativeConvex convexA(otherConvexHull, aToB); + LocalConvex convexB(convexHull); + if(!gjkRaycastPenetration< RelativeConvex, LocalConvex >(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) + return false; + + if(hasInitialOverlap(sweepHit, unitDir, toi, normal, closestA, convexPose, isMtd, false)) + return true; + + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = convexTransf.transform(closestA); + const Vec3V destNormal = V3Neg(V3Normalize(convexTransf.rotate(normal))); + const FloatV length = FMul(dist, toi); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(worldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + + return computeFaceIndex(sweepHit, hitFlags, otherConvexGeom, otherHullData, pose, unitDir); +} diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h new file mode 100644 index 000000000..81c1ad2bd --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h @@ -0,0 +1,56 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SHARED_TESTS_H +#define GU_SWEEP_SHARED_TESTS_H + +#include "CmPhysXCommon.h" +#include "GuBoxConversion.h" + +namespace physx +{ +PX_FORCE_INLINE void computeWorldToBoxMatrix(physx::Cm::Matrix34& worldToBox, const physx::Gu::Box& box) +{ + physx::Cm::Matrix34 boxToWorld; + physx::buildMatrixFromBox(boxToWorld, box); + worldToBox = boxToWorld.getInverseRT(); +} + +PX_FORCE_INLINE PxU32 getTriangleIndex(PxU32 i, PxU32 cachedIndex) +{ + PxU32 triangleIndex; + if(i==0) triangleIndex = cachedIndex; + else if(i==cachedIndex) triangleIndex = 0; + else triangleIndex = i; + return triangleIndex; +} +} + + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp b/src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp new file mode 100644 index 000000000..8f213b38b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp @@ -0,0 +1,604 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTests.h" +#include "PxSphereGeometry.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" +#include "GuVecTriangle.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" +#include "PsFoundation.h" +#include "GuGJKRaycast.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +bool sweepCapsule_BoxGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(hitFlags); + + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + const PxBoxGeometry& boxGeom = static_cast(geom); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents0 = V3LoadU(boxGeom.halfExtents); + const FloatV dist = FLoad(distance); + const Vec3V worldDir = V3LoadU(unitDir); + + const PsTransformV capPos = loadTransformU(capsulePose_); + const PsTransformV boxPos = loadTransformU(pose); + + const PsMatTransformV aToB(boxPos.transformInv(capPos)); + + const FloatV capsuleHalfHeight = FLoad(capsuleGeom_.halfHeight); + const FloatV capsuleRadius = FLoad(lss.radius); + + BoxV box(zeroV, boxExtents0); + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + const Vec3V dir = boxPos.rotateInv(V3Neg(V3Scale(worldDir, dist))); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi = FMax(); + Vec3V closestA, normal;//closestA and normal is in the local space of box + LocalConvex convexA(capsule); + LocalConvex convexB(box); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), box.getCenter()); + if(!gjkRaycastPenetration, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, + closestA, lss.radius + inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + if(FAllGrtrOrEq(zero, toi)) + { + //initial overlap + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + const FloatV length = toi; + const Vec3V destWorldPointA = V3NegScaleSub(destNormal, length, worldPointA); + V3StoreU(destWorldPointA, sweepHit.position); + V3StoreU(destNormal, sweepHit.normal); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V worldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + const FloatV length = FMul(dist, toi); + const Vec3V destWorldPointA = V3ScaleAdd(worldDir, length, worldPointA); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sweepBox_SphereGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eSPHERE); + PX_UNUSED(hitFlags); + PX_UNUSED(boxGeom_); + + const PxSphereGeometry& sphereGeom = static_cast(geom); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV worldDist = FLoad(distance); + const Vec3V unitDirV = V3LoadU(unitDir); + + const FloatV sphereRadius = FLoad(sphereGeom.radius); + + const PsTransformV spherePos = loadTransformU(pose); + const PsTransformV boxPos = loadTransformU(boxPose_); + + const PsMatTransformV aToB(boxPos.transformInv(spherePos)); + + BoxV boxV(zeroV, boxExtents); + CapsuleV capsuleV(aToB.p, sphereRadius); + + //transform into b space + const Vec3V dir = boxPos.rotateInv(V3Scale(unitDirV, worldDist)); + + bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + const Vec3V initialSearchDir = V3Sub(capsuleV.getCenter(), boxV.getCenter()); + LocalConvex convexA(capsuleV); + LocalConvex convexB(boxV); + if(!gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, sphereGeom.radius+inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + + //initial overlap + if(FAllGrtrOrEq(zero, toi)) + { + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = V3Neg(boxPos.rotate(normal)); + const FloatV length = toi; + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = V3Neg(boxPos.rotate(normal)); + const FloatV length = FMul(worldDist, toi); + V3StoreU(destNormal, sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +bool sweepBox_CapsuleGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + using namespace Ps::aos; + PX_ASSERT(geom.getType() == PxGeometryType::eCAPSULE); + PX_UNUSED(hitFlags); + PX_UNUSED(boxGeom_); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + const FloatV capsuleHalfHeight = FLoad(capsuleGeom.halfHeight); + const FloatV capsuleRadius = FLoad(capsuleGeom.radius); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(box.extents); + const FloatV worldDist = FLoad(distance); + const Vec3V unitDirV = V3LoadU(unitDir); + + const PsTransformV capPos = loadTransformU(pose); + const PsTransformV boxPos = loadTransformU(boxPose_); + + const PsMatTransformV aToB(boxPos.transformInv(capPos)); + + BoxV boxV(zeroV, boxExtents); + CapsuleV capsuleV(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + //transform into b space + const Vec3V dir = boxPos.rotateInv(V3Scale(unitDirV, worldDist)); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + const Vec3V initialSearchDir = V3Sub(capsuleV.getCenter(), boxV.getCenter()); + LocalConvex convexA(capsuleV); + LocalConvex convexB(boxV); + if(!gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, capsuleGeom.radius+inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + + //initial overlap + if(FAllGrtrOrEq(zero, toi)) + { + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + //initial overlap is toi < 0 + const FloatV length = toi; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + return true; + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxPos.transform(closestA); + const Vec3V destNormal = boxPos.rotate(normal); + const FloatV length = FMul(worldDist, toi); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +bool sweepBox_BoxGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eBOX); + PX_UNUSED(boxGeom_); + + const PxBoxGeometry& boxGeom = static_cast(geom); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents0 = V3LoadU(boxGeom.halfExtents); + const Vec3V boxExtents1 = V3LoadU(box.extents); + const FloatV worldDist = FLoad(distance); + const Vec3V unitDirV = V3LoadU(unitDir); + + const PsTransformV boxTrans0 = loadTransformU(pose); + const PsTransformV boxTrans1 = loadTransformU(boxPose_); + + const PsMatTransformV aToB(boxTrans1.transformInv(boxTrans0)); + + BoxV box0(zeroV, boxExtents0); + BoxV box1(zeroV, boxExtents1); + + //transform into b space + const Vec3V dir = boxTrans1.rotateInv(V3Scale(unitDirV, worldDist)); + const bool isMtd = hitFlags & PxHitFlag::eMTD; + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + RelativeConvex convexA(box0, aToB); + LocalConvex convexB(box1); + if(!gjkRaycastPenetration, LocalConvex >(convexA, convexB, aToB.p, zero, zeroV, dir, toi, normal, closestA, inflation, isMtd)) + return false; + + sweepHit.flags = PxHitFlag::eNORMAL; + if(FAllGrtrOrEq(zero, toi)) + { + if(isMtd) + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const FloatV length = toi; + const Vec3V destWorldPointA = boxTrans1.transform(closestA); + const Vec3V destNormal = V3Normalize(boxTrans1.rotate(normal)); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + const Vec3V destWorldPointA = boxTrans1.transform(closestA); + const Vec3V destNormal = V3Normalize(boxTrans1.rotate(normal)); + const FloatV length = FMul(worldDist, toi); + V3StoreU(V3Neg(destNormal), sweepHit.normal); + V3StoreU(destWorldPointA, sweepHit.position); + FStore(length, &sweepHit.distance); + } + return true; +} + +bool Gu::sweepBoxTriangles(GU_SWEEP_TRIANGLES_FUNC_PARAMS(PxBoxGeometry)) +{ + PX_UNUSED(hitFlags); + + if(!nbTris) + return false; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool doBackfaceCulling = !doubleSided && !meshBothSides; + + Box box; + buildFrom(box, pose.p, geom.halfExtents, pose.q); + + PxSweepHit sweepHit; + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localMotion = localDir * distance; + + const Vec3V base0 = V3LoadU(worldToBox.m.column0); + const Vec3V base1 = V3LoadU(worldToBox.m.column1); + const Vec3V base2 = V3LoadU(worldToBox.m.column2); + const Mat33V matV(base0, base1, base2); + const Vec3V p = V3LoadU(worldToBox.p); + const PsMatTransformV worldToBoxV(p, matV); + + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(box.extents); + const Vec3V boxDir = V3LoadU(localDir); + const FloatV inflationV = FLoad(inflation); + const Vec3V absBoxDir = V3Abs(boxDir); + const FloatV boxRadiusV = FAdd(V3Dot(absBoxDir, boxExtents), inflationV); + BoxV boxV(zeroV, boxExtents); + +#if PX_DEBUG + PxU32 totalTestsExpected = nbTris; + PxU32 totalTestsReal = 0; + PX_UNUSED(totalTestsExpected); + PX_UNUSED(totalTestsReal); +#endif + + Vec3V boxLocalMotion = V3LoadU(localMotion); + Vec3V minClosestA = zeroV, minNormal = zeroV; + PxU32 minTriangleIndex = 0; + PxVec3 bestTriNormal(0.0f); + FloatV dist = FLoad(distance); + + const PsTransformV boxPos = loadTransformU(pose); + + bool status = false; + + const PxU32 idx = cachedIndex ? *cachedIndex : 0; + + for(PxU32 ii=0;ii convexA(triangleV); + LocalConvex convexB(boxV); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter()); + if(gjkRaycastPenetration, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, boxLocalMotion, lambda, normal, closestA, inflation, false)) + { + //hitCount++; + + if(FAllGrtrOrEq(zero, lambda)) + { + hit.distance = 0.0f; + hit.faceIndex = triangleIndex; + hit.normal = -unitDir; + hit.flags = PxHitFlag::eNORMAL; + return true; + } + + dist = FMul(dist, lambda); + boxLocalMotion = V3Scale(boxDir, dist); + minClosestA = closestA; + minNormal = normal; + minTriangleIndex = triangleIndex; + V3StoreU(triNormal, bestTriNormal); + status = true; + if(hitFlags & PxHitFlag::eMESH_ANY) + break; + } + } + + if(!status) + return false; + + hit.faceIndex = minTriangleIndex; + const Vec3V destNormal = V3Neg(V3Normalize(boxPos.rotate(minNormal))); + const Vec3V destWorldPointA = boxPos.transform(minClosestA); + V3StoreU(destNormal, hit.normal); + V3StoreU(destWorldPointA, hit.position); + FStore(dist, &hit.distance); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(hit.normal, meshBothSides, doubleSided, bestTriNormal, unitDir)) + hit.normal = -hit.normal; + + hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL; + return true; +} + +bool sweepCapsule_SphereGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_PlaneGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_CapsuleGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_BoxGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_BoxGeom_Precise (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_ConvexGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_MeshGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); +bool sweepCapsule_HeightFieldGeom (GU_CAPSULE_SWEEP_FUNC_PARAMS); + +bool sweepBox_SphereGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_SphereGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_PlaneGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_CapsuleGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_CapsuleGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_BoxGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_BoxGeom_Precise (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_ConvexGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_MeshGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_HeightFieldGeom (GU_BOX_SWEEP_FUNC_PARAMS); +bool sweepBox_HeightFieldGeom_Precise(GU_BOX_SWEEP_FUNC_PARAMS); + +bool sweepConvex_SphereGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_PlaneGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_CapsuleGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_BoxGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_ConvexGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_MeshGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); +bool sweepConvex_HeightFieldGeom (GU_CONVEX_SWEEP_FUNC_PARAMS); + +static bool sweepCapsule_HeightfieldUnregistered(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(lss); + PX_UNUSED(unitDir); + PX_UNUSED(distance); + PX_UNUSED(sweepHit); + PX_UNUSED(hitFlags); + PX_UNUSED(inflation); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Sweep test called with height fields unregistered "); + return false; +} +static bool sweepBox_HeightfieldUnregistered(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(box); + PX_UNUSED(unitDir); + PX_UNUSED(distance); + PX_UNUSED(sweepHit); + PX_UNUSED(hitFlags); + PX_UNUSED(inflation); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Sweep test called with height fields unregistered "); + return false; +} +static bool sweepConvex_HeightfieldUnregistered(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(geom); + PX_UNUSED(pose); + PX_UNUSED(convexGeom); + PX_UNUSED(convexPose); + PX_UNUSED(unitDir); + PX_UNUSED(distance); + PX_UNUSED(sweepHit); + PX_UNUSED(hitFlags); + PX_UNUSED(inflation); + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Height Field Sweep test called with height fields unregistered "); + return false; +} + +Gu::GeomSweepFuncs gGeomSweepFuncs = +{ + { + sweepCapsule_SphereGeom, + sweepCapsule_PlaneGeom, + sweepCapsule_CapsuleGeom, + sweepCapsule_BoxGeom, + sweepCapsule_ConvexGeom, + sweepCapsule_MeshGeom, + sweepCapsule_HeightfieldUnregistered + }, + { + sweepCapsule_SphereGeom, + sweepCapsule_PlaneGeom, + sweepCapsule_CapsuleGeom, + sweepCapsule_BoxGeom_Precise, + sweepCapsule_ConvexGeom, + sweepCapsule_MeshGeom , + sweepCapsule_HeightfieldUnregistered + }, + { + sweepBox_SphereGeom, + sweepBox_PlaneGeom, + sweepBox_CapsuleGeom, + sweepBox_BoxGeom, + sweepBox_ConvexGeom, + sweepBox_MeshGeom, + sweepBox_HeightfieldUnregistered + }, + { + sweepBox_SphereGeom_Precise, + sweepBox_PlaneGeom, + sweepBox_CapsuleGeom_Precise, + sweepBox_BoxGeom_Precise, + sweepBox_ConvexGeom, + sweepBox_MeshGeom, + sweepBox_HeightfieldUnregistered + }, + { + sweepConvex_SphereGeom, // 0 + sweepConvex_PlaneGeom, // 1 + sweepConvex_CapsuleGeom, // 2 + sweepConvex_BoxGeom, // 3 + sweepConvex_ConvexGeom, // 4 + sweepConvex_MeshGeom, // 5 + sweepConvex_HeightfieldUnregistered // 6 + } +}; + +PX_PHYSX_COMMON_API const GeomSweepFuncs& Gu::getSweepFuncTable() +{ + return gGeomSweepFuncs; +} + +void registerHeightFields_Sweeps() +{ + gGeomSweepFuncs.capsuleMap[PxGeometryType::eHEIGHTFIELD] = sweepCapsule_HeightFieldGeom; + gGeomSweepFuncs.preciseCapsuleMap[PxGeometryType::eHEIGHTFIELD] = sweepCapsule_HeightFieldGeom; + gGeomSweepFuncs.boxMap[PxGeometryType::eHEIGHTFIELD] = sweepBox_HeightFieldGeom; + gGeomSweepFuncs.preciseBoxMap[PxGeometryType::eHEIGHTFIELD] = sweepBox_HeightFieldGeom_Precise; + gGeomSweepFuncs.convexMap[PxGeometryType::eHEIGHTFIELD] = sweepConvex_HeightFieldGeom; +} diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepTests.h b/src/PhysX/physx/source/geomutils/src/GuSweepTests.h new file mode 100644 index 000000000..a77fcd3d1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/GuSweepTests.h @@ -0,0 +1,136 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_TESTS_H +#define GU_SWEEP_TESTS_H + +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" +#include "PxGeometry.h" + +namespace physx +{ + class PxConvexMeshGeometry; + class PxCapsuleGeometry; + class PxTriangle; + class PxBoxGeometry; + + // PT: TODO: unify this with raycast calls (names and order of params) + + // PT: we use defines to be able to quickly change the signature of all sweep functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] geom geometry object to sweep against + // \param[in] pose pose of geometry object + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] sweepHit hit result + // \param[in] hitFlags query behavior flags + // \param[in] inflation optional inflation value for swept shape + + // PT: sweep parameters for capsule + #define GU_CAPSULE_SWEEP_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxCapsuleGeometry& capsuleGeom_, const PxTransform& capsulePose_, const Gu::Capsule& lss, \ + const PxVec3& unitDir, PxReal distance, \ + PxSweepHit& sweepHit, const PxHitFlags hitFlags, PxReal inflation + + // PT: sweep parameters for box + #define GU_BOX_SWEEP_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxBoxGeometry& boxGeom_, const PxTransform& boxPose_, const Gu::Box& box, \ + const PxVec3& unitDir, PxReal distance, \ + PxSweepHit& sweepHit, const PxHitFlags hitFlags, PxReal inflation + + // PT: sweep parameters for convex + #define GU_CONVEX_SWEEP_FUNC_PARAMS const PxGeometry& geom, const PxTransform& pose, \ + const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, \ + const PxVec3& unitDir, PxReal distance, \ + PxSweepHit& sweepHit, const PxHitFlags hitFlags, PxReal inflation + namespace Gu + { + class Capsule; + class Box; + + // PT: function pointer for Geom-indexed capsule sweep functions + // See GU_CAPSULE_SWEEP_FUNC_PARAMS for function parameters details. + // \return true if a hit was found, false otherwise + typedef bool (*SweepCapsuleFunc) (GU_CAPSULE_SWEEP_FUNC_PARAMS); + + // PT: function pointer for Geom-indexed box sweep functions + // See GU_BOX_SWEEP_FUNC_PARAMS for function parameters details. + // \return true if a hit was found, false otherwise + typedef bool (*SweepBoxFunc) (GU_BOX_SWEEP_FUNC_PARAMS); + + // PT: function pointer for Geom-indexed box sweep functions + // See GU_CONVEX_SWEEP_FUNC_PARAMS for function parameters details. + // \return true if a hit was found, false otherwise + typedef bool (*SweepConvexFunc) (GU_CONVEX_SWEEP_FUNC_PARAMS); + + // PT: typedef for bundles of all sweep functions, i.e. the function tables themselves (indexed by geom-type). + typedef SweepCapsuleFunc GeomSweepCapsuleTable [PxGeometryType::eGEOMETRY_COUNT]; + typedef SweepBoxFunc GeomSweepBoxTable [PxGeometryType::eGEOMETRY_COUNT]; + typedef SweepConvexFunc GeomSweepConvexTable [PxGeometryType::eGEOMETRY_COUNT]; + + struct GeomSweepFuncs + { + GeomSweepCapsuleTable capsuleMap; + GeomSweepCapsuleTable preciseCapsuleMap; + GeomSweepBoxTable boxMap; + GeomSweepBoxTable preciseBoxMap; + GeomSweepConvexTable convexMap; + }; + // PT: grabs all sweep function tables at once (for access by external non-Gu modules) + PX_PHYSX_COMMON_API const GeomSweepFuncs& getSweepFuncTable(); + + // PT: signature for sweep-vs-triangles functions. + // We use defines to be able to quickly change the signature of all sweep functions. + // (this also ensures they all use consistent names for passed parameters). + // \param[in] nbTris number of triangles in input array + // \param[in] triangles array of triangles to sweep the shape against + // \param[in] doubleSided true if input triangles are double-sided + // \param[in] x geom to sweep against input triangles + // \param[in] pose pose of geom x + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] hit hit result + // \param[in] cachedIndex optional initial triangle index (must be +{ + PX_NOCOPY(AccumCallback) + public: + + Ps::InlineArray& mResult; + AccumCallback(Ps::InlineArray& result) + : MeshHitCallback(CallbackMode::eMULTIPLE), + mResult(result) + { + } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3&, const PxVec3&, const PxVec3&, PxReal&, const PxU32*) + { + mResult.pushBack(hit.faceIndex); + return true; + } +}; + +// PT: TODO: refactor with MidPhaseQueryLocalReport +struct EntityReportContainerCallback : public EntityReport +{ + Ps::InlineArray& container; + EntityReportContainerCallback(Ps::InlineArray& container_) : container(container_) + { + container.forceSize_Unsafe(0); + } + virtual ~EntityReportContainerCallback() {} + + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + for(PxU32 i=0; iget()); + + Ps::InlineArray tempContainer; + + EntityReportContainerCallback callback(tempContainer); + + PxVec3 trA = transform0.p - lastTm0.p; + PxVec3 trB = transform1.p - lastTm1.p; + + PxVec3 relTr = trA - trB; + PxVec3 halfRelTr = relTr * 0.5f; + + const PxVec3 ext = shape0.mExtents + halfRelTr.abs() + PxVec3(restDistance); + const PxVec3 cent = shape0.mCenter + halfRelTr; + + PxBounds3 bounds0(cent - ext, cent + ext); + + hfUtil.overlapAABBTriangles(transform1, bounds0, GuHfQueryFlags::eWORLD_SPACE, &callback); + + Ps::Array orderedContainer(tempContainer.size()); + + Ps::Array distanceEntries(tempContainer.size()); + + PxU32* orderedList = orderedContainer.begin(); + PxF32* distances = reinterpret_cast(distanceEntries.begin()); + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents + PxVec3(restDistance); + + PxReal minTOI = PX_MAX_REAL; + + PxU32 numTrigs = tempContainer.size(); + PxU32* trianglesIndices = tempContainer.begin(); + + PxU32 count = 0; + for(PxU32 a = 0; a < numTrigs; ++a) + { + PxTriangle tri; + hfUtil.getTriangle(shape1.mPrevTransform, tri, 0, 0, trianglesIndices[a], true, true); + + PxVec3 resultNormal = -(tri.verts[1]-tri.verts[0]).cross(tri.verts[2]-tri.verts[0]); + resultNormal.normalize(); + + if(relTr.dot(resultNormal) >= fastMovingThreshold) + { + + PxBounds3 bounds; + bounds.setEmpty(); + bounds.include(tri.verts[0]); + bounds.include(tri.verts[1]); + bounds.include(tri.verts[2]); + + PxF32 toi = sweepAABBAABB(origin, extent * 1.1f, bounds.getCenter(), (bounds.getExtents() + PxVec3(0.01f, 0.01f, 0.01f)) * 1.1f, trA, trB); + + PxU32 index = 0; + if(toi <= 1.f) + { + for(PxU32 b = count; b > 0; --b) + { + if(distances[b-1] <= toi) + { + //shuffle down and swap + index = b; + break; + } + PX_ASSERT(b > 0); + PX_ASSERT(b < numTrigs); + distances[b] = distances[b-1]; + orderedList[b] = orderedList[b-1]; + } + PX_ASSERT(index < numTrigs); + orderedList[index] = trianglesIndices[a]; + distances[index] = toi; + count++; + } + } + } + + + + worldNormal = PxVec3(PxReal(0)); + worldPoint = PxVec3(PxReal(0)); + Cm::FastVertex2ShapeScaling idScale; + PxU32 ccdFaceIndex = PXC_CONTACT_NO_FACE_INDEX; + + PxVec3 sphereCenter(shape0.mPrevTransform.p); + PxF32 inSphereRadius = shape0.mFastMovingThreshold; + PxF32 inRadSq = inSphereRadius * inSphereRadius; + + + PxVec3 sphereCenterInTr1 = transform1.transformInv(sphereCenter); + PxVec3 sphereCenterInTr1T0 = transform1.transformInv(lastTm0.p); + + PxVec3 tempWorldNormal(0.f), tempWorldPoint(0.f); + + for (PxU32 ti = 0; ti < count; ti++) + { + PxTriangle tri; + hfUtil.getTriangle(lastTm1, tri, 0, 0, orderedList[ti], false, false); + + PxVec3 resultNormal, resultPoint; + + TriangleV triangle(V3LoadU(tri.verts[0]), V3LoadU(tri.verts[1]), V3LoadU(tri.verts[2])); + + //do sweep + + PxReal res = SweepShapeTriangle( + *shape0.mGeometry, *shape1.mGeometry, transform0, transform1, lastTm0, lastTm1, restDistance, + resultNormal, resultPoint, Cm::FastVertex2ShapeScaling(), triangle, + 0.f); + + if(res <= 0.f) + { + res = 0.f; + + const PxVec3 v0 = tri.verts[1] - tri.verts[0] ; + const PxVec3 v1 = tri.verts[2] - tri.verts[0]; + + //Now we have a 0 TOI, lets see if the in-sphere hit it! + + PxF32 distanceSq = distancePointTriangleSquared( sphereCenterInTr1, tri.verts[0], v0, v1); + + if(distanceSq < inRadSq) + { + const PxVec3 nor = v0.cross(v1); + const PxF32 distance = PxSqrt(distanceSq); + res = distance - inSphereRadius; + const PxF32 d = nor.dot(tri.verts[0]); + const PxF32 dd = nor.dot(sphereCenterInTr1T0); + if((dd - d) > 0.f) + { + //back side, penetration + res = -(2.f * inSphereRadius - distance); + } + } + } + + if (res < minTOI) + { + const PxVec3 v0 = tri.verts[1] - tri.verts[0] ; + const PxVec3 v1 = tri.verts[2] - tri.verts[0]; + + PxVec3 resultNormal1 = v0.cross(v1); + resultNormal1.normalize(); + //if(norDotRel > 1e-6f) + { + tempWorldNormal = resultNormal1; + tempWorldPoint = resultPoint; + minTOI = res; + ccdFaceIndex = orderedList[ti]; + } + } + + } + + worldNormal = transform1.rotate(tempWorldNormal); + worldPoint = tempWorldPoint; + + outCCDFaceIndex = ccdFaceIndex; + + return minTOI; +} + + +PxReal SweepEstimateAnyShapeHeightfield(GU_SWEEP_ESTIMATE_ARGS) +{ + HeightFieldUtil hfUtil(shape1.mGeometry->get()); + + Ps::InlineArray tempContainer; + + EntityReportContainerCallback callback(tempContainer); + + PxVec3 trA = transform0.p - lastTr0.p; + PxVec3 trB = transform1.p - lastTr1.p; + + PxVec3 relTr = trA - trB; + PxVec3 halfRelTr = relTr * 0.5f; + + const PxVec3 extents = shape0.mExtents + halfRelTr.abs() + PxVec3(restDistance); + const PxVec3 center = shape0.mCenter + halfRelTr; + + + PxBounds3 bounds0(center - extents, center + extents); + + + hfUtil.overlapAABBTriangles(transform1, bounds0, GuHfQueryFlags::eWORLD_SPACE, &callback); + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents; + + PxReal minTOI = PX_MAX_REAL; + + PxU32 numTrigs = tempContainer.size(); + PxU32* trianglesIndices = tempContainer.begin(); + + for(PxU32 a = 0; a < numTrigs; ++a) + { + + PxTriangle tri; + hfUtil.getTriangle(shape1.mPrevTransform, tri, 0, 0, trianglesIndices[a], true, true); + + + + PxVec3 resultNormal = -(tri.verts[1]-tri.verts[0]).cross(tri.verts[2]-tri.verts[0]); + resultNormal.normalize(); + + if(relTr.dot(resultNormal) >= fastMovingThreshold) + { + + PxBounds3 bounds; + bounds.setEmpty(); + bounds.include(tri.verts[0]); + bounds.include(tri.verts[1]); + bounds.include(tri.verts[2]); + + PxF32 toi = sweepAABBAABB(origin, extent * 1.1f, bounds.getCenter(), (bounds.getExtents() + PxVec3(0.01f, 0.01f, 0.01f)) * 1.1f, trA, trB); + + minTOI = PxMin(minTOI, toi); + } + } + + return minTOI; +} + + + +PxReal SweepAnyShapeMesh(GU_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(toiEstimate); + // this is the trimesh midphase for convex vs mesh sweep. shape0 is the convex shape. + + // Get actual shape data + const PxTriangleMeshGeometryLL& shapeMesh = shape1.mGeometry->get(); + + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + /*---------------------------------------------------*\ + | + | STEP1: OPCODE Geometry collection + | + \*---------------------------------------------------*/ + + PxVec3 trA = transform0.p - lastTm0.p; + PxVec3 trB = transform1.p - lastTm1.p; + + PxVec3 relTr = trA - trB; + PxVec3 unitDir = relTr; + PxReal length = unitDir.normalize(); + + PxMat33 matRot(PxIdentity); + + + //1) Compute the swept bounds + Box sweptBox; + computeSweptBox(sweptBox, shape0.mExtents, shape0.mCenter, matRot, unitDir, length); + + Box vertexSpaceBox; + if (shapeMesh.scale.isIdentity()) + vertexSpaceBox = transformBoxOrthonormal(sweptBox, transform1.getInverse()); + else + computeVertexSpaceOBB(vertexSpaceBox, sweptBox, transform1, shapeMesh.scale); + + + vertexSpaceBox.extents += PxVec3(restDistance); + + Ps::InlineArray tempContainer; + + AccumCallback callback(tempContainer); + + // AP scaffold: early out opportunities, should probably use fat raycast + Midphase::intersectOBB(shapeMesh.meshData, vertexSpaceBox, callback, true); + + if (tempContainer.size() == 0) + return PX_MAX_REAL; + + // Intersection found, fetch triangles + PxU32 numTrigs = tempContainer.size(); + const PxU32* triangleIndices = tempContainer.begin(); + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents + PxVec3(restDistance); + + Ps::InlineArray orderedContainer; + orderedContainer.resize(tempContainer.size()); + + Ps::InlineArray distanceEntries; + distanceEntries.resize(tempContainer.size()); + + PxU32* orderedList = orderedContainer.begin(); + PxF32* distances = reinterpret_cast(distanceEntries.begin()); + + PxReal minTOI = PX_MAX_REAL; + + + PxU32 count = 0; + for(PxU32 a = 0; a < numTrigs; ++a) + { + PxU32 unused; + ConvexTriangles convexPartOfMesh1(shapeMesh, meshScaling, &triangleIndices[a], 1, &unused); + + PxVec3 resultNormal = -transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + + if(relTr.dot(resultNormal) >= fastMovingThreshold) + { + PxBounds3 bounds; + convexPartOfMesh1.getBounds(bounds, lastTm1); + //OK, we have all 3 vertices, now calculate bounds... + + PxF32 toi = sweepAABBAABB(origin, extent, bounds.getCenter(), bounds.getExtents() + PxVec3(0.02f, 0.02f, 0.02f), trA, trB); + + PxU32 index = 0; + if(toi <= 1.f) + { + for(PxU32 b = count; b > 0; --b) + { + if(distances[b-1] <= toi) + { + //shuffle down and swap + index = b; + break; + } + PX_ASSERT(b > 0); + PX_ASSERT(b < numTrigs); + distances[b] = distances[b-1]; + orderedList[b] = orderedList[b-1]; + } + PX_ASSERT(index < numTrigs); + orderedList[index] = triangleIndices[a]; + distances[index] = toi; + count++; + } + } + } + + + + PxVec3 tempWorldNormal(0.f), tempWorldPoint(0.f); + + Cm::FastVertex2ShapeScaling idScale; + PxU32 ccdFaceIndex = PXC_CONTACT_NO_FACE_INDEX; + + PxVec3 sphereCenter(lastTm1.p); + PxF32 inSphereRadius = shape0.mFastMovingThreshold; + //PxF32 inRadSq = inSphereRadius * inSphereRadius; + + PxVec3 sphereCenterInTransform1 = transform1.transformInv(sphereCenter); + + PxVec3 sphereCenterInTransform0p = transform1.transformInv(lastTm0.p); + + + for (PxU32 ti = 0; ti < count /*&& PxMax(minTOI, 0.f) >= distances[ti]*/; ti++) + { + PxU32 unused; + ConvexTriangles convexPartOfMesh1(shapeMesh, meshScaling, &orderedList[ti], 1, &unused); + + PxVec3 resultNormal, resultPoint, v0l, v1l, v2l; + TriangleVertexPointers::getTriangleVerts(shapeMesh.meshData, orderedList[ti], v0l, v1l, v2l); + const bool flipNormal = meshScaling.flipsNormal(); + + const PxVec3 v0 = meshScaling * v0l; + const PxVec3 v1 = meshScaling * (flipNormal ? v2l : v1l); + const PxVec3 v2 = meshScaling * (flipNormal ? v1l : v2l); + + TriangleV triangle(V3LoadU(v0), V3LoadU(v1), V3LoadU(v2)); + + //do sweep + PxReal res = SweepShapeTriangle( + *shape0.mGeometry, *shape1.mGeometry, transform0, transform1, lastTm0, lastTm1, restDistance, + resultNormal, resultPoint, Cm::FastVertex2ShapeScaling(), triangle, + 0.f); + + resultNormal = -resultNormal; + + if(res <= 0.f) + { + res = 0.f; + + PxF32 inRad = inSphereRadius + restDistance; + PxF32 inRadSq = inRad*inRad; + + const PxVec3 vv0 = v1 - v0 ; + const PxVec3 vv1 = v2 - v0; + const PxVec3 nor = vv0.cross(vv1); + + //Now we have a 0 TOI, lets see if the in-sphere hit it! + + PxF32 distanceSq = distancePointTriangleSquared( sphereCenterInTransform1, v0, vv0, vv1); + + if(distanceSq < inRadSq) + { + const PxF32 distance = PxSqrt(distanceSq); + res = distance - inRad; + const PxF32 d = nor.dot(v0); + const PxF32 dd = nor.dot(sphereCenterInTransform0p); + if((dd - d) < 0.f) + { + //back side, penetration + res = -(2.f * inRad - distance); + } + } + PX_ASSERT(PxIsFinite(res)); + resultNormal = transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + } + + if (res < minTOI) + { + tempWorldNormal = resultNormal;//convexPartOfMesh1.getPolygonNormal(0);//transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + tempWorldPoint = resultPoint; + minTOI = res; + ccdFaceIndex = orderedList[ti]; + } + + } + + worldNormal = tempWorldNormal;//transform1.rotate(tempWorldNormal); + worldPoint = tempWorldPoint; + outCCDFaceIndex = ccdFaceIndex; + return minTOI; +} + + +/** +\brief This code performs a conservative estimate of the TOI of a shape v mesh. +*/ +PxReal SweepEstimateAnyShapeMesh(GU_SWEEP_ESTIMATE_ARGS) +{ + // this is the trimesh midphase for convex vs mesh sweep. shape0 is the convex shape. + // Get actual shape data + const PxTriangleMeshGeometryLL& shapeMesh = shape1.mGeometry->get(); + + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + + /*---------------------------------------------------*\ + | + | STEP1: OPCODE Geometry collection + | + \*---------------------------------------------------*/ + + PxVec3 trA = transform0.p - lastTr0.p; + PxVec3 trB = transform1.p - lastTr1.p; + + PxVec3 relTr = trA - trB; + PxVec3 unitDir = relTr; + PxReal length = unitDir.normalize(); + + PxMat33 matRot(PxIdentity); + + //1) Compute the swept bounds + Box sweptBox; + computeSweptBox(sweptBox, shape0.mExtents, shape0.mCenter, matRot, unitDir, length); + + Box vertexSpaceBox; + computeVertexSpaceOBB(vertexSpaceBox, sweptBox, transform1, shapeMesh.scale); + + vertexSpaceBox.extents += PxVec3(restDistance); + + // TODO: implement a cached mode that fetches the trigs from a cache rather than per opcode if there is little motion. + + struct CB : MeshHitCallback + { + PxReal minTOI; + PxReal sumFastMovingThresh; + const PxTriangleMeshGeometryLL& shapeMesh; + const Cm::FastVertex2ShapeScaling& meshScaling; + const PxVec3& relTr; + const PxVec3& trA; + const PxVec3& trB; + const PxTransform& transform1; + const PxVec3& origin; + const PxVec3& extent; + + CB(PxReal aSumFast, const PxTriangleMeshGeometryLL& aShapeMesh, const Cm::FastVertex2ShapeScaling& aMeshScaling, + const PxVec3& aRelTr, const PxVec3& atrA, const PxVec3& atrB, const PxTransform& aTransform1, const PxVec3& aOrigin, const PxVec3& aExtent) + : MeshHitCallback(CallbackMode::eMULTIPLE), + sumFastMovingThresh(aSumFast), shapeMesh(aShapeMesh), meshScaling(aMeshScaling), relTr(aRelTr), trA(atrA), trB(atrB), + transform1(aTransform1), origin(aOrigin), extent(aExtent) + { + minTOI = PX_MAX_REAL; + } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3&, const PxVec3&, const PxVec3&, PxReal& shrunkMaxT, const PxU32*) + { + PxU32 unused; + ConvexTriangles convexPartOfMesh1(shapeMesh, meshScaling, &hit.faceIndex, 1, &unused); + PxVec3 resultNormal = -transform1.rotate(convexPartOfMesh1.getPolygonNormal(0)); + if(relTr.dot(resultNormal) >= sumFastMovingThresh) + { + PxBounds3 bounds; + convexPartOfMesh1.getBounds(bounds, transform1); + //OK, we have all 3 vertices, now calculate bounds... + + PxF32 toi = sweepAABBAABB( + origin, extent * 1.1f, bounds.getCenter(), (bounds.getExtents() + PxVec3(0.01f, 0.01f, 0.01f)) * 1.1f, trA, trB); + + minTOI = PxMin(minTOI, toi); + shrunkMaxT = minTOI; + } + + return (minTOI > 0.0f); // stop traversal if minTOI == 0.0f + } + + void operator=(const CB&) {} + }; + + PxVec3 origin = shape0.mCenter; + PxVec3 extent = shape0.mExtents + PxVec3(restDistance); + + CB callback(fastMovingThreshold, shapeMesh, meshScaling, relTr, trA, trB, transform1, origin, extent); + Midphase::intersectOBB(shapeMesh.meshData, vertexSpaceBox, callback, true); + + return callback.minTOI; +} + + +} +} + diff --git a/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h new file mode 100644 index 000000000..70eecc954 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef GU_CCD_SWEEP_H +#define GU_CCD_SWEEP_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsVecTransform.h" +#include "GuGeometryUnion.h" +#include "CmScaling.h" + +#define GU_TRIANGLE_SWEEP_METHOD_ARGS \ + const Gu::GeometryUnion& shape0, \ + const Gu::GeometryUnion& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const PxTransform& lastTm0, \ + const PxTransform& lastTm1, \ + PxReal restDistance, \ + PxVec3& worldNormal, \ + PxVec3& worldPoint, \ + const Cm::FastVertex2ShapeScaling& meshScaling, \ + Gu::TriangleV& triangle, \ + const PxF32 toiEstimate + + +#define GU_SWEEP_METHOD_ARGS \ + const Gu::CCDShape& shape0, \ + const Gu::CCDShape& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const PxTransform& lastTm0, \ + const PxTransform& lastTm1, \ + PxReal restDistance, \ + PxVec3& worldNormal, \ + PxVec3& worldPoint, \ + const PxF32 toiEstimate, \ + PxU32& outCCDFaceIndex, \ + const PxReal fastMovingThreshold + + +#define GU_SWEEP_ESTIMATE_ARGS \ + const CCDShape& shape0, \ + const CCDShape& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const PxTransform& lastTr0, \ + const PxTransform& lastTr1, \ + const PxReal restDistance, \ + const PxReal fastMovingThreshold + + +#define GU_SWEEP_METHOD_ARGS_UNUSED \ + const Gu::CCDShape& /*shape0*/, \ + const Gu::CCDShape& /*shape1*/, \ + const PxTransform& /*transform0*/, \ + const PxTransform& /*transform1*/, \ + const PxTransform& /*lastTm0*/, \ + const PxTransform& /*lastTm1*/, \ + PxReal /*restDistance*/, \ + PxVec3& /*worldNormal*/, \ + PxVec3& /*worldPoint*/, \ + const PxF32 /*toiEstimate*/, \ + PxU32& /*outCCDFaceIndex*/, \ + const PxReal /*fastMovingThreshold*/ + +namespace physx +{ +namespace Gu +{ + struct CCDShape + { + const Gu::GeometryUnion* mGeometry; + PxReal mFastMovingThreshold; //The CCD threshold for this shape + PxTransform mPrevTransform; //This shape's previous transform + PxTransform mCurrentTransform; //This shape's current transform + PxVec3 mExtents; //The extents of this shape's AABB + PxVec3 mCenter; //The center of this shape's AABB + PxU32 mUpdateCount; //How many times this shape has been updated in the CCD. This is correlated with the CCD body's update count. + }; + + PX_FORCE_INLINE PxF32 sweepAABBAABB(const PxVec3& centerA, const PxVec3& extentsA, const PxVec3& centerB, const PxVec3& extentsB, const PxVec3& trA, const PxVec3& trB) + { + //Sweep 2 AABBs against each other, return the TOI when they hit else PX_MAX_REAL if they don't hit + const PxVec3 cAcB = centerA - centerB; + const PxVec3 sumExtents = extentsA + extentsB; + + //Initial hit + if(PxAbs(cAcB.x) <= sumExtents.x && + PxAbs(cAcB.y) <= sumExtents.y && + PxAbs(cAcB.z) <= sumExtents.z) + return 0.f; + + //No initial hit - perform the sweep + const PxVec3 relTr = trB - trA; + PxF32 tfirst = 0.f; + PxF32 tlast = 1.f; + + const PxVec3 aMax = centerA + extentsA; + const PxVec3 aMin = centerA - extentsA; + const PxVec3 bMax = centerB + extentsB; + const PxVec3 bMin = centerB - extentsB; + + const PxF32 eps = 1e-6f; + + for(PxU32 a = 0; a < 3; ++a) + { + if(relTr[a] < -eps) + { + if(bMax[a] < aMin[a]) + return PX_MAX_REAL; + if(aMax[a] < bMin[a]) + tfirst = PxMax((aMax[a] - bMin[a])/relTr[a], tfirst); + if(bMax[a] > aMin[a]) + tlast = PxMin((aMin[a] - bMax[a])/relTr[a], tlast); + } + else if(relTr[a] > eps) + { + if(bMin[a] > aMax[a]) + return PX_MAX_REAL; + if(bMax[a] < aMin[a]) + tfirst = PxMax((aMin[a] - bMax[a])/relTr[a], tfirst); + if(aMax[a] > bMin[a]) + tlast = PxMin((aMax[a] - bMin[a])/relTr[a], tlast); + } + else + { + if(bMax[a] < aMin[a] || bMin[a] > aMax[a]) + return PX_MAX_REAL; + } + + //No hit + if(tfirst > tlast) + return PX_MAX_REAL; + } + //There was a hit so return the TOI + return tfirst; + } + + PX_PHYSX_COMMON_API PxReal SweepShapeShape(GU_SWEEP_METHOD_ARGS); + + PX_PHYSX_COMMON_API PxReal SweepEstimateAnyShapeHeightfield(GU_SWEEP_ESTIMATE_ARGS); + + PX_PHYSX_COMMON_API PxReal SweepEstimateAnyShapeMesh(GU_SWEEP_ESTIMATE_ARGS); + + +} +} +#endif + diff --git a/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp new file mode 100644 index 000000000..8f6b1401b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp @@ -0,0 +1,286 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "Ps.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecTriangle.h" +#include "GuGJKRaycast.h" +#include "GuCCDSweepConvexMesh.h" +#include "GuGJKType.h" + +namespace physx +{ +namespace Gu +{ + +using namespace Ps::aos; + +template PX_FORCE_INLINE PxReal getRadius(const PxGeometry&) +{ + return 0; +} + +template<> PX_FORCE_INLINE PxReal getRadius(const PxGeometry& g) +{ + PX_ASSERT(g.getType() == PxGeometryType::eCAPSULE || g.getType() == PxGeometryType::eSPHERE); + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(PxSphereGeometry, radius) == PX_OFFSET_OF(PxCapsuleGeometry, radius)); + return static_cast(g).radius; +} + + + +template +static PxReal CCDSweep(ConvexA& a, ConvexB& b, const PxTransform& transform0, const PxTransform& transform1, const PxTransform& lastTm0, const PxTransform& lastTm1, + const Ps::aos::FloatV& toiEstimate, PxVec3& worldPoint, PxVec3& worldNormal, PxReal inflation = 0.f) +{ + PX_UNUSED(toiEstimate); //KS - TODO - can we use this again? + using namespace Ps::aos; + + const Vec3V zero = V3Zero(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&lastTm0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&lastTm1.p.x); + + const PsTransformV tr0(p0, q0); + const PsTransformV tr1(p1, q1); + + const PsMatTransformV aToB(tr1.transformInv(tr0)); + + const Vec3V trans0p = V3LoadU(transform0.p); + const Vec3V trans1p = V3LoadU(transform1.p); + const Vec3V trA = V3Sub(trans0p, p0); + const Vec3V trB = V3Sub(trans1p, p1); + const Vec3V relTr = tr1.rotateInv(V3Sub(trB, trA)); + + FloatV lambda; + Vec3V closestA, normal; + const FloatV initialLambda = FZero(); + const RelativeConvex convexA(a, aToB); + const LocalConvex convexB(b); + if(gjkRaycastPenetration, LocalConvex >(convexA, convexB, aToB.p, initialLambda, zero, relTr, lambda, normal, closestA, inflation, true)) + { + //Adjust closestA because it will be on the surface of convex a in its initial position (s). If the TOI > 0, we need to move + //the point along the sweep direction to get the world-space hit position. + PxF32 res; + FStore(lambda, &res); + closestA = V3ScaleAdd(trA, FMax(lambda, FZero()), tr1.transform(closestA)); + normal = tr1.rotate(normal); + + V3StoreU(normal, worldNormal); + V3StoreU(closestA, worldPoint); + return res; + } + return PX_MAX_REAL; +} + + + +// +// lookup table for geometry-vs-geometry sweeps +// + + +PxReal UnimplementedSweep (GU_SWEEP_METHOD_ARGS_UNUSED) +{ + return PX_MAX_REAL; //no impact +} + +template +PxReal SweepGeomGeom(GU_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(outCCDFaceIndex); + PX_UNUSED(fastMovingThreshold); + + const PxGeometry& g0 = shape0.mGeometry->getGeometry(); + const PxGeometry& g1 = shape1.mGeometry->getGeometry(); + + typename ConvexGeom::Type geom0(g0); + typename ConvexGeom::Type geom1(g1); + + return CCDSweep(geom0, geom1, transform0, transform1, lastTm0, lastTm1, FLoad(toiEstimate), worldPoint, worldNormal, restDistance+getRadius(g0)+getRadius(g1) ); +} + +typedef PxReal (*SweepMethod) (GU_SWEEP_METHOD_ARGS); + +PxReal SweepAnyShapeHeightfield(GU_SWEEP_METHOD_ARGS); +PxReal SweepAnyShapeMesh(GU_SWEEP_METHOD_ARGS); + +SweepMethod g_SweepMethodTable[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT] = +{ + //PxGeometryType::eSPHERE + { + SweepGeomGeom, //PxGeometryType::eSPHERE + UnimplementedSweep, //PxGeometryType::ePLANE + SweepGeomGeom, //PxGeometryType::eCAPSULE + SweepGeomGeom, //PxGeometryType::eBOX + SweepGeomGeom, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD //TODO + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + UnimplementedSweep, //PxGeometryType::ePLANE + UnimplementedSweep, //PxGeometryType::eCAPSULE + UnimplementedSweep, //PxGeometryType::eBOX + UnimplementedSweep, //PxGeometryType::eCONVEXMESH + UnimplementedSweep, //PxGeometryType::eTRIANGLEMESH + UnimplementedSweep, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + SweepGeomGeom, //PxGeometryType::eCAPSULE + SweepGeomGeom, //PxGeometryType::eBOX + SweepGeomGeom, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + SweepGeomGeom, //PxGeometryType::eBOX + SweepGeomGeom, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + SweepGeomGeom, //PxGeometryType::eCONVEXMESH + SweepAnyShapeMesh, //PxGeometryType::eTRIANGLEMESH + SweepAnyShapeHeightfield, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + UnimplementedSweep, //PxGeometryType::eTRIANGLEMESH + UnimplementedSweep, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + UnimplementedSweep, //PxGeometryType::eHEIGHTFIELD + }, +}; + + +PxReal SweepShapeShape(GU_SWEEP_METHOD_ARGS) +{ + PxGeometryType::Enum type0 = shape0.mGeometry->getType(); + PxGeometryType::Enum type1 = shape1.mGeometry->getType(); + + return g_SweepMethodTable[type0][type1](shape0, shape1, transform0, transform1, lastTm0, lastTm1, + restDistance, worldNormal, worldPoint, toiEstimate, outCCDFaceIndex, fastMovingThreshold); + +} + +// +// lookup table for sweeps agains triangles +// + +PxReal UnimplementedTriangleSweep(GU_TRIANGLE_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(shape1); + PX_UNUSED(transform0); + PX_UNUSED(transform1); + PX_UNUSED(lastTm0); + PX_UNUSED(lastTm1); + PX_UNUSED(restDistance); + PX_UNUSED(worldNormal); + PX_UNUSED(worldPoint); + PX_UNUSED(meshScaling); + PX_UNUSED(triangle); + PX_UNUSED(toiEstimate); + + return 1e10f; //no impact +} + +template +PxReal SweepGeomTriangles(GU_TRIANGLE_SWEEP_METHOD_ARGS) +{ + PX_UNUSED(meshScaling); + PX_UNUSED(shape1); + + const PxGeometry& g = shape0.getGeometry(); + //Geom geom(g); + typename ConvexGeom::Type geom(g); + + return CCDSweep(triangle, geom, transform1, transform0, lastTm1, lastTm0, FLoad(toiEstimate), worldPoint, worldNormal, restDistance+getRadius(g) ); +} + +typedef PxReal (*TriangleSweepMethod) (GU_TRIANGLE_SWEEP_METHOD_ARGS); +TriangleSweepMethod g_TriangleSweepMethodTable[PxGeometryType::eGEOMETRY_COUNT] = +{ + SweepGeomTriangles, //PxGeometryType::eSPHERE + UnimplementedTriangleSweep, //PxGeometryType::ePLANE + SweepGeomTriangles, //PxGeometryType::eCAPSULE + SweepGeomTriangles, //PxGeometryType::eBOX + SweepGeomTriangles, //PxGeometryType::eCONVEXMESH + UnimplementedTriangleSweep, //PxGeometryType::eTRIANGLEMESH + UnimplementedTriangleSweep, //PxGeometryType::eHEIGHTFIELD +}; + +PxReal SweepShapeTriangle(GU_TRIANGLE_SWEEP_METHOD_ARGS) +{ + const PxGeometryType::Enum type0 = shape0.getType(); + TriangleSweepMethod method = g_TriangleSweepMethodTable[type0]; + return method(shape0, shape1, transform0, transform1, lastTm0, lastTm1, restDistance, worldNormal, worldPoint, meshScaling, triangle, toiEstimate); +} + +} +} + diff --git a/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp new file mode 100644 index 000000000..a072a3918 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp @@ -0,0 +1,89 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuBarycentricCoordinates.h" + +using namespace physx; +using namespace Ps::aos; + +void Gu::barycentricCoordinates(const Vec3VArg p, const Vec3VArg a, const Vec3VArg b, FloatV& v) +{ + const Vec3V v0 = V3Sub(a, p); + const Vec3V v1 = V3Sub(b, p); + const Vec3V d = V3Sub(v1, v0); + const FloatV denominator = V3Dot(d, d); + const FloatV numerator = V3Dot(V3Neg(v0), d); + const FloatV zero = FZero(); + const FloatV denom = FSel(FIsGrtr(denominator, zero), FRecip(denominator), zero); + v = FMul(numerator, denom); +} + +void Gu::barycentricCoordinates(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, Ps::aos::FloatV& v, Ps::aos::FloatV& w) +{ + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + + const Vec3V n = V3Cross(ab, ac); + + const VecCrossV crossA = V3PrepareCross(V3Sub(a, p)); + const VecCrossV crossB = V3PrepareCross(V3Sub(b, p)); + const VecCrossV crossC = V3PrepareCross(V3Sub(c, p)); + const Vec3V bCrossC = V3Cross(crossB, crossC); + const Vec3V cCrossA = V3Cross(crossC, crossA); + const Vec3V aCrossB = V3Cross(crossA, crossB); + + const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + const FloatV totalArea =FAdd(va, FAdd(vb, vc)); + const FloatV zero = FZero(); + const FloatV denom = FSel(FIsEq(totalArea, zero), zero, FRecip(totalArea)); + v = FMul(vb, denom); + w = FMul(vc, denom); + +} + +/* + v0 = b - a; + v1 = c - a; + v2 = p - a; +*/ +void Gu::barycentricCoordinates(const Vec3VArg v0, const Vec3VArg v1, const Vec3VArg v2, FloatV& v, FloatV& w) +{ + const FloatV d00 = V3Dot(v0, v0); + const FloatV d01 = V3Dot(v0, v1); + const FloatV d11 = V3Dot(v1, v1); + const FloatV d20 = V3Dot(v2, v0); + const FloatV d21 = V3Dot(v2, v1); + const FloatV denom = FRecip(FSub(FMul(d00,d11), FMul(d01, d01))); + v = FMul(FSub(FMul(d11, d20), FMul(d01, d21)), denom); + w = FMul(FSub(FMul(d00, d21), FMul(d01, d20)), denom); +} + diff --git a/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h new file mode 100644 index 000000000..4dea0543a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BARYCENTRIC_COORDINATES_H +#define GU_BARYCENTRIC_COORDINATES_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + //calculate the barycentric coorinates for a point in a segment + void barycentricCoordinates(const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + Ps::aos::FloatV& v); + + //calculate the barycentric coorinates for a point in a triangle + void barycentricCoordinates(const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& v, + Ps::aos::FloatV& w); + + void barycentricCoordinates(const Ps::aos::Vec3VArg v0, + const Ps::aos::Vec3VArg v1, + const Ps::aos::Vec3VArg v2, + Ps::aos::FloatV& v, + Ps::aos::FloatV& w); + + PX_INLINE Ps::aos::BoolV isValidTriangleBarycentricCoord(const Ps::aos::FloatVArg v, const Ps::aos::FloatVArg w) + { + using namespace Ps::aos; + const FloatV zero = FNeg(FEps()); + const FloatV one = FAdd(FOne(), FEps()); + + const BoolV con0 = BAnd(FIsGrtrOrEq(v, zero), FIsGrtrOrEq(one, v)); + const BoolV con1 = BAnd(FIsGrtrOrEq(w, zero), FIsGrtrOrEq(one, w)); + const BoolV con2 = FIsGrtr(one, FAdd(v, w)); + return BAnd(con0, BAnd(con1, con2)); + } + + PX_INLINE Ps::aos::BoolV isValidTriangleBarycentricCoord2(const Ps::aos::Vec4VArg vwvw) + { + using namespace Ps::aos; + const Vec4V eps = V4Splat(FEps()); + const Vec4V zero =V4Neg(eps); + const Vec4V one = V4Add(V4One(), eps); + + const Vec4V v0v1v0v1 = V4PermXZXZ(vwvw); + const Vec4V w0w1w0w1 = V4PermYWYW(vwvw); + + const BoolV con0 = BAnd(V4IsGrtrOrEq(v0v1v0v1, zero), V4IsGrtrOrEq(one, v0v1v0v1)); + const BoolV con1 = BAnd(V4IsGrtrOrEq(w0w1w0w1, zero), V4IsGrtrOrEq(one, w0w1w0w1)); + const BoolV con2 = V4IsGrtr(one, V4Add(v0v1v0v1, w0w1w0w1)); + return BAnd(con0, BAnd(con1, con2)); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h b/src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h new file mode 100644 index 000000000..5d2c01c9e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h @@ -0,0 +1,121 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BOX_CONVERSION_H +#define GU_BOX_CONVERSION_H + +#include "GuBox.h" +#include "PsMathUtils.h" +#include "CmMatrix34.h" +#include "PsVecMath.h" + +namespace physx +{ + // PT: builds rot from quat. WARNING: writes 4 bytes after 'dst.rot'. + PX_FORCE_INLINE void buildFrom(Gu::Box& dst, const PxQuat& q) + { + using namespace Ps::aos; + const QuatV qV = V4LoadU(&q.x); + Vec3V column0, column1, column2; + QuatGetMat33V(qV, column0, column1, column2); + // PT: TODO: investigate if these overlapping stores are a problem + V4StoreU(Vec4V_From_Vec3V(column0), &dst.rot.column0.x); + V4StoreU(Vec4V_From_Vec3V(column1), &dst.rot.column1.x); + V4StoreU(Vec4V_From_Vec3V(column2), &dst.rot.column2.x); + } + + PX_FORCE_INLINE void buildFrom(Gu::Box& dst, const PxVec3& center, const PxVec3& extents, const PxQuat& q) + { + using namespace Ps::aos; + // PT: writes 4 bytes after 'rot' but it's safe since we then write 'center' just afterwards + buildFrom(dst, q); + dst.center = center; + dst.extents = extents; + } + + PX_FORCE_INLINE void buildMatrixFromBox(Cm::Matrix34& mat34, const Gu::Box& box) + { + mat34.m = box.rot; + mat34.p = box.center; + } + + // SD: function is now the same as FastVertex2ShapeScaling::transformQueryBounds + // PT: lots of LHS in that one. TODO: revisit... + PX_INLINE Gu::Box transform(const Cm::Matrix34& transfo, const Gu::Box& box) + { + Gu::Box ret; + PxMat33& obbBasis = ret.rot; + + obbBasis.column0 = transfo.rotate(box.rot.column0 * box.extents.x); + obbBasis.column1 = transfo.rotate(box.rot.column1 * box.extents.y); + obbBasis.column2 = transfo.rotate(box.rot.column2 * box.extents.z); + + ret.center = transfo.transform(box.center); + ret.extents = Ps::optimizeBoundingBox(obbBasis); + return ret; + } + + PX_INLINE Gu::Box transformBoxOrthonormal(const Gu::Box& box, const PxTransform& t) + { + Gu::Box ret; + PxMat33& obbBasis = ret.rot; + obbBasis.column0 = t.rotate(box.rot.column0); + obbBasis.column1 = t.rotate(box.rot.column1); + obbBasis.column2 = t.rotate(box.rot.column2); + ret.center = t.transform(box.center); + ret.extents = box.extents; + return ret; + } + + /** + \brief recomputes the OBB after an arbitrary transform by a 4x4 matrix. + \param mtx [in] the transform matrix + \param obb [out] the transformed OBB + */ + PX_INLINE void rotate(const Gu::Box& src, const Cm::Matrix34& mtx, Gu::Box& obb) + { + // The extents remain constant + obb.extents = src.extents; + // The center gets x-formed + obb.center = mtx.transform(src.center); + // Combine rotations + obb.rot = mtx.m * src.rot; + } + +// PT: TODO: move this to a better place + PX_FORCE_INLINE void getInverse(PxMat33& dstRot, PxVec3& dstTrans, const PxMat33& srcRot, const PxVec3& srcTrans) + { + const PxMat33 invRot = srcRot.getInverse(); + dstTrans = invRot.transform(-srcTrans); + dstRot = invRot; + } + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h b/src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h new file mode 100644 index 000000000..3729fc600 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h @@ -0,0 +1,85 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EDGECACHE_H +#define GU_EDGECACHE_H + +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "PsHash.h" + +namespace physx +{ +namespace Gu +{ + class EdgeCache + { +#define NUM_EDGES_IN_CACHE 64 //must be power of 2. 32 lines result in 10% extra work (due to cache misses), 64 lines in 6% extra work, 128 lines in 4%. + public: + EdgeCache() + { + PxMemZero(cacheLines, NUM_EDGES_IN_CACHE*sizeof(CacheLine)); + } + + PxU32 hash(PxU32 key) const + { + return (NUM_EDGES_IN_CACHE - 1) & Ps::hash(key); //Only a 16 bit hash would be needed here. + } + + bool isInCache(PxU8 vertex0, PxU8 vertex1) + { + PX_ASSERT(vertex1 >= vertex0); + PxU16 key = PxU16((vertex0 << 8) | vertex1); + PxU32 h = hash(key); + CacheLine& cl = cacheLines[h]; + if (cl.fullKey == key) + { + return true; + } + else //cache the line now as it's about to be processed + { + cl.fullKey = key; + return false; + } + } + + private: + struct CacheLine + { + PxU16 fullKey; + }; + CacheLine cacheLines[NUM_EDGES_IN_CACHE]; +#undef NUM_EDGES_IN_CACHE + }; +} + +} + +#endif + diff --git a/src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h b/src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h new file mode 100644 index 000000000..c32b58c94 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h @@ -0,0 +1,153 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EDGE_LIST_DATA_H +#define GU_EDGE_LIST_DATA_H + +#include "foundation/PxSimpleTypes.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + +/*! +NOTICE! + +This is a data-code separated version of PxPhysics::EdgeList. + +It is to be shared between high and low level code, so make sure both are recompiled +if any change is done here. +*/ + +// Flags +enum EdgeType +{ + PX_EDGE_UNDEFINED, + + PX_EDGE_BOUNDARY, //!< Edge belongs to a single triangle + PX_EDGE_INTERNAL, //!< Edge belongs to exactly two triangles + PX_EDGE_SINGULAR, //!< Edge belongs to three or more triangles + + PX_EDGE_FORCE_DWORD = 0x7fffffff +}; + +enum EdgeFlag +{ + PX_EDGE_ACTIVE = (1<<0) +}; + + +// Data + + +//! Basic edge-data +struct EdgeData +{ + PxU32 Ref0; //!< First vertex reference + PxU32 Ref1; //!< Second vertex reference +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeData) == 8); + + +//! Basic edge-data using 8-bit references +struct Edge8Data +{ + PxU8 Ref0; //!< First vertex reference + PxU8 Ref1; //!< Second vertex reference +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::Edge8Data) == 2); + + +//! A count/offset pair = an edge descriptor +struct EdgeDescData +{ + PxU16 Flags; + PxU16 Count; + PxU32 Offset; +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeDescData) == 8); + + +//! Edge<->triangle mapping +struct EdgeTriangleData +{ + PxU32 mLink[3]; +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeTriangleData) == 12); + + +struct EdgeListData +{ + // The edge list + PxU32 mNbEdges; //!< Number of edges in the list + Gu::EdgeData* mEdges; //!< List of edges + // Faces to edges + PxU32 mNbFaces; //!< Number of faces for which we have data + Gu::EdgeTriangleData* mEdgeFaces; //!< Array of edge-triangles referencing mEdges + // Edges to faces + Gu::EdgeDescData* mEdgeToTriangles; //!< An EdgeDesc structure for each edge + PxU32* mFacesByEdges; //!< A pool of face indices +}; +#if PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeListData) == 48); +#else +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeListData) == 24); +#endif + + +// Accessors + +enum +{ + MSH_EDGE_LINK_MASK = 0x0fffffff, + MSH_ACTIVE_EDGE_MASK = 0x80000000, + MSH_ACTIVE_VERTEX_MASK = 0x40000000 +}; + +class EdgeTriangleAC +{ +public: + PX_INLINE static PxU32 GetEdge01(const Gu::EdgeTriangleData& data) { return data.mLink[0] & MSH_EDGE_LINK_MASK; } + PX_INLINE static PxU32 GetEdge12(const Gu::EdgeTriangleData& data) { return data.mLink[1] & MSH_EDGE_LINK_MASK; } + PX_INLINE static PxU32 GetEdge20(const Gu::EdgeTriangleData& data) { return data.mLink[2] & MSH_EDGE_LINK_MASK; } + PX_INLINE static PxU32 GetEdge(const Gu::EdgeTriangleData& data, PxU32 i) { return data.mLink[i] & MSH_EDGE_LINK_MASK; } + + PX_INLINE static Ps::IntBool HasActiveEdge01(const Gu::EdgeTriangleData& data) { return Ps::IntBool(data.mLink[0] & MSH_ACTIVE_EDGE_MASK); } + PX_INLINE static Ps::IntBool HasActiveEdge12(const Gu::EdgeTriangleData& data) { return Ps::IntBool(data.mLink[1] & MSH_ACTIVE_EDGE_MASK); } + PX_INLINE static Ps::IntBool HasActiveEdge20(const Gu::EdgeTriangleData& data) { return Ps::IntBool(data.mLink[2] & MSH_ACTIVE_EDGE_MASK); } + PX_INLINE static Ps::IntBool HasActiveEdge(const Gu::EdgeTriangleData& data, PxU32 i) { return Ps::IntBool(data.mLink[i] & MSH_ACTIVE_EDGE_MASK); } +}; + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp b/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp new file mode 100644 index 000000000..8f20642b2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSeparatingAxes.h" + +using namespace physx; + +union FloatInt +{ + float f; + PxU32 i; +}; + +bool Gu::SeparatingAxes::addAxis(const PxVec3& axis) +{ + PxU32 numAxes = getNumAxes(); + const PxVec3* PX_RESTRICT axes = getAxes(); + const PxVec3* PX_RESTRICT axes_end = axes + numAxes; + while(axes0.9999f) + return false; + axes++; + } + +#ifdef SEP_AXIS_FIXED_MEMORY + if(mNbAxes (y)) +#define ABS_SMALLER_EQUAL(x, y) (PxAbs(x) <= (y)) +//#define AIR(x) ((PxU32&)(x)&SIGN_BITMASK) +//#define ABS_GREATER(x, y) (AIR(x) > IR(y)) +//#define ABS_SMALLER_EQUAL(x, y) (AIR(x) <= IR(y)) + +#if PX_X86 && !PX_OSX + + // Some float optimizations ported over from novodex. + + //returns non zero if the value is negative. + #define PXC_IS_NEGATIVE(x) (((PxU32&)(x)) & 0x80000000) + +#else + + //On most platforms using the integer rep is worse(produces LHSs) since the CPU has more registers. + + //returns non zero if the value is negative. + #define PXC_IS_NEGATIVE(x) ((x) < 0.0f) + +#endif + + +enum +{ + AXIS_A0, AXIS_A1, AXIS_A2, + AXIS_B0, AXIS_B1, AXIS_B2 +}; + +struct VertexInfo +{ + PxVec3 pos; + bool penetrate; + bool area; +}; + + +/*static PxI32 doBoxBoxContactGeneration(PxVec3 ctcPts[MAX_NB_CTCS], PxReal depths[MAX_NB_CTCS], PxVec3* ctcNrm, + const PxVec3& extents0, const PxVec3& extents1, + PxU32& collisionData, + const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance);*/ + +static PxI32 doBoxBoxContactGeneration(ContactBuffer& contactBuffer, + const PxVec3& extents0, const PxVec3& extents1, + PxU32& collisionData, + const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance); + +namespace physx +{ + +namespace Gu +{ +bool contactBoxBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + // Get actual shape data + const PxBoxGeometry& shapeBox0 = shape0.get(); + const PxBoxGeometry& shapeBox1 = shape1.get(); + + PxU32 pd = PxU32(cache.mPairData); + PxI32 Nb = doBoxBoxContactGeneration(contactBuffer, + shapeBox0.halfExtents, shapeBox1.halfExtents, + pd, + Cm::Matrix34(transform0), Cm::Matrix34(transform1), + params.mContactDistance); + + cache.mPairData = Ps::to8(pd); + + if(!Nb) + { + cache.mPairData = 0; // Mark as separated for temporal coherence + return false; // WARNING: the contact stream code below used to output stuff even for 0 contacts (!). Now we just return here. + } + return true; +} +}//Gu +}//physx + +// face => 4 vertices of a face of the cube (i.e. a quad) +static PX_FORCE_INLINE PxReal IsInYZ(const PxReal y, const PxReal z, const VertexInfo** PX_RESTRICT face) +{ + // Warning, indices have been remapped. We're now actually like this: + // + // 3+------+2 + // | | | + // | *--| + // | (y,z)| + // 0+------+1 + PxReal PreviousY = face[3]->pos.y; + PxReal PreviousZ = face[3]->pos.z; + + // Loop through quad vertices + for(PxI32 i=0; i<4; i++) + { + const PxReal CurrentY = face[i]->pos.y; + const PxReal CurrentZ = face[i]->pos.z; + + // |CurrentY - PreviousY y - PreviousY| + // |CurrentZ - PreviousZ z - PreviousZ| + // => similar to backface culling, check each one of the 4 triangles are consistent, in which case + // the point is within the parallelogram. + if((CurrentY - PreviousY)*(z - PreviousZ) - (CurrentZ - PreviousZ)*(y - PreviousY) >= 0.0f) return -1.0f; + + PreviousY = CurrentY; + PreviousZ = CurrentZ; + } + + PxReal x = face[0]->pos.x; + { + const PxReal ay = y - face[0]->pos.y; + const PxReal az = z - face[0]->pos.z; + + PxVec3 b = face[1]->pos - face[0]->pos; // ### could be precomputed ? + x += b.x * (ay*b.y + az*b.z) / b.magnitudeSquared(); // ### could be precomputed ? + + b = face[3]->pos - face[0]->pos; // ### could be precomputed ? + x += b.x * (ay*b.y + az*b.z) / b.magnitudeSquared(); // ### could be precomputed ? + } + + return x; +} + +// Test with respect to the quad defined by (0,-y1,-z1) and (0,y1,z1) +// +------+ y1 y +// | | | +// | * | | +// | | | +// +------+ -y1 *-----z +static PxI32 generateContacts(//PxVec3 ctcPts[], PxReal depths[], + ContactBuffer& contactBuffer, const PxVec3& contactNormal, + PxReal y1, PxReal z1, const PxVec3& box2, + const Cm::Matrix34& transform0, const Cm::Matrix34& transform1, PxReal contactDistance) +{ +// PxI32 NbContacts=0; + contactBuffer.reset(); + y1 += contactDistance; + z1 += contactDistance; + + const Cm::Matrix34 trans1to0 = transform0.getInverseRT() * transform1; + + VertexInfo vtx[8]; // The 8 cube vertices +// PxI32 i; + + // 6+------+7 + // /| /| + // / | / | + // / 4+---/--+5 + // 2+------+3 / y z + // | / | / | / + // |/ |/ |/ + // 0+------+1 *---x + + { + const PxVec3 ex = trans1to0.m.column0 * box2.x; + const PxVec3 ey = trans1to0.m.column1 * box2.y; + const PxVec3 ez = trans1to0.m.column2 * box2.z; + + /* + vtx[0].pos = mat.pos - ex - ey - ez; + vtx[1].pos = mat.pos + ex - ey - ez; + vtx[2].pos = mat.pos - ex + ey - ez; + vtx[3].pos = mat.pos + ex + ey - ez; + vtx[4].pos = mat.pos - ex - ey + ez; + vtx[5].pos = mat.pos + ex - ey + ez; + vtx[6].pos = mat.pos - ex + ey + ez; + vtx[7].pos = mat.pos + ex + ey + ez; + */ + + // 12 vector ops = 12*3 = 36 FPU ops + vtx[0].pos = vtx[2].pos = vtx[4].pos = vtx[6].pos = trans1to0.p - ex; + vtx[1].pos = vtx[3].pos = vtx[5].pos = vtx[7].pos = trans1to0.p + ex; + + PxVec3 e = ey+ez; + vtx[0].pos -= e; + vtx[1].pos -= e; + vtx[6].pos += e; + vtx[7].pos += e; + + e = ey-ez; + vtx[2].pos += e; + vtx[3].pos += e; + vtx[4].pos -= e; + vtx[5].pos -= e; + } + + // Create vertex info for 8 vertices + for(PxU32 i=0; i<8; i++) + { + // Vertex suivant + VertexInfo& p = vtx[i]; + // test the point with respect to the x = 0 plane + // if(p.pos.x < 0) + if(p.pos.x < -contactDistance) //if(PXC_IS_NEGATIVE(p.pos.x)) + { + p.area = false; + p.penetrate = false; + continue; + } + + { + // we penetrated the quad plane + p.penetrate = true; + // test to see if we are in the quad + // PxAbs => thus we test Y with respect to -Y1 and +Y1 (same for Z) + // if(PxAbs(p->pos.y) <= y1 && PxAbs(p->pos.z) <= z1) + if(ABS_SMALLER_EQUAL(p.pos.y, y1) && ABS_SMALLER_EQUAL(p.pos.z, z1)) + { + // the point is inside the quad + p.area=true; + // Since we are testing with respect to x = 0, the penetration is directly the x coordinate. +// depths[NbContacts] = p.pos.x; + + // We take the vertex as the impact point +// ctcPts[NbContacts++] = p.pos; + contactBuffer.contact(p.pos, contactNormal, -p.pos.x); + } + else + { + p.area=false; + } + } + } + + // Teste 12 edges on the quad + static const PxI32 indices[]={ 0,1, 1,3, 3,2, 2,0, 4,5, 5,7, 7,6, 6,4, 0,4, 1,5, 2,6, 3,7, }; + const PxI32* runningLine = indices; + const PxI32* endLine = runningLine+24; + while(runningLine!=endLine) + { + // The two vertices of the current edge + const VertexInfo* p1 = &vtx[*runningLine++]; + const VertexInfo* p2 = &vtx[*runningLine++]; + + // Penetrate|Area|Penetrate|Area => 16 cases + + // We only take the edges that at least penetrated the quad's plane into account. + if(p1->penetrate || p2->penetrate) + // if(p1->penetrate + p2->penetrate) // One branch only + { + // If at least one of the two vertices is not in the quad... + if(!p1->area || !p2->area) + // if(!p1->area + !p2->area) // One branch only + { + // Test y + if(p1->pos.y > p2->pos.y) { const VertexInfo* tmp=p1; p1=p2; p2=tmp; } + // Impact on the +Y1 edge of the quad + if(p1->pos.y < +y1 && p2->pos.y >= +y1) + // => a point under Y1, the other above + { + // Case 1 + PxReal a = (+y1 - p1->pos.y)/(p2->pos.y - p1->pos.y); + PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a; + if(PxAbs(z) <= z1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, y1, z); + contactBuffer.contact(PxVec3(x, y1, z), contactNormal, -x); + } + } + } + // Impact on the edge -Y1 of the quad + if(p1->pos.y < -y1 && p2->pos.y >= -y1) + { + // Case 2 + PxReal a = (-y1 - p1->pos.y)/(p2->pos.y - p1->pos.y); + PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a; + if(PxAbs(z) <= z1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, -y1, z); + contactBuffer.contact(PxVec3(x, -y1, z), contactNormal, -x); + } + } + } + + // Test z + if(p1->pos.z > p2->pos.z) { const VertexInfo* tmp=p1; p1=p2; p2=tmp; } + // Impact on the edge +Z1 of the quad + if(p1->pos.z < +z1 && p2->pos.z >= +z1) + { + // Case 3 + PxReal a = (+z1 - p1->pos.z)/(p2->pos.z - p1->pos.z); + PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a; + if(PxAbs(y) <= y1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, y, z1); + contactBuffer.contact(PxVec3(x, y, z1), contactNormal, -x); + } + } + } + // Impact on the edge -Z1 of the quad + if(p1->pos.z < -z1 && p2->pos.z >= -z1) + { + // Case 4 + PxReal a = (-z1 - p1->pos.z)/(p2->pos.z - p1->pos.z); + PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a; + if(PxAbs(y) <= y1) + { + PxReal x = p1->pos.x + (p2->pos.x - p1->pos.x)*a; + if(x+contactDistance>=0.0f) + { +// depths[NbContacts] = x; +// ctcPts[NbContacts++] = PxVec3(x, y, -z1); + contactBuffer.contact(PxVec3(x, y, -z1), contactNormal, -x); + } + } + } + } + + // The case where one point penetrates the plane, and the other is not in the quad. + if((!p1->penetrate && !p2->area) || (!p2->penetrate && !p1->area)) + { + // Case 5 + PxReal a = (-p1->pos.x)/(p2->pos.x - p1->pos.x); + PxReal y = p1->pos.y + (p2->pos.y - p1->pos.y)*a; + if(PxAbs(y) <= y1) + { + PxReal z = p1->pos.z + (p2->pos.z - p1->pos.z)*a; + if(PxAbs(z) <= z1) + { +// depths[NbContacts] = 0; +// ctcPts[NbContacts++] = PxVec3(0, y, z); + contactBuffer.contact(PxVec3(0, y, z), contactNormal, 0); + } + } + } + + } + } + + { + // 6 quads => 6 faces of the cube + static const PxI32 face[][4]={ {0,1,3,2}, {1,5,7,3}, {5,4,6,7}, {4,0,2,6}, {2,3,7,6}, {0,4,5,1} }; + PxI32 addflg=0; + for(PxU32 i=0; i<6 && addflg!=0x0f; i++) + { + const PxI32* p = face[i]; + const VertexInfo* q[4]; + if((q[0]=&vtx[p[0]])->penetrate && (q[1]=&vtx[p[1]])->penetrate && (q[2]=&vtx[p[2]])->penetrate && (q[3]=&vtx[p[3]])->penetrate) + { + if(!q[0]->area || !q[1]->area || !q[2]->area || !q[3]->area) + { + if(!(addflg&1)) { PxReal x = IsInYZ(-y1, -z1, q); if(x>=0.0f) { addflg|=1; contactBuffer.contact(PxVec3(x, -y1, -z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, -y1, -z1);*/ } } + if(!(addflg&2)) { PxReal x = IsInYZ(+y1, -z1, q); if(x>=0.0f) { addflg|=2; contactBuffer.contact(PxVec3(x, +y1, -z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, +y1, -z1);*/ } } + if(!(addflg&4)) { PxReal x = IsInYZ(-y1, +z1, q); if(x>=0.0f) { addflg|=4; contactBuffer.contact(PxVec3(x, -y1, +z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, -y1, +z1);*/ } } + if(!(addflg&8)) { PxReal x = IsInYZ(+y1, +z1, q); if(x>=0.0f) { addflg|=8; contactBuffer.contact(PxVec3(x, +y1, +z1), contactNormal, -x); /*depths[NbContacts]=x; ctcPts[NbContacts++] = PxVec3(x, +y1, +z1);*/ } } + } + } + } + } + +// for(i=0; i=0.0f && d=0 !! otherwise bug at sep = 0 + } + + collisionData = PxU32(minIndex + 1); // Leave "0" for separation + +#if PX_X86 + const PxU32 sign = PXC_IS_NEGATIVE(d1[minIndex]); +#else + const PxU32 sign = PxU32(PXC_IS_NEGATIVE(d1[minIndex])); +#endif + Cm::Matrix34 trs; + PxVec3 ctcNrm; + + switch(minIndex) + { + default: + return 0; + + case AXIS_A0: +// *ctcNrm = axis00; + if(sign) + { + ctcNrm = axis00; + trs.m = transform0.m; + trs.p = transform0.p - extents0.x*axis00; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis00; + + trs.m.column0 = -axis00; + trs.m.column1 = -axis01; + trs.m.column2 = axis02; + trs.p = transform0.p + extents0.x*axis00; + } +// return generateContacts(ctcPts, depths, extents0.y, extents0.z, extents1, trs, transform1, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents0.y, extents0.z, extents1, trs, transform1, contactDistance); + + case AXIS_A1: +// *ctcNrm = axis01; + trs.m.column2 = axis00; // Factored out + if(sign) + { + ctcNrm = axis01; + trs.m.column0 = axis01; + trs.m.column1 = axis02; + trs.p = transform0.p - extents0.y*axis01; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis01; + + trs.m.column0 = -axis01; + trs.m.column1 = -axis02; + trs.p = transform0.p + extents0.y*axis01; + } +// return generateContacts(ctcPts, depths, extents0.z, extents0.x, extents1, trs, transform1, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents0.z, extents0.x, extents1, trs, transform1, contactDistance); + + case AXIS_A2: +// *ctcNrm = axis02; + trs.m.column2 = axis01; // Factored out + + if(sign) + { + ctcNrm = axis02; + trs.m.column0 = axis02; + trs.m.column1 = axis00; + trs.p = transform0.p - extents0.z*axis02; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis02; + + trs.m.column0 = -axis02; + trs.m.column1 = -axis00; + trs.p = transform0.p + extents0.z*axis02; + } +// return generateContacts(ctcPts, depths, extents0.x, extents0.y, extents1, trs, transform1, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents0.x, extents0.y, extents1, trs, transform1, contactDistance); + + case AXIS_B0: +// *ctcNrm = axis10; + if(sign) + { + ctcNrm = axis10; + trs.m.column0 = -axis10; + trs.m.column1 = -axis11; + trs.m.column2 = axis12; + trs.p = transform1.p + extents1.x*axis10; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis10; + trs.m = transform1.m; + trs.p = transform1.p - extents1.x*axis10; + + } +// return generateContacts(ctcPts, depths, extents1.y, extents1.z, extents0, trs, transform0, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents1.y, extents1.z, extents0, trs, transform0, contactDistance); + + case AXIS_B1: +// *ctcNrm = axis11; + trs.m.column2 = axis10; // Factored out + if(sign) + { + ctcNrm = axis11; + trs.m.column0 = -axis11; + trs.m.column1 = -axis12; + trs.p = transform1.p + extents1.y*axis11; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis11; + + trs.m.column0 = axis11; + trs.m.column1 = axis12; + trs.m.column2 = axis10; + trs.p = transform1.p - extents1.y*axis11; + } +// return generateContacts(ctcPts, depths, extents1.z, extents1.x, extents0, trs, transform0, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents1.z, extents1.x, extents0, trs, transform0, contactDistance); + + case AXIS_B2: +// *ctcNrm = axis12; + trs.m.column2 = axis11; // Factored out + + if(sign) + { + ctcNrm = axis12; + trs.m.column0 = -axis12; + trs.m.column1 = -axis10; + trs.p = transform1.p + extents1.z*axis12; + } + else + { +// *ctcNrm = -*ctcNrm; + ctcNrm = -axis12; + + trs.m.column0 = axis12; + trs.m.column1 = axis10; + trs.p = transform1.p - extents1.z*axis12; + } + +// return generateContacts(ctcPts, depths, extents1.x, extents1.y, extents0, trs, transform0, contactDistance); + return generateContacts(contactBuffer, ctcNrm, extents1.x, extents1.y, extents0, trs, transform0, contactDistance); + } +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp new file mode 100644 index 000000000..c2502147f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp @@ -0,0 +1,457 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactBuffer.h" +#include "GuIntersectionRayBox.h" +#include "GuDistanceSegmentBox.h" +#include "GuInternal.h" +#include "GuContactMethodImpl.h" +#include "PsMathUtils.h" +#include "PsUtilities.h" +#include "GuGeometryUnion.h" +#include "GuBoxConversion.h" + +using namespace physx; +using namespace Gu; + +/*namespace Gu +{ +const PxU8* getBoxEdges(); +}*/ + +///////// + /*#include "CmRenderOutput.h" + #include "PxsContext.h" + static void gVisualizeBox(const Box& box, PxcNpThreadContext& context, PxU32 color=0xffffff) + { + PxMat33 rot(box.base.column0, box.base.column1, box.base.column2); + PxMat44 m(rot, box.origin); + + DebugBox db(box.extent); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m; + out << db; + } + static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) + { + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::LINES << a << b; + }*/ +///////// + + +static const PxReal fatBoxEdgeCoeff = 0.01f; + +static bool intersectEdgeEdgePreca(const PxVec3& p1, const PxVec3& p2, const PxVec3& v1, const PxPlane& plane, PxU32 i, PxU32 j, float coeff, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip) +{ + // if colliding edge (p3,p4) does not cross plane return no collision + // same as if p3 and p4 on same side of plane return 0 + // + // Derivation: + // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // if d3 and d4 have the same sign, they're on the same side of the plane => no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + PxVec3 v2 = p4 - p3; + + temp = plane.n.dot(v2); + if(temp==0.0f) return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i]))*coeff; + if(dist<0.0f) return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<0.0f) return true; // collision found + + return false; // no collision +} + + +static bool GuTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, const Box& box, PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project box + PxReal Min1, Max1; + { + const PxReal BoxCen = box.center.dot(axis); + const PxReal BoxExt = + PxAbs(box.rot.column0.dot(axis)) * box.extents.x + + PxAbs(box.rot.column1.dot(axis)) * box.extents.y + + PxAbs(box.rot.column2.dot(axis)) * box.extents.z; + + Min1 = BoxCen - BoxExt; + Max1 = BoxCen + BoxExt; + } + + // Test projections + if(max0=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + + +static bool GuCapsuleOBBOverlap3(const Segment& segment, PxReal radius, const Box& box, PxReal* t=NULL, PxVec3* pp=NULL) +{ + PxVec3 Sep(PxReal(0)); + PxReal PenDepth = PX_MAX_REAL; + + // Test normals + for(PxU32 i=0;i<3;i++) + { + PxReal d; + if(!GuTestAxis(box.rot[i], segment, radius, box, d)) + return false; + + if(d(); + const PxBoxGeometry& shapeBox = shape1.get(); + + // PT: TODO: move computations to local space + + // Capsule data + Segment worldSegment; + getCapsuleSegment(transform0, shapeCapsule, worldSegment); + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + // Box data + Box worldBox; + buildFrom(worldBox, transform1.p, shapeBox.halfExtents, transform1.q); + + // Collision detection + PxReal t; + PxVec3 onBox; + const PxReal squareDist = distanceSegmentBoxSquared(worldSegment.p0, worldSegment.p1, worldBox.center, worldBox.extents, worldBox.rot, &t, &onBox); + + if(squareDist >= inflatedRadius*inflatedRadius) + return false; + + PX_ASSERT(contactBuffer.count==0); + + if(squareDist != 0.0f) + { + // PT: the capsule segment doesn't intersect the box => distance-based version + const PxVec3 onSegment = worldSegment.getPointAt(t); + onBox = worldBox.center + worldBox.rot.transform(onBox); + + PxVec3 normal = onSegment - onBox; + PxReal normalLen = normal.magnitude(); + + if(normalLen > 0.0f) + { + normal *= 1.0f/normalLen; + + // PT: generate VF contacts for segment's vertices vs box + GuGenerateVFContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, normal, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + GuGenerateEEContacts2(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, normal, params.mContactDistance); + + // PT: run VF case for box-vertex-vs-capsule only if we don't have any contact yet + if(!contactBuffer.count) + contactBuffer.contact(onBox, normal, sqrtf(squareDist) - shapeCapsule.radius); + } + else + { + // On linux we encountered the following: + // For a case where a segment endpoint lies on the surface of a box, the squared distance between segment and box was tiny but still larger than 0. + // However, the computation of the normal length was exactly 0. In that case we should have switched to the penetration based version so we do it now + // instead. + goto PenetrationBasedCode; + } + } + else + { + PenetrationBasedCode: + + // PT: the capsule segment intersects the box => penetration-based version + + // PT: compute penetration vector (MTD) + PxVec3 sepAxis; + PxReal depth; + if(!GuCapsuleOBBOverlap3(worldSegment, shapeCapsule.radius, worldBox, &depth, &sepAxis)) return false; + + // PT: generate VF contacts for segment's vertices vs box + GuGenerateVFContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, sepAxis, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + GuGenerateEEContacts(contactBuffer, worldSegment, shapeCapsule.radius, worldBox, sepAxis); + + if(!contactBuffer.count) + { + contactBuffer.contact(worldSegment.computeCenter(), sepAxis, -(shapeCapsule.radius + depth)); + return true; + } + } + return true; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp new file mode 100644 index 000000000..13ce6e029 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp @@ -0,0 +1,155 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactBuffer.h" +#include "GuDistanceSegmentSegment.h" +#include "GuContactMethodImpl.h" +#include "GuInternal.h" +#include "GuGeometryUnion.h" + +using namespace physx; + +namespace physx +{ +namespace Gu +{ +bool contactCapsuleCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom0 = shape0.get(); + const PxCapsuleGeometry& capsuleGeom1 = shape1.get(); + + // PT: get capsules in local space + PxVec3 dir[2]; + Segment segment[2]; + { + const PxVec3 capsuleLocalSegment0 = getCapsuleHalfHeightVector(transform0, capsuleGeom0); + const PxVec3 capsuleLocalSegment1 = getCapsuleHalfHeightVector(transform1, capsuleGeom1); + + const PxVec3 delta = transform1.p - transform0.p; + segment[0].p0 = capsuleLocalSegment0; + segment[0].p1 = -capsuleLocalSegment0; + dir[0] = -capsuleLocalSegment0*2.0f; + segment[1].p0 = capsuleLocalSegment1 + delta; + segment[1].p1 = -capsuleLocalSegment1 + delta; + dir[1] = -capsuleLocalSegment1*2.0f; + } + + // PT: compute distance between capsules' segments + PxReal s,t; + const PxReal squareDist = distanceSegmentSegmentSquared(segment[0], segment[1], &s, &t); + const PxReal radiusSum = capsuleGeom0.radius + capsuleGeom1.radius; + const PxReal inflatedSum = radiusSum + params.mContactDistance; + const PxReal inflatedSumSquared = inflatedSum*inflatedSum; + + if(squareDist >= inflatedSumSquared) + return false; + + // PT: TODO: optimize this away + PxReal segLen[2]; + segLen[0] = dir[0].magnitude(); + segLen[1] = dir[1].magnitude(); + + if (segLen[0]) dir[0] *= 1.0f / segLen[0]; + if (segLen[1]) dir[1] *= 1.0f / segLen[1]; + + if (PxAbs(dir[0].dot(dir[1])) > 0.9998f) //almost parallel, ca. 1 degree difference --> generate two contact points at ends + { + PxU32 numCons = 0; + + PxReal segLenEps[2]; + segLenEps[0] = segLen[0] * 0.001f;//0.1% error is ok. + segLenEps[1] = segLen[1] * 0.001f; + + //project the two end points of each onto the axis of the other and take those 4 points. + //we could also generate a single normal at the single closest point, but this would be 'unstable'. + + for (PxU32 destShapeIndex = 0; destShapeIndex < 2; destShapeIndex ++) + { + for (PxU32 startEnd = 0; startEnd < 2; startEnd ++) + { + const PxU32 srcShapeIndex = 1-destShapeIndex; + //project start/end of srcShapeIndex onto destShapeIndex. + PxVec3 pos[2]; + pos[destShapeIndex] = startEnd ? segment[srcShapeIndex].p1 : segment[srcShapeIndex].p0; + const PxReal p = dir[destShapeIndex].dot(pos[destShapeIndex] - segment[destShapeIndex].p0); + if (p >= -segLenEps[destShapeIndex] && p <= (segLen[destShapeIndex] + segLenEps[destShapeIndex])) + { + pos[srcShapeIndex] = p * dir[destShapeIndex] + segment[destShapeIndex].p0; + + PxVec3 normal = pos[1] - pos[0]; + + const PxReal normalLenSq = normal.magnitudeSquared(); + if (normalLenSq > 1e-6f && normalLenSq < inflatedSumSquared) + { + const PxReal distance = PxSqrt(normalLenSq); + normal *= 1.0f/distance; + PxVec3 point = pos[1] - normal * (srcShapeIndex ? capsuleGeom1 : capsuleGeom0).radius; + point += transform0.p; + contactBuffer.contact(point, normal, distance - radiusSum); + numCons++; + } + } + } + } + + if (numCons) //if we did not have contacts, then we may have the case where they are parallel, but are stacked end to end, in which case the old code will generate good contacts. + return true; + } + + // Collision response + PxVec3 pos1 = segment[0].getPointAt(s); + PxVec3 pos2 = segment[1].getPointAt(t); + + PxVec3 normal = pos1 - pos2; + + const PxReal normalLenSq = normal.magnitudeSquared(); + if (normalLenSq < 1e-6f) + { + // PT: TODO: revisit this. "FW" sounds old. + // Zero normal -> pick the direction of segment 0. + // Not always accurate but consistent with FW. + if (segLen[0] > 1e-6f) + normal = dir[0]; + else + normal = PxVec3(1.0f, 0.0f, 0.0f); + } + else + { + normal *= PxRecipSqrt(normalLenSq); + } + + pos1 += transform0.p; + contactBuffer.contact(pos1 - normal * capsuleGeom0.radius, normal, PxSqrt(squareDist) - radiusSum); + return true; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp new file mode 100644 index 000000000..772f04f7b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp @@ -0,0 +1,587 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexMesh.h" +#include "GuConvexHelper.h" +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuVecConvexHull.h" +#include "GuVecCapsule.h" +#include "GuInternal.h" +#include "GuGJK.h" +#include "GuGeometryUnion.h" + +using namespace physx; +using namespace Gu; + +/////////// +// #include "CmRenderOutput.h" +// #include "PxsContext.h" +// static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) +// { +// PxMat44 m = PxMat44::identity(); +// +// Cm::RenderOutput& out = context.mRenderOutput; +// out << color << m << Cm::RenderOutput::LINES << a << b; +// } +/////////// + +static const PxReal fatConvexEdgeCoeff = 0.01f; + +static bool intersectEdgeEdgePreca(const PxVec3& p1, const PxVec3& p2, const PxVec3& v1, const PxPlane& plane, PxU32 i, PxU32 j, float coeff, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip, float limit) +{ + // if colliding edge (p3,p4) does not cross plane return no collision + // same as if p3 and p4 on same side of plane return 0 + // + // Derivation: + // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // if d3 and d4 have the same sign, they're on the same side of the plane => no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) + return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + PxVec3 v2 = p4 - p3; + + temp = plane.n.dot(v2); + if(temp==0.0f) + return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i]))*coeff; + if(distmax0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project convex + PxReal Min1, Max1; + (polyData.mProjectHull)(polyData, axis, worldTM, scaling, Min1, Max1); + + // Test projections + if(max0=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +static bool GuCapsuleConvexOverlap(const Gu::Segment& segment, PxReal radius, + const PolygonalData& polyData, + const Cm::FastVertex2ShapeScaling& scaling, + const PxTransform& transform, + PxReal* t, PxVec3* pp, bool isSphere) +{ + // TODO: + // - test normal & edge in same loop + // - local space + // - use precomputed face value + // - optimize projection + + PxVec3 Sep(0,0,0); + PxReal PenDepth = PX_MAX_REAL; + + PxU32 nbPolys = polyData.mNbPolygons; + const Gu::HullPolygonData* polys = polyData.mPolygons; + + const Cm::Matrix34 worldTM(transform); + + // Test normals + for(PxU32 i=0;i 1E-7f) //the ray direction "exits" from the back side + { + earliestExit = physx::intrinsics::selectMin(earliestExit, distAlongRay); + } + else if (dn < -1E-7f) //the ray direction "enters" from the front side + { +/* if (distAlongRay > latestEntry) + { + latestEntry = distAlongRay; + }*/ + latestEntry = physx::intrinsics::selectMax(latestEntry, distAlongRay); + } + else + { + //plane normal and ray dir are orthogonal + if(distToPlane > 0.0f) + return false; //a plane is parallel with ray -- and we're outside the ray -- we definitely miss the entire convex! + } + } + + if(latestEntry < earliestExit && latestEntry != -FLT_MAX && latestEntry < maxDist-1e-5f) + { + t = latestEntry; + return true; + } + return false; +} + +// PT: version based on Gu::raycast_convexMesh to handle scaling, but modified to make sure it works when ray starts inside the convex +static void GuGenerateVFContacts2(ContactBuffer& contactBuffer, + // + const PxTransform& convexPose, + const PolygonalData& polyData, // Convex data + const PxMeshScale& scale, + // + PxU32 nbPts, + const PxVec3* PX_RESTRICT points, + const PxReal radius, // Capsule's radius + // + const PxVec3& normal, + const PxReal contactDistance) +{ + PX_ASSERT(PxAbs(normal.magnitudeSquared()-1)<1e-4f); + + //scaling: transform the ray to vertex space + const Cm::Matrix34 world2vertexSkew = scale.getInverse() * convexPose.getInverse(); + + const PxVec3 vrayDir = world2vertexSkew.rotate( -normal ); + + const PxReal maxDist = contactDistance + radius; + + for(PxU32 i=0;i(); + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + + PxVec3 onSegment, onConvex; + PxReal distance; + PxVec3 normal_; + { + Gu::ConvexMesh* cm = static_cast(shapeConvex.convexMesh); + + using namespace Ps::aos; + Vec3V closA, closB, normalV; + GjkStatus status; + FloatV dist; + { + + const Vec3V zeroV = V3Zero(); + const Gu::ConvexHullData* hullData = &cm->getHull(); + + const FloatV capsuleHalfHeight = FLoad(shapeCapsule.halfHeight); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const PsMatTransformV aToB(transform1.transformInv(transform0)); + + Gu::ConvexHullV convexHull(hullData, zeroV, vScale, vQuat, shapeConvex.scale.isIdentity()); + + //transform capsule(a) into the local space of convexHull(b), treat capsule as segment + Gu::CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), FZero()); + + + LocalConvex convexA(capsule); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(convexA.getCenter(), convexB.getCenter()); + + status = gjk, LocalConvex >(convexA, convexB, initialSearchDir, FMax(),closA, closB, normalV, dist); + } + + + if(status == GJK_CONTACT) + distance = 0.f; + else + { + //const FloatV sqDist = FMul(dist, dist); + V3StoreU(closB, onConvex); + FStore(dist, &distance); + V3StoreU(normalV, normal_); + onConvex = transform1.transform(onConvex); + normal_ = transform1.rotate(normal_); + } + } + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + if(distance >= inflatedRadius) + return false; + + Gu::Segment worldSegment; + getCapsuleSegment(transform0, shapeCapsule, worldSegment); + + const bool isSphere = worldSegment.p0 == worldSegment.p1; + const PxU32 nbPts = PxU32(isSphere ? 1 : 2); + + PX_ASSERT(contactBuffer.count==0); + + Cm::FastVertex2ShapeScaling convexScaling; + const bool idtConvexScale = shapeConvex.scale.isIdentity(); + if(!idtConvexScale) + convexScaling.init(shapeConvex.scale); + + PolygonalData polyData; + getPolygonalData_Convex(&polyData, shapeConvex.hullData, convexScaling); + +// if(0) + if(distance > 0.f) + { + // PT: the capsule segment doesn't intersect the convex => distance-based version + PxVec3 normal = -normal_; + + // PT: generate VF contacts for segment's vertices vs convex + GuGenerateVFContacts2( contactBuffer, + transform1, polyData, shapeConvex.scale, + nbPts, &worldSegment.p0, shapeCapsule.radius, + normal, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + if(!isSphere) + { + const Cm::Matrix34 worldTM(transform1); + GuGenerateEEContacts2b(contactBuffer, worldSegment, shapeCapsule.radius, + worldTM, polyData, convexScaling, + normal, params.mContactDistance); + } + + // PT: run VF case for convex-vertex-vs-capsule only if we don't have any contact yet + if(!contactBuffer.count) + { +// gVisualizeLine(onConvex, onConvex + normal, context, PxDebugColor::eARGB_RED); + //PxReal distance = PxSqrt(sqDistance); + contactBuffer.contact(onConvex, normal, distance - shapeCapsule.radius); + } + } + else + { + // PT: the capsule segment intersects the convex => penetration-based version +//printf("Penetration-based:\n"); + + // PT: compute penetration vector (MTD) + PxVec3 SepAxis; + if(!GuCapsuleConvexOverlap(worldSegment, shapeCapsule.radius, polyData, convexScaling, transform1, NULL, &SepAxis, isSphere)) + { +//printf("- no overlap\n"); + return false; + } + + // PT: generate VF contacts for segment's vertices vs convex + GuGenerateVFContacts2( contactBuffer, + transform1, polyData, shapeConvex.scale, + nbPts, &worldSegment.p0, shapeCapsule.radius, + SepAxis, params.mContactDistance); + + // PT: early exit if we already have 2 stable contacts +//printf("- %d VF contacts\n", contactBuffer.count); + if(contactBuffer.count==2) + return true; + + // PT: else generate slower EE contacts + if(!isSphere) + { + GuGenerateEEContacts(contactBuffer, worldSegment, shapeCapsule.radius, params.mContactDistance, polyData, transform1, convexScaling, SepAxis); +//printf("- %d total contacts\n", contactBuffer.count); + } + } + return true; +} + +} +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp new file mode 100644 index 000000000..cc56351c1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp @@ -0,0 +1,635 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionEdgeEdge.h" +#include "GuDistanceSegmentTriangle.h" +#include "GuIntersectionRayTriangle.h" +#include "GuIntersectionTriangleBox.h" +#include "GuInternal.h" +#include "GuContactMethodImpl.h" +#include "GuFeatureCode.h" +#include "GuContactBuffer.h" +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuConvexEdgeFlags.h" +#include "GuGeometryUnion.h" +#include "GuSIMDHelpers.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; + +#define DEBUG_RENDER_MESHCONTACTS 0 + +#if DEBUG_RENDER_MESHCONTACTS +#include "PxPhysics.h" +#include "PxScene.h" +#endif + +#define USE_AABB_TRI_CULLING + +//#define USE_CAPSULE_TRI_PROJ_CULLING +//#define USE_CAPSULE_TRI_SAT_CULLING +#define VISUALIZE_TOUCHED_TRIS 0 +#define VISUALIZE_CULLING_BOX 0 + +#if VISUALIZE_TOUCHED_TRIS +#include "CmRenderOutput.h" +#include "PxsContactManager.h" +#include "PxsContext.h" +static void gVisualizeLine(const PxVec3& a, const PxVec3& b, PxcNpThreadContext& context, PxU32 color=0xffffff) +{ + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::LINES << a << b; +} +static void gVisualizeTri(const PxVec3& a, const PxVec3& b, const PxVec3& c, PxcNpThreadContext& context, PxU32 color=0xffffff) +{ + PxMat44 m = PxMat44::identity(); + + Cm::RenderOutput& out = context.mRenderOutput; + out << color << m << Cm::RenderOutput::TRIANGLES << a << b << c; +} + +static PxU32 gColors[8] = { 0xff0000ff, 0xff00ff00, 0xffff0000, + 0xff00ffff, 0xffff00ff, 0xffffff00, + 0xff000080, 0xff008000}; +#endif + +static const float fatBoxEdgeCoeff = 0.01f; + +static bool PxcTestAxis(const PxVec3& axis, const Segment& segment, PxReal radius, + const PxVec3* PX_RESTRICT triVerts, PxReal& depth) +{ + // Project capsule + PxReal min0 = segment.p0.dot(axis); + PxReal max0 = segment.p1.dot(axis); + if(min0>max0) Ps::swap(min0, max0); + min0 -= radius; + max0 += radius; + + // Project triangle + float Min1, Max1; + { + Min1 = Max1 = triVerts[0].dot(axis); + const PxReal dp1 = triVerts[1].dot(axis); + Min1 = physx::intrinsics::selectMin(Min1, dp1); + Max1 = physx::intrinsics::selectMax(Max1, dp1); + const PxReal dp2 = triVerts[2].dot(axis); + Min1 = physx::intrinsics::selectMin(Min1, dp2); + Max1 = physx::intrinsics::selectMax(Max1, dp2); + } + + // Test projections + if(max0=0.0f); + const PxReal d1 = Max1 - min0; + PX_ASSERT(d1>=0.0f); + depth = physx::intrinsics::selectMin(d0, d1); + return true; +} + +PX_FORCE_INLINE static PxVec3 PxcComputeTriangleNormal(const PxVec3* PX_RESTRICT triVerts) +{ + return ((triVerts[0]-triVerts[1]).cross(triVerts[0]-triVerts[2])).getNormalized(); +} + +PX_FORCE_INLINE static PxVec3 PxcComputeTriangleCenter(const PxVec3* PX_RESTRICT triVerts) +{ + static const PxReal inv3 = 1.0f / 3.0f; + return (triVerts[0] + triVerts[1] + triVerts[2]) * inv3; +} + +static bool PxcCapsuleTriOverlap3(PxU8 edgeFlags, const Segment& segment, PxReal radius, const PxVec3* PX_RESTRICT triVerts, + PxReal* PX_RESTRICT t=NULL, PxVec3* PX_RESTRICT pp=NULL) +{ + PxReal penDepth = PX_MAX_REAL; + + // Test normal + PxVec3 sep = PxcComputeTriangleNormal(triVerts); + if(!PxcTestAxis(sep, segment, radius, triVerts, penDepth)) + return false; + + // Test edges + // ML:: use the active edge flag instead of the concave flag + const PxU32 activeEdgeFlag[] = {ETD_CONVEX_EDGE_01, ETD_CONVEX_EDGE_12, ETD_CONVEX_EDGE_20}; + const PxVec3 capsuleAxis = (segment.p1 - segment.p0).getNormalized(); + for(PxU32 i=0;i<3;i++) + { + //bool active =((edgeFlags & ignoreEdgeFlag[i]) == 0); + + if(edgeFlags & activeEdgeFlag[i]) + { + + const PxVec3 e0 = triVerts[i]; +// const PxVec3 e1 = triVerts[(i+1)%3]; + const PxVec3 e1 = triVerts[Ps::getNextIndex3(i)]; + const PxVec3 edge = e0 - e1; + + PxVec3 cross = capsuleAxis.cross(edge); + if(!Ps::isAlmostZero(cross)) + { + cross = cross.getNormalized(); + PxReal d; + if(!PxcTestAxis(cross, segment, radius, triVerts, d)) + return false; + if(dgetRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << Hit << (Hit + wn * 10.0f); + #endif + } + } +} + +// PT: PxcGenerateEEContacts2 uses a segment-triangle distance function, which breaks when the segment +// intersects the triangle, in which case you need to switch to a penetration-depth computation. +// If you don't do this thin capsules don't work. +static void PxcGenerateEEContacts( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, const PxReal radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, PxU32 triangleIndex) +{ + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff); + + for(PxU32 i=0;i<3;i++) + { + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge(triVerts[i], triVerts[Ps::getNextIndex3(i)], -normal, s0, s1, dist, ip)) + { + ip = meshAbsPose.transform(ip); + const PxVec3 wn = meshAbsPose.rotate(normal); + + contactBuffer.contact(ip, wn, - (radius + dist), triangleIndex); + #if DEBUG_RENDER_MESHCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << ip << (ip + wn * 10.0f); + #endif + } + } +} + +static void PxcGenerateEEContacts2( const Cm::Matrix34& meshAbsPose, ContactBuffer& contactBuffer, const Segment& segment, const PxReal radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& normal, PxU32 triangleIndex, PxReal contactDistance) +{ + PxVec3 s0 = segment.p0; + PxVec3 s1 = segment.p1; + Ps::makeFatEdge(s0, s1, fatBoxEdgeCoeff); + + for(PxU32 i=0;i<3;i++) + { + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge(triVerts[i], triVerts[Ps::getNextIndex3(i)], normal, s0, s1, dist, ip) && dist < radius+contactDistance) + { + ip = meshAbsPose.transform(ip); + const PxVec3 wn = meshAbsPose.rotate(normal); + + contactBuffer.contact(ip, wn, dist - radius, triangleIndex); + #if DEBUG_RENDER_MESHCONTACTS + PxScene *s; PxGetPhysics().getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) << Cm::RenderOutput::LINES << PxDebugColor::eARGB_BLUE // red + << ip << (ip + wn * 10.0f); + #endif + } + } +} + +namespace +{ +struct CapsuleMeshContactGeneration +{ + ContactBuffer& mContactBuffer; + const Cm::Matrix34 mMeshAbsPose; + const Segment& mMeshCapsule; +#ifdef USE_AABB_TRI_CULLING + Vec3p mBC; + Vec3p mBE; +#endif + PxReal mInflatedRadius; + PxReal mContactDistance; + PxReal mShapeCapsuleRadius; + + CapsuleMeshContactGeneration(ContactBuffer& contactBuffer, const PxTransform& transform1, const Segment& meshCapsule, PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius) : + mContactBuffer (contactBuffer), + mMeshAbsPose (Cm::Matrix34(transform1)), + mMeshCapsule (meshCapsule), + mInflatedRadius (inflatedRadius), + mContactDistance (contactDistance), + mShapeCapsuleRadius (shapeCapsuleRadius) + { + PX_ASSERT(contactBuffer.count==0); +#ifdef USE_AABB_TRI_CULLING + mBC = (meshCapsule.p0 + meshCapsule.p1)*0.5f; + const Vec3p be = (meshCapsule.p0 - meshCapsule.p1)*0.5f; + mBE.x = fabsf(be.x) + inflatedRadius; + mBE.y = fabsf(be.y) + inflatedRadius; + mBE.z = fabsf(be.z) + inflatedRadius; +#endif + } + + void processTriangle(PxU32 triangleIndex, const TrianglePadded& tri, PxU8 extraData/*, const PxU32* vertInds*/) + { +#ifdef USE_AABB_TRI_CULLING + #if VISUALIZE_CULLING_BOX + { + Cm::RenderOutput& out = context.mRenderOutput; + PxTransform idt = PxTransform(PxIdentity); + out << idt; + out << 0xffffffff; + out << Cm::DebugBox(mBC, mBE, true); + } + #endif +#endif + const PxVec3& p0 = tri.verts[0]; + const PxVec3& p1 = tri.verts[1]; + const PxVec3& p2 = tri.verts[2]; + +#ifdef USE_AABB_TRI_CULLING + // PT: this one is safe because triangle class is padded + // PT: TODO: is this test really needed? Not done in midphase already? + if(!intersectTriangleBox_Unsafe(mBC, mBE, p0, p1, p2)) + return; +#endif + +#ifdef USE_CAPSULE_TRI_PROJ_CULLING + PxVec3 triCenter = (p0 + p1 + p2)*0.33333333f; + PxVec3 delta = mBC - triCenter; + + PxReal depth; + if(!PxcTestAxis(delta, mMeshCapsule, mInflatedRadius, tri.verts, depth)) + return; +#endif + +#if VISUALIZE_TOUCHED_TRIS + gVisualizeTri(p0, p1, p2, context, PxDebugColor::eARGB_RED); +#endif + +#ifdef USE_CAPSULE_TRI_SAT_CULLING + PxVec3 SepAxis; + if(!PxcCapsuleTriOverlap3(extraData, mMeshCapsule, mInflatedRadius, tri.verts, NULL, &SepAxis)) + return; +#endif + + PxReal t,u,v; + const PxVec3 p1_p0 = p1 - p0; + const PxVec3 p2_p0 = p2 - p0; + const PxReal squareDist = distanceSegmentTriangleSquared(mMeshCapsule, p0, p1_p0, p2_p0, &t, &u, &v); + + // PT: do cheaper test first! + if(squareDist >= mInflatedRadius*mInflatedRadius) + return; + + // PT: backface culling without the normalize + // PT: TODO: consider doing before the segment-triangle distance test if it's cheaper + const PxVec3 planeNormal = p1_p0.cross(p2_p0); + const PxF32 planeD = planeNormal.dot(p0); // PT: actually -d compared to PxcPlane + if(planeNormal.dot(mBC) < planeD) + return; + + if(squareDist > 0.001f*0.001f) + { + // Contact information + PxVec3 normal; + if(selectNormal(extraData, u, v)) + { + normal = planeNormal.getNormalized(); + } + else + { + const PxVec3 pointOnTriangle = Ps::computeBarycentricPoint(p0, p1, p2, u, v); + + const PxVec3 pointOnSegment = mMeshCapsule.getPointAt(t); + normal = pointOnSegment - pointOnTriangle; + const PxReal l = normal.magnitude(); + if(l == 0.0f) + return; + normal = normal / l; + } + + PxcGenerateEEContacts2(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, normal, triangleIndex, mContactDistance); + PxcGenerateVFContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, normal, triangleIndex, mContactDistance); + } + else + { + PxVec3 SepAxis; + if(!PxcCapsuleTriOverlap3(extraData, mMeshCapsule, mInflatedRadius, tri.verts, NULL, &SepAxis)) + return; + + PxcGenerateEEContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, SepAxis, triangleIndex); + PxcGenerateVFContacts(mMeshAbsPose, mContactBuffer, mMeshCapsule, mShapeCapsuleRadius, tri.verts, SepAxis, triangleIndex, mContactDistance); + } + } + +private: + CapsuleMeshContactGeneration& operator=(const CapsuleMeshContactGeneration&); +}; + +struct CapsuleMeshContactGenerationCallback_NoScale : MeshHitCallback +{ + CapsuleMeshContactGeneration mGeneration; + const TriangleMesh* mMeshData; + + CapsuleMeshContactGenerationCallback_NoScale( + ContactBuffer& contactBuffer, + const PxTransform& transform1, const Segment& meshCapsule, + PxReal inflatedRadius, PxReal contactDistance, + PxReal shapeCapsuleRadius, const TriangleMesh* meshData + ) : + MeshHitCallback (CallbackMode::eMULTIPLE), + mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius), + mMeshData (meshData) + { + PX_ASSERT(contactBuffer.count==0); + } + + PX_FORCE_INLINE PxAgain processTriangle(const PxRaycastHit& hit, const TrianglePadded& tri) + { + const PxU32 triangleIndex = hit.faceIndex; + + //ML::set all the edges to be active, if the mExtraTrigData exist, we overwrite this flag + const PxU8 extraData = getConvexEdgeFlags(mMeshData->getExtraTrigData(), triangleIndex); + mGeneration.processTriangle(triangleIndex, tri, extraData); + return true; + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/) + { + TrianglePadded tri; + // PT: TODO: revisit this, avoid the copy + tri.verts[0] = v0; + tri.verts[1] = v1; + tri.verts[2] = v2; + return processTriangle(hit, tri); + } + +private: + CapsuleMeshContactGenerationCallback_NoScale& operator=(const CapsuleMeshContactGenerationCallback_NoScale&); +}; + +struct CapsuleMeshContactGenerationCallback_Scale : CapsuleMeshContactGenerationCallback_NoScale +{ + const Cm::FastVertex2ShapeScaling& mScaling; + + CapsuleMeshContactGenerationCallback_Scale( + ContactBuffer& contactBuffer, + const PxTransform& transform1, const Segment& meshCapsule, + PxReal inflatedRadius, const Cm::FastVertex2ShapeScaling& scaling, PxReal contactDistance, + PxReal shapeCapsuleRadius, const TriangleMesh* meshData + ) : + CapsuleMeshContactGenerationCallback_NoScale(contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius, meshData), + mScaling (scaling) + { + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* /*vInds*/) + { + TrianglePadded tri; + getScaledVertices(tri.verts, v0, v1, v2, false, mScaling); + return processTriangle(hit, tri); + } + +private: + CapsuleMeshContactGenerationCallback_Scale& operator=(const CapsuleMeshContactGenerationCallback_Scale&); +}; + +} + +// PT: computes local capsule without going to world-space +static PX_FORCE_INLINE Segment computeLocalCapsule(const PxTransform& transform0, const PxTransform& transform1, const PxCapsuleGeometry& shapeCapsule) +{ + const PxVec3 halfHeight = getCapsuleHalfHeightVector(transform0, shapeCapsule); + const PxVec3 delta = transform1.p - transform0.p; + return Segment( + transform1.rotateInv(halfHeight - delta), + transform1.rotateInv(-halfHeight - delta)); +} + +bool Gu::contactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate! + const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule); + + const TriangleMesh* meshData = shapeMesh.meshData; + + //bound the capsule in shape space by an OBB: + Box queryBox; + { + const Capsule queryCapsule(meshCapsule, inflatedRadius); + queryBox.create(queryCapsule); + } + + if(shapeMesh.scale.isIdentity()) + { + CapsuleMeshContactGenerationCallback_NoScale callback(contactBuffer, transform1, meshCapsule, + inflatedRadius, params.mContactDistance, shapeCapsule.radius, meshData); + + // PT: TODO: switch to capsule query here + Midphase::intersectOBB(meshData, queryBox, callback, true); + } + else + { + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + CapsuleMeshContactGenerationCallback_Scale callback(contactBuffer, transform1, meshCapsule, + inflatedRadius, meshScaling, params.mContactDistance, shapeCapsule.radius, meshData); + + //switched from capsuleCollider to boxCollider so we can support nonuniformly scaled meshes by scaling the query region: + + //apply the skew transform to the box: + meshScaling.transformQueryBounds(queryBox.center, queryBox.extents, queryBox.rot); + + Midphase::intersectOBB(meshData, queryBox, callback, true); + } + return contactBuffer.count > 0; +} + +namespace +{ +struct CapsuleHeightfieldContactGenerationCallback : EntityReport +{ + CapsuleMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + const PxTransform& mTransform1; + + CapsuleHeightfieldContactGenerationCallback( + ContactBuffer& contactBuffer, + const PxTransform& transform1, HeightFieldUtil& hfUtil, const Segment& meshCapsule, + PxReal inflatedRadius, PxReal contactDistance, PxReal shapeCapsuleRadius + ) : + mGeneration (contactBuffer, transform1, meshCapsule, inflatedRadius, contactDistance, shapeCapsuleRadius), + mHfUtil (hfUtil), + mTransform1 (transform1) + { + PX_ASSERT(contactBuffer.count==0); + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + const PxU8 nextInd[] = {2,0,1}; + + while(nb--) + { + const PxU32 triangleIndex = *indices++; + + PxU32 vertIndices[3]; + TrianglePadded currentTriangle; // in world space + PxU32 adjInds[3]; + mHfUtil.getTriangle(mTransform1, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); + + PxVec3 normal; + currentTriangle.normal(normal); + + PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF + + for(PxU32 a = 0; a < 3; ++a) + { + if(adjInds[a] != 0xFFFFFFFF) + { + PxTriangle adjTri; + mHfUtil.getTriangle(mTransform1, adjTri, NULL, NULL, adjInds[a], false, false); + //We now compare the triangles to see if this edge is active + + PxVec3 adjNormal; + adjTri.denormalizedNormal(adjNormal); + PxU32 otherIndex = nextInd[a]; + PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); + if(projD < 0.f) + { + adjNormal.normalize(); + + PxF32 proj = adjNormal.dot(normal); + + if(proj < 0.999f) + { + triFlags |= 1 << (a+3); + } + } + } + else + { + triFlags |= 1 << (a+3); + } + } + + mGeneration.processTriangle(triangleIndex, currentTriangle, triFlags); + } + return true; + } + +private: + CapsuleHeightfieldContactGenerationCallback& operator=(const CapsuleHeightfieldContactGenerationCallback&); +}; +} + +bool Gu::contactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get(); + const PxHeightFieldGeometryLL& shapeMesh = shape1.get(); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; //AM: inflate! + const Segment meshCapsule = computeLocalCapsule(transform0, transform1, shapeCapsule); + + // We must be in local space to use the cache + + HeightFieldUtil hfUtil(shapeMesh); + + CapsuleHeightfieldContactGenerationCallback callback( + contactBuffer, transform1, hfUtil, meshCapsule, inflatedRadius, params.mContactDistance, shapeCapsule.radius); + + //switched from capsuleCollider to boxCollider so we can support nonuniformly scaled meshes by scaling the query region: + + //bound the capsule in shape space by an OBB: + + PxBounds3 bounds; + bounds.maximum = PxVec3(shapeCapsule.halfHeight + inflatedRadius, inflatedRadius, inflatedRadius); + bounds.minimum = -bounds.maximum; + + bounds = PxBounds3::transformFast(transform1.transformInv(transform0), bounds); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &callback); + + return contactBuffer.count > 0; +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp new file mode 100644 index 000000000..e5ed03c3e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp @@ -0,0 +1,1033 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsMathUtils.h" +#include "GuContactPolygonPolygon.h" +#include "GuGeometryUnion.h" +#include "GuConvexHelper.h" +#include "GuInternal.h" +#include "GuSeparatingAxes.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "PsFPU.h" + +// csigg: the single reference of gEnableOptims (below) has been +// replaced with the actual value to prevent ios64 compiler crash. +// static const int gEnableOptims = 1; +#define CONVEX_CONVEX_ROUGH_FIRST_PASS +#define TEST_INTERNAL_OBJECTS +#ifdef TEST_INTERNAL_OBJECTS + #define USE_BOX_DATA +#endif + +using namespace physx; +using namespace Gu; +using namespace shdfnd::aos; +using namespace intrinsics; + +#ifdef TEST_INTERNAL_OBJECTS +#ifdef USE_BOX_DATA +PX_FORCE_INLINE void BoxSupport(const float extents[3], const PxVec3& sv, float p[3]) +{ + const PxU32* iextents = reinterpret_cast(extents); + const PxU32* isv = reinterpret_cast(&sv); + PxU32* ip = reinterpret_cast(p); + + ip[0] = iextents[0]|(isv[0]&PX_SIGN_BITMASK); + ip[1] = iextents[1]|(isv[1]&PX_SIGN_BITMASK); + ip[2] = iextents[2]|(isv[2]&PX_SIGN_BITMASK); +} +#endif + +#if PX_DEBUG +static const PxReal testInternalObjectsEpsilon = 1.0e-3f; +#endif + +#ifdef DO_NOT_REMOVE + PX_FORCE_INLINE void testInternalObjects_( const PxVec3& delta_c, const PxVec3& axis, + const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& tr0, const Cm::Matrix34& tr1, + float& dmin, float contactDistance) + { + { +/* float projected0 = axis.dot(tr0.p); + float projected1 = axis.dot(tr1.p); + float min0 = projected0 - polyData0.mInternal.mRadius; + float max0 = projected0 + polyData0.mInternal.mRadius; + float min1 = projected1 - polyData1.mInternal.mRadius; + float max1 = projected1 + polyData1.mInternal.mRadius; +*/ + float MinMaxRadius = polyData0.mInternal.mRadius + polyData1.mInternal.mRadius; + PxVec3 delta = tr0.p - tr1.p; + const PxReal dp = axis.dot(delta); +// const PxReal dp2 = axis.dot(delta_c); + +// const PxReal d0 = max0 - min1; +// const PxReal d0 = projected0 + polyData0.mInternal.mRadius - projected1 + polyData1.mInternal.mRadius; +// const PxReal d0 = projected0 - projected1 + polyData0.mInternal.mRadius + polyData1.mInternal.mRadius; +// const PxReal d0 = projected0 - projected1 + MinMaxRadius; +// const PxReal d0 = axis.dot(tr0.p) - axis.dot(tr1.p) + MinMaxRadius; +// const PxReal d0 = axis.dot(tr0.p - tr1.p) + MinMaxRadius; +// const PxReal d0 = MinMaxRadius + axis.dot(delta); + const PxReal d0 = MinMaxRadius + dp; + +// const PxReal d1 = max1 - min0; +// const PxReal d1 = projected1 + polyData1.mInternal.mRadius - projected0 + polyData0.mInternal.mRadius; +// const PxReal d1 = projected1 - projected0 + polyData1.mInternal.mRadius + polyData0.mInternal.mRadius; +// const PxReal d1 = projected1 - projected0 + MinMaxRadius; +// const PxReal d1 = axis.dot(tr1.p) - axis.dot(tr0.p) + MinMaxRadius; +// const PxReal d1 = axis.dot(tr1.p - tr0.p) + MinMaxRadius; +// const PxReal d1 = MinMaxRadius - axis.dot(delta); + const PxReal d1 = MinMaxRadius - dp; + + dmin = selectMin(d0, d1); + return; + } + + #ifdef USE_BOX_DATA + const PxVec3 localAxis0 = tr0.rotateTranspose(axis); + const PxVec3 localAxis1 = tr1.rotateTranspose(axis); + + const float dp = delta_c.dot(axis); + + float p0[3]; + BoxSupport(polyData0.mInternal.mExtents, localAxis0, p0); + float p1[3]; + BoxSupport(polyData1.mInternal.mExtents, localAxis1, p1); + + const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z; + const float Radius1 = p1[0]*localAxis1.x + p1[1]*localAxis1.y + p1[2]*localAxis1.z; + + const float MinRadius = selectMax(Radius0, polyData0.mInternal.mRadius); + const float MaxRadius = selectMax(Radius1, polyData1.mInternal.mRadius); + #else + const float dp = delta_c.dot(axis); + const float MinRadius = polyData0.mInternal.mRadius; + const float MaxRadius = polyData1.mInternal.mRadius; + #endif + const float MinMaxRadius = MaxRadius + MinRadius; + const float d0 = MinMaxRadius + dp; + const float d1 = MinMaxRadius - dp; + + dmin = selectMin(d0, d1); + } +#endif + +static PX_FORCE_INLINE bool testInternalObjects( const PxVec3& delta_c, const PxVec3& axis, + const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& tr0, const Cm::Matrix34& tr1, + float dmin) +{ +#ifdef USE_BOX_DATA + const PxVec3 localAxis0 = tr0.rotateTranspose(axis); + const PxVec3 localAxis1 = tr1.rotateTranspose(axis); + + const float dp = delta_c.dot(axis); + + float p0[3]; + BoxSupport(polyData0.mInternal.mExtents, localAxis0, p0); + float p1[3]; + BoxSupport(polyData1.mInternal.mExtents, localAxis1, p1); + + const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z; + const float Radius1 = p1[0]*localAxis1.x + p1[1]*localAxis1.y + p1[2]*localAxis1.z; + + const float MinRadius = selectMax(Radius0, polyData0.mInternal.mRadius); + const float MaxRadius = selectMax(Radius1, polyData1.mInternal.mRadius); +#else + const float dp = delta_c.dot(axis); + const float MinRadius = polyData0.mInternal.mRadius; + const float MaxRadius = polyData1.mInternal.mRadius; +#endif + const float MinMaxRadius = MaxRadius + MinRadius; + const float d0 = MinMaxRadius + dp; + const float d1 = MinMaxRadius - dp; + + const float depth = selectMin(d0, d1); + if(depth>dmin) + return false; + return true; +} +#endif + +PX_FORCE_INLINE float PxcMultiplyAdd3x4(PxU32 i, const PxVec3& p0, const PxVec3& p1, const Cm::Matrix34& world) +{ + return (p1.x + p0.x) * world.m.column0[i] + (p1.y + p0.y) * world.m.column1[i] + (p1.z + p0.z) * world.m.column2[i] + 2.0f * world.p[i]; +} + +PX_FORCE_INLINE float PxcMultiplySub3x4(PxU32 i, const PxVec3& p0, const PxVec3& p1, const Cm::Matrix34& world) +{ + return (p1.x - p0.x) * world.m.column0[i] + (p1.y - p0.y) * world.m.column1[i] + (p1.z - p0.z) * world.m.column2[i]; +} + +static PX_FORCE_INLINE bool testNormal( const PxVec3& axis, PxReal min0, PxReal max0, + const PolygonalData& polyData1, + const Cm::Matrix34& m1to0, + const Cm::FastVertex2ShapeScaling& scaling1, + PxReal& depth, PxReal contactDistance) +{ + //The separating axis we want to test is a face normal of hull0 + PxReal min1, max1; + (polyData1.mProjectHull)(polyData1, axis, m1to0, scaling1, min1, max1); + + if(max0+contactDistance boxExtent.x + fAWdU[0]) return false; + + dir.y = PxcMultiplySub3x4(1, p0, p1, world); + boxExtent.y = maximum.y - minimum.y; + diff.y = (PxcMultiplyAdd3x4(1, p0, p1, world) - (maximum.y+minimum.y)); + fAWdU[1] = PxAbs(dir.y); + if(PxAbs(diff.y)> boxExtent.y + fAWdU[1]) return false; + + dir.z = PxcMultiplySub3x4(2, p0, p1, world); + boxExtent.z = maximum.z - minimum.z; + diff.z = (PxcMultiplyAdd3x4(2, p0, p1, world) - (maximum.z+minimum.z)); + fAWdU[2] = PxAbs(dir.z); + if(PxAbs(diff.z)> boxExtent.z + fAWdU[2]) return false; + + PxReal f; + f = dir.y * diff.z - dir.z*diff.y; if(PxAbs(f)>boxExtent.y*fAWdU[2] + boxExtent.z*fAWdU[1]) return false; + f = dir.z * diff.x - dir.x*diff.z; if(PxAbs(f)>boxExtent.x*fAWdU[2] + boxExtent.z*fAWdU[0]) return false; + f = dir.x * diff.y - dir.y*diff.x; if(PxAbs(f)>boxExtent.x*fAWdU[1] + boxExtent.y*fAWdU[0]) return false; + return true; +} + +#define EXPERIMENT + +/* +Edge culling can clearly be improved : in ConvexTest02 for example, edges don't even touch the other mesh sometimes. +*/ +static void PxcFindSeparatingAxes( SeparatingAxes& sa, const PxU32* PX_RESTRICT indices, PxU32 numPolygons, + const PolygonalData& polyData, + const Cm::Matrix34& world0, const PxPlane& plane, + const Cm::Matrix34& m0to1, const PxBounds3& aabb, PxReal contactDistance, + const Cm::FastVertex2ShapeScaling& scaling) +{ +// EdgeCache edgeCache; // PT: TODO: check this is actually useful + const PxVec3* PX_RESTRICT vertices = polyData.mVerts; + const Gu::HullPolygonData* PX_RESTRICT polygons = polyData.mPolygons; + const PxU8* PX_RESTRICT vrefsBase = polyData.mPolygonVertexRefs; + + while(numPolygons--) + { + //Get current polygon + const Gu::HullPolygonData& P = polygons[*indices++]; + const PxU8* PX_RESTRICT VData = vrefsBase + P.mVRef8; + + // Loop through polygon vertices == polygon edges + PxU32 numVerts = P.mNbVerts; + +#ifdef EXPERIMENT + PxVec3 p0,p1; + PxU8 VRef0 = VData[0]; + p0 = scaling * vertices[VRef0]; + bool b0 = plane.distance(p0) <= contactDistance; +#endif + + for(PxU32 j = 0; j < numVerts; j++) + { + PxU32 j1 = j+1; + if(j1 >= numVerts) j1 = 0; + +#ifndef EXPERIMENT + PxU8 VRef0 = VData[j]; +#endif + PxU8 VRef1 = VData[j1]; + +// Ps::order(VRef0, VRef1); //make sure edge (a,b) == edge (b,a) +// if (edgeCache.isInCache(VRef0, VRef1)) +// continue; + + //transform points //TODO: once this works we could transform plan instead, that is more efficient!! + +#ifdef EXPERIMENT + p1 = scaling * vertices[VRef1]; +#else + const PxVec3 p0 = scaling * vertices[VRef0]; + const PxVec3 p1 = scaling * vertices[VRef1]; +#endif + + // Cheap but effective culling! +#ifdef EXPERIMENT + bool b1 = plane.distance(p1) <= contactDistance; + if(b0 || b1) +#else + if(plane.signedDistanceHessianNormalForm(p0) <= contactDistance || + plane.signedDistanceHessianNormalForm(p1) <= contactDistance) +#endif + { + if(PxcSegmentAABBIntersect(p0, p1, aabb.minimum, aabb.maximum, m0to1)) + { + // Create current edge. We're only interested in different edge directions so we normalize. + const PxVec3 currentEdge = world0.rotate(p0 - p1).getNormalized(); + sa.addAxis(currentEdge); + } + } +#ifdef EXPERIMENT + VRef0 = VRef1; + p0 = p1; + b0 = b1; +#endif + } + } +} + +static bool PxcTestFacesSepAxesBackface(const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m1to0, const PxVec3& delta, + PxReal& dmin, PxVec3& sep, PxU32& id, PxU32* PX_RESTRICT indices_, PxU32& numIndices, + PxReal contactDistance, float toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , const PxVec3& worldDelta +#endif + ) +{ + PX_UNUSED(toleranceLength); // Only used in Debug + id = PX_INVALID_U32; + PxU32* indices = indices_; + + const PxU32 num = polyData0.mNbPolygons; + const PxVec3* PX_RESTRICT vertices = polyData0.mVerts; + const Gu::HullPolygonData* PX_RESTRICT polygons = polyData0.mPolygons; + + //transform delta from hull0 shape into vertex space: + const PxVec3 vertSpaceDelta = scaling0 % delta; + + // PT: prefetch polygon data + { + const PxU32 dataSize = num*sizeof(Gu::HullPolygonData); + for(PxU32 offset=0; offset < dataSize; offset+=128) + Ps::prefetchLine(polygons, offset); + } + + for(PxU32 i=0; i < num; i++) + { + const Gu::HullPolygonData& P = polygons[i]; + const PxPlane& PL = P.mPlane; + + // Do backface-culling + if(PL.n.dot(vertSpaceDelta) < 0.0f) + continue; + + //normals transform by inverse transpose: (and transpose(skew) == skew as its symmetric) + PxVec3 shapeSpaceNormal = scaling0 % PL.n; + //renormalize: (Arr!) + const PxReal magnitude = shapeSpaceNormal.normalize(); // PT: We need to find a way to skip this normalize + +#ifdef TEST_INTERNAL_OBJECTS + +/* +const PxVec3 worldNormal_ = world0.rotate(shapeSpaceNormal); +PxReal d0; +bool test0 = PxcTestSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, worldNormal_, d0, contactDistance); + +PxReal d1; +const float invMagnitude0 = 1.0f / magnitude; +bool test1 = PxcTestNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude0, P.getMax() * invMagnitude0, polyData1, m1to0, scaling1, d1, contactDistance); + +PxReal d2; +testInternalObjects_(worldDelta, worldNormal_, polyData0, polyData1, world0, world1, d2, contactDistance); +*/ + + const PxVec3 worldNormal = world0.rotate(shapeSpaceNormal); + if(!testInternalObjects(worldDelta, worldNormal, polyData0, polyData1, world0, world1, dmin)) + { + #if PX_DEBUG + PxReal d; + const float invMagnitude = 1.0f / magnitude; + if(testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s. + { + PX_ASSERT(d + testInternalObjectsEpsilon*toleranceLength >= dmin); + } + #endif + continue; + } +#endif + + *indices++ = i; + + //////////////////// + /* + //gotta transform minimum and maximum from vertex to shape space! + //I think they transform like the 'd' of the plane, basically by magnitude division! + //unfortunately I am not certain of that, so let's convert them to a point in vertex space, transform that, and then convert back to a plane d. + + //let's start by transforming the plane's d: + //a point on the plane: + + PxVec3 vertSpaceDPoint = PL.normal * -PL.d; + + //make sure this is on the plane: + PxReal distZero = PL.signedDistanceHessianNormalForm(vertSpaceDPoint); //should be zero + + //transform: + + PxVec3 shapeSpaceDPoint = cache.mVertex2ShapeSkew[skewIndex] * vertSpaceDPoint; + + //make into a d offset again by projecting along the plane: + PxcPlane shapeSpacePlane(shapeSpaceNormal, shapeSpaceDPoint); + + //see what D is!! + + + //NOTE: for boxes scale[0] is always id so this is all redundant. Hopefully for convex convex it will become useful! + */ + + //////////////////// + + PxReal d; + const float invMagnitude = 1.0f / magnitude; + if(!testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s. + return false; + + if(d < dmin) + { +#ifdef TEST_INTERNAL_OBJECTS + sep = worldNormal; +#else + sep = world0.rotate(shapeSpaceNormal); +#endif + dmin = d; + id = i; + } + } + + numIndices = PxU32(indices - indices_); + + PX_ASSERT(id!=PX_INVALID_U32); //Should never happen with this version + + return true; +} + +// PT: isolating this piece of code allows us to better see what happens in PIX. For some reason it looks +// like isolating it creates *less* LHS than before, but I don't understand why. There's still a lot of +// bad stuff there anyway +//PX_FORCE_INLINE +static void prepareData(PxPlane& witnessPlane0, PxPlane& witnessPlane1, + PxBounds3& aabb0, PxBounds3& aabb1, + const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PxPlane& vertSpacePlane0, const PxPlane& vertSpacePlane1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, + PxReal contactDistance + ) +{ + scaling0.transformPlaneToShapeSpace(vertSpacePlane0.n, vertSpacePlane0.d, witnessPlane0.n, witnessPlane0.d); + scaling1.transformPlaneToShapeSpace(vertSpacePlane1.n, vertSpacePlane1.d, witnessPlane1.n, witnessPlane1.d); + + //witnessPlane0 = witnessPlane0.getTransformed(m0to1); + //witnessPlane1 = witnessPlane1.getTransformed(m1to0); + const PxVec3 newN0 = m0to1.rotate(witnessPlane0.n); + witnessPlane0 = PxPlane(newN0, witnessPlane0.d - m0to1.p.dot(newN0)); + const PxVec3 newN1 = m1to0.rotate(witnessPlane1.n); + witnessPlane1 = PxPlane(newN1, witnessPlane1.d - m1to0.p.dot(newN1)); + + aabb0 = hullBounds0; + aabb1 = hullBounds1; + + //gotta inflate // PT: perfect LHS recipe here.... + const PxVec3 inflate(contactDistance); + aabb0.minimum -= inflate; + aabb1.minimum -= inflate; + aabb0.maximum += inflate; + aabb1.maximum += inflate; +} + +static bool PxcBruteForceOverlapBackface( const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, const PxVec3& delta, + PxU32& id0, PxU32& id1, + PxReal& depth, PxVec3& sep, PxcSepAxisType& code, PxReal contactDistance, float toleranceLength) +{ + const PxVec3 localDelta0 = world0.rotateTranspose(delta); + PxU32* PX_RESTRICT indices0 = reinterpret_cast(PxAlloca(polyData0.mNbPolygons*sizeof(PxU32))); + + PxU32 numIndices0; + PxReal dmin0 = PX_MAX_REAL; + PxVec3 vec0; + if(!PxcTestFacesSepAxesBackface(polyData0, polyData1, world0, world1, scaling0, scaling1, m1to0, localDelta0, dmin0, vec0, id0, indices0, numIndices0, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , -delta +#endif + )) + return false; + + const PxVec3 localDelta1 = world1.rotateTranspose(delta); + + PxU32* PX_RESTRICT indices1 = reinterpret_cast(PxAlloca(polyData1.mNbPolygons*sizeof(PxU32))); + + PxU32 numIndices1; + PxReal dmin1 = PX_MAX_REAL; + PxVec3 vec1; + if(!PxcTestFacesSepAxesBackface(polyData1, polyData0, world1, world0, scaling1, scaling0, m0to1, -localDelta1, dmin1, vec1, id1, indices1, numIndices1, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , delta +#endif + )) + return false; + + PxReal dmin = dmin0; + PxVec3 vec = vec0; + code = SA_NORMAL0; + + if(dmin1 < dmin) + { + dmin = dmin1; + vec = vec1; + code = SA_NORMAL1; + } + + PX_ASSERT(id0!=PX_INVALID_U32); + PX_ASSERT(id1!=PX_INVALID_U32); + + // Brute-force find a separating axis + SeparatingAxes mSA0; + SeparatingAxes mSA1; + mSA0.reset(); + mSA1.reset(); + PxPlane witnessPlane0; + PxPlane witnessPlane1; + PxBounds3 aabb0, aabb1; + + prepareData(witnessPlane0, witnessPlane1, + aabb0, aabb1, + hullBounds0, hullBounds1, + polyData0.mPolygons[id0].mPlane, + polyData1.mPolygons[id1].mPlane, + scaling0, scaling1, + m0to1, m1to0, + contactDistance); + + // Find possibly separating axes + PxcFindSeparatingAxes(mSA0, indices0, numIndices0, polyData0, world0, witnessPlane1, m0to1, aabb1, contactDistance, scaling0); + PxcFindSeparatingAxes(mSA1, indices1, numIndices1, polyData1, world1, witnessPlane0, m1to0, aabb0, contactDistance, scaling1); +// PxcFindSeparatingAxes(context.mSA0, &id0, 1, hull0, world0, witnessPlane1, m0to1, aabbMin1, aabbMax1); +// PxcFindSeparatingAxes(context.mSA1, &id1, 1, hull1, world1, witnessPlane0, m1to0, aabbMin0, aabbMax0); + + const PxU32 numEdges0 = mSA0.getNumAxes(); + const PxVec3* PX_RESTRICT edges0 = mSA0.getAxes(); + + const PxU32 numEdges1 = mSA1.getNumAxes(); + const PxVec3* PX_RESTRICT edges1 = mSA1.getAxes(); + + // Worst case = convex test 02 with big meshes: 23 & 23 edges => 23*23 = 529 tests! + // printf("%d - %d\n", NbEdges0, NbEdges1); // maximum = ~20 in test scenes. + + for(PxU32 i=0; i < numEdges0; i++) + { + const PxVec3& edge0 = edges0[i]; + for(PxU32 j=0; j < numEdges1; j++) + { + const PxVec3& edge1 = edges1[j]; + + PxVec3 sepAxis = edge0.cross(edge1); + if(!Ps::isAlmostZero(sepAxis)) + { + sepAxis = sepAxis.getNormalized(); + +#ifdef TEST_INTERNAL_OBJECTS + if(!testInternalObjects(-delta, sepAxis, polyData0, polyData1, world0, world1, dmin)) + { + #if PX_DEBUG + PxReal d; + if(testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, sepAxis, d, contactDistance)) + { + PX_ASSERT(d + testInternalObjectsEpsilon*toleranceLength >= dmin); + } + #endif + continue; + } +#endif + PxReal d; + if(!testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, sepAxis, d, contactDistance)) + return false; + + if(d= dmin); + } + #endif + continue; + } +#endif + + PxReal d; + const float invMagnitude = 1.0f / magnitude; + if(!testNormal(shapeSpaceNormal, P.getMin(vertices) * invMagnitude, P.getMax() * invMagnitude, /*hull1,*/ polyData1, m1to0, scaling1, d, contactDistance)) //note how we scale scalars by skew magnitude change as we do plane d-s. + return false; + + if(d < dmin) + { +#ifdef TEST_INTERNAL_OBJECTS + sep = worldNormal; +#else + sep = world0.rotate(shapeSpaceNormal); +#endif + dmin = d; + id = i; + } + } + + PX_ASSERT(id!=PX_INVALID_U32); //Should never happen with this version + + return true; +} + +static bool GuBruteForceOverlapBackfaceRoughPass( const PolygonalData& polyData0, const PolygonalData& polyData1, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, const PxVec3& delta, + PxU32& id0, PxU32& id1, + PxReal& depth, PxVec3& sep, PxcSepAxisType& code, PxReal contactDistance, PxReal toleranceLength) +{ + PxReal dmin0 = PX_MAX_REAL; + PxReal dmin1 = PX_MAX_REAL; + PxVec3 vec0, vec1; + + const PxVec3 localDelta0 = world0.rotateTranspose(delta); + const PxVec3 localCenter1in0 = m1to0.transform(polyData1.mCenter); + if(!GuTestFacesSepAxesBackfaceRoughPass(polyData0, polyData1, world0, world1, scaling0, scaling1, m1to0, localCenter1in0, localDelta0, dmin0, vec0, id0, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , -delta +#endif + )) + return false; + + const PxVec3 localDelta1 = world1.rotateTranspose(delta); + const PxVec3 localCenter0in1 = m0to1.transform(polyData0.mCenter); + if(!GuTestFacesSepAxesBackfaceRoughPass(polyData1, polyData0, world1, world0, scaling1, scaling0, m0to1, localCenter0in1, -localDelta1, dmin1, vec1, id1, contactDistance, toleranceLength +#ifdef TEST_INTERNAL_OBJECTS + , delta +#endif + )) + return false; + + PxReal dmin = dmin0; + PxVec3 vec = vec0; + code = SA_NORMAL0; + + if(dmin1 < dmin) + { + dmin = dmin1; + vec = vec1; + code = SA_NORMAL1; + } + + PX_ASSERT(id0!=PX_INVALID_U32); + PX_ASSERT(id1!=PX_INVALID_U32); + + depth = dmin; + sep = vec; + + return true; +} + +static bool GuContactHullHull( const PolygonalData& polyData0, const PolygonalData& polyData1, + const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PxTransform& transform0, const PxTransform& transform1, + const NarrowPhaseParams& params, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + bool idtScale0, bool idtScale1); + +namespace physx +{ + +namespace Gu +{ +// Box-convex contact generation +// this can no longer share code with convex-convex because that case needs scaling for both shapes, while this only needs it for one, which may be significant perf wise. +// +// PT: duplicating the full convex-vs-convex codepath is a lot worse. Look at it this way: if scaling is really "significant perf wise" then we made the whole convex-convex +// codepath significantly slower, and this is a lot more important than just box-vs-convex. The proper approach is to share the code and make sure scaling is NOT a perf hit. +// PT: please leave this function in the same translation unit as PxcContactHullHull. + +bool contactBoxConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxBoxGeometry& shapeBox = shape0.get(); + + Cm::FastVertex2ShapeScaling idtScaling; + + const PxBounds3 boxBounds(-shapeBox.halfExtents, shapeBox.halfExtents); + + PolygonalData polyData0; + PolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData0); + + /////// + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 convexBounds; + PolygonalData polyData1; + const bool idtScale = getConvexData(shape1, convexScaling, convexBounds, polyData1); + + return GuContactHullHull( polyData0, polyData1, boxBounds, convexBounds, + transform0, transform1, params, contactBuffer, + idtScaling, convexScaling, true, idtScale); +} + +// PT: please leave this function in the same translation unit as PxcContactHullHull. +bool contactConvexConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + Cm::FastVertex2ShapeScaling scaling0, scaling1; + PxBounds3 convexBounds0, convexBounds1; + PolygonalData polyData0, polyData1; + const bool idtScale0 = getConvexData(shape0, scaling0, convexBounds0, polyData0); + const bool idtScale1 = getConvexData(shape1, scaling1, convexBounds1, polyData1); + + return GuContactHullHull( polyData0, polyData1, convexBounds0, convexBounds1, + transform0, transform1, params, contactBuffer, + scaling0, scaling1, idtScale0, idtScale1); +} +}//Gu +}//physx + +static bool GuContactHullHull( const PolygonalData& polyData0, const PolygonalData& polyData1, + const PxBounds3& hullBounds0, const PxBounds3& hullBounds1, + const PxTransform& transform0, const PxTransform& transform1, + const NarrowPhaseParams& params, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& scaling0, const Cm::FastVertex2ShapeScaling& scaling1, + bool idtScale0, bool idtScale1) +{ + // Compute matrices + const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + + const PxVec3 worldCenter0 = world0.transform(polyData0.mCenter); + const PxVec3 worldCenter1 = world1.transform(polyData1.mCenter); + + const PxVec3 deltaC = worldCenter1 - worldCenter0; + + PxReal depth; + + /////////////////////////////////////////////////////////////////////////// + + // Early-exit test: quickly discard obviously non-colliding cases. + // PT: we used to skip this when objects were touching, which provided a small speedup (saving 2 full hull projections). + // We may want to add this back at some point in the future, if possible. + if(true) //AM: now we definitely want to test the cached axis to get the depth value for the cached axis, even if we had a contact before. + { + if(!testSeparatingAxis(polyData0, polyData1, world0, world1, scaling0, scaling1, deltaC, depth, params.mContactDistance)) + //there was no contact previously and we reject using the same prev. axis. + return false; + } + + /////////////////////////////////////////////////////////////////////////// + + // Compute relative transforms + PxTransform t0to1 = transform1.transformInv(transform0); + PxTransform t1to0 = transform0.transformInv(transform1); + + PxU32 c0(0x7FFF); + PxU32 c1(0x7FFF); + PxVec3 worldNormal; + + const Cm::Matrix34 m0to1(t0to1); + const Cm::Matrix34 m1to0(t1to0); + +#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS + // PT: it is a bad idea to skip the rough pass, for two reasons: + // 1) performance. The rough pass is obviously a lot faster. + // 2) stability. The "skipIt" optimization relies on the rough pass being present to catch the cases where it fails. If you disable the rough pass + // "skipIt" can skip the whole work, contacts get lost, and we never "try again" ==> explosions +// bool TryRoughPass = (contactDistance == 0.0f); //Rough first pass doesn't work with dist based for some reason. + for(int TryRoughPass = 1 /*gEnableOptims*/; 0 <= TryRoughPass; --TryRoughPass) + { +#endif + + { + PxcSepAxisType code; + PxU32 id0, id1; + //PxReal depth; +#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS + bool Status; + if(TryRoughPass) + { + Status = GuBruteForceOverlapBackfaceRoughPass(polyData0, polyData1, world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, params.mContactDistance, params.mToleranceLength); + } + else + { + Status = PxcBruteForceOverlapBackface( +// hull0, hull1, + hullBounds0, hullBounds1, + polyData0, polyData1, world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, params.mContactDistance, params.mToleranceLength); + } + + if(!Status) +#else + if(!PxcBruteForceOverlapBackface( +// hull0, hull1, + hullBounds0, hullBounds1, + polyData0, polyData1, + world0, world1, scaling0, scaling1, m0to1, m1to0, deltaC, id0, id1, depth, worldNormal, code, contactDistance)) +#endif + return false; + + if(deltaC.dot(worldNormal) < 0.0f) + worldNormal = -worldNormal; + +/* +worldNormal = -partialSep; +depth = -partialDepth; +code = SA_EE; +*/ + + if(code==SA_NORMAL0) + { + c0 = id0; + c1 = (polyData1.mSelectClosestEdgeCB)(polyData1, scaling1, world1.rotateTranspose(-worldNormal)); + } + else if(code==SA_NORMAL1) + { + c0 = (polyData0.mSelectClosestEdgeCB)(polyData0, scaling0, world0.rotateTranspose(worldNormal)); + c1 = id1; + } + else if(code==SA_EE) + { + c0 = (polyData0.mSelectClosestEdgeCB)(polyData0, scaling0, world0.rotateTranspose(worldNormal)); + c1 = (polyData1.mSelectClosestEdgeCB)(polyData1, scaling1, world1.rotateTranspose(-worldNormal)); + } + } + + const Gu::HullPolygonData& HP0 = polyData0.mPolygons[c0]; + const Gu::HullPolygonData& HP1 = polyData1.mPolygons[c1]; + // PT: prefetching those guys saves ~600.000 cycles in convex-convex benchmark + Ps::prefetchLine(&HP0.mPlane); + Ps::prefetchLine(&HP1.mPlane); + + //ok, we have a new depth value. convert to real distance. +// PxReal separation = -depth; //depth was either computed in initial cached-axis check, or better, when skipIt was false, in sep axis search. +// if (separation < 0.0f) +// separation = 0.0f; //we don't want to store penetration values. + const PxReal separation = fsel(depth, 0.0f, -depth); + + PxVec3 worldNormal0; + PX_ALIGN(16, PxPlane) shapeSpacePlane0; + if(idtScale0) + { + V4StoreA(V4LoadU(&HP0.mPlane.n.x), &shapeSpacePlane0.n.x); + worldNormal0 = world0.rotate(HP0.mPlane.n); + } + else + { + scaling0.transformPlaneToShapeSpace(HP0.mPlane.n,HP0.mPlane.d,shapeSpacePlane0.n,shapeSpacePlane0.d); + worldNormal0 = world0.rotate(shapeSpacePlane0.n); + } + + PxVec3 worldNormal1; + PX_ALIGN(16, PxPlane) shapeSpacePlane1; + if(idtScale1) + { + V4StoreA(V4LoadU(&HP1.mPlane.n.x), &shapeSpacePlane1.n.x); + worldNormal1 = world1.rotate(HP1.mPlane.n); + } + else + { + scaling1.transformPlaneToShapeSpace(HP1.mPlane.n,HP1.mPlane.d,shapeSpacePlane1.n,shapeSpacePlane1.d); + worldNormal1 = world1.rotate(shapeSpacePlane1.n); + } + + Ps::IntBool flag; + { + const PxReal d0 = PxAbs(worldNormal0.dot(worldNormal)); + const PxReal d1 = PxAbs(worldNormal1.dot(worldNormal)); + bool f = d0>d1; //which face normal is the separating axis closest to. + flag = f; + } + +////////////////////NEW DIST HANDLING////////////////////// + PX_ASSERT(separation >= 0.0f); //be sure this got fetched somewhere in the chaos above. + + const PxReal cCCDEpsilon = params.mMeshContactMargin; + const PxReal contactGenPositionShift = separation + cCCDEpsilon; //if we're at a distance, shift so we're in penetration. + + const PxVec3 contactGenPositionShiftVec = worldNormal * -contactGenPositionShift; //shift one of the bodies this distance toward the other just for Pierre's contact generation. Then the bodies should be penetrating exactly by MIN_SEPARATION_FOR_PENALTY - ideal conditions for this contact generator. + + //note: for some reason this has to change sign! + + //this will make contact gen always generate contacts at about MSP. Shift them back to the true real distance, and then to a solver compliant distance given that + //the solver converges to MSP penetration, while we want it to converge to 0 penetration. + //to real distance: + + //The system: We always shift convex 0 (arbitrary). If the contact is attached to convex 0 then we will need to shift the contact point, otherwise not. + const PxVec3 newp = world0.p - contactGenPositionShiftVec; + const Cm::Matrix34 world0_Tweaked(world0.m.column0, world0.m.column1, world0.m.column2, newp); + PxTransform shifted0(newp, transform0.q); + + t0to1 = transform1.transformInv(shifted0); + t1to0 = shifted0.transformInv(transform1); + Cm::Matrix34 m0to1_Tweaked; + Cm::Matrix34 m1to0_Tweaked; + m0to1_Tweaked.set(t0to1.q); m0to1_Tweaked.p = t0to1.p; + m1to0_Tweaked.set(t1to0.q); m1to0_Tweaked.p = t1to0.p; + +////////////////////////////////////////////////// + //pretransform convex polygon if we have scaling! + PxVec3* scaledVertices0; + PxU8* stackIndices0; + GET_SCALEX_CONVEX(scaledVertices0, stackIndices0, idtScale0, HP0.mNbVerts, scaling0, polyData0.mVerts, polyData0.getPolygonVertexRefs(HP0)) + + //pretransform convex polygon if we have scaling! + PxVec3* scaledVertices1; + PxU8* stackIndices1; + GET_SCALEX_CONVEX(scaledVertices1, stackIndices1, idtScale1, HP1.mNbVerts, scaling1, polyData1.mVerts, polyData1.getPolygonVertexRefs(HP1)) + + // So we need to switch: + // - HP0, HP1 + // - scaledVertices0, scaledVertices1 + // - stackIndices0, stackIndices1 + // - world0, world1 + // - shapeSpacePlane0, shapeSpacePlane1 + // - worldNormal0, worldNormal1 + // - m0to1, m1to0 + // - true, false + const PxMat33 RotT0 = findRotationMatrixFromZ(shapeSpacePlane0.n); + const PxMat33 RotT1 = findRotationMatrixFromZ(shapeSpacePlane1.n); + + if(flag) + { + if(contactPolygonPolygonExt(HP0.mNbVerts, scaledVertices0, stackIndices0, world0_Tweaked, shapeSpacePlane0, RotT0, + HP1.mNbVerts, scaledVertices1, stackIndices1, world1, shapeSpacePlane1, RotT1, + worldNormal0, m0to1_Tweaked, m1to0_Tweaked, + PXC_CONTACT_NO_FACE_INDEX, PXC_CONTACT_NO_FACE_INDEX, + contactBuffer, + true, contactGenPositionShiftVec, contactGenPositionShift)) + return true; + } + else + { + if(contactPolygonPolygonExt(HP1.mNbVerts, scaledVertices1, stackIndices1, world1, shapeSpacePlane1, RotT1, + HP0.mNbVerts, scaledVertices0, stackIndices0, world0_Tweaked, shapeSpacePlane0, RotT0, + worldNormal1, m1to0_Tweaked, m0to1_Tweaked, + PXC_CONTACT_NO_FACE_INDEX, PXC_CONTACT_NO_FACE_INDEX, + contactBuffer, + false, contactGenPositionShiftVec, contactGenPositionShift)) + return true; + } + +#ifdef CONVEX_CONVEX_ROUGH_FIRST_PASS + } +#endif + return false; +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp new file mode 100644 index 000000000..2f70453f8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp @@ -0,0 +1,1447 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexUtilsInternal.h" +#include "GuInternal.h" +#include "GuContactPolygonPolygon.h" +#include "GuConvexEdgeFlags.h" +#include "GuSeparatingAxes.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuMidphaseInterface.h" +#include "GuConvexHelper.h" +#include "GuTriangleCache.h" +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "GuGeometryUnion.h" +#include "GuIntersectionTriangleBox.h" +#include "CmUtils.h" +#include "PsAllocator.h" +#include "GuBox.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; +using namespace shdfnd::aos; +using namespace intrinsics; + +#define LOCAL_TOUCHED_TRIG_SIZE 192 + +//#define USE_TRIANGLE_NORMAL +#define TEST_INTERNAL_OBJECTS + +static PX_FORCE_INLINE void projectTriangle(const PxVec3& localSpaceDirection, const PxVec3* PX_RESTRICT triangle, PxReal& min1, PxReal& max1) +{ + const PxReal dp0 = triangle[0].dot(localSpaceDirection); + const PxReal dp1 = triangle[1].dot(localSpaceDirection); + min1 = selectMin(dp0, dp1); + max1 = selectMax(dp0, dp1); + + const PxReal dp2 = triangle[2].dot(localSpaceDirection); + min1 = selectMin(min1, dp2); + max1 = selectMax(max1, dp2); +} + +#ifdef TEST_INTERNAL_OBJECTS + +static PX_FORCE_INLINE void boxSupport(const float extents[3], const PxVec3& sv, float p[3]) +{ + const PxU32* iextents = reinterpret_cast(extents); + const PxU32* isv = reinterpret_cast(&sv); + PxU32* ip = reinterpret_cast(p); + + ip[0] = iextents[0]|(isv[0]&PX_SIGN_BITMASK); + ip[1] = iextents[1]|(isv[1]&PX_SIGN_BITMASK); + ip[2] = iextents[2]|(isv[2]&PX_SIGN_BITMASK); +} + +#if PX_DEBUG +static const PxReal testInternalObjectsEpsilon = 1.0e-3f; +#endif + +static PX_FORCE_INLINE bool testInternalObjects(const PxVec3& localAxis0, + const PolygonalData& polyData0, + const PxVec3* PX_RESTRICT triangleInHullSpace, + float dmin) +{ + PxReal min1, max1; + projectTriangle(localAxis0, triangleInHullSpace, min1, max1); + + const float dp = polyData0.mCenter.dot(localAxis0); + + float p0[3]; + boxSupport(polyData0.mInternal.mExtents, localAxis0, p0); + const float Radius0 = p0[0]*localAxis0.x + p0[1]*localAxis0.y + p0[2]*localAxis0.z; + const float bestRadius = selectMax(Radius0, polyData0.mInternal.mRadius); + const PxReal min0 = dp - bestRadius; + const PxReal max0 = dp + bestRadius; + + const PxReal d0 = max0 - min1; + const PxReal d1 = max1 - min0; + + const float depth = selectMin(d0, d1); + if(depth>dmin) + return false; + return true; +} +#endif + +static PX_FORCE_INLINE bool testNormal( const PxVec3& sepAxis, PxReal min0, PxReal max0, + const PxVec3* PX_RESTRICT triangle, + PxReal& depth, PxReal contactDistance) +{ + PxReal min1, max1; + projectTriangle(sepAxis, triangle, min1, max1); + + if(max0+contactDistance 0.0f) +#else + // ### this is dubious since the triangle center is likely to be very close to the hull, if not inside. Why not use the triangle normal? + if(PL.distance(witness) < 0.0f) +#endif + continue; //backface culled + + *hullIndices++ = i; + + const PxVec3 sepAxis = m0to1.rotate(PL.n); + const PxReal dp = sepAxis.dot(trans); + + PxReal d; + if(!testNormal(sepAxis, P.getMin(vertices) + dp, P.getMax() + dp, triangle, + d, contactDistance)) + return false; + + if(d < dmin) + { + dmin = d; + sep = sepAxis; + id = i; + } + } + } + else + { +#ifndef USE_TRIANGLE_NORMAL + //transform delta from hull0 shape into vertex space: + const PxVec3 vertSpaceWitness = convexScaling % witness; +#endif + for(PxU32 i=0; i 0.0f) +#else + // ### this is dubious since the triangle center is likely to be very close to the hull, if not inside. Why not use the triangle normal? + if(PL.distance(vertSpaceWitness) < 0.0f) +#endif + continue; //backface culled + + //normals transform by inverse transpose: (and transpose(skew) == skew as its symmetric) + PxVec3 shapeSpaceNormal = convexScaling % PL.n; + //renormalize: (Arr!) + const PxReal magnitude = shapeSpaceNormal.normalize(); + + *hullIndices++ = i; + const PxVec3 sepAxis = m0to1.rotate(shapeSpaceNormal); + PxReal d; + const PxReal dp = sepAxis.dot(trans); + + const float oneOverM = 1.0f / magnitude; + if(!testNormal(sepAxis, P.getMin(vertices) * oneOverM + dp, P.getMax() * oneOverM + dp, triangle, + d, contactDistance)) + return false; + + if(d < dmin) + { + dmin = d; + sep = sepAxis; + id = i; + } + } + } + numHullIndices = PxU32(hullIndices - hullIndices_); + } + + // Backup + if(id == PX_INVALID_U32) + { + if(idtConvexScale) + { + for(PxU32 i=0; i=numVerts) j1 = 0; + + const PxU32 VRef0 = data[j]; + const PxU32 VRef1 = data[j1]; + + if(edgeCulling(vertexSpacePlane, hullVerts[VRef0], hullVerts[VRef1], contactDistance)) + { + const PxVec3 currentHullEdge = m0to1.rotate(convexScaling * (hullVerts[VRef0] - hullVerts[VRef1])); //matrix mult is distributive! + + PxVec3 sepAxis = currentHullEdge.cross(currentPolyEdge); + if(!Ps::isAlmostZero(sepAxis)) + SA.addAxis(sepAxis.getNormalized()); + } + } + } + } + + dmin = PX_MAX_REAL; + PxU32 numAxes = SA.getNumAxes(); + const PxVec3* PX_RESTRICT axes = SA.getAxes(); + +#ifdef TEST_INTERNAL_OBJECTS + PxVec3 triangleInHullSpace[3]; + if(numAxes) + { + triangleInHullSpace[0] = m1to0.transform(triangle[0]); + triangleInHullSpace[1] = m1to0.transform(triangle[1]); + triangleInHullSpace[2] = m1to0.transform(triangle[2]); + } +#endif + + while(numAxes--) + { + const PxVec3& currentAxis = *axes++; + +#ifdef TEST_INTERNAL_OBJECTS + const PxVec3 localAxis0 = m1to0.rotate(currentAxis); + if(!testInternalObjects(localAxis0, polyData0, triangleInHullSpace, dmin)) + { + #if PX_DEBUG + PxReal dtest; + if(testSepAxis(currentAxis, polyData0, triangle, m0to1, convexScaling, dtest, contactDistance)) + { + PX_ASSERT(dtest + testInternalObjectsEpsilon*toleranceLength >= dmin); + } + #endif + continue; + } +#endif + + PxReal d; + if(!testSepAxis(currentAxis, polyData0, triangle, + m0to1, convexScaling, d, contactDistance)) + { + return false; + } + + if(d < dmin) + { + dmin = d; + vec = currentAxis; + } + } + return true; +} + +static bool triangleConvexTest( const PolygonalData& polyData0, + const PxU8 triFlags, + PxU32 index, const PxVec3* PX_RESTRICT localPoints, + const PxPlane& localPlane, + const PxVec3& groupCenterHull, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, const Cm::Matrix34& m0to1, const Cm::Matrix34& m1to0, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, PxReal toleranceLength, + PxVec3& groupAxis, PxReal& groupMinDepth, bool& faceContact, + bool idtConvexScale + ) +{ + PxU32 id0 = PX_INVALID_U32; + PxReal dmin0 = PX_MAX_REAL; + PxVec3 vec0; + + PxU32 numHullIndices = 0; + PxU32* PX_RESTRICT const hullIndices = reinterpret_cast(PxAlloca(polyData0.mNbPolygons*sizeof(PxU32))); + + // PT: we test the hull normals first because they don't need any hull projection. If we can early exit thanks + // to those, we completely avoid all hull projections. + bool status = testFacesSepAxesBackface(polyData0, world0, world1, m0to1, groupCenterHull, localPoints, + convexScaling, numHullIndices, hullIndices, dmin0, vec0, id0, contactDistance, idtConvexScale); + if(!status) + return false; + + groupAxis = PxVec3(0); + groupMinDepth = PX_MAX_REAL; + + const PxReal eps = 0.0001f; // DE7748 + + //Test in mesh-space + PxVec3 sepAxis; + PxReal depth; + + { + // Test triangle normal + PxReal d; + if(!testSepAxis(localPlane.n, polyData0, localPoints, + m0to1, convexScaling, d, contactDistance)) + return false; + + if(d& mDelayedContacts; + Gu::CacheMap mEdgeCache; + Gu::CacheMap mVertCache; + + const Cm::Matrix34 m0to1; + const Cm::Matrix34 m1to0; + + PxVec3 mHullCenterMesh; + PxVec3 mHullCenterWorld; + + const PolygonalData& mPolyData0; + const Cm::Matrix34& mWorld0; + const Cm::Matrix34& mWorld1; + + const Cm::FastVertex2ShapeScaling& mConvexScaling; + + PxReal mContactDistance; + PxReal mToleranceLength; + bool mIdtMeshScale, mIdtConvexScale; + PxReal mCCDEpsilon; + const PxTransform& mTransform0; + const PxTransform& mTransform1; + ContactBuffer& mContactBuffer; + bool mAnyHits; + + ConvexMeshContactGeneration( + Ps::InlineArray& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer + ); + + void processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + void generateLastContacts(); + + bool generateContacts( + const PxPlane& localPlane, + const PxVec3* PX_RESTRICT localPoints, + const PxVec3& triCenter, PxVec3& groupAxis, + PxReal groupMinDepth, PxU32 index) const; + + private: + ConvexMeshContactGeneration& operator=(const ConvexMeshContactGeneration&); + }; + +// 17 entries. 1088/17 = 64 triangles in the local array +struct SavedContactData +{ + PxU32 mTriangleIndex; + PxVec3 mVerts[3]; + PxU32 mInds[3]; + PxVec3 mGroupAxis; + PxReal mGroupMinDepth; +}; +} + +ConvexMeshContactGeneration::ConvexMeshContactGeneration( + Ps::InlineArray& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer +) : + mDelayedContacts(delayedContacts), + m0to1 (t0to1), + m1to0 (t1to0), + mPolyData0 (polyData0), + mWorld0 (world0), + mWorld1 (world1), + mConvexScaling (convexScaling), + mContactDistance(contactDistance), + mToleranceLength(toleranceLength), + mIdtConvexScale (idtConvexScale), + mCCDEpsilon (cCCDEpsilon), + mTransform0 (transform0), + mTransform1 (transform1), + mContactBuffer (contactBuffer) +{ + delayedContacts.forceSize_Unsafe(0); + mAnyHits = false; + + // Hull center in local space + const PxVec3& hullCenterLocal = mPolyData0.mCenter; + // Hull center in mesh space + mHullCenterMesh = m0to1.transform(hullCenterLocal); + // Hull center in world space + mHullCenterWorld = mWorld0.transform(hullCenterLocal); +} + +struct ConvexMeshContactGenerationCallback : MeshHitCallback +{ + ConvexMeshContactGeneration mGeneration; + const Cm::FastVertex2ShapeScaling& mMeshScaling; + const PxU8* PX_RESTRICT mExtraTrigData; + bool mIdtMeshScale; + const TriangleMesh* mMeshData; + const BoxPadded& mBox; + + ConvexMeshContactGenerationCallback( + Ps::InlineArray& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const TriangleMesh* meshData, + const PxU8* PX_RESTRICT extraTrigData, + const Cm::FastVertex2ShapeScaling& meshScaling, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtMeshScale, bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer, + const BoxPadded& box + ) : + MeshHitCallback (CallbackMode::eMULTIPLE), + mGeneration (delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, contactDistance, toleranceLength, idtConvexScale, cCCDEpsilon, transform0, transform1, contactBuffer), + mMeshScaling (meshScaling), + mExtraTrigData (extraTrigData), + mIdtMeshScale (idtMeshScale), + mMeshData (meshData), + mBox (box) + { + } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + // PT: this one is safe because incoming vertices from midphase are always safe to V4Load (by design) + // PT: TODO: is this test really needed? Not done in midphase already? + if(!intersectTriangleBox(mBox, v0, v1, v2)) + return true; + + PxVec3 verts[3]; + getScaledVertices(verts, v0, v1, v2, mIdtMeshScale, mMeshScaling); + + const PxU32 triangleIndex = hit.faceIndex; + + const PxU8 extraData = getConvexEdgeFlags(mExtraTrigData, triangleIndex); + mGeneration.processTriangle(verts, triangleIndex, extraData, vinds); + return true; + } + +protected: + ConvexMeshContactGenerationCallback &operator=(const ConvexMeshContactGenerationCallback &); +}; + +bool ConvexMeshContactGeneration::generateContacts( + const PxPlane& localPlane, + const PxVec3* PX_RESTRICT localPoints, + const PxVec3& triCenter, PxVec3& groupAxis, + PxReal groupMinDepth, PxU32 index) const +{ + const PxVec3 worldGroupCenter = mWorld1.transform(triCenter); + const PxVec3 deltaC = mHullCenterWorld - worldGroupCenter; + if(deltaC.dot(groupAxis) < 0.0f) + groupAxis = -groupAxis; + + const PxU32 id = (mPolyData0.mSelectClosestEdgeCB)(mPolyData0, mConvexScaling, mWorld0.rotateTranspose(-groupAxis)); + + const HullPolygonData& HP = mPolyData0.mPolygons[id]; + PX_ALIGN(16, PxPlane) shapeSpacePlane0; + if(mIdtConvexScale) + V4StoreA(V4LoadU(&HP.mPlane.n.x), &shapeSpacePlane0.n.x); + else + mConvexScaling.transformPlaneToShapeSpace(HP.mPlane.n, HP.mPlane.d, shapeSpacePlane0.n, shapeSpacePlane0.d); + + const PxVec3 hullNormalWorld = mWorld0.rotate(shapeSpacePlane0.n); + + const PxReal d0 = PxAbs(hullNormalWorld.dot(groupAxis)); + + const PxVec3 triNormalWorld = mWorld1.rotate(localPlane.n); + const PxReal d1 = PxAbs(triNormalWorld.dot(groupAxis)); + const bool d0biggerd1 = d0 > d1; + +////////////////////NEW DIST HANDLING////////////////////// + //TODO: skip this if there is no dist involved! + +PxReal separation = - groupMinDepth; //convert to real distance. + +separation = fsel(separation, separation, 0.0f); //don't do anything when penetrating! + +//printf("\nseparation = %f", separation); + +PxReal contactGenPositionShift = separation + mCCDEpsilon; //if we're at a distance, shift so we're within penetration. + +PxVec3 contactGenPositionShiftVec = groupAxis * contactGenPositionShift; //shift one of the bodies this distance toward the other just for Pierre's contact generation. Then the bodies should be penetrating exactly by MIN_SEPARATION_FOR_PENALTY - ideal conditions for this contact generator. + +//note: for some reason this has to change sign! + +//this will make contact gen always generate contacts at about MSP. Shift them back to the true real distance, and then to a solver compliant distance given that +//the solver converges to MSP penetration, while we want it to converge to 0 penetration. +//to real distance: +// PxReal polyPolySeparationShift = separation; //(+ or - depending on which way normal goes) + +//The system: We always shift convex 0 (arbitrary). If the contact is attached to convex 0 then we will need to shift the contact point, otherwise not. + +//TODO: make these overwrite orig location if its safe to do so. + +Cm::Matrix34 world0_(mWorld0); +PxTransform transform0_(mTransform0); + +world0_.p -= contactGenPositionShiftVec; +transform0_.p = world0_.p; //reset this too. + +PxTransform t0to1_ = mTransform1.transformInv(transform0_); +PxTransform t1to0_ = transform0_.transformInv(mTransform1); +Cm::Matrix34 m0to1_(t0to1_); +Cm::Matrix34 m1to0_(t1to0_); + + PxVec3* scaledVertices0; + PxU8* stackIndices0; + GET_SCALEX_CONVEX(scaledVertices0, stackIndices0, mIdtConvexScale, HP.mNbVerts, mConvexScaling, mPolyData0.mVerts, mPolyData0.getPolygonVertexRefs(HP)) + + const PxU8 indices[3] = {0, 1, 2}; + + const PxMat33 RotT0 = findRotationMatrixFromZ(shapeSpacePlane0.n); + const PxMat33 RotT1 = findRotationMatrixFromZ(localPlane.n); + + if(d0biggerd1) + { + if(contactPolygonPolygonExt( + HP.mNbVerts, scaledVertices0, stackIndices0, world0_, shapeSpacePlane0, + RotT0, + 3, localPoints, indices, mWorld1, localPlane, + RotT1, + hullNormalWorld, m0to1_, m1to0_, PXC_CONTACT_NO_FACE_INDEX, index, + mContactBuffer, + true, + contactGenPositionShiftVec, contactGenPositionShift)) + { + return true; + } + } + else + { + if(contactPolygonPolygonExt( + 3, localPoints, indices, mWorld1, localPlane, + RotT1, + HP.mNbVerts, scaledVertices0, stackIndices0, world0_, shapeSpacePlane0, + RotT0, + triNormalWorld, m1to0_, m0to1_, PXC_CONTACT_NO_FACE_INDEX, index, + mContactBuffer, + false, + contactGenPositionShiftVec, contactGenPositionShift)) + { + return true; + } + } + return false; +} + +enum FeatureCode +{ + FC_VERTEX0, + FC_VERTEX1, + FC_VERTEX2, + FC_EDGE01, + FC_EDGE12, + FC_EDGE20, + FC_FACE, + + FC_UNDEFINED +}; + +static FeatureCode computeFeatureCode(const PxVec3& point, const PxVec3* verts) +{ + const PxVec3& triangleOrigin = verts[0]; + const PxVec3 triangleEdge0 = verts[1] - verts[0]; + const PxVec3 triangleEdge1 = verts[2] - verts[0]; + + const PxVec3 kDiff = triangleOrigin - point; + const PxReal fA00 = triangleEdge0.magnitudeSquared(); + const PxReal fA01 = triangleEdge0.dot(triangleEdge1); + const PxReal fA11 = triangleEdge1.magnitudeSquared(); + const PxReal fB0 = kDiff.dot(triangleEdge0); + const PxReal fB1 = kDiff.dot(triangleEdge1); + const PxReal fDet = PxAbs(fA00*fA11 - fA01*fA01); + const PxReal u = fA01*fB1-fA11*fB0; + const PxReal v = fA01*fB0-fA00*fB1; + + FeatureCode fc = FC_UNDEFINED; + + if(u + v <= fDet) + { + if(u < 0.0f) + { + if(v < 0.0f) // region 4 + { + if(fB0 < 0.0f) + { + if(-fB0 >= fA00) + fc = FC_VERTEX1; + else + fc = FC_EDGE01; + } + else + { + if(fB1 >= 0.0f) + fc = FC_VERTEX0; + else if(-fB1 >= fA11) + fc = FC_VERTEX2; + else + fc = FC_EDGE20; + } + } + else // region 3 + { + if(fB1 >= 0.0f) + fc = FC_VERTEX0; + else if(-fB1 >= fA11) + fc = FC_VERTEX2; + else + fc = FC_EDGE20; + } + } + else if(v < 0.0f) // region 5 + { + if(fB0 >= 0.0f) + fc = FC_VERTEX0; + else if(-fB0 >= fA00) + fc = FC_VERTEX1; + else + fc = FC_EDGE01; + } + else // region 0 + { + // minimum at interior PxVec3 + if(fDet==0.0f) + fc = FC_VERTEX0; + else + fc = FC_FACE; + } + } + else + { + PxReal fTmp0, fTmp1, fNumer, fDenom; + + if(u < 0.0f) // region 2 + { + fTmp0 = fA01 + fB0; + fTmp1 = fA11 + fB1; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + fc = FC_VERTEX1; + else + fc = FC_EDGE12; + } + else + { + if(fTmp1 <= 0.0f) + fc = FC_VERTEX2; + else if(fB1 >= 0.0f) + fc = FC_VERTEX0; + else + fc = FC_EDGE20; + } + } + else if(v < 0.0f) // region 6 + { + fTmp0 = fA01 + fB1; + fTmp1 = fA00 + fB0; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + fc = FC_VERTEX2; + else + fc = FC_EDGE12; + } + else + { + if(fTmp1 <= 0.0f) + fc = FC_VERTEX1; + else if(fB0 >= 0.0f) + fc = FC_VERTEX0; + else + fc = FC_EDGE01; + } + } + else // region 1 + { + fNumer = fA11 + fB1 - fA01 - fB0; + if(fNumer <= 0.0f) + { + fc = FC_VERTEX2; + } + else + { + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + fc = FC_VERTEX1; + else + fc = FC_EDGE12; + } + } + } + return fc; +} + + +//static bool validateVertex(PxU32 vref, const PxU32 count, const ContactPoint* PX_RESTRICT contacts, const TriangleMesh& meshData) +//{ +// PxU32 previous = 0xffffffff; +// for(PxU32 i=0;itvref1) +// Ps::swap(tvref0, tvref1); +// +// if(tvref0==vref0 && tvref1==vref1) +// return false; +// return true; +//} + +//static bool validateEdge(PxU32 vref0, PxU32 vref1, const PxU32 count, const ContactPoint* PX_RESTRICT contacts, const TriangleMesh& meshData) +//{ +// if(vref0>vref1) +// Ps::swap(vref0, vref1); +// +// PxU32 previous = 0xffffffff; +// for(PxU32 i=0;ivref1) +// Ps::swap(vref0, vref1); +// +// for(PxU32 i=0;i(mDelayedContacts.end()); + mDelayedContacts.forceSize_Unsafe(newSize); + + cd->mTriangleIndex = triangleIndex; + cd->mVerts[0] = verts[0]; + cd->mVerts[1] = verts[1]; + cd->mVerts[2] = verts[2]; + cd->mInds[0] = vertInds[0]; + cd->mInds[1] = vertInds[1]; + cd->mInds[2] = vertInds[2]; + cd->mGroupAxis = groupAxis; + cd->mGroupMinDepth = groupMinDepth; + } +} + +void ConvexMeshContactGeneration::generateLastContacts() +{ + // Process delayed contacts + PxU32 nbEntries = mDelayedContacts.size(); + if(nbEntries) + { + nbEntries /= sizeof(SavedContactData)/sizeof(PxU32); + + // PT: TODO: replicate this fix in sphere-vs-mesh ###FIX + //const PxU32 count = mContactBuffer.count; + //const ContactPoint* PX_RESTRICT contacts = mContactBuffer.contacts; + + const SavedContactData* PX_RESTRICT cd = reinterpret_cast(mDelayedContacts.begin()); + for(PxU32 i=0;i delayedContacts; + + ConvexMeshContactGenerationCallback blockCallback( + delayedContacts, + t0to1, t1to0, polyData0, world0, world1, meshData, meshData->getExtraTrigData(), meshScaling, + convexScaling, params.mContactDistance, params.mToleranceLength, + idtMeshScale, idtConvexScale, params.mMeshContactMargin, + transform0, transform1, + contactBuffer, hullOBB + ); + + Midphase::intersectOBB(meshData, hullOBB, blockCallback, false); + + blockCallback.mGeneration.generateLastContacts(); + + return blockCallback.mGeneration.mAnyHits; +} + +///////////// + +bool Gu::contactConvexMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData0; + const bool idtScaleConvex = getConvexData(shape0, convexScaling, hullAABB, polyData0); + + return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, convexScaling, meshScaling, idtScaleConvex, idtScaleMesh); +} + +bool Gu::contactBoxMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxBoxGeometry& shapeBox = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + PolygonalData polyData0; + PolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData0); + + const PxBounds3 hullAABB(-shapeBox.halfExtents, shapeBox.halfExtents); + + const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling idtScaling; + return contactHullMesh2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, meshScaling, true, idtScaleMesh); +} + +///////////// + +namespace +{ +struct ConvexVsHeightfieldContactGenerationCallback : EntityReport +{ + ConvexMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + + ConvexVsHeightfieldContactGenerationCallback( + HeightFieldUtil& hfUtil, + Ps::InlineArray& delayedContacts, + const PxTransform& t0to1, const PxTransform& t1to0, + const PolygonalData& polyData0, const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const Cm::FastVertex2ShapeScaling& convexScaling, + PxReal contactDistance, + PxReal toleranceLength, + bool idtConvexScale, + PxReal cCCDEpsilon, + const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer + ) : mGeneration(delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, contactDistance, toleranceLength, idtConvexScale, cCCDEpsilon, transform0, + transform1, contactBuffer), + mHfUtil(hfUtil) + { + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + const PxU8 nextInd[] = {2,0,1}; + + while(nb--) + { + const PxU32 triangleIndex = *indices++; + + PxU32 vertIndices[3]; + PxTriangle currentTriangle; // in world space + PxU32 adjInds[3]; + mHfUtil.getTriangle(mGeneration.mTransform1, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); + + PxVec3 normal; + currentTriangle.normal(normal); + + PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF + + for(PxU32 a = 0; a < 3; ++a) + { + if(adjInds[a] != 0xFFFFFFFF) + { + PxTriangle adjTri; + mHfUtil.getTriangle(mGeneration.mTransform1, adjTri, NULL, NULL, adjInds[a], false, false); + //We now compare the triangles to see if this edge is active + + PxVec3 adjNormal; + adjTri.denormalizedNormal(adjNormal); + PxU32 otherIndex = nextInd[a]; + PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); + if(projD < 0.f) + { + adjNormal.normalize(); + + PxF32 proj = adjNormal.dot(normal); + + if(proj < 0.999f) + { + triFlags |= 1 << (a+3); + } + } + } + else + triFlags |= (1 << (a+3)); + } + mGeneration.processTriangle(currentTriangle.verts, triangleIndex, triFlags, vertIndices); + } + return true; + } + +protected: + ConvexVsHeightfieldContactGenerationCallback &operator=(const ConvexVsHeightfieldContactGenerationCallback &); +}; +} + +static bool contactHullHeightfield2(const PolygonalData& polyData0, const PxBounds3& hullAABB, const PxHeightFieldGeometry& shape1, + const PxTransform& transform0, const PxTransform& transform1, + const NarrowPhaseParams& params, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale) +{ + //We need to create a callback that fills triangles from the HF + + HeightFieldUtil hfUtil(shape1); + + const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + + //////////////////// + + // Compute relative transforms + const PxTransform t0to1 = transform1.transformInv(transform0); + const PxTransform t1to0 = transform0.transformInv(transform1); + + Ps::InlineArray delayedContacts; + + ConvexVsHeightfieldContactGenerationCallback blockCallback(hfUtil, delayedContacts, t0to1, t1to0, polyData0, world0, world1, convexScaling, params.mContactDistance, params.mToleranceLength, + idtConvexScale, params.mMeshContactMargin, transform0, transform1, contactBuffer); + + hfUtil.overlapAABBTriangles(transform1, PxBounds3::transformFast(t0to1, hullAABB), 0, &blockCallback); + + blockCallback.mGeneration.generateLastContacts(); + + return blockCallback.mGeneration.mAnyHits; +} + +bool Gu::contactConvexHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + //Create a triangle cache from the HF triangles and then feed triangles to NP mesh methods + const physx::PxHeightFieldGeometryLL& shapeMesh = shape1.get(); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData0; + const bool idtScaleConvex = getConvexData(shape0, convexScaling, hullAABB, polyData0); + const PxVec3 inflation(params.mContactDistance); + hullAABB.minimum -= inflation; + hullAABB.maximum += inflation; + + return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, convexScaling, idtScaleConvex); +} + +bool Gu::contactBoxHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + //Create a triangle cache from the HF triangles and then feed triangles to NP mesh methods + const physx::PxHeightFieldGeometryLL& shapeMesh = shape1.get(); + + const PxBoxGeometry& shapeBox = shape0.get(); + + PolygonalData polyData0; + PolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData0); + + const PxVec3 inflatedExtents = shapeBox.halfExtents + PxVec3(params.mContactDistance); + + const PxBounds3 hullAABB = PxBounds3(-inflatedExtents, inflatedExtents); + + const Cm::FastVertex2ShapeScaling idtScaling; + + return contactHullHeightfield2(polyData0, hullAABB, shapeMesh, transform0, transform1, params, contactBuffer, idtScaling, true); +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h b/src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h new file mode 100644 index 000000000..1a73901c6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h @@ -0,0 +1,170 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONTACTMETHODIMPL_H +#define GU_CONTACTMETHODIMPL_H + +#include "foundation/PxAssert.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "collision/PxCollisionDefs.h" + +namespace physx +{ +namespace Cm +{ + class RenderOutput; +} + +namespace Gu +{ + class GeometryUnion; + class ContactBuffer; + struct NarrowPhaseParams; + class PersistentContactManifold; + class MultiplePersistentContactManifold; + + enum ManifoldFlags + { + IS_MANIFOLD = (1<<0), + IS_MULTI_MANIFOLD = (1<<1) + }; + + struct Cache : public PxCache + { + Cache() + { + } + + PX_FORCE_INLINE void setManifold(void* manifold) + { + PX_ASSERT((size_t(manifold) & 0xF) == 0); + mCachedData = reinterpret_cast(manifold); + mManifoldFlags |= IS_MANIFOLD; + } + + PX_FORCE_INLINE void setMultiManifold(void* manifold) + { + PX_ASSERT((size_t(manifold) & 0xF) == 0); + mCachedData = reinterpret_cast(manifold); + mManifoldFlags |= IS_MANIFOLD|IS_MULTI_MANIFOLD; + } + + PX_FORCE_INLINE PxU8 isManifold() const + { + return PxU8(mManifoldFlags & IS_MANIFOLD); + } + + PX_FORCE_INLINE PxU8 isMultiManifold() const + { + return PxU8(mManifoldFlags & IS_MULTI_MANIFOLD); + } + + PX_FORCE_INLINE PersistentContactManifold& getManifold() + { + PX_ASSERT(isManifold()); + PX_ASSERT(!isMultiManifold()); + PX_ASSERT((uintptr_t(mCachedData) & 0xf) == 0); + return *reinterpret_cast(mCachedData); + } + + PX_FORCE_INLINE MultiplePersistentContactManifold& getMultipleManifold() + { + PX_ASSERT(isManifold()); + PX_ASSERT(isMultiManifold()); + PX_ASSERT((uintptr_t(mCachedData) & 0xf) == 0); + return *reinterpret_cast(mCachedData); + } + }; +} + +#define GU_CONTACT_METHOD_ARGS \ + const Gu::GeometryUnion& shape0, \ + const Gu::GeometryUnion& shape1, \ + const PxTransform& transform0, \ + const PxTransform& transform1, \ + const Gu::NarrowPhaseParams& params, \ + Gu::Cache& cache, \ + Gu::ContactBuffer& contactBuffer, \ + Cm::RenderOutput* renderOutput + +namespace Gu +{ + PX_PHYSX_COMMON_API bool contactSphereSphere(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactSphereCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactSphereBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactConvexConvex(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool contactSphereMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactConvexMesh(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool contactSphereHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactCapsuleHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactBoxHeightfield(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactConvexHeightfield(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool contactSpherePlane(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactPlaneBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactPlaneCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool contactPlaneConvex(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool pcmContactSphereMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactConvexMesh(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereHeightField(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxHeightField(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactConvexHeightField(GU_CONTACT_METHOD_ARGS); + + PX_PHYSX_COMMON_API bool pcmContactPlaneCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactPlaneBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactPlaneConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereSphere(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSpherePlane(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactSphereConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactCapsuleConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxBox(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactBoxConvex(GU_CONTACT_METHOD_ARGS); + PX_PHYSX_COMMON_API bool pcmContactConvexConvex(GU_CONTACT_METHOD_ARGS); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp new file mode 100644 index 000000000..533ddb65b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp @@ -0,0 +1,128 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" +#include "CmMatrix34.h" + +#include "PsUtilities.h" +#include "foundation/PxUnionCast.h" + +namespace physx +{ +namespace Gu +{ +bool contactPlaneBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape0); + + // Get actual shape data + //const PxPlaneGeometry& shapePlane = shape.get(); + const PxBoxGeometry& shapeBox = shape1.get(); + + const PxVec3 negPlaneNormal = -transform0.q.getBasisVector0(); + + //Make sure we have a normalized plane + //PX_ASSERT(PxAbs(shape0.mNormal.magnitudeSquared() - 1.0f) < 0.000001f); + + Cm::Matrix34 boxMatrix(transform1); + Cm::Matrix34 boxToPlane(transform0.transformInv(transform1)); + + PxVec3 point; + + PX_ASSERT(contactBuffer.count==0); + +/* for(int vx=-1; vx<=1; vx+=2) + for(int vy=-1; vy<=1; vy+=2) + for(int vz=-1; vz<=1; vz+=2) + { + //point = boxToPlane.transform(PxVec3(shapeBox.halfExtents.x*vx, shapeBox.halfExtents.y*vy, shapeBox.halfExtents.z*vz)); + //PxReal planeEq = point.x; + //Optimized a bit + point.set(shapeBox.halfExtents.x*vx, shapeBox.halfExtents.y*vy, shapeBox.halfExtents.z*vz); + const PxReal planeEq = boxToPlane.m.column0.x*point.x + boxToPlane.m.column1.x*point.y + boxToPlane.m.column2.x*point.z + boxToPlane.p.x; + + if(planeEq <= contactDistance) + { + contactBuffer.contact(boxMatrix.transform(point), negPlaneNormal, planeEq); + + //no point in making more than 4 contacts. + if (contactBuffer.count >= 6) //was: 4) actually, with strong interpenetration more than just the bottom surface goes through, + //and we want to find the *deepest* 4 vertices, really. + return true; + } + }*/ + + // PT: the above code is shock full of LHS/FCMPs. And there's no point in limiting the number of contacts to 6 when the max possible is 8. + + const PxReal limit = params.mContactDistance - boxToPlane.p.x; + const PxReal dx = shapeBox.halfExtents.x; + const PxReal dy = shapeBox.halfExtents.y; + const PxReal dz = shapeBox.halfExtents.z; + const PxReal bxdx = boxToPlane.m.column0.x * dx; + const PxReal bxdy = boxToPlane.m.column1.x * dy; + const PxReal bxdz = boxToPlane.m.column2.x * dz; + + PxReal depths[8]; + depths[0] = bxdx + bxdy + bxdz - limit; + depths[1] = bxdx + bxdy - bxdz - limit; + depths[2] = bxdx - bxdy + bxdz - limit; + depths[3] = bxdx - bxdy - bxdz - limit; + depths[4] = - bxdx + bxdy + bxdz - limit; + depths[5] = - bxdx + bxdy - bxdz - limit; + depths[6] = - bxdx - bxdy + bxdz - limit; + depths[7] = - bxdx - bxdy - bxdz - limit; + + //const PxU32* binary = reinterpret_cast(depths); + const PxU32* binary = PxUnionCast(depths); + + if(binary[0] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, dy, dz)), negPlaneNormal, depths[0] + params.mContactDistance); + if(binary[1] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, dy, -dz)), negPlaneNormal, depths[1] + params.mContactDistance); + if(binary[2] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, -dy, dz)), negPlaneNormal, depths[2] + params.mContactDistance); + if(binary[3] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(dx, -dy, -dz)), negPlaneNormal, depths[3] + params.mContactDistance); + if(binary[4] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, dy, dz)), negPlaneNormal, depths[4] + params.mContactDistance); + if(binary[5] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, dy, -dz)), negPlaneNormal, depths[5] + params.mContactDistance); + if(binary[6] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, -dy, dz)), negPlaneNormal, depths[6] + params.mContactDistance); + if(binary[7] & PX_SIGN_BITMASK) + contactBuffer.contact(boxMatrix.transform(PxVec3(-dx, -dy, -dz)), negPlaneNormal, depths[7] + params.mContactDistance); + + return contactBuffer.count > 0; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp new file mode 100644 index 000000000..bfde6cf2f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp @@ -0,0 +1,80 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuInternal.h" +#include "GuSegment.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactPlaneCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape0); + + // Get actual shape data + //const PxPlaneGeometry& shapePlane = shape.get(); + const PxCapsuleGeometry& shapeCapsule = shape1.get(); + + const PxTransform capsuleToPlane = transform0.transformInv(transform1); + + //Capsule in plane space + Segment segment; + getCapsuleSegment(capsuleToPlane, shapeCapsule, segment); + + const PxVec3 negPlaneNormal = transform0.q.getBasisVector0(); + + bool contact = false; + + const PxReal separation0 = segment.p0.x - shapeCapsule.radius; + const PxReal separation1 = segment.p1.x - shapeCapsule.radius; + if(separation0 <= params.mContactDistance) + { + const PxVec3 temp(segment.p0.x - shapeCapsule.radius, segment.p0.y, segment.p0.z); + const PxVec3 point = transform0.transform(temp); + contactBuffer.contact(point, -negPlaneNormal, separation0); + contact = true; + } + + if(separation1 <= params.mContactDistance) + { + const PxVec3 temp(segment.p1.x - shapeCapsule.radius, segment.p1.y, segment.p1.z); + const PxVec3 point = transform0.transform(temp); + contactBuffer.contact(point, -negPlaneNormal, separation1); + contact = true; + } + return contact; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp new file mode 100644 index 000000000..28a84855d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp @@ -0,0 +1,101 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexMeshData.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" +#include "CmScaling.h" + + +namespace physx +{ +namespace Gu +{ +bool contactPlaneConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape0); + + // Get actual shape data + //const PxPlaneGeometry& shapePlane = shape.get(); + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + + const PxVec3* PX_RESTRICT hullVertices = shapeConvex.hullData->getHullVertices(); + PxU32 numHullVertices = shapeConvex.hullData->mNbHullVertices; +// Ps::prefetch128(hullVertices); + + // Plane is implicitly <1,0,0> 0 in localspace + Cm::Matrix34 convexToPlane (transform0.transformInv(transform1)); + PxMat33 convexToPlane_rot(convexToPlane[0], convexToPlane[1], convexToPlane[2] ); + + bool idtScale = shapeConvex.scale.isIdentity(); + Cm::FastVertex2ShapeScaling convexScaling; // PT: TODO: remove default ctor + if(!idtScale) + convexScaling.init(shapeConvex.scale); + + convexToPlane = Cm::Matrix34( convexToPlane_rot * convexScaling.getVertex2ShapeSkew(), convexToPlane[3] ); + + //convexToPlane = context.mVertex2ShapeSkew[1].getVertex2WorldSkew(convexToPlane); + + const Cm::Matrix34 planeToW (transform0); + + // This is rather brute-force + + bool status = false; + + const PxVec3 contactNormal = -planeToW.m.column0; + + while(numHullVertices--) + { + const PxVec3& vertex = *hullVertices++; +// if(numHullVertices) +// Ps::prefetch128(hullVertices); + + const PxVec3 pointInPlane = convexToPlane.transform(vertex); //TODO: this multiply could be factored out! + if(pointInPlane.x <= params.mContactDistance) + { +// const PxVec3 pointInW = planeToW.transform(pointInPlane); +// contactBuffer.contact(pointInW, -planeToW.m.column0, pointInPlane.x); + status = true; + Gu::ContactPoint* PX_RESTRICT pt = contactBuffer.contact(); + if(pt) + { + pt->normal = contactNormal; + pt->point = planeToW.transform(pointInPlane); + pt->separation = pointInPlane.x; + pt->internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + } + } + return status; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp new file mode 100644 index 000000000..740c77cf4 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp @@ -0,0 +1,861 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "GuContactPolygonPolygon.h" +#include "GuShapeConvex.h" +#include "GuContactBuffer.h" +#include "PsMathUtils.h" +#include "PsAllocator.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +#define CONTACT_REDUCTION + +/* +void gVisualizeLocalLine(const PxVec3& a, const PxVec3& b, const Cm::Matrix34& m, PxsContactManager& manager) //temp debug +{ + Cm::RenderOutput out = manager.getContext()->getRenderOutput(); + out << 0xffffff << m << Cm::RenderOutput::LINES << a << b; +} +*/ + +#ifdef CONTACT_REDUCTION +static PX_FORCE_INLINE PxReal dot2D(const PxVec3& v0, const PxVec3& v1) +{ + return v0.x * v1.x + v0.y * v1.y; +} + +static void ContactReductionAllIn( ContactBuffer& contactBuffer, PxU32 nbExistingContacts, PxU32 numIn, + const PxMat33& rotT, + const PxVec3* PX_RESTRICT vertices, const PxU8* PX_RESTRICT indices) +{ + // Number of contacts created by current call + const PxU32 nbNewContacts = contactBuffer.count - nbExistingContacts; + + if(nbNewContacts<=4) + return; // no reduction for less than 4 verts + + // We have 3 different numbers here: + // - numVerts = number of vertices in the convex polygon we're dealing with + // - numIn = number of those that were "inside" the other convex polygon (should be <= numVerts) + // - contactBuffer.count = total number of contacts *from both polygons* (that's the catch here) + // The fast path can only be chosen when the contact buffer contains all the verts from current polygon, + // i.e. when contactBuffer.count == numIn == numVerts + + Gu::ContactPoint* PX_RESTRICT ctcs = contactBuffer.contacts + nbExistingContacts; + + if(numIn == nbNewContacts) + { + // Codepath 1: all vertices generated a contact + + PxReal deepestSeparation = ctcs[0].separation; + PxU32 deepestIndex = 0; + for(PxU32 i=1; i ctcs[i].separation) + { + deepestSeparation = ctcs[i].separation; + deepestIndex = i; + } + } + + PxU32 index = 0; + const PxU32 step = (numIn<<16)>>2; // Fixed point math, don't use floats here please + bool needsExtraPoint = true; + for(PxU32 i=0;i<4;i++) + { + const PxU32 contactIndex = index>>16; + ctcs[i] = ctcs[contactIndex]; + if(contactIndex==deepestIndex) + needsExtraPoint = false; + index += step; + } + + if(needsExtraPoint) + { + ctcs[4] = ctcs[deepestIndex]; + contactBuffer.count = nbExistingContacts + 5; + } + else + { + contactBuffer.count = nbExistingContacts + 4; + } + +/* PT: TODO: investigate why this one does not work + PxU32 index = deepestIndex<<16; + const PxU32 step = (numIn<<16)>>2; // Fixed point math, don't use floats here please + for(PxU32 i=0;i<4;i++) + { + PxU32 contactIndex = index>>16; + if(contactIndex>=numIn) + contactIndex -= numIn; + ctcs[i] = ctcs[contactIndex]; + index += step; + } + contactBuffer.count = nbExistingContacts + 4;*/ + } + else + { + // Codepath 2: all vertices are "in" but only some of them generated a contact + + // WARNING: this path doesn't work when the buffer contains vertices from both polys. + + // TODO: precompute those axes + const PxU32 nbAxes = 8; + PxVec3 dirs[nbAxes]; + float angle = 0.0f; + const float angleStep = Ps::degToRad(180.0f/float(nbAxes)); + for(PxU32 i=0;imaxVariance) + { + maxVariance = variance; + bestAxis = i; + } + } + + const PxVec3 u = dirs[bestAxis]; + const PxVec3 v = PxVec3(-u.y, u.x, 0.0f); + // PxVec3(1.0f, 0.0f, 0.0f) => PxVec3(0.0f, 1.0f, 0.0f) + // PxVec3(0.0f, 1.0f, 0.0f) => PxVec3(-1.0f, 0.0f, 0.0f) + // PxVec3(-1.0f, 1.0f, 0.0f) => PxVec3(-1.0f, -1.0f, 0.0f) + // PxVec3(1.0f, 1.0f, 0.0f) => PxVec3(-1.0f, 1.0f, 0.0f) + + float dpminu = PX_MAX_F32; + float dpmaxu = -PX_MAX_F32; + float dpminv = PX_MAX_F32; + float dpmaxv = -PX_MAX_F32; + PxU32 indexMinU = 0; + PxU32 indexMaxU = 0; + PxU32 indexMinV = 0; + PxU32 indexMaxV = 0; + + for(PxU32 i=0;idpmaxu) + { + dpmaxu=dpu; + indexMaxU = i; + } + + if(dpvdpmaxv) + { + dpmaxv=dpv; + indexMaxV = i; + } + } + + if(indexMaxU == indexMinU) + indexMaxU = 0xffffffff; + if(indexMinV == indexMinU || indexMinV == indexMaxU) + indexMinV = 0xffffffff; + if(indexMaxV == indexMinU || indexMaxV == indexMaxU || indexMaxV == indexMinV) + indexMaxV = 0xffffffff; + + PxU32 newCount = 0; + for(PxU32 i=0;i0.0f && y>0.0f && z<0.0f) return TRUE; + // else return FALSE; +// return (( IR(z) & ~(IR(x)|IR(y)) ) & SIGN_BITMASK) != 0; + if(x>0.0f && y>0.0f && z<0.0f) return true; + else return false; +} + + + enum OutCode + { + OUT_XP = (1<<0), + OUT_XN = (1<<1), + OUT_YP = (1<<2), + OUT_YN = (1<<3) + }; + +static +//PX_FORCE_INLINE +bool PointInConvexPolygon2D_OutCodes(const float* PX_RESTRICT pgon2D, PxU32 numVerts, const PxReal tx, const PxReal ty, const PxReal maxX, const PxReal maxY, PxU8& outCodes) +{ + PxU32 out = 0; + if(tx<0.0f) out |= OUT_XN; + if(ty<0.0f) out |= OUT_YN; + if(tx>maxX) out |= OUT_XP; + if(ty>maxY) out |= OUT_YP; + outCodes = PxU8(out); + if(out) + return false; + + if(numVerts==3) + return pointInTriangle2D( tx, ty, + pgon2D[0], pgon2D[1], + pgon2D[2] - pgon2D[0], + pgon2D[3] - pgon2D[1], + pgon2D[4] - pgon2D[0], + pgon2D[5] - pgon2D[1]); + +#define X 0 +#define Y 1 + + const PxReal* PX_RESTRICT vtx0_ = pgon2D + (numVerts-1)*2; + const PxReal* PX_RESTRICT vtx1_ = pgon2D; + + const int* PX_RESTRICT ivtx0 = reinterpret_cast(vtx0_); + const int* PX_RESTRICT ivtx1 = reinterpret_cast(vtx1_); + //const int itx = (int&)tx; + //const int ity = (int&)ty; +// const int ity = PX_SIR(ty); + const int* tmp = reinterpret_cast(&ty); + const int ity = *tmp; + + // get test bit for above/below X axis + int yflag0 = ivtx0[Y] >= ity; + + int InsideFlag = 0; + + while(numVerts--) + { + const int yflag1 = ivtx1[Y] >= ity; + if(yflag0 != yflag1) + { + const PxReal* PX_RESTRICT vtx0 = reinterpret_cast(ivtx0); + const PxReal* PX_RESTRICT vtx1 = reinterpret_cast(ivtx1); + if( ((vtx1[Y]-ty) * (vtx0[X]-vtx1[X]) > (vtx1[X]-tx) * (vtx0[Y]-vtx1[Y])) == yflag1 ) + { + if(InsideFlag == 1) return false; + + InsideFlag++; + } + } + yflag0 = yflag1; + ivtx0 = ivtx1; + ivtx1 += 2; + } +#undef X +#undef Y + + return InsideFlag & 1; +} + +// Helper function to detect contact between two edges +PX_FORCE_INLINE bool EdgeEdgeContactSpecial(const PxVec3& v1, const PxPlane& plane, + const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, + PxReal& dist, PxVec3& ip, unsigned int i, unsigned int j, float coeff) +{ + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp > 0.0f) + return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + const PxVec3 v2 = (p4-p3); + temp = plane.n.dot(v2); + if(temp == 0.0f) // ### epsilon would be better + return false; + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i])) * coeff; + if(dist < 0.0f) + return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<0.0f) + return true; // collision found + + return false; //no collision +} + +//This one can also handle 2 vertex 'polygons' (useful for capsule surface segments) and can shift the results before contact generation. +bool Gu::contactPolygonPolygonExt( PxU32 numVerts0, const PxVec3* vertices0, const PxU8* indices0, //polygon 0 + const Cm::Matrix34& world0, const PxPlane& localPlane0, //xform of polygon 0, plane of polygon + const PxMat33& rotT0, + // + PxU32 numVerts1, const PxVec3* PX_RESTRICT vertices1, const PxU8* PX_RESTRICT indices1, //polygon 1 + const Cm::Matrix34& world1, const PxPlane& localPlane1, //xform of polygon 1, plane of polygon + const PxMat33& rotT1, + // + const PxVec3& worldSepAxis, //world normal of separating plane - this is the world space normal of polygon0!! + const Cm::Matrix34& transform0to1, const Cm::Matrix34& transform1to0, //transforms between polygons + PxU32 /*polyIndex0*/, PxU32 polyIndex1, //feature indices for contact callback + ContactBuffer& contactBuffer, + bool flipNormal, const PxVec3& posShift, PxReal sepShift) // shape order, result shift +{ + const PxVec3 n = flipNormal ? -worldSepAxis : worldSepAxis; + + PX_ASSERT(indices0 != NULL && indices1 != NULL); + + // - optimize "from to" computation + // - do the raycast case && EE tests in same space as 2D case... + // - project all edges at the same time ? + PxU32 NumIn = 0; + bool status = false; + + void* PX_RESTRICT stackMemory; + { + const PxU32 maxNumVert = PxMax(numVerts0, numVerts1); + stackMemory = PxAlloca(maxNumVert * sizeof(PxVec3)); + } + + const PxU32 size0 = numVerts0 * sizeof(bool); + bool* PX_RESTRICT flags0 = reinterpret_cast(PxAlloca(size0)); + PxU8* PX_RESTRICT outCodes0 = reinterpret_cast(PxAlloca(size0)); +// Ps::memZero(flags0, size0); +// Ps::memZero(outCodes0, size0); + + const PxU32 size1 = numVerts1 * sizeof(bool); + bool* PX_RESTRICT flags1 = reinterpret_cast(PxAlloca(size1)); + PxU8* PX_RESTRICT outCodes1 = reinterpret_cast(PxAlloca(size1)); +// Ps::memZero(flags1, size1); +// Ps::memZero(outCodes1, size1); + +#ifdef CONTACT_REDUCTION + // We want to do contact reduction on newly created contacts, not on all the already existing ones... + PxU32 nbExistingContacts = contactBuffer.count; + PxU32 nbCurrentContacts=0; + PxU8 indices[ContactBuffer::MAX_CONTACTS]; +#endif + + { + //polygon 1 + float* PX_RESTRICT verts2D = NULL; + float minX=0, minY=0; + float maxX=0, maxY=0; + + const PxVec3 localDir = -world1.rotateTranspose(worldSepAxis); //contactNormal in hull1 space + //that's redundant, its equal to -localPlane1.d + const Cm::Matrix34 t0to2D = transformTranspose(rotT1, transform0to1); //transform from hull0 to RotT + + PxReal dn = localDir.dot(localPlane1.n); //if the contactNormal == +-(normal of poly0) is NOT orthogonal to poly1 ...this is just to protect the division below. + + // PT: TODO: if "numVerts1>2" we may skip more + if (numVerts1 > 2 //no need to test whether we're 'inside' ignore capsule segments and points +// if(!(-1E-7 < dn && dn < 1E-7)) + && dn >= 1E-7f) // PT: it should never be negative so this unique test is enough + { + dn = 1.0f / dn; + const float ld1 = -localPlane1.d; // PT: unavoidable "int-to-float" LHS here, so we only want to read it once! + + // Lazy-transform vertices + if(!verts2D) + { + verts2D = reinterpret_cast(stackMemory); + //Project points + transformVertices( + minX, minY, + maxX, maxY, + verts2D, numVerts1, vertices1, indices1, rotT1); + } + + for(PxU32 i=0; i < numVerts0; i++) //for all vertices of poly0 + { + const PxVec3& p = vertices0[indices0[i]]; + const float p0_z = transformZ(p, t0to2D); //transform ith vertex of poly0 to RotT + + const PxVec3 pIn1 = transform0to1.transform(p); //transform vertex to hull1 space, in which we have the poly1 vertices. + + const PxReal dd = (p0_z - ld1) * dn; //(p0_z + localPlane1.d) is the depth of the vertex behind the triangle measured along the triangle's normal. + //we convert this to being measured along the 'contact normal' using the division. + +// if(dd < 0.0f) //if the penetrating vertex will have a penetration along the contact normal: +// PX_ASSERT(dd <= 0.0f); // PT: dn is always positive, so dd is always negative + { + float px, py; + transform2DT(px, py, pIn1 - dd*localDir, rotT1); //project vertex into poly1 plane along CONTACT NORMAL - not the polygon's normal. + + const bool res = PointInConvexPolygon2D_OutCodes(verts2D, numVerts1, px-minX, py-minY, maxX, maxY, outCodes0[i]); + flags0[i] = res; + if(res) + { + NumIn++; + + if(p0_z < ld1) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { +#ifdef CONTACT_REDUCTION + indices[nbCurrentContacts++] = indices0[i]; +#endif + ctc->normal = n; + ctc->point = world0.transform(p) + (flipNormal ? posShift : PxVec3(0.0f)); + ctc->separation = dd + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + } + } + else + { + PxMemZero(flags0, size0); + PxMemZero(outCodes0, size0); + } + + if(NumIn == numVerts0) + { + //All vertices0 are inside polygon 1 +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT0, vertices0, indices); +#endif + return status; + } + +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT0, vertices0, indices); +#endif + +#ifdef CONTACT_REDUCTION + nbExistingContacts = contactBuffer.count; + nbCurrentContacts = 0; +#endif + NumIn = 0; + verts2D = NULL; + + //Polygon 0 + const Cm::Matrix34 t1to2D = transformTranspose(rotT0, transform1to0); + + if (numVerts0 > 2) //no need to test whether we're 'inside' ignore capsule segments and points + { + const float ld0 = -localPlane0.d; // PT: unavoidable "int-to-float" LHS here, so we only want to read it once! + + // Lazy-transform vertices + if(!verts2D) + { + verts2D = reinterpret_cast(stackMemory); + //Project vertices + transformVertices( + minX, minY, + maxX, maxY, + verts2D, numVerts0, vertices0, indices0, rotT0); + } + + for(PxU32 i=0; i < numVerts1; i++) + { + const PxVec3& p = vertices1[indices1[i]]; + + float px, py; + transform2D(px, py, p, t1to2D); + + const bool res = PointInConvexPolygon2D_OutCodes(verts2D, numVerts0, px-minX, py-minY, maxX, maxY, outCodes1[i]); + flags1[i] = res; + if(res) + { + NumIn++; + + const float pz = transformZ(p, t1to2D); + if(pz < ld0) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function + + // PT: in theory, with this contact point we should use "worldSepAxis" as a contact normal. + // However we want to output the same normal for all contact points not to break friction + // patches!!! In theory again, it should be exactly the same since the contact point at + // time of impact is supposed to be the same on both bodies. In practice however, and with + // a depth-based engine, this is not the case. So the contact point here is not exactly + // right, but preserving the friction patch seems more important. + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { +#ifdef CONTACT_REDUCTION + indices[nbCurrentContacts++] = indices1[i]; +#endif + ctc->normal = n; + ctc->point = world1.transform(p) + (flipNormal ? PxVec3(0.0f) : posShift); + ctc->separation = (pz - ld0) + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + + if(NumIn == numVerts1) + { + //all vertices 1 are inside polygon 0 +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT1, vertices1, indices); +#endif + return status; + } +#ifdef CONTACT_REDUCTION + ContactReductionAllIn(contactBuffer, nbExistingContacts, NumIn, rotT1, vertices1, indices); +#endif + } + else + { + PxMemZero(flags1, size1); + PxMemZero(outCodes1, size1); + } + } + + //Edge/edge case + //Calculation done in space 0 + PxVec3* PX_RESTRICT verts1in0 = reinterpret_cast(stackMemory); + for(PxU32 i=0; i= 2 && numVerts1 >= 2)//useless if one of them is degenerate. + for(PxU32 j=0; j= numVerts1) j1 = 0; + +// if(!(flags1[j] ^ flags1[j1])) +// continue; + if(flags1[j] && flags1[j1]) + continue; + if(outCodes1[j]&outCodes1[j1]) + continue; + + const PxVec3& p0 = verts1in0[j]; + const PxVec3& p1 = verts1in0[j1]; + +// gVisualizeLocalLine(vertices1[indices1[j]], vertices1[indices1[j1]], world1, callback.getManager()); + + const PxVec3 v1 = p1-p0; + const PxVec3 planeNormal = v1.cross(localPlane0.n); + const PxPlane plane(planeNormal, -(planeNormal.dot(p0))); + + // find largest 2D plane projection + PxU32 _i, _j; + Ps::closestAxis(planeNormal, _i, _j); + + const PxReal coeff = 1.0f / (v1[_i]*localPlane0.n[_j]-v1[_j]*localPlane0.n[_i]); + + for(PxU32 i=0; i= numVerts0) i1 = 0; + +// if(!(flags0[i] ^ flags0[i1])) +// continue; + if(flags0[i] && flags0[i1]) + continue; + if(outCodes0[i]&outCodes0[i1]) + continue; + + const PxVec3& p0b = vertices0[indices0[i]]; + const PxVec3& p1b = vertices0[indices0[i1]]; + +// gVisualizeLocalLine(p0b, p1b, world0, callback.getManager()); + + PxReal dist; + PxVec3 p; + + if(EdgeEdgeContactSpecial(v1, plane, p0, p1, localPlane0.n, p0b, p1b, dist, p, _i, _j, coeff)) + { + status = true; // PT: keep this first to avoid an LHS when leaving the function +/* p = world0.transform(p); + + //contacts are generated on the edges of polygon 1 + //we only have to shift the position of polygon 1 if flipNormal is false, because + //in this case convex 0 gets passed as polygon 1, and it is convex 0 that was shifted. + if (!flipNormal) + p += posShift; + + contactBuffer.contact(p, n, -dist + sepShift, polyIndex0, polyIndex1, convexID);*/ + + Gu::ContactPoint* PX_RESTRICT ctc = contactBuffer.contact(); + if(ctc) + { + ctc->normal = n; + ctc->point = world0.transform(p) + (flipNormal ? PxVec3(0.0f) : posShift); + ctc->separation = -dist + sepShift; + ctc->internalFaceIndex1 = polyIndex1; + } + } + } + } + return status; +} diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h new file mode 100644 index 000000000..7789b5af1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONTACTPOLYGONPOLYGON_H +#define GU_CONTACTPOLYGONPOLYGON_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ + +namespace Cm +{ + class Matrix34; + class FastVertex2ShapeScaling; +} + +namespace Gu +{ +class ContactBuffer; + +PX_PHYSX_COMMON_API PxMat33 findRotationMatrixFromZ(const PxVec3& to); + +PX_PHYSX_COMMON_API bool contactPolygonPolygonExt( PxU32 numVerts0, const PxVec3* vertices0, const PxU8* indices0,//polygon 0 + const Cm::Matrix34& world0, const PxPlane& localPlane0, //xform of polygon 0, plane of polygon + const PxMat33& RotT0, + + PxU32 numVerts1, const PxVec3* vertices1, const PxU8* indices1,//polygon 1 + const Cm::Matrix34& world1, const PxPlane& localPlane1, //xform of polygon 1, plane of polygon + const PxMat33& RotT1, + + const PxVec3& worldSepAxis, //world normal of separating plane - this is the world space normal of polygon0!! + const Cm::Matrix34& transform0to1, const Cm::Matrix34& transform1to0,//transforms between polygons + PxU32 polyIndex0, PxU32 polyIndex1, //face indices for contact callback, + ContactBuffer& contactBuffer, + bool flipNormal, const PxVec3& posShift, float sepShift + ); // shape order, post gen shift. +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp new file mode 100644 index 000000000..722364c44 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp @@ -0,0 +1,181 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" + +using namespace physx; + +//This version is ported 1:1 from novodex +static PX_FORCE_INLINE bool ContactSphereBox(const PxVec3& sphereOrigin, + PxReal sphereRadius, + const PxVec3& boxExtents, +// const PxcCachedTransforms& boxCacheTransform, + const PxTransform& boxTransform, + PxVec3& point, + PxVec3& normal, + PxReal& separation, + PxReal contactDistance) +{ +// const PxTransform& boxTransform = boxCacheTransform.getShapeToWorld(); + + //returns true on contact + const PxVec3 delta = sphereOrigin - boxTransform.p; // s1.center - s2.center; + PxVec3 dRot = boxTransform.rotateInv(delta); //transform delta into OBB body coords. + + //check if delta is outside ABB - and clip the vector to the ABB. + bool outside = false; + + if (dRot.x < -boxExtents.x) + { + outside = true; + dRot.x = -boxExtents.x; + } + else if (dRot.x > boxExtents.x) + { + outside = true; + dRot.x = boxExtents.x; + } + + if (dRot.y < -boxExtents.y) + { + outside = true; + dRot.y = -boxExtents.y; + } + else if (dRot.y > boxExtents.y) + { + outside = true; + dRot.y = boxExtents.y; + } + + if (dRot.z < -boxExtents.z) + { + outside = true; + dRot.z =-boxExtents.z; + } + else if (dRot.z > boxExtents.z) + { + outside = true; + dRot.z = boxExtents.z; + } + + if (outside) //if clipping was done, sphere center is outside of box. + { + point = boxTransform.rotate(dRot); //get clipped delta back in world coords. + normal = delta - point; //what we clipped away. + const PxReal lenSquared = normal.magnitudeSquared(); + const PxReal inflatedDist = sphereRadius + contactDistance; + if (lenSquared > inflatedDist * inflatedDist) + return false; //disjoint + + //normalize to make it into the normal: + separation = PxRecipSqrt(lenSquared); + normal *= separation; + separation *= lenSquared; + //any plane that touches the sphere is tangential, so a vector from contact point to sphere center defines normal. + //we could also use point here, which has same direction. + //this is either a faceFace or a vertexFace contact depending on whether the box's face or vertex collides, but we did not distinguish. + //We'll just use vertex face for now, this info isn't really being used anyway. + //contact point is point on surface of cube closest to sphere center. + point += boxTransform.p; + separation -= sphereRadius; + return true; + } + else + { + //center is in box, we definitely have a contact. + PxVec3 locNorm; //local coords contact normal + + /*const*/ PxVec3 absdRot; + absdRot = PxVec3(PxAbs(dRot.x), PxAbs(dRot.y), PxAbs(dRot.z)); + /*const*/ PxVec3 distToSurface = boxExtents - absdRot; //dist from embedded center to box surface along 3 dimensions. + + //find smallest element of distToSurface + if (distToSurface.y < distToSurface.x) + { + if (distToSurface.y < distToSurface.z) + { + //y + locNorm = PxVec3(0.0f, dRot.y > 0.0f ? 1.0f : -1.0f, 0.0f); + separation = -distToSurface.y; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + else + { + if (distToSurface.x < distToSurface.z) + { + //x + locNorm = PxVec3(dRot.x > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f); + separation = -distToSurface.x; + } + else + { + //z + locNorm = PxVec3(0.0f,0.0f, dRot.z > 0.0f ? 1.0f : -1.0f); + separation = -distToSurface.z; + } + } + //separation so far is just the embedding of the center point; we still have to push out all of the radius. + point = sphereOrigin; + normal = boxTransform.rotate(locNorm); + separation -= sphereRadius; + return true; + } +} + +namespace physx +{ +namespace Gu +{ +bool contactSphereBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = shape0.get(); + const PxBoxGeometry& boxGeom = shape1.get(); + + PxVec3 normal; + PxVec3 point; + PxReal separation; + if(!ContactSphereBox(transform0.p, sphereGeom.radius, boxGeom.halfExtents, transform1, point, normal, separation, params.mContactDistance)) + return false; + + contactBuffer.contact(point, normal, separation); + return true; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp new file mode 100644 index 000000000..54d7c4d93 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp @@ -0,0 +1,82 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuDistancePointSegment.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuInternal.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactSphereCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = shape0.get(); + const PxCapsuleGeometry& capsuleGeom = shape1.get(); + + // PT: get capsule in local space + const PxVec3 capsuleLocalSegment = getCapsuleHalfHeightVector(transform1, capsuleGeom); + const Segment localSegment(capsuleLocalSegment, -capsuleLocalSegment); + + // PT: get sphere in capsule space + const PxVec3 sphereCenterInCapsuleSpace = transform0.p - transform1.p; + + const PxReal radiusSum = sphereGeom.radius + capsuleGeom.radius; + const PxReal inflatedSum = radiusSum + params.mContactDistance; + + // PT: compute distance between sphere center & capsule's segment + PxReal u; + const PxReal squareDist = distancePointSegmentSquared(localSegment, sphereCenterInCapsuleSpace, &u); + if(squareDist >= inflatedSum*inflatedSum) + return false; + + // PT: compute contact normal + PxVec3 normal = sphereCenterInCapsuleSpace - localSegment.getPointAt(u); + + // We do a *manual* normalization to check for singularity condition + const PxReal lenSq = normal.magnitudeSquared(); + if(lenSq==0.0f) + normal = PxVec3(1.0f, 0.0f, 0.0f); // PT: zero normal => pick up random one + else + normal *= PxRecipSqrt(lenSq); + + // PT: compute contact point + const PxVec3 point = sphereCenterInCapsuleSpace + transform1.p - normal * sphereGeom.radius; + + // PT: output unique contact + contactBuffer.contact(point, normal, PxSqrt(squareDist) - radiusSum); + return true; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp new file mode 100644 index 000000000..2c1eeea81 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp @@ -0,0 +1,614 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuDistancePointTriangle.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuGeometryUnion.h" +#include "GuFeatureCode.h" +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuBox.h" +#include "PsSort.h" + +#include "CmRenderOutput.h" + +#define DEBUG_RENDER_MESHCONTACTS 0 + +using namespace physx; +using namespace Gu; + +static const bool gDrawTouchedTriangles = false; + +static void outputErrorMessage() +{ +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Dropping contacts in sphere vs mesh: exceeded limit of 64 "); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: a customized version that also returns the feature code +static PxVec3 closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t, FeatureCode& fc) +{ + // Check if P in vertex region outside A + const PxVec3 ab = b - a; + const PxVec3 ac = c - a; + const PxVec3 ap = p - a; + const float d1 = ab.dot(ap); + const float d2 = ac.dot(ap); + if(d1<=0.0f && d2<=0.0f) + { + s = 0.0f; + t = 0.0f; + fc = FC_VERTEX0; + return a; // Barycentric coords 1,0,0 + } + + // Check if P in vertex region outside B + const PxVec3 bp = p - b; + const float d3 = ab.dot(bp); + const float d4 = ac.dot(bp); + if(d3>=0.0f && d4<=d3) + { + s = 1.0f; + t = 0.0f; + fc = FC_VERTEX1; + return b; // Barycentric coords 0,1,0 + } + + // Check if P in edge region of AB, if so return projection of P onto AB + const float vc = d1*d4 - d3*d2; + if(vc<=0.0f && d1>=0.0f && d3<=0.0f) + { + const float v = d1 / (d1 - d3); + s = v; + t = 0.0f; + fc = FC_EDGE01; + return a + v * ab; // barycentric coords (1-v, v, 0) + } + + // Check if P in vertex region outside C + const PxVec3 cp = p - c; + const float d5 = ab.dot(cp); + const float d6 = ac.dot(cp); + if(d6>=0.0f && d5<=d6) + { + s = 0.0f; + t = 1.0f; + fc = FC_VERTEX2; + return c; // Barycentric coords 0,0,1 + } + + // Check if P in edge region of AC, if so return projection of P onto AC + const float vb = d5*d2 - d1*d6; + if(vb<=0.0f && d2>=0.0f && d6<=0.0f) + { + const float w = d2 / (d2 - d6); + s = 0.0f; + t = w; + fc = FC_EDGE20; + return a + w * ac; // barycentric coords (1-w, 0, w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + const float va = d3*d6 - d5*d4; + if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f) + { + const float w = (d4-d3) / ((d4 - d3) + (d5-d6)); + s = 1.0f-w; + t = w; + fc = FC_EDGE12; + return b + w * (c-b); // barycentric coords (0, 1-w, w) + } + + // P inside face region. Compute Q through its barycentric coords (u,v,w) + const float denom = 1.0f / (va + vb + vc); + const float v = vb * denom; + const float w = vc * denom; + s = v; + t = w; + fc = FC_FACE; + return a + ab*v + ac*w; +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: we use a separate structure to make sorting faster +struct SortKey +{ + float mSquareDist; + PxU32 mIndex; + + PX_FORCE_INLINE bool operator < (const SortKey& data) const + { + return mSquareDist < data.mSquareDist; + } +}; + +struct TriangleData +{ + PxVec3 mDelta; + FeatureCode mFC; + PxU32 mTriangleIndex; + PxU32 mVRef[3]; +}; + +struct CachedTriangleIndices +{ + PxU32 mVRef[3]; +}; + +static PX_FORCE_INLINE bool validateSquareDist(PxReal squareDist) +{ + return squareDist>0.0001f; +} + +static bool validateEdge(PxU32 vref0, PxU32 vref1, const CachedTriangleIndices* cachedTris, PxU32 nbCachedTris) +{ + while(nbCachedTris--) + { + const CachedTriangleIndices& inds = *cachedTris++; + const PxU32 vi0 = inds.mVRef[0]; + const PxU32 vi1 = inds.mVRef[1]; + const PxU32 vi2 = inds.mVRef[2]; + + if(vi0==vref0) + { + if(vi1==vref1 || vi2==vref1) + return false; + } + else if(vi1==vref0) + { + if(vi0==vref1 || vi2==vref1) + return false; + } + else if(vi2==vref0) + { + if(vi1==vref1 || vi0==vref1) + return false; + } + } + return true; +} + +static bool validateVertex(PxU32 vref, const CachedTriangleIndices* cachedTris, PxU32 nbCachedTris) +{ + while(nbCachedTris--) + { + const CachedTriangleIndices& inds = *cachedTris++; + if(inds.mVRef[0]==vref || inds.mVRef[1]==vref || inds.mVRef[2]==vref) + return false; + } + return true; +} + +namespace +{ + class NullAllocator + { + public: + PX_FORCE_INLINE NullAllocator() { } + PX_FORCE_INLINE void* allocate(size_t, const char*, int) { return NULL; } + PX_FORCE_INLINE void deallocate(void*) { } + }; + +struct SphereMeshContactGeneration +{ + const PxSphereGeometry& mShapeSphere; + const PxTransform& mTransform0; + const PxTransform& mTransform1; + ContactBuffer& mContactBuffer; + const PxVec3& mSphereCenterShape1Space; + PxF32 mInflatedRadius2; + PxU32 mNbDelayed; + TriangleData mSavedData[ContactBuffer::MAX_CONTACTS]; + SortKey mSortKey[ContactBuffer::MAX_CONTACTS]; + PxU32 mNbCachedTris; + CachedTriangleIndices mCachedTris[ContactBuffer::MAX_CONTACTS]; + Cm::RenderOutput* mRenderOutput; + + SphereMeshContactGeneration(const PxSphereGeometry& shapeSphere, const PxTransform& transform0, const PxTransform& transform1, + ContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, + Cm::RenderOutput* renderOutput) : + mShapeSphere (shapeSphere), + mTransform0 (transform0), + mTransform1 (transform1), + mContactBuffer (contactBuffer), + mSphereCenterShape1Space (sphereCenterShape1Space), + mInflatedRadius2 (inflatedRadius*inflatedRadius), + mNbDelayed (0), + mNbCachedTris (0), + mRenderOutput (renderOutput) + { + } + + PX_FORCE_INLINE void cacheTriangle(PxU32 ref0, PxU32 ref1, PxU32 ref2) + { + const PxU32 nb = mNbCachedTris++; + mCachedTris[nb].mVRef[0] = ref0; + mCachedTris[nb].mVRef[1] = ref1; + mCachedTris[nb].mVRef[2] = ref2; + } + + PX_FORCE_INLINE void addContact(const PxVec3& d, PxReal squareDist, PxU32 triangleIndex) + { + float dist; + PxVec3 delta; + if(validateSquareDist(squareDist)) + { + // PT: regular contact. Normalize 'delta'. + dist = PxSqrt(squareDist); + delta = d / dist; + } + else + { + // PT: singular contact: 'd' is the non-unit triangle's normal in this case. + dist = 0.0f; + delta = -d.getNormalized(); + } + + const PxVec3 worldNormal = -mTransform1.rotate(delta); + + const PxVec3 localHit = mSphereCenterShape1Space + mShapeSphere.radius*delta; + const PxVec3 hit = mTransform1.transform(localHit); + + if(!mContactBuffer.contact(hit, worldNormal, dist - mShapeSphere.radius, triangleIndex)) + outputErrorMessage(); + } + + void processTriangle(PxU32 triangleIndex, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, const PxU32* vertInds) + { + // PT: compute closest point between sphere center and triangle + PxReal u, v; + FeatureCode fc; + const PxVec3 cp = closestPtPointTriangle(mSphereCenterShape1Space, v0, v1, v2, u, v, fc); + + // PT: compute 'delta' vector between closest point and sphere center + const PxVec3 delta = cp - mSphereCenterShape1Space; + const PxReal squareDist = delta.magnitudeSquared(); + if(squareDist >= mInflatedRadius2) + return; + + // PT: backface culling without the normalize + // PT: TODO: consider doing before the pt-triangle distance test if it's cheaper + // PT: TODO: e0/e1 already computed in closestPtPointTriangle + const PxVec3 e0 = v1 - v0; + const PxVec3 e1 = v2 - v0; + const PxVec3 planeNormal = e0.cross(e1); + const PxF32 planeD = planeNormal.dot(v0); // PT: actually -d compared to PxcPlane + if(planeNormal.dot(mSphereCenterShape1Space) < planeD) + return; + + // PT: for a regular contact, 'delta' is non-zero (and so is 'squareDist'). However when the sphere's center exactly touches + // the triangle, then both 'delta' and 'squareDist' become zero. This needs to be handled as a special case to avoid dividing + // by zero. We will use the triangle's normal as a contact normal in this special case. + // + // 'validateSquareDist' is called twice because there are conflicting goals here. We could call it once now and already + // compute the proper data for generating the contact. But this would mean doing a square-root and a division right here, + // even when the contact is not actually needed in the end. We could also call it only once in "addContact', but the plane's + // normal would not always be available (in case of delayed contacts), and thus it would need to be either recomputed (slower) + // or stored within 'TriangleData' (using more memory). Calling 'validateSquareDist' twice is a better option overall. + PxVec3 d; + if(validateSquareDist(squareDist)) + d = delta; + else + d = planeNormal; + + if(fc==FC_FACE) + { + addContact(d, squareDist, triangleIndex); + + if(mNbCachedTrismDelta = d; + saved->mVRef[0] = vertInds[0]; + saved->mVRef[1] = vertInds[1]; + saved->mVRef[2] = vertInds[2]; + saved->mFC = fc; + saved->mTriangleIndex = triangleIndex; + } + else outputErrorMessage(); + } + } + + void generateLastContacts() + { + const PxU32 count = mNbDelayed; + if(!count) + return; + + Ps::sort(mSortKey, count, Ps::Less(), NullAllocator(), ContactBuffer::MAX_CONTACTS); + + TriangleData* touchedTris = mSavedData; + for(PxU32 i=0;i +{ + SphereMeshContactGeneration mGeneration; + const TriangleMesh& mMeshData; + + SphereMeshContactGenerationCallback_NoScale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, const PxTransform& transform1, ContactBuffer& contactBuffer, + const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, Cm::RenderOutput* renderOutput + ) : MeshHitCallback (CallbackMode::eMULTIPLE), + mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput), + mMeshData (meshData) + { + } + + virtual ~SphereMeshContactGenerationCallback_NoScale() + { + mGeneration.generateLastContacts(); + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + if(gDrawTouchedTriangles) + { + (*mGeneration.mRenderOutput) << 0xffffffff; + (*mGeneration.mRenderOutput) << PxMat44(PxIdentity); + const PxVec3 wp0 = mGeneration.mTransform1.transform(v0); + const PxVec3 wp1 = mGeneration.mTransform1.transform(v1); + const PxVec3 wp2 = mGeneration.mTransform1.transform(v2); + mGeneration.mRenderOutput->outputSegment(wp0, wp1); + mGeneration.mRenderOutput->outputSegment(wp1, wp2); + mGeneration.mRenderOutput->outputSegment(wp2, wp0); + } + + mGeneration.processTriangle(hit.faceIndex, v0, v1, v2, vinds); + return true; + } + +protected: + SphereMeshContactGenerationCallback_NoScale &operator=(const SphereMeshContactGenerationCallback_NoScale &); +}; + +struct SphereMeshContactGenerationCallback_Scale : SphereMeshContactGenerationCallback_NoScale +{ + const Cm::FastVertex2ShapeScaling& mMeshScaling; + + SphereMeshContactGenerationCallback_Scale(const TriangleMesh& meshData, const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, const PxTransform& transform1, const Cm::FastVertex2ShapeScaling& meshScaling, + ContactBuffer& contactBuffer, const PxVec3& sphereCenterShape1Space, PxF32 inflatedRadius, Cm::RenderOutput* renderOutput + ) : SphereMeshContactGenerationCallback_NoScale(meshData, shapeSphere, + transform0, transform1, contactBuffer, sphereCenterShape1Space, inflatedRadius, renderOutput), + mMeshScaling (meshScaling) + { + } + + virtual ~SphereMeshContactGenerationCallback_Scale() {} + + virtual PxAgain processHit(const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + PxVec3 verts[3]; + getScaledVertices(verts, v0, v1, v2, false, mMeshScaling); + + if(gDrawTouchedTriangles) + { + (*mGeneration.mRenderOutput) << 0xffffffff; + (*mGeneration.mRenderOutput) << PxMat44(PxIdentity); + const PxVec3 wp0 = mGeneration.mTransform1.transform(verts[0]); + const PxVec3 wp1 = mGeneration.mTransform1.transform(verts[1]); + const PxVec3 wp2 = mGeneration.mTransform1.transform(verts[2]); + mGeneration.mRenderOutput->outputSegment(wp0, wp1); + mGeneration.mRenderOutput->outputSegment(wp1, wp2); + mGeneration.mRenderOutput->outputSegment(wp2, wp0); + } + + mGeneration.processTriangle(hit.faceIndex, verts[0], verts[1], verts[2], vinds); + return true; + } +protected: + SphereMeshContactGenerationCallback_Scale &operator=(const SphereMeshContactGenerationCallback_Scale &); +}; + +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Gu::contactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + // We must be in local space to use the cache + const PxVec3 sphereCenterInMeshSpace = transform1.transformInv(transform0.p); + const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + const TriangleMesh* meshData = shapeMesh.meshData; + + // mesh scale is not baked into cached verts + if(shapeMesh.scale.isIdentity()) + { + SphereMeshContactGenerationCallback_NoScale callback( + *meshData, shapeSphere, transform0, transform1, + contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + // PT: TODO: switch to sphere query here + const Box obb(sphereCenterInMeshSpace, PxVec3(inflatedRadius), PxMat33(PxIdentity)); + Midphase::intersectOBB(meshData, obb, callback, true); + } + else + { + const Cm::FastVertex2ShapeScaling meshScaling(shapeMesh.scale); + + SphereMeshContactGenerationCallback_Scale callback( + *meshData, shapeSphere, transform0, transform1, + meshScaling, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + PxVec3 obbCenter = sphereCenterInMeshSpace; + PxVec3 obbExtents = PxVec3(inflatedRadius); + PxMat33 obbRot(PxIdentity); + meshScaling.transformQueryBounds(obbCenter, obbExtents, obbRot); + + const Box obb(obbCenter, obbExtents, obbRot); + + Midphase::intersectOBB(meshData, obb, callback, true); + } + return contactBuffer.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +namespace +{ +struct SphereHeightfieldContactGenerationCallback : EntityReport +{ + SphereMeshContactGeneration mGeneration; + HeightFieldUtil& mHfUtil; + + SphereHeightfieldContactGenerationCallback( + HeightFieldUtil& hfUtil, + const PxSphereGeometry& shapeSphere, + const PxTransform& transform0, + const PxTransform& transform1, + ContactBuffer& contactBuffer, + const PxVec3& sphereCenterInMeshSpace, + PxF32 inflatedRadius, + Cm::RenderOutput* renderOutput + ) : + mGeneration (shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput), + mHfUtil (hfUtil) + { + } + + // PT: TODO: refactor/unify with similar code in other places + virtual bool onEvent(PxU32 nb, PxU32* indices) + { + while(nb--) + { + const PxU32 triangleIndex = *indices++; + PxU32 vertIndices[3]; + PxTriangle currentTriangle; + mHfUtil.getTriangle(mGeneration.mTransform1, currentTriangle, vertIndices, NULL, triangleIndex, false, false); + + mGeneration.processTriangle(triangleIndex, currentTriangle.verts[0], currentTriangle.verts[1], currentTriangle.verts[2], vertIndices); + } + return true; + } +protected: + SphereHeightfieldContactGenerationCallback &operator=(const SphereHeightfieldContactGenerationCallback &); +}; +} + +bool Gu::contactSphereHeightfield(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxHeightFieldGeometryLL& shapeMesh = shape1.get(); + + HeightFieldUtil hfUtil(shapeMesh); + + const PxVec3 sphereCenterInMeshSpace = transform1.transformInv(transform0.p); + const PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + const PxVec3 inflatedRV3(inflatedRadius); + + const PxBounds3 bounds(sphereCenterInMeshSpace - inflatedRV3, sphereCenterInMeshSpace + inflatedRV3); + + SphereHeightfieldContactGenerationCallback blockCallback(hfUtil, shapeSphere, transform0, transform1, contactBuffer, sphereCenterInMeshSpace, inflatedRadius, renderOutput); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &blockCallback); + + blockCallback.mGeneration.generateLastContacts(); + + return contactBuffer.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp new file mode 100644 index 000000000..470b2d586 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactSpherePlane(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape1); + + // Get actual shape data + const PxSphereGeometry& shapeSphere = shape0.get(); + //const PxPlaneGeometry& shapePlane = shape1.get(); + + //Sphere in plane space + const PxVec3 sphere = transform1.transformInv(transform0.p); + + //Make sure we have a normalized plane + //The plane is implicitly n=<1,0,0> d=0 (in plane-space) + //PX_ASSERT(PxAbs(shape1.mNormal.magnitudeSquared() - 1.0f) < 0.000001f); + + //Separation + const PxReal separation = sphere.x - shapeSphere.radius; + + if(separation<=params.mContactDistance) + { + const PxVec3 normal = transform1.q.getBasisVector0(); + const PxVec3 point = transform0.p - normal * shapeSphere.radius; + contactBuffer.contact(point, normal, separation); + return true; + } + return false; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp new file mode 100644 index 000000000..d8ffdb4b9 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "GuGeometryUnion.h" + +namespace physx +{ +namespace Gu +{ +bool contactSphereSphere(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom0 = shape0.get(); + const PxSphereGeometry& sphereGeom1 = shape1.get(); + + PxVec3 delta = transform0.p - transform1.p; + + const PxReal distanceSq = delta.magnitudeSquared(); + const PxReal radiusSum = sphereGeom0.radius + sphereGeom1.radius; + const PxReal inflatedSum = radiusSum + params.mContactDistance; + if(distanceSq >= inflatedSum*inflatedSum) + return false; + + // We do a *manual* normalization to check for singularity condition + const PxReal magn = PxSqrt(distanceSq); + if(magn<=0.00001f) + delta = PxVec3(1.0f, 0.0f, 0.0f); // PT: spheres are exactly overlapping => can't create normal => pick up random one + else + delta *= 1.0f/magn; + + // PT: TODO: why is this formula different from the original code? + const PxVec3 contact = delta * ((sphereGeom0.radius + magn - sphereGeom1.radius)*-0.5f) + transform0.p; + + contactBuffer.contact(contact, delta, magn - radiusSum); + return true; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp new file mode 100644 index 000000000..d0bc0ecf1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp @@ -0,0 +1,128 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexEdgeFlags.h" +#include "GuFeatureCode.h" + +using namespace physx; +using namespace Gu; + +static FeatureCode computeFeatureCode(PxReal u, PxReal v) +{ + // Analysis + if(u==0.0f) + { + if(v==0.0f) + { + // Vertex 0 + return FC_VERTEX0; + } + else if(v==1.0f) + { + // Vertex 2 + return FC_VERTEX2; + } + else + { + // Edge 0-2 + return FC_EDGE20; + } + } + else if(u==1.0f) + { + if(v==0.0f) + { + // Vertex 1 + return FC_VERTEX1; + } + } + else + { + if(v==0.0f) + { + // Edge 0-1 + return FC_EDGE01; + } + else + { + if((u+v)>=0.9999f) + { + // Edge 1-2 + return FC_EDGE12; + } + else + { + // Face + return FC_FACE; + } + } + } + return FC_UNDEFINED; +} + + +bool Gu::selectNormal(PxU8 data, PxReal u, PxReal v) +{ + bool useFaceNormal = false; + const FeatureCode FC = computeFeatureCode(u, v); + switch(FC) + { + case FC_VERTEX0: + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_20))) + useFaceNormal = true; + break; + case FC_VERTEX1: + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_12))) + useFaceNormal = true; + break; + case FC_VERTEX2: + if(!(data & (Gu::ETD_CONVEX_EDGE_12|Gu::ETD_CONVEX_EDGE_20))) + useFaceNormal = true; + break; + case FC_EDGE01: + if(!(data & Gu::ETD_CONVEX_EDGE_01)) + useFaceNormal = true; + break; + case FC_EDGE12: + if(!(data & Gu::ETD_CONVEX_EDGE_12)) + useFaceNormal = true; + break; + case FC_EDGE20: + if(!(data & Gu::ETD_CONVEX_EDGE_20)) + useFaceNormal = true; + break; + case FC_FACE: + useFaceNormal = true; + break; + case FC_UNDEFINED: + break; + }; + return useFaceNormal; +} + diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h new file mode 100644 index 000000000..ab1e0cb8e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h @@ -0,0 +1,56 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_FEATURE_CODE_H +#define GU_FEATURE_CODE_H + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + enum FeatureCode + { + FC_VERTEX0, + FC_VERTEX1, + FC_VERTEX2, + FC_EDGE01, + FC_EDGE12, + FC_EDGE20, + FC_FACE, + + FC_UNDEFINED + }; + + bool selectNormal(PxU8 data, PxReal u, PxReal v); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp new file mode 100644 index 000000000..52e94931d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp @@ -0,0 +1,203 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "PsUserAllocated.h" +#include "GuSerialize.h" +#include "GuBigConvexData2.h" +#include "GuCubeIndex.h" +#include "PsIntrinsics.h" +#include "CmUtils.h" +#include "PsUtilities.h" +#include "PsAllocator.h" + +using namespace physx; +using namespace Gu; + +BigConvexData::BigConvexData() : mVBuffer(NULL) +{ + mData.mSubdiv = 0; + mData.mNbSamples = 0; + mData.mSamples = NULL; + + ////// + + mData.mNbVerts = 0; + mData.mNbAdjVerts = 0; + mData.mValencies = NULL; + mData.mAdjacentVerts = NULL; +} + +BigConvexData::~BigConvexData() +{ + PX_FREE(mData.mSamples); + + /////////// + + if(mVBuffer) + { + PX_FREE(mVBuffer); + } + else + { + // Allocated from somewhere else!! + PX_FREE(mData.mValencies); + PX_FREE(mData.mAdjacentVerts); + } +} + +void BigConvexData::CreateOffsets() +{ + // Create offsets (radix style) + mData.mValencies[0].mOffset = 0; + for(PxU32 i=1;i(mVBuffer); + mData.mAdjacentVerts = (reinterpret_cast(mVBuffer)) + sizeof(Gu::Valency)*numVerts; + + PX_ASSERT(0 == (size_t(mData.mAdjacentVerts) & 0xf)); + PX_ASSERT(Version==2); + + { + PxU16* temp = reinterpret_cast(mData.mValencies); + + PxU32 MaxIndex = readDword(Mismatch, stream); + ReadIndices(Ps::to16(MaxIndex), mData.mNbVerts, temp, stream, Mismatch); + + // We transform from: + // + // |5555|4444|3333|2222|1111|----|----|----|----|----| + // + // to: + // + // |5555|4444|4444|2222|3333|----|2222|----|1111|----| + // + for(PxU32 i=0;i(PX_ALLOC(sizeof(PxU8)*mData.mNbSamples*2, "BigConvex Samples Data")); + + // These byte buffers shouldn't need converting + stream.read(mData.mSamples, sizeof(PxU8)*mData.mNbSamples*2); + + //load the valencies + return VLoad(stream); +} + +// PX_SERIALIZATION +void BigConvexData::exportExtraData(PxSerializationContext& stream) +{ + if(mData.mSamples) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData.mSamples, sizeof(PxU8)*mData.mNbSamples*2); + } + + if(mData.mValencies) + { + stream.alignData(PX_SERIAL_ALIGN); + PxU32 numVerts = (mData.mNbVerts+3)&~3; + const PxU32 TotalSize = sizeof(Gu::Valency)*numVerts + sizeof(PxU8)*mData.mNbAdjVerts; + stream.writeData(mData.mValencies, TotalSize); + } +} + +void BigConvexData::importExtraData(PxDeserializationContext& context) +{ + if(mData.mSamples) + mData.mSamples = context.readExtraData(PxU32(mData.mNbSamples*2)); + + if(mData.mValencies) + { + context.alignExtraData(); + PxU32 numVerts = (mData.mNbVerts+3)&~3; + mData.mValencies = context.readExtraData(numVerts); + mData.mAdjacentVerts = context.readExtraData(mData.mNbAdjVerts); + + } +} +//~PX_SERIALIZATION + diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h new file mode 100644 index 000000000..90af247ec --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BIG_CONVEX_DATA_H +#define GU_BIG_CONVEX_DATA_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + +class BigConvexDataBuilder; +class PxcHillClimb; +class BigConvexData; + +// Data + +namespace Gu +{ + +struct Valency +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PxU16 mCount; + PxU16 mOffset; +}; +PX_COMPILE_TIME_ASSERT(sizeof(Gu::Valency) == 4); + +struct BigConvexRawData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + // Support vertex map + PxU16 mSubdiv; // "Gaussmap" subdivision + PxU16 mNbSamples; // Total #samples in gaussmap PT: this is not even needed at runtime! + + PxU8* mSamples; + PX_FORCE_INLINE const PxU8* getSamples2() const + { + return mSamples + mNbSamples; + } + //~Support vertex map + + // Valencies data + PxU32 mNbVerts; //!< Number of vertices + PxU32 mNbAdjVerts; //!< Total number of adjacent vertices ### PT: this is useless at runtime and should not be stored here + Gu::Valency* mValencies; //!< A list of mNbVerts valencies (= number of neighbors) + PxU8* mAdjacentVerts; //!< List of adjacent vertices + //~Valencies data +}; +#if PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(sizeof(Gu::BigConvexRawData) == 40); +#else +PX_COMPILE_TIME_ASSERT(sizeof(Gu::BigConvexRawData) == 24); +#endif + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h new file mode 100644 index 000000000..9fc5d717f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BIG_CONVEX_DATA2_H +#define GU_BIG_CONVEX_DATA2_H + +#include "GuBigConvexData.h" +#include "PxMetaData.h" + +namespace physx +{ + class PxSerializationContext; + class PxDeserializationContext; + + class PX_PHYSX_COMMON_API BigConvexData : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + BigConvexData(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + BigConvexData(); + ~BigConvexData(); + // Support vertex map + bool Load(PxInputStream& stream); + + PxU32 ComputeOffset(const PxVec3& dir) const; + PxU32 ComputeNearestOffset(const PxVec3& dir) const; + + // Data access + PX_INLINE PxU32 GetSubdiv() const { return mData.mSubdiv; } + PX_INLINE PxU32 GetNbSamples() const { return mData.mNbSamples; } + //~Support vertex map + + // Valencies + // Data access + PX_INLINE PxU32 GetNbVerts() const { return mData.mNbVerts; } + PX_INLINE const Gu::Valency* GetValencies() const { return mData.mValencies; } + PX_INLINE PxU16 GetValency(PxU32 i) const { return mData.mValencies[i].mCount; } + PX_INLINE PxU16 GetOffset(PxU32 i) const { return mData.mValencies[i].mOffset; } + PX_INLINE const PxU8* GetAdjacentVerts() const { return mData.mAdjacentVerts; } + + PX_INLINE PxU16 GetNbNeighbors(PxU32 i) const { return mData.mValencies[i].mCount; } + PX_INLINE const PxU8* GetNeighbors(PxU32 i) const { return &mData.mAdjacentVerts[mData.mValencies[i].mOffset]; } + +// PX_SERIALIZATION + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); +//~PX_SERIALIZATION + Gu::BigConvexRawData mData; + protected: + void* mVBuffer; + // Internal methods + void CreateOffsets(); + bool VLoad(PxInputStream& stream); + //~Valencies + friend class BigConvexDataBuilder; + }; + +} + +#endif // BIG_CONVEX_DATA_H + diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h new file mode 100644 index 000000000..9756e3de6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h @@ -0,0 +1,59 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_EDGE_FLAGS_H +#define GU_CONVEX_EDGE_FLAGS_H + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + enum ExtraTrigDataFlag + { + ETD_SILHOUETTE_EDGE_01 = (1 << 0), //First edge is a silhouette edge + ETD_SILHOUETTE_EDGE_12 = (1 << 1), //Second edge is a silhouette edge + ETD_SILHOUETTE_EDGE_20 = (1 << 2), //Third edge is a silhouette edge + ETD_CONVEX_EDGE_01 = (1<<3), // PT: important value, don't change + ETD_CONVEX_EDGE_12 = (1<<4), // PT: important value, don't change + ETD_CONVEX_EDGE_20 = (1<<5), // PT: important value, don't change + + ETD_CONVEX_EDGE_ALL = ETD_CONVEX_EDGE_01|ETD_CONVEX_EDGE_12|ETD_CONVEX_EDGE_20 + }; + + // PT: helper function to make sure we use the proper default flags everywhere + PX_FORCE_INLINE PxU8 getConvexEdgeFlags(const PxU8* extraTrigData, PxU32 triangleIndex) + { + return extraTrigData ? extraTrigData[triangleIndex] : PxU8(ETD_CONVEX_EDGE_ALL); + } +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp new file mode 100644 index 000000000..53fee1171 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp @@ -0,0 +1,137 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexHelper.h" +#include "GuGeometryUnion.h" +#include "GuInternal.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +// PT: we can't call alloca in a function and we want to avoid defines or duplicating the code. This makes it a bit tricky to write. +void Gu::getScaledConvex( PxVec3*& scaledVertices, PxU8*& scaledIndices, PxVec3* dstVertices, PxU8* dstIndices, + bool idtConvexScale, const PxVec3* srcVerts, const PxU8* srcIndices, PxU32 nbVerts, const Cm::FastVertex2ShapeScaling& convexScaling) +{ + //pretransform convex polygon if we have scaling! + if(idtConvexScale) // PT: the scale is always 1 for boxes so no need to test the type + { + scaledVertices = const_cast(srcVerts); + scaledIndices = const_cast(srcIndices); + } + else + { + scaledIndices = dstIndices; + scaledVertices = dstVertices; + for(PxU32 i=0; i(); + + const bool idtScale = shapeConvex.scale.isIdentity(); + if(!idtScale) + scaling.init(shapeConvex.scale); + + // PT: this version removes all the FCMPs and almost all LHS. This is temporary until + // the legacy 3x3 matrix totally vanishes but meanwhile do NOT do useless matrix conversions, + // it's a perfect recipe for LHS. + PX_ASSERT(!shapeConvex.hullData->mAABB.isEmpty()); + bounds = shapeConvex.hullData->mAABB.transformFast(scaling.getVertex2ShapeSkew()); + + getPolygonalData_Convex(&polyData, shapeConvex.hullData, scaling); + + // PT: non-uniform scaling invalidates the "internal objects" optimization, since our internal sphere + // might become an ellipsoid or something. Just disable the optimization if scaling is used... + if(!idtScale) + polyData.mInternal.reset(); + + return idtScale; +} + +PxU32 Gu::findUniqueConvexEdges(PxU32 maxNbEdges, ConvexEdge* PX_RESTRICT edges, PxU32 numPolygons, const Gu::HullPolygonData* PX_RESTRICT polygons, const PxU8* PX_RESTRICT vertexData) +{ + PxU32 nbEdges = 0; + + while(numPolygons--) + { + const HullPolygonData& polygon = *polygons++; + const PxU8* vRefBase = vertexData + polygon.mVRef8; + PxU32 numEdges = polygon.mNbVerts; + + PxU32 a = numEdges - 1; + PxU32 b = 0; + while(numEdges--) + { + PxU8 vi0 = vRefBase[a]; + PxU8 vi1 = vRefBase[b]; + + if(vi1 < vi0) + { + PxU8 tmp = vi0; + vi0 = vi1; + vi1 = tmp; + } + + bool found=false; + for(PxU32 i=0;i(PxAlloca(nbVerts * sizeof(PxVec3))), \ + idtScaling ? NULL : reinterpret_cast(PxAlloca(nbVerts * sizeof(PxU8))), \ + idtScaling, srcVerts, srcIndices, nbVerts, scaling); + + PX_PHYSX_COMMON_API bool getConvexData(const Gu::GeometryUnion& shape, Cm::FastVertex2ShapeScaling& scaling, PxBounds3& bounds, PolygonalData& polyData); + + struct ConvexEdge + { + PxU8 vref0; + PxU8 vref1; + PxVec3 normal; // warning: non-unit vector! + }; + + PX_PHYSX_COMMON_API PxU32 findUniqueConvexEdges(PxU32 maxNbEdges, ConvexEdge* PX_RESTRICT edges, PxU32 numPolygons, const Gu::HullPolygonData* PX_RESTRICT polygons, const PxU8* PX_RESTRICT vertexData); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp new file mode 100644 index 000000000..feb66f8da --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp @@ -0,0 +1,415 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVisualizationParameter.h" +#include "PsIntrinsics.h" +#include "CmPhysXCommon.h" +#include "CmRenderOutput.h" +#include "PsMathUtils.h" +#include "GuConvexMesh.h" +#include "GuTriangle32.h" +#include "GuBigConvexData2.h" +#include "GuSerialize.h" +#include "GuMeshFactory.h" +#include "CmUtils.h" +#include "PxMeshScale.h" +#include "PsAllocator.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + +// PX_SERIALIZATION +#include "PsIntrinsics.h" +//~PX_SERIALIZATION + +bool Gu::ConvexMesh::getPolygonData(PxU32 i, PxHullPolygon& data) const +{ + if(i>=mHullData.mNbPolygons) + return false; + + const HullPolygonData& poly = mHullData.mPolygons[i]; + data.mPlane[0] = poly.mPlane.n.x; + data.mPlane[1] = poly.mPlane.n.y; + data.mPlane[2] = poly.mPlane.n.z; + data.mPlane[3] = poly.mPlane.d; + data.mNbVerts = poly.mNbVerts; + data.mIndexBase = poly.mVRef8; + return true; +} + +/// ====================================== + +static void initConvexHullData(Gu::ConvexHullData& data) +{ + data.mAABB.setEmpty(); + data.mCenterOfMass = PxVec3(0); + data.mNbEdges = PxBitAndWord(); + data.mNbHullVertices = 0; + data.mNbPolygons = 0; + data.mPolygons = NULL; + data.mBigConvexRawData = NULL; + data.mInternal.mRadius = 0.0f; + data.mInternal.mExtents[0] = data.mInternal.mExtents[1] = data.mInternal.mExtents[2] = 0.0f; +} + +Gu::ConvexMesh::ConvexMesh() +: PxConvexMesh(PxConcreteType::eCONVEX_MESH, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mNb (0) +, mBigConvexData (NULL) +, mMass (0) +, mInertia (PxMat33(PxIdentity)) +{ + initConvexHullData(mHullData); +} + +Gu::ConvexMesh::ConvexMesh(GuMeshFactory& factory, ConvexHullData& data) +: PxConvexMesh(PxConcreteType::eCONVEX_MESH, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mNb(0) +, mBigConvexData(NULL) +, mMass(0) +, mInertia(PxMat33(PxIdentity)) +, mMeshFactory(&factory) +{ + mHullData = data; +} + +Gu::ConvexMesh::~ConvexMesh() +{ +// PX_SERIALIZATION + if(getBaseFlags()&PxBaseFlag::eOWNS_MEMORY) +//~PX_SERIALIZATION + { + PX_DELETE_POD(mHullData.mPolygons); + PX_DELETE_AND_RESET(mBigConvexData); + } +} + +bool Gu::ConvexMesh::isGpuCompatible() const +{ + return mHullData.mNbHullVertices <= 64 && + mHullData.mPolygons[0].mNbVerts < 32 && + mHullData.mNbEdges.isBitSet(); +} + +// PX_SERIALIZATION +void Gu::ConvexMesh::exportExtraData(PxSerializationContext& stream) +{ + stream.alignData(PX_SERIAL_ALIGN); + const PxU32 bufferSize = computeBufferSize(mHullData, getNb()); + stream.writeData(mHullData.mPolygons, bufferSize); + + if(mBigConvexData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mBigConvexData, sizeof(BigConvexData)); + + mBigConvexData->exportExtraData(stream); + } +} + +void Gu::ConvexMesh::importExtraData(PxDeserializationContext& context) +{ + const PxU32 bufferSize = computeBufferSize(mHullData, getNb()); + mHullData.mPolygons = reinterpret_cast(context.readExtraData(bufferSize)); + + if(mBigConvexData) + { + mBigConvexData = context.readExtraData(); + new(mBigConvexData)BigConvexData(PxEmpty); + mBigConvexData->importExtraData(context); + mHullData.mBigConvexRawData = &mBigConvexData->mData; + } +} + +Gu::ConvexMesh* Gu::ConvexMesh::createObject(PxU8*& address, PxDeserializationContext& context) +{ + ConvexMesh* obj = new (address) ConvexMesh(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(ConvexMesh); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +static bool convexHullLoad(Gu::ConvexHullData& data, PxInputStream& stream, PxBitAndDword& bufferSize) +{ + PxU32 version; + bool Mismatch; + if(!ReadHeader('C', 'L', 'H', 'L', version, Mismatch, stream)) + return false; + + if(!ReadHeader('C', 'V', 'H', 'L', version, Mismatch, stream)) + return false; + + PxU32 Nb; + + // Import figures + { + PxU32 tmp[4]; + ReadDwordBuffer(tmp, 4, Mismatch, stream); + data.mNbHullVertices = Ps::to8(tmp[0]); + data.mNbEdges = Ps::to16(tmp[1]); + data.mNbPolygons = Ps::to8(tmp[2]); + Nb = tmp[3]; + } + + //AM: In practice the old aligner approach wastes 20 bytes and there is no reason to 20 byte align this data. + //I changed the code to just 4 align for the time being. + //On consoles if anything we will need to make this stuff 16 byte align vectors to have any sense, which will have to be done by padding data structures. + PX_ASSERT(sizeof(Gu::HullPolygonData) % sizeof(PxReal) == 0); //otherwise please pad it. + PX_ASSERT(sizeof(PxVec3) % sizeof(PxReal) == 0); + + PxU32 bytesNeeded = computeBufferSize(data, Nb); + + PX_FREE(data.mPolygons); // Load() can be called for an existing convex mesh. In that case we need to free + // the memory first. + + bufferSize = Nb; + void* mDataMemory = PX_ALLOC(bytesNeeded, "ConvexHullData data"); + + PxU8* address = reinterpret_cast(mDataMemory); + + data.mPolygons = reinterpret_cast(address); address += sizeof(Gu::HullPolygonData) * data.mNbPolygons; + PxVec3* mDataHullVertices = reinterpret_cast(address); address += sizeof(PxVec3) * data.mNbHullVertices; + PxU8* mDataFacesByEdges8 = address; address += sizeof(PxU8) * data.mNbEdges * 2; + PxU8* mDataFacesByVertices8 = address; address += sizeof(PxU8) * data.mNbHullVertices * 3; + PxU16* mEdges = reinterpret_cast(address); address += data.mNbEdges.isBitSet() ? (sizeof(PxU16) * data.mNbEdges * 2) : 0; + PxU8* mDataVertexData8 = address; address += sizeof(PxU8) * Nb; // PT: leave that one last, so that we don't need to serialize "Nb" + + PX_ASSERT(!(size_t(mDataHullVertices) % sizeof(PxReal))); + PX_ASSERT(!(size_t(data.mPolygons) % sizeof(PxReal))); + PX_ASSERT(size_t(address)<=size_t(mDataMemory)+bytesNeeded); + + // Import vertices + readFloatBuffer(&mDataHullVertices->x, PxU32(3*data.mNbHullVertices), Mismatch, stream); + + if(version<=6) + { + PxU16 useUnquantizedNormals = readWord(Mismatch, stream); + PX_UNUSED(useUnquantizedNormals); + } + + // Import polygons + stream.read(data.mPolygons, data.mNbPolygons*sizeof(Gu::HullPolygonData)); + + if(Mismatch) + { + for(PxU32 i=0;iLoad(stream); + mHullData.mBigConvexRawData = &mBigConvexData->mData; + } + } + +/* + printf("\n\n"); + printf("COM: %f %f %f\n", massInfo.centerOfMass.x, massInfo.centerOfMass.y, massInfo.centerOfMass.z); + printf("BND: %f %f %f\n", mHullData.aabb.getCenter().x, mHullData.aabb.getCenter().y, mHullData.aabb.getCenter().z); + printf("CNT: %f %f %f\n", mHullData.mCenterxx.x, mHullData.mCenterxx.y, mHullData.mCenterxx.z); + printf("COM-BND: %f BND-CNT: %f, CNT-COM: %f\n", (massInfo.centerOfMass - mHullData.aabb.getCenter()).magnitude(), (mHullData.aabb.getCenter() - mHullData.mCenterxx).magnitude(), (mHullData.mCenterxx - massInfo.centerOfMass).magnitude()); +*/ + +// TEST_INTERNAL_OBJECTS + readFloatBuffer(&mHullData.mInternal.mRadius, 4, mismatch, stream); + + PX_ASSERT(PxVec3(mHullData.mInternal.mExtents[0], mHullData.mInternal.mExtents[1], mHullData.mInternal.mExtents[2]).isFinite()); + PX_ASSERT(mHullData.mInternal.mExtents[0] != 0.0f); + PX_ASSERT(mHullData.mInternal.mExtents[1] != 0.0f); + PX_ASSERT(mHullData.mInternal.mExtents[2] != 0.0f); +//~TEST_INTERNAL_OBJECTS + return true; +} + +void Gu::ConvexMesh::release() +{ + decRefCount(); +} + +void Gu::ConvexMesh::onRefCountZero() +{ + if ((!getBufferSize()) || mMeshFactory->removeConvexMesh(*this)) // when the mesh failed to load properly, it will not have been added to the convex array + { + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, PxConcreteType::eCONVEX_MESH); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::ConvexMesh::release: double deletion detected!"); +} + +void Gu::ConvexMesh::acquireReference() +{ + incRefCount(); +} + +PxU32 Gu::ConvexMesh::getReferenceCount() const +{ + return getRefCount(); +} + +void Gu::ConvexMesh::getMassInformation(PxReal& mass, PxMat33& localInertia, PxVec3& localCenterOfMass) const +{ + mass = Gu::ConvexMesh::getMass(); + localInertia = Gu::ConvexMesh::getInertia(); + localCenterOfMass = Gu::ConvexMesh::getHull().mCenterOfMass; +} + +PxBounds3 Gu::ConvexMesh::getLocalBounds() const +{ + PX_ASSERT(mHullData.mAABB.isValid()); + return PxBounds3::centerExtents(mHullData.mAABB.mCenter, mHullData.mAABB.mExtents); +} diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h new file mode 100644 index 000000000..9a7d08ed6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_COLLISION_CONVEXMESH_H +#define GU_COLLISION_CONVEXMESH_H + +#include "foundation/PxBitAndData.h" +#include "PxConvexMesh.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "CmRefCountable.h" +#include "GuConvexMeshData.h" + +// PX_SERIALIZATION +#include "PxMetaData.h" +#include "CmRenderOutput.h" +//~PX_SERIALIZATION + +namespace physx +{ + +class BigConvexData; +class GuMeshFactory; +class PxMeshScale; + + +namespace Gu +{ + struct HullPolygonData; + + PX_INLINE PxU32 computeBufferSize(const Gu::ConvexHullData& data, PxU32 nb) + { + PxU32 bytesNeeded = sizeof(Gu::HullPolygonData) * data.mNbPolygons; + bytesNeeded += sizeof(PxVec3) * data.mNbHullVertices; + bytesNeeded += sizeof(PxU8) * data.mNbEdges * 2; // mFacesByEdges8 + bytesNeeded += sizeof(PxU8) * data.mNbHullVertices * 3; // mFacesByVertices8; + bytesNeeded += data.mNbEdges.isBitSet() ? (sizeof(PxU16) * data.mNbEdges * 2) : 0; // mEdges; + bytesNeeded += sizeof(PxU8) * nb; // mVertexData8 + + //4 align the whole thing! + const PxU32 mod = bytesNeeded % sizeof(PxReal); + if (mod) + bytesNeeded += sizeof(PxReal) - mod; + return bytesNeeded; + } + + // 0: includes raycast map + // 1: discarded raycast map + // 2: support map not always there + // 3: support stackless trees for non-recursive collision queries + // 4: no more opcode model + // 5: valencies table and gauss map combined, only exported over a vertex count treshold that depends on the platform cooked for. + // 6: removed support for edgeData16. + // 7: removed support for edge8Data. + // 8: removed support for triangles. + + // 9: removed local sphere. + //10: removed geometric center. + //11: removed mFlags, and mERef16 from Poly; nbVerts is just a byte. + //12: removed explicit minimum, maximum from Poly + //13: internal objects + #define PX_CONVEX_VERSION 13 + + class ConvexMesh : public PxConvexMesh, public Ps::UserAllocated, public Cm::RefCountable + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + // PX_SERIALIZATION + PX_PHYSX_COMMON_API ConvexMesh(PxBaseFlags baseFlags) : PxConvexMesh(baseFlags), Cm::RefCountable(PxEmpty), mHullData(PxEmpty), mNb(PxEmpty) + { + mNb.setBit(); + } + + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& stream); + PX_PHYSX_COMMON_API void importExtraData(PxDeserializationContext& context); + PX_PHYSX_COMMON_API virtual void onRefCountZero(); + PX_PHYSX_COMMON_API static ConvexMesh* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); + void resolveReferences(PxDeserializationContext&) {} + virtual void requiresObjects(PxProcessPxBaseCallback&){} + //~PX_SERIALIZATION + PX_PHYSX_COMMON_API ConvexMesh(); + + ConvexMesh(GuMeshFactory& factory, ConvexHullData& data); + + PX_PHYSX_COMMON_API bool load(PxInputStream& stream); + + // PxConvexMesh + PX_PHYSX_COMMON_API virtual void release(); + PX_PHYSX_COMMON_API virtual PxU32 getNbVertices() const { return mHullData.mNbHullVertices; } + PX_PHYSX_COMMON_API virtual const PxVec3* getVertices() const { return mHullData.getHullVertices(); } + PX_PHYSX_COMMON_API virtual const PxU8* getIndexBuffer() const { return mHullData.getVertexData8(); } + PX_PHYSX_COMMON_API virtual PxU32 getNbPolygons() const { return mHullData.mNbPolygons; } + PX_PHYSX_COMMON_API virtual bool getPolygonData(PxU32 i, PxHullPolygon& data) const; + PX_PHYSX_COMMON_API virtual bool isGpuCompatible() const; + PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const; + PX_PHYSX_COMMON_API virtual void acquireReference(); + + PX_PHYSX_COMMON_API virtual void getMassInformation(PxReal& mass, PxMat33& localInertia, PxVec3& localCenterOfMass) const; + PX_PHYSX_COMMON_API virtual PxBounds3 getLocalBounds() const; + //~PxConvexMesh + + PX_FORCE_INLINE PxU32 getNbVerts() const { return mHullData.mNbHullVertices; } + PX_FORCE_INLINE const PxVec3* getVerts() const { return mHullData.getHullVertices(); } + PX_FORCE_INLINE PxU32 getNbPolygonsFast() const { return mHullData.mNbPolygons; } + PX_FORCE_INLINE const HullPolygonData& getPolygon(PxU32 i) const { return mHullData.mPolygons[i]; } + PX_FORCE_INLINE const HullPolygonData* getPolygons() const { return mHullData.mPolygons; } + PX_FORCE_INLINE PxU32 getNbEdges() const { return mHullData.mNbEdges; } + + PX_FORCE_INLINE const ConvexHullData& getHull() const { return mHullData; } + PX_FORCE_INLINE ConvexHullData& getHull() { return mHullData; } + PX_FORCE_INLINE const CenterExtents& getLocalBoundsFast() const { return mHullData.mAABB; } + PX_FORCE_INLINE PxReal getMass() const { return mMass; } + PX_FORCE_INLINE void setMass(PxReal mass) { mMass = mass; } + PX_FORCE_INLINE const PxMat33& getInertia() const { return mInertia; } + PX_FORCE_INLINE void setInertia(const PxMat33& inertia) { mInertia = inertia; } + + PX_FORCE_INLINE BigConvexData* getBigConvexData() const { return mBigConvexData; } + PX_FORCE_INLINE void setBigConvexData(BigConvexData* bcd) { mBigConvexData = bcd; } + + PX_FORCE_INLINE PxU32 getBufferSize() const { return computeBufferSize(mHullData, getNb()); } + + PX_PHYSX_COMMON_API virtual ~ConvexMesh(); + + PX_FORCE_INLINE void setMeshFactory(GuMeshFactory* f) { mMeshFactory = f; } + + PX_FORCE_INLINE void setNb(PxU32 nb) { mNb = nb; } + + protected: + ConvexHullData mHullData; + PxBitAndDword mNb; // ### PT: added for serialization. Try to remove later? + + BigConvexData* mBigConvexData; //!< optional, only for large meshes! PT: redundant with ptr in chull data? Could also be end of other buffer + PxReal mMass; //this is mass assuming a unit density that can be scaled by instances! + PxMat33 mInertia; //in local space of mesh! +private: + GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization + + PX_FORCE_INLINE PxU32 getNb() const { return mNb; } + PX_FORCE_INLINE PxU32 ownsMemory() const { return PxU32(!mNb.isBitSet()); } + }; + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h new file mode 100644 index 000000000..12e311914 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_MESH_DATA_H +#define GU_CONVEX_MESH_DATA_H + +#include "foundation/PxPlane.h" +#include "PsIntrinsics.h" +#include "GuSerialize.h" +#include "GuCenterExtents.h" +#include "foundation/PxBitAndData.h" + +// Data definition + +namespace physx +{ +namespace Gu +{ + struct BigConvexRawData; + + struct HullPolygonData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + // PT: this structure won't be allocated with PX_NEW because polygons aren't allocated alone (with a dedicated alloc). + // Instead they are part of a unique allocation/buffer containing all data for the ConvexHullData class (polygons, followed by + // hull vertices, edge data, etc). As a result, ctors for embedded classes like PxPlane won't be called. + + PxPlane mPlane; //!< Plane equation for this polygon //Could drop 4th elem as it can be computed from any vertex as: d = - p.dot(n); + PxU16 mVRef8; //!< Offset of vertex references in hull vertex data (CS: can we assume indices are tightly packed and offsets are ascending?? DrawObjects makes and uses this assumption) + PxU8 mNbVerts; //!< Number of vertices/edges in the polygon + PxU8 mMinIndex; //!< Index of the polygon vertex that has minimal projection along this plane's normal. + + PX_FORCE_INLINE PxReal getMin(const PxVec3* PX_RESTRICT hullVertices) const //minimum of projection of the hull along this plane normal + { + return mPlane.n.dot(hullVertices[mMinIndex]); + } + + PX_FORCE_INLINE PxReal getMax() const { return -mPlane.d; } //maximum of projection of the hull along this plane normal + }; + + PX_FORCE_INLINE void flipData(Gu::HullPolygonData& data) + { + flip(data.mPlane.n.x); + flip(data.mPlane.n.y); + flip(data.mPlane.n.z); + flip(data.mPlane.d); + flip(data.mVRef8); + } + // PT: if this one breaks, please make sure the 'flipData' function is properly updated. + PX_COMPILE_TIME_ASSERT(sizeof(Gu::HullPolygonData) == 20); + +// TEST_INTERNAL_OBJECTS + struct InternalObjectsData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + PxReal mRadius; + PxReal mExtents[3]; + + PX_FORCE_INLINE void reset() + { + mRadius = 0.0f; + mExtents[0] = 0.0f; + mExtents[1] = 0.0f; + mExtents[2] = 0.0f; + } + }; + PX_COMPILE_TIME_ASSERT(sizeof(Gu::InternalObjectsData) == 16); +//~TEST_INTERNAL_OBJECTS + + struct ConvexHullData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading + CenterExtents mAABB; //!< bounds TODO: compute this on the fly from first 6 vertices in the vertex array. We'll of course need to sort the most extreme ones to the front. + PxVec3 mCenterOfMass; //in local space of mesh! + + // PT: WARNING: mNbHullVertices *must* appear before mBigConvexRawData for ConvX to be able to do "big raw data" surgery + + PxBitAndWord mNbEdges; //!(mAABB); + } + + PX_FORCE_INLINE const PxVec3* getHullVertices() const //!< Convex hull vertices + { + const char* tmp = reinterpret_cast(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + return reinterpret_cast(tmp); + } + + PX_FORCE_INLINE const PxU8* getFacesByEdges8() const //!< for each edge, gives 2 adjacent polygons; used by convex-convex code to come up with all the convex' edge normals. + { + const char* tmp = reinterpret_cast(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + return reinterpret_cast(tmp); + } + + PX_FORCE_INLINE const PxU8* getFacesByVertices8() const //!< for each edge, gives 2 adjacent polygons; used by convex-convex code to come up with all the convex' edge normals. + { + const char* tmp = reinterpret_cast(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + tmp += sizeof(PxU8) * mNbEdges * 2; + return reinterpret_cast(tmp); + } + + //If we don't build the convex hull with grb data, we will return NULL pointer + PX_FORCE_INLINE const PxU16* getVerticesByEdges16() const //!< Vertex indices indexed by unique edges + { + if (mNbEdges.isBitSet()) + { + const char* tmp = reinterpret_cast(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + tmp += sizeof(PxU8) * mNbEdges * 2; + tmp += sizeof(PxU8) * mNbHullVertices * 3; + return reinterpret_cast(tmp); + } + return NULL; + } + + PX_FORCE_INLINE const PxU8* getVertexData8() const //!< Vertex indices indexed by hull polygons + { + const char* tmp = reinterpret_cast(mPolygons); + tmp += sizeof(Gu::HullPolygonData) * mNbPolygons; + tmp += sizeof(PxVec3) * mNbHullVertices; + tmp += sizeof(PxU8) * mNbEdges * 2; + tmp += sizeof(PxU8) * mNbHullVertices * 3; + if (mNbEdges.isBitSet()) + tmp += sizeof(PxU16) * mNbEdges * 2; + return reinterpret_cast(tmp); + } + + }; + #if PX_P64_FAMILY + PX_COMPILE_TIME_ASSERT(sizeof(Gu::ConvexHullData) == 72); + #else + PX_COMPILE_TIME_ASSERT(sizeof(Gu::ConvexHullData) == 64); + #endif + + // PT: 'getPaddedBounds()' is only safe if we make sure the bounds member is followed by at least 32bits of data + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(Gu::ConvexHullData, mCenterOfMass)>=PX_OFFSET_OF(Gu::ConvexHullData, mAABB)+4); + +} // namespace Gu + +} + +//#pragma PX_POP_PACK + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp new file mode 100644 index 000000000..564f68cbb --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp @@ -0,0 +1,44 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecBox.h" + +namespace physx +{ + const Ps::aos::BoolV boxVertexTable[8] = { + Ps::aos::BFFFF(),//--- + Ps::aos::BTFFF(),//+-- + Ps::aos::BFTFF(),//-+- + Ps::aos::BTTFF(),//++- + Ps::aos::BFFTF(),//--+ + Ps::aos::BTFTF(),//+-+ + Ps::aos::BFTTF(),//-++ + Ps::aos::BTTTF(),//+++ + }; +} diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h new file mode 100644 index 000000000..1147ec982 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_SUPPORT_TABLE_H +#define GU_CONVEX_SUPPORT_TABLE_H + +#include "GuVecConvex.h" +#include "PsVecTransform.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ +namespace Gu +{ + + class TriangleV; + class CapsuleV; + class BoxV; + class ConvexHullV; + class ConvexHullNoScaleV; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + class SupportLocal + { + public: + Ps::aos::Vec3V shapeSpaceCenterOfMass; + const Ps::aos::PsTransformV& transform; + const Ps::aos::Mat33V& vertex2Shape; + const Ps::aos::Mat33V& shape2Vertex; + const bool isIdentityScale; + + SupportLocal(const Ps::aos::PsTransformV& _transform, const Ps::aos::Mat33V& _vertex2Shape, const Ps::aos::Mat33V& _shape2Vertex, const bool _isIdentityScale = true): transform(_transform), + vertex2Shape(_vertex2Shape), shape2Vertex(_shape2Vertex), isIdentityScale(_isIdentityScale) + { + } + + PX_FORCE_INLINE void setShapeSpaceCenterofMass(const Ps::aos::Vec3VArg _shapeSpaceCenterOfMass) + { + shapeSpaceCenterOfMass = _shapeSpaceCenterOfMass; + } + virtual ~SupportLocal() {} + virtual Ps::aos::Vec3V doSupport(const Ps::aos::Vec3VArg dir) const = 0; + virtual void doSupport(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max) const = 0; + virtual void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts)const = 0; + + protected: + SupportLocal& operator=(const SupportLocal&); + }; +#if PX_VC + #pragma warning(pop) +#endif + + template + class SupportLocalImpl : public SupportLocal + { + + public: + const Convex& conv; + SupportLocalImpl(const Convex& _conv, const Ps::aos::PsTransformV& _transform, const Ps::aos::Mat33V& _vertex2Shape, const Ps::aos::Mat33V& _shape2Vertex, const bool _isIdentityScale = true) : + SupportLocal(_transform, _vertex2Shape, _shape2Vertex, _isIdentityScale), conv(_conv) + { + } + + Ps::aos::Vec3V doSupport(const Ps::aos::Vec3VArg dir) const + { + //return conv.supportVertsLocal(dir); + return conv.supportLocal(dir); + } + + void doSupport(const Ps::aos::Vec3VArg dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max) const + { + return conv.supportLocal(dir, min, max); + } + + void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts) const + { + conv.populateVerts(inds, numInds, originalVerts, verts); + } + + protected: + SupportLocalImpl& operator=(const SupportLocalImpl&); + + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp new file mode 100644 index 000000000..851110773 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuConvexUtilsInternal.h" + +#include "foundation/PxBounds3.h" +#include "CmScaling.h" +#include "GuBoxConversion.h" +#include "PxConvexMeshGeometry.h" +#include "GuConvexMesh.h" + +using namespace physx; +using namespace Gu; + +void Gu::computeHullOBB(Box& hullOBB, const PxBounds3& hullAABB, float offset, + const Cm::Matrix34& convexPose, + const Cm::Matrix34& meshPose, const Cm::FastVertex2ShapeScaling& meshScaling, bool idtScaleMesh) +{ + // transform bounds = mesh space + Cm::Matrix34 m0to1 = meshPose.transformTranspose(convexPose); + + hullOBB.extents = hullAABB.getExtents() + PxVec3(offset); + hullOBB.center = m0to1.transform(hullAABB.getCenter()); + hullOBB.rot = m0to1.m; + + if(!idtScaleMesh) + meshScaling.transformQueryBounds(hullOBB.center, hullOBB.extents, hullOBB.rot); +} + +void Gu::computeVertexSpaceOBB(Box& dst, const Box& src, const PxTransform& meshPose, const PxMeshScale& meshScale) +{ + // AP scaffold failure in x64 debug in GuConvexUtilsInternal.cpp + //PX_ASSERT("Performance warning - this path shouldn't execute for identity mesh scale." && !meshScale.isIdentity()); + + dst = transform(meshScale.getInverse() * Cm::Matrix34(meshPose.getInverse()), src); +} + +void Gu::computeOBBAroundConvex( + Box& obb, const PxConvexMeshGeometry& convexGeom, const PxConvexMesh* cm, const PxTransform& convexPose) +{ + const CenterExtents& aabb = static_cast(cm)->getLocalBoundsFast(); + + if(convexGeom.scale.isIdentity()) + { + const PxMat33 m(convexPose.q); + obb = Gu::Box(m.transform(aabb.mCenter) + convexPose.p, aabb.mExtents, m); + } + else + { + obb = transform(Cm::Matrix34(convexPose) * convexGeom.scale.toMat33(), Box(aabb.mCenter, aabb.mExtents, PxMat33(PxIdentity))); + } +} diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h new file mode 100644 index 000000000..d7b2226a8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h @@ -0,0 +1,67 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CONVEX_UTILS_INTERNALS_H +#define GU_CONVEX_UTILS_INTERNALS_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +class PxMeshScale; +class PxConvexMeshGeometry; +class PxConvexMesh; + +namespace Cm +{ + class Matrix34; + class FastVertex2ShapeScaling; +} + +namespace Gu +{ + class Box; + + void computeHullOBB( + Gu::Box& hullOBB, const PxBounds3& hullAABB, float offset, const Cm::Matrix34& world0, + const Cm::Matrix34& world1, const Cm::FastVertex2ShapeScaling& meshScaling, bool idtScaleMesh); + + // src = input + // computes a box in vertex space (including skewed scale) from src world box + void computeVertexSpaceOBB(Gu::Box& dst, const Gu::Box& src, const PxTransform& meshPose, const PxMeshScale& meshScale); + + PX_PHYSX_COMMON_API void computeOBBAroundConvex( + Gu::Box& obb, const PxConvexMeshGeometry& convexGeom, const PxConvexMesh* cm, const PxTransform& convexPose); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h b/src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h new file mode 100644 index 000000000..2001ad736 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h @@ -0,0 +1,155 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_CUBE_INDEX_H +#define GU_CUBE_INDEX_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PsFPU.h" + +namespace physx +{ + + enum CubeIndex + { + CUBE_RIGHT, + CUBE_LEFT, + CUBE_TOP, + CUBE_BOTTOM, + CUBE_FRONT, + CUBE_BACK, + + CUBE_FORCE_DWORD = 0x7fffffff + }; + + /* + It's pretty straightforwards in concept (though the execution in hardware is + a bit crufty and complex). You use a 3D texture coord to look up a texel in + a cube map. First you find which of the axis has the largest value (i.e. + X,Y,Z), and then the sign of that axis decides which face you are going to + use. Which is why the faces are called +X, -X, +Y, -Y, +Z, -Z - after their + principle axis. Then you scale the vector so that the largest value is +/-1. + Then use the other two as 2D coords to look up your texel (with a 0.5 scale + & offset). + + For example, vector (0.4, -0.2, -0.5). Largest value is the Z axis, and it's + -ve, so we're reading from the -Z map. Scale so that this Z axis is +/-1, + and you get the vector (0.8, -0.4, -1.0). So now use the other two values to + look up your texel. So we look up texel (0.8, -0.4). The scale & offset move + the -1->+1 range into the usual 0->1 UV range, so we actually look up texel + (0.9, 0.3). The filtering is extremely complex, especially where three maps + meet, but that's a hardware problem :-) + */ + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Cubemap lookup function. + * + * To transform returned uvs into mapping coordinates : + * u += 1.0f; u *= 0.5f; + * v += 1.0f; v *= 0.5f; + * + * \fn CubemapLookup(const PxVec3& direction, float& u, float& v) + * \param direction [in] a direction vector + * \param u [out] impact coordinate on the unit cube, in [-1,1] + * \param v [out] impact coordinate on the unit cube, in [-1,1] + * \return cubemap texture index + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PX_INLINE CubeIndex CubemapLookup(const PxVec3& direction, float& u, float& v); + + PX_INLINE PxU32 ComputeCubemapOffset(const PxVec3& dir, PxU32 subdiv) + { + float u,v; + const CubeIndex CI = CubemapLookup(dir, u, v); + + // Remap to [0, subdiv[ + const float Coeff = 0.5f * float(subdiv-1); + u += 1.0f; u *= Coeff; + v += 1.0f; v *= Coeff; + + // Compute offset + return PxU32(CI)*(subdiv*subdiv) + PxU32(u)*subdiv + PxU32(v); + } + + + PX_INLINE PxU32 ComputeCubemapNearestOffset(const PxVec3& dir, PxU32 subdiv) + { + float u,v; + const CubeIndex CI = CubemapLookup(dir, u, v); + + // Remap to [0, subdiv] + const float Coeff = 0.5f * float(subdiv-1); + u += 1.0f; u *= Coeff; + v += 1.0f; v *= Coeff; + + // Compute offset + return PxU32(CI)*(subdiv*subdiv) + PxU32(u + 0.5f)*subdiv + PxU32(v + 0.5f); + } + + + PX_INLINE CubeIndex CubemapLookup(const PxVec3& direction, float& u, float& v) + { + const PxU32* binary = reinterpret_cast(&direction.x); + + const PxU32 absPx = binary[0] & ~PX_SIGN_BITMASK; + const PxU32 absNy = binary[1] & ~PX_SIGN_BITMASK; + const PxU32 absNz = binary[2] & ~PX_SIGN_BITMASK; + + PxU32 Index1 = 0; //x biggest axis + PxU32 Index2 = 1; + PxU32 Index3 = 2; + if( (absNy > absPx) & (absNy > absNz)) + { + //y biggest + Index2 = 2; + Index3 = 0; + Index1 = 1; + } + else if(absNz > absPx) + { + //z biggest + Index2 = 0; + Index3 = 1; + Index1 = 2; + } + + const PxF32* data = &direction.x; + const float Coeff = 1.0f / fabsf(data[Index1]); + u = data[Index2] * Coeff; + v = data[Index3] * Coeff; + + const PxU32 Sign = binary[Index1]>>31; + return CubeIndex(Sign|(Index1+Index1)); + } + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp new file mode 100644 index 000000000..cb03c9953 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp @@ -0,0 +1,96 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec3.h" +#include "foundation/PxAssert.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "GuHillClimbing.h" +#include "GuBigConvexData2.h" + +namespace physx +{ + +void localSearch(PxU32& id, const PxVec3& dir, const PxVec3* verts, const Gu::BigConvexRawData* val) +{ + // WARNING: there is a problem on x86 with a naive version of this code, where truncation + // of values from 80 bits to 32 bits as they're stored in memory means that iteratively moving to + // an adjacent vertex of greater support can go into an infinite loop. So we use a version which + // never visits a vertex twice. Note - this might not be enough for GJK, since local + // termination of the support function might not be enough to ensure convergence of GJK itself. + + // if we got here, we'd better have vertices and valencies + PX_ASSERT(verts && val); + + class TinyBitMap + { + public: + PxU32 m[8]; + PX_FORCE_INLINE TinyBitMap() { m[0] = m[1] = m[2] = m[3] = m[4] = m[5] = m[6] = m[7] = 0; } + PX_FORCE_INLINE void set(PxU8 v) { m[v>>5] |= 1<<(v&31); } + PX_FORCE_INLINE bool get(PxU8 v) const { return (m[v>>5] & 1<<(v&31)) != 0; } + }; + + TinyBitMap visited; + + const Gu::Valency* Valencies = val->mValencies; + const PxU8* Adj = val->mAdjacentVerts; + + PX_ASSERT(Valencies && Adj); + + // Get the initial value and the initial vertex + float MaxVal = dir.dot(verts[id]); + PxU32 NextVtx = id; + + do + { + PxU16 NbNeighbors = Valencies[NextVtx].mCount; + const PxU8* Run = Adj + Valencies[NextVtx].mOffset; + id = NextVtx; + while(NbNeighbors--) + { + const PxU8 Neighbor = *Run++; + + if(!visited.get(Neighbor)) + { + visited.set(Neighbor); + + const float CurVal = dir.dot(verts[Neighbor]); + + if(CurVal>MaxVal) + { + MaxVal = CurVal; + NextVtx = Neighbor; + } + } + } + } while(NextVtx!=id); +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h new file mode 100644 index 000000000..0db865e86 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HILL_CLIMBING_H +#define GU_HILL_CLIMBING_H + +#include "Ps.h" +#include "PxPhysXCommonConfig.h" + +namespace physx +{ + namespace Gu + { + struct BigConvexRawData; + } + + void localSearch(PxU32& id, const PxVec3& dir, const PxVec3* verts, const Gu::BigConvexRawData* val); +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp new file mode 100644 index 000000000..4b089626d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp @@ -0,0 +1,516 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuShapeConvex.h" +#include "GuBigConvexData.h" +#include "GuEdgeListData.h" +#include "GuInternal.h" + +#include "CmMatrix34.h" +#include "GuHillClimbing.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +static PX_FORCE_INLINE PxU32 selectClosestPolygon(PxReal& maxDp_, PxU32 numPolygons, const Gu::HullPolygonData* polys, const PxVec3& axis) +{ + float maxDp = polys[0].mPlane.n.dot(axis); + PxU32 closest = 0; + + // Loop through polygons + for(PxU32 i=1; i maxDp) + { + maxDp = dp; + closest = i; + } + } + maxDp_ = maxDp; + return closest; +} + +static PxU32 SelectClosestEdgeCB_Convex(const PolygonalData& data, const Cm::FastVertex2ShapeScaling& scaling, const PxVec3& localSpaceDirection) +{ + //vertex1TOShape1Skew is a symmetric matrix. + //it has the property that (vertex1TOShape1Skew * v)|localSpaceDirection == (vertex1TOShape1Skew * localSpaceDirection)|v + const PxVec3 vertexSpaceDirection = scaling * localSpaceDirection; + + const Gu::HullPolygonData* PX_RESTRICT polys = data.mPolygons; + + PxReal maxDp; + // ##might not be needed + PxU32 closest = ::selectClosestPolygon(maxDp, data.mNbPolygons, polys, vertexSpaceDirection); + + // Since the convex is closed, at least some poly must satisfy this + PX_ASSERT(maxDp>=0); + + const PxU32 numEdges = data.mNbEdges; + const PxU8* const edgeToFace = data.mFacesByEdges; + + //Loop through edges + PxU32 closestEdge = 0xffffffff; + PxReal maxDpSq = maxDp * maxDp; + for(PxU32 i=0; i < numEdges; i++) + { + const PxU8 f0 = edgeToFace[i*2]; + const PxU8 f1 = edgeToFace[i*2+1]; + + // unnormalized edge normal + const PxVec3 edgeNormal = polys[f0].mPlane.n + polys[f1].mPlane.n; + const PxReal enMagSq = edgeNormal.magnitudeSquared(); + //Test normal of current edge - squared test is valid if dp and maxDp both >= 0 + const float dp = edgeNormal.dot(vertexSpaceDirection); + if(dp>=0.0f && dp*dp>maxDpSq*enMagSq) + { + maxDpSq = dp*dp/enMagSq; + closestEdge = i; + } + } + + if(closestEdge!=0xffffffff) + { + const PxU8* FBE = edgeToFace; + + const PxU32 f0 = FBE[closestEdge*2]; + const PxU32 f1 = FBE[closestEdge*2+1]; + + const PxReal dp0 = polys[f0].mPlane.n.dot(vertexSpaceDirection); + const PxReal dp1 = polys[f1].mPlane.n.dot(vertexSpaceDirection); + if(dp0>dp1) + closest = f0; + else + closest = f1; + } + return closest; +} + +// Hull projection callback for "small" hulls +static void HullProjectionCB_SmallConvex(const PolygonalData& data, const PxVec3& dir, + const Cm::Matrix34& world, + const Cm::FastVertex2ShapeScaling& scaling, + PxReal& min, PxReal& max) +{ + const PxVec3 localSpaceDirection = world.rotateTranspose(dir); + //vertex1TOShape1Skew is a symmetric matrix. + //it has the property that (vertex1TOShape1Skew * v)|localSpaceDirection == (vertex1TOShape1Skew * localSpaceDirection)|v + const PxVec3 vertexSpaceDirection = scaling * localSpaceDirection; + + // PT: prevents aliasing + PxReal minimum = PX_MAX_REAL; + PxReal maximum = -PX_MAX_REAL; + + //brute-force, localspace + { + const PxVec3* PX_RESTRICT verts = data.mVerts; + PxU32 numVerts = data.mNbVerts; + while(numVerts--) + { + const PxReal dp = (*verts++).dot(vertexSpaceDirection); + minimum = physx::intrinsics::selectMin(minimum, dp); + maximum = physx::intrinsics::selectMax(maximum, dp); + } + + } + + const PxReal offset = world.p.dot(dir); + min = minimum + offset; + max = maximum + offset; +} + +static PxU32 computeNearestOffset(const PxU32 subdiv, const PxVec3& dir) +{ + // ComputeCubemapNearestOffset(const Point& dir, udword subdiv) + + // PT: ok so why exactly was the code duplicated here? + // PxU32 CI = CubemapLookup(dir,u,v) + + PxU32 index; + PxReal coeff; + // find largest axis + PxReal absNx = PxAbs(dir.x); + PxReal absNy = PxAbs(dir.y); + PxReal absNz = PxAbs(dir.z); + + if( absNy > absNx && + absNy > absNz) + { + //y biggest + index = 1; + coeff = 1.0f/absNy; + } + else if(absNz > absNx) + { + index = 2; + coeff = 1.0f/absNz; + } + else + { + index = 0; + coeff = 1.0f/absNx; + } + + union + { + PxU32 aU32; + PxReal aFloat; + } conv; + + conv.aFloat = dir[index]; + PxU32 sign = conv.aU32>>31; + + const PxU32 index2 = Ps::getNextIndex3(index); + const PxU32 index3 = Ps::getNextIndex3(index2); + PxReal u = dir[index2] * coeff; + PxReal v = dir[index3] * coeff; + + PxU32 CI = (sign | (index+index)); + + //Remap to [0, subdiv[ + coeff = 0.5f * PxReal(subdiv-1); + u += 1.0f; u *= coeff; + v += 1.0f; v *= coeff; + + //Round to nearest + PxU32 ui = PxU32(u); + PxU32 vi = PxU32(v); + + PxReal du = u - PxReal(ui); + PxReal dv = v - PxReal(vi); + if(du>0.5f) ui++; + if(dv>0.5f) vi++; + + //Compute offset + return CI*(subdiv*subdiv) + ui*subdiv + vi; +} + +// Hull projection callback for "big" hulls +static void HullProjectionCB_BigConvex(const PolygonalData& data, const PxVec3& dir, const Cm::Matrix34& world, const Cm::FastVertex2ShapeScaling& scaling, PxReal& minimum, PxReal& maximum) +{ + const PxVec3* PX_RESTRICT verts = data.mVerts; + + const PxVec3 localSpaceDirection = world.rotateTranspose(dir); + //vertex1TOShape1Skew is a symmetric matrix. + //it has the property that (vertex1TOShape1Skew * v)|localSpaceDirection == (vertex1TOShape1Skew * localSpaceDirection)|v + const PxVec3 vertexSpaceDirection = scaling * localSpaceDirection; //NB: triangles are always shape 1! eek! + + // This version is better for objects with a lot of vertices + const Gu::BigConvexRawData* bigData = data.mBigData; + PxU32 minID = 0, maxID = 0; + { + const PxU32 offset = computeNearestOffset(bigData->mSubdiv, -vertexSpaceDirection); + minID = bigData->mSamples[offset]; + maxID = bigData->getSamples2()[offset]; + } + + // Do hillclimbing! + localSearch(minID, -vertexSpaceDirection, verts, bigData); + localSearch(maxID, vertexSpaceDirection, verts, bigData); + + const PxReal offset = world.p.dot(dir); + minimum = offset + verts[minID].dot(vertexSpaceDirection); + maximum = offset + verts[maxID].dot(vertexSpaceDirection); + PX_ASSERT(maximum >= minimum); +} + +void Gu::getPolygonalData_Convex(PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Cm::FastVertex2ShapeScaling& scaling) +{ + dst->mCenter = scaling * src->mCenterOfMass; + dst->mNbVerts = src->mNbHullVertices; + dst->mNbPolygons = src->mNbPolygons; + dst->mNbEdges = src->mNbEdges; + dst->mPolygons = src->mPolygons; + dst->mVerts = src->getHullVertices(); + dst->mPolygonVertexRefs = src->getVertexData8(); + dst->mFacesByEdges = src->getFacesByEdges8(); + +// TEST_INTERNAL_OBJECTS + dst->mInternal = src->mInternal; +//~TEST_INTERNAL_OBJECTS + + dst->mBigData = src->mBigConvexRawData; + + // This threshold test doesnt cost much and many customers cook on PC and use this on 360. + // 360 has a much higher threshold than PC(and it makes a big difference) + // PT: the cool thing is that this test is now done once by contact generation call, not once by hull projection + if(!src->mBigConvexRawData) + dst->mProjectHull = HullProjectionCB_SmallConvex; + else + dst->mProjectHull = HullProjectionCB_BigConvex; + dst->mSelectClosestEdgeCB = SelectClosestEdgeCB_Convex; +} + +// Box emulating convex mesh + +// Face0: 0-1-2-3 +// Face1: 1-5-6-2 +// Face2: 5-4-7-6 +// Face3: 4-0-3-7 +// Face4; 3-2-6-7 +// Face5: 4-5-1-0 + +// 7+------+6 0 = --- +// /| /| 1 = +-- +// / | / | 2 = ++- +// / 4+---/--+5 3 = -+- +// 3+------+2 / y z 4 = --+ +// | / | / | / 5 = +-+ +// |/ |/ |/ 6 = +++ +// 0+------+1 *---x 7 = -++ + +static const PxU8 gPxcBoxPolygonData[] = { + 0, 1, 2, 3, + 1, 5, 6, 2, + 5, 4, 7, 6, + 4, 0, 3, 7, + 3, 2, 6, 7, + 4, 5, 1, 0, +}; + +#define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) +static PxVec3 gPxcBoxEdgeNormals[] = +{ + PxVec3(0, -INVSQRT2, -INVSQRT2), // 0-1 + PxVec3(INVSQRT2, 0, -INVSQRT2), // 1-2 + PxVec3(0, INVSQRT2, -INVSQRT2), // 2-3 + PxVec3(-INVSQRT2, 0, -INVSQRT2), // 3-0 + + PxVec3(0, INVSQRT2, INVSQRT2), // 7-6 + PxVec3(INVSQRT2, 0, INVSQRT2), // 6-5 + PxVec3(0, -INVSQRT2, INVSQRT2), // 5-4 + PxVec3(-INVSQRT2, 0, INVSQRT2), // 4-7 + + PxVec3(INVSQRT2, -INVSQRT2, 0), // 1-5 + PxVec3(INVSQRT2, INVSQRT2, 0), // 6-2 + PxVec3(-INVSQRT2, INVSQRT2, 0), // 3-7 + PxVec3(-INVSQRT2, -INVSQRT2, 0) // 4-0 +}; +#undef INVSQRT2 + +// ### needs serious checkings + // Flags(16), Count(16), Offset(32); +static Gu::EdgeDescData gPxcBoxEdgeDesc[] = { + {Gu::PX_EDGE_ACTIVE, 2, 0}, + {Gu::PX_EDGE_ACTIVE, 2, 2}, + {Gu::PX_EDGE_ACTIVE, 2, 4}, + {Gu::PX_EDGE_ACTIVE, 2, 6}, + {Gu::PX_EDGE_ACTIVE, 2, 8}, + {Gu::PX_EDGE_ACTIVE, 2, 10}, + {Gu::PX_EDGE_ACTIVE, 2, 12}, + {Gu::PX_EDGE_ACTIVE, 2, 14}, + {Gu::PX_EDGE_ACTIVE, 2, 16}, + {Gu::PX_EDGE_ACTIVE, 2, 18}, + {Gu::PX_EDGE_ACTIVE, 2, 20}, + {Gu::PX_EDGE_ACTIVE, 2, 22}, +}; + +// ### needs serious checkings +static PxU8 gPxcBoxFaceByEdge[] = { + 0,5, // Edge 0-1 + 0,1, // Edge 1-2 + 0,4, // Edge 2-3 + 0,3, // Edge 3-0 + 2,4, // Edge 7-6 + 1,2, // Edge 6-5 + 2,5, // Edge 5-4 + 2,3, // Edge 4-7 + 1,5, // Edge 1-5 + 1,4, // Edge 6-2 + 3,4, // Edge 3-7 + 3,5, // Edge 4-0 +}; + +static PxU32 SelectClosestEdgeCB_Box(const PolygonalData& data, const Cm::FastVertex2ShapeScaling& scaling, const PxVec3& localDirection) +{ + PX_UNUSED(scaling); + + PxReal maxDp; + // ##might not be needed + PxU32 closest = ::selectClosestPolygon(maxDp, 6, data.mPolygons, localDirection); + + PxU32 numEdges = 12; + const PxVec3* PX_RESTRICT edgeNormals = gPxcBoxEdgeNormals; + + //Loop through edges + PxU32 closestEdge = 0xffffffff; + for(PxU32 i=0; i < numEdges; i++) + { + //Test normal of current edge + const float dp = edgeNormals[i].dot(localDirection); + if(dp>maxDp) + { + maxDp = dp; + closestEdge = i; + } + } + + if(closestEdge!=0xffffffff) + { + const Gu::EdgeDescData* PX_RESTRICT ED = gPxcBoxEdgeDesc; + const PxU8* PX_RESTRICT FBE = gPxcBoxFaceByEdge; + + PX_ASSERT(ED[closestEdge].Count==2); + const PxU32 f0 = FBE[ED[closestEdge].Offset]; + const PxU32 f1 = FBE[ED[closestEdge].Offset+1]; + + const PxReal dp0 = data.mPolygons[f0].mPlane.n.dot(localDirection); + const PxReal dp1 = data.mPolygons[f1].mPlane.n.dot(localDirection); + if(dp0>dp1) + closest = f0; + else + closest = f1; + } + + return closest; +} + +static PX_FORCE_INLINE void projectBox(PxVec3& p, const PxVec3& localDir, const PxVec3& extents) +{ + // PT: the original code didn't have branches or FPU comparisons. Why rewrite it ? +// p.x = (localDir.x >= 0) ? extents.x : -extents.x; +// p.y = (localDir.y >= 0) ? extents.y : -extents.y; +// p.z = (localDir.z >= 0) ? extents.z : -extents.z; + p.x = physx::intrinsics::fsel(localDir.x, extents.x, -extents.x); + p.y = physx::intrinsics::fsel(localDir.y, extents.y, -extents.y); + p.z = physx::intrinsics::fsel(localDir.z, extents.z, -extents.z); +} + +static void HullProjectionCB_Box(const PolygonalData& data, const PxVec3& dir, const Cm::Matrix34& world, const Cm::FastVertex2ShapeScaling& scaling, PxReal& minimum, PxReal& maximum) +{ + PX_UNUSED(scaling); + + const PxVec3 localDir = world.rotateTranspose(dir); + + PxVec3 p; + projectBox(p, localDir, *data.mHalfSide); + + const PxReal offset = world.p.dot(dir); + const PxReal tmp = p.dot(localDir); + maximum = offset + tmp; + minimum = offset - tmp; +} + +PolygonalBox::PolygonalBox(const PxVec3& halfSide) : mHalfSide(halfSide) +{ + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + PxVec3 minimum = -mHalfSide; + PxVec3 maximum = mHalfSide; + // Generate 8 corners of the bbox + mVertices[0] = PxVec3(minimum.x, minimum.y, minimum.z); + mVertices[1] = PxVec3(maximum.x, minimum.y, minimum.z); + mVertices[2] = PxVec3(maximum.x, maximum.y, minimum.z); + mVertices[3] = PxVec3(minimum.x, maximum.y, minimum.z); + mVertices[4] = PxVec3(minimum.x, minimum.y, maximum.z); + mVertices[5] = PxVec3(maximum.x, minimum.y, maximum.z); + mVertices[6] = PxVec3(maximum.x, maximum.y, maximum.z); + mVertices[7] = PxVec3(minimum.x, maximum.y, maximum.z); + + //Setup the polygons + for(PxU8 i=0; i < 6; i++) + { + mPolygons[i].mNbVerts = 4; + mPolygons[i].mVRef8 = PxU16(i*4); + } + + // ### planes needs *very* careful checks + // X axis + mPolygons[1].mPlane.n = PxVec3(1.0f, 0.0f, 0.0f); + mPolygons[1].mPlane.d = -mHalfSide.x; + mPolygons[3].mPlane.n = PxVec3(-1.0f, 0.0f, 0.0f); + mPolygons[3].mPlane.d = -mHalfSide.x; + + mPolygons[1].mMinIndex = 0; + mPolygons[3].mMinIndex = 1; + +// mPolygons[1].mMinObsolete = -mHalfSide.x; +// mPolygons[3].mMinObsolete = -mHalfSide.x; + + PX_ASSERT(mPolygons[1].getMin(mVertices) == -mHalfSide.x); + PX_ASSERT(mPolygons[3].getMin(mVertices) == -mHalfSide.x); + + + // Y axis + mPolygons[4].mPlane.n = PxVec3(0.f, 1.0f, 0.0f); + mPolygons[4].mPlane.d = -mHalfSide.y; + mPolygons[5].mPlane.n = PxVec3(0.0f, -1.0f, 0.0f); + mPolygons[5].mPlane.d = -mHalfSide.y; + + mPolygons[4].mMinIndex = 0; + mPolygons[5].mMinIndex = 2; +// mPolygons[4].mMinObsolete = -mHalfSide.y; +// mPolygons[5].mMinObsolete = -mHalfSide.y; + + PX_ASSERT(mPolygons[4].getMin(mVertices) == -mHalfSide.y); + PX_ASSERT(mPolygons[5].getMin(mVertices) == -mHalfSide.y); + + // Z axis + mPolygons[2].mPlane.n = PxVec3(0.f, 0.0f, 1.0f); + mPolygons[2].mPlane.d = -mHalfSide.z; + mPolygons[0].mPlane.n = PxVec3(0.0f, 0.0f, -1.0f); + mPolygons[0].mPlane.d = -mHalfSide.z; + + mPolygons[2].mMinIndex = 0; + mPolygons[0].mMinIndex = 4; +// mPolygons[2].mMinObsolete = -mHalfSide.z; +// mPolygons[0].mMinObsolete = -mHalfSide.z; + PX_ASSERT(mPolygons[2].getMin(mVertices) == -mHalfSide.z); + PX_ASSERT(mPolygons[0].getMin(mVertices) == -mHalfSide.z); +} + +void PolygonalBox::getPolygonalData(PolygonalData* PX_RESTRICT dst) const +{ + dst->mCenter = PxVec3(0.0f, 0.0f, 0.0f); + dst->mNbVerts = 8; + dst->mNbPolygons = 6; + dst->mPolygons = mPolygons; + dst->mNbEdges = 0; + dst->mVerts = mVertices; + dst->mPolygonVertexRefs = gPxcBoxPolygonData; + dst->mFacesByEdges = NULL; + dst->mInternal.mRadius = 0.0f; + dst->mInternal.mExtents[0] = 0.0f; + dst->mInternal.mExtents[1] = 0.0f; + dst->mInternal.mExtents[2] = 0.0f; +// dst->mBigData = NULL; + dst->mHalfSide = &mHalfSide; + dst->mProjectHull = HullProjectionCB_Box; + dst->mSelectClosestEdgeCB = SelectClosestEdgeCB_Box; +} diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h new file mode 100644 index 000000000..8d6f81d0b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h @@ -0,0 +1,100 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SHAPECONVEX_H +#define GU_SHAPECONVEX_H + +#include "GuConvexMeshData.h" +#include "CmScaling.h" + +namespace physx +{ +namespace Gu +{ + struct PolygonalData; + typedef void (*HullPrefetchCB) (PxU32 numVerts, const PxVec3* PX_RESTRICT verts); + typedef void (*HullProjectionCB) (const PolygonalData& data, const PxVec3& dir, const Cm::Matrix34& world2hull, const Cm::FastVertex2ShapeScaling& scaling, PxReal& minimum, PxReal& maximum); + typedef PxU32 (*SelectClosestEdgeCB) (const PolygonalData& data, const Cm::FastVertex2ShapeScaling& scaling, const PxVec3& localDirection); + + struct PolygonalData + { + // Data + PxVec3 mCenter; + PxU32 mNbVerts; + PxU32 mNbPolygons; + PxU32 mNbEdges; + const Gu::HullPolygonData* mPolygons; + const PxVec3* mVerts; + const PxU8* mPolygonVertexRefs; + const PxU8* mFacesByEdges; + const PxU16* mVerticesByEdges; + + Gu::InternalObjectsData mInternal; + union + { + const Gu::BigConvexRawData* mBigData; // Only for big convexes + const PxVec3* mHalfSide; // Only for boxes + }; + + // Code + HullProjectionCB mProjectHull; + SelectClosestEdgeCB mSelectClosestEdgeCB; + + PX_FORCE_INLINE const PxU8* getPolygonVertexRefs(const Gu::HullPolygonData& poly) const + { + return mPolygonVertexRefs + poly.mVRef8; + } + }; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + class PX_PHYSX_COMMON_API PolygonalBox + { + public: + PolygonalBox(const PxVec3& halfSide); + + void getPolygonalData(PolygonalData* PX_RESTRICT dst) const; + + const PxVec3& mHalfSide; + PxVec3 mVertices[8]; + Gu::HullPolygonData mPolygons[6]; + private: + PolygonalBox& operator=(const PolygonalBox&); + }; +#if PX_VC + #pragma warning(pop) +#endif + + void getPolygonalData_Convex(PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Cm::FastVertex2ShapeScaling& scaling); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp new file mode 100644 index 000000000..e564e083e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuDistancePointBox.h" + +using namespace physx; + +PxReal Gu::distancePointBoxSquared( const PxVec3& point, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxVec3* boxParam) +{ + // Compute coordinates of point in box coordinate system + const PxVec3 diff = point - boxOrigin; + + PxVec3 closest( boxBase.column0.dot(diff), + boxBase.column1.dot(diff), + boxBase.column2.dot(diff)); + + // Project test point onto box + PxReal sqrDistance = 0.0f; + for(PxU32 ax=0; ax<3; ax++) + { + if(closest[ax] < -boxExtent[ax]) + { + const PxReal delta = closest[ax] + boxExtent[ax]; + sqrDistance += delta*delta; + closest[ax] = -boxExtent[ax]; + } + else if(closest[ax] > boxExtent[ax]) + { + const PxReal delta = closest[ax] - boxExtent[ax]; + sqrDistance += delta*delta; + closest[ax] = boxExtent[ax]; + } + } + + if(boxParam) *boxParam = closest; + + return sqrDistance; +} diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h new file mode 100644 index 000000000..8ebcc3e51 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_BOX_H +#define GU_DISTANCE_POINT_BOX_H + +#include "GuBox.h" + +namespace physx +{ +namespace Gu +{ + /** + Return the square of the minimum distance from the surface of the box to the given point. + \param point The point + \param boxOrigin The origin of the box + \param boxExtent The extent of the box + \param boxBase The orientation of the box + \param boxParam Set to coordinates of the closest point on the box in its local space + */ + PxReal distancePointBoxSquared( const PxVec3& point, + const PxVec3& boxOrigin, + const PxVec3& boxExtent, + const PxMat33& boxBase, + PxVec3* boxParam=NULL); + + /** + Return the square of the minimum distance from the surface of the box to the given point. + \param point The point + \param box The box + \param boxParam Set to coordinates of the closest point on the box in its local space + */ + PX_FORCE_INLINE PxReal distancePointBoxSquared( const PxVec3& point, + const Gu::Box& box, + PxVec3* boxParam=NULL) + { + return distancePointBoxSquared(point, box.center, box.extents, box.rot, boxParam); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h new file mode 100644 index 000000000..a441cde18 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_SEGMENT_H +#define GU_DISTANCE_POINT_SEGMENT_H + +#include "PxPhysXCommonConfig.h" +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + // dir = p1 - p0 + PX_FORCE_INLINE PxReal distancePointSegmentSquaredInternal(const PxVec3& p0, const PxVec3& dir, const PxVec3& point, PxReal* param=NULL) + { + PxVec3 diff = point - p0; + PxReal fT = diff.dot(dir); + + if(fT<=0.0f) + { + fT = 0.0f; + } + else + { + const PxReal sqrLen = dir.magnitudeSquared(); + if(fT>=sqrLen) + { + fT = 1.0f; + diff -= dir; + } + else + { + fT /= sqrLen; + diff -= fT*dir; + } + } + + if(param) + *param = fT; + + return diff.magnitudeSquared(); + } + + /** + A segment is defined by S(t) = mP0 * (1 - t) + mP1 * t, with 0 <= t <= 1 + Alternatively, a segment is S(t) = Origin + t * Direction for 0 <= t <= 1. + Direction is not necessarily unit length. The end points are Origin = mP0 and Origin + Direction = mP1. + */ + PX_FORCE_INLINE PxReal distancePointSegmentSquared(const PxVec3& p0, const PxVec3& p1, const PxVec3& point, PxReal* param=NULL) + { + return distancePointSegmentSquaredInternal(p0, p1 - p0, point, param); + } + + PX_INLINE PxReal distancePointSegmentSquared(const Gu::Segment& segment, const PxVec3& point, PxReal* param=NULL) + { + return distancePointSegmentSquared(segment.p0, segment.p1, point, param); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp new file mode 100644 index 000000000..b86a49bf8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp @@ -0,0 +1,353 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec3.h" +#include "GuDistancePointTriangle.h" +#include "GuDistancePointTriangleSIMD.h" + +using namespace physx; + +// Based on Christer Ericson's book +PxVec3 Gu::closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t) +{ + // Check if P in vertex region outside A + const PxVec3 ab = b - a; + const PxVec3 ac = c - a; + const PxVec3 ap = p - a; + const float d1 = ab.dot(ap); + const float d2 = ac.dot(ap); + if(d1<=0.0f && d2<=0.0f) + { + s = 0.0f; + t = 0.0f; + return a; // Barycentric coords 1,0,0 + } + + // Check if P in vertex region outside B + const PxVec3 bp = p - b; + const float d3 = ab.dot(bp); + const float d4 = ac.dot(bp); + if(d3>=0.0f && d4<=d3) + { + s = 1.0f; + t = 0.0f; + return b; // Barycentric coords 0,1,0 + } + + // Check if P in edge region of AB, if so return projection of P onto AB + const float vc = d1*d4 - d3*d2; + if(vc<=0.0f && d1>=0.0f && d3<=0.0f) + { + const float v = d1 / (d1 - d3); + s = v; + t = 0.0f; + return a + v * ab; // barycentric coords (1-v, v, 0) + } + + // Check if P in vertex region outside C + const PxVec3 cp = p - c; + const float d5 = ab.dot(cp); + const float d6 = ac.dot(cp); + if(d6>=0.0f && d5<=d6) + { + s = 0.0f; + t = 1.0f; + return c; // Barycentric coords 0,0,1 + } + + // Check if P in edge region of AC, if so return projection of P onto AC + const float vb = d5*d2 - d1*d6; + if(vb<=0.0f && d2>=0.0f && d6<=0.0f) + { + const float w = d2 / (d2 - d6); + s = 0.0f; + t = w; + return a + w * ac; // barycentric coords (1-w, 0, w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + const float va = d3*d6 - d5*d4; + if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f) + { + const float w = (d4-d3) / ((d4 - d3) + (d5-d6)); + s = 1.0f-w; + t = w; + return b + w * (c-b); // barycentric coords (0, 1-w, w) + } + + // P inside face region. Compute Q through its barycentric coords (u,v,w) + const float denom = 1.0f / (va + vb + vc); + const float v = vb * denom; + const float w = vc * denom; + s = v; + t = w; + return a + ab*v + ac*w; +} + +//Ps::aos::FloatV Gu::distancePointTriangleSquared( const Ps::aos::Vec3VArg p, +// const Ps::aos::Vec3VArg a, +// const Ps::aos::Vec3VArg b, +// const Ps::aos::Vec3VArg c, +// Ps::aos::FloatV& u, +// Ps::aos::FloatV& v, +// Ps::aos::Vec3V& closestP) +//{ +// using namespace Ps::aos; +// +// const FloatV zero = FZero(); +// const FloatV one = FOne(); +// //const Vec3V zero = V3Zero(); +// const Vec3V ab = V3Sub(b, a); +// const Vec3V ac = V3Sub(c, a); +// const Vec3V bc = V3Sub(c, b); +// const Vec3V ap = V3Sub(p, a); +// const Vec3V bp = V3Sub(p, b); +// const Vec3V cp = V3Sub(p, c); +// +// const FloatV d1 = V3Dot(ab, ap); // snom +// const FloatV d2 = V3Dot(ac, ap); // tnom +// const FloatV d3 = V3Dot(ab, bp); // -sdenom +// const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 +// const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 +// const FloatV d6 = V3Dot(ac, cp); // -tdenom +// const FloatV unom = FSub(d4, d3); +// const FloatV udenom = FSub(d5, d6); +// +// //check if p in vertex region outside a +// const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 +// const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 +// const BoolV con0 = BAnd(con00, con01); // vertex region a +// const FloatV u0 = zero; +// const FloatV v0 = zero; +// +// //check if p in vertex region outside b +// const BoolV con10 = FIsGrtrOrEq(d3, zero); +// const BoolV con11 = FIsGrtrOrEq(d3, d4); +// const BoolV con1 = BAnd(con10, con11); // vertex region b +// const FloatV u1 = one; +// const FloatV v1 = zero; +// +// //check if p in vertex region outside c +// const BoolV con20 = FIsGrtrOrEq(d6, zero); +// const BoolV con21 = FIsGrtrOrEq(d6, d5); +// const BoolV con2 = BAnd(con20, con21); // vertex region c +// const FloatV u2 = zero; +// const FloatV v2 = one; +// +// //check if p in edge region of AB +// const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); +// +// const BoolV con30 = FIsGrtr(zero, vc); +// const BoolV con31 = FIsGrtrOrEq(d1, zero); +// const BoolV con32 = FIsGrtr(zero, d3); +// const BoolV con3 = BAnd(con30, BAnd(con31, con32)); +// const FloatV sScale = FDiv(d1, FSub(d1, d3)); +// const Vec3V closest3 = V3Add(a, V3Scale(ab, sScale)); +// const FloatV u3 = sScale; +// const FloatV v3 = zero; +// +// //check if p in edge region of BC +// const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); +// const BoolV con40 = FIsGrtr(zero, va); +// const BoolV con41 = FIsGrtrOrEq(d4, d3); +// const BoolV con42 = FIsGrtrOrEq(d5, d6); +// const BoolV con4 = BAnd(con40, BAnd(con41, con42)); +// const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); +// const Vec3V closest4 = V3Add(b, V3Scale(bc, uScale)); +// const FloatV u4 = FSub(one, uScale); +// const FloatV v4 = uScale; +// +// //check if p in edge region of AC +// const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); +// const BoolV con50 = FIsGrtr(zero, vb); +// const BoolV con51 = FIsGrtrOrEq(d2, zero); +// const BoolV con52 = FIsGrtr(zero, d6); +// const BoolV con5 = BAnd(con50, BAnd(con51, con52)); +// const FloatV tScale = FDiv(d2, FSub(d2, d6)); +// const Vec3V closest5 = V3Add(a, V3Scale(ac, tScale)); +// const FloatV u5 = zero; +// const FloatV v5 = tScale; +// +// //P must project inside face region. Compute Q using Barycentric coordinates +// const FloatV denom = FRecip(FAdd(va, FAdd(vb, vc))); +// const FloatV t = FMul(vb, denom); +// const FloatV w = FMul(vc, denom); +// const Vec3V bCom = V3Scale(ab, t); +// const Vec3V cCom = V3Scale(ac, w); +// const Vec3V closest6 = V3Add(a, V3Add(bCom, cCom)); +// const FloatV u6 = t; +// const FloatV v6 = w; +// +// const Vec3V closest= V3Sel(con0, a, V3Sel(con1, b, V3Sel(con2, c, V3Sel(con3, closest3, V3Sel(con4, closest4, V3Sel(con5, closest5, closest6)))))); +// u = FSel(con0, u0, FSel(con1, u1, FSel(con2, u2, FSel(con3, u3, FSel(con4, u4, FSel(con5, u5, u6)))))); +// v = FSel(con0, v0, FSel(con1, v1, FSel(con2, v2, FSel(con3, v3, FSel(con4, v4, FSel(con5, v5, v6)))))); +// closestP = closest; +// +// const Vec3V vv = V3Sub(p, closest); +// +// return V3Dot(vv, vv); +//} + + +Ps::aos::FloatV Gu::distancePointTriangleSquared( const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& u, + Ps::aos::FloatV& v, + Ps::aos::Vec3V& closestP) +{ + using namespace Ps::aos; + + const FloatV zero = FZero(); + const FloatV one = FOne(); + //const Vec3V zero = V3Zero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V bp = V3Sub(p, b); + const Vec3V cp = V3Sub(p, c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + + if(BAllEqTTTT(con0)) + { + u = zero; + v = zero; + const Vec3V vv = V3Sub(p, a); + closestP = a; + return V3Dot(vv, vv); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + u = one; + v = zero; + const Vec3V vv = V3Sub(p, b); + closestP = b; + return V3Dot(vv, vv); + } + + //check if p in vertex region outside c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if(BAllEqTTTT(con2)) + { + u = zero; + v = one; + const Vec3V vv = V3Sub(p, c); + closestP = c; + return V3Dot(vv, vv); + } + + //check if p in edge region of AB + const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); + + const BoolV con30 = FIsGrtr(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtr(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + if(BAllEqTTTT(con3)) + { + const FloatV sScale = FDiv(d1, FSub(d1, d3)); + const Vec3V closest3 = V3Add(a, V3Scale(ab, sScale)); + u = sScale; + v = zero; + const Vec3V vv = V3Sub(p, closest3); + closestP = closest3; + return V3Dot(vv, vv); + } + + //check if p in edge region of BC + const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); + const BoolV con40 = FIsGrtr(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + if(BAllEqTTTT(con4)) + { + const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); + const Vec3V closest4 = V3Add(b, V3Scale(bc, uScale)); + u = FSub(one, uScale); + v = uScale; + const Vec3V vv = V3Sub(p, closest4); + closestP = closest4; + return V3Dot(vv, vv); + } + + //check if p in edge region of AC + const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); + const BoolV con50 = FIsGrtr(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtr(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + if(BAllEqTTTT(con5)) + { + const FloatV tScale = FDiv(d2, FSub(d2, d6)); + const Vec3V closest5 = V3Add(a, V3Scale(ac, tScale)); + u = zero; + v = tScale; + const Vec3V vv = V3Sub(p, closest5); + closestP = closest5; + return V3Dot(vv, vv); + } + + //P must project inside face region. Compute Q using Barycentric coordinates + const FloatV denom = FRecip(FAdd(va, FAdd(vb, vc))); + const FloatV t = FMul(vb, denom); + const FloatV w = FMul(vc, denom); + const Vec3V bCom = V3Scale(ab, t); + const Vec3V cCom = V3Scale(ac, w); + const Vec3V closest6 = V3Add(a, V3Add(bCom, cCom)); + u = t; + v = w; + closestP = closest6; + + const Vec3V vv = V3Sub(p, closest6); + + return V3Dot(vv, vv); +} diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h new file mode 100644 index 000000000..279991f54 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h @@ -0,0 +1,125 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_TRIANGLE_H +#define GU_DISTANCE_POINT_TRIANGLE_H + +#include "foundation/PxVec3.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + // PT: special version: + // - inlined + // - doesn't compute (s,t) output params + // - expects precomputed edges in input + PX_FORCE_INLINE PxVec3 closestPtPointTriangle2(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, const PxVec3& ab, const PxVec3& ac) + { + // Check if P in vertex region outside A + //const PxVec3 ab = b - a; + //const PxVec3 ac = c - a; + const PxVec3 ap = p - a; + const float d1 = ab.dot(ap); + const float d2 = ac.dot(ap); + if(d1<=0.0f && d2<=0.0f) + return a; // Barycentric coords 1,0,0 + + // Check if P in vertex region outside B + const PxVec3 bp = p - b; + const float d3 = ab.dot(bp); + const float d4 = ac.dot(bp); + if(d3>=0.0f && d4<=d3) + return b; // Barycentric coords 0,1,0 + + // Check if P in edge region of AB, if so return projection of P onto AB + const float vc = d1*d4 - d3*d2; + if(vc<=0.0f && d1>=0.0f && d3<=0.0f) + { + const float v = d1 / (d1 - d3); + return a + v * ab; // barycentric coords (1-v, v, 0) + } + + // Check if P in vertex region outside C + const PxVec3 cp = p - c; + const float d5 = ab.dot(cp); + const float d6 = ac.dot(cp); + if(d6>=0.0f && d5<=d6) + return c; // Barycentric coords 0,0,1 + + // Check if P in edge region of AC, if so return projection of P onto AC + const float vb = d5*d2 - d1*d6; + if(vb<=0.0f && d2>=0.0f && d6<=0.0f) + { + const float w = d2 / (d2 - d6); + return a + w * ac; // barycentric coords (1-w, 0, w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + const float va = d3*d6 - d5*d4; + if(va<=0.0f && (d4-d3)>=0.0f && (d5-d6)>=0.0f) + { + const float w = (d4-d3) / ((d4 - d3) + (d5-d6)); + return b + w * (c-b); // barycentric coords (0, 1-w, w) + } + + // P inside face region. Compute Q through its barycentric coords (u,v,w) + const float denom = 1.0f / (va + vb + vc); + const float v = vb * denom; + const float w = vc * denom; + return a + ab*v + ac*w; + } + + PX_PHYSX_COMMON_API PxVec3 closestPtPointTriangle(const PxVec3& p, const PxVec3& a, const PxVec3& b, const PxVec3& c, float& s, float& t); + + PX_FORCE_INLINE PxReal distancePointTriangleSquared(const PxVec3& point, + const PxVec3& triangleOrigin, + const PxVec3& triangleEdge0, + const PxVec3& triangleEdge1, + PxReal* param0=NULL, + PxReal* param1=NULL) + { + const PxVec3 pt0 = triangleEdge0 + triangleOrigin; + const PxVec3 pt1 = triangleEdge1 + triangleOrigin; + float s,t; + const PxVec3 cp = closestPtPointTriangle(point, triangleOrigin, pt0, pt1, s, t); + if(param0) + *param0 = s; + if(param1) + *param1 = t; + return (cp - point).magnitudeSquared(); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h new file mode 100644 index 000000000..587952c87 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_POINT_TRIANGLE_SIMD_H +#define GU_DISTANCE_POINT_TRIANGLE_SIMD_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + + PX_PHYSX_COMMON_API Ps::aos::FloatV distancePointTriangleSquared( const Ps::aos::Vec3VArg point, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& u, + Ps::aos::FloatV& v, + Ps::aos::Vec3V& closestP); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp new file mode 100644 index 000000000..f97b039cf --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp @@ -0,0 +1,549 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuDistanceSegmentBox.h" +#include "GuDistancePointBox.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistancePointSegment.h" +#include "GuIntersectionRayBox.h" + +using namespace physx; + +static void face(unsigned int i0, unsigned int i1, unsigned int i2, PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, const PxVec3& rkPmE, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxVec3 kPpE; + PxReal fLSqr, fInv, fTmp, fParam, fT, fDelta; + + kPpE[i1] = rkPnt[i1] + extents[i1]; + kPpE[i2] = rkPnt[i2] + extents[i2]; + if(rkDir[i0]*kPpE[i1] >= rkDir[i1]*rkPmE[i0]) + { + if(rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0]) + { + // v[i1] >= -e[i1], v[i2] >= -e[i2] (distance = 0) + if(pfLParam) + { + rkPnt[i0] = extents[i0]; + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= rkDir[i1]*rkPmE[i0]*fInv; + rkPnt[i2] -= rkDir[i2]*rkPmE[i0]*fInv; + *pfLParam = -rkPmE[i0]*fInv; + } + } + else + { + // v[i1] >= -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp <= 2.0f*fLSqr*extents[i1]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } + } + else + { + if ( rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0] ) + { + // v[i1] < -e[i1], v[i2] >= -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + } + else + { + // v[i1] < -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0]+rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp >= 0.0f) + { + // v[i1]-edge is closest + if ( fTmp <= 2.0f*fLSqr*extents[i1] ) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + return; + } + + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp >= 0.0f) + { + // v[i2]-edge is closest + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + return; + } + + // (v[i1],v[i2])-corner is closest + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } +} + +static void caseNoZeros(PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxVec3 kPmE(rkPnt.x - extents.x, rkPnt.y - extents.y, rkPnt.z - extents.z); + + PxReal fProdDxPy, fProdDyPx, fProdDzPx, fProdDxPz, fProdDzPy, fProdDyPz; + + fProdDxPy = rkDir.x*kPmE.y; + fProdDyPx = rkDir.y*kPmE.x; + if(fProdDyPx >= fProdDxPy) + { + fProdDzPx = rkDir.z*kPmE.x; + fProdDxPz = rkDir.x*kPmE.z; + if(fProdDzPx >= fProdDxPz) + { + // line intersects x = e0 + face(0, 1, 2, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } + else + { + fProdDzPy = rkDir.z*kPmE.y; + fProdDyPz = rkDir.y*kPmE.z; + if(fProdDzPy >= fProdDyPz) + { + // line intersects y = e1 + face(1, 2, 0, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } +} + +static void case0(unsigned int i0, unsigned int i1, unsigned int i2, PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxReal fPmE0 = rkPnt[i0] - extents[i0]; + PxReal fPmE1 = rkPnt[i1] - extents[i1]; + PxReal fProd0 = rkDir[i1]*fPmE0; + PxReal fProd1 = rkDir[i0]*fPmE1; + PxReal fDelta, fInvLSqr, fInv; + + if(fProd0 >= fProd1) + { + // line intersects P[i0] = e[i0] + rkPnt[i0] = extents[i0]; + + PxReal fPpE1 = rkPnt[i1] + extents[i1]; + fDelta = fProd0 - rkDir[i0]*fPpE1; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i1] = -extents[i1]; + *pfLParam = -(rkDir[i0]*fPmE0+rkDir[i1]*fPpE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= fProd0*fInv; + *pfLParam = -fPmE0*fInv; + } + } + } + else + { + // line intersects P[i1] = e[i1] + rkPnt[i1] = extents[i1]; + + PxReal fPpE0 = rkPnt[i0] + extents[i0]; + fDelta = fProd1 - rkDir[i1]*fPpE0; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i0] = -extents[i0]; + *pfLParam = -(rkDir[i0]*fPpE0+rkDir[i1]*fPmE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i1]; + rkPnt[i0] -= fProd1*fInv; + *pfLParam = -fPmE1*fInv; + } + } + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = -extents[i2]; + } + else if ( rkPnt[i2] > extents[i2] ) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void case00(unsigned int i0, unsigned int i1, unsigned int i2, PxVec3& rkPnt, const PxVec3& rkDir, const PxVec3& extents, PxReal* pfLParam, PxReal& rfSqrDistance) +{ + PxReal fDelta; + + if(pfLParam) + *pfLParam = (extents[i0] - rkPnt[i0])/rkDir[i0]; + + rkPnt[i0] = extents[i0]; + + if(rkPnt[i1] < -extents[i1]) + { + fDelta = rkPnt[i1] + extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = -extents[i1]; + } + else if(rkPnt[i1] > extents[i1]) + { + fDelta = rkPnt[i1] - extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = extents[i1]; + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = -extents[i2]; + } + else if(rkPnt[i2] > extents[i2]) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void case000(PxVec3& rkPnt, const PxVec3& extents, PxReal& rfSqrDistance) +{ + PxReal fDelta; + + if(rkPnt.x < -extents.x) + { + fDelta = rkPnt.x + extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = -extents.x; + } + else if(rkPnt.x > extents.x) + { + fDelta = rkPnt.x - extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = extents.x; + } + + if(rkPnt.y < -extents.y) + { + fDelta = rkPnt.y + extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = -extents.y; + } + else if(rkPnt.y > extents.y) + { + fDelta = rkPnt.y - extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = extents.y; + } + + if(rkPnt.z < -extents.z) + { + fDelta = rkPnt.z + extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = -extents.z; + } + else if(rkPnt.z > extents.z) + { + fDelta = rkPnt.z - extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = extents.z; + } +} + +//! Compute the smallest distance from the (infinite) line to the box. +static PxReal distanceLineBoxSquared(const PxVec3& lineOrigin, const PxVec3& lineDirection, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxReal* lineParam, + PxVec3* boxParam) +{ + const PxVec3& axis0 = boxBase.column0; + const PxVec3& axis1 = boxBase.column1; + const PxVec3& axis2 = boxBase.column2; + + // compute coordinates of line in box coordinate system + const PxVec3 diff = lineOrigin - boxOrigin; + PxVec3 pnt(diff.dot(axis0), diff.dot(axis1), diff.dot(axis2)); + PxVec3 dir(lineDirection.dot(axis0), lineDirection.dot(axis1), lineDirection.dot(axis2)); + + // Apply reflections so that direction vector has nonnegative components. + bool reflect[3]; + for(unsigned int i=0;i<3;i++) + { + if(dir[i]<0.0f) + { + pnt[i] = -pnt[i]; + dir[i] = -dir[i]; + reflect[i] = true; + } + else + { + reflect[i] = false; + } + } + + PxReal sqrDistance = 0.0f; + + if(dir.x>0.0f) + { + if(dir.y>0.0f) + { + if(dir.z>0.0f) caseNoZeros(pnt, dir, boxExtent, lineParam, sqrDistance); // (+,+,+) + else case0(0, 1, 2, pnt, dir, boxExtent, lineParam, sqrDistance); // (+,+,0) + } + else + { + if(dir.z>0.0f) case0(0, 2, 1, pnt, dir, boxExtent, lineParam, sqrDistance); // (+,0,+) + else case00(0, 1, 2, pnt, dir, boxExtent, lineParam, sqrDistance); // (+,0,0) + } + } + else + { + if(dir.y>0.0f) + { + if(dir.z>0.0f) case0(1, 2, 0, pnt, dir, boxExtent, lineParam, sqrDistance); // (0,+,+) + else case00(1, 0, 2, pnt, dir, boxExtent, lineParam, sqrDistance); // (0,+,0) + } + else + { + if(dir.z>0.0f) case00(2, 0, 1, pnt, dir, boxExtent, lineParam, sqrDistance); // (0,0,+) + else + { + case000(pnt, boxExtent, sqrDistance); // (0,0,0) + if(lineParam) + *lineParam = 0.0f; + } + } + } + + if(boxParam) + { + // undo reflections + for(unsigned int i=0;i<3;i++) + { + if(reflect[i]) + pnt[i] = -pnt[i]; + } + + *boxParam = pnt; + } + + return sqrDistance; +} + +//! Compute the smallest distance from the (finite) line segment to the box. +PxReal Gu::distanceSegmentBoxSquared( const PxVec3& segmentPoint0, const PxVec3& segmentPoint1, + const PxVec3& boxOrigin, const PxVec3& boxExtent, const PxMat33& boxBase, + PxReal* segmentParam, + PxVec3* boxParam) +{ + // compute coordinates of line in box coordinate system + + PxReal lp; + PxVec3 bp; + PxReal sqrDistance = distanceLineBoxSquared(segmentPoint0, segmentPoint1 - segmentPoint0, boxOrigin, boxExtent, boxBase, &lp, &bp); + if(lp>=0.0f) + { + if(lp<=1.0f) + { + if(segmentParam) + *segmentParam = lp; + if(boxParam) + *boxParam = bp; + return sqrDistance; + } + else + { + if(segmentParam) + *segmentParam = 1.0f; + return Gu::distancePointBoxSquared(segmentPoint1, boxOrigin, boxExtent, boxBase, boxParam); + } + } + else + { + if(segmentParam) + *segmentParam = 0.0f; + return Gu::distancePointBoxSquared(segmentPoint0, boxOrigin, boxExtent, boxBase, boxParam); + } +} diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp new file mode 100644 index 000000000..3e97cc884 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp @@ -0,0 +1,576 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentSegmentSIMD.h" + +using namespace physx; +using namespace Ps; +using namespace aos; + +static const float ZERO_TOLERANCE = 1e-06f; + +// S0 = origin + extent * dir; +// S1 = origin - extent * dir; +PxReal Gu::distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& dir0, PxReal extent0, + const PxVec3& origin1, const PxVec3& dir1, PxReal extent1, + PxReal* param0, PxReal* param1) +{ + const PxVec3 kDiff = origin0 - origin1; + const PxReal fA01 = -dir0.dot(dir1); + const PxReal fB0 = kDiff.dot(dir0); + const PxReal fB1 = -kDiff.dot(dir1); + const PxReal fC = kDiff.magnitudeSquared(); + const PxReal fDet = PxAbs(1.0f - fA01*fA01); + PxReal fS0, fS1, fSqrDist, fExtDet0, fExtDet1, fTmpS0, fTmpS1; + + if (fDet >= ZERO_TOLERANCE) + { + // segments are not parallel + fS0 = fA01*fB1-fB0; + fS1 = fA01*fB0-fB1; + fExtDet0 = extent0*fDet; + fExtDet1 = extent1*fDet; + + if (fS0 >= -fExtDet0) + { + if (fS0 <= fExtDet0) + { + if (fS1 >= -fExtDet1) + { + if (fS1 <= fExtDet1) // region 0 (interior) + { + // minimum at two interior points of 3D lines + PxReal fInvDet = 1.0f/fDet; + fS0 *= fInvDet; + fS1 *= fInvDet; + fSqrDist = fS0*(fS0+fA01*fS1+2.0f*fB0) + fS1*(fA01*fS0+fS1+2.0f*fB1)+fC; + } + else // region 3 (side) + { + fS1 = extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + } + } + else // region 7 (side) + { + fS1 = -extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + } + } + else + { + if (fS1 >= -fExtDet1) + { + if (fS1 <= fExtDet1) // region 1 (side) + { + fS0 = extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0)+fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + else // region 2 (corner) + { + fS1 = extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + else // region 8 (corner) + { + fS1 = -extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 < -extent0) + { + fS0 = -extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 <= extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 > extent1) + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 >= -extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + } + else + { + if (fS1 >= -fExtDet1) + { + if (fS1 <= fExtDet1) // region 5 (side) + { + fS0 = -extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0)+fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + else // region 4 (corner) + { + fS1 = extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 > extent0) + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 >= -extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = -extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + else // region 6 (corner) + { + fS1 = -extent1; + fTmpS0 = -(fA01*fS1+fB0); + if (fTmpS0 > extent0) + { + fS0 = extent0; + fSqrDist = fS0*(fS0-2.0f*fTmpS0) + fS1*(fS1+2.0f*fB1)+fC; + } + else if (fTmpS0 >= -extent0) + { + fS0 = fTmpS0; + fSqrDist = -fS0*fS0+fS1*(fS1+2.0f*fB1)+fC; + } + else + { + fS0 = -extent0; + fTmpS1 = -(fA01*fS0+fB1); + if (fTmpS1 < -extent1) + { + fS1 = -extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + else if (fTmpS1 <= extent1) + { + fS1 = fTmpS1; + fSqrDist = -fS1*fS1+fS0*(fS0+2.0f*fB0) + fC; + } + else + { + fS1 = extent1; + fSqrDist = fS1*(fS1-2.0f*fTmpS1) + fS0*(fS0+2.0f*fB0)+fC; + } + } + } + } + } + else + { + // The segments are parallel. + PxReal fE0pE1 = extent0 + extent1; + PxReal fSign = (fA01 > 0.0f ? -1.0f : 1.0f); + PxReal b0Avr = 0.5f*(fB0 - fSign*fB1); + PxReal fLambda = -b0Avr; + if(fLambda < -fE0pE1) + { + fLambda = -fE0pE1; + } + else if(fLambda > fE0pE1) + { + fLambda = fE0pE1; + } + + fS1 = -fSign*fLambda*extent1/fE0pE1; + fS0 = fLambda + fSign*fS1; + fSqrDist = fLambda*(fLambda + 2.0f*b0Avr) + fC; + } + + if(param0) + *param0 = fS0; + if(param1) + *param1 = fS1; + + // account for numerical round-off error + return physx::intrinsics::selectMax(0.0f, fSqrDist); +} + +PxReal Gu::distanceSegmentSegmentSquared( const PxVec3& origin0, const PxVec3& extent0, + const PxVec3& origin1, const PxVec3& extent1, + PxReal* param0, + PxReal* param1) +{ + // Some conversion is needed between the old & new code + // Old: + // segment (s0, s1) + // origin = s0 + // extent = s1 - s0 + // + // New: + // s0 = origin + extent * dir; + // s1 = origin - extent * dir; + + // dsequeira: is this really sensible? We use a highly optimized Wild Magic routine, + // then use a segment representation that requires an expensive conversion to/from... + + PxVec3 dir0 = extent0; + const PxVec3 center0 = origin0 + extent0*0.5f; + PxReal length0 = extent0.magnitude(); //AM: change to make it work for degenerate (zero length) segments. + const bool b0 = length0 != 0.0f; + PxReal oneOverLength0 = 0.0f; + if(b0) + { + oneOverLength0 = 1.0f / length0; + dir0 *= oneOverLength0; + length0 *= 0.5f; + } + + PxVec3 dir1 = extent1; + const PxVec3 center1 = origin1 + extent1*0.5f; + PxReal length1 = extent1.magnitude(); + const bool b1 = length1 != 0.0f; + PxReal oneOverLength1 = 0.0f; + if(b1) + { + oneOverLength1 = 1.0f / length1; + dir1 *= oneOverLength1; + length1 *= 0.5f; + } + + // the return param vals have -extent = s0, extent = s1 + + const PxReal d2 = distanceSegmentSegmentSquared(center0, dir0, length0, + center1, dir1, length1, + param0, param1); + + //ML : This is wrong for some reason, I guess it has precision issue + //// renormalize into the 0 = s0, 1 = s1 range + //if (param0) + // *param0 = b0 ? ((*param0) * oneOverLength0 * 0.5f + 0.5f) : 0.0f; + //if (param1) + // *param1 = b1 ? ((*param1) * oneOverLength1 * 0.5f + 0.5f) : 0.0f; + + if(param0) + *param0 = b0 ? ((length0 + (*param0))*oneOverLength0) : 0.0f; + if(param1) + *param1 = b1 ? ((length1 + (*param1))*oneOverLength1) : 0.0f; + + return d2; +} + +/* + S0 = origin + extent * dir; + S1 = origin + extent * dir; + dir is the vector from start to end point + p1 is the start point of segment1 + d1 is the direction vector(q1 - p1) + p2 is the start point of segment2 + d2 is the direction vector(q2 - p2) +*/ + +FloatV Gu::distanceSegmentSegmentSquared( const Vec3VArg p1, + const Vec3VArg d1, + const Vec3VArg p2, + const Vec3VArg d2, + FloatV& s, + FloatV& t) +{ + const FloatV zero = FZero(); + const FloatV one = FOne(); + const FloatV eps = FEps(); + + const Vec3V r = V3Sub(p1, p2); + const Vec4V combinedDot = V3Dot4(d1, d1, d2, d2, d1, d2, d1, r); + const Vec4V combinedRecip = V4Sel(V4IsGrtr(combinedDot, V4Splat(eps)), V4Recip(combinedDot), V4Splat(zero)); + const FloatV a = V4GetX(combinedDot); + const FloatV e = V4GetY(combinedDot); + const FloatV b = V4GetZ(combinedDot); + const FloatV c = V4GetW(combinedDot); + const FloatV aRecip = V4GetX(combinedRecip);//FSel(FIsGrtr(a, eps), FRecip(a), zero); + const FloatV eRecip = V4GetY(combinedRecip);//FSel(FIsGrtr(e, eps), FRecip(e), zero); + + const FloatV f = V3Dot(d2, r); + + /* + s = (b*f - c*e)/(a*e - b*b); + t = (a*f - b*c)/(a*e - b*b); + + s = (b*t - c)/a; + t = (b*s + f)/e; + */ + + //if segments not parallel, the general non-degenerated case, compute closest point on two segments and clamp to segment1 + const FloatV denom = FSub(FMul(a, e), FMul(b, b)); + const FloatV temp = FSub(FMul(b, f), FMul(c, e)); + const FloatV s0 = FClamp(FDiv(temp, denom), zero, one); + + //if segment is parallel, demon < eps + const BoolV con2 = FIsGrtr(eps, denom);//FIsEq(denom, zero); + const FloatV sTmp = FSel(con2, FHalf(), s0); + + //compute point on segment2 closest to segment1 + //const FloatV tTmp = FMul(FAdd(FMul(b, sTmp), f), eRecip); + const FloatV tTmp = FMul(FScaleAdd(b, sTmp, f), eRecip); + + //if t is in [zero, one], done. otherwise clamp t + const FloatV t2 = FClamp(tTmp, zero, one); + + //recompute s for the new value + const FloatV comp = FMul(FSub(FMul(b,t2), c), aRecip); + const FloatV s2 = FClamp(comp, zero, one); + + s = s2; + t = t2; + + const Vec3V closest1 = V3ScaleAdd(d1, s2, p1);//V3Add(p1, V3Scale(d1, tempS)); + const Vec3V closest2 = V3ScaleAdd(d2, t2, p2);//V3Add(p2, V3Scale(d2, tempT)); + const Vec3V vv = V3Sub(closest1, closest2); + return V3Dot(vv, vv); +} + + + + +/* + segment (p, d) and segment (p02, d02) + segment (p, d) and segment (p12, d12) + segment (p, d) and segment (p22, d22) + segment (p, d) and segment (p32, d32) +*/ +Vec4V Gu::distanceSegmentSegmentSquared4( const Vec3VArg p, const Vec3VArg d0, + const Vec3VArg p02, const Vec3VArg d02, + const Vec3VArg p12, const Vec3VArg d12, + const Vec3VArg p22, const Vec3VArg d22, + const Vec3VArg p32, const Vec3VArg d32, + Vec4V& s, Vec4V& t) +{ + const Vec4V zero = V4Zero(); + const Vec4V one = V4One(); + const Vec4V eps = V4Eps(); + const Vec4V half = V4Splat(FHalf()); + + const Vec4V d0X = V4Splat(V3GetX(d0)); + const Vec4V d0Y = V4Splat(V3GetY(d0)); + const Vec4V d0Z = V4Splat(V3GetZ(d0)); + const Vec4V pX = V4Splat(V3GetX(p)); + const Vec4V pY = V4Splat(V3GetY(p)); + const Vec4V pZ = V4Splat(V3GetZ(p)); + + Vec4V d024 = Vec4V_From_Vec3V(d02); + Vec4V d124 = Vec4V_From_Vec3V(d12); + Vec4V d224 = Vec4V_From_Vec3V(d22); + Vec4V d324 = Vec4V_From_Vec3V(d32); + + Vec4V p024 = Vec4V_From_Vec3V(p02); + Vec4V p124 = Vec4V_From_Vec3V(p12); + Vec4V p224 = Vec4V_From_Vec3V(p22); + Vec4V p324 = Vec4V_From_Vec3V(p32); + + Vec4V d0123X, d0123Y, d0123Z; + Vec4V p0123X, p0123Y, p0123Z; + + PX_TRANSPOSE_44_34(d024, d124, d224, d324, d0123X, d0123Y, d0123Z); + PX_TRANSPOSE_44_34(p024, p124, p224, p324, p0123X, p0123Y, p0123Z); + + const Vec4V rX = V4Sub(pX, p0123X); + const Vec4V rY = V4Sub(pY, p0123Y); + const Vec4V rZ = V4Sub(pZ, p0123Z); + + //TODO - store this in a transposed state and avoid so many dot products? + + const FloatV dd = V3Dot(d0, d0); + + const Vec4V e = V4MulAdd(d0123Z, d0123Z, V4MulAdd(d0123X, d0123X, V4Mul(d0123Y, d0123Y))); + const Vec4V b = V4MulAdd(d0Z, d0123Z, V4MulAdd(d0X, d0123X, V4Mul(d0Y, d0123Y))); + const Vec4V c = V4MulAdd(d0Z, rZ, V4MulAdd(d0X, rX, V4Mul(d0Y, rY))); + const Vec4V f = V4MulAdd(d0123Z, rZ, V4MulAdd(d0123X, rX, V4Mul(d0123Y, rY))); + + const Vec4V a(V4Splat(dd)); + + const Vec4V aRecip(V4Recip(a)); + const Vec4V eRecip(V4Recip(e)); + + //if segments not parallell, compute closest point on two segments and clamp to segment1 + const Vec4V denom = V4Sub(V4Mul(a, e), V4Mul(b, b)); + const Vec4V temp = V4Sub(V4Mul(b, f), V4Mul(c, e)); + const Vec4V s0 = V4Clamp(V4Div(temp, denom), zero, one); + + //test whether segments are parallel + const BoolV con2 = V4IsGrtrOrEq(eps, denom); + const Vec4V sTmp = V4Sel(con2, half, s0); + + //compute point on segment2 closest to segment1 + const Vec4V tTmp = V4Mul(V4Add(V4Mul(b, sTmp), f), eRecip); + + //if t is in [zero, one], done. otherwise clamp t + const Vec4V t2 = V4Clamp(tTmp, zero, one); + + //recompute s for the new value + const Vec4V comp = V4Mul(V4Sub(V4Mul(b,t2), c), aRecip); + const BoolV aaNearZero = V4IsGrtrOrEq(eps, a); // check if aRecip is valid (aa>eps) + const Vec4V s2 = V4Sel(aaNearZero, V4Zero(), V4Clamp(comp, zero, one)); + + /* s = V4Sel(con0, zero, V4Sel(con1, cd, s2)); + t = V4Sel(con1, zero, V4Sel(con0, cg, t2)); */ + s = s2; + t = t2; + + const Vec4V closest1X = V4MulAdd(d0X, s2, pX); + const Vec4V closest1Y = V4MulAdd(d0Y, s2, pY); + const Vec4V closest1Z = V4MulAdd(d0Z, s2, pZ); + + const Vec4V closest2X = V4MulAdd(d0123X, t2, p0123X); + const Vec4V closest2Y = V4MulAdd(d0123Y, t2, p0123Y); + const Vec4V closest2Z = V4MulAdd(d0123Z, t2, p0123Z); + + const Vec4V vvX = V4Sub(closest1X, closest2X); + const Vec4V vvY = V4Sub(closest1Y, closest2Y); + const Vec4V vvZ = V4Sub(closest1Z, closest2Z); + + const Vec4V vd = V4MulAdd(vvX, vvX, V4MulAdd(vvY, vvY, V4Mul(vvZ, vvZ))); + + return vd; +} diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h new file mode 100644 index 000000000..f5d6394ac --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_SEGMENT_SIMD_H +#define GU_DISTANCE_SEGMENT_SEGMENT_SIMD_H + +#include "common/PxPhysXCommonConfig.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + PX_PHYSX_COMMON_API Ps::aos::FloatV distanceSegmentSegmentSquared( const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg d1, const Ps::aos::Vec3VArg p2, const Ps::aos::Vec3VArg d2, + Ps::aos::FloatV& param0, + Ps::aos::FloatV& param1); + + /* + This function do four segment segment closest point test in one go + */ + Ps::aos::Vec4V distanceSegmentSegmentSquared4( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg d, + const Ps::aos::Vec3VArg p02, const Ps::aos::Vec3VArg d02, + const Ps::aos::Vec3VArg p12, const Ps::aos::Vec3VArg d12, + const Ps::aos::Vec3VArg p22, const Ps::aos::Vec3VArg d22, + const Ps::aos::Vec3VArg p32, const Ps::aos::Vec3VArg d32, + Ps::aos::Vec4V& s, Ps::aos::Vec4V& t); +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp new file mode 100644 index 000000000..c2ab4e964 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp @@ -0,0 +1,541 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "GuDistanceSegmentTriangle.h" +#include "GuDistanceSegmentTriangleSIMD.h" +#include "GuDistancePointTriangle.h" +#include "GuDistancePointTriangleSIMD.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentSegmentSIMD.h" +#include "GuBarycentricCoordinates.h" + +using namespace physx; +using namespace Gu; + +// ptchernev: +// The Magic Software code uses a relative error test for parallel case. +// The Novodex code does not presumably as an optimization. +// Since the Novodex code is working in the trunk I see no reason +// to reintroduce the relative error test here. + +// PT: this might just be because the relative error test has been added +// after we grabbed the code. I don't remember making this change. A good +// idea would be NOT to refactor Magic's code, to easily grab updated +// versions from the website............................................. + +// ptchernev: +// The code has been modified to use a relative error test since the absolute +// test would break down for small geometries. (TTP 4021) + +static PX_FORCE_INLINE void updateClosestHit( PxReal fSqrDist0, PxReal fR0, PxReal fS0, PxReal fT0, + PxReal& fSqrDist, PxReal& fR, PxReal& fS, PxReal& fT) +{ + if(fSqrDist0 < fSqrDist) + { + fSqrDist = fSqrDist0; + fR = fR0; + fS = fS0; + fT = fT0; + } +} + +PxReal Gu::distanceSegmentTriangleSquared( const PxVec3& origin, const PxVec3& dir, + const PxVec3& p0, const PxVec3& triEdge0, const PxVec3& triEdge1, + PxReal* t, PxReal* u, PxReal* v) +{ + const PxReal fA00 = dir.magnitudeSquared(); + if(fA00 < 1e-6f*1e-6f) + { + if(t) + *t = 0.0f; + return distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, u, v); + } + const PxVec3 kDiff = p0 - origin; + const PxReal fA01 = -(dir.dot(triEdge0)); + const PxReal fA02 = -(dir.dot(triEdge1)); + const PxReal fA11 = triEdge0.magnitudeSquared(); + const PxReal fA12 = triEdge0.dot(triEdge1); + const PxReal fA22 = triEdge1.dot(triEdge1); + const PxReal fB0 = -(kDiff.dot(dir)); + const PxReal fB1 = kDiff.dot(triEdge0); + const PxReal fB2 = kDiff.dot(triEdge1); + const PxReal fCof00 = fA11*fA22-fA12*fA12; + const PxReal fCof01 = fA02*fA12-fA01*fA22; + const PxReal fCof02 = fA01*fA12-fA02*fA11; + const PxReal fDet = fA00*fCof00+fA01*fCof01+fA02*fCof02; + + PxReal fSqrDist, fSqrDist0, fR, fS, fT, fR0, fS0, fT0; + + // Set up for a relative error test on the angle between ray direction + // and triangle normal to determine parallel/nonparallel status. + const PxVec3 kNormal = triEdge0.cross(triEdge1); + const PxReal fDot = kNormal.dot(dir); + if(fDot*fDot >= 1e-6f*dir.magnitudeSquared()*kNormal.magnitudeSquared()) + { + const PxReal fCof11 = fA00*fA22-fA02*fA02; + const PxReal fCof12 = fA02*fA01-fA00*fA12; + const PxReal fCof22 = fA00*fA11-fA01*fA01; + const PxReal fInvDet = fDet == 0.0f ? 0.0f : 1.0f/fDet; + const PxReal fRhs0 = -fB0*fInvDet; + const PxReal fRhs1 = -fB1*fInvDet; + const PxReal fRhs2 = -fB2*fInvDet; + + fR = fCof00*fRhs0+fCof01*fRhs1+fCof02*fRhs2; + fS = fCof01*fRhs0+fCof11*fRhs1+fCof12*fRhs2; + fT = fCof02*fRhs0+fCof12*fRhs1+fCof22*fRhs2; + + if(fR < 0.0f) + { + if(fS+fT <= 1.0f) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4m + { + // minimum on face s=0 or t=0 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); + fT0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 3m + { + // minimum on face s=0 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + } + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 5m + { + // minimum on face t=0 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 0m + { + // minimum on face r=0 + fSqrDist = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS, &fT); + fR = 0.0f; + } + } + else + { + if(fS < 0.0f) // region 2m + { + // minimum on face s=0 or s+t=1 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 6m + { + // minimum on face t=0 or s+t=1 or r=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 1m + { + // minimum on face s+t=1 or r=0 + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); + fS = 1.0f-fT; + } + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + } + else if(fR <= 1.0f) + { + if(fS+fT <= 1.0f) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4 + { + // minimum on face s=0 or t=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); + fT0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 3 + { + // minimum on face s=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + } + } + else if(fT < 0.0f) // region 5 + { + // minimum on face t=0 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + } + else // region 0 + { + // global minimum is interior, done + fSqrDist = fR*(fA00*fR+fA01*fS+fA02*fT+2.0f*fB0) + +fS*(fA01*fR+fA11*fS+fA12*fT+2.0f*fB1) + +fT*(fA02*fR+fA12*fS+fA22*fT+2.0f*fB2) + +kDiff.magnitudeSquared(); + } + } + else + { + if(fS < 0.0f) // region 2 + { + // minimum on face s=0 or s+t=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 6 + { + // minimum on face t=0 or s+t=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 1 + { + // minimum on face s+t=1 + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); + fS = 1.0f-fT; + } + } + } + else // fR > 1 + { + if(fS+fT <= 1.0f) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4p + { + // minimum on face s=0 or t=0 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR0, &fS0); + fT0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 3p + { + // minimum on face s=0 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + } + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 5p + { + // minimum on face t=0 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 0p + { + // minimum face on r=1 + const PxVec3 kPt = origin+dir; + fSqrDist = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS, &fT); + fR = 1.0f; + } + } + else + { + if(fS < 0.0f) // region 2p + { + // minimum on face s=0 or s+t=1 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR, &fT); + fS = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else if(fT < 0.0f) // region 6p + { + // minimum on face t=0 or s+t=1 or r=1 + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + else // region 1p + { + // minimum on face s+t=1 or r=1 + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1-triEdge0; + fSqrDist = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR, &fT); + fS = 1.0f-fT; + } + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + } + } + else + { + // segment and triangle are parallel + fSqrDist = distanceSegmentSegmentSquared(origin, dir, p0, triEdge0, &fR, &fS); + fT = 0.0f; + + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, p0, triEdge1, &fR0, &fT0); + fS0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + + const PxVec3 kTriSegOrig = p0+triEdge0; + const PxVec3 kTriSegDir = triEdge1 - triEdge0; + fSqrDist0 = distanceSegmentSegmentSquared(origin, dir, kTriSegOrig, kTriSegDir, &fR0, &fT0); + fS0 = 1.0f-fT0; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + + fSqrDist0 = distancePointTriangleSquared(origin, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 0.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + + const PxVec3 kPt = origin+dir; + fSqrDist0 = distancePointTriangleSquared(kPt, p0, triEdge0, triEdge1, &fS0, &fT0); + fR0 = 1.0f; + updateClosestHit(fSqrDist0, fR0, fS0, fT0, fSqrDist, fR, fS, fT); + } + + if(t) *t = fR; + if(u) *u = fS; + if(v) *v = fT; + + // account for numerical round-off error + return physx::intrinsics::selectMax(0.0f, fSqrDist); +} + + +/* + closest0 is the closest point on segment pq + closest1 is the closest point on triangle abc +*/ +Ps::aos::FloatV Gu::distanceSegmentTriangleSquared( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + Ps::aos::Vec3V& closest0, Ps::aos::Vec3V& closest1) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + //const FloatV one = FOne(); + //const FloatV parallelTolerance = FloatV_From_F32(PX_PARALLEL_TOLERANCE); + + const Vec3V pq = V3Sub(q, p); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V aq = V3Sub(q, a); + + //This is used to calculate the barycentric coordinate + const FloatV d00 = V3Dot(ab,ab); + const FloatV d01 = V3Dot(ab, ac); + const FloatV d11 = V3Dot(ac, ac); + const FloatV tDenom = FSub(FMul(d00, d11), FMul(d01, d01)); + + const FloatV bdenom = FSel(FIsGrtr(tDenom, zero), FRecip(tDenom), zero); + + const Vec3V n =V3Normalize(V3Cross(ab, ac)); // normalize vector + + //compute the closest point of p and triangle plane abc + const FloatV dist3 = V3Dot(ap, n); + const FloatV sqDist3 = FMul(dist3, dist3); + + + //compute the closest point of q and triangle plane abc + const FloatV dist4 = V3Dot(aq, n); + const FloatV sqDist4 = FMul(dist4, dist4); + const FloatV dMul = FMul(dist3, dist4); + const BoolV con = FIsGrtr(zero, dMul); + + + // intersect with the plane + if(BAllEqTTTT(con)) + { + //compute the intersect point + const FloatV nom = FNeg(V3Dot(n, ap)); + const FloatV denom = FRecip(V3Dot(n, pq)); + const FloatV t = FMul(nom, denom); + const Vec3V ip = V3ScaleAdd(pq, t, p);//V3Add(p, V3Scale(pq, t)); + const Vec3V v2 = V3Sub(ip, a); + const FloatV d20 = V3Dot(v2, ab); + const FloatV d21 = V3Dot(v2, ac); + const FloatV v0 = FMul(FSub(FMul(d11, d20), FMul(d01, d21)), bdenom); + const FloatV w0 = FMul(FSub(FMul(d00, d21), FMul(d01, d20)), bdenom); + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + if(BAllEqTTTT(con0)) + { + closest0 = closest1 = ip; + return zero; + } + } + + + Vec4V t40, t41; + const Vec4V sqDist44 = distanceSegmentSegmentSquared4(p,pq,a,ab, b,bc, a,ac, a,ab, t40, t41); + + const FloatV t00 = V4GetX(t40); + const FloatV t10 = V4GetY(t40); + const FloatV t20 = V4GetZ(t40); + + const FloatV t01 = V4GetX(t41); + const FloatV t11 = V4GetY(t41); + const FloatV t21 = V4GetZ(t41); + + const FloatV sqDist0(V4GetX(sqDist44)); + const FloatV sqDist1(V4GetY(sqDist44)); + const FloatV sqDist2(V4GetZ(sqDist44)); + + const Vec3V closestP00 = V3ScaleAdd(pq, t00, p); + const Vec3V closestP01 = V3ScaleAdd(ab, t01, a); + + const Vec3V closestP10 = V3ScaleAdd(pq, t10, p); + const Vec3V closestP11 = V3ScaleAdd(bc, t11, b); + + const Vec3V closestP20 = V3ScaleAdd(pq, t20, p); + const Vec3V closestP21 = V3ScaleAdd(ac, t21, a); + + + //Get the closest point of all edges + const BoolV con20 = FIsGrtr(sqDist1, sqDist0); + const BoolV con21 = FIsGrtr(sqDist2, sqDist0); + const BoolV con2 = BAnd(con20,con21); + const BoolV con30 = FIsGrtrOrEq(sqDist0, sqDist1); + const BoolV con31 = FIsGrtr(sqDist2, sqDist1); + const BoolV con3 = BAnd(con30, con31); + const FloatV sqDistPE = FSel(con2, sqDist0, FSel(con3, sqDist1, sqDist2)); + //const FloatV tValue = FSel(con2, t00, FSel(con3, t10, t20)); + const Vec3V closestPE0 = V3Sel(con2, closestP00, V3Sel(con3, closestP10, closestP20)); // closestP on segment + const Vec3V closestPE1 = V3Sel(con2, closestP01, V3Sel(con3, closestP11, closestP21)); // closestP on triangle + + + const Vec3V closestP31 = V3NegScaleSub(n, dist3, p);//V3Sub(p, V3Scale(n, dist3)); + const Vec3V closestP30 = p; + + //Compute the barycentric coordinate for project point of q + const Vec3V pV20 = V3Sub(closestP31, a); + const FloatV pD20 = V3Dot(pV20, ab); + const FloatV pD21 = V3Dot(pV20, ac); + const FloatV v0 = FMul(FSub(FMul(d11, pD20), FMul(d01, pD21)), bdenom); + const FloatV w0 = FMul(FSub(FMul(d00, pD21), FMul(d01, pD20)), bdenom); + + //check closestP3 is inside the triangle + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + + + + const Vec3V closestP41 = V3NegScaleSub(n, dist4, q);// V3Sub(q, V3Scale(n, dist4)); + const Vec3V closestP40 = q; + + //Compute the barycentric coordinate for project point of q + const Vec3V qV20 = V3Sub(closestP41, a); + const FloatV qD20 = V3Dot(qV20, ab); + const FloatV qD21 = V3Dot(qV20, ac); + const FloatV v1 = FMul(FSub(FMul(d11, qD20), FMul(d01, qD21)), bdenom); + const FloatV w1 = FMul(FSub(FMul(d00, qD21), FMul(d01, qD20)), bdenom); + + const BoolV con1 = isValidTriangleBarycentricCoord(v1, w1); + + /* + p is interior point but not q + */ + const BoolV d0 = FIsGrtr(sqDistPE, sqDist3); + const Vec3V c00 = V3Sel(d0, closestP30, closestPE0); + const Vec3V c01 = V3Sel(d0, closestP31, closestPE1); + + /* + q is interior point but not p + */ + const BoolV d1 = FIsGrtr(sqDistPE, sqDist4); + const Vec3V c10 = V3Sel(d1, closestP40, closestPE0); + const Vec3V c11 = V3Sel(d1, closestP41, closestPE1); + + /* + p and q are interior point + */ + const BoolV d2 = FIsGrtr(sqDist4, sqDist3); + const Vec3V c20 = V3Sel(d2, closestP30, closestP40); + const Vec3V c21 = V3Sel(d2, closestP31, closestP41); + + const BoolV cond2 = BAnd(con0, con1); + + const Vec3V closestP0 = V3Sel(cond2, c20, V3Sel(con0, c00, V3Sel(con1, c10, closestPE0))); + const Vec3V closestP1 = V3Sel(cond2, c21, V3Sel(con0, c01, V3Sel(con1, c11, closestPE1))); + + const Vec3V vv = V3Sub(closestP1, closestP0); + closest0 = closestP0; + closest1 = closestP1; + return V3Dot(vv, vv); +} diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h new file mode 100644 index 000000000..f91778702 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_TRIANGLE_H +#define GU_DISTANCE_SEGMENT_TRIANGLE_H + +#include "PxPhysXCommonConfig.h" +#include "GuSegment.h" + +namespace physx +{ +namespace Gu +{ + + PX_PHYSX_COMMON_API PxReal distanceSegmentTriangleSquared( + const PxVec3& segmentOrigin, const PxVec3& segmentExtent, + const PxVec3& triangleOrigin, const PxVec3& triangleEdge0, const PxVec3& triangleEdge1, + PxReal* t=NULL, PxReal* u=NULL, PxReal* v=NULL); + + PX_INLINE PxReal distanceSegmentTriangleSquared( + const Gu::Segment& segment, + const PxVec3& triangleOrigin, + const PxVec3& triangleEdge0, + const PxVec3& triangleEdge1, + PxReal* t=NULL, + PxReal* u=NULL, + PxReal* v=NULL) + { + return distanceSegmentTriangleSquared( + segment.p0, segment.computeDirection(), triangleOrigin, triangleEdge0, triangleEdge1, t, u, v); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h new file mode 100644 index 000000000..1c9938943 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_DISTANCE_SEGMENT_TRIANGLE_SIMD_H +#define GU_DISTANCE_SEGMENT_TRIANGLE_SIMD_H + +#include "PxPhysXCommonConfig.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Gu +{ + + /* + closest0 is the closest point on segment pq + closest1 is the closest point on triangle abc + */ + PX_PHYSX_COMMON_API Ps::aos::FloatV distanceSegmentTriangleSquared( + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + Ps::aos::Vec3V& closest0, Ps::aos::Vec3V& closest1); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp new file mode 100644 index 000000000..f45df41c9 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp @@ -0,0 +1,615 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuEPA.h" +#include "GuEPAFacet.h" +#include "GuGJKSimplex.h" +#include "CmPriorityQueue.h" +#include "PsAllocator.h" + + + +namespace physx +{ +namespace Gu +{ + using namespace Ps::aos; + + class ConvexV; + + struct FacetDistanceComparator + { + bool operator()(const Facet* left, const Facet* right) const + { + return *left < *right; + } + }; + + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + + class EPA + { + public: + EPA(){} + GjkStatus PenetrationDepth(const GjkConvex& a, const GjkConvex& b, const Ps::aos::Vec3V* PX_RESTRICT A, const Ps::aos::Vec3V* PX_RESTRICT B, const PxU8 size, const bool takeCoreShape, + const FloatV tolerenceLength, GjkOutput& output); + bool expandPoint(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg upperBound); + bool expandSegment(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg upperBound); + bool expandTriangle(PxI32& numVerts, const FloatVArg upperBound); + + Facet* addFacet(const PxU32 i0, const PxU32 i1, const PxU32 i2, const Ps::aos::FloatVArg upper); + + bool originInTetrahedron(const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const Ps::aos::Vec3VArg p3, const Ps::aos::Vec3VArg p4); + + Cm::InlinePriorityQueue heap; + Ps::aos::Vec3V aBuf[MaxSupportPoints]; + Ps::aos::Vec3V bBuf[MaxSupportPoints]; + Facet facetBuf[MaxFacets]; + EdgeBuffer edgeBuffer; + EPAFacetManager facetManager; + + private: + PX_NOCOPY(EPA) + }; +#if PX_VC + #pragma warning(pop) +#endif + + PX_FORCE_INLINE bool EPA::originInTetrahedron(const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const Ps::aos::Vec3VArg p3, const Ps::aos::Vec3VArg p4) + { + using namespace Ps::aos; + return BAllEqFFFF(PointOutsideOfPlane4(p1, p2, p3, p4)) == 1; + } + + + static PX_FORCE_INLINE void doSupport(const GjkConvex& a, const GjkConvex& b, const Ps::aos::Vec3VArg dir, Ps::aos::Vec3V& supportA, Ps::aos::Vec3V& supportB, Ps::aos::Vec3V& support) + { + const Vec3V tSupportA = a.support(V3Neg(dir)); + const Vec3V tSupportB = b.support(dir); + //avoid LHS + supportA = tSupportA; + supportB = tSupportB; + support = V3Sub(tSupportA, tSupportB); + } + + GjkStatus epaPenetration(const GjkConvex& a, const GjkConvex& b, const PxU8* PX_RESTRICT aInd, const PxU8* PX_RESTRICT bInd, const PxU8 size, + const bool takeCoreShape, const FloatV tolerenceLength, GjkOutput& output) + { + + PX_ASSERT(size > 0 && size <=4); + + Vec3V A[4]; + Vec3V B[4]; + + //ML: we construct a simplex based on the gjk simplex indices + for(PxU32 i=0; im_FacetId = PxU8(facetId); + + const BoolV validTriangle = facet->isValid2(i0, i1, i2, aBuf, bBuf, upper); + + if(BAllEqTTTT(validTriangle)) + { + heap.push(facet); + facet->m_inHeap = true; + } + else + { + facet->m_inHeap = false; + } + return facet; + } + + //ML: this function performs a flood fill over the boundary of the current polytope. + void Facet::silhouette(const PxU32 _index, const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, EPAFacetManager& manager) + { + using namespace Ps::aos; + const FloatV zero = FZero(); + Edge stack[MaxFacets]; + stack[0] = Edge(this, _index); + PxI32 size = 1; + while(size--) + { + Facet* const PX_RESTRICT f = stack[size].m_facet; + const PxU32 index = stack[size].m_index; + PX_ASSERT(f->Valid()); + + if(!f->m_obsolete) + { + //ML: if the point is above the facet, the facet has an reflex edge, which will make the polytope concave. Therefore, we need to + //remove this facet and make sure the expanded polytope is convex. + const FloatV pointPlaneDist = f->getPlaneDist(w, aBuf, bBuf); + + if(FAllGrtr(zero, pointPlaneDist)) + { + //ML: facet isn't visible from w (we don't have a reflex edge), this facet will be on the boundary and part of the new polytope so that + //we will push it into our edgeBuffer + if(!edgeBuffer.Insert(f, index)) + return; + } + else + { + //ML:facet is visible from w, therefore, we need to remove this facet from the heap and push its adjacent facets onto the stack + f->m_obsolete = true; // Facet is visible from w + const PxU32 next(incMod3(index)); + const PxU32 next2(incMod3(next)); + stack[size++] = Edge(f->m_adjFacets[next2],PxU32(f->m_adjEdges[next2])); + stack[size++] = Edge(f->m_adjFacets[next], PxU32(f->m_adjEdges[next])); + + PX_ASSERT(size <= MaxFacets); + if(!f->m_inHeap) + { + //if the facet isn't in the heap, we can release that memory + manager.deferredFreeID(f->m_FacetId); + } + } + } + } + } + + //ML: this function perform flood fill for the adjancent facet and store the boundary facet into the edgeBuffer + void Facet::silhouette(const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, EPAFacetManager& manager) + { + m_obsolete = true; + for(PxU32 a = 0; a < 3; ++a) + { + m_adjFacets[a]->silhouette(PxU32(m_adjEdges[a]), w, aBuf, bBuf, edgeBuffer, manager); + } + } + + bool EPA::expandPoint(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg upperBound) + { + const Vec3V x = V3UnitX(); + Vec3V q0 = V3Sub(aBuf[0], bBuf[0]); + Vec3V q1; + doSupport(a, b, x, aBuf[1], bBuf[1], q1); + if (V3AllEq(q0, q1)) + return false; + return expandSegment(a, b, numVerts, upperBound); + } + + //ML: this function use the segement to create a triangle + bool EPA::expandSegment(const GjkConvex& a, const GjkConvex& b, PxI32& numVerts, const FloatVArg upperBound) + { + const Vec3V q0 = V3Sub(aBuf[0], bBuf[0]); + const Vec3V q1 = V3Sub(aBuf[1], bBuf[1]); + const Vec3V v = V3Sub(q1, q0); + const Vec3V absV = V3Abs(v); + + const FloatV x = V3GetX(absV); + const FloatV y = V3GetY(absV); + const FloatV z = V3GetZ(absV); + + Vec3V axis = V3UnitX(); + const BoolV con0 = BAnd(FIsGrtr(x, y), FIsGrtr(z, y)); + if (BAllEqTTTT(con0)) + { + axis = V3UnitY(); + } + else if(FAllGrtr(x, z)) + { + axis = V3UnitZ(); + } + + const Vec3V n = V3Normalize(V3Cross(axis, v)); + Vec3V q2; + doSupport(a, b, n, aBuf[2], bBuf[2], q2); + + return expandTriangle(numVerts, upperBound); + } + + bool EPA::expandTriangle(PxI32& numVerts, const FloatVArg upperBound) + { + numVerts = 3; + + Facet * PX_RESTRICT f0 = addFacet(0, 1, 2, upperBound); + Facet * PX_RESTRICT f1 = addFacet(1, 0, 2, upperBound); + + if(heap.empty()) + return false; + + f0->link(0, f1, 0); + f0->link(1, f1, 2); + f0->link(2, f1, 1); + + return true; + } + + //ML: this function calculates contact information. If takeCoreShape flag is true, this means the two closest points will be on the core shape used in the support functions. + //For example, we treat sphere/capsule as a point/segment in the support function for GJK/EPA, so that the core shape for sphere/capsule is a point/segment. For PCM, we need + //to take the point from the core shape because this will allows us recycle the contacts more stably. For SQ sweeps, we need to take the point on the surface of the sphere/capsule + //when we calculate MTD because this is what will be reported to the user. Therefore, the takeCoreShape flag will be set to be false in SQ. + static void calculateContactInformation(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, Facet* facet, const GjkConvex& a, const GjkConvex& b, const bool takeCoreShape, GjkOutput& output) + { + const FloatV zero = FZero(); + Vec3V _pa, _pb; + facet->getClosestPoint(aBuf, bBuf, _pa, _pb); + + //dist > 0 means two shapes are penetrated. If dist < 0(when origin isn't inside the polytope), two shapes status are unknown + const FloatV dist = FAbs(facet->getPlaneDist()); + + const Vec3V planeNormal = V3Neg(facet->getPlaneNormal()); + + if(takeCoreShape) + { + output.closestA = _pa; + output.closestB = _pb; + output.normal = planeNormal; + output.penDep = FNeg(dist); + } + else + { + //for sphere/capusule to take the surface point + const BoolV aQuadratic = a.isMarginEqRadius(); + const BoolV bQuadratic = b.isMarginEqRadius(); + + const FloatV marginA = FSel(aQuadratic, a.getMargin(), zero); + const FloatV marginB = FSel(bQuadratic, b.getMargin(), zero); + const FloatV sumMargin = FAdd(marginA, marginB); + output.closestA = V3NegScaleSub(planeNormal, marginA, _pa); + output.closestB = V3ScaleAdd(planeNormal, marginB, _pb); + output.normal = planeNormal; + output.penDep = FNeg(FAdd(dist, sumMargin)); + } + } + + //ML: This function returns one of three status codes: + //(1)EPA_FAIL: the algorithm failed to create a valid polytope(the origin wasn't inside the polytope) from the input simplex + //(2)EPA_CONTACT : the algorithm found the MTD and converged successfully. + //(3)EPA_DEGENERATE: the algorithm cannot make further progress and the result is unknown. + GjkStatus EPA::PenetrationDepth(const GjkConvex& a, const GjkConvex& b, const Ps::aos::Vec3V* PX_RESTRICT A, const Ps::aos::Vec3V* PX_RESTRICT B, const PxU8 size, const bool takeCoreShape, + const FloatV tolerenceLength, GjkOutput& output) + { + + using namespace Ps::aos; + + PX_UNUSED(tolerenceLength); + + Ps::prefetchLine(&facetBuf[0]); + Ps::prefetchLine(&facetBuf[0], 128); + + const FloatV zero = FZero(); + + const FloatV _max = FMax(); + FloatV upper_bound(_max); + + aBuf[0]=A[0]; aBuf[1]=A[1]; aBuf[2]=A[2]; aBuf[3]=A[3]; + bBuf[0]=B[0]; bBuf[1]=B[1]; bBuf[2]=B[2]; bBuf[3]=B[3]; + + PxI32 numVertsLocal = 0; + + heap.clear(); + + //if the simplex isn't a tetrahedron, we need to construct one before we can expand it + switch (size) + { + case 1: + { + // Touching contact. Yes, we have a collision and the penetration will be zero + if(!expandPoint(a, b, numVertsLocal, upper_bound)) + return EPA_FAIL; + break; + } + case 2: + { + // We have a line segment inside the Minkowski sum containing the + // origin. we need to construct two back to back triangles which link to each other + if(!expandSegment(a, b, numVertsLocal, upper_bound)) + return EPA_FAIL; + break; + } + case 3: + { + // We have a triangle inside the Minkowski sum containing + // the origin. We need to construct two back to back triangles which link to each other + if(!expandTriangle(numVertsLocal, upper_bound)) + return EPA_FAIL; + + break; + + } + case 4: + { + //check for input face normal. All face normals in this tetrahedron should be all pointing either inwards or outwards. If all face normals are pointing outward, we are good to go. Otherwise, we need to + //shuffle the input vertexes and make sure all face normals are pointing outward + const Vec3V pa0(aBuf[0]); + const Vec3V pa1(aBuf[1]); + const Vec3V pa2(aBuf[2]); + const Vec3V pa3(aBuf[3]); + + const Vec3V pb0(bBuf[0]); + const Vec3V pb1(bBuf[1]); + const Vec3V pb2(bBuf[2]); + const Vec3V pb3(bBuf[3]); + + const Vec3V p0 = V3Sub(pa0, pb0); + const Vec3V p1 = V3Sub(pa1, pb1); + const Vec3V p2 = V3Sub(pa2, pb2); + const Vec3V p3 = V3Sub(pa3, pb3); + + const Vec3V v1 = V3Sub(p1, p0); + const Vec3V v2 = V3Sub(p2, p0); + + const Vec3V planeNormal = V3Normalize(V3Cross(v1, v2)); + + const FloatV signDist = V3Dot(planeNormal, V3Sub(p3, p0)); + + if (FAllGrtr(signDist, zero)) + { + //shuffle the input vertexes + const Vec3V tempA0 = aBuf[2]; + const Vec3V tempB0 = bBuf[2]; + aBuf[2] = aBuf[1]; + bBuf[2] = bBuf[1]; + aBuf[1] = tempA0; + bBuf[1] = tempB0; + } + + Facet * PX_RESTRICT f0 = addFacet(0, 1, 2, upper_bound); + Facet * PX_RESTRICT f1 = addFacet(0, 3, 1, upper_bound); + Facet * PX_RESTRICT f2 = addFacet(0, 2, 3, upper_bound); + Facet * PX_RESTRICT f3 = addFacet(1, 3, 2, upper_bound); + + if (heap.empty()) + return EPA_FAIL; + +#if EPA_DEBUG + PX_ASSERT(f0->m_planeDist >= -1e-2f); + PX_ASSERT(f1->m_planeDist >= -1e-2f); + PX_ASSERT(f2->m_planeDist >= -1e-2f); + PX_ASSERT(f3->m_planeDist >= -1e-2f); +#endif + + f0->link(0, f1, 2); + f0->link(1, f3, 2); + f0->link(2, f2, 0); + f1->link(0, f2, 2); + f1->link(1, f3, 0); + f2->link(1, f3, 1); + numVertsLocal = 4; + + break; + } + } + + + const FloatV minMargin = FMin(a.getMinMargin(), b.getMinMargin()); + const FloatV eps = FMul(minMargin, FLoad(0.1f)); + + Facet* PX_RESTRICT facet = NULL; + + Vec3V tempa, tempb, q; + + do + { + + facetManager.processDeferredIds(); + facet = heap.pop(); //get the shortest distance triangle of origin from the list + facet->m_inHeap = false; + + if (!facet->isObsolete()) + { + Ps::prefetchLine(edgeBuffer.m_pEdges); + Ps::prefetchLine(edgeBuffer.m_pEdges,128); + Ps::prefetchLine(edgeBuffer.m_pEdges,256); + + const Vec3V planeNormal = facet->getPlaneNormal(); + const FloatV planeDist = facet->getPlaneDist(); + + tempa = a.support(planeNormal); + tempb = b.support(V3Neg(planeNormal)); + + q = V3Sub(tempa, tempb); + + Ps::prefetchLine(&aBuf[numVertsLocal],128); + Ps::prefetchLine(&bBuf[numVertsLocal],128); + + //calculate the distance from support point to the origin along the plane normal. Because the support point is search along + //the plane normal, which means the distance should be positive. However, if the origin isn't contained in the polytope, dist + //might be negative + const FloatV dist = V3Dot(q, planeNormal); + + const BoolV con0 = FIsGrtrOrEq(eps, FAbs(FSub(dist, planeDist))); + + if (BAllEqTTTT(con0)) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, takeCoreShape, output); + + if (takeCoreShape) + { + const FloatV toleranceEps = FMul(FLoad(1e-3f), tolerenceLength); + const Vec3V dif = V3Sub(output.closestA, output.closestB); + const FloatV pen = FAdd(FAbs(output.penDep), toleranceEps); + const FloatV sqDif = V3Dot(dif, dif); + const FloatV length = FSel(FIsGrtr(sqDif, zero), FSqrt(sqDif), zero); + if (FAllGrtr(length, pen)) + return EPA_DEGENERATE; + } + return EPA_CONTACT; + + } + + //update the upper bound to the minimum between existing upper bound and the distance + upper_bound = FMin(upper_bound, dist); + + aBuf[numVertsLocal]=tempa; + bBuf[numVertsLocal]=tempb; + + const PxU32 index =PxU32(numVertsLocal++); + + // Compute the silhouette cast by the new vertex + // Note that the new vertex is on the positive side + // of the current facet, so the current facet will + // not be in the polytope. Start local search + // from this facet. + + edgeBuffer.MakeEmpty(); + + facet->silhouette(q, aBuf, bBuf, edgeBuffer, facetManager); + + if (!edgeBuffer.IsValid()) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, takeCoreShape, output); + return EPA_DEGENERATE; + } + + Edge* PX_RESTRICT edge=edgeBuffer.Get(0); + + PxU32 bufferSize=edgeBuffer.Size(); + + //check to see whether we have enough space in the facet manager to create new facets + if(bufferSize > facetManager.getNumRemainingIDs()) + { + calculateContactInformation(aBuf, bBuf, facet, a, b, takeCoreShape, output); + return EPA_DEGENERATE; + } + + Facet *firstFacet = addFacet(edge->getTarget(), edge->getSource(),index, upper_bound); + PX_ASSERT(firstFacet); + firstFacet->link(0, edge->getFacet(), edge->getIndex()); + + Facet * PX_RESTRICT lastFacet = firstFacet; + +#if EPA_DEBUG + bool degenerate = false; + for(PxU32 i=1; (igetTarget(), edge->getSource(),index, upper_bound); + PX_ASSERT(newFacet); + const bool b0 = newFacet->link(0, edge->getFacet(), edge->getIndex()); + const bool b1 = newFacet->link(2, lastFacet, 1); + degenerate = degenerate || !b0 || !b1; + lastFacet = newFacet; + } + + if (degenerate) + Ps::debugBreak(); +#else + for (PxU32 i = 1; igetTarget(), edge->getSource(), index, upper_bound); + newFacet->link(0, edge->getFacet(), edge->getIndex()); + newFacet->link(2, lastFacet, 1); + lastFacet = newFacet; + } +#endif + + firstFacet->link(2, lastFacet, 1); + } + facetManager.freeID(facet->m_FacetId); + + } + while((heap.size() > 0) && FAllGrtr(upper_bound, heap.top()->getPlaneDist()) && numVertsLocal != MaxSupportPoints); + + calculateContactInformation(aBuf, bBuf, facet, a, b, takeCoreShape, output); + return EPA_DEGENERATE; + } +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h new file mode 100644 index 000000000..4ae929579 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h @@ -0,0 +1,60 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EPA_H +#define GU_EPA_H + +#include "GuGJKUtil.h" +#include "GuGJKType.h" + +namespace physx +{ + +namespace Gu +{ + //ML: The main entry point for EPA. + // + //This function returns one of three status codes: + //(1)EPA_FAIL: the algorithm failed to create a valid polytope(the origin wasn't inside the polytope) from the input simplex. + //(2)EPA_CONTACT : the algorithm found the MTD and converged successfully. + //(3)EPA_DEGENERATE: the algorithm cannot make further progress and the result is unknown. + + GjkStatus epaPenetration( const GjkConvex& a, //convex a in the space of convex b + const GjkConvex& b, //convex b + const PxU8* PX_RESTRICT aInd, //warm start index for convex a to create an initial simplex + const PxU8* PX_RESTRICT bInd, //warm start index for convex b to create an initial simplex + const PxU8 size, //number of warm-start indices + const bool takeCoreShape, //indicates whether we take support point from the core shape or surface of capsule/sphere + const Ps::aos::FloatV tolerenceLength, //the length of meter + GjkOutput& output); //result +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h b/src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h new file mode 100644 index 000000000..44de1afb6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h @@ -0,0 +1,306 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_EPA_FACET_H +#define GU_EPA_FACET_H + +#include "CmPhysXCommon.h" +#include "PsVecMath.h" +#include "PsFPU.h" +#include "PsUtilities.h" +#include "CmIDPool.h" + +#if (defined __GNUC__ && defined _DEBUG) +#define PX_EPA_FORCE_INLINE +#else +#define PX_EPA_FORCE_INLINE PX_FORCE_INLINE +#endif + +#define EPA_DEBUG 0 + +namespace physx +{ +#define MaxEdges 32 +#define MaxFacets 64 +#define MaxSupportPoints 64 + +namespace Gu +{ + const PxU32 lookUp[3] = {1, 2, 0}; + + PX_FORCE_INLINE PxU32 incMod3(PxU32 i) { return lookUp[i]; } + + class EdgeBuffer; + class Edge; + typedef Cm::InlineDeferredIDPool EPAFacetManager; + + class Facet + { + public: + + Facet() + { + } + + PX_FORCE_INLINE Facet(const PxU32 _i0, const PxU32 _i1, const PxU32 _i2) + : m_obsolete(false), m_inHeap(false) + { + m_indices[0]= Ps::toI8(_i0); + m_indices[1]= Ps::toI8(_i1); + m_indices[2]= Ps::toI8(_i2); + + m_adjFacets[0] = m_adjFacets[1] = m_adjFacets[2] = NULL; + m_adjEdges[0] = m_adjEdges[1] = m_adjEdges[2] = -1; + } + + + PX_FORCE_INLINE void invalidate() + { + m_adjFacets[0] = m_adjFacets[1] = m_adjFacets[2] = NULL; + m_adjEdges[0] = m_adjEdges[1] = m_adjEdges[2] = -1; + } + + PX_FORCE_INLINE bool Valid() + { + return (m_adjFacets[0] != NULL) & (m_adjFacets[1] != NULL) & (m_adjFacets[2] != NULL); + } + + PX_FORCE_INLINE PxU32 operator[](const PxU32 i) const + { + return PxU32(m_indices[i]); + } + + //create ajacency information + bool link(const PxU32 edge0, Facet* PX_RESTRICT facet, const PxU32 edge1); + + PX_FORCE_INLINE bool isObsolete() const { return m_obsolete; } + + //calculate the signed distance from a point to a plane + PX_FORCE_INLINE Ps::aos::FloatV getPlaneDist(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf) const; + + //check to see whether the triangle is a valid triangle, calculate plane normal and plane distance at the same time + Ps::aos::BoolV isValid2(const PxU32 i0, const PxU32 i1, const PxU32 i2, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, + const Ps::aos::FloatVArg upper); + + //return the absolute value for the plane distance from origin + PX_FORCE_INLINE Ps::aos::FloatV getPlaneDist() const + { + return Ps::aos::FLoad(m_planeDist); + } + + //return the plane normal + PX_FORCE_INLINE Ps::aos::Vec3V getPlaneNormal()const + { + return m_planeNormal; + } + + //calculate the closest points for a shape pair + void getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, Ps::aos::Vec3V& closestA, + Ps::aos::Vec3V& closestB); + + //calculate the closest points for a shape pair + //void getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, Ps::aos::FloatV& v, Ps::aos::FloatV& w); + + //performs a flood fill over the boundary of the current polytope. + void silhouette(const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, EPAFacetManager& manager); + + + //m_planeDist is positive + bool operator <(const Facet& b) const + { + return m_planeDist < b.m_planeDist; + } + + //store all the boundary facets for the new polytope in the edgeBuffer and free indices when an old facet isn't part of the boundary anymore + PX_FORCE_INLINE void silhouette(const PxU32 index, const Ps::aos::Vec3VArg w, const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, EdgeBuffer& edgeBuffer, + EPAFacetManager& manager); + + Ps::aos::Vec3V m_planeNormal; //16 + PxF32 m_planeDist; +#if EPA_DEBUG + PxF32 m_lambda1; + PxF32 m_lambda2; +#endif + + Facet* PX_RESTRICT m_adjFacets[3]; //the triangle adjacent to edge i in this triangle //32 + PxI8 m_adjEdges[3]; //the edge connected with the corresponding triangle //35 + PxI8 m_indices[3]; //the index of vertices of the triangle //38 + bool m_obsolete; //a flag to denote whether the triangle are still part of the bundeary of the new polytope //39 + bool m_inHeap; //a flag to indicate whether the triangle is in the heap //40 + PxU8 m_FacetId; //41 //73 + + }; + + + class Edge + { + public: + PX_FORCE_INLINE Edge() {} + PX_FORCE_INLINE Edge(Facet * PX_RESTRICT facet, const PxU32 index) : m_facet(facet), m_index(index) {} + PX_FORCE_INLINE Edge(const Edge& other) : m_facet(other.m_facet), m_index(other.m_index){} + + PX_FORCE_INLINE Edge& operator = (const Edge& other) + { + m_facet = other.m_facet; + m_index = other.m_index; + return *this; + } + + PX_FORCE_INLINE Facet *getFacet() const { return m_facet; } + PX_FORCE_INLINE PxU32 getIndex() const { return m_index; } + + //get out the associated start vertex index in this edge from the facet + PX_FORCE_INLINE PxU32 getSource() const + { + PX_ASSERT(m_index < 3); + return (*m_facet)[m_index]; + } + + //get out the associated end vertex index in this edge from the facet + PX_FORCE_INLINE PxU32 getTarget() const + { + PX_ASSERT(m_index < 3); + return (*m_facet)[incMod3(m_index)]; + } + + Facet* PX_RESTRICT m_facet; + PxU32 m_index; + }; + + + class EdgeBuffer + { + public: + EdgeBuffer() : m_Size(0), m_OverFlow(false) + { + } + + Edge* Insert(Facet* PX_RESTRICT facet, const PxU32 index) + { + if (m_Size < MaxEdges) + { + Edge* pEdge = &m_pEdges[m_Size++]; + pEdge->m_facet = facet; + pEdge->m_index = index; + return pEdge; + } + m_OverFlow = true; + return NULL; + } + + Edge* Get(const PxU32 index) + { + PX_ASSERT(index < m_Size); + return &m_pEdges[index]; + } + + PxU32 Size() + { + return m_Size; + } + + bool IsValid() + { + return m_Size > 0 && !m_OverFlow; + } + + void MakeEmpty() + { + m_Size = 0; + m_OverFlow = false; + } + + Edge m_pEdges[MaxEdges]; + PxU32 m_Size; + bool m_OverFlow; + }; + + //ML: calculate MTD points for a shape pair + PX_FORCE_INLINE void Facet::getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT aBuf, const Ps::aos::Vec3V* PX_RESTRICT bBuf, Ps::aos::Vec3V& closestA, + Ps::aos::Vec3V& closestB) + { + using namespace Ps::aos; + + const Vec3V pa0(aBuf[m_indices[0]]); + const Vec3V pa1(aBuf[m_indices[1]]); + const Vec3V pa2(aBuf[m_indices[2]]); + + const Vec3V pb0(bBuf[m_indices[0]]); + const Vec3V pb1(bBuf[m_indices[1]]); + const Vec3V pb2(bBuf[m_indices[2]]); + + const Vec3V p0 = V3Sub(pa0, pb0); + const Vec3V p1 = V3Sub(pa1, pb1); + const Vec3V p2 = V3Sub(pa2, pb2); + + const Vec3V v0 = V3Sub(p1, p0); + const Vec3V v1 = V3Sub(p2, p0); + + const Vec3V closestP = V3Scale(m_planeNormal, FLoad(m_planeDist)); + const Vec3V v2 = V3Sub(closestP, p0); + //calculate barycentric coordinates + const FloatV d00 = V3Dot(v0, v0); + const FloatV d01 = V3Dot(v0, v1); + const FloatV d11 = V3Dot(v1, v1); + + const FloatV d20 = V3Dot(v2, v0); + const FloatV d21 = V3Dot(v2, v1); + + const FloatV det = FNegScaleSub(d01, d01, FMul(d00, d11));//FSub( FMul(v1dv1, v2dv2), FMul(v1dv2, v1dv2) ); // non-negative + const FloatV recip = FSel(FIsGrtr(det, FEps()), FRecip(det), FZero()); + + const FloatV lambda1 = FMul(FNegScaleSub(d01, d21, FMul(d11, d20)), recip); + const FloatV lambda2 = FMul(FNegScaleSub(d01, d20, FMul(d00, d21)), recip); + +#if EPA_DEBUG + FStore(lambda1, &m_lambda1); + FStore(lambda2, &m_lambda2); +#endif + + const FloatV u = FSub(FOne(), FAdd(lambda1, lambda2)); + closestA = V3ScaleAdd(pa0, u, V3ScaleAdd(pa1, lambda1, V3Scale(pa2, lambda2))); + closestB = V3ScaleAdd(pb0, u, V3ScaleAdd(pb1, lambda1, V3Scale(pb2, lambda2))); + } + + //ML: create adjacency informations for both facets + PX_FORCE_INLINE bool Facet::link(const PxU32 edge0, Facet * PX_RESTRICT facet, const PxU32 edge1) + { + m_adjFacets[edge0] = facet; + m_adjEdges[edge0] = Ps::toI8(edge1); + facet->m_adjFacets[edge1] = this; + facet->m_adjEdges[edge1] = Ps::toI8(edge0); + + return (m_indices[edge0] == facet->m_indices[incMod3(edge1)]) && (m_indices[incMod3(edge0)] == facet->m_indices[edge1]); + } + +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h new file mode 100644 index 000000000..015005e76 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h @@ -0,0 +1,219 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJK_H +#define GU_GJK_H + + +#include "GuGJKType.h" +#include "GuGJKUtil.h" +#include "GuConvexSupportTable.h" +#include "GuGJKSimplex.h" +#include "PsFPU.h" + +#define GJK_SEPERATING_AXIS_VALIDATE 0 + +namespace physx +{ +namespace Gu +{ + + class ConvexV; + +#if GJK_SEPERATING_AXIS_VALIDATE + template + static void validateSeparatingAxis(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg separatingAxis) + { + using namespace Ps::aos; + const Vec3V minV0 = a.ConvexA::support(V3Neg(separatingAxis)); + const Vec3V maxV0 = a.ConvexA::support(separatingAxis); + + const Vec3V minV1 = b.ConvexB::support(V3Neg(separatingAxis)); + const Vec3V maxV1 = b.ConvexB::support(separatingAxis); + + const FloatV min0 = V3Dot(minV0, separatingAxis); + const FloatV max0 = V3Dot(maxV0, separatingAxis); + + const FloatV min1 = V3Dot(minV1, separatingAxis); + const FloatV max1 = V3Dot(maxV1, separatingAxis); + + PX_ASSERT(FAllGrtr(min1, max0) || FAllGrtr(min0, max1)); + } +#endif + + + /* + + initialSearchDir :initial search direction in the mincowsky sum, it should be in the local space of ConvexB + closestA :it is the closest point in ConvexA in the local space of ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + closestB :it is the closest point in ConvexB in the local space of ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + normal :normal pointing from ConvexA to ConvexB in the local space of ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + distance :the distance of the closest points between ConvexA and ConvexB if acceptance threshold is sufficent large. Otherwise, it will be garbage + contactDist :the distance which we will generate contact information if ConvexA and ConvexB are both separated within contactDist + */ + + //*Each convex has + //* a support function + //* a margin - the amount by which we shrunk the shape for a convex or box. If the shape are sphere/capsule, margin is the radius + //* a minMargin - some percentage of margin, which is used to determine the termination condition for gjk + + //*We'll report: + //* GJK_NON_INTERSECT if the sign distance between the shapes is greater than the sum of the margins and the the contactDistance + //* GJK_CLOSE if the minimum distance between the shapes is less than the sum of the margins and the the contactDistance + //* GJK_CONTACT if the two shapes are overlapped with each other + template + GjkStatus gjk(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3V& initialSearchDir, const Ps::aos::FloatV& contactDist, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, Ps::aos::Vec3V& normal, + Ps::aos::FloatV& distance) + { + using namespace Ps::aos; + Vec3V Q[4]; + Vec3V A[4]; + Vec3V B[4]; + + const FloatV zero = FZero(); + PxU32 size=0; + + //const Vec3V _initialSearchDir = aToB.p; + Vec3V closest = V3Sel(FIsGrtr(V3Dot(initialSearchDir, initialSearchDir), zero), initialSearchDir, V3UnitX()); + Vec3V v = V3Normalize(closest); + + // ML: eps2 is the square value of an epsilon value which applied in the termination condition for two shapes overlap. + // GJK will terminate based on sq(v) < eps and indicate that two shapes are overlapping. + // we calculate the eps based on 10% of the minimum margin of two shapes + const FloatV tenPerc = FLoad(0.1f); + const FloatV minMargin = FMin(a.getMinMargin(), b.getMinMargin()); + const FloatV eps = FMul(minMargin, tenPerc); + + // ML:epsRel is square value of 1.5% which applied to the distance of a closest point(v) to the origin. + // If |v|- v/|v|.dot(w) < epsRel*|v|, + // two shapes are clearly separated, GJK terminate and return non intersect. + // This adjusts the termination condition based on the length of v + // which avoids ill-conditioned terminations. + const FloatV epsRel = FLoad(0.000225f);//1.5%. + + FloatV dist = FMax(); + FloatV prevDist; + Vec3V prevClos, prevDir; + + const BoolV bTrue = BTTTT(); + BoolV bNotTerminated = bTrue; + BoolV bNotDegenerated = bTrue; + + //ML: we treate sphere as a point and capsule as segment in the support function, so that we need to add on radius + const BoolV aQuadratic = a.isMarginEqRadius(); + const BoolV bQuadratic = b.isMarginEqRadius(); + + const FloatV sumMargin = FAdd(FSel(aQuadratic, a.getMargin(), zero), FSel(bQuadratic, b.getMargin(), zero)); + const FloatV separatingDist = FAdd(sumMargin, contactDist); + const FloatV relDif = FSub(FOne(), epsRel); + + do + { + prevDist = dist; + prevClos = closest; + prevDir = v; + + //de-virtualize, we don't need to use a normalize direction to get the support point + //this will allow the cpu better pipeline the normalize calculation while it does the + //support map + const Vec3V supportA=a.ConvexA::support(V3Neg(closest)); + const Vec3V supportB=b.ConvexB::support(closest); + + //calculate the support point + const Vec3V support = V3Sub(supportA, supportB); + + const FloatV signDist = V3Dot(v, support); + + if(FAllGrtr(signDist, separatingDist)) + { + //ML:gjk found a separating axis for these two objects and the distance is large than the seperating distance, gjk might not converage so that + //we won't generate contact information +#if GJK_SEPERATING_AXIS_VALIDATE + validateSeparatingAxis(a, b, v); +#endif + return GJK_NON_INTERSECT; + } + + const BoolV con = BAnd(FIsGrtr(signDist, sumMargin), FIsGrtr(signDist, FMul(relDif, dist))); + + if(BAllEqTTTT(con)) + { + //ML:: gjk converage and we get the closest point information + Vec3V closA, closB; + //normal point from A to B + const Vec3V n = V3Neg(v); + getClosestPoint(Q, A, B, closest, closA, closB, size); + closestA = V3Sel(aQuadratic, V3ScaleAdd(n, a.getMargin(), closA), closA); + closestB = V3Sel(bQuadratic, V3NegScaleSub(n, b.getMargin(), closB), closB); + distance = FMax(zero, FSub(dist, sumMargin)); + normal = n;//V3Normalize(V3Neg(closest)); + return GJK_CLOSE; + } + + PX_ASSERT(size < 4); + A[size]=supportA; + B[size]=supportB; + Q[size++]=support; + + //calculate the closest point between two convex hull + closest = GJKCPairDoSimplex(Q, A, B, support, size); + + dist = V3Length(closest); + v = V3ScaleInv(closest, dist); + bNotDegenerated = FIsGrtr(prevDist, dist); + bNotTerminated = BAnd(FIsGrtr(dist, eps), bNotDegenerated); + }while(BAllEqTTTT(bNotTerminated)); + + if(BAllEqTTTT(bNotDegenerated)) + { + //GJK_CONTACT + distance = zero; + return GJK_CONTACT; + } + + //GJK degenerated, use the previous closest point + const FloatV acceptancePerc = FLoad(0.2f); + const FloatV acceptanceMargin = FMul(acceptancePerc, FMin(a.getMargin(), b.getMargin())); + const FloatV acceptanceDist = FSel(FIsGrtr(sumMargin, zero), sumMargin, acceptanceMargin); + Vec3V closA, closB; + const Vec3V n = V3Neg(prevDir);//V3Normalize(V3Neg(prevClos)); + getClosestPoint(Q, A, B, prevClos, closA, closB, size); + closestA = V3Sel(aQuadratic, V3ScaleAdd(n, a.getMargin(), closA), closA); + closestB = V3Sel(bQuadratic, V3NegScaleSub(n, b.getMargin(), closB), closB); + normal = n; + dist = FMax(zero, FSub(prevDist, sumMargin)); + distance = dist; + + return FAllGrtr(dist, acceptanceDist) ? GJK_CLOSE: GJK_CONTACT; + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h new file mode 100644 index 000000000..23fb003d0 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h @@ -0,0 +1,320 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJK_PENETRATION_H +#define GU_GJK_PENETRATION_H + + +#include "GuConvexSupportTable.h" +#include "GuGJKSimplex.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGJKUtil.h" +#include "PsUtilities.h" +#include "GuGJKType.h" + +#define GJK_VALIDATE 0 + + +namespace physx +{ +namespace Gu +{ + + class ConvexV; + + + PX_FORCE_INLINE void assignWarmStartValue(PxU8* PX_RESTRICT aIndices, PxU8* PX_RESTRICT bIndices, PxU8& size_, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, PxU32 size ) + { + if(aIndices) + { + PX_ASSERT(bIndices); + size_ = Ps::to8(size); + for(PxU32 i=0; i + PX_NOINLINE GjkStatus gjkPenetration(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg contactDist, const bool takeCoreShape, + PxU8* PX_RESTRICT aIndices, PxU8* PX_RESTRICT bIndices, PxU8& warmStartSize, + GjkOutput& output) + { + using namespace Ps::aos; + + //ML: eps is the threshold that uses to determine whether two (shrunk) shapes overlap. We calculate eps2 based on 10% of the minimum margin of two shapes + const FloatV minMargin = FMin(a.ConvexA::getMinMargin(), b.ConvexB::getMinMargin()); + const FloatV eps = FMul(minMargin, FLoad(0.1f)); + + //const FloatV eps2 = FMul(_eps2, _eps2); + //ML: epsRel2 is the square of 0.01. This is used to scale the square distance of a closest point to origin to determine whether two shrunk shapes overlap in the margin, but + //they don't overlap. + //const FloatV epsRel2 = FMax(FLoad(0.0001), eps2); + + // ML:epsRel is square value of 1.5% which applied to the distance of a closest point(v) to the origin. + // If |v|- v/|v|.dot(w) < epsRel*|v|=>(|v|*(1-epsRel) < v/|v|.dot(w)), + // two shapes are clearly separated, GJK terminate and return non intersect. + // This adjusts the termination condition based on the length of v + // which avoids ill-conditioned terminations. + const FloatV epsRel = FLoad(0.000225f);//1.5%. + const FloatV relDif = FSub(FOne(), epsRel); + + const FloatV zero = FZero(); + + //capsule/sphere will have margin which is its radius + const FloatV marginA = a.getMargin(); + const FloatV marginB = b.getMargin(); + + const BoolV aQuadratic = a.isMarginEqRadius(); + const BoolV bQuadratic = b.isMarginEqRadius(); + const FloatV tMarginA = FSel(aQuadratic, marginA, zero); + const FloatV tMarginB = FSel(bQuadratic, marginB, zero); + + const FloatV sumMargin = FAdd(tMarginA, tMarginB); + const FloatV sumExpandedMargin = FAdd(sumMargin, contactDist); + + FloatV dist = FMax(); + FloatV prevDist = dist; + const Vec3V zeroV = V3Zero(); + Vec3V prevClos = zeroV; + + const BoolV bTrue = BTTTT(); + BoolV bNotTerminated = bTrue; + BoolV bNotDegenerated = bTrue; + Vec3V closest; + + Vec3V Q[4]; + Vec3V A[4]; + Vec3V B[4]; + PxI32 aInd[4]; + PxI32 bInd[4]; + Vec3V supportA = zeroV, supportB = zeroV, support=zeroV; + Vec3V v; + + PxU32 size = 0;//_size; + + + //ML: if _size!=0, which means we pass in the previous frame simplex so that we can warm-start the simplex. + //In this case, GJK will normally terminate in one iteration + if(warmStartSize != 0) + { + for(PxU32 i=0; i sqMargin * sq(v), which means the original objects do not intesect, GJK terminate with GJK_NON_INTERSECT. + //(3)two shapes don't overlap. However, they interect within margin distance. if sq(v)- vw < epsRel2*sq(v), this means the shrunk shapes interect in the margin, + // GJK terminate with GJK_CONTACT. + while(BAllEqTTTT(bNotTerminated)) + { + //prevDist, prevClos are used to store the previous iteration's closest point and the square distance from the closest point + //to origin in Mincowski space + prevDist = dist; + prevClos = closest; + + //de-virtualize + supportA = a.ConvexA::support(V3Neg(closest), aInd[size]); + supportB = b.ConvexB::support(closest, bInd[size]); + + //calculate the support point + support = V3Sub(supportA, supportB); + + const FloatV vw = V3Dot(v, support); + if(FAllGrtr(vw, sumExpandedMargin)) + { + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size); + return GJK_NON_INTERSECT; + } + + //if(FAllGrtr(FMul(epsRel, dist), FSub(dist, vw))) + if(FAllGrtr(vw, FMul(dist, relDif))) + { + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size); + PX_ASSERT(FAllGrtr(dist, FEps())); + //const Vec3V n = V3ScaleInv(closest, dist);//normalise + output.normal = v; + Vec3V closA, closB; + getClosestPoint(Q, A, B, closest, closA, closB, size); + //ML: if one of the shape is sphere/capsule and the takeCoreShape flag is true, the contact point for sphere/capsule will be the sphere center or a point in the + //capsule segment. This will increase the stability for the manifold recycling code. Otherwise, we will return a contact point on the surface for sphere/capsule + //while the takeCoreShape flag is set to be false + if(takeCoreShape) + { + output.closestA= closA; + output.closestB = closB; + output.penDep = dist; + + } + else + { + //This is for capsule/sphere want to take the surface point. For box/convex, + //we need to get rid of margin + output.closestA = V3NegScaleSub(v, tMarginA, closA); + output.closestB = V3ScaleAdd(v, tMarginB, closB); + output.penDep = FSub(dist, sumMargin); + } + + return GJK_CONTACT; + } + + A[size] = supportA; + B[size] = supportB; + Q[size++]=support; + PX_ASSERT(size <= 4); + + //calculate the closest point between two convex hull + closest = GJKCPairDoSimplex(Q, A, B, aInd, bInd, support, size); + + dist = V3Length(closest); + v = V3ScaleInv(closest, dist); + + bNotDegenerated = FIsGrtr(prevDist, dist); + bNotTerminated = BAnd(FIsGrtr(dist, eps), bNotDegenerated); + } + + if(BAllEqFFFF(bNotDegenerated)) + { + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size-1); + + //Reset back to older closest point + dist = prevDist; + closest = prevClos;//V3Sub(closA, closB); + Vec3V closA, closB; + getClosestPoint(Q, A, B, closest, closA, closB, size); + + //PX_ASSERT(FAllGrtr(dist, FEps())); + const Vec3V n = V3ScaleInv(prevClos, prevDist);//normalise + output.normal = n; + + output.searchDir = v; + + if(takeCoreShape) + { + output.closestA = closA; + output.closestB = closB; + output.penDep = dist; + } + else + { + //This is for capsule/sphere want to take the surface point. For box/convex, + //we need to get rid of margin + output.closestA = V3NegScaleSub(n, tMarginA, closA); + output.closestB = V3ScaleAdd(n, tMarginB, closB); + output.penDep = FSub(dist, sumMargin); + if (FAllGrtrOrEq(sumMargin, dist)) + return GJK_CONTACT; + } + + return GJK_DEGENERATE; + } + else + { + //this two shapes are deeply intersected with each other, we need to use EPA algorithm to calculate MTD + assignWarmStartValue(aIndices, bIndices, warmStartSize, aInd, bInd, size); + return EPA_CONTACT; + + } + } + +}//Gu + +}//physx + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h new file mode 100644 index 000000000..79c04931c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h @@ -0,0 +1,295 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKRAYCAST_H +#define GU_GJKRAYCAST_H + +#include "GuGJKType.h" +#include "GuGJKSimplex.h" +#include "GuConvexSupportTable.h" +#include "GuGJKPenetration.h" +#include "GuEPA.h" + + +namespace physx +{ + + +namespace Gu +{ + + /* + ConvexA is in the local space of ConvexB + lambda : the time of impact(TOI) + initialLambda : the start time of impact value (disable) + s : the sweep ray origin + r : the normalized sweep ray direction scaled by the sweep distance. r should be in ConvexB's space + normal : the contact normal + inflation : the amount by which we inflate the swept shape. If the inflated shapes aren't initially-touching, + the TOI will return the time at which both shapes are at a distance equal to inflation separated. If inflation is 0 + the TOI will return the time at which both shapes are touching. + + */ + template + bool gjkRaycast(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation) + { + PX_UNUSED(initialLambda); + + using namespace Ps::aos; + + const FloatV inflation = FLoad(_inflation); + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + const FloatV one = FOne(); + const BoolV bTrue = BTTTT(); + + const FloatV maxDist = FLoad(PX_MAX_REAL); + + FloatV _lambda = zero;//initialLambda; + Vec3V x = V3ScaleAdd(r, _lambda, s); + PxU32 size=1; + + //const Vec3V dir = V3Sub(a.getCenter(), b.getCenter()); + const Vec3V _initialSearchDir = V3Sel(FIsGrtr(V3Dot(initialDir, initialDir), FEps()), initialDir, V3UnitX()); + const Vec3V initialSearchDir = V3Normalize(_initialSearchDir); + + const Vec3V initialSupportA(a.ConvexA::support(V3Neg(initialSearchDir))); + const Vec3V initialSupportB( b.ConvexB::support(initialSearchDir)); + + Vec3V Q[4] = {V3Sub(initialSupportA, initialSupportB), zeroV, zeroV, zeroV}; //simplex set + Vec3V A[4] = {initialSupportA, zeroV, zeroV, zeroV}; //ConvexHull a simplex set + Vec3V B[4] = {initialSupportB, zeroV, zeroV, zeroV}; //ConvexHull b simplex set + + + Vec3V v = V3Neg(Q[0]); + Vec3V supportA = initialSupportA; + Vec3V supportB = initialSupportB; + Vec3V support = Q[0]; + + const FloatV minMargin = FMin(a.ConvexA::getSweepMargin(), b.ConvexB::getSweepMargin()); + const FloatV eps1 = FMul(minMargin, FLoad(0.1f)); + const FloatV inflationPlusEps(FAdd(eps1, inflation)); + const FloatV eps2 = FMul(eps1, eps1); + + const FloatV inflation2 = FMul(inflationPlusEps, inflationPlusEps); + + Vec3V clos(Q[0]); + Vec3V preClos = clos; + //Vec3V closA(initialSupportA); + FloatV sDist = V3Dot(v, v); + FloatV minDist = sDist; + //Vec3V closAA = initialSupportA; + //Vec3V closBB = initialSupportB; + + BoolV bNotTerminated = FIsGrtr(sDist, eps2); + BoolV bNotDegenerated = bTrue; + + Vec3V nor = v; + + while(BAllEqTTTT(bNotTerminated)) + { + + minDist = sDist; + preClos = clos; + + const Vec3V vNorm = V3Normalize(v); + const Vec3V nvNorm = V3Neg(vNorm); + + supportA=a.ConvexA::support(vNorm); + supportB=V3Add(x, b.ConvexB::support(nvNorm)); + + //calculate the support point + support = V3Sub(supportA, supportB); + const Vec3V w = V3Neg(support); + const FloatV vw = FSub(V3Dot(vNorm, w), inflationPlusEps); + if(FAllGrtr(vw, zero)) + { + const FloatV vr = V3Dot(vNorm, r); + if(FAllGrtrOrEq(vr, zero)) + { + return false; + } + else + { + const FloatV _oldLambda = _lambda; + _lambda = FSub(_lambda, FDiv(vw, vr)); + if(FAllGrtr(_lambda, _oldLambda)) + { + if(FAllGrtr(_lambda, one)) + { + return false; + } + const Vec3V bPreCenter = x; + x = V3ScaleAdd(r, _lambda, s); + + const Vec3V offSet =V3Sub(x, bPreCenter); + const Vec3V b0 = V3Add(B[0], offSet); + const Vec3V b1 = V3Add(B[1], offSet); + const Vec3V b2 = V3Add(B[2], offSet); + + B[0] = b0; + B[1] = b1; + B[2] = b2; + + Q[0]=V3Sub(A[0], b0); + Q[1]=V3Sub(A[1], b1); + Q[2]=V3Sub(A[2], b2); + + supportB = V3Add(x, b.ConvexB::support(nvNorm)); + support = V3Sub(supportA, supportB); + minDist = maxDist; + nor = v; + //size=0; + } + } + } + + PX_ASSERT(size < 4); + A[size]=supportA; + B[size]=supportB; + Q[size++]=support; + + //calculate the closest point between two convex hull + clos = GJKCPairDoSimplex(Q, A, B, support, size); + v = V3Neg(clos); + sDist = V3Dot(clos, clos); + + bNotDegenerated = FIsGrtr(minDist, sDist); + bNotTerminated = BAnd(FIsGrtr(sDist, inflation2), bNotDegenerated); + } + + + const BoolV aQuadratic = a.isMarginEqRadius(); + //ML:if the Minkowski sum of two objects are too close to the original(eps2 > sDist), we can't take v because we will lose lots of precision. Therefore, we will take + //previous configuration's normal which should give us a reasonable approximation. This effectively means that, when we do a sweep with inflation, we always keep v because + //the shapes converge separated. If we do a sweep without inflation, we will usually use the previous configuration's normal. + nor = V3Sel(BAnd(FIsGrtr(sDist, eps2), bNotDegenerated), v, nor); + nor = V3Neg(V3NormalizeSafe(nor, V3Zero())); + normal = nor; + lambda = _lambda; + + const Vec3V closestP = V3Sel(bNotDegenerated, clos, preClos); + Vec3V closA = zeroV, closB = zeroV; + getClosestPoint(Q, A, B, closestP, closA, closB, size); + closestA = V3Sel(aQuadratic, V3NegScaleSub(nor, a.getMargin(), closA), closA); + + return true; + } + + + + /* + ConvexA is in the local space of ConvexB + lambda : the time of impact(TOI) + initialLambda : the start time of impact value (disable) + s : the sweep ray origin in ConvexB's space + r : the normalized sweep ray direction scaled by the sweep distance. r should be in ConvexB's space + normal : the contact normal in ConvexB's space + closestA : the tounching contact in ConvexB's space + inflation : the amount by which we inflate the swept shape. If the inflated shapes aren't initially-touching, + the TOI will return the time at which both shapes are at a distance equal to inflation separated. If inflation is 0 + the TOI will return the time at which both shapes are touching. + + */ + template + bool gjkRaycastPenetration(const ConvexA& a, const ConvexB& b, const Ps::aos::Vec3VArg initialDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, + Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation, const bool initialOverlap) + { + using namespace Ps::aos; + Vec3V closA; + Vec3V norm; + FloatV _lambda; + if(gjkRaycast(a, b, initialDir, initialLambda, s, r, _lambda, norm, closA, _inflation)) + { + const FloatV zero = FZero(); + lambda = _lambda; + if(FAllEq(_lambda, zero) && initialOverlap) + { + + //time of impact is zero, the sweep shape is intesect, use epa to get the normal and contact point + const FloatV contactDist = getSweepContactEps(a.getMargin(), b.getMargin()); + + FloatV sDist(zero); + PxU8 aIndices[4]; + PxU8 bIndices[4]; + PxU8 size=0; + GjkOutput output; + + //PX_COMPILE_TIME_ASSERT(typename Shrink::Type != Gu::BoxV); + + typename ConvexA::ConvexGeomType convexA = a.getGjkConvex(); + typename ConvexB::ConvexGeomType convexB = b.getGjkConvex(); + + GjkStatus status = gjkPenetration(convexA, convexB, + initialDir, contactDist, false, aIndices, bIndices, size, output); + //norm = V3Neg(norm); + if(status == GJK_CONTACT) + { + closA = output.closestA; + sDist = output.penDep; + norm = output.normal; + } + else if(status == EPA_CONTACT) + { + status = epaPenetration(a, b, aIndices, bIndices, size, false, FLoad(1.f), output); + + if (status == EPA_CONTACT || status == EPA_DEGENERATE) + { + closA = output.closestA; + sDist = output.penDep; + norm = output.normal; + } + else + { + //ML: if EPA fail, we will use the ray direction as the normal and set pentration to be zero + closA = V3Zero(); + sDist = zero; + norm = V3Normalize(V3Neg(r)); + } + } + else + { + //ML:: this will be gjk degenerate case. However, we can still + //reply on the previous iteration's closest feature information + closA = output.closestA; + sDist = output.penDep; + norm = output.normal; + } + lambda = FMin(zero, sDist); + } + closestA = closA; + normal = norm; + return true; + } + return false; + } +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp new file mode 100644 index 000000000..9dc648cf5 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp @@ -0,0 +1,216 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGJKSimplex.h" + +namespace physx +{ +namespace Gu +{ + + using namespace Ps::aos; + + static Vec3V getClosestPtPointTriangle(Vec3V* PX_RESTRICT Q, const BoolVArg bIsOutside4, PxU32* indices, PxU32& size) + { + FloatV bestSqDist = FMax(); + + PxU32 _indices[3] = {0, 1, 2}; + + Vec3V closestPt = V3Zero(); + + if(BAllEqTTTT(BGetX(bIsOutside4))) + { + //use the original indices, size, v and w + bestSqDist = closestPtPointTriangleBaryCentric(Q[0], Q[1], Q[2], indices, size, closestPt); + } + + if(BAllEqTTTT(BGetY(bIsOutside4))) + { + + PxU32 _size = 3; + _indices[0] = 0; _indices[1] = 2; _indices[2] = 3; + Vec3V tClosestPt; + const FloatV sqDist = closestPtPointTriangleBaryCentric(Q[0], Q[2], Q[3], _indices, _size, tClosestPt); + + const BoolV con = FIsGrtr(bestSqDist, sqDist); + if(BAllEqTTTT(con)) + { + closestPt = tClosestPt; + bestSqDist = sqDist; + + indices[0] = _indices[0]; + indices[1] = _indices[1]; + indices[2] = _indices[2]; + + size = _size; + } + } + + if(BAllEqTTTT(BGetZ(bIsOutside4))) + { + PxU32 _size = 3; + + _indices[0] = 0; _indices[1] = 3; _indices[2] = 1; + + Vec3V tClosestPt; + const FloatV sqDist = closestPtPointTriangleBaryCentric(Q[0], Q[3], Q[1], _indices, _size, tClosestPt); + + const BoolV con = FIsGrtr(bestSqDist, sqDist); + if(BAllEqTTTT(con)) + { + closestPt = tClosestPt; + bestSqDist = sqDist; + + indices[0] = _indices[0]; + indices[1] = _indices[1]; + indices[2] = _indices[2]; + + size = _size; + } + + } + + if(BAllEqTTTT(BGetW(bIsOutside4))) + { + + + PxU32 _size = 3; + _indices[0] = 1; _indices[1] = 3; _indices[2] = 2; + Vec3V tClosestPt; + const FloatV sqDist = closestPtPointTriangleBaryCentric(Q[1], Q[3], Q[2], _indices, _size, tClosestPt); + + const BoolV con = FIsGrtr(bestSqDist, sqDist); + + if(BAllEqTTTT(con)) + { + closestPt = tClosestPt; + bestSqDist = sqDist; + + indices[0] = _indices[0]; + indices[1] = _indices[1]; + indices[2] = _indices[2]; + + size = _size; + } + } + + return closestPt; + } + + PX_NOALIAS Vec3V closestPtPointTetrahedron(Vec3V* PX_RESTRICT Q, Vec3V* PX_RESTRICT A, Vec3V* PX_RESTRICT B, PxU32& size) + { + + const FloatV eps = FLoad(1e-4f); + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V d = Q[3]; + + //degenerated + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V n = V3Normalize(V3Cross(ab, ac)); + const FloatV signDist = V3Dot(n, V3Sub(d, a)); + if(FAllGrtr(eps, FAbs(signDist))) + { + size = 3; + return closestPtPointTriangle(Q, A, B, size); + } + + const BoolV bIsOutside4 = PointOutsideOfPlane4(a, b, c, d); + + if(BAllEqFFFF(bIsOutside4)) + { + //All inside + return V3Zero(); + } + + PxU32 indices[3] = {0, 1, 2}; + + const Vec3V closest = getClosestPtPointTriangle(Q, bIsOutside4, indices, size); + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; const Vec3V q2 = Q[indices[2]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; const Vec3V a2 = A[indices[2]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; const Vec3V b2 = B[indices[2]]; + Q[0] = q0; Q[1] = q1; Q[2] = q2; + A[0] = a0; A[1] = a1; A[2] = a2; + B[0] = b0; B[1] = b1; B[2] = b2; + + return closest; + } + + PX_NOALIAS Vec3V closestPtPointTetrahedron(Vec3V* PX_RESTRICT Q, Vec3V* PX_RESTRICT A, Vec3V* PX_RESTRICT B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, PxU32& size) + { + + const FloatV eps = FLoad(1e-4f); + const Vec3V zeroV = V3Zero(); + + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V d = Q[3]; + + //degenerated + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V n = V3Normalize(V3Cross(ab, ac)); + const FloatV signDist = V3Dot(n, V3Sub(d, a)); + if(FAllGrtr(eps, FAbs(signDist))) + { + size = 3; + return closestPtPointTriangle(Q, A, B, aInd, bInd, size); + } + + const BoolV bIsOutside4 = PointOutsideOfPlane4(a, b, c, d); + + if(BAllEqFFFF(bIsOutside4)) + { + //All inside + return zeroV; + } + + PxU32 indices[3] = {0, 1, 2}; + const Vec3V closest = getClosestPtPointTriangle(Q, bIsOutside4, indices, size); + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; const Vec3V q2 = Q[indices[2]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; const Vec3V a2 = A[indices[2]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; const Vec3V b2 = B[indices[2]]; + const PxI32 _aInd0 = aInd[indices[0]]; const PxI32 _aInd1 = aInd[indices[1]]; const PxI32 _aInd2 = aInd[indices[2]]; + const PxI32 _bInd0 = bInd[indices[0]]; const PxI32 _bInd1 = bInd[indices[1]]; const PxI32 _bInd2 = bInd[indices[2]]; + Q[0] = q0; Q[1] = q1; Q[2] = q2; + A[0] = a0; A[1] = a1; A[2] = a2; + B[0] = b0; B[1] = b1; B[2] = b2; + aInd[0] = _aInd0; aInd[1] = _aInd1; aInd[2] = _aInd2; + bInd[0] = _bInd0; bInd[1] = _bInd1; bInd[2] = _bInd2; + + return closest; + } +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h new file mode 100644 index 000000000..415d94070 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h @@ -0,0 +1,473 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKSIMPLEX_H +#define GU_GJKSIMPLEX_H + +#include "CmPhysXCommon.h" +#include "PsVecMath.h" +#include "GuBarycentricCoordinates.h" + +#if (defined __GNUC__ && defined _DEBUG) +#define PX_GJK_INLINE PX_INLINE +#define PX_GJK_FORCE_INLINE PX_INLINE +#else +#define PX_GJK_INLINE PX_INLINE +#define PX_GJK_FORCE_INLINE PX_FORCE_INLINE +#endif + + +namespace physx +{ +namespace Gu +{ + + + PX_NOALIAS Ps::aos::Vec3V closestPtPointTetrahedron(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, PxU32& size); + + PX_NOALIAS Ps::aos::Vec3V closestPtPointTetrahedron(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, + PxU32& size); + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::BoolV PointOutsideOfPlane4(const Ps::aos::Vec3VArg _a, const Ps::aos::Vec3VArg _b, const Ps::aos::Vec3VArg _c, const Ps::aos::Vec3VArg _d) + { + using namespace Ps::aos; + + // this is not 0 because of the following scenario: + // All the points lie on the same plane and the plane goes through the origin (0,0,0). + // On the Wii U, the math below has the problem that when point A gets projected on the + // plane cumputed by A, B, C, the distance to the plane might not be 0 for the mentioned + // scenario but a small positive or negative value. This can lead to the wrong boolean + // results. Using a small negative value as threshold is more conservative but safer. + const Vec4V zero = V4Load(-1e-6f); + + const Vec3V ab = V3Sub(_b, _a); + const Vec3V ac = V3Sub(_c, _a); + const Vec3V ad = V3Sub(_d, _a); + const Vec3V bd = V3Sub(_d, _b); + const Vec3V bc = V3Sub(_c, _b); + + const Vec3V v0 = V3Cross(ab, ac); + const Vec3V v1 = V3Cross(ac, ad); + const Vec3V v2 = V3Cross(ad, ab); + const Vec3V v3 = V3Cross(bd, bc); + + const FloatV signa0 = V3Dot(v0, _a); + const FloatV signa1 = V3Dot(v1, _a); + const FloatV signa2 = V3Dot(v2, _a); + const FloatV signd3 = V3Dot(v3, _a); + + const FloatV signd0 = V3Dot(v0, _d); + const FloatV signd1 = V3Dot(v1, _b); + const FloatV signd2 = V3Dot(v2, _c); + const FloatV signa3 = V3Dot(v3, _b); + + const Vec4V signa = V4Merge(signa0, signa1, signa2, signa3); + const Vec4V signd = V4Merge(signd0, signd1, signd2, signd3); + return V4IsGrtrOrEq(V4Mul(signa, signd), zero);//same side, outside of the plane + + + } + + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::Vec3V closestPtPointSegment(Ps::aos::Vec3V* PX_RESTRICT Q, PxU32& size) + { + using namespace Ps::aos; + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + + //const Vec3V origin = V3Zero(); + const FloatV zero = FZero(); + const FloatV one = FOne(); + + //Test degenerated case + const Vec3V ab = V3Sub(b, a); + const FloatV denom = V3Dot(ab, ab); + const Vec3V ap = V3Neg(a);//V3Sub(origin, a); + const FloatV nom = V3Dot(ap, ab); + const BoolV con = FIsGrtrOrEq(FEps(), denom);//FIsEq(denom, zero); + //TODO - can we get rid of this branch? The problem is size, which isn't a vector! + if(BAllEqTTTT(con)) + { + size = 1; + return Q[0]; + } + + /* const PxU32 count = BAllEq(con, bTrue); + size = 2 - count;*/ + + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + return V3ScaleAdd(ab, tValue, a); + } + + + PX_FORCE_INLINE void getClosestPoint(const Ps::aos::Vec3V* PX_RESTRICT Q, const Ps::aos::Vec3V* PX_RESTRICT A, const Ps::aos::Vec3V* PX_RESTRICT B, const Ps::aos::Vec3VArg closest, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, const PxU32 size) + { + using namespace Ps::aos; + + switch(size) + { + case 1: + { + closestA = A[0]; + closestB = B[0]; + break; + } + case 2: + { + FloatV v; + barycentricCoordinates(closest, Q[0], Q[1], v); + const Vec3V av = V3Sub(A[1], A[0]); + const Vec3V bv = V3Sub(B[1], B[0]); + closestA = V3ScaleAdd(av, v, A[0]); + closestB = V3ScaleAdd(bv, v, B[0]); + + break; + } + case 3: + { + //calculate the Barycentric of closest point p in the mincowsky sum + FloatV v, w; + barycentricCoordinates(closest, Q[0], Q[1], Q[2], v, w); + + const Vec3V av0 = V3Sub(A[1], A[0]); + const Vec3V av1 = V3Sub(A[2], A[0]); + const Vec3V bv0 = V3Sub(B[1], B[0]); + const Vec3V bv1 = V3Sub(B[2], B[0]); + + closestA = V3Add(A[0], V3Add(V3Scale(av0, v), V3Scale(av1, w))); + closestB = V3Add(B[0], V3Add(V3Scale(bv0, v), V3Scale(bv1, w))); + } + }; + } + + PX_NOALIAS PX_GJK_FORCE_INLINE Ps::aos::FloatV closestPtPointTriangleBaryCentric(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + PxU32* PX_RESTRICT indices, PxU32& size, Ps::aos::Vec3V& closestPt) + { + using namespace Ps::aos; + + size = 3; + const FloatV zero = FZero(); + const FloatV eps = FEps(); + + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + + const Vec3V n = V3Cross(ab, ac); + //ML: if the shape is oblong, the degeneracy test sometime can't catch the degeneracy in the tetraheron. Therefore, we need to make sure we still can ternimate with the previous + //triangle by returning the maxinum distance. + const FloatV nn = V3Dot(n, n); + if (FAllEq(nn, zero)) + return FMax(); + + //const FloatV va = FNegScaleSub(d5, d4, FMul(d3, d6));//edge region of BC + //const FloatV vb = FNegScaleSub(d1, d6, FMul(d5, d2));//edge region of AC + //const FloatV vc = FNegScaleSub(d3, d2, FMul(d1, d4));//edge region of AB + + //const FloatV va = V3Dot(n, V3Cross(b, c));//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + //const FloatV vb = V3Dot(n, V3Cross(c, a));//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + //const FloatV vc = V3Dot(n, V3Cross(a, b));//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + + const VecCrossV crossA = V3PrepareCross(a); + const VecCrossV crossB = V3PrepareCross(b); + const VecCrossV crossC = V3PrepareCross(c); + const Vec3V bCrossC = V3Cross(crossB, crossC); + const Vec3V cCrossA = V3Cross(crossC, crossA); + const Vec3V aCrossB = V3Cross(crossA, crossB); + + const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + + const BoolV isFacePoints = BAnd(FIsGrtrOrEq(va, zero), BAnd(FIsGrtrOrEq(vb, zero), FIsGrtrOrEq(vc, zero))); + + + //face region + if(BAllEqTTTT(isFacePoints)) + { + const FloatV t = FDiv(V3Dot(n, a), nn); + const Vec3V q = V3Scale(n, t); + closestPt = q; + return V3Dot(q, q); + } + + const Vec3V ap = V3Neg(a); + const Vec3V bp = V3Neg(b); + const Vec3V cp = V3Neg(c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + + + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + size = 2; + //check if p in edge region of AB + const BoolV con30 = FIsGrtrOrEq(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtrOrEq(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32));//edge AB region + if(BAllEqTTTT(con3)) + { + const FloatV toRecipAB = FSub(d1, d3); + const FloatV recipAB = FSel(FIsGrtr(FAbs(toRecipAB), eps), FRecip(toRecipAB), zero); + const FloatV t = FMul(d1, recipAB); + const Vec3V q = V3ScaleAdd(ab, t, a); + closestPt = q; + return V3Dot(q, q); + } + + //check if p in edge region of BC + const BoolV con40 = FIsGrtrOrEq(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); //edge BC region + if(BAllEqTTTT(con4)) + { + const Vec3V bc = V3Sub(c, b); + const FloatV toRecipBC = FAdd(unom, udenom); + const FloatV recipBC = FSel(FIsGrtr(FAbs(toRecipBC), eps), FRecip(toRecipBC), zero); + const FloatV t = FMul(unom, recipBC); + indices[0] = indices[1]; + indices[1] = indices[2]; + const Vec3V q = V3ScaleAdd(bc, t, b); + closestPt = q; + return V3Dot(q, q); + } + + //check if p in edge region of AC + const BoolV con50 = FIsGrtrOrEq(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtrOrEq(zero, d6); + + const BoolV con5 = BAnd(con50, BAnd(con51, con52));//edge AC region + if(BAllEqTTTT(con5)) + { + const FloatV toRecipAC = FSub(d2, d6); + const FloatV recipAC = FSel(FIsGrtr(FAbs(toRecipAC), eps), FRecip(toRecipAC), zero); + const FloatV t = FMul(d2, recipAC); + indices[1]=indices[2]; + const Vec3V q = V3ScaleAdd(ac, t, a); + closestPt = q; + return V3Dot(q, q); + } + + size = 1; + //check if p in vertex region outside a + const BoolV con00 = FIsGrtrOrEq(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtrOrEq(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + if(BAllEqTTTT(con0)) + { + closestPt = a; + return V3Dot(a, a); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + indices[0] = indices[1]; + closestPt = b; + return V3Dot(b, b); + } + + //p is in vertex region outside c + indices[0] = indices[2]; + closestPt = c; + return V3Dot(c, c); + + } + + PX_NOALIAS PX_GJK_FORCE_INLINE Ps::aos::Vec3V closestPtPointTriangle(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* A, Ps::aos::Vec3V* B, PxU32& size) + { + + using namespace Ps::aos; + + size = 3; + + const FloatV eps = FEps(); + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V signArea = V3Cross(ab, ac);//0.5*(abXac) + const FloatV area = V3Dot(signArea, signArea); + if(FAllGrtrOrEq(eps, area)) + { + //degenerate + size = 2; + return closestPtPointSegment(Q, size); + } + + PxU32 _size; + PxU32 indices[3]={0, 1, 2}; + Vec3V closestPt; + closestPtPointTriangleBaryCentric(a, b, c, indices, _size, closestPt); + + if(_size != 3) + { + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; + + Q[0] = q0; Q[1] = q1; + A[0] = a0; A[1] = a1; + B[0] = b0; B[1] = b1; + + size = _size; + } + + return closestPt; + } + + PX_NOALIAS PX_GJK_FORCE_INLINE Ps::aos::Vec3V closestPtPointTriangle(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* A, Ps::aos::Vec3V* B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, + PxU32& size) + { + + using namespace Ps::aos; + + size = 3; + + const FloatV eps = FEps(); + + const Vec3V a = Q[0]; + const Vec3V b = Q[1]; + const Vec3V c = Q[2]; + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V signArea = V3Cross(ab, ac);//0.5*(abXac) + const FloatV area = V3Dot(signArea, signArea); + if(FAllGrtrOrEq(eps, area)) + { + //degenerate + size = 2; + return closestPtPointSegment(Q, size); + } + + PxU32 _size; + PxU32 indices[3]={0, 1, 2}; + Vec3V closestPt; + closestPtPointTriangleBaryCentric(a, b, c, indices, _size, closestPt); + + if(_size != 3) + { + + const Vec3V q0 = Q[indices[0]]; const Vec3V q1 = Q[indices[1]]; + const Vec3V a0 = A[indices[0]]; const Vec3V a1 = A[indices[1]]; + const Vec3V b0 = B[indices[0]]; const Vec3V b1 = B[indices[1]]; + const PxI32 aInd0 = aInd[indices[0]]; const PxI32 aInd1 = aInd[indices[1]]; + const PxI32 bInd0 = bInd[indices[0]]; const PxI32 bInd1 = bInd[indices[1]]; + + Q[0] = q0; Q[1] = q1; + A[0] = a0; A[1] = a1; + B[0] = b0; B[1] = b1; + aInd[0] = aInd0; aInd[1] = aInd1; + bInd[0] = bInd0; bInd[1] = bInd1; + + size = _size; + } + + return closestPt; + } + + + + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::Vec3V GJKCPairDoSimplex(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, const Ps::aos::Vec3VArg support, + PxU32& size) + { + using namespace Ps::aos; + + //const PxU32 tempSize = size; + //calculate a closest from origin to the simplex + switch(size) + { + case 1: + { + return support; + } + case 2: + { + return closestPtPointSegment(Q, size); + } + case 3: + { + return closestPtPointTriangle(Q, A, B, size); + } + case 4: + return closestPtPointTetrahedron(Q, A, B, size); + default: + PX_ASSERT(0); + } + return support; + } + + + PX_NOALIAS PX_FORCE_INLINE Ps::aos::Vec3V GJKCPairDoSimplex(Ps::aos::Vec3V* PX_RESTRICT Q, Ps::aos::Vec3V* PX_RESTRICT A, Ps::aos::Vec3V* PX_RESTRICT B, PxI32* PX_RESTRICT aInd, PxI32* PX_RESTRICT bInd, + const Ps::aos::Vec3VArg support, PxU32& size) + { + using namespace Ps::aos; + + //const PxU32 tempSize = size; + //calculate a closest from origin to the simplex + switch(size) + { + case 1: + { + return support; + } + case 2: + { + return closestPtPointSegment(Q, size); + } + case 3: + { + return closestPtPointTriangle(Q, A, B, aInd, bInd, size); + } + case 4: + return closestPtPointTetrahedron(Q, A, B, aInd, bInd, size); + default: + PX_ASSERT(0); + } + return support; + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp new file mode 100644 index 000000000..3e252d511 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuGJK.h" +#include "GuGJKRaycast.h" +#include "GuGJKPenetration.h" +#include "GuGJKTest.h" + +namespace physx +{ +namespace Gu +{ + +using namespace Ps::aos; + +GjkStatus testGjk(GjkConvex& a, GjkConvex& b, const Vec3VArg initialSearchDir, const FloatVArg contactDist, Vec3V& closestA, Vec3V& closestB, Vec3V& normal, FloatV& dist) +{ + return gjk(a, b, initialSearchDir, contactDist, closestA, closestB, normal, dist); +} + + +bool testGjkRaycast(GjkConvex& a, GjkConvex& b, const Vec3VArg initialSearchDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, Ps::aos::FloatV& lambda, + Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal inflation) +{ + return gjkRaycast(a, b, initialSearchDir, initialLambda, s, r, lambda, normal, closestA, inflation); +} + +GjkStatus testGjkPenetration(GjkConvex& a, GjkConvex& b, const Vec3VArg initialSearchDir, const FloatVArg contactDist, + PxU8* aIndices, PxU8* bIndices, PxU8& size, GjkOutput& output) +{ + return gjkPenetration(a, b, initialSearchDir, contactDist, true, + aIndices, bIndices, size, output); +} + +GjkStatus testEpaPenetration(GjkConvex& a, GjkConvex& b, const PxU8* aIndices, const PxU8* bIndices, const PxU8 size, + GjkOutput& output) +{ + return epaPenetration(a, b, aIndices, bIndices, size, true, Ps::aos::FLoad(1.f), output); +} + +} +} + diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h new file mode 100644 index 000000000..412b9a438 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h @@ -0,0 +1,58 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJK_TEST_H +#define GU_GJK_TEST_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "GuGJKUtil.h" + +namespace physx +{ +namespace Gu +{ + struct GjkConvex; + + + PX_PHYSX_COMMON_API GjkStatus testGjk(GjkConvex& a, GjkConvex& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& closestA, Ps::aos::Vec3V& closestB, + Ps::aos::Vec3V& normal, Ps::aos::FloatV& dist); + + PX_PHYSX_COMMON_API bool testGjkRaycast(GjkConvex& a, GjkConvex& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg initialLambda, const Ps::aos::Vec3VArg s, const Ps::aos::Vec3VArg r, + Ps::aos::FloatV& lambda, Ps::aos::Vec3V& normal, Ps::aos::Vec3V& closestA, const PxReal _inflation, const bool initialOverlap); + + PX_PHYSX_COMMON_API GjkStatus testGjkPenetration(GjkConvex& a, GjkConvex& b, const Ps::aos::Vec3VArg initialSearchDir, const Ps::aos::FloatVArg contactDist, + PxU8* aIndices, PxU8* bIndices, PxU8& size, GjkOutput& output); + + PX_PHYSX_COMMON_API GjkStatus testEpaPenetration(GjkConvex& a, GjkConvex& b, const PxU8* aIndices, const PxU8* bIndices, const PxU8 size, + GjkOutput& output); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h new file mode 100644 index 000000000..6856a2611 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h @@ -0,0 +1,177 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKTYPE_H +#define GU_GJKTYPE_H + +#include "GuVecConvex.h" +#include "PsVecTransform.h" + +namespace physx +{ +namespace Gu +{ + class ConvexHullV; + class ConvexHullNoScaleV; + class BoxV; + + template struct ConvexGeom { typedef Convex Type; }; + template <> struct ConvexGeom { typedef ConvexHullV Type; }; + template <> struct ConvexGeom { typedef ConvexHullNoScaleV Type; }; + template <> struct ConvexGeom { typedef BoxV Type; }; + + struct GjkConvexBase + { + + GjkConvexBase(const ConvexV& convex) : mConvex(convex){} + PX_FORCE_INLINE Ps::aos::FloatV getMinMargin() const { return mConvex.getMinMargin(); } + PX_FORCE_INLINE Ps::aos::BoolV isMarginEqRadius() const { return mConvex.isMarginEqRadius(); } + PX_FORCE_INLINE bool getMarginIsRadius() const { return mConvex.getMarginIsRadius(); } + PX_FORCE_INLINE Ps::aos::FloatV getMargin() const { return mConvex.getMargin(); } + + + template + PX_FORCE_INLINE const Convex& getConvex() const { return static_cast(mConvex); } + + virtual Ps::aos::Vec3V supportPoint(const PxI32 index) const = 0; + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const = 0; + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index) const = 0; + virtual Ps::aos::FloatV getSweepMargin() const = 0; + virtual Ps::aos::Vec3V getCenter() const = 0; + virtual ~GjkConvexBase(){} + + + private: + GjkConvexBase& operator = (const GjkConvexBase&); + protected: + const ConvexV& mConvex; + }; + + struct GjkConvex : public GjkConvexBase + { + GjkConvex(const ConvexV& convex) : GjkConvexBase(convex) { } + + virtual Ps::aos::Vec3V supportPoint(const PxI32 index) const { return doVirtualSupportPoint(index); } + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const { return doVirtualSupport(v); } + virtual Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index) const{ return doVirtualSupport(dir, index); } + + virtual Ps::aos::FloatV getSweepMargin() const { return doVirtualGetSweepMargin(); } + + private: + Ps::aos::Vec3V doVirtualSupportPoint(const PxI32 index) const + { + return supportPoint(index); //Call the v-table + } + Ps::aos::Vec3V doVirtualSupport(const Ps::aos::Vec3VArg v) const + { + return support(v); //Call the v-table + } + Ps::aos::Vec3V doVirtualSupport(const Ps::aos::Vec3VArg dir, PxI32& index) const + { + return support(dir, index); //Call the v-table + } + + Ps::aos::FloatV doVirtualGetSweepMargin() const { return getSweepMargin(); } + + GjkConvex& operator = (const GjkConvex&); + }; + + template + struct LocalConvex : public GjkConvex + { + LocalConvex(const Convex& convex) : GjkConvex(convex){} + + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index) const { return getConvex().supportPoint(index); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const { return getConvex().supportLocal(v); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index) const + { + return getConvex().supportLocal(dir, index); + } + + virtual Ps::aos::Vec3V getCenter() const { return getConvex().getCenter(); } + + //ML: we can't force inline function, otherwise win modern will throw compiler error + PX_INLINE LocalConvex::Type > getGjkConvex() const + { + return LocalConvex::Type >(static_cast::Type&>(GjkConvex::mConvex)); + } + + PX_INLINE Ps::aos::FloatV getSweepMargin() const { return getConvex().getSweepMargin(); } + + typedef LocalConvex::Type > ConvexGeomType; + + typedef Convex Type; + + + private: + LocalConvex& operator = (const LocalConvex&); + }; + + template + struct RelativeConvex : public GjkConvex + { + RelativeConvex(const Convex& convex, const Ps::aos::PsMatTransformV& aToB) : GjkConvex(convex), mAToB(aToB), mAToBTransposed(aToB) + { + shdfnd::aos::V3Transpose(mAToBTransposed.rot.col0, mAToBTransposed.rot.col1, mAToBTransposed.rot.col2); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index) const { return mAToB.transform(getConvex().supportPoint(index)); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg v) const { return getConvex().supportRelative(v, mAToB, mAToBTransposed); } + Ps::aos::Vec3V support(const Ps::aos::Vec3VArg dir, PxI32& index) const + { + return getConvex().supportRelative(dir, mAToB, mAToBTransposed, index); + } + + virtual Ps::aos::Vec3V getCenter() const { return mAToB.transform(getConvex().getCenter()); } + + PX_FORCE_INLINE Ps::aos::PsMatTransformV& getRelativeTransform(){ return mAToB; } + + //ML: we can't force inline function, otherwise win modern will throw compiler error + PX_INLINE RelativeConvex::Type > getGjkConvex() const + { + return RelativeConvex::Type >(static_cast::Type&>(GjkConvex::mConvex), mAToB); + } + + PX_INLINE Ps::aos::FloatV getSweepMargin() const { return getConvex().getSweepMargin(); } + + typedef RelativeConvex::Type > ConvexGeomType; + + typedef Convex Type; + + private: + RelativeConvex& operator = (const RelativeConvex&); + const Ps::aos::PsMatTransformV& mAToB; + Ps::aos::PsMatTransformV mAToBTransposed; // PT: precomputed mAToB transpose (because 'rotate' is faster than 'rotateInv') + }; + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h new file mode 100644 index 000000000..b83364d0c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_GJKUTIL_H +#define GU_GJKUTIL_H + +#include "PsVecMath.h" +#include "CmPhysXCommon.h" + +/* + This file is used to avoid the inner loop cross DLL calls +*/ +namespace physx +{ +namespace Gu +{ + +enum GjkStatus +{ + GJK_NON_INTERSECT, // two shapes doesn't intersect + GJK_CLOSE, // two shapes doesn't intersect and gjk algorithm will return closest point information + GJK_CONTACT, // two shapes overlap within margin + GJK_UNDEFINED, // undefined status + GJK_DEGENERATE, // gjk can't converage + + EPA_CONTACT, // two shapes intersect + EPA_DEGENERATE, // epa can't converage + EPA_FAIL // epa fail to construct an initial polygon to work with +}; + +struct GjkOutput +{ +public: + GjkOutput() + { + using namespace Ps::aos; + closestA = closestB = normal = V3Zero(); + penDep = FZero(); + } + Ps::aos::Vec3V closestA; + Ps::aos::Vec3V closestB; + Ps::aos::Vec3V normal; + Ps::aos::Vec3V searchDir; + Ps::aos::FloatV penDep; +}; + +}//Gu +}//physx + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h new file mode 100644 index 000000000..d8e3d167e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h @@ -0,0 +1,226 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_BOX_H +#define GU_VEC_BOX_H + +/** \addtogroup geomutils +@{ +*/ +#include "foundation/PxTransform.h" +#include "PxPhysXCommonConfig.h" +#include "GuVecConvex.h" +#include "PsVecTransform.h" +#include "GuConvexSupportTable.h" +#include "PxBoxGeometry.h" + +namespace physx +{ +PX_PHYSX_COMMON_API extern const Ps::aos::BoolV boxVertexTable[8]; + +namespace Gu +{ + +#define BOX_MARGIN_RATIO 0.15f +#define BOX_MIN_MARGIN_RATIO 0.05f +#define BOX_SWEEP_MARGIN_RATIO 0.05f + +#define BOX_MARGIN_CCD_RATIO 0.01f +#define BOX_MIN_MARGIN_CCD_RATIO 0.005f + + + class CapsuleV; + + + PX_FORCE_INLINE void CalculateBoxMargin(const Ps::aos::Vec3VArg extent, PxReal& margin, PxReal& minMargin, PxReal& sweepMargin, + const PxReal marginR = BOX_MARGIN_RATIO, const PxReal minMarginR = BOX_MIN_MARGIN_RATIO) + { + using namespace Ps::aos; + + PxReal minExtent; + const FloatV min = V3ExtractMin(extent); + FStore(min, &minExtent); + + margin = minExtent * marginR; + minMargin = minExtent * minMarginR; + sweepMargin = minExtent * BOX_SWEEP_MARGIN_RATIO; + } + + PX_FORCE_INLINE Ps::aos::FloatV CalculateBoxTolerance(const Ps::aos::Vec3VArg extent) + { + using namespace Ps::aos; + + const FloatV r0 = FLoad(0.01f); + const FloatV min = V3ExtractMin(extent);//FMin(V3GetX(extent), FMin(V3GetY(extent), V3GetZ(extent))); + return FMul(min, r0); + } + + //This method is called in the PCM contact gen for the refreshing contacts + PX_FORCE_INLINE Ps::aos::FloatV CalculatePCMBoxMargin(const Ps::aos::Vec3VArg extent, const PxReal toleranceLength, const PxReal toleranceMarginRatio = BOX_MARGIN_RATIO) + { + using namespace Ps::aos; + + const FloatV min = V3ExtractMin(extent);//FMin(V3GetX(extent), FMin(V3GetY(extent), V3GetZ(extent))); + const FloatV toleranceMargin = FLoad(toleranceLength * toleranceMarginRatio); + return FMin(FMul(min, FLoad(BOX_MARGIN_RATIO)), toleranceMargin); + } + + PX_FORCE_INLINE Ps::aos::FloatV CalculateMTDBoxMargin(const Ps::aos::Vec3VArg extent) + { + using namespace Ps::aos; + + const FloatV min = V3ExtractMin(extent);//FMin(V3GetX(extent), FMin(V3GetY(extent), V3GetZ(extent))); + return FMul(min, FLoad(BOX_MARGIN_RATIO)); + } + + class BoxV : public ConvexV + { + public: + + /** + \brief Constructor + */ + PX_INLINE BoxV() : ConvexV(ConvexType::eBOX) + { + } + + PX_FORCE_INLINE BoxV(const Ps::aos::Vec3VArg origin, const Ps::aos::Vec3VArg extent) : + ConvexV(ConvexType::eBOX, origin), extents(extent) + { + CalculateBoxMargin(extent, margin, minMargin, sweepMargin); + } + + //this constructor is used by the CCD system + PX_FORCE_INLINE BoxV(const PxGeometry& geom) : ConvexV(ConvexType::eBOX, Ps::aos::V3Zero()) + { + using namespace Ps::aos; + const PxBoxGeometry& boxGeom = static_cast(geom); + const Vec3V extent = Ps::aos::V3LoadU(boxGeom.halfExtents); + extents = extent; + CalculateBoxMargin(extent, margin, minMargin, sweepMargin, BOX_MARGIN_CCD_RATIO, BOX_MIN_MARGIN_CCD_RATIO); + } + + /** + \brief Destructor + */ + PX_INLINE ~BoxV() + { + } + + PX_FORCE_INLINE void resetMargin(const PxReal toleranceLength) + { + minMargin = PxMin(toleranceLength * BOX_MIN_MARGIN_RATIO, minMargin); + } + + //! Assignment operator + PX_FORCE_INLINE const BoxV& operator=(const BoxV& other) + { + center = other.center; + extents = other.extents; + margin = other.margin; + minMargin = other.minMargin; + sweepMargin = other.sweepMargin; + return *this; + } + + PX_FORCE_INLINE void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* verts)const + { + using namespace Ps::aos; + + for(PxU32 i=0; i(geom); + + const Vec3V axis = V3Scale(V3UnitX(), FLoad(capsuleGeom.halfHeight)); + const FloatV r = FLoad(capsuleGeom.radius); + p0 = axis; + p1 = V3Neg(axis); + radius = r; + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + bMarginIsRadius = true; + } + + /** + \brief Constructor + + \param _radius Radius of the capsule. + */ + + /** + \brief Destructor + */ + PX_INLINE ~CapsuleV() + { + } + + PX_FORCE_INLINE void initialize(const Ps::aos::Vec3VArg _p0, const Ps::aos::Vec3VArg _p1, const Ps::aos::FloatVArg _radius) + { + using namespace Ps::aos; + radius = _radius; + p0 = _p0; + p1 = _p1; + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + center = V3Scale(V3Add(_p0, _p1), FHalf()); + } + + PX_INLINE Ps::aos::Vec3V computeDirection() const + { + return Ps::aos::V3Sub(p1, p0); + } + + PX_FORCE_INLINE Ps::aos::FloatV getRadius() const + { + return radius; + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index)const + { + return (&p0)[1-index]; + } + + PX_FORCE_INLINE void getIndex(const Ps::aos::BoolV con, PxI32& index)const + { + using namespace Ps::aos; + const VecI32V v = VecI32V_From_BoolV(con); + const VecI32V t = VecI32V_And(v, VecI32V_One()); + PxI32_From_VecI32V(t, &index); + } + + PX_FORCE_INLINE void setCenter(const Ps::aos::Vec3VArg _center) + { + using namespace Ps::aos; + Vec3V offset = V3Sub(_center, center); + center = _center; + + p0 = V3Add(p0, offset); + p1 = V3Add(p1, offset); + } + + //dir, p0 and p1 are in the local space of dir + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir)const + { + using namespace Ps::aos; + //const Vec3V _dir = V3Normalize(dir); + const FloatV dist0 = V3Dot(p0, dir); + const FloatV dist1 = V3Dot(p1, dir); + return V3Sel(FIsGrtr(dist0, dist1), p0, p1); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportRelative(const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aToB, const Ps::aos::PsMatTransformV& aTobT) const + { + using namespace Ps::aos; + //transform dir into the local space of a +// const Vec3V _dir = aToB.rotateInv(dir); + const Vec3V _dir = aTobT.rotate(dir); + const Vec3V p = supportLocal(_dir); + //transform p back to the local space of b + return aToB.transform(p); + } + + //dir, p0 and p1 are in the local space of dir + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(const Ps::aos::Vec3VArg dir, PxI32& index)const + { + using namespace Ps::aos; + + const FloatV dist0 = V3Dot(p0, dir); + const FloatV dist1 = V3Dot(p1, dir); + const BoolV comp = FIsGrtr(dist0, dist1); + getIndex(comp, index); + return V3Sel(comp, p0, p1); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportRelative( const Ps::aos::Vec3VArg dir, const Ps::aos::PsMatTransformV& aToB, + const Ps::aos::PsMatTransformV& aTobT, PxI32& index)const + { + using namespace Ps::aos; + //transform dir into the local space of a +// const Vec3V _dir = aToB.rotateInv(dir); + const Vec3V _dir = aTobT.rotate(dir); + + const Vec3V p = supportLocal(_dir, index); + //transform p back to the local space of b + return aToB.transform(p); + } + + + PX_FORCE_INLINE Ps::aos::Vec3V supportLocal(Ps::aos::Vec3V& support, const PxI32& index, const Ps::aos::BoolV comp)const + { + PX_UNUSED(index); + + using namespace Ps::aos; + const Vec3V p = V3Sel(comp, p0, p1); + support = p; + return p; + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepMargin() const + { + return Ps::aos::FZero(); + } + + //don't change the order of p0 and p1, the getPoint function depend on the order + Ps::aos::Vec3V p0; //!< Start of segment + Ps::aos::Vec3V p1; //!< End of segment + Ps::aos::FloatV radius; + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h new file mode 100644 index 000000000..4133bc1e9 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h @@ -0,0 +1,179 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_CONVEX_H +#define GU_VEC_CONVEX_H + +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +#define PX_SUPPORT_INLINE PX_FORCE_INLINE +#define PX_SUPPORT_FORCE_INLINE PX_FORCE_INLINE + +namespace physx +{ +namespace Gu +{ + + struct ConvexType + { + enum Type + { + eCONVEXHULL = 0, + eCONVEXHULLNOSCALE = 1, + eSPHERE = 2, + eBOX = 3, + eCAPSULE = 4, + eTRIANGLE = 5 + }; + }; + + class ConvexV + { + public: + + PX_FORCE_INLINE ConvexV(const ConvexType::Type type_) : type(type_), bMarginIsRadius(false) + { + margin = 0.f; + minMargin = 0.f; + sweepMargin = 0.f; + center = Ps::aos::V3Zero(); + } + + PX_FORCE_INLINE ConvexV(const ConvexType::Type type_, const Ps::aos::Vec3VArg center_) : type(type_), bMarginIsRadius(false) + { + using namespace Ps::aos; + center = center_; + margin = 0.f; + minMargin = 0.f; + sweepMargin = 0.f; + } + + //everytime when someone transform the object, they need to up + PX_FORCE_INLINE void setCenter(const Ps::aos::Vec3VArg _center) + { + center = _center; + } + + PX_FORCE_INLINE void setMargin(const Ps::aos::FloatVArg margin_) + { + Ps::aos::FStore(margin_, &margin); + } + + PX_FORCE_INLINE void setMargin(const PxReal margin_) + { + margin = margin_; + } + + + PX_FORCE_INLINE void setMinMargin(const Ps::aos::FloatVArg minMargin_) + { + Ps::aos::FStore(minMargin_, & minMargin); + } + + PX_FORCE_INLINE void setSweepMargin(const Ps::aos::FloatVArg sweepMargin_) + { + Ps::aos::FStore(sweepMargin_, &sweepMargin); + } + + PX_FORCE_INLINE Ps::aos::Vec3V getCenter()const + { + return center; + } + + PX_FORCE_INLINE Ps::aos::FloatV getMargin() const + { + return Ps::aos::FLoad(margin); + } + + PX_FORCE_INLINE Ps::aos::FloatV getMinMargin() const + { + return Ps::aos::FLoad(minMargin); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepMargin() const + { + return Ps::aos::FLoad(sweepMargin); + } + + PX_FORCE_INLINE ConvexType::Type getType() const + { + return type; + } + + PX_FORCE_INLINE Ps::aos::BoolV isMarginEqRadius()const + { + return Ps::aos::BLoad(bMarginIsRadius); + } + + PX_FORCE_INLINE bool getMarginIsRadius() const + { + return bMarginIsRadius; + } + + PX_FORCE_INLINE PxReal getMarginF() const + { + return margin; + } + + + protected: + ~ConvexV(){} + Ps::aos::Vec3V center; + PxReal margin; //margin is the amount by which we shrunk the shape for a convex or box. If the shape are sphere/capsule, margin is the radius + PxReal minMargin; //minMargin is some percentage of marginBase, which is used to determine the termination condition for gjk + PxReal sweepMargin; //sweepMargin minMargin is some percentage of marginBase, which is used to determine the termination condition for gjkRaycast + ConvexType::Type type; + bool bMarginIsRadius; + }; + + PX_FORCE_INLINE Ps::aos::FloatV getContactEps(const Ps::aos::FloatV& _marginA, const Ps::aos::FloatV& _marginB) + { + using namespace Ps::aos; + + const FloatV ratio = FLoad(0.25f); + const FloatV minMargin = FMin(_marginA, _marginB); + + return FMul(minMargin, ratio); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepContactEps(const Ps::aos::FloatV& _marginA, const Ps::aos::FloatV& _marginB) + { + using namespace Ps::aos; + + const FloatV ratio = FLoad(100.f); + const FloatV minMargin = FAdd(_marginA, _marginB); + + return FMul(minMargin, ratio); + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h new file mode 100644 index 000000000..ff675fdb7 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h @@ -0,0 +1,501 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_CONVEXHULL_H +#define GU_VEC_CONVEXHULL_H + +#include "PxPhysXCommonConfig.h" +#include "GuVecConvex.h" +#include "GuConvexMeshData.h" +#include "GuBigConvexData.h" +#include "GuConvexSupportTable.h" +#include "GuCubeIndex.h" +#include "PsFPU.h" +#include "GuGeometryUnion.h" +#include "PsVecQuat.h" +#include "PxMeshScale.h" + +namespace physx +{ +namespace Gu +{ +#define CONVEX_MARGIN_RATIO 0.1f +#define CONVEX_MIN_MARGIN_RATIO 0.05f +#define CONVEX_SWEEP_MARGIN_RATIO 0.025f +#define TOLERANCE_MARGIN_RATIO 0.08f +#define TOLERANCE_MIN_MARGIN_RATIO 0.05f + + + //This margin is used in Persistent contact manifold + PX_SUPPORT_FORCE_INLINE Ps::aos::FloatV CalculatePCMConvexMargin(const Gu::ConvexHullData* hullData, const Ps::aos::Vec3VArg scale, + const PxReal toleranceLength, const PxReal toleranceRatio = TOLERANCE_MIN_MARGIN_RATIO) + { + + using namespace Ps::aos; + const Vec3V extents= V3Mul(V3LoadU(hullData->mInternal.mExtents), scale); + const FloatV min = V3ExtractMin(extents); + const FloatV toleranceMargin = FLoad(toleranceLength * toleranceRatio); + //ML: 25% of the minimum extents of the internal AABB as this convex hull's margin + return FMin(FMul(min, FLoad(0.25f)), toleranceMargin); + } + + PX_SUPPORT_FORCE_INLINE Ps::aos::FloatV CalculateMTDConvexMargin(const Gu::ConvexHullData* hullData, const Ps::aos::Vec3VArg scale) + { + using namespace Ps::aos; + const Vec3V extents = V3Mul(V3LoadU(hullData->mInternal.mExtents), scale); + const FloatV min = V3ExtractMin(extents); + //ML: 25% of the minimum extents of the internal AABB as this convex hull's margin + return FMul(min, FLoad(0.25f)); + } + + + //This minMargin is used in PCM contact gen + PX_SUPPORT_FORCE_INLINE void CalculateConvexMargin(const Gu::ConvexHullData* hullData, PxReal& margin, PxReal& minMargin, PxReal& sweepMargin, + const Ps::aos::Vec3VArg scale) + { + using namespace Ps::aos; + + const Vec3V extents = V3Mul(V3LoadU(hullData->mInternal.mExtents), scale); + const FloatV min_ = V3ExtractMin(extents); + + PxReal minExtent; + FStore(min_, &minExtent); + + //margin is used as acceptanceTolerance for overlap. + margin = minExtent * CONVEX_MARGIN_RATIO; + //minMargin is used in the GJK termination condition + minMargin = minExtent * CONVEX_MIN_MARGIN_RATIO; + //this is used for sweep(gjkRaycast) + sweepMargin = minExtent * CONVEX_SWEEP_MARGIN_RATIO; + } + + PX_SUPPORT_FORCE_INLINE Ps::aos::Mat33V ConstructSkewMatrix(const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg rotation) + { + using namespace Ps::aos; + Mat33V rot; + QuatGetMat33V(rotation, rot.col0, rot.col1, rot.col2); + Mat33V trans = M33Trnsps(rot); + trans.col0 = V3Scale(trans.col0, V3GetX(scale)); + trans.col1 = V3Scale(trans.col1, V3GetY(scale)); + trans.col2 = V3Scale(trans.col2, V3GetZ(scale)); + return M33MulM33(trans, rot); + } + + PX_SUPPORT_FORCE_INLINE void ConstructSkewMatrix(const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg rotation, Ps::aos::Mat33V& vertex2Shape, Ps::aos::Mat33V& shape2Vertex, Ps::aos::Vec3V& center, const bool idtScale) + { + using namespace Ps::aos; + + PX_ASSERT(!V3AllEq(scale, V3Zero())); + + if(idtScale) + { + //create identity buffer + const Mat33V identity = M33Identity(); + vertex2Shape = identity; + shape2Vertex = identity; + } + else + { + const FloatV scaleX = V3GetX(scale); + const Vec3V invScale = V3Recip(scale); + + //this is uniform scale + if(V3AllEq(V3Splat(scaleX), scale)) + { + vertex2Shape = M33Diagonal(scale); + shape2Vertex = M33Diagonal(invScale); + } + else + { + Mat33V rot; + QuatGetMat33V(rotation, rot.col0, rot.col1, rot.col2); + const Mat33V trans = M33Trnsps(rot); + /* + vertex2shape + skewMat = Inv(R)*Diagonal(scale)*R; + */ + + const Mat33V temp(V3Scale(trans.col0, scaleX), V3Scale(trans.col1, V3GetY(scale)), V3Scale(trans.col2, V3GetZ(scale))); + vertex2Shape = M33MulM33(temp, rot); + + //don't need it in the support function + /* + shape2Vertex + invSkewMat =(invSkewMat)= Inv(R)*Diagonal(1/scale)*R; + */ + + shape2Vertex.col0 = V3Scale(trans.col0, V3GetX(invScale)); + shape2Vertex.col1 = V3Scale(trans.col1, V3GetY(invScale)); + shape2Vertex.col2 = V3Scale(trans.col2, V3GetZ(invScale)); + shape2Vertex = M33MulM33(shape2Vertex, rot); + + //shape2Vertex = M33Inverse(vertex2Shape); + } + + //transform center to shape space + center = M33MulV3(vertex2Shape, center); + } + } + + PX_SUPPORT_FORCE_INLINE Ps::aos::Mat33V ConstructVertex2ShapeMatrix(const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg rotation) + { + using namespace Ps::aos; + Mat33V rot; + QuatGetMat33V(rotation, rot.col0, rot.col1, rot.col2); + const Mat33V trans = M33Trnsps(rot); + /* + vertex2shape + skewMat = Inv(R)*Diagonal(scale)*R; + */ + + const Mat33V temp(V3Scale(trans.col0, V3GetX(scale)), V3Scale(trans.col1, V3GetY(scale)), V3Scale(trans.col2, V3GetZ(scale))); + return M33MulM33(temp, rot); + } + + + class ConvexHullV : public ConvexV + { + + class TinyBitMap + { + public: + PxU32 m[8]; + PX_FORCE_INLINE TinyBitMap() { m[0] = m[1] = m[2] = m[3] = m[4] = m[5] = m[6] = m[7] = 0; } + PX_FORCE_INLINE void set(PxU8 v) { m[v >> 5] |= 1 << (v & 31); } + PX_FORCE_INLINE bool get(PxU8 v) const { return (m[v >> 5] & 1 << (v & 31)) != 0; } + }; + + + public: + /** + \brief Constructor + */ + PX_SUPPORT_INLINE ConvexHullV() : ConvexV(ConvexType::eCONVEXHULL) + { + } + + PX_SUPPORT_INLINE ConvexHullV(const Gu::ConvexHullData* _hullData, const Ps::aos::Vec3VArg _center, const Ps::aos::Vec3VArg scale, const Ps::aos::QuatVArg scaleRot, + const bool idtScale) : + ConvexV(ConvexType::eCONVEXHULL, _center) + { + using namespace Ps::aos; + + hullData = _hullData; + const PxVec3* PX_RESTRICT tempVerts = _hullData->getHullVertices(); + verts = tempVerts; + numVerts = _hullData->mNbHullVertices; + CalculateConvexMargin(_hullData, margin, minMargin, sweepMargin, scale); + ConstructSkewMatrix(scale, scaleRot, vertex2Shape, shape2Vertex, center, idtScale); + data = _hullData->mBigConvexRawData; + } + + //this is used by CCD system + PX_SUPPORT_INLINE ConvexHullV(const PxGeometry& geom) : ConvexV(ConvexType::eCONVEXHULL, Ps::aos::V3Zero()) + { + using namespace Ps::aos; + const PxConvexMeshGeometryLL& convexGeom = static_cast(geom); + const Gu::ConvexHullData* hData = convexGeom.hullData; + + const Vec3V vScale = V3LoadU_SafeReadW(convexGeom.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vRot = QuatVLoadU(&convexGeom.scale.rotation.x); + const bool idtScale = convexGeom.scale.isIdentity(); + + hullData = hData; + const PxVec3* PX_RESTRICT tempVerts = hData->getHullVertices(); + verts = tempVerts; + numVerts = hData->mNbHullVertices; + CalculateConvexMargin(hData, margin, minMargin, sweepMargin, vScale); + ConstructSkewMatrix(vScale, vRot, vertex2Shape, shape2Vertex, center, idtScale); + + data = hData->mBigConvexRawData; + + } + + PX_SUPPORT_INLINE void initialize(const Gu::ConvexHullData* _hullData, const Ps::aos::Vec3VArg _center, const Ps::aos::Vec3VArg scale, + const Ps::aos::QuatVArg scaleRot, const bool idtScale) + { + using namespace Ps::aos; + + const PxVec3* tempVerts = _hullData->getHullVertices(); + CalculateConvexMargin(_hullData, margin, minMargin, sweepMargin, scale); + ConstructSkewMatrix(scale, scaleRot, vertex2Shape, shape2Vertex, center, idtScale); + + verts = tempVerts; + numVerts = _hullData->mNbHullVertices; + //rot = _rot; + + center = _center; + + // searchIndex = 0; + data = _hullData->mBigConvexRawData; + + hullData = _hullData; + if (_hullData->mBigConvexRawData) + { + Ps::prefetchLine(hullData->mBigConvexRawData->mValencies); + Ps::prefetchLine(hullData->mBigConvexRawData->mValencies, 128); + Ps::prefetchLine(hullData->mBigConvexRawData->mAdjacentVerts); + } + } + + + PX_FORCE_INLINE void resetMargin(const PxReal toleranceLength) + { + const PxReal toleranceMinMargin = toleranceLength * TOLERANCE_MIN_MARGIN_RATIO; + const PxReal toleranceMargin = toleranceLength * TOLERANCE_MARGIN_RATIO; + + margin = PxMin(margin, toleranceMargin); + minMargin = PxMin(minMargin, toleranceMinMargin); + } + + PX_FORCE_INLINE Ps::aos::Vec3V supportPoint(const PxI32 index)const + { + using namespace Ps::aos; + + return M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[index])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + } + + PX_NOINLINE PxU32 hillClimbing(const Ps::aos::Vec3VArg _dir)const + { + using namespace Ps::aos; + + const Gu::Valency* valency = data->mValencies; + const PxU8* adjacentVerts = data->mAdjacentVerts; + + //NotSoTinyBitMap visited; + PxU32 smallBitMap[8] = {0,0,0,0,0,0,0,0}; + + // PxU32 index = searchIndex; + PxU32 index = 0; + + { + PxVec3 vertexSpaceDirection; + V3StoreU(_dir, vertexSpaceDirection); + const PxU32 offset = ComputeCubemapNearestOffset(vertexSpaceDirection, data->mSubdiv); + //const PxU32 offset = ComputeCubemapOffset(vertexSpaceDirection, data->mSubdiv); + index = data->mSamples[offset]; + } + + Vec3V maxPoint = V3LoadU_SafeReadW(verts[index]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + FloatV max = V3Dot(maxPoint, _dir); + + PxU32 initialIndex = index; + + do + { + initialIndex = index; + const PxU32 numNeighbours = valency[index].mCount; + const PxU32 offset = valency[index].mOffset; + + for(PxU32 a = 0; a < numNeighbours; ++a) + { + const PxU32 neighbourIndex = adjacentVerts[offset + a]; + + const Vec3V vertex = V3LoadU_SafeReadW(verts[neighbourIndex]); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + const FloatV dist = V3Dot(vertex, _dir); + if(FAllGrtr(dist, max)) + { + const PxU32 ind = neighbourIndex>>5; + const PxU32 mask = PxU32(1 << (neighbourIndex & 31)); + if((smallBitMap[ind] & mask) == 0) + { + smallBitMap[ind] |= mask; + max = dist; + index = neighbourIndex; + } + } + } + + }while(index != initialIndex); + + return index; + } + + PX_SUPPORT_INLINE PxU32 bruteForceSearch(const Ps::aos::Vec3VArg _dir)const + { + using namespace Ps::aos; + //brute force + PxVec3 dir; + V3StoreU(_dir, dir); + + PxReal max = verts[0].dot(dir); + PxU32 maxIndex = 0; + + for (PxU32 i = 1; i < numVerts; ++i) + { + const PxReal dist = verts[i].dot(dir); + if (dist > max) + { + max = dist; + maxIndex = i; + } + } + return maxIndex; + } + + //points are in vertex space, _dir in vertex space + PX_NOINLINE PxU32 supportVertexIndex(const Ps::aos::Vec3VArg _dir)const + { + using namespace Ps::aos; + if(data) + return hillClimbing(_dir); + else + return bruteForceSearch(_dir); + } + + //dir is in the vertex space + PX_SUPPORT_INLINE void bruteForceSearchMinMax(const Ps::aos::Vec3VArg _dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + using namespace Ps::aos; + + //brute force + PxVec3 dir; + V3StoreU(_dir, dir); + //get the support point from the orignal margin + PxReal _max = verts[0].dot(dir); + PxReal _min = _max; + + for(PxU32 i = 1; i < numVerts; ++i) + { + const PxReal dist = verts[i].dot(dir); + _max = PxMax(dist, _max); + _min = PxMin(dist, _min); + } + min = FLoad(_min); + max = FLoad(_max); + } + + //This function is used in the full contact manifold generation code, points are in vertex space. + //This function support scaling, _dir is in the shape space + PX_SUPPORT_INLINE void supportVertexMinMax(const Ps::aos::Vec3VArg _dir, Ps::aos::FloatV& min, Ps::aos::FloatV& max)const + { + using namespace Ps::aos; + + //dir is in the vertex space + const Vec3V dir = M33TrnspsMulV3(vertex2Shape, _dir); + + if(data) + { + const PxU32 maxIndex= hillClimbing(dir); + const PxU32 minIndex= hillClimbing(V3Neg(dir)); + const Vec3V maxPoint= M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[maxIndex])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + const Vec3V minPoint= M33MulV3(vertex2Shape, V3LoadU_SafeReadW(verts[minIndex])); // PT: safe because of the way vertex memory is allocated in ConvexHullData (and 'verts' is initialized with ConvexHullData::getHullVertices()) + min = V3Dot(_dir, minPoint); + max = V3Dot(_dir, maxPoint); + } + else + { + //dir is in the vertex space + bruteForceSearchMinMax(dir, min, max); + } + } + + //This function is used in the full contact manifold generation code + PX_SUPPORT_INLINE void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* _verts)const + { + using namespace Ps::aos; + + for(PxU32 i=0; i(x)) +} + +} + +#endif // diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h new file mode 100644 index 000000000..ca95a2d87 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h @@ -0,0 +1,224 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_PLANE_H +#define GU_VEC_PLANE_H +/** \addtogroup geomutils +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxPlane.h" +#include "CmPhysXCommon.h" +#include "PsVecMath.h" + +/** +\brief Representation of a plane. + +Plane equation used: a*x + b*y + c*z + d = 0 +*/ +namespace physx +{ +namespace Gu +{ + + class PlaneV + { + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE PlaneV() + { + } + + /** + \brief Constructor from a normal and a distance + */ + PX_FORCE_INLINE PlaneV(const Ps::aos::FloatVArg nx, const Ps::aos::FloatVArg ny, const Ps::aos::FloatVArg nz, const Ps::aos::FloatVArg _d) + { + set(nx, ny, nz, _d); + } + + PX_FORCE_INLINE PlaneV(const PxPlane& plane) + { + using namespace Ps::aos; + const Vec3V _n = V3LoadU(plane.n); + const FloatV _d = FLoad(plane.d); + nd = V4SetW(Vec4V_From_Vec3V(_n), _d); + } + + + /** + \brief Constructor from three points + */ + PX_FORCE_INLINE PlaneV(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2) + { + set(p0, p1, p2); + } + + /** + \brief Constructor from a normal and a distance + */ + PX_FORCE_INLINE PlaneV(const Ps::aos::Vec3VArg _n, const Ps::aos::FloatVArg _d) + { + nd = Ps::aos::V4SetW(Ps::aos::Vec4V_From_Vec3V(_n), _d); + } + + /** + \brief Copy constructor + */ + PX_FORCE_INLINE PlaneV(const PlaneV& plane) : nd(plane.nd) + { + } + + /** + \brief Destructor + */ + PX_FORCE_INLINE ~PlaneV() + { + } + + /** + \brief Sets plane to zero. + */ + PX_FORCE_INLINE PlaneV& setZero() + { + nd = Ps::aos::V4Zero(); + return *this; + } + + PX_FORCE_INLINE PlaneV& set(const Ps::aos::FloatVArg nx, const Ps::aos::FloatVArg ny, const Ps::aos::FloatVArg nz, const Ps::aos::FloatVArg _d) + { + + using namespace Ps::aos; + const Vec3V n= V3Merge(nx, ny, nz); + nd = V4SetW(Vec4V_From_Vec3V(n), _d); + return *this; + } + + PX_FORCE_INLINE PlaneV& set(const Ps::aos::Vec3VArg _normal, Ps::aos::FloatVArg _d) + { + nd = Ps::aos::V4SetW(Ps::aos::Vec4V_From_Vec3V(_normal), _d); + return *this; + } + + /** + \brief Computes the plane equation from 3 points. + */ + PlaneV& set(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2) + { + using namespace Ps::aos; + const Vec3V edge0 = V3Sub(p1, p0); + const Vec3V edge1 = V3Sub(p2, p0); + + const Vec3V n = V3Normalize(V3Cross(edge0, edge1)); + // See comments in set() for computation of d + const FloatV d = FNeg(V3Dot(p0, n)); + nd = V4SetW(Vec4V_From_Vec3V(n), d); + return *this; + } + + /*** + \brief Computes distance, assuming plane is normalized + \sa normalize + */ + PX_FORCE_INLINE Ps::aos::FloatV distance(const Ps::aos::Vec3VArg p) const + { + // Valid for plane equation a*x + b*y + c*z + d = 0 + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return FAdd(V3Dot(p, n), V4GetW(nd)); + } + + PX_FORCE_INLINE Ps::aos::BoolV belongs(const Ps::aos::Vec3VArg p) const + { + using namespace Ps::aos; + const FloatV eps = FLoad(1.0e-7f); + return FIsGrtr(eps, FAbs(distance(p))); + } + + /** + \brief projects p into the plane + */ + PX_FORCE_INLINE Ps::aos::Vec3V project(const Ps::aos::Vec3VArg p) const + { + // Pretend p is on positive side of plane, i.e. plane.distance(p)>0. + // To project the point we have to go in a direction opposed to plane's normal, i.e.: + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return V3Sub(p, V3Scale(n, V4GetW(nd))); + + } + + PX_FORCE_INLINE Ps::aos::FloatV signedDistanceHessianNormalForm(const Ps::aos::Vec3VArg point) const + { + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return FAdd(V3Dot(n, point), V4GetW(nd)); + } + + PX_FORCE_INLINE Ps::aos::Vec3V getNormal() const + { + return Ps::aos::Vec3V_From_Vec4V(nd); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSignDist() const + { + return Ps::aos::V4GetW(nd); + } + + /** + \brief find an arbitrary point in the plane + */ + PX_FORCE_INLINE Ps::aos::Vec3V pointInPlane() const + { + // Project origin (0,0,0) to plane: + // (0) - normal * distance(0) = - normal * ((p|(0)) + d) = -normal*d + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + return V3Neg(V3Scale(n, V4GetW(nd))); + } + + PX_FORCE_INLINE void normalize() + { + using namespace Ps::aos; + const Vec3V n = Vec3V_From_Vec4V(nd); + const FloatV denom = FRecip(V3Length(n)); + V4Scale(nd, denom); + } + + Ps::aos::Vec4V nd; //!< The normal to the plan , w store the distance from the origin + }; +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h new file mode 100644 index 000000000..95845b192 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h @@ -0,0 +1,243 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_SPHERE_H +#define GU_VEC_SPHERE_H +/** \addtogroup geomutils +@{ +*/ + +#include "GuVecConvex.h" +#include "GuConvexSupportTable.h" +#include "PxSphereGeometry.h" + +/** +\brief Represents a sphere defined by its center point and radius. +*/ +namespace physx +{ +namespace Gu +{ + class SphereV : public ConvexV + { + public: + /** + \brief Constructor + */ + PX_INLINE SphereV(): ConvexV(ConvexType::eSPHERE) + { + radius = Ps::aos::FZero(); + bMarginIsRadius = true; + } + + PX_INLINE SphereV(const Ps::aos::Vec3VArg _center, const Ps::aos::FloatV _radius) : ConvexV(ConvexType::eSPHERE, _center) + { + using namespace Ps::aos; + radius = _radius; + FStore(radius, &margin); + FStore(radius, &minMargin); + FStore(radius, &sweepMargin); + bMarginIsRadius = true; + } + + + /** + \brief Copy constructor + */ + PX_INLINE SphereV(const SphereV& sphere) : ConvexV(ConvexType::eSPHERE), radius(sphere.radius) + { + + margin = sphere.margin; + minMargin = sphere.minMargin; + sweepMargin = sphere.sweepMargin; + bMarginIsRadius = true; + } + + PX_INLINE SphereV(const PxGeometry& geom) : ConvexV(ConvexType::eSPHERE, Ps::aos::V3Zero()) + { + using namespace Ps::aos; + const PxSphereGeometry& sphereGeom = static_cast(geom); + const FloatV r = FLoad(sphereGeom.radius); + radius = r; + margin = sphereGeom.radius; + minMargin = sphereGeom.radius; + sweepMargin = sphereGeom.radius; + bMarginIsRadius = true; + } + + /** + \brief Destructor + */ + PX_INLINE ~SphereV() + { + } + + PX_INLINE void setV(const Ps::aos::Vec3VArg _center, const Ps::aos::FloatVArg _radius) + { + center = _center; + radius = _radius; + } + + /** + \brief Checks the sphere is valid. + + \return true if the sphere is valid + */ + PX_INLINE bool isValid() const + { + // Consistency condition for spheres: Radius >= 0.0f + using namespace Ps::aos; + return BAllEqTTTT(FIsGrtrOrEq(radius, FZero())) != 0; + } + + /** + \brief Tests if a point is contained within the sphere. + + \param[in] p the point to test + \return true if inside the sphere + */ + PX_INLINE bool contains(const Ps::aos::Vec3VArg p) const + { + using namespace Ps::aos; + const FloatV rr = FMul(radius, radius); + const FloatV cc = V3LengthSq(V3Sub(center, p)); + return FAllGrtrOrEq(rr, cc) != 0; + } + + /** + \brief Tests if a sphere is contained within the sphere. + + \param sphere [in] the sphere to test + \return true if inside the sphere + */ + PX_INLINE bool contains(const SphereV& sphere) const + { + using namespace Ps::aos; + + const Vec3V centerDif= V3Sub(center, sphere.center); + const FloatV radiusDif = FSub(radius, sphere.radius); + const FloatV cc = V3Dot(centerDif, centerDif); + const FloatV rr = FMul(radiusDif, radiusDif); + + const BoolV con0 = FIsGrtrOrEq(radiusDif, FZero());//might contain + const BoolV con1 = FIsGrtr(rr, cc);//return true + return BAllEqTTTT(BAnd(con0, con1))==1; + } + + /** + \brief Tests if a box is contained within the sphere. + + \param minimum [in] minimum value of the box + \param maximum [in] maximum value of the box + \return true if inside the sphere + */ + PX_INLINE bool contains(const Ps::aos::Vec3VArg minimum, const Ps::aos::Vec3VArg maximum) const + { + + //compute the sphere which wrap around the box + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV half = FHalf(); + + const Vec3V boxSphereCenter = V3Scale(V3Add(maximum, minimum), half); + const Vec3V v = V3Scale(V3Sub(maximum, minimum), half); + const FloatV boxSphereR = V3Length(v); + + const Vec3V w = V3Sub(center, boxSphereCenter); + const FloatV wLength = V3Length(w); + const FloatV dif = FSub(FSub(radius, wLength), boxSphereR); + + return FAllGrtrOrEq(dif, zero) != 0; + } + + /** + \brief Tests if the sphere intersects another sphere + + \param sphere [in] the other sphere + \return true if spheres overlap + */ + PX_INLINE bool intersect(const SphereV& sphere) const + { + using namespace Ps::aos; + const Vec3V centerDif = V3Sub(center, sphere.center); + const FloatV cc = V3Dot(centerDif, centerDif); + const FloatV r = FAdd(radius, sphere.radius); + const FloatV rr = FMul(r, r); + return FAllGrtrOrEq(rr, cc) != 0; + } + + //return point in local space + PX_FORCE_INLINE Ps::aos::Vec3V getPoint(const PxU8) + { + return Ps::aos::V3Zero(); + } + // + //sweep code need to have full version + PX_FORCE_INLINE Ps::aos::Vec3V supportSweep(const Ps::aos::Vec3VArg dir)const + { + using namespace Ps::aos; + const Vec3V _dir = V3Normalize(dir); + return V3ScaleAdd(_dir, radius, center); + } + + //make the support function the same as support margin + PX_FORCE_INLINE Ps::aos::Vec3V support(const Ps::aos::Vec3VArg)const + { + return center;//_margin is the same as radius + } + + + PX_FORCE_INLINE Ps::aos::Vec3V supportMargin(const Ps::aos::Vec3VArg dir, const Ps::aos::FloatVArg _margin, Ps::aos::Vec3V& support)const + { + PX_UNUSED(_margin); + PX_UNUSED(dir); + + support = center; + return center;//_margin is the same as radius + } + + PX_FORCE_INLINE Ps::aos::BoolV isMarginEqRadius()const + { + return Ps::aos::BTTTT(); + } + + PX_FORCE_INLINE Ps::aos::FloatV getSweepMargin() const + { + return Ps::aos::FZero(); + } + + + Ps::aos::FloatV radius; //!< Sphere's center, w component is radius + + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h new file mode 100644 index 000000000..6dd676ab5 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h @@ -0,0 +1,268 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_VEC_TRIANGLE_H +#define GU_VEC_TRIANGLE_H +/** \addtogroup geomutils + @{ +*/ + +#include "GuVecConvex.h" +#include "GuConvexSupportTable.h" +#include "GuDistancePointTriangleSIMD.h" + +namespace physx +{ +namespace Gu +{ + + + class TriangleV : public ConvexV + { + public: + /** + \brief Constructor + */ + PX_FORCE_INLINE TriangleV() : ConvexV(ConvexType::eTRIANGLE) + { + margin = 0.02f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + /** + \brief Constructor + + \param[in] p0 Point 0 + \param[in] p1 Point 1 + \param[in] p2 Point 2 + */ + + PX_FORCE_INLINE TriangleV(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2): ConvexV(ConvexType::eTRIANGLE) + { + using namespace Ps::aos; + //const FloatV zero = FZero(); + const FloatV num = FLoad(0.333333f); + center = V3Scale(V3Add(V3Add(p0, p1), p2), num); + verts[0] = p0; + verts[1] = p1; + verts[2] = p2; + margin = 0.f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + + PX_FORCE_INLINE TriangleV(const PxVec3* pts) : ConvexV(ConvexType::eTRIANGLE) + { + using namespace Ps::aos; + const Vec3V p0 = V3LoadU(pts[0]); + const Vec3V p1 = V3LoadU(pts[1]); + const Vec3V p2 = V3LoadU(pts[2]); + const FloatV num = FLoad(0.333333f); + center = V3Scale(V3Add(V3Add(p0, p1), p2), num); + verts[0] = p0; + verts[1] = p1; + verts[2] = p2; + margin = 0.f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + + /** + \brief Copy constructor + + \param[in] triangle Tri to copy + */ + PX_FORCE_INLINE TriangleV(const Gu::TriangleV& triangle) : ConvexV(ConvexType::eTRIANGLE) + { + using namespace Ps::aos; + verts[0] = triangle.verts[0]; + verts[1] = triangle.verts[1]; + verts[2] = triangle.verts[2]; + + center = triangle.center; + margin = 0.f; + minMargin = PX_MAX_REAL; + sweepMargin = PX_MAX_REAL; + } + /** + \brief Destructor + */ + PX_FORCE_INLINE ~TriangleV() + { + } + + PX_FORCE_INLINE void populateVerts(const PxU8* inds, PxU32 numInds, const PxVec3* originalVerts, Ps::aos::Vec3V* vertexs)const + { + using namespace Ps::aos; + + for(PxU32 i=0; i +class EntityReport +{ + public: + + virtual ~EntityReport() {} + + virtual PxAgain onEvent(PxU32 nbEntities, T* entities) = 0; +}; + + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp new file mode 100644 index 000000000..d7e795d61 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp @@ -0,0 +1,711 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxMemory.h" +#include "PsIntrinsics.h" +#include "GuHeightField.h" +#include "PsAllocator.h" +#include "PsUtilities.h" +#include "GuMeshFactory.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "CmBitMap.h" +#include "PsFoundation.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Gu::HeightField::HeightField(GuMeshFactory* meshFactory) +: PxHeightField(PxConcreteType::eHEIGHTFIELD, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mSampleStride (0) +, mNbSamples (0) +, mMinHeight (0.0f) +, mMaxHeight (0.0f) +, mModifyCount (0) +, mMeshFactory (meshFactory) +{ + mData.format = PxHeightFieldFormat::eS16_TM; + mData.rows = 0; + mData.columns = 0; + mData.convexEdgeThreshold = 0; + mData.flags = PxHeightFieldFlags(); + mData.samples = NULL; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Gu::HeightField::HeightField(GuMeshFactory& factory, Gu::HeightFieldData& data) +: PxHeightField(PxConcreteType::eHEIGHTFIELD, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mSampleStride (0) +, mNbSamples (0) +, mMinHeight (0.0f) +, mMaxHeight (0.0f) +, mModifyCount (0) +, mMeshFactory (&factory) +{ + mData = data; + data.samples = NULL; // set to null so that we don't release the memory +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Gu::HeightField::~HeightField() +{ + releaseMemory(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PX_SERIALIZATION +void Gu::HeightField::onRefCountZero() +{ + PX_ASSERT(mMeshFactory); + if(mMeshFactory->removeHeightField(*this)) + { + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, PxConcreteType::eHEIGHTFIELD); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::HeightField::onRefCountZero: double deletion detected!"); +} + +void Gu::HeightField::exportExtraData(PxSerializationContext& stream) +{ + // PT: warning, order matters for the converter. Needs to export the base stuff first + const PxU32 size = mData.rows * mData.columns * sizeof(PxHeightFieldSample); + stream.alignData(PX_SERIAL_ALIGN); // PT: generic align within the generic allocator + stream.writeData(mData.samples, size); +} + +void Gu::HeightField::importExtraData(PxDeserializationContext& context) +{ + mData.samples = context.readExtraData(mData.rows * mData.columns); +} + +Gu::HeightField* Gu::HeightField::createObject(PxU8*& address, PxDeserializationContext& context) +{ + HeightField* obj = new (address) HeightField(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(HeightField); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +//~PX_SERIALIZATION + +void Gu::HeightField::release() +{ + decRefCount(); +} + +void Gu::HeightField::acquireReference() +{ + incRefCount(); +} + +PxU32 Gu::HeightField::getReferenceCount() const +{ + return getRefCount(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Gu::HeightField::modifySamples(PxI32 startCol, PxI32 startRow, const PxHeightFieldDesc& desc, bool shrinkBounds) +{ + const PxU32 nbCols = getNbColumns(); + const PxU32 nbRows = getNbRows(); + PX_CHECK_AND_RETURN_NULL(desc.format == mData.format, "Gu::HeightField::modifySamples: desc.format mismatch"); + //PX_CHECK_AND_RETURN_NULL(startCol + desc.nbColumns <= nbCols, + // "Gu::HeightField::modifySamples: startCol + nbColumns out of range"); + //PX_CHECK_AND_RETURN_NULL(startRow + desc.nbRows <= nbRows, + // "Gu::HeightField::modifySamples: startRow + nbRows out of range"); + //PX_CHECK_AND_RETURN_NULL(desc.samples.stride == mSampleStride, "Gu::HeightField::modifySamples: desc.samples.stride mismatch"); + + // by default bounds don't shrink since the whole point of this function is to avoid modifying the whole HF + // unless shrinkBounds is specified. then the bounds will be fully recomputed later + PxReal minHeight = mMinHeight; + PxReal maxHeight = mMaxHeight; + PxU32 hiRow = PxMin(PxU32(PxMax(0, startRow + PxI32(desc.nbRows))), nbRows); + PxU32 hiCol = PxMin(PxU32(PxMax(0, startCol + PxI32(desc.nbColumns))), nbCols); + for (PxU32 row = PxU32(PxMax(startRow, 0)); row < hiRow; row++) + { + for (PxU32 col = PxU32(PxMax(startCol, 0)); col < hiCol; col++) + { + const PxU32 vertexIndex = col + row*nbCols; + PxHeightFieldSample* targetSample = &mData.samples[vertexIndex]; + + // update target sample from source sample + const PxHeightFieldSample& sourceSample = + (reinterpret_cast(desc.samples.data))[col - startCol + (row - startRow) * desc.nbColumns]; + *targetSample = sourceSample; + + if(isCollisionVertexPreca(vertexIndex, row, col, PxHeightFieldMaterial::eHOLE)) + targetSample->materialIndex1.setBit(); + else + targetSample->materialIndex1.clearBit(); + + // grow (but not shrink) the height extents + const PxReal h = getHeight(vertexIndex); + minHeight = physx::intrinsics::selectMin(h, minHeight); + maxHeight = physx::intrinsics::selectMax(h, maxHeight); + } + } + + if (shrinkBounds) + { + // do a full recompute on vertical bounds to allow shrinking + minHeight = PX_MAX_REAL; + maxHeight = -PX_MAX_REAL; + // have to recompute the min&max from scratch... + for (PxU32 vertexIndex = 0; vertexIndex < nbRows * nbCols; vertexIndex ++) + { + // update height extents + const PxReal h = getHeight(vertexIndex); + minHeight = physx::intrinsics::selectMin(h, minHeight); + maxHeight = physx::intrinsics::selectMax(h, maxHeight); + } + } + mMinHeight = minHeight; + mMaxHeight = maxHeight; + + // update local space aabb + CenterExtents& bounds = mData.mAABB; + bounds.mCenter.y = (maxHeight + minHeight)*0.5f; + bounds.mExtents.y = (maxHeight - minHeight)*0.5f; + + mModifyCount++; + + return true; +} + +bool Gu::HeightField::load(PxInputStream& stream) +{ + // release old memory + releaseMemory(); + + // Import header + PxU32 version; + bool endian; + if(!readHeader('H', 'F', 'H', 'F', version, endian, stream)) + return false; + + // load mData + mData.rows = readDword(endian, stream); + mData.columns = readDword(endian, stream); + mData.rowLimit = readFloat(endian, stream); + mData.colLimit = readFloat(endian, stream); + mData.nbColumns = readFloat(endian, stream); + const float thickness = readFloat(endian, stream); + PX_UNUSED(thickness); + mData.convexEdgeThreshold = readFloat(endian, stream); + + PxU16 flags = readWord(endian, stream); + mData.flags = PxHeightFieldFlags(flags); + + PxU32 format = readDword(endian, stream); + mData.format = PxHeightFieldFormat::Enum(format); + + PxBounds3 minMaxBounds; + minMaxBounds.minimum.x = readFloat(endian, stream); + minMaxBounds.minimum.y = readFloat(endian, stream); + minMaxBounds.minimum.z = readFloat(endian, stream); + minMaxBounds.maximum.x = readFloat(endian, stream); + minMaxBounds.maximum.y = readFloat(endian, stream); + minMaxBounds.maximum.z = readFloat(endian, stream); + mData.mAABB = CenterExtents(minMaxBounds); + + mSampleStride = readDword(endian, stream); + mNbSamples = readDword(endian, stream); + mMinHeight = readFloat(endian, stream); + mMaxHeight = readFloat(endian, stream); + + // allocate height samples + mData.samples = NULL; + const PxU32 nbVerts = mData.rows * mData.columns; + if (nbVerts > 0) + { + mData.samples = reinterpret_cast(PX_ALLOC(nbVerts*sizeof(PxHeightFieldSample), "PxHeightFieldSample")); + if (mData.samples == NULL) + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Gu::HeightField::load: PX_ALLOC failed!"); + return false; + } + stream.read(mData.samples, mNbSamples*sizeof(PxHeightFieldSample)); + if (endian) + for(PxU32 i = 0; i < mNbSamples; i++) + { + PxHeightFieldSample& s = mData.samples[i]; + PX_ASSERT(sizeof(PxU16) == sizeof(s.height)); + flip(s.height); + } + } + + return true; +} + +bool Gu::HeightField::loadFromDesc(const PxHeightFieldDesc& desc) +{ + // verify descriptor + PX_CHECK_AND_RETURN_NULL(desc.isValid(), "Gu::HeightField::loadFromDesc: desc.isValid() failed!"); + + // release old memory + releaseMemory(); + + // copy trivial data + mData.format = desc.format; + mData.rows = desc.nbRows; + mData.columns = desc.nbColumns; + mData.convexEdgeThreshold = desc.convexEdgeThreshold; + mData.flags = desc.flags; + mSampleStride = desc.samples.stride; + + // PT: precompute some data - mainly for Xbox + mData.rowLimit = float(mData.rows - 2); + mData.colLimit = float(mData.columns - 2); + mData.nbColumns = float(desc.nbColumns); + + // allocate and copy height samples + // compute extents too + mData.samples = NULL; + const PxU32 nbVerts = desc.nbRows * desc.nbColumns; + mMinHeight = PX_MAX_REAL; + mMaxHeight = -PX_MAX_REAL; + + if(nbVerts > 0) + { + mData.samples = reinterpret_cast(PX_ALLOC(nbVerts*sizeof(PxHeightFieldSample), "PxHeightFieldSample")); + if(!mData.samples) + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Gu::HeightField::load: PX_ALLOC failed!"); + return false; + } + const PxU8* PX_RESTRICT src = reinterpret_cast(desc.samples.data); + PxHeightFieldSample* PX_RESTRICT dst = mData.samples; + PxI16 minHeight = PX_MAX_I16; + PxI16 maxHeight = PX_MIN_I16; + for(PxU32 i=0;i(src); + *dst++ = sample; + const PxI16 height = sample.height; + minHeight = height < minHeight ? height : minHeight; + maxHeight = height > maxHeight ? height : maxHeight; + src += desc.samples.stride; + } + mMinHeight = PxReal(minHeight); + mMaxHeight = PxReal(maxHeight); + } + + PX_ASSERT(mMaxHeight >= mMinHeight); + + parseTrianglesForCollisionVertices(PxHeightFieldMaterial::eHOLE); + +// PT: "mNbSamples" only used by binary converter + mNbSamples = mData.rows * mData.columns; + + //Compute local space aabb. + PxBounds3 bounds; + bounds.minimum.y = getMinHeight(); + bounds.maximum.y = getMaxHeight(); + + bounds.minimum.x = 0; + bounds.maximum.x = PxReal(getNbRowsFast() - 1); + bounds.minimum.z = 0; + bounds.maximum.z = PxReal(getNbColumnsFast() - 1); + mData.mAABB=bounds; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxU32 Gu::HeightField::saveCells(void* destBuffer, PxU32 destBufferSize) const +{ + PxU32 n = mData.columns * mData.rows * sizeof(PxHeightFieldSample); + if (n > destBufferSize) n = destBufferSize; + PxMemCopy(destBuffer, mData.samples, n); + + return n; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PX_PHYSX_COMMON_API void Gu::HeightField::releaseMemory() +{ +// PX_SERIALIZATION + if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) +//~PX_SERIALIZATION + { + PX_FREE(mData.samples); + mData.samples = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: TODO: use those faster functions everywhere +namespace physx +{ + +PX_PHYSX_COMMON_API PxU32 getVertexEdgeIndices(const Gu::HeightField& heightfield, PxU32 vertexIndex, PxU32 row, PxU32 column, EdgeData edgeIndices[8]) +{ + const PxU32 nbColumns = heightfield.getData().columns; + const PxU32 nbRows = heightfield.getData().rows; + PX_ASSERT((vertexIndex / nbColumns)==row); + PX_ASSERT((vertexIndex % nbColumns)==column); + + PxU32 count = 0; + + if (row > 0) + { +// edgeIndices[count++] = 3 * (vertexIndex - nbColumns) + 2; + const PxU32 cell = vertexIndex - nbColumns; + edgeIndices[count].edgeIndex = 3 * cell + 2; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row-1; + edgeIndices[count].column = column; + count++; + } + + if (column < nbColumns-1) + { + if (row > 0) + { + if (!heightfield.isZerothVertexShared(vertexIndex - nbColumns)) + { +// edgeIndices[count++] = 3 * (vertexIndex - nbColumns) + 1; + const PxU32 cell = vertexIndex - nbColumns; + edgeIndices[count].edgeIndex = 3 * cell + 1; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row-1; + edgeIndices[count].column = column; + count++; + } + } +// edgeIndices[count++] = 3 * vertexIndex; + edgeIndices[count].edgeIndex = 3 * vertexIndex; + edgeIndices[count].cell = vertexIndex; + edgeIndices[count].row = row; + edgeIndices[count].column = column; + count++; + + if (row < nbRows - 1) + { + if (heightfield.isZerothVertexShared(vertexIndex)) + { +// edgeIndices[count++] = 3 * vertexIndex + 1; + edgeIndices[count].edgeIndex = 3 * vertexIndex + 1; + edgeIndices[count].cell = vertexIndex; + edgeIndices[count].row = row; + edgeIndices[count].column = column; + count++; + } + } + } + + if (row < nbRows - 1) + { +// edgeIndices[count++] = 3 * vertexIndex + 2; + edgeIndices[count].edgeIndex = 3 * vertexIndex + 2; + edgeIndices[count].cell = vertexIndex; + edgeIndices[count].row = row; + edgeIndices[count].column = column; + count++; + } + + if (column > 0) + { + if (row < nbRows - 1) + { + if (!heightfield.isZerothVertexShared(vertexIndex - 1)) + { +// edgeIndices[count++] = 3 * (vertexIndex - 1) + 1; + const PxU32 cell = vertexIndex - 1; + edgeIndices[count].edgeIndex = 3 * cell + 1; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row; + edgeIndices[count].column = column-1; + count++; + } + } +// edgeIndices[count++] = 3 * (vertexIndex - 1); + const PxU32 cell = vertexIndex - 1; + edgeIndices[count].edgeIndex = 3 * cell; + edgeIndices[count].cell = cell; + edgeIndices[count].row = row; + edgeIndices[count].column = column-1; + count++; + if (row > 0) + { + if (heightfield.isZerothVertexShared(vertexIndex - nbColumns - 1)) + { +// edgeIndices[count++] = 3 * (vertexIndex - nbColumns - 1) + 1; + const PxU32 cell1 = vertexIndex - nbColumns - 1; + edgeIndices[count].edgeIndex = 3 * cell1 + 1; + edgeIndices[count].cell = cell1; + edgeIndices[count].row = row-1; + edgeIndices[count].column = column-1; + count++; + } + } + } + return count; +} + +PX_PHYSX_COMMON_API PxU32 getEdgeTriangleIndices(const Gu::HeightField& heightfield, const EdgeData& edgeData, PxU32* PX_RESTRICT triangleIndices) +{ + const PxU32 nbColumns = heightfield.getData().columns; + const PxU32 nbRows = heightfield.getData().rows; + + const PxU32 edgeIndex = edgeData.edgeIndex; + const PxU32 cell = edgeData.cell; + const PxU32 row = edgeData.row; + const PxU32 column = edgeData.column; + PX_ASSERT(cell==edgeIndex / 3); + PX_ASSERT(row==cell / nbColumns); + PX_ASSERT(column==cell % nbColumns); + PxU32 count = 0; + switch (edgeIndex - cell*3) + { + case 0: + if (column < nbColumns - 1) + { + if (row > 0) + { + if (heightfield.isZerothVertexShared(cell - nbColumns)) + triangleIndices[count++] = ((cell - nbColumns) << 1); + else + triangleIndices[count++] = ((cell - nbColumns) << 1) + 1; + } + if (row < nbRows - 1) + { + if (heightfield.isZerothVertexShared(cell)) + triangleIndices[count++] = (cell << 1) + 1; + else + triangleIndices[count++] = cell << 1; + } + } + break; + case 1: + if ((row < nbRows - 1) && (column < nbColumns - 1)) + { + triangleIndices[count++] = cell << 1; + triangleIndices[count++] = (cell << 1) + 1; + } + break; + case 2: + if (row < nbRows - 1) + { + if (column > 0) + { + triangleIndices[count++] = ((cell - 1) << 1) + 1; + } + if (column < nbColumns - 1) + { + triangleIndices[count++] = cell << 1; + } + } + break; + } + + return count; +} + +} + +PX_FORCE_INLINE PxU32 anyHole(PxU32 doubleMatIndex, PxU16 holeMaterialIndex) +{ + return PxU32((doubleMatIndex & 0xFFFF) == holeMaterialIndex) | (PxU32(doubleMatIndex >> 16) == holeMaterialIndex); +} + +void Gu::HeightField::parseTrianglesForCollisionVertices(PxU16 holeMaterialIndex) +{ + const PxU32 nbColumns = getNbColumnsFast(); + const PxU32 nbRows = getNbRowsFast(); + + Cm::BitMap rowHoles[2]; + rowHoles[0].resizeAndClear(nbColumns + 1); + rowHoles[1].resizeAndClear(nbColumns + 1); + + for (PxU32 iCol = 0; iCol < nbColumns; iCol++) + { + if (anyHole(getMaterialIndex01(iCol), holeMaterialIndex)) + { + rowHoles[0].set(iCol); + rowHoles[0].set(iCol + 1); + } + PxU32 vertIndex = iCol; + if(isCollisionVertexPreca(vertIndex, 0, iCol, holeMaterialIndex)) + mData.samples[vertIndex].materialIndex1.setBit(); + else + mData.samples[vertIndex].materialIndex1.clearBit(); + } + + PxU32 nextRow = 1, currentRow = 0; + for (PxU32 iRow = 1; iRow < nbRows; iRow++) + { + PxU32 rowOffset = iRow*nbColumns; + for (PxU32 iCol = 0; iCol < nbColumns; iCol++) + { + const PxU32 vertIndex = rowOffset + iCol; // column index plus current row offset (vertex/cell index) + if(anyHole(getMaterialIndex01(vertIndex), holeMaterialIndex)) + { + rowHoles[currentRow].set(iCol); + rowHoles[currentRow].set(iCol + 1); + rowHoles[nextRow].set(iCol); + rowHoles[nextRow].set(iCol + 1); + } + + if ((iCol == 0) || (iCol == nbColumns - 1) || (iRow == nbRows - 1) || rowHoles[currentRow].test(iCol)) + { + if(isCollisionVertexPreca(vertIndex, iRow, iCol, holeMaterialIndex)) + mData.samples[vertIndex].materialIndex1.setBit(); + else + mData.samples[vertIndex].materialIndex1.clearBit(); + } else + { + if (isConvexVertex(vertIndex, iRow, iCol)) + mData.samples[vertIndex].materialIndex1.setBit(); + } + } + + rowHoles[currentRow].clear(); + + // swap prevRow and prevPrevRow + nextRow ^= 1; currentRow ^= 1; + } +} + +bool Gu::HeightField::isSolidVertex(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex, bool& nbSolid) const +{ + // check if solid and boundary + // retrieve edge indices for current vertexIndex + EdgeData edgeIndices[8]; + const PxU32 edgeCount = ::getVertexEdgeIndices(*this, vertexIndex, row, column, edgeIndices); + + PxU32 faceCounts[8]; + PxU32 faceIndices[2 * 8]; + PxU32* dst = faceIndices; + for (PxU32 i = 0; i < edgeCount; i++) + { + faceCounts[i] = ::getEdgeTriangleIndices(*this, edgeIndices[i], dst); + dst += 2; + } + + nbSolid = false; + const PxU32* currentfaceIndices = faceIndices; // parallel array of pairs of face indices per edge index + for (PxU32 i = 0; i < edgeCount; i++) + { + if (faceCounts[i] > 1) + { + const PxU16& material0 = getTriangleMaterial(currentfaceIndices[0]); + const PxU16& material1 = getTriangleMaterial(currentfaceIndices[1]); + // ptchernev TODO: this is a bit arbitrary + if (material0 != holeMaterialIndex) + { + nbSolid = true; + if (material1 == holeMaterialIndex) + return true; // edge between solid and hole => return true + } + if (material1 != holeMaterialIndex) + { + nbSolid = true; + if (material0 == holeMaterialIndex) + return true; // edge between hole and solid => return true + } + } + else + { + if (getTriangleMaterial(currentfaceIndices[0]) != holeMaterialIndex) + return true; + } + currentfaceIndices += 2; // 2 face indices per edge + } + return false; +} + +bool Gu::HeightField::isCollisionVertexPreca(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(isValidVertex(vertexIndex)); +#endif + PX_ASSERT((vertexIndex / getNbColumnsFast()) == row); + PX_ASSERT((vertexIndex % getNbColumnsFast()) == column); + + // check boundary conditions - boundary edges shouldn't produce collision with eNO_BOUNDARY_EDGES flag + if(mData.flags & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) + if ((row == 0) || (column == 0) || (row >= mData.rows-1) || (column >= mData.columns-1)) + return false; + + bool nbSolid; + if(isSolidVertex(vertexIndex, row, column, holeMaterialIndex, nbSolid)) + return true; + + // return true if it is boundary or solid and convex + return (nbSolid && isConvexVertex(vertexIndex, row, column)); +} + +// AP: this naming is confusing and inconsistent with return value. the function appears to compute vertex coord rather than cell coords +// it would most likely be better to stay in cell coords instead, since fractional vertex coords just do not make any sense +PxU32 Gu::HeightField::computeCellCoordinates(PxReal x, PxReal z, PxReal& fracX, PxReal& fracZ) const +{ + namespace i = physx::intrinsics; + + x = i::selectMax(x, 0.0f); + z = i::selectMax(z, 0.0f); +#if 0 // validation code for scaled clamping epsilon computation + for (PxReal ii = 1.0f; ii < 100000.0f; ii+=1.0f) + { + PX_UNUSED(ii); + PX_ASSERT(PxFloor(ii+(1-1e-7f*ii)) == ii); + } +#endif + PxF32 epsx = 1.0f - PxAbs(x+1.0f) * 1e-6f; // epsilon needs to scale with values of x,z... + PxF32 epsz = 1.0f - PxAbs(z+1.0f) * 1e-6f; + PxF32 x1 = i::selectMin(x, mData.rowLimit+epsx); + PxF32 z1 = i::selectMin(z, mData.colLimit+epsz); + x = PxFloor(x1); + fracX = x1 - x; + z = PxFloor(z1); + fracZ = z1 - z; + PX_ASSERT(x >= 0.0f && x < PxF32(mData.rows)); + PX_ASSERT(z >= 0.0f && z < PxF32(mData.columns)); + + const PxU32 vertexIndex = PxU32(x * (mData.nbColumns) + z); + PX_ASSERT(vertexIndex < (mData.rows)*(mData.columns)); + + return vertexIndex; +} + diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h new file mode 100644 index 000000000..7a1d36330 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h @@ -0,0 +1,1266 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HEIGHTFIELD_H +#define GU_HEIGHTFIELD_H + +#include "PsUserAllocated.h" +#include "CmRefCountable.h" +#include "PsMathUtils.h" +#include "GuSphere.h" +#include "PxHeightFieldSample.h" +#include "PxHeightFieldDesc.h" +#include "GuHeightFieldData.h" +#include "PxHeightField.h" + +//#define PX_HEIGHTFIELD_VERSION 0 +#define PX_HEIGHTFIELD_VERSION 1 // tiled version that was needed for PS3 only has been removed + +namespace physx +{ +class GuMeshFactory; +class PxHeightFieldDesc; +} + +namespace physx +{ +namespace Gu +{ +class HeightField : public PxHeightField, public Ps::UserAllocated, public Cm::RefCountable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + HeightField(PxBaseFlags baseFlags) : PxHeightField(baseFlags), Cm::RefCountable(PxEmpty), mData(PxEmpty), mModifyCount(0) {} + + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext&); + PX_PHYSX_COMMON_API void importExtraData(PxDeserializationContext& context); + PX_FORCE_INLINE void setMeshFactory(GuMeshFactory* f) { mMeshFactory = f; } + PX_PHYSX_COMMON_API static HeightField* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); + void resolveReferences(PxDeserializationContext&) {} + + virtual void requiresObjects(PxProcessPxBaseCallback&){} +//~PX_SERIALIZATION + + PX_PHYSX_COMMON_API HeightField(GuMeshFactory* meshFactory); + PX_PHYSX_COMMON_API HeightField(GuMeshFactory& factory, Gu::HeightFieldData& data); + + // PxHeightField + PX_PHYSX_COMMON_API virtual void release(); + PX_PHYSX_COMMON_API virtual PxU32 saveCells(void* destBuffer, PxU32 destBufferSize) const; + PX_PHYSX_COMMON_API virtual bool modifySamples(PxI32 startCol, PxI32 startRow, const PxHeightFieldDesc& subfieldDesc, bool shrinkBounds); + PX_PHYSX_COMMON_API virtual PxU32 getNbRows() const { return mData.rows; } + PX_PHYSX_COMMON_API virtual PxU32 getNbColumns() const { return mData.columns; } + PX_PHYSX_COMMON_API virtual PxHeightFieldFormat::Enum getFormat() const { return mData.format; } + PX_PHYSX_COMMON_API virtual PxU32 getSampleStride() const { return sizeof(PxHeightFieldSample); } + PX_PHYSX_COMMON_API virtual PxReal getConvexEdgeThreshold() const { return mData.convexEdgeThreshold; } + PX_PHYSX_COMMON_API virtual PxHeightFieldFlags getFlags() const { return mData.flags; } + PX_PHYSX_COMMON_API virtual PxReal getHeight(PxReal x, PxReal z) const { return getHeightInternal(x, z); } + + PX_PHYSX_COMMON_API virtual void acquireReference(); + PX_PHYSX_COMMON_API virtual PxU32 getReferenceCount() const; + //~PxHeightField + + // RefCountable + PX_PHYSX_COMMON_API virtual void onRefCountZero(); + //~RefCountable + PX_PHYSX_COMMON_API virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const + { + return getTriangleMaterial(triangleIndex); + } + + PX_PHYSX_COMMON_API virtual PxVec3 getTriangleNormal(PxTriangleID triangleIndex) const + { + return getTriangleNormalInternal(triangleIndex); + } + + PX_PHYSX_COMMON_API virtual const PxHeightFieldSample& getSample(PxU32 row, PxU32 column) const + { + const PxU32 cell = row * getNbColumnsFast() + column; + return getSample(cell); + } + /** + \brief Returns the number of times the heightfield data has been modified + + Each time the heightfield is changed via 'modifySamples' this increments a counter. This method will return + the number of times the heightfield has been modified so that rendering code can know whether or not it needs to + rebuild the graphics representation of the mesh. + + \return the number of times the heightfield sample data has been modified. + */ + PX_PHYSX_COMMON_API virtual PxU32 getTimestamp() const { return mModifyCount; } + + PX_PHYSX_COMMON_API bool loadFromDesc(const PxHeightFieldDesc&); + PX_PHYSX_COMMON_API bool load(PxInputStream&); + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbRowsFast() const { return mData.rows; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbColumnsFast() const { return mData.columns; } + PX_FORCE_INLINE PxHeightFieldFormat::Enum getFormatFast() const { return mData.format; } + PX_FORCE_INLINE PxU32 getFlagsFast() const { return mData.flags; } + + PX_FORCE_INLINE bool isDeltaHeightInsideExtent(PxReal dy, PxReal eps = 0.0f) const + { + const float thickness = 0.0f; + return (thickness <= 0.0f && dy <= eps && dy >= thickness) || + (thickness > 0.0f && dy > -eps && dy < thickness); + } + + PX_FORCE_INLINE bool isDeltaHeightOppositeExtent(PxReal dy) const + { + const float thickness = 0.0f; + return (thickness <= 0.0f && dy > 0.0f) || (thickness > 0.0f && dy < 0.0f); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isZerothVertexShared(PxU32 vertexIndex) const + { +// return (getSample(vertexIndex).tessFlag & PxHeightFieldTessFlag::e0TH_VERTEX_SHARED); + return getSample(vertexIndex).tessFlag() != 0; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex0(PxU32 vertexIndex) const { return getSample(vertexIndex).materialIndex0; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getMaterialIndex1(PxU32 vertexIndex) const { return getSample(vertexIndex).materialIndex1; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaterialIndex01(PxU32 vertexIndex) const + { + const PxHeightFieldSample& sample = getSample(vertexIndex); + return PxU32(sample.materialIndex0 | (sample.materialIndex1 << 16)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxReal getHeight(PxU32 vertexIndex) const + { + return PxReal(getSample(vertexIndex).height); + } + + PX_INLINE PxReal getHeightInternal2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const; + PX_FORCE_INLINE PxReal getHeightInternal(PxReal x, PxReal z) const + { + PxReal fracX, fracZ; + const PxU32 vertexIndex = computeCellCoordinates(x, z, fracX, fracZ); + + return getHeightInternal2(vertexIndex, fracX, fracZ); + } + + PX_FORCE_INLINE bool isValidVertex(PxU32 vertexIndex) const { return vertexIndex < mData.rows*mData.columns; } + + PX_INLINE PxVec3 getVertex(PxU32 vertexIndex) const; + PX_INLINE bool isConvexVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const; + + PX_INLINE bool isValidEdge(PxU32 edgeIndex) const; + PX_INLINE PxU32 getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2]) const; + PX_INLINE PxU32 getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2], PxU32 cell, PxU32 row, PxU32 column) const; + PX_INLINE void getEdgeVertexIndices(PxU32 edgeIndex, PxU32& vertexIndex0, PxU32& vertexIndex1) const; +// PX_INLINE bool isConvexEdge(PxU32 edgeIndex) const; + PX_INLINE bool isConvexEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const; + PX_FORCE_INLINE bool isConvexEdge(PxU32 edgeIndex) const + { + const PxU32 cell = edgeIndex / 3; + const PxU32 row = cell / mData.columns; + const PxU32 column = cell % mData.columns; + return isConvexEdge(edgeIndex, cell, row, column); + } + + PX_PHYSX_COMMON_API PxU32 computeCellCoordinates(PxReal x, PxReal z, PxReal& fracX, PxReal& fracZ) const; + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMin(PxReal x, PxU32 nb) const + { + if(x<0.0f) + return 0; + if(x>PxReal(nb)) + return nb; + + const PxReal cx = Ps::floor(x); + const PxU32 icx = PxU32(cx); + return icx; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMax(PxReal x, PxU32 nb) const + { + if(x<0.0f) + return 0; + if(x>PxReal(nb)) + return nb; + + const PxReal cx = Ps::ceil(x); + const PxU32 icx = PxU32(cx); + return icx; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMinRow(PxReal x) const { return getMin(x, mData.rows-2); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaxRow(PxReal x) const { return getMax(x, mData.rows-1); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMinColumn(PxReal z) const { return getMin(z, mData.columns-2); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getMaxColumn(PxReal z) const { return getMax(z, mData.columns-1); } + + PX_CUDA_CALLABLE PX_INLINE bool isValidTriangle(PxU32 triangleIndex) const; + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFirstTriangle(PxU32 triangleIndex) const { return ((triangleIndex & 0x1) == 0); } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU16 getTriangleMaterial(PxU32 triangleIndex) const + { + return isFirstTriangle(triangleIndex) ? getMaterialIndex0(triangleIndex >> 1) : getMaterialIndex1(triangleIndex >> 1); + } + + PX_CUDA_CALLABLE PX_INLINE void getTriangleVertexIndices(PxU32 triangleIndex, PxU32& vertexIndex0, PxU32& vertexIndex1, PxU32& vertexIndex2) const; + PX_CUDA_CALLABLE PX_INLINE PxVec3 getTriangleNormalInternal(PxU32 triangleIndex) const; + PX_INLINE void getTriangleAdjacencyIndices(PxU32 triangleIndex,PxU32 vertexIndex0, PxU32 vertexIndex1, PxU32 vertexIndex2, PxU32& adjacencyIndex0, PxU32& adjacencyIndex1, PxU32& adjacencyIndex2) const; + + PX_INLINE PxVec3 getNormal_2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const; + PX_FORCE_INLINE PxVec3 getNormal_(PxReal x, PxReal z, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const + { + PxReal fracX, fracZ; + const PxU32 vertexIndex = computeCellCoordinates(x, z, fracX, fracZ); + + return getNormal_2(vertexIndex, fracX, fracZ, xcoeff, ycoeff, zcoeff); + } + + PX_INLINE PxU32 getTriangleIndex(PxReal x, PxReal z) const; + PX_INLINE PxU32 getTriangleIndex2(PxU32 cell, PxReal fracX, PxReal fracZ) const; + PX_FORCE_INLINE PxU16 getMaterial(PxReal x, PxReal z) const + { + return getTriangleMaterial(getTriangleIndex(x, z)); + } + + PX_FORCE_INLINE PxReal getMinHeight() const { return mMinHeight; } + PX_FORCE_INLINE PxReal getMaxHeight() const { return mMaxHeight; } + + PX_FORCE_INLINE const Gu::HeightFieldData& getData() const { return mData; } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void getTriangleVertices(PxU32 triangleIndex, PxU32 row, PxU32 column, PxVec3& v0, PxVec3& v1, PxVec3& v2) const; + + // checks if current vertex is solid or not + bool isSolidVertex(PxU32 vertexIndex, PxU32 row, PxU32 coloumn, PxU16 holeMaterialIndex, bool& nbSolid) const; + + // if precomputed bitmap define is used, the collision vertex information + // is precomputed during create height field and stored as a bit in materialIndex1 + PX_PHYSX_COMMON_API bool isCollisionVertexPreca(PxU32 vertexIndex, PxU32 row, PxU32 column, PxU16 holeMaterialIndex) const; + PX_FORCE_INLINE bool isCollisionVertex(PxU32 vertexIndex, PxU32, PxU32, PxU16) const + { + return getSample(vertexIndex).materialIndex1.isBitSet()!=0; + } + void parseTrianglesForCollisionVertices(PxU16 holeMaterialIndex); + + PX_FORCE_INLINE + PX_CUDA_CALLABLE const PxHeightFieldSample& getSample(PxU32 vertexIndex) const + { + PX_ASSERT(isValidVertex(vertexIndex)); + return mData.samples[vertexIndex]; + } + +#ifdef __CUDACC__ + PX_CUDA_CALLABLE void setSamplePtr(PxHeightFieldSample* s) { mData.samples = s; } +#endif + + Gu::HeightFieldData mData; + PxU32 mSampleStride; + PxU32 mNbSamples; // PT: added for platform conversion. Try to remove later. + PxReal mMinHeight; + PxReal mMaxHeight; + PxU32 mModifyCount; + // methods + PX_PHYSX_COMMON_API void releaseMemory(); + + PX_PHYSX_COMMON_API virtual ~HeightField(); + +private: + GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization +}; + +} // namespace Gu + +PX_INLINE PxVec3 Gu::HeightField::getVertex(PxU32 vertexIndex) const +{ + const PxU32 row = vertexIndex / mData.columns; + const PxU32 column = vertexIndex % mData.columns; +// return PxVec3(PxReal(row), getHeight(row * mData.columns + column), PxReal(column)); + return PxVec3(PxReal(row), getHeight(vertexIndex), PxReal(column)); +} + +// PT: only called from "isCollisionVertex", should move +PX_INLINE bool Gu::HeightField::isConvexVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(isValidVertex(vertexIndex)); +#endif + PX_ASSERT((vertexIndex / mData.columns)==row); + PX_ASSERT((vertexIndex % mData.columns)==column); + +// PxReal h0 = PxReal(2) * getHeight(vertexIndex); + PxI32 h0 = getSample(vertexIndex).height; + h0 += h0; + + bool definedInX, definedInZ; + PxI32 convexityX, convexityZ; + + if ((row > 0) && (row < mData.rows - 1)) + { +// convexityX = h0 - getHeight(vertexIndex + mData.columns) - getHeight(vertexIndex - mData.columns); + convexityX = h0 - getSample(vertexIndex + mData.columns).height - getSample(vertexIndex - mData.columns).height; + definedInX = true; + } + else + { + convexityX = 0; + definedInX = false; + } + + if ((column > 0) && (column < mData.columns - 1)) + { +// convexityZ = h0 - getHeight(vertexIndex + 1) - getHeight(vertexIndex - 1); + convexityZ = h0 - getSample(vertexIndex + 1).height - getSample(vertexIndex - 1).height; + definedInZ = true; + } + else + { + convexityZ = 0; + definedInZ = false; + } + + if(definedInX || definedInZ) + { + // PT: use XOR here + // saddle points +/* if ((convexityX > 0) && (convexityZ < 0)) + return false; + if ((convexityX < 0) && (convexityZ > 0)) + return false;*/ + if(((convexityX ^ convexityZ) & 0x80000000)==0) + return false; + + const PxReal value = PxReal(convexityX + convexityZ); + return value > mData.convexEdgeThreshold; + } + + // this has to be one of the two corner vertices + return true; +} + +PX_INLINE bool Gu::HeightField::isValidEdge(PxU32 edgeIndex) const +{ + const PxU32 cell = (edgeIndex / 3); + const PxU32 row = cell / mData.columns; + const PxU32 column = cell % mData.columns; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (row > mData.rows - 1) return false; + if (column >= mData.columns - 1) return false; + break; + case 1: + if (row >= mData.rows - 1) return false; + if (column >= mData.columns - 1) return false; + break; + case 2: + if (row >= mData.rows - 1) return false; + if (column > mData.columns - 1) return false; + break; + } + return true; +} + +PX_INLINE PxU32 Gu::HeightField::getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2]) const +{ + const PxU32 cell = edgeIndex / 3; + const PxU32 row = cell / mData.columns; + const PxU32 column = cell % mData.columns; + PxU32 count = 0; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (column < mData.columns - 1) + { + if (row > 0) + { +/* if (isZerothVertexShared(cell - mData.columns)) + triangleIndices[count++] = ((cell - mData.columns) << 1); + else + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1;*/ + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1 - isZerothVertexShared(cell - mData.columns); + } + if (row < mData.rows - 1) + { +/* if (isZerothVertexShared(cell)) + triangleIndices[count++] = (cell << 1) + 1; + else + triangleIndices[count++] = cell << 1;*/ + triangleIndices[count++] = (cell << 1) + isZerothVertexShared(cell); + } + } + break; + case 1: + if ((row < mData.rows - 1) && (column < mData.columns - 1)) + { + triangleIndices[count++] = cell << 1; + triangleIndices[count++] = (cell << 1) + 1; + } + break; + case 2: + if (row < mData.rows - 1) + { + if (column > 0) + triangleIndices[count++] = ((cell - 1) << 1) + 1; + if (column < mData.columns - 1) + triangleIndices[count++] = cell << 1; + } + break; + } + return count; +} + +PX_INLINE PxU32 Gu::HeightField::getEdgeTriangleIndices(PxU32 edgeIndex, PxU32 triangleIndices[2], PxU32 cell, PxU32 row, PxU32 column) const +{ +// const PxU32 cell = edgeIndex / 3; +// const PxU32 row = cell / mData.columns; +// const PxU32 column = cell % mData.columns; + PxU32 count = 0; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + if (column < mData.columns - 1) + { + if (row > 0) + { +/* if (isZerothVertexShared(cell - mData.columns)) + triangleIndices[count++] = ((cell - mData.columns) << 1); + else + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1;*/ + triangleIndices[count++] = ((cell - mData.columns) << 1) + 1 - isZerothVertexShared(cell - mData.columns); + } + if (row < mData.rows - 1) + { +/* if (isZerothVertexShared(cell)) + triangleIndices[count++] = (cell << 1) + 1; + else + triangleIndices[count++] = cell << 1;*/ + triangleIndices[count++] = (cell << 1) + isZerothVertexShared(cell); + } + } + break; + case 1: + if ((row < mData.rows - 1) && (column < mData.columns - 1)) + { + triangleIndices[count++] = cell << 1; + triangleIndices[count++] = (cell << 1) + 1; + } + break; + case 2: + if (row < mData.rows - 1) + { + if (column > 0) + triangleIndices[count++] = ((cell - 1) << 1) + 1; + if (column < mData.columns - 1) + triangleIndices[count++] = cell << 1; + } + break; + } + return count; +} + +PX_INLINE void Gu::HeightField::getEdgeVertexIndices(PxU32 edgeIndex, PxU32& vertexIndex0, PxU32& vertexIndex1) const +{ + const PxU32 cell = edgeIndex / 3; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + vertexIndex0 = cell; + vertexIndex1 = cell + 1; + break; + case 1: + { +/* if (isZerothVertexShared(cell)) + { + vertexIndex0 = cell; + vertexIndex1 = cell + mData.columns + 1; + } + else + { + vertexIndex0 = cell + 1; + vertexIndex1 = cell + mData.columns; + }*/ + const bool b = isZerothVertexShared(cell); + vertexIndex0 = cell + 1 - b; + vertexIndex1 = cell + mData.columns + b; + } + break; + case 2: + vertexIndex0 = cell; + vertexIndex1 = cell + mData.columns; + break; + } +} + +PX_INLINE bool Gu::HeightField::isConvexEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const +{ +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); + +// const PxU32 row = cell / mData.columns; + PX_ASSERT(row == cell / mData.columns); + if (row > mData.rows-2) return false; + +// const PxU32 column = cell % mData.columns; + PX_ASSERT(column == cell % mData.columns); + if (column > mData.columns-2) return false; + +// PxReal h0 = 0, h1 = 0, h2 = 0, h3 = 0; +// PxReal convexity = 0; + PxI32 h0 = 0, h1 = 0, h2 = 0, h3 = 0; + PxI32 convexity = 0; + +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + { + if (row < 1) return false; +/* if(isZerothVertexShared(cell - mData.columns)) + { + // <------ COL + // +----+ 0 R + // | / /# O + // | / / # W + // | / / # | + // |/ / # | + // + +====1 | + // | + // | + // | + // | + // | + // | + // V + // +// h0 = getHeight(cell - mData.columns); +// h1 = getHeight(cell); + h0 = getSample(cell - mData.columns).height; + h1 = getSample(cell).height; + } + else + { + // <------ COL + // 0 +----+ R + // #\ \ | O + // # \ \ | W + // # \ \ | | + // # \ \| | + // 1====+ + | + // | + // | + // | + // | + // | + // | + // V + // +// h0 = getHeight(cell - mData.columns + 1); +// h1 = getHeight(cell + 1); + h0 = getSample(cell - mData.columns + 1).height; + h1 = getSample(cell + 1).height; + }*/ + const bool b0 = !isZerothVertexShared(cell - mData.columns); + h0 = getSample(cell - mData.columns + b0).height; + h1 = getSample(cell + b0).height; + +/* if(isZerothVertexShared(cell)) + { + // <------ COL + // R + // O + // W + // | + // | + // | + // 2====+ 0 | + // # / /| | + // # / / | | + // # / / | | + // #/ / | | + // 3 +----+ | + // V + // +// h2 = getHeight(cell + 1); +// h3 = getHeight(cell + mData.columns + 1); + h2 = getSample(cell + 1).height; + h3 = getSample(cell + mData.columns + 1).height; + } + else + { + // <------ COL + // R + // O + // W + // | + // | + // | + // + +====2 | + // |\ \ # | + // | \ \ # | + // | \ \ # | + // | \ \# | + // +----+ 3 | + // V + // +// h2 = getHeight(cell); +// h3 = getHeight(cell + mData.columns); + h2 = getSample(cell).height; + h3 = getSample(cell + mData.columns).height; + }*/ + const bool b1 = isZerothVertexShared(cell); + h2 = getSample(cell + b1).height; + h3 = getSample(cell + mData.columns + b1).height; + + //convex = (h3-h2) < (h1-h0); + convexity = (h1-h0) - (h3-h2); + } + break; + case 1: +// h0 = getHeight(cell); +// h1 = getHeight(cell + 1); +// h2 = getHeight(cell + mData.columns); +// h3 = getHeight(cell + mData.columns + 1); + h0 = getSample(cell).height; + h1 = getSample(cell + 1).height; + h2 = getSample(cell + mData.columns).height; + h3 = getSample(cell + mData.columns + 1).height; + if (isZerothVertexShared(cell)) + //convex = (h0 + h3) > (h1 + h2); + convexity = (h0 + h3) - (h1 + h2); + else + //convex = (h2 + h1) > (h0 + h3); + convexity = (h2 + h1) - (h0 + h3); + break; + case 2: + { + if (column < 1) return false; +/* if(isZerothVertexShared(cell-1)) + { + // <-------------- COL + // 1====0 + R + // + / /| O + // + / / | W + // + / / | | + // +/ / | | + // + +----+ V + // +// h0 = getHeight(cell - 1); +// h1 = getHeight(cell); + h0 = getSample(cell - 1).height; + h1 = getSample(cell).height; + } + else + { + // <-------------- COL + // + +----+ R + // +\ \ | O + // + \ \ | W + // + \ \ | | + // + \ \| | + // 1====0 + V + // +// h0 = getHeight(cell - 1 + mData.columns); +// h1 = getHeight(cell + mData.columns); + h0 = getSample(cell - 1 + mData.columns).height; + h1 = getSample(cell + mData.columns).height; + }*/ + const PxU32 offset0 = isZerothVertexShared(cell-1) ? 0 : mData.columns; + h0 = getSample(cell - 1 + offset0).height; + h1 = getSample(cell + offset0).height; + +/* if(isZerothVertexShared(cell)) + { + // <-------------- COL + // +----+ + R + // | / /+ O + // | / / + W + // | / / + | + // |/ / + | + // + 3====2 V + // +// h2 = getHeight(cell + mData.columns); +// h3 = getHeight(cell + mData.columns + 1); + h2 = getSample(cell + mData.columns).height; + h3 = getSample(cell + mData.columns + 1).height; + } + else + { + // <-------------- COL + // + 3====2 R + // |\ \ + O + // | \ \ + W + // | \ \ + | + // | \ \+ | + // +----+ + V + // +// h2 = getHeight(cell); +// h3 = getHeight(cell + 1); + h2 = getSample(cell).height; + h3 = getSample(cell + 1).height; + }*/ + const PxU32 offset1 = isZerothVertexShared(cell) ? mData.columns : 0; + h2 = getSample(cell + offset1).height; + h3 = getSample(cell + offset1 + 1).height; + + //convex = (h3-h2) < (h1-h0); + convexity = (h1-h0) - (h3-h2); + } + break; + } + + const PxI32 threshold = PxI32(mData.convexEdgeThreshold); + return convexity > threshold; +} + +PX_INLINE bool Gu::HeightField::isValidTriangle(PxU32 triangleIndex) const +{ + const PxU32 cell = triangleIndex >> 1; + const PxU32 row = cell / mData.columns; + if (row >= (mData.rows - 1)) return false; + const PxU32 column = cell % mData.columns; + if (column >= (mData.columns - 1)) return false; + return true; +} + +PX_INLINE void Gu::HeightField::getTriangleVertexIndices(PxU32 triangleIndex, PxU32& vertexIndex0, PxU32& vertexIndex1, PxU32& vertexIndex2) const +{ + const PxU32 cell = triangleIndex >> 1; + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 1 R + // | 1 / /| O + // | / / | W + // | / / | | + // |/ / 0 | | + // 1 2----0 V + // + if (isFirstTriangle(triangleIndex)) + { + vertexIndex0 = cell + mData.columns; + vertexIndex1 = cell; + vertexIndex2 = cell + mData.columns + 1; + } + else + { + vertexIndex0 = cell + 1; + vertexIndex1 = cell + mData.columns + 1; + vertexIndex2 = cell; + } + } + else + { + // <---- COL + // 2 1----0 R + // |\ \ 0 | O + // | \ \ | W + // | \ \ | | + // | 1 \ \| | + // 0----1 2 V + // + if (isFirstTriangle(triangleIndex)) + { + vertexIndex0 = cell; + vertexIndex1 = cell + 1; + vertexIndex2 = cell + mData.columns; + } + else + { + vertexIndex0 = cell + mData.columns + 1; + vertexIndex1 = cell + mData.columns; + vertexIndex2 = cell + 1; + } + } +} + +PX_INLINE void Gu::HeightField::getTriangleAdjacencyIndices(PxU32 triangleIndex, PxU32 vertexIndex0, PxU32 vertexIndex1, PxU32 vertexIndex2, PxU32& adjacencyIndex0, PxU32& adjacencyIndex1, PxU32& adjacencyIndex2) const +{ + PX_UNUSED(vertexIndex0); + PX_UNUSED(vertexIndex1); + PX_UNUSED(vertexIndex2); + + const PxU32 cell = triangleIndex >> 1; + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 1 R + // | 1 / /| O + // | / / | W + // | / / | | + // |/ / 0 | | + // 1 2----0 V + // + if (isFirstTriangle(triangleIndex)) + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex + 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if((cell % (mData.columns) != 0)) + { + adjacencyIndex0 = triangleIndex - 1; + } + + if((cell / mData.columns != mData.rows - 2)) + { + const PxU32 tMod = isZerothVertexShared(cell + mData.columns) ? 1u : 0u; + adjacencyIndex2 = ((cell + mData.columns) * 2) + tMod; + } + } + else + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex - 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if(cell % (mData.columns) < (mData.columns - 2)) + { + adjacencyIndex0 = triangleIndex + 1; + } + + if(cell >= mData.columns - 1) + { + const PxU32 tMod = isZerothVertexShared(cell - mData.columns) ? 0u : 1u; + adjacencyIndex2 = ((cell - mData.columns) * 2) + tMod; + } + } + } + else + { + // <---- COL + // 2 1----0 R + // |\ \ 0 | O + // | \ \ | W + // | \ \ | | + // | 1 \ \| | + // 0----1 2 V + // + if (isFirstTriangle(triangleIndex)) + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex + 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if(cell >= mData.columns - 1) + { + const PxU32 tMod = isZerothVertexShared(cell - mData.columns) ? 0u : 1u; + adjacencyIndex0 = ((cell - (mData.columns)) * 2) + tMod; + } + + if((cell % (mData.columns) != 0)) + { + adjacencyIndex2 = triangleIndex - 1; + } + } + else + { + adjacencyIndex0 = 0xFFFFFFFF; + adjacencyIndex1 = triangleIndex - 1; + adjacencyIndex2 = 0xFFFFFFFF; + + if((cell / mData.columns != mData.rows - 2)) + { + const PxU32 tMod = isZerothVertexShared(cell + mData.columns) ? 1u : 0u; + adjacencyIndex0 = (cell + (mData.columns)) * 2 + tMod; + } + + if(cell % (mData.columns) < (mData.columns - 2)) + { + adjacencyIndex2 = triangleIndex + 1; + } + } + } +} + +PX_INLINE PxVec3 Gu::HeightField::getTriangleNormalInternal(PxU32 triangleIndex) const +{ + PxU32 v0, v1, v2; + getTriangleVertexIndices(triangleIndex, v0, v1, v2); + +// const PxReal h0 = getHeight(v0); +// const PxReal h1 = getHeight(v1); +// const PxReal h2 = getHeight(v2); + const PxI32 h0 = getSample(v0).height; + const PxI32 h1 = getSample(v1).height; + const PxI32 h2 = getSample(v2).height; + + const float thickness = 0.0f; + const PxReal coeff = physx::intrinsics::fsel(thickness, -1.0f, 1.0f); + +// PxVec3 n(0,1,0); + const PxU32 cell = triangleIndex >> 1; + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 1 R + // | 1 / /| O + // | / / | W + // | / / | | + // |/ / 0 | | + // 1 2----0 V + // + if (isFirstTriangle(triangleIndex)) + { +// n.x = -(h0-h1); +// n.z = -(h2-h0); + return PxVec3(coeff*PxReal(h1-h0), coeff, coeff*PxReal(h0-h2)); + } + else + { +// n.x = -(h1-h0); +// n.z = -(h0-h2); + return PxVec3(coeff*PxReal(h0-h1), coeff, coeff*PxReal(h2-h0)); + } + } + else + { + // <---- COL + // 2 1----0 R + // |\ \ 0 | O + // | \ \ | W + // | \ \ | | + // | 1 \ \| | + // 0----1 2 V + // + if (isFirstTriangle(triangleIndex)) + { +// n.x = -(h2-h0); +// n.z = -(h1-h0); + return PxVec3(coeff*PxReal(h0-h2), coeff, coeff*PxReal(h0-h1)); + } + else + { +// n.x = -(h0-h2); +// n.z = -(h0-h1); + return PxVec3(coeff*PxReal(h2-h0), coeff, coeff*PxReal(h1-h0)); + } + } +// return n; +} + +PX_INLINE PxReal Gu::HeightField::getHeightInternal2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ) const +{ + if (isZerothVertexShared(vertexIndex)) + { + // <----Z---+ + // +----+ | + // | /| | + // | / | X + // | / | | + // |/ | | + // +----+ | + // V + const PxReal h0 = getHeight(vertexIndex); + const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); + if (fracZ > fracX) + { + // <----Z---+ + // 1----0 | + // | / | + // | / X + // | / | + // |/ | + // 2 | + // V + const PxReal h1 = getHeight(vertexIndex + 1); + return h0 + fracZ*(h1-h0) + fracX*(h2-h1); + } + else + { + // <----Z---+ + // 0 | + // /| | + // / | X + // / | | + // / | | + // 2----1 | + // V + const PxReal h1 = getHeight(vertexIndex + mData.columns); + return h0 + fracX*(h1-h0) + fracZ*(h2-h1); + } + } + else + { + // <----Z---+ + // +----+ | + // |\ | | + // | \ | X + // | \ | | + // | \| | + // +----+ | + // V + const PxReal h2 = getHeight(vertexIndex + mData.columns); + const PxReal h1 = getHeight(vertexIndex + 1); + if (fracX + fracZ < 1.0f) + { + // <----Z---+ + // 1----0 | + // \ | | + // \ | X + // \ | | + // \| | + // 2 | + // V + const PxReal h0 = getHeight(vertexIndex); + return h0 + fracZ*(h1-h0) + fracX*(h2-h0); + } + else + { + // <----Z---+ + // 1 | + // |\ | + // | \ X + // | \ | + // | \ | + // 0----2 | + // V + // + // Note that we need to flip fracX and fracZ since we are moving the origin + const PxReal h0 = getHeight(vertexIndex + mData.columns + 1); + return h0 + (1.0f - fracZ)*(h2-h0) + (1.0f - fracX)*(h1-h0); + } + } +} + +PX_INLINE PxVec3 Gu::HeightField::getNormal_2(PxU32 vertexIndex, PxReal fracX, PxReal fracZ, PxReal xcoeff, PxReal ycoeff, PxReal zcoeff) const +{ + PxVec3 normal; + if (isZerothVertexShared(vertexIndex)) + { + // <----Z---+ + // +----+ | + // | /| | + // | / | X + // | / | | + // |/ | | + // +----+ | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); + const PxI32 ih0 = getSample(vertexIndex).height; + const PxI32 ih2 = getSample(vertexIndex + mData.columns + 1).height; + if (fracZ >= fracX) + { + // <----Z---+ + // 1----0 | + // | / | + // | / X + // | / | + // |/ | + // 2 | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h1 = getHeight(vertexIndex + 1); +// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); +// normal.set(-(h2-h1), 1.0f, -(h1-h0)); + const PxI32 ih1 = getSample(vertexIndex + 1).height; + normal = PxVec3(PxReal(ih1 - ih2)*xcoeff, ycoeff, PxReal(ih0 - ih1)*zcoeff); + } + else + { + // <----Z---+ + // 0 | + // /| | + // / | X + // / | | + // / | | + // 2----1 | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h1 = getHeight(vertexIndex + mData.columns); +// const PxReal h2 = getHeight(vertexIndex + mData.columns + 1); +// normal.set(-(h1-h0), 1.0f, -(h2-h1)); + const PxI32 ih1 = getSample(vertexIndex + mData.columns).height; + normal = PxVec3(PxReal(ih0 - ih1)*xcoeff, ycoeff, PxReal(ih1 - ih2)*zcoeff); + } + } + else + { + // <----Z---+ + // +----+ | + // |\ | | + // | \ | X + // | \ | | + // | \| | + // +----+ | + // V + const PxI32 ih1 = getSample(vertexIndex + 1).height; + const PxI32 ih2 = getSample(vertexIndex + mData.columns).height; + if (fracX + fracZ <= PxReal(1)) + { + // <----Z---+ + // 1----0 | + // \ | | + // \ | X + // \ | | + // \| | + // 2 | + // V +// const PxReal h0 = getHeight(vertexIndex); +// const PxReal h1 = getHeight(vertexIndex + 1); +// const PxReal h2 = getHeight(vertexIndex + mData.columns); +// normal.set(-(h2-h0), 1.0f, -(h1-h0)); + const PxI32 ih0 = getSample(vertexIndex).height; +// const PxI32 ih1 = getSample(vertexIndex + 1).height; +// const PxI32 ih2 = getSample(vertexIndex + mData.columns).height; + normal = PxVec3(PxReal(ih0 - ih2)*xcoeff, ycoeff, PxReal(ih0 - ih1)*zcoeff); + } + else + { + // <----Z---+ + // 2 | + // |\ | + // | \ X + // | \ | + // | \ | + // 0----1 | + // V + // + // Note that we need to flip fracX and fracZ since we are moving the origin +// const PxReal h2 = getHeight(vertexIndex + 1); +// const PxReal h1 = getHeight(vertexIndex + mData.columns); +// const PxReal h0 = getHeight(vertexIndex + mData.columns + 1); +// normal.set(-(h0-h2), 1.0f, -(h0-h1)); +// const PxI32 ih2 = getSample(vertexIndex + 1).height; +// const PxI32 ih1 = getSample(vertexIndex + mData.columns).height; + const PxI32 ih0 = getSample(vertexIndex + mData.columns + 1).height; +// normal.set(PxReal(ih2 - ih0), 1.0f, PxReal(ih1b - ih0)); + normal = PxVec3(PxReal(ih1 - ih0)*xcoeff, ycoeff, PxReal(ih2 - ih0)*zcoeff); + } + } + return normal; +} + +PX_INLINE PxU32 Gu::HeightField::getTriangleIndex2(PxU32 cell, PxReal fracX, PxReal fracZ) const +{ + if (isZerothVertexShared(cell)) + return (fracZ > fracX) ? (cell << 1) + 1 : (cell << 1); + else + return (fracX + fracZ > 1) ? (cell << 1) + 1 : (cell << 1); +} + +PX_INLINE PxU32 Gu::HeightField::getTriangleIndex(PxReal x, PxReal z) const +{ + PxReal fracX, fracZ; + const PxU32 cell = computeCellCoordinates(x, z, fracX, fracZ); + + return getTriangleIndex2(cell, fracX, fracZ); +} + +PX_FORCE_INLINE void Gu::HeightField::getTriangleVertices(PxU32 triangleIndex, PxU32 row, PxU32 column, PxVec3& v0, PxVec3& v1, PxVec3& v2) const +{ + PxU32 cell = triangleIndex >> 1; + PX_ASSERT(row * getNbColumnsFast() + column == cell); + + PxReal h0 = getHeight(cell); + PxReal h1 = getHeight(cell + 1); + PxReal h2 = getHeight(cell + getNbColumnsFast()); + PxReal h3 = getHeight(cell + getNbColumnsFast() + 1); + + if (isFirstTriangle(triangleIndex)) + { + if (isZerothVertexShared(cell)) + { + // <---- COL + // 1 R + // /| O + // / | W + // / | | + // / 0 | | + // 2----0 V + // + v0 = PxVec3(PxReal(row + 1), h2, PxReal(column )); + v1 = PxVec3(PxReal(row ), h0, PxReal(column )); + v2 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1)); + } + else + { + // <---- COL + // 1----0 R + // \ 0 | O + // \ | W + // \ | | + // \| | + // 2 V + // + v0 = PxVec3(PxReal(row ), h0, PxReal(column )); + v1 = PxVec3(PxReal(row ), h1, PxReal(column + 1)); + v2 = PxVec3(PxReal(row + 1), h2, PxReal(column )); + } + } + else + { + if (isZerothVertexShared(cell)) + { + // <---- COL + // 0----2 R + // | 1 / O + // | / W + // | / | + // |/ | + // 1 V + // + v0 = PxVec3(PxReal(row ), h1, PxReal(column + 1)); + v1 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1)); + v2 = PxVec3(PxReal(row ), h0, PxReal(column )); + } + else + { + // <---- COL + // 2 R + // |\ O + // | \ W + // | \ | + // | 1 \ | + // 0----1 V + // + v0 = PxVec3(PxReal(row + 1), h3, PxReal(column + 1)); + v1 = PxVec3(PxReal(row + 1), h2, PxReal(column )); + v2 = PxVec3(PxReal(row ), h1, PxReal(column + 1)); + } + } +} + +struct EdgeData +{ + PxU32 edgeIndex; + PxU32 cell; + PxU32 row; + PxU32 column; +}; +PX_PHYSX_COMMON_API PxU32 getVertexEdgeIndices(const Gu::HeightField& heightfield, PxU32 vertexIndex, PxU32 row, PxU32 column, EdgeData edgeIndices[8]); +PX_PHYSX_COMMON_API PxU32 getEdgeTriangleIndices(const Gu::HeightField& heightfield, const EdgeData& edgeData, PxU32* PX_RESTRICT triangleIndices); + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h new file mode 100644 index 000000000..2e165936d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HEIGHTFIELD_DATA_H +#define GU_HEIGHTFIELD_DATA_H + +#include "foundation/PxSimpleTypes.h" +#include "PxHeightFieldFlag.h" +#include "PxHeightFieldSample.h" +#include "GuCenterExtents.h" + +namespace physx +{ + +namespace Gu +{ + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif +struct PX_PHYSX_COMMON_API HeightFieldData +{ +// PX_SERIALIZATION + PX_FORCE_INLINE HeightFieldData() {} + PX_FORCE_INLINE HeightFieldData(const PxEMPTY) : flags(PxEmpty) {} +//~PX_SERIALIZATION + + //properties + // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading + CenterExtents mAABB; + PxU32 rows; // PT: WARNING: don't change this member's name (used in ConvX) + PxU32 columns; // PT: WARNING: don't change this member's name (used in ConvX) + PxReal rowLimit; // PT: to avoid runtime int-to-float conversions on Xbox + PxReal colLimit; // PT: to avoid runtime int-to-float conversions on Xbox + PxReal nbColumns; // PT: to avoid runtime int-to-float conversions on Xbox + PxHeightFieldSample* samples; // PT: WARNING: don't change this member's name (used in ConvX) + PxReal convexEdgeThreshold; + + PxHeightFieldFlags flags; + + PxHeightFieldFormat::Enum format; + + PX_FORCE_INLINE const CenterExtentsPadded& getPaddedBounds() const + { + // PT: see compile-time assert below + return static_cast(mAABB); + } +}; +#if PX_VC + #pragma warning(pop) +#endif + + // PT: 'getPaddedBounds()' is only safe if we make sure the bounds member is followed by at least 32bits of data + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(Gu::HeightFieldData, rows)>=PX_OFFSET_OF(Gu::HeightFieldData, mAABB)+4); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp new file mode 100644 index 000000000..d4e9b9bb8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp @@ -0,0 +1,832 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "GuHeightFieldUtil.h" +#include "GuSweepSharedTests.h" + +#include "PsFoundation.h" +#include "GuHeightField.h" +#include "GuEntityReport.h" +#include "PxMeshScale.h" + +using namespace physx; + +void Gu::HeightFieldUtil::computeLocalBounds(PxBounds3& bounds) const +{ + const PxMeshScale scale(PxVec3(mHfGeom->rowScale, mHfGeom->heightScale, mHfGeom->columnScale), PxQuat(PxIdentity)); + const PxMat33 mat33 = scale.toMat33(); + + bounds.minimum = mat33.transform(mHeightField->getData().mAABB.getMin()); + bounds.maximum = mat33.transform(mHeightField->getData().mAABB.getMax()); + + // PT: HFs will assert in Gu::intersectRayAABB2() if we don't deal with that + const float deltaY = GU_MIN_AABB_EXTENT*0.5f - (bounds.maximum.y - bounds.minimum.y); + if(deltaY>0.0f) + { + bounds.maximum.y += deltaY*0.6f; + bounds.minimum.y -= deltaY*0.6f; + } +} + +PxU32 Gu::HeightFieldUtil::getFaceIndexAtShapePoint(PxReal x, PxReal z) const +{ + if (isShapePointOnHeightField(x, z)) + { + const PxU32 triangleIndex = mHeightField->getTriangleIndex(x * mOneOverRowScale, z * mOneOverColumnScale); + return (mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE) ? triangleIndex : 0xffffffff; + } + return 0xffffffff; +} + +PxVec3 Gu::HeightFieldUtil::getVertexNormal(PxU32 vertexIndex, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidVertex(vertexIndex)); +#endif + // PxU32 edges[8]; + // const PxU32 edgeCount = mHeightField.getVertexEdgeIndices(vertexIndex, edges); + + //const PxU32 nbColumns = mHeightField.getData().columns; + //const PxU32 row = vertexIndex / nbColumns; + //const PxU32 column = vertexIndex % nbColumns; + PX_ASSERT(row == vertexIndex / mHeightField->getData().columns); + PX_ASSERT(column == vertexIndex % mHeightField->getData().columns); + EdgeData edgeIndices[8]; + const PxU32 edgeCount = ::getVertexEdgeIndices(*mHeightField, vertexIndex, row, column, edgeIndices); + + PxVec3 n(0.0f); + PxVec3 tn; + for (PxU32 i=0; igetTriangleMaterial(faces[j]) != PxHeightFieldMaterial::eHOLE) + { + tn = hf2shapen(mHeightField->getTriangleNormalInternal(faces[j])).getNormalized(); + n+=tn; + } + } + } + + return n.getNormalized(); +} + +PxU32 Gu::HeightFieldUtil::findClosestPointsOnCell( + PxU32 row, PxU32 column, PxVec3 point, + PxVec3* PX_RESTRICT closestPoints, PxU32* PX_RESTRICT featureCodes, + bool testFaces, bool testEdges, bool skipEdgesIfFaceHits) const +{ + PxU32 count = 0; + + const PxU32 offset = row * mHeightField->getNbColumnsFast() + column; + const PxU32 firstEdgeIndex = 3 * offset; + + // ptchernev TODO: + // move the material assignments to an else in the ifs on triangle material + // instead of doing it all the time + + PX_ASSERT(row < (mHeightField->getNbRowsFast() - 1)); + PX_ASSERT(column < (mHeightField->getNbColumnsFast() - 1)); + const bool lastRow = (row == (mHeightField->getNbRowsFast() - 2)); + const bool lastColumn = (column == (mHeightField->getNbColumnsFast() - 2)); + + bool testVertex0 = testEdges; + bool testColumnEdge0 = testEdges; + bool testRowEdge0 = testEdges; + bool testDiagonal = testEdges; + bool testVertex1 = lastColumn && testEdges; + bool testVertex2 = lastRow && testEdges; + + bool testRowEdge1 = lastColumn && testEdges; + bool testColumnEdge1 = lastRow && testEdges; + bool testVertex3 = lastRow && lastColumn && testEdges; + + const PxU32 triangleIndex0 = offset << 1; + const PxMaterialTableIndex materialIndex0 = mHeightField->getTriangleMaterial(triangleIndex0); + const PxU32 triangleIndex1 = triangleIndex0 + 1; + const PxMaterialTableIndex materialIndex1 = mHeightField->getTriangleMaterial(triangleIndex1); + + if (testFaces) + { + if (materialIndex0 != PxHeightFieldMaterial::eHOLE) + { + // face 0 + PxVec3 closestPoint; + if (findProjectionOnTriangle(triangleIndex0, row, column, point, closestPoint)) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(triangleIndex0, eFACE); + count++; + testRowEdge0 = false; + testVertex0 = false; + testVertex2 = false; + testDiagonal = false; + } + } + + if (materialIndex1 != PxHeightFieldMaterial::eHOLE) + { + // face 1 + PxVec3 closestPoint; + if (findProjectionOnTriangle(triangleIndex1, row, column, point, closestPoint)) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(triangleIndex1, eFACE); + count++; + testRowEdge1 = false; + testVertex1 = false; + testVertex3 = false; + testDiagonal = false; + } + } + if (!testEdges) + return count; + } + + // if there were any face contacts and we asked to skip edges if face contacts, return current count here + if (count && skipEdgesIfFaceHits) + return count; + + const PxU32 nbColumns = mHeightField->getNbColumnsFast(); + if (testVertex0 || testColumnEdge0 || testVertex1) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex, offset, row, column, point, closestPoint); + if (t <= 0) + { + if (testVertex0 && 0xffffffff != (getVertexFaceIndex(offset, row, column))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column, eVERTEX); + count++; + } + testVertex0 = false; + } + else if (t < 1) + { + if (testColumnEdge0 && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex, eEDGE); + count++; + } + } + else + { + if (testVertex1 && 0xffffffff != (getVertexFaceIndex(offset + 1, row, column + 1))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column+1, eVERTEX); + count++; + } + } + } + + if (testVertex0 || testRowEdge0 || testVertex2) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 2, offset, row, column, point, closestPoint); + if (t <= 0) + { + if (testVertex0 && 0xffffffff != (getVertexFaceIndex(offset, row, column))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(row*nbColumns+column, eVERTEX); + count++; + } + } + else if(t < 1) + { + if (testRowEdge0 && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 2))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+2, eEDGE); + count++; + } + } + else + { + if (testVertex2 && 0xffffffff != (getVertexFaceIndex(offset + nbColumns, row + 1, column))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode((row+1)*nbColumns+column, eVERTEX); + count++; + } + } + } + + if (testColumnEdge1) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 3 * nbColumns, offset + nbColumns, row + 1, column, point, closestPoint); + if (t <= 0) + ; // do nothing + else if (t < 1) + { + const PxU32 edgeIndex3 = firstEdgeIndex + 3 * nbColumns; + if (0xffffffff != (getEdgeFaceIndex(edgeIndex3))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(edgeIndex3, eEDGE); + count++; + } + } + } + + if (testRowEdge1) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 5, offset + 1, row, column + 1, point, closestPoint); + if (t <= 0) + ; // do nothing + else if (t < 1) + { + if (0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 5))) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+5, eEDGE); + count++; + } + } + } + + if (testVertex3 && 0xffffffff != (getVertexFaceIndex(offset + nbColumns + 1, row + 1, column + 1))) + { + closestPoints[count] = PxVec3((row + 1) * mHfGeom->rowScale, mHfGeom->heightScale * mHeightField->getHeight(offset + mHeightField->getNbColumnsFast() + 1), (column + 1) * mHfGeom->columnScale); + if (featureCodes) featureCodes[count] = makeFeatureCode((row+1)*nbColumns+column+1, eVERTEX); + count++; + } + + if (testDiagonal && 0xffffffff != (getEdgeFaceIndex(firstEdgeIndex + 1))) + { + PxVec3 closestPoint; + PxReal t = findClosestPointOnEdge(firstEdgeIndex + 1, offset, row, column, point, closestPoint); + if (t <= 0) + ; // do nothing + else if (t < 1) + { + closestPoints[count] = closestPoint; + if (featureCodes) featureCodes[count] = makeFeatureCode(firstEdgeIndex+1, eEDGE); + count++; + } + } + + return count; +} + +//PxReal Gu::HeightFieldUtil::findClosestPointOnEdge(PxU32 edgeIndex, const PxVec3& point, PxVec3& closestPoint) const +PxReal Gu::HeightFieldUtil::findClosestPointOnEdge( + PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& closestPoint) const +{ +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); +// const PxU32 row = cell / mHeightField->getNbColumnsFast(); + PX_ASSERT(row == cell / mHeightField->getNbColumnsFast()); +// const PxU32 column = cell % mHeightField->getNbColumnsFast(); + PX_ASSERT(column == cell % mHeightField->getNbColumnsFast()); + + PxVec3 origin, direction; + PxReal lengthSquared; +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + const PxReal dy = y1 - y0; + direction = PxVec3(0, dy, mHfGeom->columnScale); + lengthSquared = mHfGeom->columnScale * mHfGeom->columnScale + dy * dy; + } + break; + case 1: + if (mHeightField->isZerothVertexShared(cell)) + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + const PxReal dy = y3 - y0; + direction = PxVec3(mHfGeom->rowScale, dy, mHfGeom->columnScale); + lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + mHfGeom->columnScale * mHfGeom->columnScale + dy * dy; + } + else + { + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale); + const PxReal dy = y2 - y1; + direction = PxVec3(mHfGeom->rowScale, dy, -mHfGeom->columnScale); + lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + mHfGeom->columnScale * mHfGeom->columnScale + dy * dy; + } + break; + case 2: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + const PxReal dy = y2 - y0; + direction = PxVec3(mHfGeom->rowScale, dy, 0); + lengthSquared = mHfGeom->rowScale * mHfGeom->rowScale + dy * dy; + } + break; + default: + origin = direction = PxVec3(PxReal(0)); + lengthSquared = 0.0f; + PX_ALWAYS_ASSERT_MESSAGE("Invalid edge index in findClosestPointOnEdge"); + } // switch (edgeIndex % 3) + + const PxVec3 relative = point - origin; + const PxReal t = relative.dot(direction) / lengthSquared; + if (t < 0) + closestPoint = origin; + else if (t > 1) + closestPoint = origin + direction; + else + closestPoint = origin + direction * t; + + return t; +} + +PxU32 Gu::HeightFieldUtil::getVertexFaceIndex(PxU32 vertexIndex, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidVertex(vertexIndex)); +#endif + +// PxU32 edgeIndices[8]; +// const PxU32 count = mHeightField->getVertexEdgeIndices(vertexIndex, edgeIndices); + +//const PxU32 nbColumns = mHeightField->getData().columns; +//const PxU32 row = vertexIndex / nbColumns; +//const PxU32 column = vertexIndex % nbColumns; +PX_ASSERT(row == vertexIndex / mHeightField->getData().columns); +PX_ASSERT(column == vertexIndex % mHeightField->getData().columns); +EdgeData edgeIndices[8]; +const PxU32 count = ::getVertexEdgeIndices(*mHeightField, vertexIndex, row, column, edgeIndices); + + for (PxU32 i = 0; iisValidEdge(edgeIndex)); +#endif + PxU32 faceIndices[2]; + const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices); + if (count > 1) + { + // ptchernev TODO: this is a bit arbitrary + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1]; + } + else + { + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + } + return 0xffffffff; +} + +PxU32 Gu::HeightFieldUtil::getEdgeFaceIndex(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif + PxU32 faceIndices[2]; + const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices, cell, row, column); + if (count > 1) + { + // ptchernev TODO: this is a bit arbitrary + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1]; + } + else + { + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + } + return 0xffffffff; +} + +PxU32 Gu::HeightFieldUtil::getEdgeFaceIndex(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices) const +{ + PX_UNUSED(edgeIndex); + +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif +// PxU32 faceIndices[2]; +// const PxU32 count = mHeightField->getEdgeTriangleIndices(edgeIndex, faceIndices); + if (count > 1) + { + // ptchernev TODO: this is a bit arbitrary + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + if (mHeightField->getTriangleMaterial(faceIndices[1]) != PxHeightFieldMaterial::eHOLE) return faceIndices[1]; + } + else + { + if (mHeightField->getTriangleMaterial(faceIndices[0]) != PxHeightFieldMaterial::eHOLE) return faceIndices[0]; + } + return 0xffffffff; +} + +bool Gu::HeightFieldUtil::findProjectionOnTriangle(PxU32 triangleIndex, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& projection) const +{ + const PxU32 cell = (triangleIndex >> 1); + PX_ASSERT(row == cell / mHeightField->getNbColumnsFast()); + PX_ASSERT(column == cell % mHeightField->getNbColumnsFast()); + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1); + PxVec3 origin; + PxReal h0, h1, h2, uInvScale, vInvScale; + + // specify a triangle according to current triangle index as origin, 3 heights and (uInvScale, vInvScale) vector for (z,x) + // set uInvScale in h1-h0; vScale in h2-h0 direction + if (mHeightField->isZerothVertexShared(cell)) + { + // COLUMN --> + // + // R 0---1 + // O |\ 1| + // W | \ | + // | |0 \| + // | 2---3 + // V + if ((triangleIndex & 1) == 0) + { + // case 0 + // face 0 + origin = PxVec3((row + 1) * mHfGeom->rowScale, y2, column * mHfGeom->columnScale); + // origin -> 2 + // verts -> 2,3,0 + h0 = y2; + h1 = y3; + h2 = y0; + uInvScale = mOneOverColumnScale; + vInvScale = -mOneOverRowScale; + } + else // if (testFace1) + { + // case 1 + // face 1 + origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale); + // origin -> 1 + // verts -> 1,0,3 + h0 = y1; + h1 = y0; + h2 = y3; + uInvScale = -mOneOverColumnScale; + vInvScale = mOneOverRowScale; + } + } + else + { + // COLUMN --> + // + // R 0---1 + // O |0 /| + // W | / | + // | |/ 1| + // | 2---3 + // V + if ((triangleIndex & 1) == 0) + { + // case 2 + // face 0 + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + // origin -> 0 + // verts -> 0,1,2 + h0 = y0; + h1 = y1; + h2 = y2; + uInvScale = mOneOverColumnScale; + vInvScale = mOneOverRowScale; + } + else + { + // case 3 + // face 1 + origin = PxVec3((row + 1) * mHfGeom->rowScale, y3, (column + 1) * mHfGeom->columnScale); + // origin -> 3 + // verts -> 3,2,1 + h0 = y3; + h1 = y2; + h2 = y1; + uInvScale = -mOneOverColumnScale; + vInvScale = -mOneOverRowScale; + } + } + + // vector from triangle origin to point we want to project + const PxVec3 relative = point - origin; + + // Looking at the triangle diagram for case 2 + // The normal computation should be + // n = (p1-p0) x (p2-p0) + // For a right handed cross product that's pointing into the screen (negative h), so -n is in the direction of increasing h + // cs = column scale, rs = row scale, h10 = h1-h0; u=column, v=row + // (i j k); + // p1-p0 = (cs, h10, 0); this is column, u and z + // p2-p0 = (0, h20, rs); this is row, v and x + // n = (h10*rs, -cs*rs, +cs*h20) + // n/(cs*rs) = (h10/cs, -1, h20/rs) + // -n = (-h10/cs, 1, -h20/rs) + PxReal h10 = h1-h0, h20 = h2-h0; + PxReal nu = -h10 * uInvScale; + PxReal nv = -h20 * vInvScale; + + PxVec3 n(nv, 1.0f, nu); // for whatever reason.. x is v, z is u. + //n *= 1.0f / PxSqrt(nu*nu + nv*nv + 1.0f); // technically we need to do this but since later + + // project relative onto the n plane, it gives us unclipped projection onto the triangle + // the computation without sqrt shortcut is relPrj = relative - n.dot(relative)*n, but because we divide by sqrt^2 we skip sqrt + PxVec3 relPrj = relative - n.dot(relative)* (1.0f/(nu*nu+nv*nv+1.0f)) * n; + + // project relPrj onto 2d UV plane with h = 0 oriented according to vInvScale, uInvScale (for x and z) + // to convert to HF cell coords we'd multiply by inv scale, after that the coords should be >0 and the sum within 1 to be + // inside of a 2d triangle + PxReal scaledX = relPrj.x * vInvScale, scaledZ = relPrj.z * uInvScale; + //PxVec3 testProjection = relPrj + origin; + //PxVec3 testN = (point - testProjection).getNormalized(); + if (scaledX > 0.0f && scaledZ > 0.0f && scaledX + scaledZ < 1.0f) + { + projection = relPrj + origin; + return true; + } + + return false; +} + +void Gu::HeightFieldUtil::getEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, PxVec3& origin, PxVec3& extent) const +{ +#ifdef PX_HEIGHTFIELD_DEBUG + PX_ASSERT(mHeightField->isValidEdge(edgeIndex)); +#endif +// const PxU32 cell = edgeIndex / 3; + PX_ASSERT(cell == edgeIndex / 3); +// const PxU32 row = cell / mHeightField->getNbColumnsFast(); + PX_ASSERT(row == cell / mHeightField->getNbColumnsFast()); +// const PxU32 column = cell % mHeightField->getNbColumnsFast(); + PX_ASSERT(column == cell % mHeightField->getNbColumnsFast()); + +// switch (edgeIndex % 3) + switch (edgeIndex - cell*3) + { + case 0: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + extent = PxVec3(0, y1 - y0, mHfGeom->columnScale); + } + break; + case 1: + if (mHeightField->isZerothVertexShared(cell)) + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y3 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast() + 1); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + extent = PxVec3(mHfGeom->rowScale, y3 - y0, mHfGeom->columnScale); + } + else + { + const PxReal y1 = mHfGeom->heightScale * mHeightField->getHeight(cell + 1); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y1, (column + 1) * mHfGeom->columnScale); + extent = PxVec3(mHfGeom->rowScale, y2 - y1, -mHfGeom->columnScale); + } + break; + case 2: + { + const PxReal y0 = mHfGeom->heightScale * mHeightField->getHeight(cell); + const PxReal y2 = mHfGeom->heightScale * mHeightField->getHeight(cell + mHeightField->getNbColumnsFast()); + origin = PxVec3(row * mHfGeom->rowScale, y0, column * mHfGeom->columnScale); + extent = PxVec3(mHfGeom->rowScale, y2 - y0, 0); + } + break; + } +} + +bool Gu::HeightFieldUtil::overlapAABBTriangles(const PxTransform& pose, const PxBounds3& bounds, PxU32 flags, EntityReport* callback) const +{ + PX_ASSERT(!bounds.isEmpty()); + + PxBounds3 localBounds = (flags & GuHfQueryFlags::eWORLD_SPACE) ? PxBounds3::transformFast(pose.getInverse(), bounds) : bounds; + + localBounds.minimum.x *= mOneOverRowScale; + localBounds.minimum.y *= mOneOverHeightScale; + localBounds.minimum.z *= mOneOverColumnScale; + + localBounds.maximum.x *= mOneOverRowScale; + localBounds.maximum.y *= mOneOverHeightScale; + localBounds.maximum.z *= mOneOverColumnScale; + + if(mHfGeom->rowScale < 0.0f) + Ps::swap(localBounds.minimum.x, localBounds.maximum.x); + + if(mHfGeom->columnScale < 0.0f) + Ps::swap(localBounds.minimum.z, localBounds.maximum.z); + + // early exit for aabb does not overlap in XZ plane + // DO NOT MOVE: since rowScale / columnScale may be negative this has to be done after scaling localBounds + const PxU32 nbRows = mHeightField->getNbRowsFast(); + const PxU32 nbColumns = mHeightField->getNbColumnsFast(); + if(localBounds.minimum.x > float(nbRows - 1)) + return false; + if(localBounds.minimum.z > float(nbColumns - 1)) + return false; + if(localBounds.maximum.x < 0.0f) + return false; + if(localBounds.maximum.z < 0.0f) + return false; + + const PxU32 minRow = mHeightField->getMinRow(localBounds.minimum.x); + const PxU32 maxRow = mHeightField->getMaxRow(localBounds.maximum.x); + const PxU32 minColumn = mHeightField->getMinColumn(localBounds.minimum.z); + const PxU32 maxColumn = mHeightField->getMaxColumn(localBounds.maximum.z); + + PxU32 maxNbTriangles = 2 * (maxColumn - minColumn) * (maxRow - minRow); + + if(!maxNbTriangles) + return false; + + if(flags & GuHfQueryFlags::eFIRST_CONTACT) + maxNbTriangles = 1; + + const PxU32 bufferSize = HF_SWEEP_REPORT_BUFFER_SIZE; + PxU32 indexBuffer[bufferSize]; + PxU32 indexBufferUsed = 0; + PxU32 nb = 0; + + PxU32 offset = minRow * mHeightField->getNbColumnsFast() + minColumn; + + const PxReal miny = localBounds.minimum.y; + const PxReal maxy = localBounds.maximum.y; + + for(PxU32 row=minRow; rowgetHeight(offset); + const PxReal h1 = mHeightField->getHeight(offset + 1); + const PxReal h2 = mHeightField->getHeight(offset + mHeightField->getNbColumnsFast()); + const PxReal h3 = mHeightField->getHeight(offset + mHeightField->getNbColumnsFast() + 1); + if(!((maxy < h0 && maxy < h1 && maxy < h2 && maxy < h3) || (miny > h0 && miny > h1 && miny > h2 && miny > h3))) + { + const PxU32 material0 = mHeightField->getMaterialIndex0(offset); + if(material0 != PxHeightFieldMaterial::eHOLE) + { + if(indexBufferUsed >= bufferSize) + { + callback->onEvent(indexBufferUsed, indexBuffer); + indexBufferUsed = 0; + } + + indexBuffer[indexBufferUsed++] = offset << 1; + nb++; + + if(flags & GuHfQueryFlags::eFIRST_CONTACT) + goto search_done; + } + + const PxU32 material1 = mHeightField->getMaterialIndex1(offset); + if(material1 != PxHeightFieldMaterial::eHOLE) + { + if(indexBufferUsed >= bufferSize) + { + callback->onEvent(indexBufferUsed, indexBuffer); + indexBufferUsed = 0; + } + + indexBuffer[indexBufferUsed++] = (offset << 1) + 1; + nb++; + + if(flags & GuHfQueryFlags::eFIRST_CONTACT) + goto search_done; + } + } + offset++; + } + offset += (mHeightField->getNbColumnsFast() - (maxColumn - minColumn)); + } + +search_done: + + if(indexBufferUsed > 0) + callback->onEvent(indexBufferUsed, indexBuffer); + + return nb > 0; +} + +PxU32 Gu::HeightFieldUtil::getTriangle(const PxTransform& pose, PxTriangle& worldTri, + PxU32* _vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation, bool worldSpaceRotation) const +{ +#if PX_CHECKED + if (!mHeightField->isValidTriangle(triangleIndex)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "HeightFieldShape::getTriangle: Invalid triangle index!"); + return 0; + } +#endif + + PxVec3 handedness(1.0f); // Vector to invert normal coordinates according to the heightfield scales + bool wrongHanded = false; + if (mHfGeom->columnScale < 0) + { + wrongHanded = !wrongHanded; + handedness.z = -1.0f; + } + if (mHfGeom->rowScale < 0) + { + wrongHanded = !wrongHanded; + handedness.x = -1.0f; + } + +/* if (0) // ptchernev: Iterating over triangles becomes a pain. + { + if (mHeightField.getTriangleMaterial(triangleIndex) == mHfGeom.holeMaterialIndex) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "HeightFieldShape::getTriangle: Non-existing triangle (triangle has hole material)!"); + return 0; + } + }*/ + + PxU32 vertexIndices[3]; + mHeightField->getTriangleVertexIndices(triangleIndex, vertexIndices[0], vertexIndices[1+wrongHanded], vertexIndices[2-wrongHanded]); + + if(adjacencyIndices) + { + mHeightField->getTriangleAdjacencyIndices( triangleIndex, vertexIndices[0], vertexIndices[1+wrongHanded], vertexIndices[2-wrongHanded], + adjacencyIndices[wrongHanded ? 2 : 0], adjacencyIndices[1], adjacencyIndices[wrongHanded ? 0 : 2]); + } + + if(_vertexIndices) + { + _vertexIndices[0] = vertexIndices[0]; + _vertexIndices[1] = vertexIndices[1]; + _vertexIndices[2] = vertexIndices[2]; + } + + if (worldSpaceRotation) + { + if (worldSpaceTranslation) + { + for (PxU32 vi = 0; vi < 3; vi++) + worldTri.verts[vi] = hf2worldp(pose, mHeightField->getVertex(vertexIndices[vi])); + } + else + { + for (PxU32 vi = 0; vi < 3; vi++) + { + // TTP 2390 + // local space here is rotated (but not translated) world space + worldTri.verts[vi] = pose.q.rotate(hf2shapep(mHeightField->getVertex(vertexIndices[vi]))); + } + } + } + else + { + const PxVec3 offset = worldSpaceTranslation ? pose.p : PxVec3(0.0f); + for (PxU32 vi = 0; vi < 3; vi++) + worldTri.verts[vi] = hf2shapep(mHeightField->getVertex(vertexIndices[vi])) + offset; + } + return PxU32(mHeightField->getTriangleMaterial(triangleIndex) != PxHeightFieldMaterial::eHOLE); +} diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h new file mode 100644 index 000000000..18e52420a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h @@ -0,0 +1,904 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_HEIGHTFIELD_UTIL_H +#define GU_HEIGHTFIELD_UTIL_H + +#include "PxHeightFieldGeometry.h" +#include "GuHeightField.h" +#include "PxTriangle.h" +#include "../intersection/GuIntersectionRayTriangle.h" +#include "../intersection/GuIntersectionRayBox.h" +#include "PsBasicTemplates.h" + +namespace physx +{ +#define HF_SWEEP_REPORT_BUFFER_SIZE 64 + +/** +\brief Used to control contact queries. +*/ +struct GuHfQueryFlags +{ + enum Enum + { + eWORLD_SPACE = (1<<0), //!< world-space parameter, else object space + eFIRST_CONTACT = (1<<1) //!< returns first contact only, else returns all contacts + }; +}; + +namespace Gu +{ + template class EntityReport; + + class PX_PHYSX_COMMON_API HeightFieldUtil + { + public: + PxReal mOneOverRowScale; + PxReal mOneOverHeightScale; + PxReal mOneOverColumnScale; + const Gu::HeightField* mHeightField; + const PxHeightFieldGeometry* mHfGeom; + + PX_FORCE_INLINE HeightFieldUtil(const PxHeightFieldGeometry& hfGeom) : mHeightField(static_cast(hfGeom.heightField)), mHfGeom(&hfGeom) + { + const PxReal absRowScale = PxAbs(mHfGeom->rowScale); + const PxReal absColScale = PxAbs(mHfGeom->columnScale); + //warning #1931-D on WIIU: sizeof is not a type, variable, or dereferenced pointer expression + PX_ASSERT(sizeof(reinterpret_cast(0)->height) == 2); + //PxReal minHeightPerSample = PX_MIN_HEIGHTFIELD_Y_SCALE; + PX_ASSERT(mHfGeom->heightScale >= PX_MIN_HEIGHTFIELD_Y_SCALE); + PX_ASSERT(absRowScale >= PX_MIN_HEIGHTFIELD_XZ_SCALE); + PX_ASSERT(absColScale >= PX_MIN_HEIGHTFIELD_XZ_SCALE); + PX_UNUSED(absRowScale); + PX_UNUSED(absColScale); + //using physx::intrinsics::fsel; + //mOneOverHeightScale = fsel(mHfGeom->heightScale - minHeightPerSample, 1.0f / mHfGeom->heightScale, 1.0f / minHeightPerSample); + mOneOverHeightScale = 1.0f / mHfGeom->heightScale; + mOneOverRowScale = 1.0f / mHfGeom->rowScale; + mOneOverColumnScale = 1.0f / mHfGeom->columnScale; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const Gu::HeightField& getHeightField() const { return *mHeightField; } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxHeightFieldGeometry& getHeightFieldGeometry() const { return *mHfGeom; } + + PX_FORCE_INLINE PxReal getOneOverRowScale() const { return mOneOverRowScale; } + PX_FORCE_INLINE PxReal getOneOverHeightScale() const { return mOneOverHeightScale; } + PX_FORCE_INLINE PxReal getOneOverColumnScale() const { return mOneOverColumnScale; } + + void computeLocalBounds(PxBounds3& bounds) const; + PX_FORCE_INLINE bool isCollisionVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const + { + return mHeightField->isCollisionVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE); + } + PX_FORCE_INLINE PxReal getHeightAtShapePoint(PxReal x, PxReal z) const + { + return mHfGeom->heightScale * mHeightField->getHeightInternal(x * mOneOverRowScale, z * mOneOverColumnScale); + } + + PX_FORCE_INLINE PxVec3 getNormalAtShapePoint(PxReal x, PxReal z) const + { + return mHeightField->getNormal_(x * mOneOverRowScale, z * mOneOverColumnScale, mOneOverRowScale, mOneOverHeightScale, mOneOverColumnScale); + } + + PxU32 getFaceIndexAtShapePoint(PxReal x, PxReal z) const; + + PxVec3 getVertexNormal(PxU32 vertexIndex, PxU32 row, PxU32 column) const; + PX_FORCE_INLINE PxVec3 getVertexNormal(PxU32 vertexIndex) const + { + const PxU32 nbColumns = mHeightField->getData().columns; + const PxU32 row = vertexIndex / nbColumns; + const PxU32 column = vertexIndex % nbColumns; + return getVertexNormal(vertexIndex, row, column); + } + + PxU32 getVertexFaceIndex(PxU32 vertexIndex, PxU32 row, PxU32 column) const; + void getEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, PxVec3& origin, PxVec3& extent) const; + + PxU32 getEdgeFaceIndex(PxU32 edgeIndex) const; + PxU32 getEdgeFaceIndex(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column) const; + PxU32 getEdgeFaceIndex(PxU32 edgeIndex, PxU32 count, const PxU32* PX_RESTRICT faceIndices) const; + + enum Feature { eFACE, eEDGE, eVERTEX }; + static PX_INLINE Feature getFeatureType(PxU32 featureCode) { return Feature(featureCode >> 30); } + static PX_INLINE PxU32 getFeatureIndex(PxU32 featureCode) { return (featureCode & ~0xC0000000); } + static PX_INLINE PxU32 makeFeatureCode(PxU32 index, Feature type) { return (index | (PxU32(type) << 30)); } + + // possible improvement: face index and edge index can be folded into one incremental feature index (along with vert index) + // such as, 0: vert index for cell, 1,2: edge indices, 3,4: face indices, then wrap around in multiples of 5 or 8 + // all return arrays must have a size of 11 to accomodate the possible 11 contacts + // the feature index in featureIndices array is encoded as follows: + // the high 2 bits are 00 - face, 01 - edge, 10 - vertex, 11 - unused/hole + PxU32 findClosestPointsOnCell( + PxU32 row, PxU32 column, PxVec3 point, + PxVec3* PX_RESTRICT closestPoints, PxU32* PX_RESTRICT featureCodes, + bool testFaces, bool testEdges, bool skipEdgesIfFaceHits) const; + + bool findProjectionOnTriangle(PxU32 triangleIndex, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& projection) const; + + PxReal findClosestPointOnEdge(PxU32 edgeIndex, PxU32 cell, PxU32 row, PxU32 column, const PxVec3& point, PxVec3& closestPoint) const; + + PxU32 getTriangle(const PxTransform&, PxTriangle& worldTri, + PxU32* vertexIndices, PxU32* adjacencyIndices, PxTriangleID triangleIndex, bool worldSpaceTranslation=true, bool worldSpaceRotation=true) const; + bool overlapAABBTriangles(const PxTransform&, const PxBounds3& bounds, PxU32 flags, EntityReport* callback) const; + + // check's if vertex can be used for scene query - note it is not the same as collision vertex + // the difference here is because of the eNO_BOUNDARY_EDGES flag, which should not ignore boundary edges. + // We don t have precomputed data for this case, we need to manually check these vertices if they are inside + // a hole or not - check if the vertex isSolid + PX_FORCE_INLINE bool isQueryVertex(PxU32 vertexIndex, PxU32 row, PxU32 column) const + { + // if noBoundaryEdges flag is on, we need to check the solid vertices manually for + // vertices which are on the boundaries, as those data are not precomputed + if((mHeightField->getFlags() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES) && + (row == 0 || column == 0 || (row >= mHeightField->getNbRowsFast() - 1) || (column >= mHeightField->getNbColumnsFast() - 1))) + { + // early exit if the material0 for the vertex is not a hole + if(mHeightField->getMaterialIndex0(vertexIndex) != PxHeightFieldMaterial::eHOLE) + return true; + bool nbSolid; + return mHeightField->isSolidVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE, nbSolid); + } + else + { + return mHeightField->isCollisionVertex(vertexIndex, row, column, PxHeightFieldMaterial::eHOLE); + } + } + + PX_FORCE_INLINE PxVec3 computePointNormal(PxU32 meshFlags, const PxVec3& d, const PxTransform& transform, PxReal l_sqr, PxReal x, PxReal z, PxReal epsilon, PxReal& l) const + { + PX_UNUSED(meshFlags); + + PxVec3 n; + if(l_sqr > epsilon) + { + n = transform.rotate(d); + l = n.normalize(); + } + else // l == 0 + { + n = transform.rotate(getNormalAtShapePoint(x, z)); + n = n.getNormalized(); + l = PxSqrt(l_sqr); + } + return n; + } + + PX_INLINE bool isShapePointOnHeightField(PxReal x, PxReal z) const + { + x *= mOneOverRowScale; + z *= mOneOverColumnScale; +/* return ((!(x < 0)) + && (!(z < 0)) + && (x < (mHeightField.getNbRowsFast()-1)) + && (z < (mHeightField.getNbColumnsFast()-1)));*/ + return ((x >= 0.0f) + && (z >= 0.0f) + && (x < (mHeightField->getData().rowLimit+1.0f)) + && (z < (mHeightField->getData().colLimit+1.0f))); + } + + PX_FORCE_INLINE PxVec3 hf2shapen(const PxVec3& v) const + { + return PxVec3(v.x * mOneOverRowScale, v.y * mOneOverHeightScale, v.z * mOneOverColumnScale); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 shape2hfp(const PxVec3& v) const + { + return PxVec3(v.x * mOneOverRowScale, v.y * mOneOverHeightScale, v.z * mOneOverColumnScale); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 hf2shapep(const PxVec3& v) const + { + return PxVec3(v.x * mHfGeom->rowScale, v.y * mHfGeom->heightScale, v.z * mHfGeom->columnScale); + } + + PX_INLINE PxVec3 hf2worldp(const PxTransform& pose, const PxVec3& v) const + { + const PxVec3 s = hf2shapep(v); + return pose.transform(s); + } + + PX_INLINE PxVec3 hf2worldn(const PxTransform& pose, const PxVec3& v) const + { + const PxVec3 s = hf2shapen(v); + return pose.q.rotate(s); + } + }; + + class PX_PHYSX_COMMON_API HeightFieldTraceUtil : public HeightFieldUtil + { + public: + PX_FORCE_INLINE HeightFieldTraceUtil(const PxHeightFieldGeometry& hfGeom) : HeightFieldUtil(hfGeom) {} + + // floor and ceil don't clamp down exact integers but we want that + static PX_FORCE_INLINE PxF32 floorDown(PxF32 x) { PxF32 f = PxFloor(x); return (f == x) ? f-1 : f; } + static PX_FORCE_INLINE PxF32 ceilUp (PxF32 x) { PxF32 f = PxCeil (x); return (f == x) ? f+1 : f; } + + // helper class for testing triangle height and reporting the overlapped triangles + template + class OverlapTraceSegment + { + public: + // helper rectangle struct + struct OverlapRectangle + { + PxI32 mMinu; + PxI32 mMaxu; + PxI32 mMinv; + PxI32 mMaxv; + + void invalidate() + { + mMinu = 1; + mMaxu = -1; + mMinv = 1; + mMaxv = -1; + } + }; + + // helper line struct + struct OverlapLine + { + bool mColumn; + PxI32 mLine; + PxI32 mMin; + PxI32 mMax; + + void invalidate() + { + mMin = 1; + mMax = -1; + } + }; + + public: + void operator = (OverlapTraceSegment&) {} + + OverlapTraceSegment(const HeightFieldUtil& hfUtil,const Gu::HeightField& hf) + : mInitialized(false), mHfUtil(hfUtil), mHf(hf), mNbIndices(0) {} + + PX_FORCE_INLINE bool initialized() const { return mInitialized; } + + // prepare for iterations, set the expand u|v + PX_INLINE void prepare(const PxVec3& aP0, const PxVec3& aP1, const PxVec3& overlapObjectExtent, PxF32& expandu, PxF32& expandv) + { + // height test bounds + mMinY = (PxMin(aP1.y,aP0.y) - overlapObjectExtent.y) * mHfUtil.getOneOverHeightScale(); + mMaxY = (PxMax(aP1.y,aP0.y) + overlapObjectExtent.y) * mHfUtil.getOneOverHeightScale(); + + // sets the clipping variables + mMinRow = PxI32(mHf.getMinRow((PxMin(aP1.x,aP0.x) - overlapObjectExtent.x)* mHfUtil.getOneOverRowScale())); + mMaxRow = PxI32(mHf.getMaxRow((PxMax(aP1.x,aP0.x) + overlapObjectExtent.x)* mHfUtil.getOneOverRowScale())); + mMinColumn = PxI32(mHf.getMinColumn((PxMin(aP1.z,aP0.z) - overlapObjectExtent.z)* mHfUtil.getOneOverColumnScale())); + mMaxColumn = PxI32(mHf.getMaxColumn((PxMax(aP1.z,aP0.z) + overlapObjectExtent.z)* mHfUtil.getOneOverColumnScale())); + + // sets the expanded u|v coordinates + expandu = PxCeil(overlapObjectExtent.x*mHfUtil.getOneOverRowScale()); + expandv = PxCeil(overlapObjectExtent.z*mHfUtil.getOneOverColumnScale()); + + // sets the offset that will be overlapped in each axis + mOffsetU = PxI32(expandu) + 1; + mOffsetV = PxI32(expandv) + 1; + } + + // sets all necessary variables and makes initial rectangle setup and overlap + PX_INLINE bool init(const PxI32 ui, const PxI32 vi, const PxI32 nbVi, const PxI32 step_ui, const PxI32 step_vi, T* aCallback) + { + mInitialized = true; + mCallback = aCallback; + mNumColumns = nbVi; + mStep_ui = step_ui > 0 ? 0 : -1; + mStep_vi = step_vi > 0 ? 0 : -1; + + // sets the rectangles + mCurrentRectangle.invalidate(); + mPreviousRectangle.mMinu = ui - mOffsetU; + mPreviousRectangle.mMaxu = ui + mOffsetU; + mPreviousRectangle.mMinv = vi - mOffsetV; + mPreviousRectangle.mMaxv = vi + mOffsetV; + + // visits all cells in given initial rectangle + if(!visitCells(mPreviousRectangle)) + return false; + + // reports all overlaps + if(!reportOverlaps()) + return false; + + return true; + } + + // u|v changed, check for new rectangle - compare with previous one and parse + // the added line, which is a result from the rectangle compare + PX_INLINE bool step(const PxI32 ui, const PxI32 vi) + { + mCurrentRectangle.mMinu = ui - mOffsetU; + mCurrentRectangle.mMaxu = ui + mOffsetU; + mCurrentRectangle.mMinv = vi - mOffsetV; + mCurrentRectangle.mMaxv = vi + mOffsetV; + OverlapLine line; + computeRectangleDifference(mCurrentRectangle,mPreviousRectangle,line); + + if(!visitCells(line)) + return false; + if(!reportOverlaps()) + return false; + + mPreviousRectangle = mCurrentRectangle; + return true; + } + + PX_INLINE void computeRectangleDifference(const OverlapRectangle& currentRectangle, const OverlapRectangle& previousRectangle, OverlapLine& line) + { + // check if u changes - add the row for visit + if(currentRectangle.mMinu != previousRectangle.mMinu) + { + line.mColumn = false; + line.mLine = currentRectangle.mMinu < previousRectangle.mMinu ? currentRectangle.mMinu : currentRectangle.mMaxu; + line.mMin = currentRectangle.mMinv; + line.mMax = currentRectangle.mMaxv; + return; + } + + // check if v changes - add the column for visit + if(currentRectangle.mMinv != previousRectangle.mMinv) + { + line.mColumn = true; + line.mLine = currentRectangle.mMinv < previousRectangle.mMinv ? currentRectangle.mMinv : currentRectangle.mMaxv; + line.mMin = currentRectangle.mMinu; + line.mMax = currentRectangle.mMaxu; + } + } + + // visits all cells in given rectangle + PX_INLINE bool visitCells(const OverlapRectangle& rectangle) + { + for(PxI32 ui = rectangle.mMinu + mStep_ui; ui <= rectangle.mMaxu + mStep_ui; ui++) + { + if(ui < mMinRow) + continue; + if(ui >= mMaxRow) + break; + for(PxI32 vi = rectangle.mMinv + mStep_vi; vi <= rectangle.mMaxv + mStep_vi; vi++) + { + if(vi < mMinColumn) + continue; + if(vi >= mMaxColumn) + break; + const PxI32 vertexIndex = ui*mNumColumns + vi; + if(!testVertexIndex(PxU32(vertexIndex))) + return false; + } + } + return true; + } + + // visits all cells in given line - can be row or column + PX_INLINE bool visitCells(const OverlapLine& line) + { + if(line.mMin > line.mMax) + return true; + + if(line.mColumn) + { + const PxI32 vi = line.mLine + mStep_vi; + // early exit if column is out of hf clip area + if(vi < mMinColumn) + return true; + if(vi >= mMaxColumn) + return true; + + for(PxI32 ui = line.mMin + mStep_ui; ui <= line.mMax + mStep_ui; ui++) + { + // early exit or continue if row is out of hf clip area + if(ui >= mMaxRow) + break; + // continue if we did not reach the valid area, we can still get there + if(ui < mMinRow) + continue; + // if the cell has not been tested test and report + if(!testVertexIndex(PxU32(mNumColumns * ui + vi))) + return false; + } + } + else + { + const PxI32 ui = line.mLine + mStep_ui; + // early exit if row is out of hf clip area + if(ui < mMinRow) + return true; + if(ui >= mMaxRow) + return true; + + for(PxI32 vi = line.mMin + mStep_vi; vi <= line.mMax + mStep_vi; vi++) + { + // early exit or continue if column is out of hf clip area + if(vi >= mMaxColumn) + break; + // continue if we did not reach the valid area, we can still get there + if(vi < mMinColumn) + continue; + // if the cell has not been tested test and report + if(!testVertexIndex(PxU32(mNumColumns * ui + vi))) + return false; + } + } + return true; + } + + // does height check and if succeeded adds to report + PX_INLINE bool testVertexIndex(const PxU32 vertexIndex) + { +#define ISHOLE0 (mHf.getMaterialIndex0(vertexIndex) == PxHeightFieldMaterial::eHOLE) +#define ISHOLE1 (mHf.getMaterialIndex1(vertexIndex) == PxHeightFieldMaterial::eHOLE) + const PxReal h0 = mHf.getHeight(vertexIndex); + const PxReal h1 = mHf.getHeight(vertexIndex + 1); + const PxReal h2 = mHf.getHeight(vertexIndex + mNumColumns); + const PxReal h3 = mHf.getHeight(vertexIndex + mNumColumns + 1); + // actual height test, if some height pass we accept the cell + if(!((mMaxY < h0 && mMaxY < h1 && mMaxY < h2 && mMaxY < h3) || (mMinY > h0 && mMinY > h1 && mMinY > h2 && mMinY > h3))) + { + // check if the triangle is not a hole + if(!ISHOLE0) + { + if(!addIndex(vertexIndex*2)) + return false; + } + if(!ISHOLE1) + { + if(!addIndex(vertexIndex*2 + 1)) + return false; + } + } +#undef ISHOLE0 +#undef ISHOLE1 + return true; + } + + // add triangle index, if we get out of buffer size, report them + bool addIndex(PxU32 triangleIndex) + { + if(mNbIndices == HF_SWEEP_REPORT_BUFFER_SIZE) + { + if(!reportOverlaps()) + return false; + } + + mIndexBuffer[mNbIndices++] = triangleIndex; + return true; + } + + PX_FORCE_INLINE bool reportOverlaps() + { + if(mNbIndices) + { + if(!mCallback->onEvent(mNbIndices, mIndexBuffer)) + return false; + mNbIndices = 0; + } + return true; + } + + private: + bool mInitialized; + const HeightFieldUtil& mHfUtil; + const Gu::HeightField& mHf; + T* mCallback; + PxI32 mOffsetU; + PxI32 mOffsetV; + float mMinY; + float mMaxY; + PxI32 mMinRow; + PxI32 mMaxRow; + PxI32 mMinColumn; + PxI32 mMaxColumn; + PxI32 mNumColumns; + PxI32 mStep_ui; + PxI32 mStep_vi; + OverlapRectangle mPreviousRectangle; + OverlapRectangle mCurrentRectangle; + PxU32 mIndexBuffer[HF_SWEEP_REPORT_BUFFER_SIZE]; + PxU32 mNbIndices; + }; + + // If useUnderFaceCalblack is false, traceSegment will report segment/triangle hits via + // faceHit(const Gu::HeightFieldUtil& hf, const PxVec3& point, PxU32 triangleIndex) + // Otherwise traceSegment will report all triangles the segment passes under via + // underFaceHit(const Gu::HeightFieldUtil& hf, const PxVec3& triNormal, const PxVec3& crossedEdge, + // PxF32 x, PxF32 z, PxF32 rayHeight, PxU32 triangleIndex) + // where x,z is the point of previous intercept in hf coords, rayHeight is at that same point + // crossedEdge is the edge vector crossed from last call to underFaceHit, undefined for first call + // Note that underFaceHit can be called when a line is above a triangle if it's within AABB for that hf cell + // Note that backfaceCull is ignored if useUnderFaceCallback is true + // overlapObjectExtent (localSpace) and overlap are used for triangle collecting using an inflated tracesegment + // Note that hfLocalBounds are passed as a parameter instead of being computed inside the traceSegment. + // The localBounds can be obtained: PxBounds3 hfLocalBounds; hfUtil.computeLocalBounds(hfLocalBounds); and passed as + // a parameter. + template + PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDir, const float rayLength , T* aCallback, const PxBounds3& hfLocalBounds, bool backfaceCull, + const PxVec3* overlapObjectExtent = NULL) const + { + PxF32 tnear, tfar; + if(!Gu::intersectRayAABB2(hfLocalBounds.minimum, hfLocalBounds.maximum, aP0, rayDir, rayLength, tnear, tfar)) + return; + + const PxVec3 p0 = aP0 + rayDir * tnear; + const PxVec3 p1 = aP0 + rayDir * tfar; + + // helper class used for overlap tests + OverlapTraceSegment overlapTraceSegment(*this, *mHeightField); + + // values which expand the HF area + PxF32 expandu = 0.0f, expandv = 0.0f; + + if (overlap) + { + // setup overlap variables + overlapTraceSegment.prepare(aP0,aP0 + rayDir*rayLength,*overlapObjectExtent,expandu,expandv); + } + + // row = x|u, column = z|v + const PxF32 rowScale = mHfGeom->rowScale, columnScale = mHfGeom->columnScale, heightScale = mHfGeom->heightScale; + const PxI32 nbVi = PxI32(mHeightField->getNbColumnsFast()), nbUi = PxI32(mHeightField->getNbRowsFast()); + PX_ASSERT(nbVi > 0 && nbUi > 0); + + // clampEps is chosen so that we get a reasonable clamp value for 65536*0.9999999f = 65535.992187500000 + const PxF32 clampEps = 1e-7f; // shrink u,v to within 1e-7 away from the world bounds + + // we now clamp uvs to [1e-7, rowLimit-1e-7] to avoid out of range uvs and eliminate related checks in the loop + const PxF32 nbUcells = PxF32(nbUi-1)*(1.0f-clampEps), nbVcells = PxF32(nbVi-1)*(1.0f-clampEps); + + // if u0,v0 is near an integer, shift up or down in direction opposite to du,dv by PxMax(|u,v|*1e-7, 1e-7) + // (same direction as du,dv for u1,v1) + // we do this to ensure that we get at least one intersection with u or v when near the cell edge to eliminate special cases in the loop + // we need to extend the field for the inflated radius, we will now operate even with negative u|v + + // map p0 from (x, z, y) to (u0, v0, h0) + // we need to use the unclamped values, otherwise we change the direction of the traversal + const PxF32 uu0 = p0.x * mOneOverRowScale; + PxF32 u0 = PxMin(PxMax(uu0, 1e-7f - expandu), nbUcells + expandu); // multiplication rescales the u,v grid steps to 1 + const PxF32 uv0 = p0.z * mOneOverColumnScale; + PxF32 v0 = PxMin(PxMax(uv0, 1e-7f - expandv), nbVcells + expandv); + const PxReal h0 = p0.y; // we don't scale y + + // map p1 from (x, z, y) to (u1, v1, h1) + // we need to use the unclamped values, otherwise we change the direction of the traversal + const PxF32 uu1 = p1.x * mOneOverRowScale; + const PxF32 uv1 = p1.z * mOneOverColumnScale; + const PxReal h1 = p1.y; // we don't scale y + + PxF32 du = uu1 - uu0, dv = uv1 - uv0; // recompute du, dv from adjusted uvs + const PxReal dh = h1 - h0; + + // grid u&v step is always either 1 or -1, we precompute as both integers and floats to avoid conversions + // so step_uif is +/-1.0f, step_ui is +/-1 + const PxF32 step_uif = PxSign(du), step_vif = PxSign(dv); + const PxI32 step_ui = PxI32(step_uif), step_vi = PxI32(step_vif); + + // clamp magnitude of du, dv to at least clampEpsilon to avoid special cases when dividing + const PxF32 divEpsilon = 1e-10f; + if(PxAbs(du) < divEpsilon) + du = step_uif * divEpsilon; + if(PxAbs(dv) < divEpsilon) + dv = step_vif * divEpsilon; + + const PxVec3 auhP0(aP0.x*mOneOverRowScale, aP0.y, aP0.z*mOneOverColumnScale); + const PxVec3 duhv(rayDir.x*rayLength*mOneOverRowScale, rayDir.y*rayLength, rayDir.z*rayLength*mOneOverColumnScale); + const PxReal duhvLength = duhv.magnitude(); + PxVec3 duhvNormalized = duhv; + if(duhvLength > PX_NORMALIZATION_EPSILON) + duhvNormalized *= 1.0f/duhvLength; + + // Math derivation: + // points on 2d segment are parametrized as: [u0,v0] + t [du, dv]. We solve for t_u[n], t for nth u-intercept + // u0 + t_un du = un + // t_un = (un-u0) / du + // t_un1 = (un+1-u0) / du ; we use +1 since we rescaled the grid step to 1 + // therefore step_tu = t_un - t_un1 = 1/du + + // seed the initial integer cell coordinates with u0, v0 rounded up or down with standard PxFloor/Ceil behavior + // to ensure we have the correct first cell between (ui,vi) and (ui+step_ui,vi+step_vi) + PxI32 ui = (du > 0.0f) ? PxI32(PxFloor(u0)) : PxI32(PxCeil(u0)); + PxI32 vi = (dv > 0.0f) ? PxI32(PxFloor(v0)) : PxI32(PxCeil(v0)); + + // find the nearest integer u, v in ray traversal direction and corresponding tu and tv + const PxReal uhit0 = du > 0.0f ? ceilUp(u0) : floorDown(u0); + const PxReal vhit0 = dv > 0.0f ? ceilUp(v0) : floorDown(v0); + + // tu, tv can be > 1 but since the loop is structured as do {} while(tMin < tEnd) we still visit the first cell + PxF32 last_tu = 0.0f, last_tv = 0.0f; + PxReal tu = (uhit0 - uu0) / du; + PxReal tv = (vhit0 - uv0) / dv; + if(tu < 0.0f) // negative value may happen, as we may have started out of the AABB (since we did enlarge it) + tu = PxAbs(clampEps / du); + if(tv < 0.0f) // negative value may happen, as we may have started out of the AABB (since we did enlarge it) + tv = PxAbs(clampEps / dv); + + // compute step_tu and step_tv; t steps per grid cell in u and v direction + const PxReal step_tu = 1.0f / PxAbs(du), step_tv = 1.0f / PxAbs(dv); + + // t advances at the same rate for u, v and h therefore we can compute h at u,v grid intercepts + #define COMPUTE_H_FROM_T(t) (h0 + (t) * dh) + + const PxF32 hEpsilon = 1e-4f; + PxF32 uif = PxF32(ui), vif = PxF32(vi); + + // these are used to remap h values to correspond to u,v increasing order + PxI32 uflip = 1-step_ui; /*0 or 2*/ + PxI32 vflip = (1-step_vi)/2; /*0 or 1*/ + + // this epsilon is needed to ensure that we include the last [t, t+1] range in the do {} while(t= 0 - expandu && ui < nbUi + expandu && vi >= 0 - expandv && vi < nbVi + expandv); + PX_ASSERT(ui+step_ui >= 0 - expandu && ui+step_ui < nbUi + expandu && vi+step_vi >= 0 - expandv && vi+step_vi < nbVi + expandv); + + // handle overlap in overlapCallback + if(overlap) + { + if(!overlapTraceSegment.initialized()) + { + // initial overlap and setup + if(!overlapTraceSegment.init(ui,vi,nbVi,step_ui,step_vi,aCallback)) + return; + } + else + { + // overlap step + if(!overlapTraceSegment.step(ui,vi)) + return; + } + } + else + { + const PxU32 colIndex0 = PxU32(nbVi * ui + vi); + const PxU32 colIndex1 = PxU32(nbVi * (ui + step_ui) + vi); + const PxReal h[4] = { // h[0]=h00, h[1]=h01, h[2]=h10, h[3]=h11 - oriented relative to step_uv + hf.getHeight(colIndex0) * heightScale, hf.getHeight(colIndex0 + step_vi) * heightScale, + hf.getHeight(colIndex1) * heightScale, hf.getHeight(colIndex1 + step_vi) * heightScale }; + + PxF32 minH = PxMin(PxMin(h[0], h[1]), PxMin(h[2], h[3])); + PxF32 maxH = PxMax(PxMax(h[0], h[1]), PxMax(h[2], h[3])); + + // how much space in h have we covered from previous to current u or v intercept + PxF32 hLineCellRangeMin = PxMin(hLinePrev, hLineNext); + PxF32 hLineCellRangeMax = PxMax(hLinePrev, hLineNext); + + // do a quick overlap test in h, this should be rejecting the vast majority of tests + if(!(hLineCellRangeMin-hEpsilon > maxH || hLineCellRangeMax+hEpsilon < minH) || + (useUnderFaceCallback && hLineCellRangeMax < maxH)) + { + // arrange h so that h00 corresponds to min(uif, uif+step_uif) h10 to max et c. + // this is only needed for backface culling to work so we know the proper winding order without branches + // uflip is 0 or 2, vflip is 0 or 1 (corresponding to positive and negative ui_step and vi_step) + PxF32 h00 = h[0+uflip+vflip]; + PxF32 h01 = h[1+uflip-vflip]; + PxF32 h10 = h[2-uflip+vflip]; + PxF32 h11 = h[3-uflip-vflip]; + + PxF32 minuif = PxMin(uif, uif+step_uif); + PxF32 maxuif = PxMax(uif, uif+step_uif); + PxF32 minvif = PxMin(vif, vif+step_vif); + PxF32 maxvif = PxMax(vif, vif+step_vif); + PxVec3 p00(minuif, h00, minvif); + PxVec3 p01(minuif, h01, maxvif); + PxVec3 p10(maxuif, h10, minvif); + PxVec3 p11(maxuif, h11, maxvif); + + const PxF32 enlargeEpsilon = 0.0001f; + const PxVec3* p00a = &p00, *p01a = &p01, *p10a = &p10, *p11a = &p11; + PxU32 minui = PxU32(PxMin(ui+step_ui, ui)), minvi = PxU32(PxMin(vi+step_vi, vi)); + + // row = x|u, column = z|v + const PxU32 vertIndex = nbVi * minui + minvi; + const PxU32 cellIndex = vertIndex; // this adds a dummy unused cell in the end of each row; was -minui + bool isZVS = hf.isZerothVertexShared(vertIndex); + if(!isZVS) + { + // rotate the pointers for flipped edge cells + p10a = &p00; + p00a = &p01; + p01a = &p11; + p11a = &p10; + } + + // For triangle index computation, see illustration in Gu::HeightField::getTriangleNormal() + // Since row = u, column = v + // for zeroth vert shared the 10 index is the corner of the 0-index triangle, and 01 is 1-index + // if zeroth vertex is not shared, the 00 index is the corner of 0-index triangle + if(!useUnderFaceCallback) + { + #define ISHOLE0 (hf.getMaterialIndex0(vertIndex) == PxHeightFieldMaterial::eHOLE) + #define ISHOLE1 (hf.getMaterialIndex1(vertIndex) == PxHeightFieldMaterial::eHOLE) + PxReal triT0 = PX_MAX_REAL, triT1 = PX_MAX_REAL; + bool hit0 = false, hit1 = false; + PxF32 triU0, triV0, triU1, triV1; + if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p10a, *p00a, *p11a, triT0, triU0, triV0, backfaceCull, enlargeEpsilon) && + triT0 >= 0.0f && triT0 <= duhvLength && !ISHOLE0) + { + hit0 = true; + } else + triT0 = PX_MAX_REAL; + if(Gu::intersectRayTriangle(auhP0, duhvNormalized, *p01a, *p11a, *p00a, triT1, triU1, triV1, backfaceCull, enlargeEpsilon) + && triT1 >= 0.0f && triT1 <= duhvLength && !ISHOLE1) + { + hit1 = true; + } else + triT1 = PX_MAX_REAL; + + if(hit0 && triT0 <= triT1) + { + const PxVec3 hitPoint((auhP0.x + duhvNormalized.x*triT0) * rowScale, auhP0.y + duhvNormalized.y * triT0, (auhP0.z + duhvNormalized.z*triT0) * columnScale); + if(!aCallback->faceHit(*this, hitPoint, cellIndex*2, triU0, triV0)) + return; + if(hit1) // possible to hit both triangles in a cell with eMESH_MULTIPLE + { + PxVec3 hitPoint1((auhP0.x + duhvNormalized.x*triT1) * rowScale, auhP0.y + duhvNormalized.y * triT1, (auhP0.z + duhvNormalized.z*triT1) * columnScale); + if(!aCallback->faceHit(*this, hitPoint1, cellIndex*2 + 1, triU1, triV1)) + return; + } + } + else if(hit1 && triT1 <= triT0) + { + PxVec3 hitPoint((auhP0.x + duhvNormalized.x*triT1) * rowScale, auhP0.y + duhvNormalized.y * triT1, (auhP0.z + duhvNormalized.z*triT1) * columnScale); + if(!aCallback->faceHit(*this, hitPoint, cellIndex*2 + 1, triU1, triV1)) + return; + if(hit0) // possible to hit both triangles in a cell with eMESH_MULTIPLE + { + PxVec3 hitPoint1((auhP0.x + duhvNormalized.x*triT0) * rowScale, auhP0.y + duhvNormalized.y * triT0, (auhP0.z + duhvNormalized.z*triT0) * columnScale); + if(!aCallback->faceHit(*this, hitPoint1, cellIndex*2, triU0, triV0)) + return; + } + } + #undef ISHOLE0 + #undef ISHOLE1 + } + else + { + // TODO: quite a few optimizations are possible here. edges can be shared, intersectRayTriangle inlined etc + // Go to shape space. Height is already in shape space so we only scale x and z + PxVec3 p00s(p00a->x * rowScale, p00a->y, p00a->z * columnScale); + PxVec3 p01s(p01a->x * rowScale, p01a->y, p01a->z * columnScale); + PxVec3 p10s(p10a->x * rowScale, p10a->y, p10a->z * columnScale); + PxVec3 p11s(p11a->x * rowScale, p11a->y, p11a->z * columnScale); + + PxVec3 triNormals[2] = { (p00s - p10s).cross(p11s - p10s), (p11s - p01s).cross(p00s-p01s) }; + triNormals[0] *= PxRecipSqrt(triNormals[0].magnitudeSquared()); + triNormals[1] *= PxRecipSqrt(triNormals[1].magnitudeSquared()); + // since the heightfield can be mirrored with negative rowScale or columnScale, this assert doesn't hold + //PX_ASSERT(triNormals[0].y >= 0.0f && triNormals[1].y >= 0.0f); + + // at this point we need to compute the edge direction that we crossed + // also since we don't DDA the w we need to find u,v for w-intercept (w refers to diagonal adjusted with isZVS) + PxF32 wnu = isZVS ? -1.0f : 1.0f, wnv = 1.0f; // uv-normal to triangle edge that splits the cell + PxF32 wpu = uif + 0.5f * step_uif, wpv = vif + 0.5f * step_vif; // a point on triangle edge that splits the cell + // note that (wpu, wpv) is on both edges (for isZVS and non-ZVS cases) which is nice + + // we clamp tNext to 1 because we still want to issue callbacks even if we stay in one cell + // note that tNext can potentially be arbitrarily large for a segment contained within a cell + PxF32 tNext = PxMin(PxMin(tu, tv), 1.0f), tPrev = PxMax(last_tu, last_tv); + + // compute uvs corresponding to tPrev, tNext + PxF32 unext = u0 + tNext*du, vnext = v0 + tNext*dv; + PxF32 uprev = u0 + tPrev*du, vprev = v0 + tPrev*dv; + + const PxReal& h00_ = h[0], &h01_ = h[1], &h10_ = h[2]/*, h11_ = h[3]*/; // aliases for step-oriented h + + // (wpu, wpv) is a point on the diagonal + // we compute a dot of ((unext, vnext) - (wpu, wpv), wn) to see on which side of triangle edge we are + // if the dot is positive we need to add 1 to triangle index + PxU32 dotPrevGtz = PxU32(((uprev - wpu) * wnu + (vprev - wpv) * wnv) > 0); + PxU32 dotNextGtz = PxU32(((unext - wpu) * wnu + (vnext - wpv) * wnv) > 0); + PxU32 triIndex0 = cellIndex*2 + dotPrevGtz; + PxU32 triIndex1 = cellIndex*2 + dotNextGtz; + PxU32 isHole0 = PxU32(hf.getMaterialIndex0(vertIndex) == PxHeightFieldMaterial::eHOLE); + PxU32 isHole1 = PxU32(hf.getMaterialIndex1(vertIndex) == PxHeightFieldMaterial::eHOLE); + if(triIndex0 > triIndex1) + shdfnd::swap(isHole0, isHole1); + + // TODO: compute height at u,v inside here, change callback param to PxVec3 + PxVec3 crossedEdge; + if(last_tu > last_tv) // previous intercept was at u, so we use u=const edge + crossedEdge = PxVec3(0.0f, h01_-h00_, step_vif * columnScale); + else // previous intercept at v, use v=const edge + crossedEdge = PxVec3(step_uif * rowScale, h10_-h00_, 0.0f); + + if(!isHole0 && !aCallback->underFaceHit(*this, triNormals[dotPrevGtz], crossedEdge, + uprev * rowScale, vprev * columnScale, COMPUTE_H_FROM_T(tPrev), triIndex0)) + return; + + if(triIndex1 != triIndex0 && !isHole1) // if triIndex0 != triIndex1 that means we cross the triangle edge + { + // Need to compute tw, the t for ray intersecting the diagonal within the current cell + // dot((wnu, wnv), (u0+tw*du, v0+tw*dv)-(wpu, wpv)) = 0 + // wnu*(u0+tw*du-wpu) + wnv*(v0+tw*dv-wpv) = 0 + // wnu*u0+wnv*v0-wnu*wpu-wnv*wpv + tw*(du*wnu + dv*wnv) = 0 + const PxF32 denom = du*wnu + dv*wnv; + if(PxAbs(denom) > 1e-6f) + { + const PxF32 tw = (wnu*(wpu-u0)+wnv*(wpv-v0)) / denom; + if(!aCallback->underFaceHit(*this, triNormals[dotNextGtz], p10s-p01s, + (u0+tw*du) * rowScale, (v0+tw*dv) * columnScale, COMPUTE_H_FROM_T(tw), triIndex1)) + return; + } + } + } + } + } + + if(tu < tv) + { + last_tu = tu; + ui += step_ui; + // AP: very rare condition, wasn't able to repro but we need this if anyway (DE6565) + if(ui+step_ui< (0 - expandu) || ui+step_ui>=(nbUi + expandu)) // should hold true for ui without step from previous iteration + break; + uif += step_uif; + tu += step_tu; + } + else + { + last_tv = tv; + vi += step_vi; + // AP: very rare condition, wasn't able to repro but we need this if anyway (DE6565) + if(vi+step_vi< (0 - expandv) || vi+step_vi>=(nbVi + expandv)) // should hold true for vi without step from previous iteration + break; + vif += step_vif; + tv += step_tv; + } + hLinePrev = hLineNext; + } + // since min(tu,tv) is the END of the active interval we need to check if PREVIOUS min(tu,tv) was past interval end + // since we update tMinUV in the beginning of the loop, at this point it stores the min(last tu,last tv) + while (tMinUV < tEnd); + #undef COMPUTE_H_FROM_T + } + }; + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp new file mode 100644 index 000000000..4b14b885e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp @@ -0,0 +1,751 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "PsAllocator.h" +#include "GuOverlapTests.h" + +#include "PsUtilities.h" +#include "PsVecMath.h" + +#include "GuHeightFieldUtil.h" +#include "GuIntersectionBoxBox.h" +#include "GuIntersectionTriangleBox.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentBox.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentSegmentSIMD.h" + +#include "PxSphereGeometry.h" +#include "PxBoxGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" + +#include "GuCapsule.h" +#include "GuEdgeCache.h" +#include "GuBoxConversion.h" + +#include "GuInternal.h" +#include "GuConvexUtilsInternal.h" + +#include "GuVecTriangle.h" +#include "GuVecSphere.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuConvexMesh.h" + +using namespace physx; +using namespace Cm; +using namespace Gu; +using namespace Ps::aos; + +static bool intersectHeightFieldSphere(const HeightFieldUtil& hfUtil, const Sphere& sphereInHfShape) +{ + const HeightField& hf = hfUtil.getHeightField(); + + // sample the sphere center in the heightfield to find out + // if we have penetration with more than the sphere radius + if(hfUtil.isShapePointOnHeightField(sphereInHfShape.center.x, sphereInHfShape.center.z)) + { + // The sphere origin projects within the bounds of the heightfield in the X-Z plane + PxReal sampleHeight = hfUtil.getHeightAtShapePoint(sphereInHfShape.center.x, sphereInHfShape.center.z); + PxReal deltaHeight = sphereInHfShape.center.y - sampleHeight; + if(hf.isDeltaHeightInsideExtent(deltaHeight)) + { + // The sphere origin is 'below' the heightfield surface + PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(sphereInHfShape.center.x, sphereInHfShape.center.z); + if(faceIndex != 0xffffffff) + { + return true; + } + return false; + } + } + + const PxReal radiusSquared = sphereInHfShape.radius * sphereInHfShape.radius; + + const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape.center); + + const PxReal radiusOverRowScale = sphereInHfShape.radius * PxAbs(hfUtil.getOneOverRowScale()); + const PxReal radiusOverColumnScale = sphereInHfShape.radius * PxAbs(hfUtil.getOneOverColumnScale()); + + const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale); + const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale); + const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale); + const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale); + + for(PxU32 r = minRow; r < maxRow; r++) + { + for(PxU32 c = minColumn; c < maxColumn; c++) + { + + // x--x--x + // | x | + // x x x + // | x | + // x--x--x + PxVec3 pcp[11]; + PxU32 npcp = 0; + npcp = hfUtil.findClosestPointsOnCell(r, c, sphereInHfShape.center, pcp, NULL, true, true, true); + + for(PxU32 pi = 0; pi < npcp; pi++) + { + PxVec3 d = sphereInHfShape.center - pcp[pi]; + + PxReal ll = d.magnitudeSquared(); + + if(ll > radiusSquared) + // Too far + continue; + + return true; + } + } + } + return false; +} + +static bool intersectHeightFieldCapsule(const HeightFieldUtil& hfUtil, const PxCapsuleGeometry& capsuleGeom, const PxTransform& capsulePose) +{ + const HeightField& hf = hfUtil.getHeightField(); + + PxVec3 verticesInHfShape[2]; + PxVec3 capsuleOrigin, capsuleExtent; + { + const PxVec3 capsuleHalfHeightVector = getCapsuleHalfHeightVector(capsulePose, capsuleGeom); + + capsuleOrigin = capsulePose.p + capsuleHalfHeightVector; + capsuleExtent = -capsuleHalfHeightVector*2.0f; + + verticesInHfShape[0] = capsuleOrigin; + verticesInHfShape[1] = capsulePose.p - capsuleHalfHeightVector; + } + + const PxReal radius = capsuleGeom.radius; + const PxReal radiusOverRowScale = radius * PxAbs(hfUtil.getOneOverRowScale()); + const PxReal radiusOverColumnScale = radius * PxAbs(hfUtil.getOneOverColumnScale()); + + PxU32 absMinRow = 0xffffffff; + PxU32 absMaxRow = 0; + PxU32 absMinColumn = 0xffffffff; + PxU32 absMaxColumn = 0; + + PxReal radiusSquared = radius * radius; + + // test both of capsule's corner vertices+radius for HF overlap + for(PxU32 i = 0; i<2; i++) + { + const PxVec3& sphereInHfShape = verticesInHfShape[i]; + + // we have to do this first to update the absMin / absMax correctly even if + // we decide to continue from inside the deep penetration code. + + const PxVec3 sphereInHF = hfUtil.shape2hfp(sphereInHfShape); + + const PxU32 minRow = hf.getMinRow(sphereInHF.x - radiusOverRowScale); + const PxU32 maxRow = hf.getMaxRow(sphereInHF.x + radiusOverRowScale); + const PxU32 minColumn = hf.getMinColumn(sphereInHF.z - radiusOverColumnScale); + const PxU32 maxColumn = hf.getMaxColumn(sphereInHF.z + radiusOverColumnScale); + + if(minRow < absMinRow) absMinRow = minRow; + if(minColumn < absMinColumn) absMinColumn = minColumn; + if(maxRow > absMaxRow) absMaxRow = maxRow; + if(maxColumn > absMaxColumn) absMaxColumn = maxColumn; + + if(hfUtil.isShapePointOnHeightField(sphereInHfShape.x, sphereInHfShape.z)) + { + // The sphere origin projects within the bounds of the heightfield in the X-Z plane + const PxReal sampleHeight = hfUtil.getHeightAtShapePoint(sphereInHfShape.x, sphereInHfShape.z); + const PxReal deltaHeight = sphereInHfShape.y - sampleHeight; + if(hf.isDeltaHeightInsideExtent(deltaHeight)) + { + // The sphere origin is 'below' the heightfield surface + const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(sphereInHfShape.x, sphereInHfShape.z); + if(faceIndex != 0xffffffff) + { + return true; + } + continue; + } + } + + for(PxU32 r = minRow; r < maxRow; r++) + { + for(PxU32 c = minColumn; c < maxColumn; c++) + { + + // x--x--x + // | x | + // x x x + // | x | + // x--x--x + PxVec3 pcp[11]; + PxU32 npcp = 0; + npcp = hfUtil.findClosestPointsOnCell(r, c, sphereInHfShape, pcp, NULL, true, true, true); + + for(PxU32 pi = 0; pi < npcp; pi++) + { + const PxVec3 d = sphereInHfShape - pcp[pi]; + + if(hf.isDeltaHeightOppositeExtent(d.y)) + { + // We are 'above' the heightfield + + const PxReal ll = d.magnitudeSquared(); + if(ll > radiusSquared) + // Too far above + continue; + + return true; + } + } + } + } + } + + const Vec3V p1 = V3LoadU(capsuleOrigin); + const Vec3V d1 = V3LoadU(capsuleExtent); + + // now test capsule's inflated segment for overlap with HF edges + PxU32 row, column; + for(row = absMinRow; row <= absMaxRow; row++) + { + for(column = absMinColumn; column <= absMaxColumn; column++) + { + const PxU32 vertexIndex = row * hf.getNbColumnsFast() + column; + const PxU32 firstEdge = 3 * vertexIndex; + // omg I am sorry about this code but I can't find a simpler way: + // last column will only test edge 2 + // last row will only test edge 0 + // and most importantly last row and column will not go inside the for + const PxU32 minEi = (column == absMaxColumn) ? 2u : 0; + const PxU32 maxEi = (row == absMaxRow ) ? 1u : 3u; + for(PxU32 ei = minEi; ei < maxEi; ei++) + { + const PxU32 edgeIndex = firstEdge + ei; + + const PxU32 cell = vertexIndex; + PX_ASSERT(cell == edgeIndex / 3); + const PxU32 row_ = row; + PX_ASSERT(row_ == cell / hf.getNbColumnsFast()); + const PxU32 column_ = column; + PX_ASSERT(column_ == cell % hf.getNbColumnsFast()); + + const PxU32 faceIndex = hfUtil.getEdgeFaceIndex(edgeIndex, cell, row_, column_); + if(faceIndex != 0xffffffff) + { + PxVec3 origin; + PxVec3 direction; + hfUtil.getEdge(edgeIndex, cell, row_, column_, origin, direction); + + const Vec3V p2 = V3LoadU(origin); + const Vec3V d2 = V3LoadU(direction); + FloatV s, t; + const FloatV llV = Gu::distanceSegmentSegmentSquared(p1, d1, p2, d2, s, t); + + PxReal ll; + FStore(llV, &ll); + + if(ll < radiusSquared) + return true; + } + } + } + } + return false; +} + +namespace physx +{ +namespace Gu +{ + const PxReal signs[24] = + { + -1,-1,-1, + -1,-1, 1, + -1, 1,-1, + -1, 1, 1, + 1,-1,-1, + 1,-1, 1, + 1, 1,-1, + 1, 1, 1, + }; + + const char edges[24] = + { + 0,1, + 1,3, + 3,2, + 2,0, + 4,5, + 5,7, + 7,6, + 6,4, + 0,4, + 1,5, + 2,6, + 3,7, + }; + + struct TriggerTraceSegmentCallback + { + bool intersection; + + PX_INLINE TriggerTraceSegmentCallback() : intersection(false) + { + } + + PX_INLINE bool underFaceHit( + const HeightFieldUtil&, const PxVec3&, + const PxVec3&, PxF32, PxF32, PxF32, PxU32) + { + return true; + } + + PX_INLINE bool faceHit(const HeightFieldUtil&, const PxVec3&, PxU32, PxReal, PxReal) + { + intersection = true; + return false; + } + bool onEvent(PxU32 , PxU32* ) + { + return true; + } + }; + + + class OverlapHeightfieldTraceSegmentHelper + { + PX_NOCOPY(OverlapHeightfieldTraceSegmentHelper) + public: + OverlapHeightfieldTraceSegmentHelper(const HeightFieldTraceUtil& hfUtil) : mHfUtil(hfUtil) + { + mHfUtil.computeLocalBounds(mLocalBounds); + } + + PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& aP1, TriggerTraceSegmentCallback* aCallback) const + { + mHfUtil.traceSegment(aP0, aP1 - aP0, 1.0f, aCallback, mLocalBounds, false, NULL); + } + + private: + const HeightFieldTraceUtil& mHfUtil; + PxBounds3 mLocalBounds; + }; + +} // namespace +} + +static bool intersectHeightFieldBox(const HeightFieldTraceUtil& hfUtil, const Box& boxInHfShape) +{ + const HeightField& hf = hfUtil.getHeightField(); + + // Get box vertices + PxVec3 boxVertices[8]; + for(PxU32 i=0; i<8; i++) + boxVertices[i] = PxVec3(boxInHfShape.extents.x*signs[3*i], boxInHfShape.extents.y*signs[3*i+1], boxInHfShape.extents.z*signs[3*i+2]); + + // Transform box vertices to HeightFieldShape space + PxVec3 boxVerticesInHfShape[8]; + for(PxU32 i=0; i<8; i++) + boxVerticesInHfShape[i] = boxInHfShape.transform(boxVertices[i]); + + // Test box vertices. + { + for(PxU32 i=0; i<8; i++) + { + const PxVec3& boxVertexInHfShape = boxVerticesInHfShape[i]; + if(hfUtil.isShapePointOnHeightField(boxVertexInHfShape.x, boxVertexInHfShape.z)) + { + const PxReal y = hfUtil.getHeightAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z); + const PxReal dy = boxVertexInHfShape.y - y; + if(hf.isDeltaHeightInsideExtent(dy)) + { + PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(boxVertexInHfShape.x, boxVertexInHfShape.z); + if(faceIndex != 0xffffffff) + { + return true; + } + } + } + } + } + + // Test box edges. + { + OverlapHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil); + + for(PxU32 i=0; i<12; i++) + { + const PxVec3 v0 = boxVerticesInHfShape[PxU8(edges[2*i])]; + const PxVec3 v1 = boxVerticesInHfShape[PxU8(edges[2*i+1])]; + TriggerTraceSegmentCallback cb; + traceSegmentHelper.traceSegment(v0, v1, &cb); + if(cb.intersection) + return true; + } + } + + // Test HeightField vertices. + { + PsTransformV _hfShape2BoxShape; + const PxQuat bq(boxInHfShape.rot); + const QuatV q1 = QuatVLoadU(&bq.x); + const Vec3V p1 = V3LoadU(&boxInHfShape.center.x); + const PsTransformV _boxPose(p1, q1); + _hfShape2BoxShape = _boxPose.getInverse(); + + PxReal minx(PX_MAX_REAL); + PxReal minz(PX_MAX_REAL); + PxReal maxx(-PX_MAX_REAL); + PxReal maxz(-PX_MAX_REAL); + + for(PxU32 i=0; i<8; i++) + { + const PxVec3& boxVertexInHfShape = boxVerticesInHfShape[i]; + +/* if(boxVertexInHfShape.x < minx) minx = boxVertexInHfShape.x; + if(boxVertexInHfShape.z < minz) minz = boxVertexInHfShape.z; + if(boxVertexInHfShape.x > maxx) maxx = boxVertexInHfShape.x; + if(boxVertexInHfShape.z > maxz) maxz = boxVertexInHfShape.z;*/ + minx = physx::intrinsics::selectMin(boxVertexInHfShape.x, minx); + minz = physx::intrinsics::selectMin(boxVertexInHfShape.z, minz); + maxx = physx::intrinsics::selectMax(boxVertexInHfShape.x, maxx); + maxz = physx::intrinsics::selectMax(boxVertexInHfShape.z, maxz); + } + + const PxReal oneOverRowScale = hfUtil.getOneOverRowScale(); + const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale(); + const PxU32 minRow = hf.getMinRow(minx * oneOverRowScale); + const PxU32 maxRow = hf.getMaxRow(maxx * oneOverRowScale); + const PxU32 minColumn = hf.getMinColumn(minz * oneOverColumnScale); + const PxU32 maxColumn = hf.getMaxColumn(maxz * oneOverColumnScale); + + const Vec4V extentV = V4LoadXYZW(boxInHfShape.extents.x, boxInHfShape.extents.y, boxInHfShape.extents.z, PX_MAX_REAL); + const PxHeightFieldGeometry& geom = hfUtil.getHeightFieldGeometry(); + + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + PxU32 vertexIndex = row * hf.getNbColumnsFast() + column; + if(hfUtil.isQueryVertex(vertexIndex, row, column)) + { + // check if hf vertex is inside the box + const Vec4V hfVertex = V4LoadXYZW(geom.rowScale * row, geom.heightScale * hf.getHeight(vertexIndex), geom.columnScale * column, 0.0f); + const Vec4V hfVertexInBoxShape = Vec4V_From_Vec3V(_hfShape2BoxShape.transform(Vec3V_From_Vec4V(hfVertex))); + const Vec4V hfVertexInBoxShapeAbs = V4Abs(hfVertexInBoxShape); + + if(V4AllGrtr(extentV, hfVertexInBoxShapeAbs)) + { + return true; + } + } + } + } + } + return false; +} + +static Matrix34 multiplyInverseRTLeft(const Matrix34& left, const Matrix34& right) +{ +// t = left.M % (right.t - left.t); + PxVec3 t = left.rotateTranspose(right.p - left.p); + +// M.setMultiplyTransposeLeft(left.M, right.M); + const PxMat33& left33 = left.m; + const PxMat33& right33 = right.m; + PxMat33 multiplyTransposeLeft33 = (left33.getTranspose()) * right33; + + return Matrix34(multiplyTransposeLeft33, t); +} + +static bool intersectHeightFieldConvex( + const HeightFieldTraceUtil& hfUtil, const PxTransform& _hfAbsPose, const ConvexMesh& convexMesh, + const PxTransform& _convexAbsPose, const PxMeshScale& convexMeshScaling) +{ + const Matrix34 hfAbsPose34(_hfAbsPose); + const Matrix34 convexAbsPose34(_convexAbsPose); + const Matrix34 vertexToShapeSkew34(convexMeshScaling.toMat33()); + const Matrix34 temp34 = convexAbsPose34 * vertexToShapeSkew34; + const Matrix34 convexShape2HfShapeSkew34 = multiplyInverseRTLeft(hfAbsPose34, temp34); + + const ConvexHullData* hull = &convexMesh.getHull(); + + // Allocate space for transformed vertices. + PxVec3* convexVerticesInHfShape = reinterpret_cast(PxAlloca(hull->mNbHullVertices*sizeof(PxVec3))); + + // Transform vertices to height field shape + const PxVec3* hullVerts = hull->getHullVertices(); + for(PxU32 i=0; imNbHullVertices; i++) + convexVerticesInHfShape[i] = convexShape2HfShapeSkew34.transform(hullVerts[i]); + + // Compute bounds of convex in hf space + PxBounds3 convexBoundsInHfShape; + computeBoundsAroundVertices(convexBoundsInHfShape, hull->mNbHullVertices, convexVerticesInHfShape); + + // Compute the height field extreme over the bounds area. + const HeightField& hf = hfUtil.getHeightField(); + PxReal hfExtreme = -PX_MAX_REAL; + const PxReal oneOverRowScale = hfUtil.getOneOverRowScale(); + const PxReal oneOverColumnScale = hfUtil.getOneOverColumnScale(); + const PxReal rowScale = (1.0f / hfUtil.getOneOverRowScale()); + const PxReal columnScale = (1.0f / hfUtil.getOneOverColumnScale()); + const PxReal heightScale = (1.0f / hfUtil.getOneOverHeightScale()); + + // negative scale support + PxU32 minRow; + PxU32 maxRow; + if(oneOverRowScale > 0.0f) + { + minRow = hf.getMinRow(convexBoundsInHfShape.minimum.x * oneOverRowScale); + maxRow = hf.getMaxRow(convexBoundsInHfShape.maximum.x * oneOverRowScale); + } + else + { + minRow = hf.getMinRow(convexBoundsInHfShape.maximum.x * oneOverRowScale); + maxRow = hf.getMaxRow(convexBoundsInHfShape.minimum.x * oneOverRowScale); + } + + PxU32 minColumn; + PxU32 maxColumn; + if(oneOverColumnScale > 0.0f) + { + minColumn = hf.getMinColumn(convexBoundsInHfShape.minimum.z * oneOverColumnScale); + maxColumn = hf.getMaxColumn(convexBoundsInHfShape.maximum.z * oneOverColumnScale); + } + else + { + minColumn = hf.getMinColumn(convexBoundsInHfShape.maximum.z * oneOverColumnScale); + maxColumn = hf.getMaxColumn(convexBoundsInHfShape.minimum.z * oneOverColumnScale); + } + + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + const PxReal h = hf.getHeight(row * hf.getNbColumnsFast() + column); + hfExtreme = PxMax(hfExtreme, h); + } + } + hfExtreme *= heightScale; + + + // Return if convex is on the wrong side of the extreme. + if(convexBoundsInHfShape.minimum.y > hfExtreme) + return false; + + // Test convex vertices + { + for(PxU32 i=0; imNbHullVertices; i++) + { + const PxVec3& convexVertexInHfShape = convexVerticesInHfShape[i]; + bool insideExtreme = convexVertexInHfShape.y < hfExtreme; + if(insideExtreme && hfUtil.isShapePointOnHeightField(convexVertexInHfShape.x, convexVertexInHfShape.z)) + { + const PxReal y = hfUtil.getHeightAtShapePoint(convexVertexInHfShape.x, convexVertexInHfShape.z); + const PxReal dy = convexVertexInHfShape.y - y; + if(hf.isDeltaHeightInsideExtent(dy)) + { + const PxU32 faceIndex = hfUtil.getFaceIndexAtShapePoint(convexVertexInHfShape.x, convexVertexInHfShape.z); + if(faceIndex != 0xffffffff) + return true; + } + } + } + } + + // Test convex edges. + { + EdgeCache edgeCache; + PxU32 numPolygons = hull->mNbPolygons; + const HullPolygonData* polygons = hull->mPolygons; + const PxU8* const vertexData = hull->getVertexData8(); + + OverlapHeightfieldTraceSegmentHelper traceSegmentHelper(hfUtil); + + while(numPolygons--) + { + const HullPolygonData& polygon = *polygons++; + + const PxU8* verts = vertexData + polygon.mVRef8; + + PxU32 numEdges = polygon.mNbVerts; + + PxU32 a = numEdges - 1; + PxU32 b = 0; + while(numEdges--) + { + PxU8 vi0 = verts[a]; + PxU8 vi1 = verts[b]; + + if(vi1 < vi0) + { + PxU8 tmp = vi0; + vi0 = vi1; + vi1 = tmp; + } + + if(edgeCache.isInCache(vi0, vi1)) //avoid processing edges 2x if possible (this will typically have cache misses about 5% of the time leading to 5% redundant work. + continue; + + const PxVec3& sv0 = convexVerticesInHfShape[vi0]; + const PxVec3& sv1 = convexVerticesInHfShape[vi1]; + a = b; + b++; + + + if((sv0.y > hfExtreme) && (sv1.y > hfExtreme)) + continue; + + const PxVec3 v0 = sv0; + const PxVec3 v1 = sv1; + TriggerTraceSegmentCallback cb; + traceSegmentHelper.traceSegment(v0, v1, &cb); + if(cb.intersection) + return true; + } + } + } + + // Test HeightField vertices + { + const Matrix34 tmp34 = multiplyInverseRTLeft(convexAbsPose34, hfAbsPose34); + const Matrix34 hfShape2ConvexShapeSkew34 = vertexToShapeSkew34 * tmp34; + + for(PxU32 row = minRow; row <= maxRow; row++) + { + for(PxU32 column = minColumn; column <= maxColumn; column++) + { + const PxU32 hfVertexIndex = row * hf.getNbColumnsFast() + column; + if(hfUtil.isQueryVertex(hfVertexIndex, row, column)) + { + // Check if hf vertex is inside the convex + const PxVec3 hfVertex(rowScale * row, heightScale * hf.getHeight(hfVertexIndex), columnScale * column); + const PxVec3 hfVertexInConvexShape = hfShape2ConvexShapeSkew34.transform(hfVertex); + + bool inside = true; + for(PxU32 poly = 0; poly < hull->mNbPolygons; poly++) + { + PxReal d = hull->mPolygons[poly].mPlane.distance(hfVertexInConvexShape); + if(d >= 0) + { + inside = false; + break; + } + } + if(inside) + return true; + } + } + } + } + return false; +} + +bool Gu::checkOverlapAABB_heightFieldGeom(const PxGeometry& geom, const PxTransform& pose, const PxBounds3& box) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + const PxHeightFieldGeometry& hfGeom = static_cast(geom); + + const Matrix34 invAbsPose(pose.getInverse()); + + const Box boxInHfShape( + invAbsPose.transform(box.getCenter()), + box.getExtents(), + invAbsPose.m); + + HeightFieldTraceUtil hfUtil(hfGeom); + return intersectHeightFieldBox(hfUtil, boxInHfShape); +} + +bool GeomOverlapCallback_SphereHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast(geom1); + + const Sphere sphereInHf(pose1.transformInv(pose0.p), sphereGeom.radius); + + HeightFieldUtil hfUtil(hfGeom); + return intersectHeightFieldSphere(hfUtil, sphereInHf); +} + +bool GeomOverlapCallback_CapsuleHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast(geom1); + + const PxTransform capsuleShapeToHfShape = pose1.transformInv(pose0); + + const HeightFieldUtil hfUtil(hfGeom); + return intersectHeightFieldCapsule(hfUtil, capsuleGeom, capsuleShapeToHfShape); +} + +bool GeomOverlapCallback_BoxHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast(geom1); + + const PxTransform boxShape2HfShape = pose1.transformInv(pose0); + + Box box; + buildFrom(box, boxShape2HfShape.p, boxGeom.halfExtents, boxShape2HfShape.q); + + HeightFieldTraceUtil hfUtil(hfGeom); + return intersectHeightFieldBox(hfUtil, box); +} + +/////////////////////////////////////////////////////////////////////////////// +bool GeomOverlapCallback_ConvexHeightfield(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(cache); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + const PxHeightFieldGeometry& hfGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + + HeightFieldTraceUtil hfUtil(hfGeom); + return intersectHeightFieldConvex(hfUtil, pose1, *cm, pose0, convexGeom.scale); +} diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp new file mode 100644 index 000000000..32e400b0a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp @@ -0,0 +1,606 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTests.h" +#include "GuHeightFieldUtil.h" +#include "GuEntityReport.h" +#include "GuVecCapsule.h" +#include "GuSweepMTD.h" +#include "GuSweepTriangleUtils.h" +#include "GuVecBox.h" +#include "CmScaling.h" +#include "GuSweepCapsuleTriangle.h" +#include "GuInternal.h" +#include "GuGJKRaycast.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +#include "GuSweepConvexTri.h" + +#define AbortTraversal false +#define ContinueTraversal true + +/////////////////////////////////////////////////////////////////////////////// + +class HeightFieldTraceSegmentSweepHelper +{ + PX_NOCOPY(HeightFieldTraceSegmentSweepHelper) +public: + HeightFieldTraceSegmentSweepHelper(const HeightFieldTraceUtil& hfUtil, const PxVec3& aabbExtentHfLocalSpace) + : mHfUtil(hfUtil), mOverlapObjectExtent(aabbExtentHfLocalSpace) + { + mHfUtil.computeLocalBounds(mLocalBounds); + // extend the bounds + mLocalBounds.minimum = mLocalBounds.minimum - aabbExtentHfLocalSpace; + mLocalBounds.maximum = mLocalBounds.maximum + aabbExtentHfLocalSpace; + } + + template + PX_INLINE void traceSegment(const PxVec3& aP0, const PxVec3& rayDirNorm, const float rayLength, T* aCallback) const + { + mHfUtil.traceSegment(aP0, rayDirNorm, rayLength, aCallback, mLocalBounds, false, &mOverlapObjectExtent); + } + +private: + const HeightFieldTraceUtil& mHfUtil; + const PxVec3& mOverlapObjectExtent; + PxBounds3 mLocalBounds; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class HeightFieldTraceSegmentReport : public EntityReport +{ + PX_NOCOPY(HeightFieldTraceSegmentReport) +public: + + HeightFieldTraceSegmentReport(const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags) : + mHfUtil (hfUtil), + mHitFlags (hitFlags), + mStatus (false), + mInitialOverlap (false), + mIsDoubleSided ((hfUtil.getHeightFieldGeometry().heightFieldFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES)), + mIsAnyHit (hitFlags & PxHitFlag::eMESH_ANY) + { + } + + bool underFaceHit(const Gu::HeightFieldUtil&, const PxVec3&, const PxVec3&, PxF32, PxF32, PxF32, PxU32) + { + return true; + } + + bool faceHit(const Gu::HeightFieldUtil&, const PxVec3&, PxU32, PxReal, PxReal) + { + return true; + } + + protected: + const HeightFieldUtil& mHfUtil; + const PxHitFlags mHitFlags; + bool mStatus; + bool mInitialOverlap; + const bool mIsDoubleSided; + const bool mIsAnyHit; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class CapsuleTraceSegmentReport : public HeightFieldTraceSegmentReport +{ + PX_NOCOPY(CapsuleTraceSegmentReport) +public: + CapsuleTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags, + const Capsule& inflatedCapsule, + const PxVec3& unitDir, PxSweepHit& sweepHit, const PxTransform& pose, PxReal distance) : + HeightFieldTraceSegmentReport (hfUtil, hitFlags), + mInflatedCapsule (inflatedCapsule), + mUnitDir (unitDir), + mSweepHit (sweepHit), + mPose (pose), + mDistance (distance) + { + mSweepHit.faceIndex = 0xFFFFffff; + } + + virtual PxAgain onEvent(PxU32 nb, PxU32* indices) + { + PX_ALIGN_PREFIX(16) PxU8 tribuf[HF_SWEEP_REPORT_BUFFER_SIZE*sizeof(PxTriangle)] PX_ALIGN_SUFFIX(16); + PxTriangle* tmpT = reinterpret_cast(tribuf); + PX_ASSERT(nb <= HF_SWEEP_REPORT_BUFFER_SIZE); + for(PxU32 i=0; i(geom); + + const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); + + // Compute swept box + Box capsuleBox; + computeBoxAroundCapsule(inflatedCapsule, capsuleBox); + + const PxVec3 capsuleAABBExtents = capsuleBox.computeAABBExtent(); + + const HeightFieldTraceUtil hfUtil(hfGeom); + CapsuleTraceSegmentReport myReport(hfUtil, hitFlags, inflatedCapsule, unitDir, sweepHit, pose, distance); + + sweepHit.distance = PX_MAX_F32; + + // need hf local space stuff + const PxTransform inversePose = pose.getInverse(); + const PxVec3 centerLocalSpace = inversePose.transform(capsuleBox.center); + const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); + const PxVec3 capsuleAABBBExtentHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), capsuleAABBExtents).getExtents(); + + HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, capsuleAABBBExtentHfLocalSpace); + traceSegmentHelper.traceSegment(centerLocalSpace, sweepDirLocalSpace, distance, &myReport); + + return myReport.finalizeHit(sweepHit, hfGeom, pose, lss, inflatedCapsule, unitDir); +} + +/////////////////////////////////////////////////////////////////////////////// + +class ConvexTraceSegmentReport : public HeightFieldTraceSegmentReport +{ + PX_NOCOPY(ConvexTraceSegmentReport) +public: + ConvexTraceSegmentReport( const HeightFieldUtil& hfUtil, const ConvexHullData& hull, const PxMeshScale& convexScale, + const PxTransform& convexPose, const PxTransform& heightFieldPose, + const PxVec3& unitDir, PxReal distance, PxHitFlags hitFlags, PxReal inflation) : + HeightFieldTraceSegmentReport (hfUtil, hitFlags), + mUnitDir (unitDir), + mInflation (inflation) + { + using namespace Ps::aos; + mSweepHit.faceIndex = 0xFFFFffff; + mSweepHit.distance = distance; + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const QuatV q0 = QuatVLoadU(&heightFieldPose.q.x); + const Vec3V p0 = V3LoadU(&heightFieldPose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV meshTransf(p0, q0); + const PsTransformV convexTransf(p1, q1); + + mMeshToConvex = convexTransf.transformInv(meshTransf); + mConvexPoseV = convexTransf; + mConvexSpaceDir = convexTransf.rotateInv(V3Neg(V3Scale(worldDir, dist))); + mDistance = dist; + + const Vec3V vScale = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexScale.rotation.x); + + mMeshSpaceUnitDir = heightFieldPose.rotateInv(unitDir); + mConvexHull.initialize(&hull, V3Zero(), vScale, vQuat, convexScale.isIdentity()); + } + + virtual PxAgain onEvent(PxU32 nbEntities, PxU32* entities) + { + const PxTransform idt(PxIdentity); + for(PxU32 i=0; i(geom); + + const Matrix34 convexTM(convexPose); + const Matrix34 meshTM(pose); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + + FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + PX_ASSERT(!convexMesh->getLocalBoundsFast().isEmpty()); + const PxBounds3 hullAABBLocalSpace = convexMesh->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew()); + + const HeightFieldTraceUtil hfUtil(hfGeom); + ConvexTraceSegmentReport entityReport( + hfUtil, convexMesh->getHull(), convexGeom.scale, convexPose, pose, -unitDir, distance, hitFlags, inflation); + + // need hf local space stuff + const PxBounds3 hullAABB = PxBounds3::transformFast(convexPose, hullAABBLocalSpace); + const PxVec3 aabbExtents = hullAABB.getExtents() + PxVec3(inflation); + const PxTransform inversePose = pose.getInverse(); + const PxVec3 centerLocalSpace = inversePose.transform(hullAABB.getCenter()); + const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); + const PxVec3 convexAABBExtentHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), aabbExtents).getExtents(); + + HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, convexAABBExtentHfLocalSpace); + traceSegmentHelper.traceSegment(centerLocalSpace, sweepDirLocalSpace, distance, &entityReport); + + return entityReport.finalizeHit(sweepHit, hfGeom, pose, convexGeom, convexPose, unitDir, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +class BoxTraceSegmentReport : public HeightFieldTraceSegmentReport +{ + PX_NOCOPY(BoxTraceSegmentReport) +public: + BoxTraceSegmentReport( const HeightFieldUtil& hfUtil, const PxHitFlags hitFlags, + const PsTransformV& worldToBoxV, const PxTransform& pose, const BoxV& box, const PxVec3& localMotion, + PxSweepHit& sweepHit, PxReal inflation) : + HeightFieldTraceSegmentReport (hfUtil, hitFlags), + mWorldToBoxV (worldToBoxV), + mPose (pose), + mBox (box), + mLocalMotion (localMotion), + mSweepHit (sweepHit), + mInflation (inflation) + { + mMinToi = FMax(); + mSweepHit.faceIndex = 0xFFFFffff; + } + + virtual PxAgain onEvent(PxU32 nb, PxU32* indices) + { + const FloatV zero = FZero(); + const Vec3V zeroV = V3Zero(); + const Vec3V dir = V3LoadU(mLocalMotion); + //FloatV minToi = FMax(); + FloatV toi; + Vec3V closestA, normal;//closestA and normal is in the local space of box + + for(PxU32 i=0; i convexA(triangle); + const LocalConvex convexB(mBox); + const Vec3V initialSearchDir = V3Sub(triangle.getCenter(), mBox.getCenter()); + + if(gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, dir, toi, normal, closestA, mInflation, false)) + { + mStatus = true; + if(FAllGrtr(toi, zero)) + { + if(FAllGrtr(mMinToi, toi)) + { + mMinToi = toi; + FStore(toi, &mSweepHit.distance); + V3StoreU(normal, mSweepHit.normal); + V3StoreU(closestA, mSweepHit.position); + mSweepHit.faceIndex = triangleIndex; + + if(mIsAnyHit) + return AbortTraversal; + } + } + else + { + mSweepHit.distance = 0.0f; + mSweepHit.faceIndex = triangleIndex; + mInitialOverlap = true; + return AbortTraversal; + } + } + } + return ContinueTraversal; + } + + bool finalizeHit(PxSweepHit& sweepHit, + const PxHeightFieldGeometry& hfGeom, const PxTransform& pose, + const PxTransform& boxPose_, const Box& box, + const PxVec3& unitDir, PxReal distance, PxReal inflation) + { + if(!mStatus) + return false; + + if(mInitialOverlap) + { + // PT: TODO: consider using 'setInitialOverlapResults' here + + sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + + if(mHitFlags & PxHitFlag::eMTD) + { + const bool hasContacts = computeBox_HeightFieldMTD(hfGeom, pose, box, boxPose_, inflation, mIsDoubleSided, GuHfQueryFlags::eWORLD_SPACE, sweepHit); + + //ML: the center of mass is below the surface, we won't have MTD contact generate + if(!hasContacts) + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + else + { + sweepHit.flags |= PxHitFlag::ePOSITION; + } + } + else + { + sweepHit.distance = 0.0f; + sweepHit.normal = -unitDir; + } + } + else + { + PxVec3 n = sweepHit.normal.getNormalized(); + if((n.dot(mLocalMotion))>0.0f) + n = -n; + + sweepHit.distance *= distance; // stored as toi [0,1] during computation -> scale + sweepHit.normal = boxPose_.rotate(n); + sweepHit.position = boxPose_.transform(sweepHit.position); + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + } + return true; + } + + private: + const PsTransformV& mWorldToBoxV; + const PxTransform& mPose; + const BoxV& mBox; + FloatV mMinToi; + const PxVec3 mLocalMotion; + PxSweepHit& mSweepHit; + const PxReal mInflation; +}; + +#if PX_VC + #pragma warning(pop) +#endif + +bool sweepBox_HeightFieldGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eHEIGHTFIELD); + PX_UNUSED(boxGeom_); + PX_UNUSED(hitFlags); + + const PxHeightFieldGeometry& hfGeom = static_cast(geom); + + const PxVec3 boxAABBExtent = box.computeAABBExtent() + PxVec3(inflation); + + // Move to AABB space + PX_ALIGN_PREFIX(16) PxTransform WorldToBox PX_ALIGN_SUFFIX(16); + WorldToBox = boxPose_.getInverse(); + + const QuatV q1 = QuatVLoadA(&WorldToBox.q.x); + const Vec3V p1 = V3LoadA(&WorldToBox.p.x); + const PsTransformV WorldToBoxV(p1, q1); + + const PxVec3 motion = unitDir * distance; + const PxVec3 localMotion = WorldToBox.rotate(motion); + + const BoxV boxV(V3Zero(), V3LoadU(box.extents)); + + sweepHit.distance = PX_MAX_F32; + + const HeightFieldTraceUtil hfUtil(hfGeom); + BoxTraceSegmentReport myReport(hfUtil, hitFlags, WorldToBoxV, pose, boxV, localMotion, sweepHit, inflation); + + // need hf local space stuff + const PxTransform inversePose = pose.getInverse(); + const PxVec3 centerLocalSpace = inversePose.transform(box.center); + const PxVec3 sweepDirLocalSpace = inversePose.rotate(unitDir); + const PxVec3 boxAABBExtentInHfLocalSpace = PxBounds3::basisExtent(centerLocalSpace, PxMat33Padded(inversePose.q), boxAABBExtent).getExtents(); + + HeightFieldTraceSegmentSweepHelper traceSegmentHelper(hfUtil, boxAABBExtentInHfLocalSpace); + traceSegmentHelper.traceSegment(centerLocalSpace, sweepDirLocalSpace, distance, &myReport); + + return myReport.finalizeHit(sweepHit, hfGeom, pose, boxPose_, box, unitDir, distance, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp new file mode 100644 index 000000000..120a81c42 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp @@ -0,0 +1,139 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionBoxBox.h" + +using namespace physx; + +bool Gu::intersectOBBOBB(const PxVec3& e0, const PxVec3& c0, const PxMat33& r0, const PxVec3& e1, const PxVec3& c1, const PxMat33& r1, bool full_test) +{ + // Translation, in parent frame + const PxVec3 v = c1 - c0; + // Translation, in A's frame + const PxVec3 T(v.dot(r0[0]), v.dot(r0[1]), v.dot(r0[2])); + + // B's basis with respect to A's local frame + PxReal R[3][3]; + PxReal FR[3][3]; + PxReal ra, rb, t; + + // Calculate rotation matrix + for(PxU32 i=0;i<3;i++) + { + for(PxU32 k=0;k<3;k++) + { + R[i][k] = r0[i].dot(r1[k]); + FR[i][k] = 1e-6f + PxAbs(R[i][k]); // Precompute fabs matrix + } + } + + // A's basis vectors + for(PxU32 i=0;i<3;i++) + { + ra = e0[i]; + + rb = e1[0]*FR[i][0] + e1[1]*FR[i][1] + e1[2]*FR[i][2]; + + t = PxAbs(T[i]); + + if(t > ra + rb) return false; + } + + // B's basis vectors + for(PxU32 k=0;k<3;k++) + { + ra = e0[0]*FR[0][k] + e0[1]*FR[1][k] + e0[2]*FR[2][k]; + + rb = e1[k]; + + t = PxAbs(T[0]*R[0][k] + T[1]*R[1][k] + T[2]*R[2][k]); + + if( t > ra + rb ) return false; + } + + if(full_test) + { + //9 cross products + + //L = A0 x B0 + ra = e0[1]*FR[2][0] + e0[2]*FR[1][0]; + rb = e1[1]*FR[0][2] + e1[2]*FR[0][1]; + t = PxAbs(T[2]*R[1][0] - T[1]*R[2][0]); + if(t > ra + rb) return false; + + //L = A0 x B1 + ra = e0[1]*FR[2][1] + e0[2]*FR[1][1]; + rb = e1[0]*FR[0][2] + e1[2]*FR[0][0]; + t = PxAbs(T[2]*R[1][1] - T[1]*R[2][1]); + if(t > ra + rb) return false; + + //L = A0 x B2 + ra = e0[1]*FR[2][2] + e0[2]*FR[1][2]; + rb = e1[0]*FR[0][1] + e1[1]*FR[0][0]; + t = PxAbs(T[2]*R[1][2] - T[1]*R[2][2]); + if(t > ra + rb) return false; + + //L = A1 x B0 + ra = e0[0]*FR[2][0] + e0[2]*FR[0][0]; + rb = e1[1]*FR[1][2] + e1[2]*FR[1][1]; + t = PxAbs(T[0]*R[2][0] - T[2]*R[0][0]); + if(t > ra + rb) return false; + + //L = A1 x B1 + ra = e0[0]*FR[2][1] + e0[2]*FR[0][1]; + rb = e1[0]*FR[1][2] + e1[2]*FR[1][0]; + t = PxAbs(T[0]*R[2][1] - T[2]*R[0][1]); + if(t > ra + rb) return false; + + //L = A1 x B2 + ra = e0[0]*FR[2][2] + e0[2]*FR[0][2]; + rb = e1[0]*FR[1][1] + e1[1]*FR[1][0]; + t = PxAbs(T[0]*R[2][2] - T[2]*R[0][2]); + if(t > ra + rb) return false; + + //L = A2 x B0 + ra = e0[0]*FR[1][0] + e0[1]*FR[0][0]; + rb = e1[1]*FR[2][2] + e1[2]*FR[2][1]; + t = PxAbs(T[1]*R[0][0] - T[0]*R[1][0]); + if(t > ra + rb) return false; + + //L = A2 x B1 + ra = e0[0]*FR[1][1] + e0[1]*FR[0][1]; + rb = e1[0] *FR[2][2] + e1[2]*FR[2][0]; + t = PxAbs(T[1]*R[0][1] - T[0]*R[1][1]); + if(t > ra + rb) return false; + + //L = A2 x B2 + ra = e0[0]*FR[1][2] + e0[1]*FR[0][2]; + rb = e1[0]*FR[2][1] + e1[1]*FR[2][0]; + t = PxAbs(T[1]*R[0][2] - T[0]*R[1][2]); + if(t > ra + rb) return false; + } + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp new file mode 100644 index 000000000..2cbb0eb5c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp @@ -0,0 +1,61 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionCapsuleTriangle.h" +#include "GuDistancePointSegment.h" + +using namespace physx; +using namespace Gu; + +bool Gu::intersectCapsuleTriangle(const PxVec3& N, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const Gu::Capsule& capsule, const CapsuleTriangleOverlapData& params) +{ + PX_ASSERT(capsule.p0!=capsule.p1); + + { + const PxReal d2 = distancePointSegmentSquaredInternal(capsule.p0, params.mCapsuleDir, p0); + if(d2<=capsule.radius*capsule.radius) + return true; + } + +// const PxVec3 N = (p0 - p1).cross(p0 - p2); + + if(!testAxis(p0, p1, p2, capsule, N)) + return false; + + if(!testAxis(p0, p1, p2, capsule, computeEdgeAxis(p0, p1 - p0, capsule.p0, params.mCapsuleDir, params.mBDotB, params.mOneOverBDotB))) + return false; + + if(!testAxis(p0, p1, p2, capsule, computeEdgeAxis(p1, p2 - p1, capsule.p0, params.mCapsuleDir, params.mBDotB, params.mOneOverBDotB))) + return false; + + if(!testAxis(p0, p1, p2, capsule, computeEdgeAxis(p2, p0 - p2, capsule.p0, params.mCapsuleDir, params.mBDotB, params.mOneOverBDotB))) + return false; + + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h new file mode 100644 index 000000000..72c67d6ba --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h @@ -0,0 +1,137 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_CAPSULE_TRIANGLE_H +#define GU_INTERSECTION_CAPSULE_TRIANGLE_H + +#include "CmPhysXCommon.h" +#include "GuCapsule.h" +#include "PsUtilities.h" + +namespace physx +{ +namespace Gu +{ + // PT: precomputed data for capsule-triangle test. Useful when testing the same capsule vs several triangles. + struct CapsuleTriangleOverlapData + { + PxVec3 mCapsuleDir; + float mBDotB; + float mOneOverBDotB; + + void init(const Capsule& capsule) + { + const PxVec3 dir = capsule.p1 - capsule.p0; + const float BDotB = dir.dot(dir); + mCapsuleDir = dir; + mBDotB = BDotB; + mOneOverBDotB = BDotB!=0.0f ? 1.0f/BDotB : 0.0f; + } + }; + + // PT: tests if projections of capsule & triangle overlap on given axis + PX_FORCE_INLINE PxU32 testAxis(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const Capsule& capsule, const PxVec3& axis) + { + // Project capsule + float min0 = capsule.p0.dot(axis); + float max0 = capsule.p1.dot(axis); + if(min0>max0) + Ps::swap(min0, max0); + const float MR = axis.magnitude()*capsule.radius; + min0 -= MR; + max0 += MR; + + // Project triangle + float min1, max1; + { + min1 = max1 = p0.dot(axis); + float dp = p1.dot(axis); + if(dpmax1) max1 = dp; + dp = p2.dot(axis); + if(dpmax1) max1 = dp; + } + + // Test projections + if(max01.0f) + { + u = 1.0f; + t = (ADotB + ADotT) / ADotA; + t = PxClamp(t, 0.0f, 1.0f); + } + return T + b*u - a*t; + } + + /** + * Checks if a capsule intersects a triangle. + * + * \param normal [in] triangle normal (orientation does not matter) + * \param p0 [in] triangle's first point + * \param p1 [in] triangle's second point + * \param p2 [in] triangle's third point + * \param capsule [in] capsule + * \param params [in] precomputed capsule params + * \return true if capsule overlaps triangle + */ + bool intersectCapsuleTriangle(const PxVec3& normal, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const Gu::Capsule& capsule, const CapsuleTriangleOverlapData& params); +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp new file mode 100644 index 000000000..4a5be9d0e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp @@ -0,0 +1,84 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionEdgeEdge.h" +#include "PsMathUtils.h" +#include "CmPhysXCommon.h" + +using namespace physx; + +bool Gu::intersectEdgeEdge(const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip) +{ + const PxVec3 v1 = p2 - p1; + + // Build plane P based on edge (p1, p2) and direction (dir) + PxPlane plane; + plane.n = v1.cross(dir); + plane.d = -(plane.n.dot(p1)); + + // if colliding edge (p3,p4) does not cross plane return no collision + // same as if p3 and p4 on same side of plane return 0 + // + // Derivation: + // d3 = d(p3, P) = (p3 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // d4 = d(p4, P) = (p4 | plane.n) - plane.d; Reversed sign compared to Plane::Distance() because plane.d is negated. + // if d3 and d4 have the same sign, they're on the same side of the plane => no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + PxVec3 v2 = p4 - p3; + + temp = plane.n.dot(v2); + if(temp==0.0f) return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp); + + // find largest 2D plane projection + PxU32 i,j; + Ps::closestAxis(plane.n, i, j); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i]))/(v1[i]*dir[j]-v1[j]*dir[i]); + if(dist<0.0f) return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + temp = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + if(temp<1e-3f) return true; // collision found + + return false; // no collision +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h new file mode 100644 index 000000000..d9a4cd7c6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h @@ -0,0 +1,52 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_EDGE_EDGE_H +#define GU_INTERSECTION_EDGE_EDGE_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + + // collide edge (p1,p2) moving in direction (dir) colliding + // width edge (p3,p4). Return true on a collision with + // collision distance (dist) and intersection point (ip) + // note: dist and ip are invalid if function returns false. + // note: ip is on (p1,p2), not (p1+dist*dir,p2+dist*dir) + PX_PHYSX_COMMON_API bool intersectEdgeEdge(const PxVec3& p1, const PxVec3& p2, const PxVec3& dir, const PxVec3& p3, const PxVec3& p4, PxReal& dist, PxVec3& ip); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h new file mode 100644 index 000000000..2e120457e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h @@ -0,0 +1,38 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_H +#define GU_INTERSECTION_RAY_H + +// PT: small distance between a ray origin and a potentially hit surface. Should be small enough to +// limit accuracy issues coming from large distance values, but not too close to the surface to make +// sure we don't start inside the shape. +#define GU_RAY_SURFACE_OFFSET 10.0f + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp new file mode 100644 index 000000000..3b6c3fe95 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp @@ -0,0 +1,449 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec3.h" +#include "foundation/PxIntrinsics.h" +#include "PsFPU.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionRayBoxSIMD.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Computes a ray-AABB intersection. +* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 +* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) +* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) +* +* Hence this version is faster as well as more robust than the original one. +* +* Should work provided: +* 1) the integer representation of 0.0f is 0x00000000 +* 2) the sign bit of the float is the most significant one +* +* Report bugs: p.terdiman@codercorner.com +* +* \param aabb [in] the axis-aligned bounding box +* \param origin [in] ray origin +* \param dir [in] ray direction +* \param coord [out] impact coordinates +* \return true if ray intersects AABB +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define RAYAABB_EPSILON 0.00001f +bool Gu::rayAABBIntersect(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& origin, const PxVec3& _dir, PxVec3& coord) +{ + Ps::IntBool Inside = Ps::IntTrue; + PxVec3 MaxT(-1.0f, -1.0f, -1.0f); + const PxReal* dir = &_dir.x; + const PxU32* idir = reinterpret_cast(dir); + // Find candidate planes. + for(PxU32 i=0;i<3;i++) + { + if(origin[i] < minimum[i]) + { + coord[i] = minimum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (minimum[i] - origin[i]) / dir[i]; + } + else if(origin[i] > maximum[i]) + { + coord[i] = maximum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (maximum[i] - origin[i]) / dir[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord = origin; + return true; + } + + // Get largest of the maxT's for final choice of intersection + PxU32 WhichPlane = 0; + if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; + if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + const PxU32* tmp = reinterpret_cast(&MaxT[WhichPlane]); + if((*tmp)&PX_SIGN_BITMASK) +// if(PX_IR(MaxT[WhichPlane])&PX_SIGN_BITMASK) + return false; + + for(PxU32 i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord[i] = origin[i] + MaxT[WhichPlane] * dir[i]; +#ifdef RAYAABB_EPSILON + if(coord[i] < minimum[i] - RAYAABB_EPSILON || coord[i] > maximum[i] + RAYAABB_EPSILON) +#else + if(coord[i] < minimum[i] || coord[i] > maximum[i]) +#endif + return false; + } + } + return true; // ray hits box +} + + + +/** +* Computes a ray-AABB intersection. +* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 +* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) +* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) +* Return of intersected face code and parameter by Adam! Also modified behavior for ray starts inside AABB. 2004 :-p +* +* Hence this version is faster as well as more robust than the original one. +* +* Should work provided: +* 1) the integer representation of 0.0f is 0x00000000 +* 2) the sign bit of the float is the most significant one +* +* Report bugs: p.terdiman@codercorner.com +* +* \param minimum [in] the smaller corner of the bounding box +* \param maximum [in] the larger corner of the bounding box +* \param origin [in] ray origin +* \param _dir [in] ray direction +* \param coord [out] impact coordinates +* \param t [out] t such that coord = origin + dir * t +* \return false if ray does not intersect AABB, or ray origin is inside AABB. Else: + 1 + coordinate index of box axis that was hit + + Note: sign bit that determines if the minimum (0) or maximum (1) of the axis was hit is equal to sign(coord[returnVal-1]). +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PxU32 Gu::rayAABBIntersect2(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& origin, const PxVec3& _dir, PxVec3& coord, PxReal & t) +{ + Ps::IntBool Inside = Ps::IntTrue; + PxVec3 MaxT(-1.0f, -1.0f, -1.0f); + const PxReal* dir = &_dir.x; + const PxU32* idir = reinterpret_cast(dir); + // Find candidate planes. + for(PxU32 i=0;i<3;i++) + { + if(origin[i] < minimum[i]) + { + coord[i] = minimum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (minimum[i] - origin[i]) / dir[i]; + } + else if(origin[i] > maximum[i]) + { + coord[i] = maximum[i]; + Inside = Ps::IntFalse; + + // Calculate T distances to candidate planes + if(idir[i]) +// if(PX_IR(dir[i])) + MaxT[i] = (maximum[i] - origin[i]) / dir[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord = origin; + t = 0; + return 1; + } + + // Get largest of the maxT's for final choice of intersection + PxU32 WhichPlane = 0; + if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; + if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + const PxU32* tmp = reinterpret_cast(&MaxT[WhichPlane]); + if((*tmp)&PX_SIGN_BITMASK) +// if(PX_IR(MaxT[WhichPlane])&PX_SIGN_BITMASK) + return 0; + + for(PxU32 i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord[i] = origin[i] + MaxT[WhichPlane] * dir[i]; +#ifdef RAYAABB_EPSILON + if(coord[i] < minimum[i] - RAYAABB_EPSILON || coord[i] > maximum[i] + RAYAABB_EPSILON) return 0; +#else + if(coord[i] < minimum[i] || coord[i] > maximum[i]) return 0; +#endif + } + } + t = MaxT[WhichPlane]; + return 1 + WhichPlane; // ray hits box +} + +// Collide ray defined by ray origin (ro) and ray direction (rd) +// with the bounding box. Returns -1 on no collision and the face index +// for first intersection if a collision is found together with +// the distance to the collision points (tnear and tfar) + +// ptchernev: +// Should we use an enum, or should we keep the anonymous ints? +// Should we increment the return code by one (return 0 for non intersection)? + +int Gu::intersectRayAABB(const PxVec3& minimum, const PxVec3& maximum, const PxVec3& ro, const PxVec3& rd, float& tnear, float& tfar) +{ + // Refactor + int ret=-1; + + tnear = -PX_MAX_F32; + tfar = PX_MAX_F32; + // PT: why did we change the initial epsilon value? + #define LOCAL_EPSILON PX_EPS_F32 + //#define LOCAL_EPSILON 0.0001f + + for(unsigned int a=0;a<3;a++) + { + if(rd[a]>-LOCAL_EPSILON && rd[a]maximum[a]) + return -1; + } + else + { + const PxReal OneOverDir = 1.0f / rd[a]; + PxReal t1 = (minimum[a]-ro[a]) * OneOverDir; + PxReal t2 = (maximum[a]-ro[a]) * OneOverDir; + + unsigned int b = a; + if(t1>t2) + { + PxReal t=t1; + t1=t2; + t2=t; + b += 3; + } + + if(t1>tnear) + { + tnear = t1; + ret = int(b); + } + if(t2tfar || tfartfar || tfar-LOCAL_EPSILON && rd.xmaximum.x) + return -1; + if(physx::intrinsics::abs(rd.y)-LOCAL_EPSILON && rd.ymaximum.y) + return -1; + if(physx::intrinsics::abs(rd.z)-LOCAL_EPSILON && rd.zmaximum.z) + return -1; + + PxReal t1x = (minimum.x - ro.x) * oneOverDir.x; + PxReal t2x = (maximum.x - ro.x) * oneOverDir.x; + PxReal t1y = (minimum.y - ro.y) * oneOverDir.y; + PxReal t2y = (maximum.y - ro.y) * oneOverDir.y; + PxReal t1z = (minimum.z - ro.z) * oneOverDir.z; + PxReal t2z = (maximum.z - ro.z) * oneOverDir.z; + + int bx; + int by; + int bz; + + if(t1x>t2x) + { + PxReal t=t1x; t1x=t2x; t2x=t; + bx = 3; + } + else + { + bx = 0; + } + + if(t1y>t2y) + { + PxReal t=t1y; t1y=t2y; t2y=t; + by = 4; + } + else + { + by = 1; + } + + if(t1z>t2z) + { + PxReal t=t1z; t1z=t2z; t2z=t; + bz = 5; + } + else + { + bz = 2; + } + + int ret; +// if(t1x>tnear) // PT: no need to test for the first value + { + tnear = t1x; + ret = bx; + } +// tfar = PxMin(tfar, t2x); + tfar = t2x; // PT: no need to test for the first value + + if(t1y>tnear) + { + tnear = t1y; + ret = by; + } + tfar = PxMin(tfar, t2y); + + if(t1z>tnear) + { + tnear = t1z; + ret = bz; + } + tfar = PxMin(tfar, t2z); + + if(tnear>tfar || tfar= GU_MIN_AABB_EXTENT*0.5f); + PX_ASSERT(maximum.y-minimum.y >= GU_MIN_AABB_EXTENT*0.5f); + PX_ASSERT(maximum.z-minimum.z >= GU_MIN_AABB_EXTENT*0.5f); + // not using vector math due to vector to integer pipeline penalties. TODO: verify that it's indeed faster + namespace i = physx::intrinsics; + + // P+tD=a; t=(a-P)/D + // t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x) + const PxF32 dEpsilon = 1e-9f; + // using recipFast fails height field unit tests case where a ray cast from y=10000 to 0 gets clipped to 0.27 in y + PxF32 invDx = i::recip(i::selectMax(i::abs(rd.x), dEpsilon) * i::sign(rd.x)); +#ifdef RAYAABB_EPSILON + PxF32 tx0 = (minimum.x - RAYAABB_EPSILON - ro.x) * invDx; + PxF32 tx1 = (maximum.x + RAYAABB_EPSILON - ro.x) * invDx; +#else + PxF32 tx0 = (minimum.x - ro.x) * invDx; + PxF32 tx1 = (maximum.x - ro.x) * invDx; +#endif + PxF32 txMin = i::selectMin(tx0, tx1); + PxF32 txMax = i::selectMax(tx0, tx1); + + PxF32 invDy = i::recip(i::selectMax(i::abs(rd.y), dEpsilon) * i::sign(rd.y)); +#ifdef RAYAABB_EPSILON + PxF32 ty0 = (minimum.y - RAYAABB_EPSILON - ro.y) * invDy; + PxF32 ty1 = (maximum.y + RAYAABB_EPSILON - ro.y) * invDy; +#else + PxF32 ty0 = (minimum.y - ro.y) * invDy; + PxF32 ty1 = (maximum.y - ro.y) * invDy; +#endif + PxF32 tyMin = i::selectMin(ty0, ty1); + PxF32 tyMax = i::selectMax(ty0, ty1); + + PxF32 invDz = i::recip(i::selectMax(i::abs(rd.z), dEpsilon) * i::sign(rd.z)); +#ifdef RAYAABB_EPSILON + PxF32 tz0 = (minimum.z - RAYAABB_EPSILON - ro.z) * invDz; + PxF32 tz1 = (maximum.z + RAYAABB_EPSILON - ro.z) * invDz; +#else + PxF32 tz0 = (minimum.z - ro.z) * invDz; + PxF32 tz1 = (maximum.z - ro.z) * invDz; +#endif + PxF32 tzMin = i::selectMin(tz0, tz1); + PxF32 tzMax = i::selectMax(tz0, tz1); + + PxF32 maxOfNears = i::selectMax(i::selectMax(txMin, tyMin), tzMin); + PxF32 minOfFars = i::selectMin(i::selectMin(txMax, tyMax), tzMax); + + tnear = i::selectMax(maxOfNears, 0.0f); + tfar = i::selectMin(minOfFars, maxDist); + + return (tnear 0.0f) + { + PxReal fInvLength; + if ( PxAbs(kW.x) >= PxAbs(kW.y) ) + { + // W.x or W.z is the largest magnitude component, swap them + fInvLength = PxRecipSqrt(kW.x*kW.x + kW.z*kW.z); + kU.x = -kW.z*fInvLength; + kU.y = 0.0f; + kU.z = kW.x*fInvLength; + } + else + { + // W.y or W.z is the largest magnitude component, swap them + fInvLength = PxRecipSqrt(kW.y*kW.y + kW.z*kW.z); + kU.x = 0.0f; + kU.y = kW.z*fInvLength; + kU.z = -kW.y*fInvLength; + } + } + + PxVec3 kV = kW.cross(kU); + kV.normalize(); // PT: fixed november, 24, 2004. This is a bug in Magic. + + // compute intersection + + PxVec3 kD(kU.dot(dir), kV.dot(dir), kW.dot(dir)); + const float fDLength = kD.magnitude(); + const float fInvDLength = fDLength!=0.0f ? 1.0f/fDLength : 0.0f; + kD *= fInvDLength; + + const PxVec3 kDiff = origin - p0; + const PxVec3 kP(kU.dot(kDiff), kV.dot(kDiff), kW.dot(kDiff)); + const PxReal fRadiusSqr = radius*radius; + + // Is the velocity parallel to the capsule direction? (or zero) + if ( PxAbs(kD.z) >= 1.0f - PX_EPS_REAL || fDLength < PX_EPS_REAL ) + { + const float fAxisDir = dir.dot(kW); + + const PxReal fDiscr = fRadiusSqr - kP.x*kP.x - kP.y*kP.y; + if ( fAxisDir < 0 && fDiscr >= 0.0f ) + { + // Velocity anti-parallel to the capsule direction + const PxReal fRoot = PxSqrt(fDiscr); + s[0] = (kP.z + fRoot)*fInvDLength; + s[1] = -(fWLength - kP.z + fRoot)*fInvDLength; + return 2; + } + else if ( fAxisDir > 0 && fDiscr >= 0.0f ) + { + // Velocity parallel to the capsule direction + const PxReal fRoot = PxSqrt(fDiscr); + s[0] = -(kP.z + fRoot)*fInvDLength; + s[1] = (fWLength - kP.z + fRoot)*fInvDLength; + return 2; + } + else + { + // sphere heading wrong direction, or no velocity at all + return 0; + } + } + + // test intersection with infinite cylinder + PxReal fA = kD.x*kD.x + kD.y*kD.y; + PxReal fB = kP.x*kD.x + kP.y*kD.y; + PxReal fC = kP.x*kP.x + kP.y*kP.y - fRadiusSqr; + PxReal fDiscr = fB*fB - fA*fC; + if ( fDiscr < 0.0f ) + { + // line does not intersect infinite cylinder + return 0; + } + + PxU32 iQuantity = 0; + + if ( fDiscr > 0.0f ) + { + // line intersects infinite cylinder in two places + const PxReal fRoot = PxSqrt(fDiscr); + const PxReal fInv = 1.0f/fA; + PxReal fT = (-fB - fRoot)*fInv; + PxReal fTmp = kP.z + fT*kD.z; + const float epsilon = 1e-3f; // PT: see TA35174 + if ( fTmp >= -epsilon && fTmp <= fWLength+epsilon ) + s[iQuantity++] = fT*fInvDLength; + + fT = (-fB + fRoot)*fInv; + fTmp = kP.z + fT*kD.z; + if ( fTmp >= -epsilon && fTmp <= fWLength+epsilon ) + s[iQuantity++] = fT*fInvDLength; + + if ( iQuantity == 2 ) + { + // line intersects capsule wall in two places + return 2; + } + } + else + { + // line is tangent to infinite cylinder + const PxReal fT = -fB/fA; + const PxReal fTmp = kP.z + fT*kD.z; + if ( 0.0f <= fTmp && fTmp <= fWLength ) + { + s[0] = fT*fInvDLength; + return 1; + } + } + + // test intersection with bottom hemisphere + // fA = 1 + fB += kP.z*kD.z; + fC += kP.z*kP.z; + fDiscr = fB*fB - fC; + if ( fDiscr > 0.0f ) + { + const PxReal fRoot = PxSqrt(fDiscr); + PxReal fT = -fB - fRoot; + PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp <= 0.0f ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + + fT = -fB + fRoot; + fTmp = kP.z + fT*kD.z; + if ( fTmp <= 0.0f ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + else if ( fDiscr == 0.0f ) + { + const PxReal fT = -fB; + const PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp <= 0.0f ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + + // test intersection with top hemisphere + // fA = 1 + fB -= kD.z*fWLength; + fC += fWLength*(fWLength - 2.0f*kP.z); + + fDiscr = fB*fB - fC; + if ( fDiscr > 0.0f ) + { + const PxReal fRoot = PxSqrt(fDiscr); + PxReal fT = -fB - fRoot; + PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp >= fWLength ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + + fT = -fB + fRoot; + fTmp = kP.z + fT*kD.z; + if ( fTmp >= fWLength ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + else if ( fDiscr == 0.0f ) + { + const PxReal fT = -fB; + const PxReal fTmp = kP.z + fT*kD.z; + if ( fTmp >= fWLength ) + { + s[iQuantity++] = fT*fInvDLength; + if ( iQuantity == 2 ) + return 2; + } + } + return iQuantity; +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h new file mode 100644 index 000000000..bac7c0b52 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_CAPSULE_H +#define GU_INTERSECTION_RAY_CAPSULE_H + +#include "CmPhysXCommon.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuIntersectionRay.h" + +namespace physx +{ +namespace Gu +{ + PxU32 intersectRayCapsuleInternal(const PxVec3& origin, const PxVec3& dir, const PxVec3& p0, const PxVec3& p1, float radius, PxReal s[2]); + + PX_FORCE_INLINE bool intersectRayCapsule(const PxVec3& origin, const PxVec3& dir, const PxVec3& p0, const PxVec3& p1, float radius, PxReal& t) + { + // PT: move ray origin close to capsule, to solve accuracy issues. + // We compute the distance D between the ray origin and the capsule's segment. + // Then E = D - radius = distance between the ray origin and the capsule. + // We can move the origin freely along 'dir' up to E units before touching the capsule. + PxReal l = distancePointSegmentSquaredInternal(p0, p1 - p0, origin); + l = PxSqrt(l) - radius; + + // PT: if this becomes negative or null, the ray starts inside the capsule and we can early exit + if(l<=0.0f) + { + t = 0.0f; + return true; + } + + // PT: we remove an arbitrary GU_RAY_SURFACE_OFFSET units to E, to make sure we don't go close to the surface. + // If we're moving in the direction of the capsule, the origin is now about GU_RAY_SURFACE_OFFSET units from it. + // If we're moving away from the capsule, the ray won't hit the capsule anyway. + // If l is smaller than GU_RAY_SURFACE_OFFSET we're close enough, accuracy is good, there is nothing to do. + if(l>GU_RAY_SURFACE_OFFSET) + l -= GU_RAY_SURFACE_OFFSET; + else + l = 0.0f; + + // PT: move origin closer to capsule and do the raycast + PxReal s[2]; + const PxU32 nbHits = Gu::intersectRayCapsuleInternal(origin + l*dir, dir, p0, p1, radius, s); + if(!nbHits) + return false; + + // PT: keep closest hit only + if(nbHits == 1) + t = s[0]; + else + t = (s[0] < s[1]) ? s[0] : s[1]; + + // PT: fix distance (smaller than expected after moving ray close to capsule) + t += l; + return true; + } + + PX_FORCE_INLINE bool intersectRayCapsule(const PxVec3& origin, const PxVec3& dir, const Gu::Capsule& capsule, PxReal& t) + { + return Gu::intersectRayCapsule(origin, dir, capsule.p0, capsule.p1, capsule.radius, t); + } +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h new file mode 100644 index 000000000..0418f1f49 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h @@ -0,0 +1,58 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_PLANE_H +#define GU_INTERSECTION_RAY_PLANE_H + +#include "foundation/PxPlane.h" + +namespace physx +{ +namespace Gu +{ + // Returns true if line and plane are not parallel + PX_INLINE bool intersectRayPlane(const PxVec3& orig, const PxVec3& dir, const PxPlane& plane, float& distanceAlongLine, PxVec3* pointOnPlane = NULL) + { + const float dn = dir.dot(plane.n); + if(-1E-7f < dn && dn < 1E-7f) + return false; // parallel + + distanceAlongLine = -plane.distance(orig)/dn; + + if(pointOnPlane) + *pointOnPlane = orig + distanceAlongLine * dir; + + return true; + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp new file mode 100644 index 000000000..d6244b714 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp @@ -0,0 +1,105 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec3.h" +#include "GuIntersectionRaySphere.h" +#include "GuIntersectionRay.h" + +using namespace physx; + +// Based on GD Mag code, but now works correctly when origin is inside the sphere. +// This version has limited accuracy. +bool Gu::intersectRaySphereBasic(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos) +{ + // get the offset vector + const PxVec3 offset = center - origin; + + // get the distance along the ray to the center point of the sphere + const PxReal ray_dist = dir.dot(offset); + + // get the squared distances + const PxReal off2 = offset.dot(offset); + const PxReal rad_2 = radius * radius; + if(off2 <= rad_2) + { + // we're in the sphere + if(hit_pos) + *hit_pos = origin; + dist = 0.0f; + return true; + } + + if(ray_dist <= 0 || (ray_dist - length) > radius) + { + // moving away from object or too far away + return false; + } + + // find hit distance squared + const PxReal d = rad_2 - (off2 - ray_dist * ray_dist); + if(d<0.0f) + { + // ray passes by sphere without hitting + return false; + } + + // get the distance along the ray + dist = ray_dist - PxSqrt(d); + if(dist > length) + { + // hit point beyond length + return false; + } + + // sort out the details + if(hit_pos) + *hit_pos = origin + dir * dist; + return true; +} + +// PT: modified version calls the previous function, but moves the ray origin closer to the sphere. The test accuracy is +// greatly improved as a result. This is an idea proposed on the GD-Algorithms list by Eddie Edwards. +// See: http://www.codercorner.com/blog/?p=321 +bool Gu::intersectRaySphere(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos) +{ + const PxVec3 x = origin - center; + PxReal l = PxSqrt(x.dot(x)) - radius - GU_RAY_SURFACE_OFFSET; + +// if(l<0.0f) +// l=0.0f; + l = physx::intrinsics::selectMax(l, 0.0f); + + bool status = intersectRaySphereBasic(origin + l*dir, dir, length - l, center, radius, dist, hit_pos); + if(status) + { +// dist += l/length; + dist += l; + } + return status; +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h new file mode 100644 index 000000000..f97d892a8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h @@ -0,0 +1,50 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_SPHERE_H +#define GU_INTERSECTION_RAY_SPHERE_H + +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + // PT: basic version, limited accuracy, might fail for long rays vs small spheres + PX_PHYSX_COMMON_API bool intersectRaySphereBasic(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos = NULL); + + // PT: version with improved accuracy + PX_PHYSX_COMMON_API bool intersectRaySphere(const PxVec3& origin, const PxVec3& dir, PxReal length, const PxVec3& center, PxReal radius, PxReal& dist, PxVec3* hit_pos = NULL); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h new file mode 100644 index 000000000..25c081f5a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h @@ -0,0 +1,179 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_RAY_TRIANGLE_H +#define GU_INTERSECTION_RAY_TRIANGLE_H + +#include "foundation/PxVec3.h" +#include "PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +namespace Gu +{ + // PT: this is used for backface culling. It existed in Moller's original code already. Basically this is only to avoid dividing by zero. + // This should not depend on what units are used, and neither should it depend on the size of triangles. A large triangle with the same + // orientation as a small triangle should be backface culled the same way. A triangle whose orientation does not change should not suddenly + // become culled or visible when we scale it. + // + // An absolute epsilon is fine here. The computation will work fine for small triangles, and large triangles will simply make 'det' larger, + // more and more inaccurate, but it won't suddenly make it negative. + // + // Using FLT_EPSILON^2 ensures that triangles whose edges are smaller than FLT_EPSILON long are rejected. This epsilon makes the code work + // for very small triangles, while still preventing divisions by too small values. + #define GU_CULLING_EPSILON_RAY_TRIANGLE FLT_EPSILON*FLT_EPSILON + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes a ray-triangle intersection test. + * From Tomas Moeller's "Fast Minimum Storage Ray-Triangle Intersection" + * Could be optimized and cut into 2 methods (culled or not). Should make a batch one too to avoid the call overhead, or make it inline. + * + * \param orig [in] ray origin + * \param dir [in] ray direction + * \param vert0 [in] triangle vertex + * \param vert1 [in] triangle vertex + * \param vert2 [in] triangle vertex + * \param at [out] distance + * \param au [out] impact barycentric coordinate + * \param av [out] impact barycentric coordinate + * \param cull [in] true to use backface culling + * \param enlarge [in] enlarge triangle by specified epsilon in UV space to avoid false near-edge rejections + * \return true on overlap + * \note u, v and t will remain unchanged if false is returned. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PX_FORCE_INLINE bool intersectRayTriangle( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, + PxReal& at, PxReal& au, PxReal& av, + bool cull, float enlarge=0.0f) + { + // Find vectors for two edges sharing vert0 + const PxVec3 edge1 = vert1 - vert0; + const PxVec3 edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); // error ~ |v2-v0| + + // If determinant is near zero, ray lies in plane of triangle + const PxReal det = edge1.dot(pvec); // error ~ |v2-v0|*|v1-v0| + + if(cull) + { + if(detuvlimit2) + return false; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const PxReal v = dir.dot(qvec); + if(vuvlimit2) + return false; + + // Calculate t, scale parameters, ray intersects triangle + const PxReal t = edge2.dot(qvec); + + const PxReal inv_det = 1.0f / det; + at = t*inv_det; + au = u*inv_det; + av = v*inv_det; + } + else + { + // the non-culling branch + if(PxAbs(det)1.0f+enlarge) + return false; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const PxReal v = dir.dot(qvec) * inv_det; + if(v<-enlarge || (u+v)>1.0f+enlarge) + return false; + + // Calculate t, ray intersects triangle + const PxReal t = edge2.dot(qvec) * inv_det; + + at = t; + au = u; + av = v; + } + return true; + } + + /* \note u, v and t will remain unchanged if false is returned. */ + PX_FORCE_INLINE bool intersectRayTriangleCulling( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, + PxReal& t, PxReal& u, PxReal& v, + float enlarge=0.0f) + { + return intersectRayTriangle(orig, dir, vert0, vert1, vert2, t, u, v, true, enlarge); + } + + /* \note u, v and t will remain unchanged if false is returned. */ + PX_FORCE_INLINE bool intersectRayTriangleNoCulling( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, + PxReal& t, PxReal& u, PxReal& v, + float enlarge=0.0f) + { + return intersectRayTriangle(orig, dir, vert0, vert1, vert2, t, u, v, false, enlarge); + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp new file mode 100644 index 000000000..26638001b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp @@ -0,0 +1,88 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionSphereBox.h" +#include "GuSphere.h" +#include "GuBox.h" + +using namespace physx; + +bool Gu::intersectSphereBox(const Sphere& sphere, const Box& box) +{ + const PxVec3 delta = sphere.center - box.center; + PxVec3 dRot = box.rot.transformTranspose(delta); //transform delta into OBB body coords. (use method call!) + + //check if delta is outside AABB - and clip the vector to the AABB. + bool outside = false; + + if(dRot.x < -box.extents.x) + { + outside = true; + dRot.x = -box.extents.x; + } + else if(dRot.x > box.extents.x) + { + outside = true; + dRot.x = box.extents.x; + } + + if(dRot.y < -box.extents.y) + { + outside = true; + dRot.y = -box.extents.y; + } + else if(dRot.y > box.extents.y) + { + outside = true; + dRot.y = box.extents.y; + } + + if(dRot.z < -box.extents.z) + { + outside = true; + dRot.z = -box.extents.z; + } + else if(dRot.z > box.extents.z) + { + outside = true; + dRot.z = box.extents.z; + } + + if(outside) //if clipping was done, sphere center is outside of box. + { + const PxVec3 clippedDelta = box.rot.transform(dRot); //get clipped delta back in world coords. + + const PxVec3 clippedVec = delta - clippedDelta; //what we clipped away. + const PxReal lenSquared = clippedVec.magnitudeSquared(); + const PxReal radius = sphere.radius; + if(lenSquared > radius * radius) // PT: objects are defined as closed, so we return 'true' in case of equality + return false; //disjoint + } + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h new file mode 100644 index 000000000..e787c39d0 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_INTERSECTION_SPHERE_BOX_H +#define GU_INTERSECTION_SPHERE_BOX_H + +namespace physx +{ +namespace Gu +{ + class Sphere; + class Box; + + /** + Checks if a sphere intersects a box. Based on: Jim Arvo, A Simple Method for Box-Sphere Intersection Testing, Graphics Gems, pp. 247-250. + + \param sphere [in] sphere + \param box [in] box + + \return true if sphere overlaps box (or exactly touches it) + */ + bool intersectSphereBox(const Gu::Sphere& sphere, const Gu::Box& box); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp new file mode 100644 index 000000000..854fe53e0 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp @@ -0,0 +1,198 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionTriangleBox.h" +#include "GuIntersectionTriangleBoxRef.h" +#include "CmMatrix34.h" +#include "PsVecMath.h" +#include "GuBox.h" +#include "GuSIMDHelpers.h" + +using namespace physx; + + +Ps::IntBool Gu::intersectTriangleBox_ReferenceCode(const PxVec3& boxcenter, const PxVec3& extents, const PxVec3& tp0, const PxVec3& tp1, const PxVec3& tp2) +{ + return intersectTriangleBox_RefImpl(boxcenter, extents, tp0, tp1, tp2); +} + + +using namespace Ps::aos; + +static PX_FORCE_INLINE int testClassIIIAxes(const Vec4V& e0V, const Vec4V v0V, const Vec4V v1V, const Vec4V v2V, const PxVec3& extents) +{ + const Vec4V e0XZY_V = V4PermYZXW(e0V); + + const Vec4V v0XZY_V = V4PermYZXW(v0V); + const Vec4V p0V = V4NegMulSub(v0XZY_V, e0V, V4Mul(v0V, e0XZY_V)); + + const Vec4V v1XZY_V = V4PermYZXW(v1V); + const Vec4V p1V = V4NegMulSub(v1XZY_V, e0V, V4Mul(v1V, e0XZY_V)); + + const Vec4V v2XZY_V = V4PermYZXW(v2V); + const Vec4V p2V = V4NegMulSub(v2XZY_V, e0V, V4Mul(v2V, e0XZY_V)); + + Vec4V minV = V4Min(p0V, p1V); + minV = V4Min(minV, p2V); + + const Vec4V extentsV = V4LoadU(&extents.x); + const Vec4V fe0ZYX_V = V4Abs(e0V); + + const Vec4V fe0XZY_V = V4PermYZXW(fe0ZYX_V); + const Vec4V extentsXZY_V = V4PermYZXW(extentsV); + Vec4V radV = V4MulAdd(extentsV, fe0XZY_V, V4Mul(extentsXZY_V, fe0ZYX_V)); + + if(V4AnyGrtr3(minV, radV)) + return 0; + + Vec4V maxV = V4Max(p0V, p1V); + maxV = V4Max(maxV, p2V); + + radV = V4Sub(V4Zero(), radV); + + if(V4AnyGrtr3(radV, maxV)) + return 0; + return 1; +} + +static const VecU32V signV = U4LoadXYZW(0x80000000, 0x80000000, 0x80000000, 0x80000000); + +static PX_FORCE_INLINE Ps::IntBool intersectTriangleBoxInternal(const Vec4V v0V, const Vec4V v1V, const Vec4V v2V, const PxVec3& extents) +{ + // Test box axes + { + Vec4V extentsV = V4LoadU(&extents.x); + + { + const Vec4V cV = V4Abs(v0V); + if(V4AllGrtrOrEq3(extentsV, cV)) + return 1; + } + + Vec4V minV = V4Min(v0V, v1V); + minV = V4Min(minV, v2V); + + if(V4AnyGrtr3(minV, extentsV)) + return 0; + + Vec4V maxV = V4Max(v0V, v1V); + maxV = V4Max(maxV, v2V); + extentsV = V4Sub(V4Zero(), extentsV); + + if(V4AnyGrtr3(extentsV, maxV)) + return 0; + } + + // Test if the box intersects the plane of the triangle + const Vec4V e0V = V4Sub(v1V, v0V); + const Vec4V e1V = V4Sub(v2V, v1V); + { + const Vec4V normalV = V4Cross(e0V, e1V); + const Vec4V dV = Vec4V_From_FloatV(V4Dot3(normalV, v0V)); + + const Vec4V extentsV = V4LoadU(&extents.x); + VecU32V normalSignsV = V4U32and(VecU32V_ReinterpretFrom_Vec4V(normalV), signV); + const Vec4V maxV = Vec4V_ReinterpretFrom_VecU32V(V4U32or(VecU32V_ReinterpretFrom_Vec4V(extentsV), normalSignsV)); + + Vec4V tmpV = Vec4V_From_FloatV(V4Dot3(normalV, maxV)); + if(V4AnyGrtr3(dV, tmpV)) + return 0; + + normalSignsV = V4U32xor(normalSignsV, signV); + const Vec4V minV = Vec4V_ReinterpretFrom_VecU32V(V4U32or(VecU32V_ReinterpretFrom_Vec4V(extentsV), normalSignsV)); + + tmpV = Vec4V_From_FloatV(V4Dot3(normalV, minV)); + if(V4AnyGrtr3(tmpV, dV)) + return 0; + } + + // Edge-edge tests + { + if(!testClassIIIAxes(e0V, v0V, v1V, v2V, extents)) + return 0; + if(!testClassIIIAxes(e1V, v0V, v1V, v2V, extents)) + return 0; + const Vec4V e2V = V4Sub(v0V, v2V); + if(!testClassIIIAxes(e2V, v0V, v1V, v2V, extents)) + return 0; + } + return 1; +} + +// PT: a SIMD version of Tomas Moller's triangle-box SAT code +Ps::IntBool Gu::intersectTriangleBox_Unsafe(const PxVec3& center, const PxVec3& extents, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) +{ + // Move everything so that the boxcenter is in (0,0,0) + const Vec4V BoxCenterV = V4LoadU(¢er.x); + const Vec4V v0V = V4Sub(V4LoadU(&p0.x), BoxCenterV); + const Vec4V v1V = V4Sub(V4LoadU(&p1.x), BoxCenterV); + const Vec4V v2V = V4Sub(V4LoadU(&p2.x), BoxCenterV); + + return intersectTriangleBoxInternal(v0V, v1V, v2V, extents); +} + +Ps::IntBool Gu::intersectTriangleBox(const BoxPadded& box, const PxVec3& p0_, const PxVec3& p1_, const PxVec3& p2_) +{ + // PT: TODO: SIMDify this part + + // Vec3p ensures we can safely V4LoadU the data + const Vec3p p0 = box.rotateInv(p0_ - box.center); + const Vec3p p1 = box.rotateInv(p1_ - box.center); + const Vec3p p2 = box.rotateInv(p2_ - box.center); + + const Vec4V v0V = V4LoadU(&p0.x); + const Vec4V v1V = V4LoadU(&p1.x); + const Vec4V v2V = V4LoadU(&p2.x); + + return intersectTriangleBoxInternal(v0V, v1V, v2V, box.extents); +} + +static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33& mat) +{ + const FloatV xxxV = V4GetX(p); + const FloatV yyyV = V4GetY(p); + const FloatV zzzV = V4GetZ(p); + + Vec4V ResV = V4Scale(V4LoadU(&mat.column0.x), xxxV); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat.column1.x), yyyV)); + ResV = V4Add(ResV, V4Scale(V4LoadU(&mat.column2.x), zzzV)); + return ResV; +} + +// PT: warning: all params must be safe to V4LoadU +Ps::IntBool intersectTriangleBoxBV4(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, + const PxMat33& rotModelToBox, const PxVec3& transModelToBox, const PxVec3& extents) +{ + const Vec4V transModelToBoxV = V4LoadU(&transModelToBox.x); + const Vec4V v0V = V4Add(multiply3x3V(V4LoadU(&p0.x), rotModelToBox), transModelToBoxV); + const Vec4V v1V = V4Add(multiply3x3V(V4LoadU(&p1.x), rotModelToBox), transModelToBoxV); + const Vec4V v2V = V4Add(multiply3x3V(V4LoadU(&p2.x), rotModelToBox), transModelToBoxV); + + return intersectTriangleBoxInternal(v0V, v1V, v2V, extents); +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp new file mode 100644 index 000000000..e049f883e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp @@ -0,0 +1,276 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "GuBV32.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + + +BV32Tree::BV32Tree(SourceMesh* meshInterface, const PxBounds3& localBounds) +{ + reset(); + init(meshInterface, localBounds); +} + +BV32Tree::BV32Tree() +{ + reset(); +} + +void BV32Tree::release() +{ + if (!mUserAllocated) + { + DELETEARRAY(mNodes); + PX_FREE_AND_RESET(mPackedNodes); + } + mNodes = NULL; + mNbNodes = 0; +} + +BV32Tree::~BV32Tree() +{ + release(); +} + +void BV32Tree::reset() +{ + mMeshInterface = NULL; + mNbNodes = 0; + mNodes = NULL; + mNbPackedNodes = 0; + mPackedNodes = NULL; + mInitData = 0; + mUserAllocated = false; +} + +void BV32Tree::operator=(BV32Tree& v) +{ + mMeshInterface = v.mMeshInterface; + mLocalBounds = v.mLocalBounds; + mNbNodes = v.mNbNodes; + mNodes = v.mNodes; + mInitData = v.mInitData; + mUserAllocated = v.mUserAllocated; + v.reset(); +} + +bool BV32Tree::init(SourceMesh* meshInterface, const PxBounds3& localBounds) +{ + mMeshInterface = meshInterface; + mLocalBounds.init(localBounds); + return true; +} + +// PX_SERIALIZATION +BV32Tree::BV32Tree(const PxEMPTY) +{ + mUserAllocated = true; +} + +void BV32Tree::exportExtraData(PxSerializationContext& stream) +{ + stream.alignData(16); + stream.writeData(mPackedNodes, mNbNodes*sizeof(BV32DataPacked)); +} + +void BV32Tree::importExtraData(PxDeserializationContext& context) +{ + context.alignExtraData(16); + mPackedNodes = context.readExtraData(mNbNodes); +} +//~PX_SERIALIZATION + +bool BV32Tree::load(PxInputStream& stream, bool mismatch_) +{ + PX_ASSERT(!mUserAllocated); + + release(); + + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if(a != 'B' || b != 'V' || c != '3' || d != '2') + return false; + + bool mismatch; + PxU32 fileVersion; + if(!readBigEndianVersionNumber(stream, mismatch_, fileVersion, mismatch)) + return false; + + mLocalBounds.mCenter.x = readFloat(mismatch, stream); + mLocalBounds.mCenter.y = readFloat(mismatch, stream); + mLocalBounds.mCenter.z = readFloat(mismatch, stream); + mLocalBounds.mExtentsMagnitude = readFloat(mismatch, stream); + + mInitData = readDword(mismatch, stream); + + /*const PxU32 nbNodes = readDword(mismatch, stream); + mNbNodes = nbNodes; + + if (nbNodes) + { + BV32Data* nodes = PX_NEW(BV32Data)[nbNodes]; + + mNodes = nodes; + Cm::markSerializedMem(nodes, sizeof(BV32Data)*nbNodes); + + for (PxU32 i = 0; i(PX_ALLOC(sizeof(BV32DataPacked)*nbPackedNodes, "BV32DataPacked")); + + Cm::markSerializedMem(mPackedNodes, sizeof(BV32DataPacked)*nbPackedNodes); + + for (PxU32 i = 0; i < nbPackedNodes; ++i) + { + BV32DataPacked& node = mPackedNodes[i]; + node.mNbNodes = readDword(mismatch, stream); + PX_ASSERT(node.mNbNodes > 0); + ReadDwordBuffer(node.mData, node.mNbNodes, mismatch, stream); + const PxU32 nbElements = 4 * node.mNbNodes; + readFloatBuffer(&node.mCenter[0].x, nbElements, mismatch, stream); + readFloatBuffer(&node.mExtents[0].x, nbElements, mismatch, stream); + + } + } + + return true; +} + + +void BV32Tree::calculateLeafNode(BV32Data& node) +{ + if (!node.isLeaf()) + { + const PxU32 nbChildren = node.getNbChildren(); + const PxU32 offset = node.getChildOffset(); + //calcualte how many children nodes are leaf nodes + PxU32 nbLeafNodes = 0; + for (PxU32 i = 0; i < nbChildren; ++i) + { + BV32Data& child = mNodes[offset + i]; + + if (child.isLeaf()) + { + nbLeafNodes++; + } + } + + node.mNbLeafNodes = nbLeafNodes; + for (PxU32 i = 0; i < nbChildren; ++i) + { + BV32Data& child = mNodes[offset + i]; + calculateLeafNode(child); + } + + } +} + + + +void BV32Tree::createSOAformatNode(BV32DataPacked& packedData, const BV32Data& node, const PxU32 childOffset, PxU32& currentIndex, PxU32& nbPackedNodes) +{ + + //found the next 32 nodes and fill it in SOA format + + const PxU32 nbChildren = node.getNbChildren(); + const PxU32 offset = node.getChildOffset(); + + + for (PxU32 i = 0; i < nbChildren; ++i) + { + BV32Data& child = mNodes[offset + i]; + + packedData.mCenter[i] = PxVec4(child.mCenter, 0.f); + packedData.mExtents[i] = PxVec4(child.mExtents, 0.f); + packedData.mData[i] = PxU32(child.mData); + } + + packedData.mNbNodes = nbChildren; + + PxU32 NbToGo = 0; + PxU32 NextIDs[32]; + memset(NextIDs, PX_INVALID_U32, sizeof(PxU32) * 32); + const BV32Data* ChildNodes[32]; + memset(ChildNodes, 0, sizeof(BV32Data*) * 32); + + + for (PxU32 i = 0; i< nbChildren; i++) + { + BV32Data& child = mNodes[offset + i]; + + if (!child.isLeaf()) + { + const PxU32 NextID = currentIndex; + + const PxU32 ChildSize = child.getNbChildren() - child.mNbLeafNodes; + currentIndex += ChildSize; + + //packedData.mData[i] = (packedData.mData[i] & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) | (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT); + packedData.mData[i] = (packedData.mData[i] & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) | ((childOffset + NbToGo) << GU_BV4_CHILD_OFFSET_SHIFT_COUNT); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = &child; + NbToGo++; + } + } + + nbPackedNodes += NbToGo; + for (PxU32 i = 0; i < NbToGo; ++i) + { + const BV32Data& child = *ChildNodes[i]; + + BV32DataPacked& childData = mPackedNodes[childOffset+i]; + + createSOAformatNode(childData, child, NextIDs[i], currentIndex, nbPackedNodes); + + } + +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h new file mode 100644 index 000000000..1ac5d53c2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h @@ -0,0 +1,146 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV32_H +#define GU_BV32_H + +#include "foundation/PxBounds3.h" +#include "PxSerialFramework.h" +#include "PsUserAllocated.h" +#include "GuBV4.h" +#include "CmPhysXCommon.h" +#include "PsArray.h" +#include "foundation/PxVec4.h" + +namespace physx +{ + namespace Gu + { + struct BV32Data : public physx::shdfnd::UserAllocated + { + PxVec3 mCenter; + PxU32 mNbLeafNodes; + PxVec3 mExtents; + size_t mData; + + + PX_FORCE_INLINE BV32Data() : mNbLeafNodes(0), mData(PX_INVALID_U32) + { + setEmpty(); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 isLeaf() const { return mData & 1; } + + //if the node is leaf, + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbReferencedTriangles() const { PX_ASSERT(isLeaf()); return PxU32((mData >>1)&63); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getTriangleStartIndex() const { PX_ASSERT(isLeaf()); return PxU32(mData >> 7); } + + //PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getPrimitive() const { return mData >> 1; } + //if the node isn't leaf, we will get the childOffset + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getChildOffset() const { PX_ASSERT(!isLeaf()); return PxU32(mData >> GU_BV4_CHILD_OFFSET_SHIFT_COUNT); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbChildren() const { PX_ASSERT(!isLeaf()); return ((mData) & ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1))>>1; } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void getMinMax(PxVec3& min, PxVec3& max) const + { + min = mCenter - mExtents; + max = mCenter + mExtents; + } + + PX_FORCE_INLINE void setEmpty() + { + mCenter = PxVec3(0.0f, 0.0f, 0.0f); + mExtents = PxVec3(-1.0f, -1.0f, -1.0f); + } + + }; + + PX_ALIGN_PREFIX(16) + struct BV32DataPacked + { + PxVec4 mCenter[32]; + PxVec4 mExtents[32]; + PxU32 mData[32]; + PxU32 mNbNodes; + PxU32 pad[3]; + + PX_CUDA_CALLABLE PX_FORCE_INLINE BV32DataPacked() : mNbNodes(0) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 isLeaf(const PxU32 index) const { return mData[index] & 1; } + //if the node is leaf, + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbReferencedTriangles(const PxU32 index) const { PX_ASSERT(isLeaf(index)); return (mData[index] >> 1) & 63; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getTriangleStartIndex(const PxU32 index) const { PX_ASSERT(isLeaf(index)); return (mData[index] >> 7); } + //if the node isn't leaf, we will get the childOffset + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getChildOffset(const PxU32 index) const { PX_ASSERT(!isLeaf(index)); return mData[index] >> GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNbChildren(const PxU32 index) const { PX_ASSERT(!isLeaf(index)); return ((mData[index])& ((1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT) - 1)) >> 1; } + } + PX_ALIGN_SUFFIX(16); + + class BV32Tree : public physx::shdfnd::UserAllocated + { + public: + // PX_SERIALIZATION + BV32Tree(const PxEMPTY); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + PX_PHYSX_COMMON_API BV32Tree(); + PX_PHYSX_COMMON_API BV32Tree(SourceMesh* meshInterface, const PxBounds3& localBounds); + PX_PHYSX_COMMON_API ~BV32Tree(); + + bool load(PxInputStream& stream, bool mismatch); + + void calculateLeafNode(BV32Data& node); + void createSOAformatNode(BV32DataPacked& packedData, const BV32Data& node, const PxU32 childOffset, PxU32& currentIndex, PxU32& nbPackedNodes); + + void reset(); + void operator = (BV32Tree& v); + + bool init(SourceMesh* meshInterface, const PxBounds3& localBounds); + void release(); + + SourceMesh* mMeshInterface; + LocalBounds mLocalBounds; + + PxU32 mNbNodes; + BV32Data* mNodes; + BV32DataPacked* mPackedNodes; + PxU32 mNbPackedNodes; + PxU32 mInitData; + bool mUserAllocated; // PT: please keep these 4 bytes right after mCenterOrMinCoeff/mExtentsOrMaxCoeff for safe V4 loading + bool mPadding[3]; + }; + + } // namespace Gu +} + +#endif // GU_BV32_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp new file mode 100644 index 000000000..2f2e633f4 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp @@ -0,0 +1,530 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec4.h" +#include "GuBV32Build.h" +#include "GuBV32.h" +#include "PxTriangle.h" +#include "CmPhysXCommon.h" +#include "PsBasicTemplates.h" +#include "GuCenterExtents.h" +#include "GuBV4Build.h" +#include "PsAllocator.h" + +using namespace physx; +using namespace Gu; + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#define DELETESINGLE(x) if (x) { delete x; x = NULL; } +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +struct BV32Node : public physx::shdfnd::UserAllocated +{ + BV32Node() : mNbChildBVNodes(0) + {} + + BV32Data mBVData[32]; + PxU32 mNbChildBVNodes; + + PX_FORCE_INLINE size_t isLeaf(PxU32 i) const { return mBVData[i].mData & 1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return PxU32(mBVData[i].mData >> 1); } + PX_FORCE_INLINE const BV32Node* getChild(PxU32 i) const { return reinterpret_cast(mBVData[i].mData); } + + + PxU32 getSize() const + { + return sizeof(BV32Data)*mNbChildBVNodes; + } +}; + + +static void fillInNodes(const AABBTreeNode* current_node, const PxU32 startIndex, const PxU32 endIndex, const AABBTreeNode** NODES, PxU32& stat) +{ + + if (startIndex + 1 == endIndex) + { + //fill in nodes + const AABBTreeNode* P = current_node->getPos(); + const AABBTreeNode* N = current_node->getNeg(); + NODES[startIndex] = P; + NODES[endIndex] = N; + stat += 2; + } + else + { + const AABBTreeNode* P = current_node->getPos(); + const AABBTreeNode* N = current_node->getNeg(); + const PxU32 midIndex = startIndex + ((endIndex - startIndex) / 2); + if (!P->isLeaf()) + fillInNodes(P, startIndex, midIndex, NODES, stat); + else + { + NODES[startIndex] = P; + stat++; + } + + if (!N->isLeaf()) + fillInNodes(N, midIndex + 1, endIndex, NODES, stat); + else + { + NODES[midIndex + 1] = N; + stat++; + } + } +} + + + +static void setPrimitive(const AABBTree& source, BV32Node* node32, PxU32 i, const AABBTreeNode* node, float epsilon) +{ + const PxU32 nbPrims = node->getNbPrimitives(); + PX_ASSERT(nbPrims<=32); + const PxU32* indexBase = source.getIndices(); + const PxU32* prims = node->getPrimitives(); + const PxU32 offset = PxU32(prims - indexBase); + +#if BV32_VALIDATE + for (PxU32 j = 0; jmBVData[i].mCenter = node->getAABB().getCenter(); + node32->mBVData[i].mExtents = node->getAABB().getExtents(); + if (epsilon != 0.0f) + node32->mBVData[i].mExtents += PxVec3(epsilon, epsilon, epsilon); + node32->mBVData[i].mData = (primitiveIndex << 1) | 1; +} + +static BV32Node* setNode(const AABBTree& source, BV32Node* node32, PxU32 i, const AABBTreeNode* node, float epsilon) +{ + BV32Node* child = NULL; + + if (node) + { + if (node->isLeaf()) + { + setPrimitive(source, node32, i, node, epsilon); + } + else + { + node32->mBVData[i].mCenter = node->getAABB().getCenter(); + node32->mBVData[i].mExtents = node->getAABB().getExtents(); + if (epsilon != 0.0f) + node32->mBVData[i].mExtents += PxVec3(epsilon, epsilon, epsilon); + + child = PX_NEW(BV32Node); + node32->mBVData[i].mData = size_t(child); + } + } + + return child; +} + + +static void _BuildBV32(const AABBTree& source, BV32Node* tmp, const AABBTreeNode* current_node, float epsilon, PxU32& nbNodes) +{ + PX_ASSERT(!current_node->isLeaf()); + + const AABBTreeNode* NODES[32]; + memset(NODES, 0, sizeof(AABBTreeNode*) * 32); + + fillInNodes(current_node, 0, 31, NODES, tmp->mNbChildBVNodes); + + PxU32 left = 0; + PxU32 right = 31; + + while (left < right) + { + + //sweep from the front + while (leftmNbChildBVNodes; + + for (PxU32 i = 0; i < tmp->mNbChildBVNodes; ++i) + { + const AABBTreeNode* tempNode = NODES[i]; + BV32Node* Child = setNode(source, tmp, i, tempNode, epsilon); + if (Child) + { + _BuildBV32(source, Child, tempNode, epsilon, nbNodes); + } + } + +} + +// +//static void validateTree(const AABBTree& Source, const AABBTreeNode* currentNode) +//{ +// if (currentNode->isLeaf()) +// { +// const PxU32* indexBase = Source.getIndices(); +// const PxU32* prims = currentNode->getPrimitives(); +// const PxU32 offset = PxU32(prims - indexBase); +// const PxU32 nbPrims = currentNode->getNbPrimitives(); +// for (PxU32 j = 0; jgetPos(); +// validateTree(Source, pos); +// const AABBTreeNode* neg = currentNode->getNeg(); +// validateTree(Source, neg); +// } +//} + +#if BV32_VALIDATE +static void validateNodeBound(const BV32Node* currentNode, SourceMesh* mesh) +{ + const PxU32 nbNodes = currentNode->mNbChildBVNodes; + for (PxU32 i = 0; i < nbNodes; ++i) + { + const BV32Node* node = currentNode->getChild(i); + if (currentNode->isLeaf(i)) + { + BV32Data data = currentNode->mBVData[i]; + PxU32 nbTriangles = data.getNbReferencedTriangles(); + PxU32 startIndex = data.getTriangleStartIndex(); + const IndTri32* triIndices = mesh->getTris32(); + const PxVec3* verts = mesh->getVerts(); + PxVec3 min(PX_MAX_F32, PX_MAX_F32, PX_MAX_F32); + PxVec3 max(-PX_MAX_F32, -PX_MAX_F32, -PX_MAX_F32); + for (PxU32 j = 0; j < nbTriangles; ++j) + { + IndTri32 index = triIndices[startIndex + j]; + + for (PxU32 k = 0; k < 3; ++k) + { + const PxVec3& v = verts[index.mRef[k]]; + + min.x = (min.x > v.x) ? v.x : min.x; + min.y = (min.y > v.y) ? v.y : min.y; + min.z = (min.z > v.z) ? v.z : min.z; + + max.x = (max.x < v.x) ? v.x : max.x; + max.y = (max.y > v.y) ? v.y : max.y; + max.z = (max.z > v.z) ? v.z : max.z; + } + } + + PxVec3 dMin, dMax; + data.getMinMax(dMin, dMax); + PX_ASSERT(dMin.x <= min.x && dMin.y <= min.y && dMin.z <= min.z); + PX_ASSERT(dMax.x >= max.x && dMax.y >= max.y && dMax.z >= min.z); + + } + else + { + validateNodeBound(node, mesh); + } + } +} +#endif + +static bool BuildBV32Internal(BV32Tree& bv32Tree, const AABBTree& Source, SourceMesh* mesh, float epsilon) +{ + if (mesh->getNbTriangles() <= 32) + { + bv32Tree.mNbPackedNodes = 1; + bv32Tree.mPackedNodes = reinterpret_cast(PX_ALLOC(sizeof(BV32DataPacked), "BV32DataPacked")); + BV32DataPacked& packedData = bv32Tree.mPackedNodes[0]; + packedData.mNbNodes = 1; + packedData.mCenter[0] = PxVec4(Source.getBV().getCenter(), 0.f); + packedData.mExtents[0] = PxVec4(Source.getBV().getExtents(), 0.f); + packedData.mData[0] = (mesh->getNbTriangles() << 1) | 1; + return bv32Tree.init(mesh, Source.getBV()); + } + + { + struct Local + { + static void _CheckMD(const AABBTreeNode* current_node, PxU32& md, PxU32& cd) + { + cd++; + md = PxMax(md, cd); + + if (current_node->getPos()) { _CheckMD(current_node->getPos(), md, cd); cd--; } + if (current_node->getNeg()) { _CheckMD(current_node->getNeg(), md, cd); cd--; } + } + + static void _Check(AABBTreeNode* current_node) + { + if (current_node->isLeaf()) + return; + + AABBTreeNode* P = const_cast(current_node->getPos()); + AABBTreeNode* N = const_cast(current_node->getNeg()); + { + PxU32 MDP = 0; PxU32 CDP = 0; _CheckMD(P, MDP, CDP); + PxU32 MDN = 0; PxU32 CDN = 0; _CheckMD(N, MDN, CDN); + + if (MDP>MDN) + // if(MDP(Source.getNodes())); + } + + + PxU32 nbNodes = 1; + BV32Node* Root32 = PX_NEW(BV32Node); + + + _BuildBV32(Source, Root32, Source.getNodes(), epsilon, nbNodes); + +#if BV32_VALIDATE + validateNodeBound(Root32, mesh); +#endif + + if (!bv32Tree.init(mesh, Source.getBV())) + return false; + BV32Tree* T = &bv32Tree; + + // Version with variable-sized nodes in single stream + { + struct Local + { + static void _Flatten(BV32Data* const dest, const PxU32 box_id, PxU32& current_id, const BV32Node* current, PxU32& max_depth, PxU32& current_depth, const PxU32 nb_nodes) + { + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if (current_depth>max_depth) + max_depth = current_depth; + + for (PxU32 i = 0; imNbChildBVNodes; i++) + { + dest[box_id + i].mCenter = current->mBVData[i].mCenter; + dest[box_id + i].mExtents = current->mBVData[i].mExtents; + dest[box_id + i].mData = PxU32(current->mBVData[i].mData); + + PX_ASSERT(box_id + i < nb_nodes); + } + + PxU32 NbToGo = 0; + PxU32 NextIDs[32]; + memset(NextIDs, PX_INVALID_U32, sizeof(PxU32)*32); + const BV32Node* ChildNodes[32]; + memset(ChildNodes, 0, sizeof(BV32Node*)*32); + + BV32Data* data = dest + box_id; + for (PxU32 i = 0; imNbChildBVNodes; i++) + { + PX_ASSERT(current->mBVData[i].mData != PX_INVALID_U32); + + if (!current->isLeaf(i)) + { + + const BV32Node* ChildNode = current->getChild(i); + + const PxU32 NextID = current_id; + + const PxU32 ChildSize = ChildNode->mNbChildBVNodes; + current_id += ChildSize; + + const PxU32 ChildType = ChildNode->mNbChildBVNodes << 1; + data[i].mData = size_t(ChildType + (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + //PX_ASSERT(data[i].mData == size_t(ChildType+(NextID<<3))); + + PX_ASSERT(box_id + i < nb_nodes); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = ChildNode; + NbToGo++; + } + } + + + + for (PxU32 i = 0; imNbChildBVNodes+1; + + BV32Data* Nodes = PX_NEW(BV32Data)[nbNodes]; + Nodes[0].mCenter = Source.getBV().getCenter(); + Nodes[0].mExtents = Source.getBV().getExtents(); + + const PxU32 ChildType = Root32->mNbChildBVNodes << 1; + Nodes[0].mData = size_t(ChildType + (1 << GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + + const PxU32 nbChilden = Nodes[0].getNbChildren(); + + PX_UNUSED(nbChilden); + + + T->mInitData = CurID; + PxU32 MaxDepth = 0; + PxU32 CurrentDepth = 0; + + Local::_Flatten(Nodes, 1, CurID, Root32, MaxDepth, CurrentDepth, nbNodes); + + PX_ASSERT(CurID == nbNodes); + + T->mNbNodes = nbNodes; + + T->mNodes = Nodes; + } + + + bv32Tree.calculateLeafNode(bv32Tree.mNodes[0]); + + bv32Tree.mPackedNodes = reinterpret_cast(PX_ALLOC(sizeof(BV32DataPacked)*nbNodes, "BV32DataPacked")); + bv32Tree.mNbPackedNodes = nbNodes; + + PxU32 nbPackedNodes = 1; + PxU32 currentIndex = bv32Tree.mNodes[0].getNbChildren() - bv32Tree.mNodes[0].mNbLeafNodes + 1; + BV32DataPacked& packedData = bv32Tree.mPackedNodes[0]; + bv32Tree.createSOAformatNode(packedData, bv32Tree.mNodes[0], 1, currentIndex, nbPackedNodes); + + bv32Tree.mNbPackedNodes = nbPackedNodes; + + PX_ASSERT(nbPackedNodes == currentIndex); + PX_ASSERT(nbPackedNodes > 0); + + return true; +} + +///// + +struct ReorderData32 +{ + const SourceMesh* mMesh; + PxU32* mOrder; + PxU32 mNbTrisPerLeaf; + PxU32 mIndex; + PxU32 mNbTris; + PxU32 mStats[32]; +}; + +static bool gReorderCallback(const AABBTreeNode* current, PxU32 /*depth*/, void* userData) +{ + ReorderData32* Data = reinterpret_cast(userData); + if (current->isLeaf()) + { + const PxU32 n = current->getNbPrimitives(); + PX_ASSERT(n > 0); + PX_ASSERT(n <= Data->mNbTrisPerLeaf); + Data->mStats[n-1]++; + PxU32* Prims = const_cast(current->getPrimitives()); + + for (PxU32 i = 0; imNbTris); + Data->mOrder[Data->mIndex] = Prims[i]; + PX_ASSERT(Data->mIndexmNbTris); + Prims[i] = Data->mIndex; + Data->mIndex++; + } + } + return true; +} + + +bool physx::Gu::BuildBV32Ex(BV32Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf) +{ + const PxU32 nbTris = mesh.mNbTris; + + AABBTree Source; + if (!Source.buildFromMesh(mesh, nbTrisPerLeaf)) + return false; + + + { + PxU32* order = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTris, "BV32")); + ReorderData32 RD; + RD.mMesh = &mesh; + RD.mOrder = order; + RD.mNbTrisPerLeaf = nbTrisPerLeaf; + RD.mIndex = 0; + RD.mNbTris = nbTris; + for (PxU32 i = 0; i<32; i++) + RD.mStats[i] = 0; + Source.walk(gReorderCallback, &RD); + PX_ASSERT(RD.mIndex == nbTris); + mesh.remapTopology(order); + PX_FREE(order); + // for(PxU32 i=0;i<16;i++) + // printf("%d: %d\n", i, RD.mStats[i]); + } + + + //if (mesh.getNbTriangles() <= nbTrisPerLeaf) + // return tree.init(&mesh, Source.getBV()); + + return BuildBV32Internal(tree, Source, &mesh, epsilon); +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h new file mode 100644 index 000000000..7c03e8193 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h @@ -0,0 +1,50 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV32_BUILD_H +#define GU_BV32_BUILD_H + +#include "foundation/PxSimpleTypes.h" +#include "common/PxPhysXCommonConfig.h" + +#define BV32_VALIDATE 0 + +namespace physx +{ + namespace Gu + { + class BV32Tree; + class SourceMesh; + + PX_PHYSX_COMMON_API bool BuildBV32Ex(BV32Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf); + + } // namespace Gu +} + +#endif // GU_BV32_BUILD_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp new file mode 100644 index 000000000..8d7896148 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp @@ -0,0 +1,390 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "GuBV4.h" +#include "GuBV4_Common.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +SourceMeshBase::SourceMeshBase() +{ + mNbVerts = 0; + mVerts = NULL; + mRemap = NULL; +} + +SourceMeshBase::~SourceMeshBase() +{ + PX_FREE_AND_RESET(mRemap); +} + +SourceMesh::SourceMesh() +{ + reset(); +} + +SourceMesh::~SourceMesh() +{ + +} + +void SourceMesh::reset() +{ + mNbVerts = 0; + mVerts = NULL; + mNbTris = 0; + mTriangles32 = NULL; + mTriangles16 = NULL; + mRemap = NULL; +} + +void SourceMesh::operator=(SourceMesh& v) +{ + mNbVerts = v.mNbVerts; + mVerts = v.mVerts; + mNbTris = v.mNbTris; + mTriangles32 = v.mTriangles32; + mTriangles16 = v.mTriangles16; + mRemap = v.mRemap; + v.reset(); +} + +void SourceMesh::remapTopology(const PxU32* order) +{ + if(!mNbTris) + return; + + if(mTriangles32) + { + IndTri32* newTopo = PX_NEW(IndTri32)[mNbTris]; + for(PxU32 i=0;i(PX_ALLOC(sizeof(PxU32)*mNbTris, "OPC2")); + for(PxU32 i=0;i(mNbNodes); + else + mNodes = context.readExtraData(mNbNodes); +#else + mNodes = context.readExtraData(mNbNodes); +#endif + } +} +//~PX_SERIALIZATION + +bool BV4Tree::load(PxInputStream& stream, bool mismatch_) +{ + PX_ASSERT(!mUserAllocated); + + release(); + + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if(a!='B' || b!='V' || c!='4' || d!=' ') + return false; + + bool mismatch; + PxU32 fileVersion; + if(!readBigEndianVersionNumber(stream, mismatch_, fileVersion, mismatch)) + return false; + + readFloatBuffer(&mLocalBounds.mCenter.x, 3, mismatch, stream); + mLocalBounds.mExtentsMagnitude = readFloat(mismatch, stream); + + mInitData = readDword(mismatch, stream); + + readFloatBuffer(&mCenterOrMinCoeff.x, 3, mismatch, stream); + readFloatBuffer(&mExtentsOrMaxCoeff.x, 3, mismatch, stream); + + // PT: version 3 + if(fileVersion>=3) + { + const PxU32 Quantized = readDword(mismatch, stream); + mQuantized = Quantized!=0; + } + else + mQuantized = true; + + const PxU32 nbNodes = readDword(mismatch, stream); + mNbNodes = nbNodes; + + if(nbNodes) + { + PxU32 dataSize = 0; +#ifdef GU_BV4_USE_SLABS + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + const PxU32 nodeSize = mQuantized ? sizeof(BVDataPackedQ) : sizeof(BVDataPackedNQ); + #else + const PxU32 nodeSize = sizeof(BVDataPackedQ); + #endif + dataSize = nodeSize*nbNodes; + void* nodes = PX_ALLOC(dataSize, "BV4 nodes"); // PT: PX_NEW breaks alignment here +// BVDataPacked* nodes = reinterpret_cast(PX_ALLOC(sizeof(BVDataPacked)*nbNodes, "BV4 nodes")); // PT: PX_NEW breaks alignment here + mNodes = nodes; +#else + BVDataPacked* nodes = PX_NEW(BVDataPacked)[nbNodes]; + mNodes = nodes; +#endif +// Cm::markSerializedMem(nodes, dataSize); + + stream.read(nodes, dataSize); + PX_ASSERT(!mismatch); + } + else mNodes = NULL; + + return true; +} + +void BV4Tree::refit(float epsilon) +{ + if(mQuantized) + return; + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + // PT: TODO: SIMD + const PxVec3 eps(epsilon); + PX_ASSERT(!(mNbNodes&3)); + PxU32 nb = mNbNodes/4; + BVDataSwizzledNQ* data = reinterpret_cast(mNodes); + while(nb--) + { + BVDataSwizzledNQ* current = data + nb; + + for(PxU32 j=0;j<4;j++) + { + if(current->getChildData(j)==PX_INVALID_U32) + continue; + + PxBounds3 refitBox; + refitBox.setEmpty(); + + if(current->isLeaf(j)) + { + PxU32 primIndex = current->getPrimitive(j); + + PxU32 nbToGo = getNbPrimitives(primIndex); + VertexPointers VP; + do + { + PX_ASSERT(primIndexgetNbTriangles()); + mMeshInterface->getTriangle(VP, primIndex); + + refitBox.include(*VP.Vertex[0]); + refitBox.include(*VP.Vertex[1]); + refitBox.include(*VP.Vertex[2]); + + primIndex++; + }while(nbToGo--); + } + else + { + PxU32 childOffset = current->getChildOffset(j); + PX_ASSERT(!(childOffset&3)); + childOffset>>=2; + PX_ASSERT(childOffset>nb); + const PxU32 childType = current->getChildType(j); + + const BVDataSwizzledNQ* next = data + childOffset; + { + if(childType>1) + { + const PxBounds3 childBox( PxVec3(next->mMinX[3], next->mMinY[3], next->mMinZ[3]), + PxVec3(next->mMaxX[3], next->mMaxY[3], next->mMaxZ[3])); + refitBox.include(childBox); + } + + if(childType>0) + { + const PxBounds3 childBox( PxVec3(next->mMinX[2], next->mMinY[2], next->mMinZ[2]), + PxVec3(next->mMaxX[2], next->mMaxY[2], next->mMaxZ[2])); + refitBox.include(childBox); + } + + { + const PxBounds3 childBox( PxVec3(next->mMinX[1], next->mMinY[1], next->mMinZ[1]), + PxVec3(next->mMaxX[1], next->mMaxY[1], next->mMaxZ[1])); + refitBox.include(childBox); + } + + { + const PxBounds3 childBox( PxVec3(next->mMinX[0], next->mMinY[0], next->mMinZ[0]), + PxVec3(next->mMaxX[0], next->mMaxY[0], next->mMaxZ[0])); + refitBox.include(childBox); + } + } + } + refitBox.minimum -= eps; + refitBox.maximum += eps; + + current->mMinX[j] = refitBox.minimum.x; + current->mMinY[j] = refitBox.minimum.y; + current->mMinZ[j] = refitBox.minimum.z; + current->mMaxX[j] = refitBox.maximum.x; + current->mMaxY[j] = refitBox.maximum.y; + current->mMaxZ[j] = refitBox.maximum.z; + } + } +#else + PX_UNUSED(epsilon); +#endif +} + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h new file mode 100644 index 000000000..098205a5e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h @@ -0,0 +1,283 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_H +#define GU_BV4_H + +#include "foundation/PxBounds3.h" +#include "PxSerialFramework.h" +#include "PsUserAllocated.h" +#include "GuBV4Settings.h" +#include "GuCenterExtents.h" + +#define V4LoadU_Safe V4LoadU +#define V4LoadA_Safe V4LoadA +#define V4StoreA_Safe V4StoreA +#define V4StoreU_Safe V4StoreU + +namespace physx +{ +namespace Gu +{ + + struct VertexPointers + { + const PxVec3* Vertex[3]; + }; + + class IndTri32 : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE IndTri32() {} + PX_FORCE_INLINE IndTri32(PxU32 r0, PxU32 r1, PxU32 r2) { mRef[0]=r0; mRef[1]=r1; mRef[2]=r2; } + PX_FORCE_INLINE IndTri32(const IndTri32& triangle) + { + mRef[0] = triangle.mRef[0]; + mRef[1] = triangle.mRef[1]; + mRef[2] = triangle.mRef[2]; + } + PX_FORCE_INLINE ~IndTri32() {} + PxU32 mRef[3]; + }; + PX_COMPILE_TIME_ASSERT(sizeof(IndTri32)==12); + + class IndTri16 : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE IndTri16() {} + PX_FORCE_INLINE IndTri16(PxU16 r0, PxU16 r1, PxU16 r2) { mRef[0]=r0; mRef[1]=r1; mRef[2]=r2; } + PX_FORCE_INLINE IndTri16(const IndTri16& triangle) + { + mRef[0] = triangle.mRef[0]; + mRef[1] = triangle.mRef[1]; + mRef[2] = triangle.mRef[2]; + } + PX_FORCE_INLINE ~IndTri16() {} + PxU16 mRef[3]; + }; + PX_COMPILE_TIME_ASSERT(sizeof(IndTri16)==6); + + PX_FORCE_INLINE void getVertexReferences(PxU32& vref0, PxU32& vref1, PxU32& vref2, PxU32 index, const IndTri32* T32, const IndTri16* T16) + { + if(T32) + { + const IndTri32* PX_RESTRICT tri = T32 + index; + vref0 = tri->mRef[0]; + vref1 = tri->mRef[1]; + vref2 = tri->mRef[2]; + } + else + { + const IndTri16* PX_RESTRICT tri = T16 + index; + vref0 = tri->mRef[0]; + vref1 = tri->mRef[1]; + vref2 = tri->mRef[2]; + } + } + + + class SourceMeshBase : public physx::shdfnd::UserAllocated + { + public: + PX_PHYSX_COMMON_API SourceMeshBase(); + PX_PHYSX_COMMON_API ~SourceMeshBase(); + + SourceMeshBase(const PxEMPTY) {} + + PxU32 mNbVerts; + const PxVec3* mVerts; + + PX_FORCE_INLINE PxU32 getNbVertices() const { return mNbVerts; } + PX_FORCE_INLINE const PxVec3* getVerts() const { return mVerts; } + + PX_FORCE_INLINE void setNbVertices(PxU32 nb) { mNbVerts = nb; } + + PX_FORCE_INLINE void initRemap() { mRemap = NULL; } + PX_FORCE_INLINE const PxU32* getRemap() const { return mRemap; } + PX_FORCE_INLINE void releaseRemap() { PX_FREE_AND_RESET(mRemap); } + + protected: + PxU32* mRemap; + + }; + + class SourceMesh : public SourceMeshBase + { + public: + PX_PHYSX_COMMON_API SourceMesh(); + PX_PHYSX_COMMON_API ~SourceMesh(); + // PX_SERIALIZATION + SourceMesh(const PxEMPTY) {} + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + void reset(); + void operator = (SourceMesh& v); + + PxU32 mNbTris; + IndTri32* mTriangles32; + IndTri16* mTriangles16; + + PX_FORCE_INLINE PxU32 getNbTriangles() const { return mNbTris; } + //PX_FORCE_INLINE PxU32 getNbVertices() const { return mNbVerts; } + PX_FORCE_INLINE const IndTri32* getTris32() const { return mTriangles32; } + PX_FORCE_INLINE const IndTri16* getTris16() const { return mTriangles16; } + //PX_FORCE_INLINE const PxVec3* getVerts() const { return mVerts; } + + PX_FORCE_INLINE void setNbTriangles(PxU32 nb) { mNbTris = nb; } + //PX_FORCE_INLINE void setNbVertices(PxU32 nb) { mNbVerts = nb; } + + PX_FORCE_INLINE void setPointers(IndTri32* tris32, IndTri16* tris16, const PxVec3* verts) + { + mTriangles32 = tris32; + mTriangles16 = tris16; + mVerts = verts; + } + + void remapTopology(const PxU32* order); + + bool isValid() const; + + PX_FORCE_INLINE void getTriangle(VertexPointers& vp, PxU32 index) const + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, index, mTriangles32, mTriangles16); + vp.Vertex[0] = mVerts + VRef0; + vp.Vertex[1] = mVerts + VRef1; + vp.Vertex[2] = mVerts + VRef2; + } + + }; + + struct LocalBounds + { + // PX_SERIALIZATION + LocalBounds(const PxEMPTY) {} + //~PX_SERIALIZATION + LocalBounds() : mCenter(PxVec3(0.0f)), mExtentsMagnitude(0.0f) {} + + PxVec3 mCenter; + float mExtentsMagnitude; + + PX_FORCE_INLINE void init(const PxBounds3& bounds) + { + mCenter = bounds.getCenter(); + // PT: TODO: compute mag first, then multiplies by 0.5f (TA34704) + mExtentsMagnitude = bounds.getExtents().magnitude(); + } + }; + + class QuantizedAABB + { + public: + + struct Data + { + PxU16 mExtents; //!< Quantized extents + PxI16 mCenter; //!< Quantized center + }; + Data mData[3]; + }; + PX_COMPILE_TIME_ASSERT(sizeof(QuantizedAABB)==12); + + ///// + + #define GU_BV4_CHILD_OFFSET_SHIFT_COUNT 11 + static PX_FORCE_INLINE PxU32 getChildOffset(PxU32 data) { return data>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + static PX_FORCE_INLINE PxU32 getChildType(PxU32 data) { return (data>>1)&3; } + + template + struct BVDataPackedT : public physx::shdfnd::UserAllocated + { + BoxType mAABB; + PxU32 mData; + + PX_FORCE_INLINE PxU32 isLeaf() const { return mData&1; } + PX_FORCE_INLINE PxU32 getPrimitive() const { return mData>>1; } + PX_FORCE_INLINE PxU32 getChildOffset() const { return mData>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT;} + PX_FORCE_INLINE PxU32 getChildType() const { return (mData>>1)&3; } + PX_FORCE_INLINE PxU32 getChildData() const { return mData; } + + PX_FORCE_INLINE void encodePNS(PxU32 code) + { + PX_ASSERT(code<256); + mData |= code<<3; + } + PX_FORCE_INLINE PxU32 decodePNSNoShift() const { return mData; } + }; + + typedef BVDataPackedT BVDataPackedQ; +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + typedef BVDataPackedT BVDataPackedNQ; +#endif + + // PT: TODO: align class to 16? (TA34704) + class BV4Tree : public physx::shdfnd::UserAllocated + { + public: + // PX_SERIALIZATION + BV4Tree(const PxEMPTY); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + PX_PHYSX_COMMON_API BV4Tree(); + PX_PHYSX_COMMON_API BV4Tree(SourceMesh* meshInterface, const PxBounds3& localBounds); + PX_PHYSX_COMMON_API ~BV4Tree(); + + bool load(PxInputStream& stream, bool mismatch); + + void reset(); + void operator = (BV4Tree& v); + + bool init(SourceMesh* meshInterface, const PxBounds3& localBounds); + void release(); + + void refit(float epsilon); + + // PT: TODO: use a single pointer to SourceMeshBase + SourceMesh* mMeshInterface; + LocalBounds mLocalBounds; + + PxU32 mNbNodes; + void* mNodes; // PT: BVDataPacked / BVDataSwizzled + PxU32 mInitData; + // PT: the dequantization coeffs are only used for quantized trees + PxVec3 mCenterOrMinCoeff; // PT: dequantization coeff, either for Center or Min (depending on AABB format) + PxVec3 mExtentsOrMaxCoeff; // PT: dequantization coeff, either for Extents or Max (depending on AABB format) + bool mUserAllocated; // PT: please keep these 4 bytes right after mCenterOrMinCoeff/mExtentsOrMaxCoeff for safe V4 loading + bool mQuantized; // PT: true for quantized trees + bool mPadding[2]; + }; + +} // namespace Gu +} + +#endif // GU_BV4_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp new file mode 100644 index 000000000..6cf6efabc --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp @@ -0,0 +1,1503 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec4.h" +#include "foundation/PxMemory.h" +#include "GuBV4Build.h" +#include "GuBV4.h" +#include "PxTriangle.h" +#include "CmPhysXCommon.h" +#include "PsBasicTemplates.h" +#include "GuCenterExtents.h" +#include + +using namespace physx; +using namespace Gu; + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#define GU_BV4_USE_NODE_POOLS + +#define DELETESINGLE(x) if (x) { delete x; x = NULL; } +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +static PX_FORCE_INLINE PxU32 largestAxis(const PxVec4& v) +{ + const float* Vals = &v.x; + PxU32 m = 0; + if(Vals[1] > Vals[m]) m = 1; + if(Vals[2] > Vals[m]) m = 2; + return m; +} + +AABBTree::AABBTree() : mIndices(NULL), mPool(NULL), mTotalNbNodes(0) +{ +} + +AABBTree::~AABBTree() +{ + release(); +} + +void AABBTree::release() +{ + DELETEARRAY(mPool); + PX_FREE_AND_RESET(mIndices); +} + +static PxU32 local_Split(const AABBTreeNode* PX_RESTRICT node, const PxBounds3* PX_RESTRICT /*Boxes*/, const PxVec3* PX_RESTRICT centers, PxU32 axis) +{ + const PxU32 nb = node->mNbPrimitives; + PxU32* PX_RESTRICT prims = node->mNodePrimitives; + + // Get node split value + const float splitValue = node->mBV.getCenter(axis); + + PxU32 nbPos = 0; + // Loop through all node-related primitives. Their indices range from mNodePrimitives[0] to mNodePrimitives[mNbPrimitives-1]. + // Those indices map the global list in the tree builder. + const size_t ptrValue = size_t(centers) + axis*sizeof(float); + const PxVec3* PX_RESTRICT centersX = reinterpret_cast(ptrValue); + + for(PxU32 i=0;i splitValue) + { + // Swap entries + prims[i] = prims[nbPos]; + prims[nbPos] = index; + // Count primitives assigned to positive space + nbPos++; + } + } + return nbPos; +} + +static bool local_Subdivide(AABBTreeNode* PX_RESTRICT node, const PxBounds3* PX_RESTRICT boxes, const PxVec3* PX_RESTRICT centers, BuildStats& stats, const AABBTreeNode* const PX_RESTRICT node_base, PxU32 limit) +{ + const PxU32* PX_RESTRICT prims = node->mNodePrimitives; + const PxU32 nb = node->mNbPrimitives; + + // Compute bv & means at the same time + Vec4V meansV; + { + Vec4V minV = V4LoadU(&boxes[prims[0]].minimum.x); + Vec4V maxV = V4LoadU(&boxes[prims[0]].maximum.x); + meansV = V4LoadU(¢ers[prims[0]].x); + + for(PxU32 i=1;imBV.minimum = PxVec3(mergedMin.x, mergedMin.y, mergedMin.z); + node->mBV.maximum = PxVec3(mergedMax.x, mergedMax.y, mergedMax.z); + } + +// // Stop subdividing if we reach a leaf node. This is always performed here, +// // else we could end in trouble if user overrides this. +// if(nb==1) +// return false; + if(nb<=limit) + return false; + + bool validSplit = true; + PxU32 nbPos; + { + // Compute variances + Vec4V varsV = V4Zero(); + for(PxU32 i=0;ilimit) + { + nbPos = node->mNbPrimitives>>1; + + if(1) + { + // Test 3 axis, take the best + float results[3]; + nbPos = local_Split(node, boxes, centers, 0); results[0] = float(nbPos)/float(node->mNbPrimitives); + nbPos = local_Split(node, boxes, centers, 1); results[1] = float(nbPos)/float(node->mNbPrimitives); + nbPos = local_Split(node, boxes, centers, 2); results[2] = float(nbPos)/float(node->mNbPrimitives); + results[0]-=0.5f; results[0]*=results[0]; + results[1]-=0.5f; results[1]*=results[1]; + results[2]-=0.5f; results[2]*=results[2]; + PxU32 Min=0; + if(results[1]mNbPrimitives) + nbPos = node->mNbPrimitives>>1; + } + } + //else return + } + + // Now create children and assign their pointers. + // We use a pre-allocated linear pool for complete trees [Opcode 1.3] + const PxU32 count = stats.getCount(); + node->mPos = size_t(node_base + count); + + // Update stats + stats.increaseCount(2); + + // Assign children + AABBTreeNode* pos = const_cast(node->getPos()); + AABBTreeNode* neg = const_cast(node->getNeg()); + pos->mNodePrimitives = node->mNodePrimitives; + pos->mNbPrimitives = nbPos; + neg->mNodePrimitives = node->mNodePrimitives + nbPos; + neg->mNbPrimitives = node->mNbPrimitives - nbPos; + return true; +} + +static void local_BuildHierarchy(AABBTreeNode* PX_RESTRICT node, const PxBounds3* PX_RESTRICT Boxes, const PxVec3* PX_RESTRICT centers, BuildStats& stats, const AABBTreeNode* const PX_RESTRICT node_base, PxU32 limit) +{ + if(local_Subdivide(node, Boxes, centers, stats, node_base, limit)) + { + AABBTreeNode* pos = const_cast(node->getPos()); + AABBTreeNode* neg = const_cast(node->getNeg()); + local_BuildHierarchy(pos, Boxes, centers, stats, node_base, limit); + local_BuildHierarchy(neg, Boxes, centers, stats, node_base, limit); + } +} + +bool AABBTree::buildFromMesh(SourceMesh& mesh, PxU32 limit) +{ + const PxU32 nbBoxes = mesh.getNbTriangles(); + if(!nbBoxes) + return false; + PxBounds3* boxes = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(nbBoxes+1), "BV4")); // PT: +1 to safely V4Load/V4Store the last element + PxVec3* centers = reinterpret_cast(PX_ALLOC(sizeof(PxVec3)*(nbBoxes+1), "BV4")); // PT: +1 to safely V4Load/V4Store the last element + const FloatV halfV = FLoad(0.5f); + for(PxU32 i=0;ix); + const Vec4V v1V = V4LoadU(&VP.Vertex[1]->x); + const Vec4V v2V = V4LoadU(&VP.Vertex[2]->x); + Vec4V minV = V4Min(v0V, v1V); + minV = V4Min(minV, v2V); + Vec4V maxV = V4Max(v0V, v1V); + maxV = V4Max(maxV, v2V); + V4StoreU_Safe(minV, &boxes[i].minimum.x); // PT: safe because 'maximum' follows 'minimum' + V4StoreU_Safe(maxV, &boxes[i].maximum.x); // PT: safe because we allocated one more box + + const Vec4V centerV = V4Scale(V4Add(maxV, minV), halfV); + V4StoreU_Safe(centerV, ¢ers[i].x); // PT: safe because we allocated one more PxVec3 + } + + { + // Release previous tree + release(); + + // Init stats + BuildStats Stats; + Stats.setCount(1); + + // Initialize indices. This list will be modified during build. + mIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbBoxes, "BV4 indices")); + // Identity permutation + for(PxU32 i=0;imNodePrimitives = mIndices; + mPool->mNbPrimitives = nbBoxes; + + // Build the hierarchy + local_BuildHierarchy(mPool, boxes, centers, Stats, mPool, limit); + + // Get back total number of nodes + mTotalNbNodes = Stats.getCount(); + } + + PX_FREE(centers); + PX_FREE(boxes); + return true; +} + +PxU32 AABBTree::walk(WalkingCallback cb, void* userData) const +{ + // Call it without callback to compute max depth + PxU32 maxDepth = 0; + PxU32 currentDepth = 0; + + struct Local + { + static void _Walk(const AABBTreeNode* current_node, PxU32& max_depth, PxU32& current_depth, WalkingCallback callback, void* userData_) + { + // Checkings + if(!current_node) + return; + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if(current_depth>max_depth) + max_depth = current_depth; + + // Callback + if(callback && !(callback)(current_node, current_depth, userData_)) + return; + + // Recurse + if(current_node->getPos()) { _Walk(current_node->getPos(), max_depth, current_depth, callback, userData_); current_depth--; } + if(current_node->getNeg()) { _Walk(current_node->getNeg(), max_depth, current_depth, callback, userData_); current_depth--; } + } + }; + Local::_Walk(mPool, maxDepth, currentDepth, cb, userData); + return maxDepth; +} + + + + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT +// PT: see http://www.codercorner.com/blog/?p=734 +static PxU32 precomputeNodeSorting(const PxBounds3& box0, const PxBounds3& box1) +{ + const PxVec3 C0 = box0.getCenter(); + const PxVec3 C1 = box1.getCenter(); + + PxVec3 dirPPP(1.0f, 1.0f, 1.0f); dirPPP.normalize(); + PxVec3 dirPPN(1.0f, 1.0f, -1.0f); dirPPN.normalize(); + PxVec3 dirPNP(1.0f, -1.0f, 1.0f); dirPNP.normalize(); + PxVec3 dirPNN(1.0f, -1.0f, -1.0f); dirPNN.normalize(); + PxVec3 dirNPP(-1.0f, 1.0f, 1.0f); dirNPP.normalize(); + PxVec3 dirNPN(-1.0f, 1.0f, -1.0f); dirNPN.normalize(); + PxVec3 dirNNP(-1.0f, -1.0f, 1.0f); dirNNP.normalize(); + PxVec3 dirNNN(-1.0f, -1.0f, -1.0f); dirNNN.normalize(); + + const PxVec3 deltaC = C0 - C1; + const bool bPPP = deltaC.dot(dirPPP)<0.0f; + const bool bPPN = deltaC.dot(dirPPN)<0.0f; + const bool bPNP = deltaC.dot(dirPNP)<0.0f; + const bool bPNN = deltaC.dot(dirPNN)<0.0f; + const bool bNPP = deltaC.dot(dirNPP)<0.0f; + const bool bNPN = deltaC.dot(dirNPN)<0.0f; + const bool bNNP = deltaC.dot(dirNNP)<0.0f; + const bool bNNN = deltaC.dot(dirNNN)<0.0f; + + PxU32 code = 0; + if(!bPPP) + code |= (1<<7); // Bit 0: PPP + if(!bPPN) + code |= (1<<6); // Bit 1: PPN + if(!bPNP) + code |= (1<<5); // Bit 2: PNP + if(!bPNN) + code |= (1<<4); // Bit 3: PNN + if(!bNPP) + code |= (1<<3); // Bit 4: NPP + if(!bNPN) + code |= (1<<2); // Bit 5: NPN + if(!bNNP) + code |= (1<<1); // Bit 6: NNP + if(!bNNN) + code |= (1<<0); // Bit 7: NNN + return code; +} +#endif + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Common.h" +#endif + +static void setEmpty(CenterExtents& box) +{ + box.mCenter = PxVec3(0.0f, 0.0f, 0.0f); + box.mExtents = PxVec3(-1.0f, -1.0f, -1.0f); +} + +// Data: +// 1 bit for leaf/no leaf +// 2 bits for child-node type +// 8 bits for PNS +// => 32 - 1 - 2 - 8 = 21 bits left for encoding triangle index or node *offset* +// => limited to 2.097.152 triangles +// => and 2Mb-large trees (this one may not work out well in practice) +// ==> lines marked with //* have been changed to address this. Now we don't store offsets in bytes directly +// but in BVData indices. There's more work at runtime calculating addresses, but now the format can support +// 2 million single nodes. +// +// That being said we only need 3*8 = 24 bits in total, so that could be only 6 bits in each BVData. +// For type0: we have 2 nodes, we need 8 bits => 6 bits/node = 12 bits available, ok +// For type1: we have 3 nodes, we need 8*2 = 16 bits => 6 bits/node = 18 bits available, ok +// For type2: we have 4 nodes, we need 8*3 = 24 bits => 6 bits/node = 24 bits available, ok +//#pragma pack(1) +struct BVData : public physx::shdfnd::UserAllocated +{ + BVData(); + CenterExtents mAABB; + size_t mData; +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + PxU32 mTempPNS; +#endif +}; +//#pragma pack() + +BVData::BVData() : mData(PX_INVALID_U32) +{ + setEmpty(mAABB); +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + mTempPNS = 0; +#endif +} + +struct BV4Node : public physx::shdfnd::UserAllocated +{ + PX_FORCE_INLINE BV4Node() {} + PX_FORCE_INLINE ~BV4Node() {} + + BVData mBVData[4]; + + PX_FORCE_INLINE size_t isLeaf(PxU32 i) const { return mBVData[i].mData&1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return PxU32(mBVData[i].mData>>1); } + PX_FORCE_INLINE const BV4Node* getChild(PxU32 i) const { return reinterpret_cast(mBVData[i].mData); } + + PxU32 getType() const + { + PxU32 Nb=0; + for(PxU32 i=0;i<4;i++) + { + if(mBVData[i].mData!=PX_INVALID_U32) + Nb++; + } + return Nb; + } + + PxU32 getSize() const + { + const PxU32 type = getType(); + return sizeof(BVData)*type; + } +}; + +#define NB_NODES_PER_SLAB 256 +struct BV4BuildParams +{ + PX_FORCE_INLINE BV4BuildParams(float epsilon) : mEpsilon(epsilon) +#ifdef GU_BV4_USE_NODE_POOLS + ,mTop(NULL) +#endif + {} + ~BV4BuildParams(); + + // Stats + PxU32 mNbNodes; + PxU32 mStats[4]; + + // + float mEpsilon; + +#ifdef GU_BV4_USE_NODE_POOLS + // + struct Slab : public physx::shdfnd::UserAllocated + { + BV4Node mNodes[NB_NODES_PER_SLAB]; + PxU32 mNbUsedNodes; + Slab* mNext; + }; + Slab* mTop; + + BV4Node* allocateNode(); + void releaseNodes(); +#endif +}; + +BV4BuildParams::~BV4BuildParams() +{ +#ifdef GU_BV4_USE_NODE_POOLS + releaseNodes(); +#endif +} + +#ifdef GU_BV4_USE_NODE_POOLS +BV4Node* BV4BuildParams::allocateNode() +{ + if(!mTop || mTop->mNbUsedNodes==NB_NODES_PER_SLAB) + { + Slab* newSlab = PX_NEW(Slab); + newSlab->mNbUsedNodes = 0; + newSlab->mNext = mTop; + mTop = newSlab; + } + return &mTop->mNodes[mTop->mNbUsedNodes++]; +} + +void BV4BuildParams::releaseNodes() +{ + Slab* current = mTop; + while(current) + { + Slab* next = current->mNext; + PX_DELETE(current); + current = next; + } + mTop = NULL; +} +#endif + +static void setPrimitive(const AABBTree& source, BV4Node* node4, PxU32 i, const AABBTreeNode* node, float epsilon) +{ + const PxU32 nbPrims = node->getNbPrimitives(); + PX_ASSERT(nbPrims<16); + const PxU32* indexBase = source.getIndices(); + const PxU32* prims = node->getPrimitives(); + const PxU32 offset = PxU32(prims - indexBase); + for(PxU32 j=0;jmBVData[i].mAABB = node->getAABB(); + if(epsilon!=0.0f) + node4->mBVData[i].mAABB.mExtents += PxVec3(epsilon); + node4->mBVData[i].mData = (primitiveIndex<<1)|1; +} + +static BV4Node* setNode(const AABBTree& source, BV4Node* node4, PxU32 i, const AABBTreeNode* node, BV4BuildParams& params) +{ + BV4Node* child = NULL; + if(node->isLeaf()) + { + setPrimitive(source, node4, i, node, params.mEpsilon); + } + else + { + node4->mBVData[i].mAABB = node->getAABB(); + if(params.mEpsilon!=0.0f) + node4->mBVData[i].mAABB.mExtents += PxVec3(params.mEpsilon); + + params.mNbNodes++; +#ifdef GU_BV4_USE_NODE_POOLS + child = params.allocateNode(); +#else + child = PX_NEW(BV4Node); +#endif + node4->mBVData[i].mData = size_t(child); + } + return child; +} + +static void _BuildBV4(const AABBTree& source, BV4Node* tmp, const AABBTreeNode* current_node, BV4BuildParams& params) +{ + PX_ASSERT(!current_node->isLeaf()); + + // In the regular tree we have current node A, and: + // ____A____ + // P N + // __|__ __|__ + // PP PN NP NN + // + // For PNS we have: + // bit0 to sort P|N + // bit1 to sort PP|PN + // bit2 to sort NP|NN + // + // As much as possible we need to preserve the original order in BV4, if we want to reuse the same PNS bits. + // + // bit0|bit1|bit2 Order 8bits code + // 0 0 0 PP PN NP NN 0 1 2 3 + // 0 0 1 PP PN NN NP 0 1 3 2 + // 0 1 0 PN PP NP NN 1 0 2 3 + // 0 1 1 PN PP NN NP 1 0 3 2 + // 1 0 0 NP NN PP PN 2 3 0 1 + // 1 0 1 NN NP PP PN 3 2 0 1 + // 1 1 0 NP NN PN PP 2 3 1 0 + // 1 1 1 NN NP PN PP 3 2 1 0 + // + // So we can fetch/compute the sequence from the bits, combine it with limitations from the node type, and process the nodes in order. In theory. + // 8*8bits => the whole thing fits in a single 64bit register, so we could potentially use a "register LUT" here. + + const AABBTreeNode* P = current_node->getPos(); + const AABBTreeNode* N = current_node->getNeg(); + + const bool PLeaf = P->isLeaf(); + const bool NLeaf = N->isLeaf(); + + if(PLeaf) + { + if(NLeaf) + { + // Case 1: P and N are both leaves: + // ____A____ + // P N + // => store as (P,N) and keep bit0 + params.mStats[0]++; + // PN leaves => store 2 triangle pointers, lose 50% of node space + setPrimitive(source, tmp, 0, P, params.mEpsilon); + setPrimitive(source, tmp, 1, N, params.mEpsilon); + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); +#endif + } + else + { + // Case 2: P leaf, N no leaf + // ____A____ + // P N + // __|__ + // NP NN + // => store as (P,NP,NN), keep bit0 and bit2 + params.mStats[1]++; + // P leaf => store 1 triangle pointers and 2 node pointers + // => 3 slots used, 25% wasted + setPrimitive(source, tmp, 0, P, params.mEpsilon); + + // + + const AABBTreeNode* NP = N->getPos(); + const AABBTreeNode* NN = N->getNeg(); + +//#define NODE_FUSION +#ifdef NODE_FUSION + PxU32 c=0; + BV4Node* ChildNP; + if(!NP->isLeaf() && NP->getPos()->isLeaf() && NP->getNeg()->isLeaf()) + { + // Drag the terminal leaves directly into this BV4 node, drop internal node NP + setPrimitive(source, tmp, 1, NP->getPos(), params.mEpsilon); + setPrimitive(source, tmp, 2, NP->getNeg(), params.mEpsilon); + ChildNP = NULL; + params.mStats[1]--; + params.mStats[3]++; + c=1; + } + else + { + ChildNP = setNode(source, tmp, 1, NP, params); + } + + BV4Node* ChildNN; + if(c==0 && !NN->isLeaf() && NN->getPos()->isLeaf() && NN->getNeg()->isLeaf()) + { + // Drag the terminal leaves directly into this BV4 node, drop internal node NN + setPrimitive(source, tmp, 2, NN->getPos(), params.mEpsilon); + setPrimitive(source, tmp, 3, NN->getNeg(), params.mEpsilon); + ChildNN = NULL; + params.mStats[1]--; + params.mStats[3]++; + } + else + { + ChildNN = setNode(source, tmp, 2+c, NN, params); + } + + //BV4Node* ChildNN = setNode(tmp, 2+c, NN, epsilon, params); +#else + BV4Node* ChildNP = setNode(source, tmp, 1, NP, params); + BV4Node* ChildNN = setNode(source, tmp, 2, NN, params); +#endif + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); + tmp->mBVData[2].mTempPNS = precomputeNodeSorting(NP->mBV, NN->mBV); +#endif + if(ChildNP) + _BuildBV4(source, ChildNP, NP, params); + if(ChildNN) + _BuildBV4(source, ChildNN, NN, params); + } + } + else + { + if(NLeaf) + { + // Case 3: P no leaf, N leaf + // ____A____ + // P N + // __|__ + // PP PN + // => store as (PP,PN,N), keep bit0 and bit1 + params.mStats[2]++; + + // N leaf => store 1 triangle pointers and 2 node pointers + // => 3 slots used, 25% wasted + setPrimitive(source, tmp, 2, N, params.mEpsilon); + + // + + const AABBTreeNode* PP = P->getPos(); + const AABBTreeNode* PN = P->getNeg(); + + BV4Node* ChildPP = setNode(source, tmp, 0, PP, params); + BV4Node* ChildPN = setNode(source, tmp, 1, PN, params); + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); + tmp->mBVData[1].mTempPNS = precomputeNodeSorting(PP->mBV, PN->mBV); +#endif + if(ChildPP) + _BuildBV4(source, ChildPP, PP, params); + if(ChildPN) + _BuildBV4(source, ChildPN, PN, params); + } + else + { + // Case 4: P and N are no leaves: + // => store as (PP,PN,NP,NN), keep bit0/bit1/bit2 + params.mStats[3]++; + + // No leaves => store 4 node pointers + const AABBTreeNode* PP = P->getPos(); + const AABBTreeNode* PN = P->getNeg(); + const AABBTreeNode* NP = N->getPos(); + const AABBTreeNode* NN = N->getNeg(); + + BV4Node* ChildPP = setNode(source, tmp, 0, PP, params); + BV4Node* ChildPN = setNode(source, tmp, 1, PN, params); + BV4Node* ChildNP = setNode(source, tmp, 2, NP, params); + BV4Node* ChildNN = setNode(source, tmp, 3, NN, params); + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + tmp->mBVData[0].mTempPNS = precomputeNodeSorting(P->mBV, N->mBV); + tmp->mBVData[1].mTempPNS = precomputeNodeSorting(PP->mBV, PN->mBV); + tmp->mBVData[2].mTempPNS = precomputeNodeSorting(NP->mBV, NN->mBV); +#endif + if(ChildPP) + _BuildBV4(source, ChildPP, PP, params); + if(ChildPN) + _BuildBV4(source, ChildPN, PN, params); + if(ChildNP) + _BuildBV4(source, ChildNP, NP, params); + if(ChildNN) + _BuildBV4(source, ChildNN, NN, params); + } + } +} + +#ifdef GU_BV4_USE_SLABS +static void _ComputeMaxValues(const BV4Node* current, PxVec3& MinMax, PxVec3& MaxMax) +#else +static void _ComputeMaxValues(const BV4Node* current, PxVec3& CMax, PxVec3& EMax) +#endif +{ + for(PxU32 i=0; i<4; i++) + { + if(current->mBVData[i].mData != PX_INVALID_U32) + { + const CenterExtents& Box = current->mBVData[i].mAABB; +#ifdef GU_BV4_USE_SLABS + const PxVec3 Min = Box.mCenter - Box.mExtents; + const PxVec3 Max = Box.mCenter + Box.mExtents; + if(fabsf(Min.x)>MinMax.x) MinMax.x = fabsf(Min.x); + if(fabsf(Min.y)>MinMax.y) MinMax.y = fabsf(Min.y); + if(fabsf(Min.z)>MinMax.z) MinMax.z = fabsf(Min.z); + if(fabsf(Max.x)>MaxMax.x) MaxMax.x = fabsf(Max.x); + if(fabsf(Max.y)>MaxMax.y) MaxMax.y = fabsf(Max.y); + if(fabsf(Max.z)>MaxMax.z) MaxMax.z = fabsf(Max.z); +#else + if(fabsf(Box.mCenter.x)>CMax.x) CMax.x = fabsf(Box.mCenter.x); + if(fabsf(Box.mCenter.y)>CMax.y) CMax.y = fabsf(Box.mCenter.y); + if(fabsf(Box.mCenter.z)>CMax.z) CMax.z = fabsf(Box.mCenter.z); + if(fabsf(Box.mExtents.x)>EMax.x) EMax.x = fabsf(Box.mExtents.x); + if(fabsf(Box.mExtents.y)>EMax.y) EMax.y = fabsf(Box.mExtents.y); + if(fabsf(Box.mExtents.z)>EMax.z) EMax.z = fabsf(Box.mExtents.z); +#endif + if(!current->isLeaf(i)) + { + const BV4Node* ChildNode = current->getChild(i); +#ifdef GU_BV4_USE_SLABS + _ComputeMaxValues(ChildNode, MinMax, MaxMax); +#else + _ComputeMaxValues(ChildNode, CMax, EMax); +#endif + } + } + } +} + +// PT: duplicated for now.... + +static void _FlattenQ( BVDataPackedQ* const dest, const PxU32 box_id, PxU32& current_id, const BV4Node* current, PxU32& max_depth, PxU32& current_depth, + const PxVec3& CQuantCoeff, const PxVec3& EQuantCoeff, const PxVec3& mCenterCoeff, const PxVec3& mExtentsCoeff) +{ + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if(current_depth>max_depth) + max_depth = current_depth; + +// dest[box_id] = *current; + const PxU32 CurrentType = current->getType(); + for(PxU32 i=0; imBVData[i].mAABB; +#ifdef GU_BV4_USE_SLABS + const PxVec3 m = Box.mCenter - Box.mExtents; + const PxVec3 M = Box.mCenter + Box.mExtents; + + dest[box_id + i].mAABB.mData[0].mCenter = PxI16(m.x * CQuantCoeff.x); + dest[box_id + i].mAABB.mData[1].mCenter = PxI16(m.y * CQuantCoeff.y); + dest[box_id + i].mAABB.mData[2].mCenter = PxI16(m.z * CQuantCoeff.z); + dest[box_id + i].mAABB.mData[0].mExtents = PxU16(PxI16(M.x * EQuantCoeff.x)); + dest[box_id + i].mAABB.mData[1].mExtents = PxU16(PxI16(M.y * EQuantCoeff.y)); + dest[box_id + i].mAABB.mData[2].mExtents = PxU16(PxI16(M.z * EQuantCoeff.z)); + + if (1) + { + for (PxU32 j = 0; j<3; j++) + { + // Dequantize the min/max + // const float qmin = float(dest[box_id+i].mAABB.mData[j].mCenter) * mCenterCoeff[j]; + // const float qmax = float(PxI16(dest[box_id+i].mAABB.mData[j].mExtents)) * mExtentsCoeff[j]; + // Compare real & dequantized values + /* if(qmaxm[j]) + { + int stop=1; + }*/ + bool CanLeave; + do + { + CanLeave = true; + const float qmin = float(dest[box_id + i].mAABB.mData[j].mCenter) * mCenterCoeff[j]; + const float qmax = float(PxI16(dest[box_id + i].mAABB.mData[j].mExtents)) * mExtentsCoeff[j]; + + if (qmaxm[j]) + { + if (dest[box_id + i].mAABB.mData[j].mCenter) + { + dest[box_id + i].mAABB.mData[j].mCenter--; + CanLeave = false; + } + } + } while (!CanLeave); + } + } +#else // GU_BV4_USE_SLABS + dest[box_id + i].mAABB.mData[0].mCenter = PxI16(Box.mCenter.x * CQuantCoeff.x); + dest[box_id + i].mAABB.mData[1].mCenter = PxI16(Box.mCenter.y * CQuantCoeff.y); + dest[box_id + i].mAABB.mData[2].mCenter = PxI16(Box.mCenter.z * CQuantCoeff.z); + dest[box_id + i].mAABB.mData[0].mExtents = PxU16(Box.mExtents.x * EQuantCoeff.x); + dest[box_id + i].mAABB.mData[1].mExtents = PxU16(Box.mExtents.y * EQuantCoeff.y); + dest[box_id + i].mAABB.mData[2].mExtents = PxU16(Box.mExtents.z * EQuantCoeff.z); + + // Fix quantized boxes + if (1) + { + // Make sure the quantized box is still valid + const PxVec3 Max = Box.mCenter + Box.mExtents; + const PxVec3 Min = Box.mCenter - Box.mExtents; + // For each axis + for (PxU32 j = 0; j<3; j++) + { // Dequantize the box center + const float qc = float(dest[box_id + i].mAABB.mData[j].mCenter) * mCenterCoeff[j]; + bool FixMe = true; + do + { // Dequantize the box extent + const float qe = float(dest[box_id + i].mAABB.mData[j].mExtents) * mExtentsCoeff[j]; + // Compare real & dequantized values + if (qc + qeMin[j]) dest[box_id + i].mAABB.mData[j].mExtents++; + else FixMe = false; + // Prevent wrapping + if (!dest[box_id + i].mAABB.mData[j].mExtents) + { + dest[box_id + i].mAABB.mData[j].mExtents = 0xffff; + FixMe = false; + } + } while (FixMe); + } + } +#endif // GU_BV4_USE_SLABS + + dest[box_id + i].mData = PxU32(current->mBVData[i].mData); +// dest[box_id+i].encodePNS(current->mBVData[i].mTempPNS); + } + + PxU32 NbToGo = 0; + PxU32 NextIDs[4] = { PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32 }; + const BV4Node* ChildNodes[4] = { NULL,NULL,NULL,NULL }; + + BVDataPackedQ* data = dest + box_id; + for(PxU32 i=0; i<4; i++) + { + if(current->mBVData[i].mData != PX_INVALID_U32 && !current->isLeaf(i)) + { + const BV4Node* ChildNode = current->getChild(i); + + const PxU32 NextID = current_id; +#ifdef GU_BV4_USE_SLABS + current_id += 4; +#else + const PxU32 ChildSize = ChildNode->getType(); + current_id += ChildSize; +#endif + const PxU32 ChildType = (ChildNode->getType() - 2) << 1; + data[i].mData = size_t(ChildType + (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + //PX_ASSERT(data[i].mData == size_t(ChildType+(NextID<<3))); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = ChildNode; + NbToGo++; + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + data[i].encodePNS(current->mBVData[i].mTempPNS); +#endif +//#define DEPTH_FIRST +#ifdef DEPTH_FIRST + _Flatten(dest, NextID, current_id, ChildNode, max_depth, current_depth, CQuantCoeff, EQuantCoeff, mCenterCoeff, mExtentsCoeff, quantized); + current_depth--; +#endif + } +#ifdef GU_BV4_USE_SLABS + if (current->mBVData[i].mData == PX_INVALID_U32) + { + data[i].mAABB.mData[0].mExtents = 0; + data[i].mAABB.mData[1].mExtents = 0; + data[i].mAABB.mData[2].mExtents = 0; + data[i].mAABB.mData[0].mCenter = 0; + data[i].mAABB.mData[1].mCenter = 0; + data[i].mAABB.mData[2].mCenter = 0; + data[i].mData = PX_INVALID_U32; + } +#endif + } + +#ifndef DEPTH_FIRST + for(PxU32 i=0; i increase depth + current_depth++; + // Keep track of max depth + if(current_depth>max_depth) + max_depth = current_depth; + +// dest[box_id] = *current; + const PxU32 CurrentType = current->getType(); + for(PxU32 i=0; imBVData[i].mAABB; + dest[box_id + i].mAABB.mCenter = Box.mCenter - Box.mExtents; + dest[box_id + i].mAABB.mExtents = Box.mCenter + Box.mExtents; +#else // GU_BV4_USE_SLABS + dest[box_id + i].mAABB = current->mBVData[i].mAABB; +#endif // GU_BV4_USE_SLABS + + dest[box_id + i].mData = PxU32(current->mBVData[i].mData); +// dest[box_id+i].encodePNS(current->mBVData[i].mTempPNS); + } + + PxU32 NbToGo = 0; + PxU32 NextIDs[4] = { PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32 }; + const BV4Node* ChildNodes[4] = { NULL,NULL,NULL,NULL }; + + BVDataPackedNQ* data = dest + box_id; + for(PxU32 i=0; i<4; i++) + { + if(current->mBVData[i].mData != PX_INVALID_U32 && !current->isLeaf(i)) + { + const BV4Node* ChildNode = current->getChild(i); + + const PxU32 NextID = current_id; +#ifdef GU_BV4_USE_SLABS + current_id += 4; +#else + const PxU32 ChildSize = ChildNode->getType(); + current_id += ChildSize; +#endif + const PxU32 ChildType = (ChildNode->getType() - 2) << 1; + data[i].mData = size_t(ChildType + (NextID << GU_BV4_CHILD_OFFSET_SHIFT_COUNT)); + //PX_ASSERT(data[i].mData == size_t(ChildType+(NextID<<3))); + + NextIDs[NbToGo] = NextID; + ChildNodes[NbToGo] = ChildNode; + NbToGo++; + +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + data[i].encodePNS(current->mBVData[i].mTempPNS); +#endif +//#define DEPTH_FIRST +#ifdef DEPTH_FIRST + _Flatten(dest, NextID, current_id, ChildNode, max_depth, current_depth, CQuantCoeff, EQuantCoeff, mCenterCoeff, mExtentsCoeff, quantized); + current_depth--; +#endif + } +#ifdef GU_BV4_USE_SLABS + if (current->mBVData[i].mData == PX_INVALID_U32) + { + data[i].mAABB.mCenter = PxVec3(0.0f); + data[i].mAABB.mExtents = PxVec3(0.0f); + data[i].mData = PX_INVALID_U32; + } +#endif + } + +#ifndef DEPTH_FIRST + for(PxU32 i=0; imQuantized = quantized; +#else + T->mQuantized = true; +#endif + + // Version with variable-sized nodes in single stream + { + const PxU32 NbSingleNodes = Params.mStats[0] * 2 + (Params.mStats[1] + Params.mStats[2]) * 3 + Params.mStats[3] * 4; + + PxU32 CurID = Root->getType(); + PxU32 InitData = PX_INVALID_U32; +#ifdef GU_BV4_USE_SLABS + PX_UNUSED(NbSingleNodes); + const PxU32 NbNeeded = (Params.mStats[0] + Params.mStats[1] + Params.mStats[2] + Params.mStats[3]) * 4; + + // PT: TODO: refactor with code in BV4Tree::load +// BVDataPacked* Nodes = reinterpret_cast(PX_ALLOC(sizeof(BVDataPacked)*NbNeeded, "BV4 nodes")); // PT: PX_NEW breaks alignment here +// BVDataPacked* Nodes = PX_NEW(BVDataPacked)[NbNeeded]; + void* nodes; + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + const PxU32 nodeSize = T->mQuantized ? sizeof(BVDataPackedQ) : sizeof(BVDataPackedNQ); + #else + const PxU32 nodeSize = sizeof(BVDataPackedQ); + #endif + const PxU32 dataSize = nodeSize*NbNeeded; + nodes = PX_ALLOC(dataSize, "BV4 nodes"); // PT: PX_NEW breaks alignment here + } + + if (CurID == 2) + { + InitData = 0; + } + else if (CurID == 3) + { + InitData = 2; + } + else if (CurID == 4) + { + InitData = 4; + } + + CurID = 4; +// PxU32 CurID = 4; +// PxU32 InitData = 4; +#else + BVDataPacked* Nodes = PX_NEW(BVDataPacked)[NbSingleNodes]; + + if (CurID == 2) + { + InitData = 0; + } + else if (CurID == 3) + { + InitData = 2; + } + else if (CurID == 4) + { + InitData = 4; + } +#endif + + T->mInitData = InitData; + PxU32 MaxDepth = 0; + PxU32 CurrentDepth = 0; + + PxVec3 CQuantCoeff(0.0f); + PxVec3 EQuantCoeff(0.0f); + if(T->mQuantized) + { +#ifdef GU_BV4_USE_SLABS + PxVec3 MinQuantCoeff, MaxQuantCoeff; + + // Get max values + PxVec3 MinMax(-FLT_MAX); + PxVec3 MaxMax(-FLT_MAX); + _ComputeMaxValues(Root, MinMax, MaxMax); + + const PxU32 nbm = 15; + + // Compute quantization coeffs + const float MinCoeff = float((1 << nbm) - 1); + const float MaxCoeff = float((1 << nbm) - 1); + MinQuantCoeff.x = MinMax.x != 0.0f ? MinCoeff / MinMax.x : 0.0f; + MinQuantCoeff.y = MinMax.y != 0.0f ? MinCoeff / MinMax.y : 0.0f; + MinQuantCoeff.z = MinMax.z != 0.0f ? MinCoeff / MinMax.z : 0.0f; + MaxQuantCoeff.x = MaxMax.x != 0.0f ? MaxCoeff / MaxMax.x : 0.0f; + MaxQuantCoeff.y = MaxMax.y != 0.0f ? MaxCoeff / MaxMax.y : 0.0f; + MaxQuantCoeff.z = MaxMax.z != 0.0f ? MaxCoeff / MaxMax.z : 0.0f; + // Compute and save dequantization coeffs + T->mCenterOrMinCoeff.x = MinMax.x / MinCoeff; + T->mCenterOrMinCoeff.y = MinMax.y / MinCoeff; + T->mCenterOrMinCoeff.z = MinMax.z / MinCoeff; + T->mExtentsOrMaxCoeff.x = MaxMax.x / MaxCoeff; + T->mExtentsOrMaxCoeff.y = MaxMax.y / MaxCoeff; + T->mExtentsOrMaxCoeff.z = MaxMax.z / MaxCoeff; + + CQuantCoeff = MinQuantCoeff; + EQuantCoeff = MaxQuantCoeff; +#else + // Get max values + PxVec3 CMax(-FLT_MAX); + PxVec3 EMax(-FLT_MAX); + _ComputeMaxValues(Root, CMax, EMax); + + const PxU32 nbc = 15; + const PxU32 nbe = 16; +// const PxU32 nbc=7; +// const PxU32 nbe=8; + + const float UnitQuantError = 2.0f / 65535.0f; + EMax.x += CMax.x*UnitQuantError; + EMax.y += CMax.y*UnitQuantError; + EMax.z += CMax.z*UnitQuantError; + + // Compute quantization coeffs + const float CCoeff = float((1 << nbc) - 1); + CQuantCoeff.x = CMax.x != 0.0f ? CCoeff / CMax.x : 0.0f; + CQuantCoeff.y = CMax.y != 0.0f ? CCoeff / CMax.y : 0.0f; + CQuantCoeff.z = CMax.z != 0.0f ? CCoeff / CMax.z : 0.0f; + const float ECoeff = float((1 << nbe) - 32); + EQuantCoeff.x = EMax.x != 0.0f ? ECoeff / EMax.x : 0.0f; + EQuantCoeff.y = EMax.y != 0.0f ? ECoeff / EMax.y : 0.0f; + EQuantCoeff.z = EMax.z != 0.0f ? ECoeff / EMax.z : 0.0f; + // Compute and save dequantization coeffs + T->mCenterOrMinCoeff.x = CMax.x / CCoeff; + T->mCenterOrMinCoeff.y = CMax.y / CCoeff; + T->mCenterOrMinCoeff.z = CMax.z / CCoeff; + T->mExtentsOrMaxCoeff.x = EMax.x / ECoeff; + T->mExtentsOrMaxCoeff.y = EMax.y / ECoeff; + T->mExtentsOrMaxCoeff.z = EMax.z / ECoeff; +#endif + _FlattenQ(reinterpret_cast(nodes), 0, CurID, Root, MaxDepth, CurrentDepth, CQuantCoeff, EQuantCoeff, T->mCenterOrMinCoeff, T->mExtentsOrMaxCoeff); + } + else + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + _FlattenNQ(reinterpret_cast(nodes), 0, CurID, Root, MaxDepth, CurrentDepth, CQuantCoeff, EQuantCoeff, T->mCenterOrMinCoeff, T->mExtentsOrMaxCoeff); + #else + PX_ASSERT(0); + #endif + } + +#ifdef GU_BV4_USE_NODE_POOLS + Params.releaseNodes(); +#endif + +#ifdef GU_BV4_USE_SLABS + // PT: TODO: revisit this, don't duplicate everything + if(T->mQuantized) + { + BVDataPackedQ* _Nodes = reinterpret_cast(nodes); + + PX_COMPILE_TIME_ASSERT(sizeof(BVDataSwizzledQ) == sizeof(BVDataPackedQ) * 4); + BVDataPackedQ* Copy = PX_NEW(BVDataPackedQ)[NbNeeded]; + PxMemCopy(Copy, nodes, sizeof(BVDataPackedQ)*NbNeeded); + for (PxU32 i = 0; i(_Nodes + i * 4); + for (PxU32 j = 0; j<4; j++) + { + // We previously stored m/M within c/e so we just need to swizzle now + const QuantizedAABB& Box = Src[j].mAABB; + Dst->mX[j].mMin = Box.mData[0].mCenter; + Dst->mY[j].mMin = Box.mData[1].mCenter; + Dst->mZ[j].mMin = Box.mData[2].mCenter; + Dst->mX[j].mMax = PxI16(Box.mData[0].mExtents); + Dst->mY[j].mMax = PxI16(Box.mData[1].mExtents); + Dst->mZ[j].mMax = PxI16(Box.mData[2].mExtents); + Dst->mData[j] = Src[j].mData; + } + } + DELETEARRAY(Copy); + } + else + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + BVDataPackedNQ* _Nodes = reinterpret_cast(nodes); + + PX_COMPILE_TIME_ASSERT(sizeof(BVDataSwizzledNQ) == sizeof(BVDataPackedNQ) * 4); + BVDataPackedNQ* Copy = PX_NEW(BVDataPackedNQ)[NbNeeded]; + PxMemCopy(Copy, nodes, sizeof(BVDataPackedNQ)*NbNeeded); + for (PxU32 i = 0; i(_Nodes + i * 4); + for (PxU32 j = 0; j<4; j++) + { + // We previously stored m/M within c/e so we just need to swizzle now + const CenterExtents& Box = Src[j].mAABB; + Dst->mMinX[j] = Box.mCenter.x; + Dst->mMinY[j] = Box.mCenter.y; + Dst->mMinZ[j] = Box.mCenter.z; + Dst->mMaxX[j] = Box.mExtents.x; + Dst->mMaxY[j] = Box.mExtents.y; + Dst->mMaxZ[j] = Box.mExtents.z; + Dst->mData[j] = Src[j].mData; + } + } + DELETEARRAY(Copy); + + if(0) + { + const PxVec3 eps(epsilon); + float maxError = 0.0f; + PxU32 nb = NbNeeded/4; + BVDataSwizzledNQ* data = reinterpret_cast(nodes); + while(nb--) + { + BVDataSwizzledNQ* current = data + nb; + + for(PxU32 j=0;j<4;j++) + { + if(current->getChildData(j)==PX_INVALID_U32) + continue; + + const PxBounds3 localBox( PxVec3(current->mMinX[j], current->mMinY[j], current->mMinZ[j]), + PxVec3(current->mMaxX[j], current->mMaxY[j], current->mMaxZ[j])); + PxBounds3 refitBox; + refitBox.setEmpty(); + + if(current->isLeaf(j)) + { + PxU32 primIndex = current->getPrimitive(j); + + PxU32 nbToGo = getNbPrimitives(primIndex); + VertexPointers VP; + do + { + PX_ASSERT(primIndexmMeshInterface->getNbTriangles()); + T->mMeshInterface->getTriangle(VP, primIndex); + + refitBox.include(*VP.Vertex[0]); + refitBox.include(*VP.Vertex[1]); + refitBox.include(*VP.Vertex[2]); + + primIndex++; + }while(nbToGo--); + } + else + { + PxU32 childOffset = current->getChildOffset(j); + PX_ASSERT(!(childOffset&3)); + childOffset>>=2; + PX_ASSERT(childOffset>nb); + const PxU32 childType = current->getChildType(j); + + const BVDataSwizzledNQ* next = data + childOffset; + { + if(childType>1) + { + const PxBounds3 childBox( PxVec3(next->mMinX[3], next->mMinY[3], next->mMinZ[3]), + PxVec3(next->mMaxX[3], next->mMaxY[3], next->mMaxZ[3])); + refitBox.include(childBox); + } + + if(childType>0) + { + const PxBounds3 childBox( PxVec3(next->mMinX[2], next->mMinY[2], next->mMinZ[2]), + PxVec3(next->mMaxX[2], next->mMaxY[2], next->mMaxZ[2])); + refitBox.include(childBox); + } + + { + const PxBounds3 childBox( PxVec3(next->mMinX[1], next->mMinY[1], next->mMinZ[1]), + PxVec3(next->mMaxX[1], next->mMaxY[1], next->mMaxZ[1])); + refitBox.include(childBox); + } + + { + const PxBounds3 childBox( PxVec3(next->mMinX[0], next->mMinY[0], next->mMinZ[0]), + PxVec3(next->mMaxX[0], next->mMaxY[0], next->mMaxZ[0])); + refitBox.include(childBox); + } + } + } + refitBox.minimum -= eps; + refitBox.maximum += eps; + { + float error = (refitBox.minimum - localBox.minimum).magnitude(); + if(error>maxError) + maxError = error; + } + { + float error = (refitBox.maximum - localBox.maximum).magnitude(); + if(error>maxError) + maxError = error; + } + } + } + printf("maxError: %f\n", maxError); + } +#else + PX_ASSERT(0); +#endif + } + T->mNbNodes = NbNeeded; +#else + PX_ASSERT(CurID == NbSingleNodes); + T->mNbNodes = NbSingleNodes; +#endif + T->mNodes = nodes; + } + return true; +} + +static bool BuildBV4Internal(BV4Tree& tree, const AABBTree& Source, SourceMesh* mesh, float epsilon, bool quantized) +{ + if(mesh->getNbTriangles()<=4) + return tree.init(mesh, Source.getBV()); + + { + struct Local + { + static void _CheckMD(const AABBTreeNode* current_node, PxU32& md, PxU32& cd) + { + cd++; + md = PxMax(md, cd); + + if(current_node->getPos()) { _CheckMD(current_node->getPos(), md, cd); cd--; } + if(current_node->getNeg()) { _CheckMD(current_node->getNeg(), md, cd); cd--; } + } + + static void _Check(AABBTreeNode* current_node) + { + if(current_node->isLeaf()) + return; + + AABBTreeNode* P = const_cast(current_node->getPos()); + AABBTreeNode* N = const_cast(current_node->getNeg()); + { + PxU32 MDP = 0; PxU32 CDP = 0; _CheckMD(P, MDP, CDP); + PxU32 MDN = 0; PxU32 CDN = 0; _CheckMD(N, MDN, CDN); + + if(MDP>MDN) +// if(MDP(Source.getNodes())); + } + + BV4BuildParams Params(epsilon); + Params.mNbNodes=1; // Root node + Params.mStats[0]=0; + Params.mStats[1]=0; + Params.mStats[2]=0; + Params.mStats[3]=0; + +#ifdef GU_BV4_USE_NODE_POOLS + BV4Node* Root = Params.allocateNode(); +#else + BV4Node* Root = PX_NEW(BV4Node); +#endif + _BuildBV4(Source, Root, Source.getNodes(), Params); + + if(!tree.init(mesh, Source.getBV())) + return false; + + return BuildBV4FromRoot(tree, Root, Params, quantized, epsilon); +} + +///// + +#define REORDER_STATS_SIZE 16 +struct ReorderDataBase +{ +public: + PxU32* mOrder; + PxU32 mNbPrimsPerLeaf; + PxU32 mIndex; + PxU32 mNbPrims; + PxU32 mStats[REORDER_STATS_SIZE]; +}; + +struct ReorderData : public ReorderDataBase +{ +public: + const SourceMesh* mMesh; +}; + +static bool gReorderCallback(const AABBTreeNode* current, PxU32 /*depth*/, void* userData) +{ + ReorderData* Data = reinterpret_cast(userData); + if(current->isLeaf()) + { + const PxU32 n = current->getNbPrimitives(); + PX_ASSERT(n<=Data->mNbPrimsPerLeaf); + Data->mStats[n]++; + PxU32* Prims = const_cast(current->getPrimitives()); + + for(PxU32 i=0;imNbPrims); + Data->mOrder[Data->mIndex] = Prims[i]; + PX_ASSERT(Data->mIndexmNbPrims); + Prims[i] = Data->mIndex; + Data->mIndex++; + } + } + return true; +} + +bool physx::Gu::BuildBV4Ex(BV4Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf) +{ + const PxU32 nbTris = mesh.mNbTris; + + AABBTree Source; + if(!Source.buildFromMesh(mesh, nbTrisPerLeaf)) + return false; + + { + PxU32* order = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTris, "BV4")); + ReorderData RD; + RD.mMesh = &mesh; + RD.mOrder = order; + RD.mNbPrimsPerLeaf = nbTrisPerLeaf; + RD.mIndex = 0; + RD.mNbPrims = nbTris; + for(PxU32 i=0;i no release + mNbPrimitives = 0; + } + // Data access + PX_FORCE_INLINE const PxBounds3& getAABB() const { return mBV; } + + PX_FORCE_INLINE const AABBTreeNode* getPos() const { return reinterpret_cast(mPos); } + PX_FORCE_INLINE const AABBTreeNode* getNeg() const { const AABBTreeNode* P = getPos(); return P ? P+1 : NULL; } + + PX_FORCE_INLINE bool isLeaf() const { return !getPos(); } + + PxBounds3 mBV; // Global bounding-volume enclosing all the node-related primitives + size_t mPos; // "Positive" & "Negative" children + + // Data access + PX_FORCE_INLINE const PxU32* getPrimitives() const { return mNodePrimitives; } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mNbPrimitives; } + + PxU32* mNodePrimitives; //!< Node-related primitives (shortcut to a position in mIndices below) + PxU32 mNbPrimitives; //!< Number of primitives for this node + }; + + typedef bool (*WalkingCallback) (const AABBTreeNode* current, PxU32 depth, void* userData); + + // PT: TODO: refactor with SQ version (TA34704) + class AABBTree : public physx::shdfnd::UserAllocated + { + public: + AABBTree(); + ~AABBTree(); + + bool buildFromMesh(SourceMesh& mesh, PxU32 limit); + void release(); + + PX_FORCE_INLINE const PxU32* getIndices() const { return mIndices; } //!< Catch the indices + PX_FORCE_INLINE PxU32 getNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes + + PX_FORCE_INLINE const PxU32* getPrimitives() const { return mPool->mNodePrimitives; } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mPool->mNbPrimitives; } + PX_FORCE_INLINE const AABBTreeNode* getNodes() const { return mPool; } + PX_FORCE_INLINE const PxBounds3& getBV() const { return mPool->mBV; } + + PxU32 walk(WalkingCallback callback, void* userData) const; + private: + PxU32* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation). + AABBTreeNode* mPool; //!< Linear pool of nodes for complete trees. Null otherwise. [Opcode 1.3] + PxU32 mTotalNbNodes; //!< Number of nodes in the tree. + }; + + PX_PHYSX_COMMON_API bool BuildBV4Ex(BV4Tree& tree, SourceMesh& mesh, float epsilon, PxU32 nbTrisPerLeaf); + +} // namespace Gu +} + +#endif // GU_BV4_BUILD_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h new file mode 100644 index 000000000..f0dca6e4a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h @@ -0,0 +1,40 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SETTINGS_H +#define GU_BV4_SETTINGS_H + + // PT: "BV4" ported from "Opcode 2.0". Available compile-time options are: + #define GU_BV4_STACK_SIZE 256 // Default size of local stacks for non-recursive traversals. + #define GU_BV4_PRECOMPUTED_NODE_SORT // Use node sorting or not. This should probably always be enabled. +// #define GU_BV4_QUANTIZED_TREE // Use AABB quantization/compression or not. + #define GU_BV4_USE_SLABS // Use swizzled data format or not. Swizzled = faster raycasts, but slower overlaps & larger trees. +// #define GU_BV4_COMPILE_NON_QUANTIZED_TREE // + +#endif // GU_BV4_SETTINGS_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h new file mode 100644 index 000000000..a6cdd6978 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h @@ -0,0 +1,114 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_AABB_AABB_SWEEP_TEST_H +#define GU_BV4_AABB_AABB_SWEEP_TEST_H + +#ifndef GU_BV4_USE_SLABS +#if PX_INTEL_FAMILY + PX_FORCE_INLINE Ps::IntBool BV4_SegmentAABBOverlap(const PxVec3& center, const PxVec3& extents, const PxVec3& extents2, const RayParams* PX_RESTRICT params) + { + const PxU32 maskI = 0x7fffffff; + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V extentsV = V4Add(V4LoadU(&extents.x), V4LoadU(&extents2.x)); + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), V4LoadU(¢er.x)); + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); + absDV = _mm_cmpgt_ps(absDV, V4Add(extentsV, fdirV)); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + if(test2&7) + return 0; + return 1; + } + } + +#ifdef GU_BV4_QUANTIZED_TREE + template + PX_FORCE_INLINE Ps::IntBool BV4_SegmentAABBOverlap(const T* PX_RESTRICT node, const PxVec3& extents2, const RayParams* PX_RESTRICT params) + { + const __m128i testV = _mm_load_si128((__m128i*)node->mAABB.mData); + const __m128i qextentsV = _mm_and_si128(testV, _mm_set1_epi32(0x0000ffff)); + const __m128i qcenterV = _mm_srai_epi32(testV, 16); + const Vec4V centerV0 = V4Mul(_mm_cvtepi32_ps(qcenterV), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + const Vec4V extentsV0 = V4Mul(_mm_cvtepi32_ps(qextentsV), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + + const PxU32 maskI = 0x7fffffff; + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V extentsV = V4Add(extentsV0, V4LoadU(&extents2.x)); + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), centerV0); + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); + absDV = _mm_cmpgt_ps(absDV, V4Add(extentsV, fdirV)); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + if(test2&7) + return 0; + return 1; + } + } +#endif +#endif +#endif + +#endif // GU_BV4_AABB_AABB_SWEEP_TEST_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp new file mode 100644 index 000000000..17188acfa --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp @@ -0,0 +1,39 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) +#define SWEEP_AABB_IMPL +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; +#include "GuBV4_BoxSweep_Internal.h" +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h new file mode 100644 index 000000000..29bd6a888 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h @@ -0,0 +1,201 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_BOX_BOX_OVERLAP_TEST_H +#define GU_BV4_BOX_BOX_OVERLAP_TEST_H + +#if PX_INTEL_FAMILY +#ifndef GU_BV4_USE_SLABS + PX_FORCE_INLINE Ps::IntBool BV4_BoxBoxOverlap(const PxVec3& extents, const PxVec3& center, const OBBTestParams* PX_RESTRICT params) + { + const PxU32 maskI = 0x7fffffff; + + const Vec4V extentsV = V4LoadU(&extents.x); + + const Vec4V TV = V4Sub(V4LoadA_Safe(¶ms->mTBoxToModel_PaddedAligned.x), V4LoadU(¢er.x)); + { + __m128 absTV = _mm_and_ps(TV, _mm_load1_ps((float*)&maskI)); + absTV = _mm_cmpgt_ps(absTV, V4Add(extentsV, V4LoadA_Safe(¶ms->mBB_PaddedAligned.x))); + const PxU32 test = (PxU32)_mm_movemask_ps(absTV); + if(test&7) + return 0; + } + + __m128 tV; + { + const __m128 T_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,0,2,1))); + const __m128 T_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,1,0,2))); + + tV = V4Mul(TV, V4LoadA_Safe(¶ms->mPreca0_PaddedAligned.x)); + tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(¶ms->mPreca1_PaddedAligned.x))); + tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(¶ms->mPreca2_PaddedAligned.x))); + } + + __m128 t2V; + { + const __m128 extents_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 extents_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,1,0,2))); + + t2V = V4Mul(extentsV, V4LoadA_Safe(¶ms->mPreca0b_PaddedAligned.x)); + t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(¶ms->mPreca1b_PaddedAligned.x))); + t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(¶ms->mPreca2b_PaddedAligned.x))); + t2V = V4Add(t2V, V4LoadA_Safe(¶ms->mBoxExtents_PaddedAligned.x)); + } + + { + __m128 abstV = _mm_and_ps(tV, _mm_load1_ps((float*)&maskI)); + abstV = _mm_cmpgt_ps(abstV, t2V); + const PxU32 test = (PxU32)_mm_movemask_ps(abstV); + if(test&7) + return 0; + } + return 1; + } + +#ifdef GU_BV4_QUANTIZED_TREE + template + PX_FORCE_INLINE Ps::IntBool BV4_BoxBoxOverlap(const T* PX_RESTRICT node, const OBBTestParams* PX_RESTRICT params) + { +#define NEW_VERSION +#ifdef NEW_VERSION + SSE_CONST4(maskV, 0x7fffffff); + SSE_CONST4(maskQV, 0x0000ffff); +#else + const PxU32 maskI = 0x7fffffff; +#endif + + Vec4V centerV = V4LoadA((float*)node->mAABB.mData); +#ifdef NEW_VERSION + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), SSE_CONST(maskQV))); +#else + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), _mm_set1_epi32(0x0000ffff))); +#endif + extentsV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(extentsV)), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + centerV = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(centerV), 16)); + centerV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(centerV)), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + + const Vec4V TV = V4Sub(V4LoadA_Safe(¶ms->mTBoxToModel_PaddedAligned.x), centerV); + { +#ifdef NEW_VERSION + __m128 absTV = _mm_and_ps(TV, SSE_CONSTF(maskV)); +#else + __m128 absTV = _mm_and_ps(TV, _mm_load1_ps((float*)&maskI)); +#endif + + absTV = _mm_cmpgt_ps(absTV, V4Add(extentsV, V4LoadA_Safe(¶ms->mBB_PaddedAligned.x))); + const PxU32 test = (PxU32)_mm_movemask_ps(absTV); + if(test&7) + return 0; + } + + __m128 tV; + { + const __m128 T_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,0,2,1))); + const __m128 T_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,1,0,2))); + + tV = V4Mul(TV, V4LoadA_Safe(¶ms->mPreca0_PaddedAligned.x)); + tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(¶ms->mPreca1_PaddedAligned.x))); + tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(¶ms->mPreca2_PaddedAligned.x))); + } + + __m128 t2V; + { + const __m128 extents_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 extents_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,1,0,2))); + + t2V = V4Mul(extentsV, V4LoadA_Safe(¶ms->mPreca0b_PaddedAligned.x)); + t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(¶ms->mPreca1b_PaddedAligned.x))); + t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(¶ms->mPreca2b_PaddedAligned.x))); + t2V = V4Add(t2V, V4LoadA_Safe(¶ms->mBoxExtents_PaddedAligned.x)); + } + + { +#ifdef NEW_VERSION + __m128 abstV = _mm_and_ps(tV, SSE_CONSTF(maskV)); +#else + __m128 abstV = _mm_and_ps(tV, _mm_load1_ps((float*)&maskI)); +#endif + abstV = _mm_cmpgt_ps(abstV, t2V); + const PxU32 test = (PxU32)_mm_movemask_ps(abstV); + if(test&7) + return 0; + } + return 1; + } +#endif // GU_BV4_QUANTIZED_TREE +#endif // GU_BV4_USE_SLABS + +#ifdef GU_BV4_USE_SLABS + PX_FORCE_INLINE Ps::IntBool BV4_BoxBoxOverlap(const __m128 boxCenter, const __m128 extentsV, const OBBTestParams* PX_RESTRICT params) + { + const PxU32 maskI = 0x7fffffff; + + const Vec4V TV = V4Sub(V4LoadA_Safe(¶ms->mTBoxToModel_PaddedAligned.x), boxCenter); + { + __m128 absTV = _mm_and_ps(TV, _mm_load1_ps(reinterpret_cast(&maskI))); + absTV = _mm_cmpgt_ps(absTV, V4Add(extentsV, V4LoadA_Safe(¶ms->mBB_PaddedAligned.x))); + const PxU32 test = PxU32(_mm_movemask_ps(absTV)); + if(test&7) + return 0; + } + + __m128 tV; + { + const __m128 T_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,0,2,1))); + const __m128 T_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(TV), _MM_SHUFFLE(3,1,0,2))); + + tV = V4Mul(TV, V4LoadA_Safe(¶ms->mPreca0_PaddedAligned.x)); + tV = V4Add(tV, V4Mul(T_YZX_V, V4LoadA_Safe(¶ms->mPreca1_PaddedAligned.x))); + tV = V4Add(tV, V4Mul(T_ZXY_V, V4LoadA_Safe(¶ms->mPreca2_PaddedAligned.x))); + } + + __m128 t2V; + { + const __m128 extents_YZX_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 extents_ZXY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,1,0,2))); + + t2V = V4Mul(extentsV, V4LoadA_Safe(¶ms->mPreca0b_PaddedAligned.x)); + t2V = V4Add(t2V, V4Mul(extents_YZX_V, V4LoadA_Safe(¶ms->mPreca1b_PaddedAligned.x))); + t2V = V4Add(t2V, V4Mul(extents_ZXY_V, V4LoadA_Safe(¶ms->mPreca2b_PaddedAligned.x))); + t2V = V4Add(t2V, V4LoadA_Safe(¶ms->mBoxExtents_PaddedAligned.x)); + } + + { + __m128 abstV = _mm_and_ps(tV, _mm_load1_ps(reinterpret_cast(&maskI))); + abstV = _mm_cmpgt_ps(abstV, t2V); + const PxU32 test = PxU32(_mm_movemask_ps(abstV)); + if(test&7) + return 0; + } + return 1; + } +#endif // GU_BV4_USE_SLABS +#endif // PX_INTEL_FAMILY + +#endif // GU_BV4_BOX_BOX_OVERLAP_TEST_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp new file mode 100644 index 000000000..64603e3ba --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp @@ -0,0 +1,463 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuInternal.h" +#include "GuDistancePointSegment.h" +#include "GuIntersectionCapsuleTriangle.h" +#include "GuIntersectionTriangleBox.h" + +#include "GuBV4_BoxOverlap_Internal.h" +#include "GuBV4_BoxBoxOverlapTest.h" + +// Box overlap any + +struct OBBParams : OBBTestParams +{ + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + + PxMat33 mRModelToBox_Padded; //!< Rotation from model space to obb space + Vec3p mTModelToBox_Padded; //!< Translation from model space to obb space +}; + +// PT: TODO: this used to be inlined so we lost some perf by moving to PhysX's version. Revisit. (TA34704) +Ps::IntBool intersectTriangleBoxBV4(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, + const PxMat33& rotModelToBox, const PxVec3& transModelToBox, const PxVec3& extents); +namespace +{ +class LeafFunction_BoxOverlapAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +template +static PX_FORCE_INLINE void setupBoxParams(ParamsT* PX_RESTRICT params, const Box& localBox, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh) +{ + invertBoxMatrix(params->mRModelToBox_Padded, params->mTModelToBox_Padded, localBox); + params->mTBoxToModel_PaddedAligned = localBox.center; + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + params->precomputeBoxData(localBox.extents, &localBox.rot); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "GuBV4_BoxBoxOverlapTest.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamNoOrder_OBBOBB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_SwizzledNoOrder.h" +#endif + +#define GU_BV4_PROCESS_STREAM_NO_ORDER +#include "GuBV4_Internal.h" + +Ps::IntBool BV4_OverlapBoxAny(const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + Box localBox; + computeLocalBox(localBox, box, worldm_Aligned); + + OBBParams Params; + setupBoxParams(&Params, localBox, &tree, mesh); + + if(tree.mNodes) + return processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + return LeafFunction_BoxOverlapAny::doLeafTest(&Params, nbTris); + } +} + + +// Box overlap all + +struct OBBParamsAll : OBBParams +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxU32* mHits; +}; + +namespace +{ +class LeafFunction_BoxOverlapAll +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned)) + { + OBBParamsAll* ParamsAll = static_cast(params); + if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits) + return 1; + ParamsAll->mHits[ParamsAll->mNbHits] = primIndex; + ParamsAll->mNbHits++; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +} + +PxU32 BV4_OverlapBoxAll(const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + Box localBox; + computeLocalBox(localBox, box, worldm_Aligned); + + OBBParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = size; + Params.mHits = results; + setupBoxParams(&Params, localBox, &tree, mesh); + + if(tree.mNodes) + overflow = processStreamNoOrder(tree, &Params)!=0; + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + overflow = LeafFunction_BoxOverlapAll::doLeafTest(&Params, nbTris)!=0; + } + return Params.mNbHits; +} + +// Box overlap - callback version + +struct OBBParamsCB : OBBParams +{ + MeshOverlapCallback mCallback; + void* mUserData; +}; + +namespace +{ +class LeafFunction_BoxOverlapCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const OBBParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(intersectTriangleBoxBV4(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params->mRModelToBox_Padded, params->mTModelToBox_Padded, params->mBoxExtents_PaddedAligned)) + { + const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + if((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], primIndex, vrefs)) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +void BV4_OverlapBoxCB(const Box& localBox, const BV4Tree& tree, MeshOverlapCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + OBBParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupBoxParams(&Params, localBox, &tree, mesh); + + if(tree.mNodes) + processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + LeafFunction_BoxOverlapCB::doLeafTest(&Params, nbTris); + } +} + +// Capsule overlap any + +struct CapsuleParamsAny : OBBParams +{ + Capsule mLocalCapsule; // Capsule in mesh space + CapsuleTriangleOverlapData mData; +}; + +// PT: TODO: try to refactor this one with the PhysX version (TA34704) +static bool CapsuleVsTriangle_SAT(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const CapsuleParamsAny* PX_RESTRICT params) +{ +// PX_ASSERT(capsule.p0!=capsule.p1); + + { + const PxReal d2 = distancePointSegmentSquaredInternal(params->mLocalCapsule.p0, params->mData.mCapsuleDir, p0); + if(d2<=params->mLocalCapsule.radius*params->mLocalCapsule.radius) + return 1; + } + + const PxVec3 N = (p0 - p1).cross(p0 - p2); + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, N)) + return 0; + + const float BDotB = params->mData.mBDotB; + const float oneOverBDotB = params->mData.mOneOverBDotB; + const PxVec3& capP0 = params->mLocalCapsule.p0; + const PxVec3& capDir = params->mData.mCapsuleDir; + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p0, p1 - p0, capP0, capDir, BDotB, oneOverBDotB))) + return 0; + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p1, p2 - p1, capP0, capDir, BDotB, oneOverBDotB))) + return 0; + + if(!testAxis(p0, p1, p2, params->mLocalCapsule, computeEdgeAxis(p2, p0 - p2, capP0, capDir, BDotB, oneOverBDotB))) + return 0; + + return 1; +} + +static Ps::IntBool PX_FORCE_INLINE __CapsuleTriangle(const CapsuleParamsAny* PX_RESTRICT params, PxU32 primIndex) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + return CapsuleVsTriangle_SAT(params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params); +} + +namespace +{ +class LeafFunction_CapsuleOverlapAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__CapsuleTriangle(static_cast(params), primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +template +static PX_FORCE_INLINE void setupCapsuleParams(ParamsT* PX_RESTRICT params, const Capsule& capsule, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh) +{ + computeLocalCapsule(params->mLocalCapsule, capsule, worldm_Aligned); + + params->mData.init(params->mLocalCapsule); + + Box localBox; + computeBoxAroundCapsule(params->mLocalCapsule, localBox); + + setupBoxParams(params, localBox, tree, mesh); +} + +Ps::IntBool BV4_OverlapCapsuleAny(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleParamsAny Params; + setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + return processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + return LeafFunction_CapsuleOverlapAny::doLeafTest(&Params, nbTris); + } +} + + +// Capsule overlap all + +struct CapsuleParamsAll : CapsuleParamsAny +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxU32* mHits; +}; + +namespace +{ +class LeafFunction_CapsuleOverlapAll +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(OBBParams* PX_RESTRICT params, PxU32 primIndex) + { + CapsuleParamsAll* ParamsAll = static_cast(params); + + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__CapsuleTriangle(ParamsAll, primIndex)) + { + if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits) + return 1; + ParamsAll->mHits[ParamsAll->mNbHits] = primIndex; + ParamsAll->mNbHits++; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +PxU32 BV4_OverlapCapsuleAll(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = size; + Params.mHits = results; + setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + overflow = processStreamNoOrder(tree, &Params)!=0; + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + overflow = LeafFunction_CapsuleOverlapAll::doLeafTest(&Params, nbTris)!=0; + } + return Params.mNbHits; +} + +// Capsule overlap - callback version + +struct CapsuleParamsCB : CapsuleParamsAny +{ + MeshOverlapCallback mCallback; + void* mUserData; +}; + +namespace +{ +class LeafFunction_CapsuleOverlapCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const CapsuleParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + if(CapsuleVsTriangle_SAT(p0, p1, p2, params)) + { + const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + if((params->mCallback)(params->mUserData, p0, p1, p2, primIndex, vrefs)) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: this one is currently not used +void BV4_OverlapCapsuleCB(const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupCapsuleParams(&Params, capsule, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + LeafFunction_CapsuleOverlapCB::doLeafTest(&Params, nbTris); + } +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h new file mode 100644 index 000000000..5d89f5012 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_BOX_OVERLAP_INTERNAL_H +#define GU_BV4_BOX_OVERLAP_INTERNAL_H + +#include "GuBV4_Common.h" + + template + PX_FORCE_INLINE void precomputeData(ParamsT* PX_RESTRICT dst, PxMat33* PX_RESTRICT absRot, const PxMat33* PX_RESTRICT boxToModelR) + { + // Precompute absolute box-to-model rotation matrix + dst->mPreca0_PaddedAligned.x = boxToModelR->column0.x; + dst->mPreca0_PaddedAligned.y = boxToModelR->column1.y; + dst->mPreca0_PaddedAligned.z = boxToModelR->column2.z; + + dst->mPreca1_PaddedAligned.x = boxToModelR->column0.y; + dst->mPreca1_PaddedAligned.y = boxToModelR->column1.z; + dst->mPreca1_PaddedAligned.z = boxToModelR->column2.x; + + dst->mPreca2_PaddedAligned.x = boxToModelR->column0.z; + dst->mPreca2_PaddedAligned.y = boxToModelR->column1.x; + dst->mPreca2_PaddedAligned.z = boxToModelR->column2.y; + + // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID) + const PxReal epsilon = 1e-6f; + absRot->column0.x = dst->mPreca0b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.x); + absRot->column0.y = dst->mPreca1b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.y); + absRot->column0.z = dst->mPreca2b_PaddedAligned.x = epsilon + fabsf(boxToModelR->column0.z); + + absRot->column1.x = dst->mPreca2b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.x); + absRot->column1.y = dst->mPreca0b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.y); + absRot->column1.z = dst->mPreca1b_PaddedAligned.y = epsilon + fabsf(boxToModelR->column1.z); + + absRot->column2.x = dst->mPreca1b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.x); + absRot->column2.y = dst->mPreca2b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.y); + absRot->column2.z = dst->mPreca0b_PaddedAligned.z = epsilon + fabsf(boxToModelR->column2.z); + } + + template + PX_FORCE_INLINE void setupBoxData(ParamsT* PX_RESTRICT dst, const PxVec3& extents, const PxMat33* PX_RESTRICT mAR) + { + dst->mBoxExtents_PaddedAligned = extents; + + const float Ex = extents.x; + const float Ey = extents.y; + const float Ez = extents.z; + dst->mBB_PaddedAligned.x = Ex*mAR->column0.x + Ey*mAR->column1.x + Ez*mAR->column2.x; + dst->mBB_PaddedAligned.y = Ex*mAR->column0.y + Ey*mAR->column1.y + Ez*mAR->column2.y; + dst->mBB_PaddedAligned.z = Ex*mAR->column0.z + Ey*mAR->column1.z + Ez*mAR->column2.z; + } + + struct OBBTestParams // Data needed to perform the OBB-OBB overlap test + { + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mTBoxToModel_PaddedAligned); //!< Translation from obb space to model space + BV4_ALIGN16(Vec3p mBB_PaddedAligned); + BV4_ALIGN16(Vec3p mBoxExtents_PaddedAligned); + + BV4_ALIGN16(Vec3p mPreca0_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca1_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca2_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca0b_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca1b_PaddedAligned); + BV4_ALIGN16(Vec3p mPreca2b_PaddedAligned); + + PX_FORCE_INLINE void precomputeBoxData(const PxVec3& extents, const PxMat33* PX_RESTRICT box_to_model) + { + PxMat33 absRot; //!< Absolute rotation matrix + precomputeData(this, &absRot, box_to_model); + + setupBoxData(this, extents, &absRot); + } + }; + +#endif // GU_BV4_BOX_OVERLAP_INTERNAL_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h new file mode 100644 index 000000000..a5ba47544 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h @@ -0,0 +1,518 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTriangleUtils.h" +#include "GuSweepBoxTriangle_FeatureBased.h" +#include "GuSweepBoxTriangle_SAT.h" +#include "GuBV4_BoxOverlap_Internal.h" + +// PT: for box-sweeps please refer to %SDKRoot%\InternalDocumentation\GU\Sweep strategies.ppt. +// We use: +// - method 3 if the box is an AABB (SWEEP_AABB_IMPL is defined) +// - method 2 if the box is an OBB (SWEEP_AABB_IMPL is undefined) + +#ifdef SWEEP_AABB_IMPL + // PT: TODO: refactor structure (TA34704) + struct RayParams + { + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); + #ifndef GU_BV4_USE_SLABS + BV4_ALIGN16(Vec3p mData2_PaddedAligned); + BV4_ALIGN16(Vec3p mFDir_PaddedAligned); + BV4_ALIGN16(Vec3p mData_PaddedAligned); + BV4_ALIGN16(Vec3p mLocalDir_PaddedAligned); + #endif + BV4_ALIGN16(Vec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) + }; + + #include "GuBV4_AABBAABBSweepTest.h" +#else + #include "GuBV4_BoxBoxOverlapTest.h" +#endif + +#include "GuBV4_BoxSweep_Params.h" + +static PX_FORCE_INLINE Vec4V multiply3x3V(const Vec4V p, const PxMat33& mat_Padded) +{ + const FloatV xxxV = V4GetX(p); + const FloatV yyyV = V4GetY(p); + const FloatV zzzV = V4GetZ(p); + + Vec4V ResV = V4Scale(V4LoadU_Safe(&mat_Padded.column0.x), xxxV); + ResV = V4Add(ResV, V4Scale(V4LoadU_Safe(&mat_Padded.column1.x), yyyV)); + ResV = V4Add(ResV, V4Scale(V4LoadU_Safe(&mat_Padded.column2.x), zzzV)); + + return ResV; +} + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ triBoxSweep(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex, bool nodeSorting=true) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + // Don't bother doing the actual sweep test if the triangle is too far away + if(1) + { + const float dp0 = p0.dot(params->mLocalDir_Padded); + const float dp1 = p1.dot(params->mLocalDir_Padded); + const float dp2 = p2.dot(params->mLocalDir_Padded); + + float TriMin = PxMin(dp0, dp1); + TriMin = PxMin(TriMin, dp2); + + if(TriMin >= params->mOffset + params->mStabbedFace.mDistance) + return false; + } + + TrianglePadded triBoxSpace; + const Vec4V transModelToBoxV = V4LoadU_Safe(¶ms->mTModelToBox_Padded.x); + const Vec4V v0V = V4Add(multiply3x3V(V4LoadU_Safe(&p0.x), params->mRModelToBox_Padded), transModelToBoxV); + V4StoreU_Safe(v0V, &triBoxSpace.verts[0].x); + const Vec4V v1V = V4Add(multiply3x3V(V4LoadU_Safe(&p1.x), params->mRModelToBox_Padded), transModelToBoxV); + V4StoreU_Safe(v1V, &triBoxSpace.verts[1].x); + const Vec4V v2V = V4Add(multiply3x3V(V4LoadU_Safe(&p2.x), params->mRModelToBox_Padded), transModelToBoxV); + V4StoreU_Safe(v2V, &triBoxSpace.verts[2].x); + + float Dist; + if(triBoxSweepTestBoxSpace_inlined(triBoxSpace, params->mOriginalExtents_Padded, params->mOriginalDir_Padded*params->mStabbedFace.mDistance, params->mOneOverDir_Padded, 1.0f, Dist, params->mBackfaceCulling)) + { + // PT: TODO: these muls & divs may not be needed at all - we just pass the unit dir/inverse dir to the sweep code. Revisit. (TA34704) + Dist *= params->mStabbedFace.mDistance; + params->mOneOverDir_Padded = params->mOneOverOriginalDir / Dist; + params->mStabbedFace.mDistance = Dist; + params->mStabbedFace.mTriangleID = primIndex; + // PT: TODO: revisit this (TA34704) + params->mP0 = triBoxSpace.verts[0]; + params->mP1 = triBoxSpace.verts[1]; + params->mP2 = triBoxSpace.verts[2]; +// V4StoreU_Safe(v0V, ¶ms->mP0.x); +// V4StoreU_Safe(v1V, ¶ms->mP1.x); +// V4StoreU_Safe(v2V, ¶ms->mP2.x); + + if(nodeSorting) + { +#ifdef SWEEP_AABB_IMPL + #ifndef GU_BV4_USE_SLABS + setupRayData(params, Dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned); + #endif +#else + params->ShrinkOBB(Dist); +#endif + } + return true; + } + return false; +} + +namespace +{ +class LeafFunction_BoxSweepClosest +{ +public: + static PX_FORCE_INLINE void doLeafTest(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + triBoxSweep(params, primIndex); + primIndex++; + }while(nbToGo--); + } +}; + +class LeafFunction_BoxSweepAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(BoxSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triBoxSweep(params, primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: TODO: refactor with sphere/capsule versions (TA34704) +static PX_FORCE_INLINE bool computeImpactData(const Box& box, const PxVec3& dir, SweepHit* PX_RESTRICT hit, const BoxSweepParams* PX_RESTRICT params, bool isDoubleSided, bool meshBothSides) +{ + if(params->mStabbedFace.mTriangleID==PX_INVALID_U32) + return false; // We didn't touch any triangle + + if(hit) + { + const float t = params->mStabbedFace.mDistance; + hit->mTriangleID = params->mStabbedFace.mTriangleID; + hit->mDistance = t; + + if(t==0.0f) + { + hit->mPos = PxVec3(0.0f); + hit->mNormal = -dir; + } + else + { + // PT: TODO: revisit/optimize/use this (TA34704) + const PxTriangle triInBoxSpace(params->mP0, params->mP1, params->mP2); + PxHitFlags outFlags = PxHitFlag::Enum(0); + computeBoxLocalImpact(hit->mPos, hit->mNormal, outFlags, box, params->mOriginalDir_Padded, triInBoxSpace, PxHitFlag::ePOSITION|PxHitFlag::eNORMAL, isDoubleSided, meshBothSides, t); + } + } + return true; +} + +template +static PX_FORCE_INLINE void setupBoxSweepParams(ParamsT* PX_RESTRICT params, const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh, PxU32 flags) +{ + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + setupParamsFlags(params, flags); + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + prepareSweepData(localBox, localDir, maxDist, params); + +#ifdef SWEEP_AABB_IMPL + params->mOrigin_Padded = localBox.center; + #ifndef GU_BV4_USE_SLABS + params->mLocalDir_PaddedAligned = localDir; + setupRayData(params, maxDist, localBox.center, localDir); + #endif +#endif +} + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#ifdef SWEEP_AABB_IMPL + #include "GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h" + #include "GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h" + #ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_KajiyaNoOrder.h" + #include "GuBV4_Slabs_KajiyaOrdered.h" + #endif +#else + #include "GuBV4_ProcessStreamOrdered_OBBOBB.h" + #include "GuBV4_ProcessStreamNoOrder_OBBOBB.h" + #ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_SwizzledNoOrder.h" + #include "GuBV4_Slabs_SwizzledOrdered.h" + #endif +#endif + +#ifdef SWEEP_AABB_IMPL + #define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER + #define GU_BV4_PROCESS_STREAM_RAY_ORDERED +#else + #define GU_BV4_PROCESS_STREAM_NO_ORDER + #define GU_BV4_PROCESS_STREAM_ORDERED +#endif +#include "GuBV4_Internal.h" + +#ifdef SWEEP_AABB_IMPL +Ps::IntBool Sweep_AABB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) +#else +Ps::IntBool Sweep_OBB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) +#endif +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + BoxSweepParams Params; + setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags); + + if(tree.mNodes) + { +#ifdef SWEEP_AABB_IMPL + if(Params.mEarlyExit) + processStreamRayNoOrder<1, LeafFunction_BoxSweepAny>(tree, &Params); + else + processStreamRayOrdered<1, LeafFunction_BoxSweepClosest>(tree, &Params); +#else + if(Params.mEarlyExit) + processStreamNoOrder(tree, &Params); + else + processStreamOrdered(tree, &Params); +#endif + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); + + return computeImpactData(localBox, localDir, hit, &Params, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + + + +// PT: box sweep callback version - currently not used + +namespace +{ + struct BoxSweepParamsCB : BoxSweepParams + { + // PT: these new members are only here to call computeImpactData during traversal :( + // PT: TODO: most of them may not be needed + Box mBoxCB; // Box in original space (maybe not local/mesh space) + PxVec3 mDirCB; // Dir in original space (maybe not local/mesh space) + const PxMat44* mWorldm_Aligned; + PxU32 mFlags; + + SweepUnlimitedCallback mCallback; + void* mUserData; + float mMaxDist; + bool mNodeSorting; + }; + +class LeafFunction_BoxSweepCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(BoxSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triBoxSweep(params, primIndex, params->mNodeSorting)) + { + // PT: TODO: in this version we must compute the impact data immediately, + // which is a terrible idea in general, but I'm not sure what else I can do. + SweepHit hit; + const bool b = computeImpactData(params->mBoxCB, params->mDirCB, &hit, params, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); + PX_ASSERT(b); + + // PT: then replicate part from BV4_BoxSweepSingle: + if(b && params->mWorldm_Aligned) + { + // Move to world space + // PT: TODO: optimize (TA34704) + hit.mPos = params->mWorldm_Aligned->transform(hit.mPos); + hit.mNormal = params->mWorldm_Aligned->rotate(hit.mNormal); + } + + reportUnlimitedCallbackHit(params, hit); + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +// PT: 'worldm_Aligned' is only here to move back results to world space, but input is already in local space. +#ifdef SWEEP_AABB_IMPL +void Sweep_AABB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +#else +void Sweep_OBB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +#endif +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + BoxSweepParamsCB Params; + Params.mBoxCB = localBox; + Params.mDirCB = localDir; + Params.mWorldm_Aligned = worldm_Aligned; + Params.mFlags = flags; + + Params.mCallback = callback; + Params.mUserData = userData; + Params.mMaxDist = maxDist; + Params.mNodeSorting = nodeSorting; + setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags); + + PX_ASSERT(!Params.mEarlyExit); + + if(tree.mNodes) + { + if(nodeSorting) + { +#ifdef SWEEP_AABB_IMPL + processStreamRayOrdered<1, LeafFunction_BoxSweepCB>(tree, &Params); +#else + processStreamOrdered(tree, &Params); +#endif + } + else + { +#ifdef SWEEP_AABB_IMPL + processStreamRayNoOrder<1, LeafFunction_BoxSweepCB>(tree, &Params); +#else + processStreamNoOrder(tree, &Params); +#endif + } + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); +} + + + + +// New callback-based box sweeps. Reuses code above, allow early exits. Some init code may be done in vain +// since the leaf tests are not performed (we don't do box-sweeps-vs-tri since the box is only a BV around +// the actual shape, say a convex) + +namespace +{ +struct GenericSweepParamsCB : BoxSweepParams +{ + MeshSweepCallback mCallback; + void* mUserData; +}; + +class LeafFunction_BoxSweepClosestCB +{ +public: + static PX_FORCE_INLINE void doLeafTest(GenericSweepParamsCB* PX_RESTRICT params, PxU32 prim_index) + { + PxU32 nbToGo = getNbPrimitives(prim_index); + do + { + // PT: in the regular version we'd do a box-vs-triangle sweep test here + // Instead we just grab the triangle and send it to the callback + // + // This can be used for regular "closest hit" sweeps, when the scale is not identity or + // when the box is just around a more complex shape (e.g. convex). In this case we want + // the calling code to compute a convex-triangle distance, and then we want to shrink + // the ray/box while doing an ordered traversal. + // + // For "sweep all" or "sweep any" purposes we want to either report all hits or early exit + // as soon as we find one. There is no need for shrinking or ordered traversals here. + + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, prim_index, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + // Don't bother doing the actual sweep test if the triangle is too far away + const float dp0 = p0.dot(params->mLocalDir_Padded); + const float dp1 = p1.dot(params->mLocalDir_Padded); + const float dp2 = p2.dot(params->mLocalDir_Padded); + + float TriMin = PxMin(dp0, dp1); + TriMin = PxMin(TriMin, dp2); + + if(TriMin < params->mOffset + params->mStabbedFace.mDistance) + { +// const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + float Dist = params->mStabbedFace.mDistance; + if((params->mCallback)(params->mUserData, p0, p1, p2, prim_index, /*vrefs,*/ Dist)) + return; // PT: TODO: we return here but the ordered path doesn't really support early exits (TA34704) + + if(DistmStabbedFace.mDistance) + { + params->mStabbedFace.mDistance = Dist; + params->mStabbedFace.mTriangleID = prim_index; +#ifdef SWEEP_AABB_IMPL + #ifndef GU_BV4_USE_SLABS + setupRayData(params, Dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned); + #endif +#else + params->ShrinkOBB(Dist); +#endif + } + } + + prim_index++; + }while(nbToGo--); + } +}; + +class LeafFunction_BoxSweepAnyCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(GenericSweepParamsCB* PX_RESTRICT params, PxU32 prim_index) + { + PxU32 nbToGo = getNbPrimitives(prim_index); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, prim_index, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + { +// const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + float Dist = params->mStabbedFace.mDistance; + if((params->mCallback)(params->mUserData, p0, p1, p2, prim_index, /*vrefs,*/ Dist)) + return 1; + } + + prim_index++; + }while(nbToGo--); + + return 0; + } +}; +} + +#ifdef SWEEP_AABB_IMPL +void GenericSweep_AABB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags) +#else +void GenericSweep_OBB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags) +#endif +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + GenericSweepParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupBoxSweepParams(&Params, localBox, localDir, maxDist, &tree, mesh, flags); + + if(tree.mNodes) + { +#ifdef SWEEP_AABB_IMPL + if(Params.mEarlyExit) + processStreamRayNoOrder<1, LeafFunction_BoxSweepAnyCB>(tree, &Params); + else + processStreamRayOrdered<1, LeafFunction_BoxSweepClosestCB>(tree, &Params); +#else + if(Params.mEarlyExit) + processStreamNoOrder(tree, &Params); + else + processStreamOrdered(tree, &Params); +#endif + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h new file mode 100644 index 000000000..a80e18e8c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h @@ -0,0 +1,211 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This is used by the box-sweep & capsule-sweep code + +#if PX_VC + #pragma warning(disable: 4505) // unreferenced local function has been removed +#endif + +#include "PsBasicTemplates.h" + +namespace +{ +#ifdef SWEEP_AABB_IMPL +struct BoxSweepParams : RayParams +#else +struct BoxSweepParams : OBBTestParams +#endif +{ + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + +#ifndef SWEEP_AABB_IMPL + Box mLocalBox; +#endif + PxVec3 mLocalDir_Padded; + RaycastHitInternal mStabbedFace; + + PxU32 mBackfaceCulling; + PxU32 mEarlyExit; + + PxVec3 mP0, mP1, mP2; + PxVec3 mBestTriNormal; + + float mOffset; + PxVec3 mProj; + PxVec3 mDP; + +#ifndef SWEEP_AABB_IMPL + PxMat33 mAR; //!< Absolute rotation matrix +#endif + + PxMat33 mRModelToBox_Padded; //!< Rotation from model space to obb space + PxVec3 mTModelToBox_Padded; //!< Translation from model space to obb space + PxVec3 mOriginalExtents_Padded; + PxVec3 mOriginalDir_Padded; + PxVec3 mOneOverDir_Padded; + PxVec3 mOneOverOriginalDir; + +#ifndef SWEEP_AABB_IMPL + PX_FORCE_INLINE void ShrinkOBB(float d) + { + const PxVec3 BoxExtents = mDP + d * mProj; + mTBoxToModel_PaddedAligned = mLocalBox.center + mLocalDir_Padded*d*0.5f; + + setupBoxData(this, BoxExtents, &mAR); + } +#endif +}; +} + +// PT: TODO: check asm again in PhysX version, compare to original (TA34704) +static void prepareSweepData(const Box& box, const PxVec3& dir, float maxDist, BoxSweepParams* PX_RESTRICT params) +{ + invertBoxMatrix(params->mRModelToBox_Padded, params->mTModelToBox_Padded, box); + + params->mOriginalExtents_Padded = box.extents; + + const PxVec3 OriginalDir = params->mRModelToBox_Padded.transform(dir); + params->mOriginalDir_Padded = OriginalDir; + + const PxVec3 OneOverOriginalDir(OriginalDir.x!=0.0f ? 1.0f/OriginalDir.x : 0.0f, + OriginalDir.y!=0.0f ? 1.0f/OriginalDir.y : 0.0f, + OriginalDir.z!=0.0f ? 1.0f/OriginalDir.z : 0.0f); + + params->mOneOverOriginalDir = OneOverOriginalDir; + params->mOneOverDir_Padded = OneOverOriginalDir / maxDist; + + { + const Box& LocalBox = box; + const PxVec3& LocalDir = dir; + + params->mLocalDir_Padded = LocalDir; + params->mStabbedFace.mDistance = maxDist; +#ifndef SWEEP_AABB_IMPL + params->mLocalBox = LocalBox; // PT: TODO: check asm for operator= +#endif + + PxMat33 boxToModelR; + + // Original code: + // OBB::CreateOBB(LocalBox, LocalDir, 0.5f) + { + PxVec3 R1, R2; + { + float dd[3]; + dd[0] = fabsf(LocalBox.rot.column0.dot(LocalDir)); + dd[1] = fabsf(LocalBox.rot.column1.dot(LocalDir)); + dd[2] = fabsf(LocalBox.rot.column2.dot(LocalDir)); + float dmax = dd[0]; + PxU32 ax0=1; + PxU32 ax1=2; + if(dd[1]>dmax) + { + dmax=dd[1]; + ax0=0; + ax1=2; + } + if(dd[2]>dmax) + { + dmax=dd[2]; + ax0=0; + ax1=1; + } + if(dd[ax1]mRBoxToModel + boxToModelR.column0 = LocalDir; + boxToModelR.column1 = R1; + boxToModelR.column2 = R2; + + // Original code: + // float Offset[3]; + // 0.5f comes from the Offset[r]*0.5f, doesn't mean 'd' is 0.5f + params->mProj.x = 0.5f; + params->mProj.y = LocalDir.dot(R1)*0.5f; + params->mProj.z = LocalDir.dot(R2)*0.5f; + + // Original code: + //mExtents[r] = Offset[r]*0.5f + fabsf(box.mRot[0]|R)*box.mExtents.x + fabsf(box.mRot[1]|R)*box.mExtents.y + fabsf(box.mRot[2]|R)*box.mExtents.z; + // => we store the first part of the computation, minus 'Offset[r]*0.5f' + for(PxU32 r=0;r<3;r++) + { + const PxVec3& R = boxToModelR[r]; + params->mDP[r] = fabsf(LocalBox.rot.column0.dot(R)*LocalBox.extents.x) + + fabsf(LocalBox.rot.column1.dot(R)*LocalBox.extents.y) + + fabsf(LocalBox.rot.column2.dot(R)*LocalBox.extents.z); + } + // In the original code, both mCenter & mExtents depend on 'd', and thus we will need to recompute these two members. + // + // For mExtents we have: + // + // float Offset[3]; + // Offset[0] = d; + // Offset[1] = d*(dir|R1); + // Offset[2] = d*(dir|R2); + // + // mExtents[r] = Offset[r]*0.5f + fabsf(box.mRot[0]|R)*box.mExtents.x + fabsf(box.mRot[1]|R)*box.mExtents.y + fabsf(box.mRot[2]|R)*box.mExtents.z; + // <=> mExtents[r] = Offset[r]*0.5f + Params.mDP[r]; We precompute the second part that doesn't depend on d, stored in mDP + // <=> mExtents[r] = Params.mProj[r]*d + Params.mDP[r]; We extract d from the first part, store what is left in mProj + // + // Thus in ShrinkOBB the code needed to update the extents is just: + // mBoxExtents = mDP + d * mProj; + // + // For mCenter we have: + // + // mCenter = box.mCenter + dir*d*0.5f; + // + // So we simply use this formula directly, with the new d. Result is stored in 'mTBoxToModel' +/* + PX_FORCE_INLINE void ShrinkOBB(float d) + { + mBoxExtents = mDP + d * mProj; + mTBoxToModel = mLocalBox.mCenter + mLocalDir*d*0.5f; +*/ + } + + // This one is for culling tris, unrelated to CreateOBB + params->mOffset = params->mDP.x + LocalBox.center.dot(LocalDir); + +#ifndef SWEEP_AABB_IMPL + precomputeData(params, ¶ms->mAR, &boxToModelR); + + params->ShrinkOBB(maxDist); +#endif + } +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp new file mode 100644 index 000000000..f0ee08a08 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp @@ -0,0 +1,173 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +#include "GuSweepSphereTriangle.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuSIMDHelpers.h" +#include "GuInternal.h" + +#include "GuBV4_BoxOverlap_Internal.h" +#include "GuBV4_BoxSweep_Params.h" + +namespace +{ + struct CapsuleSweepParams : BoxSweepParams + { + Capsule mLocalCapsule; + PxVec3 mCapsuleCenter; + PxVec3 mExtrusionDir; + float mBestAlignmentValue; + float mBestDistance; + float mMaxDist; + }; +} + +#include "GuBV4_CapsuleSweep_Internal.h" +#include "GuBV4_BoxBoxOverlapTest.h" + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamOrdered_OBBOBB.h" +#include "GuBV4_ProcessStreamNoOrder_OBBOBB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_SwizzledNoOrder.h" + #include "GuBV4_Slabs_SwizzledOrdered.h" +#endif + +#define GU_BV4_PROCESS_STREAM_NO_ORDER +#define GU_BV4_PROCESS_STREAM_ORDERED +#include "GuBV4_Internal.h" + +Ps::IntBool BV4_CapsuleSweepSingle(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleSweepParams Params; + setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamNoOrder(tree, &Params); + else + processStreamOrdered(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); + + return computeImpactDataT(capsule, dir, hit, &Params, NULL, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + +// PT: capsule sweep callback version - currently not used + +namespace +{ + struct CapsuleSweepParamsCB : CapsuleSweepParams + { + // PT: these new members are only here to call computeImpactDataT during traversal :( + // PT: TODO: most of them may not be needed + // PT: TODO: for example mCapsuleCB probably dup of mLocalCapsule + Capsule mCapsuleCB; // Capsule in original space (maybe not local/mesh space) + PxVec3 mDirCB; // Dir in original space (maybe not local/mesh space) + const PxMat44* mWorldm_Aligned; + PxU32 mFlags; + + SweepUnlimitedCallback mCallback; + void* mUserData; + bool mNodeSorting; + }; + +class LeafFunction_CapsuleSweepCB +{ +public: + + static PX_FORCE_INLINE Ps::IntBool doLeafTest(CapsuleSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triCapsuleSweep(params, primIndex, params->mNodeSorting)) + { + // PT: TODO: in this version we must compute the impact data immediately, + // which is a terrible idea in general, but I'm not sure what else I can do. + SweepHit hit; + const bool b = computeImpactDataT(params->mCapsuleCB, params->mDirCB, &hit, params, params->mWorldm_Aligned, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); + PX_ASSERT(b); + PX_UNUSED(b); + + reportUnlimitedCallbackHit(params, hit); + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +void BV4_CapsuleSweepCB(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleSweepParamsCB Params; + Params.mCapsuleCB = capsule; + Params.mDirCB = dir; + Params.mWorldm_Aligned = worldm_Aligned; + Params.mFlags = flags; + + Params.mCallback = callback; + Params.mUserData = userData; + Params.mMaxDist = maxDist; + Params.mNodeSorting = nodeSorting; + setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags); + + PX_ASSERT(!Params.mEarlyExit); + + if(tree.mNodes) + { + if(nodeSorting) + processStreamOrdered(tree, &Params); + else + processStreamNoOrder(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp new file mode 100644 index 000000000..ea1d77bf9 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +#include "GuSweepSphereTriangle.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuBV4_Common.h" +#include "GuInternal.h" + +#define SWEEP_AABB_IMPL + + // PT: TODO: refactor structure (TA34704) + struct RayParams + { + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); + #ifndef GU_BV4_USE_SLABS + BV4_ALIGN16(Vec3p mData2_PaddedAligned); + BV4_ALIGN16(Vec3p mFDir_PaddedAligned); + BV4_ALIGN16(Vec3p mData_PaddedAligned); + BV4_ALIGN16(Vec3p mLocalDir_PaddedAligned); + #endif + BV4_ALIGN16(Vec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) + }; + +#include "GuBV4_BoxSweep_Params.h" + +namespace +{ + struct CapsuleSweepParams : BoxSweepParams + { + Capsule mLocalCapsule; + PxVec3 mCapsuleCenter; + PxVec3 mExtrusionDir; + float mBestAlignmentValue; + float mBestDistance; + float mMaxDist; + }; +} + +#include "GuBV4_CapsuleSweep_Internal.h" +#include "GuBV4_AABBAABBSweepTest.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h" +#include "GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_KajiyaNoOrder.h" + #include "GuBV4_Slabs_KajiyaOrdered.h" +#endif + +#define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER +#define GU_BV4_PROCESS_STREAM_RAY_ORDERED +#include "GuBV4_Internal.h" + +Ps::IntBool BV4_CapsuleSweepSingleAA(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + CapsuleSweepParams Params; + setupCapsuleParams(&Params, capsule, dir, maxDist, &tree, mesh, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamRayNoOrder<1, LeafFunction_CapsuleSweepAny>(tree, &Params); + else + processStreamRayOrdered<1, LeafFunction_CapsuleSweepClosest>(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); + + return computeImpactDataT(capsule, dir, hit, &Params, NULL, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h new file mode 100644 index 000000000..276fe4e8c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h @@ -0,0 +1,434 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_CAPSULE_SWEEP_INTERNAL_H +#define GU_BV4_CAPSULE_SWEEP_INTERNAL_H + +// PT: for capsule-sweeps please refer to %SDKRoot%\InternalDocumentation\GU\Sweep strategies.ppt. +// We use: +// - method 3 if the capsule is axis-aligned (SWEEP_AABB_IMPL is defined) +// - method 2 otherwise (SWEEP_AABB_IMPL is undefined) + +// PT: TODO: get rid of that one +static PX_FORCE_INLINE bool sweepSphereVSTriangle( const PxVec3& center, const float radius, + const PxVec3* PX_RESTRICT triVerts, const PxVec3& triUnitNormal, + const PxVec3& unitDir, + float& curT, bool& directHit) +{ + float currentDistance; + if(!sweepSphereVSTri(triVerts, triUnitNormal, center, radius, unitDir, currentDistance, directHit, true)) + return false; + + // PT: using ">" or ">=" is enough to block the CCT or not in the DE5967 visual test. Change to ">=" if a repro is needed. + if(currentDistance > curT) + return false; + curT = currentDistance; + return true; +} + +static PX_FORCE_INLINE bool sweepSphereVSQuad( const PxVec3& center, const float radius, + const PxVec3* PX_RESTRICT quadVerts, const PxVec3& quadUnitNormal, + const PxVec3& unitDir, + float& curT) +{ + float currentDistance; + if(!sweepSphereVSQuad(quadVerts, quadUnitNormal, center, radius, unitDir, currentDistance)) + return false; + + // PT: using ">" or ">=" is enough to block the CCT or not in the DE5967 visual test. Change to ">=" if a repro is needed. + if(currentDistance > curT) + return false; + curT = currentDistance; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ testTri( const CapsuleSweepParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& N, + const PxVec3& unitDir, const float capsuleRadius, const float dpc0, float& curT, bool& status) +{ + // PT: TODO: check the assembly here (TA34704) + PxVec3 currentTri[3]; + // PT: TODO: optimize this copy (TA34704) + currentTri[0] = p0; + currentTri[1] = p1; + currentTri[2] = p2; + + // PT: beware, culling is only ok on the sphere I think + if(rejectTriangle(params->mCapsuleCenter, unitDir, curT, capsuleRadius, currentTri, dpc0)) + return false; + + float magnitude = N.magnitude(); + if(magnitude==0.0f) + return false; + + PxVec3 triNormal = N / magnitude; + + bool DirectHit; + if(sweepSphereVSTriangle(params->mCapsuleCenter, capsuleRadius, currentTri, triNormal, unitDir, curT, DirectHit)) + { + status = true; + } + return DirectHit; +} + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static void /*__fastcall*/ testQuad(const CapsuleSweepParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const PxVec3& p3, const PxVec3& N, + const PxVec3& unitDir, const float capsuleRadius, const float dpc0, float& curT, bool& status) +{ + // PT: TODO: optimize this copy (TA34704) + PxVec3 currentQuad[4]; + currentQuad[0] = p0; + currentQuad[1] = p1; + currentQuad[2] = p2; + currentQuad[3] = p3; + + // PT: beware, culling is only ok on the sphere I think + if(rejectQuad(params->mCapsuleCenter, unitDir, curT, capsuleRadius, currentQuad, dpc0)) + return; + + float magnitude = N.magnitude(); + if(magnitude==0.0f) + return; + + PxVec3 triNormal = N / magnitude; + + if(sweepSphereVSQuad(params->mCapsuleCenter, capsuleRadius, currentQuad, triNormal, unitDir, curT)) + { + status = true; + } +} + +static PX_FORCE_INLINE float Set2(const PxVec3& p0, const PxVec3& n, const PxVec3& p) +{ + return (p-p0).dot(n); +} + +static PX_FORCE_INLINE bool sweepCapsuleVsTriangle(const CapsuleSweepParams* PX_RESTRICT params, const PxTriangle& triangle, float& t, bool isDoubleSided, PxVec3& normal) +{ + const PxVec3& unitDir = params->mLocalDir_Padded; + + // Create triangle normal + PxVec3 denormalizedNormal = (triangle.verts[0] - triangle.verts[1]).cross(triangle.verts[0] - triangle.verts[2]); + + normal = denormalizedNormal; + + // Backface culling + const bool culled = denormalizedNormal.dot(unitDir) > 0.0f; + if(culled) + { + if(!isDoubleSided) + return false; + + denormalizedNormal = -denormalizedNormal; + } + + const float capsuleRadius = params->mLocalCapsule.radius; + float curT = params->mStabbedFace.mDistance; + const float dpc0 = params->mCapsuleCenter.dot(unitDir); + + bool status = false; + + // Extrude mesh on the fly + const PxVec3 p0 = triangle.verts[0] - params->mExtrusionDir; + const PxVec3 p1 = triangle.verts[1+culled] - params->mExtrusionDir; + const PxVec3 p2 = triangle.verts[2-culled] - params->mExtrusionDir; + + const PxVec3 p0b = triangle.verts[0] + params->mExtrusionDir; + const PxVec3 p1b = triangle.verts[1+culled] + params->mExtrusionDir; + const PxVec3 p2b = triangle.verts[2-culled] + params->mExtrusionDir; + + const float extrusionSign = denormalizedNormal.dot(params->mExtrusionDir); + + const PxVec3 p2b_p1b = p2b - p1b; + const PxVec3 p0b_p1b = p0b - p1b; + const PxVec3 p2b_p2 = 2.0f * params->mExtrusionDir; + const PxVec3 p1_p1b = -p2b_p2; + + const PxVec3 N1 = p2b_p1b.cross(p0b_p1b); + const float dp0 = Set2(p0b, N1, params->mCapsuleCenter); + + const PxVec3 N2 = (p2 - p1).cross(p0 - p1); + const float dp1 = -Set2(p0, N2, params->mCapsuleCenter); + + bool directHit; + if(extrusionSign >= 0.0f) + directHit = testTri(params, p0b, p1b, p2b, N1, unitDir, capsuleRadius, dpc0, curT, status); + else + directHit = testTri(params, p0, p1, p2, N2, unitDir, capsuleRadius, dpc0, curT, status); + + const PxVec3 N3 = p2b_p1b.cross(p1_p1b); + const float dp2 = -Set2(p1, N3, params->mCapsuleCenter); + if(!directHit) + { + const float dp = N3.dot(unitDir); + if(dp*extrusionSign>=0.0f) + testQuad(params, p1, p1b, p2, p2b, N3, unitDir, capsuleRadius, dpc0, curT, status); + } + + const PxVec3 N5 = p2b_p2.cross(p0 - p2); + const float dp3 = -Set2(p0, N5, params->mCapsuleCenter); + if(!directHit) + { + const float dp = N5.dot(unitDir); + if(dp*extrusionSign>=0.0f) + testQuad(params, p2, p2b, p0, p0b, N5, unitDir, capsuleRadius, dpc0, curT, status); + } + + const PxVec3 N7 = p1_p1b.cross(p0b_p1b); + const float dp4 = -Set2(p0b, N7, params->mCapsuleCenter); + if(!directHit) + { + const float dp = N7.dot(unitDir); + if(dp*extrusionSign>=0.0f) + testQuad(params, p0, p0b, p1, p1b, N7, unitDir, capsuleRadius, dpc0, curT, status); + } + + if(1) + { + bool originInside = true; + if(extrusionSign<0.0f) + { + if(dp0<0.0f || dp1<0.0f || dp2<0.0f || dp3<0.0f || dp4<0.0f) + originInside = false; + } + else + { + if(dp0>0.0f || dp1>0.0f || dp2>0.0f || dp3>0.0f || dp4>0.0f) + originInside = false; + } + if(originInside) + { + t = 0.0f; + return true; + } + } + + if(!status) + return false; // We didn't touch any triangle + + t = curT; + + return true; +} + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ triCapsuleSweep(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex, bool nodeSorting=true) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + const PxTriangle Tri(p0, p1, p2); // PT: TODO: check calls to empty ctor/dtor here (TA34704) + + const bool isDoubleSided = params->mBackfaceCulling==0; + + float Dist; + PxVec3 denormalizedNormal; + if(sweepCapsuleVsTriangle(params, Tri, Dist, isDoubleSided, denormalizedNormal)) + { + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal alignmentValue = computeAlignmentValue(denormalizedNormal, params->mLocalDir_Padded); + + if(keepTriangle(Dist, alignmentValue, params->mBestDistance, params->mBestAlignmentValue, params->mMaxDist, distEpsilon)) + { + params->mStabbedFace.mDistance = Dist; + params->mStabbedFace.mTriangleID = primIndex; + + params->mP0 = p0; + params->mP1 = p1; + params->mP2 = p2; + + params->mBestDistance = PxMin(params->mBestDistance, Dist); // exact lower bound + params->mBestAlignmentValue = alignmentValue; + params->mBestTriNormal = denormalizedNormal; + + if(nodeSorting) + { +#ifdef SWEEP_AABB_IMPL + #ifndef GU_BV4_USE_SLABS + setupRayData(params, Dist, params->mOrigin_Padded, params->mLocalDir_PaddedAligned); + #endif +#else + params->ShrinkOBB(Dist); +#endif + } + return true; + } + } + return false; +} + +#include "GuDistanceSegmentTriangleSIMD.h" + +namespace +{ +class LeafFunction_CapsuleSweepClosest +{ +public: + static PX_FORCE_INLINE void doLeafTest(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + triCapsuleSweep(params, primIndex); + primIndex++; + }while(nbToGo--); + } +}; + +class LeafFunction_CapsuleSweepAny +{ +public: + + static PX_FORCE_INLINE Ps::IntBool doLeafTest(CapsuleSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triCapsuleSweep(params, primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +class ImpactFunctionCapsule +{ +public: + static PX_FORCE_INLINE void computeImpact(PxVec3& impactPos, PxVec3& impactNormal, const Capsule& capsule, const PxVec3& dir, const PxReal t, const TrianglePadded& triangle) + { + const PxVec3 delta = dir * t; + const Vec3p P0 = capsule.p0 + delta; + const Vec3p P1 = capsule.p1 + delta; + Vec3V pointOnSeg, pointOnTri; + distanceSegmentTriangleSquared( + // PT: we use Vec3p so it is safe to V4LoadU P0 and P1 + V3LoadU_SafeReadW(P0), V3LoadU_SafeReadW(P1), + // PT: we use TrianglePadded so it is safe to V4LoadU the triangle vertices + V3LoadU_SafeReadW(triangle.verts[0]), V3LoadU_SafeReadW(triangle.verts[1]), V3LoadU_SafeReadW(triangle.verts[2]), + pointOnSeg, pointOnTri); + + PxVec3 localImpactPos, tmp; + V3StoreU(pointOnTri, localImpactPos); + V3StoreU(pointOnSeg, tmp); + + // PT: TODO: refactor with computeSphereTriImpactData (TA34704) + PxVec3 localImpactNormal = tmp - localImpactPos; + const float M = localImpactNormal.magnitude(); + if(M<1e-3f) + { + localImpactNormal = (triangle.verts[0] - triangle.verts[1]).cross(triangle.verts[0] - triangle.verts[2]); + localImpactNormal.normalize(); + } + else + localImpactNormal /= M; + + impactPos = localImpactPos; + impactNormal = localImpactNormal; + } +}; +} + +static void computeBoxAroundCapsule(const Capsule& capsule, Box& box, PxVec3& extrusionDir) +{ + // Box center = center of the two capsule's endpoints + box.center = capsule.computeCenter(); + + extrusionDir = (capsule.p0 - capsule.p1)*0.5f; + const PxF32 d = extrusionDir.magnitude(); + + // Box extents + box.extents.x = capsule.radius + d; + box.extents.y = capsule.radius; + box.extents.z = capsule.radius; + + // Box orientation + if(d==0.0f) + { + box.rot = PxMat33(PxIdentity); + } + else + { + PxVec3 dir, right, up; + Ps::computeBasis(capsule.p0, capsule.p1, dir, right, up); + box.setAxes(dir, right, up); + } +} + +template +static PX_FORCE_INLINE void setupCapsuleParams(ParamsT* PX_RESTRICT params, const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree* PX_RESTRICT tree, const SourceMesh* PX_RESTRICT mesh, PxU32 flags) +{ + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + params->mBestAlignmentValue = 2.0f; + params->mBestDistance = maxDist + GU_EPSILON_SAME_DISTANCE; + params->mMaxDist = maxDist; + + setupParamsFlags(params, flags); + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + params->mLocalCapsule = capsule; + + Box localBox; + computeBoxAroundCapsule(capsule, localBox, params->mExtrusionDir); + + params->mCapsuleCenter = localBox.center; + + const PxVec3& localDir = dir; + +#ifdef SWEEP_AABB_IMPL + const PxVec3& localP0 = params->mLocalCapsule.p0; + const PxVec3& localP1 = params->mLocalCapsule.p1; + const PxVec3 sweepOrigin = (localP0+localP1)*0.5f; + const PxVec3 sweepExtents = PxVec3(params->mLocalCapsule.radius) + (localP0-localP1).abs()*0.5f; + + #ifndef GU_BV4_USE_SLABS + params->mLocalDir_PaddedAligned = localDir; + #endif + params->mOrigin_Padded = sweepOrigin; + + const Box aabb(sweepOrigin, sweepExtents, PxMat33(PxIdentity)); + prepareSweepData(aabb, localDir, maxDist, params); // PT: TODO: optimize this call for idt rotation (TA34704) + + #ifndef GU_BV4_USE_SLABS + setupRayData(params, maxDist, sweepOrigin, localDir); + #endif +#else + prepareSweepData(localBox, localDir, maxDist, params); +#endif +} + +#endif // GU_BV4_CAPSULE_SWEEP_INTERNAL_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h new file mode 100644 index 000000000..3ba60c972 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h @@ -0,0 +1,452 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef GU_BV4_COMMON_H +#define GU_BV4_COMMON_H + +#include "foundation/PxMat44.h" +#include "GuBox.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuSIMDHelpers.h" + +#define BV4_ALIGN16(x) PX_ALIGN_PREFIX(16) x PX_ALIGN_SUFFIX(16) + +namespace physx +{ +namespace Gu +{ + enum QueryModifierFlag + { + QUERY_MODIFIER_ANY_HIT = (1<<0), + QUERY_MODIFIER_DOUBLE_SIDED = (1<<1), + QUERY_MODIFIER_MESH_BOTH_SIDES = (1<<2) + }; + + template + PX_FORCE_INLINE void setupParamsFlags(ParamsT* PX_RESTRICT params, PxU32 flags) + { + params->mBackfaceCulling = (flags & (QUERY_MODIFIER_DOUBLE_SIDED|QUERY_MODIFIER_MESH_BOTH_SIDES)) ? 0 : 1u; + params->mEarlyExit = flags & QUERY_MODIFIER_ANY_HIT; + } + + enum HitCode + { + HIT_NONE = 0, //!< No hit + HIT_CONTINUE = 1, //!< Hit found, but keep looking for closer one + HIT_EXIT = 2 //!< Hit found, you can early-exit (raycast any) + }; + + class RaycastHitInternal : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE RaycastHitInternal() {} + PX_FORCE_INLINE ~RaycastHitInternal() {} + + float mDistance; + PxU32 mTriangleID; + }; + + class SweepHit : public physx::shdfnd::UserAllocated + { + public: + PX_FORCE_INLINE SweepHit() {} + PX_FORCE_INLINE ~SweepHit() {} + + PxU32 mTriangleID; //!< Index of touched face + float mDistance; //!< Impact distance + + PxVec3 mPos; + PxVec3 mNormal; + }; + + typedef HitCode (*MeshRayCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, float dist, float u, float v); + typedef bool (*MeshOverlapCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* vertexIndices); + typedef bool (*MeshSweepCallback) (void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist); + typedef bool (*SweepUnlimitedCallback) (void* userData, const SweepHit& hit); + + template + PX_FORCE_INLINE void reportUnlimitedCallbackHit(ParamsT* PX_RESTRICT params, const SweepHit& hit) + { + // PT: we can't reuse the MeshSweepCallback here since it's designed for doing the sweep test inside the callback + // (in the user's code) rather than inside the traversal code. So we use the SweepUnlimitedCallback instead to + // report the already fully computed hit to users. + // PT: TODO: this may not be very efficient, since computing the full hit is expensive. If we use this codepath + // to implement the Epic Tweak, the resulting code will not be optimal. + (params->mCallback)(params->mUserData, hit); + + // PT: the existing traversal code already shrunk the ray. For real "sweep all" calls we must undo that by reseting the max dist. + // (params->mStabbedFace.mDistance is used in computeImpactDataX code, so we need it before that point - we can't simply avoid + // modifying this value before this point). + if(!params->mNodeSorting) + params->mStabbedFace.mDistance = params->mMaxDist; + } + + PX_FORCE_INLINE void invertPRMatrix(PxMat44* PX_RESTRICT dest, const PxMat44* PX_RESTRICT src) + { + const float m30 = src->column3.x; + const float m31 = src->column3.y; + const float m32 = src->column3.z; + + const float m00 = src->column0.x; + const float m01 = src->column0.y; + const float m02 = src->column0.z; + + dest->column0.x = m00; + dest->column1.x = m01; + dest->column2.x = m02; + dest->column3.x = -(m30*m00 + m31*m01 + m32*m02); + + const float m10 = src->column1.x; + const float m11 = src->column1.y; + const float m12 = src->column1.z; + + dest->column0.y = m10; + dest->column1.y = m11; + dest->column2.y = m12; + dest->column3.y = -(m30*m10 + m31*m11 + m32*m12); + + const float m20 = src->column2.x; + const float m21 = src->column2.y; + const float m22 = src->column2.z; + + dest->column0.z = m20; + dest->column1.z = m21; + dest->column2.z = m22; + dest->column3.z = -(m30*m20 + m31*m21 + m32*m22); + + dest->column0.w = 0.0f; + dest->column1.w = 0.0f; + dest->column2.w = 0.0f; + dest->column3.w = 1.0f; + } + + PX_FORCE_INLINE void invertBoxMatrix(PxMat33& m, PxVec3& t, const Gu::Box& box) + { + const float m30 = box.center.x; + const float m31 = box.center.y; + const float m32 = box.center.z; + + const float m00 = box.rot.column0.x; + const float m01 = box.rot.column0.y; + const float m02 = box.rot.column0.z; + + m.column0.x = m00; + m.column1.x = m01; + m.column2.x = m02; + t.x = -(m30*m00 + m31*m01 + m32*m02); + + const float m10 = box.rot.column1.x; + const float m11 = box.rot.column1.y; + const float m12 = box.rot.column1.z; + + m.column0.y = m10; + m.column1.y = m11; + m.column2.y = m12; + t.y = -(m30*m10 + m31*m11 + m32*m12); + + const float m20 = box.rot.column2.x; + const float m21 = box.rot.column2.y; + const float m22 = box.rot.column2.z; + + m.column0.z = m20; + m.column1.z = m21; + m.column2.z = m22; + t.z = -(m30*m20 + m31*m21 + m32*m22); + } + +#ifdef GU_BV4_USE_SLABS + // PT: this class moved here to make things compile with pedantic compilers. + + // PT: now duplicated because not easy to do otherwise + + struct BVDataSwizzledQ : public physx::shdfnd::UserAllocated + { + struct Data + { + PxI16 mMin; //!< Quantized min + PxI16 mMax; //!< Quantized max + }; + + Data mX[4]; + Data mY[4]; + Data mZ[4]; + + PxU32 mData[4]; + + PX_FORCE_INLINE PxU32 isLeaf(PxU32 i) const { return mData[i]&1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return mData[i]>>1; } + PX_FORCE_INLINE PxU32 getChildOffset(PxU32 i) const { return mData[i]>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + PX_FORCE_INLINE PxU32 getChildType(PxU32 i) const { return (mData[i]>>1)&3; } + PX_FORCE_INLINE PxU32 getChildData(PxU32 i) const { return mData[i]; } + PX_FORCE_INLINE PxU32 decodePNSNoShift(PxU32 i) const { return mData[i]; } + }; + + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + struct BVDataSwizzledNQ : public physx::shdfnd::UserAllocated + { + float mMinX[4]; + float mMinY[4]; + float mMinZ[4]; + float mMaxX[4]; + float mMaxY[4]; + float mMaxZ[4]; + + PxU32 mData[4]; + + PX_FORCE_INLINE PxU32 isLeaf(PxU32 i) const { return mData[i]&1; } + PX_FORCE_INLINE PxU32 getPrimitive(PxU32 i) const { return mData[i]>>1; } + PX_FORCE_INLINE PxU32 getChildOffset(PxU32 i) const { return mData[i]>>GU_BV4_CHILD_OFFSET_SHIFT_COUNT; } + PX_FORCE_INLINE PxU32 getChildType(PxU32 i) const { return (mData[i]>>1)&3; } + PX_FORCE_INLINE PxU32 getChildData(PxU32 i) const { return mData[i]; } + PX_FORCE_INLINE PxU32 decodePNSNoShift(PxU32 i) const { return mData[i]; } + }; + #endif + +#else + #define SSE_CONST4(name, val) static const __declspec(align(16)) PxU32 name[4] = { (val), (val), (val), (val) } + #define SSE_CONST(name) *(const __m128i *)&name + #define SSE_CONSTF(name) *(const __m128 *)&name +#endif + + PX_FORCE_INLINE PxU32 getNbPrimitives(PxU32& primIndex) + { + PxU32 NbToGo = (primIndex & 15)-1; + primIndex>>=4; + return NbToGo; + } + + template + PX_FORCE_INLINE void setupMeshPointersAndQuantizedCoeffs(ParamsT* PX_RESTRICT params, const SourceMesh* PX_RESTRICT mesh, const BV4Tree* PX_RESTRICT tree) + { + using namespace physx::shdfnd::aos; + + params->mTris32 = mesh->getTris32(); + params->mTris16 = mesh->getTris16(); + params->mVerts = mesh->getVerts(); + + V4StoreA_Safe(V4LoadU_Safe(&tree->mCenterOrMinCoeff.x), ¶ms->mCenterOrMinCoeff_PaddedAligned.x); + V4StoreA_Safe(V4LoadU_Safe(&tree->mExtentsOrMaxCoeff.x), ¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + } + + PX_FORCE_INLINE void rotateBox(Gu::Box& dst, const PxMat44& m, const Gu::Box& src) + { + // The extents remain constant + dst.extents = src.extents; + // The center gets x-formed + dst.center = m.transform(src.center); + // Combine rotations + // PT: TODO: revisit.. this is awkward... grab 3x3 part of 4x4 matrix (TA34704) + const PxMat33 tmp( PxVec3(m.column0.x, m.column0.y, m.column0.z), + PxVec3(m.column1.x, m.column1.y, m.column1.z), + PxVec3(m.column2.x, m.column2.y, m.column2.z)); + dst.rot = tmp * src.rot; + } + + PX_FORCE_INLINE PxVec3 inverseRotate(const PxMat44* PX_RESTRICT src, const PxVec3& p) + { + const float m00 = src->column0.x; + const float m01 = src->column0.y; + const float m02 = src->column0.z; + + const float m10 = src->column1.x; + const float m11 = src->column1.y; + const float m12 = src->column1.z; + + const float m20 = src->column2.x; + const float m21 = src->column2.y; + const float m22 = src->column2.z; + + return PxVec3( m00*p.x + m01*p.y + m02*p.z, + m10*p.x + m11*p.y + m12*p.z, + m20*p.x + m21*p.y + m22*p.z); + } + + PX_FORCE_INLINE PxVec3 inverseTransform(const PxMat44* PX_RESTRICT src, const PxVec3& p) + { + const float m30 = src->column3.x; + const float m31 = src->column3.y; + const float m32 = src->column3.z; + + const float m00 = src->column0.x; + const float m01 = src->column0.y; + const float m02 = src->column0.z; + + const float m10 = src->column1.x; + const float m11 = src->column1.y; + const float m12 = src->column1.z; + + const float m20 = src->column2.x; + const float m21 = src->column2.y; + const float m22 = src->column2.z; + + return PxVec3( m00*p.x + m01*p.y + m02*p.z -(m30*m00 + m31*m01 + m32*m02), + m10*p.x + m11*p.y + m12*p.z -(m30*m10 + m31*m11 + m32*m12), + m20*p.x + m21*p.y + m22*p.z -(m30*m20 + m31*m21 + m32*m22)); + } + + PX_FORCE_INLINE void computeLocalRay(PxVec3& localDir, PxVec3& localOrigin, const PxVec3& dir, const PxVec3& origin, const PxMat44* PX_RESTRICT worldm_Aligned) + { + if(worldm_Aligned) + { + localDir = inverseRotate(worldm_Aligned, dir); + localOrigin = inverseTransform(worldm_Aligned, origin); + } + else + { + localDir = dir; + localOrigin = origin; + } + } + + PX_FORCE_INLINE void computeLocalSphere(float& radius2, PxVec3& local_center, const Sphere& sphere, const PxMat44* PX_RESTRICT worldm_Aligned) + { + radius2 = sphere.radius * sphere.radius; + if(worldm_Aligned) + { + local_center = inverseTransform(worldm_Aligned, sphere.center); + } + else + { + local_center = sphere.center; + } + } + + PX_FORCE_INLINE void computeLocalCapsule(Capsule& localCapsule, const Capsule& capsule, const PxMat44* PX_RESTRICT worldm_Aligned) + { + localCapsule.radius = capsule.radius; + if(worldm_Aligned) + { + localCapsule.p0 = inverseTransform(worldm_Aligned, capsule.p0); + localCapsule.p1 = inverseTransform(worldm_Aligned, capsule.p1); + } + else + { + localCapsule.p0 = capsule.p0; + localCapsule.p1 = capsule.p1; + } + } + + PX_FORCE_INLINE void computeLocalBox(Gu::Box& dst, const Gu::Box& src, const PxMat44* PX_RESTRICT worldm_Aligned) + { + if(worldm_Aligned) + { + PxMat44 invWorldM; + invertPRMatrix(&invWorldM, worldm_Aligned); + + rotateBox(dst, invWorldM, src); + } + else + { + dst = src; // PT: TODO: check asm for operator= (TA34704) + } + } + + template + static PX_FORCE_INLINE bool computeImpactDataT(const ShapeT& shape, const PxVec3& dir, SweepHit* PX_RESTRICT hit, const ParamsT* PX_RESTRICT params, const PxMat44* PX_RESTRICT worldm, bool isDoubleSided, bool meshBothSides) + { + if(params->mStabbedFace.mTriangleID==PX_INVALID_U32) + return false; // We didn't touch any triangle + + if(hit) + { + const float t = params->mStabbedFace.mDistance; + hit->mTriangleID = params->mStabbedFace.mTriangleID; + hit->mDistance = t; + + if(t==0.0f) + { + hit->mPos = PxVec3(0.0f); + hit->mNormal = -dir; + } + else + { + // PT: TODO: we shouldn't compute impact in world space, and in fact moving this to local space is necessary if we want to reuse this for box-sweeps (TA34704) + TrianglePadded WP; + if(worldm) + { + WP.verts[0] = worldm->transform(params->mP0); + WP.verts[1] = worldm->transform(params->mP1); + WP.verts[2] = worldm->transform(params->mP2); + } + else + { + WP.verts[0] = params->mP0; + WP.verts[1] = params->mP1; + WP.verts[2] = params->mP2; + } + + PxVec3 impactNormal; + ImpactFunctionT::computeImpact(hit->mPos, impactNormal, shape, dir, t, WP); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(impactNormal, meshBothSides, isDoubleSided, params->mBestTriNormal, dir)) + impactNormal = -impactNormal; + + hit->mNormal = impactNormal; + } + } + return true; + } + + // PT: we don't create a structure for small meshes with just a few triangles. We use brute-force tests on these. + template + void doBruteForceTests(PxU32 nbTris, ParamsT* PX_RESTRICT params) + { + PX_ASSERT(nbTris<16); + if(params->mEarlyExit) + LeafFunction_AnyT::doLeafTest(params, nbTris); + else + LeafFunction_ClosestT::doLeafTest(params, nbTris); + } + +#if PX_INTEL_FAMILY +#ifndef GU_BV4_USE_SLABS + template + PX_FORCE_INLINE void setupRayData(ParamsT* PX_RESTRICT params, float max_dist, const PxVec3& origin, const PxVec3& dir) + { + const float Half = 0.5f*max_dist; + const FloatV HalfV = FLoad(Half); + const Vec4V DataV = V4Scale(V4LoadU(&dir.x), HalfV); + const Vec4V Data2V = V4Add(V4LoadU(&origin.x), DataV); + const PxU32 MaskI = 0x7fffffff; + const Vec4V FDirV = _mm_and_ps(_mm_load1_ps((float*)&MaskI), DataV); + V4StoreA_Safe(DataV, ¶ms->mData_PaddedAligned.x); + V4StoreA_Safe(Data2V, ¶ms->mData2_PaddedAligned.x); + V4StoreA_Safe(FDirV, ¶ms->mFDir_PaddedAligned.x); + } +#endif +#endif + +} +} + +#endif // GU_BV4_COMMON_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h new file mode 100644 index 000000000..5f24bdcbb --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h @@ -0,0 +1,318 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_INTERNAL_H +#define GU_BV4_INTERNAL_H + +#include "CmPhysXCommon.h" +#include "PsFPU.h" + + // PT: the general structure is that there is a root "process stream" function which is the entry point for the query. + // It then calls "process node" functions for each traversed node, except for the Slabs-based raycast versions that deal + // with 4 nodes at a time within the "process stream" function itself. When a leaf is found, "doLeafTest" functors + // passed to the "process stream" entry point are called. +#ifdef GU_BV4_USE_SLABS + // PT: Linux tries to compile templates even when they're not used so I had to wrap them all with defines to avoid build errors. Blame the only platform that does this. + #ifdef GU_BV4_PROCESS_STREAM_NO_ORDER + template + PX_FORCE_INLINE Ps::IntBool processStreamNoOrder(const BV4Tree& tree, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + if(tree.mQuantized) + #endif + return BV4_ProcessStreamSwizzledNoOrderQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + else + return BV4_ProcessStreamSwizzledNoOrderNQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #endif + } + #endif + + #ifdef GU_BV4_PROCESS_STREAM_ORDERED + template + PX_FORCE_INLINE void processStreamOrdered(const BV4Tree& tree, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + if(tree.mQuantized) + #endif + BV4_ProcessStreamSwizzledOrderedQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + else + BV4_ProcessStreamSwizzledOrderedNQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #endif + } + #endif + + #ifdef GU_BV4_PROCESS_STREAM_RAY_NO_ORDER + template + PX_FORCE_INLINE Ps::IntBool processStreamRayNoOrder(const BV4Tree& tree, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + if(tree.mQuantized) + #endif + return BV4_ProcessStreamKajiyaNoOrderQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + else + return BV4_ProcessStreamKajiyaNoOrderNQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #endif + } + #endif + + #ifdef GU_BV4_PROCESS_STREAM_RAY_ORDERED + template + PX_FORCE_INLINE void processStreamRayOrdered(const BV4Tree& tree, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + if(tree.mQuantized) + #endif + BV4_ProcessStreamKajiyaOrderedQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + else + BV4_ProcessStreamKajiyaOrderedNQ(reinterpret_cast(tree.mNodes), tree.mInitData, params); + #endif + } + #endif +#else + #define processStreamNoOrder BV4_ProcessStreamNoOrder + #define processStreamOrdered BV4_ProcessStreamOrdered2 + #define processStreamRayNoOrder(a, b) BV4_ProcessStreamNoOrder + #define processStreamRayOrdered(a, b) BV4_ProcessStreamOrdered2 +#endif + +#ifndef GU_BV4_USE_SLABS +#ifdef GU_BV4_PRECOMPUTED_NODE_SORT + // PT: see http://www.codercorner.com/blog/?p=734 + + // PT: TODO: refactor with dup in bucket pruner (TA34704) + PX_FORCE_INLINE PxU32 computeDirMask(const PxVec3& dir) + { + // XYZ + // --- + // --+ + // -+- + // -++ + // +-- + // +-+ + // ++- + // +++ + + const PxU32 X = PX_IR(dir.x)>>31; + const PxU32 Y = PX_IR(dir.y)>>31; + const PxU32 Z = PX_IR(dir.z)>>31; + const PxU32 bitIndex = Z|(Y<<1)|(X<<2); + return 1u< + static Ps::IntBool BV4_ProcessStreamNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + const PxU32 nodeType = getChildType(childData); + + if(nodeType>1 && BV4_ProcessNodeNoOrder(stack, nb, node, params)) + return 1; + if(nodeType>0 && BV4_ProcessNodeNoOrder(stack, nb, node, params)) + return 1; + if(BV4_ProcessNodeNoOrder(stack, nb, node, params)) + return 1; + if(BV4_ProcessNodeNoOrder(stack, nb, node, params)) + return 1; + + }while(nb); + + return 0; + } + + template + static void BV4_ProcessStreamOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32 dirMask = computeDirMask(params->mLocalDir)<<3; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const PxU8* PX_RESTRICT ord = order + decodePNS(node, dirMask)*4; + const PxU32 limit = 2 + getChildType(childData); + + BV4_ProcessNodeOrdered(stack, nb, node, params, ord[0], limit); + BV4_ProcessNodeOrdered(stack, nb, node, params, ord[1], limit); + BV4_ProcessNodeOrdered(stack, nb, node, params, ord[2], limit); + BV4_ProcessNodeOrdered(stack, nb, node, params, ord[3], limit); + }while(Nb); + } + + // Alternative, experimental version using PNS + template + static void BV4_ProcessStreamOrdered2(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; + const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; + const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<(code, node, params); + BV4_ProcessNodeOrdered2(code, node, params); + if(nodeType>0) + BV4_ProcessNodeOrdered2(code, node, params); + if(nodeType>1) + BV4_ProcessNodeOrdered2(code, node, params); + + if(code) + { + // PT: TODO: check which implementation is best on each platform (TA34704) +#define FOURTH_TEST // Version avoids computing the PNS index, and also avoids all non-constant shifts. Full of branches though. Fastest on Win32. +#ifdef FOURTH_TEST + { + if(node[0].decodePNSNoShift() & dirMask) // Bit2 + { + if(node[1].decodePNSNoShift() & dirMask) // Bit1 + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(3,2,1,0) // 7 + else + PNS_BLOCK2(2,3,1,0) // 6 + } + else + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(3,2,0,1) // 5 + else + PNS_BLOCK2(2,3,0,1) // 4 + } + } + else + { + if(node[1].decodePNSNoShift() & dirMask) // Bit1 + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(1,0,3,2) // 3 + else + PNS_BLOCK2(1,0,2,3) // 2 + } + else + { + if(node[2].decodePNSNoShift() & dirMask) // Bit0 + PNS_BLOCK2(0,1,3,2) // 1 + else + PNS_BLOCK2(0,1,2,3) // 0 + } + } + } +#endif + } + }while(nb); + } +#endif // PX_INTEL_FAMILY +#endif // GU_BV4_USE_SLABS + +#endif // GU_BV4_INTERNAL_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp new file mode 100644 index 000000000..fb87e71ce --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp @@ -0,0 +1,170 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; +#include "GuBV4_BoxSweep_Internal.h" + +Ps::IntBool Sweep_AABB_BV4(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags); +void GenericSweep_AABB_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, PxU32 flags); +void Sweep_AABB_BV4_CB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting); + +// PT: TODO: optimize this (TA34704) +static PX_FORCE_INLINE void computeLocalData(Box& localBox, PxVec3& localDir, const Box& box, const PxVec3& dir, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + if(worldm_Aligned) + { + PxMat44 IWM; + invertPRMatrix(&IWM, worldm_Aligned); + + localDir = IWM.rotate(dir); + + rotateBox(localBox, IWM, box); + } + else + { + localDir = dir; + localBox = box; // PT: TODO: check asm for operator= (TA34704) + } +} + +static PX_FORCE_INLINE bool isAxisAligned(const PxVec3& axis) +{ + const PxReal minLimit = 1e-3f; + const PxReal maxLimit = 1.0f - 1e-3f; + + const PxReal absX = PxAbs(axis.x); + if(absX>minLimit && absXminLimit && absYminLimit && absZmPos = worldm_Aligned->transform(hit->mPos); + hit->mNormal = worldm_Aligned->rotate(hit->mNormal); + } + return Status; +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +void BV4_BoxSweepCB(const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +{ + Box localBox; + PxVec3 localDir; + computeLocalData(localBox, localDir, box, dir, worldm_Aligned); + + if(isAABB(localBox)) + Sweep_AABB_BV4_CB(localBox, localDir, maxDist, tree, worldm_Aligned, callback, userData, flags, nodeSorting); + else + Sweep_OBB_BV4_CB(localBox, localDir, maxDist, tree, worldm_Aligned, callback, userData, flags, nodeSorting); +} + + +// PT: this generic sweep uses an OBB because this is the most versatile volume, but it does not mean this function is +// a "box sweep function" per-se. In fact it could be used all alone to implement all sweeps in the SDK (but that would +// have an impact on performance). +// +// So the idea here is simply to provide and use a generic function for everything that the BV4 code does not support directly. +// In particular this should be used: +// - for convex sweeps (where the OBB is the box around the swept convex) +// - for non-trivial sphere/capsule/box sweeps where mesh scaling or inflation +// +// By design we don't do leaf tests inside the BV4 traversal code here (because we don't support them, e.g. convex +// sweeps. If we could do them inside the BV4 traversal code, like we do for regular sweeps, then this would not be a generic +// sweep function, but instead a built-in, natively supported query). So the leaf tests are performed outside of BV4, in the +// client code, through MeshSweepCallback. This has a direct impact on the design & parameters of MeshSweepCallback. +// +// On the other hand this is used for "regular sweeps with shapes we don't natively support", i.e. SweepSingle kind of queries. +// This means that we need to support an early-exit codepath (without node-sorting) and a regular sweep single codepath (with +// node sorting) for this generic function. The leaf tests are external, but everything traversal-related should be exactly the +// same as the regular box-sweep function otherwise. +// +// As a consequence, this function is not well-suited to implement "unlimited results" kind of queries, a.k.a. "sweep all": +// +// - for regular sphere/capsule/box "sweep all" queries, the leaf tests should be internal (same as sweep single queries). This +// means the existing MeshSweepCallback can't be reused. +// +// - there is no need to support "sweep any" (it is already supported by the other sweep functions). +// +// - there may be no need for ordered traversal/node sorting/ray shrinking, since we want to return all results anyway. But this +// may not be true if the "sweep all" function is used to emulate the Epic Tweak. In that case we still want to shrink the ray +// and use node sorting. Since both versions are useful, we should probably have a bool param to enable/disable node sorting. +// +// - we are interested in all hits so we can't delay the computation of impact data (computing it only once in the end, for the +// closest hit). We actually need to compute the data for all hits, possibly within the traversal code. +void BV4_GenericSweepCB(const Box& localBox, const PxVec3& localDir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, bool anyHit) +{ + const PxU32 flags = anyHit ? PxU32(QUERY_MODIFIER_ANY_HIT) : 0; + + if(isAABB(localBox)) + GenericSweep_AABB_CB(localBox, localDir, maxDist, tree, callback, userData, flags); + else + GenericSweep_OBB_CB(localBox, localDir, maxDist, tree, callback, userData, flags); +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h new file mode 100644 index 000000000..47093e431 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h @@ -0,0 +1,111 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H +#define GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H + +#ifdef GU_BV4_USE_SLABS +/* template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_Swizzled(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CE(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + }*/ + + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_SwizzledQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CEQ(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + } + + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_SwizzledNQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledNQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CENQ(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + } + #endif +#else + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_BoxBoxOverlap(node+i, params)) + #else + if(BV4_BoxBoxOverlap(node[i].mAABB.mExtents, node[i].mAABB.mCenter, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_OBB_OBB_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h new file mode 100644 index 000000000..327c4105e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h @@ -0,0 +1,55 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H +#define GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H + +#ifndef GU_BV4_USE_SLABS + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h new file mode 100644 index 000000000..a613ffdbd --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h @@ -0,0 +1,55 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H +#define GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H + +#ifndef GU_BV4_USE_SLABS + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params->mOriginalExtents_Padded, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params->mOriginalExtents_Padded, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_SEGMENT_AABB_INFLATED_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h new file mode 100644 index 000000000..bce32cf01 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h @@ -0,0 +1,113 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H +#define GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H + +#ifdef GU_BV4_USE_SLABS +/* template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_Swizzled(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { +// OPC_SLABS_GET_CE(i) + OPC_SLABS_GET_CE2(i) + + if(BV4_SphereAABBOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + }*/ + + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_SwizzledQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CE2Q(i) + + if(BV4_SphereAABBOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + } + + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder_SwizzledNQ(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataSwizzledNQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CE2NQ(i) + + if(BV4_SphereAABBOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + { + if(LeafTestT::doLeafTest(params, node->getPrimitive(i))) + return 1; + } + else + Stack[Nb++] = node->getChildData(i); + } + return 0; + } + #endif + +#else + template + PX_FORCE_INLINE Ps::IntBool BV4_ProcessNodeNoOrder(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SphereAABBOverlap(node+i, params)) + #else + if(BV4_SphereAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params)) + #endif + { + if(node[i].isLeaf()) + { + if(LeafTestT::doLeafTest(params, node[i].getPrimitive())) + return 1; + } + else + Stack[Nb++] = node[i].getChildData(); + } + return 0; + } +#endif + +#endif // GU_BV4_PROCESS_STREAM_NOORDER_SPHERE_AABB_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h new file mode 100644 index 000000000..ce1ec85ff --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h @@ -0,0 +1,111 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_PROCESS_STREAM_ORDERED_OBB_OBB_H +#define GU_BV4_PROCESS_STREAM_ORDERED_OBB_OBB_H + +#ifdef GU_BV4_USE_SLABS +/* template + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2_Swizzled(PxU32& code, const BVDataSwizzled* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CE(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + LeafTestT::doLeafTest(params, node->getPrimitive(i)); + else + code |= 1< + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2_SwizzledQ(PxU32& code, const BVDataSwizzledQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CEQ(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + LeafTestT::doLeafTest(params, node->getPrimitive(i)); + else + code |= 1< + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2_SwizzledNQ(PxU32& code, const BVDataSwizzledNQ* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + OPC_SLABS_GET_CENQ(i) + + if(BV4_BoxBoxOverlap(centerV, extentsV, params)) + { + if(node->isLeaf(i)) + LeafTestT::doLeafTest(params, node->getPrimitive(i)); + else + code |= 1< + PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(i + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_BoxBoxOverlap(node+i, params)) + #else + if(BV4_BoxBoxOverlap(node[i].mAABB.mExtents, node[i].mAABB.mCenter, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + code |= 1< + PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(i + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + code |= 1< + PX_FORCE_INLINE void BV4_ProcessNodeOrdered(PxU32* PX_RESTRICT Stack, PxU32& Nb, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params, PxU32 i, PxU32 limit) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(imOriginalExtents, params)) + #else + if(imOriginalExtents, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + Stack[Nb++] = node[i].getChildData(); + } + } + + template + PX_FORCE_INLINE void BV4_ProcessNodeOrdered2(PxU32& code, const BVDataPacked* PX_RESTRICT node, ParamsT* PX_RESTRICT params) + { + #ifdef GU_BV4_QUANTIZED_TREE + if(BV4_SegmentAABBOverlap(node+i, params->mOriginalExtents_Padded, params)) + #else + if(BV4_SegmentAABBOverlap(node[i].mAABB.mCenter, node[i].mAABB.mExtents, params->mOriginalExtents_Padded, params)) + #endif + { + if(node[i].isLeaf()) + LeafTestT::doLeafTest(params, node[i].getPrimitive()); + else + code |= 1< +PX_FORCE_INLINE Ps::IntBool RayTriOverlapT(PxRaycastHit& mStabbedFace, const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, const T* PX_RESTRICT params) +{ + // Find vectors for two edges sharing vert0 + const PxVec3 edge1 = vert1 - vert0; + const PxVec3 edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = params->mLocalDir_Padded.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const float det = edge1.dot(pvec); + + if(params->mBackfaceCulling) + { + if(detmOrigin_Padded - vert0; + + // Calculate U parameter and test bounds + const float u = tvec.dot(pvec); + + const PxReal enlargeCoeff = params->mGeomEpsilon*det; + const PxReal uvlimit = -enlargeCoeff; + const PxReal uvlimit2 = det + enlargeCoeff; + + if(u < uvlimit || u > uvlimit2) + return 0; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const float v = params->mLocalDir_Padded.dot(qvec); + if(v < uvlimit || (u + v) > uvlimit2) + return 0; + + // Calculate t, scale parameters, ray intersects triangle + const float d = edge2.dot(qvec); + // Det > 0 so we can early exit here + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(d<0.0f) + return 0; + + // Else go on + const float OneOverDet = 1.0f / det; + mStabbedFace.distance = d * OneOverDet; + mStabbedFace.u = u * OneOverDet; + mStabbedFace.v = v * OneOverDet; + } + else + { + if(PxAbs(det)mOrigin_Padded - vert0; + + const float u = tvec.dot(pvec) * OneOverDet; + if(u<-params->mGeomEpsilon || u>1.0f+params->mGeomEpsilon) + return 0; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + const float v = params->mLocalDir_Padded.dot(qvec) * OneOverDet; + if(v < -params->mGeomEpsilon || (u + v) > 1.0f + params->mGeomEpsilon) + return 0; + + // Calculate t, ray intersects triangle + const float d = edge2.dot(qvec) * OneOverDet; + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(d<0.0f) + return 0; + mStabbedFace.distance = d; + mStabbedFace.u = u; + mStabbedFace.v = v; + } + return 1; +} + +#if PX_VC +#pragma warning ( disable : 4324 ) +#endif + +struct RayParams +{ + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); +// Organized in the order they are accessed +#ifndef GU_BV4_USE_SLABS + BV4_ALIGN16(Vec3p mData2_PaddedAligned); + BV4_ALIGN16(Vec3p mFDir_PaddedAligned); + BV4_ALIGN16(Vec3p mData_PaddedAligned); +#endif + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + PxVec3 mLocalDir_Padded; + PxVec3 mOrigin_Padded; + + float mGeomEpsilon; + PxU32 mBackfaceCulling; + + RaycastHitInternalUV mStabbedFace; + PxU32 mEarlyExit; + + PxVec3 mOriginalExtents_Padded; // Added to please the slabs code + + BV4_ALIGN16(Vec3p mP0_PaddedAligned); + BV4_ALIGN16(Vec3p mP1_PaddedAligned); + BV4_ALIGN16(Vec3p mP2_PaddedAligned); +}; + +/////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE void updateParamsAfterImpact(RayParams* PX_RESTRICT params, PxU32 primIndex, PxU32 VRef0, PxU32 VRef1, PxU32 VRef2, const PxRaycastHit& StabbedFace) +{ + V4StoreA_Safe(V4LoadU_Safe(¶ms->mVerts[VRef0].x), ¶ms->mP0_PaddedAligned.x); + V4StoreA_Safe(V4LoadU_Safe(¶ms->mVerts[VRef1].x), ¶ms->mP1_PaddedAligned.x); + V4StoreA_Safe(V4LoadU_Safe(¶ms->mVerts[VRef2].x), ¶ms->mP2_PaddedAligned.x); + + params->mStabbedFace.mTriangleID = primIndex; + params->mStabbedFace.mDistance = StabbedFace.distance; + params->mStabbedFace.mU = StabbedFace.u; + params->mStabbedFace.mV = StabbedFace.v; +} + +namespace +{ +class LeafFunction_RaycastClosest +{ +public: + static /*PX_FORCE_INLINE*/ Ps::IntBool doLeafTest(RayParams* PX_RESTRICT params, PxU32 primIndex) + { + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& StabbedFace = reinterpret_cast(buffer); + + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + if(RayTriOverlapT(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params)) + { + if(StabbedFace.distancemStabbedFace.mDistance) //### just for a corner case UT in PhysX :( + { + updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace); + +#ifndef GU_BV4_USE_SLABS + setupRayData(params, StabbedFace.distance, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif + } + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +class LeafFunction_RaycastAny +{ +public: + static /*PX_FORCE_INLINE*/ Ps::IntBool doLeafTest(RayParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& StabbedFace = reinterpret_cast(buffer); + if(RayTriOverlapT(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params)) + { + if(StabbedFace.distancemStabbedFace.mDistance) //### just for a corner case UT in PhysX :( + { + updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace); + return 1; + } + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +static PX_FORCE_INLINE Vec4V multiply3x3V_Aligned(const Vec4V p, const PxMat44* PX_RESTRICT mat) +{ + const FloatV xxxV = V4GetX(p); + const FloatV yyyV = V4GetY(p); + const FloatV zzzV = V4GetZ(p); + + Vec4V ResV = V4Scale(V4LoadA(&mat->column0.x), xxxV); + ResV = V4Add(ResV, V4Scale(V4LoadA(&mat->column1.x), yyyV)); + ResV = V4Add(ResV, V4Scale(V4LoadA(&mat->column2.x), zzzV)); + return ResV; +} + +static PX_FORCE_INLINE Ps::IntBool computeImpactData(PxRaycastHit* PX_RESTRICT hit, const RayParams* PX_RESTRICT params, const PxMat44* PX_RESTRICT worldm_Aligned, PxHitFlags /*hitFlags*/) +{ + if(params->mStabbedFace.mTriangleID!=PX_INVALID_U32 /*&& !params->mEarlyExit*/) //### PhysX needs the raycast data even for "any hit" :( + { + const float u = params->mStabbedFace.mU; + const float v = params->mStabbedFace.mV; + const float d = params->mStabbedFace.mDistance; + const PxU32 id = params->mStabbedFace.mTriangleID; + hit->u = u; + hit->v = v; + hit->distance = d; + hit->faceIndex = id; + + { + const Vec4V P0V = V4LoadA_Safe(¶ms->mP0_PaddedAligned.x); + const Vec4V P1V = V4LoadA_Safe(¶ms->mP1_PaddedAligned.x); + const Vec4V P2V = V4LoadA_Safe(¶ms->mP2_PaddedAligned.x); + + const FloatV uV = FLoad(params->mStabbedFace.mU); + const FloatV vV = FLoad(params->mStabbedFace.mV); + const float w = 1.0f - params->mStabbedFace.mU - params->mStabbedFace.mV; + const FloatV wV = FLoad(w); + //pt = (1.0f - u - v)*p0 + u*p1 + v*p2; + Vec4V LocalPtV = V4Scale(P1V, uV); + LocalPtV = V4Add(LocalPtV, V4Scale(P2V, vV)); + LocalPtV = V4Add(LocalPtV, V4Scale(P0V, wV)); + + const Vec4V LocalNormalV = V4Cross(V4Sub(P0V, P1V), V4Sub(P0V, P2V)); + + BV4_ALIGN16(Vec3p tmp_PaddedAligned); + if(worldm_Aligned) + { + const Vec4V TransV = V4LoadA(&worldm_Aligned->column3.x); + V4StoreU_Safe(V4Add(multiply3x3V_Aligned(LocalPtV, worldm_Aligned), TransV), &hit->position.x); + V4StoreA_Safe(multiply3x3V_Aligned(LocalNormalV, worldm_Aligned), &tmp_PaddedAligned.x); + } + else + { + V4StoreU_Safe(LocalPtV, &hit->position.x); + V4StoreA_Safe(LocalNormalV, &tmp_PaddedAligned.x); + } + tmp_PaddedAligned.normalize(); + hit->normal = tmp_PaddedAligned; // PT: TODO: check asm here (TA34704) + } + } + return params->mStabbedFace.mTriangleID!=PX_INVALID_U32; +} + +static PX_FORCE_INLINE float clipRay(const PxVec3& ray_orig, const PxVec3& ray_dir, const LocalBounds& local_bounds) +{ + const float dpc = local_bounds.mCenter.dot(ray_dir); + const float dpMin = dpc - local_bounds.mExtentsMagnitude; + const float dpMax = dpc + local_bounds.mExtentsMagnitude; + const float dpO = ray_orig.dot(ray_dir); + const float boxLength = local_bounds.mExtentsMagnitude * 2.0f; + const float distToBox = PxMin(fabsf(dpMin - dpO), fabsf(dpMax - dpO)); + return distToBox + boxLength * 2.0f; +} + +template +static PX_FORCE_INLINE void setupRayParams(ParamsT* PX_RESTRICT params, const PxVec3& origin, const PxVec3& dir, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT world, const SourceMesh* PX_RESTRICT mesh, float maxDist, float geomEpsilon, PxU32 flags) +{ + params->mGeomEpsilon = geomEpsilon; + setupParamsFlags(params, flags); + + computeLocalRay(params->mLocalDir_Padded, params->mOrigin_Padded, dir, origin, world); + + // PT: TODO: clipRay may not be needed with GU_BV4_USE_SLABS (TA34704) + const float MaxDist = clipRay(params->mOrigin_Padded, params->mLocalDir_Padded, tree->mLocalBounds); + maxDist = PxMin(maxDist, MaxDist); + params->mStabbedFace.mDistance = maxDist; + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + +#ifndef GU_BV4_USE_SLABS + setupRayData(params, maxDist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif +} + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamOrdered_SegmentAABB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_KajiyaNoOrder.h" + #include "GuBV4_Slabs_KajiyaOrdered.h" +#endif + +#define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER +#define GU_BV4_PROCESS_STREAM_RAY_ORDERED +#include "GuBV4_Internal.h" + +#ifndef GU_BV4_USE_SLABS +#ifdef GU_BV4_QUANTIZED_TREE + +#define NEW_VERSION + +static PX_FORCE_INLINE /*PX_NOINLINE*/ Ps::IntBool BV4_SegmentAABBOverlap(const BVDataPacked* PX_RESTRICT node, const RayParams* PX_RESTRICT params) +{ +#ifdef NEW_VERSION + SSE_CONST4(maskV, 0x7fffffff); + SSE_CONST4(maskQV, 0x0000ffff); +#else + const PxU32 maskI = 0x7fffffff; +#endif + + Vec4V centerV = V4LoadA((float*)node->mAABB.mData); +#ifdef NEW_VERSION + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), SSE_CONST(maskQV))); +#else + __m128 extentsV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(centerV), _mm_set1_epi32(0x0000ffff))); +#endif + extentsV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(extentsV)), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + centerV = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(centerV), 16)); + centerV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(centerV)), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), centerV); + +#ifdef NEW_VERSION + __m128 absDV = _mm_and_ps(DV, SSE_CONSTF(maskV)); +#else + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); +#endif + + absDV = V4Sub(V4Add(extentsV, fdirV), absDV); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + +#ifdef NEW_VERSION + __m128 absfV = _mm_and_ps(fV, SSE_CONSTF(maskV)); +#else + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); +#endif + absfV = V4Sub(fg, absfV); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + + if(test2&7) + return 0; + return 1; + } +} +#else +static PX_FORCE_INLINE /*PX_NOINLINE*/ Ps::IntBool BV4_SegmentAABBOverlap(const PxVec3& center, const PxVec3& extents, const RayParams* PX_RESTRICT params) +{ + const PxU32 maskI = 0x7fffffff; + + const Vec4V fdirV = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const Vec4V extentsV = V4LoadU(&extents.x); + + const Vec4V DV = V4Sub(V4LoadA_Safe(¶ms->mData2_PaddedAligned.x), V4LoadU(¢er.x)); //###center should be aligned + + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps((float*)&maskI)); + + absDV = _mm_cmpgt_ps(absDV, V4Add(extentsV, fdirV)); + const PxU32 test = (PxU32)_mm_movemask_ps(absDV); + if(test&7) + return 0; + + if(1) + { + const Vec4V dataZYX_V = V4LoadA_Safe(¶ms->mData_PaddedAligned.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const Vec4V fV = V4Sub(V4Mul(dataZYX_V, DXZY_V), V4Mul(dataXZY_V, DV)); + + const Vec4V fdirZYX_V = V4LoadA_Safe(¶ms->mFDir_PaddedAligned.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + // PT: TODO: use V4MulAdd here (TA34704) + const Vec4V fg = V4Add(V4Mul(extentsV, fdirXZY_V), V4Mul(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps((float*)&maskI)); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = (PxU32)_mm_movemask_ps(absfV); + if(test2&7) + return 0; + return 1; + } +} +#endif +#endif + +Ps::IntBool BV4_RaycastSingle(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hit, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + RayParams Params; + setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamRayNoOrder<0, LeafFunction_RaycastAny>(tree, &Params); + else + processStreamRayOrdered<0, LeafFunction_RaycastClosest>(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); + + return computeImpactData(hit, &Params, worldm_Aligned, hitFlags); +} + + + +// Callback-based version + +namespace +{ + +struct RayParamsCB : RayParams +{ + MeshRayCallback mCallback; + void* mUserData; +}; + +class LeafFunction_RaycastCB +{ +public: + static Ps::IntBool doLeafTest(RayParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& StabbedFace = reinterpret_cast(buffer); + if(RayTriOverlapT(StabbedFace, p0, p1, p2, params)) + { + HitCode Code = (params->mCallback)(params->mUserData, p0, p1, p2, primIndex, StabbedFace.distance, StabbedFace.u, StabbedFace.v); + if(Code==HIT_EXIT) + return 1; + + // PT: TODO: no shrinking here? (TA34704) + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +} + +#include "GuBV4_ProcessStreamNoOrder_SegmentAABB.h" + +void BV4_RaycastCB(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, float maxDist, float geomEpsilon, PxU32 flags, MeshRayCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + //### beware, some parameters in the struct aren't used + RayParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags); + + if(tree.mNodes) + processStreamRayNoOrder<0, LeafFunction_RaycastCB>(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); +// if(Params.mEarlyExit) +// LeafFunction_BoxSweepAnyCB::doLeafTest(&Params, nbTris); +// else + LeafFunction_RaycastCB::doLeafTest(&Params, nbTris); + } +} + +// Raycast all + +namespace +{ +struct RayParamsAll : RayParams +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxRaycastHit* mHits; + const PxMat44* mWorld_Aligned; + PxHitFlags mHitFlags; +}; + +class LeafFunction_RaycastAll +{ +public: + static /*PX_FORCE_INLINE*/ Ps::IntBool doLeafTest(RayParams* PX_RESTRICT p, PxU32 primIndex) + { + RayParamsAll* params = static_cast(p); + + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + PxRaycastHit& StabbedFace = params->mHits[params->mNbHits]; + if(RayTriOverlapT(StabbedFace, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], params)) + { + updateParamsAfterImpact(params, primIndex, VRef0, VRef1, VRef2, StabbedFace); + + computeImpactData(&StabbedFace, params, params->mWorld_Aligned, params->mHitFlags); + + params->mNbHits++; + if(params->mNbHits==params->mMaxNbHits) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: this function is not used yet, but eventually it should be +PxU32 BV4_RaycastAll(const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hits, PxU32 maxNbHits, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + RayParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = maxNbHits; + Params.mHits = hits; + Params.mWorld_Aligned = worldm_Aligned; + Params.mHitFlags = hitFlags; + setupRayParams(&Params, origin, dir, &tree, worldm_Aligned, mesh, maxDist, geomEpsilon, flags); + + if(tree.mNodes) + processStreamRayNoOrder<0, LeafFunction_RaycastAll>(tree, &Params); + else + { + PX_ASSERT(0); + } + return Params.mNbHits; +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h new file mode 100644 index 000000000..23ed4288e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h @@ -0,0 +1,193 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_H +#define GU_BV4_SLABS_H + +#include "PsFPU.h" +#include "GuBV4_Common.h" + +#ifdef GU_BV4_USE_SLABS + + // PT: contains code for tree-traversal using the swizzled format. + // PT: ray traversal based on Kay & Kajiya's slab intersection code, but using SIMD to do 4 ray-vs-AABB tests at a time. + // PT: other (ordered or unordered) traversals just process one node at a time, similar to the non-swizzled format. + + #define BV4_SLABS_FIX + #define BV4_SLABS_SORT + + #define PNS_BLOCK3(a, b, c, d) { \ + if(code2 & (1<getChildData(a); } \ + if(code2 & (1<getChildData(b); } \ + if(code2 & (1<getChildData(c); } \ + if(code2 & (1<getChildData(d); } } \ + + #define OPC_SLABS_GET_MIN_MAX(i) \ + const __m128i minVi = _mm_set_epi32(0, node->mZ[i].mMin, node->mY[i].mMin, node->mX[i].mMin); \ + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); \ + Vec4V minV = V4Mul(_mm_cvtepi32_ps(minVi), minCoeffV); \ + const __m128i maxVi = _mm_set_epi32(0, node->mZ[i].mMax, node->mY[i].mMax, node->mX[i].mMax); \ + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); \ + Vec4V maxV = V4Mul(_mm_cvtepi32_ps(maxVi), maxCoeffV); \ + + #define OPC_SLABS_GET_CEQ(i) \ + OPC_SLABS_GET_MIN_MAX(i) \ + const FloatV HalfV = FLoad(0.5f); \ + const Vec4V centerV = V4Scale(V4Add(maxV, minV), HalfV); \ + const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), HalfV); + + #define OPC_SLABS_GET_CE2Q(i) \ + OPC_SLABS_GET_MIN_MAX(i) \ + const Vec4V centerV = V4Add(maxV, minV); \ + const Vec4V extentsV = V4Sub(maxV, minV); + + #define OPC_SLABS_GET_CENQ(i) \ + const FloatV HalfV = FLoad(0.5f); \ + const Vec4V minV = _mm_set_ps(0.0f, node->mMinZ[i], node->mMinY[i], node->mMinX[i]); \ + const Vec4V maxV = _mm_set_ps(0.0f, node->mMaxZ[i], node->mMaxY[i], node->mMaxX[i]); \ + const Vec4V centerV = V4Scale(V4Add(maxV, minV), HalfV); \ + const Vec4V extentsV = V4Scale(V4Sub(maxV, minV), HalfV); + + #define OPC_SLABS_GET_CE2NQ(i) \ + const Vec4V minV = _mm_set_ps(0.0f, node->mMinZ[i], node->mMinY[i], node->mMinX[i]); \ + const Vec4V maxV = _mm_set_ps(0.0f, node->mMaxZ[i], node->mMaxY[i], node->mMaxX[i]); \ + const Vec4V centerV = V4Add(maxV, minV); \ + const Vec4V extentsV = V4Sub(maxV, minV); + +#if PX_PS4 + // PT: TODO: for some reason using the intrinsics directly produces a compile error on PS4. TODO: find a better fix. + PX_FORCE_INLINE __m128i my_mm_srai_epi32(__m128i a, int count) + { + return _mm_srai_epi32(a, count); + } + + PX_FORCE_INLINE __m128i my_mm_slli_epi32(__m128i a, int count) + { + return _mm_slli_epi32(a, count); + } +#else + #define my_mm_srai_epi32 _mm_srai_epi32 + #define my_mm_slli_epi32 _mm_slli_epi32 +#endif + +#define OPC_DEQ4(part2xV, part1xV, mMember, minCoeff, maxCoeff) \ +{ \ + part2xV = V4LoadA(reinterpret_cast(tn->mMember)); \ + part1xV = _mm_castsi128_ps(_mm_and_si128(_mm_castps_si128(part2xV), _mm_set1_epi32(0x0000ffff))); \ + part1xV = _mm_castsi128_ps(my_mm_srai_epi32(my_mm_slli_epi32(_mm_castps_si128(part1xV), 16), 16)); \ + part1xV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(part1xV)), minCoeff); \ + part2xV = _mm_castsi128_ps(my_mm_srai_epi32(_mm_castps_si128(part2xV), 16)); \ + part2xV = V4Mul(_mm_cvtepi32_ps(_mm_castps_si128(part2xV)), maxCoeff); \ +} + +#define SLABS_INIT\ + Vec4V maxT4 = V4Load(params->mStabbedFace.mDistance);\ + const Vec4V rayP = V4LoadU_Safe(¶ms->mOrigin_Padded.x);\ + Vec4V rayD = V4LoadU_Safe(¶ms->mLocalDir_Padded.x);\ + const VecU32V raySign = V4U32and(VecU32V_ReinterpretFrom_Vec4V(rayD), signMask);\ + const Vec4V rayDAbs = V4Abs(rayD);\ + Vec4V rayInvD = Vec4V_ReinterpretFrom_VecU32V(V4U32or(raySign, VecU32V_ReinterpretFrom_Vec4V(V4Max(rayDAbs, epsFloat4))));\ + rayD = rayInvD;\ + rayInvD = V4RecipFast(rayInvD);\ + rayInvD = V4Mul(rayInvD, V4NegMulSub(rayD, rayInvD, twos));\ + const Vec4V rayPinvD = V4NegMulSub(rayInvD, rayP, zeroes);\ + const Vec4V rayInvDsplatX = V4SplatElement<0>(rayInvD);\ + const Vec4V rayInvDsplatY = V4SplatElement<1>(rayInvD);\ + const Vec4V rayInvDsplatZ = V4SplatElement<2>(rayInvD);\ + const Vec4V rayPinvDsplatX = V4SplatElement<0>(rayPinvD);\ + const Vec4V rayPinvDsplatY = V4SplatElement<1>(rayPinvD);\ + const Vec4V rayPinvDsplatZ = V4SplatElement<2>(rayPinvD); + +#define SLABS_TEST\ + const Vec4V tminxa0 = V4MulAdd(minx4a, rayInvDsplatX, rayPinvDsplatX);\ + const Vec4V tminya0 = V4MulAdd(miny4a, rayInvDsplatY, rayPinvDsplatY);\ + const Vec4V tminza0 = V4MulAdd(minz4a, rayInvDsplatZ, rayPinvDsplatZ);\ + const Vec4V tmaxxa0 = V4MulAdd(maxx4a, rayInvDsplatX, rayPinvDsplatX);\ + const Vec4V tmaxya0 = V4MulAdd(maxy4a, rayInvDsplatY, rayPinvDsplatY);\ + const Vec4V tmaxza0 = V4MulAdd(maxz4a, rayInvDsplatZ, rayPinvDsplatZ);\ + const Vec4V tminxa = V4Min(tminxa0, tmaxxa0);\ + const Vec4V tmaxxa = V4Max(tminxa0, tmaxxa0);\ + const Vec4V tminya = V4Min(tminya0, tmaxya0);\ + const Vec4V tmaxya = V4Max(tminya0, tmaxya0);\ + const Vec4V tminza = V4Min(tminza0, tmaxza0);\ + const Vec4V tmaxza = V4Max(tminza0, tmaxza0);\ + const Vec4V maxOfNeasa = V4Max(V4Max(tminxa, tminya), tminza);\ + const Vec4V minOfFarsa = V4Min(V4Min(tmaxxa, tmaxya), tmaxza);\ + + #define SLABS_TEST2\ + __m128 ignore4a = _mm_cmpgt_ps(epsFloat4, minOfFarsa); /* if tfar is negative, ignore since its a ray, not a line */\ + ignore4a = _mm_or_ps(ignore4a, _mm_cmpgt_ps(maxOfNeasa, maxT4)); /* if tnear is over maxT, ignore this result */\ + __m128 resa4 = _mm_cmpgt_ps(maxOfNeasa, minOfFarsa); /* if 1 => fail */\ + resa4 = _mm_or_ps(resa4, ignore4a);\ + const int code = _mm_movemask_ps(resa4);\ + if(code==15)\ + continue; + +#define SLABS_PNS \ + if(code2) \ + { \ + if(tn->decodePNSNoShift(0) & dirMask) \ + { \ + if(tn->decodePNSNoShift(1) & dirMask) \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(3,2,1,0) \ + else \ + PNS_BLOCK3(2,3,1,0) \ + } \ + else \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(3,2,0,1) \ + else \ + PNS_BLOCK3(2,3,0,1) \ + } \ + } \ + else \ + { \ + if(tn->decodePNSNoShift(1) & dirMask) \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(1,0,3,2) \ + else \ + PNS_BLOCK3(1,0,2,3) \ + } \ + else \ + { \ + if(tn->decodePNSNoShift(2) & dirMask) \ + PNS_BLOCK3(0,1,3,2) \ + else \ + PNS_BLOCK3(0,1,2,3) \ + } \ + } \ + } + +#endif // GU_BV4_USE_SLABS + +#endif // GU_BV4_SLABS_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h new file mode 100644 index 000000000..a36f76d97 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h @@ -0,0 +1,309 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_KAJIYA_NO_ORDER_H +#define GU_BV4_SLABS_KAJIYA_NO_ORDER_H + +#include "GuBVConstants.h" + +#ifdef REMOVED + // Kajiya, no sort + template + static Ps::IntBool BV4_ProcessStreamKajiyaNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + /// + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + if(inflateT) + { + Vec4V fattenAABBs4 = V4LoadU_Safe(¶ms->mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + +#ifdef GU_BV4_QUANTIZED_TREE + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV); + const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV); + const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV); + const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV); + const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV); + const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV); +#endif + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzled* tn = reinterpret_cast(node); + +#ifdef GU_BV4_QUANTIZED_TREE + Vec4V minx4a; + Vec4V maxx4a; + OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV) + + Vec4V miny4a; + Vec4V maxy4a; + OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV) + + Vec4V minz4a; + Vec4V maxz4a; + OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV) +#else + Vec4V minx4a = V4LoadA(tn->mMinX); + Vec4V miny4a = V4LoadA(tn->mMinY); + Vec4V minz4a = V4LoadA(tn->mMinZ); + + Vec4V maxx4a = V4LoadA(tn->mMaxX); + Vec4V maxy4a = V4LoadA(tn->mMaxY); + Vec4V maxz4a = V4LoadA(tn->mMaxZ); +#endif + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + + SLABS_TEST2 + +#define DO_LEAF_TEST(x) \ + {if(tn->isLeaf(x)) \ + { \ + if(LeafTestT::doLeafTest(params, tn->getPrimitive(x))) \ + return 1; \ + } \ + else \ + stack[nb++] = tn->getChildData(x);} + + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + }while(nb); + + return 0; + } +#undef DO_LEAF_TEST +#endif + + + + + +#define DO_LEAF_TEST(x) \ + {if(tn->isLeaf(x)) \ + { \ + if(LeafTestT::doLeafTest(params, tn->getPrimitive(x))) \ + return 1; \ + } \ + else \ + stack[nb++] = tn->getChildData(x);} + + + // Kajiya, no sort + template + static Ps::IntBool BV4_ProcessStreamKajiyaNoOrderQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + /// + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + if(inflateT) + { + Vec4V fattenAABBs4 = V4LoadU_Safe(¶ms->mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV); + const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV); + const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV); + const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV); + const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV); + const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV); + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledQ* tn = reinterpret_cast(node); + + Vec4V minx4a; + Vec4V maxx4a; + OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV) + + Vec4V miny4a; + Vec4V maxy4a; + OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV) + + Vec4V minz4a; + Vec4V maxz4a; + OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV) + + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + + SLABS_TEST2 + + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + }while(nb); + + return 0; + } + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + // Kajiya, no sort + template + static Ps::IntBool BV4_ProcessStreamKajiyaNoOrderNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedNQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + /// + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + if(inflateT) + { + Vec4V fattenAABBs4 = V4LoadU_Safe(¶ms->mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledNQ* tn = reinterpret_cast(node); + + Vec4V minx4a = V4LoadA(tn->mMinX); + Vec4V miny4a = V4LoadA(tn->mMinY); + Vec4V minz4a = V4LoadA(tn->mMinZ); + + Vec4V maxx4a = V4LoadA(tn->mMaxX); + Vec4V maxy4a = V4LoadA(tn->mMaxY); + Vec4V maxz4a = V4LoadA(tn->mMaxZ); + + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + + SLABS_TEST2 + + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + }while(nb); + + return 0; + } +#endif + +#undef DO_LEAF_TEST + +#endif // GU_BV4_SLABS_KAJIYA_NO_ORDER_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h new file mode 100644 index 000000000..e9d3ec241 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h @@ -0,0 +1,552 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_KAJIYA_ORDERED_H +#define GU_BV4_SLABS_KAJIYA_ORDERED_H + +#include "GuBVConstants.h" + +#ifdef REMOVED + // Kajiya + PNS + template + static void BV4_ProcessStreamKajiyaOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + +#ifdef BV4_SLABS_SORT + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + +#ifdef GU_BV4_QUANTIZED_TREE + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV); + const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV); + const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV); + const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV); + const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV); + const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV); +#endif + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzled* tn = reinterpret_cast(node); + +#ifdef GU_BV4_QUANTIZED_TREE + Vec4V minx4a; + Vec4V maxx4a; + OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV) + + Vec4V miny4a; + Vec4V maxy4a; + OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV) + + Vec4V minz4a; + Vec4V maxz4a; + OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV) +#else + Vec4V minx4a = V4LoadA(tn->mMinX); + Vec4V miny4a = V4LoadA(tn->mMinY); + Vec4V minz4a = V4LoadA(tn->mMinZ); + + Vec4V maxx4a = V4LoadA(tn->mMaxX); + Vec4V maxy4a = V4LoadA(tn->mMaxY); + Vec4V maxz4a = V4LoadA(tn->mMaxZ); +#endif + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + +#ifdef BV4_SLABS_FIX + if(inflateT) + _mm_store_ps(distances4, maxOfNeasa); +#endif + + SLABS_TEST2 + +#ifdef BV4_SLABS_SORT + #ifdef BV4_SLABS_FIX + // PT: for some unknown reason the PS4/Linux/OSX compilers fail to understand this version +/* #define DO_LEAF_TEST(x) \ + { \ + if(!inflateT) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<mStabbedFace.mDistance) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<mStabbedFace.mDistance + GU_EPSILON_SAME_DISTANCE) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + SLABS_PNS +#else + #define DO_LEAF_TEST(x) \ + {if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + stack[nb++] = tn->getChildData(x); \ + }} + + + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) +#endif + + }while(nb); + } +#undef DO_LEAF_TEST +#endif + + + +#ifdef BV4_SLABS_SORT + #ifdef BV4_SLABS_FIX + // PT: for some unknown reason the PS4/Linux/OSX compilers fail to understand this version +/* #define DO_LEAF_TEST(x) \ + { \ + if(!inflateT) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<mStabbedFace.mDistance) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<mStabbedFace.mDistance + GU_EPSILON_SAME_DISTANCE) \ + { \ + if(tn->isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + code2 |= 1<isLeaf(x)) \ + { \ + LeafTestT::doLeafTest(params, tn->getPrimitive(x)); \ + maxT4 = V4Load(params->mStabbedFace.mDistance); \ + } \ + else \ + { \ + stack[nb++] = tn->getChildData(x); \ + }} +#endif + + // Kajiya + PNS + template + static void BV4_ProcessStreamKajiyaOrderedQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + +#ifdef BV4_SLABS_SORT + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + + const Vec4V minCoeffV = V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x); + const Vec4V maxCoeffV = V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x); + const Vec4V minCoeffxV = V4SplatElement<0>(minCoeffV); + const Vec4V minCoeffyV = V4SplatElement<1>(minCoeffV); + const Vec4V minCoeffzV = V4SplatElement<2>(minCoeffV); + const Vec4V maxCoeffxV = V4SplatElement<0>(maxCoeffV); + const Vec4V maxCoeffyV = V4SplatElement<1>(maxCoeffV); + const Vec4V maxCoeffzV = V4SplatElement<2>(maxCoeffV); + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledQ* tn = reinterpret_cast(node); + + Vec4V minx4a; + Vec4V maxx4a; + OPC_DEQ4(maxx4a, minx4a, mX, minCoeffxV, maxCoeffxV) + + Vec4V miny4a; + Vec4V maxy4a; + OPC_DEQ4(maxy4a, miny4a, mY, minCoeffyV, maxCoeffyV) + + Vec4V minz4a; + Vec4V maxz4a; + OPC_DEQ4(maxz4a, minz4a, mZ, minCoeffzV, maxCoeffzV) + + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + +#ifdef BV4_SLABS_FIX + if(inflateT) + _mm_store_ps(distances4, maxOfNeasa); +#endif + + SLABS_TEST2 + +#ifdef BV4_SLABS_SORT + PxU32 code2 = 0; + const PxU32 nodeType = getChildType(childData); + + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + SLABS_PNS +#else + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) +#endif + + }while(nb); + } + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + // Kajiya + PNS + template + static void BV4_ProcessStreamKajiyaOrderedNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedNQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + +#ifdef BV4_SLABS_SORT + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<mOriginalExtents_Padded.x); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + /// + + SLABS_INIT + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledNQ* tn = reinterpret_cast(node); + + Vec4V minx4a = V4LoadA(tn->mMinX); + Vec4V miny4a = V4LoadA(tn->mMinY); + Vec4V minz4a = V4LoadA(tn->mMinZ); + + Vec4V maxx4a = V4LoadA(tn->mMaxX); + Vec4V maxy4a = V4LoadA(tn->mMaxY); + Vec4V maxz4a = V4LoadA(tn->mMaxZ); + + if(inflateT) + { + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + SLABS_TEST + +#ifdef BV4_SLABS_FIX + if(inflateT) + _mm_store_ps(distances4, maxOfNeasa); +#endif + + SLABS_TEST2 + +#ifdef BV4_SLABS_SORT + PxU32 code2 = 0; + const PxU32 nodeType = getChildType(childData); + + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) + + SLABS_PNS +#else + const PxU32 nodeType = getChildType(childData); + if(!(code&8) && nodeType>1) + DO_LEAF_TEST(3) + + if(!(code&4) && nodeType>0) + DO_LEAF_TEST(2) + + if(!(code&2)) + DO_LEAF_TEST(1) + + if(!(code&1)) + DO_LEAF_TEST(0) +#endif + + }while(nb); + } +#endif +#undef DO_LEAF_TEST + +#endif // GU_BV4_SLABS_KAJIYA_ORDERED_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h new file mode 100644 index 000000000..decaebad4 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h @@ -0,0 +1,133 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_SWIZZLED_NO_ORDER_H +#define GU_BV4_SLABS_SWIZZLED_NO_ORDER_H + + // Generic, no sort +/* template + static Ps::IntBool BV4_ProcessStreamSwizzledNoOrder(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzled* tn = reinterpret_cast(node); + + const PxU32 nodeType = getChildType(childData); + + if(nodeType>1 && BV4_ProcessNodeNoOrder_Swizzled(stack, nb, tn, params)) + return 1; + if(nodeType>0 && BV4_ProcessNodeNoOrder_Swizzled(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_Swizzled(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_Swizzled(stack, nb, tn, params)) + return 1; + + }while(nb); + + return 0; + }*/ + + + template + static Ps::IntBool BV4_ProcessStreamSwizzledNoOrderQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledQ* tn = reinterpret_cast(node); + + const PxU32 nodeType = getChildType(childData); + + if(nodeType>1 && BV4_ProcessNodeNoOrder_SwizzledQ(stack, nb, tn, params)) + return 1; + if(nodeType>0 && BV4_ProcessNodeNoOrder_SwizzledQ(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_SwizzledQ(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_SwizzledQ(stack, nb, tn, params)) + return 1; + + }while(nb); + + return 0; + } + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + template + static Ps::IntBool BV4_ProcessStreamSwizzledNoOrderNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedNQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + do + { + const PxU32 childData = stack[--nb]; + node = root + getChildOffset(childData); + + const BVDataSwizzledNQ* tn = reinterpret_cast(node); + + const PxU32 nodeType = getChildType(childData); + + if(nodeType>1 && BV4_ProcessNodeNoOrder_SwizzledNQ(stack, nb, tn, params)) + return 1; + if(nodeType>0 && BV4_ProcessNodeNoOrder_SwizzledNQ(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_SwizzledNQ(stack, nb, tn, params)) + return 1; + if(BV4_ProcessNodeNoOrder_SwizzledNQ(stack, nb, tn, params)) + return 1; + + }while(nb); + + return 0; + } +#endif + +#endif // GU_BV4_SLABS_SWIZZLED_NO_ORDER_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h new file mode 100644 index 000000000..c00d72037 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h @@ -0,0 +1,158 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV4_SLABS_SWIZZLED_ORDERED_H +#define GU_BV4_SLABS_SWIZZLED_ORDERED_H + + // Generic + PNS +/* template + static void BV4_ProcessStreamSwizzledOrdered(const BVDataPacked* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPacked* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<(node); + + PxU32 code2 = 0; + BV4_ProcessNodeOrdered2_Swizzled(code2, tn, params); + BV4_ProcessNodeOrdered2_Swizzled(code2, tn, params); + if(nodeType>0) + BV4_ProcessNodeOrdered2_Swizzled(code2, tn, params); + if(nodeType>1) + BV4_ProcessNodeOrdered2_Swizzled(code2, tn, params); + + SLABS_PNS + + }while(nb); + }*/ + + // Generic + PNS + template + static void BV4_ProcessStreamSwizzledOrderedQ(const BVDataPackedQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<(node); + + PxU32 code2 = 0; + BV4_ProcessNodeOrdered2_SwizzledQ(code2, tn, params); + BV4_ProcessNodeOrdered2_SwizzledQ(code2, tn, params); + if(nodeType>0) + BV4_ProcessNodeOrdered2_SwizzledQ(code2, tn, params); + if(nodeType>1) + BV4_ProcessNodeOrdered2_SwizzledQ(code2, tn, params); + + SLABS_PNS + + }while(nb); + } + +#ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + // Generic + PNS + template + static void BV4_ProcessStreamSwizzledOrderedNQ(const BVDataPackedNQ* PX_RESTRICT node, PxU32 initData, ParamsT* PX_RESTRICT params) + { + const BVDataPackedNQ* root = node; + + PxU32 nb=1; + PxU32 stack[GU_BV4_STACK_SIZE]; + stack[0] = initData; + + const PxU32* tmp = reinterpret_cast(¶ms->mLocalDir_Padded); + const PxU32 X = tmp[0]>>31; + const PxU32 Y = tmp[1]>>31; + const PxU32 Z = tmp[2]>>31; +// const PxU32 X = PX_IR(params->mLocalDir_Padded.x)>>31; +// const PxU32 Y = PX_IR(params->mLocalDir_Padded.y)>>31; +// const PxU32 Z = PX_IR(params->mLocalDir_Padded.z)>>31; + const PxU32 bitIndex = 3+(Z|(Y<<1)|(X<<2)); + const PxU32 dirMask = 1u<(node); + + PxU32 code2 = 0; + BV4_ProcessNodeOrdered2_SwizzledNQ(code2, tn, params); + BV4_ProcessNodeOrdered2_SwizzledNQ(code2, tn, params); + if(nodeType>0) + BV4_ProcessNodeOrdered2_SwizzledNQ(code2, tn, params); + if(nodeType>1) + BV4_ProcessNodeOrdered2_SwizzledNQ(code2, tn, params); + + SLABS_PNS + + }while(nb); + } +#endif + +#endif // GU_BV4_SLABS_SWIZZLED_ORDERED_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp new file mode 100644 index 000000000..362ca6135 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp @@ -0,0 +1,326 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuBV4_Common.h" +#include "GuSphere.h" +#include "GuDistancePointTriangle.h" +#include "PsVecMath.h" + +using namespace physx::shdfnd::aos; + +#if PX_VC +#pragma warning ( disable : 4324 ) +#endif + +// Sphere overlap any + +struct SphereParams +{ + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); + + BV4_ALIGN16(PxVec3 mCenter_PaddedAligned); float mRadius2; +#ifdef GU_BV4_USE_SLABS + BV4_ALIGN16(PxVec3 mCenter_PaddedAligned2); float mRadius22; +#endif +}; + +#ifndef GU_BV4_USE_SLABS +#ifndef GU_BV4_QUANTIZED_TREE +// PT: TODO: refactor with bucket pruner code (TA34704) +static PX_FORCE_INLINE Ps::IntBool BV4_SphereAABBOverlap(const PxVec3& center, const PxVec3& extents, const SphereParams* PX_RESTRICT params) +{ + const Vec4V mCenter = V4LoadA_Safe(¶ms->mCenter_PaddedAligned.x); + const FloatV mRadius2 = FLoad(params->mRadius2); + + const Vec4V boxCenter = V4LoadU(¢er.x); + const Vec4V boxExtents = V4LoadU(&extents.x); + + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const PxU32 test = (PxU32)_mm_movemask_ps(FIsGrtrOrEq(mRadius2, V4Dot3(d, d))); + return (test & 0x7) == 0x7; +} +#endif +#endif + +static PX_FORCE_INLINE Ps::IntBool __SphereTriangle(const SphereParams* PX_RESTRICT params, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) +{ + { + const float sqrDist = (p0 - params->mCenter_PaddedAligned).magnitudeSquared(); + if(sqrDist <= params->mRadius2) + return 1; + } + + const PxVec3 edge10 = p1 - p0; + const PxVec3 edge20 = p2 - p0; + const PxVec3 cp = closestPtPointTriangle2(params->mCenter_PaddedAligned, p0, p1, p2, edge10, edge20); + const float sqrDist = (cp - params->mCenter_PaddedAligned).magnitudeSquared(); + return sqrDist <= params->mRadius2; +} + +// PT: TODO: evaluate if SIMD distance function would be faster here (TA34704) +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static /*PX_FORCE_INLINE*/ Ps::IntBool /*__fastcall*/ __SphereTriangle(const SphereParams* PX_RESTRICT params, PxU32 primIndex) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + return __SphereTriangle(params, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2]); +} + +namespace +{ +class LeafFunction_SphereOverlapAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const SphereParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__SphereTriangle(params, primIndex)) + return 1; + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +template +static PX_FORCE_INLINE void setupSphereParams(ParamsT* PX_RESTRICT params, const Sphere& sphere, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh) +{ + computeLocalSphere(params->mRadius2, params->mCenter_PaddedAligned, sphere, worldm_Aligned); + +#ifdef GU_BV4_USE_SLABS + params->mCenter_PaddedAligned2 = params->mCenter_PaddedAligned*2.0f; + params->mRadius22 = params->mRadius2*4.0f; +#endif + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); +} + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" + + static PX_FORCE_INLINE Ps::IntBool BV4_SphereAABBOverlap(const Vec4V boxCenter, const Vec4V boxExtents, const SphereParams* PX_RESTRICT params) + { + const Vec4V mCenter = V4LoadA_Safe(¶ms->mCenter_PaddedAligned2.x); + const FloatV mRadius2 = FLoad(params->mRadius22); + + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const PxU32 test = PxU32(_mm_movemask_ps(FIsGrtrOrEq(mRadius2, V4Dot3(d, d)))); + return (test & 0x7) == 0x7; + } +#else + #ifdef GU_BV4_QUANTIZED_TREE + static PX_FORCE_INLINE Ps::IntBool BV4_SphereAABBOverlap(const BVDataPacked* PX_RESTRICT node, const SphereParams* PX_RESTRICT params) + { + const __m128i testV = _mm_load_si128((__m128i*)node->mAABB.mData); + const __m128i qextentsV = _mm_and_si128(testV, _mm_set1_epi32(0x0000ffff)); + const __m128i qcenterV = _mm_srai_epi32(testV, 16); + const Vec4V boxCenter = V4Mul(_mm_cvtepi32_ps(qcenterV), V4LoadA_Safe(¶ms->mCenterOrMinCoeff_PaddedAligned.x)); + const Vec4V boxExtents = V4Mul(_mm_cvtepi32_ps(qextentsV), V4LoadA_Safe(¶ms->mExtentsOrMaxCoeff_PaddedAligned.x)); + + const Vec4V mCenter = V4LoadA_Safe(¶ms->mCenter_PaddedAligned.x); + const FloatV mRadius2 = FLoad(params->mRadius2); + + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const PxU32 test = (PxU32)_mm_movemask_ps(FIsGrtrOrEq(mRadius2, V4Dot3(d, d))); + return (test & 0x7) == 0x7; + } + #endif +#endif + +#include "GuBV4_ProcessStreamNoOrder_SphereAABB.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_SwizzledNoOrder.h" +#endif + +#define GU_BV4_PROCESS_STREAM_NO_ORDER +#include "GuBV4_Internal.h" + +Ps::IntBool BV4_OverlapSphereAny(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereParams Params; + setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + return processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + return LeafFunction_SphereOverlapAny::doLeafTest(&Params, nbTris); + } +} + +// Sphere overlap all + +struct SphereParamsAll : SphereParams +{ + PxU32 mNbHits; + PxU32 mMaxNbHits; + PxU32* mHits; +}; + +namespace +{ +class LeafFunction_SphereOverlapAll +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(SphereParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(__SphereTriangle(params, primIndex)) + { + SphereParamsAll* ParamsAll = static_cast(params); + if(ParamsAll->mNbHits==ParamsAll->mMaxNbHits) + return 1; + ParamsAll->mHits[ParamsAll->mNbHits] = primIndex; + ParamsAll->mNbHits++; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +PxU32 BV4_OverlapSphereAll(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereParamsAll Params; + Params.mNbHits = 0; + Params.mMaxNbHits = size; + Params.mHits = results; + + setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + overflow = processStreamNoOrder(tree, &Params)!=0; + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + overflow = LeafFunction_SphereOverlapAll::doLeafTest(&Params, nbTris)!=0; + } + return Params.mNbHits; +} + + +// Sphere overlap - callback version + +struct SphereParamsCB : SphereParams +{ + MeshOverlapCallback mCallback; + void* mUserData; +}; + +namespace +{ +class LeafFunction_SphereOverlapCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(const SphereParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + if(__SphereTriangle(params, p0, p1, p2)) + { + const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + if((params->mCallback)(params->mUserData, p0, p1, p2, primIndex, vrefs)) + return 1; + } + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: this one is currently not used +void BV4_OverlapSphereCB(const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + setupSphereParams(&Params, sphere, &tree, worldm_Aligned, mesh); + + if(tree.mNodes) + processStreamNoOrder(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + LeafFunction_SphereOverlapCB::doLeafTest(&Params, nbTris); + } +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp new file mode 100644 index 000000000..8250997ed --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp @@ -0,0 +1,386 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxMat44.h" +#include "GuBV4.h" +#include "GuBox.h" +#include "GuSphere.h" +#include "GuSIMDHelpers.h" +#include "GuSweepSphereTriangle.h" + +using namespace physx; +using namespace Gu; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuBV4_Common.h" + +// PT: for sphere-sweeps we use method 3 in %SDKRoot%\InternalDocumentation\GU\Sweep strategies.ppt + +namespace +{ + // PT: TODO: refactor structure (TA34704) + struct RayParams + { + BV4_ALIGN16(Vec3p mCenterOrMinCoeff_PaddedAligned); + BV4_ALIGN16(Vec3p mExtentsOrMaxCoeff_PaddedAligned); +#ifndef GU_BV4_USE_SLABS + BV4_ALIGN16(Vec3p mData2_PaddedAligned); + BV4_ALIGN16(Vec3p mFDir_PaddedAligned); + BV4_ALIGN16(Vec3p mData_PaddedAligned); +#endif + BV4_ALIGN16(Vec3p mLocalDir_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) + BV4_ALIGN16(Vec3p mOrigin_Padded); // PT: TODO: this one could be switched to PaddedAligned & V4LoadA (TA34704) + }; + + struct SphereSweepParams : RayParams + { + const IndTri32* PX_RESTRICT mTris32; + const IndTri16* PX_RESTRICT mTris16; + const PxVec3* PX_RESTRICT mVerts; + + PxVec3 mOriginalExtents_Padded; + + RaycastHitInternal mStabbedFace; + PxU32 mBackfaceCulling; + PxU32 mEarlyExit; + + PxVec3 mP0, mP1, mP2; + PxVec3 mBestTriNormal; + float mBestAlignmentValue; + float mBestDistance; + float mMaxDist; + }; +} + +#include "GuBV4_AABBAABBSweepTest.h" + +// PT: TODO: __fastcall removed to make it compile everywhere. Revisit. +static bool /*__fastcall*/ triSphereSweep(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex, bool nodeSorting=true) +{ + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + const PxVec3& p0 = params->mVerts[VRef0]; + const PxVec3& p1 = params->mVerts[VRef1]; + const PxVec3& p2 = params->mVerts[VRef2]; + + PxVec3 normal = (p1 - p0).cross(p2 - p0); + + // Backface culling + const bool culled = params->mBackfaceCulling && normal.dot(params->mLocalDir_Padded) > 0.0f; + if(culled) + return false; + + const PxTriangle T(p0, p1, p2); // PT: TODO: check potential bad ctor/dtor here (TA34704) <= or avoid creating the tri, not needed anymore + + normal.normalize(); + + // PT: TODO: we lost some perf when switching to PhysX version. Revisit/investigate. (TA34704) + float dist; + bool directHit; + if(!sweepSphereVSTri(T.verts, normal, params->mOrigin_Padded, params->mOriginalExtents_Padded.x, params->mLocalDir_Padded, dist, directHit, true)) + return false; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal alignmentValue = computeAlignmentValue(normal, params->mLocalDir_Padded); + if(keepTriangle(dist, alignmentValue, params->mBestDistance, params->mBestAlignmentValue, params->mMaxDist, distEpsilon)) + { + params->mStabbedFace.mDistance = dist; + params->mStabbedFace.mTriangleID = primIndex; + params->mP0 = p0; + params->mP1 = p1; + params->mP2 = p2; + params->mBestDistance = PxMin(params->mBestDistance, dist); // exact lower bound + params->mBestAlignmentValue = alignmentValue; + params->mBestTriNormal = normal; + if(nodeSorting) + { +#ifndef GU_BV4_USE_SLABS + setupRayData(params, dist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif + } + return true; + } + return false; +} + +namespace +{ +class LeafFunction_SphereSweepClosest +{ +public: + static PX_FORCE_INLINE void doLeafTest(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + triSphereSweep(params, primIndex); + primIndex++; + }while(nbToGo--); + } +}; + +class LeafFunction_SphereSweepAny +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(SphereSweepParams* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triSphereSweep(params, primIndex)) + return 1; + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; + +class ImpactFunctionSphere +{ +public: + static PX_FORCE_INLINE void computeImpact(PxVec3& impactPos, PxVec3& impactNormal, const Sphere& sphere, const PxVec3& dir, const PxReal t, const TrianglePadded& triangle) + { + computeSphereTriImpactData(impactPos, impactNormal, sphere.center, dir, t, triangle); + } +}; +} + +template +static PX_FORCE_INLINE void setupSphereParams(ParamsT* PX_RESTRICT params, const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree* PX_RESTRICT tree, const PxMat44* PX_RESTRICT worldm_Aligned, const SourceMesh* PX_RESTRICT mesh, PxU32 flags) +{ + params->mOriginalExtents_Padded = PxVec3(sphere.radius); + params->mStabbedFace.mTriangleID = PX_INVALID_U32; + params->mStabbedFace.mDistance = maxDist; + params->mBestDistance = PX_MAX_REAL; + params->mBestAlignmentValue = 2.0f; + params->mMaxDist = maxDist; + setupParamsFlags(params, flags); + + setupMeshPointersAndQuantizedCoeffs(params, mesh, tree); + + computeLocalRay(params->mLocalDir_Padded, params->mOrigin_Padded, dir, sphere.center, worldm_Aligned); + +#ifndef GU_BV4_USE_SLABS + setupRayData(params, maxDist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif +} + +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs.h" +#endif +#include "GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h" +#include "GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h" +#ifdef GU_BV4_USE_SLABS + #include "GuBV4_Slabs_KajiyaNoOrder.h" + #include "GuBV4_Slabs_KajiyaOrdered.h" +#endif + +#define GU_BV4_PROCESS_STREAM_RAY_NO_ORDER +#define GU_BV4_PROCESS_STREAM_RAY_ORDERED +#include "GuBV4_Internal.h" + +Ps::IntBool BV4_SphereSweepSingle(const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereSweepParams Params; + setupSphereParams(&Params, sphere, dir, maxDist, &tree, worldm_Aligned, mesh, flags); + + if(tree.mNodes) + { + if(Params.mEarlyExit) + processStreamRayNoOrder<1, LeafFunction_SphereSweepAny>(tree, &Params); + else + processStreamRayOrdered<1, LeafFunction_SphereSweepClosest>(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); + + return computeImpactDataT(sphere, dir, hit, &Params, worldm_Aligned, (flags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (flags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); +} + +// PT: sphere sweep callback version - currently not used + +namespace +{ + struct SphereSweepParamsCB : SphereSweepParams + { + // PT: these new members are only here to call computeImpactDataT during traversal :( + // PT: TODO: most of them may not be needed if we just move sphere to local space before traversal + Sphere mSphere; // Sphere in original space (maybe not local/mesh space) + PxVec3 mDir; // Dir in original space (maybe not local/mesh space) + const PxMat44* mWorldm_Aligned; + PxU32 mFlags; + + SweepUnlimitedCallback mCallback; + void* mUserData; + bool mNodeSorting; + }; + +class LeafFunction_SphereSweepCB +{ +public: + static PX_FORCE_INLINE Ps::IntBool doLeafTest(SphereSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + if(triSphereSweep(params, primIndex, params->mNodeSorting)) + { + // PT: TODO: in this version we must compute the impact data immediately, + // which is a terrible idea in general, but I'm not sure what else I can do. + SweepHit hit; + const bool b = computeImpactDataT(params->mSphere, params->mDir, &hit, params, params->mWorldm_Aligned, (params->mFlags & QUERY_MODIFIER_DOUBLE_SIDED)!=0, (params->mFlags & QUERY_MODIFIER_MESH_BOTH_SIDES)!=0); + PX_ASSERT(b); + PX_UNUSED(b); + + reportUnlimitedCallbackHit(params, hit); + } + + primIndex++; + }while(nbToGo--); + + return 0; + } +}; +} + +// PT: for design decisions in this function, refer to the comments of BV4_GenericSweepCB(). +void BV4_SphereSweepCB(const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting) +{ + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + SphereSweepParamsCB Params; + Params.mSphere = sphere; + Params.mDir = dir; + Params.mWorldm_Aligned = worldm_Aligned; + Params.mFlags = flags; + + Params.mCallback = callback; + Params.mUserData = userData; + Params.mMaxDist = maxDist; + Params.mNodeSorting = nodeSorting; + setupSphereParams(&Params, sphere, dir, maxDist, &tree, worldm_Aligned, mesh, flags); + + PX_ASSERT(!Params.mEarlyExit); + + if(tree.mNodes) + { + if(nodeSorting) + processStreamRayOrdered<1, LeafFunction_SphereSweepCB>(tree, &Params); + else + processStreamRayNoOrder<1, LeafFunction_SphereSweepCB>(tree, &Params); + } + else + doBruteForceTests(mesh->getNbTriangles(), &Params); +} + + +// Old box sweep callback version, using sphere code + +namespace +{ +struct BoxSweepParamsCB : SphereSweepParams +{ + MeshSweepCallback mCallback; + void* mUserData; +}; + +class ExLeafTestSweepCB +{ +public: + static PX_FORCE_INLINE void doLeafTest(BoxSweepParamsCB* PX_RESTRICT params, PxU32 primIndex) + { + PxU32 nbToGo = getNbPrimitives(primIndex); + do + { + PxU32 VRef0, VRef1, VRef2; + getVertexReferences(VRef0, VRef1, VRef2, primIndex, params->mTris32, params->mTris16); + + { +// const PxU32 vrefs[3] = { VRef0, VRef1, VRef2 }; + float dist = params->mStabbedFace.mDistance; + if((params->mCallback)(params->mUserData, params->mVerts[VRef0], params->mVerts[VRef1], params->mVerts[VRef2], primIndex, /*vrefs,*/ dist)) + return; + + if(distmStabbedFace.mDistance) + { + params->mStabbedFace.mDistance = dist; +#ifndef GU_BV4_USE_SLABS + setupRayData(params, dist, params->mOrigin_Padded, params->mLocalDir_Padded); +#endif + } + } + + primIndex++; + }while(nbToGo--); + } +}; +} + +void BV4_GenericSweepCB_Old(const PxVec3& origin, const PxVec3& extents, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshSweepCallback callback, void* userData) +{ + BoxSweepParamsCB Params; + Params.mCallback = callback; + Params.mUserData = userData; + Params.mOriginalExtents_Padded = extents; + + Params.mStabbedFace.mTriangleID = PX_INVALID_U32; + Params.mStabbedFace.mDistance = maxDist; + + computeLocalRay(Params.mLocalDir_Padded, Params.mOrigin_Padded, dir, origin, worldm_Aligned); + +#ifndef GU_BV4_USE_SLABS + setupRayData(&Params, maxDist, Params.mOrigin_Padded, Params.mLocalDir_Padded); +#endif + + const SourceMesh* PX_RESTRICT mesh = tree.mMeshInterface; + + setupMeshPointersAndQuantizedCoeffs(&Params, mesh, &tree); + + if(tree.mNodes) + processStreamRayOrdered<1, ExLeafTestSweepCB>(tree, &Params); + else + { + const PxU32 nbTris = mesh->getNbTriangles(); + PX_ASSERT(nbTris<16); + ExLeafTestSweepCB::doLeafTest(&Params, nbTris); + } +} + +#endif + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h new file mode 100644 index 000000000..7363fa62d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h @@ -0,0 +1,44 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_BV_CONSTANTS_H +#define GU_BV_CONSTANTS_H + +#include "PsVecMath.h" + +namespace +{ + const physx::Ps::aos::VecU32V signMask = physx::Ps::aos::U4LoadXYZW((physx::PxU32(1)<<31), (physx::PxU32(1)<<31), (physx::PxU32(1)<<31), (physx::PxU32(1)<<31)); + const physx::Ps::aos::Vec4V epsFloat4 = physx::Ps::aos::V4Load(1e-9f); + const physx::Ps::aos::Vec4V zeroes = physx::Ps::aos::V4Zero(); + const physx::Ps::aos::Vec4V twos = physx::Ps::aos::V4Load(2.0f); + const physx::Ps::aos::Vec4V epsInflateFloat4 = physx::Ps::aos::V4Load(1e-7f); +} + +#endif // GU_BV_CONSTANTS_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h new file mode 100644 index 000000000..bd4fc0dfc --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h @@ -0,0 +1,300 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MESH_DATA_H +#define GU_MESH_DATA_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec4.h" +#include "foundation/PxBounds3.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsAllocator.h" +#include "PxTriangleMesh.h" +#include "GuRTree.h" +#include "GuBV4.h" +#include "GuBV32.h" + +namespace physx +{ +namespace Gu { + +// 1: support stackless collision trees for non-recursive collision queries +// 2: height field functionality not supported anymore +// 3: mass struct removed +// 4: bounding sphere removed +// 5: RTree added, opcode tree still in the binary image, physx 3.0 +// 6: opcode tree removed from binary image +// 7: convex decomposition is out +// 8: adjacency information added +// 9: removed leaf triangles and most of opcode data, changed rtree layout +// 10: float rtrees +// 11: new build, isLeaf added to page +// 12: isLeaf is now the lowest bit in ptrs +// 13: TA30159 removed deprecated convexEdgeThreshold and bumped version +// 14: added midphase ID +// 15: GPU data simplification + +#define PX_MESH_VERSION 15 + +// these flags are used to indicate/validate the contents of a cooked mesh file +enum InternalMeshSerialFlag +{ + IMSF_MATERIALS = (1<<0), //!< if set, the cooked mesh file contains per-triangle material indices + IMSF_FACE_REMAP = (1<<1), //!< if set, the cooked mesh file contains a remap table + IMSF_8BIT_INDICES = (1<<2), //!< if set, the cooked mesh file contains 8bit indices (topology) + IMSF_16BIT_INDICES = (1<<3), //!< if set, the cooked mesh file contains 16bit indices (topology) + IMSF_ADJACENCIES = (1<<4), //!< if set, the cooked mesh file contains adjacency structures + IMSF_GRB_DATA = (1<<5) //!< if set, the cooked mesh file contains GRB data structures +}; + + + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + class MeshDataBase : public Ps::UserAllocated + { + public: + PxMeshMidPhase::Enum mType; + PxU8 mFlags; + PxU32 mNbVertices; + PxVec3* mVertices; + + PxBounds3 mAABB; + PxReal mGeomEpsilon; + + PxU32* mFaceRemap; + PxU32* mAdjacencies; + + // GRB data ------------------------- + void * mGRB_primIndices; //!< GRB: GPU-friendly primitive indices(either triangle) + // TODO avoroshilov: adjacency info - duplicated, remove it and use 'mAdjacencies' and 'mExtraTrigData' see GuTriangleMesh.cpp:325 + void * mGRB_primAdjacencies; //!< GRB: adjacency data, with BOUNDARY and NONCONVEX flags (flags replace adj indices where applicable) [uin4] + PxU32* mGRB_faceRemap; //!< GRB: this remap the GPU triangle indices to CPU triangle indices + // End of GRB data ------------------ + + + MeshDataBase() : + mFlags(0), + mNbVertices(0), + mVertices(NULL), + mAABB(PxBounds3::empty()), + mGeomEpsilon(0.0f), + + mFaceRemap(NULL), + mAdjacencies(NULL), + + mGRB_primIndices(NULL), + mGRB_primAdjacencies(NULL), + mGRB_faceRemap(NULL) + { + } + + virtual ~MeshDataBase() + { + if (mVertices) + PX_FREE(mVertices); + if (mFaceRemap) + PX_DELETE_POD(mFaceRemap); + if (mAdjacencies) + PX_DELETE_POD(mAdjacencies); + + + if (mGRB_primIndices) + PX_FREE(mGRB_primIndices); + if (mGRB_primAdjacencies) + PX_DELETE_POD(mGRB_primAdjacencies); + + if (mGRB_faceRemap) + PX_DELETE_POD(mGRB_faceRemap); + } + + + PxVec3* allocateVertices(PxU32 nbVertices) + { + PX_ASSERT(!mVertices); + // PT: we allocate one more vertex to make sure it's safe to V4Load the last one + const PxU32 nbAllocatedVerts = nbVertices + 1; + mVertices = reinterpret_cast(PX_ALLOC(nbAllocatedVerts * sizeof(PxVec3), "PxVec3")); + mNbVertices = nbVertices; + return mVertices; + } + + + PX_FORCE_INLINE bool has16BitIndices() const + { + return (mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? true : false; + } + }; + + class TriangleMeshData : public MeshDataBase + { + public: + + PxU32 mNbTriangles; + void* mTriangles; + + PxU8* mExtraTrigData; + PxU16* mMaterialIndices; + + // GRB data ------------------------- + void* mGRB_BV32Tree; + // End of GRB data ------------------ + + TriangleMeshData() : + mNbTriangles (0), + mTriangles (NULL), + mExtraTrigData (NULL), + mMaterialIndices (NULL), + mGRB_BV32Tree (NULL) + { + } + + virtual ~TriangleMeshData() + { + + if(mTriangles) + PX_FREE(mTriangles); + if(mMaterialIndices) + PX_DELETE_POD(mMaterialIndices); + if(mExtraTrigData) + PX_DELETE_POD(mExtraTrigData); + + if (mGRB_BV32Tree) + { + Gu::BV32Tree* bv32Tree = reinterpret_cast(mGRB_BV32Tree); + PX_DELETE(bv32Tree); + mGRB_BV32Tree = NULL; + } + + } + + PxU32* allocateAdjacencies() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mAdjacencies); + mAdjacencies = PX_NEW(PxU32)[mNbTriangles * 3]; + mFlags |= PxTriangleMeshFlag::eADJACENCY_INFO; + return mAdjacencies; + } + + PxU32* allocateFaceRemap() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mFaceRemap); + mFaceRemap = PX_NEW(PxU32)[mNbTriangles]; + return mFaceRemap; + } + + void* allocateTriangles(PxU32 nbTriangles, bool force32Bit, PxU32 allocateGPUData = 0) + { + PX_ASSERT(mNbVertices); + PX_ASSERT(!mTriangles); + + bool index16 = mNbVertices <= 0xffff && !force32Bit; + if(index16) + mFlags |= PxTriangleMeshFlag::e16_BIT_INDICES; + + mTriangles = PX_ALLOC(nbTriangles * (index16 ? sizeof(PxU16) : sizeof(PxU32)) * 3, "mTriangles"); + if (allocateGPUData) + mGRB_primIndices = PX_ALLOC(nbTriangles * (index16 ? sizeof(PxU16) : sizeof(PxU32)) * 3, "mGRB_triIndices"); + mNbTriangles = nbTriangles; + return mTriangles; + } + + PxU16* allocateMaterials() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mMaterialIndices); + mMaterialIndices = PX_NEW(PxU16)[mNbTriangles]; + return mMaterialIndices; + } + + PxU8* allocateExtraTrigData() + { + PX_ASSERT(mNbTriangles); + PX_ASSERT(!mExtraTrigData); + mExtraTrigData = PX_NEW(PxU8)[mNbTriangles]; + return mExtraTrigData; + } + + PX_FORCE_INLINE void setTriangleAdjacency(PxU32 triangleIndex, PxU32 adjacency, PxU32 offset) + { + PX_ASSERT(mAdjacencies); + mAdjacencies[triangleIndex*3 + offset] = adjacency; + } + + + }; + + class RTreeTriangleData : public TriangleMeshData + { + public: + RTreeTriangleData() { mType = PxMeshMidPhase::eBVH33; } + virtual ~RTreeTriangleData() {} + + Gu::RTree mRTree; + }; + + class BV4TriangleData : public TriangleMeshData + { + public: + BV4TriangleData() { mType = PxMeshMidPhase::eBVH34; } + virtual ~BV4TriangleData() {} + + Gu::SourceMesh mMeshInterface; + Gu::BV4Tree mBV4Tree; + }; + + + class BV32TriangleData : public TriangleMeshData + { + public: + //using the same type as BV4 + BV32TriangleData() { mType = PxMeshMidPhase::eBVH34; } + virtual ~BV32TriangleData() {} + + Gu::SourceMesh mMeshInterface; + Gu::BV32Tree mBV32Tree; + }; + + +#if PX_VC +#pragma warning(pop) +#endif + + +} // namespace Gu + +} + +#endif // #ifdef GU_MESH_DATA_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp new file mode 100644 index 000000000..c4e2be499 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp @@ -0,0 +1,304 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "PxMeshQuery.h" +#include "GuInternal.h" +#include "PxSphereGeometry.h" +#include "PxGeometryQuery.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuBoxConversion.h" +#include "GuIntersectionTriangleBox.h" +#include "CmScaling.h" +#include "GuSweepTests.h" +#include "GuSIMDHelpers.h" +#include "GuMidphaseInterface.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Gu; + +namespace { + + class HfTrianglesEntityReport2 : public EntityReport, public LimitedResults + { + public: + HfTrianglesEntityReport2( + PxU32* results, PxU32 maxResults, PxU32 startIndex, + HeightFieldUtil& hfUtil, + const PxVec3& boxCenter, const PxVec3& boxExtents, const PxQuat& boxRot, + bool aabbOverlap) : + LimitedResults (results, maxResults, startIndex), + mHfUtil (hfUtil), + mAABBOverlap (aabbOverlap) + { + buildFrom(mBox2Hf, boxCenter, boxExtents, boxRot); + } + + virtual bool onEvent(PxU32 nbEntities, PxU32* entities) + { + if(mAABBOverlap) + { + while(nbEntities--) + if(!add(*entities++)) + return false; + } + else + { + const PxTransform idt(PxIdentity); + for(PxU32 i=0; i(triGeom.triangleMesh); + + PX_CHECK_AND_RETURN(triangleIndexgetNbTriangles(), "PxMeshQuery::getTriangle: triangle index is out of bounds"); + + if(adjacencyIndices && !tm->getAdjacencies()) + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacency information not created. Set buildTriangleAdjacencies on Cooking params."); + + const Cm::Matrix34 vertex2worldSkew = globalPose * triGeom.scale; + tm->computeWorldTriangle(triangle, triangleIndex, vertex2worldSkew, triGeom.scale.hasNegativeDeterminant(), vertexIndices, adjacencyIndices); +} + +/////////////////////////////////////////////////////////////////////////////// + +void physx::PxMeshQuery::getTriangle(const PxHeightFieldGeometry& hfGeom, const PxTransform& globalPose, PxTriangleID triangleIndex, PxTriangle& triangle, PxU32* vertexIndices, PxU32* adjacencyIndices) +{ + HeightFieldUtil hfUtil(hfGeom); + + hfUtil.getTriangle(globalPose, triangle, vertexIndices, adjacencyIndices, triangleIndex, true, true); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 physx::PxMeshQuery::findOverlapTriangleMesh( + const PxGeometry& geom, const PxTransform& geomPose, + const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose, + PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow) +{ + PX_SIMD_GUARD; + + LimitedResults limitedResults(results, maxResults, startIndex); + + TriangleMesh* tm = static_cast(meshGeom.triangleMesh); + + switch(geom.getType()) + { + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast(geom); + + Box box; + buildFrom(box, geomPose.p, boxGeom.halfExtents, geomPose.q); + + Midphase::intersectBoxVsMesh(box, *tm, meshPose, meshGeom.scale, &limitedResults); + break; + } + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsGeom = static_cast(geom); + + Capsule capsule; + getCapsule(capsule, capsGeom, geomPose); + + Midphase::intersectCapsuleVsMesh(capsule, *tm, meshPose, meshGeom.scale, &limitedResults); + break; + } + + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast(geom); + Midphase::intersectSphereVsMesh(Sphere(geomPose.p, sphereGeom.radius), *tm, meshPose, meshGeom.scale, &limitedResults); + break; + } + + case PxGeometryType::ePLANE: + case PxGeometryType::eCONVEXMESH: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + PX_CHECK_MSG(false, "findOverlapTriangleMesh: Only box, capsule and sphere geometries are supported."); + } + } + + overflow = limitedResults.mOverflow; + return limitedResults.mNbResults; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 physx::PxMeshQuery::findOverlapHeightField( const PxGeometry& geom, const PxTransform& geomPose, + const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose, + PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow) +{ + PX_SIMD_GUARD; + const PxTransform localPose0 = hfPose.transformInv(geomPose); + PxBoxGeometry boxGeom; + + switch(geom.getType()) + { + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& cap = static_cast(geom); + boxGeom.halfExtents = PxVec3(cap.halfHeight+cap.radius, cap.radius, cap.radius); + } + break; + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sph = static_cast(geom); + boxGeom.halfExtents = PxVec3(sph.radius, sph.radius, sph.radius); + } + break; + case PxGeometryType::eBOX: + boxGeom = static_cast(geom); + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eCONVEXMESH: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + { + overflow = false; + PX_CHECK_AND_RETURN_VAL(false, "findOverlapHeightField: Only box, sphere and capsule queries are supported.", false); + } + } + + const bool isAABB = ((localPose0.q.x == 0.0f) && (localPose0.q.y == 0.0f) && (localPose0.q.z == 0.0f)); + + PxBounds3 bounds; + if (isAABB) + bounds = PxBounds3::centerExtents(localPose0.p, boxGeom.halfExtents); + else + bounds = PxBounds3::poseExtent(localPose0, boxGeom.halfExtents); // box.halfExtents is really extent + + HeightFieldUtil hfUtil(hfGeom); + HfTrianglesEntityReport2 entityReport(results, maxResults, startIndex, hfUtil, localPose0.p, boxGeom.halfExtents, localPose0.q, isAABB); + + hfUtil.overlapAABBTriangles(hfPose, bounds, 0, &entityReport); + overflow = entityReport.mOverflow; + return entityReport.mNbResults; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool physx::PxMeshQuery::sweep( const PxVec3& unitDir, const PxReal maxDistance, + const PxGeometry& geom, const PxTransform& pose, + PxU32 triangleCount, const PxTriangle* triangles, + PxSweepHit& sweepHit, PxHitFlags hitFlags, + const PxU32* cachedIndex, const PxReal inflation, bool doubleSided) +{ + PX_SIMD_GUARD; + PX_CHECK_AND_RETURN_VAL(pose.isValid(), "PxMeshQuery::sweep(): pose is not valid.", false); + PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "PxMeshQuery::sweep(): unitDir is not valid.", false); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(maxDistance), "PxMeshQuery::sweep(): distance is not valid.", false); + PX_CHECK_AND_RETURN_VAL(maxDistance > 0, "PxMeshQuery::sweep(): sweep distance must be greater than 0.", false); + + PX_PROFILE_ZONE("MeshQuery.sweep", 0); + + const PxReal distance = PxMin(maxDistance, PX_MAX_SWEEP_DISTANCE); + + switch(geom.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast(geom); + + // PT: TODO: technically this capsule with 0.0 half-height is invalid ("isValid" returns false) + const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f); + + return sweepCapsuleTriangles( triangleCount, triangles, doubleSided, capsuleGeom, pose, unitDir, distance, + sweepHit, cachedIndex, inflation, hitFlags); + } + + case PxGeometryType::eCAPSULE: + { + const PxCapsuleGeometry& capsuleGeom = static_cast(geom); + + return sweepCapsuleTriangles( triangleCount, triangles, doubleSided, capsuleGeom, pose, unitDir, distance, + sweepHit, cachedIndex, inflation, hitFlags); + } + + case PxGeometryType::eBOX: + { + const PxBoxGeometry& boxGeom = static_cast(geom); + + if(hitFlags & PxHitFlag::ePRECISE_SWEEP) + { + return sweepBoxTriangles_Precise( triangleCount, triangles, doubleSided, boxGeom, pose, unitDir, distance, sweepHit, cachedIndex, + inflation, hitFlags); + } + else + { + return sweepBoxTriangles( triangleCount, triangles, doubleSided, boxGeom, pose, unitDir, distance, sweepHit, cachedIndex, + inflation, hitFlags); + } + } + case PxGeometryType::ePLANE: + case PxGeometryType::eCONVEXMESH: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_CHECK_MSG(false, "PxMeshQuery::sweep(): geometry object parameter must be sphere, capsule or box geometry."); + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp new file mode 100644 index 000000000..0e141cbc1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp @@ -0,0 +1,999 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuBV4.h" +using namespace physx; +using namespace Gu; + +#include "PsVecMath.h" +using namespace physx::shdfnd::aos; + +#include "GuSweepMesh.h" +#include "GuBV4Build.h" +#include "GuBV4_Common.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuBoxConversion.h" +#include "GuConvexUtilsInternal.h" +#include "GuVecTriangle.h" +#include "GuIntersectionTriangleBox.h" +#include "GuIntersectionCapsuleTriangle.h" +#include "GuIntersectionRayBox.h" +#include "PxTriangleMeshGeometry.h" +#include "CmScaling.h" +#include "GuTriangleMeshBV4.h" + +// This file contains code specific to the BV4 midphase. + +// PT: TODO: revisit/inline static sweep functions (TA34704) + +using namespace physx; +using namespace Gu; +using namespace Cm; + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) +Ps::IntBool BV4_RaycastSingle (const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hit, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags); +PxU32 BV4_RaycastAll (const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxRaycastHit* PX_RESTRICT hits, PxU32 maxNbHits, float maxDist, float geomEpsilon, PxU32 flags, PxHitFlags hitFlags); +void BV4_RaycastCB (const PxVec3& origin, const PxVec3& dir, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, float maxDist, float geomEpsilon, PxU32 flags, MeshRayCallback callback, void* userData); + +Ps::IntBool BV4_OverlapSphereAny (const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned); +PxU32 BV4_OverlapSphereAll (const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow); +void BV4_OverlapSphereCB (const Sphere& sphere, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData); + +Ps::IntBool BV4_OverlapBoxAny (const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned); +PxU32 BV4_OverlapBoxAll (const Box& box, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow); +void BV4_OverlapBoxCB (const Box& box, const BV4Tree& tree, MeshOverlapCallback callback, void* userData); + +Ps::IntBool BV4_OverlapCapsuleAny (const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned); +PxU32 BV4_OverlapCapsuleAll (const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, PxU32* results, PxU32 size, bool& overflow); +void BV4_OverlapCapsuleCB (const Capsule& capsule, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshOverlapCallback callback, void* userData); + +Ps::IntBool BV4_SphereSweepSingle (const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags); +void BV4_SphereSweepCB (const Sphere& sphere, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting); + +Ps::IntBool BV4_BoxSweepSingle (const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepHit* PX_RESTRICT hit, PxU32 flags); +void BV4_BoxSweepCB (const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags, bool nodeSorting); + +Ps::IntBool BV4_CapsuleSweepSingle (const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags); +Ps::IntBool BV4_CapsuleSweepSingleAA(const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, SweepHit* PX_RESTRICT hit, PxU32 flags); +void BV4_CapsuleSweepCB (const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags); +void BV4_CapsuleSweepAACB (const Capsule& capsule, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, SweepUnlimitedCallback callback, void* userData, PxU32 flags); + +void BV4_GenericSweepCB_Old (const PxVec3& origin, const PxVec3& extents, const PxVec3& dir, float maxDist, const BV4Tree& tree, const PxMat44* PX_RESTRICT worldm_Aligned, MeshSweepCallback callback, void* userData); +void BV4_GenericSweepCB (const Box& box, const PxVec3& dir, float maxDist, const BV4Tree& tree, MeshSweepCallback callback, void* userData, bool anyHit); + +static PX_FORCE_INLINE void setIdentity(PxMat44& m) +{ + m.column0 = PxVec4(1.0f, 0.0f, 0.0f, 0.0f); + m.column1 = PxVec4(0.0f, 1.0f, 0.0f, 0.0f); + m.column2 = PxVec4(0.0f, 0.0f, 1.0f, 0.0f); + m.column3 = PxVec4(0.0f, 0.0f, 0.0f, 1.0f); +} + +static PX_FORCE_INLINE void setRotation(PxMat44& m, const PxQuat& q) +{ + const PxReal x = q.x; + const PxReal y = q.y; + const PxReal z = q.z; + const PxReal w = q.w; + + const PxReal x2 = x + x; + const PxReal y2 = y + y; + const PxReal z2 = z + z; + + const PxReal xx = x2*x; + const PxReal yy = y2*y; + const PxReal zz = z2*z; + + const PxReal xy = x2*y; + const PxReal xz = x2*z; + const PxReal xw = x2*w; + + const PxReal yz = y2*z; + const PxReal yw = y2*w; + const PxReal zw = z2*w; + + m.column0 = PxVec4(1.0f - yy - zz, xy + zw, xz - yw, 0.0f); + m.column1 = PxVec4(xy - zw, 1.0f - xx - zz, yz + xw, 0.0f); + m.column2 = PxVec4(xz + yw, yz - xw, 1.0f - xx - yy, 0.0f); +} + +#define IEEE_1_0 0x3f800000 //!< integer representation of 1.0 +static PX_FORCE_INLINE const PxMat44* setupWorldMatrix(PxMat44& world, const float* meshPos, const float* meshRot) +{ +// world = PxMat44(PxIdentity); + setIdentity(world); + + bool isIdt = true; + if(meshRot) + { + const PxU32* Bin = reinterpret_cast(meshRot); + if(Bin[0]!=0 || Bin[1]!=0 || Bin[2]!=0 || Bin[3]!=IEEE_1_0) + { +// const PxQuat Q(meshRot[0], meshRot[1], meshRot[2], meshRot[3]); +// world = PxMat44(Q); + setRotation(world, PxQuat(meshRot[0], meshRot[1], meshRot[2], meshRot[3])); + isIdt = false; + } + } + + if(meshPos) + { + const PxU32* Bin = reinterpret_cast(meshPos); + if(Bin[0]!=0 || Bin[1]!=0 || Bin[2]!=0) + { +// world.setPosition(PxVec3(meshPos[0], meshPos[1], meshPos[2])); + world.column3.x = meshPos[0]; + world.column3.y = meshPos[1]; + world.column3.z = meshPos[2]; + isIdt = false; + } + } + return isIdt ? NULL : &world; +} + +static PX_FORCE_INLINE PxU32 setupFlags(bool anyHit, bool doubleSided, bool meshBothSides) +{ + PxU32 flags = 0; + if(anyHit) + flags |= QUERY_MODIFIER_ANY_HIT; + if(doubleSided) + flags |= QUERY_MODIFIER_DOUBLE_SIDED; + if(meshBothSides) + flags |= QUERY_MODIFIER_MESH_BOTH_SIDES; + return flags; +} + +static Ps::IntBool boxSweepVsMesh(SweepHit& h, const BV4Tree& tree, const float* meshPos, const float* meshRot, const Box& box, const PxVec3& dir, float maxDist, bool anyHit, bool doubleSided, bool meshBothSides) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + const PxU32 flags = setupFlags(anyHit, doubleSided, meshBothSides); + return BV4_BoxSweepSingle(box, dir, maxDist, tree, TM, &h, flags); +} + +static Ps::IntBool sphereSweepVsMesh(SweepHit& h, const BV4Tree& tree, const PxVec3& center, float radius, const PxVec3& dir, float maxDist, const PxMat44* TM, const PxU32 flags) +{ + // PT: TODO: avoid this copy (TA34704) + const Sphere tmp(center, radius); + + return BV4_SphereSweepSingle(tmp, dir, maxDist, tree, TM, &h, flags); +} + +static bool capsuleSweepVsMesh(SweepHit& h, const BV4Tree& tree, const Capsule& capsule, const PxVec3& dir, float maxDist, const PxMat44* TM, const PxU32 flags) +{ + Capsule localCapsule; + computeLocalCapsule(localCapsule, capsule, TM); + + // PT: TODO: optimize + PxVec3 localDir, unused; + computeLocalRay(localDir, unused, dir, dir, TM); + + const PxVec3 capsuleDir = localCapsule.p1 - localCapsule.p0; + PxU32 nbNullComponents = 0; + const float epsilon = 1e-3f; + if(PxAbs(capsuleDir.x)transform(h.mPos); + h.mNormal = TM->rotate(h.mNormal); + } + return status!=0; +} + +static PX_FORCE_INLINE void boxSweepVsMeshCBOld(const BV4Tree& tree, const float* meshPos, const float* meshRot, const PxVec3& center, const PxVec3& extents, const PxVec3& dir, float maxDist, MeshSweepCallback callback, void* userData) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + BV4_GenericSweepCB_Old(center, extents, dir, maxDist, tree, TM, callback, userData); +} + +// + +static PX_FORCE_INLINE bool raycastVsMesh(PxRaycastHit& hitData, const BV4Tree& tree, const float* meshPos, const float* meshRot, const PxVec3& orig, const PxVec3& dir, float maxDist, float geomEpsilon, bool doubleSided, PxHitFlags hitFlags) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + const PxU32 flags = setupFlags(anyHit, doubleSided, false); + + if(!BV4_RaycastSingle(orig, dir, tree, TM, &hitData, maxDist, geomEpsilon, flags, hitFlags)) + return false; + + return true; +} + +/*static PX_FORCE_INLINE PxU32 raycastVsMeshAll(PxRaycastHit* hits, PxU32 maxNbHits, const BV4Tree& tree, const float* meshPos, const float* meshRot, const PxVec3& orig, const PxVec3& dir, float maxDist, float geomEpsilon, bool doubleSided, PxHitFlags hitFlags) +{ + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, meshPos, meshRot); + + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + const PxU32 flags = setupFlags(anyHit, doubleSided, false); + + return BV4_RaycastAll(orig, dir, tree, TM, hits, maxNbHits, maxDist, geomEpsilon, flags, hitFlags); +}*/ + +static PX_FORCE_INLINE void raycastVsMeshCB(const BV4Tree& tree, const PxVec3& orig, const PxVec3& dir, float maxDist, float geomEpsilon, bool doubleSided, MeshRayCallback callback, void* userData) +{ + const PxU32 flags = setupFlags(false, doubleSided, false); + BV4_RaycastCB(orig, dir, tree, NULL, maxDist, geomEpsilon, flags, callback, userData); +} + +struct BV4RaycastCBParams +{ + PX_FORCE_INLINE BV4RaycastCBParams( PxRaycastHit* hits, PxU32 maxHits, const PxMeshScale* scale, const PxTransform* pose, + const Cm::Matrix34* world2vertexSkew, PxU32 hitFlags, + const PxVec3& rayDir, bool isDoubleSided, float distCoeff) : + mDstBase (hits), + mHitNum (0), + mMaxHits (maxHits), + mScale (scale), + mPose (pose), + mWorld2vertexSkew (world2vertexSkew), + mHitFlags (hitFlags), + mRayDir (rayDir), + mIsDoubleSided (isDoubleSided), + mDistCoeff (distCoeff) + { + } + + PxRaycastHit* mDstBase; + PxU32 mHitNum; + PxU32 mMaxHits; + const PxMeshScale* mScale; + const PxTransform* mPose; + const Cm::Matrix34* mWorld2vertexSkew; + PxU32 mHitFlags; + const PxVec3& mRayDir; + bool mIsDoubleSided; + float mDistCoeff; + +private: + BV4RaycastCBParams& operator=(const BV4RaycastCBParams&); +}; + +static PX_FORCE_INLINE PxVec3 processLocalNormal(const Cm::Matrix34* PX_RESTRICT world2vertexSkew, const PxTransform* PX_RESTRICT pose, const PxVec3& localNormal, const PxVec3& rayDir, const bool isDoubleSided) +{ + PxVec3 normal; + if(world2vertexSkew) + normal = world2vertexSkew->rotateTranspose(localNormal); + else + normal = pose->rotate(localNormal); + normal.normalize(); + + // PT: figure out correct normal orientation (DE7458) + // - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES. + // - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction. + if(isDoubleSided && normal.dot(rayDir) > 0.0f) + normal = -normal; + return normal; +} + +static HitCode gRayCallback(void* userData, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxU32 triangleIndex, float dist, float u, float v) +{ + BV4RaycastCBParams* params = reinterpret_cast(userData); + +//const bool last = params->mHitNum == params->mMaxHits; + + //not worth concatenating to do 1 transform: PxMat34Legacy vertex2worldSkew = scaling.getVertex2WorldSkew(absPose); + // PT: TODO: revisit this for N hits + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& hit = reinterpret_cast(buffer); +//PxRaycastHit& hit = last ? (PxRaycastHit&)buffer : params->mDstBase[params->mHitNum]; + + hit.distance = dist * params->mDistCoeff; + hit.u = u; + hit.v = v; + hit.faceIndex = triangleIndex; + + PxVec3 localImpact = (1.0f - u - v)*lp0 + u*lp1 + v*lp2; + if(params->mWorld2vertexSkew) + { + localImpact = params->mScale->transform(localImpact); + if(params->mScale->hasNegativeDeterminant()) + Ps::swap(hit.u, hit.v); // have to swap the UVs though since they were computed in mesh local space + } + + hit.position = params->mPose->transform(localImpact); + hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + + PxVec3 normal(0.0f); + // Compute additional information if needed + if(params->mHitFlags & PxHitFlag::eNORMAL) + { + const PxVec3 localNormal = (lp1 - lp0).cross(lp2 - lp0); + normal = processLocalNormal(params->mWorld2vertexSkew, params->mPose, localNormal, params->mRayDir, params->mIsDoubleSided); + hit.flags |= PxHitFlag::eNORMAL; + } + hit.normal = normal; + + // PT: no callback => store results in provided buffer + if(params->mHitNum == params->mMaxHits) +// if(last) + return HIT_EXIT; + + params->mDstBase[params->mHitNum++] = hit; +// params->mHitNum++; + + return HIT_NONE; +} + +PxU32 physx::Gu::raycast_triangleMesh_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast(mesh); + + const bool multipleHits = (maxHits > 1); + const bool idtScale = meshGeom.scale.isIdentity(); + + const bool isDoubleSided = meshGeom.meshFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED); + const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES); + + const BV4Tree& tree = static_cast(meshData)->getBV4Tree(); + if(idtScale && !multipleHits) + { + bool b = raycastVsMesh(*hits, tree, &pose.p.x, &pose.q.x, rayOrigin, rayDir, maxDist, meshData->getGeomEpsilon(), bothSides, hitFlags); + if(b) + { + PxHitFlags dstFlags = PxHitFlag::ePOSITION|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + + // PT: TODO: pass flags to BV4 code (TA34704) + if(hitFlags & PxHitFlag::eNORMAL) + { + dstFlags |= PxHitFlag::eNORMAL; + if(isDoubleSided) + { + PxVec3 normal = hits->normal; + // PT: figure out correct normal orientation (DE7458) + // - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES. + // - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction. + if(normal.dot(rayDir) > 0.0f) + normal = -normal; + hits->normal = normal; + } + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = dstFlags; + } + return PxU32(b); + } + +/* + if(idtScale && multipleHits) + { + PxU32 nbHits = raycastVsMeshAll(hits, maxHits, tree, &pose.p.x, &pose.q.x, rayOrigin, rayDir, maxDist, meshData->getGeomEpsilon(), bothSides, hitFlags); + + return nbHits; + } +*/ + + //scaling: transform the ray to vertex space + PxVec3 orig, dir; + Cm::Matrix34 world2vertexSkew; + Cm::Matrix34* world2vertexSkewP = NULL; + PxReal distCoeff = 1.0f; + if(idtScale) + { + orig = pose.transformInv(rayOrigin); + dir = pose.rotateInv(rayDir); + } + else + { + world2vertexSkew = meshGeom.scale.getInverse() * pose.getInverse(); + world2vertexSkewP = &world2vertexSkew; + orig = world2vertexSkew.transform(rayOrigin); + dir = world2vertexSkew.rotate(rayDir); + { + distCoeff = dir.normalize(); + maxDist *= distCoeff; + maxDist += 1e-3f; + distCoeff = 1.0f/distCoeff; + } + } + + if(!multipleHits) + { + bool b = raycastVsMesh(*hits, tree, NULL, NULL, orig, dir, maxDist, meshData->getGeomEpsilon(), bothSides, hitFlags); + if(b) + { + hits->distance *= distCoeff; + hits->position = pose.transform(meshGeom.scale.transform(hits->position)); + PxHitFlags dstFlags = PxHitFlag::ePOSITION|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + + if(meshGeom.scale.hasNegativeDeterminant()) + Ps::swap(hits->u, hits->v); // have to swap the UVs though since they were computed in mesh local space + + // PT: TODO: pass flags to BV4 code (TA34704) + // Compute additional information if needed + if(hitFlags & PxHitFlag::eNORMAL) + { + dstFlags |= PxHitFlag::eNORMAL; + hits->normal = processLocalNormal(world2vertexSkewP, &pose, hits->normal, rayDir, isDoubleSided); + } + else + { + hits->normal = PxVec3(0.0f); + } + hits->flags = dstFlags; + } + return PxU32(b); + } + + BV4RaycastCBParams callback(hits, maxHits, &meshGeom.scale, &pose, world2vertexSkewP, hitFlags, rayDir, isDoubleSided, distCoeff); + + raycastVsMeshCB( static_cast(meshData)->getBV4Tree(), + orig, dir, + maxDist, meshData->getGeomEpsilon(), bothSides, + gRayCallback, &callback); + return callback.mHitNum; +} + +namespace +{ +struct IntersectShapeVsMeshCallback +{ + IntersectShapeVsMeshCallback(LimitedResults* results, bool flipNormal) : mResults(results), mAnyHits(false), mFlipNormal(flipNormal) {} + + LimitedResults* mResults; + bool mAnyHits; + bool mFlipNormal; + + PX_FORCE_INLINE bool recordHit(PxU32 faceIndex, Ps::IntBool hit) + { + if(hit) + { + mAnyHits = true; + if(mResults) + mResults->add(faceIndex); + else + return false; // abort traversal if we are only interested in firstContact (mResults is NULL) + } + return true; // if we are here, either no triangles were hit or multiple results are expected => continue traversal + } +}; + +// PT: TODO: get rid of this (TA34704) +struct IntersectSphereVsMeshCallback : IntersectShapeVsMeshCallback +{ + PX_FORCE_INLINE IntersectSphereVsMeshCallback(const PxMeshScale& meshScale, const PxTransform& meshTransform, const Sphere& sphere, LimitedResults* r, bool flipNormal) + : IntersectShapeVsMeshCallback(r, flipNormal) + { + mVertexToShapeSkew = meshScale.toMat33(); + mLocalCenter = meshTransform.transformInv(sphere.center); // sphereCenterInMeshSpace + mSphereRadius2 = sphere.radius*sphere.radius; + } + + PxMat33 mVertexToShapeSkew; + PxVec3 mLocalCenter; // PT: sphere center in local/mesh space + PxF32 mSphereRadius2; + + PX_FORCE_INLINE PxAgain processHit(PxU32 faceIndex, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2) + { + const Vec3V v0 = V3LoadU(mVertexToShapeSkew * av0); + const Vec3V v1 = V3LoadU(mVertexToShapeSkew * (mFlipNormal ? av2 : av1)); + const Vec3V v2 = V3LoadU(mVertexToShapeSkew * (mFlipNormal ? av1 : av2)); + + FloatV dummy1, dummy2; + Vec3V closestP; + PxReal dist2; + FStore(distancePointTriangleSquared(V3LoadU(mLocalCenter), v0, v1, v2, dummy1, dummy2, closestP), &dist2); + return recordHit(faceIndex, dist2 <= mSphereRadius2); + } +}; + +// PT: TODO: get rid of this (TA34704) +struct IntersectCapsuleVsMeshCallback : IntersectShapeVsMeshCallback +{ + PX_FORCE_INLINE IntersectCapsuleVsMeshCallback(const PxMeshScale& meshScale, const PxTransform& meshTransform, const Capsule& capsule, LimitedResults* r, bool flipNormal) + : IntersectShapeVsMeshCallback(r, flipNormal) + { + mVertexToShapeSkew = meshScale.toMat33(); + + // transform world capsule to mesh shape space + mLocalCapsule.p0 = meshTransform.transformInv(capsule.p0); + mLocalCapsule.p1 = meshTransform.transformInv(capsule.p1); + mLocalCapsule.radius = capsule.radius; + mParams.init(mLocalCapsule); + } + + PxMat33 mVertexToShapeSkew; + Capsule mLocalCapsule; // PT: capsule in mesh/local space + CapsuleTriangleOverlapData mParams; + + PX_FORCE_INLINE PxAgain processHit(PxU32 faceIndex, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2) + { + const PxVec3 v0 = mVertexToShapeSkew * av0; + const PxVec3 v1 = mVertexToShapeSkew * (mFlipNormal ? av2 : av1); + const PxVec3 v2 = mVertexToShapeSkew * (mFlipNormal ? av1 : av2); + const PxVec3 normal = (v0 - v1).cross(v0 - v2); + bool hit = intersectCapsuleTriangle(normal, v0, v1, v2, mLocalCapsule, mParams); + return recordHit(faceIndex, hit); + } +}; + +// PT: TODO: get rid of this (TA34704) +struct IntersectBoxVsMeshCallback : IntersectShapeVsMeshCallback +{ + PX_FORCE_INLINE IntersectBoxVsMeshCallback(const PxMeshScale& meshScale, const PxTransform& meshTransform, const Box& box, LimitedResults* r, bool flipNormal) + : IntersectShapeVsMeshCallback(r, flipNormal) + { + const PxMat33 vertexToShapeSkew = meshScale.toMat33(); + + // mesh scale needs to be included - inverse transform and optimize the box + const PxMat33 vertexToWorldSkew_Rot = PxMat33Padded(meshTransform.q) * vertexToShapeSkew; + const PxVec3& vertexToWorldSkew_Trans = meshTransform.p; + + Matrix34 tmp; + buildMatrixFromBox(tmp, box); + const Matrix34 inv = tmp.getInverseRT(); + const Matrix34 _vertexToWorldSkew(vertexToWorldSkew_Rot, vertexToWorldSkew_Trans); + + mVertexToBox = inv * _vertexToWorldSkew; + mBoxCenter = PxVec3(0.0f); + mBoxExtents = box.extents; // extents do not change + } + + Matrix34 mVertexToBox; + Vec3p mBoxExtents, mBoxCenter; + + PX_FORCE_INLINE PxAgain processHit(PxU32 faceIndex, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2) + { + const Vec3p v0 = mVertexToBox.transform(av0); + const Vec3p v1 = mVertexToBox.transform(mFlipNormal ? av2 : av1); + const Vec3p v2 = mVertexToBox.transform(mFlipNormal ? av1 : av2); + + // PT: this one is safe because we're using Vec3p for all parameters + const Ps::IntBool hit = intersectTriangleBox_Unsafe(mBoxCenter, mBoxExtents, v0, v1, v2); + return recordHit(faceIndex, hit); + } +}; +} + +static bool gSphereVsMeshCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* /*vertexIndices*/) +{ + IntersectSphereVsMeshCallback* callback = reinterpret_cast(userData); + return !callback->processHit(triangleIndex, p0, p1, p2); +} + +static bool gCapsuleVsMeshCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* /*vertexIndices*/) +{ + IntersectCapsuleVsMeshCallback* callback = reinterpret_cast(userData); + return !callback->processHit(triangleIndex, p0, p1, p2); +} + +static bool gBoxVsMeshCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* /*vertexIndices*/) +{ + IntersectBoxVsMeshCallback* callback = reinterpret_cast(userData); + return !callback->processHit(triangleIndex, p0, p1, p2); +} + +bool physx::Gu::intersectSphereVsMesh_BV4(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4Tree& tree = static_cast(triMesh).getBV4Tree(); + + if(meshScale.isIdentity()) + { + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &meshTransform.p.x, &meshTransform.q.x); + if(results) + { + const PxU32 nbResults = BV4_OverlapSphereAll(sphere, tree, TM, results->mResults, results->mMaxResults, results->mOverflow); + results->mNbResults = nbResults; + return nbResults!=0; + } + else + { + return BV4_OverlapSphereAny(sphere, tree, TM)!=0; + } + } + else + { + // PT: TODO: we don't need to use this callback here (TA34704) + IntersectSphereVsMeshCallback callback(meshScale, meshTransform, sphere, results, meshScale.hasNegativeDeterminant()); + + const Box worldOBB_(sphere.center, PxVec3(sphere.radius), PxMat33(PxIdentity)); + Box vertexOBB; + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + + BV4_OverlapBoxCB(vertexOBB, tree, gSphereVsMeshCallback, &callback); + return callback.mAnyHits; + } +} + +bool physx::Gu::intersectBoxVsMesh_BV4(const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4Tree& tree = static_cast(triMesh).getBV4Tree(); + + if(meshScale.isIdentity()) + { + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &meshTransform.p.x, &meshTransform.q.x); + if(results) + { + const PxU32 nbResults = BV4_OverlapBoxAll(box, tree, TM, results->mResults, results->mMaxResults, results->mOverflow); + results->mNbResults = nbResults; + return nbResults!=0; + } + else + { + return BV4_OverlapBoxAny(box, tree, TM)!=0; + } + } + else + { + // PT: TODO: we don't need to use this callback here (TA34704) + IntersectBoxVsMeshCallback callback(meshScale, meshTransform, box, results, meshScale.hasNegativeDeterminant()); + + Box vertexOBB; // query box in vertex space + computeVertexSpaceOBB(vertexOBB, box, meshTransform, meshScale); + + BV4_OverlapBoxCB(vertexOBB, tree, gBoxVsMeshCallback, &callback); + return callback.mAnyHits; + } +} + +bool physx::Gu::intersectCapsuleVsMesh_BV4(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4Tree& tree = static_cast(triMesh).getBV4Tree(); + + if(meshScale.isIdentity()) + { + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &meshTransform.p.x, &meshTransform.q.x); + if(results) + { + const PxU32 nbResults = BV4_OverlapCapsuleAll(capsule, tree, TM, results->mResults, results->mMaxResults, results->mOverflow); + results->mNbResults = nbResults; + return nbResults!=0; + } + else + { + return BV4_OverlapCapsuleAny(capsule, tree, TM)!=0; + } + } + else + { + // PT: TODO: we don't need to use this callback here (TA34704) + IntersectCapsuleVsMeshCallback callback(meshScale, meshTransform, capsule, results, meshScale.hasNegativeDeterminant()); + + // make vertex space OBB + Box vertexOBB; + Box worldOBB_; + worldOBB_.create(capsule); // AP: potential optimization (meshTransform.inverse is already in callback.mCapsule) + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + + BV4_OverlapBoxCB(vertexOBB, tree, gCapsuleVsMeshCallback, &callback); + return callback.mAnyHits; + } +} + +// PT: TODO: get rid of this (TA34704) +static bool gVolumeCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, const PxU32* vertexIndices) +{ + MeshHitCallback* callback = reinterpret_cast*>(userData); + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& hit = reinterpret_cast(buffer); + hit.faceIndex = triangleIndex; + PxReal dummy; + return !callback->processHit(hit, p0, p1, p2, dummy, vertexIndices); +} + +void physx::Gu::intersectOBB_BV4(const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned) +{ + PX_UNUSED(checkObbIsAligned); + PX_UNUSED(bothTriangleSidesCollide); + BV4_OverlapBoxCB(obb, static_cast(mesh)->getBV4Tree(), gVolumeCallback, &callback); +} + + + + +#include "GuVecCapsule.h" +#include "GuSweepMTD.h" + +static bool gCapsuleMeshSweepCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist) +{ + SweepCapsuleMeshHitCallback* callback = reinterpret_cast(userData); + PxRaycastHit meshHit; + meshHit.faceIndex = triangleIndex; + return !callback->SweepCapsuleMeshHitCallback::processHit(meshHit, p0, p1, p2, dist, NULL/*vertexIndices*/); +} + +// PT: TODO: refactor/share bits of this (TA34704) +bool physx::Gu::sweepCapsule_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast(mesh); + + const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + bool isDoubleSided = (triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED); + const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + + if(isIdentity) + { + const BV4Tree& tree = meshData->getBV4Tree(); + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + + BV4_ALIGN16(PxMat44 World); + const PxMat44* TM = setupWorldMatrix(World, &pose.p.x, &pose.q.x); + + const PxU32 flags = setupFlags(anyHit, isDoubleSided, meshBothSides!=0); + + SweepHit hitData; + if(lss.p0==lss.p1) + { + if(!sphereSweepVsMesh(hitData, tree, inflatedCapsule.p0, inflatedCapsule.radius, unitDir, distance, TM, flags)) + return false; + } + else + { + if(!capsuleSweepVsMesh(hitData, tree, inflatedCapsule, unitDir, distance, TM, flags)) + return false; + } + + sweepHit.distance = hitData.mDistance; + sweepHit.position = hitData.mPos; + sweepHit.normal = hitData.mNormal; + sweepHit.faceIndex = hitData.mTriangleID; + + if(hitData.mDistance==0.0f) + { + sweepHit.flags = PxHitFlag::eNORMAL; + + if(meshBothSides) + isDoubleSided = true; + + // PT: TODO: consider using 'setInitialOverlapResults' here + bool hasContacts = false; + if(hitFlags & PxHitFlag::eMTD) + { + const Vec3V p0 = V3LoadU(inflatedCapsule.p0); + const Vec3V p1 = V3LoadU(inflatedCapsule.p1); + const FloatV radius = FLoad(lss.radius); + CapsuleV capsuleV; + capsuleV.initialize(p0, p1, radius); + + //we need to calculate the MTD + hasContacts = computeCapsule_TriangleMeshMTD(triMeshGeom, pose, capsuleV, inflatedCapsule.radius, isDoubleSided, sweepHit); + } + setupSweepHitForMTD(sweepHit, hasContacts, unitDir); + } + else + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + return true; + } + + // compute sweptAABB + const PxVec3 localP0 = pose.transformInv(inflatedCapsule.p0); + const PxVec3 localP1 = pose.transformInv(inflatedCapsule.p1); + PxVec3 sweepOrigin = (localP0+localP1)*0.5f; + PxVec3 sweepDir = pose.rotateInv(unitDir); + PxVec3 sweepExtents = PxVec3(inflatedCapsule.radius) + (localP0-localP1).abs()*0.5f; + PxReal distance1 = distance; + PxReal distCoef = 1.0f; + Matrix34 poseWithScale; + if(!isIdentity) + { + poseWithScale = pose * triMeshGeom.scale; + distance1 = computeSweepData(triMeshGeom, sweepOrigin, sweepExtents, sweepDir, distance); + distCoef = distance1 / distance; + } else + poseWithScale = Matrix34(pose); + + SweepCapsuleMeshHitCallback callback(sweepHit, poseWithScale, distance, isDoubleSided, inflatedCapsule, unitDir, hitFlags, triMeshGeom.scale.hasNegativeDeterminant(), distCoef); + + boxSweepVsMeshCBOld(meshData->getBV4Tree(), NULL, NULL, sweepOrigin, sweepExtents, sweepDir, distance1, gCapsuleMeshSweepCallback, &callback); + + if(meshBothSides) + isDoubleSided = true; + + return callback.finalizeHit(sweepHit, inflatedCapsule, triMeshGeom, pose, isDoubleSided); +} + +#include "GuSweepSharedTests.h" +static bool gBoxMeshSweepCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist) +{ + SweepBoxMeshHitCallback* callback = reinterpret_cast(userData); + PxRaycastHit meshHit; + meshHit.faceIndex = triangleIndex; + return !callback->SweepBoxMeshHitCallback::processHit(meshHit, p0, p1, p2, dist, NULL/*vertexIndices*/); +} + +// PT: TODO: refactor/share bits of this (TA34704) +bool physx::Gu::sweepBox_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast(mesh); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool isDoubleSided = triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED; + + if(isIdentity && inflation==0.0f) + { + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + + // PT: TODO: this is wrong, we shouldn't actually sweep the inflated version +// const PxVec3 inflated = (box.extents + PxVec3(inflation)) * 1.01f; + // PT: TODO: avoid this copy +// const Box tmp(box.center, inflated, box.rot); + + SweepHit hitData; +// if(!boxSweepVsMesh(hitData, meshData->getBV4Tree(), &pose.p.x, &pose.q.x, tmp, unitDir, distance, anyHit, isDoubleSided, meshBothSides)) + if(!boxSweepVsMesh(hitData, meshData->getBV4Tree(), &pose.p.x, &pose.q.x, box, unitDir, distance, anyHit, isDoubleSided, meshBothSides)) + return false; + + sweepHit.distance = hitData.mDistance; + sweepHit.position = hitData.mPos; + sweepHit.normal = hitData.mNormal; + sweepHit.faceIndex = hitData.mTriangleID; + + if(hitData.mDistance==0.0f) + { + sweepHit.flags = PxHitFlag::eNORMAL; + + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + const PxTransform boxTransform = box.getTransform(); + + bool hasContacts = false; + if(hitFlags & PxHitFlag::eMTD) + hasContacts = computeBox_TriangleMeshMTD(triMeshGeom, pose, box, boxTransform, inflation, bothTriangleSidesCollide, sweepHit); + + setupSweepHitForMTD(sweepHit, hasContacts, unitDir); + } + else + { + sweepHit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + } + return true; + } + + // PT: TODO: revisit this codepath, we don't need to sweep an AABB all the time (TA34704) + + Matrix34 meshToWorldSkew; + PxVec3 sweptAABBMeshSpaceExtents, meshSpaceOrigin, meshSpaceDir; + + // Input sweep params: geom, pose, box, unitDir, distance + // We convert the origin from world space to mesh local space + // and convert the box+pose to mesh space AABB + if(isIdentity) + { + meshToWorldSkew = Matrix34(pose); + PxMat33 worldToMeshRot(pose.q.getConjugate()); // extract rotation matrix from pose.q + meshSpaceOrigin = worldToMeshRot.transform(box.center - pose.p); + meshSpaceDir = worldToMeshRot.transform(unitDir) * distance; + PxMat33 boxToMeshRot = worldToMeshRot * box.rot; + sweptAABBMeshSpaceExtents = boxToMeshRot.column0.abs() * box.extents.x + + boxToMeshRot.column1.abs() * box.extents.y + + boxToMeshRot.column2.abs() * box.extents.z; + } + else + { + meshToWorldSkew = pose * triMeshGeom.scale; + const PxMat33 meshToWorldSkew_Rot = PxMat33Padded(pose.q) * triMeshGeom.scale.toMat33(); + const PxVec3& meshToWorldSkew_Trans = pose.p; + + PxMat33 worldToVertexSkew_Rot; + PxVec3 worldToVertexSkew_Trans; + getInverse(worldToVertexSkew_Rot, worldToVertexSkew_Trans, meshToWorldSkew_Rot, meshToWorldSkew_Trans); + + //make vertex space OBB + Box vertexSpaceBox1; + const Matrix34 worldToVertexSkew(worldToVertexSkew_Rot, worldToVertexSkew_Trans); + vertexSpaceBox1 = transform(worldToVertexSkew, box); + // compute swept aabb + sweptAABBMeshSpaceExtents = vertexSpaceBox1.computeAABBExtent(); + + meshSpaceOrigin = worldToVertexSkew.transform(box.center); + meshSpaceDir = worldToVertexSkew.rotate(unitDir*distance); // also applies scale to direction/length + } + + sweptAABBMeshSpaceExtents += PxVec3(inflation); // inflate the bounds with additive inflation + sweptAABBMeshSpaceExtents *= 1.01f; // fatten the bounds to account for numerical discrepancies + + PxReal dirLen = PxMax(meshSpaceDir.magnitude(), 1e-5f); + PxReal distCoeff = 1.0f; + if (!isIdentity) + distCoeff = dirLen / distance; + + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + + const Matrix34Padded meshToBox = worldToBox*meshToWorldSkew; + const PxTransform boxTransform = box.getTransform(); // PT: TODO: this is not needed when there's no hit (TA34704) + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localDirDist = localDir*distance; + SweepBoxMeshHitCallback callback( // using eMULTIPLE with shrinkMaxT + CallbackMode::eMULTIPLE, meshToBox, distance, bothTriangleSidesCollide, box, localDirDist, localDir, unitDir, hitFlags, inflation, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff); + + const PxVec3 dir = meshSpaceDir/dirLen; + boxSweepVsMeshCBOld(meshData->getBV4Tree(), NULL, NULL, meshSpaceOrigin, sweptAABBMeshSpaceExtents, dir, dirLen, gBoxMeshSweepCallback, &callback); + + return callback.finalizeHit(sweepHit, triMeshGeom, pose, boxTransform, localDir, meshBothSides, isDoubleSided); +} + +static bool gConvexVsMeshSweepCallback(void* userData, const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, PxU32 triangleIndex, /*const PxU32* vertexIndices,*/ float& dist) +{ + SweepConvexMeshHitCallback* callback = reinterpret_cast(userData); + PX_ALIGN_PREFIX(16) char buffer[sizeof(PxRaycastHit)] PX_ALIGN_SUFFIX(16); + PxRaycastHit& hit = reinterpret_cast(buffer); + hit.faceIndex = triangleIndex; + return !callback->SweepConvexMeshHitCallback::processHit(hit, p0, p1, p2, dist, NULL/*vertexIndices*/); +} + +void physx::Gu::sweepConvex_MeshGeom_BV4(const TriangleMesh* mesh, const Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH34); + const BV4TriangleMesh* meshData = static_cast(mesh); + BV4_GenericSweepCB(hullBox, localDir, distance, meshData->getBV4Tree(), gConvexVsMeshSweepCallback, &callback, anyHit); +} + +#endif + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h new file mode 100644 index 000000000..d9574476b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h @@ -0,0 +1,417 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_MIDPHASE_INTERFACE_H +#define GU_MIDPHASE_INTERFACE_H + +#include "GuOverlapTests.h" +#include "GuRaycastTests.h" +#include "GuTriangleMesh.h" +#include "PsVecMath.h" + +// PT: this file contains the common interface for all midphase implementations. Specifically the Midphase namespace contains the +// midphase-related entry points, dispatching calls to the proper implementations depending on the triangle mesh's type. The rest of it +// is simply classes & structs shared by all implementations. + +namespace physx +{ + class PxMeshScale; + class PxTriangleMeshGeometry; +namespace Cm +{ + class Matrix34; + class FastVertex2ShapeScaling; +} + +namespace Gu +{ + struct ConvexHullData; + + struct CallbackMode { enum Enum { eANY, eCLOSEST, eMULTIPLE }; }; + + template + struct MeshHitCallback + { + CallbackMode::Enum mode; + + MeshHitCallback(CallbackMode::Enum aMode) : mode(aMode) {} + + PX_FORCE_INLINE bool inAnyMode() const { return mode == CallbackMode::eANY; } + PX_FORCE_INLINE bool inClosestMode() const { return mode == CallbackMode::eCLOSEST; } + PX_FORCE_INLINE bool inMultipleMode() const { return mode == CallbackMode::eMULTIPLE; } + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const HitType& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32* vIndices) = 0; + + virtual ~MeshHitCallback() {} + }; + + struct SweepConvexMeshHitCallback; + + struct LimitedResults + { + PxU32* mResults; + PxU32 mNbResults; + PxU32 mMaxResults; + PxU32 mStartIndex; + PxU32 mNbSkipped; + bool mOverflow; + + PX_FORCE_INLINE LimitedResults(PxU32* results, PxU32 maxResults, PxU32 startIndex) + : mResults(results), mMaxResults(maxResults), mStartIndex(startIndex) + { + reset(); + } + + PX_FORCE_INLINE void reset() + { + mNbResults = 0; + mNbSkipped = 0; + mOverflow = false; + } + + PX_FORCE_INLINE bool add(PxU32 index) + { + if(mNbResults>=mMaxResults) + { + mOverflow = true; + return false; + } + + if(mNbSkipped>=mStartIndex) + mResults[mNbResults++] = index; + else + mNbSkipped++; + + return true; + } + }; + + // RTree forward declarations + PX_PHYSX_COMMON_API PxU32 raycast_triangleMesh_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits); + PX_PHYSX_COMMON_API bool intersectSphereVsMesh_RTREE(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectBoxVsMesh_RTREE (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectCapsuleVsMesh_RTREE(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API void intersectOBB_RTREE(const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned); + PX_PHYSX_COMMON_API bool sweepCapsule_MeshGeom_RTREE( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API bool sweepBox_MeshGeom_RTREE( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API void sweepConvex_MeshGeom_RTREE(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit); + +#if PX_INTEL_FAMILY + // BV4 forward declarations + PX_PHYSX_COMMON_API PxU32 raycast_triangleMesh_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits); + PX_PHYSX_COMMON_API bool intersectSphereVsMesh_BV4 (const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectBoxVsMesh_BV4 (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API bool intersectCapsuleVsMesh_BV4 (const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + PX_PHYSX_COMMON_API void intersectOBB_BV4(const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned); + PX_PHYSX_COMMON_API bool sweepCapsule_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API bool sweepBox_MeshGeom_BV4( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + PX_PHYSX_COMMON_API void sweepConvex_MeshGeom_BV4(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit); +#endif + + typedef PxU32 (*MidphaseRaycastFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits); + + typedef bool (*MidphaseSphereOverlapFunction) (const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + typedef bool (*MidphaseBoxOverlapFunction) (const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + typedef bool (*MidphaseCapsuleOverlapFunction) (const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results); + typedef void (*MidphaseBoxCBOverlapFunction) (const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned); + + typedef bool (*MidphaseCapsuleSweepFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + typedef bool (*MidphaseBoxSweepFunction)( const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation); + typedef void (*MidphaseConvexSweepFunction)( const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit); + +namespace Midphase +{ + PX_FORCE_INLINE bool outputError() + { + static bool reportOnlyOnce = false; + if(!reportOnlyOnce) + { + reportOnlyOnce = true; + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "BV4 midphase only supported on Intel platforms."); + } + return false; + } +} + + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + #else + static PxU32 unsupportedMidphase( const TriangleMesh*, const PxTriangleMeshGeometry&, const PxTransform&, + const PxVec3&, const PxVec3&, PxReal, + PxHitFlags, PxU32, PxRaycastHit* PX_RESTRICT) + { + return PxU32(Midphase::outputError()); + } + static bool unsupportedSphereOverlapMidphase(const Sphere&, const TriangleMesh&, const PxTransform&, const PxMeshScale&, LimitedResults*) + { + return Midphase::outputError(); + } + static bool unsupportedBoxOverlapMidphase(const Box&, const TriangleMesh&, const PxTransform&, const PxMeshScale&, LimitedResults*) + { + return Midphase::outputError(); + } + static bool unsupportedCapsuleOverlapMidphase(const Capsule&, const TriangleMesh&, const PxTransform&, const PxMeshScale&, LimitedResults*) + { + return Midphase::outputError(); + } + static void unsupportedBoxCBOverlapMidphase(const TriangleMesh*, const Box&, MeshHitCallback&, bool, bool) + { + Midphase::outputError(); + } + static bool unsupportedBoxSweepMidphase(const TriangleMesh*, const PxTriangleMeshGeometry&, const PxTransform&, const Gu::Box&, const PxVec3&, const PxReal, PxSweepHit&, PxHitFlags, const PxReal) + { + return Midphase::outputError(); + } + static bool unsupportedCapsuleSweepMidphase(const TriangleMesh*, const PxTriangleMeshGeometry&, const PxTransform&, const Gu::Capsule&, const PxVec3&, const PxReal, PxSweepHit&, PxHitFlags, const PxReal) + { + return Midphase::outputError(); + } + static void unsupportedConvexSweepMidphase(const TriangleMesh*, const Gu::Box&, const PxVec3&, const PxReal, SweepConvexMeshHitCallback&, bool) + { + Midphase::outputError(); + } + #endif + + static const MidphaseRaycastFunction gMidphaseRaycastTable[PxMeshMidPhase::eLAST] = + { + raycast_triangleMesh_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + raycast_triangleMesh_BV4, + #else + unsupportedMidphase, + #endif + }; + + static const MidphaseSphereOverlapFunction gMidphaseSphereOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectSphereVsMesh_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + intersectSphereVsMesh_BV4, + #else + unsupportedSphereOverlapMidphase, + #endif + }; + + static const MidphaseBoxOverlapFunction gMidphaseBoxOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectBoxVsMesh_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + intersectBoxVsMesh_BV4, + #else + unsupportedBoxOverlapMidphase, + #endif + }; + + static const MidphaseCapsuleOverlapFunction gMidphaseCapsuleOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectCapsuleVsMesh_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + intersectCapsuleVsMesh_BV4, + #else + unsupportedCapsuleOverlapMidphase, + #endif + }; + + static const MidphaseBoxCBOverlapFunction gMidphaseBoxCBOverlapTable[PxMeshMidPhase::eLAST] = + { + intersectOBB_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + intersectOBB_BV4, + #else + unsupportedBoxCBOverlapMidphase, + #endif + }; + + static const MidphaseBoxSweepFunction gMidphaseBoxSweepTable[PxMeshMidPhase::eLAST] = + { + sweepBox_MeshGeom_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + sweepBox_MeshGeom_BV4, + #else + unsupportedBoxSweepMidphase, + #endif + }; + + static const MidphaseCapsuleSweepFunction gMidphaseCapsuleSweepTable[PxMeshMidPhase::eLAST] = + { + sweepCapsule_MeshGeom_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + sweepCapsule_MeshGeom_BV4, + #else + unsupportedCapsuleSweepMidphase, + #endif + }; + + static const MidphaseConvexSweepFunction gMidphaseConvexSweepTable[PxMeshMidPhase::eLAST] = + { + sweepConvex_MeshGeom_RTREE, + #if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + sweepConvex_MeshGeom_BV4, + #else + unsupportedConvexSweepMidphase, + #endif + }; + +namespace Midphase +{ + // \param[in] mesh triangle mesh to raycast against + // \param[in] meshGeom geometry object associated with the mesh + // \param[in] meshTransform pose/transform of geometry object + // \param[in] rayOrigin ray's origin + // \param[in] rayDir ray's unit dir + // \param[in] maxDist ray's length/max distance + // \param[in] hitFlags query behavior flags + // \param[in] maxHits max number of hits = size of 'hits' buffer + // \param[out] hits result buffer where to write raycast hits + // \return number of hits written to 'hits' result buffer + // \note there's no mechanism to report overflow. Returned number of hits is just clamped to maxHits. + PX_FORCE_INLINE PxU32 raycastTriangleMesh( const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseRaycastTable[index](mesh, meshGeom, meshTransform, rayOrigin, rayDir, maxDist, hitFlags, maxHits, hits); + } + + // \param[in] sphere sphere + // \param[in] mesh triangle mesh + // \param[in] meshTransform pose/transform of triangle mesh + // \param[in] meshScale mesh scale + // \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough + // \return true if at least one overlap has been found + PX_FORCE_INLINE bool intersectSphereVsMesh(const Sphere& sphere, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) + { + const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseSphereOverlapTable[index](sphere, mesh, meshTransform, meshScale, results); + } + + // \param[in] box box + // \param[in] mesh triangle mesh + // \param[in] meshTransform pose/transform of triangle mesh + // \param[in] meshScale mesh scale + // \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough + // \return true if at least one overlap has been found + PX_FORCE_INLINE bool intersectBoxVsMesh(const Box& box, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) + { + const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseBoxOverlapTable[index](box, mesh, meshTransform, meshScale, results); + } + + // \param[in] capsule capsule + // \param[in] mesh triangle mesh + // \param[in] meshTransform pose/transform of triangle mesh + // \param[in] meshScale mesh scale + // \param[out] results results object if multiple hits are needed, NULL if a simple boolean answer is enough + // \return true if at least one overlap has been found + PX_FORCE_INLINE bool intersectCapsuleVsMesh(const Capsule& capsule, const TriangleMesh& mesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) + { + const PxU32 index = PxU32(mesh.getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseCapsuleOverlapTable[index](capsule, mesh, meshTransform, meshScale, results); + } + + // \param[in] mesh triangle mesh + // \param[in] box box + // \param[in] callback callback object, called each time a hit is found + // \param[in] bothTriangleSidesCollide true for double-sided meshes + // \param[in] checkObbIsAligned true to use a dedicated codepath for axis-aligned boxes + PX_FORCE_INLINE void intersectOBB(const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned = true) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + gMidphaseBoxCBOverlapTable[index](mesh, obb, callback, bothTriangleSidesCollide, checkObbIsAligned); + } + + // \param[in] mesh triangle mesh + // \param[in] meshGeom geometry object associated with the mesh + // \param[in] meshTransform pose/transform of geometry object + // \param[in] capsule swept capsule + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] sweepHit hit result + // \param[in] hitFlags query behavior flags + // \param[in] inflation optional inflation value for swept shape + // \return true if a hit was found, false otherwise + PX_FORCE_INLINE bool sweepCapsuleVsMesh(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform, + const Gu::Capsule& capsule, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseCapsuleSweepTable[index](mesh, meshGeom, meshTransform, capsule, unitDir, distance, sweepHit, hitFlags, inflation); + } + + // \param[in] mesh triangle mesh + // \param[in] meshGeom geometry object associated with the mesh + // \param[in] meshTransform pose/transform of geometry object + // \param[in] box swept box + // \param[in] unitDir sweep's unit dir + // \param[in] distance sweep's length/max distance + // \param[out] sweepHit hit result + // \param[in] hitFlags query behavior flags + // \param[in] inflation optional inflation value for swept shape + // \return true if a hit was found, false otherwise + PX_FORCE_INLINE bool sweepBoxVsMesh(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshTransform, + const Gu::Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + return gMidphaseBoxSweepTable[index](mesh, meshGeom, meshTransform, box, unitDir, distance, sweepHit, hitFlags, inflation); + } + + // \param[in] mesh triangle mesh + // \param[in] hullBox hull's bounding box + // \param[in] localDir sweep's unit dir, in local/mesh space + // \param[in] distance sweep's length/max distance + // \param[in] callback callback object, called each time a hit is found + // \param[in] anyHit true for PxHitFlag::eMESH_ANY queries + PX_FORCE_INLINE void sweepConvexVsMesh(const TriangleMesh* mesh, const Gu::Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool anyHit) + { + const PxU32 index = PxU32(mesh->getConcreteType() - PxConcreteType::eTRIANGLE_MESH_BVH33); + gMidphaseConvexSweepTable[index](mesh, hullBox, localDir, distance, callback, anyHit); + } +} +} +} +#endif // GU_MIDPHASE_INTERFACE_H diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp new file mode 100644 index 000000000..f88f55381 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp @@ -0,0 +1,887 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "GuSweepMesh.h" +#include "GuIntersectionRayTriangle.h" +#include "GuIntersectionCapsuleTriangle.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionRayBoxSIMD.h" +#include "GuSphere.h" +#include "GuBoxConversion.h" +#include "GuConvexUtilsInternal.h" +#include "GuVecTriangle.h" +#include "GuIntersectionTriangleBox.h" +#include "GuSIMDHelpers.h" +#include "GuTriangleVertexPointers.h" +#include "GuRTree.h" +#include "GuTriangleMeshRTree.h" +#include "GuInternal.h" + +// This file contains code specific to the RTree midphase. + +using namespace physx; +using namespace Cm; +using namespace Gu; +using namespace physx::shdfnd::aos; + +struct MeshRayCollider +{ + template + PX_PHYSX_COMMON_API static void collide( + const PxVec3& orig, const PxVec3& dir, // dir is not normalized (full length), both in mesh space (unless meshWorld is non-zero) + PxReal maxT, // maxT is from [0,1], if maxT is 0.0f, AABB traversal will be used + bool bothTriangleSidesCollide, const RTreeTriangleMesh* mesh, MeshHitCallback& callback, + const PxVec3* inflate = NULL); + + PX_PHYSX_COMMON_API static void collideOBB( + const Box& obb, bool bothTriangleSidesCollide, const RTreeTriangleMesh* mesh, MeshHitCallback& callback, + bool checkObbIsAligned = true); // perf hint, pass false if obb is rarely axis aligned +}; + +class SimpleRayTriOverlap +{ +public: + PX_FORCE_INLINE SimpleRayTriOverlap(const PxVec3& origin, const PxVec3& dir, bool bothSides, PxReal geomEpsilon) + : mOrigin(origin), mDir(dir), mBothSides(bothSides), mGeomEpsilon(geomEpsilon) + { + } + + PX_FORCE_INLINE Ps::IntBool overlap(const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, PxRaycastHit& hit) const + { + if(!intersectRayTriangle(mOrigin, mDir, vert0, vert1, vert2, hit.distance, hit.u, hit.v, !mBothSides, mGeomEpsilon)) + return false; + + if(hit.distance< 0.0f) // test if the ray intersection t is negative + return false; + + return true; + } + + PxVec3 mOrigin; + PxVec3 mDir; + bool mBothSides; + PxReal mGeomEpsilon; +}; + +using Gu::RTree; + +// This callback comes from RTree and decodes LeafTriangle indices stored in rtree into actual triangles +// This callback is needed because RTree doesn't know that it stores triangles since it's a general purpose spatial index + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +template +struct RayRTreeCallback : RTree::CallbackRaycast, RTree::Callback +{ + MeshHitCallback& outerCallback; + PxI32 has16BitIndices; + const void* mTris; + const PxVec3* mVerts; + const PxVec3* mInflate; + const SimpleRayTriOverlap rayCollider; + PxReal maxT; + PxRaycastHit closestHit; // recorded closest hit over the whole traversal (only for callback mode eCLOSEST) + PxVec3 cv0, cv1, cv2; // PT: make sure these aren't last in the class, to safely V4Load them + PxU32 cis[3]; + bool hadClosestHit; + const bool closestMode; + Vec3V inflateV, rayOriginV, rayDirV; + + RayRTreeCallback( + PxReal geomEpsilon, MeshHitCallback& callback, + PxI32 has16BitIndices_, const void* tris, const PxVec3* verts, + const PxVec3& origin, const PxVec3& dir, PxReal maxT_, bool bothSides, const PxVec3* inflate) + : outerCallback(callback), has16BitIndices(has16BitIndices_), + mTris(tris), mVerts(verts), mInflate(inflate), rayCollider(origin, dir, bothSides, geomEpsilon), + maxT(maxT_), closestMode(callback.inClosestMode()) + { + PX_ASSERT(closestHit.distance == PX_MAX_REAL); + hadClosestHit = false; + if (tInflate) + inflateV = V3LoadU(*mInflate); + rayOriginV = V3LoadU(rayCollider.mOrigin); + rayDirV = V3LoadU(rayCollider.mDir); + } + + PX_FORCE_INLINE void getVertIndices(PxU32 triIndex, PxU32& i0, PxU32 &i1, PxU32 &i2) + { + if(has16BitIndices) + { + const PxU16* p = reinterpret_cast(mTris) + triIndex*3; + i0 = p[0]; i1 = p[1]; i2 = p[2]; + } + else + { + const PxU32* p = reinterpret_cast(mTris) + triIndex*3; + i0 = p[0]; i1 = p[1]; i2 = p[2]; + } + } + + virtual PX_FORCE_INLINE bool processResults(PxU32 NumTouched, PxU32* Touched, PxF32& newMaxT) + { + PX_ASSERT(NumTouched > 0); + // Loop through touched leaves + PxRaycastHit tempHit; + for(PxU32 leaf = 0; leaf0 so we can use integers + if (closestMode) + { + if(tempHit.distance < closestHit.distance) + { + closestHit = tempHit; + newMaxT = PxMin(tempHit.distance, newMaxT); + cv0 = v0; cv1 = v1; cv2 = v2; + cis[0] = vinds[0]; cis[1] = vinds[1]; cis[2] = vinds[2]; + hadClosestHit = true; + } + } else + { + PxReal shrunkMaxT = newMaxT; + PxAgain again = outerCallback.processHit(tempHit, v0, v1, v2, shrunkMaxT, vinds); + if (!again) + return false; + if (shrunkMaxT < newMaxT) + { + newMaxT = shrunkMaxT; + maxT = shrunkMaxT; + } + } + + if (outerCallback.inAnyMode()) // early out if in ANY mode + return false; + } + + } // for(PxU32 leaf = 0; leaf& callback, + bool checkObbIsAligned) +{ + const PxU32 maxResults = RTREE_N; // maxResults=rtree page size for more efficient early out + PxU32 buf[maxResults]; + RayRTreeCallback rTreeCallback( + mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(), + PxVec3(0), PxVec3(0), 0.0f, bothTriangleSidesCollide, NULL); + if (checkObbIsAligned && PxAbs(PxQuat(obb.rot).w) > 0.9999f) + { + PxVec3 aabbExtents = obb.computeAABBExtent(); + mi->getRTree().traverseAABB(obb.center - aabbExtents, obb.center + aabbExtents, maxResults, buf, &rTreeCallback); + } else + mi->getRTree().traverseOBB(obb, maxResults, buf, &rTreeCallback); +} + +template +void MeshRayCollider::collide( + const PxVec3& orig, const PxVec3& dir, PxReal maxT, bool bothSides, + const RTreeTriangleMesh* mi, MeshHitCallback& callback, + const PxVec3* inflate) +{ + const PxU32 maxResults = RTREE_N; // maxResults=rtree page size for more efficient early out + PxU32 buf[maxResults]; + if (maxT == 0.0f) // AABB traversal path + { + RayRTreeCallback rTreeCallback( + mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(), + orig, dir, maxT, bothSides, inflate); + PxVec3 inflate1 = tInflate ? *inflate : PxVec3(0); // both maxT and inflate can be zero, so need to check tInflate + mi->getRTree().traverseAABB(orig-inflate1, orig+inflate1, maxResults, buf, &rTreeCallback); + } + else // ray traversal path + { + RayRTreeCallback rTreeCallback( + mi->getGeomEpsilon(), callback, mi->has16BitIndices(), mi->getTrianglesFast(), mi->getVerticesFast(), + orig, dir, maxT, bothSides, inflate); + mi->getRTree().traverseRay(orig, dir, maxResults, buf, &rTreeCallback, inflate, maxT); + } +} + + +#define TINST(a,b) \ +template void MeshRayCollider::collide( \ + const PxVec3& orig, const PxVec3& dir, PxReal maxT, bool bothSides, const RTreeTriangleMesh* mesh, \ + MeshHitCallback& callback, const PxVec3* inflate); + +TINST(0,0) +TINST(1,0) +TINST(0,1) +TINST(1,1) + +#undef TINST + +#include "GuRaycastTests.h" +#include "PxTriangleMeshGeometry.h" +#include "GuTriangleMesh.h" +#include "CmScaling.h" + +struct RayMeshColliderCallback : public MeshHitCallback +{ + PxRaycastHit* mDstBase; + PxU32 mHitNum; + PxU32 mMaxHits; + const PxMeshScale* mScale; + const PxTransform* mPose; + const Matrix34* mWorld2vertexSkew; + PxU32 mHitFlags; + const PxVec3& mRayDir; + bool mIsDoubleSided; + float mDistCoeff; + + RayMeshColliderCallback( + CallbackMode::Enum mode_, PxRaycastHit* hits, PxU32 maxHits, const PxMeshScale* scale, const PxTransform* pose, + const Matrix34* world2vertexSkew, PxU32 hitFlags, const PxVec3& rayDir, bool isDoubleSided, float distCoeff) : + MeshHitCallback (mode_), + mDstBase (hits), + mHitNum (0), + mMaxHits (maxHits), + mScale (scale), + mPose (pose), + mWorld2vertexSkew (world2vertexSkew), + mHitFlags (hitFlags), + mRayDir (rayDir), + mIsDoubleSided (isDoubleSided), + mDistCoeff (distCoeff) + { + } + + // return false for early out + virtual bool processHit( + const PxRaycastHit& lHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal&, const PxU32*) + { + const PxReal u = lHit.u, v = lHit.v; + const PxVec3 localImpact = (1.0f - u - v)*lp0 + u*lp1 + v*lp2; + + //not worth concatenating to do 1 transform: PxMat34Legacy vertex2worldSkew = scaling.getVertex2WorldSkew(absPose); + // PT: TODO: revisit this for N hits + PxRaycastHit hit = lHit; + hit.position = mPose->transform(mScale->transform(localImpact)); + hit.flags = PxHitFlag::ePOSITION|PxHitFlag::eUV|PxHitFlag::eFACE_INDEX; + hit.normal = PxVec3(0.0f); + hit.distance *= mDistCoeff; + + // Compute additional information if needed + if(mHitFlags & PxHitFlag::eNORMAL) + { + // User requested impact normal + const PxVec3 localNormal = (lp1 - lp0).cross(lp2 - lp0); + + if(mWorld2vertexSkew) + { + hit.normal = mWorld2vertexSkew->rotateTranspose(localNormal); + if (mScale->hasNegativeDeterminant()) + Ps::swap(hit.u, hit.v); // have to swap the UVs though since they were computed in mesh local space + } + else + hit.normal = hit.normal = mPose->rotate(localNormal); + hit.normal.normalize(); + + // PT: figure out correct normal orientation (DE7458) + // - if the mesh is single-sided the normal should be the regular triangle normal N, regardless of eMESH_BOTH_SIDES. + // - if the mesh is double-sided the correct normal can be either N or -N. We take the one opposed to ray direction. + if(mIsDoubleSided && hit.normal.dot(mRayDir) > 0.0f) + hit.normal = -hit.normal; + + hit.flags |= PxHitFlag::eNORMAL; + } + + // PT: no callback => store results in provided buffer + if(mHitNum == mMaxHits) + return false; + + mDstBase[mHitNum++] = hit; + return true; + } + +private: + RayMeshColliderCallback& operator=(const RayMeshColliderCallback&); +}; + +PxU32 physx::Gu::raycast_triangleMesh_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, + PxHitFlags hitFlags, PxU32 maxHits, PxRaycastHit* PX_RESTRICT hits) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + + const RTreeTriangleMesh* meshData = static_cast(mesh); + + //scaling: transform the ray to vertex space + + PxVec3 orig, dir; + Matrix34 world2vertexSkew; + Matrix34* world2vertexSkewP = NULL; + PxReal distCoeff = 1.0f; + if(meshGeom.scale.isIdentity()) + { + orig = pose.transformInv(rayOrigin); + dir = pose.rotateInv(rayDir); + } + else + { + world2vertexSkew = meshGeom.scale.getInverse() * pose.getInverse(); + world2vertexSkewP = &world2vertexSkew; + orig = world2vertexSkew.transform(rayOrigin); + dir = world2vertexSkew.rotate(rayDir); + { + distCoeff = dir.normalize(); + maxDist *= distCoeff; + maxDist += 1e-3f; + distCoeff = 1.0f / distCoeff; + } + } + + const bool isDoubleSided = meshGeom.meshFlags.isSet(PxMeshGeometryFlag::eDOUBLE_SIDED); + const bool bothSides = isDoubleSided || (hitFlags & PxHitFlag::eMESH_BOTH_SIDES); + + RayMeshColliderCallback callback( + (maxHits > 1) ? CallbackMode::eMULTIPLE : (hitFlags & PxHitFlag::eMESH_ANY ? CallbackMode::eANY : CallbackMode::eCLOSEST), + hits, maxHits, &meshGeom.scale, &pose, world2vertexSkewP, hitFlags, rayDir, isDoubleSided, distCoeff); + + MeshRayCollider::collide<0, 1>(orig, dir, maxDist, bothSides, static_cast(meshData), callback, NULL); + return callback.mHitNum; +} + + +static PX_INLINE void computeSweptAABBAroundOBB( + const Box& obb, PxVec3& sweepOrigin, PxVec3& sweepExtents, PxVec3& sweepDir, PxReal& sweepLen) +{ + PxU32 other1, other2; + // largest axis of the OBB is the sweep direction, sum of abs of two other is the swept AABB extents + PxU32 lai = Ps::largestAxis(obb.extents, other1, other2); + PxVec3 longestAxis = obb.rot[lai]*obb.extents[lai]; + PxVec3 absOther1 = obb.rot[other1].abs()*obb.extents[other1]; + PxVec3 absOther2 = obb.rot[other2].abs()*obb.extents[other2]; + sweepOrigin = obb.center - longestAxis; + sweepExtents = absOther1 + absOther2 + PxVec3(GU_MIN_AABB_EXTENT); // see comments for GU_MIN_AABB_EXTENT + sweepLen = 2.0f; // length is already included in longestAxis + sweepDir = longestAxis; +} + +enum { eSPHERE, eCAPSULE, eBOX }; // values for tSCB + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. + #pragma warning( disable : 4512 ) // assignment operator could not be generated +#endif + +namespace +{ +struct IntersectShapeVsMeshCallback : MeshHitCallback +{ + PX_NOCOPY(IntersectShapeVsMeshCallback) +public: + IntersectShapeVsMeshCallback(const PxMat33& vertexToShapeSkew, LimitedResults* results, bool flipNormal) + : MeshHitCallback(CallbackMode::eMULTIPLE), + mVertexToShapeSkew (vertexToShapeSkew), + mResults (results), + mAnyHits (false), + mFlipNormal (flipNormal) + { + } + virtual ~IntersectShapeVsMeshCallback(){} + + const PxMat33& mVertexToShapeSkew; // vertex to box without translation for boxes + LimitedResults* mResults; + bool mAnyHits; + bool mFlipNormal; + + PX_FORCE_INLINE bool recordHit(const PxRaycastHit& aHit, Ps::IntBool hit) + { + if(hit) + { + mAnyHits = true; + if(mResults) + mResults->add(aHit.faceIndex); + else + return false; // abort traversal if we are only interested in firstContact (mResults is NULL) + } + return true; // if we are here, either no triangles were hit or multiple results are expected => continue traversal + } +}; + +template +struct IntersectSphereVsMeshCallback : IntersectShapeVsMeshCallback +{ + IntersectSphereVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {} + virtual ~IntersectSphereVsMeshCallback(){} + PxF32 mMinDist2; + PxVec3 mLocalCenter; // PT: sphere center in local/mesh space + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*) + { + const Vec3V v0 = V3LoadU(tScaleIsIdentity ? av0 : mVertexToShapeSkew * av0); + const Vec3V v1 = V3LoadU(tScaleIsIdentity ? av1 : mVertexToShapeSkew * (mFlipNormal ? av2 : av1)); + const Vec3V v2 = V3LoadU(tScaleIsIdentity ? av2 : mVertexToShapeSkew * (mFlipNormal ? av1 : av2)); + + FloatV dummy1, dummy2; + Vec3V closestP; + PxReal dist2; + FStore(distancePointTriangleSquared(V3LoadU(mLocalCenter), v0, v1, v2, dummy1, dummy2, closestP), &dist2); + return recordHit(aHit, dist2 <= mMinDist2); + } +}; + +template +struct IntersectCapsuleVsMeshCallback : IntersectShapeVsMeshCallback +{ + IntersectCapsuleVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {} + virtual ~IntersectCapsuleVsMeshCallback(){} + + Capsule mLocalCapsule; // PT: capsule in mesh/local space + CapsuleTriangleOverlapData mParams; + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*) + { + bool hit; + if(tScaleIsIdentity) + { + const PxVec3 normal = (av0 - av1).cross(av0 - av2); + hit = intersectCapsuleTriangle(normal, av0, av1, av2, mLocalCapsule, mParams); + } + else + { + const PxVec3 v0 = mVertexToShapeSkew * av0; + const PxVec3 v1 = mVertexToShapeSkew * (mFlipNormal ? av2 : av1); + const PxVec3 v2 = mVertexToShapeSkew * (mFlipNormal ? av1 : av2); + const PxVec3 normal = (v0 - v1).cross(v0 - v2); + hit = intersectCapsuleTriangle(normal, v0, v1, v2, mLocalCapsule, mParams); + } + return recordHit(aHit, hit); + } +}; + +template +struct IntersectBoxVsMeshCallback : IntersectShapeVsMeshCallback +{ + IntersectBoxVsMeshCallback(const PxMat33& m, LimitedResults* r, bool flipNormal) : IntersectShapeVsMeshCallback(m, r, flipNormal) {} + virtual ~IntersectBoxVsMeshCallback(){} + + Matrix34 mVertexToBox; + Vec3p mBoxExtents, mBoxCenter; + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal&, const PxU32*) + { + Vec3p v0, v1, v2; + if(tScaleIsIdentity) + { + v0 = mVertexToShapeSkew * av0; // transform from skewed mesh vertex to box space, + v1 = mVertexToShapeSkew * av1; // this includes inverse skew, inverse mesh shape transform and inverse box basis + v2 = mVertexToShapeSkew * av2; + } + else + { + v0 = mVertexToBox.transform(av0); + v1 = mVertexToBox.transform(mFlipNormal ? av2 : av1); + v2 = mVertexToBox.transform(mFlipNormal ? av1 : av2); + } + + // PT: this one is safe because we're using Vec3p for all parameters + const Ps::IntBool hit = intersectTriangleBox_Unsafe(mBoxCenter, mBoxExtents, v0, v1, v2); + return recordHit(aHit, hit); + } +}; +} + +#if PX_VC + #pragma warning(pop) +#endif + +template +static bool intersectAnyVsMeshT( + const Sphere* worldSphere, const Capsule* worldCapsule, const Box* worldOBB, + const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, + LimitedResults* results) +{ + const bool flipNormal = meshScale.hasNegativeDeterminant(); + PxMat33 shapeToVertexSkew, vertexToShapeSkew; + if (!idtMeshScale && tSCB != eBOX) + { + vertexToShapeSkew = meshScale.toMat33(); + shapeToVertexSkew = vertexToShapeSkew.getInverse(); + } + + if (tSCB == eSPHERE) + { + IntersectSphereVsMeshCallback callback(vertexToShapeSkew, results, flipNormal); + // transform sphere center from world to mesh shape space + const PxVec3 center = meshTransform.transformInv(worldSphere->center); + + // callback will transform verts + callback.mLocalCenter = center; + callback.mMinDist2 = worldSphere->radius*worldSphere->radius; + + PxVec3 sweepOrigin, sweepDir, sweepExtents; + PxReal sweepLen; + if (!idtMeshScale) + { + // AP: compute a swept AABB around an OBB around a skewed sphere + // TODO: we could do better than an AABB around OBB actually because we can slice off the corners.. + const Box worldOBB_(worldSphere->center, PxVec3(worldSphere->radius), PxMat33(PxIdentity)); + Box vertexOBB; + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + computeSweptAABBAroundOBB(vertexOBB, sweepOrigin, sweepExtents, sweepDir, sweepLen); + } else + { + sweepOrigin = center; + sweepDir = PxVec3(1.0f,0,0); + sweepLen = 0.0f; + sweepExtents = PxVec3(PxMax(worldSphere->radius, GU_MIN_AABB_EXTENT)); + } + + MeshRayCollider::collide<1, 1>(sweepOrigin, sweepDir, sweepLen, true, static_cast(&triMesh), callback, &sweepExtents); + + return callback.mAnyHits; + } + else if (tSCB == eCAPSULE) + { + IntersectCapsuleVsMeshCallback callback(vertexToShapeSkew, results, flipNormal); + const PxF32 radius = worldCapsule->radius; + + // transform world capsule to mesh shape space + callback.mLocalCapsule.p0 = meshTransform.transformInv(worldCapsule->p0); + callback.mLocalCapsule.p1 = meshTransform.transformInv(worldCapsule->p1); + callback.mLocalCapsule.radius = radius; + callback.mParams.init(callback.mLocalCapsule); + + if (idtMeshScale) + { + // traverse a sweptAABB around the capsule + const PxVec3 radius3(radius); + MeshRayCollider::collide<1, 0>(callback.mLocalCapsule.p0, callback.mLocalCapsule.p1-callback.mLocalCapsule.p0, 1.0f, true, static_cast(&triMesh), callback, &radius3); + } + else + { + // make vertex space OBB + Box vertexOBB; + Box worldOBB_; + worldOBB_.create(*worldCapsule); // AP: potential optimization (meshTransform.inverse is already in callback.mCapsule) + computeVertexSpaceOBB(vertexOBB, worldOBB_, meshTransform, meshScale); + + MeshRayCollider::collideOBB(vertexOBB, true, static_cast(&triMesh), callback); + } + return callback.mAnyHits; + } + else if (tSCB == eBOX) + { + Box vertexOBB; // query box in vertex space + if (idtMeshScale) + { + // mesh scale is identity - just inverse transform the box without optimization + vertexOBB = transformBoxOrthonormal(*worldOBB, meshTransform.getInverse()); + // mesh vertices will be transformed from skewed vertex space directly to box AABB space + // box inverse rotation is baked into the vertexToShapeSkew transform + // if meshScale is not identity, vertexOBB already effectively includes meshScale transform + PxVec3 boxCenter; + getInverse(vertexToShapeSkew, boxCenter, vertexOBB.rot, vertexOBB.center); + IntersectBoxVsMeshCallback callback(vertexToShapeSkew, results, flipNormal); + + callback.mBoxCenter = -boxCenter; + callback.mBoxExtents = worldOBB->extents; // extents do not change + + MeshRayCollider::collideOBB(vertexOBB, true, static_cast(&triMesh), callback); + + return callback.mAnyHits; + } else + { + computeVertexSpaceOBB(vertexOBB, *worldOBB, meshTransform, meshScale); + + // mesh scale needs to be included - inverse transform and optimize the box + const PxMat33 vertexToWorldSkew_Rot = PxMat33Padded(meshTransform.q) * meshScale.toMat33(); + const PxVec3& vertexToWorldSkew_Trans = meshTransform.p; + + Matrix34 tmp; + buildMatrixFromBox(tmp, *worldOBB); + const Matrix34 inv = tmp.getInverseRT(); + const Matrix34 _vertexToWorldSkew(vertexToWorldSkew_Rot, vertexToWorldSkew_Trans); + + IntersectBoxVsMeshCallback callback(vertexToShapeSkew, results, flipNormal); + callback.mVertexToBox = inv * _vertexToWorldSkew; + callback.mBoxCenter = PxVec3(0.0f); + callback.mBoxExtents = worldOBB->extents; // extents do not change + + MeshRayCollider::collideOBB(vertexOBB, true, static_cast(&triMesh), callback); + + return callback.mAnyHits; + } + } + else + { + PX_ASSERT(0); + return false; + } +} + +template +static bool intersectAnyVsMesh( + const Sphere* worldSphere, const Capsule* worldCapsule, const Box* worldOBB, + const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, + LimitedResults* results) +{ + PX_ASSERT(triMesh.getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + if (meshScale.isIdentity()) + return intersectAnyVsMeshT(worldSphere, worldCapsule, worldOBB, triMesh, meshTransform, meshScale, results); + else + return intersectAnyVsMeshT(worldSphere, worldCapsule, worldOBB, triMesh, meshTransform, meshScale, results); +} + +bool physx::Gu::intersectSphereVsMesh_RTREE(const Sphere& sphere, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + return intersectAnyVsMesh(&sphere, NULL, NULL, triMesh, meshTransform, meshScale, results); +} + +bool physx::Gu::intersectBoxVsMesh_RTREE(const Box& box, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + return intersectAnyVsMesh(NULL, NULL, &box, triMesh, meshTransform, meshScale, results); +} + +bool physx::Gu::intersectCapsuleVsMesh_RTREE(const Capsule& capsule, const TriangleMesh& triMesh, const PxTransform& meshTransform, const PxMeshScale& meshScale, LimitedResults* results) +{ + return intersectAnyVsMesh(NULL, &capsule, NULL, triMesh, meshTransform, meshScale, results); +} + +void physx::Gu::intersectOBB_RTREE(const TriangleMesh* mesh, const Box& obb, MeshHitCallback& callback, bool bothTriangleSidesCollide, bool checkObbIsAligned) +{ + MeshRayCollider::collideOBB(obb, bothTriangleSidesCollide, static_cast(mesh), callback, checkObbIsAligned); +} + +// PT: TODO: refactor/share bits of this +bool physx::Gu::sweepCapsule_MeshGeom_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Capsule& lss, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + const RTreeTriangleMesh* meshData = static_cast(mesh); + + const Capsule inflatedCapsule(lss.p0, lss.p1, lss.radius + inflation); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + bool isDoubleSided = (triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED); + const PxU32 meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + + // compute sweptAABB + const PxVec3 localP0 = pose.transformInv(inflatedCapsule.p0); + const PxVec3 localP1 = pose.transformInv(inflatedCapsule.p1); + PxVec3 sweepOrigin = (localP0+localP1)*0.5f; + PxVec3 sweepDir = pose.rotateInv(unitDir); + PxVec3 sweepExtents = PxVec3(inflatedCapsule.radius) + (localP0-localP1).abs()*0.5f; + PxReal distance1 = distance; + PxReal distCoeff = 1.0f; + Matrix34 poseWithScale; + if(!isIdentity) + { + poseWithScale = pose * triMeshGeom.scale; + distance1 = computeSweepData(triMeshGeom, sweepOrigin, sweepExtents, sweepDir, distance); + distCoeff = distance1 / distance; + } else + poseWithScale = Matrix34(pose); + + SweepCapsuleMeshHitCallback callback(sweepHit, poseWithScale, distance, isDoubleSided, inflatedCapsule, unitDir, hitFlags, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff); + + MeshRayCollider::collide<1, 1>(sweepOrigin, sweepDir, distance1, true, meshData, callback, &sweepExtents); + + if(meshBothSides) + isDoubleSided = true; + + return callback.finalizeHit(sweepHit, inflatedCapsule, triMeshGeom, pose, isDoubleSided); +} + +#include "GuSweepSharedTests.h" + +// PT: TODO: refactor/share bits of this +bool physx::Gu::sweepBox_MeshGeom_RTREE(const TriangleMesh* mesh, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const Box& box, const PxVec3& unitDir, const PxReal distance, + PxSweepHit& sweepHit, PxHitFlags hitFlags, const PxReal inflation) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + const RTreeTriangleMesh* meshData = static_cast(mesh); + + const bool isIdentity = triMeshGeom.scale.isIdentity(); + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool isDoubleSided = triMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED; + + Matrix34 meshToWorldSkew; + PxVec3 sweptAABBMeshSpaceExtents, meshSpaceOrigin, meshSpaceDir; + + // Input sweep params: geom, pose, box, unitDir, distance + // We convert the origin from world space to mesh local space + // and convert the box+pose to mesh space AABB + if(isIdentity) + { + meshToWorldSkew = Matrix34(pose); + PxMat33 worldToMeshRot(pose.q.getConjugate()); // extract rotation matrix from pose.q + meshSpaceOrigin = worldToMeshRot.transform(box.center - pose.p); + meshSpaceDir = worldToMeshRot.transform(unitDir) * distance; + PxMat33 boxToMeshRot = worldToMeshRot * box.rot; + sweptAABBMeshSpaceExtents = boxToMeshRot.column0.abs() * box.extents.x + + boxToMeshRot.column1.abs() * box.extents.y + + boxToMeshRot.column2.abs() * box.extents.z; + } + else + { + meshToWorldSkew = pose * triMeshGeom.scale; + const PxMat33 meshToWorldSkew_Rot = PxMat33Padded(pose.q) * triMeshGeom.scale.toMat33(); + const PxVec3& meshToWorldSkew_Trans = pose.p; + + PxMat33 worldToVertexSkew_Rot; + PxVec3 worldToVertexSkew_Trans; + getInverse(worldToVertexSkew_Rot, worldToVertexSkew_Trans, meshToWorldSkew_Rot, meshToWorldSkew_Trans); + + //make vertex space OBB + Box vertexSpaceBox1; + const Matrix34 worldToVertexSkew(worldToVertexSkew_Rot, worldToVertexSkew_Trans); + vertexSpaceBox1 = transform(worldToVertexSkew, box); + // compute swept aabb + sweptAABBMeshSpaceExtents = vertexSpaceBox1.computeAABBExtent(); + + meshSpaceOrigin = worldToVertexSkew.transform(box.center); + meshSpaceDir = worldToVertexSkew.rotate(unitDir*distance); // also applies scale to direction/length + } + + sweptAABBMeshSpaceExtents += PxVec3(inflation); // inflate the bounds with additive inflation + sweptAABBMeshSpaceExtents *= 1.01f; // fatten the bounds to account for numerical discrepancies + + PxReal dirLen = PxMax(meshSpaceDir.magnitude(), 1e-5f); + PxReal distCoeff = 1.0f; + if (!isIdentity) + distCoeff = dirLen / distance; + + // Move to AABB space + Matrix34 worldToBox; + computeWorldToBoxMatrix(worldToBox, box); + + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + + const Matrix34Padded meshToBox = worldToBox*meshToWorldSkew; + const PxTransform boxTransform = box.getTransform(); + + const PxVec3 localDir = worldToBox.rotate(unitDir); + const PxVec3 localDirDist = localDir*distance; + SweepBoxMeshHitCallback callback( // using eMULTIPLE with shrinkMaxT + CallbackMode::eMULTIPLE, meshToBox, distance, bothTriangleSidesCollide, box, localDirDist, localDir, unitDir, hitFlags, inflation, triMeshGeom.scale.hasNegativeDeterminant(), distCoeff); + + MeshRayCollider::collide<1, 1>(meshSpaceOrigin, meshSpaceDir/dirLen, dirLen, bothTriangleSidesCollide, meshData, callback, &sweptAABBMeshSpaceExtents); + + return callback.finalizeHit(sweepHit, triMeshGeom, pose, boxTransform, localDir, meshBothSides, isDoubleSided); +} + +#include "GuInternal.h" +void physx::Gu::sweepConvex_MeshGeom_RTREE(const TriangleMesh* mesh, const Box& hullBox, const PxVec3& localDir, const PxReal distance, SweepConvexMeshHitCallback& callback, bool) +{ + PX_ASSERT(mesh->getConcreteType()==PxConcreteType::eTRIANGLE_MESH_BVH33); + const RTreeTriangleMesh* meshData = static_cast(mesh); + + // create temporal bounds + Box querySweptBox; + computeSweptBox(querySweptBox, hullBox.extents, hullBox.center, hullBox.rot, localDir, distance); + + MeshRayCollider::collideOBB(querySweptBox, true, meshData, callback); +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp new file mode 100644 index 000000000..bc5ced894 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp @@ -0,0 +1,241 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuMidphaseInterface.h" +#include "CmScaling.h" +#include "GuSphere.h" +#include "GuInternal.h" +#include "GuConvexUtilsInternal.h" +#include "GuVecTriangle.h" +#include "GuVecConvexHull.h" +#include "GuConvexMesh.h" +#include "GuGJK.h" +#include "GuSweepSharedTests.h" + +using namespace physx; +using namespace Cm; +using namespace Gu; +using namespace physx::shdfnd::aos; + +// PT: TODO: remove this function, replace with Midphase:: call at calling sites (TA34704) +bool Gu::checkOverlapAABB_triangleGeom(const PxGeometry& geom, const PxTransform& pose, const PxBounds3& box) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + + // PT: TODO: pass AABB directly to interface + const Box obb(box.getCenter(), box.getExtents(), PxMat33(PxIdentity)); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + return Midphase::intersectBoxVsMesh(obb, *meshData, pose, meshGeom.scale, NULL); +} + +bool GeomOverlapCallback_SphereMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eSPHERE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxSphereGeometry& sphereGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + const Sphere worldSphere(pose0.p, sphereGeom.radius); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + return Midphase::intersectSphereVsMesh(worldSphere, *meshData, pose1, meshGeom.scale, NULL); +} + +bool GeomOverlapCallback_CapsuleMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCAPSULE); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxCapsuleGeometry& capsuleGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + Capsule capsule; + getCapsule(capsule, capsuleGeom, pose0); + return Midphase::intersectCapsuleVsMesh(capsule, *meshData, pose1, meshGeom.scale, NULL); +} + +bool GeomOverlapCallback_BoxMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eBOX); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxBoxGeometry& boxGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + Box box; + buildFrom(box, pose0.p, boxGeom.halfExtents, pose0.q); + return Midphase::intersectBoxVsMesh(box, *meshData, pose1, meshGeom.scale, NULL); +} + +/////////////////////////////////////////////////////////////////////////////// +struct ConvexVsMeshOverlapCallback : MeshHitCallback +{ + PsMatTransformV MeshToBoxV; + Vec3V boxExtents; + + ConvexVsMeshOverlapCallback( + const ConvexMesh& cm, const PxMeshScale& convexScale, const FastVertex2ShapeScaling& meshScale, + const PxTransform& tr0, const PxTransform& tr1, bool identityScale, const Box& meshSpaceOBB) + : + MeshHitCallback(CallbackMode::eMULTIPLE), + mAnyHit (false), + mIdentityScale (identityScale) + { + if (!mIdentityScale) // not done in initializer list for performance + mMeshScale = Ps::aos::Mat33V( + V3LoadU(meshScale.getVertex2ShapeSkew().column0), + V3LoadU(meshScale.getVertex2ShapeSkew().column1), + V3LoadU(meshScale.getVertex2ShapeSkew().column2) ); + using namespace Ps::aos; + + const ConvexHullData* hullData = &cm.getHull(); + + const Vec3V vScale0 = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat0 = QuatVLoadU(&convexScale.rotation.x); + + mConvex = ConvexHullV(hullData, V3Zero(), vScale0, vQuat0, convexScale.isIdentity()); + aToB = PsMatTransformV(tr0.transformInv(tr1)); + + mIdentityScale = identityScale; + + { + // Move to AABB space + Matrix34 MeshToBox; + computeWorldToBoxMatrix(MeshToBox, meshSpaceOBB); + + const Vec3V base0 = V3LoadU(MeshToBox.m.column0); + const Vec3V base1 = V3LoadU(MeshToBox.m.column1); + const Vec3V base2 = V3LoadU(MeshToBox.m.column2); + const Mat33V matV(base0, base1, base2); + const Vec3V p = V3LoadU(MeshToBox.p); + MeshToBoxV = PsMatTransformV(p, matV); + boxExtents = V3LoadU(meshSpaceOBB.extents+PxVec3(0.001f)); + } + } + virtual ~ConvexVsMeshOverlapCallback() {} + + virtual PxAgain processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit&, const PxVec3& v0a, const PxVec3& v1a, const PxVec3& v2a, PxReal&, const PxU32*) + { + using namespace Ps::aos; + Vec3V v0 = V3LoadU(v0a), v1 = V3LoadU(v1a), v2 = V3LoadU(v2a); + + // test triangle AABB in box space vs box AABB in box local space + const Vec3V triV0 = MeshToBoxV.transform(v0); // AP: MeshToBoxV already includes mesh scale so we have to use unscaled verts here + const Vec3V triV1 = MeshToBoxV.transform(v1); + const Vec3V triV2 = MeshToBoxV.transform(v2); + Vec3V triMn = V3Min(V3Min(triV0, triV1), triV2); + Vec3V triMx = V3Max(V3Max(triV0, triV1), triV2); + Vec3V negExtents = V3Neg(boxExtents); + BoolV minSeparated = V3IsGrtr(triMn, boxExtents), maxSeparated = V3IsGrtr(negExtents, triMx); + BoolV bSeparated = BAnyTrue3(BOr(minSeparated, maxSeparated)); + if (BAllEqTTTT(bSeparated)) + return true; // continue traversal + + if (!mIdentityScale) + { + v0 = M33MulV3(mMeshScale, v0); + v1 = M33MulV3(mMeshScale, v1); + v2 = M33MulV3(mMeshScale, v2); + } + + TriangleV triangle(v0, v1, v2); + Vec3V contactA, contactB, normal; + FloatV dist; + GjkStatus status; + RelativeConvex convexA(triangle, aToB); + LocalConvex convexB(mConvex); + status = gjk(convexA, convexB, aToB.p, FZero(), contactA, contactB, normal, dist); + if (status == GJK_CONTACT)// || FAllGrtrOrEq(mSqTolerance, sqDist)) + { + mAnyHit = true; + return false; // abort traversal + } + return true; // continue traversal + } + + ConvexHullV mConvex; + PsMatTransformV aToB; + Ps::aos::Mat33V mMeshScale; + bool mAnyHit; + bool mIdentityScale; + +private: + ConvexVsMeshOverlapCallback& operator=(const ConvexVsMeshOverlapCallback&); +}; + +// PT: TODO: refactor bits of this with convex-vs-mesh code +bool GeomOverlapCallback_ConvexMesh(GU_OVERLAP_FUNC_PARAMS) +{ + PX_ASSERT(geom0.getType()==PxGeometryType::eCONVEXMESH); + PX_ASSERT(geom1.getType()==PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(cache); + + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom1); + + ConvexMesh* cm = static_cast(convexGeom.convexMesh); + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + const bool idtScaleMesh = meshGeom.scale.isIdentity(); + + FastVertex2ShapeScaling convexScaling; + if (!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + FastVertex2ShapeScaling meshScaling; + if (!idtScaleMesh) + meshScaling.init(meshGeom.scale); + + const Matrix34 world0(pose0); + const Matrix34 world1(pose1); + + PX_ASSERT(!cm->getLocalBoundsFast().isEmpty()); + const PxBounds3 hullAABB = cm->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew()); + + Box hullOBB; + computeHullOBB(hullOBB, hullAABB, 0.0f, world0, world1, meshScaling, idtScaleMesh); + + ConvexVsMeshOverlapCallback cb(*cm, convexGeom.scale, meshScaling, pose0, pose1, idtScaleMesh, hullOBB); + Midphase::intersectOBB(meshData, hullOBB, cb, true, false); + + return cb.mAnyHit; +} + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp new file mode 100644 index 000000000..7034f90c3 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp @@ -0,0 +1,430 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" + +#define RTREE_TEXT_DUMP_ENABLE 0 +#if PX_P64_FAMILY +#define RTREE_PAGES_PER_POOL_SLAB 16384 // preallocate all pages in first batch to make sure we stay within 32 bits for relative pointers.. this is 2 megs +#else +#define RTREE_PAGES_PER_POOL_SLAB 128 +#endif + +#define INSERT_SCAN_LOOKAHEAD 1 // enable one level lookahead scan for determining which child page is best to insert a node into + +#define RTREE_INFLATION_EPSILON 5e-4f + +#include "GuRTree.h" +#include "PsSort.h" +#include "GuSerialize.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +using namespace physx; +#if PX_ENABLE_DYNAMIC_MESH_RTREE +using namespace shdfnd::aos; +#endif +using Ps::Array; +using Ps::sort; +using namespace Gu; + +namespace physx +{ +namespace Gu { + +bool RTree::load(PxInputStream& stream, PxU32 meshVersion, bool mismatch_) // PT: 'meshVersion' is the PX_MESH_VERSION from cooked file +{ + PX_UNUSED(meshVersion); + + release(); + + PxI8 a, b, c, d; + readChunk(a, b, c, d, stream); + if(a!='R' || b!='T' || c!='R' || d!='E') + return false; + + bool mismatch; + PxU32 fileVersion; + if(!readBigEndianVersionNumber(stream, mismatch_, fileVersion, mismatch)) + return false; + + readFloatBuffer(&mBoundsMin.x, 4, mismatch, stream); + readFloatBuffer(&mBoundsMax.x, 4, mismatch, stream); + readFloatBuffer(&mInvDiagonal.x, 4, mismatch, stream); + readFloatBuffer(&mDiagonalScaler.x, 4, mismatch, stream); + mPageSize = readDword(mismatch, stream); + mNumRootPages = readDword(mismatch, stream); + mNumLevels = readDword(mismatch, stream); + mTotalNodes = readDword(mismatch, stream); + mTotalPages = readDword(mismatch, stream); + PxU32 unused = readDword(mismatch, stream); PX_UNUSED(unused); // backwards compatibility + mPages = static_cast(Ps::AlignedAllocator<128>().allocate(sizeof(RTreePage)*mTotalPages, __FILE__, __LINE__)); + Cm::markSerializedMem(mPages, sizeof(RTreePage)*mTotalPages); + for(PxU32 j=0; jnodeCount(); + PX_ASSERT(nc > 0 && nc <= RTREE_N); + // old version pointer, up to PX_MESH_VERSION 8 + PxU32 ptr = (rightMostPage->ptrs[nc-1]) * multiplier; + PX_ASSERT(ptr % sizeof(RTreePage) == 0); + const RTreePage* rightMostPageNext = mPages + (ptr / sizeof(RTreePage)); + curCount = PxU32(rightMostPageNext - rightMostPage); + rightMostPage = rightMostPageNext; + } + + return mTotalPages - topCount; +} + +///////////////////////////////////////////////////////////////////////// +RTree::RTree(const PxEMPTY) +{ + mFlags |= USER_ALLOCATED; +} + + +// PX_SERIALIZATION +///////////////////////////////////////////////////////////////////////// +void RTree::exportExtraData(PxSerializationContext& stream) +{ + stream.alignData(128); + stream.writeData(mPages, mTotalPages*sizeof(RTreePage)); +} + +///////////////////////////////////////////////////////////////////////// +void RTree::importExtraData(PxDeserializationContext& context) +{ + context.alignExtraData(128); + mPages = context.readExtraData(mTotalPages); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE PxU32 RTreePage::nodeCount() const +{ + for (int j = 0; j < RTREE_N; j ++) + if (minx[j] == MX) + return PxU32(j); + + return RTREE_N; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::clearNode(PxU32 nodeIndex) +{ + PX_ASSERT(nodeIndex < RTREE_N); + minx[nodeIndex] = miny[nodeIndex] = minz[nodeIndex] = MX; // initialize empty node with sentinels + maxx[nodeIndex] = maxy[nodeIndex] = maxz[nodeIndex] = MN; + ptrs[nodeIndex] = 0; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::getNode(const PxU32 nodeIndex, RTreeNodeQ& r) const +{ + PX_ASSERT(nodeIndex < RTREE_N); + r.minx = minx[nodeIndex]; + r.miny = miny[nodeIndex]; + r.minz = minz[nodeIndex]; + r.maxx = maxx[nodeIndex]; + r.maxy = maxy[nodeIndex]; + r.maxz = maxz[nodeIndex]; + r.ptr = ptrs[nodeIndex]; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::setEmpty(PxU32 startIndex) +{ + PX_ASSERT(startIndex < RTREE_N); + for (PxU32 j = startIndex; j < RTREE_N; j ++) + clearNode(j); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::computeBounds(RTreeNodeQ& newBounds) +{ + RTreeValue _minx = MX, _miny = MX, _minz = MX, _maxx = MN, _maxy = MN, _maxz = MN; + for (PxU32 j = 0; j < RTREE_N; j++) + { + if (isEmpty(j)) + continue; + _minx = PxMin(_minx, minx[j]); + _miny = PxMin(_miny, miny[j]); + _minz = PxMin(_minz, minz[j]); + _maxx = PxMax(_maxx, maxx[j]); + _maxy = PxMax(_maxy, maxy[j]); + _maxz = PxMax(_maxz, maxz[j]); + } + newBounds.minx = _minx; + newBounds.miny = _miny; + newBounds.minz = _minz; + newBounds.maxx = _maxx; + newBounds.maxy = _maxy; + newBounds.maxz = _maxz; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::adjustChildBounds(PxU32 index, const RTreeNodeQ& adjChild) +{ + PX_ASSERT(index < RTREE_N); + minx[index] = adjChild.minx; + miny[index] = adjChild.miny; + minz[index] = adjChild.minz; + maxx[index] = adjChild.maxx; + maxy[index] = adjChild.maxy; + maxz[index] = adjChild.maxz; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::growChildBounds(PxU32 index, const RTreeNodeQ& child) +{ + PX_ASSERT(index < RTREE_N); + minx[index] = PxMin(minx[index], child.minx); + miny[index] = PxMin(miny[index], child.miny); + minz[index] = PxMin(minz[index], child.minz); + maxx[index] = PxMax(maxx[index], child.maxx); + maxy[index] = PxMax(maxy[index], child.maxy); + maxz[index] = PxMax(maxz[index], child.maxz); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::copyNode(PxU32 targetIndex, const RTreePage& sourcePage, PxU32 sourceIndex) +{ + PX_ASSERT(targetIndex < RTREE_N); + PX_ASSERT(sourceIndex < RTREE_N); + minx[targetIndex] = sourcePage.minx[sourceIndex]; + miny[targetIndex] = sourcePage.miny[sourceIndex]; + minz[targetIndex] = sourcePage.minz[sourceIndex]; + maxx[targetIndex] = sourcePage.maxx[sourceIndex]; + maxy[targetIndex] = sourcePage.maxy[sourceIndex]; + maxz[targetIndex] = sourcePage.maxz[sourceIndex]; + ptrs[targetIndex] = sourcePage.ptrs[sourceIndex]; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreePage::setNode(PxU32 targetIndex, const RTreeNodeQ& sourceNode) +{ + PX_ASSERT(targetIndex < RTREE_N); + minx[targetIndex] = sourceNode.minx; + miny[targetIndex] = sourceNode.miny; + minz[targetIndex] = sourceNode.minz; + maxx[targetIndex] = sourceNode.maxx; + maxy[targetIndex] = sourceNode.maxy; + maxz[targetIndex] = sourceNode.maxz; + ptrs[targetIndex] = sourceNode.ptr; +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreeNodeQ::grow(const RTreePage& page, int nodeIndex) +{ + PX_ASSERT(nodeIndex < RTREE_N); + minx = PxMin(minx, page.minx[nodeIndex]); + miny = PxMin(miny, page.miny[nodeIndex]); + minz = PxMin(minz, page.minz[nodeIndex]); + maxx = PxMax(maxx, page.maxx[nodeIndex]); + maxy = PxMax(maxy, page.maxy[nodeIndex]); + maxz = PxMax(maxz, page.maxz[nodeIndex]); +} + +///////////////////////////////////////////////////////////////////////// +PX_FORCE_INLINE void RTreeNodeQ::grow(const RTreeNodeQ& node) +{ + minx = PxMin(minx, node.minx); miny = PxMin(miny, node.miny); minz = PxMin(minz, node.minz); + maxx = PxMax(maxx, node.maxx); maxy = PxMax(maxy, node.maxy); maxz = PxMax(maxz, node.maxz); +} + +///////////////////////////////////////////////////////////////////////// +#if PX_ENABLE_DYNAMIC_MESH_RTREE +void RTree::validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page, CallbackRefit* cbLeaf) +#else +void RTree::validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page) +#endif +{ + PX_UNUSED(parentBounds); + + static PxU32 validateCounter = 0; // this is to suppress a warning that recursive call has no side effects + validateCounter++; + + RTreeNodeQ n; + PxU32 pageNodeCount = page->nodeCount(); + for (PxU32 j = 0; j < pageNodeCount; j++) + { + page->getNode(j, n); + if (page->isEmpty(j)) + continue; + PX_ASSERT(n.minx >= parentBounds.minx); PX_ASSERT(n.miny >= parentBounds.miny); PX_ASSERT(n.minz >= parentBounds.minz); + PX_ASSERT(n.maxx <= parentBounds.maxx); PX_ASSERT(n.maxy <= parentBounds.maxy); PX_ASSERT(n.maxz <= parentBounds.maxz); + if (!n.isLeaf()) + { + PX_ASSERT((n.ptr&1) == 0); + RTreePage* childPage = reinterpret_cast(size_t(mPages) + n.ptr); +#if PX_ENABLE_DYNAMIC_MESH_RTREE + validateRecursive(level+1, n, childPage, cbLeaf); + } else if (cbLeaf) + { + Vec3V mnv, mxv; + cbLeaf->recomputeBounds(page->ptrs[j] & ~1, mnv, mxv); + PxVec3 mn3, mx3; V3StoreU(mnv, mn3); V3StoreU(mxv, mx3); + const PxBounds3 lb(mn3, mx3); + const PxVec3& mn = lb.minimum; const PxVec3& mx = lb.maximum; PX_UNUSED(mn); PX_UNUSED(mx); + PX_ASSERT(mn.x >= n.minx); PX_ASSERT(mn.y >= n.miny); PX_ASSERT(mn.z >= n.minz); + PX_ASSERT(mx.x <= n.maxx); PX_ASSERT(mx.y <= n.maxy); PX_ASSERT(mx.z <= n.maxz); + } +#else + validateRecursive(level+1, n, childPage); + } +#endif + } + RTreeNodeQ recomputedBounds; + page->computeBounds(recomputedBounds); + PX_ASSERT((recomputedBounds.minx - parentBounds.minx)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.miny - parentBounds.miny)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.minz - parentBounds.minz)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.maxx - parentBounds.maxx)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.maxy - parentBounds.maxy)<=RTREE_INFLATION_EPSILON); + PX_ASSERT((recomputedBounds.maxz - parentBounds.maxz)<=RTREE_INFLATION_EPSILON); +} + +///////////////////////////////////////////////////////////////////////// +#if PX_ENABLE_DYNAMIC_MESH_RTREE +void RTree::validate(CallbackRefit* cbLeaf) +#else +void RTree::validate() +#endif +{ + for (PxU32 j = 0; j < mNumRootPages; j++) + { + RTreeNodeQ rootBounds; + mPages[j].computeBounds(rootBounds); +#if PX_ENABLE_DYNAMIC_MESH_RTREE + validateRecursive(0, rootBounds, mPages+j, cbLeaf); +#else + validateRecursive(0, rootBounds, mPages+j); +#endif + } +} + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +void RTree::refitAllStaticTree(CallbackRefit& cb, PxBounds3* retBounds) +{ + PxU8* treeNodes8 = reinterpret_cast(mPages); + + // since pages are ordered we can scan back to front and the hierarchy will be updated + for (PxI32 iPage = PxI32(mTotalPages)-1; iPage>=0; iPage--) + { + RTreePage& page = mPages[iPage]; + for (PxU32 j = 0; j < RTREE_N; j++) + { + if (page.isEmpty(j)) + continue; + if (page.isLeaf(j)) + { + Vec3V childMn, childMx; + cb.recomputeBounds(page.ptrs[j]-1, childMn, childMx); // compute the bound around triangles + PxVec3 mn3, mx3; + V3StoreU(childMn, mn3); + V3StoreU(childMx, mx3); + page.minx[j] = mn3.x; page.miny[j] = mn3.y; page.minz[j] = mn3.z; + page.maxx[j] = mx3.x; page.maxy[j] = mx3.y; page.maxz[j] = mx3.z; + } else + { + const RTreePage* child = reinterpret_cast(treeNodes8 + page.ptrs[j]); + PX_COMPILE_TIME_ASSERT(RTREE_N == 4); + bool first = true; + for (PxU32 k = 0; k < RTREE_N; k++) + { + if (child->isEmpty(k)) + continue; + if (first) + { + page.minx[j] = child->minx[k]; page.miny[j] = child->miny[k]; page.minz[j] = child->minz[k]; + page.maxx[j] = child->maxx[k]; page.maxy[j] = child->maxy[k]; page.maxz[j] = child->maxz[k]; + first = false; + } else + { + page.minx[j] = PxMin(page.minx[j], child->minx[k]); + page.miny[j] = PxMin(page.miny[j], child->miny[k]); + page.minz[j] = PxMin(page.minz[j], child->minz[k]); + page.maxx[j] = PxMax(page.maxx[j], child->maxx[k]); + page.maxy[j] = PxMax(page.maxy[j], child->maxy[k]); + page.maxz[j] = PxMax(page.maxz[j], child->maxz[k]); + } + } + } + } + } + + if (retBounds) + { + RTreeNodeQ bound1; + for (PxU32 ii = 0; iiminimum = PxVec3(bound1.minx, bound1.miny, bound1.minz); + retBounds->maximum = PxVec3(bound1.maxx, bound1.maxy, bound1.maxz); + } else + { + retBounds->minimum = retBounds->minimum.minimum(PxVec3(bound1.minx, bound1.miny, bound1.minz)); + retBounds->maximum = retBounds->maximum.maximum(PxVec3(bound1.maxx, bound1.maxy, bound1.maxz)); + } + } + } + +#if PX_CHECKED + validate(&cb); +#endif +} +#endif // PX_ENABLE_DYNAMIC_MESH_RTREE + +//~PX_SERIALIZATION +const RTreeValue RTreePage::MN = -PX_MAX_F32; +const RTreeValue RTreePage::MX = PX_MAX_F32; + +} // namespace Gu + +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h new file mode 100644 index 000000000..4cd3ad9c5 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h @@ -0,0 +1,297 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_RTREE_H +#define GU_RTREE_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec4.h" +#include "foundation/PxBounds3.h" +#include "foundation/PxAssert.h" +#include "PsUserAllocated.h" // for PxSerializationContext +#include "PxSerialFramework.h" +#include "PxTriangleMesh.h" +#include "PsAlignedMalloc.h" + + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +#include "PsVecMath.h" +#endif + +#define RTREE_N 4 // changing this number will affect the mesh format +PX_COMPILE_TIME_ASSERT(RTREE_N == 4 || RTREE_N == 8); // using the low 5 bits for storage of index(childPtr) for dynamic rtree + +namespace physx +{ + + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +namespace Gu { + + class Box; + struct RTreePage; + + typedef PxF32 RTreeValue; + + ///////////////////////////////////////////////////////////////////////// + // quantized untransposed RTree node - used for offline build and dynamic insertion + struct RTreeNodeQ + { + RTreeValue minx, miny, minz, maxx, maxy, maxz; + PxU32 ptr; // lowest bit is leaf flag + + PX_FORCE_INLINE void setLeaf(bool set) { if (set) ptr |= 1; else ptr &= ~1; } + PX_FORCE_INLINE PxU32 isLeaf() const { return ptr & 1; } + PX_FORCE_INLINE void setEmpty(); + PX_FORCE_INLINE void grow(const RTreePage& page, int nodeIndex); + PX_FORCE_INLINE void grow(const RTreeNodeQ& node); + }; + + ///////////////////////////////////////////////////////////////////////// + // RTreePage data structure, holds RTREE_N transposed nodes + + // RTreePage data structure, holds 8 transposed nodes + PX_ALIGN_PREFIX(16) + struct RTreePage + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + static const RTreeValue MN, MX; + + RTreeValue minx[RTREE_N]; // [min=MX, max=MN] is used as a sentinel range for empty bounds + RTreeValue miny[RTREE_N]; + RTreeValue minz[RTREE_N]; + RTreeValue maxx[RTREE_N]; + RTreeValue maxy[RTREE_N]; + RTreeValue maxz[RTREE_N]; + PxU32 ptrs[RTREE_N]; // for static rtree this is an offset relative to the first page divided by 16, for dynamics it's an absolute pointer divided by 16 + + PX_FORCE_INLINE PxU32 nodeCount() const; // returns the number of occupied nodes in this page + PX_FORCE_INLINE void setEmpty(PxU32 startIndex = 0); + PX_FORCE_INLINE bool isEmpty(PxU32 index) const { return minx[index] > maxx[index]; } + PX_FORCE_INLINE void copyNode(PxU32 targetIndex, const RTreePage& sourcePage, PxU32 sourceIndex); + PX_FORCE_INLINE void setNode(PxU32 targetIndex, const RTreeNodeQ& node); + PX_FORCE_INLINE void clearNode(PxU32 nodeIndex); + PX_FORCE_INLINE void getNode(PxU32 nodeIndex, RTreeNodeQ& result) const; + PX_FORCE_INLINE void computeBounds(RTreeNodeQ& bounds); + PX_FORCE_INLINE void adjustChildBounds(PxU32 index, const RTreeNodeQ& adjustedChildBounds); + PX_FORCE_INLINE void growChildBounds(PxU32 index, const RTreeNodeQ& adjustedChildBounds); + PX_FORCE_INLINE PxU32 getNodeHandle(PxU32 index) const; + PX_FORCE_INLINE PxU32 isLeaf(PxU32 index) const { return ptrs[index] & 1; } + } PX_ALIGN_SUFFIX(16); + + ///////////////////////////////////////////////////////////////////////// + // RTree root data structure + PX_ALIGN_PREFIX(16) + struct RTree + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + // PX_SERIALIZATION + RTree(const PxEMPTY); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + PX_INLINE RTree(); // offline static rtree constructor used with cooking + + ~RTree() { release(); } + + PX_INLINE void release(); + bool load(PxInputStream& stream, PxU32 meshVersion, bool mismatch); + + //////////////////////////////////////////////////////////////////////////// + // QUERIES + struct Callback + { + // result buffer should have room for at least RTREE_N items + // should return true to continue traversal. If false is returned, traversal is aborted + virtual bool processResults(PxU32 count, PxU32* buf) = 0; + virtual void profile() {} + virtual ~Callback() {} + }; + + struct CallbackRaycast + { + // result buffer should have room for at least RTREE_N items + // should return true to continue traversal. If false is returned, traversal is aborted + // newMaxT serves as both input and output, as input it's the maxT so far + // set it to a new value (which should be smaller) and it will become the new far clip t + virtual bool processResults(PxU32 count, PxU32* buf, PxF32& newMaxT) = 0; + virtual ~CallbackRaycast() {} + }; + + // callback will be issued as soon as the buffer overflows maxResultsPerBlock-RTreePage:SIZE entries + // use maxResults = RTreePage:SIZE and return false from callback for "first hit" early out + void traverseAABB( + const PxVec3& boxMin, const PxVec3& boxMax, + const PxU32 maxResultsPerBlock, PxU32* resultsBlockBuf, Callback* processResultsBlockCallback) const; + void traverseOBB( + const Gu::Box& obb, + const PxU32 maxResultsPerBlock, PxU32* resultsBlockBuf, Callback* processResultsBlockCallback) const; + + template + void traverseRay( + const PxVec3& rayOrigin, const PxVec3& rayDir, // dir doesn't have to be normalized and is B-A for raySegment + const PxU32 maxResults, PxU32* resultsPtr, + Gu::RTree::CallbackRaycast* callback, + const PxVec3* inflateAABBs, // inflate tree's AABBs by this amount. This function turns into AABB sweep. + PxF32 maxT = PX_MAX_F32 // maximum ray t parameter, p(t)=origin+t*dir; use 1.0f for ray segment + ) const; + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + struct CallbackRefit + { + // In this callback index is the number stored in the RTree, which is a LeafTriangles object for current PhysX mesh + virtual void recomputeBounds(PxU32 index, shdfnd::aos::Vec3V& mn, shdfnd::aos::Vec3V& mx) = 0; + virtual ~CallbackRefit() {} + }; + void refitAllStaticTree(CallbackRefit& cb, PxBounds3* resultMeshBounds); // faster version of refit for static RTree only +#endif + + + //////////////////////////////////////////////////////////////////////////// + // DEBUG HELPER FUNCTIONS +#if PX_ENABLE_DYNAMIC_MESH_RTREE + PX_PHYSX_COMMON_API void validate(CallbackRefit* cb = NULL); // verify that all children are indeed included in parent bounds +#else + PX_PHYSX_COMMON_API void validate(); // verify that all children are indeed included in parent bounds +#endif + void openTextDump(); + void closeTextDump(); + void textDump(const char* prefix); + void maxscriptExport(); + PxU32 computeBottomLevelCount(PxU32 storedToMemMultiplier) const; + + //////////////////////////////////////////////////////////////////////////// + // DATA + // remember to update save() and load() when adding or removing data + PxVec4 mBoundsMin, mBoundsMax, mInvDiagonal, mDiagonalScaler; // 16 + PxU32 mPageSize; + PxU32 mNumRootPages; + PxU32 mNumLevels; + PxU32 mTotalNodes; // 16 + PxU32 mTotalPages; + PxU32 mFlags; enum { USER_ALLOCATED = 0x1, IS_EDGE_SET = 0x2 }; + RTreePage* mPages; + + protected: + typedef PxU32 NodeHandle; +#if PX_ENABLE_DYNAMIC_MESH_RTREE + void validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page, CallbackRefit* cb = NULL); +#else + void validateRecursive(PxU32 level, RTreeNodeQ parentBounds, RTreePage* page); +#endif + + friend struct RTreePage; + } PX_ALIGN_SUFFIX(16); + +#if PX_SUPPORT_EXTERN_TEMPLATE + //explicit template instantiation declaration + extern template + void RTree::traverseRay<0>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const; + + extern template + void RTree::traverseRay<1>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const; +#endif + +#if PX_VC +#pragma warning(pop) +#endif + + ///////////////////////////////////////////////////////////////////////// + PX_INLINE RTree::RTree() + { + mFlags = 0; + mPages = NULL; + mTotalNodes = 0; + mNumLevels = 0; + mPageSize = RTREE_N; + } + + ///////////////////////////////////////////////////////////////////////// + PX_INLINE void RTree::release() + { + if ((mFlags & USER_ALLOCATED) == 0 && mPages) + { + physx::shdfnd::AlignedAllocator<128>().deallocate(mPages); + mPages = NULL; + } + } + + ///////////////////////////////////////////////////////////////////////// + PX_FORCE_INLINE void RTreeNodeQ::setEmpty() + { + minx = miny = minz = RTreePage::MX; + maxx = maxy = maxz = RTreePage::MN; + } + + + // bit 1 is always expected to be set to differentiate between leaf and non-leaf node + PX_FORCE_INLINE PxU32 LeafGetNbTriangles(PxU32 Data) { return ((Data>>1) & 15)+1; } + PX_FORCE_INLINE PxU32 LeafGetTriangleIndex(PxU32 Data) { return Data>>5; } + PX_FORCE_INLINE PxU32 LeafSetData(PxU32 nb, PxU32 index) + { + PX_ASSERT(nb>0 && nb<=16); PX_ASSERT(index < (1<<27)); + return (index<<5)|(((nb-1)&15)<<1) | 1; + } + + struct LeafTriangles + { + PxU32 Data; + + // Gets number of triangles in the leaf, returns the number of triangles N, with 0 < N <= 16 + PX_FORCE_INLINE PxU32 GetNbTriangles() const { return LeafGetNbTriangles(Data); } + + // Gets triangle index for this leaf. Indexed model's array of indices retrieved with RTreeMidphase::GetIndices() + PX_FORCE_INLINE PxU32 GetTriangleIndex() const { return LeafGetTriangleIndex(Data); } + PX_FORCE_INLINE void SetData(PxU32 nb, PxU32 index) { Data = LeafSetData(nb, index); } + }; + + PX_COMPILE_TIME_ASSERT(sizeof(LeafTriangles)==4); // RTree has space for 4 bytes + +} // namespace Gu + +} + +#endif // #ifdef PX_COLLISION_RTREE diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp new file mode 100644 index 000000000..4030ba0d7 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp @@ -0,0 +1,573 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/* +General notes: + + rtree depth-first traversal looks like this: + push top level page onto stack + + pop page from stack + for each node in page + if node overlaps with testrect + push node's subpage + + we want to efficiently keep track of current stack level to know if the current page is a leaf or not + (since we don't store a flag with the page due to no space, we can't determine it just by looking at current page) + since we traverse depth first, the levels for nodes on the stack look like this: + l0 l0 l1 l2 l2 l3 l3 l3 l4 + + we can encode this as an array of 4 bits per level count into a 32-bit integer + to simplify the code->level computation we also keep track of current level by incrementing the level whenever any subpages + from current test page are pushed onto the stack + when we pop a page off the stack we use this encoding to determine if we should decrement the stack level +*/ + +#include "foundation/PxBounds3.h" +#include "GuRTree.h" +#include "PsIntrinsics.h" +#include "GuBox.h" +#include "PsVecMath.h" +#include "PxQueryReport.h" // for PxAgain +#include "PsBitUtils.h" +#include "GuBVConstants.h" + +//#define VERIFY_RTREE +#ifdef VERIFY_RTREE +#include "GuIntersectionRayBox.h" +#include "GuIntersectionBoxBox.h" +#include "stdio.h" +#endif + +using namespace physx; +using namespace physx::shdfnd; +using namespace Ps::aos; + +namespace physx +{ +namespace Gu { + +using namespace Ps::aos; + +#define v_absm(a) V4Andc(a, signMask) +#define V4FromF32A(x) V4LoadA(x) +#define PxF32FV(x) FStore(x) +#define CAST_U8(a) reinterpret_cast(a) + +///////////////////////////////////////////////////////////////////////// +void RTree::traverseAABB(const PxVec3& boxMin, const PxVec3& boxMax, const PxU32 maxResults, PxU32* resultsPtr, Callback* callback) const +{ + PX_UNUSED(resultsPtr); + + PX_ASSERT(callback); + PX_ASSERT(maxResults >= mPageSize); + PX_UNUSED(maxResults); + + const PxU32 maxStack = 128; + PxU32 stack1[maxStack]; + PxU32* stack = stack1+1; + + PX_ASSERT(mPages); + PX_ASSERT((uintptr_t(mPages) & 127) == 0); + PX_ASSERT((uintptr_t(this) & 15) == 0); + + // conservatively quantize the input box + Vec4V nqMin = Vec4V_From_PxVec3_WUndefined(boxMin); + Vec4V nqMax = Vec4V_From_PxVec3_WUndefined(boxMax); + + Vec4V nqMinx4 = V4SplatElement<0>(nqMin); + Vec4V nqMiny4 = V4SplatElement<1>(nqMin); + Vec4V nqMinz4 = V4SplatElement<2>(nqMin); + Vec4V nqMaxx4 = V4SplatElement<0>(nqMax); + Vec4V nqMaxy4 = V4SplatElement<1>(nqMax); + Vec4V nqMaxz4 = V4SplatElement<2>(nqMax); + + // on 64-bit platforms the dynamic rtree pointer is also relative to mPages + PxU8* treeNodes8 = CAST_U8(mPages); + PxU32* stackPtr = stack; + + // AP potential perf optimization - fetch the top level right away + PX_ASSERT(RTREE_N == 4 || RTREE_N == 8); + PX_ASSERT(Ps::isPowerOfTwo(mPageSize)); + + for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --) + *stackPtr++ = j*sizeof(RTreePage); + + PxU32 cacheTopValid = true; + PxU32 cacheTop = 0; + + do { + stackPtr--; + PxU32 top; + if (cacheTopValid) // branch is faster than lhs + top = cacheTop; + else + top = stackPtr[0]; + PX_ASSERT(!cacheTopValid || stackPtr[0] == cacheTop); + RTreePage* PX_RESTRICT tn = reinterpret_cast(treeNodes8 + top); + const PxU32* ptrs = (reinterpret_cast(tn))->ptrs; + + Vec4V minx4 = V4LoadA(tn->minx); + Vec4V miny4 = V4LoadA(tn->miny); + Vec4V minz4 = V4LoadA(tn->minz); + Vec4V maxx4 = V4LoadA(tn->maxx); + Vec4V maxy4 = V4LoadA(tn->maxy); + Vec4V maxz4 = V4LoadA(tn->maxz); + + // AABB/AABB overlap test + BoolV res0 = V4IsGrtr(nqMinx4, maxx4); BoolV res1 = V4IsGrtr(nqMiny4, maxy4); BoolV res2 = V4IsGrtr(nqMinz4, maxz4); + BoolV res3 = V4IsGrtr(minx4, nqMaxx4); BoolV res4 = V4IsGrtr(miny4, nqMaxy4); BoolV res5 = V4IsGrtr(minz4, nqMaxz4); + BoolV resx = BOr(BOr(BOr(res0, res1), BOr(res2, res3)), BOr(res4, res5)); + PX_ALIGN_PREFIX(16) PxU32 resa[RTREE_N] PX_ALIGN_SUFFIX(16); + + VecU32V res4x = VecU32V_From_BoolV(resx); + U4StoreA(res4x, resa); + + cacheTopValid = false; + for (PxU32 i = 0; i < RTREE_N; i++) + { + PxU32 ptr = ptrs[i] & ~1; // clear the isLeaf bit + if (resa[i]) + continue; + if (tn->isLeaf(i)) + { + if (!callback->processResults(1, &ptr)) + return; + } + else + { + *(stackPtr++) = ptr; + cacheTop = ptr; + cacheTopValid = true; + } + } + } while (stackPtr > stack); +} + +///////////////////////////////////////////////////////////////////////// +template +void RTree::traverseRay( + const PxVec3& rayOrigin, const PxVec3& rayDir, + const PxU32 maxResults, PxU32* resultsPtr, Gu::RTree::CallbackRaycast* callback, + const PxVec3* fattenAABBs, PxF32 maxT) const +{ + // implements Kay-Kajiya 4-way SIMD test + PX_UNUSED(resultsPtr); + PX_UNUSED(maxResults); + + const PxU32 maxStack = 128; + PxU32 stack1[maxStack]; + PxU32* stack = stack1+1; + + PX_ASSERT(mPages); + PX_ASSERT((uintptr_t(mPages) & 127) == 0); + PX_ASSERT((uintptr_t(this) & 15) == 0); + + PxU8* treeNodes8 = CAST_U8(mPages); + + Vec4V fattenAABBsX, fattenAABBsY, fattenAABBsZ; + PX_UNUSED(fattenAABBsX); PX_UNUSED(fattenAABBsY); PX_UNUSED(fattenAABBsZ); + if (inflate) + { + Vec4V fattenAABBs4 = Vec4V_From_PxVec3_WUndefined(*fattenAABBs); + fattenAABBs4 = V4Add(fattenAABBs4, epsInflateFloat4); // US2385 - shapes are "closed" meaning exactly touching shapes should report overlap + fattenAABBsX = V4SplatElement<0>(fattenAABBs4); + fattenAABBsY = V4SplatElement<1>(fattenAABBs4); + fattenAABBsZ = V4SplatElement<2>(fattenAABBs4); + } + + Vec4V maxT4; + maxT4 = V4Load(maxT); + Vec4V rayP = Vec4V_From_PxVec3_WUndefined(rayOrigin); + Vec4V rayD = Vec4V_From_PxVec3_WUndefined(rayDir); + VecU32V raySign = V4U32and(VecU32V_ReinterpretFrom_Vec4V(rayD), signMask); + Vec4V rayDAbs = V4Abs(rayD); // abs value of rayD + Vec4V rayInvD = Vec4V_ReinterpretFrom_VecU32V(V4U32or(raySign, VecU32V_ReinterpretFrom_Vec4V(V4Max(rayDAbs, epsFloat4)))); // clamp near-zero components up to epsilon + rayD = rayInvD; + + //rayInvD = V4Recip(rayInvD); + // Newton-Raphson iteration for reciprocal (see wikipedia): + // X[n+1] = X[n]*(2-original*X[n]), X[0] = V4RecipFast estimate + //rayInvD = rayInvD*(twos-rayD*rayInvD); + rayInvD = V4RecipFast(rayInvD); // initial estimate, not accurate enough + rayInvD = V4Mul(rayInvD, V4NegMulSub(rayD, rayInvD, twos)); + + // P+tD=a; t=(a-P)/D + // t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x) + Vec4V rayPinvD = V4NegMulSub(rayInvD, rayP, zeroes); + Vec4V rayInvDsplatX = V4SplatElement<0>(rayInvD); + Vec4V rayInvDsplatY = V4SplatElement<1>(rayInvD); + Vec4V rayInvDsplatZ = V4SplatElement<2>(rayInvD); + Vec4V rayPinvDsplatX = V4SplatElement<0>(rayPinvD); + Vec4V rayPinvDsplatY = V4SplatElement<1>(rayPinvD); + Vec4V rayPinvDsplatZ = V4SplatElement<2>(rayPinvD); + + PX_ASSERT(RTREE_N == 4 || RTREE_N == 8); + PX_ASSERT(mNumRootPages > 0); + + PxU32 stackPtr = 0; + for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --) + stack[stackPtr++] = j*sizeof(RTreePage); + + PX_ALIGN_PREFIX(16) PxU32 resa[4] PX_ALIGN_SUFFIX(16); + + while (stackPtr) + { + PxU32 top = stack[--stackPtr]; + if (top&1) // isLeaf test + { + top--; + PxF32 newMaxT = maxT; + if (!callback->processResults(1, &top, newMaxT)) + return; + /* shrink the ray if newMaxT is reduced compared to the original maxT */ + if (maxT != newMaxT) + { + PX_ASSERT(newMaxT < maxT); + maxT = newMaxT; + maxT4 = V4Load(newMaxT); + } + continue; + } + + RTreePage* PX_RESTRICT tn = reinterpret_cast(treeNodes8 + top); + + // 6i load + Vec4V minx4a = V4LoadA(tn->minx), miny4a = V4LoadA(tn->miny), minz4a = V4LoadA(tn->minz); + Vec4V maxx4a = V4LoadA(tn->maxx), maxy4a = V4LoadA(tn->maxy), maxz4a = V4LoadA(tn->maxz); + + // 1i disabled test + // AP scaffold - optimization opportunity - can save 2 instructions here + VecU32V ignore4a = V4IsGrtrV32u(minx4a, maxx4a); // 1 if degenerate box (empty slot in the page) + + if (inflate) + { + // 6i + maxx4a = V4Add(maxx4a, fattenAABBsX); maxy4a = V4Add(maxy4a, fattenAABBsY); maxz4a = V4Add(maxz4a, fattenAABBsZ); + minx4a = V4Sub(minx4a, fattenAABBsX); miny4a = V4Sub(miny4a, fattenAABBsY); minz4a = V4Sub(minz4a, fattenAABBsZ); + } + + // P+tD=a; t=(a-P)/D + // t=(a - p.x)*1/d.x = a/d.x +(- p.x/d.x) + // 6i + Vec4V tminxa0 = V4MulAdd(minx4a, rayInvDsplatX, rayPinvDsplatX); + Vec4V tminya0 = V4MulAdd(miny4a, rayInvDsplatY, rayPinvDsplatY); + Vec4V tminza0 = V4MulAdd(minz4a, rayInvDsplatZ, rayPinvDsplatZ); + Vec4V tmaxxa0 = V4MulAdd(maxx4a, rayInvDsplatX, rayPinvDsplatX); + Vec4V tmaxya0 = V4MulAdd(maxy4a, rayInvDsplatY, rayPinvDsplatY); + Vec4V tmaxza0 = V4MulAdd(maxz4a, rayInvDsplatZ, rayPinvDsplatZ); + + // test half-spaces + // P+tD=dN + // t = (d(N,D)-(P,D))/(D,D) , (D,D)=1 + + // compute 4x dot products (N,D) and (P,N) for each AABB in the page + + // 6i + // now compute tnear and tfar for each pair of planes for each box + Vec4V tminxa = V4Min(tminxa0, tmaxxa0); Vec4V tmaxxa = V4Max(tminxa0, tmaxxa0); + Vec4V tminya = V4Min(tminya0, tmaxya0); Vec4V tmaxya = V4Max(tminya0, tmaxya0); + Vec4V tminza = V4Min(tminza0, tmaxza0); Vec4V tmaxza = V4Max(tminza0, tmaxza0); + + // 8i + Vec4V maxOfNeasa = V4Max(V4Max(tminxa, tminya), tminza); + Vec4V minOfFarsa = V4Min(V4Min(tmaxxa, tmaxya), tmaxza); + ignore4a = V4U32or(ignore4a, V4IsGrtrV32u(epsFloat4, minOfFarsa)); // if tfar is negative, ignore since its a ray, not a line + // AP scaffold: update the build to eliminate 3 more instructions for ignore4a above + //VecU32V ignore4a = V4IsGrtrV32u(epsFloat4, minOfFarsa); // if tfar is negative, ignore since its a ray, not a line + ignore4a = V4U32or(ignore4a, V4IsGrtrV32u(maxOfNeasa, maxT4)); // if tnear is over maxT, ignore this result + + // 2i + VecU32V resa4 = V4IsGrtrV32u(maxOfNeasa, minOfFarsa); // if 1 => fail + resa4 = V4U32or(resa4, ignore4a); + + // 1i + V4U32StoreAligned(resa4, reinterpret_cast(resa)); + + PxU32* ptrs = (reinterpret_cast(tn))->ptrs; + + stack[stackPtr] = ptrs[0]; stackPtr += (1+resa[0]); // AP scaffold TODO: use VecU32add + stack[stackPtr] = ptrs[1]; stackPtr += (1+resa[1]); + stack[stackPtr] = ptrs[2]; stackPtr += (1+resa[2]); + stack[stackPtr] = ptrs[3]; stackPtr += (1+resa[3]); + } +} + +//explicit template instantiation +template void RTree::traverseRay<0>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const; + +template void RTree::traverseRay<1>(const PxVec3&, const PxVec3&, const PxU32, PxU32*, Gu::RTree::CallbackRaycast*, const PxVec3*, PxF32) const; + +///////////////////////////////////////////////////////////////////////// +void RTree::traverseOBB( + const Gu::Box& obb, const PxU32 maxResults, PxU32* resultsPtr, Gu::RTree::Callback* callback) const +{ + PX_UNUSED(resultsPtr); + PX_UNUSED(maxResults); + + const PxU32 maxStack = 128; + PxU32 stack[maxStack]; + + PX_ASSERT(mPages); + PX_ASSERT((uintptr_t(mPages) & 127) == 0); + PX_ASSERT((uintptr_t(this) & 15) == 0); + + PxU8* treeNodes8 = CAST_U8(mPages); + PxU32* stackPtr = stack; + + Vec4V ones, halves, eps; + ones = V4Load(1.0f); + halves = V4Load(0.5f); + eps = V4Load(1e-6f); + + PX_UNUSED(ones); + + Vec4V obbO = Vec4V_From_PxVec3_WUndefined(obb.center); + Vec4V obbE = Vec4V_From_PxVec3_WUndefined(obb.extents); + // Gu::Box::rot matrix columns are the OBB axes + Vec4V obbX = Vec4V_From_PxVec3_WUndefined(obb.rot.column0); + Vec4V obbY = Vec4V_From_PxVec3_WUndefined(obb.rot.column1); + Vec4V obbZ = Vec4V_From_PxVec3_WUndefined(obb.rot.column2); + +#if PX_WINDOWS || PX_XBOXONE + // Visual Studio compiler hangs with #defines + // On VMX platforms we use #defines in the other branch of this #ifdef to avoid register spills (LHS) + Vec4V obbESplatX = V4SplatElement<0>(obbE); + Vec4V obbESplatY = V4SplatElement<1>(obbE); + Vec4V obbESplatZ = V4SplatElement<2>(obbE); + Vec4V obbESplatNegX = V4Sub(zeroes, obbESplatX); + Vec4V obbESplatNegY = V4Sub(zeroes, obbESplatY); + Vec4V obbESplatNegZ = V4Sub(zeroes, obbESplatZ); + Vec4V obbXE = V4MulAdd(obbX, obbESplatX, zeroes); // scale axii by E + Vec4V obbYE = V4MulAdd(obbY, obbESplatY, zeroes); // scale axii by E + Vec4V obbZE = V4MulAdd(obbZ, obbESplatZ, zeroes); // scale axii by E + Vec4V obbOSplatX = V4SplatElement<0>(obbO); + Vec4V obbOSplatY = V4SplatElement<1>(obbO); + Vec4V obbOSplatZ = V4SplatElement<2>(obbO); + Vec4V obbXSplatX = V4SplatElement<0>(obbX); + Vec4V obbXSplatY = V4SplatElement<1>(obbX); + Vec4V obbXSplatZ = V4SplatElement<2>(obbX); + Vec4V obbYSplatX = V4SplatElement<0>(obbY); + Vec4V obbYSplatY = V4SplatElement<1>(obbY); + Vec4V obbYSplatZ = V4SplatElement<2>(obbY); + Vec4V obbZSplatX = V4SplatElement<0>(obbZ); + Vec4V obbZSplatY = V4SplatElement<1>(obbZ); + Vec4V obbZSplatZ = V4SplatElement<2>(obbZ); + Vec4V obbXESplatX = V4SplatElement<0>(obbXE); + Vec4V obbXESplatY = V4SplatElement<1>(obbXE); + Vec4V obbXESplatZ = V4SplatElement<2>(obbXE); + Vec4V obbYESplatX = V4SplatElement<0>(obbYE); + Vec4V obbYESplatY = V4SplatElement<1>(obbYE); + Vec4V obbYESplatZ = V4SplatElement<2>(obbYE); + Vec4V obbZESplatX = V4SplatElement<0>(obbZE); + Vec4V obbZESplatY = V4SplatElement<1>(obbZE); + Vec4V obbZESplatZ = V4SplatElement<2>(obbZE); +#else + #define obbESplatX V4SplatElement<0>(obbE) + #define obbESplatY V4SplatElement<1>(obbE) + #define obbESplatZ V4SplatElement<2>(obbE) + #define obbESplatNegX V4Sub(zeroes, obbESplatX) + #define obbESplatNegY V4Sub(zeroes, obbESplatY) + #define obbESplatNegZ V4Sub(zeroes, obbESplatZ) + #define obbXE V4MulAdd(obbX, obbESplatX, zeroes) + #define obbYE V4MulAdd(obbY, obbESplatY, zeroes) + #define obbZE V4MulAdd(obbZ, obbESplatZ, zeroes) + #define obbOSplatX V4SplatElement<0>(obbO) + #define obbOSplatY V4SplatElement<1>(obbO) + #define obbOSplatZ V4SplatElement<2>(obbO) + #define obbXSplatX V4SplatElement<0>(obbX) + #define obbXSplatY V4SplatElement<1>(obbX) + #define obbXSplatZ V4SplatElement<2>(obbX) + #define obbYSplatX V4SplatElement<0>(obbY) + #define obbYSplatY V4SplatElement<1>(obbY) + #define obbYSplatZ V4SplatElement<2>(obbY) + #define obbZSplatX V4SplatElement<0>(obbZ) + #define obbZSplatY V4SplatElement<1>(obbZ) + #define obbZSplatZ V4SplatElement<2>(obbZ) + #define obbXESplatX V4SplatElement<0>(obbXE) + #define obbXESplatY V4SplatElement<1>(obbXE) + #define obbXESplatZ V4SplatElement<2>(obbXE) + #define obbYESplatX V4SplatElement<0>(obbYE) + #define obbYESplatY V4SplatElement<1>(obbYE) + #define obbYESplatZ V4SplatElement<2>(obbYE) + #define obbZESplatX V4SplatElement<0>(obbZE) + #define obbZESplatY V4SplatElement<1>(obbZE) + #define obbZESplatZ V4SplatElement<2>(obbZE) +#endif + + PX_ASSERT(mPageSize == 4 || mPageSize == 8); + PX_ASSERT(mNumRootPages > 0); + + for (PxI32 j = PxI32(mNumRootPages-1); j >= 0; j --) + *stackPtr++ = j*sizeof(RTreePage); + PxU32 cacheTopValid = true; + PxU32 cacheTop = 0; + + PX_ALIGN_PREFIX(16) PxU32 resa_[4] PX_ALIGN_SUFFIX(16); + + do { + stackPtr--; + + PxU32 top; + if (cacheTopValid) // branch is faster than lhs + top = cacheTop; + else + top = stackPtr[0]; + PX_ASSERT(!cacheTopValid || top == cacheTop); + RTreePage* PX_RESTRICT tn = reinterpret_cast(treeNodes8 + top); + + const PxU32 offs = 0; + PxU32* ptrs = (reinterpret_cast(tn))->ptrs; + + // 6i + Vec4V minx4a = V4LoadA(tn->minx+offs); + Vec4V miny4a = V4LoadA(tn->miny+offs); + Vec4V minz4a = V4LoadA(tn->minz+offs); + Vec4V maxx4a = V4LoadA(tn->maxx+offs); + Vec4V maxy4a = V4LoadA(tn->maxy+offs); + Vec4V maxz4a = V4LoadA(tn->maxz+offs); + + VecU32V noOverlapa; + VecU32V resa4u; + { + // PRECOMPUTE FOR A BLOCK + // 109 instr per 4 OBB/AABB + // ABB iteration 1, start with OBB origin as other point -- 6 + Vec4V p1ABBxa = V4Max(minx4a, V4Min(maxx4a, obbOSplatX)); + Vec4V p1ABBya = V4Max(miny4a, V4Min(maxy4a, obbOSplatY)); + Vec4V p1ABBza = V4Max(minz4a, V4Min(maxz4a, obbOSplatZ)); + + // OBB iteration 1, move to OBB space first -- 12 + Vec4V p1ABBOxa = V4Sub(p1ABBxa, obbOSplatX); + Vec4V p1ABBOya = V4Sub(p1ABBya, obbOSplatY); + Vec4V p1ABBOza = V4Sub(p1ABBza, obbOSplatZ); + Vec4V obbPrjXa = V4MulAdd(p1ABBOxa, obbXSplatX, V4MulAdd(p1ABBOya, obbXSplatY, V4MulAdd(p1ABBOza, obbXSplatZ, zeroes))); + Vec4V obbPrjYa = V4MulAdd(p1ABBOxa, obbYSplatX, V4MulAdd(p1ABBOya, obbYSplatY, V4MulAdd(p1ABBOza, obbYSplatZ, zeroes))); + Vec4V obbPrjZa = V4MulAdd(p1ABBOxa, obbZSplatX, V4MulAdd(p1ABBOya, obbZSplatY, V4MulAdd(p1ABBOza, obbZSplatZ, zeroes))); + // clamp AABB point in OBB space to OBB extents. Since we scaled the axii, the extents are [-1,1] -- 6 + Vec4V pOBBxa = V4Max(obbESplatNegX, V4Min(obbPrjXa, obbESplatX)); + Vec4V pOBBya = V4Max(obbESplatNegY, V4Min(obbPrjYa, obbESplatY)); + Vec4V pOBBza = V4Max(obbESplatNegZ, V4Min(obbPrjZa, obbESplatZ)); + // go back to AABB space. we have x,y,z in obb space, need to multiply by axii -- 9 + Vec4V p1OBBxa = V4MulAdd(pOBBxa, obbXSplatX, V4MulAdd(pOBBya, obbYSplatX, V4MulAdd(pOBBza, obbZSplatX, obbOSplatX))); + Vec4V p1OBBya = V4MulAdd(pOBBxa, obbXSplatY, V4MulAdd(pOBBya, obbYSplatY, V4MulAdd(pOBBza, obbZSplatY, obbOSplatY))); + Vec4V p1OBBza = V4MulAdd(pOBBxa, obbXSplatZ, V4MulAdd(pOBBya, obbYSplatZ, V4MulAdd(pOBBza, obbZSplatZ, obbOSplatZ))); + + // ABB iteration 2 -- 6 instructions + Vec4V p2ABBxa = V4Max(minx4a, V4Min(maxx4a, p1OBBxa)); + Vec4V p2ABBya = V4Max(miny4a, V4Min(maxy4a, p1OBBya)); + Vec4V p2ABBza = V4Max(minz4a, V4Min(maxz4a, p1OBBza)); + // above blocks add up to 12+12+15=39 instr + // END PRECOMPUTE FOR A BLOCK + + // for AABBs precompute extents and center -- 9i + Vec4V abbCxa = V4MulAdd(V4Add(maxx4a, minx4a), halves, zeroes); + Vec4V abbCya = V4MulAdd(V4Add(maxy4a, miny4a), halves, zeroes); + Vec4V abbCza = V4MulAdd(V4Add(maxz4a, minz4a), halves, zeroes); + Vec4V abbExa = V4Sub(maxx4a, abbCxa); + Vec4V abbEya = V4Sub(maxy4a, abbCya); + Vec4V abbEza = V4Sub(maxz4a, abbCza); + + // now test separating axes D1 = p1OBB-p1ABB and D2 = p1OBB-p2ABB -- 37 instructions per axis + // D1 first -- 3 instructions + Vec4V d1xa = V4Sub(p1OBBxa, p1ABBxa), d1ya = V4Sub(p1OBBya, p1ABBya), d1za = V4Sub(p1OBBza, p1ABBza); + + // for AABB compute projections of extents and center -- 6 + Vec4V abbExd1Prja = V4MulAdd(d1xa, abbExa, zeroes); + Vec4V abbEyd1Prja = V4MulAdd(d1ya, abbEya, zeroes); + Vec4V abbEzd1Prja = V4MulAdd(d1za, abbEza, zeroes); + Vec4V abbCd1Prja = V4MulAdd(d1xa, abbCxa, V4MulAdd(d1ya, abbCya, V4MulAdd(d1za, abbCza, zeroes))); + + // for obb project each halfaxis and origin and add abs values of half-axis projections -- 12 instructions + Vec4V obbXEd1Prja = V4MulAdd(d1xa, obbXESplatX, V4MulAdd(d1ya, obbXESplatY, V4MulAdd(d1za, obbXESplatZ, zeroes))); + Vec4V obbYEd1Prja = V4MulAdd(d1xa, obbYESplatX, V4MulAdd(d1ya, obbYESplatY, V4MulAdd(d1za, obbYESplatZ, zeroes))); + Vec4V obbZEd1Prja = V4MulAdd(d1xa, obbZESplatX, V4MulAdd(d1ya, obbZESplatY, V4MulAdd(d1za, obbZESplatZ, zeroes))); + Vec4V obbOd1Prja = V4MulAdd(d1xa, obbOSplatX, V4MulAdd(d1ya, obbOSplatY, V4MulAdd(d1za, obbOSplatZ, zeroes))); + + // compare lengths between projected centers with sum of projected radii -- 16i + Vec4V originDiffd1a = v_absm(V4Sub(abbCd1Prja, obbOd1Prja)); + Vec4V absABBRd1a = V4Add(V4Add(v_absm(abbExd1Prja), v_absm(abbEyd1Prja)), v_absm(abbEzd1Prja)); + Vec4V absOBBRd1a = V4Add(V4Add(v_absm(obbXEd1Prja), v_absm(obbYEd1Prja)), v_absm(obbZEd1Prja)); + VecU32V noOverlapd1a = V4IsGrtrV32u(V4Sub(originDiffd1a, eps), V4Add(absABBRd1a, absOBBRd1a)); + VecU32V epsNoOverlapd1a = V4IsGrtrV32u(originDiffd1a, eps); + + // D2 next (35 instr) + // 3i + Vec4V d2xa = V4Sub(p1OBBxa, p2ABBxa), d2ya = V4Sub(p1OBBya, p2ABBya), d2za = V4Sub(p1OBBza, p2ABBza); + // for AABB compute projections of extents and center -- 6 + Vec4V abbExd2Prja = V4MulAdd(d2xa, abbExa, zeroes); + Vec4V abbEyd2Prja = V4MulAdd(d2ya, abbEya, zeroes); + Vec4V abbEzd2Prja = V4MulAdd(d2za, abbEza, zeroes); + Vec4V abbCd2Prja = V4MulAdd(d2xa, abbCxa, V4MulAdd(d2ya, abbCya, V4MulAdd(d2za, abbCza, zeroes))); + // for obb project each halfaxis and origin and add abs values of half-axis projections -- 12i + Vec4V obbXEd2Prja = V4MulAdd(d2xa, obbXESplatX, V4MulAdd(d2ya, obbXESplatY, V4MulAdd(d2za, obbXESplatZ, zeroes))); + Vec4V obbYEd2Prja = V4MulAdd(d2xa, obbYESplatX, V4MulAdd(d2ya, obbYESplatY, V4MulAdd(d2za, obbYESplatZ, zeroes))); + Vec4V obbZEd2Prja = V4MulAdd(d2xa, obbZESplatX, V4MulAdd(d2ya, obbZESplatY, V4MulAdd(d2za, obbZESplatZ, zeroes))); + Vec4V obbOd2Prja = V4MulAdd(d2xa, obbOSplatX, V4MulAdd(d2ya, obbOSplatY, V4MulAdd(d2za, obbOSplatZ, zeroes))); + // compare lengths between projected centers with sum of projected radii -- 16i + Vec4V originDiffd2a = v_absm(V4Sub(abbCd2Prja, obbOd2Prja)); + Vec4V absABBRd2a = V4Add(V4Add(v_absm(abbExd2Prja), v_absm(abbEyd2Prja)), v_absm(abbEzd2Prja)); + Vec4V absOBBRd2a = V4Add(V4Add(v_absm(obbXEd2Prja), v_absm(obbYEd2Prja)), v_absm(obbZEd2Prja)); + VecU32V noOverlapd2a = V4IsGrtrV32u(V4Sub(originDiffd2a, eps), V4Add(absABBRd2a, absOBBRd2a)); + VecU32V epsNoOverlapd2a = V4IsGrtrV32u(originDiffd2a, eps); + + // 8i + noOverlapa = V4U32or(V4U32and(noOverlapd1a, epsNoOverlapd1a), V4U32and(noOverlapd2a, epsNoOverlapd2a)); + VecU32V ignore4a = V4IsGrtrV32u(minx4a, maxx4a); // 1 if degenerate box (empty slot) + noOverlapa = V4U32or(noOverlapa, ignore4a); + resa4u = V4U32Andc(U4Load(1), noOverlapa); // 1 & ~noOverlap + V4U32StoreAligned(resa4u, reinterpret_cast(resa_)); + ///// 8+16+12+6+3+16+12+6+3+9+6+9+6+12+6+6=136i from load to result + } + + cacheTopValid = false; + for (PxU32 i = 0; i < 4; i++) + { + PxU32 ptr = ptrs[i+offs] & ~1; // clear the isLeaf bit + if (resa_[i]) + { + if (tn->isLeaf(i)) + { + if (!callback->processResults(1, &ptr)) + return; + } + else + { + *(stackPtr++) = ptr; + cacheTop = ptr; + cacheTopValid = true; + } + } + } + } while (stackPtr > stack); +} + +} // namespace Gu + +} diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h new file mode 100644 index 000000000..d6d803174 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h @@ -0,0 +1,105 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_CONVEX_TRI +#define GU_SWEEP_CONVEX_TRI + +#include "GuVecTriangle.h" +#include "GuVecConvexHull.h" +#include "GuConvexMesh.h" +#include "PxConvexMeshGeometry.h" +#include "GuGJKRaycast.h" + +// return true if hit, false if no hit +static PX_FORCE_INLINE bool sweepConvexVsTriangle( + const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, + ConvexHullV& convexHull, const Ps::aos::PsMatTransformV& meshToConvex, const Ps::aos::PsTransformV& convexTransfV, + const Ps::aos::Vec3VArg convexSpaceDir, const PxVec3& unitDir, const PxVec3& meshSpaceUnitDir, + const Ps::aos::FloatVArg fullDistance, PxReal shrunkDistance, + PxSweepHit& hit, bool isDoubleSided, const PxReal inflation, bool& initialOverlap, PxU32 faceIndex) +{ + using namespace Ps::aos; + if(!isDoubleSided) + { + // Create triangle normal + const PxVec3 denormalizedNormal = (v1 - v0).cross(v2 - v1); + + // Backface culling + // PT: WARNING, the test is reversed compared to usual because we pass -unitDir to this function + const bool culled = denormalizedNormal.dot(meshSpaceUnitDir) <= 0.0f; + if(culled) + return false; + } + + const Vec3V zeroV = V3Zero(); + const FloatV zero = FZero(); + + const Vec3V p0 = V3LoadU(v0); // in mesh local space + const Vec3V p1 = V3LoadU(v1); + const Vec3V p2 = V3LoadU(v2); + + // transform triangle verts from mesh local to convex local space + TriangleV triangleV(meshToConvex.transform(p0), meshToConvex.transform(p1), meshToConvex.transform(p2)); + + FloatV toi; + Vec3V closestA,normal; + + LocalConvex convexA(triangleV); + LocalConvex convexB(convexHull); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), convexHull.getCenter()); + // run GJK raycast + // sweep triangle in convex local space vs convex, closestA will be the impact point in convex local space + const bool gjkHit = gjkRaycastPenetration, LocalConvex >( + convexA, convexB, initialSearchDir, zero, zeroV, convexSpaceDir, toi, normal, closestA, inflation, false); + if(!gjkHit) + return false; + + if(FAllGrtrOrEq(zero, toi)) + { + initialOverlap = true; // PT: TODO: redundant with hit distance, consider removing + return setInitialOverlapResults(hit, unitDir, faceIndex); + } + + const FloatV minDist = FLoad(shrunkDistance); + const FloatV dist = FMul(toi, fullDistance); // scale the toi to original full sweep distance + if(FAllGrtr(minDist, dist)) // is current dist < minDist? + { + hit.faceIndex = faceIndex; + hit.flags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX; + const Vec3V destWorldPointA = convexTransfV.transform(closestA); + const Vec3V destNormal = V3Normalize(convexTransfV.rotate(normal)); + V3StoreU(destWorldPointA, hit.position); + V3StoreU(destNormal, hit.normal); + FStore(dist, &hit.distance); + return true; // report a hit + } + return false; // report no hit +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h new file mode 100644 index 000000000..7d568acb9 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h @@ -0,0 +1,169 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_MESH_H +#define GU_SWEEP_MESH_H + +#include "GuMidphaseInterface.h" +#include "GuVecConvexHull.h" + +namespace physx +{ + +namespace Gu +{ + // PT: class to make sure we can safely V4Load Matrix34's last column + class Matrix34Padded : public Cm::Matrix34 + { + public: + PX_FORCE_INLINE Matrix34Padded(const Matrix34& src) : Matrix34(src) {} + PX_FORCE_INLINE Matrix34Padded() {} + PX_FORCE_INLINE ~Matrix34Padded() {} + PxU32 padding; + }; + PX_COMPILE_TIME_ASSERT(0==(sizeof(Matrix34Padded)==16)); + + // PT: intermediate class containing shared bits of code & members + struct SweepShapeMeshHitCallback : MeshHitCallback + { + SweepShapeMeshHitCallback(CallbackMode::Enum mode, const PxHitFlags& hitFlags, bool flipNormal, float distCoef); + + const PxHitFlags mHitFlags; + bool mStatus; // Default is false, set to true if a valid hit is found. Stays true once true. + bool mInitialOverlap; // Default is false, set to true if an initial overlap hit is found. Reset for each hit. + bool mFlipNormal; // If negative scale is used we need to flip normal + PxReal mDistCoeff; // dist coeff from unscaled to scaled distance + + void operator=(const SweepShapeMeshHitCallback&) {} + }; + + struct SweepCapsuleMeshHitCallback : SweepShapeMeshHitCallback + { + PxSweepHit& mSweepHit; + const Cm::Matrix34& mVertexToWorldSkew; + const PxReal mTrueSweepDistance; // max sweep distance that can be used + PxReal mBestAlignmentValue; // best alignment value for triangle normal + PxReal mBestDist; // best distance, not the same as sweepHit.distance, can be shorter by epsilon + const Capsule& mCapsule; + const PxVec3& mUnitDir; + const bool mMeshDoubleSided; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED + const bool mIsSphere; + + SweepCapsuleMeshHitCallback(PxSweepHit& sweepHit, const Cm::Matrix34& worldMatrix, PxReal distance, bool meshDoubleSided, + const Capsule& capsule, const PxVec3& unitDir, const PxHitFlags& hitFlags, bool flipNormal, float distCoef); + + virtual PxAgain processHit(const PxRaycastHit& aHit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32*); + + // PT: TODO: unify these operators + void operator=(const SweepCapsuleMeshHitCallback&) {} + + bool finalizeHit( PxSweepHit& sweepHit, const Capsule& lss, const PxTriangleMeshGeometry& triMeshGeom, + const PxTransform& pose, bool isDoubleSided) const; + }; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + struct SweepBoxMeshHitCallback : SweepShapeMeshHitCallback + { + const Matrix34Padded& mMeshToBox; + PxReal mDist, mDist0; + physx::shdfnd::aos::FloatV mDistV; + const Box& mBox; + const PxVec3& mLocalDir; + const PxVec3& mWorldUnitDir; + PxReal mInflation; + PxTriangle mHitTriangle; + physx::shdfnd::aos::Vec3V mMinClosestA; + physx::shdfnd::aos::Vec3V mMinNormal; + physx::shdfnd::aos::Vec3V mLocalMotionV; + PxU32 mMinTriangleIndex; + PxVec3 mOneOverDir; + const bool mBothTriangleSidesCollide; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED || PxHitFlag::eMESH_BOTH_SIDES + + SweepBoxMeshHitCallback(CallbackMode::Enum mode_, const Matrix34Padded& meshToBox, PxReal distance, bool bothTriangleSidesCollide, + const Box& box, const PxVec3& localMotion, const PxVec3& localDir, const PxVec3& unitDir, + const PxHitFlags& hitFlags, const PxReal inflation, bool flipNormal, float distCoef); + + virtual ~SweepBoxMeshHitCallback() {} + + virtual PxAgain processHit(const PxRaycastHit& meshHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal& shrinkMaxT, const PxU32*); + + bool finalizeHit( PxSweepHit& sweepHit, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const PxTransform& boxTransform, const PxVec3& localDir, + bool meshBothSides, bool isDoubleSided) const; + + private: + SweepBoxMeshHitCallback& operator=(const SweepBoxMeshHitCallback&); + }; + + struct SweepConvexMeshHitCallback : SweepShapeMeshHitCallback + { + PxTriangle mHitTriangle; + ConvexHullV mConvexHull; + physx::shdfnd::aos::PsMatTransformV mMeshToConvex; + physx::shdfnd::aos::PsTransformV mConvexPoseV; + const Cm::FastVertex2ShapeScaling& mMeshScale; + PxSweepHit mSweepHit; // stores either the closest or any hit depending on value of mAnyHit + physx::shdfnd::aos::FloatV mInitialDistance; + physx::shdfnd::aos::Vec3V mConvexSpaceDir; // convexPose.rotateInv(-unit*distance) + PxVec3 mUnitDir; + PxVec3 mMeshSpaceUnitDir; + PxReal mInflation; + const bool mAnyHit; + const bool mBothTriangleSidesCollide; // PT: true if PxMeshGeometryFlag::eDOUBLE_SIDED || PxHitFlag::eMESH_BOTH_SIDES + + SweepConvexMeshHitCallback( const ConvexHullData& hull, const PxMeshScale& convexScale, const Cm::FastVertex2ShapeScaling& meshScale, + const PxTransform& convexPose, const PxTransform& meshPose, + const PxVec3& unitDir, const PxReal distance, PxHitFlags hitFlags, const bool bothTriangleSidesCollide, const PxReal inflation, + const bool anyHit, float distCoef); + + virtual ~SweepConvexMeshHitCallback() {} + + virtual PxAgain processHit(const PxRaycastHit& hit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal& shrunkMaxT, const PxU32*); + + bool finalizeHit(PxSweepHit& sweepHit, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, + const PxVec3& unitDir, PxReal inflation, + bool isMtd, bool meshBothSides, bool isDoubleSided, bool bothTriangleSidesCollide); + + private: + SweepConvexMeshHitCallback& operator=(const SweepConvexMeshHitCallback&); + }; + +#if PX_VC + #pragma warning(pop) +#endif + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp new file mode 100644 index 000000000..b4028ef8c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp @@ -0,0 +1,603 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepTests.h" +#include "GuSweepMesh.h" +#include "GuInternal.h" +#include "GuConvexUtilsInternal.h" +#include "CmScaling.h" +#include "GuSweepMTD.h" +#include "GuVecBox.h" +#include "GuVecCapsule.h" +#include "GuSweepBoxTriangle_SAT.h" +#include "GuSweepCapsuleTriangle.h" +#include "GuSweepSphereTriangle.h" +#include "GuDistancePointTriangle.h" +#include "GuCapsule.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; +using namespace physx::shdfnd::aos; + +#include "GuSweepConvexTri.h" + +/////////////////////////////////////////////////////////////////////////////// + +static bool sweepSphereTriangle(const PxTriangle& tri, + const PxVec3& center, PxReal radius, + const PxVec3& unitDir, const PxReal distance, + PxSweepHit& hit, PxVec3& triNormalOut, + PxHitFlags hitFlags, bool isDoubleSided) +{ + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + + // PT: test if shapes initially overlap + // PT: add culling here for now, but could be made more efficiently... + + // Create triangle normal + PxVec3 denormalizedNormal; + tri.denormalizedNormal(denormalizedNormal); + + // Backface culling + if(doBackfaceCulling && (denormalizedNormal.dot(unitDir) > 0.0f)) + return false; + + float s_unused, t_unused; + const PxVec3 cp = closestPtPointTriangle(center, tri.verts[0], tri.verts[1], tri.verts[2], s_unused, t_unused); + const PxReal dist2 = (cp - center).magnitudeSquared(); + if(dist2<=radius*radius) + { + triNormalOut = denormalizedNormal.getNormalized(); + return setInitialOverlapResults(hit, unitDir, 0); + } + } + + return sweepSphereTriangles(1, &tri, + center, radius, + unitDir, distance, + NULL, + hit, triNormalOut, + isDoubleSided, meshBothSides, false, false); +} + +/////////////////////////////////////////////////////////////////////////////// + +SweepShapeMeshHitCallback::SweepShapeMeshHitCallback(CallbackMode::Enum mode, const PxHitFlags& hitFlags, bool flipNormal, float distCoef) : + MeshHitCallback (mode), + mHitFlags (hitFlags), + mStatus (false), + mInitialOverlap (false), + mFlipNormal (flipNormal), + mDistCoeff (distCoef) +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +SweepCapsuleMeshHitCallback::SweepCapsuleMeshHitCallback( + PxSweepHit& sweepHit, const Matrix34& worldMatrix, PxReal distance, bool meshDoubleSided, + const Capsule& capsule, const PxVec3& unitDir, const PxHitFlags& hitFlags, bool flipNormal, float distCoef) : + SweepShapeMeshHitCallback (CallbackMode::eMULTIPLE, hitFlags, flipNormal, distCoef), + mSweepHit (sweepHit), + mVertexToWorldSkew (worldMatrix), + mTrueSweepDistance (distance), + mBestAlignmentValue (2.0f), + mBestDist (distance + GU_EPSILON_SAME_DISTANCE), + mCapsule (capsule), + mUnitDir (unitDir), + mMeshDoubleSided (meshDoubleSided), + mIsSphere (capsule.p0 == capsule.p1) +{ + mSweepHit.distance = mTrueSweepDistance; +} + +PxAgain SweepCapsuleMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& aHit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal& shrunkMaxT, const PxU32*) +{ + const PxTriangle tmpt( mVertexToWorldSkew.transform(v0), + mVertexToWorldSkew.transform(mFlipNormal ? v2 : v1), + mVertexToWorldSkew.transform(mFlipNormal ? v1 : v2)); + + PxSweepHit localHit; // PT: TODO: ctor! + PxVec3 triNormal; + // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + // make it a relative epsilon to make sure it still works with large distances + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE * PxMax(1.0f, mSweepHit.distance); + const float minD = mSweepHit.distance + distEpsilon; + if(mIsSphere) + { + if(!::sweepSphereTriangle( tmpt, + mCapsule.p0, mCapsule.radius, + mUnitDir, minD, + localHit, triNormal, + mHitFlags, mMeshDoubleSided)) + return true; + } + else + { + // PT: this one is safe because cullbox is NULL (no need to allocate one more triangle) + if(!sweepCapsuleTriangles_Precise( 1, &tmpt, + mCapsule, + mUnitDir, minD, + NULL, + localHit, triNormal, + mHitFlags, mMeshDoubleSided, + NULL)) + return true; + } + + const PxReal alignmentValue = computeAlignmentValue(triNormal, mUnitDir); +// if(keepTriangle(localHit.distance, alignmentValue, mBestDist, mBestAlignmentValue, mTrueSweepDistance, distEpsilon)) + if(keepTriangle(localHit.distance, alignmentValue, mBestDist, mBestAlignmentValue, mTrueSweepDistance, GU_EPSILON_SAME_DISTANCE)) + { + mBestAlignmentValue = alignmentValue; + + // AP: need to shrink the sweep distance passed into sweepCapsuleTriangles for correctness so that next sweep is closer + shrunkMaxT = localHit.distance * mDistCoeff; // shrunkMaxT is scaled + + mBestDist = PxMin(mBestDist, localHit.distance); // exact lower bound + mSweepHit.flags = localHit.flags; + mSweepHit.distance = localHit.distance; + mSweepHit.normal = localHit.normal; + mSweepHit.position = localHit.position; + mSweepHit.faceIndex = aHit.faceIndex; + + mStatus = true; + //ML:this is the initial overlap condition + if(localHit.distance == 0.0f) + { + mInitialOverlap = true; + return false; + } + if(mHitFlags & PxHitFlag::eMESH_ANY) + return false; // abort traversal + } + return true; +} + +bool SweepCapsuleMeshHitCallback::finalizeHit( PxSweepHit& sweepHit, const Capsule& lss, const PxTriangleMeshGeometry& triMeshGeom, + const PxTransform& pose, bool isDoubleSided) const +{ + if(!mStatus) + return false; + + if(mInitialOverlap) + { + // PT: TODO: consider using 'setInitialOverlapResults' here + bool hasContacts = false; + if(mHitFlags & PxHitFlag::eMTD) + { + const Vec3V p0 = V3LoadU(mCapsule.p0); + const Vec3V p1 = V3LoadU(mCapsule.p1); + const FloatV radius = FLoad(lss.radius); + CapsuleV capsuleV; + capsuleV.initialize(p0, p1, radius); + + //we need to calculate the MTD + hasContacts = computeCapsule_TriangleMeshMTD(triMeshGeom, pose, capsuleV, mCapsule.radius, isDoubleSided, sweepHit); + } + setupSweepHitForMTD(sweepHit, hasContacts, mUnitDir); + } + else + { + sweepHit.flags = PxHitFlag::eNORMAL | PxHitFlag::ePOSITION | PxHitFlag::eFACE_INDEX; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool sweepCapsule_MeshGeom(GU_CAPSULE_SWEEP_FUNC_PARAMS) +{ + PX_UNUSED(capsuleGeom_); + PX_UNUSED(capsulePose_); + + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + return Midphase::sweepCapsuleVsMesh(meshData, meshGeom, pose, lss, unitDir, distance, sweepHit, hitFlags, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// + + // same as 'mat.transform(p)' but using SIMD + static PX_FORCE_INLINE Vec4V transformV(const Vec4V p, const Matrix34Padded& mat) + { + Vec4V ResV = V4Scale(V4LoadU(&mat.m.column0.x), V4GetX(p)); + ResV = V4ScaleAdd(V4LoadU(&mat.m.column1.x), V4GetY(p), ResV); + ResV = V4ScaleAdd(V4LoadU(&mat.m.column2.x), V4GetZ(p), ResV); + ResV = V4Add(ResV, V4LoadU(&mat.p.x)); // PT: this load is safe thanks to padding + return ResV; + } + +/////////////////////////////////////////////////////////////////////////////// + +SweepBoxMeshHitCallback::SweepBoxMeshHitCallback( CallbackMode::Enum mode_, const Matrix34Padded& meshToBox, PxReal distance, bool bothTriangleSidesCollide, + const Box& box, const PxVec3& localMotion, const PxVec3& localDir, const PxVec3& unitDir, + const PxHitFlags& hitFlags, const PxReal inflation, bool flipNormal, float distCoef) : + SweepShapeMeshHitCallback (mode_, hitFlags, flipNormal,distCoef), + mMeshToBox (meshToBox), + mDist (distance), + mBox (box), + mLocalDir (localDir), + mWorldUnitDir (unitDir), + mInflation (inflation), + mBothTriangleSidesCollide (bothTriangleSidesCollide) +{ + mLocalMotionV = V3LoadU(localMotion); + mDistV = FLoad(distance); + mDist0 = distance; + mOneOverDir = PxVec3( + mLocalDir.x!=0.0f ? 1.0f/mLocalDir.x : 0.0f, + mLocalDir.y!=0.0f ? 1.0f/mLocalDir.y : 0.0f, + mLocalDir.z!=0.0f ? 1.0f/mLocalDir.z : 0.0f); +} + +PxAgain SweepBoxMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& meshHit, const PxVec3& lp0, const PxVec3& lp1, const PxVec3& lp2, PxReal& shrinkMaxT, const PxU32*) +{ + if(mHitFlags & PxHitFlag::ePRECISE_SWEEP) + { + const PxTriangle currentTriangle( + mMeshToBox.transform(lp0), + mMeshToBox.transform(mFlipNormal ? lp2 : lp1), + mMeshToBox.transform(mFlipNormal ? lp1 : lp2)); + + PxF32 t = PX_MAX_REAL; // PT: could be better! + if(!triBoxSweepTestBoxSpace(currentTriangle, mBox.extents, mLocalDir, mOneOverDir, mDist, t, !mBothTriangleSidesCollide)) + return true; + + if(t <= mDist) + { + // PT: test if shapes initially overlap + mDist = t; + shrinkMaxT = t * mDistCoeff; // shrunkMaxT is scaled + mMinClosestA = V3LoadU(currentTriangle.verts[0]); // PT: this is arbitrary + mMinNormal = V3LoadU(-mWorldUnitDir); + mStatus = true; + mMinTriangleIndex = meshHit.faceIndex; + mHitTriangle = currentTriangle; + if(t == 0.0f) + { + mInitialOverlap = true; + return false; // abort traversal + } + } + } + else + { + const FloatV zero = FZero(); + + // PT: SIMD code similar to: + // const Vec3V triV0 = V3LoadU(mMeshToBox.transform(lp0)); + // const Vec3V triV1 = V3LoadU(mMeshToBox.transform(lp1)); + // const Vec3V triV2 = V3LoadU(mMeshToBox.transform(lp2)); + // + // SIMD version works but we need to ensure all loads are safe. + // For incoming vertices they should either come from the vertex array or from a binary deserialized file. + // For the vertex array we can just allocate one more vertex. For the binary file it should be ok as soon + // as vertices aren't the last thing serialized in the file. + // For the matrix only the last column is a problem, and we can easily solve that with some padding in the local class. + const Vec3V triV0 = Vec3V_From_Vec4V(transformV(V4LoadU(&lp0.x), mMeshToBox)); + const Vec3V triV1 = Vec3V_From_Vec4V(transformV(V4LoadU(mFlipNormal ? &lp2.x : &lp1.x), mMeshToBox)); + const Vec3V triV2 = Vec3V_From_Vec4V(transformV(V4LoadU(mFlipNormal ? &lp1.x : &lp2.x), mMeshToBox)); + + if(!mBothTriangleSidesCollide) + { + const Vec3V triNormal = V3Cross(V3Sub(triV2, triV1),V3Sub(triV0, triV1)); + if(FAllGrtrOrEq(V3Dot(triNormal, mLocalMotionV), zero)) + return true; + } + + const Vec3V zeroV = V3Zero(); + const Vec3V boxExtents = V3LoadU(mBox.extents); + const BoxV boxV(zeroV, boxExtents); + + const TriangleV triangleV(triV0, triV1, triV2); + + FloatV lambda; + Vec3V closestA, normal;//closestA and normal is in the local space of convex hull + LocalConvex convexA(triangleV); + LocalConvex convexB(boxV); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter()); + if(!gjkRaycastPenetration< LocalConvex, LocalConvex >(convexA, convexB, initialSearchDir, zero, zeroV, mLocalMotionV, lambda, normal, closestA, mInflation, false)) + return true; + + mStatus = true; + mMinClosestA = closestA; + mMinTriangleIndex = meshHit.faceIndex; + if(FAllGrtrOrEq(zero, lambda)) // lambda < 0? => initial overlap + { + mInitialOverlap = true; + shrinkMaxT = 0.0f; + mDistV = zero; + mDist = 0.0f; + mMinNormal = V3LoadU(-mWorldUnitDir); + return false; + } + + PxF32 f; + FStore(lambda, &f); + mDist = f*mDist; // shrink dist + mLocalMotionV = V3Scale(mLocalMotionV, lambda); // shrink localMotion + mDistV = FMul(mDistV, lambda); // shrink distV + mMinNormal = normal; + if(mDist * mDistCoeff < shrinkMaxT) // shrink shrinkMaxT + shrinkMaxT = mDist * mDistCoeff; // shrunkMaxT is scaled + + //mHitTriangle = currentTriangle; + V3StoreU(triV0, mHitTriangle.verts[0]); + V3StoreU(triV1, mHitTriangle.verts[1]); + V3StoreU(triV2, mHitTriangle.verts[2]); + } + return true; +} + +bool SweepBoxMeshHitCallback::finalizeHit( PxSweepHit& sweepHit, const PxTriangleMeshGeometry& triMeshGeom, const PxTransform& pose, + const PxTransform& boxTransform, const PxVec3& localDir, + bool meshBothSides, bool isDoubleSided) const +{ + if(!mStatus) + return false; + + Vec3V minClosestA = mMinClosestA; + Vec3V minNormal = mMinNormal; + sweepHit.faceIndex = mMinTriangleIndex; + + if(mInitialOverlap) + { + bool hasContacts = false; + if(mHitFlags & PxHitFlag::eMTD) + hasContacts = computeBox_TriangleMeshMTD(triMeshGeom, pose, mBox, boxTransform, mInflation, mBothTriangleSidesCollide, sweepHit); + + setupSweepHitForMTD(sweepHit, hasContacts, mWorldUnitDir); + } + else + { + sweepHit.distance = mDist; + sweepHit.flags = PxHitFlag::eFACE_INDEX; + + // PT: we need the "best triangle" normal in order to call 'shouldFlipNormal'. We stored the best + // triangle in both GJK & precise codepaths (in box space). We use a dedicated 'shouldFlipNormal' + // function that delays computing the triangle normal. + // TODO: would still be more efficient to store the best normal directly, it's already computed at least + // in the GJK codepath. + + const Vec3V p0 = V3LoadU(&boxTransform.p.x); + const QuatV q0 = QuatVLoadU(&boxTransform.q.x); + const PsTransformV boxPos(p0, q0); + + if(mHitFlags & PxHitFlag::ePRECISE_SWEEP) + { + computeBoxLocalImpact(sweepHit.position, sweepHit.normal, sweepHit.flags, mBox, localDir, mHitTriangle, mHitFlags, isDoubleSided, meshBothSides, mDist); + } + else + { + sweepHit.flags |= PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + + // PT: now for the GJK path, we must first always negate the returned normal. Similar to what happens in the precise path, + // we can't delay this anymore: our normal must be properly oriented in order to call 'shouldFlipNormal'. + minNormal = V3Neg(minNormal); + + // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention + PxVec3 tmp; + V3StoreU(minNormal, tmp); + + if(shouldFlipNormal(tmp, meshBothSides, isDoubleSided, mHitTriangle, localDir, NULL)) + minNormal = V3Neg(minNormal); + + // PT: finally, this moves everything back to world space + V3StoreU(boxPos.rotate(minNormal), sweepHit.normal); + V3StoreU(boxPos.transform(minClosestA), sweepHit.position); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool sweepBox_MeshGeom(GU_BOX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + PX_UNUSED(boxPose_); + PX_UNUSED(boxGeom_); + + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + return Midphase::sweepBoxVsMesh(meshData, meshGeom, pose, box, unitDir, distance, sweepHit, hitFlags, inflation); +} + +/////////////////////////////////////////////////////////////////////////////// + +SweepConvexMeshHitCallback::SweepConvexMeshHitCallback( const ConvexHullData& hull, const PxMeshScale& convexScale, const FastVertex2ShapeScaling& meshScale, + const PxTransform& convexPose, const PxTransform& meshPose, + const PxVec3& unitDir, const PxReal distance, PxHitFlags hitFlags, const bool bothTriangleSidesCollide, const PxReal inflation, + const bool anyHit, float distCoef) : + SweepShapeMeshHitCallback (CallbackMode::eMULTIPLE, hitFlags, meshScale.flipsNormal(), distCoef), + mMeshScale (meshScale), + mUnitDir (unitDir), + mInflation (inflation), + mAnyHit (anyHit), + mBothTriangleSidesCollide (bothTriangleSidesCollide) +{ + mSweepHit.distance = distance; // this will be shrinking progressively as we sweep and clip the sweep length + mSweepHit.faceIndex = 0xFFFFFFFF; + + mMeshSpaceUnitDir = meshPose.rotateInv(unitDir); + + const Vec3V worldDir = V3LoadU(unitDir); + const FloatV dist = FLoad(distance); + const QuatV q0 = QuatVLoadU(&meshPose.q.x); + const Vec3V p0 = V3LoadU(&meshPose.p.x); + + const QuatV q1 = QuatVLoadU(&convexPose.q.x); + const Vec3V p1 = V3LoadU(&convexPose.p.x); + + const PsTransformV meshPoseV(p0, q0); + const PsTransformV convexPoseV(p1, q1); + + mMeshToConvex = convexPoseV.transformInv(meshPoseV); + mConvexPoseV = convexPoseV; + mConvexSpaceDir = convexPoseV.rotateInv(V3Neg(V3Scale(worldDir, dist))); + mInitialDistance = dist; + + const Vec3V vScale = V3LoadU_SafeReadW(convexScale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const QuatV vQuat = QuatVLoadU(&convexScale.rotation.x); + mConvexHull.initialize(&hull, V3Zero(), vScale, vQuat, convexScale.isIdentity()); +} + +PxAgain SweepConvexMeshHitCallback::processHit( // all reported coords are in mesh local space including hit.position + const PxRaycastHit& hit, const PxVec3& av0, const PxVec3& av1, const PxVec3& av2, PxReal& shrunkMaxT, const PxU32*) +{ + const PxVec3 v0 = mMeshScale * av0; + const PxVec3 v1 = mMeshScale * (mFlipNormal ? av2 : av1); + const PxVec3 v2 = mMeshScale * (mFlipNormal ? av1 : av2); + + // mSweepHit will be updated if sweep distance is < input mSweepHit.distance + const PxReal oldDist = mSweepHit.distance; + if(sweepConvexVsTriangle( + v0, v1, v2, mConvexHull, mMeshToConvex, mConvexPoseV, mConvexSpaceDir, + mUnitDir, mMeshSpaceUnitDir, mInitialDistance, oldDist, mSweepHit, mBothTriangleSidesCollide, + mInflation, mInitialOverlap, hit.faceIndex)) + { + mStatus = true; + shrunkMaxT = mSweepHit.distance * mDistCoeff; // shrunkMaxT is scaled + + // PT: added for 'shouldFlipNormal' + mHitTriangle.verts[0] = v0; + mHitTriangle.verts[1] = v1; + mHitTriangle.verts[2] = v2; + + if(mAnyHit) + return false; // abort traversal + + if(mSweepHit.distance == 0.0f) + return false; + } + return true; // continue traversal +} + +bool SweepConvexMeshHitCallback::finalizeHit( PxSweepHit& sweepHit, const PxTriangleMeshGeometry& meshGeom, const PxTransform& pose, + const PxConvexMeshGeometry& convexGeom, const PxTransform& convexPose, + const PxVec3& unitDir, PxReal inflation, + bool isMtd, bool meshBothSides, bool isDoubleSided, bool bothTriangleSidesCollide) +{ + if(!mStatus) + return false; + + if(mInitialOverlap) + { + bool hasContacts = false; + if(isMtd) + hasContacts = computeConvex_TriangleMeshMTD(meshGeom, pose, convexGeom, convexPose, inflation, bothTriangleSidesCollide, sweepHit); + + setupSweepHitForMTD(sweepHit, hasContacts, unitDir); + + sweepHit.faceIndex = mSweepHit.faceIndex; + } + else + { + sweepHit = mSweepHit; + //sweepHit.position += unitDir * sweepHit.distance; + sweepHit.normal = -sweepHit.normal; + sweepHit.normal.normalize(); + + // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention + // PT: beware, the best triangle is in mesh-space, but the impact data is in world-space already + if(shouldFlipNormal(sweepHit.normal, meshBothSides, isDoubleSided, mHitTriangle, unitDir, &pose)) + sweepHit.normal = -sweepHit.normal; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool sweepConvex_MeshGeom(GU_CONVEX_SWEEP_FUNC_PARAMS) +{ + PX_ASSERT(geom.getType() == PxGeometryType::eTRIANGLEMESH); + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + + ConvexMesh* convexMesh = static_cast(convexGeom.convexMesh); + TriangleMesh* meshData = static_cast(meshGeom.triangleMesh); + + const bool idtScaleConvex = convexGeom.scale.isIdentity(); + const bool idtScaleMesh = meshGeom.scale.isIdentity(); + + FastVertex2ShapeScaling convexScaling; + if(!idtScaleConvex) + convexScaling.init(convexGeom.scale); + + FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(meshGeom.scale); + + PX_ASSERT(!convexMesh->getLocalBoundsFast().isEmpty()); + const PxBounds3 hullAABB = convexMesh->getLocalBoundsFast().transformFast(convexScaling.getVertex2ShapeSkew()); + + Box hullOBB; + computeHullOBB(hullOBB, hullAABB, 0.0f, Matrix34(convexPose), Matrix34(pose), meshScaling, idtScaleMesh); + + hullOBB.extents.x += inflation; + hullOBB.extents.y += inflation; + hullOBB.extents.z += inflation; + + const PxVec3 localDir = pose.rotateInv(unitDir); + + // inverse transform the sweep direction and distance to mesh space + PxVec3 meshSpaceSweepVector = meshScaling.getShape2VertexSkew().transform(localDir*distance); + const PxReal meshSpaceSweepDist = meshSpaceSweepVector.normalize(); + + PxReal distCoeff = 1.0f; + if (!idtScaleMesh) + distCoeff = meshSpaceSweepDist / distance; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool isDoubleSided = meshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED; + const bool bothTriangleSidesCollide = isDoubleSided || meshBothSides; + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + SweepConvexMeshHitCallback callback( + convexMesh->getHull(), convexGeom.scale, meshScaling, convexPose, pose, -unitDir, distance, hitFlags, + bothTriangleSidesCollide, inflation, anyHit, distCoeff); + + Midphase::sweepConvexVsMesh(meshData, hullOBB, meshSpaceSweepVector, meshSpaceSweepDist, callback, anyHit); + + const bool isMtd = hitFlags & PxHitFlag::eMTD; + return callback.finalizeHit(sweepHit, meshGeom, pose, convexGeom, convexPose, unitDir, inflation, isMtd, meshBothSides, isDoubleSided, bothTriangleSidesCollide); +} + +/////////////////////////////////////////////////////////////////////////////// + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h new file mode 100644 index 000000000..421198706 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLE32_H +#define GU_TRIANGLE32_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PsUtilities.h" + +namespace physx +{ +namespace Gu +{ + /** + \brief Structure used to store indices for a triangles points. T is either PxU32 or PxU16 + + */ + + template + struct TriangleT// : public Ps::UserAllocated + { + PX_INLINE TriangleT() {} + PX_INLINE TriangleT(T a, T b, T c) { v[0] = a; v[1] = b; v[2] = c; } + template + PX_INLINE TriangleT(const TriangleT& other) { v[0] = other[0]; v[1] = other[1]; v[2] = other[2]; } + PX_INLINE T& operator[](T i) { return v[i]; } + template//any type of TriangleT<>, possibly with different T + PX_INLINE TriangleT& operator=(const TriangleT& i) { v[0]=i[0]; v[1]=i[1]; v[2]=i[2]; return *this; } + PX_INLINE const T& operator[](T i) const { return v[i]; } + + void flip() + { + Ps::swap(v[1], v[2]); + } + + PX_INLINE void center(const PxVec3* verts, PxVec3& center) const + { + const PxVec3& p0 = verts[v[0]]; + const PxVec3& p1 = verts[v[1]]; + const PxVec3& p2 = verts[v[2]]; + center = (p0+p1+p2)*0.33333333333333333333f; + } + + float area(const PxVec3* verts) const + { + const PxVec3& p0 = verts[v[0]]; + const PxVec3& p1 = verts[v[1]]; + const PxVec3& p2 = verts[v[2]]; + return ((p0-p1).cross(p0-p2)).magnitude() * 0.5f; + } + + PxU8 findEdge(T vref0, T vref1) const + { + if(v[0]==vref0 && v[1]==vref1) return 0; + else if(v[0]==vref1 && v[1]==vref0) return 0; + else if(v[0]==vref0 && v[2]==vref1) return 1; + else if(v[0]==vref1 && v[2]==vref0) return 1; + else if(v[1]==vref0 && v[2]==vref1) return 2; + else if(v[1]==vref1 && v[2]==vref0) return 2; + return 0xff; + } + + // counter clock wise order + PxU8 findEdgeCCW(T vref0, T vref1) const + { + if(v[0]==vref0 && v[1]==vref1) return 0; + else if(v[0]==vref1 && v[1]==vref0) return 0; + else if(v[0]==vref0 && v[2]==vref1) return 2; + else if(v[0]==vref1 && v[2]==vref0) return 2; + else if(v[1]==vref0 && v[2]==vref1) return 1; + else if(v[1]==vref1 && v[2]==vref0) return 1; + return 0xff; + } + + bool replaceVertex(T oldref, T newref) + { + if(v[0]==oldref) { v[0] = newref; return true; } + else if(v[1]==oldref) { v[1] = newref; return true; } + else if(v[2]==oldref) { v[2] = newref; return true; } + return false; + } + + bool isDegenerate() const + { + if(v[0]==v[1]) return true; + if(v[1]==v[2]) return true; + if(v[2]==v[0]) return true; + return false; + } + + PX_INLINE void denormalizedNormal(const PxVec3* verts, PxVec3& normal) const + { + const PxVec3& p0 = verts[v[0]]; + const PxVec3& p1 = verts[v[1]]; + const PxVec3& p2 = verts[v[2]]; + normal = ((p2 - p1).cross(p0 - p1)); + } + + T v[3]; //vertex indices + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h new file mode 100644 index 000000000..5c46b8857 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h @@ -0,0 +1,207 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLE_CACHE_H +#define GU_TRIANGLE_CACHE_H +#include "PsHash.h" +#include "PsUtilities.h" + +namespace physx +{ + namespace Gu + { + struct CachedEdge + { + protected: + PxU32 mId0, mId1; + public: + CachedEdge(PxU32 i0, PxU32 i1) + { + mId0 = PxMin(i0, i1); + mId1 = PxMax(i0, i1); + } + + CachedEdge() + { + } + + PxU32 getId0() const { return mId0; } + PxU32 getId1() const { return mId1; } + + bool operator == (const CachedEdge& other) const + { + return mId0 == other.mId0 && mId1 == other.mId1; + } + + PxU32 getHashCode() const + { + return Ps::hash(mId0 << 16 | mId1); + } + }; + + struct CachedVertex + { + private: + PxU32 mId; + public: + CachedVertex(PxU32 id) + { + mId = id; + } + + CachedVertex() + { + } + + PxU32 getId() const { return mId; } + + PxU32 getHashCode() const + { + return mId; + } + + bool operator == (const CachedVertex& other) const + { + return mId == other.mId; + } + }; + + template + struct CacheMap + { + PX_COMPILE_TIME_ASSERT(MaxCount < 0xFF); + Elem mCache[MaxCount]; + PxU8 mNextInd[MaxCount]; + PxU8 mIndex[MaxCount]; + PxU32 mSize; + + CacheMap() : mSize(0) + { + for(PxU32 a = 0; a < MaxCount; ++a) + { + mIndex[a] = 0xFF; + } + } + + bool addData(const Elem& data) + { + if(mSize == MaxCount) + return false; + + const PxU8 hash = PxU8(data.getHashCode() % MaxCount); + + PxU8 index = hash; + PxU8 nextInd = mIndex[hash]; + while(nextInd != 0xFF) + { + index = nextInd; + if(mCache[index] == data) + return false; + nextInd = mNextInd[nextInd]; + } + + if(mIndex[hash] == 0xFF) + { + mIndex[hash] = Ps::to8(mSize); + } + else + { + mNextInd[index] = Ps::to8(mSize); + } + mNextInd[mSize] = 0xFF; + mCache[mSize++] = data; + return true; + } + + bool contains(const Elem& data) const + { + PxU32 hash = (data.getHashCode() % MaxCount); + PxU8 index = mIndex[hash]; + + while(index != 0xFF) + { + if(mCache[index] == data) + return true; + index = mNextInd[index]; + } + return false; + } + + const Elem* get(const Elem& data) const + { + PxU32 hash = (data.getHashCode() % MaxCount); + PxU8 index = mIndex[hash]; + + while(index != 0xFF) + { + if(mCache[index] == data) + return &mCache[index]; + index = mNextInd[index]; + } + return NULL; + } + }; + + template + struct TriangleCache + { + PxVec3 mVertices[3*MaxTriangles]; + PxU32 mIndices[3*MaxTriangles]; + PxU32 mTriangleIndex[MaxTriangles]; + PxU8 mEdgeFlags[MaxTriangles]; + PxU32 mNumTriangles; + + TriangleCache() : mNumTriangles(0) + { + } + + PX_FORCE_INLINE bool isEmpty() const { return mNumTriangles == 0; } + PX_FORCE_INLINE bool isFull() const { return mNumTriangles == MaxTriangles; } + PX_FORCE_INLINE void reset() { mNumTriangles = 0; } + + void addTriangle(const PxVec3* verts, const PxU32* indices, PxU32 triangleIndex, PxU8 edgeFlag) + { + PX_ASSERT(mNumTriangles < MaxTriangles); + PxU32 triInd = mNumTriangles++; + PxU32 triIndMul3 = triInd*3; + mVertices[triIndMul3] = verts[0]; + mVertices[triIndMul3+1] = verts[1]; + mVertices[triIndMul3+2] = verts[2]; + mIndices[triIndMul3] = indices[0]; + mIndices[triIndMul3+1] = indices[1]; + mIndices[triIndMul3+2] = indices[2]; + mTriangleIndex[triInd] = triangleIndex; + mEdgeFlags[triInd] = edgeFlag; + } + }; + } +} + +#endif + diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp new file mode 100644 index 000000000..dfe813423 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp @@ -0,0 +1,232 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsIntrinsics.h" +#include "GuMidphaseInterface.h" +#include "GuSerialize.h" +#include "GuMeshFactory.h" +#include "CmRenderOutput.h" +#include "PxVisualizationParameter.h" +#include "GuBox.h" +#include "PxMeshScale.h" +#include "CmUtils.h" + +using namespace physx; + +namespace physx +{ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PxConcreteType::Enum gTable[] = { PxConcreteType::eTRIANGLE_MESH_BVH33, + PxConcreteType::eTRIANGLE_MESH_BVH34 + }; + +Gu::TriangleMesh::TriangleMesh(GuMeshFactory& factory, TriangleMeshData& d) +: PxTriangleMesh(PxType(gTable[d.mType]), PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mNbVertices (d.mNbVertices) +, mNbTriangles (d.mNbTriangles) +, mVertices (d.mVertices) +, mTriangles (d.mTriangles) +, mAABB (d.mAABB) +, mExtraTrigData (d.mExtraTrigData) +, mGeomEpsilon (d.mGeomEpsilon) +, mFlags (d.mFlags) +, mMaterialIndices (d.mMaterialIndices) +, mFaceRemap (d.mFaceRemap) +, mAdjacencies (d.mAdjacencies) + +, mMeshFactory (&factory) + +, mGRB_triIndices (d.mGRB_primIndices) + +, mGRB_triAdjacencies (d.mGRB_primAdjacencies) + +, mGRB_faceRemap (d.mGRB_faceRemap) +, mGRB_BV32Tree (d.mGRB_BV32Tree) +{ + // this constructor takes ownership of memory from the data object + d.mVertices = 0; + d.mTriangles = 0; + d.mExtraTrigData = 0; + d.mFaceRemap = 0; + d.mAdjacencies = 0; + d.mMaterialIndices = 0; + + d.mGRB_primIndices = 0; + + d.mGRB_primAdjacencies = 0; + d.mGRB_faceRemap = 0; + d.mGRB_BV32Tree = 0; + + // PT: 'getPaddedBounds()' is only safe if we make sure the bounds member is followed by at least 32bits of data + PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(Gu::TriangleMesh, mExtraTrigData)>=PX_OFFSET_OF(Gu::TriangleMesh, mAABB)+4); + +} + +Gu::TriangleMesh::~TriangleMesh() +{ + if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + { + PX_FREE_AND_RESET(mExtraTrigData); + PX_FREE_AND_RESET(mFaceRemap); + PX_FREE_AND_RESET(mAdjacencies); + PX_FREE_AND_RESET(mMaterialIndices); + PX_FREE_AND_RESET(mTriangles); + PX_FREE_AND_RESET(mVertices); + + PX_FREE_AND_RESET(mGRB_triIndices); + + PX_FREE_AND_RESET(mGRB_triAdjacencies); + PX_FREE_AND_RESET(mGRB_faceRemap); + + BV32Tree* bv32Tree = reinterpret_cast(mGRB_BV32Tree); + PX_DELETE_AND_RESET(bv32Tree); + + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: used to be automatic but making it manual saves bytes in the internal mesh + +void Gu::TriangleMesh::exportExtraData(PxSerializationContext& stream) +{ + //PX_DEFINE_DYNAMIC_ARRAY(TriangleMesh, mVertices, PxField::eVEC3, mNbVertices, Ps::PxFieldFlag::eSERIALIZE), + if(mVertices) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mVertices, mNbVertices * sizeof(PxVec3)); + } + + if(mTriangles) + { + const PxU32 triangleSize = mFlags & PxTriangleMeshFlag::e16_BIT_INDICES ? sizeof(PxU16) : sizeof(PxU32); + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mTriangles, mNbTriangles * 3 * triangleSize); + } + + //PX_DEFINE_DYNAMIC_ARRAY(TriangleMesh, mExtraTrigData, PxField::eBYTE, mNbTriangles, Ps::PxFieldFlag::eSERIALIZE), + if(mExtraTrigData) + { + // PT: it might not be needed to 16-byte align this array of PxU8.... + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mExtraTrigData, mNbTriangles * sizeof(PxU8)); + } + + if(mMaterialIndices) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mMaterialIndices, mNbTriangles * sizeof(PxU16)); + } + + if(mFaceRemap) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mFaceRemap, mNbTriangles * sizeof(PxU32)); + } + + if(mAdjacencies) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mAdjacencies, mNbTriangles * sizeof(PxU32) * 3); + } +} + +void Gu::TriangleMesh::importExtraData(PxDeserializationContext& context) +{ + // PT: vertices are followed by indices, so it will be safe to V4Load vertices from a deserialized binary file + if(mVertices) + mVertices = context.readExtraData(mNbVertices); + + if(mTriangles) + { + if(mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) + mTriangles = context.readExtraData(3*mNbTriangles); + else + mTriangles = context.readExtraData(3*mNbTriangles); + } + + if(mExtraTrigData) + mExtraTrigData = context.readExtraData(mNbTriangles); + + if(mMaterialIndices) + mMaterialIndices = context.readExtraData(mNbTriangles); + + if(mFaceRemap) + mFaceRemap = context.readExtraData(mNbTriangles); + + if(mAdjacencies) + mAdjacencies = context.readExtraData(3*mNbTriangles); + + mGRB_triIndices = NULL; + mGRB_triAdjacencies = NULL; + mGRB_faceRemap = NULL; + mGRB_BV32Tree = NULL; +} + +void Gu::TriangleMesh::onRefCountZero() +{ + if(mMeshFactory->removeTriangleMesh(*this)) + { + const PxType type = getConcreteType(); + GuMeshFactory* mf = mMeshFactory; + Cm::deletePxBase(this); + mf->notifyFactoryListener(this, type); + return; + } + + // PT: if we reach this point, we didn't find the mesh in the Physics object => don't delete! + // This prevents deleting the object twice. + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Gu::TriangleMesh::release: double deletion detected!"); +} +//~PX_SERIALIZATION + +void Gu::TriangleMesh::release() +{ + decRefCount(); +} + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +PxVec3* Gu::TriangleMesh::getVerticesForModification() +{ + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxTriangleMesh::getVerticesForModification() is only supported for meshes with PxMeshMidPhase::eBVH33."); + + return NULL; +} + +PxBounds3 Gu::TriangleMesh::refitBVH() +{ + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxTriangleMesh::refitBVH() is only supported for meshes with PxMeshMidPhase::eBVH33."); + + return PxBounds3(mAABB.getMin(), mAABB.getMax()); +} +#endif + +} // namespace physx diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h new file mode 100644 index 000000000..20bdb5c4b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h @@ -0,0 +1,284 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLEMESH_H +#define GU_TRIANGLEMESH_H + +#include "foundation/PxIO.h" +#include "PxSimpleTriangleMesh.h" +#include "PxTriangleMeshGeometry.h" +#include "CmScaling.h" +#include "GuTriangle32.h" +#include "CmRefCountable.h" +#include "PxTriangle.h" +#include "PxTriangleMesh.h" +#include "CmRenderOutput.h" +#include "GuMeshData.h" +#include "GuCenterExtents.h" + +namespace physx +{ + +class GuMeshFactory; +class PxMeshScale; + +namespace Gu +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +// Possible optimization: align the whole struct to cache line +class TriangleMesh : public PxTriangleMesh, public Ps::UserAllocated, public Cm::RefCountable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + +// PX_SERIALIZATION + TriangleMesh(PxBaseFlags baseFlags) : PxTriangleMesh(baseFlags), Cm::RefCountable(PxEmpty) {} + virtual void exportExtraData(PxSerializationContext& ctx); + void importExtraData(PxDeserializationContext&); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); + virtual void release(); + + void resolveReferences(PxDeserializationContext& ) {} + virtual void requiresObjects(PxProcessPxBaseCallback&){} +//~PX_SERIALIZATION + +// Cm::RefCountable + virtual void onRefCountZero(); +//~Cm::RefCountable + + TriangleMesh(GuMeshFactory& factory, TriangleMeshData& data); + virtual ~TriangleMesh(); + +// PxTriangleMesh + virtual PxU32 getNbVertices() const { return mNbVertices; } + virtual const PxVec3* getVertices() const { return mVertices; } + virtual const PxU32* getTrianglesRemap() const { return mFaceRemap; } + virtual PxU32 getNbTriangles() const { return mNbTriangles; } + virtual const void* getTriangles() const { return mTriangles; } + virtual PxTriangleMeshFlags getTriangleMeshFlags() const { return PxTriangleMeshFlags(mFlags); } + virtual PxMaterialTableIndex getTriangleMaterialIndex(PxTriangleID triangleIndex) const { + return hasPerTriangleMaterials() ? getMaterials()[triangleIndex] : PxMaterialTableIndex(0xffff); } + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + virtual PxVec3* getVerticesForModification(); + virtual PxBounds3 refitBVH(); +#endif + + virtual PxBounds3 getLocalBounds() const + { + PX_ASSERT(mAABB.isValid()); + return PxBounds3::centerExtents(mAABB.mCenter, mAABB.mExtents); + } + + virtual void acquireReference() { incRefCount(); } + virtual PxU32 getReferenceCount() const { return getRefCount(); } +//~PxTriangleMesh + // PT: this one is just to prevent instancing Gu::TriangleMesh. + // But you should use PxBase::getConcreteType() instead to avoid the virtual call. + virtual PxMeshMidPhase::Enum getMidphaseID() const = 0; + + PX_FORCE_INLINE const PxU32* getFaceRemap() const { return mFaceRemap; } + PX_FORCE_INLINE bool has16BitIndices() const { return (mFlags & PxMeshFlag::e16_BIT_INDICES) ? true : false; } + PX_FORCE_INLINE bool hasPerTriangleMaterials() const { return mMaterialIndices != NULL; } + PX_FORCE_INLINE PxU32 getNbVerticesFast() const { return mNbVertices; } + PX_FORCE_INLINE PxU32 getNbTrianglesFast() const { return mNbTriangles; } + PX_FORCE_INLINE const void* getTrianglesFast() const { return mTriangles; } + PX_FORCE_INLINE const PxVec3* getVerticesFast() const { return mVertices; } + PX_FORCE_INLINE const PxU32* getAdjacencies() const { return mAdjacencies; } + PX_FORCE_INLINE PxReal getGeomEpsilon() const { return mGeomEpsilon; } + PX_FORCE_INLINE const CenterExtents& getLocalBoundsFast() const { return mAABB; } + PX_FORCE_INLINE const PxU16* getMaterials() const { return mMaterialIndices; } + PX_FORCE_INLINE const PxU8* getExtraTrigData() const { return mExtraTrigData; } + + PX_FORCE_INLINE const CenterExtentsPadded& getPaddedBounds() const + { + // PT: see compile-time assert in cpp + return static_cast(mAABB); + } + + PX_FORCE_INLINE void computeWorldTriangle( + PxTriangle& worldTri, PxTriangleID triangleIndex, const Cm::Matrix34& worldMatrix, bool flipNormal = false, + PxU32* PX_RESTRICT vertexIndices=NULL, PxU32* PX_RESTRICT adjacencyIndices=NULL) const; + PX_FORCE_INLINE void getLocalTriangle(PxTriangle& localTri, PxTriangleID triangleIndex, bool flipNormal = false) const; + + void setMeshFactory(GuMeshFactory* factory) { mMeshFactory = factory; } + +protected: + PxU32 mNbVertices; + PxU32 mNbTriangles; + PxVec3* mVertices; + void* mTriangles; //!< 16 (<= 0xffff #vertices) or 32 bit trig indices (mNbTriangles * 3) + // 16 bytes block + + // PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading + CenterExtents mAABB; + PxU8* mExtraTrigData; //one per trig + PxReal mGeomEpsilon; //!< see comments in cooking code referencing this variable + // 16 bytes block + /* + low 3 bits (mask: 7) are the edge flags: + b001 = 1 = ignore edge 0 = edge v0-->v1 + b010 = 2 = ignore edge 1 = edge v0-->v2 + b100 = 4 = ignore edge 2 = edge v1-->v2 + */ + PxU8 mFlags; //!< Flag whether indices are 16 or 32 bits wide + //!< Flag whether triangle adajacencies are build + PxU16* mMaterialIndices; //!< the size of the array is numTriangles. + PxU32* mFaceRemap; //!< new faces to old faces mapping (after cleaning, etc). Usage: old = faceRemap[new] + PxU32* mAdjacencies; //!< Adjacency information for each face - 3 adjacent faces + //!< Set to 0xFFFFffff if no adjacent face + + GuMeshFactory* mMeshFactory; // PT: changed to pointer for serialization +public: + + // GRB data ------------------------- + void * mGRB_triIndices; //!< GRB: GPU-friendly tri indices [uint4] + + // TODO avoroshilov: cooking - adjacency info - duplicated, remove it and use 'mAdjacencies' and 'mExtraTrigData' see GuTriangleMesh.cpp:325 + void * mGRB_triAdjacencies; //!< GRB: adjacency data, with BOUNDARY and NONCONVEX flags (flags replace adj indices where applicable) + + PxU32* mGRB_faceRemap; //!< GRB : gpu to cpu triangle indice remap + void* mGRB_BV32Tree; //!< GRB: BV32 tree + // End of GRB data ------------------ + +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Gu + +PX_FORCE_INLINE void Gu::TriangleMesh::computeWorldTriangle(PxTriangle& worldTri, PxTriangleID triangleIndex, const Cm::Matrix34& worldMatrix, bool flipNormal, + PxU32* PX_RESTRICT vertexIndices, PxU32* PX_RESTRICT adjacencyIndices) const +{ + PxU32 vref0, vref1, vref2; + if(has16BitIndices()) + { + const Gu::TriangleT& T = (reinterpret_cast*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + else + { + const Gu::TriangleT& T = (reinterpret_cast*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + if (flipNormal) + Ps::swap(vref1, vref2); + const PxVec3* PX_RESTRICT vertices = getVerticesFast(); + worldTri.verts[0] = worldMatrix.transform(vertices[vref0]); + worldTri.verts[1] = worldMatrix.transform(vertices[vref1]); + worldTri.verts[2] = worldMatrix.transform(vertices[vref2]); + + if(vertexIndices) + { + vertexIndices[0] = vref0; + vertexIndices[1] = vref1; + vertexIndices[2] = vref2; + } + + if(adjacencyIndices) + { + if(getAdjacencies()) + { + adjacencyIndices[0] = flipNormal ? getAdjacencies()[triangleIndex*3 + 2] : getAdjacencies()[triangleIndex*3 + 0]; + adjacencyIndices[1] = getAdjacencies()[triangleIndex*3 + 1]; + adjacencyIndices[2] = flipNormal ? getAdjacencies()[triangleIndex*3 + 0] : getAdjacencies()[triangleIndex*3 + 2]; + } + else + { + adjacencyIndices[0] = 0xffffffff; + adjacencyIndices[1] = 0xffffffff; + adjacencyIndices[2] = 0xffffffff; + } + } +} + +PX_FORCE_INLINE void Gu::TriangleMesh::getLocalTriangle(PxTriangle& localTri, PxTriangleID triangleIndex, bool flipNormal) const +{ + PxU32 vref0, vref1, vref2; + if(has16BitIndices()) + { + const Gu::TriangleT& T = (reinterpret_cast*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + else + { + const Gu::TriangleT& T = (reinterpret_cast*>(getTrianglesFast()))[triangleIndex]; + vref0 = T.v[0]; + vref1 = T.v[1]; + vref2 = T.v[2]; + } + if (flipNormal) + Ps::swap(vref1, vref2); + const PxVec3* PX_RESTRICT vertices = getVerticesFast(); + localTri.verts[0] = vertices[vref0]; + localTri.verts[1] = vertices[vref1]; + localTri.verts[2] = vertices[vref2]; +} + +PX_INLINE float computeSweepData(const PxTriangleMeshGeometry& triMeshGeom, /*const Cm::FastVertex2ShapeScaling& scaling,*/ PxVec3& sweepOrigin, PxVec3& sweepExtents, PxVec3& sweepDir, float distance) +{ + PX_ASSERT(!Cm::isEmpty(sweepOrigin, sweepExtents)); + + const PxVec3 endPt = sweepOrigin + sweepDir*distance; + PX_ASSERT(!Cm::isEmpty(endPt, sweepExtents)); + + const Cm::FastVertex2ShapeScaling meshScaling(triMeshGeom.scale.getInverse()); // shape to vertex transform + + const PxMat33& vertex2ShapeSkew = meshScaling.getVertex2ShapeSkew(); + + const PxVec3 originBoundsCenter = vertex2ShapeSkew * sweepOrigin; + const PxVec3 originBoundsExtents = Cm::basisExtent(vertex2ShapeSkew.column0, vertex2ShapeSkew.column1, vertex2ShapeSkew.column2, sweepExtents); + + sweepOrigin = originBoundsCenter; + sweepExtents = originBoundsExtents; + sweepDir = (vertex2ShapeSkew * endPt) - originBoundsCenter; + return sweepDir.normalizeSafe(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp new file mode 100644 index 000000000..71945ef5e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" + +using namespace physx; + +namespace physx +{ + +Gu::BV4TriangleMesh::BV4TriangleMesh(GuMeshFactory& factory, TriangleMeshData& d) +: TriangleMesh(factory, d) +{ + PX_ASSERT(d.mType==PxMeshMidPhase::eBVH34); + + BV4TriangleData& bv4Data = static_cast(d); + mMeshInterface = bv4Data.mMeshInterface; + mBV4Tree = bv4Data.mBV4Tree; + mBV4Tree.mMeshInterface = &mMeshInterface; +} + +Gu::TriangleMesh* Gu::BV4TriangleMesh::createObject(PxU8*& address, PxDeserializationContext& context) +{ + BV4TriangleMesh* obj = new (address) BV4TriangleMesh(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(BV4TriangleMesh); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void Gu::BV4TriangleMesh::exportExtraData(PxSerializationContext& stream) +{ + mBV4Tree.exportExtraData(stream); + TriangleMesh::exportExtraData(stream); +} + +void Gu::BV4TriangleMesh::importExtraData(PxDeserializationContext& context) +{ + mBV4Tree.importExtraData(context); + TriangleMesh::importExtraData(context); + + if(has16BitIndices()) + mMeshInterface.setPointers(NULL, const_cast(reinterpret_cast(getTrianglesFast())), getVerticesFast()); + else + mMeshInterface.setPointers(const_cast(reinterpret_cast(getTrianglesFast())), NULL, getVerticesFast()); + mBV4Tree.mMeshInterface = &mMeshInterface; +} + +} // namespace physx diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h new file mode 100644 index 000000000..558e2debd --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLEMESH_BV4_H +#define GU_TRIANGLEMESH_BV4_H + +#include "GuTriangleMesh.h" + +namespace physx +{ +class GuMeshFactory; + +namespace Gu +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +class BV4TriangleMesh : public TriangleMesh +{ + public: + virtual const char* getConcreteTypeName() const { return "PxBVH34TriangleMesh"; } +// PX_SERIALIZATION + BV4TriangleMesh(PxBaseFlags baseFlags) : TriangleMesh(baseFlags), mMeshInterface(PxEmpty), mBV4Tree(PxEmpty) {} + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& ctx); + void importExtraData(PxDeserializationContext&); + PX_PHYSX_COMMON_API static TriangleMesh* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + BV4TriangleMesh(GuMeshFactory& factory, TriangleMeshData& data); + virtual ~BV4TriangleMesh(){} + + virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH34; } + PX_FORCE_INLINE const Gu::BV4Tree& getBV4Tree() const { return mBV4Tree; } + private: + Gu::SourceMesh mMeshInterface; + Gu::BV4Tree mBV4Tree; +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp new file mode 100644 index 000000000..3c2710c4c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp @@ -0,0 +1,151 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuTriangleMesh.h" +#include "GuTriangleMeshRTree.h" +#if PX_ENABLE_DYNAMIC_MESH_RTREE +#include "GuConvexEdgeFlags.h" +#endif + +using namespace physx; + +namespace physx +{ + +Gu::RTreeTriangleMesh::RTreeTriangleMesh(GuMeshFactory& factory, TriangleMeshData& d) +: TriangleMesh(factory, d) +{ + PX_ASSERT(d.mType==PxMeshMidPhase::eBVH33); + + RTreeTriangleData& rtreeData = static_cast(d); + mRTree = rtreeData.mRTree; + rtreeData.mRTree.mPages = NULL; +} + +Gu::TriangleMesh* Gu::RTreeTriangleMesh::createObject(PxU8*& address, PxDeserializationContext& context) +{ + RTreeTriangleMesh* obj = new (address) RTreeTriangleMesh(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(RTreeTriangleMesh); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void Gu::RTreeTriangleMesh::exportExtraData(PxSerializationContext& stream) +{ + mRTree.exportExtraData(stream); + TriangleMesh::exportExtraData(stream); +} + +void Gu::RTreeTriangleMesh::importExtraData(PxDeserializationContext& context) +{ + mRTree.importExtraData(context); + TriangleMesh::importExtraData(context); +} + +#if PX_ENABLE_DYNAMIC_MESH_RTREE +PxVec3 * Gu::RTreeTriangleMesh::getVerticesForModification() +{ + return const_cast(getVertices()); +} + +template +struct RefitCallback : Gu::RTree::CallbackRefit +{ + const PxVec3* newPositions; + const IndexType* indices; + + RefitCallback(const PxVec3* aNewPositions, const IndexType* aIndices) : newPositions(aNewPositions), indices(aIndices) {} + PX_FORCE_INLINE ~RefitCallback() {} + + virtual void recomputeBounds(PxU32 index, shdfnd::aos::Vec3V& aMn, shdfnd::aos::Vec3V& aMx) + { + using namespace shdfnd::aos; + + // Each leaf box has a set of triangles + Gu::LeafTriangles currentLeaf; currentLeaf.Data = index; + PxU32 nbTris = currentLeaf.GetNbTriangles(); + PxU32 baseTri = currentLeaf.GetTriangleIndex(); + PX_ASSERT(nbTris > 0); + const IndexType* vInds = indices + 3 * baseTri; + Vec3V vPos = V3LoadU(newPositions[vInds[0]]); + Vec3V mn = vPos, mx = vPos; + //PxBounds3 result(newPositions[vInds[0]], newPositions[vInds[0]]); + vPos = V3LoadU(newPositions[vInds[1]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + vPos = V3LoadU(newPositions[vInds[2]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + for (PxU32 i = 1; i < nbTris; i++) + { + const IndexType* vInds1 = indices + 3 * (baseTri + i); + vPos = V3LoadU(newPositions[vInds1[0]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + vPos = V3LoadU(newPositions[vInds1[1]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + vPos = V3LoadU(newPositions[vInds1[2]]); + mn = V3Min(mn, vPos); mx = V3Max(mx, vPos); + } + + aMn = mn; + aMx = mx; + } +}; + +PxBounds3 Gu::RTreeTriangleMesh::refitBVH() +{ + PxBounds3 meshBounds; + if (has16BitIndices()) + { + RefitCallback cb(mVertices, static_cast(mTriangles)); + mRTree.refitAllStaticTree(cb, &meshBounds); + } + else + { + RefitCallback cb(mVertices, static_cast(mTriangles)); + mRTree.refitAllStaticTree(cb, &meshBounds); + } + + // reset edge flags and remember we did that using a mesh flag (optimization) + if ((mRTree.mFlags & RTree::IS_EDGE_SET) == 0) + { + mRTree.mFlags |= RTree::IS_EDGE_SET; + if(mExtraTrigData) + { + const PxU32 nbTris = getNbTriangles(); + for(PxU32 i = 0; i < nbTris; i++) + mExtraTrigData[i] |= ETD_CONVEX_EDGE_ALL; + } + } + + mAABB = meshBounds; + return meshBounds; +} +#endif + +} // namespace physx diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h new file mode 100644 index 000000000..168281546 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h @@ -0,0 +1,81 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLEMESH_RTREE_H +#define GU_TRIANGLEMESH_RTREE_H + +#include "GuTriangleMesh.h" + +namespace physx +{ +class GuMeshFactory; + +namespace Gu +{ + +#if PX_VC +#pragma warning(push) +#pragma warning(disable: 4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +class RTreeTriangleMesh : public TriangleMesh +{ + public: + virtual const char* getConcreteTypeName() const { return "PxBVH33TriangleMesh"; } +// PX_SERIALIZATION + RTreeTriangleMesh(PxBaseFlags baseFlags) : TriangleMesh(baseFlags), mRTree(PxEmpty) {} + PX_PHYSX_COMMON_API virtual void exportExtraData(PxSerializationContext& ctx); + void importExtraData(PxDeserializationContext&); + PX_PHYSX_COMMON_API static TriangleMesh* createObject(PxU8*& address, PxDeserializationContext& context); + PX_PHYSX_COMMON_API static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + RTreeTriangleMesh(GuMeshFactory& factory, TriangleMeshData& data); + virtual ~RTreeTriangleMesh(){} + + virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH33; } + +#if PX_ENABLE_DYNAMIC_MESH_RTREE + virtual PxVec3* getVerticesForModification(); + virtual PxBounds3 refitBVH(); +#endif + + PX_FORCE_INLINE const Gu::RTree& getRTree() const { return mRTree; } + private: + Gu::RTree mRTree; +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h new file mode 100644 index 000000000..e32534bff --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h @@ -0,0 +1,65 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_TRIANGLE_VERTEX_POINTERS_H +#define GU_TRIANGLE_VERTEX_POINTERS_H + +#include "PxTriangleMesh.h" +#include "GuTriangleMesh.h" + +namespace physx { + namespace Gu { + + // PT: TODO: replace with Gu::TriangleMesh::getLocalTriangle(...) + struct TriangleVertexPointers + { + static void PX_FORCE_INLINE getTriangleVerts(const TriangleMesh* mesh, PxU32 triangleIndex, PxVec3& v0, PxVec3& v1, PxVec3& v2) + { + const PxVec3* verts = mesh->getVerticesFast(); + if(mesh->has16BitIndices()) + { + const PxU16* tris = reinterpret_cast(mesh->getTrianglesFast()); + const PxU16* inds = tris+triangleIndex*3; + v0 = verts[inds[0]]; + v1 = verts[inds[1]]; + v2 = verts[inds[2]]; + } + else + { + const PxU32* tris = reinterpret_cast(mesh->getTrianglesFast()); + const PxU32* inds = tris+triangleIndex*3; + v0 = verts[inds[0]]; + v1 = verts[inds[1]]; + v2 = verts[inds[2]]; + } + } + }; +} } // physx, Gu + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp new file mode 100644 index 000000000..59a8faa08 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp @@ -0,0 +1,1030 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecBox.h" +#include "GuGeometryUnion.h" + +#include "GuConvexHelper.h" +#include "GuPCMShapeConvex.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGenUtil.h" + + +namespace physx +{ + +namespace Gu +{ + + static void getIncidentPolygon(Ps::aos::Vec3V* pts, Ps::aos::Vec3V& faceNormal, const Ps::aos::Vec3VArg axis, const Ps::aos::PsMatTransformV& transf1To0, + const Ps::aos::Vec3VArg extents) + { + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + FloatV ex = V3GetX(extents); + FloatV ey = V3GetY(extents); + FloatV ez = V3GetZ(extents); + + const Vec3V u0 = transf1To0.getCol0(); + const Vec3V u1 = transf1To0.getCol1(); + const Vec3V u2 = transf1To0.getCol2(); + + //calcaulte the insident face for b + const FloatV d0 = V3Dot(u0, axis); + const FloatV d1 = V3Dot(u1, axis); + const FloatV d2 = V3Dot(u2, axis); + + const FloatV absd0 = FAbs(d0); + const FloatV absd1 = FAbs(d1); + const FloatV absd2 = FAbs(d2); + + Vec3V r0, r1, r2; + + + if(FAllGrtrOrEq(absd0, absd1) && FAllGrtrOrEq(absd0, absd2)) + { + //the incident face is on u0 + const BoolV con = FIsGrtr(d0, zero); + faceNormal = V3Sel(con, V3Neg(u0), u0); + ex = FSel(con, FNeg(ex), ex); + r0 = V3Scale(u0, ex); + r1 = V3Scale(u1, ey); + r2 = V3Scale(u2, ez); + + const Vec3V temp0 = V3Add(transf1To0.p, r0); + const Vec3V temp1 = V3Add(r1, r2); + const Vec3V temp2 = V3Sub(r1, r2); + + + pts[0] = V3Add(temp0, temp1); // (-x/x, y, z) + pts[1] = V3Add(temp0, temp2); // (-x/x, y, -z) + pts[2] = V3Sub(temp0, temp1); // (-x/x, -y, -z) + pts[3] = V3Sub(temp0, temp2); // (-x/x, -y, z) + + } + else if(FAllGrtrOrEq(absd1, absd2)) + { + //the incident face is on u1 + const BoolV con = FIsGrtr(d1, zero); + faceNormal = V3Sel(con, V3Neg(u1), u1); + ey = FSel(con, FNeg(ey), ey); + r0 = V3Scale(u0, ex); + r1 = V3Scale(u1, ey); + r2 = V3Scale(u2, ez); + + const Vec3V temp0 = V3Add(transf1To0.p, r1); + const Vec3V temp1 = V3Add(r0, r2); + const Vec3V temp2 = V3Sub(r0, r2); + + pts[0] = V3Add(temp0, temp1); // (x, -y/y, z) + pts[1] = V3Add(temp0, temp2); // (x, -y/y, -z) + pts[2] = V3Sub(temp0, temp1); // (-x, -y/y, -z) + pts[3] = V3Sub(temp0, temp2); // (-x, -y/y, z) + + } + else + { + //the incident face is on u2 + const BoolV con = FIsGrtr(d2, zero); + faceNormal = V3Sel(con, V3Neg(u2), u2); + ez = FSel(con, FNeg(ez), ez); + r0 = V3Scale(u0, ex); + r1 = V3Scale(u1, ey); + r2 = V3Scale(u2, ez); + + const Vec3V temp0 = V3Add(transf1To0.p, r2); + const Vec3V temp1 = V3Add(r0, r1); + const Vec3V temp2 = V3Sub(r0, r1); + + pts[0] = V3Add(temp0, temp1); // ( x, y, z) + pts[1] = V3Add(temp0, temp2); // ( x, -y, z) + pts[2] = V3Sub(temp0, temp1); // (-x, -y, z) + pts[3] = V3Sub(temp0, temp2); // (-x, y, z) + } + } + + + //p0 and p1 is in the local space of AABB + static bool intersectSegmentAABB(const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg d, const Ps::aos::Vec3VArg max, const Ps::aos::Vec3VArg min, Ps::aos::FloatV& tmin, Ps::aos::FloatV& tmax) + { + using namespace Ps::aos; + + const Vec3V eps = V3Load(1e-6f); + const Vec3V absV = V3Abs(d); + const FloatV one = FOne(); + const Vec3V zero = V3Zero(); + const Vec3V fMax = Vec3V_From_FloatV(FMax()); + + FloatV tminf = FZero(); + FloatV tmaxf = one; + const BoolV isParallel = V3IsGrtr(eps, absV); + const BoolV isOutsideOfRange = BOr(V3IsGrtr(p0, max), V3IsGrtr(min, p0)); + //const BoolV isParallelAndOutOfRange = BAnd(isParallel, isOutsideOfRange); + + if(!BAllEqFFFF(BAnd(isParallel, isOutsideOfRange))) + { + return false; + } + + const Vec3V odd = V3RecipFast(d); + const Vec3V t1 = V3Sel(isParallel, zero, V3Mul(V3Sub(min, p0), odd)); + const Vec3V t2 = V3Sel(isParallel, fMax, V3Mul(V3Sub(max, p0), odd)); + + const Vec3V tt1 = V3Min(t1, t2); + const Vec3V tt2 = V3Max(t1, t2); + + const FloatV ft1 = V3ExtractMax(tt1); + const FloatV ft2 = V3ExtractMin(tt2); + + tminf = FMax(ft1, tminf); + tmaxf = FMin(tmaxf, ft2); + + tmin = tminf; + tmax = tmaxf; + + const BoolV con0 = FIsGrtr(tminf, tmaxf); + const BoolV con1 = FIsGrtr(tminf, one); + const BoolV isNotIntersect = BOr(con0, con1); + return BAllEqFFFF(isNotIntersect) == 1; + } + + + //pts, faceNormal and contact normal are in the local space of new space + static void calculateContacts( const Ps::aos::FloatVArg extentX, const Ps::aos::FloatVArg extentY, Ps::aos::Vec3V* pts, const Ps::aos::Vec3VArg incidentFaceNormalInNew, const Ps::aos::Vec3VArg localNormal, Gu::PersistentContact* manifoldContacts, PxU32& numContacts, const Ps::aos::FloatVArg contactDist) + { + using namespace Ps::aos; + + const FloatV zero = FZero(); + const FloatV max = FMax(); + + const FloatV nExtentX = FNeg(extentX); + const FloatV nExtentY = FNeg(extentY); + + bool pPenetration[4]; + bool pArea[4]; + + Vec3V bmin = V3Splat(max); + Vec3V bmax = V3Neg(bmin); + + const Vec3V bound = V3Merge(extentX, extentY, max); + + + //get the projection point of pts + for(PxU32 i=0; i< 4; ++i) + { + bmin = V3Min(bmin, pts[i]); + bmax = V3Max(bmax, pts[i]); + const FloatV z = FNeg(V3GetZ(pts[i])); + if(FAllGrtr(contactDist, z)) + { + + pPenetration[i] = true; + + const Vec3V absPt= V3Abs(pts[i]); + const BoolV con = V3IsGrtrOrEq(bound, absPt); + if(BAllEqTTTT(con)) + { + pArea[i] = true; + + //Add the point to the manifold + manifoldContacts[numContacts].mLocalPointA = V3SetZ(pts[i], zero); //transformNewTo0.transform(localPointA); + manifoldContacts[numContacts].mLocalPointB = pts[i];//transform1ToNew.transformInv(pts[i]); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), z); + } + else + { + pArea[i] = false; + } + } + else + { + pPenetration[i] = false; + pArea[i] = false; + } + + } + + if(numContacts == 4) + return; + + //if(pPenetration[0] && pPenetration[1] && pPenetration[2] && pPenetration[3]) + { + //if(!pArea[0] || !pArea[1] || !pArea[2] || !pArea[3]) + { + const FloatV denom = V3GetZ(incidentFaceNormalInNew); + { + const Vec3V q0 = V3Merge(extentX, extentY, zero); + + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + + { + const Vec3V q0 = V3Merge(extentX, nExtentY, zero); + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + + { + const Vec3V q0 = V3Merge( nExtentX, extentY, zero); + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + + { + const Vec3V q0 = V3Merge(nExtentX, nExtentY, zero); + + if(contains(pts, 4, q0, bmin, bmax)) + { + const FloatV nom = V3Dot(incidentFaceNormalInNew, V3Sub(pts[0], q0)); + const FloatV t = FDiv(nom, denom); + const FloatV pen = FNeg(t); + if(FAllGrtr(contactDist, pen)) + { + manifoldContacts[numContacts].mLocalPointA = q0; + manifoldContacts[numContacts].mLocalPointB = V3SetZ(q0, t); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + } + } + } + } + + + + const Vec3V ext = V3Merge(extentX, extentY, max); + const Vec3V negExt = V3Merge(nExtentX, nExtentY, FNeg(FAdd(contactDist, FEps()))); + + for (PxU32 rStart = 0, rEnd = 3; rStart < 4; rEnd = rStart++) + { + const Vec3V p0 = pts[rStart]; + const Vec3V p1 = pts[rEnd]; + + if(!pPenetration[rStart] && !pPenetration[rEnd]) + continue; + + const bool con0 = pPenetration[rStart] && pArea[rStart]; + const bool con1 = pPenetration[rEnd] && pArea[rEnd]; + if(con0 && con1) + continue; + + //intersect t value with x plane + const Vec3V p0p1 = V3Sub(p1, p0); + + FloatV tmin, tmax; + if(Gu::intersectSegmentAABB(p0, p0p1, ext, negExt, tmin, tmax)) + { + if(!con0) + { + const Vec3V intersectP = V3ScaleAdd(p0p1, tmin, p0); + manifoldContacts[numContacts].mLocalPointA = V3SetZ(intersectP, zero); + manifoldContacts[numContacts].mLocalPointB = intersectP; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), FNeg(V3GetZ(intersectP))); + } + if(!con1) + { + const Vec3V intersectP = V3ScaleAdd(p0p1, tmax, p0); + manifoldContacts[numContacts].mLocalPointA = V3SetZ(intersectP, zero); + manifoldContacts[numContacts].mLocalPointB = intersectP; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), FNeg(V3GetZ(intersectP))); + } + } + } +} + + +static PxU32 doBoxBoxGenerateContacts(const Ps::aos::Vec3VArg box0Extent, const Ps::aos::Vec3VArg box1Extent, const Ps::aos::PsMatTransformV& transform0, const Ps::aos::PsMatTransformV& transform1, const Ps::aos::FloatVArg contactDist, Gu::PersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + + const FloatV ea0 = V3GetX(box0Extent); + const FloatV ea1 = V3GetY(box0Extent); + const FloatV ea2 = V3GetZ(box0Extent); + + const FloatV eb0 = V3GetX(box1Extent); + const FloatV eb1 = V3GetY(box1Extent); + const FloatV eb2 = V3GetZ(box1Extent); + + + const PsMatTransformV transform1To0 = transform0.transformInv(transform1); + const Mat33V rot0To1 =M33Trnsps(transform1To0.rot); + + const Vec3V uEps = V3Load(1e-6f); + + const FloatV zero = FZero(); + + const FloatV tx = V3GetX(transform1To0.p); + const FloatV ty = V3GetY(transform1To0.p); + const FloatV tz = V3GetZ(transform1To0.p); + const Vec3V col0 = transform1To0.getCol0(); + const Vec3V col1 = transform1To0.getCol1(); + const Vec3V col2 = transform1To0.getCol2(); + + const Vec3V abs1To0Col0 = V3Add(V3Abs(col0), uEps); + const Vec3V abs1To0Col1 = V3Add(V3Abs(col1), uEps); + const Vec3V abs1To0Col2 = V3Add(V3Abs(col2), uEps); + + const Vec3V abs0To1Col0 = V3Add(V3Abs(rot0To1.col0), uEps); + const Vec3V abs0To1Col1 = V3Add(V3Abs(rot0To1.col1), uEps); + const Vec3V abs0To1Col2 = V3Add(V3Abs(rot0To1.col2), uEps); + + + FloatV sign[6]; + FloatV overlap[6]; + + FloatV ra, rb, radiusSum; + //ua0 + { + + sign[0] = tx; + + const Vec3V vtemp3 = V3Mul(abs0To1Col0, box1Extent); + rb = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ea0, rb); + overlap[0] = FAdd(FSub(radiusSum, FAbs(sign[0])), contactDist); + if(FAllGrtr(zero, overlap[0])) + return false; + } + + //ua1 + { + sign[1] = ty; + + const Vec3V vtemp3 = V3Mul(abs0To1Col1, box1Extent); + rb = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ea1, rb); + overlap[1] = FAdd(FSub(radiusSum, FAbs(sign[1])), contactDist); + if(FAllGrtr(zero, overlap[1])) + return false; + + } + + + //ua2 + { + sign[2] = tz; + ra = ea2; + + const Vec3V vtemp3 = V3Mul(abs0To1Col2, box1Extent); + rb = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ea2, rb); + overlap[2] = FAdd(FSub(radiusSum, FAbs(sign[2])), contactDist); + if(FAllGrtr(zero, overlap[2])) + return false; + + } + + //ub0 + { + sign[3] = V3Dot(transform1To0.p, col0); + + const Vec3V vtemp3 = V3Mul(abs1To0Col0, box0Extent); + ra = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ra, eb0); + overlap[3] = FAdd(FSub(radiusSum, FAbs(sign[3])), contactDist); + if(FAllGrtr(zero, overlap[3])) + return false; + + } + + //ub1 + { + sign[4] = V3Dot(transform1To0.p, col1); + + const Vec3V vtemp3 = V3Mul(abs1To0Col1, box0Extent); + ra = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ra, eb1); + overlap[4] = FAdd(FSub(radiusSum, FAbs(sign[4])), contactDist); + if(FAllGrtr(zero, overlap[4])) + return false; + } + + //ub2 + { + sign[5] = V3Dot(transform1To0.p, col2); + + const Vec3V vtemp3 = V3Mul(abs1To0Col2, box0Extent); + ra = FAdd(V3GetX(vtemp3), FAdd(V3GetY(vtemp3), V3GetZ(vtemp3))); + + radiusSum = FAdd(ra, eb2); + overlap[5] = FAdd(FSub(radiusSum, FAbs(sign[5])), contactDist); + if(FAllGrtr(zero, overlap[5])) + return false; + } + + + //ua0 X ub0 + { + //B into A's space, ua0Xub0[0,-b3, b2] + const FloatV absSign = FAbs(FSub(FMul(V3GetY(col0), tz), FMul(V3GetZ(col0), ty))); + + //B into A's space, ua0Xub0[0,-b3, b2] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col0), ea1); + const FloatV vtemp1 = FMul(V3GetY(abs1To0Col0), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub0[0, a3, -a2] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col0), eb1); + const FloatV vtemp02 = FMul(V3GetY(abs0To1Col0), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum)) return false; + } + + //ua0 X ub1 + { + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV absSign = FAbs(FSub(FMul(V3GetY(col1), tz), FMul(V3GetZ(col1), ty))); + + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col1), ea1); + const FloatV vtemp1 = FMul(V3GetY(abs1To0Col1), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[-a3, 0, a1] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col0), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col0), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + + if(FAllGrtr(absSign, radiusSum)) return false; + + } + + //ua0 X ub2 + { + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV absSign = FAbs(FSub(FMul(V3GetY(col2), tz), FMul(V3GetZ(col2), ty))); + + + //B into A's space, ua0Xub0[0, -b3, b2] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col2), ea1); + const FloatV vtemp1 = FMul(V3GetY(abs1To0Col2), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[a2, -a1, 0] + const FloatV vtemp01 = FMul(V3GetY(abs0To1Col0), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col0), eb1); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum)) return false; + + } + + //ua1 X ub0 + { + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV absSign = FAbs(FSub(FMul(V3GetZ(col0), tx), FMul(V3GetX(col0), tz))); + + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col0), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col0), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[0, a3, -a2] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col1), eb1); + const FloatV vtemp02 = FMul(V3GetY(abs0To1Col1), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + + } + + //ua1 X ub1 + { + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV absSign = FAbs(FSub(FMul(V3GetZ(col1), tx), FMul(V3GetX(col1), tz))); + + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col1), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col1), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[-a3, 0, -a1] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col1), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col1), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua1 X ub2 + { + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV absSign=FAbs(FSub(FMul(V3GetZ(col2), tx), FMul(V3GetX(col2), tz))); + + //B into A's space, ua0Xub0[b3, 0, -b1] + const FloatV vtemp0 = FMul(V3GetZ(abs1To0Col2), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col2), ea2); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[a2, -a1, 0] + const FloatV vtemp01 = FMul(V3GetY(abs0To1Col1), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col1), eb1); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua2 X ub0 + { + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV absSign = FAbs(FSub(FMul(V3GetX(col0), ty), FMul(V3GetY(col0), tx))); + + + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV vtemp0 = FMul(V3GetY(abs1To0Col0), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col0), ea1); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[0, a3, -a2] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col2), eb1); + const FloatV vtemp02 = FMul(V3GetY(abs0To1Col2), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua2 X ub1 + { + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV absSign = FAbs(FSub(FMul(V3GetX(col1), ty), FMul(V3GetY(col1), tx))); + + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV vtemp0 = FMul(V3GetY(abs1To0Col1), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col1), ea1); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[-a3, 0, a1] + const FloatV vtemp01 = FMul(V3GetZ(abs0To1Col2), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col2), eb2); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + //ua2 X ub2 + { + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV absSign=FAbs(FSub(FMul(V3GetX(col2), ty), FMul(V3GetY(col2), tx))); + + //B into A's space, ua2Xub0[-b2, b1, 0] + const FloatV vtemp0 = FMul(V3GetY(abs1To0Col2), ea0); + const FloatV vtemp1 = FMul(V3GetX(abs1To0Col2), ea1); + ra = FAdd(vtemp0, vtemp1); + + //A into B's space, ua0Xub1[a2, -a1, 0] + const FloatV vtemp01 = FMul(V3GetY(abs0To1Col2), eb0); + const FloatV vtemp02 = FMul(V3GetX(abs0To1Col2), eb1); + rb = FAdd(vtemp01, vtemp02); + + radiusSum = FAdd(FAdd(ra, rb), contactDist); + if(FAllGrtr(absSign, radiusSum))return false; + } + + Vec3V mtd; + + PxU32 feature = 0; + FloatV minOverlap = overlap[0]; + + for(PxU32 i=1; i<6; ++i) + { + if(FAllGrtr(minOverlap, overlap[i])) + { + minOverlap = overlap[i]; + feature = i; + } + } + + + PsMatTransformV newTransformV; + const Vec3V axis00 = transform0.getCol0(); + const Vec3V axis01 = transform0.getCol1(); + const Vec3V axis02 = transform0.getCol2(); + const Vec3V axis10 = transform1.getCol0(); + const Vec3V axis11 = transform1.getCol1(); + const Vec3V axis12 = transform1.getCol2(); + + Vec3V incidentFaceNormalInNew; + Vec3V pts[4]; + bool flip = false; + switch(feature) + { + case 0: //ua0 + { + + + if(FAllGrtrOrEq(zero, sign[0])) + { + mtd = axis00; + newTransformV.rot.col0 = V3Neg(axis02); + newTransformV.rot.col1 = axis01; + newTransformV.rot.col2 = axis00; + newTransformV.p = V3NegScaleSub(axis00, ea0, transform0.p); + } + else + { + + const Vec3V nAxis00 = V3Neg(axis00); + mtd = nAxis00; + newTransformV.rot.col0 = axis02; + newTransformV.rot.col1 = axis01; + newTransformV.rot.col2 = nAxis00; + newTransformV.p = V3ScaleAdd(axis00, ea0, transform0.p); + + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform1); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, V3Neg(localNormal), transform1ToNew, box1Extent); + + calculateContacts(ea2, ea1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + + break; + }; + case 1: //ua1 + { + + if(FAllGrtrOrEq(zero, sign[1])) + { + mtd = axis01; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = V3Neg(axis02); + newTransformV.rot.col2 = axis01; + newTransformV.p = V3NegScaleSub(axis01, ea1, transform0.p); + + } + else + { + + + const Vec3V nAxis01 = V3Neg(axis01); + mtd = nAxis01; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = axis02; + newTransformV.rot.col2 = nAxis01; + newTransformV.p = V3ScaleAdd(axis01, ea1, transform0.p); + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform1); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, V3Neg(localNormal), transform1ToNew, box1Extent); + + calculateContacts(ea0, ea2, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + break; + + }; + case 2: //ua2 + { + + if(FAllGrtrOrEq(zero, sign[2])) + { + mtd = axis02; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = axis01; + newTransformV.rot.col2 = axis02; + + newTransformV.p = V3NegScaleSub(axis02, ea2, transform0.p); + } + else + { + const Vec3V nAxis02 = V3Neg(axis02); + mtd = nAxis02; + newTransformV.rot.col0 = axis00; + newTransformV.rot.col1 = V3Neg(axis01); + newTransformV.rot.col2 = nAxis02; + newTransformV.p = V3ScaleAdd(axis02, ea2, transform0.p); + + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform1); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, V3Neg(localNormal), transform1ToNew, box1Extent); + + calculateContacts(ea0, ea1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + + break; + }; + case 3: //ub0 + { + + flip = true; + if(FAllGrtrOrEq(zero, sign[3])) + { + mtd = axis10; + newTransformV.rot.col0 = axis12; + newTransformV.rot.col1 = axis11; + newTransformV.rot.col2 = V3Neg(axis10); + newTransformV.p = V3ScaleAdd(axis10, eb0, transform1.p); //transform0.p - extents0.x*axis00; + } + else + { + mtd = V3Neg(axis10); + newTransformV.rot.col0 = V3Neg(axis12); + newTransformV.rot.col1 = axis11; + newTransformV.rot.col2 = axis10; + newTransformV.p =V3NegScaleSub(axis10, eb0, transform1.p);//transform0.p + extents0.x*axis00; + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform0); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, localNormal, transform1ToNew, box0Extent); + + calculateContacts(eb2, eb1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + + break; + }; + case 4: //ub1; + { + flip = true; + if(FAllGrtrOrEq(zero, sign[4])) + { + mtd = axis11; + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = axis12; + newTransformV.rot.col2 = V3Neg(axis11); + + newTransformV.p = V3ScaleAdd(axis11, eb1, transform1.p); + + } + else + { + mtd = V3Neg(axis11); + + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = V3Neg(axis12); + newTransformV.rot.col2 = axis11; + newTransformV.p = V3NegScaleSub(axis11, eb1, transform1.p); //transform0.p + extents0.x*axis00; + + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform0); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, localNormal, transform1ToNew, box0Extent); + calculateContacts(eb0, eb2, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + break; + } + case 5: //ub2; + { + + flip = true; + if(FAllGrtrOrEq(zero, sign[5])) + { + mtd = axis12; + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = V3Neg(axis11); + newTransformV.rot.col2 = V3Neg(axis12); + newTransformV.p = V3ScaleAdd(axis12, eb2, transform1.p); + } + else + { + mtd = V3Neg(axis12); + + newTransformV.rot.col0 = axis10; + newTransformV.rot.col1 = axis11; + newTransformV.rot.col2 = axis12; + newTransformV.p = V3NegScaleSub(axis12, eb2, transform1.p); + } + + const Ps::aos::PsMatTransformV transform1ToNew = newTransformV.transformInv(transform0); + const Vec3V localNormal =newTransformV.rotateInv(mtd); + getIncidentPolygon(pts, incidentFaceNormalInNew, localNormal, transform1ToNew, box0Extent); + + calculateContacts(eb0, eb1, pts, incidentFaceNormalInNew, localNormal, manifoldContacts, numContacts, contactDist); + break; + }; + default: + return false; + } + + if(flip) + { + for(PxU32 i=0; i(); + const PxBoxGeometry& shapeBox1 = shape1.get(); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + + const FloatV contactDist = FLoad(params.mContactDistance); + const Vec3V boxExtents0 = V3LoadU(shapeBox0.halfExtents); + const Vec3V boxExtents1 = V3LoadU(shapeBox1.halfExtents); + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV boxMargin0 = Gu::CalculatePCMBoxMargin(boxExtents0, toleranceLength); + const FloatV boxMargin1 = Gu::CalculatePCMBoxMargin(boxExtents1, toleranceLength); + const FloatV minMargin = FMin(boxMargin0, boxMargin1); + + const PxU32 initialContacts = manifold.mNumContacts; + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, contactDist); + + const PxU32 newContacts = manifold.mNumContacts; + + const bool bLostContacts = (newContacts != initialContacts); + + PX_UNUSED(bLostContacts); + + if(bLostContacts || manifold.invalidate_BoxConvex(curRTrans, minMargin)) + { + + manifold.setRelativeTransform(curRTrans); + + const PsMatTransformV transfV0(transf0); + const PsMatTransformV transfV1(transf1); + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + + if(doBoxBoxGenerateContacts(boxExtents0, boxExtents1, transfV0, transfV1, contactDist, manifoldContacts, numContacts)) + { + if(numContacts > 0) + { + + manifold.addBatchManifoldContacts(manifoldContacts, numContacts, toleranceLength); + const Vec3V worldNormal = V3Normalize(transfV1.rotate(Vec3V_From_Vec4V(manifold.mContactPoints[0].mLocalNormalPen))); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transfV1); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + else + { + const Vec3V zeroV = V3Zero(); + BoxV box0(zeroV, boxExtents0); + BoxV box1(zeroV, boxExtents1); + + manifold.mNumWarmStartPoints = 0; + RelativeConvex convexA(box0, aToB); + LocalConvex convexB(box1); + GjkOutput output; + + GjkStatus status = gjkPenetration, LocalConvex >(convexA, convexB, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + if(status == EPA_CONTACT) + { + + RelativeConvex convexA1(box0, aToB); + LocalConvex convexB1(box1); + + status= epaPenetration(convexA1, convexB1, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + } + + if(status == GJK_CONTACT || status == EPA_CONTACT) + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + const Vec3V localPointA = aToB.transformInv(output.closestA);//curRTrans.transformInv(closestA); + const Vec3V localPointB = output.closestB; + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + numContacts += manifold.addManifoldPoint(localPointA, localPointB, localNormalPen, replaceBreakingThreshold); + + //transform the normal back to world space + const Vec3V worldNormal = V3Normalize(transf1.rotate(output.normal)); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + + return true; + } + + } + } + } + else if(manifold.getNumContacts() > 0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + + return false; +} + +} +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp new file mode 100644 index 000000000..8e118556b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp @@ -0,0 +1,274 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "GuContactBuffer.h" + + + +#define PCM_BOX_HULL_DEBUG 0 + +namespace physx +{ + +using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationBoxConvex(const GjkConvex* relativeConvex, const GjkConvex* localConvex, const PsTransformV& transf0, const PsTransformV& transf1, + PersistentContact* manifoldContacts, ContactBuffer& contactBuffer, Gu::PersistentContactManifold& manifold, Vec3VArg normal, + const Vec3VArg closestA, const Vec3VArg closestB, const FloatVArg contactDist, const bool idtScale, const bool doOverlapTest, Cm::RenderOutput* renderOutput, + const PxReal toleranceLength) +{ + Gu::PolygonalData polyData0; + + const BoxV& box = relativeConvex->getConvex(); + const ConvexHullV& convexHull = localConvex->getConvex(); + + PxVec3 halfExtents; + V3StoreU(box.extents, halfExtents); + PCMPolygonalBox polyBox0(halfExtents); + polyBox0.getPolygonalData(&polyData0); + polyData0.mPolygonVertexRefs = gPCMBoxPolygonData; + + Gu::PolygonalData polyData1; + getPCMConvexData(convexHull, idtScale, polyData1); + + Mat33V identity = M33Identity(); + SupportLocalImpl map0(box, transf0, identity, identity, true); + + PxU8 buff1[sizeof(SupportLocalImpl)]; + + SupportLocal* map1 = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(static_cast(convexHull), transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale)) : + static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(convexHull, transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale))); + + PxU32 numContacts = 0; + if(generateFullContactManifold(polyData0, polyData1, &map0, map1, manifoldContacts, numContacts, contactDist, normal, closestA, closestB, box.getMarginF(), convexHull.getMarginF(), + doOverlapTest, renderOutput, toleranceLength)) + { + if (numContacts > 0) + { + //reduce contacts + manifold.addBatchManifoldContacts(manifoldContacts, numContacts, toleranceLength); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + } + else + { + //if doOverlapTest is true, which means GJK/EPA degenerate so we won't have any contact in the manifoldContacts array + if (!doOverlapTest) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + } + } + + return true; + } + + return false; + +} + +static bool generateOrProcessContactsBoxConvex(const GjkConvex* relativeConvex, const GjkConvex* localConvex, const PsTransformV& transf0, const PsTransformV& transf1, + const PsMatTransformV& aToB, GjkStatus status, GjkOutput& output, PersistentContactManifold& manifold, ContactBuffer& contactBuffer, + const PxU32 initialContacts, const FloatV minMargin, const FloatV contactDist, + const bool idtScale, const PxReal toleranceLength, Cm::RenderOutput* renderOutput) +{ + + if (status == GJK_NON_INTERSECT) + { + return false; + } + else + { + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + + const Vec3V localNor = manifold.mNumContacts ? manifold.getLocalNormal() : V3Zero(); + + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + //addGJKEPAContacts will increase the number of contacts in manifold. If status == EPA_CONTACT, we need to run epa algorithm and generate closest points, normal and + //pentration. If epa doesn't degenerate, we will store the contacts information in the manifold. Otherwise, we will return true to do the fallback test + const bool doOverlapTest = addGJKEPAContacts(relativeConvex, localConvex, aToB, status, manifoldContacts, replaceBreakingThreshold, FLoad(toleranceLength), output, manifold); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + //ML: after we refresh the contacts(newContacts) and generate a GJK/EPA contacts(we will store that in the manifold), if the number of contacts is still less than the original contacts, + //which means we lose too mang contacts and we should regenerate all the contacts in the current configuration + //Also, we need to look at the existing contacts, if the existing contacts has very different normal than the GJK/EPA contacts, + //which means we should throw away the existing contacts and do full contact gen + const bool fullContactGen = FAllGrtr(FLoad(0.707106781f), V3Dot(localNor, output.normal)) || (manifold.mNumContacts < initialContacts); + + if (fullContactGen || doOverlapTest) + { + return fullContactsGenerationBoxConvex(relativeConvex, localConvex, transf0, transf1, manifoldContacts, contactBuffer, + manifold, output.normal, output.closestA, output.closestB, contactDist, idtScale, doOverlapTest, renderOutput, toleranceLength); + } + else + { + const Vec3V newLocalNor = V3Add(localNor, output.normal); + const Vec3V worldNormal = V3Normalize(transf1.rotate(newLocalNor)); + //const Vec3V worldNormal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + return true; + } + } +} + +bool pcmContactBoxConvex(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + const PxBoxGeometry& shapeBox = shape0.get(); + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(shapeConvex.hullData); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const FloatV contactDist = FLoad(params.mContactDistance); + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const PxReal toleranceLength = params.mToleranceLength; + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale, toleranceLength); + const FloatV boxMargin = Gu::CalculatePCMBoxMargin(boxExtents, toleranceLength); + + +#if PCM_BOX_HULL_DEBUG + const PxVec3* verts = hullData->getHullVertices(); + + for (PxU32 i = 0; i < hullData->mNbPolygons; ++i) + { + const HullPolygonData& polygon = hullData->mPolygons[i]; + const PxU8* inds = hullData->getVertexData8() + polygon.mVRef8; + Vec3V* points = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*polygon.mNbVerts, 16)); + + for (PxU32 j = 0; j < polygon.mNbVerts; ++j) + { + points[j] = V3LoadU_SafeReadW(verts[inds[j]]); + } + + Gu::PersistentContactManifold::drawPolygon(*renderOutput, transf1, points, (PxU32)polygon.mNbVerts, 0x00ff0000); + } +#endif + + const FloatV minMargin = FMin(convexMargin, boxMargin);//FMin(boxMargin, convexMargin); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + const PxU32 initialContacts = manifold.mNumContacts; + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, contactDist); + + //After the refresh contact points, the numcontacts in the manifold will be changed + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + if(bLostContacts || manifold.invalidate_BoxConvex(curRTrans, minMargin)) + { + + manifold.setRelativeTransform(curRTrans); + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + const bool idtScale = shapeConvex.scale.isIdentity(); + Gu::ConvexHullV convexHull(hullData, V3LoadU(hullData->mCenterOfMass), vScale, vQuat, idtScale); + Gu::BoxV box(V3Zero(), boxExtents); + GjkOutput output; + + RelativeConvex relativeConvex(box, aToB); + + if(idtScale) + { + + LocalConvex localConvex(static_cast(convexHull)); + + status = gjkPenetration, LocalConvex >(relativeConvex, localConvex, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + return generateOrProcessContactsBoxConvex(&relativeConvex, &localConvex, transf0, transf1, aToB, + status, output, manifold, contactBuffer, initialContacts, + minMargin, contactDist, idtScale, toleranceLength, renderOutput); + + } + else + { + LocalConvex localConvex(convexHull); + + status = gjkPenetration, LocalConvex >(relativeConvex, localConvex, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + return generateOrProcessContactsBoxConvex(&relativeConvex, &localConvex, transf0, transf1, aToB, + status, output, manifold, contactBuffer, initialContacts, + minMargin, contactDist, idtScale, toleranceLength, renderOutput); + } + } + else if(manifold.getNumContacts()>0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + + return false; + +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp new file mode 100644 index 000000000..cb77a96d8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp @@ -0,0 +1,224 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecBox.h" +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "GuGJKPenetration.h" +#include "GuEPA.h" + + +namespace physx +{ + using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationCapsuleBox(const CapsuleV& capsule, const BoxV& box, const PxVec3 halfExtents, const PsMatTransformV& aToB, const PsTransformV& transf0, const PsTransformV& transf1, + PersistentContact* manifoldContacts, PxU32& numContacts, ContactBuffer& contactBuffer, PersistentContactManifold& manifold, Vec3V& normal, const Vec3VArg closest, + const PxReal boxMargin, const FloatVArg contactDist, const bool doOverlapTest, const PxReal toleranceScale) +{ + + PolygonalData polyData; + PCMPolygonalBox polyBox(halfExtents); + polyBox.getPolygonalData(&polyData); + + Mat33V identity = M33Identity(); + SupportLocalImpl map(box, transf1, identity, identity); + + PxU32 origContacts = numContacts; + if (generateCapsuleBoxFullContactManifold(capsule, polyData, &map, aToB, manifoldContacts, numContacts, contactDist, normal, closest, boxMargin, doOverlapTest, toleranceScale)) + { + //EPA has contacts and we have new contacts, we discard the EPA contacts + if(origContacts != 0 && numContacts != origContacts) + { + numContacts--; + manifoldContacts++; + } + + manifold.addBatchManifoldContacts2(manifoldContacts, numContacts); + + normal = transf1.rotate(normal); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsule.radius, contactDist); + + return true; + + } + + return false; + +} + + +bool pcmContactCapsuleBox(GU_CONTACT_METHOD_ARGS) +{ + + PX_UNUSED(renderOutput); + + PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + const PxCapsuleGeometry& shapeCapsule = shape0.get(); + const PxBoxGeometry& shapeBox = shape1.get(); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + + const PsTransformV curRTrans = transf1.transformInv(transf0); + const PsMatTransformV aToB_(curRTrans); + + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV capsuleHalfHeight = FLoad(shapeCapsule.halfHeight); + + const PxU32 initialContacts = manifold.mNumContacts; + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV boxMargin = Gu::CalculatePCMBoxMargin(boxExtents, toleranceLength); + + const FloatV minMargin = FMin(boxMargin, capsuleRadius); + + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + + const FloatV refreshDist = FAdd(contactDist, capsuleRadius); + //refreshContactPoints remove invalid contacts from the manifold and update the number correspondingly + manifold.refreshContactPoints(aToB_, projectBreakingThreshold, refreshDist); + + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + if(bLostContacts || manifold.invalidate_SphereCapsule(curRTrans, minMargin)) + { + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + manifold.setRelativeTransform(curRTrans); + const PsMatTransformV aToB(curRTrans); + + BoxV box(transf1.p, boxExtents); + //transform capsule into the local space of box + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + LocalConvex convexA(capsule); + LocalConvex convexB(box); + GjkOutput output; + + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), box.getCenter()); + status = gjkPenetration, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + bool doOverlapTest = false; + if(status == GJK_NON_INTERSECT) + { + return false; + } + else if(status == GJK_DEGENERATE) + { + return fullContactsGenerationCapsuleBox(capsule, box, shapeBox.halfExtents, aToB, transf0, transf1, manifoldContacts, numContacts, contactBuffer, + manifold, output.normal, output.closestB, box.getMarginF(), contactDist, true, params.mToleranceLength); + } + else + { + if(status == GJK_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(output.closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + //Add contact to contact stream + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = output.closestB; + manifoldContacts[numContacts++].mLocalNormalPen = localNormalPen; + } + else + { + PX_ASSERT(status == EPA_CONTACT); + + status= epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + if(status == EPA_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(output.closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + //Add contact to contact stream + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = output.closestB; + manifoldContacts[numContacts++].mLocalNormalPen = localNormalPen; + + } + else + { + doOverlapTest = true; + } + + } + + + if(initialContacts == 0 || bLostContacts || doOverlapTest) + { + return fullContactsGenerationCapsuleBox(capsule, box, shapeBox.halfExtents, aToB, transf0, transf1, manifoldContacts, numContacts, contactBuffer, manifold, output.normal, + output.closestB, box.getMarginF(), contactDist, doOverlapTest, params.mToleranceLength); + } + else + { + + //The contacts is either come from GJK or EPA + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.1f)); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + manifold.addManifoldPoint2(curRTrans.transformInv(output.closestA), output.closestB, localNormalPen, replaceBreakingThreshold); + + const Vec3V normal = transf1.rotate(output.normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsuleRadius, contactDist); + + return true; + } + } + } + else if(manifold.getNumContacts() > 0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, worldNormal, transf0, capsuleRadius, contactDist); + return true; + } + + return false; + +} +} +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp new file mode 100644 index 000000000..708c2cfb8 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp @@ -0,0 +1,297 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuDistanceSegmentSegmentSIMD.h" + +using namespace physx; +using namespace Gu; +using namespace Ps; +using namespace aos; + +static Vec4V pcmDistancePointSegmentTValue22( const Vec3VArg a0, const Vec3VArg b0, + const Vec3VArg a1, const Vec3VArg b1, + const Vec3VArg p0, const Vec3VArg p1, + const Vec3VArg p2, const Vec3VArg p3) +{ + const Vec4V zero = V4Zero(); + const Vec3V ap00 = V3Sub(p0, a0); + const Vec3V ap10 = V3Sub(p1, a0); + const Vec3V ap01 = V3Sub(p2, a1); + const Vec3V ap11 = V3Sub(p3, a1); + + const Vec3V ab0 = V3Sub(b0, a0); + const Vec3V ab1 = V3Sub(b1, a1); + +/* const FloatV nom00 = V3Dot(ap00, ab0); + const FloatV nom10 = V3Dot(ap10, ab0); + const FloatV nom01 = V3Dot(ap01, ab1); + const FloatV nom11 = V3Dot(ap11, ab1);*/ + + const Vec4V combinedDot = V3Dot4(ap00, ab0, ap10, ab0, ap01, ab1, ap11, ab1); + const FloatV nom00 = V4GetX(combinedDot); + const FloatV nom10 = V4GetY(combinedDot); + const FloatV nom01 = V4GetZ(combinedDot); + const FloatV nom11 = V4GetW(combinedDot); + + const FloatV denom0 = V3Dot(ab0, ab0); + const FloatV denom1 = V3Dot(ab1, ab1); + + const Vec4V nom = V4Merge(nom00, nom10, nom01, nom11); + const Vec4V denom = V4Merge(denom0, denom0, denom1, denom1); + + const Vec4V tValue = V4Div(nom, denom); + return V4Sel(V4IsEq(denom, zero), zero, tValue); +} + +namespace physx +{ +namespace Gu +{ + + static void storeContact(const Vec3VArg contact, const Vec3VArg normal, const FloatVArg separation, Gu::ContactBuffer& buffer) + { + Gu::ContactPoint& point = buffer.contacts[buffer.count++]; + + const Vec4V normalSep = Ps::aos::V4SetW(Vec4V_From_Vec3V(normal), separation); + + V4StoreA(normalSep, &point.normal.x); + V3StoreA(contact, point.point); + point.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + } + +bool pcmContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + // Get actual shape data + const PxCapsuleGeometry& shapeCapsule0 = shape0.get(); + const PxCapsuleGeometry& shapeCapsule1 = shape1.get(); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V _p0 = V3LoadA(&transform0.p.x); + const QuatV q0 = QuatVLoadA(&transform0.q.x); + + const Vec3V _p1 = V3LoadA(&transform1.p.x); + const QuatV q1 = QuatVLoadA(&transform1.q.x); + + /*PsTransformV transf0(p0, q0); + PsTransformV transf1(p1, q1);*/ + + + const FloatV r0 = FLoad(shapeCapsule0.radius); + const FloatV halfHeight0 = FLoad(shapeCapsule0.halfHeight); + + const FloatV r1 = FLoad(shapeCapsule1.radius); + const FloatV halfHeight1 = FLoad(shapeCapsule1.halfHeight); + + const FloatV cDist = FLoad(params.mContactDistance); + + const Vec3V positionOffset = V3Scale(V3Add(_p0, _p1), FHalf()); + const Vec3V p0 = V3Sub(_p0, positionOffset); + const Vec3V p1 = V3Sub(_p1, positionOffset); + + const FloatV zero = FZero(); + //const FloatV one = FOne(); + const Vec3V zeroV = V3Zero(); + + + /*const Vec3V positionOffset = V3Scale(V3Add(transf0.p, transf1.p), FloatV_From_F32(0.5f)); + transf0.p = V3Sub(transf0.p, positionOffset); + transf1.p = V3Sub(transf1.p, positionOffset);*/ + + const Vec3V basisVector0 = QuatGetBasisVector0(q0); + const Vec3V tmp0 = V3Scale(basisVector0, halfHeight0); + const Vec3V s0 = V3Add(p0, tmp0); + const Vec3V e0 = V3Sub(p0, tmp0); + const Vec3V d0 = V3Sub(e0, s0); + + const Vec3V basisVector1 = QuatGetBasisVector0(q1); + const Vec3V tmp1 = V3Scale(basisVector1, halfHeight1); + const Vec3V s1 = V3Add(p1, tmp1); + const Vec3V e1 = V3Sub(p1, tmp1); + + const Vec3V d1 = V3Sub(e1, s1); + + const FloatV sumRadius = FAdd(r0, r1); + const FloatV inflatedSum = FAdd(sumRadius, cDist); + const FloatV inflatedSumSquared = FMul(inflatedSum, inflatedSum); + const FloatV a = V3Dot(d0, d0);//squared length of segment1 + const FloatV e = V3Dot(d1, d1);//squared length of segment2 + const FloatV eps = FLoad(1e-6f);//FEps(); + + FloatV t0, t1; + const FloatV sqDist0 = distanceSegmentSegmentSquared(s0, d0, s1, d1, t0, t1); + + if(FAllGrtrOrEq(inflatedSumSquared, sqDist0)) + { + const Vec4V zeroV4 = V4Zero(); + const Vec4V oneV4 = V4One(); + //check to see whether these two capsule are paralle + const FloatV parallelTolerance = FLoad(0.9998f); + + + const BoolV con0 = FIsGrtr(eps, a); + const BoolV con1 = FIsGrtr(eps, e); + const Vec3V dir0 = V3Sel(con0, zeroV, V3ScaleInv(d0, FSqrt(a))); + const Vec3V dir1 = V3Sel(con1, zeroV, V3ScaleInv(d1, FSqrt(e))); + + const FloatV cos = FAbs(V3Dot(dir0, dir1)); + if(FAllGrtr(cos, parallelTolerance))//paralle + { + //project s, e into s1e1 + const Vec4V t= pcmDistancePointSegmentTValue22(s0, e0, s1, e1, + s1, e1, s0, e0); + + const BoolV con = BAnd(V4IsGrtrOrEq(t, zeroV4), V4IsGrtrOrEq(oneV4, t)); + const BoolV con00 = BGetX(con); + const BoolV con01 = BGetY(con); + const BoolV con10 = BGetZ(con); + const BoolV con11 = BGetW(con); + + /* PX_ALIGN(16, PxU32 conditions[4]); + F32Array_Aligned_From_Vec4V(con, (PxF32*)conditions);*/ + + + PxU32 numContact=0; + + if(BAllEqTTTT(con00)) + { + const Vec3V projS1 = V3ScaleAdd(d0, V4GetX(t), s0); + const Vec3V v = V3Sub(projS1, s1); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, projS1); + const Vec3V p = V3Add(_p, positionOffset); + + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + if(BAllEqTTTT(con01)) + { + const Vec3V projE1 = V3ScaleAdd(d0, V4GetY(t), s0); + const Vec3V v = V3Sub(projE1, e1); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, projE1); + const Vec3V p = V3Add(_p, positionOffset); + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + + if(BAllEqTTTT(con10)) + { + const Vec3V projS0 = V3ScaleAdd(d1, V4GetZ(t), s1); + const Vec3V v = V3Sub(s0, projS0); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, s0); + const Vec3V p = V3Add(_p, positionOffset); + //const Vec3V p = V3ScaleAdd(normal, r0, s0); + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + + if(BAllEqTTTT(con11)) + { + const Vec3V projE0 = V3ScaleAdd(d1, V4GetW(t), s1); + const Vec3V v = V3Sub(e0, projE0); + const FloatV sqDist = V3Dot(v, v); + const BoolV bCon = BAnd(FIsGrtr(sqDist, eps), FIsGrtr(inflatedSumSquared, sqDist)); + + if(BAllEqTTTT(bCon)) + { + const FloatV dist = FSqrt(sqDist); + const FloatV pen = FSub(dist, sumRadius); + const Vec3V normal = V3ScaleInv(v, dist); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _p = V3NegScaleSub(normal, r0, e0); + const Vec3V p = V3Add(_p, positionOffset); + //const Vec3V p = V3ScaleAdd(normal, r0, e0); + storeContact(p, normal, pen, contactBuffer); + numContact++; + } + } + + if(numContact) + return true; + + } + + const Vec3V closestA = V3ScaleAdd(d0, t0, s0); + const Vec3V closestB = V3ScaleAdd(d1, t1, s1); + + const BoolV con = FIsGrtr(eps, sqDist0); + //const Vec3V normal = V3Sel(FIsEq(dist, zero), V3Sel(FIsGrtr(a, eps), V3Normalise(d0), V3Scale(V3Sub(closestA, closestB), FRecip(dist))); + const Vec3V _normal = V3Sel(con, V3Sel(FIsGrtr(a, eps), d0, V3UnitX()), V3Sub(closestA, closestB)); + const Vec3V normal = V3Normalize(_normal); + PX_ASSERT(isFiniteVec3V(normal)); + const Vec3V _point = V3NegScaleSub(normal, r0, closestA); + const Vec3V p = V3Add(_point, positionOffset); + const FloatV dist = FSel(con, zero, FSqrt(sqDist0)); + const FloatV pen = FSub(dist, sumRadius); + //PX_ASSERT(FAllGrtrOrEq(zero, pen)); + storeContact(p, normal, pen, contactBuffer); + return true; + } + return false; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp new file mode 100644 index 000000000..d7a637c1f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp @@ -0,0 +1,266 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" + +namespace physx +{ + +using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationCapsuleConvex(const CapsuleV& capsule, const ConvexHullV& convexHull, const PsMatTransformV& aToB, const PsTransformV& transf0,const PsTransformV& transf1, + PersistentContact* manifoldContacts, ContactBuffer& contactBuffer, const bool idtScale, PersistentContactManifold& manifold, Vec3VArg normal, + const Vec3VArg closest, const PxReal tolerance, const FloatVArg contactDist, const bool doOverlapTest, Cm::RenderOutput* renderOutput, const PxReal toleranceLength) +{ + + PX_UNUSED(renderOutput); + Gu::PolygonalData polyData; + getPCMConvexData(convexHull,idtScale, polyData); + + PxU8 buff[sizeof(SupportLocalImpl)]; + SupportLocal* map = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(static_cast(convexHull), transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale)) : + static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(convexHull, transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale))); + + PxU32 numContacts = 0; + if (generateFullContactManifold(capsule, polyData, map, aToB, manifoldContacts, numContacts, contactDist, normal, closest, tolerance, doOverlapTest, toleranceLength)) + { + + if (numContacts > 0) + { + manifold.addBatchManifoldContacts2(manifoldContacts, numContacts); + //transform normal into the world space + normal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsule.radius, contactDist); + } + else + { + if (!doOverlapTest) + { + normal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsule.radius, contactDist); + } + } + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + + } + return false; + +} + +bool pcmContactCapsuleConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + const PxCapsuleGeometry& shapeCapsule = shape0.get(); + + PersistentContactManifold& manifold = cache.getManifold(); + + Ps::prefetchLine(shapeConvex.hullData); + + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + + const FloatV contactDist = FLoad(params.mContactDistance); + const FloatV capsuleHalfHeight = FLoad(shapeCapsule.halfHeight); + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const ConvexHullData* hullData =shapeConvex.hullData; + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const PxReal toleranceLength = params.mToleranceLength ; + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale, toleranceLength); + const FloatV capsuleMinMargin = Gu::CalculateCapsuleMinMargin(capsuleRadius); + const FloatV minMargin = FMin(convexMargin, capsuleMinMargin); + + const PxU32 initialContacts = manifold.mNumContacts; + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(1.25f)); + const FloatV refreshDist = FAdd(contactDist, capsuleRadius); + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, refreshDist); + + //ML: after refreshContactPoints, we might lose some contacts + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + PX_UNUSED(bLostContacts); + if(bLostContacts || manifold.invalidate_SphereCapsule(curRTrans, minMargin)) + { + const bool idtScale = shapeConvex.scale.isIdentity(); + + manifold.setRelativeTransform(curRTrans); + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + ConvexHullV convexHull(hullData, V3LoadU(hullData->mCenterOfMass), vScale, vQuat, idtScale); + + //transform capsule(a) into the local space of convexHull(b) + CapsuleV capsule(aToB.p, aToB.rotate(V3Scale(V3UnitX(), capsuleHalfHeight)), capsuleRadius); + + GjkOutput output; + LocalConvex convexA(capsule); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(idtScale) + { + LocalConvex convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + + status = gjkPenetration, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + } + else + { + LocalConvex convexB(convexHull); + status = gjkPenetration, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + } + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + bool doOverlapTest = false; + if(status == GJK_NON_INTERSECT) + { + return false; + } + else if(status == GJK_DEGENERATE) + { + return fullContactsGenerationCapsuleConvex(capsule, convexHull, aToB, transf0, transf1, manifoldContacts, contactBuffer, idtScale, manifold, output.normal, + output.closestB, convexHull.getMarginF(), contactDist, true, renderOutput, toleranceLength); + } + else + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + if(status == GJK_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(output.closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = output.closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint2(localPointA, output.closestB, localNormalPen, replaceBreakingThreshold); + } + else + { + PX_ASSERT(status == EPA_CONTACT); + + if(idtScale) + { + LocalConvex convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + } + else + { + LocalConvex convexB(convexHull); + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + } + + + if(status == EPA_CONTACT) + { + const Vec3V localPointA = aToB.transformInv(output.closestA);//curRTrans.transformInv(closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = output.closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint2(localPointA, output.closestB, localNormalPen, replaceBreakingThreshold); + + + } + else + { + doOverlapTest = true; + } + } + + + if(initialContacts == 0 || bLostContacts || doOverlapTest) + { + return fullContactsGenerationCapsuleConvex(capsule, convexHull, aToB, transf0, transf1, manifoldContacts, contactBuffer, idtScale, manifold, output.normal, + output.closestB, convexHull.getMarginF(), contactDist, doOverlapTest, renderOutput, toleranceLength); + } + else + { + //This contact is either come from GJK or EPA + const Vec3V normal = transf1.rotate(output.normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsuleRadius, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + } + } + else if (manifold.getNumContacts() > 0) + { + const Vec3V normal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, normal, normal, transf0, capsuleRadius, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + return false; +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp new file mode 100644 index 000000000..b8b4d6a6b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp @@ -0,0 +1,158 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsVecMath.h" +#include "PsVecTransform.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuHeightField.h" +#include "GuPCMContactConvexCommon.h" +#include "GuSegment.h" +#include "GuInternal.h" +#include "GuPCMContactMeshCallback.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +namespace physx +{ + +struct PCMCapsuleVsHeightfieldContactGenerationCallback : PCMHeightfieldContactGenerationCallback +{ + PCMCapsuleVsHeightfieldContactGenerationCallback& operator=(const PCMCapsuleVsHeightfieldContactGenerationCallback&); + +public: + PCMCapsuleVsMeshContactGeneration mGeneration; + + PCMCapsuleVsHeightfieldContactGenerationCallback( + const Gu::CapsuleV& capsule, + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + + const PsTransformV& capsuleTransform, + const PsTransformV& heightfieldTransform, + const PxTransform& heightfieldTransform1, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Ps::InlineArray* deferredContacts, + Gu::HeightFieldUtil& hfUtil + + + ) : + PCMHeightfieldContactGenerationCallback(hfUtil, heightfieldTransform1), + mGeneration(capsule, contactDistance, replaceBreakingThreshold, capsuleTransform, heightfieldTransform, multiManifold, + contactBuffer, deferredContacts) + { + } + + template + void processTriangleCache(Gu::TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + +bool Gu::pcmContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + const PxCapsuleGeometry& shapeCapsule = shape0.get(); + const PxHeightFieldGeometryLL& shapeHeight = shape1.get(); + + Gu::MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV capsuleTransform = loadTransformA(transform0);//capsule transform + const PsTransformV heightfieldTransform = loadTransformA(transform1);//height feild + + const PsTransformV curTransform = heightfieldTransform.transformInv(capsuleTransform); + + const FloatV replaceBreakingThreshold = FMul(capsuleRadius, FLoad(0.001f)); + + if(multiManifold.invalidate(curTransform, capsuleRadius, FLoad(0.02f))) + { + + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + Gu::HeightFieldUtil hfUtil(shapeHeight); + + const PxVec3 tmp = getCapsuleHalfHeightVector(transform0, shapeCapsule); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + const PxVec3 capsuleCenterInMesh = transform1.transformInv(transform0.p); + const PxVec3 capsuleDirInMesh = transform1.rotateInv(tmp); + const Gu::CapsuleV capsule(V3LoadU(capsuleCenterInMesh), V3LoadU(capsuleDirInMesh), capsuleRadius); + + PCMCapsuleVsHeightfieldContactGenerationCallback callback( + capsule, + contactDist, + replaceBreakingThreshold, + capsuleTransform, + heightfieldTransform, + transform1, + multiManifold, + contactBuffer, + NULL, + hfUtil + ); + + PxBounds3 bounds; + bounds.maximum = PxVec3(shapeCapsule.halfHeight + inflatedRadius, inflatedRadius, inflatedRadius); + bounds.minimum = -bounds.maximum; + + bounds = PxBounds3::transformFast(transform1.transformInv(transform0), bounds); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &callback); + + callback.mGeneration.processContacts(GU_CAPSULE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + // We must be in local space to use the cache + const FloatV projectBreakingThreshold = FMul(capsuleRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(capsuleRadius, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + + } + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, capsuleTransform, heightfieldTransform, capsuleRadius); +} + + +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp new file mode 100644 index 000000000..57bcf18df --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp @@ -0,0 +1,185 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecTriangle.h" +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" +#include "PsVecMath.h" +#include "PsVecTransform.h" + +#include "GuContactMethodImpl.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuPCMContactConvexCommon.h" +#include "GuSegment.h" +#include "GuVecCapsule.h" +#include "GuInternal.h" +#include "GuPCMContactMeshCallback.h" +#include "GuConvexEdgeFlags.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMCapsuleVsMeshContactGenerationCallback : PCMMeshContactGenerationCallback< PCMCapsuleVsMeshContactGenerationCallback > +{ + PCMCapsuleVsMeshContactGenerationCallback& operator=(const PCMCapsuleVsMeshContactGenerationCallback&); +public: + PCMCapsuleVsMeshContactGeneration mGeneration; + + PCMCapsuleVsMeshContactGenerationCallback( + const CapsuleV& capsule, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const PsTransformV& sphereTransform, + const PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + const PxU8* extraTriData, + const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtMeshScale, + Ps::InlineArray* deferredContacts, + Cm::RenderOutput* renderOutput = NULL + ) : + PCMMeshContactGenerationCallback(meshScaling, extraTriData, idtMeshScale), + mGeneration(capsule, contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, contactBuffer, + deferredContacts, renderOutput) + { + } + + PX_FORCE_INLINE bool doTest(const PxVec3&, const PxVec3&, const PxVec3&) { return true; } + + template + void processTriangleCache(TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + +bool Gu::pcmContactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + const PxCapsuleGeometry& shapeCapsule= shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + //gRenderOutPut = cache.mRenderOutput; + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV capsuleTransform = loadTransformA(transform0);//capsule transform + const PsTransformV meshTransform = loadTransformA(transform1);//triangleMesh + + const PsTransformV curTransform = meshTransform.transformInv(capsuleTransform); + + // We must be in local space to use the cache + if(multiManifold.invalidate(curTransform, capsuleRadius, FLoad(0.02f))) + { + const FloatV replaceBreakingThreshold = FMul(capsuleRadius, FLoad(0.001f)); + //const FloatV capsuleHalfHeight = FloatV_From_F32(shapeCapsule.halfHeight); + Cm::FastVertex2ShapeScaling meshScaling; + const bool idtMeshScale = shapeMesh.scale.isIdentity(); + if(!idtMeshScale) + meshScaling.init(shapeMesh.scale); + + // Capsule data + const PxVec3 tmp = getCapsuleHalfHeightVector(transform0, shapeCapsule); + Segment worldCapsule; + worldCapsule.p0 = transform0.p + tmp; + worldCapsule.p1 = transform0.p - tmp; + + + const Segment meshCapsule( // Capsule in mesh space + transform1.transformInv(worldCapsule.p0), + transform1.transformInv(worldCapsule.p1)); + + const PxReal inflatedRadius = shapeCapsule.radius + params.mContactDistance; + + const PxVec3 capsuleCenterInMesh = transform1.transformInv(transform0.p); + const PxVec3 capsuleDirInMesh = transform1.rotateInv(tmp); + const CapsuleV capsule(V3LoadU(capsuleCenterInMesh), V3LoadU(capsuleDirInMesh), capsuleRadius); + + // We must be in local space to use the cache + const Capsule queryCapsule(meshCapsule, inflatedRadius); + + const TriangleMesh* meshData = shapeMesh.meshData; + + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + const PxU8* PX_RESTRICT extraData = meshData->getExtraTrigData(); + // mesh scale is not baked into cached verts + PCMCapsuleVsMeshContactGenerationCallback callback( + capsule, + contactDist, + replaceBreakingThreshold, + capsuleTransform, + meshTransform, + multiManifold, + contactBuffer, + extraData, + meshScaling, + idtMeshScale, + NULL, + renderOutput); + + //bound the capsule in shape space by an OBB: + Box queryBox; + queryBox.create(queryCapsule); + + //apply the skew transform to the box: + if(!idtMeshScale) + meshScaling.transformQueryBounds(queryBox.center, queryBox.extents, queryBox.rot); + + Midphase::intersectOBB(meshData, queryBox, callback, true); + + callback.flushCache(); + + callback.mGeneration.processContacts(GU_CAPSULE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(capsuleRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(capsuleRadius, contactDist); + //multiManifold.refreshManifold(aToB, projectBreakingThreshold, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + } + + //multiManifold.drawManifold(*gRenderOutPut, capsuleTransform, meshTransform); + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, capsuleTransform, meshTransform, capsuleRadius); +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp new file mode 100644 index 000000000..be2692f68 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp @@ -0,0 +1,1354 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecTriangle.h" +#include "GuPCMContactConvexCommon.h" +#include "GuConvexEdgeFlags.h" +#include "GuBarycentricCoordinates.h" +#include "PsSort.h" + +namespace physx +{ + +namespace Gu +{ +/* + This function adds the newly created manifold contacts to a new patch or existing patches +*/ +void PCMConvexVsMeshContactGeneration::addContactsToPatch(const Ps::aos::Vec3VArg patchNormal, const PxU32 previousNumContacts) +{ + using namespace Ps::aos; + + const Vec3V patchNormalInTriangle = mMeshToConvex.rotateInv(patchNormal); + + + const PxU32 newContacts = mNumContacts - previousNumContacts; + + if(newContacts > GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE) + { + //if the current created manifold contacts are more than GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points, we will reduce the total numContacts + //to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE. However, after we add these points into a patch, the patch contacts will be variable. Then we will + //do contact reduction for that patch in the processContacts. After the contact reduction, there will be no more than GU_SINGLE_MANIFOLD_CACHE_SIZE(6) + //contacts inside a signlePersistentContactManifold + Gu::SinglePersistentContactManifold::reduceContacts(&mManifoldContacts[previousNumContacts], newContacts); + mNumContacts = previousNumContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + + //get rid of duplicate manifold contacts for the newly created contacts + for(PxU32 i = previousNumContacts; i= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + PX_ASSERT(mNumContacts <= ContactBuffer::MAX_CONTACTS); + processContacts(GU_SINGLE_MANIFOLD_CACHE_SIZE); + } +} + + +void PCMConvexVsMeshContactGeneration::generateLastContacts() +{ + using namespace Ps::aos; + // Process delayed contacts + PxU32 nbEntries = mDeferredContacts->size(); + + if(nbEntries) + { + nbEntries /= sizeof(PCMDeferredPolyData)/sizeof(PxU32); + + const PCMDeferredPolyData* PX_RESTRICT cd = reinterpret_cast(mDeferredContacts->begin()); + for(PxU32 i=0;ipreviousNumContacts; --j) + { + PxU32 ind = j-1; + //calculate the barycentric coordinate of the contacts in localTriangle, p = a + v(b-a) + w(c-a)., p=ua+vb+wc + barycentricCoordinates(mManifoldContacts[ind].mLocalPointB, localTriangle.verts[0], localTriangle.verts[1], localTriangle.verts[2], v, w); + //const FloatV u = FSub(one, FAdd(v, w)); + + bool keepContact = true; + if(FAllGrtr(v, upperBound))//v > upperBound + { + //vertex1 + keepContact = !mVertexCache.contains(Gu::CachedVertex(ref1)); + } + else if(FAllGrtr(w, upperBound))// w > upperBound + { + //vertex2 + keepContact = !mVertexCache.contains(Gu::CachedVertex(ref2)); + } + else if(FAllGrtrOrEq(lowerBound, FAdd(v, w))) // u(1-(v+w)) > upperBound + { + //vertex0 + keepContact = !mVertexCache.contains(Gu::CachedVertex(ref0)); + } + + if(!keepContact) + { + //ML: if feature code is any of the vertex in this triangle and we have generated contacts with any other triangles which contains this vertex, we should drop it + currentContacts--; + + for(PxU32 k = ind; k < currentContacts; ++k) + { + mManifoldContacts[k] = mManifoldContacts[k+1]; + } + } + } + + mNumContacts = currentContacts; + + if(currentContacts > previousNumContacts) + { + addContactsToPatch(patchNormal, previousNumContacts); + } + + } + } + } + +} + +bool PCMConvexVsMeshContactGeneration::processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds) +{ + using namespace Ps::aos; + + + const Mat33V identity = M33Identity(); + const FloatV zero = FZero(); + + const Vec3V v0 = V3LoadU(verts[0]); + const Vec3V v1 = V3LoadU(verts[1]); + const Vec3V v2 = V3LoadU(verts[2]); + + + + const Vec3V v10 = V3Sub(v1, v0); + const Vec3V v20 = V3Sub(v2, v0); + + const Vec3V n = V3Normalize(V3Cross(v10, v20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(v0, n);//d = -p0.dot(n); + + const FloatV dist = FSub(V3Dot(mHullCenterMesh, n), d);//p.dot(n) + d; + + // Backface culling + if(FAllGrtr(zero, dist)) + return false; + + + //tranform verts into the box local space + const Vec3V locV0 = mMeshToConvex.transform(v0); + const Vec3V locV1 = mMeshToConvex.transform(v1); + const Vec3V locV2 = mMeshToConvex.transform(v2); + + Gu::TriangleV localTriangle(locV0, locV1, locV2); + + { + + SupportLocalImpl localTriMap(localTriangle, mConvexTransform, identity, identity, true); + + const PxU32 previousNumContacts = mNumContacts; + Vec3V patchNormal; + + generateTriangleFullContactManifold(localTriangle, triangleIndex, vertInds, triFlags, mPolyData, &localTriMap, mPolyMap, mManifoldContacts, mNumContacts, mContactDist, patchNormal); + + if(mNumContacts > previousNumContacts) + { +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawTriangle(*mRenderOutput, mMeshTransform.transform(v0), mMeshTransform.transform(v1), mMeshTransform.transform(v2), 0x00ff00); +#endif + + bool inActiveEdge0 = (triFlags & ETD_CONVEX_EDGE_01) == 0; + bool inActiveEdge1 = (triFlags & ETD_CONVEX_EDGE_12) == 0; + bool inActiveEdge2 = (triFlags & ETD_CONVEX_EDGE_20) == 0; + + if(inActiveEdge0) + mEdgeCache.addData(CachedEdge(vertInds[0], vertInds[1])); + if(inActiveEdge1) + mEdgeCache.addData(CachedEdge(vertInds[1], vertInds[2])); + if(inActiveEdge2) + mEdgeCache.addData(CachedEdge(vertInds[2], vertInds[0])); + + mVertexCache.addData(CachedVertex(vertInds[0])); + mVertexCache.addData(CachedVertex(vertInds[1])); + mVertexCache.addData(CachedVertex(vertInds[2])); + + addContactsToPatch(patchNormal, previousNumContacts); + } + } + + + + return true; +} + + +bool PCMConvexVsMeshContactGeneration::processTriangle(const Gu::PolygonalData& polyData, SupportLocal* polyMap, const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags,const Ps::aos::FloatVArg inflation, const bool isDoubleSided, + const Ps::aos::PsTransformV& convexTransform, const Ps::aos::PsMatTransformV& meshToConvex, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + + + const Mat33V identity = M33Identity(); + const FloatV zero = FZero(); + + const Vec3V v0 = V3LoadU(verts[0]); + const Vec3V v1 = V3LoadU(verts[1]); + const Vec3V v2 = V3LoadU(verts[2]); + + //tranform verts into the box local space + const Vec3V locV0 = meshToConvex.transform(v0); + const Vec3V locV1 = meshToConvex.transform(v1); + const Vec3V locV2 = meshToConvex.transform(v2); + + const Vec3V v10 = V3Sub(locV1, locV0); + const Vec3V v20 = V3Sub(locV2, locV0); + + const Vec3V n = V3Normalize(V3Cross(v10, v20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(locV0, n);//d = -p0.dot(n); + + const FloatV dist = FSub(V3Dot(polyMap->shapeSpaceCenterOfMass, n), d);//p.dot(n) + d; + + // Backface culling + const bool culled = !isDoubleSided && (FAllGrtr(zero, dist)); + if(culled) + return false; + + + Gu::TriangleV localTriangle(locV0, locV1, locV2); + + SupportLocalImpl localTriMap(localTriangle, convexTransform, identity, identity, true); + + Vec3V patchNormal; + + generateTriangleFullContactManifold(localTriangle, triangleIndex, triFlags, polyData, &localTriMap, polyMap, manifoldContacts, numContacts, inflation, patchNormal); + + return true; +} + +PX_FORCE_INLINE Ps::aos::Vec4V pcmDistanceSegmentSegmentSquared4( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg d0, + const Ps::aos::Vec3VArg p02, const Ps::aos::Vec3VArg d02, + const Ps::aos::Vec3VArg p12, const Ps::aos::Vec3VArg d12, + const Ps::aos::Vec3VArg p22, const Ps::aos::Vec3VArg d22, + const Ps::aos::Vec3VArg p32, const Ps::aos::Vec3VArg d32, + Ps::aos::Vec4V& s, Ps::aos::Vec4V& t) +{ + using namespace Ps::aos; + const Vec4V zero = V4Zero(); + const Vec4V one = V4One(); + const Vec4V eps = V4Eps(); + const Vec4V half = V4Splat(FHalf()); + + const Vec4V d0X = V4Splat(V3GetX(d0)); + const Vec4V d0Y = V4Splat(V3GetY(d0)); + const Vec4V d0Z = V4Splat(V3GetZ(d0)); + const Vec4V pX = V4Splat(V3GetX(p)); + const Vec4V pY = V4Splat(V3GetY(p)); + const Vec4V pZ = V4Splat(V3GetZ(p)); + + Vec4V d024 = Vec4V_From_Vec3V(d02); + Vec4V d124 = Vec4V_From_Vec3V(d12); + Vec4V d224 = Vec4V_From_Vec3V(d22); + Vec4V d324 = Vec4V_From_Vec3V(d32); + + Vec4V p024 = Vec4V_From_Vec3V(p02); + Vec4V p124 = Vec4V_From_Vec3V(p12); + Vec4V p224 = Vec4V_From_Vec3V(p22); + Vec4V p324 = Vec4V_From_Vec3V(p32); + + Vec4V d0123X, d0123Y, d0123Z; + Vec4V p0123X, p0123Y, p0123Z; + + PX_TRANSPOSE_44_34(d024, d124, d224, d324, d0123X, d0123Y, d0123Z); + PX_TRANSPOSE_44_34(p024, p124, p224, p324, p0123X, p0123Y, p0123Z); + + const Vec4V rX = V4Sub(pX, p0123X); + const Vec4V rY = V4Sub(pY, p0123Y); + const Vec4V rZ = V4Sub(pZ, p0123Z); + + //TODO - store this in a transposed state and avoid so many dot products? + + const FloatV dd = V3Dot(d0, d0); + + const Vec4V e = V4MulAdd(d0123Z, d0123Z, V4MulAdd(d0123X, d0123X, V4Mul(d0123Y, d0123Y))); + const Vec4V b = V4MulAdd(d0Z, d0123Z, V4MulAdd(d0X, d0123X, V4Mul(d0Y, d0123Y))); + const Vec4V c = V4MulAdd(d0Z, rZ, V4MulAdd(d0X, rX, V4Mul(d0Y, rY))); + const Vec4V f = V4MulAdd(d0123Z, rZ, V4MulAdd(d0123X, rX, V4Mul(d0123Y, rY))); + + const Vec4V a(V4Splat(dd)); + + const Vec4V aRecip(V4Recip(a)); + const Vec4V eRecip(V4Recip(e)); + + //if segments not parallell, compute closest point on two segments and clamp to segment1 + const Vec4V denom = V4Sub(V4Mul(a, e), V4Mul(b, b)); + const Vec4V temp = V4Sub(V4Mul(b, f), V4Mul(c, e)); + //const Vec4V s0 = V4Clamp(V4Div(temp, denom), zero, one); + //In PS3, 0(temp)/0(denom) will produce QNaN and V4Clamp can't clamp the value to zero and one. In PC, 0/0 will produce inf and V4Clamp clamp the value to be one. + //Therefore, we need to add the select code to protect against this case + const Vec4V value = V4Sel(V4IsEq(denom, zero), one, V4Div(temp, denom)); + const Vec4V s0 = V4Clamp(value, zero, one); + + //test whether segments are parallel + const BoolV con2 = V4IsGrtrOrEq(eps, denom); + const Vec4V sTmp = V4Sel(con2, half, s0); + + //compute point on segment2 closest to segment1 + //const Vec4V tTmp = V4Mul(V4Add(V4Mul(b, sTmp), f), eRecip); + const Vec4V tTmp = V4Sel(V4IsEq(e, zero), one, V4Mul(V4Add(V4Mul(b, sTmp), f), eRecip)); + + //if t is in [zero, one], done. otherwise clamp t + const Vec4V t2 = V4Clamp(tTmp, zero, one); + + //recompute s for the new value + //const Vec4V comp = V4Mul(V4Sub(V4Mul(b,t2), c), aRecip); + const Vec4V comp = V4Sel(V4IsEq(a, zero), one, V4Mul(V4Sub(V4Mul(b,t2), c), aRecip)); + const Vec4V s2 = V4Clamp(comp, zero, one); + + s = s2; + t = t2; + + const Vec4V closest1X = V4MulAdd(d0X, s2, pX); + const Vec4V closest1Y = V4MulAdd(d0Y, s2, pY); + const Vec4V closest1Z = V4MulAdd(d0Z, s2, pZ); + + const Vec4V closest2X = V4MulAdd(d0123X, t2, p0123X); + const Vec4V closest2Y = V4MulAdd(d0123Y, t2, p0123Y); + const Vec4V closest2Z = V4MulAdd(d0123Z, t2, p0123Z); + + const Vec4V vvX = V4Sub(closest1X, closest2X); + const Vec4V vvY = V4Sub(closest1Y, closest2Y); + const Vec4V vvZ = V4Sub(closest1Z, closest2Z); + + const Vec4V vd = V4MulAdd(vvX, vvX, V4MulAdd(vvY, vvY, V4Mul(vvZ, vvZ))); + + return vd; +} + +static Ps::aos::FloatV pcmDistancePointTriangleSquared( const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, + const PxU8 triFlags, + Ps::aos::Vec3V& closestP, + bool& generateContact, + bool& faceContact) +{ + using namespace Ps::aos; + + faceContact = false; + const FloatV zero = FZero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V bp = V3Sub(p, b); + const Vec3V cp = V3Sub(p, c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + const Vec3V n = V3Cross(ab, ac); + const VecCrossV crossA = V3PrepareCross(ap); + const VecCrossV crossB = V3PrepareCross(bp); + const VecCrossV crossC = V3PrepareCross(cp); + const Vec3V bCrossC = V3Cross(crossB, crossC); + const Vec3V cCrossA = V3Cross(crossC, crossA); + const Vec3V aCrossB = V3Cross(crossA, crossB); + + //const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + //const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + //const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + + if(BAllEqTTTT(con0)) + { + //Vertex 0 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_01) || (triFlags & Gu::ETD_CONVEX_EDGE_20); + closestP = a; + return V3Dot(ap, ap); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + //Vertex 1 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_01) || (triFlags & Gu::ETD_CONVEX_EDGE_12); + closestP = b; + return V3Dot(bp, bp); + } + + //check if p in vertex region outside c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if(BAllEqTTTT(con2)) + { + //Vertex 2 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_12) || (triFlags & Gu::ETD_CONVEX_EDGE_20); + closestP = c; + return V3Dot(cp, cp); + } + + //check if p in edge region of AB + //const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); + const FloatV vc = V3Dot(n, aCrossB);//edge region of AB, signed area rab, w = S(rab)/S(abc) for c + const BoolV con30 = FIsGrtr(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtr(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + if(BAllEqTTTT(con3)) + { + // Edge 01 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_01) != 0; + const FloatV sScale = FDiv(d1, FSub(d1, d3)); + const Vec3V closest3 = V3ScaleAdd(ab, sScale, a);//V3Add(a, V3Scale(ab, sScale)); + const Vec3V vv = V3Sub(p, closest3); + closestP = closest3; + return V3Dot(vv, vv); + } + + //check if p in edge region of BC + //const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); + const FloatV va = V3Dot(n, bCrossC);//edge region of BC, signed area rbc, u = S(rbc)/S(abc) for a + + const BoolV con40 = FIsGrtr(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + if(BAllEqTTTT(con4)) + { + // Edge 12 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_12) != 0; + const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); + const Vec3V closest4 = V3ScaleAdd(bc, uScale, b);//V3Add(b, V3Scale(bc, uScale)); + const Vec3V vv = V3Sub(p, closest4); + closestP = closest4; + return V3Dot(vv, vv); + } + + //check if p in edge region of AC + //const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); + const FloatV vb = V3Dot(n, cCrossA);//edge region of AC, signed area rac, v = S(rca)/S(abc) for b + const BoolV con50 = FIsGrtr(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtr(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + if(BAllEqTTTT(con5)) + { + //Edge 20 + generateContact = (triFlags & Gu::ETD_CONVEX_EDGE_20) != 0; + const FloatV tScale = FDiv(d2, FSub(d2, d6)); + const Vec3V closest5 = V3ScaleAdd(ac, tScale, a);//V3Add(a, V3Scale(ac, tScale)); + const Vec3V vv = V3Sub(p, closest5); + closestP = closest5; + return V3Dot(vv, vv); + } + + generateContact = true; + faceContact = true; + + //P must project inside face region. Compute Q using Barycentric coordinates + const FloatV nn = V3Dot(n, n); + const FloatV t = FDiv(V3Dot(n, V3Sub(a, p)), nn); + const Vec3V vv = V3Scale(n, t); + closestP = V3Add(p, vv); + return V3Dot(vv, vv); +} + +bool Gu::PCMSphereVsMeshContactGeneration::processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds) +{ + PX_UNUSED(triangleIndex); + PX_UNUSED(vertInds); + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const Vec3V v0 = V3LoadU(verts[0]); + const Vec3V v1 = V3LoadU(verts[1]); + const Vec3V v2 = V3LoadU(verts[2]); + + const Vec3V v10 = V3Sub(v1, v0); + const Vec3V v20 = V3Sub(v2, v0); + + const Vec3V n = V3Normalize(V3Cross(v10, v20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(v0, n);//d = -p0.dot(n); + + const FloatV dist0 = FSub(V3Dot(mSphereCenter, n), d);//p.dot(n) + d; + + // Backface culling + if(FAllGrtr(zero, dist0)) + return false; + + + const FloatV tolerance = FLoad(0.996f);//around 5 degree + //FloatV u, v; + Vec3V closestP; + //mSphereCenter will be in the local space of the triangle mesh + bool generateContact = false; + bool faceContact = false; + FloatV sqDist = pcmDistancePointTriangleSquared(mSphereCenter, v0, v1, v2, triFlags, closestP, generateContact, faceContact); + + //sphere overlap with triangles + if (FAllGrtr(mSqInflatedSphereRadius, sqDist)) + { + + //sphere center is on the triangle surface, we take triangle normal as the patchNormal. Otherwise, we need to calculate the patchNormal + Vec3V patchNormal = n; + if (!faceContact) + patchNormal = V3Normalize(V3Sub(mSphereCenter, closestP)); + + const FloatV cosTheta = V3Dot(patchNormal, n); + + //two normal less than 5 degree, generate contacts + if (FAllGrtr(cosTheta, tolerance)) + { + const FloatV dist = FSqrt(sqDist); + + mEdgeCache.addData(CachedEdge(vertInds[0], vertInds[1])); + mEdgeCache.addData(CachedEdge(vertInds[1], vertInds[2])); + mEdgeCache.addData(CachedEdge(vertInds[2], vertInds[0])); + + addToPatch(closestP, patchNormal, dist, triangleIndex); + } + else + { + //ML : defer the contacts generation + const PxU32 nb = sizeof(PCMDeferredPolyData) / sizeof(PxU32); + PxU32 newSize = nb + mDeferredContacts->size(); + mDeferredContacts->reserve(newSize); + PCMDeferredPolyData* PX_RESTRICT data = reinterpret_cast(mDeferredContacts->end()); + mDeferredContacts->forceSize_Unsafe(newSize); + + SortedTriangle sortedTriangle; + sortedTriangle.mSquareDist = sqDist; + sortedTriangle.mIndex = mSortedTriangle.size(); + mSortedTriangle.pushBack(sortedTriangle); + + data->mTriangleIndex = triangleIndex; + data->mFeatureIndex = 0; + data->triFlags = PxU8(generateContact); + data->mInds[0] = vertInds[0]; + data->mInds[1] = vertInds[1]; + data->mInds[2] = vertInds[2]; + V3StoreU(closestP, data->mVerts[0]); + V3StoreU(patchNormal, data->mVerts[1]); + V3StoreU(V3Splat(sqDist), data->mVerts[2]); + + } + } + return true; +} + +void Gu::PCMSphereVsMeshContactGeneration::addToPatch(const Ps::aos::Vec3VArg contactP, const Ps::aos::Vec3VArg patchNormal, const Ps::aos::FloatV dist, + const PxU32 triangleIndex) +{ + using namespace Ps::aos; + + PX_ASSERT(mNumContactPatch < PCM_MAX_CONTACTPATCH_SIZE); + + const Vec3V sphereCenter = V3Zero(); // in sphere local space + + bool foundPatch = false; + if (mNumContactPatch > 0) + { + if (FAllGrtr(V3Dot(mContactPatch[mNumContactPatch - 1].mPatchNormal, patchNormal), mAcceptanceEpsilon)) + { + PCMContactPatch& patch = mContactPatch[mNumContactPatch - 1]; + + PX_ASSERT((patch.mEndIndex - patch.mStartIndex) == 1); + + if (FAllGrtr(patch.mPatchMaxPen, dist)) + { + //overwrite the old contact + mManifoldContacts[patch.mStartIndex].mLocalPointA = sphereCenter;//in sphere's space + mManifoldContacts[patch.mStartIndex].mLocalPointB = contactP; + mManifoldContacts[patch.mStartIndex].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(patchNormal), dist); + mManifoldContacts[patch.mStartIndex].mFaceIndex = triangleIndex; + patch.mPatchMaxPen = dist; + } + + foundPatch = true; + } + } + if (!foundPatch) + { + mManifoldContacts[mNumContacts].mLocalPointA = sphereCenter;//in sphere's space + mManifoldContacts[mNumContacts].mLocalPointB = contactP; + mManifoldContacts[mNumContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(patchNormal), dist); + mManifoldContacts[mNumContacts++].mFaceIndex = triangleIndex; + + mContactPatch[mNumContactPatch].mStartIndex = mNumContacts - 1; + mContactPatch[mNumContactPatch].mEndIndex = mNumContacts; + mContactPatch[mNumContactPatch].mPatchMaxPen = dist; + mContactPatch[mNumContactPatch++].mPatchNormal = patchNormal; + } + + PX_ASSERT(mNumContactPatch < PCM_MAX_CONTACTPATCH_SIZE); + + if (mNumContacts >= 16) + { + PX_ASSERT(mNumContacts <= 64); + processContacts(GU_SPHERE_MANIFOLD_CACHE_SIZE); + } +} + +void Gu::PCMSphereVsMeshContactGeneration::generateLastContacts() +{ + using namespace Ps::aos; + // Process delayed contacts + PxU32 nbSortedTriangle = mSortedTriangle.size(); + + if (nbSortedTriangle) + { + Ps::sort(mSortedTriangle.begin(), mSortedTriangle.size(), Ps::Less()); + + const PCMDeferredPolyData* PX_RESTRICT cd = reinterpret_cast(mDeferredContacts->begin()); + + for (PxU32 i = 0; i < nbSortedTriangle; ++i) + { + const PCMDeferredPolyData& currentContact = cd[mSortedTriangle[i].mIndex]; + const PxU32 ref0 = currentContact.mInds[0]; + const PxU32 ref1 = currentContact.mInds[1]; + const PxU32 ref2 = currentContact.mInds[2]; + + PxU8 generateContacts = currentContact.triFlags; + + //if addData sucessful, which means mEdgeCache doesn't has the edge + const bool noEdge01 = mEdgeCache.addData(CachedEdge(ref0, ref1)); + const bool noEdge12 = mEdgeCache.addData(CachedEdge(ref1, ref2)); + const bool noEdge20 = mEdgeCache.addData(CachedEdge(ref2, ref0)); + + const bool needsProcessing = (noEdge01 && noEdge12 && noEdge20 && generateContacts); + + if (needsProcessing) + { + //we store the contact, patch normal and sq distance in the vertex memory in PCMDeferredPolyData + const Vec3V contactP = V3LoadU(currentContact.mVerts[0]); + const Vec3V patchNormal = V3LoadU(currentContact.mVerts[1]); + const FloatV sqDist = FLoad(currentContact.mVerts[2].x); + const FloatV dist = FSqrt(sqDist); + addToPatch(contactP, patchNormal, dist, currentContact.mTriangleIndex); + } + + } + } +} + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEE(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, + const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V n = V3Cross(ab, normal); + const FloatV d = V3Dot(n, a); + const FloatV np = V3Dot(n, p); + const FloatV nq = V3Dot(n,q); + const FloatV signP = FSub(np, d); + const FloatV signQ = FSub(nq, d); + const FloatV temp = FMul(signP, signQ); + if(FAllGrtr(temp, zero)) return;//If both points in the same side as the plane, no intersect points + + // if colliding edge (p3,p4) and plane are parallel return no collision + const Vec3V pq = V3Sub(q, p); + const FloatV npq= V3Dot(n, pq); + if(FAllEq(npq, zero)) return; + + const FloatV one = FOne(); + //calculate the intersect point in the segment pq + const FloatV segTValue = FDiv(FSub(d, np), npq); + const Vec3V localPointA = V3ScaleAdd(pq, segTValue, p); + + //calculate a normal perpendicular to ray localPointA + normal, 2D segment segment intersection + const Vec3V perNormal = V3Cross(normal, pq); + const Vec3V ap = V3Sub(localPointA, a); + const FloatV nom = V3Dot(perNormal, ap); + const FloatV denom = V3Dot(perNormal, ab); + + //const FloatV tValue = FClamp(FDiv(nom, denom), zero, FOne()); + const FloatV tValue = FDiv(nom, denom); + const BoolV con = BAnd(FIsGrtrOrEq(one, tValue), FIsGrtrOrEq(tValue, zero)); + if(BAllEqFFFF(con)) + return; + + //const Vec3V localPointB = V3ScaleAdd(ab, tValue, a); v = V3Sub(localPointA, localPointB); v = V3NegScaleSub(ab, tValue, tap) + const Vec3V v = V3NegScaleSub(ab, tValue, ap); + const FloatV sqDist = V3Dot(v, v); + + if(FAllGrtr(sqInflatedRadius, sqDist)) + { + + const Vec3V localPointB = V3Sub(localPointA, v); + const FloatV signedDist = V3Dot(v, normal); + + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = localPointB; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), signedDist); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + } +} + + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEEContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, + const PxU32 triangleIndex, const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + + using namespace Ps::aos; + generateEE(p, q, sqInflatedRadius, normal, triangleIndex, a, b, manifoldContacts, numContacts); + generateEE(p, q, sqInflatedRadius, normal, triangleIndex, b, c, manifoldContacts, numContacts); + generateEE(p, q, sqInflatedRadius, normal, triangleIndex, a, c, manifoldContacts, numContacts); + +} + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEEMTD(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, + const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V n = V3Cross(ab, normal); + const FloatV d = V3Dot(n, a); + const FloatV np = V3Dot(n, p); + const FloatV nq = V3Dot(n,q); + const FloatV signP = FSub(np, d); + const FloatV signQ = FSub(nq, d); + const FloatV temp = FMul(signP, signQ); + if(FAllGrtr(temp, zero)) return;//If both points in the same side as the plane, no intersect points + + // if colliding edge (p3,p4) and plane are parallel return no collision + const Vec3V pq = V3Sub(q, p); + const FloatV npq= V3Dot(n, pq); + if(FAllEq(npq, zero)) return; + + //calculate the intersect point in the segment pq + const FloatV segTValue = FDiv(FSub(d, np), npq); + const Vec3V localPointA = V3ScaleAdd(pq, segTValue, p); + + //calculate a normal perpendicular to ray localPointA + normal, 2D segment segment intersection + const Vec3V perNormal = V3Cross(normal, pq); + const Vec3V ap = V3Sub(localPointA, a); + const FloatV nom = V3Dot(perNormal, ap); + const FloatV denom = V3Dot(perNormal, ab); + + const FloatV tValue = FClamp(FDiv(nom, denom), zero, FOne()); + + const Vec3V v = V3NegScaleSub(ab, tValue, ap); + const FloatV signedDist = V3Dot(v, normal); + + if(FAllGrtr(inflatedRadius, signedDist)) + { + + const Vec3V localPointB = V3Sub(localPointA, v); + manifoldContacts[numContacts].mLocalPointA = localPointA; + manifoldContacts[numContacts].mLocalPointB = localPointB; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), signedDist); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + } +} + + +void Gu::PCMCapsuleVsMeshContactGeneration::generateEEContactsMTD(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, + const PxU32 triangleIndex, const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + + using namespace Ps::aos; + generateEEMTD(p, q, inflatedRadius, normal, triangleIndex, a, b, manifoldContacts, numContacts); + generateEEMTD(p, q, inflatedRadius, normal, triangleIndex, b, c, manifoldContacts, numContacts); + generateEEMTD(p, q, inflatedRadius, normal, triangleIndex, a, c, manifoldContacts, numContacts); + +} + +bool Gu::PCMCapsuleVsMeshContactGeneration::generateContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg planeNormal, + const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::FloatVArg inflatedRadius, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + + using namespace Ps::aos; + + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V ap = V3Sub(p, a); + const Vec3V aq = V3Sub(q, a); + + //This is used to calculate the barycentric coordinate + const FloatV d00 = V3Dot(ab, ab); + const FloatV d01 = V3Dot(ab, ac); + const FloatV d11 = V3Dot(ac, ac); + const FloatV bdenom = FRecip(FSub(FMul(d00,d11), FMul(d01, d01))); + + //compute the intersect point of p and triangle plane abc + const FloatV inomp = V3Dot(planeNormal, V3Neg(ap)); + const FloatV ideom = V3Dot(planeNormal, normal); + + const FloatV ipt = FSel(FIsGrtr(ideom, FZero()), FDiv(inomp, ideom), FZero()); + //compute the distance from triangle plane abc + const FloatV dist3 = V3Dot(ap, planeNormal); + + const Vec3V closestP31 = V3ScaleAdd(normal, ipt, p); + const Vec3V closestP30 = p; + + + //Compute the barycentric coordinate for project point of q + const Vec3V pV20 = V3Sub(closestP31, a); + const FloatV pD20 = V3Dot(pV20, ab); + const FloatV pD21 = V3Dot(pV20, ac); + const FloatV v0 = FMul(FSub(FMul(d11, pD20), FMul(d01, pD21)), bdenom); + const FloatV w0 = FMul(FSub(FMul(d00, pD21), FMul(d01, pD20)), bdenom); + + //check closestP3 is inside the triangle + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + + + const BoolV tempCon0 = BAnd(con0, FIsGrtr(inflatedRadius, dist3)); + if(BAllEqTTTT(tempCon0)) + { + manifoldContacts[numContacts].mLocalPointA = closestP30;//transform to B space, it will get convert to A space later + manifoldContacts[numContacts].mLocalPointB = closestP31; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), FNeg(ipt)); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + } + + const FloatV inomq = V3Dot(planeNormal, V3Neg(aq)); + + //compute the distance of q and triangle plane abc + const FloatV dist4 = V3Dot(aq, planeNormal); + + const FloatV iqt = FSel(FIsGrtr(ideom, FZero()), FDiv(inomq, ideom), FZero()); + + const Vec3V closestP41 = V3ScaleAdd(normal, iqt, q); + const Vec3V closestP40 = q; + + //Compute the barycentric coordinate for project point of q + const Vec3V qV20 = V3Sub(closestP41, a); + const FloatV qD20 = V3Dot(qV20, ab); + const FloatV qD21 = V3Dot(qV20, ac); + const FloatV v1 = FMul(FSub(FMul(d11, qD20), FMul(d01, qD21)), bdenom); + const FloatV w1 = FMul(FSub(FMul(d00, qD21), FMul(d01, qD20)), bdenom); + + + const BoolV con1 = isValidTriangleBarycentricCoord(v1, w1); + + const BoolV tempCon1 = BAnd(con1, FIsGrtr(inflatedRadius, dist4)); + if(BAllEqTTTT(tempCon1)) + { + manifoldContacts[numContacts].mLocalPointA = closestP40; + manifoldContacts[numContacts].mLocalPointB = closestP41; + manifoldContacts[numContacts].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal),FNeg(iqt)); + manifoldContacts[numContacts++].mFaceIndex = triangleIndex; + + } + + return false; +} + + +/* + t is the barycenteric coordinate of a segment + u is the barycenteric coordinate of a triangle + v is the barycenteric coordinate of a triangle +*/ +Ps::aos::FloatV pcmDistanceSegmentTriangleSquared( const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c, + Ps::aos::FloatV& t, Ps::aos::FloatV& u, Ps::aos::FloatV& v) +{ + using namespace Ps::aos; + + const FloatV one = FOne(); + const FloatV zero = FZero(); + + const Vec3V pq = V3Sub(q, p); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V aq = V3Sub(q, a); + + const Vec3V n =V3Normalize(V3Cross(ab, ac)); // normalize vector + + const Vec4V combinedDot = V3Dot4(ab, ab, ab, ac, ac, ac, ap, n); + const FloatV d00 = V4GetX(combinedDot); + const FloatV d01 = V4GetY(combinedDot); + const FloatV d11 = V4GetZ(combinedDot); + const FloatV dist3 = V4GetW(combinedDot); + + const FloatV bdenom = FRecip(FSub(FMul(d00,d11), FMul(d01, d01))); + + + const FloatV sqDist3 = FMul(dist3, dist3); + + + //compute the closest point of q and triangle plane abc + const FloatV dist4 = V3Dot(aq, n); + const FloatV sqDist4 = FMul(dist4, dist4); + const FloatV dMul = FMul(dist3, dist4); + const BoolV con = FIsGrtr(zero, dMul); + + if(BAllEqTTTT(con)) + { + //compute the intersect point + const FloatV nom = FNeg(V3Dot(n, ap)); + const FloatV denom = FRecip(V3Dot(n, pq)); + const FloatV t0 = FMul(nom, denom); + const Vec3V ip = V3ScaleAdd(pq, t0, p);//V3Add(p, V3Scale(pq, t)); + const Vec3V v2 = V3Sub(ip, a); + const FloatV d20 = V3Dot(v2, ab); + const FloatV d21 = V3Dot(v2, ac); + + const FloatV v0 = FMul(FNegScaleSub(d01, d21, FMul(d11, d20)), bdenom); + const FloatV w0 = FMul(FNegScaleSub(d01, d20, FMul(d00, d21)), bdenom); + + const BoolV con0 = isValidTriangleBarycentricCoord(v0, w0); + if(BAllEqTTTT(con0)) + { + t = t0; + u = v0; + v = w0; + return zero; + } + } + + const Vec3V closestP31 = V3NegScaleSub(n, dist3, p);//V3Sub(p, V3Scale(n, dist3)); + const Vec3V closestP41 = V3NegScaleSub(n, dist4, q);// V3Sub(q, V3Scale(n, dist4)); + + //Compute the barycentric coordinate for project point of q + const Vec3V pV20 = V3Sub(closestP31, a); + const Vec3V qV20 = V3Sub(closestP41, a); + + const Vec4V pD2 = V3Dot4(pV20, ab, pV20, ac, qV20, ab, qV20, ac); + + const Vec4V pD2Swizzle = V4PermYXWZ(pD2); + + const Vec4V d11d00 = V4UnpackXY(V4Splat(d11), V4Splat(d00)); + + const Vec4V v0w0v1w1 = V4Scale(V4NegMulSub(V4Splat(d01), pD2Swizzle, V4Mul(d11d00, pD2)), bdenom); + + const FloatV v0 = V4GetX(v0w0v1w1); + const FloatV w0 = V4GetY(v0w0v1w1); + const FloatV v1 = V4GetZ(v0w0v1w1); + const FloatV w1 = V4GetW(v0w0v1w1); + + const BoolV _con = isValidTriangleBarycentricCoord2(v0w0v1w1); + + const BoolV con0 = BGetX(_con); + const BoolV con1 = BGetY(_con); + + + const BoolV cond2 = BAnd(con0, con1); + + if(BAllEqTTTT(cond2)) + { + /* + both p and q project points are interior point + */ + const BoolV d2 = FIsGrtr(sqDist4, sqDist3); + t = FSel(d2, zero, one); + u = FSel(d2, v0, v1); + v = FSel(d2, w0, w1); + return FSel(d2, sqDist3, sqDist4); + } + else + { + Vec4V t40, t41; + const Vec4V sqDist44 = pcmDistanceSegmentSegmentSquared4(p,pq,a,ab, b,bc, a,ac, a,ab, t40, t41); + + const FloatV t00 = V4GetX(t40); + const FloatV t10 = V4GetY(t40); + const FloatV t20 = V4GetZ(t40); + + const FloatV t01 = V4GetX(t41); + const FloatV t11 = V4GetY(t41); + const FloatV t21 = V4GetZ(t41); + + //edge ab + const FloatV u01 = t01; + const FloatV v01 = zero; + + //edge bc + const FloatV u11 = FSub(one, t11); + const FloatV v11 = t11; + + //edge ac + const FloatV u21 = zero; + const FloatV v21 = t21; + + const FloatV sqDist0(V4GetX(sqDist44)); + const FloatV sqDist1(V4GetY(sqDist44)); + const FloatV sqDist2(V4GetZ(sqDist44)); + + const BoolV con2 = BAnd(FIsGrtr(sqDist1, sqDist0), FIsGrtr(sqDist2, sqDist0)); + const BoolV con3 = FIsGrtr(sqDist2, sqDist1); + const FloatV sqDistPE = FSel(con2, sqDist0, FSel(con3, sqDist1, sqDist2)); + const FloatV uEdge = FSel(con2, u01, FSel(con3, u11, u21)); + const FloatV vEdge = FSel(con2, v01, FSel(con3, v11, v21)); + const FloatV tSeg = FSel(con2, t00, FSel(con3, t10, t20)); + + + if(BAllEqTTTT(con0)) + { + //p's project point is an interior point + const BoolV d2 = FIsGrtr(sqDistPE, sqDist3); + t = FSel(d2, zero, tSeg); + u = FSel(d2, v0, uEdge); + v = FSel(d2, w0, vEdge); + return FSel(d2, sqDist3, sqDistPE); + } + else if(BAllEqTTTT(con1)) + { + //q's project point is an interior point + const BoolV d2 = FIsGrtr(sqDistPE, sqDist4); + t = FSel(d2, one, tSeg); + u = FSel(d2, v1, uEdge); + v = FSel(d2, w1, vEdge); + return FSel(d2, sqDist4, sqDistPE); + } + else + { + t = tSeg; + u = uEdge; + v = vEdge; + return sqDistPE; + } + + } + +} + + +static bool selectNormal(const Ps::aos::FloatVArg u, Ps::aos::FloatVArg v, PxU8 data) +{ + using namespace Ps::aos; + const FloatV zero = FLoad(1e-6f); + const FloatV one = FLoad(0.999999f); + // Analysis + if(FAllGrtr(zero, u)) + { + if(FAllGrtr(zero, v)) + { + // Vertex 0 + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_20))) + return true; + } + else if(FAllGrtr(v, one)) + { + // Vertex 2 + if(!(data & (Gu::ETD_CONVEX_EDGE_12|Gu::ETD_CONVEX_EDGE_20))) + return true; + } + else + { + // Edge 0-2 + if(!(data & Gu::ETD_CONVEX_EDGE_20)) + return true; + } + } + else if(FAllGrtr(u,one)) + { + if(FAllGrtr(zero, v)) + { + // Vertex 1 + if(!(data & (Gu::ETD_CONVEX_EDGE_01|Gu::ETD_CONVEX_EDGE_12))) + return true; + + } + } + else + { + if(FAllGrtr(zero, v)) + { + // Edge 0-1 + if(!(data & Gu::ETD_CONVEX_EDGE_01)) + return true; + } + else + { + const FloatV threshold = FLoad(0.9999f); + const FloatV temp = FAdd(u, v); + if(FAllGrtrOrEq(temp, threshold)) + { + // Edge 1-2 + if(!(data & Gu::ETD_CONVEX_EDGE_12)) + return true; + } + else + { + // Face + return true; + } + } + } + return false; +} + + +bool Gu::PCMCapsuleVsMeshContactGeneration::processTriangle(const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds) +{ + PX_UNUSED(triangleIndex); + PX_UNUSED(vertInds); + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const Vec3V p0 = V3LoadU(verts[0]); + const Vec3V p1 = V3LoadU(verts[1]); + const Vec3V p2 = V3LoadU(verts[2]); + + const Vec3V p10 = V3Sub(p1, p0); + const Vec3V p20 = V3Sub(p2, p0); + + const Vec3V n = V3Normalize(V3Cross(p10, p20));//(p1 - p0).cross(p2 - p0).getNormalized(); + const FloatV d = V3Dot(p0, n);//d = -p0.dot(n); + + const FloatV dist = FSub(V3Dot(mCapsule.getCenter(), n), d);//p.dot(n) + d; + + // Backface culling + if(FAllGrtr(zero, dist)) + return false; + + + FloatV t, u, v; + const FloatV sqDist = pcmDistanceSegmentTriangleSquared(mCapsule.p0, mCapsule.p1, p0, p1, p2, t, u, v); + + if(FAllGrtr(mSqInflatedRadius, sqDist)) + { + + Vec3V patchNormalInTriangle; + if(selectNormal(u, v, triFlags)) + { + patchNormalInTriangle = n; + } + else + { + if(FAllEq(sqDist, zero)) + { + //segment intersect with the triangle + patchNormalInTriangle = n; + } + else + { + const Vec3V pq = V3Sub(mCapsule.p1, mCapsule.p0); + const Vec3V pointOnSegment = V3ScaleAdd(pq, t, mCapsule.p0); + const FloatV w = FSub(FOne(), FAdd(u, v)); + const Vec3V pointOnTriangle = V3ScaleAdd(p0, w, V3ScaleAdd(p1, u, V3Scale(p2, v))); + patchNormalInTriangle = V3Normalize(V3Sub(pointOnSegment, pointOnTriangle)); + + } + } + + const PxU32 previousNumContacts = mNumContacts; + + generateContacts(p0, p1, p2, n, patchNormalInTriangle, triangleIndex, mCapsule.p0, mCapsule.p1, mInflatedRadius, mManifoldContacts, mNumContacts); + //ML: this need to use the sqInflatedRadius to avoid some bad contacts + generateEEContacts(p0, p1, p2,patchNormalInTriangle, triangleIndex, mCapsule.p0, mCapsule.p1, mSqInflatedRadius, mManifoldContacts, mNumContacts); + + + PxU32 numContacts = mNumContacts - previousNumContacts; + + if(numContacts > 0) + { + FloatV maxPen = FMax(); + for(PxU32 i = previousNumContacts; i= 16) + { + PX_ASSERT(mNumContacts <= 64); + processContacts(GU_CAPSULE_MANIFOLD_CACHE_SIZE); + } + } + } + + return true; +} + +bool Gu::PCMCapsuleVsMeshContactGeneration::processTriangle(const TriangleV& triangleV, const PxU32 triangleIndex, const CapsuleV& capsule, const Ps::aos::FloatVArg inflatedRadius, const PxU8 trigFlag, + Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts) +{ + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const Vec3V p0 = triangleV.verts[0]; + const Vec3V p1 = triangleV.verts[1]; + const Vec3V p2 = triangleV.verts[2]; + + const Vec3V n = triangleV.normal(); + + const FloatV sqInflatedRadius = FMul(inflatedRadius, inflatedRadius); + + + FloatV t, u, v; + const FloatV sqDist = pcmDistanceSegmentTriangleSquared(capsule.p0, capsule.p1, p0, p1, p2, t, u, v); + + if(FAllGrtr(sqInflatedRadius, sqDist)) + { + + Vec3V patchNormalInTriangle; + if(selectNormal(u, v, trigFlag)) + { + patchNormalInTriangle = n; + } + else + { + if(FAllEq(sqDist, zero)) + { + //segment intersect with the triangle + patchNormalInTriangle = n; + } + else + { + const Vec3V pq = V3Sub(capsule.p1, capsule.p0); + const Vec3V pointOnSegment = V3ScaleAdd(pq, t, capsule.p0); + const FloatV w = FSub(FOne(), FAdd(u, v)); + const Vec3V pointOnTriangle = V3ScaleAdd(p0, w, V3ScaleAdd(p1, u, V3Scale(p2, v))); + patchNormalInTriangle = V3Normalize(V3Sub(pointOnSegment, pointOnTriangle)); + } + } + + generateContacts(p0, p1, p2, n, patchNormalInTriangle, triangleIndex, capsule.p0, capsule.p1, inflatedRadius, manifoldContacts, numContacts); + + generateEEContactsMTD(p0, p1, p2, patchNormalInTriangle, triangleIndex, capsule.p0, capsule.p1, inflatedRadius, manifoldContacts, numContacts); + + + } + + return true; + +} + +} +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h new file mode 100644 index 000000000..2e46431b6 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h @@ -0,0 +1,437 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_CONVEX_COMMON_H +#define GU_PCM_CONTACT_CONVEX_COMMON_H + +#define PCM_MAX_CONTACTPATCH_SIZE 32 + +#include "GuContactBuffer.h" +#include "GuVecCapsule.h" +#include "GuPCMTriangleContactGen.h" +#include "PsInlineArray.h" +#include "GuTriangleCache.h" + +namespace physx +{ + +namespace Gu +{ + +#define MAX_CACHE_SIZE 128 + +class PCMMeshContactGeneration +{ + PX_NOCOPY(PCMMeshContactGeneration) +public: + PCMContactPatch mContactPatch[PCM_MAX_CONTACTPATCH_SIZE]; + PCMContactPatch* mContactPatchPtr[PCM_MAX_CONTACTPATCH_SIZE]; + const Ps::aos::FloatV mContactDist; + const Ps::aos::FloatV mReplaceBreakingThreshold; + const Ps::aos::PsTransformV& mConvexTransform; + const Ps::aos::PsTransformV& mMeshTransform; + Gu::MultiplePersistentContactManifold& mMultiManifold; + Gu::ContactBuffer& mContactBuffer; + + Ps::aos::FloatV mAcceptanceEpsilon; + Ps::aos::FloatV mSqReplaceBreakingThreshold; + Ps::aos::PsMatTransformV mMeshToConvex; + Gu::MeshPersistentContact* mManifoldContacts; + PxU32 mNumContacts; + PxU32 mNumContactPatch; + PxU32 mNumCalls; + Gu::CacheMap mEdgeCache; + Ps::InlineArray* mDeferredContacts; + Cm::RenderOutput* mRenderOutput; + + PCMMeshContactGeneration( + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& convexTransform, + const Ps::aos::PsTransformV& meshTransform, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Ps::InlineArray* deferredContacts, + Cm::RenderOutput* renderOutput + + ) : + mContactDist(contactDist), + mReplaceBreakingThreshold(replaceBreakingThreshold), + mConvexTransform(convexTransform), + mMeshTransform(meshTransform), + mMultiManifold(multiManifold), + mContactBuffer(contactBuffer), + mDeferredContacts(deferredContacts), + mRenderOutput(renderOutput) + + { + using namespace Ps::aos; + mNumContactPatch = 0; + mNumContacts = 0; + mNumCalls = 0; + + mMeshToConvex = mConvexTransform.transformInv(mMeshTransform); + + //Assign the PCMContactPatch to the PCMContactPathPtr + for(PxU32 i=0; i + bool processTriangleCache(Gu::TriangleCache& cache) + { + PxU32 count = cache.mNumTriangles; + PxVec3* verts = cache.mVertices; + PxU32* vertInds = cache.mIndices; + PxU32* triInds = cache.mTriangleIndex; + PxU8* edgeFlags = cache.mEdgeFlags; + while(count--) + { + (static_cast(this))->processTriangle(verts, *triInds, *edgeFlags, vertInds); + verts += 3; + vertInds += 3; + triInds++; + edgeFlags++; + } + return true; + } + void prioritizeContactPatches(); + void addManifoldPointToPatch(const Ps::aos::Vec3VArg currentPatchNormal, const Ps::aos::FloatVArg maxPen, const PxU32 previousNumContacts); + void processContacts(const PxU8 maxContactPerManifold, const bool isNotLastPatch = true); +}; + +/* + This function is based on the current patch normal to either create a new patch or merge the manifold contacts in this patch with the manifold contacts in the last existing + patch. This means there might be more than GU_SINGLE_MANIFOLD_CACHE_SIZE in a SinglePersistentContactManifold. +*/ +PX_FORCE_INLINE void PCMMeshContactGeneration::addManifoldPointToPatch(const Ps::aos::Vec3VArg currentPatchNormal, const Ps::aos::FloatVArg maxPen, const PxU32 previousNumContacts) +{ + using namespace Ps::aos; + + bool foundPatch = false; + //we have existing patch + if(mNumContactPatch > 0) + { + //if the direction between the last existing patch normal and the current patch normal are within acceptance epsilon, which means we will be + //able to merge the last patch's contacts with the current patch's contacts. This is just to avoid to create an extra patch. We have some logic + //later to refine the patch again + if(FAllGrtr(V3Dot(mContactPatch[mNumContactPatch-1].mPatchNormal, currentPatchNormal), mAcceptanceEpsilon)) + { + //get the last patch + PCMContactPatch& patch = mContactPatch[mNumContactPatch-1]; + + //remove duplicate contacts + for(PxU32 i = patch.mStartIndex; imPatchMaxPen, mContactPatchPtr[i]->mPatchMaxPen)) + { + //swap + PCMContactPatch* tmp = mContactPatchPtr[indexi]; + mContactPatchPtr[indexi] = mContactPatchPtr[i]; + mContactPatchPtr[i] = tmp; + + for(PxI32 j=PxI32(i-2); j>=0; j--) + { + const PxU32 indexj = PxU32(j+1); + if(FAllGrtrOrEq(mContactPatchPtr[indexj]->mPatchMaxPen, mContactPatchPtr[j]->mPatchMaxPen)) + break; + //swap + PCMContactPatch* temp = mContactPatchPtr[indexj]; + mContactPatchPtr[indexj] = mContactPatchPtr[j]; + mContactPatchPtr[j] = temp; + } + } + } +} + + +PX_FORCE_INLINE void PCMMeshContactGeneration::processContacts(const PxU8 maxContactPerManifold, bool isNotLastPatch) +{ + using namespace Ps::aos; + + if(mNumContacts != 0) + { + //reorder the contact patches based on the max penetration + prioritizeContactPatches(); + //connect the patches which's angle between patch normals are within 5 degree + mMultiManifold.refineContactPatchConnective(mContactPatchPtr, mNumContactPatch, mManifoldContacts, mAcceptanceEpsilon); + //get rid of duplicate manifold contacts in connected contact patches + mMultiManifold.reduceManifoldContactsInDifferentPatches(mContactPatchPtr, mNumContactPatch, mManifoldContacts, mNumContacts, mSqReplaceBreakingThreshold); + //add the manifold contact to the corresponding manifold + mMultiManifold.addManifoldContactPoints(mManifoldContacts, mNumContacts, mContactPatchPtr, mNumContactPatch, mSqReplaceBreakingThreshold, mAcceptanceEpsilon, maxContactPerManifold); + + mNumContacts = 0; + mNumContactPatch = 0; + + if(isNotLastPatch) + { + //remap the contact patch pointer to contact patch + for(PxU32 i=0; i mVertexCache; + Ps::aos::Vec3V mHullCenterMesh; + + const Gu::PolygonalData& mPolyData; + SupportLocal* mPolyMap; + const Cm::FastVertex2ShapeScaling& mConvexScaling; + bool mIdtConvexScale; + bool mSilhouetteEdgesAreActive; + + PCMConvexVsMeshContactGeneration( + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& convexTransform, + const Ps::aos::PsTransformV& meshTransform, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + + const Gu::PolygonalData& polyData, + SupportLocal* polyMap, + Ps::InlineArray* delayedContacts, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale, + bool silhouetteEdgesAreActive, + Cm::RenderOutput* renderOutput + + ) : PCMMeshContactGeneration(contactDistance, replaceBreakingThreshold, convexTransform, meshTransform, multiManifold, contactBuffer, + delayedContacts, renderOutput), + mPolyData(polyData), + mPolyMap(polyMap), + mConvexScaling(convexScaling), + mIdtConvexScale(idtConvexScale), + mSilhouetteEdgesAreActive(silhouetteEdgesAreActive) + { + using namespace Ps::aos; + + // Hull center in local space + const Vec3V hullCenterLocal = V3LoadU(mPolyData.mCenter); + // Hull center in mesh space + mHullCenterMesh = mMeshToConvex.transformInv(hullCenterLocal); + + } + + bool generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU32* triIndices, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal); + + bool generatePolyDataContactManifold(Gu::TriangleV& localTriangle, const PxU32 featureIndex, const PxU32 triangleIndex, const PxU8 triFlags, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal); + void generateLastContacts(); + void addContactsToPatch(const Ps::aos::Vec3VArg patchNormal, const PxU32 previousNumContacts); + + bool processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + + static bool generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal, Cm::RenderOutput* renderOutput = NULL); + + static bool processTriangle(const Gu::PolygonalData& polyData, SupportLocal* polyMap, const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags, const Ps::aos::FloatVArg inflation, const bool isDoubleSided, + const Ps::aos::PsTransformV& convexTransform, const Ps::aos::PsMatTransformV& meshToConvex, Gu::MeshPersistentContact* manifoldContact, PxU32& numContacts); +}; + +#if PX_VC + #pragma warning(pop) +#endif + +struct SortedTriangle +{ + Ps::aos::FloatV mSquareDist; + PxU32 mIndex; + + PX_FORCE_INLINE bool operator < (const SortedTriangle& data) const + { + return Ps::aos::FAllGrtrOrEq(mSquareDist, data.mSquareDist) ==0; + } +}; + +class PCMSphereVsMeshContactGeneration : public PCMMeshContactGeneration +{ +public: + Ps::aos::Vec3V mSphereCenter; + Ps::aos::FloatV mSphereRadius; + Ps::aos::FloatV mSqInflatedSphereRadius; + Ps::InlineArray mSortedTriangle; + + PCMSphereVsMeshContactGeneration( + const Ps::aos::Vec3VArg sphereCenter, + const Ps::aos::FloatVArg sphereRadius, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& sphereTransform, + const Ps::aos::PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + Ps::InlineArray* deferredContacts, + Cm::RenderOutput* renderOutput = NULL + + ) : PCMMeshContactGeneration(contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, + contactBuffer, deferredContacts, renderOutput), + mSphereCenter(sphereCenter), + mSphereRadius(sphereRadius) + { + using namespace Ps::aos; + const FloatV inflatedSphereRadius = FAdd(sphereRadius, contactDist); + mSqInflatedSphereRadius = FMul(inflatedSphereRadius, inflatedSphereRadius); + } + + bool processTriangle(const PxVec3* verts, PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + void generateLastContacts(); + void addToPatch(const Ps::aos::Vec3VArg contactP, const Ps::aos::Vec3VArg patchNormal, + const Ps::aos::FloatV pen, const PxU32 triangleIndex); +}; + +class PCMCapsuleVsMeshContactGeneration : public PCMMeshContactGeneration +{ + PCMCapsuleVsMeshContactGeneration &operator=(PCMCapsuleVsMeshContactGeneration &); +public: + Ps::aos::FloatV mInflatedRadius; + Ps::aos::FloatV mSqInflatedRadius; + const CapsuleV& mCapsule; + + + PCMCapsuleVsMeshContactGeneration( + const CapsuleV& capsule, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Ps::aos::PsTransformV& sphereTransform, + const Ps::aos::PsTransformV& meshTransform, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Ps::InlineArray* deferredContacts, + Cm::RenderOutput* renderOutput = NULL + + ) : PCMMeshContactGeneration(contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, contactBuffer, + deferredContacts, renderOutput), + mCapsule(capsule) + { + using namespace Ps::aos; + mInflatedRadius = FAdd(capsule.radius, contactDist); + mSqInflatedRadius = FMul(mInflatedRadius, mInflatedRadius); + } + + void generateEEContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b,const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + void generateEE(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg sqInflatedRadius, const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + static bool generateContacts(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b,const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg planeNormal, const Ps::aos::Vec3VArg normal, + const PxU32 triangleIndex, const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + static void generateEEContactsMTD(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b,const Ps::aos::Vec3VArg c, const Ps::aos::Vec3VArg normal, const PxU32 triangleIndex, + const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + static void generateEEMTD(const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg q, const Ps::aos::FloatVArg inflatedRadius, const Ps::aos::Vec3VArg normal, const PxU32 trianlgeIndex, + const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); + + bool processTriangle(const PxVec3* verts, const PxU32 triangleIndex, PxU8 triFlags, const PxU32* vertInds); + + static bool processTriangle(const TriangleV& triangle, const PxU32 triangleIndex, const CapsuleV& capsule, const Ps::aos::FloatVArg inflatedRadius, const PxU8 triFlag, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts); +}; + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp new file mode 100644 index 000000000..27deee46c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp @@ -0,0 +1,294 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMShapeConvex.h" +#include "GuPCMContactGen.h" +#include "GuContactBuffer.h" + + +namespace physx +{ +using namespace Ps::aos; + +namespace Gu +{ + +static bool fullContactsGenerationConvexConvex(const GjkConvex* relativeConvex, const GjkConvex* localConvex, const PsTransformV& transf0, const PsTransformV& transf1, + const bool idtScale0, const bool idtScale1, PersistentContact* manifoldContacts, ContactBuffer& contactBuffer, + PersistentContactManifold& manifold, Vec3VArg normal, const Vec3VArg closestA, const Vec3VArg closestB, + const FloatVArg contactDist, const bool doOverlapTest, Cm::RenderOutput* renderOutput, const PxReal toleranceLength) +{ + Gu::PolygonalData polyData0, polyData1; + const ConvexHullV& convexHull0 = relativeConvex->getConvex(); + const ConvexHullV& convexHull1 = localConvex->getConvex(); + getPCMConvexData(convexHull0, idtScale0, polyData0); + getPCMConvexData(convexHull1, idtScale1, polyData1); + + PxU8 buff0[sizeof(SupportLocalImpl)]; + PxU8 buff1[sizeof(SupportLocalImpl)]; + + SupportLocal* map0 = (idtScale0 ? static_cast(PX_PLACEMENT_NEW(buff0, SupportLocalImpl)(static_cast(convexHull0), transf0, convexHull0.vertex2Shape, convexHull0.shape2Vertex, idtScale0)) : + static_cast(PX_PLACEMENT_NEW(buff0, SupportLocalImpl)(convexHull0, transf0, convexHull0.vertex2Shape, convexHull0.shape2Vertex, idtScale0))); + + SupportLocal* map1 = (idtScale1 ? static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(static_cast(convexHull1), transf1, convexHull1.vertex2Shape, convexHull1.shape2Vertex, idtScale1)) : + static_cast(PX_PLACEMENT_NEW(buff1, SupportLocalImpl)(convexHull1, transf1, convexHull1.vertex2Shape, convexHull1.shape2Vertex, idtScale1))); + + + PxU32 numContacts = 0; + + if(generateFullContactManifold(polyData0, polyData1, map0, map1, manifoldContacts, numContacts, contactDist, normal, closestA, closestB, convexHull0.getMarginF(), + convexHull1.getMarginF(), doOverlapTest, renderOutput, toleranceLength)) + { + + if (numContacts > 0) + { + //reduce contacts + manifold.addBatchManifoldContacts(manifoldContacts, numContacts, toleranceLength); + + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + //add the manifold contacts; + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + } + else + { + //if doOverlapTest is true, which means GJK/EPA degenerate so we won't have any contact in the manifoldContacts array + if (!doOverlapTest) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + } + + } + return true; + + } + + return false; + +} + +static bool generateOrProcessContactsConvexConvex(const GjkConvex* relativeConvex, const GjkConvex* localConvex, const PsTransformV& transf0, const PsTransformV& transf1, + const PsMatTransformV& aToB, GjkStatus status, GjkOutput& output, PersistentContactManifold& manifold, ContactBuffer& contactBuffer, + const PxU32 initialContacts, const FloatV minMargin, const FloatV contactDist, + const bool idtScale0, const bool idtScale1, const PxReal toleranceLength, Cm::RenderOutput* renderOutput) +{ + + if (status == GJK_NON_INTERSECT) + { + return false; + } + else + { + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + + const Vec3V localNor = manifold.mNumContacts ? manifold.getLocalNormal() : V3Zero(); + + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + //addGJKEPAContacts will increase the number of contacts in manifold. If status == EPA_CONTACT, we need to run epa algorithm and generate closest points, normal and + //pentration. If epa doesn't degenerate, we will store the contacts information in the manifold. Otherwise, we will return true to do the fallback test + const bool doOverlapTest = addGJKEPAContacts(relativeConvex, localConvex, aToB, status, manifoldContacts, replaceBreakingThreshold, FLoad(toleranceLength), output, manifold); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + //ML: after we refresh the contacts(newContacts) and generate a GJK/EPA contacts(we will store that in the manifold), if the number of contacts is still less than the original contacts, + //which means we lose too mang contacts and we should regenerate all the contacts in the current configuration + //Also, we need to look at the existing contacts, if the existing contacts has very different normal than the GJK/EPA contacts, + //which means we should throw away the existing contacts and do full contact gen + const bool fullContactGen = FAllGrtr(FLoad(0.707106781f), V3Dot(localNor, output.normal)) || (manifold.mNumContacts < initialContacts); + + if (fullContactGen || doOverlapTest) + { + return fullContactsGenerationConvexConvex(relativeConvex, localConvex, transf0, transf1, idtScale0, idtScale1, manifoldContacts, contactBuffer, + manifold, output.normal, output.closestA, output.closestB, contactDist, doOverlapTest, renderOutput, toleranceLength); + } + else + { + const Vec3V newLocalNor = V3Add(localNor, output.normal); + const Vec3V worldNormal = V3Normalize(transf1.rotate(newLocalNor)); + //const Vec3V worldNormal = transf1.rotate(normal); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); + return true; + } + } +} + +static bool convexHullNoScale0(const ConvexHullV& convexHull0, const ConvexHullV& convexHull1, const PsTransformV& transf0, const PsTransformV& transf1, + const PsMatTransformV& aToB, GjkOutput& output, PersistentContactManifold& manifold, ContactBuffer& contactBuffer, + const PxU32 initialContacts, const FloatV minMargin, const FloatV contactDist, + const bool idtScale1, const PxReal toleranceLength, Cm::RenderOutput* renderOutput) +{ + + const RelativeConvex convexA(static_cast(convexHull0), aToB); + if(idtScale1) + { + const LocalConvex convexB(static_cast(convexHull1)); + GjkStatus status = gjkPenetration, LocalConvex >(convexA, convexB, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + return generateOrProcessContactsConvexConvex(&convexA, &convexB, transf0, transf1, aToB, status, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, true, true, toleranceLength, renderOutput); + + } + else + { + const LocalConvex convexB(convexHull1); + GjkStatus status = gjkPenetration, LocalConvex >(convexA, convexB, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + + return generateOrProcessContactsConvexConvex(&convexA, &convexB, transf0, transf1, aToB, status, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, true, false, toleranceLength, renderOutput); + } +} + +static bool convexHullHasScale0(const ConvexHullV& convexHull0, const ConvexHullV& convexHull1, const PsTransformV& transf0, const PsTransformV& transf1, + const PsMatTransformV& aToB, GjkOutput& output, PersistentContactManifold& manifold, ContactBuffer& contactBuffer, + const PxU32 initialContacts, const FloatV minMargin, const FloatV contactDist, + const bool idtScale1, const PxReal toleranceLength, Cm::RenderOutput* renderOutput) +{ + + RelativeConvex convexA(convexHull0, aToB); + if(idtScale1) + { + LocalConvex convexB(static_cast(convexHull1)); + GjkStatus status = gjkPenetration< RelativeConvex, LocalConvex >(convexA, convexB, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints,output); + + return generateOrProcessContactsConvexConvex(&convexA, &convexB, transf0, transf1, aToB, status, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, false, true, toleranceLength, renderOutput); + + } + else + { + LocalConvex convexB(convexHull1); + GjkStatus status = gjkPenetration, LocalConvex >(convexA, convexB, aToB.p, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + return generateOrProcessContactsConvexConvex(&convexA, &convexB, transf0, transf1, aToB, status, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, false, false, toleranceLength, renderOutput); + } +} + + +bool pcmContactConvexConvex(GU_CONTACT_METHOD_ARGS) +{ + const PxConvexMeshGeometryLL& shapeConvex0 = shape0.get(); + const PxConvexMeshGeometryLL& shapeConvex1 = shape1.get(); + PersistentContactManifold& manifold = cache.getManifold(); + + Ps::prefetchLine(shapeConvex0.hullData); + Ps::prefetchLine(shapeConvex1.hullData); + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + const Vec3V vScale0 = V3LoadU_SafeReadW(shapeConvex0.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const Vec3V vScale1 = V3LoadU_SafeReadW(shapeConvex1.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const FloatV contactDist = FLoad(params.mContactDistance); + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const Gu::ConvexHullData* hullData0 = shapeConvex0.hullData; + const Gu::ConvexHullData* hullData1 = shapeConvex1.hullData; + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV convexMargin0 = Gu::CalculatePCMConvexMargin(hullData0, vScale0, toleranceLength); + const FloatV convexMargin1 = Gu::CalculatePCMConvexMargin(hullData1, vScale1, toleranceLength); + + const PxU32 initialContacts = manifold.mNumContacts; + + const FloatV minMargin = FMin(convexMargin0, convexMargin1); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + + manifold.refreshContactPoints(aToB, projectBreakingThreshold, contactDist); + + //ML: after refreshContactPoints, we might lose some contacts + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + if(bLostContacts || manifold.invalidate_BoxConvex(curRTrans, minMargin)) + { + manifold.setRelativeTransform(curRTrans); + + const bool idtScale0 = shapeConvex0.scale.isIdentity(); + const bool idtScale1 = shapeConvex1.scale.isIdentity(); + const QuatV vQuat0 = QuatVLoadU(&shapeConvex0.scale.rotation.x); + const QuatV vQuat1 = QuatVLoadU(&shapeConvex1.scale.rotation.x); + + Gu::ConvexHullV convexHull0(hullData0, V3LoadU(hullData0->mCenterOfMass), vScale0, vQuat0, idtScale0); + Gu::ConvexHullV convexHull1(hullData1, V3LoadU(hullData1->mCenterOfMass), vScale1, vQuat1, idtScale1); + + GjkOutput output; + + if(idtScale0) + { + return convexHullNoScale0(convexHull0, convexHull1, transf0, transf1, aToB, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, idtScale1, toleranceLength, renderOutput); + } + else + { + return convexHullHasScale0(convexHull0, convexHull1, transf0, transf1, aToB, output, manifold, + contactBuffer, initialContacts, minMargin, contactDist, idtScale1, toleranceLength, renderOutput); + } + + } + else if(manifold.getNumContacts()> 0) + { + const Vec3V worldNormal = manifold.getWorldNormal(transf1); + manifold.addManifoldContactsToContactBuffer(contactBuffer, worldNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return true; + } + + return false; + +} +} +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp new file mode 100644 index 000000000..afcceee2b --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp @@ -0,0 +1,267 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMShapeConvex.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuHeightField.h" +#include "GuHeightFieldUtil.h" +#include "GuPCMContactConvexCommon.h" +#include "GuPCMContactMeshCallback.h" + +#include "PsVecMath.h" + + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMConvexVsHeightfieldContactGenerationCallback + : PCMHeightfieldContactGenerationCallback< PCMConvexVsHeightfieldContactGenerationCallback > +{ + PCMConvexVsHeightfieldContactGenerationCallback& operator=(const PCMConvexVsHeightfieldContactGenerationCallback&); +public: + PCMConvexVsMeshContactGeneration mGeneration; + + PCMConvexVsHeightfieldContactGenerationCallback( + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const Gu::PolygonalData& polyData, + SupportLocal* polyMap, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale, + const PsTransformV& convexTransform, + const PsTransformV& heightfieldTransform, + const PxTransform& heightfieldTransform1, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Gu::HeightFieldUtil& hfUtil, + Ps::InlineArray* delayedContacts, + bool silhouetteEdgesAreActive, + Cm::RenderOutput* renderOutput = NULL + + ) : + PCMHeightfieldContactGenerationCallback< PCMConvexVsHeightfieldContactGenerationCallback >(hfUtil, heightfieldTransform1), + mGeneration(contactDistance, replaceBreakingThreshold, convexTransform, heightfieldTransform, multiManifold, + contactBuffer, polyData, polyMap, delayedContacts, convexScaling, idtConvexScale, silhouetteEdgesAreActive, renderOutput) + { + } + + template + void processTriangleCache(Gu::TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + +bool Gu::PCMContactConvexHeightfield( + const Gu::PolygonalData& polyData, Gu::SupportLocal* polyMap, const Ps::aos::FloatVArg minMargin, + const PxBounds3& hullAABB, const PxHeightFieldGeometry& shapeHeightfield, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, Gu::ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, bool idtConvexScale, + Gu::MultiplePersistentContactManifold& multiManifold, Cm::RenderOutput* renderOutput) + +{ + + using namespace Ps::aos; + using namespace Gu; + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV contactDist = FLoad(contactDistance); + //Transfer A into the local space of B + const PsTransformV convexTransform(p0, q0);//box + const PsTransformV heightfieldTransform(p1, q1);//heightfield + const PsTransformV curTransform = heightfieldTransform.transformInv(convexTransform); + + + if(multiManifold.invalidate(curTransform, minMargin)) + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + //////////////////// + + const PxTransform t0to1 = transform1.transformInv(transform0); + + Gu::HeightFieldUtil hfUtil(shapeHeightfield); + const Gu::HeightField& hf = hfUtil.getHeightField(); + + //////////////////// + + /*const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + + const PxU8* PX_RESTRICT extraData = meshData->mExtraTrigData;*/ + + Ps::InlineArray delayedContacts; + + PCMConvexVsHeightfieldContactGenerationCallback blockCallback( + contactDist, + replaceBreakingThreshold, + polyData, + polyMap, + convexScaling, + idtConvexScale, + convexTransform, + heightfieldTransform, + transform1, + multiManifold, + contactBuffer, + hfUtil, + &delayedContacts, + !(hf.getFlags() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES), + renderOutput + ); + + hfUtil.overlapAABBTriangles(transform1, PxBounds3::transformFast(t0to1, hullAABB), 0, &blockCallback); + + PX_ASSERT(multiManifold.mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + blockCallback.mGeneration.generateLastContacts(); + blockCallback.mGeneration.processContacts(GU_SINGLE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.6f)); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, contactDist); + } + +#if PCM_LOW_LEVEL_DEBUG + multiManifold.drawManifold(*renderOutput, convexTransform, heightfieldTransform); +#endif + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, heightfieldTransform); + +} + + +bool Gu::pcmContactConvexHeightField(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + const PxConvexMeshGeometryLL& shapeConvex = shape0.get(); + const physx::PxHeightFieldGeometryLL& shapHeightField = shape1.get(); + + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + Gu::MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const PsTransformV convexTransform(p0, q0); + + //const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + //Cm::FastVertex2ShapeScaling meshScaling; + //if(!idtScaleMesh) + // meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData; + const bool idtScaleConvex = getPCMConvexData(shape0, convexScaling, hullAABB, polyData); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV minMargin = Gu::CalculatePCMConvexMargin(hullData, vScale, toleranceLength, GU_PCM_MESH_MANIFOLD_EPSILON); + + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + Gu::ConvexHullV convexHull(hullData, V3Zero(), vScale, vQuat, shapeConvex.scale.isIdentity()); + + if(idtScaleConvex) + { + SupportLocalImpl convexMap(static_cast(convexHull), convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex); + return Gu::PCMContactConvexHeightfield(polyData, &convexMap, minMargin, hullAABB, shapHeightField, transform0, transform1, params.mContactDistance, contactBuffer, convexScaling, + idtScaleConvex, multiManifold, renderOutput); + } + else + { + SupportLocalImpl convexMap(convexHull, convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScaleConvex); + return Gu::PCMContactConvexHeightfield(polyData, &convexMap, minMargin, hullAABB, shapHeightField, transform0, transform1, params.mContactDistance, contactBuffer, convexScaling, + idtScaleConvex, multiManifold, renderOutput); + } +} + +bool Gu::pcmContactBoxHeightField(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const PxBoxGeometry& shapeBox = shape0.get(); + const physx::PxHeightFieldGeometryLL& shapHeightField = shape1.get(); + + const PxVec3 ext = shapeBox.halfExtents + PxVec3(params.mContactDistance); + const PxBounds3 hullAABB(-ext, ext); + + Cm::FastVertex2ShapeScaling idtScaling; + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const PxReal toranceLength = params.mToleranceLength; + const FloatV minMargin = Gu::CalculatePCMBoxMargin(boxExtents, toranceLength, GU_PCM_MESH_MANIFOLD_EPSILON); + + Gu::BoxV boxV(V3Zero(), boxExtents); + + const PsTransformV boxTransform(p0, q0);//box + + Gu::PolygonalData polyData; + Gu::PCMPolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData); + + Mat33V identity = M33Identity(); + //SupportLocalImpl boxMap(boxV, boxTransform, identity, identity); + SupportLocalImpl boxMap(boxV, boxTransform, identity, identity, true); + + return Gu::PCMContactConvexHeightfield(polyData, &boxMap, minMargin, hullAABB, shapHeightField, transform0, transform1, params.mContactDistance, contactBuffer, + idtScaling, true, multiManifold, renderOutput); +} +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp new file mode 100644 index 000000000..07ca96521 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp @@ -0,0 +1,259 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuPCMShapeConvex.h" +#include "GuConvexUtilsInternal.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuPCMContactConvexCommon.h" +#include "GuPCMContactMeshCallback.h" +#include "GuIntersectionTriangleBox.h" +#include "GuBox.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMConvexVsMeshContactGenerationCallback : PCMMeshContactGenerationCallback +{ + PCMConvexVsMeshContactGenerationCallback& operator=(const PCMConvexVsMeshContactGenerationCallback&); +public: + PCMConvexVsMeshContactGeneration mGeneration; + const BoxPadded& mBox; + + PCMConvexVsMeshContactGenerationCallback( + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const PsTransformV& convexTransform, + const PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + const PolygonalData& polyData, + SupportLocal* polyMap, + Ps::InlineArray* delayedContacts, + const Cm::FastVertex2ShapeScaling& convexScaling, + bool idtConvexScale, + const Cm::FastVertex2ShapeScaling& meshScaling, + const PxU8* extraTriData, + bool idtMeshScale, + bool silhouetteEdgesAreActive, + const BoxPadded& box, + Cm::RenderOutput* renderOutput = NULL + + ) : + PCMMeshContactGenerationCallback(meshScaling, extraTriData, idtMeshScale), + mGeneration(contactDistance, replaceBreakingThreshold, convexTransform, meshTransform, multiManifold, contactBuffer, polyData, + polyMap, delayedContacts, convexScaling, idtConvexScale, silhouetteEdgesAreActive, renderOutput), + mBox(box) + { + } + + PX_FORCE_INLINE Ps::IntBool doTest(const PxVec3& v0, const PxVec3& v1, const PxVec3& v2) + { + // PT: this one is safe because midphase vertices are directly passed to the function + return intersectTriangleBox(mBox, v0, v1, v2); + } + + template + void processTriangleCache(TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + + +bool Gu::PCMContactConvexMesh(const PolygonalData& polyData, SupportLocal* polyMap, const Ps::aos::FloatVArg minMargin, const PxBounds3& hullAABB, const PxTriangleMeshGeometryLL& shapeMesh, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtConvexScale, bool idtMeshScale, Gu::MultiplePersistentContactManifold& multiManifold, + Cm::RenderOutput* renderOutput) + +{ + using namespace Ps::aos; + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV contactDist = FLoad(contactDistance); + //Transfer A into the local space of B + const PsTransformV convexTransform(p0, q0);//box + const PsTransformV meshTransform(p1, q1);//triangleMesh + const PsTransformV curTransform = meshTransform.transformInv(convexTransform); + + + if(multiManifold.invalidate(curTransform, minMargin)) + { + const FloatV replaceBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + + //////////////////// + const TriangleMesh* PX_RESTRICT meshData = shapeMesh.meshData; + + const Cm::Matrix34 world0(transform0); + const Cm::Matrix34 world1(transform1); + BoxPadded hullOBB; + computeHullOBB(hullOBB, hullAABB, contactDistance, world0, world1, meshScaling, idtMeshScale); + + // Setup the collider + + Ps::InlineArray delayedContacts; + + const PxU8* PX_RESTRICT extraData = meshData->getExtraTrigData(); + PCMConvexVsMeshContactGenerationCallback blockCallback( + contactDist, replaceBreakingThreshold, convexTransform, meshTransform, multiManifold, contactBuffer, + polyData, polyMap, &delayedContacts, convexScaling, idtConvexScale, meshScaling, extraData, idtMeshScale, true, + hullOBB, renderOutput); + + Midphase::intersectOBB(meshData, hullOBB, blockCallback, true); + + PX_ASSERT(multiManifold.mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + + blockCallback.flushCache(); + //This is very important + blockCallback.mGeneration.generateLastContacts(); + blockCallback.mGeneration.processContacts(GU_SINGLE_MANIFOLD_CACHE_SIZE, false); + +#if PCM_LOW_LEVEL_DEBUG + multiManifold.drawManifold(*renderOutput, transform0, transform1); +#endif + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.8f)); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, contactDist); + } + + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, meshTransform); +} + +bool Gu::pcmContactConvexMesh(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + PX_UNUSED(renderOutput); + + const PxConvexMeshGeometryLL& shapeConvex = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + const ConvexHullData* hullData = shapeConvex.hullData; + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const PsTransformV convexTransform = loadTransformA(transform0); + + const bool idtScaleMesh = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtScaleMesh) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling convexScaling; + PxBounds3 hullAABB; + PolygonalData polyData; + const bool idtScaleConvex = getPCMConvexData(shape0, convexScaling, hullAABB, polyData); + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + + const PxReal toleranceScale = params.mToleranceLength; + const FloatV minMargin = CalculatePCMConvexMargin(hullData, vScale, toleranceScale, GU_PCM_MESH_MANIFOLD_EPSILON); + + ConvexHullV convexHull(hullData, V3Zero(), vScale, vQuat, idtScaleConvex); + + if(idtScaleConvex) + { + SupportLocalImpl convexMap(static_cast(convexHull), convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, true); + return Gu::PCMContactConvexMesh(polyData, &convexMap, minMargin, hullAABB, shapeMesh,transform0,transform1, params.mContactDistance, contactBuffer, convexScaling, + meshScaling, idtScaleConvex, idtScaleMesh, multiManifold, renderOutput); + } + else + { + SupportLocalImpl convexMap(convexHull, convexTransform, convexHull.vertex2Shape, convexHull.shape2Vertex, false); + return Gu::PCMContactConvexMesh(polyData, &convexMap, minMargin, hullAABB, shapeMesh,transform0,transform1, params.mContactDistance, contactBuffer, convexScaling, + meshScaling, idtScaleConvex, idtScaleMesh, multiManifold, renderOutput); + } +} + +bool Gu::pcmContactBoxMesh(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + PX_UNUSED(renderOutput); + + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const PxBoxGeometry& shapeBox = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + const PxBounds3 hullAABB(-shapeBox.halfExtents, shapeBox.halfExtents); + + const bool idtMeshScale = shapeMesh.scale.isIdentity(); + + Cm::FastVertex2ShapeScaling meshScaling; + if(!idtMeshScale) + meshScaling.init(shapeMesh.scale); + + Cm::FastVertex2ShapeScaling idtScaling; + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + const PxReal toleranceLength = params.mToleranceLength; + const FloatV minMargin = Gu::CalculatePCMBoxMargin(boxExtents, toleranceLength, GU_PCM_MESH_MANIFOLD_EPSILON); + + BoxV boxV(V3Zero(), boxExtents); + + const PsTransformV boxTransform = loadTransformA(transform0);//box + + PolygonalData polyData; + PCMPolygonalBox polyBox(shapeBox.halfExtents); + polyBox.getPolygonalData(&polyData); + + Mat33V identity = M33Identity(); + SupportLocalImpl boxMap(boxV, boxTransform, identity, identity, true); + + return Gu::PCMContactConvexMesh(polyData, &boxMap, minMargin, hullAABB, shapeMesh,transform0,transform1, params.mContactDistance, contactBuffer, idtScaling, meshScaling, + true, idtMeshScale, multiManifold, renderOutput); +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h new file mode 100644 index 000000000..c072acd4c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h @@ -0,0 +1,82 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_GEN_H +#define GU_PCM_CONTACT_GEN_H + + +#include "GuConvexSupportTable.h" +#include "GuPersistentContactManifold.h" +#include "GuShapeConvex.h" +#include "GuSeparatingAxes.h" +#include "GuGJKType.h" +#include "GuGJKUtil.h" + +namespace physx +{ + +namespace Gu +{ + + + //full contact gen code for box/convexhull vs convexhull + bool generateFullContactManifold(Gu::PolygonalData& polyData0, Gu::PolygonalData& polyData1, Gu::SupportLocal* map0, Gu::SupportLocal* map1, Gu::PersistentContact* manifoldContacts, + PxU32& numContacts, const Ps::aos::FloatVArg contactDist, const Ps::aos::Vec3VArg normal, const Ps::aos::Vec3VArg closestA, const Ps::aos::Vec3VArg closestB, + const PxReal toleranceA, const PxReal toleranceB, bool doOverlapTest, Cm::RenderOutput* renderOutput, const PxReal toleranceLength); + + //full contact gen code for capsule vs convexhulll + bool generateFullContactManifold(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, const Ps::aos::PsMatTransformV& aToB, Gu::PersistentContact* manifoldContacts, + PxU32& numContacts, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& normal, const Ps::aos::Vec3VArg closest, const PxReal tolerance, bool doOverlapTest, const PxReal toleranceScale); + + //full contact gen code for capsule vs box + bool generateCapsuleBoxFullContactManifold(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, const Ps::aos::PsMatTransformV& aToB, Gu::PersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& normal, const Ps::aos::Vec3VArg closest, const PxReal boxMargin, const bool doOverlapTest, const PxReal toeranceScale); + + //based on the gjk status to decide whether we should do full contact gen with GJK/EPA normal. Also, this method store + //GJK/EPA point to the manifold in case full contact gen doesn't generate any contact + bool addGJKEPAContacts(const Gu::GjkConvex* relativeConvex, const Gu::GjkConvex* localConvex, const Ps::aos::PsMatTransformV& aToB, Gu::GjkStatus status, + Gu::PersistentContact* manifoldContacts, const Ps::aos::FloatV replaceBreakingThreshold, const Ps::aos::FloatV toleranceLength, GjkOutput& output, + Gu::PersistentContactManifold& manifold); + + //MTD code for box/convexhull vs box/convexhull + bool computeMTD(Gu::PolygonalData& polyData0, Gu::PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, Ps::aos::FloatV& penDepth, Ps::aos::Vec3V& normal); + + //MTD code for capsule vs box/convexhull + bool computeMTD(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, Ps::aos::FloatV& penDepth, Ps::aos::Vec3V& normal); + + void buildPartialHull(const Gu::PolygonalData& polyData, SupportLocal* map, Gu::SeparatingAxes& validAxes, const Ps::aos::Vec3VArg v, const Ps::aos::Vec3VArg _dir); + + //full contact gen code for sphere vs convexhull + bool generateSphereFullContactManifold(const Gu::CapsuleV& capsule, Gu::PolygonalData& polyData, Gu::SupportLocal* map, Gu::PersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& normal, const bool doOverlapTest); + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp new file mode 100644 index 000000000..6bb333116 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp @@ -0,0 +1,811 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "CmRenderOutput.h" +#include "GuPCMContactGenUtil.h" +#include "PsVecMath.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" +#include "GuEPA.h" + + +#define PCM_USE_INTERNAL_OBJECT 1 + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + +namespace physx +{ + +namespace Gu +{ + + static bool testFaceNormal(const PolygonalData& polyData0, const PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, const PsMatTransformV& transform0To1, const PsMatTransformV& transform1To0, const FloatVArg contactDist, + FloatV& minOverlap, PxU32& feature, Vec3V& faceNormal, const FeatureStatus faceStatus, FeatureStatus& status) + { + PX_UNUSED(polyData1); + + FloatV _minOverlap = FMax();//minOverlap; + PxU32 _feature = 0; + Vec3V _faceNormal = faceNormal; + FloatV min0, max0; + FloatV min1, max1; + + const Vec3V center1To0 = transform1To0.p; + +#if PCM_USE_INTERNAL_OBJECT + const Vec3V zeroV = V3Zero(); + const Vec3V shapeSpaceCenter1 = V3LoadU(polyData1.mCenter); + const Vec3V internalCenter1In0 = transform1To0.transform(shapeSpaceCenter1); + const FloatV internalRadius1 = FLoad(polyData1.mInternal.mRadius); + const Vec3V internalExtents1 = V3LoadU(polyData1.mInternal.mExtents); + const Vec3V negInternalExtents1 = V3Neg(internalExtents1); + +#endif + + //in the local space of polyData0 + for(PxU32 i=0; ishape2Vertex, vertexSpacePlaneNormal); + + const FloatV magnitude = FRecip(V3Length(shapeSpacePlaneNormal)); + //ML::use this to avoid LHS + min0 = FMul(V3Dot(vertexSpacePlaneNormal, minVert), magnitude); + max0 = FMul(FNeg(planeDist), magnitude); + + //normalize shape space normal + const Vec3V n0 = V3Scale(shapeSpacePlaneNormal, magnitude); + + //calculate polyData1 projection + //rotate polygon's normal into the local space of polyData1 + const Vec3V n1 = transform0To1.rotate(n0); + + +#if PCM_USE_INTERNAL_OBJECT + + //test internal object + //ML: we don't need to transform the normal into the vertex space. If polyData1 don't have scale, + //the vertex2Shape matrix will be identity, shape space normal will be the same as vertex space's normal. + //If polyData0 have scale, internalExtens1 will be 0. + const Vec3V proj = V3Sel(V3IsGrtr(n1, zeroV), internalExtents1, negInternalExtents1); + const FloatV radius = FMax(V3Dot(n1, proj), internalRadius1); + const FloatV internalTrans = V3Dot(internalCenter1In0, n0); + + const FloatV _min1 = FSub(internalTrans, radius); + const FloatV _max1 = FAdd(internalTrans, radius); + + const FloatV _min = FMax(min0, _min1); + const FloatV _max = FMin(max0, _max1); + + const FloatV _tempOverlap = FSub(_max, _min); + //const FloatV _tempOverlap = FSub(max0, _min1); + + //Internal object overlaps more than current min, so can skip it + //because (a) it isn't a separating axis and (b) it isn't the smallest axis + if(FAllGrtr(_tempOverlap, _minOverlap)) + { + continue; + } + +#endif + + const FloatV translate = V3Dot(center1To0, n0); + map1->doSupport(n1, min1, max1); + + min1 = FAdd(translate, min1); + max1 = FAdd(translate, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + _feature = i; + _faceNormal = n0; + } + } + + if(FAllGrtr(minOverlap, _minOverlap)) + { + faceNormal = _faceNormal; + minOverlap = _minOverlap; + status = faceStatus; + } + + + + feature = _feature; + + return true; + + } + + + //plane is in the shape space of polyData + void buildPartialHull(const PolygonalData& polyData, SupportLocal* map, SeparatingAxes& validAxes, const Vec3VArg planeP, const Vec3VArg planeDir) + { + const FloatV zero = FZero(); + const Vec3V dir = V3Normalize(planeDir); + for(PxU32 i=0; ivertex2Shape, V3LoadU_SafeReadW(polyData.mVerts[inds[0]])); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + FloatV dist0 = V3Dot(dir, V3Sub(v0, planeP)); + + for (PxU32 iStart = 0, iEnd = PxU32(polygon.mNbVerts - 1); iStart < polygon.mNbVerts; iEnd = iStart++) + { + const Vec3V v1 = M33MulV3(map->vertex2Shape, V3LoadU_SafeReadW(polyData.mVerts[inds[iEnd]])); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + const FloatV dist1 = V3Dot(dir, V3Sub(v1, planeP)); + + const BoolV con = BOr(FIsGrtr(dist0, zero), FIsGrtr(dist1, zero)); + + //cull edge if either of the vertex will on the positive size of the plane + if(BAllEqTTTT(con)) + { + const Vec3V tempV = V3Sub(v0, v1); + PxVec3 temp; + V3StoreU(tempV, temp); + validAxes.addAxis(temp.getNormalized()); + } + + v0 = v1; + dist0 = dist1; + } + } + } + + + static bool testEdgeNormal(const PolygonalData& polyData0, const PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, const PsMatTransformV& transform0To1, const PsMatTransformV& transform1To0, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& edgeNormalIn0, const FeatureStatus edgeStatus, FeatureStatus& status) + { + + FloatV overlap = minOverlap; + FloatV min0, max0; + FloatV min1, max1; + const FloatV eps = FEps(); + + const Vec3V shapeSpaceCenter0 = V3LoadU(polyData0.mCenter); + const Vec3V shapeSpaceCenter1 = V3LoadU(polyData1.mCenter); + +#if PCM_USE_INTERNAL_OBJECT + const Vec3V zeroV = V3Zero(); + const Vec3V internalCenter1In0 = V3Sub(transform1To0.transform(shapeSpaceCenter1), shapeSpaceCenter0); + const FloatV internalRadius1 = FLoad(polyData1.mInternal.mRadius); + const Vec3V internalExtents1 = V3LoadU(polyData1.mInternal.mExtents); + const Vec3V negInternalExtents1 = V3Neg(internalExtents1); + + const FloatV internalRadius0 = FLoad(polyData0.mInternal.mRadius); + const Vec3V internalExtents0 = V3LoadU(polyData0.mInternal.mExtents); + const Vec3V negInternalExtents0 = V3Neg(internalExtents0); +#endif + + const Vec3V center1To0 = transform1To0.p; + + //in polyData0 shape space + const Vec3V dir0 = V3Sub(transform1To0.transform(shapeSpaceCenter1), shapeSpaceCenter0); + const Vec3V support0 = map0->doSupport(dir0); + + //in polyData1 shape space + const Vec3V dir1 = transform0To1.rotate(V3Neg(dir0)); + const Vec3V support1 = map1->doSupport(dir1); + + const Vec3V support0In1 = transform0To1.transform(support0); + const Vec3V support1In0 = transform1To0.transform(support1); + + SeparatingAxes mSA0; + SeparatingAxes mSA1; + mSA0.reset(); + mSA1.reset(); + + buildPartialHull(polyData0, map0, mSA0, support1In0, dir0); + buildPartialHull(polyData1, map1, mSA1, support0In1, dir1); + + const PxVec3* axe0 = mSA0.getAxes(); + const PxVec3* axe1 = mSA1.getAxes(); + const PxU32 numAxe0 = mSA0.getNumAxes(); + const PxU32 numAxe1 = mSA1.getNumAxes(); + for(PxU32 i=0; i < numAxe0; ++i) + { + //axe0[i] is in the shape space of polyData0 + const Vec3V v0 = V3LoadU(axe0[i]); + + for(PxU32 j=0; j< numAxe1; ++j) + { + //axe1[j] is in the shape space of polyData1 + const Vec3V v1 = V3LoadU(axe1[j]); + + const Vec3V dir = V3Cross(v0, transform1To0.rotate(v1)); + const FloatV lenSq = V3Dot(dir, dir); + if(FAllGrtr(eps, lenSq)) + continue; + + //n0 is in polyData0's local space + const Vec3V n0 = V3Scale(dir, FRsqrt(lenSq)); + + //n1 is in polyData1's local space + const Vec3V n1 = transform0To1.rotate(n0); + +#if PCM_USE_INTERNAL_OBJECT + + //ML: we don't need to transform the normal into the vertex space. If polyData1 don't have scale, + //the vertex2Shape matrix will be identity, shape space normal will be the same as vertex space's normal. + //If polyData0 have scale, internalExtens1 will be 0. + //vertex space n1 + const Vec3V proj = V3Sel(V3IsGrtr(n1, zeroV), internalExtents1, negInternalExtents1); + const FloatV radius = FMax(V3Dot(n1, proj), internalRadius1); + + const FloatV internalTrans = V3Dot(internalCenter1In0, n0); + + const FloatV _min1 = FSub(internalTrans, radius); + const FloatV _max1 = FAdd(internalTrans, radius); + + const Vec3V proj0 = V3Sel(V3IsGrtr(n0, zeroV), internalExtents0, negInternalExtents0); + const FloatV radius0 = FMax(V3Dot(n0, proj0), internalRadius0); + + const FloatV _max0 = radius0; + const FloatV _min0 = FNeg(radius0); + + PX_ASSERT(FAllGrtrOrEq(_max0, _min0)); + PX_ASSERT(FAllGrtrOrEq(_max1, _min1)); + + + const FloatV _min = FMax(_min0, _min1); + const FloatV _max = FMin(_max0, _max1); + + const FloatV _tempOverlap = FSub(_max, _min); + + //Internal object overlaps more than current min, so can skip it + //because (a) it isn't a separating axis and (b) it isn't the smallest axis + if(FAllGrtr(_tempOverlap, overlap)) + { + continue; + } + +#endif + //get polyData0's projection + map0->doSupport(n0, min0, max0); + + const FloatV translate = V3Dot(center1To0, n0); + + //get polyData1's projection + map1->doSupport(n1, min1, max1); + + min1 = FAdd(translate, min1); + max1 = FAdd(translate, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + +#if PCM_USE_INTERNAL_OBJECT + PX_ASSERT(FAllGrtrOrEq(tempOverlap, _tempOverlap)); +#endif + + if(FAllGrtr(overlap, tempOverlap)) + { + overlap = tempOverlap; + edgeNormalIn0 = n0; + status = edgeStatus; + } + } + } + + minOverlap = overlap; + + return true; + + } + + + //contactNormal is in the space of polyData0 + void generatedContacts(PolygonalData& polyData0, PolygonalData& polyData1, const HullPolygonData& referencePolygon, const HullPolygonData& incidentPolygon, + SupportLocal* map0, SupportLocal* map1, const PsMatTransformV& transform0To1, PersistentContact* manifoldContacts, + PxU32& numContacts, const FloatVArg contactDist, Cm::RenderOutput* renderOutput) + { + + PX_UNUSED(renderOutput); + const FloatV zero = FZero(); + + const PxU8* inds0 = polyData0.mPolygonVertexRefs + referencePolygon.mVRef8; + + //transform the plane normal to shape space + const Vec3V contactNormal = V3Normalize(M33TrnspsMulV3(map0->shape2Vertex, V3LoadU(referencePolygon.mPlane.n))); + + //this is the matrix transform all points to the 2d plane + const Mat33V rot = findRotationMatrixFromZAxis(contactNormal); + + const PxU8* inds1 = polyData1.mPolygonVertexRefs + incidentPolygon.mVRef8; + + + Vec3V* points0In0 = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + Vec3V* points1In0 = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*incidentPolygon.mNbVerts, 16)); + bool* points1In0Penetration = reinterpret_cast(PxAlloca(sizeof(bool)*incidentPolygon.mNbVerts)); + FloatV* points1In0TValue = reinterpret_cast(PxAllocaAligned(sizeof(FloatV)*incidentPolygon.mNbVerts, 16)); + + //Transform all the verts from vertex space to shape space + map0->populateVerts(inds0, referencePolygon.mNbVerts, polyData0.mVerts, points0In0); + map1->populateVerts(inds1, incidentPolygon.mNbVerts, polyData1.mVerts, points1In0); + +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map0->transform, points0In0, (PxU32)referencePolygon.mNbVerts, 0x00ff0000); + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map1->transform, points1In0, (PxU32)incidentPolygon.mNbVerts, 0x0000ff00); +#endif + + //This is used to calculate the project point when the 2D reference face points is inside the 2D incident face point + const Vec3V sPoint = points1In0[0]; + + PX_ASSERT(incidentPolygon.mNbVerts <= 64); + + Vec3V eps = Vec3V_From_FloatV(FEps()); + Vec3V max = Vec3V_From_FloatV(FMax()); + Vec3V nmax = V3Neg(max); + + //transform reference polygon to 2d, calculate min and max + Vec3V rPolygonMin= max; + Vec3V rPolygonMax = nmax; + for(PxU32 i=0; ishape2Vertex, V3LoadU(incidentPolygon.mPlane.n))); + + const Vec3V contactNormalIn1 = transform0To1.rotate(contactNormal); + + for(PxU32 i=0; itransform.transformInv(map1->transform); + const PsMatTransformV transform0To1V = map1->transform.transformInv(map0->transform); + + + if(doOverlapTest) + { + //if gjk fail, SAT based yes/no test + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + Vec3V minNormal = V3Zero(); + + PxU32 feature0; + //in the local space of polyData0, minNormal is in polyData0 space + if(!testFaceNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + //in the local space of polyData1, if minOverlap is overwrite inside this function, minNormal will be in polyData1 space + if(!testFaceNormal(polyData1, polyData0, map1, map0, transform1To0V, transform0To1V, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + + bool doEdgeTest = false; + +EdgeTest: + if(doEdgeTest) + { + + if(!testEdgeNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + if(status != EDGE) + return true; + } + + + if(status == POLYDATA0) + { + //minNormal is in the local space of polydata0 + + const HullPolygonData& referencePolygon = polyData0.mPolygons[feature0]; + const Vec3V n = transform0To1V.rotate(minNormal); + const HullPolygonData& incidentPolygon = polyData1.mPolygons[getPolygonIndex(polyData1, map1, n)]; + + generatedContacts(polyData0, polyData1, referencePolygon, incidentPolygon, map0, map1, transform0To1V, manifoldContacts, numContacts, contactDist, renderOutput); + + if (numContacts > 0) + { + const Vec3V nn = V3Neg(n); + //flip the contacts + for(PxU32 i=0; ishape2Vertex, V3LoadU(referencePolygon.mPlane.n))); + const Vec3V incidentNormal = V3Normalize(M33TrnspsMulV3(map0->shape2Vertex, V3LoadU(incidentPolygon.mPlane.n))); + + const FloatV referenceProject = FAbs(V3Dot(referenceNormal, negNormal)); + const FloatV incidentProject = FAbs(V3Dot(incidentNormal, normalIn0)); + + if (FAllGrtrOrEq(referenceProject, incidentProject)) + { + generatedContacts(polyData1, polyData0, referencePolygon, incidentPolygon, map1, map0, transform1To0V, manifoldContacts, numContacts, contactDist, renderOutput); + } + else + { + generatedContacts(polyData0, polyData1, incidentPolygon, referencePolygon, map0, map1, transform0To1V, manifoldContacts, numContacts, contactDist, renderOutput); + + if (numContacts > 0) + { + const Vec3V n = transform0To1V.rotate(incidentNormal); + + const Vec3V nn = V3Neg(n); + //flip the contacts + for (PxU32 i = 0; igetCenter(); + const Vec3V centreB = localConvex->getCenter(); + + const Vec3V dir = V3Normalize(V3Sub(centreA, centreB)); + const FloatV theta = V3Dot(dir, normal); + + if (FAllGrtr(theta, FLoad(0.707f))) + { + addManifoldPoint(manifoldContacts, manifold, output, aToB, replaceBreakingThreshold); + } + else + { + doOverlapTest = true; + } + } + else + { + doOverlapTest = true; + } + } + else if (status == GJK_CONTACT) + { + addManifoldPoint(manifoldContacts, manifold, output, aToB, replaceBreakingThreshold); + } + else + { + PX_ASSERT(status == EPA_CONTACT); + + status = epaPenetration(*relativeConvex, *localConvex, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, tolerenceLength, output); + if (status == EPA_CONTACT) + { + addManifoldPoint(manifoldContacts, manifold, output, aToB, replaceBreakingThreshold); + } + else + { + doOverlapTest = true; + } + } + + return doOverlapTest; + } + + + bool computeMTD(Gu::PolygonalData& polyData0, Gu::PolygonalData& polyData1, SupportLocal* map0, SupportLocal* map1, Ps::aos::FloatV& penDepth, Ps::aos::Vec3V& normal) + { + + using namespace Ps::aos; + + const PsMatTransformV transform1To0V = map0->transform.transformInv(map1->transform); + const PsMatTransformV transform0To1V = map1->transform.transformInv(map0->transform); + + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + Vec3V minNormal = V3Zero(); + const FloatV contactDist = FZero(); + + PxU32 feature0; + //in the local space of polyData0, minNormal is in polyData0 space + if(!testFaceNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + //in the local space of polyData1, if minOverlap is overwrite inside this function, minNormal will be in polyData1 space + if(!testFaceNormal(polyData1, polyData0, map1, map0, transform1To0V, transform0To1V, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + if(!testEdgeNormal(polyData0, polyData1, map0, map1, transform0To1V, transform1To0V, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + penDepth = minOverlap; + if(status == POLYDATA1) + { + //minNormal is in the local space of polydata1 + normal = map1->transform.rotate(minNormal); + } + else + { + PX_ASSERT(status == POLYDATA0 || status == EDGE); + //ML: status == POLYDATA0 or status == EDGE, minNormal is in the local space of polydata0 + normal = V3Neg(map0->transform.rotate(minNormal)); + } + + return true; + } + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp new file mode 100644 index 000000000..25d92d74c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp @@ -0,0 +1,444 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" +#include "CmRenderOutput.h" +#include "GuPCMContactGenUtil.h" +#include "PsVecMath.h" +#include "GuVecCapsule.h" +#include "GuVecBox.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + + + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + +namespace physx +{ + +namespace Gu +{ + + static bool testSATCapsulePoly(const CapsuleV& capsule, const PolygonalData& polyData, SupportLocal* map, const FloatVArg contactDist, FloatV& minOverlap, Vec3V& separatingAxis) + { + using namespace Ps::aos; + FloatV _minOverlap = FMax();//minOverlap; + FloatV min0, max0; + FloatV min1, max1; + Vec3V tempAxis = V3UnitY(); + const FloatV eps = FEps(); + + //ML: project capsule to polydata axis + if(!testPolyDataAxis(capsule, polyData, map, contactDist, _minOverlap, tempAxis)) + return false; + + const Vec3V capsuleAxis = V3Sub(capsule.p1, capsule.p0); + + for(PxU32 i=0;ishape2Vertex, vertexSpaceV); + + const Vec3V dir = V3Cross(capsuleAxis, shapeSpaceV); + const FloatV lenSq = V3Dot(dir, dir); + if(FAllGrtr(eps, lenSq)) + continue; + const Vec3V normal = V3ScaleInv(dir, FSqrt(lenSq)); + + map->doSupport(normal, min0, max0); + + const FloatV tempMin = V3Dot(capsule.p0, normal); + const FloatV tempMax = V3Dot(capsule.p1, normal); + min1 = FMin(tempMin, tempMax); + max1 = FMax(tempMin, tempMax); + + min1 = FSub(min1, capsule.radius); + max1 = FAdd(max1, capsule.radius); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + tempAxis = normal; + } + } + } + + + separatingAxis = tempAxis; + minOverlap = _minOverlap; + + return true; + + } + + + void generatedCapsuleBoxFaceContacts(const CapsuleV& capsule, PolygonalData& polyData, const HullPolygonData& referencePolygon, SupportLocal* map, const PsMatTransformV& aToB, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, const Vec3VArg normal) + { + + const FloatV radius = FAdd(capsule.radius, contactDist); + + //calculate the intersect point of ray to plane + const Vec3V planeNormal = V3Normalize(M33TrnspsMulV3(map->shape2Vertex, V3LoadU(referencePolygon.mPlane.n))); + const PxU8* inds = polyData.mPolygonVertexRefs + referencePolygon.mVRef8; + const Vec3V a = M33MulV3(map->vertex2Shape, V3LoadU_SafeReadW(polyData.mVerts[inds[0]])); // PT: safe because of the way vertex memory is allocated in ConvexHullData + + const FloatV denom0 = V3Dot(planeNormal, V3Sub(capsule.p0, a)); + const FloatV denom1 = V3Dot(planeNormal, V3Sub(capsule.p1, a)); + const FloatV projPlaneN = V3Dot(planeNormal, normal); + const FloatV numer = FSel(FIsGrtr(projPlaneN, FZero()), FRecip(projPlaneN), FZero()); + const FloatV t0 = FMul(denom0, numer); + const FloatV t1 = FMul(denom1, numer); + + const BoolV con0 = FIsGrtrOrEq(radius, t0); + const BoolV con1 = FIsGrtrOrEq(radius, t1); + if(BAllEqTTTT(BOr(con0, con1))) + { + + const Mat33V rot = findRotationMatrixFromZAxis(planeNormal); + Vec3V* points0In0 = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + map->populateVerts(inds, referencePolygon.mNbVerts, polyData.mVerts, points0In0); + + Vec3V rPolygonMin = V3Splat(FMax()); + Vec3V rPolygonMax = V3Neg(rPolygonMin); + for(PxU32 i=0; ishape2Vertex, dir); + const Vec3V p0 = M33MulV3(map->shape2Vertex, capsule.p0); + + if(intersectSegmentPolyhedron(p0, vertexSpaceDir, polyData, tEnter, tExit) && FAllGrtrOrEq(inflatedRadius, tEnter)) + { + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(capsule.p0); + manifoldContacts[numContacts].mLocalPointB = V3ScaleAdd(dir, tEnter, capsule.p0); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), tEnter); + } + + const Vec3V p1 = M33MulV3(map->shape2Vertex, capsule.p1); + if(intersectSegmentPolyhedron(p1, vertexSpaceDir, polyData, tEnter, tExit) && FAllGrtrOrEq(inflatedRadius, tEnter)) + { + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(capsule.p1); + manifoldContacts[numContacts].mLocalPointB = V3ScaleAdd(dir, tEnter, capsule.p1); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), tEnter); + } + + } + + + static void generateEE(const Vec3VArg p, const Vec3VArg q, const Vec3VArg normal, const Vec3VArg a, const Vec3VArg b, const PsMatTransformV& aToB, + PersistentContact* manifoldContacts, PxU32& numContacts, const FloatVArg inflatedRadius) + { + + const FloatV zero = FZero(); + const FloatV expandedRatio = FLoad(0.005f); + const Vec3V ab = V3Sub(b, a); + const Vec3V n = V3Cross(ab, normal); + const FloatV d = V3Dot(n, a); + const FloatV np = V3Dot(n, p); + const FloatV nq = V3Dot(n,q); + const FloatV signP = FSub(np, d); + const FloatV signQ = FSub(nq, d); + const FloatV temp = FMul(signP, signQ); + if(FAllGrtr(temp, zero)) return;//If both points in the same side as the plane, no intersect points + + // if colliding edge (p3,p4) and plane are parallel return no collision + const Vec3V pq = V3Sub(q, p); + const FloatV npq= V3Dot(n, pq); + if(FAllEq(npq, zero)) return; + + + //calculate the intersect point in the segment pq with plane n(x - a). + const FloatV segTValue = FDiv(FSub(d, np), npq); + const Vec3V localPointA = V3ScaleAdd(pq, segTValue, p); + + //ML: ab, localPointA and normal is in the same plane, so that we can do 2D segment segment intersection + //calculate a normal perpendicular to ray localPointA + normal*t, then do 2D segment segment intersection + const Vec3V perNormal = V3Cross(normal, pq); + const Vec3V ap = V3Sub(localPointA, a); + const FloatV nom = V3Dot(perNormal, ap); + const FloatV denom = V3Dot(perNormal, ab); + + const FloatV tValue = FDiv(nom, denom); + + const FloatV max = FAdd(FOne(), expandedRatio); + const FloatV min = FSub(zero, expandedRatio); + if(FAllGrtr(tValue, max) || FAllGrtr(min, tValue)) + return; + + const Vec3V v = V3NegScaleSub(ab, tValue, ap); + const FloatV signedDist = V3Dot(v, normal); + if(FAllGrtrOrEq(inflatedRadius, signedDist)) + { + const Vec3V localPointB = V3Sub(localPointA, v); + manifoldContacts[numContacts].mLocalPointA = aToB.transformInv(localPointA); + manifoldContacts[numContacts].mLocalPointB = localPointB; + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), signedDist); + + } + } + + + void generatedContactsEEContacts(const CapsuleV& capsule, PolygonalData& polyData, const HullPolygonData& referencePolygon, SupportLocal* map, const PsMatTransformV& aToB, + PersistentContact* manifoldContacts, PxU32& numContacts, const FloatVArg contactDist, const Vec3VArg contactNormal) + { + + const PxU8* inds = polyData.mPolygonVertexRefs + referencePolygon.mVRef8; + + Vec3V* points0In0 = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + + + //Transform all the verts from vertex space to shape space + map->populateVerts(inds, referencePolygon.mNbVerts, polyData.mVerts, points0In0); + + const FloatV inflatedRadius = FAdd(capsule.radius, contactDist); + + for (PxU32 rStart = 0, rEnd = PxU32(referencePolygon.mNbVerts - 1); rStart < referencePolygon.mNbVerts; rEnd = rStart++) + { + generateEE(capsule.p0, capsule.p1, contactNormal,points0In0[rStart], points0In0[rEnd], aToB, manifoldContacts, numContacts, inflatedRadius); + + } + } + + + bool generateCapsuleBoxFullContactManifold(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, const PsMatTransformV& aToB, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, Vec3V& normal, const Vec3VArg closest, const PxReal margin, const bool doOverlapTest, const PxReal toleranceScale) + { + + const PxU32 originalContacts = numContacts; + + const Gu::HullPolygonData* referencePolygon = NULL; + + if(doOverlapTest) + { + Ps::aos::FloatV minOverlap; + //overwrite the normal + if(!testSATCapsulePoly(capsule, polyData, map, contactDist, minOverlap, normal)) + return false; + + referencePolygon = &polyData.mPolygons[getPolygonIndex(polyData, map, V3Neg(normal))]; + } + else + { + const PxReal lowerEps = toleranceScale * PCM_WITNESS_POINT_LOWER_EPS; + const PxReal upperEps = toleranceScale * PCM_WITNESS_POINT_UPPER_EPS; + const PxReal tolerance = PxClamp(margin, lowerEps, upperEps); + + const PxU32 featureIndex = getWitnessPolygonIndex(polyData, map, V3Neg(normal), closest, tolerance); + referencePolygon = &polyData.mPolygons[featureIndex]; + + } + + generatedCapsuleBoxFaceContacts(capsule, polyData, *referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, normal); + const PxU32 faceContacts = numContacts - originalContacts; + if(faceContacts < 2) + { + generatedContactsEEContacts(capsule, polyData, *referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, normal); + } + + return true; + } + + //capsule is in the local space of polyData + bool generateFullContactManifold(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, const PsMatTransformV& aToB, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, Vec3V& normal, const Vec3VArg closest, const PxReal margin, const bool doOverlapTest, const PxReal toleranceLength) + { + const PxU32 originalContacts = numContacts; + + Vec3V tNormal = normal; + + if(doOverlapTest) + { + FloatV minOverlap; + + //overwrite the normal with the sperating axis + if(!testSATCapsulePoly(capsule, polyData, map, contactDist, minOverlap, tNormal)) + return false; + + generatedFaceContacts(capsule, polyData, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + + const PxU32 faceContacts = numContacts - originalContacts; + if(faceContacts < 2) + { + const Gu::HullPolygonData& referencePolygon = polyData.mPolygons[getPolygonIndex(polyData, map, V3Neg(tNormal))]; + generatedContactsEEContacts(capsule, polyData,referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + } + } + else + { + generatedFaceContacts(capsule, polyData, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + + const PxU32 faceContacts = numContacts - originalContacts; + if(faceContacts < 2) + { + + const PxReal lowerEps = toleranceLength * PCM_WITNESS_POINT_LOWER_EPS; + const PxReal upperEps = toleranceLength * PCM_WITNESS_POINT_UPPER_EPS; + const PxReal tolerance = PxClamp(margin, lowerEps, upperEps); + + const PxU32 featureIndex = getWitnessPolygonIndex(polyData, map, V3Neg(tNormal), closest, tolerance); + const Gu::HullPolygonData& referencePolygon = polyData.mPolygons[featureIndex]; + generatedContactsEEContacts(capsule, polyData,referencePolygon, map, aToB, manifoldContacts, numContacts, contactDist, tNormal); + } + } + + normal = tNormal; + + return true; + } + + //sphere is in the local space of polyData, we treate sphere as capsule + bool generateSphereFullContactManifold(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, PersistentContact* manifoldContacts, PxU32& numContacts, + const FloatVArg contactDist, Vec3V& normal, const bool doOverlapTest) + { + + if(doOverlapTest) + { + FloatV minOverlap; + //overwrite the normal with the sperating axis + if(!testPolyDataAxis(capsule, polyData, map, contactDist, minOverlap, normal)) + return false; + } + + const FloatV zero = FZero(); + FloatV tEnter=zero, tExit=zero; + const FloatV inflatedRadius = FAdd(capsule.radius, contactDist); + const Vec3V dir = V3Neg(normal); + const Vec3V vertexSpaceDir = M33MulV3(map->shape2Vertex, dir); + const Vec3V p0 = M33MulV3(map->shape2Vertex, capsule.p0); + if(intersectSegmentPolyhedron(p0, vertexSpaceDir, polyData, tEnter, tExit) && FAllGrtrOrEq(inflatedRadius, tEnter)) + { + //ML: for sphere, the contact point A is always zeroV in its local space + manifoldContacts[numContacts].mLocalPointA = V3Zero(); + manifoldContacts[numContacts].mLocalPointB = V3ScaleAdd(dir, tEnter, capsule.p0); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(normal), tEnter); + } + + return true; + + } + + + //capsule is in the shape space of polyData + bool computeMTD(const CapsuleV& capsule, PolygonalData& polyData, SupportLocal* map, FloatV& penDepth, Vec3V& normal) + { + const FloatV contactDist = FZero(); + Vec3V separatingAxis; + FloatV minOverlap; + if(!testSATCapsulePoly(capsule, polyData, map, contactDist, minOverlap, separatingAxis)) + return false; + + //transform normal in to the worl space + normal = map->transform.rotate(separatingAxis); + penDepth = minOverlap; + + return true; + } + + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h new file mode 100644 index 000000000..9e1084a53 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h @@ -0,0 +1,464 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_GEN_UTIL_H +#define GU_PCM_CONTACT_GEN_UTIL_H + +#include "PsVecMath.h" +#include "CmPhysXCommon.h" +#include "GuShapeConvex.h" +#include "GuVecCapsule.h" +#include "GuConvexSupportTable.h" + +//The smallest epsilon we will permit (scaled by PxTolerancesScale.length) +#define PCM_WITNESS_POINT_LOWER_EPS 1e-2f +//The largest epsilon we will permit (scaled by PxTolerancesScale.length) +#define PCM_WITNESS_POINT_UPPER_EPS 5e-2f + +namespace physx +{ + +namespace Gu +{ + + enum FeatureStatus + { + POLYDATA0, + POLYDATA1, + EDGE + }; + + + PX_FORCE_INLINE bool contains(Ps::aos::Vec3V* verts, const PxU32 numVerts, const Ps::aos::Vec3VArg p, const Ps::aos::Vec3VArg min, const Ps::aos::Vec3VArg max) + { + using namespace Ps::aos; + + const BoolV tempCon = BOr(V3IsGrtr(min, p), V3IsGrtr(p, max)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if (BAllEqTTTT(con)) + return false; + + const FloatV tx = V3GetX(p); + const FloatV ty = V3GetY(p); + + const FloatV eps = FEps(); + const FloatV zero = FZero(); + PxU32 intersectionPoints = 0; + + PxU32 i = 0, j = numVerts - 1; + + for (; i < numVerts; j = i++) + { + const FloatV jy = V3GetY(verts[j]); + const FloatV iy = V3GetY(verts[i]); + + const FloatV jx = V3GetX(verts[j]); + const FloatV ix = V3GetX(verts[i]); + + //if p is one of the end point, we will return intersect + const BoolV con0 = BAnd(FIsEq(tx, jx), FIsEq(ty, jy)); + const BoolV con1 = BAnd(FIsEq(tx, ix), FIsEq(ty, iy)); + + if (BAllEqTTTT(BOr(con0, con1))) + { + return true; + } + + //(verts[i].y > test.y) != (points[j].y > test.y) + const PxU32 yflag0 = FAllGrtr(jy, ty); + const PxU32 yflag1 = FAllGrtr(iy, ty); + + //ML: the only case the ray will intersect this segment is when the p's y is in between two segments y + if (yflag0 != yflag1) + { + + //ML: choose ray, which start at p and every points in the ray will have the same y component + //t1 = (yp - yj)/(yi - yj) + //qx = xj + t1*(xi-xj) + //t = qx - xp > 0 for the ray and segment intersection happen + const FloatV jix = FSub(ix, jx); + const FloatV jiy = FSub(iy, jy); + //const FloatV jtx = FSub(tx, jy); + const FloatV jty = FSub(ty, jy); + const FloatV part1 = FMul(jty, jix); + //const FloatV part2 = FMul(jx, jiy); + //const FloatV part3 = FMul(V3Sub(tx, eps), jiy); + const FloatV part2 = FMul(FAdd(jx, eps), jiy); + const FloatV part3 = FMul(tx, jiy); + + const BoolV comp = FIsGrtr(jiy, zero); + const FloatV tmp = FAdd(part1, part2); + const FloatV comp1 = FSel(comp, tmp, part3); + const FloatV comp2 = FSel(comp, part3, tmp); + + + if (FAllGrtrOrEq(comp1, comp2)) + { + if (intersectionPoints == 1) + { + return false; + } + intersectionPoints++; + } + } + } + return intersectionPoints> 0; + } + + + PX_FORCE_INLINE bool boxContainsInXY(const Ps::aos::FloatVArg x, const Ps::aos::FloatVArg y, const Ps::aos::Vec3VArg p, const Ps::aos::Vec3V* verts, const Ps::aos::Vec3VArg min, const Ps::aos::Vec3VArg max) + { + using namespace Ps::aos; + + const BoolV tempCon = BOr(V3IsGrtr(min, p), V3IsGrtr(p, max)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if(BAllEqTTTT(con)) + return false; + + const FloatV zero = FZero(); + FloatV PreviousX = V3GetX(verts[3]); + FloatV PreviousY = V3GetY(verts[3]); + + + // Loop through quad vertices + for(PxI32 i=0; i<4; i++) + { + const FloatV CurrentX = V3GetX(verts[i]); + const FloatV CurrentY = V3GetY(verts[i]); + + // |CurrentX - PreviousX x - PreviousX| + // |CurrentY - PreviousY y - PreviousY| + // => similar to backface culling, check each one of the 4 triangles are consistent, in which case + // the point is within the parallelogram. + const FloatV v00 = FSub(CurrentX, PreviousX); + const FloatV v01 = FSub(y, PreviousY); + const FloatV v10 = FSub(CurrentY, PreviousY); + const FloatV v11 = FSub(x, PreviousX); + const FloatV temp0 = FMul(v00, v01); + const FloatV temp1 = FMul(v10, v11); + if(FAllGrtrOrEq(FSub(temp0, temp1), zero)) + return false; + + PreviousX = CurrentX; + PreviousY = CurrentY; + } + + return true; + + } + + + PX_FORCE_INLINE Ps::aos::FloatV signed2DTriArea(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg c) + { + using namespace Ps::aos; + const Vec3V ca = V3Sub(a, c); + const Vec3V cb = V3Sub(b, c); + + const FloatV t0 = FMul(V3GetX(ca), V3GetY(cb)); + const FloatV t1 = FMul(V3GetY(ca), V3GetX(cb)); + + return FSub(t0, t1); + } + + + PX_FORCE_INLINE PxI32 getPolygonIndex(const Gu::PolygonalData& polyData, SupportLocal* map, const Ps::aos::Vec3VArg normal) + { + using namespace Ps::aos; + + //normal is in shape space, need to transform the vertex space + const Vec3V n = M33TrnspsMulV3(map->vertex2Shape, normal); + const Vec3V nnormal = V3Neg(n); + const Vec3V planeN_ = V3LoadU_SafeReadW(polyData.mPolygons[0].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + FloatV minProj = V3Dot(n, planeN_); + + const FloatV zero = FZero(); + PxI32 closestFaceIndex = 0; + + for(PxU32 i=1; i< polyData.mNbPolygons; ++i) + { + const Vec3V planeN = V3LoadU_SafeReadW(polyData.mPolygons[i].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + const FloatV proj = V3Dot(n, planeN); + if(FAllGrtr(minProj, proj)) + { + minProj = proj; + closestFaceIndex = PxI32(i); + } + } + + const PxU32 numEdges = polyData.mNbEdges; + const PxU8* const edgeToFace = polyData.mFacesByEdges; + + //Loop through edges + PxU32 closestEdge = 0xffffffff; + FloatV maxDpSq = FMul(minProj, minProj); + + for(PxU32 i=0; i < numEdges; ++i)//, inc = VecI32V_Add(inc, vOne)) + { + const PxU32 index = i*2; + const PxU8 f0 = edgeToFace[index]; + const PxU8 f1 = edgeToFace[index+1]; + + const Vec3V planeNormal0 = V3LoadU_SafeReadW(polyData.mPolygons[f0].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + const Vec3V planeNormal1 = V3LoadU_SafeReadW(polyData.mPolygons[f1].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + + // unnormalized edge normal + const Vec3V edgeNormal = V3Add(planeNormal0, planeNormal1);//polys[f0].mPlane.n + polys[f1].mPlane.n; + const FloatV enMagSq = V3Dot(edgeNormal, edgeNormal);//edgeNormal.magnitudeSquared(); + //Test normal of current edge - squared test is valid if dp and maxDp both >= 0 + const FloatV dp = V3Dot(edgeNormal, nnormal);//edgeNormal.dot(normal); + const FloatV sqDp = FMul(dp, dp); + + const BoolV con0 = FIsGrtrOrEq(dp, zero); + const BoolV con1 = FIsGrtr(sqDp, FMul(maxDpSq, enMagSq)); + const BoolV con = BAnd(con0, con1); + if(BAllEqTTTT(con)) + { + maxDpSq = FDiv(sqDp, enMagSq); + closestEdge = i; + } + } + + if(closestEdge!=0xffffffff) + { + const PxU8* FBE = edgeToFace; + + const PxU32 index = closestEdge*2; + const PxU32 f0 = FBE[index]; + const PxU32 f1 = FBE[index+1]; + + const Vec3V planeNormal0 = V3LoadU_SafeReadW(polyData.mPolygons[f0].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + const Vec3V planeNormal1 = V3LoadU_SafeReadW(polyData.mPolygons[f1].mPlane.n); // PT: safe because 'd' follows 'n' in the plane class + + const FloatV dp0 = V3Dot(planeNormal0, nnormal); + const FloatV dp1 = V3Dot(planeNormal1, nnormal); + if(FAllGrtr(dp0, dp1)) + { + closestFaceIndex = PxI32(f0); + } + else + { + closestFaceIndex = PxI32(f1); + } + } + + return closestFaceIndex; + + } + + + PX_FORCE_INLINE PxU32 getWitnessPolygonIndex(const Gu::PolygonalData& polyData, SupportLocal* map, const Ps::aos::Vec3VArg normal, + const Ps::aos::Vec3VArg closest, const PxReal tolerance) + { + using namespace Ps::aos; + + PxReal pd[256]; + //first pass : calculate the smallest distance from the closest point to the polygon face + + //transform the closest p to vertex space + const Vec3V p = M33MulV3(map->shape2Vertex, closest); + PxU32 closestFaceIndex = 0; + + PxVec3 closestP; + V3StoreU(p, closestP); + + const PxReal eps = -tolerance; + + PxPlane plane = polyData.mPolygons[0].mPlane; + PxReal dist = plane.distance(closestP); + PxReal minDist = dist >= eps ? dist : PX_MAX_F32; + pd[0] = minDist; + PxReal maxDist = dist; + PxU32 maxFaceIndex = 0; + + for (PxU32 i = 1; i < polyData.mNbPolygons; ++i) + { + plane = polyData.mPolygons[i].mPlane; + dist = plane.distance(closestP); + pd[i] = dist >= eps ? dist : PX_MAX_F32; + if (minDist > pd[i]) + { + minDist = pd[i]; + closestFaceIndex = i; + } + if (dist > maxDist) + { + maxDist = dist; + maxFaceIndex = i; + } + } + + if (minDist == PX_MAX_F32) + return maxFaceIndex; + + //second pass : select the face which has the normal most close to the gjk/epa normal + Vec4V plane4 = V4LoadU(&polyData.mPolygons[closestFaceIndex].mPlane.n.x); + Vec3V n = Vec3V_From_Vec4V(plane4); + n = V3Normalize(M33TrnspsMulV3(map->shape2Vertex, n)); + FloatV bestProj = V3Dot(n, normal); + + const PxU32 firstPassIndex = closestFaceIndex; + + for (PxU32 i = 0; i< polyData.mNbPolygons; ++i) + { + //if the difference between the minimum distance and the distance of p to plane i is within tolerance, we use the normal to chose the best plane + if ((tolerance >(pd[i] - minDist)) && (firstPassIndex != i)) + { + plane4 = V4LoadU(&polyData.mPolygons[i].mPlane.n.x); + n = Vec3V_From_Vec4V(plane4); + //rotate the plane normal to shape space. We can roate normal into the vertex space. The reason for + //that is because it will lose some numerical precision if the object has large scale and this algorithm + //will choose different face + n = V3Normalize(M33TrnspsMulV3(map->shape2Vertex, n)); + FloatV proj = V3Dot(n, normal); + + if (FAllGrtr(bestProj, proj)) + { + closestFaceIndex = i; + bestProj = proj; + } + } + } + + return closestFaceIndex; + } + + //ML: this function is shared by the sphere/capsule vs convex hulls full contact gen, capsule in the local space of polyData + PX_FORCE_INLINE bool testPolyDataAxis(const Gu::CapsuleV& capsule, const Gu::PolygonalData& polyData, SupportLocal* map, const Ps::aos::FloatVArg contactDist, Ps::aos::FloatV& minOverlap, Ps::aos::Vec3V& separatingAxis) + { + using namespace Ps::aos; + FloatV _minOverlap = FMax();//minOverlap; + FloatV min0, max0; + FloatV min1, max1; + Vec3V tempAxis = V3UnitY(); + + //capsule in the local space of polyData + for(PxU32 i=0; ishape2Vertex, vertexSpacePlaneNormal); + + const FloatV magnitude = FRecip(V3Length(shapeSpacePlaneNormal)); + //normalize shape space normal + const Vec3V planeN = V3Scale(shapeSpacePlaneNormal, magnitude); + //ML::use this to avoid LHS + min0 = FMul(V3Dot(vertexSpacePlaneNormal, minVert), magnitude); + max0 = FMul(FNeg(planeDist), magnitude); + + + const FloatV tempMin = V3Dot(capsule.p0, planeN); + const FloatV tempMax = V3Dot(capsule.p1, planeN); + min1 = FMin(tempMin, tempMax); + max1 = FMax(tempMin, tempMax); + + min1 = FSub(min1, capsule.radius); + max1 = FAdd(max1, capsule.radius); + + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + tempAxis = planeN; + } + } + + separatingAxis = tempAxis; + minOverlap = _minOverlap; + + return true; + + } + + //ML: this function is shared by sphere/capsule. Point a and direction d need to be in the local space of polyData + PX_FORCE_INLINE bool intersectSegmentPolyhedron(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg dir, const PolygonalData& polyData, Ps::aos::FloatV& tEnter, Ps::aos::FloatV& tExit) + { + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV one = FOne(); + const FloatV eps = FLoad(1e-7f); + FloatV tFirst = zero; + FloatV tLast= one; + + for(PxU32 k=0; k 0 means the ray is exiting halfspace + const BoolV con = FIsGrtr(zero, denominator); + const BoolV con0= FIsGrtr(tTemp, tFirst); + const BoolV con1 = FIsGrtr(tLast, tTemp); + + tFirst = FSel(BAnd(con, con0), tTemp, tFirst); + tLast = FSel(BAndNot(con1, con), tTemp, tLast); + } + + if(FAllGrtr(tFirst, tLast)) + return false; + } + + //calculate the intersect p in the local space + tEnter = tFirst; + tExit = tLast; + + return true; + } + +}//Gu +}//physx + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h new file mode 100644 index 000000000..c96349973 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h @@ -0,0 +1,208 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_CONTACT_MESH_CALLBACK_H +#define GU_PCM_CONTACT_MESH_CALLBACK_H + +#include "GuMidphaseInterface.h" +#include "GuEntityReport.h" +#include "GuHeightFieldUtil.h" +#include "GuTriangleCache.h" +#include "GuConvexEdgeFlags.h" + +namespace physx +{ + +namespace Gu +{ + +template +struct PCMMeshContactGenerationCallback : MeshHitCallback +{ +public: + const Cm::FastVertex2ShapeScaling& mMeshScaling; + const PxU8* PX_RESTRICT mExtraTrigData; + bool mIdtMeshScale; + static const PxU32 CacheSize = 16; + Gu::TriangleCache mCache; + + PCMMeshContactGenerationCallback(const Cm::FastVertex2ShapeScaling& meshScaling, const PxU8* extraTrigData, bool idtMeshScale) + : MeshHitCallback(CallbackMode::eMULTIPLE), + mMeshScaling(meshScaling), mExtraTrigData(extraTrigData), mIdtMeshScale(idtMeshScale) + { + } + + void flushCache() + { + if (!mCache.isEmpty()) + { + (static_cast(this))->template processTriangleCache< CacheSize >(mCache); + mCache.reset(); + } + } + + virtual PxAgain processHit( + const PxRaycastHit& hit, const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, PxReal&, const PxU32* vinds) + { + if (!(static_cast(this))->doTest(v0, v1, v2)) + return true; + + PxVec3 v[3]; + if(mIdtMeshScale) + { + v[0] = v0; + v[1] = v1; + v[2] = v2; + } + else + { + const PxI32 winding = mMeshScaling.flipsNormal() ? 1 : 0; + v[0] = mMeshScaling * v0; + v[1 + winding] = mMeshScaling * v1; + v[2 - winding] = mMeshScaling * v2; + } + + const PxU32 triangleIndex = hit.faceIndex; + const PxU8 extraData = getConvexEdgeFlags(mExtraTrigData, triangleIndex); + + if (mCache.isFull()) + { + (static_cast(this))->template processTriangleCache< CacheSize >(mCache); + mCache.reset(); + } + mCache.addTriangle(v, vinds, triangleIndex, extraData); + + return true; + } + +protected: + PCMMeshContactGenerationCallback& operator=(const PCMMeshContactGenerationCallback&); +}; + +template +struct PCMHeightfieldContactGenerationCallback : Gu::EntityReport +{ +public: + const Gu::HeightFieldUtil& mHfUtil; + const PxTransform& mHeightfieldTransform; + bool mBoundaryCollisions; + + PCMHeightfieldContactGenerationCallback(const Gu::HeightFieldUtil& hfUtil, const PxTransform& heightfieldTransform) : + mHfUtil(hfUtil), mHeightfieldTransform(heightfieldTransform) + { + mBoundaryCollisions = !(hfUtil.getHeightField().getFlags() & PxHeightFieldFlag::eNO_BOUNDARY_EDGES); + } + + // PT: TODO: refactor/unify with similar code in other places + virtual PxAgain onEvent(PxU32 nb, PxU32* indices) + { + const PxU32 CacheSize = 16; + Gu::TriangleCache cache; + + const PxU32 nbPasses = (nb+(CacheSize-1))/CacheSize; + PxU32 nbTrigs = nb; + PxU32* inds0 = indices; + + const PxU8 nextInd[] = {2,0,1}; + + for(PxU32 i = 0; i < nbPasses; ++i) + { + cache.mNumTriangles = 0; + PxU32 trigCount = PxMin(nbTrigs, CacheSize); + nbTrigs -= trigCount; + while(trigCount--) + { + PxU32 triangleIndex = *(inds0++); + PxU32 vertIndices[3]; + + PxTriangle currentTriangle; // in world space + + PxU32 adjInds[3]; + mHfUtil.getTriangle(mHeightfieldTransform, currentTriangle, vertIndices, adjInds, triangleIndex, false, false); + + PxVec3 normal; + currentTriangle.normal(normal); + + PxU8 triFlags = 0; //KS - temporary until we can calculate triFlags for HF + + for(PxU32 a = 0; a < 3; ++a) + { + + if (adjInds[a] != 0xFFFFFFFF) + { + PxTriangle adjTri; + PxU32 inds[3]; + mHfUtil.getTriangle(mHeightfieldTransform, adjTri, inds, NULL, adjInds[a], false, false); + //We now compare the triangles to see if this edge is active + + PX_ASSERT(inds[0] == vertIndices[a] || inds[1] == vertIndices[a] || inds[2] == vertIndices[a]); + PX_ASSERT(inds[0] == vertIndices[(a + 1) % 3] || inds[1] == vertIndices[(a + 1) % 3] || inds[2] == vertIndices[(a + 1) % 3]); + + + PxVec3 adjNormal; + adjTri.denormalizedNormal(adjNormal); + PxU32 otherIndex = nextInd[a]; + PxF32 projD = adjNormal.dot(currentTriangle.verts[otherIndex] - adjTri.verts[0]); + + if (projD < 0.f) + { + adjNormal.normalize(); + + PxF32 proj = adjNormal.dot(normal); + + if (proj < 0.997f) + { + triFlags |= (1 << (a + 3)); + } + } + } + else if (mBoundaryCollisions) + { + triFlags |= (1 << (a + 3)); //Mark boundary edge active + } + else + triFlags |= (1 << a); //Mark as silhouette edge + } + + cache.addTriangle(currentTriangle.verts, vertIndices, triangleIndex, triFlags); + } + PX_ASSERT(cache.mNumTriangles <= 16); + + (static_cast(this))->template processTriangleCache< CacheSize >(cache); + } + return true; + } +protected: + PCMHeightfieldContactGenerationCallback& operator=(const PCMHeightfieldContactGenerationCallback&); +}; + +}//Gu +}//physx + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp new file mode 100644 index 000000000..fd184ab8f --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp @@ -0,0 +1,219 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecBox.h" +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + +#include "GuPersistentContactManifold.h" + +namespace physx +{ +namespace Gu +{ +bool pcmContactPlaneBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + + // Get actual shape data + const PxBoxGeometry& shapeBox = shape1.get(); + + const PsTransformV transf0 = loadTransformA(transform1);//box transform + const PsTransformV transf1 = loadTransformA(transform0);//plane transform + + //box to plane + const PsTransformV curTransf(transf1.transformInv(transf0)); + + //in world space + const Vec3V negPlaneNormal = V3Normalize(V3Neg(QuatGetBasisVector0(transf1.q))); + + const FloatV contactDist = FLoad(params.mContactDistance); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV boxMargin = CalculatePCMBoxMargin(boxExtents, toleranceLength); + const FloatV projectBreakingThreshold = FMul(boxMargin, FLoad(0.2f)); + const PxU32 initialContacts = manifold.mNumContacts; + + manifold.refreshContactPoints(curTransf, projectBreakingThreshold, contactDist); + + const PxU32 newContacts = manifold.mNumContacts; + const bool bLostContacts = (newContacts != initialContacts);//((initialContacts == 0) || (newContacts != initialContacts)); + + if(bLostContacts || manifold.invalidate_PrimitivesPlane(curTransf, boxMargin, FLoad(0.2f))) + { + //ML:localNormal is the local space of plane normal, however, because shape1 is box and shape0 is plane, we need to use the reverse of contact normal(which will be the plane normal) to make the refreshContactPoints + //work out the correct pentration for points + const Vec3V localNormal = V3UnitX(); + + manifold.mNumContacts = 0; + manifold.setRelativeTransform(curTransf); + + const PsMatTransformV aToB(curTransf); + const FloatV bx = V3GetX(boxExtents); + const FloatV by = V3GetY(boxExtents); + const FloatV bz = V3GetZ(boxExtents); + + const FloatV nbx = FNeg(bx); + const FloatV nby = FNeg(by); + const FloatV nbz = FNeg(bz); + + const Vec3V temp0 = V3Scale(aToB.getCol0(), bx); + const Vec3V temp1 = V3Scale(aToB.getCol1(), by); + const Vec3V temp2 = V3Scale(aToB.getCol2(), bz); + + const Vec3V ntemp2 = V3Neg(temp2); + + const FloatV px = V3GetX(aToB.p); + + //box's points in the local space of plane + const Vec3V temp01 = V3Add(temp0, temp1);//(x, y) + const Vec3V temp02 = V3Sub(temp0, temp1);//(x, -y) + + const FloatV s0 = V3GetX(V3Add(temp2, temp01));//(x, y, z) + const FloatV s1 = V3GetX(V3Add(ntemp2, temp01));//(x, y, -z) + const FloatV s2 = V3GetX(V3Add(temp2, temp02));//(x, -y, z) + const FloatV s3 = V3GetX(V3Add(ntemp2, temp02));//(x, -y, -z) + const FloatV s4 = V3GetX(V3Sub(temp2, temp02));//(-x, y, z) + const FloatV s5 = V3GetX(V3Sub(ntemp2, temp02));//(-x, y, -z) + const FloatV s6 = V3GetX(V3Sub(temp2, temp01));//(-x, -y, z) + const FloatV s7 = V3GetX(V3Sub(ntemp2, temp01));//(-x, -y, -z) + + const FloatV acceptanceDist = FSub(contactDist, px); + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + + if(FAllGrtr(acceptanceDist, s0)) + { + const FloatV pen = FAdd(s0, px); + //(x, y, z) + manifoldContacts[numContacts].mLocalPointA = boxExtents;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(boxExtents)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s1)) + { + const FloatV pen = FAdd(s1, px); + //(x, y, -z) + const Vec3V p = V3Merge(bx, by, nbz); + //add to contact stream + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s2)) + { + const FloatV pen = FAdd(s2, px); + //(x, -y, z) + const Vec3V p = V3Merge(bx, nby, bz); + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s3)) + { + const FloatV pen = FAdd(s3, px); + //(x, -y, -z) + const Vec3V p = V3Merge(bx, nby, nbz); + manifoldContacts[numContacts].mLocalPointA = p; + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s4)) + { + const FloatV pen = FAdd(s4, px); + //(-x, y, z) + const Vec3V p =V3Merge(nbx, by, bz); + manifoldContacts[numContacts].mLocalPointA = p; + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s5)) + { + const FloatV pen = FAdd(s5, px); + //(-x, y, -z) + const Vec3V p = V3Merge(nbx, by, nbz); + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + + if(FAllGrtr(acceptanceDist, s6)) + { + const FloatV pen = FAdd(s6, px); + //(-x, -y, z) + const Vec3V p = V3Merge(nbx, nby, bz); + manifoldContacts[numContacts].mLocalPointA = p;//aToB.transformInv(p); + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + if(FAllGrtr(acceptanceDist, s7)) + { + const FloatV pen = FAdd(s7, px); + //(-x, -y, -z) + const Vec3V p = V3Merge(nbx, nby, nbz); + manifoldContacts[numContacts].mLocalPointA = p; + manifoldContacts[numContacts].mLocalPointB = V3NegScaleSub(localNormal, pen, aToB.transform(p)); + manifoldContacts[numContacts++].mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), pen); + } + + //reduce contacts + manifold.addBatchManifoldContactsCluster(manifoldContacts, numContacts); + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf1, contactDist); + + return manifold.getNumContacts() > 0; + } + else + { + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf1, contactDist); + + //manifold.drawManifold(*gRenderOutPut, transf0, transf1); + return manifold.getNumContacts() > 0; + } +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp new file mode 100644 index 000000000..9e0023c9e --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp @@ -0,0 +1,133 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPersistentContactManifold.h" + +namespace physx +{ +namespace Gu +{ +bool pcmContactPlaneCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + + // Get actual shape data + const PxCapsuleGeometry& shapeCapsule = shape1.get(); + + const PsTransformV transf0 = loadTransformA(transform1);//capsule transform + const PsTransformV transf1 = loadTransformA(transform0);//plane transform + //capsule to plane + const PsTransformV aToB(transf1.transformInv(transf0)); + + //in world space + const Vec3V planeNormal = V3Normalize(QuatGetBasisVector0(transf1.q)); + const Vec3V contactNormal = V3Neg(planeNormal); + + //ML:localNormal is the local space of plane normal, however, because shape1 is capulse and shape0 is plane, we need to use the reverse of contact normal(which will be the plane normal) to make the refreshContactPoints + //work out the correct pentration for points + const Vec3V localNormal = V3UnitX(); + + const FloatV contactDist = FLoad(params.mContactDistance); + + const FloatV radius = FLoad(shapeCapsule.radius); + const FloatV halfHeight = FLoad(shapeCapsule.halfHeight); + + //capsule is in the local space of plane(n = (1.f, 0.f, 0.f), d=0.f) + const Vec3V basisVector = QuatGetBasisVector0(aToB.q); + const Vec3V tmp = V3Scale(basisVector, halfHeight); + const Vec3V s = V3Add(aToB.p, tmp); + const Vec3V e = V3Sub(aToB.p, tmp); + + const FloatV inflatedRadius = FAdd(radius, contactDist); + const FloatV replaceBreakingThreshold = FMul(radius, FLoad(0.001f)); + const FloatV projectBreakingThreshold = FMul(radius, FLoad(0.05f)); + const PxU32 initialContacts = manifold.mNumContacts; + + //manifold.refreshContactPoints(curRTrans, projectBreakingThreshold, contactDist); + const FloatV refreshDist = FAdd(contactDist, radius); + manifold.refreshContactPoints(aToB, projectBreakingThreshold, refreshDist); + + const PxU32 newContacts = manifold.mNumContacts; + const bool bLostContacts = (newContacts != initialContacts);//((initialContacts == 0) || (newContacts != initialContacts)); + + if(bLostContacts || manifold.invalidate_PrimitivesPlane(aToB, radius, FLoad(0.02f))) + { + manifold.mNumContacts = 0; + manifold.setRelativeTransform(aToB); + //calculate the distance from s to the plane + const FloatV signDist0 = V3GetX(s);//V3Dot(localNormal, s); + if(FAllGrtr(inflatedRadius, signDist0)) + { + const Vec3V localPointA = aToB.transformInv(s); + const Vec3V localPointB = V3NegScaleSub(localNormal, signDist0, s); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), signDist0); + //add to manifold + manifold.addManifoldPoint2(localPointA, localPointB, localNormalPen, replaceBreakingThreshold); + } + + const FloatV signDist1 = V3GetX(e);//V3Dot(localNormal, e); + if(FAllGrtr(inflatedRadius, signDist1)) + { + const Vec3V localPointA = aToB.transformInv(e); + + const Vec3V localPointB = V3NegScaleSub(localNormal, signDist1, e); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), signDist1); + //add to manifold + manifold.addManifoldPoint2(localPointA, localPointB, localNormalPen, replaceBreakingThreshold); + } + + manifold.addManifoldContactsToContactBuffer(contactBuffer, contactNormal, planeNormal, transf0, radius, contactDist); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + + return manifold.getNumContacts() > 0; + } + else + { + manifold.addManifoldContactsToContactBuffer(contactBuffer, contactNormal, planeNormal, transf0, radius, contactDist); + return manifold.getNumContacts() > 0; + } +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp new file mode 100644 index 000000000..43b6dc00d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp @@ -0,0 +1,159 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecConvexHull.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPersistentContactManifold.h" + + +namespace physx +{ +namespace Gu +{ +bool pcmContactPlaneConvex(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(shape0); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + Ps::prefetchLine(&manifold, 256); + + // Get actual shape data + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + + const PsTransformV transf0 = loadTransformA(transform1);//convex transform + const PsTransformV transf1 = loadTransformA(transform0);//plane transform + //convex to plane + const PsTransformV curTransf(transf1.transformInv(transf0)); + + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale, toleranceLength); + + //in world space + const Vec3V planeNormal = V3Normalize(QuatGetBasisVector0(transf1.q)); + const Vec3V negPlaneNormal = V3Neg(planeNormal); + + const FloatV contactDist = FLoad(params.mContactDistance); + + //const FloatV replaceBreakingThreshold = FMul(convexMargin, FLoad(0.001f)); + const FloatV projectBreakingThreshold = FMul(convexMargin, FLoad(0.2f)); + const PxU32 initialContacts = manifold.mNumContacts; + + manifold.refreshContactPoints(curTransf, projectBreakingThreshold, contactDist); + + const PxU32 newContacts = manifold.mNumContacts; + const bool bLostContacts = (newContacts != initialContacts);//((initialContacts == 0) || (newContacts != initialContacts)); + + + if(bLostContacts || manifold.invalidate_PrimitivesPlane(curTransf, convexMargin, FLoad(0.2f))) + { + const PsMatTransformV aToB(curTransf); + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const Mat33V vertex2Shape = ConstructVertex2ShapeMatrix(vScale, vQuat); + + //ML:localNormal is the local space of plane normal, however, because shape1 is box and shape0 is plane, we need to use the reverse of contact normal(which will be the plane normal) to make the refreshContactPoints + //work out the correct pentration for points + const Vec3V localNormal = V3UnitX(); + + manifold.mNumContacts = 0; + manifold.setRelativeTransform(curTransf); + const PxVec3* PX_RESTRICT verts = hullData->getHullVertices(); + const PxU8 numVerts = hullData->mNbHullVertices; + + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + PxU32 numContacts = 0; + + const PsMatTransformV aToBVertexSpace(aToB.p, M33MulM33(aToB.rot, vertex2Shape)); + //brute force each points + for(PxU8 i=0; i= Gu::ContactBuffer::MAX_CONTACTS) + { + //ML: number of contacts are more than MAX_CONTACTS, we need to force contact reduction + manifold.reduceBatchContacts(manifoldContacts, numContacts, toleranceLength); + numContacts = GU_MANIFOLD_CACHE_SIZE; + for(PxU32 j=0; j 0; + } + else + { + manifold.addManifoldContactsToContactBuffer(contactBuffer, negPlaneNormal, transf1, contactDist); +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1); +#endif + return manifold.getNumContacts() > 0; + } +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp new file mode 100644 index 000000000..a9e057ea0 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp @@ -0,0 +1,156 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +//#include "GuGJKWrapper.h" +#include "GuVecBox.h" +#include "GuVecSphere.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + + + +namespace physx +{ + +namespace Gu +{ +bool pcmContactSphereBox(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + + using namespace Ps::aos; + // Get actual shape data + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxBoxGeometry& shapeBox = shape1.get(); + // + + //const PsTransformV transf0(transform0); + const Vec3V sphereOrigin = V3LoadA(&transform0.p.x); + //const PsTransformV transf1(transform1); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV radius = FLoad(shapeSphere.radius); + + const PsTransformV transf1(p1, q1); + + const FloatV cDist = FLoad(params.mContactDistance); + + const Vec3V boxExtents = V3LoadU(shapeBox.halfExtents); + + //translate sphere center into the box space + const Vec3V sphereCenter = transf1.transformInv(sphereOrigin); + + const Vec3V nBoxExtents = V3Neg(boxExtents); + + //const FloatV radSq = FMul(radius, radius); + + const FloatV inflatedSum = FAdd(radius, cDist); + const FloatV sqInflatedSum = FMul(inflatedSum, inflatedSum); + + const Vec3V p = V3Clamp(sphereCenter, nBoxExtents, boxExtents); + const Vec3V v = V3Sub(sphereCenter, p); + const FloatV lengthSq = V3Dot(v, v); + + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + + if(FAllGrtr(sqInflatedSum, lengthSq))//intersect + { + //check whether the spherCenter is inside the box + const BoolV bInsideBox = V3IsGrtrOrEq(boxExtents, V3Abs(sphereCenter)); + // PT: TODO: ??? revisit this, why do we have both BAllEqTTTT and BAllTrue3? + if(BAllEqTTTT(BAllTrue3(bInsideBox)))//sphere center inside the box + { + //Pick directions and sign + const Vec3V absP = V3Abs(p); + const Vec3V distToSurface = V3Sub(boxExtents, absP);//dist from embedded center to box surface along 3 dimensions. + + const FloatV x = V3GetX(distToSurface); + const FloatV y = V3GetY(distToSurface); + const FloatV z = V3GetZ(distToSurface); + + const Vec3V xV = V3Splat(x); + const Vec3V zV = V3Splat(z); + + //find smallest element of distToSurface + const BoolV con0 = BAllTrue3(V3IsGrtrOrEq(distToSurface, zV)); + const BoolV con1 = BAllTrue3(V3IsGrtrOrEq(distToSurface, xV)); + const Vec3V sign = V3Sign(p); + + const Vec3V tmpX = V3Mul(V3UnitX(), sign); + const Vec3V tmpY = V3Mul(V3UnitY(), sign); + const Vec3V tmpZ = V3Mul(V3UnitZ(), sign); + + const Vec3V locNorm= V3Sel(con0, tmpZ, V3Sel(con1, tmpX, tmpY));////local coords contact normal + const FloatV dist = FNeg(FSel(con0, z, FSel(con1, x, y))); + + //separation so far is just the embedding of the center point; we still have to push out all of the radius. + const Vec3V normal = transf1.rotate(locNorm); + const FloatV penetration = FSub(dist, radius); + const Vec3V point = V3Sub(sphereOrigin , V3Scale(normal, dist)); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(penetration, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + //context.mContactBuffer.contact(point, normal, penetration); + } + else + { + //get the closest point from the center to the box surface + const FloatV recipLength = FRsqrt(lengthSq); + const FloatV length = FRecip(recipLength); + const Vec3V locNorm = V3Scale(v, recipLength); + const FloatV penetration = FSub(length, radius); + const Vec3V normal = transf1.rotate(locNorm); + const Vec3V point = transf1.transform(p); + + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(penetration, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + //context.mContactBuffer.contact(point, normal, penetration); + } + return true; + } + return false; +} + +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp new file mode 100644 index 000000000..a7851fe59 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp @@ -0,0 +1,121 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +//#include "GuGJKWrapper.h" +#include "GuVecSphere.h" +#include "GuVecCapsule.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" + + +namespace physx +{ + +namespace Gu +{ +PX_FORCE_INLINE Ps::aos::FloatV PxcDistancePointSegmentSquared(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg p, Ps::aos::FloatV& param) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV one = FOne(); + + const Vec3V ap = V3Sub(p, a); + const Vec3V ab = V3Sub(b, a); + const FloatV nom = V3Dot(ap, ab); + + const FloatV denom = V3Dot(ab, ab); + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + + const FloatV t = FSel(FIsEq(denom, zero), zero, tValue); + const Vec3V v = V3NegScaleSub(ab, t, ap); + param = t; + return V3Dot(v, v); +} + +bool pcmContactSphereCapsule(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxCapsuleGeometry& shapeCapsule = shape1.get(); + + //Sphere in world space + const Vec3V sphereCenter = V3LoadA(&transform0.p.x); + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV capsuleRadius = FLoad(shapeCapsule.radius); + const FloatV cDist = FLoad(params.mContactDistance); + + //const FloatV r0 = FloatV_From_F32(shapeCapsule.radius); + const FloatV halfHeight0 = FLoad(shapeCapsule.halfHeight); + const Vec3V basisVector0 = QuatGetBasisVector0(q1); + const Vec3V tmp0 = V3Scale(basisVector0, halfHeight0); + const Vec3V s = V3Add(p1, tmp0); + const Vec3V e = V3Sub(p1, tmp0); + + + const FloatV radiusSum = FAdd(sphereRadius, capsuleRadius); + const FloatV inflatedSum = FAdd(radiusSum, cDist); + + // Collision detection + FloatV t; + const FloatV squareDist = PxcDistancePointSegmentSquared(s, e, sphereCenter, t); + const FloatV sqInflatedSum = FMul(inflatedSum, inflatedSum); + + if(FAllGrtr(sqInflatedSum, squareDist))//BAllEq(con, bTrue)) + { + const Vec3V p = V3ScaleAdd(V3Sub(e, s), t, s); + const Vec3V dir = V3Sub(sphereCenter, p); + const Vec3V normal = V3NormalizeSafe(dir, V3UnitX()); + const Vec3V point = V3NegScaleSub(normal, sphereRadius, sphereCenter);//transform back to the world space + + const FloatV dist = FSub(FSqrt(squareDist), radiusSum); + //context.mContactBuffer.contact(point, normal, FSub(FSqrt(squareDist), radiusSum)); + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(dist, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + return true; + } + return false; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp new file mode 100644 index 000000000..2bc568c42 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp @@ -0,0 +1,284 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuGJKPenetration.h" +#include "GuEPA.h" +#include "GuVecCapsule.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuContactBuffer.h" +#include "GuPCMContactGen.h" +#include "GuPCMShapeConvex.h" + +namespace physx +{ +namespace Gu +{ + +static void addToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg worldNormal, const Ps::aos::Vec3VArg worldPoint, const Ps::aos::FloatVArg penDep) +{ + using namespace Ps::aos; + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(worldNormal), reinterpret_cast(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldPoint), reinterpret_cast(&contact.point.x)); + FStore(penDep, &contact.separation); + + PX_ASSERT(contact.point.isFinite()); + PX_ASSERT(contact.normal.isFinite()); + PX_ASSERT(PxIsFinite(contact.separation)); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + +} + +static bool fullContactsGenerationSphereConvex(const Gu::CapsuleV& capsule, const Gu::ConvexHullV& convexHull, const Ps::aos::PsTransformV& transf0,const Ps::aos::PsTransformV& transf1, + Gu::PersistentContact* manifoldContacts, Gu::ContactBuffer& contactBuffer, const bool idtScale, Gu::PersistentContactManifold& manifold, + Ps::aos::Vec3VArg normal, const Ps::aos::FloatVArg contactDist, bool doOverlapTest, Cm::RenderOutput* renderOutput) +{ + PX_UNUSED(renderOutput); + using namespace Ps::aos; + Gu::PolygonalData polyData; + getPCMConvexData(convexHull,idtScale, polyData); + + PxU8 buff[sizeof(SupportLocalImpl)]; + SupportLocal* map = (idtScale ? static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(static_cast(convexHull), transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale)) : + static_cast(PX_PLACEMENT_NEW(buff, SupportLocalImpl)(convexHull, transf1, convexHull.vertex2Shape, convexHull.shape2Vertex, idtScale))); + + PxU32 numContacts = 0; + if(generateSphereFullContactManifold(capsule, polyData, map, manifoldContacts, numContacts, contactDist, normal, doOverlapTest)) + { + + if(numContacts > 0) + { + + Gu::PersistentContact& p = manifold.getContactPoint(0); + + p.mLocalPointA = manifoldContacts[0].mLocalPointA; + p.mLocalPointB = manifoldContacts[0].mLocalPointB; + p.mLocalNormalPen = manifoldContacts[0].mLocalNormalPen; + manifold.mNumContacts =1; + + //transform normal to world space + const Vec3V worldNormal = transf1.rotate(normal); + const Vec3V worldP = V3NegScaleSub(worldNormal, capsule.radius, transf0.p); + const FloatV penDep = FSub(V4GetW(manifoldContacts[0].mLocalNormalPen), capsule.radius); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, capsule.radius); +#endif + + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + + return true; + } + + } + + return false; +} + +bool pcmContactSphereConvex(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + + PX_ASSERT(transform1.q.isSane()); + PX_ASSERT(transform0.q.isSane()); + + + const PxConvexMeshGeometryLL& shapeConvex = shape1.get(); + const PxSphereGeometry& shapeSphere = shape0.get(); + + Gu::PersistentContactManifold& manifold = cache.getManifold(); + + const Vec3V zeroV = V3Zero(); + + Ps::prefetchLine(shapeConvex.hullData); + const Vec3V vScale = V3LoadU_SafeReadW(shapeConvex.scale.scale); // PT: safe because 'rotation' follows 'scale' in PxMeshScale + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + const Gu::ConvexHullData* hullData = shapeConvex.hullData; + + //Transfer A into the local space of B + const PsTransformV transf0 = loadTransformA(transform0); + const PsTransformV transf1 = loadTransformA(transform1); + const PsTransformV curRTrans(transf1.transformInv(transf0)); + const PsMatTransformV aToB(curRTrans); + + const PxReal toleranceLength = params.mToleranceLength; + const FloatV convexMargin = Gu::CalculatePCMConvexMargin(hullData, vScale, toleranceLength); + + const PxU32 initialContacts = manifold.mNumContacts; + const FloatV minMargin = FMin(convexMargin, sphereRadius); + const FloatV projectBreakingThreshold = FMul(minMargin, FLoad(0.05f)); + + const FloatV refreshDistance = FAdd(sphereRadius, contactDist); + manifold.refreshContactPoints(aToB, projectBreakingThreshold, refreshDistance); + //ML: after refreshContactPoints, we might lose some contacts + const bool bLostContacts = (manifold.mNumContacts != initialContacts); + + if(bLostContacts || manifold.invalidate_SphereCapsule(curRTrans, minMargin)) + { + + GjkStatus status = manifold.mNumContacts > 0 ? GJK_UNDEFINED : GJK_NON_INTERSECT; + + manifold.setRelativeTransform(curRTrans); + + const QuatV vQuat = QuatVLoadU(&shapeConvex.scale.rotation.x); + + const bool idtScale = shapeConvex.scale.isIdentity(); + //use the original shape + ConvexHullV convexHull(hullData, V3LoadU(hullData->mCenterOfMass), vScale, vQuat, idtScale); + //transform capsule into the local space of convexHull + CapsuleV capsule(aToB.p, sphereRadius); + + GjkOutput output; + + LocalConvex convexA(capsule); + const Vec3V initialSearchDir = V3Sub(capsule.getCenter(), convexHull.getCenter()); + if(idtScale) + { + LocalConvex convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + status = gjkPenetration, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + } + else + { + LocalConvex convexB(convexHull); + status = gjkPenetration, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, true, + manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, output); + + } + + if(status == GJK_NON_INTERSECT) + { + return false; + } + else if(status == GJK_CONTACT) + { + Gu::PersistentContact& p = manifold.getContactPoint(0); + p.mLocalPointA = zeroV;//sphere center + p.mLocalPointB = output.closestB; + p.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + manifold.mNumContacts =1; + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, capsule.radius); +#endif + + //transform normal to world space + const Vec3V worldNormal = transf1.rotate(output.normal); + const Vec3V worldP = V3NegScaleSub(worldNormal, sphereRadius, transf0.p); + const FloatV penDep = FSub(output.penDep, sphereRadius); + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + return true; + + } + else if(status == GJK_DEGENERATE) + { + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + + return fullContactsGenerationSphereConvex(capsule, convexHull, transf0, transf1, manifoldContacts, contactBuffer, idtScale, + manifold, output.normal, contactDist, true, renderOutput); + } + else if(status == EPA_CONTACT) + { + + if(idtScale) + { + LocalConvex convexB(*PX_CONVEX_TO_NOSCALECONVEX(&convexHull)); + + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + + } + else + { + LocalConvex convexB(convexHull); + + status= Gu::epaPenetration(convexA, convexB, manifold.mAIndice, manifold.mBIndice, manifold.mNumWarmStartPoints, + true, FLoad(toleranceLength), output); + + } + + if(status == EPA_CONTACT) + { + Gu::PersistentContact& p = manifold.getContactPoint(0); + p.mLocalPointA = zeroV;//sphere center + p.mLocalPointB = output.closestB; + p.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + manifold.mNumContacts =1; + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, capsule.radius); +#endif + + //transform normal to world space + const Vec3V worldNormal = transf1.rotate(output.normal); + const Vec3V worldP = V3NegScaleSub(worldNormal, sphereRadius, transf0.p); + const FloatV penDep = FSub(output.penDep, sphereRadius); + + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + return true; + } + else + { + Gu::PersistentContact* manifoldContacts = PX_CP_TO_PCP(contactBuffer.contacts); + return fullContactsGenerationSphereConvex(capsule, convexHull, transf0, transf1, manifoldContacts, contactBuffer, idtScale, + manifold, output.normal, contactDist, true, renderOutput); + + } + + } + } + else if(manifold.mNumContacts > 0) + { + //ML:: the manifold originally has contacts + Gu::PersistentContact& p = manifold.getContactPoint(0); + const Vec3V worldNormal = transf1.rotate(Vec3V_From_Vec4V(p.mLocalNormalPen)); + const Vec3V worldP = V3NegScaleSub(worldNormal, sphereRadius, transf0.p); + const FloatV penDep = FSub(V4GetW(p.mLocalNormalPen), sphereRadius); + +#if PCM_LOW_LEVEL_DEBUG + manifold.drawManifold(*renderOutput, transf0, transf1, sphereRadius); +#endif + + addToContactBuffer(contactBuffer, worldNormal, worldP, penDep); + return true; + } + + return false; + +} +}//Gu +}//phyxs + diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp new file mode 100644 index 000000000..fb8288271 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp @@ -0,0 +1,159 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuVecBox.h" +#include "GuVecConvexHull.h" +#include "GuVecConvexHullNoScale.h" +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuHeightField.h" +#include "GuPCMContactConvexCommon.h" +#include "GuPCMContactMeshCallback.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +struct PCMSphereVsHeightfieldContactGenerationCallback : PCMHeightfieldContactGenerationCallback +{ + +public: + PCMSphereVsMeshContactGeneration mGeneration; + + PCMSphereVsHeightfieldContactGenerationCallback( + const Ps::aos::Vec3VArg sphereCenter, + const Ps::aos::FloatVArg sphereRadius, + const Ps::aos::FloatVArg contactDistance, + const Ps::aos::FloatVArg replaceBreakingThreshold, + + const PsTransformV& sphereTransform, + const PsTransformV& heightfieldTransform, + const PxTransform& heightfieldTransform1, + Gu::MultiplePersistentContactManifold& multiManifold, + Gu::ContactBuffer& contactBuffer, + Ps::InlineArray* deferredContacts, + Gu::HeightFieldUtil& hfUtil + + + ) : + PCMHeightfieldContactGenerationCallback(hfUtil, heightfieldTransform1), + mGeneration(sphereCenter, sphereRadius, contactDistance, replaceBreakingThreshold, sphereTransform, + heightfieldTransform, multiManifold, contactBuffer, deferredContacts) + { + } + + template + void processTriangleCache(Gu::TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + + +bool Gu::pcmContactSphereHeightField(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxHeightFieldGeometryLL& shapeHeight = shape1.get(); + + Gu::MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV sphereTransform(p0, q0);//sphere transform + const PsTransformV heightfieldTransform(p1, q1);//height feild + const PsTransformV curTransform = heightfieldTransform.transformInv(sphereTransform); + + + // We must be in local space to use the cache + + if(multiManifold.invalidate(curTransform, sphereRadius, FLoad(0.02f))) + { + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + const FloatV replaceBreakingThreshold = FMul(sphereRadius, FLoad(0.001f)); + Gu::HeightFieldUtil hfUtil(shapeHeight); + const PxVec3 sphereCenterShape1Space = transform1.transformInv(transform0.p); + const Vec3V sphereCenter = V3LoadU(sphereCenterShape1Space); + PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + PxVec3 inflatedRadiusV(inflatedRadius); + + PxBounds3 bounds(sphereCenterShape1Space - inflatedRadiusV, sphereCenterShape1Space + inflatedRadiusV); + + Ps::InlineArray delayedContacts; + + PCMSphereVsHeightfieldContactGenerationCallback blockCallback( + sphereCenter, + sphereRadius, + contactDist, + replaceBreakingThreshold, + sphereTransform, + heightfieldTransform, + transform1, + multiManifold, + contactBuffer, + &delayedContacts, + hfUtil); + + hfUtil.overlapAABBTriangles(transform1, bounds, 0, &blockCallback); + + blockCallback.mGeneration.generateLastContacts(); + blockCallback.mGeneration.processContacts(GU_SPHERE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(sphereRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(sphereRadius, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + + } + + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, sphereTransform, heightfieldTransform, sphereRadius); +} + + +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp new file mode 100644 index 000000000..549b78c79 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuVecTriangle.h" +#include "GuGeometryUnion.h" + +#include "GuContactMethodImpl.h" +#include "GuConvexUtilsInternal.h" +#include "PxTriangleMesh.h" +#include "GuContactBuffer.h" +#include "GuTriangleCache.h" +#include "GuPCMContactConvexCommon.h" +#include "GuHeightFieldUtil.h" +#include "GuPCMContactMeshCallback.h" +#include "GuBox.h" + + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + + +namespace physx +{ + + +struct PCMSphereVsMeshContactGenerationCallback : PCMMeshContactGenerationCallback< PCMSphereVsMeshContactGenerationCallback > +{ + +public: + PCMSphereVsMeshContactGeneration mGeneration; + + + PCMSphereVsMeshContactGenerationCallback( + const Ps::aos::Vec3VArg sphereCenter, + const Ps::aos::FloatVArg sphereRadius, + const Ps::aos::FloatVArg contactDist, + const Ps::aos::FloatVArg replaceBreakingThreshold, + const PsTransformV& sphereTransform, + const PsTransformV& meshTransform, + MultiplePersistentContactManifold& multiManifold, + ContactBuffer& contactBuffer, + const PxU8* extraTriData, + const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtMeshScale, + Ps::InlineArray* deferredContacts, + Cm::RenderOutput* renderOutput = NULL + ) : + PCMMeshContactGenerationCallback(meshScaling, extraTriData, idtMeshScale), + mGeneration(sphereCenter, sphereRadius, contactDist, replaceBreakingThreshold, sphereTransform, meshTransform, multiManifold, contactBuffer, deferredContacts, renderOutput) + { + } + + PX_FORCE_INLINE bool doTest(const PxVec3&, const PxVec3&, const PxVec3&) { return true; } + + template + void processTriangleCache(TriangleCache& cache) + { + mGeneration.processTriangleCache(cache); + } + +}; + + +bool Gu::pcmContactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + MultiplePersistentContactManifold& multiManifold = cache.getMultipleManifold(); + const PxSphereGeometry& shapeSphere = shape0.get(); + const PxTriangleMeshGeometryLL& shapeMesh = shape1.get(); + + const QuatV q0 = QuatVLoadA(&transform0.q.x); + const Vec3V p0 = V3LoadA(&transform0.p.x); + + const QuatV q1 = QuatVLoadA(&transform1.q.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV sphereRadius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV sphereTransform(p0, q0);//sphere transform + const PsTransformV meshTransform(p1, q1);//triangleMesh + const PsTransformV curTransform = meshTransform.transformInv(sphereTransform); + + // We must be in local space to use the cache + if(multiManifold.invalidate(curTransform, sphereRadius, FLoad(0.02f))) + { + const FloatV replaceBreakingThreshold = FMul(sphereRadius, FLoad(0.001f)); + const PxVec3 sphereCenterShape1Space = transform1.transformInv(transform0.p); + PxReal inflatedRadius = shapeSphere.radius + params.mContactDistance; + + const Vec3V sphereCenter = V3LoadU(sphereCenterShape1Space); + + const TriangleMesh* meshData = shapeMesh.meshData; + + Cm::FastVertex2ShapeScaling meshScaling; // PT: TODO: get rid of default ctor :( + const bool idtMeshScale = shapeMesh.scale.isIdentity(); + if(!idtMeshScale) + meshScaling.init(shapeMesh.scale); + + multiManifold.mNumManifolds = 0; + multiManifold.setRelativeTransform(curTransform); + + Ps::InlineArray delayedContacts; + + const PxU8* PX_RESTRICT extraData = meshData->getExtraTrigData(); + // mesh scale is not baked into cached verts + PCMSphereVsMeshContactGenerationCallback callback( + sphereCenter, + sphereRadius, + contactDist, + replaceBreakingThreshold, + sphereTransform, + meshTransform, + multiManifold, + contactBuffer, + extraData, + meshScaling, + idtMeshScale, + &delayedContacts, + renderOutput); + + PxVec3 obbCenter = sphereCenterShape1Space; + PxVec3 obbExtents = PxVec3(inflatedRadius); + PxMat33 obbRot(PxIdentity); + if(!idtMeshScale) + meshScaling.transformQueryBounds(obbCenter, obbExtents, obbRot); + const Box obb(obbCenter, obbExtents, obbRot); + + Midphase::intersectOBB(meshData, obb, callback, true); + + callback.flushCache(); + + callback.mGeneration.generateLastContacts(); + callback.mGeneration.processContacts(GU_SPHERE_MANIFOLD_CACHE_SIZE, false); + } + else + { + const PsMatTransformV aToB(curTransform); + const FloatV projectBreakingThreshold = FMul(sphereRadius, FLoad(0.05f)); + const FloatV refereshDistance = FAdd(sphereRadius, contactDist); + multiManifold.refreshManifold(aToB, projectBreakingThreshold, refereshDistance); + } + + //multiManifold.drawManifold(*gRenderOutPut, sphereTransform, meshTransform); + return multiManifold.addManifoldContactsToContactBuffer(contactBuffer, sphereTransform, meshTransform, sphereRadius); +} + +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp new file mode 100644 index 000000000..203ed52a1 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp @@ -0,0 +1,86 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuContactBuffer.h" +#include "PsVecTransform.h" + +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" + +namespace physx +{ + +namespace Gu +{ +bool pcmContactSpherePlane(GU_CONTACT_METHOD_ARGS) +{ + using namespace Ps::aos; + PX_UNUSED(renderOutput); + PX_UNUSED(cache); + PX_UNUSED(shape1); + + // Get actual shape data + const PxSphereGeometry& shapeSphere = shape0.get(); + + //sphere transform + const Vec3V p0 = V3LoadU_SafeReadW(transform0.p); // PT: safe because 'mRefCount' follows 'mTransform' in PxsTransform + + //plane transform + const Vec3V p1 = V3LoadU_SafeReadW(transform1.p); // PT: safe because 'mRefCount' follows 'mTransform' in PxsTransform + const QuatV q1 = QuatVLoadU(&transform1.q.x); + + const FloatV radius = FLoad(shapeSphere.radius); + const FloatV contactDist = FLoad(params.mContactDistance); + + const PsTransformV transf1(p1, q1); + //Sphere in plane space + const Vec3V sphereCenterInPlaneSpace = transf1.transformInv(p0); + + + //Separation + const FloatV separation = FSub(V3GetX(sphereCenterInPlaneSpace), radius); + + if(FAllGrtrOrEq(contactDist, separation)) + { + //get the plane normal + const Vec3V worldNormal = QuatGetBasisVector0(q1); + const Vec3V worldPoint = V3NegScaleSub(worldNormal, radius, p0); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(worldNormal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(worldPoint), &contact.point.x); + FStore(separation, &contact.separation); + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + return true; + } + return false; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp new file mode 100644 index 000000000..5ba3fee61 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp @@ -0,0 +1,84 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" + +#include "GuContactBuffer.h" +#include "GuContactMethodImpl.h" +#include "PsVecTransform.h" + +namespace physx +{ +namespace Gu +{ +bool pcmContactSphereSphere(GU_CONTACT_METHOD_ARGS) +{ + PX_UNUSED(cache); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + const PxSphereGeometry& shapeSphere0 = shape0.get(); + const PxSphereGeometry& shapeSphere1 = shape1.get(); + + const FloatV cDist = FLoad(params.mContactDistance); + const Vec3V p0 = V3LoadA(&transform0.p.x); + const Vec3V p1 = V3LoadA(&transform1.p.x); + + const FloatV r0 = FLoad(shapeSphere0.radius); + const FloatV r1 = FLoad(shapeSphere1.radius); + + + const Vec3V _delta = V3Sub(p0, p1); + const FloatV distanceSq = V3Dot(_delta, _delta); + const FloatV radiusSum = FAdd(r0, r1); + const FloatV inflatedSum = FAdd(radiusSum, cDist); + + if(FAllGrtr(FMul(inflatedSum, inflatedSum), distanceSq)) + { + const FloatV eps = FLoad(0.00001f); + const FloatV dist = FSqrt(distanceSq); + const BoolV bCon = FIsGrtrOrEq(eps, dist); + const Vec3V normal = V3Sel(bCon, V3UnitX(), V3ScaleInv(_delta, dist)); + const Vec3V point = V3ScaleAdd(normal, r1, p1); + const FloatV pen = FSub(dist, radiusSum); + + PX_ASSERT(contactBuffer.count < ContactBuffer::MAX_CONTACTS); + Gu::ContactPoint& contact = contactBuffer.contacts[contactBuffer.count++]; + V4StoreA(Vec4V_From_Vec3V(normal), &contact.normal.x); + V4StoreA(Vec4V_From_Vec3V(point), &contact.point.x); + FStore(pen, &contact.separation); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + + return true; + } + return false; +} +}//Gu +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp new file mode 100644 index 000000000..9f805a02c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp @@ -0,0 +1,210 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" +#include "GuPCMShapeConvex.h" +#include "GuVecConvexHull.h" +#include "GuPCMContactGen.h" +#include "CmRenderOutput.h" + +using namespace physx; +using namespace Gu; + +namespace physx +{ +namespace Gu +{ + const PxU8 gPCMBoxPolygonData[24] = + { + 0, 3, 2, 1, + 1, 2, 6, 5, + 5, 6, 7, 4, + 4, 7, 3, 0, + 3, 7, 6, 2, + 4, 0, 1, 5, + }; + + + Gu::PCMPolygonalBox::PCMPolygonalBox(const PxVec3& halfSide) : mHalfSide(halfSide) + { + //Precompute the convex data + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + PxVec3 minimum = -mHalfSide; + PxVec3 maximum = mHalfSide; + // Generate 8 corners of the bbox + mVertices[0] = PxVec3(minimum.x, minimum.y, minimum.z); + mVertices[1] = PxVec3(maximum.x, minimum.y, minimum.z); + mVertices[2] = PxVec3(maximum.x, maximum.y, minimum.z); + mVertices[3] = PxVec3(minimum.x, maximum.y, minimum.z); + mVertices[4] = PxVec3(minimum.x, minimum.y, maximum.z); + mVertices[5] = PxVec3(maximum.x, minimum.y, maximum.z); + mVertices[6] = PxVec3(maximum.x, maximum.y, maximum.z); + mVertices[7] = PxVec3(minimum.x, maximum.y, maximum.z); + + //Setup the polygons + for(PxU8 i=0; i < 6; i++) + { + mPolygons[i].mNbVerts = 4; + mPolygons[i].mVRef8 = PxU16(i*4); + } + + // ### planes needs *very* careful checks + // X axis + mPolygons[1].mPlane.n = PxVec3(1.0f, 0.0f, 0.0f); + mPolygons[1].mPlane.d = -mHalfSide.x; + mPolygons[3].mPlane.n = PxVec3(-1.0f, 0.0f, 0.0f); + mPolygons[3].mPlane.d = -mHalfSide.x; + + mPolygons[1].mMinIndex = 0; + mPolygons[3].mMinIndex = 1; + + PX_ASSERT(mPolygons[1].getMin(mVertices) == -mHalfSide.x); + PX_ASSERT(mPolygons[3].getMin(mVertices) == -mHalfSide.x); + + + // Y axis + mPolygons[4].mPlane.n = PxVec3(0.f, 1.0f, 0.0f); + mPolygons[4].mPlane.d = -mHalfSide.y; + mPolygons[5].mPlane.n = PxVec3(0.0f, -1.0f, 0.0f); + mPolygons[5].mPlane.d = -mHalfSide.y; + + mPolygons[4].mMinIndex = 0; + mPolygons[5].mMinIndex = 2; + + + PX_ASSERT(mPolygons[4].getMin(mVertices) == -mHalfSide.y); + PX_ASSERT(mPolygons[5].getMin(mVertices) == -mHalfSide.y); + + // Z axis + mPolygons[2].mPlane.n = PxVec3(0.f, 0.0f, 1.0f); + mPolygons[2].mPlane.d = -mHalfSide.z; + mPolygons[0].mPlane.n = PxVec3(0.0f, 0.0f, -1.0f); + mPolygons[0].mPlane.d = -mHalfSide.z; + + mPolygons[2].mMinIndex = 0; + mPolygons[0].mMinIndex = 4; + PX_ASSERT(mPolygons[2].getMin(mVertices) == -mHalfSide.z); + PX_ASSERT(mPolygons[0].getMin(mVertices) == -mHalfSide.z); + } + + void Gu::PCMPolygonalBox::getPolygonalData(Gu::PolygonalData* PX_RESTRICT dst) const + { + dst->mCenter = PxVec3(0.0f, 0.0f, 0.0f); + dst->mNbVerts = 8; + dst->mNbPolygons = 6; + dst->mPolygons = mPolygons; + dst->mNbEdges = 0; + dst->mVerts = mVertices; + dst->mPolygonVertexRefs = gPCMBoxPolygonData; + dst->mFacesByEdges = NULL; + dst->mVerticesByEdges = NULL; + dst->mInternal.mRadius = 0.0f; + dst->mInternal.mExtents[0] = 0.0f; + dst->mInternal.mExtents[1] = 0.0f; + dst->mInternal.mExtents[2] = 0.0f; + } + + static void getPCMPolygonalData_Convex(Gu::PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Ps::aos::Mat33V& vertexToShape) + { + using namespace Ps::aos; + const Vec3V vertexSpaceCenterOfMass = V3LoadU(src->mCenterOfMass); + const Vec3V shapeSpaceCenterOfMass = M33MulV3(vertexToShape, vertexSpaceCenterOfMass); + V3StoreU(shapeSpaceCenterOfMass, dst->mCenter); + dst->mNbVerts = src->mNbHullVertices; + dst->mNbPolygons = src->mNbPolygons; + dst->mNbEdges = src->mNbEdges; + dst->mPolygons = src->mPolygons; + dst->mVerts = src->getHullVertices(); + dst->mPolygonVertexRefs = src->getVertexData8(); + dst->mFacesByEdges = src->getFacesByEdges8(); + dst->mVerticesByEdges = src->getVerticesByEdges16(); + + dst->mBigData = src->mBigConvexRawData; + + dst->mInternal = src->mInternal; + } + + + static void getPCMPolygonalData_Convex(Gu::PolygonalData* PX_RESTRICT dst, const Gu::ConvexHullData* PX_RESTRICT src, const Cm::FastVertex2ShapeScaling& scaling) + { + dst->mCenter = scaling * src->mCenterOfMass; + dst->mNbVerts = src->mNbHullVertices; + dst->mNbPolygons = src->mNbPolygons; + dst->mNbEdges = src->mNbEdges; + dst->mPolygons = src->mPolygons; + dst->mVerts = src->getHullVertices(); + dst->mPolygonVertexRefs = src->getVertexData8(); + dst->mFacesByEdges = src->getFacesByEdges8(); + dst->mVerticesByEdges = src->getVerticesByEdges16(); + + dst->mBigData = src->mBigConvexRawData; + dst->mInternal = src->mInternal; + } + + + void getPCMConvexData(const Gu::ConvexHullV& convexHull, const bool idtScale, PolygonalData& polyData) + { + + PX_ASSERT(!convexHull.hullData->mAABB.isEmpty()); + + //this is used to calculate the convex hull's center of mass + getPCMPolygonalData_Convex(&polyData, convexHull.hullData, convexHull.vertex2Shape); + + if(!idtScale) + polyData.mInternal.reset(); + + } + + bool getPCMConvexData(const Gu::GeometryUnion& shape, Cm::FastVertex2ShapeScaling& scaling, PxBounds3& bounds, PolygonalData& polyData) + { + const PxConvexMeshGeometryLL& shapeConvex = shape.get(); + + const bool idtScale = shapeConvex.scale.isIdentity(); + if(!idtScale) + scaling.init(shapeConvex.scale); + + PX_ASSERT(!shapeConvex.hullData->mAABB.isEmpty()); + bounds = shapeConvex.hullData->mAABB.transformFast(scaling.getVertex2ShapeSkew()); + + getPCMPolygonalData_Convex(&polyData, shapeConvex.hullData, scaling); + + return idtScale; + } + +} +} + diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h new file mode 100644 index 000000000..a86b06b1d --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_SHAPE_CONVEX_H +#define GU_PCM_SHAPE_CONVEX_H + +#include "GuConvexSupportTable.h" +#include "GuPersistentContactManifold.h" +#include "GuShapeConvex.h" + + +namespace physx +{ + +namespace Gu +{ + class GeometryUnion; + + extern const PxU8 gPCMBoxPolygonData[24]; + + class PCMPolygonalBox + { + public: + PCMPolygonalBox(const PxVec3& halfSide); + + void getPolygonalData(Gu::PolygonalData* PX_RESTRICT dst) const; + + const PxVec3& mHalfSide; + PxVec3 mVertices[8]; + Gu::HullPolygonData mPolygons[6]; + private: + PCMPolygonalBox& operator=(const PCMPolygonalBox&); + }; + + + void getPCMConvexData(const Gu::ConvexHullV& convexHull, const bool idtScale, Gu::PolygonalData& polyData); + bool getPCMConvexData(const Gu::GeometryUnion& shape, Cm::FastVertex2ShapeScaling& scaling, PxBounds3& bounds, Gu::PolygonalData& polyData); + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp new file mode 100644 index 000000000..efd27ee93 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp @@ -0,0 +1,1254 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuGeometryUnion.h" +#include "GuPCMTriangleContactGen.h" +#include "GuPCMContactConvexCommon.h" +#include "GuVecTriangle.h" +#include "GuBarycentricCoordinates.h" +#include "GuConvexEdgeFlags.h" + +#if PCM_LOW_LEVEL_DEBUG +#include "PxRenderBuffer.h" +#endif + +#define EDGE_EDGE_GAUSS_MAP 0 +#define BRUTE_FORCE_EDGE_EDGE 0 + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +namespace physx +{ + static bool testPolyFaceNormal(const Gu::TriangleV& triangle, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, PxU32& feature, Vec3V& faceNormal, const FeatureStatus faceStatus, FeatureStatus& status) + { + PX_UNUSED(triangle); + + FloatV _minOverlap = FMax(); + PxU32 _feature = 0; + Vec3V _faceNormal = faceNormal; + FloatV min0, max0; + FloatV min1, max1; + const FloatV eps = FEps(); + + if(polyMap->isIdentityScale) + { + //in the local space of polyData0 + for(PxU32 i=0; idoSupport(planeNormal, min1, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + _feature = i; + _faceNormal = planeNormal; + } + } + } + else + { + + //in the local space of polyData0 + for(PxU32 i=0; ishape2Vertex, vertexSpacePlaneNormal); + + const FloatV magnitude = FRsqrtFast(V3LengthSq(shapeSpacePlaneNormal)); //FRecip(V3Length(shapeSpacePlaneNormal)); + + //ML::avoid lHS, don't use the exiting function + min0 = FMul(V3Dot(vertexSpacePlaneNormal, minVert), magnitude); + max0 = FMul(FNeg(planeDist), magnitude); + + //normalize the shapeSpacePlaneNormal + const Vec3V planeN = V3Scale(shapeSpacePlaneNormal, magnitude); + + triMap->doSupport(planeN, min1, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if(FAllGrtr(_minOverlap, tempOverlap)) + { + _minOverlap = tempOverlap; + _feature = i; + _faceNormal = planeN; + } + } + } + + if(FAllGrtr(minOverlap, FAdd(_minOverlap, eps))) + { + faceNormal = _faceNormal; + minOverlap = _minOverlap; + status = faceStatus; + } + + feature = _feature; + + return true; + + } + + + + //triangle is in the local space of polyData + static bool testTriangleFaceNormal(const TriangleV& triangle, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, PxU32& feature, Vec3V& faceNormal, const FeatureStatus faceStatus, FeatureStatus& status) + { + PX_UNUSED(triMap); + PX_UNUSED(polyData); + + FloatV min1, max1; + const FloatV eps = FEps(); + + const Vec3V triangleLocNormal = triangle.normal(); + + const FloatV min0 = V3Dot(triangleLocNormal, triangle.verts[0]); + const FloatV max0 = min0; + + //triangle normal is in the vertex space + polyMap->doSupport(triangleLocNormal, min1, max1); + + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + + if(BAllEqTTTT(con)) + return false; + + minOverlap = FSub(FSub(max0, min1), eps); + status = faceStatus; + feature = 0; + faceNormal=triangleLocNormal; + + return true; + + } + + static bool testPolyEdgeNormal(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status) + { + PX_UNUSED(triFlags); + FloatV overlap = minOverlap; + FloatV min0, max0; + FloatV min1, max1; + const FloatV zero = FZero(); + const Vec3V eps2 = V3Splat(FLoad(1e-6f)); + + const Vec3V v0 = M33MulV3(polyMap->shape2Vertex, triangle.verts[0]); + const Vec3V v1 = M33MulV3(polyMap->shape2Vertex, triangle.verts[1]); + const Vec3V v2 = M33MulV3(polyMap->shape2Vertex, triangle.verts[2]); + + TriangleV vertexSpaceTriangle(v0, v1, v2); + + PxU32 nbTriangleAxes = 0; + Vec3V triangleAxes[3]; + for(PxI8 kStart = 0, kEnd =2; kStart<3; kEnd = kStart++) + { + bool active = (triFlags & (1 << (kEnd+3))) != 0; + + if(active) + { + const Vec3V p00 = vertexSpaceTriangle.verts[kStart]; + const Vec3V p01 = vertexSpaceTriangle.verts[kEnd]; + triangleAxes[nbTriangleAxes++] = V3Sub(p01, p00); + } + } + + if(nbTriangleAxes == 0) + return true; + + //create localTriPlane in the vertex space + const Vec3V vertexSpaceTriangleNormal = vertexSpaceTriangle.normal(); + + for(PxU32 i =0; ishape2Vertex, v); + const Vec3V n0 = V3Normalize(shapeSpaceV); + triMap->doSupport(n0, min0, max0); + polyMap->doSupport(n0, min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if (BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if (FAllGrtr(overlap, tempOverlap)) + { + overlap = tempOverlap; + minNormal = n0; + status = edgeStatus; + } + + } + } + + } + } + minOverlap = overlap; + + return true; + + } + +#if BRUTE_FORCE_EDGE_EDGE + + + bool testPolyEdgeNormalBruteForce(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status) + { + PX_UNUSED(triFlags); + FloatV min0, max0; + FloatV min1, max1; + + FloatV bestDist = FLoad(PX_MAX_F32); + Vec3V bestAxis = V3Zero(); + + const Vec3V eps2 = V3Splat(FLoad(1e-6)); + + PxU32 bestPolyIndex = 0; + PxU32 bestStart = 0; + PxU32 bestEnd = 0; + PxI8 bestTriStart = 0; + PxI8 bestTriEnd = 0; + + for(PxU32 i =0; ivertex2Shape, p10); + const Vec3V vertex11 = M33MulV3(polyMap->vertex2Shape, p11); + + const Vec3V convexEdge = V3Sub(vertex11, vertex10); + + for (PxI8 kEnd = 0, kStart = 2; kEnd<3; kStart = kEnd++) + { + + const Vec3V triVert0 = triangle.verts[kStart]; + const Vec3V triVert1 = triangle.verts[kEnd]; + + const Vec3V triangleEdge = V3Sub(triVert1, triVert0); + const Vec3V v = V3Cross(convexEdge, triangleEdge); + + if (!V3AllGrtr(eps2, V3Abs(v))) + { + //transform the v back to the shape space + const Vec3V n0 = V3Normalize(v); + triMap->doSupport(n0, min0, max0); + polyMap->doSupport(n0, min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if(BAllEqTTTT(con)) + return false; + + const FloatV tempOverlap = FSub(max0, min1); + + if (FAllGrtr(bestDist, tempOverlap)) + { + bestDist = tempOverlap; + bestAxis = n0; + bestPolyIndex = i; + bestStart = lStart; + bestEnd = lEnd; + bestTriStart = kStart; + bestTriEnd = kEnd; + } + + } + } + } + } + + if (FAllGrtr(minOverlap, bestDist)) + { + minOverlap = bestDist; + minNormal = bestAxis; + status = edgeStatus; + } + + return true; + + } + + bool testPolyEdgeNormalBruteForceVertsByEdges(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status, PxU32& bestEdgeIndex, PxI8& bestTriStart, PxI8& bestTriEnd) + { + + PX_UNUSED(triFlags); + + const PxU32 numConvexEdges = polyData.mNbEdges; + const PxU16* verticesByEdges16 = polyData.mVerticesByEdges; + const PxVec3* vertices = polyData.mVerts; + + FloatV bestDist = FLoad(PX_MAX_F32); + Vec3V bestAxis = V3Zero(); + const Vec3V eps2 = V3Splat(FLoad(1e-6)); + + for (PxU32 convexEdgeIdx = 0; convexEdgeIdx < numConvexEdges; ++convexEdgeIdx) + { + const PxU16 v0idx1 = verticesByEdges16[convexEdgeIdx * 2]; + const PxU32 v1idx1 = verticesByEdges16[convexEdgeIdx * 2 + 1]; + + //shape sapce + const Vec3V vertex10 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v0idx1])); + const Vec3V vertex11 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v1idx1])); + + Vec3V convexEdge = V3Sub(vertex11, vertex10); + + + for (PxI8 kEnd = 0, kStart = 2; kEnd<3; kStart = kEnd++) + { + const Vec3V triVert0 = triangle.verts[kStart]; + const Vec3V triVert1 = triangle.verts[kEnd]; + const Vec3V triEdge = V3Sub(triVert1, triVert0); + + // compute the separation along this axis in vertex space + Vec3V axis = V3Cross(convexEdge, triEdge); + + if (!V3AllGrtr(eps2, V3Abs(axis))) + { + axis = V3Normalize(axis); + + FloatV min0, max0; + FloatV min1, max1; + triMap->doSupport(axis, min0, max0); + polyMap->doSupport(axis, min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if (BAllEqTTTT(con)) + return false; + + const FloatV dist0 = FSub(max0, min1); + const FloatV dist1 = FSub(max1, min0); + + if (FAllGrtr(dist0, dist1)) + axis = V3Neg(axis); + + const FloatV dist = FMin(dist0, dist1); + + if (FAllGrtr(bestDist, dist)) + { + bestDist = dist; + bestAxis = axis; + + bestEdgeIndex = convexEdgeIdx; + bestTriStart = kStart; + bestTriEnd = kEnd; + } + } + } + + } + + if (FAllGrtr(minOverlap, bestDist)) + { + minOverlap = bestDist; + minNormal = bestAxis; + status = edgeStatus; + + } + return true; + + } + + +#endif + +#if EDGE_EDGE_GAUSS_MAP + + + + bool isMinkowskiFace(const Vec3V& A, const Vec3V& B, const Vec3V& B_x_A, const Vec3V& C, const Vec3V& D, const Vec3V& D_x_C) + { + + const FloatV zero = FZero(); + // Two edges build a face on the Minkowski sum if the associated arcs AB and CD intersect on the Gauss map. + // The associated arcs are defined by the adjacent face normals of each edge. + const FloatV CBA = V3Dot(C, B_x_A); + const FloatV DBA = V3Dot(D, B_x_A); + const FloatV ADC = V3Dot(A, D_x_C); + const FloatV BDC = V3Dot(B, D_x_C); + + const BoolV con0 = FIsGrtrOrEq(zero, FMul(CBA, DBA)); + const BoolV con1 = FIsGrtrOrEq(zero, FMul(ADC, BDC)); + const BoolV con2 = FIsGrtr(FMul(CBA, BDC), zero); + + const BoolV con = BAnd(con2, BAnd(con0, con1)); + + return (BAllEqTTTT(con) != 0); + + //return CBA * DBA < 0.0f && ADC * BDC < 0.0f && CBA * BDC > 0.0f; + } + +#define SAT_VARIFY 0 + + bool testPolyEdgeNormalGaussMap(const TriangleV& triangle, const PxU8 triFlags, const PolygonalData& polyData, SupportLocalImpl* triMap, SupportLocal* polyMap, const FloatVArg contactDist, + FloatV& minOverlap, Vec3V& minNormal, const FeatureStatus edgeStatus, FeatureStatus& status, PxU32& gaussMapBestEdgeIndex, PxI8& gaussMapBestTriStart, PxI8& gaussMapBestTriEnd) + { + + PX_UNUSED(triFlags); + const FloatV zero = FZero(); + + const Vec3V triNormal = triangle.normal(); + + const PxU32 numConvexEdges = polyData.mNbEdges; + const PxU16* verticesByEdges16 = polyData.mVerticesByEdges; + const PxU8* facesByEdges8 = polyData.mFacesByEdges; + const PxVec3* vertices = polyData.mVerts; + + FloatV bestDist = FLoad(-PX_MAX_F32); + Vec3V axis = V3Zero(); + Vec3V bestAxis = V3Zero(); + const Vec3V eps2 = V3Splat(FLoad(1e-6)); + + + //Center is in shape space + const Vec3V shapeSpaceCOM =V3LoadU(polyData.mCenter); + const Vec3V vertexSpaceCOM = M33MulV3(polyMap->shape2Vertex, shapeSpaceCOM); + + PxVec3 vertexCOM; + V3StoreU(vertexSpaceCOM, vertexCOM); + + for (PxU32 convexEdgeIdx = 0; convexEdgeIdx < numConvexEdges; ++convexEdgeIdx) + { + + const PxU16 v0idx1 = verticesByEdges16[convexEdgeIdx*2]; + const PxU32 v1idx1 = verticesByEdges16[convexEdgeIdx * 2 + 1]; + + const PxU8 f10 = facesByEdges8[convexEdgeIdx * 2]; + const PxU8 f11 = facesByEdges8[convexEdgeIdx * 2 + 1]; + + //shape sapce + const Vec3V vertex10 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v0idx1])); + const Vec3V vertex11 = M33MulV3(polyMap->vertex2Shape, V3LoadU(vertices[v1idx1])); + + Vec3V convexEdge = V3Sub(vertex11, vertex10); + + const Gu::HullPolygonData& face0 = polyData.mPolygons[f10]; + const Gu::HullPolygonData& face1 = polyData.mPolygons[f11]; + + const Vec3V convexNormal0 = M33TrnspsMulV3(polyMap->shape2Vertex, V3LoadU(face0.mPlane.n)); + const Vec3V convexNormal1 = M33TrnspsMulV3(polyMap->shape2Vertex, V3LoadU(face1.mPlane.n)); + + float signDist0 = face0.mPlane.distance(vertexCOM); + float signDist1 = face1.mPlane.distance(vertexCOM); + + PX_ASSERT(signDist0 < 0.f); + PX_ASSERT(signDist1 < 0.f); + + for (PxI8 kEnd = 0, kStart = 2; kEnd<3; kStart = kEnd++) + { + + const Vec3V triVert0 = triangle.verts[kStart]; + const Vec3V triVert1 = triangle.verts[kEnd]; + const Vec3V triEdge = V3Sub(triVert1, triVert0); + + //if (isMinkowskiFace(convexNormal0, convexNormal1, V3Neg(convexEdge), V3Neg(triNormal), triNormal, V3Neg(triEdge))) + if (isMinkowskiFace(convexNormal0, convexNormal1, convexEdge, V3Neg(triNormal), triNormal, triEdge)) + { + + // compute the separation along this axis in vertex space + axis = V3Cross(convexEdge, triEdge); + + if (!V3AllGrtr(eps2, V3Abs(axis))) + { + axis = V3Normalize(axis); + + const Vec3V v = V3Sub(vertex10, shapeSpaceCOM); + + // ensure the axis is outward pointing on the edge on the minkowski sum. Assure normal points from convex hull to triangle + const FloatV temp = V3Dot(axis, v); + if (FAllGrtr(zero, temp)) + axis = V3Neg(axis); + + //compute the distance from any of the verts in the triangle to plane(axis, vertex10)(n.dot(p-a)) + const Vec3V ap = V3Sub(triVert0, vertex10); + + const FloatV dist = V3Dot(axis, ap); + + if (FAllGrtr(dist, contactDist)) + return false; + +#if SAT_VARIFY + FloatV min0, max0; + FloatV min1, max1; + triMap->doSupport(V3Neg(axis), min0, max0); + polyMap->doSupport(V3Neg(axis), min1, max1); + const BoolV con = BOr(FIsGrtr(min1, FAdd(max0, contactDist)), FIsGrtr(min0, FAdd(max1, contactDist))); + if (BAllEqTTTT(con)) + return false; + + /*const FloatV dist = FSub(max0, min1); + + if (FAllGrtr(bestDist, dist)) + { + bestDist = dist; + bestAxis = axis; + }*/ + const FloatV tempDist = FSub(max0, min1); + + const FloatV dif = FAbs(FAdd(tempDist, dist)); + PX_UNUSED(dif); + PX_ASSERT(FAllGrtr(FLoad(1e-4f), dif)); + +#endif + + if (FAllGrtr(dist, bestDist)) + { + bestDist = dist; + bestAxis = axis; + + gaussMapBestEdgeIndex = convexEdgeIdx; + gaussMapBestTriStart = kStart; + gaussMapBestTriEnd = kEnd; + } + } + } + } + + } + + if (FAllGrtr(minOverlap, bestDist)) + { + minOverlap = bestDist; + minNormal = bestAxis; + status = edgeStatus; + } + return true; + + } + +#endif + + static PX_FORCE_INLINE PxU32 addMeshContacts(MeshPersistentContact* manifoldContacts, const Vec3V& pA, const Vec3V& pB, const Vec4V& normalPen, const PxU32 triangleIndex, const PxU32 numContacts) + { + manifoldContacts[numContacts].mLocalPointA = pA; + manifoldContacts[numContacts].mLocalPointB = pB; + manifoldContacts[numContacts].mLocalNormalPen = normalPen; + manifoldContacts[numContacts].mFaceIndex = triangleIndex; + return numContacts+1; + } + + + static void generatedTriangleContacts(const Gu::TriangleV& triangle, const PxU32 triangleIndex, const PxU8/* _triFlags*/, const Gu::PolygonalData& polyData1, const Gu::HullPolygonData& incidentPolygon, Gu::SupportLocal* map1, Gu::MeshPersistentContact* manifoldContacts, PxU32& numManifoldContacts, + const Ps::aos::FloatVArg contactDist, const Ps::aos::Vec3VArg contactNormal, Cm::RenderOutput* renderOutput) + { + + PX_UNUSED(renderOutput); + using namespace Ps::aos; + + //PxU8 triFlags = _triFlags; + const PxU32 previousContacts = numManifoldContacts; + + const FloatV zero = FZero(); + + const Mat33V rot = findRotationMatrixFromZAxis(contactNormal); + + const PxU8* inds1 = polyData1.mPolygonVertexRefs + incidentPolygon.mVRef8; + + Vec3V points0In0[3]; + Vec3V* points1In0 = reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*incidentPolygon.mNbVerts, 16)); + FloatV* points1In0TValue = reinterpret_cast(PxAllocaAligned(sizeof(FloatV)*incidentPolygon.mNbVerts, 16)); + bool* points1In0Penetration = reinterpret_cast(PxAlloca(sizeof(bool)*incidentPolygon.mNbVerts)); + + + points0In0[0] = triangle.verts[0]; + points0In0[1] = triangle.verts[1]; + points0In0[2] = triangle.verts[2]; + + + + //Transform all the verts from vertex space to shape space + map1->populateVerts(inds1, incidentPolygon.mNbVerts, polyData1.mVerts, points1In0); + +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map1->transform, points1In0, incidentPolygon.mNbVerts, (PxU32)PxDebugColor::eARGB_RED); + //Gu::PersistentContactManifold::drawTriangle(*renderOutput, map1->transform.transform(points1In0[0]), map1->transform.transform(points0In0[1]), map1->transform.transform(points0In0[2]), (PxU32)PxDebugColor::eARGB_BLUE); +#endif + + Vec3V eps = Vec3V_From_FloatV(FEps()); + Vec3V max = Vec3V_From_FloatV(FMax()); + Vec3V nmax = V3Neg(max); + + //transform reference polygon to 2d, calculate min and max + Vec3V rPolygonMin= max; + Vec3V rPolygonMax = nmax; + for(PxU32 i=0; i<3; ++i) + { + points0In0[i] = M33MulV3(rot, points0In0[i]); + rPolygonMin = V3Min(rPolygonMin, points0In0[i]); + rPolygonMax = V3Max(rPolygonMax, points0In0[i]); + } + + + rPolygonMin = V3Sub(rPolygonMin, eps); + rPolygonMax = V3Add(rPolygonMax, eps); + + const FloatV d = V3GetZ(points0In0[0]); + const FloatV rd = FAdd(d, contactDist); + + Vec3V iPolygonMin= max; + Vec3V iPolygonMax = nmax; + + + PxU32 inside = 0; + for(PxU32 i=0; i= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + + points1In0Penetration[i] = penetrated; + } + + + + if(inside == incidentPolygon.mNbVerts) + { + return; + } + + inside = 0; + iPolygonMin = V3Sub(iPolygonMin, eps); + iPolygonMax = V3Add(iPolygonMax, eps); + + const Vec3V incidentNormal = V3Normalize(M33TrnspsMulV3(map1->shape2Vertex, V3LoadU(incidentPolygon.mPlane.n))); + const FloatV iPlaneD = V3Dot(incidentNormal, M33MulV3(map1->vertex2Shape, V3LoadU(polyData1.mVerts[inds1[0]]))); + + for(PxU32 i=0; i<3; ++i) + { + if(contains(points1In0, incidentPolygon.mNbVerts, points0In0[i], iPolygonMin, iPolygonMax)) + { + + inside++; + + const Vec3V vert0 = M33TrnspsMulV3(rot, points0In0[i]); + const FloatV t = FSub(V3Dot(incidentNormal, vert0), iPlaneD); + + if(FAllGrtr(t, contactDist)) + continue; + + + const Vec3V projPoint = V3NegScaleSub(incidentNormal, t, vert0); + + const Vec3V v = V3Sub(projPoint, vert0); + const FloatV t3 = V3Dot(v, contactNormal); + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(contactNormal), t3); + numManifoldContacts = addMeshContacts(manifoldContacts, projPoint, vert0, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if(numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + + } + + + if(inside == 3) + return; + + //(2) segment intesection + for (PxU32 rStart = 0, rEnd = 2; rStart < 3; rEnd = rStart++) + { + + + const Vec3V rpA = points0In0[rStart]; + const Vec3V rpB = points0In0[rEnd]; + + const Vec3V rMin = V3Min(rpA, rpB); + const Vec3V rMax = V3Max(rpA, rpB); + + for (PxU32 iStart = 0, iEnd = PxU32(incidentPolygon.mNbVerts - 1); iStart < incidentPolygon.mNbVerts; iEnd = iStart++) + { + if ((!points1In0Penetration[iStart] && !points1In0Penetration[iEnd]))//|| (points1In0[i].status == POINT_OUTSIDE && points1In0[incidentIndex].status == POINT_OUTSIDE)) + continue; + + const Vec3V ipA = points1In0[iStart]; + const Vec3V ipB = points1In0[iEnd]; + + const Vec3V iMin = V3Min(ipA, ipB); + const Vec3V iMax = V3Max(ipA, ipB); + + const BoolV tempCon = BOr(V3IsGrtr(iMin, rMax), V3IsGrtr(rMin, iMax)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if (BAllEqTTTT(con)) + continue; + + FloatV a1 = signed2DTriArea(rpA, rpB, ipA); + FloatV a2 = signed2DTriArea(rpA, rpB, ipB); + + if (FAllGrtr(zero, FMul(a1, a2))) + { + FloatV a3 = signed2DTriArea(ipA, ipB, rpA); + FloatV a4 = signed2DTriArea(ipA, ipB, rpB); + + if (FAllGrtr(zero, FMul(a3, a4))) + { + + //these two segment intersect in 2d + const FloatV t = FMul(a1, FRecip(FSub(a2, a1))); + + const Vec3V ipAOri = V3SetZ(points1In0[iStart], FAdd(points1In0TValue[iStart], d)); + const Vec3V ipBOri = V3SetZ(points1In0[iEnd], FAdd(points1In0TValue[iEnd], d)); + + const Vec3V pBB = V3NegScaleSub(V3Sub(ipBOri, ipAOri), t, ipAOri); + const Vec3V pAA = V3SetZ(pBB, d); + const Vec3V pA = M33TrnspsMulV3(rot, pAA); + const Vec3V pB = M33TrnspsMulV3(rot, pBB); + const FloatV pen = FSub(V3GetZ(pBB), V3GetZ(pAA)); + + if (FAllGrtr(pen, contactDist)) + continue; + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(contactNormal), pen); + numManifoldContacts = addMeshContacts(manifoldContacts, pB, pA, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if (numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + } + + } + + } + + + static void generatedPolyContacts(const Gu::PolygonalData& polyData0, const Gu::HullPolygonData& referencePolygon, const Gu::TriangleV& triangle, const PxU32 triangleIndex, const PxU8 triFlags, + Gu::SupportLocal* map0, Gu::MeshPersistentContact* manifoldContacts, PxU32& numManifoldContacts, const Ps::aos::FloatVArg contactDist, const Ps::aos::Vec3VArg contactNormal, + Cm::RenderOutput* renderOutput) + { + PX_UNUSED(triFlags); + PX_UNUSED(renderOutput); + + using namespace Ps::aos; + + const FloatV zero = FZero(); + + const PxU32 previousContacts = numManifoldContacts; + + const PxU8* inds0 = polyData0.mPolygonVertexRefs + referencePolygon.mVRef8; + + const Vec3V nContactNormal = V3Neg(contactNormal); + + //this is the matrix transform all points to the 2d plane + const Mat33V rot = findRotationMatrixFromZAxis(contactNormal); + + Vec3V* points0In0=reinterpret_cast(PxAllocaAligned(sizeof(Vec3V)*referencePolygon.mNbVerts, 16)); + Vec3V points1In0[3]; + FloatV points1In0TValue[3]; + + bool points1In0Penetration[3]; + + //Transform all the verts from vertex space to shape space + map0->populateVerts(inds0, referencePolygon.mNbVerts, polyData0.mVerts, points0In0); + + points1In0[0] = triangle.verts[0]; + points1In0[1] = triangle.verts[1]; + points1In0[2] = triangle.verts[2]; + + +#if PCM_LOW_LEVEL_DEBUG + Gu::PersistentContactManifold::drawPolygon(*renderOutput, map0->transform, points0In0, referencePolygon.mNbVerts, (PxU32)PxDebugColor::eARGB_GREEN); + //Gu::PersistentContactManifold::drawTriangle(*gRenderOutPut, map0->transform.transform(points1In0[0]), map0->transform.transform(points1In0[1]), map0->transform.transform(points1In0[2]), (PxU32)PxDebugColor::eARGB_BLUE); +#endif + + //the first point in the reference plane + const Vec3V referencePoint = points0In0[0]; + + Vec3V eps = Vec3V_From_FloatV(FEps()); + Vec3V max = Vec3V_From_FloatV(FMax()); + Vec3V nmax = V3Neg(max); + + //transform reference polygon to 2d, calculate min and max + Vec3V rPolygonMin= max; + Vec3V rPolygonMax = nmax; + for(PxU32 i=0; i= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + + } + + + if(inside == 3) + { + return; + } + + inside = 0; + iPolygonMin = V3Sub(iPolygonMin, eps); + iPolygonMax = V3Add(iPolygonMax, eps); + + const Vec3V incidentNormal = triangle.normal(); + const FloatV iPlaneD = V3Dot(incidentNormal, triangle.verts[0]); + const FloatV one = FOne(); + for(PxU32 i=0; i= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + + } + + } + + if(inside == referencePolygon.mNbVerts) + return; + + + + //Always generate segment contacts + //(2) segment intesection + for (PxU32 iStart = 0, iEnd = 2; iStart < 3; iEnd = iStart++) + { + if((!points1In0Penetration[iStart] && !points1In0Penetration[iEnd] ) ) + continue; + + const Vec3V ipA = points1In0[iStart]; + const Vec3V ipB = points1In0[iEnd]; + + const Vec3V iMin = V3Min(ipA, ipB); + const Vec3V iMax = V3Max(ipA, ipB); + + for (PxU32 rStart = 0, rEnd = PxU32(referencePolygon.mNbVerts - 1); rStart < referencePolygon.mNbVerts; rEnd = rStart++) + { + + const Vec3V rpA = points0In0[rStart]; + const Vec3V rpB = points0In0[rEnd]; + + const Vec3V rMin = V3Min(rpA, rpB); + const Vec3V rMax = V3Max(rpA, rpB); + + const BoolV tempCon =BOr(V3IsGrtr(iMin, rMax), V3IsGrtr(rMin, iMax)); + const BoolV con = BOr(BGetX(tempCon), BGetY(tempCon)); + + if(BAllEqTTTT(con)) + continue; + + + FloatV a1 = signed2DTriArea(rpA, rpB, ipA); + FloatV a2 = signed2DTriArea(rpA, rpB, ipB); + + + if(FAllGrtr(zero, FMul(a1, a2))) + { + FloatV a3 = signed2DTriArea(ipA, ipB, rpA); + FloatV a4 = signed2DTriArea(ipA, ipB, rpB); + + if(FAllGrtr(zero, FMul(a3, a4))) + { + + //these two segment intersect + const FloatV t = FMul(a1, FRecip(FSub(a2, a1))); + + const Vec3V ipAOri = V3SetZ(points1In0[iStart], FAdd(points1In0TValue[iStart], d)); + const Vec3V ipBOri = V3SetZ(points1In0[iEnd], FAdd(points1In0TValue[iEnd], d)); + + const Vec3V pBB = V3NegScaleSub(V3Sub(ipBOri, ipAOri), t, ipAOri); + const Vec3V pAA = V3SetZ(pBB, d); + const Vec3V pA = M33TrnspsMulV3(rot, pAA); + const Vec3V pB = M33TrnspsMulV3(rot, pBB); + const FloatV pen = FSub(V3GetZ(pBB), V3GetZ(pAA)); + + if(FAllGrtr(pen, contactDist)) + continue; + + + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(nContactNormal), pen); + numManifoldContacts = addMeshContacts(manifoldContacts, pA, pB, localNormalPen, triangleIndex, numManifoldContacts); + + //if the numContacts are more than GU_MESH_CONTACT_REDUCTION_THRESHOLD, we need to do contact reduction + const PxU32 numContacts = numManifoldContacts - previousContacts; + if(numContacts >= GU_MESH_CONTACT_REDUCTION_THRESHOLD) + { + //a polygon has more than GU_MESH_CONTACT_REDUCTION_THRESHOLD(16) contacts with this triangle, we will reduce + //the contacts to GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE(4) points + Gu::SinglePersistentContactManifold::reduceContacts(&manifoldContacts[previousContacts], numContacts); + numManifoldContacts = previousContacts + GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE; + } + } + } + } + } + + } + + + bool Gu::PCMConvexVsMeshContactGeneration::generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU32* triIndices, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal) + { + + using namespace Ps::aos; + + { + + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + //minNormal will be in the local space of polyData + Vec3V minNormal = V3Zero(); + + + PxU32 feature0; + if(!testTriangleFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + if(!testPolyFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + + if (!testPolyEdgeNormal(localTriangle, triFlags, polyData, localTriMap, polyMap, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + + const Vec3V triNormal = localTriangle.normal(); + + if(status == POLYDATA0) + { + //minNormal is the triangle normal and it is in the local space of polydata0 + const Gu::HullPolygonData& referencePolygon = polyData.mPolygons[getPolygonIndex(polyData, polyMap, minNormal)]; + + patchNormal = triNormal; + generatedTriangleContacts(localTriangle, triangleIndex, triFlags, polyData, referencePolygon, polyMap, manifoldContacts, numContacts, contactDist, triNormal, mRenderOutput); + + } + else + { + + if(status == POLYDATA1) + { + const Gu::HullPolygonData* referencePolygon = &polyData.mPolygons[feature1]; + + const FloatV cosTheta = V3Dot(V3Neg(minNormal), triNormal); + + const FloatV threshold = FLoad(0.707106781f);//about 45 degree0 + + if(FAllGrtr(cosTheta, threshold)) + { + patchNormal = triNormal; + generatedTriangleContacts(localTriangle, triangleIndex, triFlags, polyData, *referencePolygon, polyMap, manifoldContacts, numContacts, contactDist, triNormal, mRenderOutput); + } + else + { + //ML : defer the contacts generation + + if (mSilhouetteEdgesAreActive || + !(triFlags & (ETD_SILHOUETTE_EDGE_01 | ETD_SILHOUETTE_EDGE_12 | ETD_SILHOUETTE_EDGE_20))) + { + const PxU32 nb = sizeof(PCMDeferredPolyData) / sizeof(PxU32); + PxU32 newSize = nb + mDeferredContacts->size(); + mDeferredContacts->reserve(newSize); + PCMDeferredPolyData* PX_RESTRICT data = reinterpret_cast(mDeferredContacts->end()); + mDeferredContacts->forceSize_Unsafe(newSize); + + data->mTriangleIndex = triangleIndex; + data->mFeatureIndex = feature1; + data->triFlags = triFlags; + data->mInds[0] = triIndices[0]; + data->mInds[1] = triIndices[1]; + data->mInds[2] = triIndices[2]; + V3StoreU(localTriangle.verts[0], data->mVerts[0]); + V3StoreU(localTriangle.verts[1], data->mVerts[1]); + V3StoreU(localTriangle.verts[2], data->mVerts[2]); + } + return true; + } + } + else + { + feature1 = PxU32(getPolygonIndex(polyData, polyMap, minNormal)); + const Gu::HullPolygonData* referencePolygon = &polyData.mPolygons[feature1]; + + const Vec3V contactNormal = V3Normalize(M33TrnspsMulV3(polyMap->shape2Vertex, V3LoadU(referencePolygon->mPlane.n))); + const Vec3V nContactNormal = V3Neg(contactNormal); + + //if the minimum sperating axis is edge case, we don't defer it because it is an activeEdge + patchNormal = nContactNormal; + generatedPolyContacts(polyData, *referencePolygon, localTriangle, triangleIndex, triFlags, polyMap, manifoldContacts, numContacts, contactDist, contactNormal, mRenderOutput); + + } + } + } + + return true; + } + + + bool Gu::PCMConvexVsMeshContactGeneration::generateTriangleFullContactManifold(Gu::TriangleV& localTriangle, const PxU32 triangleIndex, const PxU8 triFlags, const Gu::PolygonalData& polyData, Gu::SupportLocalImpl* localTriMap, Gu::SupportLocal* polyMap, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, + const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal, Cm::RenderOutput* renderOutput) + { + + using namespace Ps::aos; + + const FloatV threshold = FLoad(0.7071f);//about 45 degree + PX_UNUSED(threshold); + { + + FeatureStatus status = POLYDATA0; + FloatV minOverlap = FMax(); + //minNormal will be in the local space of polyData + Vec3V minNormal = V3Zero(); + + PxU32 feature0; + if(!testTriangleFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature0, minNormal, POLYDATA0, status)) + return false; + + PxU32 feature1; + if(!testPolyFaceNormal(localTriangle, polyData, localTriMap, polyMap, contactDist, minOverlap, feature1, minNormal, POLYDATA1, status)) + return false; + + if(!testPolyEdgeNormal(localTriangle, triFlags, polyData, localTriMap, polyMap, contactDist, minOverlap, minNormal, EDGE, status)) + return false; + + const Vec3V triNormal = localTriangle.normal(); + patchNormal = triNormal; + + const Gu::HullPolygonData* referencePolygon = &polyData.mPolygons[getPolygonIndex(polyData, polyMap, triNormal)]; + generatedTriangleContacts(localTriangle, triangleIndex, triFlags, polyData, *referencePolygon, polyMap, manifoldContacts, numContacts, contactDist, triNormal, renderOutput); + + } + + return true; + } + + bool Gu::PCMConvexVsMeshContactGeneration::generatePolyDataContactManifold(Gu::TriangleV& localTriangle, const PxU32 featureIndex, const PxU32 triangleIndex, const PxU8 triFlags, Gu::MeshPersistentContact* manifoldContacts, PxU32& numContacts, const Ps::aos::FloatVArg contactDist, Ps::aos::Vec3V& patchNormal) + { + + using namespace Ps::aos; + + const Gu::HullPolygonData* referencePolygon = &mPolyData.mPolygons[featureIndex]; + + const Vec3V contactNormal = V3Normalize(M33TrnspsMulV3(mPolyMap->shape2Vertex, V3LoadU(referencePolygon->mPlane.n))); + const Vec3V nContactNormal = V3Neg(contactNormal); + + patchNormal = nContactNormal; + generatedPolyContacts(mPolyData, *referencePolygon, localTriangle, triangleIndex, triFlags, mPolyMap, manifoldContacts, numContacts, contactDist, contactNormal, mRenderOutput); + + return true; + } + + +}//physx diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h new file mode 100644 index 000000000..ece7b1050 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_PCM_TRIANGLE_CONTACT_GEN_H +#define GU_PCM_TRIANGLE_CONTACT_GEN_H + +#include "GuPCMContactGenUtil.h" +#include "GuPersistentContactManifold.h" + +namespace physx +{ + struct PxTriangleMeshGeometryLL; + class PxHeightFieldGeometry; + +namespace Gu +{ + + bool PCMContactConvexMesh(const Gu::PolygonalData& polyData0, + Gu::SupportLocal* polyMap, + const Ps::aos::FloatVArg minMargin, + const PxBounds3& hullAABB, + const PxTriangleMeshGeometryLL& shapeMesh, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, Gu::ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, const Cm::FastVertex2ShapeScaling& meshScaling, + bool idtConvexScale, bool idtMeshScale, Gu::MultiplePersistentContactManifold& multiManifold, + Cm::RenderOutput* renderOutput); + + bool PCMContactConvexHeightfield(const Gu::PolygonalData& polyData0, + Gu::SupportLocal* polyMap, + const Ps::aos::FloatVArg minMargin, + const PxBounds3& hullAABB, + const PxHeightFieldGeometry& shapeHeightfield, + const PxTransform& transform0, const PxTransform& transform1, + PxReal contactDistance, Gu::ContactBuffer& contactBuffer, + const Cm::FastVertex2ShapeScaling& convexScaling, bool idtConvexScale, Gu::MultiplePersistentContactManifold& multiManifold, + Cm::RenderOutput* renderOutput); + + +} +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp new file mode 100644 index 000000000..1360be1c7 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp @@ -0,0 +1,2432 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "GuPersistentContactManifold.h" +#include "GuContactBuffer.h" +#include "PsAllocator.h" +#include "PsVecTransform.h" +#include "PsUtilities.h" +#include "GuGJKUtil.h" + +using namespace physx; + +namespace physx +{ +namespace Gu +{ + +/* + This local function is to avoid DLL call +*/ +static Ps::aos::FloatV distancePointSegmentSquaredLocal(const Ps::aos::Vec3VArg a, const Ps::aos::Vec3VArg b, const Ps::aos::Vec3VArg p) +{ + using namespace Ps::aos; + const FloatV zero = FZero(); + const FloatV one = FOne(); + + const Vec3V ap = V3Sub(p, a); + const Vec3V ab = V3Sub(b, a); + const FloatV nom = V3Dot(ap, ab); + + const FloatV denom = V3Dot(ab, ab); + const FloatV tValue = FClamp(FDiv(nom, denom), zero, one); + + const FloatV t = FSel(FIsEq(denom, zero), zero, tValue); + const Vec3V v = V3NegScaleSub(ab, t, ap); + return V3Dot(v, v); +} + +/* + This local function is to avoid DLL call +*/ +static Ps::aos::FloatV distancePointTriangleSquaredLocal( const Ps::aos::Vec3VArg p, + const Ps::aos::Vec3VArg a, + const Ps::aos::Vec3VArg b, + const Ps::aos::Vec3VArg c) +{ + using namespace Ps::aos; + + const FloatV zero = FZero(); + //const Vec3V zero = V3Zero(); + const Vec3V ab = V3Sub(b, a); + const Vec3V ac = V3Sub(c, a); + const Vec3V bc = V3Sub(c, b); + const Vec3V ap = V3Sub(p, a); + const Vec3V bp = V3Sub(p, b); + const Vec3V cp = V3Sub(p, c); + + const FloatV d1 = V3Dot(ab, ap); // snom + const FloatV d2 = V3Dot(ac, ap); // tnom + const FloatV d3 = V3Dot(ab, bp); // -sdenom + const FloatV d4 = V3Dot(ac, bp); // unom = d4 - d3 + const FloatV d5 = V3Dot(ab, cp); // udenom = d5 - d6 + const FloatV d6 = V3Dot(ac, cp); // -tdenom + const FloatV unom = FSub(d4, d3); + const FloatV udenom = FSub(d5, d6); + + //check if p in vertex region outside a + const BoolV con00 = FIsGrtr(zero, d1); // snom <= 0 + const BoolV con01 = FIsGrtr(zero, d2); // tnom <= 0 + const BoolV con0 = BAnd(con00, con01); // vertex region a + + if(BAllEqTTTT(con0)) + { + const Vec3V vv = V3Sub(p, a); + return V3Dot(vv, vv); + } + + //check if p in vertex region outside b + const BoolV con10 = FIsGrtrOrEq(d3, zero); + const BoolV con11 = FIsGrtrOrEq(d3, d4); + const BoolV con1 = BAnd(con10, con11); // vertex region b + if(BAllEqTTTT(con1)) + { + const Vec3V vv = V3Sub(p, b); + return V3Dot(vv, vv); + } + + //check if p in vertex region outside c + const BoolV con20 = FIsGrtrOrEq(d6, zero); + const BoolV con21 = FIsGrtrOrEq(d6, d5); + const BoolV con2 = BAnd(con20, con21); // vertex region c + if(BAllEqTTTT(con2)) + { + const Vec3V vv = V3Sub(p, c); + return V3Dot(vv, vv); + } + + //check if p in edge region of AB + const FloatV vc = FSub(FMul(d1, d4), FMul(d3, d2)); + + const BoolV con30 = FIsGrtr(zero, vc); + const BoolV con31 = FIsGrtrOrEq(d1, zero); + const BoolV con32 = FIsGrtr(zero, d3); + const BoolV con3 = BAnd(con30, BAnd(con31, con32)); + if(BAllEqTTTT(con3)) + { + const FloatV sScale = FDiv(d1, FSub(d1, d3)); + const Vec3V closest3 = V3ScaleAdd(ab, sScale, a);//V3Add(a, V3Scale(ab, sScale)); + const Vec3V vv = V3Sub(p, closest3); + return V3Dot(vv, vv); + } + + //check if p in edge region of BC + const FloatV va = FSub(FMul(d3, d6),FMul(d5, d4)); + const BoolV con40 = FIsGrtr(zero, va); + const BoolV con41 = FIsGrtrOrEq(d4, d3); + const BoolV con42 = FIsGrtrOrEq(d5, d6); + const BoolV con4 = BAnd(con40, BAnd(con41, con42)); + if(BAllEqTTTT(con4)) + { + const FloatV uScale = FDiv(unom, FAdd(unom, udenom)); + const Vec3V closest4 = V3ScaleAdd(bc, uScale, b);//V3Add(b, V3Scale(bc, uScale)); + const Vec3V vv = V3Sub(p, closest4); + return V3Dot(vv, vv); + } + + //check if p in edge region of AC + const FloatV vb = FSub(FMul(d5, d2), FMul(d1, d6)); + const BoolV con50 = FIsGrtr(zero, vb); + const BoolV con51 = FIsGrtrOrEq(d2, zero); + const BoolV con52 = FIsGrtr(zero, d6); + const BoolV con5 = BAnd(con50, BAnd(con51, con52)); + if(BAllEqTTTT(con5)) + { + const FloatV tScale = FDiv(d2, FSub(d2, d6)); + const Vec3V closest5 = V3ScaleAdd(ac, tScale, a);//V3Add(a, V3Scale(ac, tScale)); + const Vec3V vv = V3Sub(p, closest5); + return V3Dot(vv, vv); + } + + //P must project inside face region. Compute Q using Barycentric coordinates + const Vec3V n = V3Cross(ab, ac); + const FloatV nn = V3Dot(n,n); + const FloatV t = FSel(FIsGrtr(nn, zero),FDiv(V3Dot(n, V3Sub(a, p)), nn), zero); + const Vec3V closest6 = V3Add(p, V3Scale(n, t)); + + const Vec3V vv = V3Sub(p, closest6); + + return V3Dot(vv, vv); +} + + + + +//This is the translational threshold used in invalidate_BoxConvexHull. 0.5 is 50% of the object margin. we use different threshold between +//0 and 4 points. This threashold is a scale that is multiplied by the objects' margins. +const PxF32 invalidateThresholds[5] = { 0.5f, 0.125f, 0.25f, 0.375f, 0.375f }; + +//This is the translational threshold used in invalidate_SphereCapsule. 0.5 is 50% of the object margin, we use different threshold between +//0 and 2 points. This threshold is a scale that is multiplied by the objects' margin + +const PxF32 invalidateThresholds2[3] = { 0.5f, 0.1f, 0.75f }; + +//This is the rotational threshold used in invalidate_BoxConvexHull. 0.9998 is a threshold for quat difference +//between previous and current frame +const PxF32 invalidateQuatThresholds[5] = { 0.9998f, 0.9999f, 0.9999f, 0.9999f, 0.9999f }; + + +//This is the rotational threshold used in invalidate_SphereCapsule. 0.9995f is a threshold for quat difference +//between previous and current frame +const PxF32 invalidateQuatThresholds2[3] = { 0.9995f, 0.9999f, 0.9997f }; + +} +} + + +#if VISUALIZE_PERSISTENT_CONTACT +#include "CmRenderOutput.h" + +static void drawManifoldPoint(const Gu::PersistentContact& manifold, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, Cm::RenderOutput& out, PxU32 color=0xffffff) +{ + PX_UNUSED(color); + + using namespace Ps::aos; + const Vec3V worldA = trA.transform(manifold.mLocalPointA); + const Vec3V worldB = trB.transform(manifold.mLocalPointB); + const Vec3V localNormal = Vec3V_From_Vec4V(manifold.mLocalNormalPen); + const FloatV pen = V4GetW(manifold.mLocalNormalPen); + + const Vec3V worldNormal = trB.rotate(localNormal); + PxVec3 a, b, v; + V3StoreU(worldA, a); + V3StoreU(worldB, b); + V3StoreU(worldNormal, v); + PxF32 dist; + FStore(pen, &dist); + PxVec3 e = a - v*dist; + + PxF32 size = 0.05f; + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxF32 size2 = 0.1f; + const PxVec3 up2(0.f, size2, 0.f); + const PxVec3 right2(size2, 0.f, 0.f); + const PxVec3 forwards2(0.f, 0.f, size2); + + PxMat44 m = PxMat44(PxIdentity); + + out << 0xffff00ff << m << Cm::RenderOutput::LINES << a << e; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + up << a - up; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + right << a - right; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + up2 << b - up2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + right2 << b - right2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + forwards2 << b - forwards2; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << a << b; + + PxVec3 c = a - v*10.f; + out << 0xffff00ff << m << Cm::RenderOutput::LINES << a << c; + +} + +static void drawManifoldPoint(const Gu::PersistentContact& manifold, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius, Cm::RenderOutput& out, PxU32 color=0xffffff) +{ + PX_UNUSED(color); + + using namespace Ps::aos; + + const Vec3V localNormal = Vec3V_From_Vec4V(manifold.mLocalNormalPen); + const Vec3V worldNormal = trB.rotate(localNormal); + const Vec3V worldA = V3NegScaleSub(worldNormal, radius, trA.transform(manifold.mLocalPointA)); + const Vec3V worldB = trB.transform(manifold.mLocalPointB); + const FloatV pen = FSub(V4GetW(manifold.mLocalNormalPen), radius); + + PxVec3 a, b, v; + V3StoreU(worldA, a); + V3StoreU(worldB, b); + V3StoreU(worldNormal, v); + PxF32 dist; + FStore(pen, &dist); + PxVec3 e = a - v*dist; + + PxF32 size = 0.05f; + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxF32 size2 = 0.1f; + const PxVec3 up2(0.f, size2, 0.f); + const PxVec3 right2(size2, 0.f, 0.f); + const PxVec3 forwards2(0.f, 0.f, size2); + + PxMat44 m = PxMat44(PxIdentity); + + out << 0xffff00ff << m << Cm::RenderOutput::LINES << a << e; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + up << a - up; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + right << a - right; + out << 0xff00ffff << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + up2 << b - up2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + right2 << b - right2; + out << 0xffff0000 << m << Cm::RenderOutput::LINES << b + forwards2 << b - forwards2; + + out << 0xffff0000 << m << Cm::RenderOutput::LINES << a << b; + +} + + +static PxU32 gColors[8] = { 0xff0000ff, 0xff00ff00, 0xffff0000, + 0xff00ffff, 0xffff00ff, 0xffffff00, + 0xff000080, 0xff008000}; + +#endif + +/* + SIMD version +*/ +Ps::aos::Mat33V Gu::findRotationMatrixFromZAxis(const Ps::aos::Vec3VArg to) +{ + using namespace Ps::aos; + + const FloatV one = FOne(); + const FloatV threshold = FLoad(0.9999f); + + const FloatV e = V3GetZ(to); + const FloatV f = FAbs(e); + + if(FAllGrtr(threshold, f)) + { + const FloatV vx = FNeg(V3GetY(to)); + const FloatV vy = V3GetX(to); + const FloatV h = FRecip(FAdd(one, e)); + const FloatV hvx = FMul(h,vx); + const FloatV hvxy = FMul(hvx, vy); + + const Vec3V col0 = V3Merge(FScaleAdd(hvx, vx, e), hvxy, vy); + const Vec3V col1 = V3Merge(hvxy, FScaleAdd(h, FMul(vy, vy), e), FNeg(vx)); + const Vec3V col2 = V3Merge(FNeg(vy), vx, e); + + return Mat33V(col0, col1, col2); + + } + else + { + + const FloatV two = FLoad(2.f); + const Vec3V from = V3UnitZ(); + const Vec3V absFrom = V3UnitY(); + + const Vec3V u = V3Sub(absFrom, from); + const Vec3V v = V3Sub(absFrom, to); + + const FloatV dotU = V3Dot(u, u); + const FloatV dotV = V3Dot(v, v); + const FloatV dotUV = V3Dot(u, v); + + const FloatV c1 = FNeg(FDiv(two, dotU)); + const FloatV c2 = FNeg(FDiv(two, dotV)); + const FloatV c3 = FMul(c1, FMul(c2, dotUV)); + + const Vec3V c1u = V3Scale(u, c1); + const Vec3V c2v = V3Scale(v, c2); + const Vec3V c3v = V3Scale(v, c3); + + + FloatV temp0 = V3GetX(c1u); + FloatV temp1 = V3GetX(c2v); + FloatV temp2 = V3GetX(c3v); + + Vec3V col0 = V3ScaleAdd(u, temp0, V3ScaleAdd(v, temp1, V3Scale(u, temp2))); + col0 = V3SetX(col0, FAdd(V3GetX(col0), one)); + + temp0 = V3GetY(c1u); + temp1 = V3GetY(c2v); + temp2 = V3GetY(c3v); + + Vec3V col1 = V3ScaleAdd(u, temp0, V3ScaleAdd(v, temp1, V3Scale(u, temp2))); + col1 = V3SetY(col1, FAdd(V3GetY(col1), one)); + + temp0 = V3GetZ(c1u); + temp1 = V3GetZ(c2v); + temp2 = V3GetZ(c3v); + + Vec3V col2 = V3ScaleAdd(u, temp0, V3ScaleAdd(v, temp1, V3Scale(u, temp2))); + col2 = V3SetZ(col2, FAdd(V3GetZ(col2), one)); + + return Mat33V(col0, col1, col2); + + } +} + + +void Gu::PersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxVec3 a, b; + V3StoreU(trA.p, a); + V3StoreU(trB.p, b); + + for(PxU32 i = 0; i< mNumContacts; ++i) + { + Gu::PersistentContact& m = mContactPoints[i]; + drawManifoldPoint(m, trA, trB, out, gColors[i]); + } +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); +#endif +} + +void Gu::PersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxVec3 a, b; + V3StoreU(trA.p, a); + V3StoreU(trB.p, b); + + for(PxU32 i = 0; i< mNumContacts; ++i) + { + Gu::PersistentContact& m = mContactPoints[i]; + drawManifoldPoint(m, trA, trB, radius, out, gColors[i]); + } +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); + PX_UNUSED(radius); +#endif +} + +void Gu::PersistentContactManifold::drawManifold(const Gu::PersistentContact& m, Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ +#if VISUALIZE_PERSISTENT_CONTACT + drawManifoldPoint(m, trA, trB, out, gColors[0]); +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); + PX_UNUSED(m); +#endif +} + +void Gu::PersistentContactManifold::drawPoint(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p, const PxF32 size, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxVec3 a; + V3StoreU(p, a); + + PxMat44 m = PxMat44(PxIdentity); + + out << color << m << Cm::RenderOutput::LINES << a + up << a - up; + out << color << m << Cm::RenderOutput::LINES << a + right << a - right; + out << color << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; +#else + PX_UNUSED(out); + PX_UNUSED(p); + PX_UNUSED(size); + PX_UNUSED(color); +#endif +} + +void Gu::PersistentContactManifold::drawLine(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + PxVec3 a, b; + V3StoreU(p0, a); + V3StoreU(p1, b); + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::LINES << a << b; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(color); +#endif +} + +void Gu::PersistentContactManifold::drawTriangle(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + PxVec3 a, b, c; + V3StoreU(p0, a); + V3StoreU(p1, b); + V3StoreU(p2, c); + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::TRIANGLES << a << b << c; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(p2); + PX_UNUSED(color); +#endif +} + +void Gu::PersistentContactManifold::drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + for(PxU32 i=0; i(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast(&contact.point.x)); + FStore(dist, &contact.separation); + + PX_ASSERT(contact.point.isFinite()); + PX_ASSERT(contact.normal.isFinite()); + PX_ASSERT(PxIsFinite(contact.separation)); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + + } + + contactBuffer.count = contactCount; +} + +/* + This function is for direct implementation for box vs box. We don't need to discard the contacts based on whether the contact offset is larger than penetration/dist because + the direct implementation will guarantee the penetration/dist won't be larger than the contact offset. Also, we won't have points from the cache if the direct implementation + of box vs box is called. +*/ +void Gu::PersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsMatTransformV& transf1) +{ + using namespace Ps::aos; + + //add the manifold contacts; + PxU32 contactCount = 0;//contactBuffer.count; + for(PxU32 i=0; (i< mNumContacts) & (contactCount < Gu::ContactBuffer::MAX_CONTACTS); ++i) + { + + PersistentContact& p = getContactPoint(i); + + const Vec3V worldP =transf1.transform(p.mLocalPointB); + const FloatV dist = V4GetW(p.mLocalNormalPen); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast(&contact.point.x)); + FStore(dist, &contact.separation); + PX_ASSERT(PxIsFinite(contact.point.x)); + PX_ASSERT(PxIsFinite(contact.point.y)); + PX_ASSERT(PxIsFinite(contact.point.z)); + + //PX_ASSERT(contact.separation > -0.2f); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + + contactBuffer.count = contactCount; +} + +/* + This function is for sphere/capsule vs other primitives. We treat sphere as a point and capsule as a segment in the contact gen and store the sphere center or a point in the segment for capsule + in the manifold. +*/ +void Gu::PersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::Vec3VArg projectionNormal, + const Ps::aos::PsTransformV& transf0, const Ps::aos::FloatVArg radius, const Ps::aos::FloatVArg contactOffset) +{ + using namespace Ps::aos; + + //add the manifold contacts; + PxU32 contactCount = 0;//contactBuffer.count; + for(PxU32 i=0; (i< mNumContacts) & (contactCount < Gu::ContactBuffer::MAX_CONTACTS); ++i) + { + + PersistentContact& p = getContactPoint(i); + const FloatV dist = FSub(V4GetW(p.mLocalNormalPen), radius); + + //The newly created points should have a dist < contactOffset. However, points might come from the PCM contact cache so we might still have points which are + //larger than contactOffset. The reason why we don't want to discard the contacts in the contact cache whose dist > contactOffset is because the contacts may + //only temporarily become separated. The points still project onto roughly the same spot but so, if the bodies move together again, the contacts may be valid again. + //This is important when simulating at large time-steps because GJK can only generate 1 point of contact and missing contacts for just a single frame with large time-steps + //may introduce noticeable instability. + if(FAllGrtrOrEq(contactOffset, dist)) + { + const Vec3V worldP =V3NegScaleSub(projectionNormal, radius, transf0.transform(p.mLocalPointA)); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast(&contact.point.x)); + FStore(dist, &contact.separation); + //PX_ASSERT(PxAbs(contact.separation) < 2.f); + + contact.internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + } + + contactBuffer.count = contactCount; +} + + +/* + This function is used in the box/convexhull full manifold contact genenation. We will pass in a list of manifold contacts. If the number of contacts are more than + GU_MANIFOLD_CACHE_SIZE, we will need to do contact reduction while we are storing the chosen manifold contacts from the manifold contact list to the manifold contact + buffer. +*/ +void Gu::PersistentContactManifold::addBatchManifoldContacts(const PersistentContact* manifoldContacts, + const PxU32 numPoints, const PxReal toleranceLength) +{ + + if(numPoints <= GU_MANIFOLD_CACHE_SIZE) + { + for(PxU32 i=0; i 4, we will reduce the contact points to 4 +*/ +void Gu::PersistentContactManifold::reduceBatchContacts(const PersistentContact* manifoldPoints, + const PxU32 numPoints, const PxReal tolereanceLength) +{ + using namespace Ps::aos; + + PxU8 chosenIndices[4]; + PxU8 candidates[64]; + + const FloatV zero = FZero(); + const FloatV max = FMax(); + const FloatV nmax = FNeg(max); + FloatV maxPen = V4GetW(manifoldPoints[0].mLocalNormalPen); + FloatV minPen = nmax; + PxU32 index = 0; + candidates[0] = 0; + PxU32 candidateIndex = 0; + + PxU32 nbCandiates = numPoints; + //keep the deepest point, candidateIndex will be the same as index + for (PxU32 i = 1; i 2, we will reduce the contact points to 2 +*/ +void Gu::PersistentContactManifold::reduceBatchContacts2(const PersistentContact* manifoldPoints, const PxU32 numPoints) +{ + using namespace Ps::aos; + + PX_ASSERT(numPoints < 64); + bool chosen[64]; + physx::PxMemZero(chosen, sizeof(bool)*numPoints); + FloatV maxDis = V4GetW(manifoldPoints[0].mLocalNormalPen); + PxI32 index = 0; + //keep the deepest point + for(PxU32 i=1; imStartIndex; jmEndIndex; ++j) + { + mContactPoints[tempNumContacts++] = manifoldContact[j]; + } + currentPatch = currentPatch->mNextPatch; + } + mNumContacts = tempNumContacts; + return patch.mPatchMaxPen; + } + else + { + //contact reduction + const FloatV maxPen = reduceBatchContactsConvex(manifoldContact, numContactExt, patch); + mNumContacts = GU_SINGLE_MANIFOLD_CACHE_SIZE; + return maxPen; + } +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::addBatchManifoldContactsSphere(const MeshPersistentContact* manifoldContact, const PxU32 numContactExt, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold) +{ + PX_UNUSED(replaceBreakingThreshold); + + using namespace Ps::aos; + + const FloatV maxPen = reduceBatchContactsSphere(manifoldContact, numContactExt, patch); + mNumContacts = 1; + return maxPen; +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::addBatchManifoldContactsCapsule(const MeshPersistentContact* manifoldContact, const PxU32 numContactExt, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold) +{ + PX_UNUSED(replaceBreakingThreshold); + + using namespace Ps::aos; + + if(patch.mTotalSize <=GU_CAPSULE_MANIFOLD_CACHE_SIZE) + { + PCMContactPatch* currentPatch = &patch; + + //this is because we already add the manifold contacts into manifoldContact array + PxU32 tempNumContacts = 0; + while(currentPatch) + { + for(PxU32 j=currentPatch->mStartIndex; jmEndIndex; ++j) + { + mContactPoints[tempNumContacts++] = manifoldContact[j]; + } + currentPatch = currentPatch->mNextPatch; + } + mNumContacts = tempNumContacts; + return patch.mPatchMaxPen; + } + else + { + + const FloatV maxPen = reduceBatchContactsCapsule(manifoldContact, numContactExt, patch); + mNumContacts = GU_CAPSULE_MANIFOLD_CACHE_SIZE; + return maxPen; + } + +} + + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::reduceBatchContactsSphere(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch) +{ + PX_UNUSED(numContacts); + + using namespace Ps::aos; + FloatV max = FMax(); + FloatV maxDist = max; + PxI32 index = -1; + + + PCMContactPatch* currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + if(FAllGrtr(maxDist, pen)) + { + maxDist = pen; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + PX_ASSERT(index!=-1); + mContactPoints[0] = manifoldContactExt[index]; + + return maxDist; + +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::reduceBatchContactsCapsule(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch) +{ + using namespace Ps::aos; + + bool* chosen = reinterpret_cast(PxAlloca(sizeof(bool)*numContacts)); + physx::PxMemZero(chosen, sizeof(bool)*numContacts); + const FloatV max = FMax(); + FloatV maxDis = max; + PxI32 index = -1; + + FloatV maxPen = max; + + + PCMContactPatch* currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + if(FAllGrtr(maxDis, pen)) + { + maxDis = pen; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + chosen[index] = true; + mContactPoints[0] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + + //calculate the furthest away points + Vec3V v = V3Sub(manifoldContactExt[patch.mStartIndex].mLocalPointB, mContactPoints[0].mLocalPointB); + maxDis = V3Dot(v, v); + index = PxI32(patch.mStartIndex); + + currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, mContactPoints[0].mLocalPointB); + const FloatV d = V3Dot(v, v); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = PxI32(i); + } + } + currentPatch = currentPatch->mNextPatch; + } + + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + mContactPoints[1] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + //keep the second deepest point + maxDis = max; + currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + if(!chosen[i]) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + //const FloatV v = V3Dot(manifoldContactExt[i].mLocalPointB, manifoldContactExt[i].mLocalPointB); + if(FAllGrtr(maxDis, pen)) + { + maxDis = pen; + index = PxI32(i); + } + } + } + currentPatch = currentPatch->mNextPatch; + } + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + mContactPoints[2] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + return maxPen; +} + +Ps::aos::FloatV Gu::SinglePersistentContactManifold::reduceBatchContactsConvex(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch) +{ + using namespace Ps::aos; + + bool* chosen = reinterpret_cast(PxAlloca(sizeof(bool)*numContacts)); + physx::PxMemZero(chosen, sizeof(bool)*numContacts); + const FloatV max = FMax(); + const FloatV nmax = FNeg(max); + FloatV maxDis = nmax; + PxU32 index = GU_MANIFOLD_INVALID_INDEX; + + PCMContactPatch* currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + //const FloatV pen = V4GetW(manifoldContactExt[i].localNormalPen); + const FloatV v = V3Dot(manifoldContactExt[i].mLocalPointB, manifoldContactExt[i].mLocalPointB); + if(FAllGrtr(v, maxDis)) + { + maxDis = v; + index = i; + } + } + currentPatch = currentPatch->mNextPatch; + } + + chosen[index] = true; + + const Vec3V contact0 = manifoldContactExt[index].mLocalPointB; + mContactPoints[0] = manifoldContactExt[index]; + + FloatV maxPen = V4GetW(manifoldContactExt[index].mLocalNormalPen); + + //calculate the furthest away points + Vec3V v = V3Sub(manifoldContactExt[patch.mStartIndex].mLocalPointB, contact0); + maxDis = V3Dot(v, v); + index = patch.mStartIndex; + + currentPatch = &patch; + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, contact0); + const FloatV d = V3Dot(v, v); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = i; + } + } + currentPatch = currentPatch->mNextPatch; + } + + //PX_ASSERT(chosen[index] == false); + chosen[index] = true; + const Vec3V contact1 = manifoldContactExt[index].mLocalPointB; + mContactPoints[1] = manifoldContactExt[index]; + + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + maxDis = nmax; + index = GU_MANIFOLD_INVALID_INDEX; + v = V3Sub(contact1, contact0); + const Vec3V cn0 = Vec3V_From_Vec4V(mContactPoints[0].mLocalNormalPen); + Vec3V norm = V3Cross(v, cn0); + const FloatV sqLen = V3Dot(norm, norm); + norm = V3Sel(FIsGrtr(sqLen, FZero()), V3ScaleInv(norm, FSqrt(sqLen)), cn0); + FloatV minDis = max; + PxU32 index1 = GU_MANIFOLD_INVALID_INDEX; + + + //calculate the point furthest way to the segment + currentPatch = &patch; + + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, contact0); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index = i; + } + + if(FAllGrtr(minDis, d)) + { + minDis = d; + index1 = i; + } + } + } + currentPatch = currentPatch->mNextPatch; + } + + //PX_ASSERT(chosen[index] == false); + + chosen[index] = true; + mContactPoints[2] = manifoldContactExt[index]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index].mLocalNormalPen)); + + //if min and max in the same side, choose again + const FloatV temp = FMul(minDis, maxDis); + if(FAllGrtr(temp, FZero())) + { + //choose again + maxDis = nmax; + //calculate the point furthest way to the segment + currentPatch = &patch; + + while(currentPatch) + { + for(PxU32 i=currentPatch->mStartIndex; imEndIndex; ++i) + { + if(!chosen[i]) + { + v = V3Sub(manifoldContactExt[i].mLocalPointB, contact0); + const FloatV d = V3Dot(v, norm); + if(FAllGrtr(d, maxDis)) + { + maxDis = d; + index1 = i; + } + } + } + currentPatch = currentPatch->mNextPatch; + } + } + + //PX_ASSERT(chosen[index1] == false); + + chosen[index1] = true; + mContactPoints[3] = manifoldContactExt[index1]; + maxPen = FMin(maxPen, V4GetW(manifoldContactExt[index1].mLocalNormalPen)); + + const PxU32 NB_TO_ADD = GU_SINGLE_MANIFOLD_CACHE_SIZE - 4; + + FloatV pens[NB_TO_ADD]; + PxU32 inds[NB_TO_ADD]; + for (PxU32 a = 0; a < NB_TO_ADD; ++a) + { + pens[a] = FMax(); + inds[a] = 0; + } + { + currentPatch = &patch; + + while (currentPatch) + { + for (PxU32 i = currentPatch->mStartIndex; i < currentPatch->mEndIndex; ++i) + { + if (!chosen[i]) + { + const FloatV pen = V4GetW(manifoldContactExt[i].mLocalNormalPen); + for (PxU32 a = 0; a < NB_TO_ADD; ++a) + { + if (FAllGrtr(pens[a], pen)) + { + for (PxU32 b = a + 1; b < NB_TO_ADD; ++b) + { + pens[b] = pens[b - 1]; + inds[b] = inds[b - 1]; + } + pens[a] = pen; + inds[a] = i; + break; + } + } + } + } + currentPatch = currentPatch->mNextPatch; + } + for (PxU32 i = 0; i < NB_TO_ADD; ++i) + { + mContactPoints[i + 4] = manifoldContactExt[inds[i]]; + maxPen = FMin(maxPen, pens[i]); + } + } + return maxPen; +} + +PxU32 Gu::SinglePersistentContactManifold::reduceContacts(MeshPersistentContact* manifoldPoints, PxU32 numPoints) +{ + using namespace Ps::aos; + + PxU8 chosenIndices[GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE]; + PxU8* candidates = reinterpret_cast(PxAlloca(sizeof(PxU8) * numPoints)); + + const FloatV max = FMax(); + const FloatV nmax = FNeg(max); + const FloatV zero = FZero(); + FloatV maxPen = V4GetW(manifoldPoints[0].mLocalNormalPen); + PxU32 index = 0; + candidates[0] = 0; + PxU32 candidateIndex = 0; + PxU32 nbCandiates = numPoints; + + MeshPersistentContact newManifold[GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE]; + + //keep the deepest point + for (PxU32 i = 1; i 0; --i) + { + MeshPersistentContact& manifoldPoint = mContactPoints[i-1]; + const Vec3V localAInB = aToB.transform( manifoldPoint.mLocalPointA ); // from a to b + const Vec3V localBInB = manifoldPoint.mLocalPointB; + const Vec3V v = V3Sub(localAInB, localBInB); + + const Vec3V localNormal = Vec3V_From_Vec4V(manifoldPoint.mLocalNormalPen); // normal in b space + const FloatV dist= V3Dot(v, localNormal); + + const Vec3V projectedPoint = V3NegScaleSub(localNormal, dist, localAInB);//manifoldPoint.worldPointA - manifoldPoint.worldPointB * manifoldPoint.m_distance1; + const Vec3V projectedDifference = V3Sub(localBInB, projectedPoint); + + const FloatV distance2d = V3Dot(projectedDifference, projectedDifference); + //const BoolV con = BOr(FIsGrtr(dist, contactOffset), FIsGrtr(distance2d, sqProjectBreakingThreshold)); + const BoolV con = FIsGrtr(distance2d, sqProjectBreakingThreshold); + if(BAllEqTTTT(con)) + { + removeContactPoint(i-1); + } + else + { + manifoldPoint.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), dist); + maxPen = FMin(maxPen, dist); + } + } + + return maxPen; +} + + +void Gu::SinglePersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxVec3 a, b; + V3StoreU(trA.p, a); + V3StoreU(trB.p, b); + + for(PxU32 i = 0; i< mNumContacts; ++i) + { + Gu::MeshPersistentContact& m = mContactPoints[i]; + drawManifoldPoint(m, trA, trB, out, gColors[i]); + } +#else + PX_UNUSED(out); + PX_UNUSED(trA); + PX_UNUSED(trB); +#endif +} + +void Gu::MultiplePersistentContactManifold::drawManifold( Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB) +{ + for(PxU32 i=0; idrawManifold(out, trA, trB); + } +} + +void Gu::MultiplePersistentContactManifold::drawLine(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + PxVec3 a, b; + V3StoreU(p0, a); + V3StoreU(p1, b); + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::LINES << a << b; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(color); + +#endif +} + +void Gu::MultiplePersistentContactManifold::drawLine(Cm::RenderOutput& out, const PxVec3 p0, const PxVec3 p1, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + + PxMat44 m = PxMat44(PxIdentity); + out << color << m << Cm::RenderOutput::LINES << p0 << p1; +#else + PX_UNUSED(out); + PX_UNUSED(p0); + PX_UNUSED(p1); + PX_UNUSED(color); +#endif +} + +void Gu::MultiplePersistentContactManifold::drawPoint(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p, const PxF32 size, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + const PxVec3 up(0.f, size, 0.f); + const PxVec3 right(size, 0.f, 0.f); + const PxVec3 forwards(0.f, 0.f, size); + + PxVec3 a; + V3StoreU(p, a); + + PxMat44 m = PxMat44(PxIdentity); + + out << color << m << Cm::RenderOutput::LINES << a + up << a - up; + out << color << m << Cm::RenderOutput::LINES << a + right << a - right; + out << color << m << Cm::RenderOutput::LINES << a + forwards << a - forwards; +#else + PX_UNUSED(out); + PX_UNUSED(p); + PX_UNUSED(size); + PX_UNUSED(color); +#endif +} + + +void Gu::MultiplePersistentContactManifold::drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color) +{ + using namespace Ps::aos; +#if VISUALIZE_PERSISTENT_CONTACT + for(PxU32 i=0; iaddBatchManifoldContactsSphere(manifoldContact, numManifoldContacts, *patch, sqReplaceBreakingThreshold); + case GU_CAPSULE_MANIFOLD_CACHE_SIZE://capsule, need to implement keep two deepest + return manifold->addBatchManifoldContactsCapsule(manifoldContact, numManifoldContacts, *patch, sqReplaceBreakingThreshold); + default://cache size GU_SINGLE_MANIFOLD_CACHE_SIZE + return manifold->addBatchManifoldContactsConvex(manifoldContact, numManifoldContacts, *patch, sqReplaceBreakingThreshold); + }; +} + +/* + This function adds the manifold contacts with different patches into the corresponding single persistent contact manifold +*/ +void Gu::MultiplePersistentContactManifold::addManifoldContactPoints(MeshPersistentContact* manifoldContact, PxU32 numManifoldContacts, PCMContactPatch** contactPatch, const PxU32 numPatch, const Ps::aos::FloatVArg sqReplaceBreakingThreshold, const Ps::aos::FloatVArg acceptanceEpsilon, PxU8 maxContactsPerManifold) +{ + using namespace Ps::aos; + + if(mNumManifolds == 0) + { + for(PxU32 i=0; imRoot == patch) + { + + SinglePersistentContactManifold* manifold = getEmptyManifold(); + if(manifold) + { + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(manifold, manifoldContact, numManifoldContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[mNumManifolds]]); + mNumManifolds++; + } + else + { + //We already pre-sorted the patches so we know we can return here + return; + } + + } + } + + + } + else + { + //we do processContacts() when the number of contacts are more than 16, such that, we might call processContacts() multiple times when we have very detailed mesh + //or very large contact offset/objects. In this case, the mNumManifolds will be large than 0. + PCMContactPatch tempPatch; + for(PxU32 i=0; imRoot == patch) + { + PX_ASSERT(mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + for(PxU32 j=0; jmPatchNormal, pNor); + + if(FAllGrtrOrEq(d, acceptanceEpsilon)) + { + //appending the existing contacts to the manifold contact stream + for(PxU32 k=0; k< manifold.mNumContacts; ++k) + { + PxU32 index = k + numManifoldContacts; + //not duplicate point + PX_ASSERT(index < 64); + manifoldContact[index] = manifold.mContactPoints[k]; + } + + //create a new patch for the exiting manifold + tempPatch.mStartIndex = numManifoldContacts; + tempPatch.mEndIndex = numManifoldContacts + manifold.mNumContacts; + tempPatch.mPatchNormal = pNor;//manifold.getLocalNormal(); + tempPatch.mRoot = patch; + tempPatch.mNextPatch = NULL; + + patch->mEndPatch->mNextPatch = &tempPatch; + + //numManifoldContacts += manifold.numContacts; + patch->mTotalSize += manifold.mNumContacts; + patch->mPatchMaxPen = FMin(patch->mPatchMaxPen, FLoad(mMaxPen[mManifoldIndices[j]])); + + //manifold.numContacts = 0; + + PX_ASSERT((numManifoldContacts+manifold.mNumContacts) <= 64); + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(&manifold, manifoldContact, numManifoldContacts+manifold.mNumContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[j]]); + found = true; + break; + } + } + + if(!found)// && numManifolds < 4) + { + SinglePersistentContactManifold* manifold = getEmptyManifold(); + //we still have slot to create a new manifold + if(manifold) + { + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(manifold, manifoldContact, numManifoldContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[mNumManifolds]]); + mNumManifolds++; + } + else + { + //we can't allocate a new manifold and no existing manifold has the same normal as this patch, we need to find the shallowest penetration manifold. If this manifold is shallower than + //the current patch, replace the manifold with the current patch + PxU32 index = 0; + for(PxU32 j=1; j mMaxPen[mManifoldIndices[index]]) + { + index = j; + } + } + + if(FAllGrtr(FLoad(mMaxPen[mManifoldIndices[index]]), patch->mPatchMaxPen)) + { + manifold = getManifold(index); + manifold->mNumContacts = 0; + const FloatV _maxPen = addBatchManifoldContactsToSingleManifold(manifold, manifoldContact, numManifoldContacts, patch, sqReplaceBreakingThreshold, maxContactsPerManifold); + FStore(_maxPen, &mMaxPen[mManifoldIndices[index]]); + + } + return; + } + } + } + } + } +} + +//This function adds the multi manifold contacts to the contact buffer for box/convexhull +bool Gu::MultiplePersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::PsTransformV& meshTransform) +{ + using namespace Ps::aos; + PxU32 contactCount = 0;//contactBuffer.count; + PxU32 numContacts = 0; + mNumTotalContacts = 0; + //drawManifold(*gRenderOutPut, convexTransform, meshTransform); + for(PxU32 i=0; i < mNumManifolds; ++i) + { + Gu::SinglePersistentContactManifold& manifold = *getManifold(i); + numContacts = manifold.getNumContacts(); + PX_ASSERT(mNumTotalContacts + numContacts <= 0xFF); + mNumTotalContacts += Ps::to8(numContacts); + const Vec3V normal = manifold.getWorldNormal(meshTransform); + + for(PxU32 j=0; (j< numContacts) & (contactCount < ContactBuffer::MAX_CONTACTS); ++j) + { + Gu::MeshPersistentContact& p = manifold.getContactPoint(j); + + const Vec3V worldP =meshTransform.transform(p.mLocalPointB); + const FloatV dist = V4GetW(p.mLocalNormalPen); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast(&contact.point.x)); + FStore(dist, &contact.separation); + + contact.internalFaceIndex1 = p.mFaceIndex; + + } + } + + PX_ASSERT(contactCount <= 64); + contactBuffer.count = contactCount; + return contactCount > 0; +} + +//This function adds the multi manifold contacts to the contact buffer for sphere and capsule, radius is corresponding to the sphere radius/capsule radius +bool Gu::MultiplePersistentContactManifold::addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius) +{ + using namespace Ps::aos; + PxU32 contactCount = 0; + PxU32 numContacts = 0; + mNumTotalContacts = 0; + + for(PxU32 i=0; i < mNumManifolds; ++i) + { + //get a single manifold + Gu::SinglePersistentContactManifold& manifold = *getManifold(i); + numContacts = manifold.getNumContacts(); + PX_ASSERT(mNumTotalContacts + numContacts <= 0xFF); + mNumTotalContacts += Ps::to8(numContacts); + const Vec3V normal = manifold.getWorldNormal(trB); + + //iterate all the contacts in this single manifold and add contacts to the contact buffer + //each manifold contact point store two points which are in each other's local space. In this + //case, mLocalPointA is stored as the center of sphere/a point in the segment for capsule + for(PxU32 j=0; (j< numContacts) & (contactCount < ContactBuffer::MAX_CONTACTS); ++j) + { + Gu::MeshPersistentContact& p = manifold.getContactPoint(j); + + const Vec3V worldP =V3NegScaleSub(normal, radius, trA.transform(p.mLocalPointA)); + const FloatV dist = FSub(V4GetW(p.mLocalNormalPen), radius); + + Gu::ContactPoint& contact = contactBuffer.contacts[contactCount++]; + //Fast allign store + V4StoreA(Vec4V_From_Vec3V(normal), reinterpret_cast(&contact.normal.x)); + V4StoreA(Vec4V_From_Vec3V(worldP), reinterpret_cast(&contact.point.x)); + FStore(dist, &contact.separation); + + contact.internalFaceIndex1 = p.mFaceIndex; + + } + } + + PX_ASSERT(contactCount <= 64); + contactBuffer.count = contactCount; + return contactCount > 0; +} + + +/* + This function copies the mesh persistent contact from compress buffer(NpCacheStreamPair in the PxcNpThreadContext) to the multiple manifold +*/ +// PT: function moved to cpp to go around a compiler bug on PS4 +void physx::Gu::MultiplePersistentContactManifold::fromBuffer(PxU8* PX_RESTRICT buffer) +{ + using namespace Ps::aos; + PxU32 numManifolds = 0; + if (buffer != NULL) + { + PX_ASSERT(((uintptr_t(buffer)) & 0xF) == 0); + PxU8* PX_RESTRICT buff = buffer; + MultiPersistentManifoldHeader* PX_RESTRICT header = reinterpret_cast(buff); + buff += sizeof(MultiPersistentManifoldHeader); + + numManifolds = header->mNumManifolds; + + PX_ASSERT(numManifolds <= GU_MAX_MANIFOLD_SIZE); + mRelativeTransform = header->mRelativeTransform; + + for (PxU32 a = 0; a < numManifolds; ++a) + { + mManifoldIndices[a] = PxU8(a); + SingleManifoldHeader* PX_RESTRICT manHeader = reinterpret_cast(buff); + buff += sizeof(SingleManifoldHeader); + PxU32 numContacts = manHeader->mNumContacts; + PX_ASSERT(numContacts <= GU_SINGLE_MANIFOLD_CACHE_SIZE); + SinglePersistentContactManifold& manifold = mManifolds[a]; + manifold.mNumContacts = numContacts; + PX_ASSERT((uintptr_t(buff) & 0xf) == 0); + CachedMeshPersistentContact* contacts = reinterpret_cast(buff); + for (PxU32 b = 0; b < manifold.mNumContacts; ++b) + { + manifold.mContactPoints[b].mLocalPointA = Vec3V_From_Vec4V(V4LoadA(&contacts[b].mLocalPointA.x)); + manifold.mContactPoints[b].mLocalPointB = Vec3V_From_Vec4V(V4LoadA(&contacts[b].mLocalPointB.x)); + manifold.mContactPoints[b].mLocalNormalPen = V4LoadA(&contacts[b].mLocalNormal.x); + manifold.mContactPoints[b].mFaceIndex = contacts[b].mFaceIndex; + } + buff += sizeof(Gu::CachedMeshPersistentContact) * numContacts; + } + } + else + { + mRelativeTransform.Invalidate(); + } + mNumManifolds = PxU8(numManifolds); + for (PxU32 a = numManifolds; a < GU_MAX_MANIFOLD_SIZE; ++a) + { + mManifoldIndices[a] = PxU8(a); + } +} + +void Gu::addManifoldPoint(Gu::PersistentContact* manifoldContacts, Gu::PersistentContactManifold& manifold, GjkOutput& output, + const Ps::aos::PsMatTransformV& aToB, const Ps::aos::FloatV replaceBreakingThreshold) +{ + using namespace Ps::aos; + + const Vec3V localPointA = aToB.transformInv(output.closestA); + const Vec4V localNormalPen = V4SetW(Vec4V_From_Vec3V(output.normal), output.penDep); + + //Add contact to contact stream + manifoldContacts[0].mLocalPointA = localPointA; + manifoldContacts[0].mLocalPointB = output.closestB; + manifoldContacts[0].mLocalNormalPen = localNormalPen; + + //Add contact to manifold + manifold.addManifoldPoint(localPointA, output.closestB, localNormalPen, replaceBreakingThreshold); +} diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h new file mode 100644 index 000000000..5dc8dd965 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h @@ -0,0 +1,855 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef Gu_PERSISTENTCONTACTMANIFOLD_H +#define Gu_PERSISTENTCONTACTMANIFOLD_H + +#include "PxPhysXCommonConfig.h" +#include "foundation/PxUnionCast.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "PsVecTransform.h" + +#define PCM_LOW_LEVEL_DEBUG 0 + +namespace physx +{ + +#define VISUALIZE_PERSISTENT_CONTACT 1 +//This is for pritimives vs primitives +#define GU_MANIFOLD_CACHE_SIZE 4 +//These are for pritimives vs mesh +#define GU_SINGLE_MANIFOLD_SINGLE_POLYGONE_CACHE_SIZE 5 +#define GU_SINGLE_MANIFOLD_CACHE_SIZE 6 +#define GU_SPHERE_MANIFOLD_CACHE_SIZE 1 +#define GU_CAPSULE_MANIFOLD_CACHE_SIZE 3 +#define GU_MAX_MANIFOLD_SIZE 6 +#define GU_MESH_CONTACT_REDUCTION_THRESHOLD 16 + +#define GU_MANIFOLD_INVALID_INDEX 0xffffffff + + +//ML: this is used to compared with the shape's margin to decide the final tolerance used in the manifold to validate the existing contacts. +//In the case of big shape and relatively speaking small triangles in the mesh, we need to take a smaller margin. This helps because the PCM +//recycling thresholds are proportionate to margin so it makes it less likely to discard previous contacts due to separation +#define GU_PCM_MESH_MANIFOLD_EPSILON 0.05f + + +namespace Cm +{ + class RenderOutput; +} + + +namespace Gu +{ + struct ContactPoint; + class ContactBuffer; + struct GjkOutput; + +extern const PxF32 invalidateThresholds[5]; +extern const PxF32 invalidateQuatThresholds[5]; +extern const PxF32 invalidateThresholds2[3]; +extern const PxF32 invalidateQuatThresholds2[3]; + + +Ps::aos::Mat33V findRotationMatrixFromZAxis(const Ps::aos::Vec3VArg to); + +//This contact is used in the primitives vs primitives contact gen +class PersistentContact +{ +public: + PersistentContact() + { + } + + PersistentContact(Ps::aos::Vec3V _localPointA, Ps::aos::Vec3V _localPointB, Ps::aos::Vec4V _localNormalPen) : + mLocalPointA(_localPointA), mLocalPointB(_localPointB), mLocalNormalPen(_localNormalPen) + { + + } + + Ps::aos::Vec3V mLocalPointA; + Ps::aos::Vec3V mLocalPointB; + Ps::aos::Vec4V mLocalNormalPen; // the (x, y, z) is the local normal, and the w is the penetration depth +}; + +//This contact is used in the mesh contact gen to store an extra variable +class MeshPersistentContact : public PersistentContact +{ +public: + PxU32 mFaceIndex; +}; + +//This contact is used in the compress stream buffer(NpCacheStreamPair in the PxcNpThreadContext) to store the data we need +PX_ALIGN_PREFIX(16) +class CachedMeshPersistentContact +{ +public: + PxVec3 mLocalPointA; //16 byte aligned + PxU32 mFaceIndex; //face index + PxVec3 mLocalPointB; //16 byte aligned + PxU32 mPad; //pad + PxVec3 mLocalNormal; //16 byte aligned + PxReal mPen; //penetration +}PX_ALIGN_SUFFIX(16); + + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +//This class is used to store the start and end index of the mesh persistent contacts in an array. +struct PCMContactPatch +{ +public: + PCMContactPatch() + { + mNextPatch = NULL; + mEndPatch = NULL; + mRoot = this; + mPatchMaxPen = Ps::aos::FMax(); + } + Ps::aos::Vec3V mPatchNormal; + PCMContactPatch* mNextPatch;//store the next patch pointer in the patch list + PCMContactPatch* mEndPatch;//store the last patch pointer in the patch list + PCMContactPatch* mRoot;//if this is the start of the patch list which has very similar patch normal, the root will be itself + Ps::aos::FloatV mPatchMaxPen;//store the deepest penetration of the whole patch + PxU32 mStartIndex;//store the start index of the manifold contacts in the manifold contacts stream + PxU32 mEndIndex;//store the end index of the manifold contacts in the manifold contacts stream + PxU32 mTotalSize;//if this is the root, the total size will store the total number of manifold contacts in the whole patch list +}; + +#if PX_VC + #pragma warning(pop) +#endif + +//ML: this is needed because it seems NEON doesn't force the alignment on SIMD type, which cause some of the PCM unit tests fail +PX_ALIGN_PREFIX(16) +class PersistentContactManifold +{ +public: + + PersistentContactManifold(PersistentContact* contactPointsBuff, PxU8 capacity): mNumContacts(0), mCapacity(capacity), mNumWarmStartPoints(0), mContactPoints(contactPointsBuff) + { + mRelativeTransform.Invalidate(); + } + + PX_FORCE_INLINE PxU32 getNumContacts() const { return mNumContacts;} + + PX_FORCE_INLINE bool isEmpty() { return mNumContacts==0; } + + PX_FORCE_INLINE PersistentContact& getContactPoint(const PxU32 index) + { + PX_ASSERT(index < GU_MANIFOLD_CACHE_SIZE); + return mContactPoints[index]; + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformdelta(const Ps::aos::PsTransformV& curTransform) + { + using namespace Ps::aos; + const Vec4V p0 = Vec4V_From_Vec3V(mRelativeTransform.p); + const Vec4V q0 = mRelativeTransform.q; + const Vec4V p1 = Vec4V_From_Vec3V(curTransform.p); + const Vec4V q1 = curTransform.q; + + const Vec4V dp = V4Abs(V4Sub(p1, p0)); + const Vec4V dq = V4Abs(V4Sub(q1, q0)); + + const Vec4V max0 = V4Max(dp, dq); + + //need to work out max from a single vector... + return V4ExtractMax(max0); + } + + PX_FORCE_INLINE Ps::aos::Vec4V maxTransformdelta2(const Ps::aos::PsTransformV& curTransform) + { + using namespace Ps::aos; + const Vec4V p0 = Vec4V_From_Vec3V(mRelativeTransform.p); + const Vec4V q0 = mRelativeTransform.q; + const Vec4V p1 = Vec4V_From_Vec3V(curTransform.p); + const Vec4V q1 = curTransform.q; + + const Vec4V dp = V4Abs(V4Sub(p1, p0)); + const Vec4V dq = V4Abs(V4Sub(q1, q0)); + + const Vec4V max0 = V4Max(dp, dq); + + //need to work out max from a single vector... + return max0; + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformPositionDelta(const Ps::aos::Vec3V& curP) + { + using namespace Ps::aos; + + const Vec3V deltaP = V3Sub(curP, mRelativeTransform.p); + const Vec4V delta = Vec4V_From_Vec3V(V3Abs(deltaP)); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformQuatDelta(const Ps::aos::QuatV& curQ) + { + using namespace Ps::aos; + + const Vec4V deltaQ = V4Sub(curQ, mRelativeTransform.q); + const Vec4V delta = V4Abs(deltaQ); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE void setRelativeTransform(const Ps::aos::PsTransformV& transform) + { + mRelativeTransform = transform; + } + + //This is used for the box/convexhull vs box/convexhull contact gen to decide whether the relative movement of a pair of objects are + //small enough. In this case, we can skip the collision detection all together + PX_FORCE_INLINE PxU32 invalidate_BoxConvex(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin) + { + using namespace Ps::aos; + PX_ASSERT(mNumContacts <= GU_MANIFOLD_CACHE_SIZE); + const FloatV ratio = FLoad(invalidateThresholds[mNumContacts]); + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + + const FloatV thresholdQ = FLoad(invalidateQuatThresholds[mNumContacts]); + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + //This is used for the sphere/capsule vs other primitives contact gen to decide whether the relative movement of a pair of objects are + //small enough. In this case, we can skip the collision detection all together + PX_FORCE_INLINE PxU32 invalidate_SphereCapsule(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin) + { + using namespace Ps::aos; + PX_ASSERT(mNumContacts <= 2); + const FloatV ratio = FLoad(invalidateThresholds2[mNumContacts]); + + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + + const FloatV thresholdQ = FLoad(invalidateQuatThresholds2[mNumContacts]); + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + //This is used for plane contact gen to decide whether the relative movement of a pair of objects are small enough. In this case, + //we can skip the collision detection all together + PX_FORCE_INLINE PxU32 invalidate_PrimitivesPlane(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin, const Ps::aos::FloatVArg ratio) + { + using namespace Ps::aos; + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + + const FloatV thresholdQ = FLoad(0.9998f);//about 1 degree + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + PX_FORCE_INLINE void removeContactPoint (PxU32 index) + { + mNumContacts--; + mContactPoints[index] = mContactPoints[mNumContacts]; + } + + bool validContactDistance(const PersistentContact& pt, const Ps::aos::FloatVArg breakingThreshold) const + { + using namespace Ps::aos; + const FloatV dist = V4GetW(pt.mLocalNormalPen); + return FAllGrtr(breakingThreshold, dist) != 0; + } + + PX_FORCE_INLINE void clearManifold() + { + mNumWarmStartPoints = 0; + mNumContacts = 0; + mRelativeTransform.Invalidate(); + } + + PX_FORCE_INLINE void initialize() + { + clearManifold(); + } + + //This function is used to replace the existing contact with the newly created contact if their distance are within some threshold + bool replaceManifoldPoint(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen, const Ps::aos::FloatVArg replaceBreakingThreshold); + + //This function is to add a point(in box/convexhull contact gen) to the exising manifold. If the number of manifold is more than 4, we need to do contact reduction + PxU32 addManifoldPoint( const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalAPen, const Ps::aos::FloatVArg replaceBreakingThreshold); + //This function is to add a point(in capsule contact gen) to the exising manifold. If the number of manifold is more than 4, we need to do contact reduction + PxU32 addManifoldPoint2( const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalAPen, const Ps::aos::FloatVArg replaceBreakingThreshold);//max two points of contacts + //This function is used in box-plane contact gen to add the plane contacts to the manifold + void addBatchManifoldContactsCluster( const PersistentContact* manifoldPoints, const PxU32 numPoints); + + //This function is used in the capsule full manifold contact genenation(maximum 2 points). + void addBatchManifoldContacts2( const PersistentContact* manifoldPoints, const PxU32 numPoints);//max two points of contacts + + //This function is used in the box/convexhull full manifold contact generation(maximum 4 points). + void addBatchManifoldContacts(const PersistentContact* manifoldPoints, const PxU32 numPoints, const PxReal toleranceLength); + //This function is using the cluster algorithm to reduce contacts + void reduceBatchContactsCluster(const PersistentContact* manifoldPoints, const PxU32 numPoints); + //This function is called by addBatchManifoldContacts2 to reduce the manifold contacts to 2 points; + void reduceBatchContacts2(const PersistentContact* manifoldPoints, const PxU32 numPoints); + //This function is called by addBatchManifoldContacts to reduce the manifold contacts to 4 points + void reduceBatchContacts(const PersistentContact* manifoldPoints, const PxU32 numPoints, const PxReal toleranceLength); + + + //This function is used for incremental manifold contact reduction for box/convexhull + PxU32 reduceContactsForPCM(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen); + //This function is used for incremental manifold contact reduction for capsule + PxU32 reduceContactSegment(const Ps::aos::Vec3VArg localPointA, const Ps::aos::Vec3VArg localPointB, const Ps::aos::Vec4VArg localNormalPen); + + + /* + This function recalculate the contacts in the manifold based on the current relative transform between a pair of objects. If the recalculated contacts are within some threshold, + we will keep the contacts; Otherwise, we will remove the contacts. + */ + void refreshContactPoints(const Ps::aos::PsMatTransformV& relTra, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg contactOffset); + //This function is just used in boxbox contact gen for fast transform + void addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsMatTransformV& transf1); + //This function is for adding box/convexhull manifold contacts to the contact buffer + void addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::PsTransformV& transf1, const Ps::aos::FloatVArg contactOffset); + //This function is for adding sphere/capsule manifold contacts to the contact buffer + void addManifoldContactsToContactBuffer(Gu::ContactBuffer& contactBuffer, const Ps::aos::Vec3VArg normal, const Ps::aos::Vec3VArg projectionNormal, const Ps::aos::PsTransformV& transf0, const Ps::aos::FloatVArg radius, const Ps::aos::FloatVArg contactOffset); + + //get the average normal in the manifold in world space + Ps::aos::Vec3V getWorldNormal(const Ps::aos::PsTransformV& trB); + //get the average normal in the manifold in local B object space + Ps::aos::Vec3V getLocalNormal(); + + void recordWarmStart(PxU8* aIndices, PxU8* bIndices, PxU8& nbWarmStartPoints); + void setWarmStart(const PxU8* aIndices, const PxU8* bIndices, const PxU8 nbWarmStartPoints); + void drawManifold(Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB); + void drawManifold(Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB, const Ps::aos::FloatVArg radius); + void drawManifold(const PersistentContact& m, Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB); + static void drawPoint(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p, const PxF32 size, const PxU32 color = 0x0000ff00); + static void drawLine(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const PxU32 color = 0xff00ffff); + static void drawTriangle(Cm::RenderOutput& out, const Ps::aos::Vec3VArg p0, const Ps::aos::Vec3VArg p1, const Ps::aos::Vec3VArg p2, const PxU32 color = 0xffff0000); + static void drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color = 0xff00ffff); + static void drawPolygon( Cm::RenderOutput& out, const Ps::aos::PsMatTransformV& transform, Ps::aos::Vec3V* points, const PxU32 numVerts, const PxU32 color = 0xff00ffff); + + Ps::aos::PsTransformV mRelativeTransform;//aToB + PxU8 mNumContacts; + PxU8 mCapacity; + PxU8 mNumWarmStartPoints; + PxU8 mAIndice[4]; + PxU8 mBIndice[4]; + PersistentContact* mContactPoints; +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +class LargePersistentContactManifold : public PersistentContactManifold +{ +public: + LargePersistentContactManifold() : PersistentContactManifold(mContactPointsBuff, GU_MANIFOLD_CACHE_SIZE) + { + } + + PersistentContact mContactPointsBuff[GU_MANIFOLD_CACHE_SIZE]; +}PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +class SpherePersistentContactManifold : public PersistentContactManifold +{ +public: + SpherePersistentContactManifold() : PersistentContactManifold(mContactPointsBuff, GU_SPHERE_MANIFOLD_CACHE_SIZE) + { + } + + PersistentContact mContactPointsBuff[GU_SPHERE_MANIFOLD_CACHE_SIZE]; +}PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +class SinglePersistentContactManifold +{ +public: + + SinglePersistentContactManifold(): mNumContacts(0) + { + } + + PX_FORCE_INLINE PxU32 getNumContacts() const { return mNumContacts;} + + PX_FORCE_INLINE bool isEmpty() { return mNumContacts==0; } + + PX_FORCE_INLINE MeshPersistentContact& getContactPoint(const PxU32 index) + { + PX_ASSERT(index < GU_SINGLE_MANIFOLD_CACHE_SIZE); + return mContactPoints[index]; + } + + PX_FORCE_INLINE void removeContactPoint (PxU32 index) + { + mNumContacts--; + mContactPoints[index] = mContactPoints[mNumContacts]; + } + + PX_FORCE_INLINE void clearManifold() + { + mNumContacts = 0; + } + + PX_FORCE_INLINE void initialize() + { + clearManifold(); + } + + + PX_FORCE_INLINE Ps::aos::Vec3V getWorldNormal(const Ps::aos::PsTransformV& trB) + { + using namespace Ps::aos; + Vec4V nPen = mContactPoints[0].mLocalNormalPen; + for(PxU32 i =1; i < mNumContacts; ++i) + { + nPen = V4Add(nPen, mContactPoints[i].mLocalNormalPen); + } + + const Vec3V n = Vec3V_From_Vec4V(nPen); + return V3Normalize(trB.rotate(n)); + } + + PX_FORCE_INLINE Ps::aos::Vec3V getLocalNormal() + { + using namespace Ps::aos; + Vec4V nPen = mContactPoints[0].mLocalNormalPen; + for(PxU32 i =1; i < mNumContacts; ++i) + { + nPen = V4Add(nPen, mContactPoints[i].mLocalNormalPen); + } + return V3Normalize(Vec3V_From_Vec4V(nPen)); + } + + //This function reduces the manifold contact list in a patch for box/convexhull vs mesh + Ps::aos::FloatV reduceBatchContactsConvex(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch); + //This function reduces the manifold contact list in a patch for sphere vs mesh + Ps::aos::FloatV reduceBatchContactsSphere(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch); + //This function reduces the manifold contact list in a pathc for capsuel vs mesh + Ps::aos::FloatV reduceBatchContactsCapsule(const MeshPersistentContact* manifoldContactExt, const PxU32 numContacts, PCMContactPatch& patch); + + //This function adds the manifold contact list in a patch for box/convexhull vs mesh + Ps::aos::FloatV addBatchManifoldContactsConvex(const MeshPersistentContact* manifoldContact, const PxU32 numContacts, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold); + //This function adds the manifold contact list in a patch for sphere vs mesh + Ps::aos::FloatV addBatchManifoldContactsSphere(const MeshPersistentContact* manifoldContact, const PxU32 numContacts, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold); + //This function adds the manifold contact list in a patch for capsule vs mesh + Ps::aos::FloatV addBatchManifoldContactsCapsule(const MeshPersistentContact* manifoldContact, const PxU32 numContacts, PCMContactPatch& patch, const Ps::aos::FloatVArg replaceBreakingThreshold); + + //This is used for in the addContactsToPatch for convex mesh contact gen. + static PxU32 reduceContacts(MeshPersistentContact* manifoldContactExt, PxU32 numContacts); + + //This function is to recalculate the contacts based on the relative transform between a pair of objects + Ps::aos::FloatV refreshContactPoints(const Ps::aos::PsMatTransformV& relTra, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg contactOffset); + + void drawManifold(Cm::RenderOutput& out, const Ps::aos::PsTransformV& trA, const Ps::aos::PsTransformV& trB); + + MeshPersistentContact mContactPoints[GU_SINGLE_MANIFOLD_CACHE_SIZE];//384 bytes + PxU32 mNumContacts;//400 bytes + +} PX_ALIGN_SUFFIX(16); + +//This is a structure used to cache a multi-persistent-manifold in the cache stream +struct MultiPersistentManifoldHeader +{ + Ps::aos::PsTransformV mRelativeTransform;//aToB + PxU32 mNumManifolds; + PxU32 pad[3]; +}; + +struct SingleManifoldHeader +{ + PxU32 mNumContacts; + PxU32 pad[3]; +}; + +#if PX_VC +#pragma warning(push) +#pragma warning( disable : 4251 ) // class needs to have dll-interface to be used by clients of class +#endif + +PX_ALIGN_PREFIX(16) +class PX_PHYSX_COMMON_API MultiplePersistentContactManifold +{ +public: + MultiplePersistentContactManifold():mNumManifolds(0), mNumTotalContacts(0) + { + mRelativeTransform.Invalidate(); + } + + PX_FORCE_INLINE void setRelativeTransform(const Ps::aos::PsTransformV& transform) + { + mRelativeTransform = transform; + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformPositionDelta(const Ps::aos::Vec3V& curP) + { + using namespace Ps::aos; + + const Vec3V deltaP = V3Sub(curP, mRelativeTransform.p); + const Vec4V delta = Vec4V_From_Vec3V(V3Abs(deltaP)); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE Ps::aos::FloatV maxTransformQuatDelta(const Ps::aos::QuatV& curQ) + { + using namespace Ps::aos; + + const Vec4V deltaQ = V4Sub(curQ, mRelativeTransform.q); + const Vec4V delta = V4Abs(deltaQ); + //need to work out max from a single vector... + return V4ExtractMax(delta); + } + + PX_FORCE_INLINE PxU32 invalidate(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin, const Ps::aos::FloatVArg ratio) + { + using namespace Ps::aos; + + const FloatV thresholdP = FMul(minMargin, ratio); + const FloatV deltaP = maxTransformPositionDelta(curRTrans.p); + const FloatV thresholdQ = FLoad(0.9998f);//about 1 degree + const FloatV deltaQ = QuatDot(curRTrans.q, mRelativeTransform.q); + const BoolV con = BOr(FIsGrtr(deltaP, thresholdP), FIsGrtr(thresholdQ, deltaQ)); + + return BAllEqTTTT(con); + } + + PX_FORCE_INLINE PxU32 invalidate(const Ps::aos::PsTransformV& curRTrans, const Ps::aos::FloatVArg minMargin) + { + using namespace Ps::aos; + return invalidate(curRTrans, minMargin, FLoad(0.2f)); + } + + /* + This function work out the contact patch connectivity. If two patches's normal are within 5 degree, we would link these two patches together and reset the total size. + */ + PX_FORCE_INLINE void refineContactPatchConnective(PCMContactPatch** contactPatch, PxU32 numContactPatch, MeshPersistentContact* manifoldContacts, const Ps::aos::FloatVArg acceptanceEpsilon) + { + PX_UNUSED(manifoldContacts); + + using namespace Ps::aos; + + //work out the contact patch connectivity, the patchNormal should be in the local space of mesh + for(PxU32 i=0; imRoot = patch; + patch->mEndPatch = patch; + patch->mTotalSize = patch->mEndIndex - patch->mStartIndex; + patch->mNextPatch = NULL; + + for(PxU32 j=i; j>0; --j) + { + PCMContactPatch* other = contactPatch[j-1]; + const FloatV d = V3Dot(patch->mPatchNormal, other->mRoot->mPatchNormal); + if(FAllGrtrOrEq(d, acceptanceEpsilon))//less than 5 degree + { + + other->mNextPatch = patch; + other->mRoot->mEndPatch = patch; + patch->mRoot = other->mRoot; + other->mRoot->mTotalSize += patch->mEndIndex - patch->mStartIndex; + break; + } + } + } + } + + + /* + This function uses to reduce the manifold contacts which are in different connected patchs but are within replace breaking threshold + */ + PX_FORCE_INLINE PxU32 reduceManifoldContactsInDifferentPatches(PCMContactPatch** contactPatch, PxU32 numContactPatch, MeshPersistentContact* manifoldContacts, PxU32 numContacts, const Ps::aos::FloatVArg sqReplaceBreaking) + { + using namespace Ps::aos; + + for(PxU32 i=0; imRoot == currentPatch) + { + while(currentPatch) + { + PCMContactPatch* nextPatch = currentPatch->mNextPatch; + if(nextPatch) + { + for(PxU32 k = currentPatch->mStartIndex; kmEndIndex; ++k) + { + for(PxU32 l = nextPatch->mStartIndex; l < nextPatch->mEndIndex; ++l) + { + Vec3V dif = V3Sub(manifoldContacts[l].mLocalPointB, manifoldContacts[k].mLocalPointB); + FloatV d = V3Dot(dif, dif); + if(FAllGrtr(sqReplaceBreaking, d)) + { + //if two manifold contacts are within threshold, we will get rid of the manifold contacts in the other contact patch + manifoldContacts[l] = manifoldContacts[nextPatch->mEndIndex-1]; + nextPatch->mEndIndex--; + numContacts--; + l--; + } + } + } + } + currentPatch = nextPatch; + } + } + } + + return numContacts; + } + + + /* + This function is for the multiple manifold loop through each individual single manifold to recalculate the contacts based on the relative transform between a pair of objects + */ + PX_FORCE_INLINE void refreshManifold(const Ps::aos::PsMatTransformV& relTra, const Ps::aos::FloatVArg projectBreakingThreshold, const Ps::aos::FloatVArg contactDist) + { + using namespace Ps::aos; + + //refresh manifold contacts + for(PxU32 i=0; i < mNumManifolds; ++i) + { + PxU8 ind = mManifoldIndices[i]; + PX_ASSERT(mManifoldIndices[i] < GU_MAX_MANIFOLD_SIZE); + PxU32 nextInd = PxMin(i, mNumManifolds-2u)+1; + Ps::prefetchLine(&mManifolds[mManifoldIndices[nextInd]]); + Ps::prefetchLine(&mManifolds[mManifoldIndices[nextInd]],128); + Ps::prefetchLine(&mManifolds[mManifoldIndices[nextInd]],256); + FloatV _maxPen = mManifolds[ind].refreshContactPoints(relTra, projectBreakingThreshold, contactDist); + if(mManifolds[ind].isEmpty()) + { + //swap the index with the next manifolds + PxU8 index = mManifoldIndices[--mNumManifolds]; + mManifoldIndices[mNumManifolds] = ind; + mManifoldIndices[i] = index; + i--; + } + else + { + FStore(_maxPen, &mMaxPen[ind]); + } + } + } + + + PX_FORCE_INLINE void initialize() + { + mNumManifolds = 0; + mNumTotalContacts = 0; + mRelativeTransform.Invalidate(); + for(PxU8 i=0; i 0; --i) + { + PersistentContact& manifoldPoint = mContactPoints[i-1]; + const Vec3V localAInB = aToB.transform( manifoldPoint.mLocalPointA ); // from a to b + const Vec3V localBInB = manifoldPoint.mLocalPointB; + const Vec3V v = V3Sub(localAInB, localBInB); + + const Vec3V localNormal = Vec3V_From_Vec4V(manifoldPoint.mLocalNormalPen); // normal in b space + const FloatV dist= V3Dot(v, localNormal); + + const Vec3V projectedPoint = V3NegScaleSub(localNormal, dist, localAInB);//manifoldPoint.worldPointA - manifoldPoint.worldPointB * manifoldPoint.m_distance1; + const Vec3V projectedDifference = V3Sub(localBInB, projectedPoint); + + const FloatV distance2d = V3Dot(projectedDifference, projectedDifference); + //const BoolV con = BOr(FIsGrtr(dist, contactOffset), FIsGrtr(distance2d, sqProjectBreakingThreshold)); + const BoolV con = FIsGrtr(distance2d, sqProjectBreakingThreshold); + if(BAllEqTTTT(con)) + { + removeContactPoint(i-1); + } + else + { + manifoldPoint.mLocalNormalPen = V4SetW(Vec4V_From_Vec3V(localNormal), dist); + } + } +} + +/* + This function copies the mesh persistent contact from the multiple manifold to compress buffer(NpCacheStreamPair in the PxcNpThreadContext) +*/ +PX_INLINE void MultiplePersistentContactManifold::toBuffer(PxU8* PX_RESTRICT buffer) +{ + using namespace Ps::aos; + PxU8* buff = buffer; + + PX_ASSERT(((uintptr_t(buff)) & 0xF) == 0); + MultiPersistentManifoldHeader* PX_RESTRICT header = reinterpret_cast(buff); + buff += sizeof(MultiPersistentManifoldHeader); + + PX_ASSERT(mNumManifolds <= GU_MAX_MANIFOLD_SIZE); + header->mNumManifolds = mNumManifolds; + header->mRelativeTransform = mRelativeTransform; + + for(PxU32 a = 0; a < mNumManifolds; ++a) + { + SingleManifoldHeader* manHeader = reinterpret_cast(buff); + buff += sizeof(SingleManifoldHeader); + SinglePersistentContactManifold& manifold = *getManifold(a); + manHeader->mNumContacts = manifold.mNumContacts; + PX_ASSERT((uintptr_t(buff) & 0xf) == 0); + CachedMeshPersistentContact* contacts = reinterpret_cast(buff); + //convert the mesh persistent contact to cached mesh persistent contact to save 16 byte memory per contact + for(PxU32 b = 0; b(contactPoint)) //this is used in the normal pcm contact gen +#define PX_CP_TO_MPCP(contactPoint) (reinterpret_cast(contactPoint))//this is used in the mesh pcm contact gen + +void addManifoldPoint(PersistentContact* manifoldContacts, PersistentContactManifold& manifold, GjkOutput& output, + const Ps::aos::PsMatTransformV& aToB, const Ps::aos::FloatV replaceBreakingThreshold); + +}//Gu +}//physx + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp new file mode 100644 index 000000000..a7210a921 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp @@ -0,0 +1,272 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepBoxBox.h" +#include "GuBox.h" +#include "GuIntersectionBoxBox.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionEdgeEdge.h" +#include "GuSweepSharedTests.h" +#include "CmMatrix34.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +namespace +{ +// PT: TODO: get rid of this copy +static const PxReal gFatBoxEdgeCoeff = 0.01f; + +// PT: TODO: get rid of this copy +static const PxVec3 gNearPlaneNormal[] = +{ + PxVec3(1.0f, 0.0f, 0.0f), + PxVec3(0.0f, 1.0f, 0.0f), + PxVec3(0.0f, 0.0f, 1.0f), + PxVec3(-1.0f, 0.0f, 0.0f), + PxVec3(0.0f, -1.0f, 0.0f), + PxVec3(0.0f, 0.0f, -1.0f) +}; + +#define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) + +static PxVec3 EdgeNormals[] = +{ + PxVec3(0, -INVSQRT2, -INVSQRT2), // 0-1 + PxVec3(INVSQRT2, 0, -INVSQRT2), // 1-2 + PxVec3(0, INVSQRT2, -INVSQRT2), // 2-3 + PxVec3(-INVSQRT2, 0, -INVSQRT2), // 3-0 + + PxVec3(0, INVSQRT2, INVSQRT2), // 7-6 + PxVec3(INVSQRT2, 0, INVSQRT2), // 6-5 + PxVec3(0, -INVSQRT2, INVSQRT2), // 5-4 + PxVec3(-INVSQRT2, 0, INVSQRT2), // 4-7 + + PxVec3(INVSQRT2, -INVSQRT2, 0), // 1-5 + PxVec3(INVSQRT2, INVSQRT2, 0), // 6-2 + PxVec3(-INVSQRT2, INVSQRT2, 0), // 3-7 + PxVec3(-INVSQRT2, -INVSQRT2, 0) // 4-0 +}; + +// PT: TODO: get rid of this copy +static const PxVec3* getBoxLocalEdgeNormals() +{ + return EdgeNormals; +} + +/** +Returns world edge normal +\param edgeIndex [in] 0 <= edge index < 12 +\param worldNormal [out] edge normal in world space +*/ +static void computeBoxWorldEdgeNormal(const Box& box, PxU32 edgeIndex, PxVec3& worldNormal) +{ + PX_ASSERT(edgeIndex<12); + worldNormal = box.rotate(getBoxLocalEdgeNormals()[edgeIndex]); +} + +} + +// ### optimize! and refactor. And optimize for aabbs +bool Gu::sweepBoxBox(const Box& box0, const Box& box1, const PxVec3& dir, PxReal length, PxHitFlags hitFlags, PxSweepHit& sweepHit) +{ + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(intersectOBBOBB(box0.extents, box0.center, box0.rot, box1.extents, box1.center, box1.rot, true)) + { + sweepHit.flags = PxHitFlag::eNORMAL; + sweepHit.distance = 0.0f; + sweepHit.normal = -dir; + return true; + } + } + + PxVec3 boxVertices0[8]; box0.computeBoxPoints(boxVertices0); + PxVec3 boxVertices1[8]; box1.computeBoxPoints(boxVertices1); + + // float MinDist = PX_MAX_F32; + PxReal MinDist = length; + int col = -1; + + // In following VF tests: + // - the direction is FW/BK since we project one box onto the other *and vice versa* + // - the normal reaction is FW/BK for the same reason + + // Vertices1 against Box0 + { + // We need: + + // - Box0 in local space + const PxVec3 Min0 = -box0.extents; + const PxVec3 Max0 = box0.extents; + + // - Vertices1 in Box0 space + Matrix34 worldToBox0; + computeWorldToBoxMatrix(worldToBox0, box0); + + // - the dir in Box0 space + const PxVec3 localDir0 = worldToBox0.rotate(dir); + + const PxVec3* boxNormals0 = gNearPlaneNormal; + + for(PxU32 i=0; i<8; i++) + { + PxReal tnear, tfar; + const int plane = intersectRayAABB(Min0, Max0, worldToBox0.transform(boxVertices1[i]), -localDir0, tnear, tfar); + + if(plane==-1 || tnear<0.0f) + continue; + + if(tnear <= MinDist) + { + MinDist = tnear; + sweepHit.normal = box0.rotate(boxNormals0[plane]); + sweepHit.position = boxVertices1[i]; + col = 0; + } + } + } + + // Vertices0 against Box1 + { + // We need: + + // - Box1 in local space + const PxVec3 Min1 = -box1.extents; + const PxVec3 Max1 = box1.extents; + + // - Vertices0 in Box1 space + Matrix34 worldToBox1; + computeWorldToBoxMatrix(worldToBox1, box1); + + // - the dir in Box1 space + const PxVec3 localDir1 = worldToBox1.rotate(dir); + + const PxVec3* boxNormals1 = gNearPlaneNormal; + + for(PxU32 i=0; i<8; i++) + { + PxReal tnear, tfar; + const int plane = intersectRayAABB(Min1, Max1, worldToBox1.transform(boxVertices0[i]), localDir1, tnear, tfar); + + if(plane==-1 || tnear<0.0f) + continue; + + if(tnear <= MinDist) + { + MinDist = tnear; + sweepHit.normal = box1.rotate(-boxNormals1[plane]); + sweepHit.position = boxVertices0[i] + tnear * dir; + col = 1; + } + } + } + + PxVec3 p1s, p2s, p3s, p4s; + { + const PxU8* PX_RESTRICT edges0 = getBoxEdges(); + const PxU8* PX_RESTRICT edges1 = getBoxEdges(); + + PxVec3 edgeNormals0[12]; + PxVec3 edgeNormals1[12]; + for(PxU32 i=0; i<12; i++) + computeBoxWorldEdgeNormal(box0, i, edgeNormals0[i]); + for(PxU32 i=0; i<12; i++) + computeBoxWorldEdgeNormal(box1, i, edgeNormals1[i]); + + // Loop through box edges + for(PxU32 i=0; i<12; i++) // 12 edges + { + if(!(edgeNormals0[i].dot(dir) >= 0.0f)) + continue; + + // Catch current box edge // ### one vertex already known using line-strips + + // Make it fat ### + PxVec3 p1 = boxVertices0[edges0[i*2+0]]; + PxVec3 p2 = boxVertices0[edges0[i*2+1]]; + Ps::makeFatEdge(p1, p2, gFatBoxEdgeCoeff); + + // Loop through box edges + for(PxU32 j=0;j<12;j++) + { + if(edgeNormals1[j].dot(dir) >= 0.0f) + continue; + + // Orientation culling + // PT: this was commented for some reason, but it fixes the "stuck" bug reported by Ubi. + // So I put it back. We'll have to see whether it produces Bad Things in particular cases. + if(edgeNormals0[i].dot(edgeNormals1[j]) >= 0.0f) + continue; + + // Catch current box edge + + // Make it fat ### + PxVec3 p3 = boxVertices1[edges1[j*2+0]]; + PxVec3 p4 = boxVertices1[edges1[j*2+1]]; + Ps::makeFatEdge(p3, p4, gFatBoxEdgeCoeff); + + PxReal Dist; + PxVec3 ip; + if(intersectEdgeEdge(p1, p2, dir, p3, p4, Dist, ip)) + { + if(Dist<=MinDist) + { + p1s = p1; + p2s = p2; + p3s = p3; + p4s = p4; + + sweepHit.position = ip + Dist * dir; + + col = 2; + MinDist = Dist; + } + } + } + } + } + + if(col==-1) + return false; + + if(col==2) + { + computeEdgeEdgeNormal(sweepHit.normal, p1s, p2s-p1s, p3s, p4s-p3s, dir, MinDist); + sweepHit.normal.normalize(); + } + + sweepHit.flags = PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + sweepHit.distance = MinDist; + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h new file mode 100644 index 000000000..efb643ca3 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h @@ -0,0 +1,49 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_BOX_H +#define GU_SWEEP_BOX_BOX_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Box; + + bool sweepBoxBox(const Box& box0, const Box& box1, const PxVec3& dir, PxReal length, PxHitFlags hitFlags, PxSweepHit& sweepHit); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp new file mode 100644 index 000000000..21a99edfd --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp @@ -0,0 +1,158 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepBoxSphere.h" +#include "GuOverlapTests.h" +#include "GuSphere.h" +#include "GuBoxConversion.h" +#include "GuCapsule.h" +#include "GuIntersectionRayCapsule.h" +#include "GuIntersectionRayBox.h" +#include "GuIntersectionSphereBox.h" +#include "GuDistancePointSegment.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; +using namespace Cm; + +namespace +{ +// PT: TODO: get rid of this copy +static const PxVec3 gNearPlaneNormal[] = +{ + PxVec3(1.0f, 0.0f, 0.0f), + PxVec3(0.0f, 1.0f, 0.0f), + PxVec3(0.0f, 0.0f, 1.0f), + PxVec3(-1.0f, 0.0f, 0.0f), + PxVec3(0.0f, -1.0f, 0.0f), + PxVec3(0.0f, 0.0f, -1.0f) +}; + +} + +bool Gu::sweepBoxSphere(const Box& box, PxReal sphereRadius, const PxVec3& spherePos, const PxVec3& dir, PxReal length, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags) +{ + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(intersectSphereBox(Sphere(spherePos, sphereRadius), box)) + { + // Overlap + min_dist = 0.0f; + normal = -dir; + return true; + } + } + + PxVec3 boxPts[8]; + box.computeBoxPoints(boxPts); + const PxU8* PX_RESTRICT edges = getBoxEdges(); + PxReal MinDist = length; + bool Status = false; + for(PxU32 i=0; i<12; i++) + { + const PxU8 e0 = *edges++; + const PxU8 e1 = *edges++; + const Capsule capsule(boxPts[e0], boxPts[e1], sphereRadius); + + PxReal t; + if(intersectRayCapsule(spherePos, dir, capsule, t)) + { + if(t>=0.0f && t<=MinDist) + { + MinDist = t; + + const PxVec3 ip = spherePos + t*dir; + distancePointSegmentSquared(capsule, ip, &t); + + PxVec3 ip2; + capsule.computePoint(ip2, t); + + normal = (ip2 - ip); + normal.normalize(); + Status = true; + } + } + } + + PxVec3 localPt; + { + Matrix34 M2; + buildMatrixFromBox(M2, box); + + localPt = M2.rotateTranspose(spherePos - M2.p); + } + + const PxVec3* boxNormals = gNearPlaneNormal; + + const PxVec3 localDir = box.rotateInv(dir); + + // PT: when the box exactly touches the sphere, the test for initial overlap can fail on some platforms. + // In this case we reach the sweep code below, which may return a slightly negative time of impact (it should be 0.0 + // but it ends up a bit negative because of limited FPU accuracy). The epsilon ensures that we correctly detect a hit + // in this case. + const PxReal epsilon = -1e-5f; + + PxReal tnear, tfar; + + PxVec3 extents = box.extents; + extents.x += sphereRadius; + int plane = intersectRayAABB(-extents, extents, localPt, localDir, tnear, tfar); + if(plane!=-1 && tnear>=epsilon && tnear <= MinDist) + { + MinDist = PxMax(tnear, 0.0f); + normal = box.rotate(boxNormals[plane]); + Status = true; + } + + extents = box.extents; + extents.y += sphereRadius; + plane = intersectRayAABB(-extents, extents, localPt, localDir, tnear, tfar); + if(plane!=-1 && tnear>=epsilon && tnear <= MinDist) + { + MinDist = PxMax(tnear, 0.0f); + normal = box.rotate(boxNormals[plane]); + Status = true; + } + + extents = box.extents; + extents.z += sphereRadius; + plane = intersectRayAABB(-extents, extents, localPt, localDir, tnear, tfar); + if(plane!=-1 && tnear>=epsilon && tnear <= MinDist) + { + MinDist = PxMax(tnear, 0.0f); + normal = box.rotate(boxNormals[plane]); + Status = true; + } + + min_dist = MinDist; + + return Status; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h new file mode 100644 index 000000000..0e561f2c4 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h @@ -0,0 +1,49 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_SPHERE_H +#define GU_SWEEP_BOX_SPHERE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Box; + + bool sweepBoxSphere(const Box& box, PxReal sphereRadius, const PxVec3& spherePos, const PxVec3& dir, PxReal length, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp new file mode 100644 index 000000000..659e40023 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp @@ -0,0 +1,622 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxBounds3.h" +#include "GuSweepBoxTriangle_FeatureBased.h" +#include "GuIntersectionRayBox.h" +#include "PxTriangle.h" +#include "GuSweepTriangleUtils.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Gu; + +#define LOCAL_EPSILON 0.00001f // PT: this value makes the 'basicAngleTest' pass. Fails because of a ray almost parallel to a triangle + +namespace +{ +static const PxReal gFatTriangleCoeff = 0.02f; + +static const PxVec3 gNearPlaneNormal[] = +{ + PxVec3(1.0f, 0.0f, 0.0f), + PxVec3(0.0f, 1.0f, 0.0f), + PxVec3(0.0f, 0.0f, 1.0f), + PxVec3(-1.0f, 0.0f, 0.0f), + PxVec3(0.0f, -1.0f, 0.0f), + PxVec3(0.0f, 0.0f, -1.0f) +}; +} + +#define INVSQRT3 0.577350269189f //!< 1 / sqrt(3) + +/** +Returns vertex normals. +\return 24 floats (8 normals) +*/ +static const PxF32* getBoxVertexNormals() +{ + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + static PxF32 VertexNormals[] = + { + -INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, INVSQRT3, INVSQRT3, + -INVSQRT3, INVSQRT3, INVSQRT3 + }; + + return VertexNormals; +} + +static PxTriangle inflateTriangle(const PxTriangle& triangle, PxReal fat_coeff) +{ + PxTriangle fatTri = triangle; + + // Compute triangle center + const PxVec3& p0 = triangle.verts[0]; + const PxVec3& p1 = triangle.verts[1]; + const PxVec3& p2 = triangle.verts[2]; + const PxVec3 center = (p0 + p1 + p2)*0.333333333f; + + // Don't normalize? + // Normalize => add a constant border, regardless of triangle size + // Don't => add more to big triangles + for(PxU32 i=0;i<3;i++) + { + const PxVec3 v = fatTri.verts[i] - center; + fatTri.verts[i] += v * fat_coeff; + } + return fatTri; +} + +// PT: special version to fire N parallel rays against the same tri +static PX_FORCE_INLINE Ps::IntBool rayTriPrecaCull( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, const PxVec3& pvec, + PxReal det, PxReal oneOverDet, PxReal& t) +{ + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter and test bounds + PxReal u = tvec.dot(pvec); + if((u < 0.0f) || u>det) + return 0; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + PxReal v = dir.dot(qvec); + if((v < 0.0f) || u+v>det) + return 0; + + // Calculate t, scale parameters, ray intersects triangle + t = edge2.dot(qvec); + t *= oneOverDet; + return 1; +} + +static PX_FORCE_INLINE Ps::IntBool rayTriPrecaNoCull( const PxVec3& orig, const PxVec3& dir, + const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, const PxVec3& pvec, + PxReal /*det*/, PxReal oneOverDet, PxReal& t) +{ + // Calculate distance from vert0 to ray origin + const PxVec3 tvec = orig - vert0; + + // Calculate U parameter and test bounds + PxReal u = (tvec.dot(pvec)) * oneOverDet; + if((u < 0.0f) || u>1.0f) + return 0; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + PxReal v = (dir.dot(qvec)) * oneOverDet; + if((v < 0.0f) || u+v>1.0f) + return 0; + + // Calculate t, ray intersects triangle + t = (edge2.dot(qvec)) * oneOverDet; + return 1; +} + +// PT: specialized version where oneOverDir is available +// PT: why did we change the initial epsilon value? +#define LOCAL_EPSILON_RAY_BOX PX_EPS_F32 +//#define LOCAL_EPSILON_RAY_BOX 0.0001f +static PX_FORCE_INLINE int intersectRayAABB2(const PxVec3& minimum, const PxVec3& maximum, + const PxVec3& ro, const PxVec3& /*rd*/, const PxVec3& oneOverDir, + float& tnear, float& tfar, + bool fbx, bool fby, bool fbz) +{ + // PT: this unrolled loop is a lot faster on Xbox + + if(fbx) + if(ro.xmaximum.x) + { +// tnear = FLT_MAX; + return -1; + } + if(fby) + if(ro.ymaximum.y) + { +// tnear = FLT_MAX; + return -1; + } + if(fbz) + if(ro.zmaximum.z) + { +// tnear = FLT_MAX; + return -1; + } + + PxReal t1x = (minimum.x - ro.x) * oneOverDir.x; + PxReal t2x = (maximum.x - ro.x) * oneOverDir.x; + PxReal t1y = (minimum.y - ro.y) * oneOverDir.y; + PxReal t2y = (maximum.y - ro.y) * oneOverDir.y; + PxReal t1z = (minimum.z - ro.z) * oneOverDir.z; + PxReal t2z = (maximum.z - ro.z) * oneOverDir.z; + + int bx; + int by; + int bz; + + if(t1x>t2x) + { + PxReal t=t1x; t1x=t2x; t2x=t; + bx = 3; + } + else + { + bx = 0; + } + + if(t1y>t2y) + { + PxReal t=t1y; t1y=t2y; t2y=t; + by = 4; + } + else + { + by = 1; + } + + if(t1z>t2z) + { + PxReal t=t1z; t1z=t2z; t2z=t; + bz = 5; + } + else + { + bz = 2; + } + + int ret; + if(!fbx) + { +// if(t1x>tnear) // PT: no need to test for the first value + { + tnear = t1x; + ret = bx; + } +// tfar = Px::intrinsics::selectMin(tfar, t2x); + tfar = t2x; // PT: no need to test for the first value + } + else + { + ret=-1; + tnear = -PX_MAX_F32; + tfar = PX_MAX_F32; + } + + if(!fby) + { + if(t1y>tnear) + { + tnear = t1y; + ret = by; + } + tfar = physx::intrinsics::selectMin(tfar, t2y); + } + + if(!fbz) + { + if(t1z>tnear) + { + tnear = t1z; + ret = bz; + } + tfar = physx::intrinsics::selectMin(tfar, t2z); + } + + if(tnear>tfar || tfar no collision + // We test both sides at the same time by only testing Sign(d3 * d4). + // ### put that in the Plane class + // ### also check that code in the triangle class that might be similar + const PxReal d3 = plane.distance(p3); + + const PxReal temp = d3 * plane.distance(p4); + if(temp>0.0f) return false; + + // if colliding edge (p3,p4) and plane are parallel return no collision + const PxVec3 v2 = p4 - p3; + + const PxReal temp2 = plane.n.dot(v2); + if(temp2==0.0f) return false; // ### epsilon would be better + + // compute intersection point of plane and colliding edge (p3,p4) + ip = p3-v2*(d3/temp2); + + // compute distance of intersection from line (ip, -dir) to line (p1,p2) + dist = (v1[i]*(ip[j]-p1[j])-v1[j]*(ip[i]-p1[i])) * coeff; + if(dist<0.0f) return false; + + // compute intersection point on edge (p1,p2) line + ip -= dist*dir; + + // check if intersection point (ip) is between edge (p1,p2) vertices + const PxReal temp3 = (p1.x-ip.x)*(p2.x-ip.x)+(p1.y-ip.y)*(p2.y-ip.y)+(p1.z-ip.z)*(p2.z-ip.z); + return temp3<0.0f; +} + +namespace +{ +static const PxReal gFatBoxEdgeCoeff = 0.01f; +#define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) + +static const PxVec3 EdgeNormals[] = +{ + PxVec3(0, -INVSQRT2, -INVSQRT2), // 0-1 + PxVec3(INVSQRT2, 0, -INVSQRT2), // 1-2 + PxVec3(0, INVSQRT2, -INVSQRT2), // 2-3 + PxVec3(-INVSQRT2, 0, -INVSQRT2), // 3-0 + + PxVec3(0, INVSQRT2, INVSQRT2), // 7-6 + PxVec3(INVSQRT2, 0, INVSQRT2), // 6-5 + PxVec3(0, -INVSQRT2, INVSQRT2), // 5-4 + PxVec3(-INVSQRT2, 0, INVSQRT2), // 4-7 + + PxVec3(INVSQRT2, -INVSQRT2, 0), // 1-5 + PxVec3(INVSQRT2, INVSQRT2, 0), // 6-2 + PxVec3(-INVSQRT2, INVSQRT2, 0), // 3-7 + PxVec3(-INVSQRT2, -INVSQRT2, 0) // 4-0 +}; + +static const PxVec3* getBoxLocalEdgeNormals() +{ + return EdgeNormals; +} +} + +static PX_FORCE_INLINE void closestAxis2(const PxVec3& v, PxU32& j, PxU32& k) +{ + // find largest 2D plane projection + const PxF32 absPx = physx::intrinsics::abs(v.x); + const PxF32 absPy = physx::intrinsics::abs(v.y); + const PxF32 absPz = physx::intrinsics::abs(v.z); + //PxU32 m = 0; //x biggest axis + j = 1; + k = 2; + if( absPy > absPx && absPy > absPz) + { + //y biggest + j = 2; + k = 0; + //m = 1; + } + else if(absPz > absPx) + { + //z biggest + j = 0; + k = 1; + //m = 2; + } +// return m; +} + +bool Gu::sweepBoxTriangle( const PxTriangle& tri, const PxBounds3& box, + const PxVec3& motion, const PxVec3& oneOverMotion, + PxVec3& hit, PxVec3& normal, PxReal& d, bool isDoubleSided) +{ + // Create triangle normal + PxVec3 denormalizedTriNormal; + tri.denormalizedNormal(denormalizedTriNormal); + + // Backface culling + const bool doBackfaceCulling = !isDoubleSided; + if(doBackfaceCulling && (denormalizedTriNormal.dot(motion)) >= 0.0f) // ">=" is important ! + return false; + + ///////////////////////// + + PxVec3 boxVertices[8]; + computeBoxPoints(box, boxVertices); + + ///////////////////////// + + // Make fat triangle + const PxTriangle fatTri = inflateTriangle(tri, gFatTriangleCoeff); + + PxReal minDist = d; // Initialize with current best distance + int col = -1; + + // Box vertices VS triangle + { + // ### cull using box-plane distance ? + const PxVec3 edge1 = fatTri.verts[1] - fatTri.verts[0]; + const PxVec3 edge2 = fatTri.verts[2] - fatTri.verts[0]; + const PxVec3 PVec = motion.cross(edge2); + const PxReal Det = edge1.dot(PVec); + + // We can't use stamps here since we can still find a better TOI for a given vertex, + // even if that vertex has already been tested successfully against another triangle. + const PxVec3* VN = reinterpret_cast(getBoxVertexNormals()); + + const PxReal oneOverDet = Det!=0.0f ? 1.0f / Det : 0.0f; + + PxU32 hitIndex=0; + if(doBackfaceCulling) + { + if(Det>=LOCAL_EPSILON) + { + for(PxU32 i=0;i<8;i++) + { + // Orientation culling + if((VN[i].dot(denormalizedTriNormal) >= 0.0f)) // Can't rely on triangle normal for double-sided faces + continue; + + // ### test this + // ### ok, this causes the bug in level3's v-shaped desk. Not really a real "bug", it just happens + // that this VF test fixes this case, so it's a bad idea to cull it. Oh, well. + // If we use a penetration-depth code to fixup bad cases, we can enable this culling again. (also + // if we find a better way to handle that desk) + // Discard back vertices +// if(VN[i].dot(motion)<0.0f) +// continue; + + // Shoot a ray from vertex against triangle, in direction "motion" + PxReal t; + if(!rayTriPrecaCull(boxVertices[i], motion, fatTri.verts[0], edge1, edge2, PVec, Det, oneOverDet, t)) + continue; + + //if(t<=OffsetLength) t=0.0f; + // Only consider positive distances, closer than current best + // ### we could test that first on tri vertices & discard complete tri if it's further than current best (or equal!) + if(t < 0.0f || t > minDist) + continue; + + minDist = t; + col = 0; +// hit = boxVertices[i] + t * motion; + hitIndex = i; + } + } + } + else + { + if(Det<=-LOCAL_EPSILON || Det>=LOCAL_EPSILON) + { + for(PxU32 i=0;i<8;i++) + { + // ### test this + // ### ok, this causes the bug in level3's v-shaped desk. Not really a real "bug", it just happens + // that this VF test fixes this case, so it's a bad idea to cull it. Oh, well. + // If we use a penetration-depth code to fixup bad cases, we can enable this culling again. (also + // if we find a better way to handle that desk) + // Discard back vertices + // if(!VN[i].SameDirection(motion)) + // continue; + + // Shoot a ray from vertex against triangle, in direction "motion" + PxReal t; + if(!rayTriPrecaNoCull(boxVertices[i], motion, fatTri.verts[0], edge1, edge2, PVec, Det, oneOverDet, t)) + continue; + + //if(t<=OffsetLength) t=0.0f; + // Only consider positive distances, closer than current best + // ### we could test that first on tri vertices & discard complete tri if it's further than current best (or equal!) + if(t < 0.0f || t > minDist) + continue; + + minDist = t; + col = 0; +// hit = boxVertices[i] + t * motion; + hitIndex = i; + } + } + } + + // Only copy this once, if needed + if(col==0) + { + // PT: hit point on triangle + hit = boxVertices[hitIndex] + minDist * motion; + normal = denormalizedTriNormal; + } + } + + // Triangle vertices VS box + { + const PxVec3 negMotion = -motion; + const PxVec3 negInvMotion = -oneOverMotion; + + // PT: precompute fabs-test for ray-box + // - doing this outside of the ray-box function gets rid of 3 fabs/fcmp per call + // - doing this with integer code removes the 3 remaining fabs/fcmps totally + // - doing this outside reduces the LHS + const bool b0 = physx::intrinsics::abs(negMotion.x) flips normals + normal = boxNormals[plane]; + col = 1; + + // PT: hit point on triangle + hit = tri.verts[i]; + } + } + } + + PxU32 saved_j = PX_INVALID_U32; + PxU32 saved_k = PX_INVALID_U32; + PxVec3 p1s; + PxVec3 v1s; + + // Edge-vs-edge + { + // Loop through box edges + const PxU8* PX_RESTRICT edges = getBoxEdges(); + const PxVec3* PX_RESTRICT edgeNormals = getBoxLocalEdgeNormals(); + for(PxU32 i=0;i<12;i++) // 12 edges + { + // PT: TODO: skip this if edge is culled + PxVec3 p1 = boxVertices[*edges++]; + PxVec3 p2 = boxVertices[*edges++]; + Ps::makeFatEdge(p1, p2, gFatBoxEdgeCoeff); + + if(edgeNormals[i].dot(motion) < 0.0f) + continue; + + // While we're at it, precompute some more data for EE tests + const PxVec3 v1 = p2 - p1; + + // Build plane P based on edge (p1, p2) and direction (dir) + const PxVec3 planeNormal = v1.cross(motion); + const PxPlane plane(planeNormal, -(planeNormal.dot(p1))); + + // find largest 2D plane projection + PxU32 closest_i, closest_j; + // Ps::closestAxis(plane.normal, ii, jj); + closestAxis2(planeNormal, closest_i, closest_j); + + const PxReal coeff = 1.0f / (v1[closest_i]*motion[closest_j] - v1[closest_j]*motion[closest_i]); + + // Loop through triangle edges + for(PxU32 j=0; j<3; j++) + { + // Catch current triangle edge + // j=0 => 0-1 + // j=1 => 1-2 + // j=2 => 2-0 + // => this is compatible with EdgeList + const PxU32 k = Ps::getNextIndex3(j); + + PxReal dist; + PxVec3 ip; + if(intersectEdgeEdge3(plane, p1, p2, motion, v1, tri.verts[j], tri.verts[k], dist, ip, closest_i, closest_j, coeff)) + { + if(dist<=minDist) + { + p1s = p1; + v1s = v1; + saved_j = j; + saved_k = k; + + col = 2; + minDist = dist; + + // PT: hit point on triangle + hit = ip + motion*dist; + } + } + } + } + } + + if(col==-1) + return false; + + if(col==2) + { + PX_ASSERT(saved_j != PX_INVALID_U32); + PX_ASSERT(saved_k != PX_INVALID_U32); + const PxVec3& p3 = tri.verts[saved_j]; + const PxVec3& p4 = tri.verts[saved_k]; + computeEdgeEdgeNormal(normal, p1s, v1s, p3, p4-p3, motion, minDist); + } + + d = minDist; + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h new file mode 100644 index 000000000..81ebc2878 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h @@ -0,0 +1,67 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_TRIANGLE_FEATURE_BASED_H +#define GU_SWEEP_BOX_TRIANGLE_FEATURE_BASED_H + +#include "foundation/PxVec3.h" +#include "foundation/PxPlane.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + class PxTriangle; + + namespace Gu + { + /** + Sweeps a box against a triangle, using a 'feature-based' approach. + + This is currently only used for computing the box-sweep impact data, in a second pass, + after the best triangle has been identified using faster approaches (SAT/GJK). + + \warning Returned impact normal is not normalized + + \param tri [in] the triangle + \param box [in] the box + \param motion [in] (box) motion vector + \param oneOverMotion [in] precomputed inverse of motion vector + \param hit [out] impact point + \param normal [out] impact normal (warning: not normalized) + \param d [in/out] impact distance (please initialize with best current distance) + \param isDoubleSided [in] whether triangle is double-sided or not + \return true if an impact has been found + */ + bool sweepBoxTriangle( const PxTriangle& tri, const PxBounds3& box, + const PxVec3& motion, const PxVec3& oneOverMotion, + PxVec3& hit, PxVec3& normal, PxReal& d, bool isDoubleSided=false); + } // namespace Gu +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp new file mode 100644 index 000000000..13219ea87 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp @@ -0,0 +1,39 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepBoxTriangle_SAT.h" + +using namespace physx; +using namespace Gu; + +// PT: SAT-based version, in box space +int Gu::triBoxSweepTestBoxSpace(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& toi, bool doBackfaceCulling) +{ + return triBoxSweepTestBoxSpace_inlined(tri, extents, dir, oneOverDir, tmax, toi, PxU32(doBackfaceCulling)); +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h new file mode 100644 index 000000000..3116f86e2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h @@ -0,0 +1,235 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_BOX_TRIANGLE_SAT_H +#define GU_SWEEP_BOX_TRIANGLE_SAT_H + +#include "GuSweepSharedTests.h" +#include "PxTriangle.h" + + #define RetType int + #define MTDType bool + +namespace physx +{ +namespace Gu +{ + +// We have separation if one of those conditions is true: +// -BoxExt > TriMax (box strictly to the right of the triangle) +// BoxExt < TriMin (box strictly to the left of the triangle +// <=> d0 = -BoxExt - TriMax > 0 +// d1 = BoxExt - TriMin < 0 +// Hence we have overlap if d0 <= 0 and d1 >= 0 +// overlap = (d0<=0.0f && d1>=0.0f) +#define TEST_OVERLAP \ + const float d0 = -BoxExt - TriMax; \ + const float d1 = BoxExt - TriMin; \ + const bool bIntersect = (d0<=0.0f && d1>=0.0f); \ + bValidMTD &= bIntersect; + + // PT: inlining this one is important. Returning floats looks bad but is faster on Xbox. + static PX_FORCE_INLINE RetType testAxis(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& axis, MTDType& bValidMTD, float& tfirst, float& tlast) + { + const float d0t = tri.verts[0].dot(axis); + const float d1t = tri.verts[1].dot(axis); + const float d2t = tri.verts[2].dot(axis); + + float TriMin = PxMin(d0t, d1t); + float TriMax = PxMax(d0t, d1t); + TriMin = PxMin(TriMin, d2t); + TriMax = PxMax(TriMax, d2t); + + //////// + + const float BoxExt = PxAbs(axis.x)*extents.x + PxAbs(axis.y)*extents.y + PxAbs(axis.z)*extents.z; + TEST_OVERLAP + + const float v = dir.dot(axis); + if(PxAbs(v) < 1.0E-6f) + return bIntersect; + const float oneOverV = -1.0f / v; + + // float t0 = d0 * oneOverV; + // float t1 = d1 * oneOverV; + // if(t0 > t1) TSwap(t0, t1); + const float t0_ = d0 * oneOverV; + const float t1_ = d1 * oneOverV; + float t0 = PxMin(t0_, t1_); + float t1 = PxMax(t0_, t1_); + + if(t0 > tlast) return false; + if(t1 < tfirst) return false; + + // if(t1 < tlast) tlast = t1; + tlast = PxMin(t1, tlast); + + // if(t0 > tfirst) tfirst = t0; + tfirst = PxMax(t0, tfirst); + + return true; + } + + template + static PX_FORCE_INLINE RetType testAxisXYZ(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, float oneOverDir, MTDType& bValidMTD, float& tfirst, float& tlast) + { + const float d0t = tri.verts[0][XYZ]; + const float d1t = tri.verts[1][XYZ]; + const float d2t = tri.verts[2][XYZ]; + + float TriMin = PxMin(d0t, d1t); + float TriMax = PxMax(d0t, d1t); + TriMin = PxMin(TriMin, d2t); + TriMax = PxMax(TriMax, d2t); + + //////// + + const float BoxExt = extents[XYZ]; + TEST_OVERLAP + + const float v = dir[XYZ]; + if(PxAbs(v) < 1.0E-6f) + return bIntersect; + + const float oneOverV = -oneOverDir; + + // float t0 = d0 * oneOverV; + // float t1 = d1 * oneOverV; + // if(t0 > t1) TSwap(t0, t1); + const float t0_ = d0 * oneOverV; + const float t1_ = d1 * oneOverV; + float t0 = PxMin(t0_, t1_); + float t1 = PxMax(t0_, t1_); + + if(t0 > tlast) return false; + if(t1 < tfirst) return false; + + // if(t1 < tlast) tlast = t1; + tlast = PxMin(t1, tlast); + + // if(t0 > tfirst) tfirst = t0; + tfirst = PxMax(t0, tfirst); + + return true; + } + + PX_FORCE_INLINE int testSeparationAxes( const PxTriangle& tri, const PxVec3& extents, + const PxVec3& normal, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& tcoll) + { + bool bValidMTD = true; + float tfirst = -FLT_MAX; + float tlast = FLT_MAX; + + // Triangle normal + if(!testAxis(tri, extents, dir, normal, bValidMTD, tfirst, tlast)) + return 0; + + // Box normals + if(!testAxisXYZ<0>(tri, extents, dir, oneOverDir.x, bValidMTD, tfirst, tlast)) + return 0; + if(!testAxisXYZ<1>(tri, extents, dir, oneOverDir.y, bValidMTD, tfirst, tlast)) + return 0; + if(!testAxisXYZ<2>(tri, extents, dir, oneOverDir.z, bValidMTD, tfirst, tlast)) + return 0; + + // Edges + for(PxU32 i=0; i<3; i++) + { + int ip1 = int(i+1); + if(i>=2) ip1 = 0; + const PxVec3 TriEdge = tri.verts[ip1] - tri.verts[i]; + + { + const PxVec3 Sep = Ps::cross100(TriEdge); + if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)) + return 0; + } + { + const PxVec3 Sep = Ps::cross010(TriEdge); + if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)) + return 0; + } + { + const PxVec3 Sep = Ps::cross001(TriEdge); + if((Sep.dot(Sep))>=1.0E-6f && !testAxis(tri, extents, dir, Sep, bValidMTD, tfirst, tlast)) + return 0; + } + } + + if(tfirst > tmax || tlast < 0.0f) + return 0; + + if(tfirst <= 0.0f) + { + if(!bValidMTD) + return 0; + tcoll = 0.0f; + } + else tcoll = tfirst; + + return 1; + } + + //! Inlined version of triBoxSweepTestBoxSpace. See that other function for comments. + PX_FORCE_INLINE int triBoxSweepTestBoxSpace_inlined(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& toi, PxU32 doBackfaceCulling) + { + // Create triangle normal + PxVec3 triNormal; + tri.denormalizedNormal(triNormal); + + // Backface culling + if(doBackfaceCulling && (triNormal.dot(dir)) >= 0.0f) // ">=" is important ! + return 0; + + // The SAT test will properly detect initial overlaps, no need for extra tests + return testSeparationAxes(tri, extents, triNormal, dir, oneOverDir, tmax, toi); + } + + /** + Sweeps a box against a triangle, using a 'SAT' approach (Separating Axis Theorem). + + The test is performed in box-space, i.e. the box is axis-aligned and its center is (0,0,0). In other words it is + defined by its extents alone. The triangle must have been transformed to this "box-space" before calling the function. + + \param tri [in] triangle in box-space + \param extents [in] box extents + \param dir [in] sweep direction. Does not need to be normalized. + \param oneOverDir [in] precomputed inverse of sweep direction + \param tmax [in] sweep length + \param toi [out] time of impact/impact distance. Does not need to be initialized before calling the function. + \param doBackfaceCulling [in] true to enable backface culling, false for double-sided triangles + \return non-zero value if an impact has been found (in which case returned 'toi' value is valid) + */ + int triBoxSweepTestBoxSpace(const PxTriangle& tri, const PxVec3& extents, const PxVec3& dir, const PxVec3& oneOverDir, float tmax, float& toi, bool doBackfaceCulling); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp new file mode 100644 index 000000000..36dafac7c --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxBounds3.h" +#include "foundation/PxTransform.h" +#include "GuSweepCapsuleBox.h" +#include "GuSweepSphereTriangle.h" +#include "GuCapsule.h" +#include "PxTriangle.h" +#include "GuDistanceSegmentBox.h" +#include "GuInternal.h" +#include "PsAlloca.h" +#include "GuSIMDHelpers.h" + +using namespace physx; +using namespace Gu; + +namespace +{ +/** +* Returns triangles. +* \return 36 indices (12 triangles) indexing the list returned by ComputePoints() +*/ +static const PxU8* getBoxTriangles() +{ + static PxU8 Indices[] = { + 0,2,1, 0,3,2, + 1,6,5, 1,2,6, + 5,7,4, 5,6,7, + 4,3,0, 4,7,3, + 3,6,2, 3,7,6, + 5,0,1, 5,4,0 + }; + return Indices; +} +} + +#define OUTPUT_TRI(t, p0, p1, p2){ \ +t->verts[0] = p0; \ +t->verts[1] = p1; \ +t->verts[2] = p2; \ +t++;} + +#define OUTPUT_TRI2(t, p0, p1, p2, d){ \ +t->verts[0] = p0; \ +t->verts[1] = p1; \ +t->verts[2] = p2; \ +t->denormalizedNormal(denormalizedNormal); \ +if((denormalizedNormal.dot(d))>0.0f) { \ +PxVec3 Tmp = t->verts[1]; \ +t->verts[1] = t->verts[2]; \ +t->verts[2] = Tmp; \ +} \ +t++; *ids++ = i; } + +static PxU32 extrudeMesh( PxU32 nbTris, const PxTriangle* triangles, + const PxVec3& extrusionDir, PxTriangle* tris, PxU32* ids, const PxVec3& dir) +{ + const PxU32* base = ids; + + for(PxU32 i=0; i 0.0f; + if(culled) continue; + + PxVec3 p0 = currentTriangle.verts[0]; + PxVec3 p1 = currentTriangle.verts[1]; + PxVec3 p2 = currentTriangle.verts[2]; + + PxVec3 p0b = p0 + extrusionDir; + PxVec3 p1b = p1 + extrusionDir; + PxVec3 p2b = p2 + extrusionDir; + + p0 -= extrusionDir; + p1 -= extrusionDir; + p2 -= extrusionDir; + + if(denormalizedNormal.dot(extrusionDir) >= 0.0f) OUTPUT_TRI(tris, p0b, p1b, p2b) + else OUTPUT_TRI(tris, p0, p1, p2) + *ids++ = i; + + // ### it's probably useless to extrude all the shared edges !!!!! + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE12) + { + OUTPUT_TRI2(tris, p1, p1b, p2b, dir) + OUTPUT_TRI2(tris, p1, p2b, p2, dir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE20) + { + OUTPUT_TRI2(tris, p0, p2, p2b, dir) + OUTPUT_TRI2(tris, p0, p2b, p0b, dir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE01) + { + OUTPUT_TRI2(tris, p0b, p1b, p1, dir) + OUTPUT_TRI2(tris, p0b, p1, p0, dir) + } + } + return PxU32(ids-base); +} + +static PxU32 extrudeBox(const PxBounds3& localBox, const PxTransform* world, const PxVec3& extrusionDir, PxTriangle* tris, const PxVec3& dir) +{ + // Handle the box as a mesh + + PxTriangle boxTris[12]; + + PxVec3 p[8]; + computeBoxPoints(localBox, p); + + const PxU8* PX_RESTRICT indices = getBoxTriangles(); + + for(PxU32 i=0; i<12; i++) + { + const PxU8 VRef0 = indices[i*3+0]; + const PxU8 VRef1 = indices[i*3+1]; + const PxU8 VRef2 = indices[i*3+2]; + + PxVec3 p0 = p[VRef0]; + PxVec3 p1 = p[VRef1]; + PxVec3 p2 = p[VRef2]; + if(world) + { + p0 = world->transform(p0); + p1 = world->transform(p1); + p2 = world->transform(p2); + } + + boxTris[i].verts[0] = p0; + boxTris[i].verts[1] = p1; + boxTris[i].verts[2] = p2; + } + PxU32 fakeIDs[12*7]; + return extrudeMesh(12, boxTris, extrusionDir, tris, fakeIDs, dir); +} + +// +// The problem of testing a swept capsule against a box is transformed into sweeping a sphere (lying at the center +// of the capsule) against the extruded triangles of the box. The box triangles are extruded along the +// capsule segment axis. +// +bool Gu::sweepCapsuleBox(const Capsule& capsule, const PxTransform& boxWorldPose, const PxVec3& boxDim, const PxVec3& dir, PxReal length, PxVec3& hit, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags) +{ + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(distanceSegmentBoxSquared(capsule.p0, capsule.p1, boxWorldPose.p, boxDim, PxMat33Padded(boxWorldPose.q)) < capsule.radius*capsule.radius) + { + min_dist = 0.0f; + normal = -dir; + return true; + } + } + + // Extrusion dir = capsule segment + const PxVec3 extrusionDir = (capsule.p1 - capsule.p0)*0.5f; + + // Extrude box + PxReal MinDist = length; + bool Status = false; + { + const PxBounds3 aabb(-boxDim, boxDim); + + PX_ALLOCA(triangles, PxTriangle, 12*7); + const PxU32 nbTris = extrudeBox(aabb, &boxWorldPose, extrusionDir, triangles, dir); + PX_ASSERT(nbTris<=12*7); + + // Sweep sphere vs extruded box + PxSweepHit h; // PT: TODO: ctor! + PxVec3 bestNormal; + if(sweepSphereTriangles(nbTris, triangles, capsule.computeCenter(), capsule.radius, dir, length, NULL, h, bestNormal, false, false, false, false)) + { + hit = h.position; + MinDist = h.distance; + normal = h.normal; + Status = true; + } + } + + min_dist = MinDist; + return Status; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h new file mode 100644 index 000000000..55b094c02 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h @@ -0,0 +1,49 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_CAPSULE_BOX_H +#define GU_SWEEP_CAPSULE_BOX_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Capsule; + + bool sweepCapsuleBox(const Capsule& capsule, const PxTransform& boxWorldPose, const PxVec3& boxDim, const PxVec3& dir, PxReal length, PxVec3& hit, PxReal& min_dist, PxVec3& normal, PxHitFlags hitFlags); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp new file mode 100644 index 000000000..5b05d0d75 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp @@ -0,0 +1,344 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepCapsuleCapsule.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuDistanceSegmentSegment.h" +#include "GuIntersectionRayCapsule.h" +#include "PxQueryReport.h" +#include "PxTriangle.h" + +using namespace physx; +using namespace Gu; + +#define LOCAL_EPSILON 0.00001f // PT: this value makes the 'basicAngleTest' pass. Fails because of a ray almost parallel to a triangle + +static void edgeEdgeDist(PxVec3& x, PxVec3& y, // closest points + const PxVec3& p, const PxVec3& a, // seg 1 origin, vector + const PxVec3& q, const PxVec3& b) // seg 2 origin, vector +{ + const PxVec3 T = q - p; + const PxReal ADotA = a.dot(a); + const PxReal BDotB = b.dot(b); + const PxReal ADotB = a.dot(b); + const PxReal ADotT = a.dot(T); + const PxReal BDotT = b.dot(T); + + // t parameterizes ray (p, a) + // u parameterizes ray (q, b) + + // Compute t for the closest point on ray (p, a) to ray (q, b) + const PxReal Denom = ADotA*BDotB - ADotB*ADotB; + + PxReal t; + if(Denom!=0.0f) + { + t = (ADotT*BDotB - BDotT*ADotB) / Denom; + + // Clamp result so t is on the segment (p, a) + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + + // find u for point on ray (q, b) closest to point at t + PxReal u; + if(BDotB!=0.0f) + { + u = (t*ADotB - BDotT) / BDotB; + + // if u is on segment (q, b), t and u correspond to closest points, otherwise, clamp u, recompute and clamp t + if(u<0.0f) + { + u = 0.0f; + if(ADotA!=0.0f) + { + t = ADotT / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + } + else if(u > 1.0f) + { + u = 1.0f; + if(ADotA!=0.0f) + { + t = (ADotB + ADotT) / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + } + } + else + { + u = 0.0f; + + if(ADotA!=0.0f) + { + t = ADotT / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + } + + x = p + a * t; + y = q + b * u; +} + +static bool rayQuad(const PxVec3& orig, const PxVec3& dir, const PxVec3& vert0, const PxVec3& vert1, const PxVec3& vert2, PxReal& t, PxReal& u, PxReal& v, bool cull) +{ + // Find vectors for two edges sharing vert0 + const PxVec3 edge1 = vert1 - vert0; + const PxVec3 edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const PxReal det = edge1.dot(pvec); + + if(cull) + { + if(detdet) + return false; + + // Prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + v = dir.dot(qvec); + if(v<0.0f || v>det) + return false; + + // Calculate t, scale parameters, ray intersects triangle + t = edge2.dot(qvec); + const PxReal oneOverDet = 1.0f / det; + t *= oneOverDet; + u *= oneOverDet; + v *= oneOverDet; + } + else + { + // the non-culling branch + if(det>-LOCAL_EPSILON && det1.0f) + return false; + + // prepare to test V parameter + const PxVec3 qvec = tvec.cross(edge1); + + // Calculate V parameter and test bounds + v = (dir.dot(qvec)) * oneOverDet; + if(v<0.0f || v>1.0f) + return false; + + // Calculate t, ray intersects triangle + t = (edge2.dot(qvec)) * oneOverDet; + } + return true; +} + +bool Gu::sweepCapsuleCapsule(const Capsule& capsule0, const Capsule& capsule1, const PxVec3& dir, PxReal length, PxReal& min_dist, PxVec3& ip, PxVec3& normal, PxU32 inHitFlags, PxU16& outHitFlags) +{ + const PxReal radiusSum = capsule0.radius + capsule1.radius; + + if(!(inHitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + + // PT: It would be better not to use the same code path for spheres and capsules. The segment-segment distance + // function doesn't work for degenerate capsules so we need to test all combinations here anyway. + bool initialOverlapStatus; + if(capsule0.p0==capsule0.p1) + initialOverlapStatus = distancePointSegmentSquared(capsule1, capsule0.p0)= 0) // Same direction + { + Normal *= radiusSum; + pc = p0 - Normal; + pa = p1 - Normal; + pb = p1b - Normal; + } + else + { + Normal *= radiusSum; + pb = p0 + Normal; + pa = p1 + Normal; + pc = p1b + Normal; + } + PxReal t, u, v; + const PxVec3 center = capsule1.computeCenter(); + if(rayQuad(center, dir, pa, pb, pc, t, u, v, true) && t>=0.0f && t=0.0f && w<= MinDist) + { + MinDist = w; + Status = true; + } + } + } + } + + if(Status) + { + outHitFlags = PxHitFlags(0); + if(inHitFlags & PxU32(PxHitFlag::ePOSITION|PxHitFlag::eNORMAL)) + { + const PxVec3 p00 = capsule0.p0 - MinDist * dir; + const PxVec3 p01 = capsule0.p1 - MinDist * dir; +// const PxVec3 p10 = capsule1.p0;// - MinDist * dir; +// const PxVec3 p11 = capsule1.p1;// - MinDist * dir; + + const PxVec3 edge0 = p01 - p00; + const PxVec3 edge1 = capsuleExtent1; + + PxVec3 x, y; + edgeEdgeDist(x, y, p00, edge0, capsule1.p0, edge1); + + if(inHitFlags & PxHitFlag::eNORMAL) + { + normal = (x - y); + const float epsilon = 0.001f; + if(normal.normalize()0.0f) { \ + PxVec3 tmp = tri.verts[1]; \ + tri.verts[1] = tri.verts[2]; \ + tri.verts[2] = tmp; \ + nrm = -nrm; \ + } \ + extrudedTrisNormals[nbExtrudedTris] = nrm; \ + nbExtrudedTris++; } + + +bool Gu::sweepCapsuleTriangles_Precise( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const Capsule& capsule, // Capsule data + const PxVec3& unitDir, const PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& hit, PxVec3& triNormalOut, // Results + PxHitFlags hitFlags, bool isDoubleSided, // Query modifiers + const BoxPadded* cullBox) // Cull data +{ + if(!nbTris) + return false; + + const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; + const bool testInitialOverlap = !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP); + + // PT: we can fallback to sphere sweep: + // - if the capsule is degenerate (i.e. it's a sphere) + // - if the sweep direction is the same as the capsule axis, in which case we can just sweep the top or bottom sphere + + const PxVec3 extrusionDir = (capsule.p0 - capsule.p1)*0.5f; // Extrusion dir = capsule segment + const PxReal halfHeight = extrusionDir.magnitude(); + bool mustExtrude = halfHeight!=0.0f; + if(!mustExtrude) + { + // PT: capsule is a sphere. Switch to sphere path (intersectCapsuleTriangle doesn't work for degenerate capsules) + return sweepSphereTriangles(nbTris, triangles, capsule.p0, capsule.radius, unitDir, distance, cachedIndex, hit, triNormalOut, isDoubleSided, meshBothSides, anyHit, testInitialOverlap); + } + else + { + const PxVec3 capsuleAxis = extrusionDir/halfHeight; + const PxReal colinearity = PxAbs(capsuleAxis.dot(unitDir)); + mustExtrude = (colinearity < (1.0f - COLINEARITY_EPSILON)); + } + + const PxVec3 capsuleCenter = capsule.computeCenter(); + + if(!mustExtrude) + { + CapsuleTriangleOverlapData params; + params.init(capsule); + // PT: unfortunately we need to do IO test with the *capsule*, even though we're in the sphere codepath. So we + // can't directly reuse the sphere function. + const PxVec3 sphereCenter = capsuleCenter + unitDir * halfHeight; + // PT: this is a copy of 'sweepSphereTriangles' but with a capsule IO test. Saves double backface culling.... + { + PxU32 index = PX_INVALID_U32; + const PxU32 initIndex = getInitIndex(cachedIndex, nbTris); + + PxReal curT = distance; + const PxReal dpc0 = sphereCenter.dot(unitDir); + + PxReal bestAlignmentValue = 2.0f; + + PxVec3 bestTriNormal(0.0f); + + for(PxU32 ii=0; ii 0.0f)) + continue; + + if(testInitialOverlap && intersectCapsuleTriangle(triNormal, currentTri.verts[0], currentTri.verts[1], currentTri.verts[2], capsule, params)) + { + triNormalOut = -unitDir; + return setInitialOverlapResults(hit, unitDir, i); + } + + const PxReal magnitude = triNormal.magnitude(); + if(magnitude==0.0f) + continue; + + triNormal /= magnitude; + + PxReal currentDistance; + bool unused; + if(!sweepSphereVSTri(currentTri.verts, triNormal, sphereCenter, capsule.radius, unitDir, currentDistance, unused, false)) + continue; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal hitDot = computeAlignmentValue(triNormal, unitDir); + if(!keepTriangle(currentDistance, hitDot, curT, bestAlignmentValue, distance, distEpsilon)) + continue; + + curT = currentDistance; + index = i; + bestAlignmentValue = hitDot; + bestTriNormal = triNormal; + if(anyHit) + break; + } + return computeSphereTriangleImpactData(hit, triNormalOut, index, curT, sphereCenter, unitDir, bestTriNormal, triangles, isDoubleSided, meshBothSides); + } + } + + // PT: extrude mesh on the fly. This is a modified copy of sweepSphereTriangles, unfortunately + PxTriangle extrudedTris[7]; + PxVec3 extrudedTrisNormals[7]; // Not normalized + + hit.faceIndex = PX_INVALID_U32; + const PxU32 initIndex = getInitIndex(cachedIndex, nbTris); + + const PxReal radius = capsule.radius; + PxReal curT = distance; + const PxReal dpc0 = capsuleCenter.dot(unitDir); + + // PT: we will copy the best triangle here. Using indices alone doesn't work + // since we extrude on-the-fly (and we don't want to re-extrude later) + PxTriangle bestTri; + PxVec3 bestTriNormal(0.0f); + PxReal mostOpposingHitDot = 2.0f; + + CapsuleTriangleOverlapData params; + params.init(capsule); + + for(PxU32 ii=0; ii 0.0f)) + continue; + + if(cullBox) + { + if(!intersectTriangleBox(*cullBox, currentSrcTri.verts[0], currentSrcTri.verts[1], currentSrcTri.verts[2])) + continue; + } + + if(testInitialOverlap && intersectCapsuleTriangle(denormalizedNormal, currentSrcTri.verts[0], currentSrcTri.verts[1], currentSrcTri.verts[2], capsule, params)) + { + triNormalOut = -unitDir; + return setInitialOverlapResults(hit, unitDir, i); + } + + // Extrude mesh on the fly + PxU32 nbExtrudedTris=0; + + const PxVec3 p0 = currentSrcTri.verts[0] - extrusionDir; + const PxVec3 p1 = currentSrcTri.verts[1] - extrusionDir; + const PxVec3 p2 = currentSrcTri.verts[2] - extrusionDir; + + const PxVec3 p0b = currentSrcTri.verts[0] + extrusionDir; + const PxVec3 p1b = currentSrcTri.verts[1] + extrusionDir; + const PxVec3 p2b = currentSrcTri.verts[2] + extrusionDir; + + if(denormalizedNormal.dot(extrusionDir) >= 0.0f) OUTPUT_TRI(p0b, p1b, p2b) + else OUTPUT_TRI(p0, p1, p2) + + // ### it's probably useless to extrude all the shared edges !!!!! + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE12) + { + OUTPUT_TRI2(p1, p1b, p2b, unitDir) + OUTPUT_TRI2(p1, p2b, p2, unitDir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE20) + { + OUTPUT_TRI2(p0, p2, p2b, unitDir) + OUTPUT_TRI2(p0, p2b, p0b, unitDir) + } + //if(CurrentFlags & TriangleCollisionFlag::eACTIVE_EDGE01) + { + OUTPUT_TRI2(p0b, p1b, p1, unitDir) + OUTPUT_TRI2(p0b, p1, p0, unitDir) + } +///////////// + + // PT: TODO: this one is new, to fix the tweak issue. However this wasn't + // here before so the perf hit should be analyzed. + denormalizedNormal.normalize(); + const PxReal hitDot1 = computeAlignmentValue(denormalizedNormal, unitDir); + + for(PxU32 j=0;j 0.0f) + continue; + + // PT: beware, culling is only ok on the sphere I think + if(rejectTriangle(capsuleCenter, unitDir, curT, radius, currentTri.verts, dpc0)) + continue; + + const PxReal magnitude = triNormal.magnitude(); + if(magnitude==0.0f) + continue; + + triNormal /= magnitude; + + PxReal currentDistance; + bool unused; + if(!sweepSphereVSTri(currentTri.verts, triNormal, capsuleCenter, radius, unitDir, currentDistance, unused, false)) + continue; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + if(!keepTriangle(currentDistance, hitDot1, curT, mostOpposingHitDot, distance, distEpsilon)) + continue; + + curT = currentDistance; + hit.faceIndex = i; + mostOpposingHitDot = hitDot1; // arbitrary bias. works for hitDot1=-1, prevHitDot=0 + bestTri = currentTri; + bestTriNormal = denormalizedNormal; + if(anyHit) + goto Exit; // PT: using goto to have one test per hit, not test per triangle ('break' doesn't work here) + } + } +Exit: + if(hit.faceIndex==PX_INVALID_U32) + return false; // We didn't touch any triangle + + hit.distance = curT; + + triNormalOut = bestTriNormal; + + // Compute impact data only once, using best triangle + computeSphereTriImpactData(hit.position, hit.normal, capsuleCenter, unitDir, hit.distance, bestTri); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(hit.normal, meshBothSides, isDoubleSided, bestTriNormal, unitDir)) + hit.normal = -hit.normal; + + // PT: revisit this + if(hit.faceIndex!=PX_INVALID_U32) + { + // PT: we need to recompute a hit here because the hit between the *capsule* and the source mesh can be very + // different from the hit between the *sphere* and the extruded mesh. + + // Touched tri + const PxVec3& p0 = triangles[hit.faceIndex].verts[0]; + const PxVec3& p1 = triangles[hit.faceIndex].verts[1]; + const PxVec3& p2 = triangles[hit.faceIndex].verts[2]; + + // AP: measured to be a bit faster than the scalar version + const PxVec3 delta = unitDir*hit.distance; + Vec3V pointOnSeg, pointOnTri; + distanceSegmentTriangleSquared( + V3LoadU(capsule.p0 + delta), V3LoadU(capsule.p1 + delta), + V3LoadU(p0), V3LoadU(p1), V3LoadU(p2), + pointOnSeg, pointOnTri); + V3StoreU(pointOnTri, hit.position); + + hit.flags = PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + } + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h new file mode 100644 index 000000000..fa5dfdec0 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h @@ -0,0 +1,75 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_CAPSULE_TRIANGLE_H +#define GU_SWEEP_CAPSULE_TRIANGLE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ + class PxTriangle; + +namespace Gu +{ + class BoxPadded; + class Capsule; + + /** + Sweeps a capsule against a set of triangles. + + \param nbTris [in] number of triangles in input array + \param triangles [in] array of input triangles + \param capsule [in] the capsule + \param unitDir [in] sweep's unit direcion + \param distance [in] sweep's length + \param cachedIndex [in] cached triangle index, or NULL. Cached triangle will be tested first. + \param hit [out] results + \param triNormalOut [out] triangle normal + \param hitFlags [in] query modifiers + \param isDoubleSided [in] true if input triangles are double-sided + \param cullBox [in] additional/optional culling box. Triangles not intersecting the box are quickly discarded. + \warning if using a cullbox, make sure all triangles can be safely V4Loaded (i.e. allocate 4 more bytes after last triangle) + \return true if an impact has been found + */ + bool sweepCapsuleTriangles_Precise( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const Capsule& capsule, // Capsule data + const PxVec3& unitDir, const PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& hit, PxVec3& triNormalOut, // Results + PxHitFlags hitFlags, bool isDoubleSided, // Query modifiers + const BoxPadded* cullBox=NULL); // Cull data + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp new file mode 100644 index 000000000..740c8d7b2 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepSphereCapsule.h" +#include "GuSphere.h" +#include "GuCapsule.h" +#include "GuDistancePointSegment.h" +#include "GuSweepSphereSphere.h" +#include "GuIntersectionRayCapsule.h" + +using namespace physx; +using namespace Gu; + +bool Gu::sweepSphereCapsule(const Sphere& sphere, const Capsule& lss, const PxVec3& dir, PxReal length, PxReal& d, PxVec3& ip, PxVec3& nrm, PxHitFlags hitFlags) +{ + const PxReal radiusSum = lss.radius + sphere.radius; + + if(!(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) + { + // PT: test if shapes initially overlap + if(distancePointSegmentSquared(lss.p0, lss.p1, sphere.center)=0.0f && t<=length) + { + d = t; + +// PT: TODO: +// const Ps::IntBool needsImpactPoint = hitFlags & PxHitFlag::ePOSITION; +// if(needsImpactPoint || hitFlags & PxHitFlag::eNORMAL) + { + // Move capsule against sphere + const PxVec3 tdir = t*dir; + Inflated.p0 -= tdir; + Inflated.p1 -= tdir; + + // Compute closest point between moved capsule & sphere + distancePointSegmentSquared(Inflated, sphere.center, &t); + Inflated.computePoint(ip, t); + + // Normal + nrm = (ip - sphere.center); + nrm.normalize(); + +// if(needsImpactPoint) // PT: TODO + ip -= nrm * lss.radius; + } + return true; + } + } + return false; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h new file mode 100644 index 000000000..98e7e5691 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h @@ -0,0 +1,50 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SPHERE_CAPSULE_H +#define GU_SWEEP_SPHERE_CAPSULE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" +#include "PxQueryReport.h" + +namespace physx +{ +namespace Gu +{ + class Sphere; + class Capsule; + + bool sweepSphereCapsule(const Sphere& sphere, const Capsule& lss, const PxVec3& dir, PxReal length, PxReal& d, PxVec3& ip, PxVec3& nrm, PxHitFlags hitFlags); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp new file mode 100644 index 000000000..a9378d64a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp @@ -0,0 +1,116 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepSphereSphere.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Gu; + +// Adapted from Gamasutra (Gomez article) +// Return true if r1 and r2 are real +static PX_FORCE_INLINE bool quadraticFormula(const PxReal a, const PxReal b, const PxReal c, PxReal& r1, PxReal& r2) +{ + const PxReal q = b*b - 4*a*c; + if(q>=0.0f) + { + PX_ASSERT(a!=0.0f); + const PxReal sq = PxSqrt(q); + const PxReal d = 1.0f / (2.0f*a); + r1 = (-b + sq) * d; + r2 = (-b - sq) * d; + return true;//real roots + } + else + { + return false;//complex roots + } +} + +static bool sphereSphereSweep( const PxReal ra, //radius of sphere A + const PxVec3& A0, //previous position of sphere A + const PxVec3& A1, //current position of sphere A + const PxReal rb, //radius of sphere B + const PxVec3& B0, //previous position of sphere B + const PxVec3& B1, //current position of sphere B + PxReal& u0, //normalized time of first collision + PxReal& u1 //normalized time of second collision + ) +{ + const PxVec3 va = A1 - A0; + const PxVec3 vb = B1 - B0; + const PxVec3 AB = B0 - A0; + const PxVec3 vab = vb - va; // relative velocity (in normalized time) + const PxReal rab = ra + rb; + + const PxReal a = vab.dot(vab); //u*u coefficient + const PxReal b = 2.0f*(vab.dot(AB)); //u coefficient + + const PxReal c = (AB.dot(AB)) - rab*rab; //constant term + + //check if they're currently overlapping + if(c<=0.0f || a==0.0f) + { + u0 = 0.0f; + u1 = 0.0f; + return true; + } + + //check if they hit each other during the frame + if(quadraticFormula(a, b, c, u0, u1)) + { + if(u0>u1) + Ps::swap(u0, u1); + + // u01.0f) return false; + if(u1<0.0f || u0>1.0f) return false; + + return true; + } + return false; +} + +bool Gu::sweepSphereSphere(const PxVec3& center0, PxReal radius0, const PxVec3& center1, PxReal radius1, const PxVec3& motion, PxReal& d, PxVec3& nrm) +{ + const PxVec3 movedCenter = center1 + motion; + + PxReal tmp; + if(!sphereSphereSweep(radius0, center0, center0, radius1, center1, movedCenter, d, tmp)) + return false; + + // Compute normal + // PT: if spheres initially overlap, the convention is that returned normal = -sweep direction + if(d==0.0f) + nrm = -motion; + else + nrm = (center1 + d * motion) - center0; + nrm.normalize(); + return true; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h new file mode 100644 index 000000000..1c80b0273 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SPHERE_SPHERE_H +#define GU_SWEEP_SPHERE_SPHERE_H + +#include "foundation/PxVec3.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Gu +{ + bool sweepSphereSphere(const PxVec3& center0, PxReal radius0, const PxVec3& center1, PxReal radius1, const PxVec3& motion, PxReal& d, PxVec3& nrm); + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp new file mode 100644 index 000000000..30728ac5a --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp @@ -0,0 +1,524 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuSweepSphereTriangle.h" +#include "GuIntersectionRaySphere.h" +#include "GuIntersectionRayCapsule.h" +#include "GuIntersectionRayTriangle.h" +#include "GuCapsule.h" +#include "GuInternal.h" +#include "PsUtilities.h" +#include "GuDistancePointTriangle.h" + +using namespace physx; +using namespace Gu; + +// PT: using GU_CULLING_EPSILON_RAY_TRIANGLE fails here, in capsule-vs-mesh's triangle extrusion, when +// the sweep dir is almost the same as the capsule's dir (i.e. when we usually fallback to the sphere codepath). +// I suspect det becomes so small that we lose all accuracy when dividing by det and using the result in computing +// impact distance. +#define LOCAL_EPSILON 0.00001f + +// PT: special version computing (u,v) even when the ray misses the tri. Version working on precomputed edges. +static PX_FORCE_INLINE PxU32 rayTriSpecial(const PxVec3& orig, const PxVec3& dir, const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, PxReal& t, PxReal& u, PxReal& v) +{ + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const PxReal det = edge1.dot(pvec); + + // the non-culling branch +// if(det>-GU_CULLING_EPSILON_RAY_TRIANGLE && det-LOCAL_EPSILON && det1.0f) + return 1; + if(v<0.0f || u+v>1.0f) + return 1; + + // Calculate t, ray intersects triangle + t = (edge2.dot(qvec)) * oneOverDet; + + return 2; +} + +// Returns true if sphere can be tested against triangle vertex, false if edge test should be performed +// +// Uses a conservative approach to work for "sliver triangles" (long & thin) as well. +static PX_FORCE_INLINE bool edgeOrVertexTest(const PxVec3& planeIntersectPoint, const PxVec3* PX_RESTRICT tri, PxU32 vertIntersectCandidate, PxU32 vert0, PxU32 vert1, PxU32& secondEdgeVert) +{ + { + const PxVec3 edge0 = tri[vertIntersectCandidate] - tri[vert0]; + const PxReal edge0LengthSqr = edge0.dot(edge0); + + const PxVec3 diff = planeIntersectPoint - tri[vert0]; + + if (edge0.dot(diff) < edge0LengthSqr) // If the squared edge length is used for comparison, the edge vector does not need to be normalized + { + secondEdgeVert = vert0; + return false; + } + } + + { + const PxVec3 edge1 = tri[vertIntersectCandidate] - tri[vert1]; + const PxReal edge1LengthSqr = edge1.dot(edge1); + + const PxVec3 diff = planeIntersectPoint - tri[vert1]; + + if (edge1.dot(diff) < edge1LengthSqr) + { + secondEdgeVert = vert1; + return false; + } + } + return true; +} + +static PX_FORCE_INLINE bool testRayVsSphereOrCapsule(PxReal& impactDistance, bool testSphere, const PxVec3& center, PxReal radius, const PxVec3& dir, const PxVec3* PX_RESTRICT verts, PxU32 e0, PxU32 e1) +{ + if(testSphere) + { + PxReal t; + if(intersectRaySphere(center, dir, PX_MAX_F32, verts[e0], radius, t)) + { + impactDistance = t; + return true; + } + } + else + { + PxReal t; + if(intersectRayCapsule(center, dir, verts[e0], verts[e1], radius, t)) + { + if(t>=0.0f/* && t= 0.0f) + R = -R; + + // The first point of the sphere to hit the triangle plane is the point of the sphere nearest to + // the triangle plane. Hence, we use center - (normal*radius) below. + + // PT: casting against the extruded triangle in direction R is the same as casting from a ray moved by -R + PxReal t; + const PxU32 r = rayTriSpecial(center-R, dir, triVerts[0], edge10, edge20, t, u, v); + if(!r) + return false; + if(r==2) + { + if(t<0.0f) + return false; + impactDistance = t; + directHit = true; + return true; + } + } + + // + // Let's do some art! + // + // The triangle gets divided into the following areas (based on the barycentric coordinates (u,v)): + // + // \ A0 / + // \ / + // \ / + // \/ 0 + // A02 * A01 + // u / / \ \ v + // * / \ * + // / \ . + // 2 / \ 1 + // ------*--------------*------- + // / \ . + // A2 / A12 \ A1 + // + // + // Based on the area where the computed triangle plane intersection point lies in, a different sweep test will be applied. + // + // A) A01, A02, A12 : Test sphere against the corresponding edge + // B) A0, A1, A2 : Test sphere against the corresponding vertex + // + // Unfortunately, B) does not work for long, thin triangles. Hence there is some extra code which does a conservative check and + // switches to edge tests if necessary. + // + + bool TestSphere; + PxU32 e0,e1; + if(u<0.0f) + { + if(v<0.0f) + { + // 0 or 0-1 or 0-2 + e0 = 0; + const PxVec3 intersectPoint = INTERSECT_POINT; + TestSphere = edgeOrVertexTest(intersectPoint, triVerts, 0, 1, 2, e1); + } + else if(u+v>1.0f) + { + // 2 or 2-0 or 2-1 + e0 = 2; + const PxVec3 intersectPoint = INTERSECT_POINT; + TestSphere = edgeOrVertexTest(intersectPoint, triVerts, 2, 0, 1, e1); + } + else + { + // 0-2 + TestSphere = false; + e0 = 0; + e1 = 2; + } + } + else + { + if(v<0.0f) + { + if(u+v>1.0f) + { + // 1 or 1-0 or 1-2 + e0 = 1; + const PxVec3 intersectPoint = INTERSECT_POINT; + TestSphere = edgeOrVertexTest(intersectPoint, triVerts, 1, 0, 2, e1); + } + else + { + // 0-1 + TestSphere = false; + e0 = 0; + e1 = 1; + } + } + else + { + PX_ASSERT(u+v>=1.0f); // Else hit triangle + // 1-2 + TestSphere = false; + e0 = 1; + e1 = 2; + } + } + return testRayVsSphereOrCapsule(impactDistance, TestSphere, center, radius, dir, triVerts, e0, e1); +} + +bool Gu::sweepSphereTriangles( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const PxVec3& center, const PxReal radius, // Sphere data + const PxVec3& unitDir, PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& h, PxVec3& triNormalOut, // Results + bool isDoubleSided, bool meshBothSides, bool anyHit, bool testInitialOverlap) // Query modifiers +{ + if(!nbTris) + return false; + + const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; + + PxU32 index = PX_INVALID_U32; + const PxU32 initIndex = getInitIndex(cachedIndex, nbTris); + + PxReal curT = distance; + const PxReal dpc0 = center.dot(unitDir); + + PxReal bestAlignmentValue = 2.0f; + + PxVec3 bestTriNormal(0.0f); + + for(PxU32 ii=0; ii 0.0f)) + continue; + + const PxReal magnitude = triNormal.magnitude(); + if(magnitude==0.0f) + continue; + + triNormal /= magnitude; + + PxReal currentDistance; + bool unused; + if(!sweepSphereVSTri(currentTri.verts, triNormal, center, radius, unitDir, currentDistance, unused, testInitialOverlap)) + continue; + + const PxReal distEpsilon = GU_EPSILON_SAME_DISTANCE; // pick a farther hit within distEpsilon that is more opposing than the previous closest hit + const PxReal hitDot = computeAlignmentValue(triNormal, unitDir); + if(!keepTriangle(currentDistance, hitDot, curT, bestAlignmentValue, distance, distEpsilon)) + continue; + + if(currentDistance==0.0f) + { + triNormalOut = -unitDir; + return setInitialOverlapResults(h, unitDir, i); + } + + curT = currentDistance; + index = i; + bestAlignmentValue = hitDot; + bestTriNormal = triNormal; + if(anyHit) + break; + } + return computeSphereTriangleImpactData(h, triNormalOut, index, curT, center, unitDir, bestTriNormal, triangles, isDoubleSided, meshBothSides); +} + +static PX_FORCE_INLINE PxU32 rayQuadSpecial2(const PxVec3& orig, const PxVec3& dir, const PxVec3& vert0, const PxVec3& edge1, const PxVec3& edge2, float& t, float& u, float& v) +{ + // Begin calculating determinant - also used to calculate U parameter + const PxVec3 pvec = dir.cross(edge2); + + // If determinant is near zero, ray lies in plane of triangle + const float det = edge1.dot(pvec); + + // the non-culling branch + if(det>-LOCAL_EPSILON && det1.0f) + return 1; + if(v<0.0f || v>1.0f) + return 1; + + // Calculate t, ray intersects triangle + t = edge2.dot(qvec) * OneOverDet; + + return 2; +} + +bool Gu::sweepSphereVSQuad(const PxVec3* PX_RESTRICT quadVerts, const PxVec3& normal, const PxVec3& center, float radius, const PxVec3& dir, float& impactDistance) +{ + // Quad formed by 2 tris: + // p0 p1 p2 + // p2 p1 p3 = p3 p2 p1 + // + // p0___p2 + // | /| + // | / | + // | / | + // |/ | + // p1---p3 + // + // Edge10 = p1 - p0 + // Edge20 = p2 - p0 + // Impact point = Edge10*u + Edge20*v + p0 + // => u is along Y, between 0.0 (p0;p2) and 1.0 (p1;p3) + // => v is along X, between 0.0 (p0;p1) and 1.0 (p2;p3) + // + // For the second triangle, + // Edge10b = p2 - p3 = -Edge10 + // Edge20b = p1 - p3 = -Edge20 + + const PxVec3 Edge10 = quadVerts[1] - quadVerts[0]; + const PxVec3 Edge20 = quadVerts[2] - quadVerts[0]; + + if(1) // ### brute force version that always works, but we can probably do better + { + const float r2 = radius*radius; + { + const PxVec3 Cp = closestPtPointTriangle2(center, quadVerts[0], quadVerts[1], quadVerts[2], Edge10, Edge20); + if((Cp - center).magnitudeSquared() <= r2) + { + impactDistance = 0.0f; + return true; + } + } + { + const PxVec3 Cp = closestPtPointTriangle2(center, quadVerts[3], quadVerts[2], quadVerts[1], -Edge10, -Edge20); + if((Cp - center).magnitudeSquared() <= r2) + { + impactDistance = 0.0f; + return true; + } + } + } + + float u,v; + if(1) + { + PxVec3 R = normal * radius; + if(dir.dot(R) >= 0.0f) + R = -R; + + // The first point of the sphere to hit the quad plane is the point of the sphere nearest to + // the quad plane. Hence, we use center - (normal*radius) below. + + // PT: casting against the extruded quad in direction R is the same as casting from a ray moved by -R + float t; + PxU32 r = rayQuadSpecial2(center-R, dir, quadVerts[0], Edge10, Edge20, t, u, v); + if(!r) + return false; + if(r==2) + { + if(t<0.0f) + return false; + impactDistance = t; + return true; + } + } + + #define INTERSECT_POINT_Q (quadVerts[1]*u) + (quadVerts[2]*v) + (quadVerts[0] * (1.0f-u-v)) + + Ps::swap(u,v); + bool TestSphere; + PxU32 e0,e1; + if(u<0.0f) + { + if(v<0.0f) + { + // 0 or 0-1 or 0-2 + e0 = 0; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 0, 1, 2, e1); + } + else if(v>1.0f) + { + // 1 or 1-0 or 1-3 + e0 = 1; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 1, 0, 3, e1); + } + else + { + // 0-1 + TestSphere = false; + e0 = 0; + e1 = 1; + } + } + else if(u>1.0f) + { + if(v<0.0f) + { + // 2 or 2-0 or 2-3 + e0 = 2; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 2, 0, 3, e1); + } + else if(v>1.0f) + { + // 3 or 3-1 or 3-2 + e0 = 3; + const PxVec3 intersectPoint = INTERSECT_POINT_Q; + TestSphere = edgeOrVertexTest(intersectPoint, quadVerts, 3, 1, 2, e1); + } + else + { + // 2-3 + TestSphere = false; + e0 = 2; + e1 = 3; + } + } + else + { + if(v<0.0f) + { + // 0-2 + TestSphere = false; + e0 = 0; + e1 = 2; + } + else + { + PX_ASSERT(v>=1.0f); // Else hit quad + // 1-3 + TestSphere = false; + e0 = 1; + e1 = 3; + } + } + return testRayVsSphereOrCapsule(impactDistance, TestSphere, center, radius, dir, quadVerts, e0, e1); +} + diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h new file mode 100644 index 000000000..ecfe6ab00 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h @@ -0,0 +1,156 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_SPHERE_TRIANGLE_H +#define GU_SWEEP_SPHERE_TRIANGLE_H + +#include "GuSweepTriangleUtils.h" + +namespace physx +{ + +namespace Gu +{ + /** + Sweeps a sphere against a triangle. + + All input parameters (sphere, triangle, sweep direction) must be in the same space. Sweep length is assumed to be infinite. + + By default, 'testInitialOverlap' must be set to true to properly handle the case where the sphere already overlaps the triangle + at the start of the sweep. In such a case, returned impact distance is exactly 0.0f. If it is known ahead of time that the sphere + cannot overlap the triangle at t=0.0, then 'testInitialOverlap' can be set to false to skip the initial overlap test and make the + function run faster. + + If the ray defined by the sphere's center and the unit direction directly intersects the triangle-related part of the TSS (*) (i.e. + the prism from the Minkowski sum of the inflated triangle) then 'directHit' is set to true. Otherwise it is set to false. + + (*) For Triangle Swept Sphere, see http://gamma.cs.unc.edu/SSV/ssv.pdf for the origin of these names. + + \param triVerts [in] triangle vertices + \param triUnitNormal [in] triangle's normalized normal + \param sphereCenter [in] sphere's center + \param sphereRadius [in] sphere's radius + \param unitDir [in] normalized sweep direction. + \param impactDistance [out] impact distance, if a hit has been found. Does not need to be initialized before calling the function. + \param directHit [out] true if a direct hit has been found, see comments above. + \param testInitialOverlap [in] true if an initial sphere-vs-triangle overlap test must be performed, see comments above. + + \return true if an impact has been found (in which case returned result values are valid) + */ + bool sweepSphereVSTri( const PxVec3* PX_RESTRICT triVerts, const PxVec3& triUnitNormal,// Triangle data + const PxVec3& sphereCenter, PxReal sphereRadius, // Sphere data + const PxVec3& unitDir, // Ray data + PxReal& impactDistance, bool& directHit, // Results + bool testInitialOverlap); // Query modifier + + /** + Sweeps a sphere against a quad. + + All input parameters (sphere, quad, sweep direction) must be in the same space. Sweep length is assumed to be infinite. + + Quad must be formed by 2 tris like this: + + p0___p2 + | /| + | / | + | / | + |/ | + p1---p3 + + \param quadVerts [in] quad vertices + \param quadUnitNormal [in] quad's normalized normal + \param sphereCenter [in] sphere's center + \param sphereRadius [in] sphere's radius + \param unitDir [in] normalized sweep direction. + \param impactDistance [out] impact distance, if a hit has been found. Does not need to be initialized before calling the function. + + \return true if an impact has been found (in which case returned result values are valid) + */ + bool sweepSphereVSQuad( const PxVec3* PX_RESTRICT quadVerts, const PxVec3& quadUnitNormal, // Quad data + const PxVec3& sphereCenter, float sphereRadius, // Sphere data + const PxVec3& unitDir, // Ray data + float& impactDistance); // Results + + + // PT: computes proper impact data for sphere-sweep-vs-tri, after the closest tri has been found + PX_FORCE_INLINE bool computeSphereTriangleImpactData(PxSweepHit& h, PxVec3& triNormalOut, PxU32 index, PxReal curT, + const PxVec3& center, const PxVec3& unitDir, const PxVec3& bestTriNormal, + const PxTriangle* PX_RESTRICT triangles, + bool isDoubleSided, bool meshBothSides) + { + if(index==PX_INVALID_U32) + return false; // We didn't touch any triangle + + // Compute impact data only once, using best triangle + PxVec3 hitPos, normal; + computeSphereTriImpactData(hitPos, normal, center, unitDir, curT, triangles[index]); + + // PT: by design, returned normal is opposed to the sweep direction. + if(shouldFlipNormal(normal, meshBothSides, isDoubleSided, bestTriNormal, unitDir)) + normal = -normal; + + h.position = hitPos; + h.normal = normal; + h.distance = curT; + h.faceIndex = index; + h.flags = PxHitFlag::eNORMAL|PxHitFlag::ePOSITION; + triNormalOut = bestTriNormal; + return true; + } + + /** + Sweeps a sphere against a set of triangles. + + \param nbTris [in] number of triangles in input array + \param triangles [in] array of input triangles + \param center [in] sphere's center + \param radius [in] sphere's radius + \param unitDir [in] sweep's unit direcion + \param distance [in] sweep's length + \param cachedIndex [in] cached triangle index, or NULL. Cached triangle will be tested first. + \param hit [out] results + \param triNormalOut [out] triangle normal + \param isDoubleSided [in] true if input triangles are double-sided + \param meshBothSides [in] true if PxHitFlag::eMESH_BOTH_SIDES is used + \param anyHit [in] true if PxHitFlag::eMESH_ANY is used + \param testInitialOverlap [in] true if PxHitFlag::eASSUME_NO_INITIAL_OVERLAP is not used + \return true if an impact has been found + */ + bool sweepSphereTriangles( PxU32 nbTris, const PxTriangle* PX_RESTRICT triangles, // Triangle data + const PxVec3& center, const PxReal radius, // Sphere data + const PxVec3& unitDir, PxReal distance, // Ray data + const PxU32* PX_RESTRICT cachedIndex, // Cache data + PxSweepHit& hit, PxVec3& triNormalOut, // Results + bool isDoubleSided, bool meshBothSides, bool anyHit, bool testInitialOverlap); // Query modifiers + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp new file mode 100644 index 000000000..3960e0025 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp @@ -0,0 +1,223 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxBounds3.h" +#include "GuSweepTriangleUtils.h" +#include "GuDistancePointTriangle.h" +#include "GuVecTriangle.h" +#include "GuVecBox.h" +#include "GuSweepBoxTriangle_FeatureBased.h" +#include "GuInternal.h" +#include "GuGJK.h" + +using namespace physx; +using namespace Gu; +using namespace physx::shdfnd::aos; + +#define GU_SAFE_DISTANCE_FOR_NORMAL_COMPUTATION 0.1f + +void Gu::computeSphereTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& center, const PxVec3& dir, float t, const PxTriangle& tri) +{ + const PxVec3 newSphereCenter = center + dir*t; + + // We need the impact point, not computed by the new code + PxReal u_unused, v_unused; + const PxVec3 localHit = closestPtPointTriangle(newSphereCenter, tri.verts[0], tri.verts[1], tri.verts[2], u_unused, v_unused); + PX_UNUSED(u_unused); + PX_UNUSED(v_unused); + + // This is responsible for the cap-vs-box stuck while jumping. However it's needed to slide on box corners! + // PT: this one is also dubious since the sphere/capsule center can be far away from the hit point when the radius is big! + PxVec3 localNormal = newSphereCenter - localHit; + const PxReal m = localNormal.normalize(); + if(m<1e-3f) + tri.normal(localNormal); + + hit = localHit; + normal = localNormal; +} + +// PT: not inlining this rarely-run function makes the benchmark ~500.000 cycles faster... +// PT: using this version all the time makes the benchmark ~300.000 cycles slower. So we just use it as a backup. +static bool runBackupProcedure(PxVec3& hit, PxVec3& normal, const PxVec3& localMotion, const PxVec3& boxExtents, const PxTriangle& triInBoxSpace) +{ + const Vec3V v0 = V3LoadU(triInBoxSpace.verts[0]); + const Vec3V v1 = V3LoadU(triInBoxSpace.verts[1]); + const Vec3V v2 = V3LoadU(triInBoxSpace.verts[2]); + + const TriangleV triangleV(v0, v1, v2); + + // PT: the box is in the triangle's space already + //BoxV boxV(V3LoadU(PxVec3(0.0f)), V3LoadU(boxExtents), + // V3LoadU(PxVec3(1.0f, 0.0f, 0.0f)), V3LoadU(PxVec3(0.0f, 1.0f, 0.0f)), V3LoadU(PxVec3(0.0f, 0.0f, 1.0f))); + + const BoxV boxV(V3Zero(), V3LoadU(boxExtents)); + + Vec3V closestA; + Vec3V closestB; + Vec3V normalV; + FloatV distV; + LocalConvex convexA(triangleV); + LocalConvex convexB(boxV); + const Vec3V initialSearchDir = V3Sub(triangleV.getCenter(), boxV.getCenter()); + const FloatV contactDist = FMax(); + GjkStatus status_ = gjk, LocalConvex >(convexA, convexB, initialSearchDir, contactDist, closestA, closestB, normalV, distV); + + if(status_==GJK_CONTACT) + return false; + + PxVec3 ml_closestB; + PxVec3 ml_normal; + V3StoreU(closestB, ml_closestB); + V3StoreU(normalV, ml_normal); + + hit = ml_closestB + localMotion; +// normal = -ml_normal; + if((ml_normal.dot(localMotion))>0.0f) + ml_normal = -ml_normal; + normal = ml_normal; + return true; +} + +void Gu::computeBoxTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& boxExtents, const PxVec3& localDir, const PxTriangle& triInBoxSpace, PxReal impactDist) +{ + // PT: the triangle is in "box space", i.e. the box can be seen as an AABB centered around the origin. + + // PT: compute impact point/normal in a second pass. Here we simply re-sweep the box against the best triangle, + // using the feature-based code (which computes impact point and normal). This is not great because: + // - we know there's an impact so why do all tests again? + // - the SAT test & the feature-based tests could return different results because of FPU accuracy. + // The backup procedure makes sure we compute a proper answer even when the SAT and feature-based versions differ. + const PxBounds3 aabb(-boxExtents, boxExtents); + + const PxVec3 oneOverDir( + localDir.x!=0.0f ? 1.0f/localDir.x : 0.0f, + localDir.y!=0.0f ? 1.0f/localDir.y : 0.0f, + localDir.z!=0.0f ? 1.0f/localDir.z : 0.0f); + + // PT: TODO: this is the only place left using sweepBoxTriangle() + // Backface culling could be removed here since we know we want a hit no matter what. Plus, it's sometimes + // incorrectly culled and we hit the backup procedure for no reason. On Win32Modern for unknown reasons + // returned normal is sometimes (0,0,0). In these cases we also switch to the backup procedure. + float t = PX_MAX_F32; // PT: no need to initialize with best dist here since we want a hit no matter what + if(!sweepBoxTriangle(triInBoxSpace, aabb, localDir, oneOverDir, hit, normal, t) || normal.isZero()) + { + // PT: move triangle close to box + const PxVec3 localMotion = localDir*impactDist; + + const PxVec3 delta = localMotion - localDir*GU_SAFE_DISTANCE_FOR_NORMAL_COMPUTATION; + const PxTriangle movedTriangle( + triInBoxSpace.verts[0] - delta, + triInBoxSpace.verts[1] - delta, + triInBoxSpace.verts[2] - delta); + + if(!runBackupProcedure(hit, normal, localMotion, boxExtents, movedTriangle)) + { + // PT: if the backup procedure fails, we give up + hit = PxVec3(0.0f); + normal = -localDir; + } + } +} + +// PT: copy where we know that input vectors are not zero +static PX_FORCE_INLINE void edgeEdgeDistNoZeroVector( PxVec3& x, PxVec3& y, // closest points + const PxVec3& p, const PxVec3& a, // seg 1 origin, vector + const PxVec3& q, const PxVec3& b) // seg 2 origin, vector +{ + const PxVec3 T = q - p; + const PxReal ADotA = a.dot(a); + const PxReal BDotB = b.dot(b); + PX_ASSERT(ADotA!=0.0f); + PX_ASSERT(BDotB!=0.0f); + const PxReal ADotB = a.dot(b); + const PxReal ADotT = a.dot(T); + const PxReal BDotT = b.dot(T); + + // t parameterizes ray (p, a) + // u parameterizes ray (q, b) + + // Compute t for the closest point on ray (p, a) to ray (q, b) + const PxReal Denom = ADotA*BDotB - ADotB*ADotB; + + PxReal t; + if(Denom!=0.0f) + { + t = (ADotT*BDotB - BDotT*ADotB) / Denom; + + // Clamp result so t is on the segment (p, a) + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else + { + t = 0.0f; + } + + // find u for point on ray (q, b) closest to point at t + PxReal u; + { + u = (t*ADotB - BDotT) / BDotB; + + // if u is on segment (q, b), t and u correspond to closest points, otherwise, clamp u, recompute and clamp t + if(u<0.0f) + { + u = 0.0f; + t = ADotT / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + else if(u > 1.0f) + { + u = 1.0f; + t = (ADotB + ADotT) / ADotA; + + if(t<0.0f) t = 0.0f; + else if(t>1.0f) t = 1.0f; + } + } + + x = p + a * t; + y = q + b * u; +} + +void Gu::computeEdgeEdgeNormal(PxVec3& normal, const PxVec3& p1, const PxVec3& p2_p1, const PxVec3& p3, const PxVec3& p4_p3, const PxVec3& dir, float d) +{ + // PT: cross-product doesn't produce nice normals so we use an edge-edge distance function itself + + // PT: move the edges "0.1" units from each other before the computation. If the edges are too far + // away, computed normal tend to align itself with the swept direction. If the edges are too close, + // closest points x and y become identical and we can't compute a proper normal. + const PxVec3 p1s = p1 + dir*(d-GU_SAFE_DISTANCE_FOR_NORMAL_COMPUTATION); + + PxVec3 x, y; + edgeEdgeDistNoZeroVector(x, y, p1s, p2_p1, p3, p4_p3); + normal = x - y; +} diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h new file mode 100644 index 000000000..323529351 --- /dev/null +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h @@ -0,0 +1,338 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef GU_SWEEP_TRIANGLE_UTILS_H +#define GU_SWEEP_TRIANGLE_UTILS_H + +#include "CmPhysXCommon.h" +#include "GuSweepSharedTests.h" +#include "GuInternal.h" +#include "PxTriangle.h" +#include "PxQueryReport.h" + +namespace physx +{ + +namespace Gu +{ + // PT: computes proper impact data for sphere-sweep-vs-tri, after the closest tri has been found. + void computeSphereTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& center, const PxVec3& dir, float t, const PxTriangle& tri); + + // PT: computes proper impact data for box-sweep-vs-tri, after the closest tri has been found. + void computeBoxTriImpactData(PxVec3& hit, PxVec3& normal, const PxVec3& boxExtents, const PxVec3& localDir, const PxTriangle& triInBoxSpace, PxReal impactDist); + + // PT: computes impact normal between two edges. Produces better normals than just the EE cross product. + // This version properly computes the closest points between two colliding edges and makes a normal from these. + void computeEdgeEdgeNormal(PxVec3& normal, const PxVec3& p1, const PxVec3& p2_p1, const PxVec3& p3, const PxVec3& p4_p3, const PxVec3& dir, float d); + + // PT: small function just to avoid duplicating the code. + // Returns index of first triangle we should process (when processing arrays of input triangles) + PX_FORCE_INLINE PxU32 getInitIndex(const PxU32* PX_RESTRICT cachedIndex, PxU32 nbTris) + { + PxU32 initIndex = 0; // PT: by default the first triangle to process is just the first one in the array + if(cachedIndex) // PT: but if we cached the last closest triangle from a previous call... + { + PX_ASSERT(*cachedIndex < nbTris); + PX_UNUSED(nbTris); + initIndex = *cachedIndex; // PT: ...then we should start with that one, to potentially shrink the ray as early as possible + } + return initIndex; + } + + // PT: quick triangle rejection for sphere-based sweeps. + // Please refer to %SDKRoot%\InternalDocumentation\GU\cullTriangle.png for details & diagram. + PX_FORCE_INLINE bool cullTriangle(const PxVec3* PX_RESTRICT triVerts, const PxVec3& dir, PxReal radius, PxReal t, const PxReal dpc0) + { + // PT: project triangle on axis + const PxReal dp0 = triVerts[0].dot(dir); + const PxReal dp1 = triVerts[1].dot(dir); + const PxReal dp2 = triVerts[2].dot(dir); + + // PT: keep min value = earliest possible impact distance + PxReal dp = dp0; + dp = physx::intrinsics::selectMin(dp, dp1); + dp = physx::intrinsics::selectMin(dp, dp2); + + // PT: make sure we keep triangles that are about as close as best current distance + radius += 0.001f + GU_EPSILON_SAME_DISTANCE; + + // PT: if earliest possible impact distance for this triangle is already larger than + // sphere's current best known impact distance, we can skip the triangle + if(dp>dpc0 + t + radius) + { + //PX_ASSERT(resx == 0.0f); + return false; + } + + // PT: if triangle is fully located before the sphere's initial position, skip it too + const PxReal dpc1 = dpc0 - radius; + if(dp0dpc0 + t + radius) + return false; + + // PT: if quad is fully located before the sphere's initial position, skip it too + const float dpc1 = dpc0 - radius; + if(dp0 + // t (t<=fT) t (t>fT) + // return (A)^2 return (B)^2 + // + // |<-------------->| + // fT + // + PX_FORCE_INLINE PxReal squareDistance(const PxVec3& p0, const PxVec3& dir, PxReal t, const PxVec3& point) + { + PxVec3 diff = point - p0; + PxReal fT = diff.dot(dir); + fT = physx::intrinsics::selectMax(fT, 0.0f); + fT = physx::intrinsics::selectMin(fT, t); + diff -= fT*dir; + return diff.magnitudeSquared(); + } + + // PT: quick triangle culling for sphere-based sweeps + // Please refer to %SDKRoot%\InternalDocumentation\GU\coarseCulling.png for details & diagram. + PX_FORCE_INLINE bool coarseCullingTri(const PxVec3& center, const PxVec3& dir, PxReal t, PxReal radius, const PxVec3* PX_RESTRICT triVerts) + { + // PT: compute center of triangle ### could be precomputed? + const PxVec3 triCenter = (triVerts[0] + triVerts[1] + triVerts[2]) * (1.0f/3.0f); + + // PT: distance between the triangle center and the swept path (an LSS) + // Same as: distancePointSegmentSquared(center, center+dir*t, TriCenter); + PxReal d = PxSqrt(squareDistance(center, dir, t, triCenter)) - radius - 0.0001f; + + if (d < 0.0f) // The triangle center lies inside the swept sphere + return true; + + d*=d; + + // PT: coarse capsule-vs-triangle overlap test ### distances could be precomputed? + if(1) + { + if(d <= (triCenter-triVerts[0]).magnitudeSquared()) + return true; + if(d <= (triCenter-triVerts[1]).magnitudeSquared()) + return true; + if(d <= (triCenter-triVerts[2]).magnitudeSquared()) + return true; + } + else + { + const float d0 = (triCenter-triVerts[0]).magnitudeSquared(); + const float d1 = (triCenter-triVerts[1]).magnitudeSquared(); + const float d2 = (triCenter-triVerts[2]).magnitudeSquared(); + float triRadius = physx::intrinsics::selectMax(d0, d1); + triRadius = physx::intrinsics::selectMax(triRadius, d2); + if(d <= triRadius) + return true; + } + return false; + } + + // PT: quick quad culling for sphere-based sweeps. Same as for triangle, adapted for one more vertex. + PX_FORCE_INLINE bool coarseCullingQuad(const PxVec3& center, const PxVec3& dir, PxReal t, PxReal radius, const PxVec3* PX_RESTRICT quadVerts) + { + // PT: compute center of quad ### could be precomputed? + const PxVec3 quadCenter = (quadVerts[0] + quadVerts[1] + quadVerts[2] + quadVerts[3]) * (1.0f/4.0f); + + // PT: distance between the quad center and the swept path (an LSS) + PxReal d = PxSqrt(squareDistance(center, dir, t, quadCenter)) - radius - 0.0001f; + + if (d < 0.0f) // The quad center lies inside the swept sphere + return true; + + d*=d; + + // PT: coarse capsule-vs-quad overlap test ### distances could be precomputed? + if(1) + { + if(d <= (quadCenter-quadVerts[0]).magnitudeSquared()) + return true; + if(d <= (quadCenter-quadVerts[1]).magnitudeSquared()) + return true; + if(d <= (quadCenter-quadVerts[2]).magnitudeSquared()) + return true; + if(d <= (quadCenter-quadVerts[3]).magnitudeSquared()) + return true; + } + return false; + } + + // PT: combined triangle culling for sphere-based sweeps + PX_FORCE_INLINE bool rejectTriangle(const PxVec3& center, const PxVec3& unitDir, PxReal curT, PxReal radius, const PxVec3* PX_RESTRICT triVerts, const PxReal dpc0) + { + if(!coarseCullingTri(center, unitDir, curT, radius, triVerts)) + return true; + if(!cullTriangle(triVerts, unitDir, radius, curT, dpc0)) + return true; + return false; + } + + // PT: combined quad culling for sphere-based sweeps + PX_FORCE_INLINE bool rejectQuad(const PxVec3& center, const PxVec3& unitDir, PxReal curT, PxReal radius, const PxVec3* PX_RESTRICT quadVerts, const PxReal dpc0) + { + if(!coarseCullingQuad(center, unitDir, curT, radius, quadVerts)) + return true; + if(!cullQuad(quadVerts, unitDir, radius, curT, dpc0)) + return true; + return false; + } + + PX_FORCE_INLINE bool shouldFlipNormal(const PxVec3& normal, bool meshBothSides, bool isDoubleSided, const PxVec3& triangleNormal, const PxVec3& dir) + { + // PT: this function assumes that input normal is opposed to the ray/sweep direction. This is always + // what we want except when we hit a single-sided back face with 'meshBothSides' enabled. + + if(!meshBothSides || isDoubleSided) + return false; + + PX_ASSERT(normal.dot(dir) <= 0.0f); // PT: if this fails, the logic below cannot be applied + PX_UNUSED(normal); + return triangleNormal.dot(dir) > 0.0f; // PT: true for back-facing hits + } + + PX_FORCE_INLINE bool shouldFlipNormal(const PxVec3& normal, bool meshBothSides, bool isDoubleSided, const PxTriangle& triangle, const PxVec3& dir, const PxTransform* pose) + { + // PT: this function assumes that input normal is opposed to the ray/sweep direction. This is always + // what we want except when we hit a single-sided back face with 'meshBothSides' enabled. + + if(!meshBothSides || isDoubleSided) + return false; + + PX_ASSERT(normal.dot(dir) <= 0.0f); // PT: if this fails, the logic below cannot be applied + PX_UNUSED(normal); + + PxVec3 triangleNormal; + triangle.denormalizedNormal(triangleNormal); + + if(pose) + triangleNormal = pose->rotate(triangleNormal); + + return triangleNormal.dot(dir) > 0.0f; // PT: true for back-facing hits + } + + // PT: implements the spec for IO sweeps in a single place (to ensure consistency) + PX_FORCE_INLINE bool setInitialOverlapResults(PxSweepHit& hit, const PxVec3& unitDir, PxU32 faceIndex) + { + // PT: please write these fields in the order they are listed in the struct. + hit.faceIndex = faceIndex; + hit.flags = PxHitFlag::eNORMAL|PxHitFlag::eFACE_INDEX; + hit.normal = -unitDir; + hit.distance = 0.0f; + return true; // PT: true indicates a hit, saves some lines in calling code + } + + PX_FORCE_INLINE void computeBoxLocalImpact( PxVec3& pos, PxVec3& normal, PxHitFlags& outFlags, + const Box& box, const PxVec3& localDir, const PxTriangle& triInBoxSpace, + const PxHitFlags inFlags, bool isDoubleSided, bool meshBothSides, PxReal impactDist) + { + if(inFlags & (PxHitFlag::eNORMAL|PxHitFlag::ePOSITION)) + { + PxVec3 localPos, localNormal; + computeBoxTriImpactData(localPos, localNormal, box.extents, localDir, triInBoxSpace, impactDist); + + if(inFlags & PxHitFlag::eNORMAL) + { + localNormal.normalize(); + + // PT: doing this after the 'rotate' minimizes errors when normal and dir are close to perpendicular + // ....but we must do it before the rotate now, because triangleNormal is in box space (and thus we + // need the normal with the proper orientation, in box space. We can't fix it after it's been rotated + // to box space. + // Technically this one is only here because of the EE cross product in the feature-based sweep. + // PT: TODO: revisit corresponding code in computeImpactData, get rid of ambiguity + // PT: TODO: this may not be needed anymore + if((localNormal.dot(localDir))>0.0f) + localNormal = -localNormal; + + // PT: this one is to ensure the normal respects the mesh-both-sides/double-sided convention + if(shouldFlipNormal(localNormal, meshBothSides, isDoubleSided, triInBoxSpace, localDir, NULL)) + localNormal = -localNormal; + + normal = box.rotate(localNormal); + outFlags |= PxHitFlag::eNORMAL; + } + + if(inFlags & PxHitFlag::ePOSITION) + { + pos = box.transform(localPos); + outFlags |= PxHitFlag::ePOSITION; + } + } + } + +} // namespace Gu + +} + +#endif diff --git a/src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp b/src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp new file mode 100644 index 000000000..e0215b60b --- /dev/null +++ b/src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp @@ -0,0 +1,769 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxImmediateMode.h" +#include "CmUtils.h" +#include "PsMathUtils.h" +#include "../../lowleveldynamics/src/DyBodyCoreIntegrator.h" +#include "../../lowleveldynamics/src/DySolverBody.h" +#include "../../lowleveldynamics/src/DyContactPrep.h" +#include "../../lowleveldynamics/src/DyCorrelationBuffer.h" +#include "../../lowleveldynamics/src/DyConstraintPrep.h" +#include "../../lowleveldynamics/src/DySolverControl.h" +#include "../../lowleveldynamics/src/DySolverContext.h" +#include "PxGeometry.h" +#include "GuGeometryUnion.h" +#include "GuContactMethodImpl.h" +#include "../../lowlevel/common/include/collision/PxcContactMethodImpl.h" +#include "GuPersistentContactManifold.h" +#include "NpConstraint.h" + +namespace physx +{ + + namespace + { + +#define MAX_NUM_PARTITIONS 32u + + static PxU32 bitTable[32] = + { + 1u << 0, 1u << 1, 1u << 2, 1u << 3, 1u << 4, 1u << 5, 1u << 6, 1u << 7, 1u << 8, 1u << 9, 1u << 10, 1u << 11, 1u << 12, 1u << 13, 1u << 14, 1u << 15, 1u << 16, 1u << 17, + 1u << 18, 1u << 19, 1u << 20, 1u << 21, 1u << 22, 1u << 23, 1u << 24, 1u << 25, 1u << 26, 1u << 27, 1u << 28, 1u << 29, 1u << 30, 1u << 31 + }; + + PxU32 getBit(const PxU32 index) + { + PX_ASSERT(index < 32); + return bitTable[index]; + } + + + class RigidBodyClassification + { + PxSolverBody* PX_RESTRICT mBodies; + PxU32 mNumBodies; + + public: + RigidBodyClassification(PxSolverBody* PX_RESTRICT bodies, PxU32 numBodies) : mBodies(bodies), mNumBodies(numBodies) + { + } + + //Returns true if it is a dynamic-dynamic constriant; false if it is a dynamic-static or dynamic-kinematic constraint + PX_FORCE_INLINE bool classifyConstraint(const PxSolverConstraintDesc& desc, uintptr_t& indexA, uintptr_t& indexB, bool& activeA, bool& activeB) const + { + indexA = uintptr_t(desc.bodyA - mBodies); + indexB = uintptr_t(desc.bodyB - mBodies); + activeA = indexA < mNumBodies; + activeB = indexB < mNumBodies; + return activeA && activeB; + } + + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(PxU32* numConstraintsPerPartition) + { + for (PxU32 a = 0; a < mNumBodies; ++a) + { + mBodies[a].solverProgress = 0; + + for (PxU32 b = 0; b < mBodies[a].maxSolverFrictionProgress; ++b) + { + PxU32 partId = PxMin(PxU32(mBodies[a].maxSolverNormalProgress + b), MAX_NUM_PARTITIONS); + numConstraintsPerPartition[partId]++; + } + } + } + }; + + template + void classifyConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + PxU32* numConstraintsPerPartition) + { + const PxSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxMemZero(numConstraintsPerPartition, sizeof(PxU32) * 33); + + for (PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB); + + if (notContainsStatic) + { + PxU32 partitionsA = _desc->bodyA->solverProgress; + PxU32 partitionsB = _desc->bodyB->solverProgress; + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + //Write to overflow partition... + numConstraintsPerPartition[availablePartition]++; + _desc->bodyA->maxSolverNormalProgress = MAX_NUM_PARTITIONS; + _desc->bodyB->maxSolverNormalProgress = MAX_NUM_PARTITIONS; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + partitionsA |= partitionBit; + partitionsB |= partitionBit; + } + + _desc->bodyA->solverProgress = partitionsA; + _desc->bodyB->solverProgress = partitionsB; + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + _desc->bodyA->maxSolverNormalProgress = PxMax(_desc->bodyA->maxSolverNormalProgress, PxU16(availablePartition)); + _desc->bodyB->maxSolverNormalProgress = PxMax(_desc->bodyB->maxSolverNormalProgress, PxU16(availablePartition)); + } + else + { + //Just count the number of static constraints and store in maxSolverFrictionProgress... + if (activeA) + _desc->bodyA->maxSolverFrictionProgress++; + else if (activeB) + _desc->bodyB->maxSolverFrictionProgress++; + } + } + + classification.reserveSpaceForStaticConstraints(numConstraintsPerPartition); + + } + + template + void writeConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + PxU32* accumulatedConstraintsPerPartition, PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDesc) + { + const PxSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + for (PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB); + + if (notContainsStatic) + { + PxU32 partitionsA = _desc->bodyA->solverProgress; + PxU32 partitionsB = _desc->bodyB->solverProgress; + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + + partitionsA |= partitionBit; + partitionsB |= partitionBit; + } + + _desc->bodyA->solverProgress = partitionsA; + _desc->bodyB->solverProgress = partitionsB; + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + } + else + { + //Just count the number of static constraints and store in maxSolverFrictionProgress... + PxU32 index = 0; + if (activeA) + index = PxMin(PxU32(_desc->bodyA->maxSolverNormalProgress + _desc->bodyA->maxSolverFrictionProgress++), MAX_NUM_PARTITIONS); + else if (activeB) + index = PxMin(PxU32(_desc->bodyB->maxSolverNormalProgress + _desc->bodyB->maxSolverFrictionProgress++), MAX_NUM_PARTITIONS); + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[index]++] = *_desc; + } + } + } + + } + + + void immediate::PxConstructSolverBodies(const PxRigidBodyData* inRigidData, PxSolverBodyData* outSolverBodyData, const PxU32 nbBodies, const PxVec3& gravity, const PxReal dt) + { + for (PxU32 a = 0; a < nbBodies; ++a) + { + const PxRigidBodyData& rigidData = inRigidData[a]; + PxVec3 lv = rigidData.linearVelocity, av = rigidData.angularVelocity; + Dy::bodyCoreComputeUnconstrainedVelocity(gravity, dt, rigidData.linearDamping, rigidData.angularDamping, 1.f, rigidData.maxLinearVelocitySq, rigidData.maxAngularVelocitySq, lv, av, false); + Dy::copyToSolverBodyData(lv, av, rigidData.invMass, rigidData.invInertia, rigidData.body2World, -rigidData.maxDepenetrationVelocity, rigidData.maxContactImpulse, IG_INVALID_NODE, PX_MAX_F32, outSolverBodyData[a], 0); + } + } + + void immediate::PxConstructStaticSolverBody(const PxTransform& globalPose, PxSolverBodyData& solverBodyData) + { + PxVec3 zero(0.f); + Dy::copyToSolverBodyData(zero, zero, 0.f, zero, globalPose, -PX_MAX_F32, PX_MAX_F32, IG_INVALID_NODE, PX_MAX_F32, solverBodyData, 0); + } + + + void immediate::PxIntegrateSolverBodies(PxSolverBodyData* solverBodyData, PxSolverBody* solverBody, const PxVec3* linearMotionVelocity, const PxVec3* angularMotionState, const PxU32 nbBodiesToIntegrate, PxReal dt) + { + for (PxU32 i = 0; i < nbBodiesToIntegrate; ++i) + { + PxVec3 lmv = linearMotionVelocity[i]; + PxVec3 amv = angularMotionState[i]; + Dy::integrateCore(lmv, amv, solverBody[i], solverBodyData[i], dt); + } + } + + + PxU32 immediate::PxBatchConstraints(PxSolverConstraintDesc* solverConstraintDescs, const PxU32 nbConstraints, PxSolverBody* solverBodies, PxU32 numBodies, PxConstraintBatchHeader* outBatchHeaders, + PxSolverConstraintDesc* outOrderedConstraintDescs) + { + PxU32 constraintsPerPartition[MAX_NUM_PARTITIONS + 1]; + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = solverBodies[a]; + body.solverProgress = 0; + //We re-use maxSolverFrictionProgress and maxSolverNormalProgress to record the + //maximum partition used by dynamic constraints and the number of static constraints affecting + //a body. We use this to make partitioning much cheaper and be able to support + body.maxSolverFrictionProgress = 0; + body.maxSolverNormalProgress = 0; + } + + { + RigidBodyClassification classification(solverBodies, numBodies); + classifyConstraintDesc(solverConstraintDescs, nbConstraints, classification, constraintsPerPartition); + + PxU32 accumulation = 0; + for (PxU32 a = 0; a < MAX_NUM_PARTITIONS+1; ++a) + { + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; + } + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = solverBodies[a]; + body.solverProgress = 0; + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... + body.maxSolverFrictionProgress = 0; + } + + writeConstraintDesc(solverConstraintDescs, nbConstraints, classification, constraintsPerPartition, outOrderedConstraintDescs); + + PxU32 numHeaders = 0; + PxU32 currentPartition = 0; + PxU32 maxJ = nbConstraints == 0 ? 0 : constraintsPerPartition[0]; + + const PxU32 maxBatchPartition = MAX_NUM_PARTITIONS; + for (PxU32 a = 0; a < nbConstraints;) + { + PxConstraintBatchHeader& header = outBatchHeaders[numHeaders++]; + header.mStartIndex = a; + + PxU32 loopMax = PxMin(maxJ - a, 4u); + PxU16 j = 0; + if (loopMax > 0) + { + j = 1; + PxSolverConstraintDesc& desc = outOrderedConstraintDescs[a]; + if (currentPartition < maxBatchPartition) + { + for (; j < loopMax && desc.constraintLengthOver16 == outOrderedConstraintDescs[a + j].constraintLengthOver16; ++j); + } + header.mStride = j; + header.mConstraintType = desc.constraintLengthOver16; + } + if (maxJ == (a + j) && maxJ != nbConstraints) + { + currentPartition++; + maxJ = constraintsPerPartition[currentPartition]; + } + a += j; + } + return numHeaders; + } + } + + + bool immediate::PxCreateContactConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverContactDesc* contactDescs, + PxConstraintAllocator& allocator, PxReal invDt, PxReal bounceThreshold, PxReal frictionOffsetThreshold, PxReal correlationDistance) + { + PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); + PX_ASSERT(bounceThreshold < 0.f); + PX_ASSERT(frictionOffsetThreshold > 0.f); + PX_ASSERT(correlationDistance > 0.f); + + Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; + + Dy::CorrelationBuffer cb; + + PxU32 currentContactDescIdx = 0; + + for (PxU32 i = 0; i < nbHeaders; ++i) + { + PxConstraintBatchHeader& batchHeader = batchHeaders[i]; + if (batchHeader.mStride == 4) + { + PxU32 totalContacts = contactDescs[currentContactDescIdx].numContacts + contactDescs[currentContactDescIdx + 1].numContacts + + contactDescs[currentContactDescIdx + 2].numContacts + contactDescs[currentContactDescIdx + 3].numContacts; + + if (totalContacts <= 64) + { + state = Dy::createFinalizeSolverContacts4(cb, + contactDescs + currentContactDescIdx, + invDt, + bounceThreshold, + frictionOffsetThreshold, + correlationDistance, + 0.f, + allocator); + } + } + + if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) + { + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + Dy::createFinalizeSolverContacts(contactDescs[currentContactDescIdx + a], cb, invDt, bounceThreshold, + frictionOffsetThreshold, correlationDistance, 0.f, allocator, NULL); + } + } + PxU8 type = *contactDescs[currentContactDescIdx].desc->constraint; + + if (type == DY_SC_TYPE_STATIC_CONTACT) + { + //Check if any block of constraints is classified as type static (single) contact constraint. + //If they are, iterate over all constraints grouped with it and switch to "dynamic" contact constraint + //type if there's a dynamic contact constraint in the group. + for (PxU32 c = 1; c < batchHeader.mStride; ++c) + { + if (*contactDescs[currentContactDescIdx + c].desc->constraint == DY_SC_TYPE_RB_CONTACT) + { + type = DY_SC_TYPE_RB_CONTACT; + break; + } + } + } + + batchHeader.mConstraintType = type; + + currentContactDescIdx += batchHeader.mStride; + } + + + + + + return true; + } + + + bool immediate::PxCreateJointConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, PxReal dt, PxReal invDt) + { + PX_ASSERT(dt > 0.f); + PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); + + Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; + + PxU32 currentDescIdx = 0; + for (PxU32 i = 0; i < nbHeaders; ++i) + { + PxConstraintBatchHeader& batchHeader = batchHeaders[i]; + + PxU8 type = DY_SC_TYPE_BLOCK_1D; + if (batchHeader.mStride == 4) + { + PxU32 totalRows = 0; + PxU32 maxRows = 0; + bool batchable = true; + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + if (jointDescs[currentDescIdx + a].numRows == 0) + { + batchable = false; + break; + } + totalRows += jointDescs[currentDescIdx + a].numRows; + maxRows = PxMax(maxRows, jointDescs[currentDescIdx + a].numRows); + } + + if (batchable) + { + state = Dy::setupSolverConstraint4 + (jointDescs + currentDescIdx, + dt, invDt, totalRows, + allocator, maxRows); + } + } + + if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) + { + type = DY_SC_TYPE_RB_1D; + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + Dy::ConstraintHelper::setupSolverConstraint(jointDescs[currentDescIdx + a], + allocator, dt, invDt, NULL); + } + } + + batchHeader.mConstraintType = type; + currentDescIdx += batchHeader.mStride; + } + + return true; + } + + bool immediate::PxCreateJointConstraintsWithShaders(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxConstraint** constraints, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, + PxReal dt, PxReal invDt) + { + Px1DConstraint allRows[Dy::MAX_CONSTRAINT_ROWS * 4]; + + //Runs shaders to fill in rows... + + PxU32 currentDescIdx = 0; + + for (PxU32 i = 0; i < nbHeaders; ++i) + { + + PxU32 numRows = 0; + PxU32 preppedIndex = 0; + PxU32 maxRows = 0; + + PxConstraintBatchHeader& batchHeader = batchHeaders[i]; + + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + Px1DConstraint* rows = allRows + numRows; + PxSolverConstraintPrepDesc& desc = jointDescs[currentDescIdx + a]; + + NpConstraint* npConstraint = static_cast(constraints[currentDescIdx + a]); + + npConstraint->updateConstants(); + + Sc::ConstraintCore& core = npConstraint->getScbConstraint().getScConstraint(); + + PxConstraintSolverPrep prep = core.getPxConnector()->getPrep(); + const void* constantBlock = core.getPxConnector()->getConstantBlock(); + PX_ASSERT(prep); + + PxMemZero(rows + preppedIndex, sizeof(Px1DConstraint)*(Dy::MAX_CONSTRAINT_ROWS)); + for (PxU32 b = preppedIndex; b < Dy::MAX_CONSTRAINT_ROWS; ++b) + { + Px1DConstraint& c = rows[b]; + //Px1DConstraintInit(c); + c.minImpulse = -PX_MAX_REAL; + c.maxImpulse = PX_MAX_REAL; + } + + desc.mInvMassScales.linear0 = desc.mInvMassScales.linear1 = desc.mInvMassScales.angular0 = desc.mInvMassScales.angular1 = 1.f; + + desc.body0WorldOffset = PxVec3(0.f); + + PxVec3 cA2w, cB2w; + PxU32 constraintCount = prep(rows, + desc.body0WorldOffset, + Dy::MAX_CONSTRAINT_ROWS, + desc.mInvMassScales, + constantBlock, + desc.bodyFrame0, desc.bodyFrame1, + !!(core.getFlags() & PxConstraintFlag::eENABLE_EXTENDED_LIMITS), + cA2w, cB2w); + + preppedIndex = Dy::MAX_CONSTRAINT_ROWS - constraintCount; + + maxRows = PxMax(constraintCount, maxRows); + + desc.rows = rows; + desc.numRows = constraintCount; + numRows += constraintCount; + } + + PxCreateJointConstraints(&batchHeader, 1, jointDescs + currentDescIdx, allocator, dt, invDt); + + currentDescIdx += batchHeader.mStride; + } + + return true; //KS - TODO - do some error reporting/management... + } + + PX_FORCE_INLINE bool PxIsZero(PxSolverBody* bodies, PxU32 nbBodies) + { + for (PxU32 i = 0; i < nbBodies; ++i) + { + if (!bodies[i].linearVelocity.isZero() || + !bodies[i].angularState.isZero()) + return false; + } + return true; + } + + + void immediate::PxSolveConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbBatchHeaders, PxSolverConstraintDesc* solverConstraintDescs, PxSolverBody* solverBodies, + PxVec3* linearMotionVelocity, PxVec3* angularMotionVelocity, const PxU32 nbSolverBodies, const PxU32 nbPositionIterations, const PxU32 nbVelocityIterations) + { + PX_ASSERT(nbPositionIterations > 0); + PX_ASSERT(nbVelocityIterations > 0); + PX_ASSERT(PxIsZero(solverBodies, nbSolverBodies)); //Ensure that solver body velocities have been zeroed before solving + + //Stage 1: solve the position iterations... + Dy::SolveBlockMethod* solveTable = Dy::getSolveBlockTable(); + + Dy::SolveBlockMethod* solveConcludeTable = Dy::getSolverConcludeBlockTable(); + + Dy::SolveWriteBackBlockMethod* solveWritebackTable = Dy::getSolveWritebackBlockTable(); + + Dy::SolverContext cache; + cache.mThresholdStreamIndex = 0; + cache.mThresholdStreamLength = 0xFFFFFFF; + + PX_ASSERT(nbPositionIterations > 0); + PX_ASSERT(nbVelocityIterations > 0); + + for (PxU32 i = nbPositionIterations; i > 1; --i) + { + cache.doFriction = i <= 3; + for (PxU32 a = 0; a < nbBatchHeaders; ++a) + { + PxConstraintBatchHeader& batch = batchHeaders[a]; + solveTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + } + } + + cache.doFriction = true; + for (PxU32 a = 0; a < nbBatchHeaders; ++a) + { + PxConstraintBatchHeader& batch = batchHeaders[a]; + solveConcludeTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + } + + //Save motion velocities... + + for (PxU32 a = 0; a < nbSolverBodies; ++a) + { + linearMotionVelocity[a] = solverBodies[a].linearVelocity; + angularMotionVelocity[a] = solverBodies[a].angularState; + } + + for (PxU32 i = nbVelocityIterations; i > 1; --i) + { + for (PxU32 a = 0; a < nbBatchHeaders; ++a) + { + PxConstraintBatchHeader& batch = batchHeaders[a]; + solveTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + } + } + + for (PxU32 a = 0; a < nbBatchHeaders; ++a) + { + PxConstraintBatchHeader& batch = batchHeaders[a]; + solveWritebackTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + } + + } + + + void createCache(Gu::Cache& cache, PxGeometryType::Enum geomType0, PxGeometryType::Enum geomType1, PxCacheAllocator& allocator) + { + + if (gEnablePCMCaching[geomType0][geomType1]) + { + if (geomType0 <= PxGeometryType::eCONVEXMESH && + geomType1 <= PxGeometryType::eCONVEXMESH) + { + if (geomType0 == PxGeometryType::eSPHERE || geomType1 == PxGeometryType::eSPHERE) + { + Gu::PersistentContactManifold* manifold = PX_PLACEMENT_NEW(allocator.allocateCacheData(sizeof(Gu::SpherePersistentContactManifold)), Gu::SpherePersistentContactManifold)(); + cache.setManifold(manifold); + } + else + { + Gu::PersistentContactManifold* manifold = PX_PLACEMENT_NEW(allocator.allocateCacheData(sizeof(Gu::LargePersistentContactManifold)), Gu::LargePersistentContactManifold)(); + cache.setManifold(manifold); + + } + cache.getManifold().clearManifold(); + } + else + { + //ML: raised 1 to indicate the manifold is multiManifold which is for contact gen in mesh/height field + //cache.manifold = 1; + cache.setMultiManifold(NULL); + } + } + else + { + //cache.manifold = 0; + cache.mCachedData = NULL; + cache.mManifoldFlags = 0; + } + } + + + + bool immediate::PxGenerateContacts(const PxGeometry* const * geom0, const PxGeometry* const * geom1, const PxTransform* pose0, const PxTransform* pose1, PxCache* contactCache, const PxU32 nbPairs, PxContactRecorder& contactRecorder, + const PxReal contactDistance, const PxReal meshContactMargin, const PxReal toleranceLength, PxCacheAllocator& allocator) + { + PX_ASSERT(meshContactMargin > 0.f); + PX_ASSERT(toleranceLength > 0.f); + PX_ASSERT(contactDistance > 0.f); + Gu::ContactBuffer contactBuffer; + + for (PxU32 i = 0; i < nbPairs; ++i) + { + + contactBuffer.count = 0; + PxGeometryType::Enum type0 = geom0[i]->getType(); + PxGeometryType::Enum type1 = geom1[i]->getType(); + + const PxGeometry* tempGeom0 = geom0[i]; + const PxGeometry* tempGeom1 = geom1[i]; + + PX_ALIGN(16, PxTransform) transform0 = pose0[i]; + PX_ALIGN(16, PxTransform) transform1 = pose1[i]; + + const bool bSwap = type0 > type1; + if (bSwap) + { + const PxGeometry* temp = tempGeom0; + tempGeom0 = geom1[i]; + tempGeom1 = temp; + PxGeometryType::Enum tempType = type0; + type0 = type1; + type1 = tempType; + transform1 = pose0[i]; + transform0 = pose1[i]; + } + + const Gu::GeometryUnion geomUnion0(*tempGeom0); + const Gu::GeometryUnion geomUnion1(*tempGeom1); + + //Now work out which type of PCM we need... + + Gu::Cache& cache = static_cast(contactCache[i]); + + bool needsMultiManifold = type1 > PxGeometryType::eCONVEXMESH; + + Gu::NarrowPhaseParams params(contactDistance, meshContactMargin, toleranceLength); + + if (needsMultiManifold) + { + Gu::MultiplePersistentContactManifold multiManifold; + + if (cache.isMultiManifold()) + { + multiManifold.fromBuffer(reinterpret_cast(&cache.getMultipleManifold())); + } + else + { + multiManifold.initialize(); + } + cache.setMultiManifold(&multiManifold); + + //Do collision detection, then write manifold out... + g_PCMContactMethodTable[type0][type1](geomUnion0, geomUnion1, transform0, transform1, params, cache, contactBuffer, NULL); + + const PxU32 size = (sizeof(Gu::MultiPersistentManifoldHeader) + + multiManifold.mNumManifolds * sizeof(Gu::SingleManifoldHeader) + + multiManifold.mNumTotalContacts * sizeof(Gu::CachedMeshPersistentContact)); + + PxU8* buffer = allocator.allocateCacheData(size); + + multiManifold.toBuffer(buffer); + + cache.setMultiManifold(buffer); + + } + else + { + //Allocate the type of manifold we need again... + Gu::PersistentContactManifold* oldManifold = NULL; + + if (cache.isManifold()) + oldManifold = &cache.getManifold(); + + //Allocates and creates the PCM... + createCache(cache, type0, type1, allocator); + + //Copy PCM from old to new manifold... + if (oldManifold) + { + Gu::PersistentContactManifold& manifold = cache.getManifold(); + manifold.mRelativeTransform = oldManifold->mRelativeTransform; + manifold.mNumContacts = oldManifold->mNumContacts; + manifold.mNumWarmStartPoints = oldManifold->mNumWarmStartPoints; + manifold.mAIndice[0] = oldManifold->mAIndice[0]; manifold.mAIndice[1] = oldManifold->mAIndice[1]; + manifold.mAIndice[2] = oldManifold->mAIndice[2]; manifold.mAIndice[3] = oldManifold->mAIndice[3]; + manifold.mBIndice[0] = oldManifold->mBIndice[0]; manifold.mBIndice[1] = oldManifold->mBIndice[1]; + manifold.mBIndice[2] = oldManifold->mBIndice[2]; manifold.mBIndice[3] = oldManifold->mBIndice[3]; + PxMemCopy(manifold.mContactPoints, oldManifold->mContactPoints, sizeof(Gu::PersistentContact)*manifold.mNumContacts); + } + + g_PCMContactMethodTable[type0][type1](geomUnion0, geomUnion1, transform0, transform1, params, cache, contactBuffer, NULL); + } + + if (bSwap) + { + for (PxU32 a = 0; a < contactBuffer.count; ++a) + { + contactBuffer.contacts[a].normal = -contactBuffer.contacts[a].normal; + } + } + + + if (contactBuffer.count != 0) + { + //Record this contact pair... + contactRecorder.recordContacts(contactBuffer.contacts, contactBuffer.count, i); + } + } + return true; + } + +} + diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h new file mode 100644 index 000000000..9178d0578 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h @@ -0,0 +1,167 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_MATERIAL_H +#define PXS_MATERIAL_H + +#include "foundation/PxVec3.h" +#include "PxMaterial.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PxMetaData.h" +#include "PsUtilities.h" + +namespace physx +{ + +#define MATERIAL_INVALID_HANDLE 0xffffffff + + //PX_ALIGN_PREFIX(16) struct PxsMaterialData + //{ + // PxReal dynamicFriction; + // PxReal staticFriction; + // PxReal restitution; + // PxReal dynamicFrictionV; + // PxReal staticFrictionV; + // PxVec3 dirOfAnisotropy;//might need to get rid of this + // PxCombineMode::Enum frictionCombineMode; + // PxCombineMode::Enum restitutionCombineMode; + + // PxMaterialFlags flags; + // PxU8 paddingFromFlags[2]; + // PxU32 pads; + + // PxsMaterialData() + // : dynamicFriction(0.0) + // , staticFriction(0.0f) + // , restitution(0.0f) + // , dynamicFrictionV(0.0f) + // , staticFrictionV(0.0f) + // , dirOfAnisotropy(1,0,0) + // , frictionCombineMode(PxCombineMode::eAVERAGE) + // , restitutionCombineMode(PxCombineMode::eAVERAGE) + // {} + + // PX_FORCE_INLINE PxCombineMode::Enum getFrictionCombineMode() const + // { + // return frictionCombineMode; + // } + + // + // PX_FORCE_INLINE PxCombineMode::Enum getRestitutionCombineMode() const + // { + // return restitutionCombineMode; + // } + + // PX_FORCE_INLINE void setFrictionCombineMode(PxCombineMode::Enum frictionFlags) + // { + // frictionCombineMode = frictionFlags; + // } + + // PX_FORCE_INLINE void setRestitutionCombineMode(PxCombineMode::Enum restitutionFlags) + // { + // restitutionCombineMode = restitutionFlags; + // } + + //}PX_ALIGN_SUFFIX(16); + + PX_ALIGN_PREFIX(16) struct PxsMaterialData + { + PxReal dynamicFriction; //4 + PxReal staticFriction; //8 + PxReal restitution; //12 + PxMaterialFlags flags; //14 + PxU8 fricRestCombineMode; //15 + PxU8 padding; //16 + + PxsMaterialData() + : dynamicFriction(0.0f) + , staticFriction(0.0f) + , restitution(0.0f) + , fricRestCombineMode((PxCombineMode::eAVERAGE << 4) | PxCombineMode::eAVERAGE) + , padding(0) + {} + + PxsMaterialData(const PxEMPTY) {} + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxCombineMode::Enum getFrictionCombineMode() const + { + return PxCombineMode::Enum(fricRestCombineMode >> 4); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxCombineMode::Enum getRestitutionCombineMode() const + { + return PxCombineMode::Enum(fricRestCombineMode & 0xf); + } + + PX_FORCE_INLINE void setFrictionCombineMode(PxCombineMode::Enum frictionFlags) + { + fricRestCombineMode = Ps::to8((fricRestCombineMode & 0xf) | (frictionFlags << 4)); + } + + PX_FORCE_INLINE void setRestitutionCombineMode(PxCombineMode::Enum restitutionFlags) + { + fricRestCombineMode = Ps::to8((fricRestCombineMode & 0xf0) | (restitutionFlags)); + } + + }PX_ALIGN_SUFFIX(16); + + + class PxsMaterialCore : public PxsMaterialData, public Ps::UserAllocated + { + public: + + PxsMaterialCore(const PxsMaterialData& desc): PxsMaterialData(desc), mNxMaterial(0), mMaterialIndex(MATERIAL_INVALID_HANDLE) + { + } + + PxsMaterialCore(): mNxMaterial(0), mMaterialIndex(MATERIAL_INVALID_HANDLE) + { + } + + PxsMaterialCore(const PxEMPTY) : PxsMaterialData(PxEmpty) {} + + ~PxsMaterialCore() + { + } + + PX_FORCE_INLINE void setNxMaterial(PxMaterial* m) { mNxMaterial = m; } + PX_FORCE_INLINE PxMaterial* getNxMaterial() const { return mNxMaterial; } + PX_FORCE_INLINE void setMaterialIndex(const PxU32 materialIndex) { mMaterialIndex = materialIndex; } + PX_FORCE_INLINE PxU32 getMaterialIndex() const { return mMaterialIndex; } + + protected: + PxMaterial* mNxMaterial; + PxU32 mMaterialIndex; //handle assign by the handle manager + }; + +} //namespace phyxs + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h new file mode 100644 index 000000000..d81ca4eb6 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_MATERIALMANAGER +#define PXS_MATERIALMANAGER + +#include "PxsMaterialCore.h" +#include "PsAlignedMalloc.h" + +namespace physx +{ + struct PxsMaterialInfo + { + PxU16 mMaterialIndex0; + PxU16 mMaterialIndex1; + }; + + class PxsMaterialManager + { + public: + PxsMaterialManager() + { + const PxU32 matCount = 128; + materials = reinterpret_cast(physx::shdfnd::AlignedAllocator<16>().allocate(sizeof(PxsMaterialCore)*matCount, __FILE__, __LINE__)); + maxMaterials = matCount; + for(PxU32 i=0; i().deallocate(materials); + } + + void setMaterial(PxsMaterialCore* mat) + { + const PxU32 materialIndex = mat->getMaterialIndex(); + resize(materialIndex+1); + materials[materialIndex] = *mat; + } + + void updateMaterial(PxsMaterialCore* mat) + { + materials[mat->getMaterialIndex()] =*mat; + } + + void removeMaterial(PxsMaterialCore* mat) + { + mat->setMaterialIndex(MATERIAL_INVALID_HANDLE); + } + + PX_FORCE_INLINE PxsMaterialCore* getMaterial(const PxU32 index)const + { + PX_ASSERT(index < maxMaterials); + return &materials[index]; + } + + PxU32 getMaxSize()const + { + return maxMaterials; + } + + void resize(PxU32 minValueForMax) + { + if(maxMaterials>=minValueForMax) + return; + + const PxU32 numMaterials = maxMaterials; + + maxMaterials = (minValueForMax+31)&~31; + PxsMaterialCore* mat = reinterpret_cast(physx::shdfnd::AlignedAllocator<16>().allocate(sizeof(PxsMaterialCore)*maxMaterials, __FILE__, __LINE__)); + for(PxU32 i=0; i().deallocate(materials); + + materials = mat; + } + + PxsMaterialCore* materials;//make sure materials's start address is 16 bytes align + PxU32 maxMaterials; + PxU32 mPad[2]; + }; + + class PxsMaterialManagerIterator + { + + public: + PxsMaterialManagerIterator(PxsMaterialManager& manager) : mManager(manager), mIndex(0) + { + } + + bool getNextMaterial(PxsMaterialCore*& materialCore) + { + const PxU32 maxSize = mManager.getMaxSize(); + PxU32 index = mIndex; + while(index < maxSize && mManager.getMaterial(index)->getMaterialIndex() == MATERIAL_INVALID_HANDLE) + index++; + materialCore = NULL; + if(index < maxSize) + materialCore = mManager.getMaterial(index++); + mIndex = index; + return materialCore!=NULL; + } + + private: + PxsMaterialManagerIterator& operator=(const PxsMaterialManagerIterator&); + PxsMaterialManager& mManager; + PxU32 mIndex; + }; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h b/src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h new file mode 100644 index 000000000..524e26a8f --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_CONFIG_H +#define PXD_CONFIG_H + +/*! \file internal top level include file for lowlevel. */ + +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" + +/************************************************************************/ +/* Compiler workarounds */ +/************************************************************************/ +#if PX_VC +#pragma warning(disable: 4355 ) // "this" used in base member initializer list +#pragma warning(disable: 4146 ) // unary minus operator applied to unsigned type. +#endif + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h b/src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h new file mode 100644 index 000000000..edaff42e3 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXV_DYNAMICS_H +#define PXV_DYNAMICS_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "foundation/PxSimpleTypes.h" +#include "PsIntrinsics.h" +#include "PxRigidDynamic.h" + +namespace physx +{ + +/*! +\file +Dynamics interface. +*/ + +/************************************************************************/ +/* Atoms */ +/************************************************************************/ + +class PxsContext; +class PxsRigidBody; +class PxShape; +class PxGeometry; +struct PxsShapeCore; + + +struct PxsRigidCore +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PxsRigidCore() : mFlags(0), mIdtBody2Actor(0), solverIterationCounts(0) {} + PxsRigidCore(const PxEMPTY) : mFlags(PxEmpty) {} + + PX_ALIGN_PREFIX(16) + PxTransform body2World PX_ALIGN_SUFFIX(16); + PxRigidBodyFlags mFlags; // API body flags + PxU8 mIdtBody2Actor; // PT: true if PxsBodyCore::body2Actor is identity + PxU16 solverIterationCounts; //vel iters are in low word and pos iters in high word. + + PX_FORCE_INLINE PxU32 isKinematic() const + { + return mFlags & PxRigidBodyFlag::eKINEMATIC; + } + + PX_FORCE_INLINE PxU32 hasCCD() const + { + return mFlags & PxRigidBodyFlag::eENABLE_CCD; + } + + PX_FORCE_INLINE PxU32 hasCCDFriction() const + { + return mFlags & PxRigidBodyFlag::eENABLE_CCD_FRICTION; + } +}; +PX_COMPILE_TIME_ASSERT(sizeof(PxsRigidCore) == 32); + + +struct PxsBodyCore: public PxsRigidCore +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + PxsBodyCore() : PxsRigidCore() {} + PxsBodyCore(const PxEMPTY) : PxsRigidCore(PxEmpty) {} + + PX_FORCE_INLINE const PxTransform& getBody2Actor() const { return body2Actor; } + PX_FORCE_INLINE void setBody2Actor(const PxTransform& t) + { + mIdtBody2Actor = PxU8(t.p.isZero() && t.q.isIdentity()); + + body2Actor = t; + } + protected: + PxTransform body2Actor; + public: + PxReal ccdAdvanceCoefficient; //64 + + PxVec3 linearVelocity; + PxReal maxPenBias; + + PxVec3 angularVelocity; + PxReal contactReportThreshold; //96 + + PxReal maxAngularVelocitySq; + PxReal maxLinearVelocitySq; + PxReal linearDamping; + PxReal angularDamping; //112 + + PxVec3 inverseInertia; + PxReal inverseMass; //128 + + PxReal maxContactImpulse; + PxReal sleepThreshold; + PxReal freezeThreshold; + PxReal wakeCounter; //144 this is authoritative wakeCounter + + PxReal solverWakeCounter; //this is calculated by the solver when it performs sleepCheck. It is committed to wakeCounter in ScAfterIntegrationTask if the body is still awake. + PxU32 numCountedInteractions; + PxU32 numBodyInteractions; //Used by adaptive force to keep track of the total number of body interactions + PxU16 isFastMoving; //This could be a single bit but it's a u16 at the moment for simplicity's sake + PxRigidDynamicLockFlags lockFlags; //160 This could be a u8 but it is a u16 for simplicity's sake. All fits into 16 byte alignment + + PX_FORCE_INLINE bool shouldCreateContactReports() const + { + const PxU32* binary = reinterpret_cast(&contactReportThreshold); + return *binary != 0x7f7fffff; // PX_MAX_REAL + } +}; + +PX_COMPILE_TIME_ASSERT(sizeof(PxsBodyCore) == 160); + + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h b/src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h new file mode 100644 index 000000000..aef2d999b --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXV_GEOMETRY_H +#define PXV_GEOMETRY_H + +#include "foundation/PxTransform.h" +#include "PxvConfig.h" + +namespace physx +{ + +class PxsRigidBody; + +namespace Gu +{ + struct ConvexHullData; + class TriangleMesh; + struct HeightFieldData; +} + +} + +/*! +\file +Geometry interface +*/ + +/************************************************************************/ +/* Shapes */ +/************************************************************************/ + +// moved to +#include "GuGeometryUnion.h" + +namespace physx +{ + +struct PxsShapeCore +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + +// PX_SERIALIZATION + PxsShapeCore() {} + PxsShapeCore(const PxEMPTY) : geometry(PxEmpty) {} +//~PX_SERIALIZATION + + PX_ALIGN_PREFIX(16) + PxTransform transform PX_ALIGN_SUFFIX(16); + PxReal contactOffset; + PxU8 mShapeFlags; // !< API shape flags // PT: TODO: use PxShapeFlags here. Needs to move flags to separate file. + PxU8 mOwnsMaterialIdxMemory; // PT: for de-serialization to avoid deallocating material index list. Moved there from Sc::ShapeCore (since one byte was free). + PxU16 materialIndex; + Gu::GeometryUnion geometry; +}; + +PX_COMPILE_TIME_ASSERT( (sizeof(PxsShapeCore)&0xf) == 0); + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h b/src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h new file mode 100644 index 000000000..de321befc --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h @@ -0,0 +1,116 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_INIT_H +#define PXD_INIT_H + +#include "PxvConfig.h" +#include "PsBasicTemplates.h" + +namespace physx +{ + +/*! +\file +PhysX Low-level, Memory management +*/ + +/************************************************************************/ +/* Error Handling */ +/************************************************************************/ + + +enum PxvErrorCode +{ + PXD_ERROR_NO_ERROR = 0, + PXD_ERROR_INVALID_PARAMETER, + PXD_ERROR_INVALID_PARAMETER_SIZE, + PXD_ERROR_INTERNAL_ERROR, + PXD_ERROR_NOT_IMPLEMENTED, + PXD_ERROR_NO_CONTEXT, + PXD_ERROR_NO_TASK_MANAGER, + PXD_ERROR_WARNING +}; + +class PxShape; +class PxRigidActor; +struct PxsShapeCore; +struct PxsRigidCore; + +struct PxvOffsetTable +{ + PX_FORCE_INLINE PxvOffsetTable() {} + + PX_FORCE_INLINE const PxShape* convertPxsShape2Px(const PxsShapeCore* pxs) const + { + return shdfnd::pointerOffset(pxs, pxsShapeCore2PxShape); + } + + PX_FORCE_INLINE const PxRigidActor* convertPxsRigidCore2PxRigidBody(const PxsRigidCore* pxs) const + { + return shdfnd::pointerOffset(pxs, pxsRigidCore2PxRigidBody); + } + + PX_FORCE_INLINE const PxRigidActor* convertPxsRigidCore2PxRigidStatic(const PxsRigidCore* pxs) const + { + return shdfnd::pointerOffset(pxs, pxsRigidCore2PxRigidStatic); + } + + ptrdiff_t pxsShapeCore2PxShape; + ptrdiff_t pxsRigidCore2PxRigidBody; + ptrdiff_t pxsRigidCore2PxRigidStatic; +}; +extern PxvOffsetTable gPxvOffsetTable; + +/*! +Initialize low-level implementation. +*/ + +void PxvInit(const PxvOffsetTable& offsetTable); + + +/*! +Shut down low-level implementation. +*/ +void PxvTerm(); + +/*! +Initialize low-level implementation. +*/ + +void PxvRegisterHeightFields(); + +#if PX_SUPPORT_GPU_PHYSX +class PxPhysXGpu* PxvGetPhysXGpu(bool createIfNeeded); +#endif + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvManager.h b/src/PhysX/physx/source/lowlevel/api/include/PxvManager.h new file mode 100644 index 000000000..685340ff9 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvManager.h @@ -0,0 +1,239 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXV_MANAGER_H +#define PXV_MANAGER_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMemory.h" +#include "PxvConfig.h" +#include "PxvGeometry.h" + +namespace physx +{ + +class PxvContact; + +/*! +\file +Manager interface +*/ + +/************************************************************************/ +/* Managers */ +/************************************************************************/ + +class PxsContactManager; +class PxsContext; + +struct PxsRigidCore; +struct PxsShapeCore; + + + +/*! +Type of PXD_MANAGER_CCD_MODE property +*/ +enum PxvContactManagerCCDMode +{ + PXD_MANAGER_CCD_NONE, + PXD_MANAGER_CCD_LINEAR +}; + + +/*! +Manager descriptor +*/ +struct PxvManagerDescRigidRigid +{ + /*! + Manager user data + + \sa PXD_MANAGER_USER_DATA + */ + //void* userData; + + /*! + Dominance setting for one way interactions. + A dominance of 0 means the corresp. body will + not be pushable by the other body in the constraint. + \sa PXD_MANAGER_DOMINANCE0 + */ + PxU8 dominance0; + + /*! + Dominance setting for one way interactions. + A dominance of 0 means the corresp. body will + not be pushable by the other body in the constraint. + \sa PXD_MANAGER_DOMINANCE1 + */ + PxU8 dominance1; + + /*! + PxsRigidBodies + */ + PxsRigidBody* rigidBody0; + PxsRigidBody* rigidBody1; + + /*! + Shape Core structures + */ + + const PxsShapeCore* shapeCore0; + const PxsShapeCore* shapeCore1; + + /*! + Body Core structures + */ + + PxsRigidCore* rigidCore0; + PxsRigidCore* rigidCore1; + + /*! + Enable contact information reporting. + + */ + int reportContactInfo; + + /*! + Enable contact impulse threshold reporting. + + */ + int hasForceThreshold; + + /*! + Enable generated contacts to be changeable + + */ + int contactChangeable; + + /*! + Disable strong friction + + */ + //int disableStrongFriction; + + /*! + Contact resolution rest distance. + + */ + PxReal restDistance; + + /*! + Disable contact response + + */ + int disableResponse; + + /*! + Disable discrete contact generation + + */ + int disableDiscreteContact; + + /*! + Disable CCD contact generation + + */ + int disableCCDContact; + + /*! + Is connected to an articulation (1 - first body, 2 - second body) + + */ + int hasArticulations; + + /*! + is connected to a dynamic (1 - first body, 2 - second body) + */ + int hasDynamics; + + /*! + Is the pair touching? Use when re-creating the manager with prior knowledge about touch status. + + positive: pair is touching + 0: touch state unknown (this is a new pair) + negative: pair is not touching + + Default is 0 + */ + int hasTouch; + + /*! + Identifies whether body 1 is kinematic. We can treat kinematics as statics and embed velocity into constraint + because kinematic bodies' velocities will not change + */ + bool body1Kinematic; + + /* + Index entries into the transform cache for shape 0 + */ + + PxU32 transformCache0; + + /* + Index entries into the transform cache for shape 1 + */ + + PxU32 transformCache1; + + + PxvManagerDescRigidRigid() + { + PxMemSet(this, 0, sizeof(PxvManagerDescRigidRigid)); + + dominance0 = 1u; + dominance1 = 1u; + } +}; + + +/*! +Report struct for contact manager touch reports +*/ +struct PxvContactManagerTouchEvent +{ + /*! + Manager handle + */ + PxsContactManager* manager; + + /*! + Manager userdata + */ + void* userData; +}; + +} + +#endif + diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h b/src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h new file mode 100644 index 000000000..530574b3a --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h @@ -0,0 +1,114 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXV_SIM_STATS_H +#define PXV_SIM_STATS_H + +#include "foundation/PxAssert.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "PxGeometry.h" + +namespace physx +{ + +/*! +\file +Context handling +*/ + +/************************************************************************/ +/* Context handling, types */ +/************************************************************************/ + +/*! +Description: contains statistics for the simulation. +*/ +struct PxvSimStats +{ + PxvSimStats() { clearAll(); } + void clearAll() { PxMemZero(this, sizeof(PxvSimStats)); } // set counters to zero + + PX_FORCE_INLINE void incCCDPairs(PxGeometryType::Enum g0, PxGeometryType::Enum g1) + { + PX_ASSERT(g0 <= g1); // That's how they should be sorted + mNbCCDPairs[g0][g1]++; + } + + PX_FORCE_INLINE void decCCDPairs(PxGeometryType::Enum g0, PxGeometryType::Enum g1) + { + PX_ASSERT(g0 <= g1); // That's how they should be sorted + PX_ASSERT(mNbCCDPairs[g0][g1]); + mNbCCDPairs[g0][g1]--; + } + + PX_FORCE_INLINE void incModifiedContactPairs(PxGeometryType::Enum g0, PxGeometryType::Enum g1) + { + PX_ASSERT(g0 <= g1); // That's how they should be sorted + mNbModifiedContactPairs[g0][g1]++; + } + + PX_FORCE_INLINE void decModifiedContactPairs(PxGeometryType::Enum g0, PxGeometryType::Enum g1) + { + PX_ASSERT(g0 <= g1); // That's how they should be sorted + PX_ASSERT(mNbModifiedContactPairs[g0][g1]); + mNbModifiedContactPairs[g0][g1]--; + } + + // PT: those guys are now persistent and shouldn't be cleared each frame + PxU32 mNbDiscreteContactPairs [PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 mNbCCDPairs [PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + + PxU32 mNbModifiedContactPairs [PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + + PxU32 mNbDiscreteContactPairsTotal; // PT: sum of mNbDiscreteContactPairs, i.e. number of pairs reaching narrow phase + PxU32 mNbDiscreteContactPairsWithCacheHits; + PxU32 mNbDiscreteContactPairsWithContacts; + PxU32 mNbActiveConstraints; + PxU32 mNbActiveDynamicBodies; + PxU32 mNbActiveKinematicBodies; + + PxU32 mNbAxisSolverConstraints; + PxU32 mTotalCompressedContactSize; + PxU32 mTotalConstraintSize; + PxU32 mPeakConstraintBlockAllocations; + + PxU32 mNbNewPairs; + PxU32 mNbLostPairs; + + PxU32 mNbNewTouches; + PxU32 mNbLostTouches; + + PxU32 mNbPartitions; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp b/src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp new file mode 100644 index 000000000..69603db69 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxvGlobals.h" +#include "PxsContext.h" +#include "PxcContactMethodImpl.h" +#include "GuContactMethodImpl.h" + + +#if PX_SUPPORT_GPU_PHYSX +#include "PxPhysXGpu.h" + +physx::PxPhysXGpu* gPxPhysXGpu; +#endif + +namespace physx +{ + +PxvOffsetTable gPxvOffsetTable; + +bool PxcPCMContactSphereHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcPCMContactCapsuleHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcPCMContactBoxHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcPCMContactConvexHeightField (GU_CONTACT_METHOD_ARGS); + +bool PxcContactSphereHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcContactCapsuleHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcContactBoxHeightField (GU_CONTACT_METHOD_ARGS); +bool PxcContactConvexHeightField (GU_CONTACT_METHOD_ARGS); + +void PxvRegisterHeightFields() +{ + g_ContactMethodTable[PxGeometryType::eSPHERE][PxGeometryType::eHEIGHTFIELD] = PxcContactSphereHeightField; + g_ContactMethodTable[PxGeometryType::eCAPSULE][PxGeometryType::eHEIGHTFIELD] = PxcContactCapsuleHeightField; + g_ContactMethodTable[PxGeometryType::eBOX][PxGeometryType::eHEIGHTFIELD] = PxcContactBoxHeightField; + g_ContactMethodTable[PxGeometryType::eCONVEXMESH][PxGeometryType::eHEIGHTFIELD] = PxcContactConvexHeightField; + + g_PCMContactMethodTable[PxGeometryType::eSPHERE][PxGeometryType::eHEIGHTFIELD] = PxcPCMContactSphereHeightField; + g_PCMContactMethodTable[PxGeometryType::eCAPSULE][PxGeometryType::eHEIGHTFIELD] = PxcPCMContactCapsuleHeightField; + g_PCMContactMethodTable[PxGeometryType::eBOX][PxGeometryType::eHEIGHTFIELD] = PxcPCMContactBoxHeightField; + g_PCMContactMethodTable[PxGeometryType::eCONVEXMESH][PxGeometryType::eHEIGHTFIELD] = PxcPCMContactConvexHeightField; +} + +void PxvInit(const PxvOffsetTable& offsetTable) +{ +#if PX_SUPPORT_GPU_PHYSX + gPxPhysXGpu = NULL; +#endif + gPxvOffsetTable = offsetTable; +} + +void PxvTerm() +{ +#if PX_SUPPORT_GPU_PHYSX + if (gPxPhysXGpu) + { + gPxPhysXGpu->release(); + gPxPhysXGpu = NULL; + } +#endif +} + +} + +#if PX_SUPPORT_GPU_PHYSX +namespace physx +{ + //forward declare stuff from PxPhysXGpuModuleLoader.cpp + void PxLoadPhysxGPUModule(const char* appGUID); + typedef physx::PxPhysXGpu* (PxCreatePhysXGpu_FUNC)(); + extern PxCreatePhysXGpu_FUNC* g_PxCreatePhysXGpu_Func; + + PxPhysXGpu* PxvGetPhysXGpu(bool createIfNeeded) + { + if (!gPxPhysXGpu && createIfNeeded) + { +#ifdef PX_PHYSX_GPU_STATIC + gPxPhysXGpu = PxCreatePhysXGpu(); +#else + PxLoadPhysxGPUModule(NULL); + if (g_PxCreatePhysXGpu_Func) + { + gPxPhysXGpu = g_PxCreatePhysXGpu_Func(); + } +#endif + } + + return gPxPhysXGpu; + } +} +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h b/src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h new file mode 100644 index 000000000..1ca5dccbd --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXC_CONTACTMETHODIMPL_H +#define PXC_CONTACTMETHODIMPL_H + +#include "GuGeometryUnion.h" +#include "CmPhysXCommon.h" +#include "GuContactMethodImpl.h" + +namespace physx +{ +namespace Cm +{ + class RenderOutput; +} + +namespace Gu +{ + class ContactBuffer; + struct Cache; + struct NarrowPhaseParams; +} + +struct PxcNpCache; +class PxcNpThreadContext; +class PxsContext; +class PxsRigidBody; +struct PxsCCDShape; + +namespace Cm +{ + class FastVertex2ShapeScaling; +} + +/*!\file +This file contains forward declarations of all implemented contact methods. +*/ + + +/*! Parameter list without names to avoid unused parameter warnings +*/ +#define CONTACT_METHOD_ARGS_UNUSED \ + const Gu::GeometryUnion&, \ + const Gu::GeometryUnion&, \ + const PxTransform&, \ + const PxTransform&, \ + const Gu::NarrowPhaseParams&, \ + Gu::Cache&, \ + Gu::ContactBuffer&, \ + Cm::RenderOutput* + + +/*! +Method prototype for contact generation routines +*/ +typedef bool (*PxcContactMethod) (GU_CONTACT_METHOD_ARGS); + + +// Matrix of types +extern PxcContactMethod g_ContactMethodTable[][PxGeometryType::eGEOMETRY_COUNT]; +extern const bool g_CanUseContactCache[][PxGeometryType::eGEOMETRY_COUNT]; +extern PxcContactMethod g_PCMContactMethodTable[][PxGeometryType::eGEOMETRY_COUNT]; + +extern bool gEnablePCMCaching[][PxGeometryType::eGEOMETRY_COUNT]; +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcCCDStateStreamPair.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcCCDStateStreamPair.h new file mode 100644 index 000000000..5504ce468 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcCCDStateStreamPair.h @@ -0,0 +1,29 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h new file mode 100644 index 000000000..53f610bce --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h @@ -0,0 +1,166 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_CONSTRAINTBLOCKPOOL_H +#define PXC_CONSTRAINTBLOCKPOOL_H + +#include "PxvConfig.h" +#include "PsArray.h" +#include "PsMutex.h" +#include "PxcNpMemBlockPool.h" + +namespace physx +{ + +class PxsConstraintBlockManager +{ +public: + PxsConstraintBlockManager(PxcNpMemBlockPool & blockPool): + mBlockPool(blockPool) + { + } + + + PX_FORCE_INLINE void reset() + { + mBlockPool.releaseConstraintBlocks(mTrackingArray); + } + + + PxcNpMemBlockArray mTrackingArray; + PxcNpMemBlockPool& mBlockPool; + +private: + PxsConstraintBlockManager& operator=(const PxsConstraintBlockManager&); +}; + +class PxcConstraintBlockStream +{ + PX_NOCOPY(PxcConstraintBlockStream) +public: + PxcConstraintBlockStream(PxcNpMemBlockPool & blockPool): + mBlockPool(blockPool), + mBlock(NULL), + mUsed(0) + { + } + + PX_FORCE_INLINE PxU8* reserve(PxU32 size, PxsConstraintBlockManager& manager) + { + size = (size+15)&~15; + if(size>PxcNpMemBlock::SIZE) + return mBlockPool.acquireExceptionalConstraintMemory(size); + + if(mBlock == NULL || size+mUsed>PxcNpMemBlock::SIZE) + { + mBlock = mBlockPool.acquireConstraintBlock(manager.mTrackingArray); + PX_ASSERT(0==mBlock || mBlock->data == reinterpret_cast(mBlock)); + mUsed = size; + return reinterpret_cast(mBlock); + } + PX_ASSERT(mBlock && mBlock->data == reinterpret_cast(mBlock)); + PxU8* PX_RESTRICT result = mBlock->data+mUsed; + mUsed += size; + return result; + } + + PX_FORCE_INLINE void reset() + { + mBlock = NULL; + mUsed = 0; + } + + PX_FORCE_INLINE PxcNpMemBlockPool& getMemBlockPool() + { + return mBlockPool; + } + +private: + PxcNpMemBlockPool& mBlockPool; + PxcNpMemBlock* mBlock; // current constraint block + PxU32 mUsed; // number of bytes used in constraint block + //Tracking peak allocations + PxU32 mPeakUsed; +}; + +class PxcContactBlockStream +{ + PX_NOCOPY(PxcContactBlockStream) +public: + PxcContactBlockStream(PxcNpMemBlockPool & blockPool): + mBlockPool(blockPool), + mBlock(NULL), + mUsed(0) + { + } + + PX_FORCE_INLINE PxU8* reserve(PxU32 size) + { + size = (size+15)&~15; + + if(size>PxcNpMemBlock::SIZE) + return mBlockPool.acquireExceptionalConstraintMemory(size); + + PX_ASSERT(size <= PxcNpMemBlock::SIZE); + + if(mBlock == NULL || size+mUsed>PxcNpMemBlock::SIZE) + { + mBlock = mBlockPool.acquireContactBlock(); + PX_ASSERT(0==mBlock || mBlock->data == reinterpret_cast(mBlock)); + mUsed = size; + return reinterpret_cast(mBlock); + } + PX_ASSERT(mBlock && mBlock->data == reinterpret_cast(mBlock)); + PxU8* PX_RESTRICT result = mBlock->data+mUsed; + mUsed += size; + return result; + } + + PX_FORCE_INLINE void reset() + { + mBlock = NULL; + mUsed = 0; + } + + PX_FORCE_INLINE PxcNpMemBlockPool& getMemBlockPool() + { + return mBlockPool; + } + +private: + PxcNpMemBlockPool& mBlockPool; + PxcNpMemBlock* mBlock; // current constraint block + PxU32 mUsed; // number of bytes used in constraint block +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h new file mode 100644 index 000000000..d8deb405d --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXC_CONTACT_CACHE_H +#define PXC_CONTACT_CACHE_H + +#include "foundation/PxTransform.h" +#include "PxvConfig.h" +#include "PxcContactMethodImpl.h" + +namespace physx +{ + bool PxcCacheLocalContacts( PxcNpThreadContext& context, Gu::Cache& pairContactCache, + const PxTransform& tm0, const PxTransform& tm1, + const PxcContactMethod conMethod, + const Gu::GeometryUnion& shape0, const Gu::GeometryUnion& shape1); + + struct PxcLocalContactsCache + { + PxTransform mTransform0; + PxTransform mTransform1; + PxU16 mNbCachedContacts; + bool mUseFaceIndices; + bool mSameNormal; + + PX_FORCE_INLINE void operator = (const PxcLocalContactsCache& other) + { + mTransform0 = other.mTransform0; + mTransform1 = other.mTransform1; + mNbCachedContacts = other.mNbCachedContacts; + mUseFaceIndices = other.mUseFaceIndices; + mSameNormal = other.mSameNormal; + } + }; + +} + +#endif // PXC_CONTACT_CACHE_H diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h new file mode 100644 index 000000000..041ef0711 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXC_MATERIALMETHOD_H +#define PXC_MATERIALMETHOD_H + +#include "CmPhysXCommon.h" +#include "PxGeometry.h" + +namespace physx +{ + +struct PxsShapeCore; +struct PxsMaterialInfo; +class PxcNpThreadContext; + +#define MATERIAL_METHOD_ARGS \ + const PxsShapeCore* shape0, \ + const PxsShapeCore* shape1, \ + PxcNpThreadContext& pairContext, \ + PxsMaterialInfo* materialInfo + + +#define SINGLE_MATERIAL_METHOD_ARGS \ + const PxsShapeCore* shape, \ + const PxU32 index, \ + PxcNpThreadContext& pairContext, \ + PxsMaterialInfo* materialInfo + +/*! +Method prototype for fetch material routines +*/ +typedef bool (*PxcGetMaterialMethod) (MATERIAL_METHOD_ARGS); + +typedef bool (*PxcGetSingleMaterialMethod) (SINGLE_MATERIAL_METHOD_ARGS); + +extern PxcGetMaterialMethod g_GetMaterialMethodTable[][PxGeometryType::eGEOMETRY_COUNT]; + +extern PxcGetSingleMaterialMethod g_GetSingleMaterialMethodTable[PxGeometryType::eGEOMETRY_COUNT]; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h new file mode 100644 index 000000000..1ccb09358 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h @@ -0,0 +1,65 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXC_NP_BATCH_H +#define PXC_NP_BATCH_H + +#include "PxvConfig.h" + +namespace physx +{ + struct PxcNpWorkUnit; + class PxcNpThreadContext; + +struct PxcNpWorkUnit; +class PxsContactManager; +struct PxsContactManagerOutput; + +namespace Gu +{ + struct Cache; +} + +namespace Cm +{ + class FlushPool; +} + +class PxLightCpuTask; + +namespace Gu +{ + class PxgGpuNarrowphaseCoreInterface; +} + +void PxcDiscreteNarrowPhase(PxcNpThreadContext& context, const PxcNpWorkUnit& cmInput, Gu::Cache& cache, PxsContactManagerOutput& output); +void PxcDiscreteNarrowPhasePCM(PxcNpThreadContext& context, const PxcNpWorkUnit& cmInput, Gu::Cache& cache, PxsContactManagerOutput& output); +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h new file mode 100644 index 000000000..ed8048b68 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h @@ -0,0 +1,154 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXC_NPCACHE_H +#define PXC_NPCACHE_H + +#include "foundation/PxMemory.h" + +#include "PsIntrinsics.h" +#include "PxcNpCacheStreamPair.h" + +#include "PsPool.h" +#include "PsFoundation.h" +#include "GuContactMethodImpl.h" +#include "PsUtilities.h" + +namespace physx +{ + +template +void PxcNpCacheWrite(PxcNpCacheStreamPair& streams, + Gu::Cache& cache, + const T& payload, + PxU32 bytes, + const PxU8* data) +{ + const PxU32 payloadSize = (sizeof(payload)+3)&~3; + cache.mCachedSize = Ps::to16((payloadSize + 4 + bytes + 0xF)&~0xF); + + PxU8* ls = streams.reserve(cache.mCachedSize); + cache.mCachedData = ls; + if(ls==NULL || (reinterpret_cast(-1))==ls) + { + if(ls==NULL) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for narrow phase. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + return; + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in narrowphase. " + "Either accept dropped contacts or simplify collision geometry."); + cache.mCachedData = NULL; + ls = NULL; + return; + } + } + + *reinterpret_cast(ls) = payload; + *reinterpret_cast(ls+payloadSize) = bytes; + if(data) + PxMemCopy(ls+payloadSize+sizeof(PxU32), data, bytes); +} + + +template +PxU8* PxcNpCacheWriteInitiate(PxcNpCacheStreamPair& streams, Gu::Cache& cache, const T& payload, PxU32 bytes) +{ + PX_UNUSED(payload); + + const PxU32 payloadSize = (sizeof(payload)+3)&~3; + cache.mCachedSize = Ps::to16((payloadSize + 4 + bytes + 0xF)&~0xF); + + PxU8* ls = streams.reserve(cache.mCachedSize); + cache.mCachedData = ls; + if(NULL==ls || reinterpret_cast(-1)==ls) + { + if(NULL==ls) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for narrow phase. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in narrowphase. " + "Either accept dropped contacts or simplify collision geometry."); + cache.mCachedData = NULL; + ls = NULL; + } + } + return ls; +} + +template +PX_FORCE_INLINE void PxcNpCacheWriteFinalize(PxU8* ls, const T& payload, PxU32 bytes, const PxU8* data) +{ + const PxU32 payloadSize = (sizeof(payload)+3)&~3; + *reinterpret_cast(ls) = payload; + *reinterpret_cast(ls+payloadSize) = bytes; + if(data) + PxMemCopy(ls+payloadSize+sizeof(PxU32), data, bytes); +} + + +template +PX_FORCE_INLINE PxU8* PxcNpCacheRead(Gu::Cache& cache, T*& payload) +{ + PxU8* ls = cache.mCachedData; + payload = reinterpret_cast(ls); + const PxU32 payloadSize = (sizeof(T)+3)&~3; + return reinterpret_cast(ls+payloadSize+sizeof(PxU32)); +} + +template +const PxU8* PxcNpCacheRead2(Gu::Cache& cache, T& payload, PxU32& bytes) +{ + const PxU8* ls = cache.mCachedData; + if(ls==NULL) + { + bytes = 0; + return NULL; + } + + const PxU32 payloadSize = (sizeof(payload)+3)&~3; + payload = *reinterpret_cast(ls); + bytes = *reinterpret_cast(ls+payloadSize); + PX_ASSERT(cache.mCachedSize == ((payloadSize + 4 + bytes+0xF)&~0xF)); + return reinterpret_cast(ls+payloadSize+sizeof(PxU32)); +} + +} + +#endif // #ifndef PXC_NPCACHE_H diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h new file mode 100644 index 000000000..ba7da39d2 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_NPCACHESTREAMPAIR_H +#define PXC_NPCACHESTREAMPAIR_H + +#include "foundation/PxSimpleTypes.h" +#include "PxvConfig.h" +#include "PxcNpMemBlockPool.h" + +namespace physx +{ + +static const PxU32 PXC_NPCACHE_BLOCK_SIZE = 16384; + + +struct PxcNpCacheStreamPair +{ +public: + PxcNpCacheStreamPair(PxcNpMemBlockPool& blockPool); + + // reserve can fail and return null. + PxU8* reserve(PxU32 byteCount); + void reset(); +private: + PxcNpMemBlockPool& mBlockPool; + PxcNpMemBlock* mBlock; + PxU32 mUsed; +private: + PxcNpCacheStreamPair& operator=(const PxcNpCacheStreamPair&); +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h new file mode 100644 index 000000000..5683b0d1b --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXC_NPCONTACTPREPSHARED_H +#define PXC_NPCONTACTPREPSHARED_H + +namespace physx +{ +class PxcNpThreadContext; +struct PxsMaterialInfo; +class PxsMaterialManager; +class PxsConstraintBlockManager; +class PxcConstraintBlockStream; +struct PxsContactManagerOutput; + +namespace Gu +{ + struct ContactPoint; +} + +static const PxReal PXC_SAME_NORMAL = 0.999f; //Around 6 degrees + +PxU32 writeCompressedContact(const Gu::ContactPoint* const PX_RESTRICT contactPoints, const PxU32 numContactPoints, PxcNpThreadContext* threadContext, + PxU8& writtenContactCount, PxU8*& outContactPatches, PxU8*& outContactPoints, PxU16& compressedContactSize, PxReal*& contactForces, PxU32 contactForceByteSize, + const PxsMaterialManager* materialManager, bool hasModifiableContacts, bool forceNoResponse, PxsMaterialInfo* PX_RESTRICT pMaterial, PxU8& numPatches, + PxU32 additionalHeaderSize = 0, PxsConstraintBlockManager* manager = NULL, PxcConstraintBlockStream* blockStream = NULL, bool insertAveragePoint = false, + PxcDataStreamPool* pool = NULL, PxcDataStreamPool* patchStreamPool = NULL, PxcDataStreamPool* forcePool = NULL, const bool isMeshType = false); + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h new file mode 100644 index 000000000..bfe4debf4 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h @@ -0,0 +1,119 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_NP_MEM_BLOCK_POOL_H +#define PXC_NP_MEM_BLOCK_POOL_H + +#include "PxvConfig.h" +#include "PsArray.h" +#include "PxcScratchAllocator.h" + +namespace physx +{ +struct PxcNpMemBlock +{ + enum + { + SIZE = 16384 + }; + PxU8 data[SIZE]; +}; + +typedef Ps::Array PxcNpMemBlockArray; + +class PxcNpMemBlockPool +{ + PX_NOCOPY(PxcNpMemBlockPool) +public: + PxcNpMemBlockPool(PxcScratchAllocator& allocator); + ~PxcNpMemBlockPool(); + + void init(PxU32 initial16KDataBlocks, PxU32 maxBlocks); + void flush(); + void setBlockCount(PxU32 count); + PxU32 getUsedBlockCount() const; + PxU32 getMaxUsedBlockCount() const; + PxU32 getPeakConstraintBlockCount() const; + void releaseUnusedBlocks(); + + PxcNpMemBlock* acquireConstraintBlock(); + PxcNpMemBlock* acquireConstraintBlock(PxcNpMemBlockArray& memBlocks); + PxcNpMemBlock* acquireContactBlock(); + PxcNpMemBlock* acquireFrictionBlock(); + PxcNpMemBlock* acquireNpCacheBlock(); + + PxU8* acquireExceptionalConstraintMemory(PxU32 size); + + void acquireConstraintMemory(); + void releaseConstraintMemory(); + void releaseConstraintBlocks(PxcNpMemBlockArray& memBlocks); + void releaseContacts(); + void swapFrictionStreams(); + void swapNpCacheStreams(); + + void flushUnused(); + +private: + + + Ps::Mutex mLock; + PxcNpMemBlockArray mConstraints; + PxcNpMemBlockArray mContacts[2]; + PxcNpMemBlockArray mFriction[2]; + PxcNpMemBlockArray mNpCache[2]; + PxcNpMemBlockArray mScratchBlocks; + Ps::Array mExceptionalConstraints; + + PxcNpMemBlockArray mUnused; + + PxU32 mNpCacheActiveStream; + PxU32 mFrictionActiveStream; + PxU32 mCCDCacheActiveStream; + PxU32 mContactIndex; + PxU32 mAllocatedBlocks; + PxU32 mMaxBlocks; + PxU32 mInitialBlocks; + PxU32 mUsedBlocks; + PxU32 mMaxUsedBlocks; + PxcNpMemBlock* mScratchBlockAddr; + PxU32 mNbScratchBlocks; + PxcScratchAllocator& mScratchAllocator; + + PxU32 mPeakConstraintAllocations; + PxU32 mConstraintAllocations; + + PxcNpMemBlock* acquire(PxcNpMemBlockArray& trackingArray, PxU32* allocationCount = NULL, PxU32* peakAllocationCount = NULL, bool isScratchAllocation = false); + void release(PxcNpMemBlockArray& deadArray, PxU32* allocationCount = NULL); +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h new file mode 100644 index 000000000..3a3bca94c --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h @@ -0,0 +1,211 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXC_NPTHREADCONTEXT_H +#define PXC_NPTHREADCONTEXT_H + +#include "PxvConfig.h" +#include "CmScaling.h" +#include "CmRenderOutput.h" +#include "PxcNpCacheStreamPair.h" +#include "PxcConstraintBlockStream.h" +#include "GuContactBuffer.h" +#include "PxcThreadCoherentCache.h" +#include "PxGeometry.h" +#include "CmBitMap.h" +#include "../pcm/GuPersistentContactManifold.h" + +namespace physx +{ + +class PxsTransformCache; +class PxsMaterialManager; + +namespace Sc +{ + class BodySim; +} + +/*! +Per-thread context used by contact generation routines. +*/ + +struct PxcDataStreamPool +{ + PxU8* mDataStream; + PxI32 mSharedDataIndex; + PxU32 mDataStreamSize; + PxU32 mSharedDataIndexGPU; + + bool isOverflown() const + { + //FD: my expectaton is that reading those variables is atomic, shared indices are non-decreasing, + //so we can only get a false overflow alert because of concurrency issues, which is not a big deal as it means + //it did overflow a bit later + return mSharedDataIndex + mSharedDataIndexGPU >= mDataStreamSize; + } +}; + +struct PxcNpContext +{ + private: + PX_NOCOPY(PxcNpContext) + public: + + PxcNpContext() : + mNpMemBlockPool (mScratchAllocator), + mMeshContactMargin (0.0f), + mToleranceLength (0.0f), + mCreateContactStream (false), + mContactStreamPool (NULL), + mPatchStreamPool (NULL), + mForceAndIndiceStreamPool(NULL), + mMaterialManager (NULL) + { + } + + PxcScratchAllocator mScratchAllocator; + PxcNpMemBlockPool mNpMemBlockPool; + PxReal mMeshContactMargin; + PxReal mToleranceLength; + Cm::RenderBuffer mRenderBuffer; + bool mCreateContactStream; // flag to enforce that contacts are stored persistently per workunit. Used for PVD. + PxcDataStreamPool* mContactStreamPool; + PxcDataStreamPool* mPatchStreamPool; + PxcDataStreamPool* mForceAndIndiceStreamPool; + PxcDataStreamPool* mConstraintWriteBackStreamPool; + PxsMaterialManager* mMaterialManager; + + PX_FORCE_INLINE PxReal getToleranceLength() const { return mToleranceLength; } + PX_FORCE_INLINE void setToleranceLength(PxReal x) { mToleranceLength = x; } + PX_FORCE_INLINE PxReal getMeshContactMargin() const { return mMeshContactMargin; } + PX_FORCE_INLINE void setMeshContactMargin(PxReal x) { mMeshContactMargin = x; } + PX_FORCE_INLINE bool getCreateContactStream() { return mCreateContactStream; } + + PX_FORCE_INLINE PxcNpMemBlockPool& getNpMemBlockPool() { return mNpMemBlockPool; } + PX_FORCE_INLINE const PxcNpMemBlockPool& getNpMemBlockPool() const { return mNpMemBlockPool; } + PX_FORCE_INLINE void setMaterialManager(PxsMaterialManager* m){ mMaterialManager = m; } + PX_FORCE_INLINE PxsMaterialManager* getMaterialManager() const { return mMaterialManager; } + + Cm::RenderOutput getRenderOutput() { return Cm::RenderOutput(mRenderBuffer); } +}; + +class PxcNpThreadContext : public PxcThreadCoherentCache::EntryBase +{ + PX_NOCOPY(PxcNpThreadContext) +public: + PxcNpThreadContext(PxcNpContext* params); + ~PxcNpThreadContext(); + +#if PX_ENABLE_SIM_STATS + void clearStats(); +#endif + + PX_FORCE_INLINE void setCreateContactStream(bool to) { mCreateContactStream = to; } + + PX_FORCE_INLINE void addLocalNewTouchCount(PxU32 newTouchCMCount) { mLocalNewTouchCount += newTouchCMCount; } + PX_FORCE_INLINE void addLocalLostTouchCount(PxU32 lostTouchCMCount) { mLocalLostTouchCount += lostTouchCMCount; } + PX_FORCE_INLINE PxU32 getLocalNewTouchCount() const { return mLocalNewTouchCount; } + PX_FORCE_INLINE PxU32 getLocalLostTouchCount() const { return mLocalLostTouchCount; } + + PX_FORCE_INLINE void addLocalFoundPatchCount(PxU32 foundPatchCount) { mLocalFoundPatchCount += foundPatchCount; } + PX_FORCE_INLINE void addLocalLostPatchCount(PxU32 lostPatchCount) { mLocalLostPatchCount += lostPatchCount; } + PX_FORCE_INLINE PxU32 getLocalFoundPatchCount() const { return mLocalFoundPatchCount; } + PX_FORCE_INLINE PxU32 getLocalLostPatchCount() const { return mLocalLostPatchCount; } + + PX_FORCE_INLINE Cm::BitMap& getLocalChangeTouch() { return mLocalChangeTouch; } + + PX_FORCE_INLINE Cm::BitMap& getLocalPatchChangeMap() { return mLocalPatchCountChange; } + + void reset(PxU32 cmCount); + // debugging + Cm::RenderOutput mRenderOutput; + + // dsequeira: Need to think about this block pool allocation a bit more. Ideally we'd be + // taking blocks from a single pool, except that we want to be able to selectively reclaim + // blocks if the user needs to defragment, depending on which artifacts they're willing + // to tolerate, such that the blocks we don't reclaim are contiguous. +#if PX_ENABLE_SIM_STATS + PxU32 mDiscreteContactPairs [PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 mModifiedContactPairs [PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; +#endif + PxcContactBlockStream mContactBlockStream; // constraint block pool + PxcNpCacheStreamPair mNpCacheStreamPair; // narrow phase pairwise data cache + + // Everything below here is scratch state. Most of it can even overlap. + + // temporary contact buffer + Gu::ContactBuffer mContactBuffer; + + PX_ALIGN(16, Gu::MultiplePersistentContactManifold mTempManifold); + + Gu::NarrowPhaseParams mNarrowPhaseParams; + + // DS: this stuff got moved here from the PxcNpPairContext. As Pierre says: + ////////// PT: those members shouldn't be there in the end, it's not necessary + Ps::Array mBodySimPool; + PxsTransformCache* mTransformCache; + PxReal* mContactDistance; + bool mPCM; + bool mContactCache; + bool mCreateContactStream; // flag to enforce that contacts are stored persistently per workunit. Used for PVD. + bool mCreateAveragePoint; // flag to enforce whether we create average points +#if PX_ENABLE_SIM_STATS + PxU32 mCompressedCacheSize; + PxU32 mNbDiscreteContactPairsWithCacheHits; + PxU32 mNbDiscreteContactPairsWithContacts; +#endif + PxReal mDt; // AP: still needed for ccd + PxU32 mCCDPass; + PxU32 mCCDFaceIndex; + + PxU32 mMaxPatches; + //PxU32 mTotalContactCount; + PxU32 mTotalCompressedCacheSize; + //PxU32 mTotalPatchCount; + + PxcDataStreamPool* mContactStreamPool; + PxcDataStreamPool* mPatchStreamPool; + PxcDataStreamPool* mForceAndIndiceStreamPool; //this stream is used to store the force buffer and triangle index if we are performing mesh/heightfield contact gen + PxcDataStreamPool* mConstraintWriteBackStreamPool; + PxsMaterialManager* mMaterialManager; +private: + // change touch handling. + Cm::BitMap mLocalChangeTouch; + Cm::BitMap mLocalPatchCountChange; + PxU32 mLocalNewTouchCount; + PxU32 mLocalLostTouchCount; + PxU32 mLocalFoundPatchCount; + PxU32 mLocalLostPatchCount; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h new file mode 100644 index 000000000..0cb68d584 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h @@ -0,0 +1,165 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_NPWORKUNIT_H +#define PXC_NPWORKUNIT_H + +#include "PxcNpThreadContext.h" +#include "PxcMaterialMethodImpl.h" +#include "PxcNpCache.h" + +namespace physx +{ + +class PxvContact; + +struct PxsRigidCore; +struct PxsShapeCore; + +class PxsMaterialManager; + +struct PxcNpWorkUnitFlag +{ + enum Enum + { + eOUTPUT_CONTACTS = 1, + eOUTPUT_CONSTRAINTS = 2, + eDISABLE_STRONG_FRICTION = 4, + eARTICULATION_BODY0 = 8, + eARTICULATION_BODY1 = 16, + eDYNAMIC_BODY0 = 32, + eDYNAMIC_BODY1 = 64, + eMODIFIABLE_CONTACT = 128, + eFORCE_THRESHOLD = 256, + eDETECT_DISCRETE_CONTACT = 512, + eHAS_KINEMATIC_ACTOR = 1024, + eDISABLE_RESPONSE = 2048, + eDETECT_CCD_CONTACTS = 4096 + }; +}; + +struct PxcNpWorkUnitStatusFlag +{ + enum Enum + { + eHAS_NO_TOUCH = (1 << 0), + eHAS_TOUCH = (1 << 1), + //eHAS_SOLVER_CONSTRAINTS = (1 << 2), + eREQUEST_CONSTRAINTS = (1 << 3), + eHAS_CCD_RETOUCH = (1 << 4), // Marks pairs that are touching at a CCD pass and were touching at discrete collision or at a previous CCD pass already + // but we can not tell whether they lost contact in a pass before. We send them as pure eNOTIFY_TOUCH_CCD events to the + // contact report callback if requested. + eDIRTY_MANAGER = (1 << 5), + eREFRESHED_WITH_TOUCH = (1 << 6), + eTOUCH_KNOWN = eHAS_NO_TOUCH | eHAS_TOUCH // The touch status is known (if narrowphase never ran for a pair then no flag will be set) + }; +}; + +/* + * A struct to record the number of work units a particular constraint pointer references. + * This is created at the beginning of the constriant data and is used to bypass constraint preparation when the + * bodies are not moving a lot. In this case, we can recycle the constraints and save ourselves some cycles. +*/ +struct PxcNpWorkUnit; +struct PxcNpWorkUnitBatch +{ + PxcNpWorkUnit* mUnits[4]; + PxU32 mSize; +}; + +struct PxcNpWorkUnit +{ + + const PxsRigidCore* rigidCore0; // INPUT //4 //8 + const PxsRigidCore* rigidCore1; // INPUT //8 //16 + + const PxsShapeCore* shapeCore0; // INPUT //12 //24 + const PxsShapeCore* shapeCore1; // INPUT //16 //32 + + PxU8* ccdContacts; // OUTPUT //20 //40 + + PxU8* frictionDataPtr; // INOUT //24 //48 + + PxU16 flags; // INPUT //26 //50 + PxU8 frictionPatchCount; // INOUT //27 //51 + PxU8 statusFlags; // OUTPUT (see PxcNpWorkUnitStatusFlag) //28 //52 + + PxU8 dominance0; // INPUT //29 //53 + PxU8 dominance1; // INPUT //30 //54 + PxU8 geomType0; // INPUT //31 //55 + PxU8 geomType1; // INPUT //32 //56 + + PxU32 index; // INPUT //36 //60 + + PxReal restDistance; // INPUT //40 //64 + + PxU32 mTransformCache0; // //44 //68 + PxU32 mTransformCache1; // //48 //72 + + PxU32 mEdgeIndex; //inout the island gen edge index //52 //76 + PxU32 mNpIndex; //INPUT //56 //80 + + PxReal mTorsionalPatchRadius; //60 //84 + PxReal mMinTorsionalPatchRadius; //64 //88 + +}; + +//#if !defined(PX_P64) +//PX_COMPILE_TIME_ASSERT(0 == (sizeof(PxcNpWorkUnit) & 0x0f)); +//#endif + +PX_FORCE_INLINE void PxcNpWorkUnitClearContactState(PxcNpWorkUnit& n) +{ + n.ccdContacts = NULL; +} + + +PX_FORCE_INLINE void PxcNpWorkUnitClearCachedState(PxcNpWorkUnit& n) +{ + n.frictionDataPtr = 0; + n.frictionPatchCount = 0; + n.ccdContacts = NULL; +} + +PX_FORCE_INLINE void PxcNpWorkUnitClearFrictionCachedState(PxcNpWorkUnit& n) +{ + n.frictionDataPtr = 0; + n.frictionPatchCount = 0; + n.ccdContacts = NULL; +} + +#if !defined(PX_P64) +//PX_COMPILE_TIME_ASSERT(sizeof(PxcNpWorkUnit)==128); +#endif + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h new file mode 100644 index 000000000..1f698d59e --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h @@ -0,0 +1,118 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXC_RIGIDBODY_H +#define PXC_RIGIDBODY_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "PxvDynamics.h" +#include "CmSpatialVector.h" + +namespace physx +{ + +class PxsContactManager; +struct PxsCCDPair; +struct PxsCCDBody; + +#define PX_INTERNAL_LOCK_FLAG_START 8 + +PX_ALIGN_PREFIX(16) +class PxcRigidBody +{ +public: + + enum PxcRigidBodyFlag + { + eFROZEN = 1 << 0, //This flag indicates that the stabilization is enabled and the body is + //"frozen". By "frozen", we mean that the body's transform is unchanged + //from the previous frame. This permits various optimizations. + eFREEZE_THIS_FRAME = 1 << 1, + eUNFREEZE_THIS_FRAME = 1 << 2, + eACTIVATE_THIS_FRAME = 1 << 3, + eDEACTIVATE_THIS_FRAME = 1 << 4, + eDISABLE_GRAVITY = 1 << 5, + eSPECULATIVE_CCD = 1 << 6, + //KS - copied here for GPU simulation to avoid needing to pass another set of flags around. + eLOCK_LINEAR_X = 1 << (PX_INTERNAL_LOCK_FLAG_START), + eLOCK_LINEAR_Y = 1 << (PX_INTERNAL_LOCK_FLAG_START + 1), + eLOCK_LINEAR_Z = 1 << (PX_INTERNAL_LOCK_FLAG_START + 2), + eLOCK_ANGULAR_X = 1 << (PX_INTERNAL_LOCK_FLAG_START + 3), + eLOCK_ANGULAR_Y = 1 << (PX_INTERNAL_LOCK_FLAG_START + 4), + eLOCK_ANGULAR_Z = 1 << (PX_INTERNAL_LOCK_FLAG_START + 5) + + + }; + + PX_FORCE_INLINE PxcRigidBody(PxsBodyCore* core) + : mLastTransform(core->body2World), + mCCD(NULL), + mCore(core) + { + } + + void adjustCCDLastTransform(); + +protected: + + ~PxcRigidBody() + { + } + +public: + + PxTransform mLastTransform; //28 (28) + + PxU16 mInternalFlags; //30 (30) + PxU16 solverIterationCounts; //32 (32) + + PxsCCDBody* mCCD; //36 (40) // only valid during CCD + + PxsBodyCore* mCore; //40 (48) + +#if !PX_P64_FAMILY + PxU32 alignmentPad[2]; //48 (48) +#endif + + PxVec3 sleepLinVelAcc; //60 (60) + PxReal freezeCount; //64 (64) + + PxVec3 sleepAngVelAcc; //76 (76) + PxReal accelScale; //80 (80) + + +} +PX_ALIGN_SUFFIX(16); +PX_COMPILE_TIME_ASSERT(0 == (sizeof(PxcRigidBody) & 0x0f)); + +} + +#endif //PXC_RIGIDBODY_H diff --git a/src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h new file mode 100644 index 000000000..70d918e88 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h @@ -0,0 +1,138 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXC_SCRATCHALLOCATOR_H +#define PXC_SCRATCHALLOCATOR_H + +#include "foundation/PxAssert.h" +#include "PxvConfig.h" +#include "PsMutex.h" +#include "PsArray.h" +#include "PsAllocator.h" + +namespace physx +{ +class PxcScratchAllocator +{ + PX_NOCOPY(PxcScratchAllocator) +public: + PxcScratchAllocator() : mStack(PX_DEBUG_EXP("PxcScratchAllocator")), mStart(NULL), mSize(0) + { + mStack.reserve(64); + mStack.pushBack(0); + } + + void setBlock(void* addr, PxU32 size) + { + // if the stack is not empty then some scratch memory was not freed on the previous frame. That's + // likely indicative of a problem, because when the scratch block is too small the memory will have + // come from the heap + + PX_ASSERT(mStack.size()==1); + mStack.popBack(); + + mStart = reinterpret_cast(addr); + mSize = size; + mStack.pushBack(mStart + size); + } + + void* allocAll(PxU32& size) + { + Ps::Mutex::ScopedLock lock(mLock); + PX_ASSERT(mStack.size()>0); + size = PxU32(mStack.back()-mStart); + + if(size==0) + return NULL; + + mStack.pushBack(mStart); + return mStart; + } + + + void* alloc(PxU32 requestedSize, bool fallBackToHeap = false) + { + requestedSize = (requestedSize+15)&~15; + + Ps::Mutex::ScopedLock lock(mLock); + PX_ASSERT(mStack.size()>=1); + + PxU8* top = mStack.back(); + + if(top - mStart >= ptrdiff_t(requestedSize)) + { + PxU8* addr = top - requestedSize; + mStack.pushBack(addr); + return addr; + } + + if(!fallBackToHeap) + return NULL; + + return PX_ALLOC(requestedSize, "Scratch Block Fallback"); + } + + void free(void* addr) + { + PX_ASSERT(addr!=NULL); + if(!isScratchAddr(addr)) + { + PX_FREE(addr); + return; + } + + Ps::Mutex::ScopedLock lock(mLock); + PX_ASSERT(mStack.size()>1); + + PxU32 i=mStack.size()-1; + while(mStack[i](addr); + return a>= mStart && a mStack; + PxU8* mStart; + PxU32 mSize; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h new file mode 100644 index 000000000..d375eff46 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h @@ -0,0 +1,150 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXC_THREADCOHERENTCACHE_H +#define PXC_THREADCOHERENTCACHE_H + +#include "PsMutex.h" +#include "PsAllocator.h" +#include "PsSList.h" + +namespace physx +{ + +class PxsContext; +/*! +Controls a pool of large objects which must be thread safe. +Tries to return the object most recently used by the thread(for better cache coherancy). +Assumes the object has a default contructor. + +(Note the semantics are different to a pool because we dont want to construct/destroy each time +an object is requested, which may be expensive). + +TODO: add thread coherancy. +*/ +template +class PxcThreadCoherentCache : public Ps::AlignedAllocator<16, Ps::ReflectionAllocator > +{ + typedef Ps::AlignedAllocator<16, Ps::ReflectionAllocator > Allocator; + PX_NOCOPY(PxcThreadCoherentCache) +public: + + typedef Ps::SListEntry EntryBase; + + PX_INLINE PxcThreadCoherentCache(Params* params, const Allocator& alloc = Allocator()) : Allocator(alloc), mParams(params) + { + } + + PX_INLINE ~PxcThreadCoherentCache() + { + T* np = static_cast(root.pop()); + + while(np!=NULL) + { + np->~T(); + Allocator::deallocate(np); + np = static_cast(root.pop()); + } + } + + PX_INLINE T* get() + { + T* rv = static_cast(root.pop()); + if(rv==NULL) + { + rv = reinterpret_cast(Allocator::allocate(sizeof(T), __FILE__, __LINE__)); + new (rv) T(mParams); + } + + return rv; + } + + PX_INLINE void put(T* item) + { + root.push(*item); + } + + +private: + Ps::SList root; + Params* mParams; + + template + friend class PxcThreadCoherentCacheIterator; +}; + +/*! +Used to iterate over all objects controlled by the cache. + +Note: The iterator flushes the cache(extracts all items on construction and adds them back on +destruction so we can iterate the list in a safe manner). +*/ +template +class PxcThreadCoherentCacheIterator +{ +public: + PxcThreadCoherentCacheIterator(PxcThreadCoherentCache& cache) : mCache(cache) + { + mNext = cache.root.flush(); + mFirst = mNext; + } + ~PxcThreadCoherentCacheIterator() + { + Ps::SListEntry* np = mFirst; + while(np != NULL) + { + Ps::SListEntry* npNext = np->next(); + mCache.root.push(*np); + np = npNext; + } + } + + PX_INLINE T* getNext() + { + if(mNext == NULL) + return NULL; + + T* rv = static_cast(mNext); + mNext = mNext->next(); + + return rv; + } +private: + + PxcThreadCoherentCacheIterator& operator=(const PxcThreadCoherentCacheIterator&); + PxcThreadCoherentCache &mCache; + Ps::SListEntry* mNext; + Ps::SListEntry* mFirst; + +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp b/src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp new file mode 100644 index 000000000..7840341ed --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp @@ -0,0 +1,267 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxcContactMethodImpl.h" + +namespace physx +{ + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +//non-pcm sphere function +bool PxcContactSphereSphere(GU_CONTACT_METHOD_ARGS) +{ + return contactSphereSphere(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSphereCapsule(GU_CONTACT_METHOD_ARGS) +{ + return contactSphereCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSphereBox(GU_CONTACT_METHOD_ARGS) +{ + return contactSphereBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSpherePlane(GU_CONTACT_METHOD_ARGS) +{ + return contactSpherePlane(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSphereConvex(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSphereHeightField(GU_CONTACT_METHOD_ARGS) +{ + return contactSphereHeightfield(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + return contactSphereMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//non-pcm plane functions +bool PxcContactPlaneBox(GU_CONTACT_METHOD_ARGS) +{ + return contactPlaneBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactPlaneCapsule(GU_CONTACT_METHOD_ARGS) +{ + return contactPlaneCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactPlaneConvex(GU_CONTACT_METHOD_ARGS) +{ + return contactPlaneConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//non-pcm capsule funtions +bool PxcContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactCapsuleBox(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactCapsuleConvex(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleHeightfield(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + return contactCapsuleMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//non-pcm box functions +bool PxcContactBoxBox(GU_CONTACT_METHOD_ARGS) +{ + return contactBoxBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactBoxConvex(GU_CONTACT_METHOD_ARGS) +{ + return contactBoxConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactBoxHeightField(GU_CONTACT_METHOD_ARGS) +{ + return contactBoxHeightfield(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactBoxMesh(GU_CONTACT_METHOD_ARGS) +{ + return contactBoxMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//non-pcm convex functions +bool PxcContactConvexConvex(GU_CONTACT_METHOD_ARGS) +{ + return contactConvexConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactConvexHeightField(GU_CONTACT_METHOD_ARGS) +{ + return contactConvexHeightfield(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcContactConvexMesh(GU_CONTACT_METHOD_ARGS) +{ + return contactConvexMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//pcm sphere functions +bool PxcPCMContactSphereSphere(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereSphere(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSpherePlane(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSpherePlane(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSphereCapsule(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSphereBox(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSphereConvex(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSphereHeightField(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereHeightField(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactSphereMesh(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactSphereMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//pcm plane functions +bool PxcPCMContactPlaneBox(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactPlaneBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactPlaneCapsule(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactPlaneCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactPlaneConvex(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactPlaneConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//pcm capsule functions +bool PxcPCMContactCapsuleCapsule(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactCapsuleCapsule(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactCapsuleBox(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactCapsuleBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactCapsuleConvex(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactCapsuleConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactCapsuleHeightField(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactCapsuleHeightField(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactCapsuleMesh(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactCapsuleMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//pcm box functions +bool PxcPCMContactBoxBox(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactBoxBox(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactBoxConvex(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactBoxConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactBoxHeightField(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactBoxHeightField(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactBoxMesh(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactBoxMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +//pcm convex functions +bool PxcPCMContactConvexConvex(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactConvexConvex(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactConvexHeightField(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactConvexHeightField(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +bool PxcPCMContactConvexMesh(GU_CONTACT_METHOD_ARGS) +{ + return pcmContactConvexMesh(shape0, shape1, transform0, transform1, params, cache, contactBuffer, renderOutput); +} + +} diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp new file mode 100644 index 000000000..419163225 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp @@ -0,0 +1,393 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxcContactCache.h" +#include "PxsContactManager.h" +#include "PsUtilities.h" +#include "PxcNpCache.h" + +using namespace physx; +using namespace Gu; + +//#define ENABLE_CONTACT_CACHE_STATS + +#ifdef ENABLE_CONTACT_CACHE_STATS + static PxU32 gNbCalls; + static PxU32 gNbHits; +#endif + +void PxcClearContactCacheStats() +{ +#ifdef ENABLE_CONTACT_CACHE_STATS + gNbCalls = 0; + gNbHits = 0; +#endif +} + +void PxcDisplayContactCacheStats() +{ +#ifdef ENABLE_CONTACT_CACHE_STATS + pxPrintf("%d|%d (%f)\n", gNbHits, gNbCalls, gNbCalls ? float(gNbHits)/float(gNbCalls) : 0.0f); +#endif +} + +namespace physx +{ + const bool g_CanUseContactCache[][PxGeometryType::eGEOMETRY_COUNT] = + { + //PxGeometryType::eSPHERE + { + false, //PxcContactSphereSphere + false, //PxcContactSpherePlane + true, //PxcContactSphereCapsule + false, //PxcContactSphereBox + true, //PxcContactSphereConvex + true, //PxcContactSphereMesh + true, //PxcContactSphereHeightField + }, + + //PxGeometryType::ePLANE + { + false, //- + false, //PxcInvalidContactPair + true, //PxcContactPlaneCapsule + true, //PxcContactPlaneBox + true, //PxcContactPlaneConvex + false, //PxcInvalidContactPair + false, //PxcInvalidContactPair + }, + + //PxGeometryType::eCAPSULE + { + false, //- + false, //- + true, //PxcContactCapsuleCapsule + true, //PxcContactCapsuleBox + true, //PxcContactCapsuleConvex + true, //PxcContactCapsuleMesh + true, //PxcContactCapsuleHeightField + }, + + //PxGeometryType::eBOX + { + false, //- + false, //- + false, //- + true, //PxcContactBoxBox + true, //PxcContactBoxConvex + true, //PxcContactBoxMesh + true, //PxcContactBoxHeightField + }, + + //PxGeometryType::eCONVEXMESH + { + false, //- + false, //- + false, //- + false, //- + true, //PxcContactConvexConvex + true, //PxcContactConvexMesh2 + true, //PxcContactConvexHeightField + }, + + //PxGeometryType::eTRIANGLEMESH + { + false, //- + false, //- + false, //- + false, //- + false, //- + false, //PxcInvalidContactPair + false, //PxcInvalidContactPair + }, + + //PxGeometryType::eHEIGHTFIELD + { + false, //- + false, //- + false, //- + false, //- + false, //- + false, //- + false, //PxcInvalidContactPair + }, + }; +} + +static PX_FORCE_INLINE void updateContact( Gu::ContactPoint& dst, const PxcLocalContactsCache& contactsData, + const Cm::Matrix34& world0, const Cm::Matrix34& world1, + const PxVec3& point, const PxVec3& normal, float separation) +{ + const PxVec3 tmp0 = contactsData.mTransform0.transformInv(point); + const PxVec3 worldpt0 = world0.transform(tmp0); + + const PxVec3 tmp1 = contactsData.mTransform1.transformInv(point); + const PxVec3 worldpt1 = world1.transform(tmp1); + + const PxVec3 motion = worldpt0 - worldpt1; + dst.normal = normal; + dst.point = (worldpt0 + worldpt1)*0.5f; + //dst.point = point; + dst.separation = separation + motion.dot(normal); +} + +static PX_FORCE_INLINE void prefetchData128(PxU8* PX_RESTRICT ptr, PxU32 size) +{ + // PT: always prefetch the cache line containing our address (which unfortunately won't be aligned to 128 most of the time) + Ps::prefetchLine(ptr, 0); + // PT: compute start offset of our data within its cache line + const PxU32 startOffset = PxU32(size_t(ptr)&127); + // PT: prefetch next cache line if needed + if(startOffset+size>128) + Ps::prefetchLine(ptr+128, 0); +} + +static PX_FORCE_INLINE PxU8* outputToCache(PxU8* PX_RESTRICT bytes, const PxVec3& v) +{ + *reinterpret_cast(bytes) = v; + return bytes + sizeof(PxVec3); +} + +static PX_FORCE_INLINE PxU8* outputToCache(PxU8* PX_RESTRICT bytes, PxReal v) +{ + *reinterpret_cast(bytes) = v; + return bytes + sizeof(PxReal); +} + +static PX_FORCE_INLINE PxU8* outputToCache(PxU8* PX_RESTRICT bytes, PxU32 v) +{ + *reinterpret_cast(bytes) = v; + return bytes + sizeof(PxU32); +} + +//PxU32 gContactCache_NbCalls = 0; +//PxU32 gContactCache_NbHits = 0; + +static PX_FORCE_INLINE PxReal maxComponentDeltaPos(const PxTransform& t0, const PxTransform& t1) +{ + PxReal delta = PxAbs(t0.p.x - t1.p.x); + delta = PxMax(delta, PxAbs(t0.p.y - t1.p.y)); + delta = PxMax(delta, PxAbs(t0.p.z - t1.p.z)); + return delta; +} + +static PX_FORCE_INLINE PxReal maxComponentDeltaRot(const PxTransform& t0, const PxTransform& t1) +{ + PxReal delta = PxAbs(t0.q.x - t1.q.x); + delta = PxMax(delta, PxAbs(t0.q.y - t1.q.y)); + delta = PxMax(delta, PxAbs(t0.q.z - t1.q.z)); + delta = PxMax(delta, PxAbs(t0.q.w - t1.q.w)); + return delta; +} + +bool physx::PxcCacheLocalContacts( PxcNpThreadContext& context, Gu::Cache& pairContactCache, + const PxTransform& tm0, const PxTransform& tm1, + const PxcContactMethod conMethod, + const Gu::GeometryUnion& shape0, const Gu::GeometryUnion& shape1) +{ + const Gu::NarrowPhaseParams& params = context.mNarrowPhaseParams; + +// gContactCache_NbCalls++; + + if(pairContactCache.mCachedData) + prefetchData128(pairContactCache.mCachedData, pairContactCache.mCachedSize); + + ContactBuffer& contactBuffer = context.mContactBuffer; + contactBuffer.reset(); + + PxcLocalContactsCache contactsData; + PxU32 nbCachedBytes; + const PxU8* cachedBytes = PxcNpCacheRead2(pairContactCache, contactsData, nbCachedBytes); + + pairContactCache.mCachedData = NULL; + pairContactCache.mCachedSize = 0; + +#ifdef ENABLE_CONTACT_CACHE_STATS + gNbCalls++; +#endif + + const PxU32 payloadSize = (sizeof(PxcLocalContactsCache)+3)&~3; + + if(cachedBytes) + { + // PT: we used to store the relative TM but it's better to save memory and recompute it + const PxTransform t0to1 = tm1.transformInv(tm0); + const PxTransform relTM = contactsData.mTransform1.transformInv(contactsData.mTransform0); + + const PxReal epsilon = 0.01f; + if( maxComponentDeltaPos(t0to1, relTM)(cachedBytes); + const PxVec3* normal0 = NULL; + for(PxU32 i=0;i(contacts); contacts += sizeof(PxVec3); + normal0 = cachedNormal; + } + else + { + cachedNormal = normal0; + } + + const PxVec3* cachedPoint = reinterpret_cast(contacts); contacts += sizeof(PxVec3); + const PxReal* cachedPD = reinterpret_cast(contacts); contacts += sizeof(PxReal); + + updateContact(*dst, contactsData, world0, world1, *cachedPoint, *cachedNormal, *cachedPD); + + if(contactsData.mUseFaceIndices) + { + const PxU32* cachedIndex1 = reinterpret_cast(contacts); contacts += sizeof(PxU32); + + dst->internalFaceIndex1 = *cachedIndex1; + } + else + { + dst->internalFaceIndex1 = PXC_CONTACT_NO_FACE_INDEX; + } + dst++; + } + } + if(ls) + PxcNpCacheWriteFinalize(ls, contactsData, nbCachedBytes, cachedBytes); +#ifdef ENABLE_CONTACT_CACHE_STATS + gNbHits++; +#endif + return true; + } + else + { + // PT: if we reach this point we cached the contacts but we couldn't use them next frame + // => waste of time and memory + } + } + + conMethod(shape0, shape1, tm0, tm1, params, pairContactCache, context.mContactBuffer, &context.mRenderOutput); + + //if(contactBuffer.count) + { + contactsData.mTransform0 = tm0; + contactsData.mTransform1 = tm1; + + PxU32 nbBytes = 0; + const PxU8* bytes = NULL; + const PxU32 count = contactBuffer.count; + if(count) + { + const bool useFaceIndices = contactBuffer.contacts[0].internalFaceIndex1!=PXC_CONTACT_NO_FACE_INDEX; + contactsData.mNbCachedContacts = Ps::to16(count); + contactsData.mUseFaceIndices = useFaceIndices; + + const Gu::ContactPoint* PX_RESTRICT srcContacts = contactBuffer.contacts; + // PT: this loop should not be here. We should output the contacts directly compressed, as we used to. + bool sameNormal = true; + { + const PxVec3 normal0 = srcContacts->normal; + for(PxU32 i=1;i(ls) = contactsData; + *reinterpret_cast(ls+payloadSize) = nbBytes; + bytes = ls+payloadSize+sizeof(PxU32); + PxU8* dest = const_cast(bytes); + for(PxU32 i=0;i> 1; + const bool isFirstTriangle = (triangleIndex & 0x1) == 0; + + //get sample + const PxHeightFieldSample* hf = &hfData->samples[sampleIndex]; + return isFirstTriangle ? hf->materialIndex0 : hf->materialIndex1; +} + +bool physx::PxcGetMaterialHeightField(const PxsShapeCore* shape, const PxU32 index, PxcNpThreadContext& context, PxsMaterialInfo* materialInfo) +{ + PX_ASSERT(index == 1); + PX_UNUSED(index); + const ContactBuffer& contactBuffer = context.mContactBuffer; + const PxHeightFieldGeometryLL& hfGeom = shape->geometry.get(); + if(hfGeom.materials.numIndices <= 1) + { + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + (&materialInfo[i].mMaterialIndex0)[index] = shape->materialIndex; + } + } + else + { + const PxU16* materialIndices = hfGeom.materials.indices; + + const Gu::HeightFieldData* hf = hfGeom.heightFieldData; + + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + const Gu::ContactPoint& contact = contactBuffer.contacts[i]; + const PxU32 localMaterialIndex = GetMaterialIndex(hf, contact.internalFaceIndex1); + (&materialInfo[i].mMaterialIndex0)[index] = materialIndices[localMaterialIndex]; + } + } + return true; +} + +bool physx::PxcGetMaterialShapeHeightField(const PxsShapeCore* shape0, const PxsShapeCore* shape1, PxcNpThreadContext& context, PxsMaterialInfo* materialInfo) +{ + const ContactBuffer& contactBuffer = context.mContactBuffer; + const PxHeightFieldGeometryLL& hfGeom = shape1->geometry.get(); + if(hfGeom.materials.numIndices <= 1) + { + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + materialInfo[i].mMaterialIndex0 = shape0->materialIndex; + materialInfo[i].mMaterialIndex1 = shape1->materialIndex; + } + } + else + { + const PxU16* materialIndices = hfGeom.materials.indices; + + const Gu::HeightFieldData* hf = hfGeom.heightFieldData; + + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + const Gu::ContactPoint& contact = contactBuffer.contacts[i]; + materialInfo[i].mMaterialIndex0 = shape0->materialIndex; + //contact.featureIndex0 = shape0->materialIndex; + const PxU32 localMaterialIndex = GetMaterialIndex(hf, contact.internalFaceIndex1); + //contact.featureIndex1 = materialIndices[localMaterialIndex]; + PX_ASSERT(localMaterialIndexgeometry.get(); + if(shapeMesh.materials.numIndices <= 1) + { + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + (&materialInfo[i].mMaterialIndex0)[index] = shape->materialIndex; + } + } + else + { + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + + Gu::ContactPoint& contact = contactBuffer.contacts[i]; + const PxU16* eaMaterialIndices = shapeMesh.materialIndices; + + const PxU32 localMaterialIndex = eaMaterialIndices[contact.internalFaceIndex1];//shapeMesh.triangleMesh->getTriangleMaterialIndex(contact.featureIndex1); + (&materialInfo[i].mMaterialIndex0)[index] = shapeMesh.materials.indices[localMaterialIndex]; + } + } + return true; +} + +bool physx::PxcGetMaterialShapeMesh(const PxsShapeCore* shape0, const PxsShapeCore* shape1, PxcNpThreadContext& context, PxsMaterialInfo* materialInfo) +{ + + ContactBuffer& contactBuffer = context.mContactBuffer; + const PxTriangleMeshGeometryLL& shapeMesh = shape1->geometry.get(); +// const Gu::TriangleMesh* meshData = shapeMesh.meshData; + if(shapeMesh.materials.numIndices <= 1) + { + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + materialInfo[i].mMaterialIndex0 = shape0->materialIndex; + materialInfo[i].mMaterialIndex1 = shape1->materialIndex; + } + } + else + { + + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + + Gu::ContactPoint& contact = contactBuffer.contacts[i]; + //contact.featureIndex0 = shape0->materialIndex; + materialInfo[i].mMaterialIndex0 = shape0->materialIndex; + const PxU16* eaMaterialIndices = shapeMesh.materialIndices; + + const PxU32 localMaterialIndex = eaMaterialIndices[contact.internalFaceIndex1];//shapeMesh.triangleMesh->getTriangleMaterialIndex(contact.featureIndex1); + //contact.featureIndex1 = shapeMesh.materials.indices[localMaterialIndex]; + materialInfo[i].mMaterialIndex1 = shapeMesh.materials.indices[localMaterialIndex]; + + } + } + + return true; +} diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp new file mode 100644 index 000000000..3a8dfcb6c --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp @@ -0,0 +1,140 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxGeometry.h" +#include "PxcMaterialMethodImpl.h" + +namespace physx +{ +bool PxcGetMaterialShapeShape (MATERIAL_METHOD_ARGS); +bool PxcGetMaterialShapeMesh (MATERIAL_METHOD_ARGS); +bool PxcGetMaterialShapeHeightField (MATERIAL_METHOD_ARGS); +bool PxcGetMaterialShape (SINGLE_MATERIAL_METHOD_ARGS); +bool PxcGetMaterialMesh (SINGLE_MATERIAL_METHOD_ARGS); +bool PxcGetMaterialHeightField (SINGLE_MATERIAL_METHOD_ARGS); + + +PxcGetSingleMaterialMethod g_GetSingleMaterialMethodTable[PxGeometryType::eGEOMETRY_COUNT] = +{ + PxcGetMaterialShape, //PxGeometryType::eSPHERE + PxcGetMaterialShape, //PxGeometryType::ePLANE + PxcGetMaterialShape, //PxGeometryType::eCAPSULE + PxcGetMaterialShape, //PxGeometryType::eBOX + PxcGetMaterialShape, //PxGeometryType::eCONVEXMESH + PxcGetMaterialMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + PxcGetMaterialHeightField, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + +}; + +//Table of contact methods for different shape-type combinations +PxcGetMaterialMethod g_GetMaterialMethodTable[][PxGeometryType::eGEOMETRY_COUNT] = +{ + + //PxGeometryType::eSPHERE + { + PxcGetMaterialShapeShape, //PxGeometryType::eSPHERE + PxcGetMaterialShapeShape, //PxGeometryType::ePLANE + PxcGetMaterialShapeShape, //PxGeometryType::eCAPSULE + PxcGetMaterialShapeShape, //PxGeometryType::eBOX + PxcGetMaterialShapeShape, //PxGeometryType::eCONVEXMESH + PxcGetMaterialShapeMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + PxcGetMaterialShapeHeightField, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + + }, + + //PxGeometryType::ePLANE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + PxcGetMaterialShapeShape, //PxGeometryType::eCAPSULE + PxcGetMaterialShapeShape, //PxGeometryType::eBOX + PxcGetMaterialShapeShape, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + 0, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eCAPSULE + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + PxcGetMaterialShapeShape, //PxGeometryType::eCAPSULE + PxcGetMaterialShapeShape, //PxGeometryType::eBOX + PxcGetMaterialShapeShape, //PxGeometryType::eCONVEXMESH + PxcGetMaterialShapeMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + PxcGetMaterialShapeHeightField, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + }, + + //PxGeometryType::eBOX + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + PxcGetMaterialShapeShape, //PxGeometryType::eBOX + PxcGetMaterialShapeShape, //PxGeometryType::eCONVEXMESH + PxcGetMaterialShapeMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + PxcGetMaterialShapeHeightField, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + }, + + //PxGeometryType::eCONVEXMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + PxcGetMaterialShapeShape, //PxGeometryType::eCONVEXMESH + PxcGetMaterialShapeMesh, //PxGeometryType::eTRIANGLEMESH //not used: mesh always uses swept method for midphase. + PxcGetMaterialShapeHeightField, //PxGeometryType::eHEIGHTFIELD //TODO: make HF midphase that will mask this + }, + + //PxGeometryType::eTRIANGLEMESH + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + 0, //PxGeometryType::eHEIGHTFIELD + }, + + //PxGeometryType::eHEIGHTFIELD + { + 0, //PxGeometryType::eSPHERE + 0, //PxGeometryType::ePLANE + 0, //PxGeometryType::eCAPSULE + 0, //PxGeometryType::eBOX + 0, //PxGeometryType::eCONVEXMESH + 0, //PxGeometryType::eTRIANGLEMESH + 0, //PxGeometryType::eHEIGHTFIELD + }, + +}; + +} diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp new file mode 100644 index 000000000..8b948eb5e --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp @@ -0,0 +1,62 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxTriangleMesh.h" +#include "PxvGeometry.h" +#include "PxsMaterialManager.h" +#include "PxcNpThreadContext.h" +#include "GuHeightField.h" + +using namespace physx; +using namespace Gu; + +namespace physx +{ +bool PxcGetMaterialShape(const PxsShapeCore* shape, const PxU32 index, PxcNpThreadContext& context, PxsMaterialInfo* materialInfo) +{ + ContactBuffer& contactBuffer = context.mContactBuffer; + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + (&materialInfo[i].mMaterialIndex0)[index] = shape->materialIndex; + } + return true; +} + +bool PxcGetMaterialShapeShape(const PxsShapeCore* shape0, const PxsShapeCore* shape1, PxcNpThreadContext& context, PxsMaterialInfo* materialInfo) +{ + ContactBuffer& contactBuffer = context.mContactBuffer; + for(PxU32 i=0; i< contactBuffer.count; ++i) + { + materialInfo[i].mMaterialIndex0 = shape0->materialIndex; + materialInfo[i].mMaterialIndex1 = shape1->materialIndex; + } + return true; +} +} + diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp new file mode 100644 index 000000000..0b3441ad9 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp @@ -0,0 +1,445 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxcNpBatch.h" +#include "PxcNpWorkUnit.h" +#include "PxsContactManager.h" +#include "GuGeometryUnion.h" +#include "PxcContactCache.h" +#include "PxcMaterialMethodImpl.h" +#include "PxcNpContactPrepShared.h" +#include "PxvDynamics.h" // for PxsBodyCore +#include "PxvGeometry.h" // for PxsShapeCore +#include "CmFlushPool.h" +#include "CmTask.h" +#include "PxTriangleMesh.h" +#include "PxsMaterialManager.h" +#include "PxsTransformCache.h" +#include "GuPersistentContactManifold.h" +#include "PxsContactManagerState.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + + +static void startContacts(PxsContactManagerOutput& output, PxcNpThreadContext& context) +{ + context.mContactBuffer.reset(); + + output.contactForces = NULL; + output.contactPatches = NULL; + output.contactPoints = NULL; + output.nbContacts = 0; + output.nbPatches = 0; + output.statusFlag = 0; +} + +static void flipContacts(PxcNpThreadContext& threadContext, PxsMaterialInfo* PX_RESTRICT materialInfo) +{ + ContactBuffer& buffer = threadContext.mContactBuffer; + for(PxU32 i=0; imSharedDataIndex, PxI32(contactSize))); + + if(context.mContactStreamPool->isOverflown()) + { + PX_WARN_ONCE("Contact buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + contactPoints = context.mContactStreamPool->mDataStream + context.mContactStreamPool->mDataStreamSize - index; + + const PxU32 patchIndex = PxU32(Ps::atomicAdd(&context.mPatchStreamPool->mSharedDataIndex, PxI32(patchSize))); + + if(context.mPatchStreamPool->isOverflown()) + { + PX_WARN_ONCE("Patch buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + contactPatches = context.mPatchStreamPool->mDataStream + context.mPatchStreamPool->mDataStreamSize - patchIndex; + + if(forceSize) + { + index = PxU32(Ps::atomicAdd(&context.mForceAndIndiceStreamPool->mSharedDataIndex, PxI32(forceSize))); + + if(context.mForceAndIndiceStreamPool->isOverflown()) + { + PX_WARN_ONCE("Force buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + forceBuffer = reinterpret_cast(context.mForceAndIndiceStreamPool->mDataStream + context.mForceAndIndiceStreamPool->mDataStreamSize - index); + } + + if(isOverflown) + { + contactPatches = NULL; + contactPoints = NULL; + forceBuffer = NULL; + cmOutput.nbContacts = cmOutput.nbPatches = 0; + } + else + { + PxMemCopy(contactPatches, oldPatches, patchSize); + PxMemCopy(contactPoints, oldContacts, contactSize); + if (isMeshType) + { + PxMemCopy(forceBuffer + cmOutput.nbContacts, oldForces + cmOutput.nbContacts, sizeof(PxU32) * cmOutput.nbContacts); + } + } + } + else + { + const PxU32 alignedOldSize = (oldSize + 0xf) & 0xfffffff0; + + PxU8* data = context.mContactBlockStream.reserve(alignedOldSize + forceSize); + if(forceSize) + forceBuffer = reinterpret_cast(data + alignedOldSize); + + contactPatches = data; + contactPoints = data + cmOutput.nbPatches * sizeof(PxContactPatch); + + PxMemCopy(data, oldPatches, oldSize); + if (isMeshType) + { + PxMemCopy(forceBuffer + cmOutput.nbContacts, oldForces + cmOutput.nbContacts, sizeof(PxU32) * cmOutput.nbContacts); + } + } + + if(forceSize) + PxMemZero(forceBuffer, forceSize); + + cmOutput.contactPatches= contactPatches; + cmOutput.contactPoints = contactPoints; + cmOutput.contactForces = forceBuffer; + } + + if(cache.mCachedSize) + { + if(cache.isMultiManifold()) + { + PX_ASSERT((cache.mCachedSize & 0xF) == 0); + PxU8* newData = context.mNpCacheStreamPair.reserve(cache.mCachedSize); + PX_ASSERT((reinterpret_cast(newData)& 0xF) == 0); + PxMemCopy(newData, & cache.getMultipleManifold(), cache.mCachedSize); + cache.setMultiManifold(newData); + } + else if(useContactCache) + { + //Copy cache information as well... + const PxU8* cachedData = cache.mCachedData; + PxU8* newData = context.mNpCacheStreamPair.reserve(PxU32(cache.mCachedSize + 0xf) & 0xfff0); + PxMemCopy(newData, cachedData, cache.mCachedSize); + cache.mCachedData = newData; + } + } + return ret; +} + +//ML: isMeshType is used in the GPU codepath. If the collision pair is mesh/heightfield vs primitives, we need to allocate enough memory for the mForceAndIndiceStreamPool in the threadContext. +static bool finishContacts(const PxcNpWorkUnit& input, PxsContactManagerOutput& npOutput, PxcNpThreadContext& threadContext, PxsMaterialInfo* PX_RESTRICT pMaterials, const bool isMeshType) +{ + ContactBuffer& buffer = threadContext.mContactBuffer; + + PX_ASSERT((npOutput.statusFlag & PxsContactManagerStatusFlag::eTOUCH_KNOWN) != PxsContactManagerStatusFlag::eTOUCH_KNOWN); + PxU8 statusFlags = PxU16(npOutput.statusFlag & (~PxsContactManagerStatusFlag::eTOUCH_KNOWN)); + if (buffer.count != 0) + statusFlags |= PxsContactManagerStatusFlag::eHAS_TOUCH; + else + statusFlags |= PxsContactManagerStatusFlag::eHAS_NO_TOUCH; + + npOutput.nbContacts = Ps::to8(buffer.count); + + if(buffer.count==0) + { + npOutput.statusFlag = statusFlags; + npOutput.nbContacts = 0; + npOutput.nbPatches = 0; + return true; + } + +#if PX_ENABLE_SIM_STATS + if(buffer.count) + threadContext.mNbDiscreteContactPairsWithContacts++; +#endif + + npOutput.statusFlag = statusFlags; + + PxU32 contactForceByteSize = buffer.count * sizeof(PxReal); + + //Regardless of the flags, we need to now record the compressed contact stream + + PxU16 compressedContactSize; + + const bool createReports = + input.flags & PxcNpWorkUnitFlag::eOUTPUT_CONTACTS + || threadContext.mCreateContactStream + || (input.flags & PxcNpWorkUnitFlag::eFORCE_THRESHOLD); + + if(!buffer.count || (!isMeshType && !createReports)) + contactForceByteSize = 0; + + bool res = (writeCompressedContact(buffer.contacts, buffer.count, &threadContext, npOutput.nbContacts, npOutput.contactPatches, npOutput.contactPoints, compressedContactSize, + reinterpret_cast(npOutput.contactForces), contactForceByteSize, threadContext.mMaterialManager, ((input.flags & PxcNpWorkUnitFlag::eMODIFIABLE_CONTACT) != 0), + false, pMaterials, npOutput.nbPatches, 0, NULL, NULL, threadContext.mCreateAveragePoint, threadContext.mContactStreamPool, + threadContext.mPatchStreamPool, threadContext.mForceAndIndiceStreamPool, isMeshType) != 0) || (buffer.count == 0); + + //handle buffer overflow + if (buffer.count && !npOutput.nbContacts) + { + PxU8 thisStatusFlags = PxU16(npOutput.statusFlag & (~PxsContactManagerStatusFlag::eTOUCH_KNOWN)); + thisStatusFlags |= PxsContactManagerStatusFlag::eHAS_NO_TOUCH; + + npOutput.statusFlag = thisStatusFlags; + npOutput.nbContacts = 0; + npOutput.nbPatches = 0; +#if PX_ENABLE_SIM_STATS + if(buffer.count) + threadContext.mNbDiscreteContactPairsWithContacts--; +#endif + } + return res; +} + +template +static PX_FORCE_INLINE bool checkContactsMustBeGenerated(PxcNpThreadContext& context, const PxcNpWorkUnit& input, Gu::Cache& cache, PxsContactManagerOutput& output, + const PxsCachedTransform* cachedTransform0, const PxsCachedTransform* cachedTransform1, + const bool flip, PxGeometryType::Enum type0, PxGeometryType::Enum type1) +{ + PX_ASSERT(cachedTransform0->transform.isSane() && cachedTransform1->transform.isSane()); + + //ML : if user doesn't raise the eDETECT_DISCRETE_CONTACT, we should not generate contacts + if(!(input.flags & PxcNpWorkUnitFlag::eDETECT_DISCRETE_CONTACT)) + return false; + + if(!(output.statusFlag & PxcNpWorkUnitStatusFlag::eDIRTY_MANAGER) && !(input.flags & PxcNpWorkUnitFlag::eMODIFIABLE_CONTACT)) + { + const PxU32 body0Dynamic = PxU32(input.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY0); + const PxU32 body1Dynamic = PxU32(input.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1); + + const PxU32 active0 = PxU32(body0Dynamic && !cachedTransform0->isFrozen()); + const PxU32 active1 = PxU32(body1Dynamic && !cachedTransform1->isFrozen()); + + if(!(active0 || active1)) + { + if(flip) + Ps::swap(type0, type1); + + const bool useContactCache = useContactCacheT ? context.mContactCache && g_CanUseContactCache[type0][type1] : false; + +#if PX_ENABLE_SIM_STATS + if(output.nbContacts) + context.mNbDiscreteContactPairsWithContacts++; +#endif + const bool isMeshType = type1 > PxGeometryType::eCONVEXMESH; + copyBuffers(output, cache, context, useContactCache, isMeshType); + return false; + } + } + + output.statusFlag &= (~PxcNpWorkUnitStatusFlag::eDIRTY_MANAGER); + + const PxReal contactDist0 = context.mContactDistance[input.mTransformCache0]; + const PxReal contactDist1 = context.mContactDistance[input.mTransformCache1]; + //context.mNarrowPhaseParams.mContactDistance = shape0->contactOffset + shape1->contactOffset; + context.mNarrowPhaseParams.mContactDistance = contactDist0 + contactDist1; + + return true; +} + +template +static PX_FORCE_INLINE void discreteNarrowPhase(PxcNpThreadContext& context, const PxcNpWorkUnit& input, Gu::Cache& cache, PxsContactManagerOutput& output) +{ + PxGeometryType::Enum type0 = static_cast(input.geomType0); + PxGeometryType::Enum type1 = static_cast(input.geomType1); + + const bool flip = (type1getTransformCache(input.mTransformCache0); + const PxsCachedTransform* cachedTransform1 = &context.mTransformCache->getTransformCache(input.mTransformCache1); + + if(!checkContactsMustBeGenerated(context, input, cache, output, cachedTransform0, cachedTransform1, flip, type0, type1)) + return; + + PxsShapeCore* shape0 = const_cast(input.shapeCore0); + PxsShapeCore* shape1 = const_cast(input.shapeCore1); + + if(flip) + { + Ps::swap(type0, type1); + Ps::swap(shape0, shape1); + Ps::swap(cachedTransform0, cachedTransform1); + } + + PxsMaterialInfo materialInfo[ContactBuffer::MAX_CONTACTS]; + + Gu::MultiplePersistentContactManifold& manifold = context.mTempManifold; + bool isMultiManifold = false; + + if(!useLegacyCodepath) + { + if(cache.isMultiManifold()) + { + //We are using a multi-manifold. This is cached in a reduced npCache... + isMultiManifold = true; + uintptr_t address = uintptr_t(&cache.getMultipleManifold()); + manifold.fromBuffer(reinterpret_cast(address)); + cache.setMultiManifold(&manifold); + } + else if(cache.isManifold()) + { + void* address = reinterpret_cast(&cache.getManifold()); + Ps::prefetch(address); + Ps::prefetch(address, 128); + Ps::prefetch(address, 256); + } + } + + updateDiscreteContactStats(context, type0, type1); + + startContacts(output, context); + + const PxTransform* tm0 = &cachedTransform0->transform; + const PxTransform* tm1 = &cachedTransform1->transform; + PX_ASSERT(tm0->isSane() && tm1->isSane()); + + if(useLegacyCodepath) + { + // PT: many cache misses here... + + Ps::prefetchLine(shape1, 0); // PT: at least get rid of L2s for shape1 + + const PxcContactMethod conMethod = g_ContactMethodTable[type0][type1]; + PX_ASSERT(conMethod); + + const bool useContactCache = context.mContactCache && g_CanUseContactCache[type0][type1]; + if(useContactCache) + { +#if PX_ENABLE_SIM_STATS + if(PxcCacheLocalContacts(context, cache, *tm0, *tm1, conMethod, shape0->geometry, shape1->geometry)) + context.mNbDiscreteContactPairsWithCacheHits++; +#else + PxcCacheLocalContacts(context, n.pairCache, *tm0, *tm1, conMethod, shape0->geometry, shape1->geometry); +#endif + } + else + { + conMethod(shape0->geometry, shape1->geometry, *tm0, *tm1, context.mNarrowPhaseParams, cache, context.mContactBuffer, &context.mRenderOutput); + } + } + else + { + const PxcContactMethod conMethod = g_PCMContactMethodTable[type0][type1]; + PX_ASSERT(conMethod); + + conMethod(shape0->geometry, shape1->geometry, *tm0, *tm1, context.mNarrowPhaseParams, cache, context.mContactBuffer, &context.mRenderOutput); + } + + const PxcGetMaterialMethod materialMethod = g_GetMaterialMethodTable[type0][type1]; + PX_ASSERT(materialMethod); + + materialMethod(shape0, shape1, context, materialInfo); + + if(flip) + flipContacts(context, materialInfo); + + if(!useLegacyCodepath) + { + if(isMultiManifold) + { + //Store the manifold back... + const PxU32 size = (sizeof(MultiPersistentManifoldHeader) + + manifold.mNumManifolds * sizeof(SingleManifoldHeader) + + manifold.mNumTotalContacts * sizeof(Gu::CachedMeshPersistentContact)); + + PxU8* buffer = context.mNpCacheStreamPair.reserve(size); + + PX_ASSERT((reinterpret_cast(buffer)& 0xf) == 0); + manifold.toBuffer(buffer); + cache.setMultiManifold(buffer); + cache.mCachedSize = Ps::to16(size); + } + } + + const bool isMeshType = type1 > PxGeometryType::eCONVEXMESH; + finishContacts(input, output, context, materialInfo, isMeshType); +} + +void physx::PxcDiscreteNarrowPhase(PxcNpThreadContext& context, const PxcNpWorkUnit& input, Gu::Cache& cache, PxsContactManagerOutput& output) +{ + discreteNarrowPhase(context, input, cache, output); +} + +void physx::PxcDiscreteNarrowPhasePCM(PxcNpThreadContext& context, const PxcNpWorkUnit& input, Gu::Cache& cache, PxsContactManagerOutput& output) +{ + discreteNarrowPhase(context, input, cache, output); +} diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp new file mode 100644 index 000000000..a31890e22 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp @@ -0,0 +1,75 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxcNpCacheStreamPair.h" +#include "PsUserAllocated.h" +#include "PxcNpMemBlockPool.h" + +using namespace physx; + +void PxcNpCacheStreamPair::reset() +{ + mBlock = NULL; + mUsed = 0; +} + +PxcNpCacheStreamPair::PxcNpCacheStreamPair(PxcNpMemBlockPool& blockPool): + mBlockPool(blockPool), mBlock(NULL), mUsed(0) +{ +} + +// reserve can fail and return null. Read should never fail +PxU8* PxcNpCacheStreamPair::reserve(PxU32 size) +{ + size = (size+15)&~15; + + if(size>PxcNpMemBlock::SIZE) + { + return reinterpret_cast(-1); + } + + if(mBlock == NULL || mUsed + size > PxcNpMemBlock::SIZE) + { + mBlock = mBlockPool.acquireNpCacheBlock(); + mUsed = 0; + } + + PxU8* ptr; + if(mBlock == NULL) + ptr = 0; + else + { + ptr = mBlock->data+mUsed; + mUsed += size; + } + + return ptr; +} + diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp new file mode 100644 index 000000000..a824d6a93 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp @@ -0,0 +1,554 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsMathUtils.h" +#include "PxcNpWorkUnit.h" +#include "PxvDynamics.h" + +using namespace physx; +using namespace Gu; + +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" + +#include "PxcNpContactPrepShared.h" +#include "PsAtomic.h" +#include "PxsContactManagerState.h" + +#include "PsVecMath.h" +using namespace physx; +using namespace Ps::aos; + +static PX_FORCE_INLINE void copyContactPoint(PxContact* PX_RESTRICT point, const Gu::ContactPoint* PX_RESTRICT cp) +{ + // PT: TODO: consider moving "separation" right after "point" in both structures, to copy both at the same time. +// point->contact = cp->point; + const Vec4V contactV = V4LoadA(&cp->point.x); // PT: V4LoadA safe because 'point' is aligned. + V4StoreU(contactV, &point->contact.x); + + point->separation = cp->separation; +} + +struct StridePatch +{ + PxU8 startIndex; + PxU8 endIndex; + PxU8 nextIndex; + PxU8 totalCount; + bool isRoot; +}; + +PxU32 physx::writeCompressedContact(const Gu::ContactPoint* const PX_RESTRICT contactPoints, const PxU32 numContactPoints, PxcNpThreadContext* threadContext, + PxU8& writtenContactCount, PxU8*& outContactPatches, PxU8*& outContactPoints, PxU16& compressedContactSize, PxReal*& outContactForces, PxU32 contactForceByteSize, + const PxsMaterialManager* materialManager, bool hasModifiableContacts, bool forceNoResponse, PxsMaterialInfo* PX_RESTRICT pMaterial, PxU8& numPatches, + PxU32 additionalHeaderSize, PxsConstraintBlockManager* manager, PxcConstraintBlockStream* blockStream, bool insertAveragePoint, + PxcDataStreamPool* contactStreamPool, PxcDataStreamPool* patchStreamPool, PxcDataStreamPool* forceStreamPool, const bool isMeshType) +{ + if(numContactPoints == 0) + { + writtenContactCount = 0; + outContactPatches = NULL; + outContactPoints = NULL; + outContactForces = NULL; + compressedContactSize = 0; + numPatches = 0; + return 0; + } + + //Calculate the size of the contact buffer... + PX_ALLOCA(strPatches, StridePatch, numContactPoints); + + StridePatch* stridePatches = &strPatches[0]; + + PxU32 numStrideHeaders = 1; + + /*const bool hasInternalFaceIndex = contactPoints[0].internalFaceIndex0 != PXC_CONTACT_NO_FACE_INDEX || + contactPoints[0].internalFaceIndex1 != PXC_CONTACT_NO_FACE_INDEX;*/ + const bool isModifiable = !forceNoResponse && hasModifiableContacts; + + PxU32 totalUniquePatches = 1; + + PxU32 totalContactPoints = numContactPoints; + + PxU32 strideStart = 0; + bool root = true; + StridePatch* parentRootPatch = NULL; + { + const PxReal closeNormalThresh = PXC_SAME_NORMAL; + //Go through and tag how many patches we have... + PxVec3 normal = contactPoints[0].normal; + PxU16 mat0 = pMaterial[0].mMaterialIndex0; + PxU16 mat1 = pMaterial[0].mMaterialIndex1; + + for(PxU32 a = 1; a < numContactPoints; ++a) + { + if(normal.dot(contactPoints[a].normal) < closeNormalThresh || + pMaterial[a].mMaterialIndex0 != mat0 || pMaterial[a].mMaterialIndex1 != mat1) + { + StridePatch& patch = stridePatches[numStrideHeaders-1]; + + patch.startIndex = PxU8(strideStart); + patch.endIndex = PxU8(a); + patch.nextIndex = 0xFF; + patch.totalCount = PxU8(a - strideStart); + patch.isRoot = root; + if(parentRootPatch) + parentRootPatch->totalCount += PxU8(a - strideStart); + + root = true; + parentRootPatch = NULL; + for(PxU32 b = 1; b < numStrideHeaders; ++b) + { + StridePatch& thisPatch = stridePatches[b-1]; + if(thisPatch.isRoot) + { + PxU32 ind = thisPatch.startIndex; + PxReal dp2 = contactPoints[a].normal.dot(contactPoints[ind].normal); + if(dp2 >= closeNormalThresh && pMaterial[a].mMaterialIndex0 == pMaterial[ind].mMaterialIndex0 && + pMaterial[a].mMaterialIndex1 == pMaterial[ind].mMaterialIndex1) + { + PxU32 nextInd = b-1; + while(stridePatches[nextInd].nextIndex != 0xFF) + nextInd = stridePatches[nextInd].nextIndex; + stridePatches[nextInd].nextIndex = PxU8(numStrideHeaders); + root = false; + parentRootPatch = &stridePatches[b-1]; + break; + } + } + } + + normal = contactPoints[a].normal; + + mat0 = pMaterial[a].mMaterialIndex0; + mat1 = pMaterial[a].mMaterialIndex1; + totalContactPoints = insertAveragePoint && (a - strideStart) > 1 ? totalContactPoints + 1 : totalContactPoints; + strideStart = a; + numStrideHeaders++; + if(root) + totalUniquePatches++; + } + } + totalContactPoints = insertAveragePoint &&(numContactPoints - strideStart) > 1 ? totalContactPoints + 1 : totalContactPoints; + contactForceByteSize = insertAveragePoint && contactForceByteSize != 0 ? contactForceByteSize + sizeof(PxF32) * (totalContactPoints - numContactPoints) : contactForceByteSize; + } + { + StridePatch& patch = stridePatches[numStrideHeaders-1]; + patch.startIndex = PxU8(strideStart); + patch.endIndex = PxU8(numContactPoints); + patch.nextIndex = 0xFF; + patch.totalCount = PxU8(numContactPoints - strideStart); + patch.isRoot = root; + if(parentRootPatch) + parentRootPatch->totalCount += PxU8(numContactPoints - strideStart); + } + + numPatches = PxU8(totalUniquePatches); + + //Calculate the number of patches/points required + + const PxU32 patchHeaderSize = sizeof(PxContactPatch) * (isModifiable ? totalContactPoints : totalUniquePatches) + additionalHeaderSize; + const PxU32 pointSize = totalContactPoints * (isModifiable ? sizeof(PxModifiableContact) : sizeof(PxContact)); + + PxU32 requiredContactSize = pointSize; + PxU32 requiredPatchSize = patchHeaderSize; + PxU32 totalRequiredSize; + + PxU8* PX_RESTRICT contactData = NULL; + PxU8* PX_RESTRICT patchData = NULL; + PxReal* PX_RESTRICT forceData = NULL; + PxU32* PX_RESTRICT triangleIndice = NULL; + + if(contactStreamPool && !isModifiable && additionalHeaderSize == 0) //If the contacts are modifiable, we **DON'T** allocate them in GPU pinned memory. This will be handled later when they're modified + { + bool isOverflown = false; + + PxU32 contactIndex = PxU32(Ps::atomicAdd(&contactStreamPool->mSharedDataIndex, PxI32(requiredContactSize))); + + if (contactStreamPool->isOverflown()) + { + PX_WARN_ONCE("Contact buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + + contactData = contactStreamPool->mDataStream + contactStreamPool->mDataStreamSize - contactIndex; + + PxU32 patchIndex = PxU32(Ps::atomicAdd(&patchStreamPool->mSharedDataIndex, PxI32(requiredPatchSize))); + + if (patchStreamPool->isOverflown()) + { + PX_WARN_ONCE("Patch buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + + patchData = patchStreamPool->mDataStream + patchStreamPool->mDataStreamSize - patchIndex; + + if(contactForceByteSize) + { + contactForceByteSize = isMeshType ? contactForceByteSize * 2 : contactForceByteSize; + contactIndex = PxU32(Ps::atomicAdd(&forceStreamPool->mSharedDataIndex, PxI32(contactForceByteSize))); + + if (forceStreamPool->isOverflown()) + { + PX_WARN_ONCE("Force buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + forceData = reinterpret_cast(forceStreamPool->mDataStream + forceStreamPool->mDataStreamSize - contactIndex); + if (isMeshType) + triangleIndice = reinterpret_cast(forceData + numContactPoints); + } + + totalRequiredSize = requiredContactSize + requiredPatchSize; + + if (isOverflown) + { + patchData = NULL; + contactData = NULL; + forceData = NULL; + triangleIndice = NULL; + } + } + else + { + PxU32 alignedRequiredSize = (requiredContactSize + requiredPatchSize + 0xf) & 0xfffffff0; + contactForceByteSize = (isMeshType ? contactForceByteSize * 2 : contactForceByteSize); + PxU32 totalSize = alignedRequiredSize + contactForceByteSize; + PxU8* data = manager ? blockStream->reserve(totalSize, *manager) : threadContext->mContactBlockStream.reserve(totalSize); + + patchData = data; + contactData = patchData + requiredPatchSize; + + if(contactForceByteSize) + { + forceData = reinterpret_cast((data + alignedRequiredSize)); + + if (isMeshType) + triangleIndice = reinterpret_cast(forceData + numContactPoints); + + if(data) + { + PxMemZero(forceData, contactForceByteSize); + } + } + + totalRequiredSize = alignedRequiredSize; + + } + + Ps::prefetchLine(patchData); + Ps::prefetchLine(contactData); + + if(patchData == NULL) + { + writtenContactCount = 0; + outContactPatches = NULL; + outContactPoints = NULL; + outContactForces = NULL; + compressedContactSize = 0; + numPatches = 0; + return 0; + } + +#if PX_ENABLE_SIM_STATS + if(threadContext) + { + threadContext->mCompressedCacheSize += totalRequiredSize; + threadContext->mTotalCompressedCacheSize += totalRequiredSize; + } +#endif + compressedContactSize = Ps::to16(totalRequiredSize); + + + + //PxU32 startIndex = 0; + + //Extract first material + PxU16 origMatIndex0 = pMaterial[0].mMaterialIndex0; + PxU16 origMatIndex1 = pMaterial[0].mMaterialIndex1; + + PxReal staticFriction, dynamicFriction, combinedRestitution; + PxU32 materialFlags; + { + const PxsMaterialData& data0 = *materialManager->getMaterial(origMatIndex0); + const PxsMaterialData& data1 = *materialManager->getMaterial(origMatIndex1); + + combinedRestitution = PxsMaterialCombiner::combineRestitution(data0, data1); + PxsMaterialCombiner combiner(1.0f, 1.0f); + PxsMaterialCombiner::PxsCombinedMaterial combinedMat = combiner.combineIsotropicFriction(data0, data1); + staticFriction = combinedMat.staFriction; + dynamicFriction = combinedMat.dynFriction; + materialFlags = combinedMat.flags; + } + + + PxU8* PX_RESTRICT dataPlusOffset = patchData + additionalHeaderSize; + PxContactPatch* PX_RESTRICT patches = reinterpret_cast(dataPlusOffset); + PxU32* PX_RESTRICT faceIndice = triangleIndice; + + outContactPatches = patchData; + outContactPoints = contactData; + outContactForces = forceData; + + if(isModifiable) + { + + PxU32 flags = PxU32(isModifiable ? PxContactPatch::eMODIFIABLE : 0) | + (forceNoResponse ? PxContactPatch::eFORCE_NO_RESPONSE : 0) | + (isMeshType ? PxContactPatch::eHAS_FACE_INDICES : 0); + + PxU32 currentIndex = 0; + + PxModifiableContact* PX_RESTRICT point = reinterpret_cast(contactData); + + for(PxU32 a = 0; a < numStrideHeaders; ++a) + { + StridePatch& rootPatch = stridePatches[a]; + if(rootPatch.isRoot) + { + PxContactPatch* PX_RESTRICT patch = patches++; + + PxU32 startIndex = rootPatch.startIndex; + + const PxU16 matIndex0 = pMaterial[startIndex].mMaterialIndex0; + const PxU16 matIndex1 = pMaterial[startIndex].mMaterialIndex1; + if(matIndex0 != origMatIndex0 || matIndex1 != origMatIndex1) + { + const PxsMaterialData& data0 = *materialManager->getMaterial(matIndex0); + const PxsMaterialData& data1 = *materialManager->getMaterial(matIndex1); + + combinedRestitution = PxsMaterialCombiner::combineRestitution(data0, data1); + PxsMaterialCombiner combiner(1.0f, 1.0f); + PxsMaterialCombiner::PxsCombinedMaterial combinedMat = combiner.combineIsotropicFriction(data0, data1); + staticFriction = combinedMat.staFriction; + dynamicFriction = combinedMat.dynFriction; + materialFlags = combinedMat.flags; + origMatIndex0 = matIndex0; + origMatIndex1 = matIndex1; + } + + patch->nbContacts = rootPatch.totalCount; + + patch->startContactIndex = Ps::to8(currentIndex); + patch->materialFlags = PxU8(materialFlags); + patch->staticFriction = staticFriction; + patch->dynamicFriction = dynamicFriction; + patch->restitution = combinedRestitution; + patch->materialIndex0 = matIndex0; + patch->materialIndex1 = matIndex1; + patch->normal = contactPoints[0].normal; + patch->mMassModification.mInvMassScale0 = 1.0f; + patch->mMassModification.mInvMassScale1 = 1.0f; + patch->mMassModification.mInvInertiaScale0 = 1.0f; + patch->mMassModification.mInvInertiaScale1 = 1.0f; + patch->internalFlags = PxU8(flags); + + //const PxU32 endIndex = strideHeader[a]; + const PxU32 totalCountThisPatch = rootPatch.totalCount; + if(insertAveragePoint && totalCountThisPatch > 1) + { + PxVec3 avgPt(0.0f); + PxF32 avgPen(0.0f); + PxF32 recipCount = 1.0f/(PxF32(rootPatch.totalCount)); + + PxU32 index = a; + while(index != 0xFF) + { + StridePatch& p = stridePatches[index]; + for(PxU32 b = p.startIndex; b < p.endIndex; ++b) + { + avgPt += contactPoints[b].point; + avgPen += contactPoints[b].separation; + } + index = p.nextIndex; + } + + if (faceIndice) + { + StridePatch& p = stridePatches[index]; + *faceIndice = contactPoints[p.startIndex].internalFaceIndex1; + faceIndice++; + } + + patch->nbContacts++; + point->contact = avgPt * recipCount; + point->separation = avgPen * recipCount; + point->normal = contactPoints[0].normal; + point->maxImpulse = PX_MAX_REAL; + point->targetVelocity = PxVec3(0.0f); + point->staticFriction = staticFriction; + point->dynamicFriction = dynamicFriction; + point->restitution = combinedRestitution; + point->materialFlags = materialFlags; + point->materialIndex0 = matIndex0; + point->materialIndex1 = matIndex1; + point++; + currentIndex++; + Ps::prefetchLine(point, 128); + } + + PxU32 index = a; + while(index != 0xFF) + { + StridePatch& p = stridePatches[index]; + + for(PxU32 b = p.startIndex; b < p.endIndex; ++b) + { + copyContactPoint(point, &contactPoints[b]); + point->normal = contactPoints[b].normal; + point->maxImpulse = PX_MAX_REAL; + point->targetVelocity = PxVec3(0.0f); + point->staticFriction = staticFriction; + point->dynamicFriction = dynamicFriction; + point->restitution = combinedRestitution; + point->materialFlags = materialFlags; + point->materialIndex0 = matIndex0; + point->materialIndex1 = matIndex1; + if (faceIndice) + { + *faceIndice = contactPoints[b].internalFaceIndex1; + faceIndice++; + } + point++; + currentIndex++; + Ps::prefetchLine(point, 128); + } + index = p.nextIndex; + } + } + } + } + else + { + PxU32 flags = PxU32(isMeshType ? PxContactPatch::eHAS_FACE_INDICES : 0); + + PxContact* PX_RESTRICT point = reinterpret_cast(contactData); + + PxU32 currentIndex = 0; + { + for(PxU32 a = 0; a < numStrideHeaders; ++a) + { + StridePatch& rootPatch = stridePatches[a]; + + if(rootPatch.isRoot) + { + const PxU16 matIndex0 = pMaterial[rootPatch.startIndex].mMaterialIndex0; + const PxU16 matIndex1 = pMaterial[rootPatch.startIndex].mMaterialIndex1; + if(matIndex0 != origMatIndex0 || matIndex1 != origMatIndex1) + { + const PxsMaterialData& data0 = *materialManager->getMaterial(matIndex0); + const PxsMaterialData& data1 = *materialManager->getMaterial(matIndex1); + + combinedRestitution = PxsMaterialCombiner::combineRestitution(data0, data1); + PxsMaterialCombiner combiner(1.0f, 1.0f); + PxsMaterialCombiner::PxsCombinedMaterial combinedMat = combiner.combineIsotropicFriction(data0, data1); + staticFriction = combinedMat.staFriction; + dynamicFriction = combinedMat.dynFriction; + materialFlags = combinedMat.flags; + origMatIndex0 = matIndex0; + origMatIndex1 = matIndex1; + } + + PxContactPatch* PX_RESTRICT patch = patches++; + patch->normal = contactPoints[rootPatch.startIndex].normal; + patch->nbContacts = rootPatch.totalCount; + patch->startContactIndex = Ps::to8(currentIndex); + //KS - we could probably compress this further into the header but the complexity might not be worth it + patch->staticFriction = staticFriction; + patch->dynamicFriction = dynamicFriction; + patch->restitution = combinedRestitution; + patch->materialIndex0 = matIndex0; + patch->materialIndex1 = matIndex1; + patch->materialFlags = PxU8(materialFlags); + patch->internalFlags = PxU8(flags); + patch->mMassModification.mInvMassScale0 = 1.0f; + patch->mMassModification.mInvMassScale1 = 1.0f; + patch->mMassModification.mInvInertiaScale0 = 1.0f; + patch->mMassModification.mInvInertiaScale1 = 1.0f; + if(insertAveragePoint && (rootPatch.totalCount) > 1) + { + patch->nbContacts++; + PxVec3 avgPt(0.0f); + PxF32 avgPen(0.0f); + PxF32 recipCount = 1.0f/(PxF32(rootPatch.totalCount)); + PxU32 index = a; + + while(index != 0xFF) + { + StridePatch& p = stridePatches[index]; + for(PxU32 b = p.startIndex; b < p.endIndex; ++b) + { + avgPt += contactPoints[b].point; + avgPen += contactPoints[b].separation; + } + index = stridePatches[index].nextIndex; + } + + if (faceIndice) + { + StridePatch& p = stridePatches[index]; + *faceIndice = contactPoints[p.startIndex].internalFaceIndex1; + faceIndice++; + } + point->contact = avgPt * recipCount; + point->separation = avgPen * recipCount; + + point++; + currentIndex++; + Ps::prefetchLine(point, 128); + } + + PxU32 index = a; + while(index != 0xFF) + { + StridePatch& p = stridePatches[index]; + for(PxU32 b = p.startIndex; b < p.endIndex; ++b) + { + copyContactPoint(point, &contactPoints[b]); + if (faceIndice) + { + *faceIndice = contactPoints[b].internalFaceIndex1; + faceIndice++; + } + point++; + currentIndex++; + Ps::prefetchLine(point, 128); + } + index = stridePatches[index].nextIndex; + } + } + } + } + } + + writtenContactCount = Ps::to8(totalContactPoints); + + return totalRequiredSize; +} diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp new file mode 100644 index 000000000..2c8b0160c --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp @@ -0,0 +1,351 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxMath.h" +#include "PxcNpMemBlockPool.h" +#include "PsUserAllocated.h" +#include "PsInlineArray.h" +#include "PsFoundation.h" + +using namespace physx; + +PxcNpMemBlockPool::PxcNpMemBlockPool(PxcScratchAllocator& allocator): + mConstraints(PX_DEBUG_EXP("PxcNpMemBlockPool::mConstraints")), + mExceptionalConstraints(PX_DEBUG_EXP("PxcNpMemBlockPool::mExceptionalConstraints")), + mNpCacheActiveStream(0), + mFrictionActiveStream(0), + mCCDCacheActiveStream(0), + mContactIndex(0), + mAllocatedBlocks(0), + mMaxBlocks(0), + mUsedBlocks(0), + mMaxUsedBlocks(0), + mScratchBlockAddr(0), + mNbScratchBlocks(0), + mScratchAllocator(allocator), + mPeakConstraintAllocations(0), + mConstraintAllocations(0) +{ +} + +void PxcNpMemBlockPool::init(PxU32 initialBlockCount, PxU32 maxBlocks) +{ + mMaxBlocks = maxBlocks; + mInitialBlocks = initialBlockCount; + + PxU32 reserve = PxMax(initialBlockCount, 64); + + mConstraints.reserve(reserve); + mExceptionalConstraints.reserve(16); + + mFriction[0].reserve(reserve); + mFriction[1].reserve(reserve); + mNpCache[0].reserve(reserve); + mNpCache[1].reserve(reserve); + mUnused.reserve(reserve); + + setBlockCount(initialBlockCount); +} + +PxU32 PxcNpMemBlockPool::getUsedBlockCount() const +{ + return mUsedBlocks; +} + +PxU32 PxcNpMemBlockPool::getMaxUsedBlockCount() const +{ + return mMaxUsedBlocks; +} + +PxU32 PxcNpMemBlockPool::getPeakConstraintBlockCount() const +{ + return mPeakConstraintAllocations; +} + + +void PxcNpMemBlockPool::setBlockCount(PxU32 blockCount) +{ + Ps::Mutex::ScopedLock lock(mLock); + PxU32 current = getUsedBlockCount(); + for(PxU32 i=current;i(PX_ALLOC(PxcNpMemBlock::SIZE, "PxcNpMemBlock"))); + mAllocatedBlocks++; + } +} + +void PxcNpMemBlockPool::releaseUnusedBlocks() +{ + Ps::Mutex::ScopedLock lock(mLock); + while(mUnused.size()) + { + PX_FREE(mUnused.popBack()); + mAllocatedBlocks--; + } +} + + +PxcNpMemBlockPool::~PxcNpMemBlockPool() +{ + // swapping twice guarantees all blocks are released from the stream pairs + swapFrictionStreams(); + swapFrictionStreams(); + + swapNpCacheStreams(); + swapNpCacheStreams(); + + releaseConstraintMemory(); + releaseContacts(); + releaseContacts(); + + PX_ASSERT(mUsedBlocks == 0); + + flushUnused(); +} + +void PxcNpMemBlockPool::acquireConstraintMemory() +{ + PxU32 size; + void* addr = mScratchAllocator.allocAll(size); + size = size&~(PxcNpMemBlock::SIZE-1); + + PX_ASSERT(mScratchBlocks.size()==0); + mScratchBlockAddr = reinterpret_cast(addr); + mNbScratchBlocks = size/PxcNpMemBlock::SIZE; + + mScratchBlocks.resize(mNbScratchBlocks); + for(PxU32 i=0;i0); + mUsedBlocks--; + } + } + + for(PxU32 i=0;i0) + { + PxcNpMemBlock* block = mScratchBlocks.popBack(); + trackingArray.pushBack(block); + return block; + } + + + if(mUnused.size()) + { + PxcNpMemBlock* block = mUnused.popBack(); + trackingArray.pushBack(block); + mMaxUsedBlocks = PxMax(mUsedBlocks+1, mMaxUsedBlocks); + mUsedBlocks++; + return block; + } + + + if(mAllocatedBlocks == mMaxBlocks) + { +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Reached maximum number of allocated blocks so 16k block allocation will fail!"); +#endif + return NULL; + } + +#if PX_CHECKED + if(mInitialBlocks) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Number of required 16k memory blocks has exceeded the initial number of blocks. Allocator is being called. Consider increasing the number of pre-allocated 16k blocks."); + } +#endif + + // increment here so that if we hit the limit in separate threads we won't overallocated + mAllocatedBlocks++; + + PxcNpMemBlock* block = reinterpret_cast(PX_ALLOC(sizeof(PxcNpMemBlock), "PxcNpMemBlock")); + + if(block) + { + trackingArray.pushBack(block); + mMaxUsedBlocks = PxMax(mUsedBlocks+1, mMaxUsedBlocks); + mUsedBlocks++; + } + else + mAllocatedBlocks--; + + return block; +} + +PxU8* PxcNpMemBlockPool::acquireExceptionalConstraintMemory(PxU32 size) +{ + PxU8* memory = reinterpret_cast(PX_ALLOC(size, "PxcNpExceptionalMemory")); + if(memory) + { + Ps::Mutex::ScopedLock lock(mLock); + mExceptionalConstraints.pushBack(memory); + } + return memory; +} + +void PxcNpMemBlockPool::release(PxcNpMemBlockArray& deadArray, PxU32* allocationCount) +{ + Ps::Mutex::ScopedLock lock(mLock); + PX_ASSERT(mUsedBlocks >= deadArray.size()); + mUsedBlocks -= deadArray.size(); + if(allocationCount) + { + *allocationCount -= deadArray.size(); + } + while(deadArray.size()) + { + PxcNpMemBlock* block = deadArray.popBack(); + for(PxU32 a = 0; a < mUnused.size(); ++a) + { + PX_ASSERT(mUnused[a] != block); + } + mUnused.pushBack(block); + } +} + +void PxcNpMemBlockPool::flushUnused() +{ + while(mUnused.size()) + PX_FREE(mUnused.popBack()); +} + + +PxcNpMemBlock* PxcNpMemBlockPool::acquireConstraintBlock() +{ + // we track the scratch blocks in the constraint block array, because the code in acquireMultipleConstraintBlocks + // assumes that acquired blocks are listed there. + + return acquire(mConstraints); +} + +PxcNpMemBlock* PxcNpMemBlockPool::acquireConstraintBlock(PxcNpMemBlockArray& memBlocks) +{ + return acquire(memBlocks, &mConstraintAllocations, &mPeakConstraintAllocations, true); +} + +PxcNpMemBlock* PxcNpMemBlockPool::acquireContactBlock() +{ + return acquire(mContacts[mContactIndex], NULL, NULL, true); +} + + +void PxcNpMemBlockPool::releaseConstraintBlocks(PxcNpMemBlockArray& memBlocks) +{ + Ps::Mutex::ScopedLock lock(mLock); + + while(memBlocks.size()) + { + PxcNpMemBlock* block = memBlocks.popBack(); + if(mScratchAllocator.isScratchAddr(block)) + mScratchBlocks.pushBack(block); + else + { + mUnused.pushBack(block); + PX_ASSERT(mUsedBlocks>0); + mUsedBlocks--; + } + } +} + +void PxcNpMemBlockPool::releaseContacts() +{ + //releaseConstraintBlocks(mContacts); + release(mContacts[1-mContactIndex]); + mContactIndex = 1-mContactIndex; +} + +PxcNpMemBlock* PxcNpMemBlockPool::acquireFrictionBlock() +{ + return acquire(mFriction[mFrictionActiveStream]); +} + +void PxcNpMemBlockPool::swapFrictionStreams() +{ + release(mFriction[1-mFrictionActiveStream]); + mFrictionActiveStream = 1-mFrictionActiveStream; +} + +PxcNpMemBlock* PxcNpMemBlockPool::acquireNpCacheBlock() +{ + return acquire(mNpCache[mNpCacheActiveStream]); +} + +void PxcNpMemBlockPool::swapNpCacheStreams() +{ + release(mNpCache[1-mNpCacheActiveStream]); + mNpCacheActiveStream = 1-mNpCacheActiveStream; +} + diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp new file mode 100644 index 000000000..9a20f7ea7 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxcConstraintBlockStream.h" +#include "PxcNpThreadContext.h" + +using namespace physx; + +PxcNpThreadContext::PxcNpThreadContext(PxcNpContext* params) : + mRenderOutput (params->mRenderBuffer), + mContactBlockStream (params->mNpMemBlockPool), + mNpCacheStreamPair (params->mNpMemBlockPool), + mNarrowPhaseParams (0.0f, params->mMeshContactMargin, params->mToleranceLength), + mPCM (false), + mContactCache (false), + mCreateContactStream (params->mCreateContactStream), + mCreateAveragePoint (false), +#if PX_ENABLE_SIM_STATS + mCompressedCacheSize (0), + mNbDiscreteContactPairsWithCacheHits(0), + mNbDiscreteContactPairsWithContacts (0), +#endif + mMaxPatches (0), + mTotalCompressedCacheSize (0), + mContactStreamPool (params->mContactStreamPool), + mPatchStreamPool (params->mPatchStreamPool), + mForceAndIndiceStreamPool (params->mForceAndIndiceStreamPool), + mMaterialManager(params->mMaterialManager), + mLocalNewTouchCount (0), + mLocalLostTouchCount (0), + mLocalFoundPatchCount (0), + mLocalLostPatchCount (0) +{ +#if PX_ENABLE_SIM_STATS + clearStats(); +#endif +} + +PxcNpThreadContext::~PxcNpThreadContext() +{ +} + +#if PX_ENABLE_SIM_STATS +void PxcNpThreadContext::clearStats() +{ + PxMemSet(mDiscreteContactPairs, 0, sizeof(mDiscreteContactPairs)); + PxMemSet(mModifiedContactPairs, 0, sizeof(mModifiedContactPairs)); + mCompressedCacheSize = 0; + mNbDiscreteContactPairsWithCacheHits = 0; + mNbDiscreteContactPairsWithContacts = 0; +} +#endif + +void PxcNpThreadContext::reset(PxU32 cmCount) +{ + mContactBlockStream.reset(); + mNpCacheStreamPair.reset(); + + mLocalChangeTouch.clear(); + mLocalChangeTouch.resize(cmCount); + mLocalPatchCountChange.clear(); + mLocalPatchCountChange.resize(cmCount); + mLocalNewTouchCount = 0; + mLocalLostTouchCount = 0; + mLocalFoundPatchCount = 0; + mLocalLostPatchCount = 0; +} diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h b/src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h new file mode 100644 index 000000000..12bc731da --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h @@ -0,0 +1,51 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_BODYSIM_H +#define PXS_BODYSIM_H + +#include "PxsRigidBody.h" + +namespace physx +{ + +struct PxsBodySim +{ +public: + PxsBodySim() : mRigidBody(NULL)/*, mBodySimIndex(0xffffffff), mUpdated(0)*/ + { + } + + PxsRigidBody* mRigidBody; //4 or 8 + //PxU32 mUpdated; //12 or 16 +}; + +}//physx + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h b/src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h new file mode 100644 index 000000000..883c49732 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h @@ -0,0 +1,622 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "Ps.h" +#include "PxGeometry.h" +#include "GuCCDSweepConvexMesh.h" +#include "PsHashMap.h" +#include "PxsIslandSim.h" + +#ifndef PXS_CCD_H +#define PXS_CCD_H + +#define CCD_DEBUG_PRINTS 0 +#define CCD_POST_DEPENETRATE_DIST 0.001f +#define CCD_ROTATION_LOCKING 0 +#define CCD_MIN_TIME_LEFT 0.01f +#define CCD_ANGULAR_IMPULSE 0 + +#define DEBUG_RENDER_CCD 0 + +#if CCD_DEBUG_PRINTS +namespace physx { + extern void printCCDDebug(const char* msg, const PxsRigidBody* atom0, PxGeometryType::Enum g0, bool printPtr = true); + extern void printShape(PxsRigidBody* atom0, PxGeometryType::Enum g0, const char* annotation, PxReal dt, PxU32 pass, bool printPtr = true); +} +#define PRINTCCDSHAPE(x) printShape x +#define PRINTCCDDEBUG(x) printCCDDebug x +#else +#define PRINTCCDSHAPE(x) +#define PRINTCCDDEBUG(x) +#endif + +namespace physx +{ + +// ------------------------------------------------------------------------------------------------------------ +// a fraction of objects will be CCD active so this is dynamic, not a member of PsxRigidBody +// CCD code builds a temporary array of PxsCCDPair objects (allocated in blocks) +// this is done to gather scattered data from memory and also to reduce PxsRidigBody permanent memory footprint +// we have to do it every pass since new CMs can become fast moving after each pass (and sometimes cease to be) +// +struct PxsCCDBody; +class PxsRigidBody; +struct PxsShapeCore; +struct PxsRigidCore; +class PxsContactManager; +class PxsContext; +class PxCCDContactModifyCallback; +class PxcNpThreadContext; + +class PxvNphaseImplementationContext; + +namespace Dy +{ + class ThresholdStream; +} + + +/** +\brief structure to represent interactions between a given body and another body. +*/ +struct PxsCCDOverlap +{ + //The body the interaction relates to + PxsCCDBody* mBody; + //The next interaction in the list + PxsCCDOverlap* mNext; +}; + +/** +\brief Temporary CCD representation for a shape. + +Stores data about a shape that may be frequently used in CCD. It also stores update counters per-shape that can be compared with the body's update +counter to determine if the shape needs its transforms re-calculated. This avoids us needing to store a list of shapes in a CCD body. +*/ +struct PxsCCDShape : public Gu::CCDShape +{ +public: + const PxsShapeCore* mShapeCore; //Shape core (can be shared) + const PxsRigidCore* mRigidCore; //Rigid body core + IG::NodeIndex mNodeIndex; + + /** + \brief Returns the world-space pose for this shape + \param[in] atom The rigid body that this CCD shape is associated with + */ + PxTransform getAbsPose(const PxsRigidBody* atom) const; + /** + \brief Returns the world-space previous pose for this shape + \param[in] atom The rigid body that this CCD shape is associated with + */ + PxTransform getLastCCDAbsPose(const PxsRigidBody* atom) const; +}; + +/** +\brief Structure to represent a body in the CCD system. +*/ +struct PxsCCDBody +{ + Cm::SpatialVector mPreSolverVelocity; + PxU16 mIndex; //The CCD body's index + bool mPassDone; //Whether it has been processed in the current CCD pass + bool mHasAnyPassDone; //Whether this body was influenced by any passes + PxReal mTimeLeft; //CCD time left to elapse (normalized in range 0-1) + PxsRigidBody* mBody; //The rigid body + PxsCCDOverlap* mOverlappingObjects; //A list of overlapping bodies for island update + PxU32 mUpdateCount; //How many times this body has eben updated in the CCD. This is correlated with CCD shapes' update counts. + PxU32 mNbInteractionsThisPass; //How many interactions this pass + + + + /** + \brief Returns the CCD body's index. + \return The CCD body's index. + */ + PX_FORCE_INLINE PxU32 getIndex() const { return mIndex; } + + /** + \brief Tests whether this body has already registered an overlap with a given body. + \param[in] body The body to test against. + \return Whether this body has already registered an overlap with a given body. + */ + bool overlaps(PxsCCDBody* body) const + { + PxsCCDOverlap* overlaps = mOverlappingObjects; + + while(overlaps) + { + if(overlaps->mBody == body) + return true; + overlaps = overlaps->mNext; + } + return false; + } + + /** + \brief Registers an overlap with a given body + \param[in] overlap The CCD overlap to register. + */ + void addOverlap(PxsCCDOverlap* overlap) + { + overlap->mNext = mOverlappingObjects; + mOverlappingObjects = overlap; + } + +}; + +/** +\brief a container class used in the CCD that minimizes frequency of hitting the allocator. + +This class stores a set of blocks of memory. It is effectively an array that resizes more efficiently because it doesn't need to +reallocate an entire buffer and copy data. +*/ +template +struct PxsCCDBlockArray +{ + /** + \brief A block of data + */ + struct Block : Ps::UserAllocated { T items[BLOCK_SIZE]; }; + /** + \brief A header for a block of data. + */ + struct BlockInfo + { + Block* block; + PxU32 count; // number of elements in this block + BlockInfo(Block* aBlock, PxU32 aCount) : block(aBlock), count(aCount) {} + }; + /* + \brief An array of block headers + */ + Ps::Array blocks; + /** + \brief The current block. + */ + PxU32 currentBlock; + + /** + \brief Constructor + */ + PxsCCDBlockArray() : currentBlock(0) + { + blocks.pushBack(BlockInfo(PX_NEW(Block), 0)); + } + + /** + \brief Destructor + */ + ~PxsCCDBlockArray() + { + for (PxU32 i = 0; i < blocks.size(); i++) + { + PX_DELETE(blocks[i].block); + } + currentBlock = 0; + } + + /** + \brief Clears this block array. + \note This clear function also deletes all additional blocks + */ + void clear() + { + for (PxU32 i = 0; i < blocks.size(); i++) + { + PX_DELETE(blocks[i].block); + } + blocks.clear(); + blocks.pushBack(BlockInfo(PX_NEW(Block), 0)); // at least one block is expected to always be present in the array + currentBlock = 0; + } + + /** + \brief Clears this block array but does not release the memory. + */ + void clear_NoDelete() + { + currentBlock = 0; + blocks[0].count = 0; + } + + /** + \brief Push a new element onto the back of the block array + \return The new element + */ + T& pushBack() + { + PxU32 numBlocks = blocks.size(); + if (blocks[currentBlock].count == BLOCK_SIZE) + { + if((currentBlock + 1) == numBlocks) + { + blocks.pushBack(BlockInfo(PX_NEW(Block), 0)); + numBlocks ++; + } + currentBlock++; + blocks[currentBlock].count = 0; + } + const PxU32 count = blocks[currentBlock].count ++; + + return blocks[currentBlock].block->items[count]; + } + + /** + \brief Pushes a new element onto the back of this array, intitializing it to match the data + \param data The data to initialize the new element to + \return The new element + */ + T& pushBack(T& data) + { + PxU32 numBlocks = blocks.size(); + if (blocks[currentBlock].count == BLOCK_SIZE) + { + if((currentBlock + 1) == numBlocks) + { + blocks.pushBack(BlockInfo(PX_NEW(Block), 0)); + numBlocks ++; + } + currentBlock++; + blocks[currentBlock].count = 0; + } + const PxU32 count = blocks[currentBlock].count ++; + blocks[currentBlock].block->items[count] = data; + return blocks[currentBlock].block->items[count]; + } + + /** + \brief Pops the last element from the list. + */ + void popBack() + { + PX_ASSERT(blocks[currentBlock].count > 0); + if (blocks[currentBlock].count > 1) + blocks[currentBlock].count --; + else + { + PX_DELETE(blocks[currentBlock].block); + blocks.popBack(); + currentBlock--; + } + } + + /** + \brief Returns the current size of the array. + \return The current size of the array. + */ + PxU32 size() const + { + return (currentBlock)*BLOCK_SIZE + blocks[currentBlock].count; + } + + /** + \brief Returns the element at a given index in the array + \param[in] index The index of the element in the array + \return The element at a given index in the array. + */ + T& operator[] (PxU32 index) const + { + PX_ASSERT(index/BLOCK_SIZE < blocks.size()); + PX_ASSERT(index%BLOCK_SIZE < blocks[index/BLOCK_SIZE].count); + return blocks[index/BLOCK_SIZE].block->items[index%BLOCK_SIZE]; + } +}; + +/** +\brief A structure to represent a potential CCD interaction between a pair of shapes +*/ +struct PxsCCDPair +{ + /** + \brief Defines whether this is an estimated TOI or an accurate TOI. + + We store pairs in a priority queue based on the TOIs. We use cheap estimates to cull away work and lazily evaluate TOIs. This means that an element in the + priority queue may either be an estimate or a precise result. + */ + enum E_TOIType + { + eEstimate, + ePrecise + }; + PxsRigidBody* mBa0; // Body A. Can be NULL for statics + PxsRigidBody* mBa1; // Body B. Can be NULL for statics + PxsCCDShape* mCCDShape0; // Shape A + PxsCCDShape* mCCDShape1; // Shape B + PxVec3 mMinToiNormal; // The contact normal. Only valid for precise results. On the surface of body/shape A + PxReal mMinToi; // Min TOI. Valid for both precise and estimated results but estimates may be too early (i.e. conservative). + PxReal mPenetrationPostStep; // Valid only for precise sweeps. Only used for initial intersections (i.e. at TOI = 0). + PxVec3 mMinToiPoint; // The contact point. Only valid for precise sweep results. + PxReal mPenetration; // The penetration. Only valid for precise sweep results. + PxsContactManager* mCm; // The contact manager. + PxU32 mIslandId; // The index of the island this pair is in + PxGeometryType::Enum mG0, mG1; // The geometry types for shapes 0 and 1 + bool mIsEarliestToiHit; // Indicates this was the earliest hit for one of the bodies in the pair + bool mIsModifiable; // Indicates whether this contact is modifiable + PxU32 mFaceIndex; // The face index. Only valid for precise sweeps involving meshes or heightfields. + PxU16 mMaterialIndex0; // The material index for shape 0 + PxU16 mMaterialIndex1; // The material index for shape 1 + PxReal mDynamicFriction; // The dynamic friction coefficient + PxReal mStaticFriction; // The static friction coefficient + PxReal mRestitution; // The restitution coefficient + PxU32 mEstimatePass; // The current estimation pass. Used after a sweep hit was found to determine if the pair needs re-estimating. + PxReal mAppliedForce; // The applied force for this pair. Only valid if the pair has been responded to. + PxReal mMaxImpulse; // The maximum impulse to be applied + + E_TOIType mToiType; // The TOI type (estimate, precise). + bool mHasFriction; // Whether we want to simulate CCD friction for this pair + + /** + \brief Perform a precise sweep for this pair + \param[in] threadContext The per-thread context + \param[in] dt The time-step + \param[in] pass The current CCD pass + \return The normalized TOI. <=1.0 indicates a hit. Otherwise PX_MAX_REAL. + */ + PxReal sweepFindToi(PxcNpThreadContext& threadContext, PxReal dt, PxU32 pass); + /** + \brief Performs a sweep estimation for this pair + \return The normalized TOI. <= 1.0 indicates a potential hit, otherwise PX_MAX_REAL. + */ + PxReal sweepEstimateToi(); + /** + \brief Advances this pair to the TOI + \param[in] dt The time-step + \param[in] clipTrajectoryToToi Indicates whether we clip the body's trajectory to the end pose. Only done in the final pass + \return Whether the advance was successful. An advance will be unsuccessful if body bodies were already updated. + */ + bool sweepAdvanceToToi(PxReal dt, bool clipTrajectoryToToi); + /** + \brief Updates the transforms of the shapes involved in this pair. + */ + void updateShapes(); + +}; + +/** +\brief Block array of CCD bodies +*/ +typedef PxsCCDBlockArray PxsCCDBodyArray; +/** +\brief Block array of CCD pairs +*/ +typedef PxsCCDBlockArray PxsCCDPairArray; +/** +\brief Block array of CCD overlaps +*/ +typedef PxsCCDBlockArray PxsCCDOverlapArray; +/** +\brief Block array of CCD shapes +*/ +typedef PxsCCDBlockArray PxsCCDShapeArray; + +/** +\brief Pair structure to be able to look-up a rigid body-shape pair in a map +*/ +typedef Ps::Pair PxsRigidShapePair; + + +/** +\brief CCD context object. +*/ +class PxsCCDContext +{ +public: + + /** + \brief Creates this PxsCCDContext + */ + static PxsCCDContext* create(PxsContext* context, Dy::ThresholdStream& dynamicsContext, PxvNphaseImplementationContext& nPhaseContext); + + /** + \brief Destroys this PxsCCDContext + */ + void destroy(); + + /** + \brief Returns the CCD contact modification callback + \return The CCD contact modification callback + */ + PX_FORCE_INLINE PxCCDContactModifyCallback* getCCDContactModifyCallback() const { return mCCDContactModifyCallback; } + /** + \brief Sets the CCD contact modification callback + \param[in] c The CCD contact modification callback + */ + PX_FORCE_INLINE void setCCDContactModifyCallback(PxCCDContactModifyCallback* c) { mCCDContactModifyCallback = c; } + /** + \brief Returns the maximum number of CCD passes + \return The maximum number of CCD passes + */ + PX_FORCE_INLINE PxU32 getCCDMaxPasses() const { return mCCDMaxPasses; } + /** + \brief Sets the maximum number of CCD passes + \param[in] ccdMaxPasses The maximum number of CCD passes + */ + PX_FORCE_INLINE void setCCDMaxPasses(PxU32 ccdMaxPasses) { mCCDMaxPasses = ccdMaxPasses; } + /** + \brief Returns the current CCD pass + \return The current CCD pass + */ + PX_FORCE_INLINE PxU32 getCurrentCCDPass() const { return miCCDPass; } + /** + \brief Returns The number of swept hits reported + \return The number of swept hits reported + */ + PX_FORCE_INLINE PxI32 getNumSweepHits() const { return mSweepTotalHits; } + /** + \brief Returns The number of updated bodies + \return The number of updated bodies in this CCD pass + */ + PX_FORCE_INLINE PxU32 getNumUpdatedBodies() const { return mUpdatedCCDBodies.size(); } + /** + \brief Returns The update bodies array + \return The updated bodies array from this CCD pass + */ + PX_FORCE_INLINE PxsRigidBody*const* getUpdatedBodies() const { return mUpdatedCCDBodies.begin(); } + + /** + \brief Returns Clears the updated bodies array + */ + PX_FORCE_INLINE void clearUpdatedBodies() { mUpdatedCCDBodies.forceSize_Unsafe(0); } + + /** + \brief Runs the CCD contact modification. + \param[in] contacts The list of modifiable contacts + \param[in] contactCount The number of contacts + \param[in] shapeCore0 The first shape core + \param[in] shapeCore1 The second shape core + \param[in] rigidCore0 The first rigid core + \param[in] rigidCore1 The second rigid core + \param[in] rigid0 The first rigid body + \param[in] rigid1 The second rigid body + */ + void runCCDModifiableContact(PxModifiableContact* PX_RESTRICT contacts, PxU32 contactCount, const PxsShapeCore* PX_RESTRICT shapeCore0, + const PxsShapeCore* PX_RESTRICT shapeCore1, const PxsRigidCore* PX_RESTRICT rigidCore0, const PxsRigidCore* PX_RESTRICT rigidCore1, + const PxsRigidBody* PX_RESTRICT rigid0, const PxsRigidBody* PX_RESTRICT rigid1); + + /** + \brief Performs a single CCD update + This occurs after broad phase and is responsible for creating islands, finding the TOI of collisions, filtering contacts, issuing modification callbacks and responding to + collisions. At the end of this phase all bodies will have stepper to their first TOI if they were involved in a CCD collision this frame. + \param[in] dt The timestep to simulate + \param[in] continuation The continuation task + \param[in] islandSim The island manager + \param[in] disableResweep If this is true, we perform a reduced-fidelity CCD approach + */ + void updateCCD(PxReal dt, PxBaseTask* continuation, IG::IslandSim& islandSim, bool disableResweep, PxI32 numFastMovingShapes); + + /** + \brief Signals the beginning of a CCD multi-pass update + */ + void updateCCDBegin(); + + /** + \brief Resets the CCD contact state in any contact managers that previously had a reported CCD touch. This must be called if CCD update is bypassed for a frame + */ + void resetContactManagers(); + + + + +protected: + + /** + \brief Constructor for PxsCCDContext + \param[in] context The PxsContext that is associated with this PxsCCDContext. + */ + PxsCCDContext(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext); + /** + \brief Destructor for PxsCCDContext + */ + ~PxsCCDContext(); + +private: + + + /** + \brief Verifies the consistency of the CCD context at the beginning + */ + void verifyCCDBegin(); + + /** + \brief Cleans up after the CCD update has completed + */ + void updateCCDEnd(); + + /** + \brief Spawns the update island tasks after the initial sweep estimates have been performed + \param[in] continuation The continuation task + */ + void postCCDSweep(PxBaseTask* continuation); + /** + \brief Creates contact buffers for CCD contacts. These will be sent to the user in the contact notification. + \param[in] continuation The continuation task + */ + void postCCDAdvance(PxBaseTask* continuation); + /** + \brief The final phase of the CCD task chain. Cleans up after the parallel update/postCCDAdvance stages. + \param[in] continuation The continuation task + */ + void postCCDDepenetrate(PxBaseTask* continuation); + + typedef Cm::DelegateTask PostCCDSweepTask; + typedef Cm::DelegateTask PostCCDAdvanceTask; + typedef Cm::DelegateTask PostCCDDepenetrateTask; + + PostCCDSweepTask mPostCCDSweepTask; + PostCCDAdvanceTask mPostCCDAdvanceTask; + PostCCDDepenetrateTask mPostCCDDepenetrateTask; + + PxCCDContactModifyCallback* mCCDContactModifyCallback; + + // CCD global data + bool mDisableCCDResweep; + PxU32 miCCDPass; + PxI32 mSweepTotalHits; + + // a fraction of objects will be CCD active so PxsCCDBody is dynamic, not a member of PxsRigidBody + PxsCCDBodyArray mCCDBodies; + PxsCCDOverlapArray mCCDOverlaps; + PxsCCDShapeArray mCCDShapes; + Ps::Array mIslandBodies; + Ps::Array mIslandSizes; + Ps::Array mUpdatedCCDBodies; + Ps::HashMap mMap; + + // temporary array updated during CCD update + //Array mCCDPairs; + PxsCCDPairArray mCCDPairs; + Ps::Array mCCDPtrPairs; + // number of pairs per island + Ps::Array mCCDIslandHistogram; + // thread context valid during CCD update + PxcNpThreadContext* mCCDThreadContext; + // number of pairs to process per thread + PxU32 mCCDPairsPerBatch; + PxU32 mCCDMaxPasses; + + PxsContext* mContext; + Dy::ThresholdStream& mThresholdStream; + + PxvNphaseImplementationContext& mNphaseContext; + + Ps::Mutex mMutex; + +private: + + PX_NOCOPY(PxsCCDContext) +}; + + +} + + + +#endif + diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h new file mode 100644 index 000000000..1293e2aeb --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_CONTACTMANAGER_H +#define PXS_CONTACTMANAGER_H + +#include "PxvConfig.h" +#include "PxcNpWorkUnit.h" + +namespace physx +{ + +class PxsContext; +class PxsRigidBody; +struct PxsCCDBody; +class PxsMaterialManager; +struct PxsCCDShape; + +namespace Dy +{ + class DynamicsContext; +} + +namespace Sc +{ + class ShapeInteraction; +} + +enum PxsPairVisColor +{ + + eVIS_COLOR_SWEPTINTEGRATE_OFF = 0x000000, + eVIS_COLOR_SWEPTINTEGRATE_SLOW = 0x404040, + eVIS_COLOR_SWEPTINTEGRATE_CLEAR = 0x007f00, + eVIS_COLOR_SWEPTINTEGRATE_IMPACT = 0x1680ff, + eVIS_COLOR_SWEPTINTEGRATE_FAIL = 0x0000ff + +}; + + +/** +\brief Additional header structure for CCD contact data stream. +*/ +struct PxsCCDContactHeader +{ + /** + \brief Stream for next collision. The same pair can collide multiple times during multiple CCD passes. + */ + PxsCCDContactHeader* nextStream; //4 //8 + /** + \brief Size (in bytes) of the CCD contact stream (not including force buffer) + */ + PxU16 contactStreamSize; //6 //10 + /** + \brief Defines whether the stream is from a previous pass. + + It could happen that the stream can not get allocated because we run out of memory. In that case the current event should not use the stream + from an event of the previous pass. + */ + PxU16 isFromPreviousPass; //8 //12 + + PxU8 pad[12 - sizeof(PxsCCDContactHeader*)]; //16 +}; + +PX_COMPILE_TIME_ASSERT((sizeof(PxsCCDContactHeader) & 0xF) == 0); + + +class PxsContactManager +{ +public: + PxsContactManager(PxsContext* context, PxU32 index); + ~PxsContactManager(); + + + PX_FORCE_INLINE void setDisableStrongFriction(PxU32 d) { (!d) ? mNpUnit.flags &= ~PxcNpWorkUnitFlag::eDISABLE_STRONG_FRICTION + : mNpUnit.flags |= PxcNpWorkUnitFlag::eDISABLE_STRONG_FRICTION; } + + PX_FORCE_INLINE PxReal getRestDistance() const { return mNpUnit.restDistance; } + PX_FORCE_INLINE void setRestDistance(PxReal v) { mNpUnit.restDistance = v; } + + void destroy(); + + PX_FORCE_INLINE PxU8 getDominance0() const { return mNpUnit.dominance0; } + PX_FORCE_INLINE void setDominance0(PxU8 v) { mNpUnit.dominance0 = v; } + + PX_FORCE_INLINE PxU8 getDominance1() const { return mNpUnit.dominance1; } + PX_FORCE_INLINE void setDominance1(PxU8 v) { mNpUnit.dominance1 = v; } + + PX_FORCE_INLINE PxU16 getTouchStatus() const { return PxU16(mNpUnit.statusFlags & PxcNpWorkUnitStatusFlag::eHAS_TOUCH); } + PX_FORCE_INLINE PxU16 touchStatusKnown() const { return PxU16(mNpUnit.statusFlags & PxcNpWorkUnitStatusFlag::eTOUCH_KNOWN); } + PX_FORCE_INLINE PxI32 getTouchIdx() const { return (mNpUnit.statusFlags& PxcNpWorkUnitStatusFlag::eHAS_TOUCH) ? 1 : (mNpUnit.statusFlags& PxcNpWorkUnitStatusFlag::eHAS_NO_TOUCH ? -1 : 0); } + + PX_FORCE_INLINE PxU32 getIndex() const { return mNpUnit.index; } + + PX_FORCE_INLINE PxU16 getHasCCDRetouch() const { return PxU16(mNpUnit.statusFlags & PxcNpWorkUnitStatusFlag::eHAS_CCD_RETOUCH); } + PX_FORCE_INLINE void clearCCDRetouch() { mNpUnit.statusFlags &= ~PxcNpWorkUnitStatusFlag::eHAS_CCD_RETOUCH; } + PX_FORCE_INLINE void raiseCCDRetouch() { mNpUnit.statusFlags |= PxcNpWorkUnitStatusFlag::eHAS_CCD_RETOUCH; } + + + + // flags stuff - needs to be refactored + + PX_FORCE_INLINE Ps::IntBool isChangeable() const { return Ps::IntBool(mFlags & PXS_CM_CHANGEABLE); } + PX_FORCE_INLINE Ps::IntBool getCCD() const { return Ps::IntBool((mFlags & PXS_CM_CCD_LINEAR) && (mNpUnit.flags & PxcNpWorkUnitFlag::eDETECT_CCD_CONTACTS)); } + PX_FORCE_INLINE Ps::IntBool getHadCCDContact() const { return Ps::IntBool(mFlags & PXS_CM_CCD_CONTACT); } + PX_FORCE_INLINE void setHadCCDContact() { mFlags |= PXS_CM_CCD_CONTACT; } + void setCCD(bool enable); + PX_FORCE_INLINE void clearCCDContactInfo() { mFlags &= ~PXS_CM_CCD_CONTACT; mNpUnit.ccdContacts = NULL; } + + PX_FORCE_INLINE PxcNpWorkUnit& getWorkUnit() { return mNpUnit; } + PX_FORCE_INLINE const PxcNpWorkUnit& getWorkUnit() const { return mNpUnit; } + + PX_FORCE_INLINE void* getUserData() const { return mShapeInteraction; } + + // Setup solver-constraints + void resetCachedState(); + void resetFrictionCachedState(); + + Sc::ShapeInteraction* getShapeInteraction() const { return mShapeInteraction; } + +private: + //KS - moving this up - we want to get at flags + + PxsRigidBody* mRigidBody0; //4 //8 + PxsRigidBody* mRigidBody1; //8 //16 + PxU32 mFlags; //20 //36 + Sc::ShapeInteraction* mShapeInteraction; //16 //32 + + + + friend class PxsContext; + // everything required for narrow phase to run + PxcNpWorkUnit mNpUnit; + + enum + { + PXS_CM_CHANGEABLE = (1<<0), + PXS_CM_CCD_LINEAR = (1<<1), + PXS_CM_CCD_CONTACT = (1 << 2) + }; + + friend class Dy::DynamicsContext; + friend struct PxsCCDPair; + friend class PxsIslandManager; + friend class PxsCCDContext; + friend class Sc::ShapeInteraction; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h new file mode 100644 index 000000000..767eb08f3 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_CONTACT_MANAGER_STATE_H +#define PXS_CONTACT_MANAGER_STATE_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + + struct PxsShapeCore; + + /** + There is an implicit 1:1 mapping between PxgContactManagerInput and PxsContactManagerOutput. The structures are split because PxgNpContactManagerInput contains constant + data that is produced by the CPU code and PxgNpContactManagerOutput contains per-frame contact information produced by the NP. + + There is also a 1:1 mapping between the PxgNpContactManager and PxsContactManager. This mapping is handled within the PxgNPhaseCore. + + The previous contact states are implicitly cached in PxsContactManager and will be propagated to the solver. Friction correlation is also done implicitly using cached + information in PxsContactManager. + The NP will produce a list of pairs that found/lost patches for the solver along with updating the PxgNpContactManagerOutput for all pairs. + */ + + struct PxsContactManagerStatusFlag + { + enum Enum + { + eHAS_NO_TOUCH = (1 << 0), + eHAS_TOUCH = (1 << 1), + //eHAS_SOLVER_CONSTRAINTS = (1 << 2), + eREQUEST_CONSTRAINTS = (1 << 3), + eHAS_CCD_RETOUCH = (1 << 4), // Marks pairs that are touching at a CCD pass and were touching at discrete collision or at a previous CCD pass already + // but we can not tell whether they lost contact in a pass before. We send them as pure eNOTIFY_TOUCH_CCD events to the + // contact report callback if requested. + eDIRTY_MANAGER = (1 << 5), + eTOUCH_KNOWN = eHAS_NO_TOUCH | eHAS_TOUCH // The touch status is known (if narrowphase never ran for a pair then no flag will be set) + }; + }; + + + struct PX_ALIGN_PREFIX(16) PxsContactManagerOutput + { + PxU8* contactPatches; //Start index/ptr for contact patches + PxU8* contactPoints; //Start index/ptr for contact points + PxReal* contactForces; //Start index/ptr for contact forces + PxU8 nbContacts; //Num contacts + PxU8 nbPatches; //Num patches + PxU8 statusFlag; //Status flag (has touch etc.) + PxU8 prevPatches; //Previous number of patches + + PX_FORCE_INLINE PxU32* getInternalFaceIndice() + { + return reinterpret_cast(contactForces + nbContacts); + } + } + PX_ALIGN_SUFFIX(16); + + struct /*PX_ALIGN_PREFIX(16)*/ PxsContactManagerPersistency + { + PxU8 mPrevPatches; + PxU8 mNbFrictionPatches; + PxU8 mNbPrevFrictionPatches; + } + /*PX_ALIGN_SUFFIX(16)*/; + +} + +#endif //PXG_CONTACT_MANAGER_H diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsContext.h b/src/PhysX/physx/source/lowlevel/software/include/PxsContext.h new file mode 100644 index 000000000..7f5fcc5d8 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsContext.h @@ -0,0 +1,333 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_CONTEXT_H +#define PXS_CONTEXT_H + +#include "PxVisualizationParameter.h" +#include "PxSceneDesc.h" + +#include "CmPool.h" + +#include "PxvNphaseImplementationContext.h" +#include "PxvSimStats.h" +#include "PxsContactManager.h" +#include "PxcNpBatch.h" +#include "PxcConstraintBlockStream.h" +#include "PxcNpCacheStreamPair.h" +#include "PxcNpMemBlockPool.h" +#include "CmRenderOutput.h" +#include "CmUtils.h" +#include "CmTask.h" + +#include "PxContactModifyCallback.h" + +#include "PxsTransformCache.h" +#include "GuPersistentContactManifold.h" +#include "DyArticulation.h" + + +#if PX_SUPPORT_GPU_PHYSX +namespace physx +{ + class PxCudaContextManager; +} +#endif + +namespace physx +{ + +class PxsRigidBody; +struct PxcConstraintBlock; +class PxsMaterialManager; +class PxsCCDContext; +struct PxsContactManagerOutput; +struct PxvContactManagerTouchEvent; + +namespace Cm +{ + class FlushPool; +} + +namespace IG +{ + class SimpleIslandManager; + typedef PxU32 EdgeIndex; +} + +enum PxsTouchEventCount +{ + PXS_LOST_TOUCH_COUNT = 0, + PXS_NEW_TOUCH_COUNT = 1, + PXS_CCD_RETOUCH_COUNT = 2, // pairs that are touching at a CCD pass and were touching at discrete collision or at a previous CCD pass already + // (but they could have lost touch in between) + PXS_PATCH_FOUND_COUNT = 3, + PXS_PATCH_LOST_COUNT = 4, + PXS_TOUCH_EVENT_COUNT = 5 +}; + +class PxsContext : public Ps::UserAllocated, public PxcNpContext +{ + PX_NOCOPY(PxsContext) +public: + PxsContext( const PxSceneDesc& desc, PxTaskManager*, Cm::FlushPool&, PxU64 contextID); + ~PxsContext(); + + void removeRigidBody(PxsRigidBody&); + + Dy::Articulation* createArticulation(); + void destroyArticulation(Dy::Articulation&); + + void createTransformCache(Ps::VirtualAllocatorCallback& allocatorCallback); + + PxsContactManager* createContactManager(PxsContactManager* contactManager, const bool useCCD); + void createCache(Gu::Cache& cache, PxsContactManager* cm, PxU8 geomType0, PxU8 geomType1); + void destroyCache(Gu::Cache& cache); + void destroyContactManager(PxsContactManager* cm); + + + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + + // Collision properties + PX_FORCE_INLINE PxContactModifyCallback* getContactModifyCallback() const { return mContactModifyCallback; } + PX_FORCE_INLINE void setContactModifyCallback(PxContactModifyCallback* c) { mContactModifyCallback = c; mNpImplementationContext->setContactModifyCallback(c);} + + + // resource-related + void setScratchBlock(void* addr, PxU32 size); + + void setContactDistance(Ps::Array* contactDistance); + + // Task-related + void updateContactManager(PxReal dt, bool hasBoundsArrayChanged, bool hasContactDistanceChanged, PxBaseTask* continuation, PxBaseTask* firstPassContinuation); + void secondPassUpdateContactManager(PxReal dt, PxBaseTask* continuation); + void fetchUpdateContactManager(); + void swapStreams(); + + void resetThreadContexts(); + + // Manager status change + bool getManagerTouchEventCount(int* newTouch, int* lostTouch, int* ccdTouch) const; + bool fillManagerTouchEvents( + PxvContactManagerTouchEvent* newTouch, PxI32& newTouchCount, + PxvContactManagerTouchEvent* lostTouch, PxI32& lostTouchCount, + PxvContactManagerTouchEvent* ccdTouch, PxI32& ccdTouchCount); + + PX_FORCE_INLINE void getManagerPatchEventCount(PxU32& foundPatch, PxU32& lostPatch) const { foundPatch = mCMTouchEventCount[PXS_PATCH_FOUND_COUNT]; lostPatch = mCMTouchEventCount[PXS_PATCH_LOST_COUNT]; } + bool fillManagerPatchChangedEvents( + PxsContactManager** foundPatch, PxU32& foundPatchCount, + PxsContactManager** lostPatch, PxU32& lostPatchCount); + + void beginUpdate(); + + // PX_ENABLE_SIM_STATS + PX_FORCE_INLINE PxvSimStats& getSimStats() { return mSimStats; } + PX_FORCE_INLINE const PxvSimStats& getSimStats() const { return mSimStats; } + + PX_FORCE_INLINE Cm::FlushPool& getTaskPool() const { return mTaskPool; } + PX_FORCE_INLINE Cm::RenderBuffer& getRenderBuffer() { return mRenderBuffer; } + + PxReal getVisualizationParameter(PxVisualizationParameter::Enum param) const; + void setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value); + + PX_FORCE_INLINE void setVisualizationCullingBox(const PxBounds3& box) { mVisualizationCullingBox = box; } + PX_FORCE_INLINE const PxBounds3& getVisualizationCullingBox()const { return mVisualizationCullingBox; } + + PX_FORCE_INLINE PxReal getRenderScale() const { return mVisualizationParams[PxVisualizationParameter::eSCALE]; } + Cm::RenderOutput getRenderOutput() { return Cm::RenderOutput(mRenderBuffer); } + PX_FORCE_INLINE bool getPCM() const { return mPCM; } + PX_FORCE_INLINE bool getContactCacheFlag() const { return mContactCache; } + PX_FORCE_INLINE bool getCreateAveragePoint() const { return mCreateAveragePoint; } + + // general stuff + void shiftOrigin(const PxVec3& shift); + + void setCreateContactStream(bool to); + PX_FORCE_INLINE void setPCM(bool enabled) { mPCM = enabled; } + PX_FORCE_INLINE void setContactCache(bool enabled) { mContactCache = enabled; } + + PX_FORCE_INLINE PxcScratchAllocator& getScratchAllocator() { return mScratchAllocator; } + PX_FORCE_INLINE PxsTransformCache& getTransformCache() { return *mTransformCache; } + PX_FORCE_INLINE PxReal* getContactDistance() { return mContactDistance->begin(); } + + PX_FORCE_INLINE PxvNphaseImplementationContext* getNphaseImplementationContext() const + { + return mNpImplementationContext; + } + + PX_FORCE_INLINE void setNphaseImplementationContext(PxvNphaseImplementationContext* ctx) + { + mNpImplementationContext = ctx; + } + + PX_FORCE_INLINE PxvNphaseImplementationContext* getNphaseFallbackImplementationContext() const + { + return mNpFallbackImplementationContext; + } + + PX_FORCE_INLINE void setNphaseFallbackImplementationContext(PxvNphaseImplementationContext* ctx) + { + mNpFallbackImplementationContext = ctx; + } + + PxU32 getTotalCompressedContactSize() const { return mTotalCompressedCacheSize; } + PxU32 getMaxPatchCount() const { return mMaxPatches; } + + PX_FORCE_INLINE PxcThreadCoherentCache& getNpThreadContextPool() + { + return mNpThreadContextPool; + } + + PX_FORCE_INLINE PxcNpThreadContext* getNpThreadContext() + { + // We may want to conditional compile to exclude this on single threaded implementations + // if it is determined to be a performance hit. + return mNpThreadContextPool.get(); + } + + PX_FORCE_INLINE void putNpThreadContext(PxcNpThreadContext* threadContext) + { mNpThreadContextPool.put(threadContext); } + PX_FORCE_INLINE Ps::Mutex& getLock() { return mLock; } + + PX_FORCE_INLINE PxTaskManager& getTaskManager() + { + PX_ASSERT(mTaskManager); + return *mTaskManager; + } + + PX_FORCE_INLINE void clearManagerTouchEvents(); + + PX_FORCE_INLINE Cm::PoolList& getContactManagerPool() + { + return this->mContactManagerPool; + } + + PX_FORCE_INLINE void setActiveContactManager(const PxsContactManager* manager) + { + const PxU32 index = manager->getIndex(); + if (index >= mActiveContactManager.size()) + { + PxU32 newSize = (2 * index + 256)&~255; + mActiveContactManager.resize(newSize); + } + mActiveContactManager.set(index); + + //Record any pairs that have CCD enabled! + if (manager->getCCD()) + { + if (index >= mActiveContactManagersWithCCD.size()) + { + PxU32 newSize = (2 * index + 256)&~255; + mActiveContactManagersWithCCD.resize(newSize); + } + mActiveContactManagersWithCCD.set(index); + } + } + + +private: + void mergeCMDiscreteUpdateResults(PxBaseTask* continuation); + + PxU32 mIndex; + + // Threading + PxcThreadCoherentCache + mNpThreadContextPool; + + // Contact managers + Cm::PoolList mContactManagerPool; + Ps::Pool mManifoldPool; + Ps::Pool mSphereManifoldPool; + + Cm::BitMap mActiveContactManager; + Cm::BitMap mActiveContactManagersWithCCD; //KS - adding to filter any pairs that had a touch + Cm::BitMap mContactManagersWithCCDTouch; //KS - adding to filter any pairs that had a touch + Cm::BitMap mContactManagerTouchEvent; + Cm::BitMap mContactManagerPatchChangeEvent; + PxU32 mCMTouchEventCount[PXS_TOUCH_EVENT_COUNT]; + + Ps::Mutex mLock; + + + + PxContactModifyCallback* mContactModifyCallback; + + // narrowphase platform-dependent implementations support + PxvNphaseImplementationContext* mNpImplementationContext; + PxvNphaseImplementationContext* mNpFallbackImplementationContext; + + + // debug rendering (CS TODO: MS would like to have these wrapped into a class) + PxReal mVisualizationParams[PxVisualizationParameter::eNUM_VALUES]; + + PxBounds3 mVisualizationCullingBox; + + PxTaskManager* mTaskManager; + Cm::FlushPool& mTaskPool; + + + // PxU32 mTouchesLost; + // PxU32 mTouchesFound; + + // PX_ENABLE_SIM_STATS + PxvSimStats mSimStats; + bool mPCM; + bool mContactCache; + bool mCreateAveragePoint; + + PxsTransformCache* mTransformCache; + Ps::Array* mContactDistance; + + + PxU32 mMaxPatches; + PxU32 mTotalCompressedCacheSize; + + PxU64 mContextID; + + friend class PxsCCDContext; + friend class PxsNphaseImplementationContext; + friend class PxgNphaseImplementationContext; //FDTODO ideally it shouldn't be here.. +}; + + +PX_FORCE_INLINE void PxsContext::clearManagerTouchEvents() +{ + mContactManagerTouchEvent.clear(); + mContactManagerPatchChangeEvent.clear(); + for(PxU32 i = 0; i < PXS_TOUCH_EVENT_COUNT; ++i) + { + mCMTouchEventCount[i] = 0; + } +} + + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h new file mode 100644 index 000000000..950698b01 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_DEFAULT_MEMORY_MANAGER_H +#define PXS_DEFAULT_MEMORY_MANAGER_H + +#include "PxsMemoryManager.h" +#include "PsAllocator.h" +#include "PsArray.h" + +namespace physx +{ + + class PxsDefaultMemoryAllocator : public Ps::VirtualAllocatorCallback + { + public: + + PxsDefaultMemoryAllocator(const char* name = NULL) + { + PX_UNUSED(name); +#if 0 //PX_USE_NAMED_ALLOCATOR + if (name) + strcpy(mName, name); + else + strcpy(mName, ""); +#endif + } + + virtual ~PxsDefaultMemoryAllocator() + { + } + + virtual void* allocate(const size_t newByteSize, const char* filename, const int line) + { + PX_UNUSED(line); + PX_UNUSED(filename); +#if 0 //PX_USE_NAMED_ALLOCATOR + return PX_ALLOC(newByteSize, mName); +#else + return PX_ALLOC(newByteSize, filename); +#endif + } + + virtual void deallocate(void* ptr) + { + if (ptr) + PX_FREE(ptr); + } + +#if 0 //PX_USE_NAMED_ALLOCATOR + char mName[32]; +#endif + }; + + + class PxsDefaultMemoryManager : public PxsMemoryManager + { + public: + virtual ~PxsDefaultMemoryManager(); + virtual Ps::VirtualAllocatorCallback* createHostMemoryAllocator(const PxU32 gpuComputeVersion = 0); + virtual Ps::VirtualAllocatorCallback* createDeviceMemoryAllocator(const PxU32 gpuComputeVersion = 0); + + virtual void destroyMemoryAllocator(); + + Ps::Array mAllocators; + + }; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h b/src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h new file mode 100644 index 000000000..0646fe2d7 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_HEAP_MEMORY_ALLOCATOR_H +#define PXS_HEAP_MEMORY_ALLOCATOR_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + namespace shdfnd + { + class VirtualAllocatorCallback; + } + + class PxErrorCallback; + class PxsHostMemoryAllocator; + + class PxsHeapMemoryAllocator : public Ps::VirtualAllocatorCallback + { + public: + virtual ~PxsHeapMemoryAllocator(){} + virtual void* allocate(const size_t size, const char* file, const int line) = 0; + virtual void deallocate(void* ptr) = 0; + + }; + + class PxsHeapMemoryAllocatorManager + { + public: + virtual ~PxsHeapMemoryAllocatorManager() + { + + } + PxsHeapMemoryAllocator* mMappedMemoryAllocators; + }; +} + +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h new file mode 100644 index 000000000..0c75adab5 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h @@ -0,0 +1,40 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_INCREMENTAL_CONSTRAINT_PARTITIONING_H +#define PXS_INCREMENTAL_CONSTRAINT_PARTITIONING_H + +#include "PxsSimpleIslandManager.h" + +namespace physx +{ + +} + +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h new file mode 100644 index 000000000..021cd95f0 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h @@ -0,0 +1,363 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_ISLAND_MANAGER_TYPES_H +#define PXS_ISLAND_MANAGER_TYPES_H + +#include "CmPhysXCommon.h" + +namespace physx +{ + +class PxsContactManager; +class PxsRigidBody; +namespace Dy +{ + struct Constraint; + class Articulation; +} + +typedef PxU32 NodeType; +typedef PxU32 EdgeType; +typedef PxU32 IslandType; +#define INVALID_NODE 0xffffffff +#define INVALID_EDGE 0xffffffff +#define INVALID_ISLAND 0xffffffff + +namespace Dy +{ + typedef size_t ArticulationLinkHandle; +} + +//----------------------------------------------------------------------------// + +template class PxsIslandManagerHook +{ + friend class PxsIslandManager; + T index; + +public: + + static const T INVALID = INVLD; + + PX_FORCE_INLINE PxsIslandManagerHook(): index(INVLD) {} + PX_FORCE_INLINE PxsIslandManagerHook(const T id): index(id) {} + PX_FORCE_INLINE PxsIslandManagerHook(const PxsIslandManagerHook& src) : index(src.index) {} + PX_FORCE_INLINE ~PxsIslandManagerHook(){} + + PX_FORCE_INLINE bool isManaged() const { return index!=INVLD; } + +private: +}; + +typedef PxsIslandManagerHook PxsIslandManagerNodeHook; +typedef PxsIslandManagerHook PxsIslandManagerEdgeHook; +typedef PxsIslandManagerHook PxsIslandManagerIslandHook; + +//----------------------------------------------------------------------------// + +/** +\brief PxsIslandObjects contains arrays of all rigid bodies, articulations, contact managers, and constraints that +belong to all awake islands. The per array indices denoting the ownership per island are stored in PxsIslandIndices. + +@see PxsIslandManager::getIslandObjects +*/ +struct PxsIslandObjects +{ + /** + \brief Array of all rigid bodies in all awake islands. + + \note Each rigid body corresponds to the void* passed to PxsIslandManager::addBody. The PxsRigidBody ptr is computed + by adding the rigid body offset value (passed to PxsIslandManager::create) to the void* pointer + ie [(PxsRigidBody*)((PxU8*)owner + rigidBodyOffset)] + + @see PxsIslandManager::addBody, PxsIslandManager::create + */ + PxsRigidBody*const* bodies; + + /** + \brief Array of all articulation roots in all awake islands. + + \note Each Articulation* corresponds to Dy::getArticulation(articLinkHandle) where + articLinkHandle is the handle passed to PxsIslandManager::setArticulationRootLinkHandle. + + @see PxsIslandManager::setArticulationRootLinkHandle, Dy::getArticulation + */ + Dy::Articulation*const* articulations; + + /** + \brief Array of all articulation roots in all awake islands. + + \note Each void* corresponds to the void* passed to PxsIslandManager::setArticulationRootLinkHandle + + @see PxsIslandManager::setArticulationRootLinkHandle + */ + void*const* articulationOwners; + + /** + \brief Array of all contact managers in all awake islands. + + @see PxsIslandManager::setEdgeRigidCM + */ + struct PxsIndexedContactManager* contactManagers; + + /** + \brief Array of all constraints in all awake islands. + + @see PxsIslandManager::setEdgeConstraint + */ + struct PxsIndexedConstraint* constraints; + + + PxsIslandObjects() : bodies(NULL), articulations(NULL), articulationOwners(NULL), contactManagers(NULL), constraints(NULL) + { + } +}; + + +//----------------------------------------------------------------------------// + +/** +\brief An array of PxsIslandIndices describes the rigid bodies, articulations, contacts and constraints that +belong to each island. + +\note Given an array of PxsIslandIndices, the rigid bodies of the ith island span the inclusive range: + (PxsIslandObjects::bodies[PxsIslandIndices[i]], PxsIslandObjects::bodies[PxsIslandIndices[i+1]-1]) + +\note Given an array of PxsIslandIndices, the constraints of the ith island span the inclusive range: + (PxsIslandObjects::constraints[PxsIslandIndices[i]], PxsIslandObjects::constraints[PxsIslandIndices[i+1]-1]) + +@see PxsIslandObjects::getIslandIndices, PxsIslandObjects::getIslandCount +*/ +class PxsIslandIndices +{ +public: + + PxsIslandIndices(){} + ~PxsIslandIndices(){} + + /** + \brief Return true if the corresponding island has a contact with a static rigid body. + */ + PX_FORCE_INLINE bool getHasStaticContact() const + { + return (1 & hasStaticContact) ? true : false; + } + + /** + \brief The starting index of island rigid bodies in the array PxsIslandObjects::bodies + */ + NodeType bodies; + + /** + \brief The starting index of island articulations in the arrays PxsIslandObjects::articulations and PxsIslandObjects::articulationOwners + + \note The total number of articulations is clamped at 32767 on any platform that uses 16-bit handles. + */ + NodeType articulations : 8*sizeof(NodeType)-1; + +private: + + NodeType hasStaticContact : 1; + +public: + + /** + \brief The starting index of island contact managers in the array PxsIslandObjects::contactManagers. + */ + EdgeType contactManagers; + + /** + \brief The starting index of island constraints in the array PxsIslandObjects::constraints. + + \note islandId is for internal use only and is used for tracking islands that need a second pass. + */ + union + { + EdgeType constraints; + IslandType islandId; + }; + +//private: + + /** + \brief Internal use only. + */ + PX_FORCE_INLINE void setHasStaticContact(const bool b) + { + hasStaticContact = NodeType(b ? 1 : 0); + } +}; +PX_COMPILE_TIME_ASSERT(0==(0x07 & sizeof(PxsIslandIndices))); + + +//----------------------------------------------------------------------------// + +typedef Dy::ArticulationLinkHandle PxsNodeType; + + +/** +\brief Each contact manager or constraint references two separate bodies, where +a body can be a dynamic rigid body, a kinematic rigid body, an articulation or a static. +The struct PxsIndexedInteraction describes the bodies that make up the pair. +*/ +struct PxsIndexedInteraction +{ + /** + \brief An enumerated list of all possible body types. + A body type is stored for each body in the pair. + */ + enum Enum + { + eBODY = 0, + eKINEMATIC = 1, + eARTICULATION = 2, + eWORLD = 3 + }; + + /** + \brief An index describing how to access body0 + + \note If body0 is a dynamic (eBODY) rigid body then solverBody0 is an index into PxsIslandObjects::bodies. + \note If body0 is a kinematic (eKINEMATIC) rigid body then solverBody0 is an index into PxsIslandManager::getActiveKinematics. + + \note If body0 is a static (eWORLD) then solverBody0 is PX_MAX_U32 or PX_MAX_U64, depending on the platform being 32- or 64-bit. + + \note If body0 is an articulation then the articulation is found directly from Dy::getArticulation(articulation0) + */ + union + { + PxsNodeType solverBody0; + Dy::ArticulationLinkHandle articulation0; + }; + + /** + \brief An index describing how to access body1 + + \note If body1 is a dynamic (eBODY) rigid body then solverBody1 is an index into PxsIslandObjects::bodies. + \note If body1 is a kinematic (eKINEMATIC) rigid body then solverBody1 is an index into PxsIslandManager::getActiveKinematics. + + \note If body1 is a static (eWORLD) then solverBody1 is PX_MAX_U32 or PX_MAX_U64, depending on the platform being 32- or 64-bit. + + \note If body1 is an articulation then the articulation is found directly from Dy::getArticulation(articulation1) + */ + union + { + PxsNodeType solverBody1; + Dy::ArticulationLinkHandle articulation1; + }; + + /** + \brief The type (eBODY, eKINEMATIC etc) of body0 + */ + PxU8 indexType0; + + /** + \brief The type (eBODY, eKINEMATIC etc) of body1 + */ + PxU8 indexType1; + + PxU8 pad[2]; +}; + +/** +@see PxsIslandObjects, PxsIndexedInteraction +*/ +struct PxsIndexedContactManager : public PxsIndexedInteraction +{ + /** + \brief The contact manager corresponds to the value set in PxsIslandManager::setEdgeRigidCM + */ + PxsContactManager* contactManager; + + PxsIndexedContactManager(PxsContactManager* cm) : contactManager(cm) {} +}; +#if !PX_X64 +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxsIndexedContactManager) & 0x0f)); +#endif + +/** +@see PxsIslandObjects, PxsIndexedInteraction +*/ +struct PxsIndexedConstraint : public PxsIndexedInteraction +{ + /** + \brief The constraint corresponds to the value set in PxsIslandManager::setEdgeConstraint + */ + Dy::Constraint* constraint; + + PxsIndexedConstraint(Dy::Constraint* c) : constraint(c) {} +}; +#if !PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxsIndexedConstraint) & 0x0f)); +#endif + +//----------------------------------------------------------------------------// + +/** +\brief Any sleeping contact pair that finds itself in an awake island after 1st pass island gen +must participate in 2nd pass narrowphase so that contacts can be generated. + +\note Contact managers in sleeping pairs are NULL until PxsIslandManager::setWokenPairContactManagers is complete. + +@see PxsIslandManager::getNarrowPhaseSecondPassContactManagers, PxsIslandManager::getNumNarrowPhaseSecondPassContactManagers, +PxsIslandManager::setWokenPairContactManagers +*/ +struct PxsNarrowPhaseSecondPassContactManager +{ + /** + \brief The contact manager that is to participate in 2nd pass narrowphase. + + \note This pointer is NULL after 1st pass island gen and remains NULL until PxsIslandManager::setWokenPairContactManagers + completes. + */ + PxsContactManager* mCM; + + /** + \brief The corresponding entry in PxsIslandObjects::contactManagers. + + \note All sleeping pairs have a null contact manager during 1st pass island gen. After 1st pass island gen completes, + the bodies to be woken are externally processed. Waking up bodies generates contact managers and passes the pointer to the + corresponding edge. So that the contact manager can be efficiently passed to PxsIslandObjects we store mEdgeId and mSolverCMId. + The contact manager pointers are set in PxsIslandManager::setWokenPairContactManagers + */ + EdgeType mSolverCMId; //Keeps a track of which entries in the solver islands temporarily have a null contact manager + + /** + \brief The internal id of the corresponding edge. + */ + EdgeType mEdgeId; +}; + + +} //namespace physx + + +#endif //PXS_ISLAND_MANAGER_TYPES_H diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h new file mode 100644 index 000000000..713ae169b --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_ISLAND_NODEINDEX_H +#define PXS_ISLAND_NODEINDEX_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ +namespace IG +{ +#define IG_INVALID_NODE 0x1FFFFFFu + + class NodeIndex + { + private: + PxU32 ind; + + public: + + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE NodeIndex(PxU32 id, PxU32 articLinkId) : ind((id << 7) | (articLinkId << 1) | 1) + { + } + + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE NodeIndex(PxU32 id = IG_INVALID_NODE) : ind((id << 7)) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 index() const { return ind >> 7; } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 articulationLinkId() const { return ((ind >> 1) & 63); } + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 isArticulation() const { return ind & 1; } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isStaticBody() const { return (ind >> 7) == IG_INVALID_NODE; } + + PX_CUDA_CALLABLE bool isValid() const { return (ind >> 7) != IG_INVALID_NODE; } + + PX_CUDA_CALLABLE void setIndices(PxU32 index, PxU32 articLinkId) { ind = ((index << 7) | (articLinkId << 1) | 1); } + + PX_CUDA_CALLABLE void setIndices(PxU32 index) { ind = ((index << 7)); } + + PX_CUDA_CALLABLE bool operator < (const IG::NodeIndex& other) const { return ind < other.ind; } + + PX_CUDA_CALLABLE bool operator <= (const IG::NodeIndex& other) const { return ind <= other.ind; } + + PX_CUDA_CALLABLE bool operator == (const IG::NodeIndex& other) const { return ind == other.ind; } + }; + +} +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h new file mode 100644 index 000000000..2390636bf --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h @@ -0,0 +1,935 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_ISLAND_SIM_H +#define PXS_ISLAND_SIM_H + +#include "CmPhysXCommon.h" +#include "foundation/PxAssert.h" +#include "PsArray.h" +#include "CmBitMap.h" +#include "CmPriorityQueue.h" +#include "CmBlockArray.h" +#include "PxsIslandNodeIndex.h" + +namespace physx +{ + +namespace Dy +{ + struct Constraint; + class ArticulationV; +} + +namespace Sc +{ + class ArticulationSim; +} + +class PxsContactManager; +class PxsRigidBody; + +struct PartitionEdge; + +namespace IG +{ + +//This index is +#define IG_INVALID_ISLAND 0xFFFFFFFFu +#define IG_INVALID_EDGE 0xFFFFFFFFu +#define IG_INVALID_LINK 0xFFu + +typedef PxU32 IslandId; +typedef PxU32 EdgeIndex; +typedef PxU32 EdgeInstanceIndex; + +class IslandSim; + +struct Edge +{ + //Edge instances can be implicitly calculated based on this edge index, which is an offset into the array of edges. + //From that, the child edge index is simply the + //The constraint or contact referenced by this edge + + enum EdgeType + { + eCONTACT_MANAGER, + eCONSTRAINT, + eEDGE_TYPE_COUNT + }; + + + enum EdgeState + { + eINSERTED =1<<0, + ePENDING_DESTROYED =1<<1, + eACTIVE =1<<2, + eIN_DIRTY_LIST =1<<3, + eDESTROYED =1<<4, + eREPORT_ONLY_DESTROY=1<<5, + eACTIVATING =1<<6 + }; + + + //NodeIndex mNode1, mNode2; + EdgeType mEdgeType; + PxU16 mEdgeState; + + EdgeIndex mNextIslandEdge, mPrevIslandEdge; + + + + PX_FORCE_INLINE void setInserted() { mEdgeState |= (eINSERTED); } + + PX_FORCE_INLINE void clearInserted() { mEdgeState &= (~eINSERTED); } + + PX_FORCE_INLINE void clearDestroyed() { mEdgeState &=(~eDESTROYED);} + PX_FORCE_INLINE void setPendingDestroyed() { mEdgeState |= ePENDING_DESTROYED; } + PX_FORCE_INLINE void clearPendingDestroyed() { mEdgeState &= (~ePENDING_DESTROYED); } + PX_FORCE_INLINE void activateEdge() { mEdgeState |= eACTIVE; } + PX_FORCE_INLINE void deactivateEdge() { mEdgeState &= (~eACTIVE); } + PX_FORCE_INLINE void markInDirtyList() { mEdgeState |= (eIN_DIRTY_LIST); } + PX_FORCE_INLINE void clearInDirtyList() { mEdgeState &= (~eIN_DIRTY_LIST); } + PX_FORCE_INLINE void setReportOnlyDestroy() { mEdgeState |= (eREPORT_ONLY_DESTROY); } + PX_FORCE_INLINE void clearReportOnlyDestroy() { mEdgeState &= (~eREPORT_ONLY_DESTROY); } +public: + Edge() : mEdgeType(Edge::eCONTACT_MANAGER), mEdgeState(eDESTROYED), + mNextIslandEdge(IG_INVALID_EDGE), mPrevIslandEdge(IG_INVALID_EDGE) + { + } + PX_FORCE_INLINE bool isInserted() const { return !!(mEdgeState & eINSERTED);} + PX_FORCE_INLINE bool isDestroyed() const { return !!(mEdgeState & eDESTROYED); } + PX_FORCE_INLINE bool isPendingDestroyed() const { return !!(mEdgeState & ePENDING_DESTROYED); } + PX_FORCE_INLINE bool isActive() const { return !!(mEdgeState & eACTIVE); } + PX_FORCE_INLINE bool isInDirtyList() const { return !!(mEdgeState & eIN_DIRTY_LIST); } + PX_FORCE_INLINE EdgeType getEdgeType() const { return mEdgeType; } + //PX_FORCE_INLINE const NodeIndex getIndex1() const { return mNode1; } + //PX_FORCE_INLINE const NodeIndex getIndex2() const { return mNode2; } + PX_FORCE_INLINE bool isReportOnlyDestroy() { return !!(mEdgeState & eREPORT_ONLY_DESTROY); } +}; + +struct EdgeInstance +{ + EdgeInstanceIndex mNextEdge, mPrevEdge; //The next edge instance in this node's list of edge instances + + EdgeInstance() : mNextEdge(IG_INVALID_EDGE), mPrevEdge(IG_INVALID_EDGE) + { + } +}; + +template +class HandleManager +{ + Ps::Array mFreeHandles; + Handle mCurrentHandle; + +public: + + HandleManager() : mFreeHandles(PX_DEBUG_EXP("FreeHandles")), mCurrentHandle(0) + { + } + + ~HandleManager(){} + + Handle getHandle() + { + if(mFreeHandles.size()) + { + Handle handle = mFreeHandles.popBack(); + PX_ASSERT(isValidHandle(handle)); + return handle; + } + return mCurrentHandle++; + } + + bool isNotFreeHandle(Handle handle) + { + for(PxU32 a = 0; a < mFreeHandles.size(); ++a) + { + if(mFreeHandles[a] == handle) + return false; + } + return true; + } + + void freeHandle(Handle handle) + { + PX_ASSERT(isValidHandle(handle)); + PX_ASSERT(isNotFreeHandle(handle)); + if(handle == mCurrentHandle) + mCurrentHandle--; + else + mFreeHandles.pushBack(handle); + } + + bool isValidHandle(Handle handle) + { + return handle < mCurrentHandle; + } + + PX_FORCE_INLINE PxU32 getTotalHandles() const { return mCurrentHandle; } +}; + +class Node +{ + +public: + enum NodeType + { + eRIGID_BODY_TYPE, + eARTICULATION_TYPE, + eTYPE_COUNT + }; + enum State + { + eREADY_FOR_SLEEPING = 1u << 0, //! Ready to go to sleep + eACTIVE = 1u << 1, //! Active + eKINEMATIC = 1u << 2, //! Kinematic + eDELETED = 1u << 3, //! Is pending deletion + eDIRTY = 1u << 4, //! Is dirty (i.e. lost a connection) + eACTIVATING = 1u << 5, //! Is in the activating list + eDEACTIVATING = 1u << 6 //! It is being forced to deactivate this frame + }; + EdgeInstanceIndex mFirstEdgeIndex; + + PxU8 mFlags; + PxU8 mType; + PxU16 mStaticTouchCount; + //PxU32 mActiveNodeIndex; //! Look-up for this node in the active nodes list, activating list or deactivating list... + + NodeIndex mNextNode, mPrevNode; + + //A counter for the number of active references to this body. Whenever an edge is activated, this is incremented. + //Whenver an edge is deactivated, this is decremented. This is used for kinematic bodies to determine if they need + //to be in the active kinematics list + PxU32 mActiveRefCount; + + + //A node can correspond with either a rigid body or an articulation or softBody + union + { + PxsRigidBody* mRigidBody; + Dy::ArticulationV* mLLArticulation; + }; + + + + PX_FORCE_INLINE Node() : mFirstEdgeIndex(IG_INVALID_EDGE), mFlags(eDELETED), mType(eRIGID_BODY_TYPE), + mStaticTouchCount(0), mActiveRefCount(0), mRigidBody(NULL) + { + } + + PX_FORCE_INLINE ~Node() {} + + PX_FORCE_INLINE void reset() + { + mFirstEdgeIndex = IG_INVALID_EDGE; + mFlags = eDELETED; + mRigidBody = NULL; + mActiveRefCount = 0; + mStaticTouchCount = 0; + } + + PX_FORCE_INLINE void setRigidBody(PxsRigidBody* body) { mRigidBody = body; } + + PX_FORCE_INLINE PxsRigidBody* getRigidBody() const { return mRigidBody; } + + PX_FORCE_INLINE Dy::ArticulationV* getArticulation() const { return mLLArticulation; } + + + PX_FORCE_INLINE void setActive() { mFlags |= eACTIVE; } + PX_FORCE_INLINE void clearActive() { mFlags &= ~eACTIVE; } + + PX_FORCE_INLINE void setActivating() { mFlags |= eACTIVATING; } + PX_FORCE_INLINE void clearActivating() { mFlags &= ~eACTIVATING; } + + PX_FORCE_INLINE void setDeactivating() { mFlags |= eDEACTIVATING; } + PX_FORCE_INLINE void clearDeactivating() { mFlags &= (~eDEACTIVATING); } + + + //Activates a body/node. + PX_FORCE_INLINE void setIsReadyForSleeping() { mFlags |= eREADY_FOR_SLEEPING; } + + PX_FORCE_INLINE void clearIsReadyForSleeping(){ mFlags &= (~eREADY_FOR_SLEEPING);} + + PX_FORCE_INLINE void setIsDeleted(){mFlags |= eDELETED; } + + PX_FORCE_INLINE void setKinematicFlag() {PX_ASSERT(!isKinematic()); mFlags |= eKINEMATIC;} + + PX_FORCE_INLINE void clearKinematicFlag(){ PX_ASSERT(isKinematic()); mFlags &= (~eKINEMATIC);} + + PX_FORCE_INLINE void markDirty(){mFlags |= eDIRTY;} + + PX_FORCE_INLINE void clearDirty(){mFlags &= (~eDIRTY);} + +public: + + PX_FORCE_INLINE bool isActive() const { return !!(mFlags & eACTIVE); } + + PX_FORCE_INLINE bool isActiveOrActivating() const { return !!(mFlags & (eACTIVE | eACTIVATING)); } + + PX_FORCE_INLINE bool isActivating() const { return !!(mFlags & eACTIVATING); } + + PX_FORCE_INLINE bool isDeactivating() const { return !!(mFlags & eDEACTIVATING); } + + PX_FORCE_INLINE bool isKinematic() const { return !!(mFlags & eKINEMATIC); } + + PX_FORCE_INLINE bool isDeleted() const { return !!(mFlags & eDELETED); } + + PX_FORCE_INLINE bool isDirty() const { return !!(mFlags & eDIRTY); } + + PX_FORCE_INLINE bool isReadyForSleeping() const { return !!(mFlags & eREADY_FOR_SLEEPING); } + + PX_FORCE_INLINE NodeType getNodeType() const { return NodeType(mType); } + + friend class SimpleIslandManager; + +}; + +struct Island +{ + NodeIndex mRootNode; + NodeIndex mLastNode; + PxU32 mSize[Node::eTYPE_COUNT]; + PxU32 mActiveIndex; + + EdgeIndex mFirstEdge[Edge::eEDGE_TYPE_COUNT], mLastEdge[Edge::eEDGE_TYPE_COUNT]; + PxU32 mEdgeCount[Edge::eEDGE_TYPE_COUNT]; + + Island() : mActiveIndex(IG_INVALID_ISLAND) + { + for(PxU32 a = 0; a < Edge::eEDGE_TYPE_COUNT; ++a) + { + mFirstEdge[a] = IG_INVALID_EDGE; + mLastEdge[a] = IG_INVALID_EDGE; + mEdgeCount[a] = 0; + } + + for(PxU32 a = 0; a < Node::eTYPE_COUNT; ++a) + { + mSize[a] = 0; + } + } +}; + +struct TraversalState +{ + NodeIndex mNodeIndex; + PxU32 mCurrentIndex; + PxU32 mPrevIndex; + PxU32 mDepth; + + TraversalState() + { + } + + TraversalState(NodeIndex nodeIndex, PxU32 currentIndex, PxU32 prevIndex, PxU32 depth) : + mNodeIndex(nodeIndex), mCurrentIndex(currentIndex), mPrevIndex(prevIndex), mDepth(depth) + { + } +}; + +struct QueueElement +{ + TraversalState* mState; + PxU32 mHopCount; + + QueueElement() + { + } + + QueueElement(TraversalState* state, PxU32 hopCount) : mState(state), mHopCount(hopCount) + { + } +}; + +struct NodeComparator +{ + NodeComparator() + { + } + + bool operator() (const QueueElement& node0, const QueueElement& node1) const + { + return node0.mHopCount < node1.mHopCount; + } +private: + NodeComparator& operator = (const NodeComparator&); +}; + + +class IslandSim +{ + HandleManager mIslandHandles; //! Handle manager for islands + + Ps::Array mNodes; //! The nodes used in the constraint graph + Ps::Array mActiveNodeIndex; //! The active node index for each node + Cm::BlockArray mEdges; + Cm::BlockArray mEdgeInstances; //! Edges used to connect nodes in the constraint graph + Ps::Array mIslands; //! The array of islands + Ps::Array mIslandStaticTouchCount; //! Array of static touch counts per-island + + + Ps::Array mActiveNodes[Node::eTYPE_COUNT]; //! An array of active nodes + Ps::Array mActiveKinematicNodes; //! An array of active or referenced kinematic nodes + Ps::Array mActivatedEdges[Edge::eEDGE_TYPE_COUNT]; //! An array of active edges + + PxU32 mActiveEdgeCount[Edge::eEDGE_TYPE_COUNT]; + + Ps::Array mHopCounts; //! The observed number of "hops" from a given node to its root node. May be inaccurate but used to accelerate searches. + Ps::Array mFastRoute; //! The observed last route from a given node to the root node. We try the fast route (unless its broken) before trying others. + + Ps::Array mIslandIds; //! The array of per-node island ids + + Cm::BitMap mIslandAwake; //! Indicates whether an island is awake or not + + Cm::BitMap mActiveContactEdges; + + //An array of active islands + Ps::Array mActiveIslands; + + PxU32 mInitialActiveNodeCount[Edge::eEDGE_TYPE_COUNT]; + + Ps::Array mNodesToPutToSleep[Node::eTYPE_COUNT]; + + //Input to this frame's island management (changed nodes/edges) + + //Input list of changes observed this frame. If there no changes, no work to be done. + Ps::Array mDirtyEdges[Edge::eEDGE_TYPE_COUNT]; + //Dirty nodes. These nodes lost at least one connection so we need to recompute islands from these nodes + //Ps::Array mDirtyNodes; + Cm::BitMap mDirtyMap; + PxU32 mLastMapIndex; + + //An array of nodes to activate + Ps::Array mActivatingNodes; + Ps::Array mDestroyedEdges; + Ps::Array mTempIslandIds; + + + //Temporary, transient data used for traversals. TODO - move to PxsSimpleIslandManager. Or if we keep it here, we can + //process multiple island simulations in parallel + Cm::PriorityQueue + mPriorityQueue; //! Priority queue used for graph traversal + Ps::Array mVisitedNodes; //! The list of nodes visited in the current traversal + Cm::BitMap mVisitedState; //! Indicates whether a node has been visited + Ps::Array mIslandSplitEdges[Edge::eEDGE_TYPE_COUNT]; + + Ps::Array mDeactivatingEdges[Edge::eEDGE_TYPE_COUNT]; + + Ps::Array* mFirstPartitionEdges; + Cm::BlockArray& mEdgeNodeIndices; + Ps::Array* mDestroyedPartitionEdges; + + PxU32* mNpIndexPtr; + + PxU64 mContextId; + +public: + + IslandSim(Ps::Array* firstPartitionEdges, Cm::BlockArray& edgeNodeIndices, Ps::Array* destroyedPartitionEdges, PxU64 contextID); + ~IslandSim() {} + + void resize(const PxU32 nbNodes, const PxU32 nbContactManagers, const PxU32 nbConstraints); + + void addRigidBody(PxsRigidBody* body, bool isKinematic, bool isActive, NodeIndex nodeIndex); + + void addArticulation(Sc::ArticulationSim* articulation, Dy::ArticulationV* llArtic, bool isActive, NodeIndex nodeIndex); + + void addContactManager(PxsContactManager* manager, NodeIndex nodeHandle1, NodeIndex nodeHandle2, EdgeIndex handle); + + void addConstraint(Dy::Constraint* constraint, NodeIndex nodeHandle1, NodeIndex nodeHandle2, EdgeIndex handle); + + void activateNode(NodeIndex index); + void deactivateNode(NodeIndex index); + void putNodeToSleep(NodeIndex index); + + void removeConnection(EdgeIndex edgeIndex); + + PX_FORCE_INLINE PxU32 getNbNodes() const { return mNodes.size(); } + + PX_FORCE_INLINE PxU32 getNbActiveNodes(Node::NodeType type) const { return mActiveNodes[type].size(); } + + PX_FORCE_INLINE const NodeIndex* getActiveNodes(Node::NodeType type) const { return mActiveNodes[type].begin(); } + + PX_FORCE_INLINE PxU32 getNbActiveKinematics() const { return mActiveKinematicNodes.size(); } + + PX_FORCE_INLINE const NodeIndex* getActiveKinematics() const { return mActiveKinematicNodes.begin(); } + + PX_FORCE_INLINE PxU32 getNbNodesToActivate(Node::NodeType type) const { return mActiveNodes[type].size() - mInitialActiveNodeCount[type]; } + + PX_FORCE_INLINE const NodeIndex* getNodesToActivate(Node::NodeType type) const { return mActiveNodes[type].begin() + mInitialActiveNodeCount[type]; } + + PX_FORCE_INLINE PxU32 getNbNodesToDeactivate(Node::NodeType type) const { return mNodesToPutToSleep[type].size(); } + + PX_FORCE_INLINE const NodeIndex* getNodesToDeactivate(Node::NodeType type) const { return mNodesToPutToSleep[type].begin(); } + + PX_FORCE_INLINE PxU32 getNbActivatedEdges(Edge::EdgeType type) const { return mActivatedEdges[type].size(); } + + PX_FORCE_INLINE const EdgeIndex* getActivatedEdges(Edge::EdgeType type) const { return mActivatedEdges[type].begin(); } + + PX_FORCE_INLINE PxU32 getNbActiveEdges(Edge::EdgeType type) const { return mActiveEdgeCount[type]; } + + PX_FORCE_INLINE PartitionEdge* getFirstPartitionEdge(IG::EdgeIndex edgeIndex) const { return (*mFirstPartitionEdges)[edgeIndex]; } + PX_FORCE_INLINE void setFirstPartitionEdge(IG::EdgeIndex edgeIndex, PartitionEdge* partitionEdge) { (*mFirstPartitionEdges)[edgeIndex] = partitionEdge; } + + //PX_FORCE_INLINE const EdgeIndex* getActiveEdges(Edge::EdgeType type) const { return mActiveEdges[type].begin(); } + + PX_FORCE_INLINE PxsRigidBody* getRigidBody(NodeIndex nodeIndex) const + { + const Node& node = mNodes[nodeIndex.index()]; + PX_ASSERT(node.mType == Node::eRIGID_BODY_TYPE); + return node.mRigidBody; + } + + PX_FORCE_INLINE Dy::ArticulationV* getLLArticulation(NodeIndex nodeIndex) const + { + const Node& node = mNodes[nodeIndex.index()]; + PX_ASSERT(node.mType == Node::eARTICULATION_TYPE); + return node.mLLArticulation; + } + + PX_FORCE_INLINE void clearDeactivations() + { + mNodesToPutToSleep[0].forceSize_Unsafe(0); + mNodesToPutToSleep[1].forceSize_Unsafe(0); + + mDeactivatingEdges[0].forceSize_Unsafe(0); + mDeactivatingEdges[1].forceSize_Unsafe(0); + } + + PX_FORCE_INLINE const Island& getIsland(IG::IslandId islandIndex) const { return mIslands[islandIndex]; } + + PX_FORCE_INLINE PxU32 getNbActiveIslands() const { return mActiveIslands.size(); } + PX_FORCE_INLINE const IslandId* getActiveIslands() const { return mActiveIslands.begin(); } + + PX_FORCE_INLINE PxU32 getNbDeactivatingEdges(const IG::Edge::EdgeType edgeType) const { return mDeactivatingEdges[edgeType].size(); } + PX_FORCE_INLINE const EdgeIndex* getDeactivatingEdges(const IG::Edge::EdgeType edgeType) const { return mDeactivatingEdges[edgeType].begin(); } + + PX_FORCE_INLINE PxU32 getNbDestroyedEdges() const { return mDestroyedEdges.size(); } + PX_FORCE_INLINE const EdgeIndex* getDestroyedEdges() const { return mDestroyedEdges.begin(); } + + PX_FORCE_INLINE PxU32 getNbDestroyedPartitionEdges() const { return mDestroyedPartitionEdges->size(); } + PX_FORCE_INLINE const PartitionEdge*const * getDestroyedPartitionEdges() const { return mDestroyedPartitionEdges->begin(); } + PX_FORCE_INLINE PartitionEdge** getDestroyedPartitionEdges() { return mDestroyedPartitionEdges->begin(); } + + PX_FORCE_INLINE PxU32 getNbDirtyEdges(IG::Edge::EdgeType type) const { return mDirtyEdges[type].size(); } + PX_FORCE_INLINE const EdgeIndex* getDirtyEdges(IG::Edge::EdgeType type) const { return mDirtyEdges[type].begin(); } + + PX_FORCE_INLINE const Edge& getEdge(const EdgeIndex edgeIndex) const { return mEdges[edgeIndex]; } + + PX_FORCE_INLINE Edge& getEdge(const EdgeIndex edgeIndex) { return mEdges[edgeIndex]; } + + PX_FORCE_INLINE const Node& getNode(const NodeIndex& nodeIndex) const { return mNodes[nodeIndex.index()]; } + + PX_FORCE_INLINE const Island& getIsland(const NodeIndex& nodeIndex) const { PX_ASSERT(mIslandIds[nodeIndex.index()] != IG_INVALID_ISLAND); return mIslands[mIslandIds[nodeIndex.index()]]; } + + PX_FORCE_INLINE PxU32 getIslandStaticTouchCount(const NodeIndex& nodeIndex) const { PX_ASSERT(mIslandIds[nodeIndex.index()] != IG_INVALID_ISLAND); return mIslandStaticTouchCount[mIslandIds[nodeIndex.index()]]; } + + PX_FORCE_INLINE const Cm::BitMap& getActiveContactManagerBitmap() const { return mActiveContactEdges; } + + PX_FORCE_INLINE PxU32 getActiveNodeIndex(const NodeIndex& nodeIndex) const { PxU32 activeNodeIndex = mActiveNodeIndex[nodeIndex.index()]; return activeNodeIndex;} + + PX_FORCE_INLINE const PxU32* getActiveNodeIndex() const { return mActiveNodeIndex.begin(); } + + PX_FORCE_INLINE PxU32 getNbActiveNodeIndex() const { return mActiveNodeIndex.size(); } + + void setKinematic(IG::NodeIndex nodeIndex); + + void setDynamic(IG::NodeIndex nodeIndex); + + PX_FORCE_INLINE void setEdgeNodeIndexPtr(PxU32* ptr) { mNpIndexPtr = ptr; } + + PX_FORCE_INLINE NodeIndex getNodeIndex1(IG::EdgeIndex index) const { return mEdgeNodeIndices[2 * index]; } + PX_FORCE_INLINE NodeIndex getNodeIndex2(IG::EdgeIndex index) const { return mEdgeNodeIndices[2 * index + 1]; } + + PX_FORCE_INLINE PxU32* getEdgeNodeIndexPtr() const { return mNpIndexPtr; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextId; } + + PxU32 getNbIslands() const { return mIslandStaticTouchCount.size(); } + + const PxU32* getIslandStaticTouchCount() const { return mIslandStaticTouchCount.begin(); } + + const PxU32* getIslandIds() const { return mIslandIds.begin(); } + + bool checkInternalConsistency(); + +private: + + void insertNewEdges(); + void removeDestroyedEdges(); + void wakeIslands(); + void wakeIslands2(); + void processNewEdges(); + void processLostEdges(Ps::Array& destroyedNodes, bool allowDeactivation, bool permitKinematicDeactivation, PxU32 dirtyNodeLimit); + + void removeConnectionInternal(EdgeIndex edgeIndex); + + void addConnection(NodeIndex nodeHandle1, NodeIndex nodeHandle2, Edge::EdgeType edgeType, EdgeIndex handle); + + void addConnectionToGraph(EdgeIndex index); + void removeConnectionFromGraph(EdgeIndex edgeIndex); + void connectEdge(EdgeInstance& instance, EdgeInstanceIndex edgeIndex, Node& source, NodeIndex destination); + void disconnectEdge(EdgeInstance& instance, EdgeInstanceIndex edgeIndex, Node& node); + + //Merges 2 islands together. The returned id is the id of the merged island + IslandId mergeIslands(IslandId island0, IslandId island1, NodeIndex node0, NodeIndex node1); + + void mergeIslandsInternal(Island& island0, Island& island1, IslandId islandId0, IslandId islandId1, NodeIndex node0, NodeIndex node1); + + + IslandSim& operator = (const IslandSim&); + IslandSim(const IslandSim&); + + void unwindRoute(PxU32 traversalIndex, NodeIndex lastNode, PxU32 hopCount, IslandId id); + + void activateIsland(IslandId island); + + void deactivateIsland(IslandId island); + + bool canFindRoot(NodeIndex startNode, NodeIndex targetNode, Ps::Array* visitedNodes); + + bool tryFastPath(NodeIndex startNode, NodeIndex targetNode, IslandId islandId); + + bool findRoute(NodeIndex startNode, NodeIndex targetNode, IslandId islandId); + + bool isPathTo(NodeIndex startNode, NodeIndex targetNode); + + void addNode(bool isActive, bool isKinematic, Node::NodeType type, NodeIndex nodeIndex); + + void activateNodeInternal(NodeIndex index); + void deactivateNodeInternal(NodeIndex index); + + PX_FORCE_INLINE void notifyReadyForSleeping(const NodeIndex nodeIndex) + { + Node& node = mNodes[nodeIndex.index()]; + //PX_ASSERT(node.isActive()); + node.setIsReadyForSleeping(); + } + + PX_FORCE_INLINE void notifyNotReadyForSleeping(const NodeIndex nodeIndex) + { + Node& node = mNodes[nodeIndex.index()]; + PX_ASSERT(node.isActive() || node.isActivating()); + node.clearIsReadyForSleeping(); + } + + PX_FORCE_INLINE void markIslandActive(IslandId islandId) + { + Island& island = mIslands[islandId]; + PX_ASSERT(!mIslandAwake.test(islandId)); + PX_ASSERT(island.mActiveIndex == IG_INVALID_ISLAND); + + mIslandAwake.set(islandId); + island.mActiveIndex = mActiveIslands.size(); + mActiveIslands.pushBack(islandId); + } + + PX_FORCE_INLINE void markIslandInactive(IslandId islandId) + { + Island& island = mIslands[islandId]; + PX_ASSERT(mIslandAwake.test(islandId)); + PX_ASSERT(island.mActiveIndex != IG_INVALID_ISLAND); + PX_ASSERT(mActiveIslands[island.mActiveIndex] == islandId); + IslandId replaceId = mActiveIslands[mActiveIslands.size()-1]; + PX_ASSERT(mIslandAwake.test(replaceId)); + Island& replaceIsland = mIslands[replaceId]; + replaceIsland.mActiveIndex = island.mActiveIndex; + mActiveIslands[island.mActiveIndex] = replaceId; + mActiveIslands.forceSize_Unsafe(mActiveIslands.size()-1); + island.mActiveIndex = IG_INVALID_ISLAND; + mIslandAwake.reset(islandId); + } + + PX_FORCE_INLINE void markKinematicActive(NodeIndex index) + { + Node& node = mNodes[index.index()]; + PX_ASSERT(node.isKinematic()); + if(node.mActiveRefCount == 0 && mActiveNodeIndex[index.index()] == IG_INVALID_NODE) + { + //PX_ASSERT(mActiveNodeIndex[index.index()] == IG_INVALID_NODE); + //node.mActiveNodeIndex = mActiveKinematicNodes.size(); + mActiveNodeIndex[index.index()] = mActiveKinematicNodes.size(); + mActiveKinematicNodes.pushBack(index); + } + } + + PX_FORCE_INLINE void markKinematicInactive(NodeIndex index) + { + Node& node = mNodes[index.index()]; + PX_ASSERT(node.isKinematic()); + PX_ASSERT(mActiveNodeIndex[index.index()] != IG_INVALID_NODE); + PX_ASSERT(mActiveKinematicNodes[mActiveNodeIndex[index.index()]].index() == index.index()); + + if(node.mActiveRefCount == 0) + { + //Only remove from active kinematic list if it has no active contacts referencing it *and* it is asleep + if(mActiveNodeIndex[index.index()] != IG_INVALID_NODE) + { + //Need to verify active node index because there is an edge case where a node could be woken, then put to + //sleep in the same frame. This would mean that it would not have an active index at this stage. + NodeIndex replaceIndex = mActiveKinematicNodes.back(); + PX_ASSERT(mActiveNodeIndex[replaceIndex.index()] == mActiveKinematicNodes.size()-1); + mActiveNodeIndex[replaceIndex.index()] = mActiveNodeIndex[index.index()]; + mActiveKinematicNodes[mActiveNodeIndex[index.index()]] = replaceIndex; + mActiveKinematicNodes.forceSize_Unsafe(mActiveKinematicNodes.size()-1); + mActiveNodeIndex[index.index()] = IG_INVALID_NODE; + } + } + } + + PX_FORCE_INLINE void markActive(NodeIndex index) + { + Node& node = mNodes[index.index()]; + PX_ASSERT(!node.isKinematic()); + PX_ASSERT(mActiveNodeIndex[index.index()] == IG_INVALID_NODE); + mActiveNodeIndex[index.index()] = mActiveNodes[node.mType].size(); + mActiveNodes[node.mType].pushBack(index); + } + + PX_FORCE_INLINE void markInactive(NodeIndex index) + { + Node& node = mNodes[index.index()]; + + PX_ASSERT(!node.isKinematic()); + PX_ASSERT(mActiveNodeIndex[index.index()] != IG_INVALID_NODE); + + Ps::Array& activeNodes = mActiveNodes[node.mType]; + + PX_ASSERT(activeNodes[mActiveNodeIndex[index.index()]].index() == index.index()); + const PxU32 initialActiveNodeCount = mInitialActiveNodeCount[node.mType]; + + if(mActiveNodeIndex[index.index()] < initialActiveNodeCount) + { + //It's in the initial active node set. We retain a list of active nodes, where the existing active nodes + //are at the beginning of the array and the newly activated nodes are at the end of the array... + //The solution is to move the node to the end of the initial active node list in this case + PxU32 activeNodeIndex = mActiveNodeIndex[index.index()]; + NodeIndex replaceIndex = activeNodes[initialActiveNodeCount-1]; + PX_ASSERT(mActiveNodeIndex[replaceIndex.index()] == initialActiveNodeCount-1); + mActiveNodeIndex[index.index()] = mActiveNodeIndex[replaceIndex.index()]; + mActiveNodeIndex[replaceIndex.index()] = activeNodeIndex; + activeNodes[activeNodeIndex] = replaceIndex; + activeNodes[mActiveNodeIndex[index.index()]] = index; + mInitialActiveNodeCount[node.mType]--; + } + + PX_ASSERT(!node.isKinematic()); + PX_ASSERT(mActiveNodeIndex[index.index()] != IG_INVALID_NODE); + PX_ASSERT(activeNodes[mActiveNodeIndex[index.index()]].index() == index.index()); + + NodeIndex replaceIndex = activeNodes.back(); + PX_ASSERT(mActiveNodeIndex[replaceIndex.index()] == activeNodes.size()-1); + mActiveNodeIndex[replaceIndex.index()] = mActiveNodeIndex[index.index()]; + activeNodes[mActiveNodeIndex[index.index()]] = replaceIndex; + activeNodes.forceSize_Unsafe(activeNodes.size()-1); + mActiveNodeIndex[index.index()] = IG_INVALID_NODE; + } + + PX_FORCE_INLINE void markEdgeActive(EdgeIndex index) + { + Edge& edge = mEdges[index]; + + PX_ASSERT((edge.mEdgeState & Edge::eACTIVATING) == 0); + + edge.mEdgeState |= Edge::eACTIVATING; + + mActivatedEdges[edge.mEdgeType].pushBack(index); + + mActiveEdgeCount[edge.mEdgeType]++; + + //Set the active bit... + if(edge.mEdgeType == Edge::eCONTACT_MANAGER) + mActiveContactEdges.set(index); + + NodeIndex nodeIndex1 = mEdgeNodeIndices[2 * index]; + NodeIndex nodeIndex2 = mEdgeNodeIndices[2 * index + 1]; + + if (nodeIndex1.index() != IG_INVALID_NODE && nodeIndex2.index() != IG_INVALID_NODE) + { + PX_ASSERT((!mNodes[nodeIndex1.index()].isKinematic()) || (!mNodes[nodeIndex2.index()].isKinematic()) || edge.getEdgeType() == IG::Edge::eCONTACT_MANAGER); + { + Node& node = mNodes[nodeIndex1.index()]; + + if(node.mActiveRefCount == 0 && node.isKinematic() && !(node.isActive() || node.isActivating())) + { + //Add to active kinematic list + markKinematicActive(nodeIndex1); + } + node.mActiveRefCount++; + } + + { + Node& node = mNodes[nodeIndex2.index()]; + if(node.mActiveRefCount == 0 && node.isKinematic() && !(node.isActive() || node.isActivating())) + { + //Add to active kinematic list + markKinematicActive(nodeIndex2); + } + node.mActiveRefCount++; + } + } + + } + + void removeEdgeFromActivatingList(EdgeIndex index); + + PX_FORCE_INLINE void removeEdgeFromIsland(Island& island, EdgeIndex edgeIndex) + { + Edge& edge = mEdges[edgeIndex]; + if(edge.mNextIslandEdge != IG_INVALID_EDGE) + { + PX_ASSERT(mEdges[edge.mNextIslandEdge].mPrevIslandEdge == edgeIndex); + mEdges[edge.mNextIslandEdge].mPrevIslandEdge = edge.mPrevIslandEdge; + } + else + { + PX_ASSERT(island.mLastEdge[edge.mEdgeType] == edgeIndex); + island.mLastEdge[edge.mEdgeType] = edge.mPrevIslandEdge; + } + + if(edge.mPrevIslandEdge != IG_INVALID_EDGE) + { + PX_ASSERT(mEdges[edge.mPrevIslandEdge].mNextIslandEdge == edgeIndex); + mEdges[edge.mPrevIslandEdge].mNextIslandEdge = edge.mNextIslandEdge; + } + else + { + PX_ASSERT(island.mFirstEdge[edge.mEdgeType] == edgeIndex); + island.mFirstEdge[edge.mEdgeType] = edge.mNextIslandEdge; + } + + island.mEdgeCount[edge.mEdgeType]--; + edge.mNextIslandEdge = edge.mPrevIslandEdge = IG_INVALID_EDGE; + } + + PX_FORCE_INLINE void addEdgeToIsland(Island& island, EdgeIndex edgeIndex) + { + Edge& edge = mEdges[edgeIndex]; + PX_ASSERT(edge.mNextIslandEdge == IG_INVALID_EDGE && edge.mPrevIslandEdge == IG_INVALID_EDGE); + + if(island.mLastEdge[edge.mEdgeType] != IG_INVALID_EDGE) + { + PX_ASSERT(mEdges[island.mLastEdge[edge.mEdgeType]].mNextIslandEdge == IG_INVALID_EDGE); + mEdges[island.mLastEdge[edge.mEdgeType]].mNextIslandEdge = edgeIndex; + } + else + { + PX_ASSERT(island.mFirstEdge[edge.mEdgeType] == IG_INVALID_EDGE); + island.mFirstEdge[edge.mEdgeType] = edgeIndex; + } + + edge.mPrevIslandEdge = island.mLastEdge[edge.mEdgeType]; + island.mLastEdge[edge.mEdgeType] = edgeIndex; + island.mEdgeCount[edge.mEdgeType]++; + } + + PX_FORCE_INLINE void removeNodeFromIsland(Island& island, NodeIndex nodeIndex) + { + Node& node = mNodes[nodeIndex.index()]; + if(node.mNextNode.isValid()) + { + PX_ASSERT(mNodes[node.mNextNode.index()].mPrevNode.index() == nodeIndex.index()); + mNodes[node.mNextNode.index()].mPrevNode = node.mPrevNode; + } + else + { + PX_ASSERT(island.mLastNode.index() == nodeIndex.index()); + island.mLastNode = node.mPrevNode; + } + + if(node.mPrevNode.isValid()) + { + PX_ASSERT(mNodes[node.mPrevNode.index()].mNextNode.index() == nodeIndex.index()); + mNodes[node.mPrevNode.index()].mNextNode = node.mNextNode; + } + else + { + PX_ASSERT(island.mRootNode.index() == nodeIndex.index()); + island.mRootNode = node.mNextNode; + } + + island.mSize[node.mType]--; + + node.mNextNode = NodeIndex(); node.mPrevNode = NodeIndex(); + } + + //void setEdgeConnectedInternal(EdgeIndex edgeIndex); + + //void setEdgeDisconnectedInternal(EdgeIndex edgeIndex); + + friend class SimpleIslandManager; + friend class ThirdPassTask; + +}; + + +} + + +struct PartitionIndexData +{ + PxU16 mPartitionIndex; //! The current partition this edge is in. Used to find the edge efficiently. PxU8 is probably too small (256 partitions max) but PxU16 should be more than enough + PxU8 mPatchIndex; //! The patch index for this partition edge. There may be multiple entries for a given edge if there are multiple patches. + PxU8 mCType; //! The type of constraint this is + PxU32 mPartitionEntryIndex; //! index of partition edges for this partition +}; + +struct PartitionNodeData +{ + IG::NodeIndex mNodeIndex0; + IG::NodeIndex mNodeIndex1; + PxU32 mNextIndex0; + PxU32 mNextIndex1; +}; + + +#define INVALID_PARTITION_INDEX 0xFFFF + +struct PartitionEdge +{ + IG::EdgeIndex mEdgeIndex; //! The edge index into the island manager. Used to identify the contact manager/constraint + IG::NodeIndex mNode0; //! The node index for node 0. Can be obtained from the edge index alternatively + IG::NodeIndex mNode1; //! The node idnex for node 1. Can be obtained from the edge index alternatively + bool mInfiniteMass0; //! Whether body 0 is kinematic + bool mInfiniteMass1; //! Whether body 1 is kinematic + + PartitionEdge* mNextPatch; //! for the contact manager has more than 1 patch, we have next patch's edge and previous patch's edge to connect to this edge + + PxU32 mUniqueIndex; //! a unique ID for this edge + + PartitionEdge() : mEdgeIndex(IG_INVALID_EDGE), mInfiniteMass0(false), + mInfiniteMass1(false), mNextPatch(NULL) + { + } +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h b/src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h new file mode 100644 index 000000000..9990dcb77 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h @@ -0,0 +1,49 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_KERNEL_WRANGLER_H +#define PXS_KERNEL_WRANGLER_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + class KernelWrangler; + class PxGpuDispatcher; + class PxErrorCallback; + + class PxsKernelWranglerManager + { + public: + virtual ~PxsKernelWranglerManager(){} + virtual KernelWrangler* getKernelWrangler() = 0; + }; +} +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h b/src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h new file mode 100644 index 000000000..83a5e1337 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h @@ -0,0 +1,143 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_MATERIALCOMBINER_H +#define PXS_MATERIALCOMBINER_H + +#include "PxsMaterialCore.h" + +namespace physx +{ + + class PxsMaterialCombiner + { + public: + + class PxsCombinedMaterial + { + public: + PxReal staFriction; + PxReal dynFriction; + PxU32 flags; //PxMaterialFlag::eDISABLE_FRICTION, PxMaterialFlag::eDISABLE_STRONG_FRICTION. + }; + + static PxReal combineRestitution(const PxsMaterialData& material0, const PxsMaterialData& material1); + + PxsMaterialCombiner(PxReal staticFrictionScaling, PxReal dynamicFrictionScaling); + + PxsCombinedMaterial combineIsotropicFriction(const PxsMaterialData& material0, const PxsMaterialData& material1); + + //ML:: move this function to header file to avoid LHS in Xbox + PX_FORCE_INLINE void combineIsotropicFriction(const PxsMaterialData& mat0, const PxsMaterialData& mat1, PxReal& dynamicFriction, PxReal& staticFriction, PxU32& flags) + { + + const PxU32 combineFlags= (mat0.flags | mat1.flags); //& (PxMaterialFlag::eDISABLE_STRONG_FRICTION|PxMaterialFlag::eDISABLE_FRICTION); //eventually set DisStrongFric flag, lower all others. + + if (!(combineFlags & PxMaterialFlag::eDISABLE_FRICTION)) + { + const PxI32 fictionCombineMode = PxMax(mat0.getFrictionCombineMode(), mat1.getFrictionCombineMode()); + PxReal dynFriction = 0.f; + PxReal staFriction = 0.f; + + + switch (fictionCombineMode) + { + case PxCombineMode::eAVERAGE: + dynFriction = 0.5f * (mat0.dynamicFriction + mat1.dynamicFriction); + staFriction = 0.5f * (mat0.staticFriction + mat1.staticFriction); + break; + case PxCombineMode::eMIN: + dynFriction = PxMin(mat0.dynamicFriction, mat1.dynamicFriction); + staFriction = PxMin(mat0.staticFriction, mat1.staticFriction); + break; + case PxCombineMode::eMULTIPLY: + dynFriction = (mat0.dynamicFriction * mat1.dynamicFriction); + staFriction = (mat0.staticFriction * mat1.staticFriction); + break; + case PxCombineMode::eMAX: + dynFriction = PxMax(mat0.dynamicFriction, mat1.dynamicFriction); + staFriction = PxMax(mat0.staticFriction, mat1.staticFriction); + break; + } + + dynFriction*=mDynamicFrictionScaling; + staFriction*=mStaticFrictionScaling; + //isotropic case + const PxReal fDynFriction = PxMax(dynFriction, 0.f); + + const PxReal fStaFriction = physx::intrinsics::fsel(staFriction - fDynFriction, staFriction*mStaticFrictionScaling, fDynFriction); + /*dest.dynFriction = fDynFriction; + dest.staFriction = fStaFriction;*/ + + dynamicFriction = fDynFriction; + staticFriction = fStaFriction; + flags = combineFlags; + } + else + { + /* dest.flags |= PxMaterialFlag::eDISABLE_STRONG_FRICTION; + dest.staFriction = 0.0f; + dest.dynFriction = 0.0f;*/ + flags = (combineFlags | PxMaterialFlag::eDISABLE_STRONG_FRICTION); + dynamicFriction = 0.f; + staticFriction = 0.f; + } + + } + + + //private: + protected: + static PX_FORCE_INLINE PxReal combineScalars(PxReal a, PxReal b, PxI32 nxCombineMode) + { + switch (nxCombineMode) + { + case PxCombineMode::eAVERAGE: + return 0.5f * (a + b); + case PxCombineMode::eMIN: + return PxMin(a,b); + case PxCombineMode::eMULTIPLY: + return a * b; + case PxCombineMode::eMAX: + return PxMax(a,b); + default: + /* Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Sc::MaterialCombiner::combineScalars(): unknown combine mode");*/ + return PxReal(0); + } + } + + PxReal mStaticFrictionScaling, mDynamicFrictionScaling; + + }; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h new file mode 100644 index 000000000..9cf8d7b78 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h @@ -0,0 +1,61 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_MEMORY_MANAGER_H +#define PXS_MEMORY_MANAGER_H + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxSimpleTypes.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + namespace shdfnd + { + class VirtualAllocatorCallback; + } + + class PxGpuDispatcher; + + class PxsMemoryManager + { + public: + virtual ~PxsMemoryManager(){} + virtual Ps::VirtualAllocatorCallback* createHostMemoryAllocator(const PxU32 gpuComputeVersion = 0) = 0; + virtual Ps::VirtualAllocatorCallback* createDeviceMemoryAllocator(const PxU32 gpuComputeVersion = 0) = 0; + + virtual void destroyMemoryAllocator() = 0; + + }; + + PxsMemoryManager* createMemoryManager(); +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h b/src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h new file mode 100644 index 000000000..d06467212 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h @@ -0,0 +1,143 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_NPHASE_IMPLEMENTATION_CONTEXT_H +#define PXS_NPHASE_IMPLEMENTATION_CONTEXT_H + +#include "PxvNphaseImplementationContext.h" +#include "PxsContactManagerState.h" +#include "PxcNpCache.h" + +namespace physx +{ + +struct PxsContactManagers : PxsContactManagerBase +{ + Ps::Array mOutputContactManagers; + Ps::Array mContactManagerMapping; + Ps::Array mCaches; + + + PxsContactManagers(const PxU32 bucketId) : PxsContactManagerBase(bucketId), + mOutputContactManagers(PX_DEBUG_EXP("mOutputContactManagers")), + mContactManagerMapping(PX_DEBUG_EXP("mContactManagerMapping")), + mCaches(PX_DEBUG_EXP("mCaches")) + { + } + + void clear() + { + mOutputContactManagers.forceSize_Unsafe(0); + mContactManagerMapping.forceSize_Unsafe(0); + mCaches.forceSize_Unsafe(0); + + } +private: + PX_NOCOPY(PxsContactManagers) +}; + + +class PxsNphaseImplementationContext: public PxvNphaseImplementationContextUsableAsFallback +{ +public: + static PxsNphaseImplementationContext* create(PxsContext& context, IG::IslandSim* islandSim); + + PxsNphaseImplementationContext(PxsContext& context, IG::IslandSim* islandSim, PxU32 index = 0): PxvNphaseImplementationContextUsableAsFallback(context), mNarrowPhasePairs(index), mNewNarrowPhasePairs(index), + mModifyCallback(NULL), mIslandSim(islandSim) {} + virtual void destroy(); + virtual void updateContactManager(PxReal dt, bool hasBoundsArrayChanged, bool hasContactDistanceChanged, PxBaseTask* continuation, PxBaseTask* firstPassContinuation); + virtual void postBroadPhaseUpdateContactManager() {} + virtual void secondPassUpdateContactManager(PxReal dt, PxBaseTask* continuation); + + virtual void registerContactManager(PxsContactManager* cm, PxI32 touching, PxU32 numPatches); + virtual void registerContactManagers(PxsContactManager** cm, PxU32 nbContactManagers, PxU32 maxContactManagerId); + virtual void unregisterContactManager(PxsContactManager* cm); + virtual void unregisterContactManagerFallback(PxsContactManager* cm, PxsContactManagerOutput* cmOutputs); + + + virtual void refreshContactManager(PxsContactManager* cm); + virtual void refreshContactManagerFallback(PxsContactManager* cm, PxsContactManagerOutput* cmOutputs); + + virtual void registerShape(const PxsShapeCore& shapeCore); + + virtual void updateShapeMaterial(const PxsShapeCore& shapeCore); + virtual void updateShapeContactOffset(const PxsShapeCore& shapeCore); + + virtual void unregisterShape(const PxsShapeCore& shapeCore); + + virtual void registerMaterial(const PxsMaterialCore& materialCore); + virtual void updateMaterial(const PxsMaterialCore& materialCore); + virtual void unregisterMaterial(const PxsMaterialCore& materialCore); + + virtual void appendContactManagers(); + virtual void appendContactManagersFallback(PxsContactManagerOutput* cmOutputs); + + virtual void removeContactManagersFallback(PxsContactManagerOutput* cmOutputs); + + virtual void setContactModifyCallback(PxContactModifyCallback* callback) { mModifyCallback = callback; } + + virtual PxsContactManagerOutputIterator getContactManagerOutputs(); + + virtual PxsContactManagerOutput& getNewContactManagerOutput(PxU32 npIndex); + + virtual PxsContactManagerOutput* getGPUContactManagerOutputBase() { return NULL; } + + virtual void acquireContext(){} + virtual void releaseContext(){} + virtual void preallocateNewBuffers(PxU32 /*nbNewPairs*/, PxU32 /*maxIndex*/) { /*TODO - implement if it's useful to do so*/} + + void processContactManager(PxReal dt, PxsContactManagerOutput* cmOutputs, PxBaseTask* continuation); + void processContactManagerSecondPass(PxReal dt, PxBaseTask* continuation); + void fetchUpdateContactManager() {} + + + + void startNarrowPhaseTasks() {} + + + + Ps::Array mRemovedContactManagers; + PxsContactManagers mNarrowPhasePairs; + PxsContactManagers mNewNarrowPhasePairs; + + PxContactModifyCallback* mModifyCallback; + + IG::IslandSim* mIslandSim; + +private: + + void unregisterContactManagerInternal(PxU32 npIndex, PxsContactManagers& managers, PxsContactManagerOutput* cmOutputs); + + PX_NOCOPY(PxsNphaseImplementationContext) +}; + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h b/src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h new file mode 100644 index 000000000..571cba466 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h @@ -0,0 +1,169 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_BODYATOM_H +#define PXS_BODYATOM_H + +#include "PxcRigidBody.h" +#include "PxvDynamics.h" + +namespace physx +{ + +class PxsRigidBody : public PxcRigidBody +{ + public: + PX_FORCE_INLINE PxsRigidBody(PxsBodyCore* core) : PxcRigidBody(core) { } + PX_FORCE_INLINE ~PxsRigidBody() {} + + PX_FORCE_INLINE const PxTransform& getPose() const { PX_ASSERT(mCore->body2World.isSane()); return mCore->body2World; } + + //PX_FORCE_INLINE const Cm::SpatialVector& getAccelerationV() const { return mAcceleration; } + //PX_FORCE_INLINE void setAccelerationV(const Cm::SpatialVector& v) { mAcceleration = v; } + + PX_FORCE_INLINE const PxVec3& getLinearVelocity() const { PX_ASSERT(mCore->linearVelocity.isFinite()); return mCore->linearVelocity; } + PX_FORCE_INLINE const PxVec3& getAngularVelocity() const { PX_ASSERT(mCore->angularVelocity.isFinite()); return mCore->angularVelocity; } + + PX_FORCE_INLINE void setVelocity(const PxVec3& linear, + const PxVec3& angular) { PX_ASSERT(linear.isFinite()); PX_ASSERT(angular.isFinite()); + mCore->linearVelocity = linear; + mCore->angularVelocity = angular; } + PX_FORCE_INLINE void setLinearVelocity(const PxVec3& linear) { PX_ASSERT(linear.isFinite()); mCore->linearVelocity = linear; } + PX_FORCE_INLINE void setAngularVelocity(const PxVec3& angular) { PX_ASSERT(angular.isFinite()); mCore->angularVelocity = angular; } + + PX_FORCE_INLINE void constrainLinearVelocity(); + PX_FORCE_INLINE void constrainAngularVelocity(); + + PX_FORCE_INLINE PxU32 getIterationCounts() { return mCore->solverIterationCounts; } + + PX_FORCE_INLINE PxReal getReportThreshold() const { return mCore->contactReportThreshold; } + + // AP newccd todo: merge into get both velocities, compute inverse transform once, precompute mLastTransform.getInverse() + PX_FORCE_INLINE PxVec3 getLinearMotionVelocity(PxReal invDt) const { + // delta(t0(x))=t1(x) + // delta(t0(t0`(x)))=t1(t0`(x)) + // delta(x)=t1(t0`(x)) + PxVec3 deltaP = mCore->body2World.p - getLastCCDTransform().p; + return deltaP * invDt; + } + PX_FORCE_INLINE PxVec3 getAngularMotionVelocity(PxReal invDt) const { + PxQuat deltaQ = mCore->body2World.q * getLastCCDTransform().q.getConjugate(); + PxVec3 axis; + PxReal angle; + deltaQ.toRadiansAndUnitAxis(angle, axis); + return axis * angle * invDt; + } + PX_FORCE_INLINE PxVec3 getLinearMotionVelocity(PxReal dt, const PxsBodyCore* PX_RESTRICT bodyCore) const { + // delta(t0(x))=t1(x) + // delta(t0(t0`(x)))=t1(t0`(x)) + // delta(x)=t1(t0`(x)) + PxVec3 deltaP = bodyCore->body2World.p - getLastCCDTransform().p; + return deltaP * 1.0f / dt; + } + PX_FORCE_INLINE PxVec3 getAngularMotionVelocity(PxReal dt, const PxsBodyCore* PX_RESTRICT bodyCore) const { + PxQuat deltaQ = bodyCore->body2World.q * getLastCCDTransform().q.getConjugate(); + PxVec3 axis; + PxReal angle; + deltaQ.toRadiansAndUnitAxis(angle, axis); + return axis * angle * 1.0f/dt; + } + + PX_FORCE_INLINE const PxTransform& getLastCCDTransform() const { return mLastTransform;} + PX_FORCE_INLINE void saveLastCCDTransform() { mLastTransform = mCore->body2World; } + + PX_FORCE_INLINE bool isKinematic() const { return (mCore->inverseMass == 0.0f); } + + PX_FORCE_INLINE void setPose(const PxTransform& pose) { mCore->body2World = pose; } + PX_FORCE_INLINE void setPosition(const PxVec3& position) { mCore->body2World.p = position; } + PX_FORCE_INLINE PxReal getInvMass() const { return mCore->inverseMass; } + PX_FORCE_INLINE PxVec3 getInvInertia() const { return mCore->inverseInertia; } + PX_FORCE_INLINE PxReal getMass() const { return 1.0f/mCore->inverseMass; } + PX_FORCE_INLINE PxVec3 getInertia() const { return PxVec3(1.0f/mCore->inverseInertia.x, + 1.0f/mCore->inverseInertia.y, + 1.0f/mCore->inverseInertia.z); } + PX_FORCE_INLINE PxsBodyCore& getCore() { return *mCore; } + PX_FORCE_INLINE const PxsBodyCore& getCore() const { return *mCore; } + + PX_FORCE_INLINE PxU32 isActivateThisFrame() const { return PxU32(mInternalFlags & eACTIVATE_THIS_FRAME); } + + PX_FORCE_INLINE PxU32 isDeactivateThisFrame() const { return PxU32(mInternalFlags & eDEACTIVATE_THIS_FRAME); } + + PX_FORCE_INLINE PxU32 isFreezeThisFrame() const { return PxU32(mInternalFlags & eFREEZE_THIS_FRAME); } + + PX_FORCE_INLINE PxU32 isUnfreezeThisFrame() const { return PxU32(mInternalFlags & eUNFREEZE_THIS_FRAME); } + + PX_FORCE_INLINE void clearFreezeFlag() { mInternalFlags &= ~eFREEZE_THIS_FRAME; } + + PX_FORCE_INLINE void clearUnfreezeFlag() { mInternalFlags &= ~eUNFREEZE_THIS_FRAME; } + + PX_FORCE_INLINE void clearAllFrameFlags() { mInternalFlags &= (eFROZEN | eDISABLE_GRAVITY); } + + void advanceToToi(PxReal toi, PxReal dt, bool clip); + void advancePrevPoseToToi(PxReal toi); + PxTransform getAdvancedTransform(PxReal toi) const; + Cm::SpatialVector getPreSolverVelocities() const; + + +}; + + +void PxsRigidBody::constrainLinearVelocity() +{ + const PxU32 lockFlags = mCore->lockFlags; + + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + mCore->linearVelocity.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + mCore->linearVelocity.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + mCore->linearVelocity.z = 0.f; + } +} + +void PxsRigidBody::constrainAngularVelocity() +{ + const PxU32 lockFlags = mCore->lockFlags; + + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + mCore->angularVelocity.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + mCore->angularVelocity.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + mCore->angularVelocity.z = 0.f; + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h b/src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h new file mode 100644 index 000000000..7cadd5c01 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h @@ -0,0 +1,52 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_SHAPESIM_H +#define PXS_SHAPESIM_H + +#include "PxsBodySim.h" +#include "PxsIslandNodeIndex.h" + +namespace physx +{ +//PxsBodySim is 12 or 16 bytes +struct PxsShapeSim// : public PxsBodySim +{ + PxsShapeCore* mShapeCore; // 4 or 8 + IG::NodeIndex mBodySimIndex; // 8 or 12 + PxU32 mElementIndex; // 12 or 16 transform cache and bound index + PxU32 mShapeIndex; // 16 or 20 +#if PX_P64_FAMILY + PxU32 mPad[3]; +#endif +}; + +}//physx + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h new file mode 100644 index 000000000..6c78eaa01 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h @@ -0,0 +1,202 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_SIMPLE_ISLAND_GEN_H +#define PXS_SIMPLE_ISLAND_GEN_H + +#include "PxsIslandSim.h" +#include "CmTask.h" + +namespace physx +{ + +namespace Sc +{ + class Interaction; +} +namespace IG +{ + + class SimpleIslandManager; + +class ThirdPassTask : public Cm::Task +{ + SimpleIslandManager& mIslandManager; + IslandSim& mIslandSim; + +public: + + ThirdPassTask(PxU64 contextID, SimpleIslandManager& islandManager, IslandSim& islandSim); + + virtual void runInternal(); + + virtual const char* getName() const + { + return "ThirdPassIslandGenTask"; + } + +private: + PX_NOCOPY(ThirdPassTask) +}; + +class PostThirdPassTask : public Cm::Task +{ + SimpleIslandManager& mIslandManager; + +public: + + PostThirdPassTask(PxU64 contextID, SimpleIslandManager& islandManager); + + virtual void runInternal(); + + virtual const char* getName() const + { + return "PostThirdPassTask"; + } +private: + PX_NOCOPY(PostThirdPassTask) +}; + +class SimpleIslandManager +{ + + HandleManager mNodeHandles; //! Handle manager for nodes + HandleManager mEdgeHandles; //! Handle manager for edges + + //An array of destroyed nodes + Ps::Array mDestroyedNodes; + Cm::BlockArray mInteractions; + + + //Edges destroyed this frame + Ps::Array mDestroyedEdges; + Ps::Array mFirstPartitionEdges; + Ps::Array mDestroyedPartitionEdges; + //KS - stores node indices for a given edge. Node index 0 is at 2* edgeId and NodeIndex1 is at 2*edgeId + 1 + //can also be used for edgeInstance indexing so there's no need to figure out outboundNode ID either! + Cm::BlockArray mEdgeNodeIndices; + Cm::BlockArray mConstraintOrCm; //! Pointers to either the constraint or Cm for this pair + + Cm::BitMap mConnectedMap; + + IslandSim mIslandManager; + IslandSim mSpeculativeIslandManager; + + ThirdPassTask mSpeculativeThirdPassTask; + ThirdPassTask mAccurateThirdPassTask; + + PostThirdPassTask mPostThirdPassTask; + PxU32 mMaxDirtyNodesPerFrame; + + PxU64 mContextID; +public: + + SimpleIslandManager(bool useEnhancedDeterminism, PxU64 contextID); + + ~SimpleIslandManager(); + + NodeIndex addRigidBody(PxsRigidBody* body, bool isKinematic, bool isActive); + + void removeNode(const NodeIndex index); + + NodeIndex addArticulation(Sc::ArticulationSim* articulation, Dy::ArticulationV* llArtic, bool isActive); + + EdgeIndex addContactManager(PxsContactManager* manager, NodeIndex nodeHandle1, NodeIndex nodeHandle2, Sc::Interaction* interaction); + + EdgeIndex addConstraint(Dy::Constraint* constraint, NodeIndex nodeHandle1, NodeIndex nodeHandle2, Sc::Interaction* interaction); + + bool isConnected(EdgeIndex edgeIndex) const { return !!mConnectedMap.test(edgeIndex); } + + PX_FORCE_INLINE NodeIndex getEdgeIndex(EdgeInstanceIndex edgeIndex) const { return mEdgeNodeIndices[edgeIndex]; } + + void activateNode(NodeIndex index); + void deactivateNode(NodeIndex index); + void putNodeToSleep(NodeIndex index); + + void removeConnection(EdgeIndex edgeIndex); + + void firstPassIslandGen(); + void additionalSpeculativeActivation(); + void secondPassIslandGen(); + void thirdPassIslandGen(PxBaseTask* continuation); + + void clearDestroyedEdges(); + + void setEdgeConnected(EdgeIndex edgeIndex); + void setEdgeDisconnected(EdgeIndex edgeIndex); + + bool getIsEdgeConnected(EdgeIndex edgeIndex); + + void setEdgeRigidCM(const EdgeIndex edgeIndex, PxsContactManager* cm); + + void clearEdgeRigidCM(const EdgeIndex edgeIndex); + + void setKinematic(IG::NodeIndex nodeIndex); + + void setDynamic(IG::NodeIndex nodeIndex); + + const IslandSim& getSpeculativeIslandSim() const { return mSpeculativeIslandManager; } + const IslandSim& getAccurateIslandSim() const { return mIslandManager; } + + IslandSim& getAccurateIslandSim() { return mIslandManager; } + + PX_FORCE_INLINE PxU32 getNbEdgeHandles() const { return mEdgeHandles.getTotalHandles(); } + + PX_FORCE_INLINE PxU32 getNbNodeHandles() const { return mNodeHandles.getTotalHandles(); } + + void deactivateEdge(const EdgeIndex edge); + + PX_FORCE_INLINE PxsContactManager* getContactManager(IG::EdgeIndex edgeId) const { return reinterpret_cast(mConstraintOrCm[edgeId]); } + PX_FORCE_INLINE PxsContactManager* getContactManagerUnsafe(IG::EdgeIndex edgeId) const { return reinterpret_cast(mConstraintOrCm[edgeId]); } + PX_FORCE_INLINE Dy::Constraint* getConstraint(IG::EdgeIndex edgeId) const { return reinterpret_cast(mConstraintOrCm[edgeId]); } + PX_FORCE_INLINE Dy::Constraint* getConstraintUnsafe(IG::EdgeIndex edgeId) const { return reinterpret_cast(mConstraintOrCm[edgeId]); } + + PX_FORCE_INLINE Sc::Interaction* getInteraction(IG::EdgeIndex edgeId) const { return mInteractions[edgeId]; } + + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + + bool checkInternalConsistency(); + + +private: + + friend class ThirdPassTask; + friend class PostThirdPassTask; + + bool validateDeactivations() const; + + PX_NOCOPY(SimpleIslandManager) +}; + + + +} +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h b/src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h new file mode 100644 index 000000000..625352642 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h @@ -0,0 +1,139 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXS_SIMULATION_CONTROLLER_H +#define PXS_SIMULATION_CONTROLLER_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxPreprocessor.h" +#include "foundation/PxTransform.h" +#include "CmBitMap.h" +#include "PsArray.h" + + +namespace physx +{ + namespace Dy + { + class Context; + struct Constraint; + class ArticulationV; + } + + namespace Cm + { + class EventProfiler; + } + + namespace Bp + { + class BoundsArray; + class BroadPhase; + } + + namespace IG + { + class SimpleIslandManager; + class IslandSim; + class NodeIndex; + } + + class PxGpuDispatcher; + class PxsTransformCache; + class PxvNphaseImplementationContext; + class PxBaseTask; + + struct PxsBodySim; + struct PxsShapeSim; + class PxsRigidBody; + class PxsKernelWranglerManager; + class PxsHeapMemoryAllocatorManager; + + template class PxgIterator; + struct PxgSolverConstraintManagerConstants; + + + class PxsSimulationControllerCallback + { + public: + virtual void updateScBodyAndShapeSim(PxBaseTask* continuation) = 0; + virtual PxU32 getNbCcdBodies() = 0; + + virtual ~PxsSimulationControllerCallback() {} + }; + + + class PxsSimulationController + { + public: + PxsSimulationController(PxsSimulationControllerCallback* callback): mCallback(callback){} + virtual ~PxsSimulationController(){} + + virtual void addJoint(const PxU32 edgeIndex, Dy::Constraint* constraint, IG::IslandSim& islandSim, Ps::Array& jointIndices, + Ps::Array& managerIter, PxU32 uniqueId) = 0; + virtual void removeJoint(const PxU32 edgeIndex, Dy::Constraint* constraint, Ps::Array& jointIndices, IG::IslandSim& islandSim) = 0; + virtual void addShape(PxsShapeSim* shapeSim, const PxU32 index) = 0; + virtual void removeShape(const PxU32 index) = 0; + virtual void addDynamic(PxsRigidBody* rigidBody, const IG::NodeIndex& nodeIndex) = 0; + virtual void addDynamics(PxsRigidBody** rigidBody, const PxU32* nodeIndex, PxU32 nbToProcess) = 0; + virtual void addArticulation(Dy::ArticulationV* articulation, const IG::NodeIndex& nodeIndex) = 0; + virtual void releaseArticulation(Dy::ArticulationV* articulation) = 0; + virtual void releaseDeferredArticulationIds() = 0; + virtual void updateDynamic(const bool isArticulationLink, const IG::NodeIndex&) = 0; + virtual void updateJoint(const PxU32 edgeIndex, Dy::Constraint* constraint) = 0; + virtual void updateBodies(PxsRigidBody** rigidBodies, PxU32* nodeIndices, const PxU32 nbBodies) = 0; + virtual void updateBodiesAndShapes(PxBaseTask* continuation) = 0; + virtual void update(const PxU32 bitMapWordCounts) = 0; + virtual void gpuDmabackData(PxsTransformCache& cache, Bp::BoundsArray& boundArray, Cm::BitMapPinned& changedAABBMgrHandles) = 0; + virtual void udpateScBodyAndShapeSim(PxsTransformCache& cache, Bp::BoundsArray& boundArray, PxBaseTask* continuation) = 0; + virtual PxU32* getActiveBodies() = 0; + virtual PxU32* getDeactiveBodies() = 0; + virtual void** getRigidBodies() = 0; + virtual PxU32 getNbBodies() = 0; + + virtual PxU32* getUnfrozenShapes() = 0; + virtual PxU32* getFrozenShapes() = 0; + virtual PxsShapeSim** getShapeSims() = 0; + virtual PxU32 getNbFrozenShapes() = 0; + virtual PxU32 getNbUnfrozenShapes() = 0; + + virtual void clear() = 0; + virtual void setBounds(Bp::BoundsArray* boundArray) = 0; + virtual void reserve(const PxU32 nbBodies) = 0; + + protected: + PxsSimulationControllerCallback* mCallback; + + }; + + PxsSimulationController* createSimulationController(PxsSimulationControllerCallback* callback); +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h b/src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h new file mode 100644 index 000000000..1970356f1 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h @@ -0,0 +1,144 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXS_TRANSFORM_CACHE_H +#define PXS_TRANSFORM_CACHE_H + +#include "CmPhysXCommon.h" +#include "CmIDPool.h" +#include "CmBitMap.h" +#include "PsUserAllocated.h" +#include "PsAllocator.h" + +#define PX_DEFAULT_CACHE_SIZE 512 + +namespace physx +{ + struct PxsTransformFlag + { + enum Flags + { + eFROZEN = (1 << 0) + }; + }; + + struct PX_ALIGN_PREFIX(16) PxsCachedTransform + { + PxTransform transform; + PxU32 flags; + + PX_FORCE_INLINE PxU32 isFrozen() const { return flags & PxsTransformFlag::eFROZEN; } + } + PX_ALIGN_SUFFIX(16); + + + class PxsTransformCache : public Ps::UserAllocated + { + typedef PxU32 RefCountType; + + public: + PxsTransformCache(Ps::VirtualAllocatorCallback& allocatorCallback) : mTransformCache(Ps::VirtualAllocator(&allocatorCallback)), mHasAnythingChanged(true) + { + /*mTransformCache.reserve(PX_DEFAULT_CACHE_SIZE); + mTransformCache.forceSize_Unsafe(PX_DEFAULT_CACHE_SIZE);*/ + mUsedSize = 0; + } + + void initEntry(PxU32 index) + { + PxU32 oldCapacity = mTransformCache.capacity(); + if (index >= oldCapacity) + { + PxU32 newCapacity = Ps::nextPowerOfTwo(index); + mTransformCache.reserve(newCapacity); + mTransformCache.forceSize_Unsafe(newCapacity); + } + mUsedSize = PxMax(mUsedSize, index + 1u); + } + + + PX_FORCE_INLINE void setTransformCache(const PxTransform& transform, const PxU32 flags, const PxU32 index) + { + mTransformCache[index].transform = transform; + mTransformCache[index].flags = flags; + mHasAnythingChanged = true; + } + + PX_FORCE_INLINE const PxsCachedTransform& getTransformCache(const PxU32 index) const + { + return mTransformCache[index]; + } + + + PX_FORCE_INLINE PxsCachedTransform& getTransformCache(const PxU32 index) + { + return mTransformCache[index]; + } + + PX_FORCE_INLINE void shiftTransforms(const PxVec3& shift) + { + for (PxU32 i = 0; i < mTransformCache.capacity(); i++) + { + mTransformCache[i].transform.p += shift; + } + mHasAnythingChanged = true; + } + + PX_FORCE_INLINE PxU32 getTotalSize() const + { + return mUsedSize; + } + + PX_FORCE_INLINE const PxsCachedTransform* getTransforms() const + { + return mTransformCache.begin(); + } + + PX_FORCE_INLINE PxsCachedTransform* getTransforms() + { + return mTransformCache.begin(); + } + + PX_FORCE_INLINE Ps::Array* getCachedTransformArray() + { + return &mTransformCache; + } + + PX_FORCE_INLINE void resetChangedState() { mHasAnythingChanged = false; } + PX_FORCE_INLINE void setChangedState() { mHasAnythingChanged = true; } + PX_FORCE_INLINE bool hasChanged() const { return mHasAnythingChanged; } + + private: + Ps::Array mTransformCache; + PxU32 mUsedSize; + bool mHasAnythingChanged; + }; +} + +#endif diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h b/src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h new file mode 100644 index 000000000..6e8d84c9f --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h @@ -0,0 +1,217 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXV_NPHASE_IMPLEMENTATION_CONTEXT_H +#define PXV_NPHASE_IMPLEMENTATION_CONTEXT_H + +#include "PxSceneDesc.h" +#include "PxsContactManagerState.h" +#include "PsArray.h" + +namespace physx +{ + +namespace IG +{ + class SimpleIslandManager; + class IslandSim; + typedef PxU32 EdgeIndex; +} + +namespace Dy +{ + class Context; +} + +class PxBaseTask; +class PxsContext; +struct PxsShapeCore; +class PxsMaterialCore; +struct PxgDynamicsMemoryConfig; +class PxsContactManager; +struct PxsContactManagerOutput; +class PxsKernelWranglerManager; +class PxsHeapMemoryAllocatorManager; + + +struct PxsContactManagerBase +{ + static const PxU32 NEW_CONTACT_MANAGER_MASK = 0x80000000; + static const PxU32 GPU_NP_OFFSET = 0x4; + + static const PxU32 MaxBucketBits = 3; + + const PxU32 mBucketId; + + PxsContactManagerBase(const PxU32 bucketId) : mBucketId(bucketId) + { + PX_ASSERT(bucketId < (1<> MaxBucketBits; } + static PX_FORCE_INLINE PxU32 computeBucketIndexFromId(const PxU32 id) { return id & ((1<getPose(); + if (pair.mBa1) + pair.mBa1->getPose(); +#endif +} + +#if CCD_DEBUG_PRINTS +namespace physx { + +static const char* gGeomTypes[PxGeometryType::eGEOMETRY_COUNT+1] = { + "sphere", "plane", "capsule", "box", "convex", "trimesh", "heightfield", "*" +}; + +FILE* gCCDLog = NULL; + +static inline void openCCDLog() +{ + if (gCCDLog) + { + fclose(gCCDLog); + gCCDLog = NULL; + } + gCCDLog = fopen("c:\\ccd.txt", "wt"); + fprintf(gCCDLog, ">>>>>>>>>>>>>>>>>>>>>>>>>>> CCD START FRAME <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); +} + +static inline void printSeparator( + const char* prefix, PxU32 pass, PxsRigidBody* atom0, PxGeometryType::Enum g0, PxsRigidBody* atom1, PxGeometryType::Enum g1) +{ + fprintf(gCCDLog, "------- %s pass %d (%s %x) vs (%s %x)\n", prefix, pass, gGeomTypes[g0], atom0, gGeomTypes[g1], atom1); + fflush(gCCDLog); +} + +static inline void printCCDToi( + const char* header, PxF32 t, const PxVec3& v, + PxsRigidBody* atom0, PxGeometryType::Enum g0, PxsRigidBody* atom1, PxGeometryType::Enum g1 +) +{ + fprintf(gCCDLog, "%s (%s %x vs %s %x): %.5f, (%.2f, %.2f, %.2f)\n", header, gGeomTypes[g0], atom0, gGeomTypes[g1], atom1, t, v.x, v.y, v.z); + fflush(gCCDLog); +} + +static inline void printCCDPair(const char* header, PxsCCDPair& pair) +{ + printCCDToi(header, pair.mMinToi, pair.mMinToiNormal, pair.mBa0, pair.mG0, pair.mBa1, pair.mG1); +} + +// also used in PxcSweepConvexMesh.cpp +void printCCDDebug(const char* msg, const PxsRigidBody* atom0, PxGeometryType::Enum g0, bool printPtr) +{ + fprintf(gCCDLog, " %s (%s %x)\n", msg, gGeomTypes[g0], printPtr ? atom0 : 0); + fflush(gCCDLog); +} + +// also used in PxcSweepConvexMesh.cpp +void printShape( + PxsRigidBody* atom0, PxGeometryType::Enum g0, const char* annotation, PxReal dt, PxU32 pass, bool printPtr) +{ + fprintf(gCCDLog, "%s (%s %x) atom=(%.2f, %.2f, %.2f)>(%.2f, %.2f, %.2f) v=(%.1f, %.1f, %.1f), mv=(%.1f, %.1f, %.1f)\n", + annotation, gGeomTypes[g0], printPtr ? atom0 : 0, + atom0->getLastCCDTransform().p.x, atom0->getLastCCDTransform().p.y, atom0->getLastCCDTransform().p.z, + atom0->getPose().p.x, atom0->getPose().p.y, atom0->getPose().p.z, + atom0->getLinearVelocity().x, atom0->getLinearVelocity().y, atom0->getLinearVelocity().z, + atom0->getLinearMotionVelocity(dt).x, atom0->getLinearMotionVelocity(dt).y, atom0->getLinearMotionVelocity(dt).z ); + fflush(gCCDLog); + #if DEBUG_RENDER_CCD && DEBUG_RENDER_CCD_ATOM_PTR + if (!DEBUG_RENDER_CCD_FIRST_PASS_ONLY || pass == 0) + { + PxScene *s; PxGetPhysics()->getScenes(&s, 1, 0); + Cm::RenderOutput((Cm::RenderBuffer&)s->getRenderBuffer()) + << Cm::DebugText(atom0->getPose().p, 0.05f, "%x", atom0); + } + #endif +} + +static inline void flushCCDLog() +{ + fflush(gCCDLog); +} + +} // namespace physx + +#else + +namespace physx +{ + +void printShape(PxsRigidBody* /*atom0*/, PxGeometryType::Enum /*g0*/, const char* /*annotation*/, PxReal /*dt*/, PxU32 /*pass*/, bool printPtr = true) +{PX_UNUSED(printPtr);} + +static inline void openCCDLog() {} + +static inline void flushCCDLog() {} + +void printCCDDebug(const char* /*msg*/, const PxsRigidBody* /*atom0*/, PxGeometryType::Enum /*g0*/, bool printPtr = true) {PX_UNUSED(printPtr);} + +static inline void printSeparator( + const char* /*prefix*/, PxU32 /*pass*/, PxsRigidBody* /*atom0*/, PxGeometryType::Enum /*g0*/, + PxsRigidBody* /*atom1*/, PxGeometryType::Enum /*g1*/) {} +} // namespace physx +#endif + +namespace +{ + // PT: TODO: refactor with ShapeSim version (SIMD) + PX_INLINE PxTransform getShapeAbsPose(const PxsShapeCore* shapeCore, const PxsRigidCore* rigidCore, PxU32 isDynamic) + { + if(isDynamic) + { + const PxsBodyCore* PX_RESTRICT bodyCore = static_cast(rigidCore); + return bodyCore->body2World * bodyCore->getBody2Actor().getInverse() *shapeCore->transform; + } + else + { + return rigidCore->body2World * shapeCore->transform; + } + } +} + +namespace physx +{ + + PxsCCDContext::PxsCCDContext(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext) : + mPostCCDSweepTask (context->getContextId(), this, "PxsContext.postCCDSweep"), + mPostCCDAdvanceTask (context->getContextId(), this, "PxsContext.postCCDAdvance"), + mPostCCDDepenetrateTask (context->getContextId(), this, "PxsContext.postCCDDepenetrate"), + mDisableCCDResweep (false), + miCCDPass (0), + mSweepTotalHits (0), + mCCDThreadContext (NULL), + mCCDPairsPerBatch (0), + mCCDMaxPasses (1), + mContext (context), + mThresholdStream (thresholdStream), + mNphaseContext(nPhaseContext) +{ +} + +PxsCCDContext::~PxsCCDContext() +{ +} + +PxsCCDContext* PxsCCDContext::create(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext) +{ + PxsCCDContext* dc = reinterpret_cast( + PX_ALLOC(sizeof(PxsCCDContext), "PxsCCDContext")); + + if(dc) + { + new(dc) PxsCCDContext(context, thresholdStream, nPhaseContext); + } + return dc; +} + +void PxsCCDContext::destroy() +{ + this->~PxsCCDContext(); + PX_FREE(this); +} + +PxTransform PxsCCDShape::getAbsPose(const PxsRigidBody* atom) const +{ + // PT: TODO: refactor with ShapeSim version (SIMD) - or with redundant getShapeAbsPose() above in this same file! + if(atom) + return atom->getPose() * atom->getCore().getBody2Actor().getInverse() * mShapeCore->transform; + else + return mRigidCore->body2World * mShapeCore->transform; +} + +PxTransform PxsCCDShape::getLastCCDAbsPose(const PxsRigidBody* atom) const +{ + // PT: TODO: refactor with ShapeSim version (SIMD) + return atom->getLastCCDTransform() * atom->getCore().getBody2Actor().getInverse() * mShapeCore->transform; +} + +PxReal PxsCCDPair::sweepFindToi(PxcNpThreadContext& context, PxReal dt, PxU32 pass) +{ + printSeparator("findToi", pass, mBa0, mG0, NULL, PxGeometryType::eGEOMETRY_COUNT); + //Update shape transforms if necessary + updateShapes(); + + //Extract the bodies + PxsRigidBody* atom0 = mBa0; + PxsRigidBody* atom1 = mBa1; + PxsCCDShape* ccdShape0 = mCCDShape0; + PxsCCDShape* ccdShape1 = mCCDShape1; + PxGeometryType::Enum g0 = mG0, g1 = mG1; + + //If necessary, flip the bodies to make sure that g0 <= g1 + if(mG1 < mG0) + { + g0 = mG1; + g1 = mG0; + ccdShape0 = mCCDShape1; + ccdShape1 = mCCDShape0; + atom0 = mBa1; + atom1 = mBa0; + } + + PX_ALIGN(16, PxTransform tm0); + PX_ALIGN(16, PxTransform tm1); + PX_ALIGN(16, PxTransform lastTm0); + PX_ALIGN(16, PxTransform lastTm1); + + tm0 = ccdShape0->mCurrentTransform; + lastTm0 = ccdShape0->mPrevTransform; + + tm1 = ccdShape1->mCurrentTransform; + lastTm1 = ccdShape1->mPrevTransform; + + const PxVec3 trA = tm0.p - lastTm0.p; + const PxVec3 trB = tm1.p - lastTm1.p; + const PxVec3 relTr = trA - trB; + + // Do the sweep + PxVec3 sweepNormal(0.0f); + PxVec3 sweepPoint(0.0f); + + PxReal restDistance = PxMax(mCm->getWorkUnit().restDistance, 0.f); + + context.mDt = dt; + context.mCCDFaceIndex = PXC_CONTACT_NO_FACE_INDEX; + + //Cull the sweep hit based on the relative velocity along the normal + const PxReal fastMovingThresh0 = ccdShape0->mFastMovingThreshold; + const PxReal fastMovingThresh1 = ccdShape1->mFastMovingThreshold; + + const PxReal sumFastMovingThresh = (fastMovingThresh0 + fastMovingThresh1); + + + PxReal toi = Gu::SweepShapeShape(*ccdShape0, *ccdShape1, tm0, tm1, lastTm0, lastTm1, restDistance, + sweepNormal, sweepPoint, mMinToi, context.mCCDFaceIndex, sumFastMovingThresh); + + //If toi is after the end of TOI, return no hit + if (toi >= 1.0f) + { + mToiType = PxsCCDPair::ePrecise; + mPenetration = 0.f; + mPenetrationPostStep = 0.f; + mMinToi = PX_MAX_REAL; // needs to be reset in case of resweep + return toi; + } + + PX_ASSERT(PxIsFinite(toi)); + PX_ASSERT(sweepNormal.isFinite()); + mFaceIndex = context.mCCDFaceIndex; + + //Work out linear motion (used to cull the sweep hit) + const PxReal linearMotion = relTr.dot(-sweepNormal); + + //If we swapped the shapes, swap them back + if(mG1 >= mG0) + sweepNormal = -sweepNormal; + + + mToiType = PxsCCDPair::ePrecise; + + PxReal penetration = 0.f; + PxReal penetrationPostStep = 0.f; + + //If linear motion along normal < the CCD threshold, set toi to no-hit. + if((linearMotion) < sumFastMovingThresh) + { + mMinToi = PX_MAX_REAL; // needs to be reset in case of resweep + return PX_MAX_REAL; + } + else if(toi <= 0.f) + { + //If the toi <= 0.f, this implies an initial overlap. If the value < 0.f, it implies penetration + PxReal stepRatio0 = atom0 ? atom0->mCCD->mTimeLeft : 1.f; + PxReal stepRatio1 = atom1 ? atom1->mCCD->mTimeLeft : 1.f; + + PxReal stepRatio = PxMin(stepRatio0, stepRatio1); + penetration = -toi; + + toi = 0.f; + + if(stepRatio == 1.f) + { + //If stepRatio == 1.f (i.e. neither body has stepped forwards any time at all) + //we extract the advance coefficients from the bodies and permit the bodies to step forwards a small amount + //to ensure that they won't remain jammed because TOI = 0.0 + const PxReal advance0 = atom0 ? atom0->mCore->ccdAdvanceCoefficient : 1.f; + const PxReal advance1 = atom1 ? atom1->mCore->ccdAdvanceCoefficient : 1.f; + const PxReal advance = PxMin(advance0, advance1); + + PxReal fastMoving = PxMin(fastMovingThresh0, atom1 ? fastMovingThresh1 : PX_MAX_REAL); + PxReal advanceCoeff = advance * fastMoving; + penetrationPostStep = advanceCoeff/linearMotion; + + } + PX_ASSERT(PxIsFinite(toi)); + } + + //Store TOI, penetration and post-step (how much to step forward in initial overlap conditions) + mMinToi = toi; + mPenetration = penetration; + mPenetrationPostStep = penetrationPostStep; + + mMinToiPoint = sweepPoint; + + mMinToiNormal = sweepNormal; + + + //Work out the materials for the contact (restitution, friction etc.) + context.mContactBuffer.count = 0; + context.mContactBuffer.contact(mMinToiPoint, mMinToiNormal, 0.f, g1 == PxGeometryType::eTRIANGLEMESH || g1 == PxGeometryType::eHEIGHTFIELD? mFaceIndex : PXC_CONTACT_NO_FACE_INDEX); + + PxsMaterialInfo materialInfo; + + g_GetSingleMaterialMethodTable[g0](ccdShape0->mShapeCore, 0, context, &materialInfo); + g_GetSingleMaterialMethodTable[g1](ccdShape1->mShapeCore, 1, context, &materialInfo); + + const PxsMaterialData& data0 = *context.mMaterialManager->getMaterial(materialInfo.mMaterialIndex0); + const PxsMaterialData& data1 = *context.mMaterialManager->getMaterial(materialInfo.mMaterialIndex1); + + const PxReal restitution = PxsMaterialCombiner::combineRestitution(data0, data1); + PxsMaterialCombiner combiner(1.0f, 1.0f); + PxsMaterialCombiner::PxsCombinedMaterial combinedMat = combiner.combineIsotropicFriction(data0, data1); + const PxReal sFriction = combinedMat.staFriction; + const PxReal dFriction = combinedMat.dynFriction; + + mMaterialIndex0 = materialInfo.mMaterialIndex0; + mMaterialIndex1 = materialInfo.mMaterialIndex1; + mDynamicFriction = dFriction; + mStaticFriction = sFriction; + mRestitution = restitution; + + return toi; +} + +void PxsCCDPair::updateShapes() +{ + if(mBa0) + { + //If the CCD shape's update count doesn't match the body's update count, this shape needs its transforms and bounds re-calculated + if(mBa0->mCCD->mUpdateCount != mCCDShape0->mUpdateCount) + { + const PxTransform tm0 = mCCDShape0->getAbsPose(mBa0); + const PxTransform lastTm0 = mCCDShape0->getLastCCDAbsPose(mBa0); + + const PxVec3 trA = tm0.p - lastTm0.p; + + Gu::Vec3p origin, extents; + Gu::computeBoundsWithCCDThreshold(origin, extents, mCCDShape0->mShapeCore->geometry.getGeometry(), tm0, NULL); + + mCCDShape0->mCenter = origin - trA; + mCCDShape0->mExtents = extents; + mCCDShape0->mPrevTransform = lastTm0; + mCCDShape0->mCurrentTransform = tm0; + mCCDShape0->mUpdateCount = mBa0->mCCD->mUpdateCount; + } + } + + if(mBa1) + { + //If the CCD shape's update count doesn't match the body's update count, this shape needs its transforms and bounds re-calculated + if(mBa1->mCCD->mUpdateCount != mCCDShape1->mUpdateCount) + { + const PxTransform tm1 = mCCDShape1->getAbsPose(mBa1); + const PxTransform lastTm1 = mCCDShape1->getLastCCDAbsPose(mBa1); + + const PxVec3 trB = tm1.p - lastTm1.p; + + Vec3p origin, extents; + computeBoundsWithCCDThreshold(origin, extents, mCCDShape1->mShapeCore->geometry.getGeometry(), tm1, NULL); + + mCCDShape1->mCenter = origin - trB; + mCCDShape1->mExtents = extents; + mCCDShape1->mPrevTransform = lastTm1; + mCCDShape1->mCurrentTransform = tm1; + mCCDShape1->mUpdateCount = mBa1->mCCD->mUpdateCount; + } + } +} + +PxReal PxsCCDPair::sweepEstimateToi() +{ + //Update shape transforms if necessary + updateShapes(); + + //PxsRigidBody* atom1 = mBa1; + //PxsRigidBody* atom0 = mBa0; + PxsCCDShape* ccdShape0 = mCCDShape0; + PxsCCDShape* ccdShape1 = mCCDShape1; + PxGeometryType::Enum g0 = mG0, g1 = mG1; + PX_UNUSED(g0); + + + //Flip shapes if necessary + if(mG1 < mG0) + { + g0 = mG1; + g1 = mG0; + /*atom0 = mBa1; + atom1 = mBa0;*/ + ccdShape0 = mCCDShape1; + ccdShape1 = mCCDShape0; + } + + //Extract previous/current transforms, translations etc. + PxTransform tm0, lastTm0, tm1, lastTm1; + + PxVec3 trA(0.f); + PxVec3 trB(0.f); + tm0 = ccdShape0->mCurrentTransform; + lastTm0 = ccdShape0->mPrevTransform; + trA = tm0.p - lastTm0.p; + + tm1 = ccdShape1->mCurrentTransform; + lastTm1 = ccdShape1->mPrevTransform; + trB = tm1.p - lastTm1.p; + + PxReal restDistance = PxMax(mCm->getWorkUnit().restDistance, 0.f); + + const PxVec3 relTr = trA - trB; + + //Work out the sum of the fast moving thresholds scaled by the step ratio + const PxReal fastMovingThresh0 = ccdShape0->mFastMovingThreshold; + const PxReal fastMovingThresh1 = ccdShape1->mFastMovingThreshold; + const PxReal sumFastMovingThresh = (fastMovingThresh0 + fastMovingThresh1); + + mToiType = eEstimate; + + //If the objects are not moving fast-enough relative to each-other to warrant CCD, set estimated time as PX_MAX_REAL + if((relTr.magnitudeSquared()) <= (sumFastMovingThresh * sumFastMovingThresh)) + { + mToiType = eEstimate; + mMinToi = PX_MAX_REAL; + return PX_MAX_REAL; + } + + //Otherwise, the objects *are* moving fast-enough so perform estimation pass + if(g1 == PxGeometryType::eTRIANGLEMESH) + { + //Special-case estimation code for meshes + PxF32 toi = Gu::SweepEstimateAnyShapeMesh(*ccdShape0, *ccdShape1, tm0, tm1, lastTm0, lastTm1, restDistance, sumFastMovingThresh); + + mMinToi = toi; + return toi; + } + else if (g1 == PxGeometryType::eHEIGHTFIELD) + { + //Special-case estimation code for heightfields + PxF32 toi = Gu::SweepEstimateAnyShapeHeightfield(*ccdShape0, *ccdShape1, tm0, tm1, lastTm0, lastTm1, restDistance, sumFastMovingThresh); + + mMinToi = toi; + return toi; + } + + //Generic estimation code for prim-prim sweeps + PxVec3 centreA, extentsA; + PxVec3 centreB, extentsB; + centreA = ccdShape0->mCenter; + extentsA = ccdShape0->mExtents + PxVec3(restDistance); + + centreB = ccdShape1->mCenter; + extentsB = ccdShape1->mExtents; + + PxF32 toi = Gu::sweepAABBAABB(centreA, extentsA * 1.1f, centreB, extentsB * 1.1f, trA, trB); + mMinToi = toi; + return toi; +} + +bool PxsCCDPair::sweepAdvanceToToi(PxReal dt, bool clipTrajectoryToToi) +{ + PxsCCDShape* ccds0 = mCCDShape0; + PxsRigidBody* atom0 = mBa0; + PxsCCDShape* ccds1 = mCCDShape1; + PxsRigidBody* atom1 = mBa1; + + const PxsCCDPair* thisPair = this; + + //Both already had a pass so don't do anything + if ((atom0 == NULL || atom0->mCCD->mPassDone) && (atom1 == NULL || atom1->mCCD->mPassDone)) + return false; + + //Test to validate that they're both infinite mass objects. If so, there can be no response so terminate on a notification-only + if((atom0 == NULL || atom0->mCore->inverseMass == 0.f) && (atom1 == NULL || atom1->mCore->inverseMass == 0.f)) + return false; + + //If the TOI < 1.f. If not, this hit happens after this frame or at the very end of the frame. Either way, next frame can handle it + if (thisPair->mMinToi < 1.0f) + { + if(thisPair->mCm->getWorkUnit().flags & PxcNpWorkUnitFlag::eDISABLE_RESPONSE || thisPair->mMaxImpulse == 0.f) + { + //Don't mark pass as done on either body + return true; + } + + + PxReal minToi = thisPair->mMinToi; + + PxF32 penetration = -mPenetration * 10.f; + + PxVec3 minToiNormal = thisPair->mMinToiNormal; + + if (!minToiNormal.isNormalized()) + { + // somehow we got zero normal. This can happen for instance if two identical objects spawn exactly on top of one another + // abort ccd and clip to current toi + if (atom0 && !atom0->mCCD->mPassDone) + { + atom0->advancePrevPoseToToi(minToi); + atom0->advanceToToi(minToi, dt, true); + atom0->mCCD->mUpdateCount++; + } + return true; + } + + + + //Get the material indices... + const PxReal restitution = mRestitution; + const PxReal sFriction = mStaticFriction; + const PxReal dFriction = mDynamicFriction; + + + PxVec3 v0(0.f), v1(0.f); + + PxReal invMass0(0.f), invMass1(0.f); +#if CCD_ANGULAR_IMPULSE + PxMat33 invInertia0(PxVec3(0.f), PxVec3(0.f), PxVec3(0.f)), invInertia1(PxVec3(0.f), PxVec3(0.f), PxVec3(0.f)); + PxVec3 localPoint0(0.f), localPoint1(0.f); +#endif + + PxReal dom0 = mCm->getDominance0(); + PxReal dom1 = mCm->getDominance1(); + + //Work out velocity and invMass for body 0 + if(atom0) + { + //Put contact point in local space, then find how much point is moving using point velocity... + +#if CCD_ANGULAR_IMPULSE + localPoint0 = mMinToiPoint - trA.p; + v0 = atom0->mCore->linearVelocity + atom0->mCore->angularVelocity.cross(localPoint0); + + physx::Cm::transformInertiaTensor(atom0->mCore->inverseInertia, PxMat33(trA.q),invInertia0); + invInertia0 *= dom0; +#else + v0 = atom0->mCore->linearVelocity + atom0->mCore->angularVelocity.cross(ccds0->mCurrentTransform.p - atom0->mCore->body2World.p); +#endif + invMass0 = atom0->getInvMass() * dom0; + + } + + //Work out velocity and invMass for body 1 + if(atom1) + { + + //Put contact point in local space, then find how much point is moving using point velocity... +#if CCD_ANGULAR_IMPULSE + + localPoint1 = mMinToiPoint - trB.p; + v1 = atom1->mCore->linearVelocity + atom1->mCore->angularVelocity.cross(localPoint1); + physx::Cm::transformInertiaTensor(atom1->mCore->inverseInertia, PxMat33(trB.q),invInertia1); + invInertia1 *= dom1; +#else + v1 = atom1->mCore->linearVelocity + atom1->mCore->angularVelocity.cross(ccds1->mCurrentTransform.p - atom1->mCore->body2World.p); +#endif + invMass1 = atom1->getInvMass() * dom1; + } + + PX_ASSERT(v0.isFinite() && v1.isFinite()); + + //Work out relative velocity + PxVec3 vRel = v1 - v0; + + //Project relative velocity onto contact normal and bias with penetration + PxReal relNorVel = vRel.dot(minToiNormal); + PxReal relNorVelPlusPen = relNorVel + penetration; + +#if CCD_ANGULAR_IMPULSE + if(relNorVelPlusPen >= -1e-6f) + { + //we fall back on linear only parts... + localPoint0 = PxVec3(0.f); + localPoint1 = PxVec3(0.f); + v0 = atom0 ? atom0->getLinearVelocity() : PxVec3(0.f); + v1 = atom1 ? atom1->getLinearVelocity() : PxVec3(0.f); + vRel = v1 - v0; + relNorVel = vRel.dot(minToiNormal); + relNorVelPlusPen = relNorVel + penetration; + } +#endif + + + //If the relative motion is moving towards each-other, respond + if(relNorVelPlusPen < -1e-6f) + { + PxReal sumRecipMass = invMass0 + invMass1; + + const PxReal jLin = relNorVelPlusPen; + const PxReal normalResponse = (1.f + restitution) * jLin; + +#if CCD_ANGULAR_IMPULSE + const PxVec3 angularMom0 = invInertia0 * (localPoint0.cross(mMinToiNormal)); + const PxVec3 angularMom1 = invInertia1 * (localPoint1.cross(mMinToiNormal)); + + const PxReal jAng = minToiNormal.dot(angularMom0.cross(localPoint0) + angularMom1.cross(localPoint1)); + const PxReal impulseDivisor = sumRecipMass + jAng; + +#else + const PxReal impulseDivisor = sumRecipMass; +#endif + const PxReal jImp = PxMax(-mMaxImpulse, normalResponse/impulseDivisor); + + PxVec3 j(0.f); + + //If the user requested CCD friction, calculate friction forces. + //Note, CCD is *linear* so friction is also linear. The net result is that CCD friction can stop bodies' lateral motion so its better to have it disabled + //unless there's a real need for it. + if(mHasFriction) + { + + PxVec3 vPerp = vRel - relNorVel * minToiNormal; + PxVec3 tDir = vPerp; + PxReal length = tDir.normalize(); + PxReal vPerpImp = length/impulseDivisor; + PxF32 fricResponse = 0.f; + PxF32 staticResponse = (jImp*sFriction); + PxF32 dynamicResponse = (jImp*dFriction); + + + if (PxAbs(staticResponse) >= vPerpImp) + fricResponse = vPerpImp; + else + { + fricResponse = -dynamicResponse /* times m0 */; + } + + + //const PxVec3 fricJ = -vPerp.getNormalized() * (fricResponse/impulseDivisor); + const PxVec3 fricJ = tDir * (fricResponse); + j = jImp * mMinToiNormal + fricJ; + } + else + { + j = jImp * mMinToiNormal; + } + + verifyCCDPair(*this); + //If we have a negative impulse value, then we need to apply it. If not, the bodies are separating (no impulse to apply). + if(jImp < 0.f) + { + mAppliedForce = -jImp; + + //Adjust velocities + if((atom0 != NULL && atom0->mCCD->mPassDone) || + (atom1 != NULL && atom1->mCCD->mPassDone)) + { + mPenetrationPostStep = 0.f; + } + else + { + if (atom0) + { + //atom0->mAcceleration.linear = atom0->getLinearVelocity(); // to provide pre-"solver" velocity in contact reports + + atom0->setLinearVelocity(atom0->getLinearVelocity() + j * invMass0); + atom0->constrainLinearVelocity(); + #if CCD_ANGULAR_IMPULSE + atom0->mAcceleration.angular = atom0->getAngularVelocity(); // to provide pre-"solver" velocity in contact reports + atom0->setAngularVelocity(atom0->getAngularVelocity() + invInertia0 * localPoint0.cross(j)); + atom0->constrainAngularVelocity(); + #endif + } + if (atom1) + { + //atom1->mAcceleration.linear = atom1->getLinearVelocity(); // to provide pre-"solver" velocity in contact reports + atom1->setLinearVelocity(atom1->getLinearVelocity() - j * invMass1); + atom1->constrainLinearVelocity(); + #if CCD_ANGULAR_IMPULSE + atom1->mAcceleration.angular = atom1->getAngularVelocity(); // to provide pre-"solver" velocity in contact reports + atom1->setAngularVelocity(atom1->getAngularVelocity() - invInertia1 * localPoint1.cross(j)); + atom1->constrainAngularVelocity(); + #endif + } + } + } + } + + //Update poses + if (atom0 && !atom0->mCCD->mPassDone) + { + atom0->advancePrevPoseToToi(minToi); + atom0->advanceToToi(minToi, dt, clipTrajectoryToToi && mPenetrationPostStep == 0.f); + atom0->mCCD->mUpdateCount++; + } + if (atom1 && !atom1->mCCD->mPassDone) + { + atom1->advancePrevPoseToToi(minToi); + atom1->advanceToToi(minToi, dt, clipTrajectoryToToi && mPenetrationPostStep == 0.f); + atom1->mCCD->mUpdateCount++; + } + + //If we had a penetration post-step (i.e. an initial overlap), step forwards slightly after collision response + if(mPenetrationPostStep > 0.f) + { + if (atom0 && !atom0->mCCD->mPassDone) + { + atom0->advancePrevPoseToToi(mPenetrationPostStep); + if(clipTrajectoryToToi) + atom0->advanceToToi(mPenetrationPostStep, dt, clipTrajectoryToToi); + } + if (atom1 && !atom1->mCCD->mPassDone) + { + atom1->advancePrevPoseToToi(mPenetrationPostStep); + if(clipTrajectoryToToi) + atom1->advanceToToi(mPenetrationPostStep, dt, clipTrajectoryToToi); + } + } + //Mark passes as done + if (atom0) + { + atom0->mCCD->mPassDone = true; + atom0->mCCD->mHasAnyPassDone = true; + } + if (atom1) + { + atom1->mCCD->mPassDone = true; + atom1->mCCD->mHasAnyPassDone = true; + } + + return true; + + //return false; + } + else + { + printCCDDebug("advToi: clean sweep", atom0, mG0); + } + + return false; +} + +struct IslandCompare +{ + bool operator()(PxsCCDPair& a, PxsCCDPair& b) const { return a.mIslandId < b.mIslandId; } +}; + +struct IslandPtrCompare +{ + bool operator()(PxsCCDPair*& a, PxsCCDPair*& b) const { return a->mIslandId < b->mIslandId; } +}; + +struct ToiCompare +{ + bool operator()(PxsCCDPair& a, PxsCCDPair& b) const + { + return (a.mMinToi < b.mMinToi) || + ((a.mMinToi == b.mMinToi) && (a.mBa1 != NULL && b.mBa1 == NULL)); + } +}; + +struct ToiPtrCompare +{ + bool operator()(PxsCCDPair*& a, PxsCCDPair*& b) const + { + return (a->mMinToi < b->mMinToi) || + ((a->mMinToi == b->mMinToi) && (a->mBa1 != NULL && b->mBa1 == NULL)); + } +}; + + +// -------------------------------------------------------------- +/** +\brief Class to perform a set of sweep estimate tasks +*/ +class PxsCCDSweepTask : public Cm::Task +{ + PxsCCDPair** mPairs; + PxU32 mNumPairs; +public: + PxsCCDSweepTask(PxU64 contextID, PxsCCDPair** pairs, PxU32 nPairs) + : Cm::Task(contextID), mPairs(pairs), mNumPairs(nPairs) + { + } + + virtual void runInternal() + { + for (PxU32 j = 0; j < mNumPairs; j++) + { + PxsCCDPair& pair = *mPairs[j]; + pair.sweepEstimateToi(); + pair.mEstimatePass = 0; + } + } + + virtual const char *getName() const + { + return "PxsContext.CCDSweep"; + } + +private: + PxsCCDSweepTask& operator=(const PxsCCDSweepTask&); +}; + +#define ENABLE_RESWEEP 1 + +// -------------------------------------------------------------- +/** +\brief Class to advance a set of islands +*/ +class PxsCCDAdvanceTask : public Cm::Task +{ + PxsCCDPair** mCCDPairs; + PxU32 mNumPairs; + PxsContext* mContext; + PxsCCDContext* mCCDContext; + PxReal mDt; + PxU32 mCCDPass; + const PxsCCDBodyArray& mCCDBodies; + + PxU32 mFirstThreadIsland; + PxU32 mIslandsPerThread; + PxU32 mTotalIslandCount; + PxU32 mFirstIslandPair; // pairs are sorted by island + PxsCCDBody** mIslandBodies; + PxU16* mNumIslandBodies; + PxI32* mSweepTotalHits; + bool mClipTrajectory; + bool mDisableResweep; + + PxsCCDAdvanceTask& operator=(const PxsCCDAdvanceTask&); +public: + PxsCCDAdvanceTask(PxsCCDPair** pairs, PxU32 nPairs, const PxsCCDBodyArray& ccdBodies, + PxsContext* context, PxsCCDContext* ccdContext, PxReal dt, PxU32 ccdPass, + PxU32 firstIslandPair, PxU32 firstThreadIsland, PxU32 islandsPerThread, PxU32 totalIslands, + PxsCCDBody** islandBodies, PxU16* numIslandBodies, bool clipTrajectory, bool disableResweep, + PxI32* sweepTotalHits) + : Cm::Task(context->getContextId()), mCCDPairs(pairs), mNumPairs(nPairs), mContext(context), mCCDContext(ccdContext), mDt(dt), + mCCDPass(ccdPass), mCCDBodies(ccdBodies), mFirstThreadIsland(firstThreadIsland), + mIslandsPerThread(islandsPerThread), mTotalIslandCount(totalIslands), mFirstIslandPair(firstIslandPair), + mIslandBodies(islandBodies), mNumIslandBodies(numIslandBodies), mSweepTotalHits(sweepTotalHits), + mClipTrajectory(clipTrajectory), mDisableResweep(disableResweep) + + { + PX_ASSERT(mFirstIslandPair < mNumPairs); + } + + virtual void runInternal() + { + + PxI32 sweepTotalHits = 0; + + PxcNpThreadContext* threadContext = mContext->getNpThreadContext(); + + // -------------------------------------------------------------------------------------- + // loop over island labels assigned to this thread + PxU32 islandStart = mFirstIslandPair; + PxU32 lastIsland = PxMin(mFirstThreadIsland + mIslandsPerThread, mTotalIslandCount); + for (PxU32 iIsland = mFirstThreadIsland; iIsland < lastIsland; iIsland++) + { + if (islandStart >= mNumPairs) + // this is possible when for instance there are two islands with 0 pairs in the second + // since islands are initially segmented using bodies, not pairs, it can happen + break; + + // -------------------------------------------------------------------------------------- + // sort all pairs within current island by toi + PxU32 islandEnd = islandStart+1; + PX_ASSERT(mCCDPairs[islandStart]->mIslandId == iIsland); + while (islandEnd < mNumPairs && mCCDPairs[islandEnd]->mIslandId == iIsland) // find first index past the current island id + islandEnd++; + + if (islandEnd > islandStart+1) + shdfnd::sort(mCCDPairs+islandStart, islandEnd-islandStart, ToiPtrCompare()); + + PX_ASSERT(islandEnd <= mNumPairs); + + // -------------------------------------------------------------------------------------- + // advance all affected pairs within each island to min toi + // for each pair (A,B) in toi order, find any later-toi pairs that collide against A or B + // and resweep against changed trajectories of either A or B (excluding statics and kinematics) + PxReal islandMinToi = PX_MAX_REAL; + PxU32 estimatePass = 1; + + PxReal dt = mDt; + + for (PxU32 iFront = islandStart; iFront < islandEnd; iFront++) + { + PxsCCDPair& pair = *mCCDPairs[iFront]; + + verifyCCDPair(pair); + + //If we have reached a pair with a TOI after 1.0, we can terminate this island + if(pair.mMinToi > 1.f) + break; + + bool needSweep0 = (pair.mBa0 && pair.mBa0->mCCD->mPassDone == false); + bool needSweep1 = (pair.mBa1 && pair.mBa1->mCCD->mPassDone == false); + + //If both bodies have been updated (or one has been updated and the other is static), we can skip to the next pair + if(!(needSweep0 || needSweep1)) + continue; + + { + //If the pair was an estimate, we must perform an accurate sweep now + if(pair.mToiType == PxsCCDPair::eEstimate) + { + pair.sweepFindToi(*threadContext, dt, mCCDPass); + + //Test to see if the pair is still the earliest pair. + if((iFront + 1) < islandEnd && mCCDPairs[iFront+1]->mMinToi < pair.mMinToi) + { + //If there is an earlier pair, we push this pair into its correct place in the list and return to the start + //of this update loop + PxsCCDPair* tmp = &pair; + PxU32 index = iFront; + while((index + 1) < islandEnd && mCCDPairs[index+1]->mMinToi < pair.mMinToi) + { + mCCDPairs[index] = mCCDPairs[index+1]; + ++index; + } + + mCCDPairs[index] = tmp; + + --iFront; + continue; + } + } + + if (pair.mMinToi > 1.f) + break; + + //We now have the earliest contact pair for this island and one/both of the bodies have not been updated. We now perform + //contact modification to find out if the user still wants to respond to the collision + if(pair.mMinToi <= islandMinToi && + pair.mIsModifiable && + mCCDContext->getCCDContactModifyCallback()) + { + + PX_ALIGN(16, PxU8 dataBuffer[sizeof(PxModifiableContact) + sizeof(PxContactPatch)]); + + PxContactPatch* patch = reinterpret_cast(dataBuffer); + PxModifiableContact* point = reinterpret_cast(patch + 1); + + patch->mMassModification.mInvInertiaScale0 = 1.f; + patch->mMassModification.mInvInertiaScale1 = 1.f; + patch->mMassModification.mInvMassScale0 = 1.f; + patch->mMassModification.mInvMassScale1 = 1.f; + + patch->normal = pair.mMinToiNormal; + + patch->dynamicFriction = pair.mDynamicFriction; + patch->staticFriction = pair.mStaticFriction; + patch->materialIndex0 = pair.mMaterialIndex0; + patch->materialIndex1 = pair.mMaterialIndex1; + + patch->startContactIndex = 0; + patch->nbContacts = 1; + + patch->materialFlags = 0; + patch->internalFlags = 0; //44 //Can be a U16 + + + point->contact = pair.mMinToiPoint; + point->normal = pair.mMinToiNormal; + + //KS - todo - reintroduce face indices!!!! + //point.internalFaceIndex0 = PXC_CONTACT_NO_FACE_INDEX; + //point.internalFaceIndex1 = pair.mFaceIndex; + point->materialIndex0 = pair.mMaterialIndex0; + point->materialIndex1 = pair.mMaterialIndex1; + point->dynamicFriction = pair.mDynamicFriction; + point->staticFriction = pair.mStaticFriction; + point->restitution = pair.mRestitution; + point->separation = 0.f; + point->maxImpulse = PX_MAX_REAL; + point->materialFlags = 0; + point->targetVelocity = PxVec3(0.f); + + mCCDContext->runCCDModifiableContact(point, 1, pair.mCCDShape0->mShapeCore, pair.mCCDShape1->mShapeCore, + pair.mCCDShape0->mRigidCore, pair.mCCDShape1->mRigidCore, pair.mBa0, pair.mBa1); + + if ((patch->internalFlags & PxContactPatch::eHAS_MAX_IMPULSE)) + pair.mMaxImpulse = point->maxImpulse; + + pair.mDynamicFriction = point->dynamicFriction; + pair.mStaticFriction = point->staticFriction; + pair.mRestitution = point->restitution; + pair.mMinToiPoint = point->contact; + pair.mMinToiNormal = point->normal; + + } + + } + + // pair.mIsEarliestToiHit is used for contact notification. + // only mark as such if this is the first impact for both atoms of this pair (the impacts are sorted) + // and there was an actual impact for this pair + bool atom0FirstSweep = (pair.mBa0 && pair.mBa0->mCCD->mPassDone == false) || pair.mBa0 == NULL; + bool atom1FirstSweep = (pair.mBa1 && pair.mBa1->mCCD->mPassDone == false) || pair.mBa1 == NULL; + if (pair.mMinToi <= 1.0f && atom0FirstSweep && atom1FirstSweep) + pair.mIsEarliestToiHit = true; + + // sweepAdvanceToToi sets mCCD->mPassDone flags on both atoms, doesn't advance atoms with flag already set + // can advance one atom if the other already has the flag set + bool advanced = pair.sweepAdvanceToToi( dt, mClipTrajectory); + if(pair.mMinToi < 0.f) + pair.mMinToi = 0.f; + verifyCCDPair(pair); + + if (advanced && pair.mMinToi <= 1.0f) + { + sweepTotalHits++; + PxU32 islandStartIndex = iIsland == 0 ? 0 : PxU32(mNumIslandBodies[iIsland - 1]); + PxU32 islandEndIndex = mNumIslandBodies[iIsland]; + + if(pair.mMinToi > 0.f) + { + for(PxU32 a = islandStartIndex; a < islandEndIndex; ++a) + { + if(!mIslandBodies[a]->mPassDone) + { + //If the body has not updated, we advance it to the current time-step that the island has reached. + PxsRigidBody* atom = mIslandBodies[a]->mBody; + atom->advancePrevPoseToToi(pair.mMinToi); + atom->mCCD->mTimeLeft = PxMax(atom->mCCD->mTimeLeft * (1.0f - pair.mMinToi), CCD_MIN_TIME_LEFT); + atom->mCCD->mUpdateCount++; + } + } + + //Adjust remaining dt for the island + dt -= dt * pair.mMinToi; + + const PxReal recipOneMinusToi = 1.f/(1.f - pair.mMinToi); + for(PxU32 k = iFront+1; k < islandEnd; ++k) + { + PxsCCDPair& pair1 = *mCCDPairs[k]; + pair1.mMinToi = (pair1.mMinToi - pair.mMinToi)*recipOneMinusToi; + } + + } + + //If we disabled response, we don't need to resweep at all + if(!mDisableResweep && !(pair.mCm->getWorkUnit().flags & PxcNpWorkUnitFlag::eDISABLE_RESPONSE) && pair.mMaxImpulse != 0.f) + { + void* a0 = pair.mBa0 == NULL ? NULL : reinterpret_cast(pair.mBa0); + void* a1 = pair.mBa1 == NULL ? NULL : reinterpret_cast(pair.mBa1); + + for(PxU32 k = iFront+1; k < islandEnd; ++k) + { + PxsCCDPair& pair1 = *mCCDPairs[k]; + + void* b0 = pair1.mBa0 == NULL ? reinterpret_cast(pair1.mCCDShape0) : reinterpret_cast(pair1.mBa0); + void* b1 = pair1.mBa1 == NULL ? reinterpret_cast(pair1.mCCDShape1) : reinterpret_cast(pair1.mBa1); + + bool containsStatic = pair1.mBa0 == NULL || pair1.mBa1 == NULL; + + PX_ASSERT(b0 != NULL && b1 != NULL); + + if ((!containsStatic) && + ((b0 == a0 && b1 != a1) || (b1 == a0 && b0 != a1) || + (b0 == a1 && b1 != a0) || (b1 == a1 && b0 != a0)) + ) + { + if(estimatePass != pair1.mEstimatePass) + { + pair1.mEstimatePass = estimatePass; + // resweep pair1 since either b0 or b1 trajectory has changed + PxReal oldToi = pair1.mMinToi; + verifyCCDPair(pair1); + PxReal toi1 = pair1.sweepEstimateToi(); + PX_ASSERT(pair1.mBa0); // this is because mMinToiNormal is the impact point here + if (toi1 < oldToi) + { + // if toi decreased, resort the array backwards + PxU32 kk = k; + PX_ASSERT(kk > 0); + + while (kk-1 > iFront && mCCDPairs[kk-1]->mMinToi > toi1) + { + PxsCCDPair* temp = mCCDPairs[kk-1]; + mCCDPairs[kk-1] = mCCDPairs[kk]; + mCCDPairs[kk] = temp; + kk--; + } + + } + else if (toi1 > oldToi) + { + // if toi increased, resort the array forwards + PxU32 kk = k; + PX_ASSERT(kk > 0); + PxU32 stepped = 0; + while (kk+1 < islandEnd && mCCDPairs[kk+1]->mMinToi < toi1) + { + stepped = 1; + PxsCCDPair* temp = mCCDPairs[kk+1]; + mCCDPairs[kk+1] = mCCDPairs[kk]; + mCCDPairs[kk] = temp; + kk++; + } + k -= stepped; + } + } + } + } + } + estimatePass++; + } // if pair.minToi <= 1.0f + } // for iFront + + islandStart = islandEnd; + } // for (PxU32 iIsland = mFirstThreadIsland; iIsland < lastIsland; iIsland++) + + Ps::atomicAdd(mSweepTotalHits, sweepTotalHits); + mContext->putNpThreadContext(threadContext); + } + + virtual const char *getName() const + { + return "PxsContext.CCDAdvance"; + } +}; + +// -------------------------------------------------------------- +// CCD main function +// Overall structure: +/* +for nPasses (passes are now handled in void Sc::Scene::updateCCDMultiPass) + + update CCD broadphase, generate a list of CMs + + foreach CM + create CCDPairs, CCDBodies from CM + add shapes, overlappingShapes to CCDBodies + + foreach CCDBody + assign island labels per body + uses overlappingShapes + + foreach CCDPair + assign island label to pair + + sort all pairs by islandId + + foreach CCDPair + sweep/find toi + compute normal: + + foreach island + sort within island by toi + foreach pair within island + advanceToToi + from curPairInIsland to lastPairInIsland + resweep if needed + +*/ +// -------------------------------------------------------------- +void PxsCCDContext::updateCCDBegin() +{ + openCCDLog(); + + miCCDPass = 0; + mSweepTotalHits = 0; +} + +// -------------------------------------------------------------- +void PxsCCDContext::updateCCDEnd() +{ + if (miCCDPass == mCCDMaxPasses - 1 || mSweepTotalHits == 0) + { + // -------------------------------------------------------------------------------------- + // At last CCD pass we need to reset mBody pointers back to NULL + // so that the next frame we know which ones need to be newly paired with PxsCCDBody objects + // also free the CCDBody memory blocks + + mMutex.lock(); + for (PxU32 j = 0, n = mCCDBodies.size(); j < n; j++) + { + if (mCCDBodies[j].mBody->mCCD && mCCDBodies[j].mBody->mCCD->mHasAnyPassDone) + { + //Record this body in the list of bodies that were updated + mUpdatedCCDBodies.pushBack(mCCDBodies[j].mBody); + } + mCCDBodies[j].mBody->mCCD = NULL; + mCCDBodies[j].mBody->getCore().isFastMoving = false; //Clear the "isFastMoving" bool + } + mMutex.unlock(); + + mCCDBodies.clear_NoDelete(); + + } + + mCCDShapes.clear_NoDelete(); + + mMap.clear(); + + miCCDPass++; +} + +// -------------------------------------------------------------- +void PxsCCDContext::verifyCCDBegin() +{ + #if 0 + // validate that all bodies have a NULL mCCD pointer + if (miCCDPass == 0) + { + Cm::BitMap::Iterator it(mActiveContactManager); + for (PxU32 index = it.getNext(); index != Cm::BitMap::Iterator::DONE; index = it.getNext()) + { + PxsContactManager* cm = mContactManagerPool.findByIndexFast(index); + PxsRigidBody* b0 = cm->mBodyShape0->getBodyAtom(), *b1 = cm->mBodyShape1->getBodyAtom(); + PX_ASSERT(b0 == NULL || b0->mCCD == NULL); + PX_ASSERT(b1 == NULL || b1->mCCD == NULL); + } + } + #endif +} + +void PxsCCDContext::resetContactManagers() +{ + Cm::BitMap::Iterator it(mContext->mContactManagersWithCCDTouch); + + for (PxU32 index = it.getNext(); index != Cm::BitMap::Iterator::DONE; index = it.getNext()) + { + PxsContactManager* cm = mContext->mContactManagerPool.findByIndexFast(index); + cm->clearCCDContactInfo(); + } + + mContext->mContactManagersWithCCDTouch.clear(); +} + +// -------------------------------------------------------------- +void PxsCCDContext::updateCCD(PxReal dt, PxBaseTask* continuation, IG::IslandSim& islandSim, bool disableResweep, PxI32 numFastMovingShapes) +{ + //Flag to run a slightly less-accurate version of CCD that will ensure that objects don't tunnel through the static world but is not as reliable for dynamic-dynamic collisions + mDisableCCDResweep = disableResweep; + mThresholdStream.clear(); // clear force threshold report stream + + mContext->clearManagerTouchEvents(); + + if (miCCDPass == 0) + { + resetContactManagers(); + } + + + // If we're not in the first pass and the previous pass had no sweeps or the BP didn't generate any fast-moving shapes, we can skip CCD entirely + if ((miCCDPass > 0 && mSweepTotalHits == 0) || (numFastMovingShapes == 0)) + { + mSweepTotalHits = 0; + updateCCDEnd(); + return; + } + mSweepTotalHits = 0; + + PX_ASSERT(continuation); + PX_ASSERT(continuation->getReference() > 0); + + //printf("CCD 1\n"); + + mCCDThreadContext = mContext->getNpThreadContext(); + mCCDThreadContext->mDt = dt; // doesn't get set anywhere else since it's only used for CCD now + + verifyCCDBegin(); + + // -------------------------------------------------------------------------------------- + // From a list of active CMs, build a temporary array of PxsCCDPair objects (allocated in blocks) + // this is done to gather scattered data from memory and also to reduce PxsRidigBody permanent memory footprint + // we have to do it every pass since new CMs can become fast moving after each pass (and sometimes cease to be) + mCCDPairs.clear_NoDelete(); + mCCDPtrPairs.forceSize_Unsafe(0); + + mUpdatedCCDBodies.forceSize_Unsafe(0); + + mCCDOverlaps.clear_NoDelete(); + + PxU32 nbKinematicStaticCollisions = 0; + + + bool needsSweep = false; + + { + PX_PROFILE_ZONE("Sim.ccdPair", mContext->mContextID); + + Cm::BitMap::Iterator it(mContext->mActiveContactManagersWithCCD); + for (PxU32 index = it.getNext(); index != Cm::BitMap::Iterator::DONE; index = it.getNext()) + { + PxsContactManager* cm = mContext->mContactManagerPool.findByIndexFast(index); + + // skip disabled pairs + if(!cm->getCCD()) + continue; + + bool isJoint0 = (cm->mNpUnit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) == PxcNpWorkUnitFlag::eARTICULATION_BODY0; + bool isJoint1 = (cm->mNpUnit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) == PxcNpWorkUnitFlag::eARTICULATION_BODY1; + // skip articulation vs articulation ccd + //Actually. This is fundamentally wrong also :(. We only want to skip links in the same articulation - not all articulations!!! + if (isJoint0 && isJoint1) + continue; + + bool isFastMoving0 = static_cast(cm->mNpUnit.rigidCore0)->isFastMoving != 0; + + bool isFastMoving1 = (cm->mNpUnit.flags & (PxcNpWorkUnitFlag::eARTICULATION_BODY1 | PxcNpWorkUnitFlag::eDYNAMIC_BODY1)) ? static_cast(cm->mNpUnit.rigidCore1)->isFastMoving != 0: false; + + if (!(isFastMoving0 || isFastMoving1)) + continue; + + PxcNpWorkUnit& unit = cm->getWorkUnit(); + const PxsRigidCore* rc0 = unit.rigidCore0; + const PxsRigidCore* rc1 = unit.rigidCore1; + + { + const PxsShapeCore* sc0 = unit.shapeCore0; + const PxsShapeCore* sc1 = unit.shapeCore1; + + PxsRigidBody* ba0 = cm->mRigidBody0; + PxsRigidBody* ba1 = cm->mRigidBody1; + + //Look up the body/shape pair in our CCDShape map + const Ps::Pair* ccdShapePair0 = mMap.find(PxsRigidShapePair(rc0, sc0)); + const Ps::Pair* ccdShapePair1 = mMap.find(PxsRigidShapePair(rc1, sc1)); + + //If the CCD shapes exist, extract them from the map + PxsCCDShape* ccdShape0 = ccdShapePair0 ? ccdShapePair0->second : NULL; + PxsCCDShape* ccdShape1 = ccdShapePair1 ? ccdShapePair1->second : NULL; + + PxReal threshold0 = 0.f; + PxReal threshold1 = 0.f; + + PxVec3 trA(0.f); + PxVec3 trB(0.f); + + if(ccdShape0 == NULL) + { + //If we hadn't already created ccdShape, create one + ccdShape0 = &mCCDShapes.pushBack(); + mMap.insert(PxsRigidShapePair(rc0, sc0), ccdShape0); + + ccdShape0->mRigidCore = rc0; + ccdShape0->mShapeCore = sc0; + ccdShape0->mGeometry = &sc0->geometry; + + const PxTransform tm0 = ccdShape0->getAbsPose(ba0); + const PxTransform oldTm0 = ba0 ? ccdShape0->getLastCCDAbsPose(ba0) : tm0; + + trA = tm0.p - oldTm0.p; + + Vec3p origin, extents; + + //Compute the shape's bounds and CCD threshold + threshold0 = computeBoundsWithCCDThreshold(origin, extents, sc0->geometry.getGeometry(), tm0, NULL); + + //Set up the CCD shape + ccdShape0->mCenter = origin - trA; + ccdShape0->mExtents = extents; + ccdShape0->mFastMovingThreshold = threshold0; + ccdShape0->mPrevTransform = oldTm0; + ccdShape0->mCurrentTransform = tm0; + ccdShape0->mUpdateCount = 0; + ccdShape0->mNodeIndex = islandSim.getNodeIndex1(cm->getWorkUnit().mEdgeIndex); + } + else + { + //We had already created the shape, so extract the threshold and translation components + threshold0 = ccdShape0->mFastMovingThreshold; + trA = ccdShape0->mCurrentTransform.p - ccdShape0->mPrevTransform.p; + } + + if(ccdShape1 == NULL) + { + //If the CCD shape was not already constructed, create it + ccdShape1 = &mCCDShapes.pushBack(); + ccdShape1->mRigidCore = rc1; + ccdShape1->mShapeCore = sc1; + ccdShape1->mGeometry = &sc1->geometry; + + mMap.insert(PxsRigidShapePair(rc1, sc1), ccdShape1); + + const PxTransform tm1 = ccdShape1->getAbsPose(ba1); + const PxTransform oldTm1 = ba1 ? ccdShape1->getLastCCDAbsPose(ba1) : tm1; + + trB = tm1.p - oldTm1.p; + + Vec3p origin, extents; + //Compute the shape's bounds and CCD threshold + threshold1 = computeBoundsWithCCDThreshold(origin, extents, sc1->geometry.getGeometry(), tm1, NULL); + + //Set up the CCD shape + ccdShape1->mCenter = origin - trB; + ccdShape1->mExtents = extents; + ccdShape1->mFastMovingThreshold = threshold1; + ccdShape1->mPrevTransform = oldTm1; + ccdShape1->mCurrentTransform = tm1; + ccdShape1->mUpdateCount = 0; + ccdShape1->mNodeIndex = islandSim.getNodeIndex2(cm->getWorkUnit().mEdgeIndex); + } + else + { + //CCD shape already constructed so just extract thresholds and trB components + threshold1 = ccdShape1->mFastMovingThreshold; + trB = ccdShape1->mCurrentTransform.p - ccdShape1->mPrevTransform.p; + } + + { + //Initialize the CCD bodies + PxsRigidBody* atoms[2] = {ba0, ba1}; + for (int k = 0; k < 2; k++) + { + PxsRigidBody* b = atoms[k]; + //If there isn't a body (i.e. it's a static), no need to create a CCD body + if (!b) + continue; + if (b->mCCD == NULL) + { + // this rigid body has no CCD body created for it yet. Create and initialize one. + PxsCCDBody& newB = mCCDBodies.pushBack(); + b->mCCD = &newB; + b->mCCD->mIndex = Ps::to16(mCCDBodies.size()-1); + b->mCCD->mBody = b; + b->mCCD->mTimeLeft = 1.0f; + b->mCCD->mOverlappingObjects = NULL; + b->mCCD->mUpdateCount = 0; + b->mCCD->mHasAnyPassDone = false; + b->mCCD->mNbInteractionsThisPass = 0; + } + b->mCCD->mPassDone = 0; + b->mCCD->mNbInteractionsThisPass++; + } + if(ba0 && ba1) + { + //If both bodies exist (i.e. this is dynamic-dynamic collision), we create an + //overlap between the 2 bodies used for island detection. + if(!(ba0->isKinematic() || ba1->isKinematic())) + { + if(!ba0->mCCD->overlaps(ba1->mCCD)) + { + PxsCCDOverlap* overlapA = &mCCDOverlaps.pushBack(); + PxsCCDOverlap* overlapB = &mCCDOverlaps.pushBack(); + + overlapA->mBody = ba1->mCCD; + overlapB->mBody = ba0->mCCD; + + ba0->mCCD->addOverlap(overlapA); + ba1->mCCD->addOverlap(overlapB); + } + } + } + } + + //We now create the CCD pair. These are used in the CCD sweep and update phases + if (ba0->isKinematic() && (ba1 == NULL || ba1->isKinematic())) + nbKinematicStaticCollisions++; + { + PxsCCDPair& p = mCCDPairs.pushBack(); + p.mBa0 = ba0; + p.mBa1 = ba1; + p.mCCDShape0 = ccdShape0; + p.mCCDShape1 = ccdShape1; + p.mHasFriction = rc0->hasCCDFriction() || rc1->hasCCDFriction(); + p.mMinToi = PX_MAX_REAL; + p.mG0 = cm->mNpUnit.shapeCore0->geometry.getType(); + p.mG1 = cm->mNpUnit.shapeCore1->geometry.getType(); + p.mCm = cm; + p.mIslandId = 0xFFFFffff; + p.mIsEarliestToiHit = false; + p.mFaceIndex = PXC_CONTACT_NO_FACE_INDEX; + p.mIsModifiable = cm->isChangeable() != 0; + p.mAppliedForce = 0.f; + p.mMaxImpulse = PxMin((ba0->mCore->mFlags & PxRigidBodyFlag::eENABLE_CCD_MAX_CONTACT_IMPULSE) ? ba0->mCore->maxContactImpulse : PX_MAX_F32, + (ba1 && ba1->mCore->mFlags & PxRigidBodyFlag::eENABLE_CCD_MAX_CONTACT_IMPULSE) ? ba1->mCore->maxContactImpulse : PX_MAX_F32); + +#if PX_ENABLE_SIM_STATS + mContext->mSimStats.mNbCCDPairs[PxMin(p.mG0, p.mG1)][PxMax(p.mG0, p.mG1)] ++; +#endif + + //Calculate the sum of the thresholds and work out if we need to perform a sweep. + + const PxReal thresh = threshold0 + threshold1; + //If no shape pairs in the entire scene are fast-moving, we can bypass the entire of the CCD. + needsSweep = needsSweep || (trA - trB).magnitudeSquared() >= (thresh * thresh); + } + + } + } + //There are no fast-moving pairs in this scene, so we can terminate right now without proceeding any further + if(!needsSweep) + { + updateCCDEnd(); + mContext->putNpThreadContext(mCCDThreadContext); + return; + } + } + + //Create the pair pointer buffer. This is a flattened array of pointers to pairs. It is used to sort the pairs + //into islands and is also used to prioritize the pairs into their TOIs + { + const PxU32 size = mCCDPairs.size(); + mCCDPtrPairs.reserve(size); + for(PxU32 a = 0; a < size; ++a) + { + mCCDPtrPairs.pushBack(&mCCDPairs[a]); + } + + mThresholdStream.reserve(Ps::nextPowerOfTwo(size)); + + for (PxU32 a = 0; a < mCCDBodies.size(); ++a) + { + mCCDBodies[a].mPreSolverVelocity.linear = mCCDBodies[a].mBody->getLinearVelocity(); + mCCDBodies[a].mPreSolverVelocity.angular = mCCDBodies[a].mBody->getAngularVelocity(); + } + } + + + PxU32 ccdBodyCount = mCCDBodies.size(); + + // -------------------------------------------------------------------------------------- + // assign island labels + const PxU16 noLabelYet = 0xFFFF; + + //Temporary array allocations. Ideally, we should use the scratch pad for there + Array islandLabels; + islandLabels.resize(ccdBodyCount); + Array stack; + stack.reserve(ccdBodyCount); + stack.forceSize_Unsafe(ccdBodyCount); + + + //Initialize all islands labels (for each body) to be unitialized + mIslandSizes.forceSize_Unsafe(0); + mIslandSizes.reserve(ccdBodyCount + 1); + mIslandSizes.forceSize_Unsafe(ccdBodyCount + 1); + for (PxU32 j = 0; j < ccdBodyCount; j++) + islandLabels[j] = noLabelYet; + + PxU32 islandCount = 0; + PxU32 stackSize = 0; + const PxsCCDBody* top = NULL; + + for (PxU32 j = 0; j < ccdBodyCount; j++) + { + //If the body has already been labelled or if it is kinematic, continue + //Also, if the body has no interactions this pass, continue. In single-pass CCD, only bodies with interactions would be part of the CCD. However, + //with multi-pass CCD, we keep all bodies that interacted in previous passes. If the body now has no interactions, we skip it to ensure that island grouping doesn't fail in + //later stages by assigning an island ID to a body with no interactions + if (islandLabels[j] != noLabelYet || mCCDBodies[j].mBody->isKinematic() || mCCDBodies[j].mNbInteractionsThisPass == 0) + continue; + + top = &mCCDBodies[j]; + //Otherwise push it back into the queue and proceed + islandLabels[j] = islandCount; + + stack[stackSize++] = top; + // assign new label to unlabeled atom + // assign the same label to all connected nodes using stack traversal + PxU16 islandSize = 0; + while (stackSize > 0) + { + --stackSize; + const PxsCCDBody* ccdb = top; + top = stack[PxMax(1u, stackSize)-1]; + + PxsCCDOverlap* overlaps = ccdb->mOverlappingObjects; + while(overlaps) + { + if (islandLabels[overlaps->mBody->mIndex] == noLabelYet) // non-static & unlabeled? + { + islandLabels[overlaps->mBody->mIndex] = islandCount; + stack[stackSize++] = overlaps->mBody; // push adjacent node to the top of the stack + top = overlaps->mBody; + islandSize++; + } + overlaps = overlaps->mNext; + } + } + //Record island size + mIslandSizes[islandCount] = PxU16(islandSize + 1); + islandCount++; + } + + PxU32 kinematicIslandId = islandCount; + + islandCount += nbKinematicStaticCollisions; + + for (PxU32 i = kinematicIslandId; i < islandCount; ++i) + mIslandSizes[i] = 1; + + + + // -------------------------------------------------------------------------------------- + // label pairs with island ids + // (need an extra loop since we don't maintain a mapping from atom to all of it's pairs) + mCCDIslandHistogram.clear(); // number of pairs per island + mCCDIslandHistogram.resize(islandCount); + + PxU32 totalActivePairs = 0; + for (PxU32 j = 0, n = mCCDPtrPairs.size(); j < n; j++) + { + const PxU32 staticLabel = 0xFFFFffff; + PxsCCDPair& p = *mCCDPtrPairs[j]; + PxU32 id0 = p.mBa0 && !p.mBa0->isKinematic()? islandLabels[p.mBa0->mCCD->getIndex()] : staticLabel; + PxU32 id1 = p.mBa1 && !p.mBa1->isKinematic()? islandLabels[p.mBa1->mCCD->getIndex()] : staticLabel; + + PxU32 islandId = PxMin(id0, id1); + if (islandId == staticLabel) + islandId = kinematicIslandId++; + + p.mIslandId = islandId; + mCCDIslandHistogram[p.mIslandId] ++; + PX_ASSERT(p.mIslandId != staticLabel); + totalActivePairs++; + } + + PxU16 count = 0; + for(PxU16 a = 0; a < islandCount+1; ++a) + { + PxU16 islandSize = mIslandSizes[a]; + mIslandSizes[a] = count; + count += islandSize; + } + + mIslandBodies.forceSize_Unsafe(0); + mIslandBodies.reserve(ccdBodyCount); + mIslandBodies.forceSize_Unsafe(ccdBodyCount); + for(PxU32 a = 0; a < mCCDBodies.size(); ++a) + { + const PxU32 island = islandLabels[mCCDBodies[a].mIndex]; + if (island != 0xFFFF) + { + PxU16 writeIndex = mIslandSizes[island]; + mIslandSizes[island] = PxU16(writeIndex + 1); + mIslandBodies[writeIndex] = &mCCDBodies[a]; + } + } + + // -------------------------------------------------------------------------------------- + // setup tasks + mPostCCDDepenetrateTask.setContinuation(continuation); + mPostCCDAdvanceTask.setContinuation(&mPostCCDDepenetrateTask); + mPostCCDSweepTask.setContinuation(&mPostCCDAdvanceTask); + + // -------------------------------------------------------------------------------------- + // sort all pairs by islands + shdfnd::sort(mCCDPtrPairs.begin(), mCCDPtrPairs.size(), IslandPtrCompare()); + + // -------------------------------------------------------------------------------------- + // sweep all CCD pairs + const PxU32 nPairs = mCCDPtrPairs.size(); + const PxU32 numThreads = PxMax(1u, mContext->mTaskManager->getCpuDispatcher()->getWorkerCount()); PX_ASSERT(numThreads > 0); + mCCDPairsPerBatch = PxMax((nPairs)/numThreads, 1); + + for (PxU32 batchBegin = 0; batchBegin < nPairs; batchBegin += mCCDPairsPerBatch) + { + void* ptr = mContext->mTaskPool.allocate(sizeof(PxsCCDSweepTask)); + PX_ASSERT_WITH_MESSAGE(ptr, "Failed to allocate PxsCCDSweepTask"); + const PxU32 batchEnd = PxMin(nPairs, batchBegin + mCCDPairsPerBatch); + PX_ASSERT(batchEnd >= batchBegin); + PxsCCDSweepTask* task = PX_PLACEMENT_NEW(ptr, PxsCCDSweepTask)(mContext->getContextId(), mCCDPtrPairs.begin() + batchBegin, batchEnd - batchBegin); + task->setContinuation(*mContext->mTaskManager, &mPostCCDSweepTask); + task->removeReference(); + } + + mPostCCDSweepTask.removeReference(); + mPostCCDAdvanceTask.removeReference(); + mPostCCDDepenetrateTask.removeReference(); +} + +void PxsCCDContext::postCCDSweep(PxBaseTask* continuation) +{ + // -------------------------------------------------------------------------------------- + // batch up the islands and send them over to worker threads + PxU32 firstIslandPair = 0; + PxU32 islandCount = mCCDIslandHistogram.size(); + for (PxU32 firstIslandInBatch = 0; firstIslandInBatch < islandCount;) + { + PxU32 pairSum = 0; + PxU32 lastIslandInBatch = firstIslandInBatch+1; + PxU32 j; + // add up the numbers in the histogram until we reach target pairsPerBatch + for (j = firstIslandInBatch; j < islandCount; j++) + { + pairSum += mCCDIslandHistogram[j]; + if (pairSum > mCCDPairsPerBatch) + { + lastIslandInBatch = j+1; + break; + } + } + if (j == islandCount) // j is islandCount if not enough pairs were left to fill up to pairsPerBatch + { + if (pairSum == 0) + break; // we are done and there are no islands in this batch + lastIslandInBatch = islandCount; + } + + void* ptr = mContext->mTaskPool.allocate(sizeof(PxsCCDAdvanceTask)); + PX_ASSERT_WITH_MESSAGE(ptr , "Failed to allocate PxsCCDSweepTask"); + bool clipTrajectory = (miCCDPass == mCCDMaxPasses-1); + PxsCCDAdvanceTask* task = PX_PLACEMENT_NEW(ptr, PxsCCDAdvanceTask) ( + mCCDPtrPairs.begin(), mCCDPtrPairs.size(), mCCDBodies, mContext, this, mCCDThreadContext->mDt, miCCDPass, + firstIslandPair, firstIslandInBatch, lastIslandInBatch-firstIslandInBatch, islandCount, + mIslandBodies.begin(), mIslandSizes.begin(), clipTrajectory, mDisableCCDResweep, + &mSweepTotalHits); + firstIslandInBatch = lastIslandInBatch; + firstIslandPair += pairSum; + task->setContinuation(*mContext->mTaskManager, continuation); + task->removeReference(); + } // for iIsland +} + +void PxsCCDContext::postCCDAdvance(PxBaseTask* /*continuation*/) +{ + // -------------------------------------------------------------------------------------- + // contact notifications: update touch status (multi-threading this section would probably slow it down but might be worth a try) + PxU32 countLost = 0, countFound = 0, countRetouch = 0; + + PxU32 islandCount = mCCDIslandHistogram.size(); + PxU32 index = 0; + + for (PxU32 island = 0; island < islandCount; ++island) + { + PxU32 islandEnd = mCCDIslandHistogram[island] + index; + for(PxU32 j = index; j < islandEnd; ++j) + { + PxsCCDPair& p = *mCCDPtrPairs[j]; + //The CCD pairs are ordered by TOI. If we reach a TOI > 1, we can terminate + if(p.mMinToi > 1.f) + break; + + //If this was the earliest touch for the pair of bodies, we can notify the user about it. If not, it's a future collision that we haven't stepped to yet + if(p.mIsEarliestToiHit) + { + //Flag that we had a CCD contact + p.mCm->setHadCCDContact(); + + //Test/set the changed touch map + PxU16 oldTouch = p.mCm->getTouchStatus(); + if (!oldTouch) + { + mContext->mContactManagerTouchEvent.growAndSet(p.mCm->getIndex()); + p.mCm->mNpUnit.statusFlags = PxU16((p.mCm->mNpUnit.statusFlags & (~PxcNpWorkUnitStatusFlag::eHAS_NO_TOUCH)) | PxcNpWorkUnitStatusFlag::eHAS_TOUCH); + //Also need to write it in the CmOutput structure!!!!! + + //The achieve this, we need to unregister the CM from the Nphase, then re-register it with the status set. This is the only way to force a push to the GPU + mNphaseContext.unregisterContactManager(p.mCm); + mNphaseContext.registerContactManager(p.mCm, 1, 0); + countFound++; + } + else + { + mContext->mContactManagerTouchEvent.growAndSet(p.mCm->getIndex()); + p.mCm->raiseCCDRetouch(); + countRetouch++; + } + + //Do we want to create reports? + const bool createReports = + p.mCm->mNpUnit.flags & PxcNpWorkUnitFlag::eOUTPUT_CONTACTS + || (p.mCm->mNpUnit.flags & PxcNpWorkUnitFlag::eFORCE_THRESHOLD + && ((p.mCm->mNpUnit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY0 && static_cast(p.mCm->mNpUnit.rigidCore0)->shouldCreateContactReports()) + || (p.mCm->mNpUnit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1 && static_cast(p.mCm->mNpUnit.rigidCore1)->shouldCreateContactReports()))); + + if(createReports) + { + mContext->mContactManagersWithCCDTouch.growAndSet(p.mCm->getIndex()); + + const PxU32 numContacts = 1; + PxsMaterialInfo matInfo; + Gu::ContactBuffer& buffer = mCCDThreadContext->mContactBuffer; + + Gu::ContactPoint& cp = buffer.contacts[0]; + cp.point = p.mMinToiPoint; + cp.normal = -p.mMinToiNormal; //KS - discrete contact gen produces contacts pointing in the opposite direction to CCD sweeps + cp.internalFaceIndex1 = p.mFaceIndex; + cp.separation = 0.0f; + cp.restitution = p.mRestitution; + cp.dynamicFriction = p.mDynamicFriction; + cp.staticFriction = p.mStaticFriction; + cp.targetVel = PxVec3(0.f); + cp.maxImpulse = PX_MAX_REAL; + + matInfo.mMaterialIndex0 = p.mMaterialIndex0; + matInfo.mMaterialIndex1 = p.mMaterialIndex1; + + //Write contact stream for the contact. This will allocate memory for the contacts and forces + PxReal* contactForces; + //PxU8* contactStream; + PxU8* contactPatches; + PxU8* contactPoints; + PxU16 contactStreamSize; + PxU8 contactCount; + PxU8 nbPatches; + PxsCCDContactHeader* ccdHeader = reinterpret_cast(p.mCm->mNpUnit.ccdContacts); + if (writeCompressedContact(buffer.contacts, numContacts, mCCDThreadContext, contactCount, contactPatches, + contactPoints, contactStreamSize, contactForces, numContacts*sizeof(PxReal), mCCDThreadContext->mMaterialManager, + ((p.mCm->mNpUnit.flags & PxcNpWorkUnitFlag::eMODIFIABLE_CONTACT) != 0), true, &matInfo, nbPatches, sizeof(PxsCCDContactHeader),NULL, NULL, + false, NULL, NULL, NULL, p.mFaceIndex != PXC_CONTACT_NO_FACE_INDEX)) + { + PxsCCDContactHeader* newCCDHeader = reinterpret_cast(contactPatches); + newCCDHeader->contactStreamSize = Ps::to16(contactStreamSize); + newCCDHeader->isFromPreviousPass = 0; + + p.mCm->mNpUnit.ccdContacts = contactPatches; // put the latest stream at the head of the linked list since it needs to get accessed every CCD pass + // to prepare the reports + + if (!ccdHeader) + newCCDHeader->nextStream = NULL; + else + { + newCCDHeader->nextStream = ccdHeader; + ccdHeader->isFromPreviousPass = 1; + } + + //And write the force and contact count + PX_ASSERT(contactForces != NULL); + contactForces[0] = p.mAppliedForce; + } + else if (!ccdHeader) + { + p.mCm->mNpUnit.ccdContacts = NULL; + // we do not set the status flag on failure because the pair might have written + // a contact stream sucessfully during discrete collision this frame. + } + else + ccdHeader->isFromPreviousPass = 1; + + //If the touch event already existed, the solver would have already configured the threshold stream + if((p.mCm->mNpUnit.flags & (PxcNpWorkUnitFlag::eARTICULATION_BODY0 | PxcNpWorkUnitFlag::eARTICULATION_BODY1)) == 0 && p.mAppliedForce) + { +#if 1 + ThresholdStreamElement elt; + elt.normalForce = p.mAppliedForce; + elt.accumulatedForce = 0.f; + elt.threshold = PxMin(p.mBa0 == NULL ? PX_MAX_REAL : p.mBa0->mCore->contactReportThreshold, p.mBa1 == NULL ? PX_MAX_REAL : + p.mBa1->mCore->contactReportThreshold); + elt.nodeIndexA = p.mCCDShape0->mNodeIndex; + elt.nodeIndexB =p.mCCDShape1->mNodeIndex; + Ps::order(elt.nodeIndexA,elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA.index() < elt.nodeIndexB.index()); + mThresholdStream.pushBack(elt); +#endif + } + } + } + } + index = islandEnd; + } + + mContext->mCMTouchEventCount[PXS_LOST_TOUCH_COUNT] += countLost; + mContext->mCMTouchEventCount[PXS_NEW_TOUCH_COUNT] += countFound; + mContext->mCMTouchEventCount[PXS_CCD_RETOUCH_COUNT] += countRetouch; +} + +void PxsCCDContext::postCCDDepenetrate(PxBaseTask* /*continuation*/) +{ + // -------------------------------------------------------------------------------------- + // reset mOverlappingShapes array for all bodies + // we do it each pass because this set can change due to movement as well as new objects + // becoming fast moving due to intra-frame impacts + + for (PxU32 j = 0; j < mCCDBodies.size(); j ++) + { + mCCDBodies[j].mOverlappingObjects = NULL; + mCCDBodies[j].mNbInteractionsThisPass = 0; + } + + mCCDOverlaps.clear_NoDelete(); + + updateCCDEnd(); + + mContext->putNpThreadContext(mCCDThreadContext); + + flushCCDLog(); +} + +Cm::SpatialVector PxsRigidBody::getPreSolverVelocities() const +{ + if (mCCD) + return mCCD->mPreSolverVelocity; + return Cm::SpatialVector(PxVec3(0.f), PxVec3(0.f)); +} + + +PxTransform PxsRigidBody::getAdvancedTransform(PxReal toi) const +{ + //If it is kinematic, just return identity. We don't fully support kinematics yet + if (isKinematic()) + return PxTransform(PxIdentity); + + //Otherwise we interpolate the pose between the current and previous pose and return that pose + PxVec3 newLastP = mLastTransform.p*(1.0f-toi) + mCore->body2World.p*toi; // advance mLastTransform position to toi + PxQuat newLastQ = slerp(toi, getLastCCDTransform().q, mCore->body2World.q); // advance mLastTransform rotation to toi + return PxTransform(newLastP, newLastQ); +} + +void PxsRigidBody::advancePrevPoseToToi(PxReal toi) +{ + //If this is kinematic, just return + if (isKinematic()) + return; + + //update latest pose + PxVec3 newLastP = mLastTransform.p*(1.0f-toi) + mCore->body2World.p*toi; // advance mLastTransform position to toi + mLastTransform.p = newLastP; +#if CCD_ROTATION_LOCKING + mCore->body2World.q = getLastCCDTransform().q; +#else + // slerp from last transform to current transform with ratio of toi + PxQuat newLastQ = slerp(toi, getLastCCDTransform().q, mCore->body2World.q); // advance mLastTransform rotation to toi + mLastTransform.q = newLastQ; +#endif + + + +} + + +void PxsRigidBody::advanceToToi(PxReal toi, PxReal dt, bool clip) +{ + if (isKinematic()) + return; + + + if (clip) + { + //If clip is true, we set the previous and current pose to be the same. This basically makes the object appear stationary in the CCD + mCore->body2World.p = getLastCCDTransform().p; +#if !CCD_ROTATION_LOCKING + mCore->body2World.q = getLastCCDTransform().q; +#endif + } + else + { + // advance new CCD target after impact to remaining toi using post-impact velocities + mCore->body2World.p = getLastCCDTransform().p + getLinearVelocity() * dt * (1.0f - toi); +#if !CCD_ROTATION_LOCKING + PxVec3 angularDelta = getAngularVelocity() * dt * (1.0f - toi); + PxReal deltaMag = angularDelta.magnitude(); + PxVec3 deltaAng = deltaMag > 1e-20f ? angularDelta / deltaMag : PxVec3(1.0f, 0.0f, 0.0f); + PxQuat angularQuat(deltaMag, deltaAng); + mCore->body2World.q = getLastCCDTransform().q * angularQuat; +#endif + PX_ASSERT(mCore->body2World.isSane()); + } + + // rescale total time left to elapse this frame + mCCD->mTimeLeft = PxMax(mCCD->mTimeLeft * (1.0f - toi), CCD_MIN_TIME_LEFT); +} + + +void PxsCCDContext::runCCDModifiableContact(PxModifiableContact* PX_RESTRICT contacts, PxU32 contactCount, const PxsShapeCore* PX_RESTRICT shapeCore0, + const PxsShapeCore* PX_RESTRICT shapeCore1, const PxsRigidCore* PX_RESTRICT rigidCore0, const PxsRigidCore* PX_RESTRICT rigidCore1, + const PxsRigidBody* PX_RESTRICT rigid0, const PxsRigidBody* PX_RESTRICT rigid1) +{ + if(!mCCDContactModifyCallback) + return; + + class PxcContactSet: public PxContactSet + { + public: + PxcContactSet(PxU32 count, PxModifiableContact* contacts_) + { + mContacts = contacts_; + mCount = count; + } + }; + { + PxContactModifyPair p; + + p.shape[0] = gPxvOffsetTable.convertPxsShape2Px(shapeCore0); + p.shape[1] = gPxvOffsetTable.convertPxsShape2Px(shapeCore1); + + p.actor[0] = rigid0 != NULL ? gPxvOffsetTable.convertPxsRigidCore2PxRigidBody(rigidCore0) + : gPxvOffsetTable.convertPxsRigidCore2PxRigidStatic(rigidCore0); + + p.actor[1] = rigid1 != NULL ? gPxvOffsetTable.convertPxsRigidCore2PxRigidBody(rigidCore1) + : gPxvOffsetTable.convertPxsRigidCore2PxRigidStatic(rigidCore1); + + p.transform[0] = getShapeAbsPose(shapeCore0, rigidCore0, PxU32(rigid0 != NULL)); + p.transform[1] = getShapeAbsPose(shapeCore1, rigidCore1, PxU32(rigid1 != NULL)); + + static_cast(p.contacts) = + PxcContactSet(contactCount, contacts); + + mCCDContactModifyCallback->onCCDContactModify(&p, 1); + } +} + + +} //namespace physx + + diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp new file mode 100644 index 000000000..a9c84dd27 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxsContactManager.h" +#include "PxsRigidBody.h" +#include "PxcContactMethodImpl.h" +#include "PxvManager.h" +#include "PxsIslandSim.h" + +using namespace physx; + +PxsContactManager::PxsContactManager(PxsContext*, PxU32 index) /*: + mUserData (NULL)*/ +{ + mFlags = 0; + + // PT: TODO: any reason why we don't initialize all members here, e.g. shapeCore pointers? + mNpUnit.index = index; + mNpUnit.rigidCore0 = NULL; + mNpUnit.rigidCore1 = NULL; + mNpUnit.restDistance = 0; + mNpUnit.dominance0 = 1u; + mNpUnit.dominance1 = 1u; + mNpUnit.frictionDataPtr = NULL; + mNpUnit.frictionPatchCount = 0; +} + +PxsContactManager::~PxsContactManager() +{ +} + + +void PxsContactManager::setCCD(bool enable) +{ + PxU32 flags = mFlags & (~PXS_CM_CCD_CONTACT); + if (enable) + flags |= PXS_CM_CCD_LINEAR; + else + flags &= ~PXS_CM_CCD_LINEAR; + + mFlags = flags; +} + + + +void PxsContactManager::resetCachedState() +{ + // happens when the body transform or shape relative transform changes. + + PxcNpWorkUnitClearCachedState(mNpUnit); +} + +void PxsContactManager::resetFrictionCachedState() +{ + // happens when the body transform or shape relative transform changes. + + PxcNpWorkUnitClearFrictionCachedState(mNpUnit); +} + + diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp new file mode 100644 index 000000000..56412e13c --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp @@ -0,0 +1,623 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "PxvConfig.h" +#include "PxcContactCache.h" +#include "PxsRigidBody.h" +#include "PxsContactManager.h" +#include "PxsContext.h" +#include "PxPhysXConfig.h" + +#include "CmBitMap.h" +#include "CmFlushPool.h" + +#include "PxsMaterialManager.h" +#include "PxSceneDesc.h" +#include "PxsCCD.h" +#include "PxvGeometry.h" +#include "PxvManager.h" +#include "PxsSimpleIslandManager.h" + +#if PX_SUPPORT_GPU_PHYSX +#include "task/PxGpuDispatcher.h" +#include "PxPhysXGpu.h" +#endif + +#include "PxcNpContactPrepShared.h" +#include "PxcNpCache.h" + + +using namespace physx; +using namespace physx::shdfnd; + +#define PXS_CONTACTMANAGER_SLABSIZE 1024 +#define PXS_MAX_CONTACTMANAGER_SLABS 64 + +#define PXS_BODYSHAPE_SLABSIZE 1024 +#define PXS_MAX_BODYSHAPE_SLABS 16 + +PxsContext::PxsContext(const PxSceneDesc& desc, PxTaskManager* taskManager, Cm::FlushPool& taskPool, PxU64 contextID) : + mNpThreadContextPool (this), + mContactManagerPool ("mContactManagerPool", this, 256, 8192), + mManifoldPool ("mManifoldPool", 256), + mSphereManifoldPool ("mSphereManifoldPool", 256), + mContactModifyCallback (NULL), + mNpImplementationContext (NULL), + mNpFallbackImplementationContext(NULL), + mTaskManager (taskManager), + mTaskPool (taskPool), + mPCM (desc.flags & PxSceneFlag::eENABLE_PCM), + mContactCache (false), + mCreateAveragePoint (desc.flags & PxSceneFlag::eENABLE_AVERAGE_POINT), + mContextID (contextID) +{ + clearManagerTouchEvents(); + mVisualizationCullingBox.setMaximal(); + + PxMemZero(mVisualizationParams, sizeof(PxReal) * PxVisualizationParameter::eNUM_VALUES); + + mNpMemBlockPool.init(desc.nbContactDataBlocks, desc.maxNbContactDataBlocks); +} + +PxsContext::~PxsContext() +{ + if(mTransformCache) + { + mTransformCache->~PxsTransformCache(); + PX_FREE(mTransformCache); + } + mTransformCache = NULL; + + mContactManagerPool.destroy(); //manually destroy the contact manager pool, otherwise pool deletion order is random and we can get into trouble with references into other pools needed during destruction. +} + +// =========================== Create methods +namespace physx +{ + bool gEnablePCMCaching[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT] = + { + //eSPHERE, + { + false, //eSPHERE + false, //ePLANE + false, //eCAPSULE + false, //eBOX + true, //eCONVEXMESH + true, //eTRIANGLEMESH + true //eHEIGHTFIELD + }, + + //ePLANE + { + false, //eSPHERE + false, //ePLANE + true, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + false, //eTRIANGLEMESH + false //eHEIGHTFIELD + }, + + //eCAPSULE, + { + false, //eSPHERE + true, //ePLANE + false, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + true, //eTRIANGLEMESH + true //eHEIGHTFIELD + }, + + //eBOX, + { + false, //eSPHERE + true, //ePLANE + true, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + true, //eTRIANGLEMESH + true //eHEIGHTFIELD + }, + + //eCONVEXMESH, + { + true, //eSPHERE + true, //ePLANE + true, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + true, //eTRIANGLEMESH + true //eHEIGHTFIELD + }, + //eTRIANGLEMESH, + { + true, //eSPHERE + false, //ePLANE + true, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + false, //eTRIANGLEMESH + false //eHEIGHTFIELD + }, + + //eHEIGHTFIELD, + { + true, //eSPHERE + false, //ePLANE + true, //eCAPSULE + true, //eBOX + true, //eCONVEXMESH + false, //eTRIANGLEMESH + false //eHEIGHTFIELD + } + }; +} + +void PxsContext::createTransformCache(Ps::VirtualAllocatorCallback& allocatorCallback) +{ + mTransformCache = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxsTransformCache), PX_DEBUG_EXP("PxsTransformCache")), PxsTransformCache(allocatorCallback)); +} + +PxsContactManager* PxsContext::createContactManager(PxsContactManager* contactManager, const bool useCCD) +{ + PxsContactManager* cm = contactManager? contactManager : mContactManagerPool.get(); + + if(cm) + { + PxcNpWorkUnitClearContactState(cm->getWorkUnit()); + PxcNpWorkUnitClearCachedState(cm->getWorkUnit()); + + if (contactManager == NULL) + { + if (cm->getIndex() >= mActiveContactManager.size()) + { + PxU32 newSize = (2 * cm->getIndex() + 256)&~255; + mActiveContactManager.resize(newSize); + } + mActiveContactManager.set(cm->getIndex()); + + if (useCCD) + { + if (cm->getIndex() >= mActiveContactManagersWithCCD.size()) + { + PxU32 newSize = (2 * cm->getIndex() + 256)&~255; + mActiveContactManagersWithCCD.resize(newSize); + } + mActiveContactManagersWithCCD.set(cm->getIndex()); + } + } + } + else + { + PX_WARN_ONCE("Reached limit of contact pairs."); + } + + return cm; +} + +void PxsContext::createCache(Gu::Cache& cache, PxsContactManager* cm, PxU8 geomType0, PxU8 geomType1) +{ + if(cm) + { + if(mPCM) + { + if(gEnablePCMCaching[geomType0][geomType1]) + { + if(geomType0 <= PxGeometryType::eCONVEXMESH && + geomType1 <= PxGeometryType::eCONVEXMESH) + { + if(geomType0 == PxGeometryType::eSPHERE || geomType1 == PxGeometryType::eSPHERE) + { + Gu::PersistentContactManifold* manifold = mSphereManifoldPool.allocate(); + new(manifold) Gu::SpherePersistentContactManifold(); + cache.setManifold(manifold); + } + else + { + Gu::PersistentContactManifold* manifold = mManifoldPool.allocate(); + new(manifold) Gu::LargePersistentContactManifold(); + cache.setManifold(manifold); + + } + cache.getManifold().clearManifold(); + + } + else + { + //ML: raised 1 to indicate the manifold is multiManifold which is for contact gen in mesh/height field + //cache.manifold = 1; + cache.setMultiManifold(NULL); + } + } + else + { + //cache.manifold = 0; + cache.mCachedData = NULL; + cache.mManifoldFlags = 0; + } + } + } +} + +void PxsContext::destroyContactManager(PxsContactManager* cm) +{ + const PxU32 idx = cm->getIndex(); + if (cm->getCCD()) + mActiveContactManagersWithCCD.growAndReset(idx); + mActiveContactManager.growAndReset(idx); + mContactManagerTouchEvent.growAndReset(idx); + mContactManagerPatchChangeEvent.growAndReset(idx); + mContactManagerPool.put(cm); +} + +void PxsContext::destroyCache(Gu::Cache& cache) +{ + if(cache.isManifold()) + { + if(!cache.isMultiManifold()) + { + Gu::PersistentContactManifold& manifold = cache.getManifold(); + if (manifold.mCapacity == GU_SPHERE_MANIFOLD_CACHE_SIZE) + { + mSphereManifoldPool.deallocate(static_cast(&manifold)); + } + else + { + mManifoldPool.deallocate(static_cast(&manifold)); + } + } + cache.mCachedData = NULL; + cache.mManifoldFlags = 0; + } +} + +void PxsContext::setScratchBlock(void* addr, PxU32 size) +{ + mScratchAllocator.setBlock(addr, size); +} + +void PxsContext::setContactDistance(Ps::Array* contactDistance) +{ + mContactDistance = contactDistance; +} + + +void PxsContext::shiftOrigin(const PxVec3& shift) +{ + // transform cache + mTransformCache->shiftTransforms(-shift); + +#if 0 + if (getContactCacheFlag()) + { + //Iterate all active contact managers + Cm::BitMap::Iterator it(mActiveContactManager); + PxU32 index = it.getNext(); + while(index != Cm::BitMap::Iterator::DONE) + { + PxsContactManager* cm = mContactManagerPool.findByIndexFast(index); + + PxcNpWorkUnit& npwUnit = cm->getWorkUnit(); + + // contact cache + if(!npwUnit.pairCache.isManifold()) + { + PxU8* contactCachePtr = npwUnit.pairCache.mCachedData; + if (contactCachePtr) + { + PxcLocalContactsCache* lcc; + PxU8* contacts = PxcNpCacheRead(npwUnit.pairCache, lcc); +#ifdef _DEBUG + PxcLocalContactsCache testCache; + PxU32 testBytes; + const PxU8* testPtr = PxcNpCacheRead2(npwUnit.pairCache, testCache, testBytes); +#endif + lcc->mTransform0.p -= shift; + lcc->mTransform1.p -= shift; + + const PxU32 nbContacts = lcc->mNbCachedContacts; + const bool sameNormal = lcc->mSameNormal; + const bool useFaceIndices = lcc->mUseFaceIndices; + + for(PxU32 i=0; i < nbContacts; i++) + { + if (i != nbContacts-1) + Ps::prefetchLine(contacts, 128); + + if(!i || !sameNormal) + contacts += sizeof(PxVec3); + + PxVec3* cachedPoint = reinterpret_cast(contacts); + *cachedPoint -= shift; + contacts += sizeof(PxVec3); + contacts += sizeof(PxReal); + + if(useFaceIndices) + contacts += 2 * sizeof(PxU32); + } +#ifdef _DEBUG + PX_ASSERT(contacts == (testPtr + testBytes)); +#endif + } + } + + index = it.getNext(); + } + + } +#endif + + // + // adjust visualization culling box + // + PxBounds3 maximalBounds; + maximalBounds.setMaximal(); + if ((mVisualizationCullingBox.minimum != maximalBounds.minimum) || (mVisualizationCullingBox.maximum != maximalBounds.maximum)) + { + mVisualizationCullingBox.minimum -= shift; + mVisualizationCullingBox.maximum -= shift; + } +} + +void PxsContext::swapStreams() +{ + mNpMemBlockPool.swapNpCacheStreams(); +} + +void PxsContext::mergeCMDiscreteUpdateResults(PxBaseTask* /*continuation*/) +{ + PX_PROFILE_ZONE("Sim.narrowPhaseMerge", mContextID); + + this->mNpImplementationContext->appendContactManagers(); + + //Note: the iterator extracts all the items and returns them to the cache on destruction(for thread safety). + PxcThreadCoherentCacheIterator threadContextIt(mNpThreadContextPool); + + for(PxcNpThreadContext* threadContext = threadContextIt.getNext(); threadContext; threadContext = threadContextIt.getNext()) + { + mCMTouchEventCount[PXS_LOST_TOUCH_COUNT] += threadContext->getLocalLostTouchCount(); + mCMTouchEventCount[PXS_NEW_TOUCH_COUNT] += threadContext->getLocalNewTouchCount(); + mCMTouchEventCount[PXS_PATCH_FOUND_COUNT] += threadContext->getLocalFoundPatchCount(); + mCMTouchEventCount[PXS_PATCH_LOST_COUNT] += threadContext->getLocalLostPatchCount(); + +#if PX_ENABLE_SIM_STATS + for(PxU32 i=0;imDiscreteContactPairs[i][j]); + #endif + for(PxU32 j=i; jmDiscreteContactPairs[i][j]; + const PxU32 nbModified = threadContext->mModifiedContactPairs[i][j]; + mSimStats.mNbDiscreteContactPairs[i][j] += nb; + mSimStats.mNbModifiedContactPairs[i][j] += nbModified; + mSimStats.mNbDiscreteContactPairsTotal += nb; + } + } + + mSimStats.mNbDiscreteContactPairsWithCacheHits += threadContext->mNbDiscreteContactPairsWithCacheHits; + mSimStats.mNbDiscreteContactPairsWithContacts += threadContext->mNbDiscreteContactPairsWithContacts; + + mSimStats.mTotalCompressedContactSize += threadContext->mCompressedCacheSize; + //KS - this data is not available yet + //mSimStats.mTotalConstraintSize += threadContext->mConstraintSize; + threadContext->clearStats(); +#endif + mContactManagerTouchEvent.combineInPlace(threadContext->getLocalChangeTouch()); + mContactManagerPatchChangeEvent.combineInPlace(threadContext->getLocalPatchChangeMap()); + mTotalCompressedCacheSize += threadContext->mTotalCompressedCacheSize; + mMaxPatches = PxMax(mMaxPatches, threadContext->mMaxPatches); + + threadContext->mTotalCompressedCacheSize = threadContext->mMaxPatches = 0; + } +} + +void PxsContext::setCreateContactStream(bool to) +{ + mCreateContactStream = to; + PxcThreadCoherentCacheIterator threadContextIt(mNpThreadContextPool); + for(PxcNpThreadContext* threadContext = threadContextIt.getNext(); threadContext; threadContext = threadContextIt.getNext()) + { + threadContext->setCreateContactStream(to); + } +} + +void PxsContext::updateContactManager(PxReal dt, bool hasBoundsArrayChanged, bool hasContactDistanceChanged, PxBaseTask* continuation, PxBaseTask* firstPassContinuation) +{ + PX_ASSERT(mNpImplementationContext); + mNpImplementationContext->updateContactManager(dt, hasBoundsArrayChanged, hasContactDistanceChanged, continuation, firstPassContinuation); +} + +void PxsContext::secondPassUpdateContactManager(PxReal dt, PxBaseTask* continuation) +{ + PX_ASSERT(mNpImplementationContext); + mNpImplementationContext->secondPassUpdateContactManager(dt, continuation); +} + +void PxsContext::fetchUpdateContactManager() +{ + PX_ASSERT(mNpImplementationContext); + mNpImplementationContext->fetchUpdateContactManager(); + mergeCMDiscreteUpdateResults(NULL); +} + +void PxsContext::resetThreadContexts() +{ + //Note: the iterator extracts all the items and returns them to the cache on destruction(for thread safety). + PxcThreadCoherentCacheIterator threadContextIt(mNpThreadContextPool); + PxcNpThreadContext* threadContext = threadContextIt.getNext(); + + while(threadContext != NULL) + { + threadContext->reset(mContactManagerTouchEvent.size()); + threadContext = threadContextIt.getNext(); + } +} + +bool PxsContext::getManagerTouchEventCount(int* newTouch, int* lostTouch, int* ccdTouch) const +{ + if(newTouch) + *newTouch = int(mCMTouchEventCount[PXS_NEW_TOUCH_COUNT]); + + if(lostTouch) + *lostTouch = int(mCMTouchEventCount[PXS_LOST_TOUCH_COUNT]); + + if(ccdTouch) + *ccdTouch = int(mCMTouchEventCount[PXS_CCD_RETOUCH_COUNT]); + + return true; +} + +bool PxsContext::fillManagerTouchEvents(PxvContactManagerTouchEvent* newTouch, PxI32& newTouchCount, PxvContactManagerTouchEvent* lostTouch, PxI32& lostTouchCount, + PxvContactManagerTouchEvent* ccdTouch, PxI32& ccdTouchCount) +{ + PxU32 index; + + const PxvContactManagerTouchEvent* newTouchStart = newTouch; + const PxvContactManagerTouchEvent* lostTouchStart = lostTouch; + const PxvContactManagerTouchEvent* ccdTouchStart = ccdTouch; + + const PxvContactManagerTouchEvent* newTouchEnd = newTouch + newTouchCount; + const PxvContactManagerTouchEvent* lostTouchEnd = lostTouch + lostTouchCount; + const PxvContactManagerTouchEvent* ccdTouchEnd = ccdTouch + ccdTouchCount; + + PX_UNUSED(newTouchEnd); + PX_UNUSED(lostTouchEnd); + PX_UNUSED(ccdTouchEnd); + + Cm::BitMap::Iterator it(mContactManagerTouchEvent); + + while((index = it.getNext()) != Cm::BitMap::Iterator::DONE) + { + PxsContactManager* cm = mContactManagerPool.findByIndexFast(index); + + if(cm->getTouchStatus()) + { + if (!cm->getHasCCDRetouch()) + { + PX_ASSERT(newTouch < newTouchEnd); + newTouch->manager = cm; + newTouch->userData = cm->getUserData(); + newTouch++; + } + else + { + PX_ASSERT(ccdTouch); + PX_ASSERT(ccdTouch < ccdTouchEnd); + ccdTouch->manager = cm; + ccdTouch->userData = cm->getUserData(); + cm->clearCCDRetouch(); + ccdTouch++; + } + } + else + { + PX_ASSERT(lostTouch < lostTouchEnd); + lostTouch->manager = cm; + lostTouch->userData = cm->getUserData(); + lostTouch++; + } + } + newTouchCount = PxI32(newTouch - newTouchStart); + lostTouchCount = PxI32(lostTouch - lostTouchStart); + ccdTouchCount = PxI32(ccdTouch - ccdTouchStart); + return true; +} + + + +bool PxsContext::fillManagerPatchChangedEvents(PxsContactManager** foundPatch, PxU32& foundPatchCount, + PxsContactManager** lostPatch, PxU32& lostPatchCount) +{ + Cm::BitMap::Iterator it(mContactManagerPatchChangeEvent); + + PxsContactManagerOutputIterator outputs = mNpImplementationContext->getContactManagerOutputs(); + + PxU32 index; + PxsContactManager** currFoundPatch = foundPatch; + PxsContactManager** currLostPatch = lostPatch; + while((index = it.getNext()) != Cm::BitMap::Iterator::DONE) + { + PxsContactManager* cm = mContactManagerPool.findByIndexFast(index); + PxcNpWorkUnit& workUnit = cm->getWorkUnit(); + PxsContactManagerOutput& output = outputs.getContactManager(workUnit.mNpIndex); + if(output.nbPatches > output.prevPatches) + { + PX_ASSERT(PxU32(currFoundPatch - foundPatch) < foundPatchCount); + *currFoundPatch = cm; + currFoundPatch++; + } + else if(output.nbPatches < output.prevPatches) + { + PX_ASSERT(PxU32(currLostPatch - lostPatch) < lostPatchCount); + *currLostPatch = cm; + currLostPatch++; + } + } + + foundPatchCount = PxU32(currFoundPatch - foundPatch); + lostPatchCount = PxU32(currLostPatch - lostPatch); + return true; +} + + +void PxsContext::beginUpdate() +{ +#if PX_ENABLE_SIM_STATS + mSimStats.clearAll(); +#endif +} + + +// Contact manager related + +PxReal PxsContext::getVisualizationParameter(PxVisualizationParameter::Enum param) const +{ + PX_ASSERT(param < PxVisualizationParameter::eNUM_VALUES); + + return mVisualizationParams[param]; +} + +void PxsContext::setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) +{ + PX_ASSERT(param < PxVisualizationParameter::eNUM_VALUES); + PX_ASSERT(value >= 0.0f); + + mVisualizationParams[param] = value; +} + + + + + + diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp new file mode 100644 index 000000000..381830ed7 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxsDefaultMemoryManager.h" + +namespace physx +{ + + PxsDefaultMemoryManager::~PxsDefaultMemoryManager() + { + for (PxU32 i = 0; i < mAllocators.size(); ++i) + { + mAllocators[i]->~VirtualAllocatorCallback(); + PX_FREE(mAllocators[i]); + } + } + + Ps::VirtualAllocatorCallback* PxsDefaultMemoryManager::createHostMemoryAllocator(const PxU32 gpuComputeVersion) + { + PX_UNUSED(gpuComputeVersion); + Ps::VirtualAllocatorCallback* allocator = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxsDefaultMemoryAllocator), "PxsDefaultMemoryAllocator"), PxsDefaultMemoryAllocator()); + mAllocators.pushBack(allocator); + return allocator; + } + + //this is an empty stub + Ps::VirtualAllocatorCallback* PxsDefaultMemoryManager::createDeviceMemoryAllocator(const PxU32 gpuComputeVersion) + { + PX_UNUSED(gpuComputeVersion); + return NULL; + } + + void PxsDefaultMemoryManager::destroyMemoryAllocator() + { + for (PxU32 i = 0; i < mAllocators.size(); ++i) + { + mAllocators[i]->~VirtualAllocatorCallback(); + PX_FREE(mAllocators[i]); + } + } + + + PxsMemoryManager* createMemoryManager() + { + return PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxsDefaultMemoryManager), PX_DEBUG_EXP("PxsDefaultMemoryManager")), PxsDefaultMemoryManager()); + } + +} diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp new file mode 100644 index 000000000..2dd427ee4 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp @@ -0,0 +1,2323 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxsIslandSim.h" +#include "PsSort.h" +#include "PsUtilities.h" +#include "common/PxProfileZone.h" + +#define IG_SANITY_CHECKS 0 + +namespace physx +{ +namespace IG +{ + + IslandSim::IslandSim(Ps::Array* firstPartitionEdges, Cm::BlockArray& edgeNodeIndices, + Ps::Array* destroyedPartitionEdges, PxU64 contextID) : + mNodes(PX_DEBUG_EXP("IslandSim::mNodes")), + mActiveNodeIndex(PX_DEBUG_EXP("IslandSim::mActiveNodeIndex")), + mIslands(PX_DEBUG_EXP("IslandSim::mIslands")), + mIslandStaticTouchCount(PX_DEBUG_EXP("IslandSim.activeStaticTouchCount")), + mActiveKinematicNodes(PX_DEBUG_EXP("IslandSim::mActiveKinematicNodes")), + mHopCounts(PX_DEBUG_EXP("IslandSim::mHopCounts")), + mFastRoute(PX_DEBUG_EXP("IslandSim::,FastRoute")), + mIslandIds(PX_DEBUG_EXP("IslandSim::mIslandIds")), + mActiveIslands(PX_DEBUG_EXP("IslandSim::mActiveIslands")), + mLastMapIndex(0), + mActivatingNodes(PX_DEBUG_EXP("IslandSim::mActivatingNodes")), + mDestroyedEdges(PX_DEBUG_EXP("IslandSim::mDestroyedEdges")), + mTempIslandIds(PX_DEBUG_EXP("IslandSim::mTempIslandIds")), + mVisitedNodes(PX_DEBUG_EXP("IslandSim::mVisitedNodes")), + mFirstPartitionEdges(firstPartitionEdges), + mEdgeNodeIndices(edgeNodeIndices), + mDestroyedPartitionEdges(destroyedPartitionEdges), + mContextId(contextID) + { + mInitialActiveNodeCount[0] = 0; mInitialActiveNodeCount[1] = 0; mNpIndexPtr = NULL; + mActiveEdgeCount[0] = mActiveEdgeCount[1] = 0; + } + +#if PX_ENABLE_ASSERTS +template +static bool contains(Ps::Array& arr, const Thing& thing) +{ + for(PxU32 a = 0; a < arr.size(); ++a) + { + if(thing == arr[a]) + return true; + } + return false; +} +#endif + +void IslandSim::resize(const PxU32 nbNodes, const PxU32 nbContactManagers, const PxU32 nbConstraints) +{ + PxU32 totalEdges = nbContactManagers + nbConstraints; + mNodes.reserve(nbNodes); + mIslandIds.reserve(nbNodes); + mEdges.reserve(totalEdges); + mActiveContactEdges.resize(totalEdges); + mEdgeInstances.reserve(totalEdges*2); +} + +void IslandSim::addNode(bool isActive, bool isKinematic, Node::NodeType type, NodeIndex nodeIndex) +{ + PxU32 handle = nodeIndex.index(); + if(handle == mNodes.capacity()) + { + const PxU32 newCapacity = PxMax(2*mNodes.capacity(), 256u); + mNodes.reserve(newCapacity); + mIslandIds.reserve(newCapacity); + mFastRoute.reserve(newCapacity); + mHopCounts.reserve(newCapacity); + mActiveNodeIndex.reserve(newCapacity); + } + + const PxU32 newSize = PxMax(handle+1, mNodes.size()); + mNodes.resize(newSize); + mIslandIds.resize(newSize); + mFastRoute.resize(newSize); + mHopCounts.resize(newSize); + mActiveNodeIndex.resize(newSize); + + mActiveNodeIndex[handle] = IG_INVALID_NODE; + + + Node& node = mNodes[handle]; + node.mType = Ps::to8(type); + //Ensure that the node is not currently being used. + PX_ASSERT(node.isDeleted()); + + PxU8 flags = PxU16(isActive ? 0 : Node::eREADY_FOR_SLEEPING); + if(isKinematic) + flags |= Node::eKINEMATIC; + node.mFlags = flags; + mIslandIds[handle] = IG_INVALID_ISLAND; + mFastRoute[handle].setIndices(IG_INVALID_NODE); + mHopCounts[handle] = 0; + + if(!isKinematic) + { + IslandId islandHandle = mIslandHandles.getHandle(); + + if(islandHandle == mIslands.capacity()) + { + const PxU32 newCapacity = PxMax(2*mIslands.capacity(), 256u); + mIslands.reserve(newCapacity); + mIslandAwake.resize(newCapacity); + mIslandStaticTouchCount.reserve(newCapacity); + } + mIslands.resize(PxMax(islandHandle+1, mIslands.size())); + mIslandStaticTouchCount.resize(PxMax(islandHandle+1, mIslands.size())); + mIslandAwake.growAndReset(PxMax(islandHandle+1, mIslands.size())); + Island& island = mIslands[islandHandle]; + island.mLastNode = island.mRootNode = nodeIndex; + island.mSize[type] = 1; + mIslandIds[handle] = islandHandle; + mIslandStaticTouchCount[islandHandle] = 0; + } + + if(isActive) + { + activateNode(nodeIndex); + } +} + +void IslandSim::addRigidBody(PxsRigidBody* body, bool isKinematic, bool isActive, NodeIndex nodeIndex) +{ + addNode(isActive, isKinematic, Node::eRIGID_BODY_TYPE, nodeIndex); + Node& node = mNodes[nodeIndex.index()]; + node.mRigidBody = body; +} + +void IslandSim::addArticulation(Sc::ArticulationSim* /*articulation*/, Dy::ArticulationV* llArtic, bool isActive, NodeIndex nodeIndex) +{ + addNode(isActive, false, Node::eARTICULATION_TYPE, nodeIndex); + Node& node = mNodes[nodeIndex.index()]; + node.mLLArticulation = llArtic; +} + +void IslandSim::connectEdge(EdgeInstance& instance, EdgeInstanceIndex edgeIndex, Node& source, NodeIndex /*destination*/) +{ + PX_ASSERT(instance.mNextEdge == IG_INVALID_EDGE); + PX_ASSERT(instance.mPrevEdge == IG_INVALID_EDGE); + + instance.mNextEdge = source.mFirstEdgeIndex; + if(source.mFirstEdgeIndex != IG_INVALID_EDGE) + { + EdgeInstance& firstEdge = mEdgeInstances[source.mFirstEdgeIndex]; + firstEdge.mPrevEdge = edgeIndex; + } + + source.mFirstEdgeIndex = edgeIndex; + instance.mPrevEdge = IG_INVALID_EDGE; +} + +void IslandSim::addConnection(NodeIndex nodeHandle1, NodeIndex nodeHandle2, Edge::EdgeType edgeType, EdgeIndex handle) +{ + PX_UNUSED(nodeHandle1); + PX_UNUSED(nodeHandle2); + if(handle >= mEdges.capacity()) + { + PX_PROFILE_ZONE("ReserveIslandEdges", getContextId()); + const PxU32 newSize = handle + 2048; + mEdges.reserve(newSize); + mActiveContactEdges.resize(mEdges.capacity()); + } + mEdges.resize(PxMax(mEdges.size(), handle+1)); + mActiveContactEdges.reset(handle); + + Edge& edge = mEdges[handle]; + + if(edge.isPendingDestroyed()) + { + //If it's in this state, then the edge has been tagged for destruction but actually is now not needed to be destroyed + edge.clearPendingDestroyed(); + return; + } + + if(edge.isInDirtyList()) + { + PX_ASSERT(mEdgeNodeIndices[handle * 2].index() == nodeHandle1.index()); + PX_ASSERT(mEdgeNodeIndices[handle * 2 + 1].index() == nodeHandle2.index()); + PX_ASSERT(edge.mEdgeType == edgeType); + return; + } + + PX_ASSERT(!edge.isInserted()); + + PX_ASSERT(edge.isDestroyed()); + edge.clearDestroyed(); + + PX_ASSERT(edge.mNextIslandEdge == IG_INVALID_ISLAND); + PX_ASSERT(edge.mPrevIslandEdge == IG_INVALID_ISLAND); + + PX_ASSERT(mEdgeInstances.size() <= 2*handle || mEdgeInstances[2*handle].mNextEdge == IG_INVALID_EDGE); + PX_ASSERT(mEdgeInstances.size() <= 2*handle || mEdgeInstances[2*handle+1].mNextEdge == IG_INVALID_EDGE); + PX_ASSERT(mEdgeInstances.size() <= 2*handle || mEdgeInstances[2*handle].mPrevEdge == IG_INVALID_EDGE); + PX_ASSERT(mEdgeInstances.size() <= 2*handle || mEdgeInstances[2*handle+1].mPrevEdge == IG_INVALID_EDGE); + + edge.mEdgeType = edgeType; + + PX_ASSERT(handle*2 >= mEdgeInstances.size() || mEdgeInstances[handle*2].mNextEdge == IG_INVALID_EDGE); + PX_ASSERT(handle*2+1 >= mEdgeInstances.size() || mEdgeInstances[handle*2+1].mNextEdge == IG_INVALID_EDGE); + PX_ASSERT(handle*2 >= mEdgeInstances.size() || mEdgeInstances[handle*2].mPrevEdge == IG_INVALID_EDGE); + PX_ASSERT(handle*2+1 >= mEdgeInstances.size() || mEdgeInstances[handle*2+1].mPrevEdge == IG_INVALID_EDGE); + + //Add the new handle + if(!edge.isInDirtyList()) + { + PX_ASSERT(!contains(mDirtyEdges[edgeType], handle)); + mDirtyEdges[edgeType].pushBack(handle); + edge.markInDirtyList(); + } + edge.mEdgeState &= ~(Edge::eACTIVATING); +} + +void IslandSim::addConnectionToGraph(EdgeIndex handle) +{ + EdgeInstanceIndex instanceHandle = 2*handle; + PX_ASSERT(instanceHandle < mEdgeInstances.capacity()); + /*if(instanceHandle == mEdgeInstances.capacity()) + { + mEdgeInstances.reserve(2*mEdgeInstances.capacity() + 2); + }*/ + mEdgeInstances.resize(PxMax(instanceHandle+2, mEdgeInstances.size())); + + Edge& edge = mEdges[handle]; + + bool activeEdge = false; + bool kinematicKinematicEdge = true; + + NodeIndex nodeIndex1 = mEdgeNodeIndices[instanceHandle]; + NodeIndex nodeIndex2 = mEdgeNodeIndices[instanceHandle+1]; + + if(nodeIndex1.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex1.index()]; + connectEdge(mEdgeInstances[instanceHandle], instanceHandle, node, nodeIndex2); + activeEdge = node.isActive() || node.isActivating(); + kinematicKinematicEdge = node.isKinematic(); + } + + if (nodeIndex1.index() != nodeIndex2.index() && nodeIndex2.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex2.index()]; + connectEdge(mEdgeInstances[instanceHandle + 1], instanceHandle + 1, node, nodeIndex1); + activeEdge = activeEdge || node.isActive() || node.isActivating(); + kinematicKinematicEdge = kinematicKinematicEdge && node.isKinematic(); + } + + if(activeEdge && (!kinematicKinematicEdge || edge.getEdgeType() == IG::Edge::eCONTACT_MANAGER)) + { + markEdgeActive(handle); + edge.activateEdge(); + } +} + +void IslandSim::removeConnectionFromGraph(EdgeIndex edgeIndex) +{ + NodeIndex nodeIndex1 = mEdgeNodeIndices[2 * edgeIndex]; + NodeIndex nodeIndex2 = mEdgeNodeIndices[2 * edgeIndex+1]; + if (nodeIndex1.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex1.index()]; + if (nodeIndex2.index() == mFastRoute[nodeIndex1.index()].index()) + mFastRoute[nodeIndex1.index()].setIndices(IG_INVALID_NODE); + if(!node.isDirty()) + { + //mDirtyNodes.pushBack(nodeIndex1); + mDirtyMap.growAndSet(nodeIndex1.index()); + node.markDirty(); + } + } + + if (nodeIndex2.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex2.index()]; + if (nodeIndex1.index() == mFastRoute[nodeIndex2.index()].index()) + mFastRoute[nodeIndex2.index()].setIndices(IG_INVALID_NODE); + if(!node.isDirty()) + { + mDirtyMap.growAndSet(nodeIndex2.index()); + node.markDirty(); + } + } +} + +void IslandSim::disconnectEdge(EdgeInstance& instance, EdgeInstanceIndex edgeIndex, Node& node) +{ + + PX_ASSERT(instance.mNextEdge == IG_INVALID_EDGE || mEdgeInstances[instance.mNextEdge].mPrevEdge == edgeIndex); + PX_ASSERT(instance.mPrevEdge == IG_INVALID_EDGE || mEdgeInstances[instance.mPrevEdge].mNextEdge == edgeIndex); + + if(node.mFirstEdgeIndex == edgeIndex) + { + PX_ASSERT(instance.mPrevEdge == IG_INVALID_EDGE); + node.mFirstEdgeIndex = instance.mNextEdge; + } + else + { + EdgeInstance& prev = mEdgeInstances[instance.mPrevEdge]; + PX_ASSERT(prev.mNextEdge == edgeIndex); + prev.mNextEdge = instance.mNextEdge; + } + + if(instance.mNextEdge != IG_INVALID_EDGE) + { + EdgeInstance& next = mEdgeInstances[instance.mNextEdge]; + PX_ASSERT(next.mPrevEdge == edgeIndex); + next.mPrevEdge = instance.mPrevEdge; + } + + PX_ASSERT(instance.mNextEdge == IG_INVALID_EDGE || mEdgeInstances[instance.mNextEdge].mPrevEdge == instance.mPrevEdge); + PX_ASSERT(instance.mPrevEdge == IG_INVALID_EDGE || mEdgeInstances[instance.mPrevEdge].mNextEdge == instance.mNextEdge); + + instance.mNextEdge = IG_INVALID_EDGE; + instance.mPrevEdge = IG_INVALID_EDGE; +} + +void IslandSim::removeConnection(EdgeIndex edgeIndex) +{ + Edge& edge = mEdges[edgeIndex]; + if(!edge.isPendingDestroyed())// && edge.isInserted()) + { + mDestroyedEdges.pushBack(edgeIndex); + /*if(!edge.isInserted()) + edge.setReportOnlyDestroy();*/ + } + edge.setPendingDestroyed(); +} + +void IslandSim::removeConnectionInternal(EdgeIndex edgeIndex) +{ + PX_ASSERT(edgeIndex != IG_INVALID_EDGE); + EdgeInstanceIndex edgeInstanceBase = edgeIndex*2; + + + NodeIndex nodeIndex1 = mEdgeNodeIndices[edgeIndex * 2]; + + if (nodeIndex1.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex1.index()]; + disconnectEdge(mEdgeInstances[edgeInstanceBase], edgeInstanceBase, node); + } + + NodeIndex nodeIndex2 = mEdgeNodeIndices[edgeIndex * 2 + 1]; + + if (nodeIndex2.index() != IG_INVALID_NODE && nodeIndex1.index() != nodeIndex2.index()) + { + Node& node = mNodes[nodeIndex2.index()]; + disconnectEdge(mEdgeInstances[edgeInstanceBase+1], edgeInstanceBase+1, node); + } +} + + +void IslandSim::addContactManager(PxsContactManager* /*manager*/, NodeIndex nodeHandle1, NodeIndex nodeHandle2, EdgeIndex handle) +{ + addConnection(nodeHandle1, nodeHandle2, Edge::eCONTACT_MANAGER, handle); +} + +void IslandSim::addConstraint(Dy::Constraint* /*constraint*/, NodeIndex nodeHandle1, NodeIndex nodeHandle2, EdgeIndex handle) +{ + addConnection(nodeHandle1, nodeHandle2, Edge::eCONSTRAINT, handle); +} + +void IslandSim::activateNode(NodeIndex nodeIndex) +{ + if(nodeIndex.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex.index()]; + + if(!(node.isActive() || + node.isActivating())) + { + + //If the node is kinematic and already in the active node list, then we need to remove it + //from the active kinematic node list, then re-add it after the wake-up. It's a bit stupid + //but it means that we don't need another index + + if(node.isKinematic() && mActiveNodeIndex[nodeIndex.index()] != IG_INVALID_NODE) + { + //node.setActive(); + //node.clearIsReadyForSleeping(); //Clear the "isReadyForSleeping" flag. Just in case it was set + //return; + + PxU32 activeRefCount = node.mActiveRefCount; + node.mActiveRefCount = 0; + node.clearActive(); + markKinematicInactive(nodeIndex); + node.mActiveRefCount = activeRefCount; + } + + node.setActivating(); //Tag it as activating + PX_ASSERT(mActiveNodeIndex[nodeIndex.index()] == IG_INVALID_NODE); + mActiveNodeIndex[nodeIndex.index()] = mActivatingNodes.size(); + //Add to waking list + mActivatingNodes.pushBack(nodeIndex); + } + node.clearIsReadyForSleeping(); //Clear the "isReadyForSleeping" flag. Just in case it was set + node.clearDeactivating(); + } +} + +void IslandSim::deactivateNode(NodeIndex nodeIndex) +{ + if(nodeIndex.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeIndex.index()]; + + //If the node is activating, clear its activating state and remove it from the activating list. + //If it wasn't already activating, then it's probably already in the active list + + bool wasActivating = node.isActivating(); + + if(wasActivating) + { + //Already activating, so remove it from the activating list + node.clearActivating(); + PX_ASSERT(mActivatingNodes[mActiveNodeIndex[nodeIndex.index()]].index() == nodeIndex.index()); + NodeIndex replaceIndex = mActivatingNodes[mActivatingNodes.size()-1]; + mActiveNodeIndex[replaceIndex.index()] = mActiveNodeIndex[nodeIndex.index()]; + mActivatingNodes[mActiveNodeIndex[nodeIndex.index()]] = replaceIndex; + mActivatingNodes.forceSize_Unsafe(mActivatingNodes.size()-1); + mActiveNodeIndex[nodeIndex.index()] = IG_INVALID_NODE; + + if(node.isKinematic()) + { + //If we were temporarily removed from the active kinematic list to be put in the waking kinematic list + //then add the node back in before deactivating the node. This is a bit counter-intuitive but the active + //kinematic list contains all active kinematics and all kinematics that are referenced by an active constraint + PX_ASSERT(mActiveNodeIndex[nodeIndex.index()] == IG_INVALID_NODE); + mActiveNodeIndex[nodeIndex.index()] = mActiveKinematicNodes.size(); + mActiveKinematicNodes.pushBack(nodeIndex); + } + } + + //Raise the "ready for sleeping" flag so that island gen can put this node to sleep + node.setIsReadyForSleeping(); + } +} + +void IslandSim::putNodeToSleep(NodeIndex nodeIndex) +{ + if(nodeIndex.index() != IG_INVALID_NODE) + { + deactivateNode(nodeIndex); + } +} + + +void IslandSim::activateNodeInternal(NodeIndex nodeIndex) +{ + //This method should activate the node, then activate all the connections involving this node + Node& node = mNodes[nodeIndex.index()]; + + if(!node.isActive()) + { + PX_ASSERT(mActiveNodeIndex[nodeIndex.index()] == IG_INVALID_NODE); + + //Activate all the edges + nodes... + + EdgeInstanceIndex index = node.mFirstEdgeIndex; + + while(index != IG_INVALID_EDGE) + { + EdgeIndex idx = index/2; + Edge& edge = mEdges[idx]; //InstanceIndex/2 = edgeIndex + if(!edge.isActive()) + { + //Make the edge active... + PX_ASSERT(mEdgeNodeIndices[idx * 2].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2].index()].isKinematic()); + PX_ASSERT(mEdgeNodeIndices[idx * 2 + 1].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isKinematic()); + + markEdgeActive(idx); + edge.activateEdge(); + + } + index = mEdgeInstances[index].mNextEdge; + } + + if(node.isKinematic()) + { + markKinematicActive(nodeIndex); + } + else + { + markActive(nodeIndex); + } + node.setActive(); + } + +} + +void IslandSim::deactivateNodeInternal(NodeIndex nodeIndex) +{ + //We deactivate a node, we need to loop through all the edges and deactivate them *if* both bodies are asleep + + Node& node = mNodes[nodeIndex.index()]; + + if(node.isActive()) + { + if(node.isKinematic()) + { + markKinematicInactive(nodeIndex); + } + else + { + markInactive(nodeIndex); + } + + //Clear the active status flag + node.clearActive(); + node.clearActivating(); + + EdgeInstanceIndex index = node.mFirstEdgeIndex; + + while(index != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[index]; + + + NodeIndex outboundNode = mEdgeNodeIndices[index ^ 1]; + if(outboundNode.index() == IG_INVALID_NODE || + !mNodes[outboundNode.index()].isActive()) + { + EdgeIndex idx = index/2; + Edge& edge = mEdges[idx]; //InstanceIndex/2 = edgeIndex + //PX_ASSERT(edge.isActive()); //The edge must currently be inactive because the node was active + //Deactivate the edge if both nodes connected are inactive OR if one node is static/kinematic and the other is inactive... + PX_ASSERT(mEdgeNodeIndices[index & (~1)].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[index & (~1)].index()].isActive()); + PX_ASSERT(mEdgeNodeIndices[index | 1].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[index | 1].index()].isActive()); + if(edge.isActive()) + { + edge.deactivateEdge(); + mActiveEdgeCount[edge.mEdgeType]--; + removeEdgeFromActivatingList(idx); + mDeactivatingEdges[edge.mEdgeType].pushBack(idx); + } + } + index = instance.mNextEdge; + } + } + +} + +bool IslandSim::canFindRoot(NodeIndex startNode, NodeIndex targetNode, Ps::Array* visitedNodes) +{ + if(visitedNodes) + visitedNodes->pushBack(startNode); + if(startNode.index() == targetNode.index()) + return true; + Cm::BitMap visitedState; + visitedState.resizeAndClear(mNodes.size()); + + Ps::Array stack; + + stack.pushBack(startNode); + + visitedState.set(startNode.index()); + + do + { + NodeIndex currentIndex = stack.popBack(); + Node& currentNode = mNodes[currentIndex.index()]; + + EdgeInstanceIndex currentEdge = currentNode.mFirstEdgeIndex; + + while(currentEdge != IG_INVALID_EDGE) + { + EdgeInstance& edge = mEdgeInstances[currentEdge]; + NodeIndex outboundNode = mEdgeNodeIndices[currentEdge ^ 1]; + if(outboundNode.index() != IG_INVALID_NODE && !mNodes[outboundNode.index()].isKinematic() && !visitedState.test(outboundNode.index())) + { + if(outboundNode.index() == targetNode.index()) + { + return true; + } + + visitedState.set(outboundNode.index()); + stack.pushBack(outboundNode); + if(visitedNodes) + visitedNodes->pushBack(outboundNode); + } + + currentEdge = edge.mNextEdge; + } + + } + while(stack.size()); + + return false; + +} + + + +void IslandSim::unwindRoute(PxU32 traversalIndex, NodeIndex lastNode, PxU32 hopCount, IslandId id) +{ + //We have found either a witness *or* the root node with this traversal. In the event of finding the root node, hopCount will be 0. In the event of finding + //a witness, hopCount will be the hopCount that witness reported as being the distance to the root. + + PxU32 currIndex = traversalIndex; + PxU32 hc = hopCount+1; //Add on 1 for the hop to the witness/root node. + do + { + TraversalState& state = mVisitedNodes[currIndex]; + mHopCounts[state.mNodeIndex.index()] = hc++; + mIslandIds[state.mNodeIndex.index()] = id; + mFastRoute[state.mNodeIndex.index()] = lastNode; + currIndex = state.mPrevIndex; + lastNode = state.mNodeIndex; + } + while(currIndex != IG_INVALID_NODE); +} + +void IslandSim::activateIsland(IslandId islandId) +{ + Island& island = mIslands[islandId]; + PX_ASSERT(!mIslandAwake.test(islandId)); + PX_ASSERT(island.mActiveIndex == IG_INVALID_ISLAND); + + NodeIndex currentNode = island.mRootNode; + while(currentNode.index() != IG_INVALID_NODE) + { + activateNodeInternal(currentNode); + currentNode = mNodes[currentNode.index()].mNextNode; + } + markIslandActive(islandId); +} + +void IslandSim::deactivateIsland(IslandId islandId) +{ + PX_ASSERT(mIslandAwake.test(islandId)); + Island& island = mIslands[islandId]; + + NodeIndex currentNode = island.mRootNode; + while(currentNode.index() != IG_INVALID_NODE) + { + Node& node = mNodes[currentNode.index()]; + //if(mActiveNodeIndex[currentNode.index()] < mInitialActiveNodeCount[node.mType]) + mNodesToPutToSleep[node.mType].pushBack(currentNode); //If this node was previously active, then push it to the list of nodes to deactivate + deactivateNodeInternal(currentNode); + currentNode = node.mNextNode; + } + markIslandInactive(islandId); +} + + +void IslandSim::wakeIslands() +{ + PX_PROFILE_ZONE("Basic.wakeIslands", getContextId()); + + //(1) Iterate over activating nodes and activate them + + + PxU32 originalActiveIslands = mActiveIslands.size(); + + for (PxU32 a = 0; a < Edge::eEDGE_TYPE_COUNT; ++a) + { + for (PxU32 i = 0, count = mActivatedEdges[a].size(); i < count; ++i) + { + IG::Edge& edge = mEdges[mActivatedEdges[a][i]]; + edge.mEdgeState &= (~Edge::eACTIVATING); + } + + } + + mActivatedEdges[0].forceSize_Unsafe(0); + mActivatedEdges[1].forceSize_Unsafe(0); + /*mInitialActiveEdgeCount[0] = mActiveEdges[0].size(); + mInitialActiveEdgeCount[1] = mActiveEdges[1].size();*/ + + for(PxU32 a = 0; a < mActivatingNodes.size(); ++a) + { + NodeIndex wakeNode = mActivatingNodes[a]; + + IslandId islandId = mIslandIds[wakeNode.index()]; + + Node& node = mNodes[wakeNode.index()]; + node.clearActivating(); + if(islandId != IG_INVALID_ISLAND) + { + if(!mIslandAwake.test(islandId)) + { + markIslandActive(islandId); + } + mActiveNodeIndex[wakeNode.index()] = IG_INVALID_NODE; //Mark active node as invalid. + activateNodeInternal(wakeNode); + } + else + { + PX_ASSERT(node.isKinematic()); + node.setActive(); + PX_ASSERT(mActiveNodeIndex[wakeNode.index()] == a); + mActiveNodeIndex[wakeNode.index()] = mActiveKinematicNodes.size(); + mActiveKinematicNodes.pushBack(wakeNode); + + //Wake up the islands connected to this waking kinematic! + EdgeInstanceIndex index = node.mFirstEdgeIndex; + while(index != IG_INVALID_EDGE) + { + EdgeInstance& edgeInstance = mEdgeInstances[index]; + + NodeIndex outboundNode = mEdgeNodeIndices[index ^ 1]; + //Edge& edge = mEdges[index/2]; + //if(edge.isConnected()) //Only wake up if the edge is not connected... + NodeIndex nodeIndex = outboundNode; + + if (nodeIndex.isStaticBody() || mIslandIds[nodeIndex.index()] == IG_INVALID_ISLAND) + { + //If the edge connects to a static body *or* it connects to a node which is not part of an island (i.e. a kinematic), then activate the edge + EdgeIndex idx = index / 2; + Edge& edge = mEdges[idx]; + if (!edge.isActive() && edge.getEdgeType() != IG::Edge::eCONSTRAINT) + { + //Make the edge active... + PX_ASSERT(mEdgeNodeIndices[idx * 2].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2].index()].isKinematic()); + PX_ASSERT(mEdgeNodeIndices[idx * 2 + 1].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isKinematic()); + + markEdgeActive(idx); + edge.activateEdge(); + + } + } + else + { + IslandId connectedIslandId = mIslandIds[nodeIndex.index()]; + if(!mIslandAwake.test(connectedIslandId)) + { + //Wake up that island + markIslandActive(connectedIslandId); + } + } + + index = edgeInstance.mNextEdge; + } + } + } + + mInitialActiveNodeCount[0] = mActiveNodes[0].size(); + mInitialActiveNodeCount[1] = mActiveNodes[1].size(); + + mActivatingNodes.forceSize_Unsafe(0); + + for(PxU32 a = originalActiveIslands; a < mActiveIslands.size(); ++a) + { + Island& island = mIslands[mActiveIslands[a]]; + + NodeIndex currentNode = island.mRootNode; + while(currentNode.index() != IG_INVALID_NODE) + { + activateNodeInternal(currentNode); + currentNode = mNodes[currentNode.index()].mNextNode; + } + } +} + +void IslandSim::wakeIslands2() +{ + PxU32 originalActiveIslands = mActiveIslands.size(); + + for (PxU32 a = 0; a < mActivatingNodes.size(); ++a) + { + NodeIndex wakeNode = mActivatingNodes[a]; + + IslandId islandId = mIslandIds[wakeNode.index()]; + + Node& node = mNodes[wakeNode.index()]; + node.clearActivating(); + if (islandId != IG_INVALID_ISLAND) + { + if (!mIslandAwake.test(islandId)) + { + markIslandActive(islandId); + } + mActiveNodeIndex[wakeNode.index()] = IG_INVALID_NODE; //Mark active node as invalid. + activateNodeInternal(wakeNode); + } + else + { + PX_ASSERT(node.isKinematic()); + node.setActive(); + PX_ASSERT(mActiveNodeIndex[wakeNode.index()] == a); + mActiveNodeIndex[wakeNode.index()] = mActiveKinematicNodes.size(); + mActiveKinematicNodes.pushBack(wakeNode); + + //Wake up the islands connected to this waking kinematic! + EdgeInstanceIndex index = node.mFirstEdgeIndex; + while (index != IG_INVALID_EDGE) + { + EdgeInstance& edgeInstance = mEdgeInstances[index]; + + NodeIndex outboundNode = mEdgeNodeIndices[index ^ 1]; + //Edge& edge = mEdges[index/2]; + //if(edge.isConnected()) //Only wake up if the edge is not connected... + NodeIndex nodeIndex = outboundNode; + + if (nodeIndex.isStaticBody() || mIslandIds[nodeIndex.index()] == IG_INVALID_ISLAND) + { + //If the edge connects to a static body *or* it connects to a node which is not part of an island (i.e. a kinematic), then activate the edge + EdgeIndex idx = index / 2; + Edge& edge = mEdges[idx]; + if (!edge.isActive() && edge.getEdgeType() != IG::Edge::eCONSTRAINT) + { + //Make the edge active... + PX_ASSERT(mEdgeNodeIndices[idx * 2].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2].index()].isKinematic()); + PX_ASSERT(mEdgeNodeIndices[idx * 2 + 1].index() == IG_INVALID_NODE || !mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isActive() || mNodes[mEdgeNodeIndices[idx * 2 + 1].index()].isKinematic()); + + markEdgeActive(idx); + edge.activateEdge(); + + } + } + else + { + IslandId connectedIslandId = mIslandIds[nodeIndex.index()]; + if (!mIslandAwake.test(connectedIslandId)) + { + //Wake up that island + markIslandActive(connectedIslandId); + } + } + + index = edgeInstance.mNextEdge; + } + } + } + + mActivatingNodes.forceSize_Unsafe(0); + + for (PxU32 a = originalActiveIslands; a < mActiveIslands.size(); ++a) + { + Island& island = mIslands[mActiveIslands[a]]; + + NodeIndex currentNode = island.mRootNode; + while (currentNode.index() != IG_INVALID_NODE) + { + activateNodeInternal(currentNode); + currentNode = mNodes[currentNode.index()].mNextNode; + } + } +} + +void IslandSim::insertNewEdges() +{ + PX_PROFILE_ZONE("Basic.insertNewEdges", getContextId()); + + mEdgeInstances.reserve(mEdges.capacity()*2); + + for(PxU32 i = 0; i < Edge::eEDGE_TYPE_COUNT; ++i) + { + for(PxU32 a = 0; a < mDirtyEdges[i].size(); ++a) + { + EdgeIndex edgeIndex = mDirtyEdges[i][a]; + + Edge& edge = mEdges[edgeIndex]; + + if(!edge.isPendingDestroyed()) + { + //PX_ASSERT(!edge.isInserted()); + if(!edge.isInserted()) + { + addConnectionToGraph(edgeIndex); + edge.setInserted(); + } + } + } + } +} + +void IslandSim::removeDestroyedEdges() +{ + PX_PROFILE_ZONE("Basic.removeDestroyedEdges", getContextId()); + + for(PxU32 a = 0; a < mDestroyedEdges.size(); ++a) + { + EdgeIndex edgeIndex = mDestroyedEdges[a]; + + Edge& edge = mEdges[edgeIndex]; + + if(edge.isPendingDestroyed()) + { + if(!edge.isInDirtyList() && edge.isInserted()) + { + PX_ASSERT(edge.isInserted()); + removeConnectionInternal(edgeIndex); + removeConnectionFromGraph(edgeIndex); + //edge.clearInserted(); + } + //edge.clearDestroyed(); + } + } +} + +void IslandSim::processNewEdges() +{ + PX_PROFILE_ZONE("Basic.processNewEdges", getContextId()); + //Stage 1: we process the list of new pairs. To do this, we need to first sort them based on a predicate... + + insertNewEdges(); + + mHopCounts.resize(mNodes.size()); //Make sure we have enough space for hop counts for all nodes + mFastRoute.resize(mNodes.size()); + + + for(PxU32 i = 0; i < Edge::eEDGE_TYPE_COUNT; ++i) + { + for(PxU32 a = 0; a < mDirtyEdges[i].size(); ++a) + { + EdgeIndex edgeIndex = mDirtyEdges[i][a]; + Edge& edge = mEdges[edgeIndex]; + + /*PX_ASSERT(edge.mState != Edge::eDESTROYED || ((edge.mNode1.index() == IG_INVALID_NODE || mNodes[edge.mNode1.index()].isKinematic() || mNodes[edge.mNode1.index()].isActive() == false) && + (edge.mNode2.index() == IG_INVALID_NODE || mNodes[edge.mNode2.index()].isKinematic() || mNodes[edge.mNode2.index()].isActive() == false)));*/ + + //edge.clearInDirtyList(); + + + //We do not process either destroyed or disconnected edges + if(/*edge.isConnected() && */!edge.isPendingDestroyed()) + { + //Conditions: + //(1) Neither body is in an island (static/kinematics are never in islands) so we need to create a new island containing these bodies + // or just 1 body if the other is kinematic/static + //(2) Both bodies are already in the same island. Update root node hop count estimates for the bodies if a route through the new connection + // is shorter for either body + //(3) One body is already in an island and the other isn't, so we just add the new body to the existing island. + //(4) Both bodies are in different islands. In that case, we merge the islands + + NodeIndex nodeIndex1 = mEdgeNodeIndices[2 * edgeIndex]; + NodeIndex nodeIndex2 = mEdgeNodeIndices[2 * edgeIndex+1]; + + IslandId islandId1 = nodeIndex1.index() == IG_INVALID_NODE ? IG_INVALID_ISLAND : mIslandIds[nodeIndex1.index()]; + IslandId islandId2 = nodeIndex2.index() == IG_INVALID_NODE ? IG_INVALID_ISLAND : mIslandIds[nodeIndex2.index()]; + + //TODO - wake ups!!!! + //If one of the nodes is awake and the other is asleep, we need to wake 'em up + + //When a node is activated, the island must also be activated... + + bool active1 = nodeIndex1.index() != IG_INVALID_NODE && mNodes[nodeIndex1.index()].isActive(); + bool active2 = nodeIndex2.index() != IG_INVALID_NODE && mNodes[nodeIndex2.index()].isActive(); + + IslandId islandId = IG_INVALID_ISLAND; + + if(islandId1 == IG_INVALID_ISLAND && islandId2 == IG_INVALID_ISLAND) + { + //All nodes should be introduced in an island now unless they are static or kinematic. Therefore, if we get here, we have an edge + //between 2 kinematic nodes or a kinematic and static node. These should not influence island management so we should just ignore + //these edges. + } + else if(islandId1 == islandId2) + { + islandId = islandId1; + if(active1 || active2) + { + PX_ASSERT(mIslandAwake.test(islandId1)); //If we got here, where the 2 were already in an island, if 1 node is awake, the whole island must be awake + } + //Both bodies in the same island. Nothing major to do already but we should see if this creates a shorter path to root for either node + PxU32 hopCount1 = mHopCounts[nodeIndex1.index()]; + PxU32 hopCount2 = mHopCounts[nodeIndex2.index()]; + if((hopCount1+1) < hopCount2) + { + //It would be faster for node 2 to go through node 1 + mHopCounts[nodeIndex2.index()] = hopCount1 + 1; + mFastRoute[nodeIndex2.index()] = nodeIndex1; + } + else if((hopCount2+1) < hopCount1) + { + //It would be faster for node 1 to go through node 2 + mHopCounts[nodeIndex1.index()] = hopCount2 + 1; + mFastRoute[nodeIndex1.index()] = nodeIndex2; + } + + //No need to activate/deactivate the island. Its state won't have changed + + } + else if(islandId1 == IG_INVALID_ISLAND) + { + islandId = islandId2; + if (nodeIndex1.index() != IG_INVALID_NODE) + { + if (!mNodes[nodeIndex1.index()].isKinematic()) + { + PX_ASSERT(islandId2 != IG_INVALID_ISLAND); + //We need to add node 1 to island2 + PX_ASSERT(mNodes[nodeIndex1.index()].mNextNode.index() == IG_INVALID_NODE); //Ensure that this node is not in any other island + PX_ASSERT(mNodes[nodeIndex1.index()].mPrevNode.index() == IG_INVALID_NODE); //Ensure that this node is not in any other island + + Island& island = mIslands[islandId2]; + + Node& lastNode = mNodes[island.mLastNode.index()]; + + PX_ASSERT(lastNode.mNextNode.index() == IG_INVALID_NODE); + + Node& node = mNodes[nodeIndex1.index()]; + lastNode.mNextNode = nodeIndex1; + node.mPrevNode = island.mLastNode; + island.mLastNode = nodeIndex1; + island.mSize[node.mType]++; + mIslandIds[nodeIndex1.index()] = islandId2; + mHopCounts[nodeIndex1.index()] = mHopCounts[nodeIndex2.index()] + 1; + mFastRoute[nodeIndex1.index()] = nodeIndex2; + + if(active1 || active2) + { + if(!mIslandAwake.test(islandId2)) + { + //This island wasn't already awake, so need to wake the whole island up + activateIsland(islandId2); + } + if(!active1) + { + //Wake up this node... + activateNodeInternal(nodeIndex1); + } + } + } + else if(active1 && !active2) + { + //Active kinematic object -> wake island! + activateIsland(islandId2); + } + } + else + { + //A new touch with a static body... + Node& node = mNodes[nodeIndex2.index()]; + node.mStaticTouchCount++; //Increment static touch counter on the body + //Island& island = mIslands[islandId2]; + //island.mStaticTouchCount++; //Increment static touch counter on the island + mIslandStaticTouchCount[islandId2]++; + + } + } + else if (islandId2 == IG_INVALID_ISLAND) + { + islandId = islandId1; + if (nodeIndex2.index() != IG_INVALID_NODE) + { + if (!mNodes[nodeIndex2.index()].isKinematic()) + { + PX_ASSERT(islandId1 != IG_INVALID_NODE); + //We need to add node 1 to island2 + PX_ASSERT(mNodes[nodeIndex2.index()].mNextNode.index() == IG_INVALID_NODE); //Ensure that this node is not in any other island + PX_ASSERT(mNodes[nodeIndex2.index()].mPrevNode.index() == IG_INVALID_NODE); //Ensure that this node is not in any other island + + Island& island = mIslands[islandId1]; + + Node& lastNode = mNodes[island.mLastNode.index()]; + PX_ASSERT(lastNode.mNextNode.index() == IG_INVALID_NODE); + Node& node = mNodes[nodeIndex2.index()]; + lastNode.mNextNode = nodeIndex2; + node.mPrevNode = island.mLastNode; + island.mLastNode = nodeIndex2; + island.mSize[node.mType]++; + mIslandIds[nodeIndex2.index()] = islandId1; + mHopCounts[nodeIndex2.index()] = mHopCounts[nodeIndex1.index()] + 1; + mFastRoute[nodeIndex2.index()] = nodeIndex1; + + if(active1 || active2) + { + if(!mIslandAwake.test(islandId1)) + { + //This island wasn't already awake, so need to wake the whole island up + activateIsland(islandId1); + } + if(!active1) + { + //Wake up this node... + activateNodeInternal(nodeIndex2); + } + } + } + else if(active2 && !active1) + { + //Active kinematic object -> wake island! + activateIsland(islandId1); + } + } + else + { + //New static touch + //A new touch with a static body... + Node& node = mNodes[nodeIndex1.index()]; + node.mStaticTouchCount++; //Increment static touch counter on the body + //Island& island = mIslands[islandId1]; + mIslandStaticTouchCount[islandId1]++; + //island.mStaticTouchCount++; //Increment static touch counter on the island + } + + } + else + { + PX_ASSERT(islandId1 != islandId2); + PX_ASSERT(islandId1 != IG_INVALID_ISLAND && islandId2 != IG_INVALID_ISLAND); + + if(active1 || active2) + { + //One of the 2 islands was awake, so need to wake the other one! We do this now, before we merge the islands, to ensure that all + //the bodies are activated + if(!mIslandAwake.test(islandId1)) + { + //This island wasn't already awake, so need to wake the whole island up + activateIsland(islandId1); + } + if(!mIslandAwake.test(islandId2)) + { + //This island wasn't already awake, so need to wake the whole island up + activateIsland(islandId2); + } + } + + //OK. We need to merge these islands together... + islandId = mergeIslands(islandId1, islandId2, nodeIndex1, nodeIndex2); + } + + if(islandId != IG_INVALID_ISLAND) + { + //Add new edge to existing island + Island& island = mIslands[islandId]; + addEdgeToIsland(island, edgeIndex); + } + } + } + + } + +} + +bool IslandSim::isPathTo(NodeIndex startNode, NodeIndex targetNode) +{ + Node& node = mNodes[startNode.index()]; + + EdgeInstanceIndex index = node.mFirstEdgeIndex; + while(index != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[index]; + if(/*mEdges[index/2].isConnected() &&*/ mEdgeNodeIndices[index^1].index() == targetNode.index()) + return true; + index = instance.mNextEdge; + } + return false; +} + +bool IslandSim::tryFastPath(NodeIndex startNode, NodeIndex targetNode, IslandId islandId) +{ + PX_UNUSED(startNode); + PX_UNUSED(targetNode); + + NodeIndex currentNode = startNode; + + PxU32 currentVisitedNodes = mVisitedNodes.size(); + + PxU32 depth = 0; + + bool found = false; + do + { + //Get the fast path from this node... + + if(mVisitedState.test(currentNode.index())) + { + found = mIslandIds[currentNode.index()] != IG_INVALID_ISLAND; //Already visited and not tagged with invalid island == a witness! + break; + } + if( currentNode.index() == targetNode.index()) + { + found = true; + break; + } + + mVisitedNodes.pushBack(TraversalState(currentNode, mVisitedNodes.size(), mVisitedNodes.size()-1, depth++)); + + PX_ASSERT(mFastRoute[currentNode.index()].index() == IG_INVALID_NODE || isPathTo(currentNode, mFastRoute[currentNode.index()])); + + mIslandIds[currentNode.index()] = IG_INVALID_ISLAND; + mVisitedState.set(currentNode.index()); + + currentNode = mFastRoute[currentNode.index()]; + } + while(currentNode.index() != IG_INVALID_NODE); + + for(PxU32 a = currentVisitedNodes; a < mVisitedNodes.size(); ++a) + { + TraversalState& state = mVisitedNodes[a]; + mIslandIds[state.mNodeIndex.index()] = islandId; + } + + if(!found) + { + for(PxU32 a = currentVisitedNodes; a < mVisitedNodes.size(); ++a) + { + TraversalState& state = mVisitedNodes[a]; + mVisitedState.reset(state.mNodeIndex.index()); + } + + mVisitedNodes.forceSize_Unsafe(currentVisitedNodes); + } + return found; + +} + +bool IslandSim::findRoute(NodeIndex startNode, NodeIndex targetNode, IslandId islandId) +{ + + //Firstly, traverse the fast path and tag up witnesses. TryFastPath can fail. In that case, no witnesses are left but this node is permitted to report + //that it is still part of the island. Whichever node lost its fast path will be tagged as dirty and will be responsible for recovering the fast path + //and tagging up the visited nodes + if(mFastRoute[startNode.index()].index() != IG_INVALID_NODE) + { + if(tryFastPath(startNode, targetNode, islandId)) + return true; + + //Try fast path can either be successful or not. If it was successful, then we had a valid fast path cached and all nodes on that fast path were tagged + //as witness nodes (visited and with a valid island ID). If the fast path was not successful, then no nodes were tagged as witnesses. + //Technically, we need to find a route to the root node but, as an optimization, we can simply return true from here with no witnesses added. + //Whichever node actually broke the "fast path" will also be on the list of dirty nodes and will be processed later. + //If that broken edge triggered an island separation, this node will be re-visited and added to that island, otherwise + //the path to the root node will be re-established. The end result is the same - the island state is computed - this just saves us some work. + //return true; + } + + { + //If we got here, there was no fast path. Therefore, we need to fall back on searching for the root node. This is optimized by using "hop counts". + //These are per-node counts that indicate the expected number of hops from this node to the root node. These are lazily evaluated and updated + //as new edges are formed or when traversals occur to re-establish islands. As a result, they may be inaccurate but they still serve the purpose + //of guiding our search to minimize the chances of us doing an exhaustive search to find the root node. + mIslandIds[startNode.index()] = IG_INVALID_ISLAND; + TraversalState* startTraversal = &mVisitedNodes.pushBack(TraversalState(startNode, mVisitedNodes.size(), IG_INVALID_NODE, 0)); + mVisitedState.set(startNode.index()); + QueueElement element(startTraversal, mHopCounts[startNode.index()]); + mPriorityQueue.push(element); + + do + { + QueueElement currentQE = mPriorityQueue.pop(); + + TraversalState& currentState = *currentQE.mState; + + Node& currentNode = mNodes[currentState.mNodeIndex.index()]; + + EdgeInstanceIndex edge = currentNode.mFirstEdgeIndex; + + while(edge != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[edge]; + { + NodeIndex nextIndex = mEdgeNodeIndices[edge ^ 1]; + + //Static or kinematic nodes don't connect islands. + if(nextIndex.index() != IG_INVALID_NODE && !mNodes[nextIndex.index()].isKinematic()) + { + if(nextIndex.index() == targetNode.index()) + { + unwindRoute(currentState.mCurrentIndex, nextIndex, 0, islandId); + return true; + } + + if(mVisitedState.test(nextIndex.index())) + { + //We already visited this node. This means that it's either in the priority queue already or we + //visited in on a previous pass. If it was visited on a previous pass, then it already knows what island it's in. + //We now need to test the island id to find out if this node knows the root. + //If it has a valid root id, that id *is* our new root. We can guesstimate our hop count based on the node's properties + + IslandId visitedIslandId = mIslandIds[nextIndex.index()]; + if(visitedIslandId != IG_INVALID_ISLAND) + { + //If we get here, we must have found a node that knows a route to our root node. It must not be a different island + //because that would caused me to have been visited already because totally separate islands trigger a full traversal on + //the orphaned side. + PX_ASSERT(visitedIslandId == islandId); + unwindRoute(currentState.mCurrentIndex, nextIndex, mHopCounts[nextIndex.index()], islandId); + return true; + } + } + else + { + //This node has not been visited yet, so we need to push it into the stack and continue traversing + TraversalState* state = &mVisitedNodes.pushBack(TraversalState(nextIndex, mVisitedNodes.size(), currentState.mCurrentIndex, currentState.mDepth+1)); + QueueElement qe(state, mHopCounts[nextIndex.index()]); + mPriorityQueue.push(qe); + mVisitedState.set(nextIndex.index()); + PX_ASSERT(mIslandIds[nextIndex.index()] == islandId); + mIslandIds[nextIndex.index()] = IG_INVALID_ISLAND; //Flag as invalid island until we know whether we can find root or an island id. + } + } + } + + edge = instance.mNextEdge; + } + } + while(mPriorityQueue.size()); + + return false; + } +} + +#define IG_LIMIT_DIRTY_NODES 0 + + +void IslandSim::processLostEdges(Ps::Array& destroyedNodes, bool allowDeactivation, bool permitKinematicDeactivation, + PxU32 dirtyNodeLimit) +{ + PX_UNUSED(dirtyNodeLimit); + PX_PROFILE_ZONE("Basic.processLostEdges", getContextId()); + //At this point, all nodes and edges are activated. + + //Bit map for visited + mVisitedState.resizeAndClear(mNodes.size()); + + //Reserve space on priority queue for at least 1024 nodes. It will resize if more memory is required during traversal. + mPriorityQueue.reserve(1024); + + mIslandSplitEdges[0].reserve(1024); + mIslandSplitEdges[1].reserve(1024); + + mVisitedNodes.reserve(mNodes.size()); //Make sure we have enough space for all nodes! + + const PxU32 nbDestroyedEdges = mDestroyedEdges.size(); + PX_UNUSED(nbDestroyedEdges); + { + PX_PROFILE_ZONE("Basic.removeEdgesFromIslands", getContextId()); + for (PxU32 a = 0; a < mDestroyedEdges.size(); ++a) + { + EdgeIndex lostIndex = mDestroyedEdges[a]; + Edge& lostEdge = mEdges[lostIndex]; + + if (lostEdge.isPendingDestroyed() && !lostEdge.isInDirtyList()) + { + //Process this edge... + if (!lostEdge.isReportOnlyDestroy() && lostEdge.isInserted()) + { + PxU32 index1 = mEdgeNodeIndices[mDestroyedEdges[a] * 2].index(); + PxU32 index2 = mEdgeNodeIndices[mDestroyedEdges[a] * 2 + 1].index(); + + IslandId islandId = IG_INVALID_ISLAND; + if (index1 != IG_INVALID_NODE && index2 != IG_INVALID_NODE) + { + PX_ASSERT(mIslandIds[index1] == IG_INVALID_ISLAND || mIslandIds[index2] == IG_INVALID_ISLAND || + mIslandIds[index1] == mIslandIds[index2]); + islandId = mIslandIds[index1] != IG_INVALID_ISLAND ? mIslandIds[index1] : mIslandIds[index2]; + } + else if (index1 != IG_INVALID_NODE) + { + PX_ASSERT(index2 == IG_INVALID_NODE); + Node& node = mNodes[index1]; + if (!node.isKinematic()) + { + islandId = mIslandIds[index1]; + node.mStaticTouchCount--; + //Island& island = mIslands[islandId]; + mIslandStaticTouchCount[islandId]--; + //island.mStaticTouchCount--; + } + } + else if (index2 != IG_INVALID_NODE) + { + PX_ASSERT(index1 == IG_INVALID_NODE); + Node& node = mNodes[index2]; + if (!node.isKinematic()) + { + islandId = mIslandIds[index2]; + node.mStaticTouchCount--; + //Island& island = mIslands[islandId]; + mIslandStaticTouchCount[islandId]--; + //island.mStaticTouchCount--; + } + } + + if (islandId != IG_INVALID_ISLAND) + { + //We need to remove this edge from the island + Island& island = mIslands[islandId]; + removeEdgeFromIsland(island, lostIndex); + } + } + + lostEdge.clearInserted(); + + } + } + } + + if (allowDeactivation) + { + PX_PROFILE_ZONE("Basic.findPathsAndBreakIslands", getContextId()); + + + //KS - process only this many dirty nodes, deferring future dirty nodes to subsequent frames. + //This means that it may take several frames for broken edges to trigger islands to completely break but this is better + //than triggering large performance spikes. +#if IG_LIMIT_DIRTY_NODES + Cm::BitMap::CircularIterator iter(mDirtyMap, mLastMapIndex); + const PxU32 MaxCount = dirtyNodeLimit;// +10000000; + PxU32 lastMapIndex = mLastMapIndex; + PxU32 count = 0; +#else + Cm::BitMap::Iterator iter(mDirtyMap); +#endif + + + PxU32 dirtyIdx; + +#if IG_LIMIT_DIRTY_NODES + while ((dirtyIdx = iter.getNext()) != Cm::BitMap::CircularIterator::DONE + && (count++ < MaxCount) +#else + while ((dirtyIdx = iter.getNext()) != Cm::BitMap::Iterator::DONE +#endif + ) + { +#if IG_LIMIT_DIRTY_NODES + lastMapIndex = dirtyIdx + 1; +#endif + //Process dirty nodes. Figure out if we can make our way from the dirty node to the root. + + mPriorityQueue.clear(); //Clear the queue used for traversal + mVisitedNodes.forceSize_Unsafe(0); //Clear the list of nodes in this island + NodeIndex dirtyNodeIndex(dirtyIdx); + Node& dirtyNode = mNodes[dirtyNodeIndex.index()]; + + //Check whether this node has already been touched. If it has been touched this frame, then its island state is reliable + //and we can just unclear the dirty flag on the body. If we were already visited, then the state should have already been confirmed in a + //previous pass. + if (!dirtyNode.isKinematic() && !dirtyNode.isDeleted() && !mVisitedState.test(dirtyNodeIndex.index())) + { + //We haven't visited this node in our island repair passes yet, so we still need to process until we've hit a visited node or found + //our root node. Note that, as soon as we hit a visited node that has already been processed in a previous pass, we know that we can rely + //on its island information although the hop counts may not be optimal. It also indicates that this island was not broken immediately because + //otherwise, the entire new sub-island would already have been visited and this node would have already had its new island state assigned. + + //Indicate that I've been visited + + IslandId islandId = mIslandIds[dirtyNodeIndex.index()]; + Island& findIsland = mIslands[islandId]; + + NodeIndex searchNode = findIsland.mRootNode;//The node that we're searching for! + + if (searchNode.index() != dirtyNodeIndex.index()) //If we are the root node, we don't need to do anything! + { + if (findRoute(dirtyNodeIndex, searchNode, islandId)) + { + //We found the root node so let's let every visited node know that we found its root + //and we can also update our hop counts because we recorded how many hops it took to reach this + //node + + //We already filled in the path to the root/witness with accurate hop counts. Now we just need to fill in the estimates + //for the remaining nodes and re-define their islandIds. We approximate their path to the root by just routing them through + //the route we already found. + + //This loop works because mVisitedNodes are recorded in the order they were visited and we already filled in the critical path + //so the remainder of the paths will just fork from that path. + + //Verify state (that we can see the root from this node)... + +#if IG_SANITY_CHECKS + PX_ASSERT(canFindRoot(dirtyNode, searchNode, NULL)); //Verify that we found the connection +#endif + + for (PxU32 b = 0; b < mVisitedNodes.size(); ++b) + { + TraversalState& state = mVisitedNodes[b]; + if (mIslandIds[state.mNodeIndex.index()] == IG_INVALID_ISLAND) + { + mHopCounts[state.mNodeIndex.index()] = mHopCounts[mVisitedNodes[state.mPrevIndex].mNodeIndex.index()] + 1; + mFastRoute[state.mNodeIndex.index()] = mVisitedNodes[state.mPrevIndex].mNodeIndex; + mIslandIds[state.mNodeIndex.index()] = islandId; + } + } + } + else + { + //If I traversed and could not find the root node, then I have established a new island. In this island, I am the root node + //and I will point all my nodes towards me. Furthermore, I have established how many steps it took to reach all nodes in my island + + //OK. We need to separate the islands. We have a list of nodes that are part of the new island (mVisitedNodes) and we know that the + //first node in that list is the root node. + + + //OK, we need to remove all these actors from their current island, then add them to the new island... + + Island& oldIsland = mIslands[islandId]; + //We can just unpick these nodes from the island because they do not contain the root node (if they did, then we wouldn't be + //removing this node from the island at all). The only challenge is if we need to remove the last node. In that case + //we need to re-establish the new last node in the island but perhaps the simplest way to do that would be to traverse + //the island to establish the last node again + +#if IG_SANITY_CHECKS + PX_ASSERT(!canFindRoot(dirtyNode, searchNode, NULL)); +#endif + + PxU32 totalStaticTouchCount = 0; + mIslandSplitEdges[0].forceSize_Unsafe(0); + mIslandSplitEdges[1].forceSize_Unsafe(0); + PxU32 size[2] = { 0,0 }; + + //NodeIndex lastIndex = oldIsland.mLastNode; + + //size[node.mType] = 1; + + for (PxU32 a = 0; a < mVisitedNodes.size(); ++a) + { + NodeIndex index = mVisitedNodes[a].mNodeIndex; + Node& node = mNodes[index.index()]; + + if (node.mNextNode.index() != IG_INVALID_NODE) + mNodes[node.mNextNode.index()].mPrevNode = node.mPrevNode; + else + oldIsland.mLastNode = node.mPrevNode; + if (node.mPrevNode.index() != IG_INVALID_NODE) + mNodes[node.mPrevNode.index()].mNextNode = node.mNextNode; + + size[node.mType]++; + + node.mNextNode.setIndices(IG_INVALID_NODE); + node.mPrevNode.setIndices(IG_INVALID_NODE); + + PX_ASSERT(mNodes[oldIsland.mLastNode.index()].mNextNode.index() == IG_INVALID_NODE); + + totalStaticTouchCount += node.mStaticTouchCount; + + EdgeInstanceIndex idx = node.mFirstEdgeIndex; + + while (idx != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[idx]; + const EdgeIndex edgeIndex = idx / 2; + Edge& edge = mEdges[edgeIndex]; + + //Only split the island if we're processing the first node or if the first node is infinte-mass + if (!(idx & 1) || (mEdgeNodeIndices[idx & (~1)].index() == IG_INVALID_NODE || mNodes[mEdgeNodeIndices[idx & (~1)].index()].isKinematic())) + { + //We will remove this edge from the island... + mIslandSplitEdges[edge.mEdgeType].pushBack(edgeIndex); + + removeEdgeFromIsland(oldIsland, edgeIndex); + + } + idx = instance.mNextEdge; + } + + } + + //oldIsland.mStaticTouchCount -= totalStaticTouchCount; + mIslandStaticTouchCount[islandId] -= totalStaticTouchCount; + + oldIsland.mSize[0] -= size[0]; + oldIsland.mSize[1] -= size[1]; + + //Now add all these nodes to the new island + + //(1) Create the new island... + IslandId newIslandHandle = mIslandHandles.getHandle(); + /*if(newIslandHandle == mIslands.capacity()) + { + mIslands.reserve(2*mIslands.capacity() + 1); + }*/ + mIslands.resize(PxMax(newIslandHandle + 1, mIslands.size())); + mIslandStaticTouchCount.resize(PxMax(newIslandHandle + 1, mIslandStaticTouchCount.size())); + Island& newIsland = mIslands[newIslandHandle]; + + if (mIslandAwake.test(islandId)) + { + newIsland.mActiveIndex = mActiveIslands.size(); + mActiveIslands.pushBack(newIslandHandle); + mIslandAwake.growAndSet(newIslandHandle); //Separated island, so it should be awake + } + else + { + mIslandAwake.growAndReset(newIslandHandle); + } + + newIsland.mRootNode = dirtyNodeIndex; + mHopCounts[dirtyNodeIndex.index()] = 0; + mIslandIds[dirtyNodeIndex.index()] = newIslandHandle; + //newIsland.mTotalSize = mVisitedNodes.size(); + + mNodes[dirtyNodeIndex.index()].mPrevNode.setIndices(IG_INVALID_NODE); //First node so doesn't have a preceding node + mFastRoute[dirtyNodeIndex.index()].setIndices(IG_INVALID_NODE); + + size[0] = 0; size[1] = 0; + + size[dirtyNode.mType] = 1; + + for (PxU32 a = 1; a < mVisitedNodes.size(); ++a) + { + NodeIndex index = mVisitedNodes[a].mNodeIndex; + Node& thisNode = mNodes[index.index()]; + NodeIndex prevNodeIndex = mVisitedNodes[a - 1].mNodeIndex; + thisNode.mPrevNode = prevNodeIndex; + mNodes[prevNodeIndex.index()].mNextNode = index; + size[thisNode.mType]++; + mIslandIds[index.index()] = newIslandHandle; + mHopCounts[index.index()] = mVisitedNodes[a].mDepth; //How many hops to root + mFastRoute[index.index()] = mVisitedNodes[mVisitedNodes[a].mPrevIndex].mNodeIndex; + } + + newIsland.mSize[0] = size[0]; + newIsland.mSize[1] = size[1]; + //Last node in the island + NodeIndex lastIndex = mVisitedNodes[mVisitedNodes.size() - 1].mNodeIndex; + mNodes[lastIndex.index()].mNextNode.setIndices(IG_INVALID_NODE); + newIsland.mLastNode = lastIndex; + //newIsland.mStaticTouchCount = totalStaticTouchCount; + mIslandStaticTouchCount[newIslandHandle] = totalStaticTouchCount; + newIsland.mSize[0] = size[0]; + newIsland.mSize[1] = size[1]; + + PX_ASSERT(mNodes[newIsland.mLastNode.index()].mNextNode.index() == IG_INVALID_NODE); + + for (PxU32 j = 0; j < 2; ++j) + { + Ps::Array& splitEdges = mIslandSplitEdges[j]; + const PxU32 splitEdgeSize = splitEdges.size(); + if (splitEdgeSize) + { + splitEdges.pushBack(IG_INVALID_EDGE); //Push in a dummy invalid edge to complete the connectivity + mEdges[splitEdges[0]].mNextIslandEdge = splitEdges[1]; + for (PxU32 a = 1; a < splitEdgeSize; ++a) + { + EdgeIndex edgeIndex = splitEdges[a]; + Edge& edge = mEdges[edgeIndex]; + edge.mNextIslandEdge = splitEdges[a + 1]; + edge.mPrevIslandEdge = splitEdges[a - 1]; + } + + newIsland.mFirstEdge[j] = splitEdges[0]; + newIsland.mLastEdge[j] = splitEdges[splitEdgeSize - 1]; + newIsland.mEdgeCount[j] = splitEdgeSize; + } + } + + } + } + + } + + dirtyNode.clearDirty(); +#if IG_LIMIT_DIRTY_NODES + mDirtyMap.reset(dirtyIdx); +#endif +} + + + +#if IG_LIMIT_DIRTY_NODES + mLastMapIndex = lastMapIndex; + if (count < MaxCount) + mLastMapIndex = 0; +#else + mDirtyMap.clear(); +#endif + + //mDirtyNodes.forceSize_Unsafe(0); + } + + + { + PX_PROFILE_ZONE("Basic.clearDestroyedEdges", getContextId()); + //Now process the lost edges... + for (PxU32 a = 0; a < mDestroyedEdges.size(); ++a) + { + //Process these destroyed edges. Recompute island information. Update the islands and hop counters accordingly + EdgeIndex index = mDestroyedEdges[a]; + + Edge& edge = mEdges[index]; + if (edge.isPendingDestroyed()) + { + //if(edge.mFirstPartitionEdge) + PartitionEdge* pEdge = mFirstPartitionEdges ? (*mFirstPartitionEdges)[index] : NULL; + if (pEdge) + { + mDestroyedPartitionEdges->pushBack(pEdge); + (*mFirstPartitionEdges)[index] = NULL; //Force first partition edge to NULL to ensure we don't have a clash + } + if (edge.isActive()) + { + removeEdgeFromActivatingList(index); //TODO - can we remove this call? Can we handle this elsewhere, e.g. when destroying the nodes... + mActiveEdgeCount[edge.mEdgeType]--; + } + + edge = Edge(); //Reset edge + mActiveContactEdges.growAndReset(index); + } + } + + mDestroyedEdges.forceSize_Unsafe(0); + } + + { + PX_PROFILE_ZONE("Basic.clearDestroyedNodes", getContextId()); + + for (PxU32 a = 0; a < destroyedNodes.size(); ++a) + { + NodeIndex nodeIndex = destroyedNodes[a]; + IslandId islandId = mIslandIds[nodeIndex.index()]; + Node& node = mNodes[nodeIndex.index()]; + if (islandId != IG_INVALID_ISLAND) + { + Island& island = mIslands[islandId]; + + removeNodeFromIsland(island, nodeIndex); + + mIslandIds[nodeIndex.index()] = IG_INVALID_ISLAND; + + if ((island.mSize[0] + island.mSize[1]) == 0) + { + mIslandHandles.freeHandle(islandId); + if (island.mActiveIndex != IG_INVALID_ISLAND) + { + IslandId replaceId = mActiveIslands[mActiveIslands.size() - 1]; + Island& replaceIsland = mIslands[replaceId]; + replaceIsland.mActiveIndex = island.mActiveIndex; + mActiveIslands[island.mActiveIndex] = replaceId; + mActiveIslands.forceSize_Unsafe(mActiveIslands.size() - 1); + island.mActiveIndex = IG_INVALID_ISLAND; + //island.mStaticTouchCount -= node.mStaticTouchCount; //Remove the static touch count from the island + mIslandStaticTouchCount[islandId] -= node.mStaticTouchCount; + } + mIslandAwake.reset(islandId); + island.mLastNode.setIndices(IG_INVALID_NODE); + island.mRootNode.setIndices(IG_INVALID_NODE); + island.mActiveIndex = IG_INVALID_ISLAND; + } + } + + if (node.isKinematic()) + { + if (mActiveNodeIndex[nodeIndex.index()] != IG_INVALID_NODE) + { + //Remove from the active kinematics list... + markKinematicInactive(nodeIndex); + } + } + else + { + if (mActiveNodeIndex[nodeIndex.index()] != IG_INVALID_NODE) + { + markInactive(nodeIndex); + } + } + + node.reset(); + } + } + //Now we need to produce the list of active edges and nodes!!! + + //If we get here, we have a list of active islands. From this, we need to iterate over all active islands and establish if that island + //can, in fact, go to sleep. In order to become deactivated, all nodes in the island must be ready for sleeping... + + if (allowDeactivation) + { + PX_PROFILE_ZONE("Basic.deactivation", getContextId()); + for (PxU32 a = 0; a < mActiveIslands.size(); a++) + { + IslandId islandId = mActiveIslands[a]; + + mIslandAwake.reset(islandId); + } + + //Loop over the active kinematic nodes and tag all islands touched by active kinematics as awake + for (PxU32 a = mActiveKinematicNodes.size(); a > 0; --a) + { + NodeIndex kinematicIndex = mActiveKinematicNodes[a - 1]; + + Node& kinematicNode = mNodes[kinematicIndex.index()]; + + if (kinematicNode.isReadyForSleeping()) + { + if (permitKinematicDeactivation) + { + kinematicNode.clearActive(); + markKinematicInactive(kinematicIndex); + } + } + else //if(!kinematicNode.isReadyForSleeping()) + { + //KS - if kinematic is active, then wake up all islands the kinematic is touching + EdgeInstanceIndex edgeId = kinematicNode.mFirstEdgeIndex; + while (edgeId != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[edgeId]; + //Edge& edge = mEdges[edgeId/2]; + //Only wake up islands if a connection was present + //if(edge.isConnected()) + { + NodeIndex outNode = mEdgeNodeIndices[edgeId ^ 1]; + if (outNode.index() != IG_INVALID_NODE) + { + IslandId islandId = mIslandIds[outNode.index()]; + if (islandId != IG_INVALID_ISLAND) + { + mIslandAwake.set(islandId); + PX_ASSERT(mIslands[islandId].mActiveIndex != IG_INVALID_ISLAND); + } + } + } + edgeId = instance.mNextEdge; + } + } + } + + for (PxU32 a = mActiveIslands.size(); a > 0; --a) + { + IslandId islandId = mActiveIslands[a - 1]; + + Island& island = mIslands[islandId]; + + bool canDeactivate = !mIslandAwake.test(islandId); + mIslandAwake.set(islandId); + + //If it was touched by an active kinematic in the above loop, we can't deactivate it. + //Therefore, no point in testing the nodes in the island. They must remain awake + if (canDeactivate) + { + NodeIndex nodeId = island.mRootNode; + while (nodeId.index() != IG_INVALID_NODE) + { + Node& node = mNodes[nodeId.index()]; + if (!node.isReadyForSleeping()) + { + canDeactivate = false; + break; + } + nodeId = node.mNextNode; + } + if (canDeactivate) + { + //If all nodes in this island are ready for sleeping and there were no active + //kinematics interacting with the any bodies in the island, we can deactivate the island. + deactivateIsland(islandId); + } + } + } + } + + { + PX_PROFILE_ZONE("Basic.resetDirtyEdges", getContextId()); + for (PxU32 i = 0; i < Edge::eEDGE_TYPE_COUNT; ++i) + { + for (PxU32 a = 0; a < mDirtyEdges[i].size(); ++a) + { + Edge& edge = mEdges[mDirtyEdges[i][a]]; + edge.clearInDirtyList(); + } + mDirtyEdges[i].clear(); //All new edges processed + } + } + +} + +IslandId IslandSim::mergeIslands(IslandId island0, IslandId island1, NodeIndex node0, NodeIndex node1) +{ + Island& is0 = mIslands[island0]; + Island& is1 = mIslands[island1]; + + //We defer this process and do it later instead. That way, if we have some pathalogical + //case where multiple islands get merged repeatedly, we don't end up repeatedly remapping all the nodes in those islands + //to their new island. Instead, we just choose the largest island and remap the smaller island to that. + + PxU32 totalSize0 = is0.mSize[0] + is0.mSize[1]; + PxU32 totalSize1 = is1.mSize[0] + is1.mSize[1]; + if(totalSize0 > totalSize1) + { + mergeIslandsInternal(is0, is1, island0, island1, node0, node1); + mIslandAwake.reset(island1); + mIslandHandles.freeHandle(island1); + mFastRoute[node1.index()] = node0; + return island0; + } + else + { + mergeIslandsInternal(is1, is0, island1, island0, node1, node0); + mIslandAwake.reset(island0); + mIslandHandles.freeHandle(island0); + mFastRoute[node0.index()] = node1; + return island1; + } +} + +bool IslandSim::checkInternalConsistency() +{ + //Loop over islands, confirming that the island data is consistent... + //Really expensive. Turn off unless investigating some random issue... +#if 0 + for (PxU32 a = 0; a < mIslands.size(); ++a) + { + Island& island = mIslands[a]; + + PxU32 expectedSize = island.mSize[0] + island.mSize[1]; + bool metLastNode = expectedSize == 0; + + NodeIndex nodeId = island.mRootNode; + + while (nodeId.index() != IG_INVALID_NODE) + { + + PX_ASSERT(mIslandIds[nodeId.index()] == a); + + if (nodeId.index() == island.mLastNode.index()) + { + metLastNode = true; + PX_ASSERT(mNodes[nodeId.index()].mNextNode.index() == IG_INVALID_NODE); + } + + --expectedSize; + + nodeId = mNodes[nodeId.index()].mNextNode; + } + + PX_ASSERT(expectedSize == 0); + PX_ASSERT(metLastNode); + } +#endif + + return true; + +} + +void IslandSim::mergeIslandsInternal(Island& island0, Island& island1, IslandId islandId0, IslandId islandId1, NodeIndex nodeIndex0, NodeIndex nodeIndex1) +{ + PX_ASSERT((island0.mSize[0] + island0.mSize[1]) >= (island1.mSize[0] + island1.mSize[1])); //We only ever merge the smaller island to the larger island + //Stage 1 - we need to move all the nodes across to the new island ID (i.e. write all their new island indices, move them to the + //island and then also update their estimated hop counts to the root. As we don't want to do a full traversal at this point, + //instead, we estimate based on the route from the node to their previous root and then from that root to the new connection + //between the 2 islands. This is probably a very indirect route but it will be refined later. + + //In this case, island1 is subsumed by island0 + + //It takes mHopCounts[nodeIndex1] to get from node1 to its old root. It takes mHopCounts[nodeIndex0] to get from nodeIndex0 to the new root + //and it takes 1 extra hop to go from node1 to node0. Therefore, a sub-optimal route can be planned going via the old root node that should take + //mHopCounts[nodeIndex0] + mHopCounts[nodeIndex1] + 1 + mHopCounts[nodeIndex] to travel from any arbitrary node (nodeIndex) in island1 to the root + //of island2. + + + PxU32 extraPath = mHopCounts[nodeIndex0.index()] + mHopCounts[nodeIndex1.index()] + 1; + + NodeIndex islandNode = island1.mRootNode; + while(islandNode.index() != IG_INVALID_NODE) + { + mHopCounts[islandNode.index()] += extraPath; + mIslandIds[islandNode.index()] = islandId0; + + //mFastRoute[islandNode] = IG_INVALID_NODE; + + Node& node = mNodes[islandNode.index()]; + islandNode = node.mNextNode; + } + + //Now fill in the hop count for node1, which is directly connected to node0. + mHopCounts[nodeIndex1.index()] = mHopCounts[nodeIndex0.index()] + 1; + Node& lastNode = mNodes[island0.mLastNode.index()]; + Node& firstNode = mNodes[island1.mRootNode.index()]; + PX_ASSERT(lastNode.mNextNode.index() == IG_INVALID_NODE); + PX_ASSERT(firstNode.mPrevNode.index() == IG_INVALID_NODE); + PX_ASSERT(island1.mRootNode.index() != island0.mLastNode.index()); + + PX_ASSERT(mNodes[island0.mLastNode.index()].mNextNode.index() == IG_INVALID_NODE); + PX_ASSERT(mNodes[island1.mLastNode.index()].mNextNode.index() == IG_INVALID_NODE); + + PX_ASSERT(mIslandIds[island0.mLastNode.index()] == islandId0); + + + + lastNode.mNextNode = island1.mRootNode; + firstNode.mPrevNode = island0.mLastNode; + + + + island0.mLastNode = island1.mLastNode; + island0.mSize[0] += island1.mSize[0]; + island0.mSize[1] += island1.mSize[1]; + //island0.mStaticTouchCount += island1.mStaticTouchCount; + mIslandStaticTouchCount[islandId0] += mIslandStaticTouchCount[islandId1]; + + //Merge the edge list for the islands... + for(PxU32 a = 0; a < 2; ++a) + { + if(island0.mLastEdge[a] != IG_INVALID_EDGE) + { + PX_ASSERT(mEdges[island0.mLastEdge[a]].mNextIslandEdge == IG_INVALID_EDGE); + mEdges[island0.mLastEdge[a]].mNextIslandEdge = island1.mFirstEdge[a]; + } + else + { + PX_ASSERT(island0.mFirstEdge[a] == IG_INVALID_EDGE); + island0.mFirstEdge[a] = island1.mFirstEdge[a]; + } + if(island1.mFirstEdge[a] != IG_INVALID_EDGE) + { + PX_ASSERT(mEdges[island1.mFirstEdge[a]].mPrevIslandEdge == IG_INVALID_EDGE); + mEdges[island1.mFirstEdge[a]].mPrevIslandEdge = island0.mLastEdge[a]; + island0.mLastEdge[a] = island1.mLastEdge[a]; + } + + island0.mEdgeCount[a] += island1.mEdgeCount[a]; + island1.mFirstEdge[a] = IG_INVALID_EDGE; + island1.mLastEdge[a] = IG_INVALID_EDGE; + island1.mEdgeCount[a] = 0; + } + + + island1.mLastNode.setIndices(IG_INVALID_NODE); + island1.mRootNode.setIndices(IG_INVALID_NODE); + island1.mSize[0] = 0; + island1.mSize[1] = 0; + mIslandStaticTouchCount[islandId1] = 0; + //island1.mStaticTouchCount = 0; + + //Remove from active island list + if(island1.mActiveIndex != IG_INVALID_ISLAND) + { + markIslandInactive(islandId1); + } + +} + +void IslandSim::removeEdgeFromActivatingList(EdgeIndex index) +{ + Edge& edge = mEdges[index]; + + if (edge.mEdgeState & Edge::eACTIVATING) + { + for (PxU32 a = 0, count = mActivatedEdges[edge.mEdgeType].size(); a < count; a++) + { + if (mActivatedEdges[edge.mEdgeType][a] == index) + { + mActivatedEdges[edge.mEdgeType].replaceWithLast(a); + break; + } + } + + edge.mEdgeState &= (~Edge::eACTIVATING); + } + + + NodeIndex nodeIndex1 = mEdgeNodeIndices[index * 2]; + NodeIndex nodeIndex2 = mEdgeNodeIndices[index * 2 + 1]; + + if (nodeIndex1.isValid() && nodeIndex2.isValid()) + { + { + Node& node = mNodes[nodeIndex1.index()]; + node.mActiveRefCount--; + } + { + Node& node = mNodes[nodeIndex2.index()]; + node.mActiveRefCount--; + } + } + + if(edge.mEdgeType == Edge::eCONTACT_MANAGER) + mActiveContactEdges.reset(index); + +} + +void IslandSim::setKinematic(IG::NodeIndex nodeIndex) +{ + + Node& node = mNodes[nodeIndex.index()]; + + if(!node.isKinematic()) + { + + //Transition from dynamic to kinematic: + //(1) Remove this node from the island + //(2) Remove this node from the active node list + //(3) If active or referenced, add it to the active kinematic list + //(4) Tag the node as kinematic + //External code will re-filter interactions and lost touches will be reported + + IslandId islandId = mIslandIds[nodeIndex.index()]; + PX_ASSERT(islandId != IG_INVALID_ISLAND); + + Island& island = mIslands[islandId]; + + mIslandIds[nodeIndex.index()] = IG_INVALID_ISLAND; + + removeNodeFromIsland(island, nodeIndex); + + bool isActive = node.isActive(); + + if (isActive) + { + //Remove from active list... + markInactive(nodeIndex); + } + else if (node.isActivating()) + { + //Remove from activating list... + node.clearActivating(); + PX_ASSERT(mActivatingNodes[mActiveNodeIndex[nodeIndex.index()]].index() == nodeIndex.index()); + + NodeIndex replaceIndex = mActivatingNodes[mActivatingNodes.size() - 1]; + mActiveNodeIndex[replaceIndex.index()] = mActiveNodeIndex[nodeIndex.index()]; + mActivatingNodes[mActiveNodeIndex[nodeIndex.index()]] = replaceIndex; + mActivatingNodes.forceSize_Unsafe(mActivatingNodes.size() - 1); + mActiveNodeIndex[nodeIndex.index()] = IG_INVALID_NODE; + } + + node.setKinematicFlag(); + + node.clearActive(); + + if (/*isActive || */node.mActiveRefCount != 0) + { + //Add to active kinematic list... + PX_ASSERT(mActiveNodeIndex[nodeIndex.index()] == IG_INVALID_NODE); + + mActiveNodeIndex[nodeIndex.index()] = mActivatingNodes.size(); + mActivatingNodes.pushBack(nodeIndex); + node.setActivating(); + } + + PxU32 newSize = island.mSize[0] + island.mSize[1]; + + { + //This node was in an island with other bodies. We need to force an island recomputation in case the + //islands became broken due to losing this connection. Same rules as losing a contact, we just + //tag the nodes directly connected to the lost edge as "dirty" and force an island recomputation if + //it resulted in lost connections + EdgeInstanceIndex edgeId = node.mFirstEdgeIndex; + while(edgeId != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[edgeId]; + EdgeInstanceIndex nextId = instance.mNextEdge; + + PxU32 idx = edgeId/2; + IG::Edge& edge = mEdges[edgeId/2]; + + removeEdgeFromIsland(island, idx); + + removeConnectionInternal(idx); + removeConnectionFromGraph(idx); + + edge.clearInserted(); + + if (edge.isActive()) + { + removeEdgeFromActivatingList(idx); + edge.deactivateEdge(); + mActiveEdgeCount[edge.mEdgeType]--; + } + + if(!edge.isPendingDestroyed()) + { + if(!edge.isInDirtyList()) + { + PX_ASSERT(!contains(mDirtyEdges[edge.mEdgeType], idx)); + mDirtyEdges[edge.mEdgeType].pushBack(idx); + edge.markInDirtyList(); + } + } + else + { + edge.setReportOnlyDestroy(); + } + + edgeId = nextId; + } + } + + if(newSize == 0) + { + //This node was in an island by itself, so no need to mess with any connections + for(PxU32 a = 0; a < Edge::eEDGE_TYPE_COUNT; ++a) + { + island.mFirstEdge[a] = island.mLastEdge[a] = IG_INVALID_EDGE; + island.mEdgeCount[a] = 0; + mIslandStaticTouchCount[islandId] = 0; + //island.mStaticTouchCount = 0; + } + + if(island.mActiveIndex != IG_INVALID_ISLAND) + { + markIslandInactive(islandId); + } + + mIslandAwake.reset(islandId); + mIslandHandles.freeHandle(islandId); + } + } +} + +void IslandSim::setDynamic(IG::NodeIndex nodeIndex) +{ + //(1) Remove all edges involving this node from all islands they may be in + //(2) Mark all edges as "new" edges - let island gen re-process them! + //(3) Remove this node from the active kinematic list + //(4) Add this node to the active dynamic list (if it is active) + //(5) Mark node as dynamic + + + Node& node = mNodes[nodeIndex.index()]; + + if(node.isKinematic()) + { + //EdgeInstanceIndex edgeIndex = node.mFirstEdgeIndex; + + EdgeInstanceIndex edgeId = node.mFirstEdgeIndex; + while(edgeId != IG_INVALID_EDGE) + { + EdgeInstance& instance = mEdgeInstances[edgeId]; + EdgeInstanceIndex nextId = instance.mNextEdge; + + NodeIndex otherNode = mEdgeNodeIndices[edgeId^1]; + + PxU32 idx = edgeId/2; + IG::Edge& edge = mEdges[edgeId/2]; + + if(!otherNode.isStaticBody()) + { + IslandId islandId = mIslandIds[otherNode.index()]; + if(islandId != IG_INVALID_ISLAND) + removeEdgeFromIsland(mIslands[islandId], idx); + } + + removeConnectionInternal(idx); + removeConnectionFromGraph(idx); + + edge.clearInserted(); + if (edge.isActive()) + { + edge.deactivateEdge(); + removeEdgeFromActivatingList(idx); + mActiveEdgeCount[edge.mEdgeType]--; + } + + if(!edge.isPendingDestroyed()) + { + if(!edge.isInDirtyList()) + { + PX_ASSERT(!contains(mDirtyEdges[edge.mEdgeType], idx)); + mDirtyEdges[edge.mEdgeType].pushBack(idx); + edge.markInDirtyList(); + } + } + else + { + edge.setReportOnlyDestroy(); + } + + edgeId = nextId; + } + + + if(!node.isActivating() && mActiveNodeIndex[nodeIndex.index()] != IG_INVALID_NODE) + { + //Remove from active kinematic list, add to active dynamic list + PxU32 oldRefCount = node.mActiveRefCount; + node.mActiveRefCount = 0; + markKinematicInactive(nodeIndex); + node.mActiveRefCount = oldRefCount; + } + + node.clearKinematicFlag(); + + + + //Create an island for this node. If there are any edges affecting this node, they will have been marked as + //"new" and will be processed next island update. + { + IslandId islandHandle = mIslandHandles.getHandle(); + + if(islandHandle == mIslands.capacity()) + { + const PxU32 newCapacity = 2*mIslands.capacity()+1; + mIslands.reserve(newCapacity); + mIslandAwake.resize(newCapacity); + mIslandStaticTouchCount.resize(newCapacity); + } + mIslandAwake.reset(islandHandle); + mIslands.resize(PxMax(islandHandle+1, mIslands.size())); + mIslandStaticTouchCount.resize(PxMax(islandHandle + 1, mIslands.size())); + Island& island = mIslands[islandHandle]; + island.mLastNode = island.mRootNode = nodeIndex; + PX_ASSERT(mNodes[nodeIndex.index()].mNextNode.index() == IG_INVALID_NODE); + island.mSize[node.mType] = 1; + mIslandIds[nodeIndex.index()] = islandHandle; + mIslandStaticTouchCount[islandHandle] = 0; + + + if(node.isActive()) + { + node.clearActive(); + + activateNode(nodeIndex); + } + } + } +} + +} +} diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp new file mode 100644 index 000000000..476d3bc71 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp @@ -0,0 +1,102 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxsMaterialCombiner.h" +#include "PsMathUtils.h" +#include "CmPhysXCommon.h" +#include "PsFoundation.h" + +namespace physx +{ + + +PxsMaterialCombiner::PxsMaterialCombiner(PxReal staticFrictionScaling, PxReal dynamicFrictionScaling) +: mStaticFrictionScaling(staticFrictionScaling), mDynamicFrictionScaling(dynamicFrictionScaling) +{} + + +PxReal PxsMaterialCombiner::combineRestitution(const PxsMaterialData& mat0, const PxsMaterialData& mat1) +{ + /*return combineScalars(mat0.restitution, mat1.restitution, PxMax(mat0.restitutionCombineMode, mat1.restitutionCombineMode));*/ + return combineScalars(mat0.restitution, mat1.restitution, PxMax(mat0.getRestitutionCombineMode(), mat1.getRestitutionCombineMode())); +} + +PxsMaterialCombiner::PxsCombinedMaterial PxsMaterialCombiner::combineIsotropicFriction(const PxsMaterialData& mat0, const PxsMaterialData& mat1) +{ + PxsCombinedMaterial dest; + + dest.flags = (mat0.flags | mat1.flags); //& (PxMaterialFlag::eDISABLE_STRONG_FRICTION|PxMaterialFlag::eDISABLE_FRICTION); //eventually set DisStrongFric flag, lower all others. + + if (!(dest.flags & PxMaterialFlag::eDISABLE_FRICTION)) + { + const PxI32 fictionCombineMode = PxMax(mat0.getFrictionCombineMode(), mat1.getFrictionCombineMode()); + PxReal dynFriction = 0.f; + PxReal staFriction = 0.f; + + + switch (fictionCombineMode) + { + case PxCombineMode::eAVERAGE: + dynFriction = 0.5f * (mat0.dynamicFriction + mat1.dynamicFriction); + staFriction = 0.5f * (mat0.staticFriction + mat1.staticFriction); + break; + case PxCombineMode::eMIN: + dynFriction = PxMin(mat0.dynamicFriction, mat1.dynamicFriction); + staFriction = PxMin(mat0.staticFriction, mat1.staticFriction); + break; + case PxCombineMode::eMULTIPLY: + dynFriction = (mat0.dynamicFriction * mat1.dynamicFriction); + staFriction = (mat0.staticFriction * mat1.staticFriction); + break; + case PxCombineMode::eMAX: + dynFriction = PxMax(mat0.dynamicFriction, mat1.dynamicFriction); + staFriction = PxMax(mat0.staticFriction, mat1.staticFriction); + break; + } + + dynFriction*=mDynamicFrictionScaling; + staFriction*=mStaticFrictionScaling; + //isotropic case + const PxReal fDynFriction = PxMax(dynFriction, 0.f); + + const PxReal fStaFriction = physx::intrinsics::fsel(staFriction - fDynFriction, staFriction, fDynFriction); + dest.dynFriction = fDynFriction; + dest.staFriction = fStaFriction; + } + else + { + dest.flags |= PxMaterialFlag::eDISABLE_STRONG_FRICTION; + dest.staFriction = 0.0f; + dest.dynFriction = 0.0f; + } + + return dest; +} +} diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp new file mode 100644 index 000000000..4dbd52df2 --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp @@ -0,0 +1,999 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxsContext.h" +#include "CmFlushPool.h" +#include "PxsSimpleIslandManager.h" +#include "common/PxProfileZone.h" + +#if PX_SUPPORT_GPU_PHYSX +#include "PxPhysXGpu.h" +#include "task/PxGpuDispatcher.h" +#endif + +#include "PxsContactManagerState.h" + +#include "PxsNphaseImplementationContext.h" +#include "PxvGeometry.h" +#include "PxvDynamics.h" +#include "PxvGlobals.h" + +#include "PxcNpContactPrepShared.h" + +using namespace physx; +using namespace physx::shdfnd; + + +class PxsCMUpdateTask : public Cm::Task +{ +public: + + static const PxU32 BATCH_SIZE = 128; + + PxsCMUpdateTask(PxsContext* context, PxReal dt, PxsContactManager** cmArray, PxsContactManagerOutput* cmOutputs, Gu::Cache* caches, PxU32 cmCount, PxContactModifyCallback* callback) : + Cm::Task (context->getContextId()), + mCmArray (cmArray), + mCmOutputs (cmOutputs), + mCaches (caches), + mCmCount (cmCount), + mDt (dt), + mContext (context), + mCallback (callback) + { + } + + virtual void release(); + + /*PX_FORCE_INLINE void insert(PxsContactManager* cm) + { + PX_ASSERT(mCmCount < BATCH_SIZE); + mCmArray[mCmCount++]=cm; + }*/ + +protected: + //PxsContactManager* mCmArray[BATCH_SIZE]; + PxsContactManager** mCmArray; + PxsContactManagerOutput* mCmOutputs; + Gu::Cache* mCaches; + PxU32 mCmCount; + PxReal mDt; //we could probably retrieve from context to save space? + PxsContext* mContext; + PxContactModifyCallback* mCallback; +}; + +void PxsCMUpdateTask::release() +{ + // We used to do Task::release(); here before fixing DE1106 (xbox pure virtual crash) + // Release in turn causes the dependent tasks to start running + // The problem was that between the time release was called and by the time we got to the destructor + // The task chain would get all the way to scene finalization code which would reset the allocation pool + // And a new task would get allocated at the same address, then we would invoke the destructor on that freshly created task + // This could potentially cause any number of other problems, it is suprising that it only manifested itself + // as a pure virtual crash + PxBaseTask* saveContinuation = mCont; + this->~PxsCMUpdateTask(); + if (saveContinuation) + saveContinuation->removeReference(); +} + +class PxsCMDiscreteUpdateTask : public PxsCMUpdateTask +{ +public: + PxsCMDiscreteUpdateTask(PxsContext* context, PxReal dt, PxsContactManager** cms, PxsContactManagerOutput* cmOutputs, Gu::Cache* caches, PxU32 nbCms, + PxContactModifyCallback* callback): + PxsCMUpdateTask(context, dt, cms, cmOutputs, caches, nbCms, callback) + {} + + virtual ~PxsCMDiscreteUpdateTask() + {} + + void runModifiableContactManagers(PxU32* modifiableIndices, PxU32 nbModifiableManagers, PxcNpThreadContext& threadContext, PxU32& foundPatchCount_, PxU32& lostPatchCount_, + PxU32& maxPatches_) + { + PX_ASSERT(nbModifiableManagers != 0); + + PxU32 foundPatchCount = foundPatchCount_; + PxU32 lostPatchCount = lostPatchCount_; + PxU32 maxPatches = maxPatches_; + + Cm::BitMap& localPatchChangedMap = threadContext.getLocalPatchChangeMap(); + + class PxcContactSet: public PxContactSet + { + public: + PxcContactSet(PxU32 count, PxModifiableContact *contacts) + { + mContacts = contacts; + mCount = count; + } + PxModifiableContact* getContacts() { return mContacts; } + PxU32 getCount() { return mCount; } + + }; + + + + if(mCallback) + { + PX_ALLOCA(mModifiablePairArray, PxContactModifyPair, nbModifiableManagers); + + + PxsTransformCache& transformCache = mContext->getTransformCache(); + + for(PxU32 i = 0; i < nbModifiableManagers; ++i) + { + PxU32 index = modifiableIndices[i]; + PxsContactManager& cm = *mCmArray[index]; + + PxsContactManagerOutput& output = mCmOutputs[index]; + + PxU32 count = output.nbContacts; + + if(count) + { + PxContactModifyPair& p = mModifiablePairArray[i]; + PxcNpWorkUnit &unit = cm.getWorkUnit(); + + p.shape[0] = gPxvOffsetTable.convertPxsShape2Px(unit.shapeCore0); + p.shape[1] = gPxvOffsetTable.convertPxsShape2Px(unit.shapeCore1); + + p.actor[0] = unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY0 ? gPxvOffsetTable.convertPxsRigidCore2PxRigidBody(unit.rigidCore0) + : gPxvOffsetTable.convertPxsRigidCore2PxRigidStatic(unit.rigidCore0); + + p.actor[1] = unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1 ? gPxvOffsetTable.convertPxsRigidCore2PxRigidBody(unit.rigidCore1) + : gPxvOffsetTable.convertPxsRigidCore2PxRigidStatic(unit.rigidCore1); + + p.transform[0] = transformCache.getTransformCache(unit.mTransformCache0).transform; + p.transform[1] = transformCache.getTransformCache(unit.mTransformCache1).transform; + + PxModifiableContact* contacts = reinterpret_cast(output.contactPoints); + static_cast(p.contacts) = PxcContactSet(count, contacts); + + PxReal mi0 = unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY0 ? static_cast(unit.rigidCore0)->maxContactImpulse : PX_MAX_F32; + PxReal mi1 = unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1 ? static_cast(unit.rigidCore1)->maxContactImpulse : PX_MAX_F32; + PxReal maxImpulse = PxMin(mi0, mi1); + for (PxU32 j = 0; j < count; j++) + contacts[j].maxImpulse = maxImpulse; + + #if PX_ENABLE_SIM_STATS + PxU8 gt0 = Ps::to8(unit.geomType0), gt1 = Ps::to8(unit.geomType1); + threadContext.mModifiedContactPairs[PxMin(gt0, gt1)][PxMax(gt0, gt1)]++; + #endif + } + } + + mCallback->onContactModify(mModifiablePairArray, nbModifiableManagers); + } + + for(PxU32 i = 0; i < nbModifiableManagers; ++i) + { + PxU32 index = modifiableIndices[i]; + PxsContactManager& cm = *mCmArray[index]; + + //Loop through the contacts in the contact stream and update contact count! + + PxU32 numContacts = 0; + PxcNpWorkUnit& unit = cm.getWorkUnit(); + PxsContactManagerOutput& output = mCmOutputs[index]; + + + PxU32 numPatches = output.nbPatches; + + if (output.nbContacts) + { + //PxU8* compressedContacts = cm.getWorkUnit().compressedContacts; + //PxModifyContactHeader* header = reinterpret_cast(compressedContacts); + PxContactPatch* patches = reinterpret_cast(output.contactPatches); + PxModifiableContact* points = reinterpret_cast(output.contactPoints); + + if (patches->internalFlags & PxContactPatch::eREGENERATE_PATCHES) + { + //Some data was modified that must trigger patch re-generation... + for (PxU8 k = 0; k < numPatches; ++k) + { + PxU32 startIndex = patches[k].startContactIndex; + + patches[k].normal = points[startIndex].normal; + patches[k].dynamicFriction = points[startIndex].dynamicFriction; + patches[k].staticFriction = points[startIndex].staticFriction; + patches[k].restitution = points[startIndex].restitution; + + for (PxU32 j = 1; j < patches[k].nbContacts; ++j) + { + if (points[startIndex].normal.dot(points[j + startIndex].normal) < PXC_SAME_NORMAL + && points[startIndex].maxImpulse > 0.f) //TODO - this needs extending for material indices but we don't support modifying those yet + { + //The points are now in a separate friction patch... + for (PxU32 c = numPatches - 1; c > k; --c) + { + patches[c + 1] = patches[c]; + } + numPatches++; + patches[k + 1].materialFlags = patches[k].materialFlags; + patches[k + 1].internalFlags = patches[k].internalFlags; + patches[k + 1].startContactIndex = Ps::to8(j + startIndex); + patches[k + 1].nbContacts = Ps::to8(patches[k].nbContacts - j); + //Fill in patch information now that final patches are available + patches[k].nbContacts = PxU8(j); + break; + } + } + } + } + + if (output.prevPatches < numPatches) + { + foundPatchCount++; + localPatchChangedMap.growAndSet(unit.index); + } + + maxPatches = PxMax(maxPatches, PxU32(numPatches)); + + output.nbPatches = PxU8(numPatches); + + for (PxU32 a = 0; a < output.nbContacts; ++a) + { + numContacts += points[a].maxImpulse != 0.f; + } + } + + if(output.nbPatches < output.prevPatches) + { + lostPatchCount++; + //Trigger a lost patch event...required to let the solver + localPatchChangedMap.growAndSet(unit.index); + } + + if(!numContacts) + { + //KS - we still need to retain the patch count from the previous frame to detect found/lost events... + PxcNpWorkUnitClearFrictionCachedState(unit); + output.nbPatches = 0; + output.nbContacts = 0; + + if(output.prevPatches) + { + lostPatchCount++; + //Trigger a lost patch event...required to let the solver + localPatchChangedMap.growAndSet(unit.index); + } + + continue; + } + + if(threadContext.mContactStreamPool) + { + //We need to allocate a new structure inside the contact stream pool + + PxU32 patchSize = output.nbPatches * sizeof(PxContactPatch); + PxU32 contactSize = output.nbContacts * sizeof(PxExtendedContact); + + /*PxI32 increment = (PxI32)(patchSize + contactSize); + PxI32 index = Ps::atomicAdd(&mContactStreamPool->mSharedContactIndex, increment) - increment; + PxU8* address = mContactStreamPool->mContactStream + index;*/ + bool isOverflown = false; + + PxI32 contactIncrement = PxI32(contactSize); + PxI32 contactIndex = Ps::atomicAdd(&threadContext.mContactStreamPool->mSharedDataIndex, contactIncrement); + + if (threadContext.mContactStreamPool->isOverflown()) + { + PX_WARN_ONCE("Contact buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + + PxU8* contactAddress = threadContext.mContactStreamPool->mDataStream + threadContext.mContactStreamPool->mDataStreamSize - contactIndex; + + PxI32 patchIncrement = PxI32(patchSize); + PxI32 patchIndex = Ps::atomicAdd(&threadContext.mPatchStreamPool->mSharedDataIndex, patchIncrement); + + if (threadContext.mPatchStreamPool->isOverflown()) + { + PX_WARN_ONCE("Patch buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + + PxU8* patchAddress = threadContext.mPatchStreamPool->mDataStream + threadContext.mPatchStreamPool->mDataStreamSize - patchIndex; + + + PxU32 internalFlags = reinterpret_cast(output.contactPatches)->internalFlags; + + PxI32 increment2 = PxI32(output.nbContacts * sizeof(PxReal)); + PxI32 index2 = Ps::atomicAdd(&threadContext.mForceAndIndiceStreamPool->mSharedDataIndex, increment2); + + if (threadContext.mForceAndIndiceStreamPool->isOverflown()) + { + PX_WARN_ONCE("Force buffer overflow detected, please increase its size in the scene desc!\n"); + isOverflown = true; + } + + if (isOverflown) + { + output.contactPoints = NULL; + output.contactPatches = NULL; + output.contactForces = NULL; + + output.nbContacts = output.nbPatches = 0; + } + else + { + output.contactForces = reinterpret_cast(threadContext.mForceAndIndiceStreamPool->mDataStream + threadContext.mForceAndIndiceStreamPool->mDataStreamSize - index2); + + PxMemZero(output.contactForces, sizeof(PxReal) * output.nbContacts); + + PxExtendedContact* contacts = reinterpret_cast(contactAddress); + PxMemCopy(patchAddress, output.contactPatches, sizeof(PxContactPatch) * output.nbPatches); + + PxContactPatch* newPatches = reinterpret_cast(patchAddress); + + internalFlags |= PxContactPatch::eCOMPRESSED_MODIFIED_CONTACT; + + for(PxU32 a = 0; a < output.nbPatches; ++a) + { + newPatches[a].internalFlags = PxU8(internalFlags); + } + + //KS - only the first patch will have mass modification properties set. For the GPU solver, this must be propagated to the remaining patches + for(PxU32 a = 1; a < output.nbPatches; ++a) + { + newPatches[a].mMassModification = newPatches->mMassModification; + } + + PxModifiableContact* sourceContacts = reinterpret_cast(output.contactPoints); + + for(PxU32 a = 0; a < output.nbContacts; ++a) + { + PxExtendedContact& contact = contacts[a]; + PxModifiableContact& srcContact = sourceContacts[a]; + contact.contact = srcContact.contact; + contact.separation = srcContact.separation; + contact.targetVelocity = srcContact.targetVelocity; + contact.maxImpulse = srcContact.maxImpulse; + } + + output.contactPatches = patchAddress; + output.contactPoints = reinterpret_cast(contacts); + } + } + } + + foundPatchCount_ = foundPatchCount; + lostPatchCount_ = lostPatchCount; + maxPatches_ = maxPatches; + } + + + template < void (*NarrowPhase)(PxcNpThreadContext&, const PxcNpWorkUnit&, Gu::Cache&, PxsContactManagerOutput&)> + void processCms(PxcNpThreadContext* threadContext) + { + // PT: use local variables to avoid reading class members N times, if possible + const PxU32 nb = mCmCount; + PxsContactManager** PX_RESTRICT cmArray = mCmArray; + + PxU32 lostPatchCount = 0, foundPatchCount = 0; + + PxU32 maxPatches = threadContext->mMaxPatches; + + PxU32 newTouchCMCount = 0, lostTouchCMCount = 0; + Cm::BitMap& localChangeTouchCM = threadContext->getLocalChangeTouch(); + Cm::BitMap& localPatchChangedMap = threadContext->getLocalPatchChangeMap(); + + PX_ALLOCA(modifiableIndices, PxU32, nb); + PxU32 modifiableCount = 0; + + for(PxU32 i=0;igetWorkUnit().shapeCore0); + Ps::prefetchLine(cmArray[prefetch1]->getWorkUnit().shapeCore1); + Ps::prefetchLine(&threadContext->mTransformCache->getTransformCache(cmArray[prefetch1]->getWorkUnit().mTransformCache0)); + Ps::prefetchLine(&threadContext->mTransformCache->getTransformCache(cmArray[prefetch1]->getWorkUnit().mTransformCache1)); + + PxsContactManager* cm = cmArray[i]; + + if(cm) + { + PxsContactManagerOutput& output = mCmOutputs[i]; + PxcNpWorkUnit& unit = cm->getWorkUnit(); + + output.prevPatches = output.nbPatches; + + + PxU8 oldStatusFlag = output.statusFlag; + + PxU8 oldTouch = Ps::to8(oldStatusFlag & PxsContactManagerStatusFlag::eHAS_TOUCH); + + Gu::Cache& cache = mCaches[i]; + + NarrowPhase(*threadContext, unit, cache, output); + + PxU16 newTouch = Ps::to8(output.statusFlag & PxsContactManagerStatusFlag::eHAS_TOUCH); + + + bool modifiable = output.nbPatches != 0 && unit.flags & PxcNpWorkUnitFlag::eMODIFIABLE_CONTACT; + + if(modifiable) + { + modifiableIndices[modifiableCount++] = i; + } + else + { + maxPatches = PxMax(maxPatches, Ps::to32(output.nbPatches)); + + if(output.prevPatches != output.nbPatches) + { + localPatchChangedMap.growAndSet(cmArray[i]->getIndex()); + if(output.prevPatches < output.nbPatches) + foundPatchCount++; + else + lostPatchCount++; + } + } + + if (newTouch ^ oldTouch) + { + cm->getWorkUnit().statusFlags = PxU8(output.statusFlag | (unit.statusFlags & PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH)); //KS - todo - remove the need to access the work unit at all! + localChangeTouchCM.growAndSet(cmArray[i]->getIndex()); + if(newTouch) + newTouchCMCount++; + else + lostTouchCMCount++; + } + else if (!(oldStatusFlag&PxsContactManagerStatusFlag::eTOUCH_KNOWN)) + { + cm->getWorkUnit().statusFlags = PxU8(output.statusFlag | (unit.statusFlags & PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH)); //KS - todo - remove the need to access the work unit at all! + } + } + } + + if(modifiableCount) + { + runModifiableContactManagers(modifiableIndices, modifiableCount, *threadContext, foundPatchCount, lostPatchCount, maxPatches); + } + + + threadContext->addLocalNewTouchCount(newTouchCMCount); + threadContext->addLocalLostTouchCount(lostTouchCMCount); + + threadContext->addLocalFoundPatchCount(foundPatchCount); + threadContext->addLocalLostPatchCount(lostPatchCount); + + threadContext->mMaxPatches = maxPatches; + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("Sim.narrowPhase", mContext->getContextId()); + + PxcNpThreadContext* PX_RESTRICT threadContext = mContext->getNpThreadContext(); + + threadContext->mDt = mDt; + + const bool pcm = mContext->getPCM(); + threadContext->mPCM = pcm; + threadContext->mCreateAveragePoint = mContext->getCreateAveragePoint(); + threadContext->mContactCache = mContext->getContactCacheFlag(); + threadContext->mTransformCache = &mContext->getTransformCache(); + threadContext->mContactDistance = mContext->getContactDistance(); + + if(pcm) + { + processCms(threadContext); + } + else + { + processCms(threadContext); + } + + mContext->putNpThreadContext(threadContext); + } + + virtual const char* getName() const + { + return "PxsContext.contactManagerDiscreteUpdate"; + } +}; + +void PxsNphaseImplementationContext::processContactManager(PxReal dt, PxsContactManagerOutput* cmOutputs, PxBaseTask* continuation) +{ + //Iterate all active contact managers + mContext.mTaskPool.lock(); + const PxU32 nbCmsToProcess = mNarrowPhasePairs.mContactManagerMapping.size(); + + for(PxU32 a = 0; a < nbCmsToProcess;) + { + void* ptr = mContext.mTaskPool.allocateNotThreadSafe(sizeof(PxsCMDiscreteUpdateTask)); + PxU32 nbToProcess = PxMin(nbCmsToProcess - a, PxsCMUpdateTask::BATCH_SIZE); + PxsCMDiscreteUpdateTask* task = PX_PLACEMENT_NEW(ptr, PxsCMDiscreteUpdateTask)(&mContext, dt, mNarrowPhasePairs.mContactManagerMapping.begin() + a, + cmOutputs + a, mNarrowPhasePairs.mCaches.begin() + a, nbToProcess, mModifyCallback); + + a += nbToProcess; + + task->setContinuation(continuation); + task->removeReference(); + } + mContext.mTaskPool.unlock(); +} + +void PxsNphaseImplementationContext::processContactManagerSecondPass(PxReal dt, PxBaseTask* continuation) +{ + //Iterate all active contact managers + mContext.mTaskPool.lock(); + + const PxU32 nbCmsToProcess = mNewNarrowPhasePairs.mContactManagerMapping.size(); + + for(PxU32 a = 0; a < nbCmsToProcess;) + { + void* ptr = mContext.mTaskPool.allocateNotThreadSafe(sizeof(PxsCMDiscreteUpdateTask)); + PxU32 nbToProcess = PxMin(nbCmsToProcess - a, PxsCMUpdateTask::BATCH_SIZE); + PxsCMDiscreteUpdateTask* task = PX_PLACEMENT_NEW(ptr, PxsCMDiscreteUpdateTask)(&mContext, dt, mNewNarrowPhasePairs.mContactManagerMapping.begin() + a, + mNewNarrowPhasePairs.mOutputContactManagers.begin() + a, mNewNarrowPhasePairs.mCaches.begin() + a, nbToProcess, + mModifyCallback); + + a += nbToProcess; + + task->setContinuation(continuation); + task->removeReference(); + } + mContext.mTaskPool.unlock(); +} + +void PxsNphaseImplementationContext::updateContactManager(PxReal dt, bool /*hasBoundsArrayChanged*/, bool /*hasContactDistanceChanged*/, PxBaseTask* continuation, PxBaseTask* firstPassNpContinuation) +{ + PX_PROFILE_ZONE("Sim.queueNarrowPhase", mContext.mContextID); + + firstPassNpContinuation->removeReference(); + + mContext.clearManagerTouchEvents(); + +#if PX_ENABLE_SIM_STATS + mContext.mSimStats.mNbDiscreteContactPairsTotal = 0; + mContext.mSimStats.mNbDiscreteContactPairsWithCacheHits = 0; + mContext.mSimStats.mNbDiscreteContactPairsWithContacts = 0; +#endif + + + //KS - temporarily put this here. TODO - move somewhere better + mContext.mTotalCompressedCacheSize = 0; + mContext.mMaxPatches = 0; + + processContactManager(dt, mNarrowPhasePairs.mOutputContactManagers.begin(), continuation); + +} + +void PxsNphaseImplementationContext::secondPassUpdateContactManager(PxReal dt, PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.queueNarrowPhase", mContext.mContextID); + + processContactManagerSecondPass(dt, continuation); +} + +PxsNphaseImplementationContext* PxsNphaseImplementationContext::create(PxsContext& context, IG::IslandSim* islandSim) +{ + PxsNphaseImplementationContext* npImplContext = reinterpret_cast( + PX_ALLOC(sizeof(PxsNphaseImplementationContext), "PxsNphaseImplementationContext")); + + if (npImplContext) + { + new(npImplContext) PxsNphaseImplementationContext(context, islandSim); + } + + return npImplContext; +} + +void PxsNphaseImplementationContext::destroy() +{ + this->~PxsNphaseImplementationContext(); + PX_FREE(this); +} + +void PxsNphaseImplementationContext::registerContactManagers(PxsContactManager** cms, PxU32 nbContactManagers, PxU32 maxContactManagerId) +{ + PX_UNUSED(maxContactManagerId); + for (PxU32 a = 0; a < nbContactManagers; ++a) + { + registerContactManager(cms[a], 0, 0); + } +} + +void PxsNphaseImplementationContext::registerContactManager(PxsContactManager* cm, PxI32 touching, PxU32 patchCount) +{ + PxcNpWorkUnit& workUnit = cm->getWorkUnit(); + PxsContactManagerOutput output; + + PxU8 geomType0 = PxU8(workUnit.geomType0); + PxU8 geomType1 = PxU8(workUnit.geomType1); + + Gu::Cache cache; + + mContext.createCache(cache, cm, geomType0, geomType1); + + PxMemZero(&output, sizeof(output)); + output.nbPatches = Ps::to8(patchCount); + + if(workUnit.flags & PxcNpWorkUnitFlag::eOUTPUT_CONSTRAINTS) + output.statusFlag |= PxsContactManagerStatusFlag::eREQUEST_CONSTRAINTS; + + if (touching > 0) + { + output.statusFlag |= PxsContactManagerStatusFlag::eHAS_TOUCH; + } + else if (touching < 0) + { + output.statusFlag |= PxsContactManagerStatusFlag::eHAS_NO_TOUCH; + } + + output.statusFlag |= PxsContactManagerStatusFlag::eDIRTY_MANAGER; + + if (cm->getWorkUnit().statusFlags & PxcNpWorkUnitStatusFlag::eHAS_TOUCH) + cm->getWorkUnit().statusFlags |= PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH; + + mNewNarrowPhasePairs.mOutputContactManagers.pushBack(output); + mNewNarrowPhasePairs.mCaches.pushBack(cache); + mNewNarrowPhasePairs.mContactManagerMapping.pushBack(cm); + PxU32 newSz = mNewNarrowPhasePairs.mOutputContactManagers.size(); + cm->getWorkUnit().mNpIndex = mNewNarrowPhasePairs.computeId(newSz - 1) | PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK; +} + +void PxsNphaseImplementationContext::removeContactManagersFallback(PxsContactManagerOutput* cmOutputs) +{ + if (mRemovedContactManagers.size()) + { + Ps::sort(mRemovedContactManagers.begin(), mRemovedContactManagers.size(), Ps::Greater()); + + for (PxU32 a = 0; a < mRemovedContactManagers.size(); ++a) + { +#if PX_DEBUG + if (a > 0) + PX_ASSERT(mRemovedContactManagers[a] < mRemovedContactManagers[a - 1]); +#endif + unregisterContactManagerInternal(mRemovedContactManagers[a], mNarrowPhasePairs, cmOutputs); + } + + mRemovedContactManagers.forceSize_Unsafe(0); + } +} + +void PxsNphaseImplementationContext::unregisterContactManager(PxsContactManager* cm) +{ + PxcNpWorkUnit& unit = cm->getWorkUnit(); + PxU32 index = unit.mNpIndex; + PX_ASSERT(index != 0xFFffFFff); + + if (!(index & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK)) + { + unregisterContactManagerInternal(index, mNarrowPhasePairs, mNarrowPhasePairs.mOutputContactManagers.begin()); + mNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNarrowPhasePairs.mOutputContactManagers.size()-1); + } + else + { + //KS - the index in the "new" list will be the index + unregisterContactManagerInternal(index, mNewNarrowPhasePairs, mNewNarrowPhasePairs.mOutputContactManagers.begin()); + mNewNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNewNarrowPhasePairs.mOutputContactManagers.size()-1); + } +} + +void PxsNphaseImplementationContext::refreshContactManager(PxsContactManager* cm) +{ + PxcNpWorkUnit& unit = cm->getWorkUnit(); + PxU32 index = unit.mNpIndex; + PX_ASSERT(index != 0xFFffFFff); + PxsContactManagerOutput output; + if (!(index & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK)) + { + output = mNarrowPhasePairs.mOutputContactManagers[PxsContactManagerBase::computeIndexFromId(index)]; + unregisterContactManagerInternal(index, mNarrowPhasePairs, mNarrowPhasePairs.mOutputContactManagers.begin()); + mNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNarrowPhasePairs.mOutputContactManagers.size()-1); + } + else + { + output = mNewNarrowPhasePairs.mOutputContactManagers[PxsContactManagerBase::computeIndexFromId(index & (~PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK))]; + //KS - the index in the "new" list will be the index + unregisterContactManagerInternal(index, mNewNarrowPhasePairs, mNewNarrowPhasePairs.mOutputContactManagers.begin()); + mNewNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNewNarrowPhasePairs.mOutputContactManagers.size()-1); + } + PxI32 touching = 0; + if(output.statusFlag & PxsContactManagerStatusFlag::eHAS_TOUCH) + touching = 1; + else if (output.statusFlag & PxsContactManagerStatusFlag::eHAS_NO_TOUCH) + touching = -1; + registerContactManager(cm, touching, output.nbPatches); +} + +void PxsNphaseImplementationContext::unregisterContactManagerFallback(PxsContactManager* cm, PxsContactManagerOutput* /*cmOutputs*/) +{ + PxcNpWorkUnit& unit = cm->getWorkUnit(); + PxU32 index = unit.mNpIndex; + PX_ASSERT(index != 0xFFffFFff); + + if (!(index & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK)) + { + mRemovedContactManagers.pushBack(index); + } + else + { + //KS - the index in the "new" list will be the index + unregisterContactManagerInternal(index, mNewNarrowPhasePairs, mNewNarrowPhasePairs.mOutputContactManagers.begin()); + mNewNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNewNarrowPhasePairs.mOutputContactManagers.size()-1); + } +} + +void PxsNphaseImplementationContext::refreshContactManagerFallback(PxsContactManager* cm, PxsContactManagerOutput* cmOutputs) +{ + PxcNpWorkUnit& unit = cm->getWorkUnit(); + PxU32 index = unit.mNpIndex; + PX_ASSERT(index != 0xFFffFFff); + + PxsContactManagerOutput output; + if (!(index & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK)) + { + output = cmOutputs[PxsContactManagerBase::computeIndexFromId(index)]; + //unregisterContactManagerInternal(index, mNarrowPhasePairs, cmOutputs); + unregisterContactManagerFallback(cm, cmOutputs); + } + else + { + //KS - the index in the "new" list will be the index + output = mNewNarrowPhasePairs.mOutputContactManagers[PxsContactManagerBase::computeIndexFromId(index & (~PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK))]; + unregisterContactManagerInternal(index, mNewNarrowPhasePairs, mNewNarrowPhasePairs.mOutputContactManagers.begin()); + mNewNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(mNewNarrowPhasePairs.mOutputContactManagers.size()-1); + } + + PxI32 touching = 0; + if(output.statusFlag & PxsContactManagerStatusFlag::eHAS_TOUCH) + { + touching = 1; + cm->getWorkUnit().statusFlags |= PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH; + } + else if (output.statusFlag & PxsContactManagerStatusFlag::eHAS_NO_TOUCH) + touching = -1; + registerContactManager(cm, touching, output.nbPatches); + + +} + +void PxsNphaseImplementationContext::appendContactManagers() +{ + //Copy new pairs to end of old pairs. Clear new flag, update npIndex on CM and clear the new pair buffer + const PxU32 existingSize = mNarrowPhasePairs.mContactManagerMapping.size(); + const PxU32 nbToAdd = mNewNarrowPhasePairs.mContactManagerMapping.size(); + const PxU32 newSize =existingSize + nbToAdd; + + if(newSize > mNarrowPhasePairs.mContactManagerMapping.capacity()) + { + PxU32 newSz = PxMax(256u, PxMax(mNarrowPhasePairs.mContactManagerMapping.capacity()*2, newSize)); + + mNarrowPhasePairs.mContactManagerMapping.reserve(newSz); + mNarrowPhasePairs.mOutputContactManagers.reserve(newSz); + mNarrowPhasePairs.mCaches.reserve(newSz); + } + + mNarrowPhasePairs.mContactManagerMapping.forceSize_Unsafe(newSize); + mNarrowPhasePairs.mOutputContactManagers.forceSize_Unsafe(newSize); + mNarrowPhasePairs.mCaches.forceSize_Unsafe(newSize); + + PxMemCopy(mNarrowPhasePairs.mContactManagerMapping.begin() + existingSize, mNewNarrowPhasePairs.mContactManagerMapping.begin(), sizeof(PxsContactManager*)*nbToAdd); + PxMemCopy(mNarrowPhasePairs.mOutputContactManagers.begin() + existingSize, mNewNarrowPhasePairs.mOutputContactManagers.begin(), sizeof(PxsContactManagerOutput)*nbToAdd); + PxMemCopy(mNarrowPhasePairs.mCaches.begin() + existingSize, mNewNarrowPhasePairs.mCaches.begin(), sizeof(Gu::Cache)*nbToAdd); + + PxU32* edgeNodeIndices = mIslandSim->getEdgeNodeIndexPtr(); + + for(PxU32 a = 0; a < mNewNarrowPhasePairs.mContactManagerMapping.size(); ++a) + { + PxsContactManager* cm = mNewNarrowPhasePairs.mContactManagerMapping[a]; + PxcNpWorkUnit& unit = cm->getWorkUnit(); + unit.mNpIndex = mNarrowPhasePairs.computeId(existingSize + a); + + if(unit.statusFlags & PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH) + { + unit.statusFlags &= (~PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH); + if(!(unit.flags & PxcNpWorkUnitFlag::eDISABLE_RESPONSE)) + { + PartitionEdge* partitionEdge = mIslandSim->getFirstPartitionEdge(unit.mEdgeIndex); + + while(partitionEdge) + { + edgeNodeIndices[partitionEdge->mUniqueIndex] = unit.mNpIndex; + partitionEdge = partitionEdge->mNextPatch; + } + } + } + } + + mNewNarrowPhasePairs.clear(); +} + +void PxsNphaseImplementationContext::appendContactManagersFallback(PxsContactManagerOutput* cmOutputs) +{ + PX_PROFILE_ZONE("PxsNphaseImplementationContext.appendContactManagersFallback", mContext.mContextID); + + //Copy new pairs to end of old pairs. Clear new flag, update npIndex on CM and clear the new pair buffer + const PxU32 existingSize = mNarrowPhasePairs.mContactManagerMapping.size(); + const PxU32 nbToAdd = mNewNarrowPhasePairs.mContactManagerMapping.size(); + const PxU32 newSize =existingSize + nbToAdd; + + if(newSize > mNarrowPhasePairs.mContactManagerMapping.capacity()) + { + PxU32 newSz = PxMax(mNarrowPhasePairs.mContactManagerMapping.capacity()*2, newSize); + + mNarrowPhasePairs.mContactManagerMapping.reserve(newSz); + mNarrowPhasePairs.mCaches.reserve(newSz); + /*mNarrowPhasePairs.mLostFoundPairsCms.reserve(2 * newSz); + mNarrowPhasePairs.mLostFoundPairsOutputData.reserve(2*newSz);*/ + } + + mNarrowPhasePairs.mContactManagerMapping.forceSize_Unsafe(newSize); + mNarrowPhasePairs.mCaches.forceSize_Unsafe(newSize); + + PxMemCopy(mNarrowPhasePairs.mContactManagerMapping.begin() + existingSize, mNewNarrowPhasePairs.mContactManagerMapping.begin(), sizeof(PxsContactManager*)*nbToAdd); + PxMemCopy(cmOutputs + existingSize, mNewNarrowPhasePairs.mOutputContactManagers.begin(), sizeof(PxsContactManagerOutput)*nbToAdd); + PxMemCopy(mNarrowPhasePairs.mCaches.begin() + existingSize, mNewNarrowPhasePairs.mCaches.begin(), sizeof(Gu::Cache)*nbToAdd); + + PxU32* edgeNodeIndices = mIslandSim->getEdgeNodeIndexPtr(); + + for(PxU32 a = 0; a < mNewNarrowPhasePairs.mContactManagerMapping.size(); ++a) + { + PxsContactManager* cm = mNewNarrowPhasePairs.mContactManagerMapping[a]; + PxcNpWorkUnit& unit = cm->getWorkUnit(); + unit.mNpIndex = mNarrowPhasePairs.computeId(existingSize + a); + + if(unit.statusFlags & PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH) + { + unit.statusFlags &= (~PxcNpWorkUnitStatusFlag::eREFRESHED_WITH_TOUCH); + if(!(unit.flags & PxcNpWorkUnitFlag::eDISABLE_RESPONSE)) + { + PartitionEdge* partitionEdge = mIslandSim->getFirstPartitionEdge(unit.mEdgeIndex); + + while(partitionEdge) + { + edgeNodeIndices[partitionEdge->mUniqueIndex] = unit.mNpIndex; + partitionEdge = partitionEdge->mNextPatch; + } + } + } + } + + //const PxU32 existingLostFoundPairs = mNarrowPhasePairs.mLostFoundPairsCms.size(); + //const PxU32 newLostFoundPairs = mNewNarrowPhasePairs.mLostFoundPairsCms.size(); + //const PxU32 newLostFoundSize = existingLostFoundPairs + newLostFoundPairs; + + //if (mNarrowPhasePairs.mLostFoundPairsCms.capacity() < newLostFoundSize) + //{ + // const PxU32 newSz = PxMax(newLostFoundSize, 2 * mNarrowPhasePairs.mLostFoundPairsCms.capacity()); + // mNarrowPhasePairs.mLostFoundPairsCms.reserve(newSz); + // mNarrowPhasePairs.mLostFoundPairsOutputData.reserve(newSz); + //} + + //mNarrowPhasePairs.mLostFoundPairsCms.forceSize_Unsafe(newLostFoundSize); + //mNarrowPhasePairs.mLostFoundPairsOutputData.forceSize_Unsafe(newLostFoundSize); + + //PxMemCopy(mNarrowPhasePairs.mLostFoundPairsCms.begin() + existingLostFoundPairs, mNewNarrowPhasePairs.mLostFoundPairsCms.begin(), sizeof(PxsContactManager*)* newLostFoundPairs); + //PxMemCopy(mNarrowPhasePairs.mLostFoundPairsOutputData.begin() + existingLostFoundPairs, mNewNarrowPhasePairs.mLostFoundPairsOutputData.begin(), sizeof(PxsContactManagerOutput) * newLostFoundPairs); + + mNewNarrowPhasePairs.clear(); +} + + + +void PxsNphaseImplementationContext::unregisterContactManagerInternal(PxU32 npIndex, PxsContactManagers& managers, PxsContactManagerOutput* cmOutputs) +{ + //TODO - remove this element from the list. + PxU32 index = PxsContactManagerBase::computeIndexFromId((npIndex & (~PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK))); + + //Now we replace-with-last and remove the elements... + + PxU32 replaceIndex = managers.mContactManagerMapping.size()-1; + + PxsContactManager* replaceManager = managers.mContactManagerMapping[replaceIndex]; + + mContext.destroyCache(managers.mCaches[index]); + + managers.mContactManagerMapping[index] = replaceManager; + managers.mCaches[index] = managers.mCaches[replaceIndex]; + cmOutputs[index] = cmOutputs[replaceIndex]; + + PxU32* edgeNodeIndices = mIslandSim->getEdgeNodeIndexPtr(); + + PxcNpWorkUnit& replaceUnit = replaceManager->getWorkUnit(); + replaceUnit.mNpIndex = npIndex; + if(replaceUnit.statusFlags & PxcNpWorkUnitStatusFlag::eHAS_TOUCH) + { + if(!(replaceUnit.flags & PxcNpWorkUnitFlag::eDISABLE_RESPONSE)) + { + PartitionEdge* partitionEdge = mIslandSim->getFirstPartitionEdge(replaceUnit.mEdgeIndex); + while(partitionEdge) + { + edgeNodeIndices[partitionEdge->mUniqueIndex] = replaceUnit.mNpIndex; + partitionEdge = partitionEdge->mNextPatch; + } + } + } + + managers.mContactManagerMapping.forceSize_Unsafe(replaceIndex); + managers.mCaches.forceSize_Unsafe(replaceIndex); +} + +PxsContactManagerOutput& PxsNphaseImplementationContext::getNewContactManagerOutput(PxU32 npId) +{ + PX_ASSERT(npId & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK); + return this->mNewNarrowPhasePairs.mOutputContactManagers[PxsContactManagerBase::computeIndexFromId(npId & (~PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK))]; +} + +void PxsNphaseImplementationContext::registerShape(const PxsShapeCore& shapeCore) +{ + PX_UNUSED(shapeCore); +} + +void PxsNphaseImplementationContext::updateShapeMaterial(const PxsShapeCore& shapeCore) +{ + PX_UNUSED(shapeCore); +} + +void PxsNphaseImplementationContext::updateShapeContactOffset(const PxsShapeCore& shapeCore) +{ + PX_UNUSED(shapeCore); +} + +void PxsNphaseImplementationContext::unregisterShape(const PxsShapeCore& shapeCore) +{ + PX_UNUSED(shapeCore); +} + +void PxsNphaseImplementationContext::registerMaterial(const PxsMaterialCore& materialCore) +{ + PX_UNUSED(materialCore); +} + +void PxsNphaseImplementationContext::updateMaterial(const PxsMaterialCore& materialCore) +{ + PX_UNUSED(materialCore); +} + +void PxsNphaseImplementationContext::unregisterMaterial(const PxsMaterialCore& materialCore) +{ + PX_UNUSED(materialCore); +} + +PxsContactManagerOutputIterator PxsNphaseImplementationContext::getContactManagerOutputs() +{ + PxU32 offsets[1] = {0}; + return PxsContactManagerOutputIterator(offsets, 1, this->mNarrowPhasePairs.mOutputContactManagers.begin()); +} + + +PxvNphaseImplementationContextUsableAsFallback* physx::createNphaseImplementationContext(PxsContext& context, IG::IslandSim* islandSim) +{ + return PxsNphaseImplementationContext::create(context, islandSim); +} + diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp new file mode 100644 index 000000000..728e6910b --- /dev/null +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp @@ -0,0 +1,377 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "PxsSimpleIslandManager.h" +#include "PsSort.h" +#include "PxsContactManager.h" +#include "CmTask.h" +#include "DyVArticulation.h" + + +#define IG_SANITY_CHECKS 0 + +namespace physx +{ +namespace IG +{ + ThirdPassTask::ThirdPassTask(PxU64 contextID, SimpleIslandManager& islandManager, IslandSim& islandSim) : Cm::Task(contextID), mIslandManager(islandManager), mIslandSim(islandSim) + { + } + + PostThirdPassTask::PostThirdPassTask(PxU64 contextID, SimpleIslandManager& islandManager) : Cm::Task(contextID), mIslandManager(islandManager) + { + } + + SimpleIslandManager::SimpleIslandManager(bool useEnhancedDeterminism, PxU64 contextID) : + mDestroyedNodes (PX_DEBUG_EXP("mDestroyedNodes")), + mDestroyedEdges (PX_DEBUG_EXP("mDestroyedEdges")), + mFirstPartitionEdges (PX_DEBUG_EXP("mFirstPartitionEdges")), + mDestroyedPartitionEdges (PX_DEBUG_EXP("IslandSim::mDestroyedPartitionEdges")), + mIslandManager (&mFirstPartitionEdges, mEdgeNodeIndices, &mDestroyedPartitionEdges, contextID), + mSpeculativeIslandManager (NULL, mEdgeNodeIndices, NULL, contextID), + mSpeculativeThirdPassTask (contextID, *this, mSpeculativeIslandManager), + mAccurateThirdPassTask (contextID, *this, mIslandManager), + mPostThirdPassTask (contextID, *this), + mContextID (contextID) +{ + mFirstPartitionEdges.resize(1024); + mMaxDirtyNodesPerFrame = useEnhancedDeterminism ? 0xFFFFFFFF : 1000u; +} + +SimpleIslandManager::~SimpleIslandManager() +{ +} + +NodeIndex SimpleIslandManager::addRigidBody(PxsRigidBody* body, bool isKinematic, bool isActive) +{ + PxU32 handle = mNodeHandles.getHandle(); + NodeIndex nodeIndex(handle); + mIslandManager.addRigidBody(body, isKinematic, isActive, nodeIndex); + mSpeculativeIslandManager.addRigidBody(body, isKinematic, isActive, nodeIndex); + return nodeIndex; +} + +void SimpleIslandManager::removeNode(const NodeIndex index) +{ + PX_ASSERT(mNodeHandles.isValidHandle(index.index())); + mDestroyedNodes.pushBack(index); +} + +NodeIndex SimpleIslandManager::addArticulation(Sc::ArticulationSim* articulation, Dy::ArticulationV* llArtic, bool isActive) +{ + PxU32 handle = mNodeHandles.getHandle(); + NodeIndex nodeIndex(handle); + mIslandManager.addArticulation(articulation, llArtic, isActive, nodeIndex); + mSpeculativeIslandManager.addArticulation(articulation, llArtic, isActive, nodeIndex); + return nodeIndex; +} + +EdgeIndex SimpleIslandManager::addContactManager(PxsContactManager* manager, NodeIndex nodeHandle1, NodeIndex nodeHandle2, Sc::Interaction* interaction) +{ + EdgeIndex handle = mEdgeHandles.getHandle(); + + PxU32 nodeIds = 2 * handle; + if (mEdgeNodeIndices.size() == nodeIds) + { + PX_PROFILE_ZONE("ReserveEdges", getContextId()); + const PxU32 newSize = nodeIds + 2048; + mEdgeNodeIndices.resize(newSize); + mConstraintOrCm.resize(newSize); + mInteractions.resize(newSize); + } + + mEdgeNodeIndices[nodeIds] = nodeHandle1; + mEdgeNodeIndices[nodeIds+1] = nodeHandle2; + mConstraintOrCm[handle] = manager; + mInteractions[handle] = interaction; + + mSpeculativeIslandManager.addContactManager(manager, nodeHandle1, nodeHandle2, handle); + + if (manager) + manager->getWorkUnit().mEdgeIndex = handle; + + if(mConnectedMap.size() == handle) + { + mConnectedMap.resize(2 * (handle + 1)); + } + if (mFirstPartitionEdges.capacity() == handle) + { + mFirstPartitionEdges.resize(2 * (handle + 1)); + } + mConnectedMap.reset(handle); + return handle; +} + +EdgeIndex SimpleIslandManager::addConstraint(Dy::Constraint* constraint, NodeIndex nodeHandle1, NodeIndex nodeHandle2, Sc::Interaction* interaction) +{ + EdgeIndex handle = mEdgeHandles.getHandle(); + + PxU32 nodeIds = 2 * handle; + if (mEdgeNodeIndices.size() == nodeIds) + { + mEdgeNodeIndices.resize(2 * (mEdgeNodeIndices.size() + 2)); + mConstraintOrCm.resize(2 * (handle + 1)); + mInteractions.resize(2 * (handle + 1)); + } + + mEdgeNodeIndices[nodeIds] = nodeHandle1; + mEdgeNodeIndices[nodeIds + 1] = nodeHandle2; + + mConstraintOrCm[handle] = constraint; + + mInteractions[handle] = interaction; + + mIslandManager.addConstraint(constraint, nodeHandle1, nodeHandle2, handle); + mSpeculativeIslandManager.addConstraint(constraint, nodeHandle1, nodeHandle2, handle); + if(mConnectedMap.size() == handle) + { + mConnectedMap.resize(2*(mConnectedMap.size()+1)); + } + + if (mFirstPartitionEdges.capacity() == handle) + { + mFirstPartitionEdges.resize(2 * (mFirstPartitionEdges.capacity() + 1)); + } + mConnectedMap.set(handle); + return handle; +} + +void SimpleIslandManager::activateNode(NodeIndex index) +{ + mIslandManager.activateNode(index); + mSpeculativeIslandManager.activateNode(index); +} + +void SimpleIslandManager::deactivateNode(NodeIndex index) +{ + mIslandManager.deactivateNode(index); + mSpeculativeIslandManager.deactivateNode(index); +} + +void SimpleIslandManager::putNodeToSleep(NodeIndex index) +{ + mIslandManager.putNodeToSleep(index); + mSpeculativeIslandManager.putNodeToSleep(index); +} + +void SimpleIslandManager::removeConnection(EdgeIndex edgeIndex) +{ + if(edgeIndex == IG_INVALID_EDGE) + return; + mDestroyedEdges.pushBack(edgeIndex); + mSpeculativeIslandManager.removeConnection(edgeIndex); + if(mConnectedMap.test(edgeIndex)) + { + mIslandManager.removeConnection(edgeIndex); + mConnectedMap.reset(edgeIndex); + } + + mConstraintOrCm[edgeIndex] = NULL; + mInteractions[edgeIndex] = NULL; +} + +void SimpleIslandManager::firstPassIslandGen() +{ + PX_PROFILE_ZONE("Basic.firstPassIslandGen", getContextId()); + mSpeculativeIslandManager.clearDeactivations(); + mSpeculativeIslandManager.wakeIslands(); + mSpeculativeIslandManager.processNewEdges(); + mSpeculativeIslandManager.removeDestroyedEdges(); + mSpeculativeIslandManager.processLostEdges(mDestroyedNodes, false, false, mMaxDirtyNodesPerFrame); +} + +void SimpleIslandManager::additionalSpeculativeActivation() +{ + mSpeculativeIslandManager.wakeIslands2(); +} + +void SimpleIslandManager::secondPassIslandGen() +{ + PX_PROFILE_ZONE("Basic.secondPassIslandGen", getContextId()); + + mIslandManager.wakeIslands(); + mIslandManager.processNewEdges(); + + mIslandManager.removeDestroyedEdges(); + mIslandManager.processLostEdges(mDestroyedNodes, false, false, mMaxDirtyNodesPerFrame); + + for(PxU32 a = 0; a < mDestroyedNodes.size(); ++a) + { + mNodeHandles.freeHandle(mDestroyedNodes[a].index()); + } + mDestroyedNodes.clear(); + //mDestroyedEdges.clear(); +} + +bool SimpleIslandManager::validateDeactivations() const +{ + //This method sanity checks the deactivations produced by third-pass island gen. Specifically, it ensures that any bodies that + //the speculative IG wants to deactivate are also candidates for deactivation in the accurate island gen. In practice, both should be the case. If this fails, something went wrong... + + const NodeIndex* const nodeIndices = mSpeculativeIslandManager.getNodesToDeactivate(Node::eRIGID_BODY_TYPE); + const PxU32 nbNodesToDeactivate = mSpeculativeIslandManager.getNbNodesToDeactivate(Node::eRIGID_BODY_TYPE); + + for(PxU32 i = 0; i < nbNodesToDeactivate; ++i) + { + //Node is active in accurate sim => mismatch between accurate and inaccurate sim! + const Node& node = mIslandManager.getNode(nodeIndices[i]); + const Node& speculativeNode = mSpeculativeIslandManager.getNode(nodeIndices[i]); + //KS - we need to verify that the bodies in the "deactivating" list are still candidates for deactivation. There are cases where they may not no longer be candidates, e.g. if the application + //put bodies to sleep and activated them + if(node.isActive() && !speculativeNode.isActive()) + return false; + } + return true; +} + +void ThirdPassTask::runInternal() +{ + PX_PROFILE_ZONE("Basic.thirdPassIslandGen", mIslandSim.getContextId()); + mIslandSim.removeDestroyedEdges(); + mIslandSim.processLostEdges(mIslandManager.mDestroyedNodes, true, true, mIslandManager.mMaxDirtyNodesPerFrame); +} + +void PostThirdPassTask::runInternal() +{ + for (PxU32 a = 0; a < mIslandManager.mDestroyedNodes.size(); ++a) + { + mIslandManager.mNodeHandles.freeHandle(mIslandManager.mDestroyedNodes[a].index()); + } + mIslandManager.mDestroyedNodes.clear(); + + for (PxU32 a = 0; a < mIslandManager.mDestroyedEdges.size(); ++a) + { + mIslandManager.mEdgeHandles.freeHandle(mIslandManager.mDestroyedEdges[a]); + } + mIslandManager.mDestroyedEdges.clear(); + + PX_ASSERT(mIslandManager.validateDeactivations()); +} + +void SimpleIslandManager::thirdPassIslandGen(PxBaseTask* continuation) +{ + + mIslandManager.clearDeactivations(); + + mPostThirdPassTask.setContinuation(continuation); + + mSpeculativeThirdPassTask.setContinuation(&mPostThirdPassTask); + mAccurateThirdPassTask.setContinuation(&mPostThirdPassTask); + + mSpeculativeThirdPassTask.removeReference(); + mAccurateThirdPassTask.removeReference(); + + mPostThirdPassTask.removeReference(); + + //PX_PROFILE_ZONE("Basic.thirdPassIslandGen", getContextId()); + //mSpeculativeIslandManager.removeDestroyedEdges(); + //mSpeculativeIslandManager.processLostEdges(mDestroyedNodes, true, true); + + //mIslandManager.removeDestroyedEdges(); + //mIslandManager.processLostEdges(mDestroyedNodes, true, true); + + +} + +bool SimpleIslandManager::checkInternalConsistency() +{ + return mIslandManager.checkInternalConsistency() && mSpeculativeIslandManager.checkInternalConsistency(); +} + +void SimpleIslandManager::clearDestroyedEdges() +{ + mDestroyedPartitionEdges.forceSize_Unsafe(0); +} + +void SimpleIslandManager::setEdgeConnected(EdgeIndex edgeIndex) +{ + if(!mConnectedMap.test(edgeIndex)) + { + mIslandManager.addContactManager(reinterpret_cast(mConstraintOrCm[edgeIndex]), mEdgeNodeIndices[edgeIndex * 2], mEdgeNodeIndices[edgeIndex * 2 + 1], edgeIndex); + mConnectedMap.set(edgeIndex); + } +} + +bool SimpleIslandManager::getIsEdgeConnected(EdgeIndex edgeIndex) +{ + return !!mConnectedMap.test(edgeIndex); +} + +void SimpleIslandManager::deactivateEdge(const EdgeIndex edgeIndex) +{ + if (mFirstPartitionEdges[edgeIndex]) + { + mDestroyedPartitionEdges.pushBack(mFirstPartitionEdges[edgeIndex]); + mFirstPartitionEdges[edgeIndex] = NULL; + } +} + +void SimpleIslandManager::setEdgeDisconnected(EdgeIndex edgeIndex) +{ + if(mConnectedMap.test(edgeIndex)) + { + //PX_ASSERT(!mIslandManager.getEdge(edgeIndex).isInDirtyList()); + mIslandManager.removeConnection(edgeIndex); + mConnectedMap.reset(edgeIndex); + } +} + +void SimpleIslandManager::setEdgeRigidCM(const EdgeIndex edgeIndex, PxsContactManager* cm) +{ + mConstraintOrCm[edgeIndex] = cm; + cm->getWorkUnit().mEdgeIndex = edgeIndex; +} + +void SimpleIslandManager::clearEdgeRigidCM(const EdgeIndex edgeIndex) +{ + mConstraintOrCm[edgeIndex] = NULL; + if (mFirstPartitionEdges[edgeIndex]) + { + //this is the partition edges created/updated by the gpu solver + mDestroyedPartitionEdges.pushBack(mFirstPartitionEdges[edgeIndex]); + mFirstPartitionEdges[edgeIndex] = NULL; + } +} + +void SimpleIslandManager::setKinematic(IG::NodeIndex nodeIndex) +{ + mIslandManager.setKinematic(nodeIndex); + mSpeculativeIslandManager.setKinematic(nodeIndex); +} + +void SimpleIslandManager::setDynamic(IG::NodeIndex nodeIndex) +{ + mIslandManager.setDynamic(nodeIndex); + mSpeculativeIslandManager.setDynamic(nodeIndex); +} + +} +} + diff --git a/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h b/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h new file mode 100644 index 000000000..3c54c0f3d --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h @@ -0,0 +1,610 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_AABBMANAGER_H +#define BP_AABBMANAGER_H + + #include "PxvConfig.h" + #include "CmPhysXCommon.h" + #include "BpBroadPhaseUpdate.h" + #include "GuGeometryUnion.h" + #include "CmBitMap.h" + #include "CmTask.h" + #include "PsAllocator.h" + #include "GuBounds.h" + #include "PsHashMap.h" + #include "CmRadixSortBuffered.h" + #include "PsFoundation.h" + #include "BpAABBManagerTasks.h" + #include "PsHashSet.h" + #include "PxFiltering.h" + #include "PsSList.h" + + namespace physx + { + class PxcScratchAllocator; + struct PxBroadPhaseType; + + namespace Cm + { + class RenderOutput; + class EventProfiler; + class FlushPool; + } + + namespace Bp + { + typedef PxU32 BoundsIndex; + typedef PxU32 AggregateHandle; // PT: currently an index in mAggregates array + typedef PxU32 ActorHandle; + + struct BroadPhasePair; + + struct ElementType + { + enum Enum + { + eSHAPE = 0, + eTRIGGER, + + eCOUNT + }; + }; + PX_COMPILE_TIME_ASSERT(ElementType::eCOUNT <= 4); // 2 bits reserved for type + + /** + \brief Changes to the configuration of overlap pairs are reported as void* pairs. + \note Each void* in the pair corresponds to the void* passed to AABBManager::createVolume. + @see AABBManager::createVolume, AABBManager::getCreatedOverlaps, AABBManager::getDestroyedOverlaps + */ + struct AABBOverlap + { + PX_FORCE_INLINE AABBOverlap() {} + PX_FORCE_INLINE AABBOverlap(void* userData0, void* userData1/*, ActorHandle pairHandle*/) : mUserData0(userData0), mUserData1(userData1)/*, mPairHandle(pairHandle)*/ {} + + void* mUserData0; + void* mUserData1; + /* union + { + ActorHandle mPairHandle; //For created pairs, this is the index into the pair in the pair manager + void* mUserData; //For deleted pairs, this is the user data written by the application to the pair + };*/ + void* mPairUserData; //For deleted pairs, this is the user data written by the application to the pair + }; + + struct BpCacheData : public Ps::SListEntry + { + Ps::Array mCreatedPairs[2]; + Ps::Array mDeletedPairs[2]; + + void reset() + { + mCreatedPairs[0].resizeUninitialized(0); + mCreatedPairs[1].resizeUninitialized(0); + mDeletedPairs[0].resizeUninitialized(0); + mDeletedPairs[1].resizeUninitialized(0); + } + }; + + class BoundsArray : public Ps::UserAllocated + { + PX_NOCOPY(BoundsArray) + + public: + BoundsArray(Ps::VirtualAllocator& allocator) : mBounds(allocator) + { + } + + PX_FORCE_INLINE void initEntry(PxU32 index) + { + index++; // PT: always pretend we need one more entry, to make sure reading the last used entry will be SIMD-safe. + const PxU32 oldCapacity = mBounds.capacity(); + if(index>=oldCapacity) + { + const PxU32 newCapacity = Ps::nextPowerOfTwo(index); + mBounds.reserve(newCapacity); + mBounds.forceSize_Unsafe(newCapacity); + } + } + + PX_FORCE_INLINE void updateBounds(const PxTransform& transform, const Gu::GeometryUnion& geom, PxU32 index) + { + Gu::computeBounds(mBounds[index], geom.getGeometry(), transform, 0.0f, NULL, 1.0f); + mHasAnythingChanged = true; + } + + PX_FORCE_INLINE const PxBounds3& getBounds(PxU32 index) const + { + return mBounds[index]; + } + + PX_FORCE_INLINE void setBounds(const PxBounds3& bounds, PxU32 index) + { + // PX_CHECK_AND_RETURN(bounds.isValid() && !bounds.isEmpty(), "BoundsArray::setBounds - illegal bounds\n"); + mBounds[index] = bounds; + mHasAnythingChanged = true; + } + + PX_FORCE_INLINE const PxBounds3* begin() const + { + return mBounds.begin(); + } + + PX_FORCE_INLINE PxBounds3* begin() + { + return mBounds.begin(); + } + + PX_FORCE_INLINE Ps::Array& getBounds() + { + return mBounds; + } + + PX_FORCE_INLINE PxU32 getCapacity() const + { + return mBounds.size(); + } + + void shiftOrigin(const PxVec3& shift) + { + // we shift some potential NaNs here because we don't know what's active, but should be harmless + for(PxU32 i=0;i mBounds; + bool mHasAnythingChanged; + }; + + struct VolumeData + { + PX_FORCE_INLINE void reset() + { + mAggregate = PX_INVALID_U32; + mUserData = NULL; + } + + PX_FORCE_INLINE void setSingleActor() { mAggregate = PX_INVALID_U32; } + PX_FORCE_INLINE bool isSingleActor() const { return mAggregate == PX_INVALID_U32; } + + PX_FORCE_INLINE void setUserData(void* userData) + { + // PX_ASSERT(!(reinterpret_cast(userData) & 3)); + mUserData = userData; + } + + PX_FORCE_INLINE void* getUserData() const + { + return reinterpret_cast(reinterpret_cast(mUserData)& (~size_t(3))); + } + + PX_FORCE_INLINE void setVolumeType(ElementType::Enum volumeType) + { + PX_ASSERT(volumeType < 2); + mUserData = reinterpret_cast(reinterpret_cast(getUserData()) | static_cast(volumeType)); + } + + PX_FORCE_INLINE ElementType::Enum getVolumeType() const + { + return ElementType::Enum(reinterpret_cast(mUserData) & 3); + } + + PX_FORCE_INLINE void setAggregate(AggregateHandle handle) + { + PX_ASSERT(handle!=PX_INVALID_U32); + mAggregate = (handle<<1)|1; + } + PX_FORCE_INLINE bool isAggregate() const { return !isSingleActor() && ((mAggregate&1)!=0); } + + PX_FORCE_INLINE void setAggregated(AggregateHandle handle) + { + PX_ASSERT(handle!=PX_INVALID_U32); + mAggregate = (handle<<1)|0; + } + + PX_FORCE_INLINE bool isAggregated() const + { + return !isSingleActor() && ((mAggregate&1)==0); + } + + PX_FORCE_INLINE AggregateHandle getAggregateOwner() const { return mAggregate>>1; } + PX_FORCE_INLINE AggregateHandle getAggregate() const { return mAggregate>>1; } + + private: + void* mUserData; + // PT: TODO: consider moving this to a separate array, which wouldn't be allocated at all for people not using aggregates. + // PT: current encoding: + // aggregate == PX_INVALID_U32 => single actor + // aggregate != PX_INVALID_U32 => aggregate index<<1|LSB. LSB==1 for aggregates, LSB==0 for aggregated actors. + AggregateHandle mAggregate; + }; + + // PT: TODO: revisit this..... + class Aggregate; + class PersistentPairs; + class PersistentActorAggregatePair; + class PersistentAggregateAggregatePair; + class PersistentSelfCollisionPairs; + struct AggPair + { + PX_FORCE_INLINE AggPair() {} + PX_FORCE_INLINE AggPair(ShapeHandle index0, ShapeHandle index1) : mIndex0(index0), mIndex1(index1) {} + ShapeHandle mIndex0; + ShapeHandle mIndex1; + + PX_FORCE_INLINE bool operator==(const AggPair& p) const + { + return (p.mIndex0 == mIndex0) && (p.mIndex1 == mIndex1); + } + }; + typedef Ps::CoalescedHashMap AggPairMap; + + // PT: TODO: isn't there a generic pair structure somewhere? refactor with AggPair anyway + struct Pair + { + PX_FORCE_INLINE Pair(PxU32 id0, PxU32 id1) : mID0(id0), mID1(id1) {} + PX_FORCE_INLINE Pair(){} + + PX_FORCE_INLINE bool operator<(const Pair& p) const + { + const PxU64 value0 = *reinterpret_cast(this); + const PxU64 value1 = *reinterpret_cast(&p); + return value0 < value1; + } + + PX_FORCE_INLINE bool operator==(const Pair& p) const + { + return (p.mID0 == mID0) && (p.mID1 == mID1); + } + + PX_FORCE_INLINE bool operator!=(const Pair& p) const + { + return (p.mID0 != mID0) || (p.mID1 != mID1); + } + + PxU32 mID0; + PxU32 mID1; + }; + + class AABBManager; + + class PostBroadPhaseStage2Task : public Cm::Task + { + Cm::FlushPool* mFlushPool; + AABBManager& mManager; + + PX_NOCOPY(PostBroadPhaseStage2Task) + public: + + PostBroadPhaseStage2Task(PxU64 contextID, AABBManager& manager) : Cm::Task(contextID), mFlushPool(NULL), mManager(manager) + { + } + + virtual const char* getName() const { return "PostBroadPhaseStage2Task"; } + + void setFlushPool(Cm::FlushPool* pool) { mFlushPool = pool; } + + virtual void runInternal(); + + }; + + class ProcessAggPairsBase; + + /** + \brief A structure responsible for: + * storing an aabb representation for each active shape in the related scene + * managing the creation/removal of aabb representations when their related shapes are created/removed + * updating all aabbs that require an update due to modification of shape geometry or transform + * updating the aabb of all aggregates from the union of the aabbs of all shapes that make up each aggregate + * computing and reporting the incremental changes to the set of overlapping aabb pairs + */ + class AABBManager : public Ps::UserAllocated + { + PX_NOCOPY(AABBManager) + public: + + AABBManager(BroadPhase& bp, BoundsArray& boundsArray, Ps::Array& contactDistance, + PxU32 maxNbAggregates, PxU32 maxNbShapes, Ps::VirtualAllocator& allocator, PxU64 contextID, + PxPairFilteringMode::Enum kineKineFilteringMode, PxPairFilteringMode::Enum staticKineFilteringMode); + + void destroy(); + + AggregateHandle createAggregate(BoundsIndex index, Bp::FilterGroup::Enum group, void* userData, const bool selfCollisions); + bool destroyAggregate(BoundsIndex& index, Bp::FilterGroup::Enum& group, AggregateHandle aggregateHandle); + + bool addBounds(BoundsIndex index, PxReal contactDistance, Bp::FilterGroup::Enum group, void* userdata, AggregateHandle aggregateHandle, ElementType::Enum volumeType); + void reserveSpaceForBounds(BoundsIndex index); + void removeBounds(BoundsIndex index); + PX_FORCE_INLINE Ps::IntBool isMarkedForRemove(BoundsIndex index) const { return mRemovedHandleMap.boundedTest(index); } + + void setContactOffset(BoundsIndex handle, PxReal offset) + { + // PT: this works even for aggregated shapes, since the corresponding bit will also be set in the 'updated' map. + mContactDistance.begin()[handle] = offset; + mPersistentStateChanged = true; + mChangedHandleMap.growAndSet(handle); + } + + void setVolumeType(BoundsIndex handle, ElementType::Enum volumeType) + { + mVolumeData[handle].setVolumeType(volumeType); + } + + void setBPGroup(BoundsIndex index, Bp::FilterGroup::Enum group) + { + PX_ASSERT((index + 1) < mVolumeData.size()); + PX_ASSERT(group != Bp::FilterGroup::eINVALID); // PT: we use group == Bp::FilterGroup::eINVALID to mark removed/invalid entries + mGroups[index] = group; + } + + // PT: TODO: revisit name: we don't "update AABBs" here anymore + void updateAABBsAndBP( PxU32 numCpuTasks, + Cm::FlushPool& flushPool, + PxcScratchAllocator* scratchAllocator, + bool hasContactDistanceUpdated, + PxBaseTask* continuation, + PxBaseTask* narrowPhaseUnlockTask); + + void finalizeUpdate( PxU32 numCpuTasks, + PxcScratchAllocator* scratchAllocator, + PxBaseTask* continuation, + PxBaseTask* narrowPhaseUnlockTask); + + AABBOverlap* getCreatedOverlaps(ElementType::Enum type, PxU32& count) + { + PX_ASSERT(type < ElementType::eCOUNT); + count = mCreatedOverlaps[type].size(); + return mCreatedOverlaps[type].begin(); + } + + AABBOverlap* getDestroyedOverlaps(ElementType::Enum type, PxU32& count) + { + PX_ASSERT(type < ElementType::eCOUNT); + count = mDestroyedOverlaps[type].size(); + return mDestroyedOverlaps[type].begin(); + } + + void freeBuffers(); + + void** getOutOfBoundsObjects(PxU32& nbOutOfBoundsObjects) + { + nbOutOfBoundsObjects = mOutOfBoundsObjects.size(); + return mOutOfBoundsObjects.begin(); + } + + void clearOutOfBoundsObjects() + { + mOutOfBoundsObjects.clear(); + } + + void** getOutOfBoundsAggregates(PxU32& nbOutOfBoundsAggregates) + { + nbOutOfBoundsAggregates = mOutOfBoundsAggregates.size(); + return mOutOfBoundsAggregates.begin(); + } + + void clearOutOfBoundsAggregates() + { + mOutOfBoundsAggregates.clear(); + } + + void shiftOrigin(const PxVec3& shift); + + void visualize(Cm::RenderOutput& out); + + PX_FORCE_INLINE BroadPhase* getBroadPhase() const { return &mBroadPhase; } + PX_FORCE_INLINE BoundsArray& getBoundsArray() { return mBoundsArray; } + PX_FORCE_INLINE PxU32 getNbActiveAggregates() const { return mNbAggregates; } + PX_FORCE_INLINE const float* getContactDistances() const { return mContactDistance.begin(); } + PX_FORCE_INLINE Cm::BitMapPinned& getChangedAABBMgActorHandleMap() { return mChangedHandleMap; } + + PX_FORCE_INLINE void* getUserData(const BoundsIndex index) const { if (index < mVolumeData.size()) return mVolumeData[index].getUserData(); return NULL; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + + void postBroadPhase(PxBaseTask*, PxBaseTask* narrowPhaseUnlockTask, Cm::FlushPool& flushPool); + + + + BpCacheData* getBpCacheData(); + void putBpCacheData(BpCacheData*); + void resetBpCacheData(); + + Ps::Mutex mMapLock; + + private: + void reserveShapeSpace(PxU32 nbShapes); + + void postBpStage2(PxBaseTask*, Cm::FlushPool&); + + void postBpStage3(PxBaseTask*); + + PostBroadPhaseStage2Task mPostBroadPhase2; + Cm::DelegateTask mPostBroadPhase3; + + //Cm::DelegateTask mPostBroadPhase; + + FinalizeUpdateTask mFinalizeUpdateTask; + + // PT: we have bitmaps here probably to quickly handle added/removed objects during same frame. + // PT: TODO: consider replacing with plain arrays (easier to parse, already existing below, etc) + Cm::BitMap mAddedHandleMap; // PT: indexed by BoundsIndex + Cm::BitMap mRemovedHandleMap; // PT: indexed by BoundsIndex + Cm::BitMapPinned mChangedHandleMap; + + PX_FORCE_INLINE void removeBPEntry(BoundsIndex index) // PT: only for objects passed to the BP + { + if(mAddedHandleMap.test(index)) // PT: if object had been added this frame... + mAddedHandleMap.reset(index); // PT: ...then simply revert the previous operation locally (it hasn't been passed to the BP yet). + else + mRemovedHandleMap.set(index); // PT: else we need to remove it from the BP + } + + PX_FORCE_INLINE void addBPEntry(BoundsIndex index) + { + if(mRemovedHandleMap.test(index)) + mRemovedHandleMap.reset(index); + else + mAddedHandleMap.set(index); + } + + // PT: TODO: when do we need 'Ps::VirtualAllocator' and when don't we? When memory is passed to GPU BP? + //ML: we create mGroups and mContactDistance in the AABBManager constructor. Ps::Array will take Ps::VirtualAllocator as a parameter. Therefore, if GPU BP is using, + //we will passed a pinned host memory allocator, otherwise, we will just pass a normal allocator. + Ps::Array mGroups; // NOTE: we stick Bp::FilterGroup::eINVALID in this slot to indicate that the entry is invalid (removed or never inserted.) + Ps::Array& mContactDistance; + Ps::Array mVolumeData; + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + bool mLUT[Bp::FilterType::COUNT][Bp::FilterType::COUNT]; + #endif + PX_FORCE_INLINE void initEntry(BoundsIndex index, PxReal contactDistance, Bp::FilterGroup::Enum group, void* userData) + { + if ((index + 1) >= mVolumeData.size()) + reserveShapeSpace(index + 1); + + // PT: TODO: why is this needed at all? Why aren't size() and capacity() enough? + mUsedSize = PxMax(index+1, mUsedSize); + + PX_ASSERT(group != Bp::FilterGroup::eINVALID); // PT: we use group == Bp::FilterGroup::eINVALID to mark removed/invalid entries + mGroups[index] = group; + mContactDistance.begin()[index] = contactDistance; + mVolumeData[index].setUserData(userData); + } + + PX_FORCE_INLINE void resetEntry(BoundsIndex index) + { + mGroups[index] = Bp::FilterGroup::eINVALID; + mContactDistance.begin()[index] = 0.0f; + mVolumeData[index].reset(); + } + + // PT: TODO: remove confusion between BoundsIndex and ShapeHandle here! + Ps::Array mAddedHandles; + Ps::Array mUpdatedHandles; + Ps::Array mRemovedHandles; + + BroadPhase& mBroadPhase; + BoundsArray& mBoundsArray; + + Ps::Array mOutOfBoundsObjects; + Ps::Array mOutOfBoundsAggregates; + Ps::Array mCreatedOverlaps[ElementType::eCOUNT]; + Ps::Array mDestroyedOverlaps[ElementType::eCOUNT]; + + PxcScratchAllocator* mScratchAllocator; + + PxBaseTask* mNarrowPhaseUnblockTask; + PxU32 mUsedSize; // highest used value + 1 + bool mOriginShifted; + bool mPersistentStateChanged; + + PxU32 mNbAggregates; + PxU32 mFirstFreeAggregate; + Ps::Array mAggregates; // PT: indexed by AggregateHandle + Ps::Array mDirtyAggregates; + + PxU32 mTimestamp; + + AggPairMap mActorAggregatePairs; + AggPairMap mAggregateAggregatePairs; + + Ps::Array mAggPairTasks; + + #ifdef BP_USE_AGGREGATE_GROUP_TAIL + // PT: TODO: even in the 3.4 trunk this stuff is a clumsy mess: groups are "BpHandle" suddenly passed + // to BroadPhaseUpdateData as "ShapeHandle". + //Free aggregate group ids. + PxU32 mAggregateGroupTide; + Ps::Array mFreeAggregateGroups; // PT: TODO: remove this useless array + #endif + Ps::HashSet mCreatedPairs; + + PxU64 mContextID; + + Ps::SList mBpThreadContextPool; + + PX_FORCE_INLINE Aggregate* getAggregateFromHandle(AggregateHandle handle) + { + PX_ASSERT(handle minB and maxB > minA for all three axes. + The rule that minima(maxima) are even(odd) (see BroadPhaseUpdateData) removes the ambiguity of touching bounds. + + */ + virtual BroadPhasePair* getCreatedPairs() = 0; + + /** + \brief Return the number of deleted overlap pairs computed in the execution of update() that has just completed. + */ + virtual PxU32 getNbDeletedPairs() const = 0; + + /** + \brief Return the number of deleted overlap pairs computed in the execution of update() that has just completed. + Note that a deleted pair can only be reported if that pair has already appeared in the list of created pairs in an earlier update. + A lost overlap occurs when a pair of bounds previously overlapped on all three axes but have now separated on at least one axis. + A lost overlap must involve at least one of the bounds of the lost overlap pair appearing in the updated list. + Lost overlaps arising from removal of bounds from the broadphase do not appear in the list of deleted pairs. + It is impossible for the same pair to appear simultaneously in the list of created and deleted pairs. + The test for overlap is conservative throughout, meaning that deleted pairs do not include touching pairs. + */ + virtual BroadPhasePair* getDeletedPairs() = 0; + + /** + \brief After the broadphase has completed its update() function and the created/deleted pairs have been queried + with getCreatedPairs/getDeletedPairs it is desirable to free any memory that was temporarily acquired for the update but is + is no longer required post-update. This can be achieved with the function freeBuffers(). + */ + virtual void freeBuffers() = 0; + + /** + \brief Adjust internal structures after all bounds have been adjusted due to a scene origin shift. + */ + virtual void shiftOrigin(const PxVec3& shift) = 0; + + /** + \brief Test that the created/updated/removed lists obey the rules that + 1. object ids can only feature in the created list if they have never been previously added or if they were previously removed. + 2. object ids can only be added to the updated list if they have been previously added without being removed. + 3. objects ids can only be added to the removed list if they have been previously added without being removed. + */ +#if PX_CHECKED + virtual bool isValid(const BroadPhaseUpdateData& updateData) const = 0; +#endif + + virtual BroadPhasePair* getBroadPhasePairs() const = 0; + + virtual void deletePairs() = 0; + + // PT: for unit-testing the non-GPU versions + virtual void singleThreadedUpdate(PxcScratchAllocator* /*scratchAllocator*/, const BroadPhaseUpdateData& /*updateData*/){} +}; + +} //namespace Bp + +} //namespace physx + +#endif //BP_BROADPHASE_H diff --git a/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h b/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h new file mode 100644 index 000000000..6fe718204 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h @@ -0,0 +1,523 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_UPDATE_H +#define BP_BROADPHASE_UPDATE_H + +#include "foundation/PxAssert.h" +#include "foundation/PxUnionCast.h" +#include "CmPhysXCommon.h" +#include "PxBroadPhase.h" +#include "Ps.h" + +namespace physx +{ +namespace Bp +{ +typedef PxU32 ShapeHandle; +typedef PxU32 BpHandle; +#define BP_INVALID_BP_HANDLE 0x3fffffff + +#define ALIGN_SIZE_16(size) ((unsigned(size)+15)&(unsigned(~15))) + +#define BP_USE_AGGREGATE_GROUP_TAIL +#define BP_FILTERING_USES_TYPE_IN_GROUP + + /* + \brief AABBManager volumes with the same filter group value are guaranteed never to generate an overlap pair. + \note To ensure that static pairs never overlap, add static shapes with eSTATICS. + The value eDYNAMICS_BASE provides a minimum recommended group value for dynamic shapes. + If dynamics shapes are assigned group values greater than or equal to eDYNAMICS_BASE then + they are allowed to generate broadphase overlaps with statics, and other dynamic shapes provided + they have different group values. + @see AABBManager::createVolume + */ + struct FilterGroup + { + enum Enum + { + eSTATICS = 0, + eDYNAMICS_BASE = 1, +#ifdef BP_USE_AGGREGATE_GROUP_TAIL + eAGGREGATE_BASE = 0xfffffffe, +#endif + eINVALID = 0xffffffff + }; + }; + +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + struct FilterType + { + enum Enum + { + STATIC = 0, + KINEMATIC = 1, + DYNAMIC = 2, + AGGREGATE = 3, + + COUNT = 4 + }; + }; +#endif + + PX_FORCE_INLINE Bp::FilterGroup::Enum getFilterGroup_Statics() + { + return Bp::FilterGroup::eSTATICS; + } + + PX_FORCE_INLINE Bp::FilterGroup::Enum getFilterGroup_Dynamics(PxU32 rigidId, bool isKinematic) + { + const PxU32 group = rigidId + Bp::FilterGroup::eDYNAMICS_BASE; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const PxU32 type = isKinematic ? FilterType::KINEMATIC : FilterType::DYNAMIC; + return Bp::FilterGroup::Enum((group<<2)|type); +#else + PX_UNUSED(isKinematic); + return Bp::FilterGroup::Enum(group); +#endif + } + + PX_FORCE_INLINE Bp::FilterGroup::Enum getFilterGroup(bool isStatic, PxU32 rigidId, bool isKinematic) + { + return isStatic ? getFilterGroup_Statics() : getFilterGroup_Dynamics(rigidId, isKinematic); + } + +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + PX_FORCE_INLINE bool groupFiltering(const Bp::FilterGroup::Enum group0, const Bp::FilterGroup::Enum group1, const bool* PX_RESTRICT lut) + { +/* const int g0 = group0 & ~3; + const int g1 = group1 & ~3; + if(g0==g1) + return false;*/ + if(group0==group1) + { + PX_ASSERT((group0 & ~3)==(group1 & ~3)); + return false; + } + + const int type0 = group0 & 3; + const int type1 = group1 & 3; + return lut[type0*4+type1]; + } +#else + PX_FORCE_INLINE bool groupFiltering(const Bp::FilterGroup::Enum group0, const Bp::FilterGroup::Enum group1) + { + return group0!=group1; + } +#endif + + /* + \brief Encode a single float value with lossless encoding to integer + */ + PX_FORCE_INLINE PxU32 encodeFloat(PxU32 ir) + { + //we may need to check on -0 and 0 + //But it should make no practical difference. + if(ir & PX_SIGN_BITMASK) //negative? + return ~ir;//reverse sequence of negative numbers + else + return ir | PX_SIGN_BITMASK; // flip sign + } + + /* + \brief Encode a single float value with lossless encoding to integer + */ + PX_FORCE_INLINE PxU32 decodeFloat(PxU32 ir) + { + if(ir & PX_SIGN_BITMASK) //positive? + return ir & ~PX_SIGN_BITMASK; //flip sign + else + return ~ir; //undo reversal + } + + +/** +\brief Integer representation of PxBounds3 used by BroadPhase +@see BroadPhaseUpdateData +*/ + +typedef PxU32 ValType; + +class IntegerAABB +{ +public: + + enum + { + MIN_X = 0, + MIN_Y, + MIN_Z, + MAX_X, + MAX_Y, + MAX_Z + }; + + IntegerAABB(const PxBounds3& b, PxReal contactDistance) + { + const PxVec3 dist(contactDistance); + encode(PxBounds3(b.minimum - dist, b.maximum + dist)); + } + + /* + \brief Return the minimum along a specified axis + \param[in] i is the axis + */ + PX_FORCE_INLINE ValType getMin(PxU32 i) const { return (mMinMax)[MIN_X+i]; } + + /* + \brief Return the maximum along a specified axis + \param[in] i is the axis + */ + PX_FORCE_INLINE ValType getMax(PxU32 i) const { return (mMinMax)[MAX_X+i]; } + + /* + \brief Return one of the six min/max values of the bound + \param[in] isMax determines whether a min or max value is returned + \param[in] index is the axis + */ + PX_FORCE_INLINE ValType getExtent(PxU32 isMax, PxU32 index) const + { + PX_ASSERT(isMax<=1); + return (mMinMax)[3*isMax+index]; + } + + /* + \brief Return the minimum on the x axis + */ + PX_FORCE_INLINE ValType getMinX() const { return mMinMax[MIN_X]; } + + /* + \brief Return the minimum on the y axis + */ + PX_FORCE_INLINE ValType getMinY() const { return mMinMax[MIN_Y]; } + + /* + \brief Return the minimum on the z axis + */ + PX_FORCE_INLINE ValType getMinZ() const { return mMinMax[MIN_Z]; } + + /* + \brief Return the maximum on the x axis + */ + PX_FORCE_INLINE ValType getMaxX() const { return mMinMax[MAX_X]; } + + /* + \brief Return the maximum on the y axis + */ + PX_FORCE_INLINE ValType getMaxY() const { return mMinMax[MAX_Y]; } + + /* + \brief Return the maximum on the z axis + */ + PX_FORCE_INLINE ValType getMaxZ() const { return mMinMax[MAX_Z]; } + + /* + \brief Encode float bounds so they are stored as integer bounds + \param[in] bounds is the bounds to be encoded + \note The integer values of minima are always even, while the integer values of maxima are always odd + \note The encoding process masks off the last four bits for minima and masks on the last four bits for maxima. + This keeps the bounds constant when its shape is subjected to small global pose perturbations. In turn, this helps + reduce computational effort in the broadphase update by reducing the amount of sorting required on near-stationary + bodies that are aligned along one or more axis. + @see decode + */ + PX_FORCE_INLINE void encode(const PxBounds3& bounds) + { + const PxU32* PX_RESTRICT min = PxUnionCast(&bounds.minimum.x); + const PxU32* PX_RESTRICT max = PxUnionCast(&bounds.maximum.x); + //Avoid min=max by enforcing the rule that mins are even and maxs are odd. + mMinMax[MIN_X] = encodeFloatMin(min[0]); + mMinMax[MIN_Y] = encodeFloatMin(min[1]); + mMinMax[MIN_Z] = encodeFloatMin(min[2]); + mMinMax[MAX_X] = encodeFloatMax(max[0]) | (1<<2); + mMinMax[MAX_Y] = encodeFloatMax(max[1]) | (1<<2); + mMinMax[MAX_Z] = encodeFloatMax(max[2]) | (1<<2); + } + + /* + \brief Decode from integer bounds to float bounds + \param[out] bounds is the decoded float bounds + \note Encode followed by decode will produce a float bound larger than the original + due to the masking in encode. + @see encode + */ + PX_FORCE_INLINE void decode(PxBounds3& bounds) const + { + PxU32* PX_RESTRICT min = PxUnionCast(&bounds.minimum.x); + PxU32* PX_RESTRICT max = PxUnionCast(&bounds.maximum.x); + min[0] = decodeFloat(mMinMax[MIN_X]); + min[1] = decodeFloat(mMinMax[MIN_Y]); + min[2] = decodeFloat(mMinMax[MIN_Z]); + max[0] = decodeFloat(mMinMax[MAX_X]); + max[1] = decodeFloat(mMinMax[MAX_Y]); + max[2] = decodeFloat(mMinMax[MAX_Z]); + } + + /* + \brief Encode a single minimum value from integer bounds to float bounds + \note The encoding process masks off the last four bits for minima + @see encode + */ + static PX_FORCE_INLINE ValType encodeFloatMin(PxU32 source) + { + return ((encodeFloat(source) >> eGRID_SNAP_VAL) - 1) << eGRID_SNAP_VAL; + } + + /* + \brief Encode a single maximum value from integer bounds to float bounds + \note The encoding process masks on the last four bits for maxima + @see encode + */ + static PX_FORCE_INLINE ValType encodeFloatMax(PxU32 source) + { + return ((encodeFloat(source) >> eGRID_SNAP_VAL) + 1) << eGRID_SNAP_VAL; + } + + /* + \brief Shift the encoded bounds by a specified vector + \param[in] shift is the vector used to shift the bounds + */ + PX_FORCE_INLINE void shift(const PxVec3& shift) + { + ::physx::PxBounds3 elemBounds; + decode(elemBounds); + elemBounds.minimum -= shift; + elemBounds.maximum -= shift; + encode(elemBounds); + } + + /* + \brief Test if this aabb lies entirely inside another aabb + \param[in] box is the other box + \return True if this aabb lies entirely inside box + */ + PX_INLINE bool isInside(const IntegerAABB& box) const + { + if(box.mMinMax[MIN_X]>mMinMax[MIN_X]) return false; + if(box.mMinMax[MIN_Y]>mMinMax[MIN_Y]) return false; + if(box.mMinMax[MIN_Z]>mMinMax[MIN_Z]) return false; + if(box.mMinMax[MAX_X] mMinMax[MAX_X] || mMinMax[MIN_X] > b.mMinMax[MAX_X] || + b.mMinMax[MIN_Y] > mMinMax[MAX_Y] || mMinMax[MIN_Y] > b.mMinMax[MAX_Y] || + b.mMinMax[MIN_Z] > mMinMax[MAX_Z] || mMinMax[MIN_Z] > b.mMinMax[MAX_Z]); + } + + PX_FORCE_INLINE bool intersects1D(const IntegerAABB& b, const PxU32 axis) const + { + const PxU32 maxAxis = axis + 3; + return !(b.mMinMax[axis] > mMinMax[maxAxis] || mMinMax[axis] > b.mMinMax[maxAxis]); + } + + + /* + \brief Expand bounds to include another + \note This is used to compute the aggregate bounds of multiple shape bounds + \param[in] b is the bounds to be included + */ + PX_FORCE_INLINE void include(const IntegerAABB& b) + { + mMinMax[MIN_X] = PxMin(mMinMax[MIN_X], b.mMinMax[MIN_X]); + mMinMax[MIN_Y] = PxMin(mMinMax[MIN_Y], b.mMinMax[MIN_Y]); + mMinMax[MIN_Z] = PxMin(mMinMax[MIN_Z], b.mMinMax[MIN_Z]); + mMinMax[MAX_X] = PxMax(mMinMax[MAX_X], b.mMinMax[MAX_X]); + mMinMax[MAX_Y] = PxMax(mMinMax[MAX_Y], b.mMinMax[MAX_Y]); + mMinMax[MAX_Z] = PxMax(mMinMax[MAX_Z], b.mMinMax[MAX_Z]); + } + + /* + \brief Set the bounds to (max, max, max), (min, min, min) + */ + PX_INLINE void setEmpty() + { + mMinMax[MIN_X] = mMinMax[MIN_Y] = mMinMax[MIN_Z] = 0xff7fffff; //PX_IR(PX_MAX_F32); + mMinMax[MAX_X] = mMinMax[MAX_Y] = mMinMax[MAX_Z] = 0x00800000; ///PX_IR(0.0f); + } + + ValType mMinMax[6]; + +private: + + enum + { + eGRID_SNAP_VAL = 4 + }; +}; + +PX_FORCE_INLINE ValType encodeMin(const PxBounds3& bounds, PxU32 axis, PxReal contactDistance) +{ + const PxReal val = bounds.minimum[axis] - contactDistance; + const PxU32 min = PxUnionCast(val); + const PxU32 m = IntegerAABB::encodeFloatMin(min); + return m; +} + +PX_FORCE_INLINE ValType encodeMax(const PxBounds3& bounds, PxU32 axis, PxReal contactDistance) +{ + const PxReal val = bounds.maximum[axis] + contactDistance; + const PxU32 max = PxUnionCast(val); + const PxU32 m = IntegerAABB::encodeFloatMax(max) | (1<<2); + return m; +} + +class BroadPhase; + +class BroadPhaseUpdateData +{ +public: + + /** + \brief A structure detailing the changes to the collection of aabbs, whose overlaps are computed in the broadphase. + The structure consists of per-object arrays of object bounds and object groups, and three arrays that index + into the per-object arrays, denoting the bounds which are to be created, updated and removed in the broad phase. + + * each entry in the object arrays represents the same shape or aggregate from frame to frame. + * each entry in an index array must be less than the capacity of the per-object arrays. + * no index value may appear in more than one index array, and may not occur more than once in that array. + + An index value is said to be "in use" if it has appeared in a created list in a previous update, and has not + since occurred in a removed list. + + \param[in] created an array of indices describing the bounds that must be inserted into the broadphase. + Each index in the array must not be in use. + + \param[in] updated an array of indices (referencing the boxBounds and boxGroups arrays) describing the bounds + that have moved since the last broadphase update. Each index in the array must be in use, and each object + whose index is in use and whose AABB has changed must appear in the update list. + + \param[in] removed an array of indices describing the bounds that must be removed from the broad phase. Each index in + the array must be in use. + + \param[in] boxBounds an array of bounds coordinates for the AABBs to be processed by the broadphase. + + An entry is valid if its values are integer bitwise representations of floating point numbers that satisfy max>min in each dimension, + along with a further rule that minima(maxima) must have even(odd) values. + + Each entry whose index is either in use or appears in the created array must be valid. An entry whose index is either not in use or + appears in the removed array need not be valid. + + \param[in] boxGroups an array of group ids, one for each bound, used for pair filtering. Bounds with the same group id will not be + reported as overlap pairs by the broad phase. Zero is reserved for static bounds. + + Entries in this array are immutable: the only way to change the group of an object is to remove it from the broad phase and reinsert + it at a different index (recall that each index must appear at most once in the created/updated/removed lists). + + \param[in] boxesCapacity the length of the boxBounds and boxGroups arrays. + + @see BroadPhase::update + */ + BroadPhaseUpdateData( + const ShapeHandle* created, const PxU32 createdSize, + const ShapeHandle* updated, const PxU32 updatedSize, + const ShapeHandle* removed, const PxU32 removedSize, + const PxBounds3* boxBounds, const Bp::FilterGroup::Enum* boxGroups, +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* lut, +#endif + const PxReal* boxContactDistances, const PxU32 boxesCapacity, + const bool stateChanged) : + mCreated (created), + mCreatedSize (createdSize), + mUpdated (updated), + mUpdatedSize (updatedSize), + mRemoved (removed), + mRemovedSize (removedSize), + mBoxBounds (boxBounds), + mBoxGroups (boxGroups), +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT (lut), +#endif + mContactDistance(boxContactDistances), + mBoxesCapacity (boxesCapacity), + mStateChanged (stateChanged) + { + } + + PX_FORCE_INLINE const ShapeHandle* getCreatedHandles() const { return mCreated; } + PX_FORCE_INLINE PxU32 getNumCreatedHandles() const { return mCreatedSize; } + + PX_FORCE_INLINE const ShapeHandle* getUpdatedHandles() const { return mUpdated; } + PX_FORCE_INLINE PxU32 getNumUpdatedHandles() const { return mUpdatedSize; } + + PX_FORCE_INLINE const ShapeHandle* getRemovedHandles() const { return mRemoved; } + PX_FORCE_INLINE PxU32 getNumRemovedHandles() const { return mRemovedSize; } + + PX_FORCE_INLINE const PxBounds3* getAABBs() const { return mBoxBounds; } + PX_FORCE_INLINE const Bp::FilterGroup::Enum* getGroups() const { return mBoxGroups; } +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + PX_FORCE_INLINE const bool* getLUT() const { return mLUT; } +#endif + PX_FORCE_INLINE PxU32 getCapacity() const { return mBoxesCapacity; } + + PX_FORCE_INLINE const PxReal* getContactDistance() const { return mContactDistance; } + + PX_FORCE_INLINE bool getStateChanged() const { return mStateChanged; } + +#if PX_CHECKED + static bool isValid(const BroadPhaseUpdateData& updateData, const BroadPhase& bp); + bool isValid() const; +#endif + +private: + + const ShapeHandle* mCreated; + PxU32 mCreatedSize; + + const ShapeHandle* mUpdated; + PxU32 mUpdatedSize; + + const ShapeHandle* mRemoved; + PxU32 mRemovedSize; + + const PxBounds3* mBoxBounds; + const Bp::FilterGroup::Enum* mBoxGroups; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* mLUT; +#endif + const PxReal* mContactDistance; + PxU32 mBoxesCapacity; + bool mStateChanged; +}; + +} //namespace Bp + +} //namespace physx + +#endif //BP_BROADPHASE_UPDATE_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp new file mode 100644 index 000000000..85109e5f6 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp @@ -0,0 +1,2532 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "BpAABBManager.h" + +#define NB_SENTINELS 6 + +#include "CmRenderOutput.h" +#include "CmFlushPool.h" +#include "BpBroadPhaseMBPCommon.h" +#include "BpBroadPhase.h" +#include "BpBroadPhaseShared.h" +#include "PsFoundation.h" +#include "PsSort.h" +#include "PsHashSet.h" +#include "PsVecMath.h" +#include "GuInternal.h" +#include "common/PxProfileZone.h" +//#include + +using namespace physx; +using namespace Bp; +using namespace Cm; +using namespace Ps::aos; + +static const bool gSingleThreaded = false; +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + #define ABP_SIMD_OVERLAP +#endif +#ifdef ABP_SIMD_OVERLAP + typedef AABB_YZn AABB_YZ; +#else + typedef AABB_YZr AABB_YZ; +#endif + +#ifdef ABP_SIMD_OVERLAP + static const bool gUseRegularBPKernel = false; // false to use "version 13" in box pruning series + static const bool gUnrollLoop = true; // true to use "version 14" in box pruning series +#else + // PT: tested on Switch, for some reason the regular version is fastest there + static const bool gUseRegularBPKernel = true; // false to use "version 13" in box pruning series + static const bool gUnrollLoop = false; // true to use "version 14" in box pruning series +#endif + +namespace physx +{ +namespace Bp +{ +static PX_FORCE_INLINE uint32_t hash(const Pair& p) +{ + return PxU32(Ps::hash( (p.mID0&0xffff)|(p.mID1<<16)) ); +} + +static PX_FORCE_INLINE uint32_t hash(const AggPair& p) +{ + return PxU32(Ps::hash( (p.mIndex0&0xffff)|(p.mIndex1<<16)) ); +} + +static PX_FORCE_INLINE bool shouldPairBeDeleted(const Ps::Array& groups, ShapeHandle h0, ShapeHandle h1) +{ + PX_ASSERT(h0 +static void resetOrClear(T& a) +{ + const PxU32 c = a.capacity(); + const PxU32 s = a.size(); + if(s>=c/2) + a.clear(); + else + a.reset(); +} + +/// + + typedef PxU32 InflatedType; + + // PT: TODO: revisit/optimize all that stuff once it works + class Aggregate : public Ps::UserAllocated + { + public: + Aggregate(BoundsIndex index, bool selfCollisions); + ~Aggregate(); + + BoundsIndex mIndex; + private: + Ps::Array mAggregated; // PT: TODO: replace with linked list? + public: + PersistentSelfCollisionPairs* mSelfCollisionPairs; + PxU32 mDirtyIndex; // PT: index in mDirtyAggregates + private: + AABB_Xi* mInflatedBoundsX; + AABB_YZ* mInflatedBoundsYZ; + PxU32 mAllocatedSize; + public: + PX_FORCE_INLINE PxU32 getNbAggregated() const { return mAggregated.size(); } + PX_FORCE_INLINE BoundsIndex getAggregated(PxU32 i) const { return mAggregated[i]; } + PX_FORCE_INLINE const BoundsIndex* getIndices() const { return mAggregated.begin(); } + PX_FORCE_INLINE void addAggregated(BoundsIndex i) { mAggregated.pushBack(i); } + PX_FORCE_INLINE bool removeAggregated(BoundsIndex i) { return mAggregated.findAndReplaceWithLast(i); } // PT: TODO: optimize? + PX_FORCE_INLINE const PxBounds3& getMergedBounds() const { return mBounds; } + + PX_FORCE_INLINE void resetDirtyState() { mDirtyIndex = PX_INVALID_U32; } + PX_FORCE_INLINE bool isDirty() const { return mDirtyIndex != PX_INVALID_U32; } + PX_FORCE_INLINE void markAsDirty(Ps::Array& dirtyAggregates) + { + if(!isDirty()) + { + mDirtyIndex = dirtyAggregates.size(); + dirtyAggregates.pushBack(this); + } + } + + void allocateBounds(); + void computeBounds(const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances) /*PX_RESTRICT*/; + + PX_FORCE_INLINE const AABB_Xi* getBoundsX() const { return mInflatedBoundsX; } + PX_FORCE_INLINE const AABB_YZ* getBoundsYZ() const { return mInflatedBoundsYZ; } + PX_FORCE_INLINE void getSortedMinBounds() + { + if(mDirtySort) + sortBounds(); + } + private: + PxBounds3 mBounds; + bool mDirtySort; + + void sortBounds(); + PX_NOCOPY(Aggregate) + }; + +/// + +namespace +{ +#define MBP_ALLOC(x) PX_ALLOC(x, "MBP") +#define MBP_ALLOC_TMP(x) PX_ALLOC_TEMP(x, "MBP_TMP") +#define MBP_FREE(x) if(x) PX_FREE_AND_RESET(x) + + struct MBPEntry; + struct RegionHandle; + struct MBP_Object; + + class MBP_PairManager : public PairManagerData + { + public: + MBP_PairManager() {} + ~MBP_PairManager() {} + + PX_FORCE_INLINE InternalPair* addPair(PxU32 id0, PxU32 id1); + }; + +/////////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE InternalPair* MBP_PairManager::addPair(PxU32 id0, PxU32 id1) +{ + PX_ASSERT(id0!=INVALID_ID); + PX_ASSERT(id1!=INVALID_ID); + return addPairInternal(id0, id1); +} + +/////////////////////////////////////////////////////////////////////////////// + +} + + typedef MBP_PairManager PairArray; + +/// + +class PersistentPairs : public Ps::UserAllocated +{ + public: + PersistentPairs() : mTimestamp(PX_INVALID_U32), mShouldBeDeleted(false) {} + virtual ~PersistentPairs() {} + + virtual bool update(AABBManager& /*manager*/, BpCacheData* /*data*/ = NULL) { return false; } + + + PX_FORCE_INLINE void updatePairs(PxU32 timestamp, const PxBounds3* bounds, const float* contactDistances, const Bp::FilterGroup::Enum* groups, +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* lut, +#endif + Ps::Array& volumeData, Ps::Array* createdOverlaps, Ps::Array* destroyedOverlaps); + void outputDeletedOverlaps(Ps::Array* overlaps, const Ps::Array& volumeData); + private: + virtual void findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) = 0; + protected: + PxU32 mTimestamp; + MBP_PairManager mPM; + public: + bool mShouldBeDeleted; +}; + +///// + #define PosXType2 PxU32 + +#if PX_INTEL_FAMILY + #define SIMD_OVERLAP_TEST_14a(box) _mm_movemask_ps(_mm_cmpngt_ps(b, _mm_load_ps(box)))==15 + + #define SIMD_OVERLAP_INIT_9c(box) \ + __m128 b = _mm_shuffle_ps(_mm_load_ps(&box.mMinY), _mm_load_ps(&box.mMinY), 78);\ + const float Coeff = -1.0f;\ + b = _mm_mul_ps(b, _mm_load1_ps(&Coeff)); + + #define SIMD_OVERLAP_TEST_9c(box) \ + const __m128 a = _mm_load_ps(&box.mMinY); \ + const __m128 d = _mm_cmpge_ps(a, b); \ + if(_mm_movemask_ps(d)==15) +#else + #define SIMD_OVERLAP_TEST_14a(box) BAllEqFFFF(V4IsGrtr(b, V4LoadA(box))) + + #define SIMD_OVERLAP_INIT_9c(box) \ + Vec4V b = V4PermZWXY(V4LoadA(&box.mMinY)); \ + b = V4Mul(b, V4Load(-1.0f)); + + #define SIMD_OVERLAP_TEST_9c(box) \ + const Vec4V a = V4LoadA(&box.mMinY); \ + const Vec4V d = V4IsGrtrOrEq(a, b); \ + if(BAllEqTTTT(d)) +#endif + + #define CODEALIGN16 //_asm align 16 +#ifdef ABP_SIMD_OVERLAP + #define SIMD_OVERLAP_PRELOAD_BOX0 SIMD_OVERLAP_INIT_9c(box0) + #define SIMD_OVERLAP_TEST(x) SIMD_OVERLAP_TEST_9c(x) +#else + #define SIMD_OVERLAP_PRELOAD_BOX0 +#endif + +#ifndef ABP_SIMD_OVERLAP +static PX_FORCE_INLINE int intersect2D(const AABB_YZ& a, const AABB_YZ& b) +{ + const bool b0 = b.mMaxY < a.mMinY; + const bool b1 = a.mMaxY < b.mMinY; + const bool b2 = b.mMaxZ < a.mMinZ; + const bool b3 = a.mMaxZ < b.mMinZ; +// const bool b4 = b0 || b1 || b2 || b3; + const bool b4 = b0 | b1 | b2 | b3; + return !b4; +} +#endif + +#ifdef ABP_SIMD_OVERLAP + #define ABP_OVERLAP_TEST(x) SIMD_OVERLAP_TEST(x) +#else + #define ABP_OVERLAP_TEST(x) if(intersect2D(box0, x)) +#endif + +//#define BIP_VERSION_1 + + struct outputPair_Bipartite + { +#ifdef BIP_VERSION_1 + outputPair_Bipartite(PairArray* pairManager, Aggregate* aggregate0, Aggregate* aggregate1, const Bp::FilterGroup::Enum* groups, const bool* lut) : +#else + outputPair_Bipartite(PairArray* pairManager, const BoundsIndex* remap0, const BoundsIndex* remap1, const Bp::FilterGroup::Enum* groups, const bool* lut) : +#endif + mPairManager (pairManager), +#ifdef BIP_VERSION_1 + mAggregate0 (aggregate0), + mAggregate1 (aggregate1), +#else + mRemap0 (remap0), + mRemap1 (remap1), +#endif + mGroups (groups), + mLUT (lut) + { + } + + PX_FORCE_INLINE void outputPair(PxU32 index0, PxU32 index1) + { +#ifdef BIP_VERSION_1 + const PxU32 aggIndex0 = mAggregate0->getAggregated(index0); + const PxU32 aggIndex1 = mAggregate1->getAggregated(index1); +#else + const PxU32 aggIndex0 = mRemap0[index0]; + const PxU32 aggIndex1 = mRemap1[index1]; +#endif + if(groupFiltering(mGroups[aggIndex0], mGroups[aggIndex1], mLUT)) + mPairManager->addPair(aggIndex0, aggIndex1); + } + + PairArray* mPairManager; +#ifdef BIP_VERSION_1 + Aggregate* mAggregate0; + Aggregate* mAggregate1; +#else + const BoundsIndex* mRemap0; + const BoundsIndex* mRemap1; +#endif + const Bp::FilterGroup::Enum* mGroups; + const bool* mLUT; + }; + +template +static void boxPruningKernel( PairArray* PX_RESTRICT pairManager, const bool* PX_RESTRICT lut, +#ifdef BIP_VERSION_1 + Aggregate* PX_RESTRICT aggregate0, Aggregate* PX_RESTRICT aggregate1, +#else + PxU32 nb0, const BoundsIndex* PX_RESTRICT remap0, const AABB_Xi* PX_RESTRICT boxes0_X, const AABB_YZ* PX_RESTRICT boxes0_YZ, + PxU32 nb1, const BoundsIndex* PX_RESTRICT remap1, const AABB_Xi* PX_RESTRICT boxes1_X, const AABB_YZ* PX_RESTRICT boxes1_YZ, +#endif + const Bp::FilterGroup::Enum* PX_RESTRICT groups) +{ +#ifdef BIP_VERSION_1 + outputPair_Bipartite pm(pairManager, aggregate0, aggregate1, groups, lut); + + const PxU32 nb0 = aggregate0->getNbAggregated(); + const PxU32 nb1 = aggregate1->getNbAggregated(); + const AABB_Xi* PX_RESTRICT boxes0_X = aggregate0->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes0_YZ = aggregate0->getBoundsYZ(); + const AABB_Xi* PX_RESTRICT boxes1_X = aggregate1->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes1_YZ = aggregate1->getBoundsYZ(); +#else +// outputPair_Bipartite pm(pairManager, aggregate0->getIndices(), aggregate1->getIndices(), groups, lut); + outputPair_Bipartite pm(pairManager, remap0, remap1, groups, lut); +#endif + + PxU32 index0 = 0; + PxU32 runningIndex1 = 0; + + while(runningIndex1(&boxes1_YZ[runningIndex1]); + const char* const CurrentBoxListX = reinterpret_cast(&boxes1_X[runningIndex1]); + + if(!gUnrollLoop) + { + while(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) + { + const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2); +#ifdef ABP_SIMD_OVERLAP + if(SIMD_OVERLAP_TEST_14a(box)) +#else + if(intersect2D(box0, *reinterpret_cast(box))) +#endif + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes1_X))>>3; + pm.outputPair(index0, Index1); + } + Offset += 8; + } + } + else + { + +#define BIP_VERSION4 +#ifdef BIP_VERSION4 +#ifdef ABP_SIMD_OVERLAP + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(SIMD_OVERLAP_TEST_14a(box)) \ + goto label; } +#else + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(intersect2D(box0, *reinterpret_cast(box))) \ + goto label; } +#endif + goto StartLoop4; + CODEALIGN16 +FoundOverlap3: + Offset += 8; + CODEALIGN16 +FoundOverlap2: + Offset += 8; + CODEALIGN16 +FoundOverlap1: + Offset += 8; + CODEALIGN16 +FoundOverlap0: + Offset += 8; + CODEALIGN16 +FoundOverlap: + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - 8 - reinterpret_cast(boxes1_X))>>3; + pm.outputPair(index0, Index1); + } + CODEALIGN16 +StartLoop4: + while(*reinterpret_cast(CurrentBoxListX + Offset + 8*5)<=maxLimit) + { + BLOCK4(0, FoundOverlap0) + BLOCK4(8, FoundOverlap1) + BLOCK4(16, FoundOverlap2) + BLOCK4(24, FoundOverlap3) + Offset += 40; + BLOCK4(-8, FoundOverlap) + } +#undef BLOCK4 +#endif + +#ifdef ABP_SIMD_OVERLAP + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(SIMD_OVERLAP_TEST_14a(reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto OverlapFound; \ + Offset += 8; +#else + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(intersect2D(box0, *reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto OverlapFound; \ + Offset += 8; +#endif + + goto LoopStart; + CODEALIGN16 +OverlapFound: + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes1_X))>>3; + pm.outputPair(index0, Index1); + } + Offset += 8; + CODEALIGN16 +LoopStart: + BLOCK + BLOCK + BLOCK + } + } + goto LoopStart; + } +#undef BLOCK + } + } + + index0++; + } +} + +static PX_FORCE_INLINE void doBipartiteBoxPruning_Leaf( + PairArray* PX_RESTRICT pairManager, const bool* PX_RESTRICT lut, + Aggregate* PX_RESTRICT aggregate0, Aggregate* PX_RESTRICT aggregate1, const Bp::FilterGroup::Enum* PX_RESTRICT groups) +{ +#ifdef BIP_VERSION_1 + boxPruningKernel<0>(pairManager, lut, aggregate0, aggregate1, groups); + boxPruningKernel<1>(pairManager, lut, aggregate1, aggregate0, groups); +#else + const PxU32 nb0 = aggregate0->getNbAggregated(); + const PxU32 nb1 = aggregate1->getNbAggregated(); + const BoundsIndex* PX_RESTRICT remap0 = aggregate0->getIndices(); + const BoundsIndex* PX_RESTRICT remap1 = aggregate1->getIndices(); + const AABB_Xi* PX_RESTRICT boxes0_X = aggregate0->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes0_YZ = aggregate0->getBoundsYZ(); + const AABB_Xi* PX_RESTRICT boxes1_X = aggregate1->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes1_YZ = aggregate1->getBoundsYZ(); + boxPruningKernel<0>(pairManager, lut, nb0, remap0, boxes0_X, boxes0_YZ, nb1, remap1, boxes1_X, boxes1_YZ, groups); + boxPruningKernel<1>(pairManager, lut, nb1, remap1, boxes1_X, boxes1_YZ, nb0, remap0, boxes0_X, boxes0_YZ, groups); +#endif +} + + struct outputPair_Complete + { + outputPair_Complete(PairArray* pairManager, Aggregate* aggregate, const Bp::FilterGroup::Enum* groups, const bool* lut) : + mPairManager (pairManager), + mAggregate (aggregate), + mGroups (groups), + mLUT (lut) + { + } + + PX_FORCE_INLINE void outputPair(PxU32 index0, PxU32 index1) + { + const PxU32 aggIndex0 = mAggregate->getAggregated(index0); + const PxU32 aggIndex1 = mAggregate->getAggregated(index1); + + if(groupFiltering(mGroups[aggIndex0], mGroups[aggIndex1], mLUT)) + mPairManager->addPair(aggIndex0, aggIndex1); + } + + PairArray* mPairManager; + Aggregate* mAggregate; + const Bp::FilterGroup::Enum* mGroups; + const bool* mLUT; + }; + +static void doCompleteBoxPruning_Leaf( PairArray* PX_RESTRICT pairManager, const bool* PX_RESTRICT lut, + Aggregate* PX_RESTRICT aggregate, const Bp::FilterGroup::Enum* PX_RESTRICT groups) +{ + outputPair_Complete pm(pairManager, aggregate, groups, lut); + const PxU32 nb = aggregate->getNbAggregated(); + const AABB_Xi* PX_RESTRICT boxes_X = aggregate->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes_YZ = aggregate->getBoundsYZ(); + + PxU32 index0 = 0; + PxU32 runningIndex = 0; + while(runningIndex(&boxes_YZ[runningIndex]); + const char* const CurrentBoxListX = reinterpret_cast(&boxes_X[runningIndex]); + + if(!gUnrollLoop) + { + while(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) + { + const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2); +#ifdef ABP_SIMD_OVERLAP + if(SIMD_OVERLAP_TEST_14a(box)) +#else + if(intersect2D(box0, *reinterpret_cast(box))) +#endif + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes_X))>>3; + pm.outputPair(index0, Index); + } + Offset += 8; + } + } + else + { + +#define VERSION4c +#ifdef VERSION4c +#define VERSION3 // Enable this as our safe loop +#ifdef ABP_SIMD_OVERLAP + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(SIMD_OVERLAP_TEST_14a(box)) \ + goto label; } +#else + #define BLOCK4(x, label) {const AABB_YZ* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(intersect2D(box0, *box)) \ + goto label; } +#endif + goto StartLoop4; + CODEALIGN16 +FoundOverlap3: + Offset += 8; + CODEALIGN16 +FoundOverlap2: + Offset += 8; + CODEALIGN16 +FoundOverlap1: + Offset += 8; + CODEALIGN16 +FoundOverlap0: + Offset += 8; + CODEALIGN16 +FoundOverlap: + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - 8 - reinterpret_cast(boxes_X))>>3; + pm.outputPair(index0, Index); + } + CODEALIGN16 +StartLoop4: + while(*reinterpret_cast(CurrentBoxListX + Offset + 8*5)<=maxLimit) + { + BLOCK4(0, FoundOverlap0) + BLOCK4(8, FoundOverlap1) + BLOCK4(16, FoundOverlap2) + BLOCK4(24, FoundOverlap3) + Offset += 40; + BLOCK4(-8, FoundOverlap) + } +#endif + +#define VERSION3 +#ifdef VERSION3 +#ifdef ABP_SIMD_OVERLAP + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(SIMD_OVERLAP_TEST_14a(reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto BeforeLoop; \ + Offset += 8; +#else + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(intersect2D(box0, *reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto BeforeLoop; \ + Offset += 8; +#endif + + goto StartLoop; + CODEALIGN16 +BeforeLoop: + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes_X))>>3; + pm.outputPair(index0, Index); + Offset += 8; + } + CODEALIGN16 +StartLoop: + BLOCK + BLOCK + BLOCK + BLOCK + BLOCK + } + } + } + } + goto StartLoop; + } +#endif + } + } + + index0++; + } +} + +///// + +class PersistentActorAggregatePair : public PersistentPairs +{ + public: + PersistentActorAggregatePair(Aggregate* aggregate, ShapeHandle actorHandle); + virtual ~PersistentActorAggregatePair() {} + + virtual void findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ); + virtual bool update(AABBManager& manager, BpCacheData* data); + + ShapeHandle mAggregateHandle; + ShapeHandle mActorHandle; + Aggregate* mAggregate; +}; + +PersistentActorAggregatePair::PersistentActorAggregatePair(Aggregate* aggregate, ShapeHandle actorHandle) : + mAggregateHandle (aggregate->mIndex), + mActorHandle (actorHandle), + mAggregate (aggregate) +{ +} + +void PersistentActorAggregatePair::findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) +{ + if(0) + { + Aggregate singleActor(INVALID_ID, false); + singleActor.addAggregated(mActorHandle); + singleActor.allocateBounds(); + singleActor.computeBounds(bounds, contactDistances); + singleActor.getSortedMinBounds(); + mAggregate->getSortedMinBounds(); + doBipartiteBoxPruning_Leaf(&pairs, lut, &singleActor, mAggregate, groups); + } + else + { + + mAggregate->getSortedMinBounds(); + const PxU32 nb0 = mAggregate->getNbAggregated(); + const BoundsIndex* PX_RESTRICT remap0 = mAggregate->getIndices(); + const AABB_Xi* PX_RESTRICT boxes0_X = mAggregate->getBoundsX(); + const AABB_YZ* PX_RESTRICT boxes0_YZ = mAggregate->getBoundsYZ(); + + PX_ALIGN(16, AABB_Xi inflatedBoundsX[1+NB_SENTINELS]); + PX_ALIGN(16, AABB_YZ inflatedBoundsYZ); + + // Compute bounds + { + PX_ALIGN(16, PxVec4) boxMin; + PX_ALIGN(16, PxVec4) boxMax; + const BoundsIndex actorHandle = mActorHandle; + const PxBounds3& b = bounds[actorHandle]; + const Vec4V offsetV = V4Load(contactDistances[actorHandle]); + const Vec4V minimumV = V4Sub(V4LoadU(&b.minimum.x), offsetV); + const Vec4V maximumV = V4Add(V4LoadU(&b.maximum.x), offsetV); + V4StoreA(minimumV, &boxMin.x); + V4StoreA(maximumV, &boxMax.x); + inflatedBoundsX[0].initFromPxVec4(boxMin, boxMax); + inflatedBoundsYZ.initFromPxVec4(boxMin, boxMax); + + for(PxU32 i=0;i(&pairs, lut, nb0, remap0, boxes0_X, boxes0_YZ, nb1, remap1, boxes1_X, boxes1_YZ, groups); + boxPruningKernel<1>(&pairs, lut, nb1, remap1, boxes1_X, boxes1_YZ, nb0, remap0, boxes0_X, boxes0_YZ, groups); + + } +} + +bool PersistentActorAggregatePair::update(AABBManager& manager, BpCacheData* data) +{ + if(mShouldBeDeleted || shouldPairBeDeleted(manager.mGroups, mAggregateHandle, mActorHandle)) + return true; + + if(!mAggregate->getNbAggregated()) // PT: needed with lazy empty actors + return true; + + if(mAggregate->isDirty() || manager.mChangedHandleMap.boundedTest(mActorHandle)) + manager.updatePairs(*this, data); + + return false; +} + +///// + +class PersistentAggregateAggregatePair : public PersistentPairs +{ + public: + PersistentAggregateAggregatePair(Aggregate* aggregate0, Aggregate* aggregate1); + virtual ~PersistentAggregateAggregatePair() {} + + virtual void findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ); + virtual bool update(AABBManager& manager, BpCacheData*); + + ShapeHandle mAggregateHandle0; + ShapeHandle mAggregateHandle1; + Aggregate* mAggregate0; + Aggregate* mAggregate1; +}; + +PersistentAggregateAggregatePair::PersistentAggregateAggregatePair(Aggregate* aggregate0, Aggregate* aggregate1) : + mAggregateHandle0 (aggregate0->mIndex), + mAggregateHandle1 (aggregate1->mIndex), + mAggregate0 (aggregate0), + mAggregate1 (aggregate1) +{ +} + +void PersistentAggregateAggregatePair::findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT /*bounds*/, const float* PX_RESTRICT /*contactDistances*/, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) +{ + mAggregate0->getSortedMinBounds(); + mAggregate1->getSortedMinBounds(); + doBipartiteBoxPruning_Leaf(&pairs, lut, mAggregate0, mAggregate1, groups); +} + +bool PersistentAggregateAggregatePair::update(AABBManager& manager, BpCacheData* data) +{ + if(mShouldBeDeleted || shouldPairBeDeleted(manager.mGroups, mAggregateHandle0, mAggregateHandle1)) + return true; + + if(!mAggregate0->getNbAggregated() || !mAggregate1->getNbAggregated()) // PT: needed with lazy empty actors + return true; + + if(mAggregate0->isDirty() || mAggregate1->isDirty()) + manager.updatePairs(*this, data); + + return false; +} + +///// + +class PersistentSelfCollisionPairs : public PersistentPairs +{ + public: + PersistentSelfCollisionPairs(Aggregate* aggregate); + virtual ~PersistentSelfCollisionPairs() {} + + virtual void findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ); + + Aggregate* mAggregate; +}; + +PersistentSelfCollisionPairs::PersistentSelfCollisionPairs(Aggregate* aggregate) : + mAggregate (aggregate) +{ +} + +void PersistentSelfCollisionPairs::findOverlaps(PairArray& pairs, const PxBounds3* PX_RESTRICT/*bounds*/, const float* PX_RESTRICT/*contactDistances*/, const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) +{ + mAggregate->getSortedMinBounds(); + doCompleteBoxPruning_Leaf(&pairs, lut, mAggregate, groups); +} + +///// + +Aggregate::Aggregate(BoundsIndex index, bool selfCollisions) : + mIndex (index), + mInflatedBoundsX (NULL), + mInflatedBoundsYZ (NULL), + mAllocatedSize (0), + mDirtySort (false) +{ + resetDirtyState(); + mSelfCollisionPairs = selfCollisions ? PX_NEW(PersistentSelfCollisionPairs)(this) : NULL; +} + +Aggregate::~Aggregate() +{ + PX_FREE_AND_RESET(mInflatedBoundsYZ); + PX_FREE_AND_RESET(mInflatedBoundsX); + + if(mSelfCollisionPairs) + PX_DELETE_AND_RESET(mSelfCollisionPairs); +} + +void Aggregate::sortBounds() +{ + mDirtySort = false; + const PxU32 nbObjects = getNbAggregated(); + if(nbObjects<2) + return; + + { + PX_ALLOCA(minPosBounds, InflatedType, nbObjects+1); + bool alreadySorted = true; + InflatedType previousB = mInflatedBoundsX[0].mMinX; + minPosBounds[0] = previousB; + for(PxU32 i=1;i copy = mAggregated; + AABB_Xi* boundsXCopy = reinterpret_cast(PX_ALLOC(sizeof(AABB_Xi)*(nbObjects), "mInflatedBounds")); + AABB_YZ* boundsYZCopy = reinterpret_cast(PX_ALLOC(sizeof(AABB_YZ)*(nbObjects), "mInflatedBounds")); + PxMemCopy(boundsXCopy, mInflatedBoundsX, nbObjects*sizeof(AABB_Xi)); + PxMemCopy(boundsYZCopy, mInflatedBoundsYZ, nbObjects*sizeof(AABB_YZ)); + const PxU32* Sorted = mRS.GetRanks(); + for(PxU32 i=0;i copy = mAggregated; // PT: TODO: revisit this, avoid the copy like we do for the other buffers + AABB_Xi* sortedBoundsX = reinterpret_cast(PX_ALLOC(sizeof(AABB_Xi)*(nbObjects+NB_SENTINELS), "mInflatedBounds")); + AABB_YZ* sortedBoundsYZ = reinterpret_cast(PX_ALLOC(sizeof(AABB_YZ)*(nbObjects), "mInflatedBounds")); + + const PxU32* Sorted = mRS.GetRanks(); + for(PxU32 i=0;i(PX_ALLOC(sizeof(AABB_Xi)*(size+NB_SENTINELS), "mInflatedBounds")); + mInflatedBoundsYZ = reinterpret_cast(PX_ALLOC(sizeof(AABB_YZ)*(size), "mInflatedBounds")); + } +} + +void Aggregate::computeBounds(const PxBounds3* PX_RESTRICT bounds, const float* PX_RESTRICT contactDistances) /*PX_RESTRICT*/ +{ +// PX_PROFILE_ZONE("Aggregate::computeBounds",0); + + const PxU32 size = getNbAggregated(); + PX_ASSERT(size); + + // PT: TODO: delay the conversion to integers until we sort (i.e. really need) the aggregated bounds? + PX_ALIGN(16, PxVec4) boxMin; + PX_ALIGN(16, PxVec4) boxMax; + + const PxU32 lookAhead = 4; + Vec4V minimumV; + Vec4V maximumV; + { + const BoundsIndex index0 = getAggregated(0); + const PxU32 last = PxMin(lookAhead, size-1); + for(PxU32 i=1;i<=last;i++) + { + const BoundsIndex index = getAggregated(i); + Ps::prefetchLine(bounds + index, 0); + Ps::prefetchLine(contactDistances + index, 0); + } + const PxBounds3& b = bounds[index0]; + const Vec4V offsetV = V4Load(contactDistances[index0]); + minimumV = V4Sub(V4LoadU(&b.minimum.x), offsetV); + maximumV = V4Add(V4LoadU(&b.maximum.x), offsetV); + V4StoreA(minimumV, &boxMin.x); + V4StoreA(maximumV, &boxMax.x); + mInflatedBoundsX[0].initFromPxVec4(boxMin, boxMax); + mInflatedBoundsYZ[0].initFromPxVec4(boxMin, boxMax); + } + + for(PxU32 i=1;i& contactDistance, + PxU32 maxNbAggregates, PxU32 maxNbShapes, Ps::VirtualAllocator& allocator, PxU64 contextID, + PxPairFilteringMode::Enum kineKineFilteringMode, PxPairFilteringMode::Enum staticKineFilteringMode) : + mPostBroadPhase2(contextID, *this), + mPostBroadPhase3(contextID, this, "AABBManager::postBroadPhaseStage3"), + mFinalizeUpdateTask (contextID), + mChangedHandleMap (allocator), + mGroups (allocator), + mContactDistance (contactDistance), + mVolumeData (PX_DEBUG_EXP("AABBManager::mVolumeData")), + mAddedHandles (allocator), + mUpdatedHandles (allocator), + mRemovedHandles (allocator), + mBroadPhase (bp), + mBoundsArray (boundsArray), + mOutOfBoundsObjects (PX_DEBUG_EXP("AABBManager::mOutOfBoundsObjects")), + mOutOfBoundsAggregates (PX_DEBUG_EXP("AABBManager::mOutOfBoundsAggregates")), + //mCreatedOverlaps { Ps::Array(PX_DEBUG_EXP("AABBManager::mCreatedOverlaps")) }, + //mDestroyedOverlaps { Ps::Array(PX_DEBUG_EXP("AABBManager::mDestroyedOverlaps")) }, + mScratchAllocator (NULL), + mNarrowPhaseUnblockTask (NULL), + mUsedSize (0), + mOriginShifted (false), + mPersistentStateChanged (true), + mNbAggregates (0), + mFirstFreeAggregate (PX_INVALID_U32), + mTimestamp (0), +#ifdef BP_USE_AGGREGATE_GROUP_TAIL + mAggregateGroupTide (PxU32(Bp::FilterGroup::eAGGREGATE_BASE)), +#endif + mContextID (contextID) +{ + PX_UNUSED(maxNbAggregates); // PT: TODO: use it or remove it + reserveShapeSpace(PxMax(maxNbShapes, 1u)); + +// mCreatedOverlaps.reserve(16000); +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + { + const bool discardKineKine = kineKineFilteringMode==PxPairFilteringMode::eKILL ? true : false; + const bool discardStaticKine = staticKineFilteringMode==PxPairFilteringMode::eKILL ? true : false; + + for(int j=0;jsecond); +} + +void AABBManager::destroy() +{ + releasePairs(mActorAggregatePairs); + releasePairs(mAggregateAggregatePairs); + + const PxU32 nb = mAggregates.size(); + for(PxU32 i=0;i(mAggregates[currentFree])); + } + + if(!found) + { + Aggregate* a = mAggregates[i]; + PX_DELETE(a); + } + } + + BpCacheData* entry = static_cast(mBpThreadContextPool.pop()); + while (entry) + { + entry->~BpCacheData(); + PX_FREE(entry); + entry = static_cast(mBpThreadContextPool.pop()); + } + + PX_DELETE(this); +} + +/*bool AABBManager::checkID(ShapeHandle id) +{ + for(AggPairMap::Iterator iter = mActorAggregatePairs.getIterator(); !iter.done(); ++iter) + { + PersistentActorAggregatePair* p = static_cast(iter->second); + if(p->mActorHandle==id || p->mAggregateHandle==id) + return false; + } + + for(AggPairMap::Iterator iter = mAggregateAggregatePairs.getIterator(); !iter.done(); ++iter) + { + PersistentAggregateAggregatePair* p = static_cast(iter->second); + if(p->mAggregateHandle0==id || p->mAggregateHandle1==id) + return false; + } + return true; +}*/ + +static void removeAggregateFromDirtyArray(Aggregate* aggregate, Ps::Array& dirtyAggregates) +{ + // PT: TODO: do this lazily like for interactions? + if(aggregate->isDirty()) + { + const PxU32 dirtyIndex = aggregate->mDirtyIndex; + PX_ASSERT(dirtyAggregates[dirtyIndex]==aggregate); + dirtyAggregates.replaceWithLast(dirtyIndex); + if(dirtyIndexmDirtyIndex = dirtyIndex; + aggregate->resetDirtyState(); + } + else + { + PX_ASSERT(!dirtyAggregates.findAndReplaceWithLast(aggregate)); + } +} + +void AABBManager::reserveSpaceForBounds(BoundsIndex index) +{ + if ((index+1) >= mVolumeData.size()) + reserveShapeSpace(index+1); + + resetEntry(index); //KS - make sure this entry is flagged as invalid +} + +// PT: TODO: what is the "userData" here? +bool AABBManager::addBounds(BoundsIndex index, PxReal contactDistance, Bp::FilterGroup::Enum group, void* userData, AggregateHandle aggregateHandle, ElementType::Enum volumeType) +{ +// PX_ASSERT(checkID(index)); + + initEntry(index, contactDistance, group, userData); + mVolumeData[index].setVolumeType(volumeType); + + if(aggregateHandle==PX_INVALID_U32) + { + mVolumeData[index].setSingleActor(); + + addBPEntry(index); + + mPersistentStateChanged = true; + } + else + { +#if PX_CHECKED + if(aggregateHandle>=mAggregates.size()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "AABBManager::addBounds - aggregateId out of bounds\n"); + return false; + } + +/* { + PxU32 firstFreeAggregate = mFirstFreeAggregate; + while(firstFreeAggregate!=PX_INVALID_U32) + { + if(firstFreeAggregate==aggregateHandle) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "AABBManager::destroyAggregate - aggregate has already been removed\n"); + return BP_INVALID_BP_HANDLE; + } + firstFreeAggregate = PxU32(reinterpret_cast(mAggregates[firstFreeAggregate])); + } + }*/ +#endif + mVolumeData[index].setAggregated(aggregateHandle); + + mPersistentStateChanged = true; // PT: TODO: do we need this here? + + Aggregate* aggregate = getAggregateFromHandle(aggregateHandle); + + { + // PT: schedule the aggregate for BP insertion here, if we just added its first shape + if(!aggregate->getNbAggregated()) + addBPEntry(aggregate->mIndex); + + aggregate->addAggregated(index); + + // PT: new actor added to aggregate => mark dirty to recompute bounds later + aggregate->markAsDirty(mDirtyAggregates); + } + } + + // PT: TODO: remove or use this return value. Currently useless since always true. Gives birth to unreachable code in callers. + return true; +} + +void AABBManager::removeBounds(BoundsIndex index) +{ + // PT: TODO: shouldn't it be compared to mUsedSize? + PX_ASSERT(index < mVolumeData.size()); + + if(mVolumeData[index].isSingleActor()) + { + removeBPEntry(index); + + mPersistentStateChanged = true; + } + else + { + PX_ASSERT(mVolumeData[index].isAggregated()); + + const AggregateHandle aggregateHandle = mVolumeData[index].getAggregateOwner(); + Aggregate* aggregate = getAggregateFromHandle(aggregateHandle); + bool status = aggregate->removeAggregated(index); + (void)status; +// PX_ASSERT(status); // PT: can be false when >128 shapes + + // PT: remove empty aggregates, otherwise the BP will crash with empty bounds + if(!aggregate->getNbAggregated()) + { + removeBPEntry(aggregate->mIndex); + removeAggregateFromDirtyArray(aggregate, mDirtyAggregates); + } + else + aggregate->markAsDirty(mDirtyAggregates); // PT: actor removed from aggregate => mark dirty to recompute bounds later + + mPersistentStateChanged = true; // PT: TODO: do we need this here? + } + + resetEntry(index); +} + +// PT: TODO: the userData is actually a PxAggregate pointer. Maybe we could expose/use that. +AggregateHandle AABBManager::createAggregate(BoundsIndex index, Bp::FilterGroup::Enum group, void* userData, const bool selfCollisions) +{ +// PX_ASSERT(checkID(index)); + + Aggregate* aggregate = PX_NEW(Aggregate)(index, selfCollisions); + + AggregateHandle handle; + if(mFirstFreeAggregate==PX_INVALID_U32) + { + handle = mAggregates.size(); + mAggregates.pushBack(aggregate); + } + else + { + handle = mFirstFreeAggregate; + mFirstFreeAggregate = PxU32(reinterpret_cast(mAggregates[mFirstFreeAggregate])); + mAggregates[handle] = aggregate; + } + +#ifdef BP_USE_AGGREGATE_GROUP_TAIL +/* #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + PxU32 id = index; + id<<=2; + id|=FilterType::AGGREGATE; + initEntry(index, 0.0f, Bp::FilterGroup::Enum(id), userData); + #endif*/ + initEntry(index, 0.0f, getAggregateGroup(), userData); + PX_UNUSED(group); +#else + initEntry(index, 0.0f, group, userData); +#endif + + mVolumeData[index].setAggregate(handle); + + mBoundsArray.setBounds(PxBounds3::empty(), index); // PT: no need to set mPersistentStateChanged since "setBounds" already does something similar + + mNbAggregates++; + + // PT: we don't add empty aggregates to mAddedHandleMap yet, since they make the BP crash. + return handle; +} + +bool AABBManager::destroyAggregate(BoundsIndex& index_, Bp::FilterGroup::Enum& group_, AggregateHandle aggregateHandle) +{ +#if PX_CHECKED + if(aggregateHandle>=mAggregates.size()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "AABBManager::destroyAggregate - aggregateId out of bounds\n"); + return false; + } + + { + PxU32 firstFreeAggregate = mFirstFreeAggregate; + while(firstFreeAggregate!=PX_INVALID_U32) + { + if(firstFreeAggregate==aggregateHandle) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "AABBManager::destroyAggregate - aggregate has already been removed\n"); + return false; + } + firstFreeAggregate = PxU32(reinterpret_cast(mAggregates[firstFreeAggregate])); + } + } +#endif + + Aggregate* aggregate = getAggregateFromHandle(aggregateHandle); + +#if PX_CHECKED + if(aggregate->getNbAggregated()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "AABBManager::destroyAggregate - aggregate still has bounds that needs removed\n"); + return false; + } +#endif + + const BoundsIndex index = aggregate->mIndex; + removeAggregateFromDirtyArray(aggregate, mDirtyAggregates); + + if(mAddedHandleMap.test(index)) // PT: if object had been added this frame... + mAddedHandleMap.reset(index); // PT: ...then simply revert the previous operation locally (it hasn't been passed to the BP yet). + else if(aggregate->getNbAggregated()) // PT: else we need to remove it from the BP if it has been added there. If there's no aggregated + mRemovedHandleMap.set(index); // PT: shapes then the aggregate has never been added, or already removed. + + PX_DELETE(aggregate); + mAggregates[aggregateHandle] = reinterpret_cast(size_t(mFirstFreeAggregate)); + mFirstFreeAggregate = aggregateHandle; + + // PT: TODO: shouldn't it be compared to mUsedSize? + PX_ASSERT(index < mVolumeData.size()); + + index_ = index; + group_ = mGroups[index]; + +#ifdef BP_USE_AGGREGATE_GROUP_TAIL + releaseAggregateGroup(mGroups[index]); +#endif + resetEntry(index); + + mPersistentStateChanged = true; + + PX_ASSERT(mNbAggregates); + mNbAggregates--; + + return true; +} + +void AABBManager::handleOriginShift() +{ + mOriginShifted = false; + mPersistentStateChanged = true; + // PT: TODO: isn't the following loop potentially updating removed objects? + // PT: TODO: check that aggregates code is correct here + for(PxU32 i=0; igetNbAggregated()) + { + aggregate->markAsDirty(mDirtyAggregates); + aggregate->allocateBounds(); + aggregate->computeBounds(mBoundsArray.begin(), mContactDistance.begin()); + mBoundsArray.begin()[aggregate->mIndex] = aggregate->getMergedBounds(); + if(!mAddedHandleMap.test(i)) + mUpdatedHandles.pushBack(i); // PT: TODO: BoundsIndex-to-ShapeHandle confusion here + } + } + } + } +} + +void AggregateBoundsComputationTask::runInternal() +{ + const BoundsArray& boundArray = mManager->getBoundsArray(); + const float* contactDistances = mManager->getContactDistances(); + + PxU32 size = mNbToGo; + Aggregate** currentAggregate = mAggregates + mStart; + while(size--) + { + if(size) + { + Aggregate* nextAggregate = *(currentAggregate+1); + Ps::prefetchLine(nextAggregate, 0); + Ps::prefetchLine(nextAggregate, 64); + } + + (*currentAggregate)->computeBounds(boundArray.begin(), contactDistances); + currentAggregate++; + } +} + +void FinalizeUpdateTask::runInternal() +{ + mManager->finalizeUpdate(mNumCpuTasks, mScratchAllocator, getContinuation(), mNarrowPhaseUnlockTask); +} + +void AABBManager::startAggregateBoundsComputationTasks(PxU32 nbToGo, PxU32 numCpuTasks, Cm::FlushPool& flushPool) +{ + const PxU32 nbAggregatesPerTask = nbToGo > numCpuTasks ? nbToGo / numCpuTasks : nbToGo; + + // PT: TODO: better load balancing + + PxU32 start = 0; + while(nbToGo) + { + AggregateBoundsComputationTask* T = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(AggregateBoundsComputationTask)), AggregateBoundsComputationTask(mContextID)); + + const PxU32 nb = nbToGo < nbAggregatesPerTask ? nbToGo : nbAggregatesPerTask; + T->Init(this, start, nb, mDirtyAggregates.begin()); + start += nb; + nbToGo -= nb; + + T->setContinuation(&mFinalizeUpdateTask); + T->removeReference(); + } +} + +void AABBManager::updateAABBsAndBP(PxU32 numCpuTasks, Cm::FlushPool& flushPool, PxcScratchAllocator* scratchAllocator, bool hasContactDistanceUpdated, PxBaseTask* continuation, PxBaseTask* narrowPhaseUnlockTask) +{ + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP", getContextId()); + + mPersistentStateChanged = mPersistentStateChanged || hasContactDistanceUpdated; + + mScratchAllocator = scratchAllocator; + mNarrowPhaseUnblockTask = narrowPhaseUnlockTask; + + const bool singleThreaded = gSingleThreaded || numCpuTasks<2; + if(!singleThreaded) + { + PX_ASSERT(numCpuTasks); + mFinalizeUpdateTask.Init(this, numCpuTasks, scratchAllocator, narrowPhaseUnlockTask); + mFinalizeUpdateTask.setContinuation(continuation); + } + + // Add + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - add", getContextId()); + + resetOrClear(mAddedHandles); + + const PxU32* bits = mAddedHandleMap.getWords(); + if(bits) + { + const PxU32 lastSetBit = mAddedHandleMap.findLast(); + for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) + { + for(PxU32 b = bits[w]; b; b &= b-1) + { + const BoundsIndex handle = PxU32(w<<5|Ps::lowestSetBit(b)); + PX_ASSERT(!mVolumeData[handle].isAggregated()); + mAddedHandles.pushBack(handle); // PT: TODO: BoundsIndex-to-ShapeHandle confusion here + } + } + } + } + + // Update + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - update", getContextId()); + + resetOrClear(mUpdatedHandles); + if(!mOriginShifted) + { + // PT: TODO: + // - intercept calls marking aggregateD shapes dirty, in order to mark their aggregates dirty at the same time. That way we don't discover + // them while parsing the map, i.e. the map is already fully complete when the parsing begins (no need to parse twice etc). + // - once this is done, aggregateD shapes can be ignored during parsing (since we only needed them to go to their aggregates) + // - we can then handle aggregates while parsing the map, i.e. there's no need for sorting anymore. + // - there will be some thoughts to do about the dirty aggregates coming from the added map parsing: we still need to compute their bounds, + // but we don't want to add these to mUpdatedHandles (since they're already in mAddedHandles) + // - we still need the set of dirty aggregates post broadphase, but we don't want to re-parse the full map for self-collisions. So we may still + // need to put dirty aggregates in an array, but that might be simplified now + // - the 'isDirty' checks to updatePairs can use the update map though - but the boundedTest is probably more expensive than the current test + + // PT: TODO: another idea: just output all aggregate handles by default then have a pass on mUpdatedHandles to remove them if that wasn't actually necessary + // ...or just drop the artificial requirement for aggregates... + + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - update - bitmap iteration", getContextId()); + + const PxU32* bits = mChangedHandleMap.getWords(); + if(bits) + { + const PxU32 lastSetBit = mChangedHandleMap.findLast(); + for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) + { + for(PxU32 b = bits[w]; b; b &= b-1) + { + const BoundsIndex handle = PxU32(w<<5|Ps::lowestSetBit(b)); + PX_ASSERT(!mRemovedHandleMap.test(handle)); // a handle may only be updated and deleted if it was just added. + PX_ASSERT(!mVolumeData[handle].isAggregate()); // PT: make sure changedShapes doesn't contain aggregates + + if(mAddedHandleMap.test(handle)) // just-inserted handles may also be marked updated, so skip them + continue; + + if(mVolumeData[handle].isSingleActor()) + { + PX_ASSERT(mGroups[handle] != Bp::FilterGroup::eINVALID); + mUpdatedHandles.pushBack(handle); // PT: TODO: BoundsIndex-to-ShapeHandle confusion here + } + else + { + PX_ASSERT(mVolumeData[handle].isAggregated()); + const AggregateHandle aggregateHandle = mVolumeData[handle].getAggregateOwner(); + Aggregate* aggregate = getAggregateFromHandle(aggregateHandle); + // PT: an actor from the aggregate has been updated => mark dirty to recompute bounds later + // PT: we don't recompute the bounds right away since multiple actors from the same aggregate may have changed. + aggregate->markAsDirty(mDirtyAggregates); + } + } + } + } + } + + const PxU32 size = mDirtyAggregates.size(); + if(size) + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - update - dirty iteration", getContextId()); + for(PxU32 i=0;iallocateBounds(); + if(singleThreaded) + { + aggregate->computeBounds(mBoundsArray.begin(), mContactDistance.begin()); + mBoundsArray.begin()[aggregate->mIndex] = aggregate->getMergedBounds(); + } + + // PT: Can happen when an aggregate has been created and then its actors have been changed (with e.g. setLocalPose) + // before a BP call. + if(!mAddedHandleMap.test(aggregate->mIndex)) + mUpdatedHandles.pushBack(aggregate->mIndex); // PT: TODO: BoundsIndex-to-ShapeHandle confusion here + } + + if(!singleThreaded) + startAggregateBoundsComputationTasks(size, numCpuTasks, flushPool); + + // PT: we're already sorted if no dirty-aggregates are involved + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - update - sort", getContextId()); + + mPersistentStateChanged = true; // PT: was previously set multiple times within 'computeBounds' + // PT: TODO: remove this + Ps::sort(mUpdatedHandles.begin(), mUpdatedHandles.size()); + } + } + } + else + { + handleOriginShift(); + } + } + + // Remove + { + PX_PROFILE_ZONE("AABBManager::updateAABBsAndBP - remove", getContextId()); + + resetOrClear(mRemovedHandles); + + const PxU32* bits = mRemovedHandleMap.getWords(); + if(bits) + { + const PxU32 lastSetBit = mRemovedHandleMap.findLast(); + for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) + { + for(PxU32 b = bits[w]; b; b &= b-1) + { + const BoundsIndex handle = PxU32(w<<5|Ps::lowestSetBit(b)); + PX_ASSERT(!mVolumeData[handle].isAggregated()); + mRemovedHandles.pushBack(handle); // PT: TODO: BoundsIndex-to-ShapeHandle confusion here + } + } + } + } + + ///// + + // PT: TODO: do we need to run these threads when we origin-shifted everything before? + if(singleThreaded) + finalizeUpdate(numCpuTasks, scratchAllocator, continuation, narrowPhaseUnlockTask); + else + mFinalizeUpdateTask.removeReference(); +} + +void AABBManager::finalizeUpdate(PxU32 numCpuTasks, PxcScratchAllocator* scratchAllocator, PxBaseTask* continuation, PxBaseTask* narrowPhaseUnlockTask) +{ + PX_PROFILE_ZONE("AABBManager::finalizeUpdate", getContextId()); + + const bool singleThreaded = gSingleThreaded || numCpuTasks<2; + if(!singleThreaded) + { + const PxU32 size = mDirtyAggregates.size(); + for(PxU32 i=0;imIndex] = aggregate->getMergedBounds(); + } + } + + const BroadPhaseUpdateData updateData( mAddedHandles.begin(), mAddedHandles.size(), + mUpdatedHandles.begin(), mUpdatedHandles.size(), + mRemovedHandles.begin(), mRemovedHandles.size(), + mBoundsArray.begin(), mGroups.begin(), +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + &mLUT[0][0], +#endif + mContactDistance.begin(), mBoundsArray.getCapacity(), + mPersistentStateChanged || mBoundsArray.hasChanged()); + mPersistentStateChanged = false; + + PX_ASSERT(updateData.isValid()); + + //KS - skip broad phase if there are no updated shapes. + if (updateData.getNumCreatedHandles() != 0 || updateData.getNumRemovedHandles() != 0 || updateData.getNumUpdatedHandles() != 0) + mBroadPhase.update(numCpuTasks, scratchAllocator, updateData, continuation, narrowPhaseUnlockTask); + else + narrowPhaseUnlockTask->removeReference(); +} + +static PX_FORCE_INLINE void createOverlap(Ps::Array* overlaps, const Ps::Array& volumeData, PxU32 id0, PxU32 id1) +{ +// overlaps.pushBack(AABBOverlap(volumeData[id0].userData, volumeData[id1].userData, handle)); + const ElementType::Enum volumeType = PxMax(volumeData[id0].getVolumeType(), volumeData[id1].getVolumeType()); + overlaps[volumeType].pushBack(AABBOverlap(reinterpret_cast(size_t(id0)), reinterpret_cast(size_t(id1)))); +} + +static PX_FORCE_INLINE void deleteOverlap(Ps::Array* overlaps, const Ps::Array& volumeData, PxU32 id0, PxU32 id1) +{ +// PX_ASSERT(volumeData[id0].userData); +// PX_ASSERT(volumeData[id1].userData); + if (volumeData[id0].getUserData() && volumeData[id1].getUserData()) // PT: TODO: no idea if this is the right thing to do or if it's normal to get null ptrs here + { + const ElementType::Enum volumeType = PxMax(volumeData[id0].getVolumeType(), volumeData[id1].getVolumeType()); +// overlaps.pushBack(AABBOverlap(volumeData[id0].userData, volumeData[id1].userData, handle)); + overlaps[volumeType].pushBack(AABBOverlap(reinterpret_cast(size_t(id0)), reinterpret_cast(size_t(id1)))); + } +} + +void PersistentPairs::outputDeletedOverlaps(Ps::Array* overlaps, const Ps::Array& volumeData) +{ + const PxU32 nbActivePairs = mPM.mNbActivePairs; + for(PxU32 i=0;i& volumeData, Ps::Array* createdOverlaps, Ps::Array* destroyedOverlaps) +{ + if(mTimestamp==timestamp) + return; + + mTimestamp = timestamp; + +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + findOverlaps(mPM, bounds, contactDistances, groups, lut); +#else + findOverlaps(mPM, bounds, contactDistances, groups); +#endif + + PxU32 i=0; + PxU32 nbActivePairs = mPM.mNbActivePairs; + while(imIndex==aggregateHandle); + return PX_NEW(PersistentActorAggregatePair)(aggregate, actorHandle); // PT: TODO: use a pool or something +} + +PersistentAggregateAggregatePair* AABBManager::createPersistentAggregateAggregatePair(ShapeHandle volA, ShapeHandle volB) +{ + PX_ASSERT(mVolumeData[volA].isAggregate()); + PX_ASSERT(mVolumeData[volB].isAggregate()); + const AggregateHandle h0 = mVolumeData[volA].getAggregate(); + const AggregateHandle h1 = mVolumeData[volB].getAggregate(); + Aggregate* aggregate0 = getAggregateFromHandle(h0); + Aggregate* aggregate1 = getAggregateFromHandle(h1); + PX_ASSERT(aggregate0->mIndex==volA); + PX_ASSERT(aggregate1->mIndex==volB); + return PX_NEW(PersistentAggregateAggregatePair)(aggregate0, aggregate1); // PT: TODO: use a pool or something +} + +void AABBManager::updatePairs(PersistentPairs& p, BpCacheData* data) +{ + if (data) + { +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + p.updatePairs(mTimestamp, mBoundsArray.begin(), mContactDistance.begin(), mGroups.begin(), &mLUT[0][0], mVolumeData, data->mCreatedPairs, data->mDeletedPairs); +#else + p.updatePairs(mTimestamp, mBoundsArray.begin(), mContactDistance.begin(), mGroups.begin(), mVolumeData, data->mCreatedPairs, data->mDeletedPairs); +#endif + } + else + { +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + p.updatePairs(mTimestamp, mBoundsArray.begin(), mContactDistance.begin(), mGroups.begin(), &mLUT[0][0], mVolumeData, mCreatedOverlaps, mDestroyedOverlaps); +#else + p.updatePairs(mTimestamp, mBoundsArray.begin(), mContactDistance.begin(), mGroups.begin(), mVolumeData, mCreatedOverlaps, mDestroyedOverlaps); +#endif + } +} + +void AABBManager::processBPCreatedPair(const BroadPhasePair& pair) +{ + PX_ASSERT(!mVolumeData[pair.mVolA].isAggregated()); + PX_ASSERT(!mVolumeData[pair.mVolB].isAggregated()); + const bool isSingleActorA = mVolumeData[pair.mVolA].isSingleActor(); + const bool isSingleActorB = mVolumeData[pair.mVolB].isSingleActor(); + + if(isSingleActorA && isSingleActorB) + { + createOverlap(mCreatedOverlaps, mVolumeData, pair.mVolA, pair.mVolB); // PT: regular actor-actor pair + return; + } + + // PT: TODO: check if this is needed + ShapeHandle volA = pair.mVolA; + ShapeHandle volB = pair.mVolB; + if(volBinsert(AggPair(volA, volB), newPair); + PX_UNUSED(status); + PX_ASSERT(status); + updatePairs(*newPair); +} + +void AABBManager::processBPDeletedPair(const BroadPhasePair& pair) +{ + PX_ASSERT(!mVolumeData[pair.mVolA].isAggregated()); + PX_ASSERT(!mVolumeData[pair.mVolB].isAggregated()); + const bool isSingleActorA = mVolumeData[pair.mVolA].isSingleActor(); + const bool isSingleActorB = mVolumeData[pair.mVolB].isSingleActor(); + + if(isSingleActorA && isSingleActorB) + { + deleteOverlap(mDestroyedOverlaps, mVolumeData, pair.mVolA, pair.mVolB); // PT: regular actor-actor pair + return; + } + + // PT: TODO: check if this is needed + ShapeHandle volA = pair.mVolA; + ShapeHandle volB = pair.mVolB; + if(volBfind(AggPair(volA, volB)); + PX_ASSERT(e); + p = e->second; + } + + p->outputDeletedOverlaps(mDestroyedOverlaps, mVolumeData); + p->mShouldBeDeleted = true; +} + +struct CreatedPairHandler +{ + static PX_FORCE_INLINE void processPair(AABBManager& manager, const BroadPhasePair& pair) { manager.processBPCreatedPair(pair); } +}; + +struct DeletedPairHandler +{ + static PX_FORCE_INLINE void processPair(AABBManager& manager, const BroadPhasePair& pair) { manager.processBPDeletedPair(pair); } +}; + +template +static void processBPPairs(PxU32 nbPairs, const BroadPhasePair* pairs, AABBManager& manager) +{ + // PT: TODO: figure out this ShapeHandle/BpHandle thing. Is it ok to use "BP_INVALID_BP_HANDLE" for a "ShapeHandle"? + ShapeHandle previousA = BP_INVALID_BP_HANDLE; + ShapeHandle previousB = BP_INVALID_BP_HANDLE; + + while(nbPairs--) + { + PX_ASSERT(pairs->mVolA!=BP_INVALID_BP_HANDLE); + PX_ASSERT(pairs->mVolB!=BP_INVALID_BP_HANDLE); + // PT: TODO: why is that test needed now? GPU broadphase? + if(pairs->mVolA != previousA || pairs->mVolB != previousB) + { + previousA = pairs->mVolA; + previousB = pairs->mVolB; + FunctionT::processPair(manager, *pairs); + } + pairs++; + } +} + +static void processAggregatePairs(AggPairMap& map, AABBManager& manager) +{ + // PT: TODO: hmmm we have a list of dirty aggregates but we don't have a list of dirty pairs. + // PT: not sure how the 3.4 trunk solves this but let's just iterate all pairs for now + // PT: atm we reuse this loop to delete removed interactions + // PT: TODO: in fact we could handle all the "lost pairs" stuff right there with extra aabb-abb tests + + // PT: TODO: replace with decent hash map - or remove the hashmap entirely and use a linear array + Ps::Array removedEntries; + for(AggPairMap::Iterator iter = map.getIterator(); !iter.done(); ++iter) + { + PersistentPairs* p = iter->second; + if(p->update(manager)) + { + removedEntries.pushBack(iter->first); + PX_DELETE(p); + } + } + for(PxU32 i=0;i* mArray; + PxU32 mStartIdx; + PxU32 mCount; + + PairData() : mArray(NULL), mStartIdx(0), mCount(0) + { + } +}; + +class ProcessAggPairsBase : public Cm::Task +{ +public: + static const PxU32 MaxPairs = 16; + + PairData mCreatedPairs[2]; + PairData mDestroyedPairs[2]; + + ProcessAggPairsBase(PxU64 contextID) : Cm::Task(contextID) + { + } + + void setCache(BpCacheData& data) + { + for (PxU32 i = 0; i < 2; ++i) + { + mCreatedPairs[i].mArray = &data.mCreatedPairs[i]; + mCreatedPairs[i].mStartIdx = data.mCreatedPairs[i].size(); + mDestroyedPairs[i].mArray = &data.mDeletedPairs[i]; + mDestroyedPairs[i].mStartIdx = data.mDeletedPairs[i].size(); + } + } + + void updateCounters() + { + for (PxU32 i = 0; i < 2; ++i) + { + mCreatedPairs[i].mCount = mCreatedPairs[i].mArray->size() - mCreatedPairs[i].mStartIdx; + mDestroyedPairs[i].mCount = mDestroyedPairs[i].mArray->size() - mDestroyedPairs[i].mStartIdx; + } + } +}; + + +class ProcessAggPairsParallelTask : public ProcessAggPairsBase +{ +public: + PersistentPairs* mPersistentPairs[MaxPairs]; + Bp::AggPair mAggPairs[MaxPairs]; + PxU32 mNbPairs; + AABBManager* mManager; + AggPairMap* mMap; + Ps::Mutex* mMutex; + const char* mName; + + ProcessAggPairsParallelTask(PxU64 contextID, Ps::Mutex* mutex, AABBManager* manager, AggPairMap* map, const char* name) : ProcessAggPairsBase(contextID), + mNbPairs(0), mManager(manager), mMap(map), mMutex(mutex), mName(name) + { + } + + + void runInternal() + { + BpCacheData* data = mManager->getBpCacheData(); + + setCache(*data); + + + Ps::InlineArray removedEntries; + for (PxU32 i = 0; i < mNbPairs; ++i) + { + if (mPersistentPairs[i]->update(*mManager, data)) + { + removedEntries.pushBack(mAggPairs[i]); + PX_DELETE(mPersistentPairs[i]); + } + } + + updateCounters(); + + mManager->putBpCacheData(data); + + + if (removedEntries.size()) + { + Ps::Mutex::ScopedLock lock(*mMutex); + for (PxU32 i = 0; i < removedEntries.size(); i++) + { + bool status = mMap->erase(removedEntries[i]); + PX_ASSERT(status); + PX_UNUSED(status); + } + } + + + } + + virtual const char* getName() const { return mName; } + +}; + +class SortAggregateBoundsParallel : public Cm::Task +{ +public: + static const PxU32 MaxPairs = 16; + Aggregate** mAggregates; + PxU32 mNbAggs; + + SortAggregateBoundsParallel(PxU64 contextID, Aggregate** aggs, PxU32 nbAggs) : Cm::Task(contextID), + mAggregates(aggs), mNbAggs(nbAggs) + { + } + + + void runInternal() + { + PX_PROFILE_ZONE("SortBounds", mContextID); + for (PxU32 i = 0; i < mNbAggs; i++) + { + Aggregate* aggregate = mAggregates[i]; + + aggregate->getSortedMinBounds(); + } + } + + virtual const char* getName() const { return "SortAggregateBoundsParallel"; } + +}; + + +class ProcessSelfCollisionPairsParallel : public ProcessAggPairsBase +{ +public: + Aggregate** mAggregates; + PxU32 mNbAggs; + AABBManager* mManager; + + ProcessSelfCollisionPairsParallel(PxU64 contextID, Aggregate** aggs, PxU32 nbAggs, AABBManager* manager) : ProcessAggPairsBase(contextID), + mAggregates(aggs), mNbAggs(nbAggs), mManager(manager) + { + } + + + void runInternal() + { + BpCacheData* data = mManager->getBpCacheData(); + setCache(*data); + PX_PROFILE_ZONE("ProcessSelfCollisionPairs", mContextID); + for (PxU32 i = 0; i < mNbAggs; i++) + { + Aggregate* aggregate = mAggregates[i]; + + if (aggregate->mSelfCollisionPairs) + mManager->updatePairs(*aggregate->mSelfCollisionPairs, data); + } + updateCounters(); + mManager->putBpCacheData(data); + } + + virtual const char* getName() const { return "ProcessSelfCollisionPairsParallel"; } + +}; + +static void processAggregatePairsParallel(AggPairMap& map, AABBManager& manager, Cm::FlushPool& flushPool, + PxBaseTask* continuation, const char* taskName, Ps::Array& pairTasks) +{ + // PT: TODO: hmmm we have a list of dirty aggregates but we don't have a list of dirty pairs. + // PT: not sure how the 3.4 trunk solves this but let's just iterate all pairs for now + // PT: atm we reuse this loop to delete removed interactions + // PT: TODO: in fact we could handle all the "lost pairs" stuff right there with extra aabb-abb tests + + // PT: TODO: replace with decent hash map - or remove the hashmap entirely and use a linear array + + manager.mMapLock.lock(); + + ProcessAggPairsParallelTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ProcessAggPairsParallelTask)), ProcessAggPairsParallelTask)(0, &manager.mMapLock, &manager, &map, taskName); + + PxU32 startIdx = pairTasks.size(); + + for (AggPairMap::Iterator iter = map.getIterator(); !iter.done(); ++iter) + { + task->mAggPairs[task->mNbPairs] = iter->first; + task->mPersistentPairs[task->mNbPairs++] = iter->second; + if (task->mNbPairs == ProcessAggPairsParallelTask::MaxPairs) + { + pairTasks.pushBack(task); + task->setContinuation(continuation); + task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ProcessAggPairsParallelTask)), ProcessAggPairsParallelTask)(0, &manager.mMapLock, &manager, &map, taskName); + } + } + + manager.mMapLock.unlock(); + + for (PxU32 i = startIdx; i < pairTasks.size(); ++i) + { + pairTasks[i]->removeReference(); + } + + if (task->mNbPairs) + { + pairTasks.pushBack(task); + task->setContinuation(continuation); + task->removeReference(); + //task->runInternal(); + } +} + +void AABBManager::postBroadPhase(PxBaseTask* continuation, PxBaseTask* narrowPhaseUnlockTask, Cm::FlushPool& flushPool) +{ + PX_PROFILE_ZONE("AABBManager::postBroadPhase", getContextId()); + + //KS - There is a continuation task for discrete broad phase, but not for CCD broad phase. PostBroadPhase for CCD broad phase runs in-line. + //This probably should be revisited but it means that we can't use the parallel codepath within CCD. + if (continuation) + { + mPostBroadPhase3.setContinuation(continuation); + mPostBroadPhase2.setContinuation(&mPostBroadPhase3); + } + + mTimestamp++; + + // PT: TODO: consider merging mCreatedOverlaps & mDestroyedOverlaps + // PT: TODO: revisit memory management of mCreatedOverlaps & mDestroyedOverlaps + + //KS - if we ran broad phase, fetch the results now + if(mAddedHandles.size() != 0 || mUpdatedHandles.size() != 0 || mRemovedHandles.size() != 0) + mBroadPhase.fetchBroadPhaseResults(narrowPhaseUnlockTask); + + for(PxU32 i=0; i(mBroadPhase.getNbCreatedPairs(), mBroadPhase.getCreatedPairs(), *this); + processBPPairs(mBroadPhase.getNbDeletedPairs(), mBroadPhase.getDeletedPairs(), *this); + } + + { + //If there is a continuation task, then this is not part of CCD, so we can trigger bounds to be recomputed in parallel before pair generation runs during + //stage 2. + if (continuation) + { + const PxU32 size = mDirtyAggregates.size(); + for (PxU32 i = 0; i < size; i += SortAggregateBoundsParallel::MaxPairs) + { + const PxU32 nbToProcess = PxMin(size - i, SortAggregateBoundsParallel::MaxPairs); + + SortAggregateBoundsParallel* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(SortAggregateBoundsParallel)), SortAggregateBoundsParallel) + (mContextID, &mDirtyAggregates[i], nbToProcess); + + task->setContinuation(&mPostBroadPhase2); + task->removeReference(); + } + } + } + + + + if (continuation) + { + mPostBroadPhase2.setFlushPool(&flushPool); + mPostBroadPhase3.removeReference(); + mPostBroadPhase2.removeReference(); + } + else + { + postBpStage2(NULL, flushPool); + postBpStage3(NULL); + } + +} + +void PostBroadPhaseStage2Task::runInternal() +{ + mManager.postBpStage2(mCont, *mFlushPool); +} + +void AABBManager::postBpStage2(PxBaseTask* continuation, Cm::FlushPool& flushPool) +{ + { + const PxU32 size = mDirtyAggregates.size(); + for (PxU32 i = 0; i < size; i += ProcessSelfCollisionPairsParallel::MaxPairs) + { + const PxU32 nbToProcess = PxMin(size - i, ProcessSelfCollisionPairsParallel::MaxPairs); + + ProcessSelfCollisionPairsParallel* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ProcessSelfCollisionPairsParallel)), ProcessSelfCollisionPairsParallel) + (mContextID, &mDirtyAggregates[i], nbToProcess, this); + if (continuation) + { + task->setContinuation(continuation); + task->removeReference(); + } + else + task->runInternal(); + mAggPairTasks.pushBack(task); + } + } + + { + if (continuation) + processAggregatePairsParallel(mAggregateAggregatePairs, *this, flushPool, continuation, "AggAggPairs", mAggPairTasks); + else + processAggregatePairs(mAggregateAggregatePairs, *this); + } + + + + { + if (continuation) + processAggregatePairsParallel(mActorAggregatePairs, *this, flushPool, continuation, "AggActorPairs", mAggPairTasks); + else + processAggregatePairs(mActorAggregatePairs, *this); + } + + +} + +void AABBManager::postBpStage3(PxBaseTask*) +{ + + { + { + PX_PROFILE_ZONE("SimpleAABBManager::postBroadPhase - aggregate self-collisions", getContextId()); + const PxU32 size = mDirtyAggregates.size(); + for (PxU32 i = 0; i < size; i++) + { + Aggregate* aggregate = mDirtyAggregates[i]; + aggregate->resetDirtyState(); + + } + resetOrClear(mDirtyAggregates); + } + + { + PX_PROFILE_ZONE("SimpleAABBManager::postBroadPhase - append pairs", getContextId()); + + for (PxU32 a = 0; a < mAggPairTasks.size(); ++a) + { + ProcessAggPairsBase* task = mAggPairTasks[a]; + for (PxU32 t = 0; t < 2; t++) + { + for (PxU32 i = 0, startIdx = task->mCreatedPairs[t].mStartIdx; i < task->mCreatedPairs[t].mCount; ++i) + { + mCreatedOverlaps[t].pushBack((*task->mCreatedPairs[t].mArray)[i + startIdx]); + } + for (PxU32 i = 0, startIdx = task->mDestroyedPairs[t].mStartIdx; i < task->mDestroyedPairs[t].mCount; ++i) + { + mDestroyedOverlaps[t].pushBack((*task->mDestroyedPairs[t].mArray)[i + startIdx]); + } + } + } + + mAggPairTasks.forceSize_Unsafe(0); + + + Ps::InlineArray bpCache; + BpCacheData* entry = static_cast(mBpThreadContextPool.pop()); + + while (entry) + { + entry->reset(); + bpCache.pushBack(entry); + entry = static_cast(mBpThreadContextPool.pop()); + } + + //Now reinsert back into queue... + for (PxU32 i = 0; i < bpCache.size(); ++i) + { + mBpThreadContextPool.push(*bpCache[i]); + } + } + } + + { + PX_PROFILE_ZONE("AABBManager::postBroadPhase - process created pairs", getContextId()); + processBPPairs(mBroadPhase.getNbCreatedPairs(), mBroadPhase.getCreatedPairs(), *this); +// processBPPairs(mBroadPhase.getNbDeletedPairs(), mBroadPhase.getDeletedPairs(), *this); + } + + // PT: TODO: revisit this + // Filter out pairs in mDestroyedOverlaps that already exist in mCreatedOverlaps. This should be done better using bitmaps + // and some imposed ordering on previous operations. Later. + // We could also have a dedicated function "reinsertBroadPhase()", which would preserve the existing interactions at Sc-level. + if(1) + { + PX_PROFILE_ZONE("AABBManager::postBroadPhase - post-process", getContextId()); + + PxU32 totalCreatedOverlaps = 0; + for (PxU32 idx=0; idx(mBpThreadContextPool.pop()); + if (rv == NULL) + { + rv = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(BpCacheData), PX_DEBUG_EXP("BpCacheData")), BpCacheData)(); + } + return rv; +} +void AABBManager::putBpCacheData(BpCacheData* data) +{ + mBpThreadContextPool.push(*data); +} +void AABBManager::resetBpCacheData() +{ + Ps::InlineArray bpCache; + BpCacheData* entry = static_cast(mBpThreadContextPool.pop()); + while (entry) + { + entry->reset(); + bpCache.pushBack(entry); + entry = static_cast(mBpThreadContextPool.pop()); + } + + //Now reinsert back into queue... + for (PxU32 i = 0; i < bpCache.size(); ++i) + { + mBpThreadContextPool.push(*bpCache[i]); + } +} + +// PT: disabled this by default, since it bypasses all per-shape/per-actor visualization flags +//static const bool gVisualizeAggregateElems = false; + +void AABBManager::visualize(Cm::RenderOutput& out) +{ + const PxTransform idt = PxTransform(PxIdentity); + out << idt; + + const PxU32 N = mAggregates.size(); + for(PxU32 i=0;igetNbAggregated()) + { + out << PxU32(PxDebugColor::eARGB_GREEN); + const PxBounds3& b = mBoundsArray.getBounds(aggregate->mIndex); + out << Cm::DebugBox(b, true); + } + } + +/* const PxU32 N = mAggregateManager.getAggregatesCapacity(); + for(PxU32 i=0;inbElems) + { + if(!mAggregatesUpdated.isInList(BpHandle(i))) + out << PxU32(PxDebugColor::eARGB_GREEN); + else + out << PxU32(PxDebugColor::eARGB_RED); + + PxBounds3 decoded; + const IntegerAABB& iaabb = mBPElems.getAABB(aggregate->bpElemId); + iaabb.decode(decoded); + + out << Cm::DebugBox(decoded, true); + + if(gVisualizeAggregateElems) + { + PxU32 elem = aggregate->elemHeadID; + while(BP_INVALID_BP_HANDLE!=elem) + { + out << PxU32(PxDebugColor::eARGB_CYAN); + const IntegerAABB elemBounds = mAggregateElems.getAABB(elem); + elemBounds.decode(decoded); + out << Cm::DebugBox(decoded, true); + elem = mAggregateElems.getNextId(elem); + } + } + } + }*/ +} + +} //namespace Bp + +} //namespace physx + diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp new file mode 100644 index 000000000..0df71d8b9 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp @@ -0,0 +1,175 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "BpBroadPhase.h" +#include "BpBroadPhaseSap.h" +#include "BpBroadPhaseMBP.h" +#include "PxSceneDesc.h" +#include "CmBitMap.h" + +using namespace physx; +using namespace Bp; +using namespace Cm; + +BroadPhase* createABP( + const PxU32 maxNbBroadPhaseOverlaps, + const PxU32 maxNbStaticShapes, + const PxU32 maxNbDynamicShapes, + PxU64 contextID); + +BroadPhase* createMBP4( + const PxU32 maxNbBroadPhaseOverlaps, + const PxU32 maxNbStaticShapes, + const PxU32 maxNbDynamicShapes, + PxU64 contextID); + +BroadPhase* BroadPhase::create( + const PxBroadPhaseType::Enum bpType, + const PxU32 maxNbRegions, + const PxU32 maxNbBroadPhaseOverlaps, + const PxU32 maxNbStaticShapes, + const PxU32 maxNbDynamicShapes, + PxU64 contextID) +{ + PX_ASSERT(bpType==PxBroadPhaseType::eMBP || bpType == PxBroadPhaseType::eSAP || bpType == PxBroadPhaseType::eABP); + + if(bpType==PxBroadPhaseType::eABP) + return createABP(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); +// return createMBP4(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); + else if(bpType==PxBroadPhaseType::eMBP) + return PX_NEW(BroadPhaseMBP)(maxNbRegions, maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); + else + return PX_NEW(BroadPhaseSap)(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); +// return createABP(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); +} + + +#if PX_CHECKED +bool BroadPhaseUpdateData::isValid(const BroadPhaseUpdateData& updateData, const BroadPhase& bp) +{ + return (updateData.isValid() && bp.isValid(updateData)); +} + +static bool testHandles(PxU32 size, const BpHandle* handles, const PxU32 capacity, const Bp::FilterGroup::Enum* groups, const PxBounds3* bounds, BitMap& bitmap) +{ + if(!handles && size) + return false; + +/* ValType minVal=0; + ValType maxVal=0xffffffff;*/ + + for(PxU32 i=0;i=capacity) + return false; + + // Array in ascending order of id. + if(i>0 && (h < handles[i-1])) + return false; + + if(groups && groups[h]==FilterGroup::eINVALID) + return false; + + bitmap.set(h); + + if(bounds) + { + for(PxU32 j=0;j<3;j++) + { + //Max must be greater than min. + if(bounds[h].minimum[j]>bounds[h].maximum[j]) + return false; +#if 0 + //Bounds have an upper limit. + if(bounds[created[i]].getMax(j)>=maxVal) + return false; + + //Bounds have a lower limit. + if(bounds[created[i]].getMin(j)<=minVal) + return false; + + //Max must be odd. + if(4 != (bounds[created[i]].getMax(j) & 4)) + return false; + + //Min must be even. + if(0 != (bounds[created[i]].getMin(j) & 4)) + return false; +#endif + } + } + } + return true; +} + +static bool testBitmap(const BitMap& bitmap, PxU32 size, const BpHandle* handles) +{ + while(size--) + { + const BpHandle h = *handles++; + if(bitmap.test(h)) + return false; + } + return true; +} + +bool BroadPhaseUpdateData::isValid() const +{ + const PxBounds3* bounds = getAABBs(); + const PxU32 boxesCapacity = getCapacity(); + const Bp::FilterGroup::Enum* groups = getGroups(); + + BitMap createdBitmap; createdBitmap.resizeAndClear(boxesCapacity); + BitMap updatedBitmap; updatedBitmap.resizeAndClear(boxesCapacity); + BitMap removedBitmap; removedBitmap.resizeAndClear(boxesCapacity); + + if(!testHandles(getNumCreatedHandles(), getCreatedHandles(), boxesCapacity, groups, bounds, createdBitmap)) + return false; + if(!testHandles(getNumUpdatedHandles(), getUpdatedHandles(), boxesCapacity, groups, bounds, updatedBitmap)) + return false; + if(!testHandles(getNumRemovedHandles(), getRemovedHandles(), boxesCapacity, NULL, NULL, removedBitmap)) + return false; + + if(1) + { + // Created/updated + if(!testBitmap(createdBitmap, getNumUpdatedHandles(), getUpdatedHandles())) + return false; + // Created/removed + if(!testBitmap(createdBitmap, getNumRemovedHandles(), getRemovedHandles())) + return false; + // Updated/removed + if(!testBitmap(updatedBitmap, getNumRemovedHandles(), getRemovedHandles())) + return false; + } + return true; +} +#endif diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp new file mode 100644 index 000000000..53362dfaa --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp @@ -0,0 +1,3402 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxProfiler.h" +#include "BpBroadPhaseABP.h" +#include "BpBroadPhaseShared.h" +#include "CmRadixSortBuffered.h" +#include "PsFoundation.h" +#include "PsVecMath.h" +#include "PxcScratchAllocator.h" + +using namespace physx::shdfnd::aos; +using namespace physx; +using namespace Bp; +using namespace Cm; + +#define CHECKPOINT(x) +//#include +//#define CHECKPOINT(x) printf(x); + +//#pragma warning (disable : 4702) +#define CODEALIGN16 //_asm align 16 +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + #define ABP_SIMD_OVERLAP +#endif + +#define ABP_BATCHING 128 +#define USE_ABP_BUCKETS 5000 // PT: don't use buckets below that number... +#ifdef USE_ABP_BUCKETS + #define NB_BUCKETS 5 + // Regular version: 5 buckets a la bucket pruner (4 + cross bucket) + // Alternative version: 4 buckets + dup objects a la MBP regions +// #define USE_ALTERNATIVE_VERSION + #define ABP_USE_INTEGER_XS2 // Works but questionable speedups +#else + #define ABP_USE_INTEGER_XS +#endif +#define NB_SENTINELS 6 + +//#define RECURSE_LIMIT 20000 + + typedef PxU32 ABP_Index; + + static const bool gPrepareOverlapsFlag = true; +#ifdef ABP_SIMD_OVERLAP + static const bool gUseRegularBPKernel = false; // false to use "version 13" in box pruning series + static const bool gUnrollLoop = true; // true to use "version 14" in box pruning series +#else + // PT: tested on Switch, for some reason the regular version is fastest there + static const bool gUseRegularBPKernel = true; // false to use "version 13" in box pruning series + static const bool gUnrollLoop = false; // true to use "version 14" in box pruning series + + //ABP_SIMD_OVERLAP + //MBP.Add64KObjects 13982 ( +0.0%) 4757795 ( +0.0%) FAIL + //MBP.AddBroadPhaseRegion 0 ( +0.0%) 3213795 ( +0.0%) FAIL + //MBP.FinalizeOverlaps64KObjects 507 ( +0.0%) 5650723 ( +0.0%) FAIL + //MBP.FindOverlaps64KMixedObjects 59258 ( +0.0%) 5170179 ( +0.0%) FAIL + //MBP.FindOverlaps64KObjects 31351 ( +0.0%) 7122019 ( +0.0%) FAIL + //MBP.Remove64KObjects 4993 ( +0.0%) 5281683 ( +0.0%) FAIL + //MBP.Update64KObjects 13711 ( +0.0%) 5521699 ( +0.0%) FAIL + + //gUseRegularBPKernel: + //MBP.Add64KObjects 14406 ( +0.0%) 4757795 ( +0.0%) FAIL + //MBP.AddBroadPhaseRegion 0 ( +0.0%) 3213795 ( +0.0%) FAIL + //MBP.FinalizeOverlaps64KObjects 504 ( +0.0%) 5650723 ( +0.0%) FAIL + //MBP.FindOverlaps64KMixedObjects 48929 ( +0.0%) 5170179 ( +0.0%) FAIL + //MBP.FindOverlaps64KObjects 25636 ( +0.0%) 7122019 ( +0.0%) FAIL + //MBP.Remove64KObjects 4878 ( +0.0%) 5281683 ( +0.0%) FAIL + //MBP.Update64KObjects 13932 ( +0.0%) 5521699 ( +0.0%) FAIL + + // false/true + //MBP.Add64KObjects 14278 ( +0.0%) 4757795 ( +0.0%) FAIL + //MBP.AddBroadPhaseRegion 0 ( +0.0%) 3213795 ( +0.0%) FAIL + //MBP.FinalizeOverlaps64KObjects 504 ( +0.0%) 5650723 ( +0.0%) FAIL + //MBP.FindOverlaps64KMixedObjects 60331 ( +0.0%) 5170179 ( +0.0%) FAIL + //MBP.FindOverlaps64KObjects 32064 ( +0.0%) 7122019 ( +0.0%) FAIL + //MBP.Remove64KObjects 4930 ( +0.0%) 5281683 ( +0.0%) FAIL + //MBP.Update64KObjects 13673 ( +0.0%) 5521699 ( +0.0%) FAIL + + // false/false + //MBP.Add64KObjects 13960 ( +0.0%) 4757795 ( +0.0%) FAIL + //MBP.AddBroadPhaseRegion 0 ( +0.0%) 3213795 ( +0.0%) FAIL + //MBP.FinalizeOverlaps64KObjects 503 ( +0.0%) 5650723 ( +0.0%) FAIL + //MBP.FindOverlaps64KMixedObjects 48549 ( +0.0%) 5170179 ( +0.0%) FAIL + //MBP.FindOverlaps64KObjects 25598 ( +0.0%) 7122019 ( +0.0%) FAIL + //MBP.Remove64KObjects 4883 ( +0.0%) 5281683 ( +0.0%) FAIL + //MBP.Update64KObjects 13667 ( +0.0%) 5521699 ( +0.0%) FAIL +#endif + +#ifdef ABP_USE_INTEGER_XS + typedef PxU32 PosXType; + #define SentinelValue 0xffffffff +#else + typedef float PosXType; + #define SentinelValue FLT_MAX +#endif + +#ifdef ABP_USE_INTEGER_XS2 + typedef PxU32 PosXType2; + #define SentinelValue2 0xffffffff +#else + #ifdef ABP_USE_INTEGER_XS + typedef PxU32 PosXType2; + #define SentinelValue2 0xffffffff + #else + typedef float PosXType2; + #define SentinelValue2 FLT_MAX + #endif +#endif + +namespace internalABP{ + + struct SIMD_AABB4 : public Ps::UserAllocated + { + PX_FORCE_INLINE void initFrom2(const PxBounds3& box) + { +#ifdef ABP_USE_INTEGER_XS + mMinX = encodeFloat(PX_IR(box.minimum.x)); + mMaxX = encodeFloat(PX_IR(box.maximum.x)); + mMinY = box.minimum.y; + mMinZ = box.minimum.z; + mMaxY = box.maximum.y; + mMaxZ = box.maximum.z; +#else + mMinX = box.minimum.x; + mMinY = box.minimum.y; + mMinZ = box.minimum.z; + mMaxX = box.maximum.x; + mMaxY = box.maximum.y; + mMaxZ = box.maximum.z; +#endif + } + + PX_FORCE_INLINE void operator = (const SIMD_AABB4& box) + { + mMinX = box.mMinX; + mMinY = box.mMinY; + mMinZ = box.mMinZ; + mMaxX = box.mMaxX; + mMaxY = box.mMaxY; + mMaxZ = box.mMaxZ; + } + + PX_FORCE_INLINE void initSentinel() + { + mMinX = SentinelValue; + } + + PX_FORCE_INLINE bool isSentinel() const + { + return mMinX == SentinelValue; + } + +#ifdef USE_ABP_BUCKETS + // PT: to be able to compute bounds easily + PosXType mMinX; + float mMinY; + float mMinZ; + PosXType mMaxX; + float mMaxY; + float mMaxZ; +#else + PosXType mMinX; + PosXType mMaxX; + float mMinY; + float mMinZ; + float mMaxY; + float mMaxZ; +#endif + }; + +#define USE_SHARED_CLASSES +#ifdef USE_SHARED_CLASSES + struct SIMD_AABB_X4 : public AABB_Xi + { + PX_FORCE_INLINE void initFrom(const SIMD_AABB4& box) + { + #ifdef ABP_USE_INTEGER_XS2 + initFromFloats(&box.mMinX, &box.mMaxX); + #else + mMinX = box.mMinX; + mMaxX = box.mMaxX; + #endif + } + }; + + PX_ALIGN_PREFIX(16) + #ifdef ABP_SIMD_OVERLAP + struct SIMD_AABB_YZ4 : AABB_YZn + { + PX_FORCE_INLINE void initFrom(const SIMD_AABB4& box) + { + #ifdef ABP_SIMD_OVERLAP + mMinY = -box.mMinY; + mMinZ = -box.mMinZ; + #else + mMinY = box.mMinY; + mMinZ = box.mMinZ; + #endif + mMaxY = box.mMaxY; + mMaxZ = box.mMaxZ; + } + } + #else + struct SIMD_AABB_YZ4 : AABB_YZr + { + PX_FORCE_INLINE void initFrom(const SIMD_AABB4& box) + { + mMinY = box.mMinY; + mMinZ = box.mMinZ; + mMaxY = box.mMaxY; + mMaxZ = box.mMaxZ; + } + } + #endif + PX_ALIGN_SUFFIX(16); +#else + struct SIMD_AABB_X4 : public Ps::UserAllocated + { + PX_FORCE_INLINE void initFromFloats(const void* PX_RESTRICT minX, const void* PX_RESTRICT maxX) + { + mMinX = encodeFloat(*reinterpret_cast(minX)); + mMaxX = encodeFloat(*reinterpret_cast(maxX)); + } + + PX_FORCE_INLINE void initFrom(const SIMD_AABB4& box) + { + #ifdef ABP_USE_INTEGER_XS2 + initFromFloats(&box.mMinX, &box.mMaxX); + #else + mMinX = box.mMinX; + mMaxX = box.mMaxX; + #endif + } + + PX_FORCE_INLINE void initFromPxVec4(const PxVec4& min, const PxVec4& max) + { + #ifdef ABP_USE_INTEGER_XS2 + initFromFloats(&min.x, &max.x); + #else + #ifdef ABP_USE_INTEGER_XS + initFromFloats(&min.x, &max.x); + #else + mMinX = min.x; + mMaxX = max.x; + #endif + #endif + } + + PX_FORCE_INLINE void operator = (const SIMD_AABB_X4& box) + { + mMinX = box.mMinX; + mMaxX = box.mMaxX; + } + + PX_FORCE_INLINE void initSentinel() + { + mMinX = SentinelValue2; + } + + PX_FORCE_INLINE bool isSentinel() const + { + return mMinX == SentinelValue2; + } + + PosXType2 mMinX; + PosXType2 mMaxX; + }; + + struct SIMD_AABB_YZ4 : public Ps::UserAllocated + { + PX_FORCE_INLINE void initFrom(const SIMD_AABB4& box) + { + #ifdef ABP_SIMD_OVERLAP + mMinY = -box.mMinY; + mMinZ = -box.mMinZ; + #else + mMinY = box.mMinY; + mMinZ = box.mMinZ; + #endif + mMaxY = box.mMaxY; + mMaxZ = box.mMaxZ; + } + + PX_FORCE_INLINE void initFromPxVec4(const PxVec4& min, const PxVec4& max) + { + #ifdef ABP_SIMD_OVERLAP + mMinY = -min.y; + mMinZ = -min.z; + #else + mMinY = min.y; + mMinZ = min.z; + #endif + mMaxY = max.y; + mMaxZ = max.z; + } + + PX_FORCE_INLINE void operator = (const SIMD_AABB_YZ4& box) + { + V4StoreA(V4LoadA(&box.mMinY), &mMinY); + } + + float mMinY; + float mMinZ; + float mMaxY; + float mMaxZ; + }; +#endif + +#define MBP_ALLOC(x) PX_ALLOC(x, "MBP") +#define MBP_ALLOC_TMP(x) PX_ALLOC_TEMP(x, "MBP_TMP") +#define MBP_FREE(x) if(x) PX_FREE_AND_RESET(x) +#define DELETESINGLE(x) if (x) { delete x; x = NULL; } +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +#define INVALID_ID 0xffffffff + +/////////////////////////////////////////////////////////////////////////////// + +#define DEFAULT_NB_ENTRIES 128 + + class ABP_MM + { + public: + ABP_MM(); + ~ABP_MM(); + + void* frameAlloc(PxU32 size); + void frameFree(void* address); + + PxcScratchAllocator* mScratchAllocator; + }; + +ABP_MM::ABP_MM() : + mScratchAllocator (NULL) +{ +} + +ABP_MM::~ABP_MM() +{ +} + +void* ABP_MM::frameAlloc(PxU32 size) +{ + if(mScratchAllocator) + return mScratchAllocator->alloc(size, true); + return PX_ALLOC_TEMP(size, PX_DEBUG_EXP("frameAlloc")); +} + +void ABP_MM::frameFree(void* address) +{ + if(mScratchAllocator) + mScratchAllocator->free(address); + else + PX_FREE(address); +} + +template +static T* resizeBoxesT(PxU32 oldNbBoxes, PxU32 newNbBoxes, const T* boxes) +{ + T* newBoxes = PX_NEW(T)[newNbBoxes]; + if(oldNbBoxes) + PxMemCopy(newBoxes, boxes, oldNbBoxes*sizeof(T)); + DELETEARRAY(boxes); + return newBoxes; +} + + class Boxes + { + public: + Boxes(); + ~Boxes(); + + PX_FORCE_INLINE void init(const Boxes& boxes){ mSize = boxes.mSize; mCapacity = boxes.mCapacity; } + PX_FORCE_INLINE PxU32 getSize() const { return mSize; } + PX_FORCE_INLINE PxU32 getCapacity() const { return mCapacity; } + PX_FORCE_INLINE bool isFull() const { return mSize==mCapacity; } + PX_FORCE_INLINE void reset() { mSize = mCapacity = 0; } + PX_FORCE_INLINE PxU32 popBack() { return --mSize; } + +// protected: + PxU32 mSize; + PxU32 mCapacity; + }; + +Boxes::Boxes() : + mSize (0), + mCapacity (0) +{ +} + +Boxes::~Boxes() +{ + reset(); +} + + class StraightBoxes : public Boxes + { + public: + StraightBoxes(); + ~StraightBoxes(); + + void init(PxU32 size, PxU32 capacity, SIMD_AABB4* boxes); + void reset(); + PxU32 resize(); + PxU32 resize(PxU32 incoming); + bool allocate(PxU32 nb); + + PX_FORCE_INLINE const SIMD_AABB4* getBoxes() const { return mBoxes; } + PX_FORCE_INLINE SIMD_AABB4* getBoxes() { return mBoxes; } + + PX_FORCE_INLINE void setBounds(PxU32 index, const SIMD_AABB4& box) + { + PX_ASSERT(index(boxes.getBoxes_X()); + mBoxes_YZ = const_cast(boxes.getBoxes_YZ()); +} + +PxU32 SplitBoxes::resize() +{ + const PxU32 capacity = mCapacity; + const PxU32 size = mSize; + +// const PxU32 newCapacity = capacity ? capacity + DEFAULT_NB_ENTRIES : DEFAULT_NB_ENTRIES; +// const PxU32 newCapacity = capacity ? capacity*2 : DEFAULT_NB_ENTRIES; + const PxU32 newCapacity = capacity ? capacity*2 : DEFAULT_NB_ENTRIES; + mBoxes_X = resizeBoxesT(size, newCapacity, mBoxes_X); + mBoxes_YZ = resizeBoxesT(size, newCapacity, mBoxes_YZ); + mCapacity = newCapacity; + return newCapacity; +} + +PxU32 SplitBoxes::resize(PxU32 incoming) +{ + const PxU32 capacity = mCapacity; + const PxU32 size = mSize; + const PxU32 minCapacity = size + incoming; + if(minCapacity(mBoxes_YZ) & 15)); + mSize = mCapacity = nb; + return true; +} + + typedef SplitBoxes StaticBoxes; + typedef SplitBoxes DynamicBoxes; + +/////////////////////////////////////////////////////////////////////////////// + + struct ABP_Object : public Ps::UserAllocated + { + PX_FORCE_INLINE ABP_Object() : mIndex(INVALID_ID) + { +#if PX_DEBUG + mUpdated = false; +#endif + } + + private: + PxU32 mIndex; // Out-to-in, maps user handle to internal array. mIndex indexes either the static or dynamic array. + // PT: the type won't be available for removed objects so we have to store it there. That uses 2 bits. + // Then the "data" will need one more bit for marking sleeping objects so that leaves 28bits for the actual index. + PX_FORCE_INLINE void setData(PxU32 index, FilterType::Enum type) + { +// mIndex = index; + index <<= 2; + index |= type; + mIndex = index; + } + public: + // PT: TODO: rename "index" to data everywhere + PX_FORCE_INLINE void setActiveIndex(PxU32 index, FilterType::Enum type) + { + const PxU32 boxData = (index+index); + setData(boxData, type); + } + + PX_FORCE_INLINE void setSleepingIndex(PxU32 index, FilterType::Enum type) + { + const PxU32 boxData = (index+index)|1; + PX_ASSERT(getType()==type); + setData(boxData, type); + } + + PX_FORCE_INLINE FilterType::Enum getType() const + { + return FilterType::Enum(mIndex&3); + } + + PX_FORCE_INLINE PxU32 getData() const + { + return mIndex>>2; + } + + PX_FORCE_INLINE void invalidateIndex() + { + mIndex = INVALID_ID; + } + PX_FORCE_INLINE bool isValid() const + { + return mIndex != INVALID_ID; + } + +#if PX_DEBUG + bool mUpdated; +#endif + }; + + typedef ABP_Object ABPEntry; + +/////////////////////////////////////////////////////////////////////////////// + +//#define BIT_ARRAY_STACK 512 + + static PX_FORCE_INLINE PxU32 bitsToDwords(PxU32 nbBits) + { + return (nbBits>>5) + ((nbBits&31) ? 1 : 0); + } + + // Use that one instead of an array of bools. Takes less ram, nearly as fast [no bounds checkings and so on]. + class BitArray + { + public: + BitArray(); + BitArray(PxU32 nbBits); + ~BitArray(); + + bool init(PxU32 nbBits); + void empty(); + void resize(PxU32 nbBits); + + PX_FORCE_INLINE void checkResize(PxU32 bitNumber) + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + resize(bitNumber); + } + + PX_FORCE_INLINE void setBitChecked(PxU32 bitNumber) + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + resize(bitNumber); + mBits[index] |= 1<<(bitNumber&31); + } + + PX_FORCE_INLINE void clearBitChecked(PxU32 bitNumber) + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + resize(bitNumber); + mBits[index] &= ~(1<<(bitNumber&31)); + } + // Data management + PX_FORCE_INLINE void setBit(PxU32 bitNumber) { mBits[bitNumber>>5] |= 1<<(bitNumber&31); } + PX_FORCE_INLINE void clearBit(PxU32 bitNumber) { mBits[bitNumber>>5] &= ~(1<<(bitNumber&31)); } + PX_FORCE_INLINE void toggleBit(PxU32 bitNumber) { mBits[bitNumber>>5] ^= 1<<(bitNumber&31); } + + PX_FORCE_INLINE void clearAll() { PxMemZero(mBits, mSize*4); } + PX_FORCE_INLINE void setAll() { PxMemSet(mBits, 0xff, mSize*4); } + + // Data access + PX_FORCE_INLINE Ps::IntBool isSet(PxU32 bitNumber) const { return Ps::IntBool(mBits[bitNumber>>5] & (1<<(bitNumber&31))); } + PX_FORCE_INLINE Ps::IntBool isSetChecked(PxU32 bitNumber) const + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + return 0; + return Ps::IntBool(mBits[index] & (1<<(bitNumber&31))); + } + + PX_FORCE_INLINE const PxU32* getBits() const { return mBits; } + PX_FORCE_INLINE PxU32 getSize() const { return mSize; } + + // PT: replicate Cm::BitMap stuff for temp testing + PxU32 findLast() const + { + for(PxU32 i = mSize; i-- > 0;) + { + if(mBits[i]) + return (i<<5)+Ps::highestSetBit(mBits[i]); + } + return PxU32(0); + } + protected: + PxU32* mBits; //!< Array of bits + PxU32 mSize; //!< Size of the array in dwords +#ifdef BIT_ARRAY_STACK + PxU32 mStack[BIT_ARRAY_STACK]; +#endif + }; + +/////////////////////////////////////////////////////////////////////////////// + +BitArray::BitArray() : mBits(NULL), mSize(0) +{ +} + +BitArray::BitArray(PxU32 nbBits) : mBits(NULL), mSize(0) +{ + init(nbBits); +} + +BitArray::~BitArray() +{ + empty(); +} + +void BitArray::empty() +{ +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); + mBits = NULL; + mSize = 0; +} + +bool BitArray::init(PxU32 nbBits) +{ + mSize = bitsToDwords(nbBits); + // Get ram for n bits +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); +#ifdef BIT_ARRAY_STACK + if(mSize>BIT_ARRAY_STACK) +#endif + mBits = reinterpret_cast(MBP_ALLOC(sizeof(PxU32)*mSize)); +#ifdef BIT_ARRAY_STACK + else + mBits = mStack; +#endif + + // Set all bits to 0 + clearAll(); + return true; +} + +void BitArray::resize(PxU32 nbBits) +{ + const PxU32 newSize = bitsToDwords(nbBits+128); + PxU32* newBits = NULL; +#ifdef BIT_ARRAY_STACK + if(newSize>BIT_ARRAY_STACK) +#endif + { + // Old buffer was stack or allocated, new buffer is allocated + newBits = reinterpret_cast(MBP_ALLOC(sizeof(PxU32)*newSize)); + if(mSize) + PxMemCopy(newBits, mBits, sizeof(PxU32)*mSize); + } +#ifdef BIT_ARRAY_STACK + else + { + newBits = mStack; + if(mSize>BIT_ARRAY_STACK) + { + // Old buffer was allocated, new buffer is stack => copy to stack, shrink + CopyMemory(newBits, mBits, sizeof(PxU32)*BIT_ARRAY_STACK); + } + else + { + // Old buffer was stack, new buffer is stack => keep working on the same stack buffer, nothing to do + } + } +#endif + const PxU32 remain = newSize - mSize; + if(remain) + PxMemZero(newBits + mSize, remain*sizeof(PxU32)); + +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); + mBits = newBits; + mSize = newSize; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ABP_Index* resizeMapping(PxU32 oldNbBoxes, PxU32 newNbBoxes, ABP_Index* mapping) +{ + ABP_Index* newMapping = reinterpret_cast(MBP_ALLOC(sizeof(ABP_Index)*newNbBoxes)); + if(oldNbBoxes) + PxMemCopy(newMapping, mapping, oldNbBoxes*sizeof(ABP_Index)); + MBP_FREE(mapping); + return newMapping; +} + + struct ABP_Object; + + class ABP_PairManager : public PairManagerData + { + public: + ABP_PairManager(); + ~ABP_PairManager(); + + InternalPair* addPair (PxU32 id0, PxU32 id1); + void computeCreatedDeletedPairs (BroadPhaseABP* mbp, const BitArray& updated, const BitArray& removed); + + const Bp::FilterGroup::Enum* mGroups; + const ABP_Index* mInToOut0; + const ABP_Index* mInToOut1; + const ABPEntry* mObjects; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* mLUT; +#endif + }; + + /////////////////////////////////////////////////////////////////////////// + + struct ABP_SharedData + { + PX_FORCE_INLINE ABP_SharedData() : + mABP_Objects (NULL), + mABP_Objects_Capacity (0) + { + } + + void resize(BpHandle userID); + + PX_FORCE_INLINE void checkResize(PxU32 maxID) + { + if(mABP_Objects_CapacitycurrentCapacity) + { + const PxU32 minCapacity = PxMax(newSize, 1024u); + const PxU32 newCapacity = PxMax(minCapacity, currentCapacity*2); + PX_ASSERT(newCapacity>=newSize); + mMaxNbUpdated = newCapacity; + remap = resizeMapping(currentSize, newCapacity, mInToOut_Updated); + } + else + { + remap = mInToOut_Updated; + } + mInToOut_Updated = remap; + mNbUpdated = newSize; + + // PT: we only copy the new handles for now. The bounds will be computed later in "prepareData". + // PT: TODO: do we even need to copy them? Can't we just reuse the source ptr directly? + { + PX_ASSERT(currentSize+nb<=mMaxNbUpdated); + remap += currentSize; + PxU32 nbToGo = nb; + while(nbToGo--) + { + const BpHandle userID = *userIDs++; + + PX_ASSERT(!isNewOrUpdated(userID)); + *remap++ = markAsNewOrUpdated(userID); + + if(sharedData) + sharedData->mUpdatedObjects.setBit(userID); + } + } +} + +// PT: TODO: inline this again +void BoxManager::removeObject(ABPEntry& object, BpHandle userID) +{ + PX_UNUSED(userID); + const PxU32 boxData = object.getData(); + const PxU32 boxIndex = boxData>>1; + if(boxData&1) + { + // Sleeping object. + PX_ASSERT(boxIndex>1; + if(boxData&1) + { + // PT: benchmark for this codepath: MBP.UpdateSleeping + + // Sleeping object. We must reactivate it, i.e: + // - remove it from the array of sleeping objects + // - add it to the array of active/updated objects + + // First we remove: + { + PX_ASSERT(boxIndex also tweaking the sleeping boxes might break the "merge sleeping" array code + PX_ASSERT(mNbRemovedSleeping<=mNbSleeping); + + if(mNbRemovedSleeping==mNbSleeping) + { + // PT: remove everything + mSleepingBoxes.reset(); + PX_FREE_AND_RESET(mInToOut_Sleeping); + mNbSleeping = mNbRemovedSleeping = 0; + return; + } + + const PxU32 expectedTotal = mNbSleeping - mNbRemovedSleeping; + PxU32 nbRemovedFound = 0; + PxU32 nbSleepingLeft = 0; + const PxU32 sleepCapacity = mSleepingBoxes.getCapacity(); + if(expectedTotal>=sleepCapacity/2) + { + // PT: remove holes, keep same data buffers + SIMD_AABB_X4* boxesX = mSleepingBoxes.getBoxes_X(); + SIMD_AABB_YZ4* boxesYZ = mSleepingBoxes.getBoxes_YZ(); + ABP_Index* remap = mInToOut_Sleeping; + + for(PxU32 i=0;i(PX_ALLOC(expectedTotal*sizeof(BpHandle), PX_DEBUG_EXP("tmp"))); + + const SIMD_AABB_X4* PX_RESTRICT srcDataX = mSleepingBoxes.getBoxes_X(); + const SIMD_AABB_YZ4* PX_RESTRICT srcDataYZ = mSleepingBoxes.getBoxes_YZ(); + const ABP_Index* PX_RESTRICT srcRemap = mInToOut_Sleeping; + + for(PxU32 i=0;i(PX_ALLOC(size*sizeof(float), PX_DEBUG_EXP("tmp"))); + + // PT: in this version we compute the key on-the-fly, i.e. it will be computed twice overall. We could make this + // faster by merging bounds and distances inside the AABB manager. + const BpHandle userID = removeNewOrUpdatedMark(index); + + keys[nbUpdated] = bounds[userID].minimum.x - distances[userID]; + + if(!tempBuffer) + { + tempBuffer = reinterpret_cast(memoryManager.frameAlloc(size*sizeof(PxU32))); + newOrUpdatedIDs = tempBuffer; + sleepingIndices = tempBuffer; + } + + newOrUpdatedIDs[size - 1 - nbUpdated] = userID; +#if PX_DEBUG + SIMD_AABB4 aabb; + computeMBPBounds_Check(aabb, bounds, distances, userID); + PX_ASSERT(aabb.mMinX==keys[nbUpdated]); +#endif + nbUpdated++; + } + else + { + if(!tempBuffer) + { + tempBuffer = reinterpret_cast(memoryManager.frameAlloc(size*sizeof(PxU32))); + newOrUpdatedIDs = tempBuffer; + sleepingIndices = tempBuffer; + } + + // PT: sleeping object + sleepingIndices[nbSleeping++] = i; + } + } + PX_ASSERT(nbRemoved + nbUpdated + nbSleeping == size); + + // PT: we must process the sleeping objects first, because the bounds of new sleeping objects are located in the existing updated buffers. + // PT: TODO: *HOWEVER* we could sort things right now and then reuse the "keys" buffer? + + if(nbSleeping) + { + // PT: must merge these guys to current sleeping array + // They should already be in sorted order and we should already have the boxes. +#if PX_DEBUG + const SIMD_AABB_YZ4* boxesYZ = mUpdatedBoxes.getBoxes_YZ(); + float prevKey = -FLT_MAX; + for(PxU32 ii=0;ii=prevKey); + prevKey = key; + + SIMD_AABB4 aabb; + computeMBPBounds_Check(aabb, bounds, distances, userID); + PX_ASSERT(aabb.mMinX==key); + + #ifdef ABP_SIMD_OVERLAP + PX_ASSERT(boxesYZ[i].mMinY==-aabb.mMinY); + PX_ASSERT(boxesYZ[i].mMinZ==-aabb.mMinZ); + #else + PX_ASSERT(boxesYZ[i].mMinY==aabb.mMinY); + PX_ASSERT(boxesYZ[i].mMinZ==aabb.mMinZ); + #endif + PX_ASSERT(boxesYZ[i].mMaxY==aabb.mMaxY); + PX_ASSERT(boxesYZ[i].mMaxZ==aabb.mMaxZ); + } +#endif + + if(mNbSleeping) + { + // PT: benchmark for this codepath: MBP.MergeSleeping + CHECKPOINT("Merging sleeping objects\n"); + + // PT: here, we need to merge two arrays of sleeping objects together: + // - the ones already contained inside mSleepingBoxes + // - the new sleeping objects currently contained in mUpdatedBoxes + // Both of them should already be sorted. + // PT: TODO: super subtle stuff going on there, to revisit + + // PT: TODO: revisit names + PxU32 offsetSorted = 0; + const PxU32 nbSorted = nbSleeping; + const SIMD_AABB_X4* PX_RESTRICT sortedDataX = mUpdatedBoxes.getBoxes_X(); + const SIMD_AABB_YZ4* PX_RESTRICT sortedDataYZ = mUpdatedBoxes.getBoxes_YZ(); + const ABP_Index* PX_RESTRICT sortedRemap = mInToOut_Updated; + + PxU32 offsetNonSorted = 0; + const PxU32 nbToSort = mNbSleeping; + const SIMD_AABB_X4* PX_RESTRICT toSortDataX = mSleepingBoxes.getBoxes_X(); + const SIMD_AABB_YZ4* PX_RESTRICT toSortDataYZ = mSleepingBoxes.getBoxes_YZ(); + const ABP_Index* PX_RESTRICT toSortRemap = mInToOut_Sleeping; + + PX_ASSERT(mNbRemovedSleeping<=mNbSleeping); +#if PX_DEBUG + { + PxU32 nbRemovedFound=0; + for(PxU32 i=0;i(PX_ALLOC(nbTotal*sizeof(BpHandle), PX_DEBUG_EXP("tmp"))); + + PxU32 i=0; + PxU32 nbRemovedFound=0; + PxU32 nbToGo = nbSorted + nbToSort; + while(nbToGo--) + { + PxU32 boxIndex; + { + if(nextCandidateNonSorted(PX_ALLOC(nbSleeping*sizeof(BpHandle), PX_DEBUG_EXP("tmp"))); + PX_FREE(mInToOut_Sleeping); + mInToOut_Sleeping = inToOut_Sleeping; + } + else + { + inToOut_Sleeping = mInToOut_Sleeping; + } + + const SIMD_AABB_X4* srcBoxesX = mUpdatedBoxes.getBoxes_X(); + const SIMD_AABB_YZ4* srcBoxesYZ = mUpdatedBoxes.getBoxes_YZ(); + SIMD_AABB_X4* dstBoxesX = mSleepingBoxes.getBoxes_X(); + SIMD_AABB_YZ4* dstBoxesYZ = mSleepingBoxes.getBoxes_YZ(); + initSentinels(dstBoxesX, nbSleeping); + for(PxU32 ii=0;ii(memoryManager.frameAlloc(sizeof(PxU32)*nbUpdated)); + PxU32* ranks1 = reinterpret_cast(memoryManager.frameAlloc(sizeof(PxU32)*nbUpdated)); + StackRadixSort(rs, ranks0, ranks1); + const PxU32* sorted = rs.Sort(keys, nbUpdated).GetRanks(); + + // PT: + // - shuffle the remap table, store it in sorted order (we can probably use the "recyclable" array here again) + // - compute bounds on-the-fly, store them in sorted order + + // PT: TODO: the "keys" array can be much bigger than stricly necessary here + BpHandle* inToOut_Updated_Sorted; + if(mUpdatedBoxes.allocate(nbUpdated)) + { + inToOut_Updated_Sorted = reinterpret_cast(keys); + PX_FREE(mInToOut_Updated); + mInToOut_Updated = inToOut_Updated_Sorted; + } + else + { + PX_FREE(keys); + inToOut_Updated_Sorted = mInToOut_Updated; + } + SIMD_AABB_X4* PX_RESTRICT dstBoxesX = mUpdatedBoxes.getBoxes_X(); + initSentinels(dstBoxesX, nbUpdated); + +#ifdef USE_ABP_BUCKETS + Vec4V minV = V4Load(FLT_MAX); + Vec4V maxV = V4Load(-FLT_MAX); +#endif + for(PxU32 i=0;i +static void boxPruningKernel( PxU32 nb0, PxU32 nb1, + const SIMD_AABB_X4* PX_RESTRICT boxes0_X, const SIMD_AABB_X4* PX_RESTRICT boxes1_X, + const SIMD_AABB_YZ4* PX_RESTRICT boxes0_YZ, const SIMD_AABB_YZ4* PX_RESTRICT boxes1_YZ, + const ABP_Index* PX_RESTRICT inToOut0, const ABP_Index* PX_RESTRICT inToOut1, + ABP_PairManager* PX_RESTRICT pairManager, const ABPEntry* PX_RESTRICT objects) +{ + pairManager->mInToOut0 = inToOut0; + pairManager->mInToOut1 = inToOut1; + pairManager->mObjects = objects; + + PxU32 index0 = 0; + PxU32 runningIndex1 = 0; + + while(runningIndex1(&boxes1_YZ[runningIndex1]); + const char* const CurrentBoxListX = reinterpret_cast(&boxes1_X[runningIndex1]); + + if(!gUnrollLoop) + { + while(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) + { + const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2); +#ifdef ABP_SIMD_OVERLAP + if(SIMD_OVERLAP_TEST_14a(box)) +#else + if(intersect2D(box0, *reinterpret_cast(box))) +#endif + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes1_X))>>3; + outputPair(*pairManager, index0, Index1); + } + Offset += 8; + } + } + else + { + +#define BIP_VERSION4 +#ifdef BIP_VERSION4 +#ifdef ABP_SIMD_OVERLAP + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(SIMD_OVERLAP_TEST_14a(box)) \ + goto label; } +#else + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(intersect2D(box0, *reinterpret_cast(box))) \ + goto label; } +#endif + goto StartLoop4; + CODEALIGN16 +FoundOverlap3: + Offset += 8; + CODEALIGN16 +FoundOverlap2: + Offset += 8; + CODEALIGN16 +FoundOverlap1: + Offset += 8; + CODEALIGN16 +FoundOverlap0: + Offset += 8; + CODEALIGN16 +FoundOverlap: + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - 8 - reinterpret_cast(boxes1_X))>>3; + outputPair(*pairManager, index0, Index1); + } + CODEALIGN16 +StartLoop4: + while(*reinterpret_cast(CurrentBoxListX + Offset + 8*5)<=maxLimit) + { + BLOCK4(0, FoundOverlap0) + BLOCK4(8, FoundOverlap1) + BLOCK4(16, FoundOverlap2) + BLOCK4(24, FoundOverlap3) + Offset += 40; + BLOCK4(-8, FoundOverlap) + } +#undef BLOCK4 +#endif + +#ifdef ABP_SIMD_OVERLAP + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(SIMD_OVERLAP_TEST_14a(reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto OverlapFound; \ + Offset += 8; +#else + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(intersect2D(box0, *reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto OverlapFound; \ + Offset += 8; +#endif + + goto LoopStart; + CODEALIGN16 +OverlapFound: + { + const PxU32 Index1 = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes1_X))>>3; + outputPair(*pairManager, index0, Index1); + } + Offset += 8; + CODEALIGN16 +LoopStart: + BLOCK + BLOCK + BLOCK + } + } + goto LoopStart; + } +#undef BLOCK + } + } + + index0++; + } +} + +static /*PX_FORCE_INLINE*/ void doBipartiteBoxPruning_Leaf( + ABP_PairManager* PX_RESTRICT pairManager, + const ABPEntry* PX_RESTRICT objects, + PxU32 nb0, + PxU32 nb1, + const SIMD_AABB_X4* PX_RESTRICT boxes0_X, + const SIMD_AABB_X4* PX_RESTRICT boxes1_X, + const SIMD_AABB_YZ4* PX_RESTRICT boxes0_YZ, + const SIMD_AABB_YZ4* PX_RESTRICT boxes1_YZ, + const ABP_Index* PX_RESTRICT remap0, + const ABP_Index* PX_RESTRICT remap1 + ) +{ + PX_ASSERT(boxes0_X[nb0].isSentinel()); + PX_ASSERT(boxes1_X[nb1].isSentinel()); + boxPruningKernel<0>(nb0, nb1, boxes0_X, boxes1_X, boxes0_YZ, boxes1_YZ, remap0, remap1, pairManager, objects); + boxPruningKernel<1>(nb1, nb0, boxes1_X, boxes0_X, boxes1_YZ, boxes0_YZ, remap1, remap0, pairManager, objects); +} + +static PX_FORCE_INLINE void doBipartiteBoxPruning_Leaf(ABP_PairManager* PX_RESTRICT pairManager, const ABPEntry* PX_RESTRICT objects, + PxU32 nb0, PxU32 nb1, const SplitBoxes& boxes0, const SplitBoxes& boxes1, const ABP_Index* PX_RESTRICT remap0, const ABP_Index* PX_RESTRICT remap1) +{ + doBipartiteBoxPruning_Leaf(pairManager, objects, nb0, nb1, boxes0.getBoxes_X(), boxes1.getBoxes_X(), boxes0.getBoxes_YZ(), boxes1.getBoxes_YZ(), remap0, remap1); +} + +static void doCompleteBoxPruning_Leaf( ABP_PairManager* PX_RESTRICT pairManager, PxU32 nb, + const SIMD_AABB_X4* PX_RESTRICT boxes_X, + const SIMD_AABB_YZ4* PX_RESTRICT boxes_YZ, + const ABP_Index* PX_RESTRICT remap, + const ABPEntry* PX_RESTRICT objects) +{ + pairManager->mInToOut0 = remap; + pairManager->mInToOut1 = remap; + pairManager->mObjects = objects; + + PxU32 index0 = 0; + PxU32 runningIndex = 0; + while(runningIndex(&boxes_YZ[runningIndex]); + const char* const CurrentBoxListX = reinterpret_cast(&boxes_X[runningIndex]); + + if(!gUnrollLoop) + { + while(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) + { + const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2); +#ifdef ABP_SIMD_OVERLAP + if(SIMD_OVERLAP_TEST_14a(box)) +#else + if(intersect2D(box0, *reinterpret_cast(box))) +#endif + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes_X))>>3; + outputPair(*pairManager, index0, Index); + } + Offset += 8; + } + } + else + { + +#define VERSION4c +#ifdef VERSION4c +#define VERSION3 // Enable this as our safe loop +#ifdef ABP_SIMD_OVERLAP + #define BLOCK4(x, label) {const float* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(SIMD_OVERLAP_TEST_14a(box)) \ + goto label; } +#else + #define BLOCK4(x, label) {const SIMD_AABB_YZ4* box = reinterpret_cast(CurrentBoxListYZ + Offset*2 + x*2); \ + if(intersect2D(box0, *box)) \ + goto label; } +#endif + goto StartLoop4; + CODEALIGN16 +FoundOverlap3: + Offset += 8; + CODEALIGN16 +FoundOverlap2: + Offset += 8; + CODEALIGN16 +FoundOverlap1: + Offset += 8; + CODEALIGN16 +FoundOverlap0: + Offset += 8; + CODEALIGN16 +FoundOverlap: + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - 8 - reinterpret_cast(boxes_X))>>3; + outputPair(*pairManager, index0, Index); + } + CODEALIGN16 +StartLoop4: + while(*reinterpret_cast(CurrentBoxListX + Offset + 8*5)<=maxLimit) + { + BLOCK4(0, FoundOverlap0) + BLOCK4(8, FoundOverlap1) + BLOCK4(16, FoundOverlap2) + BLOCK4(24, FoundOverlap3) + Offset += 40; + BLOCK4(-8, FoundOverlap) + } +#endif + +#define VERSION3 +#ifdef VERSION3 +#ifdef ABP_SIMD_OVERLAP + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(SIMD_OVERLAP_TEST_14a(reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto BeforeLoop; \ + Offset += 8; +#else + #define BLOCK if(*reinterpret_cast(CurrentBoxListX + Offset)<=maxLimit) \ + {if(intersect2D(box0, *reinterpret_cast(CurrentBoxListYZ + Offset*2))) \ + goto BeforeLoop; \ + Offset += 8; +#endif + + goto StartLoop; + CODEALIGN16 +BeforeLoop: + { + const PxU32 Index = PxU32(CurrentBoxListX + Offset - reinterpret_cast(boxes_X))>>3; + outputPair(*pairManager, index0, Index); + Offset += 8; + } + CODEALIGN16 +StartLoop: + BLOCK + BLOCK + BLOCK + BLOCK + BLOCK + } + } + } + } + goto StartLoop; + } +#endif + } + } + + index0++; + } +} + +#ifdef USE_ABP_BUCKETS +static const PxU8 gCodes[] = { 4, 4, 4, 255, 4, 3, 2, 255, + 4, 1, 0, 255, 255, 255, 255, 255 }; + +static PX_FORCE_INLINE PxU8 classifyBoxNew(const SIMD_AABB_YZ4& boxYZ, const float limitY, const float limitZ) +{ +#ifdef ABP_SIMD_OVERLAP + // PT: mins have been negated for SIMD tests + const bool upperPart = (-boxYZ.mMinZ) > limitZ; + const bool rightPart = (-boxYZ.mMinY) > limitY; +#else + const bool upperPart = boxYZ.mMinZ > limitZ; + const bool rightPart = boxYZ.mMinY > limitY; +#endif + const bool lowerPart = boxYZ.mMaxZ < limitZ; + const bool leftPart = boxYZ.mMaxY < limitY; + + // Table-based box classification avoids many branches + const PxU32 Code = PxU32(rightPart)|(PxU32(leftPart)<<1)|(PxU32(upperPart)<<2)|(PxU32(lowerPart)<<3); + PX_ASSERT(gCodes[Code]!=255); + return gCodes[Code]; +} + +//#include +#ifdef RECURSE_LIMIT +static void CompleteBoxPruning_Recursive( + ABP_MM& memoryManager, + ABP_PairManager* PX_RESTRICT pairManager, + PxU32 nb, + const SIMD_AABB_X4* PX_RESTRICT listX, + const SIMD_AABB_YZ4* PX_RESTRICT listYZ, + const ABP_Index* PX_RESTRICT remap, + const ABPEntry* PX_RESTRICT objects) +{ +// printf("CompleteBoxPruning_Recursive %d\n", nb); + if(!nb) + return; + + /*__declspec(align(16))*/ float mergedMin[4]; + /*__declspec(align(16))*/ float mergedMax[4]; + { +//#ifdef SAFE_VERSION + Vec4V maxV = V4LoadA(&listYZ[0].mMinY); + for(PxU32 i=1;i(memoryManager.frameAlloc(sizeof(SIMD_AABB_X4)*(nb+NB_SENTINELS*NB_BUCKETS))); + SIMD_AABB_YZ4* BoxListYZBuffer = reinterpret_cast(memoryManager.frameAlloc(sizeof(SIMD_AABB_YZ4)*nb)); + + PxU32 Counters[NB_BUCKETS]; + for(PxU32 i=0;i(memoryManager.frameAlloc(sizeof(PxU32)*nb)); + PxU8* Indices = reinterpret_cast(memoryManager.frameAlloc(sizeof(PxU8)*nb)); + for(PxU32 i=0;i(memoryManager.frameAlloc(sizeof(SIMD_AABB_X4)*(nb+NB_SENTINELS*NB_BUCKETS))); + SIMD_AABB_YZ4* BoxListYZBuffer = reinterpret_cast(memoryManager.frameAlloc(sizeof(SIMD_AABB_YZ4)*nb)); + + const PxVec3& mergedMin = updatedBounds.minimum; + const PxVec3& mergedMax = updatedBounds.maximum; + const float limitY = (mergedMax[1] + mergedMin[1]) * 0.5f; + const float limitZ = (mergedMax[2] + mergedMin[2]) * 0.5f; + + PxU32 Counters[NB_BUCKETS]; + for(PxU32 i=0;i(memoryManager.frameAlloc(sizeof(PxU32)*nb)); + PxU8* Indices = reinterpret_cast(memoryManager.frameAlloc(sizeof(PxU8)*nb)); + for(PxU32 i=0;iUSE_ABP_BUCKETS) + CompleteBoxPruning_Version16(memoryManager, mDBM.getUpdatedBounds(), pairManager, nbUpdated, + updatedDynamicBoxes_X, + updatedDynamicBoxes_YZ, + mDBM.getRemap_Updated(), objects); + else +#endif + doCompleteBoxPruning_Leaf(pairManager, nbUpdated, + updatedDynamicBoxes_X, + updatedDynamicBoxes_YZ, + mDBM.getRemap_Updated(), objects); + } +} + +void ABP::Region_prepareOverlaps() +{ + if( !mDBM.isThereWorkToDo() + && !mKBM.isThereWorkToDo() + && !mSBM.isThereWorkToDo() + ) + return; + + if(mSBM.isThereWorkToDo()) + { + mSBM.prepareData(mRS, mShared.mABP_Objects, mShared.mABP_Objects_Capacity, mMM); + mDBM.notifyStaticChange(mShared.mABP_Objects); + mKBM.notifyStaticChange(mShared.mABP_Objects); + } + + mDBM.prepareData(mRS, mShared.mABP_Objects, mShared.mABP_Objects_Capacity, mMM); + mKBM.prepareData(mRS, mShared.mABP_Objects, mShared.mABP_Objects_Capacity, mMM); + + mRS.reset(); +} + +// Finds static-vs-dynamic and dynamic-vs-dynamic overlaps +static void findAllOverlaps(ABP_MM& memoryManager, ABP_PairManager& pairManager, const ABP_SharedData& mShared, const StaticManager& mSBM, const DynamicManager& mDBM, bool doComplete, bool doBipartite) +{ + const PxU32 nbUpdatedBoxes = mDBM.getNbUpdatedBoxes(); + + // PT: find dynamics-vs-dynamics overlaps + if(doComplete) + doCompleteBoxPruning_(memoryManager, &pairManager, mShared.mABP_Objects, mDBM); + + // PT: find dynamics-vs-statics overlaps + if(doBipartite) + { + // PT: in previous versions we did active-dynamics-vs-all-statics here. + if(nbUpdatedBoxes) + { + if(mSBM.getNbUpdatedBoxes()) + { + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + nbUpdatedBoxes, mSBM.getNbUpdatedBoxes(), + mDBM.getUpdatedBoxes(), mSBM.getUpdatedBoxes(), + mDBM.getRemap_Updated(), mSBM.getRemap_Updated() + ); + } + if(mSBM.getNbNonUpdatedBoxes()) + { + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + nbUpdatedBoxes, mSBM.getNbNonUpdatedBoxes(), + mDBM.getUpdatedBoxes(), mSBM.getSleepingBoxes(), + mDBM.getRemap_Updated(), mSBM.getRemap_Sleeping() + ); + } + } + + // PT: TODO: refactor this with kinematic stuff, etc.... bit tedious + if(mSBM.getNbUpdatedBoxes() && mDBM.getNbNonUpdatedBoxes()) + { + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + mDBM.getNbNonUpdatedBoxes(), mSBM.getNbUpdatedBoxes(), + mDBM.getSleepingBoxes(), mSBM.getUpdatedBoxes(), + mDBM.getRemap_Sleeping(), mSBM.getRemap_Updated() + ); + } + + } +} + +void ABP::Region_findOverlaps(ABP_PairManager& pairManager) +{ + if(!gPrepareOverlapsFlag) + Region_prepareOverlaps(); + + bool doKineKine = true; + bool doStaticKine = true; + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + { + const bool* lut = mPairManager.mLUT; + doStaticKine = lut[Bp::FilterType::KINEMATIC*4+Bp::FilterType::STATIC]; + doKineKine = lut[Bp::FilterType::KINEMATIC*4+Bp::FilterType::KINEMATIC]; + } + #endif + + findAllOverlaps(mMM, pairManager, mShared, mSBM, mDBM, true, true); + findAllOverlaps(mMM, pairManager, mShared, mSBM, mKBM, doKineKine, doStaticKine); + + const PxU32 nbUpdatedDynamics = mDBM.getNbUpdatedBoxes(); + const PxU32 nbNonUpdatedDynamics = mDBM.getNbNonUpdatedBoxes(); + const PxU32 nbUpdatedKinematics = mKBM.getNbUpdatedBoxes(); + const PxU32 nbNonUpdatedKinematics = mKBM.getNbNonUpdatedBoxes(); + + if(nbUpdatedDynamics) + { + // Active dynamics vs active kinematics + if(nbUpdatedKinematics) + { + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + nbUpdatedDynamics, nbUpdatedKinematics, + mDBM.getUpdatedBoxes(), mKBM.getUpdatedBoxes(), + mDBM.getRemap_Updated(), mKBM.getRemap_Updated() + ); + } + // Active dynamics vs inactive kinematics + if(nbNonUpdatedKinematics) + { + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + nbUpdatedDynamics, nbNonUpdatedKinematics, + mDBM.getUpdatedBoxes(), mKBM.getSleepingBoxes(), + mDBM.getRemap_Updated(), mKBM.getRemap_Sleeping() + ); + } + } + + if(nbUpdatedKinematics && nbNonUpdatedDynamics) + { + // Inactive dynamics vs active kinematics + doBipartiteBoxPruning_Leaf( &pairManager, mShared.mABP_Objects, + nbNonUpdatedDynamics, nbUpdatedKinematics, + mDBM.getSleepingBoxes(), mKBM.getUpdatedBoxes(), + mDBM.getRemap_Sleeping(), mKBM.getRemap_Updated() + ); + } + + mSBM.finalize(); + mDBM.finalize(); + mKBM.finalize(); +} + +/////////////////////////////////////////////////////////////////////////// + +ABP::ABP() : + mSBM (FilterType::STATIC), + mDBM (FilterType::DYNAMIC), + mKBM (FilterType::KINEMATIC), + mTransientBounds (NULL), + mTransientContactDistance (NULL) +// mTransientGroups (NULL) +{ +} + +ABP::~ABP() +{ + reset(); +} + +void ABP::freeBuffers() +{ + mShared.mRemovedObjects.empty(); +} + +void ABP::preallocate(PxU32 nbObjects, PxU32 maxNbOverlaps) +{ + if(nbObjects) + { + PX_DELETE_ARRAY(mShared.mABP_Objects); + ABP_Object* objects = PX_NEW(ABP_Object)[nbObjects]; + mShared.mABP_Objects = objects; + mShared.mABP_Objects_Capacity = nbObjects; +#if PX_DEBUG + for(PxU32 i=0;iremoveObject(object, userID); + + object.invalidateIndex(); +#if PX_DEBUG + object.mUpdated = false; +#endif +} + +void ABP::updateObject(BpHandle userID) +{ + mShared.mUpdatedObjects.setBitChecked(userID); + + PX_ASSERT(userIDupdateObject(object, userID); +} + +void ABP_PairManager::computeCreatedDeletedPairs(BroadPhaseABP* mbp, const BitArray& updated, const BitArray& removed) +{ + // PT: parse all currently active pairs. The goal here is to generate the found/lost pairs, compared to previous frame. + PxU32 i=0; + PxU32 nbActivePairs = mNbActivePairs; + while(imCreated.pushBack(BroadPhasePair(id0, id1)); + + p.clearNew(); + p.clearUpdated(); + i++; + } + else if(p.isUpdated()) + { + // Persistent pair + + // PT: this pair already existed in the structure, and has been found again this frame. Since + // MBP reports "all pairs" each frame (as opposed to SAP), this happens quite often, for each + // active persistent pair. + p.clearUpdated(); + i++; + } + else + { + // Lost pair + + // PT: if the pair is not new and not 'updated', it might be a lost (separated) pair. But this + // is not always the case since we now handle "sleeping" objects directly within MBP. A pair + // of sleeping objects does not generate an 'addPair' call, so it ends up in this codepath. + // Nonetheless the sleeping pair should not be deleted. We can only delete pairs involving + // objects that have been actually moved during the frame. This is the only case in which + // a pair can indeed become 'lost'. + const PxU32 id0 = p.getId0(); + const PxU32 id1 = p.getId1(); + PX_ASSERT(id0!=INVALID_ID); + PX_ASSERT(id1!=INVALID_ID); + + // PT: if none of the involved objects have been updated, the pair is just sleeping: keep it and skip it. + if(updated.isSetChecked(id0) || updated.isSetChecked(id1)) + { + // PT: by design (for better or worse) we do not report pairs to the client when + // one of the involved objects has been deleted. The pair must still be deleted + // from the MBP structure though. + if(!removed.isSetChecked(id0) && !removed.isSetChecked(id1)) + { + // PT: doing the group-based filtering here is useless. The pair should not have + // been added in the first place. + mbp->mDeleted.pushBack(BroadPhasePair(id0, id1)); + } + + const PxU32 hashValue = hash(id0, id1) & mMask; + removePair(id0, id1, hashValue, i); + nbActivePairs--; + } + else i++; + } + } + + shrinkMemory(); +} + +void ABP::findOverlaps(const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) +{ + mPairManager.mGroups = groups; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mPairManager.mLUT = lut; +#endif + + Region_findOverlaps(mPairManager); +} + +PxU32 ABP::finalize(BroadPhaseABP* mbp) +{ + mPairManager.computeCreatedDeletedPairs(mbp, mShared.mUpdatedObjects, mShared.mRemovedObjects); + + mShared.mUpdatedObjects.clearAll(); + + return mPairManager.mNbActivePairs; +} + +void ABP::reset() +{ + mSBM.reset(); + mDBM.reset(); + mKBM.reset(); + + PX_DELETE_ARRAY(mShared.mABP_Objects); + mShared.mABP_Objects_Capacity = 0; + mPairManager.purge(); + mShared.mUpdatedObjects.empty(); + mShared.mRemovedObjects.empty(); +} + +// PT: TODO: is is really ok to use "transient" data in this function? +void ABP::shiftOrigin(const PxVec3& shift) +{ + PX_UNUSED(shift); // PT: unused because the bounds were pre-shifted before calling this function + + // PT: the AABB manager marks all objects as updated when we shift so the stuff below may not be necessary +} + +void ABP::setTransientData(const PxBounds3* bounds, const PxReal* contactDistance/*, const Bp::FilterGroup::Enum* groups*/) +{ + mTransientBounds = bounds; + mTransientContactDistance = contactDistance; +// mTransientGroups = groups; + + mSBM.setSourceData(bounds, contactDistance); + mDBM.setSourceData(bounds, contactDistance); + mKBM.setSourceData(bounds, contactDistance); +} +/////////////////////////////////////////////////////////////////////////////// + +} + +// Below is the PhysX wrapper = link between AABBManager and ABP + +using namespace internalABP; + +#define DEFAULT_CREATED_DELETED_PAIRS_CAPACITY 1024 + +BroadPhaseABP::BroadPhaseABP( PxU32 maxNbBroadPhaseOverlaps, + PxU32 maxNbStaticShapes, + PxU32 maxNbDynamicShapes, + PxU64 /*contextID*/) : + mGroups (NULL) +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + ,mLUT (NULL) +#endif +{ + mABP = PX_NEW(ABP)(); + + const PxU32 nbObjects = maxNbStaticShapes + maxNbDynamicShapes; + mABP->preallocate(nbObjects, maxNbBroadPhaseOverlaps); + + mCreated.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); + mDeleted.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); +} + +BroadPhaseABP::~BroadPhaseABP() +{ + DELETESINGLE(mABP); +} + +void BroadPhaseABP::update(const PxU32 /*numCpuTasks*/, PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData, physx::PxBaseTask* /*continuation*/, physx::PxBaseTask* narrowPhaseUnblockTask) +{ +#if PX_CHECKED + PX_CHECK_AND_RETURN(scratchAllocator, "BroadPhaseABP::update - scratchAllocator must be non-NULL \n"); +#endif + mABP->mMM.mScratchAllocator = scratchAllocator; + + // PT: TODO: move this out of update function + if(narrowPhaseUnblockTask) + narrowPhaseUnblockTask->removeReference(); + + setUpdateData(updateData); + + update(); + postUpdate(); +} + +void BroadPhaseABP::singleThreadedUpdate(PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData) +{ + mABP->mMM.mScratchAllocator = scratchAllocator; + setUpdateData(updateData); + update(); + postUpdate(); +} + +void BroadPhaseABP::removeObjects(const BroadPhaseUpdateData& updateData) +{ + PxU32 nbToGo = updateData.getNumRemovedHandles(); + if(!nbToGo) + return; + + const BpHandle* PX_RESTRICT removed = updateData.getRemovedHandles(); + PX_ASSERT(removed); + + while(nbToGo--) + { + const BpHandle index = *removed++; + PX_ASSERT(index+1mShared.mABP_Objects_Capacity); // PT: we allocated one more box on purpose + mABP->removeObject(index); + } +} + +void BroadPhaseABP::updateObjects(const BroadPhaseUpdateData& updateData) +{ + const BpHandle* PX_RESTRICT updated = updateData.getUpdatedHandles(); + if(!updated) + return; + + PxU32 nbToGo = updateData.getNumUpdatedHandles(); + while(nbToGo--) + { + const BpHandle index = *updated++; + PX_ASSERT(index+1mShared.mABP_Objects_Capacity); // PT: we allocated one more box on purpose + mABP->updateObject(index); + } +} + +void BroadPhaseABP::addObjects(const BroadPhaseUpdateData& updateData) +{ + PxU32 nbToGo = updateData.getNumCreatedHandles(); + if(!nbToGo) + return; + + const BpHandle* PX_RESTRICT created = updateData.getCreatedHandles(); + PX_ASSERT(created); + + const Bp::FilterGroup::Enum* PX_RESTRICT groups = updateData.getGroups(); + + struct Batch + { + PX_FORCE_INLINE Batch() : mNb(0), mMaxIndex(0) {} + + PxU32 mNb; + PxU32 mMaxIndex; + BpHandle mIndices[ABP_BATCHING]; + + PX_FORCE_INLINE void add(const BpHandle index, internalABP::ABP* PX_RESTRICT abp, FilterType::Enum type) + { + PxU32 nb = mNb; + mMaxIndex = PxMax(mMaxIndex, index); + mIndices[nb++] = index; + if(nb==ABP_BATCHING) + { + mNb = 0; + // PT: TODO: we could use a function ptr here + if(type==FilterType::STATIC) + abp->addStaticObjects(mIndices, ABP_BATCHING, mMaxIndex); + else if(type==FilterType::KINEMATIC) + abp->addKinematicObjects(mIndices, ABP_BATCHING, mMaxIndex); + else + { + PX_ASSERT(type==FilterType::DYNAMIC || type==FilterType::AGGREGATE); + abp->addDynamicObjects(mIndices, ABP_BATCHING, mMaxIndex); + } + + mMaxIndex = 0; + } + else + mNb = nb; + } + }; + Batch statics; + Batch dynamics; + Batch kinematics; + + Batch* batches[FilterType::COUNT]; + batches[FilterType::STATIC] = &statics; + batches[FilterType::DYNAMIC] = &dynamics; + batches[FilterType::AGGREGATE] = &dynamics; + batches[FilterType::KINEMATIC] = &kinematics; + + while(nbToGo--) + { + const BpHandle index = *created++; + PX_ASSERT(index+1mShared.mABP_Objects_Capacity); // PT: we allocated one more box on purpose + const FilterType::Enum type = FilterType::Enum(groups[index]&3); + batches[type]->add(index, mABP, type); + } + + if(statics.mNb) + mABP->addStaticObjects(statics.mIndices, statics.mNb, statics.mMaxIndex); + if(kinematics.mNb) + mABP->addKinematicObjects(kinematics.mIndices, kinematics.mNb, kinematics.mMaxIndex); + if(dynamics.mNb) + mABP->addDynamicObjects(dynamics.mIndices, dynamics.mNb, dynamics.mMaxIndex); +} + +void BroadPhaseABP::setUpdateData(const BroadPhaseUpdateData& updateData) +{ + mABP->setTransientData(updateData.getAABBs(), updateData.getContactDistance()/*, updateData.getGroups()*/); + + const PxU32 newCapacity = updateData.getCapacity(); + mABP->mShared.checkResize(newCapacity); + +#if PX_CHECKED + // PT: WARNING: this must be done after the allocateMappingArray call + if(!BroadPhaseUpdateData::isValid(updateData, *this)) + { + PX_CHECK_MSG(false, "Illegal BroadPhaseUpdateData \n"); + return; + } +#endif + + mGroups = updateData.getGroups(); +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT = updateData.getLUT(); +#endif + + removeObjects(updateData); + addObjects(updateData); + updateObjects(updateData); + + PX_ASSERT(!mCreated.size()); + PX_ASSERT(!mDeleted.size()); + + if(gPrepareOverlapsFlag) + mABP->Region_prepareOverlaps(); +} + +void BroadPhaseABP::update() +{ + mABP->findOverlaps(mGroups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , mLUT +#endif + ); +} + +void BroadPhaseABP::postUpdate() +{ + mABP->finalize(this); +} + +PxU32 BroadPhaseABP::getNbCreatedPairs() const +{ + return mCreated.size(); +} + +BroadPhasePair* BroadPhaseABP::getCreatedPairs() +{ + return mCreated.begin(); +} + +PxU32 BroadPhaseABP::getNbDeletedPairs() const +{ + return mDeleted.size(); +} + +BroadPhasePair* BroadPhaseABP::getDeletedPairs() +{ + return mDeleted.begin(); +} + +static void freeBuffer(Ps::Array& buffer) +{ + const PxU32 size = buffer.size(); + if(size>DEFAULT_CREATED_DELETED_PAIRS_CAPACITY) + { + buffer.reset(); + buffer.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); + } + else + { + buffer.clear(); + } +} + +void BroadPhaseABP::freeBuffers() +{ + mABP->freeBuffers(); + freeBuffer(mCreated); + freeBuffer(mDeleted); +} + +#if PX_CHECKED +bool BroadPhaseABP::isValid(const BroadPhaseUpdateData& updateData) const +{ + const PxU32 nbObjects = mABP->mShared.mABP_Objects_Capacity; + PX_UNUSED(nbObjects); + const ABP_Object* PX_RESTRICT objects = mABP->mShared.mABP_Objects; + const BpHandle* created = updateData.getCreatedHandles(); + if(created) + { + PxU32 nbToGo = updateData.getNumCreatedHandles(); + while(nbToGo--) + { + const BpHandle index = *created++; + PX_ASSERT(indexshiftOrigin(shift); +} + +PxU32 BroadPhaseABP::getCurrentNbPairs() const +{ + return mABP->mPairManager.mNbActivePairs; +} + +void BroadPhaseABP::setScratchAllocator(PxcScratchAllocator* scratchAllocator) +{ + mABP->mMM.mScratchAllocator = scratchAllocator; +} + + + +BroadPhase* createABP( + const PxU32 maxNbBroadPhaseOverlaps, + const PxU32 maxNbStaticShapes, + const PxU32 maxNbDynamicShapes, + PxU64 contextID) +{ + return PX_NEW(BroadPhaseABP)(maxNbBroadPhaseOverlaps, maxNbStaticShapes, maxNbDynamicShapes, contextID); +} + diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h new file mode 100644 index 000000000..0ab400bbb --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h @@ -0,0 +1,101 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_ABP_H +#define BP_BROADPHASE_ABP_H + +#include "CmPhysXCommon.h" +#include "BpBroadPhase.h" +#include "PxPhysXConfig.h" +#include "BpBroadPhaseUpdate.h" +#include "PsUserAllocated.h" + +namespace internalABP{ + class ABP; +} + +namespace physx +{ +namespace Bp +{ + class BroadPhaseABP : public BroadPhase, public Ps::UserAllocated + { + PX_NOCOPY(BroadPhaseABP) + public: + BroadPhaseABP( PxU32 maxNbBroadPhaseOverlaps, + PxU32 maxNbStaticShapes, + PxU32 maxNbDynamicShapes, + PxU64 contextID); + virtual ~BroadPhaseABP(); + + // BroadPhase + virtual PxBroadPhaseType::Enum getType() const { return PxBroadPhaseType::eABP; } + virtual void destroy() { delete this; } + virtual void update(const PxU32 numCpuTasks, PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData, physx::PxBaseTask* continuation, physx::PxBaseTask* narrowPhaseUnblockTask); + virtual void fetchBroadPhaseResults(physx::PxBaseTask*) {} + virtual PxU32 getNbCreatedPairs() const; + virtual BroadPhasePair* getCreatedPairs(); + virtual PxU32 getNbDeletedPairs() const; + virtual BroadPhasePair* getDeletedPairs(); + virtual void freeBuffers(); + virtual void shiftOrigin(const PxVec3& shift); +#if PX_CHECKED + virtual bool isValid(const BroadPhaseUpdateData& updateData) const; +#endif + virtual BroadPhasePair* getBroadPhasePairs() const {return NULL;} //KS - TODO - implement this!!! + virtual void deletePairs(){} //KS - TODO - implement this!!! + virtual void singleThreadedUpdate(PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData); + //~BroadPhase + + internalABP::ABP* mABP; // PT: TODO: aggregate + + Ps::Array mCreated; + Ps::Array mDeleted; + + const Bp::FilterGroup::Enum*mGroups; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* mLUT; +#endif + void setUpdateData(const BroadPhaseUpdateData& updateData); + void addObjects(const BroadPhaseUpdateData& updateData); + void removeObjects(const BroadPhaseUpdateData& updateData); + void updateObjects(const BroadPhaseUpdateData& updateData); + + void update(); + void postUpdate(); + + PxU32 getCurrentNbPairs() const; + void setScratchAllocator(PxcScratchAllocator* scratchAllocator); + }; + +} //namespace Bp + +} //namespace physx + +#endif // BP_BROADPHASE_ABP_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp new file mode 100644 index 000000000..92fe49d1d --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp @@ -0,0 +1,3422 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "BpBroadPhaseMBP.h" +#include "BpBroadPhaseShared.h" +#include "CmRadixSortBuffered.h" +#include "PsUtilities.h" +#include "PsFoundation.h" +#include "PsVecMath.h" + +using namespace physx::shdfnd::aos; + +//#define CHECK_NB_OVERLAPS +#define USE_FULLY_INSIDE_FLAG +//#define MBP_USE_NO_CMP_OVERLAP_3D // Seems slower + +//HWSCAN: reverse bits in fully-inside-flag bitmaps because the code gives us indices for which bits are set (and we want the opposite) +#define HWSCAN + +using namespace physx; +using namespace Bp; +using namespace Cm; + + static PX_FORCE_INLINE MBP_Handle encodeHandle(MBP_ObjectIndex objectIndex, PxU32 flipFlop, bool isStatic) + { + /* objectIndex += objectIndex; + objectIndex |= flipFlop; + return objectIndex;*/ + return (objectIndex<<2)|(flipFlop<<1)|PxU32(isStatic); + } + + static PX_FORCE_INLINE MBP_ObjectIndex decodeHandle_Index(MBP_Handle handle) + { + // return handle>>1; + return handle>>2; + } + + static PX_FORCE_INLINE PxU32 decodeHandle_IsStatic(MBP_Handle handle) + { + return handle&1; + } + +#define MBP_ALLOC(x) PX_ALLOC(x, "MBP") +#define MBP_ALLOC_TMP(x) PX_ALLOC_TEMP(x, "MBP_TMP") +#define MBP_FREE(x) if(x) PX_FREE_AND_RESET(x) +#define DELETESINGLE(x) if (x) { delete x; x = NULL; } +#define DELETEARRAY(x) if (x) { delete []x; x = NULL; } + +#define INVALID_ID 0xffffffff + + typedef MBP_Index* MBP_Mapping; + +/* PX_FORCE_INLINE PxU32 encodeFloat(const float val) + { + // We may need to check on -0 and 0 + // But it should make no practical difference. + PxU32 ir = IR(val); + + if(ir & 0x80000000) //negative? + ir = ~ir;//reverse sequence of negative numbers + else + ir |= 0x80000000; // flip sign + + return ir; + }*/ + +struct RegionHandle : public Ps::UserAllocated +{ + PxU16 mHandle; // Handle from region + PxU16 mInternalBPHandle; // Index of region data within mRegions +}; + +enum MBPFlags +{ + MBP_FLIP_FLOP = (1<<1), + MBP_REMOVED = (1<<2) // ### added for TA24714, not needed otherwise +}; + +// We have one of those for each of the "200K" objects so we should optimize this size as much as possible +struct MBP_Object : public Ps::UserAllocated +{ + BpHandle mUserID; // Handle sent to us by the AABB manager + PxU16 mNbHandles; // Number of regions the object is part of + PxU16 mFlags; // MBPFlags ### only 1 bit used in the end + + PX_FORCE_INLINE bool getFlipFlop() const { return (mFlags & MBP_FLIP_FLOP)==0; } + + union + { + RegionHandle mHandle; + PxU32 mHandlesIndex; + }; +}; + +// This one is used in each Region +struct MBPEntry : public Ps::UserAllocated +{ + PX_FORCE_INLINE MBPEntry() + { + mMBPHandle = INVALID_ID; + } + + // ### mIndex could be PxU16 but beware, we store mFirstFree there + PxU32 mIndex; // Out-to-in, maps user handle to internal array. mIndex indexes either the static or dynamic array. + MBP_Handle mMBPHandle; // MBP-level handle (the one returned to users) +#if PX_DEBUG + bool mUpdated; +#endif + + PX_FORCE_INLINE PxU32 isStatic() const + { + return decodeHandle_IsStatic(mMBPHandle); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +//#define BIT_ARRAY_STACK 512 + + static PX_FORCE_INLINE PxU32 bitsToDwords(PxU32 nbBits) + { + return (nbBits>>5) + ((nbBits&31) ? 1 : 0); + } + + // Use that one instead of an array of bools. Takes less ram, nearly as fast [no bounds checkings and so on]. + class BitArray + { + public: + BitArray(); + BitArray(PxU32 nbBits); + ~BitArray(); + + bool init(PxU32 nbBits); + void empty(); + void resize(PxU32 nbBits); + + PX_FORCE_INLINE void setBitChecked(PxU32 bitNumber) + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + resize(bitNumber); + mBits[index] |= 1<<(bitNumber&31); + } + + PX_FORCE_INLINE void clearBitChecked(PxU32 bitNumber) + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + resize(bitNumber); + mBits[index] &= ~(1<<(bitNumber&31)); + } + // Data management + PX_FORCE_INLINE void setBit(PxU32 bitNumber) { mBits[bitNumber>>5] |= 1<<(bitNumber&31); } + PX_FORCE_INLINE void clearBit(PxU32 bitNumber) { mBits[bitNumber>>5] &= ~(1<<(bitNumber&31)); } + PX_FORCE_INLINE void toggleBit(PxU32 bitNumber) { mBits[bitNumber>>5] ^= 1<<(bitNumber&31); } + + PX_FORCE_INLINE void clearAll() { PxMemZero(mBits, mSize*4); } + PX_FORCE_INLINE void setAll() { PxMemSet(mBits, 0xff, mSize*4); } + + // Data access + PX_FORCE_INLINE Ps::IntBool isSet(PxU32 bitNumber) const { return Ps::IntBool(mBits[bitNumber>>5] & (1<<(bitNumber&31))); } + PX_FORCE_INLINE Ps::IntBool isSetChecked(PxU32 bitNumber) const + { + const PxU32 index = bitNumber>>5; + if(index>=mSize) + return 0; + return Ps::IntBool(mBits[index] & (1<<(bitNumber&31))); + } + + PX_FORCE_INLINE const PxU32* getBits() const { return mBits; } + PX_FORCE_INLINE PxU32 getSize() const { return mSize; } + + // PT: replicate Cm::BitMap stuff for temp testing + PxU32 findLast() const + { + for(PxU32 i = mSize; i-- > 0;) + { + if(mBits[i]) + return (i<<5)+Ps::highestSetBit(mBits[i]); + } + return PxU32(0); + } + protected: + PxU32* mBits; //!< Array of bits + PxU32 mSize; //!< Size of the array in dwords +#ifdef BIT_ARRAY_STACK + PxU32 mStack[BIT_ARRAY_STACK]; +#endif + }; + +/////////////////////////////////////////////////////////////////////////////// + +BitArray::BitArray() : mBits(NULL), mSize(0) +{ +} + +BitArray::BitArray(PxU32 nbBits) : mBits(NULL), mSize(0) +{ + init(nbBits); +} + +BitArray::~BitArray() +{ + empty(); +} + +void BitArray::empty() +{ +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); + mBits = NULL; + mSize = 0; +} + +bool BitArray::init(PxU32 nbBits) +{ + mSize = bitsToDwords(nbBits); + // Get ram for n bits +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); +#ifdef BIT_ARRAY_STACK + if(mSize>BIT_ARRAY_STACK) +#endif + mBits = reinterpret_cast(MBP_ALLOC(sizeof(PxU32)*mSize)); +#ifdef BIT_ARRAY_STACK + else + mBits = mStack; +#endif + + // Set all bits to 0 + clearAll(); + return true; +} + +void BitArray::resize(PxU32 nbBits) +{ + const PxU32 newSize = bitsToDwords(nbBits+128); + PxU32* newBits = NULL; +#ifdef BIT_ARRAY_STACK + if(newSize>BIT_ARRAY_STACK) +#endif + { + // Old buffer was stack or allocated, new buffer is allocated + newBits = reinterpret_cast(MBP_ALLOC(sizeof(PxU32)*newSize)); + if(mSize) + PxMemCopy(newBits, mBits, sizeof(PxU32)*mSize); + } +#ifdef BIT_ARRAY_STACK + else + { + newBits = mStack; + if(mSize>BIT_ARRAY_STACK) + { + // Old buffer was allocated, new buffer is stack => copy to stack, shrink + CopyMemory(newBits, mBits, sizeof(PxU32)*BIT_ARRAY_STACK); + } + else + { + // Old buffer was stack, new buffer is stack => keep working on the same stack buffer, nothing to do + } + } +#endif + const PxU32 remain = newSize - mSize; + if(remain) + PxMemZero(newBits + mSize, remain*sizeof(PxU32)); + +#ifdef BIT_ARRAY_STACK + if(mBits!=mStack) +#endif + MBP_FREE(mBits); + mBits = newBits; + mSize = newSize; +} + +#ifdef USE_FULLY_INSIDE_FLAG +static PX_FORCE_INLINE void setBit(BitArray& bitmap, MBP_ObjectIndex objectIndex) +{ + #ifdef HWSCAN + bitmap.clearBitChecked(objectIndex); //HWSCAN + #else + bitmap.setBitChecked(objectIndex); + #endif +} + +static PX_FORCE_INLINE void clearBit(BitArray& bitmap, MBP_ObjectIndex objectIndex) +{ + #ifdef HWSCAN + bitmap.setBitChecked(objectIndex); // HWSCAN + #else + bitmap.clearBitChecked(objectIndex); + #endif +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef MBP_SIMD_OVERLAP + typedef SIMD_AABB MBP_AABB; +#else + typedef IAABB MBP_AABB; +#endif + + struct MBPEntry; + struct RegionHandle; + struct MBP_Object; + + class MBP_PairManager : public PairManagerData + { + public: + MBP_PairManager(); + ~MBP_PairManager(); + + InternalPair* addPair (PxU32 id0, PxU32 id1); +// bool removePair (PxU32 id0, PxU32 id1); + bool computeCreatedDeletedPairs (const MBP_Object* objects, BroadPhaseMBP* mbp, const BitArray& updated, const BitArray& removed); + + const Bp::FilterGroup::Enum* mGroups; + const MBP_Object* mObjects; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* mLUT; +#endif + }; + + /////////////////////////////////////////////////////////////////////////// + + #define STACK_BUFFER_SIZE 256 + struct MBPOS_TmpBuffers + { + MBPOS_TmpBuffers(); + ~MBPOS_TmpBuffers(); + + void allocateSleeping(PxU32 nbSleeping, PxU32 nbSentinels); + void allocateUpdated(PxU32 nbUpdated, PxU32 nbSentinels); + + // PT: wtf, why doesn't the 128 version compile? +// MBP_AABB PX_ALIGN(128, mSleepingDynamicBoxes_Stack[STACK_BUFFER_SIZE]); +// MBP_AABB PX_ALIGN(128, mUpdatedDynamicBoxes_Stack[STACK_BUFFER_SIZE]); + MBP_AABB PX_ALIGN(16, mSleepingDynamicBoxes_Stack[STACK_BUFFER_SIZE]); + MBP_AABB PX_ALIGN(16, mUpdatedDynamicBoxes_Stack[STACK_BUFFER_SIZE]); + MBP_Index mInToOut_Dynamic_Sleeping_Stack[STACK_BUFFER_SIZE]; + + PxU32 mNbSleeping; + PxU32 mNbUpdated; + MBP_Index* mInToOut_Dynamic_Sleeping; + MBP_AABB* mSleepingDynamicBoxes; + MBP_AABB* mUpdatedDynamicBoxes; + }; + + struct BIP_Input + { + BIP_Input() : + mObjects (NULL), + mNbUpdatedBoxes (0), + mNbStaticBoxes (0), + mDynamicBoxes (NULL), + mStaticBoxes (NULL), + mInToOut_Static (NULL), + mInToOut_Dynamic(NULL), + mNeeded (false) + { + } + + const MBPEntry* mObjects; + PxU32 mNbUpdatedBoxes; + PxU32 mNbStaticBoxes; + const MBP_AABB* mDynamicBoxes; + const MBP_AABB* mStaticBoxes; + const MBP_Index* mInToOut_Static; + const MBP_Index* mInToOut_Dynamic; + bool mNeeded; + }; + + struct BoxPruning_Input + { + BoxPruning_Input() : + mObjects (NULL), + mUpdatedDynamicBoxes (NULL), + mSleepingDynamicBoxes (NULL), + mInToOut_Dynamic (NULL), + mInToOut_Dynamic_Sleeping (NULL), + mNbUpdated (0), + mNbNonUpdated (0), + mNeeded (false) + { + } + + const MBPEntry* mObjects; + const MBP_AABB* mUpdatedDynamicBoxes; + const MBP_AABB* mSleepingDynamicBoxes; + const MBP_Index* mInToOut_Dynamic; + const MBP_Index* mInToOut_Dynamic_Sleeping; + PxU32 mNbUpdated; + PxU32 mNbNonUpdated; + bool mNeeded; + + BIP_Input mBIPInput; + }; + + class Region : public Ps::UserAllocated + { + PX_NOCOPY(Region) + public: + Region(); + ~Region(); + + void updateObject(const MBP_AABB& bounds, MBP_Index handle); + MBP_Index addObject(const MBP_AABB& bounds, MBP_Handle mbpHandle, bool isStatic); + void removeObject(MBP_Index handle); + MBP_Handle retrieveBounds(MBP_AABB& bounds, MBP_Index handle) const; + void setBounds(MBP_Index handle, const MBP_AABB& bounds); + void prepareOverlaps(); + void findOverlaps(MBP_PairManager& pairManager); + +// private: + BoxPruning_Input PX_ALIGN(16, mInput); + PxU32 mNbObjects; + PxU32 mMaxNbObjects; + PxU32 mFirstFree; + MBPEntry* mObjects; // All objects, indexed by user handle + PxU32 mMaxNbStaticBoxes; + PxU32 mNbStaticBoxes; + PxU32 mMaxNbDynamicBoxes; + PxU32 mNbDynamicBoxes; + MBP_AABB* mStaticBoxes; + MBP_AABB* mDynamicBoxes; + MBP_Mapping mInToOut_Static; // Maps static boxes to mObjects + MBP_Mapping mInToOut_Dynamic; // Maps dynamic boxes to mObjects + PxU32* mPosList; + PxU32 mNbUpdatedBoxes; + PxU32 mPrevNbUpdatedBoxes; + BitArray mStaticBits; + RadixSortBuffered mRS; + bool mNeedsSorting; + bool mNeedsSortingSleeping; + + MBPOS_TmpBuffers mTmpBuffers; + + void optimizeMemory(); + void resizeObjects(); + void staticSort(); + void preparePruning(MBPOS_TmpBuffers& buffers); + void prepareBIPPruning(const MBPOS_TmpBuffers& buffers); + }; + + /////////////////////////////////////////////////////////////////////////// + +// We have one of those for each Region within the MBP +struct RegionData : public Ps::UserAllocated +{ + MBP_AABB mBox; // Volume of space controlled by this Region + Region* mBP; // Pointer to Region itself + Ps::IntBool mOverlap; // True if overlaps other regions + void* mUserData; // Region identifier, reused to contain "first free ID" +}; + + #define MAX_NB_MBP 256 +// #define MAX_NB_MBP 16 + + class MBP : public Ps::UserAllocated + { + public: + MBP(); + ~MBP(); + + void preallocate(PxU32 nbRegions, PxU32 nbObjects, PxU32 maxNbOverlaps); + void reset(); + void freeBuffers(); + + PxU32 addRegion(const PxBroadPhaseRegion& region, bool populateRegion); + bool removeRegion(PxU32 handle); + const Region* getRegion(PxU32 i) const; + PX_FORCE_INLINE PxU32 getNbRegions() const { return mNbRegions; } + + MBP_Handle addObject(const MBP_AABB& box, BpHandle userID, bool isStatic); + bool removeObject(MBP_Handle handle); + bool updateObject(MBP_Handle handle, const MBP_AABB& box); + bool updateObjectAfterRegionRemoval(MBP_Handle handle, Region* removedRegion); + bool updateObjectAfterNewRegionAdded(MBP_Handle handle, const MBP_AABB& box, Region* addedRegion, PxU32 regionIndex); + void prepareOverlaps(); + void findOverlaps(const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ); + PxU32 finalize(BroadPhaseMBP* mbp); + void shiftOrigin(const PxVec3& shift); + + void setTransientBounds(const PxBounds3* bounds, const PxReal* contactDistance); +// private: + PxU32 mNbRegions; + MBP_ObjectIndex mFirstFreeIndex; // First free recycled index for mMBP_Objects + PxU32 mFirstFreeIndexBP; // First free recycled index for mRegions + Ps::Array mRegions; + Ps::Array mMBP_Objects; + MBP_PairManager mPairManager; + + BitArray mUpdatedObjects; // Indexed by MBP_ObjectIndex + BitArray mRemoved; // Indexed by MBP_ObjectIndex + Ps::Array mHandles[MAX_NB_MBP+1]; + PxU32 mFirstFree[MAX_NB_MBP+1]; + PX_FORCE_INLINE RegionHandle* getHandles(MBP_Object& currentObject, PxU32 nbHandles); + void purgeHandles(MBP_Object* PX_RESTRICT object, PxU32 nbHandles); + void storeHandles(MBP_Object* PX_RESTRICT object, PxU32 nbHandles, const RegionHandle* PX_RESTRICT handles); + + Ps::Array mOutOfBoundsObjects; // These are BpHandle but the BP interface expects PxU32s + void addToOutOfBoundsArray(BpHandle id); + + const PxBounds3* mTransientBounds; + const PxReal* mTransientContactDistance; + +#ifdef USE_FULLY_INSIDE_FLAG + BitArray mFullyInsideBitmap; // Indexed by MBP_ObjectIndex +#endif + void populateNewRegion(const MBP_AABB& box, Region* addedRegion, PxU32 regionIndex); + +#ifdef MBP_REGION_BOX_PRUNING + void buildRegionData(); + MBP_AABB mSortedRegionBoxes[MAX_NB_MBP]; + PxU32 mSortedRegionIndices[MAX_NB_MBP]; + PxU32 mNbActiveRegions; + bool mDirtyRegions; +#endif + }; + +#ifdef MBP_SIMD_OVERLAP + #define MBP_OVERLAP_TEST(x) SIMD_OVERLAP_TEST(x) +#else + #define MBP_OVERLAP_TEST(x) if(intersect2D(box0, x)) +#endif + +#define DEFAULT_NB_ENTRIES 128 + +#ifdef MBP_SIMD_OVERLAP + static PX_FORCE_INLINE void initSentinel(SIMD_AABB& box) + { + // box.mMinX = encodeFloat(FLT_MAX)>>1; + box.mMinX = 0xffffffff; + } + #if PX_DEBUG + static PX_FORCE_INLINE bool isSentinel(const SIMD_AABB& box) + { + return box.mMinX == 0xffffffff; + } + #endif +#else + static PX_FORCE_INLINE void initSentinel(MBP_AABB& box) + { + // box.mMinX = encodeFloat(FLT_MAX)>>1; + box.mMinX = 0xffffffff; + } + #if PX_DEBUG + static PX_FORCE_INLINE bool isSentinel(const MBP_AABB& box) + { + return box.mMinX == 0xffffffff; + } + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// + +MBP_PairManager::MBP_PairManager() : + mGroups (NULL), + mObjects (NULL) +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + ,mLUT (NULL) +#endif +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +MBP_PairManager::~MBP_PairManager() +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +InternalPair* MBP_PairManager::addPair(PxU32 id0, PxU32 id1) +{ + PX_ASSERT(id0!=INVALID_ID); + PX_ASSERT(id1!=INVALID_ID); + PX_ASSERT(mGroups); + PX_ASSERT(mObjects); + + { + const MBP_ObjectIndex index0 = decodeHandle_Index(id0); + const MBP_ObjectIndex index1 = decodeHandle_Index(id1); + + const BpHandle object0 = mObjects[index0].mUserID; + const BpHandle object1 = mObjects[index1].mUserID; + +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + if(!groupFiltering(mGroups[object0], mGroups[object1], mLUT)) +#else + if(!groupFiltering(mGroups[object0], mGroups[object1])) +#endif + return NULL; + } + + return addPairInternal(id0, id1); +} + +/////////////////////////////////////////////////////////////////////////////// + +/*bool MBP_PairManager::removePair(PxU32 id0, PxU32 id1) +{ + // Order the ids + sort(id0, id1); + + const PxU32 hashValue = hash(id0, id1) & mMask; + const InternalPair* p = findPair(id0, id1, hashValue); + if(!p) + return false; + PX_ASSERT(p->getId0()==id0); + PX_ASSERT(p->getId1()==id1); + + PairManagerData::removePair(id0, id1, hashValue, getPairIndex(p)); + + shrinkMemory(); + return true; +}*/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef MBP_SIMD_OVERLAP + #define SIMD_OVERLAP_PRELOAD_BOX0 \ + __m128i b = _mm_loadu_si128(reinterpret_cast(&box0.mMinY)); \ + b = _mm_shuffle_epi32(b, 78); + + // PT: technically we don't need the 16 bits from _mm_movemask_epi8, we only + // need the 4 bits from _mm_movemask_ps. Would it be faster? In any case this + // works thanks to the _mm_cmpgt_epi32 which puts the same values in each byte + // of each separate 32bits components. + #define SIMD_OVERLAP_TEST(x) \ + const __m128i a = _mm_loadu_si128(reinterpret_cast(&x.mMinY)); \ + const __m128i d = _mm_cmpgt_epi32(a, b); \ + const int mask = _mm_movemask_epi8(d); \ + if(mask==0x0000ff00) + +#else + #define SIMD_OVERLAP_PRELOAD_BOX0 +#endif + +#ifdef MBP_USE_NO_CMP_OVERLAP +/*static PX_FORCE_INLINE void initBox(IAABB& box, const PxBounds3& src) +{ + box.initFrom2(src); +}*/ +#else +static PX_FORCE_INLINE void initBox(IAABB& box, const PxBounds3& src) +{ + box.initFrom(src); +} +#endif + +Region::Region() : + mNbObjects (0), + mMaxNbObjects (0), + mFirstFree (INVALID_ID), + mObjects (NULL), + mMaxNbStaticBoxes (0), + mNbStaticBoxes (0), + mMaxNbDynamicBoxes (0), + mNbDynamicBoxes (0), + mStaticBoxes (NULL), + mDynamicBoxes (NULL), + mInToOut_Static (NULL), + mInToOut_Dynamic (NULL), + mPosList (NULL), + mNbUpdatedBoxes (0), + mPrevNbUpdatedBoxes (0), + mNeedsSorting (false), + mNeedsSortingSleeping (true) +{ +} + +Region::~Region() +{ + DELETEARRAY(mObjects); + MBP_FREE(mPosList); + MBP_FREE(mInToOut_Dynamic); + MBP_FREE(mInToOut_Static); + DELETEARRAY(mDynamicBoxes); + DELETEARRAY(mStaticBoxes); +} + +// Pre-sort static boxes +#define STACK_BUFFER_SIZE_STATIC_SORT 8192 + #define DEFAULT_NUM_DYNAMIC_BOXES 1024 + +void Region::staticSort() +{ + // For now this version is only compatible with: + // MBP_USE_WORDS + // MBP_USE_SENTINELS + + mNeedsSorting = false; + + const PxU32 nbStaticBoxes = mNbStaticBoxes; + if(!nbStaticBoxes) + { + mStaticBits.empty(); + return; + } + +// PxU32 Time; +// StartProfile(Time); + + // Roadmap: + // - gather updated/modified static boxes + // - sort those, and those only + // - merge sorted set with previously existing (and previously sorted set) + + // Separate things-to-sort and things-already-sorted + const PxU32 totalSize = sizeof(PxU32)*nbStaticBoxes*4; + PxU8 stackBuffer[STACK_BUFFER_SIZE_STATIC_SORT]; + PxU8* tempMemory = totalSize<=STACK_BUFFER_SIZE_STATIC_SORT ? stackBuffer : reinterpret_cast(MBP_ALLOC_TMP(totalSize)); + PxU32* minPosList_ToSort = reinterpret_cast(tempMemory); + PxU32* minPosList_Sorted = reinterpret_cast(tempMemory + sizeof(PxU32)*nbStaticBoxes); + PxU32* boxIndices_ToSort = reinterpret_cast(tempMemory + sizeof(PxU32)*nbStaticBoxes*2); + PxU32* boxIndices_Sorted = reinterpret_cast(tempMemory + sizeof(PxU32)*nbStaticBoxes*3); + PxU32 nbToSort = 0; + PxU32 nbSorted = 0; + for(PxU32 i=0;i(MBP_ALLOC(sizeof(MBP_Index)*mMaxNbStaticBoxes)); + const PxU32 nbStaticSentinels = 2; + MBP_AABB* sortedBoxes = PX_NEW(MBP_AABB)[mMaxNbStaticBoxes+nbStaticSentinels]; + initSentinel(sortedBoxes[nbStaticBoxes]); + initSentinel(sortedBoxes[nbStaticBoxes+1]); + +// EndProfile(Time); +// printf("Part2b: %d\n", Time); + +// StartProfile(Time); + + // Merge streams to final buffers + PxU32 offsetSorted = 0; + PxU32 offsetNonSorted = 0; + + PxU32 nextCandidateNonSorted = offsetNonSorted(MBP_ALLOC(sizeof(MBP_Index)*newNbBoxes)); + if(oldNbBoxes) + PxMemCopy(newMapping, mapping, oldNbBoxes*sizeof(MBP_Index)); + MBP_FREE(mapping); + return newMapping; +} + +static PX_FORCE_INLINE void MTF(MBP_AABB* PX_RESTRICT dynamicBoxes, MBP_Index* PX_RESTRICT inToOut_Dynamic, MBPEntry* PX_RESTRICT objects, const MBP_AABB& bounds, PxU32 frontIndex, MBPEntry& updatedObject) +{ + const PxU32 updatedIndex = updatedObject.mIndex; + if(frontIndex!=updatedIndex) + { + const MBP_AABB box0 = dynamicBoxes[frontIndex]; + dynamicBoxes[frontIndex] = bounds; + dynamicBoxes[updatedIndex] = box0; + + const MBP_Index index0 = inToOut_Dynamic[frontIndex]; + inToOut_Dynamic[frontIndex] = inToOut_Dynamic[updatedIndex]; + inToOut_Dynamic[updatedIndex] = index0; + + objects[index0].mIndex = updatedIndex; + updatedObject.mIndex = frontIndex; + } + else + { + dynamicBoxes[frontIndex] = bounds; + } +} + +MBP_Index Region::addObject(const MBP_AABB& bounds, MBP_Handle mbpHandle, bool isStatic) +{ +#ifdef MBP_USE_WORDS + PX_ASSERT(mNbObjects<0xffff); +#endif + PX_ASSERT((decodeHandle_IsStatic(mbpHandle) && isStatic) || (!decodeHandle_IsStatic(mbpHandle) && !isStatic)); + + MBP_Index handle; + if(mFirstFree!=INVALID_ID) + { + handle = MBP_Index(mFirstFree); + mFirstFree = mObjects[handle].mIndex; + } + else + { + if(mMaxNbObjects==mNbObjects) + resizeObjects(); + + handle = MBP_Index(mNbObjects); + } + mNbObjects++; + /// + + PxU32 boxIndex; + if(isStatic) + { + if(mMaxNbStaticBoxes==mNbStaticBoxes) + { + const PxU32 newMaxNbBoxes = mMaxNbStaticBoxes ? mMaxNbStaticBoxes + DEFAULT_NB_ENTRIES : DEFAULT_NB_ENTRIES; +// const PxU32 newMaxNbBoxes = mMaxNbStaticBoxes ? mMaxNbStaticBoxes*2 : DEFAULT_NB_ENTRIES; + mStaticBoxes = resizeBoxes(mNbStaticBoxes, newMaxNbBoxes, mStaticBoxes); + mInToOut_Static = resizeMapping(mNbStaticBoxes, newMaxNbBoxes, mInToOut_Static); + mMaxNbStaticBoxes = newMaxNbBoxes; + } + + boxIndex = mNbStaticBoxes++; + mStaticBoxes[boxIndex] = bounds; + mInToOut_Static[boxIndex] = handle; + mNeedsSorting = true; + mStaticBits.setBitChecked(boxIndex); + } + else + { + if(mMaxNbDynamicBoxes==mNbDynamicBoxes) + { + const PxU32 newMaxNbBoxes = mMaxNbDynamicBoxes ? mMaxNbDynamicBoxes + DEFAULT_NB_ENTRIES : DEFAULT_NB_ENTRIES; +// const PxU32 newMaxNbBoxes = mMaxNbDynamicBoxes ? mMaxNbDynamicBoxes*2 : DEFAULT_NB_ENTRIES; + mDynamicBoxes = resizeBoxes(mNbDynamicBoxes, newMaxNbBoxes, mDynamicBoxes); + mInToOut_Dynamic = resizeMapping(mNbDynamicBoxes, newMaxNbBoxes, mInToOut_Dynamic); + mMaxNbDynamicBoxes = newMaxNbBoxes; + + MBP_FREE(mPosList); + mPosList = reinterpret_cast(MBP_ALLOC((newMaxNbBoxes+1)*sizeof(PxU32))); + } + + boxIndex = mNbDynamicBoxes++; + mDynamicBoxes[boxIndex] = bounds; + mInToOut_Dynamic[boxIndex] = handle; + } + + mObjects[handle].mIndex = boxIndex; + mObjects[handle].mMBPHandle = mbpHandle; +#if PX_DEBUG + mObjects[handle].mUpdated = !isStatic; +#endif + + if(!isStatic) + { + MTF(mDynamicBoxes, mInToOut_Dynamic, mObjects, bounds, mNbUpdatedBoxes, mObjects[handle]); + mNbUpdatedBoxes++; + mPrevNbUpdatedBoxes = 0; + mNeedsSortingSleeping = true; + PX_ASSERT(mNbUpdatedBoxes<=mNbDynamicBoxes); + } + return handle; +} + +// Moves box 'lastIndex' to location 'removedBoxIndex' +static PX_FORCE_INLINE void remove(MBPEntry* PX_RESTRICT objects, MBP_Index* PX_RESTRICT mapping, MBP_AABB* PX_RESTRICT boxes, PxU32 removedBoxIndex, PxU32 lastIndex) +{ + const PxU32 movedBoxHandle = mapping[lastIndex]; + boxes[removedBoxIndex] = boxes[lastIndex]; // Relocate box data + mapping[removedBoxIndex] = MBP_Index(movedBoxHandle); // Relocate mapping data + MBPEntry& movedObject = objects[movedBoxHandle]; + PX_ASSERT(movedObject.mIndex==lastIndex); // Checks index of moved box was indeed its old location + movedObject.mIndex = removedBoxIndex; // Adjust index of moved box to reflect its new location +} + +void Region::removeObject(MBP_Index handle) +{ + PX_ASSERT(handle>1)|(bits2>>2)|(bits3>>3); + return !mask; + + /* const PxU32 d0 = (b.mMaxY<<16)|a.mMaxY; + const PxU32 d0b = (b.mMaxZ<<16)|a.mMaxZ; + const PxU32 d1 = (a.mMinY<<16)|b.mMinY; + const PxU32 d1b = (a.mMinZ<<16)|b.mMinZ; + const PxU32 mask = (d0 - d1) | (d0b - d1b); + return !(mask & 0x80008000);*/ +#else + if(//mMaxX < a.mMinX || a.mMaxX < mMinX +// || + b.mMaxY < a.mMinY || a.mMaxY < b.mMinY + || + b.mMaxZ < a.mMinZ || a.mMaxZ < b.mMinZ + ) + return FALSE; + return TRUE; +#endif +} +#endif + +#ifdef MBP_USE_NO_CMP_OVERLAP_3D +static PX_FORCE_INLINE bool intersect3D(const MBP_AABB& a, const MBP_AABB& b) +{ + // PT: warning, only valid with the special encoding in InitFrom2 + const PxU32 bits0 = (b.mMaxY - a.mMinY)&0x80000000; + const PxU32 bits1 = (b.mMaxZ - a.mMinZ)&0x80000000; + const PxU32 bits2 = (a.mMaxY - b.mMinY)&0x80000000; + const PxU32 bits3 = (a.mMaxZ - b.mMinZ)&0x80000000; + const PxU32 bits4 = (b.mMaxX - a.mMinX)&0x80000000; + const PxU32 bits5 = (a.mMaxX - b.mMinX)&0x80000000; + const PxU32 mask = bits0|(bits1>>1)|(bits2>>2)|(bits3>>3)|(bits4>>4)|(bits5>>5); + return !mask; +} +#endif + +#ifdef CHECK_NB_OVERLAPS +static PxU32 gNbOverlaps = 0; +#endif + +static PX_FORCE_INLINE void outputPair( MBP_PairManager& pairManager, + PxU32 index0, PxU32 index1, + const MBP_Index* PX_RESTRICT inToOut0, const MBP_Index* PX_RESTRICT inToOut1, + const MBPEntry* PX_RESTRICT objects) +{ +#ifdef CHECK_NB_OVERLAPS + gNbOverlaps++; +#endif + const MBP_Index objectIndex0 = inToOut0[index0]; + const MBP_Index objectIndex1 = inToOut1[index1]; + PX_ASSERT(objectIndex0!=objectIndex1); + const MBP_Handle id0 = objects[objectIndex0].mMBPHandle; + const MBP_Handle id1 = objects[objectIndex1].mMBPHandle; +// printf("2: %d %d\n", index0, index1); +// printf("3: %d %d\n", objectIndex0, objectIndex1); + pairManager.addPair(id0, id1); +} + +MBPOS_TmpBuffers::MBPOS_TmpBuffers() : + mNbSleeping (0), + mNbUpdated (0), + mInToOut_Dynamic_Sleeping (NULL), + mSleepingDynamicBoxes (NULL), + mUpdatedDynamicBoxes (NULL) +{ +} + +MBPOS_TmpBuffers::~MBPOS_TmpBuffers() +{ +// printf("mNbSleeping: %d\n", mNbSleeping); + if(mInToOut_Dynamic_Sleeping!=mInToOut_Dynamic_Sleeping_Stack) + MBP_FREE(mInToOut_Dynamic_Sleeping); + + if(mSleepingDynamicBoxes!=mSleepingDynamicBoxes_Stack) + DELETEARRAY(mSleepingDynamicBoxes); + + if(mUpdatedDynamicBoxes!=mUpdatedDynamicBoxes_Stack) + DELETEARRAY(mUpdatedDynamicBoxes); + + mNbSleeping = 0; + mNbUpdated = 0; +} + +void MBPOS_TmpBuffers::allocateSleeping(PxU32 nbSleeping, PxU32 nbSentinels) +{ + if(nbSleeping>mNbSleeping) + { + if(mInToOut_Dynamic_Sleeping!=mInToOut_Dynamic_Sleeping_Stack) + MBP_FREE(mInToOut_Dynamic_Sleeping); + if(mSleepingDynamicBoxes!=mSleepingDynamicBoxes_Stack) + DELETEARRAY(mSleepingDynamicBoxes); + + if(nbSleeping+nbSentinels<=STACK_BUFFER_SIZE) + { + mSleepingDynamicBoxes = mSleepingDynamicBoxes_Stack; + mInToOut_Dynamic_Sleeping = mInToOut_Dynamic_Sleeping_Stack; + } + else + { + mSleepingDynamicBoxes = PX_NEW_TEMP(MBP_AABB)[nbSleeping+nbSentinels]; + mInToOut_Dynamic_Sleeping = reinterpret_cast(MBP_ALLOC(sizeof(MBP_Index)*nbSleeping)); + } + mNbSleeping = nbSleeping; + } +} + +void MBPOS_TmpBuffers::allocateUpdated(PxU32 nbUpdated, PxU32 nbSentinels) +{ + if(nbUpdated>mNbUpdated) + { + if(mUpdatedDynamicBoxes!=mUpdatedDynamicBoxes_Stack) + DELETEARRAY(mUpdatedDynamicBoxes); + + if(nbUpdated+nbSentinels<=STACK_BUFFER_SIZE) + mUpdatedDynamicBoxes = mUpdatedDynamicBoxes_Stack; + else + mUpdatedDynamicBoxes = PX_NEW_TEMP(MBP_AABB)[nbUpdated+nbSentinels]; + + mNbUpdated = nbUpdated; + } +} + +void Region::preparePruning(MBPOS_TmpBuffers& buffers) +{ +PxU32 _saved = mNbUpdatedBoxes; +mNbUpdatedBoxes = 0; + + if(mPrevNbUpdatedBoxes!=_saved) + mNeedsSortingSleeping = true; + + PxU32 nb = mNbDynamicBoxes; + if(!nb) + { + mInput.mNeeded = false; + mPrevNbUpdatedBoxes = 0; + mNeedsSortingSleeping = true; + return; + } + const MBP_AABB* PX_RESTRICT dynamicBoxes = mDynamicBoxes; + PxU32* PX_RESTRICT posList = mPosList; + +#if PX_DEBUG + PxU32 verifyNbUpdated = 0; + for(PxU32 i=0;i(mRS.GetRecyclable()); + for(PxU32 i=0;i buffers.mUpdatedDynamicBoxes; + mInput.mSleepingDynamicBoxes = sleepingDynamicBoxes; + mInput.mInToOut_Dynamic = inToOut_Dynamic; // Can be shared (3) => (MBP_Index*)mRS.GetRecyclable(); + mInput.mInToOut_Dynamic_Sleeping = inToOut_Dynamic_Sleeping; + mInput.mNbUpdated = nbUpdated; // Can be shared (4) + mInput.mNbNonUpdated = nbNonUpdated; + mInput.mNeeded = true; +} + +void Region::prepareBIPPruning(const MBPOS_TmpBuffers& buffers) +{ + if(!mNbUpdatedBoxes || !mNbStaticBoxes) + { + mInput.mBIPInput.mNeeded = false; + return; + } + + mInput.mBIPInput.mObjects = mObjects; // Can be shared (1) + mInput.mBIPInput.mNbUpdatedBoxes = mNbUpdatedBoxes; // Can be shared (4) + mInput.mBIPInput.mNbStaticBoxes = mNbStaticBoxes; +// mInput.mBIPInput.mDynamicBoxes = mDynamicBoxes; + mInput.mBIPInput.mDynamicBoxes = buffers.mUpdatedDynamicBoxes; // Can be shared (2) + mInput.mBIPInput.mStaticBoxes = mStaticBoxes; + mInput.mBIPInput.mInToOut_Static = mInToOut_Static; + mInput.mBIPInput.mInToOut_Dynamic = reinterpret_cast(mRS.GetRecyclable()); // Can be shared (3) + mInput.mBIPInput.mNeeded = true; +} + +static void doCompleteBoxPruning(MBP_PairManager* PX_RESTRICT pairManager, const BoxPruning_Input& input) +{ + const MBPEntry* PX_RESTRICT objects = input.mObjects; + const MBP_AABB* PX_RESTRICT updatedDynamicBoxes = input.mUpdatedDynamicBoxes; + const MBP_AABB* PX_RESTRICT sleepingDynamicBoxes = input.mSleepingDynamicBoxes; + const MBP_Index* PX_RESTRICT inToOut_Dynamic = input.mInToOut_Dynamic; + const MBP_Index* PX_RESTRICT inToOut_Dynamic_Sleeping = input.mInToOut_Dynamic_Sleeping; + const PxU32 nbUpdated = input.mNbUpdated; + const PxU32 nbNonUpdated = input.mNbNonUpdated; + + // + + // PT: find sleeping-dynamics-vs-active-dynamics overlaps + if(nbNonUpdated) + { + const PxU32 nb0 = nbUpdated; + const PxU32 nb1 = nbNonUpdated; + + // + PxU32 index0 = 0; + PxU32 runningIndex1 = 0; + + while(runningIndex1 +#endif + +// PT: TODO: +// - We could try to keep bounds around all objects (for each region), and then test the new region's bounds against these instead of +// testing all objects one by one. These new bounds (around all objects of a given region) would be delicate to maintain though. +// - Just store these "fully inside flags" (i.e. objects) in a separate list? Or can we do MTF on objects? (probably not, else we +// wouldn't have holes in the array due to removed objects) + +// PT: automatically populate new region with overlapping objects. +// Brute-force version checking all existing objects, potentially optimized using "fully inside" flags. +//#define FIRST_VERSION +#ifdef FIRST_VERSION +void MBP::populateNewRegion(const MBP_AABB& box, Region* addedRegion, PxU32 regionIndex) +{ + const RegionData* PX_RESTRICT regions = mRegions.begin(); + const PxU32 nbObjects = mMBP_Objects.size(); + MBP_Object* PX_RESTRICT objects = mMBP_Objects.begin(); + +#ifdef PRINT_STATS + PxU32 nbObjectsFound = 0; + PxU32 nbObjectsTested = 0; +#endif + +#ifdef USE_FULLY_INSIDE_FLAG + const PxU32* fullyInsideFlags = mFullyInsideBitmap.getBits(); +#endif + + PxU32 j=0; + while(j>5]; + #ifdef HWSCAN + if(blockFlags==0) //HWSCAN + #else + if(blockFlags==0xffffffff) + #endif + { + j+=32; + continue; + } + PxU32 nbToGo = PxMin(nbObjects - j, PxU32(32)); + PxU32 mask = 1; + while(nbToGo--) + { + + MBP_Object& currentObject = objects[j]; + // PT: if an object A is fully contained inside all the regions S it overlaps, we don't need to test it against the new region R. + // The rationale is that even if R does overlap A, any new object B must touch S to overlap with A. So B would be added to S and + // the (A,B) overlap would be detected in S, even if it's not detected in R. + const PxU32 res = blockFlags & mask; + PX_ASSERT((mFullyInsideBitmap.isSet(j) && res) || (!mFullyInsideBitmap.isSet(j) && !res)); + mask+=mask; + j++; + #ifdef HWSCAN + if(!res) //HWSCAN + #else + if(res) + #endif + continue; + PX_ASSERT(!(currentObject.mFlags & MBP_REMOVED)); +#else + MBP_Object& currentObject = objects[j++]; + if(currentObject.mFlags & MBP_REMOVED) + continue; // PT: object is in the free list +#endif + +#ifdef PRINT_STATS + nbObjectsTested++; +#endif + + MBP_AABB bounds; + MBP_Handle mbpHandle; + + const PxU32 nbHandles = currentObject.mNbHandles; + if(nbHandles) + { + RegionHandle* PX_RESTRICT handles = getHandles(currentObject, nbHandles); + // PT: no need to test all regions since they should contain the same info. Just retrieve bounds from the first one. + PxU32 i=0; // for(PxU32 i=0;iretrieveBounds(bounds, h.mHandle); + } + } + else + { + PX_ASSERT(mManager); + + // PT: if the object is out-of-bounds, we're out-of-luck. We don't have the object bounds, so we need to retrieve them + // from the AABB manager - and then re-encode them. This is not very elegant or efficient, but it should rarely happen + // so this is good enough for now. + const PxBounds3 decodedBounds = mManager->getBPBounds(currentObject.mUserID); + + bounds.initFrom2(decodedBounds); + + mbpHandle = currentObject.mHandlesIndex; + } + + if(bounds.intersect(box)) + { +// updateObject(mbpHandle, bounds); + updateObjectAfterNewRegionAdded(mbpHandle, bounds, addedRegion, regionIndex); +#ifdef PRINT_STATS + nbObjectsFound++; +#endif + } + +#ifdef USE_FULLY_INSIDE_FLAG + } +#endif + } +#ifdef PRINT_STATS + printf("Populating new region with %d objects (tested %d/%d object)\n", nbObjectsFound, nbObjectsTested, nbObjects); +#endif +} +#endif + +// PT: version using lowestSetBit +#define SECOND_VERSION +#ifdef SECOND_VERSION + +/* PX_FORCE_INLINE PxU32 lowestSetBitUnsafe64(PxU64 v) + { + unsigned long retval; + _BitScanForward64(&retval, v); + return retval; + }*/ + +void MBP::populateNewRegion(const MBP_AABB& box, Region* addedRegion, PxU32 regionIndex) +{ + const RegionData* PX_RESTRICT regions = mRegions.begin(); + const PxU32 nbObjects = mMBP_Objects.size(); + PX_UNUSED(nbObjects); + MBP_Object* PX_RESTRICT objects = mMBP_Objects.begin(); + const PxU32* fullyInsideFlags = mFullyInsideBitmap.getBits(); +// const PxU64* fullyInsideFlags = (const PxU64*)mFullyInsideBitmap.getBits(); + if(!fullyInsideFlags) + return; + + const PxU32 lastSetBit = mFullyInsideBitmap.findLast(); +// const PxU64 lastSetBit = mFullyInsideBitmap.findLast(); + +#ifdef PRINT_STATS + PxU32 nbObjectsFound = 0; + PxU32 nbObjectsTested = 0; +#endif + + for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) +// for(PxU64 w = 0; w <= lastSetBit >> 6; ++w) + { + for(PxU32 b = fullyInsideFlags[w]; b; b &= b-1) +// for(PxU64 b = fullyInsideFlags[w]; b; b &= b-1) + { + const PxU32 index = PxU32(w<<5|Ps::lowestSetBit(b)); +// const PxU64 index = (PxU64)(w<<6|::lowestSetBitUnsafe64(b)); + PX_ASSERT(indexretrieveBounds(bounds, h.mHandle); + } + } + else + { + // PT: if the object is out-of-bounds, we're out-of-luck. We don't have the object bounds, so we need to retrieve them + // from the AABB manager - and then re-encode them. This is not very elegant or efficient, but it should rarely happen + // so this is good enough for now. + const PxBounds3 rawBounds = mTransientBounds[currentObject.mUserID]; + PxVec3 c(mTransientContactDistance[currentObject.mUserID]); + const PxBounds3 decodedBounds(rawBounds.minimum - c, rawBounds.maximum + c); + bounds.initFrom2(decodedBounds); + + mbpHandle = currentObject.mHandlesIndex; + } + + if(bounds.intersects(box)) + { +// updateObject(mbpHandle, bounds); + updateObjectAfterNewRegionAdded(mbpHandle, bounds, addedRegion, regionIndex); +#ifdef PRINT_STATS + nbObjectsFound++; +#endif + } + } + } +#ifdef PRINT_STATS + printf("Populating new region with %d objects (tested %d/%d object)\n", nbObjectsFound, nbObjectsTested, nbObjects); +#endif +} +#endif + +//#define THIRD_VERSION +#ifdef THIRD_VERSION +void MBP::populateNewRegion(const MBP_AABB& box, Region* addedRegion, PxU32 regionIndex) +{ + const RegionData* PX_RESTRICT regions = mRegions.begin(); + const PxU32 nbObjects = mMBP_Objects.size(); + PX_UNUSED(nbObjects); + MBP_Object* PX_RESTRICT objects = mMBP_Objects.begin(); + + const PxU32* fullyInsideFlags = mFullyInsideBitmap.getBits(); + if(!fullyInsideFlags) + return; + +#ifdef PRINT_STATS + PxU32 nbObjectsFound = 0; + PxU32 nbObjectsTested = 0; +#endif + + Cm::BitMap bm; + bm.importData(mFullyInsideBitmap.getSize(), (PxU32*)fullyInsideFlags); + + Cm::BitMap::Iterator it(bm); + PxU32 index = it.getNext(); + while(index != Cm::BitMap::Iterator::DONE) + { + PX_ASSERT(indexretrieveBounds(bounds, h.mHandle); + } + } + else + { + PX_ASSERT(mManager); + + // PT: if the object is out-of-bounds, we're out-of-luck. We don't have the object bounds, so we need to retrieve them + // from the AABB manager - and then re-encode them. This is not very elegant or efficient, but it should rarely happen + // so this is good enough for now. + const PxBounds3 decodedBounds = mManager->getBPBounds(currentObject.mUserID); + + bounds.initFrom2(decodedBounds); + + mbpHandle = currentObject.mHandlesIndex; + } + + if(bounds.intersect(box)) + { +// updateObject(mbpHandle, bounds); + updateObjectAfterNewRegionAdded(mbpHandle, bounds, addedRegion, regionIndex); +#ifdef PRINT_STATS + nbObjectsFound++; +#endif + } + index = it.getNext(); + } +#ifdef PRINT_STATS + printf("Populating new region with %d objects (tested %d/%d object)\n", nbObjectsFound, nbObjectsTested, nbObjects); +#endif +} +#endif + +PxU32 MBP::addRegion(const PxBroadPhaseRegion& region, bool populateRegion) +{ + PxU32 regionHandle; + RegionData* PX_RESTRICT buffer; + + if(mFirstFreeIndexBP!=INVALID_ID) + { + regionHandle = mFirstFreeIndexBP; + + buffer = mRegions.begin(); + buffer += regionHandle; + + mFirstFreeIndexBP = PxU32(size_t(buffer->mUserData)); // PT: this is safe, we previously stored a PxU32 in there + } + else + { + if(mNbRegions>=MAX_NB_MBP) + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "MBP::addRegion: max number of regions reached."); + return INVALID_ID; + } + + regionHandle = mNbRegions++; + buffer = reserveContainerMemory(mRegions, 1); + } + + Region* newRegion = PX_NEW(Region); + buffer->mBox.initFrom2(region.bounds); + buffer->mBP = newRegion; + buffer->mUserData = region.userData; + + setupOverlapFlags(mNbRegions, mRegions.begin()); + + // PT: automatically populate new region with overlapping objects + if(populateRegion) + populateNewRegion(buffer->mBox, newRegion, regionHandle); + +#ifdef MBP_REGION_BOX_PRUNING + mDirtyRegions = true; +#endif + + return regionHandle; +} + +// ### TODO: recycle regions, make sure objects are properly deleted/transferred, etc +// ### TODO: what happens if we delete a zone then immediately add it back? Do objects get deleted? +// ### TODO: in fact if we remove a zone but we keep the objects, what happens to their current overlaps? Are they kept or discarded? +bool MBP::removeRegion(PxU32 handle) +{ + if(handle>=mNbRegions) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "MBP::removeRegion: invalid handle."); + return false; + } + + RegionData* PX_RESTRICT region = mRegions.begin(); + region += handle; + + Region* bp = region->mBP; + if(!bp) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "MBP::removeRegion: invalid handle."); + return false; + } + + PxBounds3 empty; + empty.setEmpty(); + region->mBox.initFrom2(empty); + + { + // We are going to remove the region but it can still contain objects. We need to update + // those objects so that their handles and out-of-bounds status are modified. + // + // Unfortunately there is no way to iterate over active objects in a region, so we need + // to iterate over the max amount of objects. ### TODO: optimize this + const PxU32 maxNbObjects = bp->mMaxNbObjects; + MBPEntry* PX_RESTRICT objects = bp->mObjects; + for(PxU32 j=0;jmBP = NULL; + region->mUserData = reinterpret_cast(size_t(mFirstFreeIndexBP)); + mFirstFreeIndexBP = handle; + +#ifdef MBP_REGION_BOX_PRUNING + mDirtyRegions = true; +#endif + + // A region has been removed so we need to update the overlap flags for all remaining regions + // ### TODO: optimize this + setupOverlapFlags(mNbRegions, mRegions.begin()); + return true; +} + +const Region* MBP::getRegion(PxU32 i) const +{ + if(i>=mNbRegions) + return NULL; + + const RegionData* PX_RESTRICT regions = mRegions.begin(); + return regions[i].mBP; +} + +#ifdef MBP_REGION_BOX_PRUNING +void MBP::buildRegionData() +{ + const PxU32 size = mNbRegions; + PxU32 nbValidRegions = 0; + if(size) + { + const RegionData* PX_RESTRICT regions = mRegions.begin(); + + // Gather valid regions + PxU32 minPosList[MAX_NB_MBP]; + for(PxU32 i=0;i& c = mHandles[nbHandles]; + handles = reinterpret_cast(c.begin()+handlesIndex); + } + return handles; +} + +void MBP::purgeHandles(MBP_Object* PX_RESTRICT object, PxU32 nbHandles) +{ + if(nbHandles>1) + { + const PxU32 handlesIndex = object->mHandlesIndex; + Ps::Array& c = mHandles[nbHandles]; + PxU32* recycled = c.begin() + handlesIndex; + *recycled = mFirstFree[nbHandles]; + mFirstFree[nbHandles] = handlesIndex; + } +} + +void MBP::storeHandles(MBP_Object* PX_RESTRICT object, PxU32 nbHandles, const RegionHandle* PX_RESTRICT handles) +{ + if(nbHandles==1) + { + object->mHandle = handles[0]; + } + else if(nbHandles) + { + Ps::Array& c = mHandles[nbHandles]; + const PxU32 firstFree = mFirstFree[nbHandles]; + PxU32* handlesMemory; + if(firstFree!=INVALID_ID) + { + object->mHandlesIndex = firstFree; + handlesMemory = c.begin() + firstFree; + mFirstFree[nbHandles] = *handlesMemory; + } + else + { + const PxU32 handlesIndex = c.size(); + object->mHandlesIndex = handlesIndex; + handlesMemory = reserveContainerMemory(c, sizeof(RegionHandle)*nbHandles/sizeof(PxU32)); + } + PxMemCopy(handlesMemory, handles, sizeof(RegionHandle)*nbHandles); + } +} + +MBP_Handle MBP::addObject(const MBP_AABB& box, BpHandle userID, bool isStatic) +{ + MBP_ObjectIndex objectIndex; + MBP_Object* objectMemory; + PxU32 flipFlop; + if(1) + { + if(mFirstFreeIndex!=INVALID_ID) + { + objectIndex = mFirstFreeIndex; + MBP_Object* objects = mMBP_Objects.begin(); + objectMemory = &objects[objectIndex]; + PX_ASSERT(!objectMemory->mNbHandles); + mFirstFreeIndex = objectMemory->mHandlesIndex; + flipFlop = PxU32(objectMemory->getFlipFlop()); + } + else + { + objectIndex = mMBP_Objects.size(); + objectMemory = reserveContainerMemory(mMBP_Objects, 1); + flipFlop = 0; + } + } + else + { + // PT: must be possible to use the AABB-manager's ID directly. Something like this: + objectIndex = userID; + if(mMBP_Objects.capacity()mNbObjects==0xffff) + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "MBP::addObject: 64K objects in single region reached. Some collisions might be lost."); + else +#endif + { + RegionHandle& h = tmpHandles[nbHandles++]; + h.mHandle = regions[i].mBP->addObject(box, MBPObjectHandle, isStatic); + h.mInternalBPHandle = Ps::to16(i); + } + } + } + storeHandles(objectMemory, nbHandles, tmpHandles); + + objectMemory->mNbHandles = Ps::to16(nbHandles); + PxU16 flags = 0; + if(flipFlop) + flags |= MBP_FLIP_FLOP; +#ifdef USE_FULLY_INSIDE_FLAG + if(nbHandles && newObjectIsFullyInsideRegions) + setBit(mFullyInsideBitmap, objectIndex); + else + clearBit(mFullyInsideBitmap, objectIndex); +#endif + if(!nbHandles) + { + objectMemory->mHandlesIndex = MBPObjectHandle; + addToOutOfBoundsArray(userID); + } + + if(!isStatic) + mUpdatedObjects.setBitChecked(objectIndex); + +// objectMemory->mUpdated = !isStatic; + objectMemory->mFlags = flags; + objectMemory->mUserID = userID; + + return MBPObjectHandle; +} + +bool MBP::removeObject(MBP_Handle handle) +{ + const MBP_ObjectIndex objectIndex = decodeHandle_Index(handle); + + MBP_Object* PX_RESTRICT objects = mMBP_Objects.begin(); + MBP_Object& currentObject = objects[objectIndex]; + const RegionData* PX_RESTRICT regions = mRegions.begin(); + // Parse previously overlapping regions. If still overlapping, update object. Else remove from region. + const PxU32 nbHandles = currentObject.mNbHandles; + if(nbHandles) + { + RegionHandle* handles = getHandles(currentObject, nbHandles); + for(PxU32 i=0;iremoveObject(h.mHandle); + } + + purgeHandles(¤tObject, nbHandles); + } + + currentObject.mNbHandles = 0; + currentObject.mFlags |= MBP_REMOVED; + currentObject.mHandlesIndex = mFirstFreeIndex; +// if(!decodeHandle_IsStatic(handle)) +// if(!currentObject.IsStatic()) + mUpdatedObjects.setBitChecked(objectIndex); + + mFirstFreeIndex = objectIndex; + + mRemoved.setBitChecked(objectIndex); // PT: this is cleared each frame so it's not a replacement for the MBP_REMOVED flag + +#ifdef USE_FULLY_INSIDE_FLAG + // PT: when removing an object we mark it as "fully inside" so that it is automatically + // discarded in the "populateNewRegion" function, without the need for MBP_REMOVED. + setBit(mFullyInsideBitmap, objectIndex); +#endif + return true; +} + +static PX_FORCE_INLINE bool stillIntersects(PxU32 handle, PxU32& _nb, PxU32* PX_RESTRICT currentOverlaps) +{ + const PxU32 nb = _nb; + for(PxU32 i=0;i nbHandles changes from 2 to 1 while MBP_FULLY_INSIDE is not set + setBit(mFullyInsideBitmap, objectIndex); +#endif + currentRegion.mBP->updateObject(box, h.mHandle); + return true; + } + } + + // Find regions overlapping object's new position +#ifdef USE_FULLY_INSIDE_FLAG + bool objectIsFullyInsideRegions = true; +#endif + PxU32 nbCurrentOverlaps = 0; + PxU32 currentOverlaps[MAX_NB_MBP+1]; + // PT: here, we may still parse regions which have been removed. But their boxes have been set to empty, + // so nothing will happen. + for(PxU32 i=0;iUpdateObject(box, h.mHandle); + if(stillIntersects(h.mInternalBPHandle, nbCurrentOverlaps, currentOverlaps)) + { + currentRegion.mBP->updateObject(box, h.mHandle); + // Still collides => keep handle for this frame + newHandles[nbNewHandles++] = h; + } + else + { + PX_ASSERT(!currentRegion.mBox.intersects(box)); +// if(currentRegion.mBP) + PX_ASSERT(currentRegion.mBP); + currentRegion.mBP->removeObject(h.mHandle); + } + } + + // Add to new regions if needed + for(PxU32 i=0;iaddObject(box, handle, isStatic!=0); + newHandles[nbNewHandles].mHandle = Ps::to16(BPHandle); + newHandles[nbNewHandles].mInternalBPHandle = Ps::to16(regionIndex); + nbNewHandles++; + } + + if(nbHandles==nbNewHandles) + { + for(PxU32 i=0;iaddObject(box, handle, isStatic!=0); + newHandles[nbNewHandles].mHandle = Ps::to16(BPHandle); + newHandles[nbNewHandles].mInternalBPHandle = Ps::to16(regionIndex); + nbNewHandles++; + } + + // PT: we know that we have one more handle than before, no need to test + purgeHandles(¤tObject, nbHandles); + storeHandles(¤tObject, nbNewHandles, newHandles); + + currentObject.mNbHandles = Ps::to16(nbNewHandles); + + // PT: we know that we have at least one handle (from the newly added region), so we can't be "out of bounds" here. + PX_ASSERT(nbNewHandles); + +#ifdef USE_FULLY_INSIDE_FLAG + // PT: we know that the object was not "fully inside" before, so even if it is fully inside the new region, it + // will not be fully inside all of them => no need to change its fully inside flag + // TODO: an exception to this would be the case where the object was out-of-bounds, and it's now fully inside the new region + // => we could set the flag in that case. +#endif + return true; +} + +bool MBP_PairManager::computeCreatedDeletedPairs(const MBP_Object* objects, BroadPhaseMBP* mbp, const BitArray& updated, const BitArray& removed) +{ + // PT: parse all currently active pairs. The goal here is to generate the found/lost pairs, compared to previous frame. + PxU32 i=0; + PxU32 nbActivePairs = mNbActivePairs; + while(imCreated.pushBack(BroadPhasePair(object0, object1)); + + p.clearNew(); + p.clearUpdated(); + i++; + } + else if(p.isUpdated()) + { + // Persistent pair + + // PT: this pair already existed in the structure, and has been found again this frame. Since + // MBP reports "all pairs" each frame (as opposed to SAP), this happens quite often, for each + // active persistent pair. + p.clearUpdated(); + i++; + } + else + { + // Lost pair + + // PT: if the pair is not new and not 'updated', it might be a lost (separated) pair. But this + // is not always the case since we now handle "sleeping" objects directly within MBP. A pair + // of sleeping objects does not generate an 'addPair' call, so it ends up in this codepath. + // Nonetheless the sleeping pair should not be deleted. We can only delete pairs involving + // objects that have been actually moved during the frame. This is the only case in which + // a pair can indeed become 'lost'. + const PxU32 id0 = p.getId0(); + const PxU32 id1 = p.getId1(); + PX_ASSERT(id0!=INVALID_ID); + PX_ASSERT(id1!=INVALID_ID); + + const MBP_ObjectIndex index0 = decodeHandle_Index(id0); + const MBP_ObjectIndex index1 = decodeHandle_Index(id1); + // PT: if none of the involved objects have been updated, the pair is just sleeping: keep it and skip it. + if(updated.isSetChecked(index0) || updated.isSetChecked(index1)) + { + // PT: by design (for better or worse) we do not report pairs to the client when + // one of the involved objects has been deleted. The pair must still be deleted + // from the MBP structure though. + if(!removed.isSetChecked(index0) && !removed.isSetChecked(index1)) + { + // PT: doing the group-based filtering here is useless. The pair should not have + // been added in the first place. + const BpHandle object0 = objects[index0].mUserID; + const BpHandle object1 = objects[index1].mUserID; + mbp->mDeleted.pushBack(BroadPhasePair(object0, object1)); + } + + const PxU32 hashValue = hash(id0, id1) & mMask; + PairManagerData::removePair(id0, id1, hashValue, i); + nbActivePairs--; + } + else i++; + } + } + + shrinkMemory(); + return true; +} + +void MBP::prepareOverlaps() +{ + const PxU32 nb = mNbRegions; + const RegionData* PX_RESTRICT regions = mRegions.begin(); + for(PxU32 i=0;iprepareOverlaps(); + } +} + +void MBP::findOverlaps(const Bp::FilterGroup::Enum* PX_RESTRICT groups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , const bool* PX_RESTRICT lut +#endif + ) +{ + PxU32 nb = mNbRegions; + const RegionData* PX_RESTRICT regions = mRegions.begin(); + const MBP_Object* objects = mMBP_Objects.begin(); + + mPairManager.mObjects = objects; + mPairManager.mGroups = groups; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mPairManager.mLUT = lut; +#endif + + for(PxU32 i=0;ifindOverlaps(mPairManager); + } +} + +PxU32 MBP::finalize(BroadPhaseMBP* mbp) +{ + const MBP_Object* objects = mMBP_Objects.begin(); + mPairManager.computeCreatedDeletedPairs(objects, mbp, mUpdatedObjects, mRemoved); + + mUpdatedObjects.clearAll(); + + return mPairManager.mNbActivePairs; +} + +void MBP::reset() +{ + PxU32 nb = mNbRegions; + RegionData* PX_RESTRICT regions = mRegions.begin(); + while(nb--) + { +// printf("%d objects in region\n", regions->mBP->mNbObjects); + DELETESINGLE(regions->mBP); + regions++; + } + + mNbRegions = 0; + mFirstFreeIndex = INVALID_ID; + mFirstFreeIndexBP = INVALID_ID; + for(PxU32 i=0;isetBounds(h.mHandle, bounds); + } + } + } +} + +void MBP::setTransientBounds(const PxBounds3* bounds, const PxReal* contactDistance) +{ + mTransientBounds = bounds; + mTransientContactDistance = contactDistance; +} +/////////////////////////////////////////////////////////////////////////////// + +// Below is the PhysX wrapper = link between AABBManager and MBP + +#define DEFAULT_CREATED_DELETED_PAIRS_CAPACITY 1024 + +BroadPhaseMBP::BroadPhaseMBP( PxU32 maxNbRegions, + PxU32 maxNbBroadPhaseOverlaps, + PxU32 maxNbStaticShapes, + PxU32 maxNbDynamicShapes, + PxU64 contextID) : + mMBPUpdateWorkTask (contextID), + mMBPPostUpdateWorkTask (contextID), + mMapping (NULL), + mCapacity (0), + mGroups (NULL) +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + ,mLUT (NULL) +#endif +{ + mMBP = PX_NEW(MBP)(); + + const PxU32 nbObjects = maxNbStaticShapes + maxNbDynamicShapes; + mMBP->preallocate(maxNbRegions, nbObjects, maxNbBroadPhaseOverlaps); + + if(nbObjects) + allocateMappingArray(nbObjects); + + mCreated.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); + mDeleted.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); +} + +BroadPhaseMBP::~BroadPhaseMBP() +{ + DELETESINGLE(mMBP); + PX_FREE(mMapping); +} + +void BroadPhaseMBP::allocateMappingArray(PxU32 newCapacity) +{ + PX_ASSERT(newCapacity>mCapacity); + MBP_Handle* newMapping = reinterpret_cast(PX_ALLOC(sizeof(MBP_Handle)*newCapacity, "MBP")); + if(mCapacity) + PxMemCopy(newMapping, mMapping, mCapacity*sizeof(MBP_Handle)); + for(PxU32 i=mCapacity;imNbRegions; +/* const RegionData* PX_RESTRICT regions = (const RegionData*)mMBP->mRegions.GetEntries(); + PxU32 nbActiveRegions = 0; + for(PxU32 i=0;imNbRegions; + const RegionData* PX_RESTRICT regions = mMBP->mRegions.begin(); + regions += startIndex; + + const PxU32 writeCount = PxMin(size, bufferSize); + for(PxU32 i=0;imNbStaticBoxes; + userBuffer[i].nbDynamicObjects = regions[i].mBP->mNbDynamicBoxes; + } + else + { + userBuffer[i].region.bounds.setEmpty(); + userBuffer[i].region.userData = NULL; + userBuffer[i].active = false; + userBuffer[i].overlap = false; + userBuffer[i].nbStaticObjects = 0; + userBuffer[i].nbDynamicObjects = 0; + } + } + return writeCount; +} + +PxU32 BroadPhaseMBP::addRegion(const PxBroadPhaseRegion& region, bool populateRegion) +{ + return mMBP->addRegion(region, populateRegion); +} + +bool BroadPhaseMBP::removeRegion(PxU32 handle) +{ + return mMBP->removeRegion(handle); +} + +void BroadPhaseMBP::update(const PxU32 numCpuTasks, PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData, physx::PxBaseTask* continuation, physx::PxBaseTask* narrowPhaseUnblockTask) +{ +#if PX_CHECKED + PX_CHECK_AND_RETURN(scratchAllocator, "BroadPhaseMBP::update - scratchAllocator must be non-NULL \n"); +#endif + + // PT: TODO: move this out of update function + if(narrowPhaseUnblockTask) + narrowPhaseUnblockTask->removeReference(); + + setUpdateData(updateData); + + if(1) + { + update(); + postUpdate(); + } + else + { + mMBPPostUpdateWorkTask.set(this, scratchAllocator, numCpuTasks); + mMBPUpdateWorkTask.set(this, scratchAllocator, numCpuTasks); + + mMBPPostUpdateWorkTask.setContinuation(continuation); + mMBPUpdateWorkTask.setContinuation(&mMBPPostUpdateWorkTask); + + mMBPPostUpdateWorkTask.removeReference(); + mMBPUpdateWorkTask.removeReference(); + } +} + +void BroadPhaseMBP::singleThreadedUpdate(PxcScratchAllocator* /*scratchAllocator*/, const BroadPhaseUpdateData& updateData) +{ + // PT: TODO: the scratchAllocator isn't actually needed, is it? + setUpdateData(updateData); + update(); + postUpdate(); +} + +static PX_FORCE_INLINE void computeMBPBounds(MBP_AABB& aabb, const PxBounds3* PX_RESTRICT boundsXYZ, const PxReal* PX_RESTRICT contactDistances, const BpHandle index) +{ + const PxBounds3& b = boundsXYZ[index]; + const Vec4V contactDistanceV = V4Load(contactDistances[index]); + const Vec4V inflatedMinV = V4Sub(V4LoadU(&b.minimum.x), contactDistanceV); + const Vec4V inflatedMaxV = V4Add(V4LoadU(&b.maximum.x), contactDistanceV); // PT: this one is safe because we allocated one more box in the array (in BoundsArray::initEntry) + + PX_ALIGN(16, PxVec4) boxMin; + PX_ALIGN(16, PxVec4) boxMax; + V4StoreA(inflatedMinV, &boxMin.x); + V4StoreA(inflatedMaxV, &boxMax.x); + + const PxU32* PX_RESTRICT min = PxUnionCast(&boxMin.x); + const PxU32* PX_RESTRICT max = PxUnionCast(&boxMax.x); + //Avoid min=max by enforcing the rule that mins are even and maxs are odd. + aabb.mMinX = IntegerAABB::encodeFloatMin(min[0])>>1; + aabb.mMinY = IntegerAABB::encodeFloatMin(min[1])>>1; + aabb.mMinZ = IntegerAABB::encodeFloatMin(min[2])>>1; + aabb.mMaxX = (IntegerAABB::encodeFloatMax(max[0]) | (1<<2))>>1; + aabb.mMaxY = (IntegerAABB::encodeFloatMax(max[1]) | (1<<2))>>1; + aabb.mMaxZ = (IntegerAABB::encodeFloatMax(max[2]) | (1<<2))>>1; + +/* const IntegerAABB bounds(boundsXYZ[index], contactDistances[index]); + + aabb.mMinX = bounds.mMinMax[IntegerAABB::MIN_X]>>1; + aabb.mMinY = bounds.mMinMax[IntegerAABB::MIN_Y]>>1; + aabb.mMinZ = bounds.mMinMax[IntegerAABB::MIN_Z]>>1; + aabb.mMaxX = bounds.mMinMax[IntegerAABB::MAX_X]>>1; + aabb.mMaxY = bounds.mMinMax[IntegerAABB::MAX_Y]>>1; + aabb.mMaxZ = bounds.mMinMax[IntegerAABB::MAX_Z]>>1;*/ + +/* + aabb.mMinX &= ~1; + aabb.mMinY &= ~1; + aabb.mMinZ &= ~1; + aabb.mMaxX |= 1; + aabb.mMaxY |= 1; + aabb.mMaxZ |= 1; +*/ + +/*#if PX_DEBUG + PxBounds3 decodedBox; + PxU32* bin = reinterpret_cast(&decodedBox.minimum.x); + bin[0] = decodeFloat(bounds.mMinMax[IntegerAABB::MIN_X]); + bin[1] = decodeFloat(bounds.mMinMax[IntegerAABB::MIN_Y]); + bin[2] = decodeFloat(bounds.mMinMax[IntegerAABB::MIN_Z]); + bin[3] = decodeFloat(bounds.mMinMax[IntegerAABB::MAX_X]); + bin[4] = decodeFloat(bounds.mMinMax[IntegerAABB::MAX_Y]); + bin[5] = decodeFloat(bounds.mMinMax[IntegerAABB::MAX_Z]); + + MBP_AABB PrunerBox; + PrunerBox.initFrom2(decodedBox); + PX_ASSERT(PrunerBox.mMinX==aabb.mMinX); + PX_ASSERT(PrunerBox.mMinY==aabb.mMinY); + PX_ASSERT(PrunerBox.mMinZ==aabb.mMinZ); + PX_ASSERT(PrunerBox.mMaxX==aabb.mMaxX); + PX_ASSERT(PrunerBox.mMaxY==aabb.mMaxY); + PX_ASSERT(PrunerBox.mMaxZ==aabb.mMaxZ); +#endif*/ +} + +void MBPUpdateWorkTask::runInternal() +{ + mMBP->update(); +} + +void MBPPostUpdateWorkTask::runInternal() +{ + mMBP->postUpdate(); +} + +void BroadPhaseMBP::removeObjects(const BroadPhaseUpdateData& updateData) +{ + const BpHandle* PX_RESTRICT removed = updateData.getRemovedHandles(); + if(removed) + { + PxU32 nbToGo = updateData.getNumRemovedHandles(); + while(nbToGo--) + { + const BpHandle index = *removed++; + PX_ASSERT(index+1removeObject(mMapping[index]); + PX_ASSERT(status); + PX_UNUSED(status); + + mMapping[index] = PX_INVALID_U32; + } + } +} + +void BroadPhaseMBP::updateObjects(const BroadPhaseUpdateData& updateData) +{ + const BpHandle* PX_RESTRICT updated = updateData.getUpdatedHandles(); + if(updated) + { + const PxBounds3* PX_RESTRICT boundsXYZ = updateData.getAABBs(); + PxU32 nbToGo = updateData.getNumUpdatedHandles(); + while(nbToGo--) + { + const BpHandle index = *updated++; + PX_ASSERT(index+1updateObject(mMapping[index], aabb); + PX_ASSERT(status); + PX_UNUSED(status); + } + } +} + +void BroadPhaseMBP::addObjects(const BroadPhaseUpdateData& updateData) +{ + const BpHandle* PX_RESTRICT created = updateData.getCreatedHandles(); + if(created) + { + const PxBounds3* PX_RESTRICT boundsXYZ = updateData.getAABBs(); + const Bp::FilterGroup::Enum* PX_RESTRICT groups = updateData.getGroups(); + + PxU32 nbToGo = updateData.getNumCreatedHandles(); + while(nbToGo--) + { + const BpHandle index = *created++; + PX_ASSERT(index+1addObject(aabb, index, isStatic); + } + } +} + +void BroadPhaseMBP::setUpdateData(const BroadPhaseUpdateData& updateData) +{ + mMBP->setTransientBounds(updateData.getAABBs(), updateData.getContactDistance()); + + const PxU32 newCapacity = updateData.getCapacity(); + if(newCapacity>mCapacity) + allocateMappingArray(newCapacity); + +#if PX_CHECKED + // PT: WARNING: this must be done after the allocateMappingArray call + if(!BroadPhaseUpdateData::isValid(updateData, *this)) + { + PX_CHECK_MSG(false, "Illegal BroadPhaseUpdateData \n"); + return; + } +#endif + + mGroups = updateData.getGroups(); +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT = updateData.getLUT(); +#endif + + // ### TODO: handle groups inside MBP + // ### TODO: get rid of AABB conversions + + removeObjects(updateData); + addObjects(updateData); + updateObjects(updateData); + + PX_ASSERT(!mCreated.size()); + PX_ASSERT(!mDeleted.size()); + + mMBP->prepareOverlaps(); +} + +void BroadPhaseMBP::update() +{ +#ifdef CHECK_NB_OVERLAPS + gNbOverlaps = 0; +#endif + mMBP->findOverlaps(mGroups +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + , mLUT +#endif + ); +#ifdef CHECK_NB_OVERLAPS + printf("PPU: %d overlaps\n", gNbOverlaps); +#endif +} + +void BroadPhaseMBP::postUpdate() +{ + { + PxU32 Nb = mMBP->mNbRegions; + const RegionData* PX_RESTRICT regions = mMBP->mRegions.begin(); + for(PxU32 i=0;imNbUpdatedBoxes = 0; + } + } + + mMBP->finalize(this); +} + +PxU32 BroadPhaseMBP::getNbCreatedPairs() const +{ + return mCreated.size(); +} + +BroadPhasePair* BroadPhaseMBP::getCreatedPairs() +{ + return mCreated.begin(); +} + +PxU32 BroadPhaseMBP::getNbDeletedPairs() const +{ + return mDeleted.size(); +} + +BroadPhasePair* BroadPhaseMBP::getDeletedPairs() +{ + return mDeleted.begin(); +} + +PxU32 BroadPhaseMBP::getNbOutOfBoundsObjects() const +{ + return mMBP->mOutOfBoundsObjects.size(); +} + +const PxU32* BroadPhaseMBP::getOutOfBoundsObjects() const +{ + return mMBP->mOutOfBoundsObjects.begin(); +} + +static void freeBuffer(Ps::Array& buffer) +{ + const PxU32 size = buffer.size(); + if(size>DEFAULT_CREATED_DELETED_PAIRS_CAPACITY) + { + buffer.reset(); + buffer.reserve(DEFAULT_CREATED_DELETED_PAIRS_CAPACITY); + } + else + { + buffer.clear(); + } +} + +void BroadPhaseMBP::freeBuffers() +{ + mMBP->freeBuffers(); + freeBuffer(mCreated); + freeBuffer(mDeleted); +} + +#if PX_CHECKED +bool BroadPhaseMBP::isValid(const BroadPhaseUpdateData& updateData) const +{ + const BpHandle* created = updateData.getCreatedHandles(); + if(created) + { + Ps::HashSet set; + PxU32 nbObjects = mMBP->mMBP_Objects.size(); + const MBP_Object* PX_RESTRICT objects = mMBP->mMBP_Objects.begin(); + while(nbObjects--) + { + if(!(objects->mFlags & MBP_REMOVED)) + set.insert(objects->mUserID); + objects++; + } + + PxU32 nbToGo = updateData.getNumCreatedHandles(); + while(nbToGo--) + { + const BpHandle index = *created++; + PX_ASSERT(indexshiftOrigin(shift); +} + +PxU32 BroadPhaseMBP::getCurrentNbPairs() const +{ + return mMBP->mPairManager.mNbActivePairs; +} diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h new file mode 100644 index 000000000..3ef82b480 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h @@ -0,0 +1,114 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_MBP_H +#define BP_BROADPHASE_MBP_H + +#include "CmPhysXCommon.h" +#include "BpBroadPhase.h" +#include "BpBroadPhaseMBPCommon.h" +#include "BpMBPTasks.h" + + class MBP; + +namespace physx +{ +namespace Bp +{ + class BroadPhaseMBP : public BroadPhase, public Ps::UserAllocated + { + PX_NOCOPY(BroadPhaseMBP) + public: + BroadPhaseMBP( PxU32 maxNbRegions, + PxU32 maxNbBroadPhaseOverlaps, + PxU32 maxNbStaticShapes, + PxU32 maxNbDynamicShapes, + PxU64 contextID); + virtual ~BroadPhaseMBP(); + + // BroadPhaseBase + virtual bool getCaps(PxBroadPhaseCaps& caps) const; + virtual PxU32 getNbRegions() const; + virtual PxU32 getRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + virtual PxU32 addRegion(const PxBroadPhaseRegion& region, bool populateRegion); + virtual bool removeRegion(PxU32 handle); + virtual PxU32 getNbOutOfBoundsObjects() const; + virtual const PxU32* getOutOfBoundsObjects() const; + //~BroadPhaseBase + + // BroadPhase + virtual PxBroadPhaseType::Enum getType() const { return PxBroadPhaseType::eMBP; } + virtual void destroy() { delete this; } + virtual void update(const PxU32 numCpuTasks, PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData, physx::PxBaseTask* continuation, physx::PxBaseTask* narrowPhaseUnblockTask); + virtual void fetchBroadPhaseResults(physx::PxBaseTask*) {} + virtual PxU32 getNbCreatedPairs() const; + virtual BroadPhasePair* getCreatedPairs(); + virtual PxU32 getNbDeletedPairs() const; + virtual BroadPhasePair* getDeletedPairs(); + virtual void freeBuffers(); + virtual void shiftOrigin(const PxVec3& shift); +#if PX_CHECKED + virtual bool isValid(const BroadPhaseUpdateData& updateData) const; +#endif + virtual BroadPhasePair* getBroadPhasePairs() const {return NULL;} //KS - TODO - implement this!!! + virtual void deletePairs(){} //KS - TODO - implement this!!! + virtual void singleThreadedUpdate(PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData); + //~BroadPhase + + MBPUpdateWorkTask mMBPUpdateWorkTask; + MBPPostUpdateWorkTask mMBPPostUpdateWorkTask; + + MBP* mMBP; // PT: TODO: aggregate + + MBP_Handle* mMapping; + PxU32 mCapacity; + Ps::Array mCreated; + Ps::Array mDeleted; + + const Bp::FilterGroup::Enum*mGroups; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* mLUT; +#endif + void setUpdateData(const BroadPhaseUpdateData& updateData); + void addObjects(const BroadPhaseUpdateData& updateData); + void removeObjects(const BroadPhaseUpdateData& updateData); + void updateObjects(const BroadPhaseUpdateData& updateData); + + void update(); + void postUpdate(); + void allocateMappingArray(PxU32 newCapacity); + + PxU32 getCurrentNbPairs() const; + }; + +} //namespace Bp + +} //namespace physx + +#endif // BP_BROADPHASE_MBP_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h new file mode 100644 index 000000000..1cc3f9dc8 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h @@ -0,0 +1,199 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_MBP_COMMON_H +#define BP_BROADPHASE_MBP_COMMON_H + +#include "PxPhysXConfig.h" +#include "BpBroadPhaseUpdate.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Bp +{ + +#define MBP_USE_WORDS +#define MBP_USE_NO_CMP_OVERLAP +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + #define MBP_SIMD_OVERLAP +#endif + +#ifdef MBP_USE_WORDS + typedef PxU16 MBP_Index; +#else + typedef PxU32 MBP_Index; +#endif + typedef PxU32 MBP_ObjectIndex; // PT: index in mMBP_Objects + typedef PxU32 MBP_Handle; // PT: returned to MBP users, combination of index/flip-flop/static-bit + + struct IAABB : public Ps::UserAllocated + { + PX_FORCE_INLINE bool isInside(const IAABB& box) const + { + if(box.mMinX>mMinX) return false; + if(box.mMinY>mMinY) return false; + if(box.mMinZ>mMinZ) return false; + if(box.mMaxX(&box.minimum.x); + mMinX = encodeFloat(binary[0])>>1; + mMinY = encodeFloat(binary[1])>>1; + mMinZ = encodeFloat(binary[2])>>1; + mMaxX = encodeFloat(binary[3])>>1; + mMaxY = encodeFloat(binary[4])>>1; + mMaxZ = encodeFloat(binary[5])>>1; + } + + PX_FORCE_INLINE void decode(PxBounds3& box) const + { + PxU32* PX_RESTRICT binary = reinterpret_cast(&box.minimum.x); + binary[0] = decodeFloat(mMinX<<1); + binary[1] = decodeFloat(mMinY<<1); + binary[2] = decodeFloat(mMinZ<<1); + binary[3] = decodeFloat(mMaxX<<1); + binary[4] = decodeFloat(mMaxY<<1); + binary[5] = decodeFloat(mMaxZ<<1); + } + + PX_FORCE_INLINE PxU32 getMin(PxU32 i) const { return (&mMinX)[i]; } + PX_FORCE_INLINE PxU32 getMax(PxU32 i) const { return (&mMaxX)[i]; } + + PxU32 mMinX; + PxU32 mMinY; + PxU32 mMinZ; + PxU32 mMaxX; + PxU32 mMaxY; + PxU32 mMaxZ; + }; + + struct SIMD_AABB : public Ps::UserAllocated + { + PX_FORCE_INLINE void initFrom(const PxBounds3& box) + { + const PxU32* PX_RESTRICT binary = reinterpret_cast(&box.minimum.x); + mMinX = encodeFloat(binary[0]); + mMinY = encodeFloat(binary[1]); + mMinZ = encodeFloat(binary[2]); + mMaxX = encodeFloat(binary[3]); + mMaxY = encodeFloat(binary[4]); + mMaxZ = encodeFloat(binary[5]); + } + + PX_FORCE_INLINE void initFrom2(const PxBounds3& box) + { + const PxU32* PX_RESTRICT binary = reinterpret_cast(&box.minimum.x); + mMinX = encodeFloat(binary[0])>>1; + mMinY = encodeFloat(binary[1])>>1; + mMinZ = encodeFloat(binary[2])>>1; + mMaxX = encodeFloat(binary[3])>>1; + mMaxY = encodeFloat(binary[4])>>1; + mMaxZ = encodeFloat(binary[5])>>1; + } + + PX_FORCE_INLINE void decode(PxBounds3& box) const + { + PxU32* PX_RESTRICT binary = reinterpret_cast(&box.minimum.x); + binary[0] = decodeFloat(mMinX<<1); + binary[1] = decodeFloat(mMinY<<1); + binary[2] = decodeFloat(mMinZ<<1); + binary[3] = decodeFloat(mMaxX<<1); + binary[4] = decodeFloat(mMaxY<<1); + binary[5] = decodeFloat(mMaxZ<<1); + } + + PX_FORCE_INLINE bool isInside(const SIMD_AABB& box) const + { + if(box.mMinX>mMinX) return false; + if(box.mMinY>mMinY) return false; + if(box.mMinZ>mMinZ) return false; + if(box.mMaxX + +namespace physx +{ +namespace Bp +{ +#define DEFAULT_DATA_ARRAY_CAPACITY 1024 +#define DEFAULT_CREATEDDELETED_PAIR_ARRAY_CAPACITY 64 +#define DEFAULT_CREATEDDELETED1AXIS_CAPACITY 8192 + +BroadPhaseSap::BroadPhaseSap( + const PxU32 maxNbBroadPhaseOverlaps, + const PxU32 maxNbStaticShapes, + const PxU32 maxNbDynamicShapes, + PxU64 contextID) : + mScratchAllocator (NULL), + mSapUpdateWorkTask (contextID), + mSapPostUpdateWorkTask (contextID), + mContextID (contextID) +{ + + for(PxU32 i=0;i<3;i++) + mBatchUpdateTasks[i].setContextId(contextID); + + //Boxes + mBoxesSize=0; + mBoxesSizePrev=0; + mBoxesCapacity = (((maxNbStaticShapes + maxNbDynamicShapes) + 31) & ~31); + mBoxEndPts[0] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*mBoxesCapacity)), "SapBox1D")); + mBoxEndPts[1] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*mBoxesCapacity)), "SapBox1D")); + mBoxEndPts[2] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*mBoxesCapacity)), "SapBox1D")); + for(PxU32 i=0; i(PX_ALLOC(ALIGN_SIZE_16((sizeof(PxU8)*mBoxesCapacity)), "BoxesUpdated")); + mSortedUpdateElements = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*mEndPointsCapacity)), "SortedUpdateElements")); + mActivityPockets = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BroadPhaseActivityPocket)*mEndPointsCapacity)), "BroadPhaseActivityPocket")); + + mEndPointValues[0] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(mEndPointsCapacity))), "ValType")); + mEndPointValues[1] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(mEndPointsCapacity))), "ValType")); + mEndPointValues[2] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(mEndPointsCapacity))), "ValType")); + mEndPointDatas[0] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(mEndPointsCapacity))), "BpHandle")); + mEndPointDatas[1] = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(mEndPointsCapacity))), "BpHandle")); + mEndPointDatas[2]= reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(mEndPointsCapacity))), "BpHandle")); + + // Initialize sentinels + setMinSentinel(mEndPointValues[0][0],mEndPointDatas[0][0]); + setMaxSentinel(mEndPointValues[0][1],mEndPointDatas[0][1]); + setMinSentinel(mEndPointValues[1][0],mEndPointDatas[1][0]); + setMaxSentinel(mEndPointValues[1][1],mEndPointDatas[1][1]); + setMinSentinel(mEndPointValues[2][0],mEndPointDatas[2][0]); + setMaxSentinel(mEndPointValues[2][1],mEndPointDatas[2][1]); + + mListNext = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*mEndPointsCapacity)), "NextList")); + mListPrev = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*mEndPointsCapacity)), "PrevList")); + + for(PxU32 a = 1; a < mEndPointsCapacity; ++a) + { + mListNext[a-1] = BpHandle(a); + mListPrev[a] = BpHandle(a-1); + } + mListNext[mEndPointsCapacity-1] = BpHandle(mEndPointsCapacity-1); + mListPrev[0] = 0; + + mDefaultPairsCapacity = PxMax(maxNbBroadPhaseOverlaps, PxU32(DEFAULT_CREATEDDELETED_PAIR_ARRAY_CAPACITY)); + + mPairs.init(mDefaultPairsCapacity); + + mBatchUpdateTasks[2].set(this,2); + mBatchUpdateTasks[1].set(this,1); + mBatchUpdateTasks[0].set(this,0); + mBatchUpdateTasks[2].setPairs(NULL, 0); + mBatchUpdateTasks[1].setPairs(NULL, 0); + mBatchUpdateTasks[0].setPairs(NULL, 0); + + //Initialise data array. + mData = NULL; + mDataSize = 0; + mDataCapacity = 0; + + //Initialise pairs arrays. + mCreatedPairsArray = NULL; + mCreatedPairsCapacity = 0; + mCreatedPairsSize = 0; + mDeletedPairsArray = NULL; + mDeletedPairsCapacity = 0; + mDeletedPairsSize = 0; + mActualDeletedPairSize = 0; + +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT = NULL; +#endif +} + +BroadPhaseSap::~BroadPhaseSap() +{ + PX_FREE(mBoxEndPts[0]); + PX_FREE(mBoxEndPts[1]); + PX_FREE(mBoxEndPts[2]); + + PX_FREE(mEndPointValues[0]); + PX_FREE(mEndPointValues[1]); + PX_FREE(mEndPointValues[2]); + PX_FREE(mEndPointDatas[0]); + PX_FREE(mEndPointDatas[1]); + PX_FREE(mEndPointDatas[2]); + + PX_FREE(mListNext); + PX_FREE(mListPrev); + + PX_FREE(mSortedUpdateElements); + PX_FREE(mActivityPockets); + PX_FREE(mBoxesUpdated); + + mPairs.release(); + + mBatchUpdateTasks[0].setPairs(NULL, 0); + mBatchUpdateTasks[1].setPairs(NULL, 0); + mBatchUpdateTasks[2].setPairs(NULL, 0); + + mData = NULL; + + mCreatedPairsArray = NULL; + mDeletedPairsArray = NULL; + +} + +void BroadPhaseSap::destroy() +{ + this->~BroadPhaseSap(); + PX_FREE(this); +} + +void BroadPhaseSap::resizeBuffers() +{ + const PxU32 defaultPairsCapacity = mDefaultPairsCapacity; + + mCreatedPairsArray = reinterpret_cast(mScratchAllocator->alloc(sizeof(BroadPhasePair)*defaultPairsCapacity, true)); + mCreatedPairsCapacity = defaultPairsCapacity; + mCreatedPairsSize = 0; + + mDeletedPairsArray = reinterpret_cast(mScratchAllocator->alloc(sizeof(BroadPhasePair)*defaultPairsCapacity, true)); + mDeletedPairsCapacity = defaultPairsCapacity; + mDeletedPairsSize = 0; + + mData = reinterpret_cast(mScratchAllocator->alloc(sizeof(BpHandle)*defaultPairsCapacity, true)); + mDataCapacity = defaultPairsCapacity; + mDataSize = 0; + + mBatchUpdateTasks[0].setPairs(reinterpret_cast(mScratchAllocator->alloc(sizeof(BroadPhasePair)*defaultPairsCapacity, true)), defaultPairsCapacity); + mBatchUpdateTasks[0].setNumPairs(0); + mBatchUpdateTasks[1].setPairs(reinterpret_cast(mScratchAllocator->alloc(sizeof(BroadPhasePair)*defaultPairsCapacity, true)), defaultPairsCapacity); + mBatchUpdateTasks[1].setNumPairs(0); + mBatchUpdateTasks[2].setPairs(reinterpret_cast(mScratchAllocator->alloc(sizeof(BroadPhasePair)*defaultPairsCapacity, true)), defaultPairsCapacity); + mBatchUpdateTasks[2].setNumPairs(0); +} + +void BroadPhaseSap::freeBuffers() +{ + if(mCreatedPairsArray) mScratchAllocator->free(mCreatedPairsArray); + mCreatedPairsArray = NULL; + mCreatedPairsSize = 0; + mCreatedPairsCapacity = 0; + + if(mDeletedPairsArray) mScratchAllocator->free(mDeletedPairsArray); + mDeletedPairsArray = NULL; + mDeletedPairsSize = 0; + mDeletedPairsCapacity = 0; + mActualDeletedPairSize = 0; + + if(mData) mScratchAllocator->free(mData); + mData = NULL; + mDataSize = 0; + mDataCapacity = 0; + + if(mBatchUpdateTasks[0].getPairs()) mScratchAllocator->free(mBatchUpdateTasks[0].getPairs()); + mBatchUpdateTasks[0].setPairs(NULL, 0); + mBatchUpdateTasks[0].setNumPairs(0); + if(mBatchUpdateTasks[1].getPairs()) mScratchAllocator->free(mBatchUpdateTasks[1].getPairs()); + mBatchUpdateTasks[1].setPairs(NULL, 0); + mBatchUpdateTasks[1].setNumPairs(0); + if(mBatchUpdateTasks[2].getPairs()) mScratchAllocator->free(mBatchUpdateTasks[2].getPairs()); + mBatchUpdateTasks[2].setPairs(NULL, 0); + mBatchUpdateTasks[2].setNumPairs(0); + + //Shrink pair manager buffers it they are larger than needed but only let them shrink to a minimum size. + mPairs.shrinkMemory(); +} + +PX_FORCE_INLINE static void shiftCoord3(const ValType val0, const BpHandle handle0, + const ValType val1, const BpHandle handle1, + const ValType val2, const BpHandle handle2, + const PxF32* shift, ValType& oVal0, ValType& oVal1, ValType& oVal2) +{ + PX_ASSERT(!isSentinel(handle0)); + PX_ASSERT(!isSentinel(handle1)); + PX_ASSERT(!isSentinel(handle2)); + + PxF32 fl0, fl1, fl2; + ValType* PX_RESTRICT bpVal0 = PxUnionCast(&fl0); + ValType* PX_RESTRICT bpVal1 = PxUnionCast(&fl1); + ValType* PX_RESTRICT bpVal2 = PxUnionCast(&fl2); + *bpVal0 = decodeFloat(val0); + *bpVal1 = decodeFloat(val1); + *bpVal2 = decodeFloat(val2); + fl0 -= shift[0]; + fl1 -= shift[1]; + fl2 -= shift[2]; + oVal0 = (isMax(handle0)) ? (IntegerAABB::encodeFloatMax(*bpVal0) | 1) : ((IntegerAABB::encodeFloatMin(*bpVal0) + 1) & ~1); + oVal1 = (isMax(handle1)) ? (IntegerAABB::encodeFloatMax(*bpVal1) | 1) : ((IntegerAABB::encodeFloatMin(*bpVal1) + 1) & ~1); + oVal2 = (isMax(handle2)) ? (IntegerAABB::encodeFloatMax(*bpVal2) | 1) : ((IntegerAABB::encodeFloatMin(*bpVal2) + 1) & ~1); +} + +PX_FORCE_INLINE static void testPostShiftOrder(const ValType prevVal, ValType& currVal, const BpHandle prevIsMax, const BpHandle currIsMax) +{ + if(currVal < prevVal) + { + //The order has been broken by the lossy shift. + //Correct currVal so that it is greater than prevVal. + //If currVal is a box max then ensure that the box is of finite extent. + const ValType shiftCorrection = (prevIsMax==currIsMax) ? ValType(0) : ValType(1); + currVal = prevVal + shiftCorrection; + } +} + +void BroadPhaseSap::shiftOrigin(const PxVec3& shift) +{ + // + // Note: shifting the bounds does not necessarily preserve the order of the broadphase interval endpoints. The encoding of the float bounds is a lossy + // operation, thus it is not possible to get the original float values back and shift them. The only goal of this method is to shift the endpoints + // such that the order is preserved. The new intervals might no reflect the correct bounds! Since all bounds have been marked dirty, they will get + // recomputed in the next frame anyway. This method makes sure that the next frame update can start from a valid configuration that is close to + // the correct one and does not require too many swaps. + // + + if(0==mBoxesSize) + { + return; + } + + // + // Note: processing all the axis at once improved performance on XBox 360 and PS3 because it allows to compensate for stalls + // + + const PxF32 shiftAxis[3] = { shift.x, shift.y, shift.z }; + const BpHandle* PX_RESTRICT epData0 = mEndPointDatas[0]; + ValType* PX_RESTRICT epValues0 = mEndPointValues[0]; + const BpHandle* PX_RESTRICT epData1 = mEndPointDatas[1]; + ValType* PX_RESTRICT epValues1 = mEndPointValues[1]; + const BpHandle* PX_RESTRICT epData2 = mEndPointDatas[2]; + ValType* PX_RESTRICT epValues2 = mEndPointValues[2]; + + //Shift the first value in the array of sorted values. + { + //Shifted min (first element must be a min by definition). + shiftCoord3(epValues0[1], epData0[1], epValues1[1], epData1[1], epValues2[1], epData2[1], shiftAxis, epValues0[1], epValues1[1], epValues2[1]); + PX_ASSERT(!isMax(epData0[1])); + PX_ASSERT(!isMax(epData1[1])); + PX_ASSERT(!isMax(epData2[1])); + } + + //Shift the remainder. + ValType prevVal0 = epValues0[1]; + BpHandle prevIsMax0 = isMax(epData0[1]); + ValType prevVal1 = epValues1[1]; + BpHandle prevIsMax1 = isMax(epData1[1]); + ValType prevVal2 = epValues2[1]; + BpHandle prevIsMax2 = isMax(epData2[1]); + for(PxU32 i=2; i <= mBoxesSize*2; i++) + { + const BpHandle handle0 = epData0[i]; + const BpHandle handle1 = epData1[i]; + const BpHandle handle2 = epData2[i]; + PX_ASSERT(!isSentinel(handle0)); + PX_ASSERT(!isSentinel(handle1)); + PX_ASSERT(!isSentinel(handle2)); + + //Get the relevant prev and curr values after the shift. + const BpHandle currIsMax0 = isMax(epData0[i]); + const BpHandle currIsMax1 = isMax(epData1[i]); + const BpHandle currIsMax2 = isMax(epData2[i]); + ValType currVal0, currVal1, currVal2; + shiftCoord3(epValues0[i], handle0, epValues1[i], handle1, epValues2[i], handle2, shiftAxis, currVal0, currVal1, currVal2); + + //Test if the order has been preserved by the lossy shift. + testPostShiftOrder(prevVal0, currVal0, prevIsMax0, currIsMax0); + testPostShiftOrder(prevVal1, currVal1, prevIsMax1, currIsMax1); + testPostShiftOrder(prevVal2, currVal2, prevIsMax2, currIsMax2); + + prevIsMax0 = currIsMax0; + prevVal0 = currVal0; + prevIsMax1 = currIsMax1; + prevVal1 = currVal1; + prevIsMax2 = currIsMax2; + prevVal2 = currVal2; + + epValues0[i] = currVal0; + epValues1[i] = currVal1; + epValues2[i] = currVal2; + } + + PX_ASSERT(isSelfOrdered()); +} + +#if PX_CHECKED +bool BroadPhaseSap::isValid(const BroadPhaseUpdateData& updateData) const +{ + //Test that the created bounds haven't been added already (without first being removed). + const BpHandle* created=updateData.getCreatedHandles(); + const PxU32 numCreated=updateData.getNumCreatedHandles(); + for(PxU32 i=0;i=mBoxesCapacity then we need to resize to add this id, meaning that the id must be new. + if(id= mBoxesCapacity) + { + return false; + } + } + + //Test that the updated bounds have been been added without being removed. + for(PxU32 i=0;i= mBoxesCapacity) + { + return false; + } + } + + //Test that the removed bounds have already been added and haven't been removed. + for(PxU32 i=0;iremoveReference(); + + if(setUpdateData(updateData)) + { + mScratchAllocator = scratchAllocator; + + resizeBuffers(); + + mSapPostUpdateWorkTask.setBroadPhase(this); + mSapUpdateWorkTask.setBroadPhase(this); + + mSapPostUpdateWorkTask.set(numCpuTasks); + mSapUpdateWorkTask.set(numCpuTasks); + + mSapPostUpdateWorkTask.setContinuation(continuation); + mSapUpdateWorkTask.setContinuation(&mSapPostUpdateWorkTask); + + mSapPostUpdateWorkTask.removeReference(); + mSapUpdateWorkTask.removeReference(); + } +} + +void BroadPhaseSap::singleThreadedUpdate(PxcScratchAllocator* scratchAllocator, const BroadPhaseUpdateData& updateData) +{ +#if PX_CHECKED + PX_CHECK_AND_RETURN(scratchAllocator, "BroadPhaseSap::singleThreadedUpdate - scratchAllocator must be non-NULL \n"); +#endif + + if(setUpdateData(updateData)) + { + mScratchAllocator = scratchAllocator; + resizeBuffers(); + update(); + postUpdate(); + } +} + +bool BroadPhaseSap::setUpdateData(const BroadPhaseUpdateData& updateData) +{ + PX_ASSERT(0==mCreatedPairsSize); + PX_ASSERT(0==mDeletedPairsSize); + +#if PX_CHECKED + if(!BroadPhaseUpdateData::isValid(updateData, *this)) + { + PX_CHECK_MSG(false, "Illegal BroadPhaseUpdateData \n"); + mCreated = NULL; + mCreatedSize = 0; + mUpdated = NULL; + mUpdatedSize = 0; + mRemoved = NULL; + mRemovedSize = 0; + mBoxBoundsMinMax = updateData.getAABBs(); + mBoxGroups = updateData.getGroups(); + return false; + } +#endif + + //Copy across the data ptrs and sizes. + mCreated = updateData.getCreatedHandles(); + mCreatedSize = updateData.getNumCreatedHandles(); + mUpdated = updateData.getUpdatedHandles(); + mUpdatedSize = updateData.getNumUpdatedHandles(); + mRemoved = updateData.getRemovedHandles(); + mRemovedSize = updateData.getNumRemovedHandles(); + mBoxBoundsMinMax = updateData.getAABBs(); + mBoxGroups = updateData.getGroups(); +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT = updateData.getLUT(); +#endif + mContactDistance = updateData.getContactDistance(); + + //Do we need more memory to store the positions of each box min/max in the arrays of sorted boxes min/max? + if(updateData.getCapacity() > mBoxesCapacity) + { + const PxU32 oldBoxesCapacity=mBoxesCapacity; + const PxU32 newBoxesCapacity=updateData.getCapacity(); + SapBox1D* newBoxEndPts0 = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*newBoxesCapacity)), "SapBox1D")); + SapBox1D* newBoxEndPts1 = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*newBoxesCapacity)), "SapBox1D")); + SapBox1D* newBoxEndPts2 = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(SapBox1D)*newBoxesCapacity)), "SapBox1D")); + + PxMemCopy(newBoxEndPts0, mBoxEndPts[0], sizeof(SapBox1D)*oldBoxesCapacity); + PxMemCopy(newBoxEndPts1, mBoxEndPts[1], sizeof(SapBox1D)*oldBoxesCapacity); + PxMemCopy(newBoxEndPts2, mBoxEndPts[2], sizeof(SapBox1D)*oldBoxesCapacity); + for(PxU32 i=oldBoxesCapacity;i(PX_ALLOC(ALIGN_SIZE_16((sizeof(PxU8))*newBoxesCapacity), "Updated Boxes")); + } + + //Do we need more memory for the array of sorted boxes? + if(2*(mBoxesSize + mCreatedSize) + NUM_SENTINELS > mEndPointsCapacity) + { + const PxU32 newEndPointsCapacity = 2*(mBoxesSize + mCreatedSize) + NUM_SENTINELS; + + ValType* newEndPointValuesX = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(newEndPointsCapacity))), "BPValType")); + ValType* newEndPointValuesY = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(newEndPointsCapacity))), "BPValType")); + ValType* newEndPointValuesZ = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(ValType)*(newEndPointsCapacity))), "BPValType")); + BpHandle* newEndPointDatasX = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(newEndPointsCapacity))), "BpHandle")); + BpHandle* newEndPointDatasY = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(newEndPointsCapacity))), "BpHandle")); + BpHandle* newEndPointDatasZ = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*(newEndPointsCapacity))), "BpHandle")); + + PX_FREE(mListNext); + PX_FREE(mListPrev); + + mListNext = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*newEndPointsCapacity)), "NextList")); + mListPrev = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*newEndPointsCapacity)), "Prev")); + + + for(PxU32 a = 1; a < newEndPointsCapacity; ++a) + { + mListNext[a-1] = BpHandle(a); + mListPrev[a] = BpHandle(a-1); + } + mListNext[newEndPointsCapacity-1] = BpHandle(newEndPointsCapacity-1); + mListPrev[0] = 0; + + PxMemCopy(newEndPointValuesX, mEndPointValues[0], sizeof(ValType)*(mBoxesSize*2+NUM_SENTINELS)); + PxMemCopy(newEndPointValuesY, mEndPointValues[1], sizeof(ValType)*(mBoxesSize*2+NUM_SENTINELS)); + PxMemCopy(newEndPointValuesZ, mEndPointValues[2], sizeof(ValType)*(mBoxesSize*2+NUM_SENTINELS)); + PxMemCopy(newEndPointDatasX, mEndPointDatas[0], sizeof(BpHandle)*(mBoxesSize*2+NUM_SENTINELS)); + PxMemCopy(newEndPointDatasY, mEndPointDatas[1], sizeof(BpHandle)*(mBoxesSize*2+NUM_SENTINELS)); + PxMemCopy(newEndPointDatasZ, mEndPointDatas[2], sizeof(BpHandle)*(mBoxesSize*2+NUM_SENTINELS)); + PX_FREE(mEndPointValues[0]); + PX_FREE(mEndPointValues[1]); + PX_FREE(mEndPointValues[2]); + PX_FREE(mEndPointDatas[0]); + PX_FREE(mEndPointDatas[1]); + PX_FREE(mEndPointDatas[2]); + mEndPointValues[0] = newEndPointValuesX; + mEndPointValues[1] = newEndPointValuesY; + mEndPointValues[2] = newEndPointValuesZ; + mEndPointDatas[0] = newEndPointDatasX; + mEndPointDatas[1] = newEndPointDatasY; + mEndPointDatas[2] = newEndPointDatasZ; + mEndPointsCapacity = newEndPointsCapacity; + + PX_FREE(mSortedUpdateElements); + PX_FREE(mActivityPockets); + mSortedUpdateElements = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BpHandle)*newEndPointsCapacity)), "SortedUpdateElements")); + mActivityPockets = reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16((sizeof(BroadPhaseActivityPocket)*newEndPointsCapacity)), "BroadPhaseActivityPocket")); + } + + PxMemZero(mBoxesUpdated, sizeof(PxU8) * (mBoxesCapacity)); + + for(PxU32 a=0;a volB) + addPair(volA, volB, mScratchAllocator, mPairs, da); + else + removePair(volA, volB, mScratchAllocator, mPairs, da); + } + } + + mData = da.mData; + mDataSize = da.mSize; + mDataCapacity = da.mCapacity; + + batchCreate(); + + //Compute the lists of created and deleted overlap pairs. + + ComputeCreatedDeletedPairsLists( + mBoxGroups, + mData,mDataSize, + mScratchAllocator, + mCreatedPairsArray,mCreatedPairsSize,mCreatedPairsCapacity, + mDeletedPairsArray,mDeletedPairsSize,mDeletedPairsCapacity, + mActualDeletedPairSize, + mPairs); + + //DeletePairsLists(mActualDeletedPairSize, mDeletedPairsArray, mPairs); + + PX_ASSERT(isSelfConsistent()); + mBoxesSizePrev=mBoxesSize; +} + +void BroadPhaseSap::deletePairs() +{ + DeletePairsLists(mActualDeletedPairSize, mDeletedPairsArray, mPairs); +} + +void BroadPhaseBatchUpdateWorkTask::runInternal() +{ + mPairsSize=0; + mSap->batchUpdate(mAxis, mPairs, mPairsSize, mPairsCapacity); +} + +void BroadPhaseSap::update() +{ + PX_PROFILE_ZONE("BroadPhase.SapUpdate", mContextID); + + batchRemove(); + + //Check that the overlap pairs per axis have been reset. + PX_ASSERT(0==mBatchUpdateTasks[0].getPairsSize()); + PX_ASSERT(0==mBatchUpdateTasks[1].getPairsSize()); + PX_ASSERT(0==mBatchUpdateTasks[2].getPairsSize()); + + mBatchUpdateTasks[0].runInternal(); + mBatchUpdateTasks[1].runInternal(); + mBatchUpdateTasks[2].runInternal(); +} + +/////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE void InsertEndPoints(const ValType* PX_RESTRICT newEndPointValues, const BpHandle* PX_RESTRICT newEndPointDatas, PxU32 numNewEndPoints, + ValType* PX_RESTRICT endPointValues, BpHandle* PX_RESTRICT endPointDatas, const PxU32 numEndPoints, SapBox1D* PX_RESTRICT boxes) +{ + ValType* const BaseEPValue = endPointValues; + BpHandle* const BaseEPData = endPointDatas; + + const PxU32 OldSize = numEndPoints-NUM_SENTINELS; + const PxU32 NewSize = numEndPoints-NUM_SENTINELS+numNewEndPoints; + + BaseEPValue[NewSize + 1] = BaseEPValue[OldSize + 1]; + BaseEPData[NewSize + 1] = BaseEPData[OldSize + 1]; + + PxI32 WriteIdx = PxI32(NewSize); + PxU32 CurrInsIdx = 0; + + //const SapValType* FirstValue = &BaseEPValue[0]; + const BpHandle* FirstData = &BaseEPData[0]; + const ValType* CurrentValue = &BaseEPValue[OldSize]; + const BpHandle* CurrentData = &BaseEPData[OldSize]; + while(CurrentData>=FirstData) + { + const ValType& SrcValue = *CurrentValue; + const BpHandle& SrcData = *CurrentData; + const ValType& InsValue = newEndPointValues[CurrInsIdx]; + const BpHandle& InsData = newEndPointDatas[CurrInsIdx]; + + // We need to make sure we insert maxs before mins to handle exactly equal endpoints correctly + const bool ShouldInsert = isMax(InsData) ? (SrcValue <= InsValue) : (SrcValue < InsValue); + + const ValType& MovedValue = ShouldInsert ? InsValue : SrcValue; + const BpHandle& MovedData = ShouldInsert ? InsData : SrcData; + BaseEPValue[WriteIdx] = MovedValue; + BaseEPData[WriteIdx] = MovedData; + boxes[getOwner(MovedData)].mMinMax[isMax(MovedData)] = BpHandle(WriteIdx--); + + if(ShouldInsert) + { + CurrInsIdx++; + if(CurrInsIdx >= numNewEndPoints) + break;//we just inserted the last endpoint + } + else + { + CurrentValue--; + CurrentData--; + } + } +} + +static PX_FORCE_INLINE bool Intersect3D(const ValType bDir1Min, const ValType bDir1Max, const ValType bDir2Min, const ValType bDir2Max, const ValType bDir3Min, const ValType bDir3Max, + const ValType cDir1Min, const ValType cDir1Max, const ValType cDir2Min, const ValType cDir2Max, const ValType cDir3Min, const ValType cDir3Max) +{ + return (bDir1Max >= cDir1Min && cDir1Max >= bDir1Min && + bDir2Max >= cDir2Min && cDir2Max >= bDir2Min && + bDir3Max >= cDir3Min && cDir3Max >= bDir3Min); +} + +void BroadPhaseSap::ComputeSortedLists( //const PxVec4& globalMin, const PxVec4& /*globalMax*/, + BpHandle* PX_RESTRICT newBoxIndicesSorted, PxU32& newBoxIndicesCount, BpHandle* PX_RESTRICT oldBoxIndicesSorted, PxU32& oldBoxIndicesCount, + bool& allNewBoxesStatics, bool& allOldBoxesStatics) +{ + //To help us gather the two lists of sorted boxes we are going to use a bitmap and our knowledge of the indices of the new boxes + const PxU32 bitmapWordCount = ((mBoxesCapacity*2 + 31) & ~31)/32; + Cm::TmpMem bitMapMem(bitmapWordCount); + PxU32* bitMapWords = bitMapMem.getBase(); + PxMemSet(bitMapWords, 0, sizeof(PxU32)*bitmapWordCount); + Cm::BitMap bitmap; + bitmap.setWords(bitMapWords, bitmapWordCount); + + const PxU32 axis0 = 0; + const PxU32 axis1 = 2; + const PxU32 axis2 = 1; + + const PxU32 insertAABBStart = 0; + const PxU32 insertAABBEnd = mCreatedSize; + const BpHandle* PX_RESTRICT createdAABBs = mCreated; + SapBox1D** PX_RESTRICT asapBoxes = mBoxEndPts; + const Bp::FilterGroup::Enum* PX_RESTRICT asapBoxGroupIds = mBoxGroups; + BpHandle* PX_RESTRICT asapEndPointDatas = mEndPointDatas[axis0]; + const PxU32 numSortedEndPoints = mBoxesSize*2 + NUM_SENTINELS; + + //Set the bitmap for new box ids and compute the aabb (of the sorted handles/indices and not of the values) that bounds all new boxes. + + PxU32 globalAABBMinX = PX_MAX_U32; + PxU32 globalAABBMinY = PX_MAX_U32; + PxU32 globalAABBMinZ = PX_MAX_U32; + PxU32 globalAABBMaxX = 0; + PxU32 globalAABBMaxY = 0; + PxU32 globalAABBMaxZ = 0; + + // PT: TODO: compute the global bounds from the initial data, more cache/SIMD-friendly + // => maybe doesn't work, we're dealing with indices here not actual float values IIRC + for(PxU32 i=insertAABBStart;i(globalMin.x)); + PxU32 _globalAABBMinY = IntegerAABB::encodeFloatMin(PxUnionCast(globalMin.y)); + PxU32 _globalAABBMinZ = IntegerAABB::encodeFloatMin(PxUnionCast(globalMin.z)); + PxU32 _globalAABBMaxX = IntegerAABB::encodeFloatMin(PxUnionCast(globalMax.x)); + PxU32 _globalAABBMaxY = IntegerAABB::encodeFloatMin(PxUnionCast(globalMax.y)); + PxU32 _globalAABBMaxZ = IntegerAABB::encodeFloatMin(PxUnionCast(globalMax.z)); + (void)_globalAABBMinX;*/ + + PxU32 oldStaticCount=0; + PxU32 newStaticCount=0; + + //Assign the sorted end pts to the appropriate arrays. + // PT: TODO: we could just do this loop before inserting the new endpts, i.e. no need for a bitmap etc + // => but we need to insert the pts first to have valid mMinMax data in the above loop. + // => but why do we iterate over endpoints and then skip the mins? Why not iterate directly over boxes? ====> probably to get sorted results + // => we could then just use the regular bounds data etc + for(PxU32 i=1;i nepsv(numEndPoints), bv(numEndPoints); + ValType* newEPSortedValues = nepsv.getBase(); + ValType* bufferValues = bv.getBase(); + + // PT: TODO: use the scratch allocator + Cm::RadixSortBuffered RS; + + for(PxU32 Axis=0;Axis<3;Axis++) + { + for(PxU32 i=0;i>1]); + bufferDatas[i] = setData(boxIndex, (sortedIndex&1)!=0); + } + } + + InsertEndPoints(bufferValues, bufferDatas, numEndPoints, mEndPointValues[Axis], mEndPointDatas[Axis], 2*(mBoxesSize-mCreatedSize)+NUM_SENTINELS, mBoxEndPts[Axis]); + } + } + + //Some debug tests. +#if PX_DEBUG + { + for(PxU32 i=0;i oldBoxesIndicesSortedMem(numOldBoxes); + Cm::TmpMem newBoxesIndicesSortedMem(numNewBoxes); + BpHandle* oldBoxesIndicesSorted = oldBoxesIndicesSortedMem.getBase(); + BpHandle* newBoxesIndicesSorted = newBoxesIndicesSortedMem.getBase(); + PxU32 oldBoxCount = 0; + PxU32 newBoxCount = 0; + + bool allNewBoxesStatics = false; + bool allOldBoxesStatics = false; + // PT: TODO: separate static/dynamic to speed things up, compute "minPosList" etc at the same time + // PT: TODO: isn't "newBoxesIndicesSorted" the same as what we already computed in batchCreate() ? + //Ready to gather the two lists now. + ComputeSortedLists(/*globalMin, globalMax,*/ newBoxesIndicesSorted, newBoxCount, oldBoxesIndicesSorted, oldBoxCount, allNewBoxesStatics, allOldBoxesStatics); + + //Intersect new boxes with new boxes and new boxes with existing boxes. + if(!allNewBoxesStatics || !allOldBoxesStatics) + { + const AuxData data0(newBoxCount, mBoxEndPts, newBoxesIndicesSorted, mBoxGroups); + + if(!allNewBoxesStatics) + { + performBoxPruningNewNew(&data0, mScratchAllocator, +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT, +#endif + mPairs, mData, mDataSize, mDataCapacity); + } + + // the old boxes are not the first ones in the array + if(numOldBoxes) + { + if(oldBoxCount) + { + const AuxData data1(oldBoxCount, mBoxEndPts, oldBoxesIndicesSorted, mBoxGroups); + + performBoxPruningNewOld(&data0, &data1, mScratchAllocator, +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + mLUT, +#endif + mPairs, mData, mDataSize, mDataCapacity); + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void BroadPhaseSap::batchRemove() +{ + if(!mRemovedSize) return; // Early-exit if no object has been removed + + //The box count is incremented when boxes are added to the create list but these boxes + //haven't yet been added to the pair manager or the sorted axis lists. We need to + //pretend that the box count is the value it was when the bp was last updated. + //Then, at the end, we need to set the box count to the number that includes the boxes + //in the create list and subtract off the boxes that have been removed. + PxU32 currBoxesSize=mBoxesSize; + mBoxesSize=mBoxesSizePrev; + + for(PxU32 Axis=0;Axis<3;Axis++) + { + ValType* const BaseEPValue = mEndPointValues[Axis]; + BpHandle* const BaseEPData = mEndPointDatas[Axis]; + PxU32 MinMinIndex = PX_MAX_U32; + for(PxU32 i=0;i>5); + Cm::TmpMem bitmapWords(bitmapWordCount); + PxMemZero(bitmapWords.getBase(),sizeof(PxU32)*bitmapWordCount); + Cm::BitMap bitmap; + bitmap.setWords(bitmapWords.getBase(),bitmapWordCount); + for(PxU32 i=0;i oldMaxNb); + PX_ASSERT(newMaxNb > 0); + PX_ASSERT(0==((newMaxNb*sizeof(BroadPhasePair)) & 15)); + BroadPhasePair* newElements = reinterpret_cast(scratchAllocator->alloc(sizeof(BroadPhasePair)*newMaxNb, true)); + PX_ASSERT(0==(uintptr_t(newElements) & 0x0f)); + PxMemCopy(newElements, elements, oldMaxNb*sizeof(BroadPhasePair)); + scratchAllocator->free(elements); + return newElements; +} + +#define PERFORM_COMPARISONS 1 + + +void BroadPhaseSap::batchUpdate +(const PxU32 Axis, BroadPhasePair*& pairs, PxU32& pairsSize, PxU32& pairsCapacity) +{ + //Nothin updated so don't do anything + if(mUpdatedSize == 0) + return; + + //If number updated is sufficiently fewer than number of boxes (say less than 20%) + if((mUpdatedSize*5) < mBoxesSize) + { + batchUpdateFewUpdates(Axis, pairs, pairsSize, pairsCapacity); + return; + } + + PxU32 numPairs=0; + PxU32 maxNumPairs=pairsCapacity; + + const PxBounds3* PX_RESTRICT boxMinMax3D = mBoxBoundsMinMax; + SapBox1D* boxMinMax2D[6]={mBoxEndPts[1],mBoxEndPts[2],mBoxEndPts[2],mBoxEndPts[0],mBoxEndPts[0],mBoxEndPts[1]}; + + const SapBox1D* PX_RESTRICT boxMinMax0=boxMinMax2D[2*Axis+0]; + const SapBox1D* PX_RESTRICT boxMinMax1=boxMinMax2D[2*Axis+1]; + +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + const Bp::FilterGroup::Enum* PX_RESTRICT asapBoxGroupIds=mBoxGroups; +#endif + + SapBox1D* PX_RESTRICT asapBoxes=mBoxEndPts[Axis]; + + ValType* PX_RESTRICT asapEndPointValues=mEndPointValues[Axis]; + BpHandle* PX_RESTRICT asapEndPointDatas=mEndPointDatas[Axis]; + + ValType* const PX_RESTRICT BaseEPValues = asapEndPointValues; + BpHandle* const PX_RESTRICT BaseEPDatas = asapEndPointDatas; + + PxU8* PX_RESTRICT updated = mBoxesUpdated; + + //KS - can we lazy create these inside the loop? Might benefit us + + //There are no extents, jus the sentinels, so exit early. + if(isSentinel(BaseEPDatas[1])) + return; + + //We are going to skip the 1st element in the array (the sublist will be sorted) + //but we must first update its value if it has moved + //const PxU32 startIsMax = isMax(BaseEPDatas[1]); + PX_ASSERT(!isMax(BaseEPDatas[1])); + const BpHandle startHandle = getOwner(BaseEPDatas[1]); + + //KS - in theory, we should just be able to grab the min element but there's some issue where a body's max < min (i.e. an invalid extents) that + //appears in a unit test + // ValType ThisValue_ = boxMinMax3D[startHandle].getMin(Axis); + ValType ThisValue_ = encodeMin(boxMinMax3D[startHandle], Axis, mContactDistance[startHandle]); + + BaseEPValues[1] = ThisValue_; + + PxU32 updateCounter = mUpdatedSize*2; + + updateCounter -= updated[startHandle]; + + //We'll never overlap with this sentinel but it just ensures that we don't need to branch to see if + //there's a pocket that we need to test against + + BroadPhaseActivityPocket* PX_RESTRICT currentPocket = mActivityPockets; + + currentPocket->mEndIndex = 0; + currentPocket->mStartIndex = 0; + + BpHandle ind = 2; + PxU8 wasUpdated = updated[startHandle]; + for(; !isSentinel(BaseEPDatas[ind]); ++ind) + { + BpHandle ThisData = BaseEPDatas[ind]; + + const BpHandle handle = getOwner(ThisData); + + if(updated[handle] || wasUpdated) + { + wasUpdated = updated[handle]; + updateCounter -= wasUpdated; + + BpHandle ThisIndex = ind; + + const BpHandle startIsMax = isMax(ThisData); + + //Access and write back the updated values. TODO - can we avoid this when we're walking through inactive nodes? + //BPValType ThisValue = boxMinMax1D[Axis][twoHandle+startIsMax]; + //BPValType ThisValue = startIsMax ? boxMinMax3D[handle].getMax(Axis) : boxMinMax3D[handle].getMin(Axis); + //ValType ThisValue = boxMinMax3D[handle].getExtent(startIsMax, Axis); + + ValType ThisValue = startIsMax ? encodeMax(boxMinMax3D[handle], Axis, mContactDistance[handle]) + : encodeMin(boxMinMax3D[handle], Axis, mContactDistance[handle]); + + BaseEPValues[ThisIndex] = ThisValue; + + PX_ASSERT(handle!=BP_INVALID_BP_HANDLE); + + //We always iterate back through the list... + + BpHandle CurrentIndex = mListPrev[ThisIndex]; + ValType CurrentValue = BaseEPValues[CurrentIndex]; + //PxBpHandle CurrentData = BaseEPDatas[CurrentIndex]; + + if(CurrentValue > ThisValue) + { + wasUpdated = 1; + //Get the bounds of the curr aabb. + //Get the box1d of the curr aabb. + /*const SapBox1D* PX_RESTRICT Object=&asapBoxes[handle]; + PX_ASSERT(Object->mMinMax[0]!=BP_INVALID_BP_HANDLE); + PX_ASSERT(Object->mMinMax[1]!=BP_INVALID_BP_HANDLE);*/ + + // const ValType boxMax=boxMinMax3D[handle].getMax(Axis); + + const ValType boxMax=encodeMax(boxMinMax3D[handle], Axis, mContactDistance[handle]); + + PxU32 endIndex = ind; + PxU32 startIndex = ind; + +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + const Bp::FilterGroup::Enum group = asapBoxGroupIds[handle]; +#endif + if(!isMax(ThisData)) + { + do + { + BpHandle CurrentData = BaseEPDatas[CurrentIndex]; + const BpHandle IsMax = isMax(CurrentData); + + #if PERFORM_COMPARISONS + if(IsMax) + { + const BpHandle ownerId=getOwner(CurrentData); + SapBox1D* PX_RESTRICT id1 = asapBoxes + ownerId; + // Our min passed a max => start overlap + + if( + BaseEPValues[id1->mMinMax[0]] < boxMax && + //2D intersection test using up-to-date values + Intersect2D_Handle(boxMinMax0[handle].mMinMax[0], boxMinMax0[handle].mMinMax[1], boxMinMax1[handle].mMinMax[0], boxMinMax1[handle].mMinMax[1], + boxMinMax0[ownerId].mMinMax[0],boxMinMax0[ownerId].mMinMax[1],boxMinMax1[ownerId].mMinMax[0],boxMinMax1[ownerId].mMinMax[1]) + + #if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + && groupFiltering(group, asapBoxGroupIds[ownerId], mLUT) + #else + && groupFiltering(group, asapBoxGroupIds[ownerId]) + #endif + #else + && handle!=ownerId + #endif + ) + { + if(numPairs==maxNumPairs) + { + const PxU32 newMaxNumPairs=maxNumPairs*2; + pairs = reinterpret_cast(resizeBroadPhasePairArray(maxNumPairs, newMaxNumPairs, mScratchAllocator, pairs)); + maxNumPairs=newMaxNumPairs; + } + PX_ASSERT(numPairs stop overlap + const BpHandle ownerId=getOwner(CurrentData); + +#if 1 + if( +#if BP_SAP_USE_OVERLAP_TEST_ON_REMOVES + Intersect2D_Handle(boxMinMax0[handle].mMinMax[0], boxMinMax0[handle].mMinMax[1], boxMinMax1[handle].mMinMax[0], boxMinMax1[handle].mMinMax[1], + boxMinMax0[ownerId].mMinMax[0],boxMinMax0[ownerId].mMinMax[1],boxMinMax1[ownerId].mMinMax[0],boxMinMax1[ownerId].mMinMax[1]) +#endif +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + && groupFiltering(group, asapBoxGroupIds[ownerId], mLUT) + #else + && groupFiltering(group, asapBoxGroupIds[ownerId]) + #endif +#else + && handle!=ownerId +#endif + ) +#endif + { + if(numPairs==maxNumPairs) + { + const PxU32 newMaxNumPairs=maxNumPairs*2; + pairs = reinterpret_cast(resizeBroadPhasePairArray(maxNumPairs, newMaxNumPairs, mScratchAllocator, pairs)); + maxNumPairs=newMaxNumPairs; + } + PX_ASSERT(numPairsmStartIndex) + { + currentPocket--; + } + //If our start index > currentPocket->mEndIndex, then we don't overlap so create a new pocket + if(currentPocket == mActivityPockets || startIndex > (currentPocket->mEndIndex+1)) + { + currentPocket++; + currentPocket->mStartIndex = startIndex; + } + currentPocket->mEndIndex = endIndex; + }// update max + //ind++; + } + else if (updateCounter == 0) //We've updated all the bodies and neither this nor the previous body was updated, so we're done + break; + + }// updated aabbs + + pairsSize=numPairs; + pairsCapacity=maxNumPairs; + + + BroadPhaseActivityPocket* pocket = mActivityPockets+1; + + while(pocket <= currentPocket) + { + for(PxU32 a = pocket->mStartIndex; a <= pocket->mEndIndex; ++a) + { + mListPrev[a] = BpHandle(a); + } + + //Now copy all the data to the array, updating the remap table + + PxU32 CurrIndex = pocket->mStartIndex-1; + for(PxU32 a = pocket->mStartIndex; a <= pocket->mEndIndex; ++a) + { + CurrIndex = mListNext[CurrIndex]; + PxU32 origIndex = CurrIndex; + BpHandle remappedIndex = mListPrev[origIndex]; + + if(origIndex != a) + { + const BpHandle ownerId=getOwner(BaseEPDatas[remappedIndex]); + const BpHandle IsMax = isMax(BaseEPDatas[remappedIndex]); + ValType tmp = BaseEPValues[a]; + BpHandle tmpHandle = BaseEPDatas[a]; + + BaseEPValues[a] = BaseEPValues[remappedIndex]; + BaseEPDatas[a] = BaseEPDatas[remappedIndex]; + + BaseEPValues[remappedIndex] = tmp; + BaseEPDatas[remappedIndex] = tmpHandle; + + mListPrev[remappedIndex] = mListPrev[a]; + //Write back remap index (should be an immediate jump to original index) + mListPrev[mListPrev[a]] = remappedIndex; + asapBoxes[ownerId].mMinMax[IsMax] = BpHandle(a); + } + + } + + ////Reset next and prev ptrs back + for(PxU32 a = pocket->mStartIndex-1; a <= pocket->mEndIndex; ++a) + { + mListPrev[a+1] = BpHandle(a); + mListNext[a] = BpHandle(a+1); + } + + pocket++; + } + mListPrev[0] = 0; +} + + +void BroadPhaseSap::batchUpdateFewUpdates +(const PxU32 Axis, BroadPhasePair*& pairs, PxU32& pairsSize, PxU32& pairsCapacity) +{ + PxU32 numPairs=0; + PxU32 maxNumPairs=pairsCapacity; + + const PxBounds3* PX_RESTRICT boxMinMax3D = mBoxBoundsMinMax; + SapBox1D* boxMinMax2D[6]={mBoxEndPts[1],mBoxEndPts[2],mBoxEndPts[2],mBoxEndPts[0],mBoxEndPts[0],mBoxEndPts[1]}; + +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + const Bp::FilterGroup::Enum* PX_RESTRICT asapBoxGroupIds=mBoxGroups; +#endif + + SapBox1D* PX_RESTRICT asapBoxes=mBoxEndPts[Axis]; + + /*const BPValType* PX_RESTRICT boxMinMax0=boxMinMax2D[2*Axis]; + const BPValType* PX_RESTRICT boxMinMax1=boxMinMax2D[2*Axis+1];*/ + + ValType* PX_RESTRICT asapEndPointValues=mEndPointValues[Axis]; + BpHandle* PX_RESTRICT asapEndPointDatas=mEndPointDatas[Axis]; + + ValType* const PX_RESTRICT BaseEPValues = asapEndPointValues; + BpHandle* const PX_RESTRICT BaseEPDatas = asapEndPointDatas; + + const SapBox1D* PX_RESTRICT boxMinMax0=boxMinMax2D[2*Axis+0]; + const SapBox1D* PX_RESTRICT boxMinMax1=boxMinMax2D[2*Axis+1]; + + PxU8* PX_RESTRICT updated = mBoxesUpdated; + + const PxU32 endPointSize = mBoxesSize*2 + 1; + + //There are no extents, just the sentinels, so exit early. + if(isSentinel(BaseEPDatas[1])) + return; + + PxU32 ind_ = 0; + + PxU32 index = 1; + + if(mUpdatedSize < 512) + { + //The array of updated elements is small, so use qsort to sort them + for(PxU32 a = 0; a < mUpdatedSize; ++a) + { + const PxU32 handle=mUpdated[a]; + + const SapBox1D* Object=&asapBoxes[handle]; + + PX_ASSERT(Object->mMinMax[0]!=BP_INVALID_BP_HANDLE); + PX_ASSERT(Object->mMinMax[1]!=BP_INVALID_BP_HANDLE); + + //Get the bounds of the curr aabb. + +// const ValType boxMin=boxMinMax3D[handle].getMin(Axis); +// const ValType boxMax=boxMinMax3D[handle].getMax(Axis); + + const ValType boxMin = encodeMin(boxMinMax3D[handle], Axis, mContactDistance[handle]); + const ValType boxMax = encodeMax(boxMinMax3D[handle], Axis, mContactDistance[handle]); + + BaseEPValues[Object->mMinMax[0]] = boxMin; + BaseEPValues[Object->mMinMax[1]] = boxMax; + + mSortedUpdateElements[ind_++] = Object->mMinMax[0]; + mSortedUpdateElements[ind_++] = Object->mMinMax[1]; + } + Ps::sort(mSortedUpdateElements, ind_); + } + else + { + //The array of updated elements is large so use a bucket sort to sort them + for(; index < endPointSize; ++index) + { + if(isSentinel( BaseEPDatas[index] )) + break; + BpHandle ThisData = BaseEPDatas[index]; + BpHandle owner = BpHandle(getOwner(ThisData)); + if(updated[owner]) + { + //BPValType ThisValue = isMax(ThisData) ? boxMinMax3D[owner].getMax(Axis) : boxMinMax3D[owner].getMin(Axis); + ValType ThisValue = isMax(ThisData) ? encodeMax(boxMinMax3D[owner], Axis, mContactDistance[owner]) + : encodeMin(boxMinMax3D[owner], Axis, mContactDistance[owner]); + BaseEPValues[index] = ThisValue; + mSortedUpdateElements[ind_++] = BpHandle(index); + } + } + } + + const PxU32 updateCounter = ind_; + + //We'll never overlap with this sentinel but it just ensures that we don't need to branch to see if + //there's a pocket that we need to test against + BroadPhaseActivityPocket* PX_RESTRICT currentPocket = mActivityPockets; + currentPocket->mEndIndex = 0; + currentPocket->mStartIndex = 0; + + for(PxU32 a = 0; a < updateCounter; ++a) + { + BpHandle ind = mSortedUpdateElements[a]; + + BpHandle NextData; + BpHandle PrevData; + do + { + BpHandle ThisData = BaseEPDatas[ind]; + + const BpHandle handle = getOwner(ThisData); + + BpHandle ThisIndex = ind; + ValType ThisValue = BaseEPValues[ThisIndex]; + + //Get the box1d of the curr aabb. + const SapBox1D* PX_RESTRICT Object=&asapBoxes[handle]; + + PX_ASSERT(handle!=BP_INVALID_BP_HANDLE); + + PX_ASSERT(Object->mMinMax[0]!=BP_INVALID_BP_HANDLE); + PX_ASSERT(Object->mMinMax[1]!=BP_INVALID_BP_HANDLE); + PX_UNUSED(Object); + + //Get the bounds of the curr aabb. + //const PxU32 twoHandle = 2*handle; + + const ValType boxMax=encodeMax(boxMinMax3D[handle], Axis, mContactDistance[handle]); + + //We always iterate back through the list... + BpHandle CurrentIndex = mListPrev[ThisIndex]; + ValType CurrentValue = BaseEPValues[CurrentIndex]; + + if(CurrentValue > ThisValue) + { + //We're performing some swaps so we need an activity pocket here. This structure allows us to keep track of the range of + //modifications in the sorted lists. Doesn't help when everything's moving but makes a really big difference to reconstituting the + //list when only a small number of things are moving + + PxU32 endIndex = ind; + PxU32 startIndex = ind; + + //const BPValType* PX_RESTRICT box0MinMax0 = &boxMinMax0[twoHandle]; + //const BPValType* PX_RESTRICT box0MinMax1 = &boxMinMax1[twoHandle]; +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + const Bp::FilterGroup::Enum group = asapBoxGroupIds[handle]; +#endif + if(!isMax(ThisData)) + { + do + { + BpHandle CurrentData = BaseEPDatas[CurrentIndex]; + const BpHandle IsMax = isMax(CurrentData); + + #if PERFORM_COMPARISONS + if(IsMax) + { + const BpHandle ownerId=getOwner(CurrentData); + SapBox1D* PX_RESTRICT id1 = asapBoxes + ownerId; + // Our min passed a max => start overlap + + if( + BaseEPValues[id1->mMinMax[0]] < boxMax && + //2D intersection test using up-to-date values + Intersect2D_Handle(boxMinMax0[handle].mMinMax[0], boxMinMax0[handle].mMinMax[1], boxMinMax1[handle].mMinMax[0], boxMinMax1[handle].mMinMax[1], + boxMinMax0[ownerId].mMinMax[0],boxMinMax0[ownerId].mMinMax[1],boxMinMax1[ownerId].mMinMax[0],boxMinMax1[ownerId].mMinMax[1]) + #if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + && groupFiltering(group, asapBoxGroupIds[ownerId], mLUT) + #else + && groupFiltering(group, asapBoxGroupIds[ownerId]) + #endif + #else + && Object!=id1 + #endif + ) + { + if(numPairs==maxNumPairs) + { + const PxU32 newMaxNumPairs=maxNumPairs*2; + pairs = reinterpret_cast(resizeBroadPhasePairArray(maxNumPairs, newMaxNumPairs, mScratchAllocator, pairs)); + maxNumPairs=newMaxNumPairs; + } + PX_ASSERT(numPairs stop overlap + const BpHandle ownerId=getOwner(CurrentData); + +#if 1 + if( +#if BP_SAP_USE_OVERLAP_TEST_ON_REMOVES + Intersect2D_Handle(boxMinMax0[handle].mMinMax[0], boxMinMax0[handle].mMinMax[1], boxMinMax1[handle].mMinMax[0], boxMinMax1[handle].mMinMax[1], + boxMinMax0[ownerId].mMinMax[0],boxMinMax0[ownerId].mMinMax[1],boxMinMax1[ownerId].mMinMax[0],boxMinMax1[ownerId].mMinMax[1]) +#endif +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + #ifdef BP_FILTERING_USES_TYPE_IN_GROUP + && groupFiltering(group, asapBoxGroupIds[ownerId], mLUT) + #else + && groupFiltering(group, asapBoxGroupIds[ownerId]) + #endif +#else + && Object!=id1 +#endif + ) +#endif + { + if(numPairs==maxNumPairs) + { + const PxU32 newMaxNumPairs=maxNumPairs*2; + pairs = reinterpret_cast(resizeBroadPhasePairArray(maxNumPairs, newMaxNumPairs, mScratchAllocator, pairs)); + maxNumPairs=newMaxNumPairs; + } + PX_ASSERT(numPairsmStartIndex) + { + currentPocket--; + } + //If our start index > currentPocket->mEndIndex, then we don't overlap so create a new pocket + if(currentPocket == mActivityPockets || startIndex > (currentPocket->mEndIndex+1)) + { + currentPocket++; + currentPocket->mStartIndex = startIndex; + } + currentPocket->mEndIndex = endIndex; + }// update max + //Get prev and next ptr... + + NextData = BaseEPDatas[++ind]; + PrevData = BaseEPDatas[mListPrev[ind]]; + + }while(!isSentinel(NextData) && !updated[getOwner(NextData)] && updated[getOwner(PrevData)]); + + }// updated aabbs + + pairsSize=numPairs; + pairsCapacity=maxNumPairs; + + + BroadPhaseActivityPocket* pocket = mActivityPockets+1; + + while(pocket <= currentPocket) + { + //PxU32 CurrIndex = mListPrev[pocket->mStartIndex]; + for(PxU32 a = pocket->mStartIndex; a <= pocket->mEndIndex; ++a) + { + mListPrev[a] = BpHandle(a); + } + + //Now copy all the data to the array, updating the remap table + PxU32 CurrIndex = pocket->mStartIndex-1; + for(PxU32 a = pocket->mStartIndex; a <= pocket->mEndIndex; ++a) + { + CurrIndex = mListNext[CurrIndex]; + PxU32 origIndex = CurrIndex; + BpHandle remappedIndex = mListPrev[origIndex]; + + if(origIndex != a) + { + const BpHandle ownerId=getOwner(BaseEPDatas[remappedIndex]); + const BpHandle IsMax = isMax(BaseEPDatas[remappedIndex]); + ValType tmp = BaseEPValues[a]; + BpHandle tmpHandle = BaseEPDatas[a]; + + BaseEPValues[a] = BaseEPValues[remappedIndex]; + BaseEPDatas[a] = BaseEPDatas[remappedIndex]; + + BaseEPValues[remappedIndex] = tmp; + BaseEPDatas[remappedIndex] = tmpHandle; + + mListPrev[remappedIndex] = mListPrev[a]; + //Write back remap index (should be an immediate jump to original index) + mListPrev[mListPrev[a]] = remappedIndex; + asapBoxes[ownerId].mMinMax[IsMax] = BpHandle(a); + } + + } + + for(PxU32 a = pocket->mStartIndex-1; a <= pocket->mEndIndex; ++a) + { + mListPrev[a+1] = BpHandle(a); + mListNext[a] = BpHandle(a+1); + } + pocket++; + } +} + +#if PX_DEBUG + +bool BroadPhaseSap::isSelfOrdered() const +{ + if(0==mBoxesSize) + { + return true; + } + + for(PxU32 Axis=0;Axis<3;Axis++) + { + PxU32 it=1; + PX_ASSERT(mEndPointDatas[Axis]); + while(!isSentinel(mEndPointDatas[Axis][it])) + { + //Test the array is sorted. + const ValType prevVal=mEndPointValues[Axis][it-1]; + const ValType currVal=mEndPointValues[Axis][it]; + if(currValid1) PxBpHandleSwap(id0, id1); +} + +PX_FORCE_INLINE bool DifferentPair(const BroadPhasePair& p, BpHandle id0, BpHandle id1) +{ + return (id0!=p.mVolA) || (id1!=p.mVolB); +} + +PX_FORCE_INLINE int Hash32Bits_1(int key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} + +PX_FORCE_INLINE PxU32 Hash(BpHandle id0, BpHandle id1) +{ + return PxU32(Hash32Bits_1( int(PxU32(id0)|(PxU32(id1)<<16)) )); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +SapPairManager::SapPairManager() : + mHashTable (NULL), + mNext (NULL), + mHashSize (0), + mHashCapacity (0), + mMinAllowedHashCapacity (0), + mActivePairs (NULL), + mActivePairStates (NULL), + mNbActivePairs (0), + mActivePairsCapacity (0), + mMask (0) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +SapPairManager::~SapPairManager() +{ + PX_ASSERT(NULL==mHashTable); + PX_ASSERT(NULL==mNext); + PX_ASSERT(NULL==mActivePairs); + PX_ASSERT(NULL==mActivePairStates); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +void SapPairManager::init(const PxU32 size) +{ + mHashTable=reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16(sizeof(BpHandle)*size), "BpHandle")); + mNext=reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16(sizeof(BpHandle)*size), "BpHandle")); + mActivePairs=reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16(sizeof(BroadPhasePair)*size), "BroadPhasePair")); + mActivePairStates=reinterpret_cast(PX_ALLOC(ALIGN_SIZE_16(sizeof(PxU8)*size), "BroadPhaseContextSap ActivePairStates")); + mHashCapacity=size; + mMinAllowedHashCapacity = size; + mActivePairsCapacity=size; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void SapPairManager::release() +{ + PX_FREE(mHashTable); + PX_FREE(mNext); + PX_FREE(mActivePairs); + PX_FREE(mActivePairStates); + mHashTable = NULL; + mNext = NULL; + mActivePairs = NULL; + mActivePairStates = NULL; + mNext = 0; + mHashSize = 0; + mHashCapacity = 0; + mMinAllowedHashCapacity = 0; + mNbActivePairs = 0; + mActivePairsCapacity = 0; + mMask = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const BroadPhasePair* SapPairManager::FindPair(BpHandle id0, BpHandle id1) const +{ + if(0==mHashSize) return NULL; // Nothing has been allocated yet + + // Order the ids + Sort(id0, id1); + + // Compute hash value for this pair + PxU32 HashValue = Hash(id0, id1) & mMask; + PX_ASSERT(HashValue the pair is persistent + PX_ASSERT(Offset the pair is persistent + PX_ASSERT(Offset= mHashSize) + { + // Get more entries + mHashSize = Ps::nextPowerOfTwo(mNbActivePairs+1); + mMask = mHashSize-1; + + reallocPairs(mHashSize>mHashCapacity); + + // Recompute hash value with new hash size + HashValue = Hash(id0, id1) & mMask; + } + + PX_ASSERT(mNbActivePairsmVolA = id0; // ### CMOVs would be nice here + p->mVolB = id1; + mActivePairStates[mNbActivePairs]=state; + + PX_ASSERT(mNbActivePairsmVolA, Last->mVolB) & mMask; + + // Walk the hash table to fix mNext + PX_ASSERT(LastHashValuemVolA==id0); + PX_ASSERT(P->mVolB==id1); + + RemovePair(id0, id1, HashValue, GetPairIndex(P)); + + shrinkMemory(); + + return true; +} + +bool SapPairManager::RemovePairs(const Cm::BitMap& removedAABBs) +{ + PxU32 i=0; + while(i mMinAllowedHashCapacity) || (mHashSize <= (mHashCapacity >> 2)) || (mHashSize <= (mActivePairsCapacity >> 2))); +} + +void SapPairManager::reallocPairs(const bool allocRequired) +{ + if(allocRequired) + { + PX_FREE(mHashTable); + mHashCapacity=mHashSize; + mActivePairsCapacity=mHashSize; + mHashTable = reinterpret_cast(PX_ALLOC(mHashSize*sizeof(BpHandle), "BpHandle")); + + for(PxU32 i=0;i(PX_ALLOC(mHashSize * sizeof(BroadPhasePair), "BroadPhasePair")); PX_ASSERT(NewPairs); + BpHandle* NewNext = reinterpret_cast(PX_ALLOC(mHashSize * sizeof(BpHandle), "BpHandle")); PX_ASSERT(NewNext); + PxU8* NewPairStates = reinterpret_cast(PX_ALLOC(mHashSize * sizeof(PxU8), "SapPairStates")); PX_ASSERT(NewPairStates); + + // Copy old data if needed + if(mNbActivePairs) + { + PxMemCopy(NewPairs, mActivePairs, mNbActivePairs*sizeof(BroadPhasePair)); + PxMemCopy(NewPairStates, mActivePairStates, mNbActivePairs*sizeof(PxU8)); + } + + // ### check it's actually needed... probably only for pairs whose hash value was cut by the and + // yeah, since Hash(id0, id1) is a constant + // However it might not be needed to recompute them => only less efficient but still ok + for(PxU32 i=0;i only less efficient but still ok + for(PxU32 i=0;i0); + const PxU32 newMaxNumPairs=2*maxNumPairs; + BroadPhasePair* newPairs=reinterpret_cast(PX_ALLOC(sizeof(BroadPhasePair)*newMaxNumPairs, "BroadPhasePair")); + PxMemCopy(newPairs, pairs, sizeof(BroadPhasePair)*maxNumPairs); + PX_FREE(pairs); + pairs=newPairs; + maxNumPairs=newMaxNumPairs; +} + +void ComputeCreatedDeletedPairsLists +(const Bp::FilterGroup::Enum* PX_RESTRICT boxGroups, + const BpHandle* PX_RESTRICT dataArray, const PxU32 dataArraySize, + PxcScratchAllocator* scratchAllocator, + BroadPhasePair*& createdPairsList, PxU32& numCreatedPairs, PxU32& maxNumCreatedPairs, + BroadPhasePair*& deletedPairsList, PxU32& numDeletedPairs, PxU32& maxNumDeletedPairs, + PxU32& numActualDeletedPairs, + SapPairManager& pairManager) +{ +#if BP_SAP_TEST_GROUP_ID_CREATEUPDATE + PX_UNUSED(boxGroups); +#endif + + for(PxU32 i=0;i(scratchAllocator->alloc(sizeof(BroadPhasePair)*2*maxNumDeletedPairs, true)); + PxMemCopy(newDeletedPairsList, deletedPairsList, sizeof(BroadPhasePair)*maxNumDeletedPairs); + scratchAllocator->free(deletedPairsList); + deletedPairsList = newDeletedPairsList; + maxNumDeletedPairs = 2*maxNumDeletedPairs; + } + + PX_ASSERT(numDeletedPairsmUserData != 0xcdcdcdcd); + deletedPairsList[numDeletedPairs++] = BroadPhasePair(UP->mVolA,UP->mVolB/*, ID*/); + } + } + else + { + pairManager.ClearInArray(UP); + // Add => already there... Might want to create user data, though + if(pairManager.IsNew(UP)) + { +#if !BP_SAP_TEST_GROUP_ID_CREATEUPDATE + if(groupFiltering(boxGroups[UP->mVolA], boxGroups[UP->mVolB])) +#endif + { + if(numCreatedPairs==maxNumCreatedPairs) + { + BroadPhasePair* newCreatedPairsList = reinterpret_cast(scratchAllocator->alloc(sizeof(BroadPhasePair)*2*maxNumCreatedPairs, true)); + PxMemCopy(newCreatedPairsList, createdPairsList, sizeof(BroadPhasePair)*maxNumCreatedPairs); + scratchAllocator->free(createdPairsList); + createdPairsList = newCreatedPairsList; + maxNumCreatedPairs = 2*maxNumCreatedPairs; + } + + PX_ASSERT(numCreatedPairsmVolA,UP->mVolB/*, ID*/); + } + pairManager.ClearNew(UP); + } + } + } + + //Record pairs that are to be deleted because they were simultaneously created and removed + //from different axis sorts. + numActualDeletedPairs=numDeletedPairs; + for(PxU32 i=0;i(scratchAllocator->alloc(sizeof(BroadPhasePair)*2*maxNumDeletedPairs, true)); + PxMemCopy(newDeletedPairsList, deletedPairsList, sizeof(BroadPhasePair)*maxNumDeletedPairs); + scratchAllocator->free(deletedPairsList); + deletedPairsList = newDeletedPairsList; + maxNumDeletedPairs = 2*maxNumDeletedPairs; + } + + PX_ASSERT(numActualDeletedPairs<=maxNumDeletedPairs); + deletedPairsList[numActualDeletedPairs++] = BroadPhasePair(UP->mVolA,UP->mVolB/*, ID*/); //KS - should we even get here???? + } + } + +// // #### try batch removal here +// for(PxU32 i=0;i i && boxGroups[deletedPairsList[numDeletedPairs-1].mVolA] == boxGroups[deletedPairsList[numDeletedPairs-1].mVolB]) + { + numDeletedPairs--; + } + deletedPairsList[i]=deletedPairsList[numDeletedPairs-1]; + numDeletedPairs--; + } + } +#endif +} + +void DeletePairsLists(const PxU32 numActualDeletedPairs, BroadPhasePair* deletedPairsList, SapPairManager& pairManager) +{ + // #### try batch removal here + for(PxU32 i=0;i + static PxU32 gNbIter = 0; + static PxU32 gNbTests = 0; + static PxU32 gNbPairs = 0; + #define START_STATS gNbIter = gNbTests = gNbPairs = 0; + #define INCREASE_STATS_NB_ITER gNbIter++; + #define INCREASE_STATS_NB_TESTS gNbTests++; + #define INCREASE_STATS_NB_PAIRS gNbPairs++; + #define DUMP_STATS printf("%d %d %d\n", gNbIter, gNbTests, gNbPairs); +#else + #define START_STATS + #define INCREASE_STATS_NB_ITER + #define INCREASE_STATS_NB_TESTS + #define INCREASE_STATS_NB_PAIRS + #define DUMP_STATS +#endif + +void DataArray::Resize(PxcScratchAllocator* scratchAllocator) +{ + BpHandle* newDataArray = reinterpret_cast(scratchAllocator->alloc(sizeof(BpHandle)*mCapacity*2, true)); + PxMemCopy(newDataArray, mData, mCapacity*sizeof(BpHandle)); + scratchAllocator->free(mData); + mData = newDataArray; + mCapacity *= 2; +} + +static PX_FORCE_INLINE int intersect2D(const BoxYZ& a, const BoxYZ& b) +{ + const bool b0 = b.mMaxY < a.mMinY; + const bool b1 = a.mMaxY < b.mMinY; + const bool b2 = b.mMaxZ < a.mMinZ; + const bool b3 = a.mMaxZ < b.mMinZ; +// const bool b4 = b0 || b1 || b2 || b3; + const bool b4 = b0 | b1 | b2 | b3; + return !b4; +} + +void addPair(const BpHandle id0, const BpHandle id1, PxcScratchAllocator* scratchAllocator, SapPairManager& pairManager, DataArray& dataArray) +{ + const BroadPhasePair* UP = reinterpret_cast(pairManager.AddPair(id0, id1, SapPairManager::PAIR_UNKNOWN)); + + //If the hash table has reached its limit then we're unable to add a new pair. + if(NULL==UP) + return; + + PX_ASSERT(UP); + if(pairManager.IsUnknown(UP)) + { + pairManager.ClearState(UP); + pairManager.SetInArray(UP); + dataArray.AddData(pairManager.GetPairIndex(UP), scratchAllocator); + pairManager.SetNew(UP); + } + pairManager.ClearRemoved(UP); +} + +void removePair(BpHandle id0, BpHandle id1, PxcScratchAllocator* scratchAllocator, SapPairManager& pairManager, DataArray& dataArray) +{ + const BroadPhasePair* UP = reinterpret_cast(pairManager.FindPair(id0, id1)); + if(UP) + { + if(!pairManager.IsInArray(UP)) + { + pairManager.SetInArray(UP); + dataArray.AddData(pairManager.GetPairIndex(UP), scratchAllocator); + } + pairManager.SetRemoved(UP); + } +} + +struct AddPairParams +{ + AddPairParams(const PxU32* remap0, const PxU32* remap1, PxcScratchAllocator* alloc, SapPairManager* pm, DataArray* da) : + mRemap0 (remap0), + mRemap1 (remap1), + mScratchAllocator (alloc), + mPairManager (pm), + mDataArray (da) + { + } + + const PxU32* mRemap0; + const PxU32* mRemap1; + PxcScratchAllocator* mScratchAllocator; + SapPairManager* mPairManager; + DataArray* mDataArray; +}; + +static void addPair(const AddPairParams* PX_RESTRICT params, const BpHandle id0_, const BpHandle id1_) +{ + SapPairManager& pairManager = *params->mPairManager; + + const BroadPhasePair* UP = reinterpret_cast(pairManager.AddPair(params->mRemap0[id0_], params->mRemap1[id1_], SapPairManager::PAIR_UNKNOWN)); + + //If the hash table has reached its limit then we're unable to add a new pair. + if(NULL==UP) + return; + + PX_ASSERT(UP); + if(pairManager.IsUnknown(UP)) + { + pairManager.ClearState(UP); + pairManager.SetInArray(UP); + params->mDataArray->AddData(pairManager.GetPairIndex(UP), params->mScratchAllocator); + pairManager.SetNew(UP); + } + pairManager.ClearRemoved(UP); +} + +// PT: TODO: use SIMD + +AuxData::AuxData(PxU32 nb, const SapBox1D*const* PX_RESTRICT boxes, const BpHandle* PX_RESTRICT indicesSorted, const Bp::FilterGroup::Enum* PX_RESTRICT groupIds) +{ + // PT: TODO: use scratch allocator / etc + BoxX* PX_RESTRICT boxX = reinterpret_cast(PX_ALLOC(sizeof(BoxX)*(nb+1), PX_DEBUG_EXP("mBoxX"))); + BoxYZ* PX_RESTRICT boxYZ = reinterpret_cast(PX_ALLOC(sizeof(BoxYZ)*nb, PX_DEBUG_EXP("mBoxYZ"))); + Bp::FilterGroup::Enum* PX_RESTRICT groups = reinterpret_cast(PX_ALLOC(sizeof(Bp::FilterGroup::Enum)*nb, PX_DEBUG_EXP("mGroups"))); + PxU32* PX_RESTRICT remap = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nb, PX_DEBUG_EXP("mRemap"))); + + mBoxX = boxX; + mBoxYZ = boxYZ; + mGroups = groups; + mRemap = remap; + mNb = nb; + + const PxU32 axis0 = 0; + const PxU32 axis1 = 2; + const PxU32 axis2 = 1; + + const SapBox1D* PX_RESTRICT boxes0 = boxes[axis0]; + const SapBox1D* PX_RESTRICT boxes1 = boxes[axis1]; + const SapBox1D* PX_RESTRICT boxes2 = boxes[axis2]; + + for(PxU32 i=0;imNb; + if(!nb) + return; + + DataArray da(dataArray, dataArraySize, dataArrayCapacity); + + START_STATS + { + BoxX* boxX = auxData->mBoxX; + BoxYZ* boxYZ = auxData->mBoxYZ; + Bp::FilterGroup::Enum* groups = auxData->mGroups; + PxU32* remap = auxData->mRemap; + + AddPairParams params(remap, remap, scratchAllocator, &pairManager, &da); + + PxU32 runningIndex = 0; + PxU32 index0 = 0; + + while(runningIndex(&boxYZ[index0].mMinY)); + b = _mm_shuffle_epi32(b, 78); + const __m128i a = _mm_loadu_si128(reinterpret_cast(&boxYZ[index1].mMinY)); + const __m128i d = _mm_cmpgt_epi32(a, b); + const int mask = _mm_movemask_epi8(d); + if(mask==0x0000ff00)*/ + { + INCREASE_STATS_NB_PAIRS + addPair(¶ms, index0, index1); + } + } + index1++; + } + index0++; + } + } + DUMP_STATS + + dataArray = da.mData; + dataArraySize = da.mSize; + dataArrayCapacity = da.mCapacity; +} + +template +static void bipartitePruning( + const PxU32 nb0, const BoxX* PX_RESTRICT boxX0, const BoxYZ* PX_RESTRICT boxYZ0, const PxU32* PX_RESTRICT remap0, const Bp::FilterGroup::Enum* PX_RESTRICT groups0, + const PxU32 nb1, const BoxX* PX_RESTRICT boxX1, const BoxYZ* PX_RESTRICT boxYZ1, const PxU32* PX_RESTRICT remap1, const Bp::FilterGroup::Enum* PX_RESTRICT groups1, +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + const bool* lut, +#endif + PxcScratchAllocator* scratchAllocator, SapPairManager& pairManager, DataArray& dataArray + ) +{ + AddPairParams params(remap0, remap1, scratchAllocator, &pairManager, &dataArray); + + PxU32 runningIndex = 0; + PxU32 index0 = 0; + + while(runningIndexmNb; + const PxU32 nb1 = auxData1->mNb; + + if(!nb0 || !nb1) + return; + + DataArray da(dataArray, dataArraySize, dataArrayCapacity); + + START_STATS + { + const BoxX* boxX0 = auxData0->mBoxX; + const BoxYZ* boxYZ0 = auxData0->mBoxYZ; + const Bp::FilterGroup::Enum* groups0 = auxData0->mGroups; + const PxU32* remap0 = auxData0->mRemap; + + const BoxX* boxX1 = auxData1->mBoxX; + const BoxYZ* boxYZ1 = auxData1->mBoxYZ; + const Bp::FilterGroup::Enum* groups1 = auxData1->mGroups; + const PxU32* remap1 = auxData1->mRemap; +#ifdef BP_FILTERING_USES_TYPE_IN_GROUP + bipartitePruning<0>(nb0, boxX0, boxYZ0, remap0, groups0, nb1, boxX1, boxYZ1, remap1, groups1, lut, scratchAllocator, pairManager, da); + bipartitePruning<1>(nb1, boxX1, boxYZ1, remap1, groups1, nb0, boxX0, boxYZ0, remap0, groups0, lut, scratchAllocator, pairManager, da); +#else + bipartitePruning<0>(nb0, boxX0, boxYZ0, remap0, groups0, nb1, boxX1, boxYZ1, remap1, groups1, scratchAllocator, pairManager, da); + bipartitePruning<1>(nb1, boxX1, boxYZ1, remap1, groups1, nb0, boxX0, boxYZ0, remap0, groups0, scratchAllocator, pairManager, da); +#endif + } + DUMP_STATS + + dataArray = da.mData; + dataArraySize = da.mSize; + dataArrayCapacity = da.mCapacity; +} + +} //namespace Bp + +} //namespace physx + diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h new file mode 100644 index 000000000..30c2366fd --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h @@ -0,0 +1,285 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_SAP_AUX_H +#define BP_BROADPHASE_SAP_AUX_H + +#include "foundation/PxAssert.h" +#include "CmPhysXCommon.h" +#include "PsIntrinsics.h" +#include "PsUserAllocated.h" +#include "BpBroadPhase.h" +#include "BpBroadPhaseUpdate.h" +#include "CmBitMap.h" +#include "GuAxes.h" +#include "PxcScratchAllocator.h" + +namespace physx +{ +namespace Bp +{ +#define NUM_SENTINELS 2 + +#define BP_SAP_USE_PREFETCH 1//prefetch in batchUpdate + +#define BP_SAP_USE_OVERLAP_TEST_ON_REMOVES 1// "Useless" but faster overall because seriously reduces number of calls (from ~10000 to ~3 sometimes!) + +//Set 1 to test for group ids in batchCreate/batchUpdate so we can avoid group id test in ComputeCreatedDeletedPairsLists +//Set 0 to neglect group id test in batchCreate/batchUpdate and delay test until ComputeCreatedDeletedPairsLists +#define BP_SAP_TEST_GROUP_ID_CREATEUPDATE 1 + +#define MAX_BP_HANDLE 0x3fffffff +#define PX_REMOVED_BP_HANDLE 0x3ffffffd +#define MAX_BP_PAIRS_MESSAGE "Only 4294967296 broadphase pairs are supported. This limit has been exceeded and some pairs will be dropped \n" + +PX_FORCE_INLINE void setMinSentinel(ValType& v, BpHandle& d) +{ + v = 0x00000000;//0x00800000; //0x00800000 is -FLT_MAX but setting it to 0 means we don't crash when we get a value outside the float range. + d = (BP_INVALID_BP_HANDLE & ~1); +} + +PX_FORCE_INLINE void setMaxSentinel(ValType& v, BpHandle& d) +{ + v = 0xffffffff;//0xff7fffff; //0xff7fffff is +FLT_MAX but setting it to 0xffffffff means we don't crash when we get a value outside the float range. + d = BP_INVALID_BP_HANDLE; +} + +PX_FORCE_INLINE BpHandle setData(PxU32 owner_box_id, const bool is_max) +{ + BpHandle d = BpHandle(owner_box_id<<1); + if(is_max) d |= 1; + return d; +} + +PX_FORCE_INLINE bool isSentinel(const BpHandle& d) +{ + return (d&~1)==(BP_INVALID_BP_HANDLE & ~1); +} + +PX_FORCE_INLINE BpHandle isMax(const BpHandle& d) +{ + return BpHandle(d & 1); +} + +PX_FORCE_INLINE BpHandle getOwner(const BpHandle& d) +{ + return BpHandle(d>>1); +} + +class SapBox1D +{ +public: + + PX_FORCE_INLINE SapBox1D() {} + PX_FORCE_INLINE ~SapBox1D() {} + + BpHandle mMinMax[2];//mMinMax[0]=min, mMinMax[1]=max +}; + +class SapPairManager +{ +public: + SapPairManager(); + ~SapPairManager(); + + void init(const PxU32 size); + void release(); + + void shrinkMemory(); + + const BroadPhasePair* AddPair (BpHandle id0, BpHandle id1, const PxU8 state); + bool RemovePair (BpHandle id0, BpHandle id1); + bool RemovePairs (const Cm::BitMap& removedAABBs); + const BroadPhasePair* FindPair (BpHandle id0, BpHandle id1) const; + + PX_FORCE_INLINE PxU32 GetPairIndex(const BroadPhasePair* PX_RESTRICT pair) const + { + return (PxU32((size_t(pair) - size_t(mActivePairs)))/sizeof(BroadPhasePair)); + } + + BpHandle* mHashTable; + BpHandle* mNext; + PxU32 mHashSize; + PxU32 mHashCapacity; + PxU32 mMinAllowedHashCapacity; + BroadPhasePair* mActivePairs; + PxU8* mActivePairStates; + PxU32 mNbActivePairs; + PxU32 mActivePairsCapacity; + PxU32 mMask; + + BroadPhasePair* FindPair (BpHandle id0, BpHandle id1, PxU32 hash_value) const; + void RemovePair (BpHandle id0, BpHandle id1, PxU32 hash_value, PxU32 pair_index); + void reallocPairs(const bool allocRequired); + + enum + { + PAIR_INARRAY=1, + PAIR_REMOVED=2, + PAIR_NEW=4, + PAIR_UNKNOWN=8 + }; + + PX_FORCE_INLINE bool IsInArray(const BroadPhasePair* PX_RESTRICT pair) const + { + const PxU8 state=mActivePairStates[pair-mActivePairs]; + return state & PAIR_INARRAY ? true : false; + } + PX_FORCE_INLINE bool IsRemoved(const BroadPhasePair* PX_RESTRICT pair) const + { + const PxU8 state=mActivePairStates[pair-mActivePairs]; + return state & PAIR_REMOVED ? true : false; + } + PX_FORCE_INLINE bool IsNew(const BroadPhasePair* PX_RESTRICT pair) const + { + const PxU8 state=mActivePairStates[pair-mActivePairs]; + return state & PAIR_NEW ? true : false; + } + PX_FORCE_INLINE bool IsUnknown(const BroadPhasePair* PX_RESTRICT pair) const + { + const PxU8 state=mActivePairStates[pair-mActivePairs]; + return state & PAIR_UNKNOWN ? true : false; + } + + PX_FORCE_INLINE void ClearState(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs]=0; + } + + PX_FORCE_INLINE void SetInArray(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] |= PAIR_INARRAY; + } + PX_FORCE_INLINE void SetRemoved(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] |= PAIR_REMOVED; + } + PX_FORCE_INLINE void SetNew(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] |= PAIR_NEW; + } + PX_FORCE_INLINE void ClearInArray(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] &= ~PAIR_INARRAY; + } + PX_FORCE_INLINE void ClearRemoved(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] &= ~PAIR_REMOVED; + } + PX_FORCE_INLINE void ClearNew(const BroadPhasePair* PX_RESTRICT pair) + { + mActivePairStates[pair-mActivePairs] &= ~PAIR_NEW; + } +}; + +struct DataArray +{ + DataArray(BpHandle* data, PxU32 size, PxU32 capacity) : mData(data), mSize(size), mCapacity(capacity) {} + + BpHandle* mData; + PxU32 mSize; + PxU32 mCapacity; + + PX_NOINLINE void Resize(PxcScratchAllocator* scratchAllocator); + + PX_FORCE_INLINE void AddData(const PxU32 data, PxcScratchAllocator* scratchAllocator) + { + if(mSize==mCapacity) + Resize(scratchAllocator); + + PX_ASSERT(mSize cDir1Min && cDir1Max > bDir1Min && + bDir2Max > cDir2Min && cDir2Max > bDir2Min); +} + +} //namespace Bp + +} //namespace physx + +#endif //BP_BROADPHASE_SAP_AUX_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp new file mode 100644 index 000000000..0fef90132 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp @@ -0,0 +1,246 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "BpBroadPhaseShared.h" +#include "foundation/PxMemory.h" +#include "PsBitUtils.h" + +using namespace physx; +using namespace Bp; + +#define MBP_ALLOC(x) PX_ALLOC(x, "MBP") +#define MBP_FREE(x) if(x) PX_FREE_AND_RESET(x) + +static PX_FORCE_INLINE void storeDwords(PxU32* dest, PxU32 nb, PxU32 value) +{ + while(nb--) + *dest++ = value; +} + +/////////////////////////////////////////////////////////////////////////////// + +PairManagerData::PairManagerData() : + mHashSize (0), + mMask (0), + mNbActivePairs (0), + mHashTable (NULL), + mNext (NULL), + mActivePairs (NULL), + mReservedMemory (0) +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +PairManagerData::~PairManagerData() +{ + purge(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PairManagerData::purge() +{ + MBP_FREE(mNext); + MBP_FREE(mActivePairs); + MBP_FREE(mHashTable); + mHashSize = 0; + mMask = 0; + mNbActivePairs = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PairManagerData::reallocPairs() +{ + MBP_FREE(mHashTable); + mHashTable = reinterpret_cast(MBP_ALLOC(mHashSize*sizeof(PxU32))); + storeDwords(mHashTable, mHashSize, INVALID_ID); + + // Get some bytes for new entries + InternalPair* newPairs = reinterpret_cast(MBP_ALLOC(mHashSize * sizeof(InternalPair))); PX_ASSERT(newPairs); + PxU32* newNext = reinterpret_cast(MBP_ALLOC(mHashSize * sizeof(PxU32))); PX_ASSERT(newNext); + + // Copy old data if needed + if(mNbActivePairs) + PxMemCopy(newPairs, mActivePairs, mNbActivePairs*sizeof(InternalPair)); + // ### check it's actually needed... probably only for pairs whose hash value was cut by the and + // yeah, since hash(id0, id1) is a constant + // However it might not be needed to recompute them => only less efficient but still ok + for(PxU32 i=0;igetId0(), last->getId1()) & mMask; + + // Walk the hash table to fix mNext + PxU32 offset = mHashTable[lastHashValue]; + PX_ASSERT(offset!=INVALID_ID); + + PxU32 previous=INVALID_ID; + while(offset!=lastPairIndex) + { + previous = offset; + offset = mNext[offset]; + } + + // Let us go/jump us + if(previous!=INVALID_ID) + { + PX_ASSERT(mNext[previous]==lastPairIndex); + mNext[previous] = mNext[lastPairIndex]; + } + // else we were the first + else mHashTable[lastHashValue] = mNext[lastPairIndex]; + // we're now free to reuse mNext[lastPairIndex] without breaking the list + +#if PX_DEBUG + mNext[lastPairIndex]=INVALID_ID; +#endif + + // Don't invalidate entry since we're going to shrink the array + + // 2) Re-insert in free slot + mActivePairs[pairIndex] = mActivePairs[lastPairIndex]; +#if PX_DEBUG + PX_ASSERT(mNext[pairIndex]==INVALID_ID); +#endif + mNext[pairIndex] = mHashTable[lastHashValue]; + mHashTable[lastHashValue] = pairIndex; + + mNbActivePairs--; + } + } +} + diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h new file mode 100644 index 000000000..013f7c544 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h @@ -0,0 +1,243 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_BROADPHASE_SHARED_H +#define BP_BROADPHASE_SHARED_H + +#include "BpBroadPhaseUpdate.h" +#include "PsUserAllocated.h" +#include "PsHash.h" +#include "PsVecMath.h" + +namespace physx +{ +namespace Bp +{ + #define INVALID_ID 0xffffffff + #define INVALID_USER_ID 0xffffffff + + struct InternalPair : public Ps::UserAllocated + { + PX_FORCE_INLINE PxU32 getId0() const { return id0_isNew & ~PX_SIGN_BITMASK; } + PX_FORCE_INLINE PxU32 getId1() const { return id1_isUpdated & ~PX_SIGN_BITMASK; } + + PX_FORCE_INLINE PxU32 isNew() const { return id0_isNew & PX_SIGN_BITMASK; } + PX_FORCE_INLINE PxU32 isUpdated() const { return id1_isUpdated & PX_SIGN_BITMASK; } + + PX_FORCE_INLINE void setNewPair(PxU32 id0, PxU32 id1) + { + PX_ASSERT(!(id0 & PX_SIGN_BITMASK)); + PX_ASSERT(!(id1 & PX_SIGN_BITMASK)); + id0_isNew = id0 | PX_SIGN_BITMASK; + id1_isUpdated = id1; + } + PX_FORCE_INLINE void setUpdated() { id1_isUpdated |= PX_SIGN_BITMASK; } + PX_FORCE_INLINE void clearUpdated() { id1_isUpdated &= ~PX_SIGN_BITMASK; } + PX_FORCE_INLINE void clearNew() { id0_isNew &= ~PX_SIGN_BITMASK; } + + protected: + PxU32 id0_isNew; + PxU32 id1_isUpdated; + }; + + PX_FORCE_INLINE bool differentPair(const InternalPair& p, PxU32 id0, PxU32 id1) { return (id0!=p.getId0()) || (id1!=p.getId1()); } + PX_FORCE_INLINE PxU32 hash(PxU32 id0, PxU32 id1) { return PxU32(Ps::hash( (id0&0xffff)|(id1<<16)) ); } + PX_FORCE_INLINE void sort(PxU32& id0, PxU32& id1) { if(id0>id1) Ps::swap(id0, id1); } + + class PairManagerData + { + public: + PairManagerData(); + ~PairManagerData(); + + PX_FORCE_INLINE PxU32 getPairIndex(const InternalPair* pair) const + { + return (PxU32((size_t(pair) - size_t(mActivePairs)))/sizeof(InternalPair)); + } + + // Internal version saving hash computation + PX_FORCE_INLINE InternalPair* findPair(PxU32 id0, PxU32 id1, PxU32 hashValue) const + { + if(!mHashTable) + return NULL; // Nothing has been allocated yet + + InternalPair* PX_RESTRICT activePairs = mActivePairs; + const PxU32* PX_RESTRICT next = mNext; + + // Look for it in the table + PxU32 offset = mHashTable[hashValue]; + while(offset!=INVALID_ID && differentPair(activePairs[offset], id0, id1)) + { + PX_ASSERT(activePairs[offset].getId0()!=INVALID_USER_ID); + offset = next[offset]; // Better to have a separate array for this + } + if(offset==INVALID_ID) + return NULL; + PX_ASSERT(offset the pair is persistent + + return &activePairs[offset]; + } + + PX_FORCE_INLINE InternalPair* addPairInternal(PxU32 id0, PxU32 id1) + { + // Order the ids + sort(id0, id1); + + const PxU32 fullHashValue = hash(id0, id1); + PxU32 hashValue = fullHashValue & mMask; + + { + InternalPair* PX_RESTRICT p = findPair(id0, id1, hashValue); + if(p) + { + p->setUpdated(); + return p; // Persistent pair + } + } + + // This is a new pair + if(mNbActivePairs >= mHashSize) + hashValue = growPairs(fullHashValue); + + const PxU32 pairIndex = mNbActivePairs++; + + InternalPair* PX_RESTRICT p = &mActivePairs[pairIndex]; + p->setNewPair(id0, id1); + mNext[pairIndex] = mHashTable[hashValue]; + mHashTable[hashValue] = pairIndex; + return p; + } + + PxU32 mHashSize; + PxU32 mMask; + PxU32 mNbActivePairs; + PxU32* mHashTable; + PxU32* mNext; + InternalPair* mActivePairs; + PxU32 mReservedMemory; + + void purge(); + void reallocPairs(); + void shrinkMemory(); + void reserveMemory(PxU32 memSize); + PX_NOINLINE PxU32 growPairs(PxU32 fullHashValue); + void removePair(PxU32 id0, PxU32 id1, PxU32 hashValue, PxU32 pairIndex); + }; + + struct AABB_Xi : public Ps::UserAllocated + { + PX_FORCE_INLINE AABB_Xi() {} + PX_FORCE_INLINE ~AABB_Xi() {} + + PX_FORCE_INLINE void initFromFloats(const void* PX_RESTRICT minX, const void* PX_RESTRICT maxX) + { + mMinX = encodeFloat(*reinterpret_cast(minX)); + mMaxX = encodeFloat(*reinterpret_cast(maxX)); + } + + PX_FORCE_INLINE void initFromPxVec4(const PxVec4& min, const PxVec4& max) + { + initFromFloats(&min.x, &max.x); + } + + PX_FORCE_INLINE void operator = (const AABB_Xi& box) + { + mMinX = box.mMinX; + mMaxX = box.mMaxX; + } + + PX_FORCE_INLINE void initSentinel() + { + mMinX = 0xffffffff; + } + + PX_FORCE_INLINE bool isSentinel() const + { + return mMinX == 0xffffffff; + } + + PxU32 mMinX; + PxU32 mMaxX; + }; + + struct AABB_YZn : public Ps::UserAllocated + { + PX_FORCE_INLINE AABB_YZn() {} + PX_FORCE_INLINE ~AABB_YZn() {} + + PX_FORCE_INLINE void initFromPxVec4(const PxVec4& min, const PxVec4& max) + { + mMinY = -min.y; + mMinZ = -min.z; + mMaxY = max.y; + mMaxZ = max.z; + } + + PX_FORCE_INLINE void operator = (const AABB_YZn& box) + { + using namespace physx::shdfnd::aos; + V4StoreA(V4LoadA(&box.mMinY), &mMinY); + } + + float mMinY; + float mMinZ; + float mMaxY; + float mMaxZ; + }; + + struct AABB_YZr : public Ps::UserAllocated + { + PX_FORCE_INLINE AABB_YZr() {} + PX_FORCE_INLINE ~AABB_YZr() {} + + PX_FORCE_INLINE void initFromPxVec4(const PxVec4& min, const PxVec4& max) + { + mMinY = min.y; + mMinZ = min.z; + mMaxY = max.y; + mMaxZ = max.z; + } + + PX_FORCE_INLINE void operator = (const AABB_YZr& box) + { + using namespace physx::shdfnd::aos; + V4StoreA(V4LoadA(&box.mMinY), &mMinY); + } + + float mMinY; + float mMinZ; + float mMaxY; + float mMaxZ; + }; + +} //namespace Bp +} //namespace physx + +#endif // BP_BROADPHASE_SHARED_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp new file mode 100644 index 000000000..5504ce468 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp @@ -0,0 +1,29 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h new file mode 100644 index 000000000..21f518516 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h @@ -0,0 +1,105 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_MBP_TASKS_H +#define BP_MBP_TASKS_H + +#include "PsUserAllocated.h" +#include "CmTask.h" + +namespace physx +{ + class PxcScratchAllocator; + + namespace Bp + { + class BroadPhaseMBP; + } + +#define MBP_USE_SCRATCHPAD + + class MBPTask : public Cm::Task, public shdfnd::UserAllocated + { + public: + MBPTask(PxU64 contextId) : Cm::Task(contextId), mMBP(NULL), mNumCpuTasks(0) {} + + PX_FORCE_INLINE void set(Bp::BroadPhaseMBP* mbp, PxcScratchAllocator* sa, PxU32 numCpuTasks) + { + mMBP = mbp; + mScratchAllocator = sa; + mNumCpuTasks = numCpuTasks; + } + protected: + Bp::BroadPhaseMBP* mMBP; + PxU32 mNumCpuTasks; + PxcScratchAllocator* mScratchAllocator; + private: + MBPTask& operator=(const MBPTask&); + }; + + // PT: this is the main 'update' task doing the actual box pruning work. + class MBPUpdateWorkTask : public MBPTask + { + public: + MBPUpdateWorkTask(PxU64 contextId) : MBPTask(contextId) {} + ~MBPUpdateWorkTask() {} + // PxBaseTask + virtual const char* getName() const { return "BpMBP.updateWork"; } + //~PxBaseTask + + // Cm::Task + virtual void runInternal(); + //~Cm::Task + + private: + MBPUpdateWorkTask& operator=(const MBPUpdateWorkTask&); + }; + + // PT: this task runs after MBPUpdateWorkTask. This is where MBP_PairManager::removeMarkedPairs is called, to finalize + // the work and come up with created/removed lists. This is single-threaded. + class MBPPostUpdateWorkTask : public MBPTask + { + public: + MBPPostUpdateWorkTask(PxU64 contextId) : MBPTask(contextId) {} + ~MBPPostUpdateWorkTask() {} + // PxBaseTask + virtual const char* getName() const { return "BpMBP.postUpdateWork"; } + //~PxBaseTask + + // Cm::Task + virtual void runInternal(); + //~Cm::Task + + private: + MBPPostUpdateWorkTask& operator=(const MBPPostUpdateWorkTask&); + }; + +} //namespace physx + +#endif // BP_MBP_TASKS_H diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp new file mode 100644 index 000000000..2735614b6 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "BpSAPTasks.h" +#include "BpBroadPhaseSap.h" +#include "PsTime.h" + +namespace physx +{ + +namespace Bp +{ + +/////////////////////////////////////////////////////////////////////////////// + + +// #define DUMP_TOTAL_SAP_TIME +// 256 convex stacks: from ~13000 down to ~2000 +// pot pourri box: from ~4000 to ~700 +// boxes: ~3400 to ~4000 + +#ifdef DUMP_TOTAL_SAP_TIME + static PxU64 gStartTime = shdfnd::Time::getCurrentCounterValue(); +#endif + +void SapUpdateWorkTask::runInternal() +{ + mSAP->update(); +} + +void SapPostUpdateWorkTask::runInternal() +{ + mSAP->postUpdate(); +#ifdef DUMP_TOTAL_SAP_TIME + PxU64 endTime = shdfnd::Time::getCurrentCounterValue(); + printf("SAP Time: %" PX_PRIu64 "\n", endTime - gStartTime); +#endif +} + +} //namespace Bp + +} //namespace physx diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h new file mode 100644 index 000000000..f9b0523b4 --- /dev/null +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BP_SAP_TASKS_H +#define BP_SAP_TASKS_H + +#include "CmTask.h" + +namespace physx +{ + +namespace Bp +{ + + class BroadPhaseSap; + + class SapUpdateWorkTask: public Cm::Task + { + public: + + SapUpdateWorkTask(PxU64 contextId) : Cm::Task(contextId) + { + } + + void setBroadPhase(BroadPhaseSap* sap) + { + mSAP = sap; + } + + void set(const PxU32 numCpuTasks) + { + mNumCpuTasks = numCpuTasks; + } + + virtual void runInternal(); + + virtual const char* getName() const { return "BpSAP.updateWork"; } + + private: + + BroadPhaseSap* mSAP; + PxU32 mNumCpuTasks; + }; + + class SapPostUpdateWorkTask: public Cm::Task + { + public: + + SapPostUpdateWorkTask(PxU64 contextId) : Cm::Task(contextId) + { + } + + void setBroadPhase(BroadPhaseSap* sap) + { + mSAP = sap; + } + + void set(const PxU32 numCpuTasks) + { + mNumCpuTasks=numCpuTasks; + } + + virtual void runInternal(); + + virtual const char* getName() const { return "BpSAP.postUpdateWork"; } + + private: + + BroadPhaseSap* mSAP; + PxU32 mNumCpuTasks; + }; + +} //namespace Bp + +} //namespace physx + +#endif // BP_SAP_TASKS_H diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h new file mode 100644 index 000000000..e653067f2 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h @@ -0,0 +1,284 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_ARTICULATION_H +#define PXD_ARTICULATION_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsVecMath.h" +#include "CmUtils.h" +#include "CmSpatialVector.h" +#include "PxArticulationJoint.h" +#include "DyVArticulation.h" + +namespace physx +{ + class PxConstraintAllocator; + + +#define DY_DEBUG_ARTICULATION 0 + +namespace Dy +{ + struct FsInertia; + struct FsData; + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +PX_ALIGN_PREFIX(64) + +class Articulation : public ArticulationV +{ +public: + // public interface + + Articulation(Sc::ArticulationSim*); + ~Articulation(); + + virtual bool resize(const PxU32 linkCount); + + virtual void onUpdateSolverDesc() + { + PxMemZero(mExternalLoads.begin(), sizeof(Ps::aos::Mat33V) * mExternalLoads.size()); + PxMemZero(mInternalLoads.begin(), sizeof(Ps::aos::Mat33V) * mExternalLoads.size()); + } + + FsData* getFsDataPtr() const { return reinterpret_cast(mFsDataBytes.begin()); } + //void setFsDataPtr(FsData* data) { mFsData = data; } + + // get data sizes for allocation at higher levels + virtual void getDataSizes(PxU32 linkCount, + PxU32 &solverDataSize, + PxU32& totalSize, + PxU32& scratchSize); + + virtual void getImpulseResponse( + PxU32 linkID, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaV) const; + + virtual void getImpulseSelfResponse( + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1) const; + + virtual Cm::SpatialVectorV getLinkVelocity(const PxU32 linkID) const; + + virtual Cm::SpatialVectorV getLinkMotionVector(const PxU32 linkID) const; + + //this is called by island gen to determine whether the articulation should be awake or sleep + virtual Cm::SpatialVector getMotionVelocity(const PxU32 linkID) const; + + virtual PxReal getLinkMaxPenBias(const PxU32 linkID) const; + + + static PxU32 computeUnconstrainedVelocities( + const ArticulationSolverDesc& desc, + PxReal dt, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, PxU64 contextID, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + static void computeUnconstrainedVelocitiesTGS( + const ArticulationSolverDesc& desc, + PxReal dt, const PxVec3& gravity, + PxU64 contextID, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + static PxU32 setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* /*Z*/); + + static void saveVelocity(const ArticulationSolverDesc& d, Cm::SpatialVectorF* deltaV); + + static void saveVelocityTGS(const ArticulationSolverDesc& d, PxReal invDtF32); + + static void updateBodies(const ArticulationSolverDesc& desc, PxReal dt); + + static void recordDeltaMotion(const ArticulationSolverDesc &desc, const PxReal dt, Cm::SpatialVectorF* deltaV); + + static void deltaMotionToMotionVelocity(const ArticulationSolverDesc& desc, PxReal invDt); + + virtual void pxcFsApplyImpulse(PxU32 linkID, Ps::aos::Vec3V linear, + Ps::aos::Vec3V angular, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + virtual void pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, + const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, + const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + virtual void solveInternalConstraints(const PxReal dt, const PxReal invDt, Cm::SpatialVectorF* impulses, Cm::SpatialVectorF* DeltaV, + bool velIteration); + + virtual Cm::SpatialVectorV pxcFsGetVelocity(PxU32 linkID); + virtual void pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1); + + virtual Cm::SpatialVectorV pxcFsGetVelocityTGS(PxU32 linkID) { return Articulation::pxcFsGetVelocity(linkID); } + + virtual const PxTransform& getCurrentTransform(PxU32 linkID)const + { + return mPose[linkID]; + } + + virtual const PxQuat& getDeltaQ(PxU32 linkID) const + { + return mDeltaQ[linkID]; + } + + static void prepareDataBlock(FsData& fsData, + const ArticulationLink* links, + PxU16 linkCount, + PxTransform* poses, + PxQuat* deltaQ, + FsInertia* baseInertia, + ArticulationJointTransforms* jointTransforms, + PxU32 expectedSize); + + static void prepareFsData(FsData& fsData, + const ArticulationLink* links); + + static PxReal getResistance(PxReal compliance); + + static PxU32 getFsDataSize(PxU32 linkCount); + + static PxU32 getLtbDataSize(PxU32 linkCount); + + static void setInertia(FsInertia& inertia, + const PxsBodyCore& body, + const PxTransform& pose); + + static void setJointTransforms(ArticulationJointTransforms& transforms, + const PxTransform& parentPose, + const PxTransform& childPose, + const ArticulationJointCore& joint); + + static void applyImpulses(const FsData& matrix, + Cm::SpatialVectorV* Z, + Cm::SpatialVectorV* V); + +private: + +#if DY_DEBUG_ARTICULATION + // debug quantities + + Cm::SpatialVector computeMomentum(const FsInertia *inertia) const; + void computeResiduals(const Cm::SpatialVector *, + const ArticulationJointTransforms* jointTransforms, + bool dump = false) const; + void checkLimits() const; +#endif + + //PX_FORCE_INLINE Cm::SpatialVectorV* getVelocity(FsData& matrix); + + void computeUnconstrainedVelocitiesInternal(const ArticulationSolverDesc& desc, + PxReal dt, + const PxVec3& gravity, PxU64 contextID, + FsInertia* PX_RESTRICT baseInertia, + ArticulationJointTransforms* PX_RESTRICT jointTransforms, + PxcFsScratchAllocator& allocator); + + void prepareLtbMatrix(FsData& fsData, + const FsInertia* baseInertia, + const PxTransform* poses, + const ArticulationJointTransforms* jointTransforms, + PxReal recipDt); + + void computeJointDrives(FsData& fsData, + Ps::aos::Vec3V* drives, + const ArticulationLink* links, + const PxTransform* poses, + const ArticulationJointTransforms* transforms, + const Ps::aos::Mat33V* loads, + PxReal dt); + + mutable Ps::Array mFsDataBytes; // drive cache creation (which is const) can force a resize + + // persistent state of the articulation for warm-starting joint load computation + Ps::Array mInternalLoads; + Ps::Array mExternalLoads; + Ps::Array mScratchMemory; // drive cache creation (which is const) can force a resize + Ps::Array mPose; + Ps::Array mDeltaQ; + Ps::Array mMotionVelocity; // saved here in solver to communicate back to island management/sleeping + +} PX_ALIGN_SUFFIX(64); + +#if PX_VC + #pragma warning(pop) +#endif + +class PxvArticulationDriveCache +{ +public: + // drive cache stuff + static void initialize( + FsData &cache, + PxU16 linkCount, + const ArticulationLink* links, + PxReal compliance, + PxU32 iterations, + char* scratchMemory, + PxU32 scratchMemorySize); + + static PxU32 getLinkCount(const FsData& cache); + + static void applyImpulses(const FsData& cache, + Cm::SpatialVectorV* Z, + Cm::SpatialVectorV* V); + + static void getImpulseResponse(const FsData& cache, + PxU32 linkID, + const Cm::SpatialVectorV& impulse, + Cm::SpatialVectorV& deltaV); +}; + +void PxvRegisterArticulations(); + +} + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h new file mode 100644 index 000000000..42e9dfdec --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXDV_ARTICULATION_CORE_H +#define PXDV_ARTICULATION_CORE_H + +#include "PxArticulationReducedCoordinate.h" + +namespace physx +{ + namespace Dy + { + struct ArticulationCore + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxU32 internalDriveIterations; + PxU32 externalDriveIterations; + PxU32 maxProjectionIterations; + PxU16 solverIterationCounts; //KS - made a U16 so that it matches PxsRigidCore + PxReal separationTolerance; + PxReal sleepThreshold; + PxReal freezeThreshold; + PxReal wakeCounter; + PxArticulationFlags flags; + }; + + struct ArticulationJointCoreDirtyFlag + { + enum Enum + { + eNONE = 0, + eMOTION = 1 << 0, + ePOSE = 1 << 1, + eTARGETPOSE = 1 << 2, + eTARGETVELOCITY = 1 << 3 + }; + }; + + typedef PxFlags ArticulationJointCoreDirtyFlags; + PX_FLAGS_OPERATORS(ArticulationJointCoreDirtyFlag::Enum, PxU8) + } +} + +#endif + diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h new file mode 100644 index 000000000..724b5ff7d --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h @@ -0,0 +1,144 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DV_ARTICULATION_JOINT_CORE_H +#define DV_ARTICULATION_JOINT_CORE_H + +#include "DyArticulationCore.h" +#include "PxArticulationJoint.h" + +namespace physx +{ + namespace Dy + { + + struct ArticulationLimit + { + PxReal low, high; + }; + + struct ArticulationDrive + { + PxReal stiffness, damping, maxForce; + bool isAcceleration; + }; + + struct ArticulationJointCoreBase + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool setJointPose() + { + if (dirtyFlag & ArticulationJointCoreDirtyFlag::ePOSE) + { + relativeQuat = (childPose.q * (parentPose.q.getConjugate())).getNormalized(); + + //ML: this way work in GPU + PxU8 flag = PxU8(ArticulationJointCoreDirtyFlag::ePOSE); + dirtyFlag &= ArticulationJointCoreDirtyFlags(~flag); + + return true; + } + + return false; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator=(ArticulationJointCoreBase& other) + { + parentPose = other.parentPose; + childPose = other.childPose; + + dirtyFlag = other.dirtyFlag; + + prismaticLimited = other.prismaticLimited; + + //KS - temp place to put reduced coordinate limit and drive values + for (PxU32 i = 0; i < 6; ++i) + { + limits[i] = other.limits[i]; + drives[i] = other.drives[i]; + targetP[i] = other.targetP[i]; + targetV[i] = other.targetV[i]; + + dofIds[i] = other.dofIds[i]; + motion[i] = other.motion[i]; + } + + frictionCoefficient = other.frictionCoefficient; + relativeQuat = other.relativeQuat; + jointType = other.jointType; + jointOffset = other.jointOffset; //this is the dof offset for the joint in the cache + + } + + // attachment points, don't change the order, otherwise it will break GPU code + PxTransform parentPose; //28 28 + PxTransform childPose; //28 56 + + //KS - temp place to put reduced coordinate limit and drive values + ArticulationLimit limits[6]; //48 104 + ArticulationDrive drives[6]; //96 200 + PxReal targetP[6]; //24 224 + PxReal targetV[6]; //24 248 + + // initial parent to child rotation + PxQuat relativeQuat; //16 264 + PxReal frictionCoefficient; //4 268 + //this is the dof offset for the joint in the cache + PxU32 jointOffset; //4 272 + + PxU8 dofIds[6]; //6 278 + PxArticulationMotions motion[6]; //6 284 + + PxReal maxJointVelocity; //4 288 + + ArticulationJointCoreDirtyFlags dirtyFlag; //1 289 + bool prismaticLimited; //1 290 + PxU8 jointType; //1 291 + PxU8 pad[13]; //13 304 + + + + ArticulationJointCoreBase() { maxJointVelocity = 100.f; } + // PX_SERIALIZATION + ArticulationJointCoreBase(const PxEMPTY&) {} + //~PX_SERIALIZATION + + }; + } +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h new file mode 100644 index 000000000..67f05583b --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h @@ -0,0 +1,89 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_SHADER_H +#define PXD_SHADER_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "PxvConfig.h" +#include "PxvDynamics.h" +#include "PxConstraint.h" +#include "DyConstraintWriteBack.h" + +namespace physx +{ + +class PxsRigidBody; + +namespace Dy +{ + + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif +PX_ALIGN_PREFIX(16) +struct Constraint +{ +public: + + PxReal linBreakForce; //0 + PxReal angBreakForce; //4 + PxU16 constantBlockSize; //6 + PxU16 flags; //8 + + PxConstraintSolverPrep solverPrep; //12 + PxConstraintProject project; //16 + void* constantBlock; //20 + + PxsRigidBody* body0; //24 + PxsRigidBody* body1; //28 + + PxsBodyCore* bodyCore0; //32 + PxsBodyCore* bodyCore1; //36 + PxU32 index; //40 //this is also a constraint write back index + PxReal minResponseThreshold; //44 +} +PX_ALIGN_SUFFIX(16); +#if PX_VC + #pragma warning(pop) +#endif + +#if !PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(48==sizeof(Constraint)); +#endif + +} + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h new file mode 100644 index 000000000..ca5d889a3 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h @@ -0,0 +1,65 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_CONSTRAINT_WRITE_BACK_H +#define PXD_CONSTRAINT_WRITE_BACK_H + +#include "foundation/PxVec3.h" +#include "PxvConfig.h" +#include "PxvDynamics.h" + +namespace physx +{ + namespace Dy + { + + PX_ALIGN_PREFIX(16) + struct ConstraintWriteback + { + public: + + void initialize() + { + linearImpulse = PxVec3(0); + angularImpulse = PxVec3(0); + broken = false; + } + + PxVec3 linearImpulse; + PxU32 broken; + PxVec3 angularImpulse; + PxU32 pad; + } + PX_ALIGN_SUFFIX(16); + + } +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyContext.h b/src/PhysX/physx/source/lowleveldynamics/include/DyContext.h new file mode 100644 index 000000000..b16d0feda --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyContext.h @@ -0,0 +1,400 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXV_DYNAMICS_CONTEXT_H +#define PXV_DYNAMICS_CONTEXT_H + +#include "CmPhysXCommon.h" +#include "PxSceneDesc.h" +#include "DyThresholdTable.h" +#include "PxcNpThreadContext.h" +#include "PxsSimulationController.h" +#include "DyConstraintWriteBack.h" +#include "PsAllocator.h" + +#define DY_MAX_VELOCITY_COUNT 4 + +namespace physx +{ + +class PxsIslandManager; +class PxcNpMemBlockPool; + +namespace Cm +{ + class EventProfiler; + class FlushPool; +} + +namespace IG +{ + class SimpleIslandManager; + class IslandSim; +} + +template class PxcThreadCoherentCache; +class PxcScratchAllocator; +struct PxvSimStats; +class PxTaskManager; +class PxcNpMemBlockPool; +struct PxgDynamicsMemoryConfig; +class PxsContactManagerOutputIterator; +struct PxsContactManagerOutput; +class PxsKernelWranglerManager; +class PxsHeapMemoryAllocator; +class PxsMemoryManager; +class PxsContactManager; + + +namespace Dy +{ + + +class Context +{ + PX_NOCOPY(Context) +public: + /** + \brief Returns the bounce threshold + \return The bounce threshold. + */ + PX_FORCE_INLINE PxReal getBounceThreshold() const { return mBounceThreshold; } + /** + \brief Returns the friction offset threshold + \return The friction offset threshold. + */ + PX_FORCE_INLINE PxReal getFrictionOffsetThreshold() const { return mFrictionOffsetThreshold; } + /** + \brief Returns the friction offset threshold + \return The friction offset threshold. + */ + PX_FORCE_INLINE PxReal getSolverOffsetSlop() const { return mSolverOffsetSlop; } + /** + \brief Returns the correlation distance + \return The correlation distance. + */ + PX_FORCE_INLINE PxReal getCorrelationDistance() const { return mCorrelationDistance; } + + /** + \brief Returns the CCD separation threshold + \return The CCD separation threshold. + */ + PX_FORCE_INLINE PxReal getCCDSeparationThreshold() const { return mCCDSeparationThreshold; } + + /** + \brief Sets the bounce threshold + \param[in] f The bounce threshold + */ + PX_FORCE_INLINE void setBounceThreshold(PxReal f) { mBounceThreshold = f; } + /** + \brief Sets the correlation distance + \param[in] f The correlation distance + */ + PX_FORCE_INLINE void setCorrelationDistance(PxReal f) { mCorrelationDistance = f; } + /** + \brief Sets the friction offset threshold + \param[in] offset The friction offset threshold + */ + PX_FORCE_INLINE void setFrictionOffsetThreshold(PxReal offset) { mFrictionOffsetThreshold = offset; } + /** + \brief Sets the solver offset slop + \param[in] offset The solver offset slop + */ + PX_FORCE_INLINE void setSolverOffsetSlop(PxReal offset) { mSolverOffsetSlop = offset; } + /** + \brief Sets the friction offset threshold + \param[in] offset The friction offset threshold + */ + PX_FORCE_INLINE void setCCDSeparationThreshold(PxReal offset) { mCCDSeparationThreshold = offset; } + + + /** + \brief Returns the solver batch size + \return The solver batch size. + */ + PX_FORCE_INLINE PxU32 getSolverBatchSize() const { return mSolverBatchSize; } + /** + \brief Sets the solver batch size + \param[in] f The solver batch size + */ + PX_FORCE_INLINE void setSolverBatchSize(PxU32 f) { mSolverBatchSize = f; } + /** + \brief Returns the maximum solver constraint size + \return The maximum solver constraint size in this island in bytes. + */ + PX_FORCE_INLINE PxU32 getMaxSolverConstraintSize() const { return mMaxSolverConstraintSize; } + + /** + \brief Returns the friction model being used. + \return The friction model being used. + */ + PX_FORCE_INLINE PxFrictionType::Enum getFrictionType() const { return mFrictionType; } + + /** + \brief Returns the threshold stream + \return The threshold stream + */ + PX_FORCE_INLINE ThresholdStream& getThresholdStream() { return *mThresholdStream; } + + PX_FORCE_INLINE ThresholdStream& getForceChangedThresholdStream() { return *mForceChangedThresholdStream; } + + /** + \brief Returns the threshold table + \return The threshold table + */ + PX_FORCE_INLINE ThresholdTable& getThresholdTable() { return mThresholdTable; } + + /** + \brief Sets the friction model to be used. + \param[in] f The friction model to be used. + */ + PX_FORCE_INLINE void setFrictionType(PxFrictionType::Enum f) { mFrictionType = f; } + + /** + \brief Destroys this dynamics context + */ + virtual void destroy() = 0; + + + + PX_FORCE_INLINE PxcDataStreamPool& getContactStreamPool() { return mContactStreamPool; } + + PX_FORCE_INLINE PxcDataStreamPool& getPatchStreamPool() { return mPatchStreamPool; } + + PX_FORCE_INLINE PxcDataStreamPool& getForceStreamPool() { return mForceStreamPool; } + + PX_FORCE_INLINE Ps::Array& getConstraintWriteBackPool() { return mConstraintWriteBackPool; } + + + /** + \brief Returns the current frame's timestep + \return The current frame's timestep. + */ + PX_FORCE_INLINE PxReal getDt() const { return mDt; } + /** + \brief Returns 1/(current frame's timestep) + \return 1/(current frame's timestep). + */ + PX_FORCE_INLINE PxReal getInvDt() const { return mInvDt; } + + PX_FORCE_INLINE PxReal getMaxBiasCoefficient() const { return mMaxBiasCoefficient; } + + PX_FORCE_INLINE PxVec3 getGravity() const { return mGravity; } + + + + /** + \brief The entry point for the constraint solver. + \param[in] dt The simulation time-step + \param[in] continuation The continuation task for the solver + \param[in] processLostTouchTask The task that processes lost touches + + This method is called after the island generation has completed. Its main responsibilities are: + (1) Reserving the solver body pools + (2) Initializing the static and kinematic solver bodies, which are shared resources between islands. + (3) Construct the solver task chains for each island + + Each island is solved as an independent solver task chain. In addition, large islands may be solved using multiple parallel tasks. + Island solving is asynchronous. Once all islands have been solved, the continuation task will be called. + + */ + virtual void update(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* processLostTouchTask, + PxsContactManager** foundPatchManagers, PxU32 nbFoundPatchManagers, PxsContactManager** lostPatchManagers, PxU32 nbLostPatchManagers, PxU32 maxPatchesPerCM, + PxsContactManagerOutputIterator& iterator, PxsContactManagerOutput* gpuOutputs, const PxReal dt, const PxVec3& gravity, const PxU32 bitMapWordCounts) = 0; + + virtual void processLostPatches(IG::SimpleIslandManager& simpleIslandManager, PxsContactManager** lostPatchManagers, PxU32 nbLostPatchManagers, PxsContactManagerOutputIterator& iterator) = 0; + + + /** + \brief This method copy gpu solver body data to cpu body core + */ + virtual void updateBodyCore(PxBaseTask* continuation) = 0; + + /** + \brief Called after update's task chain has completed. This collects the results of the solver together + */ + virtual void mergeResults() = 0; + + virtual void setSimulationController(PxsSimulationController* simulationController) = 0; + + virtual void getDataStreamBase(void*& contactStreamBase, void*& patchStreamBase, void*& forceAndIndiceStreamBase) = 0; + + void createThresholdStream(Ps::VirtualAllocatorCallback& callback) { PX_ASSERT(mThresholdStream == NULL); mThresholdStream = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ThresholdStream")), ThresholdStream(callback));} + + void createForceChangeThresholdStream(Ps::VirtualAllocatorCallback& callback) { PX_ASSERT(mForceChangedThresholdStream == NULL); mForceChangedThresholdStream = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ThresholdStream")), ThresholdStream(callback));} + + + +protected: + + Context(IG::IslandSim* accurateIslandSim, Ps::VirtualAllocatorCallback* allocatorCallback, + PxvSimStats& simStats, bool enableStabilization, bool useEnhancedDeterminism, bool useAdaptiveForce, + const PxReal maxBiasCoefficient) : + mThresholdStream(NULL), + mForceChangedThresholdStream(NULL), + mAccurateIslandSim(accurateIslandSim), + mDt (1.0f), + mInvDt (1.0f), + mMaxBiasCoefficient (maxBiasCoefficient), + mEnableStabilization (enableStabilization), + mUseEnhancedDeterminism (useEnhancedDeterminism), + mUseAdaptiveForce (useAdaptiveForce), + mBounceThreshold(-2.0f), + mSolverBatchSize(32), + mConstraintWriteBackPool(Ps::VirtualAllocator(allocatorCallback)), + mSimStats(simStats) + { + } + + virtual ~Context() + { + if(mThresholdStream) + { + mThresholdStream->~ThresholdStream(); + PX_FREE(mThresholdStream); + } + mThresholdStream = NULL; + if(mForceChangedThresholdStream) + { + mForceChangedThresholdStream->~ThresholdStream(); + PX_FREE(mForceChangedThresholdStream); + } + mForceChangedThresholdStream = NULL; + } + + ThresholdStream* mThresholdStream; + ThresholdStream* mForceChangedThresholdStream; + ThresholdTable mThresholdTable; + + IG::IslandSim* mAccurateIslandSim; + PxsSimulationController* mSimulationController; + /** + \brief Time-step. + */ + PxReal mDt; + /** + \brief 1/time-step. + */ + PxReal mInvDt; + + PxReal mMaxBiasCoefficient; + + const bool mEnableStabilization; + + const bool mUseEnhancedDeterminism; + + const bool mUseAdaptiveForce; + + PxVec3 mGravity; + /** + \brief max solver constraint size + */ + PxU32 mMaxSolverConstraintSize; + + /** + \brief Threshold controlling the relative velocity at which the solver transitions between restitution and bias for solving normal contact constraint. + */ + PxReal mBounceThreshold; + /** + \brief Threshold controlling whether friction anchors are constructed or not. If the separation is above mFrictionOffsetThreshold, the contact will not be considered to become a friction anchor + */ + PxReal mFrictionOffsetThreshold; + + /** + \brief Tolerance used to zero offsets along an axis if it is below this threshold. Used to compensate for small numerical divergence inside contact gen. + */ + PxReal mSolverOffsetSlop; + + /** + \brief Threshold controlling whether distant contacts are processed using bias, restitution or a combination of the two. This only has effect on pairs involving bodies that have enabled speculative CCD simulation mode. + */ + PxReal mCCDSeparationThreshold; + + /** + \brief Threshold for controlling friction correlation + */ + PxReal mCorrelationDistance; + /** + \brief The minimum size of an island to generate a solver task chain. + */ + PxU32 mSolverBatchSize; + + /** + \brief The current friction model being used + */ + PxFrictionType::Enum mFrictionType; + + /** + \brief Structure to encapsulate contact stream allocations. Used by GPU solver to reference pre-allocated pinned host memory + */ + PxcDataStreamPool mContactStreamPool; + + /** + \brief Struct to encapsulate the contact patch stream allocations. Used by GPU solver to reference pre-allocated pinned host memory + */ + + PxcDataStreamPool mPatchStreamPool; + + /** + \brief Structure to encapsulate force stream allocations. Used by GPU solver to reference pre-allocated pinned host memory for force reports. + */ + PxcDataStreamPool mForceStreamPool; + + /** + \brief Structure to encapsulate constraint write back allocations. Used by GPU/CPU solver to reference pre-allocated pinned host memory for breakable joint reports. + */ + Ps::Array mConstraintWriteBackPool; + + PxvSimStats& mSimStats; + + +}; + +Context* createDynamicsContext( PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, Cm::FlushPool& taskPool, + PxvSimStats& simStats, PxTaskManager* taskManager, Ps::VirtualAllocatorCallback* allocatorCallback, PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, PxU64 contextID, + const bool enableStabilization, const bool useEnhancedDeterminism, const bool useAdaptiveForce, const PxReal maxBiasCoefficient, + const bool frictionEveryIteration + ); + +Context* createTGSDynamicsContext(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, Cm::FlushPool& taskPool, + PxvSimStats& simStats, PxTaskManager* taskManager, Ps::VirtualAllocatorCallback* allocatorCallback, PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, PxU64 contextID, + const bool enableStabilization, const bool useEnhancedDeterminism, const bool useAdaptiveForce, const PxReal lengthScale +); + + +} + +} + +#endif + diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h new file mode 100644 index 000000000..44026b435 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h @@ -0,0 +1,857 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_FEATHERSTONE_ARTICULATION_H +#define PXD_FEATHERSTONE_ARTICULATION_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsVecMath.h" +#include "CmUtils.h" +#include "PxArticulationJoint.h" +#include "DyVArticulation.h" +#include "DyFeatherstoneArticulationUtils.h" +#include "DyFeatherstoneArticulationJointData.h" + +#ifndef FEATHERSTONE_DEBUG +#define FEATHERSTONE_DEBUG 0 +#endif + +namespace physx +{ + + class PxContactJoint; + struct PxSolverConstraintDesc; + class PxcConstraintBlockStream; + class PxcScratchAllocator; + class PxsConstraintBlockManager; + struct SolverConstraint1DExtStep; + struct PxSolverConstraintPrepDesc; + struct PxSolverBody; + struct PxSolverBodyData; + class PxConstraintAllocator; + +namespace Dy +{ + +//#if PX_VC +//#pragma warning(push) +//#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +//#endif + + //class ArticulationJointCoreData; + class ArticulationLinkData; + struct SpatialSubspaceMatrix; + struct SolverConstraint1DExt; + struct SolverConstraint1DStep; + + class FeatherstoneArticulation; + struct SpatialMatrix; + struct SpatialTransform; + struct Constraint; + + struct DyScratchAllocator + { + char* base; + size_t size; + size_t taken; + + DyScratchAllocator(char* p, size_t s) : base(p), size(s), taken(0) {} + + PxU32 round16(PxU32 size_) { return (size_ + 15)&(~15); } + + template T* alloc(PxU32 count) + { + const PxU32 sizeReq = round16(sizeof(T)*count); + PX_ASSERT((taken+ sizeReq) < size); + T* result = reinterpret_cast(base + taken); + taken += sizeReq; + return result; + } + }; + + + //This stuct is used in TGS + class ArticulationTempData + { + public: + Cm::SpatialVectorF mBaseLinkMotionVelocite; + Ps::Array mZAForce; + Ps::Array mJointPosition; // joint position + Ps::Array mJointVelocity; // joint velocity + Ps::Array mLinkTransform; // this is the transform list for links + }; + + struct ArticulationInternalConstraint + { + //Common/shared directional info between, frictions and drives + Cm::UnAlignedSpatialVector row0; //24 24 + Cm::UnAlignedSpatialVector row1; //24 48 + Cm::UnAlignedSpatialVector deltaVA; //24 72 + Cm::UnAlignedSpatialVector deltaVB; //24 96 + //Response information + PxReal recipResponse; //4 100 + PxReal response; //4 104 + //Joint limit values + PxReal lowLimit; //4 108 + PxReal highLimit; //4 112 + PxReal lowImpulse; //4 116 changed + PxReal highImpulse; //4 120 changed + PxReal erp; //4 124 + //Joint spring drive info + PxReal driveTargetVel; //4 128 + PxReal driveBiasCoefficient; //4 132 + PxReal driveTarget; //4 136 + PxReal driveVelMultiplier; //4 140 + PxReal driveImpulseMultiplier; //4 144 + PxReal maxDriveForce; //4 148 + PxReal driveForce; //4 152 + + PxReal maxFrictionForce; //4 156 + PxReal frictionForce; //4 160 + PxReal frictionForceCoefficient; //4 164 + + bool isLinearConstraint; //1 165 + PxU8 padding[11]; //15 176 + }; + + struct ArticulationInternalLockedAxis + { + //How much an impulse will effect angular velocity + Cm::UnAlignedSpatialVector deltaVA; //24 24 + Cm::UnAlignedSpatialVector deltaVB; //24 48 + //Jacobian axis that is locked + PxVec3 axis; //12 60 + //Response information + PxReal recipResponse; //4 64 + //Initial error + PxReal error; //4 68 + //Bias scale + PxReal biasScale; //4 72 + + PxU32 pad[2]; //4 80 + }; + + class ArticulationData + { + public: + + ArticulationData() : + mLinksData(NULL), mJointData(NULL), + mDofs(0xffffffff), mLocks(0), mDataDirty(true) + { + + } + + ~ArticulationData(); + + PX_FORCE_INLINE void init(); + PX_FORCE_INLINE void resizeLinkData(const PxU32 linkCount); + PX_FORCE_INLINE void resizeJointData(const PxU32 dofs); + + PX_FORCE_INLINE PxReal* getJointAccelerations() { return mJointAcceleration.begin(); } + PX_FORCE_INLINE PxReal* getJointVelocities() { return mJointVelocity.begin(); } + PX_FORCE_INLINE PxReal* getJointDeltaVelocities() { return mJointDeltaVelocity.begin(); } + PX_FORCE_INLINE PxReal* getJointPositions() { return mJointPosition.begin(); } + PX_FORCE_INLINE PxReal* getJointForces() { return mJointForce.begin(); } + //PX_FORCE_INLINE PxReal* getJointFrictionForces() { return mJointFrictionForce.begin(); } + + PX_FORCE_INLINE ArticulationInternalConstraint& getInternalConstraint(const PxU32 dofId) { return mInternalConstraints[dofId]; } + PX_FORCE_INLINE const ArticulationInternalConstraint& getInternalConstraint(const PxU32 dofId) const { return mInternalConstraints[dofId]; } + + PX_FORCE_INLINE ArticulationInternalLockedAxis& getInternalLocks(const PxU32 dofId) { return mInternalLockedAxes[dofId]; } + PX_FORCE_INLINE const ArticulationInternalLockedAxis& getInternalLocks(const PxU32 dofId) const { return mInternalLockedAxes[dofId]; } + + + PX_FORCE_INLINE Cm::SpatialVectorF* getMotionVelocities() { return mMotionVelocities.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getMotionAccelerations() { return mMotionAccelerations.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getCorioliseVectors() { return mCorioliseVectors.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getSpatialZAVectors() { return mZAForces.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getTransmittedForces() { return mJointTransmittedForce.begin(); } + + PX_FORCE_INLINE Cm::SpatialVectorF* getPosIterMotionVelocities() { return mPosIterMotionVelocities.begin(); } + PX_FORCE_INLINE PxReal* getPosIterJointDeltaVelocities() { return mPosIterJointDeltaVelocities.begin(); } + + + PX_FORCE_INLINE Cm::SpatialVectorF& getPosIterMotionVelocity(const PxU32 index) { return mPosIterMotionVelocities[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getMotionVelocity(const PxU32 index) const { return mMotionVelocities[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getMotionAcceleration(const PxU32 index) const { return mMotionAccelerations[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getCorioliseVector(const PxU32 index) const { return mCorioliseVectors[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getSpatialZAVector(const PxU32 index) const { return mZAForces[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getTransmittedForce(const PxU32 index) const { return mJointTransmittedForce[index]; } + + + PX_FORCE_INLINE Cm::SpatialVectorF& getMotionVelocity(const PxU32 index) { return mMotionVelocities[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getMotionAcceleration(const PxU32 index) { return mMotionAccelerations[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getCorioliseVector(const PxU32 index) { return mCorioliseVectors[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getSpatialZAVector(const PxU32 index) { return mZAForces[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getTransmittedForce(const PxU32 index) { return mJointTransmittedForce[index]; } + + //PX_FORCE_INLINE Dy::SpatialMatrix* getTempSpatialMatrix() { mTempSpatialMatrix.begin(); } + + + PX_FORCE_INLINE PxTransform& getPreTransform(const PxU32 index) { return mPreTransform[index]; } + PX_FORCE_INLINE const PxTransform& getPreTransform(const PxU32 index) const { return mPreTransform[index]; } + PX_FORCE_INLINE void setPreTransform(const PxU32 index, const PxTransform& tra) { mPreTransform[index] = tra; } + PX_FORCE_INLINE PxTransform* getPreTransform() { return mPreTransform.begin(); } + + PX_FORCE_INLINE const Cm::SpatialVectorF& getDeltaMotionVector(const PxU32 index) const { return mDeltaMotionVector[index]; } + PX_FORCE_INLINE void setDeltaMotionVector(const PxU32 index, const Cm::SpatialVectorF& vec) { mDeltaMotionVector[index] = vec; } + PX_FORCE_INLINE Cm::SpatialVectorF* getDeltaMotionVector() { return mDeltaMotionVector.begin(); } + + PX_FORCE_INLINE ArticulationLink* getLinks() const { return mLinks; } + PX_FORCE_INLINE PxU32 getLinkCount() const { return mLinkCount; } + + PX_FORCE_INLINE ArticulationLink& getLink(PxU32 index) const { return mLinks[index]; } + + PX_FORCE_INLINE ArticulationLinkData* getLinkData() const { return mLinksData; } + ArticulationLinkData& getLinkData(PxU32 index) const; + + PX_FORCE_INLINE ArticulationJointCoreData* getJointData() const { return mJointData; } + ArticulationJointCoreData& getJointData(PxU32 index) const { return mJointData[index]; } + + PX_FORCE_INLINE const ArticulationCore* getCore() const { return mCore; } + PX_FORCE_INLINE Cm::SpatialVector* getExternalAccelerations() { return mExternalAcceleration; } + + PX_FORCE_INLINE PxU32 getSolverDataSize() { return mSolverDataSize; } + PX_FORCE_INLINE Cm::SpatialVector& getExternalAcceleration(const PxU32 linkID) { return mExternalAcceleration[linkID]; } + + PX_FORCE_INLINE PxReal getDt() const { return mDt; } + PX_FORCE_INLINE void setDt(const PxReal dt) { mDt = dt; } + + PX_FORCE_INLINE bool getDataDirty() { return mDataDirty; } + PX_FORCE_INLINE void setDataDirty(const bool dirty) { mDataDirty = dirty; } + + PX_FORCE_INLINE PxU32 getDofs() const { return mDofs; } + PX_FORCE_INLINE void setDofs(const PxU32 dof) { mDofs = dof; } + + PX_FORCE_INLINE PxU32 getLocks() const { return mLocks; } + PX_FORCE_INLINE void setLocks(const PxU32 locks) { mLocks = locks; } + + PX_FORCE_INLINE FeatherstoneArticulation* getArticulation() { return mArticulation;} + PX_FORCE_INLINE void setArticulation(FeatherstoneArticulation* articulation) { mArticulation = articulation; } + + PX_FORCE_INLINE const SpatialMatrix& getBaseInvSpatialArticulatedInertia() const { return mBaseInvSpatialArticulatedInertia; } + + PX_FORCE_INLINE PxTransform* getAccumulatedPoses() { return mAccumulatedPoses.begin(); } + + PX_FORCE_INLINE const PxTransform* getAccumulatedPoses() const { return mAccumulatedPoses.begin(); } + + private: + + Ps::Array mJointAcceleration; // joint acceleration + Ps::Array mJointVelocity; // joint velocity + Ps::Array mJointDeltaVelocity; // joint velocity change due to contacts + Ps::Array mJointPosition; // joint position + Ps::Array mJointForce; // joint force + //Ps::Array mJointFrictionForce; // joint friction force + + Ps::Array mPosIterJointDeltaVelocities; //joint delta velocity after postion iternation before velocity iteration + Ps::Array mPosIterMotionVelocities; //link motion velocites after position iteration before velocity iteration + Ps::Array mMotionVelocities; //link motion velocites + Ps::Array mMotionAccelerations; //link motion accelerations + Ps::Array mCorioliseVectors; //link coriolise vector + Ps::Array mZAForces; //link spatial zero acceleration force/ spatial articulated force + Ps::Array mJointTransmittedForce; + Ps::Array mInternalConstraints; + Ps::Array mInternalLockedAxes; + + + Ps::Array mDeltaMotionVector; //this is for TGS solver + Ps::Array mPreTransform; //this is the previous transform list for links + + //Ps::Array mTempSpatialMatrix; + + ArticulationLink* mLinks; + PxU32 mLinkCount; + ArticulationLinkData* mLinksData; + ArticulationJointCoreData* mJointData; + PxReal mDt; + PxU32 mDofs; + PxU32 mLocks; + const ArticulationCore* mCore; + Cm::SpatialVector* mExternalAcceleration; + PxU32 mSolverDataSize; + bool mDataDirty; //this means we need to call commonInit() + bool mJointDirty; //this means joint delta velocity has been changed by contacts so we need to update joint velocity/joint acceleration + FeatherstoneArticulation* mArticulation; + + Ps::Array mAccumulatedPoses; + Ps::Array mDeltaQ; + PxReal mAccumulatedDt; + + //ArticulationTempData mTempData; + SpatialMatrix mBaseInvSpatialArticulatedInertia; + + friend class FeatherstoneArticulation; + + }; + + void ArticulationData::init() + { + //zero delta motion vector for TGS solver + PxMemZero(getDeltaMotionVector(), sizeof(Cm::SpatialVectorF) * mLinkCount); + + //zero joint velocity delta, which will be changed in pxcFsApplyImpulse if there are any contact with links + PxMemZero(getJointDeltaVelocities(), sizeof(PxReal) * mDofs); + mJointDirty = false; + + } + + + struct ScratchData + { + public: + ScratchData() + { + motionVelocities = NULL; + motionAccelerations = NULL; + coriolisVectors = NULL; + spatialZAVectors = NULL; + externalAccels = NULL; + compositeSpatialInertias = NULL; + + jointVelocities = NULL; + jointAccelerations = NULL; + jointForces = NULL; + jointPositions = NULL; + jointFrictionForces = NULL; + } + + Cm::SpatialVectorF* motionVelocities; + Cm::SpatialVectorF* motionAccelerations; + Cm::SpatialVectorF* coriolisVectors; + Cm::SpatialVectorF* spatialZAVectors; + Cm::SpatialVector* externalAccels; + Dy::SpatialMatrix* compositeSpatialInertias; + + PxReal* jointVelocities; + PxReal* jointAccelerations; + PxReal* jointForces; + PxReal* jointPositions; + PxReal* jointFrictionForces; + }; + + +#if PX_VC +#pragma warning(push) +#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + PX_ALIGN_PREFIX(64) + class FeatherstoneArticulation : public ArticulationV + { + public: + // public interface + + FeatherstoneArticulation(Sc::ArticulationSim*); + ~FeatherstoneArticulation(); + + + // get data sizes for allocation at higher levels + virtual void getDataSizes(PxU32 linkCount, + PxU32 &solverDataSize, + PxU32& totalSize, + PxU32& scratchSize); + + virtual bool resize(const PxU32 linkCount); + + virtual void onUpdateSolverDesc(); + + virtual PxU32 getDofs(); + + virtual PxU32 getDof(const PxU32 linkID); + + virtual void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag); + + virtual void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag); + + virtual void packJointData(const PxReal* maximum, PxReal* reduced); + + virtual void unpackJointData(const PxReal* reduced, PxReal* maximum); + + virtual void initializeCommonData(); + + //gravity as input, joint force as output + virtual void getGeneralizedGravityForce(const PxVec3& gravity, PxArticulationCache& cache); + + //joint velocity as input, generalised force(coriolis and centrigugal force) as output + virtual void getCoriolisAndCentrifugalForce(PxArticulationCache& cache); + + //external force as input, joint force as output + virtual void getGeneralizedExternalForce(PxArticulationCache& /*cache*/); + + //joint force as input, joint acceleration as output + virtual void getJointAcceleration(const PxVec3& gravity, PxArticulationCache& cache); + + //joint acceleration as input, joint force as out + virtual void getJointForce(PxArticulationCache& cache); + + virtual void getKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache); + + //These two functions are for closed loop system + void getKMatrix(ArticulationJointCore* loopJoint, const PxU32 parentIndex, const PxU32 childIndex, PxArticulationCache& cache); + + virtual void getCoefficentMatrix(const PxReal dt, const PxU32 linkID, const PxContactJoint* contactJoints, const PxU32 nbContacts, PxArticulationCache& cache); + + virtual void getCoefficentMatrixWithLoopJoints(ArticulationLoopConstraint* lConstraints, const PxU32 nbJoints, PxArticulationCache& cache); + + virtual bool getLambda(ArticulationLoopConstraint* lConstraints, const PxU32 nbJoints, PxArticulationCache& cache, PxArticulationCache& rollBackCache, + const PxReal* jointTorque, const PxVec3& gravity, const PxU32 maxIter); + + virtual void getGeneralizedMassMatrix(PxArticulationCache& cache); + + virtual void getGeneralizedMassMatrixCRB(PxArticulationCache& cache); + + virtual void teleportRootLink(); + + virtual void getImpulseResponse( + PxU32 linkID, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaV) const; + + virtual void getImpulseSelfResponse( + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1) const; + + virtual Cm::SpatialVectorV getLinkVelocity(const PxU32 linkID) const; + + virtual Cm::SpatialVectorV getLinkMotionVector(const PxU32 linkID) const; + + //this is called by island gen to determine whether the articulation should be awake or sleep + virtual Cm::SpatialVector getMotionVelocity(const PxU32 linkID) const; + + virtual PxReal getLinkMaxPenBias(const PxU32 linkID) const; + + + + + static PxU32 computeUnconstrainedVelocities( + const ArticulationSolverDesc& desc, + PxReal dt, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, PxU64 contextID, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + static void computeUnconstrainedVelocitiesTGS( + const ArticulationSolverDesc& desc, + PxReal dt, const PxVec3& gravity, + PxU64 contextID, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + static PxU32 setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* Z); + + static void saveVelocity(const ArticulationSolverDesc& d, Cm::SpatialVectorF* deltaV); + + static void saveVelocityTGS(const ArticulationSolverDesc& d, PxReal invDtF32); + + static void updateBodies(const ArticulationSolverDesc& desc, PxReal dt); + + static void updateBodiesTGS(const ArticulationSolverDesc& desc, PxReal dt); + + static void updateBodies(const ArticulationSolverDesc& desc, PxReal dt, bool integrateJointPosition); + + static void recordDeltaMotion(const ArticulationSolverDesc& desc, const PxReal dt, Cm::SpatialVectorF* deltaV); + + static void deltaMotionToMotionVelocity(const ArticulationSolverDesc& desc, PxReal invDt); + + virtual void pxcFsApplyImpulse(PxU32 linkID, Ps::aos::Vec3V linear, + Ps::aos::Vec3V angular, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + virtual void pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, + const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, + const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + virtual void pxcFsApplyImpulses(Cm::SpatialVectorF* Z); + + virtual Cm::SpatialVectorV pxcFsGetVelocity(PxU32 linkID); + + virtual void pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1); + + virtual Cm::SpatialVectorV pxcFsGetVelocityTGS(PxU32 linkID); + + virtual const PxTransform& getCurrentTransform(PxU32 linkID) const; + + virtual const PxQuat& getDeltaQ(PxU32 linkID) const; + + //Applies a set of N impulses, all in local space and updates the links' motion and joint velocities + void applyImpulses(Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + void getDeltaV(Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + //This method calculate the velocity change due to collision/constraint impulse, record joint velocity and acceleration + static Cm::SpatialVectorF propagateVelocity(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z, + PxReal* jointVelocity, const Cm::SpatialVectorF& hDeltaV); + + //This method calculate the velocity change due to collision/constraint impulse + static Cm::SpatialVectorF propagateVelocityTestImpulse(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, + const Cm::SpatialVectorF& Z, const Cm::SpatialVectorF& hDeltaV); + + //This method calculate zero acceration impulse due to test/actual impluse + static Cm::SpatialVectorF propagateImpulse(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z); + + void applyCacheToDest(ArticulationData& data, PxArticulationCache& cache, + PxReal* jVelocities, PxReal* jAcceleration, PxReal* jPosition, PxReal* jointForce, + const PxArticulationCacheFlags flag); + + ArticulationData& getArticulationData() { return mArticulationData; } + + void setGpuRemapId(const PxU32 id) { mGpuRemapId = id; } + PxU32 getGpuRemapId() { return mGpuRemapId; } + + private: + + + void constraintPrep(ArticulationLoopConstraint* lConstraints, const PxU32 nbJoints, + Cm::SpatialVectorF* Z, PxSolverConstraintPrepDesc& prepDesc, PxSolverBody& sBody, + PxSolverBodyData& sBodyData, PxSolverConstraintDesc* desc, PxConstraintAllocator& allocator); + + void updateArticulation(ScratchData& scratchData, + const PxVec3& gravity, + Cm::SpatialVectorF* Z, + Cm::SpatialVectorF* DeltaV); + + + PxU32 computeUnconstrainedVelocitiesInternal( + const ArticulationSolverDesc& desc, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); + + void computeUnconstrainedVelocitiesTGSInternal(const PxVec3& gravity, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); + + + //copy joint data from fromJointData to toJointData + void copyJointData(ArticulationData& data, PxReal* toJointData, PxReal* fromJointData); + + void computeDofs(); + //this function calculates motion subspace matrix(s) for all tree joint + void jcalc(ArticulationData& data); + + //this function calculates loop joint constraint subspace matrix(s) and active force + //subspace matrix + void jcalcLoopJointSubspace(ArticulationJointCore* joint, ArticulationJointCoreData& jointDatum, SpatialSubspaceMatrix& T); + + void computeSpatialInertia(ArticulationData& data); + + //compute zero acceleration force + void computeZ(ArticulationData& data, const PxVec3& gravity, ScratchData& scratchData); + + //compute drag force + void computeD(ArticulationData& data, ScratchData& scratchData, + Cm::SpatialVectorF* tZ, Cm::SpatialVectorF* tDeltaV); + + void solveInternalConstraints(const PxReal dt, const PxReal invDt, Cm::SpatialVectorF* impulses, Cm::SpatialVectorF* DeltaV, + bool velocityIteration); + + //compute coriolis force + void computeC(ArticulationData& data, ScratchData& scratchData); + + //compute relative transform child to parent + void computeRelativeTransformC2P(ArticulationData& data); + //compute relative transform child to base + void computeRelativeTransformC2B(ArticulationData& data); + + void computeLinkVelocities(ArticulationData& data, ScratchData& scratchData); + + void initLinks(ArticulationData& data, const PxVec3& gravity, + ScratchData& scratchData, Cm::SpatialVectorF* tZ, Cm::SpatialVectorF* tDeltaV); + + void computeIs(ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum); + SpatialMatrix computePropagateSpatialInertia(const PxU8 jointType, ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum); + void transformInertia(const SpatialTransform& sTod, SpatialMatrix& inertia); + + void computeArticulatedSpatialInertia(ArticulationData& data); + + void computeArticulatedSpatialZ(ArticulationData& data, ScratchData& scratchData); + + void computeJointAcceleration(ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum, + const Cm::SpatialVectorF& pMotionAcceleration, PxReal* jointAcceleration); + + //compute joint acceleration, joint velocity and link acceleration, velocity based + //on spatial force and spatial articulated inertia tensor + void computeLinkAcceleration(ArticulationData& data, ScratchData& scratchData); + + //void computeTempLinkAcceleration(ArticulationData& data, ScratchData& scratchData); + void computeJointTransmittedFrictionForce(ArticulationData& data, ScratchData& scratchData, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); + void computeJointFriction(ArticulationData& data, ScratchData& scratchData); + + //void copyFromBodyCore(); + //void copyToBodyCore(); + void updateBodies(); + + + void applyExternalImpulse(ArticulationLink* links, const PxU32 linkCount, const bool fixBase, + ArticulationData& data, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV, const PxReal dt, const PxVec3& gravity, Cm::SpatialVector* acceleration); + + static Cm::SpatialVectorF getDeltaVWithDeltaJV(const bool fixBase, const PxU32 linkID, + const ArticulationData& data, Cm::SpatialVectorF* Z, + PxReal* jointVelocities); + + static Cm::SpatialVectorF getDeltaV(const bool fixBase, const PxU32 linkID, + const ArticulationData& data, Cm::SpatialVectorF* Z); + + //impulse need to be in the linkID space + static void getZ(const PxU32 linkID, const ArticulationData& data, + Cm::SpatialVectorF* Z, const Cm::SpatialVectorF& impulse); + + ////impulse0 and impulse1 are in linkID0 and linkID1 space respectively + //static void getZ( + // const ArticulationData& data, + // Cm::SpatialVectorF* Z, + // PxU32 linkID0_, + // const Cm::SpatialVector& impulse0, + // PxU32 linkID1_, + // const Cm::SpatialVector& impulse1); + + //This method use in impulse self response. The input impulse is in the link space + static Cm::SpatialVectorF getImpulseResponse( + const PxU32 linkID, + const bool fixBase, + const ArticulationData& data, + Cm::SpatialVectorF* Z, + const Cm::SpatialVectorF& impulse); + + //This method use in impulse self response. The input impulse is in the link space + static Cm::SpatialVectorF getImpulseResponseWithJ( + const PxU32 linkID, + const bool fixBase, + const ArticulationData& data, + Cm::SpatialVectorF* Z, + const Cm::SpatialVectorF& impulse, + PxReal* jointVelocities); + + void getImpulseSelfResponseInv(const bool fixBase, + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1, + PxReal* jointVelocities); + + void getImpulseResponseSlowInv(Dy::ArticulationLink* links, + const ArticulationData& data, + PxU32 linkID0_, + const Cm::SpatialVector& impulse0, + Cm::SpatialVector& deltaV0, + PxU32 linkID1_, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV1, + PxReal* jointVelocities); + + + Cm::SpatialVectorF getImpulseResponseInv(const bool fixBase, + const PxU32 linkID, Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + PxReal* jointVelocites); + + void inverseDynamic(ArticulationData& data, const PxVec3& gravity, + ScratchData& scratchData); + + void inverseDynamicFloatingBase(ArticulationData& data, const PxVec3& gravity, + ScratchData& scratchData); + + //compute link body force with motion velocity and acceleration + void computeZAForceInv(ArticulationData& data, ScratchData& scratchData); + void initCompositeSpatialInertia(ArticulationData& data, Dy::SpatialMatrix* compositeSpatialInertia); + void computeCompositeSpatialInertiaAndZAForceInv(ArticulationData& data, ScratchData& scratchData); + + void computeRelativeGeneralizedForceInv(ArticulationData& data, ScratchData& scratchData); + + //provided joint velocity and joint acceleartion, compute link acceleration + void computeLinkAccelerationInv(ArticulationData& data, ScratchData& scratchData); + + + void computeGeneralizedForceInv(ArticulationData& data, ScratchData& scratchData); + + void calculateMassMatrixColInv(ScratchData& scratchData); + + void calculateHFixBase(PxArticulationCache& cache); + + void calculateHFloatingBase(PxArticulationCache& cache); + + //joint limits + void enforcePrismaticLimits(PxReal* jPosition, ArticulationJointCore* joint); + + + public: + static void getImpulseSelfResponse(ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + const ArticulationData& data, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1); + + void createHardLimit( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt); + + void createHardLimits( + SolverConstraint1DExt& s0, + SolverConstraint1DExt& s1, + const PxVec3& axis, + PxReal err0, + PxReal err1, + PxReal recipDt, + const Cm::SpatialVectorV& deltaVA, + const Cm::SpatialVectorV& deltaVB, + PxReal recipResponse); + + void createTangentialSpring( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt); + + PxU32 setupSolverConstraints( + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + ArticulationLink* links, + const PxU32 linkCount, + const bool fixBase, + ArticulationData& data, + Cm::SpatialVectorF* Z, + PxU32& acCount); + + void setupInternalConstraints( + ArticulationLink* links, + const PxU32 linkCount, + const bool fixBase, + ArticulationData& data, + Cm::SpatialVectorF* Z, + PxReal dt, + PxReal invDt, + PxReal erp, + bool isTGSSolver); + + void createHardLimitTGS( + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt, + const Cm::SpatialVectorV& deltaVA, + const Cm::SpatialVectorV& deltaVB, + PxReal recipResponse); + + void createTangentialSpringTGS( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt); + + + //integration + void propagateLinksDown(ArticulationData& data, PxReal* jointVelocities, PxReal* jointPositions, + Cm::SpatialVectorF* motionVelocities); + + void computeAndEnforceJointPositions(ArticulationData& data, PxReal* jointPositions); + + //update link position based on joint position provided by the cache + void teleportLinks(ArticulationData& data); + + PxU8* allocateScratchSpatialData(PxcScratchAllocator* allocator, + const PxU32 linkCount, ScratchData& scratchData, bool fallBackToHeap = false); + + void allocateScratchSpatialData(DyScratchAllocator& allocator, + const PxU32 linkCount, ScratchData& scratchData); + + //This method calculate the velocity change from parent to child using parent current motion velocity + PxTransform propagateTransform(const PxU32 linkID, ArticulationLink* links, ArticulationJointCoreData& jointDatum, + Cm::SpatialVectorF* motionVelocities, const PxReal dt, const PxTransform& pBody2World, const PxTransform& currentTransform, + PxReal* jointVelocity, PxReal* jointDeltaVelocities, PxReal* jointPosition); + + static void updateRootBody(const Cm::SpatialVectorF& motionVelocity, + const PxTransform& preTransform, ArticulationData& data, const PxReal dt); + + ArticulationData mArticulationData; + + Ps::Array mScratchMemory; + bool mHasSphericalJoint; + //this is the id remap pxgbodysim and pxgariculation. if application delete the articulation, we need to + //put back this id to the id pool + PxU32 mGpuRemapId; + + } PX_ALIGN_SUFFIX(64); + +#if PX_VC +#pragma warning(pop) +#endif + + void PxvRegisterArticulationsReducedCoordinate(); + +} //namespace Dy + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h new file mode 100644 index 000000000..023757e6f --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h @@ -0,0 +1,261 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_FEATHERSTONE_ARTICULATION_JOINTCORE_H +#define PXD_FEATHERSTONE_ARTICULATION_JOINTCORE_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsVecMath.h" +#include "CmUtils.h" +#include "CmSpatialVector.h" +#include "DyVArticulation.h" +#include "DyFeatherstoneArticulationUtils.h" +#include + +namespace physx +{ + namespace Dy + { + + + class ArticulationJointCoreData + { + public: + + ArticulationJointCoreData() : jointOffset(0xffffffff), dofInternalConstraintMask(0) + { + for (PxU32 i = 0; i < 6; ++i) + { + targetJointPosition[i] = 0.f; + targetJointVelocity[i] = 0.f; + } + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE void computeMotionMatrix(ArticulationJointCoreBase* joint) + { + const PxVec3 childOffset = -joint->childPose.p; + + //transpose(Tc)*S = 0 + //transpose(Ta)*S = 1 + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + PxReal* jJointAxis = jointAxis[0]; + PxVec3 tJointAxis(jJointAxis[3], jJointAxis[4], jJointAxis[5]); + const PxVec3 u = (joint->childPose.rotate(tJointAxis)).getNormalized(); + + motionMatrix.setNumColumns(1); + motionMatrix.setColumn(0, PxVec3(0.f), u); + + PX_ASSERT(dof == 1); + + break; + } + case PxArticulationJointType::eREVOLUTE: + { + PxReal* jJointAxis = jointAxis[0]; + PxVec3 tJointAxis(jJointAxis[0], jJointAxis[1], jJointAxis[2]); + const PxVec3 u = (joint->childPose.rotate(tJointAxis)).getNormalized(); + const PxVec3 uXd = u.cross(childOffset); + + motionMatrix.setNumColumns(1); + motionMatrix.setColumn(0, u, uXd); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + motionMatrix.setNumColumns(dof); + + for (PxU32 ind = 0; ind childPose.rotate(tJointAxis)).getNormalized(); + + const PxVec3 uXd = u.cross(childOffset); + motionMatrix.setColumn(ind, u, uXd); + } + + break; + } + case PxArticulationJointType::eFIX: + { + motionMatrix.setNumColumns(0); + + PX_ASSERT(dof == 0); + break; + } + default: + break; + } + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU8 computeJointDofs(ArticulationJointCoreBase* joint) const + { + PxU8 tDof = 0; + + for (PxU32 i = 0; i < DY_MAX_DOF; ++i) + { + if (joint->motion[i] != PxArticulationMotion::eLOCKED) + { + tDof++; + } + } + + return tDof; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void computeJointDof(ArticulationJointCoreBase* joint, const bool forceRecompute) + { + if (joint->dirtyFlag & ArticulationJointCoreDirtyFlag::eMOTION || forceRecompute) + { + + dof = 0; + lockedAxes = 0; + limitedAxes = 0; + + joint->prismaticLimited = false; + + memset(jointAxis, 0, sizeof(PxReal) * 6 * 6); + + for (PxU8 i = 0; i < DY_MAX_DOF; ++i) + { + if (joint->motion[i] != PxArticulationMotion::eLOCKED) + { + //axis is in the local space of joint + jointAxis[dof][i] = 1.f; + + if (joint->motion[i] == PxArticulationMotion::eLIMITED) + { + + if(i == PxArticulationAxis::eX + || i == PxArticulationAxis::eY + ||i == PxArticulationAxis::eZ) + joint->prismaticLimited = true; + + limitedAxes++; + } + + joint->dofIds[dof++] = i; + } + } + + lockedAxes = 0; + + //Spherical joints treat locked axes as free axes with a constraint. This produces better + //results for spherical joints with 2 dofs free, where keeping the 3rd axis locked can lead to + //an over-consrtained behaviour that is undesirable. However, the drawback is that there will be + //some drift and error on the joint axes + if (joint->jointType == PxArticulationJointType::eSPHERICAL && dof == 2) + { + for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i) + { + if (joint->motion[i] == PxArticulationMotion::eLOCKED) + { + //axis is in the local space of joint + jointAxis[dof][i] = 1.f; + joint->dofIds[dof++] = PxU8(i); + lockedAxes++; + } + } + } + + joint->dirtyFlag &= (~ArticulationJointCoreDirtyFlag::eMOTION); + } + + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE void setJointVelocityDrive(ArticulationJointCoreBase* joint) + { + if (joint->dirtyFlag & ArticulationJointCoreDirtyFlag::eTARGETVELOCITY) + { + PxU32 count = 0; + for (PxU32 i = 0; i < DY_MAX_DOF; ++i) + { + if (joint->motion[i] != PxArticulationMotion::eLOCKED) + { + targetJointVelocity[count] = joint->targetV[i]; + count++; + } + } + joint->dirtyFlag &= ~ArticulationJointCoreDirtyFlag::eTARGETVELOCITY; + } + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void setJointPoseDrive(ArticulationJointCoreBase* joint) + { + if (joint->dirtyFlag & ArticulationJointCoreDirtyFlag::eTARGETPOSE) + { + PxU32 count = 0; + for (PxU32 i = 0; i < DY_MAX_DOF; ++i) + { + if (joint->motion[i] != PxArticulationMotion::eLOCKED) + { + targetJointPosition[count] = joint->targetP[i]; + count++; + } + } + + joint->dirtyFlag &= ~ArticulationJointCoreDirtyFlag::eTARGETPOSE; + } + } + + //in the joint space + PxReal jointAxis[6][6]; //144 144 + + //this is in child body space(S) + SpatialSubspaceMatrix motionMatrix; //196 340 + PxReal targetJointVelocity[6]; //24 364 + PxReal targetJointPosition[6]; //24 388 + PxReal maxDriveForce[6]; //24 412 + + //this is the dof offset for the joint in the cache + PxU32 jointOffset; //4 416 + //degree of freedom + PxU8 dof; //1 417 + PxU8 limitedAxes; //1 418 + PxU8 dofInternalConstraintMask; //1 419 + PxU8 lockedAxes; //1 420 + PxU8 padding[12]; //12 432 + + + + }; + + }//namespace Dy +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h new file mode 100644 index 000000000..33db87784 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h @@ -0,0 +1,881 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_FEATHERSTONE_ARTICULATION_UTIL_H +#define DY_FEATHERSTONE_ARTICULATION_UTIL_H + +#include "PsVecMath.h" +#include "CmSpatialVector.h" +#include "PsBitUtils.h" +#include "foundation/PxMemory.h" + +namespace physx +{ + +namespace Dy +{ + static const size_t DY_MAX_DOF = 6; + + struct SpatialSubspaceMatrix + { + public: + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialSubspaceMatrix() :numColumns(0) + { + //PxMemZero(columns, sizeof(Cm::SpatialVectorF) * 6); + memset(columns, 0, sizeof(Cm::SpatialVectorF) * 6); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void setNumColumns(const PxU32 nc) + { + numColumns = nc; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNumColumns() const + { + return numColumns; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF transposeMultiply(Cm::SpatialVectorF& v) const + { + PxReal result[6]; + for (PxU32 i = 0; i < numColumns; ++i) + { + const Cm::SpatialVectorF& row = columns[i]; + result[i] = row.dot(v); + } + + Cm::SpatialVectorF res; + res.top.x = result[0]; res.top.y = result[1]; res.top.z = result[2]; + res.bottom.x = result[3]; res.bottom.y = result[4]; res.bottom.z = result[5]; + + return res; + + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void setColumn(const PxU32 index, const PxVec3& top, const PxVec3& bottom) + { + columns[index] = Cm::SpatialVectorF(top, bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF& operator[](unsigned int num) + { + return columns[num]; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const Cm::SpatialVectorF& operator[](unsigned int num) const + { + return columns[num]; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const Cm::SpatialVectorF* getColumns() const + { + return columns; + } + + + private: + Cm::SpatialVectorF columns[6]; //192 192 + PxU32 numColumns; //4 196 + + }; + + //this should be 6x6 matrix + //|R, 0| + //|-R*rX, R| + struct SpatialTransform + { + PxMat33 R; + PxQuat q; + PxMat33 T; + + public: + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform() : R(PxZero), T(PxZero) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform(const PxMat33& R_, const PxMat33& T_) : R(R_), T(T_) + { + q = PxQuat(R_); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform(const PxQuat& q_, const PxMat33& T_) : q(q_), T(T_) + { + R = PxMat33(q_); + } + + //This assume angular is the top vector and linear is the bottom vector + /*PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVector operator *(const Cm::SpatialVector& s) const + { + const PxVec3 angular = R * s.angular; + const PxVec3 linear = T * s.angular + R * s.linear; + return Cm::SpatialVector(linear, angular); + }*/ + + + ////This assume angular is the top vector and linear is the bottom vector + //PX_FORCE_INLINE Cm::SpatialVectorF operator *(Cm::SpatialVectorF& s) const + //{ + // const PxVec3 top = R * s.top; + // const PxVec3 bottom = T * s.top + R * s.bottom; + + // const PxVec3 top1 = q.rotate(s.top); + // const PxVec3 bottom1 = T * s.top + q.rotate(s.bottom); + + ///* const PxVec3 tDif = (top - top1).abs(); + // const PxVec3 bDif = (bottom - bottom1).abs(); + // const PxReal eps = 0.001f; + // PX_ASSERT(tDif.x < eps && tDif.y < eps && tDif.z < eps); + // PX_ASSERT(bDif.x < eps && bDif.y < eps && bDif.z < eps);*/ + // return Cm::SpatialVectorF(top1, bottom1); + //} + + //This assume angular is the top vector and linear is the bottom vector + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF operator *(const Cm::SpatialVectorF& s) const + { + //const PxVec3 top = R * s.top; + //const PxVec3 bottom = T * s.top + R * s.bottom; + + const PxVec3 top1 = q.rotate(s.top); + const PxVec3 bottom1 = T * s.top + q.rotate(s.bottom); + + return Cm::SpatialVectorF(top1, bottom1); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector operator *(const Cm::UnAlignedSpatialVector& s) const + { + //const PxVec3 top = R * s.top; + //const PxVec3 bottom = T * s.top + R * s.bottom; + + const PxVec3 top1 = q.rotate(s.top); + const PxVec3 bottom1 = T * s.top + q.rotate(s.bottom); + + return Cm::UnAlignedSpatialVector(top1, bottom1); + } + + //transpose is the same as inverse, R(inverse) = R(transpose) + //|R(t), 0 | + //|rXR(t), R(t)| + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform getTranspose() const + { + SpatialTransform ret; + ret.q = q.getConjugate(); + ret.R = R.getTranspose(); + ret.T = T.getTranspose(); + return ret; + + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF transposeTransform(const Cm::SpatialVectorF& s) const + { + const PxVec3 top1 = q.rotateInv(s.top); + const PxVec3 bottom1 = T.transformTranspose(s.top) + q.rotateInv(s.bottom); + + return Cm::SpatialVectorF(top1, bottom1); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector transposeTransform(const Cm::UnAlignedSpatialVector& s) const + { + const PxVec3 top1 = q.rotateInv(s.top); + const PxVec3 bottom1 = T.transformTranspose(s.top) + q.rotateInv(s.bottom); + + return Cm::UnAlignedSpatialVector(top1, bottom1); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator =(SpatialTransform& other) + { + R = other.R; + q = other.q; + T = other.T; + } + + }; + + //this should be 6x6 matrix and initialize to + //|0, M| + //|I, 0| + //this should be 6x6 matrix but bottomRight is the transpose of topLeft + //so we can get rid of bottomRight + struct SpatialMatrix + { + PxMat33 topLeft; // intialize to 0 + PxMat33 topRight; // initialize to mass matrix + PxMat33 bottomLeft; // initialize to inertia + PxU32 padding; //4 112 + + public: + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix() + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix(PxZERO r) : topLeft(PxZero), topRight(PxZero), + bottomLeft(PxZero) + { + PX_UNUSED(r); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix(const PxMat33& topLeft_, const PxMat33& topRight_, const PxMat33& bottomLeft_) + { + topLeft = topLeft_; + topRight = topRight_; + bottomLeft = bottomLeft_; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33 getBottomRight() const + { + return topLeft.getTranspose(); + } + + PX_FORCE_INLINE void setZero() + { + topLeft = PxMat33(0.f); + topRight = PxMat33(0.f); + bottomLeft = PxMat33(0.f); + } + + + //This assume angular is the top vector and linear is the bottom vector + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVector operator *(const Cm::SpatialVector& s) const + { + const PxVec3 angular = topLeft * s.angular + topRight * s.linear; + const PxVec3 linear = bottomLeft * s.angular + topLeft.getTranspose() * s.linear; + return Cm::SpatialVector(linear, angular); + } + + //This assume angular is the top vector and linear is the bottom vector + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF operator *(const Cm::SpatialVectorF& s) const + { + const PxVec3 top = topLeft * s.top + topRight * s.bottom; + const PxVec3 bottom = bottomLeft * s.top + topLeft.transformTranspose(s.bottom); + + return Cm::SpatialVectorF(top, bottom); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector operator *(const Cm::UnAlignedSpatialVector& s) const + { + const PxVec3 top = topLeft * s.top + topRight * s.bottom; + const PxVec3 bottom = bottomLeft * s.top + topLeft.transformTranspose(s.bottom); + + return Cm::UnAlignedSpatialVector(top, bottom); + } + + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator *(const PxReal& s) const + { + const PxMat33 newTopLeft = topLeft * s; + const PxMat33 newTopRight = topRight * s; + const PxMat33 newBottomLeft = bottomLeft * s; + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator -(const SpatialMatrix& s) const + { + PxMat33 newTopLeft = topLeft - s.topLeft; + PxMat33 newTopRight = topRight - s.topRight; + PxMat33 newBottomLeft = bottomLeft - s.bottomLeft; + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator +(const SpatialMatrix& s) const + { + PxMat33 newTopLeft = topLeft + s.topLeft; + PxMat33 newTopRight = topRight + s.topRight; + PxMat33 newBottomLeft = bottomLeft + s.bottomLeft; + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator-() + { + PxMat33 newTopLeft = -topLeft; + PxMat33 newTopRight = -topRight; + PxMat33 newBottomLeft = -bottomLeft; + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE void operator +=(const SpatialMatrix& s) + { + topLeft += s.topLeft; + topRight += s.topRight; + bottomLeft += s.bottomLeft; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator *(const SpatialMatrix& s) + { + PxMat33 sBottomRight = s.topLeft.getTranspose(); + PxMat33 bottomRight = topLeft.getTranspose(); + + PxMat33 newTopLeft = topLeft * s.topLeft + topRight * s.bottomLeft; + PxMat33 newTopRight = topLeft * s.topRight + topRight * sBottomRight; + PxMat33 newBottomLeft = bottomLeft * s.topLeft + bottomRight * s.bottomLeft; + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + static SpatialMatrix constructSpatialMatrix(const Cm::SpatialVector& Is, const Cm::SpatialVector& stI) + { + //construct top left + PxVec3 tLeftC0 = Is.angular * stI.angular.x; + PxVec3 tLeftC1 = Is.angular * stI.angular.y; + PxVec3 tLeftC2 = Is.angular * stI.angular.z; + + PxMat33 topLeft(tLeftC0, tLeftC1, tLeftC2); + + //construct top right + PxVec3 tRightC0 = Is.angular * stI.linear.x; + PxVec3 tRightC1 = Is.angular * stI.linear.y; + PxVec3 tRightC2 = Is.angular * stI.linear.z; + PxMat33 topRight(tRightC0, tRightC1, tRightC2); + + //construct bottom left + PxVec3 bLeftC0 = Is.linear * stI.angular.x; + PxVec3 bLeftC1 = Is.linear * stI.angular.y; + PxVec3 bLeftC2 = Is.linear * stI.angular.z; + PxMat33 bottomLeft(bLeftC0, bLeftC1, bLeftC2); + + return SpatialMatrix(topLeft, topRight, bottomLeft); + } + + static PX_CUDA_CALLABLE SpatialMatrix constructSpatialMatrix(const Cm::SpatialVectorF& Is, const Cm::SpatialVectorF& stI) + { + //construct top left + PxVec3 tLeftC0 = Is.top * stI.top.x; + PxVec3 tLeftC1 = Is.top * stI.top.y; + PxVec3 tLeftC2 = Is.top * stI.top.z; + + PxMat33 topLeft(tLeftC0, tLeftC1, tLeftC2); + + //construct top right + PxVec3 tRightC0 = Is.top * stI.bottom.x; + PxVec3 tRightC1 = Is.top * stI.bottom.y; + PxVec3 tRightC2 = Is.top * stI.bottom.z; + PxMat33 topRight(tRightC0, tRightC1, tRightC2); + + //construct bottom left + PxVec3 bLeftC0 = Is.bottom * stI.top.x; + PxVec3 bLeftC1 = Is.bottom * stI.top.y; + PxVec3 bLeftC2 = Is.bottom * stI.top.z; + PxMat33 bottomLeft(bLeftC0, bLeftC1, bLeftC2); + + return SpatialMatrix(topLeft, topRight, bottomLeft); + } + + static PX_CUDA_CALLABLE SpatialMatrix constructSpatialMatrix(const Cm::SpatialVectorF* columns) + { + PxMat33 topLeft(columns[0].top, columns[1].top, columns[2].top); + PxMat33 bottomLeft(columns[0].bottom, columns[1].bottom, columns[2].bottom); + PxMat33 topRight(columns[3].top, columns[4].top, columns[5].top); + + return SpatialMatrix(topLeft, topRight, bottomLeft); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix getTranspose() + { + PxMat33 newTopLeft = topLeft.getTranspose(); + PxMat33 newTopRight = bottomLeft.getTranspose(); + PxMat33 newBottomLeft = topRight.getTranspose(); + //PxMat33 newBottomRight = bottomRight.getTranspose(); + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft);// , newBottomRight); + } + + //static bool isTranspose(const PxMat33& a, const PxMat33& b) + //{ + // PxReal eps = 0.01f; + // //test bottomRight is the transpose of topLeft + // for (PxU32 i = 0; i <3; ++i) + // { + // for (PxU32 j = 0; j <3; ++j) + // { + // if (PxAbs(a[i][j] - b[j][i]) > eps) + // return false; + // } + // } + + // return true; + //} + + PX_FORCE_INLINE bool isIdentity(const PxMat33& matrix) + { + PxReal eps = 0.00001f; + float x = PxAbs(1.f - matrix.column0.x); + float y = PxAbs(1.f - matrix.column1.y); + float z = PxAbs(1.f - matrix.column2.z); + bool identity = ((x < eps) && PxAbs(matrix.column0.y - 0.f) < eps && PxAbs(matrix.column0.z - 0.f) < eps) && + (PxAbs(matrix.column1.x - 0.f) < eps && (y < eps) && PxAbs(matrix.column1.z - 0.f) < eps) && + (PxAbs(matrix.column2.x - 0.f) < eps && PxAbs(matrix.column2.y - 0.f) < eps && (z < eps)); + + return identity; + } + + PX_FORCE_INLINE bool isZero(const PxMat33& matrix) + { + PxReal eps = 0.0001f; + for (PxU32 i = 0; i < 3; ++i) + { + for (PxU32 j = 0; j < 3; ++j) + { + if (PxAbs(matrix[i][j]) > eps) + return false; + } + } + + return true; + } + + PX_FORCE_INLINE bool isIdentity() + { + bool topLeftIsIdentity = isIdentity(topLeft); + + bool topRightIsZero = isZero(topRight); + + bool bottomLeftIsZero = isZero(bottomLeft); + + return topLeftIsIdentity && topRightIsZero && bottomLeftIsZero; + } + + static bool isEqual(const PxMat33& s0, const PxMat33& s1) + { + PxReal eps = 0.00001f; + for (PxU32 i = 0; i < 3; ++i) + { + for (PxU32 j = 0; j < 3; ++j) + { + PxReal t = s0[i][j] - s1[i][j]; + if (PxAbs(t) > eps) + return false; + } + } + + return true; + } + + PX_FORCE_INLINE bool isEqual(const SpatialMatrix& s) + { + bool topLeftEqual = isEqual(topLeft, s.topLeft); + bool topRightEqual = isEqual(topRight, s.topRight); + bool bottomLeftEqual = isEqual(bottomLeft, s.bottomLeft); + + return topLeftEqual && topRightEqual && bottomLeftEqual; + } + + static PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33 invertSym33(const PxMat33& in) + { + PxVec3 v0 = in[1].cross(in[2]), + v1 = in[2].cross(in[0]), + v2 = in[0].cross(in[1]); + + PxReal det = v0.dot(in[0]); + + if (det != 0) + { + PxReal recipDet = 1.0f / det; + + return PxMat33(v0 * recipDet, + PxVec3(v0.y, v1.y, v1.z) * recipDet, + PxVec3(v0.z, v1.z, v2.z) * recipDet); + } + else + { + return PxMat33(PxIdentity); + } + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix invertInertia() + { + PxMat33 aa = bottomLeft, ll = topRight, la = topLeft; + + aa = (aa + aa.getTranspose())*0.5f; + ll = (ll + ll.getTranspose())*0.5f; + + PxMat33 AAInv = invertSym33(aa); + + PxMat33 z = -la * AAInv; + PxMat33 S = ll + z * la.getTranspose(); // Schur complement of mAA + + PxMat33 LL = invertSym33(S); + + PxMat33 LA = LL * z; + PxMat33 AA = AAInv + z.getTranspose() * LA; + + SpatialMatrix result(LA.getTranspose(), AA, LL);// , LA); + + return result; + } + + SpatialMatrix getInverse() + { + PxMat33 bottomRight = topLeft.getTranspose(); + + PxMat33 blInverse = bottomLeft.getInverse(); + PxMat33 lComp0 = blInverse * (-bottomRight); + PxMat33 lComp1 = topLeft * lComp0 + topRight; + + //This can be simplified + PxMat33 newBottomLeft = lComp1.getInverse(); + PxMat33 newTopLeft = lComp0 * newBottomLeft; + + PxMat33 trInverse = topRight.getInverse(); + PxMat33 rComp0 = trInverse * (-topLeft); + PxMat33 rComp1 = bottomLeft + bottomRight * rComp0; + + PxMat33 newTopRight = rComp1.getInverse(); + + return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft); + } + + void zero() + { + topLeft = PxMat33(PxZero); + topRight = PxMat33(PxZero); + bottomLeft = PxMat33(PxZero); + } + + }; + + struct Temp6x6Matrix; + + struct Temp6x3Matrix + { + PxReal column[3][6]; + public: + + Temp6x3Matrix() + { + + } + + Temp6x3Matrix(const Cm::SpatialVectorF* spatialAxis) + { + constructColumn(column[0], spatialAxis[0]); + constructColumn(column[1], spatialAxis[1]); + constructColumn(column[2], spatialAxis[2]); + } + + void constructColumn(PxReal* dest, const Cm::SpatialVectorF& v) + { + dest[0] = v.top.x; + dest[1] = v.top.y; + dest[2] = v.top.z; + + dest[3] = v.bottom.x; + dest[4] = v.bottom.y; + dest[5] = v.bottom.z; + } + + Temp6x6Matrix operator * (PxReal s[6][3]); + + ////s is 3x6 matrix + //PX_FORCE_INLINE Temp6x6Matrix operator * (PxReal s[6][3]) + //{ + // Temp6x6Matrix temp; + + // for (PxU32 i = 0; i < 6; ++i) + // { + // PxReal* tc = temp.column[i]; + + // for (PxU32 j = 0; j < 6; ++j) + // { + // tc[j] = 0.f; + // for (PxU32 k = 0; k < 3; ++k) + // { + // tc[j] += column[k][j] * s[i][k]; + // } + // } + // } + + // return temp; + //} + + PX_FORCE_INLINE Temp6x3Matrix operator * (const PxMat33& s) + { + Temp6x3Matrix temp; + + for (PxU32 i = 0; i < 3; ++i) + { + PxReal* tc = temp.column[i]; + PxVec3 sc = s[i]; + + for (PxU32 j = 0; j < 6; ++j) + { + tc[j] = 0.f; + for (PxU32 k = 0; k < 3; ++k) + { + tc[j] += column[k][j] * sc[k]; + } + } + } + + return temp; + } + + PX_FORCE_INLINE bool isColumnEqual(const PxU32 ind, const Cm::SpatialVectorF& col) + { + PxReal temp[6]; + constructColumn(temp, col); + const PxReal eps = 0.00001f; + for (PxU32 i = 0; i < 6; ++i) + { + const PxReal dif = column[ind][i] - temp[i]; + if (PxAbs(dif) > eps) + return false; + } + return true; + } + + }; + + + struct Temp6x6Matrix + { + PxReal column[6][6]; + public: + Temp6x6Matrix() + { + + } + + Temp6x6Matrix(const SpatialMatrix& spatialMatrix) + { + constructColumn(column[0], spatialMatrix.topLeft.column0, spatialMatrix.bottomLeft.column0); + constructColumn(column[1], spatialMatrix.topLeft.column1, spatialMatrix.bottomLeft.column1); + constructColumn(column[2], spatialMatrix.topLeft.column2, spatialMatrix.bottomLeft.column2); + + const PxMat33 bottomRight = spatialMatrix.getBottomRight(); + constructColumn(column[3], spatialMatrix.topRight.column0, bottomRight.column0); + constructColumn(column[4], spatialMatrix.topRight.column1, bottomRight.column1); + constructColumn(column[5], spatialMatrix.topRight.column2, bottomRight.column2); + } + + void constructColumn(const PxU32 ind, const PxReal* const values) + { + for (PxU32 i = 0; i < 6; ++i) + { + column[ind][i] = values[i]; + } + } + + void constructColumn(PxReal* dest, const PxVec3& top, const PxVec3& bottom) + { + dest[0] = top.x; + dest[1] = top.y; + dest[2] = top.z; + + dest[3] = bottom.x; + dest[4] = bottom.y; + dest[5] = bottom.z; + } + + Temp6x6Matrix getTranspose() const + { + Temp6x6Matrix temp; + for (PxU32 i = 0; i < 6; ++i) + { + for (PxU32 j = 0; j < 6; ++j) + { + temp.column[i][j] = column[j][i]; + } + } + return temp; + } + + PX_FORCE_INLINE Cm::SpatialVector operator * (const Cm::SpatialVector& s) const + { + Temp6x6Matrix tempMatrix = getTranspose(); + PxReal st[6]; + st[0] = s.angular.x; st[1] = s.angular.y; st[2] = s.angular.z; + st[3] = s.linear.x; st[4] = s.linear.y; st[5] = s.linear.z; + + PxReal result[6]; + for (PxU32 i = 0; i < 6; i++) + { + result[i] = 0; + for (PxU32 j = 0; j < 6; ++j) + { + result[i] += tempMatrix.column[i][j] * st[j]; + } + } + + + Cm::SpatialVector temp; + temp.angular.x = result[0]; temp.angular.y = result[1]; temp.angular.z = result[2]; + temp.linear.x = result[3]; temp.linear.y = result[4]; temp.linear.z = result[5]; + return temp; + } + + + PX_FORCE_INLINE Cm::SpatialVectorF operator * (const Cm::SpatialVectorF& s) const + { + PxReal st[6]; + st[0] = s.top.x; st[1] = s.top.y; st[2] = s.top.z; + st[3] = s.bottom.x; st[4] = s.bottom.y; st[5] = s.bottom.z; + + PxReal result[6]; + for (PxU32 i = 0; i < 6; ++i) + { + result[i] = 0.f; + for (PxU32 j = 0; j < 6; ++j) + { + result[i] += column[j][i] * st[j]; + } + } + + Cm::SpatialVectorF temp; + temp.top.x = result[0]; temp.top.y = result[1]; temp.top.z = result[2]; + temp.bottom.x = result[3]; temp.bottom.y = result[4]; temp.bottom.z = result[5]; + return temp; + } + + PX_FORCE_INLINE Temp6x3Matrix operator * (const Temp6x3Matrix& s) const + { + Temp6x3Matrix temp; + for (PxU32 i = 0; i < 3; ++i) + { + PxReal* result = temp.column[i]; + + const PxReal* input = s.column[i]; + + for (PxU32 j = 0; j < 6; ++j) + { + result[j] = 0.f; + for (PxU32 k = 0; k < 6; ++k) + { + result[j] += column[k][j] * input[k]; + } + } + } + + return temp; + + } + + PX_FORCE_INLINE Cm::SpatialVector spatialVectorMul(const Cm::SpatialVector& s) + { + PxReal st[6]; + st[0] = s.angular.x; st[1] = s.angular.y; st[2] = s.angular.z; + st[3] = s.linear.x; st[4] = s.linear.y; st[5] = s.linear.z; + + PxReal result[6]; + for (PxU32 i = 0; i < 6; ++i) + { + result[i] = 0.f; + for (PxU32 j = 0; j < 6; j++) + { + result[i] += column[i][j] * st[j]; + } + } + + Cm::SpatialVector temp; + temp.angular.x = result[0]; temp.angular.y = result[1]; temp.angular.z = result[2]; + temp.linear.x = result[3]; temp.linear.y = result[4]; temp.linear.z = result[5]; + return temp; + } + + PX_FORCE_INLINE bool isEqual(const Cm::SpatialVectorF* m) + { + PxReal temp[6]; + PxReal eps = 0.00001f; + for (PxU32 i = 0; i < 6; ++i) + { + temp[0] = m[i].top.x; temp[1] = m[i].top.y; temp[2] = m[i].top.z; + temp[3] = m[i].bottom.x; temp[4] = m[i].bottom.y; temp[5] = m[i].bottom.z; + + for (PxU32 j = 0; j < 6; ++j) + { + PxReal dif = column[i][j] - temp[j]; + if (PxAbs(dif) > eps) + return false; + } + } + + return true; + } + }; + + //s is 3x6 matrix + PX_FORCE_INLINE Temp6x6Matrix Temp6x3Matrix::operator * (PxReal s[6][3]) + { + Temp6x6Matrix temp; + + for (PxU32 i = 0; i < 6; ++i) + { + PxReal* tc = temp.column[i]; + + for (PxU32 j = 0; j < 6; ++j) + { + tc[j] = 0.f; + for (PxU32 k = 0; k < 3; ++k) + { + tc[j] += column[k][j] * s[i][k]; + } + } + } + + return temp; + } + + PX_FORCE_INLINE void calculateNewVelocity(const PxTransform& newTransform, const PxTransform& oldTransform, + const PxReal dt, PxVec3& linear, PxVec3& angular) + { + //calculate the new velocity + linear = (newTransform.p - oldTransform.p) / dt; + PxQuat quat = newTransform.q * oldTransform.q.getConjugate(); + + if (quat.w < 0) //shortest angle. + quat = -quat; + + PxReal angle; + PxVec3 axis; + quat.toRadiansAndUnitAxis(angle, axis); + angular = (axis * angle) / dt; + } + + + // generates a pair of quaternions (swing, twist) such that in = swing * twist, with + // swing.x = 0 + // twist.y = twist.z = 0, and twist is a unit quat + PX_CUDA_CALLABLE PX_FORCE_INLINE void separateSwingTwist(const PxQuat& q, PxQuat& twist, PxQuat& swing1, PxQuat& swing2) + { + twist = q.x != 0.0f ? PxQuat(q.x, 0, 0, q.w).getNormalized() : PxQuat(PxIdentity); + PxQuat swing = q * twist.getConjugate(); + swing1 = swing.y != 0.f ? PxQuat(0.f, swing.y, 0.f, swing.w).getNormalized() : PxQuat(PxIdentity); + swing = swing * swing1.getConjugate(); + swing2 = swing.z != 0.f ? PxQuat(0.f, 0.f, swing.z, swing.w).getNormalized() : PxQuat(PxIdentity); + } + + +} //namespace Dy + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h new file mode 100644 index 000000000..70e0511bd --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h @@ -0,0 +1,42 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_SLEEPING_CONFIGULATION_H +#define PXD_SLEEPING_CONFIGULATION_H + +#define PXD_FREEZE_INTERVAL 1.5f +#define PXD_FREE_EXIT_THRESHOLD 4.f +#define PXD_FREEZE_TOLERANCE 0.25f + +#define PXD_SLEEP_DAMPING 0.5f +#define PXD_ACCEL_LOSS 0.9f +#define PXD_FREEZE_SCALE 0.1f + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h b/src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h new file mode 100644 index 000000000..b44a7eb9e --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h @@ -0,0 +1,280 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_THRESHOLDTABLE_H +#define PXD_THRESHOLDTABLE_H +#include "Ps.h" +#include "PsArray.h" +#include "CmPhysXCommon.h" +#include "PsAllocator.h" +#include "PsHash.h" +#include "foundation/PxMemory.h" +#include "PxsIslandNodeIndex.h" + +namespace physx +{ + +class PxsRigidBody; + +namespace Sc +{ + class ShapeInteraction; +} + +namespace Dy +{ + +struct ThresholdStreamElement +{ + Sc::ShapeInteraction* shapeInteraction; //4 8 + PxReal normalForce; //8 12 + PxReal threshold; //12 16 + IG::NodeIndex nodeIndexA; //this is the unique node index in island gen which corresonding to that body and it is persistent 16 20 + IG::NodeIndex nodeIndexB; //This is the unique node index in island gen which corresonding to that body and it is persistent 20 24 + PxReal accumulatedForce; //24 28 + PxU32 pad; //28 32 + +#if !PX_P64_FAMILY + PxU32 pad1; //32 +#endif // !PX_X64 + + PX_CUDA_CALLABLE bool operator <= (const ThresholdStreamElement& otherPair) const + { + return ((nodeIndexA < otherPair.nodeIndexA) ||(nodeIndexA == otherPair.nodeIndexA && nodeIndexB <= otherPair.nodeIndexB)); + } + +}; + +typedef Ps::Array ThresholdArray; + +class ThresholdStream : public ThresholdArray +{ +public: + ThresholdStream(Ps::VirtualAllocatorCallback& allocatorCallback) : ThresholdArray(Ps::VirtualAllocator(&allocatorCallback)) + { + } + +}; + +class ThresholdTable +{ +public: + + ThresholdTable() + : mBuffer(NULL), + mHash(NULL), + mHashSize(0), + mHashCapactiy(0), + mPairs(NULL), + mNexts(NULL), + mPairsSize(0), + mPairsCapacity(0) + { + } + + ~ThresholdTable() + { + if(mBuffer) PX_FREE(mBuffer); + } + + void build(const ThresholdStream& stream); + + bool check(const ThresholdStream& stream, const PxU32 nodexIndexA, const PxU32 nodexIndexB, PxReal dt); + + bool check(const ThresholdStream& stream, const ThresholdStreamElement& elem, PxU32& thresholdIndex); + +//private: + + static const PxU32 NO_INDEX = 0xffffffff; + + struct Pair + { + PxU32 thresholdStreamIndex; + PxReal accumulatedForce; + //PxU32 next; // hash key & next ptr + }; + + PxU8* mBuffer; + + PxU32* mHash; + PxU32 mHashSize; + PxU32 mHashCapactiy; + + Pair* mPairs; + PxU32* mNexts; + PxU32 mPairsSize; + PxU32 mPairsCapacity; +}; + +namespace +{ + static PX_FORCE_INLINE PxU32 computeHashKey(const PxU32 nodeIndexA, const PxU32 nodeIndexB, const PxU32 hashCapacity) + { + return (Ps::hash(PxU64(nodeIndexA)<<32 | PxU64(nodeIndexB)) % hashCapacity); + } +} + +inline bool ThresholdTable::check(const ThresholdStream& stream, const ThresholdStreamElement& elem, PxU32& thresholdIndex) +{ + PxU32* PX_RESTRICT hashes = mHash; + PxU32* PX_RESTRICT nextIndices = mNexts; + Pair* PX_RESTRICT pairs = mPairs; + + PX_ASSERT(elem.nodeIndexA < elem.nodeIndexB); + PxU32 hashKey = computeHashKey(elem.nodeIndexA.index(), elem.nodeIndexB.index(), mHashSize); + + PxU32 pairIndex = hashes[hashKey]; + + while(NO_INDEX != pairIndex) + { + Pair& pair = pairs[pairIndex]; + const PxU32 thresholdStreamIndex = pair.thresholdStreamIndex; + PX_ASSERT(thresholdStreamIndex < stream.size()); + const ThresholdStreamElement& otherElement = stream[thresholdStreamIndex]; + if(otherElement.nodeIndexA==elem.nodeIndexA && otherElement.nodeIndexB==elem.nodeIndexB && otherElement.shapeInteraction == elem.shapeInteraction) + { + thresholdIndex = thresholdStreamIndex; + return true; + } + pairIndex = nextIndices[pairIndex]; + } + + thresholdIndex = NO_INDEX; + return false; +} + + +inline void ThresholdTable::build(const ThresholdStream& stream) +{ + //Handle the case of an empty stream. + if(0==stream.size()) + { + mPairsSize=0; + mPairsCapacity=0; + mHashSize=0; + mHashCapactiy=0; + if(mBuffer) PX_FREE(mBuffer); + mBuffer = NULL; + return; + } + + //Realloc/resize if necessary. + const PxU32 pairsCapacity = stream.size(); + const PxU32 hashCapacity = pairsCapacity*2+1; + if((pairsCapacity > mPairsCapacity) || (pairsCapacity < (mPairsCapacity >> 2))) + { + if(mBuffer) PX_FREE(mBuffer); + const PxU32 pairsByteSize = sizeof(Pair)*pairsCapacity; + const PxU32 nextsByteSize = sizeof(PxU32)*pairsCapacity; + const PxU32 hashByteSize = sizeof(PxU32)*hashCapacity; + const PxU32 totalByteSize = pairsByteSize + nextsByteSize + hashByteSize; + mBuffer = reinterpret_cast(PX_ALLOC(totalByteSize, "PxThresholdStream")); + + PxU32 offset = 0; + mPairs = reinterpret_cast(mBuffer + offset); + offset += pairsByteSize; + mNexts = reinterpret_cast(mBuffer + offset); + offset += nextsByteSize; + mHash = reinterpret_cast(mBuffer + offset); + offset += hashByteSize; + PX_ASSERT(totalByteSize == offset); + + mPairsCapacity = pairsCapacity; + mHashCapactiy = hashCapacity; + } + + + //Set each entry of the hash table to 0xffffffff + PxMemSet(mHash, 0xff, sizeof(PxU32)*hashCapacity); + + //Init the sizes of the pairs array and hash array. + mPairsSize = 0; + mHashSize = hashCapacity; + + PxU32* PX_RESTRICT hashes = mHash; + PxU32* PX_RESTRICT nextIndices = mNexts; + Pair* PX_RESTRICT pairs = mPairs; + + //Add all the pairs from the stream. + PxU32 pairsSize = 0; + for(PxU32 i = 0; i < pairsCapacity; i++) + { + const ThresholdStreamElement& element = stream[i]; + const IG::NodeIndex nodeIndexA = element.nodeIndexA; + const IG::NodeIndex nodeIndexB = element.nodeIndexB; + + const PxF32 force = element.normalForce; + + PX_ASSERT(nodeIndexA < nodeIndexB); + + const PxU32 hashKey = computeHashKey(nodeIndexA.index(), nodeIndexB.index(), hashCapacity); + + //Get the index of the first pair found that resulted in a hash that matched hashKey. + PxU32 prevPairIndex = hashKey; + PxU32 pairIndex = hashes[hashKey]; + + //Search through all pairs found that resulted in a hash that matched hashKey. + //Search until the exact same body pair is found. + //Increment the accumulated force if the exact same body pair is found. + while(NO_INDEX != pairIndex) + { + Pair& pair = pairs[pairIndex]; + const PxU32 thresholdStreamIndex = pair.thresholdStreamIndex; + PX_ASSERT(thresholdStreamIndex < stream.size()); + const ThresholdStreamElement& otherElement = stream[thresholdStreamIndex]; + if(nodeIndexA == otherElement.nodeIndexA && nodeIndexB==otherElement.nodeIndexB) + { + pair.accumulatedForce += force; + prevPairIndex = NO_INDEX; + pairIndex = NO_INDEX; + break; + } + prevPairIndex = pairIndex; + pairIndex = nextIndices[pairIndex]; + } + + if(NO_INDEX != prevPairIndex) + { + nextIndices[pairsSize] = hashes[hashKey]; + hashes[hashKey] = pairsSize; + Pair& newPair = pairs[pairsSize]; + newPair.thresholdStreamIndex = i; + newPair.accumulatedForce = force; + pairsSize++; + } + } + mPairsSize = pairsSize; +} + +} + +} + +#endif //DY_THRESHOLDTABLE_H diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h new file mode 100644 index 000000000..2a9bbe755 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h @@ -0,0 +1,492 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXDV_ARTICULATION_H +#define PXDV_ARTICULATION_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsVecMath.h" +#include "CmUtils.h" +#include "CmSpatialVector.h" +#include "PxArticulationJoint.h" +#include "PxArticulation.h" +#include "foundation/PxMemory.h" +#include "DyArticulationCore.h" +#include "DyArticulationJointCore.h" + + +namespace physx +{ + + class PxcRigidBody; + struct PxsBodyCore; + + class PxcConstraintBlockStream; + class PxcRigidBody; + class PxsConstraintBlockManager; + struct PxSolverConstraintDesc; + + + namespace Sc + { + class ArticulationSim; + } + + + namespace Dy + { + + struct PxcFsScratchAllocator; + struct PxTGSSolverConstraintDesc; + + static const size_t DY_ARTICULATION_MAX_SIZE = 64; + struct ArticulationJointTransforms; + class ArticulationJointCoreData; + struct Constraint; + class Context; + + struct ArticulationJointCore : public ArticulationJointCoreBase + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + // drive model + PxQuat targetPosition; + PxVec3 targetVelocity; + + PxReal spring;//old + PxReal damping;//old + + PxReal internalCompliance;//old + PxReal externalCompliance;//old + + // limit model + + PxReal swingLimitContactDistance;//old + + PxReal tangentialStiffness;//old + PxReal tangentialDamping;//old + + bool swingLimited;//old + bool twistLimited;//old + + PxU8 driveType; //both + + PxReal twistLimitContactDistance; //old + + PxReal tanQSwingY;//old + PxReal tanQSwingZ;//old + PxReal tanQSwingPad;//old + PxReal tanQTwistHigh;//old + PxReal tanQTwistLow;//old + PxReal tanQTwistPad;//old + + ArticulationJointCore() + { + //Cm::markSerializedMem(this, sizeof(ArticulationJointCore)); + parentPose = PxTransform(PxIdentity); + childPose = PxTransform(PxIdentity); + internalCompliance = 0; + externalCompliance = 0; + swingLimitContactDistance = 0.05f; + twistLimitContactDistance = 0.05f; + + driveType = PxArticulationJointDriveType::eTARGET; + + jointType = PxArticulationJointType::eFIX; + + for (PxU32 i = 0; i < 6; ++i) + { + motion[i] = PxArticulationMotion::eLOCKED; + } + + dirtyFlag = ArticulationJointCoreDirtyFlag::eMOTION; + + //initial parent to child + relativeQuat = PxQuat(PxIdentity); + + jointOffset = 0; + + } + + ArticulationJointCore(const PxTransform& parentFrame, + const PxTransform& childFrame) + { + parentPose = parentFrame; + childPose = childFrame; + + //the old articulation is eTARGET + driveType = PxArticulationJointDriveType::eTARGET; + + spring = 0.0f; + damping = 0.0f; + + internalCompliance = 1.0f; + externalCompliance = 1.0f; + + for (PxU32 i = 0; i < 6; ++i) + { + limits[i].low = 0.f; + limits[i].high = 0.f; + drives[i].stiffness = 0.f; + drives[i].damping = 0.f; + drives[i].maxForce = 0.f; + targetP[i] = 0.f; + targetV[i] = 0.f; + } + + + PxReal swingYLimit = PxPi / 4; + PxReal swingZLimit = PxPi / 4; + + limits[PxArticulationAxis::eSWING1].low = swingYLimit; + limits[PxArticulationAxis::eSWING2].low = swingZLimit; + + swingLimitContactDistance = 0.05f; + swingLimited = false; + tangentialStiffness = 0.0f; + tangentialDamping = 0.0f; + + PxReal twistLimitLow = -PxPi / 4; + PxReal twistLimitHigh = PxPi / 4; + limits[PxArticulationAxis::eTWIST].low = twistLimitLow; + limits[PxArticulationAxis::eTWIST].high = twistLimitHigh; + twistLimitContactDistance = 0.05f; + twistLimited = false; + + + tanQSwingY = PxTan(swingYLimit / 4); + tanQSwingZ = PxTan(swingZLimit / 4); + tanQSwingPad = PxTan(swingLimitContactDistance / 4); + + tanQTwistHigh = PxTan(twistLimitHigh / 4); + tanQTwistLow = PxTan(twistLimitLow / 4); + tanQTwistPad = PxTan(twistLimitContactDistance / 4); + + + + prismaticLimited = false; + + frictionCoefficient = 0.f; + + for (PxU32 i = 0; i < 6; ++i) + { + motion[i] = PxArticulationMotion::eLOCKED; + } + + dirtyFlag = ArticulationJointCoreDirtyFlag::eMOTION; + + //initial parent to child + relativeQuat = (childFrame.q * (parentFrame.q.getConjugate())).getNormalized(); + + jointOffset = 0; + + } + + void setJointPose(ArticulationJointCoreData& jointDatum); + + // PX_SERIALIZATION + ArticulationJointCore(const PxEMPTY&) {} + //~PX_SERIALIZATION + + }; + + struct ArticulationLoopConstraint + { + public: + PxU32 linkIndex0; + PxU32 linkIndex1; + Dy::Constraint* constraint; + + }; + +#define DY_ARTICULATION_LINK_NONE 0xffffffff + + typedef PxU64 ArticulationBitField; + + struct ArticulationLink + { + ArticulationBitField children; // child bitmap + ArticulationBitField pathToRoot; // path to root, including link and root + PxcRigidBody* body; + PxsBodyCore* bodyCore; + ArticulationJointCore* inboundJoint; + PxU32 parent; + }; + + typedef size_t ArticulationLinkHandle; + + class ArticulationV; + + struct ArticulationSolverDesc + { + ArticulationV* articulation; + ArticulationLink* links; + Cm::SpatialVectorV* motionVelocity; + Cm::SpatialVector* acceleration; + PxTransform* poses; + PxQuat* deltaQ; + physx::shdfnd::aos::Mat33V* externalLoads; + physx::shdfnd::aos::Mat33V* internalLoads; + const ArticulationCore* core; + char* scratchMemory; + PxU16 totalDataSize; + PxU16 solverDataSize; + PxU8 linkCount; + PxU8 numInternalConstraints; + PxU16 scratchMemorySize; + }; + + struct PxcFsScratchAllocator + { + char* base; + size_t size; + size_t taken; + PxcFsScratchAllocator(char* p, size_t s) : base(p), size(s), taken(0) {} + + template + static size_t sizeof16() + { + return (sizeof(T) + 15)&~15; + } + + template T* alloc(PxU32 count) + { + size_t s = sizeof16(); + PX_ASSERT(taken + s*count <= size); + T* result = reinterpret_cast(base + taken); + taken += s*count; + return result; + } + }; + + + static const size_t DY_ARTICULATION_IDMASK = DY_ARTICULATION_MAX_SIZE - 1; + +#if PX_VC +#pragma warning(push) +#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + PX_ALIGN_PREFIX(64) + class ArticulationV + { + public: + + // public interface + + ArticulationV(Sc::ArticulationSim* sim, PxArticulationBase::Enum type) : mArticulationSim(sim), + mContext(NULL), + mType(type), + mUpdateSolverData(true), + mDirty(false), + mMaxDepth(0) + {} + + virtual ~ArticulationV() {} + + virtual void onUpdateSolverDesc() + { + } + + virtual bool resize(const PxU32 linkCount); + + virtual void addBody() + { + mAcceleration.pushBack(Cm::SpatialVector(PxVec3(0.f), PxVec3(0.f))); + mUpdateSolverData = true; + } + + virtual void removeBody() + { + mUpdateSolverData = true; + } + + bool updateSolverData() {return mUpdateSolverData;} + + void setDirty(const bool dirty) { mDirty = dirty; } + bool getDirty() { return mDirty; } + + PX_FORCE_INLINE PxU32 getMaxDepth() { return mMaxDepth; } + PX_FORCE_INLINE void setMaxDepth(const PxU32 depth) { mMaxDepth = depth; } + + // solver methods + PX_FORCE_INLINE PxU32 getLinkIndex(ArticulationLinkHandle handle) const { return PxU32(handle&DY_ARTICULATION_IDMASK); } + PX_FORCE_INLINE PxU32 getBodyCount() const { return mSolverDesc.linkCount; } + PX_FORCE_INLINE PxU32 getSolverDataSize() const { return mSolverDesc.solverDataSize; } + PX_FORCE_INLINE PxU32 getTotalDataSize() const { return mSolverDesc.totalDataSize; } + PX_FORCE_INLINE void getSolverDesc(ArticulationSolverDesc& d) const { d = mSolverDesc; } + PX_FORCE_INLINE ArticulationSolverDesc& getSolverDesc() { return mSolverDesc; } + + PX_FORCE_INLINE const ArticulationCore* getCore() const { return mSolverDesc.core; } + PX_FORCE_INLINE PxU16 getIterationCounts() const { return mSolverDesc.core->solverIterationCounts; } + + PX_FORCE_INLINE Sc::ArticulationSim* getArticulationSim() const { return mArticulationSim; } + + PX_FORCE_INLINE PxU32 getType() const { return mType; } + + // get data sizes for allocation at higher levels + virtual void getDataSizes(PxU32 linkCount, + PxU32 &solverDataSize, + PxU32& totalSize, + PxU32& scratchSize) = 0; + + virtual PxU32 getDofs() { return 0; } + + virtual PxU32 getDof(const PxU32 /*linkID*/) { return 0; } + + virtual void applyCache(PxArticulationCache& /*cache*/, const PxArticulationCacheFlags /*flag*/) {} + + virtual void copyInternalStateToCache(PxArticulationCache&/* cache*/, const PxArticulationCacheFlags /*flag*/) {} + + virtual void packJointData(const PxReal* /*maximum*/, PxReal* /*reduced*/) {} + + virtual void unpackJointData(const PxReal* /*reduced*/, PxReal* /*maximum*/) {} + + virtual void initializeCommonData() {} + + virtual void getGeneralizedGravityForce(const PxVec3& /*gravity*/, PxArticulationCache& /*cache*/) {} + + virtual void getCoriolisAndCentrifugalForce(PxArticulationCache& /*cache*/) {} + + virtual void getGeneralizedExternalForce(PxArticulationCache& /*cache*/) {} + + virtual void getJointAcceleration(const PxVec3& /*gravity*/, PxArticulationCache& /*cache*/){} + + virtual void getJointForce(PxArticulationCache& /*cache*/){} + + virtual void getKinematicJacobian(const PxU32 /*linkID*/, PxArticulationCache& /*cache*/){} + + virtual void getCoefficentMatrix(const PxReal /*dt*/, const PxU32 /*linkID*/, const PxContactJoint* /*joints*/, const PxU32 /*nbContacts*/, PxArticulationCache& /*cache*/){} + + virtual void getCoefficentMatrixWithLoopJoints(ArticulationLoopConstraint* /*lConstraints*/, const PxU32 /*nbJoints*/, PxArticulationCache& /*cache*/) {} + + virtual bool getLambda(ArticulationLoopConstraint* /*lConstraints*/, const PxU32 /*nbJoints*/, + PxArticulationCache& /*cache*/, PxArticulationCache& /*initialState*/, const PxReal* /*jointTorque*/, + const PxVec3& /*gravity*/, const PxU32 /*maxIter*/) { return false; } + + virtual void getGeneralizedMassMatrix(PxArticulationCache& /*cache*/){} + virtual void getGeneralizedMassMatrixCRB(PxArticulationCache& /*cache*/){} + + virtual void teleportRootLink(){} + + virtual void getImpulseResponse( + PxU32 linkID, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaV) const = 0; + + virtual void getImpulseSelfResponse( + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1) const = 0; + + + virtual Cm::SpatialVectorV getLinkVelocity(const PxU32 linkID) const = 0; + + virtual Cm::SpatialVectorV getLinkMotionVector(const PxU32 linkID) const = 0; + + virtual PxReal getLinkMaxPenBias(const PxU32 linkID) const = 0; + + virtual void pxcFsApplyImpulse(PxU32 linkID, Ps::aos::Vec3V linear, + Ps::aos::Vec3V angular, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) = 0; + + virtual void pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, + const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, + const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) = 0; + + virtual void solveInternalConstraints(const PxReal dt, const PxReal invDt, Cm::SpatialVectorF* impulses, Cm::SpatialVectorF* DeltaV, + bool velIteration) = 0; + + virtual Cm::SpatialVectorV pxcFsGetVelocity(PxU32 linkID) = 0; + + virtual void pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1) = 0; + + virtual Cm::SpatialVectorV pxcFsGetVelocityTGS(PxU32 linkID) = 0; + + virtual const PxTransform& getCurrentTransform(PxU32 linkID) const= 0; + + virtual const PxQuat& getDeltaQ(PxU32 linkID) const = 0; + + //this is called by island gen to determine whether the articulation should be awake or sleep + virtual Cm::SpatialVector getMotionVelocity(const PxU32 linkID) const = 0; + + void setDyContext(Dy::Context* context) { mContext = context; } + //These variables are used in the constraint partition + PxU16 maxSolverFrictionProgress; + PxU16 maxSolverNormalProgress; + PxU32 solverProgress; + PxU8 numTotalConstraints; + + protected: + Sc::ArticulationSim* mArticulationSim; + Dy::Context* mContext; + PxU32 mType; + ArticulationSolverDesc mSolverDesc; + + Ps::Array mAcceleration; // supplied by Sc-layer to feed into articulations + + bool mUpdateSolverData; + bool mDirty; //any of links update configulations, the boolean will be set to true + PxU32 mMaxDepth; + + private: + PX_NOCOPY(ArticulationV) + } PX_ALIGN_SUFFIX(64); + +#if PX_VC +#pragma warning(pop) +#endif + + PX_FORCE_INLINE ArticulationV* getArticulation(ArticulationLinkHandle handle) + { + return reinterpret_cast(handle & ~DY_ARTICULATION_IDMASK); + } + + PX_FORCE_INLINE bool isArticulationRootLink(ArticulationLinkHandle handle) + { + return !(handle & DY_ARTICULATION_IDMASK); + } + + + } + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp new file mode 100644 index 000000000..d3bee470d --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp @@ -0,0 +1,1665 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsMathUtils.h" +#include "CmConeLimitHelper.h" +#include "DySolverConstraint1D.h" +#include "DyArticulation.h" +#include "DyArticulationHelper.h" +#include "PxsRigidBody.h" +#include "PxcConstraintBlockStream.h" +#include "DyArticulationContactPrep.h" +#include "DyDynamics.h" +#include "DyArticulationReference.h" +#include "DyArticulationPImpl.h" +#include +#include "DyArticulationUtils.h" +#include "foundation/PxProfiler.h" +#include "DyArticulationFnsSimd.h" +#include "DyTGSDynamics.h" +#include "common/PxProfileZone.h" + + +using namespace physx; + +// we encode articulation link handles in the lower bits of the pointer, so the +// articulation has to be aligned, which in an aligned pool means we need to size it +// appropriately + +namespace physx +{ + namespace Dy + { + void SolverCoreRegisterArticulationFns(); + + void SolverCoreRegisterArticulationFnsCoulomb(); + + void PxcFsFlushVelocity(FsData& matrix); + + // we pass this around by value so that when we return from a function the size is unaltered. That means we don't preserve state + // across functions - even though that could be handy to preserve baseInertia and jointTransforms across the solver so that if we + // need to run position projection positions they don't get recomputed. + + void PxcLtbFactor(FsData& m) + { + typedef ArticulationFnsSimd Fns; + LtbRow* rows = getLtbRows(m); + + for (PxU32 i = m.linkCount; --i>0;) + { + LtbRow& b = rows[i]; + PxU32 p = m.parent[i]; + const FsInertia inertia = Fns::invertInertia(b.inertia); + const Mat33V jResponse = Fns::invertSym33(M33Neg(Fns::computeSIS(inertia, b.j1, b.j1))); + b.inertia = inertia; + rows[p].inertia = Fns::multiplySubtract(rows[p].inertia, jResponse, b.j0, b.j0); + b.jResponse = jResponse; + + } + rows[0].inertia = Fns::invertInertia(rows[0].inertia); + } + + +PX_COMPILE_TIME_ASSERT((sizeof(Articulation)&(DY_ARTICULATION_MAX_SIZE-1))==0); + +Articulation::Articulation(Sc::ArticulationSim* sim) +: ArticulationV(sim, PxArticulationBase::eMaximumCoordinate) +, mFsDataBytes(PX_DEBUG_EXP("Articulation::fsData")) +, mInternalLoads(PX_DEBUG_EXP("ScArticulationSim::internalLoads")) +, mExternalLoads(PX_DEBUG_EXP("ScArticulationSim::externalLoads")) +, mScratchMemory(PX_DEBUG_EXP("ScArticulationSim::scratchMemory")) +, mPose(PX_DEBUG_EXP("ScArticulationSim::poses")) +, mDeltaQ(PX_DEBUG_EXP("ScArticulationSim::poses")) +, mMotionVelocity(PX_DEBUG_EXP("ScArticulationSim::motion velocity")) +{ + PX_ASSERT((reinterpret_cast(this) & (DY_ARTICULATION_MAX_SIZE-1))==0); +} + +Articulation::~Articulation() +{ +} + + +#if DY_DEBUG_ARTICULATION + +void Articulation::computeResiduals(const Cm::SpatialVector *v, + const ArticulationJointTransforms* jointTransforms, + bool /*dump*/) const +{ + typedef ArticulationFnsScalar Fns; + + PxReal error = 0, energy = 0; + for(PxU32 i=1;ilinkCount;i++) + { + const ArticulationJointTransforms &b = jointTransforms[i]; + PxU32 parent = mSolverDesc->links[i].parent; + const ArticulationJointCore &j = *mSolverDesc->links[i].inboundJoint; + PX_UNUSED(j); + + Cm::SpatialVector residual = Fns::translateMotion(mSolverDesc->poses[i].p - b.cB2w.p, v[i]) + - Fns::translateMotion(mSolverDesc->poses[parent].p - b.cB2w.p, v[parent]); + + error += residual.linear.magnitudeSquared(); + energy += residual.angular.magnitudeSquared(); + + } +// if(dump) + printf("Energy %f, Error %f\n", energy, error); +} + + +Cm::SpatialVector Articulation::computeMomentum(const FsInertia *inertia) const +{ + typedef ArticulationFnsScalar Fns; + + Cm::SpatialVector *velocity = reinterpret_cast(getVelocity(*mSolverDesc->fsData)); + Cm::SpatialVector m = Cm::SpatialVector::zero(); + for(PxU32 i=0;ilinkCount;i++) + m += Fns::translateForce(mSolverDesc->poses[i].p - mSolverDesc->poses[0].p, ArticulationFnsScalar::multiply(inertia[i], velocity[i])); + return m; +} + + + +void Articulation::checkLimits() const +{ + for(PxU32 i=1;ilinkCount;i++) + { + PxTransform cA2w = mSolverDesc->poses[mSolverDesc->links[i].parent].transform(mSolverDesc->links[i].inboundJoint->parentPose); + PxTransform cB2w = mSolverDesc->poses[i].transform(mSolverDesc->links[i].inboundJoint->childPose); + + PxTransform cB2cA = cA2w.transformInv(cB2w); + + // the relative quat must be the short way round for limits to work... + + if(cB2cA.q.w<0) + cB2cA.q = -cB2cA.q; + + const ArticulationJointCore& j = *mSolverDesc->links[i].inboundJoint; + + PxQuat swing, twist; + if(j.twistLimited || j.swingLimited) + Ps::separateSwingTwist(cB2cA.q, swing, twist); + + if(j.swingLimited) + { + PxReal swingLimitContactDistance = PxMin(j.swingYLimit, j.swingZLimit)/4; + + Cm::ConeLimitHelper eh(PxTan(j.swingYLimit/4), + PxTan(j.swingZLimit/4), + PxTan(swingLimitContactDistance/4)); + + PxVec3 axis; + PxReal error = 0.0f; + if(eh.getLimit(swing, axis, error)) + printf("%u, (%f, %f), %f, (%f, %f, %f), %f\n", i, j.swingYLimit, j.swingZLimit, swingLimitContactDistance, axis.x, axis.y, axis.z, error); + } + +// if(j.twistLimited) +// { +// PxReal tqTwistHigh = PxTan(j.twistLimitHigh/4), +// tqTwistLow = PxTan(j.twistLimitLow/4), +// twistPad = (tqTwistHigh - tqTwistLow)*0.25f; +// //twistPad = j.twistLimitContactDistance; +// +// PxVec3 axis = jointTransforms[i].cB2w.rotate(PxVec3(1,0,0)); +// PxReal tqPhi = Ps::tanHalf(twist.x, twist.w); +// +// if(tqPhi < tqTwistLow + twistPad) +// constraintData.pushBack(ConstraintData(-axis, -(tqTwistLow - tqPhi)*4)); +// +// if(tqPhi > tqTwistHigh - twistPad) +// constraintData.pushBack(ConstraintData(axis, (tqTwistHigh - tqPhi)*4)); +// } + } + puts(""); +} + +#endif + +bool Dy::Articulation::resize(const PxU32 linkCount) +{ + if (mUpdateSolverData) + { + if (linkCount != mSolverDesc.linkCount) + { + PxU32 solverDataSize, totalSize, scratchSize; + getDataSizes(linkCount, solverDataSize, totalSize, scratchSize); + + PX_ASSERT(mFsDataBytes.size() != totalSize); + PX_ASSERT(!(totalSize & 15) && !(solverDataSize & 15)); + mFsDataBytes.resize(totalSize); + + mExternalLoads.resize(linkCount, Ps::aos::M33Identity()); + mInternalLoads.resize(linkCount, Ps::aos::M33Identity()); + mPose.resize(linkCount, PxTransform(PxIdentity)); + + mDeltaQ.resize(linkCount, PxQuat(PxIdentity)); + + mSolverDesc.externalLoads = mExternalLoads.begin(); + mSolverDesc.internalLoads = mInternalLoads.begin(); + + mScratchMemory.resize(scratchSize); + mSolverDesc.scratchMemory = mScratchMemory.begin(); + mSolverDesc.scratchMemorySize = Ps::to16(scratchSize); + + mSolverDesc.solverDataSize = Ps::to16(solverDataSize); + mSolverDesc.totalDataSize = Ps::to16(totalSize); + mSolverDesc.poses = mPose.begin(); + mSolverDesc.deltaQ = mDeltaQ.begin(); + + mMotionVelocity.resize(linkCount, Cm::SpatialVector(PxVec3(0.0f), PxVec3(0.0f))); + + mSolverDesc.motionVelocity = mMotionVelocity.begin(); + + + } + Dy::ArticulationV::resize(linkCount); + + return true; + } + + return false; +} + +bool Dy::ArticulationV::resize(const PxU32 linkCount) +{ + if (!mUpdateSolverData) + return false; + + if (linkCount != mSolverDesc.linkCount) + { + mSolverDesc.acceleration = mAcceleration.begin(); + mSolverDesc.articulation = this; + + } + mUpdateSolverData = false; + return true; +} + + +void PxvRegisterArticulations() +{ + const PxU32 type = PxU32(PxArticulationBase::eMaximumCoordinate); + ArticulationPImpl::sComputeUnconstrainedVelocities[type] = &Articulation::computeUnconstrainedVelocities; + ArticulationPImpl::sUpdateBodies[type] = &Articulation::updateBodies; + ArticulationPImpl::sUpdateBodiesTGS[type] = &Articulation::updateBodies; + ArticulationPImpl::sSaveVelocity[type] = &Articulation::saveVelocity; + ArticulationPImpl::sSaveVelocityTGS[type] = &Articulation::saveVelocityTGS; + + ArticulationPImpl::sUpdateDeltaMotion[type] = &Articulation::recordDeltaMotion; + ArticulationPImpl::sDeltaMotionToMotionVel[type] = &Articulation::deltaMotionToMotionVelocity; + ArticulationPImpl::sComputeUnconstrainedVelocitiesTGS[type] = &Articulation::computeUnconstrainedVelocitiesTGS; + ArticulationPImpl::sSetupInternalConstraintsTGS[type] = &Articulation::setupSolverConstraintsTGS; + + SolverCoreRegisterArticulationFns(); + SolverCoreRegisterArticulationFnsCoulomb(); +} + +void Articulation::getDataSizes(PxU32 linkCount, PxU32 &solverDataSize, PxU32& totalSize, PxU32& scratchSize) +{ + solverDataSize = sizeof(FsData) // header + + sizeof(Cm::SpatialVectorV) * linkCount // velocity + + sizeof(Cm::SpatialVectorV) * linkCount // deferredVelocity + + sizeof(Cm::SpatialVectorV) * linkCount // motion size + + sizeof(Vec3V) * linkCount // deferredSZ + + sizeof(PxReal) * ((linkCount + 15) & 0xFFFFFFF0) // The maxPenBias values + + sizeof(FsJointVectors) * linkCount // joint offsets + + sizeof(FsInertia) // featherstone root inverse inertia + + sizeof(FsRow) * linkCount; // featherstone matrix rows + + totalSize = solverDataSize + + sizeof(LtbRow) * linkCount // lagrange matrix rows + + sizeof(Cm::SpatialVectorV) * linkCount // ref velocity + + sizeof(FsRowAux) * linkCount; + + scratchSize = PxU32(sizeof(FsInertia)*linkCount*3 + + ((sizeof(ArticulationJointTransforms)+15)&~15) * linkCount + + sizeof(Mat33V) * linkCount + + ((sizeof(ArticulationJointTransforms)+15)&~15) * linkCount); + +} + +void Articulation::getImpulseResponse( + PxU32 linkID, + Cm::SpatialVectorF* /*Z*/, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaV) const +{ + const FsData& matrix = *getFsDataPtr(); + ArticulationHelper::getImpulseResponse(matrix, linkID, impulse, deltaV); +} + +void Articulation::getImpulseSelfResponse( + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* /*Z*/, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1) const +{ + const FsData& matrix = *getFsDataPtr(); + ArticulationHelper::getImpulseSelfResponse(matrix, linkID0, reinterpret_cast(impulse0), + reinterpret_cast(deltaV0), linkID1, + reinterpret_cast(impulse1), + reinterpret_cast(deltaV1)); +} + + +Cm::SpatialVectorV Articulation::getLinkVelocity(const PxU32 linkID) const +{ + FsData& matrix = *getFsDataPtr(); + Cm::SpatialVectorV* velocites = addAddr(&matrix, sizeof(FsData)); + return velocites[linkID]; +} + +Cm::SpatialVectorV Articulation::getLinkMotionVector(const PxU32 linkID) const +{ + FsData& matrix = *getFsDataPtr(); + Cm::SpatialVectorV* velocites = addAddr(getDeferredVel(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); + return velocites[linkID]; +} + +Cm::SpatialVector Articulation::getMotionVelocity(const PxU32 linkID) const +{ + PxVec3 linear, angular; + const Cm::SpatialVectorV& motionVelocity = mMotionVelocity[linkID]; + V3StoreU(motionVelocity.linear, linear); + V3StoreU(motionVelocity.angular, angular); + + return Cm::SpatialVector(linear, angular); +} + +PxReal Articulation::getLinkMaxPenBias(const PxU32 linkID) const +{ + FsData& matrix = *getFsDataPtr(); + PxReal* maxPenBias = addAddr(getDeferredSZ(matrix), sizeof(Vec3V) * matrix.linkCount); + return maxPenBias[linkID]; +} + + +PxU32 Articulation::getFsDataSize(PxU32 linkCount) +{ + return sizeof(FsInertia) + sizeof(FsRow) * linkCount; +} + +PxU32 Articulation::getLtbDataSize(PxU32 linkCount) +{ + return sizeof(LtbRow) * linkCount; +} + +void Articulation::setInertia(FsInertia& inertia, + const PxsBodyCore& body, + const PxTransform& pose) +{ + // assumes that elements that are supposed to be zero (i.e. la matrix and off diagonal elements of ll) are zero + + const PxMat33 R(pose.q); + const PxVec3& v = body.inverseInertia; + const PxReal m = 1.0f / body.inverseMass; + V3WriteX(inertia.ll.col0, m); + V3WriteY(inertia.ll.col1, m); + V3WriteZ(inertia.ll.col2, m); + + PX_ALIGN_PREFIX(16) PxMat33 PX_ALIGN_SUFFIX(16) alignedInertia = R * PxMat33::createDiagonal(PxVec3(1.0f / v.x, 1.0f / v.y, 1.0f / v.z)) * R.getTranspose(); + alignedInertia = (alignedInertia + alignedInertia.getTranspose())*0.5f; + inertia.aa = Mat33V_From_PxMat33(alignedInertia); +} + +void Articulation::setJointTransforms(ArticulationJointTransforms& transforms, + const PxTransform& parentPose, + const PxTransform& childPose, + const ArticulationJointCore& joint) +{ + transforms.cA2w = parentPose.transform(joint.parentPose); + transforms.cB2w = childPose.transform(joint.childPose); + transforms.cB2cA = transforms.cA2w.transformInv(transforms.cB2w); + if (transforms.cB2cA.q.w<0) // the relative quat must be the short way round for limits to work... + { + transforms.cB2cA.q = -transforms.cB2cA.q; + transforms.cB2w.q = -transforms.cB2w.q; + } +} + +void Articulation::prepareDataBlock(FsData& fsData, + const ArticulationLink* links, + PxU16 linkCount, + PxTransform* poses, + PxQuat* deltaQ, + FsInertia* baseInertia, + ArticulationJointTransforms* jointTransforms, + PxU32 expectedSize) +{ + PxU32 stateSize = sizeof(FsData) + + sizeof(Cm::SpatialVectorV) * linkCount + + sizeof(Cm::SpatialVectorV) * linkCount + + sizeof(Cm::SpatialVectorV) * linkCount + + sizeof(Vec3V) * linkCount + + sizeof(PxReal) * ((linkCount + 15) & 0xfffffff0); + + PxU32 jointVectorSize = sizeof(FsJointVectors) * linkCount; + + PxU32 fsDataSize = getFsDataSize(linkCount); + PxU32 ltbDataSize = getLtbDataSize(linkCount); + + PxU32 totalSize = stateSize + + jointVectorSize + + fsDataSize + + ltbDataSize + + sizeof(Cm::SpatialVectorV) * linkCount + + sizeof(FsRowAux) * linkCount; + + PX_UNUSED(totalSize); + PX_UNUSED(expectedSize); + PX_ASSERT(expectedSize == 0 || totalSize == expectedSize); + + PxMemZero(&fsData, stateSize); + fsData.jointVectorOffset = PxU16(stateSize); + fsData.fsDataOffset = PxU16(stateSize + jointVectorSize); + fsData.ltbDataOffset = PxU16(stateSize + jointVectorSize + fsDataSize); + fsData.linkCount = linkCount; + + for (PxU32 i = 1; i(getVelocity(fsData)); + Cm::SpatialVector* motionVector = reinterpret_cast(getMotionVector(fsData)); + + PxMemZero(baseInertia, sizeof(FsInertia)*linkCount); + + PxReal* maxPenBias = getMaxPenBias(fsData); + + for (PxU32 i = 0; i(links[i].inboundJoint); + setJointTransforms(jointTransforms[i], poses[links[i].parent], core.body2World, *inboundJoint); + } + } + + FsJointVectors* jointVectors = getJointVectors(fsData); + for (PxU32 i = 1; i Fns; + + PxU32 linkCount = fsData.linkCount; + FsRow* rows = getFsRows(fsData); + FsRowAux* aux = getAux(fsData); + const FsJointVectors* jointVectors = getJointVectors(fsData); + + rows[0].children = links[0].children; + rows[0].pathToRoot = 1; + + PX_ALIGN_PREFIX(16) PxVec4 v[] PX_ALIGN_SUFFIX(16) = { PxVec4(1.f,0,0,0), PxVec4(0,1.f,0,0), PxVec4(0,0,1.f,0) }; + const Vec3V* axes = reinterpret_cast(v); + + for (PxU32 i = 1; i0); + return 1.0f / compliance; +} + +void Articulation::prepareLtbMatrix(FsData& fsData, + const FsInertia* baseInertia, + const PxTransform* poses, + const ArticulationJointTransforms* jointTransforms, + PxReal recipDt) +{ + PxU32 linkCount = fsData.linkCount; + LtbRow* rows = getLtbRows(fsData); + + rows[0].inertia = baseInertia[0]; + + const PxVec3 axis[3] = { PxVec3(1.0f,0.0f,0.0f), PxVec3(0.0f,1.0f,0.0f), PxVec3(0.0f,0.0f,1.0f) }; + for (PxU32 i = 1; i Fns; + + const LtbRow* rows = getLtbRows(m); + PxMemZero(y, m.linkCount * sizeof(Cm::SpatialVectorV)); + + for (PxU32 i = m.linkCount; i-->1;) + { + const LtbRow& r = rows[i]; + const PxU32 p = m.parent[i]; + + const Vec3V t = V3Sub(b[i], Fns::axisDot(r.j1, y[i])); + b[i] = t; + y[p] = Fns::subtract(y[p], Fns::axisMultiply(r.j0, t)); + } + + y[0] = Fns::multiply(rows[0].inertia, y[0]); + + for (PxU32 i = 1; i Fns; + + FloatV isf[DY_ARTICULATION_MAX_SIZE]; + + for (PxU32 i = 1; i(linkCount); + FsInertia*PX_RESTRICT contribToParent = allocator.alloc(linkCount); + + const FsRow*PX_RESTRICT row = getFsRows(matrix); + const FsRowAux*PX_RESTRICT aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_UNUSED(row); + + // gets rid of about 200 LHSs, need to change the matrix format to make this part of it + PxU64 parent[DY_ARTICULATION_MAX_SIZE]; + for (PxU32 i = 0; i1;) + { + const Cm::SpatialVectorV*PX_RESTRICT S = aux[i].S; + + Ps::prefetch(&load[i - 1]); + Ps::prefetch(&jointVectors[i - 1]); + const FsInertia tmp = Fns::propagate(inertia[i], S, load[i], isf[i]); + inertia[parent[i]] = Fns::addInertia(inertia[parent[i]], Fns::translateInertia(jointVectors[i].parentOffset, tmp)); + contribToParent[i] = tmp; + } + + for (PxU32 i = 1; i Fns; + + Cm::SpatialVectorV IS[3]; + + FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + FsInertia* inertia = allocator.alloc(matrix.linkCount); + PxMemCopy(inertia, baseInertia, matrix.linkCount * sizeof(FsInertia)); + + for (PxU32 i = matrix.linkCount; --i>0;) + { + FsRow& r = rows[i]; + const FsRowAux& a = aux[i]; + const FsJointVectors& jv = jointVectors[i]; + + const Mat33V m = Fns::computeSIS(inertia[i], a.S, IS); + const FloatV f = FLoad(isf[i]); + + const Mat33V D = Fns::invertSym33(Mat33V(V3ScaleAdd(load[i].col0, f, m.col0), + V3ScaleAdd(load[i].col1, f, m.col1), + V3ScaleAdd(load[i].col2, f, m.col2))); + r.D = D; + + inertia[matrix.parent[i]] = Fns::addInertia(inertia[matrix.parent[i]], + Fns::translateInertia(jv.parentOffset, Fns::multiplySubtract(inertia[i], D, IS, r.DSI))); + } + + getRootInverseInertia(matrix) = Fns::invertInertia(inertia[0]); +} + +PX_FORCE_INLINE Cm::SpatialVectorV propagateDrivenImpulse(const FsRow& row, + const FsJointVectors& jv, + Vec3V& SZMinusQ, + const Cm::SpatialVectorV& Z, + const Vec3V& Q) +{ + typedef ArticulationFnsSimd Fns; + + SZMinusQ = V3Sub(V3Add(Z.angular, V3Cross(Z.linear, jv.jointOffset)), Q); + Cm::SpatialVectorV result = Fns::translateForce(jv.parentOffset, Z - Fns::axisMultiply(row.DSI, SZMinusQ)); + + return result; +} + +void PxcFsApplyJointDrives(FsData& matrix, + const Vec3V* Q) +{ + typedef ArticulationFnsSimd Fns; + + PX_ASSERT(matrix.linkCount <= DY_ARTICULATION_MAX_SIZE); + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + Cm::SpatialVectorV Z[DY_ARTICULATION_MAX_SIZE]; + Cm::SpatialVectorV dV[DY_ARTICULATION_MAX_SIZE]; + Vec3V SZminusQ[DY_ARTICULATION_MAX_SIZE]; + + PxMemZero(Z, matrix.linkCount * sizeof(Cm::SpatialVectorV)); + + for (PxU32 i = matrix.linkCount; i-->1;) + Z[matrix.parent[i]] += propagateDrivenImpulse(rows[i], jointVectors[i], SZminusQ[i], Z[i], Q[i]); + + + dV[0] = Fns::multiply(getRootInverseInertia(matrix), -Z[0]); + + for (PxU32 i = 1; i Fns; + + PX_ASSERT(matrix.linkCount <= DY_ARTICULATION_MAX_SIZE); + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + Cm::SpatialVectorV dV[DY_ARTICULATION_MAX_SIZE]; + Vec3V SZ[DY_ARTICULATION_MAX_SIZE]; + + for (PxU32 i = matrix.linkCount; i-->1;) + Z[matrix.parent[i]] += Fns::propagateImpulse(rows[i], jointVectors[i], SZ[i], Z[i], aux[i]); + + dV[0] = Fns::multiply(getRootInverseInertia(matrix), -Z[0]); + + for (PxU32 i = 1; i(desc.articulation); + FsData& fsData = *articulation->getFsDataPtr(); + PxTransform* poses = desc.poses; + PxQuat* deltaQ = desc.deltaQ; + + { + PX_PROFILE_ZONE("Articulations.prepareDataBlock", contextID); + prepareDataBlock(fsData, links, linkCount, poses, deltaQ, baseInertia, jointTransforms, desc.totalDataSize); + } + + const PxReal recipDt = 1.0f / dt; + + Cm::SpatialVectorV* velocity = getVelocity(fsData); + + { + + PX_PROFILE_ZONE("Articulations.setupProject", contextID); + + PxMemZero(getLtbRows(fsData), getLtbDataSize(linkCount)); + prepareLtbMatrix(fsData, baseInertia, poses, jointTransforms, recipDt); + + PxcLtbFactor(fsData); + + Vec3V b[DY_ARTICULATION_MAX_SIZE]; + PxcLtbComputeJv(b, fsData, velocity); + + LtbRow* rows = getLtbRows(fsData); + for (PxU32 i = 1; i(&fsData, fsData.fsDataOffset), getFsDataSize(linkCount)); + prepareFsData(fsData, links); + } + + { + PX_PROFILE_ZONE("Articulations.setupDrives", contextID); + + if (!(desc.core->externalDriveIterations & 0x80000000)) + PxMemZero(desc.externalLoads, sizeof(Mat33V) * linkCount); + + if (!(desc.core->internalDriveIterations & 0x80000000)) + PxMemZero(desc.internalLoads, sizeof(Mat33V) * linkCount); + + PxReal isf[DY_ARTICULATION_MAX_SIZE], esf[DY_ARTICULATION_MAX_SIZE]; // spring factors + Vec3V drive[DY_ARTICULATION_MAX_SIZE]; + + bool externalEqualsInternalCompliance = (desc.core->internalDriveIterations & 0xffff) == (desc.core->externalDriveIterations & 0xffff); + for (PxU32 i = 1; i(*links[i].inboundJoint); + isf[i] = (1 + j.damping * dt + j.spring * dt * dt) * getResistance(j.internalCompliance); + esf[i] = (1 + j.damping * dt + j.spring * dt * dt) * getResistance(j.externalCompliance); + + externalEqualsInternalCompliance = externalEqualsInternalCompliance && j.internalCompliance == j.externalCompliance; + } + + { + PX_PROFILE_ZONE("Articulations.jointInternalLoads", contextID); + PxcFsComputeJointLoadsSimd(fsData, baseInertia, desc.internalLoads, isf, linkCount, desc.core->internalDriveIterations & 0xffff, allocator); + + } + + { + PX_PROFILE_ZONE("Articulations.propagateDrivenInertia", contextID); + PxcFsPropagateDrivenInertiaSimd(fsData, baseInertia, isf, desc.internalLoads, allocator); + } + + { + PX_PROFILE_ZONE("Articulations.computeJointDrives", contextID); + computeJointDrives(fsData, drive, links, poses, jointTransforms, desc.internalLoads, dt); + } + + { + PX_PROFILE_ZONE("Articulations.applyJointDrives", contextID); + PxcFsApplyJointDrives(fsData, drive); + } + + if (!externalEqualsInternalCompliance) + { + { + PX_PROFILE_ZONE("Articulations.jointExternalLoads", contextID); + PxcFsComputeJointLoadsSimd(fsData, baseInertia, desc.externalLoads, esf, linkCount, desc.core->externalDriveIterations & 0xffff, allocator); + } + + { + PX_PROFILE_ZONE("Articulations.propagateDrivenInertia", contextID); + PxcFsPropagateDrivenInertiaSimd(fsData, baseInertia, esf, desc.externalLoads, allocator); + } + } + } + + { + PX_PROFILE_ZONE("Articulations.applyExternalImpulses", contextID); + Cm::SpatialVectorV Z[DY_ARTICULATION_MAX_SIZE]; + + FloatV h = FLoad(dt); + + Cm::SpatialVector* acceleration = desc.acceleration; + + const Vec3V vGravity = V3LoadU(gravity); + + for (PxU32 i = 0; imInternalFlags & PxcRigidBody::eDISABLE_GRAVITY)) + linearAccel = V3Add(linearAccel, vGravity); + Cm::SpatialVectorV a(linearAccel, V3LoadA(acceleration[i].angular)); + Z[i] = -ArticulationFnsSimd::multiply(baseInertia[i], a) * h; + //KS - zero accelerations to ensure they don't get re-applied next frame if nothing touches them again. + acceleration[i].linear = PxVec3(0.f); acceleration[i].angular = PxVec3(0.f); + } + + applyImpulses(fsData, Z, getVelocity(fsData)); + } + + // save off the motion velocity in case there are no constraints with the articulation + + PxMemCopy(desc.motionVelocity, velocity, linkCount * sizeof(Cm::SpatialVectorV)); + + // set up for deferred-update solve + + fsData.dirty = 0; + + // solver progress counters + maxSolverNormalProgress = 0; + maxSolverFrictionProgress = 0; + solverProgress = 0; + + +#if DY_ARTICULATION_DEBUG_VERIFY + for (PxU32 i = 0; i(*desc.articulation); + + PxcFsScratchAllocator fsAllocator(desc.scratchMemory, desc.scratchMemorySize); + FsInertia* PX_RESTRICT baseInertia = fsAllocator.alloc(desc.linkCount); + ArticulationJointTransforms* PX_RESTRICT jointTransforms = fsAllocator.alloc(desc.linkCount); + + articulation.computeUnconstrainedVelocitiesInternal(desc, dt, gravity, contextID, baseInertia, jointTransforms, fsAllocator); + + { + PX_PROFILE_ZONE("Articulations.setupConstraints", contextID); + return ArticulationHelper::setupSolverConstraints(articulation, desc.solverDataSize, allocator, constraintDesc, links, jointTransforms, dt, acCount); + } + +} + + +void Articulation::computeUnconstrainedVelocitiesTGS( + const ArticulationSolverDesc& desc, + PxReal dt, const PxVec3& gravity, + PxU64 contextID, Cm::SpatialVectorF* /*Z*/, Cm::SpatialVectorF* /*deltaV*/) +{ + Articulation& articulation = static_cast(*desc.articulation); + PxcFsScratchAllocator allocator(desc.scratchMemory, desc.scratchMemorySize); + FsInertia* PX_RESTRICT baseInertia = allocator.alloc(desc.linkCount); + ArticulationJointTransforms* PX_RESTRICT jointTransforms = allocator.alloc(desc.linkCount); + + articulation.computeUnconstrainedVelocitiesInternal(desc, dt, gravity, + contextID, baseInertia, jointTransforms, allocator); +} + + +void Articulation::computeJointDrives(FsData& fsData, + Ps::aos::Vec3V* drives, + const ArticulationLink* links, + const PxTransform* poses, + const ArticulationJointTransforms* transforms, + const Ps::aos::Mat33V* loads, + PxReal dt) +{ + typedef ArticulationFnsScalar Fns; + + const PxU32 linkCount = fsData.linkCount; + const Cm::SpatialVector* velocity = reinterpret_cast(getVelocity(fsData)); + + for (PxU32 i = 1; i(*links[i].inboundJoint); + + const Cm::SpatialVector currentVel = Fns::translateMotion(poses[i].p - b.cA2w.p, velocity[i]) + - Fns::translateMotion(poses[parent].p - b.cA2w.p, velocity[parent]); + + // we want the quat such that q * cB2cA = targetPosition + PxVec3 rotVec; + if (j.driveType == PxU8(PxArticulationJointDriveType::eERROR)) + rotVec = j.targetPosition.getImaginaryPart(); + else + rotVec = Ps::log(j.targetPosition * b.cB2cA.q.getConjugate()); // as a rotation vector + + // NM's Tests indicate behavior is better without the term commented out below, even though + // an implicit spring derivation suggests it should be there. + + const PxVec3 posError = b.cA2w.rotate(rotVec); // - currentVel.angular * 0.5f * dt + const PxVec3 velError = b.cA2w.rotate(j.targetVelocity) - currentVel.angular; + + drives[i] = M33MulV3(loads[i], V3LoadU((j.spring * posError + j.damping * velError) * dt * getResistance(j.internalCompliance))); + } +} + + +void Articulation::saveVelocity(const ArticulationSolverDesc& desc, Cm::SpatialVectorF* /*deltaV*/) +{ + Vec3V b[DY_ARTICULATION_MAX_SIZE]; + + Articulation* articulation = static_cast(desc.articulation); + FsData& m = *articulation->getFsDataPtr(); + + Cm::SpatialVectorV* velocity = getVelocity(m); + PxcFsFlushVelocity(m); + + // save off the motion velocity + + for (PxU32 i = 0; i(desc.articulation); + FsData& m = *articulation->getFsDataPtr(); + + Cm::SpatialVectorV* velocity = getVelocity(m); + PxcFsFlushVelocity(m); + + const FloatV invDt = FLoad(invDtF32); + + Cm::SpatialVectorV* motionVector = getMotionVector(m); + + // save off the motion velocity + + for (PxU32 i = 0; i(desc.articulation); + FsData& m = *articulation->getFsDataPtr(); + + PxQuat* deltaQ = desc.deltaQ; + + Cm::SpatialVectorV* velocity = getVelocity(m); + Cm::SpatialVectorV* deltaMotion = getMotionVector(m); + PxcFsFlushVelocity(m); + + const FloatV fDt = FLoad(dt); + + for (PxU32 i = 0; i(desc.articulation); + FsData& m = *articulation->getFsDataPtr(); + + Cm::SpatialVectorV* deltaMotion = getMotionVector(m); + + Cm::SpatialVectorV* velocity = getVelocity(m); + + const FloatV fInvDt = FLoad(invDt); + + for (PxU32 i = 0; i(getRefVelocity(matrix)), linkID, reinterpret_cast(imp)); + } +#endif + + FsData& matrix = *getFsDataPtr(); + Vec3V linZ = V3Neg(linear); + Vec3V angZ = V3Neg(angular); + + const FsRow *rows = getFsRows(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + +#if DY_ARTICULATION_DEBUG_VERIFY + const FsRowAux *aux = getAux(matrix); +#endif + Vec3V *deferredSZ = getDeferredSZ(matrix); + + for (PxU32 i = linkID; i != 0; i = matrix.parent[i]) + { + const FsRow &row = rows[i]; + const FsJointVectors& jv = jointVectors[i]; + +#if DY_ARTICULATION_DEBUG_VERIFY + PxVec3 SZcheck; + Cm::SpatialVector Zcheck = ArticulationRef::propagateImpulse(row, jv, SZcheck, SpV(linZ, angZ), aux[i]); +#endif + + Vec3V SZ = V3Add(angZ, V3Cross(linZ, jv.jointOffset)); + Vec3V lrLinear = V3Sub(linZ, V3ScaleAdd(row.DSI[0].linear, V3GetX(SZ), + V3ScaleAdd(row.DSI[1].linear, V3GetY(SZ), + V3Scale(row.DSI[2].linear, V3GetZ(SZ))))); + + Vec3V lrAngular = V3Sub(angZ, V3ScaleAdd(row.DSI[0].angular, V3GetX(SZ), + V3ScaleAdd(row.DSI[1].angular, V3GetY(SZ), + V3Scale(row.DSI[2].angular, V3GetZ(SZ))))); + + linZ = lrLinear; + angZ = V3Add(lrAngular, V3Cross(jv.parentOffset, lrLinear)); + deferredSZ[i] = V3Add(deferredSZ[i], SZ); + + PX_ASSERT(Ps::aos::isFiniteVec3V(linZ)); + PX_ASSERT(Ps::aos::isFiniteVec3V(angZ)); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector Z = SpV(linZ, angZ); + PX_ASSERT((Z - Zcheck).magnitude()<1e-4*PxMax(Zcheck.magnitude(), 1.0f)); + PX_ASSERT(((PxVec3&)SZ - SZcheck).magnitude()<1e-4*PxMax(SZcheck.magnitude(), 1.0f)); +#endif + } + + matrix.deferredZ.linear = V3Add(matrix.deferredZ.linear, linZ); + matrix.deferredZ.angular = V3Add(matrix.deferredZ.angular, angZ); + + matrix.dirty |= rows[linkID].pathToRoot; +} + + +void Articulation::pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1) +{ + //Common case - linkID = parent of linkID1... + //In this case, we compute the delta velocity between the 2 and return + + v0 = pxcFsGetVelocity(linkID); + v1 = pxcFsGetVelocity(linkID1); +} + +void Articulation::pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, + const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, + const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) +{ + pxcFsApplyImpulse(linkID, linear, angular, Z, deltaV); + pxcFsApplyImpulse(linkID2, linear2, angular2, Z, deltaV); +} + + +void Articulation::solveInternalConstraints(const PxReal /*dt*/, const PxReal /*invDt*/, Cm::SpatialVectorF* /*impulses*/, + Cm::SpatialVectorF* /*DeltaV*/, bool) +{ +} + + +Cm::SpatialVectorV Articulation::pxcFsGetVelocity(PxU32 linkID) +{ + FsData& matrix = *getFsDataPtr(); + const FsRow *rows = getFsRows(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + +#if DY_ARTICULATION_DEBUG_VERIFY + const FsRowAux *aux = getAux(matrix); +#endif + Cm::SpatialVectorV* PX_RESTRICT V = getVelocity(matrix); + + // find the dirty node on the path (including the root) with the lowest index + ArticulationBitField toUpdate = rows[linkID].pathToRoot & matrix.dirty; + + + if (toUpdate) + { + // store the dV elements densely and use an array map to decode - hopefully cache friendlier + PxU32 indexToStackLoc[DY_ARTICULATION_MAX_SIZE], count = 0; + Cm::SpatialVectorV dVStack[DY_ARTICULATION_MAX_SIZE]; + + ArticulationBitField ignoreNodes = (toUpdate & (0 - toUpdate)) - 1; + //PX_ASSERT(ignoreNodes == 0); + ArticulationBitField path = rows[linkID].pathToRoot & ~ignoreNodes, p = path; + ArticulationBitField newDirty = 0; + + Vec3V ldV = V3Zero(), adV = V3Zero(); + Cm::SpatialVectorV* PX_RESTRICT defV = getDeferredVel(matrix); + Vec3V* PX_RESTRICT SZ = getDeferredSZ(matrix); + + if (p & 1) + { + const FsInertia &m = getRootInverseInertia(matrix); + Vec3V lZ = V3Neg(matrix.deferredZ.linear); + Vec3V aZ = V3Neg(matrix.deferredZ.angular); + + ldV = V3Add(M33MulV3(m.ll, lZ), M33MulV3(m.la, aZ)); + adV = V3Add(M33TrnspsMulV3(m.la, lZ), M33MulV3(m.aa, aZ)); + + V[0].linear = V3Add(V[0].linear, ldV); + V[0].angular = V3Add(V[0].angular, adV); + + matrix.deferredZ.linear = V3Zero(); + matrix.deferredZ.angular = V3Zero(); + + indexToStackLoc[0] = count; + Cm::SpatialVectorV &e = dVStack[count++]; + + e.linear = ldV; + e.angular = adV; + + newDirty = rows[0].children; + p--; + } + + + while (p) // using "for(;p;p &= (p-1))" here generates LHSs from the ArticulationLowestSetBit + { + PxU32 i = ArticulationLowestSetBit(p); + const FsJointVectors& jv = jointVectors[i]; + + p &= (p - 1); + + const FsRow* PX_RESTRICT row = rows + i; + + ldV = V3Add(ldV, defV[i].linear); + adV = V3Add(adV, defV[i].angular); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector dVcheck = ArticulationRef::propagateVelocity(*row, jv, (PxVec3&)SZ[i], SpV(ldV, adV), aux[i]); +#endif + + Vec3V DSZ = M33MulV3(row->D, SZ[i]); + + Vec3V lW = V3Add(ldV, V3Cross(adV, jv.parentOffset)); + Vec3V aW = adV; + + const Cm::SpatialVectorV*PX_RESTRICT DSI = row->DSI; + Vec3V lN = V3Merge(V3Dot(DSI[0].linear, lW), V3Dot(DSI[1].linear, lW), V3Dot(DSI[2].linear, lW)); + Vec3V aN = V3Merge(V3Dot(DSI[0].angular, aW), V3Dot(DSI[1].angular, aW), V3Dot(DSI[2].angular, aW)); + + Vec3V n = V3Add(V3Add(lN, aN), DSZ); + + ldV = V3Sub(lW, V3Cross(jv.jointOffset, n)); + adV = V3Sub(aW, n); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector dV = SpV(ldV, adV); + PX_ASSERT((dV - dVcheck).magnitude()<1e-4*PxMax(dVcheck.magnitude(), 1.0f)); +#endif + + V[i].linear = V3Add(V[i].linear, ldV); + V[i].angular = V3Add(V[i].angular, adV); + + defV[i].linear = V3Zero(); + defV[i].angular = V3Zero(); + SZ[i] = V3Zero(); + + indexToStackLoc[i] = count; + Cm::SpatialVectorV &e = dVStack[count++]; + newDirty |= rows[i].children; + + e.linear = ldV; + e.angular = adV; + } + + for (ArticulationBitField defer = newDirty&~path; defer; defer &= (defer - 1)) + { + PxU32 i = ArticulationLowestSetBit(defer); + PxU32 parent = indexToStackLoc[matrix.parent[i]]; + + defV[i].linear = V3Add(defV[i].linear, dVStack[parent].linear); + defV[i].angular = V3Add(defV[i].angular, dVStack[parent].angular); + } + + matrix.dirty = (matrix.dirty | newDirty)&~path; + } +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector v = reinterpret_cast(V[linkID]); + Cm::SpatialVector rv = reinterpret_cast(getRefVelocity(matrix)[linkID]); + PX_ASSERT((v - rv).magnitude()<1e-4f * PxMax(rv.magnitude(), 1.0f)); +#endif + + return V[linkID]; +} + +//Cm::SpatialVectorV PxcFsGetVelocity(Articulation &articulation, +// PxU32 linkID) +//{ +// FsData& matrix = *articulation.getFsDataPtr(); +// const FsRow *rows = getFsRows(matrix); +// const FsJointVectors* jointVectors = getJointVectors(matrix); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// const FsRowAux *aux = getAux(matrix); +//#endif +// Cm::SpatialVectorV* PX_RESTRICT V = getVelocity(matrix); +// +// // find the dirty node on the path (including the root) with the lowest index +// ArticulationBitField toUpdate = rows[linkID].pathToRoot & matrix.dirty; +// +// +// if (toUpdate) +// { +// // store the dV elements densely and use an array map to decode - hopefully cache friendlier +// PxU32 indexToStackLoc[DY_ARTICULATION_MAX_SIZE], count = 0; +// Cm::SpatialVectorV dVStack[DY_ARTICULATION_MAX_SIZE]; +// +// ArticulationBitField ignoreNodes = (toUpdate & (0 - toUpdate)) - 1; +// ArticulationBitField path = rows[linkID].pathToRoot & ~ignoreNodes, p = path; +// ArticulationBitField newDirty = 0; +// +// Vec3V ldV = V3Zero(), adV = V3Zero(); +// Cm::SpatialVectorV* PX_RESTRICT defV = getDeferredVel(matrix); +// Vec3V* PX_RESTRICT SZ = getDeferredSZ(matrix); +// +// if (p & 1) +// { +// const FsInertia &m = getRootInverseInertia(matrix); +// Vec3V lZ = V3Neg(matrix.deferredZ.linear); +// Vec3V aZ = V3Neg(matrix.deferredZ.angular); +// +// ldV = V3Add(M33MulV3(m.ll, lZ), M33MulV3(m.la, aZ)); +// adV = V3Add(M33TrnspsMulV3(m.la, lZ), M33MulV3(m.aa, aZ)); +// +// V[0].linear = V3Add(V[0].linear, ldV); +// V[0].angular = V3Add(V[0].angular, adV); +// +// matrix.deferredZ.linear = V3Zero(); +// matrix.deferredZ.angular = V3Zero(); +// +// indexToStackLoc[0] = count; +// Cm::SpatialVectorV &e = dVStack[count++]; +// +// e.linear = ldV; +// e.angular = adV; +// +// newDirty = rows[0].children; +// p--; +// } +// +// +// while (p) // using "for(;p;p &= (p-1))" here generates LHSs from the ArticulationLowestSetBit +// { +// PxU32 i = ArticulationLowestSetBit(p); +// const FsJointVectors& jv = jointVectors[i]; +// +// p &= (p - 1); +// +// const FsRow* PX_RESTRICT row = rows + i; +// +// ldV = V3Add(ldV, defV[i].linear); +// adV = V3Add(adV, defV[i].angular); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector dVcheck = ArticulationRef::propagateVelocity(*row, jv, (PxVec3&)SZ[i], SpV(ldV, adV), aux[i]); +//#endif +// +// Vec3V DSZ = M33MulV3(row->D, SZ[i]); +// +// Vec3V lW = V3Add(ldV, V3Cross(adV, jv.parentOffset)); +// Vec3V aW = adV; +// +// const Cm::SpatialVectorV*PX_RESTRICT DSI = row->DSI; +// Vec3V lN = V3Merge(V3Dot(DSI[0].linear, lW), V3Dot(DSI[1].linear, lW), V3Dot(DSI[2].linear, lW)); +// Vec3V aN = V3Merge(V3Dot(DSI[0].angular, aW), V3Dot(DSI[1].angular, aW), V3Dot(DSI[2].angular, aW)); +// +// Vec3V n = V3Add(V3Add(lN, aN), DSZ); +// +// ldV = V3Sub(lW, V3Cross(jv.jointOffset, n)); +// adV = V3Sub(aW, n); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector dV = SpV(ldV, adV); +// PX_ASSERT((dV - dVcheck).magnitude()<1e-4*PxMax(dVcheck.magnitude(), 1.0f)); +//#endif +// +// V[i].linear = V3Add(V[i].linear, ldV); +// V[i].angular = V3Add(V[i].angular, adV); +// +// defV[i].linear = V3Zero(); +// defV[i].angular = V3Zero(); +// SZ[i] = V3Zero(); +// +// indexToStackLoc[i] = count; +// Cm::SpatialVectorV &e = dVStack[count++]; +// newDirty |= rows[i].children; +// +// e.linear = ldV; +// e.angular = adV; +// } +// +// for (ArticulationBitField defer = newDirty&~path; defer; defer &= (defer - 1)) +// { +// PxU32 i = ArticulationLowestSetBit(defer); +// PxU32 parent = indexToStackLoc[matrix.parent[i]]; +// +// defV[i].linear = V3Add(defV[i].linear, dVStack[parent].linear); +// defV[i].angular = V3Add(defV[i].angular, dVStack[parent].angular); +// } +// +// matrix.dirty = (matrix.dirty | newDirty)&~path; +// } +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector v = reinterpret_cast(V[linkID]); +// Cm::SpatialVector rv = reinterpret_cast(getRefVelocity(matrix)[linkID]); +// PX_ASSERT((v - rv).magnitude()<1e-4f * PxMax(rv.magnitude(), 1.0f)); +//#endif +// +// return V[linkID]; +//} + +void Articulation::updateBodies(const ArticulationSolverDesc& desc, PxReal dt) +{ + Articulation* articulation = static_cast(desc.articulation); + FsData& fsData = *articulation->getFsDataPtr(); + const ArticulationCore& core = *desc.core; + const ArticulationLink* links = desc.links; + PxTransform* poses = desc.poses; + Cm::SpatialVectorV* motionVelocity = desc.motionVelocity; + + Vec3V b[DY_ARTICULATION_MAX_SIZE]; + + PxU32 linkCount = fsData.linkCount; + + PxcFsFlushVelocity(fsData); + PxcLtbComputeJv(b, fsData, getVelocity(fsData)); + PxcLtbProject(fsData, getVelocity(fsData), b); + + // update positions + PxcFsScratchAllocator allocator(desc.scratchMemory, desc.scratchMemorySize); + PxTransform* PX_RESTRICT oldPose = allocator.alloc(desc.linkCount); + + for (PxU32 i = 0; i(motionVelocity[i].linear); + const PxVec3& av = reinterpret_cast(motionVelocity[i].angular); + oldPose[i] = poses[i]; + poses[i] = PxTransform(poses[i].p + lv * dt, Ps::exp(av*dt) * poses[i].q); + } + + bool projected = false; + const PxReal recipDt = 1.0f / dt; + + FsInertia* PX_RESTRICT baseInertia = allocator.alloc(desc.linkCount); + ArticulationJointTransforms* PX_RESTRICT jointTransforms = allocator.alloc(desc.linkCount); + + for (PxU32 iterations = 0; iterations < core.maxProjectionIterations; iterations++) + { + PxReal maxSeparation = -PX_MAX_F32; + for (PxU32 i = 1; i(*links[i].inboundJoint); + maxSeparation = PxMax(maxSeparation, + (poses[links[i].parent].transform(j.parentPose).p - + poses[i].transform(j.childPose).p).magnitude()); + } + + if (maxSeparation <= core.separationTolerance) + break; + + projected = true; + + // we go around again, finding velocities which pull us back together - this + // form of projection is momentum-preserving but slow compared to hierarchical + // projection + + PxMemZero(baseInertia, sizeof(FsInertia)*linkCount); + + setInertia(baseInertia[0], *links[0].bodyCore, poses[0]); + for (PxU32 i = 1; i(*links[i].inboundJoint)); + } + + articulation->prepareLtbMatrix(fsData, baseInertia, poses, jointTransforms, recipDt); + PxcLtbFactor(fsData); + + LtbRow* rows = getLtbRows(fsData); + + for (PxU32 i = 1; i(motionVelocity[i].linear); + const PxVec3& av = reinterpret_cast(motionVelocity[i].angular); + poses[i] = PxTransform(poses[i].p + lv * dt, Ps::exp(av*dt) * poses[i].q); + } + } + + if (projected) + { + // recompute motion velocities. + for (PxU32 i = 0; ibody2World = poses[i]; + + V3StoreA(velocity[i].linear, links[i].bodyCore->linearVelocity); + V3StoreA(velocity[i].angular, links[i].bodyCore->angularVelocity); + } +} + + +void PxvArticulationDriveCache::initialize( + FsData &fsData, + PxU16 linkCount, + const ArticulationLink* links, + PxReal compliance, + PxU32 iterations, + char* scratchMemory, + PxU32 scratchMemorySize) +{ + PxcFsScratchAllocator allocator(scratchMemory, scratchMemorySize); + FsInertia* PX_RESTRICT baseInertia = allocator.alloc(linkCount); + ArticulationJointTransforms* PX_RESTRICT jointTransforms = allocator.alloc(linkCount); + PxTransform* PX_RESTRICT poses = allocator.alloc(linkCount); + PxQuat* PX_RESTRICT deltaQ = allocator.alloc(linkCount); + Mat33V* PX_RESTRICT jointLoads = allocator.alloc(linkCount); + + PxReal springFactor[DY_ARTICULATION_MAX_SIZE]; // spring factors + + Articulation::prepareDataBlock(fsData, links, linkCount, poses, deltaQ, baseInertia, jointTransforms, 0); + + PxMemZero(addAddr(&fsData, fsData.fsDataOffset), Articulation::getFsDataSize(linkCount)); + Articulation::prepareFsData(fsData, links); + + springFactor[0] = 0.0f; + for (PxU32 i = 1; i 1e10f) //can set as low as 0. + { + t0 *= PxRecipSqrt(ll); + t1 = unitNormal.cross(t0); + } + else + Ps::normalToTangents(unitNormal, t0, t1); //fallback +} + +PxReal SolverExtBody::projectVelocity(const PxVec3& linear, const PxVec3& angular) const +{ + if(mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + return mBodyData->projectVelocity(linear, angular); + } + else + { + const Cm::SpatialVectorV velocity = mArticulation->getLinkVelocity(mLinkIndex); + + FloatV fv = velocity.dot(Cm::SpatialVector(linear, angular)); + + PxF32 f; + FStore(fv, &f); + return f; + /*PxF32 f; + FStore(getVelocity(*mFsData)[mLinkIndex].dot(Cm::SpatialVector(linear, angular)), &f); + return f;*/ + } +} + +PxVec3 SolverExtBody::getLinVel() const +{ + if(mLinkIndex == PxSolverConstraintDesc::NO_LINK) + return mBodyData->linearVelocity; + else + { + Vec3V linear = mArticulation->getLinkVelocity(mLinkIndex).linear; + + PxVec3 result; + V3StoreU(linear, result); + /*PxVec3 result; + V3StoreU(getVelocity(*mFsData)[mLinkIndex].linear, result);*/ + return result; + } +} + + +PxVec3 SolverExtBody::getAngVel() const +{ + if(mLinkIndex == PxSolverConstraintDesc::NO_LINK) + return mBodyData->angularVelocity; + else + { + + Vec3V angular = mArticulation->getLinkVelocity(mLinkIndex).angular; + + PxVec3 result; + V3StoreU(angular, result); + /*PxVec3 result; + V3StoreU(getVelocity(*mFsData)[mLinkIndex].angular, result);*/ + return result; + } +} + +Cm::SpatialVector createImpulseResponseVector(const PxVec3& linear, const PxVec3& angular, const SolverExtBody& body) +{ + if(body.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + return Cm::SpatialVector(linear, body.mBodyData->sqrtInvInertia * angular); + } + return Cm::SpatialVector(linear, angular); +} + +PxReal getImpulseResponse(const SolverExtBody& b0, const Cm::SpatialVector& impulse0, Cm::SpatialVector& deltaV0, PxReal dom0, PxReal angDom0, + const SolverExtBody& b1, const Cm::SpatialVector& impulse1, Cm::SpatialVector& deltaV1, PxReal dom1, PxReal angDom1, + Cm::SpatialVectorF* Z, bool allowSelfCollision) +{ + PxReal response; + allowSelfCollision = true; + // right now self-collision with contacts crashes the solver + + //KS - knocked this out to save some space on SPU + if(allowSelfCollision && b0.mLinkIndex!=PxSolverConstraintDesc::NO_LINK && b0.mArticulation == b1.mArticulation && 0) + { + /*ArticulationHelper::getImpulseSelfResponse(*b0.mFsData,b0.mLinkIndex, impulse0, deltaV0, + b1.mLinkIndex, impulse1, deltaV1);*/ + + b0.mArticulation->getImpulseSelfResponse(b0.mLinkIndex, b1.mLinkIndex, Z, impulse0.scale(dom0, angDom0), impulse1.scale(dom1, angDom1), + deltaV0, deltaV1); + + response = impulse0.dot(deltaV0) + impulse1.dot(deltaV1); + + } + else + { + + if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + deltaV0.linear = impulse0.linear * b0.mBodyData->invMass * dom0; + deltaV0.angular = impulse0.angular * angDom0; + } + else + { + b0.mArticulation->getImpulseResponse(b0.mLinkIndex, Z, impulse0.scale(dom0, angDom0), deltaV0); + } + + response = impulse0.dot(deltaV0); + if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + deltaV1.linear = impulse1.linear * b1.mBodyData->invMass * dom1; + deltaV1.angular = impulse1.angular * angDom1; + } + else + { + b1.mArticulation->getImpulseResponse( b1.mLinkIndex, Z, impulse1.scale(dom1, angDom1), deltaV1); + } + response += impulse1.dot(deltaV1); + } + + return response; +} + + +void setupFinalizeExtSolverContacts( + const ContactPoint* buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const SolverExtBody& b0, + const SolverExtBody& b1, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + const PxReal restDist, + PxU8* frictionDataPtr, + PxReal ccdMaxContactDist, + Cm::SpatialVectorF* Z) +{ + // NOTE II: the friction patches are sparse (some of them have no contact patches, and + // therefore did not get written back to the cache) but the patch addresses are dense, + // corresponding to valid patches + + /*const bool haveFriction = PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0;*/ + + const FloatV ccdMaxSeparation = FLoad(ccdMaxContactDist); + + PxU8* PX_RESTRICT ptr = workspace; + + const FloatV zero = FZero(); + + //KS - TODO - this should all be done in SIMD to avoid LHS + //const PxF32 maxPenBias0 = b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b0.mBodyData->penBiasClamp : getMaxPenBias(*b0.mFsData)[b0.mLinkIndex]; + //const PxF32 maxPenBias1 = b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b1.mBodyData->penBiasClamp : getMaxPenBias(*b1.mFsData)[b1.mLinkIndex]; + + PxF32 maxPenBias0 = b0.mBodyData->penBiasClamp; + PxF32 maxPenBias1 = b1.mBodyData->penBiasClamp; + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias0 = b0.mArticulation->getLinkMaxPenBias(b0.mLinkIndex); + } + + if (b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias1 = b1.mArticulation->getLinkMaxPenBias(b1.mLinkIndex); + } + + const FloatV maxPenBias = FLoad(PxMax(maxPenBias0, maxPenBias1)); + + + const PxReal d0 = invMassScale0; + const PxReal d1 = invMassScale1; + + const PxReal angD0 = invInertiaScale0; + const PxReal angD1 = invInertiaScale1; + + Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4Zero(); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(d0)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(d1)); + + const FloatV restDistance = FLoad(restDist); + + PxU32 frictionPatchWritebackAddrIndex = 0; + PxU32 contactWritebackCount = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + const FloatV invDt = FLoad(invDtF32); + const FloatV p8 = FLoad(0.8f); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + + const FloatV invDtp8 = FMul(invDt, p8); + + PxU8 flags = 0; + + for(PxU32 i=0;irestitution; + + const PxReal staticFriction = contactBase0->staticFriction; + const PxReal dynamicFriction = contactBase0->dynamicFriction; + const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); + + SolverContactHeader* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeader); + + + Ps::prefetchLine(ptr + 128); + Ps::prefetchLine(ptr + 256); + Ps::prefetchLine(ptr + 384); + + const bool haveFriction = (disableStrongFriction == 0) ;//PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0; + header->numNormalConstr = Ps::to8(contactCount); + header->numFrictionConstr = Ps::to8(haveFriction ? frictionPatch.anchorCount*2 : 0); + + header->type = Ps::to8(DY_SC_TYPE_EXT_CONTACT); + + header->flags = flags; + + const FloatV restitution = FLoad(combinedRestitution); + + header->staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; + + header->angDom0 = angD0; + header->angDom1 = angD1; + + const PxU32 pointStride = sizeof(SolverContactPointExt); + const PxU32 frictionStride = sizeof(SolverContactFrictionExt); + + const Vec3V normal = V3LoadU(buffer[c.contactPatches[c.correlationListHeads[i]].start].normal); + + FloatV accumImpulse = FZero(); + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer + c.contactPatches[patch].start; + + PxU8* p = ptr; + + + + for(PxU32 j=0;j(p); + p += pointStride; + + accumImpulse = FAdd(accumImpulse, setupExtSolverContact(b0, b1, d0, d1, angD0, angD1, bodyFrame0, bodyFrame1, normal, invDt, invDtp8, restDistance, maxPenBias, restitution, + bounceThreshold, contact, *solverContact, ccdMaxSeparation, Z)); + + } + + ptr = p; + } + contactWritebackCount += contactCount; + + accumImpulse = FDiv(accumImpulse, FLoad(PxF32(contactCount))); + + header->normal_minAppliedImpulseForFrictionW = V4SetW(Vec4V_From_Vec3V(normal), accumImpulse); + + PxF32* forceBuffer = reinterpret_cast(ptr); + PxMemZero(forceBuffer, sizeof(PxF32) * contactCount); + ptr += sizeof(PxF32) * ((contactCount + 3) & (~3)); + + header->broken = 0; + + if(haveFriction) + { + //const Vec3V normal = Vec3V_From_PxVec3(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); + PxVec3 normalS = buffer[c.contactPatches[c.correlationListHeads[i]].start].normal; + + PxVec3 t0, t1; + computeFrictionTangents(b0.getLinVel() - b1.getLinVel(), normalS, t0, t1); + + Vec3V vT0 = V3LoadU(t0); + Vec3V vT1 = V3LoadU(t1); + + //We want to set the writeBack ptr to point to the broken flag of the friction patch. + //On spu we have a slight problem here because the friction patch array is + //in local store rather than in main memory. The good news is that the address of the friction + //patch array in main memory is stored in the work unit. These two addresses will be equal + //except on spu where one is local store memory and the other is the effective address in main memory. + //Using the value stored in the work unit guarantees that the main memory address is used on all platforms. + PxU8* PX_RESTRICT writeback = frictionDataPtr + frictionPatchWritebackAddrIndex*sizeof(FrictionPatch); + + header->frictionBrokenWritebackByte = writeback; + + for(PxU32 j = 0; j < frictionPatch.anchorCount; j++) + { + SolverContactFrictionExt* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionStride; + SolverContactFrictionExt* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionStride; + + PxVec3 ra = bodyFrame0.q.rotate(frictionPatch.body0Anchors[j]); + PxVec3 rb = bodyFrame1.q.rotate(frictionPatch.body1Anchors[j]); + PxVec3 error = (ra + bodyFrame0.p) - (rb + bodyFrame1.p); + + { + const PxVec3 raXn = ra.cross(t0); + const PxVec3 rbXn = rb.cross(t0); + + Cm::SpatialVector deltaV0, deltaV1; + + const Cm::SpatialVector resp0 = createImpulseResponseVector(t0, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-t0, -rbXn, b1); + FloatV resp = FLoad(getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, Z)); + + const FloatV velMultiplier = FSel(FIsGrtr(resp, FLoad(DY_ARTICULATION_MIN_RESPONSE)), FDiv(p8, resp), zero); + + PxU32 index = c.contactPatches[c.correlationListHeads[i]].start; + PxF32 targetVel = buffer[index].targetVel.dot(t0); + + if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVel -= b0.projectVelocity(t0, raXn); + else if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVel += b1.projectVelocity(t0, rbXn); + + f0->normalXYZ_appliedForceW = V4SetW(vT0, zero); + f0->raXnXYZ_velMultiplierW = V4SetW(V4LoadA(&resp0.angular.x), velMultiplier); + f0->rbXnXYZ_biasW = V4SetW(V4Neg(V4LoadA(&resp1.angular.x)), FLoad(t0.dot(error) * invDtF32)); + f0->linDeltaVA = V3LoadA(deltaV0.linear); + f0->angDeltaVA = V3LoadA(deltaV0.angular); + f0->linDeltaVB = V3LoadA(deltaV1.linear); + f0->angDeltaVB = V3LoadA(deltaV1.angular); + f0->targetVel = targetVel; + } + + { + + const PxVec3 raXn = ra.cross(t1); + const PxVec3 rbXn = rb.cross(t1); + + Cm::SpatialVector deltaV0, deltaV1; + + + const Cm::SpatialVector resp0 = createImpulseResponseVector(t1, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-t1, -rbXn, b1); + + FloatV resp = FLoad(getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, Z)); + + //const FloatV velMultiplier = FSel(FIsGrtr(resp, FLoad(DY_ARTICULATION_MIN_RESPONSE)), FMul(p8, FRecip(resp)), zero); + + const FloatV velMultiplier = FSel(FIsGrtr(resp, FLoad(DY_ARTICULATION_MIN_RESPONSE)), FDiv(p8, resp), zero); + + PxU32 index = c.contactPatches[c.correlationListHeads[i]].start; + PxF32 targetVel = buffer[index].targetVel.dot(t1); + + if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVel -= b0.projectVelocity(t1, raXn); + else if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVel += b1.projectVelocity(t1, rbXn); + + f1->normalXYZ_appliedForceW = V4SetW(vT1, zero); + f1->raXnXYZ_velMultiplierW = V4SetW(V4LoadA(&resp0.angular.x), velMultiplier); + f1->rbXnXYZ_biasW = V4SetW(V4Neg(V4LoadA(&resp1.angular.x)), FLoad(t1.dot(error) * invDtF32)); + f1->linDeltaVA = V3LoadA(deltaV0.linear); + f1->angDeltaVA = V3LoadA(deltaV0.angular); + f1->linDeltaVB = V3LoadA(deltaV1.linear); + f1->angDeltaVB = V3LoadA(deltaV1.angular); + f1->targetVel = targetVel; + } + } + } + + frictionPatchWritebackAddrIndex++; + } +} + +} + + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h new file mode 100644 index 000000000..3d9fc7abe --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h @@ -0,0 +1,97 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCONSTRAINTEXT_H +#define DY_SOLVERCONSTRAINTEXT_H + +#include "DySolverExt.h" + +namespace physx +{ + +struct PxcNpWorkUnit; + + +namespace Gu +{ + class ContactBuffer; + struct ContactPoint; +} + +namespace Dy +{ + + struct CorrelationBuffer; + + PxReal getImpulseResponse(const SolverExtBody& b0, const Cm::SpatialVector& impulse0, Cm::SpatialVector& deltaV0, PxReal dom0, PxReal angDom0, + const SolverExtBody& b1, const Cm::SpatialVector& impulse1, Cm::SpatialVector& deltaV1, PxReal dom1, PxReal angDom1, + Cm::SpatialVectorF* Z, bool allowSelfCollision = false); + + Cm::SpatialVector createImpulseResponseVector(const PxVec3& linear, const PxVec3& angular, const SolverExtBody& body); + + void setupFinalizeExtSolverContacts( + const Gu::ContactPoint* buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const SolverExtBody& b0, + const SolverExtBody& b1, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + PxReal restDistance, PxU8* frictionDataPtr, + PxReal ccdMaxContactDist, + Cm::SpatialVectorF* Z); + + + bool setupFinalizeExtSolverContactsCoulomb( + const Gu::ContactBuffer& buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + PxReal invDt, + PxReal bounceThreshold, + const SolverExtBody& b0, + const SolverExtBody& b1, + PxU32 frictionCountPerPoint, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + PxReal restDist, + PxReal ccdMaxContactDist, + Cm::SpatialVectorF* Z); + +} //namespace Dy + +} + +#endif //DY_SOLVERCONSTRAINTEXT_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp new file mode 100644 index 000000000..af2c30d54 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp @@ -0,0 +1,316 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "DyArticulationContactPrep.h" +#include "DySolverConstraintDesc.h" +#include "DySolverConstraint1D.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DyArticulationHelper.h" +#include "PxcNpWorkUnit.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DyCorrelationBuffer.h" +#include "DySolverConstraintExtShared.h" + +using namespace physx; +using namespace Gu; + +// constraint-gen only, since these use getVelocityFast methods +// which aren't valid during the solver phase + +namespace physx +{ + +namespace Dy +{ + + +bool setupFinalizeExtSolverContactsCoulomb( + const ContactBuffer& buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + PxReal invDt, + PxReal bounceThresholdF32, + const SolverExtBody& b0, + const SolverExtBody& b1, + PxU32 frictionCountPerPoint, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + PxReal restDist, + PxReal ccdMaxDistance, + Cm::SpatialVectorF* Z) +{ + // NOTE II: the friction patches are sparse (some of them have no contact patches, and + // therefore did not get written back to the cache) but the patch addresses are dense, + // corresponding to valid patches + + const FloatV ccdMaxSeparation = FLoad(ccdMaxDistance); + + PxU8* PX_RESTRICT ptr = workspace; + + //KS - TODO - this should all be done in SIMD to avoid LHS + PxF32 maxPenBias0 = b0.mBodyData->penBiasClamp; + PxF32 maxPenBias1 = b1.mBodyData->penBiasClamp; + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias0 = b0.mArticulation->getLinkMaxPenBias(b0.mLinkIndex); + } + + if (b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias1 = b1.mArticulation->getLinkMaxPenBias(b1.mLinkIndex); + } + + const FloatV maxPenBias = FLoad(PxMax(maxPenBias0, maxPenBias1)/invDt); + + const FloatV restDistance = FLoad(restDist); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + + const FloatV invDtV = FLoad(invDt); + const FloatV pt8 = FLoad(0.8f); + + const FloatV invDtp8 = FMul(invDtV, pt8); + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + const PxU32 frictionPatchCount = c.frictionPatchCount; + + const PxU32 pointStride = sizeof(SolverContactPointExt); + const PxU32 frictionStride = sizeof(SolverContactFrictionExt); + const PxU8 pointHeaderType = DY_SC_TYPE_EXT_CONTACT; + const PxU8 frictionHeaderType = DY_SC_TYPE_EXT_FRICTION; + + PxReal d0 = invMassScale0; + PxReal d1 = invMassScale1; + PxReal angD0 = invInertiaScale0; + PxReal angD1 = invInertiaScale1; + + PxU8 flags = 0; + + for(PxU32 i=0;i< frictionPatchCount;i++) + { + const PxU32 contactCount = c.frictionPatchContactCounts[i]; + if(contactCount == 0) + continue; + + const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start; + + const Vec3V normalV = Ps::aos::V3LoadA(contactBase0->normal); + const Vec3V normal = V3LoadA(contactBase0->normal); + + const PxReal combinedRestitution = contactBase0->restitution; + + + SolverContactCoulombHeader* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactCoulombHeader); + + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + + const FloatV restitution = FLoad(combinedRestitution); + + + header->numNormalConstr = PxU8(contactCount); + header->type = pointHeaderType; + //header->setRestitution(combinedRestitution); + + header->setDominance0(d0); + header->setDominance1(d1); + header->angDom0 = angD0; + header->angDom1 = angD1; + header->flags = flags; + + header->setNormal(normalV); + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer.contacts + c.contactPatches[patch].start; + + PxU8* p = ptr; + for(PxU32 j=0;j(p); + p += pointStride; + + setupExtSolverContact(b0, b1, d0, d1, angD0, angD1, bodyFrame0, bodyFrame1, normal, invDtV, invDtp8, restDistance, maxPenBias, restitution, + bounceThreshold, contact, *solverContact, ccdMaxSeparation, Z); + } + ptr = p; + } + } + + //construct all the frictions + + PxU8* PX_RESTRICT ptr2 = workspace; + + const PxF32 orthoThreshold = 0.70710678f; + const PxF32 eps = 0.00001f; + bool hasFriction = false; + + for(PxU32 i=0;i< frictionPatchCount;i++) + { + const PxU32 contactCount = c.frictionPatchContactCounts[i]; + if(contactCount == 0) + continue; + + SolverContactCoulombHeader* header = reinterpret_cast(ptr2); + header->frictionOffset = PxU16(ptr - ptr2); + ptr2 += sizeof(SolverContactCoulombHeader) + header->numNormalConstr * pointStride; + + const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start; + + PxVec3 normal = contactBase0->normal; + + const PxReal staticFriction = contactBase0->staticFriction; + const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + const bool haveFriction = (disableStrongFriction == 0); + + SolverFrictionHeader* frictionHeader = reinterpret_cast(ptr); + frictionHeader->numNormalConstr = Ps::to8(c.frictionPatchContactCounts[i]); + frictionHeader->numFrictionConstr = Ps::to8(haveFriction ? c.frictionPatchContactCounts[i] * frictionCountPerPoint : 0); + frictionHeader->flags = flags; + ptr += sizeof(SolverFrictionHeader); + PxF32* forceBuffer = reinterpret_cast(ptr); + ptr += frictionHeader->getAppliedForcePaddingSize(c.frictionPatchContactCounts[i]); + PxMemZero(forceBuffer, sizeof(PxF32) * c.frictionPatchContactCounts[i]); + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + + + const PxVec3 t0Fallback1(0.f, -normal.z, normal.y); + const PxVec3 t0Fallback2(-normal.y, normal.x, 0.f) ; + const PxVec3 tFallback1 = orthoThreshold > PxAbs(normal.x) ? t0Fallback1 : t0Fallback2; + const PxVec3 vrel = b0.getLinVel() - b1.getLinVel(); + const PxVec3 t0_ = vrel - normal * (normal.dot(vrel)); + const PxReal sqDist = t0_.dot(t0_); + const PxVec3 tDir0 = (sqDist > eps ? t0_: tFallback1).getNormalized(); + const PxVec3 tDir1 = tDir0.cross(normal); + PxVec3 tFallback[2] = {tDir0, tDir1}; + + PxU32 ind = 0; + + if(haveFriction) + { + hasFriction = true; + frictionHeader->setStaticFriction(staticFriction); + frictionHeader->invMass0D0 = d0; + frictionHeader->invMass1D1 = d1; + frictionHeader->angDom0 = angD0; + frictionHeader->angDom1 = angD1; + frictionHeader->type = frictionHeaderType; + + PxU32 totalPatchContactCount = 0; + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const PxU32 start = c.contactPatches[patch].start; + const Gu::ContactPoint* contactBase = buffer.contacts + start; + + PxU8* p = ptr; + + for(PxU32 j =0; j < count; j++) + { + const Gu::ContactPoint& contact = contactBase[j]; + const PxVec3 ra = contact.point - bodyFrame0.p; + const PxVec3 rb = contact.point - bodyFrame1.p; + + const PxVec3 targetVel = contact.targetVel; + const PxVec3 pVRa = b0.getLinVel() + b0.getAngVel().cross(ra); + const PxVec3 pVRb = b1.getLinVel() + b1.getAngVel().cross(rb); + //const PxVec3 vrel = pVRa - pVRb; + + for(PxU32 k = 0; k < frictionCountPerPoint; ++k) + { + SolverContactFrictionExt* PX_RESTRICT f0 = reinterpret_cast(p); + p += frictionStride; + + PxVec3 t0 = tFallback[ind]; + ind = 1 - ind; + PxVec3 raXn = ra.cross(t0); + PxVec3 rbXn = rb.cross(t0); + Cm::SpatialVector deltaV0, deltaV1; + + const Cm::SpatialVector resp0 = createImpulseResponseVector(t0, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-t0, -rbXn, b1); + + PxReal unitResponse = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, Z); + + PxReal tv = targetVel.dot(t0); + if(b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + tv += pVRa.dot(t0); + else if(b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + tv -= pVRb.dot(t0); + + + f0->setVelMultiplier(FLoad(unitResponse>0.0f ? 1.f/unitResponse : 0.0f)); + f0->setRaXn(resp0.angular); + f0->setRbXn(-resp1.angular); + f0->targetVel = tv; + f0->setNormal(t0); + f0->setAppliedForce(0.0f); + f0->linDeltaVA = V3LoadA(deltaV0.linear); + f0->angDeltaVA = V3LoadA(deltaV0.angular); + f0->linDeltaVB = V3LoadA(deltaV1.linear); + f0->angDeltaVB = V3LoadA(deltaV1.angular); + } + } + + totalPatchContactCount += c.contactPatches[patch].count; + + ptr = p; + } + } + } + //PX_ASSERT(ptr - workspace == n.solverConstraintSize); + return hasFriction; +} + + +} + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h new file mode 100644 index 000000000..676eb8a98 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h @@ -0,0 +1,262 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_DEBUG_FNS_H +#define DY_ARTICULATION_DEBUG_FNS_H + +#include "DyArticulationFnsScalar.h" +#include "DyArticulationFnsSimd.h" + +namespace physx +{ +namespace Dy +{ +#if 0 + void printMomentum(const char* id, PxTransform* pose, Cm::SpatialVector* velocity, FsInertia* inertia, PxU32 linkCount) + { + typedef ArticulationFnsScalar Fns; + + Cm::SpatialVector m = Cm::SpatialVector::zero(); + for(PxU32 i=0;i Simd; + typedef ArticulationFnsScalar Scalar; + +public: + + static PX_FORCE_INLINE FsInertia addInertia(const FsInertia& in1, const FsInertia& in2) + { + return FsInertia(M33Add(in1.ll, in2.ll), + M33Add(in1.la, in2.la), + M33Add(in1.aa, in2.aa)); + } + + static PX_FORCE_INLINE FsInertia subtractInertia(const FsInertia& in1, const FsInertia& in2) + { + return FsInertia(M33Sub(in1.ll, in2.ll), + M33Sub(in1.la, in2.la), + M33Sub(in1.aa, in2.aa)); + } + + static Mat33V invertSym33(const Mat33V &m) + { + PxMat33 n_ = Scalar::invertSym33(unsimdify(m)); + Mat33V n = SimdBase::invertSym33(m); + compare33(n_, unsimdify(n)); + + return n; + } + + static Mat33V invSqrt(const Mat33V &m) + { + PxMat33 n_ = Scalar::invSqrt(unsimdify(m)); + Mat33V n = SimdBase::invSqrt(m); + compare33(n_, unsimdify(n)); + + return n; + } + + + + static FsInertia invertInertia(const FsInertia &I) + { + SpInertia J_ = Scalar::invertInertia(unsimdify(I)); + FsInertia J = SimdBase::invertInertia(I); + compareInertias(J_,unsimdify(J)); + + return J; + } + + static Mat33V computeSIS(const FsInertia &I, const Cm::SpatialVectorV S[3], Cm::SpatialVectorV*PX_RESTRICT IS) + { + Cm::SpatialVector IS_[3]; + Scalar::multiply(IS_, unsimdify(I), unsimdify(&S[0])); + PxMat33 D_ = Scalar::multiplySym(IS_, unsimdify(&S[0])); + + Mat33V D = SimdBase::computeSIS(I, S, IS); + + compare33(unsimdify(D), D_); + + return D; + } + + + static FsInertia multiplySubtract(const FsInertia &I, const Mat33V &D, const Cm::SpatialVectorV IS[3], Cm::SpatialVectorV*PX_RESTRICT DSI) + { + Cm::SpatialVector DSI_[3]; + + Scalar::multiply(DSI_, unsimdify(IS), unsimdify(D)); + SpInertia J_ = Scalar::multiplySubtract(unsimdify(I), DSI_, unsimdify(IS)); + + FsInertia J = SimdBase::multiplySubtract(I, D, IS, DSI); + + compareInertias(unsimdify(J), J_); + + return J; + } + + + static FsInertia multiplySubtract(const FsInertia &I, const Cm::SpatialVectorV S[3]) + { + SpInertia J_ = Scalar::multiplySubtract(unsimdify(I), unsimdify(S), unsimdify(S)); + FsInertia J = SimdBase::multiplySubtract(I, S); + compareInertias(unsimdify(J), J_); + return J; + } + + + static FsInertia translateInertia(Vec3V offset, const FsInertia &I) + { + PxVec3 offset_; + V3StoreU(offset, offset_); + SpInertia J_ = Scalar::translate(offset_, unsimdify(I)); + FsInertia J = SimdBase::translateInertia(offset, I); + compareInertias(J_, unsimdify(J)); + + return J; + } + + + static PX_FORCE_INLINE FsInertia propagate(const FsInertia &I, + const Cm::SpatialVectorV S[3], + const Mat33V &load, + const FloatV isf) + { + SpInertia J_ = Scalar::propagate(unsimdify(I), unsimdify(&S[0]), unsimdify(load), unsimdify(isf)); + FsInertia J = Simd::propagate(I, S, load, isf); + + compareInertias(J_, unsimdify(J)); + return J; + } + + + static PX_FORCE_INLINE Mat33V computeDriveInertia(const FsInertia &I0, + const FsInertia &I1, + const Cm::SpatialVectorV S[3]) + { + PxMat33 m_ = Scalar::computeDriveInertia(unsimdify(I0), unsimdify(I1), unsimdify(&S[0])); + Mat33V m = Simd::computeDriveInertia(I0, I1, S); + + compare33(m_, unsimdify(m)); + return m; + } + + static const PxMat33 unsimdify(const Mat33V &m) + { + PX_ALIGN(16, PxMat33) m_; + PxMat33_From_Mat33V(m, m_); + return m_; + } + + static PxReal unsimdify(const FloatV &m) + { + PxF32 f; + FStore(m, &f); + return f; + } + + static SpInertia unsimdify(const FsInertia &I) + { + return SpInertia (unsimdify(I.ll), + unsimdify(I.la), + unsimdify(I.aa)); + } + + static const Cm::SpatialVector* unsimdify(const Cm::SpatialVectorV *S) + { + return reinterpret_cast(S); + } + + +private: + + static PxReal absmax(const PxVec3& n) + { + return PxMax(PxAbs(n.x), PxMax(PxAbs(n.y),PxAbs(n.z))); + } + + static PxReal norm(const PxMat33& n) + { + return PxMax(absmax(n.column0), PxMax(absmax(n.column1), absmax(n.column2))); + } + + static void compare33(const PxMat33& ref, const PxMat33& n) + { + PxReal errNorm = norm(ref-n); + PX_UNUSED(errNorm); + PX_ASSERT(errNorm <= PxMax(norm(ref)*1e-3f, 1e-4f)); + } + + static void compareInertias(const SpInertia& a, const SpInertia& b) + { + compare33(a.mLL, b.mLL); + compare33(a.mLA, b.mLA); + compare33(a.mAA, b.mAA); + } + + +}; + +#if DY_ARTICULATION_DEBUG_VERIFY +static bool isPositiveDefinite(const Mat33V& m) +{ + PX_ALIGN_PREFIX(16) PxMat33 m1 PX_ALIGN_SUFFIX(16); + PxMat33_From_Mat33V(m, m1); + return isPositiveDefinite(m1); +} + + +static bool isPositiveDefinite(const FsInertia& s) +{ + return isPositiveDefinite(ArticulationFnsDebug::unsimdify(s)); +} + +static PxReal magnitude(const Cm::SpatialVectorV &v) +{ + return PxSqrt(FStore(V3Dot(v.linear, v.linear)) + FStore(V3Dot(v.angular, v.angular))); +} + +static bool almostEqual(const Cm::SpatialVectorV &ref, const Cm::SpatialVectorV& test, PxReal tolerance) +{ + return magnitude(ref-test)<=tolerance*magnitude(ref); +} +#endif +} +} + +#endif //DY_ARTICULATION_DEBUG_FNS_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h new file mode 100644 index 000000000..24a8d3f02 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h @@ -0,0 +1,397 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_SCALAR_FNS_H +#define DY_ARTICULATION_SCALAR_FNS_H + +// Scalar helpers for articulations + +#include "DyArticulationUtils.h" +#include "DyArticulationScalar.h" +#include "DySpatial.h" + +namespace physx +{ + +namespace Dy +{ + +/* +namespace +{ + static void print(const PxMat33 &m) + { + printf("(%f, %f, %f)\n(%f, %f, %f)\n(%f, %f, %f)\n\n", + m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); + } + + static void print(const Cm::SpatialVector *v, PxU32 count) + { + for(PxU32 i=0;i(m.col0) * v.x + + reinterpret_cast(m.col1) * v.y + + reinterpret_cast(m.col2) * v.z; + } + + static PX_FORCE_INLINE PxVec3 multiplyTranspose(const Mat33V& m, const PxVec3& v) + { + return PxVec3(v.dot(reinterpret_cast(m.col0)), + v.dot(reinterpret_cast(m.col1)), + v.dot(reinterpret_cast(m.col2))); + } + + static Cm::SpatialVector multiply(const FsInertia& m, const Cm::SpatialVector& v) + { + return Cm::SpatialVector(multiply(m.ll,v.linear) + multiply(m.la,v.angular), + multiplyTranspose(m.la, v.linear) + multiply(m.aa, v.angular)); + } + + static PX_FORCE_INLINE Cm::SpatialVector getRootDeltaV(const FsData& matrix, const Cm::SpatialVector& Z) + { + return multiply(getRootInverseInertia(matrix), Z); + } +}; + +} + +} + +#endif //DY_ARTICULATION_SCALAR_FNS_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h new file mode 100644 index 000000000..cf3bfef99 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h @@ -0,0 +1,438 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_SIMD_FNS_H +#define DY_ARTICULATION_SIMD_FNS_H + +#include "DyArticulationUtils.h" + +namespace physx +{ +namespace Dy +{ + +template +class PodULike +{ + PxU8 space[sizeof(T)*count]; +public: + PX_FORCE_INLINE operator T*() { return reinterpret_cast(space); } +}; + +#define POD_U_LIKE(_T, _count, _alignment) PX_ALIGN_PREFIX(_alignment) PodULike<_T, _count> PX_ALIGN_SUFFIX(_alignment) + +class ArticulationFnsSimdBase +{ +public: + + static PX_FORCE_INLINE FsInertia addInertia(const FsInertia& in1, const FsInertia& in2) + { + return FsInertia(M33Add(in1.ll, in2.ll), + M33Add(in1.la, in2.la), + M33Add(in1.aa, in2.aa)); + } + + static PX_FORCE_INLINE FsInertia subtractInertia(const FsInertia& in1, const FsInertia& in2) + { + return FsInertia(M33Sub(in1.ll, in2.ll), + M33Sub(in1.la, in2.la), + M33Sub(in1.aa, in2.aa)); + } + + static PX_FORCE_INLINE Vec3V axisDot(const Cm::SpatialVectorV S[3], const Cm::SpatialVectorV &v) + { + return V3Merge(FAdd(V3Dot(S[0].linear,v.linear), V3Dot(S[0].angular,v.angular)), + FAdd(V3Dot(S[1].linear,v.linear), V3Dot(S[1].angular,v.angular)), + FAdd(V3Dot(S[2].linear,v.linear), V3Dot(S[2].angular,v.angular))); + } + + static PX_FORCE_INLINE Cm::SpatialVectorV axisMultiply(const Cm::SpatialVectorV S[3], Vec3V v) + { + return Cm::SpatialVectorV(V3ScaleAdd(S[0].linear, V3GetX(v), V3ScaleAdd(S[1].linear, V3GetY(v), V3Scale(S[2].linear, V3GetZ(v)))), + V3ScaleAdd(S[0].angular, V3GetX(v), V3ScaleAdd(S[1].angular, V3GetY(v), V3Scale(S[2].angular, V3GetZ(v))))); + } + + + static PX_FORCE_INLINE Cm::SpatialVectorV subtract(const Cm::SpatialVectorV &a, const Cm::SpatialVectorV &b) + { + return Cm::SpatialVectorV(V3Sub(a.linear, b.linear), V3Sub(a.angular, b.angular)); + } + + static PX_FORCE_INLINE Cm::SpatialVectorV add(const Cm::SpatialVectorV &a, const Cm::SpatialVectorV &b) + { + return Cm::SpatialVectorV(V3Add(a.linear, b.linear), V3Add(a.angular, b.angular)); + } + + + static PX_FORCE_INLINE Cm::SpatialVectorV multiply(const FsInertia &I, const Cm::SpatialVectorV &S) + { + return Cm::SpatialVectorV(V3Add(M33MulV3(I.ll,S.linear), M33MulV3(I.la,S.angular)), + V3Add(M33TrnspsMulV3(I.la,S.linear), M33MulV3(I.aa,S.angular))); + } + + + static PX_FORCE_INLINE Cm::SpatialVectorV translateMotion(const Vec3V& p, const Cm::SpatialVectorV& v) + { + return Cm::SpatialVectorV(V3Add(v.linear, V3Cross(p, v.angular)), v.angular); + } + + // translate a force resolved at position p to the origin + + static PX_FORCE_INLINE Cm::SpatialVectorV translateForce(const Vec3V& p, const Cm::SpatialVectorV& v) + { + return Cm::SpatialVectorV(v.linear, V3Add(v.angular, V3Cross(p, v.linear))); + } + + static PX_FORCE_INLINE Mat33V invertSym33(const Mat33V &m) + { + Vec3V a0 = V3Cross(m.col1, m.col2); + Vec3V a1 = V3Cross(m.col2, m.col0); + Vec3V a2 = V3Cross(m.col0, m.col1); + FloatV det = V3Dot(a0, m.col0); + FloatV recipDet = FRecip(det); + + a1 = V3SetX(a1, V3GetY(a0)); + a2 = V3Merge(V3GetZ(a0), V3GetZ(a1), V3GetZ(a2)); // make sure it's symmetric + + return Mat33V(V3Scale(a0, recipDet), + V3Scale(a1, recipDet), + V3Scale(a2, recipDet)); + } + + + static PX_FORCE_INLINE FloatV safeInvSqrt(FloatV v) + { + return FSqrt(FMax(FZero(), FRecip(v))); + } + static PX_FORCE_INLINE Mat33V invSqrt(const Mat33V& m) + { + // cholesky factor to + // (a 0 0) + // (b c 0) + // (d e f) + // except that a,c,f are the reciprocal sqrts rather than sqrts + + // PxVec3 v0 = m.column0, v1 = m.column1, v2 = m.column2; + Vec3V v0 = m.col0, v1 = m.col1, v2 = m.col2; + + const FloatV x0 = V3GetX(v0), y1 = V3GetY(v1), z2 = V3GetZ(v2); + + FloatV a = safeInvSqrt(x0); // PxReal a = PxRecipSqrt(v0.x); + + Vec3V abd = V3Scale(v0, a); // PxReal b = v0.y*a; + FloatV b = V3GetY(abd); + + FloatV c2 = FNegScaleSub(b, b, y1); // PxReal c = PxRecipSqrt(v1.y - b*b); + FloatV c = safeInvSqrt(c2); + + FloatV d = V3GetZ(abd); // PxReal d = v0.z*a; + + FloatV e = FMul(FNegScaleSub(b, d, V3GetZ(v1)), c); // PxReal e = (v1.z-d*b) * c; + + FloatV f2 = FNegScaleSub(d, d, FNegScaleSub(e, e, z2)); // PxReal f = PxRecipSqrt(v2.z - d*d - e*e); + FloatV f = safeInvSqrt(f2); + + // invert + FloatV x = FMul(FMul(b,a),c), // x = -b*a*c + y = FMul((FNegScaleSub(d,a, FMul(e,x))), f), // y = (-e*x-d*a)*f + z = FMul(e, FMul(c,f)); // z = -e*c*f + + return Mat33V(V3Merge(a, FZero(), FZero()), + V3Merge(FNeg(x), c, FZero()), + V3Merge(y, FNeg(z), f)); + } + + + static PX_FORCE_INLINE FsInertia invertInertia(const FsInertia &I) + { + Mat33V aa = M33Scale(M33Add(I.aa, M33Trnsps(I.aa)), FHalf()); + Mat33V ll = M33Scale(M33Add(I.ll, M33Trnsps(I.ll)), FHalf()); + + Mat33V AAInv = invertSym33(aa); + Mat33V z = M33MulM33(M33Neg(I.la), AAInv); + Mat33V S = M33Add(ll, M33MulM33(z, M33Trnsps(I.la))); + + Mat33V LL = invertSym33(S); + Mat33V LA = M33MulM33(LL, z); + Mat33V AA = M33Add(AAInv, M33MulM33(M33Trnsps(z), LA)); + + return FsInertia(LL, LA, AA); + } + + static PX_NOINLINE Mat33V computeSIS(const FsInertia &I, const Cm::SpatialVectorV S[3], Cm::SpatialVectorV IS[3]) + { + Vec3V S0l = S[0].linear, S0a = S[0].angular; + Vec3V S1l = S[1].linear, S1a = S[1].angular; + Vec3V S2l = S[2].linear, S2a = S[2].angular; + + Vec3V IS0l = V3Add(M33MulV3(I.ll,S0l), M33MulV3(I.la,S0a)); + Vec3V IS0a = V3Add(M33TrnspsMulV3(I.la,S0l), M33MulV3(I.aa,S0a)); + Vec3V IS1l = V3Add(M33MulV3(I.ll,S1l), M33MulV3(I.la,S1a)); + Vec3V IS1a = V3Add(M33TrnspsMulV3(I.la,S1l), M33MulV3(I.aa,S1a)); + Vec3V IS2l = V3Add(M33MulV3(I.ll,S2l), M33MulV3(I.la,S2a)); + Vec3V IS2a = V3Add(M33TrnspsMulV3(I.la,S2l), M33MulV3(I.aa,S2a)); + + // compute SIS + FloatV a00 = FAdd(V3Dot(S0l, IS0l), V3Dot(S0a, IS0a)); + FloatV a01 = FAdd(V3Dot(S0l, IS1l), V3Dot(S0a, IS1a)); + FloatV a02 = FAdd(V3Dot(S0l, IS2l), V3Dot(S0a, IS2a)); + FloatV a11 = FAdd(V3Dot(S1l, IS1l), V3Dot(S1a, IS1a)); + FloatV a12 = FAdd(V3Dot(S1l, IS2l), V3Dot(S1a, IS2a)); + FloatV a22 = FAdd(V3Dot(S2l, IS2l), V3Dot(S2a, IS2a)); + + // write IS, a useful side-effect + IS[0].linear = IS0l; IS[0].angular = IS0a; + IS[1].linear = IS1l; IS[1].angular = IS1a; + IS[2].linear = IS2l; IS[2].angular = IS2a; + + return Mat33V(V3Merge(a00, a01, a02), + V3Merge(a01, a11, a12), + V3Merge(a02, a12, a22)); + } + + + static PX_FORCE_INLINE FsInertia multiplySubtract(const FsInertia &I, const Mat33V &D, const Cm::SpatialVectorV IS[3], Cm::SpatialVectorV DSI[3]) + { + // cut'n'paste, how I love ya, how I love ya + + Vec3V IS0l = IS[0].linear, IS0a = IS[0].angular; + Vec3V IS1l = IS[1].linear, IS1a = IS[1].angular; + Vec3V IS2l = IS[2].linear, IS2a = IS[2].angular; + + Vec3V D0 = D.col0, D1 = D.col1, D2 = D.col2; + + // compute IDS + Vec3V DSI0l = V3ScaleAdd(IS0l, V3GetX(D0), V3ScaleAdd(IS1l, V3GetY(D0), V3Scale(IS2l, V3GetZ(D0)))); + Vec3V DSI1l = V3ScaleAdd(IS0l, V3GetX(D1), V3ScaleAdd(IS1l, V3GetY(D1), V3Scale(IS2l, V3GetZ(D1)))); + Vec3V DSI2l = V3ScaleAdd(IS0l, V3GetX(D2), V3ScaleAdd(IS1l, V3GetY(D2), V3Scale(IS2l, V3GetZ(D2)))); + + Vec3V DSI0a = V3ScaleAdd(IS0a, V3GetX(D0), V3ScaleAdd(IS1a, V3GetY(D0), V3Scale(IS2a, V3GetZ(D0)))); + Vec3V DSI1a = V3ScaleAdd(IS0a, V3GetX(D1), V3ScaleAdd(IS1a, V3GetY(D1), V3Scale(IS2a, V3GetZ(D1)))); + Vec3V DSI2a = V3ScaleAdd(IS0a, V3GetX(D2), V3ScaleAdd(IS1a, V3GetY(D2), V3Scale(IS2a, V3GetZ(D2)))); + + // compute J = I - DSI' IS. Each row of DSI' IS generates an inertia dyad + + Vec3V ll0 = I.ll.col0, ll1 = I.ll.col1, ll2 = I.ll.col2; + Vec3V la0 = I.la.col0, la1 = I.la.col1, la2 = I.la.col2; + Vec3V aa0 = I.aa.col0, aa1 = I.aa.col1, aa2 = I.aa.col2; + +#define SUBTRACT_DYAD(_a, _b) \ + ll0 = V3NegScaleSub(_b##l, V3GetX(_a##l), ll0); la0 = V3NegScaleSub(_b##l, V3GetX(_a##a), la0); aa0 = V3NegScaleSub(_b##a, V3GetX(_a##a), aa0); \ + ll1 = V3NegScaleSub(_b##l, V3GetY(_a##l), ll1); la1 = V3NegScaleSub(_b##l, V3GetY(_a##a), la1); aa1 = V3NegScaleSub(_b##a, V3GetY(_a##a), aa1); \ + ll2 = V3NegScaleSub(_b##l, V3GetZ(_a##l), ll2); la2 = V3NegScaleSub(_b##l, V3GetZ(_a##a), la2); aa2 = V3NegScaleSub(_b##a, V3GetZ(_a##a), aa2); + + SUBTRACT_DYAD(IS0, DSI0); + SUBTRACT_DYAD(IS1, DSI1); + SUBTRACT_DYAD(IS2, DSI2); +#undef SUBTRACT_DYAD + + DSI[0].linear = DSI0l; DSI[0].angular = DSI0a; + DSI[1].linear = DSI1l; DSI[1].angular = DSI1a; + DSI[2].linear = DSI2l; DSI[2].angular = DSI2a; + + return FsInertia(Mat33V(ll0, ll1, ll2), + Mat33V(la0, la1, la2), + Mat33V(aa0, aa1, aa2)); + } + + + static PX_FORCE_INLINE FsInertia multiplySubtract(const FsInertia &I, const Cm::SpatialVectorV S[3]) + { + // cut'n'paste, how I love ya, how I love ya + + const Vec3V S0l = S[0].linear, S0a = S[0].angular; + const Vec3V S1l = S[1].linear, S1a = S[1].angular; + const Vec3V S2l = S[2].linear, S2a = S[2].angular; + + // compute J = I - DSI' IS. Each row of DSI' IS generates an inertia dyad + + Vec3V ll0 = I.ll.col0, ll1 = I.ll.col1, ll2 = I.ll.col2; + Vec3V la0 = I.la.col0, la1 = I.la.col1, la2 = I.la.col2; + Vec3V aa0 = I.aa.col0, aa1 = I.aa.col1, aa2 = I.aa.col2; + +#define SUBTRACT_DYAD(_a, _b) \ + ll0 = V3NegScaleSub(_b##l, V3GetX(_a##l), ll0); la0 = V3NegScaleSub(_b##l, V3GetX(_a##a), la0); aa0 = V3NegScaleSub(_b##a, V3GetX(_a##a), aa0); \ + ll1 = V3NegScaleSub(_b##l, V3GetY(_a##l), ll1); la1 = V3NegScaleSub(_b##l, V3GetY(_a##a), la1); aa1 = V3NegScaleSub(_b##a, V3GetY(_a##a), aa1); \ + ll2 = V3NegScaleSub(_b##l, V3GetZ(_a##l), ll2); la2 = V3NegScaleSub(_b##l, V3GetZ(_a##a), la2); aa2 = V3NegScaleSub(_b##a, V3GetZ(_a##a), aa2); + + SUBTRACT_DYAD(S0, S0); + SUBTRACT_DYAD(S1, S1); + SUBTRACT_DYAD(S2, S2); +#undef SUBTRACT_DYAD + + return FsInertia(Mat33V(ll0, ll1, ll2), + Mat33V(la0, la1, la2), + Mat33V(aa0, aa1, aa2)); + } + + + static PX_FORCE_INLINE FsInertia translateInertia(Vec3V a, const FsInertia &input) + { + Vec3V b = V3Neg(a); + + Vec3V la0 = input.la.col0, la1 = input.la.col1, la2 = input.la.col2; + Vec3V ll0 = input.ll.col0, ll1 = input.ll.col1, ll2 = input.ll.col2; + Vec3V aa0 = input.aa.col0, aa1 = input.aa.col1, aa2 = input.aa.col2; + + FloatV aX = V3GetX(a), aY = V3GetY(a), aZ = V3GetZ(a); + FloatV bX = V3GetX(b), bY = V3GetY(b), bZ = V3GetZ(b); + FloatV Z = FZero(); + + // s - star matrix of a + Vec3V s0 = V3Merge(Z, aZ, bY), + s1 = V3Merge(bZ, Z, aX), + s2 = V3Merge(aY, bX, Z); + + // s * la + Vec3V sla0 = V3ScaleAdd(s0, V3GetX(la0), V3ScaleAdd(s1, V3GetY(la0), V3Scale(s2, V3GetZ(la0)))); + Vec3V sla1 = V3ScaleAdd(s0, V3GetX(la1), V3ScaleAdd(s1, V3GetY(la1), V3Scale(s2, V3GetZ(la1)))); + Vec3V sla2 = V3ScaleAdd(s0, V3GetX(la2), V3ScaleAdd(s1, V3GetY(la2), V3Scale(s2, V3GetZ(la2)))); + + // ll * s.transpose() (ll is symmetric) + Vec3V llst0 = V3ScaleAdd(ll2, aY, V3Scale(ll1, bZ)), + llst1 = V3ScaleAdd(ll0, aZ, V3Scale(ll2, bX)), + llst2 = V3ScaleAdd(ll1, aX, V3Scale(ll0, bY)); + + // t = sla+S*llst*0.5f; + + Vec3V sllst0 = V3ScaleAdd(s2, V3GetZ(llst0), V3ScaleAdd(s1, V3GetY(llst0), V3Scale(s0, V3GetX(llst0)))); + Vec3V sllst1 = V3ScaleAdd(s2, V3GetZ(llst1), V3ScaleAdd(s1, V3GetY(llst1), V3Scale(s0, V3GetX(llst1)))); + Vec3V sllst2 = V3ScaleAdd(s2, V3GetZ(llst2), V3ScaleAdd(s1, V3GetY(llst2), V3Scale(s0, V3GetX(llst2)))); + + Vec3V t0 = V3ScaleAdd(sllst0, FHalf(), sla0); + Vec3V t1 = V3ScaleAdd(sllst1, FHalf(), sla1); + Vec3V t2 = V3ScaleAdd(sllst2, FHalf(), sla2); + + // t+t.transpose() + Vec3V r0 = V3Add(t0, V3Merge(V3GetX(t0), V3GetX(t1), V3GetX(t2))), + r1 = V3Add(t1, V3Merge(V3GetY(t0), V3GetY(t1), V3GetY(t2))), + r2 = V3Add(t2, V3Merge(V3GetZ(t0), V3GetZ(t1), V3GetZ(t2))); + + return FsInertia(Mat33V(ll0, ll1, ll2), + + Mat33V(V3Add(la0, llst0), + V3Add(la1, llst1), + V3Add(la2, llst2)), + + Mat33V(V3Add(aa0, r0), + V3Add(aa1, r1), + V3Add(aa2, r2))); + } + +}; + +template +class ArticulationFnsSimd : public Base +{ + static PX_FORCE_INLINE void axisMultiplyLowerTriangular(Cm::SpatialVectorV ES[3], const Mat33V&E, const Cm::SpatialVectorV S[3]) + { + const Vec3V l0 = S[0].linear, l1 = S[1].linear, l2 = S[2].linear; + const Vec3V a0 = S[0].angular, a1 = S[1].angular, a2 = S[2].angular; + ES[0] = Cm::SpatialVectorV(V3Scale(l0, V3GetX(E.col0)), + V3Scale(a0, V3GetX(E.col0))); + ES[1] = Cm::SpatialVectorV(V3ScaleAdd(l0, V3GetX(E.col1), V3Scale(l1, V3GetY(E.col1))), + V3ScaleAdd(a0, V3GetX(E.col1), V3Scale(a1, V3GetY(E.col1)))); + ES[2] = Cm::SpatialVectorV(V3ScaleAdd(l0, V3GetX(E.col2), V3ScaleAdd(l1, V3GetY(E.col2), V3Scale(l2, V3GetZ(E.col2)))), + V3ScaleAdd(a0, V3GetX(E.col2), V3ScaleAdd(a1, V3GetY(E.col2), V3Scale(a2, V3GetZ(E.col2))))); + } + +public: + static PX_FORCE_INLINE FsInertia propagate(const FsInertia &I, + const Cm::SpatialVectorV S[3], + const Mat33V &load, + const FloatV isf) + { + Cm::SpatialVectorV IS[3], ISE[3]; + Mat33V D = Base::computeSIS(I, S, IS); + + D.col0 = V3ScaleAdd(load.col0, isf, D.col0); + D.col1 = V3ScaleAdd(load.col1, isf, D.col1); + D.col2 = V3ScaleAdd(load.col2, isf, D.col2); + + axisMultiplyLowerTriangular(ISE, Base::invSqrt(D), IS); + return Base::multiplySubtract(I, ISE); + } + + + + static PX_INLINE Cm::SpatialVectorV propagateImpulse(const FsRow& row, + const FsJointVectors& jv, + Vec3V& SZ, + const Cm::SpatialVectorV& Z, + const FsRowAux& aux) + { + PX_UNUSED(aux); + + SZ = V3Add(Z.angular, V3Cross(Z.linear, jv.jointOffset)); + return Base::translateForce(jv.parentOffset, Z - Base::axisMultiply(row.DSI, SZ)); + } + + static PX_INLINE Cm::SpatialVectorV propagateVelocity(const FsRow& row, + const FsJointVectors& jv, + const Vec3V& SZ, + const Cm::SpatialVectorV& v, + const FsRowAux& aux) + { + PX_UNUSED(aux); + + Cm::SpatialVectorV w = Base::translateMotion(V3Neg(jv.parentOffset), v); + Vec3V DSZ = M33MulV3(row.D, SZ); + + Vec3V n = V3Add(Base::axisDot(row.DSI, w), DSZ); + return w - Cm::SpatialVectorV(V3Cross(jv.jointOffset, n), n); + } + + + + + + static PX_FORCE_INLINE Mat33V computeDriveInertia(const FsInertia &I0, + const FsInertia &I1, + const Cm::SpatialVectorV S[3]) + { + POD_U_LIKE(Cm::SpatialVectorV, 3, 16) IS, ISD, dummy; + Mat33V D = Base::computeSIS(I0, S, IS); + Mat33V DInv = Base::invertSym33(D); + + FsInertia tmp = Base::addInertia(I0, I1); + tmp = Base::multiplySubtract(tmp, DInv, IS, ISD); + FsInertia J = Base::invertInertia(tmp); + + Mat33V E = Base::computeSIS(J, ISD, dummy); + return Base::invertSym33(M33Add(DInv,E)); + + } +}; + +} +} + +#endif //DY_ARTICULATION_SIMD_FNS_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp new file mode 100644 index 000000000..4653413c3 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp @@ -0,0 +1,524 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxVec3.h" +#include "foundation/PxMath.h" +#include "foundation/PxMemory.h" +#include "common/PxProfileZone.h" + +#include "PsUtilities.h" +#include "CmSpatialVector.h" +#include "DyArticulationHelper.h" +#include "DyArticulationReference.h" +#include "DyArticulationFnsSimd.h" +#include "DyArticulationFnsScalar.h" +#include "DyArticulationFnsDebug.h" +#include "DySolverConstraintDesc.h" +#include "PxvDynamics.h" +#include "DyArticulation.h" +#include "PxcRigidBody.h" +#include "CmConeLimitHelper.h" +#include "DySolverConstraint1D.h" +#include "PxcConstraintBlockStream.h" +#include "DySolverConstraint1D.h" +#include "DyArticulationPImpl.h" +#include "PsFoundation.h" + +namespace physx +{ + +namespace Dy +{ + + +void getImpulseResponseSlow(const FsData& matrix, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1) +{ + typedef ArticulationFnsSimd Fns; + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_ASSERT(matrix.linkCount<=DY_ARTICULATION_MAX_SIZE); + PxU32 stack[DY_ARTICULATION_MAX_SIZE]; + Vec3V SZ[DY_ARTICULATION_MAX_SIZE]; + + PxU32 i0, i1, ic; + + for(i0 = linkID0, i1 = linkID1; i0!=i1;) // find common path + { + if(i0i1 ;) + v = Fns::propagateVelocity(rows[stack[index]], jointVectors[stack[index]], SZ[stack[index]], v, aux[stack[index]]); + + deltaV1 = v; + for(PxU32 index = i1; index-->i0 ;) + deltaV1 = Fns::propagateVelocity(rows[stack[index]], jointVectors[stack[index]], SZ[stack[index]], deltaV1, aux[stack[index]]); + + deltaV0 = v; + for(PxU32 index = i0; index-->0;) + deltaV0 = Fns::propagateVelocity(rows[stack[index]], jointVectors[stack[index]], SZ[stack[index]], deltaV0, aux[stack[index]]); +} + +void PxcFsGetImpulseResponse(const FsData& matrix, + PxU32 linkID, + const Cm::SpatialVectorV& impulse, + Cm::SpatialVectorV& deltaV) +{ + typedef ArticulationFnsSimd Fns; + + PX_ASSERT(matrix.linkCount<=DY_ARTICULATION_MAX_SIZE); + Vec3V SZ[DY_ARTICULATION_MAX_SIZE]; + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + Cm::SpatialVectorV Z = -impulse; + + for(PxU32 i = linkID; i; i = matrix.parent[i]) + Z = Fns::propagateImpulse(rows[i], jointVectors[i], SZ[i], Z, aux[i]); + + deltaV = Fns::multiply(getRootInverseInertia(matrix), -Z); + + PX_ASSERT(rows[linkID].pathToRoot&1); + + for(ArticulationBitField i=rows[linkID].pathToRoot-1; i; i &= (i-1)) + { + const PxU32 index = ArticulationLowestSetBit(i); + deltaV = Fns::propagateVelocity(rows[index], jointVectors[index], SZ[index], deltaV, aux[index]); + } +} + +void PxcFsGetImpulseSelfResponse(const FsData& matrix, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1) +{ + typedef ArticulationFnsSimd Fns; + + PX_ASSERT(linkID0 != linkID1); + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + // standard case: parent-child limit + if(matrix.parent[linkID1] == linkID0) + { + Vec3V SZ; + const Cm::SpatialVectorV Z = impulse0 - Fns::propagateImpulse(rows[linkID1], jointVectors[linkID1], SZ, -impulse1, aux[linkID1]); + PxcFsGetImpulseResponse(matrix, linkID0, Z, deltaV0); + deltaV1 = Fns::propagateVelocity(rows[linkID1], jointVectors[linkID1], SZ, deltaV0, aux[linkID1]); + } + else + getImpulseResponseSlow(matrix, linkID0, impulse0, deltaV0, linkID1, impulse1, deltaV1); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector V[DY_ARTICULATION_MAX_SIZE]; + for(PxU32 i=0;i(impulse0)); + ArticulationRef::applyImpulse(matrix,V,linkID1, reinterpret_cast(impulse1)); + + Cm::SpatialVector refV0 = V[linkID0]; + Cm::SpatialVector refV1 = V[linkID1]; +#endif +} + +namespace +{ + + PX_FORCE_INLINE Cm::SpatialVectorV getImpulseResponseSimd(const FsData& matrix, PxU32 linkID, Vec3V lZ, Vec3V aZ) + { + PX_ASSERT(matrix.linkCount<=DY_ARTICULATION_MAX_SIZE); + Vec3V SZ[DY_ARTICULATION_MAX_SIZE]; + PxU32 indices[DY_ARTICULATION_MAX_SIZE], iCount = 0; + + const FsRow*PX_RESTRICT rows = getFsRows(matrix); + const FsRowAux*PX_RESTRICT aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_UNUSED(aux); + PX_ASSERT(rows[linkID].pathToRoot&1); + + lZ = V3Neg(lZ); + aZ = V3Neg(aZ); + + for(PxU32 i = linkID; i; i = matrix.parent[i]) + { + const FsRow& r = rows[i]; + const FsJointVectors& j = jointVectors[i]; + + Vec3V sz = V3Add(aZ, V3Cross(lZ, j.jointOffset)); + SZ[iCount] = sz; + + lZ = V3NegScaleSub(r.DSI[0].linear, V3GetX(sz), V3NegScaleSub(r.DSI[1].linear, V3GetY(sz), V3NegScaleSub(r.DSI[2].linear, V3GetZ(sz), lZ))); + aZ = V3NegScaleSub(r.DSI[0].angular, V3GetX(sz), V3NegScaleSub(r.DSI[1].angular, V3GetY(sz), V3NegScaleSub(r.DSI[2].angular, V3GetZ(sz), aZ))); + + aZ = V3Add(aZ, V3Cross(j.parentOffset, lZ)); + indices[iCount++] = i; + } + + const FsInertia& I = getRootInverseInertia(matrix); + + Vec3V lV = V3Neg(V3Add(M33MulV3(I.ll, lZ), M33MulV3(I.la, aZ))); + Vec3V aV = V3Neg(V3Add(M33TrnspsMulV3(I.la, lZ), M33MulV3(I.aa, aZ))); + + while(iCount) + { + PxU32 i = indices[--iCount]; + const FsRow& r = rows[i]; + const FsJointVectors& j = jointVectors[i]; + + lV = V3Sub(lV, V3Cross(j.parentOffset, aV)); + + Vec3V n = V3Add(V3Merge(V3Dot(r.DSI[0].linear, lV), V3Dot(r.DSI[1].linear, lV), V3Dot(r.DSI[2].linear, lV)), + V3Merge(V3Dot(r.DSI[0].angular, aV), V3Dot(r.DSI[1].angular, aV), V3Dot(r.DSI[2].angular, aV))); + + n = V3Add(n, M33MulV3(r.D, SZ[iCount])); + lV = V3Sub(lV, V3Cross(j.jointOffset, n)); + aV = V3Sub(aV, n); + } + + return Cm::SpatialVectorV(lV, aV); + } +} + +void ArticulationHelper::getImpulseResponse(const FsData& matrix, + PxU32 linkID, + const Cm::SpatialVectorV& impulse, + Cm::SpatialVectorV& deltaV) +{ + PX_ASSERT(matrix.linkCount<=DY_ARTICULATION_MAX_SIZE); + + deltaV = getImpulseResponseSimd(matrix, linkID, impulse.linear, impulse.angular); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVectorV deltaV_; + PxcFsGetImpulseResponse(matrix, linkID, impulse, deltaV_); + PX_ASSERT(almostEqual(deltaV_, deltaV,1e-3f)); +#endif +} + +void ArticulationHelper::getImpulseSelfResponse(const FsData& matrix, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1) +{ + PX_ASSERT(linkID0 != linkID1); + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_UNUSED(aux); + + Cm::SpatialVectorV& dV0 = deltaV0, + & dV1 = deltaV1; + + // standard case: parent-child limit + if(matrix.parent[linkID1] == linkID0) + { + const FsRow& r = rows[linkID1]; + const FsJointVectors& j = jointVectors[linkID1]; + + Vec3V lZ = V3Neg(impulse1.linear), + aZ = V3Neg(impulse1.angular); + + Vec3V sz = V3Add(aZ, V3Cross(lZ, j.jointOffset)); + + lZ = V3Sub(lZ, V3ScaleAdd(r.DSI[0].linear, V3GetX(sz), V3ScaleAdd(r.DSI[1].linear, V3GetY(sz), V3Scale(r.DSI[2].linear, V3GetZ(sz))))); + aZ = V3Sub(aZ, V3ScaleAdd(r.DSI[0].angular, V3GetX(sz), V3ScaleAdd(r.DSI[1].angular, V3GetY(sz), V3Scale(r.DSI[2].angular, V3GetZ(sz))))); + + aZ = V3Add(aZ, V3Cross(j.parentOffset, lZ)); + + lZ = V3Sub(impulse0.linear, lZ); + aZ = V3Sub(impulse0.angular, aZ); + + dV0 = getImpulseResponseSimd(matrix, linkID0, lZ, aZ); + + Vec3V aV = dV0.angular; + Vec3V lV = V3Sub(dV0.linear, V3Cross(j.parentOffset, aV)); + + Vec3V n = V3Add(V3Merge(V3Dot(r.DSI[0].linear, lV), V3Dot(r.DSI[1].linear, lV), V3Dot(r.DSI[2].linear, lV)), + V3Merge(V3Dot(r.DSI[0].angular, aV), V3Dot(r.DSI[1].angular, aV), V3Dot(r.DSI[2].angular, aV))); + + n = V3Add(n, M33MulV3(r.D, sz)); + lV = V3Sub(lV, V3Cross(j.jointOffset, n)); + aV = V3Sub(aV, n); + + dV1 = Cm::SpatialVectorV(lV, aV); + } + else + { +#if 0 + getImpulseResponseSlow(matrix, linkID0, impulse0, deltaV0, linkID1, impulse1, deltaV1); +#else + getImpulseResponse(matrix, linkID0, impulse0, deltaV0); + getImpulseResponse(matrix, linkID1, impulse1, deltaV1); +#endif + } + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVectorV dV0_, dV1_; + PxcFsGetImpulseSelfResponse(matrix, linkID0, impulse0, dV0_, linkID1, impulse1, dV1_); + + PX_ASSERT(almostEqual(dV0_, dV0, 1e-3f)); + PX_ASSERT(almostEqual(dV1_, dV1, 1e-3f)); +#endif +} + +PxU32 ArticulationHelper::getFsDataSize(PxU32 linkCount) +{ + return sizeof(FsInertia) + sizeof(FsRow) * linkCount; +} + +PxU32 ArticulationHelper::getLtbDataSize(PxU32 linkCount) +{ + return sizeof(LtbRow) * linkCount; +} + + +void ArticulationHelper::createHardLimit( const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt) +{ + init(s, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); + + ArticulationHelper::getImpulseSelfResponse(fsData, + links[linkIndex].parent,Cm::SpatialVector(PxVec3(0), axis), s.deltaVA, + linkIndex, Cm::SpatialVector(PxVec3(0), -axis), s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if(unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, joint limit ignored"); + + const PxReal recipResponse = unitResponse>0.0f ? 1.0f/unitResponse : 0.0f; + + s.constant = recipResponse * -err * recipDt; + s.unbiasedConstant = err>0.0f ? s.constant : 0.0f; + s.velMultiplier = -recipResponse; + s.impulseMultiplier = 1.0f; +} + +void ArticulationHelper::createTangentialSpring(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt) +{ + init(s, PxVec3(0), PxVec3(0), axis, axis, -PX_MAX_F32, PX_MAX_F32); + + Cm::SpatialVector axis6(PxVec3(0), axis); + PxU32 parent = links[linkIndex].parent; + getImpulseSelfResponse(fsData, parent, axis6, s.deltaVA, linkIndex, -axis6, s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if(unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, tangential spring ignored"); + const PxReal recipResponse = unitResponse>0.0F ? 1.0f/unitResponse : 0.0f; + + // this is a specialization of the spring code in setSolverConstants() for acceleration springs. + // general case is b = dt * (c.mods.spring.damping * c.velocityTarget - c.mods.spring.stiffness * geomError); + // but geomError and velocityTarget are both zero + + const PxReal a = dt * dt * stiffness + dt * damping; + const PxReal x = 1.0f/(1.0f+a); + s.constant = s.unbiasedConstant = 0.0f; + s.velMultiplier = -x * recipResponse * a; + s.impulseMultiplier = 1.0f - x; +} + +PxU32 ArticulationHelper::setupSolverConstraints( Articulation& articulation, PxU32 solverDataSize, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + const ArticulationLink* links, + const ArticulationJointTransforms* jointTransforms, + PxReal dt, + PxU32& acCount) +{ + acCount = 0; + + FsData& fsData = *articulation.getFsDataPtr(); + const PxU16 linkCount = fsData.linkCount; + PxU32 descCount = 0; + const PxReal recipDt = 1.0f/dt; + + const PxConstraintInvMassScale ims(1.0f, 1.0f, 1.0f, 1.0f); + + for(PxU16 i=1;i(*links[i].inboundJoint); + + if(i+10 || j.tangentialDamping>0); + + const PxVec3 twistAxis = jointTransforms[i].cB2w.rotate(PxVec3(1.0f,0,0)); + const PxReal tqTwistAngle = Ps::tanHalf(twist.x, twist.w); + + const bool twistLowerLimited = j.twistLimited && tqTwistAngle < Cm::tanAdd(j.tanQTwistLow, j.tanQTwistPad); + const bool twistUpperLimited = j.twistLimited && tqTwistAngle > Cm::tanAdd(j.tanQTwistHigh, -j.tanQTwistPad); + + const PxU8 constraintCount = PxU8(swingLimited + tangentialStiffness + twistUpperLimited + twistLowerLimited); + if(!constraintCount) + continue; + + PxSolverConstraintDesc& desc = constraintDesc[descCount++]; + + desc.articulationA = &articulation; + desc.linkIndexA = Ps::to16(links[i].parent); + desc.articulationALength = Ps::to16(solverDataSize); + + desc.articulationB = &articulation; + desc.linkIndexB = i; + desc.articulationBLength = Ps::to16(solverDataSize); + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeader) + + sizeof(SolverConstraint1DExt) * constraintCount; + + PX_ASSERT(0==(constraintLength & 0x0f)); + desc.constraintLengthOver16 = Ps::to16(constraintLength/16); + + //desc.constraint = stream.reserve(constraintLength + 16u, constraintBlockManager); + desc.constraint = allocator.reserveConstraintData(constraintLength + 16u); + + desc.writeBack = NULL; + + SolverConstraint1DHeader* header = reinterpret_cast(desc.constraint); + SolverConstraint1DExt* constraints = reinterpret_cast(desc.constraint + sizeof(SolverConstraint1DHeader)); + + init(*header, constraintCount, true, ims); + + PxU32 cIndex = 0; + + if(swingLimited) + { + const PxVec3 normal = jointTransforms[i].cA2w.rotate(swingLimitAxis); + createHardLimit(fsData, links, i, constraints[cIndex++], normal, swingLimitError, recipDt); + if(tangentialStiffness) + { + const PxVec3 tangent = twistAxis.cross(normal).getNormalized(); + createTangentialSpring(fsData, links, i, constraints[cIndex++], tangent, j.tangentialStiffness, j.tangentialDamping, dt); + } + } + + if(twistUpperLimited) + createHardLimit(fsData, links, i, constraints[cIndex++], twistAxis, (j.tanQTwistHigh - tqTwistAngle)*4, recipDt); + + if(twistLowerLimited) + createHardLimit(fsData, links, i, constraints[cIndex++], -twistAxis, -(j.tanQTwistLow - tqTwistAngle)*4, recipDt); + + *(desc.constraint + getConstraintLength(desc)) = 0; + + PX_ASSERT(cIndex == constraintCount); + acCount += constraintCount; + } + + return descCount; +} + + +ArticulationPImpl::ComputeUnconstrainedVelocitiesFn ArticulationPImpl::sComputeUnconstrainedVelocities[2] = { NULL, NULL }; +ArticulationPImpl::UpdateBodiesFn ArticulationPImpl::sUpdateBodies[2] = { NULL, NULL }; +ArticulationPImpl::UpdateBodiesFn ArticulationPImpl::sUpdateBodiesTGS[2] = { NULL, NULL }; +ArticulationPImpl::SaveVelocityFn ArticulationPImpl::sSaveVelocity[2] = { NULL, NULL }; +ArticulationPImpl::SaveVelocityTGSFn ArticulationPImpl::sSaveVelocityTGS[2] = { NULL, NULL }; + +ArticulationPImpl::UpdateDeltaMotionFn ArticulationPImpl::sUpdateDeltaMotion[2] = { NULL, NULL }; +ArticulationPImpl::DeltaMotionToMotionVelFn ArticulationPImpl::sDeltaMotionToMotionVel[2] = { NULL, NULL }; +ArticulationPImpl::ComputeUnconstrainedVelocitiesTGSFn ArticulationPImpl::sComputeUnconstrainedVelocitiesTGS[2] = { NULL, NULL }; + +ArticulationPImpl::SetupInternalConstraintsTGSFn ArticulationPImpl::sSetupInternalConstraintsTGS[2] = { NULL, NULL }; + +} +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h new file mode 100644 index 000000000..0775d4066 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h @@ -0,0 +1,214 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_ARTICULATION_HELPER_H +#define DY_ARTICULATION_HELPER_H + + +#include "DyArticulation.h" + +namespace physx +{ +struct PxsBodyCore; + +class PxcConstraintBlockStream; +class PxcRigidBody; +class PxsConstraintBlockManager; +struct PxSolverConstraintDesc; + +namespace Dy +{ + struct FsInertia; + struct SolverConstraint1DExt; + struct ArticulationJointCore; + struct ArticulationSolverDesc; + struct SolverConstraint1DExtStep; + struct PxcFsScratchAllocator; + struct PxTGSSolverConstraintDesc; + + +struct ArticulationJointTransforms +{ + PxTransform cA2w; // joint parent frame in world space + PxTransform cB2w; // joint child frame in world space + PxTransform cB2cA; // joint relative pose in world space +}; + +class ArticulationHelper +{ +public: + + static void getImpulseResponse( const FsData& matrix, + PxU32 linkID, + const Cm::SpatialVectorV& impulse, + Cm::SpatialVectorV& deltaV); + + + static PX_FORCE_INLINE + void getImpulseResponse( const FsData& matrix, + PxU32 linkID, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaV) + { + getImpulseResponse(matrix, linkID, reinterpret_cast(impulse), reinterpret_cast(deltaV)); + } + + static void getImpulseSelfResponse(const FsData& matrix, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1); + + //static void flushVelocity(FsData& matrix); + + static void saveVelocity(const ArticulationSolverDesc& m, Cm::SpatialVectorF* deltaV); + + static void saveVelocityTGS(const ArticulationSolverDesc& m, const PxReal invDt); + + //static void recordDeltaMotion(const ArticulationSolverDesc& m, PxReal dt); + + //static void deltaMotionToMotionVelocity(const ArticulationSolverDesc& m, PxReal invDt); + + //static void getDataSizes(PxU32 linkCount, PxU32 &solverDataSize, PxU32& totalSize, PxU32& scratchSize); + + /*static void initializeDriveCache(Articulation& articulation, + FsData &data, + PxU16 linkCount, + const ArticulationLink* links, + PxReal compliance, + PxU32 iterations, + char* scratchMemory, + PxU32 scratchMemorySize);*/ + + //static PxU32 getDriveCacheLinkCount(const FsData& cache); + + /*static void applyImpulses(const FsData& matrix, + Cm::SpatialVectorV* Z, + Cm::SpatialVectorV* V);*/ + +//private: + static PxU32 getLtbDataSize(PxU32 linkCount); + static PxU32 getFsDataSize(PxU32 linkCount); + + //static void prepareDataBlock(ArticulationV& articualtion, + // const ArticulationLink* links, + // PxU16 linkCount, + // PxTransform* poses, + // FsInertia *baseInertia, + // ArticulationJointTransforms* jointTransforms, + // PxU32 expectedSize); + + //static void setInertia(FsInertia& inertia, + // const PxsBodyCore& body, + // const PxTransform& pose); + + //static void setJointTransforms(ArticulationJointTransforms& transforms, + // const PxTransform& parentPose, + // const PxTransform& childPose, + // const ArticulationJointCore& joint); + + /*static void prepareLtbMatrix(FsData& fsData, + const FsInertia* baseInertia, + const PxTransform* poses, + const ArticulationJointTransforms* jointTransforms, + PxReal recipDt);*/ + + //static void prepareFsData(FsData& fsData, + // const ArticulationLink* links); + + //static PX_FORCE_INLINE PxReal getResistance(PxReal compliance); + + + static void createHardLimit(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt); + + static void createTangentialSpring(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt); + + static void createHardLimitTGS(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt); + + static void createTangentialSpringTGS(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt); + + static PxU32 setupSolverConstraints(Articulation& articulation, PxU32 solverDataSize, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + const ArticulationLink* links, + const ArticulationJointTransforms* jointTransforms, + PxReal dt, + PxU32& acCount); + + /*static void computeUnconstrainedVelocitiesInternal(const ArticulationSolverDesc& desc, + PxReal dt, + const PxVec3& gravity, PxU64 contextID, + FsInertia* PX_RESTRICT baseInertia, + ArticulationJointTransforms* PX_RESTRICT jointTransforms, + PxcFsScratchAllocator& allocator);*/ + + /*static void computeJointDrives(FsData& fsData, + Ps::aos::Vec3V* drives, + const ArticulationLink* links, + const PxTransform* poses, + const ArticulationJointTransforms* transforms, + const Ps::aos::Mat33V* loads, + PxReal dt);*/ + +}; + +} + +} + +#endif //DY_ARTICULATION_HELPER_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h new file mode 100644 index 000000000..917c49682 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h @@ -0,0 +1,203 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_INTERFACE_H +#define DY_ARTICULATION_INTERFACE_H + +#include "DyArticulationUtils.h" + +namespace physx +{ + +class PxcConstraintBlockStream; +class PxcScratchAllocator; +class PxsConstraintBlockManager; +struct PxSolverConstraintDesc; + +namespace Dy +{ + + struct ArticulationSolverDesc; + + +class ArticulationPImpl +{ +public: + + typedef PxU32 (*ComputeUnconstrainedVelocitiesFn)(const ArticulationSolverDesc& desc, + PxReal dt, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, PxU64 contextID, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); + + typedef void (*UpdateBodiesFn)(const ArticulationSolverDesc& desc, + PxReal dt); + + typedef void (*SaveVelocityFn)(const ArticulationSolverDesc &m, Cm::SpatialVectorF* deltaV); + + typedef void(*SaveVelocityTGSFn)(const ArticulationSolverDesc& m, PxReal invDtF32); + + typedef PxU32(*SetupInternalConstraintsTGSFn)(const ArticulationSolverDesc& desc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* Z); + + typedef void(*ComputeUnconstrainedVelocitiesTGSFn)(const ArticulationSolverDesc& desc, + PxReal dt, + const PxVec3& gravity, PxU64 contextID, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); + + typedef void(*UpdateDeltaMotionFn)(const ArticulationSolverDesc &m, const PxReal dt, Cm::SpatialVectorF* DeltaV); + + typedef void(*DeltaMotionToMotionVelFn)(const ArticulationSolverDesc &m, const PxReal dt); + + static ComputeUnconstrainedVelocitiesFn sComputeUnconstrainedVelocities[2]; + static UpdateBodiesFn sUpdateBodies[2]; + static UpdateBodiesFn sUpdateBodiesTGS[2]; + static SaveVelocityFn sSaveVelocity[2]; + static SaveVelocityTGSFn sSaveVelocityTGS[2]; + + static UpdateDeltaMotionFn sUpdateDeltaMotion[2]; + static DeltaMotionToMotionVelFn sDeltaMotionToMotionVel[2]; + static ComputeUnconstrainedVelocitiesTGSFn sComputeUnconstrainedVelocitiesTGS[2]; + static SetupInternalConstraintsTGSFn sSetupInternalConstraintsTGS[2]; + + static PxU32 computeUnconstrainedVelocities(const ArticulationSolverDesc& desc, + PxReal dt, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + PxcScratchAllocator&, + const PxVec3& gravity, PxU64 contextID, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sComputeUnconstrainedVelocities[type]); + if (sComputeUnconstrainedVelocities[type]) + return (sComputeUnconstrainedVelocities[type])(desc, dt, allocator, constraintDesc, acCount, + gravity, contextID, Z, deltaV); + else + return 0; + } + + static void updateBodies(const ArticulationSolverDesc& desc, + PxReal dt) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sUpdateBodies[type]); + if (sUpdateBodies[type]) + (*sUpdateBodies[type])(desc, dt); + } + + static void updateBodiesTGS(const ArticulationSolverDesc& desc, + PxReal dt) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sUpdateBodiesTGS[type]); + if (sUpdateBodiesTGS[type]) + (*sUpdateBodiesTGS[type])(desc, dt); + } + + static void saveVelocity(const ArticulationSolverDesc& desc, Cm::SpatialVectorF* deltaV) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sSaveVelocity[type]); + if (sSaveVelocity[type]) + (*sSaveVelocity[type])(desc, deltaV); + } + + + static void saveVelocityTGS(const ArticulationSolverDesc& desc, PxReal invDtF32) + { + PX_UNUSED(desc); + PX_UNUSED(invDtF32); + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sSaveVelocityTGS[type]); + if (sSaveVelocityTGS[type]) + (*sSaveVelocityTGS[type])(desc, invDtF32); + } + + static void computeUnconstrainedVelocitiesTGS(const ArticulationSolverDesc& desc, + PxReal dt, + const PxVec3& gravity, PxU64 contextID, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sComputeUnconstrainedVelocitiesTGS[type]); + if (sComputeUnconstrainedVelocitiesTGS[type]) + (sComputeUnconstrainedVelocitiesTGS[type])(desc, dt, gravity, contextID, Z, DeltaV); + } + + static void updateDeltaMotion(const ArticulationSolverDesc& desc, const PxReal dt, Cm::SpatialVectorF* DeltaV) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sUpdateDeltaMotion[type]); + if (sUpdateDeltaMotion[type]) + (*sUpdateDeltaMotion[type])(desc, dt, DeltaV); + } + + static void deltaMotionToMotionVel(const ArticulationSolverDesc& desc, const PxReal invDt) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sDeltaMotionToMotionVel[type]); + if (sDeltaMotionToMotionVel[type]) + (*sDeltaMotionToMotionVel)(desc, invDt); + } + + static PxU32 setupSolverInternalConstraintsTGS(const ArticulationSolverDesc& desc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* Z) + { + PxU32 type = desc.articulation->getType(); + PX_ASSERT(sSetupInternalConstraintsTGS[type]); + if (sSetupInternalConstraintsTGS[type]) + return sSetupInternalConstraintsTGS[type](desc, stream, constraintDesc, dt, invDt, totalDt, acCount, constraintBlockManager, Z); + return 0; + + } +}; + + +} +} +#endif //DY_ARTICULATION_INTERFACE_H + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h new file mode 100644 index 000000000..cece78469 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_REFERENCE_H +#define DY_ARTICULATION_REFERENCE_H + +// a per-row struct where we put extra data for debug and setup - ultimately this will move to be just +// debug only + + + +#include "DyArticulationUtils.h" +#include "DyArticulationScalar.h" +#include "DyArticulationFnsScalar.h" +#include "DySpatial.h" + +#if DY_ARTICULATION_DEBUG_VERIFY + +namespace physx +{ + +PX_FORCE_INLINE Cm::SpatialVector propagateVelocity(const FsRow& row, + const FsJointVectors& jv, + const PxVec3& SZ, + const Cm::SpatialVector& v, + const FsRowAux& aux) +{ + typedef ArticulationFnsScalar Fns; + + Cm::SpatialVector w = Fns::translateMotion(-getParentOffset(jv), v); + PxVec3 DSZ = Fns::multiply(row.D, SZ); + + PxVec3 n = Fns::axisDot(getDSI(row), w) + DSZ; + Cm::SpatialVector result = w - Cm::SpatialVector(getJointOffset(jv).cross(n),n); +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector check = ArticulationRef::propagateVelocity(row, jv, SZ, v, aux); + PX_ASSERT((result-check).magnitude()<1e-5*PxMax(check.magnitude(), 1.0f)); +#endif + return result; +} + +PX_FORCE_INLINE Cm::SpatialVector propagateImpulse(const FsRow& row, + const FsJointVectors& jv, + PxVec3& SZ, + const Cm::SpatialVector& Z, + const FsRowAux& aux) +{ + typedef ArticulationFnsScalar Fns; + + SZ = Z.angular + Z.linear.cross(getJointOffset(jv)); + Cm::SpatialVector result = Fns::translateForce(getParentOffset(jv), Z - Fns::axisMultiply(getDSI(row), SZ)); +#if DY_ARTICULATION_DEBUG_VERIFY + PxVec3 SZcheck; + Cm::SpatialVector check = ArticulationRef::propagateImpulse(row, jv, SZcheck, Z, aux); + PX_ASSERT((result-check).magnitude()<1e-5*PxMax(check.magnitude(), 1.0f)); + PX_ASSERT((SZ-SZcheck).magnitude()<1e-5*PxMax(SZcheck.magnitude(), 1.0f)); +#endif + return result; +} + +} +#endif + +#endif //DY_ARTICULATION_REFERENCE_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp new file mode 100644 index 000000000..d5b5466c8 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp @@ -0,0 +1,317 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "DySpatial.h" +#include "DyArticulation.h" +#include "DyArticulationScalar.h" +#include "DyArticulationFnsScalar.h" +#include "DyArticulationReference.h" +#include "DyArticulationFnsSimd.h" + + +namespace physx +{ +namespace Dy +{ + +#if DY_ARTICULATION_DEBUG_VERIFY +namespace +{ + Cm::SpatialVector SpV(Vec3V linear, Vec3V angular) + { + return Cm::SpatialVector((PxVec3 &)linear, (PxVec3&)angular); + } +} +#endif + +//void PxcFsApplyImpulse(ArticulationV &articulationV, +// PxU32 linkID, +// Vec3V linear, +// Vec3V angular) +//{ +//#if DY_ARTICULATION_DEBUG_VERIFY +// { +// Cm::SpatialVectorV imp(linear, angular); +// ArticulationRef::applyImpulse(matrix, reinterpret_cast(getRefVelocity(matrix)), linkID, reinterpret_cast(imp)); +// } +//#endif +// Articulation& articulation = static_cast<>(articulationV +// FsData& matrix = *articulation.getFsDataPtr(); +// Vec3V linZ = V3Neg(linear); +// Vec3V angZ = V3Neg(angular); +// +// const FsRow *rows = getFsRows(matrix); +// const FsJointVectors* jointVectors = getJointVectors(matrix); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// const FsRowAux *aux = getAux(matrix); +//#endif +// Vec3V *deferredSZ = getDeferredSZ(matrix); +// +// for(PxU32 i = linkID; i!=0; i = matrix.parent[i]) +// { +// const FsRow &row = rows[i]; +// const FsJointVectors& jv = jointVectors[i]; +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// PxVec3 SZcheck; +// Cm::SpatialVector Zcheck = ArticulationRef::propagateImpulse(row, jv, SZcheck, SpV(linZ, angZ), aux[i]); +//#endif +// +// Vec3V SZ = V3Add(angZ, V3Cross(linZ, jv.jointOffset)); +// Vec3V lrLinear = V3Sub(linZ, V3ScaleAdd(row.DSI[0].linear, V3GetX(SZ), +// V3ScaleAdd(row.DSI[1].linear, V3GetY(SZ), +// V3Scale(row.DSI[2].linear, V3GetZ(SZ))))); +// +// Vec3V lrAngular = V3Sub(angZ, V3ScaleAdd(row.DSI[0].angular, V3GetX(SZ), +// V3ScaleAdd(row.DSI[1].angular, V3GetY(SZ), +// V3Scale(row.DSI[2].angular, V3GetZ(SZ))))); +// +// linZ = lrLinear; +// angZ = V3Add(lrAngular, V3Cross(jv.parentOffset, lrLinear)); +// deferredSZ[i] = V3Add(deferredSZ[i], SZ); +// +// PX_ASSERT(Ps::aos::isFiniteVec3V(linZ)); +// PX_ASSERT(Ps::aos::isFiniteVec3V(angZ)); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector Z = SpV(linZ,angZ); +// PX_ASSERT((Z - Zcheck).magnitude()<1e-4*PxMax(Zcheck.magnitude(), 1.0f)); +// PX_ASSERT(((PxVec3&)SZ-SZcheck).magnitude()<1e-4*PxMax(SZcheck.magnitude(), 1.0f)); +//#endif +// } +// +// matrix.deferredZ.linear = V3Add(matrix.deferredZ.linear, linZ); +// matrix.deferredZ.angular = V3Add(matrix.deferredZ.angular, angZ); +// +// matrix.dirty |= rows[linkID].pathToRoot; +//} + +//Cm::SpatialVectorV PxcFsGetVelocity(Articulation &articulation, +// PxU32 linkID) +//{ +// FsData& matrix = *articulation.getFsDataPtr(); +// const FsRow *rows = getFsRows(matrix); +// const FsJointVectors* jointVectors = getJointVectors(matrix); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// const FsRowAux *aux = getAux(matrix); +//#endif +// Cm::SpatialVectorV* PX_RESTRICT V = getVelocity(matrix); +// +// // find the dirty node on the path (including the root) with the lowest index +// ArticulationBitField toUpdate = rows[linkID].pathToRoot & matrix.dirty; +// +// +// if(toUpdate) +// { +// // store the dV elements densely and use an array map to decode - hopefully cache friendlier +// PxU32 indexToStackLoc[DY_ARTICULATION_MAX_SIZE], count = 0; +// Cm::SpatialVectorV dVStack[DY_ARTICULATION_MAX_SIZE]; +// +// ArticulationBitField ignoreNodes = (toUpdate & (0-toUpdate))-1; +// ArticulationBitField path = rows[linkID].pathToRoot & ~ignoreNodes, p = path; +// ArticulationBitField newDirty = 0; +// +// Vec3V ldV = V3Zero(), adV = V3Zero(); +// Cm::SpatialVectorV* PX_RESTRICT defV = getDeferredVel(matrix); +// Vec3V* PX_RESTRICT SZ = getDeferredSZ(matrix); +// +// if(p & 1) +// { +// const FsInertia &m = getRootInverseInertia(matrix); +// Vec3V lZ = V3Neg(matrix.deferredZ.linear); +// Vec3V aZ = V3Neg(matrix.deferredZ.angular); +// +// ldV = V3Add(M33MulV3(m.ll,lZ), M33MulV3(m.la,aZ)); +// adV = V3Add(M33TrnspsMulV3(m.la,lZ), M33MulV3(m.aa,aZ)); +// +// V[0].linear = V3Add(V[0].linear, ldV); +// V[0].angular = V3Add(V[0].angular, adV); +// +// matrix.deferredZ.linear = V3Zero(); +// matrix.deferredZ.angular = V3Zero(); +// +// indexToStackLoc[0] = count; +// Cm::SpatialVectorV &e = dVStack[count++]; +// +// e.linear = ldV; +// e.angular = adV; +// +// newDirty = rows[0].children; +// p--; +// } +// +// +// while(p) // using "for(;p;p &= (p-1))" here generates LHSs from the ArticulationLowestSetBit +// { +// PxU32 i = ArticulationLowestSetBit(p); +// const FsJointVectors& jv = jointVectors[i]; +// +// p &= (p-1); +// +// const FsRow* PX_RESTRICT row = rows + i; +// +// ldV = V3Add(ldV, defV[i].linear); +// adV = V3Add(adV, defV[i].angular); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector dVcheck = ArticulationRef::propagateVelocity(*row, jv, (PxVec3&)SZ[i], SpV(ldV,adV), aux[i]); +//#endif +// +// Vec3V DSZ = M33MulV3(row->D, SZ[i]); +// +// Vec3V lW = V3Add(ldV, V3Cross(adV,jv.parentOffset)); +// Vec3V aW = adV; +// +// const Cm::SpatialVectorV*PX_RESTRICT DSI = row->DSI; +// Vec3V lN = V3Merge(V3Dot(DSI[0].linear, lW), V3Dot(DSI[1].linear, lW), V3Dot(DSI[2].linear, lW)); +// Vec3V aN = V3Merge(V3Dot(DSI[0].angular, aW), V3Dot(DSI[1].angular, aW), V3Dot(DSI[2].angular, aW)); +// +// Vec3V n = V3Add(V3Add(lN, aN), DSZ); +// +// ldV = V3Sub(lW, V3Cross(jv.jointOffset,n)); +// adV = V3Sub(aW, n); +// +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector dV = SpV(ldV,adV); +// PX_ASSERT((dV-dVcheck).magnitude()<1e-4*PxMax(dVcheck.magnitude(), 1.0f)); +//#endif +// +// V[i].linear = V3Add(V[i].linear, ldV); +// V[i].angular = V3Add(V[i].angular, adV); +// +// defV[i].linear = V3Zero(); +// defV[i].angular = V3Zero(); +// SZ[i] = V3Zero(); +// +// indexToStackLoc[i] = count; +// Cm::SpatialVectorV &e = dVStack[count++]; +// newDirty |= rows[i].children; +// +// e.linear = ldV; +// e.angular = adV; +// } +// +// for(ArticulationBitField defer = newDirty&~path; defer; defer &= (defer-1)) +// { +// PxU32 i = ArticulationLowestSetBit(defer); +// PxU32 parent = indexToStackLoc[matrix.parent[i]]; +// +// defV[i].linear = V3Add(defV[i].linear, dVStack[parent].linear); +// defV[i].angular = V3Add(defV[i].angular, dVStack[parent].angular); +// } +// +// matrix.dirty = (matrix.dirty | newDirty)&~path; +// } +//#if DY_ARTICULATION_DEBUG_VERIFY +// Cm::SpatialVector v = reinterpret_cast(V[linkID]); +// Cm::SpatialVector rv = reinterpret_cast(getRefVelocity(matrix)[linkID]); +// PX_ASSERT((v-rv).magnitude()<1e-4f * PxMax(rv.magnitude(),1.0f)); +//#endif +// +// return V[linkID]; +//} + +Cm::SpatialVectorV PxcFsGetMotionVector(ArticulationV& articulation, + PxU32 linkID) +{ + + /*const Cm::SpatialVectorV* PX_RESTRICT V = articulation.getMotionVector(); + return V[linkID];*/ + + return articulation.getLinkMotionVector(linkID); +} + +PX_FORCE_INLINE Cm::SpatialVectorV propagateVelocitySIMD(const FsRow& row, + const FsJointVectors& jv, + const Vec3V& SZ, + const Cm::SpatialVectorV& v, + const FsRowAux& aux) +{ + PX_UNUSED(aux); + + typedef ArticulationFnsSimd Fns; + + Cm::SpatialVectorV w(V3Add(v.linear, V3Cross(v.angular, jv.parentOffset)), v.angular); + Vec3V DSZ = M33MulV3(row.D, SZ); + + Vec3V n = V3Add(Fns::axisDot(row.DSI, w), DSZ); + Cm::SpatialVectorV result = w - Cm::SpatialVectorV(V3Cross(jv.jointOffset,n), n); + +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector check = ArticulationRef::propagateVelocity(row, jv, reinterpret_cast(SZ), reinterpret_cast(v), aux); + PX_ASSERT((reinterpret_cast(result)-check).magnitude()<1e-4*PxMax(check.magnitude(), 1.0f)); +#endif + + return result; +} + +void PxcFsFlushVelocity(FsData& matrix) +{ + typedef ArticulationFnsSimd Fns; + + const FsRow* PX_RESTRICT rows = getFsRows(matrix); + const FsRowAux* PX_RESTRICT aux = getAux(matrix); + const FsJointVectors*PX_RESTRICT jointVectors = getJointVectors(matrix); + + Cm::SpatialVectorV V0 = Fns::multiply(getRootInverseInertia(matrix), -matrix.deferredZ); + matrix.deferredZ = Cm::SpatialVectorV(PxZero); + + getVelocity(matrix)[0] += V0; + for(ArticulationBitField defer = rows[0].children; defer; defer &= (defer-1)) + getDeferredVel(matrix)[ArticulationLowestSetBit(defer)] += V0; + + for(PxU32 i = 1; i(getRefVelocity(matrix)[i]); + Cm::SpatialVector diff = v-rv; + PxReal m = rv.magnitude(); + PX_UNUSED(m); + PX_ASSERT(diff.magnitude()<1e-4*PxMax(1.0f,m)); + } +#endif + + matrix.dirty = 0; +} +} +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp new file mode 100644 index 000000000..5889bb330 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp @@ -0,0 +1,575 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "DyArticulationUtils.h" +#include "DyArticulationScalar.h" +#include "DyArticulationReference.h" +#include "DyArticulationFnsDebug.h" + +namespace physx +{ +namespace Dy +{ +namespace ArticulationRef +{ + Cm::SpatialVector propagateImpulse(const FsRow& row, + const FsJointVectors& j, + PxVec3& SZ, + const Cm::SpatialVector& Z, + const FsRowAux& aux) + { + typedef ArticulationFnsScalar Fns; + + SZ = Fns::axisDot(reinterpret_cast(aux.S), Z); + return Fns::translateForce(getParentOffset(j), Z - Fns::axisMultiply(getDSI(row), SZ)); + } + + Cm::SpatialVector propagateVelocity(const FsRow& row, + const FsJointVectors& j, + const PxVec3& SZ, + const Cm::SpatialVector& v, + const FsRowAux& aux) + { + typedef ArticulationFnsScalar Fns; + + Cm::SpatialVector w = Fns::translateMotion(-getParentOffset(j), v); + PxVec3 DSZ = Fns::multiply(row.D, SZ); + + return w - Fns::axisMultiply(reinterpret_cast(aux.S), DSZ + Fns::axisDot(getDSI(row), w)); + } + + void applyImpulse(const FsData& matrix, + Cm::SpatialVector* velocity, + PxU32 linkID, + const Cm::SpatialVector& impulse) + { + typedef ArticulationFnsScalar Fns; + + PX_ASSERT(matrix.linkCount<=DY_ARTICULATION_MAX_SIZE); + + const FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + Cm::SpatialVector dV[DY_ARTICULATION_MAX_SIZE]; + PxVec3 SZ[DY_ARTICULATION_MAX_SIZE]; + + for(PxU32 i=0;i0;) + { + LtbRow& b = rows[i]; + inertia[i] = Fns::invertInertia(inertia[i]); + PxU32 p = m.parent[i]; + + Cm::SpatialVector* j0 = &reinterpret_cast(*b.j0), + * j1 = &reinterpret_cast(*b.j1); + + Fns::multiply(j, inertia[i], j1); + PxMat33 jResponse = Fns::invertSym33(-Fns::multiplySym(j, j1)); + j1[0] = j[0]; j1[1] = j[1]; j1[2] = j[2]; + + b.jResponse = Mat33V_From_PxMat33(jResponse); + Fns::multiply(j, j0, jResponse); + inertia[p] = Fns::multiplySubtract(inertia[p], j, j0); + j0[0] = j[0]; j0[1] = j[1]; j0[2] = j[2]; + } + + rows[0].inertia = Fns::invertInertia(inertia[0]); + for(PxU32 i=1;i(c); + const LtbRow* rows = getLtbRows(m); + PxMemZero(y, m.linkCount*sizeof(Cm::SpatialVector)); + + for(PxU32 i=m.linkCount;i-->1;) + { + PxU32 p = m.parent[i]; + const LtbRow& r = rows[i]; + b[i] -= PxVec4(Fns::axisDot(&static_cast(*r.j1), y[i]),0); + y[p] -= Fns::axisMultiply(&static_cast(*r.j0), b[i].getXYZ()); + } + + y[0] = Fns::multiply(rows[0].inertia,y[0]); + + for(PxU32 i=1; i(*r.j0), y[p]); + y[i] = Fns::multiply(r.inertia, y[i]) - Fns::axisMultiply(&static_cast(*r.j1), t); + } +} + +void PxcFsPropagateDrivenInertiaScalar(FsData& matrix, + const FsInertia* baseInertia, + const PxReal* isf, + const Mat33V* load) +{ + typedef ArticulationFnsScalar Fns; + + Cm::SpatialVector IS[3], DSI[3]; + PxMat33 D; + + FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + SpInertia inertia[DY_ARTICULATION_MAX_SIZE]; + for(PxU32 i=0;i0;) + { + FsRow& r = rows[i]; + const FsRowAux& a = aux[i]; + const FsJointVectors& jv = jointVectors[i]; + + Fns::multiply(IS, inertia[i], &static_cast(*a.S)); + + PX_ALIGN(16, PxMat33) L; + PxMat33_From_Mat33V(load[i], L); + D = Fns::invertSym33(Fns::multiplySym(&static_cast(*a.S), IS) + L*isf[i]); + + Fns::multiply(DSI, IS, D); + + r.D = Mat33V_From_PxMat33(D); + static_cast(r.DSI[0]) = DSI[0]; + static_cast(r.DSI[1]) = DSI[1]; + static_cast(r.DSI[2]) = DSI[2]; + + inertia[matrix.parent[i]] += Fns::translate(getParentOffset(jv), Fns::multiplySubtract(inertia[i], DSI, IS)); + } + + FsInertia& m = getRootInverseInertia(matrix); + m = FsInertia(Fns::invertInertia(inertia[0])); +} + +// no need to compile this ecxcept for verification, and it consumes huge amounts of stack space +void PxcFsComputeJointLoadsScalar(const FsData& matrix, + const FsInertia*PX_RESTRICT baseInertia, + Mat33V*PX_RESTRICT load, + const PxReal*PX_RESTRICT isf, + PxU32 linkCount, + PxU32 maxIterations) +{ + typedef ArticulationFnsScalar Fns; + + // the childward S + SpInertia leafwardInertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia rootwardInertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia inertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia contribToParent[DY_ARTICULATION_MAX_SIZE]; + + // total articulated inertia assuming the articulation is rooted here + + const FsRow* row = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_UNUSED(row); + + PxMat33 load_[DY_ARTICULATION_MAX_SIZE]; + + for(PxU32 iter=0;iter1;) + { + const FsJointVectors& j = jointVectors[i]; + + leafwardInertia[i] = inertia[i]; + contribToParent[i] = Fns::propagate(inertia[i], &static_cast(*aux[i].S), load_[i], isf[i]); + inertia[matrix.parent[i]] += Fns::translate((PxVec3&)j.parentOffset, contribToParent[i]); + } + + for(PxU32 i=1;i(*aux[i].S), load_[i], isf[i]); + } + + for(PxU32 i=1;i(*aux[i].S)); + PX_ASSERT(load_[i][0].isFinite() && load_[i][1].isFinite() && load_[2][i].isFinite()); + } + } + for(PxU32 i=1;i(state.deferredZ) += Z; + state.dirty |= matrix.row[linkID].pathToRoot; +} + +Cm::SpatialVector PxcFsGetVelocity(const FsData& matrix, + PxU32 linkID) +{ + // find the dirty node on the path (including the root) with the lowest index + ArticulationBitField toUpdate = matrix.row[linkID].pathToRoot & state.dirty; + + if(toUpdate) + { + ArticulationBitField ignoreNodes = (toUpdate & (0-toUpdate))-1; + ArticulationBitField path = matrix.row[linkID].pathToRoot & ~ignoreNodes, p = path; + ArticulationBitField newDirty = 0; + + Cm::SpatialVector dV = Cm::SpatialVector::zero(); + if(p & 1) + { + dV = getRootDeltaV(matrix, -deferredZ(state)); + + velocityRef(state, 0) += dV; + for(ArticulationBitField defer = matrix.row[0].children & ~path; defer; defer &= (defer-1)) + deferredVelRef(state, ArticulationLowestSetBit(defer)) += dV; + + deferredZRef(state) = Cm::SpatialVector::zero(); + newDirty = matrix.row[0].children; + p--; + } + + for(; p; p &= (p-1)) + { + PxU32 i = ArticulationLowestSetBit(p); + + dV = propagateVelocity(matrix.row[i], deferredSZ(state,i), dV + state.deferredVel[i], matrix.aux[i]); + + velocityRef(state,i) += dV; + for(ArticulationBitField defer = matrix.row[i].children & ~path; defer; defer &= (defer-1)) + deferredVelRef(state,ArticulationLowestSetBit(defer)) += dV; + + newDirty |= matrix.row[i].children; + deferredVelRef(state,i) = Cm::SpatialVector::zero(); + deferredSZRef(state,i) = PxVec3(0); + } + + state.dirty = (state.dirty | newDirty)&~path; + } +#if DY_ARTICULATION_DEBUG_VERIFY + Cm::SpatialVector v = state.velocity[linkID]; + Cm::SpatialVector rv = state.refVelocity[linkID]; + PX_ASSERT((v-rv).magnitude()<1e-4f * rv.magnitude()); +#endif + + return state.velocity[linkID]; +} + +void PxcFsFlushVelocity(const FsData& matrix) +{ + Cm::SpatialVector V = getRootDeltaV(matrix, -deferredZ(state)); + deferredZRef(state) = Cm::SpatialVector::zero(); + velocityRef(state,0) += V; + for(ArticulationBitField defer = matrix.row[0].children; defer; defer &= (defer-1)) + deferredVelRef(state,ArticulationLowestSetBit(defer)) += V; + + for(PxU32 i = 1; i Fns; + + Cm::SpatialVectorV IS[3]; + PxMat33 D; + + FsRow* rows = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + FsInertia *inertia = allocator.alloc(matrix.linkCount); + PxMemCopy(inertia, baseInertia, matrix.linkCount*sizeof(FsInertia)); + + for(PxU32 i=matrix.linkCount; --i>0;) + { + FsRow& r = rows[i]; + const FsRowAux& a = aux[i]; + const FsJointVectors& jv = jointVectors[i]; + + Mat33V m = Fns::computeSIS(inertia[i], a.S, IS); + FloatV f = FLoad(isf[i]); + + Mat33V D = Fns::invertSym33(Mat33V(V3ScaleAdd(load[i].col0, f, m.col0), + V3ScaleAdd(load[i].col1, f, m.col1), + V3ScaleAdd(load[i].col2, f, m.col2))); + r.D = D; + + inertia[matrix.parent[i]] = Fns::addInertia(inertia[matrix.parent[i]], + Fns::translateInertia(jv.parentOffset, Fns::multiplySubtract(inertia[i], D, IS, r.DSI))); + } + + getRootInverseInertia(matrix) = Fns::invertInertia(inertia[0]); +} + +void PxcLtbSolve(const FsData& m, + Vec3V* c, // rhs error to solve for + Cm::SpatialVector* y) // velocity delta output +{ + typedef ArticulationFnsScalar Fns; + + PxVec4* b = reinterpret_cast(c); + const LtbRow* rows = getLtbRows(m); + PxMemZero(y, m.linkCount*sizeof(Cm::SpatialVector)); + + for(PxU32 i=m.linkCount;i-->1;) + { + PxU32 p = m.parent[i]; + const LtbRow& r = rows[i]; + b[i] -= PxVec4(Fns::axisDot(&static_cast(*r.j1), y[i]),0); + y[p] -= Fns::axisMultiply(&static_cast(*r.j0), b[i].getXYZ()); + } + + y[0] = Fns::multiply(rows[0].inertia,y[0]); + + for(PxU32 i=1; i(*r.j0), y[p]); + y[i] = Fns::multiply(r.inertia, y[i]) - Fns::axisMultiply(&static_cast(*r.j1), t); + } +} + + +#endif + + +#if DY_ARTICULATION_DEBUG_VERIFY +void PxcLtbFactorScalar(FsData& m) +{ + typedef ArticulationFnsScalar Fns; + LtbRow* rows = getLtbRows(m); + + SpInertia inertia[DY_ARTICULATION_MAX_SIZE]; + for(PxU32 i=0;i0;) + { + LtbRow& b = rows[i]; + inertia[i] = Fns::invertInertia(inertia[i]); + PxU32 p = m.parent[i]; + + Cm::SpatialVector* j0 = &reinterpret_cast(*b.j0), + * j1 = &reinterpret_cast(*b.j1); + + Fns::multiply(j, inertia[i], j1); + PxMat33 jResponse = Fns::invertSym33(-Fns::multiplySym(j, j1)); + j1[0] = j[0]; j1[1] = j[1]; j1[2] = j[2]; + + b.jResponse = Mat33V_From_PxMat33(jResponse); + Fns::multiply(j, j0, jResponse); + inertia[p] = Fns::multiplySubtract(inertia[p], j, j0); + j0[0] = j[0]; j0[1] = j[1]; j0[2] = j[2]; + } + + rows[0].inertia = Fns::invertInertia(inertia[0]); + for(PxU32 i=1;i0;) + { + FsRow& r = rows[i]; + const FsRowAux& a = aux[i]; + const FsJointVectors& jv = jointVectors[i]; + + Fns::multiply(IS, inertia[i], &reinterpret_cast(*a.S)); + + PX_ALIGN(16, PxMat33) L; + PxMat33_From_Mat33V(load[i], L); + D = Fns::invertSym33(Fns::multiplySym(&reinterpret_cast(*a.S), IS) + L*isf[i]); + + Fns::multiply(DSI, IS, D); + + r.D = Mat33V_From_PxMat33(D); + reinterpret_cast(r.DSI[0]) = DSI[0]; + reinterpret_cast(r.DSI[1]) = DSI[1]; + reinterpret_cast(r.DSI[2]) = DSI[2]; + + inertia[matrix.parent[i]] += Fns::translate(getParentOffset(jv), Fns::multiplySubtract(inertia[i], DSI, IS)); + } + + FsInertia& m = getRootInverseInertia(matrix); + m = FsInertia(Fns::invertInertia(inertia[0])); +} + +// no need to compile this ecxcept for verification, and it consumes huge amounts of stack space +void PxcFsComputeJointLoadsScalar(const FsData& matrix, + const FsInertia*PX_RESTRICT baseInertia, + Mat33V*PX_RESTRICT load, + const PxReal*PX_RESTRICT isf, + PxU32 linkCount, + PxU32 maxIterations) +{ + typedef ArticulationFnsScalar Fns; + + // the childward S + SpInertia leafwardInertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia rootwardInertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia inertia[DY_ARTICULATION_MAX_SIZE]; + SpInertia contribToParent[DY_ARTICULATION_MAX_SIZE]; + + // total articulated inertia assuming the articulation is rooted here + + const FsRow* row = getFsRows(matrix); + const FsRowAux* aux = getAux(matrix); + const FsJointVectors* jointVectors = getJointVectors(matrix); + + PX_UNUSED(row); + + PxMat33 load_[DY_ARTICULATION_MAX_SIZE]; + + for(PxU32 iter=0;iter1;) + { + const FsJointVectors& j = jointVectors[i]; + + leafwardInertia[i] = inertia[i]; + contribToParent[i] = Fns::propagate(inertia[i], &reinterpret_cast(*aux[i].S), load_[i], isf[i]); + inertia[matrix.parent[i]] += Fns::translate((PxVec3&)j.parentOffset, contribToParent[i]); + } + + for(PxU32 i=1;i(*aux[i].S), load_[i], isf[i]); + } + + for(PxU32 i=1;i(*aux[i].S)); + PX_ASSERT(load_[i][0].isFinite() && load_[i][1].isFinite() && load_[2][i].isFinite()); + } + } + for(PxU32 i=1;i(getVelocity(m)[i]); +} + +PX_FORCE_INLINE Cm::SpatialVector& deferredVelRef(FsData &m, PxU32 i) +{ + return reinterpret_cast(getDeferredVel(m)[i]); +} + +PX_FORCE_INLINE PxVec3& deferredSZRef(FsData &m, PxU32 i) +{ + return reinterpret_cast(getDeferredSZ(m)[i]); +} + +PX_FORCE_INLINE const PxVec3& deferredSZ(const FsData &s, PxU32 i) +{ + return reinterpret_cast(getDeferredSZ(s)[i]); +} + +PX_FORCE_INLINE Cm::SpatialVector& deferredZRef(FsData &s) +{ + return unsimdRef(s.deferredZ); +} + + +PX_FORCE_INLINE const Cm::SpatialVector& deferredZ(const FsData &s) +{ + return unsimdRef(s.deferredZ); +} + +PX_FORCE_INLINE const PxVec3& getJointOffset(const FsJointVectors& j) +{ + return reinterpret_cast(j.jointOffset); +} + +PX_FORCE_INLINE const PxVec3& getParentOffset(const FsJointVectors& j) +{ + return reinterpret_cast(j.parentOffset); +} + + + + +PX_FORCE_INLINE const Cm::SpatialVector* getDSI(const FsRow& row) +{ + return PxUnionCast(row.DSI); //reinterpret_cast(row.DSI); +} + +} + +} + +#endif //DY_ARTICULATION_SCALAR_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h new file mode 100644 index 000000000..7ac2ee46c --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h @@ -0,0 +1,336 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_ARTICULATION_H +#define DY_ARTICULATION_H + +#include "PsVecMath.h" +#include "CmSpatialVector.h" +#include "DySpatial.h" +#include "PsBitUtils.h" +#include "DyArticulation.h" +#include "DyArticulationHelper.h" + +namespace physx +{ + +namespace Dy +{ + struct ArticulationCore; + struct ArticulationLink; + typedef size_t ArticulationLinkHandle; + class Articulation; + +#define DY_ARTICULATION_DEBUG_VERIFY 0 + +PX_FORCE_INLINE PxU32 ArticulationLowestSetBit(ArticulationBitField val) +{ + PxU32 low = PxU32(val&0xffffffff), high = PxU32(val>>32); + PxU32 mask = PxU32((!low)-1); + PxU32 result = (mask&Ps::lowestSetBitUnsafe(low)) | ((~mask)&(Ps::lowestSetBitUnsafe(high)+32)); + PX_ASSERT(val & (PxU64(1)<> 32); + PxU32 mask = PxU32((!high) - 1); + PxU32 result = ((~mask)&Ps::highestSetBitUnsafe(low)) | ((mask)&(Ps::highestSetBitUnsafe(high) + 32)); + PX_ASSERT(val & (PxU64(1) << result)); + return result; +} + +using namespace Ps::aos; + + + +PX_FORCE_INLINE Cm::SpatialVector& unsimdRef(Cm::SpatialVectorV& v) { return reinterpret_cast(v); } +PX_FORCE_INLINE const Cm::SpatialVector& unsimdRef(const Cm::SpatialVectorV& v) { return reinterpret_cast(v); } + + +PX_ALIGN_PREFIX(16) +struct FsJointVectors +{ + Vec3V parentOffset; // 16 bytes world-space offset from parent to child + Vec3V jointOffset; // 16 bytes world-space offset from child to joint +} +PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct FsRow +{ + Cm::SpatialVectorV DSI[3]; // 96 bytes + Mat33V D; // 48 bytes + ArticulationBitField children; // 8 bytes bitmap of children + ArticulationBitField pathToRoot; // 8 bytes bitmap of nodes to root, including self and root +} +PX_ALIGN_SUFFIX(16); + +PX_COMPILE_TIME_ASSERT(sizeof(FsRow)==160); + + + +PX_ALIGN_PREFIX(16) +struct FsInertia +{ + Mat33V ll, la, aa; + PX_FORCE_INLINE FsInertia(const Mat33V& _ll, const Mat33V& _la, const Mat33V& _aa): ll(_ll), la(_la), aa(_aa) {} + PX_FORCE_INLINE FsInertia(const SpInertia& I) + : ll(Mat33V_From_PxMat33(I.mLL)), la(Mat33V_From_PxMat33(I.mLA)), aa(Mat33V_From_PxMat33(I.mAA)) {} + PX_FORCE_INLINE FsInertia() {} + + PX_FORCE_INLINE void operator=(const FsInertia& other) + { + ll.col0 = other.ll.col0; ll.col1 = other.ll.col1; ll.col2 = other.ll.col2; + la.col0 = other.la.col0; la.col1 = other.la.col1; la.col2 = other.la.col2; + aa.col0 = other.aa.col0; aa.col1 = other.aa.col1; aa.col2 = other.aa.col2; + } + + PX_FORCE_INLINE FsInertia(const FsInertia& other) + { + ll.col0 = other.ll.col0; ll.col1 = other.ll.col1; ll.col2 = other.ll.col2; + la.col0 = other.la.col0; la.col1 = other.la.col1; la.col2 = other.la.col2; + aa.col0 = other.aa.col0; aa.col1 = other.aa.col1; aa.col2 = other.aa.col2; + } + +}PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct LtbRow +{ + FsInertia inertia; // body inertia in world space + Cm::SpatialVectorV j0[3], j1[3]; // jacobians + Mat33V jResponse; // inverse response matrix of joint + Vec3V jC; +} PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct FsRowAux +{ + Cm::SpatialVectorV S[3]; // motion subspace +}PX_ALIGN_SUFFIX(16); + + +struct FsData +{ + //Articulation* articulationX; //4 + +#if !PX_P64_FAMILY + PxU32 pad0; //8 +#endif + PxU16 linkCount; // number of links //10 + PxU16 jointVectorOffset; // offset of read-only data //12 +// PxU16 maxSolverNormalProgress; //14 +// PxU16 maxSolverFrictionProgress; //16 + + PxU64 dirty; //24 + PxU16 ltbDataOffset; // offset of save-velocity data //26 + PxU16 fsDataOffset; // offset of joint references //28 + //PxU32 solverProgress; //32 + + + Cm::SpatialVectorV deferredZ; //64 + PxU8 parent[DY_ARTICULATION_MAX_SIZE]; //128 +}; + +PX_COMPILE_TIME_ASSERT(0 == (sizeof(FsData) & 0x0f)); + +#define SOLVER_BODY_SOLVER_PROGRESS_OFFSET 28 +#define SOLVER_BODY_MAX_SOLVER_PROGRESS_OFFSET 12 + +namespace +{ + template PX_FORCE_INLINE T addAddr(void* addr, PxU32 increment) + { + return reinterpret_cast(reinterpret_cast(addr)+increment); + } + + template PX_FORCE_INLINE T addAddr(const void* addr, PxU32 increment) + { + return reinterpret_cast(reinterpret_cast(addr)+increment); + } +} + +PX_FORCE_INLINE Cm::SpatialVectorV* getVelocity(FsData& matrix) +{ + return addAddr(&matrix, sizeof(FsData)); +} + +PX_FORCE_INLINE const Cm::SpatialVectorV* getVelocity(const FsData& matrix) +{ + return addAddr(&matrix, sizeof(FsData)); +} + +PX_FORCE_INLINE Cm::SpatialVectorV* getDeferredVel(FsData& matrix) +{ + return addAddr(getVelocity(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE const Cm::SpatialVectorV* getDeferredVel(const FsData& matrix) +{ + return addAddr(getVelocity(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE const Cm::SpatialVectorV* getMotionVector(const FsData& matrix) +{ + return addAddr(getDeferredVel(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE Cm::SpatialVectorV* getMotionVector(FsData& matrix) +{ + return addAddr(getDeferredVel(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE Vec3V* getDeferredSZ(FsData& matrix) +{ + return addAddr(getMotionVector(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE const Vec3V* getDeferredSZ(const FsData& matrix) +{ + return addAddr(getMotionVector(matrix), sizeof(Cm::SpatialVectorV) * matrix.linkCount); +} + +PX_FORCE_INLINE const PxReal* getMaxPenBias(const FsData& matrix) +{ + return addAddr(getDeferredSZ(matrix), sizeof(Vec3V) * matrix.linkCount); +} + +PX_FORCE_INLINE PxReal* getMaxPenBias(FsData& matrix) +{ + return addAddr(getDeferredSZ(matrix), sizeof(Vec3V) * matrix.linkCount); +} + + +PX_FORCE_INLINE FsJointVectors* getJointVectors(FsData& matrix) +{ + return addAddr(&matrix,matrix.jointVectorOffset); +} + +PX_FORCE_INLINE const FsJointVectors* getJointVectors(const FsData& matrix) +{ + return addAddr(&matrix,matrix.jointVectorOffset); +} + +PX_FORCE_INLINE FsInertia& getRootInverseInertia(FsData& matrix) +{ + return *addAddr(&matrix,matrix.fsDataOffset); +} + +PX_FORCE_INLINE const FsInertia& getRootInverseInertia(const FsData& matrix) +{ + return *addAddr(&matrix,matrix.fsDataOffset); + +} + +PX_FORCE_INLINE FsRow* getFsRows(FsData& matrix) +{ + return addAddr(&getRootInverseInertia(matrix),sizeof(FsInertia)); +} + +PX_FORCE_INLINE const FsRow* getFsRows(const FsData& matrix) +{ + return addAddr(&getRootInverseInertia(matrix),sizeof(FsInertia)); +} + + +PX_FORCE_INLINE LtbRow* getLtbRows(FsData& matrix) +{ + return addAddr(&matrix,matrix.ltbDataOffset); +} + +PX_FORCE_INLINE const LtbRow* getLtbRows(const FsData& matrix) +{ + return addAddr(&matrix,matrix.ltbDataOffset); +} + + +PX_FORCE_INLINE Cm::SpatialVectorV* getRefVelocity(FsData& matrix) +{ + return addAddr(getLtbRows(matrix), sizeof(LtbRow)*matrix.linkCount); +} + +PX_FORCE_INLINE const Cm::SpatialVectorV* getRefVelocity(const FsData& matrix) +{ + return addAddr(getLtbRows(matrix), sizeof(LtbRow)*matrix.linkCount); +} + +PX_FORCE_INLINE FsRowAux* getAux(FsData& matrix) +{ + return addAddr(getRefVelocity(matrix),sizeof(Cm::SpatialVectorV)*matrix.linkCount); +} + +PX_FORCE_INLINE const FsRowAux* getAux(const FsData& matrix) +{ + return addAddr(getRefVelocity(matrix),sizeof(Cm::SpatialVectorV)*matrix.linkCount); +} + +//void PxcFsApplyImpulse(ArticulationV& articulation, +// PxU32 linkID, +// Vec3V linear, +// Vec3V angular); + +//Cm::SpatialVectorV PxcFsGetVelocity(ArticulationV& articulation, +// PxU32 linkID); + +Cm::SpatialVectorV PxcFsGetMotionVector(ArticulationV& articulation, + PxU32 linkID); + + +#if DY_ARTICULATION_DEBUG_VERIFY +namespace ArticulationRef +{ + Cm::SpatialVector propagateVelocity(const FsRow& row, + const FsJointVectors& jv, + const PxVec3& SZ, + const Cm::SpatialVector& v, + const FsRowAux& aux); + + Cm::SpatialVector propagateImpulse(const FsRow& row, + const FsJointVectors& jv, + PxVec3& SZ, + const Cm::SpatialVector& Z, + const FsRowAux& aux); + + void applyImpulse(const FsData& matrix, + Cm::SpatialVector* velocity, + PxU32 linkID, + const Cm::SpatialVector& impulse); + +} +#endif + +} +} + +#endif //DY_ARTICULATION_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h b/src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h new file mode 100644 index 000000000..e8cf685e4 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h @@ -0,0 +1,409 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_BODYCORE_INTEGRATOR_H +#define DY_BODYCORE_INTEGRATOR_H + +#include "CmPhysXCommon.h" +#include "PxvDynamics.h" +#include "PsMathUtils.h" +#include "PxsRigidBody.h" +#include "DySolverBody.h" +#include "DySleepingConfigulation.h" +#include "PxsIslandSim.h" + +namespace physx +{ + +namespace Dy +{ + +PX_FORCE_INLINE void bodyCoreComputeUnconstrainedVelocity +(const PxVec3& gravity, const PxReal dt, const PxReal linearDamping, const PxReal angularDamping, const PxReal accelScale, +const PxReal maxLinearVelocitySq, const PxReal maxAngularVelocitySq, PxVec3& inOutLinearVelocity, PxVec3& inOutAngularVelocity, +bool disableGravity) +{ + + //Multiply everything that needs multiplied by dt to improve code generation. + + PxVec3 linearVelocity = inOutLinearVelocity; + PxVec3 angularVelocity = inOutAngularVelocity; + + const PxReal linearDampingTimesDT=linearDamping*dt; + const PxReal angularDampingTimesDT=angularDamping*dt; + const PxReal oneMinusLinearDampingTimesDT=1.0f-linearDampingTimesDT; + const PxReal oneMinusAngularDampingTimesDT=1.0f-angularDampingTimesDT; + + //TODO context-global gravity + if (!disableGravity) + { + const PxVec3 linearAccelTimesDT = gravity*dt *accelScale; + linearVelocity += linearAccelTimesDT; + } + + //Apply damping. + const PxReal linVelMultiplier = physx::intrinsics::fsel(oneMinusLinearDampingTimesDT, oneMinusLinearDampingTimesDT, 0.0f); + const PxReal angVelMultiplier = physx::intrinsics::fsel(oneMinusAngularDampingTimesDT, oneMinusAngularDampingTimesDT, 0.0f); + linearVelocity*=linVelMultiplier; + angularVelocity*=angVelMultiplier; + + // Clamp velocity + const PxReal linVelSq = linearVelocity.magnitudeSquared(); + if(linVelSq > maxLinearVelocitySq) + { + linearVelocity *= PxSqrt(maxLinearVelocitySq / linVelSq); + } + const PxReal angVelSq = angularVelocity.magnitudeSquared(); + if(angVelSq > maxAngularVelocitySq) + { + angularVelocity *= PxSqrt(maxAngularVelocitySq / angVelSq); + } + + inOutLinearVelocity = linearVelocity; + inOutAngularVelocity = angularVelocity; +} + + +PX_FORCE_INLINE void integrateCore(PxVec3& motionLinearVelocity, PxVec3& motionAngularVelocity, PxSolverBody& solverBody, PxSolverBodyData& solverBodyData, const PxF32 dt) +{ + PxU32 lockFlags = solverBodyData.lockFlags; + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + { + motionLinearVelocity.x = 0.f; + solverBody.linearVelocity.x = 0.f; + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + { + motionLinearVelocity.y = 0.f; + solverBody.linearVelocity.y = 0.f; + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + { + motionLinearVelocity.z = 0.f; + solverBody.linearVelocity.z = 0.f; + } + + //The angular velocity should be 0 because it is now impossible to make it rotate around that axis! + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + { + motionAngularVelocity.x = 0.f; + solverBody.angularState.x = 0.f; + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + { + motionAngularVelocity.y = 0.f; + solverBody.angularState.y = 0.f; + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + { + motionAngularVelocity.z = 0.f; + solverBody.angularState.z = 0.f; + } + } + + // Integrate linear part + PxVec3 linearMotionVel = solverBodyData.linearVelocity + motionLinearVelocity; + PxVec3 delta = linearMotionVel * dt; + PxVec3 angularMotionVel = solverBodyData.angularVelocity + solverBodyData.sqrtInvInertia * motionAngularVelocity; + PxReal w = angularMotionVel.magnitudeSquared(); + solverBodyData.body2World.p += delta; + PX_ASSERT(solverBodyData.body2World.p.isFinite()); + + //Store back the linear and angular velocities + //core.linearVelocity += solverBody.linearVelocity * solverBodyData.sqrtInvMass; + solverBodyData.linearVelocity += solverBody.linearVelocity; + solverBodyData.angularVelocity += solverBodyData.sqrtInvInertia * solverBody.angularState; + + // Integrate the rotation using closed form quaternion integrator + if (w != 0.0f) + { + w = PxSqrt(w); + // Perform a post-solver clamping + // TODO(dsequeira): ignore this for the moment + //just clamp motionVel to half float-range + const PxReal maxW = 1e+7f; //Should be about sqrt(PX_MAX_REAL/2) or smaller + if (w > maxW) + { + angularMotionVel = angularMotionVel.getNormalized() * maxW; + w = maxW; + } + const PxReal v = dt * w * 0.5f; + PxReal s, q; + Ps::sincos(v, s, q); + s /= w; + + const PxVec3 pqr = angularMotionVel * s; + const PxQuat quatVel(pqr.x, pqr.y, pqr.z, 0); + PxQuat result = quatVel * solverBodyData.body2World.q; + + result += solverBodyData.body2World.q * q; + + solverBodyData.body2World.q = result.getNormalized(); + PX_ASSERT(solverBodyData.body2World.q.isSane()); + PX_ASSERT(solverBodyData.body2World.q.isFinite()); + } + + motionLinearVelocity = linearMotionVel; + motionAngularVelocity = angularMotionVel; +} + + +PX_FORCE_INLINE PxReal updateWakeCounter(PxsRigidBody* originalBody, PxReal dt, PxReal /*invDt*/, const bool enableStabilization, const bool useAdaptiveForce, Cm::SpatialVector& motionVelocity, + bool hasStaticTouch) +{ + //KS - at most one of these features can be enabled at any time + PX_ASSERT(!useAdaptiveForce || !enableStabilization); + PxsBodyCore& bodyCore = originalBody->getCore(); + + // update the body's sleep state and + PxReal wakeCounterResetTime = 20.0f*0.02f; + + PxReal wc = bodyCore.wakeCounter; + + { + if (enableStabilization) + { + bool freeze = false; + const PxTransform& body2World = bodyCore.body2World; + + // calculate normalized energy: kinetic energy divided by mass + + const PxVec3 t = bodyCore.inverseInertia; + const PxVec3 inertia(t.x > 0.f ? 1.0f / t.x : 1.f, t.y > 0.f ? 1.0f / t.y : 1.f, t.z > 0.f ? 1.0f / t.z : 1.f); + + + PxVec3 sleepLinVelAcc = motionVelocity.linear; + PxVec3 sleepAngVelAcc = body2World.q.rotateInv(motionVelocity.angular); + + // scale threshold by cluster factor (more contacts => higher sleep threshold) + //const PxReal clusterFactor = PxReal(1u + getNumUniqueInteractions()); + + PxReal invMass = bodyCore.inverseMass; + if (invMass == 0.f) + invMass = 1.f; + + const PxReal angular = sleepAngVelAcc.multiply(sleepAngVelAcc).dot(inertia) * invMass; + const PxReal linear = sleepLinVelAcc.magnitudeSquared(); + PxReal frameNormalizedEnergy = 0.5f * (angular + linear); + + const PxReal cf = hasStaticTouch ? PxReal(PxMin(10u, bodyCore.numBodyInteractions)) : 0.f; + const PxReal freezeThresh = cf*bodyCore.freezeThreshold; + + originalBody->freezeCount = PxMax(originalBody->freezeCount - dt, 0.0f); + bool settled = true; + + PxReal accelScale = PxMin(1.f, originalBody->accelScale + dt); + + if (frameNormalizedEnergy >= freezeThresh) + { + settled = false; + originalBody->freezeCount = PXD_FREEZE_INTERVAL; + } + + if (!hasStaticTouch) + { + accelScale = 1.f; + settled = false; + } + + + if (settled) + { + //Dampen bodies that are just about to go to sleep + if (cf > 1.f) + { + const PxReal sleepDamping = PXD_SLEEP_DAMPING; + const PxReal sleepDampingTimesDT = sleepDamping*dt; + const PxReal d = 1.0f - sleepDampingTimesDT; + bodyCore.linearVelocity = bodyCore.linearVelocity * d; + bodyCore.angularVelocity = bodyCore.angularVelocity * d; + accelScale = accelScale * 0.75f + 0.25f*PXD_FREEZE_SCALE; + } + freeze = originalBody->freezeCount == 0.f && frameNormalizedEnergy < (bodyCore.freezeThreshold * PXD_FREEZE_TOLERANCE); + } + + originalBody->accelScale = accelScale; + + if (freeze) + { + //current flag isn't frozen but freeze flag raise so we need to raise the frozen flag in this frame + bool wasNotFrozen = (originalBody->mInternalFlags & PxsRigidBody::eFROZEN) == 0; + PxU16 flags = PxU16((originalBody->mInternalFlags & PxsRigidBody::eDISABLE_GRAVITY) | PxsRigidBody::eFROZEN); + if (wasNotFrozen) + { + flags |= PxsRigidBody::eFREEZE_THIS_FRAME; + } + originalBody->mInternalFlags = flags; + bodyCore.body2World = originalBody->getLastCCDTransform(); + } + else + { + PxU16 flags = PxU16(originalBody->mInternalFlags & PxsRigidBody::eDISABLE_GRAVITY); + bool wasFrozen = (originalBody->mInternalFlags & PxsRigidBody::eFROZEN) != 0; + if (wasFrozen) + { + flags |= PxsRigidBody::eUNFREEZE_THIS_FRAME; + } + originalBody->mInternalFlags = flags; + } + + /*KS: New algorithm for sleeping when using stabilization: + * Energy *this frame* must be higher than sleep threshold and accumulated energy over previous frames + * must be higher than clusterFactor*energyThreshold. + */ + if (wc < wakeCounterResetTime * 0.5f || wc < dt) + { + //Accumulate energy + originalBody->sleepLinVelAcc += sleepLinVelAcc; + originalBody->sleepAngVelAcc += sleepAngVelAcc; + + //If energy this frame is high + if (frameNormalizedEnergy >= bodyCore.sleepThreshold) + { + //Compute energy over sleep preparation time + const PxReal sleepAngular = originalBody->sleepAngVelAcc.multiply(originalBody->sleepAngVelAcc).dot(inertia) * invMass; + const PxReal sleepLinear = originalBody->sleepLinVelAcc.magnitudeSquared(); + PxReal normalizedEnergy = 0.5f * (sleepAngular + sleepLinear); + const PxReal sleepClusterFactor = PxReal(1u + bodyCore.numCountedInteractions); + // scale threshold by cluster factor (more contacts => higher sleep threshold) + const PxReal threshold = sleepClusterFactor*bodyCore.sleepThreshold; + + //If energy over sleep preparation time is high + if (normalizedEnergy >= threshold) + { + //Wake up + //PX_ASSERT(isActive()); + originalBody->sleepAngVelAcc = PxVec3(0); + originalBody->sleepLinVelAcc = PxVec3(0); + + const float factor = bodyCore.sleepThreshold == 0.f ? 2.0f : PxMin(normalizedEnergy / threshold, 2.0f); + PxReal oldWc = wc; + wc = factor * 0.5f * wakeCounterResetTime + dt * (sleepClusterFactor - 1.0f); + bodyCore.solverWakeCounter = wc; + //if (oldWc == 0.0f) // for the case where a sleeping body got activated by the system (not the user) AND got processed by the solver as well + // notifyNotReadyForSleeping(bodyCore.nodeIndex); + + if (oldWc == 0.0f) + originalBody->mInternalFlags |= PxsRigidBody::eACTIVATE_THIS_FRAME; + + return wc; + } + } + } + + } + else + { + if (useAdaptiveForce) + { + if (hasStaticTouch && bodyCore.numBodyInteractions > 1) + originalBody->accelScale = 1.f / PxReal(bodyCore.numBodyInteractions); + else + originalBody->accelScale = 1.f; + } + if (wc < wakeCounterResetTime * 0.5f || wc < dt) + { + const PxTransform& body2World = bodyCore.body2World; + + // calculate normalized energy: kinetic energy divided by mass + const PxVec3 t = bodyCore.inverseInertia; + const PxVec3 inertia(t.x > 0.f ? 1.0f / t.x : 1.f, t.y > 0.f ? 1.0f / t.y : 1.f, t.z > 0.f ? 1.0f / t.z : 1.f); + + PxVec3 sleepLinVelAcc = motionVelocity.linear; + PxVec3 sleepAngVelAcc = body2World.q.rotateInv(motionVelocity.angular); + + originalBody->sleepLinVelAcc += sleepLinVelAcc; + originalBody->sleepAngVelAcc += sleepAngVelAcc; + + PxReal invMass = bodyCore.inverseMass; + if (invMass == 0.f) + invMass = 1.f; + + const PxReal angular = originalBody->sleepAngVelAcc.multiply(originalBody->sleepAngVelAcc).dot(inertia) * invMass; + const PxReal linear = originalBody->sleepLinVelAcc.magnitudeSquared(); + PxReal normalizedEnergy = 0.5f * (angular + linear); + + // scale threshold by cluster factor (more contacts => higher sleep threshold) + const PxReal clusterFactor = PxReal(1 + bodyCore.numCountedInteractions); + const PxReal threshold = clusterFactor*bodyCore.sleepThreshold; + + if (normalizedEnergy >= threshold) + { + //PX_ASSERT(isActive()); + originalBody->sleepLinVelAcc = PxVec3(0); + originalBody->sleepAngVelAcc = PxVec3(0); + const float factor = threshold == 0.f ? 2.0f : PxMin(normalizedEnergy / threshold, 2.0f); + PxReal oldWc = wc; + wc = factor * 0.5f * wakeCounterResetTime + dt * (clusterFactor - 1.0f); + bodyCore.solverWakeCounter = wc; + PxU16 flags = PxU16(originalBody->mInternalFlags & PxsRigidBody::eDISABLE_GRAVITY); + if (oldWc == 0.0f) // for the case where a sleeping body got activated by the system (not the user) AND got processed by the solver as well + { + flags |= PxsRigidBody::eACTIVATE_THIS_FRAME; + //notifyNotReadyForSleeping(bodyCore.nodeIndex); + } + + originalBody->mInternalFlags = flags; + + return wc; + } + } + } + } + + wc = PxMax(wc - dt, 0.0f); + bodyCore.solverWakeCounter = wc; + return wc; +} + +PX_FORCE_INLINE void sleepCheck(PxsRigidBody* originalBody, const PxReal dt, const PxReal intDt, const bool enableStabilization, bool useAdaptiveForce, Cm::SpatialVector& motionVelocity, + bool hasStaticTouch) +{ + + PxReal wc = updateWakeCounter(originalBody, dt, intDt, enableStabilization, useAdaptiveForce, motionVelocity, hasStaticTouch); + bool wakeCounterZero = (wc == 0.0f); + + if (wakeCounterZero) + { + //PxsBodyCore& bodyCore = originalBody->getCore(); + originalBody->mInternalFlags |= PxsRigidBody::eDEACTIVATE_THIS_FRAME; + // notifyReadyForSleeping(bodyCore.nodeIndex); + originalBody->sleepLinVelAcc = PxVec3(0); + originalBody->sleepAngVelAcc = PxVec3(0); + } +} + +} + +} + +#endif //DY_BODYCORE_INTEGRATOR_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp new file mode 100644 index 000000000..5d95b8e69 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp @@ -0,0 +1,917 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "DyConstraintPartition.h" +#include "DyArticulationUtils.h" + +#define INTERLEAVE_SELF_CONSTRAINTS 1 + + +namespace physx +{ +namespace Dy +{ + +namespace +{ + +PX_FORCE_INLINE PxU32 getArticulationIndex(const uintptr_t eaArticulation, const uintptr_t* eaArticulations, const PxU32 numEas) +{ + PxU32 index=0xffffffff; + for(PxU32 i=0;isolverProgress; + bodyBProgress = desc.bodyB->solverProgress; + return activeA && activeB; + } + + PX_FORCE_INLINE void getProgress(const PxSolverConstraintDesc& desc, + PxU32& bodyAProgress, PxU32& bodyBProgress) + { + bodyAProgress = desc.bodyA->solverProgress; + bodyBProgress = desc.bodyB->solverProgress; + } + + PX_FORCE_INLINE void storeProgress(const PxSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress, + const PxU16 availablePartition) + { + desc.bodyA->solverProgress = bodyAProgress; + desc.bodyA->maxSolverNormalProgress = PxMax(desc.bodyA->maxSolverNormalProgress, availablePartition); + desc.bodyB->solverProgress = bodyBProgress; + desc.bodyB->maxSolverNormalProgress = PxMax(desc.bodyB->maxSolverNormalProgress, availablePartition); + } + + PX_FORCE_INLINE void storeProgress(const PxSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress) + { + desc.bodyA->solverProgress = bodyAProgress; + desc.bodyB->solverProgress = bodyBProgress; + } + + PX_FORCE_INLINE PxU32 getStaticContactWriteIndex(const PxSolverConstraintDesc& desc, + bool activeA, bool activeB) + { + if (activeA) + return PxU32(desc.bodyA->maxSolverNormalProgress + desc.bodyA->maxSolverFrictionProgress++); + else if (activeB) + return PxU32(desc.bodyB->maxSolverNormalProgress + desc.bodyB->maxSolverFrictionProgress++); + + return 0xffffffff; + + } + + PX_FORCE_INLINE void recordStaticConstraint(const PxSolverConstraintDesc& desc, bool& activeA, bool& activeB) const + { + if (activeA) + { + desc.bodyA->maxSolverFrictionProgress++; + } + + if (activeB) + { + desc.bodyB->maxSolverFrictionProgress++; + } + } + + PX_FORCE_INLINE void clearState() + { + for(PxU32 a = 0; a < mNumBodies; ++a) + mBodies[a].solverProgress = 0; + } + + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(Ps::Array& numConstraintsPerPartition) + { + for(PxU32 a = 0; a < mNumBodies; ++a) + { + mBodies[a].solverProgress = 0; + + PxU32 requiredSize = PxU32(mBodies[a].maxSolverNormalProgress + mBodies[a].maxSolverFrictionProgress); + if(requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for(PxU32 b = 0; b < mBodies[a].maxSolverFrictionProgress; ++b) + { + numConstraintsPerPartition[mBodies[a].maxSolverNormalProgress + b]++; + } + } + } +}; + +class ExtendedRigidBodyClassification +{ + + PxSolverBody* PX_RESTRICT mBodies; + PxU32 mNumBodies; + //uintptr_t* PX_RESTRICT mFsDatas; + uintptr_t* PX_RESTRICT mArticulations; + PxU32 mNumArticulations; + +public: + + ExtendedRigidBodyClassification(PxSolverBody* PX_RESTRICT bodies, PxU32 numBodies, uintptr_t* articulations, PxU32 numArticulations) + : mBodies(bodies), mNumBodies(numBodies), mArticulations(articulations), mNumArticulations(numArticulations) + { + } + + //Returns true if it is a dynamic-dynamic constriant; false if it is a dynamic-static or dynamic-kinematic constraint + PX_FORCE_INLINE bool classifyConstraint(const PxSolverConstraintDesc& desc, uintptr_t& indexA, uintptr_t& indexB, + bool& activeA, bool& activeB, PxU32& bodyAProgress, PxU32& bodyBProgress) const + { + bool hasStatic = false; + if(PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + indexA=uintptr_t(desc.bodyA - mBodies); + activeA = indexA < mNumBodies; + hasStatic = desc.bodyADataIndex == 0; + bodyAProgress = activeA ? desc.bodyA->solverProgress: 0; + } + else + { + + ArticulationV* articulationA = desc.articulationA; + indexA=mNumBodies+getArticulationIndex(uintptr_t(articulationA), mArticulations ,mNumArticulations); + //bodyAProgress = articulationA->getFsDataPtr()->solverProgress; + bodyAProgress = articulationA->solverProgress; + activeA = true; + } + if(PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + indexB=uintptr_t(desc.bodyB - mBodies); + activeB = indexB < mNumBodies; + hasStatic = hasStatic || desc.bodyBDataIndex == 0; + bodyBProgress = activeB ? desc.bodyB->solverProgress : 0; + } + else + { + Articulation* articulationB = static_cast(desc.articulationB); + indexB=mNumBodies+getArticulationIndex(uintptr_t(articulationB), mArticulations, mNumArticulations); + activeB = true; + bodyBProgress = articulationB->solverProgress; + } + return !hasStatic; + } + + PX_FORCE_INLINE void getProgress(const PxSolverConstraintDesc& desc, + PxU32& bodyAProgress, PxU32& bodyBProgress) const + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + bodyAProgress = desc.bodyA->solverProgress; + } + else + { + ArticulationV* articulationA = desc.articulationA; + bodyAProgress = articulationA->solverProgress; + } + + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + + bodyBProgress = desc.bodyB->solverProgress; + } + else + { + ArticulationV* articulationB = desc.articulationB; + bodyBProgress = articulationB->solverProgress; + } + } + + + PX_FORCE_INLINE void storeProgress(const PxSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress, + const PxU16 availablePartition) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + desc.bodyA->solverProgress = bodyAProgress; + desc.bodyA->maxSolverNormalProgress = PxMax(desc.bodyA->maxSolverNormalProgress, availablePartition); + } + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->solverProgress = bodyAProgress; + articulationA->maxSolverNormalProgress = PxMax(articulationA->maxSolverNormalProgress, availablePartition); + } + + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + desc.bodyB->solverProgress = bodyBProgress; + desc.bodyB->maxSolverNormalProgress = PxMax(desc.bodyB->maxSolverNormalProgress, availablePartition); + } + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->solverProgress = bodyBProgress; + articulationB->maxSolverNormalProgress = PxMax(articulationB->maxSolverNormalProgress, availablePartition); + } + } + + PX_FORCE_INLINE void storeProgress(const PxSolverConstraintDesc& desc, const PxU32 bodyAProgress, + const PxU32 bodyBProgress) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + desc.bodyA->solverProgress = bodyAProgress; + } + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->solverProgress = bodyAProgress; + } + + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + desc.bodyB->solverProgress = bodyBProgress; + } + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->solverProgress = bodyBProgress; + } + } + + PX_FORCE_INLINE void recordStaticConstraint(const PxSolverConstraintDesc& desc, bool& activeA, bool& activeB) + { + if (activeA) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + desc.bodyA->maxSolverFrictionProgress++; + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->maxSolverFrictionProgress++; + } + } + + if (activeB) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + desc.bodyB->maxSolverFrictionProgress++; + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->maxSolverFrictionProgress++; + } + } + } + + PX_FORCE_INLINE PxU32 getStaticContactWriteIndex(const PxSolverConstraintDesc& desc, + bool activeA, bool activeB) + { + if (activeA) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + return PxU32(desc.bodyA->maxSolverNormalProgress + desc.bodyA->maxSolverFrictionProgress++); + } + else + { + ArticulationV* articulationA = desc.articulationA; + return PxU32(articulationA->maxSolverNormalProgress + articulationA->maxSolverFrictionProgress++); + } + } + else if (activeB) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + return PxU32(desc.bodyB->maxSolverNormalProgress + desc.bodyB->maxSolverFrictionProgress++); + } + else + { + ArticulationV* articulationB = desc.articulationB; + return PxU32(articulationB->maxSolverNormalProgress + articulationB->maxSolverFrictionProgress++); + } + } + + return 0xffffffff; + } + + PX_FORCE_INLINE void clearState() + { + for(PxU32 a = 0; a < mNumBodies; ++a) + mBodies[a].solverProgress = 0; + + for(PxU32 a = 0; a < mNumArticulations; ++a) + (reinterpret_cast(mArticulations[a]))->solverProgress = 0; + } + + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(Ps::Array& numConstraintsPerPartition) + { + for(PxU32 a = 0; a < mNumBodies; ++a) + { + mBodies[a].solverProgress = 0; + + PxU32 requiredSize = PxU32(mBodies[a].maxSolverNormalProgress + mBodies[a].maxSolverFrictionProgress); + if(requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for(PxU32 b = 0; b < mBodies[a].maxSolverFrictionProgress; ++b) + { + numConstraintsPerPartition[mBodies[a].maxSolverNormalProgress + b]++; + } + } + + for(PxU32 a = 0; a < mNumArticulations; ++a) + { + ArticulationV* articulation = reinterpret_cast(mArticulations[a]); + articulation->solverProgress = 0; + + PxU32 requiredSize = PxU32(articulation->maxSolverNormalProgress + articulation->maxSolverFrictionProgress); + if(requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for(PxU32 b = 0; b < articulation->maxSolverFrictionProgress; ++b) + { + numConstraintsPerPartition[articulation->maxSolverNormalProgress + b]++; + } + } + } + +}; + +template +void classifyConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + Ps::Array& numConstraintsPerPartition, PxSolverConstraintDesc* PX_RESTRICT eaTempConstraintDescriptors) +{ + const PxSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxU32 numUnpartitionedConstraints = 0; + + numConstraintsPerPartition.forceSize_Unsafe(32); + + PxMemZero(numConstraintsPerPartition.begin(), sizeof(PxU32) * 32); + + for(PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + + PxU32 partitionsA, partitionsB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB, + partitionsA, partitionsB); + + if(notContainsStatic) + { + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if(availablePartition == MAX_NUM_PARTITIONS) + { + eaTempConstraintDescriptors[numUnpartitionedConstraints++] = *_desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + if (activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + + classification.storeProgress(*_desc, partitionsA, partitionsB, PxU16(availablePartition)); + } + else + { + classification.recordStaticConstraint(*_desc, activeA, activeB); + } + } + + PxU32 partitionStartIndex = 0; + + while(numUnpartitionedConstraints > 0) + { + classification.clearState(); + + partitionStartIndex += 32; + //Keep partitioning the un-partitioned constraints and blat the whole thing to 0! + numConstraintsPerPartition.resize(32 + numConstraintsPerPartition.size()); + PxMemZero(numConstraintsPerPartition.begin() + partitionStartIndex, sizeof(PxU32) * 32); + + PxU32 newNumUnpartitionedConstraints = 0; + PxU32 partitionsA, partitionsB; + bool activeA, activeB; + uintptr_t indexA, indexB; + for(PxU32 i = 0; i < numUnpartitionedConstraints; ++i) + { + const PxSolverConstraintDesc& desc = eaTempConstraintDescriptors[i]; + + classification.classifyConstraint(desc, indexA, indexB, activeA, activeB, + partitionsA, partitionsB); + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if(availablePartition == MAX_NUM_PARTITIONS) + { + //Need to shuffle around unpartitioned constraints... + eaTempConstraintDescriptors[newNumUnpartitionedConstraints++] = desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + + /*desc.bodyA->solverProgress = partitionsA; + desc.bodyB->solverProgress = partitionsB;*/ + availablePartition += partitionStartIndex; + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + + classification.storeProgress(desc, partitionsA, partitionsB, PxU16(availablePartition) ); + + /* desc.bodyA->maxSolverNormalProgress = PxMax(desc.bodyA->maxSolverNormalProgress, PxU16(availablePartition)); + desc.bodyB->maxSolverNormalProgress = PxMax(desc.bodyB->maxSolverNormalProgress, PxU16(availablePartition));*/ + } + + numUnpartitionedConstraints = newNumUnpartitionedConstraints; + } + + classification.reserveSpaceForStaticConstraints(numConstraintsPerPartition); + +} + +template +void writeConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + Ps::Array& accumulatedConstraintsPerPartition, PxSolverConstraintDesc* eaTempConstraintDescriptors, + PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDesc) +{ + PX_UNUSED(eaTempConstraintDescriptors); + const PxSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxU32 numUnpartitionedConstraints = 0; + + for(PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB, partitionsA, partitionsB); + + if(notContainsStatic) + { + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if(availablePartition == MAX_NUM_PARTITIONS) + { + eaTempConstraintDescriptors[numUnpartitionedConstraints++] = *_desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + classification.storeProgress(*_desc, partitionsA, partitionsB, PxU16(availablePartition + 1)); + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + } + else + { + //Just count the number of static constraints and store in maxSolverFrictionProgress... + PxU32 index = classification.getStaticContactWriteIndex(*_desc, activeA, activeB); + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[index]++] = *_desc; + } + } + + PxU32 partitionStartIndex = 0; + + while(numUnpartitionedConstraints > 0) + { + classification.clearState(); + + partitionStartIndex += 32; + PxU32 newNumUnpartitionedConstraints = 0; + + PxU32 partitionsA, partitionsB; + bool activeA, activeB; + uintptr_t indexA, indexB; + for(PxU32 i = 0; i < numUnpartitionedConstraints; ++i) + { + const PxSolverConstraintDesc& desc = eaTempConstraintDescriptors[i]; + + /* PxU32 partitionsA=desc.bodyA->solverProgress; + PxU32 partitionsB=desc.bodyB->solverProgress;*/ + + classification.classifyConstraint(desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if(availablePartition == MAX_NUM_PARTITIONS) + { + //Need to shuffle around unpartitioned constraints... + eaTempConstraintDescriptors[newNumUnpartitionedConstraints++] = desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + /*desc.bodyA->solverProgress = partitionsA; + desc.bodyB->solverProgress = partitionsB; +*/ + classification.storeProgress(desc, partitionsA, partitionsB); + availablePartition += partitionStartIndex; + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = desc; + } + + numUnpartitionedConstraints = newNumUnpartitionedConstraints; + } +} + +} + +#define PX_NORMALIZE_PARTITIONS 1 + +#if PX_NORMALIZE_PARTITIONS + +template +PxU32 normalizePartitions(Ps::Array& accumulatedConstraintsPerPartition, PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDescriptors, + const PxU32 numConstraintDescriptors, Ps::Array& bitField, const Classification& classification, const PxU32 numBodies, const PxU32 numArticulations) +{ + PxU32 numPartitions = 0; + + PxU32 prevAccumulation = 0; + for(; numPartitions < accumulatedConstraintsPerPartition.size() && accumulatedConstraintsPerPartition[numPartitions] > prevAccumulation; + prevAccumulation = accumulatedConstraintsPerPartition[numPartitions++]); + + PxU32 targetSize = (numPartitions == 0 ? 0 : (numConstraintDescriptors)/numPartitions); + + bitField.reserve((numBodies + numArticulations + 31)/32); + bitField.forceSize_Unsafe((numBodies + numArticulations + 31)/32); + + for(PxU32 i = numPartitions; i > 0; i--) + { + PxU32 partitionIndex = i-1; + + //Build the partition mask... + + PxU32 startIndex = partitionIndex == 0 ? 0 : accumulatedConstraintsPerPartition[partitionIndex-1]; + PxU32 endIndex = accumulatedConstraintsPerPartition[partitionIndex]; + + //If its greater than target size, there's nothing that will be pulled into it from earlier partitions + if((endIndex - startIndex) >= targetSize) + continue; + + + PxMemZero(bitField.begin(), sizeof(PxU32)*bitField.size()); + + for(PxU32 a = startIndex; a < endIndex; ++a) + { + PxSolverConstraintDesc& desc = eaOrderedConstraintDescriptors[a]; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + + classification.classifyConstraint(desc, indexA, indexB, activeA, activeB, partitionsA, partitionsB); + + if(activeA) + bitField[PxU32(indexA)/32] |= getBit(indexA & 31); + if(activeB) + bitField[PxU32(indexB)/32] |= getBit(indexB & 31); + } + + bool bTerm = false; + for(PxU32 a = partitionIndex; a > 0 && !bTerm; --a) + { + PxU32 pInd = a-1; + + PxU32 si = pInd == 0 ? 0 : accumulatedConstraintsPerPartition[pInd-1]; + PxU32 ei = accumulatedConstraintsPerPartition[pInd]; + + for(PxU32 b = ei; b > si && !bTerm; --b) + { + PxU32 ind = b-1; + PxSolverConstraintDesc& desc = eaOrderedConstraintDescriptors[ind]; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + + classification.classifyConstraint(desc, indexA, indexB, activeA, activeB, partitionsA, partitionsB); + + bool canAdd = true; + + if(activeA && (bitField[PxU32(indexA)/32] & (getBit(indexA & 31)))) + canAdd = false; + if(activeB && (bitField[PxU32(indexB)/32] & (getBit(indexB & 31)))) + canAdd = false; + + if(canAdd) + { + PxSolverConstraintDesc tmp = eaOrderedConstraintDescriptors[ind]; + + if(activeA) + bitField[PxU32(indexA)/32] |= (getBit(indexA & 31)); + if(activeB) + bitField[PxU32(indexB)/32] |= (getBit(indexB & 31)); + + PxU32 index = ind; + for(PxU32 c = pInd; c < partitionIndex; ++c) + { + PxU32 newIndex = --accumulatedConstraintsPerPartition[c]; + if(index != newIndex) + eaOrderedConstraintDescriptors[index] = eaOrderedConstraintDescriptors[newIndex]; + index = newIndex; + } + + if(index != ind) + eaOrderedConstraintDescriptors[index] = tmp; + + if((accumulatedConstraintsPerPartition[partitionIndex] - accumulatedConstraintsPerPartition[partitionIndex-1]) >= targetSize) + { + bTerm = true; + break; + } + } + } + } + } + + PxU32 partitionCount = 0; + PxU32 lastPartitionCount = 0; + for (PxU32 a = 0; a < numPartitions; ++a) + { + const PxU32 constraintCount = accumulatedConstraintsPerPartition[a]; + accumulatedConstraintsPerPartition[partitionCount] = constraintCount; + if (constraintCount != lastPartitionCount) + { + lastPartitionCount = constraintCount; + partitionCount++; + } + } + + accumulatedConstraintsPerPartition.forceSize_Unsafe(partitionCount); + + return partitionCount; +} + +#endif + +PxU32 partitionContactConstraints(ConstraintPartitionArgs& args) +{ + PxU32 maxPartition = 0; + //Unpack the input data. + const PxU32 numBodies=args.mNumBodies; + PxSolverBody* PX_RESTRICT eaAtoms=args.mBodies; + const PxU32 numArticulations=args.mNumArticulationPtrs; + + const PxU32 numConstraintDescriptors=args.mNumContactConstraintDescriptors; + + PxSolverConstraintDesc* PX_RESTRICT eaConstraintDescriptors=args.mContactConstraintDescriptors; + PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDescriptors=args.mOrderedContactConstraintDescriptors; + PxSolverConstraintDesc* PX_RESTRICT eaTempConstraintDescriptors=args.mTempContactConstraintDescriptors; + + Ps::Array& constraintsPerPartition = *args.mConstraintsPerPartition; + constraintsPerPartition.forceSize_Unsafe(0); + + for(PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.solverProgress = 0; + //We re-use maxSolverFrictionProgress and maxSolverNormalProgress to record the + //maximum partition used by dynamic constraints and the number of static constraints affecting + //a body. We use this to make partitioning much cheaper and be able to support + body.maxSolverFrictionProgress = 0; + body.maxSolverNormalProgress = 0; + } + + PxU32 numOrderedConstraints=0; + + PxU32 numSelfConstraintBlocks=0; + + if(numArticulations == 0) + { + RigidBodyClassification classification(eaAtoms, numBodies); + classifyConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors); + + PxU32 accumulation = 0; + for(PxU32 a = 0; a < constraintsPerPartition.size(); ++a) + { + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; + } + + for(PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.solverProgress = 0; + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... + body.maxSolverFrictionProgress = 0; + } + + writeConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors, eaOrderedConstraintDescriptors); + + numOrderedConstraints = numConstraintDescriptors; + + if(!args.enhancedDeterminism) + maxPartition = normalizePartitions(constraintsPerPartition, eaOrderedConstraintDescriptors, numConstraintDescriptors, *args.mBitField, + classification, numBodies, 0); + + } + else + { + + const ArticulationSolverDesc* articulationDescs=args.mArticulationPtrs; + PX_ALLOCA(_eaArticulations, uintptr_t, numArticulations); + uintptr_t* eaArticulations = _eaArticulations; + for(PxU32 i=0;isolverProgress = 0; + articulation->maxSolverFrictionProgress = 0; + articulation->maxSolverNormalProgress = 0; + } + ExtendedRigidBodyClassification classification(eaAtoms, numBodies, eaArticulations, numArticulations); + + classifyConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, + constraintsPerPartition, eaTempConstraintDescriptors); + + PxU32 accumulation = 0; + for(PxU32 a = 0; a < constraintsPerPartition.size(); ++a) + { + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; + } + + for(PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.solverProgress = 0; + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... + body.maxSolverFrictionProgress = 0; + } + + for(PxU32 a = 0; a < numArticulations; ++a) + { + ArticulationV* articulation = reinterpret_cast(eaArticulations[a]); + articulation->solverProgress = 0; + articulation->maxSolverFrictionProgress = 0; + } + + writeConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors, eaOrderedConstraintDescriptors); + + numOrderedConstraints = numConstraintDescriptors; + + if (!args.enhancedDeterminism) + maxPartition = normalizePartitions(constraintsPerPartition, eaOrderedConstraintDescriptors, + numConstraintDescriptors, *args.mBitField, classification, numBodies, numArticulations); + + } + + + + const PxU32 numConstraintsDifferentBodies=numOrderedConstraints; + + PX_ASSERT(numConstraintsDifferentBodies == numConstraintDescriptors); + + //Now handle the articulated self-constraints. + PxU32 totalConstraintCount = numConstraintsDifferentBodies; + + args.mNumSelfConstraintBlocks=numSelfConstraintBlocks; + + args.mNumDifferentBodyConstraints=numConstraintsDifferentBodies; + args.mNumSelfConstraints=totalConstraintCount-numConstraintsDifferentBodies; + + if (args.enhancedDeterminism) + { + PxU32 prevPartitionSize = 0; + maxPartition = 0; + for (PxU32 a = 0; a < constraintsPerPartition.size(); ++a, maxPartition++) + { + if (constraintsPerPartition[a] == prevPartitionSize) + break; + prevPartitionSize = constraintsPerPartition[a]; + } + } + + return maxPartition; +} + +} + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h new file mode 100644 index 000000000..a66cd6993 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_CONSTRAINTPARTITION_H +#define DY_CONSTRAINTPARTITION_H + +#include "DyDynamics.h" + + + +namespace physx +{ + +namespace Dy +{ +struct ConstraintPartitionArgs +{ + enum + { + eMAX_NUM_BODIES = 8192 + }; + + //Input + PxSolverBody* mBodies; + PxU32 mNumBodies; + ArticulationSolverDesc* mArticulationPtrs; + PxU32 mNumArticulationPtrs; + PxSolverConstraintDesc* mContactConstraintDescriptors; + PxU32 mNumContactConstraintDescriptors; + //output + PxSolverConstraintDesc* mOrderedContactConstraintDescriptors; + PxSolverConstraintDesc* mTempContactConstraintDescriptors; + PxU32 mNumSelfConstraintBlocks; + PxU32 mNumDifferentBodyConstraints; + PxU32 mNumSelfConstraints; + Ps::Array* mConstraintsPerPartition; + //Ps::Array* mStartIndices; + Ps::Array* mBitField; + + bool enhancedDeterminism; +}; + +PxU32 partitionContactConstraints(ConstraintPartitionArgs& args); + +} // namespace physx + +} + + + +#endif // DY_CONSTRAINTPARTITION_H + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h new file mode 100644 index 000000000..27d6190bd --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h @@ -0,0 +1,101 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_CONSTRAINTSHADER_H +#define DY_CONSTRAINTSHADER_H + +#include "DyConstraint.h" + +#include "DySolverConstraintDesc.h" +#include "PsArray.h" + +#define DY_ARTICULATION_MIN_RESPONSE 1e-5f + +#define DY_ARTICULATION_BAD_RESPONSE 0.02f + +namespace physx +{ + +class PxcConstraintBlockStream; +class PxsConstraintBlockManager; +struct PxSolverBody; +struct PxSolverBodyData; +struct PxSolverConstraintDesc; + +namespace Cm +{ + struct SpatialVectorF; +} + +namespace Dy +{ + + static const PxU32 MAX_CONSTRAINT_ROWS = 12; + +struct SolverConstraintShaderPrepDesc +{ + const Constraint* constraint; + PxConstraintSolverPrep solverPrep; + const void* constantBlock; + PxU32 constantBlockByteSize; +}; + +SolverConstraintPrepState::Enum setupSolverConstraint4 + (SolverConstraintShaderPrepDesc* PX_RESTRICT constraintShaderDescs, + PxSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal recipdt, PxU32& totalRows, + PxConstraintAllocator& allocator); + +SolverConstraintPrepState::Enum setupSolverConstraint4 + (PxSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal recipdt, PxU32& totalRows, + PxConstraintAllocator& allocator, PxU32 maxRows); + +PxU32 SetupSolverConstraint(SolverConstraintShaderPrepDesc& shaderDesc, + PxSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + PxReal dt, PxReal invdt, Cm::SpatialVectorF* Z); + + +class ConstraintHelper +{ +public: + + static PxU32 setupSolverConstraint( + PxSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + PxReal dt, PxReal invdt, Cm::SpatialVectorF* Z); +}; + +} + +} + +#endif //DY_CONSTRAINTSHADER_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp new file mode 100644 index 000000000..911f76e58 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp @@ -0,0 +1,605 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxMemory.h" +#include "DyConstraintPrep.h" +#include "PxsRigidBody.h" +#include "DySolverConstraint1D.h" +#include "PsSort.h" +#include "DySolverConstraintDesc.h" +#include "PxcConstraintBlockStream.h" +#include "DyArticulationContactPrep.h" +#include "PsFoundation.h" + +namespace physx +{ +namespace Dy +{ + // dsequeira: + // + // we can choose any linear combination of equality constraints and get the same solution + // Hence we can orthogonalize the constraints using the inner product given by the + // inverse mass matrix, so that when we use PGS, solving a constraint row for a joint + // don't disturb the solution of prior rows. + // + // We also eliminate the equality constraints from the hard inequality constraints - + // (essentially projecting the direction corresponding to the lagrange multiplier + // onto the equality constraint subspace) but 'til I've verified this generates + // exactly the same KKT/complementarity conditions, status is 'experimental'. + // + // since for equality constraints the resulting rows have the property that applying + // an impulse along one row doesn't alter the projected velocity along another row, + // all equality constraints (plus one inequality constraint) can be processed in parallel + // using SIMD + // + // Eliminating the inequality constraints from each other would require a solver change + // and not give us any more parallelism, although we might get better convergence. + +namespace +{ + PX_FORCE_INLINE Vec3V V3FromV4(Vec4V x) { return Vec3V_From_Vec4V(x); } + PX_FORCE_INLINE Vec3V V3FromV4Unsafe(Vec4V x) { return Vec3V_From_Vec4V_WUndefined(x); } + PX_FORCE_INLINE Vec4V V4FromV3(Vec3V x) { return Vec4V_From_Vec3V(x); } + //PX_FORCE_INLINE Vec4V V4ClearW(Vec4V x) { return V4SetW(x, FZero()); } + +struct MassProps +{ + FloatV invMass0; + FloatV invMass1; + FloatV invInertiaScale0; + FloatV invInertiaScale1; + + PX_FORCE_INLINE MassProps(const PxReal imass0, const PxReal imass1, const PxConstraintInvMassScale& ims) + : + invMass0(FLoad(imass0 * ims.linear0)) + , invMass1(FLoad(imass1 * ims.linear1)) + , invInertiaScale0(FLoad(ims.angular0)) + , invInertiaScale1(FLoad(ims.angular1)) + {} +}; + + +PX_FORCE_INLINE PxReal innerProduct(const Px1DConstraint& row0, Px1DConstraint& row1, + PxVec4& row0AngSqrtInvInertia0, PxVec4& row0AngSqrtInvInertia1, + PxVec4& row1AngSqrtInvInertia0, PxVec4& row1AngSqrtInvInertia1, const MassProps& m) +{ + const Vec3V l0 = V3Mul(V3Scale(V3LoadA(row0.linear0), m.invMass0), V3LoadA(row1.linear0)); + const Vec3V l1 = V3Mul(V3Scale(V3LoadA(row0.linear1), m.invMass1), V3LoadA(row1.linear1)); + Vec4V r0ang0 = V4LoadA(&row0AngSqrtInvInertia0.x); + Vec4V r1ang0 = V4LoadA(&row1AngSqrtInvInertia0.x); + Vec4V r0ang1 = V4LoadA(&row0AngSqrtInvInertia1.x); + Vec4V r1ang1 = V4LoadA(&row1AngSqrtInvInertia1.x); + + const Vec3V i0 = V3ScaleAdd(V3Mul(Vec3V_From_Vec4V(r0ang0), Vec3V_From_Vec4V(r1ang0)), m.invInertiaScale0, l0); + const Vec3V i1 = V3ScaleAdd(V3MulAdd(Vec3V_From_Vec4V(r0ang1), Vec3V_From_Vec4V(r1ang1), i0), m.invInertiaScale1, l1); + PxF32 f; + FStore(V3SumElems(i1), &f); + return f; +} + + +// indexed rotation around axis, with sine and cosine of half-angle +PX_FORCE_INLINE PxQuat indexedRotation(PxU32 axis, PxReal s, PxReal c) +{ + PxQuat q(0,0,0,c); + reinterpret_cast(&q)[axis] = s; + return q; +} + +PxQuat diagonalize(const PxMat33& m) // jacobi rotation using quaternions +{ + const PxU32 MAX_ITERS = 5; + + PxQuat q = PxQuat(PxIdentity); + + PxMat33 d; + for(PxU32 i=0; i < MAX_ITERS;i++) + { + const PxMat33 axes(q); + d = axes.getTranspose() * m * axes; + + const PxReal d0 = PxAbs(d[1][2]), d1 = PxAbs(d[0][2]), d2 = PxAbs(d[0][1]); + const PxU32 a = PxU32(d0 > d1 && d0 > d2 ? 0 : d1 > d2 ? 1 : 2); // rotation axis index, from largest off-diagonal element + + const PxU32 a1 = Ps::getNextIndex3(a), a2 = Ps::getNextIndex3(a1); + if(d[a1][a2] == 0.0f || PxAbs(d[a1][a1]-d[a2][a2]) > 2e6f*PxAbs(2.0f*d[a1][a2])) + break; + + const PxReal w = (d[a1][a1]-d[a2][a2]) / (2.0f*d[a1][a2]); // cot(2 * phi), where phi is the rotation angle + const PxReal absw = PxAbs(w); + + PxQuat r; + if(absw>1000) + r = indexedRotation(a, 1.0f/(4.0f*w), 1.f); // h will be very close to 1, so use small angle approx instead + else + { + const PxReal t = 1 / (absw + PxSqrt(w*w+1)); // absolute value of tan phi + const PxReal h = 1 / PxSqrt(t*t+1); // absolute value of cos phi + + PX_ASSERT(h!=1); // |w|<1000 guarantees this with typical IEEE754 machine eps (approx 6e-8) + r = indexedRotation(a, PxSqrt((1-h)/2) * PxSign(w), PxSqrt((1+h)/2)); + } + + q = (q*r).getNormalized(); + } + + return q; +} + + +PX_FORCE_INLINE void rescale(const Mat33V& m, PxVec3& a0, PxVec3& a1, PxVec3& a2) +{ + const Vec3V va0 = V3LoadU(a0); + const Vec3V va1 = V3LoadU(a1); + const Vec3V va2 = V3LoadU(a2); + + const Vec3V b0 = V3ScaleAdd(va0, V3GetX(m.col0), V3ScaleAdd(va1, V3GetY(m.col0), V3Scale(va2, V3GetZ(m.col0)))); + const Vec3V b1 = V3ScaleAdd(va0, V3GetX(m.col1), V3ScaleAdd(va1, V3GetY(m.col1), V3Scale(va2, V3GetZ(m.col1)))); + const Vec3V b2 = V3ScaleAdd(va0, V3GetX(m.col2), V3ScaleAdd(va1, V3GetY(m.col2), V3Scale(va2, V3GetZ(m.col2)))); + + V3StoreU(b0, a0); + V3StoreU(b1, a1); + V3StoreU(b2, a2); +} + +PX_FORCE_INLINE void rescale4(const Mat33V& m, PxReal* a0, PxReal* a1, PxReal* a2) +{ + const Vec4V va0 = V4LoadA(a0); + const Vec4V va1 = V4LoadA(a1); + const Vec4V va2 = V4LoadA(a2); + + const Vec4V b0 = V4ScaleAdd(va0, V3GetX(m.col0), V4ScaleAdd(va1, V3GetY(m.col0), V4Scale(va2, V3GetZ(m.col0)))); + const Vec4V b1 = V4ScaleAdd(va0, V3GetX(m.col1), V4ScaleAdd(va1, V3GetY(m.col1), V4Scale(va2, V3GetZ(m.col1)))); + const Vec4V b2 = V4ScaleAdd(va0, V3GetX(m.col2), V4ScaleAdd(va1, V3GetY(m.col2), V4Scale(va2, V3GetZ(m.col2)))); + + V4StoreA(b0, a0); + V4StoreA(b1, a1); + V4StoreA(b2, a2); +} + +void diagonalize(Px1DConstraint** row, + PxVec4* angSqrtInvInertia0, + PxVec4* angSqrtInvInertia1, + const MassProps &m) +{ + PxReal a00 = innerProduct(*row[0], *row[0], angSqrtInvInertia0[0], angSqrtInvInertia1[0], angSqrtInvInertia0[0], angSqrtInvInertia1[0], m); + PxReal a01 = innerProduct(*row[0], *row[1], angSqrtInvInertia0[0], angSqrtInvInertia1[0], angSqrtInvInertia0[1], angSqrtInvInertia1[1], m); + PxReal a02 = innerProduct(*row[0], *row[2], angSqrtInvInertia0[0], angSqrtInvInertia1[0], angSqrtInvInertia0[2], angSqrtInvInertia1[2], m); + PxReal a11 = innerProduct(*row[1], *row[1], angSqrtInvInertia0[1], angSqrtInvInertia1[1], angSqrtInvInertia0[1], angSqrtInvInertia1[1], m); + PxReal a12 = innerProduct(*row[1], *row[2], angSqrtInvInertia0[1], angSqrtInvInertia1[1], angSqrtInvInertia0[2], angSqrtInvInertia1[2], m); + PxReal a22 = innerProduct(*row[2], *row[2], angSqrtInvInertia0[2], angSqrtInvInertia1[2], angSqrtInvInertia0[2], angSqrtInvInertia1[2], m); + + PxMat33 a(PxVec3(a00, a01, a02), + PxVec3(a01, a11, a12), + PxVec3(a02, a12, a22)); + + PxQuat q = diagonalize(a); + + PxMat33 n(-q); + + Mat33V mn(V3LoadU(n.column0), V3LoadU(n.column1), V3LoadU(n.column2)); + + //KS - We treat as a Vec4V so that we get geometricError rescaled for free along with linear0 + rescale4(mn, &row[0]->linear0.x, &row[1]->linear0.x, &row[2]->linear0.x); + rescale(mn, row[0]->linear1, row[1]->linear1, row[2]->linear1); + //KS - We treat as a PxVec4 so that we get velocityTarget rescaled for free + rescale4(mn, &row[0]->angular0.x, &row[1]->angular0.x, &row[2]->angular0.x); + rescale(mn, row[0]->angular1, row[1]->angular1, row[2]->angular1); + rescale4(mn, &angSqrtInvInertia0[0].x, &angSqrtInvInertia0[1].x, &angSqrtInvInertia0[2].x); + rescale4(mn, &angSqrtInvInertia1[0].x, &angSqrtInvInertia1[1].x, &angSqrtInvInertia1[2].x); + +} + +void orthogonalize(Px1DConstraint** row, + PxVec4* angSqrtInvInertia0, + PxVec4* angSqrtInvInertia1, + PxU32 rowCount, + PxU32 eqRowCount, + const MassProps &m) +{ + PX_ASSERT(eqRowCount<=6); + + const FloatV zero = FZero(); + + Vec3V lin1m[6], ang1m[6], lin1[6], ang1[6]; + Vec4V lin0m[6], ang0m[6]; // must have 0 in the W-field + Vec4V lin0AndG[6], ang0AndT[6]; + + for(PxU32 i=0;ilinear0.x); // linear0 and geometric error + Vec4V a0AndT = V4LoadA(&row[i]->angular0.x); // angular0 and velocity target + + Vec3V l1 = V3FromV4(V4LoadA(&row[i]->linear1.x)); + Vec3V a1 = V3FromV4(V4LoadA(&row[i]->angular1.x)); + + Vec4V angSqrtL0 = V4LoadA(&angSqrtInvInertia0[i].x); + Vec4V angSqrtL1 = V4LoadA(&angSqrtInvInertia1[i].x); + + PxU32 eliminationRows = PxMin(i, eqRowCount); + for(PxU32 j=0;jlinear0.x); + V4StoreA(a0AndT, &row[i]->angular0.x); + V3StoreA(l1, row[i]->linear1); + V3StoreA(a1, row[i]->angular1); + V4StoreA(angSqrtL0, &angSqrtInvInertia0[i].x); + V4StoreA(angSqrtL1, &angSqrtInvInertia1[i].x); + + if(i0 && r->solveHint < sorted[j-1]->solveHint; j--) + sorted[j] = sorted[j-1]; + + sorted[j] = r; + } + + for(PxU32 i=0;isolveHint <= sorted[i+1]->solveHint); + + for (PxU32 i = 0; iangular0)); + const Vec3V angDelta1 = M33MulV3(sqrtInvInertia1, V3LoadU(sorted[i]->angular1)); + V4StoreA(Vec4V_From_Vec3V(angDelta0), &angSqrtInvInertia0[i].x); + V4StoreA(Vec4V_From_Vec3V(angDelta1), &angSqrtInvInertia1[i].x); + } + + if(disablePreprocessing) + return; + + MassProps m(invMass0, invMass1, ims); + for(PxU32 i=0;isolveHint>>8), start = i++; + while(isolveHint>>8) == groupMajorId) + i++; + + if(groupMajorId == 4 || (groupMajorId == 8 && preprocessLinear)) + { + PxU32 bCount = start; // count of bilateral constraints + for(; bCountsolveHint&255)==0; bCount++) + ; + orthogonalize(sorted+start, angSqrtInvInertia0+start, angSqrtInvInertia1+start, i-start, bCount-start, m); + } + + if(groupMajorId == 1 && diagonalizeDrive) + { + PxU32 slerp = start; // count of bilateral constraints + for(; slerpsolveHint&255)!=2; slerp++) + ; + if(slerp+3 == i) + diagonalize(sorted+slerp, angSqrtInvInertia0+slerp, angSqrtInvInertia1+slerp, m); + + PX_ASSERT(i-start==3); + diagonalize(sorted+start, angSqrtInvInertia0+start, angSqrtInvInertia1+start, m); + } + } +} + + + + + +PxU32 ConstraintHelper::setupSolverConstraint( +PxSolverConstraintPrepDesc& prepDesc, +PxConstraintAllocator& allocator, +PxReal dt, PxReal invdt, +Cm::SpatialVectorF* Z) +{ + if (prepDesc.numRows == 0) + { + prepDesc.desc->constraint = NULL; + prepDesc.desc->writeBack = NULL; + prepDesc.desc->constraintLengthOver16 = 0; + prepDesc.desc->writeBackLengthOver4 = 0; + return 0; + } + + PxSolverConstraintDesc& desc = *prepDesc.desc; + + bool isExtended = desc.linkIndexA != PxSolverConstraintDesc::NO_LINK + || desc.linkIndexB != PxSolverConstraintDesc::NO_LINK; + + PxU32 stride = isExtended ? sizeof(SolverConstraint1DExt) : sizeof(SolverConstraint1D); + const PxU32 constraintLength = sizeof(SolverConstraint1DHeader) + stride * prepDesc.numRows; + + //KS - +16 is for the constraint progress counter, which needs to be the last element in the constraint (so that we + //know SPU DMAs have completed) + PxU8* ptr = allocator.reserveConstraintData(constraintLength + 16u); + if(NULL == ptr || (reinterpret_cast(-1))==ptr) + { + if(NULL==ptr) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept joints detaching/exploding or increase buffer size allocated for constraint prep by increasing PxSceneDesc::maxNbContactDataBlocks."); + return 0; + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of constraint data. " + "Either accept joints detaching/exploding or simplify constraints."); + ptr=NULL; + return 0; + } + } + desc.constraint = ptr; + + setConstraintLength(desc,constraintLength); + + desc.writeBack = prepDesc.writeback; + setWritebackLength(desc, sizeof(ConstraintWriteback)); + + memset(desc.constraint, 0, constraintLength); + + SolverConstraint1DHeader* header = reinterpret_cast(desc.constraint); + PxU8* constraints = desc.constraint + sizeof(SolverConstraint1DHeader); + init(*header, Ps::to8(prepDesc.numRows), isExtended, prepDesc.mInvMassScales); + header->body0WorldOffset = prepDesc.body0WorldOffset; + header->linBreakImpulse = prepDesc.linBreakForce * dt; + header->angBreakImpulse = prepDesc.angBreakForce * dt; + header->breakable = PxU8((prepDesc.linBreakForce != PX_MAX_F32) || (prepDesc.angBreakForce != PX_MAX_F32)); + header->invMass0D0 = prepDesc.data0->invMass * prepDesc.mInvMassScales.linear0; + header->invMass1D1 = prepDesc.data1->invMass * prepDesc.mInvMassScales.linear1; + + + PX_ALIGN(16, PxVec4) angSqrtInvInertia0[MAX_CONSTRAINT_ROWS]; + PX_ALIGN(16, PxVec4) angSqrtInvInertia1[MAX_CONSTRAINT_ROWS]; + + Px1DConstraint* sorted[MAX_CONSTRAINT_ROWS]; + + preprocessRows(sorted, prepDesc.rows, angSqrtInvInertia0, angSqrtInvInertia1, prepDesc.numRows, + prepDesc.data0->sqrtInvInertia, prepDesc.data1->sqrtInvInertia, prepDesc.data0->invMass, prepDesc.data1->invMass, + prepDesc.mInvMassScales, isExtended || prepDesc.disablePreprocessing, prepDesc.improvedSlerp, true); + + const PxReal erp = 1.0f; + for (PxU32 i = 0; i(constraints); + Px1DConstraint& c = *sorted[i]; + + PxReal driveScale = c.flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && prepDesc.driveLimitsAreForces ? PxMin(dt, 1.0f) : 1.0f; + + PxReal unitResponse; + PxReal normalVel = 0.0f; + PxReal initVel = 0.f; + + PxReal minResponseThreshold = prepDesc.minResponseThreshold; + + if(!isExtended) + { + init(s, c.linear0, c.linear1, PxVec3(angSqrtInvInertia0[i].x, angSqrtInvInertia0[i].y, angSqrtInvInertia0[i].z), + PxVec3(angSqrtInvInertia1[i].x, angSqrtInvInertia1[i].y, angSqrtInvInertia1[i].z), c.minImpulse * driveScale, c.maxImpulse * driveScale); + s.ang0Writeback = c.angular0; + PxReal resp0 = s.lin0.magnitudeSquared() * prepDesc.data0->invMass * prepDesc.mInvMassScales.linear0 + s.ang0.magnitudeSquared() * prepDesc.mInvMassScales.angular0; + PxReal resp1 = s.lin1.magnitudeSquared() * prepDesc.data1->invMass * prepDesc.mInvMassScales.linear1 + s.ang1.magnitudeSquared() * prepDesc.mInvMassScales.angular1; + unitResponse = resp0 + resp1; + initVel = normalVel = prepDesc.data0->projectVelocity(c.linear0, c.angular0) - prepDesc.data1->projectVelocity(c.linear1, c.angular1); + } + else + { + init(s, c.linear0, c.linear1, c.angular0, c.angular1, c.minImpulse * driveScale, c.maxImpulse * driveScale); + SolverConstraint1DExt& e = static_cast(s); + + const SolverExtBody eb0(reinterpret_cast(prepDesc.body0), prepDesc.data0, desc.linkIndexA); + const SolverExtBody eb1(reinterpret_cast(prepDesc.body1), prepDesc.data1, desc.linkIndexB); + + const Cm::SpatialVector resp0 = createImpulseResponseVector(e.lin0, e.ang0, eb0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-e.lin1, -e.ang1, eb1); + unitResponse = getImpulseResponse(eb0, resp0, unsimdRef(e.deltaVA), prepDesc.mInvMassScales.linear0, prepDesc.mInvMassScales.angular0, + eb1, resp1, unsimdRef(e.deltaVB), prepDesc.mInvMassScales.linear1, prepDesc.mInvMassScales.angular1, Z, false); + + if(!(c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT)) + { + PxReal totalMag = (unsimdRef(e.deltaVA) - unsimdRef(e.deltaVB)).magnitude(); + + PxReal ratio = unitResponse / totalMag; + if (ratio < 0.05f) + unitResponse = 0.f; + } + + + s.ang0Writeback = c.angular0; + s.lin0 = resp0.linear; + s.ang0 = resp0.angular; + s.lin1 = -resp1.linear; + s.ang1 = -resp1.angular; + PxReal vel0, vel1; + if(needsNormalVel(c) || eb0.mLinkIndex == PxSolverConstraintDesc::NO_LINK || eb1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + vel0 = eb0.projectVelocity(c.linear0, c.angular0); + vel1 = eb1.projectVelocity(c.linear1, c.angular1); + + normalVel = vel0 - vel1; + + //normalVel = eb0.projectVelocity(s.lin0, s.ang0) - eb1.projectVelocity(s.lin1, s.ang1); + if(eb0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + initVel = vel0; + else if(eb1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + initVel = -vel1; + + } + + minResponseThreshold = PxMax(minResponseThreshold, DY_ARTICULATION_MIN_RESPONSE); + } + + setSolverConstants(s.constant, s.unbiasedConstant, s.velMultiplier, s.impulseMultiplier, + c, normalVel, unitResponse, minResponseThreshold, erp, dt, invdt); + + //s.targetVelocity = initVel; + const PxReal velBias = initVel * s.velMultiplier; + s.constant += velBias; + s.unbiasedConstant += velBias; + + if(c.flags & Px1DConstraintFlag::eOUTPUT_FORCE) + s.flags |= DY_SC_FLAG_OUTPUT_FORCE; + + constraints += stride; + } + + //KS - Set the solve count at the end to 0 + *(reinterpret_cast(constraints)) = 0; + *(reinterpret_cast(constraints + 4)) = 0; + PX_ASSERT(desc.constraint + getConstraintLength(desc) == constraints); + return prepDesc.numRows; +} + +PxU32 SetupSolverConstraint(SolverConstraintShaderPrepDesc& shaderDesc, + PxSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + PxReal dt, PxReal invdt, Cm::SpatialVectorF* Z) +{ + // LL shouldn't see broken constraints + + PX_ASSERT(!(reinterpret_cast(prepDesc.writeback)->broken)); + + setConstraintLength(*prepDesc.desc, 0); + + if (!shaderDesc.solverPrep) + return 0; + + //PxU32 numAxisConstraints = 0; + + Px1DConstraint rows[MAX_CONSTRAINT_ROWS]; + + // This is necessary so that there will be sensible defaults and shaders will + // continue to work (albeit with a recompile) if the row format changes. + // It's a bit inefficient because it fills in all constraint rows even if there + // is only going to be one generated. A way around this would be for the shader to + // specify the maximum number of rows it needs, or it could call a subroutine to + // prep the row before it starts filling it it. + + PxMemZero(rows, sizeof(Px1DConstraint)*MAX_CONSTRAINT_ROWS); + + for (PxU32 i = 0; isqrtInvInertia, desc.data1->sqrtInvInertia, desc.data0->invMass, desc.data1->invMass, + desc.mInvMassScales, desc.disablePreprocessing, desc.improvedSlerp); + + numRows += desc.numRows; + } + + + PxU32 stride = sizeof(SolverConstraint1DDynamic4); + + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeader4) + stride * maxRows; + + //KS - +16 is for the constraint progress counter, which needs to be the last element in the constraint (so that we + //know SPU DMAs have completed) + PxU8* ptr = allocator.reserveConstraintData(constraintLength + 16u); + if(NULL == ptr || (reinterpret_cast(-1))==ptr) + { + for(PxU32 a = 0; a < 4; ++a) + { + PxSolverConstraintPrepDesc& desc = constraintDescs[a]; + desc.desc->constraint = NULL; + setConstraintLength(*desc.desc, 0); + desc.desc->writeBack = desc.writeback; + } + + if(NULL==ptr) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept joints detaching/exploding or increase buffer size allocated for constraint prep by increasing PxSceneDesc::maxNbContactDataBlocks."); + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of constraint data. " + "Either accept joints detaching/exploding or simplify constraints."); + ptr=NULL; + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + } + //desc.constraint = ptr; + + totalRows = numRows; + + for(PxU32 a = 0; a < 4; ++a) + { + PxSolverConstraintPrepDesc& desc = constraintDescs[a]; + desc.desc->constraint = ptr; + setConstraintLength(*desc.desc, constraintLength); + desc.desc->writeBack = desc.writeback; + } + + const PxReal erp[4] = { 1.0f, 1.0f, 1.0f, 1.0f}; + //OK, now we build all 4 constraints into a single set of rows + + { + PxU8* currPtr = ptr; + SolverConstraint1DHeader4* header = reinterpret_cast(currPtr); + currPtr += sizeof(SolverConstraint1DHeader4); + + const PxSolverBodyData& bd00 = *constraintDescs[0].data0; + const PxSolverBodyData& bd01 = *constraintDescs[1].data0; + const PxSolverBodyData& bd02 = *constraintDescs[2].data0; + const PxSolverBodyData& bd03 = *constraintDescs[3].data0; + + const PxSolverBodyData& bd10 = *constraintDescs[0].data1; + const PxSolverBodyData& bd11 = *constraintDescs[1].data1; + const PxSolverBodyData& bd12 = *constraintDescs[2].data1; + const PxSolverBodyData& bd13 = *constraintDescs[3].data1; + + //Load up masses, invInertia, velocity etc. + + const Vec4V invMassScale0 = V4LoadXYZW(constraintDescs[0].mInvMassScales.linear0, constraintDescs[1].mInvMassScales.linear0, + constraintDescs[2].mInvMassScales.linear0, constraintDescs[3].mInvMassScales.linear0); + const Vec4V invMassScale1 = V4LoadXYZW(constraintDescs[0].mInvMassScales.linear1, constraintDescs[1].mInvMassScales.linear1, + constraintDescs[2].mInvMassScales.linear1, constraintDescs[3].mInvMassScales.linear1); + + + const Vec4V iMass0 = V4LoadXYZW(bd00.invMass, bd01.invMass, bd02.invMass, bd03.invMass); + + const Vec4V iMass1 = V4LoadXYZW(bd10.invMass, bd11.invMass, bd12.invMass, bd13.invMass); + + const Vec4V invMass0 = V4Mul(iMass0, invMassScale0); + const Vec4V invMass1 = V4Mul(iMass1, invMassScale1); + + + const Vec4V invInertiaScale0 = V4LoadXYZW(constraintDescs[0].mInvMassScales.angular0, constraintDescs[1].mInvMassScales.angular0, + constraintDescs[2].mInvMassScales.angular0, constraintDescs[3].mInvMassScales.angular0); + const Vec4V invInertiaScale1 = V4LoadXYZW(constraintDescs[0].mInvMassScales.angular1, constraintDescs[1].mInvMassScales.angular1, + constraintDescs[2].mInvMassScales.angular1, constraintDescs[3].mInvMassScales.angular1); + + //Velocities + Vec4V linVel00 = V4LoadA(&bd00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&bd10.linearVelocity.x); + Vec4V angVel00 = V4LoadA(&bd00.angularVelocity.x); + Vec4V angVel01 = V4LoadA(&bd10.angularVelocity.x); + + Vec4V linVel10 = V4LoadA(&bd01.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&bd11.linearVelocity.x); + Vec4V angVel10 = V4LoadA(&bd01.angularVelocity.x); + Vec4V angVel11 = V4LoadA(&bd11.angularVelocity.x); + + Vec4V linVel20 = V4LoadA(&bd02.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&bd12.linearVelocity.x); + Vec4V angVel20 = V4LoadA(&bd02.angularVelocity.x); + Vec4V angVel21 = V4LoadA(&bd12.angularVelocity.x); + + Vec4V linVel30 = V4LoadA(&bd03.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&bd13.linearVelocity.x); + Vec4V angVel30 = V4LoadA(&bd03.angularVelocity.x); + Vec4V angVel31 = V4LoadA(&bd13.angularVelocity.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2; + Vec4V linVel1T0, linVel1T1, linVel1T2; + Vec4V angVel0T0, angVel0T1, angVel0T2; + Vec4V angVel1T0, angVel1T1, angVel1T2; + + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2); + PX_TRANSPOSE_44_34(angVel00, angVel10, angVel20, angVel30, angVel0T0, angVel0T1, angVel0T2); + PX_TRANSPOSE_44_34(angVel01, angVel11, angVel21, angVel31, angVel1T0, angVel1T1, angVel1T2); + + + + //body world offsets + Vec4V workOffset0 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[0].body0WorldOffset)); + Vec4V workOffset1 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[1].body0WorldOffset)); + Vec4V workOffset2 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[2].body0WorldOffset)); + Vec4V workOffset3 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[3].body0WorldOffset)); + + Vec4V workOffsetX, workOffsetY, workOffsetZ; + + PX_TRANSPOSE_44_34(workOffset0, workOffset1, workOffset2, workOffset3, workOffsetX, workOffsetY, workOffsetZ); + + const FloatV dtV = FLoad(dt); + Vec4V linBreakForce = V4LoadXYZW(constraintDescs[0].linBreakForce, constraintDescs[1].linBreakForce, + constraintDescs[2].linBreakForce, constraintDescs[3].linBreakForce); + Vec4V angBreakForce = V4LoadXYZW(constraintDescs[0].angBreakForce, constraintDescs[1].angBreakForce, + constraintDescs[2].angBreakForce, constraintDescs[3].angBreakForce); + + + header->break0 = PxU8((constraintDescs[0].linBreakForce != PX_MAX_F32) || (constraintDescs[0].angBreakForce != PX_MAX_F32)); + header->break1 = PxU8((constraintDescs[1].linBreakForce != PX_MAX_F32) || (constraintDescs[1].angBreakForce != PX_MAX_F32)); + header->break2 = PxU8((constraintDescs[2].linBreakForce != PX_MAX_F32) || (constraintDescs[2].angBreakForce != PX_MAX_F32)); + header->break3 = PxU8((constraintDescs[3].linBreakForce != PX_MAX_F32) || (constraintDescs[3].angBreakForce != PX_MAX_F32)); + + + //OK, I think that's everything loaded in + + header->invMass0D0 = invMass0; + header->invMass1D1 = invMass1; + header->angD0 = invInertiaScale0; + header->angD1 = invInertiaScale1; + header->body0WorkOffsetX = workOffsetX; + header->body0WorkOffsetY = workOffsetY; + header->body0WorkOffsetZ = workOffsetZ; + + header->count = maxRows; + header->type = DY_SC_TYPE_BLOCK_1D; + header->linBreakImpulse = V4Scale(linBreakForce, dtV); + header->angBreakImpulse = V4Scale(angBreakForce, dtV); + header->count0 = Ps::to8(constraintDescs[0].numRows); + header->count1 = Ps::to8(constraintDescs[1].numRows); + header->count2 = Ps::to8(constraintDescs[2].numRows); + header->count3 = Ps::to8(constraintDescs[3].numRows); + + //Now we loop over the constraints and build the results... + + PxU32 index0 = 0; + PxU32 endIndex0 = constraintDescs[0].numRows - 1; + PxU32 index1 = startIndex[1]; + PxU32 endIndex1 = index1 + constraintDescs[1].numRows - 1; + PxU32 index2 = startIndex[2]; + PxU32 endIndex2 = index2 + constraintDescs[2].numRows - 1; + PxU32 index3 = startIndex[3]; + PxU32 endIndex3 = index3 + constraintDescs[3].numRows - 1; + + const FloatV one = FOne(); + + for(PxU32 a = 0; a < maxRows; ++a) + { + SolverConstraint1DDynamic4* c = reinterpret_cast(currPtr); + currPtr += stride; + + Px1DConstraint* con0 = allSorted[index0]; + Px1DConstraint* con1 = allSorted[index1]; + Px1DConstraint* con2 = allSorted[index2]; + Px1DConstraint* con3 = allSorted[index3]; + + Vec4V cangDelta00 = V4LoadA(&angSqrtInvInertia0[index0].x); + Vec4V cangDelta01 = V4LoadA(&angSqrtInvInertia0[index1].x); + Vec4V cangDelta02 = V4LoadA(&angSqrtInvInertia0[index2].x); + Vec4V cangDelta03 = V4LoadA(&angSqrtInvInertia0[index3].x); + + Vec4V cangDelta10 = V4LoadA(&angSqrtInvInertia1[index0].x); + Vec4V cangDelta11 = V4LoadA(&angSqrtInvInertia1[index1].x); + Vec4V cangDelta12 = V4LoadA(&angSqrtInvInertia1[index2].x); + Vec4V cangDelta13 = V4LoadA(&angSqrtInvInertia1[index3].x); + + index0 = index0 == endIndex0 ? index0 : index0 + 1; + index1 = index1 == endIndex1 ? index1 : index1 + 1; + index2 = index2 == endIndex2 ? index2 : index2 + 1; + index3 = index3 == endIndex3 ? index3 : index3 + 1; + + Vec4V driveScale = V4Splat(one); + if (con0->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[0].driveLimitsAreForces) + driveScale = V4SetX(driveScale, FMin(one, dtV)); + if (con1->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[1].driveLimitsAreForces) + driveScale = V4SetY(driveScale, FMin(one, dtV)); + if (con2->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[2].driveLimitsAreForces) + driveScale = V4SetZ(driveScale, FMin(one, dtV)); + if (con3->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[3].driveLimitsAreForces) + driveScale = V4SetW(driveScale, FMin(one, dtV)); + + + Vec4V clin00 = V4LoadA(&con0->linear0.x); + Vec4V clin01 = V4LoadA(&con1->linear0.x); + Vec4V clin02 = V4LoadA(&con2->linear0.x); + Vec4V clin03 = V4LoadA(&con3->linear0.x); + + Vec4V cang00 = V4LoadA(&con0->angular0.x); + Vec4V cang01 = V4LoadA(&con1->angular0.x); + Vec4V cang02 = V4LoadA(&con2->angular0.x); + Vec4V cang03 = V4LoadA(&con3->angular0.x); + + Vec4V clin0X, clin0Y, clin0Z; + Vec4V cang0X, cang0Y, cang0Z; + + PX_TRANSPOSE_44_34(clin00, clin01, clin02, clin03, clin0X, clin0Y, clin0Z); + PX_TRANSPOSE_44_34(cang00, cang01, cang02, cang03, cang0X, cang0Y, cang0Z); + + const Vec4V maxImpulse = V4LoadXYZW(con0->maxImpulse, con1->maxImpulse, con2->maxImpulse, con3->maxImpulse); + const Vec4V minImpulse = V4LoadXYZW(con0->minImpulse, con1->minImpulse, con2->minImpulse, con3->minImpulse); + + Vec4V angDelta0X, angDelta0Y, angDelta0Z; + + PX_TRANSPOSE_44_34(cangDelta00, cangDelta01, cangDelta02, cangDelta03, angDelta0X, angDelta0Y, angDelta0Z); + + c->flags[0] = 0; + c->flags[1] = 0; + c->flags[2] = 0; + c->flags[3] = 0; + + c->lin0X = clin0X; + c->lin0Y = clin0Y; + c->lin0Z = clin0Z; + c->ang0X = angDelta0X; + c->ang0Y = angDelta0Y; + c->ang0Z = angDelta0Z; + c->ang0WritebackX = cang0X; + c->ang0WritebackY = cang0Y; + c->ang0WritebackZ = cang0Z; + + c->minImpulse = V4Mul(minImpulse, driveScale); + c->maxImpulse = V4Mul(maxImpulse, driveScale); + c->appliedForce = zero; + + const Vec4V lin0MagSq = V4MulAdd(clin0Z, clin0Z, V4MulAdd(clin0Y, clin0Y, V4Mul(clin0X, clin0X))); + const Vec4V cang0DotAngDelta = V4MulAdd(angDelta0Z, angDelta0Z, V4MulAdd(angDelta0Y, angDelta0Y, V4Mul(angDelta0X, angDelta0X))); + c->flags[0] = 0; + c->flags[1] = 0; + c->flags[2] = 0; + c->flags[3] = 0; + + Vec4V unitResponse = V4MulAdd(lin0MagSq, invMass0, V4Mul(cang0DotAngDelta, invInertiaScale0)); + + Vec4V clin10 = V4LoadA(&con0->linear1.x); + Vec4V clin11 = V4LoadA(&con1->linear1.x); + Vec4V clin12 = V4LoadA(&con2->linear1.x); + Vec4V clin13 = V4LoadA(&con3->linear1.x); + + Vec4V cang10 = V4LoadA(&con0->angular1.x); + Vec4V cang11 = V4LoadA(&con1->angular1.x); + Vec4V cang12 = V4LoadA(&con2->angular1.x); + Vec4V cang13 = V4LoadA(&con3->angular1.x); + + Vec4V clin1X, clin1Y, clin1Z; + Vec4V cang1X, cang1Y, cang1Z; + PX_TRANSPOSE_44_34(clin10, clin11, clin12, clin13, clin1X, clin1Y, clin1Z); + PX_TRANSPOSE_44_34(cang10, cang11, cang12, cang13, cang1X, cang1Y, cang1Z); + + Vec4V angDelta1X, angDelta1Y, angDelta1Z; + + PX_TRANSPOSE_44_34(cangDelta10, cangDelta11, cangDelta12, cangDelta13, angDelta1X, angDelta1Y, angDelta1Z); + + const Vec4V lin1MagSq = V4MulAdd(clin1Z, clin1Z, V4MulAdd(clin1Y, clin1Y, V4Mul(clin1X, clin1X))); + const Vec4V cang1DotAngDelta = V4MulAdd(angDelta1Z, angDelta1Z, V4MulAdd(angDelta1Y, angDelta1Y, V4Mul(angDelta1X, angDelta1X))); + + c->lin1X = clin1X; + c->lin1Y = clin1Y; + c->lin1Z = clin1Z; + + c->ang1X = angDelta1X; + c->ang1Y = angDelta1Y; + c->ang1Z = angDelta1Z; + + unitResponse = V4Add(unitResponse, V4MulAdd(lin1MagSq, invMass1, V4Mul(cang1DotAngDelta, invInertiaScale1))); + + Vec4V linProj0(V4Mul(clin0X, linVel0T0)); + Vec4V linProj1(V4Mul(clin1X, linVel1T0)); + Vec4V angProj0(V4Mul(cang0X, angVel0T0)); + Vec4V angProj1(V4Mul(cang1X, angVel1T0)); + + linProj0 = V4MulAdd(clin0Y, linVel0T1, linProj0); + linProj1 = V4MulAdd(clin1Y, linVel1T1, linProj1); + angProj0 = V4MulAdd(cang0Y, angVel0T1, angProj0); + angProj1 = V4MulAdd(cang1Y, angVel1T1, angProj1); + + linProj0 = V4MulAdd(clin0Z, linVel0T2, linProj0); + linProj1 = V4MulAdd(clin1Z, linVel1T2, linProj1); + angProj0 = V4MulAdd(cang0Z, angVel0T2, angProj0); + angProj1 = V4MulAdd(cang1Z, angVel1T2, angProj1); + + const Vec4V projectVel0 = V4Add(linProj0, angProj0); + const Vec4V projectVel1 = V4Add(linProj1, angProj1); + + const Vec4V normalVel = V4Sub(projectVel0, projectVel1); + + + { + const PxVec4& ur = reinterpret_cast(unitResponse); + PxVec4& cConstant = reinterpret_cast(c->constant); + PxVec4& cUnbiasedConstant = reinterpret_cast(c->unbiasedConstant); + PxVec4& cVelMultiplier = reinterpret_cast(c->velMultiplier); + PxVec4& cImpulseMultiplier = reinterpret_cast(c->impulseMultiplier); + + setConstants(cConstant.x, cUnbiasedConstant.x, cVelMultiplier.x, cImpulseMultiplier.x, + *con0, ur.x, constraintDescs[0].minResponseThreshold, erp[0], dt, recipdt, + *constraintDescs[0].data0, *constraintDescs[0].data1, a >= constraintDescs[0].numRows); + + setConstants(cConstant.y, cUnbiasedConstant.y, cVelMultiplier.y, cImpulseMultiplier.y, + *con1, ur.y, constraintDescs[1].minResponseThreshold, erp[1], dt, recipdt, + *constraintDescs[1].data0, *constraintDescs[1].data1, a >= constraintDescs[1].numRows); + + setConstants(cConstant.z, cUnbiasedConstant.z, cVelMultiplier.z, cImpulseMultiplier.z, + *con2, ur.z, constraintDescs[2].minResponseThreshold, erp[2], dt, recipdt, + *constraintDescs[2].data0, *constraintDescs[2].data1, a >= constraintDescs[2].numRows); + + setConstants(cConstant.w, cUnbiasedConstant.w, cVelMultiplier.w, cImpulseMultiplier.w, + *con3, ur.w, constraintDescs[3].minResponseThreshold, erp[3], dt, recipdt, + *constraintDescs[3].data0, *constraintDescs[3].data1, a >= constraintDescs[3].numRows); + } + + const Vec4V velBias = V4Mul(c->velMultiplier, normalVel); + c->constant = V4Add(c->constant, velBias); + c->unbiasedConstant = V4Add(c->unbiasedConstant, velBias); + + if(con0->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[0] |= DY_SC_FLAG_OUTPUT_FORCE; + if(con1->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[1] |= DY_SC_FLAG_OUTPUT_FORCE; + if(con2->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[2] |= DY_SC_FLAG_OUTPUT_FORCE; + if(con3->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[3] |= DY_SC_FLAG_OUTPUT_FORCE; + } + *(reinterpret_cast(currPtr)) = 0; + *(reinterpret_cast(currPtr + 4)) = 0; + } + + //OK, we're ready to allocate and solve prep these constraints now :-) + return SolverConstraintPrepState::eSUCCESS; +} + +} + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp new file mode 100644 index 000000000..0b02e9eed --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp @@ -0,0 +1,820 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "PxSceneDesc.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContact4.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DyDynamics.h" +#include "DyArticulationContactPrep.h" +#include "PxsContactManager.h" +#include "PsFoundation.h" +#include "DyConstraintPrep.h" + +using namespace physx; +using namespace Gu; + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DyContactPrepShared.h" + +using namespace Ps::aos; + +namespace physx +{ +namespace Dy +{ + +PxcCreateFinalizeSolverContactMethod createFinalizeMethods[3] = +{ + createFinalizeSolverContacts, + createFinalizeSolverContactsCoulomb1D, + createFinalizeSolverContactsCoulomb2D +}; + + + +static void setupFinalizeSolverConstraints(Sc::ShapeInteraction* shapeInteraction, + const ContactPoint* buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const PxSolverBodyData& data0, + const PxSolverBodyData& data1, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + bool hasForceThreshold, bool staticOrKinematicBody, + const PxReal restDist, PxU8* frictionDataPtr, + const PxReal maxCCDSeparation, + const PxReal solverOffsetSlopF32) +{ + // NOTE II: the friction patches are sparse (some of them have no contact patches, and + // therefore did not get written back to the cache) but the patch addresses are dense, + // corresponding to valid patches + + const Vec3V solverOffsetSlop = V3Load(solverOffsetSlopF32); + + const FloatV ccdMaxSeparation = FLoad(maxCCDSeparation); + + PxU8 flags = PxU8(hasForceThreshold ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0); + + PxU8* PX_RESTRICT ptr = workspace; + + PxU8 type = Ps::to8(staticOrKinematicBody ? DY_SC_TYPE_STATIC_CONTACT + : DY_SC_TYPE_RB_CONTACT); + + const FloatV zero=FZero(); + const Vec3V v3Zero = V3Zero(); + + const FloatV d0 = FLoad(invMassScale0); + const FloatV d1 = FLoad(invMassScale1); + const FloatV angD0 = FLoad(invInertiaScale0); + const FloatV angD1 = FLoad(invInertiaScale1); + + const FloatV nDom1fV = FNeg(d1); + + const FloatV invMass0 = FLoad(data0.invMass); + const FloatV invMass1 = FLoad(data1.invMass); + + const FloatV invMass0_dom0fV = FMul(d0, invMass0); + const FloatV invMass1_dom1fV = FMul(nDom1fV, invMass1); + + + Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4Zero(); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, invMass0_dom0fV); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, invMass1_dom1fV); + + const FloatV restDistance = FLoad(restDist); + + const FloatV maxPenBias = FMax(FLoad(data0.penBiasClamp), FLoad(data1.penBiasClamp)); + + const QuatV bodyFrame0q = QuatVLoadU(&bodyFrame0.q.x); + const Vec3V bodyFrame0p = V3LoadU(bodyFrame0.p); + + const QuatV bodyFrame1q = QuatVLoadU(&bodyFrame1.q.x); + const Vec3V bodyFrame1p = V3LoadU(bodyFrame1.p); + + PxU32 frictionPatchWritebackAddrIndex = 0; + PxU32 contactWritebackCount = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + const Vec3V linVel0 = V3LoadU_SafeReadW(data0.linearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + const Vec3V linVel1 = V3LoadU_SafeReadW(data1.linearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + const Vec3V angVel0 = V3LoadU_SafeReadW(data0.angularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + const Vec3V angVel1 = V3LoadU_SafeReadW(data1.angularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + + PX_ALIGN(16, const Mat33V invSqrtInertia0) + ( + V3LoadU_SafeReadW(data0.sqrtInvInertia.column0), // PT: safe because 'column1' follows 'column0' in PxMat33 + V3LoadU_SafeReadW(data0.sqrtInvInertia.column1), // PT: safe because 'column2' follows 'column1' in PxMat33 + V3LoadU(data0.sqrtInvInertia.column2) + ); + + PX_ALIGN(16, const Mat33V invSqrtInertia1) + ( + V3LoadU_SafeReadW(data1.sqrtInvInertia.column0), // PT: safe because 'column1' follows 'column0' in PxMat33 + V3LoadU_SafeReadW(data1.sqrtInvInertia.column1), // PT: safe because 'column2' follows 'column1' in PxMat33 + V3LoadU(data1.sqrtInvInertia.column2) + ); + + const FloatV invDt = FLoad(invDtF32); + const FloatV p8 = FLoad(0.8f); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + + const FloatV invDtp8 = FMul(invDt, p8); + + + for(PxU32 i=0;irestitution; + + SolverContactHeader* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeader); + + + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + + header->shapeInteraction = shapeInteraction; + header->flags = flags; + FStore(invMass0_dom0fV, &header->invMass0); + FStore(FNeg(invMass1_dom1fV), &header->invMass1); + const FloatV restitution = FLoad(combinedRestitution); + + PxU32 pointStride = sizeof(SolverContactPoint); + PxU32 frictionStride = sizeof(SolverContactFriction); + + const Vec3V normal = V3LoadA(buffer[c.contactPatches[c.correlationListHeads[i]].start].normal); + const FloatV normalLenSq = V3LengthSq(normal); + const VecCrossV norCross = V3PrepareCross(normal); + const FloatV norVel = V3SumElems(V3NegMulSub(normal, linVel1, V3Mul(normal, linVel0))); + + const FloatV invMassNorLenSq0 = FMul(invMass0_dom0fV, normalLenSq); + const FloatV invMassNorLenSq1 = FMul(invMass1_dom1fV, normalLenSq); + + header->normal_minAppliedImpulseForFrictionW = Vec4V_From_Vec3V(normal); + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer + c.contactPatches[patch].start; + + PxU8* p = ptr; + + for(PxU32 j=0;j(p); + p += pointStride; + + constructContactConstraint(invSqrtInertia0, invSqrtInertia1, invMassNorLenSq0, + invMassNorLenSq1, angD0, angD1, bodyFrame0p, bodyFrame1p, + normal, norVel, norCross, angVel0, angVel1, + invDt, invDtp8, restDistance, maxPenBias, restitution, + bounceThreshold, contact, *solverContact, + ccdMaxSeparation, solverOffsetSlop); + } + + ptr = p; + } + contactWritebackCount += contactCount; + + PxF32* forceBuffers = reinterpret_cast(ptr); + PxMemZero(forceBuffers, sizeof(PxF32) * contactCount); + ptr += ((contactCount + 3) & (~3)) * sizeof(PxF32); // jump to next 16-byte boundary + + const PxReal staticFriction = contactBase0->staticFriction; + const PxReal dynamicFriction = contactBase0->dynamicFriction; + const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); + + const bool haveFriction = (disableStrongFriction == 0 && frictionPatch.anchorCount != 0) ;//PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0; + header->numNormalConstr = Ps::to8(contactCount); + header->numFrictionConstr = Ps::to8(haveFriction ? frictionPatch.anchorCount*2 : 0); + + header->type = type; + + header->staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; + FStore(angD0, &header->angDom0); + FStore(angD1, &header->angDom1); + + header->broken = 0; + + if(haveFriction) + { + const Vec3V linVrel = V3Sub(linVel0, linVel1); + //const Vec3V normal = Vec3V_From_PxVec3_Aligned(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); + + const FloatV orthoThreshold = FLoad(0.70710678f); + const FloatV p1 = FLoad(0.1f); + // fallback: normal.cross((1,0,0)) or normal.cross((0,0,1)) + const FloatV normalX = V3GetX(normal); + const FloatV normalY = V3GetY(normal); + const FloatV normalZ = V3GetZ(normal); + + Vec3V t0Fallback1 = V3Merge(zero, FNeg(normalZ), normalY); + Vec3V t0Fallback2 = V3Merge(FNeg(normalY), normalX, zero) ; + Vec3V t0Fallback = V3Sel(FIsGrtr(orthoThreshold, FAbs(normalX)), t0Fallback1, t0Fallback2); + + Vec3V t0 = V3Sub(linVrel, V3Scale(normal, V3Dot(normal, linVrel))); + t0 = V3Sel(FIsGrtr(V3LengthSq(t0), p1), t0, t0Fallback); + t0 = V3Normalize(t0); + + const VecCrossV t0Cross = V3PrepareCross(t0); + + const Vec3V t1 = V3Cross(norCross, t0Cross); + const VecCrossV t1Cross = V3PrepareCross(t1); + + + // since we don't even have the body velocities we can't compute the tangent dirs, so + // the only thing we can do right now is to write the geometric information (which is the + // same for both axis constraints of an anchor) We put ra in the raXn field, rb in the rbXn + // field, and the error in the normal field. See corresponding comments in + // completeContactFriction() + + //We want to set the writeBack ptr to point to the broken flag of the friction patch. + //On spu we have a slight problem here because the friction patch array is + //in local store rather than in main memory. The good news is that the address of the friction + //patch array in main memory is stored in the work unit. These two addresses will be equal + //except on spu where one is local store memory and the other is the effective address in main memory. + //Using the value stored in the work unit guarantees that the main memory address is used on all platforms. + PxU8* PX_RESTRICT writeback = frictionDataPtr + frictionPatchWritebackAddrIndex*sizeof(FrictionPatch); + + header->frictionBrokenWritebackByte = writeback; + + for(PxU32 j = 0; j < frictionPatch.anchorCount; j++) + { + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + SolverContactFriction* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionStride; + SolverContactFriction* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionStride; + + Vec3V body0Anchor = V3LoadU(frictionPatch.body0Anchors[j]); + Vec3V body1Anchor = V3LoadU(frictionPatch.body1Anchors[j]); + + const Vec3V ra = QuatRotate(bodyFrame0q, body0Anchor); + const Vec3V rb = QuatRotate(bodyFrame1q, body1Anchor); + + /*ra = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(ra)), v3Zero, ra); + rb = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rb)), v3Zero, rb);*/ + + PxU32 index = c.contactID[i][j]; + Vec3V error = V3Sub(V3Add(ra, bodyFrame0p), V3Add(rb, bodyFrame1p)); + error = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(error)), v3Zero, error); + + index = index == 0xFFFF ? c.contactPatches[c.correlationListHeads[i]].start : index; + + const Vec3V tvel = V3LoadA(buffer[index].targetVel); + + { + Vec3V raXn = V3Cross(ra, t0Cross); + Vec3V rbXn = V3Cross(rb, t0Cross); + + raXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(raXn)), V3Zero(), raXn); + rbXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rbXn)), V3Zero(), rbXn); + + const Vec3V raXnSqrtInertia = M33MulV3(invSqrtInertia0, raXn); + const Vec3V rbXnSqrtInertia = M33MulV3(invSqrtInertia1, rbXn); + + + const FloatV resp0 = FAdd(invMass0_dom0fV, FMul(angD0, V3Dot(raXnSqrtInertia, raXnSqrtInertia))); + const FloatV resp1 = FSub(FMul(angD1, V3Dot(rbXnSqrtInertia, rbXnSqrtInertia)), invMass1_dom1fV); + const FloatV resp = FAdd(resp0, resp1); + + const FloatV velMultiplier = FSel(FIsGrtr(resp, zero), FDiv(p8, resp), zero); + + FloatV targetVel = V3Dot(tvel, t0); + + const FloatV vrel1 = FAdd(V3Dot(t0, linVel0), V3Dot(raXn, angVel0)); + const FloatV vrel2 = FAdd(V3Dot(t0, linVel1), V3Dot(rbXn, angVel1)); + const FloatV vrel = FSub(vrel1, vrel2); + + targetVel = FSub(targetVel, vrel); + + f0->normalXYZ_appliedForceW = V4SetW(t0, zero); + f0->raXnXYZ_velMultiplierW = V4SetW(raXnSqrtInertia, velMultiplier); + f0->rbXnXYZ_biasW = V4SetW(rbXnSqrtInertia, FMul(V3Dot(t0, error), invDt)); + FStore(targetVel, &f0->targetVel); + } + + { + + Vec3V raXn = V3Cross(ra, t1Cross); + Vec3V rbXn = V3Cross(rb, t1Cross); + + raXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(raXn)), V3Zero(), raXn); + rbXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rbXn)), V3Zero(), rbXn); + + const Vec3V raXnSqrtInertia = M33MulV3(invSqrtInertia0, raXn); + const Vec3V rbXnSqrtInertia = M33MulV3(invSqrtInertia1, rbXn); + + const FloatV resp0 = FAdd(invMass0_dom0fV, FMul(angD0, V3Dot(raXnSqrtInertia, raXnSqrtInertia))); + const FloatV resp1 = FSub(FMul(angD1, V3Dot(rbXnSqrtInertia, rbXnSqrtInertia)), invMass1_dom1fV); + const FloatV resp = FAdd(resp0, resp1); + + const FloatV velMultiplier = FSel(FIsGrtr(resp, zero), FDiv(p8, resp), zero); + + FloatV targetVel = V3Dot(tvel, t1); + + const FloatV vrel1 = FAdd(V3Dot(t1, linVel0), V3Dot(raXn, angVel0)); + const FloatV vrel2 = FAdd(V3Dot(t1, linVel1), V3Dot(rbXn, angVel1)); + const FloatV vrel = FSub(vrel1, vrel2); + + targetVel = FSub(targetVel, vrel); + + f1->normalXYZ_appliedForceW = V4SetW(t1, zero); + f1->raXnXYZ_velMultiplierW = V4SetW(raXnSqrtInertia, velMultiplier); + f1->rbXnXYZ_biasW = V4SetW(rbXnSqrtInertia, FMul(V3Dot(t1, error), invDt)); + FStore(targetVel, &f1->targetVel); + } + } + } + + frictionPatchWritebackAddrIndex++; + } +} + + +PX_FORCE_INLINE void computeBlockStreamByteSizes(const bool useExtContacts, const CorrelationBuffer& c, + PxU32& _solverConstraintByteSize, PxU32& _frictionPatchByteSize, PxU32& _numFrictionPatches, + PxU32& _axisConstraintCount) +{ + PX_ASSERT(0 == _solverConstraintByteSize); + PX_ASSERT(0 == _frictionPatchByteSize); + PX_ASSERT(0 == _numFrictionPatches); + PX_ASSERT(0 == _axisConstraintCount); + + // PT: use local vars to remove LHS + PxU32 solverConstraintByteSize = 0; + PxU32 numFrictionPatches = 0; + PxU32 axisConstraintCount = 0; + + + for(PxU32 i = 0; i < c.frictionPatchCount; i++) + { + //Friction patches. + if(c.correlationListHeads[i] != CorrelationBuffer::LIST_END) + numFrictionPatches++; + + const FrictionPatch& frictionPatch = c.frictionPatches[i]; + + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0; + + //Solver constraint data. + if(c.frictionPatchContactCounts[i]!=0) + { + solverConstraintByteSize += sizeof(SolverContactHeader); + solverConstraintByteSize += useExtContacts ? c.frictionPatchContactCounts[i] * sizeof(SolverContactPointExt) + : c.frictionPatchContactCounts[i] * sizeof(SolverContactPoint); + solverConstraintByteSize += sizeof(PxF32) * ((c.frictionPatchContactCounts[i] + 3)&(~3)); //Add on space for applied impulses + + axisConstraintCount += c.frictionPatchContactCounts[i]; + + if(haveFriction) + { + solverConstraintByteSize += useExtContacts ? c.frictionPatches[i].anchorCount * 2 * sizeof(SolverContactFrictionExt) + : c.frictionPatches[i].anchorCount * 2 * sizeof(SolverContactFriction); + axisConstraintCount += c.frictionPatches[i].anchorCount * 2; + + } + } + } + PxU32 frictionPatchByteSize = numFrictionPatches*sizeof(FrictionPatch); + + _numFrictionPatches = numFrictionPatches; + _axisConstraintCount = axisConstraintCount; + + //16-byte alignment. + _frictionPatchByteSize = ((frictionPatchByteSize + 0x0f) & ~0x0f); + _solverConstraintByteSize = ((solverConstraintByteSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); + PX_ASSERT(0 == (_frictionPatchByteSize & 0x0f)); +} + +static bool reserveBlockStreams(const bool useExtContacts, Dy::CorrelationBuffer& cBuffer, + PxU8*& solverConstraint, + FrictionPatch*& _frictionPatches, + PxU32& numFrictionPatches, PxU32& solverConstraintByteSize, + PxU32& axisConstraintCount, PxConstraintAllocator& constraintAllocator) +{ + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(NULL == _frictionPatches); + PX_ASSERT(0 == numFrictionPatches); + PX_ASSERT(0 == solverConstraintByteSize); + PX_ASSERT(0 == axisConstraintCount); + + //From frictionPatchStream we just need to reserve a single buffer. + PxU32 frictionPatchByteSize = 0; + //Compute the sizes of all the buffers. + computeBlockStreamByteSizes( + useExtContacts, cBuffer, + solverConstraintByteSize, frictionPatchByteSize, numFrictionPatches, + axisConstraintCount); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if(constraintBlockByteSize > 0) + { + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if(0==constraintBlock || (reinterpret_cast(-1))==constraintBlock) + { + if(0==constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock=NULL; + } + } + PX_ASSERT((size_t(constraintBlock) & 0xF) == 0); + } + + FrictionPatch* frictionPatches = NULL; + //If the constraint block reservation didn't fail then reserve the friction buffer too. + if(frictionPatchByteSize >0 && (0==constraintBlockByteSize || constraintBlock)) + { + frictionPatches = reinterpret_cast(constraintAllocator.reserveFrictionData(frictionPatchByteSize)); + + if(0==frictionPatches || (reinterpret_cast(-1))==frictionPatches) + { + if(0==frictionPatches) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of friction data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + frictionPatches=NULL; + } + } + } + + _frictionPatches = frictionPatches; + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if(0==constraintBlockByteSize || constraintBlock) + { + if(solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0==(uintptr_t(solverConstraint) & 0x0f)); + } + } + + //Return true if neither of the two block reservations failed. + return ((0==constraintBlockByteSize || constraintBlock) && (0==frictionPatchByteSize || frictionPatches)); +} + + +bool createFinalizeSolverContacts( + PxSolverContactDesc& contactDesc, + CorrelationBuffer& c, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z) +{ + Ps::prefetchLine(contactDesc.body0); + Ps::prefetchLine(contactDesc.body1); + Ps::prefetchLine(contactDesc.data0); + Ps::prefetchLine(contactDesc.data1); + + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + const bool hasForceThreshold = contactDesc.hasForceThresholds; + const bool staticOrKinematicBody = contactDesc.bodyState1 == PxSolverContactDesc::eKINEMATIC_BODY || contactDesc.bodyState1 == PxSolverContactDesc::eSTATIC_BODY; + + const bool disableStrongFriction = contactDesc.disableStrongFriction; + const bool useExtContacts = ((contactDesc.bodyState0 | contactDesc.bodyState1) & PxSolverContactDesc::eARTICULATION) != 0; + + PxSolverConstraintDesc& desc = *contactDesc.desc; + + desc.constraintLengthOver16 = 0; + + + if (contactDesc.numContacts == 0) + { + contactDesc.frictionPtr = NULL; + contactDesc.frictionCount = 0; + desc.constraint = NULL; + return true; + } + + if (!disableStrongFriction) + { + getFrictionPatches(c, contactDesc.frictionPtr, contactDesc.frictionCount, contactDesc.bodyFrame0, contactDesc.bodyFrame1, correlationDistance); + } + + bool overflow = !createContactPatches(c, contactDesc.contacts, contactDesc.numContacts, PXC_SAME_NORMAL); + overflow = correlatePatches(c, contactDesc.contacts, contactDesc.bodyFrame0, contactDesc.bodyFrame1, PXC_SAME_NORMAL, 0, 0) || overflow; + PX_UNUSED(overflow); + +#if PX_CHECKED + if (overflow) + { + Ps::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Dropping contacts in solver because we exceeded limit of 32 friction patches."); + } +#endif + + growPatches(c, contactDesc.contacts, contactDesc.bodyFrame0, contactDesc.bodyFrame1, correlationDistance, 0, frictionOffsetThreshold + contactDesc.restDistance); + + //PX_ASSERT(patchCount == c.frictionPatchCount); + + FrictionPatch* frictionPatches = NULL; + PxU8* solverConstraint = NULL; + PxU32 numFrictionPatches = 0; + PxU32 solverConstraintByteSize = 0; + PxU32 axisConstraintCount = 0; + + const bool successfulReserve = reserveBlockStreams( + useExtContacts, c, + solverConstraint, frictionPatches, + numFrictionPatches, + solverConstraintByteSize, + axisConstraintCount, + constraintAllocator); + // initialise the work unit's ptrs to the various buffers. + + contactDesc.frictionPtr = NULL; + contactDesc.frictionCount = 0; + desc.constraint = NULL; + desc.constraintLengthOver16 = 0; + // patch up the work unit with the reserved buffers and set the reserved buffer data as appropriate. + + if (successfulReserve) + { + PxU8* frictionDataPtr = reinterpret_cast(frictionPatches); + contactDesc.frictionPtr = frictionDataPtr; + desc.constraint = solverConstraint; + //output.nbContacts = Ps::to8(numContacts); + contactDesc.frictionCount = Ps::to8(numFrictionPatches); + desc.constraintLengthOver16 = Ps::to16(solverConstraintByteSize / 16); + desc.writeBack = contactDesc.contactForces; + desc.writeBackLengthOver4 = PxU16(contactDesc.contactForces ? contactDesc.numContacts : 0); + + //Initialise friction buffer. + if (frictionPatches) + { + // PT: TODO: revisit this... not very satisfying + //const PxU32 maxSize = numFrictionPatches*sizeof(FrictionPatch); + Ps::prefetchLine(frictionPatches); + Ps::prefetchLine(frictionPatches, 128); + Ps::prefetchLine(frictionPatches, 256); + + for (PxU32 i = 0; i(contactDesc.body0), reinterpret_cast(&data0), desc.linkIndexA); + const SolverExtBody b1(reinterpret_cast(contactDesc.body1), reinterpret_cast(&data1), desc.linkIndexB); + + setupFinalizeExtSolverContacts(contactDesc.contacts, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + b0, b1, invDtF32, bounceThresholdF32, + contactDesc.mInvMassScales.linear0, contactDesc.mInvMassScales.angular0, contactDesc.mInvMassScales.linear1, contactDesc.mInvMassScales.angular1, + contactDesc.restDistance, frictionDataPtr, contactDesc.maxCCDSeparation, Z); + } + else + { + const PxSolverBodyData& data0 = *contactDesc.data0; + const PxSolverBodyData& data1 = *contactDesc.data1; + setupFinalizeSolverConstraints(contactDesc.shapeInteraction, contactDesc.contacts, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + data0, data1, invDtF32, bounceThresholdF32, + contactDesc.mInvMassScales.linear0, contactDesc.mInvMassScales.angular0, contactDesc.mInvMassScales.linear1, contactDesc.mInvMassScales.angular1, + hasForceThreshold, staticOrKinematicBody, contactDesc.restDistance, frictionDataPtr, contactDesc.maxCCDSeparation, solverOffsetSlop); + } + //KS - set to 0 so we have a counter for the number of times we solved the constraint + //only going to be used on SPU but might as well set on all platforms because this code is shared + *(reinterpret_cast(solverConstraint + solverConstraintByteSize)) = 0; + } + } + + return successfulReserve; +} + +FloatV setupExtSolverContact(const SolverExtBody& b0, const SolverExtBody& b1, + const PxF32 d0, const PxF32 d1, const PxF32 angD0, const PxF32 angD1, const PxTransform& bodyFrame0, const PxTransform& bodyFrame1, + const Vec3VArg normal, const FloatVArg invDt, const FloatVArg invDtp8, const FloatVArg restDistance, const FloatVArg maxPenBias, const FloatVArg restitution, + const FloatVArg bounceThreshold, const Gu::ContactPoint& contact, SolverContactPointExt& solverContact, const FloatVArg ccdMaxSeparation, Cm::SpatialVectorF* zVector) +{ + const FloatV zero = FZero(); + const FloatV separation = FLoad(contact.separation); + + const FloatV penetration = FSub(separation, restDistance); + + const PxVec3 ra = contact.point - bodyFrame0.p; + const PxVec3 rb = contact.point - bodyFrame1.p; + + const PxVec3 raXn = ra.cross(contact.normal); + const PxVec3 rbXn = rb.cross(contact.normal); + + Cm::SpatialVector deltaV0, deltaV1; + + const Cm::SpatialVector resp0 = createImpulseResponseVector(contact.normal, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-contact.normal, -rbXn, b1); + + const FloatV unitResponse = FLoad(getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, zVector)); + + + const FloatV vel0 = FLoad(b0.projectVelocity(contact.normal, raXn)); + const FloatV vel1 = FLoad(b1.projectVelocity(contact.normal, rbXn)); + + const FloatV vrel = FSub(vel0, vel1); + + FloatV velMultiplier = FSel(FIsGrtr(FLoad(DY_ARTICULATION_MIN_RESPONSE), unitResponse), zero, FRecip(unitResponse)); + //FloatV velMultiplier = FSel(FIsGrtr(FEps(), unitResponse), zero, FRecip(unitResponse)); + FloatV scaledBias = FMul(velMultiplier, FMax(maxPenBias, FMul(penetration, invDtp8))); + const FloatV penetrationInvDt = FMul(penetration, invDt); + + const BoolV isGreater2 = BAnd(BAnd(FIsGrtr(restitution, zero), FIsGrtr(bounceThreshold, vrel)), FIsGrtr(FNeg(vrel), penetrationInvDt)); + + const BoolV ccdSeparationCondition = FIsGrtrOrEq(ccdMaxSeparation, penetration); + + scaledBias = FSel(BAnd(ccdSeparationCondition, isGreater2), zero, scaledBias); + + FloatV targetVelocity = FSel(isGreater2, FMul(FNeg(vrel), restitution), zero); + + //Get the rigid body's current velocity and embed into the constraint target velocities + if (b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVelocity = FSub(targetVelocity, vel0); + else if (b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + targetVelocity = FAdd(targetVelocity, vel1); + + targetVelocity = FAdd(targetVelocity, V3Dot(V3LoadA(contact.targetVel), normal)); + + const FloatV biasedErr = FScaleAdd(targetVelocity, velMultiplier, FNeg(scaledBias)); + const FloatV unbiasedErr = FScaleAdd(targetVelocity, velMultiplier, FSel(isGreater2, zero, FNeg(FMax(scaledBias, zero)))); + + const FloatV deltaF = FMax(FNegScaleSub(vrel, velMultiplier, biasedErr), zero); + + + FStore(velMultiplier, &solverContact.velMultiplier); + FStore(biasedErr, &solverContact.biasedErr); + FStore(unbiasedErr, &solverContact.unbiasedErr); + solverContact.maxImpulse = contact.maxImpulse; + + solverContact.raXn = V3LoadA(resp0.angular); + solverContact.rbXn = V3Neg(V3LoadA(resp1.angular)); + solverContact.linDeltaVA = V3LoadA(deltaV0.linear); + solverContact.angDeltaVA = V3LoadA(deltaV0.angular); + solverContact.linDeltaVB = V3LoadA(deltaV1.linear); + solverContact.angDeltaVB = V3LoadA(deltaV1.angular); + + return deltaF; +} + + +bool createFinalizeSolverContacts(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z) +{ + ContactBuffer& buffer = threadContext.mContactBuffer; + + + + buffer.count = 0; + + // We pull the friction patches out of the cache to remove the dependency on how + // the cache is organized. Remember original addrs so we can write them back + // efficiently. + + PxU32 numContacts = 0; + { + PxReal invMassScale0 = 1.f; + PxReal invMassScale1 = 1.f; + PxReal invInertiaScale0 = 1.f; + PxReal invInertiaScale1 = 1.f; + + bool hasMaxImpulse = false, hasTargetVelocity = false; + + numContacts = extractContacts(buffer, output, hasMaxImpulse, hasTargetVelocity, invMassScale0, invMassScale1, + invInertiaScale0, invInertiaScale1, PxMin(contactDesc.data0->maxContactImpulse, contactDesc.data1->maxContactImpulse)); + + contactDesc.contacts = buffer.contacts; + contactDesc.numContacts = numContacts; + contactDesc.disableStrongFriction = contactDesc.disableStrongFriction || hasTargetVelocity; + contactDesc.hasMaxImpulse = hasMaxImpulse; + contactDesc.mInvMassScales.linear0 *= invMassScale0; + contactDesc.mInvMassScales.linear1 *= invMassScale1; + contactDesc.mInvMassScales.angular0 *= invInertiaScale0; + contactDesc.mInvMassScales.angular1 *= invInertiaScale1; + } + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + + return createFinalizeSolverContacts(contactDesc, c, invDtF32, bounceThresholdF32, frictionOffsetThreshold, + correlationDistance, solverOffsetSlop, constraintAllocator, Z); +} + +PxU32 getContactManagerConstraintDesc(const PxsContactManagerOutput& cmOutput, const PxsContactManager& /*cm*/, PxSolverConstraintDesc& desc) +{ + desc.writeBackLengthOver4 = cmOutput.nbContacts; + desc.writeBack = cmOutput.contactForces; + return cmOutput.nbContacts;// cm.getWorkUnit().axisConstraintCount; +} + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h new file mode 100644 index 000000000..9edef9ee7 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h @@ -0,0 +1,183 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_CONTACTPREP_H +#define DY_CONTACTPREP_H + +#include "DySolverConstraintDesc.h" +#include "PxSceneDesc.h" +#include "DySolverContact4.h" + +namespace physx +{ + +struct PxcNpWorkUnit; +class PxsConstraintBlockManager; +struct PxsContactManagerOutput; +struct PxSolverBody; +struct PxSolverBodyData; +struct PxSolverConstraintDesc; +class PxsContactManager; + +namespace Dy +{ + class ThreadContext; + struct CorrelationBuffer; + +#define CREATE_FINALIZE_SOLVER_CONTACT_METHOD_ARGS \ + PxSolverContactDesc& contactDesc, \ + PxsContactManagerOutput& output, \ + ThreadContext& threadContext, \ + const PxReal invDtF32, \ + PxReal bounceThresholdF32, \ + PxReal frictionOffsetThreshold, \ + PxReal correlationDistance, \ + PxReal solverOffsetSlop, \ + PxConstraintAllocator& constraintAllocator, \ + Cm::SpatialVectorF* Z + +#define CREATE_FINALIZE_SOVLER_CONTACT_METHOD_ARGS_4 \ + PxsContactManagerOutput** outputs, \ + ThreadContext& threadContext, \ + PxSolverContactDesc* blockDescs, \ + const PxReal invDtF32, \ + PxReal bounceThresholdF32, \ + PxReal frictionThresholdF32, \ + PxReal correlationDistanceF32, \ + PxReal solverOffsetSlopF32, \ + PxConstraintAllocator& constraintAllocator + + +/*! +Method prototype for create finalize solver contact +*/ + +typedef bool (*PxcCreateFinalizeSolverContactMethod)(CREATE_FINALIZE_SOLVER_CONTACT_METHOD_ARGS); + +extern PxcCreateFinalizeSolverContactMethod createFinalizeMethods[3]; + +typedef SolverConstraintPrepState::Enum (*PxcCreateFinalizeSolverContactMethod4)(CREATE_FINALIZE_SOVLER_CONTACT_METHOD_ARGS_4); + +extern PxcCreateFinalizeSolverContactMethod4 createFinalizeMethods4[3]; + + +bool createFinalizeSolverContacts( PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z); + +bool createFinalizeSolverContacts( PxSolverContactDesc& contactDesc, + CorrelationBuffer& c, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z); + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4( PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator); + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4( Dy::CorrelationBuffer& c, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator); + + + +bool createFinalizeSolverContactsCoulomb1D(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z); + +bool createFinalizeSolverContactsCoulomb2D(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z); + + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb1D( PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator); + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb2D(PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator); + + +PxU32 getContactManagerConstraintDesc(const PxsContactManagerOutput& cmOutput, const PxsContactManager& cm, PxSolverConstraintDesc& desc); + +} + +} + +#endif //DY_CONTACTPREP_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp new file mode 100644 index 000000000..d8fea61a1 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp @@ -0,0 +1,1535 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "PxSceneDesc.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContact4.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DyDynamics.h" +#include "DyArticulationContactPrep.h" +#include "PxsContactManager.h" + +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DyContactPrepShared.h" + +using namespace Ps::aos; + +namespace physx +{ +namespace Dy +{ + +PxcCreateFinalizeSolverContactMethod4 createFinalizeMethods4[3] = +{ + createFinalizeSolverContacts4, + createFinalizeSolverContacts4Coulomb1D, + createFinalizeSolverContacts4Coulomb2D +}; + +inline bool ValidateVec4(const Vec4V v) +{ + PX_ALIGN(16, PxVec4 vF); + Ps::aos::V4StoreA(v, &vF.x); + return vF.isFinite(); +} + +static void setupFinalizeSolverConstraints4(PxSolverContactDesc* PX_RESTRICT descs, CorrelationBuffer& c, PxU8* PX_RESTRICT workspace, + const PxReal invDtF32, PxReal bounceThresholdF32, const PxReal solverOffsetSlopF32, + const Ps::aos::Vec4VArg invMassScale0, const Ps::aos::Vec4VArg invInertiaScale0, + const Ps::aos::Vec4VArg invMassScale1, const Ps::aos::Vec4VArg invInertiaScale1) +{ + + //OK, we have a workspace of pre-allocated space to store all 4 descs in. We now need to create the constraints in it + + const Vec4V ccdMaxSeparation = Ps::aos::V4LoadXYZW(descs[0].maxCCDSeparation, descs[1].maxCCDSeparation, descs[2].maxCCDSeparation, descs[3].maxCCDSeparation); + const Vec4V solverOffsetSlop = V4Load(solverOffsetSlopF32); + + const Vec4V zero = V4Zero(); + const BoolV bFalse = BFFFF(); + const FloatV fZero = FZero(); + + PxU8 flags[4] = { PxU8(descs[0].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[1].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[2].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[3].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0) }; + + bool hasMaxImpulse = descs[0].hasMaxImpulse || descs[1].hasMaxImpulse || descs[2].hasMaxImpulse || descs[3].hasMaxImpulse; + + //The block is dynamic if **any** of the constraints have a non-static body B. This allows us to batch static and non-static constraints but we only get a memory/perf + //saving if all 4 are static. This simplifies the constraint partitioning such that it only needs to care about separating contacts and 1D constraints (which it already does) + bool isDynamic = false; + bool hasKinematic = false; + for(PxU32 a = 0; a < 4; ++a) + { + isDynamic = isDynamic || (descs[a].bodyState1 == PxSolverContactDesc::eDYNAMIC_BODY); + hasKinematic = hasKinematic || descs[a].bodyState1 == PxSolverContactDesc::eKINEMATIC_BODY; + } + + const PxU32 constraintSize = isDynamic ? sizeof(SolverContactBatchPointDynamic4) : sizeof(SolverContactBatchPointBase4); + const PxU32 frictionSize = isDynamic ? sizeof(SolverContactFrictionDynamic4) : sizeof(SolverContactFrictionBase4); + + PxU8* PX_RESTRICT ptr = workspace; + + const Vec4V dom0 = invMassScale0; + const Vec4V dom1 = invMassScale1; + const Vec4V angDom0 = invInertiaScale0; + const Vec4V angDom1 = invInertiaScale1; + + const Vec4V maxPenBias = V4Max(V4LoadXYZW(descs[0].data0->penBiasClamp, descs[1].data0->penBiasClamp, + descs[2].data0->penBiasClamp, descs[3].data0->penBiasClamp), + V4LoadXYZW(descs[0].data1->penBiasClamp, descs[1].data1->penBiasClamp, + descs[2].data1->penBiasClamp, descs[3].data1->penBiasClamp)); + + const Vec4V restDistance = V4LoadXYZW(descs[0].restDistance, descs[1].restDistance, descs[2].restDistance, + descs[3].restDistance); + + + //load up velocities + Vec4V linVel00 = V4LoadA(&descs[0].data0->linearVelocity.x); + Vec4V linVel10 = V4LoadA(&descs[1].data0->linearVelocity.x); + Vec4V linVel20 = V4LoadA(&descs[2].data0->linearVelocity.x); + Vec4V linVel30 = V4LoadA(&descs[3].data0->linearVelocity.x); + + Vec4V linVel01 = V4LoadA(&descs[0].data1->linearVelocity.x); + Vec4V linVel11 = V4LoadA(&descs[1].data1->linearVelocity.x); + Vec4V linVel21 = V4LoadA(&descs[2].data1->linearVelocity.x); + Vec4V linVel31 = V4LoadA(&descs[3].data1->linearVelocity.x); + + Vec4V angVel00 = V4LoadA(&descs[0].data0->angularVelocity.x); + Vec4V angVel10 = V4LoadA(&descs[1].data0->angularVelocity.x); + Vec4V angVel20 = V4LoadA(&descs[2].data0->angularVelocity.x); + Vec4V angVel30 = V4LoadA(&descs[3].data0->angularVelocity.x); + + Vec4V angVel01 = V4LoadA(&descs[0].data1->angularVelocity.x); + Vec4V angVel11 = V4LoadA(&descs[1].data1->angularVelocity.x); + Vec4V angVel21 = V4LoadA(&descs[2].data1->angularVelocity.x); + Vec4V angVel31 = V4LoadA(&descs[3].data1->angularVelocity.x); + + Vec4V linVelT00, linVelT10, linVelT20; + Vec4V linVelT01, linVelT11, linVelT21; + Vec4V angVelT00, angVelT10, angVelT20; + Vec4V angVelT01, angVelT11, angVelT21; + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVelT00, linVelT10, linVelT20); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVelT01, linVelT11, linVelT21); + PX_TRANSPOSE_44_34(angVel00, angVel10, angVel20, angVel30, angVelT00, angVelT10, angVelT20); + PX_TRANSPOSE_44_34(angVel01, angVel11, angVel21, angVel31, angVelT01, angVelT11, angVelT21); + + const Vec4V vrelX = V4Sub(linVelT00, linVelT01); + const Vec4V vrelY = V4Sub(linVelT10, linVelT11); + const Vec4V vrelZ = V4Sub(linVelT20, linVelT21); + + //Load up masses and invInertia + + /*const Vec4V sqrtInvMass0 = V4Merge(FLoad(descs[0].data0->sqrtInvMass), FLoad(descs[1].data0->sqrtInvMass), FLoad(descs[2].data0->sqrtInvMass), + FLoad(descs[3].data0->sqrtInvMass)); + + const Vec4V sqrtInvMass1 = V4Merge(FLoad(descs[0].data1->sqrtInvMass), FLoad(descs[1].data1->sqrtInvMass), FLoad(descs[2].data1->sqrtInvMass), + FLoad(descs[3].data1->sqrtInvMass));*/ + + const Vec4V invMass0 = V4LoadXYZW(descs[0].data0->invMass, descs[1].data0->invMass, descs[2].data0->invMass, descs[3].data0->invMass); + const Vec4V invMass1 = V4LoadXYZW(descs[0].data1->invMass, descs[1].data1->invMass, descs[2].data1->invMass, descs[3].data1->invMass); + + const Vec4V invMass0D0 = V4Mul(dom0, invMass0); + const Vec4V invMass1D1 = V4Mul(dom1, invMass1); + + Vec4V invInertia00X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].data0->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia00Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].data0->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia00Z = Vec4V_From_Vec3V(V3LoadU(descs[0].data0->sqrtInvInertia.column2)); + + Vec4V invInertia10X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].data0->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia10Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].data0->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia10Z = Vec4V_From_Vec3V(V3LoadU(descs[1].data0->sqrtInvInertia.column2)); + + Vec4V invInertia20X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].data0->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia20Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].data0->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia20Z = Vec4V_From_Vec3V(V3LoadU(descs[2].data0->sqrtInvInertia.column2)); + + Vec4V invInertia30X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].data0->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia30Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].data0->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia30Z = Vec4V_From_Vec3V(V3LoadU(descs[3].data0->sqrtInvInertia.column2)); + + Vec4V invInertia01X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].data1->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia01Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].data1->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia01Z = Vec4V_From_Vec3V(V3LoadU(descs[0].data1->sqrtInvInertia.column2)); + + Vec4V invInertia11X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].data1->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia11Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].data1->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia11Z = Vec4V_From_Vec3V(V3LoadU(descs[1].data1->sqrtInvInertia.column2)); + + Vec4V invInertia21X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].data1->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia21Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].data1->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia21Z = Vec4V_From_Vec3V(V3LoadU(descs[2].data1->sqrtInvInertia.column2)); + + Vec4V invInertia31X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].data1->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia31Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].data1->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia31Z = Vec4V_From_Vec3V(V3LoadU(descs[3].data1->sqrtInvInertia.column2)); + + Vec4V invInertia0X0, invInertia0X1, invInertia0X2; + Vec4V invInertia0Y0, invInertia0Y1, invInertia0Y2; + Vec4V invInertia0Z0, invInertia0Z1, invInertia0Z2; + + Vec4V invInertia1X0, invInertia1X1, invInertia1X2; + Vec4V invInertia1Y0, invInertia1Y1, invInertia1Y2; + Vec4V invInertia1Z0, invInertia1Z1, invInertia1Z2; + + PX_TRANSPOSE_44_34(invInertia00X, invInertia10X, invInertia20X, invInertia30X, invInertia0X0, invInertia0Y0, invInertia0Z0); + PX_TRANSPOSE_44_34(invInertia00Y, invInertia10Y, invInertia20Y, invInertia30Y, invInertia0X1, invInertia0Y1, invInertia0Z1); + PX_TRANSPOSE_44_34(invInertia00Z, invInertia10Z, invInertia20Z, invInertia30Z, invInertia0X2, invInertia0Y2, invInertia0Z2); + + PX_TRANSPOSE_44_34(invInertia01X, invInertia11X, invInertia21X, invInertia31X, invInertia1X0, invInertia1Y0, invInertia1Z0); + PX_TRANSPOSE_44_34(invInertia01Y, invInertia11Y, invInertia21Y, invInertia31Y, invInertia1X1, invInertia1Y1, invInertia1Z1); + PX_TRANSPOSE_44_34(invInertia01Z, invInertia11Z, invInertia21Z, invInertia31Z, invInertia1X2, invInertia1Y2, invInertia1Z2); + + + const FloatV invDt = FLoad(invDtF32); + const FloatV p8 = FLoad(0.8f); + const Vec4V p84 = V4Splat(p8); + const Vec4V bounceThreshold = V4Splat(FLoad(bounceThresholdF32)); + + const FloatV invDtp8 = FMul(invDt, p8); + + const Vec3V bodyFrame00p = V3LoadU(descs[0].bodyFrame0.p); + const Vec3V bodyFrame01p = V3LoadU(descs[1].bodyFrame0.p); + const Vec3V bodyFrame02p = V3LoadU(descs[2].bodyFrame0.p); + const Vec3V bodyFrame03p = V3LoadU(descs[3].bodyFrame0.p); + + Vec4V bodyFrame00p4 = Vec4V_From_Vec3V(bodyFrame00p); + Vec4V bodyFrame01p4 = Vec4V_From_Vec3V(bodyFrame01p); + Vec4V bodyFrame02p4 = Vec4V_From_Vec3V(bodyFrame02p); + Vec4V bodyFrame03p4 = Vec4V_From_Vec3V(bodyFrame03p); + + Vec4V bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ; + PX_TRANSPOSE_44_34(bodyFrame00p4, bodyFrame01p4, bodyFrame02p4, bodyFrame03p4, bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ); + + + const Vec3V bodyFrame10p = V3LoadU(descs[0].bodyFrame1.p); + const Vec3V bodyFrame11p = V3LoadU(descs[1].bodyFrame1.p); + const Vec3V bodyFrame12p = V3LoadU(descs[2].bodyFrame1.p); + const Vec3V bodyFrame13p = V3LoadU(descs[3].bodyFrame1.p); + + Vec4V bodyFrame10p4 = Vec4V_From_Vec3V(bodyFrame10p); + Vec4V bodyFrame11p4 = Vec4V_From_Vec3V(bodyFrame11p); + Vec4V bodyFrame12p4 = Vec4V_From_Vec3V(bodyFrame12p); + Vec4V bodyFrame13p4 = Vec4V_From_Vec3V(bodyFrame13p); + + Vec4V bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ; + PX_TRANSPOSE_44_34(bodyFrame10p4, bodyFrame11p4, bodyFrame12p4, bodyFrame13p4, bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ); + + + const QuatV bodyFrame00q = QuatVLoadU(&descs[0].bodyFrame0.q.x); + const QuatV bodyFrame01q = QuatVLoadU(&descs[1].bodyFrame0.q.x); + const QuatV bodyFrame02q = QuatVLoadU(&descs[2].bodyFrame0.q.x); + const QuatV bodyFrame03q = QuatVLoadU(&descs[3].bodyFrame0.q.x); + + const QuatV bodyFrame10q = QuatVLoadU(&descs[0].bodyFrame1.q.x); + const QuatV bodyFrame11q = QuatVLoadU(&descs[1].bodyFrame1.q.x); + const QuatV bodyFrame12q = QuatVLoadU(&descs[2].bodyFrame1.q.x); + const QuatV bodyFrame13q = QuatVLoadU(&descs[3].bodyFrame1.q.x); + + PxU32 frictionPatchWritebackAddrIndex0 = 0; + PxU32 frictionPatchWritebackAddrIndex1 = 0; + PxU32 frictionPatchWritebackAddrIndex2 = 0; + PxU32 frictionPatchWritebackAddrIndex3 = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + PxU32 frictionIndex0 = 0, frictionIndex1 = 0, frictionIndex2 = 0, frictionIndex3 = 0; + //PxU32 contactIndex0 = 0, contactIndex1 = 0, contactIndex2 = 0, contactIndex3 = 0; + + + //OK, we iterate through all friction patch counts in the constraint patch, building up the constraint list etc. + + PxU32 maxPatches = PxMax(descs[0].numFrictionPatches, PxMax(descs[1].numFrictionPatches, PxMax(descs[2].numFrictionPatches, descs[3].numFrictionPatches))); + + const Vec4V p1 = V4Splat(FLoad(0.1f)); + const Vec4V orthoThreshold = V4Splat(FLoad(0.70710678f)); + + + PxU32 contact0 = 0, contact1 = 0, contact2 = 0, contact3 = 0; + PxU32 patch0 = 0, patch1 = 0, patch2 = 0, patch3 = 0; + + PxU8 flag = 0; + if(hasMaxImpulse) + flag |= SolverContactHeader4::eHAS_MAX_IMPULSE; + + for(PxU32 i=0;i= descs[0].numFrictionPatches; + const bool hasFinished1 = i >= descs[1].numFrictionPatches; + const bool hasFinished2 = i >= descs[2].numFrictionPatches; + const bool hasFinished3 = i >= descs[3].numFrictionPatches; + + + frictionIndex0 = hasFinished0 ? frictionIndex0 : descs[0].startFrictionPatchIndex + i; + frictionIndex1 = hasFinished1 ? frictionIndex1 : descs[1].startFrictionPatchIndex + i; + frictionIndex2 = hasFinished2 ? frictionIndex2 : descs[2].startFrictionPatchIndex + i; + frictionIndex3 = hasFinished3 ? frictionIndex3 : descs[3].startFrictionPatchIndex + i; + + PxU32 clampedContacts0 = hasFinished0 ? 0 : c.frictionPatchContactCounts[frictionIndex0]; + PxU32 clampedContacts1 = hasFinished1 ? 0 : c.frictionPatchContactCounts[frictionIndex1]; + PxU32 clampedContacts2 = hasFinished2 ? 0 : c.frictionPatchContactCounts[frictionIndex2]; + PxU32 clampedContacts3 = hasFinished3 ? 0 : c.frictionPatchContactCounts[frictionIndex3]; + + PxU32 firstPatch0 = c.correlationListHeads[frictionIndex0]; + PxU32 firstPatch1 = c.correlationListHeads[frictionIndex1]; + PxU32 firstPatch2 = c.correlationListHeads[frictionIndex2]; + PxU32 firstPatch3 = c.correlationListHeads[frictionIndex3]; + + const Gu::ContactPoint* contactBase0 = descs[0].contacts + c.contactPatches[firstPatch0].start; + const Gu::ContactPoint* contactBase1 = descs[1].contacts + c.contactPatches[firstPatch1].start; + const Gu::ContactPoint* contactBase2 = descs[2].contacts + c.contactPatches[firstPatch2].start; + const Gu::ContactPoint* contactBase3 = descs[3].contacts + c.contactPatches[firstPatch3].start; + + const Vec4V restitution = V4Neg(V4LoadXYZW(contactBase0->restitution, contactBase1->restitution, contactBase2->restitution, + contactBase3->restitution)); + + SolverContactHeader4* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeader4); + + + header->flags[0] = flags[0]; + header->flags[1] = flags[1]; + header->flags[2] = flags[2]; + header->flags[3] = flags[3]; + + header->flag = flag; + + PxU32 totalContacts = PxMax(clampedContacts0, PxMax(clampedContacts1, PxMax(clampedContacts2, clampedContacts3))); + + Vec4V* PX_RESTRICT appliedNormalForces = reinterpret_cast(ptr); + ptr += sizeof(Vec4V)*totalContacts; + + PxMemZero(appliedNormalForces, sizeof(Vec4V) * totalContacts); + + header->numNormalConstr = Ps::to8(totalContacts); + header->numNormalConstr0 = Ps::to8(clampedContacts0); + header->numNormalConstr1 = Ps::to8(clampedContacts1); + header->numNormalConstr2 = Ps::to8(clampedContacts2); + header->numNormalConstr3 = Ps::to8(clampedContacts3); + //header->sqrtInvMassA = sqrtInvMass0; + //header->sqrtInvMassB = sqrtInvMass1; + header->invMass0D0 = invMass0D0; + header->invMass1D1 = invMass1D1; + header->angDom0 = angDom0; + header->angDom1 = angDom1; + header->shapeInteraction[0] = descs[0].shapeInteraction; header->shapeInteraction[1] = descs[1].shapeInteraction; + header->shapeInteraction[2] = descs[2].shapeInteraction; header->shapeInteraction[3] = descs[3].shapeInteraction; + + Vec4V* maxImpulse = reinterpret_cast(ptr + constraintSize * totalContacts); + + header->restitution = restitution; + + Vec4V normal0 = V4LoadA(&contactBase0->normal.x); + Vec4V normal1 = V4LoadA(&contactBase1->normal.x); + Vec4V normal2 = V4LoadA(&contactBase2->normal.x); + Vec4V normal3 = V4LoadA(&contactBase3->normal.x); + + Vec4V normalX, normalY, normalZ; + PX_TRANSPOSE_44_34(normal0, normal1, normal2, normal3, normalX, normalY, normalZ); + + PX_ASSERT(ValidateVec4(normalX)); + PX_ASSERT(ValidateVec4(normalY)); + PX_ASSERT(ValidateVec4(normalZ)); + + header->normalX = normalX; + header->normalY = normalY; + header->normalZ = normalZ; + + const Vec4V norVel0 = V4MulAdd(normalZ, linVelT20, V4MulAdd(normalY, linVelT10, V4Mul(normalX, linVelT00))); + const Vec4V norVel1 = V4MulAdd(normalZ, linVelT21, V4MulAdd(normalY, linVelT11, V4Mul(normalX, linVelT01))); + const Vec4V relNorVel = V4Sub(norVel0, norVel1); + + //For all correlation heads - need to pull this out I think + + //OK, we have a counter for all our patches... + PxU32 finished = (PxU32(hasFinished0)) | + ((PxU32(hasFinished1)) << 1) | + ((PxU32(hasFinished2)) << 2) | + ((PxU32(hasFinished3)) << 3); + + CorrelationListIterator iter0(c, firstPatch0); + CorrelationListIterator iter1(c, firstPatch1); + CorrelationListIterator iter2(c, firstPatch2); + CorrelationListIterator iter3(c, firstPatch3); + + //PxU32 contact0, contact1, contact2, contact3; + //PxU32 patch0, patch1, patch2, patch3; + + if(!hasFinished0) + iter0.nextContact(patch0, contact0); + if(!hasFinished1) + iter1.nextContact(patch1, contact1); + if(!hasFinished2) + iter2.nextContact(patch2, contact2); + if(!hasFinished3) + iter3.nextContact(patch3, contact3); + + PxU8* p = ptr; + + PxU32 contactCount = 0; + PxU32 newFinished = + (PxU32(hasFinished0 || !iter0.hasNextContact())) | + ((PxU32(hasFinished1 || !iter1.hasNextContact())) << 1) | + ((PxU32(hasFinished2 || !iter2.hasNextContact())) << 2) | + ((PxU32(hasFinished3 || !iter3.hasNextContact())) << 3); + + while(finished != 0xf) + { + finished = newFinished; + ++contactCount; + Ps::prefetchLine(p, 384); + Ps::prefetchLine(p, 512); + Ps::prefetchLine(p, 640); + + SolverContactBatchPointBase4* PX_RESTRICT solverContact = reinterpret_cast(p); + p += constraintSize; + + const Gu::ContactPoint& con0 = descs[0].contacts[c.contactPatches[patch0].start + contact0]; + const Gu::ContactPoint& con1 = descs[1].contacts[c.contactPatches[patch1].start + contact1]; + const Gu::ContactPoint& con2 = descs[2].contacts[c.contactPatches[patch2].start + contact2]; + const Gu::ContactPoint& con3 = descs[3].contacts[c.contactPatches[patch3].start + contact3]; + + //Now we need to splice these 4 contacts into a single structure + + { + Vec4V point0 = V4LoadA(&con0.point.x); + Vec4V point1 = V4LoadA(&con1.point.x); + Vec4V point2 = V4LoadA(&con2.point.x); + Vec4V point3 = V4LoadA(&con3.point.x); + + Vec4V pointX, pointY, pointZ; + PX_TRANSPOSE_44_34(point0, point1, point2, point3, pointX, pointY, pointZ); + + PX_ASSERT(ValidateVec4(pointX)); + PX_ASSERT(ValidateVec4(pointY)); + PX_ASSERT(ValidateVec4(pointZ)); + + Vec4V cTargetVel0 = V4LoadA(&con0.targetVel.x); + Vec4V cTargetVel1 = V4LoadA(&con1.targetVel.x); + Vec4V cTargetVel2 = V4LoadA(&con2.targetVel.x); + Vec4V cTargetVel3 = V4LoadA(&con3.targetVel.x); + + Vec4V cTargetVelX, cTargetVelY, cTargetVelZ; + PX_TRANSPOSE_44_34(cTargetVel0, cTargetVel1, cTargetVel2, cTargetVel3, cTargetVelX, cTargetVelY, cTargetVelZ); + + const Vec4V separation = V4LoadXYZW(con0.separation, con1.separation, con2.separation, con3.separation); + + const Vec4V cTargetNorVel = V4MulAdd(cTargetVelX, normalX, V4MulAdd(cTargetVelY, normalY, V4Mul(cTargetVelZ, normalZ))); + + const Vec4V raX = V4Sub(pointX, bodyFrame0pX); + const Vec4V raY = V4Sub(pointY, bodyFrame0pY); + const Vec4V raZ = V4Sub(pointZ, bodyFrame0pZ); + + const Vec4V rbX = V4Sub(pointX, bodyFrame1pX); + const Vec4V rbY = V4Sub(pointY, bodyFrame1pY); + const Vec4V rbZ = V4Sub(pointZ, bodyFrame1pZ); + + /*raX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raX)), zero, raX); + raY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raY)), zero, raY); + raZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raZ)), zero, raZ); + + rbX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbX)), zero, rbX); + rbY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbY)), zero, rbY); + rbZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbZ)), zero, rbZ);*/ + + PX_ASSERT(ValidateVec4(raX)); + PX_ASSERT(ValidateVec4(raY)); + PX_ASSERT(ValidateVec4(raZ)); + + PX_ASSERT(ValidateVec4(rbX)); + PX_ASSERT(ValidateVec4(rbY)); + PX_ASSERT(ValidateVec4(rbZ)); + + + //raXn = cross(ra, normal) which = Vec3V( a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x); + + Vec4V raXnX = V4NegMulSub(raZ, normalY, V4Mul(raY, normalZ)); + Vec4V raXnY = V4NegMulSub(raX, normalZ, V4Mul(raZ, normalX)); + Vec4V raXnZ = V4NegMulSub(raY, normalX, V4Mul(raX, normalY)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + + PX_ASSERT(ValidateVec4(delAngVel0X)); + PX_ASSERT(ValidateVec4(delAngVel0Y)); + PX_ASSERT(ValidateVec4(delAngVel0Z)); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0X, delAngVel0X, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0Z, delAngVel0Z))); + const Vec4V dotRaXnAngVel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4Mul(raXnX, angVelT00))); + + Vec4V unitResponse = V4MulAdd(invMass0D0, angDom0, dotDelAngVel0); + Vec4V vrel = V4Add(relNorVel, dotRaXnAngVel0); + + + //The dynamic-only parts - need to if-statement these up. A branch here shouldn't cost us too much + if(isDynamic) + { + SolverContactBatchPointDynamic4* PX_RESTRICT dynamicContact = static_cast(solverContact); + Vec4V rbXnX = V4NegMulSub(rbZ, normalY, V4Mul(rbY, normalZ)); + Vec4V rbXnY = V4NegMulSub(rbX, normalZ, V4Mul(rbZ, normalX)); + Vec4V rbXnZ = V4NegMulSub(rbY, normalX, V4Mul(rbX, normalY)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + Vec4V delAngVel1X = V4Mul(invInertia1X0, rbXnX); + Vec4V delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + Vec4V delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + PX_ASSERT(ValidateVec4(delAngVel1X)); + PX_ASSERT(ValidateVec4(delAngVel1Y)); + PX_ASSERT(ValidateVec4(delAngVel1Z)); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1X, delAngVel1X, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1Z, delAngVel1Z))); + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + unitResponse = V4Add(unitResponse, resp1); + + vrel = V4Sub(vrel, dotRbXnAngVel1); + + //These are for dynamic-only contacts. + dynamicContact->rbXnX = delAngVel1X; + dynamicContact->rbXnY = delAngVel1Y; + dynamicContact->rbXnZ = delAngVel1Z; + + } + else if(hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, normalY, V4Mul(rbY, normalZ)); + const Vec4V rbXnY = V4NegMulSub(rbX, normalZ, V4Mul(rbZ, normalX)); + const Vec4V rbXnZ = V4NegMulSub(rbY, normalX, V4Mul(rbX, normalY)); + + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + + vrel = V4Sub(vrel, dotRbXnAngVel1); + } + + const Vec4V velMultiplier = V4Sel(V4IsGrtr(unitResponse, zero), V4Recip(unitResponse), zero); + + const Vec4V penetration = V4Sub(separation, restDistance); + const Vec4V penInvDtPt8 = V4Max(maxPenBias, V4Scale(penetration, invDtp8)); + Vec4V scaledBias = V4Mul(penInvDtPt8, velMultiplier); + + const Vec4V penetrationInvDt = V4Scale(penetration, invDt); + + const BoolV isGreater2 = BAnd(BAnd(V4IsGrtr(zero, restitution), V4IsGrtr(bounceThreshold, vrel)), + V4IsGrtr(V4Neg(vrel), penetrationInvDt)); + + const BoolV ccdSeparationCondition = V4IsGrtrOrEq(ccdMaxSeparation, penetration); + + scaledBias = V4Sel(BAnd(ccdSeparationCondition, isGreater2), zero, V4Neg(scaledBias)); + + const Vec4V targetVelocity = V4Sel(isGreater2, V4Mul(velMultiplier, V4Mul(vrel, restitution)), zero); + + //Vec4V biasedErr = V4Sel(isGreater2, targetVelocity, scaledBias); + Vec4V biasedErr = V4Add(targetVelocity, scaledBias); + + biasedErr = V4NegMulSub(V4Sub(vrel, cTargetNorVel), velMultiplier, biasedErr); + + //These values are present for static and dynamic contacts + solverContact->raXnX = delAngVel0X; + solverContact->raXnY = delAngVel0Y; + solverContact->raXnZ = delAngVel0Z; + solverContact->velMultiplier = velMultiplier; + solverContact->biasedErr = biasedErr; + + //solverContact->scaledBias = V4Max(zero, scaledBias); + solverContact->scaledBias = V4Sel(isGreater2, scaledBias, V4Max(zero, scaledBias)); + + if(hasMaxImpulse) + { + maxImpulse[contactCount-1] = V4Merge(FLoad(con0.maxImpulse), FLoad(con1.maxImpulse), FLoad(con2.maxImpulse), + FLoad(con3.maxImpulse)); + } + } + if(!(finished & 0x1)) + { + iter0.nextContact(patch0, contact0); + newFinished |= PxU32(!iter0.hasNextContact()); + } + + if(!(finished & 0x2)) + { + iter1.nextContact(patch1, contact1); + newFinished |= (PxU32(!iter1.hasNextContact()) << 1); + } + + if(!(finished & 0x4)) + { + iter2.nextContact(patch2, contact2); + newFinished |= (PxU32(!iter2.hasNextContact()) << 2); + } + + if(!(finished & 0x8)) + { + iter3.nextContact(patch3, contact3); + newFinished |= (PxU32(!iter3.hasNextContact()) << 3); + } + } + ptr = p; + if(hasMaxImpulse) + { + ptr += sizeof(Vec4V) * totalContacts; + } + + //OK...friction time :-) + + Vec4V maxImpulseScale = V4One(); + { + const Vec4V staticFriction = V4LoadXYZW(contactBase0->staticFriction, contactBase1->staticFriction, + contactBase2->staticFriction, contactBase3->staticFriction); + + const Vec4V dynamicFriction = V4LoadXYZW(contactBase0->dynamicFriction, contactBase1->dynamicFriction, + contactBase2->dynamicFriction, contactBase3->dynamicFriction); + + PX_ASSERT(totalContacts == contactCount); + header->dynamicFriction = dynamicFriction; + header->staticFriction = staticFriction; + + const FrictionPatch& frictionPatch0 = c.frictionPatches[frictionIndex0]; + const FrictionPatch& frictionPatch1 = c.frictionPatches[frictionIndex1]; + const FrictionPatch& frictionPatch2 = c.frictionPatches[frictionIndex2]; + const FrictionPatch& frictionPatch3 = c.frictionPatches[frictionIndex3]; + + PxU32 anchorCount0 = frictionPatch0.anchorCount; + PxU32 anchorCount1 = frictionPatch1.anchorCount; + PxU32 anchorCount2 = frictionPatch2.anchorCount; + PxU32 anchorCount3 = frictionPatch3.anchorCount; + + PxU32 clampedAnchorCount0 = hasFinished0 || (contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount0; + PxU32 clampedAnchorCount1 = hasFinished1 || (contactBase1->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount1; + PxU32 clampedAnchorCount2 = hasFinished2 || (contactBase2->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount2; + PxU32 clampedAnchorCount3 = hasFinished3 || (contactBase3->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount3; + + const PxU32 maxAnchorCount = PxMax(clampedAnchorCount0, PxMax(clampedAnchorCount1, PxMax(clampedAnchorCount2, clampedAnchorCount3))); + + //if(clampedAnchorCount0 != clampedAnchorCount1 || clampedAnchorCount0 != clampedAnchorCount2 || clampedAnchorCount0 != clampedAnchorCount3) + // Ps::debugBreak(); + + + //const bool haveFriction = maxAnchorCount != 0; + header->numFrictionConstr = Ps::to8(maxAnchorCount*2); + header->numFrictionConstr0 = Ps::to8(clampedAnchorCount0*2); + header->numFrictionConstr1 = Ps::to8(clampedAnchorCount1*2); + header->numFrictionConstr2 = Ps::to8(clampedAnchorCount2*2); + header->numFrictionConstr3 = Ps::to8(clampedAnchorCount3*2); + + //KS - TODO - extend this if needed + header->type = Ps::to8(isDynamic ? DY_SC_TYPE_BLOCK_RB_CONTACT : DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT); + + if(maxAnchorCount) + { + + //Allocate the shared friction data... + + SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(ptr); + ptr += sizeof(SolverFrictionSharedData4); + PX_UNUSED(fd); + + const BoolV cond =V4IsGrtr(orthoThreshold, V4Abs(normalX)); + + const Vec4V t0FallbackX = V4Sel(cond, zero, V4Neg(normalY)); + const Vec4V t0FallbackY = V4Sel(cond, V4Neg(normalZ), normalX); + const Vec4V t0FallbackZ = V4Sel(cond, normalY, zero); + + //const Vec4V dotNormalVrel = V4MulAdd(normalZ, vrelZ, V4MulAdd(normalY, vrelY, V4Mul(normalX, vrelX))); + const Vec4V vrelSubNorVelX = V4NegMulSub(normalX, relNorVel, vrelX); + const Vec4V vrelSubNorVelY = V4NegMulSub(normalY, relNorVel, vrelY); + const Vec4V vrelSubNorVelZ = V4NegMulSub(normalZ, relNorVel, vrelZ); + + const Vec4V lenSqvrelSubNorVelZ = V4MulAdd(vrelSubNorVelX, vrelSubNorVelX, V4MulAdd(vrelSubNorVelY, vrelSubNorVelY, V4Mul(vrelSubNorVelZ, vrelSubNorVelZ))); + + const BoolV bcon2 = V4IsGrtr(lenSqvrelSubNorVelZ, p1); + + Vec4V t0X = V4Sel(bcon2, vrelSubNorVelX, t0FallbackX); + Vec4V t0Y = V4Sel(bcon2, vrelSubNorVelY, t0FallbackY); + Vec4V t0Z = V4Sel(bcon2, vrelSubNorVelZ, t0FallbackZ); + + + //Now normalize this... + const Vec4V recipLen = V4Rsqrt(V4MulAdd(t0Z, t0Z, V4MulAdd(t0Y, t0Y, V4Mul(t0X, t0X)))); + + t0X = V4Mul(t0X, recipLen); + t0Y = V4Mul(t0Y, recipLen); + t0Z = V4Mul(t0Z, recipLen); + + Vec4V t1X = V4NegMulSub(normalZ, t0Y, V4Mul(normalY, t0Z)); + Vec4V t1Y = V4NegMulSub(normalX, t0Z, V4Mul(normalZ, t0X)); + Vec4V t1Z = V4NegMulSub(normalY, t0X, V4Mul(normalX, t0Y)); + + PX_ASSERT((uintptr_t(descs[0].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[1].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[2].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[3].frictionPtr) & 0xF) == 0); + + + PxU8* PX_RESTRICT writeback0 = descs[0].frictionPtr + frictionPatchWritebackAddrIndex0*sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback1 = descs[1].frictionPtr + frictionPatchWritebackAddrIndex1*sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback2 = descs[2].frictionPtr + frictionPatchWritebackAddrIndex2*sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback3 = descs[3].frictionPtr + frictionPatchWritebackAddrIndex3*sizeof(FrictionPatch); + + PxU32 index0 = 0, index1 = 0, index2 = 0, index3 = 0; + + fd->broken = bFalse; + fd->frictionBrokenWritebackByte[0] = writeback0; + fd->frictionBrokenWritebackByte[1] = writeback1; + fd->frictionBrokenWritebackByte[2] = writeback2; + fd->frictionBrokenWritebackByte[3] = writeback3; + + + fd->normalX[0] = t0X; + fd->normalY[0] = t0Y; + fd->normalZ[0] = t0Z; + + fd->normalX[1] = t1X; + fd->normalY[1] = t1Y; + fd->normalZ[1] = t1Z; + + Vec4V* PX_RESTRICT appliedForces = reinterpret_cast(ptr); + ptr += sizeof(Vec4V)*header->numFrictionConstr; + + PxMemZero(appliedForces, sizeof(Vec4V) * header->numFrictionConstr); + + for(PxU32 j = 0; j < maxAnchorCount; j++) + { + Ps::prefetchLine(ptr, 384); + Ps::prefetchLine(ptr, 512); + Ps::prefetchLine(ptr, 640); + SolverContactFrictionBase4* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionSize; + SolverContactFrictionBase4* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionSize; + + index0 = j < clampedAnchorCount0 ? j : index0; + index1 = j < clampedAnchorCount1 ? j : index1; + index2 = j < clampedAnchorCount2 ? j : index2; + index3 = j < clampedAnchorCount3 ? j : index3; + + if(j >= clampedAnchorCount0) + maxImpulseScale = V4SetX(maxImpulseScale, fZero); + if(j >= clampedAnchorCount1) + maxImpulseScale = V4SetY(maxImpulseScale, fZero); + if(j >= clampedAnchorCount2) + maxImpulseScale = V4SetZ(maxImpulseScale, fZero); + if(j >= clampedAnchorCount3) + maxImpulseScale = V4SetW(maxImpulseScale, fZero); + + t0X = V4Mul(maxImpulseScale, t0X); + t0Y = V4Mul(maxImpulseScale, t0Y); + t0Z = V4Mul(maxImpulseScale, t0Z); + + t1X = V4Mul(maxImpulseScale, t1X); + t1Y = V4Mul(maxImpulseScale, t1Y); + t1Z = V4Mul(maxImpulseScale, t1Z); + + + Vec3V body0Anchor0 = V3LoadU(frictionPatch0.body0Anchors[index0]); + Vec3V body0Anchor1 = V3LoadU(frictionPatch1.body0Anchors[index1]); + Vec3V body0Anchor2 = V3LoadU(frictionPatch2.body0Anchors[index2]); + Vec3V body0Anchor3 = V3LoadU(frictionPatch3.body0Anchors[index3]); + + Vec4V ra0 = Vec4V_From_Vec3V(QuatRotate(bodyFrame00q, body0Anchor0)); + Vec4V ra1 = Vec4V_From_Vec3V(QuatRotate(bodyFrame01q, body0Anchor1)); + Vec4V ra2 = Vec4V_From_Vec3V(QuatRotate(bodyFrame02q, body0Anchor2)); + Vec4V ra3 = Vec4V_From_Vec3V(QuatRotate(bodyFrame03q, body0Anchor3)); + + Vec4V raX, raY, raZ; + PX_TRANSPOSE_44_34(ra0, ra1, ra2, ra3, raX, raY, raZ); + + /*raX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raX)), zero, raX); + raY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raY)), zero, raY); + raZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raZ)), zero, raZ);*/ + + const Vec4V raWorldX = V4Add(raX, bodyFrame0pX); + const Vec4V raWorldY = V4Add(raY, bodyFrame0pY); + const Vec4V raWorldZ = V4Add(raZ, bodyFrame0pZ); + + Vec3V body1Anchor0 = V3LoadU(frictionPatch0.body1Anchors[index0]); + Vec3V body1Anchor1 = V3LoadU(frictionPatch1.body1Anchors[index1]); + Vec3V body1Anchor2 = V3LoadU(frictionPatch2.body1Anchors[index2]); + Vec3V body1Anchor3 = V3LoadU(frictionPatch3.body1Anchors[index3]); + + Vec4V rb0 = Vec4V_From_Vec3V(QuatRotate(bodyFrame10q, body1Anchor0)); + Vec4V rb1 = Vec4V_From_Vec3V(QuatRotate(bodyFrame11q, body1Anchor1)); + Vec4V rb2 = Vec4V_From_Vec3V(QuatRotate(bodyFrame12q, body1Anchor2)); + Vec4V rb3 = Vec4V_From_Vec3V(QuatRotate(bodyFrame13q, body1Anchor3)); + + Vec4V rbX, rbY, rbZ; + PX_TRANSPOSE_44_34(rb0, rb1, rb2, rb3, rbX, rbY, rbZ); + + /*rbX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbX)), zero, rbX); + rbY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbY)), zero, rbY); + rbZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbZ)), zero, rbZ);*/ + + const Vec4V rbWorldX = V4Add(rbX, bodyFrame1pX); + const Vec4V rbWorldY = V4Add(rbY, bodyFrame1pY); + const Vec4V rbWorldZ = V4Add(rbZ, bodyFrame1pZ); + + Vec4V errorX = V4Sub(raWorldX, rbWorldX); + Vec4V errorY = V4Sub(raWorldY, rbWorldY); + Vec4V errorZ = V4Sub(raWorldZ, rbWorldZ); + + /*errorX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorX)), zero, errorX); + errorY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorY)), zero, errorY); + errorZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorZ)), zero, errorZ);*/ + + //KS - todo - get this working with per-point friction + PxU32 contactIndex0 = c.contactID[frictionIndex0][index0]; + PxU32 contactIndex1 = c.contactID[frictionIndex1][index1]; + PxU32 contactIndex2 = c.contactID[frictionIndex2][index2]; + PxU32 contactIndex3 = c.contactID[frictionIndex3][index3]; + + //Ensure that the ocntact indices are valid + PX_ASSERT(contactIndex0 == 0xffff || contactIndex0 < descs[0].numContacts); + PX_ASSERT(contactIndex1 == 0xffff || contactIndex1 < descs[1].numContacts); + PX_ASSERT(contactIndex2 == 0xffff || contactIndex2 < descs[2].numContacts); + PX_ASSERT(contactIndex3 == 0xffff || contactIndex3 < descs[3].numContacts); + + Vec4V targetVel0 = V4LoadA(contactIndex0 == 0xFFFF ? &contactBase0->targetVel.x : &descs[0].contacts[contactIndex0].targetVel.x); + Vec4V targetVel1 = V4LoadA(contactIndex1 == 0xFFFF ? &contactBase0->targetVel.x : &descs[1].contacts[contactIndex1].targetVel.x); + Vec4V targetVel2 = V4LoadA(contactIndex2 == 0xFFFF ? &contactBase0->targetVel.x : &descs[2].contacts[contactIndex2].targetVel.x); + Vec4V targetVel3 = V4LoadA(contactIndex3 == 0xFFFF ? &contactBase0->targetVel.x : &descs[3].contacts[contactIndex3].targetVel.x); + + Vec4V targetVelX, targetVelY, targetVelZ; + PX_TRANSPOSE_44_34(targetVel0, targetVel1, targetVel2, targetVel3, targetVelX, targetVelY, targetVelZ); + + + { + Vec4V raXnX = V4NegMulSub(raZ, t0Y, V4Mul(raY, t0Z)); + Vec4V raXnY = V4NegMulSub(raX, t0Z, V4Mul(raZ, t0X)); + Vec4V raXnZ = V4NegMulSub(raY, t0X, V4Mul(raX, t0Y)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + + Vec4V resp = V4MulAdd(dotDelAngVel0, angDom0, invMass0D0); + + const Vec4V tVel0 = V4MulAdd(t0Z, linVelT20, V4MulAdd(t0Y, linVelT10, V4Mul(t0X, linVelT00))); + Vec4V vrel = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4MulAdd(raXnX, angVelT00, tVel0))); + + if(isDynamic) + { + SolverContactFrictionDynamic4* PX_RESTRICT dynamicF0 = static_cast(f0); + + Vec4V rbXnX = V4NegMulSub(rbZ, t0Y, V4Mul(rbY, t0Z)); + Vec4V rbXnY = V4NegMulSub(rbX, t0Z, V4Mul(rbZ, t0X)); + Vec4V rbXnZ = V4NegMulSub(rbY, t0X, V4Mul(rbX, t0Y)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + Vec4V delAngVel1X = V4Mul(invInertia1X0, rbXnX); + Vec4V delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + Vec4V delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + resp = V4Add(resp, resp1); + + dynamicF0->rbXnX = delAngVel1X; + dynamicF0->rbXnY = delAngVel1Y; + dynamicF0->rbXnZ = delAngVel1Z; + + const Vec4V tVel1 = V4MulAdd(t0Z, linVelT21, V4MulAdd(t0Y, linVelT11, V4Mul(t0X, linVelT01))); + const Vec4V vel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + + vrel = V4Sub(vrel, vel1); + } + else if(hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, t0Y, V4Mul(rbY, t0Z)); + const Vec4V rbXnY = V4NegMulSub(rbX, t0Z, V4Mul(rbZ, t0X)); + const Vec4V rbXnZ = V4NegMulSub(rbY, t0X, V4Mul(rbX, t0Y)); + + const Vec4V tVel1 = V4MulAdd(t0Z, linVelT21, V4MulAdd(t0Y, linVelT11, V4Mul(t0X, linVelT01))); + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + + vrel = V4Sub(vrel, dotRbXnAngVel1); + } + + + const Vec4V velMultiplier = V4Mul(maxImpulseScale, V4Sel(V4IsGrtr(resp, zero), V4Div(p84, resp), zero)); + + Vec4V bias = V4Scale(V4MulAdd(t0Z, errorZ, V4MulAdd(t0Y, errorY, V4Mul(t0X, errorX))), invDt); + + Vec4V targetVel = V4MulAdd(t0Z, targetVelZ,V4MulAdd(t0Y, targetVelY, V4Mul(t0X, targetVelX))); + targetVel = V4Sub(targetVel, vrel); + f0->targetVelocity = V4Neg(V4Mul(targetVel, velMultiplier)); + bias = V4Sub(bias, targetVel); + + f0->raXnX = delAngVel0X; + f0->raXnY = delAngVel0Y; + f0->raXnZ = delAngVel0Z; + f0->scaledBias = V4Mul(bias, velMultiplier); + f0->velMultiplier = velMultiplier; + } + + { + Vec4V raXnX = V4NegMulSub(raZ, t1Y, V4Mul(raY, t1Z)); + Vec4V raXnY = V4NegMulSub(raX, t1Z, V4Mul(raZ, t1X)); + Vec4V raXnZ = V4NegMulSub(raY, t1X, V4Mul(raX, t1Y)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + + Vec4V resp = V4MulAdd(dotDelAngVel0, angDom0, invMass0D0); + + const Vec4V tVel0 = V4MulAdd(t1Z, linVelT20, V4MulAdd(t1Y, linVelT10, V4Mul(t1X, linVelT00))); + Vec4V vrel = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4MulAdd(raXnX, angVelT00, tVel0))); + + if(isDynamic) + { + SolverContactFrictionDynamic4* PX_RESTRICT dynamicF1 = static_cast(f1); + + Vec4V rbXnX = V4NegMulSub(rbZ, t1Y, V4Mul(rbY, t1Z)); + Vec4V rbXnY = V4NegMulSub(rbX, t1Z, V4Mul(rbZ, t1X)); + Vec4V rbXnZ = V4NegMulSub(rbY, t1X, V4Mul(rbX, t1Y)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + Vec4V delAngVel1X = V4Mul(invInertia1X0, rbXnX); + Vec4V delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + Vec4V delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + resp = V4Add(resp, resp1); + + dynamicF1->rbXnX = delAngVel1X; + dynamicF1->rbXnY = delAngVel1Y; + dynamicF1->rbXnZ = delAngVel1Z; + + const Vec4V tVel1 = V4MulAdd(t1Z, linVelT21, V4MulAdd(t1Y, linVelT11, V4Mul(t1X, linVelT01))); + const Vec4V vel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + + vrel = V4Sub(vrel, vel1); + + } + else if(hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, t1Y, V4Mul(rbY, t1Z)); + const Vec4V rbXnY = V4NegMulSub(rbX, t1Z, V4Mul(rbZ, t1X)); + const Vec4V rbXnZ = V4NegMulSub(rbY, t1X, V4Mul(rbX, t1Y)); + + const Vec4V tVel1 = V4MulAdd(t1Z, linVelT21, V4MulAdd(t1Y, linVelT11, V4Mul(t1X, linVelT01))); + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + + vrel = V4Sub(vrel, dotRbXnAngVel1); + } + + + const Vec4V velMultiplier = V4Mul(maxImpulseScale, V4Sel(V4IsGrtr(resp, zero), V4Div(p84, resp), zero)); + + Vec4V bias = V4Scale(V4MulAdd(t1Z, errorZ, V4MulAdd(t1Y, errorY, V4Mul(t1X, errorX))), invDt); + + Vec4V targetVel = V4MulAdd(t1Z, targetVelZ,V4MulAdd(t1Y, targetVelY, V4Mul(t1X, targetVelX))); + targetVel = V4Sub(targetVel, vrel); + f1->targetVelocity = V4Neg(V4Mul(targetVel, velMultiplier)); + bias = V4Sub(bias, targetVel); + f1->raXnX = delAngVel0X; + f1->raXnY = delAngVel0Y; + f1->raXnZ = delAngVel0Z; + f1->scaledBias = V4Mul(bias, velMultiplier); + f1->velMultiplier = velMultiplier; + } + } + + frictionPatchWritebackAddrIndex0++; + frictionPatchWritebackAddrIndex1++; + frictionPatchWritebackAddrIndex2++; + frictionPatchWritebackAddrIndex3++; + } + } + } +} + + + +PX_FORCE_INLINE void computeBlockStreamFrictionByteSizes(const CorrelationBuffer& c, + PxU32& _frictionPatchByteSize, PxU32& _numFrictionPatches, + PxU32 frictionPatchStartIndex, PxU32 frictionPatchEndIndex) +{ + // PT: use local vars to remove LHS + PxU32 numFrictionPatches = 0; + + for(PxU32 i = frictionPatchStartIndex; i < frictionPatchEndIndex; i++) + { + //Friction patches. + if(c.correlationListHeads[i] != CorrelationBuffer::LIST_END) + numFrictionPatches++; + } + PxU32 frictionPatchByteSize = numFrictionPatches*sizeof(FrictionPatch); + + _numFrictionPatches = numFrictionPatches; + + //16-byte alignment. + _frictionPatchByteSize = ((frictionPatchByteSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_frictionPatchByteSize & 0x0f)); +} + +static bool reserveFrictionBlockStreams(const CorrelationBuffer& c, PxConstraintAllocator& constraintAllocator, PxU32 frictionPatchStartIndex, PxU32 frictionPatchEndIndex, + FrictionPatch*& _frictionPatches, + PxU32& numFrictionPatches) +{ + + //From frictionPatchStream we just need to reserve a single buffer. + PxU32 frictionPatchByteSize = 0; + //Compute the sizes of all the buffers. + + computeBlockStreamFrictionByteSizes(c, frictionPatchByteSize, numFrictionPatches, frictionPatchStartIndex, frictionPatchEndIndex); + + FrictionPatch* frictionPatches = NULL; + //If the constraint block reservation didn't fail then reserve the friction buffer too. + if(frictionPatchByteSize > 0) + { + frictionPatches = reinterpret_cast(constraintAllocator.reserveFrictionData(frictionPatchByteSize)); + + if(0==frictionPatches || (reinterpret_cast(-1))==frictionPatches) + { + if(0==frictionPatches) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of friction data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + frictionPatches=NULL; + } + } + } + + _frictionPatches = frictionPatches; + + //Return true if neither of the two block reservations failed. + return (0==frictionPatchByteSize || frictionPatches); +} + +//The persistent friction patch correlation/allocation will already have happenned as this is per-pair. +//This function just computes the size of the combined solve data. +void computeBlockStreamByteSizes4(PxSolverContactDesc* descs, + PxU32& _solverConstraintByteSize, PxU32* _axisConstraintCount, + const CorrelationBuffer& c) +{ + PX_ASSERT(0 == _solverConstraintByteSize); + + PxU32 maxPatches = 0; + PxU32 maxFrictionPatches = 0; + PxU32 maxContactCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxU32 maxFrictionCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxMemZero(maxContactCount, sizeof(maxContactCount)); + PxMemZero(maxFrictionCount, sizeof(maxFrictionCount)); + bool hasMaxImpulse = false; + + for(PxU32 a = 0; a < 4; ++a) + { + PxU32 axisConstraintCount = 0; + hasMaxImpulse = hasMaxImpulse || descs[a].hasMaxImpulse; + for(PxU32 i = 0; i < descs[a].numFrictionPatches; i++) + { + PxU32 ind = i + descs[a].startFrictionPatchIndex; + + const FrictionPatch& frictionPatch = c.frictionPatches[ind]; + + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0 + && frictionPatch.anchorCount != 0; + //Solver constraint data. + if(c.frictionPatchContactCounts[ind]!=0) + { + maxContactCount[i] = PxMax(c.frictionPatchContactCounts[ind], maxContactCount[i]); + axisConstraintCount += c.frictionPatchContactCounts[ind]; + + if(haveFriction) + { + const PxU32 fricCount = PxU32(c.frictionPatches[ind].anchorCount) * 2; + maxFrictionCount[i] = PxMax(fricCount, maxFrictionCount[i]); + axisConstraintCount += fricCount; + } + } + } + maxPatches = PxMax(descs[a].numFrictionPatches, maxPatches); + _axisConstraintCount[a] = axisConstraintCount; + } + + for(PxU32 a = 0; a < maxPatches; ++a) + { + if(maxFrictionCount[a] > 0) + maxFrictionPatches++; + } + + + PxU32 totalContacts = 0, totalFriction = 0; + for(PxU32 a = 0; a < maxPatches; ++a) + { + totalContacts += maxContactCount[a]; + totalFriction += maxFrictionCount[a]; + } + + //OK, we have a given number of friction patches, contact points and friction constraints so we can calculate how much memory we need + + //Body 2 is considered static if it is either *not dynamic* or *kinematic* + + bool hasDynamicBody = false; + for(PxU32 a = 0; a < 4; ++a) + { + hasDynamicBody = hasDynamicBody || ((descs[a].bodyState1 == PxSolverContactDesc::eDYNAMIC_BODY)); + } + + + const bool isStatic = !hasDynamicBody; + + const PxU32 headerSize = sizeof(SolverContactHeader4) * maxPatches + sizeof(SolverFrictionSharedData4) * maxFrictionPatches; + PxU32 constraintSize = isStatic ? (sizeof(SolverContactBatchPointBase4) * totalContacts) + ( sizeof(SolverContactFrictionBase4) * totalFriction) : + (sizeof(SolverContactBatchPointDynamic4) * totalContacts) + (sizeof(SolverContactFrictionDynamic4) * totalFriction); + + //Space for the appliedForce buffer + constraintSize += sizeof(Vec4V)*(totalContacts+totalFriction); + + //If we have max impulse, reserve a buffer for it + if(hasMaxImpulse) + constraintSize += sizeof(Ps::aos::Vec4V) * totalContacts; + + _solverConstraintByteSize = ((constraintSize + headerSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); +} + +static SolverConstraintPrepState::Enum reserveBlockStreams4(PxSolverContactDesc* descs, Dy::CorrelationBuffer& c, + PxU8*& solverConstraint, PxU32* axisConstraintCount, + PxU32& solverConstraintByteSize, + PxConstraintAllocator& constraintAllocator) +{ + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(0 == solverConstraintByteSize); + + //Compute the sizes of all the buffers. + computeBlockStreamByteSizes4(descs, + solverConstraintByteSize, axisConstraintCount, + c); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if(constraintBlockByteSize > 0) + { + if((constraintBlockByteSize + 16u) > 16384) + return SolverConstraintPrepState::eUNBATCHABLE; + + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if(0==constraintBlock || (reinterpret_cast(-1))==constraintBlock) + { + if(0==constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock=NULL; + } + } + } + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if(0==constraintBlockByteSize || constraintBlock) + { + if(solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0==(uintptr_t(solverConstraint) & 0x0f)); + } + } + + return ((0==constraintBlockByteSize || constraintBlock)) ? SolverConstraintPrepState::eSUCCESS : SolverConstraintPrepState::eOUT_OF_MEMORY; +} + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4( + Dy::CorrelationBuffer& c, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + PX_ALIGN(16, PxReal invMassScale0[4]); + PX_ALIGN(16, PxReal invMassScale1[4]); + PX_ALIGN(16, PxReal invInertiaScale0[4]); + PX_ALIGN(16, PxReal invInertiaScale1[4]); + + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + for (PxU32 a = 0; a < 4; ++a) + { + PxSolverContactDesc& blockDesc = blockDescs[a]; + + invMassScale0[a] = blockDesc.mInvMassScales.linear0; + invMassScale1[a] = blockDesc.mInvMassScales.linear1; + invInertiaScale0[a] = blockDesc.mInvMassScales.angular0; + invInertiaScale1[a] = blockDesc.mInvMassScales.angular1; + + blockDesc.startFrictionPatchIndex = c.frictionPatchCount; + if (!(blockDesc.disableStrongFriction)) + { + bool valid = getFrictionPatches(c, blockDesc.frictionPtr, blockDesc.frictionCount, + blockDesc.bodyFrame0, blockDesc.bodyFrame1, correlationDistance); + if (!valid) + return SolverConstraintPrepState::eUNBATCHABLE; + } + //Create the contact patches + blockDesc.startContactPatchIndex = c.contactPatchCount; + if (!createContactPatches(c, blockDesc.contacts, blockDesc.numContacts, PXC_SAME_NORMAL)) + return SolverConstraintPrepState::eUNBATCHABLE; + blockDesc.numContactPatches = PxU16(c.contactPatchCount - blockDesc.startContactPatchIndex); + + bool overflow = correlatePatches(c, blockDesc.contacts, blockDesc.bodyFrame0, blockDesc.bodyFrame1, PXC_SAME_NORMAL, + blockDesc.startContactPatchIndex, blockDesc.startFrictionPatchIndex); + + if (overflow) + return SolverConstraintPrepState::eUNBATCHABLE; + + growPatches(c, blockDesc.contacts, blockDesc.bodyFrame0, blockDesc.bodyFrame1, correlationDistance, blockDesc.startFrictionPatchIndex, + frictionOffsetThreshold + blockDescs[a].restDistance); + + //Remove the empty friction patches - do we actually need to do this? + for (PxU32 p = c.frictionPatchCount; p > blockDesc.startFrictionPatchIndex; --p) + { + if (c.correlationListHeads[p - 1] == 0xffff) + { + //We have an empty patch...need to bin this one... + for (PxU32 p2 = p; p2 < c.frictionPatchCount; ++p2) + { + c.correlationListHeads[p2 - 1] = c.correlationListHeads[p2]; + c.frictionPatchContactCounts[p2 - 1] = c.frictionPatchContactCounts[p2]; + } + c.frictionPatchCount--; + } + } + + PxU32 numFricPatches = c.frictionPatchCount - blockDesc.startFrictionPatchIndex; + blockDesc.numFrictionPatches = numFricPatches; + } + + FrictionPatch* frictionPatchArray[4]; + PxU32 frictionPatchCounts[4]; + + for (PxU32 a = 0; a < 4; ++a) + { + PxSolverContactDesc& blockDesc = blockDescs[a]; + + const bool successfulReserve = reserveFrictionBlockStreams(c, constraintAllocator, blockDesc.startFrictionPatchIndex, blockDesc.numFrictionPatches + blockDesc.startFrictionPatchIndex, + frictionPatchArray[a], + frictionPatchCounts[a]); + + //KS - TODO - how can we recover if we failed to allocate this memory? + if (!successfulReserve) + { + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + } + //At this point, all the friction data has been calculated, the correlation has been done. Provided this was all successful, + //we are ready to create the batched constraints + + PxU8* solverConstraint = NULL; + PxU32 solverConstraintByteSize = 0; + + + + { + PxU32 axisConstraintCount[4]; + SolverConstraintPrepState::Enum state = reserveBlockStreams4(blockDescs, c, + solverConstraint, axisConstraintCount, + solverConstraintByteSize, + constraintAllocator); + + if (state != SolverConstraintPrepState::eSUCCESS) + return state; + + + for (PxU32 a = 0; a < 4; ++a) + { + + FrictionPatch* frictionPatches = frictionPatchArray[a]; + + PxSolverContactDesc& blockDesc = blockDescs[a]; + PxSolverConstraintDesc& desc = *blockDesc.desc; + blockDesc.frictionPtr = reinterpret_cast(frictionPatches); + blockDesc.frictionCount = Ps::to8(frictionPatchCounts[a]); + + //Initialise friction buffer. + if (frictionPatches) + { + // PT: TODO: revisit this... not very satisfying + //const PxU32 maxSize = numFrictionPatches*sizeof(FrictionPatch); + Ps::prefetchLine(frictionPatches); + Ps::prefetchLine(frictionPatches, 128); + Ps::prefetchLine(frictionPatches, 256); + + for (PxU32 i = 0; i(solverConstraint + solverConstraintByteSize)) = 0; + } + return SolverConstraintPrepState::eSUCCESS; +} + + +//This returns 1 of 3 states: success, unbatchable or out-of-memory. If the constraint is unbatchable, we must fall back on 4 separate constraint +//prep calls +SolverConstraintPrepState::Enum createFinalizeSolverContacts4( + PxsContactManagerOutput** cmOutputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + + for (PxU32 a = 0; a < 4; ++a) + { + blockDescs[a].desc->constraintLengthOver16 = 0; + } + + PX_ASSERT(cmOutputs[0]->nbContacts && cmOutputs[1]->nbContacts && cmOutputs[2]->nbContacts && cmOutputs[3]->nbContacts); + + + Gu::ContactBuffer& buffer = threadContext.mContactBuffer; + + buffer.count = 0; + + //PxTransform idt = PxTransform(PxIdentity); + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + + for (PxU32 a = 0; a < 4; ++a) + { + PxSolverContactDesc& blockDesc = blockDescs[a]; + PxSolverConstraintDesc& desc = *blockDesc.desc; + + //blockDesc.startContactIndex = buffer.count; + blockDesc.contacts = buffer.contacts + buffer.count; + + Ps::prefetchLine(desc.bodyA); + Ps::prefetchLine(desc.bodyB); + + + if ((buffer.count + cmOutputs[a]->nbContacts) > 64) + { + return SolverConstraintPrepState::eUNBATCHABLE; + } + + bool hasMaxImpulse = false; + bool hasTargetVelocity = false; + + //OK...do the correlation here as well... + Ps::prefetchLine(blockDescs[a].frictionPtr); + Ps::prefetchLine(blockDescs[a].frictionPtr, 64); + Ps::prefetchLine(blockDescs[a].frictionPtr, 128); + + if (a < 3) + { + Ps::prefetchLine(cmOutputs[a]->contactPatches); + Ps::prefetchLine(cmOutputs[a]->contactPoints); + } + + PxReal invMassScale0, invMassScale1, invInertiaScale0, invInertiaScale1; + + const PxReal defaultMaxImpulse = PxMin(blockDesc.data0->maxContactImpulse, blockDesc.data1->maxContactImpulse); + + PxU32 contactCount = extractContacts(buffer, *cmOutputs[a], hasMaxImpulse, hasTargetVelocity, invMassScale0, invMassScale1, + invInertiaScale0, invInertiaScale1, defaultMaxImpulse); + + if (contactCount == 0) + return SolverConstraintPrepState::eUNBATCHABLE; + + blockDesc.numContacts = contactCount; + blockDesc.hasMaxImpulse = hasMaxImpulse; + blockDesc.disableStrongFriction = blockDesc.disableStrongFriction || hasTargetVelocity; + + blockDesc.mInvMassScales.linear0 *= invMassScale0; + blockDesc.mInvMassScales.linear1 *= invMassScale1; + blockDesc.mInvMassScales.angular0 *= invInertiaScale0; + blockDesc.mInvMassScales.angular1 *= invInertiaScale1; + + //blockDesc.frictionPtr = &blockDescs[a].frictionPtr; + //blockDesc.frictionCount = blockDescs[a].frictionCount; + + } + return createFinalizeSolverContacts4(c, blockDescs, + invDtF32, bounceThresholdF32, frictionOffsetThreshold, + correlationDistance, solverOffsetSlop, constraintAllocator); +} + + + + +} + +} + + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp new file mode 100644 index 000000000..0965f438c --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp @@ -0,0 +1,1030 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +//#include "PxvGeometry.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DySolverConstraintDesc.h" +#include "DySolverBody.h" +#include "DySolverContact4.h" +#include "DySolverContactPF4.h" + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DySolverExt.h" +#include "DyArticulationContactPrep.h" +#include "DyContactPrepShared.h" +#include "PsFoundation.h" + +using namespace physx::Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ +namespace Dy +{ + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb( + PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + PxFrictionType::Enum frictionType); + +static bool setupFinalizeSolverConstraintsCoulomb4(PxSolverContactDesc* PX_RESTRICT descs, PxU8* PX_RESTRICT workspace, + const PxReal invDtF32, PxReal bounceThresholdF32, PxReal solverOffsetSlopF32, CorrelationBuffer& c, const PxU32 numFrictionPerPoint, + const PxU32 numContactPoints4, const PxU32 /*solverConstraintByteSize*/, + const Ps::aos::Vec4VArg invMassScale0, const Ps::aos::Vec4VArg invInertiaScale0, + const Ps::aos::Vec4VArg invMassScale1, const Ps::aos::Vec4VArg invInertiaScale1) +{ + //KS - final step. Create the constraints in the place we pre-allocated... + + const Vec4V ccdMaxSeparation = Ps::aos::V4LoadXYZW(descs[0].maxCCDSeparation, descs[1].maxCCDSeparation, descs[2].maxCCDSeparation, descs[3].maxCCDSeparation); + const Vec4V solverOffsetSlop = V4Load(solverOffsetSlopF32); + + const Vec4V zero = V4Zero(); + + PxU8 flags[4] = { PxU8(descs[0].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[1].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[2].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[3].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0) }; + + + //The block is dynamic if **any** of the constraints have a non-static body B. This allows us to batch static and non-static constraints but we only get a memory/perf + //saving if all 4 are static. This simplifies the constraint partitioning such that it only needs to care about separating contacts and 1D constraints (which it already does) + const bool isDynamic = ((descs[0].bodyState1 | descs[1].bodyState1 | descs[2].bodyState1 | descs[3].bodyState1) & PxSolverContactDesc::eDYNAMIC_BODY) != 0; + + const PxU32 constraintSize = isDynamic ? sizeof(SolverContact4Dynamic) : sizeof(SolverContact4Base); + const PxU32 frictionSize = isDynamic ? sizeof(SolverFriction4Dynamic) : sizeof(SolverFriction4Base); + + PxU8* PX_RESTRICT ptr = workspace; + + const Vec4V dom0 = invMassScale0; + const Vec4V dom1 = invMassScale1; + const Vec4V angDom0 = invInertiaScale0; + const Vec4V angDom1 = invInertiaScale1; + + const Vec4V maxPenBias = V4Max(V4Merge(FLoad(descs[0].data0->penBiasClamp), FLoad(descs[1].data0->penBiasClamp), + FLoad(descs[2].data0->penBiasClamp), FLoad(descs[3].data0->penBiasClamp)), + V4Merge(FLoad(descs[0].data1->penBiasClamp), FLoad(descs[1].data1->penBiasClamp), + FLoad(descs[2].data1->penBiasClamp), FLoad(descs[3].data1->penBiasClamp))); + + const Vec4V restDistance = V4Merge(FLoad(descs[0].restDistance), FLoad(descs[1].restDistance), FLoad(descs[2].restDistance), + FLoad(descs[3].restDistance)); + + //load up velocities + Vec4V linVel00 = V4LoadA(&descs[0].data0->linearVelocity.x); + Vec4V linVel10 = V4LoadA(&descs[1].data0->linearVelocity.x); + Vec4V linVel20 = V4LoadA(&descs[2].data0->linearVelocity.x); + Vec4V linVel30 = V4LoadA(&descs[3].data0->linearVelocity.x); + + Vec4V linVel01 = V4LoadA(&descs[0].data1->linearVelocity.x); + Vec4V linVel11 = V4LoadA(&descs[1].data1->linearVelocity.x); + Vec4V linVel21 = V4LoadA(&descs[2].data1->linearVelocity.x); + Vec4V linVel31 = V4LoadA(&descs[3].data1->linearVelocity.x); + + Vec4V angVel00 = V4LoadA(&descs[0].data0->angularVelocity.x); + Vec4V angVel10 = V4LoadA(&descs[1].data0->angularVelocity.x); + Vec4V angVel20 = V4LoadA(&descs[2].data0->angularVelocity.x); + Vec4V angVel30 = V4LoadA(&descs[3].data0->angularVelocity.x); + + Vec4V angVel01 = V4LoadA(&descs[0].data1->angularVelocity.x); + Vec4V angVel11 = V4LoadA(&descs[1].data1->angularVelocity.x); + Vec4V angVel21 = V4LoadA(&descs[2].data1->angularVelocity.x); + Vec4V angVel31 = V4LoadA(&descs[3].data1->angularVelocity.x); + + Vec4V linVelT00, linVelT10, linVelT20; + Vec4V linVelT01, linVelT11, linVelT21; + Vec4V angVelT00, angVelT10, angVelT20; + Vec4V angVelT01, angVelT11, angVelT21; + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVelT00, linVelT10, linVelT20); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVelT01, linVelT11, linVelT21); + PX_TRANSPOSE_44_34(angVel00, angVel10, angVel20, angVel30, angVelT00, angVelT10, angVelT20); + PX_TRANSPOSE_44_34(angVel01, angVel11, angVel21, angVel31, angVelT01, angVelT11, angVelT21); + + const Vec4V vrelX = V4Sub(linVelT00, linVelT01); + const Vec4V vrelY = V4Sub(linVelT10, linVelT11); + const Vec4V vrelZ = V4Sub(linVelT20, linVelT21); + + + + //Load up masses and invInertia + + const Vec4V invMass0 = V4Merge(FLoad(descs[0].data0->invMass), FLoad(descs[1].data0->invMass), FLoad(descs[2].data0->invMass), + FLoad(descs[3].data0->invMass)); + + const Vec4V invMass1 = V4Merge(FLoad(descs[0].data1->invMass), FLoad(descs[1].data1->invMass), FLoad(descs[2].data1->invMass), + FLoad(descs[3].data1->invMass)); + + const Vec4V invMass0_dom0fV = V4Mul(dom0, invMass0); + const Vec4V invMass1_dom1fV = V4Mul(dom1, invMass1); + + Vec4V invInertia00X = Vec4V_From_Vec3V(V3LoadU(descs[0].data0->sqrtInvInertia.column0)); + Vec4V invInertia00Y = Vec4V_From_Vec3V(V3LoadU(descs[0].data0->sqrtInvInertia.column1)); + Vec4V invInertia00Z = Vec4V_From_Vec3V(V3LoadU(descs[0].data0->sqrtInvInertia.column2)); + + Vec4V invInertia10X = Vec4V_From_Vec3V(V3LoadU(descs[1].data0->sqrtInvInertia.column0)); + Vec4V invInertia10Y = Vec4V_From_Vec3V(V3LoadU(descs[1].data0->sqrtInvInertia.column1)); + Vec4V invInertia10Z = Vec4V_From_Vec3V(V3LoadU(descs[1].data0->sqrtInvInertia.column2)); + + Vec4V invInertia20X = Vec4V_From_Vec3V(V3LoadU(descs[2].data0->sqrtInvInertia.column0)); + Vec4V invInertia20Y = Vec4V_From_Vec3V(V3LoadU(descs[2].data0->sqrtInvInertia.column1)); + Vec4V invInertia20Z = Vec4V_From_Vec3V(V3LoadU(descs[2].data0->sqrtInvInertia.column2)); + + Vec4V invInertia30X = Vec4V_From_Vec3V(V3LoadU(descs[3].data0->sqrtInvInertia.column0)); + Vec4V invInertia30Y = Vec4V_From_Vec3V(V3LoadU(descs[3].data0->sqrtInvInertia.column1)); + Vec4V invInertia30Z = Vec4V_From_Vec3V(V3LoadU(descs[3].data0->sqrtInvInertia.column2)); + + Vec4V invInertia01X = Vec4V_From_Vec3V(V3LoadU(descs[0].data1->sqrtInvInertia.column0)); + Vec4V invInertia01Y = Vec4V_From_Vec3V(V3LoadU(descs[0].data1->sqrtInvInertia.column1)); + Vec4V invInertia01Z = Vec4V_From_Vec3V(V3LoadU(descs[0].data1->sqrtInvInertia.column2)); + + Vec4V invInertia11X = Vec4V_From_Vec3V(V3LoadU(descs[1].data1->sqrtInvInertia.column0)); + Vec4V invInertia11Y = Vec4V_From_Vec3V(V3LoadU(descs[1].data1->sqrtInvInertia.column1)); + Vec4V invInertia11Z = Vec4V_From_Vec3V(V3LoadU(descs[1].data1->sqrtInvInertia.column2)); + + Vec4V invInertia21X = Vec4V_From_Vec3V(V3LoadU(descs[2].data1->sqrtInvInertia.column0)); + Vec4V invInertia21Y = Vec4V_From_Vec3V(V3LoadU(descs[2].data1->sqrtInvInertia.column1)); + Vec4V invInertia21Z = Vec4V_From_Vec3V(V3LoadU(descs[2].data1->sqrtInvInertia.column2)); + + Vec4V invInertia31X = Vec4V_From_Vec3V(V3LoadU(descs[3].data1->sqrtInvInertia.column0)); + Vec4V invInertia31Y = Vec4V_From_Vec3V(V3LoadU(descs[3].data1->sqrtInvInertia.column1)); + Vec4V invInertia31Z = Vec4V_From_Vec3V(V3LoadU(descs[3].data1->sqrtInvInertia.column2)); + + Vec4V invInertia0X0, invInertia0X1, invInertia0X2; + Vec4V invInertia0Y0, invInertia0Y1, invInertia0Y2; + Vec4V invInertia0Z0, invInertia0Z1, invInertia0Z2; + + Vec4V invInertia1X0, invInertia1X1, invInertia1X2; + Vec4V invInertia1Y0, invInertia1Y1, invInertia1Y2; + Vec4V invInertia1Z0, invInertia1Z1, invInertia1Z2; + + PX_TRANSPOSE_44_34(invInertia00X, invInertia10X, invInertia20X, invInertia30X, invInertia0X0, invInertia0Y0, invInertia0Z0); + PX_TRANSPOSE_44_34(invInertia00Y, invInertia10Y, invInertia20Y, invInertia30Y, invInertia0X1, invInertia0Y1, invInertia0Z1); + PX_TRANSPOSE_44_34(invInertia00Z, invInertia10Z, invInertia20Z, invInertia30Z, invInertia0X2, invInertia0Y2, invInertia0Z2); + + PX_TRANSPOSE_44_34(invInertia01X, invInertia11X, invInertia21X, invInertia31X, invInertia1X0, invInertia1Y0, invInertia1Z0); + PX_TRANSPOSE_44_34(invInertia01Y, invInertia11Y, invInertia21Y, invInertia31Y, invInertia1X1, invInertia1Y1, invInertia1Z1); + PX_TRANSPOSE_44_34(invInertia01Z, invInertia11Z, invInertia21Z, invInertia31Z, invInertia1X2, invInertia1Y2, invInertia1Z2); + + const FloatV invDt = FLoad(invDtF32); + const FloatV p8 = FLoad(0.8f); + //const Vec4V p84 = V4Splat(p8); + const Vec4V p1 = V4Splat(FLoad(0.1f)); + const Vec4V bounceThreshold = V4Splat(FLoad(bounceThresholdF32)); + const Vec4V orthoThreshold = V4Splat(FLoad(0.70710678f)); + + const FloatV invDtp8 = FMul(invDt, p8); + + const Vec3V bodyFrame00p = V3LoadU(descs[0].bodyFrame0.p); + const Vec3V bodyFrame01p = V3LoadU(descs[1].bodyFrame0.p); + const Vec3V bodyFrame02p = V3LoadU(descs[2].bodyFrame0.p); + const Vec3V bodyFrame03p = V3LoadU(descs[3].bodyFrame0.p); + + Vec4V bodyFrame00p4 = Vec4V_From_Vec3V(bodyFrame00p); + Vec4V bodyFrame01p4 = Vec4V_From_Vec3V(bodyFrame01p); + Vec4V bodyFrame02p4 = Vec4V_From_Vec3V(bodyFrame02p); + Vec4V bodyFrame03p4 = Vec4V_From_Vec3V(bodyFrame03p); + + Vec4V bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ; + PX_TRANSPOSE_44_34(bodyFrame00p4, bodyFrame01p4, bodyFrame02p4, bodyFrame03p4, bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ); + + + const Vec3V bodyFrame10p = V3LoadU(descs[0].bodyFrame1.p); + const Vec3V bodyFrame11p = V3LoadU(descs[1].bodyFrame1.p); + const Vec3V bodyFrame12p = V3LoadU(descs[2].bodyFrame1.p); + const Vec3V bodyFrame13p = V3LoadU(descs[3].bodyFrame1.p); + + Vec4V bodyFrame10p4 = Vec4V_From_Vec3V(bodyFrame10p); + Vec4V bodyFrame11p4 = Vec4V_From_Vec3V(bodyFrame11p); + Vec4V bodyFrame12p4 = Vec4V_From_Vec3V(bodyFrame12p); + Vec4V bodyFrame13p4 = Vec4V_From_Vec3V(bodyFrame13p); + + Vec4V bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ; + PX_TRANSPOSE_44_34(bodyFrame10p4, bodyFrame11p4, bodyFrame12p4, bodyFrame13p4, bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ); + + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + PxU32 frictionIndex0 = 0, frictionIndex1 = 0, frictionIndex2 = 0, frictionIndex3 = 0; + + + PxU32 maxPatches = PxMax(descs[0].numFrictionPatches, PxMax(descs[1].numFrictionPatches, PxMax(descs[2].numFrictionPatches, descs[3].numFrictionPatches))); + PxU32 maxContacts = numContactPoints4; + + //This is the address at which the first friction patch exists + PxU8* ptr2 = ptr + ((sizeof(SolverContactCoulombHeader4) * maxPatches) + constraintSize * maxContacts); + + //PxU32 contactId = 0; + + for(PxU32 i=0;i= descs[0].numFrictionPatches; + const bool hasFinished1 = i >= descs[1].numFrictionPatches; + const bool hasFinished2 = i >= descs[2].numFrictionPatches; + const bool hasFinished3 = i >= descs[3].numFrictionPatches; + + + frictionIndex0 = hasFinished0 ? frictionIndex0 : descs[0].startFrictionPatchIndex + i; + frictionIndex1 = hasFinished1 ? frictionIndex1 : descs[1].startFrictionPatchIndex + i; + frictionIndex2 = hasFinished2 ? frictionIndex2 : descs[2].startFrictionPatchIndex + i; + frictionIndex3 = hasFinished3 ? frictionIndex3 : descs[3].startFrictionPatchIndex + i; + + PxU32 clampedContacts0 = hasFinished0 ? 0 : c.frictionPatchContactCounts[frictionIndex0]; + PxU32 clampedContacts1 = hasFinished1 ? 0 : c.frictionPatchContactCounts[frictionIndex1]; + PxU32 clampedContacts2 = hasFinished2 ? 0 : c.frictionPatchContactCounts[frictionIndex2]; + PxU32 clampedContacts3 = hasFinished3 ? 0 : c.frictionPatchContactCounts[frictionIndex3]; + + PxU32 clampedFric0 = clampedContacts0 * numFrictionPerPoint; + PxU32 clampedFric1 = clampedContacts1 * numFrictionPerPoint; + PxU32 clampedFric2 = clampedContacts2 * numFrictionPerPoint; + PxU32 clampedFric3 = clampedContacts3 * numFrictionPerPoint; + + + const PxU32 numContacts = PxMax(clampedContacts0, PxMax(clampedContacts1, PxMax(clampedContacts2, clampedContacts3))); + const PxU32 numFrictions = PxMax(clampedFric0, PxMax(clampedFric1, PxMax(clampedFric2, clampedFric3))); + + PxU32 firstPatch0 = c.correlationListHeads[frictionIndex0]; + PxU32 firstPatch1 = c.correlationListHeads[frictionIndex1]; + PxU32 firstPatch2 = c.correlationListHeads[frictionIndex2]; + PxU32 firstPatch3 = c.correlationListHeads[frictionIndex3]; + + const Gu::ContactPoint* contactBase0 = descs[0].contacts + c.contactPatches[firstPatch0].start; + const Gu::ContactPoint* contactBase1 = descs[1].contacts + c.contactPatches[firstPatch1].start; + const Gu::ContactPoint* contactBase2 = descs[2].contacts + c.contactPatches[firstPatch2].start; + const Gu::ContactPoint* contactBase3 = descs[3].contacts + c.contactPatches[firstPatch3].start; + + const Vec4V restitution = V4Merge(FLoad(contactBase0->restitution), FLoad(contactBase1->restitution), FLoad(contactBase2->restitution), + FLoad(contactBase3->restitution)); + + const Vec4V staticFriction = V4Merge(FLoad(contactBase0->staticFriction), FLoad(contactBase1->staticFriction), FLoad(contactBase2->staticFriction), + FLoad(contactBase3->staticFriction)); + + SolverContactCoulombHeader4* PX_RESTRICT header = reinterpret_cast(ptr); + + header->frictionOffset = PxU16(ptr2 - ptr); + + ptr += sizeof(SolverContactCoulombHeader4); + + SolverFrictionHeader4* PX_RESTRICT fricHeader = reinterpret_cast(ptr2); + ptr2 += sizeof(SolverFrictionHeader4) + sizeof(Vec4V) * numContacts; + + + header->numNormalConstr0 = Ps::to8(clampedContacts0); + header->numNormalConstr1 = Ps::to8(clampedContacts1); + header->numNormalConstr2 = Ps::to8(clampedContacts2); + header->numNormalConstr3 = Ps::to8(clampedContacts3); + header->numNormalConstr = Ps::to8(numContacts); + header->invMassADom = invMass0_dom0fV; + header->invMassBDom = invMass1_dom1fV; + header->angD0 = angDom0; + header->angD1 = angDom1; + header->restitution = restitution; + + header->flags[0] = flags[0]; header->flags[1] = flags[1]; header->flags[2] = flags[2]; header->flags[3] = flags[3]; + + header->type = Ps::to8(isDynamic ? DY_SC_TYPE_BLOCK_RB_CONTACT : DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT); + header->shapeInteraction[0] = descs[0].shapeInteraction; header->shapeInteraction[1] = descs[1].shapeInteraction; + header->shapeInteraction[2] = descs[2].shapeInteraction; header->shapeInteraction[3] = descs[3].shapeInteraction; + + + fricHeader->invMassADom = invMass0_dom0fV; + fricHeader->invMassBDom = invMass1_dom1fV; + fricHeader->angD0 = angDom0; + fricHeader->angD1 = angDom1; + fricHeader->numFrictionConstr0 = Ps::to8(clampedFric0); + fricHeader->numFrictionConstr1 = Ps::to8(clampedFric1); + fricHeader->numFrictionConstr2 = Ps::to8(clampedFric2); + fricHeader->numFrictionConstr3 = Ps::to8(clampedFric3); + fricHeader->numNormalConstr = Ps::to8(numContacts); + fricHeader->numNormalConstr0 = Ps::to8(clampedContacts0); + fricHeader->numNormalConstr1 = Ps::to8(clampedContacts1); + fricHeader->numNormalConstr2 = Ps::to8(clampedContacts2); + fricHeader->numNormalConstr3 = Ps::to8(clampedContacts3); + fricHeader->type = Ps::to8(isDynamic ? DY_SC_TYPE_BLOCK_FRICTION : DY_SC_TYPE_BLOCK_STATIC_FRICTION); + fricHeader->staticFriction = staticFriction; + fricHeader->frictionPerContact = PxU32(numFrictionPerPoint == 2 ? 1 : 0); + + fricHeader->numFrictionConstr = Ps::to8(numFrictions); + + Vec4V normal0 = V4LoadA(&contactBase0->normal.x); + Vec4V normal1 = V4LoadA(&contactBase1->normal.x); + Vec4V normal2 = V4LoadA(&contactBase2->normal.x); + Vec4V normal3 = V4LoadA(&contactBase3->normal.x); + + Vec4V normalX, normalY, normalZ; + PX_TRANSPOSE_44_34(normal0, normal1, normal2, normal3, normalX, normalY, normalZ); + header->normalX = normalX; + header->normalY = normalY; + header->normalZ = normalZ; + + const Vec4V normalLenSq = V4MulAdd(normalZ, normalZ, V4MulAdd(normalY, normalY, V4Mul(normalX, normalX))); + + const Vec4V linNorVel0 = V4MulAdd(normalZ, linVelT20, V4MulAdd(normalY, linVelT10, V4Mul(normalX, linVelT00))); + const Vec4V linNorVel1 = V4MulAdd(normalZ, linVelT21, V4MulAdd(normalY, linVelT11, V4Mul(normalX, linVelT01))); + + const Vec4V invMassNorLenSq0 = V4Mul(invMass0_dom0fV, normalLenSq); + const Vec4V invMassNorLenSq1 = V4Mul(invMass1_dom1fV, normalLenSq); + + + //Calculate friction directions + const BoolV cond =V4IsGrtr(orthoThreshold, V4Abs(normalX)); + + const Vec4V t0FallbackX = V4Sel(cond, zero, V4Neg(normalY)); + const Vec4V t0FallbackY = V4Sel(cond, V4Neg(normalZ), normalX); + const Vec4V t0FallbackZ = V4Sel(cond, normalY, zero); + + const Vec4V dotNormalVrel = V4MulAdd(normalZ, vrelZ, V4MulAdd(normalY, vrelY, V4Mul(normalX, vrelX))); + const Vec4V vrelSubNorVelX = V4NegMulSub(normalX, dotNormalVrel, vrelX); + const Vec4V vrelSubNorVelY = V4NegMulSub(normalY, dotNormalVrel, vrelY); + const Vec4V vrelSubNorVelZ = V4NegMulSub(normalZ, dotNormalVrel, vrelZ); + + const Vec4V lenSqvrelSubNorVelZ = V4MulAdd(vrelSubNorVelX, vrelSubNorVelX, V4MulAdd(vrelSubNorVelY, vrelSubNorVelY, V4Mul(vrelSubNorVelZ, vrelSubNorVelZ))); + + const BoolV bcon2 = V4IsGrtr(lenSqvrelSubNorVelZ, p1); + + Vec4V t0X = V4Sel(bcon2, vrelSubNorVelX, t0FallbackX); + Vec4V t0Y = V4Sel(bcon2, vrelSubNorVelY, t0FallbackY); + Vec4V t0Z = V4Sel(bcon2, vrelSubNorVelZ, t0FallbackZ); + + //Now normalize this... + const Vec4V recipLen = V4Rsqrt(V4MulAdd(t0X, t0X, V4MulAdd(t0Y, t0Y, V4Mul(t0Z, t0Z)))); + + t0X = V4Mul(t0X, recipLen); + t0Y = V4Mul(t0Y, recipLen); + t0Z = V4Mul(t0Z, recipLen); + + const Vec4V t1X = V4NegMulSub(normalZ, t0Y, V4Mul(normalY, t0Z)); + const Vec4V t1Y = V4NegMulSub(normalX, t0Z, V4Mul(normalZ, t0X)); + const Vec4V t1Z = V4NegMulSub(normalY, t0X, V4Mul(normalX, t0Y)); + + const Vec4V tFallbackX[2] = {t0X, t1X}; + const Vec4V tFallbackY[2] = {t0Y, t1Y}; + const Vec4V tFallbackZ[2] = {t0Z, t1Z}; + + + //For all correlation heads - need to pull this out I think + + //OK, we have a counter for all our patches... + PxU32 finished = (PxU32(hasFinished0)) | + ((PxU32(hasFinished1)) << 1) | + ((PxU32(hasFinished2)) << 2) | + ((PxU32(hasFinished3)) << 3); + + CorrelationListIterator iter0(c, firstPatch0); + CorrelationListIterator iter1(c, firstPatch1); + CorrelationListIterator iter2(c, firstPatch2); + CorrelationListIterator iter3(c, firstPatch3); + + PxU32 contact0, contact1, contact2, contact3; + PxU32 patch0, patch1, patch2, patch3; + + iter0.nextContact(patch0, contact0); + iter1.nextContact(patch1, contact1); + iter2.nextContact(patch2, contact2); + iter3.nextContact(patch3, contact3); + + PxU8* p = ptr; + + PxU32 contactCount = 0; + PxU32 newFinished = + (PxU32(hasFinished0 || !iter0.hasNextContact())) | + ((PxU32(hasFinished1 || !iter1.hasNextContact())) << 1) | + ((PxU32(hasFinished2 || !iter2.hasNextContact())) << 2) | + ((PxU32(hasFinished3 || !iter3.hasNextContact())) << 3); + + PxU32 fricIndex = 0; + + while(finished != 0xf) + { + finished = newFinished; + ++contactCount; + Ps::prefetchLine(p, 384); + Ps::prefetchLine(p, 512); + Ps::prefetchLine(p, 640); + + SolverContact4Base* PX_RESTRICT solverContact = reinterpret_cast(p); + p += constraintSize; + + const Gu::ContactPoint& con0 = descs[0].contacts[c.contactPatches[patch0].start + contact0]; + const Gu::ContactPoint& con1 = descs[1].contacts[c.contactPatches[patch1].start + contact1]; + const Gu::ContactPoint& con2 = descs[2].contacts[c.contactPatches[patch2].start + contact2]; + const Gu::ContactPoint& con3 = descs[3].contacts[c.contactPatches[patch3].start + contact3]; + + //Now we need to splice these 4 contacts into a single structure + + { + Vec4V point0 = V4LoadA(&con0.point.x); + Vec4V point1 = V4LoadA(&con1.point.x); + Vec4V point2 = V4LoadA(&con2.point.x); + Vec4V point3 = V4LoadA(&con3.point.x); + + Vec4V pointX, pointY, pointZ; + PX_TRANSPOSE_44_34(point0, point1, point2, point3, pointX, pointY, pointZ); + + Vec4V targetVel0 = V4LoadA(&con0.targetVel.x); + Vec4V targetVel1 = V4LoadA(&con1.targetVel.x); + Vec4V targetVel2 = V4LoadA(&con2.targetVel.x); + Vec4V targetVel3 = V4LoadA(&con3.targetVel.x); + + Vec4V targetVelX, targetVelY, targetVelZ; + PX_TRANSPOSE_44_34(targetVel0, targetVel1, targetVel2, targetVel3, targetVelX, targetVelY, targetVelZ); + + Vec4V raX = V4Sub(pointX, bodyFrame0pX); + Vec4V raY = V4Sub(pointY, bodyFrame0pY); + Vec4V raZ = V4Sub(pointZ, bodyFrame0pZ); + + Vec4V rbX = V4Sub(pointX, bodyFrame1pX); + Vec4V rbY = V4Sub(pointY, bodyFrame1pY); + Vec4V rbZ = V4Sub(pointZ, bodyFrame1pZ); + + raX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raX)), zero, raX); + raY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raY)), zero, raY); + raZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raZ)), zero, raZ); + + rbX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbX)), zero, rbX); + rbY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbY)), zero, rbY); + rbZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbZ)), zero, rbZ); + + { + const Vec4V separation = V4Merge(FLoad(con0.separation), FLoad(con1.separation), FLoad(con2.separation), + FLoad(con3.separation)); + const Vec4V maxImpulse = V4Merge(FLoad(con0.maxImpulse), FLoad(con1.maxImpulse), FLoad(con2.maxImpulse), + FLoad(con3.maxImpulse)); + + const Vec4V cTargetVel = V4MulAdd(normalX, targetVelX, V4MulAdd(normalY, targetVelY, V4Mul(normalZ, targetVelZ))); + + //raXn = cross(ra, normal) which = Vec3V( a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x); + const Vec4V raXnX = V4NegMulSub(raZ, normalY, V4Mul(raY, normalZ)); + const Vec4V raXnY = V4NegMulSub(raX, normalZ, V4Mul(raZ, normalX)); + const Vec4V raXnZ = V4NegMulSub(raY, normalX, V4Mul(raX, normalY)); + + const Vec4V v0a0 = V4Mul(invInertia0X0, raXnX); + const Vec4V v0a1 = V4Mul(invInertia0X1, raXnX); + const Vec4V v0a2 = V4Mul(invInertia0X2, raXnX); + + const Vec4V v0PlusV1a0 = V4MulAdd(invInertia0Y0, raXnY, v0a0); + const Vec4V v0PlusV1a1 = V4MulAdd(invInertia0Y1, raXnY, v0a1); + const Vec4V v0PlusV1a2 = V4MulAdd(invInertia0Y2, raXnY, v0a2); + + const Vec4V delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, v0PlusV1a0); + const Vec4V delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, v0PlusV1a1); + const Vec4V delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, v0PlusV1a2); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + const Vec4V dotRaXnAngVel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4Mul(raXnX, angVelT00))); + + Vec4V unitResponse = V4Add(invMassNorLenSq0, dotDelAngVel0); + Vec4V vrel = V4Add(linNorVel0, dotRaXnAngVel0); + + + //The dynamic-only parts - need to if-statement these up. A branch here shouldn't cost us too much + if(isDynamic) + { + SolverContact4Dynamic* PX_RESTRICT dynamicContact = static_cast(solverContact); + const Vec4V rbXnX = V4NegMulSub(rbZ, normalY, V4Mul(rbY, normalZ)); + const Vec4V rbXnY = V4NegMulSub(rbX, normalZ, V4Mul(rbZ, normalX)); + const Vec4V rbXnZ = V4NegMulSub(rbY, normalX, V4Mul(rbX, normalY)); + + const Vec4V v0b0 = V4Mul(invInertia1X0, rbXnX); + const Vec4V v0b1 = V4Mul(invInertia1X1, rbXnX); + const Vec4V v0b2 = V4Mul(invInertia1X2, rbXnX); + + const Vec4V v0PlusV1b0 = V4MulAdd(invInertia1Y0, rbXnY, v0b0); + const Vec4V v0PlusV1b1 = V4MulAdd(invInertia1Y1, rbXnY, v0b1); + const Vec4V v0PlusV1b2 = V4MulAdd(invInertia1Y2, rbXnY, v0b2); + + const Vec4V delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, v0PlusV1b0); + const Vec4V delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, v0PlusV1b1); + const Vec4V delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, v0PlusV1b2); + + + //V3Dot(raXn, delAngVel0) + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + + const Vec4V resp1 = V4Add(dotDelAngVel1, invMassNorLenSq1); + + unitResponse = V4Add(unitResponse, resp1); + + const Vec4V vrel2 = V4Add(linNorVel1, dotRbXnAngVel1); + vrel = V4Sub(vrel, vrel2); + + //These are for dynamic-only contacts. + dynamicContact->rbXnX = delAngVel1X; + dynamicContact->rbXnY = delAngVel1Y; + dynamicContact->rbXnZ = delAngVel1Z; + + } + + const Vec4V velMultiplier = V4Sel(V4IsGrtr(unitResponse, zero), V4Recip(unitResponse), zero); + + const Vec4V penetration = V4Sub(separation, restDistance); + + const Vec4V penInvDtp8 = V4Max(maxPenBias, V4Scale(penetration, invDtp8)); + + Vec4V scaledBias = V4Mul(velMultiplier, penInvDtp8); + + const Vec4V penetrationInvDt = V4Scale(penetration, invDt); + + const BoolV isGreater2 = BAnd(BAnd(V4IsGrtr(restitution, zero), V4IsGrtr(bounceThreshold, vrel)), + V4IsGrtr(V4Neg(vrel), penetrationInvDt)); + + const BoolV ccdSeparationCondition = V4IsGrtrOrEq(ccdMaxSeparation, penetration); + + scaledBias = V4Sel(BAnd(ccdSeparationCondition, isGreater2), zero, scaledBias); + + const Vec4V sumVRel(vrel); + + const Vec4V targetVelocity = V4Sub(V4Add(V4Sel(isGreater2, V4Mul(V4Neg(sumVRel), restitution), zero), cTargetVel), vrel); + + //These values are present for static and dynamic contacts + solverContact->raXnX = delAngVel0X; + solverContact->raXnY = delAngVel0Y; + solverContact->raXnZ = delAngVel0Z; + solverContact->velMultiplier = velMultiplier; + solverContact->appliedForce = zero; + solverContact->scaledBias = scaledBias; + solverContact->targetVelocity = targetVelocity; + solverContact->maxImpulse = maxImpulse; + } + + //PxU32 conId = contactId++; + + /*Vec4V targetVel0 = V4LoadA(&con0.targetVel.x); + Vec4V targetVel1 = V4LoadA(&con1.targetVel.x); + Vec4V targetVel2 = V4LoadA(&con2.targetVel.x); + Vec4V targetVel3 = V4LoadA(&con3.targetVel.x); + + Vec4V targetVelX, targetVelY, targetVelZ; + PX_TRANSPOSE_44_34(targetVel0, targetVel1, targetVel2, targetVel3, targetVelX, targetVelY, targetVelZ);*/ + + for(PxU32 a = 0; a < numFrictionPerPoint; ++a) + { + SolverFriction4Base* PX_RESTRICT friction = reinterpret_cast(ptr2); + + ptr2 += frictionSize; + + const Vec4V tX = tFallbackX[fricIndex]; + const Vec4V tY = tFallbackY[fricIndex]; + const Vec4V tZ = tFallbackZ[fricIndex]; + + fricIndex = 1 - fricIndex; + + const Vec4V raXnX = V4NegMulSub(raZ, tY, V4Mul(raY, tZ)); + const Vec4V raXnY = V4NegMulSub(raX, tZ, V4Mul(raZ, tX)); + const Vec4V raXnZ = V4NegMulSub(raY, tX, V4Mul(raX, tY)); + + const Vec4V v0a0 = V4Mul(invInertia0X0, raXnX); + const Vec4V v0a1 = V4Mul(invInertia0X1, raXnX); + const Vec4V v0a2 = V4Mul(invInertia0X2, raXnX); + + const Vec4V v0PlusV1a0 = V4MulAdd(invInertia0Y0, raXnY, v0a0); + const Vec4V v0PlusV1a1 = V4MulAdd(invInertia0Y1, raXnY, v0a1); + const Vec4V v0PlusV1a2 = V4MulAdd(invInertia0Y2, raXnY, v0a2); + + const Vec4V delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, v0PlusV1a0); + const Vec4V delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, v0PlusV1a1); + const Vec4V delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, v0PlusV1a2); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + + const Vec4V norVel0 = V4MulAdd(tX, linVelT00, V4MulAdd(tY, linVelT10, V4Mul(tZ, linVelT20))); + const Vec4V dotRaXnAngVel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4Mul(raXnX, angVelT00))); + Vec4V vrel = V4Add(norVel0, dotRaXnAngVel0); + + Vec4V unitResponse = V4Add(invMass0_dom0fV, dotDelAngVel0); + + if(isDynamic) + { + SolverFriction4Dynamic* PX_RESTRICT dFric = static_cast(friction); + + const Vec4V rbXnX = V4NegMulSub(rbZ, tY, V4Mul(rbY, tZ)); + const Vec4V rbXnY = V4NegMulSub(rbX, tZ, V4Mul(rbZ, tX)); + const Vec4V rbXnZ = V4NegMulSub(rbY, tX, V4Mul(rbX, tY)); + + const Vec4V v0b0 = V4Mul(invInertia1X0, rbXnX); + const Vec4V v0b1 = V4Mul(invInertia1X1, rbXnX); + const Vec4V v0b2 = V4Mul(invInertia1X2, rbXnX); + + const Vec4V v0PlusV1b0 = V4MulAdd(invInertia1Y0, rbXnY, v0b0); + const Vec4V v0PlusV1b1 = V4MulAdd(invInertia1Y1, rbXnY, v0b1); + const Vec4V v0PlusV1b2 = V4MulAdd(invInertia1Y2, rbXnY, v0b2); + + const Vec4V delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, v0PlusV1b0); + const Vec4V delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, v0PlusV1b1); + const Vec4V delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, v0PlusV1b2); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V norVel1 = V4MulAdd(tX, linVelT01, V4MulAdd(tY, linVelT11, V4Mul(tZ, linVelT21))); + const Vec4V dotRbXnAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + vrel = V4Sub(vrel, V4Add(norVel1, dotRbXnAngVel1)); + + const Vec4V resp1 = V4Add(dotDelAngVel1, invMassNorLenSq1); + + unitResponse = V4Add(unitResponse, resp1); + + dFric->rbXnX = delAngVel1X; + dFric->rbXnY = delAngVel1Y; + dFric->rbXnZ = delAngVel1Z; + } + + const Vec4V velMultiplier = V4Neg(V4Sel(V4IsGrtr(unitResponse, zero), V4Recip(unitResponse), zero)); + + friction->appliedForce = zero; + friction->raXnX = delAngVel0X; + friction->raXnY = delAngVel0Y; + friction->raXnZ = delAngVel0Z; + friction->velMultiplier = velMultiplier; + friction->targetVelocity = V4Sub(V4MulAdd(targetVelZ, tZ, V4MulAdd(targetVelY, tY, V4Mul(targetVelX, tX))), vrel); + friction->normalX = tX; + friction->normalY = tY; + friction->normalZ = tZ; + } + } + if(!(finished & 0x1)) + { + iter0.nextContact(patch0, contact0); + newFinished |= PxU32(!iter0.hasNextContact()); + } + + if(!(finished & 0x2)) + { + iter1.nextContact(patch1, contact1); + newFinished |= (PxU32(!iter1.hasNextContact()) << 1); + } + + if(!(finished & 0x4)) + { + iter2.nextContact(patch2, contact2); + newFinished |= (PxU32(!iter2.hasNextContact()) << 2); + } + + if(!(finished & 0x8)) + { + iter3.nextContact(patch3, contact3); + newFinished |= (PxU32(!iter3.hasNextContact()) << 3); + } + } + ptr = p; + } + return true; +} + + + +//The persistent friction patch correlation/allocation will already have happenned as this is per-pair. +//This function just computes the size of the combined solve data. +void computeBlockStreamByteSizesCoulomb4(PxSolverContactDesc* descs, + ThreadContext& threadContext, const CorrelationBuffer& c, + const PxU32 numFrictionPerPoint, + PxU32& _solverConstraintByteSize, PxU32* _axisConstraintCount, PxU32& _numContactPoints4) +{ + PX_ASSERT(0 == _solverConstraintByteSize); + PX_UNUSED(threadContext); + + PxU32 maxPatches = 0; + PxU32 maxContactCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxU32 maxFrictionCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxMemZero(maxContactCount, sizeof(maxContactCount)); + PxMemZero(maxFrictionCount, sizeof(maxFrictionCount)); + for(PxU32 a = 0; a < 4; ++a) + { + PxU32 axisConstraintCount = 0; + + for(PxU32 i = 0; i < descs[a].numFrictionPatches; i++) + { + PxU32 ind = i + descs[a].startFrictionPatchIndex; + + const FrictionPatch& frictionPatch = c.frictionPatches[ind]; + + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0; + //Solver constraint data. + if(c.frictionPatchContactCounts[ind]!=0) + { + maxContactCount[i] = PxMax(c.frictionPatchContactCounts[ind], maxContactCount[i]); + axisConstraintCount += c.frictionPatchContactCounts[ind]; + + if(haveFriction) + { + //const PxU32 fricCount = c.frictionPatches[ind].numConstraints; + const PxU32 fricCount = c.frictionPatchContactCounts[ind] * numFrictionPerPoint; + maxFrictionCount[i] = PxMax(fricCount, maxFrictionCount[i]); + axisConstraintCount += fricCount; + } + } + } + maxPatches = PxMax(descs[a].numFrictionPatches, maxPatches); + _axisConstraintCount[a] = axisConstraintCount; + } + + PxU32 totalContacts = 0, totalFriction = 0; + for(PxU32 a = 0; a < maxPatches; ++a) + { + totalContacts += maxContactCount[a]; + totalFriction += maxFrictionCount[a]; + } + + _numContactPoints4 = totalContacts; + + + //OK, we have a given number of friction patches, contact points and friction constraints so we can calculate how much memory we need + + const bool isStatic = (((descs[0].bodyState1 | descs[1].bodyState1 | descs[2].bodyState1 | descs[3].bodyState1) & PxSolverContactDesc::eDYNAMIC_BODY) == 0); + + const PxU32 headerSize = (sizeof(SolverContactCoulombHeader4) + sizeof(SolverFrictionHeader4)) * maxPatches; + //Add on 1 Vec4V per contact for the applied force buffer + const PxU32 constraintSize = isStatic ? ((sizeof(SolverContact4Base) + sizeof(Vec4V)) * totalContacts) + ( sizeof(SolverFriction4Base) * totalFriction) : + ((sizeof(SolverContact4Dynamic) + sizeof(Vec4V)) * totalContacts) + (sizeof(SolverFriction4Dynamic) * totalFriction); + + _solverConstraintByteSize = ((constraintSize + headerSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); +} + + +static SolverConstraintPrepState::Enum reserveBlockStreamsCoulomb4(PxSolverContactDesc* descs, ThreadContext& threadContext, const CorrelationBuffer& c, + PxU8*& solverConstraint, const PxU32 numFrictionPerContactPoint, + PxU32& solverConstraintByteSize, + PxU32* axisConstraintCount, PxU32& numContactPoints4, PxConstraintAllocator& constraintAllocator) +{ + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(0 == solverConstraintByteSize); + + //From constraintBlockStream we need to reserve contact points, contact forces, and a char buffer for the solver constraint data (already have a variable for this). + //From frictionPatchStream we just need to reserve a single buffer. + + //Compute the sizes of all the buffers. + computeBlockStreamByteSizesCoulomb4( + descs, threadContext, c, numFrictionPerContactPoint, solverConstraintByteSize, + axisConstraintCount, numContactPoints4); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if(constraintBlockByteSize > 0) + { + if((constraintBlockByteSize + 16u) > 16384) + return SolverConstraintPrepState::eUNBATCHABLE; + + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if(0==constraintBlock || (reinterpret_cast(-1))==constraintBlock) + { + if(0==constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock=NULL; + } + } + } + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if(0==constraintBlockByteSize || constraintBlock) + { + if(solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0==(uintptr_t(solverConstraint) & 0x0f)); + } + } + + //Return true if neither of the two block reservations failed. + return ((0==constraintBlockByteSize || constraintBlock)) ? SolverConstraintPrepState::eSUCCESS : SolverConstraintPrepState::eOUT_OF_MEMORY; +} + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb1D( + PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + return createFinalizeSolverContacts4Coulomb(outputs, threadContext, blockDescs, invDtF32, bounceThresholdF32, + frictionOffsetThreshold, correlationDistance, solverOffsetSlop, constraintAllocator, PxFrictionType::eONE_DIRECTIONAL); +} + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb2D( + PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + return createFinalizeSolverContacts4Coulomb(outputs, threadContext, blockDescs, invDtF32, bounceThresholdF32, + frictionOffsetThreshold, correlationDistance, solverOffsetSlop, constraintAllocator, PxFrictionType::eTWO_DIRECTIONAL); +} + + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Coulomb( + PxsContactManagerOutput** outputs, + ThreadContext& threadContext, + PxSolverContactDesc* blockDescs, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + PxFrictionType::Enum frictionType) +{ + PX_UNUSED(frictionOffsetThreshold); + PX_UNUSED(correlationDistance); + + for(PxU32 i = 0; i < 4; ++i) + { + blockDescs[i].desc->constraintLengthOver16 = 0; + } + + PX_ASSERT(outputs[0]->nbContacts && outputs[1]->nbContacts && outputs[2]->nbContacts && outputs[3]->nbContacts); + + Gu::ContactBuffer& buffer = threadContext.mContactBuffer; + + buffer.count = 0; + + PxU32 numContacts = 0; + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + PxU32 numFrictionPerPoint = PxU32(frictionType == PxFrictionType::eONE_DIRECTIONAL ? 1 : 2); + + PX_ALIGN(16, PxReal invMassScale0[4]); + PX_ALIGN(16, PxReal invMassScale1[4]); + PX_ALIGN(16, PxReal invInertiaScale0[4]); + PX_ALIGN(16, PxReal invInertiaScale1[4]); + + for(PxU32 a = 0; a < 4; ++a) + { + PxSolverContactDesc& blockDesc = blockDescs[a]; + PxSolverConstraintDesc& desc = *blockDesc.desc; + + //blockDesc.startContactIndex = numContacts; + blockDesc.contacts = &buffer.contacts[numContacts]; + + Ps::prefetchLine(desc.bodyA); + Ps::prefetchLine(desc.bodyB); + + if((numContacts + outputs[a]->nbContacts) > 64) + { + return SolverConstraintPrepState::eUNBATCHABLE; + } + bool hasMaxImpulse, hasTargetVelocity; + + const PxReal defaultMaxImpulse = PxMin(blockDesc.data0->maxContactImpulse, blockDesc.data1->maxContactImpulse); + + PxU32 contactCount = extractContacts(buffer, *outputs[a], hasMaxImpulse, hasTargetVelocity, invMassScale0[a], invMassScale1[a], + invInertiaScale0[a], invInertiaScale1[a], defaultMaxImpulse); + + if(contactCount == 0) + return SolverConstraintPrepState::eUNBATCHABLE; + + numContacts+=contactCount; + + blockDesc.numContacts = contactCount; + blockDesc.hasMaxImpulse = hasMaxImpulse; + + blockDesc.startFrictionPatchIndex = c.frictionPatchCount; + blockDesc.startContactPatchIndex = c.contactPatchCount; + + createContactPatches(c, blockDesc.contacts, contactCount, PXC_SAME_NORMAL); + + bool overflow = correlatePatches(c, blockDesc.contacts, blockDesc.bodyFrame0, blockDesc.bodyFrame1, PXC_SAME_NORMAL, blockDesc.startContactPatchIndex, + blockDesc.startFrictionPatchIndex); + if(overflow) + return SolverConstraintPrepState::eUNBATCHABLE; + + blockDesc.numContactPatches = PxU16(c.contactPatchCount - blockDesc.startContactPatchIndex); + blockDesc.numFrictionPatches = c.frictionPatchCount - blockDesc.startFrictionPatchIndex; + + invMassScale0[a] *= blockDesc.mInvMassScales.linear0; + invMassScale1[a] *= blockDesc.mInvMassScales.linear1; + invInertiaScale0[a] *= blockDesc.mInvMassScales.angular0; + invInertiaScale1[a] *= blockDesc.mInvMassScales.angular1; + + } + + //OK, now we need to work out how much memory to allocate, allocate it and then block-create the constraints... + + PxU8* solverConstraint = NULL; + PxU32 solverConstraintByteSize = 0; + PxU32 axisConstraintCount[4]; + PxU32 numContactPoints4 = 0; + + SolverConstraintPrepState::Enum state = reserveBlockStreamsCoulomb4(blockDescs, threadContext, c, + solverConstraint, numFrictionPerPoint, + solverConstraintByteSize, + axisConstraintCount, numContactPoints4, constraintAllocator); + + if(state != SolverConstraintPrepState::eSUCCESS) + return state; + + //OK, we allocated the memory, now let's create the constraints + + for(PxU32 a = 0; a < 4; ++a) + { + PxSolverConstraintDesc& desc = *blockDescs[a].desc; + //n[a]->solverConstraintPointer = solverConstraint; + desc.constraint = solverConstraint; + + //KS - TODO - add back in counters for axisConstraintCount somewhere... + blockDescs[a].axisConstraintCount += Ps::to16(axisConstraintCount[a]); + + desc.constraintLengthOver16 = Ps::to16(solverConstraintByteSize/16); + + PxU32 writeBackLength = outputs[a]->nbContacts * sizeof(PxReal); + void* writeBack = outputs[a]->contactForces; + desc.writeBack = writeBack; + setWritebackLength(desc, writeBackLength); + } + + const Vec4V iMassScale0 = V4LoadA(invMassScale0); + const Vec4V iInertiaScale0 = V4LoadA(invInertiaScale0); + const Vec4V iMassScale1 = V4LoadA(invMassScale1); + const Vec4V iInertiaScale1 = V4LoadA(invInertiaScale1); + + + bool hasFriction = setupFinalizeSolverConstraintsCoulomb4(blockDescs, solverConstraint, + invDtF32, bounceThresholdF32, solverOffsetSlop, c, numFrictionPerPoint, numContactPoints4, solverConstraintByteSize, + iMassScale0, iInertiaScale0, iMassScale1, iInertiaScale1); + + *(reinterpret_cast(solverConstraint + solverConstraintByteSize)) = 0; + *(reinterpret_cast(solverConstraint + solverConstraintByteSize + 4)) = hasFriction ? 0xFFFFFFFF : 0; + + + return SolverConstraintPrepState::eSUCCESS; +} + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp new file mode 100644 index 000000000..1b0803c9d --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp @@ -0,0 +1,664 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +//#include "PxvGeometry.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DySolverConstraintDesc.h" +#include "DySolverBody.h" +#include "DySolverContact4.h" +#include "DySolverContactPF4.h" + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DySolverExt.h" +#include "DyArticulationContactPrep.h" +#include "DyContactPrepShared.h" + +#include "PsFoundation.h" + +using namespace physx::Gu; +using namespace physx::shdfnd::aos; + +namespace physx +{ +namespace Dy +{ + +bool createFinalizeSolverContactsCoulomb(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + PxFrictionType::Enum frictionType, + Cm::SpatialVectorF* Z); + +static bool setupFinalizeSolverConstraintsCoulomb( + Sc::ShapeInteraction* shapeInteraction, + const ContactBuffer& buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const PxSolverBodyData& data0, + const PxSolverBodyData& data1, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxU32 frictionPerPointCount, + const bool hasForceThresholds, + const bool staticBody, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + PxReal restDist, + const PxReal maxCCDSeparation, + const PxReal solverOffsetSlopF32) +{ + const FloatV ccdMaxSeparation = FLoad(maxCCDSeparation); + const Vec3V solverOffsetSlop = V3Load(solverOffsetSlopF32); + PxU8* PX_RESTRICT ptr = workspace; + const FloatV zero=FZero(); + + PxU8 flags = PxU8(hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0); + + const FloatV restDistance = FLoad(restDist); + + const Vec3V bodyFrame0p = V3LoadU(bodyFrame0.p); + const Vec3V bodyFrame1p = V3LoadU(bodyFrame1.p); + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + const PxU32 frictionPatchCount = c.frictionPatchCount; + + const PxU32 pointStride = sizeof(SolverContactPoint); + const PxU32 frictionStride = sizeof(SolverContactFriction); + const PxU8 pointHeaderType = Ps::to8(staticBody ? DY_SC_TYPE_STATIC_CONTACT : DY_SC_TYPE_RB_CONTACT); + const PxU8 frictionHeaderType = Ps::to8(staticBody ? DY_SC_TYPE_STATIC_FRICTION : DY_SC_TYPE_FRICTION); + + + const Vec3V linVel0 = V3LoadU(data0.linearVelocity); + const Vec3V linVel1 = V3LoadU(data1.linearVelocity); + const Vec3V angVel0 = V3LoadU(data0.angularVelocity); + const Vec3V angVel1 = V3LoadU(data1.angularVelocity); + + + const FloatV invMass0 = FLoad(data0.invMass); + const FloatV invMass1 = FLoad(data1.invMass); + + const FloatV maxPenBias = FMax(FLoad(data0.penBiasClamp), FLoad(data1.penBiasClamp)); + + // PT: the matrix is symmetric so we can read it as a PxMat33! Gets rid of 25000+ LHS. + const PxMat33& invIn0 = reinterpret_cast(data0.sqrtInvInertia); + PX_ALIGN(16, const Mat33V invSqrtInertia0) + ( + V3LoadU(invIn0.column0), + V3LoadU(invIn0.column1), + V3LoadU(invIn0.column2) + ); + const PxMat33& invIn1 = reinterpret_cast(data1.sqrtInvInertia); + PX_ALIGN(16, const Mat33V invSqrtInertia1) + ( + V3LoadU(invIn1.column0), + V3LoadU(invIn1.column1), + V3LoadU(invIn1.column2) + ); + + const FloatV invDt = FLoad(invDtF32); + const FloatV p8 = FLoad(0.8f); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + const FloatV orthoThreshold = FLoad(0.70710678f); + const FloatV eps = FLoad(0.00001f); + + const FloatV invDtp8 = FMul(invDt, p8); + + const FloatV d0 = FLoad(invMassScale0); + const FloatV d1 = FLoad(invMassScale1); + const FloatV nDom1fV = FNeg(d1); + const FloatV angD0 = FLoad(invInertiaScale0); + const FloatV angD1 = FLoad(invInertiaScale1); + + const FloatV invMass0_dom0fV = FMul(d0, invMass0); + const FloatV invMass1_dom1fV = FMul(nDom1fV, invMass1); + + + for(PxU32 i=0;i< frictionPatchCount;i++) + { + const PxU32 contactCount = c.frictionPatchContactCounts[i]; + if(contactCount == 0) + continue; + + const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start; + + const Vec3V normal = Ps::aos::V3LoadA(contactBase0->normal); + + const FloatV normalLenSq = V3LengthSq(normal); + const VecCrossV norCross = V3PrepareCross(normal); + + const FloatV restitution = FLoad(contactBase0->restitution); + + const FloatV norVel = V3SumElems(V3NegMulSub(normal, linVel1, V3Mul(normal, linVel0))); + /*const FloatV norVel0 = V3Dot(normal, linVel0); + const FloatV norVel1 = V3Dot(normal, linVel1); + const FloatV norVel = FSub(norVel0, norVel1);*/ + + const FloatV invMassNorLenSq0 = FMul(invMass0_dom0fV, normalLenSq); + const FloatV invMassNorLenSq1 = FMul(invMass1_dom1fV, normalLenSq); + + + SolverContactCoulombHeader* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactCoulombHeader); + + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + + + header->numNormalConstr = PxU8(contactCount); + header->type = pointHeaderType; + //header->setRestitution(n.restitution); + //header->setRestitution(contactBase0->restitution); + + header->setDominance0(invMass0_dom0fV); + header->setDominance1(FNeg(invMass1_dom1fV)); + FStore(angD0, &header->angDom0); + FStore(angD1, &header->angDom1); + header->setNormal(normal); + header->flags = flags; + header->shapeInteraction = shapeInteraction; + + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer.contacts + c.contactPatches[patch].start; + + + PxU8* p = ptr; + for(PxU32 j=0;j(p); + p += pointStride; + + constructContactConstraint(invSqrtInertia0, invSqrtInertia1, invMassNorLenSq0, + invMassNorLenSq1, angD0, angD1, bodyFrame0p, bodyFrame1p, + normal, norVel, norCross, angVel0, angVel1, + invDt, invDtp8, restDistance, maxPenBias, restitution, + bounceThreshold, contact, *solverContact, ccdMaxSeparation, solverOffsetSlop); + } + ptr = p; + } + } + + //construct all the frictions + + PxU8* PX_RESTRICT ptr2 = workspace; + + bool hasFriction = false; + for(PxU32 i=0;i< frictionPatchCount;i++) + { + const PxU32 contactCount = c.frictionPatchContactCounts[i]; + if(contactCount == 0) + continue; + + const Gu::ContactPoint* contactBase0 = buffer.contacts + c.contactPatches[c.correlationListHeads[i]].start; + + SolverContactCoulombHeader* header = reinterpret_cast(ptr2); + header->frictionOffset = PxU16(ptr - ptr2);// + sizeof(SolverFrictionHeader); + ptr2 += sizeof(SolverContactCoulombHeader) + header->numNormalConstr * pointStride; + + const PxReal staticFriction = contactBase0->staticFriction; + const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + const bool haveFriction = (disableStrongFriction == 0); + + SolverFrictionHeader* frictionHeader = reinterpret_cast(ptr); + frictionHeader->numNormalConstr = Ps::to8(c.frictionPatchContactCounts[i]); + frictionHeader->numFrictionConstr = Ps::to8(haveFriction ? c.frictionPatchContactCounts[i] * frictionPerPointCount : 0); + ptr += sizeof(SolverFrictionHeader); + PxF32* appliedForceBuffer = reinterpret_cast(ptr); + ptr += frictionHeader->getAppliedForcePaddingSize(c.frictionPatchContactCounts[i]); + PxMemZero(appliedForceBuffer, sizeof(PxF32)*contactCount*frictionPerPointCount); + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + + const Vec3V normal = V3LoadU(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); + + const FloatV normalX = V3GetX(normal); + const FloatV normalY = V3GetY(normal); + const FloatV normalZ = V3GetZ(normal); + + const Vec3V t0Fallback1 = V3Merge(zero, FNeg(normalZ), normalY); + const Vec3V t0Fallback2 = V3Merge(FNeg(normalY), normalX, zero) ; + + const BoolV con = FIsGrtr(orthoThreshold, FAbs(normalX)); + const Vec3V tFallback1 = V3Sel(con, t0Fallback1, t0Fallback2); + + const Vec3V linVrel = V3Sub(linVel0, linVel1); + const Vec3V t0_ = V3Sub(linVrel, V3Scale(normal, V3Dot(normal, linVrel))); + const FloatV sqDist = V3Dot(t0_,t0_); + const BoolV con1 = FIsGrtr(sqDist, eps); + const Vec3V tDir0 =V3Normalize(V3Sel(con1, t0_, tFallback1)); + const Vec3V tDir1 = V3Cross(tDir0, normal); + + Vec3V tFallback = tDir0; + Vec3V tFallbackAlt = tDir1; + + if(haveFriction) + { + //frictionHeader->setStaticFriction(n.staticFriction); + frictionHeader->setStaticFriction(staticFriction); + FStore(invMass0_dom0fV, &frictionHeader->invMass0D0); + FStore(FNeg(invMass1_dom1fV), &frictionHeader->invMass1D1); + FStore(angD0, &frictionHeader->angDom0); + FStore(angD1, &frictionHeader->angDom1); + frictionHeader->type = frictionHeaderType; + + PxU32 totalPatchContactCount = 0; + + for(PxU32 patch=c.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const PxU32 start = c.contactPatches[patch].start; + const Gu::ContactPoint* contactBase = buffer.contacts + start; + + PxU8* p = ptr; + for(PxU32 j =0; j < count; j++) + { + hasFriction = true; + const Gu::ContactPoint& contact = contactBase[j]; + const Vec3V point = V3LoadU(contact.point); + Vec3V ra = V3Sub(point, bodyFrame0p); + Vec3V rb = V3Sub(point, bodyFrame1p); + ra = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(ra)), V3Zero(), ra); + rb = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rb)), V3Zero(), rb); + const Vec3V targetVel = V3LoadU(contact.targetVel); + + for(PxU32 k = 0; k < frictionPerPointCount; ++k) + { + const Vec3V t0 = tFallback; + tFallback = tFallbackAlt; + tFallbackAlt = t0; + + SolverContactFriction* PX_RESTRICT f0 = reinterpret_cast(p); + p += frictionStride; + //f0->brokenOrContactIndex = contactId; + + const Vec3V raXn = V3Cross(ra, t0); + const Vec3V rbXn = V3Cross(rb, t0); + + const Vec3V delAngVel0 = M33MulV3(invSqrtInertia0, raXn); + const Vec3V delAngVel1 = M33MulV3(invSqrtInertia1, rbXn); + + const FloatV resp0 = FAdd(invMass0_dom0fV, FMul(angD0, V3Dot(delAngVel0, delAngVel0))); + const FloatV resp1 = FSub(FMul(angD1, V3Dot(delAngVel1, delAngVel1)), invMass1_dom1fV); + const FloatV resp = FAdd(resp0, resp1); + + const FloatV velMultiplier = FNeg(FSel(FIsGrtr(resp, zero), FRecip(resp), zero)); + + const FloatV vrel1 = FAdd(V3Dot(t0, linVel0), V3Dot(raXn, angVel0)); + const FloatV vrel2 = FAdd(V3Dot(t0, linVel1), V3Dot(rbXn, angVel1)); + const FloatV vrel = FSub(vrel1, vrel2); + + + f0->normalXYZ_appliedForceW = V4SetW(Vec4V_From_Vec3V(t0), zero); + f0->raXnXYZ_velMultiplierW = V4SetW(Vec4V_From_Vec3V(delAngVel0), velMultiplier); + //f0->rbXnXYZ_targetVelocityW = V4SetW(Vec4V_From_Vec3V(delAngVel1), FSub(V3Dot(targetVel, t0), vrel)); + f0->rbXnXYZ_biasW = Vec4V_From_Vec3V(delAngVel1); + FStore(FSub(V3Dot(targetVel, t0), vrel), &f0->targetVel); + } + } + + totalPatchContactCount += c.contactPatches[patch].count; + + ptr = p; + } + } + } + *ptr = 0; + return hasFriction; +} + + + +static void computeBlockStreamByteSizesCoulomb(const CorrelationBuffer& c, + const PxU32 frictionCountPerPoint, PxU32& _solverConstraintByteSize, + PxU32& _axisConstraintCount, + bool useExtContacts) +{ + PX_ASSERT(0 == _solverConstraintByteSize); + PX_ASSERT(0 == _axisConstraintCount); + + // PT: use local vars to remove LHS + PxU32 solverConstraintByteSize = 0; + PxU32 numFrictionPatches = 0; + PxU32 axisConstraintCount = 0; + + for(PxU32 i = 0; i < c.frictionPatchCount; i++) + { + //Friction patches. + if(c.correlationListHeads[i] != CorrelationBuffer::LIST_END) + numFrictionPatches++; + + + const FrictionPatch& frictionPatch = c.frictionPatches[i]; + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0; + + //Solver constraint data. + if(c.frictionPatchContactCounts[i]!=0) + { + solverConstraintByteSize += sizeof(SolverContactCoulombHeader); + + solverConstraintByteSize += useExtContacts ? c.frictionPatchContactCounts[i] * sizeof(SolverContactPointExt) + : c.frictionPatchContactCounts[i] * sizeof(SolverContactPoint); + + axisConstraintCount += c.frictionPatchContactCounts[i]; + + //We always need the friction headers to write the accumulated + if(haveFriction) + { + //4 bytes + solverConstraintByteSize += sizeof(SolverFrictionHeader); + //buffer to store applied forces in + solverConstraintByteSize += SolverFrictionHeader::getAppliedForcePaddingSize(c.frictionPatchContactCounts[i]); + + const PxU32 nbFrictionConstraints = c.frictionPatchContactCounts[i] * frictionCountPerPoint; + + solverConstraintByteSize += useExtContacts ? nbFrictionConstraints * sizeof(SolverContactFrictionExt) + : nbFrictionConstraints * sizeof(SolverContactFriction); + axisConstraintCount += c.frictionPatchContactCounts[i]; + } + else + { + //reserve buffers for storing accumulated impulses + solverConstraintByteSize += sizeof(SolverFrictionHeader); + solverConstraintByteSize += SolverFrictionHeader::getAppliedForcePaddingSize(c.frictionPatchContactCounts[i]); + } + } + } + _axisConstraintCount = axisConstraintCount; + + //16-byte alignment. + _solverConstraintByteSize = ((solverConstraintByteSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); +} + +static bool reserveBlockStreamsCoulomb(const CorrelationBuffer& c, + PxU8*& solverConstraint, PxU32 frictionCountPerPoint, + PxU32& solverConstraintByteSize, + PxU32& axisConstraintCount, PxConstraintAllocator& constraintAllocator, + bool useExtContacts) +{ + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(0 == solverConstraintByteSize); + PX_ASSERT(0 == axisConstraintCount); + + + //From constraintBlockStream we need to reserve contact points, contact forces, and a char buffer for the solver constraint data (already have a variable for this). + //From frictionPatchStream we just need to reserve a single buffer. + + //Compute the sizes of all the buffers. + computeBlockStreamByteSizesCoulomb( + c, + frictionCountPerPoint, solverConstraintByteSize, + axisConstraintCount, useExtContacts); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if(constraintBlockByteSize > 0) + { + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if(0==constraintBlock || (reinterpret_cast(-1))==constraintBlock) + { + if(0==constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock=NULL; + } + } + } + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if(0==constraintBlockByteSize || constraintBlock) + { + if(solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0==(uintptr_t(solverConstraint) & 0x0f)); + } + } + + //Return true if neither of the two block reservations failed. + return ((0==constraintBlockByteSize || constraintBlock)); +} + +bool createFinalizeSolverContactsCoulomb1D(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z) +{ + return createFinalizeSolverContactsCoulomb(contactDesc, output, threadContext, invDtF32, bounceThresholdF32, frictionOffsetThreshold, correlationDistance, solverOffsetSlop, + constraintAllocator, PxFrictionType::eONE_DIRECTIONAL, Z); +} + +bool createFinalizeSolverContactsCoulomb2D(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + Cm::SpatialVectorF* Z) + +{ + return createFinalizeSolverContactsCoulomb(contactDesc, output, threadContext, invDtF32, bounceThresholdF32, frictionOffsetThreshold, correlationDistance, solverOffsetSlop, + constraintAllocator, PxFrictionType::eTWO_DIRECTIONAL, Z); +} + +bool createFinalizeSolverContactsCoulomb(PxSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator, + PxFrictionType::Enum frictionType, + Cm::SpatialVectorF* Z) +{ + PX_UNUSED(frictionOffsetThreshold); + PX_UNUSED(correlationDistance); + + PxSolverConstraintDesc& desc = *contactDesc.desc; + + desc.constraintLengthOver16 = 0; + + ContactBuffer& buffer = threadContext.mContactBuffer; + + buffer.count = 0; + + // We pull the friction patches out of the cache to remove the dependency on how + // the cache is organized. Remember original addrs so we can write them back + // efficiently. + + Ps::prefetchLine(contactDesc.frictionPtr); + + PxReal invMassScale0 = 1.f; + PxReal invMassScale1 = 1.f; + PxReal invInertiaScale0 = 1.f; + PxReal invInertiaScale1 = 1.f; + + bool hasMaxImpulse = false, hasTargetVelocity = false; + + PxU32 numContacts = extractContacts(buffer, output, hasMaxImpulse, hasTargetVelocity, invMassScale0, invMassScale1, + invInertiaScale0, invInertiaScale1, PxMin(contactDesc.data0->maxContactImpulse, contactDesc.data1->maxContactImpulse)); + + if(numContacts == 0) + { + contactDesc.frictionPtr = NULL; + contactDesc.frictionCount = 0; + return true; + } + + Ps::prefetchLine(contactDesc.body0); + Ps::prefetchLine(contactDesc.body1); + Ps::prefetchLine(contactDesc.data0); + Ps::prefetchLine(contactDesc.data1); + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + createContactPatches(c, buffer.contacts, buffer.count, PXC_SAME_NORMAL); + + PxU32 numFrictionPerPatch = PxU32(frictionType == PxFrictionType::eONE_DIRECTIONAL ? 1 : 2); + + bool overflow = correlatePatches(c, buffer.contacts, contactDesc.bodyFrame0, contactDesc.bodyFrame1, PXC_SAME_NORMAL, 0, 0); + PX_UNUSED(overflow); +#if PX_CHECKED + if(overflow) + { + Ps::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Dropping contacts in solver because we exceeded limit of 32 friction patches."); + } +#endif + + + //PX_ASSERT(patchCount == c.frictionPatchCount); + + PxU8* solverConstraint = NULL; + PxU32 solverConstraintByteSize = 0; + PxU32 axisConstraintCount = 0; + + bool useExtContacts = !!((contactDesc.bodyState0 | contactDesc.bodyState1) & PxSolverContactDesc::eARTICULATION); + + const bool successfulReserve = reserveBlockStreamsCoulomb( + c, + solverConstraint, numFrictionPerPatch, + solverConstraintByteSize, + axisConstraintCount, + constraintAllocator, + useExtContacts); + + // initialise the work unit's ptrs to the various buffers. + + contactDesc.frictionPtr = NULL; + desc.constraint = NULL; + desc.constraintLengthOver16 = 0; + contactDesc.frictionCount = 0; + + // patch up the work unit with the reserved buffers and set the reserved buffer data as appropriate. + + if(successfulReserve) + { + desc.constraint = solverConstraint; + output.nbContacts = Ps::to8(numContacts); + desc.constraintLengthOver16 = Ps::to16(solverConstraintByteSize/16); + + //Initialise solverConstraint buffer. + if(solverConstraint) + { + bool hasFriction = false; + if(useExtContacts) + { + const PxSolverBodyData& data0 = *contactDesc.data0; + const PxSolverBodyData& data1 = *contactDesc.data1; + + const SolverExtBody b0(reinterpret_cast(contactDesc.body0), reinterpret_cast(&data0), desc.linkIndexA); + const SolverExtBody b1(reinterpret_cast(contactDesc.body1), reinterpret_cast(&data1), desc.linkIndexB); + + hasFriction = setupFinalizeExtSolverContactsCoulomb(buffer, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + invDtF32, bounceThresholdF32, b0, b1, numFrictionPerPatch, + invMassScale0, invInertiaScale0, invMassScale1, invInertiaScale1, contactDesc.restDistance, contactDesc.maxCCDSeparation, Z); + } + else + { + const PxSolverBodyData& data0 = *contactDesc.data0; + const PxSolverBodyData& data1 = *contactDesc.data1; + + hasFriction = setupFinalizeSolverConstraintsCoulomb(contactDesc.shapeInteraction, buffer, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + data0, data1, invDtF32, bounceThresholdF32, numFrictionPerPatch, contactDesc.hasForceThresholds, contactDesc.bodyState1 == PxSolverContactDesc::eSTATIC_BODY, + invMassScale0, invInertiaScale0, invMassScale1, invInertiaScale1, contactDesc.restDistance, contactDesc.maxCCDSeparation, solverOffsetSlop); + } + *(reinterpret_cast(solverConstraint + solverConstraintByteSize)) = 0; + *(reinterpret_cast(solverConstraint + solverConstraintByteSize + 4)) = hasFriction ? 0xFFFFFFFF : 0; + } + } + + return successfulReserve; +} + +} +} + + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h new file mode 100644 index 000000000..e9684ba38 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h @@ -0,0 +1,308 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_CONTACT_PREP_SHARED_H +#define DY_CONTACT_PREP_SHARED_H + +#include "foundation/PxPreprocessor.h" +#include "PxSceneDesc.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DyContactPrep.h" +#include "DyCorrelationBuffer.h" +#include "DyArticulationContactPrep.h" +#include "PxsContactManager.h" +#include "PxsContactManagerState.h" + +namespace physx +{ +namespace Dy +{ + + +PX_FORCE_INLINE bool pointsAreClose(const PxTransform& body1ToBody0, + const PxVec3& localAnchor0, const PxVec3& localAnchor1, + const PxVec3& axis, float correlDist) +{ + const PxVec3 body0PatchPoint1 = body1ToBody0.transform(localAnchor1); + + return PxAbs((localAnchor0 - body0PatchPoint1).dot(axis))(frictionCookie); + + //Try working out relative transforms! TODO - can we compute this lazily for the first friction patch + bool evaluated = false; + PxTransform body1ToBody0; + + while(frictionPatchCount--) + { + Ps::prefetchLine(patches,128); + const FrictionPatch& patch = *patches++; + PX_ASSERT (patch.broken == 0 || patch.broken == 1); + if(!patch.broken) + { + // if the eDISABLE_STRONG_FRICTION flag is there we need to blow away the previous frame's friction correlation, so + // that we can associate each friction anchor with a target velocity. So we lose strong friction. + if(patch.anchorCount != 0 && !(patch.materialFlags & PxMaterialFlag::eDISABLE_STRONG_FRICTION)) + { + PX_ASSERT(patch.anchorCount <= 2); + + + if(!evaluated) + { + body1ToBody0 = bodyFrame0.transformInv(bodyFrame1); + evaluated = true; + } + + + if(patch.body0Normal.dot(body1ToBody0.rotate(patch.body1Normal)) > PXC_SAME_NORMAL) + { + if(!isSeparated(patch, body1ToBody0, correlationDistance)) + { + if(c.frictionPatchCount == CorrelationBuffer::MAX_FRICTION_PATCHES) + return false; + { + c.contactID[c.frictionPatchCount][0] = 0xffff; + c.contactID[c.frictionPatchCount][1] = 0xffff; + //Rotate the contact normal into world space + c.frictionPatchWorldNormal[c.frictionPatchCount] = bodyFrame0.rotate(patch.body0Normal); + c.frictionPatchContactCounts[c.frictionPatchCount] = 0; + c.patchBounds[c.frictionPatchCount].setEmpty(); + c.correlationListHeads[c.frictionPatchCount] = CorrelationBuffer::LIST_END; + PxMemCopy(&c.frictionPatches[c.frictionPatchCount++], &patch, sizeof(FrictionPatch)); + } + } + } + } + } + } + return true; +} + +PX_FORCE_INLINE PxU32 extractContacts(Gu::ContactBuffer& buffer, PxsContactManagerOutput& npOutput, bool& hasMaxImpulse, bool& hasTargetVelocity, + PxReal& invMassScale0, PxReal& invMassScale1, PxReal& invInertiaScale0, PxReal& invInertiaScale1, PxReal defaultMaxImpulse) +{ + PxContactStreamIterator iter(npOutput.contactPatches, npOutput.contactPoints, npOutput.getInternalFaceIndice(), npOutput.nbPatches, npOutput.nbContacts); + + PxU32 numContacts = buffer.count, origContactCount = buffer.count; + if(!iter.forceNoResponse) + { + invMassScale0 = iter.getInvMassScale0(); + invMassScale1 = iter.getInvMassScale1(); + invInertiaScale0 = iter.getInvInertiaScale0(); + invInertiaScale1 = iter.getInvInertiaScale1(); + hasMaxImpulse = (iter.patch->internalFlags & PxContactPatch::eHAS_MAX_IMPULSE) != 0; + hasTargetVelocity = (iter.patch->internalFlags & PxContactPatch::eHAS_TARGET_VELOCITY) != 0; + + while(iter.hasNextPatch()) + { + iter.nextPatch(); + while(iter.hasNextContact()) + { + iter.nextContact(); + Ps::prefetchLine(iter.contact, 128); + Ps::prefetchLine(&buffer.contacts[numContacts], 128); + PxReal maxImpulse = hasMaxImpulse ? iter.getMaxImpulse() : defaultMaxImpulse; + if(maxImpulse != 0.f) + { + PX_ASSERT(numContacts < Gu::ContactBuffer::MAX_CONTACTS); + buffer.contacts[numContacts].normal = iter.getContactNormal(); + buffer.contacts[numContacts].point = iter.getContactPoint(); + buffer.contacts[numContacts].separation = iter.getSeparation(); + //KS - we use the face indices to cache the material indices and flags - avoids bloating the PxContact structure + buffer.contacts[numContacts].materialFlags = PxU8(iter.getMaterialFlags()); + buffer.contacts[numContacts].maxImpulse = maxImpulse; + buffer.contacts[numContacts].staticFriction = iter.getStaticFriction(); + buffer.contacts[numContacts].dynamicFriction = iter.getDynamicFriction(); + buffer.contacts[numContacts].restitution = iter.getRestitution(); + const PxVec3& targetVel = iter.getTargetVel(); + buffer.contacts[numContacts].targetVel = targetVel; + ++numContacts; + } + } + } + } + const PxU32 contactCount = numContacts - origContactCount; + buffer.count = numContacts; + return contactCount; +} + +struct CorrelationListIterator +{ + CorrelationBuffer& buffer; + PxU32 currPatch; + PxU32 currContact; + + CorrelationListIterator(CorrelationBuffer& correlationBuffer, PxU32 startPatch) : buffer(correlationBuffer) + { + //We need to force us to advance the correlation buffer to the first available contact (if one exists) + PxU32 newPatch = startPatch, newContact = 0; + + while(newPatch != CorrelationBuffer::LIST_END && newContact == buffer.contactPatches[newPatch].count) + { + newPatch = buffer.contactPatches[newPatch].next; + newContact = 0; + } + + currPatch = newPatch; + currContact = newContact; + } + + //Returns true if it has another contact pre-loaded. Returns false otherwise + PX_FORCE_INLINE bool hasNextContact() + { + return (currPatch != CorrelationBuffer::LIST_END && currContact < buffer.contactPatches[currPatch].count); + } + + inline void nextContact(PxU32& patch, PxU32& contact) + { + PX_ASSERT(currPatch != CorrelationBuffer::LIST_END); + PX_ASSERT(currContact < buffer.contactPatches[currPatch].count); + + patch = currPatch; + contact = currContact; + PxU32 newPatch = currPatch, newContact = currContact + 1; + + while(newPatch != CorrelationBuffer::LIST_END && newContact == buffer.contactPatches[newPatch].count) + { + newPatch = buffer.contactPatches[newPatch].next; + newContact = 0; + } + + currPatch = newPatch; + currContact = newContact; + } + +private: + CorrelationListIterator& operator=(const CorrelationListIterator&); + +}; + + + PX_FORCE_INLINE void constructContactConstraint(const Mat33V& invSqrtInertia0, const Mat33V& invSqrtInertia1, const FloatVArg invMassNorLenSq0, + const FloatVArg invMassNorLenSq1, const FloatVArg angD0, const FloatVArg angD1, const Vec3VArg bodyFrame0p, const Vec3VArg bodyFrame1p, + const Vec3VArg normal, const FloatVArg norVel, const VecCrossV& norCross, const Vec3VArg angVel0, const Vec3VArg angVel1, + const FloatVArg invDt, const FloatVArg invDtp8, const FloatVArg restDistance, const FloatVArg maxPenBias, const FloatVArg restitution, + const FloatVArg bounceThreshold, const Gu::ContactPoint& contact, SolverContactPoint& solverContact, + const FloatVArg ccdMaxSeparation, const Vec3VArg solverOffsetSlop) + { + const FloatV zero = FZero(); + const Vec3V point = V3LoadA(contact.point); + const FloatV separation = FLoad(contact.separation); + + const FloatV cTargetVel = V3Dot(normal, V3LoadA(contact.targetVel)); + + const Vec3V ra = V3Sub(point, bodyFrame0p); + const Vec3V rb = V3Sub(point, bodyFrame1p); + + /*ra = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(ra)), V3Zero(), ra); + rb = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rb)), V3Zero(), rb);*/ + + Vec3V raXn = V3Cross(ra, norCross); + Vec3V rbXn = V3Cross(rb, norCross); + + raXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(raXn)), V3Zero(), raXn); + rbXn = V3Sel(V3IsGrtr(solverOffsetSlop, V3Abs(rbXn)), V3Zero(), rbXn); + + const Vec3V raXnSqrtInertia = M33MulV3(invSqrtInertia0, raXn); + const Vec3V rbXnSqrtInertia = M33MulV3(invSqrtInertia1, rbXn); + + const FloatV resp0 = FAdd(invMassNorLenSq0, FMul(V3Dot(raXnSqrtInertia, raXnSqrtInertia), angD0)); + const FloatV resp1 = FSub(FMul(V3Dot(rbXnSqrtInertia, rbXnSqrtInertia), angD1), invMassNorLenSq1); + + const FloatV unitResponse = FAdd(resp0, resp1); + + const FloatV vrel1 = FAdd(norVel, V3Dot(raXn, angVel0)); + const FloatV vrel2 = V3Dot(rbXn, angVel1); + const FloatV vrel = FSub(vrel1, vrel2); + + const FloatV velMultiplier = FSel(FIsGrtr(unitResponse, zero), FRecip(unitResponse), zero); + + const FloatV penetration = FSub(separation, restDistance); + + const FloatV penetrationInvDt = FMul(penetration, invDt); + + const FloatV penetrationInvDtPt8 = FMax(maxPenBias, FMul(penetration, invDtp8)); + + FloatV scaledBias = FMul(velMultiplier, penetrationInvDtPt8); + + const BoolV isGreater2 = BAnd(BAnd(FIsGrtr(restitution, zero), FIsGrtr(bounceThreshold, vrel)), FIsGrtr(FNeg(vrel), penetrationInvDt)); + + const BoolV ccdSeparationCondition = FIsGrtrOrEq(ccdMaxSeparation, penetration); + + scaledBias = FSel(BAnd(ccdSeparationCondition, isGreater2), zero, scaledBias); + + const FloatV sumVRel(vrel); + + FloatV targetVelocity = FAdd(cTargetVel, FSel(isGreater2, FMul(FNeg(sumVRel), restitution), zero)); + + //Note - we add on the initial target velocity + targetVelocity = FSub(targetVelocity, vrel); + + const FloatV biasedErr = FScaleAdd(targetVelocity, velMultiplier, FNeg(scaledBias)); + const FloatV unbiasedErr = FScaleAdd(targetVelocity, velMultiplier, FSel(isGreater2, zero, FNeg(FMax(scaledBias, zero)))); + //const FloatV unbiasedErr = FScaleAdd(targetVelocity, velMultiplier, FNeg(FMax(scaledBias, zero))); + + FStore(velMultiplier, &solverContact.velMultiplier); + FStore(biasedErr, &solverContact.biasedErr); + FStore(unbiasedErr, &solverContact.unbiasedErr); + solverContact.maxImpulse = contact.maxImpulse; + + solverContact.raXn = raXnSqrtInertia; + solverContact.rbXn = rbXnSqrtInertia; + } +} +} + +#endif //DY_CONTACT_PREP_SHARED_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h b/src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h new file mode 100644 index 000000000..8ac9d62d4 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h @@ -0,0 +1,409 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_CONTACT_REDUCTION_H +#define DY_CONTACT_REDUCTION_H + +#include "GuContactPoint.h" +#include "PxsMaterialManager.h" + +namespace physx +{ + + +namespace Dy +{ + +//KS - might be OK with 4 but 5 guarantees the deepest + 4 contacts that contribute to largest surface area +#define CONTACT_REDUCTION_MAX_CONTACTS 6 +#define CONTACT_REDUCTION_MAX_PATCHES 32 +#define PXS_NORMAL_TOLERANCE 0.995f +#define PXS_SEPARATION_TOLERANCE 0.001f + + + //A patch contains a normal, pair of material indices and a list of indices. These indices are + //used to index into the PxContact array that's passed by the user + struct ReducedContactPatch + { + PxU32 numContactPoints; + PxU32 contactPoints[CONTACT_REDUCTION_MAX_CONTACTS]; + }; + + struct ContactPatch + { + PxVec3 rootNormal; + ContactPatch* mNextPatch; + PxReal maxPenetration; + PxU16 startIndex; + PxU16 stride; + PxU16 rootIndex; + PxU16 index; + }; + + struct SortBoundsPredicateManifold + { + bool operator()(const ContactPatch* idx1, const ContactPatch* idx2) const + { + return idx1->maxPenetration < idx2->maxPenetration; + } + }; + + + + template + class ContactReduction + { + public: + ReducedContactPatch mPatches[MaxPatches]; + PxU32 mNumPatches; + ContactPatch mIntermediatePatches[CONTACT_REDUCTION_MAX_PATCHES]; + ContactPatch* mIntermediatePatchesPtrs[CONTACT_REDUCTION_MAX_PATCHES]; + PxU32 mNumIntermediatePatches; + Gu::ContactPoint* PX_RESTRICT mOriginalContacts; + PxsMaterialInfo* PX_RESTRICT mMaterialInfo; + PxU32 mNumOriginalContacts; + + ContactReduction(Gu::ContactPoint* PX_RESTRICT originalContacts, PxsMaterialInfo* PX_RESTRICT materialInfo, PxU32 numContacts) : + mNumPatches(0), mNumIntermediatePatches(0), mOriginalContacts(originalContacts), mMaterialInfo(materialInfo), mNumOriginalContacts(numContacts) + { + } + + void reduceContacts() + { + //First pass, break up into contact patches, storing the start and stride of the patches + //We will need to have contact patches and then coallesce them + mIntermediatePatches[0].rootNormal = mOriginalContacts[0].normal; + mIntermediatePatches[0].mNextPatch = NULL; + mIntermediatePatches[0].startIndex = 0; + mIntermediatePatches[0].rootIndex = 0; + mIntermediatePatches[0].maxPenetration = mOriginalContacts[0].separation; + mIntermediatePatches[0].index = 0; + PxU16 numPatches = 1; + //PxU32 startIndex = 0; + PxU32 numUniquePatches = 1; + PxU16 m = 1; + for(; m < mNumOriginalContacts; ++m) + { + PxI32 index = -1; + for(PxU32 b = numPatches; b > 0; --b) + { + ContactPatch& patch = mIntermediatePatches[b-1]; + if(mMaterialInfo[patch.startIndex].mMaterialIndex0 == mMaterialInfo[m].mMaterialIndex0 && mMaterialInfo[patch.startIndex].mMaterialIndex1 == mMaterialInfo[m].mMaterialIndex1 && + patch.rootNormal.dot(mOriginalContacts[m].normal) >= PXS_NORMAL_TOLERANCE) + { + index = PxI32(b-1); + break; + } + } + + if(index != numPatches - 1) + { + mIntermediatePatches[numPatches-1].stride = PxU16(m - mIntermediatePatches[numPatches - 1].startIndex); + //Create a new patch... + if(numPatches == CONTACT_REDUCTION_MAX_PATCHES) + { + break; + } + mIntermediatePatches[numPatches].startIndex = m; + mIntermediatePatches[numPatches].mNextPatch = NULL; + if(index == -1) + { + mIntermediatePatches[numPatches].rootIndex = numPatches; + mIntermediatePatches[numPatches].rootNormal = mOriginalContacts[m].normal; + mIntermediatePatches[numPatches].maxPenetration = mOriginalContacts[m].separation; + mIntermediatePatches[numPatches].index = numPatches; + ++numUniquePatches; + } + else + { + //Find last element in the link + PxU16 rootIndex = mIntermediatePatches[index].rootIndex; + mIntermediatePatches[index].mNextPatch = &mIntermediatePatches[numPatches]; + mIntermediatePatches[numPatches].rootNormal = mIntermediatePatches[index].rootNormal; + mIntermediatePatches[rootIndex].maxPenetration = mIntermediatePatches[numPatches].maxPenetration = PxMin(mIntermediatePatches[rootIndex].maxPenetration, mOriginalContacts[m].separation); + mIntermediatePatches[numPatches].rootIndex = rootIndex; + mIntermediatePatches[numPatches].index = numPatches; + } + ++numPatches; + } + } + mIntermediatePatches[numPatches-1].stride = PxU16(m - mIntermediatePatches[numPatches-1].startIndex); + + //OK, we have a list of contact patches so that we can start contact reduction per-patch + + //OK, now we can go and reduce the contacts on a per-patch basis... + + for(PxU32 a = 0; a < numPatches; ++a) + { + mIntermediatePatchesPtrs[a] = &mIntermediatePatches[a]; + } + + + SortBoundsPredicateManifold predicate; + Ps::sort(mIntermediatePatchesPtrs, numPatches, predicate); + + PxU32 numReducedPatches = 0; + for(PxU32 a = 0; a < numPatches; ++a) + { + if(mIntermediatePatchesPtrs[a]->rootIndex == mIntermediatePatchesPtrs[a]->index) + { + //Reduce this patch... + if(numReducedPatches == MaxPatches) + break; + + ReducedContactPatch& reducedPatch = mPatches[numReducedPatches++]; + //OK, now we need to work out if we have to reduce patches... + PxU32 contactCount = 0; + { + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + + while(tmpPatch) + { + contactCount += tmpPatch->stride; + tmpPatch = tmpPatch->mNextPatch; + } + } + + if(contactCount <= CONTACT_REDUCTION_MAX_CONTACTS) + { + //Just add the contacts... + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + + PxU32 ind = 0; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + reducedPatch.contactPoints[ind++] = tmpPatch->startIndex + b; + } + tmpPatch = tmpPatch->mNextPatch; + } + reducedPatch.numContactPoints = contactCount; + } + else + { + //Iterate through and find the most extreme point + + + PxU32 ind = 0; + + { + PxReal dist = 0.f; + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + PxReal magSq = mOriginalContacts[tmpPatch->startIndex + b].point.magnitudeSquared(); + if(dist < magSq) + { + ind = tmpPatch->startIndex + b; + dist = magSq; + } + } + tmpPatch = tmpPatch->mNextPatch; + } + } + reducedPatch.contactPoints[0] = ind; + const PxVec3 p0 = mOriginalContacts[ind].point; + + //Now find the point farthest from this point... + { + PxReal maxDist = 0.f; + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + PxReal magSq = (p0 - mOriginalContacts[tmpPatch->startIndex + b].point).magnitudeSquared(); + if(magSq > maxDist) + { + ind = tmpPatch->startIndex + b; + maxDist = magSq; + } + } + tmpPatch = tmpPatch->mNextPatch; + } + } + reducedPatch.contactPoints[1] = ind; + const PxVec3 p1 = mOriginalContacts[ind].point; + + //Now find the point farthest from the segment + + PxVec3 n = (p0 - p1).cross(mIntermediatePatchesPtrs[a]->rootNormal); + + //PxReal tVal = 0.f; + { + PxReal maxDist = 0.f; + //PxReal tmpTVal; + + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + + //PxReal magSq = tmpDistancePointSegmentSquared(p0, p1, mOriginalContacts[tmpPatch->startIndex + b].point, tmpTVal); + PxReal magSq = (mOriginalContacts[tmpPatch->startIndex + b].point - p0).dot(n); + if(magSq > maxDist) + { + ind = tmpPatch->startIndex + b; + //tVal = tmpTVal; + maxDist = magSq; + } + } + tmpPatch = tmpPatch->mNextPatch; + } + } + reducedPatch.contactPoints[2] = ind; + + //const PxVec3 closest = (p0 + (p1 - p0) * tVal); + + const PxVec3 dir = -n;//closest - p3; + + { + PxReal maxDist = 0.f; + //PxReal tVal = 0.f; + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + PxReal magSq = (mOriginalContacts[tmpPatch->startIndex + b].point - p0).dot(dir); + if(magSq > maxDist) + { + ind = tmpPatch->startIndex + b; + maxDist = magSq; + } + } + tmpPatch = tmpPatch->mNextPatch; + } + } + reducedPatch.contactPoints[3] = ind; + + //Now, we iterate through all the points, and cluster the points. From this, we establish the deepest point that's within a + //tolerance of this point and keep that point + + PxReal separation[CONTACT_REDUCTION_MAX_CONTACTS]; + PxU32 deepestInd[CONTACT_REDUCTION_MAX_CONTACTS]; + for(PxU32 i = 0; i < 4; ++i) + { + PxU32 index = reducedPatch.contactPoints[i]; + separation[i] = mOriginalContacts[index].separation - PXS_SEPARATION_TOLERANCE; + deepestInd[i] = index; + } + + ContactPatch* tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + Gu::ContactPoint& point = mOriginalContacts[tmpPatch->startIndex + b]; + + PxReal distance = PX_MAX_REAL; + PxU32 index = 0; + for(PxU32 c = 0; c < 4; ++c) + { + PxVec3 dif = mOriginalContacts[reducedPatch.contactPoints[c]].point - point.point; + PxReal d = dif.magnitudeSquared(); + if(distance > d) + { + distance = d; + index = c; + } + } + if(separation[index] > point.separation) + { + deepestInd[index] = tmpPatch->startIndex+b; + separation[index] = point.separation; + } + + } + tmpPatch = tmpPatch->mNextPatch; + } + + bool chosen[64]; + PxMemZero(chosen, sizeof(chosen)); + for(PxU32 i = 0; i < 4; ++i) + { + reducedPatch.contactPoints[i] = deepestInd[i]; + chosen[deepestInd[i]] = true; + } + + for(PxU32 i = 4; i < CONTACT_REDUCTION_MAX_CONTACTS; ++i) + { + separation[i] = PX_MAX_REAL; + deepestInd[i] = 0; + } + tmpPatch = mIntermediatePatchesPtrs[a]; + while(tmpPatch) + { + for(PxU32 b = 0; b < tmpPatch->stride; ++b) + { + if(!chosen[tmpPatch->startIndex+b]) + { + Gu::ContactPoint& point = mOriginalContacts[tmpPatch->startIndex + b]; + for(PxU32 j = 4; j < CONTACT_REDUCTION_MAX_CONTACTS; ++j) + { + if(point.separation < separation[j]) + { + for(PxU32 k = CONTACT_REDUCTION_MAX_CONTACTS-1; k > j; --k) + { + separation[k] = separation[k-1]; + deepestInd[k] = deepestInd[k-1]; + } + separation[j] = point.separation; + deepestInd[j] = tmpPatch->startIndex+b; + break; + } + } + } + } + tmpPatch = tmpPatch->mNextPatch; + } + + for(PxU32 i = 4; i < CONTACT_REDUCTION_MAX_CONTACTS; ++i) + { + reducedPatch.contactPoints[i] = deepestInd[i]; + } + + reducedPatch.numContactPoints = CONTACT_REDUCTION_MAX_CONTACTS; + } + } + } + mNumPatches = numReducedPatches; + } + + }; +} + +} + + +#endif //DY_CONTACT_REDUCTION_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h b/src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h new file mode 100644 index 000000000..4a091de52 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h @@ -0,0 +1,107 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef DY_CORRELATIONBUFFER_H +#define DY_CORRELATIONBUFFER_H + +#include "PxvConfig.h" +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "DyFrictionPatch.h" +#include "GuContactBuffer.h" +#include "foundation/PxBounds3.h" + +namespace physx +{ + +struct PxcNpWorkUnit; +struct PxsMaterialInfo; + +namespace Dy +{ + +struct CorrelationBuffer +{ + static const PxU32 MAX_FRICTION_PATCHES = 32; + static const PxU16 LIST_END = 0xffff; + + struct ContactPatchData + { + PxU16 start; + PxU16 next; + PxU8 flags; + PxU8 count; + PxReal staticFriction, dynamicFriction, restitution; + PxBounds3 patchBounds; + }; + + // we can have as many contact patches as contacts, unfortunately + ContactPatchData contactPatches[Gu::ContactBuffer::MAX_CONTACTS]; + + FrictionPatch PX_ALIGN(16, frictionPatches[MAX_FRICTION_PATCHES]); + PxVec3 PX_ALIGN(16, frictionPatchWorldNormal[MAX_FRICTION_PATCHES]); + PxBounds3 patchBounds[MAX_FRICTION_PATCHES]; + + PxU32 frictionPatchContactCounts[MAX_FRICTION_PATCHES]; + PxU32 correlationListHeads[MAX_FRICTION_PATCHES+1]; + + // contact IDs are only used to identify auxiliary contact data when velocity + // targets have been set. + PxU16 contactID[MAX_FRICTION_PATCHES][2]; + + PxU32 contactPatchCount, frictionPatchCount; + +}; + +bool createContactPatches(CorrelationBuffer& fb, const Gu::ContactPoint* cb, PxU32 contactCount, PxReal normalTolerance); + +bool correlatePatches(CorrelationBuffer& fb, + const Gu::ContactPoint* cb, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxReal normalTolerance, + PxU32 startContactPatchIndex, + PxU32 startFrictionPatchIndex); + +void growPatches(CorrelationBuffer& fb, + const Gu::ContactPoint* buffer, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxReal normalTolerance, + PxU32 frictionPatchStartIndex, + PxReal frictionOffsetThreshold); + +} + +} + +#endif //DY_CORRELATIONBUFFER_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp new file mode 100644 index 000000000..46df6a084 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp @@ -0,0 +1,3145 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsTime.h" +#include "PsAtomic.h" +#include "PxvDynamics.h" + +#include "common/PxProfileZone.h" +#include "PxsRigidBody.h" +#include "PxsContactManager.h" +#include "DyDynamics.h" +#include "DyBodyCoreIntegrator.h" +#include "DySolverCore.h" +#include "DySolverControl.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DyArticulationContactPrep.h" +#include "DySolverBody.h" + +#include "DyConstraintPrep.h" +#include "DyConstraintPartition.h" +#include "DyArticulation.h" + +#include "CmFlushPool.h" +#include "DyArticulationPImpl.h" +#include "PxsMaterialManager.h" +#include "DySolverContactPF4.h" +#include "DyContactReduction.h" +#include "PxcNpContactPrepShared.h" +#include "DyContactPrep.h" +#include "DySolverControlPF.h" +#include "PxSceneDesc.h" +#include "PxsSimpleIslandManager.h" +#include "PxvNphaseImplementationContext.h" +#include "PxvSimStats.h" +#include "PxsContactManagerState.h" +#include "PxsDefaultMemoryManager.h" +#include "DyContactPrepShared.h" + +//KS - used to turn on/off batched SIMD constraints. +#define DY_BATCH_CONSTRAINTS 1 +//KS - used to specifically turn on/off batches 1D SIMD constraints. +#define DY_BATCH_1D 1 + +namespace physx +{ +namespace Dy +{ + +struct SolverIslandObjects +{ + PxsRigidBody** bodies; + ArticulationV** articulations; + Dy::ArticulationV** articulationOwners; + PxsIndexedContactManager* contactManagers; + //PxsIndexedConstraint* constraints; + + const IG::IslandId* islandIds; + PxU32 numIslands; + PxU32* bodyRemapTable; + PxU32* nodeIndexArray; + + PxSolverConstraintDesc* constraintDescs; + PxSolverConstraintDesc* orderedConstraintDescs; + PxSolverConstraintDesc* tempConstraintDescs; + PxConstraintBatchHeader* constraintBatchHeaders; + Cm::SpatialVector* motionVelocities; + PxsBodyCore** bodyCoreArray; + + SolverIslandObjects() : bodies(NULL), articulations(NULL), articulationOwners(NULL), + contactManagers(NULL), islandIds(NULL), numIslands(0), nodeIndexArray(NULL), constraintDescs(NULL), orderedConstraintDescs(NULL), + tempConstraintDescs(NULL), constraintBatchHeaders(NULL), motionVelocities(NULL), bodyCoreArray(NULL) + { + } +}; + +Context* createDynamicsContext( PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, Cm::FlushPool& taskPool, + PxvSimStats& simStats, PxTaskManager* taskManager, Ps::VirtualAllocatorCallback* allocatorCallback, + PxsMaterialManager* materialManager, IG::IslandSim* accurateIslandSim, PxU64 contextID, + const bool enableStabilization, const bool useEnhancedDeterminism, const bool useAdaptiveForce, + const PxReal maxBiasCoefficient, const bool frictionEveryIteration + ) +{ + return DynamicsContext::create( memBlockPool, scratchAllocator, taskPool, simStats, taskManager, allocatorCallback, materialManager, accurateIslandSim, + contextID, enableStabilization, useEnhancedDeterminism, useAdaptiveForce, maxBiasCoefficient, frictionEveryIteration); +} + +// PT: TODO: consider removing this function. We already have "createDynamicsContext". +DynamicsContext* DynamicsContext::create( PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocatorCallback, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal maxBiasCoefficient, + const bool frictionEveryIteration + ) +{ + // PT: TODO: inherit from UserAllocated, remove placement new + DynamicsContext* dc = reinterpret_cast(PX_ALLOC(sizeof(DynamicsContext), "DynamicsContext")); + if(dc) + { + new(dc)DynamicsContext(memBlockPool, scratchAllocator, taskPool, simStats, taskManager, allocatorCallback, materialManager, accurateIslandSim, contextID, + enableStabilization, useEnhancedDeterminism, useAdaptiveForce, maxBiasCoefficient, frictionEveryIteration); + } + return dc; +} + + +void DynamicsContext::destroy() +{ + this->~DynamicsContext(); + PX_FREE(this); +} + +void DynamicsContext::resetThreadContexts() +{ + PxcThreadCoherentCacheIterator threadContextIt(mThreadContextPool); + ThreadContext* threadContext = threadContextIt.getNext(); + + while(threadContext != NULL) + { + threadContext->reset(); + threadContext = threadContextIt.getNext(); + } +} + + +// =========================== Basic methods + + +DynamicsContext::DynamicsContext( PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocatorCallback, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal maxBiasCoefficient, + const bool frictionEveryIteration + ) : + Dy::Context (accurateIslandSim, allocatorCallback, simStats, enableStabilization, useEnhancedDeterminism, useAdaptiveForce, maxBiasCoefficient), + mThreadContextPool (memBlockPool), + mMaterialManager (materialManager), + mScratchAllocator (scratchAllocator), + mTaskPool (taskPool), + mTaskManager (taskManager), + mContextID (contextID) +{ + createThresholdStream(*allocatorCallback); + createForceChangeThresholdStream(*allocatorCallback); + mExceededForceThresholdStream[0] = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ExceededForceThresholdStream[0]")), ThresholdStream(*allocatorCallback)); + mExceededForceThresholdStream[1] = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ExceededForceThresholdStream[1]")), ThresholdStream(*allocatorCallback)); + mThresholdStreamOut = 0; + mCurrentIndex = 0; + mWorldSolverBody.linearVelocity = PxVec3(0); + mWorldSolverBody.angularState = PxVec3(0); + mWorldSolverBodyData.invMass = 0; + mWorldSolverBodyData.sqrtInvInertia = PxMat33(PxZero); + mWorldSolverBodyData.nodeIndex = IG_INVALID_NODE; + mWorldSolverBodyData.reportThreshold = PX_MAX_REAL; + mWorldSolverBodyData.penBiasClamp = -PX_MAX_REAL; + mWorldSolverBodyData.maxContactImpulse = PX_MAX_REAL; + mWorldSolverBody.solverProgress=MAX_PERMITTED_SOLVER_PROGRESS; + mWorldSolverBody.maxSolverNormalProgress=MAX_PERMITTED_SOLVER_PROGRESS; + mWorldSolverBody.maxSolverFrictionProgress=MAX_PERMITTED_SOLVER_PROGRESS; + mWorldSolverBodyData.linearVelocity = mWorldSolverBodyData.angularVelocity = PxVec3(0.f); + mWorldSolverBodyData.body2World = PxTransform(PxIdentity); + mWorldSolverBodyData.lockFlags = 0; + mSolverCore[PxFrictionType::ePATCH] = SolverCoreGeneral::create(frictionEveryIteration); + mSolverCore[PxFrictionType::eONE_DIRECTIONAL] = SolverCoreGeneralPF::create(); + mSolverCore[PxFrictionType::eTWO_DIRECTIONAL] = SolverCoreGeneralPF::create(); +} + +DynamicsContext::~DynamicsContext() +{ + for(PxU32 i = 0; i < PxFrictionType::eFRICTION_COUNT; ++i) + { + mSolverCore[i]->destroyV(); + } + + if(mExceededForceThresholdStream[0]) + { + mExceededForceThresholdStream[0]->~ThresholdStream(); + PX_FREE(mExceededForceThresholdStream[0]); + } + mExceededForceThresholdStream[0] = NULL; + + if(mExceededForceThresholdStream[1]) + { + mExceededForceThresholdStream[1]->~ThresholdStream(); + PX_FREE(mExceededForceThresholdStream[1]); + } + mExceededForceThresholdStream[1] = NULL; + +} + +#if PX_ENABLE_SIM_STATS +void DynamicsContext::addThreadStats(const ThreadContext::ThreadSimStats& stats) +{ + mSimStats.mNbActiveConstraints += stats.numActiveConstraints; + mSimStats.mNbActiveDynamicBodies += stats.numActiveDynamicBodies; + mSimStats.mNbActiveKinematicBodies += stats.numActiveKinematicBodies; + mSimStats.mNbAxisSolverConstraints += stats.numAxisSolverConstraints; +} +#endif + +// =========================== Solve methods! + +void DynamicsContext::setDescFromIndices(PxSolverConstraintDesc& desc, const PxsIndexedInteraction& constraint, const PxU32 solverBodyOffset) +{ + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eBODY == 0); + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eKINEMATIC == 1); + const PxU32 offsetMap[] = {solverBodyOffset, 0}; + //const PxU32 offsetMap[] = {mKinematicCount, 0}; + + if(constraint.indexType0 == PxsIndexedInteraction::eARTICULATION) + { + ArticulationV* a = getArticulation(constraint.articulation0); + desc.articulationA = a; + desc.articulationALength = Ps::to16(a->getSolverDataSize()); + PX_ASSERT(0==(desc.articulationALength & 0x0f)); + desc.linkIndexA = Ps::to16(a->getLinkIndex(constraint.articulation0)); + } + else + { + desc.linkIndexA = PxSolverConstraintDesc::NO_LINK; + //desc.articulationALength = 0; //this is unioned with bodyADataIndex + /*desc.bodyA = constraint.indexType0 == PxsIndexedInteraction::eWORLD ? &mWorldSolverBody + : &mSolverBodyPool[(PxU32)constraint.solverBody0 + offsetMap[constraint.indexType0]]; + desc.bodyADataIndex = PxU16(constraint.indexType0 == PxsIndexedInteraction::eWORLD ? 0 + : (PxU16)constraint.solverBody0 + 1 + offsetMap[constraint.indexType0]);*/ + + desc.bodyA = constraint.indexType0 == PxsIndexedInteraction::eWORLD ? &mWorldSolverBody + : &mSolverBodyPool[PxU32(constraint.solverBody0) + offsetMap[constraint.indexType0]]; + desc.bodyADataIndex = constraint.indexType0 == PxsIndexedInteraction::eWORLD ? 0 + : PxU32(constraint.solverBody0) + 1 + offsetMap[constraint.indexType0]; + } + + if(constraint.indexType1 == PxsIndexedInteraction::eARTICULATION) + { + ArticulationV* b = getArticulation(constraint.articulation1); + desc.articulationB = b; + desc.articulationBLength = Ps::to16(b->getSolverDataSize()); + PX_ASSERT(0==(desc.articulationBLength & 0x0f)); + desc.linkIndexB = Ps::to16(b->getLinkIndex(constraint.articulation1)); + } + else + { + desc.linkIndexB = PxSolverConstraintDesc::NO_LINK; + //desc.articulationBLength = 0; //this is unioned with bodyBDataIndex + desc.bodyB = constraint.indexType1 == PxsIndexedInteraction::eWORLD ? &mWorldSolverBody + : &mSolverBodyPool[PxU32(constraint.solverBody1) + offsetMap[constraint.indexType1]]; + desc.bodyBDataIndex = constraint.indexType1 == PxsIndexedInteraction::eWORLD ? 0 + : PxU32(constraint.solverBody1) + 1 + offsetMap[constraint.indexType1]; + } +} + +void DynamicsContext::setDescFromIndices(PxSolverConstraintDesc& desc, IG::EdgeIndex edgeIndex, const IG::SimpleIslandManager& islandManager, + PxU32* bodyRemap, const PxU32 solverBodyOffset) +{ + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eBODY == 0); + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eKINEMATIC == 1); + + const IG::IslandSim& islandSim = islandManager.getAccurateIslandSim(); + + IG::NodeIndex node1 = islandSim.getNodeIndex1(edgeIndex); + if (node1.isStaticBody()) + { + desc.bodyA = &mWorldSolverBody; + desc.bodyADataIndex = 0; + desc.linkIndexA = PxSolverConstraintDesc::NO_LINK; + } + else + { + const IG::Node& node = islandSim.getNode(node1); + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + Dy::ArticulationV* a = islandSim.getLLArticulation(node1); + desc.articulationA = a; + desc.articulationALength = Ps::to16(a->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationALength & 0x0f)); + desc.linkIndexA = Ps::to16(node1.articulationLinkId()); + } + else + { + PxU32 activeIndex = islandSim.getActiveNodeIndex(node1); + PxU32 index = node.isKinematic() ? activeIndex : bodyRemap[activeIndex] + solverBodyOffset; + desc.bodyA = &mSolverBodyPool[index]; + desc.bodyADataIndex = Ps::to16(index + 1); + desc.linkIndexA = PxSolverConstraintDesc::NO_LINK; + } + } + + IG::NodeIndex node2 = islandSim.getNodeIndex2(edgeIndex); + if (node2.isStaticBody()) + { + desc.bodyB = &mWorldSolverBody; + desc.bodyBDataIndex = 0; + desc.linkIndexB = PxSolverConstraintDesc::NO_LINK; + } + else + { + const IG::Node& node = islandSim.getNode(node2); + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + Dy::ArticulationV* b = islandSim.getLLArticulation(node2); + desc.articulationB = b; + desc.articulationBLength = Ps::to16(b->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationBLength & 0x0f)); + desc.linkIndexB = Ps::to16(node2.articulationLinkId()); + } + else + { + PxU32 activeIndex = islandSim.getActiveNodeIndex(node2); + PxU32 index = node.isKinematic() ? activeIndex : bodyRemap[activeIndex] + solverBodyOffset; + desc.bodyB = &mSolverBodyPool[index]; + desc.bodyBDataIndex = Ps::to16(index + 1); + desc.linkIndexB = PxSolverConstraintDesc::NO_LINK; + } + } +} + + +class PxsPreIntegrateTask : public Cm::Task +{ + PxsPreIntegrateTask& operator=(const PxsPreIntegrateTask&); +public: + PxsPreIntegrateTask( DynamicsContext& context, + PxsBodyCore*const* bodyArray, + PxsRigidBody*const* originalBodyArray, + PxU32 const* nodeIndexArray, + PxSolverBody* solverBodies, + PxSolverBodyData* solverBodyDataPool, + PxF32 dt, + PxU32 numBodies, + volatile PxU32* maxSolverPositionIterations, + volatile PxU32* maxSolverVelocityIterations, + const PxU32 startIndex, + const PxU32 numToIntegrate, + const PxVec3& gravity) : + Cm::Task (context.getContextId()), + mContext (context), + mBodyArray (bodyArray), + mOriginalBodyArray (originalBodyArray), + mNodeIndexArray (nodeIndexArray), + mSolverBodies (solverBodies), + mSolverBodyDataPool (solverBodyDataPool), + mDt (dt), + mNumBodies (numBodies), + mMaxSolverPositionIterations(maxSolverPositionIterations), + mMaxSolverVelocityIterations(maxSolverVelocityIterations), + mStartIndex (startIndex), + mNumToIntegrate (numToIntegrate), + mGravity (gravity) + {} + + virtual void runInternal(); + + virtual const char* getName() const + { + return "PxsDynamics.preIntegrate"; + } + +public: + DynamicsContext& mContext; + PxsBodyCore*const* mBodyArray; + PxsRigidBody*const* mOriginalBodyArray; + PxU32 const* mNodeIndexArray; + PxSolverBody* mSolverBodies; + PxSolverBodyData* mSolverBodyDataPool; + PxF32 mDt; + PxU32 mNumBodies; + volatile PxU32* mMaxSolverPositionIterations; + volatile PxU32* mMaxSolverVelocityIterations; + PxU32 mStartIndex; + PxU32 mNumToIntegrate; + PxVec3 mGravity; + +}; + + + +class PxsParallelSolverTask : public Cm::Task +{ + PxsParallelSolverTask& operator=(PxsParallelSolverTask&); +public: + + PxsParallelSolverTask(SolverIslandParams& params, DynamicsContext& context, PxFrictionType::Enum frictionType, IG::IslandSim& islandSim) + : Cm::Task(context.getContextId()), mParams(params), mContext(context), mFrictionType(frictionType), mIslandSim(islandSim) + { + } + + virtual void runInternal() + { + solveParallel(mContext, mParams, mIslandSim); + } + + virtual const char* getName() const + { + return "PxsDynamics.parallelSolver"; + } + + SolverIslandParams& mParams; + DynamicsContext& mContext; + PxFrictionType::Enum mFrictionType; + IG::IslandSim& mIslandSim; +}; + + +#define PX_CONTACT_REDUCTION 1 + +class PxsSolverConstraintPostProcessTask : public Cm::Task +{ + PxsSolverConstraintPostProcessTask& operator=(const PxsSolverConstraintPostProcessTask&); +public: + + PxsSolverConstraintPostProcessTask(DynamicsContext& context, + ThreadContext& threadContext, + const SolverIslandObjects& objects, + const PxU32 solverBodyOffset, + PxU32 startIndex, + PxU32 stride, + PxsMaterialManager* materialManager, + PxsContactManagerOutputIterator& iterator) : + Cm::Task (context.getContextId()), + mContext (context), + mThreadContext (threadContext), + mObjects (objects), + mSolverBodyOffset (solverBodyOffset), + mStartIndex (startIndex), + mStride (stride), + mMaterialManager (materialManager), + mOutputs (iterator) + {} + + void mergeContacts(CompoundContactManager& header, ThreadContext& threadContext) + { + Gu::ContactBuffer& buffer = threadContext.mContactBuffer; + PxsMaterialInfo materialInfo[Gu::ContactBuffer::MAX_CONTACTS]; + PxU32 size = 0; + + for(PxU32 a = 0; a < header.mStride; ++a) + { + PxsContactManager* manager = mThreadContext.orderedContactList[a+header.mStartIndex]->contactManager; + PxcNpWorkUnit& unit = manager->getWorkUnit(); + PxsContactManagerOutput& output = mOutputs.getContactManager(unit.mNpIndex); + PxContactStreamIterator iter(output.contactPatches, output.contactPoints, output.getInternalFaceIndice(), output.nbPatches, output.nbContacts); + + PxU32 origSize = size; + PX_UNUSED(origSize); + if(!iter.forceNoResponse) + { + while(iter.hasNextPatch()) + { + iter.nextPatch(); + while(iter.hasNextContact()) + { + PX_ASSERT(size < Gu::ContactBuffer::MAX_CONTACTS); + iter.nextContact(); + PxsMaterialInfo& info = materialInfo[size]; + Gu::ContactPoint& point = buffer.contacts[size++]; + point.dynamicFriction = iter.getDynamicFriction(); + point.staticFriction = iter.getStaticFriction(); + point.restitution = iter.getRestitution(); + point.internalFaceIndex1 = iter.getFaceIndex1(); + point.materialFlags = PxU8(iter.getMaterialFlags()); + point.maxImpulse = iter.getMaxImpulse(); + point.targetVel = iter.getTargetVel(); + point.normal = iter.getContactNormal(); + point.point = iter.getContactPoint(); + point.separation = iter.getSeparation(); + info.mMaterialIndex0 = iter.getMaterialIndex0(); + info.mMaterialIndex1 = iter.getMaterialIndex1(); + } + } + PX_ASSERT(output.nbContacts == (size - origSize)); + } + } + + PxU32 origSize = size; +#if PX_CONTACT_REDUCTION + ContactReduction<6> reduction(buffer.contacts, materialInfo, size); + reduction.reduceContacts(); + //OK, now we write back the contacts... + + PxU8 histo[Gu::ContactBuffer::MAX_CONTACTS]; + PxMemZero(histo, sizeof(histo)); + + size = 0; + for(PxU32 a = 0; a < reduction.mNumPatches; ++a) + { + ReducedContactPatch& patch = reduction.mPatches[a]; + for(PxU32 b = 0; b < patch.numContactPoints; ++b) + { + histo[patch.contactPoints[b]] = 1; + ++size; + } + } +#endif + + PxU16* PX_RESTRICT data = reinterpret_cast(threadContext.mConstraintBlockStream.reserve(size * sizeof(PxU16), mThreadContext.mConstraintBlockManager)); + header.forceBufferList = data; + + +#if PX_CONTACT_REDUCTION + const PxU32 reservedSize = size; + PX_UNUSED(reservedSize); + size = 0; + for(PxU32 a = 0; a < origSize; ++a) + { + if(histo[a]) + { + if(size != a) + { + buffer.contacts[size] = buffer.contacts[a]; + materialInfo[size] = materialInfo[a]; + } + data[size] = Ps::to16(a); + size++; + } + } + PX_ASSERT(reservedSize >= size); +#else + for(PxU32 a = 0; a < size; ++a) + data[a] = a; +#endif + + + PxU32 contactForceByteSize = size * sizeof(PxReal); + + + PxsContactManagerOutput& output = mOutputs.getContactManager(header.unit->mNpIndex); + + PxU16 compressedContactSize; + + physx::writeCompressedContact(buffer.contacts, size, NULL, output.nbContacts, output.contactPatches, output.contactPoints, compressedContactSize, + reinterpret_cast(output.contactForces), contactForceByteSize, mMaterialManager, false, + false, materialInfo, output.nbPatches, 0, &mThreadContext.mConstraintBlockManager, &threadContext.mConstraintBlockStream, false); + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("ConstraintPostProcess", 0); + PxU32 endIndex = mStartIndex + mStride; + + ThreadContext* threadContext = mContext.getThreadContext(); + //TODO - we need to do this somewhere else + //threadContext->mContactBlockStream.reset(); + threadContext->mConstraintBlockStream.reset(); + + for(PxU32 a = mStartIndex; a < endIndex; ++a) + { + mergeContacts(mThreadContext.compoundConstraints[a], *threadContext); + } + mContext.putThreadContext(threadContext); + } + + virtual const char* getName() const { return "PxsDynamics.solverConstraintPostProcess"; } + + + DynamicsContext& mContext; + ThreadContext& mThreadContext; + const SolverIslandObjects mObjects; + PxU32 mSolverBodyOffset; + PxU32 mStartIndex; + PxU32 mStride; + PxsMaterialManager* mMaterialManager; + PxsContactManagerOutputIterator& mOutputs; +}; + +class PxsForceThresholdTask : public Cm::Task +{ + DynamicsContext& mDynamicsContext; + + PxsForceThresholdTask& operator=(const PxsForceThresholdTask&); +public: + + PxsForceThresholdTask(DynamicsContext& context): Cm::Task(context.getContextId()), mDynamicsContext(context) + { + } + + void createForceChangeThresholdStream() + { + ThresholdStream& thresholdStream = mDynamicsContext.getThresholdStream(); + //bool haveThresholding = thresholdStream.size()!=0; + + ThresholdTable& thresholdTable = mDynamicsContext.getThresholdTable(); + thresholdTable.build(thresholdStream); + + //generate current force exceeded threshold stream + ThresholdStream& curExceededForceThresholdStream = *mDynamicsContext.mExceededForceThresholdStream[mDynamicsContext.mCurrentIndex]; + ThresholdStream& preExceededForceThresholdStream = *mDynamicsContext.mExceededForceThresholdStream[1 - mDynamicsContext.mCurrentIndex]; + curExceededForceThresholdStream.forceSize_Unsafe(0); + + //fill in the currrent exceeded force threshold stream + for(PxU32 i=0; i elem.threshold * mDynamicsContext.mDt) + { + elem.accumulatedForce = pair.accumulatedForce; + curExceededForceThresholdStream.pushBack(elem); + } + } + + ThresholdStream& forceChangeThresholdStream = mDynamicsContext.getForceChangedThresholdStream(); + forceChangeThresholdStream.forceSize_Unsafe(0); + Ps::Array& forceChangeMask = mDynamicsContext.mExceededForceThresholdStreamMask; + + const PxU32 nbPreExceededForce = preExceededForceThresholdStream.size(); + const PxU32 nbCurExceededForce = curExceededForceThresholdStream.size(); + + //generate force change thresholdStream + if(nbPreExceededForce) + { + thresholdTable.build(preExceededForceThresholdStream); + + //set force change mask + const PxU32 nbTotalExceededForce = nbPreExceededForce + nbCurExceededForce; + forceChangeMask.reserve(nbTotalExceededForce); + forceChangeMask.forceSize_Unsafe(nbTotalExceededForce); + + //initialize the forceChangeMask + for (PxU32 i = 0; i < nbTotalExceededForce; ++i) + forceChangeMask[i] = 1; + + for(PxU32 i=0; i< nbCurExceededForce; ++i) + { + ThresholdStreamElement& curElem = curExceededForceThresholdStream[i]; + + PxU32 pos; + if(thresholdTable.check(preExceededForceThresholdStream, curElem, pos)) + { + forceChangeMask[pos] = 0; + forceChangeMask[i + nbPreExceededForce] = 0; + } + } + + //create force change threshold stream + for(PxU32 i=0; i(left.constraint)->index > reinterpret_cast(right.constraint)->index; + } +}; + +struct ArticulationSortPredicate +{ + bool operator()(const PxsIndexedContactManager*& left, const PxsIndexedContactManager*& right) const + { + return left->contactManager->getWorkUnit().index < right->contactManager->getWorkUnit().index; + } +}; + +class BlockAllocator : public PxConstraintAllocator +{ + PxsConstraintBlockManager& mConstraintBlockManager; + PxcConstraintBlockStream& mConstraintBlockStream; + FrictionPatchStreamPair& mFrictionPatchStreamPair; + PxU32& mTotalConstraintByteSize; +public: + + BlockAllocator(PxsConstraintBlockManager& constraintBlockManager, PxcConstraintBlockStream& constraintBlockStream, FrictionPatchStreamPair& frictionPatchStreamPair, + PxU32& totalConstraintByteSize) : + mConstraintBlockManager(constraintBlockManager), mConstraintBlockStream(constraintBlockStream), mFrictionPatchStreamPair(frictionPatchStreamPair), + mTotalConstraintByteSize(totalConstraintByteSize) + { + } + + virtual PxU8* reserveConstraintData(const PxU32 size) + { + mTotalConstraintByteSize += size; + return mConstraintBlockStream.reserve(size, mConstraintBlockManager); + } + + virtual PxU8* reserveFrictionData(const PxU32 size) + { + return mFrictionPatchStreamPair.reserve(size); + } + + virtual PxU8* findInputPatches(PxU8* frictionCookie) + { + return frictionCookie; + } + + PX_NOCOPY(BlockAllocator) + +}; + + +class SolverArticulationUpdateTask : public Cm::Task +{ + + + ThreadContext& mIslandThreadContext; + + ArticulationV** mArticulations; + ArticulationSolverDesc* mArticulationDescArray; + PxU32 mNbToProcess; + + Dy::DynamicsContext& mContext; + PxU32 mStartIdx; + +public: + + static const PxU32 NbArticulationsPerTask = 8; + + SolverArticulationUpdateTask(ThreadContext& islandThreadContext, ArticulationV** articulations, ArticulationSolverDesc* articulationDescArray, PxU32 nbToProcess, Dy::DynamicsContext& context, + PxU32 startIdx) : + Cm::Task(context.getContextId()), mIslandThreadContext(islandThreadContext), mArticulations(articulations), mArticulationDescArray(articulationDescArray), mNbToProcess(nbToProcess), mContext(context), mStartIdx(startIdx) + { + } + + virtual const char* getName() const { return "SolverArticulationUpdateTask"; } + + virtual void runInternal() + { + ThreadContext& threadContext = *mContext.getThreadContext(); + + threadContext.mConstraintBlockStream.reset(); //Clear in case there's some left-over memory in this context, for which the block has already been freed + PxU32 maxVelIters = 0; + PxU32 maxPosIters = 0; + PxU32 maxArticulationLength = 0; + PxU32 maxSolverArticLength = 0; + PxU32 maxLinks = 0; + + for (PxU32 i = 0; i < mNbToProcess; i++) + { + ArticulationV& a = *(mArticulations[i]); + a.getSolverDesc(mArticulationDescArray[i]); + + maxLinks = PxMax(maxLinks, PxU32(mArticulationDescArray[i].linkCount)); + } + + threadContext.mZVector.forceSize_Unsafe(0); + threadContext.mZVector.reserve(maxLinks); + threadContext.mZVector.forceSize_Unsafe(maxLinks); + + threadContext.mDeltaV.forceSize_Unsafe(0); + threadContext.mDeltaV.reserve(maxLinks); + threadContext.mDeltaV.forceSize_Unsafe(maxLinks); + + PxU32 startIdx = mStartIdx; + + BlockAllocator blockAllocator(mIslandThreadContext.mConstraintBlockManager, threadContext.mConstraintBlockStream, threadContext.mFrictionPatchStreamPair, threadContext.mConstraintSize); + + for(PxU32 i=0;i(PxU32(iterWord >> 8), maxVelIters); + maxPosIters = PxMax(PxU32(iterWord & 0xff), maxPosIters); + startIdx += DY_ARTICULATION_MAX_SIZE; + } + + Ps::atomicMax(reinterpret_cast(&mIslandThreadContext.mMaxSolverPositionIterations), PxI32(maxPosIters)); + Ps::atomicMax(reinterpret_cast(&mIslandThreadContext.mMaxSolverVelocityIterations), PxI32(maxVelIters)); + Ps::atomicMax(reinterpret_cast(&mIslandThreadContext.mMaxArticulationLength), PxI32(maxArticulationLength)); + Ps::atomicMax(reinterpret_cast(&mIslandThreadContext.mMaxArticulationSolverLength), PxI32(maxSolverArticLength)); + Ps::atomicMax(reinterpret_cast(&mIslandThreadContext.mMaxArticulationLinks), PxI32(maxLinks)); + + mContext.putThreadContext(&threadContext); + } + +private: + PX_NOCOPY(SolverArticulationUpdateTask) +}; + + +struct EnhancedSortPredicate +{ + bool operator()(const PxsIndexedContactManager& left, const PxsIndexedContactManager& right) const + { + PxcNpWorkUnit& unit0 = left.contactManager->getWorkUnit(); + PxcNpWorkUnit& unit1 = right.contactManager->getWorkUnit(); + return (unit0.mTransformCache0 < unit1.mTransformCache0) || + ((unit0.mTransformCache0 == unit1.mTransformCache0) && (unit0.mTransformCache1 < unit1.mTransformCache1)); + } +}; + + +class PxsSolverStartTask : public Cm::Task +{ + PxsSolverStartTask& operator=(const PxsSolverStartTask&); +public: + + PxsSolverStartTask(DynamicsContext& context, + IslandContext& islandContext, + const SolverIslandObjects& objects, + const PxU32 solverBodyOffset, + const PxU32 kinematicCount, + IG::SimpleIslandManager& islandManager, + PxU32* bodyRemapTable, + PxsMaterialManager* materialManager, + PxsContactManagerOutputIterator& iterator, + bool enhancedDeterminism + ) : + Cm::Task (context.getContextId()), + mContext (context), + mIslandContext (islandContext), + mObjects (objects), + mSolverBodyOffset (solverBodyOffset), + mKinematicCount (kinematicCount), + mIslandManager (islandManager), + mBodyRemapTable (bodyRemapTable), + mMaterialManager (materialManager), + mOutputs (iterator), + mEnhancedDeterminism (enhancedDeterminism) + {} + + void startTasks() + { + PX_PROFILE_ZONE("Dynamics.solveGroup", mContext.getContextId()); + { + ThreadContext& mThreadContext = *mContext.getThreadContext(); + + mIslandContext.mThreadContext = &mThreadContext; + + mThreadContext.mMaxSolverPositionIterations = 0; + mThreadContext.mMaxSolverVelocityIterations = 0; + mThreadContext.mAxisConstraintCount = 0; + mThreadContext.mContactDescPtr = mThreadContext.contactConstraintDescArray; + mThreadContext.mFrictionDescPtr = mThreadContext.frictionConstraintDescArray.begin(); + mThreadContext.mNumDifferentBodyConstraints = 0; + mThreadContext.mNumSelfConstraintBlocks = 0; + mThreadContext.mNumSelfConstraints = 0; + mThreadContext.mNumDifferentBodyFrictionConstraints = 0; + mThreadContext.mNumSelfConstraintFrictionBlocks = 0; + mThreadContext.mNumSelfFrictionConstraints = 0; + mThreadContext.numContactConstraintBatches = 0; + mThreadContext.contactDescArraySize = 0; + mThreadContext.mMaxArticulationLinks = 0; + + + mThreadContext.contactConstraintDescArray = mObjects.constraintDescs; + mThreadContext.orderedContactConstraints = mObjects.orderedConstraintDescs; + mThreadContext.mContactDescPtr = mObjects.constraintDescs; + mThreadContext.tempConstraintDescArray = mObjects.tempConstraintDescs; + mThreadContext.contactConstraintBatchHeaders = mObjects.constraintBatchHeaders; + mThreadContext.motionVelocityArray = mObjects.motionVelocities; + mThreadContext.mBodyCoreArray = mObjects.bodyCoreArray; + mThreadContext.mRigidBodyArray = mObjects.bodies; + mThreadContext.mArticulationArray = mObjects.articulations; + mThreadContext.bodyRemapTable = mObjects.bodyRemapTable; + mThreadContext.mNodeIndexArray = mObjects.nodeIndexArray; + + const PxU32 frictionConstraintCount = mContext.getFrictionType() == PxFrictionType::ePATCH ? 0 : PxU32(mIslandContext.mCounts.contactManagers); + mThreadContext.resizeArrays(frictionConstraintCount, mIslandContext.mCounts.articulations); + + PxsBodyCore** PX_RESTRICT bodyArrayPtr = mThreadContext.mBodyCoreArray; + PxsRigidBody** PX_RESTRICT rigidBodyPtr = mThreadContext.mRigidBodyArray; + ArticulationV** PX_RESTRICT articulationPtr = mThreadContext.mArticulationArray; + PxU32* PX_RESTRICT bodyRemapTable = mThreadContext.bodyRemapTable; + PxU32* PX_RESTRICT nodeIndexArray = mThreadContext.mNodeIndexArray; + + PxU32 nbIslands = mObjects.numIslands; + const IG::IslandId* const islandIds = mObjects.islandIds; + + const IG::IslandSim& islandSim = mIslandManager.getAccurateIslandSim(); + + PxU32 bodyIndex = 0, articIndex = 0; + for (PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::NodeIndex currentIndex = island.mRootNode; + + while (currentIndex.isValid()) + { + const IG::Node& node = islandSim.getNode(currentIndex); + + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + articulationPtr[articIndex++] = node.getArticulation(); + } + else + { + PX_ASSERT(bodyIndex < (mIslandContext.mCounts.bodies + mContext.mKinematicCount + 1)); + nodeIndexArray[bodyIndex++] = currentIndex.index(); + } + + currentIndex = node.mNextNode; + } + } + + //Bodies can come in a slightly jumbled order from islandGen. It's deterministic if the scene is + //identical but can vary if there are additional bodies in the scene in a different island. + if (mEnhancedDeterminism) + { + Ps::sort(nodeIndexArray, bodyIndex); + } + + for (PxU32 a = 0; a < bodyIndex; ++a) + { + IG::NodeIndex currentIndex(nodeIndexArray[a]); + const IG::Node& node = islandSim.getNode(currentIndex); + PxsRigidBody* rigid = node.getRigidBody(); + rigidBodyPtr[a] = rigid; + bodyArrayPtr[a] = &rigid->getCore(); + bodyRemapTable[islandSim.getActiveNodeIndex(currentIndex)] = a; + } + + + PxsIndexedContactManager* indexedManagers = mObjects.contactManagers; + + PxU32 currentContactIndex = 0; + for(PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::EdgeIndex contactEdgeIndex = island.mFirstEdge[IG::Edge::eCONTACT_MANAGER]; + + while(contactEdgeIndex != IG_INVALID_EDGE) + { + const IG::Edge& edge = islandSim.getEdge(contactEdgeIndex); + + PxsContactManager* contactManager = mIslandManager.getContactManager(contactEdgeIndex); + + if(contactManager) + { + const IG::NodeIndex nodeIndex1 = islandSim.getNodeIndex1(contactEdgeIndex); + const IG::NodeIndex nodeIndex2 = islandSim.getNodeIndex2(contactEdgeIndex); + + PxsIndexedContactManager& indexedManager = indexedManagers[currentContactIndex++]; + indexedManager.contactManager = contactManager; + + PX_ASSERT(!nodeIndex1.isStaticBody()); + { + const IG::Node& node1 = islandSim.getNode(nodeIndex1); + + //Is it an articulation or not??? + if(node1.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + indexedManager.indexType0 = PxsIndexedInteraction::eARTICULATION; + indexedManager.solverBody0 = size_t(node1.getArticulation()) | nodeIndex1.articulationLinkId(); + } + else + { + if(node1.isKinematic()) + { + indexedManager.indexType0 = PxsIndexedInteraction::eKINEMATIC; + indexedManager.solverBody0 = islandSim.getActiveNodeIndex(nodeIndex1); + } + else + { + indexedManager.indexType0 = PxsIndexedInteraction::eBODY; + indexedManager.solverBody0 = bodyRemapTable[islandSim.getActiveNodeIndex(nodeIndex1)]; + } + PX_ASSERT(indexedManager.solverBody0 < (mIslandContext.mCounts.bodies + mContext.mKinematicCount + 1)); + } + + } + + if(nodeIndex2.isStaticBody()) + { + indexedManager.indexType1 = PxsIndexedInteraction::eWORLD; + } + else + { + const IG::Node& node2 = islandSim.getNode(nodeIndex2); + + //Is it an articulation or not??? + if(node2.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + indexedManager.indexType1 = PxsIndexedInteraction::eARTICULATION; + indexedManager.solverBody1 = size_t(node2.getArticulation()) | nodeIndex2.articulationLinkId(); + } + else + { + if(node2.isKinematic()) + { + indexedManager.indexType1 = PxsIndexedInteraction::eKINEMATIC; + indexedManager.solverBody1 = islandSim.getActiveNodeIndex(nodeIndex2); + } + else + { + indexedManager.indexType1 = PxsIndexedInteraction::eBODY; + indexedManager.solverBody1 = bodyRemapTable[islandSim.getActiveNodeIndex(nodeIndex2)]; + } + PX_ASSERT(indexedManager.solverBody1 < (mIslandContext.mCounts.bodies + mContext.mKinematicCount + 1)); + } + } + + } + contactEdgeIndex = edge.mNextIslandEdge; + } + } + + if (mEnhancedDeterminism) + { + Ps::sort(indexedManagers, currentContactIndex, EnhancedSortPredicate()); + } + + mIslandContext.mCounts.contactManagers = currentContactIndex; + } + } + + void integrate() + { + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + PxSolverBody* solverBodies = mContext.mSolverBodyPool.begin() + mSolverBodyOffset; + PxSolverBodyData* solverBodyData = mContext.mSolverBodyDataPool.begin() + mSolverBodyOffset; + + { + PX_PROFILE_ZONE("Dynamics.updateVelocities", mContext.getContextId()); + + mContext.preIntegrationParallel( + mContext.mDt, + mThreadContext.mBodyCoreArray, + mObjects.bodies, + mThreadContext.mNodeIndexArray, + mIslandContext.mCounts.bodies, + solverBodies, + solverBodyData, + mThreadContext.motionVelocityArray, + mThreadContext.mMaxSolverPositionIterations, + mThreadContext.mMaxSolverVelocityIterations, + *mCont + ); + } + } + + void articulationTask() + { + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + ArticulationSolverDesc* articulationDescArray = mThreadContext.getArticulations().begin(); + + for(PxU32 i=0;isetContinuation(mCont); + task->removeReference(); + + } + } + + void setupDescTask() + { + PX_PROFILE_ZONE("SetupDescs", 0); + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + PxSolverConstraintDesc* contactDescPtr = mThreadContext.mContactDescPtr; + + //PxU32 constraintCount = mCounts.constraints + mCounts.contactManagers; + + PxU32 nbIslands = mObjects.numIslands; + const IG::IslandId* const islandIds = mObjects.islandIds; + + const IG::IslandSim& islandSim = mIslandManager.getAccurateIslandSim(); + + for(PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::EdgeIndex edgeId = island.mFirstEdge[IG::Edge::eCONSTRAINT]; + + while(edgeId != IG_INVALID_EDGE) + { + PxSolverConstraintDesc& desc = *contactDescPtr; + + const IG::Edge& edge = islandSim.getEdge(edgeId); + Dy::Constraint* constraint = mIslandManager.getConstraint(edgeId); + mContext.setDescFromIndices(desc, edgeId, mIslandManager, mBodyRemapTable, mSolverBodyOffset); + desc.constraint = reinterpret_cast(constraint); + desc.constraintLengthOver16 = DY_SC_TYPE_RB_1D; + contactDescPtr++; + edgeId = edge.mNextIslandEdge; + } + + } + +#if 1 + Ps::sort(mThreadContext.mContactDescPtr, PxU32(contactDescPtr - mThreadContext.mContactDescPtr), ConstraintLess()); +#endif + + + mThreadContext.orderedContactList.forceSize_Unsafe(0); + mThreadContext.orderedContactList.reserve(mIslandContext.mCounts.contactManagers); + mThreadContext.orderedContactList.forceSize_Unsafe(mIslandContext.mCounts.contactManagers); + mThreadContext.tempContactList.forceSize_Unsafe(0); + mThreadContext.tempContactList.reserve(mIslandContext.mCounts.contactManagers); + mThreadContext.tempContactList.forceSize_Unsafe(mIslandContext.mCounts.contactManagers); + + const PxsIndexedContactManager** constraints = mThreadContext.orderedContactList.begin(); + + + //OK, we sort the orderedContactList + + mThreadContext.compoundConstraints.forceSize_Unsafe(0); + if(mIslandContext.mCounts.contactManagers) + { + { + mThreadContext.sortIndexArray.forceSize_Unsafe(0); + + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eBODY == 0); + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eKINEMATIC == 1); + + const PxI32 offsetMap[] = {PxI32(mContext.mKinematicCount), 0}; + + const PxU32 totalBodies = mContext.mKinematicCount + mIslandContext.mCounts.bodies+1; + + mThreadContext.sortIndexArray.reserve(totalBodies); + mThreadContext.sortIndexArray.forceSize_Unsafe(totalBodies); + PxMemZero(mThreadContext.sortIndexArray.begin(), totalBodies * 4); + + //Iterate over the array based on solverBodyDatapool, creating a list of sorted constraints (in order of body pair) + //We only do this with contacts. It's important that this is done this way because we don't want to break our rules that all joints + //appear before all contacts in the constraint list otherwise we will lose all guarantees about sorting joints. + + for(PxU32 a = 0; a < mIslandContext.mCounts.contactManagers; ++a) + { + PX_ASSERT(mObjects.contactManagers[a].indexType0 != PxsIndexedInteraction::eWORLD); + //Index first body... + PxU8 indexType = mObjects.contactManagers[a].indexType0; + if(indexType != PxsIndexedInteraction::eARTICULATION && mObjects.contactManagers[a].indexType1 != PxsIndexedInteraction::eARTICULATION) + { + PX_ASSERT((indexType == PxsIndexedInteraction::eBODY) || (indexType == PxsIndexedInteraction::eKINEMATIC)); + + PxI32 index = PxI32(mObjects.contactManagers[a].solverBody0 + offsetMap[indexType]); + PX_ASSERT(index >= 0); + mThreadContext.sortIndexArray[PxU32(index)]++; + } + } + + PxU32 accumulatedCount = 0; + + for(PxU32 a = mThreadContext.sortIndexArray.size(); a > 0; --a) + { + PxU32 ind = a - 1; + PxU32 val = mThreadContext.sortIndexArray[ind]; + mThreadContext.sortIndexArray[ind] = accumulatedCount; + accumulatedCount += val; + } + + //OK, now copy across data to orderedConstraintDescs, pushing articulations to the end... + for(PxU32 a = 0; a < mIslandContext.mCounts.contactManagers; ++a) + { + //Index first body... + PxU8 indexType = mObjects.contactManagers[a].indexType0; + if(indexType != PxsIndexedInteraction::eARTICULATION && mObjects.contactManagers[a].indexType1 != PxsIndexedInteraction::eARTICULATION) + { + PX_ASSERT((indexType == PxsIndexedInteraction::eBODY) || (indexType == PxsIndexedInteraction::eKINEMATIC)); + + PxI32 index = PxI32(mObjects.contactManagers[a].solverBody0 + offsetMap[indexType]); + PX_ASSERT(index >= 0); + mThreadContext.tempContactList[mThreadContext.sortIndexArray[PxU32(index)]++] = &mObjects.contactManagers[a]; + } + else + { + mThreadContext.tempContactList[accumulatedCount++] = &mObjects.contactManagers[a]; + } + } + + //Now do the same again with bodyB, being careful not to overwrite the joints + PxMemZero(mThreadContext.sortIndexArray.begin(), totalBodies * 4); + + + for(PxU32 a = 0; a < mIslandContext.mCounts.contactManagers; ++a) + { + //Index first body... + PxU8 indexType = mThreadContext.tempContactList[a]->indexType1; + if(indexType != PxsIndexedInteraction::eARTICULATION && mObjects.contactManagers[a].indexType0 != PxsIndexedInteraction::eARTICULATION) + { + PX_ASSERT((indexType == PxsIndexedInteraction::eBODY) || (indexType == PxsIndexedInteraction::eKINEMATIC) || (indexType == PxsIndexedInteraction::eWORLD)); + + PxI32 index = (indexType == PxsIndexedInteraction::eWORLD) ? 0 : PxI32(mThreadContext.tempContactList[a]->solverBody1 + offsetMap[indexType]); + PX_ASSERT(index >= 0); + mThreadContext.sortIndexArray[PxU32(index)]++; + } + } + + accumulatedCount = 0; + for(PxU32 a = mThreadContext.sortIndexArray.size(); a > 0; --a) + { + PxU32 ind = a - 1; + PxU32 val = mThreadContext.sortIndexArray[ind]; + mThreadContext.sortIndexArray[ind] = accumulatedCount; + accumulatedCount += val; + } + + PxU32 articulationStartIndex = accumulatedCount; + + //OK, now copy across data to orderedConstraintDescs, pushing articulations to the end... + for(PxU32 a = 0; a < mIslandContext.mCounts.contactManagers; ++a) + { + //Index first body... + PxU8 indexType = mThreadContext.tempContactList[a]->indexType1; + if(indexType != PxsIndexedInteraction::eARTICULATION && mObjects.contactManagers[a].indexType0 != PxsIndexedInteraction::eARTICULATION) + { + PX_ASSERT((indexType == PxsIndexedInteraction::eBODY) || (indexType == PxsIndexedInteraction::eKINEMATIC) || (indexType == PxsIndexedInteraction::eWORLD)); + + PxI32 index = (indexType == PxsIndexedInteraction::eWORLD) ? 0 : PxI32(mThreadContext.tempContactList[a]->solverBody1 + offsetMap[indexType]); + PX_ASSERT(index >= 0); + constraints[mThreadContext.sortIndexArray[PxU32(index)]++] = mThreadContext.tempContactList[a]; + } + else + { + constraints[accumulatedCount++] = mThreadContext.tempContactList[a]; + } + } + +#if 1 + Ps::sort(constraints + articulationStartIndex, accumulatedCount - articulationStartIndex, ArticulationSortPredicate()); +#endif + } + + mThreadContext.mStartContactDescPtr = contactDescPtr; + + mThreadContext.compoundConstraints.reserve(1024); + mThreadContext.compoundConstraints.forceSize_Unsafe(0); + //mThreadContext.compoundConstraints.forceSize_Unsafe(mCounts.contactManagers); + + PxSolverConstraintDesc* startDesc = contactDescPtr; + mContext.setDescFromIndices(*startDesc, *constraints[0], mSolverBodyOffset); + startDesc->constraint = reinterpret_cast(constraints[0]->contactManager); + startDesc->constraintLengthOver16 = DY_SC_TYPE_RB_CONTACT; + + PxsContactManagerOutput* startManagerOutput = &mOutputs.getContactManager(constraints[0]->contactManager->getWorkUnit().mNpIndex); + PxU32 contactCount = startManagerOutput->nbContacts; + PxU32 startIndex = 0; + PxU32 numHeaders = 0; + + const bool gDisableConstraintWelding = false; + + for(PxU32 a = 1; a < mIslandContext.mCounts.contactManagers; ++a) + { + PxSolverConstraintDesc& desc = *(contactDescPtr+1); + mContext.setDescFromIndices(desc, *constraints[a], mSolverBodyOffset); + + PxsContactManager* manager = constraints[a]->contactManager; + PxsContactManagerOutput& output = mOutputs.getContactManager(manager->getWorkUnit().mNpIndex); + + desc.constraint = reinterpret_cast(constraints[a]->contactManager); + desc.constraintLengthOver16 = DY_SC_TYPE_RB_CONTACT; + + if (contactCount == 0) + { + //This is the first object in the pair + *startDesc = *(contactDescPtr + 1); + startIndex = a; + startManagerOutput = &output; + } + + if(startDesc->bodyA != desc.bodyA || startDesc->bodyB != desc.bodyB + || startDesc->linkIndexA != PxSolverConstraintDesc::NO_LINK || startDesc->linkIndexB != PxSolverConstraintDesc::NO_LINK + || contactCount + output.nbContacts > Gu::ContactBuffer::MAX_CONTACTS + || manager->isChangeable() + || gDisableConstraintWelding + ) //If this is the first thing and no contacts...then we skip + { + PxU32 stride = a - startIndex; + if(contactCount > 0) + { + if(stride > 1) + { + ++numHeaders; + CompoundContactManager& header = mThreadContext.compoundConstraints.insert(); + header.mStartIndex = startIndex; + header.mStride = Ps::to16(stride); + header.mReducedContactCount = Ps::to16(contactCount); + PxsContactManager* manager1 = constraints[startIndex]->contactManager; + PxcNpWorkUnit& unit = manager1->getWorkUnit(); + + PX_ASSERT(startManagerOutput == &mOutputs.getContactManager(unit.mNpIndex)); + + header.unit = &unit; + header.cmOutput = startManagerOutput; + header.originalContactPatches = startManagerOutput->contactPatches; + header.originalContactPoints = startManagerOutput->contactPoints; + header.originalContactCount = startManagerOutput->nbContacts; + header.originalPatchCount = startManagerOutput->nbPatches; + header.originalForceBuffer = reinterpret_cast(startManagerOutput->contactForces); + header.originalStatusFlags = startManagerOutput->statusFlag; + } + startDesc = ++contactDescPtr; + } + else + { + //Copy back next contactDescPtr + *startDesc = *(contactDescPtr+1); + } + contactCount = 0; + startIndex = a; + startManagerOutput = &output; + } + contactCount += output.nbContacts; + + } + PxU32 stride = mIslandContext.mCounts.contactManagers - startIndex; + if(contactCount > 0) + { + if(stride > 1) + { + ++numHeaders; + CompoundContactManager& header = mThreadContext.compoundConstraints.insert(); + header.mStartIndex = startIndex; + header.mStride = Ps::to16(stride); + header.mReducedContactCount = Ps::to16(contactCount); + PxsContactManager* manager = constraints[startIndex]->contactManager; + PxcNpWorkUnit& unit = manager->getWorkUnit(); + header.unit = &unit; + header.cmOutput = startManagerOutput; + header.originalContactPatches = startManagerOutput->contactPatches; + header.originalContactPoints = startManagerOutput->contactPoints; + header.originalContactCount = startManagerOutput->nbContacts; + header.originalPatchCount = startManagerOutput->nbPatches; + header.originalForceBuffer = reinterpret_cast(startManagerOutput->contactForces); + header.originalStatusFlags = startManagerOutput->statusFlag; + } + contactDescPtr++; + } + + if(numHeaders) + { + const PxU32 unrollSize = 8; + for(PxU32 a = 0; a < numHeaders; a+= unrollSize) + { + PxsSolverConstraintPostProcessTask* postProcessTask = PX_PLACEMENT_NEW( mContext.getTaskPool().allocate(sizeof(PxsSolverConstraintPostProcessTask)), + PxsSolverConstraintPostProcessTask)(mContext, mThreadContext, mObjects, mSolverBodyOffset, a, PxMin(unrollSize, numHeaders - a), mMaterialManager, + mOutputs); + postProcessTask->setContinuation(mCont); + postProcessTask->removeReference(); + } + } + } + mThreadContext.contactDescArraySize = PxU32(contactDescPtr - mThreadContext.contactConstraintDescArray); + mThreadContext.mContactDescPtr = contactDescPtr; + } + + virtual void runInternal() + { + startTasks(); + integrate(); + setupDescTask(); + articulationTask(); + } + + virtual const char* getName() const + { + return "PxsDynamics.solverStart"; + } + +private: + DynamicsContext& mContext; + IslandContext& mIslandContext; + const SolverIslandObjects mObjects; + const PxU32 mSolverBodyOffset; + const PxU32 mKinematicCount; + IG::SimpleIslandManager& mIslandManager; + PxU32* mBodyRemapTable; + PxsMaterialManager* mMaterialManager; + PxsContactManagerOutputIterator& mOutputs; + bool mEnhancedDeterminism; +}; + +class PxsSolverConstraintPartitionTask : public Cm::Task +{ + PxsSolverConstraintPartitionTask& operator=(const PxsSolverConstraintPartitionTask&); +public: + + PxsSolverConstraintPartitionTask(DynamicsContext& context, + IslandContext& islandContext, + const SolverIslandObjects& objects, + const PxU32 solverBodyOffset, bool enhancedDeterminism) : + Cm::Task(context.getContextId()), + mContext(context), + mIslandContext(islandContext), + mObjects(objects), + mSolverBodyOffset(solverBodyOffset), + mEnhancedDeterminism(enhancedDeterminism) + {} + + virtual void runInternal() + { + + PX_PROFILE_ZONE("PartitionConstraints", 0); + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + + //Compact articulation pairs... + ArticulationSolverDesc* artics = mThreadContext.getArticulations().begin(); + + if(mIslandContext.mCounts.articulations) + { + PxU32 nbArticConstraints = artics[0].numInternalConstraints; + + PxSolverConstraintDesc* currDesc = mThreadContext.mContactDescPtr; + for(PxU32 a = 1; a < mIslandContext.mCounts.articulations; ++a) + { + //Compact pairs... + const PxU32 nbInternalConstraints = artics[a].numInternalConstraints; + const PxU32 startIdx = a * DY_ARTICULATION_MAX_SIZE; + const PxU32 endIdx = startIdx + nbInternalConstraints; + + for(PxU32 b = startIdx; b < endIdx; ++b) + { + currDesc[nbArticConstraints++] = currDesc[b]; + } + } + + mThreadContext.contactDescArraySize += nbArticConstraints; + } + + PxSolverConstraintDesc* descBegin = mThreadContext.contactConstraintDescArray; + PxU32 descCount = mThreadContext.contactDescArraySize; + + PxSolverBody* solverBodies = mContext.mSolverBodyPool.begin() + mSolverBodyOffset; + + mThreadContext.mNumDifferentBodyConstraints = descCount; + + { + mThreadContext.mNumDifferentBodyConstraints = 0; + mThreadContext.mNumSelfConstraints = 0; + mThreadContext.mNumSelfConstraintBlocks = 0; + mThreadContext.mNumDifferentBodyFrictionConstraints = 0; + mThreadContext.mNumSelfConstraintFrictionBlocks = 0; + mThreadContext.mNumSelfFrictionConstraints = 0; + + if(descCount > 0) + { + ConstraintPartitionArgs args; + args.mBodies = solverBodies; + args.mArticulationPtrs = artics; + args.mContactConstraintDescriptors = descBegin; + args.mNumArticulationPtrs = mThreadContext.getArticulations().size(); + args.mNumBodies = mIslandContext.mCounts.bodies; + args.mNumContactConstraintDescriptors = descCount; + args.mOrderedContactConstraintDescriptors = mThreadContext.orderedContactConstraints; + args.mTempContactConstraintDescriptors = mThreadContext.tempConstraintDescArray; + args.mNumDifferentBodyConstraints = args.mNumSelfConstraints = args.mNumSelfConstraintBlocks = 0; + args.mConstraintsPerPartition = &mThreadContext.mConstraintsPerPartition; + args.mBitField = &mThreadContext.mPartitionNormalizationBitmap; + args.enhancedDeterminism = mEnhancedDeterminism; + + mThreadContext.mMaxPartitions = partitionContactConstraints(args); + mThreadContext.mNumDifferentBodyConstraints = args.mNumDifferentBodyConstraints; + mThreadContext.mNumSelfConstraints = args.mNumSelfConstraints; + mThreadContext.mNumSelfConstraintBlocks = args.mNumSelfConstraintBlocks; + } + else + { + PxMemZero(mThreadContext.mConstraintsPerPartition.begin(), sizeof(PxU32)*mThreadContext.mConstraintsPerPartition.capacity()); + } + + PX_ASSERT((mThreadContext.mNumDifferentBodyConstraints + mThreadContext.mNumSelfConstraints) == descCount); + } + + } + + virtual const char* getName() const { return "PxsDynamics.solverConstraintPartition"; } + + DynamicsContext& mContext; + IslandContext& mIslandContext; + const SolverIslandObjects mObjects; + PxU32 mSolverBodyOffset; + bool mEnhancedDeterminism; +}; + + +class PxsSolverSetupSolveTask : public Cm::Task +{ + PxsSolverSetupSolveTask& operator=(const PxsSolverSetupSolveTask&); +public: + + PxsSolverSetupSolveTask( + DynamicsContext& context, + IslandContext& islandContext, + const SolverIslandObjects& objects, + const PxU32 solverBodyOffset, + IG::IslandSim& islandSim) : + Cm::Task(context.getContextId()), + mContext(context), + mIslandContext(islandContext), + mObjects(objects), + mSolverBodyOffset(solverBodyOffset), + mIslandSim(islandSim) + {} + + + virtual void runInternal() + { + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + + PxSolverConstraintDesc* contactDescBegin = mThreadContext.orderedContactConstraints; + PxSolverConstraintDesc* contactDescPtr = mThreadContext.orderedContactConstraints; + + PxSolverBody* solverBodies = mContext.mSolverBodyPool.begin() + mSolverBodyOffset; + PxSolverBodyData* solverBodyDatas = mContext.mSolverBodyDataPool.begin(); + + PxU32 frictionDescCount = mThreadContext.mNumDifferentBodyFrictionConstraints; + + PxU32 j = 0, i = 0; + + //On PS3, self-constraints will be bumped to the end of the constraint list + //and processed separately. On PC/360, they will be mixed in the array and + //classed as "different body" constraints regardless of the fact that they're self-constraints. + //PxU32 numBatches = mThreadContext.numDifferentBodyBatchHeaders; + // TODO: maybe replace with non-null joints from end of the array + + PxU32 numBatches = 0; + + PxU32 currIndex = 0; + for(PxU32 a = 0; a < mThreadContext.mConstraintsPerPartition.size(); ++a) + { + PxU32 endIndex = currIndex + mThreadContext.mConstraintsPerPartition[a]; + + PxU32 numBatchesInPartition = 0; + for(PxU32 b = currIndex; b < endIndex; ++b) + { + PxConstraintBatchHeader& _header = mThreadContext.contactConstraintBatchHeaders[b]; + PxU16 stride = _header.mStride, newStride = _header.mStride; + PxU32 startIndex = j; + for(PxU16 c = 0; c < stride; ++c) + { + if(getConstraintLength(contactDescBegin[i]) == 0) + { + newStride--; + i++; + } + else + { + if(i!=j) + contactDescBegin[j] = contactDescBegin[i]; + i++; + j++; + contactDescPtr++; + } + } + + if(newStride != 0) + { + mThreadContext.contactConstraintBatchHeaders[numBatches].mStartIndex = startIndex; + mThreadContext.contactConstraintBatchHeaders[numBatches].mStride = newStride; + PxU8 type = *contactDescBegin[startIndex].constraint; + if(type == DY_SC_TYPE_STATIC_CONTACT) + { + //Check if any block of constraints is classified as type static (single) contact constraint. + //If they are, iterate over all constraints grouped with it and switch to "dynamic" contact constraint + //type if there's a dynamic contact constraint in the group. + for(PxU32 c = 1; c < newStride; ++c) + { + if(*contactDescBegin[startIndex+c].constraint == DY_SC_TYPE_RB_CONTACT) + { + type = DY_SC_TYPE_RB_CONTACT; + } + } + } + + mThreadContext.contactConstraintBatchHeaders[numBatches].mConstraintType = type; + numBatches++; + numBatchesInPartition++; + } + } + PxU32 numHeaders = numBatchesInPartition; + currIndex += mThreadContext.mConstraintsPerPartition[a]; + mThreadContext.mConstraintsPerPartition[a] = numHeaders; + } + + PxU32 contactDescCount = PxU32(contactDescPtr - contactDescBegin); + + mThreadContext.mNumDifferentBodyConstraints = contactDescCount; + + PxU32 numSelfConstraintBlocks = mThreadContext.mNumSelfConstraintBlocks; + + //Remap self constraint array. Self-constraint blocks exists on PS3 as an optimization for SPU solver. + for(PxU32 a = 0; a < numSelfConstraintBlocks; ++a) + { + PX_ASSERT(mThreadContext.mSelfConstraintBlocks[a].startId == i); + PxU32 origNumSelfConstraints = mThreadContext.mSelfConstraintBlocks[a].numSelfConstraints; + PxU32 startId = j; + + for(PxU32 b = 0; b < origNumSelfConstraints; ++b) + { + PxSolverConstraintDesc& desc = contactDescBegin[i]; + + if(getConstraintLength(desc)) + { + PxConstraintBatchHeader& header = mThreadContext.contactConstraintBatchHeaders[numBatches++]; + header.mStride = 1; + header.mStartIndex = j; + header.mConstraintType = *desc.constraint; + if(i != j) + contactDescBegin[j] = contactDescBegin[i]; + j++; + } + i++; + } + mThreadContext.mSelfConstraintBlocks[a].startId = startId; + mThreadContext.mSelfConstraintBlocks[a].numSelfConstraints = j - startId; + } + + mThreadContext.numContactConstraintBatches = numBatches; + mThreadContext.mNumSelfConstraints = j - contactDescCount; //self constraint count + contactDescCount = j; + mThreadContext.mOrderedContactDescCount = j; + + //Now do the friction constraints if we're not using the sticky model + if(mContext.getFrictionType() != PxFrictionType::ePATCH) + { + PxSolverConstraintDesc* frictionDescBegin = mThreadContext.frictionConstraintDescArray.begin(); + PxSolverConstraintDesc* frictionDescPtr = frictionDescBegin; + + Ps::Array& frictionHeaderArray = mThreadContext.frictionConstraintBatchHeaders; + frictionHeaderArray.forceSize_Unsafe(0); + frictionHeaderArray.reserve(mThreadContext.numContactConstraintBatches); + PxConstraintBatchHeader* headers = frictionHeaderArray.begin(); + + Ps::Array& constraintsPerPartition = mThreadContext.mConstraintsPerPartition; + Ps::Array& frictionConstraintsPerPartition = mThreadContext.mFrictionConstraintsPerPartition; + frictionConstraintsPerPartition.forceSize_Unsafe(0); + frictionConstraintsPerPartition.reserve(constraintsPerPartition.capacity()); + + + PxU32 fricI = 0; + PxU32 startIndex = 0; + PxU32 fricHeaders = 0; + for(PxU32 k = 0; k < constraintsPerPartition.size(); ++k) + { + PxU32 numBatchesInK = constraintsPerPartition[k]; + PxU32 endIndex = startIndex + numBatchesInK; + + PxU32 startFricH = fricHeaders; + + for(PxU32 a = startIndex; a < endIndex; ++a) + { + PxConstraintBatchHeader& _header = mThreadContext.contactConstraintBatchHeaders[a]; + PxU16 stride = _header.mStride; + if(_header.mConstraintType == DY_SC_TYPE_RB_CONTACT || _header.mConstraintType == DY_SC_TYPE_EXT_CONTACT || + _header.mConstraintType == DY_SC_TYPE_STATIC_CONTACT) + { + PxU8 type = 0; + //Extract friction from this constraint + for(PxU16 b = 0; b < stride; ++b) + { + //create the headers... + PxSolverConstraintDesc& desc = contactDescBegin[_header.mStartIndex + b]; + PX_ASSERT(desc.constraint); + SolverContactCoulombHeader* header = reinterpret_cast(desc.constraint); + PxU32 frictionOffset = header->frictionOffset; + PxU8* PX_RESTRICT constraint = reinterpret_cast(header) + frictionOffset; + const PxU32 origLength = getConstraintLength(desc); + const PxU32 length = (origLength - frictionOffset); + + setConstraintLength(*frictionDescPtr, length); + frictionDescPtr->constraint = constraint; + frictionDescPtr->bodyA = desc.bodyA; + frictionDescPtr->bodyB = desc.bodyB; + frictionDescPtr->bodyADataIndex = desc.bodyADataIndex; + frictionDescPtr->bodyBDataIndex = desc.bodyBDataIndex; + frictionDescPtr->linkIndexA = desc.linkIndexA; + frictionDescPtr->linkIndexB = desc.linkIndexB; + frictionDescPtr->writeBack = NULL; + frictionDescPtr->writeBackLengthOver4 = 0; + type = *constraint; + frictionDescPtr++; + } + headers->mStartIndex = fricI; + headers->mStride = stride; + headers->mConstraintType = type; + headers++; + fricHeaders++; + fricI += stride; + } + else if(_header.mConstraintType == DY_SC_TYPE_BLOCK_RB_CONTACT || _header.mConstraintType == DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT) + { + //KS - TODO - Extract block of 4 contacts from this constraint. This isn't implemented yet for coulomb friction model + PX_ASSERT(contactDescBegin[_header.mStartIndex].constraint); + SolverContactCoulombHeader4* head = reinterpret_cast(contactDescBegin[_header.mStartIndex].constraint); + PxU32 frictionOffset = head->frictionOffset; + PxU8* PX_RESTRICT constraint = reinterpret_cast(head) + frictionOffset; + const PxU32 origLength = getConstraintLength(contactDescBegin[_header.mStartIndex]); + const PxU32 length = (origLength - frictionOffset); + PxU8 type = *constraint; + PX_ASSERT(type == DY_SC_TYPE_BLOCK_FRICTION || type == DY_SC_TYPE_BLOCK_STATIC_FRICTION); + for(PxU32 b = 0; b < 4; ++b) + { + PxSolverConstraintDesc& desc = contactDescBegin[_header.mStartIndex+b]; + setConstraintLength(*frictionDescPtr, length); + frictionDescPtr->constraint = constraint; + frictionDescPtr->bodyA = desc.bodyA; + frictionDescPtr->bodyB = desc.bodyB; + frictionDescPtr->bodyADataIndex = desc.bodyADataIndex; + frictionDescPtr->bodyBDataIndex = desc.bodyBDataIndex; + frictionDescPtr->linkIndexA = desc.linkIndexA; + frictionDescPtr->linkIndexB = desc.linkIndexB; + frictionDescPtr->writeBack = NULL; + frictionDescPtr->writeBackLengthOver4 = 0; + frictionDescPtr++; + } + headers->mStartIndex = fricI; + headers->mStride = stride; + headers->mConstraintType = type; + headers++; + fricHeaders++; + fricI += stride; + } + } + startIndex += numBatchesInK; + if(startFricH < fricHeaders) + { + frictionConstraintsPerPartition.pushBack(fricHeaders - startFricH); + } + } + + + frictionDescCount = PxU32(frictionDescPtr - frictionDescBegin); + + mThreadContext.mNumDifferentBodyFrictionConstraints = frictionDescCount; + + frictionHeaderArray.forceSize_Unsafe(PxU32(headers - frictionHeaderArray.begin())); + + mThreadContext.mNumSelfFrictionConstraints = fricI - frictionDescCount; //self constraint count + mThreadContext.mNumDifferentBodyFrictionConstraints = frictionDescCount; + frictionDescCount = fricI; + mThreadContext.mOrderedFrictionDescCount = frictionDescCount; + + + } + + { + { + PX_PROFILE_ZONE("Dynamics.solver", mContext.getContextId()); + + PxSolverConstraintDesc* contactDescs = mThreadContext.orderedContactConstraints; + PxSolverConstraintDesc* frictionDescs = mThreadContext.frictionConstraintDescArray.begin(); + + PxI32* thresholdPairsOut = &mContext.mThresholdStreamOut; + + SolverIslandParams& params = *reinterpret_cast(mContext.getTaskPool().allocate(sizeof(SolverIslandParams))); + params.positionIterations = mThreadContext.mMaxSolverPositionIterations; + params.velocityIterations = mThreadContext.mMaxSolverVelocityIterations; + params.bodyListStart = solverBodies; + params.bodyDataList = solverBodyDatas; + params.solverBodyOffset = mSolverBodyOffset; + params.bodyListSize = mIslandContext.mCounts.bodies; + params.articulationListStart = mThreadContext.getArticulations().begin(); + params.articulationListSize = mThreadContext.getArticulations().size(); + params.constraintList = contactDescs; + params.constraintIndex = 0; + params.constraintIndex2 = 0; + params.bodyListIndex = 0; + params.bodyListIndex2 = 0; + params.articSolveIndex = 0; + params.articSolveIndex2 = 0; + params.bodyIntegrationListIndex = 0; + params.thresholdStream = mContext.getThresholdStream().begin(); + params.thresholdStreamLength = mContext.getThresholdStream().size(); + params.outThresholdPairs = thresholdPairsOut; + params.motionVelocityArray = mThreadContext.motionVelocityArray; + params.bodyArray = mThreadContext.mBodyCoreArray; + params.numObjectsIntegrated = 0; + params.constraintBatchHeaders = mThreadContext.contactConstraintBatchHeaders; + params.numConstraintHeaders = mThreadContext.numContactConstraintBatches; + params.headersPerPartition = mThreadContext.mConstraintsPerPartition.begin(); + params.nbPartitions = mThreadContext.mConstraintsPerPartition.size(); + params.rigidBodies = const_cast(mObjects.bodies); + params.frictionHeadersPerPartition = mThreadContext.mFrictionConstraintsPerPartition.begin(); + params.nbFrictionPartitions = mThreadContext.mFrictionConstraintsPerPartition.size(); + params.frictionConstraintBatches = mThreadContext.frictionConstraintBatchHeaders.begin(); + params.numFrictionConstraintHeaders = mThreadContext.frictionConstraintBatchHeaders.size(); + params.frictionConstraintIndex = 0; + params.frictionConstraintList = frictionDescs; + params.mMaxArticulationLinks = mThreadContext.mMaxArticulationLinks; + params.dt = mContext.mDt; + params.invDt = mContext.mInvDt; + + const PxU32 unrollSize = 8; + const PxU32 denom = PxMax(1u, (mThreadContext.mMaxPartitions*unrollSize)); + const PxU32 MaxTasks = getTaskManager()->getCpuDispatcher()->getWorkerCount(); + const PxU32 idealThreads = (mThreadContext.numContactConstraintBatches+denom-1)/denom; + const PxU32 numTasks = PxMax(1u, PxMin(idealThreads, MaxTasks)); + + if(numTasks > 1) + { + const PxU32 idealBatchSize = PxMax(unrollSize, idealThreads*unrollSize/(numTasks*2)); + + params.batchSize = idealBatchSize; //assigning ideal batch size for the solver to grab work at. Only needed by the multi-threaded island solver. + + for(PxU32 a = 1; a < numTasks; ++a) + { + void* tsk = mContext.getTaskPool().allocate(sizeof(PxsParallelSolverTask)); + PxsParallelSolverTask* pTask = PX_PLACEMENT_NEW(tsk, PxsParallelSolverTask)( + params, mContext, mContext.getFrictionType(), mIslandSim); + + //Force to complete before merge task! + pTask->setContinuation(mCont); + + pTask->removeReference(); + } + + //Avoid kicking off one parallel task when we can do the work inline in this function + { + PX_PROFILE_ZONE("Dynamics.parallelSolve", mContext.getContextId()); + + solveParallel(mContext, params, mIslandSim); + } + const PxI32 numBodiesPlusArtics = PxI32( mIslandContext.mCounts.bodies + mIslandContext.mCounts.articulations ); + + PxI32* numObjectsIntegrated = ¶ms.numObjectsIntegrated; + + WAIT_FOR_PROGRESS_NO_TIMER(numObjectsIntegrated, numBodiesPlusArtics); + + } + else + { + + mThreadContext.mZVector.forceSize_Unsafe(0); + mThreadContext.mZVector.reserve(mThreadContext.mMaxArticulationLinks); + mThreadContext.mZVector.forceSize_Unsafe(mThreadContext.mMaxArticulationLinks); + + mThreadContext.mDeltaV.forceSize_Unsafe(0); + mThreadContext.mDeltaV.reserve(mThreadContext.mMaxArticulationLinks); + mThreadContext.mDeltaV.forceSize_Unsafe(mThreadContext.mMaxArticulationLinks); + + params.Z = mThreadContext.mZVector.begin(); + params.deltaV = mThreadContext.mDeltaV.begin(); + + //Only one task - a small island so do a sequential solve (avoid the atomic overheads) + solveVBlock(mContext.mSolverCore[mContext.getFrictionType()], params); + + const PxU32 bodyCountMin1 = mIslandContext.mCounts.bodies - 1u; + PxSolverBodyData* solverBodyData2 = solverBodyDatas + mSolverBodyOffset + 1; + for(PxU32 k=0; k < mIslandContext.mCounts.bodies; k++) + { + const PxU32 prefetchAddress = PxMin(k+4, bodyCountMin1); + Ps::prefetchLine(mThreadContext.mBodyCoreArray[prefetchAddress]); + Ps::prefetchLine(&mThreadContext.motionVelocityArray[k], 128); + Ps::prefetchLine(&mThreadContext.mBodyCoreArray[prefetchAddress], 128); + Ps::prefetchLine(&mObjects.bodies[prefetchAddress]); + + PxSolverBodyData& solverBodyData = solverBodyData2[k]; + + integrateCore(mThreadContext.motionVelocityArray[k].linear, mThreadContext.motionVelocityArray[k].angular, + solverBodies[k], solverBodyData, mContext.mDt); + + PxsRigidBody& rBody = *mObjects.bodies[k]; + PxsBodyCore& core = rBody.getCore(); + rBody.mLastTransform = core.body2World; + core.body2World = solverBodyData.body2World; + core.linearVelocity = solverBodyData.linearVelocity; + core.angularVelocity = solverBodyData.angularVelocity; + + + bool hasStaticTouch = mIslandSim.getIslandStaticTouchCount(IG::NodeIndex(solverBodyData.nodeIndex)) != 0; + sleepCheck(const_cast(mObjects.bodies[k]), mContext.mDt, mContext.mInvDt, mContext.mEnableStabilization, mContext.mUseAdaptiveForce, mThreadContext.motionVelocityArray[k], + hasStaticTouch); + } + + for(PxU32 cnt=0;cnt(cmOutput->contactForces); + PxU32 contactCount = cmOutput->nbContacts; + + cmOutput->contactPatches = manager.originalContactPatches; + cmOutput->contactPoints = manager.originalContactPoints; + cmOutput->nbContacts = manager.originalContactCount; + cmOutput->nbPatches = manager.originalPatchCount; + cmOutput->statusFlag = manager.originalStatusFlags; + cmOutput->contactForces = manager.originalForceBuffer; + + for(PxU32 a = 1; a < manager.mStride; ++a) + { + PxsContactManager* pManager = mThreadContext.orderedContactList[manager.mStartIndex + a]->contactManager; + pManager->getWorkUnit().frictionDataPtr = manager.unit->frictionDataPtr; + pManager->getWorkUnit().frictionPatchCount = manager.unit->frictionPatchCount; + //pManager->getWorkUnit().prevFrictionPatchCount = manager.unit->prevFrictionPatchCount; + } + + //This is a stride-based contact force writer. The assumption is that we may have skipped certain unimportant contacts reported by the + //discrete narrow phase + if(contactForces) + { + PxU32 currentContactIndex = 0; + PxU32 currentManagerIndex = manager.mStartIndex; + PxU32 currentManagerContactIndex = 0; + + for(PxU32 a = 0; a < contactCount; ++a) + { + PxU32 index = manager.forceBufferList[a]; + PxsContactManager* pManager = mThreadContext.orderedContactList[currentManagerIndex]->contactManager; + PxsContactManagerOutput* output = &mOutputs.getContactManager(pManager->getWorkUnit().mNpIndex); + while(currentContactIndex < index || output->nbContacts == 0) + { + //Step forwards...first in this manager... + + PxU32 numToStep = PxMin(index - currentContactIndex, PxU32(output->nbContacts) - currentManagerContactIndex); + currentContactIndex += numToStep; + currentManagerContactIndex += numToStep; + if(currentManagerContactIndex == output->nbContacts) + { + currentManagerIndex++; + currentManagerContactIndex = 0; + pManager = mThreadContext.orderedContactList[currentManagerIndex]->contactManager; + output = &mOutputs.getContactManager(pManager->getWorkUnit().mNpIndex); + } + } + if(output->nbContacts > 0 && output->contactForces) + output->contactForces[currentManagerContactIndex] = contactForces[a]; + } + } + } + + mThreadContext.compoundConstraints.forceSize_Unsafe(0); + + mThreadContext.mConstraintBlockManager.reset(); + + mContext.putThreadContext(&mThreadContext); + } + + + virtual const char* getName() const + { + return "PxsDynamics.solverEnd"; + } + + DynamicsContext& mContext; + IslandContext& mIslandContext; + const SolverIslandObjects mObjects; + const PxU32 mSolverBodyOffset; + PxsContactManagerOutputIterator& mOutputs; +}; + +class PxsSolverCreateFinalizeConstraintsTask : public Cm::Task +{ + PxsSolverCreateFinalizeConstraintsTask& operator=(const PxsSolverCreateFinalizeConstraintsTask&); +public: + + PxsSolverCreateFinalizeConstraintsTask( + DynamicsContext& context, + IslandContext& islandContext, + PxU32 solverDataOffset, + PxsContactManagerOutputIterator& outputs, + bool enhancedDeterminism) : + Cm::Task (context.getContextId()), + mContext (context), + mIslandContext (islandContext), + mSolverDataOffset (solverDataOffset), + mOutputs (outputs), + mEnhancedDeterminism (enhancedDeterminism) + { + } + + virtual void runInternal(); + + virtual const char* getName() const { return "PxsDynamics.solverCreateFinalizeConstraints"; } + + DynamicsContext& mContext; + IslandContext& mIslandContext; + PxU32 mSolverDataOffset; + PxsContactManagerOutputIterator& mOutputs; + bool mEnhancedDeterminism; +}; + + +// helper function to join two tasks together and ensure ref counts are correct +void chainTasks(PxLightCpuTask* first, PxLightCpuTask* next) +{ + first->setContinuation(next); + next->removeReference(); +} + +PxBaseTask* createSolverTaskChain(DynamicsContext& dynamicContext, + const SolverIslandObjects& objects, + const PxsIslandIndices& counts, + const PxU32 solverBodyOffset, + IG::SimpleIslandManager& islandManager, + PxU32* bodyRemapTable, PxsMaterialManager* materialManager, PxBaseTask* continuation, + PxsContactManagerOutputIterator& iterator, bool useEnhancedDeterminism) +{ + Cm::FlushPool& taskPool = dynamicContext.getTaskPool(); + + taskPool.lock(); + + + IslandContext* islandContext = reinterpret_cast(taskPool.allocate(sizeof(IslandContext))); + islandContext->mThreadContext = NULL; + islandContext->mCounts = counts; + + + // create lead task + PxsSolverStartTask* startTask = PX_PLACEMENT_NEW(taskPool.allocateNotThreadSafe(sizeof(PxsSolverStartTask)), PxsSolverStartTask)(dynamicContext, *islandContext, objects, solverBodyOffset, dynamicContext.getKinematicCount(), + islandManager, bodyRemapTable, materialManager, iterator, useEnhancedDeterminism); + PxsSolverEndTask* endTask = PX_PLACEMENT_NEW(taskPool.allocateNotThreadSafe(sizeof(PxsSolverEndTask)), PxsSolverEndTask)(dynamicContext, *islandContext, objects, solverBodyOffset, iterator); + + + PxsSolverCreateFinalizeConstraintsTask* createFinalizeConstraintsTask = PX_PLACEMENT_NEW(taskPool.allocateNotThreadSafe(sizeof(PxsSolverCreateFinalizeConstraintsTask)), PxsSolverCreateFinalizeConstraintsTask)(dynamicContext, *islandContext, solverBodyOffset, iterator, useEnhancedDeterminism); + PxsSolverSetupSolveTask* setupSolveTask = PX_PLACEMENT_NEW(taskPool.allocateNotThreadSafe(sizeof(PxsSolverSetupSolveTask)), PxsSolverSetupSolveTask)(dynamicContext, *islandContext, objects, solverBodyOffset, islandManager.getAccurateIslandSim()); + + PxsSolverConstraintPartitionTask* partitionConstraintsTask = PX_PLACEMENT_NEW(taskPool.allocateNotThreadSafe(sizeof(PxsSolverConstraintPartitionTask)), PxsSolverConstraintPartitionTask)(dynamicContext, *islandContext, objects, solverBodyOffset, useEnhancedDeterminism); + + endTask->setContinuation(continuation); + + // set up task chain in reverse order + chainTasks(setupSolveTask, endTask); + chainTasks(createFinalizeConstraintsTask, setupSolveTask); + chainTasks(partitionConstraintsTask, createFinalizeConstraintsTask); + chainTasks(startTask, partitionConstraintsTask); + + taskPool.unlock(); + + return startTask; +} + +class UpdateContinuationTask : public Cm::Task +{ + DynamicsContext& mContext; + IG::SimpleIslandManager& mSimpleIslandManager; + PxBaseTask* mLostTouchTask; + + PX_NOCOPY(UpdateContinuationTask) +public: + + UpdateContinuationTask(DynamicsContext& context, + IG::SimpleIslandManager& simpleIslandManager, + PxBaseTask* lostTouchTask, + PxU64 contextID) : Cm::Task(contextID), mContext(context), mSimpleIslandManager(simpleIslandManager), + mLostTouchTask(lostTouchTask) + { + } + + virtual const char* getName() const { return "UpdateContinuationTask"; } + + virtual void runInternal() + { + mContext.updatePostKinematic(mSimpleIslandManager, mCont, mLostTouchTask); + //Allow lost touch task to run once all tasks have be scheduled + mLostTouchTask->removeReference(); + } + +}; + + +class KinematicCopyTask : public Cm::Task +{ + const IG::NodeIndex* const mKinematicIndices; + const PxU32 mNbKinematics; + const IG::IslandSim& mIslandSim; + PxSolverBodyData* mBodyData; + + PX_NOCOPY(KinematicCopyTask) + +public: + + static const PxU32 NbKinematicsPerTask = 1024; + + KinematicCopyTask(const IG::NodeIndex* const kinematicIndices, + const PxU32 nbKinematics, const IG::IslandSim& islandSim, + PxSolverBodyData* datas, PxU64 contextID) : Cm::Task(contextID), + mKinematicIndices(kinematicIndices), mNbKinematics(nbKinematics), + mIslandSim(islandSim), mBodyData(datas) + { + } + + virtual const char* getName() const { return "KinematicCopyTask"; } + + virtual void runInternal() + { + for (PxU32 i = 0; igetCore(); + copyToSolverBodyData(core.linearVelocity, core.angularVelocity, core.inverseMass, core.inverseInertia, core.body2World, core.maxPenBias, + core.maxContactImpulse, mKinematicIndices[i].index(), core.contactReportThreshold, mBodyData[i + 1], core.lockFlags); + rigidBody->saveLastCCDTransform(); + } + } +}; + + + +void DynamicsContext::update(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask, + PxsContactManager** /*foundPatchManagers*/, PxU32 /*nbFoundPatchManagers*/, + PxsContactManager** /*lostPatchManagers*/, PxU32 /*nbLostPatchManagers*/, + PxU32 /*maxPatchesPerCM*/, + PxsContactManagerOutputIterator& iterator, + PxsContactManagerOutput*, + const PxReal dt, const PxVec3& gravity, const PxU32 /*bitMapWordCounts*/) +{ + PX_PROFILE_ZONE("Dynamics.solverQueueTasks", mContextID); + + PX_UNUSED(simpleIslandManager); + + mOutputIterator = iterator; + + mDt = dt; + mInvDt = dt == 0.0f ? 0.0f : 1.0f / dt; + mGravity = gravity; + + const IG::IslandSim& islandSim = simpleIslandManager.getAccurateIslandSim(); + + const PxU32 islandCount = islandSim.getNbActiveIslands(); + + const PxU32 activatedContactCount = islandSim.getNbActivatedEdges(IG::Edge::eCONTACT_MANAGER); + const IG::EdgeIndex* const activatingEdges = islandSim.getActivatedEdges(IG::Edge::eCONTACT_MANAGER); + + for (PxU32 a = 0; a < activatedContactCount; ++a) + { + PxsContactManager* cm = simpleIslandManager.getContactManager(activatingEdges[a]); + if (cm) + { + cm->getWorkUnit().frictionPatchCount = 0; //KS - zero the friction patch count on any activating edges + } + } + +#if PX_ENABLE_SIM_STATS + if (islandCount > 0) + { + mSimStats.mNbActiveKinematicBodies = islandSim.getNbActiveKinematics(); + mSimStats.mNbActiveDynamicBodies = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + mSimStats.mNbActiveConstraints = islandSim.getNbActiveEdges(IG::Edge::eCONSTRAINT); + } + else + { + mSimStats.mNbActiveKinematicBodies = islandSim.getNbActiveKinematics(); + mSimStats.mNbActiveDynamicBodies = 0; + mSimStats.mNbActiveConstraints = 0; + } +#endif + + mThresholdStreamOut = 0; + + resetThreadContexts(); + + //If there is no work to do then we can do nothing at all. + if (0 == islandCount) + { + return; + } + + //Block to make sure it doesn't run before stage2 of update! + lostTouchTask->addReference(); + + UpdateContinuationTask* task = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(UpdateContinuationTask)), UpdateContinuationTask) + (*this, simpleIslandManager, lostTouchTask, mContextID); + + task->setContinuation(continuation); + + //KS - test that world solver body's velocities are finite and 0, then set it to 0. + //Technically, the velocity should always be 0 but can be stomped if a NAN creeps into the simulation. + PX_ASSERT(mWorldSolverBody.linearVelocity == PxVec3(0.f)); + PX_ASSERT(mWorldSolverBody.angularState == PxVec3(0.f)); + PX_ASSERT(mWorldSolverBody.linearVelocity.isFinite()); + PX_ASSERT(mWorldSolverBody.angularState.isFinite()); + + mWorldSolverBody.linearVelocity = mWorldSolverBody.angularState = PxVec3(0.f); + + const PxU32 kinematicCount = islandSim.getNbActiveKinematics(); + const IG::NodeIndex* const kinematicIndices = islandSim.getActiveKinematics(); + mKinematicCount = kinematicCount; + + const PxU32 bodyCount = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + PxU32 numArtics = islandSim.getNbActiveNodes(IG::Node::eARTICULATION_TYPE); + + { + if (kinematicCount + bodyCount > mSolverBodyPool.capacity()) + { + mSolverBodyPool.reserve((kinematicCount + bodyCount + 31) & ~31); // pad out to 32 * 128 = 4k to prevent alloc churn + mSolverBodyDataPool.reserve((kinematicCount + bodyCount + 31 + 1) & ~31); // pad out to 32 * 128 = 4k to prevent alloc churn + mSolverBodyRemapTable.reserve((kinematicCount + bodyCount + 31 + 1) & ~31); + } + + { + PxSolverBody emptySolverBody; + PxMemZero(&emptySolverBody, sizeof(PxSolverBody)); + mSolverBodyPool.resize(kinematicCount + bodyCount, emptySolverBody); + PxSolverBodyData emptySolverBodyData; + PxMemZero(&emptySolverBodyData, sizeof(PxSolverBodyData)); + mSolverBodyDataPool.resize(kinematicCount + bodyCount + 1, emptySolverBodyData); + mSolverBodyRemapTable.resize(bodyCount); + } + + // integrate and copy all the kinematics - overkill, since not all kinematics + // need solver bodies + + mSolverBodyDataPool[0] = mWorldSolverBodyData; + + + { + PX_PROFILE_ZONE("Dynamics.updateKinematics", mContextID); + PxMemZero(mSolverBodyPool.begin(), kinematicCount * sizeof(PxSolverBody)); + for (PxU32 i = 0; i < kinematicCount; i+= KinematicCopyTask::NbKinematicsPerTask) + { + const PxU32 nbToProcess = PxMin(KinematicCopyTask::NbKinematicsPerTask, kinematicCount - i); + + KinematicCopyTask* copyTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(KinematicCopyTask)), KinematicCopyTask) + (&kinematicIndices[i], nbToProcess, islandSim, &mSolverBodyDataPool[i], mContextID); + + copyTask->setContinuation(task); + + copyTask->removeReference(); + } + } + } + + //Resize arrays of solver constraints... + + PxU32 numArticulationConstraints = numArtics*Dy::DY_ARTICULATION_MAX_SIZE; //Just allocate enough memory to fit worst-case maximum size articulations... + + const PxU32 nbActiveContactManagers = islandSim.getNbActiveEdges(IG::Edge::eCONTACT_MANAGER); + const PxU32 nbActiveConstraints = islandSim.getNbActiveEdges(IG::Edge::eCONSTRAINT); + + PxU32 totalConstraintCount = nbActiveConstraints + nbActiveContactManagers + numArticulationConstraints; + + mSolverConstraintDescPool.forceSize_Unsafe(0); + mSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mOrderedSolverConstraintDescPool.forceSize_Unsafe(0); + mOrderedSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mOrderedSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mTempSolverConstraintDescPool.forceSize_Unsafe(0); + mTempSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mTempSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mContactConstraintBatchHeaders.forceSize_Unsafe(0); + mContactConstraintBatchHeaders.reserve((totalConstraintCount + 63) & (~63)); + mContactConstraintBatchHeaders.forceSize_Unsafe(totalConstraintCount); + + mContactList.forceSize_Unsafe(0); + mContactList.reserve((nbActiveContactManagers + 63u) & (~63u)); + mContactList.forceSize_Unsafe(nbActiveContactManagers); + + mMotionVelocityArray.forceSize_Unsafe(0); + mMotionVelocityArray.reserve((bodyCount + 63u) & (~63u)); + mMotionVelocityArray.forceSize_Unsafe(bodyCount); + + mBodyCoreArray.forceSize_Unsafe(0); + mBodyCoreArray.reserve((bodyCount + 63u) & (~63u)); + mBodyCoreArray.forceSize_Unsafe(bodyCount); + + mRigidBodyArray.forceSize_Unsafe(0); + mRigidBodyArray.reserve((bodyCount + 63u) & (~63u)); + mRigidBodyArray.forceSize_Unsafe(bodyCount); + + mArticulationArray.forceSize_Unsafe(0); + mArticulationArray.reserve((numArtics + 63u) & (~63u)); + mArticulationArray.forceSize_Unsafe(numArtics); + + mNodeIndexArray.forceSize_Unsafe(0); + mNodeIndexArray.reserve((bodyCount + 63u) & (~63u)); + mNodeIndexArray.forceSize_Unsafe(bodyCount); + + + ThresholdStream& stream = getThresholdStream(); + stream.forceSize_Unsafe(0); + stream.reserve(Ps::nextPowerOfTwo(nbActiveContactManagers != 0 ? nbActiveContactManagers - 1 : nbActiveContactManagers)); + + //flip exceeded force threshold buffer + mCurrentIndex = 1 - mCurrentIndex; + + + task->removeReference(); + +} + +void DynamicsContext::updatePostKinematic(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* /*continuation*/, PxBaseTask* lostTouchTask) +{ + + const IG::IslandSim& islandSim = simpleIslandManager.getAccurateIslandSim(); + + const PxU32 islandCount = islandSim.getNbActiveIslands(); + + PxU32 constraintIndex = 0; + + PxU32 solverBatchMax = mSolverBatchSize; + PxU32 articulationBatchMax = 2; + PxU32 minimumConstraintCount = 1; + + + + //create force threshold tasks to produce force change events + PxsForceThresholdTask* forceThresholdTask = PX_PLACEMENT_NEW(getTaskPool().allocate(sizeof(PxsForceThresholdTask)), PxsForceThresholdTask)(*this); + forceThresholdTask->setContinuation(lostTouchTask); + + const IG::IslandId*const islandIds = islandSim.getActiveIslands(); + + PxU32 currentIsland = 0; + PxU32 currentBodyIndex = 0; + PxU32 currentArticulation = 0; + PxU32 currentContact = 0; + //while(start 0) + { + PxBaseTask* task = createSolverTaskChain(*this, objectStarts, counts, + mKinematicCount + currentBodyIndex, simpleIslandManager, mSolverBodyRemapTable.begin(), mMaterialManager, forceThresholdTask, mOutputIterator, mUseEnhancedDeterminism); + task->removeReference(); + } + + currentBodyIndex += nbBodies; + currentArticulation += nbArticulations; + currentContact += nbContactManagers; + + constraintIndex += constraintCount; + } + + //kick off forceThresholdTask + forceThresholdTask->removeReference(); +} + +void DynamicsContext::updateBodyCore(PxBaseTask* continuation) +{ + PX_UNUSED(continuation); +} + +void DynamicsContext::mergeResults() +{ + PX_PROFILE_ZONE("Dynamics.solverMergeResults", mContextID); + //OK. Sum up sim stats here... + +#if PX_ENABLE_SIM_STATS + PxcThreadCoherentCacheIterator threadContextIt(mThreadContextPool); + ThreadContext* threadContext = threadContextIt.getNext(); + + while(threadContext != NULL) + { + ThreadContext::ThreadSimStats& threadStats = threadContext->getSimStats(); + addThreadStats(threadStats); + threadStats.clear(); + threadContext = threadContextIt.getNext(); + } +#endif +} + + +static void preIntegrationParallel( + const PxF32 dt, + PxsBodyCore*const* bodyArray, // INOUT: core body attributes + PxsRigidBody*const* originalBodyArray, // IN: original bodies (LEGACY - DON'T deref the ptrs!!) + PxU32 const* nodeIndexArray, // IN: island node index + PxU32 bodyCount, // IN: body count + PxSolverBody* solverBodyPool, // IN: solver body pool (space preallocated) + PxSolverBodyData* solverBodyDataPool, // IN: solver body data pool (space preallocated) + volatile PxU32* maxSolverPositionIterations, + volatile PxU32* maxSolverVelocityIterations, + const PxVec3& gravity) +{ + PxU32 localMaxPosIter = 0; + PxU32 localMaxVelIter = 0; + + + for(PxU32 a = 1; a < bodyCount; ++a) + { + PxU32 i = a-1; + Ps::prefetchLine(bodyArray[a]); + Ps::prefetchLine(bodyArray[a],128); + Ps::prefetchLine(&solverBodyDataPool[a]); + Ps::prefetchLine(&solverBodyDataPool[a],128); + + PxsBodyCore& core = *bodyArray[i]; + const PxsRigidBody& rBody = *originalBodyArray[i]; + + PxU16 iterWord = core.solverIterationCounts; + localMaxPosIter = PxMax(PxU32(iterWord & 0xff), localMaxPosIter); + localMaxVelIter = PxMax(PxU32(iterWord >> 8), localMaxVelIter); + + //const Cm::SpatialVector& accel = originalBodyArray[i]->getAccelerationV(); + bodyCoreComputeUnconstrainedVelocity(gravity, dt, core.linearDamping, core.angularDamping, rBody.accelScale, core.maxLinearVelocitySq, core.maxAngularVelocitySq, + core.linearVelocity, core.angularVelocity, !!(rBody.mInternalFlags & PxcRigidBody::eDISABLE_GRAVITY)); + + copyToSolverBodyData(core.linearVelocity, core.angularVelocity, core.inverseMass, core.inverseInertia, core.body2World, core.maxPenBias, core.maxContactImpulse, nodeIndexArray[i], + core.contactReportThreshold, solverBodyDataPool[i + 1], core.lockFlags); + solverBodyPool[i].solverProgress = 0; + solverBodyPool[i].maxSolverNormalProgress = 0; + solverBodyPool[i].maxSolverFrictionProgress = 0; + } + const PxU32 i = bodyCount - 1; + PxsBodyCore& core = *bodyArray[i]; + const PxsRigidBody& rBody = *originalBodyArray[i]; + + PxU16 iterWord = core.solverIterationCounts; + localMaxPosIter = PxMax(PxU32(iterWord & 0xff), localMaxPosIter); + localMaxVelIter = PxMax(PxU32(iterWord >> 8), localMaxVelIter); + + bodyCoreComputeUnconstrainedVelocity(gravity, dt, core.linearDamping, core.angularDamping, rBody.accelScale, core.maxLinearVelocitySq, core.maxAngularVelocitySq, + core.linearVelocity, core.angularVelocity, !!(rBody.mInternalFlags & PxcRigidBody::eDISABLE_GRAVITY)); + + copyToSolverBodyData(core.linearVelocity, core.angularVelocity, core.inverseMass, core.inverseInertia, core.body2World, core.maxPenBias, core.maxContactImpulse, nodeIndexArray[i], + core.contactReportThreshold, solverBodyDataPool[i + 1], core.lockFlags); + solverBodyPool[i].solverProgress = 0; + solverBodyPool[i].maxSolverNormalProgress = 0; + solverBodyPool[i].maxSolverFrictionProgress = 0; + + physx::shdfnd::atomicMax(reinterpret_cast(maxSolverPositionIterations), PxI32(localMaxPosIter)); + physx::shdfnd::atomicMax(reinterpret_cast(maxSolverVelocityIterations), PxI32(localMaxVelIter)); +} + + +void PxsPreIntegrateTask::runInternal() +{ + { + PX_PROFILE_ZONE("PreIntegration", 0); + preIntegrationParallel(mDt, mBodyArray + mStartIndex, mOriginalBodyArray + mStartIndex, mNodeIndexArray + mStartIndex, mNumToIntegrate, + mSolverBodies + mStartIndex, mSolverBodyDataPool + mStartIndex, + mMaxSolverPositionIterations, mMaxSolverVelocityIterations, mGravity); + } +} + +void DynamicsContext::preIntegrationParallel( + const PxF32 dt, + PxsBodyCore*const* bodyArray, // INOUT: core body attributes + PxsRigidBody*const* originalBodyArray, // IN: original bodies (LEGACY - DON'T deref the ptrs!!) + PxU32 const* nodeIndexArray, // IN: island node index + PxU32 bodyCount, // IN: body count + PxSolverBody* solverBodyPool, // IN: solver body pool (space preallocated) + PxSolverBodyData* solverBodyDataPool, // IN: solver body data pool (space preallocated) + Cm::SpatialVector* /*motionVelocityArray*/, // OUT: motion velocities + PxU32& maxSolverPositionIterations, + PxU32& maxSolverVelocityIterations, + PxBaseTask& task + ) +{ + //TODO - make this based on some variables so we can try different configurations + const PxU32 IntegrationPerThread = 256; + + const PxU32 numTasks = ((bodyCount + IntegrationPerThread-1)/IntegrationPerThread); + const PxU32 taskBatchSize = 64; + + for(PxU32 i = 0; i < numTasks; i+=taskBatchSize) + { + const PxU32 nbTasks = PxMin(numTasks - i, taskBatchSize); + PxsPreIntegrateTask* tasks = reinterpret_cast(getTaskPool().allocate(sizeof(PxsPreIntegrateTask)*nbTasks)); + for(PxU32 a = 0; a < nbTasks; ++a) + { + PxU32 startIndex = (i+a)*IntegrationPerThread; + PxU32 nbToIntegrate = PxMin((bodyCount-startIndex), IntegrationPerThread); + PxsPreIntegrateTask* pTask = PX_PLACEMENT_NEW(&tasks[a], PxsPreIntegrateTask)(*this, bodyArray, + originalBodyArray, nodeIndexArray, solverBodyPool, solverBodyDataPool, dt, bodyCount, + &maxSolverPositionIterations, &maxSolverVelocityIterations, startIndex, + nbToIntegrate, mGravity); + + pTask->setContinuation(&task); + pTask->removeReference(); + } + } + + PxMemZero(solverBodyPool, bodyCount * sizeof(PxSolverBody)); +} + +inline void WaitBodyRequiredState(volatile PxU32* state, PxU32 requiredState) +{ + while(requiredState != *state ); +} + +void solveParallel(SOLVER_PARALLEL_METHOD_ARGS) +{ + Dy::ThreadContext& threadContext = *context.getThreadContext(); + threadContext.mZVector.forceSize_Unsafe(0); + threadContext.mZVector.reserve(params.mMaxArticulationLinks); + threadContext.mZVector.forceSize_Unsafe(params.mMaxArticulationLinks); + + threadContext.mDeltaV.forceSize_Unsafe(0); + threadContext.mDeltaV.reserve(params.mMaxArticulationLinks); + threadContext.mDeltaV.forceSize_Unsafe(params.mMaxArticulationLinks); + + context.solveParallel(params, islandSim, threadContext.mZVector.begin(), threadContext.mDeltaV.begin()); + + context.putThreadContext(&threadContext); +} + + +void DynamicsContext::solveParallel(SolverIslandParams& params, IG::IslandSim& islandSim, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) +{ + PxI32 targetCount = mSolverCore[mFrictionType]->solveVParallelAndWriteBack(params, Z, deltaV); + + PxI32* solveCount = ¶ms.constraintIndex2; + + //PxI32 targetCount = (PxI32)(params.numConstraintHeaders * (params.velocityIterations + params.positionIterations)); + + WAIT_FOR_PROGRESS_NO_TIMER(solveCount, targetCount); + + integrateCoreParallel(params, islandSim); +} + +void DynamicsContext::integrateCoreParallel(SolverIslandParams& params, IG::IslandSim& islandSim) +{ + const PxI32 unrollCount = 128; + + PxI32* bodyIntegrationListIndex = ¶ms.bodyIntegrationListIndex; + + PxI32 index = physx::shdfnd::atomicAdd(bodyIntegrationListIndex, unrollCount) - unrollCount; + + const PxI32 numBodies = PxI32(params.bodyListSize); + const PxI32 numArtics = PxI32(params.articulationListSize); + + Cm::SpatialVector* PX_RESTRICT motionVelocityArray = params.motionVelocityArray; + PxsBodyCore*const* bodyArray = params.bodyArray; + PxsRigidBody** PX_RESTRICT rigidBodies = params.rigidBodies; + ArticulationSolverDesc* PX_RESTRICT articulationListStart = params.articulationListStart; + + + PxI32 numIntegrated = 0; + + PxI32 bodyRemainder = unrollCount; + + while(index < numArtics) + { + const PxI32 remainder = PxMin(numArtics - index, unrollCount); + bodyRemainder -= remainder; + + for(PxI32 a = 0; a < remainder; ++a, index++) + { + const PxI32 i = index; + { + PX_PROFILE_ZONE("Articulations.integrate", mContextID); + + ArticulationPImpl::updateBodies(articulationListStart[i], mDt); + } + + ++numIntegrated; + } + if(bodyRemainder == 0) + { + index = physx::shdfnd::atomicAdd(bodyIntegrationListIndex, unrollCount) - unrollCount; + bodyRemainder = unrollCount; + } + } + + index -= numArtics; + + const PxI32 unrollPlusArtics = unrollCount + numArtics; + + PxSolverBody* PX_RESTRICT solverBodies = params.bodyListStart; + PxSolverBodyData* PX_RESTRICT solverBodyData = params.bodyDataList + params.solverBodyOffset+1; + + while(index < numBodies) + { + const PxI32 remainder = PxMin(numBodies - index, bodyRemainder); + bodyRemainder -= remainder; + for(PxI32 a = 0; a < remainder; ++a, index++) + { + const PxI32 prefetch = PxMin(index+4, numBodies - 1); + Ps::prefetchLine(bodyArray[prefetch]); + Ps::prefetchLine(bodyArray[prefetch],128); + Ps::prefetchLine(&solverBodies[index],128); + Ps::prefetchLine(&motionVelocityArray[index],128); + Ps::prefetchLine(&bodyArray[index+32]); + Ps::prefetchLine(&rigidBodies[prefetch]); + + PxSolverBodyData& data = solverBodyData[index]; + + integrateCore(motionVelocityArray[index].linear, motionVelocityArray[index].angular, + solverBodies[index], data, mDt); + + PxsRigidBody& rBody = *rigidBodies[index]; + PxsBodyCore& core = rBody.getCore(); + rBody.mLastTransform = core.body2World; + core.body2World = data.body2World; + core.linearVelocity = data.linearVelocity; + core.angularVelocity = data.angularVelocity; + + bool hasStaticTouch = islandSim.getIslandStaticTouchCount(IG::NodeIndex(data.nodeIndex)) != 0; + sleepCheck(rigidBodies[index], mDt, mInvDt, mEnableStabilization, mUseAdaptiveForce, motionVelocityArray[index], hasStaticTouch); + + ++numIntegrated; + } + + { + index = physx::shdfnd::atomicAdd(bodyIntegrationListIndex, unrollCount) - unrollPlusArtics; + bodyRemainder = unrollCount; + } + } + + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(¶ms.numObjectsIntegrated, numIntegrated); +} + +static PxU32 createFinalizeContacts_Parallel(PxSolverBodyData* solverBodyData, ThreadContext& mThreadContext, DynamicsContext& context, + PxU32 startIndex, PxU32 endIndex, PxsContactManagerOutputIterator& outputs) +{ + PX_PROFILE_ZONE("createFinalizeContacts_Parallel", 0); + const PxFrictionType::Enum frictionType = context.getFrictionType(); + const PxReal correlationDist = context.getCorrelationDistance(); + const PxReal bounceThreshold = context.getBounceThreshold(); + const PxReal frictionOffsetThreshold = context.getFrictionOffsetThreshold(); + const PxReal dt = context.getDt(); + const PxReal invDt = PxMin(context.getMaxBiasCoefficient(), context.getInvDt()); + const PxReal solverOffsetSlop = context.getSolverOffsetSlop(); + + PxSolverConstraintDesc* contactDescPtr = mThreadContext.orderedContactConstraints; + + PxConstraintBatchHeader* headers = mThreadContext.contactConstraintBatchHeaders; + + PxI32 axisConstraintCount = 0; + ThreadContext* threadContext = context.getThreadContext(); + threadContext->mConstraintBlockStream.reset(); //ensure there's no left-over memory that belonged to another island + + threadContext->mZVector.forceSize_Unsafe(0); + threadContext->mZVector.reserve(mThreadContext.mMaxArticulationLinks); + threadContext->mZVector.forceSize_Unsafe(mThreadContext.mMaxArticulationLinks); + + //threadContext->mDeltaV.forceSize_Unsafe(0); + //threadContext->mDeltaV.reserve(mThreadContext.mMaxArticulationLinks); + //threadContext->mDeltaV.forceSize_Unsafe(mThreadContext.mMaxArticulationLinks); + + Cm::SpatialVectorF* Z = threadContext->mZVector.begin(); + + PxTransform idt(PxIdentity); + + BlockAllocator blockAllocator(mThreadContext.mConstraintBlockManager, threadContext->mConstraintBlockStream, threadContext->mFrictionPatchStreamPair, threadContext->mConstraintSize); + + const PxReal ccdMaxSeparation = context.getCCDSeparationThreshold(); + + for(PxU32 a = startIndex; a < endIndex; ++a) + { + + PxConstraintBatchHeader& header = headers[a]; + + if(contactDescPtr[header.mStartIndex].constraintLengthOver16 == DY_SC_TYPE_RB_CONTACT) + { + + + PxSolverContactDesc blockDescs[4]; + PxsContactManagerOutput* cmOutputs[4]; + PxsContactManager* cms[4]; + for (PxU32 i = 0; i < header.mStride; ++i) + { + PxSolverConstraintDesc& desc = contactDescPtr[header.mStartIndex + i]; + PxSolverContactDesc& blockDesc = blockDescs[i]; + PxsContactManager* cm = reinterpret_cast(desc.constraint); + + cms[i] = cm; + + PxcNpWorkUnit& unit = cm->getWorkUnit(); + + cmOutputs[i] = &outputs.getContactManager(unit.mNpIndex); + + PxSolverBodyData& data0 = desc.linkIndexA != 0xffff ? solverBodyData[0] : solverBodyData[desc.bodyADataIndex]; + PxSolverBodyData& data1 = desc.linkIndexB != 0xffff ? solverBodyData[0] : solverBodyData[desc.bodyBDataIndex]; + + blockDesc.data0 = &data0; + blockDesc.data1 = &data1; + + PxU8 flags = unit.rigidCore0->mFlags; + if (unit.rigidCore1) + flags |= PxU8(unit.rigidCore1->mFlags); + + blockDesc.bodyFrame0 = unit.rigidCore0->body2World; + blockDesc.bodyFrame1 = unit.rigidCore1 ? unit.rigidCore1->body2World : idt; + blockDesc.shapeInteraction = cm->getShapeInteraction(); + blockDesc.contactForces = cmOutputs[i]->contactForces; + blockDesc.desc = &desc; + blockDesc.body0 = desc.bodyA; + blockDesc.body1 = desc.bodyB; + blockDesc.hasForceThresholds = !!(unit.flags & PxcNpWorkUnitFlag::eFORCE_THRESHOLD); + blockDesc.disableStrongFriction = !!(unit.flags & PxcNpWorkUnitFlag::eDISABLE_STRONG_FRICTION); + blockDesc.bodyState0 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) ? PxSolverContactDesc::eARTICULATION : PxSolverContactDesc::eDYNAMIC_BODY; + blockDesc.bodyState1 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) ? PxSolverContactDesc::eARTICULATION : (unit.flags & PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR) ? PxSolverContactDesc::eKINEMATIC_BODY : + ((unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1) ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eSTATIC_BODY); + //blockDesc.flags = unit.flags; + + PxReal dominance0 = unit.dominance0 ? 1.f : 0.f; + PxReal dominance1 = unit.dominance1 ? 1.f : 0.f; + + blockDesc.mInvMassScales.linear0 = blockDesc.mInvMassScales.angular0 = dominance0; + blockDesc.mInvMassScales.linear1 = blockDesc.mInvMassScales.angular1 = dominance1; + blockDesc.restDistance = unit.restDistance; + blockDesc.frictionPtr = unit.frictionDataPtr; + blockDesc.frictionCount = unit.frictionPatchCount; + blockDesc.maxCCDSeparation = (flags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) ? ccdMaxSeparation : PX_MAX_F32; + + } + +#if DY_BATCH_CONSTRAINTS + SolverConstraintPrepState::Enum state = SolverConstraintPrepState::eUNBATCHABLE; + if(header.mStride == 4) + { + //KS - todo - plumb in axisConstraintCount into this method to keep track of the number of axes + state = createFinalizeMethods4[frictionType](cmOutputs, *threadContext, + blockDescs, + invDt, + bounceThreshold, + frictionOffsetThreshold, + correlationDist, + solverOffsetSlop, + blockAllocator); + + } + if(SolverConstraintPrepState::eSUCCESS != state) +#endif + { + for(PxU32 i = 0; i < header.mStride; ++i) + { + PxSolverConstraintDesc& desc = contactDescPtr[header.mStartIndex+i]; + PxsContactManager* cm = reinterpret_cast(desc.constraint); + PxcNpWorkUnit& n = cm->getWorkUnit(); + + PxsContactManagerOutput& output = outputs.getContactManager(n.mNpIndex); + + createFinalizeMethods[frictionType](blockDescs[i], output, *threadContext, + invDt, bounceThreshold, frictionOffsetThreshold, correlationDist, solverOffsetSlop, + blockAllocator, Z); + + getContactManagerConstraintDesc(output,*cm,desc); + } + } + + for (PxU32 i = 0; i < header.mStride; ++i) + { + PxsContactManager* cm = cms[i]; + + PxcNpWorkUnit& unit = cm->getWorkUnit(); + unit.frictionDataPtr = blockDescs[i].frictionPtr; + unit.frictionPatchCount = blockDescs[i].frictionCount; + axisConstraintCount += blockDescs[i].axisConstraintCount; + + } + } + else if(contactDescPtr[header.mStartIndex].constraintLengthOver16 == DY_SC_TYPE_RB_1D) + { + + SolverConstraintShaderPrepDesc shaderDescs[4]; + PxSolverConstraintPrepDesc descs[4]; + + PxTransform id(PxIdentity); + + for (PxU32 i = 0; i < header.mStride; ++i) + { + PxSolverConstraintDesc& desc = contactDescPtr[header.mStartIndex + i]; + const Constraint* constraint = reinterpret_cast(desc.constraint); + + SolverConstraintShaderPrepDesc& shaderPrepDesc = shaderDescs[i]; + PxSolverConstraintPrepDesc& prepDesc = descs[i]; + + const PxConstraintSolverPrep solverPrep = constraint->solverPrep; + const void* constantBlock = constraint->constantBlock; + const PxU32 constantBlockByteSize = constraint->constantBlockSize; + const PxTransform& pose0 = (constraint->body0 ? constraint->body0->getPose() : id); + const PxTransform& pose1 = (constraint->body1 ? constraint->body1->getPose() : id); + const PxSolverBody* sbody0 = desc.bodyA; + const PxSolverBody* sbody1 = desc.bodyB; + PxSolverBodyData* sbodyData0 = &solverBodyData[desc.linkIndexA != PxSolverConstraintDesc::NO_LINK ? 0 : desc.bodyADataIndex]; + PxSolverBodyData* sbodyData1 = &solverBodyData[desc.linkIndexB != PxSolverConstraintDesc::NO_LINK ? 0 : desc.bodyBDataIndex]; + + shaderPrepDesc.constantBlock = constantBlock; + shaderPrepDesc.constantBlockByteSize = constantBlockByteSize; + shaderPrepDesc.constraint = constraint; + shaderPrepDesc.solverPrep = solverPrep; + + prepDesc.desc = &desc; + prepDesc.bodyFrame0 = pose0; + prepDesc.bodyFrame1 = pose1; + prepDesc.data0 = sbodyData0; + prepDesc.data1 = sbodyData1; + prepDesc.body0 = sbody0; + prepDesc.body1 = sbody1; + prepDesc.linBreakForce = constraint->linBreakForce; + prepDesc.angBreakForce = constraint->angBreakForce; + prepDesc.writeback = &context.getConstraintWriteBackPool()[constraint->index]; + prepDesc.disablePreprocessing = !!(constraint->flags & PxConstraintFlag::eDISABLE_PREPROCESSING); + prepDesc.improvedSlerp = !!(constraint->flags & PxConstraintFlag::eIMPROVED_SLERP); + prepDesc.driveLimitsAreForces = !!(constraint->flags & PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES); + prepDesc.extendedLimits = !!(constraint->flags & PxConstraintFlag::eENABLE_EXTENDED_LIMITS); + prepDesc.minResponseThreshold = constraint->minResponseThreshold; + } + +#if DY_BATCH_CONSTRAINTS && DY_BATCH_1D + SolverConstraintPrepState::Enum state = SolverConstraintPrepState::eUNBATCHABLE; + if(header.mStride == 4) + { + PxU32 totalRows; + state = setupSolverConstraint4 + (shaderDescs, descs, dt, invDt, totalRows, + blockAllocator); + + axisConstraintCount += totalRows; + } + if(state != SolverConstraintPrepState::eSUCCESS) +#endif + { + for(PxU32 i = 0; i < header.mStride; ++i) + { + axisConstraintCount += SetupSolverConstraint(shaderDescs[i], descs[i], blockAllocator, dt, invDt, Z); + } + } + } + } + + threadContext->getSimStats().numAxisSolverConstraints += axisConstraintCount; + + context.putThreadContext(threadContext); + return PxU32(axisConstraintCount); //Can't write to mThreadContext as it's shared!!!! +} + +class PxsCreateFinalizeContactsTask : public Cm::Task +{ + PxsCreateFinalizeContactsTask& operator=(const PxsCreateFinalizeContactsTask&); +public: + PxsCreateFinalizeContactsTask( const PxU32 numConstraints, PxSolverConstraintDesc* descArray, PxSolverBodyData* solverBodyData, + ThreadContext& threadContext, DynamicsContext& context, PxU32 startIndex, PxU32 endIndex, PxsContactManagerOutputIterator& outputs) : + Cm::Task(context.getContextId()), + mNumConstraints(numConstraints), mDescArray(descArray), mSolverBodyData(solverBodyData), + mThreadContext(threadContext), mDynamicsContext(context), + mOutputs(outputs), + mStartIndex(startIndex), mEndIndex(endIndex) + {} + + virtual void runInternal() + { + createFinalizeContacts_Parallel(mSolverBodyData, mThreadContext, mDynamicsContext, mStartIndex, mEndIndex, mOutputs); + } + + virtual const char* getName() const + { + return "PxsDynamics.createFinalizeContacts"; + } + +public: + const PxU32 mNumConstraints; + PxSolverConstraintDesc* mDescArray; + PxSolverBodyData* mSolverBodyData; + ThreadContext& mThreadContext; + DynamicsContext& mDynamicsContext; + PxsContactManagerOutputIterator& mOutputs; + PxU32 mStartIndex; + PxU32 mEndIndex; +}; + +void PxsSolverCreateFinalizeConstraintsTask::runInternal() +{ + PX_PROFILE_ZONE("CreateConstraints", 0); + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + + + + PxU32 descCount = mThreadContext.mNumDifferentBodyConstraints; + PxU32 selfConstraintDescCount = mThreadContext.contactDescArraySize - mThreadContext.mNumDifferentBodyConstraints; + + Ps::Array& accumulatedConstraintsPerPartition = mThreadContext.mConstraintsPerPartition; + + PxU32 numHeaders = 0; + PxU32 currentPartition = 0; + PxU32 maxJ = descCount == 0 ? 0 : accumulatedConstraintsPerPartition[0]; + + const PxU32 maxBatchPartition = 0xFFFFFFFF; + + const PxU32 maxBatchSize = mEnhancedDeterminism ? 1u : 4u; + + PxU32 headersPerPartition = 0; + for(PxU32 a = 0; a < descCount;) + { + + + PxU32 loopMax = PxMin(maxJ - a, maxBatchSize); + PxU16 j = 0; + if(loopMax > 0) + { + PxConstraintBatchHeader& header = mThreadContext.contactConstraintBatchHeaders[numHeaders++]; + + j=1; + PxSolverConstraintDesc& desc = mThreadContext.orderedContactConstraints[a]; + if(!isArticulationConstraint(desc) && (desc.constraintLengthOver16 == DY_SC_TYPE_RB_CONTACT || + desc.constraintLengthOver16 == DY_SC_TYPE_RB_1D) && currentPartition < maxBatchPartition) + { + for(; j < loopMax && desc.constraintLengthOver16 == mThreadContext.orderedContactConstraints[a+j].constraintLengthOver16 && + !isArticulationConstraint(mThreadContext.orderedContactConstraints[a+j]); ++j); + } + header.mStartIndex = a; + header.mStride = j; + headersPerPartition++; + } + if(maxJ == (a + j) && maxJ != descCount) + { + //Go to next partition! + accumulatedConstraintsPerPartition[currentPartition] = headersPerPartition; + headersPerPartition = 0; + currentPartition++; + maxJ = accumulatedConstraintsPerPartition[currentPartition]; + } + a+= j; + } + if(descCount) + accumulatedConstraintsPerPartition[currentPartition] = headersPerPartition; + + + + accumulatedConstraintsPerPartition.forceSize_Unsafe(mThreadContext.mMaxPartitions); + + PxU32 numDifferentBodyBatchHeaders = numHeaders; + + for(PxU32 a = 0; a < selfConstraintDescCount; ++a) + { + PxConstraintBatchHeader& header = mThreadContext.contactConstraintBatchHeaders[numHeaders++]; + header.mStartIndex = a + descCount; + header.mStride = 1; + } + + PxU32 numSelfConstraintBatchHeaders = numHeaders - numDifferentBodyBatchHeaders; + + mThreadContext.numDifferentBodyBatchHeaders = numDifferentBodyBatchHeaders; + mThreadContext.numSelfConstraintBatchHeaders = numSelfConstraintBatchHeaders; + mThreadContext.numContactConstraintBatches = numHeaders; + + PX_UNUSED(descCount); + + { + PxSolverConstraintDesc* descBegin = mThreadContext.orderedContactConstraints; + + const PxU32 numThreads = getTaskManager()->getCpuDispatcher()->getWorkerCount(); + + //Choose an appropriate number of constraint prep tasks. This must be proportionate to the number of constraints to prep and the number + //of worker threads available. + const PxU32 TaskBlockSize = 16; + const PxU32 TaskBlockLargeSize = 64; + const PxU32 BlockAllocationSize = 64; + + PxU32 numTasks = (numHeaders+TaskBlockLargeSize-1)/TaskBlockLargeSize; + + if(numTasks) + { + + if(numTasks < numThreads) + numTasks = PxMax(1u, (numHeaders+TaskBlockSize-1)/TaskBlockSize); + + const PxU32 constraintsPerTask = (numHeaders + numTasks-1)/numTasks; + + for(PxU32 i = 0; i < numTasks; i+=BlockAllocationSize) + { + PxU32 blockSize = PxMin(numTasks - i, BlockAllocationSize); + + PxsCreateFinalizeContactsTask* tasks = reinterpret_cast(mContext.getTaskPool().allocate(sizeof(PxsCreateFinalizeContactsTask)*blockSize)); + + for(PxU32 a = 0; a < blockSize; ++a) + { + PxU32 startIndex = (a + i) * constraintsPerTask; + PxU32 endIndex = PxMin(startIndex + constraintsPerTask, numHeaders); + PxsCreateFinalizeContactsTask* pTask = PX_PLACEMENT_NEW(&tasks[a], PxsCreateFinalizeContactsTask( descCount, descBegin, mContext.mSolverBodyDataPool.begin(), mThreadContext, mContext, startIndex, endIndex, mOutputs)); + + pTask->setContinuation(mCont); + pTask->removeReference(); + } + } + } + } +} + +} +} + + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h new file mode 100644 index 000000000..22bddcbab --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h @@ -0,0 +1,490 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_DYNAMICS_H +#define DY_DYNAMICS_H + +#include "PxvConfig.h" +#include "CmSpatialVector.h" +#include "CmTask.h" +#include "CmPool.h" +#include "PxcThreadCoherentCache.h" +#include "DyThreadContext.h" +#include "PxcConstraintBlockStream.h" +#include "DySolverBody.h" +#include "DyContext.h" +#include "PxsIslandManagerTypes.h" +#include "PxvNphaseImplementationContext.h" +#include "solver/PxSolverDefs.h" + +namespace physx +{ + +namespace Cm +{ + class FlushPool; +} + +namespace IG +{ + class SimpleIslandManager; + struct Edge; +} + +class PxsRigidBody; + +class PxsStreamedThresholdTable; + +struct PxsBodyCore; +struct PxsIslandObjects; +class PxsIslandIndices; +struct PxsIndexedInteraction; +class PxsIslandManager; +struct PxsIndexedConstraint; +struct PxsIndexedContactManager; +class PxsHeapMemoryAllocator; +class PxsMemoryManager; +class PxsDefaultMemoryManager; +struct PxSolverConstraintDesc; + +namespace Cm +{ + class Bitmap; + class SpatialVector; +} + +namespace Dy +{ + class SolverCore; + struct SolverIslandParams; + struct ArticulationSolverDesc; + class Articulation; + class DynamicsContext; + + + + +#define SOLVER_PARALLEL_METHOD_ARGS \ + DynamicsContext& context, \ + SolverIslandParams& params, \ + IG::IslandSim& islandSim + +//typedef void (*PxsSolveParallelMethod)(SOLVER_PARALLEL_METHOD_ARGS); +//extern PxsSolveParallelMethod solveParallel[3]; + +void solveParallel(SOLVER_PARALLEL_METHOD_ARGS); +void solveParallelCouloumFriction(SOLVER_PARALLEL_METHOD_ARGS); + + +struct SolverIslandObjects; + +/** +\brief Solver body pool (array) that enforces 128-byte alignment for base address of array. +\note This reduces cache misses on platforms with 128-byte-size cache lines by aligning the start of the array to the beginning of a cache line. +*/ +class SolverBodyPool : public Ps::Array > > +{ + PX_NOCOPY(SolverBodyPool) +public: + SolverBodyPool() {} +}; + +/** +\brief Solver body data pool (array) that enforces 128-byte alignment for base address of array. +\note This reduces cache misses on platforms with 128-byte-size cache lines by aligning the start of the array to the beginning of a cache line. +*/ +class SolverBodyDataPool : public Ps::Array > > +{ + PX_NOCOPY(SolverBodyDataPool) +public: + SolverBodyDataPool() {} +}; + +class SolverConstraintDescPool : public Ps::Array > > +{ + PX_NOCOPY(SolverConstraintDescPool) +public: + SolverConstraintDescPool() { } +}; + +/** +\brief Encapsulates an island's context +*/ + +struct IslandContext +{ + //The thread context for this island (set in in the island start task, released in the island end task) + ThreadContext* mThreadContext; + PxsIslandIndices mCounts; +}; + + +/** +\brief Encapsules the data used by the constraint solver. +*/ + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + +class DynamicsContext : public Context +{ + PX_NOCOPY(DynamicsContext) +public: + + /** + \brief Creates a DynamicsContext associated with a PxsContext + \return A pointer to the newly-created DynamicsContext. + */ + static DynamicsContext* create( PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocator, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal maxBiasCoefficient, + const bool frictionEveryIteration + ); + + /** + \brief Destroys this DynamicsContext + */ + void destroy(); + + /** + \brief Returns the static world solver body + \return The static world solver body. + */ + PX_FORCE_INLINE PxSolverBody& getWorldSolverBody() { return mWorldSolverBody; } + + PX_FORCE_INLINE Cm::FlushPool& getTaskPool() { return mTaskPool; } + + PX_FORCE_INLINE ThresholdStream& getThresholdStream() { return *mThresholdStream; } + + PX_FORCE_INLINE PxvSimStats& getSimStats() { return mSimStats; } + +#if PX_ENABLE_SIM_STATS + void addThreadStats(const ThreadContext::ThreadSimStats& stats); +#endif + + /** + \brief The entry point for the constraint solver. + \param[in] dt The simulation time-step + \param[in] continuation The continuation task for the solver + + This method is called after the island generation has completed. Its main responsibilities are: + (1) Reserving the solver body pools + (2) Initializing the static and kinematic solver bodies, which are shared resources between islands. + (3) Construct the solver task chains for each island + + Each island is solved as an independent solver task chain in parallel. + + */ + + virtual void update(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask, + PxsContactManager** foundPatchManagers, PxU32 nbFoundPatchManagers, PxsContactManager** lostPatchManagers, PxU32 nbLostPatchManagers, + PxU32 maxPatchesPerCM, PxsContactManagerOutputIterator& iter, PxsContactManagerOutput* gpuOutputs, const PxReal dt, const PxVec3& gravity, const PxU32 bitMapWordCounts); + + + void updatePostKinematic(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask); + + virtual void processLostPatches(IG::SimpleIslandManager& /*simpleIslandManager*/, PxsContactManager** /*lostPatchManagers*/, PxU32 /*nbLostPatchManagers*/, PxsContactManagerOutputIterator& /*iterator*/){} + + virtual void updateBodyCore(PxBaseTask* continuation); + + virtual void setSimulationController(PxsSimulationController* simulationController ){ mSimulationController = simulationController; } + /** + \brief This method combines the results of several islands, e.g. constructing scene-level simulation statistics and merging together threshold streams for contact notification. + */ + virtual void mergeResults(); + + virtual void getDataStreamBase(void*& /*contactStreamBase*/, void*& /*patchStreamBase*/, void*& /*forceAndIndicesStreamBase*/){} + + /** + \brief Allocates and returns a thread context object. + \return A thread context. + */ + PX_FORCE_INLINE ThreadContext* getThreadContext() + { + return mThreadContextPool.get(); + } + + /** + \brief Returns a thread context to the thread context pool. + \param[in] context The thread context to return to the thread context pool. + */ + void putThreadContext(ThreadContext* context) + { + mThreadContextPool.put(context); + } + + + PX_FORCE_INLINE PxU32 getKinematicCount() const { return mKinematicCount; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + +protected: + + /** + \brief Constructor for DynamicsContext + */ + DynamicsContext(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocator, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal maxBiasCoefficient, + const bool frictionEveryIteration + ); + /** + \brief Destructor for DynamicsContext + */ + virtual ~DynamicsContext(); + + + // Solver helper-methods + /** + \brief Computes the unconstrained velocity for a given PxsRigidBody + \param[in] atom The PxsRigidBody + */ + void computeUnconstrainedVelocity(PxsRigidBody* atom) const; + + /** + \brief fills in a PxSolverConstraintDesc from an indexed interaction + \param[in,out] desc The PxSolverConstraintDesc + \param[in] constraint The PxsIndexedInteraction + */ + void setDescFromIndices(PxSolverConstraintDesc& desc, + const PxsIndexedInteraction& constraint, const PxU32 solverBodyOffset); + + + void setDescFromIndices(PxSolverConstraintDesc& desc, IG::EdgeIndex edgeIndex, + const IG::SimpleIslandManager& islandManager, PxU32* bodyRemapTable, const PxU32 solverBodyOffset); + + /** + \brief Compute the unconstrained velocity for set of bodies in parallel. This function may spawn additional tasks. + \param[in] dt The timestep + \param[in] bodyArray The array of body cores + \param[in] originalBodyArray The array of PxsRigidBody + \param[in] nodeIndexArray The array of island node index + \param[in] bodyCount The number of bodies + \param[out] solverBodyPool The pool of solver bodies. These are synced with the corresponding body in bodyArray. + \param[out] solverBodyDataPool The pool of solver body data. These are synced with the corresponding body in bodyArray + \param[out] motionVelocityArray The motion velocities for the bodies + \param[out] maxSolverPositionIterations The maximum number of position iterations requested by any body in the island + \param[out] maxSolverVelocityIterations The maximum number of velocity iterations requested by any body in the island + \param[out] integrateTask The continuation task for any tasks spawned by this function. + */ + void preIntegrationParallel( + const PxF32 dt, + PxsBodyCore*const* bodyArray, // INOUT: core body attributes + PxsRigidBody*const* originalBodyArray, // IN: original body atom names (LEGACY - DON'T deref the ptrs!!) + PxU32 const* nodeIndexArray, // IN: island node index + PxU32 bodyCount, // IN: body count + PxSolverBody* solverBodyPool, // IN: solver atom pool (space preallocated) + PxSolverBodyData* solverBodyDataPool, + Cm::SpatialVector* motionVelocityArray, // OUT: motion velocities + PxU32& maxSolverPositionIterations, + PxU32& maxSolverVelocityIterations, + PxBaseTask& integrateTask + ); + + /** + \brief Solves an island in parallel. + + \param[in] params Solver parameter structure + */ + + void solveParallel(SolverIslandParams& params, IG::IslandSim& islandSim, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV); + + + + void integrateCoreParallel(SolverIslandParams& params, IG::IslandSim& islandSim); + + + + + /** + \brief Resets the thread contexts + */ + void resetThreadContexts(); + + /** + \brief Returns the scratch memory allocator. + \return The scratch memory allocator. + */ + PX_FORCE_INLINE PxcScratchAllocator& getScratchAllocator() { return mScratchAllocator; } + + //Data + + /** + \brief Body to represent the world static body. + */ + PX_ALIGN(16, PxSolverBody mWorldSolverBody); + /** + \brief Body data to represent the world static body. + */ + PX_ALIGN(16, PxSolverBodyData mWorldSolverBodyData); + + /** + \brief A thread context pool + */ + PxcThreadCoherentCache mThreadContextPool; + + /** + \brief Solver constraint desc array + */ + SolverConstraintDescPool mSolverConstraintDescPool; + + /** + \brief Ordered sover constraint desc array (after partitioning) + */ + SolverConstraintDescPool mOrderedSolverConstraintDescPool; + + /** + \brief A temporary array of constraint descs used for partitioning + */ + SolverConstraintDescPool mTempSolverConstraintDescPool; + + /** + \brief An array of contact constraint batch headers + */ + Ps::Array mContactConstraintBatchHeaders; + + /** + \brief Array of motion velocities for all bodies in the scene. + */ + Ps::Array mMotionVelocityArray; + + /** + \brief Array of body core pointers for all bodies in the scene. + */ + Ps::Array mBodyCoreArray; + + /** + \brief Array of rigid body pointers for all bodies in the scene. + */ + Ps::Array mRigidBodyArray; + + /** + \brief Array of articulationpointers for all articulations in the scene. + */ + Ps::Array mArticulationArray; + + /** + \brief Global pool for solver bodies. Kinematic bodies are at the start, and then dynamic bodies + */ + SolverBodyPool mSolverBodyPool; + /** + \brief Global pool for solver body data. Kinematic bodies are at the start, and then dynamic bodies + */ + SolverBodyDataPool mSolverBodyDataPool; + + + ThresholdStream* mExceededForceThresholdStream[2]; //this store previous and current exceeded force thresholdStream + + Ps::Array mExceededForceThresholdStreamMask; + + /** + \brief Interface to the solver core. + \note We currently only support PxsSolverCoreSIMD. Other cores may be added in future releases. + */ + SolverCore* mSolverCore[PxFrictionType::eFRICTION_COUNT]; + + Ps::Array mSolverBodyRemapTable; //Remaps from the "active island" index to the index within a solver island + + Ps::Array mNodeIndexArray; //island node index + + Ps::Array mContactList; + + /** + \brief The total number of kinematic bodies in the scene + */ + PxU32 mKinematicCount; + + /** + \brief Atomic counter for the number of threshold stream elements. + */ + PxI32 mThresholdStreamOut; + + + + PxsMaterialManager* mMaterialManager; + + PxsContactManagerOutputIterator mOutputIterator; + +private: + //private: + PxcScratchAllocator& mScratchAllocator; + Cm::FlushPool& mTaskPool; + PxTaskManager* mTaskManager; + PxU32 mCurrentIndex; // this is the index point to the current exceeded force threshold stream + + PxU64 mContextID; + + protected: + + friend class PxsSolverStartTask; + friend class PxsSolverAticulationsTask; + friend class PxsSolverSetupConstraintsTask; + friend class PxsSolverCreateFinalizeConstraintsTask; + friend class PxsSolverConstraintPartitionTask; + friend class PxsSolverSetupSolveTask; + friend class PxsSolverIntegrateTask; + friend class PxsSolverEndTask; + friend class PxsSolverConstraintPostProcessTask; + friend class PxsForceThresholdTask; + friend class SolverArticulationUpdateTask; + + friend void solveParallel(SOLVER_PARALLEL_METHOD_ARGS); +}; + +#if PX_VC + #pragma warning(pop) +#endif + +} +} + +#endif //DY_DYNAMICS_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp new file mode 100644 index 000000000..c32da63c6 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp @@ -0,0 +1,4218 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsMathUtils.h" +#include "CmConeLimitHelper.h" +#include "DySolverConstraint1D.h" +#include "DyFeatherstoneArticulation.h" +#include "PxsRigidBody.h" +#include "PxcConstraintBlockStream.h" +#include "DyArticulationContactPrep.h" +#include "DyDynamics.h" +#include "DyArticulationReference.h" +#include "DyArticulationPImpl.h" +#include "DyArticulationFnsSimd.h" +#include "PxArticulation.h" +#include "PsFoundation.h" +#include "DyFeatherstoneArticulationLink.h" +#include "DyFeatherstoneArticulationJointData.h" +#include "DySolverConstraint1DStep.h" +#include "DyTGSDynamics.h" +#include "DyConstraintPrep.h" +#include "common/PxProfileZone.h" + +#ifndef FEATURESTONE_DEBUG +#define FEATURESTONE_DEBUG 0 +#endif + + +// we encode articulation link handles in the lower bits of the pointer, so the +// articulation has to be aligned, which in an aligned pool means we need to size it +// appropriately + +namespace physx +{ + +namespace Dy +{ + void SolverCoreRegisterArticulationFns(); + + void SolverCoreRegisterArticulationFnsCoulomb(); + + void PxvRegisterArticulationsReducedCoordinate() + { + PxArticulationBase::Enum type = PxArticulationBase::eReducedCoordinate; + ArticulationPImpl::sComputeUnconstrainedVelocities[type] = &FeatherstoneArticulation::computeUnconstrainedVelocities; + ArticulationPImpl::sUpdateBodies[type] = &FeatherstoneArticulation::updateBodies; + ArticulationPImpl::sUpdateBodiesTGS[type] = &FeatherstoneArticulation::updateBodiesTGS; + ArticulationPImpl::sSaveVelocity[type] = &FeatherstoneArticulation::saveVelocity; + ArticulationPImpl::sSaveVelocityTGS[type] = &FeatherstoneArticulation::saveVelocityTGS; + + ArticulationPImpl::sUpdateDeltaMotion[type] = &FeatherstoneArticulation::recordDeltaMotion; + ArticulationPImpl::sDeltaMotionToMotionVel[type] = &FeatherstoneArticulation::deltaMotionToMotionVelocity; + ArticulationPImpl::sComputeUnconstrainedVelocitiesTGS[type] = &FeatherstoneArticulation::computeUnconstrainedVelocitiesTGS; + ArticulationPImpl::sSetupInternalConstraintsTGS[type] = &FeatherstoneArticulation::setupSolverConstraintsTGS; + + SolverCoreRegisterArticulationFns(); + SolverCoreRegisterArticulationFnsCoulomb(); + } + + + ArticulationData::~ArticulationData() + { + + if (mLinksData) + PX_FREE_AND_RESET(mLinksData); + + if (mJointData) + PX_FREE_AND_RESET(mJointData); + } + + void ArticulationData::resizeLinkData(const PxU32 linkCount) + { + mMotionVelocities.reserve(linkCount); + mMotionVelocities.forceSize_Unsafe(linkCount); + + mMotionAccelerations.reserve(linkCount); + mMotionAccelerations.forceSize_Unsafe(linkCount); + + mCorioliseVectors.reserve(linkCount); + mCorioliseVectors.forceSize_Unsafe(linkCount); + + mZAForces.reserve(linkCount); + mZAForces.forceSize_Unsafe(linkCount); + + mDeltaMotionVector.reserve(linkCount); + mDeltaMotionVector.forceSize_Unsafe(linkCount); + + mPreTransform.reserve(linkCount); + mPreTransform.forceSize_Unsafe(linkCount); + + //mTempSpatialMatrix.reserve(linkCount); + //mTempSpatialMatrix.forceSize_Unsafe(linkCount); + + mAccumulatedPoses.reserve(linkCount); + mAccumulatedPoses.forceSize_Unsafe(linkCount); + + mDeltaQ.reserve(linkCount); + mDeltaQ.forceSize_Unsafe(linkCount); + + mPosIterMotionVelocities.reserve(linkCount); + mPosIterMotionVelocities.forceSize_Unsafe(linkCount); + + mJointTransmittedForce.reserve(linkCount); + mJointTransmittedForce.forceSize_Unsafe(linkCount); + + + if (mLinksData) + PX_FREE_AND_RESET(mLinksData); + + if (mJointData) + PX_FREE_AND_RESET(mJointData); + + mLinksData = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ArticulationLinkData) * linkCount, PX_DEBUG_EXP("ArticulationLinkData")), ArticulationLinkData)(); + mJointData = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ArticulationJointCoreData) * linkCount, PX_DEBUG_EXP("ArticulationJointCoreData")), ArticulationJointCoreData)(); + + + const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount; + + PxMemZero(mMotionVelocities.begin(), size); + PxMemZero(mMotionAccelerations.begin(), size); + PxMemZero(mCorioliseVectors.begin(), size); + PxMemZero(mZAForces.begin(), size); + PxMemZero(mDeltaMotionVector.begin(), size); + + PxMemZero(mPreTransform.begin(), sizeof(PxTransform) * linkCount); + + PxMemZero(mLinksData, sizeof(ArticulationLinkData) * linkCount); + PxMemZero(mJointData, sizeof(ArticulationJointCoreData) * linkCount); + + + } + + void ArticulationData::resizeJointData(const PxU32 dofs) + { + mJointAcceleration.reserve(dofs); + mJointAcceleration.forceSize_Unsafe(dofs); + + mJointVelocity.reserve(dofs); + mJointVelocity.forceSize_Unsafe(dofs); + + mJointDeltaVelocity.reserve(dofs); + mJointDeltaVelocity.forceSize_Unsafe(dofs); + + mJointPosition.reserve(dofs); + mJointPosition.forceSize_Unsafe(dofs); + + mJointForce.reserve(dofs); + mJointForce.forceSize_Unsafe(dofs); + + /* mJointFrictionForce.reserve(dofs); + mJointFrictionForce.forceSize_Unsafe(dofs);*/ + + mPosIterJointDeltaVelocities.reserve(dofs); + mPosIterJointDeltaVelocities.forceSize_Unsafe(dofs); + + + /*mTempData.mJointForce.reserve(dofs); + mTempData.mJointForce.forceSize_Unsafe(dofs);*/ + + PxMemZero(mJointAcceleration.begin(), sizeof(PxReal) * dofs); + PxMemZero(mJointVelocity.begin(), sizeof(PxReal) * dofs); + PxMemZero(mJointDeltaVelocity.begin(), sizeof(PxReal) * dofs); + PxMemZero(mPosIterJointDeltaVelocities.begin(), sizeof(PxReal)*dofs); + PxMemZero(mJointPosition.begin(), sizeof(PxReal) * dofs); + PxMemZero(mJointForce.begin(), sizeof(PxReal) * dofs); + //PxMemZero(mJointFrictionForce.begin(), sizeof(PxReal) * dofs); + } + + ArticulationLinkData& ArticulationData::getLinkData(PxU32 index) const + { + PX_ASSERT(index < mLinkCount); + return mLinksData[index]; + } + + //ArticulationJointCoreData& ArticulationData::getJointData(PxU32 index) const + //{ + // PX_ASSERT(index < mLinkCount); + // return mJointData[index]; + //} + + void ArticulationJointCore::setJointPose(ArticulationJointCoreData& jointDatum) + { + if (dirtyFlag & ArticulationJointCoreDirtyFlag::ePOSE) + { + relativeQuat = (childPose.q * (parentPose.q.getConjugate())).getNormalized(); + + jointDatum.computeMotionMatrix(static_cast(this)); + + dirtyFlag &= ~ArticulationJointCoreDirtyFlag::ePOSE; + } + } + + PX_COMPILE_TIME_ASSERT((sizeof(Articulation)&(DY_ARTICULATION_MAX_SIZE - 1)) == 0); + + FeatherstoneArticulation::FeatherstoneArticulation(Sc::ArticulationSim* sim) + : ArticulationV(sim, PxArticulationBase::eReducedCoordinate), mHasSphericalJoint(false), + mGpuRemapId(0xffffffff) + { + PX_ASSERT((reinterpret_cast(this) & (DY_ARTICULATION_MAX_SIZE - 1)) == 0); + } + + FeatherstoneArticulation::~FeatherstoneArticulation() + { + } + + void FeatherstoneArticulation::copyJointData(ArticulationData& data, PxReal* toJointData, PxReal* fromJointData) + { + const PxU32 linkCount = data.getLinkCount(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + + PxReal* dest = &toJointData[jointDatum.jointOffset]; + + PxReal* source = &fromJointData[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + dest[ind] = source[ind]; + } + } + } + + void FeatherstoneArticulation::computeDofs() + { + const PxU32 linkCount = mArticulationData.getLinkCount(); + PxU32 totalDofs = 0; + PxU32 totalLocks = 0; + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = mArticulationData.getLink(linkID); + + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + + jointDatum.computeJointDof(link.inboundJoint, true); + jointDatum.jointOffset = totalDofs; + totalDofs += jointDatum.dof; + totalLocks += jointDatum.lockedAxes; + } + + if (totalDofs != mArticulationData.getDofs()) + { + mArticulationData.resizeJointData(totalDofs); + } + + mArticulationData.setDofs(totalDofs); + mArticulationData.setLocks(totalLocks); + } + + bool FeatherstoneArticulation::resize(const PxU32 linkCount) + { + if (Dy::ArticulationV::resize(linkCount)) + { + if (linkCount != mSolverDesc.linkCount) + { + //compute scratchSize + const PxU32 linkCount4 = (linkCount + 3)&(~3); + PxU32 scratchSize = sizeof(Cm::SpatialVectorF) * linkCount4 * 4 + + sizeof(Cm::SpatialVector) * linkCount4 + + sizeof(Dy::SpatialMatrix) * linkCount4 + + sizeof(PxReal) * linkCount4 * 4; + + mScratchMemory.resize(scratchSize); + mSolverDesc.scratchMemory = mScratchMemory.begin(); + mSolverDesc.scratchMemorySize = Ps::to16(scratchSize); + + mArticulationData.resizeLinkData(linkCount); + + } + return true; + } + return false; + } + + + void FeatherstoneArticulation::getDataSizes(PxU32 linkCount, PxU32 &solverDataSize, PxU32& totalSize, PxU32& scratchSize) + { + PX_UNUSED(linkCount); + solverDataSize = 0; + totalSize = 0; + scratchSize = 0; + } + + void FeatherstoneArticulation::onUpdateSolverDesc() + { + Dy::ArticulationV::onUpdateSolverDesc(); + mArticulationData.mLinks = mSolverDesc.links; + mArticulationData.mLinkCount = mSolverDesc.linkCount; + mArticulationData.mCore = mSolverDesc.core; + mArticulationData.mExternalAcceleration = mSolverDesc.acceleration; + mArticulationData.mSolverDataSize = mSolverDesc.solverDataSize; + mArticulationData.mArticulation = this; + + computeDofs(); + } + + PxU32 FeatherstoneArticulation::getDofs() + { + PxU32 dofs = mArticulationData.getDofs(); + + if (dofs == 0xffffffff) + { + computeDofs(); + } + + return mArticulationData.getDofs(); + } + + PxU32 FeatherstoneArticulation::getDof(const PxU32 linkID) + { + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + return jointDatum.dof; + } + + void FeatherstoneArticulation::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) + { + applyCacheToDest(mArticulationData, cache, mArticulationData.getJointVelocities(), mArticulationData.getJointAccelerations(), + mArticulationData.getJointPositions(), mArticulationData.getJointForces(), flag); + } + + void FeatherstoneArticulation::copyInternalStateToCache(PxArticulationCache& cache, + const PxArticulationCacheFlags flag) + { + if (flag & PxArticulationCache::eVELOCITY) + { + copyJointData(mArticulationData, cache.jointVelocity, mArticulationData.getJointVelocities()); + } + + if (flag & PxArticulationCache::eACCELERATION) + { + copyJointData(mArticulationData, cache.jointAcceleration, mArticulationData.getJointAccelerations()); + } + + if (flag & PxArticulationCache::ePOSITION) + { + copyJointData(mArticulationData, cache.jointPosition, mArticulationData.getJointPositions()); + } + + if (flag & PxArticulationCache::eFORCE) + { + copyJointData(mArticulationData, cache.jointForce, mArticulationData.getJointForces()); + } + + if (flag & PxArticulationCache::eROOT) + { + ArticulationLink& rLink = mArticulationData.getLink(0); + Cm::SpatialVectorF& vel = mArticulationData.getMotionVelocity(0); + Cm::SpatialVectorF& acel = mArticulationData.getMotionAcceleration(0); + PxsBodyCore& rBodyCore = *rLink.bodyCore; + const PxTransform& body2World = rBodyCore.body2World; + cache.rootLinkData.transform = body2World; + cache.rootLinkData.linVel = vel.bottom; + cache.rootLinkData.angVel = vel.top; + cache.rootLinkData.linAcel = body2World.rotate(acel.bottom); + cache.rootLinkData.angAcel = body2World.rotate(acel.top); + } + } + + void FeatherstoneArticulation::transformInertia(const SpatialTransform& sTod, SpatialMatrix& spatialInertia) + { + const SpatialTransform dTos = sTod.getTranspose(); + + PxMat33 tl = sTod.R * spatialInertia.topLeft; + PxMat33 tr = sTod.R * spatialInertia.topRight; + PxMat33 bl = sTod.T * spatialInertia.topLeft + sTod.R * spatialInertia.bottomLeft; + PxMat33 br = sTod.T * spatialInertia.topRight + sTod.R * spatialInertia.getBottomRight(); + + spatialInertia.topLeft = tl * dTos.R + tr * dTos.T; + spatialInertia.topRight = tr * dTos.R; + spatialInertia.bottomLeft = bl * dTos.R + br * dTos.T; + + //aligned inertia + spatialInertia.bottomLeft = (spatialInertia.bottomLeft + spatialInertia.bottomLeft.getTranspose()) * 0.5f; + } + + void FeatherstoneArticulation::getImpulseResponse( + PxU32 linkID, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + Cm::SpatialVector& deltaVV) const + { + PX_ASSERT(impulse.pad0 == 0.f && impulse.pad1 == 0.f); + + //impulse lin is contact normal, and ang is raxn. R is body2World, R(t) is world2Body + //| R(t), 0 | + //| R(t)*r, R(t)| + //r is the vector from center of mass to contact point + //p(impluse) = |n| + // |0| + + //ArticulationLink* links = mArticulationData.getLinks(); + + //ArticulationLink& link = links[linkID]; + //PxTransform& body2World = link.bodyCore->body2World; + const PxTransform& body2World = mArticulationData.getPreTransform(linkID); + + //transform p(impluse) from world space to the local space of linkId + Cm::SpatialVectorF impl = Cm::SpatialVectorF(body2World.rotateInv(impulse.linear), body2World.rotateInv(impulse.angular)); + + getZ(linkID, mArticulationData, Z, impl); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + Cm::SpatialVectorF deltaV = getDeltaV(fixBase, linkID, mArticulationData, Z); + PX_ASSERT(deltaV.pad0 == 0.f && deltaV.pad1 == 0.f); + + //this is in world space + deltaVV.linear = body2World.rotate(deltaV.bottom); + deltaVV.angular = body2World.rotate(deltaV.top); + } + + //This will return world space SpatialVectorV + Cm::SpatialVectorV FeatherstoneArticulation::getLinkVelocity(const PxU32 linkID) const + { + //This is in the world space + const Cm::SpatialVectorF& motionVelocity = mArticulationData.getMotionVelocity(linkID); + + Cm::SpatialVectorV velocity; + velocity.linear = V3LoadU(motionVelocity.bottom); + velocity.angular = V3LoadU(motionVelocity.top); + + return velocity; + } + + Cm::SpatialVectorV FeatherstoneArticulation::getLinkMotionVector(const PxU32 linkID) const + { + const Cm::SpatialVectorF& motionVector = mArticulationData.getDeltaMotionVector(linkID); + + //ArticulationLink& link = mArticulationData.getLink(linkID); + //PxTransform& body2World = link.bodyCore->body2World; + + /*const PxVec3 mLinear = body2World.rotate(motionVector.bottom); + const PxVec3 mAngular = body2World.rotate(motionVector.top);*/ + + Cm::SpatialVectorV velocity; + velocity.linear = V3LoadU(motionVector.bottom); + velocity.angular = V3LoadU(motionVector.top); + + return velocity; + } + + //this is called by island gen to determine whether the articulation should be awake or sleep + Cm::SpatialVector FeatherstoneArticulation::getMotionVelocity(const PxU32 linkID) const + { + //This is in the body space + const Cm::SpatialVectorF& motionVelocity = mArticulationData.getMotionVelocity(linkID); + + // ArticulationLink& link = mArticulationData.getLink(linkID); + // PxTransform& body2World = link.bodyCore->body2World; + + /*const PxVec3 linear = body2World.rotate(motionVelocity.bottom); + const PxVec3 angular = body2World.rotate(motionVelocity.top);*/ + + return Cm::SpatialVector(motionVelocity.bottom, motionVelocity.top); + } + + PxReal FeatherstoneArticulation::getLinkMaxPenBias(const PxU32 linkID) const + { + return mArticulationData.getLinkData(linkID).maxPenBias; + } + + void PxcFsFlushVelocity(FeatherstoneArticulation& articulation, Cm::SpatialVectorF* deltaV) + { + ArticulationData& data = articulation.mArticulationData; + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities(); + Cm::SpatialVectorF* deferredZ = data.getSpatialZAVectors(); + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + //PxTransform* poses = data.getAccumulatedPoses(); + PxTransform* poses = data.getPreTransform(); + + //This will be zero at the begining of the frame + PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + + if (fixBase) + { + deltaV[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + else + { + //ArticulationLink& link = links[0]; + + deltaV[0] = data.getBaseInvSpatialArticulatedInertia() * (-deferredZ[0]); + //const PxTransform& body2World0 = link.bodyCore->body2World; + const PxTransform& body2World0 = poses[0]; + + motionVelocities[0] += deltaV[0].rotate(body2World0); + + PX_ASSERT(motionVelocities[0].isFinite()); + } + + const PxU32 linkCount = data.getLinkCount(); + + for (PxU32 i = 1; i < linkCount; i++) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& tJointDatum = jointData[i]; + + Cm::SpatialVectorF dV = FeatherstoneArticulation::propagateVelocity(tLinkDatum, tJointDatum, deferredZ[i], + &jointDeltaVelocities[tJointDatum.jointOffset], deltaV[tLink.parent]); + + deltaV[i] = dV; + + const PxTransform& tBody2World = poses[i]; + motionVelocities[i] += dV.rotate(tBody2World); + + PX_ASSERT(motionVelocities[i].isFinite()); + } + + PxMemZero(deferredZ, sizeof(Cm::SpatialVectorF)*linkCount); + } + + + + + void FeatherstoneArticulation::recordDeltaMotion(const ArticulationSolverDesc& desc, + const PxReal dt, Cm::SpatialVectorF* deltaV) + { + FeatherstoneArticulation* articulation = static_cast(desc.articulation); + ArticulationData& data = articulation->mArticulationData; + const PxU32 linkCount = data.getLinkCount(); + + if(data.mJointDirty) + PxcFsFlushVelocity(*articulation, deltaV); + + Cm::SpatialVectorF* deltaMotion = data.getDeltaMotionVector(); + Cm::SpatialVectorF* motionVeloties = data.getMotionVelocities(); + PX_UNUSED(motionVeloties); + + PxReal* jointPosition = data.getJointPositions(); + PxReal* jointVelocities = data.getJointVelocities(); + PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + + + data.mAccumulatedDt += dt; + data.setDt(dt); + + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + if (!fixBase) + { + const Cm::SpatialVectorF& motionVelocity = data.getMotionVelocity(0); + PX_ASSERT(motionVelocity.top.isFinite()); + PX_ASSERT(motionVelocity.bottom.isFinite()); + + const PxTransform preTrans = data.mAccumulatedPoses[0]; + + PxVec3 lin = motionVelocity.bottom; + PxVec3 ang = motionVelocity.top; + + PxVec3 newP = preTrans.p + lin * dt; + + PxTransform newPose = PxTransform(newP, Ps::exp(ang*dt) * preTrans.q); + + //PxVec3 lin, ang; + /*calculateNewVelocity(newPose, data.mPreTransform[0], + 1.f, lin, ang); */ + + data.mAccumulatedPoses[0] = newPose; + + PxQuat dq = newPose.q * data.mPreTransform[0].q.getConjugate(); + + if (dq.w < 0.f) + dq = -dq; + + data.mDeltaQ[0] = dq; + + deltaMotion[0] += motionVelocity * dt; + /*deltaMotion[0].top = ang; + deltaMotion[0].bottom = lin;*/ + + + } + + for (PxU32 linkID = 1; linkID < linkCount; linkID++) + { + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + + PxTransform newPose = articulation->propagateTransform(linkID, data.getLinks(), jointDatum, data.getMotionVelocities(), + dt, data.mAccumulatedPoses[data.getLink(linkID).parent], data.mAccumulatedPoses[linkID], + jointVelocities, jointDeltaVelocities, jointPosition); + + //data.mDeltaQ[linkID] = data.mPreTransform[linkID].q.getConjugate() * newPose.q; + PxQuat dq = newPose.q * data.mPreTransform[linkID].q.getConjugate(); + + if(dq.w < 0.f) + dq = -dq; + + data.mDeltaQ[linkID] = dq; + + for (PxU32 i = 0, idx = jointDatum.jointOffset; i < jointDatum.dof; ++i, idx++) + { + jointDeltaVelocities[idx] = 0.f; + } + + + PxVec3 lin, ang; + calculateNewVelocity(newPose, data.mPreTransform[linkID], + 1.f, lin, ang); + + + deltaMotion[linkID].top = ang;// motionVeloties[linkID].top * dt; + deltaMotion[linkID].bottom = lin;// motionVeloties[linkID].top * dt; + + //Record the new current pose + data.mAccumulatedPoses[linkID] = newPose; + +#if 0 + + + ArticulationLink& link = data.getLink(linkID); + + if (link.inboundJoint->jointType != PxArticulationJointType::eSPHERICAL) + { + const PxTransform& parentPose = data.mAccumulatedPoses[link.parent]; + const PxTransform& body2World = newPose; + + + PxVec3 rw = body2World.p - parentPose.p; + + PxVec3 linVel = data.getMotionVelocity(link.parent).bottom + data.getMotionVelocity(link.parent).top.cross(rw); + PxVec3 angVel = data.getMotionVelocity(link.parent).top; + + const PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + + PxVec3 tDeltaV(0.f); + PxVec3 tDeltaA(0.f); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + tDeltaV += jointDatum.motionMatrix[ind].bottom * jVelocity[ind]; + tDeltaA += jointDatum.motionMatrix[ind].top * jVelocity[ind]; + } + + linVel += body2World.rotate(tDeltaV); + angVel += body2World.rotate(tDeltaA); + + PX_UNUSED(linVel); + + motionVeloties[linkID].bottom = linVel; + //motionVeloties[linkID].bottom = deltaChange/dt; + motionVeloties[linkID].top = angVel; + } +#endif + } + } + + void FeatherstoneArticulation::deltaMotionToMotionVelocity(const ArticulationSolverDesc& desc, PxReal invDt) + { + + FeatherstoneArticulation* articulation = static_cast(desc.articulation); + ArticulationData& data = articulation->mArticulationData; + const PxU32 linkCount = data.getLinkCount(); + Cm::SpatialVectorF* deltaMotion = data.getDeltaMotionVector(); + + for (PxU32 linkID = 0; linkID(delta); + + } + } + + + //This is used in the solveExt1D, solveExtContact + Cm::SpatialVectorV FeatherstoneArticulation::pxcFsGetVelocity(PxU32 linkID) + { + + Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors(); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + + if (!fixBase) + { + deltaV = mArticulationData.mBaseInvSpatialArticulatedInertia * (-deferredZ[0]); + } + + for (ArticulationBitField i = links[linkID].pathToRoot - 1; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = linkData[index]; + ArticulationJointCoreData& tJointDatum = jointData[index]; + PX_ASSERT(links[index].parent < index); + deltaV = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV); + } + + //ArticulationLink& link = mArticulationData.getLink(linkID); + //PxTransform& body2World = link.bodyCore->body2World; + //PxTransform& body2World = mArticulationData.getAccumulatedPoses()[linkID]; + PxTransform& body2World = mArticulationData.getPreTransform(linkID); + Cm::SpatialVectorF vel = mArticulationData.getMotionVelocity(linkID) + deltaV.rotate(body2World); + + return Cm::SpatialVector(vel.bottom, vel.top); + } + + void FeatherstoneArticulation::pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1) + { + //if (mArticulationData.getLink(linkID1).parent == linkID) + //{ + + // Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors(); + + // const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + // ArticulationLink* links = mArticulationData.getLinks(); + // ArticulationLinkData* linkData = mArticulationData.getLinkData(); + // ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + // Cm::SpatialVectorF deltaV = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + + // if (!fixBase) + // { + // deltaV = mArticulationData.mBaseInvSpatialArticulatedInertia * (-deferredZ[0]); + // } + + // for (ArticulationBitField i = links[linkID].pathToRoot - 1; i; i &= (i - 1)) + // { + // //index of child of link h on path to link linkID + // const PxU32 index = ArticulationLowestSetBit(i); + // ArticulationLinkData& tLinkDatum = linkData[index]; + // ArticulationJointCoreData& tJointDatum = jointData[index]; + // PX_ASSERT(links[index].parent < index); + // deltaV = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV); + // } + + // ArticulationLink& link = mArticulationData.getLink(linkID); + // PxTransform& body2World = link.bodyCore->body2World; + // Cm::SpatialVectorF vel = mArticulationData.getMotionVelocity(linkID) + deltaV.rotate(body2World); + + // v0 = Cm::SpatialVector(vel.bottom, vel.top); + + // { + // ArticulationLinkData& tLinkDatum = linkData[linkID1]; + // ArticulationJointCoreData& tJointDatum = jointData[linkID1]; + // //Now do the final step for the child link... + // deltaV = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[linkID1], deltaV); + + // ArticulationLink& link1 = mArticulationData.getLink(linkID1); + // PxTransform& body2World1 = link1.bodyCore->body2World; + // Cm::SpatialVectorF vel1 = mArticulationData.getMotionVelocity(linkID1) + deltaV.rotate(body2World1); + // v1 = Cm::SpatialVector(vel1.bottom, vel1.top); + // } + + //} + //else + { + Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors(); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + + if (!fixBase) + { + deltaV = mArticulationData.mBaseInvSpatialArticulatedInertia * (-deferredZ[0]); + } + + PxU64 common = links[linkID].pathToRoot & links[linkID1].pathToRoot; + PxU64 exclusive0 = links[linkID].pathToRoot ^ common; + PxU64 exclusive1 = links[linkID1].pathToRoot ^ common; + + for (ArticulationBitField i = common - 1; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = linkData[index]; + ArticulationJointCoreData& tJointDatum = jointData[index]; + PX_ASSERT(links[index].parent < index); + deltaV = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV); + } + + Cm::SpatialVectorF deltaV1 = deltaV; + + for (ArticulationBitField i = exclusive0; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = linkData[index]; + ArticulationJointCoreData& tJointDatum = jointData[index]; + PX_ASSERT(links[index].parent < index); + deltaV = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV); + } + + for (ArticulationBitField i = exclusive1; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = linkData[index]; + ArticulationJointCoreData& tJointDatum = jointData[index]; + PX_ASSERT(links[index].parent < index); + deltaV1 = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV1); + } + + + //ArticulationLink& link = mArticulationData.getLink(linkID); + //PxTransform& body2World = link.bodyCore->body2World; + //PxTransform& body2World = mArticulationData.getAccumulatedPoses()[linkID]; + PxTransform& body2World = mArticulationData.getPreTransform(linkID); + Cm::SpatialVectorF vel = mArticulationData.getMotionVelocity(linkID) + deltaV.rotate(body2World); + + v0 = Cm::SpatialVector(vel.bottom, vel.top); + + //ArticulationLink& link1 = mArticulationData.getLink(linkID1); + //PxTransform& body2World1 = link1.bodyCore->body2World; + //PxTransform& body2World1 = mArticulationData.getAccumulatedPoses()[linkID1]; + PxTransform& body2World1 = mArticulationData.getPreTransform(linkID1); + Cm::SpatialVectorF vel1 = mArticulationData.getMotionVelocity(linkID1) + deltaV1.rotate(body2World1); + + v1 = Cm::SpatialVector(vel1.bottom, vel1.top); + } + } + + /*Cm::SpatialVectorV FeatherstoneArticulation::pxcFsGetVelocity(PxU32 linkID) + { + + Cm::SpatialVectorF& vel = mArticulationData.getMotionVelocity(linkID); + + return Cm::SpatialVector(vel.bottom, vel.top); + }*/ + + Cm::SpatialVectorV FeatherstoneArticulation::pxcFsGetVelocityTGS(PxU32 linkID) + { + return getLinkVelocity(linkID); + } + + //This is used in the solveExt1D, solveExtContact + void FeatherstoneArticulation::pxcFsApplyImpulse(PxU32 linkID, + Ps::aos::Vec3V linear, Ps::aos::Vec3V angular, + Cm::SpatialVectorF* /*Z*/, Cm::SpatialVectorF* /*deltaV*/) + { + const ArticulationSolverDesc* desc = &mSolverDesc; + + ArticulationLink* links = static_cast(desc->links); + + //initialize all zero acceration impulse to be zero + ArticulationData& data = mArticulationData; + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + Cm::SpatialVectorF* deferredZ = data.getSpatialZAVectors(); + + //This will be zero at the begining of the frame + //PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + + //Cm::SpatialVectorF* motionVelocity = data.getMotionVelocities(); + + data.mJointDirty = true; + + //set all zero acceleration forces to be zero + //PxMemZero(Z, sizeof(Cm::SpatialVectorF) * desc->linkCount); + + //impulse is in world space + Cm::SpatialVector impulse; + V3StoreU(angular, impulse.angular); + V3StoreU(linear, impulse.linear); + + //transform p(impluse) from world space to the local space of link + //ArticulationLink& collidedlink = links[linkID]; + //PxTransform& body2World = collidedlink.bodyCore->body2World; + //PxTransform& body2World = data.getAccumulatedPoses()[linkID]; + PxTransform& body2World = data.getPreTransform(linkID); + + Cm::SpatialVectorF Z0(-body2World.rotateInv(impulse.linear), -body2World.rotateInv(impulse.angular)); + deferredZ[linkID] += Z0; + + for (PxU32 i = linkID; i; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& jointDatum = jointData[i]; + Z0 = propagateImpulse(tLinkDatum, jointDatum, Z0); + deferredZ[tLink.parent] += Z0; + } + + } + + void FeatherstoneArticulation::pxcFsApplyImpulses(Cm::SpatialVectorF* Z) + { + ArticulationLink* links = mArticulationData.getLinks(); + + //initialize all zero acceration impulse to be zero + ArticulationData& data = mArticulationData; + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + Cm::SpatialVectorF* deferredZ = data.getSpatialZAVectors(); + + const PxU32 startIndex = PxU32(linkCount - 1); + + data.mJointDirty = true; + + for (PxU32 linkID = startIndex; linkID > 0; --linkID) + { + ArticulationLink& tLink = links[linkID]; + ArticulationLinkData& tLinkDatum = linkData[linkID]; + ArticulationJointCoreData& jointDatum = jointData[linkID]; + + Z[tLink.parent] += propagateImpulse(tLinkDatum, jointDatum, Z[linkID]); + deferredZ[linkID] += Z[linkID]; + } + + deferredZ[0] += Z[0]; + + } + + void FeatherstoneArticulation::pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, + const Ps::aos::Vec3V& angular, PxU32 linkID2, const Ps::aos::Vec3V& linear2, + const Ps::aos::Vec3V& angular2, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* /*deltaV*/) + { + if (0) + { + pxcFsApplyImpulse(linkID, linear, angular, Z, NULL); + pxcFsApplyImpulse(linkID2, linear2, angular2, Z, NULL); + } + else + { + const ArticulationSolverDesc* desc = &mSolverDesc; + ArticulationData& data = mArticulationData; + data.mJointDirty = true; + Cm::SpatialVectorF* deferredZ = data.getSpatialZAVectors(); + ArticulationLink* links = static_cast(desc->links); + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + //impulse is in world space + Cm::SpatialVector impulse0; + V3StoreU(angular, impulse0.angular); + V3StoreU(linear, impulse0.linear); + + Cm::SpatialVector impulse1; + V3StoreU(angular2, impulse1.angular); + V3StoreU(linear2, impulse1.linear); + + //transform p(impluse) from world space to the local space of link + //ArticulationLink& collidedlink = links[linkID]; + //ArticulationLink& collidedlink2 = links[linkID2]; + /*PxTransform& body2World = collidedlink.bodyCore->body2World; + PxTransform& body2World2 = collidedlink2.bodyCore->body2World;*/ + + /*PxTransform& body2World = data.getAccumulatedPoses()[linkID]; + PxTransform& body2World2 = data.getAccumulatedPoses()[linkID2];*/ + + PxTransform& body2World = data.getPreTransform(linkID); + PxTransform& body2World2 = data.getPreTransform(linkID2); + + + + PxU64 commonId = links[linkID].pathToRoot & links[linkID2].pathToRoot; + PxU32 commonLink = Dy::ArticulationHighestSetBit(commonId); //Now, work from one to that common, then the other to that common, then go from there upwards... + + Cm::SpatialVectorF Z1 = Cm::SpatialVectorF(-body2World.rotateInv(impulse0.linear), -body2World.rotateInv(impulse0.angular)); + Cm::SpatialVectorF Z2 = Cm::SpatialVectorF(-body2World2.rotateInv(impulse1.linear), -body2World2.rotateInv(impulse1.angular)); + + Z[linkID2] = Z2; + deferredZ[linkID2] += Z2; + + for (PxU32 i = linkID2; i != commonLink; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& jointDatum = jointData[i]; + Z2 = propagateImpulse(tLinkDatum, jointDatum, Z2); + Z[tLink.parent] = Z2; + deferredZ[tLink.parent] += Z2; + } + + Z[linkID] = Z1; + deferredZ[linkID] += Z1; + + for (PxU32 i = linkID; i != commonLink; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& jointDatum = jointData[i]; + Z1 = propagateImpulse(tLinkDatum, jointDatum, Z1); + Z[tLink.parent] = Z1; + deferredZ[tLink.parent] += Z1; + } + + Z[commonLink] = Z1 + Z2; + + for (PxU32 i = commonLink; i; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& jointDatum = jointData[i]; + Z[tLink.parent] = propagateImpulse(tLinkDatum, jointDatum, Z[i]); + deferredZ[tLink.parent] += Z[tLink.parent]; + } + } + } + + //Z is the link space(drag force) + void FeatherstoneArticulation::applyImpulses(Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) + { + ArticulationLink* links = mArticulationData.getLinks(); + + //initialize all zero acceration impulse to be zero + ArticulationData& data = mArticulationData; + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + const PxU32 startIndex = PxU32(linkCount - 1); + + for (PxU32 linkID = startIndex; linkID > 0; --linkID) + { + ArticulationLink& tLink = links[linkID]; + ArticulationLinkData& tLinkDatum = linkData[linkID]; + ArticulationJointCoreData& jointDatum = jointData[linkID]; + + Z[tLink.parent] += propagateImpulse(tLinkDatum, jointDatum, Z[linkID]); + } + + getDeltaV(Z, deltaV); + } + + void FeatherstoneArticulation::getDeltaV(Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) + { + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + Cm::SpatialVectorF* motionVelocities = mArticulationData.getMotionVelocities(); + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + //This will be zero at the begining of the frame + PxReal* jointDeltaVelocities = mArticulationData.getJointDeltaVelocities(); + + if (fixBase) + { + deltaV[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + else + { + ArticulationLink& link = links[0]; + + deltaV[0] = mArticulationData.mBaseInvSpatialArticulatedInertia * (-Z[0]); + const PxTransform& body2World0 = link.bodyCore->body2World; + motionVelocities[0] += deltaV[0].rotate(body2World0); + + PX_ASSERT(motionVelocities[0].isFinite()); + } + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + for (PxU32 i = 1; i < linkCount; i++) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& tJointDatum = jointData[i]; + Cm::SpatialVectorF dV = propagateVelocity(tLinkDatum, tJointDatum, Z[i], + &jointDeltaVelocities[tJointDatum.jointOffset], deltaV[tLink.parent]); + + deltaV[i] = dV; + const PxTransform& tBody2World = tLink.bodyCore->body2World; + + motionVelocities[i] += dV.rotate(tBody2World); + + PX_ASSERT(motionVelocities[i].isFinite()); + } + } + + PxQuat computeSphericalJointPositions(ArticulationJointCore* joint, + const PxQuat newRot, const PxQuat pBody2WorldRot, + PxReal* jPositions); + + PxTransform FeatherstoneArticulation::propagateTransform(const PxU32 linkID, ArticulationLink* links, + ArticulationJointCoreData& jointDatum, Cm::SpatialVectorF* motionVelocities, const PxReal dt, const PxTransform& pBody2World, + const PxTransform& currentTransform, PxReal* jointVelocities, PxReal* jointDeltaVelocities, PxReal* jointPositions) + { + ArticulationLink& link = links[linkID]; + + ArticulationJointCore* joint = link.inboundJoint; + + PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + PxReal* jDeltaVelocity = &jointDeltaVelocities[jointDatum.jointOffset]; + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + + PxQuat newParentToChild; + PxQuat newWorldQ; + PxVec3 r; + + const PxVec3 childOffset = -joint->childPose.p; + const PxVec3 parentOffset = joint->parentPose.p; + + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + PxReal tJointPosition = jPosition[0] + (jVelocity[0] + jDeltaVelocity[0]) * dt; + + if (link.inboundJoint->prismaticLimited) + { + if (tJointPosition < (link.inboundJoint->limits[link.inboundJoint->dofIds[0]].low)) + tJointPosition = link.inboundJoint->limits[link.inboundJoint->dofIds[0]].low; + if (tJointPosition >(link.inboundJoint->limits[link.inboundJoint->dofIds[0]].high)) + tJointPosition = link.inboundJoint->limits[link.inboundJoint->dofIds[0]].high; + } + + jPosition[0] = tJointPosition; + jVelocity[0] += jDeltaVelocity[0]; + jDeltaVelocity[0] = 0.f; + + + + + newParentToChild = joint->relativeQuat; + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + r = e + d + jointDatum.motionMatrix[0].bottom * tJointPosition; + break; + } + case PxArticulationJointType::eREVOLUTE: + { + PxReal tJointPosition = jPosition[0] + (jVelocity[0] + jDeltaVelocity[0]) * dt; + + if (link.inboundJoint->twistLimited) + { + if (tJointPosition < (link.inboundJoint->limits[link.inboundJoint->dofIds[0]].low)) + tJointPosition = link.inboundJoint->limits[link.inboundJoint->dofIds[0]].low; + if (tJointPosition >(link.inboundJoint->limits[link.inboundJoint->dofIds[0]].high)) + tJointPosition = link.inboundJoint->limits[link.inboundJoint->dofIds[0]].high; + } + else + { + if (tJointPosition > PxTwoPi) + tJointPosition -= 2.f*PxTwoPi; + else if (tJointPosition < -PxTwoPi) + tJointPosition += 2.f*PxTwoPi; + + tJointPosition = PxClamp(tJointPosition, -2.f*PxTwoPi, 2.f*PxTwoPi); + } + + jPosition[0] = tJointPosition; + jVelocity[0] += jDeltaVelocity[0]; + jDeltaVelocity[0] = 0.f; + + const PxVec3& u = jointDatum.motionMatrix[0].top; + + PxQuat jointRotation = PxQuat(-tJointPosition, u); + if (jointRotation.w < 0) //shortest angle. + jointRotation = -jointRotation; + + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + if (jointDatum.dof < 3) + { + PxQuat jointRotation(PxIdentity); + for (PxU32 i = 0; i < jointDatum.dof; ++i) + { + const PxReal delta = (jVelocity[i] + jDeltaVelocity[i]) * dt; + + jVelocity[i] += jDeltaVelocity[i]; + + PxReal jPos = jPosition[i] + delta; + //jPosition[i] += delta; + if (jPos > PxTwoPi) + jPos -= 2.f*PxTwoPi; + else if (jPos < -PxTwoPi) + jPos += 2.f*PxTwoPi; + + jPos = PxClamp(jPos, -2.f*PxTwoPi, 2.f*PxTwoPi); + + jPosition[i] = jPos; + + jDeltaVelocity[i] = 0.f; + + const PxVec3& u = jointDatum.motionMatrix[i].top; + + PxQuat jRotation = PxQuat(-jPosition[i], u); + if (jRotation.w < 0) //shortest angle. + jRotation = -jRotation; + + jointRotation = jointRotation * jRotation; + + + } + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + + } + else + { + + + PxVec3 worldAngVel = motionVelocities[linkID].top; + + newWorldQ = Ps::exp(worldAngVel*dt) * currentTransform.q; + + newParentToChild = computeSphericalJointPositions(joint, newWorldQ, + pBody2World.q, jPosition); + + PxQuat newQ = (pBody2World.q * newParentToChild.getConjugate()).getNormalized(); + + const PxQuat cB2w = newQ * joint->childPose.q; + + const PxMat33 cB2w_m(cB2w); + + PxVec3 axis0 = cB2w_m.column0; + PxVec3 axis1 = cB2w_m.column1; + PxVec3 axis2 = cB2w_m.column2; + + PxVec3 relAngVel = worldAngVel - motionVelocities[link.parent].top; + + PxU32 dofIdx = 0; + if (joint->motion[PxArticulationAxis::eTWIST] != PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis0.dot(relAngVel); + if (joint->motion[PxArticulationAxis::eSWING1] != PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis1.dot(relAngVel); + if (joint->motion[PxArticulationAxis::eSWING2] != PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis2.dot(relAngVel); + if (joint->motion[PxArticulationAxis::eTWIST] == PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis0.dot(relAngVel); + if (joint->motion[PxArticulationAxis::eSWING1] == PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis1.dot(relAngVel); + if (joint->motion[PxArticulationAxis::eSWING2] == PxArticulationMotion::eLOCKED) + jVelocity[dofIdx++] = axis2.dot(relAngVel); + } + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + break; + } + case PxArticulationJointType::eFIX: + { + //this is fix joint so joint don't have velocity + newParentToChild = joint->relativeQuat; + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + r = e + d; + break; + } + default: + break; + } + + PxTransform cBody2World; + cBody2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized(); + cBody2World.p = pBody2World.p + cBody2World.q.rotate(r); + + PX_ASSERT(cBody2World.isSane()); + + return cBody2World; + } + + + const PxTransform& FeatherstoneArticulation::getCurrentTransform(PxU32 linkID) const + { + return mArticulationData.mAccumulatedPoses[linkID]; + } + + const PxQuat& FeatherstoneArticulation::getDeltaQ(PxU32 linkID) const + { + return mArticulationData.mDeltaQ[linkID]; + } + + //Z is the spatial acceleration impulse of links[linkID] + Cm::SpatialVectorF FeatherstoneArticulation::propagateVelocity(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, + const Cm::SpatialVectorF& Z, PxReal* jointVelocity, const Cm::SpatialVectorF& hDeltaV) + { + + Cm::SpatialVectorF pDeltaV = linkDatum.childToParent.transposeTransform(hDeltaV); //parent velocity change + + Cm::SpatialVectorF temp = linkDatum.spatialArticulatedInertia * pDeltaV + Z; + + PxReal tJointDelta[6]; + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + tJointDelta[ind] = -sa.innerProduct(temp); + } + + Cm::SpatialVectorF jointSpatialDeltaV(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + PxReal jDelta = 0.f; + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + jDelta += linkDatum.invStIs[ind2][ind] * tJointDelta[ind2]; + } + + jointVelocity[ind] += jDelta; + //PX_ASSERT(PxAbs(jointVelocity[ind]) < 500.f); + + + const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + jointSpatialDeltaV += sa * jDelta; + } + + return pDeltaV + jointSpatialDeltaV; + } + + + //This method calculate the velocity change due to collision/constraint impulse + Cm::SpatialVectorF FeatherstoneArticulation::propagateVelocityTestImpulse(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z, + const Cm::SpatialVectorF& hDeltaV) + { + + const SpatialTransform& c2p = linkDatum.childToParent; + Cm::SpatialVectorF pDeltaV = c2p.transposeTransform(hDeltaV); //parent velocity change + + Cm::SpatialVectorF temp = linkDatum.spatialArticulatedInertia * pDeltaV + Z; + + PxReal tJointDelta[6]; + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + tJointDelta[ind] = -sa.innerProduct(temp); + } + + Cm::SpatialVectorF jointSpatialDeltaV(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + PxReal jDelta = 0.f; + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + jDelta += linkDatum.invStIs[ind2][ind] * tJointDelta[ind2]; + } + + const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + jointSpatialDeltaV += sa * jDelta; + } + + return pDeltaV + jointSpatialDeltaV; + } + + //PX_FORCE_INLINE Cm::SpatialVectorF ComputeDeltaVelocity(const ArticulationLinkData& linkDatum, + // const ArticulationJointCoreData& jointDatum, Cm::SpatialVectorF& hDeltaV) + //{ + // const SpatialTransform& c2p = linkDatum.childToParent; + // Cm::SpatialVectorF pDeltaV = c2p.transposeTransform(hDeltaV); //parent velocity change + // Cm::SpatialVectorF temp = linkDatum.spatialArticulatedInertia * pDeltaV; + // PxReal tJointDelta[6]; + // for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + // { + // const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + // tJointDelta[ind] = -sa.innerProduct(temp); + // } + + // Cm::SpatialVectorF jointSpatialDeltaV(PxVec3(0.f), PxVec3(0.f)); + + // for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + // { + // PxReal jDelta = 0.f; + // for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + // { + // jDelta += linkDatum.invStIs[ind2][ind] * tJointDelta[ind2]; + // } + + // const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + // jointSpatialDeltaV += sa * jDelta; + // } + + // return pDeltaV + jointSpatialDeltaV; + //} + + Cm::SpatialVectorF FeatherstoneArticulation::propagateImpulse(const ArticulationLinkData& linkDatum, + const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z) + { + + Cm::SpatialVectorF temp(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + const PxReal stZ = sa.innerProduct(Z); + temp += linkDatum.IsInvD[ind] * stZ; + } + + //parent space's spatial zero acceleration impulse + return linkDatum.childToParent * (Z - temp); + } + + Cm::SpatialVectorF FeatherstoneArticulation::getDeltaVWithDeltaJV(const bool fixBase, const PxU32 linkID, + const ArticulationData& data, Cm::SpatialVectorF* Z, + PxReal* jointVelocities) + { + + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero(); + if (!fixBase) + { + //velocity change + //SpatialMatrix inverseArticulatedInertia = hLinkDatum.spatialArticulatedInertia.getInverse(); + const SpatialMatrix& inverseArticulatedInertia = data.mBaseInvSpatialArticulatedInertia; + deltaV = inverseArticulatedInertia * (-Z[0]); + } + + for (ArticulationBitField i = data.getLink(linkID).pathToRoot - 1; i; i &= (i - 1)) + { + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = data.getLinkData(index); + ArticulationJointCoreData& tJointDatum = data.getJointData(index); + PxReal* jVelocity = &jointVelocities[tJointDatum.jointOffset]; + deltaV = FeatherstoneArticulation::propagateVelocity(tLinkDatum, tJointDatum, Z[index], jVelocity, deltaV); + } + + return deltaV; + } + + Cm::SpatialVectorF FeatherstoneArticulation::getDeltaV(const bool fixBase, const PxU32 linkID, + const ArticulationData& data, Cm::SpatialVectorF* Z) + { + + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero(); + if (!fixBase) + { + //velocity change + //SpatialMatrix inverseArticulatedInertia = hLinkDatum.spatialArticulatedInertia.getInverse(); + const SpatialMatrix& inverseArticulatedInertia = data.mBaseInvSpatialArticulatedInertia; + deltaV = inverseArticulatedInertia * (-Z[0]); + } + + for (ArticulationBitField i = data.getLink(linkID).pathToRoot - 1; i; i &= (i - 1)) + { + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = data.getLinkData(index); + ArticulationJointCoreData& tJointDatum = data.getJointData(index); + deltaV = FeatherstoneArticulation::propagateVelocityTestImpulse(tLinkDatum, tJointDatum, Z[index], deltaV); + } + + return deltaV; + } + + void FeatherstoneArticulation::getZ(const PxU32 linkID, + const ArticulationData& data, Cm::SpatialVectorF* Z, + const Cm::SpatialVectorF& impulse) + { + ArticulationLink* links = data.getLinks(); + + //impulse need to be in linkID space!!! + Z[linkID] = -impulse; + + for (PxU32 i = linkID; i; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + const ArticulationLinkData& tLinkDatum = data.getLinkData(i); + const ArticulationJointCoreData& tJointDatum = data.getJointData(i); + Z[tLink.parent] = FeatherstoneArticulation::propagateImpulse(tLinkDatum, tJointDatum, Z[i]); + } + } + + //This method use in impulse self response. The input impulse is in the link space + Cm::SpatialVectorF FeatherstoneArticulation::getImpulseResponse( + const PxU32 linkID, + const bool fixBase, + const ArticulationData& data, + Cm::SpatialVectorF* Z, + const Cm::SpatialVectorF& impulse) + { + getZ(linkID, data, Z, impulse); + + return getDeltaV(fixBase, linkID, data, Z); + } + + + //This method use in impulse self response. The input impulse is in the link space + Cm::SpatialVectorF FeatherstoneArticulation::getImpulseResponseWithJ( + const PxU32 linkID, + const bool fixBase, + const ArticulationData& data, + Cm::SpatialVectorF* Z, + const Cm::SpatialVectorF& impulse, + PxReal* jointVelocites) + { + getZ(linkID, data, Z, impulse); + + return getDeltaVWithDeltaJV(fixBase, linkID, data, Z, jointVelocites); + } + + void FeatherstoneArticulation::saveVelocity(const ArticulationSolverDesc& d, Cm::SpatialVectorF* deltaV) + { + + FeatherstoneArticulation* arti = static_cast(d.articulation); + ArticulationData& data = arti->mArticulationData; + + //update all links' motion velocity, joint delta velocity if there are contacts/constraints + if (data.mJointDirty) + PxcFsFlushVelocity(*arti, deltaV); + + const PxU32 linkCount = data.getLinkCount(); + //copy motion velocites + Cm::SpatialVectorF* vels = data.getMotionVelocities(); + Cm::SpatialVectorF* posVels = data.getPosIterMotionVelocities(); + PxMemCopy(posVels, vels, sizeof(Cm::SpatialVectorF) * linkCount); + + //copy joint velocities + const PxU32 dofs = data.getDofs(); + + PxReal* jPosDeltaVels = data.getPosIterJointDeltaVelocities(); + + PxReal* jDeltaVels = data.getJointDeltaVelocities(); + + PxMemCopy(jPosDeltaVels, jDeltaVels, sizeof(PxReal) * dofs); + + /* for (PxU32 i = 0; i < dofs; ++i) + { + PX_ASSERT(PxAbs(jPosDeltaVels[i]) < 30.f); + }*/ + + + } + + void FeatherstoneArticulation::saveVelocityTGS(const ArticulationSolverDesc& d, PxReal invDtF32) + { + PX_UNUSED(d); + PX_UNUSED(invDtF32); + } + + + void FeatherstoneArticulation::getImpulseSelfResponse( + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1) const + { + FeatherstoneArticulation::getImpulseSelfResponse(mArticulationData.getLinks(), !!(mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE), + Z, mArticulationData, linkID0, reinterpret_cast(impulse0), + reinterpret_cast(deltaV0), linkID1, reinterpret_cast(impulse1), + reinterpret_cast(deltaV1)); + } + + + void getImpulseResponseSlow(Dy::ArticulationLink* links, + const ArticulationData& data, + PxU32 linkID0_, + const Cm::SpatialVector& impulse0, + Cm::SpatialVector& deltaV0, + PxU32 linkID1_, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV1) + { + + PxU32 stack[DY_ARTICULATION_MAX_SIZE]; + Cm::SpatialVectorF Z[DY_ARTICULATION_MAX_SIZE]; + //Cm::SpatialVectorF ZZV[DY_ARTICULATION_MAX_SIZE]; + + PxU32 i0, i1, ic; + + PxU32 linkID0 = linkID0_; + PxU32 linkID1 = linkID1_; + + + const PxTransform& transform0 = data.getLink(linkID0_).bodyCore->body2World; + const PxTransform& transform1 = data.getLink(linkID1_).bodyCore->body2World; + + for (i0 = linkID0, i1 = linkID1; i0 != i1;) // find common path + { + if (i0flags & PxArticulationFlag::eFIX_BASE) + { + Z[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + + //SpatialMatrix inverseArticulatedInertia = data.getLinkData(0).spatialArticulatedInertia.getInverse(); + const SpatialMatrix& inverseArticulatedInertia = data.getBaseInvSpatialArticulatedInertia(); + Cm::SpatialVectorF v = inverseArticulatedInertia * (-Z[0]); + + for (PxU32 index = ic; (index--) > i1;) + v = FeatherstoneArticulation::propagateVelocityTestImpulse(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], v); + + + Cm::SpatialVectorF dv1 = v; + for (PxU32 index = i1; (index--) > i0;) + dv1 = FeatherstoneArticulation::propagateVelocityTestImpulse(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], dv1); + + Cm::SpatialVectorF dv0= v; + for (PxU32 index = i0; (index--) > 0;) + dv0 = FeatherstoneArticulation::propagateVelocityTestImpulse(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], dv0); + + deltaV0.linear = transform0.rotate(dv0.bottom); + deltaV0.angular = transform0.rotate(dv0.top); + + deltaV1.linear = transform1.rotate(dv1.bottom); + deltaV1.angular = transform1.rotate(dv1.top); + + } + + + void FeatherstoneArticulation::getImpulseSelfResponse(ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + const ArticulationData& data, + PxU32 linkID0, + const Cm::SpatialVectorV& impulse0, + Cm::SpatialVectorV& deltaV0, + PxU32 linkID1, + const Cm::SpatialVectorV& impulse1, + Cm::SpatialVectorV& deltaV1) + { +#if 0 + ArticulationLink& pLink = links[linkID0]; + ArticulationLink& cLink = links[linkID1]; + + PxTransform& pBody2World = pLink.bodyCore->body2World; + PxTransform& cBody2World = cLink.bodyCore->body2World; + + Cm::SpatialVectorF pImpulse = Cm::SpatialVectorF(pBody2World.rotateInv(reinterpret_cast(impulse0.linear)), pBody2World.rotateInv(reinterpret_cast(impulse0.angular))); + Cm::SpatialVectorF cImpulse = Cm::SpatialVectorF(cBody2World.rotateInv(reinterpret_cast(impulse1.linear)), cBody2World.rotateInv(reinterpret_cast(impulse1.angular))); + + Cm::SpatialVectorF resp0 = FeatherstoneArticulation::getImpulseResponse(linkID0, fixBase, data, Z, pImpulse).rotate(pBody2World); + Cm::SpatialVectorF resp1 = FeatherstoneArticulation::getImpulseResponse(linkID1, fixBase, data, Z, cImpulse).rotate(cBody2World); + + deltaV0 = Cm::SpatialVector(resp0.bottom, resp0.top); + deltaV1 = Cm::SpatialVector(resp1.bottom, resp1.top); +#else + + ArticulationLink& link = links[linkID1]; + ArticulationLinkData& linkDatum = data.getLinkData(linkID1); + ArticulationJointCoreData& jointDatum = data.getJointData(linkID1); + if (link.parent == linkID0) + { + PX_ASSERT(linkID0 == link.parent); + PX_ASSERT(linkID0 < linkID1); + + ArticulationLink& pLink = links[linkID0]; + //impulse is in world space + Cm::SpatialVector imp1; + V3StoreU(impulse1.angular, imp1.angular); + V3StoreU(impulse1.linear, imp1.linear); + + Cm::SpatialVector imp0; + V3StoreU(impulse0.angular, imp0.angular); + V3StoreU(impulse0.linear, imp0.linear); + + PxTransform& pBody2World = pLink.bodyCore->body2World; + + Cm::SpatialVectorF pImpulse = Cm::SpatialVectorF(pBody2World.rotateInv(imp0.linear), pBody2World.rotateInv(imp0.angular)); + + PX_ASSERT(linkID0 == link.parent); + + PxTransform& body2World = link.bodyCore->body2World; + + //initialize child link spatial zero acceleration impulse + Cm::SpatialVectorF Z1 = Cm::SpatialVectorF(-body2World.rotateInv(imp1.linear), -body2World.rotateInv(imp1.angular)); + //this calculate parent link spatial zero acceleration impulse + Cm::SpatialVectorF Z0 = FeatherstoneArticulation::propagateImpulse(linkDatum, jointDatum, Z1); + + //in parent space + const Cm::SpatialVectorF impulseDif = pImpulse - Z0; + + Cm::SpatialVectorF delV0(PxVec3(0.f), PxVec3(0.f)); + Cm::SpatialVectorF delV1(PxVec3(0.f), PxVec3(0.f)); + + //calculate velocity change start from the parent link to the root + delV0 = FeatherstoneArticulation::getImpulseResponse(linkID0, fixBase, data, Z, impulseDif); + + //calculate velocity change for child link + delV1 = FeatherstoneArticulation::propagateVelocityTestImpulse(linkDatum, jointDatum, Z1, delV0); + + //translate delV0 and delV1 into world space again + const PxVec3 lin0 = pBody2World.rotate(delV0.bottom); + const PxVec3 ang0 = pBody2World.rotate(delV0.top); + const PxVec3 lin1 = body2World.rotate(delV1.bottom); + const PxVec3 ang1 = body2World.rotate(delV1.top); + + deltaV0.linear = V3LoadU(lin0); + deltaV0.angular = V3LoadU(ang0); + deltaV1.linear = V3LoadU(lin1); + deltaV1.angular = V3LoadU(ang1); + } + else + { + getImpulseResponseSlow(links, data, linkID0, reinterpret_cast(impulse0), + reinterpret_cast(deltaV0), linkID1, + reinterpret_cast(impulse1), reinterpret_cast(deltaV1)); + } +#endif + } + + void FeatherstoneArticulation::createHardLimit( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt) + { + init(s, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); + + + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkIndex].parent, Cm::SpatialVector(PxVec3(0), axis), s.deltaVA, + linkIndex, Cm::SpatialVector(PxVec3(0), -axis), s.deltaVB); + + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if (unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, joint limit ignored"); + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + s.constant = recipResponse * -err * recipDt; + s.unbiasedConstant = err>0.0f ? s.constant : 0.0f; + s.velMultiplier = -recipResponse; + s.impulseMultiplier = 1.0f; + } + + + void FeatherstoneArticulation::createHardLimits( + SolverConstraint1DExt& s0, + SolverConstraint1DExt& s1, + const PxVec3& axis, + PxReal err0, + PxReal err1, + PxReal recipDt, + const Cm::SpatialVectorV& deltaVA, + const Cm::SpatialVectorV& deltaVB, + PxReal recipResponse) + { + init(s0, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); + init(s1, PxVec3(0), PxVec3(0), -axis, -axis, 0, PX_MAX_F32); + + s0.deltaVA = deltaVA; + s0.deltaVB = deltaVB; + s1.deltaVA = -deltaVA; + s1.deltaVB = -deltaVB; + + s0.constant = recipResponse * -err0 * recipDt; + s0.unbiasedConstant = err0>0.0f ? s0.constant : 0.0f; + s0.velMultiplier = -recipResponse; + s0.impulseMultiplier = 1.0f; + + s1.constant = recipResponse * -err1 * recipDt; + s1.unbiasedConstant = err1>0.0f ? s1.constant : 0.0f; + s1.velMultiplier = -recipResponse; + s1.impulseMultiplier = 1.0f; + } + + + void FeatherstoneArticulation::createTangentialSpring( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExt& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt) + { + init(s, PxVec3(0), PxVec3(0), axis, axis, -PX_MAX_F32, PX_MAX_F32); + + Cm::SpatialVector axis6(PxVec3(0), axis); + PxU32 parent = links[linkIndex].parent; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, parent, axis6, s.deltaVA, linkIndex, -axis6, s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if (unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, tangential spring ignored"); + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + // this is a specialization of the spring code in setSolverConstants() for acceleration springs. + // general case is b = dt * (c.mods.spring.damping * c.velocityTarget - c.mods.spring.stiffness * geomError); + // but geomError and velocityTarget are both zero + + const PxReal a = dt * dt * stiffness + dt * damping; + const PxReal x = 1.0f / (1.0f + a); + s.constant = s.unbiasedConstant = 0.0f; + s.velMultiplier = -x * recipResponse * a; + s.impulseMultiplier = 1.0f - x; + } + + void createDriveOrLimit(Dy::SolverConstraint1DExt& c, PxReal bias, const PxReal minImpulse, const PxReal maxImpulse, + bool keepBias, bool isLimit, const Cm::SpatialVectorV& deltaVA, const Cm::SpatialVectorV& deltaVB, const PxReal recipResponse) + { + c.deltaVA = deltaVA; + c.deltaVB = deltaVB; + + c.minImpulse = minImpulse; + c.maxImpulse = maxImpulse; + + c.velMultiplier = -recipResponse; + c.impulseMultiplier = 1.0f; + + { + // see usage of 'for internal use' in preprocessRows() + c.constant = recipResponse * (-bias); + if (!keepBias && (!isLimit || bias < 0.f)) + c.unbiasedConstant = 0.f; + else + c.unbiasedConstant = c.constant; + } + c.appliedForce = 0.f; + c.flags = 0; + c.ang0Writeback = c.ang0; + } + + + void createSpringDrive(Dy::SolverConstraint1DExt& c, PxReal error, PxReal targetVelocity, PxReal maxImpulse, + PxReal stiffness, PxReal damping, PxReal dt, const Cm::SpatialVectorV& deltaVA, const Cm::SpatialVectorV& deltaVB, + const PxReal unitResponse) + { + c.deltaVA = deltaVA; + c.deltaVB = deltaVB; + + PxReal a = dt * dt * stiffness + dt * damping; + PxReal b = dt * (damping * targetVelocity - stiffness * error); + + PxReal x = 1.0f / (1.0f + a*unitResponse); + c.constant = c.unbiasedConstant = x * b; + c.velMultiplier = -x*a; + c.impulseMultiplier = 1.0f - x; + + c.minImpulse = -maxImpulse; + c.maxImpulse = maxImpulse; + + c.appliedForce = 0.f; + c.flags = 0; + c.ang0Writeback = c.ang0; + } + + void createSpringDrive(Dy::SolverConstraint1DExtStep& c, PxReal error, PxReal targetVelocity, PxReal maxImpulse, + PxReal stiffness, PxReal damping, PxReal dt, PxReal totalDt, const Cm::SpatialVectorV& deltaVA, const Cm::SpatialVectorV& deltaVB, + PxReal unitResponse) + { + PX_UNUSED(dt); + + c.deltaVA = deltaVA; + c.deltaVB = deltaVB; + + PxReal a2 = totalDt * (totalDt*stiffness + damping); + PxReal b = totalDt * (damping * targetVelocity - stiffness * error); + + PxReal x = 1.0f / (1.0f + a2*unitResponse); + + c.velTarget = x * b * unitResponse; + c.velMultiplier = -x*a2 * unitResponse; + c.biasScale = 0.f;//c.velMultiplier / dt; + c.impulseMultiplier = 1.0f -x; //KS - add back in -x term? + + c.minImpulse = -maxImpulse; + c.maxImpulse = maxImpulse; + c.recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.f / unitResponse : 0.f; + c.error = 0.f; + c.maxBias = 100.f; + c.angularErrorScale = 1.f; + + c.appliedForce = 0.f; + c.flags = 0; + } + + + void createDriveOrLimit(Dy::SolverConstraint1DExtStep& c, PxReal error, const PxReal minImpulse, const PxReal maxImpulse, + bool keepBias, PxReal maxBias, const PxReal velTarget, const PxReal biasCoefficient, bool isLimit, + const Cm::SpatialVectorV& deltaVA, const Cm::SpatialVectorV& deltaVB, PxReal recipResponse) + { + c.deltaVA = deltaVA; + c.deltaVB = deltaVB; + + c.minImpulse = minImpulse; + c.maxImpulse = maxImpulse; + + c.velMultiplier = -1.f; + c.impulseMultiplier = 1.0f; + + c.error = error; + c.velTarget = velTarget; + c.biasScale = -biasCoefficient; + c.recipResponse = recipResponse; + c.maxBias = maxBias; + c.appliedForce = 0.f; + + c.flags = PxU32(keepBias || (isLimit && error > 0.f) ? DY_SC_FLAG_KEEP_BIAS | DY_SC_FLAG_INEQUALITY : 0); + c.angularErrorScale = 0.f; + } + + PX_INLINE void computeJacobianAxes(PxVec3 row[3], const PxQuat& qa, const PxQuat& qb) + { + // Compute jacobian matrix for (qa* qb) [[* means conjugate in this expr]] + // d/dt (qa* qb) = 1/2 L(qa*) R(qb) (omega_b - omega_a) + // result is L(qa*) R(qb), where L(q) and R(q) are left/right q multiply matrix + + const PxReal wa = qa.w, wb = qb.w; + const PxVec3 va(qa.x, qa.y, qa.z), vb(qb.x, qb.y, qb.z); + + const PxVec3 c = vb*wa + va*wb; + const PxReal d0 = wa*wb; + const PxReal d1 = va.dot(vb); + const PxReal d = d0 - d1; + + row[0] = (va * vb.x + vb * va.x + PxVec3(d, c.z, -c.y)) * 0.5f; + row[1] = (va * vb.y + vb * va.y + PxVec3(-c.z, d, c.x)) * 0.5f; + row[2] = (va * vb.z + vb * va.z + PxVec3(c.y, -c.x, d)) * 0.5f; + + if ((d0 + d1) != 0.0f) // check if relative rotation is 180 degrees which can lead to singular matrix + return; + else + { + row[0].x += PX_EPS_F32; + row[1].y += PX_EPS_F32; + row[2].z += PX_EPS_F32; + } + } + + void FeatherstoneArticulation::setupInternalConstraints( + ArticulationLink* links, + const PxU32 linkCount, + const bool fixBase, + ArticulationData& data, + Cm::SpatialVectorF* Z, + PxReal dt, + PxReal invDt, + PxReal erp, + bool isTGSSolver) + { + PX_PROFILE_ZONE("Articulations:setupSolverConstraintsInternal", 0); + + const PxConstraintInvMassScale ims(1.0f, 1.0f, 1.0f, 1.0f); + + Cm::SpatialVectorF* jointTransmittedForce = data.getTransmittedForces(); + PxReal* jointPositions = data.getJointPositions(); + + data.mInternalConstraints.forceSize_Unsafe(0); + data.mInternalConstraints.resizeUninitialized(data.getDofs()); + + data.mInternalLockedAxes.forceSize_Unsafe(0); + data.mInternalLockedAxes.resizeUninitialized(data.getLocks()); + + ArticulationInternalConstraint* constraints = data.mInternalConstraints.begin(); + ArticulationInternalLockedAxis* locks = data.mInternalLockedAxes.begin(); + + + for (PxU16 linkID = 1; linkID < linkCount; linkID++) + { + const ArticulationLink& link = links[linkID]; + + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + PX_UNUSED(jPosition); + + const ArticulationLink& pLink = links[link.parent]; + + const ArticulationJointCore& j = *link.inboundJoint; + + //const bool jointDrive = (j.driveType != PxArticulationJointDriveType::eNONE); + + bool hasFriction = j.frictionCoefficient > 0.f; + + const PxReal fCoefficient = j.frictionCoefficient * dt; + const PxReal transmissionForce = jointTransmittedForce[linkID].magnitude() * fCoefficient; + PX_UNUSED(transmissionForce); + + const PxU32 limitedRows = 2u * jointDatum.limitedAxes; + const PxU32 lockedRows = jointDatum.lockedAxes; + + + PxU8 driveRows = 0; + + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)) + driveRows++; + } + + const PxU8 frictionRows = hasFriction ? jointDatum.dof : PxU8(0); + + const PxU8 constraintCount = PxU8(limitedRows + driveRows + frictionRows + lockedRows); + if (!constraintCount) + { + //Skip these constraints... + //constraints += jointDatum.dof; + jointDatum.dofInternalConstraintMask = 0; + continue; + } + + const PxTransform cA2w = pLink.bodyCore->body2World.transform(j.parentPose); + const PxTransform cB2w = link.bodyCore->body2World.transform(j.childPose); + + PxU32 dofId = 0; + + PxU8 dofMask = 0; + for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + dofMask |= (1 << dofId); + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].top); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + /*PxReal unitResponse = unsimdRef(deltaVA).angular.dot(axis) - unsimdRef(deltaVB).angular.dot(axis); + PxReal totalMag = (unsimdRef(deltaVA) - unsimdRef(deltaVB)).magnitude(); + + PxReal ratio = unitResponse / totalMag; + if (ratio < 1e-4f) + unitResponse = 0.f;*/ + + Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); + Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); + + PxReal r0 = deltaV0.angular.dot(axis); + PxReal r1 = deltaV1.angular.dot(axis); + + PxReal unitResponse = r0 - r1; + + //Cm::SpatialVector rem0(deltaV0.linear, deltaV0.angular - (axis * r0)); + //Cm::SpatialVector rem1(deltaV1.linear, deltaV1.angular - (axis*r1)); + + //PxReal rem = (rem0 - rem1).magnitude(); + + ///*if ((unitResponse / rem) < DY_ARTICULATION_BAD_RESPONSE) + // unitResponse = 0.f;*/ + + //unitResponse += rem; + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + //constraints->unitResponse = unitResponse; + constraints->recipResponse = recipResponse; + constraints->response = unitResponse; + constraints->row0 = Cm::SpatialVectorF(PxVec3(0), axis); + constraints->row1 = Cm::SpatialVectorF(PxVec3(0), axis); + constraints->deltaVA.top = unsimdRef(deltaVA).angular; + constraints->deltaVA.bottom = unsimdRef(deltaVA).linear; + constraints->deltaVB.top = unsimdRef(deltaVB).angular; + constraints->deltaVB.bottom = unsimdRef(deltaVB).linear; + constraints->erp = erp; + constraints->isLinearConstraint = false; + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + constraints->lowLimit = j.limits[i].low; + constraints->highLimit = j.limits[i].high; + } + else + { + constraints->lowLimit = -PX_MAX_F32; + constraints->highLimit = PX_MAX_F32; + } + + constraints->lowImpulse = 0.f; + constraints->highImpulse = 0.f; + + constraints->frictionForce = 0.f; + constraints->maxFrictionForce = hasFriction ? transmissionForce/dt : 0.f; + constraints->frictionForceCoefficient = isTGSSolver ? 0.f : 1.f; + + + if (hasDrive) + { + PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + + //KS - clamp drive target within limits - no point in having 2 parts fight against each-other + if (j.motion[i] == PxArticulationMotion::eLIMITED) + targetPos = PxClamp(targetPos, j.limits[i].low, j.limits[i].high); + + PxReal a = dt * dt * j.drives[i].stiffness + dt * j.drives[i].damping; + PxReal b = dt * (j.drives[i].damping * targetVelocity /*+ j.drives[i].stiffness * (targetPos - jointPos)*/); + PxReal x = 0.f; + + if (!j.drives[i].isAcceleration) + { + x = unitResponse > 0.f ? 1.0f / (1.0f + a*unitResponse) : 0.f; + constraints->driveTargetVel = x * b; + constraints->driveBiasCoefficient = x*j.drives[i].stiffness*dt; + constraints->driveVelMultiplier = -x*a; + } + else + { + x = 1.0f / (1.0f + a); + constraints->driveTargetVel = x * b*recipResponse; + constraints->driveBiasCoefficient = x*j.drives[i].stiffness*dt*recipResponse; + constraints->driveVelMultiplier = -x*a*recipResponse; + } + + constraints->driveTarget = targetPos; + constraints->driveImpulseMultiplier = isTGSSolver ? 1.f : 1.0f - x; + constraints->maxDriveForce = j.drives[i].maxForce;// *dt; + constraints->driveForce = 0.f; + } + else + { + constraints->driveTargetVel = 0.f; + constraints->driveTarget = 0.f; + constraints->driveBiasCoefficient = 0.f; + constraints->driveVelMultiplier = 0.f; + constraints->driveImpulseMultiplier = 0.f; + constraints->maxDriveForce = 0.f; + constraints->driveForce = 0.f; + } + constraints++; + } + dofId++; + } + } + + for (PxU32 i = PxArticulationAxis::eX; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + + dofMask |= (1 << dofId); + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].bottom); + const PxVec3 ang0 = (cA2w.p - pLink.bodyCore->body2World.p).cross(axis); + const PxVec3 ang1 = (cB2w.p - link.bodyCore->body2World.p).cross(axis); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(axis, ang0), deltaVA, + linkID, Cm::SpatialVector(-axis, -ang1), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); + Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); + + PxReal r0 = deltaV0.linear.dot(axis) + deltaV0.angular.dot(ang0); + PxReal r1 = deltaV1.linear.dot(axis) + + deltaV1.angular.dot(ang1); + + PxReal unitResponse = r0 - r1; + + //Cm::SpatialVector rem0(deltaV0.linear - axis * r0, deltaV0.angular - (ang0 * r0)); + //Cm::SpatialVector rem1(deltaV1.linear - axis * r1, deltaV1.angular - (ang1*r1)); + + //PxReal rem = (rem0 - rem1).magnitude(); + + ////unitResponse += rem; + + ///*if ((unitResponse / rem) < DY_ARTICULATION_BAD_RESPONSE) + // unitResponse = 0.f;*/ + + //unitResponse += rem; + + /*PxReal unitResponse = unsimdRef(deltaVA).linear.dot(axis) + unsimdRef(deltaVA).angular.dot(ang0) - unsimdRef(deltaVB).linear.dot(axis) - + unsimdRef(deltaVB).angular.dot(ang1); + + PxReal totalMag = (unsimdRef(deltaVA) - unsimdRef(deltaVB)).magnitude(); + + PxReal ratio = unitResponse / totalMag; + if (ratio < 1e-4f) + unitResponse = 0.f;*/ + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + constraints->response = unitResponse; + constraints->recipResponse = recipResponse; + constraints->row0 = Cm::SpatialVectorF(axis, ang0); + constraints->row1 = Cm::SpatialVectorF(axis, ang1); + constraints->deltaVA.top = unsimdRef(deltaVA).angular; + constraints->deltaVA.bottom = unsimdRef(deltaVA).linear; + constraints->deltaVB.top = unsimdRef(deltaVB).angular; + constraints->deltaVB.bottom = unsimdRef(deltaVB).linear; + constraints->erp = erp; + constraints->isLinearConstraint = true; + + constraints->lowImpulse = 0.f; + constraints->highImpulse = 0.f; + + constraints->frictionForce = 0.f; + constraints->maxFrictionForce = hasFriction ? transmissionForce/dt : 0.f; + + constraints->frictionForceCoefficient = isTGSSolver ? 0.f : 1.f; + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + constraints->lowLimit = j.limits[i].low; + constraints->highLimit = j.limits[i].high; + } + else + { + constraints->lowLimit = -PX_MAX_F32; + constraints->highLimit = PX_MAX_F32; + } + + if (hasDrive) + { + PxReal targetVelocity = -jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + + //KS - clamp drive target within limits - no point in having 2 parts fight against each-other + if (j.motion[i] == PxArticulationMotion::eLIMITED) + targetPos = PxClamp(targetPos, j.limits[i].low, j.limits[i].high); + + PxReal a = dt * dt * j.drives[i].stiffness + dt * j.drives[i].damping; + PxReal b = dt * (j.drives[i].damping * targetVelocity /*+ j.drives[i].stiffness * (targetPos - jointPos)*/); + PxReal x = 0.f; + + if (!j.drives[i].isAcceleration) + { + x = unitResponse > 0.f ? 1.0f / (1.0f + a*unitResponse) : 0.f; + constraints->driveTargetVel = x * b; + constraints->driveBiasCoefficient = x*j.drives[i].stiffness*dt; + constraints->driveVelMultiplier = -x*a; + } + else + { + x = 1.0f / (1.0f + a); + constraints->driveTargetVel = x * b*recipResponse; + constraints->driveBiasCoefficient = x*j.drives[i].stiffness*dt*recipResponse; + constraints->driveVelMultiplier = -x*a*recipResponse; + } + + constraints->driveTarget = targetPos; + constraints->driveImpulseMultiplier = isTGSSolver ? 1.f : 1.0f - x; + constraints->maxDriveForce = j.drives[i].maxForce;// *dt; + constraints->driveForce = 0.f; + } + else + { + constraints->driveTargetVel = 0.f; + constraints->driveTarget = 0.f; + constraints->driveBiasCoefficient = 0.f; + constraints->driveVelMultiplier = 0.f; + constraints->driveImpulseMultiplier = 0.f; + constraints->maxDriveForce = 0.f; + constraints->driveForce = 0.f; + } + constraints++; + } + dofId++; + } + } + + if (jointDatum.lockedAxes) + { + const PxQuat qB2qA = cA2w.q.getConjugate() * cB2w.q; + PxVec3 row[3]; + computeJacobianAxes(row, cA2w.q, cB2w.q); + + PxReal err[] = { -qB2qA.x, -qB2qA.y, -qB2qA.z }; + + for (PxU32 i = PxArticulationAxis::eTWIST; i <= PxArticulationAxis::eSWING2; ++i) + { + //Get axis, then add lock constraint... + if (j.motion[i] == PxArticulationMotion::eLOCKED) + { + const PxVec3 axis = row[i]; + PxReal error = err[i]; + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + /*PxReal unitResponse = unsimdRef(deltaVA).angular.dot(axis) - unsimdRef(deltaVB).angular.dot(axis); + + PxReal totalMag = (unsimdRef(deltaVA) - unsimdRef(deltaVB)).magnitude(); + + PxReal ratio = unitResponse / totalMag; + if (ratio < 1e-4f) + unitResponse = 0.f;*/ + + Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); + Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); + + PxReal r0 = deltaV0.angular.dot(axis); + PxReal r1 = deltaV1.angular.dot(axis); + + PxReal unitResponse = r0 - r1; + + //Cm::SpatialVector rem0(deltaV0.linear, deltaV0.angular - (axis * r0)); + //Cm::SpatialVector rem1(deltaV1.linear, deltaV1.angular - (axis * r1)); + + //PxReal rem = (rem0 - rem1).magnitude(); + + //// + ///*if ((unitResponse / rem) < DY_ARTICULATION_BAD_RESPONSE) + // unitResponse = 0.f;*/ + + //unitResponse += rem; + + const PxReal recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + locks->axis = axis; + locks->deltaVA.top = unsimdRef(deltaVA).angular; + locks->deltaVA.bottom = unsimdRef(deltaVA).linear; + locks->deltaVB.top = unsimdRef(deltaVB).angular; + locks->deltaVB.bottom = unsimdRef(deltaVB).linear; + locks->recipResponse = recipResponse; + locks->error = error; + locks->biasScale = invDt*erp;//*0.7f; + locks++; + } + } + } + + jointDatum.dofInternalConstraintMask = dofMask; + } + } + + +#if 1 + + PxU32 FeatherstoneArticulation::setupSolverConstraints( + PxConstraintAllocator& /*allocator*/, + PxSolverConstraintDesc* /*constraintDesc*/, + ArticulationLink* links, + const PxU32 linkCount, + const bool fixBase, + ArticulationData& data, + Cm::SpatialVectorF* Z, + PxU32& acCount) + { + acCount = 0; + + setupInternalConstraints(links, linkCount, fixBase, data, Z, data.getDt(), 1.f / data.getDt(), 1.f, false); + + return 0; + } + +#else + + + PxU32 FeatherstoneArticulation::setupSolverConstraints( + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + ArticulationLink* links, + const PxU32 linkCount, + const bool fixBase, + ArticulationData& data, + Cm::SpatialVectorF* Z, + PxU32& acCount) + { + PX_PROFILE_ZONE("Articulations:setupSolverConstraints", 0); + acCount = 0; + + const PxReal dt = data.getDt(); + PxU32 descCount = 0; + const PxReal recipDt = 1.0f / dt; + + const PxConstraintInvMassScale ims(1.0f, 1.0f, 1.0f, 1.0f); + + Cm::SpatialVectorF* jointTransmittedForce = data.getTransmittedForces(); + PxReal* jointPositions = data.getJointPositions(); + + for (PxU16 linkID = 1; linkID < linkCount; linkID++) + { + const ArticulationLink& link = links[linkID]; + + const ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + PX_UNUSED(jPosition); + + const ArticulationLink& pLink = links[link.parent]; + + const ArticulationJointCore& j = *link.inboundJoint; + + //const bool jointDrive = (j.driveType != PxArticulationJointDriveType::eNONE); + + bool hasFriction = j.frictionCoefficient > 0.f; + + const PxReal fCoefficient = j.frictionCoefficient * dt; + const PxReal transmissionForce = jointTransmittedForce[linkID].magnitude() * fCoefficient; + + const PxU32 limitedRows = 2u * jointDatum.limitedAxes; + + PxU8 lockedAxes = jointDatum.lockedAxes; + + + PxU8 driveRows = 0; + + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)) + driveRows++; + } + + PxU8 frictionRows = hasFriction ? jointDatum.dof : PxU8(0); + + const PxU8 constraintCount = PxU8(limitedRows + driveRows + frictionRows + lockedAxes); + if (!constraintCount) + continue; + + const PxTransform cA2w = pLink.bodyCore->body2World.transform(j.parentPose); + const PxTransform cB2w = link.bodyCore->body2World.transform(j.childPose); + + PxSolverConstraintDesc& desc = constraintDesc[descCount++]; + + desc.articulationA = data.getArticulation(); + desc.linkIndexA = Ps::to16(links[linkID].parent); + desc.articulationALength = Ps::to16(data.getSolverDataSize()); + + desc.articulationB = data.getArticulation(); + desc.linkIndexB = linkID; + desc.articulationBLength = Ps::to16(data.getSolverDataSize()); + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeader) + + sizeof(SolverConstraint1DExt) * constraintCount; + + PX_ASSERT(0 == (constraintLength & 0x0f)); + desc.constraintLengthOver16 = Ps::to16(constraintLength / 16); + + desc.constraint = allocator.reserveConstraintData(constraintLength + 16u); + + desc.writeBack = NULL; + + SolverConstraint1DHeader* header = reinterpret_cast(desc.constraint); + SolverConstraint1DExt* constraints = reinterpret_cast(desc.constraint + sizeof(SolverConstraint1DHeader)); + + init(*header, constraintCount, true, ims); + + PxU32 cIndex = 0; + + + PxU32 dofId = 0; + + for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].top); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + PxReal unitResponse = unsimdRef(deltaVA).angular.dot(axis) - unsimdRef(deltaVB).angular.dot(axis); + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + + if (frictionRows) + { + Dy::SolverConstraint1DExt& frictionRow = constraints[cIndex]; + cIndex++; + + frictionRow.lin0 = PxVec3(0.f); + frictionRow.lin1 = PxVec3(0.f); + frictionRow.ang0 = axis; + frictionRow.ang1 = axis; + + createDriveOrLimit(frictionRow, 0.f, -transmissionForce, transmissionForce, false, false, + deltaVA, deltaVB, recipResponse); + } + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + const PxReal lowLimit = j.limits[i].low; + const PxReal highLimit = j.limits[i].high; + + createHardLimits(constraints[cIndex], constraints[cIndex + 1], -axis, jointPos - lowLimit, + highLimit - jointPos, recipDt, -deltaVA, -deltaVB, recipResponse); + cIndex += 2; + } + + if (hasDrive) + { + PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + const PxReal jointPos = -data.mJointPosition[jointDatum.jointOffset + dofId]; + + Dy::SolverConstraint1DExt& c = constraints[cIndex++]; + + c.lin0 = PxVec3(0); + c.lin1 = PxVec3(0); + c.ang0 = axis; + c.ang1 = axis; + + createSpringDrive(c, jointPos + targetPos, -targetVelocity, j.drives[i].maxForce, + j.drives[i].stiffness, j.drives[i].damping, dt, deltaVA, deltaVB, unitResponse); + } + + + + } + dofId++; + } + + } + + for (PxU32 i = PxArticulationAxis::eX; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + + + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].bottom); + const PxVec3 ang0 = (cA2w.p - pLink.bodyCore->body2World.p).cross(axis); + const PxVec3 ang1 = (cB2w.p - pLink.bodyCore->body2World.p).cross(axis); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(axis, ang0), deltaVA, + linkID, Cm::SpatialVector(-axis, -ang1), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + PxReal unitResponse = unsimdRef(deltaVA).linear.dot(axis) + unsimdRef(deltaVA).angular.dot(ang0) - unsimdRef(deltaVB).linear.dot(axis) - + unsimdRef(deltaVB).angular.dot(ang1); + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + + if (frictionRows) + { + Dy::SolverConstraint1DExt& frictionRow = constraints[cIndex]; + cIndex++; + + frictionRow.lin0 = axis; + frictionRow.lin1 = axis; + frictionRow.ang0 = ang0; + frictionRow.ang1 = ang1; + + createDriveOrLimit(frictionRow, 0.f, -transmissionForce, transmissionForce, false, false, + deltaVA, deltaVB, recipResponse); + } + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + //Do linear limits... + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + const PxReal lowLimit = j.limits[i].low; + const PxReal highLimit = j.limits[i].high; + + Dy::SolverConstraint1DExt* prismaticRows = &constraints[cIndex]; + cIndex += 2; + + prismaticRows[0].lin0 = -axis; + prismaticRows[0].lin1 = -axis; + prismaticRows[0].ang0 = -ang0; + prismaticRows[0].ang1 = -ang1; + prismaticRows[1].lin0 = axis; + prismaticRows[1].lin1 = axis; + prismaticRows[1].ang0 = ang0; + prismaticRows[1].ang1 = ang1; + + createDriveOrLimit(prismaticRows[0], (jointPos - lowLimit)*recipDt, 0.f, PX_MAX_F32, false, true, -deltaVA, -deltaVB, recipResponse); + createDriveOrLimit(prismaticRows[1], (highLimit - jointPos)*recipDt, 0.f, PX_MAX_F32, false, true, deltaVA, deltaVB, recipResponse); + + } + + if (hasDrive) + { + PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + const PxReal jointPos = -data.mJointPosition[jointDatum.jointOffset + dofId]; + + Dy::SolverConstraint1DExt& c = constraints[cIndex++]; + + c.lin0 = axis; + c.lin1 = axis; + c.ang0 = ang0; + c.ang1 = ang1; + + createSpringDrive(c, jointPos + targetPos, -targetVelocity, j.drives[i].maxForce, + j.drives[i].stiffness, j.drives[i].damping, dt, -deltaVA, -deltaVB, unitResponse); + } + + + } + + dofId++; + } + } + + if (lockedAxes) + { + const PxQuat qB2qA = cA2w.q.getConjugate() * cB2w.q; + PxVec3 row[3]; + computeJacobianAxes(row, cA2w.q, cB2w.q); + + PxReal err[] = { -qB2qA.x, -qB2qA.y, -qB2qA.z }; + + for (PxU32 i = PxArticulationAxis::eTWIST; i <= PxArticulationAxis::eSWING2; ++i) + { + //Get axis, then add lock constraint... + if (j.motion[i] == PxArticulationMotion::eLOCKED) + { + const PxVec3 axis = row[i]; + PxReal error = err[i]; + Dy::SolverConstraint1DExt& lockRow = constraints[cIndex]; + cIndex++; + + //const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[i].top); + + lockRow.lin0 = PxVec3(0.f); + lockRow.lin1 = PxVec3(0.f); + lockRow.ang0 = axis; + lockRow.ang1 = axis; + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + PxReal unitResponse = unsimdRef(deltaVA).angular.dot(axis) - unsimdRef(deltaVB).angular.dot(axis); + + const PxReal recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + + createDriveOrLimit(lockRow, error*recipDt, -PX_MAX_F32, PX_MAX_F32, false, false, deltaVA, deltaVB, recipResponse); + } + } + } + + + *(desc.constraint + getConstraintLength(desc)) = 0; + + PX_ASSERT(cIndex == constraintCount); + acCount += constraintCount; + } + + return descCount; + } + +#endif + +#if 0 + + static PxU32 getConstraintLength(const PxTGSSolverConstraintDesc& desc) + { + return PxU32(desc.constraintLengthOver16 << 4); + } + + + PxU32 FeatherstoneArticulation::setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* Z) + { + + PX_PROFILE_ZONE("Articulations:setupSolverConstraintsTGS", 0); + FeatherstoneArticulation* articulation = static_cast(articDesc.articulation); + ArticulationData& data = articulation->mArticulationData; + + const PxU32 solverDataSize = articDesc.solverDataSize; + + PX_UNUSED(dt); + acCount = 0; + + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + const PxU32 linkCount = data.getLinkCount(); + ArticulationLink* links = data.getLinks(); + //ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + PxU32 descCount = 0; + const PxReal recipDt = invDt; + + const PxConstraintInvMassScale ims(1.0f, 1.0f, 1.0f, 1.0f); + + + Cm::SpatialVectorF* jointTransmittedForce = data.getTransmittedForces(); + + //for (PxU16 linkID = PxU16(linkCount-1); linkID>=1; linkID--) + for (PxU16 linkID = 1; linkID < linkCount; linkID++) + { + const ArticulationLink& link = links[linkID]; + //const ArticulationLinkData& linkDatum = linkData[linkID]; + const ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + const ArticulationLink& pLink = links[link.parent]; + + const ArticulationJointCore& j = static_cast(*links[linkID].inboundJoint); + + //const bool jointDrive = (j.driveType != PxArticulationJointDriveType::eNONE); + + bool hasFriction = j.frictionCoefficient > 0.f; + + const PxReal fCoefficient = j.frictionCoefficient * dt; + const PxReal transmissionForce = jointTransmittedForce[linkID].magnitude() * fCoefficient; + + PxU32 limitedRows = jointDatum.limitedAxes * 2u; + + PxU8 driveRows = 0; + + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)) + driveRows++; + } + + PxU8 frictionRows = hasFriction ? PxU8(jointData[linkID].dof) : PxU8(0); + + PxU32 lockedAxes = jointDatum.lockedAxes; + + + const PxU8 constraintCount = PxU8(limitedRows + driveRows + frictionRows + lockedAxes); + if (!constraintCount) + continue; + + const PxTransform cA2w = pLink.bodyCore->body2World.transform(j.parentPose); + const PxTransform cB2w = link.bodyCore->body2World.transform(j.childPose); + + //const PxTransform cB2cA = cA2w.transformInv(cB2w); + + PxTGSSolverConstraintDesc& desc = constraintDesc[descCount++]; + + desc.articulationA = articulation; + desc.linkIndexA = Ps::to16(links[linkID].parent); + desc.articulationALength = Ps::to16(solverDataSize); + + desc.articulationB = articulation; + desc.linkIndexB = linkID; + desc.articulationBLength = Ps::to16(solverDataSize); + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeaderStep) + + sizeof(SolverConstraint1DExtStep) * constraintCount; + + PX_ASSERT(0 == (constraintLength & 0x0f)); + desc.constraintLengthOver16 = Ps::to16(constraintLength / 16); + + desc.constraint = stream.reserve(constraintLength + 16u, constraintBlockManager); + + desc.writeBack = NULL; + + SolverConstraint1DHeaderStep* header = reinterpret_cast(desc.constraint); + SolverConstraint1DExtStep* constraints = reinterpret_cast(desc.constraint + sizeof(SolverConstraint1DHeaderStep)); + + init(*header, constraintCount, true, 0.f, ims); + + header->rAWorld = cA2w.p - pLink.bodyCore->body2World.p; + header->rBWorld = cB2w.p - link.bodyCore->body2World.p; + header->rALocal = j.parentPose.p; + header->rBLocal = j.childPose.p; + + PxU32 cIndex = 0; + + PxU32 dofId = 0; + + for (PxU32 i = 0; i < PxArticulationAxis::eX; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].top); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), axis), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -axis), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + PxReal unitResponse = unsimdRef(deltaVA).angular.dot(axis) - unsimdRef(deltaVB).angular.dot(axis); + + const PxReal recipResponse = unitResponse > DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + + if (frictionRows) + { + Dy::SolverConstraint1DExtStep& frictionRow = constraints[cIndex]; + cIndex++; + + frictionRow.lin0 = PxVec3(0.f); + frictionRow.lin1 = PxVec3(0.f); + frictionRow.ang0 = axis; + frictionRow.ang1 = axis; + + + createDriveOrLimit(frictionRow, 0.f, -transmissionForce, transmissionForce, false, 0.f, 0.f, 0.f, false, + deltaVA, deltaVB, recipResponse); + } + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + const PxReal lowLimit = j.limits[i].low; + const PxReal highLimit = j.limits[i].high; + + articulation->createHardLimitTGS(constraints[cIndex++], -axis, jointPos - lowLimit, recipDt, -deltaVA, -deltaVB, recipResponse); + articulation->createHardLimitTGS(constraints[cIndex++], axis, highLimit - jointPos, recipDt, deltaVA, deltaVB, recipResponse); + } + + if (hasDrive) + { + PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + + Dy::SolverConstraint1DExtStep& c = constraints[cIndex++]; + + c.lin0 = PxVec3(0); + c.lin1 = PxVec3(0); + c.ang0 = axis; + c.ang1 = axis; + + createSpringDrive(c, targetPos - jointPos, -targetVelocity, j.drives[i].maxForce, + j.drives[i].stiffness, j.drives[i].damping, dt, totalDt, deltaVA, deltaVB, unitResponse); + } + + + + } + dofId++; + } + } + + for (PxU32 i = PxArticulationAxis::eX; i < PxArticulationAxis::eCOUNT; ++i) + { + if (j.motion[i] != PxArticulationMotion::eLOCKED) + { + bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + + if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) + { + + + //Impulse response vector and axes are common for all constraints on this axis besides locked axis!!! + const PxVec3 axis = link.bodyCore->body2World.rotate(jointDatum.motionMatrix[dofId].bottom); + const PxVec3 ang0 = (cA2w.p - pLink.bodyCore->body2World.p).cross(axis); + const PxVec3 ang1 = (cB2w.p - pLink.bodyCore->body2World.p).cross(axis); + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(axis, ang0), deltaVA, + linkID, Cm::SpatialVector(-axis, -ang1), deltaVB); + + //Now add in friction rows, then limit rows, then drives... + + PxReal unitResponse = unsimdRef(deltaVA).linear.dot(axis) + unsimdRef(deltaVA).angular.dot(ang0) - unsimdRef(deltaVB).linear.dot(axis) - + unsimdRef(deltaVB).angular.dot(ang1); + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + + if (frictionRows) + { + Dy::SolverConstraint1DExtStep& frictionRow = constraints[cIndex]; + cIndex++; + + frictionRow.lin0 = axis; + frictionRow.lin1 = axis; + frictionRow.ang0 = ang0; + frictionRow.ang1 = ang1; + + createDriveOrLimit(frictionRow, 0.f, -transmissionForce, transmissionForce, false, 0.f, 0.f, 0.f, false, + deltaVA, deltaVB, recipResponse); + } + + if (j.motion[i] == PxArticulationMotion::eLIMITED) + { + //Do linear limits... + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + const PxReal lowLimit = j.limits[i].low; + const PxReal highLimit = j.limits[i].high; + + Dy::SolverConstraint1DExtStep* prismaticRows = &constraints[cIndex]; + cIndex += 2; + + prismaticRows[0].lin0 = -axis; + prismaticRows[0].lin1 = -axis; + prismaticRows[0].ang0 = -ang0; + prismaticRows[0].ang1 = -ang1; + prismaticRows[1].lin0 = axis; + prismaticRows[1].lin1 = axis; + prismaticRows[1].ang0 = ang0; + prismaticRows[1].ang1 = ang1; + + createDriveOrLimit(prismaticRows[0], (jointPos - lowLimit), 0.f, PX_MAX_F32, false, 100.f, 0.f, 0.7f*recipDt, true, -deltaVA, -deltaVB, recipResponse); + createDriveOrLimit(prismaticRows[1], (highLimit - jointPos), 0.f, PX_MAX_F32, false, 100.f, 0.f, 0.7f*recipDt, true, deltaVA, deltaVB, recipResponse); + } + + if (hasDrive) + { + PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + PxReal targetPos = jointDatum.targetJointPosition[dofId]; + const PxReal jointPos = data.mJointPosition[jointDatum.jointOffset + dofId]; + + Dy::SolverConstraint1DExtStep& c = constraints[cIndex++]; + + c.lin0 = axis; + c.lin1 = axis; + c.ang0 = ang0; + c.ang1 = ang1; + + createSpringDrive(c, targetPos - jointPos, -targetVelocity, j.drives[i].maxForce, + j.drives[i].stiffness, j.drives[i].damping, dt, totalDt, -deltaVA, -deltaVB, unitResponse); + } + + + } + dofId++; + } + } + + if (lockedAxes) + { + const PxQuat qB2qA = cA2w.q.getConjugate() * cB2w.q; + PxVec3 row[3]; + computeJacobianAxes(row, cA2w.q, cB2w.q); + + PxReal err[] = { -qB2qA.x, -qB2qA.y, -qB2qA.z }; + + for (PxU32 i = PxArticulationAxis::eTWIST; i <= PxArticulationAxis::eSWING2; ++i) + { + //Get axis, then add lock constraint... + if (j.motion[i] == PxArticulationMotion::eLOCKED) + { + Dy::SolverConstraint1DExtStep& lockRow = constraints[cIndex]; + cIndex++; + + const PxVec3 axis = row[i]; + PxReal error = err[i]; + + lockRow.lin0 = PxVec3(0.f); + lockRow.lin1 = PxVec3(0.f); + lockRow.ang0 = row[i]; + lockRow.ang1 = row[i]; + + Cm::SpatialVectorV deltaVA, deltaVB; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, + links[linkID].parent, Cm::SpatialVector(PxVec3(0), row[i]), deltaVA, + linkID, Cm::SpatialVector(PxVec3(0), -row[i]), deltaVB); + + PxReal unitResponse = unsimdRef(deltaVA).angular.dot(row[i]) - unsimdRef(deltaVB).angular.dot(row[i]); + + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + createDriveOrLimit(lockRow, error, -PX_MAX_F32, PX_MAX_F32, false, 30.f, 0.f, 0.7f*invDt, false, deltaVA, deltaVB, recipResponse); + lockRow.angularErrorScale = 1.f; + } + } + } + + + + *(desc.constraint + getConstraintLength(desc)) = 0; + + PX_ASSERT(cIndex == constraintCount); + acCount += constraintCount; + } + + return descCount; + } + +#else + + PxU32 FeatherstoneArticulation::setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc, + PxcConstraintBlockStream& /*stream*/, + PxTGSSolverConstraintDesc* /*constraintDesc*/, + PxReal dt, + PxReal invDt, + PxReal totalDt, + PxU32& acCount, + PxsConstraintBlockManager& /*constraintBlockManager*/, + Cm::SpatialVectorF* Z) + { + PX_UNUSED(dt); + PX_UNUSED(totalDt); + acCount = 0; + + FeatherstoneArticulation* thisArtic = static_cast(articDesc.articulation); + + ArticulationLink* links = thisArtic->mArticulationData.getLinks(); + const PxU32 linkCount = thisArtic->mArticulationData.getLinkCount(); + const bool fixBase = thisArtic->mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + thisArtic->setupInternalConstraints(links, linkCount, fixBase, thisArtic->mArticulationData, Z, totalDt, invDt, 0.7f, true); + + return 0; + } + +#endif + + + void FeatherstoneArticulation::createHardLimitTGS( + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt, + const Cm::SpatialVectorV& deltaVA, + const Cm::SpatialVectorV& deltaVB, + PxReal recipResponse) + { + PxReal error = err; + init(s, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); + + s.deltaVA = deltaVA; + s.deltaVB = deltaVB; + + + s.error = error; + s.biasScale = -recipDt*0.7f; + s.maxBias = 30.f; + s.velMultiplier = -1.f; + s.recipResponse = recipResponse; + s.impulseMultiplier = 1.0f; + s.velTarget = 0.f; + s.angularErrorScale = 1.f; + s.flags |= DY_SC_FLAG_INEQUALITY; + if (error > 0.f) + s.flags |= DY_SC_FLAG_KEEP_BIAS; + } + + void FeatherstoneArticulation::createTangentialSpringTGS( + ArticulationLink* links, + const bool fixBase, + Cm::SpatialVectorF* Z, + ArticulationData& data, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt) + { + init(s, PxVec3(0), PxVec3(0), axis, axis, -PX_MAX_F32, PX_MAX_F32); + + Cm::SpatialVector axis6(PxVec3(0), axis); + PxU32 parent = links[linkIndex].parent; + FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, parent, axis6, s.deltaVA, linkIndex, -axis6, s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if (unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, tangential spring ignored"); + const PxReal recipResponse = unitResponse> DY_ARTICULATION_MIN_RESPONSE ? 1.0f / unitResponse : 0.0f; + + // this is a specialization of the spring code in setSolverConstants() for acceleration springs. + // general case is b = dt * (c.mods.spring.damping * c.velocityTarget - c.mods.spring.stiffness * geomError); + // but geomError and velocityTarget are both zero + + const PxReal a = dt * dt * stiffness + dt * damping; + const PxReal x = 1.0f / (1.0f + a); + s.error = 0.f; + s.biasScale = 0.f; + s.maxBias = 0.f; + s.velMultiplier = -x * a; + s.impulseMultiplier = 1.0f - x; + s.velTarget = 0.f; + s.recipResponse = recipResponse; + s.angularErrorScale = 1.f; + } + + void FeatherstoneArticulation::teleportLinks(ArticulationData& data) + { + jcalc(data); + + ArticulationLink* links = mArticulationData.getLinks(); + + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + PxReal* jointPositions = data.getJointPositions(); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + + ArticulationJointCoreData& jointDatum = jointData[linkID]; + + ArticulationLink& pLink = links[link.parent]; + const PxTransform pBody2World = pLink.bodyCore->body2World; + + ArticulationJointCore* joint = link.inboundJoint; + + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + + PxQuat newParentToChild; + PxQuat newWorldQ; + PxVec3 r; + + + const PxVec3 childOffset = -joint->childPose.p; + const PxVec3 parentOffset = joint->parentPose.p; + + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + newParentToChild = joint->relativeQuat; + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + const PxVec3& u = jointDatum.motionMatrix[0].bottom; + + r = e + d + u * jPosition[0]; + break; + } + case PxArticulationJointType::eREVOLUTE: + { + const PxVec3& u = jointDatum.motionMatrix[0].top; + + PxQuat jointRotation = PxQuat(-jPosition[0], u); + if (jointRotation.w < 0) //shortest angle. + jointRotation = -jointRotation; + + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + PxQuat jointRotation(PxIdentity); + for (PxU32 d = jointDatum.dof; d > 0; --d) + { + PxQuat deltaRot(jPosition[d - 1], jointDatum.motionMatrix[d - 1].top); + jointRotation = jointRotation * deltaRot; + } + + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + break; + } + case PxArticulationJointType::eFIX: + { + //this is fix joint so joint don't have velocity + newParentToChild = joint->relativeQuat; + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + r = e + d; + break; + } + default: + break; + } + + PxTransform& body2World = link.bodyCore->body2World; + body2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized(); + body2World.p = pBody2World.p + body2World.q.rotate(r); + + PX_ASSERT(body2World.isSane()); + } + } + + void FeatherstoneArticulation::jcalc(ArticulationData& data) + { + + if (getDirty()) + { + ArticulationLink* links = data.getLinks(); + ArticulationJointCoreData* jointData = data.getJointData(); + const PxU32 linkCount = data.getLinkCount(); + + PxU32 totalDof = 0; + bool hasSphericalJoint = false; + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + + ArticulationLink& link = links[linkID]; + ArticulationJointCore* joint = link.inboundJoint; + ArticulationJointCoreData& jointDatum = jointData[linkID]; + + PX_CHECK_AND_RETURN(joint->jointType != PxArticulationJointType::eUNDEFINED, "FeatherstoneArticulation::jcalc application need to define valid joint type and motion"); + jointDatum.computeJointDof(joint, false); + joint->setJointPose(jointDatum); + jointDatum.setJointVelocityDrive(joint); + jointDatum.setJointPoseDrive(joint); + + if (joint->jointType == PxArticulationJointType::eSPHERICAL) + hasSphericalJoint = true; + + jointDatum.jointOffset = totalDof; + joint->jointOffset = totalDof; + totalDof += jointDatum.dof; + } + + if (totalDof != mArticulationData.getDofs()) + { + mArticulationData.resizeJointData(totalDof); + mArticulationData.setDofs(totalDof); + } + + mHasSphericalJoint = hasSphericalJoint; + + setDirty(false); + } + } + + //compute link's spatial inertia tensor + void FeatherstoneArticulation::computeSpatialInertia(ArticulationData& data) + { + for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + PxsBodyCore& core = *link.bodyCore; + + const PxVec3& ii = core.inverseInertia; + + const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass; + + //construct mass matric + linkDatum.spatialArticulatedInertia.topLeft = PxMat33(PxZero); + //linkDatum.spatialArticulatedInertia.bottomRight = PxMat33(PxZero); + linkDatum.spatialArticulatedInertia.topRight = PxMat33::createDiagonal(PxVec3(m)); + + //construct inertia matrix + PxMat33& I = linkDatum.spatialArticulatedInertia.bottomLeft; + const PxVec3 inertiaTensor = PxVec3(ii.x == 0.f ? 0.f : (1.f / ii.x), ii.y == 0.f ? 0.f : (1.f / ii.y), ii.z == 0.f ? 0.f : (1.f / ii.z)); + I = PxMat33::createDiagonal(inertiaTensor); + + linkDatum.spatialInertia = linkDatum.spatialArticulatedInertia; + } + } + + void FeatherstoneArticulation::computeZ(ArticulationData& data, + const PxVec3& gravity, ScratchData& scratchData) + { + + Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; + Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; + Cm::SpatialVector* externalAccels = scratchData.externalAccels; + + for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + PxsBodyCore& core = *link.bodyCore; + const PxTransform& body2World = core.body2World; + + //PxMat33& I = linkDatum.spatialArticulatedInertia.bottomLeft; + + //construct spatial zero acceleration + Cm::SpatialVectorF& z = spatialZAForces[linkID]; + + Cm::SpatialVectorF v; + v.top = body2World.rotateInv(motionVelocities[linkID].top); + v.bottom = body2World.rotateInv(motionVelocities[linkID].bottom); + + const PxVec3 exLinAccel = -body2World.rotateInv(gravity); + + /*Cm::SpatialVectorF Iv = linkDatum.spatialArticulatedInertia*v; + z.top = -v.bottom.cross(Iv.top) + (exLinAccel * m); + + z.bottom = v.top.cross(Iv.bottom);*/ + const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass; + const PxMat33& I = linkDatum.spatialInertia.bottomLeft; + + const PxVec3 inertiaTensor(I.column0.x, I.column1.y, I.column2.z); + z.top = (exLinAccel * m); + z.bottom = v.top.cross(inertiaTensor.multiply(v.top)); + + PX_ASSERT(z.top.isFinite()); + PX_ASSERT(z.bottom.isFinite()); + } + + if (externalAccels) + { + for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID) + { + Cm::SpatialVector& externalAccel = externalAccels[linkID]; + + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + PxsBodyCore& core = *link.bodyCore; + const PxTransform& body2World = core.body2World; + + const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass; + + PxMat33& I = linkDatum.spatialInertia.bottomLeft; + + const PxVec3 inertiaTensor(I.column0.x, I.column1.y, I.column2.z); + + //construct spatial zero acceleration + Cm::SpatialVectorF& z = spatialZAForces[linkID]; + + const PxVec3 exLinAccel = -body2World.rotateInv(externalAccel.linear); + const PxVec3 exAngAccel = -body2World.rotateInv(externalAccel.angular); + + z.top += (exLinAccel * m); + z.bottom += inertiaTensor.multiply(exAngAccel); + + PX_ASSERT(z.top.isFinite()); + PX_ASSERT(z.bottom.isFinite()); + + //ML: we can't clear the external accelerations in here because it will break + //TGS and when we are using inverse dynamic as well + //externalAccel = Cm::SpatialVector(PxVec3(0), PxVec3(0)); + } + } + } + + //This can be called by forward dynamic + void FeatherstoneArticulation::computeD(ArticulationData& data, ScratchData& scratchData, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; + + Cm::SpatialVectorF* dragForces = Z; + + PxMemZero(dragForces, sizeof(Cm::SpatialVectorF)*data.getLinkCount()); + + const PxReal dt = data.getDt(); + + bool bAppliedImpusle = false; + + for (PxU32 linkID = 0; linkID < data.getLinkCount(); ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + + PxsBodyCore& core = *link.bodyCore; + const PxTransform& body2World = core.body2World; + + const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass; + + //PxMat33& I = linkDatum.spatialArticulatedInertia.bottomLeft; + PxVec3 I = PxVec3(1.f / core.inverseInertia.x, 1.f / core.inverseInertia.y, 1.f / core.inverseInertia.z); + + Cm::SpatialVectorF& d = dragForces[linkID]; + + Cm::SpatialVectorF v; + v.top = body2World.rotateInv(motionVelocities[linkID].top); + v.bottom = body2World.rotateInv(motionVelocities[linkID].bottom); + + if (core.linearDamping > 0.f || core.angularDamping > 0.f) + { + d.top += (v.bottom * core.linearDamping*m*dt); + d.bottom += I.multiply(v.top* core.angularDamping*dt); + bAppliedImpusle = true; + } + + const PxReal maxAng = core.maxAngularVelocitySq; + const PxReal maxLin = core.maxLinearVelocitySq; + + const PxReal angMag = v.top.magnitudeSquared(); + const PxReal linMag = v.bottom.magnitudeSquared(); + + if (angMag > maxAng || linMag > maxLin) + { + + if (angMag > maxAng) + { + PxReal scale = 1.f - PxSqrt(maxAng) / PxSqrt(angMag); + PxVec3 tmpaccelerationAng = (I.multiply(v.top)*scale); + PX_UNUSED(tmpaccelerationAng); + d.bottom = tmpaccelerationAng; + + bAppliedImpusle = true; + } + + if (linMag > maxLin) + { + PxReal scale = 1.f - (PxSqrt(maxLin) / PxSqrt(linMag))*0.8f; + PxVec3 tmpaccelerationLin = (v.bottom*m*scale); + PX_UNUSED(tmpaccelerationLin); + d.top = tmpaccelerationLin; + + bAppliedImpusle = true; + + } + } + } + + if (bAppliedImpusle) + { + applyImpulses(dragForces, DeltaV); + + PxReal* deltaV = data.getJointDeltaVelocities(); + PxReal* jointV = data.getJointVelocities(); + + for (PxU32 linkID = 1; linkID < data.getLinkCount(); ++linkID) + { + ArticulationJointCoreData& tJointDatum = data.getJointData()[linkID]; + for (PxU32 i = 0; i < tJointDatum.dof; ++i) + { + jointV[i + tJointDatum.jointOffset] += deltaV[i + tJointDatum.jointOffset]; + deltaV[i + tJointDatum.jointOffset] = 0.f; + } + } + } + } + + //compute coriolis and centrifugal term + void FeatherstoneArticulation::computeC(ArticulationData& data, ScratchData& scratchData) + { + const PxReal* jointVelocities = scratchData.jointVelocities; + Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors; + + const PxU32 linkCount = data.getLinkCount(); + + if (jointVelocities) + { + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + + const PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + PX_UNUSED(jVelocity); + Cm::SpatialVectorF& coriolis = coriolisVectors[linkID]; + + if (jointDatum.dof > 0) + { + //transform parent link's angular velocity into current link's body space + const PxVec3 parentAngular = scratchData.motionVelocities[link.parent].top; + const PxTransform& body2World = link.bodyCore->body2World; + const PxVec3 pAngular = body2World.q.rotateInv(parentAngular); + const PxVec3 temp0 = pAngular.cross(pAngular.cross(linkDatum.r)); + +#if 1 + Cm::SpatialVectorF relVel(PxVec3(0.f), PxVec3(0.f)); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const PxReal jV = jVelocity[ind]; + relVel += jointDatum.motionMatrix[ind] * jV; + } + + + const PxVec3 aVec = relVel.top; + const PxVec3 force = pAngular.cross(aVec); + + //compute linear part + const PxVec3 lVel = relVel.bottom; + + const PxVec3 temp1 = 2.f * pAngular.cross(lVel); + const PxVec3 temp2 = aVec.cross(lVel); + const PxVec3 torque = temp0 + temp1 + temp2; + coriolis = Cm::SpatialVectorF(force, torque); + +#else + coriolis = Cm::SpatialVectorF(PxVec3(0.f), temp0); + + //motionMatrix is in link space + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const PxReal jV = jVelocity[ind]; + + //compute angular part + const PxVec3 aVec = jointDatum.motionMatrix[ind].top * jV; + const PxVec3 angular = pAngular.cross(aVec); + + //compute linear part + const PxVec3 lVel = jointDatum.motionMatrix[ind].bottom * jV; + + const PxVec3 temp1 = 2 * pAngular.cross(lVel); + const PxVec3 temp2 = aVec.cross(lVel); + const PxVec3 linear = temp1 + temp2; + + Cm::SpatialVectorF tCoriolis(angular, linear); + + coriolis += tCoriolis; + PX_ASSERT(coriolis.isFinite()); + } +#endif + } + else + { + //fix joint + coriolis = Cm::SpatialVectorF(PxVec3(0), PxVec3(0)); + } + } + } + else + { + PxMemZero(coriolisVectors, sizeof(Cm::SpatialVectorF)*linkCount); + } + + } + + static PxMat33 constructSkewSymmetricMatrix(const PxVec3 r) + { + PxMat33 temp; + temp.column0 = PxVec3(0.f, r.z, -r.y); + temp.column1 = PxVec3(-r.z, 0.f, r.x); + temp.column2 = PxVec3(r.y, -r.x, 0.f); + return temp; + } + + void FeatherstoneArticulation::computeRelativeTransformC2P(ArticulationData& data) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + const PxU32 linkCount = data.getLinkCount(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + PxsBodyCore& bodyCore = *link.bodyCore; + + const PxTransform& body2World = bodyCore.body2World; + + ArticulationLink& pLink = links[link.parent]; + PxsBodyCore& pBodyCore = *pLink.bodyCore; + const PxTransform& pBody2World = pBodyCore.body2World; + + PxTransform tC2P = pBody2World.transformInv(body2World).getNormalized(); + + linkDatum.childToParent.R = PxMat33(tC2P.q); + linkDatum.childToParent.q = tC2P.q; + linkDatum.r = body2World.rotateInv(body2World.p - pBody2World.p);//body space of link i + linkDatum.rw =body2World.p - pBody2World.p; + + //child to parent rotation matrix + const PxMat33& c2p = linkDatum.childToParent.R; + //r is in link body space + PxMat33 skewMatrixPR = -constructSkewSymmetricMatrix(linkDatum.r); + //rotation matrix cToP's inverse is rotation matrix pToC + linkDatum.childToParent.T = c2p * (-skewMatrixPR); + +#if FEATURESTONE_DEBUG + { + //debug + PxMat33 pToC = c2p.getTranspose(); + //parentToChild -rR + PxMat33 T2 = skewMatrixPR * pToC; + + PX_ASSERT(SpatialMatrix::isTranspose(linkDatum.childToParent.T, T2)); + } +#endif + } + } + + void FeatherstoneArticulation::computeRelativeTransformC2B(ArticulationData& data) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + const PxU32 linkCount = data.getLinkCount(); + + ArticulationLink& bLink = links[0]; + const PxTransform& bBody2World = bLink.bodyCore->body2World; + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + PxsBodyCore& bodyCore = *link.bodyCore; + + const PxTransform& body2World = bodyCore.body2World; + + PxTransform tC2B = bBody2World.transformInv(body2World).getNormalized(); + linkDatum.childToBase.R= PxMat33(tC2B.q); + linkDatum.childToBase.q = tC2B.q; + const PxVec3 r = body2World.rotateInv(body2World.p - bBody2World.p);//body space of link i + + //child to parent rotation matrix + const PxMat33& c2b = linkDatum.childToParent.R; + //r is in link body space + PxMat33 skewMatrixPR = -constructSkewSymmetricMatrix(r); + //rotation matrix cToP's inverse is rotation matrix pToC + linkDatum.childToBase.T = c2b * (-skewMatrixPR); + } + } + + + //compute all links velocities + void FeatherstoneArticulation::computeLinkVelocities(ArticulationData& data, + ScratchData& scratchData) + { + + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + const PxU32 linkCount = data.getLinkCount(); + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + //motion velocities has to be in world space to avoid numerical errors caused by space + Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + + PxReal* jointVelocities = scratchData.jointVelocities; + + ArticulationLink& baseLink = links[0]; + ArticulationLinkData& baseLinkDatum = linkData[0]; + + PxsBodyCore& core0 = *baseLink.bodyCore; + + baseLinkDatum.maxPenBias = core0.maxPenBias; + + if (fixBase) + { + motionVelocities[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + motionAccelerations[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + else + { + motionVelocities[0] = Cm::SpatialVectorF(core0.angularVelocity, core0.linearVelocity); + } + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + PxsBodyCore& bodyCore = *link.bodyCore; + + linkDatum.maxPenBias = bodyCore.maxPenBias; + + //SpatialTransform p2c = linkDatum.childToParent.getTranspose(); + + //motionVelocites[linkID] = p2c * motionVelocites[link.parent]; + //motionVelocites[linkID] = motionVelocites[link.parent]; + /*motionVelocities[linkID].top = motionVelocities[link.parent].top; + motionVelocities[linkID].bottom = motionVelocities[link.parent].bottom + motionVelocities[link.parent].top.cross(linkDatum.rw);*/ + + PxVec3 ang = motionVelocities[link.parent].top; + PxVec3 lin = motionVelocities[link.parent].bottom + motionVelocities[link.parent].top.cross(linkDatum.rw); + const PxTransform& body2World = bodyCore.body2World; + + if (jointVelocities) + { + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + //const PxReal* const jV = &jointVelocity[linkID * 6]; + PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero(); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + //KS - TODO - only hack for rotational constraints!!! + //jVelocity[ind] = PxClamp(jVelocity[ind], -100.f, 100.f); + deltaV += jointDatum.motionMatrix[ind] * jVelocity[ind]; + } + + ang += body2World.rotate(deltaV.top); + lin += body2World.rotate(deltaV.bottom); + } + + motionVelocities[linkID] = Cm::SpatialVectorF(ang, lin); + } + } + + void FeatherstoneArticulation::solveInternalConstraints(const PxReal dt, const PxReal invDt, + Cm::SpatialVectorF* impulses, Cm::SpatialVectorF* DeltaV, bool velocityIteration) + { + PX_UNUSED(dt); + PX_UNUSED(invDt); + PX_UNUSED(velocityIteration); + const PxU32 count = mArticulationData.getLinkCount(); + + if (count <= 1) + return; + + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + ////(1) Flush velocities first...TODO - incorporate into code below properly...done! + //if(mArticulationData.mJointDirty) + // PxcFsFlushVelocity(*this, DeltaV); + + ArticulationLink* links = mArticulationData.getLinks(); + PxReal* jointPositions = mArticulationData.getJointPositions(); + Cm::SpatialVectorF* baseVelocities = mArticulationData.getMotionVelocities(); + //PxTransform* transforms = mArticulationData.mAccumulatedPoses.begin(); + PxTransform* transforms = mArticulationData.mPreTransform.begin(); + Cm::SpatialVectorF* deltaP = mArticulationData.getDeltaMotionVector(); + + PX_UNUSED(baseVelocities); + PX_UNUSED(transforms); + + + //PxMemZero(DeltaV, sizeof(Cm::SpatialVector)*count); + + Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors(); + + bool impulse = false; + { + + impulses[0].top = PxVec3(0.f); + impulses[0].bottom = PxVec3(0.f); + + + + if (!fixBase) + { + //ArticulationLink& link = links[0]; + + Cm::SpatialVectorF temp = mArticulationData.getBaseInvSpatialArticulatedInertia() * (-deferredZ[0]); + + const PxTransform& body2World0 = transforms[0]; + DeltaV[0] = temp.rotate(body2World0); + } + else + { + DeltaV[0].top = PxVec3(0.f); + DeltaV[0].bottom = PxVec3(0.f); + } + + + + PxU32 dofId = 0; + PxU32 lockId = 0; + for (PxU32 linkID = 1; linkID < count; ++linkID) + { + const ArticulationLink& link = links[linkID]; + //const ArticulationLink& plink = links[link.parent]; + ArticulationLinkData& linkDatum = mArticulationData.getLinkData(linkID); + + PX_UNUSED(linkDatum); + + const ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + PX_UNUSED(jPosition); + //PxReal* jVelocity = &jointVelocity[jointDatum.jointOffset]; + //PX_UNUSED(jVelocity); + + Cm::UnAlignedSpatialVector i1(PxVec3(0.f), PxVec3(0.f)); + + + Cm::SpatialVectorF parentV = DeltaV[link.parent] + baseVelocities[link.parent]; + + + Cm::SpatialVectorF localParentAccel = DeltaV[link.parent].rotateInv(transforms[link.parent]); + + Cm::SpatialVectorF parentVelContrib = propagateVelocityTestImpulse(linkDatum, jointDatum, deferredZ[linkID], localParentAccel).rotate(transforms[linkID]); + //Cm::SpatialVectorF parentVelContrib = ComputeDeltaVelocity(linkDatum, jointDatum, localParentAccel).rotate(transforms[linkID]); + + //DeltaV[linkID] will always be zero so doesn't need to be used in here... + Cm::SpatialVectorF childV = baseVelocities[linkID] + parentVelContrib; + + //KS - we need to record this in dv1. Even if the constraint doesn't apply any impulses, a previous + //constraint could have done, and this could lead to something not being accumulated. + Cm::UnAlignedSpatialVector dv1(parentVelContrib.top, parentVelContrib.bottom); + + + if (jointDatum.dofInternalConstraintMask || jointDatum.lockedAxes) + { + Cm::UnAlignedSpatialVector i0(PxVec3(0.f), PxVec3(0.f)); + Cm::UnAlignedSpatialVector dv0(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 dof = 0; dof < jointDatum.dof; ++dof) + { + if (jointDatum.dofInternalConstraintMask & (1 << dof)) + { + ArticulationInternalConstraint& constraint = mArticulationData.mInternalConstraints[dofId++]; + + PxReal jointP = jPosition[dof]; + + if (!constraint.isLinearConstraint) + { + //Clamp jointP within +/- PxPi + if (jointP > PxTwoPi) + jointP -= 2.f*PxTwoPi; + else if (jointP < -PxTwoPi) + jointP += 2.f*PxTwoPi; + } + + PxReal error = (constraint.driveTarget - jointP); + + //if (!constraint.isLinearConstraint) + //{ + // if (PxAbs(error) > PxPi) + // { + // //drive target and current angle are in different hemispheres, so we + // //should see if we can legally reach the target going the "shorter" route, + // //otherwise we stick with the straight line route... + // PxReal targetInPHemisphere = constraint.driveTarget; + // if(error < 0.f) + // targetInPHemisphere += PxTwoPi; + // else + // targetInPHemisphere -= PxTwoPi; + + // if (constraint.lowLimit <= targetInPHemisphere && targetInPHemisphere <= constraint.highLimit) + // { + // error = targetInPHemisphere - jointP; + // } + // } + //} + + PxReal jointV = constraint.row1.innerProduct(childV) - constraint.row0.innerProduct(parentV); + + const PxReal appliedFriction = constraint.frictionForce*constraint.frictionForceCoefficient; + + PxReal frictionForce = PxClamp(-jointV *constraint.recipResponse + appliedFriction, + -constraint.maxFrictionForce*dt, constraint.maxFrictionForce*dt); + + PxReal frictionDeltaF = frictionForce - appliedFriction; + + constraint.frictionForce += frictionDeltaF; + + jointV += frictionDeltaF * constraint.response; + + + + PxReal unclampedForce = constraint.driveImpulseMultiplier * constraint.driveForce + + jointV * constraint.driveVelMultiplier + constraint.driveTargetVel + error * constraint.driveBiasCoefficient; + + PxReal clampedForce = PxClamp(unclampedForce, -constraint.maxDriveForce, constraint.maxDriveForce); + PxReal driveDeltaF = (clampedForce - constraint.driveForce); + + //Where we will be next frame - we use this to compute error bias terms to correct limits and drives... + + jointV += driveDeltaF * constraint.response; + + driveDeltaF += frictionDeltaF; + + + PxReal deltaF = 0.f; + + if (velocityIteration) + { + deltaF = PxClamp(-jointV*constraint.recipResponse, -constraint.lowImpulse, -constraint.highImpulse); + } + else + { + PxReal futureP = jointP + jointV * dt; + if (futureP > constraint.highLimit) + { + PxReal erp = jointP > constraint.highLimit ? constraint.erp : 1.f; + //PxReal deltaV = (constraint.highLimit - jointP)*invDt*erp - jointV; + PxReal deltaV = (constraint.highLimit - futureP)*invDt*erp; + deltaF = PxMin(constraint.highImpulse + deltaV * constraint.recipResponse, 0.f) - constraint.highImpulse; + + constraint.highImpulse += deltaF; + + } + else if (futureP < constraint.lowLimit) + { + PxReal erp = jointP < constraint.lowLimit ? constraint.erp : 1.f; + //PxReal deltaV = (constraint.lowLimit - jointP)*invDt*erp - jointV; + PxReal deltaV = (constraint.lowLimit - futureP)*invDt*erp; + deltaF = PxMax(constraint.lowImpulse + deltaV * constraint.recipResponse, 0.f) - constraint.lowImpulse; + constraint.lowImpulse += deltaF; + } + } + + deltaF += driveDeltaF; + + if (deltaF != 0.f) + { + impulse = true; + constraint.driveForce = clampedForce; + + i0 += constraint.row0 * deltaF; + i1 -= constraint.row1 * deltaF; + + const Cm::UnAlignedSpatialVector deltaVP = -constraint.deltaVA * deltaF; + const Cm::UnAlignedSpatialVector deltaVC = -constraint.deltaVB * deltaF; + + dv0 += deltaVP; + dv1 += deltaVC; + + parentV += Cm::SpatialVectorF(deltaVP.top, deltaVP.bottom); + childV += Cm::SpatialVectorF(deltaVC.top, deltaVC.bottom); + } + + } + } + + PxU32 startIdx = PxU32(jointDatum.dof - jointDatum.lockedAxes); + + for (PxU32 i = startIdx; i < jointDatum.dof; ++i) + { + ArticulationInternalLockedAxis& lockAxis = mArticulationData.mInternalLockedAxes[lockId++]; + + PxReal jointV = lockAxis.axis.dot(childV.top) - lockAxis.axis.dot(parentV.top); + + PxReal deltaJointP = lockAxis.axis.dot(deltaP[linkID].top) - lockAxis.axis.dot(deltaP[link.parent].top); + PX_UNUSED(deltaJointP); + + PxReal deltaV = -jointV; + //We aim to zero velocity but also correct bias... + if(!velocityIteration) + deltaV += PxClamp((lockAxis.error - deltaJointP) * lockAxis.biasScale, -30.f, 30.f); + //deltaV +=PxClamp((lockAxis.error - deltaJointP) * lockAxis.biasScale, -10.f, 10.f); + + //deltaV = PxClamp(deltaV, -5.f, 5.f); + + PxReal deltaF = deltaV * lockAxis.recipResponse; + + if (deltaF != 0.f) + { + impulse = true; + + i0.bottom += lockAxis.axis * deltaF; + i1.bottom -= lockAxis.axis * deltaF; + + const Cm::UnAlignedSpatialVector tDeltaVP = -lockAxis.deltaVA * deltaF; + const Cm::UnAlignedSpatialVector tDeltaVC = -lockAxis.deltaVB * deltaF; + + const Cm::SpatialVectorF deltaVP = Cm::SpatialVectorF(tDeltaVP.top, tDeltaVP.bottom); + const Cm::SpatialVectorF deltaVC = Cm::SpatialVectorF(tDeltaVC.top, tDeltaVC.bottom); + + dv0 += deltaVP; + dv1 += deltaVC; + + parentV += deltaVP; + childV += deltaVC; + } + + } + + DeltaV[link.parent] += Cm::SpatialVectorF(dv0.top, dv0.bottom); + impulses[link.parent] += Cm::SpatialVectorF(i0.top, i0.bottom); + } + + DeltaV[linkID] = Cm::SpatialVectorF(dv1.top, dv1.bottom); + impulses[linkID] = Cm::SpatialVectorF(i1.top, i1.bottom); + } + + if (1) + { + //Now propagate impulses... + if (impulse) + { + for (PxU32 i = 0; i < count; ++i) + { + //impulses[i] = impulses[i].rotateInv(/*transforms[i]*/links[i].bodyCore->body2World); + impulses[i] = impulses[i].rotateInv(transforms[i]); + } + pxcFsApplyImpulses(impulses); + //applyImpulses(impulses, DeltaV); + } + } + } + } + + //This method is for user update the root link transform so we need to + //fix up other link's position. In this case, we should assume all joint + //velocity/pose is to be zero + void FeatherstoneArticulation::teleportRootLink() + { + //make sure motionMatrix has been set + jcalc(mArticulationData); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + ArticulationLink* links = mArticulationData.getLinks(); + PxReal* jointPositions = mArticulationData.getJointPositions(); + Cm::SpatialVectorF* motionVelocities = mArticulationData.getMotionVelocities(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + const PxTransform oldTransform = link.bodyCore->body2World; + + ArticulationLink& pLink = links[link.parent]; + const PxTransform pBody2World = pLink.bodyCore->body2World; + + ArticulationJointCore* joint = link.inboundJoint; + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + + PxQuat newParentToChild; + PxQuat newWorldQ; + PxVec3 r; + + const PxVec3 childOffset = -joint->childPose.p; + const PxVec3 parentOffset = joint->parentPose.p; + + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + newParentToChild = joint->relativeQuat; + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + const PxVec3& u = jointDatum.motionMatrix[0].bottom; + + r = e + d + u * jPosition[0]; + break; + } + case PxArticulationJointType::eREVOLUTE: + { + const PxVec3& u = jointDatum.motionMatrix[0].top; + + PxQuat jointRotation = PxQuat(-jPosition[0], u); + if (jointRotation.w < 0) //shortest angle. + jointRotation = -jointRotation; + + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + /*PxVec3 worldAngVel = oldTransform.rotate(link.motionVelocity.top); + + newWorldQ = Ps::exp(worldAngVel*dt) * oldTransform.q; + + PxQuat newParentToChild2 = (newWorldQ.getConjugate() * joint->relativeQuat * pBody2World.q).getNormalized(); + + const PxVec3 e2 = newParentToChild2.rotate(parentOffset); + const PxVec3 d2 = childOffset; + r = e2 + d2;*/ + + PX_ASSERT(r.isFinite()); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + + //PxVec3 angVel(joint->jointVelocity[0], joint->jointVelocity[1], joint->jointVelocity[2]); + //PxVec3 worldAngVel = pLink.bodyCore->angularVelocity + oldTransform.rotate(angVel); + + PxVec3 worldAngVel = motionVelocities[linkID].top; + + /*const PxReal eps = 0.001f; + const PxVec3 dif = worldAngVel - worldAngVel2; + PX_ASSERT(PxAbs(dif.x) < eps && PxAbs(dif.y) < eps && PxAbs(dif.z) < eps);*/ + + newWorldQ = Ps::exp(worldAngVel) * oldTransform.q; + + newParentToChild = (newWorldQ.getConjugate() * joint->relativeQuat * pBody2World.q).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + break; + } + case PxArticulationJointType::eFIX: + { + //this is fix joint so joint don't have velocity + newParentToChild = joint->relativeQuat; + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + r = e + d; + break; + } + default: + break; + } + + PxTransform& body2World = link.bodyCore->body2World; + body2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized(); + body2World.p = pBody2World.p + body2World.q.rotate(r); + + PX_ASSERT(body2World.isSane()); + } + } + + + PxU8* FeatherstoneArticulation::allocateScratchSpatialData(PxcScratchAllocator* allocator, + const PxU32 linkCount, ScratchData& scratchData, bool fallBackToHeap) + { + + const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount; + const PxU32 totalSize = size * 4 + sizeof(Dy::SpatialMatrix) * linkCount; + + PxU8* tempMemory = reinterpret_cast(allocator->alloc(totalSize, fallBackToHeap)); + + scratchData.motionVelocities = reinterpret_cast(tempMemory); + PxU32 offset = size; + scratchData.motionAccelerations = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.coriolisVectors = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.spatialZAVectors = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.compositeSpatialInertias = reinterpret_cast(tempMemory + offset); + + return tempMemory; + } + + void FeatherstoneArticulation::allocateScratchSpatialData(DyScratchAllocator& allocator, + const PxU32 linkCount, ScratchData& scratchData) + { + const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount; + const PxU32 totalSize = size * 5 + sizeof(Dy::SpatialMatrix) * linkCount; + + PxU8* tempMemory = allocator.alloc(totalSize); + + scratchData.motionVelocities = reinterpret_cast(tempMemory); + PxU32 offset = size; + scratchData.motionAccelerations = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.coriolisVectors = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.spatialZAVectors = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.externalAccels = reinterpret_cast(tempMemory + offset); + offset += size; + scratchData.compositeSpatialInertias = reinterpret_cast(tempMemory + offset); + + } + + +}//namespace Dy +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h new file mode 100644 index 000000000..7eecc4d42 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXD_FEATHERSTONE_ARTICULATION_LINK_H +#define PXD_FEATHERSTONE_ARTICULATION_LINK_H + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" +#include "foundation/PxTransform.h" +#include "PsVecMath.h" +#include "CmUtils.h" +#include "CmSpatialVector.h" +#include "DyVArticulation.h" +#include "DyFeatherstoneArticulationUtils.h" + +namespace physx +{ + namespace Dy + { + + class ArticulationLinkData + { + public: + ArticulationLinkData() + { + maxPenBias = 0.f; + } + + Cm::SpatialVectorF Is[6];//stI is the transpose of Is + Cm::SpatialVectorF IsInvD[6]; + SpatialMatrix spatialInertia; + SpatialMatrix spatialArticulatedInertia; + SpatialTransform childToParent; + SpatialTransform childToBase; + PxVec3 r; //vector from parent com to child com + PxVec3 rw; //vector from parent com to child com + PxReal qstZIc[6];//jointForce - stZIc + PxReal invStIs[6][6]; + PxReal maxPenBias; + + }; + + struct ArticSolverData + { + SpatialTransform childToParent; + PxReal invStIs[6][6]; + }; + + }//namespace Dy +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp new file mode 100644 index 000000000..b6d38acd8 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp @@ -0,0 +1,1677 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsMathUtils.h" +#include "CmConeLimitHelper.h" +#include "DySolverConstraint1D.h" +#include "DyFeatherstoneArticulation.h" +#include "PxsRigidBody.h" +#include "PxcConstraintBlockStream.h" +#include "DyArticulationContactPrep.h" +#include "DyDynamics.h" +#include "DyArticulationReference.h" +#include "DyArticulationPImpl.h" +#include "DyArticulationFnsSimd.h" +#include "DyFeatherstoneArticulationLink.h" +#include "DyFeatherstoneArticulationJointData.h" +#include "PsFoundation.h" +#include "PxsIslandSim.h" +#include "common/PxProfileZone.h" +#include + + +#ifdef _MSC_VER +#pragma warning(disable:4505) +#endif + +namespace physx +{ +namespace Dy +{ + void PxcFsFlushVelocity(FeatherstoneArticulation& articulation, Cm::SpatialVectorF* deltaV); + + //initialize spatial articualted matrix and coriolis spatial force + void FeatherstoneArticulation::initLinks(ArticulationData& data, + const PxVec3& gravity, ScratchData& scratchData, Cm::SpatialVectorF* Z, + Cm::SpatialVectorF* DeltaV) + { + PX_UNUSED(Z); + PX_UNUSED(DeltaV); + //compute individual link's spatial inertia tensor + //[0, M] + //[I, 0] + computeSpatialInertia(data); + + //compute inidividual zero acceleration force + computeZ(data, gravity, scratchData); + + Cm::SpatialVectorF* za = mArticulationData.getTransmittedForces(); + //copy individual zero acceleration force to mTempData zaForce buffer + PxMemCopy(za, mArticulationData.getSpatialZAVectors(), sizeof(Cm::SpatialVectorF) * mArticulationData.getLinkCount()); + + computeArticulatedSpatialInertia(data); + + computeD(data, scratchData, Z, DeltaV); + + //compute corolis and centrifugal force + computeC(data, scratchData); + + computeArticulatedSpatialZ(mArticulationData, scratchData); + } + + +#if (FEATHERSTONE_DEBUG && (PX_DEBUG || PX_CHECKED)) + static bool isSpatialVectorEqual(Cm::SpatialVectorF& t0, Cm::SpatialVectorF& t1) + { + float eps = 0.0001f; + bool e0 = PxAbs(t0.top.x - t1.top.x) < eps && + PxAbs(t0.top.y - t1.top.y) < eps && + PxAbs(t0.top.z - t1.top.z) < eps; + + bool e1 = PxAbs(t0.bottom.x - t1.bottom.x) < eps && + PxAbs(t0.bottom.y - t1.bottom.y) < eps && + PxAbs(t0.bottom.z - t1.bottom.z) < eps; + + return e0 && e1; + } + + static bool isSpatialVectorZero(Cm::SpatialVectorF& t0) + { + float eps = 0.000001f; + + const bool c0 = PxAbs(t0.top.x) < eps && PxAbs(t0.top.y) < eps && PxAbs(t0.top.z) < eps; + const bool c1 = PxAbs(t0.bottom.x) < eps && PxAbs(t0.bottom.y) < eps && PxAbs(t0.bottom.z) < eps; + + return c0 && c1; + } +#endif + + //calculate Is + void FeatherstoneArticulation::computeIs(ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum) + { + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + linkDatum.Is[ind] = linkDatum.spatialArticulatedInertia * sa; + } + } + + + //compute inertia contribution part + SpatialMatrix FeatherstoneArticulation::computePropagateSpatialInertia(const PxU8 jointType, ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum) + { + SpatialMatrix spatialInertia; + + switch (jointType) + { + case PxArticulationJointType::ePRISMATIC: + case PxArticulationJointType::eREVOLUTE: + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[0]; + + Cm::SpatialVectorF& Is = linkDatum.Is[0]; + + const PxReal stIs = sa.innerProduct(linkDatum.Is[0]); + + linkDatum.invStIs[0][0] = (stIs > /*PX_EPS_REAL*/1e-5f) ? (1.f / stIs) : 0.f; + + linkDatum.IsInvD[0] = Is * linkDatum.invStIs[0][0]; + + + //(6x1)Is = [v0, v1]; (1x6)stI = [v1, v0] + //Cm::SpatialVector stI1(Is1.angular, Is1.linear); + Cm::SpatialVectorF stI(Is.bottom, Is.top); + + spatialInertia = SpatialMatrix::constructSpatialMatrix(linkDatum.IsInvD[0], stI); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + + +#if FEATHERSTONE_DEBUG + //This is for debugging + Temp6x6Matrix bigInertia(linkDatum.spatialArticulatedInertia); + Temp6x3Matrix bigS(jointDatum.motionMatrix.getColumns()); + + Temp6x3Matrix bigIs = bigInertia * bigS; + + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + Cm::SpatialVectorF tempIs = bigInertia * jointDatum.motionMatrix[ind]; + + PX_ASSERT(isSpatialVectorEqual(tempIs, linkDatum.Is[ind])); + + PX_ASSERT(bigIs.isColumnEqual(ind, tempIs)); + + } +#endif + + PxMat33 D(PxIdentity); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind2]; + D[ind][ind2] = sa.innerProduct(linkDatum.Is[ind]); + } + } + + PxMat33 invD = SpatialMatrix::invertSym33(D); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + linkDatum.invStIs[ind][ind2] = invD[ind][ind2]; + + } + } + +#if FEATHERSTONE_DEBUG + //debugging + Temp6x3Matrix bigIsInvD = bigIs * invD; +#endif + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + linkDatum.IsInvD[ind] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + Cm::SpatialVectorF& Is = linkDatum.Is[ind2]; + linkDatum.IsInvD[ind] += Is * linkDatum.invStIs[ind][ind2]; + } + +#if FEATHERSTONE_DEBUG + + const bool equal = bigIsInvD.isColumnEqual(ind, linkDatum.IsInvD[ind]); + PX_ASSERT(equal); +#endif + } + + +#if FEATHERSTONE_DEBUG + Temp6x6Matrix transpose6x6 = bigInertia.getTranspose(); +#endif + PxReal stI[6][3]; //[column][row] + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { +#if FEATHERSTONE_DEBUG + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + + Cm::SpatialVectorF sat = Cm::SpatialVectorF(sa.bottom, sa.top); + Cm::SpatialVectorF tstI = transpose6x6 * sat; +#endif + + Cm::SpatialVectorF& Is = linkDatum.Is[ind]; + +#if FEATHERSTONE_DEBUG + Cm::SpatialVectorF temp(Is.bottom, Is.top); + const bool equal = isSpatialVectorEqual(temp, tstI); + PX_ASSERT(equal); +#endif + + //(6x1)Is = [v0, v1]; (1x6)stI = [v1, v0] + stI[0][ind] = Is.bottom.x; + stI[1][ind] = Is.bottom.y; + stI[2][ind] = Is.bottom.z; + stI[3][ind] = Is.top.x; + stI[4][ind] = Is.top.y; + stI[5][ind] = Is.top.z; + } + + Cm::SpatialVectorF columns[6]; + for (PxU32 ind = 0; ind < 6; ++ind) + { + columns[ind] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + columns[ind] += linkDatum.IsInvD[ind2] * stI[ind][ind2]; + } + } + + spatialInertia = SpatialMatrix::constructSpatialMatrix(columns); +#if FEATHERSTONE_DEBUG + Temp6x6Matrix result = bigIsInvD * stI; + PX_ASSERT(result.isEqual(columns)); +#endif + + break; + } + default: + spatialInertia.setZero(); + break; + } + + //(I - Is*Inv(sIs)*sI) + spatialInertia = linkDatum.spatialArticulatedInertia - spatialInertia; + + return spatialInertia; + } + + void FeatherstoneArticulation::computeArticulatedSpatialInertia(ArticulationData& data) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + const PxU32 linkCount = data.getLinkCount(); + const PxU32 startIndex = PxU32(linkCount - 1); + + for (PxU32 linkID = startIndex; linkID > 0; --linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + + ArticulationJointCoreData& jointDatum = jointData[linkID]; + computeIs(linkDatum, jointDatum); + + //(I - Is*Inv(sIs)*sI) + SpatialMatrix spatialInertia = computePropagateSpatialInertia(link.inboundJoint->jointType, + linkDatum, jointDatum); + + //transform spatial inertia into parent space + transformInertia(linkDatum.childToParent, spatialInertia); + //PX_ASSERT(spatialInertia.isTranspose(spatialInertia.topLeft, spatialInertia.bottomRight)); + + //accumulate child's articulated spatial inertia to the parent's articulated spatial inertia + ArticulationLinkData& pLinkDatum = linkData[link.parent]; + pLinkDatum.spatialArticulatedInertia += spatialInertia; + } + + //cache base link inverse spatial inertia + ArticulationLinkData& bLinkDatum = linkData[0]; + data.mBaseInvSpatialArticulatedInertia = bLinkDatum.spatialArticulatedInertia.invertInertia(); + } + + void FeatherstoneArticulation::computeArticulatedSpatialZ(ArticulationData& data, + ScratchData& scratchData) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + const PxU32 linkCount = data.getLinkCount(); + const PxU32 startIndex = PxU32(linkCount - 1); + + Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors; + Cm::SpatialVectorF* articulatedZA = scratchData.spatialZAVectors; + + PxReal* jointForces = scratchData.jointForces; + + for (PxU32 linkID = startIndex; linkID > 0; --linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + + ArticulationJointCoreData& jointDatum = jointData[linkID]; + + //calculate spatial zero acceleration force, this can move out of the loop + Cm::SpatialVectorF Ic = linkDatum.spatialArticulatedInertia * coriolisVectors[linkID]; + Cm::SpatialVectorF ZIc = articulatedZA[linkID] + Ic; + + const PxReal* jF = &jointForces[jointDatum.jointOffset]; + + Cm::SpatialVectorF ZA(PxVec3(0.f), PxVec3(0.f)); + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + const PxReal stZ = sa.innerProduct(ZIc); + + //link.qstZIc[ind] = jF[ind] - stZ; + linkDatum.qstZIc[ind] = jF[ind] - stZ; + PX_ASSERT(PxIsFinite(linkDatum.qstZIc[ind])); + + ZA += linkDatum.IsInvD[ind] * linkDatum.qstZIc[ind]; + + } + //accumulate childen's articulated zero acceleration force to parent's articulated zero acceleration + ZA += ZIc; + articulatedZA[link.parent] += linkDatum.childToParent * ZA; + } + } + + void FeatherstoneArticulation::computeJointAcceleration(ArticulationLinkData& linkDatum, ArticulationJointCoreData& jointDatum, + const Cm::SpatialVectorF& pMotionAcceleration, PxReal* jointAcceleration) + { + + PxReal tJAccel[6]; + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + //stI * pAcceleration + const PxReal temp = linkDatum.Is[ind].innerProduct(pMotionAcceleration); + + tJAccel[ind] = (linkDatum.qstZIc[ind] - temp); + } + + //calculate jointAcceleration + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + jointAcceleration[ind] = 0.f; + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + jointAcceleration[ind] += linkDatum.invStIs[ind2][ind] * tJAccel[ind2]; + } + //PX_ASSERT(PxAbs(jointAcceleration[ind]) < 5000); + } + + } + + void FeatherstoneArticulation::computeLinkAcceleration(ArticulationData& data, + ScratchData& scratchData) + { + const PxU32 linkCount = data.getLinkCount(); + const PxReal dt = data.getDt(); + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + //we have initialized motionVelocity and motionAcceleration to be zero in the root link if + //fix based flag is raised + Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors; + Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; + + if (!fixBase) + { + //ArticulationLinkData& baseLinkDatum = data.getLinkData(0); + SpatialMatrix invInertia = data.mBaseInvSpatialArticulatedInertia;//baseLinkDatum.spatialArticulatedInertia.invertInertia(); + +#if FEATHERSTONE_DEBUG + SpatialMatrix result = invInertia * baseLinkDatum.spatialArticulatedInertia; + + bool isIdentity = result.isIdentity(); + + PX_ASSERT(isIdentity); + + PX_UNUSED(isIdentity); +#endif + + + ArticulationLink& baseLink = data.getLink(0); + const PxTransform& body2World = baseLink.bodyCore->body2World; + + motionAccelerations[0] = -(invInertia * spatialZAForces[0]); + Cm::SpatialVectorF deltaV = motionAccelerations[0] * dt; + + //Cm::SpatialVectorF oldMotionVel = motionVelocities[0]; + Cm::SpatialVectorF oldVel = motionVelocities[0]; + + motionVelocities[0].top += body2World.rotate(deltaV.top); + motionVelocities[0].bottom += body2World.rotate(deltaV.bottom); + + } +#if FEATHERSTONE_DEBUG + else + { + PX_ASSERT(isSpatialVectorZero(motionAccelerations[0])); + PX_ASSERT(isSpatialVectorZero(motionVelocities[0])); + } +#endif + + /*PxReal* jointAccelerations = data.getJointAccelerations(); + PxReal* jointVelocities = data.getJointVelocities(); + PxReal* jointPositions = data.getJointPositions();*/ + + PxReal* jointAccelerations = scratchData.jointAccelerations; + PxReal* jointVelocities = scratchData.jointVelocities; + + //printf("===========================\n"); + + //calculate acceleration + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + ArticulationJointCore& joint = *link.inboundJoint; + PX_UNUSED(joint); + + SpatialTransform p2C = linkDatum.childToParent.getTranspose(); + Cm::SpatialVectorF pMotionAcceleration = p2C * motionAccelerations[link.parent]; + + + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + + //calculate jointAcceleration + PxReal* jA = &jointAccelerations[jointDatum.jointOffset]; + computeJointAcceleration(linkDatum, jointDatum, pMotionAcceleration, jA); + //printf("jA %f\n", jA[0]); + + Cm::SpatialVectorF motionAcceleration(PxVec3(0.f), PxVec3(0.f)); + PxReal* jointVelocity = &jointVelocities[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + PxReal jVel = jointVelocity[ind] + jA[ind] * dt; + if (PxAbs(jVel) > joint.maxJointVelocity) + { + jVel = jVel < 0.f ? -joint.maxJointVelocity : joint.maxJointVelocity; + jA[ind] = (jVel - jointVelocity[ind]) / dt; + } + + jointVelocity[ind] = jVel; + motionAcceleration += jointDatum.motionMatrix[ind] * jA[ind]; + } + + //KS - can we just work out velocities by projecting out the joint velocities instead of accumulating all this? + motionAccelerations[linkID] = pMotionAcceleration + coriolisVectors[linkID] + motionAcceleration; + PX_ASSERT(motionAccelerations[linkID].isFinite()); + + const PxTransform body2World = link.bodyCore->body2World; + //motionVelocities[linkID] += motionAccelerations[linkID] * dt; + + const Cm::SpatialVectorF deltaV = motionAccelerations[linkID] * dt; + motionVelocities[linkID].top += body2World.rotate(deltaV.top); + motionVelocities[linkID].bottom += body2World.rotate(deltaV.bottom); + } + + + } + + + void FeatherstoneArticulation::computeJointTransmittedFrictionForce( + ArticulationData& data, ScratchData& scratchData, Cm::SpatialVectorF* /*Z*/, Cm::SpatialVectorF* /*DeltaV*/) + { + //const PxU32 linkCount = data.getLinkCount(); + const PxU32 startIndex = data.getLinkCount() - 1; + + //const PxReal frictionCoefficent =30.5f; + Cm::SpatialVectorF* transmittedForce = scratchData.spatialZAVectors; + + for (PxU32 linkID = startIndex; linkID > 1; --linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + //joint force transmitted from parent to child + transmittedForce[link.parent] += linkDatum.childToParent * transmittedForce[linkID]; + } + + transmittedForce[0] = Cm::SpatialVectorF::Zero(); + + //const PxReal dt = data.getDt(); + //for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + //{ + // //ArticulationLink& link = data.getLink(linkID); + // //ArticulationLinkData& linkDatum = data.getLinkData(linkID); + // transmittedForce[linkID] = transmittedForce[linkID] * (frictionCoefficent) * dt; + // //transmittedForce[link.parent] -= linkDatum.childToParent * transmittedForce[linkID]; + //} + + // + + //applyImpulses(transmittedForce, Z, DeltaV); + + //PxReal* deltaV = data.getJointDeltaVelocities(); + //PxReal* jointV = data.getJointVelocities(); + + //for (PxU32 linkID = 1; linkID < data.getLinkCount(); ++linkID) + //{ + // ArticulationJointCoreData& tJointDatum = data.getJointData()[linkID]; + // for (PxU32 i = 0; i < tJointDatum.dof; ++i) + // { + // jointV[i + tJointDatum.jointOffset] += deltaV[i + tJointDatum.jointOffset]; + // deltaV[i + tJointDatum.jointOffset] = 0.f; + // } + //} + } + + //void FeatherstoneArticulation::computeJointFriction(ArticulationData& data, + // ScratchData& scratchData) + //{ + // PX_UNUSED(scratchData); + // const PxU32 linkCount = data.getLinkCount(); + // PxReal* jointForces = scratchData.jointForces; + // PxReal* jointFrictionForces = data.getJointFrictionForces(); + // PxReal* jointVelocities = data.getJointVelocities(); + + // const PxReal coefficient = 0.5f; + + // for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + // { + // ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + // //compute generalized force + // PxReal* jFs = &jointForces[jointDatum.jointOffset]; + // PxReal* jVs = &jointVelocities[jointDatum.jointOffset]; + // PxReal* jFFs = &jointFrictionForces[jointDatum.jointOffset]; + + // for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + // { + // PxReal sign = jVs[ind] > 0 ? -1.f : 1.f; + // jFFs[ind] = coefficient * PxAbs(jFs[ind]) *sign; + + // //jFFs[ind] = coefficient * jVs[ind]; + // } + // } + //} + + + void FeatherstoneArticulation::applyExternalImpulse(ArticulationLink* links, const PxU32 linkCount, + const bool fixBase, ArticulationData& data, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV, + const PxReal dt, const PxVec3& gravity, Cm::SpatialVector* acceleration) + { + + PxReal* jointVelocities = data.getJointVelocities(); + PxReal* jointAccelerations = data.getJointAccelerations(); + PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + + const PxU32 totalDofs = data.getDofs(); + PxMemZero(jointDeltaVelocities, sizeof(PxReal)*totalDofs); + + ArticulationLinkData* linkData = data.getLinkData(); + ArticulationJointCoreData* jointData = data.getJointData(); + + Cm::SpatialVectorF* motionVelcities = data.getMotionVelocities(); + + //compute external impulse + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + PxsBodyCore& core = *link.bodyCore; + + const PxTransform& body2World = core.body2World; + + Cm::SpatialVector& externalAccel = acceleration[linkID]; + const PxVec3 linkGravity = body2World.rotateInv(gravity); + PxVec3 linearAccel = body2World.rotateInv(externalAccel.linear); + if (!(link.body->mInternalFlags & PxsRigidBody::eDISABLE_GRAVITY)) + linearAccel += linkGravity; + + PxVec3 angularAccel = body2World.rotateInv(externalAccel.angular); + + Cm::SpatialVectorF a(angularAccel, linearAccel); + + Z[linkID] = -linkData[linkID].spatialArticulatedInertia * a * dt; + + externalAccel.linear = PxVec3(0.f); externalAccel.angular = PxVec3(0.f); + } + + + for (PxU32 linkID = PxU32(linkCount - 1); linkID > 0; --linkID) + { + ArticulationLink& tLink = links[linkID]; + ArticulationLinkData& tLinkDatum = linkData[linkID]; + ArticulationJointCoreData& tJointDatum = jointData[linkID]; + Z[tLink.parent] += FeatherstoneArticulation::propagateImpulse(tLinkDatum, tJointDatum, Z[linkID]); + } + + + if (fixBase) + { + deltaV[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + else + { + ArticulationLinkData& hLinkDatum = linkData[0]; + + SpatialMatrix inverseArticulatedInertia = hLinkDatum.spatialArticulatedInertia.getInverse(); + + deltaV[0] = inverseArticulatedInertia * (-Z[0]); + motionVelcities[0] += deltaV[0]; + + PX_ASSERT(motionVelcities[0].isFinite()); + } + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& tLink = links[linkID]; + ArticulationLinkData& tLinkDatum = linkData[linkID]; + ArticulationJointCoreData& tJointDatum = jointData[linkID]; + PxReal* jV = &jointDeltaVelocities[tJointDatum.jointOffset]; + deltaV[linkID] = FeatherstoneArticulation::propagateVelocity(tLinkDatum, tJointDatum, Z[linkID], + jV, deltaV[tLink.parent]); + motionVelcities[linkID] += deltaV[linkID]; + PX_ASSERT(motionVelcities[linkID].isFinite()); + } + + const PxReal invDt = 1 / dt; + //update joint acceleration + for (PxU32 i = 0; i < data.getDofs(); ++i) + { + jointVelocities[i] += jointDeltaVelocities[i]; + jointAccelerations[i] = jointDeltaVelocities[i] * invDt; + } + } + + + PxU32 FeatherstoneArticulation::computeUnconstrainedVelocities( + const ArticulationSolverDesc& desc, + PxReal dt, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, PxU64 contextID, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) + { + PX_UNUSED(contextID); + PX_UNUSED(desc); + FeatherstoneArticulation* articulation = static_cast(desc.articulation); + ArticulationData& data = articulation->mArticulationData; + data.setDt(dt); + + return articulation->computeUnconstrainedVelocitiesInternal(desc, allocator, constraintDesc, + acCount, gravity, Z, deltaV); + } + + + void FeatherstoneArticulation::computeUnconstrainedVelocitiesTGS( + const ArticulationSolverDesc& desc, + PxReal dt, const PxVec3& gravity, + PxU64 contextID, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + PX_UNUSED(contextID); + + FeatherstoneArticulation* articulation = static_cast(desc.articulation); + ArticulationData& data = articulation->mArticulationData; + data.setDt(dt); + + return articulation->computeUnconstrainedVelocitiesTGSInternal(gravity, Z, DeltaV); + } + + + //void FeatherstoneArticulation::computeCounteractJointForce(const ArticulationSolverDesc& desc, ScratchData& /*scratchData*/, const PxVec3& gravity) + //{ + // const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + // const PxU32 linkCount = mArticulationData.getLinkCount(); + // const PxU32 totalDofs = mArticulationData.getDofs(); + // //common data + // computeRelativeTransform(mArticulationData); + + // jcalc(mArticulationData); + + // computeSpatialInertia(mArticulationData); + + // DyScratchAllocator allocator(desc.scratchMemory, desc.scratchMemorySize); + + // ScratchData tempScratchData; + // allocateScratchSpatialData(allocator, linkCount, tempScratchData); + + // //PxReal* gravityJointForce = allocator.alloc(totalDofs); + // //{ + // // PxMemZero(gravityJointForce, sizeof(PxReal) * totalDofs); + + // // //compute joint force due to gravity + // // tempScratchData.jointVelocities = NULL; + // // tempScratchData.jointAccelerations = NULL; + // // tempScratchData.jointForces = gravityJointForce; + // // tempScratchData.externalAccels = NULL; + + // // if (fixBase) + // // inverseDynamic(mArticulationData, gravity,tempScratchData); + // // else + // // inverseDynamicFloatingLink(mArticulationData, gravity, tempScratchData); + // //} + + // ////PxReal* jointForce = mArticulationData.getJointForces(); + // //PxReal* tempJointForce = mArticulationData.getTempJointForces(); + // //{ + // // PxMemZero(tempJointForce, sizeof(PxReal) * totalDofs); + + // // //compute joint force due to coriolis force + // // tempScratchData.jointVelocities = mArticulationData.getJointVelocities(); + // // tempScratchData.jointAccelerations = NULL; + // // tempScratchData.jointForces = tempJointForce; + // // tempScratchData.externalAccels = NULL; + + // // if (fixBase) + // // inverseDynamic(mArticulationData, PxVec3(0.f), tempScratchData); + // // else + // // inverseDynamicFloatingLink(mArticulationData, PxVec3(0.f), tempScratchData); + // //} + + // //PxReal* jointForce = mArticulationData.getJointForces(); + // //for (PxU32 i = 0; i < mArticulationData.getDofs(); ++i) + // //{ + // // jointForce[i] = tempJointForce[i] - gravityJointForce[i]; + // //} + + // //PxReal* jointForce = mArticulationData.getJointForces(); + // PxReal* tempJointForce = mArticulationData.getTempJointForces(); + // { + // PxMemZero(tempJointForce, sizeof(PxReal) * totalDofs); + + // //compute joint force due to coriolis force + // tempScratchData.jointVelocities = mArticulationData.getJointVelocities(); + // tempScratchData.jointAccelerations = NULL; + // tempScratchData.jointForces = tempJointForce; + // tempScratchData.externalAccels = mArticulationData.getExternalAccelerations(); + + // if (fixBase) + // inverseDynamic(mArticulationData, gravity, tempScratchData); + // else + // inverseDynamicFloatingLink(mArticulationData, gravity, tempScratchData); + // } + + // PxReal* jointForce = mArticulationData.getJointForces(); + // for (PxU32 i = 0; i < mArticulationData.getDofs(); ++i) + // { + // jointForce[i] = tempJointForce[i]; + // } + //} + + void FeatherstoneArticulation::updateArticulation(ScratchData& scratchData, + const PxVec3& gravity, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + computeRelativeTransformC2P(mArticulationData); + + computeLinkVelocities(mArticulationData, scratchData); + + initLinks(mArticulationData, gravity, scratchData, Z, DeltaV); + + computeLinkAcceleration(mArticulationData, scratchData); + + //KS - force a recompute of link velocities + ////computeLinkVelocities(mArticulationData, scratchData); + //{ + // ArticulationData& data = mArticulationData; + // ArticulationLink* links = data.getLinks(); + // ArticulationLinkData* linkData = data.getLinkData(); + // const PxU32 linkCount = data.getLinkCount(); + // const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + // //motion velocities has to be in world space to avoid numerical errors caused by space + // Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; + // //Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + + // PxReal* jointVelocities = scratchData.jointVelocities; + + // ArticulationLink& baseLink = links[0]; + // ArticulationLinkData& baseLinkDatum = linkData[0]; + + // PxsBodyCore& core0 = *baseLink.bodyCore; + + // baseLinkDatum.maxPenBias = core0.maxPenBias; + + // for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + // { + // ArticulationLink& link = links[linkID]; + // ArticulationLinkData& linkDatum = linkData[linkID]; + // PxsBodyCore& bodyCore = *link.bodyCore; + + // linkDatum.maxPenBias = bodyCore.maxPenBias; + + // //SpatialTransform p2c = linkDatum.childToParent.getTranspose(); + + // //motionVelocites[linkID] = p2c * motionVelocites[link.parent]; + // //motionVelocites[linkID] = motionVelocites[link.parent]; + // /*motionVelocities[linkID].top = motionVelocities[link.parent].top; + // motionVelocities[linkID].bottom = motionVelocities[link.parent].bottom + motionVelocities[link.parent].top.cross(linkDatum.rw);*/ + + // const PxTransform& body2World = bodyCore.body2World; + // Cm::SpatialVectorF deltaVel; + // deltaVel.top = body2World.rotateInv(motionVelocities[linkID].top - motionVelocities[link.parent].top); + // deltaVel.bottom = body2World.rotateInv(motionVelocities[linkID].bottom - (motionVelocities[link.parent].bottom + motionVelocities[link.parent].top.cross(linkDatum.rw))); + + // PxVec3 ang = motionVelocities[link.parent].top; + // PxVec3 lin = motionVelocities[link.parent].bottom + motionVelocities[link.parent].top.cross(linkDatum.rw); + // + + // if (jointVelocities) + // { + // ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + // //const PxReal* const jV = &jointVelocity[linkID * 6]; + // //PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + + // Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero(); + // for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + // { + // /*if (jVelocity[ind] < -100.f) + // jVelocity[ind] = -100.f; + // else if (jVelocity[ind] > 100.f) + // jVelocity[ind] = 100.f;*/ + // + // PxReal jointVel = Cm::SpatialVectorF(PxVec3(jointDatum.jointAxis[ind][0], jointDatum.jointAxis[ind][1], jointDatum.jointAxis[ind][2]), + // PxVec3(jointDatum.jointAxis[ind][3], jointDatum.jointAxis[ind][4], jointDatum.jointAxis[ind][5])).dot(deltaVel); + + // //PxReal diff = jointVel - jVelocity[ind]; + // //PX_ASSERT(PxAbs(diff) < 1e-3f); + // + // deltaV += jointDatum.motionMatrix[ind] * jointVel; + // //jVelocity[ind] = jointVel; + // } + + // ang += body2World.rotate(deltaV.top); + // lin += body2World.rotate(deltaV.bottom); + // } + + // PxVec3 angDiff = ang - motionVelocities[linkID].top; + // PxVec3 linDiff = lin - motionVelocities[linkID].bottom; + + // motionVelocities[linkID] = Cm::SpatialVectorF(ang, lin); + // } + //} + } + + + PxU32 FeatherstoneArticulation::computeUnconstrainedVelocitiesInternal( + const ArticulationSolverDesc& desc, + PxConstraintAllocator& allocator, + PxSolverConstraintDesc* constraintDesc, + PxU32& acCount, + const PxVec3& gravity, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + PX_PROFILE_ZONE("Articulations:computeUnconstrainedVelocities", 0); + + PX_UNUSED(desc); + ArticulationLink* links = mArticulationData.getLinks(); + const PxU32 linkCount = mArticulationData.getLinkCount(); + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + mArticulationData.init(); + + jcalc(mArticulationData); + + ScratchData scratchData; + scratchData.motionVelocities = mArticulationData.getMotionVelocities(); + scratchData.motionAccelerations = mArticulationData.getMotionAccelerations(); + scratchData.coriolisVectors = mArticulationData.getCorioliseVectors(); + scratchData.spatialZAVectors = mArticulationData.getSpatialZAVectors(); + scratchData.jointAccelerations = mArticulationData.getJointAccelerations(); + scratchData.jointVelocities = mArticulationData.getJointVelocities(); + scratchData.jointPositions = mArticulationData.getJointPositions(); + scratchData.jointForces = mArticulationData.getJointForces(); + scratchData.externalAccels = mArticulationData.getExternalAccelerations(); + + updateArticulation(scratchData, gravity, Z, DeltaV); + + //use individual zero acceleration force(we copy the initial Z value to the transmitted force buffers in initLink()) + scratchData.spatialZAVectors = mArticulationData.getTransmittedForces(); + computeZAForceInv(mArticulationData, scratchData); + computeJointTransmittedFrictionForce(mArticulationData, scratchData, Z, DeltaV); + + //the dirty flag is used in inverse dynamic + mArticulationData.setDataDirty(true); + + //zero zero acceleration vector in the articulation data so that we can use this buffer to accumulated + //impulse for the contacts/constraints in the PGS/TGS solvers + PxMemZero(mArticulationData.getSpatialZAVectors(), sizeof(Cm::SpatialVectorF) * linkCount); + + // solver progress counters + maxSolverNormalProgress = 0; + maxSolverFrictionProgress = 0; + solverProgress = 0; + numTotalConstraints = 0; + + for (PxU32 a = 0; a < mArticulationData.getLinkCount(); ++a) + { + mArticulationData.mAccumulatedPoses[a] = mArticulationData.getLink(a).bodyCore->body2World; + mArticulationData.mPreTransform[a] = mArticulationData.getLink(a).bodyCore->body2World; + mArticulationData.mDeltaQ[a] = PxQuat(PxIdentity); + } + + return setupSolverConstraints(allocator, constraintDesc, links, linkCount, + fixBase, mArticulationData, Z, acCount); + } + + + void FeatherstoneArticulation::computeUnconstrainedVelocitiesTGSInternal(const PxVec3& gravity, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) + { + PX_PROFILE_ZONE("Articulations:computeUnconstrainedVelocitiesSS", 0); + const PxU32 linkCount = mArticulationData.getLinkCount(); + + + mArticulationData.init(); + + jcalc(mArticulationData); + + ScratchData scratchData; + scratchData.motionVelocities = mArticulationData.getMotionVelocities(); + scratchData.motionAccelerations = mArticulationData.getMotionAccelerations(); + scratchData.coriolisVectors = mArticulationData.getCorioliseVectors(); + scratchData.spatialZAVectors = mArticulationData.getSpatialZAVectors(); + scratchData.jointAccelerations = mArticulationData.getJointAccelerations(); + scratchData.jointVelocities = mArticulationData.getJointVelocities(); + scratchData.jointPositions = mArticulationData.getJointPositions(); + scratchData.jointForces = mArticulationData.getJointForces(); + scratchData.externalAccels = mArticulationData.getExternalAccelerations(); + + updateArticulation(scratchData, gravity, Z, DeltaV); + + + scratchData.spatialZAVectors = mArticulationData.getTransmittedForces(); + computeZAForceInv(mArticulationData, scratchData); + computeJointTransmittedFrictionForce(mArticulationData, scratchData, Z, DeltaV); + + + //zero zero acceleration vector in the articulation data so that we can use this buffer to accumulated + //impulse for the contacts/constraints in the PGS/TGS solvers + PxMemZero(mArticulationData.getSpatialZAVectors(), sizeof(Cm::SpatialVectorF) * linkCount); + + //the dirty flag is used in inverse dynamic + mArticulationData.setDataDirty(true); + + for (PxU32 a = 0; a < mArticulationData.getLinkCount(); ++a) + { + mArticulationData.mAccumulatedPoses[a] = mArticulationData.getLink(a).bodyCore->body2World; + mArticulationData.mPreTransform[a] = mArticulationData.getLink(a).bodyCore->body2World; + mArticulationData.mDeltaQ[a] = PxQuat(PxIdentity); + } + mArticulationData.mAccumulatedDt = 0.f; + } + + void FeatherstoneArticulation::enforcePrismaticLimits(PxReal* jPosition, ArticulationJointCore* joint) + { + if (joint->prismaticLimited) + { + if (jPosition[0] < (joint->limits[joint->dofIds[0]].low)) + jPosition[0] = joint->limits[joint->dofIds[0]].low; + + if (jPosition[0] > (joint->limits[joint->dofIds[0]].high)) + jPosition[0] = joint->limits[joint->dofIds[0]].high; + } + } + + PxQuat computeSphericalJointPositions(ArticulationJointCore* joint, + const PxQuat newRot, const PxQuat pBody2WorldRot, + PxReal* jPositions) + { + + PxQuat newParentToChild = (newRot.getConjugate() * pBody2WorldRot).getNormalized(); + + //PxQuat newQ = (pBody2WorldRot * newParentToChild.getConjugate()).getNormalized(); + + const PxQuat cA2w = pBody2WorldRot * joint->parentPose.q; + PxQuat cB2w = newRot * joint->childPose.q; + + if (cA2w.dot(cB2w)<0.0f) // minimum dist quat (equiv to flipping cB2bB.q, which we don't use anywhere) + cB2w = -cB2w; + + const PxQuat cB2cA = cA2w.getConjugate() * cB2w; + + PxQuat twist; //x + PxQuat swing1;//y + PxQuat swing2;//z + separateSwingTwist(cB2cA, twist, swing1, swing2); + //tan(t / 2) = sin(t) / (1 + cos t), so this is the quarter angle + + const PxReal theta0 = PxAtan2(twist.x, (1.f + twist.w)) * 4.f; + const PxReal theta1 = PxAtan2(swing1.y, (1.f + swing1.w)) * 4.f; + const PxReal theta2 = PxAtan2(swing2.z, (1.f + swing2.w)) * 4.f; + + + PxU32 dofIndex = 0; + if (joint->motion[PxArticulationAxis::eTWIST] != PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta0; + if (joint->motion[PxArticulationAxis::eSWING1] != PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta1; + if (joint->motion[PxArticulationAxis::eSWING2] != PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta2; + if (joint->motion[PxArticulationAxis::eTWIST] == PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta0; + if (joint->motion[PxArticulationAxis::eSWING1] == PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta1; + if (joint->motion[PxArticulationAxis::eSWING2] == PxArticulationMotion::eLOCKED) + jPositions[dofIndex++] = theta2; + + return newParentToChild; + } + + void FeatherstoneArticulation::computeAndEnforceJointPositions(ArticulationData& data, PxReal* jointPositions) + { + ArticulationLink* links = data.getLinks(); + const PxU32 linkCount = data.getLinkCount(); + + ArticulationJointCoreData* jointData = data.getJointData(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationJointCore* joint = link.inboundJoint; + ArticulationJointCoreData& jointDatum = jointData[linkID]; + PxReal* jPositions = &jointPositions[jointDatum.jointOffset]; + + if (joint->jointType == PxArticulationJointType::eSPHERICAL) + { + ArticulationLink& pLink = links[link.parent]; + //const PxTransform pBody2World = pLink.bodyCore->body2World; + + computeSphericalJointPositions(joint, link.bodyCore->body2World.q, + pLink.bodyCore->body2World.q, jPositions); + } + else if (joint->jointType == PxArticulationJointType::eREVOLUTE) + { + /*if (jPositions[0] < -PxPi) + jPositions[0] += PxTwoPi; + else if (jPositions[0] > PxPi) + jPositions[0] -= PxTwoPi;*/ + + PxReal jPos = jPositions[0]; + + if (jPos > PxTwoPi) + jPos -= PxTwoPi*2.f; + else if (jPos < -PxTwoPi) + jPos += PxTwoPi*2.f; + + jPos = PxClamp(jPos, -PxTwoPi*2.f, PxTwoPi*2.f); + + jPositions[0] = jPos; + } + else if(joint->jointType == PxArticulationJointType::ePRISMATIC) + { + enforcePrismaticLimits(jPositions, joint); + } + } + } + + void FeatherstoneArticulation::propagateLinksDown(ArticulationData& data, PxReal* jointDeltaVelocities, PxReal* jointPositions, + Cm::SpatialVectorF* motionVelocities) + { + ArticulationLink* links = mArticulationData.getLinks(); + + PxTransform* preTransforms = mArticulationData.getPreTransform(); + PX_UNUSED(preTransforms); + PX_UNUSED(motionVelocities); + + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + const PxReal dt = data.getDt(); + + PxReal* jointVelocities = data.getJointVelocities(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + + ArticulationJointCoreData& jointDatum = jointData[linkID]; + //ArticulationLinkData& linkDatum = mArticulationData.getLinkData(linkID); + //const PxTransform oldTransform = preTransforms[linkID]; + + ArticulationLink& pLink = links[link.parent]; + const PxTransform pBody2World = pLink.bodyCore->body2World; + + ArticulationJointCore* joint = link.inboundJoint; + + PxReal* jVelocity = &jointVelocities[jointDatum.jointOffset]; + PxReal* jDeltaVelocity = &jointDeltaVelocities[jointDatum.jointOffset]; + PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + + PxQuat newParentToChild; + PxQuat newWorldQ; + PxVec3 r; + + + const PxVec3 childOffset = -joint->childPose.p; + const PxVec3 parentOffset = joint->parentPose.p; + + PxTransform& body2World = link.bodyCore->body2World; + + + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + const PxReal delta = (jVelocity[0] + jDeltaVelocity[0]) * dt; + + jPosition[0] += delta; + + enforcePrismaticLimits(jPosition, joint); + + newParentToChild = joint->relativeQuat; + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + const PxVec3& u = jointDatum.motionMatrix[0].bottom; + + r = e + d + u * jPosition[0]; + break; + } + case PxArticulationJointType::eREVOLUTE: + { + //use positional iteration JointVelociy to integrate + const PxReal delta = (jVelocity[0] + jDeltaVelocity[0]) * dt; + + PxReal jPos = jPosition[0] + delta; + + if (jPos > PxTwoPi) + jPos -= PxTwoPi*2.f; + else if (jPos < -PxTwoPi) + jPos += PxTwoPi*2.f; + + jPos = PxClamp(jPos, -PxTwoPi*2.f, PxTwoPi*2.f); + jPosition[0] = jPos; + + const PxVec3& u = jointDatum.motionMatrix[0].top; + + PxQuat jointRotation = PxQuat(-jPosition[0], u); + if (jointRotation.w < 0) //shortest angle. + jointRotation = -jointRotation; + + newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + if (jointDatum.dof < 3) + { + newParentToChild = PxQuat(PxIdentity); + //We are simulating a revolute or 2d joint, so just integrate quaternions and joint positions as above... + for (PxU32 i = 0; i < jointDatum.dof; ++i) + { + const PxReal delta = (jVelocity[i] + jDeltaVelocity[i]) * dt; + + PxReal jPos = jPosition[i] + delta; + + if (jPos > PxTwoPi) + jPos -= PxTwoPi*2.f; + else if (jPos < -PxTwoPi) + jPos += PxTwoPi*2.f; + + jPos = PxClamp(jPos, -PxTwoPi*2.f, PxTwoPi*2.f); + jPosition[i] = jPos; + + const PxVec3& u = jointDatum.motionMatrix[i].top; + + PxQuat jointRotation = PxQuat(-jPosition[i], u); + if (jointRotation.w < 0) //shortest angle. + jointRotation = -jointRotation; + + newParentToChild = newParentToChild * (jointRotation * joint->relativeQuat).getNormalized(); + } + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + + PX_ASSERT(r.isFinite()); + + } + else + { + + const PxTransform oldTransform = preTransforms[linkID]; + + PxVec3 worldAngVel = motionVelocities[linkID].top; + + newWorldQ = Ps::exp(worldAngVel*dt) * oldTransform.q; + + newParentToChild = computeSphericalJointPositions(joint, newWorldQ, + pBody2World.q, jPosition); + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + r = e + d; + } + + break; + } + case PxArticulationJointType::eFIX: + { + //this is fix joint so joint don't have velocity + newParentToChild = joint->relativeQuat; + + const PxVec3 e = newParentToChild.rotate(parentOffset); + const PxVec3 d = childOffset; + + r = e + d; + break; + } + default: + break; + } + + + body2World.q = (pBody2World.q * newParentToChild.getConjugate()).getNormalized(); + body2World.p = pBody2World.p + body2World.q.rotate(r); + + + PX_ASSERT(body2World.isSane()); + PX_ASSERT(body2World.isValid()); + + } + } + + void FeatherstoneArticulation::updateBodies(const ArticulationSolverDesc& desc, PxReal dt) + { + updateBodies(desc, dt, true); + } + + void FeatherstoneArticulation::updateBodiesTGS(const ArticulationSolverDesc& desc, PxReal dt) + { + updateBodies(desc, dt, false); + } + + void FeatherstoneArticulation::updateBodies(const ArticulationSolverDesc& desc, PxReal dt, bool integrateJointPositions) + { + FeatherstoneArticulation* articulation = static_cast(desc.articulation); + ArticulationData& data = articulation->mArticulationData; + ArticulationLink* links = data.getLinks(); + const PxU32 linkCount = data.getLinkCount(); + + Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities(); + + Cm::SpatialVector* externalAccels = data.getExternalAccelerations(); + Cm::SpatialVector zero = Cm::SpatialVector::zero(); + + if (integrateJointPositions) + data.setDt(dt); + else + data.setDt(0.f); + + + + PxTransform* preTransforms = data.getPreTransform(); + + if (articulation->mHasSphericalJoint) + { + for (PxU32 i = 0; i < linkCount; ++i) + { + ArticulationLink& link = links[i]; + + PxsBodyCore* bodyCore = link.bodyCore; + + //record link's previous transform + preTransforms[i] = bodyCore->body2World; + } + } + + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + ArticulationLink& baseLink = links[0]; + + PxsBodyCore* baseBodyCore = baseLink.bodyCore; + + if (!integrateJointPositions) + { + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + links[linkID].bodyCore->body2World = data.mAccumulatedPoses[linkID].getNormalized(); + } + + articulation->computeAndEnforceJointPositions(data, data.getJointPositions()); + } + else + { + + if (!fixBase) + { + + //body2World store new body transform integrated from solver linear/angular velocity + + ArticulationLink& link = links[0]; + + const PxTransform& preTrans = link.bodyCore->body2World; + + Cm::SpatialVectorF& posVel = data.getPosIterMotionVelocity(0); + + updateRootBody(posVel, preTrans, data, dt); + } + else if(!integrateJointPositions) + { + baseBodyCore->body2World = data.mAccumulatedPoses[0]; + } + + //using the original joint velocities and delta velocities changed in the positional iter to update joint position/body transform + articulation->propagateLinksDown(data, data.getPosIterJointDeltaVelocities(), data.getJointPositions(), data.getPosIterMotionVelocities()); + } + + //update joint velocities/accelerations due to contacts/constraints + if (data.mJointDirty && integrateJointPositions) + { + //update delta joint velocity and motion velocity due to velocity iteration changes + Cm::SpatialVectorF deltaV[64]; + + PxcFsFlushVelocity(*articulation, deltaV); + + //update joint velocity/accelerations + PxReal* jointVelocities = data.getJointVelocities(); + PxReal* jointAccelerations = data.getJointAccelerations(); + PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + const PxU32 totalDofs = data.getDofs(); + const PxReal invDt = 1.f / dt; + for (PxU32 i = 0; i < totalDofs; ++i) + { + jointVelocities[i] += jointDeltaVelocities[i]; + jointAccelerations[i] += jointDeltaVelocities[i] * invDt; + } + } + + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + PxsBodyCore* bodyCore = link.bodyCore; + + bodyCore->linearVelocity = motionVelocities[linkID].bottom; + bodyCore->angularVelocity = motionVelocities[linkID].top; + //zero external accelerations + externalAccels[linkID] = zero; + } + } + + + //void FeatherstoneArticulation::updateBodies(const ArticulationSolverDesc& desc, PxReal dt, bool integrateJointPositions) + //{ + // PX_PROFILE_ZONE("Articulations:updateBodies", 0); + // FeatherstoneArticulation* articulation = static_cast(desc.articulation); + // ArticulationData& data = articulation->mArticulationData; + // ArticulationLink* links = data.getLinks(); + // const PxU32 linkCount = data.getLinkCount(); + + // Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities(); + // Cm::SpatialVector* externalAccels = data.getExternalAccelerations(); + // Cm::SpatialVector zero = Cm::SpatialVector::zero(); + + // if (data.mJointDirty || (!integrateJointPositions)) + // { + // //printf("============collision change joint velocity============\n"); + // + // if (data.mJointDirty) + // { + // Cm::SpatialVectorF deltaV[64]; + + // PxcFsFlushVelocity(*articulation, deltaV); + // } + + // PxTransform* preTransforms = data.getPreTransform(); + + // if (integrateJointPositions) + // data.setDt(dt); + // else + // data.setDt(0.f); + + // //update joint velocity/accelerations due to contact + // PxReal* jointVelocities = data.getJointVelocities(); + // PxReal* jointAccelerations = data.getJointAccelerations(); + // PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); + // const PxU32 totalDofs = data.getDofs(); + // const PxReal invDt = 1.f / dt; + // for (PxU32 i = 0; i < totalDofs; ++i) + // { + // jointVelocities[i] += jointDeltaVelocities[i]; + // jointAccelerations[i] += jointDeltaVelocities[i] * invDt; + // //printf("jV %f\n", jointVelocities[i]); + // } + + // if (articulation->mHasSphericalJoint) + // { + // for (PxU32 i = 0; i < linkCount; ++i) + // { + // ArticulationLink& link = links[i]; + + // PxsBodyCore* bodyCore = link.bodyCore; + + // //record link's previous transform + // preTransforms[i] = bodyCore->body2World; + // } + // } + + // const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + // ArticulationLink& baseLink = links[0]; + + // PxsBodyCore* baseBodyCore = baseLink.bodyCore; + + // if (!fixBase) + // { + + // + + // //body2World store new body transform integrated from solver linear/angular velocity + // if (integrateJointPositions) + // { + // ArticulationLink& link = links[0]; + + // const PxTransform& preTrans = link.bodyCore->body2World; + + // Cm::SpatialVectorF& posVel = data.getPosIterMotionVelocity(0); + + // updateRootBody(posVel, preTrans, data, dt); + // } + // else + // { + // baseBodyCore->body2World = data.mAccumulatedPoses[0]; + // } + + // Cm::SpatialVectorF& motionVelocity = motionVelocities[0]; + // PX_ASSERT(motionVelocity.top.isFinite()); + // PX_ASSERT(motionVelocity.bottom.isFinite()); + + // baseBodyCore->linearVelocity = motionVelocity.bottom; + // baseBodyCore->angularVelocity = motionVelocity.top; + // } + + + // articulation->propagateLinksDown(data, data.getPosIterJointDeltaVelocities(), data.getJointPositions(), data.getPosIterMotionVelocities()); + + // //zero all the external accelerations + // externalAccels[0] = zero; + + // for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + // { + // ArticulationLink& link = links[linkID]; + // PxsBodyCore* bodyCore = link.bodyCore; + + // bodyCore->linearVelocity = motionVelocities[linkID].bottom; + // bodyCore->angularVelocity = motionVelocities[linkID].top; + + // externalAccels[linkID] = zero; + // } + // } + // else + // { + // for (PxU32 i = 0; i < linkCount; ++i) + // { + // ArticulationLink& link = links[i]; + + // PxsBodyCore* bodyCore = link.bodyCore; + + // //record link's unconstrainted integrated transform + // bodyCore->body2World = data.mTempData.mLinkTransform[i]; + + // } + + // for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + // { + // ArticulationLink& link = links[linkID]; + // PxsBodyCore* bodyCore = link.bodyCore; + + // bodyCore->linearVelocity = motionVelocities[linkID].bottom; + // bodyCore->angularVelocity = motionVelocities[linkID].top; + // + // //zero all the external accelerations + // externalAccels[linkID] = zero; + // + // } + // } + + //} + + void FeatherstoneArticulation::updateRootBody(const Cm::SpatialVectorF& motionVelocity, + const PxTransform& preTransform, ArticulationData& data, const PxReal dt) + { + ArticulationLink* links = data.getLinks(); + //body2World store new body transform integrated from solver linear/angular velocity + + PX_ASSERT(motionVelocity.top.isFinite()); + PX_ASSERT(motionVelocity.bottom.isFinite()); + + ArticulationLink& baseLink = links[0]; + + PxsBodyCore* baseBodyCore = baseLink.bodyCore; + + //(1) project the current body's velocity (based on its pre-pose) to the geometric COM that we're integrating around... + + PxVec3 comLinVel = motionVelocity.bottom; + + //using the position iteration motion velocity to compute the body2World + PxVec3 newP = (preTransform.p) + comLinVel * dt; + + PxQuat deltaQ = Ps::exp(motionVelocity.top*dt); + + baseBodyCore->body2World = PxTransform(newP, (deltaQ* preTransform.q).getNormalized()); + + //ML: we are going to store the motionVelocity back later + /* baseBodyCore->linearVelocity = motionVelocity.bottom; + baseBodyCore->angularVelocity = motionVelocity.top;*/ + + PX_ASSERT(baseBodyCore->body2World.isFinite() && baseBodyCore->body2World.isValid()); + } + + void FeatherstoneArticulation::updateBodies() + { + ArticulationData& data = mArticulationData; + const PxReal dt = data.getDt(); + + ArticulationLink* links = data.getLinks(); + + Cm::SpatialVectorF* motionVelocities = data.getMotionVelocities(); + PxTransform* preTransforms = data.getPreTransform(); + + const PxU32 linkCount = data.getLinkCount(); + + if (mHasSphericalJoint) + { + for (PxU32 i = 0; i < linkCount; ++i) + { + ArticulationLink& link = links[i]; + + PxsBodyCore* bodyCore = link.bodyCore; + + //record link's previous transform + PX_ASSERT(bodyCore->body2World.isFinite() && bodyCore->body2World.isValid()); + preTransforms[i] = bodyCore->body2World; + } + } + + const bool fixBase = data.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + if (!fixBase) + { + ArticulationLink& link = links[0]; + + const PxTransform& preTrans = link.bodyCore->body2World; + + Cm::SpatialVectorF& motionVelocity = motionVelocities[0]; + + updateRootBody(motionVelocity, preTrans, data, dt); + } + + propagateLinksDown(data, data.getJointVelocities(), data.getJointPositions(), data.getMotionVelocities()); + } + + void FeatherstoneArticulation::getJointAcceleration(const PxVec3& gravity, PxArticulationCache& cache) + { + + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Articulation::getJointAcceleration() commonInit need to be called first to initialize data!"); + return; + } + + const PxU32 linkCount = mArticulationData.getLinkCount(); + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + scratchData.jointVelocities = cache.jointVelocity; + scratchData.jointForces = cache.jointForce; + + computeLinkVelocities(mArticulationData, scratchData); + + //compute individual link's spatial inertia tensor + //[0, M] + //[I, 0] + computeSpatialInertia(mArticulationData); + + //compute inidividual zero acceleration force + computeZ(mArticulationData, gravity, scratchData); + + computeArticulatedSpatialInertia(mArticulationData); + + //compute corolis and centrifugal force + computeC(mArticulationData, scratchData); + + computeArticulatedSpatialZ(mArticulationData, scratchData); + + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + //we have initialized motionVelocity and motionAcceleration to be zero in the root link if + //fix based flag is raised + ArticulationLinkData& baseLinkDatum = mArticulationData.getLinkData(0); + + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; + Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors; + + if (!fixBase) + { + SpatialMatrix inverseArticulatedInertia = baseLinkDatum.spatialArticulatedInertia.getInverse(); + motionAccelerations[0] = -(inverseArticulatedInertia * spatialZAForces[0]); + } +#if FEATHERSTONE_DEBUG + else + { + PX_ASSERT(isSpatialVectorZero(motionAccelerations[0])); + } +#endif + + PxReal* jointAccelerations = cache.jointAcceleration; + //calculate acceleration + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = mArticulationData.getLink(linkID); + + ArticulationLinkData& linkDatum = mArticulationData.getLinkData(linkID); + + //SpatialTransform p2C = linkDatum.childToParent.getTranspose(); + Cm::SpatialVectorF pMotionAcceleration = linkDatum.childToParent.transposeTransform(motionAccelerations[link.parent]); + + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + //calculate jointAcceleration + PxReal* jA = &jointAccelerations[jointDatum.jointOffset]; + computeJointAcceleration(linkDatum, jointDatum, pMotionAcceleration, jA); + + Cm::SpatialVectorF motionAcceleration(PxVec3(0.f), PxVec3(0.f)); + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + motionAcceleration += jointDatum.motionMatrix[ind] * jA[ind]; + } + + motionAccelerations[linkID] = pMotionAcceleration + coriolisVectors[linkID] + motionAcceleration; + PX_ASSERT(motionAccelerations[linkID].isFinite()); + } + + allocator->free(tempMemory); + } + +}//namespace Dy +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp new file mode 100644 index 000000000..5d45c0b82 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp @@ -0,0 +1,2120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsMathUtils.h" +#include "CmConeLimitHelper.h" +#include "DySolverConstraint1D.h" +#include "DyFeatherstoneArticulation.h" +#include "PxsRigidBody.h" +#include "PxcConstraintBlockStream.h" +#include "DyArticulationContactPrep.h" +#include "DyDynamics.h" +#include "DyArticulationReference.h" +#include "DyArticulationPImpl.h" +#include "foundation/PxProfiler.h" +#include "DyArticulationFnsSimd.h" +#include "PsFoundation.h" +#include "extensions/PxContactJoint.h" +#include "DyFeatherstoneArticulationLink.h" +#include "DyFeatherstoneArticulationJointData.h" +#include "DyConstraint.h" +#include "DyConstraintPrep.h" +#include "DySolverContext.h" + +namespace physx +{ + +namespace Dy +{ + void PxcFsFlushVelocity(FeatherstoneArticulation& articulation, Cm::SpatialVectorF* deltaV); + + void FeatherstoneArticulation::computeLinkAccelerationInv(ArticulationData& data, ScratchData& scratchData) + { + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + + Cm::SpatialVectorF* coriolisVectors = scratchData.coriolisVectors; + + PxReal* jointAccelerations = scratchData.jointAccelerations; + + motionAccelerations[0] = Cm::SpatialVectorF::Zero(); + + for (PxU32 linkID = 1; linkID < data.getLinkCount(); ++linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + SpatialTransform p2c = linkDatum.childToParent.getTranspose(); + + //parent's motion acceleration into child space + Cm::SpatialVectorF pMotionAcceleration = p2c * motionAccelerations[link.parent]; + + Cm::SpatialVectorF motionAcceleration(PxVec3(0.f), PxVec3(0.f)); + + if (jointAccelerations) + { + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + const PxReal* jAcceleration = &jointAccelerations[jointDatum.jointOffset]; + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + motionAcceleration += jointDatum.motionMatrix[ind] * jAcceleration[ind]; + } + } + + motionAccelerations[linkID] = pMotionAcceleration + coriolisVectors[linkID] + motionAcceleration; + } + } + + //generalized force + void FeatherstoneArticulation::computeGeneralizedForceInv(ArticulationData& data, ScratchData& scratchData) + { + const PxU32 linkCount = data.getLinkCount(); + + Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; + PxReal* jointForces = scratchData.jointForces; + + for (PxU32 linkID = (linkCount - 1); linkID > 0; --linkID) + { + ArticulationLink& link = data.getLink(linkID); + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + //joint force + //pLink.spatialZAForce += link.childToParent * link.spatialZAForce; + spatialZAForces[link.parent] += linkDatum.childToParent * spatialZAForces[linkID]; + + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + //compute generalized force + PxReal* force = &jointForces[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + force[ind] = jointDatum.motionMatrix[ind].innerProduct(spatialZAForces[linkID]); + } + } + } + + void FeatherstoneArticulation::computeZAForceInv(ArticulationData& data, ScratchData& scratchData) + { + const PxU32 linkCount = data.getLinkCount(); + + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + + Cm::SpatialVectorF* biasForce = scratchData.spatialZAVectors; + + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + ArticulationLinkData& linkDatum = data.getLinkData(linkID); + + Cm::SpatialVectorF Ia = linkDatum.spatialInertia * motionAccelerations[linkID]; + + biasForce[linkID] +=Ia; + } + } + + void FeatherstoneArticulation::initCompositeSpatialInertia(ArticulationData& data, Dy::SpatialMatrix* compositeSpatialInertia) + { + ArticulationLinkData* linkData = data.getLinkData(); + const PxU32 linkCount = data.getLinkCount(); + + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + ArticulationLinkData& linkDatum = linkData[linkID]; + compositeSpatialInertia[linkID] = linkDatum.spatialInertia; + } + } + + void FeatherstoneArticulation::computeCompositeSpatialInertiaAndZAForceInv(ArticulationData& data, ScratchData& scratchData) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + const PxU32 linkCount = data.getLinkCount(); + const PxU32 startIndex = PxU32(linkCount - 1); + + Dy::SpatialMatrix* compositeSpatialInertia = scratchData.compositeSpatialInertias; + Cm::SpatialVectorF* zaForce = scratchData.spatialZAVectors; + + initCompositeSpatialInertia(data, compositeSpatialInertia); + + for (PxU32 linkID = startIndex; linkID > 0; --linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + + const SpatialTransform& c2p = linkDatum.childToParent; + const SpatialTransform p2c = linkDatum.childToParent.getTranspose(); + + Dy::SpatialMatrix cSpatialInertia = compositeSpatialInertia[linkID]; + + PxMat33 tl = c2p.R * cSpatialInertia.topLeft; + PxMat33 tr = c2p.R * cSpatialInertia.topRight; + PxMat33 bl = c2p.T * cSpatialInertia.topLeft + c2p.R * cSpatialInertia.bottomLeft; + PxMat33 br = c2p.T * cSpatialInertia.topRight + c2p.R * cSpatialInertia.getBottomRight(); + + cSpatialInertia.topLeft = tl * p2c.R + tr * p2c.T; + cSpatialInertia.topRight = tr * p2c.R; + cSpatialInertia.bottomLeft = bl * p2c.R + br * p2c.T; + + //aligned inertia + cSpatialInertia.bottomLeft = (cSpatialInertia.bottomLeft + cSpatialInertia.bottomLeft.getTranspose()) * 0.5f; + + //compute parent's composite spatial inertia + compositeSpatialInertia[link.parent] += cSpatialInertia; + + //compute zero acceleration force. This is the force that would be required to support the + //motion of all the bodies in childen set if root node acceleration happened to be zero + zaForce[link.parent] += c2p * zaForce[linkID]; + } + } + + void FeatherstoneArticulation::computeRelativeGeneralizedForceInv(ArticulationData& data, ScratchData& scratchData) + { + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + Dy::SpatialMatrix* compositeSpatialInertia = scratchData.compositeSpatialInertias; + Cm::SpatialVectorF* zaForce = scratchData.spatialZAVectors; + PxReal* jointForces = scratchData.jointForces; + + Dy::SpatialMatrix invInertia = compositeSpatialInertia[0].invertInertia(); + motionAccelerations[0] = -(invInertia * zaForce[0]); + + const PxU32 linkCount = data.getLinkCount(); + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = links[linkID]; + ArticulationLinkData& linkDatum = linkData[linkID]; + + const SpatialTransform& p2c = linkDatum.childToParent.getTranspose(); + + motionAccelerations[linkID] = p2c * motionAccelerations[link.parent]; + + + zaForce[linkID] = compositeSpatialInertia[linkID] * motionAccelerations[linkID] + zaForce[linkID]; + ArticulationJointCoreData& jointDatum = data.getJointData(linkID); + //compute generalized force + PxReal* jForce = &jointForces[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + jForce[ind] = jointDatum.motionMatrix[ind].innerProduct(zaForce[linkID]); + } + } + } + + void FeatherstoneArticulation::inverseDynamic(ArticulationData& data, const PxVec3& gravity, + ScratchData& scratchData) + { + //pass 1 + computeLinkVelocities(data, scratchData); + + computeC(data, scratchData); + + computeZ(data, gravity, scratchData); + + computeLinkAccelerationInv(data, scratchData); + + computeZAForceInv(data, scratchData); + + //pass 2 + computeGeneralizedForceInv(data, scratchData); + } + + void FeatherstoneArticulation::inverseDynamicFloatingBase(ArticulationData& data, const PxVec3& gravity, + ScratchData& scratchData) + { + //pass 1 + computeLinkVelocities(data, scratchData); + + computeC(data, scratchData); + + computeZ(data, gravity, scratchData); + //no gravity, no external accelerations because we have turned those in force in + //computeZ + computeLinkAccelerationInv(data, scratchData); + + computeZAForceInv(data, scratchData); + + //pass 2 + computeCompositeSpatialInertiaAndZAForceInv(data, scratchData); + + //pass 3 + computeRelativeGeneralizedForceInv(data, scratchData); + } + + + void FeatherstoneArticulation::applyCacheToDest(ArticulationData& data, PxArticulationCache& cache, + PxReal* jVelocities, PxReal* jAccelerations, PxReal* jPositions, PxReal* jointForces, + const PxArticulationCacheFlags flag) + { + if (flag & PxArticulationCache::eVELOCITY) + { + copyJointData(data, jVelocities, cache.jointVelocity); + } + + if (flag & PxArticulationCache::eACCELERATION) + { + copyJointData(data, jAccelerations, cache.jointAcceleration); + } + + if (flag & PxArticulationCache::eROOT) + { + ArticulationLink& rLink = mArticulationData.getLink(0); + rLink.bodyCore->body2World = cache.rootLinkData.transform; + rLink.bodyCore->linearVelocity = cache.rootLinkData.linVel; + rLink.bodyCore->angularVelocity = cache.rootLinkData.angVel; + } + + if (flag & PxArticulationCache::ePOSITION) + { + copyJointData(data, jPositions, cache.jointPosition); + //update link's position based on the joint position + teleportLinks(data); + } + + if (flag & PxArticulationCache::eFORCE) + { + copyJointData(data, jointForces, cache.jointForce); + } + } + + void FeatherstoneArticulation::packJointData(const PxReal* maximum, PxReal* reduced) + { + const PxU32 linkCount = mArticulationData.getLinkCount(); + + for (PxU32 linkID = 1; linkID < linkCount; linkID++) + { + + ArticulationLink& linkDatum = mArticulationData.getLink(linkID); + ArticulationJointCore* joint = linkDatum.inboundJoint; + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + + const PxReal* maxJointData = &maximum[(linkID - 1) * DY_MAX_DOF]; + PxReal* reducedJointData = &reduced[jointDatum.jointOffset]; + + PxU32 count = 0; + for (PxU32 j = 0; j < DY_MAX_DOF; ++j) + { + PxArticulationMotions motion = joint->motion[j]; + if (motion != PxArticulationMotion::eLOCKED) + { + reducedJointData[count] = maxJointData[j]; + count++; + } + } + + PX_ASSERT(count == jointDatum.dof); + } + + } + + void FeatherstoneArticulation::unpackJointData(const PxReal* reduced, PxReal* maximum) + { + const PxU32 linkCount = mArticulationData.getLinkCount(); + + for (PxU32 linkID = 1; linkID < linkCount; linkID++) + { + ArticulationLink& linkDatum = mArticulationData.getLink(linkID); + ArticulationJointCore* joint = linkDatum.inboundJoint; + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + + PxReal* maxJointData = &maximum[(linkID - 1) * DY_MAX_DOF]; + const PxReal* reducedJointData = &reduced[jointDatum.jointOffset]; + + PxU32 count = 0; + for (PxU32 j = 0; j < DY_MAX_DOF; ++j) + { + PxArticulationMotions motion = joint->motion[j]; + if (motion != PxArticulationMotion::eLOCKED) + { + maxJointData[j] = reducedJointData[count]; + count++; + } + else + { + maxJointData[j] = 0.f; + } + } + + PX_ASSERT(count == jointDatum.dof); + } + } + + void FeatherstoneArticulation::initializeCommonData() + { + computeRelativeTransformC2P(mArticulationData); + + computeRelativeTransformC2B(mArticulationData); + + jcalc(mArticulationData); + + computeSpatialInertia(mArticulationData); + + mArticulationData.setDataDirty(false); + } + + void FeatherstoneArticulation::getGeneralizedGravityForce(const PxVec3& gravity, PxArticulationCache& cache) + { + + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Articulation::getGeneralisedGravityForce() commonInit need to be called first to initialize data!"); + return; + } + +#if FEATHERSTONE_DEBUG + PxReal* jointForce = reinterpret_cast(PX_ALLOC(sizeof(PxReal) * mArticulationData.getDofs(), "jointForce")); + { + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + scratchData.jointVelocities = NULL; + scratchData.jointAccelerations = NULL; + scratchData.jointForces = jointForce; + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + if (fixBase) + inverseDynamic(mArticulationData, gravity, NULL, scratchData); + else + inverseDynamicFloatingLink(mArticulationData, gravity, NULL, scratchData); + + allocator->free(tempMemory); + } +#endif + + const PxVec3 tGravity = -gravity; + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + const PxU32 linkCount = mArticulationData.getLinkCount(); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + if (fixBase) + { + Cm::SpatialVectorF* spatialZAForces = reinterpret_cast(allocator->alloc(sizeof(Cm::SpatialVectorF) * linkCount)); + + for (PxU32 linkID = 0; linkID < linkCount; ++linkID) + { + ArticulationLink& link = mArticulationData.getLink(linkID); + + PxsBodyCore& core = *link.bodyCore; + + const PxTransform& body2World = core.body2World; + + const PxReal m = 1.0f / core.inverseMass; + + const PxVec3 linkGravity = body2World.rotateInv(tGravity); + + spatialZAForces[linkID].top = m*linkGravity; + spatialZAForces[linkID].bottom = PxVec3(0.f); + } + + ScratchData scratchData; + scratchData.spatialZAVectors = spatialZAForces; + scratchData.jointForces = cache.jointForce; + + computeGeneralizedForceInv(mArticulationData, scratchData); + + //release spatialZA vectors + allocator->free(spatialZAForces); + } + else + { + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + scratchData.jointVelocities = NULL; + scratchData.jointAccelerations = NULL; + scratchData.jointForces = cache.jointForce; + scratchData.externalAccels = NULL; + + inverseDynamicFloatingBase(mArticulationData, tGravity, scratchData); + + allocator->free(tempMemory); + } + +#if FEATHERSTONE_DEBUG + //compare joint force + const PxU32 totalDofs = mArticulationData.getDofs(); + for (PxU32 i = 0; i < totalDofs; ++i) + { + const PxReal dif = jointForce[i] - cache.jointForce[i]; + PX_ASSERT(PxAbs(dif) < 5e-3f); + } + + PX_FREE(jointForce); +#endif + + } + + //gravity, acceleration and external force(external acceleration) are zero + void FeatherstoneArticulation::getCoriolisAndCentrifugalForce(PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Articulation::getCoriolisAndCentrifugalForce() commonInit need to be called first to initialize data!"); + return; + } + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + scratchData.jointVelocities = cache.jointVelocity; + scratchData.jointAccelerations = NULL; + scratchData.jointForces = cache.jointForce; + scratchData.externalAccels = NULL; + + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + if (fixBase) + inverseDynamic(mArticulationData, PxVec3(0.f), scratchData); + else + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + + allocator->free(tempMemory); + + } + + //gravity, joint acceleration and joint velocity are zero + void FeatherstoneArticulation::getGeneralizedExternalForce(PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Articulation::getCoriolisAndCentrifugalForce() commonInit need to be called first to initialize data!"); + return; + } + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + scratchData.jointVelocities = NULL; + scratchData.jointAccelerations = NULL; + scratchData.jointForces = cache.jointForce; + + Cm::SpatialVector* accels = reinterpret_cast(allocator->alloc(sizeof(Cm::SpatialVector) * linkCount)); + + //turn external forces to external accels + for (PxU32 i = 0; i < linkCount; ++i) + { + ArticulationLink& link = mArticulationData.getLink(i); + PxsBodyCore& core = *link.bodyCore; + + Cm::SpatialVector& force = cache.externalForces[i]; + Cm::SpatialVector& accel = accels[i]; + + accel.linear = force.linear * core.inverseMass; + + PxMat33 inverseInertiaWorldSpace; + Cm::transformInertiaTensor(core.inverseInertia, PxMat33(core.body2World.q), inverseInertiaWorldSpace); + + accel.angular = inverseInertiaWorldSpace * force.angular; + } + + scratchData.externalAccels = accels; + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + if (fixBase) + inverseDynamic(mArticulationData, PxVec3(0.f), scratchData); + else + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + + allocator->free(tempMemory); + allocator->free(accels); + + } + + //provided joint acceleration, calculate joint force + void FeatherstoneArticulation::getJointForce(PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "ArticulationHelper::getJointForce() commonInit need to be called first to initialize data!"); + return; + } + + //const PxU32 size = sizeof(PxReal) * mArticulationData.getDofs(); + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + //PxReal* jointVelocities = reinterpret_cast(allocator->alloc(size)); + + + ScratchData scratchData; + scratchData.jointVelocities = NULL;//jont velocity will be zero + scratchData.jointAccelerations = cache.jointAcceleration; //input + scratchData.jointForces = cache.jointForce; //output + scratchData.externalAccels = NULL; + + PxU8* tempMemory = allocateScratchSpatialData(allocator, mArticulationData.getLinkCount(), scratchData); + + //make sure joint velocity be zero + //PxMemZero(jointVelocities, sizeof(PxReal) * mArticulationData.getDofs()); + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + if (fixBase) + inverseDynamic(mArticulationData, PxVec3(0.f), scratchData); + else + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + + //allocator->free(jointVelocities); + allocator->free(tempMemory); + } + + static void computeJacobian(PxKinematicJacobian& jacobian, ArticulationJointCoreData& jointDatum, + const PxTransform& body2World) + { + for (PxU32 ind = 0; ind < jointDatum.motionMatrix.getNumColumns(); ++ind) + { + PxReal* jacoColumn = jacobian.j[ind]; + + Cm::SpatialVectorF& column = jointDatum.motionMatrix[0]; + + const PxVec3 wAngular = body2World.rotate(column.top); + const PxVec3 wLinear = body2World.rotate(column.bottom); + + jacoColumn[0] = wAngular.x; jacoColumn[1] = wAngular.y; jacoColumn[2] = wAngular.z; + jacoColumn[3] = wLinear.x; jacoColumn[4] = wLinear.y; jacoColumn[5] = wLinear.z; + } + } + + void FeatherstoneArticulation::getKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) + { + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxMemZero(cache.jacobian, sizeof(PxKinematicJacobian) * linkCount); + + ArticulationLink& endEffectorLink = mArticulationData.getLink(linkID); + + jcalc(mArticulationData); + + for (ArticulationBitField i = endEffectorLink.pathToRoot - 1; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + + ArticulationLink& tLink = mArticulationData.getLink(linkID); + ArticulationJointCoreData& tJointData = mArticulationData.getJointData(linkID); + + PxTransform& body2World = tLink.bodyCore->body2World; + PxKinematicJacobian& jacobian = cache.jacobian[index]; + computeJacobian(jacobian, tJointData, body2World); + } + } + + void FeatherstoneArticulation::jcalcLoopJointSubspace(ArticulationJointCore* joint, + ArticulationJointCoreData& jointDatum, SpatialSubspaceMatrix& T) + { + const PxVec3 childOffset = -joint->childPose.p; + const PxVec3 zero(0.f); + + //if the column is free, we put zero for it, this is for computing K(coefficent matrix) + T.setNumColumns(6); + + //transpose(Tc)*S = 0 + //transpose(Ta)*S = 1 + switch (joint->jointType) + { + case PxArticulationJointType::ePRISMATIC: + { + PX_ASSERT(jointDatum.dof == 1); + + const PxVec3 rx = (joint->childPose.rotate(PxVec3(1.f, 0.f, 0.f))).getNormalized(); + const PxVec3 ry = (joint->childPose.rotate(PxVec3(0.f, 1.f, 0.f))).getNormalized(); + const PxVec3 rz = (joint->childPose.rotate(PxVec3(0.f, 0.f, 1.f))).getNormalized(); + + + //joint->activeForceSubspace.setNumColumns(1); + + if (jointDatum.jointAxis[0][3] == 1.f) + { + //x is the free translation axis + T.setColumn(0, rx, zero); + T.setColumn(1, ry, zero); + T.setColumn(2, rz, zero); + T.setColumn(3, zero, zero); + T.setColumn(4, zero, ry); + T.setColumn(5, zero, rz); + + //joint->activeForceSubspace.setColumn(0, PxVec3(0.f), rx); + } + else if (jointDatum.jointAxis[0][4] == 1.f) + { + //y is the free translation axis + T.setColumn(0, rx, zero); + T.setColumn(1, ry, zero); + T.setColumn(2, rz, zero); + T.setColumn(3, zero, rx); + T.setColumn(4, zero, zero); + T.setColumn(5, zero, rz); + + //joint->activeForceSubspace.setColumn(0, PxVec3(0.f), ry); + } + else if (jointDatum.jointAxis[0][5] == 1.f) + { + //z is the free translation axis + T.setColumn(0, rx, zero); + T.setColumn(1, ry, zero); + T.setColumn(2, rx, zero); + T.setColumn(3, zero, rx); + T.setColumn(4, zero, ry); + T.setColumn(5, zero, zero); + + //joint->activeForceSubspace.setColumn(0, PxVec3(0.f), rz); + } + + break; + } + case PxArticulationJointType::eREVOLUTE: + { + //joint->activeForceSubspace.setNumColumns(1); + + const PxVec3 rx = (joint->childPose.rotate(PxVec3(1.f, 0.f, 0.f))).getNormalized(); + const PxVec3 ry = (joint->childPose.rotate(PxVec3(0.f, 1.f, 0.f))).getNormalized(); + const PxVec3 rz = (joint->childPose.rotate(PxVec3(0.f, 0.f, 1.f))).getNormalized(); + + const PxVec3 rxXd = rx.cross(childOffset); + const PxVec3 ryXd = ry.cross(childOffset); + const PxVec3 rzXd = rz.cross(childOffset); + + if (jointDatum.jointAxis[0][0] == 1.f) + { + //x is the free rotation axis + + T.setColumn(0, zero, zero); + T.setColumn(1, ry, zero); + T.setColumn(2, rz, zero); + + //joint->activeForceSubspace.setColumn(0, rx, PxVec3(0.f)); + + } + else if (jointDatum.jointAxis[0][1] == 1.f) + { + //y is the free rotation axis + T.setColumn(0, rx, zero); + T.setColumn(1, zero, zero); + T.setColumn(2, rz, zero); + + //joint->activeForceSubspace.setColumn(0, ry, PxVec3(0.f)); + } + else if (jointDatum.jointAxis[0][2] == 1.f) + { + //z is the rotation axis + T.setColumn(0, rx, zero); + T.setColumn(1, ry, zero); + T.setColumn(2, zero, zero); + + //joint->activeForceSubspace.setColumn(0, rz, PxVec3(0.f)); + } + + T.setColumn(3, rxXd, rx); + T.setColumn(4, ryXd, ry); + T.setColumn(5, rzXd, rz); + + break; + } + case PxArticulationJointType::eSPHERICAL: + { + //joint->activeForceSubspace.setNumColumns(3); + + const PxVec3 rx = (joint->childPose.rotate(PxVec3(1.f, 0.f, 0.f))).getNormalized(); + const PxVec3 ry = (joint->childPose.rotate(PxVec3(0.f, 1.f, 0.f))).getNormalized(); + const PxVec3 rz = (joint->childPose.rotate(PxVec3(0.f, 0.f, 1.f))).getNormalized(); + + const PxVec3 rxXd = rx.cross(childOffset); + const PxVec3 ryXd = ry.cross(childOffset); + const PxVec3 rzXd = rz.cross(childOffset); + + T.setColumn(0, zero, zero); + T.setColumn(1, zero, zero); + T.setColumn(2, zero, zero); + + T.setColumn(3, rxXd, rx); + T.setColumn(4, ryXd, ry); + T.setColumn(5, rzXd, rz); + + //need to implement constraint force subspace matrix and active force subspace matrix + + break; + } + case PxArticulationJointType::eFIX: + { + //joint->activeForceSubspace.setNumColumns(0); + //T.setNumColumns(6); + + /* const PxVec3 rx = (joint->childPose.rotate(PxVec3(1.f, 0.f, 0.f))).getNormalized(); + const PxVec3 ry = (joint->childPose.rotate(PxVec3(0.f, 1.f, 0.f))).getNormalized(); + const PxVec3 rz = (joint->childPose.rotate(PxVec3(0.f, 0.f, 1.f))).getNormalized(); + + T.setColumn(0, rx, PxVec3(0.f)); + T.setColumn(1, ry, PxVec3(0.f)); + T.setColumn(2, rz, PxVec3(0.f)); + T.setColumn(3, PxVec3(0.f), rx); + T.setColumn(4, PxVec3(0.f), ry); + T.setColumn(5, PxVec3(0.f), rz); + */ + + T.setColumn(0, PxVec3(1.f, 0.f, 0.f), zero); + T.setColumn(1, PxVec3(0.f, 1.f, 0.f), zero); + T.setColumn(2, PxVec3(0.f, 0.f, 1.f), zero); + T.setColumn(3, zero, PxVec3(1.f, 0.f, 0.f)); + T.setColumn(4, zero, PxVec3(0.f, 1.f, 0.f)); + T.setColumn(5, zero, PxVec3(0.f, 0.f, 1.f)); + + PX_ASSERT(jointDatum.dof == 0); + break; + } + default: + break; + + } + } + + //This method supports just one loopJoint + void FeatherstoneArticulation::getKMatrix(ArticulationJointCore* loopJoint, const PxU32 parentIndex, const PxU32 childIndex, PxArticulationCache& cache) + { + PX_UNUSED(loopJoint); + PX_UNUSED(parentIndex); + PX_UNUSED(childIndex); + PX_UNUSED(cache); + + ////initialize all tree links motion subspace matrix + //jcalc(mArticulationData); + + ////linkID is the parent link, ground is the child link so child link is the fix base + //ArticulationLinkData& pLinkDatum = mArticulationData.getLinkData(parentIndex); + + //ArticulationLink& cLink = mArticulationData.getLink(childIndex); + //ArticulationLinkData& cLinkDatum = mArticulationData.getLinkData(childIndex); + // + //ArticulationJointCoreData loopJointDatum; + //loopJointDatum.computeJointDof(loopJoint); + + ////this is constraintForceSubspace in child body space(T) + //SpatialSubspaceMatrix T; + + ////loop joint constraint subspace matrix(T) + //jcalcLoopJointSubspace(loopJoint, loopJointDatum, T); + + //const PxU32 linkCount = mArticulationData.getLinkCount(); + ////set Jacobian matrix to be zero + //PxMemZero(cache.jacobian, sizeof(PxKinematicJacobian) * linkCount); + + ////transform T to world space + //PxTransform& body2World = cLink.bodyCore->body2World; + + //for (PxU32 ind = 0; ind < T.getNumColumns(); ++ind) + //{ + // Cm::SpatialVectorF& column = T[ind]; + // T.setColumn(ind, body2World.rotate(column.top), body2World.rotate(column.bottom)); + //} + + //const Cm::SpatialVectorF& pAccel = pLinkDatum.motionAcceleration; + //const Cm::SpatialVectorF& cAccel = cLinkDatum.motionAcceleration; + + //const Cm::SpatialVectorF& pVel = pLinkDatum.motionVelocity; + //const Cm::SpatialVectorF& cVel = cLinkDatum.motionVelocity; + + //Cm::SpatialVectorF k = (pAccel - cAccel) + pVel.cross(cVel); + //k = T.transposeMultiply(k); + //k = -k; + + //PxU32 i = childIndex; + //PxU32 j = parentIndex; + + //PxU32* index = NULL; + + //while (i != j) + //{ + // if (i > j) + // index = &i; + // else + // index = &j; + + // const PxU32 linkIndex = *index; + + // PxKinematicJacobian* K = cache.jacobian + linkIndex; + + // ArticulationLink& link = mArticulationData.getLink(linkIndex); + + // ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkIndex); + + // SpatialSubspaceMatrix& S = jointDatum.motionMatrix; + + // PxTransform& tBody2World = link.bodyCore->body2World; + + // Cm::SpatialVectorF res; + // for (PxU32 ind = 0; ind < S.getNumColumns(); ++ind) + // { + // Cm::SpatialVectorF& sCol = S[ind]; + + // //transform spatial axis into world space + // sCol.top = tBody2World.rotate(sCol.top); + // sCol.bottom = tBody2World.rotate(sCol.bottom); + + // res = T.transposeMultiply(sCol); + // res = -res; + + // PxReal* kSubMatrix = K->j[ind]; + + // kSubMatrix[0] = res.top.x; kSubMatrix[1] = res.top.y; kSubMatrix[2] = res.top.z; + // kSubMatrix[3] = res.bottom.x; kSubMatrix[4] = res.bottom.y; kSubMatrix[5] = res.bottom.z; + // } + + // //overwrite either i or j to its parent index + // *index = link.parent; + //} + } + + + void FeatherstoneArticulation::getCoefficentMatrix(const PxReal dt, const PxU32 linkID, const PxContactJoint* contactJoints, const PxU32 nbContacts, PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "ArticulationHelper::getCoefficentMatrix() commonInit need to be called first to initialize data!"); + return; + } + + computeArticulatedSpatialInertia(mArticulationData); + + ArticulationLink* links = mArticulationData.getLinks(); + + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxReal* coefficentMatrix = cache.coefficentMatrix; + + const PxU32 elementCount = mArticulationData.getDofs(); + + //zero coefficent matrix + PxMemZero(coefficentMatrix, sizeof(PxReal) * elementCount * nbContacts); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + + for (PxU32 a = 0; a < nbContacts; ++a) + { + + PxJacobianRow row; + contactJoints[a].computeJacobians(&row); + + //impulse lin is contact normal, and ang is raxn. R is body2World, R(t) is world2Body + //| R(t), 0 | + //| R(t)*r, R(t)| + //r is the vector from center of mass to contact point + //p(impluse) = |n| + // |0| + + //transform p(impluse) from work space to the local space of link + ArticulationLink& link = links[linkID]; + PxTransform& body2World = link.bodyCore->body2World; + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + Cm::SpatialVectorF* Z = scratchData.spatialZAVectors; + + //make sure all links' spatial zero acceleration impulse are zero + PxMemZero(Z, sizeof(Cm::SpatialVectorF) * linkCount); + + Cm::SpatialVectorF impl = Cm::SpatialVectorF(body2World.rotateInv(row.linear0), body2World.rotateInv(row.angular0)); + + getZ(linkID, mArticulationData, Z, impl); + + const PxU32 totalDofs = mArticulationData.getDofs(); + + const PxU32 size = sizeof(PxReal) * totalDofs; + + PxU8* tData = reinterpret_cast(allocator->alloc(size * 2)); + + PxReal* jointVelocities = reinterpret_cast(tData); + PxReal* jointAccelerations = reinterpret_cast(tData + size); + //zero joint Velocites + PxMemZero(jointVelocities, size); + + getDeltaVWithDeltaJV(fixBase, linkID, mArticulationData, Z, jointVelocities); + + const PxReal invDt = 1.f / dt; + //calculate joint acceleration due to velocity change + for (PxU32 i = 0; i < totalDofs; ++i) + { + jointAccelerations[i] = jointVelocities[i] * invDt; + } + + //compute individual link's spatial inertia tensor. This is very important + computeSpatialInertia(mArticulationData); + + PxReal* coeCol = &coefficentMatrix[elementCount * a]; + + //this means the joint force calculated by the inverse dynamic + //will be just influenced by joint acceleration change + scratchData.jointVelocities = NULL; + scratchData.externalAccels = NULL; + + //Input + scratchData.jointAccelerations = jointAccelerations; + + //a column of the coefficent matrix is the joint force + scratchData.jointForces = coeCol; + + if (fixBase) + { + inverseDynamic(mArticulationData, PxVec3(0.f), scratchData); + } + else + { + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + } + + allocator->free(tData); + allocator->free(tempMemory); + } + } + + void FeatherstoneArticulation::getImpulseResponseSlowInv(Dy::ArticulationLink* links, + const ArticulationData& data, + PxU32 linkID0_, + const Cm::SpatialVector& impulse0, + Cm::SpatialVector& deltaV0, + PxU32 linkID1_, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV1, + PxReal* jointVelocities) + { + PX_UNUSED(jointVelocities); + PxU32 stack[DY_ARTICULATION_MAX_SIZE]; + Cm::SpatialVectorF Z[DY_ARTICULATION_MAX_SIZE]; + //Cm::SpatialVectorF ZZV[DY_ARTICULATION_MAX_SIZE]; + + PxU32 i0, i1, ic; + + PxU32 linkID0 = linkID0_; + PxU32 linkID1 = linkID1_; + + + const PxTransform& transform0 = data.getLink(linkID0_).bodyCore->body2World; + const PxTransform& transform1 = data.getLink(linkID1_).bodyCore->body2World; + + for (i0 = linkID0, i1 = linkID1; i0 != i1;) // find common path + { + if (i0flags & PxArticulationFlag::eFIX_BASE) + { + Z[0] = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + } + + //SpatialMatrix inverseArticulatedInertia = data.getLinkData(0).spatialArticulatedInertia.getInverse(); + const SpatialMatrix& inverseArticulatedInertia = data.getBaseInvSpatialArticulatedInertia(); + Cm::SpatialVectorF v = inverseArticulatedInertia * (-Z[0]); + + for (PxU32 index = ic; (index--) > i1;) + v = FeatherstoneArticulation::propagateVelocity(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], jointVelocities, v); + + Cm::SpatialVectorF dv1 = v; + for (PxU32 index = i1; (index--) > i0;) + dv1 = FeatherstoneArticulation::propagateVelocity(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], jointVelocities, dv1); + + Cm::SpatialVectorF dv0 = v; + for (PxU32 index = i0; (index--) > 0;) + dv0 = FeatherstoneArticulation::propagateVelocity(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], jointVelocities, dv0); + + deltaV0.linear = transform0.rotate(dv0.bottom); + deltaV0.angular = transform0.rotate(dv0.top); + + deltaV1.linear = transform1.rotate(dv1.bottom); + deltaV1.angular = transform1.rotate(dv1.top); + } + + void FeatherstoneArticulation::getImpulseSelfResponseInv(const bool fixBase, + PxU32 linkID0, + PxU32 linkID1, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse0, + const Cm::SpatialVector& impulse1, + Cm::SpatialVector& deltaV0, + Cm::SpatialVector& deltaV1, + PxReal* jointVelocities) + { + ArticulationLink* links = mArticulationData.getLinks(); + + //transform p(impluse) from work space to the local space of link + ArticulationLink& link = links[linkID1]; + ArticulationLinkData& linkDatum = mArticulationData.getLinkData(linkID1); + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID1); + + + if (link.parent == linkID0) + { + PX_ASSERT(linkID0 == link.parent); + PX_ASSERT(linkID0 < linkID1); + + ArticulationLink& pLink = links[linkID0]; + + //impulse is in world space + const Cm::SpatialVector& imp1 = impulse1; + const Cm::SpatialVector& imp0 = impulse0; + + PxTransform& pBody2World = pLink.bodyCore->body2World; + + Cm::SpatialVectorF pImpulse = Cm::SpatialVectorF(pBody2World.rotateInv(imp0.linear), pBody2World.rotateInv(imp0.angular)); + + PX_ASSERT(linkID0 == link.parent); + + PxTransform& body2World = link.bodyCore->body2World; + + //initialize child link spatial zero acceleration impulse + Cm::SpatialVectorF Z1 = Cm::SpatialVectorF(-body2World.rotateInv(imp1.linear), -body2World.rotateInv(imp1.angular)); + //this calculate parent link spatial zero acceleration impulse + Cm::SpatialVectorF Z0 = FeatherstoneArticulation::propagateImpulse(linkDatum, jointDatum, Z1); + + //in parent space + const Cm::SpatialVectorF impulseDif = pImpulse - Z0; + + Cm::SpatialVectorF delV0(PxVec3(0.f), PxVec3(0.f)); + Cm::SpatialVectorF delV1(PxVec3(0.f), PxVec3(0.f)); + + //calculate velocity change start from the parent link to the root + delV0 = FeatherstoneArticulation::getImpulseResponseWithJ(linkID0, fixBase, mArticulationData, Z, impulseDif, jointVelocities); + + //calculate velocity change for child link + delV1 = FeatherstoneArticulation::propagateVelocity(linkDatum, jointDatum, Z1, jointVelocities, delV0); + + //translate delV0 and delV1 into world space again + deltaV0.linear = pBody2World.rotate(delV0.bottom); + deltaV0.angular = pBody2World.rotate(delV0.top); + deltaV1.linear = body2World.rotate(delV1.bottom); + deltaV1.angular = body2World.rotate(delV1.top); + } + else + { + getImpulseResponseSlowInv(links, mArticulationData, linkID0, impulse0, deltaV0, linkID1,impulse1, deltaV1, jointVelocities ); + } + } + + Cm::SpatialVectorF FeatherstoneArticulation::getImpulseResponseInv( + const bool fixBase, const PxU32 linkID, + Cm::SpatialVectorF* Z, + const Cm::SpatialVector& impulse, + PxReal* jointVelocities) + { + + //impulse lin is contact normal, and ang is raxn. R is body2World, R(t) is world2Body + //| R(t), 0 | + //| R(t)*r, R(t)| + //r is the vector from center of mass to contact point + //p(impluse) = |n| + // |0| + + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + ArticulationJointCoreData* jointData = mArticulationData.getJointData(); + const PxU32 linkCount = mArticulationData.getLinkCount(); + + //transform p(impluse) from work space to the local space of link + ArticulationLink& link = links[linkID]; + PxTransform& body2World = link.bodyCore->body2World; + + //make sure all links' spatial zero acceleration impulse are zero + PxMemZero(Z, sizeof(Cm::SpatialVectorF) * linkCount); + + Z[linkID] = Cm::SpatialVectorF(-body2World.rotateInv(impulse.linear), -body2World.rotateInv(impulse.angular)); + + for (PxU32 i = linkID; i; i = links[i].parent) + { + ArticulationLink& tLink = links[i]; + ArticulationLinkData& tLinkDatum = linkData[i]; + ArticulationJointCoreData& tJointDatum = jointData[i]; + Z[tLink.parent] = propagateImpulse(tLinkDatum, tJointDatum, Z[i]); + } + + //set velocity change of the root link to be zero + Cm::SpatialVectorF deltaV = Cm::SpatialVectorF(PxVec3(0.f), PxVec3(0.f)); + if (!fixBase) + deltaV = mArticulationData.mBaseInvSpatialArticulatedInertia * (-Z[0]); + + for (ArticulationBitField i = links[linkID].pathToRoot - 1; i; i &= (i - 1)) + { + //index of child of link h on path to link linkID + const PxU32 index = ArticulationLowestSetBit(i); + ArticulationLinkData& tLinkDatum = linkData[index]; + ArticulationJointCoreData& tJointDatum = jointData[index]; + + PxReal* jVelocity = &jointVelocities[tJointDatum.jointOffset]; + + PX_ASSERT(links[index].parent < index); + deltaV = propagateVelocity(tLinkDatum, tJointDatum, Z[index], jVelocity, deltaV); + } + + return deltaV; + + } + + + void FeatherstoneArticulation::getCoefficentMatrixWithLoopJoints(ArticulationLoopConstraint* lConstraints, const PxU32 nbConstraints, PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "ArticulationHelper::getCoefficentMatrix() commonInit need to be called first to initialize data!"); + return; + } + + computeArticulatedSpatialInertia(mArticulationData); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxReal* coefficentMatrix = cache.coefficentMatrix; + + const PxU32 elementCount = mArticulationData.getDofs(); + + //zero coefficent matrix + PxMemZero(coefficentMatrix, sizeof(PxReal) * elementCount * nbConstraints); + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + Cm::SpatialVectorF* Z = scratchData.spatialZAVectors; + const PxU32 totalDofs = mArticulationData.getDofs(); + + const PxU32 size = sizeof(PxReal) * totalDofs; + + PxU8* tData = reinterpret_cast(allocator->alloc(size * 2)); + + const PxReal invDt = 1.f / mArticulationData.getDt(); + PxReal* jointVelocities = reinterpret_cast(tData); + PxReal* jointAccelerations = reinterpret_cast(tData + size); + + for (PxU32 a = 0; a < nbConstraints; ++a) + { + ArticulationLoopConstraint& lConstraint = lConstraints[a]; + Constraint* aConstraint = lConstraint.constraint; + + Px1DConstraint rows[MAX_CONSTRAINT_ROWS]; + + PxMemZero(rows, sizeof(Px1DConstraint)*MAX_CONSTRAINT_ROWS); + + for (PxU32 i = 0; ibody0) + body2World0 = aConstraint->bodyCore0->body2World; + + if (aConstraint->body1) + body2World1 = aConstraint->bodyCore1->body2World; + + PxVec3 body0WorldOffset(0.f); + PxVec3 ra, rb; + PxConstraintInvMassScale invMassScales; + PxU32 constraintCount = (*aConstraint->solverPrep)(rows, + body0WorldOffset, + MAX_CONSTRAINT_ROWS, + invMassScales, + aConstraint->constantBlock, + body2World0, body2World1, !!(aConstraint->flags & PxConstraintFlag::eENABLE_EXTENDED_LIMITS), ra, rb); + + const PxU32 linkIndex0 = lConstraint.linkIndex0; + const PxU32 linkIndex1 = lConstraint.linkIndex1; + + //zero joint Velocites + PxMemZero(jointVelocities, size); + + for (PxU32 j = 0; j < constraintCount; ++j) + { + Px1DConstraint& row = rows[j]; + + if (linkIndex0 != 0x80000000 && linkIndex1 != 0x80000000) + { + const bool flip = linkIndex0 > linkIndex1; + + Cm::SpatialVector impulse0(row.linear0, row.angular0); + Cm::SpatialVector impulse1(row.linear1, row.angular1); + + Cm::SpatialVector deltaV0, deltaV1; + + if (flip) + { + getImpulseSelfResponseInv(fixBase, linkIndex1, linkIndex0, Z, impulse1, impulse0, + deltaV1, deltaV0, jointVelocities); + } + else + { + getImpulseSelfResponseInv(fixBase, linkIndex0, linkIndex1, Z, impulse0, impulse1, + deltaV0, deltaV1, jointVelocities); + } + } + else + { + if (linkIndex0 == 0x80000000) + { + Cm::SpatialVector impulse1(row.linear1, row.angular1); + getImpulseResponseInv(fixBase, linkIndex1, Z, impulse1, jointVelocities); + } + else + { + Cm::SpatialVector impulse0(row.linear0, row.angular0); + getImpulseResponseInv(fixBase, linkIndex0, Z, impulse0, jointVelocities); + } + } + } + + + //calculate joint acceleration due to velocity change + for (PxU32 i = 0; i < totalDofs; ++i) + { + jointAccelerations[i] = jointVelocities[i] * invDt; + } + + //reset spatial inertia + computeSpatialInertia(mArticulationData); + + PxReal* coeCol = &coefficentMatrix[elementCount * a]; + + //this means the joint force calculated by the inverse dynamic + //will be just influenced by joint acceleration change + scratchData.jointVelocities = NULL; + scratchData.externalAccels = NULL; + + //Input + scratchData.jointAccelerations = jointAccelerations; + + //a column of the coefficent matrix is the joint force + scratchData.jointForces = coeCol; + + if (fixBase) + { + inverseDynamic(mArticulationData, PxVec3(0.f), scratchData); + } + else + { + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + } + + allocator->free(tData); + allocator->free(tempMemory); + } + } + + void FeatherstoneArticulation::constraintPrep(ArticulationLoopConstraint* lConstraints, + const PxU32 nbJoints, Cm::SpatialVectorF* Z, PxSolverConstraintPrepDesc& prepDesc, + PxSolverBody& sBody, PxSolverBodyData& sBodyData, PxSolverConstraintDesc* descs, + PxConstraintAllocator& allocator) + { + const PxReal dt = mArticulationData.getDt(); + const PxReal invDt = 1.f / dt; + //constraint prep + for (PxU32 a = 0; a < nbJoints; ++a) + { + ArticulationLoopConstraint& lConstraint = lConstraints[a]; + Constraint* aConstraint = lConstraint.constraint; + + PxSolverConstraintDesc& desc = descs[a]; + prepDesc.desc = &desc; + prepDesc.linBreakForce = aConstraint->linBreakForce; + prepDesc.angBreakForce = aConstraint->angBreakForce; + prepDesc.writeback = &mContext->getConstraintWriteBackPool()[aConstraint->index]; + prepDesc.disablePreprocessing = !!(aConstraint->flags & PxConstraintFlag::eDISABLE_PREPROCESSING); + prepDesc.improvedSlerp = !!(aConstraint->flags & PxConstraintFlag::eIMPROVED_SLERP); + prepDesc.driveLimitsAreForces = !!(aConstraint->flags & PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES); + prepDesc.extendedLimits = !!(aConstraint->flags & PxConstraintFlag::eENABLE_EXTENDED_LIMITS); + prepDesc.minResponseThreshold = aConstraint->minResponseThreshold; + + Px1DConstraint rows[MAX_CONSTRAINT_ROWS]; + + PxMemZero(rows, sizeof(Px1DConstraint)*MAX_CONSTRAINT_ROWS); + + for (PxU32 i = 0; i < MAX_CONSTRAINT_ROWS; i++) + { + Px1DConstraint& c = rows[i]; + //Px1DConstraintInit(c); + c.minImpulse = -PX_MAX_REAL; + c.maxImpulse = PX_MAX_REAL; + } + + prepDesc.mInvMassScales.linear0 = prepDesc.mInvMassScales.linear1 = prepDesc.mInvMassScales.angular0 = prepDesc.mInvMassScales.angular1 = 1.f; + + PxTransform body2World0 = PxTransform(PxIdentity); + PxTransform body2World1 = PxTransform(PxIdentity); + + if (aConstraint->body0) + body2World0 = aConstraint->bodyCore0->body2World; + + if (aConstraint->body1) + body2World1 = aConstraint->bodyCore1->body2World; + + + PxVec3 body0WorldOffset(0.f); + PxVec3 ra, rb; + PxConstraintInvMassScale invMassScales; + PxU32 constraintCount = (*aConstraint->solverPrep)(rows, + body0WorldOffset, + MAX_CONSTRAINT_ROWS, + invMassScales, + aConstraint->constantBlock, + body2World0, body2World1, !!(aConstraint->flags & PxConstraintFlag::eENABLE_EXTENDED_LIMITS), + ra, rb); + + + prepDesc.body0WorldOffset = body0WorldOffset; + prepDesc.bodyFrame0 = body2World0; + prepDesc.bodyFrame1 = body2World1; + prepDesc.numRows = constraintCount; + prepDesc.rows = rows; + + const PxU32 linkIndex0 = lConstraint.linkIndex0; + const PxU32 linkIndex1 = lConstraint.linkIndex1; + + + if (linkIndex0 != 0x80000000 && linkIndex1 != 0x80000000) + { + desc.articulationA = this; + desc.articulationB = this; + desc.linkIndexA = PxU16(linkIndex0); + desc.linkIndexB = PxU16(linkIndex1); + + desc.bodyA = reinterpret_cast(this); + desc.bodyB = reinterpret_cast(this); + + prepDesc.bodyState0 = PxSolverConstraintPrepDescBase::eARTICULATION; + prepDesc.bodyState1 = PxSolverConstraintPrepDescBase::eARTICULATION; + + } + else if (linkIndex0 == 0x80000000) + { + desc.articulationA = NULL; + desc.articulationB = this; + + desc.linkIndexA = 0xffff; + desc.linkIndexB = PxU16(linkIndex1); + + desc.bodyA = &sBody; + desc.bodyB = reinterpret_cast(this); + + prepDesc.bodyState0 = PxSolverConstraintPrepDescBase::eSTATIC_BODY; + prepDesc.bodyState1 = PxSolverConstraintPrepDescBase::eARTICULATION; + } + else if (linkIndex1 == 0x80000000) + { + desc.articulationA = this; + desc.articulationB = NULL; + + desc.linkIndexA = PxU16(linkIndex0); + desc.linkIndexB = 0xffff; + + desc.bodyA = reinterpret_cast(this); + desc.bodyB = &sBody; + + prepDesc.bodyState0 = PxSolverConstraintPrepDescBase::eARTICULATION; + prepDesc.bodyState1 = PxSolverConstraintPrepDescBase::eSTATIC_BODY; + + } + + prepDesc.body0 = desc.bodyA; + prepDesc.body1 = desc.bodyB; + prepDesc.data0 = &sBodyData; + prepDesc.data1 = &sBodyData; + + ConstraintHelper::setupSolverConstraint(prepDesc, allocator, dt, invDt, Z); + } + + } + + class BlockBasedAllocator + { + struct AllocationPage + { + static const PxU32 PageSize = 32 * 1024; + PxU8 mPage[PageSize]; + + PxU32 currentIndex; + + AllocationPage() : currentIndex(0) {} + + PxU8* allocate(const PxU32 size) + { + PxU32 alignedSize = (size + 15)&(~15); + if ((currentIndex + alignedSize) < PageSize) + { + PxU8* ret = &mPage[currentIndex]; + currentIndex += alignedSize; + return ret; + } + return NULL; + } + }; + + AllocationPage* currentPage; + + physx::shdfnd::Array mAllocatedBlocks; + PxU32 mCurrentIndex; + + public: + BlockBasedAllocator() : currentPage(NULL), mCurrentIndex(0) + { + } + + virtual PxU8* allocate(const PxU32 byteSize) + { + if (currentPage) + { + PxU8* data = currentPage->allocate(byteSize); + if (data) + return data; + } + + if (mCurrentIndex < mAllocatedBlocks.size()) + { + currentPage = mAllocatedBlocks[mCurrentIndex++]; + currentPage->currentIndex = 0; + return currentPage->allocate(byteSize); + } + currentPage = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(AllocationPage), PX_DEBUG_EXP("AllocationPage")), AllocationPage)(); + mAllocatedBlocks.pushBack(currentPage); + mCurrentIndex = mAllocatedBlocks.size(); + + return currentPage->allocate(byteSize); + } + + void release() { for (PxU32 a = 0; a < mAllocatedBlocks.size(); ++a) PX_FREE(mAllocatedBlocks[a]); mAllocatedBlocks.clear(); currentPage = NULL; mCurrentIndex = 0; } + + void reset() { currentPage = NULL; mCurrentIndex = 0; } + + virtual ~BlockBasedAllocator() + { + release(); + } + }; + + class ArticulationBlockAllocator : public PxConstraintAllocator + { + BlockBasedAllocator mConstraintAllocator; + BlockBasedAllocator mFrictionAllocator[2]; + + PxU32 currIdx; + + public: + + ArticulationBlockAllocator() : currIdx(0) + { + } + + virtual ~ArticulationBlockAllocator() {} + + virtual PxU8* reserveConstraintData(const PxU32 size) + { + return reinterpret_cast(mConstraintAllocator.allocate(size)); + } + + virtual PxU8* reserveFrictionData(const PxU32 byteSize) + { + return reinterpret_cast(mFrictionAllocator[currIdx].allocate(byteSize)); + } + + void release() { currIdx = 1 - currIdx; mConstraintAllocator.release(); mFrictionAllocator[currIdx].release(); } + + PX_NOCOPY(ArticulationBlockAllocator) + + }; + + void solveExt1D(const PxSolverConstraintDesc& desc, SolverContext& cache); + void writeBack1D(const PxSolverConstraintDesc& desc, SolverContext&, PxSolverBodyData&, PxSolverBodyData&); + void conclude1D(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/); + void clearExt1D(const PxSolverConstraintDesc& desc, SolverContext& cache); + + bool FeatherstoneArticulation::getLambda(ArticulationLoopConstraint* lConstraints, const PxU32 nbJoints, + PxArticulationCache& cache, PxArticulationCache& initialState, + const PxReal* jointTorque, const PxVec3& gravity, const PxU32 maxIter) + { + const PxReal dt = mArticulationData.getDt(); + const PxReal invDt = 1.f / dt; + const PxU32 totalDofs = mArticulationData.getDofs(); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + ArticulationBlockAllocator bAlloc; + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + Cm::SpatialVectorF* Z = reinterpret_cast(allocator->alloc(sizeof(Cm::SpatialVectorF) * linkCount, true)); + Cm::SpatialVectorF* deltaV = reinterpret_cast(allocator->alloc(sizeof(Cm::SpatialVectorF) * linkCount, true)); + + PxReal* prevoiusLambdas =reinterpret_cast(allocator->alloc(sizeof(PxReal)*nbJoints * 2, true)); + PxReal* lambdas = cache.lambda; + + //this is the joint force changed caused by contact force based on impulse strength is 1 + PxReal* J = cache.coefficentMatrix; + + PxSolverBody staticSolverBody; + PxMemZero(&staticSolverBody, sizeof(PxSolverBody)); + PxSolverBodyData staticSolverBodyData; + PxMemZero(&staticSolverBodyData, sizeof(PxSolverBodyData)); + staticSolverBodyData.maxContactImpulse = PX_MAX_F32; + staticSolverBodyData.penBiasClamp = -PX_MAX_F32; + staticSolverBodyData.body2World = PxTransform(PxIdentity); + + Dy::SolverContext context; + context.Z = Z; + context.deltaV = deltaV; + context.doFriction = false; + + + PxSolverConstraintDesc* desc = reinterpret_cast(allocator->alloc(sizeof(PxSolverConstraintDesc) * nbJoints, true)); + ArticulationSolverDesc artiDesc; + + PxSolverConstraintDesc* constraintDescs = reinterpret_cast(allocator->alloc(sizeof(PxSolverConstraintDesc) * mArticulationData.getLinkCount()-1, true)); + + //run forward dynamic to calculate the lamba + + artiDesc.articulation = this; + PxU32 acCount = 0; + computeUnconstrainedVelocities(artiDesc, dt, bAlloc, constraintDescs, acCount, + gravity, 0, Z, deltaV); + + ScratchData scratchData; + scratchData.motionVelocities = mArticulationData.getMotionVelocities(); + scratchData.motionAccelerations = mArticulationData.getMotionAccelerations(); + scratchData.coriolisVectors = mArticulationData.getCorioliseVectors(); + scratchData.spatialZAVectors = mArticulationData.getSpatialZAVectors(); + scratchData.jointAccelerations = mArticulationData.getJointAccelerations(); + scratchData.jointVelocities = mArticulationData.getJointVelocities(); + scratchData.jointPositions = mArticulationData.getJointPositions(); + scratchData.jointForces = mArticulationData.getJointForces(); + scratchData.externalAccels = mArticulationData.getExternalAccelerations(); + + //prepare constraint data + PxSolverConstraintPrepDesc prepDesc; + constraintPrep(lConstraints, nbJoints, Z, prepDesc, staticSolverBody, + staticSolverBodyData, desc, bAlloc); + + for (PxU32 i = 0; i < nbJoints; ++i) + { + prevoiusLambdas[i] = PX_MAX_F32; + } + + bool found = true; + + for (PxU32 iter = 0; iter < maxIter; ++iter) + { + found = true; + for (PxU32 i = 0; i < nbJoints; ++i) + { + clearExt1D(desc[i], context); + } + + //solve + for (PxU32 itr = 0; itr < 4; itr++) + { + for (PxU32 i = 0; i < nbJoints; ++i) + { + solveExt1D(desc[i], context); + } + } + for (PxU32 i = 0; i < nbJoints; ++i) + { + conclude1D(desc[i], context); + } + + PxcFsFlushVelocity(*this, deltaV); + + for (PxU32 i = 0; i < nbJoints; ++i) + { + solveExt1D(desc[i], context); + writeBack1D(desc[i], context, staticSolverBodyData, staticSolverBodyData); + } + + PxReal eps = 1e-5f; + for (PxU32 i = 0; i < nbJoints; ++i) + { + Dy::Constraint* constraint = lConstraints->constraint; + + Dy::ConstraintWriteback& solverOutput = mContext->getConstraintWriteBackPool()[constraint->index]; + PxVec3 linearForce = solverOutput.linearImpulse * invDt; + + //linear force is normalize so lambda is the magnitude of linear force + lambdas[i] = linearForce.magnitude() * dt; + + const PxReal dif = PxAbs(prevoiusLambdas[i] - lambdas[i]); + if (dif > eps) + found = false; + + prevoiusLambdas[i] = lambdas[i]; + } + + if (found) + break; + + //joint force + PxReal* jf3 = cache.jointForce; + + //zero the joint force buffer + PxMemZero(jf3, sizeof(PxReal)*totalDofs); + + for (PxU32 colInd = 0; colInd < nbJoints; ++colInd) + { + PxReal* col = &J[colInd * totalDofs]; + + for (PxU32 j = 0; j < totalDofs; ++j) + { + jf3[j] += col[j] * lambdas[colInd]; + } + } + + //jointTorque is M(q)*qddot + C(q,qdot)t - g(q) + //jointTorque - J*lambda. + for (PxU32 j = 0; j < totalDofs; ++j) + { + jf3[j] = jointTorque[j] - jf3[j]; + } + + //reset all joint velocities/ + applyCache(initialState, PxArticulationCache::eALL); + + //copy constraint torque to internal data + applyCache(cache, PxArticulationCache::eFORCE); + + mArticulationData.init(); + + computeLinkVelocities(mArticulationData, scratchData); + computeZ(mArticulationData, gravity, scratchData); + computeArticulatedSpatialZ(mArticulationData, scratchData); + computeLinkAcceleration(mArticulationData, scratchData); + + //zero zero acceleration vector in the articulation data so that we can use this buffer to accumulated + //impulse for the contacts/constraints in the PGS/TGS solvers + PxMemZero(mArticulationData.getSpatialZAVectors(), sizeof(Cm::SpatialVectorF) * linkCount); + } + + allocator->free(constraintDescs); + allocator->free(prevoiusLambdas); + allocator->free(Z); + allocator->free(deltaV); + allocator->free(desc); + bAlloc.release(); + + //roll back to the current stage + applyCache(initialState, PxArticulationCache::eALL); + + return found; + + } + + //i is the current link ID, we need to compute the row/column related to the joint i with all the other joints + PxU32 computeHi(ArticulationData& data, const PxU32 i, PxReal* massMatrix, Cm::SpatialVectorF* f) + { + ArticulationLink* links = data.getLinks(); + ArticulationLinkData* linkData = data.getLinkData(); + + ArticulationJointCoreData& jointDatum = data.getJointData(i); + + const PxU32 totalDofs = data.getDofs(); + + //Hii + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + const PxU32 row = (jointDatum.jointOffset + ind)* totalDofs; + const Cm::SpatialVectorF& tf = f[ind]; + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + const PxU32 col = jointDatum.jointOffset + ind2; + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind2]; + massMatrix[row + col] = sa.innerProduct(tf); + } + } + + PxU32 j = i; + + ArticulationLink* jLink = &links[j]; + ArticulationLinkData* jLinkDatum = &linkData[j]; + while (jLink->parent != 0) + { + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + f[ind] = jLinkDatum->childToParent * f[ind]; + } + + //assign j to the parent link + j = jLink->parent; + jLink = &links[j]; + jLinkDatum = &linkData[j]; + + //Hij + ArticulationJointCoreData& pJointDatum = data.getJointData(j); + + for (PxU32 ind = 0; ind < pJointDatum.dof; ++ind) + { + Cm::SpatialVectorF& sa = pJointDatum.motionMatrix[ind]; + const PxU32 col = pJointDatum.jointOffset + ind; + + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + const PxU32 row = (jointDatum.jointOffset + ind2)* totalDofs; + + Cm::SpatialVectorF& fcol = f[ind2]; + + massMatrix[row + col] = fcol.innerProduct(sa); + } + } + + //Hji = transpose(Hij) + { + for (PxU32 ind = 0; ind < pJointDatum.dof; ++ind) + { + const PxU32 pRow = (pJointDatum.jointOffset + ind)* totalDofs; + const PxU32 col = pJointDatum.jointOffset + ind; + + for (PxU32 ind2 = 0; ind2 < jointDatum.dof; ++ind2) + { + const PxU32 pCol = jointDatum.jointOffset + ind2; + const PxU32 row = (jointDatum.jointOffset + ind2) * totalDofs; + + massMatrix[pRow + pCol] = massMatrix[row + col]; + } + } + } + + } + return j; + } + + void FeatherstoneArticulation::calculateHFixBase(PxArticulationCache& cache) + { + const PxU32 elementCount = mArticulationData.getDofs(); + + PxReal* massMatrix = cache.massMatrix; + + PxMemZero(massMatrix, sizeof(PxReal) * elementCount * elementCount); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + + const PxU32 startIndex = PxU32(linkCount - 1); + + Dy::SpatialMatrix* compositeSpatialInertia = reinterpret_cast(allocator->alloc(sizeof(Dy::SpatialMatrix) * linkCount)); + + //initialize composite spatial inertial + initCompositeSpatialInertia(mArticulationData, compositeSpatialInertia); + + Cm::SpatialVectorF F[6]; + for (PxU32 i = startIndex; i > 0; --i) + { + ArticulationLink& link = links[i]; + ArticulationLinkData& linkDatum = linkData[i]; + + Dy::SpatialMatrix cSpatialInertia = compositeSpatialInertia[i]; + //transform current link's spatial inertia to parent's space + transformInertia(linkDatum.childToParent, cSpatialInertia); + + //compute parent's composite spatial inertia + compositeSpatialInertia[link.parent] += cSpatialInertia; + + Dy::SpatialMatrix& tSpatialInertia = compositeSpatialInertia[i]; + + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(i); + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + F[ind] = tSpatialInertia* sa; + } + + //Hii, Hij, Hji + computeHi(mArticulationData, i, massMatrix, F); + } + + allocator->free(compositeSpatialInertia); + } + + + void FeatherstoneArticulation::calculateHFloatingBase(PxArticulationCache& cache) + { + const PxU32 elementCount = mArticulationData.getDofs(); + + PxReal* massMatrix = cache.massMatrix; + + PxMemZero(massMatrix, sizeof(PxReal) * elementCount * elementCount); + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ArticulationLink* links = mArticulationData.getLinks(); + ArticulationLinkData* linkData = mArticulationData.getLinkData(); + + const PxU32 startIndex = PxU32(linkCount - 1); + + Dy::SpatialMatrix* compositeSpatialInertia = reinterpret_cast(allocator->alloc(sizeof(Dy::SpatialMatrix) * linkCount)); + Cm::SpatialVectorF* F = reinterpret_cast(allocator->alloc(sizeof(Cm::SpatialVectorF) * elementCount)); + + //initialize composite spatial inertial + initCompositeSpatialInertia(mArticulationData, compositeSpatialInertia); + + for (PxU32 i = startIndex; i > 0; --i) + { + ArticulationLink& link = links[i]; + ArticulationLinkData& linkDatum = linkData[i]; + + Dy::SpatialMatrix cSpatialInertia = compositeSpatialInertia[i]; + //transform current link's spatial inertia to parent's space + transformInertia(linkDatum.childToParent, cSpatialInertia); + + //compute parent's composite spatial inertia + compositeSpatialInertia[link.parent] += cSpatialInertia; + + Dy::SpatialMatrix& tSpatialInertia = compositeSpatialInertia[i]; + + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(i); + + Cm::SpatialVectorF* f = &F[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + Cm::SpatialVectorF& sa = jointDatum.motionMatrix[ind]; + f[ind] = tSpatialInertia* sa; + } + + //Hii, Hij, Hji + const PxU32 j = computeHi(mArticulationData, i, massMatrix, f); + + //transform F to the base link space + ArticulationLinkData& fDatum = linkData[j]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + f[ind] = fDatum.childToBase * f[ind]; + } + } + + //Ib = base link composite inertia tensor + //compute transpose(F) * inv(Ib) *F + Dy::SpatialMatrix invI0 = compositeSpatialInertia[0].invertInertia(); + + //H - transpose(F) * inv(Ib) * F; + for (PxU32 row = 0; row < elementCount; ++row) + { + const Cm::SpatialVectorF& f = F[row]; + for (PxU32 col = 0; col < elementCount; ++col) + { + const Cm::SpatialVectorF invIf = invI0 * F[col]; + const PxReal v = f.innerProduct(invIf); + const PxU32 index = row * elementCount + col; + massMatrix[index] = massMatrix[index] - v; + } + } + + allocator->free(compositeSpatialInertia); + allocator->free(F); + + } + + //calcualte a single column of H, jointForce is equal to a single column of H + void FeatherstoneArticulation::calculateMassMatrixColInv(ScratchData& scratchData) + { + const PxU32 linkCount = mArticulationData.getLinkCount(); + + Cm::SpatialVectorF* motionAccelerations = scratchData.motionAccelerations; + Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; + + //Input + PxReal* jointAccelerations = scratchData.jointAccelerations; + + //set base link motion acceleration to be zero because H should + //be just affected by joint position/link position + motionAccelerations[0] = Cm::SpatialVectorF::Zero(); + spatialZAForces[0] = Cm::SpatialVectorF::Zero(); + + for (PxU32 linkID = 1; linkID < linkCount; ++linkID) + { + ArticulationLink& link = mArticulationData.getLink(linkID); + ArticulationLinkData& linkDatum = mArticulationData.getLinkData(linkID); + ArticulationJointCoreData& jointDatum = mArticulationData.getJointData(linkID); + + SpatialTransform p2c = linkDatum.childToParent.getTranspose(); + + //parent motion accelerations into child space + Cm::SpatialVectorF accel = p2c * motionAccelerations[link.parent]; + + const PxReal* jAcceleration = &jointAccelerations[jointDatum.jointOffset]; + + for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) + { + accel += jointDatum.motionMatrix[ind] * jAcceleration[ind]; + } + + motionAccelerations[linkID] = accel; + + spatialZAForces[linkID] = linkDatum.spatialArticulatedInertia * accel; + } + + computeGeneralizedForceInv(mArticulationData, scratchData); + + } + + void FeatherstoneArticulation::getGeneralizedMassMatrixCRB(PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "ArticulationHelper::getGeneralizedMassMatrix() commonInit need to be called first to initialize data!"); + return; + } + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + if (fixBase) + { + calculateHFixBase(cache); + } + else + { + calculateHFloatingBase(cache); + } + + } + + void FeatherstoneArticulation::getGeneralizedMassMatrix( PxArticulationCache& cache) + { + if (mArticulationData.getDataDirty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "ArticulationHelper::getGeneralizedMassMatrix() commonInit need to be called first to initialize data!"); + return; + } + + + //calculate each column for mass matrix + PxReal* massMatrix = cache.massMatrix; + + const PxU32 linkCount = mArticulationData.getLinkCount(); + + const PxU32 elementCount = mArticulationData.getDofs(); + + const PxU32 size = sizeof(PxReal) * elementCount; + PxcScratchAllocator* allocator = reinterpret_cast(cache.scratchAllocator); + + ScratchData scratchData; + PxU8* tempMemory = allocateScratchSpatialData(allocator, linkCount, scratchData); + + PxReal* jointAccelerations = reinterpret_cast(allocator->alloc(size)); + + scratchData.jointAccelerations = jointAccelerations; + scratchData.jointVelocities = NULL; + scratchData.externalAccels = NULL; + + const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; + + //initialize jointAcceleration to be zero + PxMemZero(jointAccelerations, size); + + for (PxU32 colInd = 0; colInd < elementCount; ++colInd) + { + PxReal* col = &massMatrix[colInd * elementCount]; + + scratchData.jointForces = col; + + //set joint acceleration 1 in the col + 1 and zero elsewhere + jointAccelerations[colInd] = 1; + + if (fixBase) + { + //jointAcceleration is Q, HQ = ID(model, qdot, Q). + calculateMassMatrixColInv(scratchData); + } + else + { + inverseDynamicFloatingBase(mArticulationData, PxVec3(0.f), scratchData); + } + + //reset joint acceleration to be zero + jointAccelerations[colInd] = 0; + } + + allocator->free(jointAccelerations); + allocator->free(tempMemory); + } + + + +} //namespace Dy + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp new file mode 100644 index 000000000..35bbfeac5 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp @@ -0,0 +1,298 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxvConfig.h" +#include "DyCorrelationBuffer.h" +#include "PxsMaterialManager.h" +#include "PsUtilities.h" +#include "foundation/PxBounds3.h" + +using namespace physx; +using namespace Gu; + +namespace physx +{ + +namespace Dy +{ + +namespace +{ +PX_FORCE_INLINE void initContactPatch(CorrelationBuffer::ContactPatchData& patch, PxU16 index, PxReal restitution, PxReal staticFriction, PxReal dynamicFriction, + PxU8 flags) +{ + patch.start = index; + patch.count = 1; + patch.next = 0; + patch.flags = flags; + patch.restitution = restitution; + patch.staticFriction = staticFriction; + patch.dynamicFriction = dynamicFriction; +} + +PX_FORCE_INLINE void initFrictionPatch(FrictionPatch& p, const PxVec3& worldNormal, const PxTransform& body0Pose, const PxTransform& body1Pose, + PxReal restitution, PxReal staticFriction, PxReal dynamicFriction, PxU8 materialFlags) +{ + p.body0Normal = body0Pose.rotateInv(worldNormal); + p.body1Normal = body1Pose.rotateInv(worldNormal); + p.relativeQuat = body0Pose.q.getConjugate() * body1Pose.q; + p.anchorCount = 0; + p.broken = 0; + p.staticFriction = staticFriction; + p.dynamicFriction = dynamicFriction; + p.restitution = restitution; + p.materialFlags = materialFlags; +} +} + + +bool createContactPatches(CorrelationBuffer& fb, const Gu::ContactPoint* cb, PxU32 contactCount, PxReal normalTolerance) +{ + + // PT: this rewritten version below doesn't have LHS + + PxU32 contactPatchCount = fb.contactPatchCount; + if(contactPatchCount == Gu::ContactBuffer::MAX_CONTACTS) + return false; + if(contactCount>0) + { + CorrelationBuffer::ContactPatchData* currentPatchData = fb.contactPatches + contactPatchCount; + const Gu::ContactPoint* PX_RESTRICT contacts = cb; + + PxU8 count=1; + + initContactPatch(fb.contactPatches[contactPatchCount++], Ps::to16(0), contacts[0].restitution, + contacts[0].staticFriction, contacts[0].dynamicFriction, PxU8(contacts[0].materialFlags)); + + PxBounds3 bounds(contacts[0].point, contacts[0].point); + + PxU32 patchIndex = 0; + + for (PxU32 i = 1; i=normalTolerance) + { + bounds.include(curContact.point); + count++; + } + else + { + if(contactPatchCount == Gu::ContactBuffer::MAX_CONTACTS) + return false; + patchIndex = i; + currentPatchData->count = count; + count = 1; + currentPatchData->patchBounds = bounds; + currentPatchData = fb.contactPatches + contactPatchCount; + + initContactPatch(fb.contactPatches[contactPatchCount++], Ps::to16(i), curContact.restitution, + curContact.staticFriction, curContact.dynamicFriction, PxU8(curContact.materialFlags)); + + bounds = PxBounds3(curContact.point, curContact.point); + } + } + if(count!=1) + currentPatchData->count = count; + + currentPatchData->patchBounds = bounds; + } + fb.contactPatchCount = contactPatchCount; + return true; +} + +bool correlatePatches(CorrelationBuffer& fb, + const Gu::ContactPoint* cb, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxReal normalTolerance, + PxU32 startContactPatchIndex, + PxU32 startFrictionPatchIndex) +{ + bool overflow = false; + PxU32 frictionPatchCount = fb.frictionPatchCount; + + for(PxU32 i=startContactPatchIndex;i= frictionPatchDiagonalSq) + continue; + + fp.anchorCount = 0; + } + + PxVec3 worldAnchors[2]; + PxU16 anchorCount = 0; + PxReal pointDistSq = 0.0f, dist0, dist1; + + // if we have an anchor already, keep it + if(fp.anchorCount == 1) + { + worldAnchors[anchorCount++] = bodyFrame0.transform(fp.body0Anchors[0]); + } + + for(PxU32 patch = fb.correlationListHeads[i]; + patch!=CorrelationBuffer::LIST_END; + patch = fb.contactPatches[patch].next) + { + CorrelationBuffer::ContactPatchData& cp = fb.contactPatches[patch]; + for(PxU16 j=0;j (correlationDistance * correlationDistance)) + { + fb.contactID[i][1] = PxU16(cp.start+j); + worldAnchors[1] = worldPoint; + anchorCount++; + } + break; + default: //case 2 + dist0 = (worldPoint-worldAnchors[0]).magnitudeSquared(); + dist1 = (worldPoint-worldAnchors[1]).magnitudeSquared(); + if (dist0 > dist1) + { + if(dist0 > pointDistSq) + { + fb.contactID[i][1] = PxU16(cp.start+j); + worldAnchors[1] = worldPoint; + pointDistSq = dist0; + } + } + else if (dist1 > pointDistSq) + { + fb.contactID[i][0] = PxU16(cp.start+j); + worldAnchors[0] = worldPoint; + pointDistSq = dist1; + } + } + } + } + } + + //PX_ASSERT(anchorCount > 0); + + // add the new anchor(s) to the patch + for(PxU32 j = fp.anchorCount; j < anchorCount; j++) + { + fp.body0Anchors[j] = bodyFrame0.transformInv(worldAnchors[j]); + fp.body1Anchors[j] = bodyFrame1.transformInv(worldAnchors[j]); + } + + // the block contact solver always reads at least one anchor per patch for performance reasons even if there are no valid patches, + // so we need to initialize this in the unexpected case that we have no anchors + + if(anchorCount==0) + fp.body0Anchors[0] = fp.body1Anchors[0] = PxVec3(0); + + fp.anchorCount = anchorCount; + } +} + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h new file mode 100644 index 000000000..5bf28a293 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h @@ -0,0 +1,83 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_FRICTIONPATCH_H +#define PXC_FRICTIONPATCH_H + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec3.h" +#include "PxvConfig.h" + +namespace physx +{ + +namespace Dy +{ + +struct FrictionPatch +{ + PxU8 broken; // PT: must be first byte of struct, see "frictionBrokenWritebackByte" + PxU8 materialFlags; + PxU16 anchorCount; + PxReal restitution; + PxReal staticFriction; + PxReal dynamicFriction; + PxVec3 body0Normal; + PxVec3 body1Normal; + PxVec3 body0Anchors[2]; + PxVec3 body1Anchors[2]; + PxQuat relativeQuat; + + PX_FORCE_INLINE void operator = (const FrictionPatch& other) + { + broken = other.broken; + materialFlags = other.materialFlags; + anchorCount = other.anchorCount; + body0Normal = other.body0Normal; + body1Normal = other.body1Normal; + body0Anchors[0] = other.body0Anchors[0]; + body0Anchors[1] = other.body0Anchors[1]; + body1Anchors[0] = other.body1Anchors[0]; + body1Anchors[1] = other.body1Anchors[1]; + relativeQuat = other.relativeQuat; + restitution = other.restitution; + staticFriction = other.staticFriction; + dynamicFriction = other.dynamicFriction; + } +}; + +//PX_COMPILE_TIME_ASSERT(sizeof(FrictionPatch)==80); + +} + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h new file mode 100644 index 000000000..461e9b9bb --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h @@ -0,0 +1,128 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + + +#ifndef PXC_FRICTIONPATCHPOOL_H +#define PXC_FRICTIONPATCHPOOL_H + +#include "foundation/PxSimpleTypes.h" +#include "PxvConfig.h" +#include "PsMutex.h" +#include "PsArray.h" + +// Each narrow phase thread has an input stream of friction patches from the +// previous frame and an output stream of friction patches which will be +// saved for next frame. The patches persist for exactly one frame at which +// point they get thrown away. + + +// There is a stream pair per thread. A contact callback reserves space +// for its friction patches and gets a cookie in return that can stash +// for next frame. Cookies are valid for one frame only. +// +// note that all friction patches reserved are guaranteed to be contiguous; +// this might turn out to be a bit inefficient if we often have a large +// number of friction patches + +#include "PxcNpMemBlockPool.h" + +namespace physx +{ + +class FrictionPatchStreamPair +{ +public: + FrictionPatchStreamPair(PxcNpMemBlockPool& blockPool); + + // reserve can fail and return null. Read should never fail + template + FrictionPatch* reserve(const PxU32 size); + + template + const FrictionPatch* findInputPatches(const PxU8* ptr) const; + void reset(); + + PxcNpMemBlockPool& getBlockPool() { return mBlockPool;} +private: + PxcNpMemBlockPool& mBlockPool; + PxcNpMemBlock* mBlock; + PxU32 mUsed; + + FrictionPatchStreamPair& operator=(const FrictionPatchStreamPair&); +}; + +PX_FORCE_INLINE FrictionPatchStreamPair::FrictionPatchStreamPair(PxcNpMemBlockPool& blockPool): + mBlockPool(blockPool), mBlock(NULL), mUsed(0) +{ +} + +PX_FORCE_INLINE void FrictionPatchStreamPair::reset() +{ + mBlock = NULL; + mUsed = 0; +} + +// reserve can fail and return null. Read should never fail +template +FrictionPatch* FrictionPatchStreamPair::reserve(const PxU32 size) +{ + if(size>PxcNpMemBlock::SIZE) + { + return reinterpret_cast(-1); + } + + PX_ASSERT(size <= PxcNpMemBlock::SIZE); + + FrictionPatch* ptr = NULL; + + if(mBlock == NULL || mUsed + size > PxcNpMemBlock::SIZE) + { + mBlock = mBlockPool.acquireFrictionBlock(); + mUsed = 0; + } + + if(mBlock) + { + ptr = reinterpret_cast(mBlock->data+mUsed); + mUsed += size; + } + + return ptr; +} + +template +const FrictionPatch* FrictionPatchStreamPair::findInputPatches(const PxU8* ptr) const +{ + return reinterpret_cast(ptr); +} + +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp new file mode 100644 index 000000000..8efc7e170 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CmUtils.h" +#include "DySolverBody.h" +#include "PxsRigidBody.h" +#include "PxvDynamics.h" + +using namespace physx; + +// PT: TODO: SIMDify all this... So far I only took care of the quat-to-matrix transform +void Dy::copyToSolverBodyData(const PxVec3& linearVelocity, const PxVec3& angularVelocity, const PxReal invMass, const PxVec3& invInertia, const PxTransform& globalPose, + const PxReal maxDepenetrationVelocity, const PxReal maxContactImpulse, const PxU32 nodeIndex, const PxReal reportThreshold, PxSolverBodyData& data, PxU32 lockFlags) +{ + data.nodeIndex = nodeIndex; + + const PxVec3 safeSqrtInvInertia = computeSafeSqrtInertia(invInertia); + + const PxMat33 rotation(globalPose.q); + + Cm::transformInertiaTensor(safeSqrtInvInertia, rotation, data.sqrtInvInertia); + + // Copy simple properties + data.linearVelocity = linearVelocity; + data.angularVelocity = angularVelocity; + + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + data.linearVelocity.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + data.linearVelocity.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + data.linearVelocity.z = 0.f; + + //KS - technically, we can zero the inertia columns and produce stiffer constraints. However, this can cause numerical issues with the + //joint solver, which is fixed by disabling joint preprocessing and setting minResponseThreshold to some reasonable value > 0. However, until + //this is handled automatically, it's probably better not to zero these inertia rows + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + { + data.angularVelocity.x = 0.f; + //data.sqrtInvInertia.column0 = PxVec3(0.f); + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + { + data.angularVelocity.y = 0.f; + //data.sqrtInvInertia.column1 = PxVec3(0.f); + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + { + data.angularVelocity.z = 0.f; + //data.sqrtInvInertia.column2 = PxVec3(0.f); + } + } + + PX_ASSERT(linearVelocity.isFinite()); + PX_ASSERT(angularVelocity.isFinite()); + + data.invMass = invMass; + data.penBiasClamp = maxDepenetrationVelocity; + data.maxContactImpulse = maxContactImpulse; + data.body2World = globalPose; + + data.lockFlags = PxU16(lockFlags); + + data.reportThreshold = reportThreshold; +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h new file mode 100644 index 000000000..148f23fc5 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h @@ -0,0 +1,72 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERBODY_H +#define DY_SOLVERBODY_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" +#include "CmPhysXCommon.h" +#include "CmSpatialVector.h" +#include "solver/PxSolverDefs.h" + +namespace physx +{ + +class PxsRigidBody; +struct PxsBodyCore; + +namespace Dy +{ + +// PT: TODO: make sure this is still needed / replace with V4sqrt +//This method returns values of 0 when the inertia is 0. This is a bit of a hack but allows us to +//represent kinematic objects' velocities in our new format +PX_FORCE_INLINE PxVec3 computeSafeSqrtInertia(const PxVec3& v) +{ + return PxVec3( v.x == 0.0f ? 0.0f : PxSqrt(v.x), + v.y == 0.0f ? 0.0f : PxSqrt(v.y), + v.z == 0.0f ? 0.0f : PxSqrt(v.z)); +} + +void copyToSolverBodyData(const PxVec3& linearVelocity, const PxVec3& angularVelocity, const PxReal invMass, const PxVec3& invInertia, const PxTransform& globalPose, + const PxReal maxDepenetrationVelocity, const PxReal maxContactImpulse, const PxU32 nodeIndex, const PxReal reportThreshold, PxSolverBodyData& solverBodyData, PxU32 lockFlags); + +// PT: TODO: using PxsBodyCore in the interface makes us write less data to the stack for passing arguments, and we can take advantage of the class layout +// (we know what is aligned or not, we know if it is safe to V4Load vectors, etc). Note that this is what we previously had, which is why PxsBodyCore was still +// forward-referenced above. +//void copyToSolverBodyData(PxSolverBodyData& solverBodyData, const PxsBodyCore& core, const PxU32 nodeIndex); + +} + +} + +#endif //DY_SOLVERBODY_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h new file mode 100644 index 000000000..303b4d0d0 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h @@ -0,0 +1,204 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVER_CONSTRAINT_1D_H +#define DY_SOLVER_CONSTRAINT_1D_H + +#include "foundation/PxVec3.h" +#include "PxvConfig.h" +#include "DyArticulationUtils.h" +#include "DySolverConstraintTypes.h" +#include "DySolverBody.h" +#include "PxConstraintDesc.h" +#include "DySolverConstraintDesc.h" + +namespace physx +{ + +namespace Dy +{ + +// dsequeira: we should probably fork these structures for constraints and extended constraints, +// since there's a few things that are used for one but not the other + +struct SolverConstraint1DHeader +{ + PxU8 type; // enum SolverConstraintType - must be first byte + PxU8 count; // count of following 1D constraints + PxU8 dominance; + PxU8 breakable; // indicate whether this constraint is breakable or not + + PxReal linBreakImpulse; + PxReal angBreakImpulse; + PxReal invMass0D0; + PxVec3 body0WorldOffset; + PxReal invMass1D1; + PxReal linearInvMassScale0; // only used by articulations + PxReal angularInvMassScale0; // only used by articulations + PxReal linearInvMassScale1; // only used by articulations + PxReal angularInvMassScale1; // only used by articulations +}; + +PX_COMPILE_TIME_ASSERT(sizeof(SolverConstraint1DHeader) == 48); + +PX_ALIGN_PREFIX(16) +struct SolverConstraint1D +{ +public: + PxVec3 lin0; //!< linear velocity projection (body 0) + PxReal constant; //!< constraint constant term + + PxVec3 lin1; //!< linear velocity projection (body 1) + PxReal unbiasedConstant; //!< constraint constant term without bias + + PxVec3 ang0; //!< angular velocity projection (body 0) + PxReal velMultiplier; //!< constraint velocity multiplier + + PxVec3 ang1; //!< angular velocity projection (body 1) + PxReal impulseMultiplier; //!< constraint impulse multiplier + + PxVec3 ang0Writeback; //!< unscaled angular velocity projection (body 0) + PxU32 pad; + + PxReal minImpulse; //!< Lower bound on impulse magnitude + PxReal maxImpulse; //!< Upper bound on impulse magnitude + PxReal appliedForce; //!< applied force to correct velocity+bias + PxU32 flags; +} PX_ALIGN_SUFFIX(16); + +PX_COMPILE_TIME_ASSERT(sizeof(SolverConstraint1D) == 96); + + +struct SolverConstraint1DExt : public SolverConstraint1D +{ +public: + Cm::SpatialVectorV deltaVA; + Cm::SpatialVectorV deltaVB; +}; + +PX_COMPILE_TIME_ASSERT(sizeof(SolverConstraint1DExt) == 160); + + +PX_FORCE_INLINE void init(SolverConstraint1DHeader& h, + PxU8 count, + bool isExtended, + const PxConstraintInvMassScale& ims) +{ + h.type = PxU8(isExtended ? DY_SC_TYPE_EXT_1D : DY_SC_TYPE_RB_1D); + h.count = count; + h.dominance = 0; + h.linearInvMassScale0 = ims.linear0; + h.angularInvMassScale0 = ims.angular0; + h.linearInvMassScale1 = -ims.linear1; + h.angularInvMassScale1 = -ims.angular1; +} + +PX_FORCE_INLINE void init(SolverConstraint1D& c, + const PxVec3& _linear0, const PxVec3& _linear1, + const PxVec3& _angular0, const PxVec3& _angular1, + PxReal _minImpulse, PxReal _maxImpulse) +{ + PX_ASSERT(_linear0.isFinite()); + PX_ASSERT(_linear1.isFinite()); + c.lin0 = _linear0; + c.lin1 = _linear1; + c.ang0 = _angular0; + c.ang1 = _angular1; + c.minImpulse = _minImpulse; + c.maxImpulse = _maxImpulse; + c.flags = 0; + c.appliedForce = 0; +} + +PX_FORCE_INLINE bool needsNormalVel(const Px1DConstraint &c) +{ + return c.flags & Px1DConstraintFlag::eRESTITUTION + || (c.flags & Px1DConstraintFlag::eSPRING && c.flags & Px1DConstraintFlag::eACCELERATION_SPRING); +} + +PX_FORCE_INLINE void setSolverConstants(PxReal& constant, + PxReal& unbiasedConstant, + PxReal& velMultiplier, + PxReal& impulseMultiplier, + const Px1DConstraint& c, + PxReal normalVel, + PxReal unitResponse, + PxReal minRowResponse, + PxReal erp, + PxReal dt, + PxReal recipdt) +{ + PX_ASSERT(PxIsFinite(unitResponse)); + PxReal recipResponse = unitResponse <= minRowResponse ? 0 : 1.0f/unitResponse; + + PxReal geomError = c.geometricError * erp; + + if(c.flags & Px1DConstraintFlag::eSPRING) + { + PxReal a = dt * dt * c.mods.spring.stiffness + dt * c.mods.spring.damping; + PxReal b = dt * (c.mods.spring.damping * c.velocityTarget - c.mods.spring.stiffness * geomError); + + if(c.flags & Px1DConstraintFlag::eACCELERATION_SPRING) + { + PxReal x = 1.0f/(1.0f+a); + constant = unbiasedConstant = x * recipResponse * b; + velMultiplier = -x * recipResponse * a; + impulseMultiplier = 1.0f-x; + } + else + { + PxReal x = unitResponse == 0.f ? 0.f : 1.0f/(1.0f+a*unitResponse); + constant = unbiasedConstant = x * b; + velMultiplier = -x*a; + impulseMultiplier = 1.0f-x; + } + } + else + { + velMultiplier = -recipResponse; + impulseMultiplier = 1.0f; + + if(c.flags & Px1DConstraintFlag::eRESTITUTION && -normalVel>c.mods.bounce.velocityThreshold) + { + unbiasedConstant = constant = recipResponse * c.mods.bounce.restitution*-normalVel; + } + else + { + // see usage of 'for internal use' in preprocessRows() + constant = recipResponse * (c.velocityTarget - geomError*recipdt); + unbiasedConstant = recipResponse * (c.velocityTarget - c.forInternalUse*recipdt); + } + } +} + +} +} + +#endif //DY_SOLVER_CONSTRAINT_1D_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h new file mode 100644 index 000000000..0da65a22a --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h @@ -0,0 +1,106 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_SOLVERCONSTRAINT1D4_H +#define DY_SOLVERCONSTRAINT1D4_H + +#include "foundation/PxVec3.h" +#include "PxvConfig.h" +#include "DyArticulationUtils.h" +#include "DySolverConstraint1D.h" + +namespace physx +{ + +namespace Dy +{ + +struct SolverConstraint1DHeader4 +{ + PxU8 type; // enum SolverConstraintType - must be first byte + PxU8 pad0[3]; + //These counts are the max of the 4 sets of data. + //When certain pairs have fewer constraints than others, they are padded with 0s so that no work is performed but + //calculations are still shared (afterall, they're computationally free because we're doing 4 things at a time in SIMD) + PxU32 count; + PxU8 count0, count1, count2, count3; + PxU8 break0, break1, break2, break3; + + Vec4V linBreakImpulse; + Vec4V angBreakImpulse; + Vec4V invMass0D0; + Vec4V invMass1D1; + Vec4V angD0; + Vec4V angD1; + + Vec4V body0WorkOffsetX; + Vec4V body0WorkOffsetY; + Vec4V body0WorkOffsetZ; +}; + +struct SolverConstraint1DBase4 +{ +public: + Vec4V lin0X; + Vec4V lin0Y; + Vec4V lin0Z; + Vec4V ang0X; + Vec4V ang0Y; + Vec4V ang0Z; + Vec4V ang0WritebackX; + Vec4V ang0WritebackY; + Vec4V ang0WritebackZ; + Vec4V constant; + Vec4V unbiasedConstant; + Vec4V velMultiplier; + Vec4V impulseMultiplier; + Vec4V minImpulse; + Vec4V maxImpulse; + Vec4V appliedForce; + PxU32 flags[4]; +}; + +PX_COMPILE_TIME_ASSERT(sizeof(SolverConstraint1DBase4) == 272); + +struct SolverConstraint1DDynamic4 : public SolverConstraint1DBase4 +{ + Vec4V lin1X; + Vec4V lin1Y; + Vec4V lin1Z; + Vec4V ang1X; + Vec4V ang1Y; + Vec4V ang1Z; +}; +PX_COMPILE_TIME_ASSERT(sizeof(SolverConstraint1DDynamic4) == 368); + +} + +} + +#endif //DY_SOLVERCONSTRAINT1D4_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h new file mode 100644 index 000000000..b2f7e25e4 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h @@ -0,0 +1,222 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVER_CONSTRAINT_1D_STEP_H +#define DY_SOLVER_CONSTRAINT_1D_STEP_H + +#include "foundation/PxVec3.h" +#include "PxvConfig.h" +#include "DyArticulationUtils.h" +#include "DySolverConstraintTypes.h" +#include "DySolverBody.h" +#include "PxConstraintDesc.h" +#include "DySolverConstraintDesc.h" + + +namespace physx +{ + namespace Dy + { + struct SolverContactHeaderStep + { + enum DySolverContactFlags + { + eHAS_FORCE_THRESHOLDS = 0x1 + }; + + PxU8 type; //Note: mType should be first as the solver expects a type in the first byte. + PxU8 flags; + PxU8 numNormalConstr; + PxU8 numFrictionConstr; //4 + + PxReal angDom0; //8 + PxReal angDom1; //12 + PxReal invMass0; //16 + + Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; //32 + PxVec3 normal; //48 + + PxReal maxPenBias; //52 + PxReal invMass1; //56 + PxReal minNormalForce; //60 + PxU32 broken; //64 + PxU8* frictionBrokenWritebackByte; //68 72 + Sc::ShapeInteraction* shapeInteraction; //72 80 + + + PX_FORCE_INLINE FloatV getStaticFriction() const { return V4GetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W); } + PX_FORCE_INLINE FloatV getDynamicFriction() const { return V4GetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W); } + PX_FORCE_INLINE FloatV getDominance0() const { return V4GetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W); } + PX_FORCE_INLINE FloatV getDominance1() const { return V4GetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W); } + }; + + struct SolverContactPointStep + { + PxVec3 raXnI; + PxF32 separation; + PxVec3 rbXnI; + PxF32 velMultiplier; + PxF32 targetVelocity; + PxF32 biasCoefficient; + //2 more slots here for extra data + PxU32 pad[2]; + }; + + struct SolverContactPointStepExt : public SolverContactPointStep + { + Vec3V linDeltaVA; + Vec3V linDeltaVB; + Vec3V angDeltaVA; + Vec3V angDeltaVB; + }; + + struct SolverContactFrictionStep + { + Vec4V normalXYZ_ErrorW; //16 + Vec4V raXnI_targetVelW; + Vec4V rbXnI_velMultiplierW; + PxReal biasScale; + PxReal appliedForce; + PxReal frictionScale; + PxU32 pad[1]; + + PX_FORCE_INLINE void setAppliedForce(const FloatV f) { FStore(f, &appliedForce); } + }; + + struct SolverContactFrictionStepExt : public SolverContactFrictionStep + { + Vec3V linDeltaVA; + Vec3V linDeltaVB; + Vec3V angDeltaVA; + Vec3V angDeltaVB; + }; + + struct SolverConstraint1DHeaderStep + { + PxU8 type; // enum SolverConstraintType - must be first byte + PxU8 count; // count of following 1D constraints + PxU8 dominance; + PxU8 breakable; // indicate whether this constraint is breakable or not + PxReal linBreakImpulse; + PxReal angBreakImpulse; + PxReal invMass0D0; + + PxVec3 body0WorldOffset; + PxReal invMass1D1; + + PxVec3 rAWorld; + PxReal linearInvMassScale0; // only used by articulations + + PxVec3 rBWorld; + PxReal angularInvMassScale0; + + PxReal linearInvMassScale1; // only used by articulations + PxReal angularInvMassScale1; + PxU32 pad[2]; + + //Ortho axes for body 0, recipResponse in W component + PxVec4 angOrthoAxis0_recipResponseW[3]; + //Ortho axes for body 1, error of body in W component + PxVec4 angOrthoAxis1_Error[3]; + }; + + + PX_FORCE_INLINE void init(SolverConstraint1DHeaderStep& h, + PxU8 count, + bool isExtended, + const PxConstraintInvMassScale& ims) + { + h.type = PxU8(isExtended ? DY_SC_TYPE_EXT_1D : DY_SC_TYPE_RB_1D); + h.count = count; + h.dominance = 0; + h.linearInvMassScale0 = ims.linear0; + h.angularInvMassScale0 = ims.angular0; + h.linearInvMassScale1 = -ims.linear1; + h.angularInvMassScale1 = -ims.angular1; + } + + + PX_ALIGN_PREFIX(16) + struct SolverConstraint1DStep + { + public: + PxVec3 lin0; //!< linear velocity projection (body 0) + PxReal error; //!< constraint error term - must be scaled by biasScale. Can be adjusted at run-time + + PxVec3 lin1; //!< linear velocity projection (body 1) + PxReal biasScale; //!< constraint constant bias scale. Constant + + PxVec3 ang0; //!< angular velocity projection (body 0) + PxReal velMultiplier; //!< constraint velocity multiplier + + PxVec3 ang1; //!< angular velocity projection (body 1) + PxReal impulseMultiplier; //!< constraint impulse multiplier + + PxReal velTarget; //!< Scaled target velocity of the constraint drive + + PxReal minImpulse; //!< Lower bound on impulse magnitude + PxReal maxImpulse; //!< Upper bound on impulse magnitude + PxReal appliedForce; //!< applied force to correct velocity+bias + + PxReal maxBias; + PxU32 flags; + PxReal recipResponse; //Constant. Only used for articulations; + PxReal angularErrorScale; //Constant + } PX_ALIGN_SUFFIX(16); + + struct SolverConstraint1DExtStep : public SolverConstraint1DStep + { + public: + Cm::SpatialVectorV deltaVA; + Cm::SpatialVectorV deltaVB; + }; + + PX_FORCE_INLINE void init(SolverConstraint1DStep& c, + const PxVec3& _linear0, const PxVec3& _linear1, + const PxVec3& _angular0, const PxVec3& _angular1, + PxReal _minImpulse, PxReal _maxImpulse) + { + PX_ASSERT(_linear0.isFinite()); + PX_ASSERT(_linear1.isFinite()); + c.lin0 = _linear0; + c.lin1 = _linear1; + c.ang0 = _angular0; + c.ang1 = _angular1; + c.minImpulse = _minImpulse; + c.maxImpulse = _maxImpulse; + c.flags = 0; + c.appliedForce = 0; + c.angularErrorScale = 1.f; + } + + }//namespace Dy +} + +#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h new file mode 100644 index 000000000..6f6408260 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCONSTRAINTDESC_H +#define DY_SOLVERCONSTRAINTDESC_H + +#include "PxvConfig.h" +#include "DySolverConstraintTypes.h" +#include "PsUtilities.h" +#include "PxConstraintDesc.h" +#include "solver/PxSolverDefs.h" + +namespace physx +{ + +struct PxcNpWorkUnit; + +struct PxsContactManagerOutput; + +namespace Cm +{ + class SpatialVector; +} + +struct PxSolverBody; +struct PxSolverBodyData; + +namespace Dy +{ + +struct FsData; + + + + +// dsequeira: moved this articulation stuff here to sever a build dep on Articulation.h through DyThreadContext.h and onward + +struct SelfConstraintBlock +{ + PxU32 startId; + PxU32 numSelfConstraints; + PxU16 fsDataLength; + PxU16 requiredSolverProgress; + uintptr_t eaFsData; +}; + +//This class rolls together multiple contact managers into a single contact manager. +struct CompoundContactManager +{ + PxU32 mStartIndex; + PxU16 mStride; + PxU16 mReducedContactCount; + + PxcNpWorkUnit* unit; //This is a work unit but the contact buffer has been adjusted to contain all the contacts for all the subsequent pairs + PxsContactManagerOutput* cmOutput; + PxU8* originalContactPatches; //This is the original contact buffer that we replaced with a combined buffer + PxU8* originalContactPoints; + PxU8 originalContactCount; + PxU8 originalPatchCount; + PxU8 originalStatusFlags; + PxReal* originalForceBuffer; //This is the original force buffer that we replaced with a combined force buffer + PxU16* forceBufferList; //This is a list of indices from the reduced force buffer to the original force buffers - we need this to fix up the write-backs from the solver +}; + +struct SolverConstraintPrepState +{ +enum Enum +{ + eOUT_OF_MEMORY, + eUNBATCHABLE, + eSUCCESS +}; +}; + +PX_FORCE_INLINE bool isArticulationConstraint(const PxSolverConstraintDesc& desc) +{ + return desc.linkIndexA != PxSolverConstraintDesc::NO_LINK || + desc.linkIndexB != PxSolverConstraintDesc::NO_LINK; +} + + +PX_FORCE_INLINE void setConstraintLength(PxSolverConstraintDesc& desc, const PxU32 constraintLength) +{ + PX_ASSERT(0==(constraintLength & 0x0f)); + PX_ASSERT(constraintLength <= PX_MAX_U16 * 16); + desc.constraintLengthOver16 = Ps::to16(constraintLength >> 4); +} + +PX_FORCE_INLINE void setWritebackLength(PxSolverConstraintDesc& desc, const PxU32 writeBackLength) +{ + PX_ASSERT(0==(writeBackLength & 0x03)); + PX_ASSERT(writeBackLength <= PX_MAX_U16 * 4); + desc.writeBackLengthOver4 = Ps::to16(writeBackLength >> 2); +} + +PX_FORCE_INLINE PxU32 getConstraintLength(const PxSolverConstraintDesc& desc) +{ + return PxU32(desc.constraintLengthOver16 << 4); +} + + +PX_FORCE_INLINE PxU32 getWritebackLength(const PxSolverConstraintDesc& desc) +{ + return PxU32(desc.writeBackLengthOver4 << 2); +} + +PX_COMPILE_TIME_ASSERT(0 == (0x0f & sizeof(PxSolverConstraintDesc))); + +#define MAX_PERMITTED_SOLVER_PROGRESS 0xFFFF + +} + +} + +#endif //DY_SOLVERCONSTRAINTDESC_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h new file mode 100644 index 000000000..007bd5972 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_SOLVER_CONSTRAINT_EXT_SHARED_H +#define DY_SOLVER_CONSTRAINT_EXT_SHARED_H + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "DyArticulationContactPrep.h" +#include "DySolverConstraintDesc.h" +#include "DySolverConstraint1D.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DyArticulationHelper.h" +#include "PxcNpWorkUnit.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" + +namespace physx +{ + namespace Dy + { + FloatV setupExtSolverContact(const SolverExtBody& b0, const SolverExtBody& b1, + const PxF32 d0, const PxF32 d1, const PxF32 angD0, const PxF32 angD1, const PxTransform& bodyFrame0, const PxTransform& bodyFrame1, + const Vec3VArg normal, const FloatVArg invDt, const FloatVArg invDtp8, const FloatVArg restDistance, const FloatVArg maxPenBias, const FloatVArg restitution, + const FloatVArg bounceThreshold, const Gu::ContactPoint& contact, SolverContactPointExt& solverContact, const FloatVArg ccdMaxSeparation, + Cm::SpatialVectorF* Z); + } //namespace Dy +} + +#endif //DY_SOLVER_CONSTRAINT_EXT_SHARED_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h new file mode 100644 index 000000000..6b857c781 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h @@ -0,0 +1,72 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCONSTRAINTTYPES_H +#define DY_SOLVERCONSTRAINTTYPES_H + +#include "foundation/PxSimpleTypes.h" +#include "PxvConfig.h" + +namespace physx +{ + +enum SolverConstraintType +{ + DY_SC_TYPE_NONE = 0, + DY_SC_TYPE_RB_CONTACT, // RB-only contact + DY_SC_TYPE_RB_1D, // RB-only 1D constraint + DY_SC_TYPE_EXT_CONTACT, // contact involving articulations + DY_SC_TYPE_EXT_1D, // 1D constraint involving articulations + DY_SC_TYPE_STATIC_CONTACT, // RB-only contact where body b is static + DY_SC_TYPE_NOFRICTION_RB_CONTACT, //RB-only contact with no friction patch + DY_SC_TYPE_BLOCK_RB_CONTACT, + DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT, + DY_SC_TYPE_BLOCK_1D, + DY_SC_TYPE_FRICTION, + DY_SC_TYPE_STATIC_FRICTION, + DY_SC_TYPE_EXT_FRICTION, + DY_SC_TYPE_BLOCK_FRICTION, + DY_SC_TYPE_BLOCK_STATIC_FRICTION, + DY_SC_CONSTRAINT_TYPE_COUNT //Count of the number of different constraint types in the solver +}; + +enum SolverConstraintFlags +{ + DY_SC_FLAG_OUTPUT_FORCE = (1<<1), + DY_SC_FLAG_KEEP_BIAS = (1<<2), + DY_SC_FLAG_ROT_EQ = (1<<3), + DY_SC_FLAG_ORTHO_TARGET = (1<<4), + DY_SC_FLAG_SPRING = (1<<5), + DY_SC_FLAG_INEQUALITY = (1<<6) +}; + +} + +#endif //DY_SOLVERCONSTRAINTTYPES_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp new file mode 100644 index 000000000..ca0027eba --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp @@ -0,0 +1,1261 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" + +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverContact.h" +#include "DySolverConstraint1D.h" +#include "DySolverConstraintDesc.h" +#include "DyThresholdTable.h" +#include "DySolverContext.h" +#include "PsUtilities.h" +#include "DyConstraint.h" +#include "PsAtomic.h" +#include "DySolverConstraintsShared.h" + +namespace physx +{ + +namespace Dy +{ + +//Port of scalar implementation to SIMD maths with some interleaving of instructions +void solve1D(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + PX_UNUSED(cache); + PxSolverBody& b0 = *desc.bodyA; + PxSolverBody& b1 = *desc.bodyB; + + PxU8* PX_RESTRICT bPtr = desc.constraint; + if (bPtr == NULL) + return; + //PxU32 length = desc.constraintLength; + + const SolverConstraint1DHeader* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1D* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeader)); + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V linVel1 = V3LoadA(b1.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + Vec3V angState1 = V3LoadA(b1.angularState); + + const FloatV invMass0 = FLoad(header->invMass0D0); + const FloatV invMass1 = FLoad(header->invMass1D1); + const FloatV invInertiaScale0 = FLoad(header->angularInvMassScale0); + const FloatV invInertiaScale1 = FLoad(header->angularInvMassScale1); + + + for(PxU32 i=0; icount;++i, base++) + { + Ps::prefetchLine(base+1); + SolverConstraint1D& c = *base; + + const Vec3V clinVel0 = V3LoadA(c.lin0); + const Vec3V clinVel1 = V3LoadA(c.lin1); + const Vec3V cangVel0 = V3LoadA(c.ang0); + const Vec3V cangVel1 = V3LoadA(c.ang1); + + const FloatV constant = FLoad(c.constant); + const FloatV vMul = FLoad(c.velMultiplier); + const FloatV iMul = FLoad(c.impulseMultiplier); + const FloatV appliedForce = FLoad(c.appliedForce); + //const FloatV targetVel = FLoad(c.targetVelocity); + + const FloatV maxImpulse = FLoad(c.maxImpulse); + const FloatV minImpulse = FLoad(c.minImpulse); + + const Vec3V v0 = V3MulAdd(linVel0, clinVel0, V3Mul(angState0, cangVel0)); + const Vec3V v1 = V3MulAdd(linVel1, clinVel1, V3Mul(angState1, cangVel1)); + + const FloatV normalVel = V3SumElems(V3Sub(v0, v1)); + const FloatV unclampedForce = FScaleAdd(iMul, appliedForce, FScaleAdd(vMul, normalVel, constant)); + const FloatV clampedForce = FMin(maxImpulse, (FMax(minImpulse, unclampedForce))); + const FloatV deltaF = FSub(clampedForce, appliedForce); + + FStore(clampedForce, &c.appliedForce); + linVel0 = V3ScaleAdd(clinVel0, FMul(deltaF, invMass0), linVel0); + linVel1 = V3NegScaleSub(clinVel1, FMul(deltaF, invMass1), linVel1); + angState0 = V3ScaleAdd(cangVel0, FMul(deltaF, invInertiaScale0), angState0); + //This should be negScaleSub but invInertiaScale1 is negated already + angState1 = V3ScaleAdd(cangVel1, FMul(deltaF, invInertiaScale1), angState1); + + } + + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(angState0, b0.angularState); + V3StoreA(linVel1, b1.linearVelocity); + V3StoreA(angState1, b1.angularState); + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularState.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularState.isFinite()); +} + +void conclude1D(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + SolverConstraint1DHeader* header = reinterpret_cast(desc.constraint); + if (header == NULL) + return; + PxU8* base = desc.constraint + sizeof(SolverConstraint1DHeader); + PxU32 stride = header->type == DY_SC_TYPE_EXT_1D ? sizeof(SolverConstraint1DExt) : sizeof(SolverConstraint1D); + + for(PxU32 i=0; icount; i++) + { + SolverConstraint1D& c = *reinterpret_cast(base); + + c.constant = c.unbiasedConstant; + + base += stride; + } + PX_ASSERT(desc.constraint + getConstraintLength(desc) == base); +} + +// ============================================================== + +void solveContact(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + PxSolverBody& b0 = *desc.bodyA; + PxSolverBody& b1 = *desc.bodyB; + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V linVel1 = V3LoadA(b1.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + Vec3V angState1 = V3LoadA(b1.angularState); + + const PxU8* PX_RESTRICT last = desc.constraint + getConstraintLength(desc); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + while(currPtr < last) + { + SolverContactHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverContactPoint* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPoint); + + PxF32* forceBuffer = reinterpret_cast(currPtr); + currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + SolverContactFriction* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFriction); + + const FloatV invMassA = FLoad(hdr->invMass0); + const FloatV invMassB = FLoad(hdr->invMass1); + + const FloatV angDom0 = FLoad(hdr->angDom0); + const FloatV angDom1 = FLoad(hdr->angDom1); + + const Vec3V contactNormal = Vec3V_From_Vec4V_WUndefined(hdr->normal_minAppliedImpulseForFrictionW); + + const FloatV accumulatedNormalImpulse = solveDynamicContacts(contacts, numNormalConstr, contactNormal, invMassA, invMassB, + angDom0, angDom1, linVel0, angState0, linVel1, angState1, forceBuffer); + + if(cache.doFriction && numFrictionConstr) + { + const FloatV staticFrictionCof = hdr->getStaticFriction(); + const FloatV dynamicFrictionCof = hdr->getDynamicFriction(); + const FloatV maxFrictionImpulse = FMul(staticFrictionCof, accumulatedNormalImpulse); + const FloatV maxDynFrictionImpulse = FMul(dynamicFrictionCof, accumulatedNormalImpulse); + const FloatV negMaxDynFrictionImpulse = FNeg(maxDynFrictionImpulse); + + BoolV broken = BFFFF(); + + if(cache.writeBackIteration) + Ps::prefetchLine(hdr->frictionBrokenWritebackByte); + + for(PxU32 i=0;i maxFrictionImpulse + // clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse] + // (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce] + // set broken flag to true || broken flag + + // FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier); + // FloatV potentialSumF = FAdd(appliedForce, deltaF); + + const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1); + + // On XBox this clamping code uses the vector simple pipe rather than vector float, + // which eliminates a lot of stall cycles + + const BoolV clamp = FIsGrtr(FAbs(totalImpulse), maxFrictionImpulse); + + const FloatV totalClamped = FMin(maxDynFrictionImpulse, FMax(negMaxDynFrictionImpulse, totalImpulse)); + + const FloatV newAppliedForce = FSel(clamp, totalClamped,totalImpulse); + + broken = BOr(broken, clamp); + + FloatV deltaF = FSub(newAppliedForce, appliedForce); + + // we could get rid of the stall here by calculating and clamping delta separately, but + // the complexity isn't really worth it. + + linVel0 = V3ScaleAdd(delLinVel0, deltaF, linVel0); + linVel1 = V3NegScaleSub(delLinVel1, deltaF, linVel1); + angState0 = V3ScaleAdd(raXn, FMul(deltaF, angDom0), angState0); + angState1 = V3NegScaleSub(rbXn, FMul(deltaF, angDom1), angState1); + + f.setAppliedForce(newAppliedForce); + + + } + Store_From_BoolV(broken, &hdr->broken); + } + + } + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularState.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularState.isFinite()); + + // Write back + V3StoreU(linVel0, b0.linearVelocity); + V3StoreU(linVel1, b1.linearVelocity); + V3StoreU(angState0, b0.angularState); + V3StoreU(angState1, b1.angularState); + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularState.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularState.isFinite()); + + PX_ASSERT(currPtr == last); +} + +void solveContact_BStatic(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + PxSolverBody& b0 = *desc.bodyA; + //PxSolverBody& b1 = *desc.bodyB; + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + + const PxU8* PX_RESTRICT last = desc.constraint + getConstraintLength(desc); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + while(currPtr < last) + { + SolverContactHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverContactPoint* PX_RESTRICT contacts = reinterpret_cast(currPtr); + //Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPoint); + + PxF32* forceBuffer = reinterpret_cast(currPtr); + currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + SolverContactFriction* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFriction); + + + + const FloatV invMassA = FLoad(hdr->invMass0); + + const Vec3V contactNormal = Vec3V_From_Vec4V_WUndefined(hdr->normal_minAppliedImpulseForFrictionW); + const FloatV angDom0 = FLoad(hdr->angDom0); + + + const FloatV accumulatedNormalImpulse = solveStaticContacts(contacts, numNormalConstr, contactNormal, + invMassA, angDom0, linVel0, angState0, forceBuffer); + + if(cache.doFriction && numFrictionConstr) + { + const FloatV maxFrictionImpulse = FMul(hdr->getStaticFriction(), accumulatedNormalImpulse); + const FloatV maxDynFrictionImpulse = FMul(hdr->getDynamicFriction(), accumulatedNormalImpulse); + + BoolV broken = BFFFF(); + if(cache.writeBackIteration) + Ps::prefetchLine(hdr->frictionBrokenWritebackByte); + + for(PxU32 i=0;i maxFrictionImpulse + // clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse] + // (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce] + // set broken flag to true || broken flag + + // FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier); + // FloatV potentialSumF = FAdd(appliedForce, deltaF); + + const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1); + + // On XBox this clamping code uses the vector simple pipe rather than vector float, + // which eliminates a lot of stall cycles + + const BoolV clamp = FIsGrtr(FAbs(totalImpulse), maxFrictionImpulse); + + const FloatV totalClamped = FMin(maxDynFrictionImpulse, FMax(negMaxDynFrictionImpulse, totalImpulse)); + + broken = BOr(broken, clamp); + + const FloatV newAppliedForce = FSel(clamp, totalClamped,totalImpulse); + + FloatV deltaF = FSub(newAppliedForce, appliedForce); + + // we could get rid of the stall here by calculating and clamping delta separately, but + // the complexity isn't really worth it. + + linVel0 = V3ScaleAdd(delLinVel0, deltaF, linVel0); + angState0 = V3ScaleAdd(raXn, FMul(deltaF, angDom0), angState0); + + f.setAppliedForce(newAppliedForce); + + } + Store_From_BoolV(broken, &hdr->broken); + } + + } + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularState.isFinite()); + + // Write back + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(angState0, b0.angularState); + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularState.isFinite()); + + PX_ASSERT(currPtr == last); +} + + +void concludeContact(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + PxU8* PX_RESTRICT cPtr = desc.constraint; + + const FloatV zero = FZero(); + + PxU8* PX_RESTRICT last = desc.constraint + getConstraintLength(desc); + while(cPtr < last) + { + const SolverContactHeader* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + //if(cPtr < last) + //Ps::prefetchLine(cPtr, 512); + Ps::prefetchLine(cPtr,128); + Ps::prefetchLine(cPtr,256); + Ps::prefetchLine(cPtr,384); + + const PxU32 pointStride = hdr->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactPointExt) + : sizeof(SolverContactPoint); + for(PxU32 i=0;i(cPtr); + cPtr += pointStride; + //c->scaledBias = PxMin(c->scaledBias, 0.f); + c->biasedErr = c->unbiasedErr; + } + + cPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); //Jump over force buffers + + const PxU32 frictionStride = hdr->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactFrictionExt) + : sizeof(SolverContactFriction); + for(PxU32 i=0;i(cPtr); + cPtr += frictionStride; + f->setBias(zero); + } + } + PX_ASSERT(cPtr == last); +} + +void writeBackContact(const PxSolverConstraintDesc& desc, SolverContext& cache, + PxSolverBodyData& bd0, PxSolverBodyData& bd1) +{ + + PxReal normalForce = 0; + + PxU8* PX_RESTRICT cPtr = desc.constraint; + PxReal* PX_RESTRICT vForceWriteback = reinterpret_cast(desc.writeBack); + PxU8* PX_RESTRICT last = desc.constraint + getConstraintLength(desc); + + bool forceThreshold = false; + + while(cPtr < last) + { + const SolverContactHeader* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactHeader); + + forceThreshold = hdr->flags & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + //if(cPtr < last) + Ps::prefetchLine(cPtr, 256); + Ps::prefetchLine(cPtr, 384); + + const PxU32 pointStride = hdr->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactPointExt) + : sizeof(SolverContactPoint); + + cPtr += pointStride * numNormalConstr; + PxF32* forceBuffer = reinterpret_cast(cPtr); + cPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + if(vForceWriteback!=NULL) + { + for(PxU32 i=0; itype == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactFrictionExt) + : sizeof(SolverContactFriction); + + if(hdr->broken && hdr->frictionBrokenWritebackByte != NULL) + { + *hdr->frictionBrokenWritebackByte = 1; + } + + cPtr += frictionStride * numFrictionConstr; + + } + PX_ASSERT(cPtr == last); + + + + if(forceThreshold && desc.linkIndexA == PxSolverConstraintDesc::NO_LINK && desc.linkIndexB == PxSolverConstraintDesc::NO_LINK && + normalForce !=0 && (bd0.reportThreshold < PX_MAX_REAL || bd1.reportThreshold < PX_MAX_REAL)) + { + ThresholdStreamElement elt; + elt.normalForce = normalForce; + elt.threshold = PxMin(bd0.reportThreshold, bd1.reportThreshold); + elt.nodeIndexA = IG::NodeIndex(bd0.nodeIndex); + elt.nodeIndexB = IG::NodeIndex(bd1.nodeIndex); + elt.shapeInteraction = reinterpret_cast(desc.constraint)->shapeInteraction; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + PX_ASSERT(cache.mThresholdStreamIndex(desc.writeBack); + if(writeback) + { + SolverConstraint1DHeader* header = reinterpret_cast(desc.constraint); + PxU8* base = desc.constraint + sizeof(SolverConstraint1DHeader); + PxU32 stride = header->type == DY_SC_TYPE_EXT_1D ? sizeof(SolverConstraint1DExt) : sizeof(SolverConstraint1D); + + PxVec3 lin(0), ang(0); + for(PxU32 i=0; icount; i++) + { + const SolverConstraint1D* c = reinterpret_cast(base); + if(c->flags & DY_SC_FLAG_OUTPUT_FORCE) + { + lin += c->lin0 * c->appliedForce; + ang += c->ang0Writeback * c->appliedForce; + } + base += stride; + } + + ang -= header->body0WorldOffset.cross(lin); + writeback->linearImpulse = lin; + writeback->angularImpulse = ang; + writeback->broken = header->breakable ? PxU32(lin.magnitude()>header->linBreakImpulse || ang.magnitude()>header->angBreakImpulse) : 0; + + PX_ASSERT(desc.constraint + getConstraintLength(desc) == base); + } +} + + +void solve1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solve1D(desc[a-1], cache); + } + solve1D(desc[constraintCount-1], cache); +} + +void solve1DConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solve1D(desc[a-1], cache); + conclude1D(desc[a-1], cache); + } + solve1D(desc[constraintCount-1], cache); + conclude1D(desc[constraintCount-1], cache); +} + +void solve1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a-1].bodyBDataIndex]; + solve1D(desc[a-1], cache); + writeBack1D(desc[a-1], cache, bd0, bd1); + } + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[constraintCount-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[constraintCount-1].bodyBDataIndex]; + solve1D(desc[constraintCount-1], cache); + writeBack1D(desc[constraintCount-1], cache, bd0, bd1); +} + +void writeBack1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a-1].bodyBDataIndex]; + writeBack1D(desc[a-1], cache, bd0, bd1); + } + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[constraintCount-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[constraintCount-1].bodyBDataIndex]; + writeBack1D(desc[constraintCount-1], cache, bd0, bd1); +} + +void solveContactBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solveContact(desc[a-1], cache); + } + solveContact(desc[constraintCount-1], cache); +} + +void solveContactConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solveContact(desc[a-1], cache); + concludeContact(desc[a-1], cache); + } + solveContact(desc[constraintCount-1], cache); + concludeContact(desc[constraintCount-1], cache); +} + +void solveContactBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a-1].bodyBDataIndex]; + solveContact(desc[a-1], cache); + writeBackContact(desc[a-1], cache, bd0, bd1); + } + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[constraintCount-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[constraintCount-1].bodyBDataIndex]; + solveContact(desc[constraintCount-1], cache); + writeBackContact(desc[constraintCount-1], cache, bd0, bd1); + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveContact_BStaticBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solveContact_BStatic(desc[a-1], cache); + } + solveContact_BStatic(desc[constraintCount-1], cache); +} + +void solveContact_BStaticConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + solveContact_BStatic(desc[a-1], cache); + concludeContact(desc[a-1], cache); + } + solveContact_BStatic(desc[constraintCount-1], cache); + concludeContact(desc[constraintCount-1], cache); +} + +void solveContact_BStaticBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 1; a < constraintCount; ++a) + { + Ps::prefetchLine(desc[a].constraint); + Ps::prefetchLine(desc[a].constraint, 128); + Ps::prefetchLine(desc[a].constraint, 256); + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a-1].bodyBDataIndex]; + solveContact_BStatic(desc[a-1], cache); + writeBackContact(desc[a-1], cache, bd0, bd1); + } + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[constraintCount-1].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[constraintCount-1].bodyBDataIndex]; + solveContact_BStatic(desc[constraintCount-1], cache); + writeBackContact(desc[constraintCount-1], cache, bd0, bd1); + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Not enough space to write 4 more thresholds back! + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void clearExt1D(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + PxU8* PX_RESTRICT bPtr = desc.constraint; + const SolverConstraint1DHeader* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DExt* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeader)); + + for (PxU32 i = 0; i < header->count; ++i, base++) + { + base->appliedForce = 0.f; + } +} + +//Port of scalar implementation to SIMD maths with some interleaving of instructions +void solveExt1D(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + PxU8* PX_RESTRICT bPtr = desc.constraint; + //PxU32 length = desc.constraintLength; + + const SolverConstraint1DHeader* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DExt* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeader)); + + Vec3V linVel0, angVel0, linVel1, angVel1; + + if (desc.articulationA == desc.articulationB) + { + Cm::SpatialVectorV v0, v1; + desc.articulationA->pxcFsGetVelocities(desc.linkIndexA, desc.linkIndexB, v0, v1); + linVel0 = v0.linear; + angVel0 = v0.angular; + linVel1 = v1.linear; + angVel1 = v1.angular; + } + else + { + + if (desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + linVel0 = V3LoadA(desc.bodyA->linearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + } + } + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + for(PxU32 i=0; icount;++i, base++) + { + Ps::prefetchLine(base+1); + + const Vec4V lin0XYZ_constantW = V4LoadA(&base->lin0.x); + const Vec4V lin1XYZ_unbiasedConstantW = V4LoadA(&base->lin1.x); + const Vec4V ang0XYZ_velMultiplierW = V4LoadA(&base->ang0.x); + const Vec4V ang1XYZ_impulseMultiplierW = V4LoadA(&base->ang1.x); + const Vec4V minImpulseX_maxImpulseY_appliedForceZ = V4LoadA(&base->minImpulse); + + const Vec3V lin0 = Vec3V_From_Vec4V(lin0XYZ_constantW); FloatV constant = V4GetW(lin0XYZ_constantW); + const Vec3V lin1 = Vec3V_From_Vec4V(lin1XYZ_unbiasedConstantW); + const Vec3V ang0 = Vec3V_From_Vec4V(ang0XYZ_velMultiplierW); FloatV vMul = V4GetW(ang0XYZ_velMultiplierW); + const Vec3V ang1 = Vec3V_From_Vec4V(ang1XYZ_impulseMultiplierW); FloatV iMul = V4GetW(ang1XYZ_impulseMultiplierW); + + const FloatV minImpulse = V4GetX(minImpulseX_maxImpulseY_appliedForceZ); + const FloatV maxImpulse = V4GetY(minImpulseX_maxImpulseY_appliedForceZ); + const FloatV appliedForce = V4GetZ(minImpulseX_maxImpulseY_appliedForceZ); + + const Vec3V v0 = V3MulAdd(linVel0, lin0, V3Mul(angVel0, ang0)); + const Vec3V v1 = V3MulAdd(linVel1, lin1, V3Mul(angVel1, ang1)); + const FloatV normalVel = V3SumElems(V3Sub(v0, v1)); + + const FloatV unclampedForce = FScaleAdd(iMul, appliedForce, FScaleAdd(vMul, normalVel, constant)); + const FloatV clampedForce = FMin(maxImpulse, (FMax(minImpulse, unclampedForce))); + const FloatV deltaF = FSub(clampedForce, appliedForce); + + FStore(clampedForce, &base->appliedForce); + li0 = V3ScaleAdd(lin0, deltaF, li0); ai0 = V3ScaleAdd(ang0, deltaF, ai0); + li1 = V3ScaleAdd(lin1, deltaF, li1); ai1 = V3ScaleAdd(ang1, deltaF, ai1); + + linVel0 = V3ScaleAdd(base->deltaVA.linear, deltaF, linVel0); angVel0 = V3ScaleAdd(base->deltaVA.angular, deltaF, angVel0); + linVel1 = V3ScaleAdd(base->deltaVB.linear, deltaF, linVel1); angVel1 = V3ScaleAdd(base->deltaVB.angular, deltaF, angVel1); + +#if 0 + PxVec3 lv0, lv1, av0, av1; + V3StoreU(linVel0, lv0); V3StoreU(linVel1, lv1); + V3StoreU(angVel0, av0); V3StoreU(angVel1, av1); + + PX_ASSERT(lv0.magnitude() < 30.f); + PX_ASSERT(lv1.magnitude() < 30.f); + PX_ASSERT(av0.magnitude() < 30.f); + PX_ASSERT(av1.magnitude() < 30.f); +#endif + + } + + if (desc.articulationA == desc.articulationB) + { + desc.articulationA->pxcFsApplyImpulses(desc.linkIndexA, V3Scale(li0, FLoad(header->linearInvMassScale0)), V3Scale(ai0, FLoad(header->angularInvMassScale0)), + desc.linkIndexB, V3Scale(li1, FLoad(header->linearInvMassScale1)), V3Scale(ai1, FLoad(header->angularInvMassScale1)), cache.Z, cache.deltaV); + } + else + { + + if (desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularState); + } + else + { + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, V3Scale(li0, FLoad(header->linearInvMassScale0)), + V3Scale(ai0, FLoad(header->angularInvMassScale0)), cache.Z, cache.deltaV); + + + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularState); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, V3Scale(li1, FLoad(header->linearInvMassScale1)), + V3Scale(ai1, FLoad(header->angularInvMassScale1)), cache.Z, cache.deltaV); + } + } + +} + +FloatV solveExtContacts(SolverContactPointExt* contacts, const PxU32 nbContactPoints, const Vec3VArg contactNormal, + Vec3V& linVel0, Vec3V& angVel0, + Vec3V& linVel1, Vec3V& angVel1, + Vec3V& li0, Vec3V& ai0, + Vec3V& li1, Vec3V& ai1, + PxF32* PX_RESTRICT appliedForceBuffer) +{ + + FloatV accumulatedNormalImpulse = FZero(); + for (PxU32 i = 0; ilinearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + } + + //Vec3V origLin0 = linVel0, origAng0 = angVel0, origLin1 = linVel1, origAng1 = angVel1; + + const PxU8* PX_RESTRICT last = desc.constraint + desc.constraintLengthOver16*16; + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + Vec3V linImpulse0 = V3Zero(), linImpulse1 = V3Zero(), angImpulse0 = V3Zero(), angImpulse1 = V3Zero(); + + while(currPtr < last) + { + SolverContactHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverContactPointExt* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPointExt); + + PxF32* appliedForceBuffer = reinterpret_cast(currPtr); + currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + SolverContactFrictionExt* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionExt); + + + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + const Vec3V contactNormal = Vec3V_From_Vec4V(hdr->normal_minAppliedImpulseForFrictionW); + const FloatV minNorImpulse = V4GetW(hdr->normal_minAppliedImpulseForFrictionW); + + const FloatV accumulatedNormalImpulse = FMax(solveExtContacts(contacts, numNormalConstr, contactNormal, linVel0, angVel0, linVel1, + angVel1, li0, ai0, li1, ai1, appliedForceBuffer), minNorImpulse); + + + if(cache.doFriction && numFrictionConstr) + { + Ps::prefetchLine(frictions); + const FloatV maxFrictionImpulse = FMul(hdr->getStaticFriction(), accumulatedNormalImpulse); + const FloatV maxDynFrictionImpulse = FMul(hdr->getDynamicFriction(), accumulatedNormalImpulse); + + BoolV broken = BFFFF(); + + for(PxU32 i=0;i maxFrictionImpulse + // clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse] + // (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce] + // set broken flag to true || broken flag + + // FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier); + // FloatV potentialSumF = FAdd(appliedForce, deltaF); + + const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1); + + // On XBox this clamping code uses the vector simple pipe rather than vector float, + // which eliminates a lot of stall cycles + + const BoolV clampLow = FIsGrtr(negMaxFrictionImpulse, totalImpulse); + const BoolV clampHigh = FIsGrtr(totalImpulse, maxFrictionImpulse); + + const FloatV totalClampedLow = FMax(negMaxDynFrictionImpulse, totalImpulse); + const FloatV totalClampedHigh = FMin(maxDynFrictionImpulse, totalImpulse); + + const FloatV newAppliedForce = FSel(clampLow, totalClampedLow, + FSel(clampHigh, totalClampedHigh, totalImpulse)); + + broken = BOr(broken, BOr(clampLow, clampHigh)); + + FloatV deltaF = FSub(newAppliedForce, appliedForce); + + linVel0 = V3ScaleAdd(f.linDeltaVA, deltaF, linVel0); + angVel0 = V3ScaleAdd(f.angDeltaVA, deltaF, angVel0); + linVel1 = V3ScaleAdd(f.linDeltaVB, deltaF, linVel1); + angVel1 = V3ScaleAdd(f.angDeltaVB, deltaF, angVel1); + + li0 = V3ScaleAdd(normal, deltaF, li0); ai0 = V3ScaleAdd(raXn, deltaF, ai0); + li1 = V3ScaleAdd(normal, deltaF, li1); ai1 = V3ScaleAdd(rbXn, deltaF, ai1); + +#if 0 + PxVec3 lv0, lv1, av0, av1; + V3StoreU(linVel0, lv0); V3StoreU(linVel1, lv1); + V3StoreU(angVel0, av0); V3StoreU(angVel1, av1); + + PX_ASSERT(lv0.magnitude() < 30.f); + PX_ASSERT(lv1.magnitude() < 30.f); + PX_ASSERT(av0.magnitude() < 30.f); + PX_ASSERT(av1.magnitude() < 30.f); +#endif + + f.setAppliedForce(newAppliedForce); + } + Store_From_BoolV(broken, &hdr->broken); + } + + linImpulse0 = V3ScaleAdd(li0, hdr->getDominance0(), linImpulse0); + angImpulse0 = V3ScaleAdd(ai0, FLoad(hdr->angDom0), angImpulse0); + linImpulse1 = V3NegScaleSub(li1, hdr->getDominance1(), linImpulse1); + angImpulse1 = V3NegScaleSub(ai1, FLoad(hdr->angDom1), angImpulse1); + } + + if(desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularState); + } + else + { + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, + linImpulse0, angImpulse0, cache.Z, cache.deltaV); + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularState); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, + linImpulse1, angImpulse1, cache.Z, cache.deltaV); + } + + PX_ASSERT(currPtr == last); +} + + +void solveExtContactBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtContact(desc[a], cache); + } +} + +void solveExtContactConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtContact(desc[a], cache); + concludeContact(desc[a], cache); + } +} + +void solveExtContactBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a].linkIndexA != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a].linkIndexB != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyBDataIndex]; + + solveExtContact(desc[a], cache); + writeBackContact(desc[a], cache, bd0, bd1); + } + if(cache.mThresholdStreamIndex > 0) + { + //Not enough space to write 4 more thresholds back! + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveExt1DBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExt1D(desc[a], cache); + } +} + +void solveExt1DConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExt1D(desc[a], cache); + conclude1D(desc[a], cache); + } +} + +void solveExt1DBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a].linkIndexA != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a].linkIndexB != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyBDataIndex]; + solveExt1D(desc[a], cache); + writeBack1D(desc[a], cache, bd0, bd1); + } +} + +void ext1DBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a].linkIndexA != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a].linkIndexB != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyBDataIndex]; + writeBack1D(desc[a], cache, bd0, bd1); + } +} + +void solveConcludeExtContact (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveExtContact(desc, cache); + concludeContact(desc, cache); +} + +void solveConcludeExt1D (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveExt1D(desc, cache); + conclude1D(desc, cache); +} + + +void solveConclude1D(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solve1D(desc, cache); + conclude1D(desc, cache); +} + +void solveConcludeContact (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveContact(desc, cache); + concludeContact(desc, cache); +} + +void solveConcludeContact_BStatic (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveContact_BStatic(desc, cache); + concludeContact(desc, cache); +} + + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp new file mode 100644 index 000000000..fb196cb62 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp @@ -0,0 +1,1227 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "PsFPU.h" + +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverContact.h" +#include "DySolverConstraint1D.h" +#include "DySolverConstraintDesc.h" +#include "DyThresholdTable.h" +#include "DySolverContext.h" +#include "PsUtilities.h" +#include "DyConstraint.h" +#include "PsAtomic.h" +#include "DySolverContact4.h" +#include "DySolverConstraint1D4.h" + +namespace physx +{ + +namespace Dy +{ + +static void solveContact4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& cache) +{ + PxSolverBody& b00 = *desc[0].bodyA; + PxSolverBody& b01 = *desc[0].bodyB; + PxSolverBody& b10 = *desc[1].bodyA; + PxSolverBody& b11 = *desc[1].bodyB; + PxSolverBody& b20 = *desc[2].bodyA; + PxSolverBody& b21 = *desc[2].bodyB; + PxSolverBody& b30 = *desc[3].bodyA; + PxSolverBody& b31 = *desc[3].bodyB; + + //We'll need this. + const Vec4V vZero = V4Zero(); + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&b01.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularState.x); + Vec4V angState01 = V4LoadA(&b01.angularState.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&b11.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularState.x); + Vec4V angState11 = V4LoadA(&b11.angularState.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&b21.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularState.x); + Vec4V angState21 = V4LoadA(&b21.angularState.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&b31.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularState.x); + Vec4V angState31 = V4LoadA(&b31.angularState.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2, linVel0T3; + Vec4V linVel1T0, linVel1T1, linVel1T2, linVel1T3; + Vec4V angState0T0, angState0T1, angState0T2, angState0T3; + Vec4V angState1T0, angState1T1, angState1T2, angState1T3; + + + PX_TRANSPOSE_44(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2, linVel0T3); + PX_TRANSPOSE_44(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2, linVel1T3); + PX_TRANSPOSE_44(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2, angState0T3); + PX_TRANSPOSE_44(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2, angState1T3); + + + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + Vec4V vMax = V4Splat(FMax()); + + const PxU8* PX_RESTRICT prefetchAddress = currPtr + sizeof(SolverContactHeader4) + sizeof(SolverContactBatchPointDynamic4); + + const SolverContactHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + const Vec4V invMassA = hdr->invMass0D0; + const Vec4V invMassB = hdr->invMass1D1; + + const Vec4V sumInvMass = V4Add(invMassA, invMassB); + + + while(currPtr < last) + { + + hdr = reinterpret_cast(currPtr); + + PX_ASSERT(hdr->type == DY_SC_TYPE_BLOCK_RB_CONTACT); + + currPtr = reinterpret_cast(const_cast(hdr) + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + Vec4V* appliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numNormalConstr; + + SolverContactBatchPointDynamic4* PX_RESTRICT contacts = reinterpret_cast(currPtr); + + Vec4V* maxImpulses; + currPtr = reinterpret_cast(contacts + numNormalConstr); + PxU32 maxImpulseMask = 0; + if(hasMaxImpulse) + { + maxImpulseMask = 0xFFFFFFFF; + maxImpulses = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V) * numNormalConstr; + } + else + { + maxImpulses = &vMax; + } + + + SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(currPtr); + if(numFrictionConstr) + currPtr += sizeof(SolverFrictionSharedData4); + + Vec4V* frictionAppliedForce = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numFrictionConstr; + + const SolverContactFrictionDynamic4* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionDynamic4); + + Vec4V accumulatedNormalImpulse = vZero; + + const Vec4V angD0 = hdr->angDom0; + const Vec4V angD1 = hdr->angDom1; + + const Vec4V _normalT0 = hdr->normalX; + const Vec4V _normalT1 = hdr->normalY; + const Vec4V _normalT2 = hdr->normalZ; + + Vec4V contactNormalVel1 = V4Mul(linVel0T0, _normalT0); + Vec4V contactNormalVel3 = V4Mul(linVel1T0, _normalT0); + contactNormalVel1 = V4MulAdd(linVel0T1, _normalT1, contactNormalVel1); + contactNormalVel3 = V4MulAdd(linVel1T1, _normalT1, contactNormalVel3); + contactNormalVel1 = V4MulAdd(linVel0T2, _normalT2, contactNormalVel1); + contactNormalVel3 = V4MulAdd(linVel1T2, _normalT2, contactNormalVel3); + + Vec4V relVel1 = V4Sub(contactNormalVel1, contactNormalVel3); + + Vec4V accumDeltaF = vZero; + + for(PxU32 i=0;istaticFriction; + const Vec4V dynamicFric = hdr->dynamicFriction; + + const Vec4V maxFrictionImpulse = V4Mul(staticFric, accumulatedNormalImpulse); + const Vec4V maxDynFrictionImpulse = V4Mul(dynamicFric, accumulatedNormalImpulse); + const Vec4V negMaxDynFrictionImpulse = V4Neg(maxDynFrictionImpulse); + //const Vec4V negMaxFrictionImpulse = V4Neg(maxFrictionImpulse); + BoolV broken = BFFFF(); + + if(cache.writeBackIteration) + { + Ps::prefetchLine(fd->frictionBrokenWritebackByte[0]); + Ps::prefetchLine(fd->frictionBrokenWritebackByte[1]); + Ps::prefetchLine(fd->frictionBrokenWritebackByte[2]); + } + + + for(PxU32 i=0;inormalX[i&1]; + const Vec4V normalT1 = fd->normalY[i&1]; + const Vec4V normalT2 = fd->normalZ[i&1]; + + Vec4V normalVel1 = V4Mul(linVel0T0, normalT0); + Vec4V normalVel2 = V4Mul(f.raXnX, angState0T0); + Vec4V normalVel3 = V4Mul(linVel1T0, normalT0); + Vec4V normalVel4 = V4Mul(f.rbXnX, angState1T0); + + normalVel1 = V4MulAdd(linVel0T1, normalT1, normalVel1); + normalVel2 = V4MulAdd(f.raXnY, angState0T1, normalVel2); + normalVel3 = V4MulAdd(linVel1T1, normalT1, normalVel3); + normalVel4 = V4MulAdd(f.rbXnY, angState1T1, normalVel4); + + normalVel1 = V4MulAdd(linVel0T2, normalT2, normalVel1); + normalVel2 = V4MulAdd(f.raXnZ, angState0T2, normalVel2); + normalVel3 = V4MulAdd(linVel1T2, normalT2, normalVel3); + normalVel4 = V4MulAdd(f.rbXnZ, angState1T2, normalVel4); + + const Vec4V _normalVel = V4Add(normalVel1, normalVel2); + const Vec4V __normalVel = V4Add(normalVel3, normalVel4); + + // appliedForce -bias * velMultiplier - a hoisted part of the total impulse computation + + const Vec4V normalVel = V4Sub(_normalVel, __normalVel ); + + const Vec4V tmp1 = V4Sub(appliedForce, f.scaledBias); + + const Vec4V totalImpulse = V4NegMulSub(normalVel, f.velMultiplier, tmp1); + + broken = BOr(broken, V4IsGrtr(V4Abs(totalImpulse), maxFrictionImpulse)); + + const Vec4V newAppliedForce = V4Sel(broken, V4Min(maxDynFrictionImpulse, V4Max(negMaxDynFrictionImpulse, totalImpulse)), totalImpulse); + + const Vec4V deltaF =V4Sub(newAppliedForce, appliedForce); + + frictionAppliedForce[i] = newAppliedForce; + + const Vec4V deltaFIM0 = V4Mul(deltaF, invMassA); + const Vec4V deltaFIM1 = V4Mul(deltaF, invMassB); + + const Vec4V angDetaF0 = V4Mul(deltaF, angD0); + const Vec4V angDetaF1 = V4Mul(deltaF, angD1); + + linVel0T0 = V4MulAdd(normalT0, deltaFIM0, linVel0T0); + linVel1T0 = V4NegMulSub(normalT0, deltaFIM1, linVel1T0); + angState0T0 = V4MulAdd(f.raXnX, angDetaF0, angState0T0); + angState1T0 = V4NegMulSub(f.rbXnX, angDetaF1, angState1T0); + + linVel0T1 = V4MulAdd(normalT1, deltaFIM0, linVel0T1); + linVel1T1 = V4NegMulSub(normalT1, deltaFIM1, linVel1T1); + angState0T1 = V4MulAdd(f.raXnY, angDetaF0, angState0T1); + angState1T1 = V4NegMulSub(f.rbXnY, angDetaF1, angState1T1); + + linVel0T2 = V4MulAdd(normalT2, deltaFIM0, linVel0T2); + linVel1T2 = V4NegMulSub(normalT2, deltaFIM1, linVel1T2); + angState0T2 = V4MulAdd(f.raXnZ, angDetaF0, angState0T2); + angState1T2 = V4NegMulSub(f.rbXnZ, angDetaF1, angState1T2); + } + fd->broken = broken; + } + } + + PX_TRANSPOSE_44(linVel0T0, linVel0T1, linVel0T2, linVel0T3, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_44(linVel1T0, linVel1T1, linVel1T2, linVel1T3, linVel01, linVel11, linVel21, linVel31); + PX_TRANSPOSE_44(angState0T0, angState0T1, angState0T2, angState0T3, angState00, angState10, angState20, angState30); + PX_TRANSPOSE_44(angState1T0, angState1T1, angState1T2, angState1T3, angState01, angState11, angState21, angState31); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularState.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularState.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularState.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularState.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularState.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularState.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularState.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularState.isFinite()); + + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(angState00, &b00.angularState.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(angState10, &b10.angularState.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(angState20, &b20.angularState.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + V4StoreA(angState30, &b30.angularState.x); + + if(desc[0].bodyBDataIndex != 0) + { + V4StoreA(linVel01, &b01.linearVelocity.x); + V4StoreA(angState01, &b01.angularState.x); + } + if(desc[1].bodyBDataIndex != 0) + { + V4StoreA(linVel11, &b11.linearVelocity.x); + V4StoreA(angState11, &b11.angularState.x); + } + if(desc[2].bodyBDataIndex != 0) + { + V4StoreA(linVel21, &b21.linearVelocity.x); + V4StoreA(angState21, &b21.angularState.x); + } + if(desc[3].bodyBDataIndex != 0) + { + V4StoreA(linVel31, &b31.linearVelocity.x); + V4StoreA(angState31, &b31.angularState.x); + } + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularState.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularState.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularState.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularState.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularState.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularState.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularState.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularState.isFinite()); +} + +static void solveContact4_StaticBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& cache) +{ + PxSolverBody& b00 = *desc[0].bodyA; + PxSolverBody& b10 = *desc[1].bodyA; + PxSolverBody& b20 = *desc[2].bodyA; + PxSolverBody& b30 = *desc[3].bodyA; + + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + + //We'll need this. + const Vec4V vZero = V4Zero(); + Vec4V vMax = V4Splat(FMax()); + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularState.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularState.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularState.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularState.x); + + Vec4V linVel0T0, linVel0T1, linVel0T2, linVel0T3; + Vec4V angState0T0, angState0T1, angState0T2, angState0T3; + + + PX_TRANSPOSE_44(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2, linVel0T3); + PX_TRANSPOSE_44(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2, angState0T3); + + const PxU8* PX_RESTRICT prefetchAddress = currPtr + sizeof(SolverContactHeader4) + sizeof(SolverContactBatchPointBase4); + + const SolverContactHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + const Vec4V invMass0 = hdr->invMass0D0; + + while((currPtr < last)) + { + hdr = reinterpret_cast(currPtr); + + PX_ASSERT(hdr->type == DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT); + + currPtr = const_cast(reinterpret_cast(hdr + 1)); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + Vec4V* appliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numNormalConstr; + + SolverContactBatchPointBase4* PX_RESTRICT contacts = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(contacts + numNormalConstr); + + Vec4V* maxImpulses; + PxU32 maxImpulseMask; + if(hasMaxImpulse) + { + maxImpulseMask = 0xFFFFFFFF; + maxImpulses = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V) * numNormalConstr; + } + else + { + maxImpulseMask = 0; + maxImpulses = &vMax; + } + + SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(currPtr); + if(numFrictionConstr) + currPtr += sizeof(SolverFrictionSharedData4); + + Vec4V* frictionAppliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numFrictionConstr; + + const SolverContactFrictionBase4* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionBase4); + + + Vec4V accumulatedNormalImpulse = vZero; + + const Vec4V angD0 = hdr->angDom0; + const Vec4V _normalT0 = hdr->normalX; + const Vec4V _normalT1 = hdr->normalY; + const Vec4V _normalT2 = hdr->normalZ; + + Vec4V contactNormalVel1 = V4Mul(linVel0T0, _normalT0); + contactNormalVel1 = V4MulAdd(linVel0T1, _normalT1, contactNormalVel1); + + contactNormalVel1 = V4MulAdd(linVel0T2, _normalT2, contactNormalVel1); + + Vec4V accumDeltaF = vZero; + + + for(PxU32 i=0;istaticFriction; + + const Vec4V dynamicFric = hdr->dynamicFriction; + + const Vec4V maxFrictionImpulse = V4Mul(staticFric, accumulatedNormalImpulse); + const Vec4V maxDynFrictionImpulse = V4Mul(dynamicFric, accumulatedNormalImpulse); + const Vec4V negMaxDynFrictionImpulse = V4Neg(maxDynFrictionImpulse); + + BoolV broken = BFFFF(); + + if(cache.writeBackIteration) + { + Ps::prefetchLine(fd->frictionBrokenWritebackByte[0]); + Ps::prefetchLine(fd->frictionBrokenWritebackByte[1]); + Ps::prefetchLine(fd->frictionBrokenWritebackByte[2]); + Ps::prefetchLine(fd->frictionBrokenWritebackByte[3]); + } + + for(PxU32 i=0;inormalX[i&1]; + const Vec4V normalT1 = fd->normalY[i&1]; + const Vec4V normalT2 = fd->normalZ[i&1]; + + Vec4V normalVel1 = V4Mul(linVel0T0, normalT0); + Vec4V normalVel2 = V4Mul(f.raXnX, angState0T0); + + normalVel1 = V4MulAdd(linVel0T1, normalT1, normalVel1); + normalVel2 = V4MulAdd(f.raXnY, angState0T1, normalVel2); + + normalVel1 = V4MulAdd(linVel0T2, normalT2, normalVel1); + normalVel2 = V4MulAdd(f.raXnZ, angState0T2, normalVel2); + + //relative normal velocity for all 4 constraints + const Vec4V normalVel = V4Add(normalVel1, normalVel2); + + // appliedForce -bias * velMultiplier - a hoisted part of the total impulse computation + const Vec4V tmp1 = V4Sub(appliedForce, f.scaledBias); + + const Vec4V totalImpulse = V4NegMulSub(normalVel, f.velMultiplier, tmp1); + + broken = BOr(broken, V4IsGrtr(V4Abs(totalImpulse), maxFrictionImpulse)); + + const Vec4V newAppliedForce = V4Sel(broken, V4Min(maxDynFrictionImpulse, V4Max(negMaxDynFrictionImpulse, totalImpulse)), totalImpulse); + + const Vec4V deltaF =V4Sub(newAppliedForce, appliedForce); + + const Vec4V deltaFInvMass = V4Mul(invMass0, deltaF); + const Vec4V angDeltaF = V4Mul(angD0, deltaF); + + linVel0T0 = V4MulAdd(normalT0, deltaFInvMass, linVel0T0); + angState0T0 = V4MulAdd(f.raXnX, angDeltaF, angState0T0); + + linVel0T1 = V4MulAdd(normalT1, deltaFInvMass, linVel0T1); + angState0T1 = V4MulAdd(f.raXnY, angDeltaF, angState0T1); + + linVel0T2 = V4MulAdd(normalT2, deltaFInvMass, linVel0T2); + angState0T2 = V4MulAdd(f.raXnZ, angDeltaF, angState0T2); + +#if 1 + frictionAppliedForces[i] = newAppliedForce; +#endif + + } + + fd->broken = broken; + } + } + + PX_TRANSPOSE_44(linVel0T0, linVel0T1, linVel0T2, linVel0T3, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_44(angState0T0, angState0T1, angState0T2, angState0T3, angState00, angState10, angState20, angState30); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularState.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularState.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularState.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularState.isFinite()); + + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + + V4StoreA(angState00, &b00.angularState.x); + V4StoreA(angState10, &b10.angularState.x); + V4StoreA(angState20, &b20.angularState.x); + V4StoreA(angState30, &b30.angularState.x); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularState.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularState.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularState.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularState.isFinite()); +} + +static void concludeContact4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& /*cache*/, PxU32 contactSize, PxU32 frictionSize) +{ + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + while((currPtr < last)) + { + const SolverContactHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + currPtr = const_cast(reinterpret_cast(hdr + 1)); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + currPtr += sizeof(Vec4V)*numNormalConstr; + + SolverContactBatchPointBase4* PX_RESTRICT contacts = reinterpret_cast(currPtr); + currPtr += (numNormalConstr * contactSize); + bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + if(hasMaxImpulse) + currPtr += sizeof(Vec4V) * numNormalConstr; + + currPtr += sizeof(Vec4V)*numFrictionConstr; + + SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(currPtr); + if(numFrictionConstr) + currPtr += sizeof(SolverFrictionSharedData4); + PX_UNUSED(fd); + + SolverContactFrictionBase4* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += (numFrictionConstr * frictionSize); + + for(PxU32 i=0;i((reinterpret_cast(contacts)) + contactSize); + c.biasedErr = V4Sub(c.biasedErr, c.scaledBias); + } + + for(PxU32 i=0;i((reinterpret_cast(frictions)) + frictionSize); + f.scaledBias = f.targetVelocity; + } + } +} + +void writeBackContact4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& cache, + const PxSolverBodyData** PX_RESTRICT bd0, const PxSolverBodyData** PX_RESTRICT bd1) +{ + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + PxReal* PX_RESTRICT vForceWriteback0 = reinterpret_cast(desc[0].writeBack); + PxReal* PX_RESTRICT vForceWriteback1 = reinterpret_cast(desc[1].writeBack); + PxReal* PX_RESTRICT vForceWriteback2 = reinterpret_cast(desc[2].writeBack); + PxReal* PX_RESTRICT vForceWriteback3 = reinterpret_cast(desc[3].writeBack); + + const PxU8 type = *desc[0].constraint; + const PxU32 contactSize = type == DY_SC_TYPE_BLOCK_RB_CONTACT ? sizeof(SolverContactBatchPointDynamic4) : sizeof(SolverContactBatchPointBase4); + const PxU32 frictionSize = type == DY_SC_TYPE_BLOCK_RB_CONTACT ? sizeof(SolverContactFrictionDynamic4) : sizeof(SolverContactFrictionBase4); + + + Vec4V normalForce = V4Zero(); + + + //We'll need this. + //const Vec4V vZero = V4Zero(); + + bool writeBackThresholds[4] = {false, false, false, false}; + + while((currPtr < last)) + { + SolverContactHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(hdr + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + Vec4V* PX_RESTRICT appliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numNormalConstr; + + //SolverContactBatchPointBase4* PX_RESTRICT contacts = (SolverContactBatchPointBase4*)currPtr; + currPtr += (numNormalConstr * contactSize); + + bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + if(hasMaxImpulse) + currPtr += sizeof(Vec4V) * numNormalConstr; + + SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(currPtr); + if(numFrictionConstr) + currPtr += sizeof(SolverFrictionSharedData4); + + currPtr += sizeof(Vec4V)*numFrictionConstr; + + //SolverContactFrictionBase4* PX_RESTRICT frictions = (SolverContactFrictionBase4*)currPtr; + currPtr += (numFrictionConstr * frictionSize); + + writeBackThresholds[0] = hdr->flags[0] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[1] = hdr->flags[1] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[2] = hdr->flags[2] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[3] = hdr->flags[3] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + + + for(PxU32 i=0;inumNormalConstr0) + FStore(appliedForce0, vForceWriteback0++); + if(vForceWriteback1 && i < hdr->numNormalConstr1) + FStore(appliedForce1, vForceWriteback1++); + if(vForceWriteback2 && i < hdr->numNormalConstr2) + FStore(appliedForce2, vForceWriteback2++); + if(vForceWriteback3 && i < hdr->numNormalConstr3) + FStore(appliedForce3, vForceWriteback3++); + } + + if(numFrictionConstr) + { + PX_ALIGN(16, PxU32 broken[4]); + BStoreA(fd->broken, broken); + + PxU8* frictionCounts = &hdr->numFrictionConstr0; + + for(PxU32 a = 0; a < 4; ++a) + { + if(frictionCounts[a] && broken[a]) + *fd->frictionBrokenWritebackByte[a] = 1; // PT: bad L2 miss here + } + } + } + + PX_ALIGN(16, PxReal nf[4]); + V4StoreA(normalForce, nf); + + Sc::ShapeInteraction** shapeInteractions = reinterpret_cast(desc[0].constraint)->shapeInteraction; + + for(PxU32 a = 0; a < 4; ++a) + { + if(writeBackThresholds[a] && desc[a].linkIndexA == PxSolverConstraintDesc::NO_LINK && desc[a].linkIndexB == PxSolverConstraintDesc::NO_LINK && + nf[a] !=0.f && (bd0[a]->reportThreshold < PX_MAX_REAL || bd1[a]->reportThreshold < PX_MAX_REAL)) + { + ThresholdStreamElement elt; + elt.normalForce = nf[a]; + elt.threshold = PxMin(bd0[a]->reportThreshold, bd1[a]->reportThreshold); + elt.nodeIndexA = IG::NodeIndex(bd0[a]->nodeIndex); + elt.nodeIndexB = IG::NodeIndex(bd1[a]->nodeIndex); + elt.shapeInteraction = shapeInteractions[a]; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + PX_ASSERT(cache.mThresholdStreamIndex(bPtr); + SolverConstraint1DDynamic4* PX_RESTRICT base = reinterpret_cast(header+1); + + //const FloatV fZero = FZero(); + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&b01.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularState.x); + Vec4V angState01 = V4LoadA(&b01.angularState.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&b11.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularState.x); + Vec4V angState11 = V4LoadA(&b11.angularState.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&b21.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularState.x); + Vec4V angState21 = V4LoadA(&b21.angularState.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&b31.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularState.x); + Vec4V angState31 = V4LoadA(&b31.angularState.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2, linVel0T3; + Vec4V linVel1T0, linVel1T1, linVel1T2, linVel1T3; + Vec4V angState0T0, angState0T1, angState0T2, angState0T3; + Vec4V angState1T0, angState1T1, angState1T2, angState1T3; + + + PX_TRANSPOSE_44(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2, linVel0T3); + PX_TRANSPOSE_44(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2, linVel1T3); + PX_TRANSPOSE_44(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2, angState0T3); + PX_TRANSPOSE_44(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2, angState1T3); + + const Vec4V invMass0D0 = header->invMass0D0; + const Vec4V invMass1D1 = header->invMass1D1; + + const Vec4V angD0 = header->angD0; + const Vec4V angD1 = header->angD1; + + PxU32 maxConstraints = header->count; + + for(PxU32 a = 0; a < maxConstraints; ++a) + { + SolverConstraint1DDynamic4& c = *base; + base++; + + Ps::prefetchLine(base); + Ps::prefetchLine(base, 64); + Ps::prefetchLine(base, 128); + Ps::prefetchLine(base, 192); + Ps::prefetchLine(base, 256); + + const Vec4V appliedForce = c.appliedForce; + + Vec4V linProj0(V4Mul(c.lin0X, linVel0T0)); + Vec4V linProj1(V4Mul(c.lin1X, linVel1T0)); + Vec4V angProj0(V4Mul(c.ang0X, angState0T0)); + Vec4V angProj1(V4Mul(c.ang1X, angState1T0)); + + linProj0 = V4MulAdd(c.lin0Y, linVel0T1, linProj0); + linProj1 = V4MulAdd(c.lin1Y, linVel1T1, linProj1); + angProj0 = V4MulAdd(c.ang0Y, angState0T1, angProj0); + angProj1 = V4MulAdd(c.ang1Y, angState1T1, angProj1); + + linProj0 = V4MulAdd(c.lin0Z, linVel0T2, linProj0); + linProj1 = V4MulAdd(c.lin1Z, linVel1T2, linProj1); + angProj0 = V4MulAdd(c.ang0Z, angState0T2, angProj0); + angProj1 = V4MulAdd(c.ang1Z, angState1T2, angProj1); + + const Vec4V projectVel0 = V4Add(linProj0, angProj0); + const Vec4V projectVel1 = V4Add(linProj1, angProj1); + + const Vec4V normalVel = V4Sub(projectVel0, projectVel1); + + const Vec4V unclampedForce = V4MulAdd(appliedForce, c.impulseMultiplier, V4MulAdd(normalVel, c.velMultiplier, c.constant)); + const Vec4V clampedForce = V4Max(c.minImpulse, V4Min(c.maxImpulse, unclampedForce)); + const Vec4V deltaF = V4Sub(clampedForce, appliedForce); + c.appliedForce = clampedForce; + + const Vec4V deltaFInvMass0 = V4Mul(deltaF, invMass0D0); + const Vec4V deltaFInvMass1 = V4Mul(deltaF, invMass1D1); + + const Vec4V angDeltaFInvMass0 = V4Mul(deltaF, angD0); + const Vec4V angDeltaFInvMass1 = V4Mul(deltaF, angD1); + + linVel0T0 = V4MulAdd(c.lin0X, deltaFInvMass0, linVel0T0); + linVel1T0 = V4NegMulSub(c.lin1X, deltaFInvMass1, linVel1T0); + angState0T0 = V4MulAdd(c.ang0X, angDeltaFInvMass0, angState0T0); + angState1T0 = V4NegMulSub(c.ang1X, angDeltaFInvMass1, angState1T0); + + linVel0T1 = V4MulAdd(c.lin0Y, deltaFInvMass0, linVel0T1); + linVel1T1 = V4NegMulSub(c.lin1Y, deltaFInvMass1, linVel1T1); + angState0T1 = V4MulAdd(c.ang0Y, angDeltaFInvMass0, angState0T1); + angState1T1 = V4NegMulSub(c.ang1Y, angDeltaFInvMass1, angState1T1); + + linVel0T2 = V4MulAdd(c.lin0Z, deltaFInvMass0, linVel0T2); + linVel1T2 = V4NegMulSub(c.lin1Z, deltaFInvMass1, linVel1T2); + angState0T2 = V4MulAdd(c.ang0Z, angDeltaFInvMass0, angState0T2); + angState1T2 = V4NegMulSub(c.ang1Z, angDeltaFInvMass1, angState1T2); + } + + PX_TRANSPOSE_44(linVel0T0, linVel0T1, linVel0T2, linVel0T3, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_44(linVel1T0, linVel1T1, linVel1T2, linVel1T3, linVel01, linVel11, linVel21, linVel31); + PX_TRANSPOSE_44(angState0T0, angState0T1, angState0T2, angState0T3, angState00, angState10, angState20, angState30); + PX_TRANSPOSE_44(angState1T0, angState1T1, angState1T2, angState1T3, angState01, angState11, angState21, angState31); + + + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + + V4StoreA(linVel01, &b01.linearVelocity.x); + V4StoreA(linVel11, &b11.linearVelocity.x); + V4StoreA(linVel21, &b21.linearVelocity.x); + V4StoreA(linVel31, &b31.linearVelocity.x); + + V4StoreA(angState00, &b00.angularState.x); + V4StoreA(angState10, &b10.angularState.x); + V4StoreA(angState20, &b20.angularState.x); + V4StoreA(angState30, &b30.angularState.x); + + V4StoreA(angState01, &b01.angularState.x); + V4StoreA(angState11, &b11.angularState.x); + V4StoreA(angState21, &b21.angularState.x); + V4StoreA(angState31, &b31.angularState.x); + +} + +static void conclude1D4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& /*cache*/) +{ + SolverConstraint1DHeader4* header = reinterpret_cast(desc[0].constraint); + PxU8* base = desc[0].constraint + sizeof(SolverConstraint1DHeader4); + PxU32 stride = header->type == DY_SC_TYPE_BLOCK_1D ? sizeof(SolverConstraint1DDynamic4) : sizeof(SolverConstraint1DBase4); + + for(PxU32 i=0; icount; i++) + { + SolverConstraint1DBase4& c = *reinterpret_cast(base); + c.constant = c.unbiasedConstant; + base += stride; + } + PX_ASSERT(desc[0].constraint + getConstraintLength(desc[0]) == base); +} + +void writeBack1D4(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& /*cache*/, + const PxSolverBodyData** PX_RESTRICT /*bd0*/, const PxSolverBodyData** PX_RESTRICT /*bd1*/) +{ + ConstraintWriteback* writeback0 = reinterpret_cast(desc[0].writeBack); + ConstraintWriteback* writeback1 = reinterpret_cast(desc[1].writeBack); + ConstraintWriteback* writeback2 = reinterpret_cast(desc[2].writeBack); + ConstraintWriteback* writeback3 = reinterpret_cast(desc[3].writeBack); + + if(writeback0 || writeback1 || writeback2 || writeback3) + { + SolverConstraint1DHeader4* header = reinterpret_cast(desc[0].constraint); + PxU8* base = desc[0].constraint + sizeof(SolverConstraint1DHeader4); + PxU32 stride = header->type == DY_SC_TYPE_BLOCK_1D ? sizeof(SolverConstraint1DDynamic4) : sizeof(SolverConstraint1DBase4); + + const Vec4V zero = V4Zero(); + Vec4V linX(zero), linY(zero), linZ(zero); + Vec4V angX(zero), angY(zero), angZ(zero); + + for(PxU32 i=0; icount; i++) + { + const SolverConstraint1DBase4* c = reinterpret_cast(base); + + //Load in flags + const VecI32V flags = I4LoadU(reinterpret_cast(&c->flags[0])); + //Work out masks + const VecI32V mask = I4Load(DY_SC_FLAG_OUTPUT_FORCE); + + const VecI32V masked = VecI32V_And(flags, mask); + const BoolV isEq = VecI32V_IsEq(masked, mask); + + const Vec4V appliedForce = V4Sel(isEq, c->appliedForce, zero); + + linX = V4MulAdd(c->lin0X, appliedForce, linX); + linY = V4MulAdd(c->lin0Y, appliedForce, linY); + linZ = V4MulAdd(c->lin0Z, appliedForce, linZ); + + angX = V4MulAdd(c->ang0WritebackX, appliedForce, angX); + angY = V4MulAdd(c->ang0WritebackY, appliedForce, angY); + angZ = V4MulAdd(c->ang0WritebackZ, appliedForce, angZ); + + base += stride; + } + + //We need to do the cross product now + + angX = V4Sub(angX, V4NegMulSub(header->body0WorkOffsetZ, linY, V4Mul(header->body0WorkOffsetY, linZ))); + angY = V4Sub(angY, V4NegMulSub(header->body0WorkOffsetX, linZ, V4Mul(header->body0WorkOffsetZ, linX))); + angZ = V4Sub(angZ, V4NegMulSub(header->body0WorkOffsetY, linX, V4Mul(header->body0WorkOffsetX, linY))); + + const Vec4V linLenSq = V4MulAdd(linZ, linZ, V4MulAdd(linY, linY, V4Mul(linX, linX))); + const Vec4V angLenSq = V4MulAdd(angZ, angZ, V4MulAdd(angY, angY, V4Mul(angX, angX))); + + const Vec4V linLen = V4Sqrt(linLenSq); + const Vec4V angLen = V4Sqrt(angLenSq); + + const BoolV broken = BOr(V4IsGrtr(linLen, header->linBreakImpulse), V4IsGrtr(angLen, header->angBreakImpulse)); + + PX_ALIGN(16, PxU32 iBroken[4]); + BStoreA(broken, iBroken); + + Vec4V lin0, lin1, lin2, lin3; + Vec4V ang0, ang1, ang2, ang3; + + PX_TRANSPOSE_34_44(linX, linY, linZ, lin0, lin1, lin2, lin3); + PX_TRANSPOSE_34_44(angX, angY, angZ, ang0, ang1, ang2, ang3); + + if(writeback0) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin0), writeback0->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang0), writeback0->angularImpulse); + writeback0->broken = header->break0 ? PxU32(iBroken[0] != 0) : 0; + } + if(writeback1) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin1), writeback1->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang1), writeback1->angularImpulse); + writeback1->broken = header->break1 ? PxU32(iBroken[1] != 0) : 0; + } + if(writeback2) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin2), writeback2->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang2), writeback2->angularImpulse); + writeback2->broken = header->break2 ? PxU32(iBroken[2] != 0) : 0; + } + if(writeback3) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin3), writeback3->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang3), writeback3->angularImpulse); + writeback3->broken = header->break3 ? PxU32(iBroken[3] != 0) : 0; + } + + PX_ASSERT(desc[0].constraint + getConstraintLength(desc[0]) == base); + } +} + + +void solveContactPreBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_Block(desc, cache); +} + +void solveContactPreBlock_Static(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_StaticBlock(desc, cache); +} + +void solveContactPreBlock_Conclude(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_Block(desc, cache); + concludeContact4_Block(desc, cache, sizeof(SolverContactBatchPointDynamic4), sizeof(SolverContactFrictionDynamic4)); +} + +void solveContactPreBlock_ConcludeStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_StaticBlock(desc, cache); + concludeContact4_Block(desc, cache, sizeof(SolverContactBatchPointBase4), sizeof(SolverContactFrictionBase4)); +} + +void solveContactPreBlock_WriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_Block(desc, cache); + + const PxSolverBodyData* bd0[4] = { &cache.solverBodyArray[desc[0].bodyADataIndex], + &cache.solverBodyArray[desc[1].bodyADataIndex], + &cache.solverBodyArray[desc[2].bodyADataIndex], + &cache.solverBodyArray[desc[3].bodyADataIndex]}; + + const PxSolverBodyData* bd1[4] = { &cache.solverBodyArray[desc[0].bodyBDataIndex], + &cache.solverBodyArray[desc[1].bodyBDataIndex], + &cache.solverBodyArray[desc[2].bodyBDataIndex], + &cache.solverBodyArray[desc[3].bodyBDataIndex]}; + + writeBackContact4_Block(desc, cache, bd0, bd1); + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveContactPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContact4_StaticBlock(desc, cache); + const PxSolverBodyData* bd0[4] = { &cache.solverBodyArray[desc[0].bodyADataIndex], + &cache.solverBodyArray[desc[1].bodyADataIndex], + &cache.solverBodyArray[desc[2].bodyADataIndex], + &cache.solverBodyArray[desc[3].bodyADataIndex]}; + + const PxSolverBodyData* bd1[4] = { &cache.solverBodyArray[desc[0].bodyBDataIndex], + &cache.solverBodyArray[desc[1].bodyBDataIndex], + &cache.solverBodyArray[desc[2].bodyBDataIndex], + &cache.solverBodyArray[desc[3].bodyBDataIndex]}; + + writeBackContact4_Block(desc, cache, bd0, bd1); + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solve1D4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solve1D4_Block(desc, cache); +} + + +void solve1D4Block_Conclude(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solve1D4_Block(desc, cache); + conclude1D4_Block(desc, cache); +} + + +void solve1D4Block_WriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solve1D4_Block(desc, cache); + + const PxSolverBodyData* bd0[4] = { &cache.solverBodyArray[desc[0].bodyADataIndex], + &cache.solverBodyArray[desc[1].bodyADataIndex], + &cache.solverBodyArray[desc[2].bodyADataIndex], + &cache.solverBodyArray[desc[3].bodyADataIndex]}; + + const PxSolverBodyData* bd1[4] = { &cache.solverBodyArray[desc[0].bodyBDataIndex], + &cache.solverBodyArray[desc[1].bodyBDataIndex], + &cache.solverBodyArray[desc[2].bodyBDataIndex], + &cache.solverBodyArray[desc[3].bodyBDataIndex]}; + + writeBack1D4(desc, cache, bd0, bd1); +} + +void writeBack1D4Block(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + const PxSolverBodyData* bd0[4] = { &cache.solverBodyArray[desc[0].bodyADataIndex], + &cache.solverBodyArray[desc[1].bodyADataIndex], + &cache.solverBodyArray[desc[2].bodyADataIndex], + &cache.solverBodyArray[desc[3].bodyADataIndex]}; + + const PxSolverBodyData* bd1[4] = { &cache.solverBodyArray[desc[0].bodyBDataIndex], + &cache.solverBodyArray[desc[1].bodyBDataIndex], + &cache.solverBodyArray[desc[2].bodyBDataIndex], + &cache.solverBodyArray[desc[3].bodyBDataIndex]}; + + writeBack1D4(desc, cache, bd0, bd1); +} + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h new file mode 100644 index 000000000..ed3d9e838 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h @@ -0,0 +1,171 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef DY_SOLVER_CORE_SHARED_H +#define DY_SOLVER_CORE_SHARED_H + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" + +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverContact.h" +#include "DySolverConstraint1D.h" +#include "DySolverConstraintDesc.h" +#include "PsUtilities.h" +#include "DyConstraint.h" +#include "PsAtomic.h" + + +namespace physx +{ + +namespace Dy +{ + PX_FORCE_INLINE static FloatV solveDynamicContacts(SolverContactPoint* contacts, const PxU32 nbContactPoints, const Vec3VArg contactNormal, + const FloatVArg invMassA, const FloatVArg invMassB, const FloatVArg angDom0, const FloatVArg angDom1, Vec3V& linVel0_, Vec3V& angState0_, + Vec3V& linVel1_, Vec3V& angState1_, PxF32* PX_RESTRICT forceBuffer) +{ + Vec3V linVel0 = linVel0_; + Vec3V angState0 = angState0_; + Vec3V linVel1 = linVel1_; + Vec3V angState1 = angState1_; + FloatV accumulatedNormalImpulse = FZero(); + + const Vec3V delLinVel0 = V3Scale(contactNormal, invMassA); + const Vec3V delLinVel1 = V3Scale(contactNormal, invMassB); + + for(PxU32 i=0;i +#include +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverConstraint1D.h" +#include "DySolverContact.h" +#include "DyThresholdTable.h" +#include "DySolverControl.h" +#include "DyArticulationHelper.h" +#include "PsAtomic.h" +#include "PsIntrinsics.h" +#include "DyArticulationPImpl.h" +#include "PsThread.h" +#include "DySolverConstraintDesc.h" +#include "DySolverContext.h" + + +namespace physx +{ + +namespace Dy +{ + +//----------------------------------- + +void solve1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContact_BStaticBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock_Static (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4_Block (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + + +void solve1DConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContact_BStaticConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock_Conclude (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock_ConcludeStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4Block_Conclude (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solve1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContact_BStaticBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4Block_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void writeBack1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void contactBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void extContactBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void ext1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void contactPreBlock_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void writeBack1D4Block (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +// could move this to PxPreprocessor.h but +// no implementation available for MSVC +#if PX_GCC_FAMILY +#define PX_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define PX_UNUSED_ATTRIBUTE +#endif + +#define DYNAMIC_ARTICULATION_REGISTRATION(x) 0 + +static SolveBlockMethod gVTableSolveBlock[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactBlock, // DY_SC_TYPE_RB_CONTACT + solve1DBlock, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactBlock), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DBlock), // DY_SC_TYPE_EXT_1D + solveContact_BStaticBlock, // DY_SC_TYPE_STATIC_CONTACT + solveContactBlock, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactPreBlock, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactPreBlock_Static, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4_Block, // DY_SC_TYPE_BLOCK_1D, +}; + +static SolveWriteBackBlockMethod gVTableSolveWriteBackBlock[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactBlockWriteBack, // DY_SC_TYPE_RB_CONTACT + solve1DBlockWriteBack, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactBlockWriteBack), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DBlockWriteBack), // DY_SC_TYPE_EXT_1D + solveContact_BStaticBlockWriteBack, // DY_SC_TYPE_STATIC_CONTACT + solveContactBlockWriteBack, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactPreBlock_WriteBack, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactPreBlock_WriteBackStatic, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4Block_WriteBack, // DY_SC_TYPE_BLOCK_1D, +}; + +static SolveBlockMethod gVTableSolveConcludeBlock[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactConcludeBlock, // DY_SC_TYPE_RB_CONTACT + solve1DConcludeBlock, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactConcludeBlock), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DConcludeBlock), // DY_SC_TYPE_EXT_1D + solveContact_BStaticConcludeBlock, // DY_SC_TYPE_STATIC_CONTACT + solveContactConcludeBlock, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactPreBlock_Conclude, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactPreBlock_ConcludeStatic, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4Block_Conclude, // DY_SC_TYPE_BLOCK_1D, +}; + +void SolverCoreRegisterArticulationFns() +{ + gVTableSolveBlock[DY_SC_TYPE_EXT_CONTACT] = solveExtContactBlock; + gVTableSolveBlock[DY_SC_TYPE_EXT_1D] = solveExt1DBlock; + + gVTableSolveWriteBackBlock[DY_SC_TYPE_EXT_CONTACT] = solveExtContactBlockWriteBack; + gVTableSolveWriteBackBlock[DY_SC_TYPE_EXT_1D] = solveExt1DBlockWriteBack; + gVTableSolveConcludeBlock[DY_SC_TYPE_EXT_CONTACT] = solveExtContactConcludeBlock; + gVTableSolveConcludeBlock[DY_SC_TYPE_EXT_1D] = solveExt1DConcludeBlock; +} + + +SolveBlockMethod* getSolveBlockTable() +{ + return gVTableSolveBlock; +} + +SolveBlockMethod* getSolverConcludeBlockTable() +{ + return gVTableSolveConcludeBlock; +} + +SolveWriteBackBlockMethod* getSolveWritebackBlockTable() +{ + return gVTableSolveWriteBackBlock; +} + + + + +SolverCoreGeneral* SolverCoreGeneral::create(bool fricEveryIteration) +{ + SolverCoreGeneral* scg = reinterpret_cast( + PX_ALLOC(sizeof(SolverCoreGeneral), "SolverCoreGeneral")); + + if (scg) + { + new (scg) SolverCoreGeneral; + scg->frictionEveryIteration = fricEveryIteration; + } + + return scg; +} + +void SolverCoreGeneral::destroyV() +{ + this->~SolverCoreGeneral(); + PX_FREE(this); +} + +void SolverCoreGeneral::solveV_Blocks(SolverIslandParams& params) const +{ + + const PxI32 TempThresholdStreamSize = 32; + ThresholdStreamElement tempThresholdStream[TempThresholdStreamSize]; + + SolverContext cache; + cache.solverBodyArray = params.bodyDataList; + cache.mThresholdStream = tempThresholdStream; + cache.mThresholdStreamLength = TempThresholdStreamSize; + cache.mThresholdStreamIndex = 0; + cache.writeBackIteration = false; + cache.Z = params.Z; + cache.deltaV = params.deltaV; + + PxI32 batchCount = PxI32(params.numConstraintHeaders); + + PxSolverBody* PX_RESTRICT bodyListStart = params.bodyListStart; + const PxU32 bodyListSize = params.bodyListSize; + + Cm::SpatialVector* PX_RESTRICT motionVelocityArray = params.motionVelocityArray; + + const PxU32 velocityIterations = params.velocityIterations; + const PxU32 positionIterations = params.positionIterations; + + const PxU32 numConstraintHeaders = params.numConstraintHeaders; + const PxU32 articulationListSize = params.articulationListSize; + + ArticulationSolverDesc* PX_RESTRICT articulationListStart = params.articulationListStart; + + PX_ASSERT(velocityIterations >= 1); + PX_ASSERT(positionIterations >= 1); + + if(numConstraintHeaders == 0) + { + for (PxU32 baIdx = 0; baIdx < bodyListSize; baIdx++) + { + Cm::SpatialVector& motionVel = motionVelocityArray[baIdx]; + PxSolverBody& atom = bodyListStart[baIdx]; + + motionVel.linear = atom.linearVelocity; + motionVel.angular = atom.angularState; + } + + //Even thought there are no external constraints, there may still be internal constraints in the articulations... + for(PxU32 i = 0; i < positionIterations; ++i) + for (PxU32 j = 0; j < articulationListSize; ++j) + articulationListStart[j].articulation->solveInternalConstraints(params.dt, params.invDt, cache.Z, cache.deltaV, false); + + for (PxU32 i = 0; i < articulationListSize; i++) + ArticulationPImpl::saveVelocity(articulationListStart[i], cache.deltaV); + + for (PxU32 i = 0; i < velocityIterations; ++i) + for (PxU32 j = 0; j < articulationListSize; ++j) + articulationListStart[j].articulation->solveInternalConstraints(params.dt, params.invDt, cache.Z, cache.deltaV, true); + + return; + } + + BatchIterator contactIterator(params.constraintBatchHeaders, params.numConstraintHeaders); + + PxSolverConstraintDesc* PX_RESTRICT constraintList = params.constraintList; + + //0-(n-1) iterations + PxI32 normalIter = 0; + + for (PxU32 iteration = positionIterations; iteration > 0; iteration--) //decreasing positive numbers == position iters + { + cache.doFriction = this->frictionEveryIteration ? true : iteration <= 3; + + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, iteration == 1 ? gVTableSolveConcludeBlock : gVTableSolveBlock, normalIter); + + for (PxU32 i = 0; i < articulationListSize; ++i) + articulationListStart[i].articulation->solveInternalConstraints(params.dt, params.invDt, cache.Z, cache.deltaV, false); + + ++normalIter; + } + + for (PxU32 baIdx = 0; baIdx < bodyListSize; baIdx++) + { + const PxSolverBody& atom = bodyListStart[baIdx]; + Cm::SpatialVector& motionVel = motionVelocityArray[baIdx]; + motionVel.linear = atom.linearVelocity; + motionVel.angular = atom.angularState; + } + + + for (PxU32 i = 0; i < articulationListSize; i++) + ArticulationPImpl::saveVelocity(articulationListStart[i], cache.deltaV); + + + const PxI32 velItersMinOne = (PxI32(velocityIterations)) - 1; + + PxI32 iteration = 0; + + for(; iteration < velItersMinOne; ++iteration) + { + + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, gVTableSolveBlock, normalIter); + + for (PxU32 i = 0; i < articulationListSize; ++i) + articulationListStart[i].articulation->solveInternalConstraints(params.dt, params.invDt, cache.Z, cache.deltaV, true); + ++normalIter; + + } + + PxI32* outThresholdPairs = params.outThresholdPairs; + ThresholdStreamElement* PX_RESTRICT thresholdStream = params.thresholdStream; + PxU32 thresholdStreamLength = params.thresholdStreamLength; + + cache.writeBackIteration = true; + cache.mSharedThresholdStream = thresholdStream; + cache.mSharedThresholdStreamLength = thresholdStreamLength; + cache.mSharedOutThresholdPairs = outThresholdPairs; + for(; iteration < PxI32(velocityIterations); ++iteration) + { + + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, gVTableSolveWriteBackBlock, normalIter); + + for (PxU32 i = 0; i < articulationListSize; ++i) + articulationListStart[i].articulation->solveInternalConstraints(params.dt, params.invDt, cache.Z, cache.deltaV, true); + + ++normalIter; + + } + + //Write back remaining threshold streams + if(cache.mThresholdStreamIndex > 0) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(outThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 b = 0; b < cache.mThresholdStreamIndex; ++b) + { + thresholdStream[b + threshIndex] = cache.mThresholdStream[b]; + } + cache.mThresholdStreamIndex = 0; + } +} + +PxI32 SolverCoreGeneral::solveVParallelAndWriteBack +(SolverIslandParams& params, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) const +{ +#if PX_PROFILE_SOLVE_STALLS + PxU64 startTime = readTimer(); + + PxU64 stallCount = 0; +#endif + + SolverContext cache; + cache.solverBodyArray = params.bodyDataList; + const PxU32 batchSize = params.batchSize; + + const PxI32 UnrollCount = PxI32(batchSize); + const PxI32 ArticCount = 2; + const PxI32 SaveUnrollCount = 32; + + const PxI32 TempThresholdStreamSize = 32; + ThresholdStreamElement tempThresholdStream[TempThresholdStreamSize]; + + const PxI32 bodyListSize = PxI32(params.bodyListSize); + const PxI32 articulationListSize = PxI32(params.articulationListSize); + + + const PxI32 batchCount = PxI32(params.numConstraintHeaders); + cache.mThresholdStream = tempThresholdStream; + cache.mThresholdStreamLength = TempThresholdStreamSize; + cache.mThresholdStreamIndex = 0; + cache.writeBackIteration = false; + cache.Z = Z; + cache.deltaV = deltaV; + + const PxReal dt = params.dt; + const PxReal invDt = params.invDt; + + const PxI32 positionIterations = PxI32(params.positionIterations); + const PxI32 velocityIterations = PxI32(params.velocityIterations); + + PxI32* constraintIndex = ¶ms.constraintIndex; + PxI32* constraintIndex2 = ¶ms.constraintIndex2; + + PxI32* articIndex = ¶ms.articSolveIndex; + PxI32* articIndex2 = ¶ms.articSolveIndex2; + + PxSolverConstraintDesc* PX_RESTRICT constraintList = params.constraintList; + + ArticulationSolverDesc* PX_RESTRICT articulationListStart = params.articulationListStart; + + const PxU32 nbPartitions = params.nbPartitions; + + PxU32* headersPerPartition = params.headersPerPartition; + + PX_UNUSED(velocityIterations); + + PX_ASSERT(velocityIterations >= 1); + PX_ASSERT(positionIterations >= 1); + + PxI32 endIndexCount = UnrollCount; + PxI32 index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + + PxI32 articSolveStart = 0; + PxI32 articSolveEnd = 0; + PxI32 maxArticIndex = 0; + PxI32 articIndexCounter = 0; + + BatchIterator contactIter(params.constraintBatchHeaders, params.numConstraintHeaders); + + PxI32 maxNormalIndex = 0; + PxI32 normalIteration = 0; + PxU32 a = 0; + PxI32 targetConstraintIndex = 0; + PxI32 targetArticIndex = 0; + + for(PxU32 i = 0; i < 2; ++i) + { + SolveBlockMethod* solveTable = i == 0 ? gVTableSolveBlock : gVTableSolveConcludeBlock; + for(; a < positionIterations - 1 + i; ++a) + { + WAIT_FOR_PROGRESS(articIndex2, targetArticIndex); + + cache.doFriction = this->frictionEveryIteration ? true : (positionIterations - a) <= 3; + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxNormalIndex += headersPerPartition[b]; + + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, solveTable, + normalIteration); + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(constraintIndex2, nbSolved); + } + targetConstraintIndex += headersPerPartition[b]; //Increment target constraint index by batch count + } + + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxArticIndex += articulationListSize; + targetArticIndex += articulationListSize; + + while (articSolveStart < maxArticIndex) + { + + PxI32 endIdx = PxMin(articSolveEnd, maxArticIndex); + + PxI32 nbSolved = 0; + while (articSolveStart < endIdx) + { + articulationListStart[articSolveStart - articIndexCounter].articulation->solveInternalConstraints(dt, invDt, cache.Z, cache.deltaV, false); + articSolveStart++; + nbSolved++; + } + + if (nbSolved) + { + physx::shdfnd::atomicAdd(articIndex2, nbSolved); + } + + PxI32 remaining = articSolveEnd - articSolveStart; + + if (remaining == 0) + { + articSolveStart = physx::shdfnd::atomicAdd(articIndex, ArticCount) - ArticCount; + articSolveEnd = articSolveStart + ArticCount; + } + + } + + articIndexCounter += articulationListSize; + + ++normalIteration; + } + } + + PxI32* bodyListIndex = ¶ms.bodyListIndex; + PxI32* bodyListIndex2 = ¶ms.bodyListIndex2; + + PxSolverBody* PX_RESTRICT bodyListStart = params.bodyListStart; + Cm::SpatialVector* PX_RESTRICT motionVelocityArray = params.motionVelocityArray; + + + //Save velocity - articulated + PxI32 endIndexCount2 = SaveUnrollCount; + PxI32 index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount; + { + WAIT_FOR_PROGRESS(articIndex2, targetArticIndex); + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + PxI32 nbConcluded = 0; + while(index2 < articulationListSize) + { + const PxI32 remainder = PxMin(SaveUnrollCount, (articulationListSize - index2)); + endIndexCount2 -= remainder; + for(PxI32 b = 0; b < remainder; ++b, ++index2) + { + ArticulationPImpl::saveVelocity(articulationListStart[index2], cache.deltaV); + } + if(endIndexCount2 == 0) + { + index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount; + endIndexCount2 = SaveUnrollCount; + } + nbConcluded += remainder; + } + + index2 -= articulationListSize; + + //save velocity + + + while(index2 < bodyListSize) + { + const PxI32 remainder = PxMin(endIndexCount2, (bodyListSize - index2)); + endIndexCount2 -= remainder; + for(PxI32 b = 0; b < remainder; ++b, ++index2) + { + Ps::prefetchLine(&bodyListStart[index2 + 8]); + Ps::prefetchLine(&motionVelocityArray[index2 + 8]); + PxSolverBody& body = bodyListStart[index2]; + Cm::SpatialVector& motionVel = motionVelocityArray[index2]; + motionVel.linear = body.linearVelocity; + motionVel.angular = body.angularState; + PX_ASSERT(motionVel.linear.isFinite()); + PX_ASSERT(motionVel.angular.isFinite()); + } + + nbConcluded += remainder; + + //Branch not required because this is the last time we use this atomic variable + //if(index2 < articulationListSizePlusbodyListSize) + { + index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount - articulationListSize; + endIndexCount2 = SaveUnrollCount; + } + } + + if(nbConcluded) + { + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(bodyListIndex2, nbConcluded); + } + } + + + WAIT_FOR_PROGRESS(bodyListIndex2, (bodyListSize + articulationListSize)); + + a = 1; + for(; a < params.velocityIterations; ++a) + { + WAIT_FOR_PROGRESS(articIndex2, targetArticIndex); + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxNormalIndex += headersPerPartition[b]; + + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, gVTableSolveBlock, + normalIteration); + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(constraintIndex2, nbSolved); + } + targetConstraintIndex += headersPerPartition[b]; //Increment target constraint index by batch count + } + + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxArticIndex += articulationListSize; + targetArticIndex += articulationListSize; + + while (articSolveStart < maxArticIndex) + { + + PxI32 endIdx = PxMin(articSolveEnd, maxArticIndex); + + PxI32 nbSolved = 0; + while (articSolveStart < endIdx) + { + articulationListStart[articSolveStart - articIndexCounter].articulation->solveInternalConstraints(dt, invDt, cache.Z, cache.deltaV, true); + articSolveStart++; + nbSolved++; + } + + if (nbSolved) + { + physx::shdfnd::atomicAdd(articIndex2, nbSolved); + } + + PxI32 remaining = articSolveEnd - articSolveStart; + + if (remaining == 0) + { + articSolveStart = physx::shdfnd::atomicAdd(articIndex, ArticCount) - ArticCount; + articSolveEnd = articSolveStart + ArticCount; + } + + } + ++normalIteration; + articIndexCounter += articulationListSize; + } + + ThresholdStreamElement* PX_RESTRICT thresholdStream = params.thresholdStream; + PxU32 thresholdStreamLength = params.thresholdStreamLength; + PxI32* outThresholdPairs = params.outThresholdPairs; + + cache.mSharedOutThresholdPairs = outThresholdPairs; + cache.mSharedThresholdStream = thresholdStream; + cache.mSharedThresholdStreamLength = thresholdStreamLength; + + //Last iteration - do writeback as well! + cache.writeBackIteration = true; + { + WAIT_FOR_PROGRESS(articIndex2, targetArticIndex); + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxNormalIndex += headersPerPartition[b]; + + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, gVTableSolveWriteBackBlock, + normalIteration); + + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(constraintIndex2, nbSolved); + } + targetConstraintIndex += headersPerPartition[b]; //Increment target constraint index by batch count + } + { + WAIT_FOR_PROGRESS(constraintIndex2, targetConstraintIndex); + + maxArticIndex += articulationListSize; + targetArticIndex += articulationListSize; + + while (articSolveStart < maxArticIndex) + { + + PxI32 endIdx = PxMin(articSolveEnd, maxArticIndex); + + PxI32 nbSolved = 0; + while (articSolveStart < endIdx) + { + articulationListStart[articSolveStart - articIndexCounter].articulation->solveInternalConstraints(dt, invDt, cache.Z, cache.deltaV, false); + articSolveStart++; + nbSolved++; + } + + if (nbSolved) + { + physx::shdfnd::atomicAdd(articIndex2, nbSolved); + } + + PxI32 remaining = articSolveEnd - articSolveStart; + + if (remaining == 0) + { + articSolveStart = physx::shdfnd::atomicAdd(articIndex, ArticCount) - ArticCount; + articSolveEnd = articSolveStart + ArticCount; + } + + } + } + + if(cache.mThresholdStreamIndex > 0) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(outThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 b = 0; b < cache.mThresholdStreamIndex; ++b) + { + thresholdStream[b + threshIndex] = cache.mThresholdStream[b]; + } + cache.mThresholdStreamIndex = 0; + } + + ++normalIteration; + + } + +#if PX_PROFILE_SOLVE_STALLS + + + PxU64 endTime = readTimer(); + PxReal totalTime = (PxReal)(endTime - startTime); + PxReal stallTime = (PxReal)stallCount; + PxReal stallRatio = stallTime/totalTime; + if(0)//stallRatio > 0.2f) + { + LARGE_INTEGER frequency; + QueryPerformanceFrequency( &frequency ); + printf("Warning -- percentage time stalled = %f; stalled for %f seconds; total Time took %f seconds\n", + stallRatio * 100.f, stallTime/(PxReal)frequency.QuadPart, totalTime/(PxReal)frequency.QuadPart); + } +#endif + + return normalIteration * batchCount; + +} + + +void SolverCoreGeneral::writeBackV +(const PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxU32 /*constraintListSize*/, PxConstraintBatchHeader* batchHeaders, const PxU32 numBatches, + ThresholdStreamElement* PX_RESTRICT thresholdStream, const PxU32 thresholdStreamLength, PxU32& outThresholdPairs, + PxSolverBodyData* atomListData, WriteBackBlockMethod writeBackTable[]) const +{ + SolverContext cache; + cache.solverBodyArray = atomListData; + cache.mThresholdStream = thresholdStream; + cache.mThresholdStreamLength = thresholdStreamLength; + cache.mThresholdStreamIndex = 0; + + PxI32 outThreshIndex = 0; + for(PxU32 j = 0; j < numBatches; ++j) + { + PxU8 type = *constraintList[batchHeaders[j].mStartIndex].constraint; + writeBackTable[type](constraintList + batchHeaders[j].mStartIndex, + batchHeaders[j].mStride, cache); + } + + outThresholdPairs = PxU32(outThreshIndex); +} + +void solveVBlock(SOLVEV_BLOCK_METHOD_ARGS) +{ + solverCore->solveV_Blocks(params); +} + +} +} + + +//#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h new file mode 100644 index 000000000..f9315a33c --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h @@ -0,0 +1,167 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCOREGENERAL_H +#define DY_SOLVERCOREGENERAL_H + +#include "DySolverCore.h" +#include "DySolverConstraintDesc.h" + +namespace physx +{ + +namespace Dy +{ + +struct FsData; + +inline void BusyWaitState(volatile PxU32* state, const PxU32 requiredState) +{ + while(requiredState != *state ); +} + +inline void WaitBodyRequiredState(PxU32* state, const PxU32 requiredState) +{ + if(*state != requiredState) + { + BusyWaitState(state, requiredState); + } +} + +inline void BusyWaitStates(volatile PxU32* stateA, volatile PxU32* stateB, const PxU32 requiredStateA, const PxU32 requiredStateB) +{ + while(*stateA != requiredStateA); + while(*stateB != requiredStateB); +} + + +class BatchIterator +{ +public: + PxConstraintBatchHeader* constraintBatchHeaders; + PxU32 mSize; + PxU32 mCurrentIndex; + + BatchIterator(PxConstraintBatchHeader* _constraintBatchHeaders, PxU32 size) : constraintBatchHeaders(_constraintBatchHeaders), + mSize(size), mCurrentIndex(0) + { + } + + PX_FORCE_INLINE const PxConstraintBatchHeader& GetCurrentHeader(const PxU32 constraintIndex) + { + PxU32 currentIndex = mCurrentIndex; + while((constraintIndex - constraintBatchHeaders[currentIndex].mStartIndex) >= constraintBatchHeaders[currentIndex].mStride) + currentIndex = (currentIndex + 1)%mSize; + Ps::prefetchLine(&constraintBatchHeaders[currentIndex], 128); + mCurrentIndex = currentIndex; + return constraintBatchHeaders[currentIndex]; + } +private: + BatchIterator& operator=(const BatchIterator&); +}; + + +inline void SolveBlockParallel (PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxI32 batchCount, const PxI32 index, + const PxI32 headerCount, SolverContext& cache, BatchIterator& iterator, + SolveBlockMethod solveTable[], + const PxI32 iteration + ) +{ + const PxI32 indA = index - (iteration * headerCount); + + const PxConstraintBatchHeader* PX_RESTRICT headers = iterator.constraintBatchHeaders; + + const PxI32 endIndex = indA + batchCount; + for(PxI32 i = indA; i < endIndex; ++i) + { + const PxConstraintBatchHeader& header = headers[i]; + + const PxI32 numToGrab = header.mStride; + PxSolverConstraintDesc* PX_RESTRICT block = &constraintList[header.mStartIndex]; + + Ps::prefetch(block[0].constraint, 384); + + for(PxI32 b = 0; b < numToGrab; ++b) + { + Ps::prefetchLine(block[b].bodyA); + Ps::prefetchLine(block[b].bodyB); + } + + //OK. We have a number of constraints to run... + solveTable[header.mConstraintType](block, PxU32(numToGrab), cache); + } +} + + + + +class SolverCoreGeneral : public SolverCore +{ +public: + bool frictionEveryIteration; + static SolverCoreGeneral* create(bool fricEveryIteration); + + // Implements SolverCore + virtual void destroyV(); + + virtual PxI32 solveVParallelAndWriteBack + (SolverIslandParams& params, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) const; + + virtual void solveV_Blocks + (SolverIslandParams& params) const; + + virtual void writeBackV + (const PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxU32 constraintListSize, PxConstraintBatchHeader* contactConstraintBatches, const PxU32 numBatches, + ThresholdStreamElement* PX_RESTRICT thresholdStream, const PxU32 thresholdStreamLength, PxU32& outThresholdPairs, + PxSolverBodyData* atomListData, WriteBackBlockMethod writeBackTable[]) const; + +private: + + //~Implements SolverCore +}; + +#define SOLVEV_BLOCK_METHOD_ARGS \ + SolverCore* solverCore, \ + SolverIslandParams& params + +void solveVBlock(SOLVEV_BLOCK_METHOD_ARGS); + +SolveBlockMethod* getSolveBlockTable(); + +SolveBlockMethod* getSolverConcludeBlockTable(); + +SolveWriteBackBlockMethod* getSolveWritebackBlockTable(); + + +} + +} + +#endif //DY_SOLVERCOREGENERAL_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp new file mode 100644 index 000000000..853ce9e15 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp @@ -0,0 +1,760 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "PsAllocator.h" +#include +#include +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverConstraint1D.h" +#include "DySolverContact.h" +#include "DyThresholdTable.h" +#include "DySolverControl.h" +#include "DyArticulationHelper.h" +#include "PsAtomic.h" +#include "PsIntrinsics.h" +#include "DyArticulationPImpl.h" +#include "PsThread.h" +#include "DySolverConstraintDesc.h" +#include "DySolverContext.h" +#include "DySolverControlPF.h" + +namespace physx +{ + +namespace Dy +{ +//----------------------------------- + +void solve1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4_Block (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + + +void solve1DConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4Block_Conclude (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solve1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExt1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solve1D4Block_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void writeBack1DBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void ext1DBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void writeBack1D4Block (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + + +void solveFrictionBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFriction_BStaticBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtFrictionBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactCoulombBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulomb_BStaticBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + + +void solveContactCoulombConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactCoulombConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulomb_BStaticConcludeBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solveContactCoulombBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtContactCoulombBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulomb_BStaticBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFrictionBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFriction_BStaticBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveExtFrictionBlockWriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +//Pre-block 1d/2d friction stuff... + +void solveContactCoulombPreBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombPreBlock_Static (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombPreBlock_Conclude (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombPreBlock_ConcludeStatic (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombPreBlock_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveContactCoulombPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFrictionCoulombPreBlock (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solveFrictionCoulombPreBlock_Static (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFrictionCoulombPreBlock_Conclude (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); +void solveFrictionCoulombPreBlock_ConcludeStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solveFrictionCoulombPreBlock_WriteBack (const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + +void solveFrictionCoulombPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache); + + +// could move this to PxPreprocessor.h but +// no implementation available for MSVC +#if PX_GCC_FAMILY +#define PX_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define PX_UNUSED_ATTRIBUTE +#endif + +#define DYNAMIC_ARTICULATION_REGISTRATION(x) 0 + + +static SolveBlockMethod gVTableSolveBlockCoulomb[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactCoulombBlock, // DY_SC_TYPE_RB_CONTACT + solve1DBlock, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactCoulombBlock), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DBlock), // DY_SC_TYPE_EXT_1D + solveContactCoulomb_BStaticBlock, // DY_SC_TYPE_STATIC_CONTACT + solveContactCoulombBlock, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactCoulombPreBlock, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactCoulombPreBlock_Static, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4_Block, // DY_SC_TYPE_BLOCK_1D, + solveFrictionBlock, // DY_SC_TYPE_FRICTION_CONSTRAINT + solveFriction_BStaticBlock, // DY_SC_TYPE_STATIC_FRICTION_CONSTRAINT + DYNAMIC_ARTICULATION_REGISTRATION(solveExtFrictionBlock), // DY_SC_TYPE_EXT_FRICTION_CONSTRAINT + solveFrictionCoulombPreBlock, // DY_SC_TYPE_BLOCK_FRICTION + solveFrictionCoulombPreBlock_Static // DY_SC_TYPE_BLOCK_STATIC_FRICTION +}; + +static SolveWriteBackBlockMethod gVTableSolveWriteBackBlockCoulomb[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactCoulombBlockWriteBack, // DY_SC_TYPE_RB_CONTACT + solve1DBlockWriteBack, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactCoulombBlockWriteBack), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DBlockWriteBack), // DY_SC_TYPE_EXT_1D + solveContactCoulomb_BStaticBlockWriteBack, // DY_SC_TYPE_STATIC_CONTACT + solveContactCoulombBlockWriteBack, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactCoulombPreBlock_WriteBack, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactCoulombPreBlock_WriteBackStatic, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4Block_WriteBack, // DY_SC_TYPE_BLOCK_1D, + solveFrictionBlockWriteBack, // DY_SC_TYPE_FRICTION_CONSTRAINT + solveFriction_BStaticBlockWriteBack, // DY_SC_TYPE_STATIC_FRICTION_CONSTRAINT + DYNAMIC_ARTICULATION_REGISTRATION(solveExtFrictionBlockWriteBack), // DY_SC_TYPE_EXT_FRICTION_CONSTRAINT + solveFrictionCoulombPreBlock_WriteBack, // DY_SC_TYPE_BLOCK_FRICTION + solveFrictionCoulombPreBlock_WriteBackStatic // DY_SC_TYPE_BLOCK_STATIC_FRICTION +}; + + +static SolveBlockMethod gVTableSolveConcludeBlockCoulomb[] PX_UNUSED_ATTRIBUTE = +{ + 0, + solveContactCoulombConcludeBlock, // DY_SC_TYPE_RB_CONTACT + solve1DConcludeBlock, // DY_SC_TYPE_RB_1D + DYNAMIC_ARTICULATION_REGISTRATION(solveExtContactCoulombConcludeBlock), // DY_SC_TYPE_EXT_CONTACT + DYNAMIC_ARTICULATION_REGISTRATION(solveExt1DConcludeBlock), // DY_SC_TYPE_EXT_1D + solveContactCoulomb_BStaticConcludeBlock, // DY_SC_TYPE_STATIC_CONTACT + solveContactCoulombConcludeBlock, // DY_SC_TYPE_NOFRICTION_RB_CONTACT + solveContactCoulombPreBlock_Conclude, // DY_SC_TYPE_BLOCK_RB_CONTACT + solveContactCoulombPreBlock_ConcludeStatic, // DY_SC_TYPE_BLOCK_STATIC_RB_CONTACT + solve1D4Block_Conclude, // DY_SC_TYPE_BLOCK_1D, + solveFrictionBlock, // DY_SC_TYPE_FRICTION_CONSTRAINT + solveFriction_BStaticBlock, // DY_SC_TYPE_STATIC_FRICTION_CONSTRAINT + DYNAMIC_ARTICULATION_REGISTRATION(solveExtFrictionBlock), // DY_SC_TYPE_EXT_FRICTION_CONSTRAINT + solveFrictionCoulombPreBlock_Conclude, // DY_SC_TYPE_BLOCK_FRICTION + solveFrictionCoulombPreBlock_ConcludeStatic // DY_SC_TYPE_BLOCK_STATIC_FRICTION +}; + + +void SolverCoreRegisterArticulationFnsCoulomb() +{ + gVTableSolveBlockCoulomb[DY_SC_TYPE_EXT_CONTACT] = solveExtContactCoulombBlock; + gVTableSolveBlockCoulomb[DY_SC_TYPE_EXT_1D] = solveExt1DBlock; + + gVTableSolveWriteBackBlockCoulomb[DY_SC_TYPE_EXT_CONTACT] = solveExtContactCoulombBlockWriteBack; + gVTableSolveWriteBackBlockCoulomb[DY_SC_TYPE_EXT_1D] = solveExt1DBlockWriteBack; + gVTableSolveConcludeBlockCoulomb[DY_SC_TYPE_EXT_CONTACT] = solveExtContactCoulombConcludeBlock; + gVTableSolveConcludeBlockCoulomb[DY_SC_TYPE_EXT_1D] = solveExt1DConcludeBlock; + + gVTableSolveBlockCoulomb[DY_SC_TYPE_EXT_FRICTION] = solveExtFrictionBlock; + gVTableSolveWriteBackBlockCoulomb[DY_SC_TYPE_EXT_FRICTION] = solveExtFrictionBlockWriteBack; + gVTableSolveConcludeBlockCoulomb[DY_SC_TYPE_EXT_FRICTION] = solveExtFrictionBlock; +} + +SolverCoreGeneralPF* SolverCoreGeneralPF::create() +{ + SolverCoreGeneralPF* scg = reinterpret_cast( + PX_ALLOC(sizeof(SolverCoreGeneralPF), "SolverCoreGeneral")); + + if(scg) + new (scg) SolverCoreGeneralPF; + + return scg; +} + +void SolverCoreGeneralPF::destroyV() +{ + this->~SolverCoreGeneralPF(); + PX_FREE(this); +} + +void SolverCoreGeneralPF::solveV_Blocks(SolverIslandParams& params) const +{ + const PxI32 TempThresholdStreamSize = 32; + ThresholdStreamElement tempThresholdStream[TempThresholdStreamSize]; + + SolverContext cache; + cache.solverBodyArray = params.bodyDataList; + cache.mThresholdStream = tempThresholdStream; + cache.mThresholdStreamLength = TempThresholdStreamSize; + cache.mThresholdStreamIndex = 0; + cache.writeBackIteration = false; + cache.deltaV = params.deltaV; + cache.Z = params.Z; + + PxI32 batchCount = PxI32(params.numConstraintHeaders); + + PxSolverBody* PX_RESTRICT bodyListStart = params.bodyListStart; + const PxU32 bodyListSize = params.bodyListSize; + + Cm::SpatialVector* PX_RESTRICT motionVelocityArray = params.motionVelocityArray; + + const PxU32 velocityIterations = params.velocityIterations; + const PxU32 positionIterations = params.positionIterations; + + const PxU32 numConstraintHeaders = params.numConstraintHeaders; + const PxU32 articulationListSize = params.articulationListSize; + + ArticulationSolverDesc* PX_RESTRICT articulationListStart = params.articulationListStart; + + + PX_ASSERT(velocityIterations >= 1); + PX_ASSERT(positionIterations >= 1); + + if(numConstraintHeaders == 0) + { + for (PxU32 baIdx = 0; baIdx < bodyListSize; baIdx++) + { + Cm::SpatialVector& motionVel = motionVelocityArray[baIdx]; + PxSolverBody& atom = bodyListStart[baIdx]; + motionVel.linear = atom.linearVelocity; + motionVel.angular = atom.angularState; + } + + for (PxU32 i = 0; i < articulationListSize; i++) + ArticulationPImpl::saveVelocity(articulationListStart[i], cache.deltaV); + + return; + } + + BatchIterator contactIterator(params.constraintBatchHeaders, params.numConstraintHeaders); + BatchIterator frictionIterator(params.frictionConstraintBatches, params.numFrictionConstraintHeaders); + + + PxI32 frictionBatchCount = PxI32(params.numFrictionConstraintHeaders); + + PxSolverConstraintDesc* PX_RESTRICT constraintList = params.constraintList; + + PxSolverConstraintDesc* PX_RESTRICT frictionConstraintList = params.frictionConstraintList; + + + //0-(n-1) iterations + PxI32 normalIter = 0; + PxI32 frictionIter = 0; + for (PxU32 iteration = positionIterations; iteration > 0; iteration--) //decreasing positive numbers == position iters + { + + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, iteration == 1 ? gVTableSolveConcludeBlockCoulomb : gVTableSolveBlockCoulomb, normalIter); + ++normalIter; + + } + + if(frictionBatchCount>0) + { + const PxU32 numIterations = positionIterations * 2; + for (PxU32 iteration = numIterations; iteration > 0; iteration--) //decreasing positive numbers == position iters + { + SolveBlockParallel(frictionConstraintList, frictionBatchCount, frictionIter * frictionBatchCount, frictionBatchCount, + cache, frictionIterator, iteration == 1 ? gVTableSolveConcludeBlockCoulomb : gVTableSolveBlockCoulomb, frictionIter); + ++frictionIter; + } + } + + for (PxU32 baIdx = 0; baIdx < bodyListSize; baIdx++) + { + const PxSolverBody& atom = bodyListStart[baIdx]; + Cm::SpatialVector& motionVel = motionVelocityArray[baIdx]; + motionVel.linear = atom.linearVelocity; + motionVel.angular = atom.angularState; + } + + + for (PxU32 i = 0; i < articulationListSize; i++) + ArticulationPImpl::saveVelocity(articulationListStart[i], cache.deltaV); + + + const PxU32 velItersMinOne = velocityIterations - 1; + + PxU32 iteration = 0; + + for(; iteration < velItersMinOne; ++iteration) + { + + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, gVTableSolveBlockCoulomb, normalIter); + ++normalIter; + + if(frictionBatchCount > 0) + { + SolveBlockParallel(frictionConstraintList, frictionBatchCount, frictionIter * frictionBatchCount, frictionBatchCount, + cache, frictionIterator, gVTableSolveBlockCoulomb, frictionIter); + ++frictionIter; + } + } + + PxI32* outThresholdPairs = params.outThresholdPairs; + ThresholdStreamElement* PX_RESTRICT thresholdStream = params.thresholdStream; + PxU32 thresholdStreamLength = params.thresholdStreamLength; + + cache.writeBackIteration = true; + + cache.mSharedOutThresholdPairs = outThresholdPairs; + cache.mSharedThresholdStreamLength = thresholdStreamLength; + cache.mSharedThresholdStream = thresholdStream; + + for(; iteration < velocityIterations; ++iteration) + { + SolveBlockParallel(constraintList, batchCount, normalIter * batchCount, batchCount, + cache, contactIterator, gVTableSolveWriteBackBlockCoulomb, normalIter); + ++normalIter; + + if(frictionBatchCount > 0) + { + SolveBlockParallel(frictionConstraintList, frictionBatchCount, frictionIter * frictionBatchCount, frictionBatchCount, + cache, frictionIterator, gVTableSolveWriteBackBlockCoulomb, frictionIter); + ++frictionIter; + } + } + + //Write back remaining threshold streams + if(cache.mThresholdStreamIndex > 0) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(reinterpret_cast(&outThresholdPairs), PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 b = 0; b < cache.mThresholdStreamIndex; ++b) + { + thresholdStream[b + threshIndex] = cache.mThresholdStream[b]; + } + cache.mThresholdStreamIndex = 0; + } + +} + +PxI32 SolverCoreGeneralPF::solveVParallelAndWriteBack(SolverIslandParams& params, + Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) const +{ + SolverContext cache; + cache.solverBodyArray = params.bodyDataList; + + const PxI32 UnrollCount = PxI32(params.batchSize); + const PxI32 SaveUnrollCount = 64; + + const PxI32 TempThresholdStreamSize = 32; + ThresholdStreamElement tempThresholdStream[TempThresholdStreamSize]; + + + const PxI32 batchCount = PxI32(params.numConstraintHeaders); + const PxI32 frictionBatchCount = PxI32(params.numFrictionConstraintHeaders);//frictionConstraintBatches.size(); + cache.mThresholdStream = tempThresholdStream; + cache.mThresholdStreamLength = TempThresholdStreamSize; + cache.mThresholdStreamIndex = 0; + cache.Z = Z; + cache.deltaV = deltaV; + + const PxI32 positionIterations = PxI32(params.positionIterations); + const PxU32 velocityIterations = params.velocityIterations; + + const PxI32 bodyListSize = PxI32(params.bodyListSize); + const PxI32 articulationListSize = PxI32(params.articulationListSize); + + PX_ASSERT(velocityIterations >= 1); + PX_ASSERT(positionIterations >= 1); + + PxI32* constraintIndex = ¶ms.constraintIndex; + PxI32* constraintIndex2 = ¶ms.constraintIndex2; + PxI32* frictionConstraintIndex = ¶ms.frictionConstraintIndex; + + PxI32 endIndexCount = UnrollCount; + PxI32 index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + PxI32 frictionIndex = physx::shdfnd::atomicAdd(frictionConstraintIndex, UnrollCount) - UnrollCount; + + + BatchIterator contactIter(params.constraintBatchHeaders, params.numConstraintHeaders); + BatchIterator frictionIter(params.frictionConstraintBatches, params.numFrictionConstraintHeaders); + + PxU32* headersPerPartition = params.headersPerPartition; + PxU32 nbPartitions = params.nbPartitions; + + PxU32* frictionHeadersPerPartition = params.frictionHeadersPerPartition; + PxU32 nbFrictionPartitions = params.nbFrictionPartitions; + + PxSolverConstraintDesc* PX_RESTRICT constraintList = params.constraintList; + PxSolverConstraintDesc* PX_RESTRICT frictionConstraintList = params.frictionConstraintList; + + + PxI32 maxNormalIndex = 0; + PxI32 maxProgress = 0; + PxI32 frictionEndIndexCount = UnrollCount; + PxI32 maxFrictionIndex = 0; + + PxI32 normalIteration = 0; + PxI32 frictionIteration = 0; + PxU32 a = 0; + for(PxU32 i = 0; i < 2; ++i) + { + SolveBlockMethod* solveTable = i == 0 ? gVTableSolveBlockCoulomb : gVTableSolveConcludeBlockCoulomb; + for(; a < positionIterations - 1 + i; ++a) + { + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxNormalIndex += headersPerPartition[b]; + maxProgress += headersPerPartition[b]; + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, solveTable, + normalIteration); + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + ++normalIteration; + } + + } + + + for(PxU32 i = 0; i < 2; ++i) + { + SolveBlockMethod* solveTable = i == 0 ? gVTableSolveBlockCoulomb : gVTableSolveConcludeBlockCoulomb; + const PxI32 numIterations = positionIterations *2; + for(; a < numIterations - 1 + i; ++a) + { + for(PxU32 b = 0; b < nbFrictionPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxProgress += frictionHeadersPerPartition[b]; + maxFrictionIndex += frictionHeadersPerPartition[b]; + PxI32 nbSolved = 0; + while(frictionIndex < maxFrictionIndex) + { + const PxI32 remainder = PxMin(maxFrictionIndex - frictionIndex, frictionEndIndexCount); + SolveBlockParallel(frictionConstraintList, remainder, frictionIndex, frictionBatchCount, cache, frictionIter, + solveTable, frictionIteration); + frictionIndex += remainder; + frictionEndIndexCount -= remainder; + nbSolved += remainder; + if(frictionEndIndexCount == 0) + { + frictionEndIndexCount = UnrollCount; + frictionIndex = physx::shdfnd::atomicAdd(frictionConstraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + ++frictionIteration; + + } + + } + + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + + + PxI32* bodyListIndex = ¶ms.bodyListIndex; + + ArticulationSolverDesc* PX_RESTRICT articulationListStart = params.articulationListStart; + + PxSolverBody* PX_RESTRICT bodyListStart = params.bodyListStart; + + Cm::SpatialVector* PX_RESTRICT motionVelocityArray = params.motionVelocityArray; + + PxI32* bodyListIndex2 = ¶ms.bodyListIndex2; + + PxI32 endIndexCount2 = SaveUnrollCount; + PxI32 index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount; + { + PxI32 nbConcluded = 0; + while(index2 < articulationListSize) + { + const PxI32 remainder = PxMin(SaveUnrollCount, (articulationListSize - index2)); + endIndexCount2 -= remainder; + for(PxI32 b = 0; b < remainder; ++b, ++index2) + { + ArticulationPImpl::saveVelocity(articulationListStart[index2], cache.deltaV); + } + nbConcluded += remainder; + if(endIndexCount2 == 0) + { + index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount; + endIndexCount2 = SaveUnrollCount; + } + nbConcluded += remainder; + } + + index2 -= articulationListSize; + + //save velocity + + + while(index2 < bodyListSize) + { + const PxI32 remainder = PxMin(endIndexCount2, (bodyListSize - index2)); + endIndexCount2 -= remainder; + for(PxI32 b = 0; b < remainder; ++b, ++index2) + { + Ps::prefetchLine(&bodyListStart[index2 + 8]); + Ps::prefetchLine(&motionVelocityArray[index2 + 8]); + PxSolverBody& body = bodyListStart[index2]; + Cm::SpatialVector& motionVel = motionVelocityArray[index2]; + motionVel.linear = body.linearVelocity; + motionVel.angular = body.angularState; + PX_ASSERT(motionVel.linear.isFinite()); + PX_ASSERT(motionVel.angular.isFinite()); + } + + nbConcluded += remainder; + + //Branch not required because this is the last time we use this atomic variable + //if(index2 < articulationListSizePlusbodyListSize) + { + index2 = physx::shdfnd::atomicAdd(bodyListIndex, SaveUnrollCount) - SaveUnrollCount - articulationListSize; + endIndexCount2 = SaveUnrollCount; + } + } + + if(nbConcluded) + { + Ps::memoryBarrier(); + physx::shdfnd::atomicAdd(bodyListIndex2, nbConcluded); + } + } + + + WAIT_FOR_PROGRESS(bodyListIndex2, (bodyListSize + articulationListSize)); + + a = 0; + for(; a < velocityIterations-1; ++a) + { + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxNormalIndex += headersPerPartition[b]; + maxProgress += headersPerPartition[b]; + + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, gVTableSolveBlockCoulomb, normalIteration); + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + ++normalIteration; + + for(PxU32 b = 0; b < nbFrictionPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxFrictionIndex += frictionHeadersPerPartition[b]; + maxProgress += frictionHeadersPerPartition[b]; + + PxI32 nbSolved = 0; + while(frictionIndex < maxFrictionIndex) + { + const PxI32 remainder = PxMin(maxFrictionIndex - frictionIndex, frictionEndIndexCount); + SolveBlockParallel(constraintList, remainder, index, batchCount, cache, contactIter, gVTableSolveBlockCoulomb, + normalIteration); + + frictionIndex += remainder; + frictionEndIndexCount -= remainder; + nbSolved += remainder; + if(frictionEndIndexCount == 0) + { + frictionEndIndexCount = UnrollCount; + frictionIndex = physx::shdfnd::atomicAdd(frictionConstraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + + ++frictionIteration; + } + + ThresholdStreamElement* PX_RESTRICT thresholdStream = params.thresholdStream; + const PxU32 thresholdStreamLength = params.thresholdStreamLength; + PxI32* outThresholdPairs = params.outThresholdPairs; + + cache.mSharedThresholdStream = thresholdStream; + cache.mSharedOutThresholdPairs = outThresholdPairs; + cache.mSharedThresholdStreamLength = thresholdStreamLength; + + { + for(PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxNormalIndex += headersPerPartition[b]; + maxProgress += headersPerPartition[b]; + + PxI32 nbSolved = 0; + while(index < maxNormalIndex) + { + const PxI32 remainder = PxMin(maxNormalIndex - index, endIndexCount); + + SolveBlockParallel(constraintList, remainder, normalIteration * batchCount, batchCount, + cache, contactIter, gVTableSolveWriteBackBlockCoulomb, normalIteration); + + index += remainder; + endIndexCount -= remainder; + nbSolved += remainder; + if(endIndexCount == 0) + { + endIndexCount = UnrollCount; + index = physx::shdfnd::atomicAdd(constraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + + ++normalIteration; + + cache.mSharedOutThresholdPairs = outThresholdPairs; + cache.mSharedThresholdStream = thresholdStream; + cache.mSharedThresholdStreamLength = thresholdStreamLength; + + for(PxU32 b = 0; b < nbFrictionPartitions; ++b) + { + WAIT_FOR_PROGRESS(constraintIndex2, maxProgress); + maxFrictionIndex += frictionHeadersPerPartition[b]; + maxProgress += frictionHeadersPerPartition[b]; + + PxI32 nbSolved = 0; + while(frictionIndex < maxFrictionIndex) + { + const PxI32 remainder = PxMin(maxFrictionIndex - frictionIndex, frictionEndIndexCount); + + SolveBlockParallel(frictionConstraintList, remainder, frictionIndex, frictionBatchCount, cache, frictionIter, + gVTableSolveWriteBackBlockCoulomb, frictionIteration); + + frictionIndex += remainder; + frictionEndIndexCount -= remainder; + nbSolved += remainder; + if(frictionEndIndexCount == 0) + { + frictionEndIndexCount = UnrollCount; + frictionIndex = physx::shdfnd::atomicAdd(frictionConstraintIndex, UnrollCount) - UnrollCount; + } + } + if(nbSolved) + { + Ps::memoryBarrier(); + Ps::atomicAdd(constraintIndex2, nbSolved); + } + } + + if(cache.mThresholdStreamIndex > 0) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(outThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 b = 0; b < cache.mThresholdStreamIndex; ++b) + { + thresholdStream[b + threshIndex] = cache.mThresholdStream[b]; + } + cache.mThresholdStreamIndex = 0; + } + + ++frictionIteration; + } + + return normalIteration * batchCount + frictionIteration * frictionBatchCount; +} + + +void SolverCoreGeneralPF::writeBackV +(const PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxU32 /*constraintListSize*/, PxConstraintBatchHeader* batchHeaders, const PxU32 numBatches, + ThresholdStreamElement* PX_RESTRICT thresholdStream, const PxU32 thresholdStreamLength, PxU32& outThresholdPairs, + PxSolverBodyData* atomListData, WriteBackBlockMethod writeBackTable[]) const +{ + SolverContext cache; + cache.solverBodyArray = atomListData; + cache.mThresholdStream = thresholdStream; + cache.mThresholdStreamLength = thresholdStreamLength; + cache.mThresholdStreamIndex = 0; + + PxI32 outThreshIndex = 0; + for(PxU32 j = 0; j < numBatches; ++j) + { + PxU8 type = *constraintList[batchHeaders[j].mStartIndex].constraint; + writeBackTable[type](constraintList + batchHeaders[j].mStartIndex, + batchHeaders[j].mStride, cache); + } + + outThresholdPairs = PxU32(outThreshIndex); +} + +} + +} + + +//#endif diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h new file mode 100644 index 000000000..7ab760bef --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h @@ -0,0 +1,71 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCONTROLPF_H +#define DY_SOLVERCONTROLPF_H + +#include "DySolverCore.h" +#include "DySolverConstraintDesc.h" + +namespace physx +{ + +namespace Dy +{ + +class SolverCoreGeneralPF : public SolverCore +{ +public: + static SolverCoreGeneralPF* create(); + + // Implements SolverCore + virtual void destroyV(); + + virtual PxI32 solveVParallelAndWriteBack + (SolverIslandParams& params, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) const; + + virtual void solveV_Blocks + (SolverIslandParams& params) const; + + virtual void writeBackV + (const PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxU32 constraintListSize, PxConstraintBatchHeader* contactConstraintBatches, const PxU32 numBatches, + ThresholdStreamElement* PX_RESTRICT thresholdStream, const PxU32 thresholdStreamLength, PxU32& outThresholdPairs, + PxSolverBodyData* atomListData, WriteBackBlockMethod writeBackTable[]) const; + +private: + + //~Implements SolverCore +}; + +} + +} + +#endif //DY_SOLVERCOREGENERALPF_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h new file mode 100644 index 000000000..dbe1da918 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h @@ -0,0 +1,251 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVERCORE_H +#define DY_SOLVERCORE_H + +#include "PxvConfig.h" +#include "PsArray.h" +#include "PsThread.h" + + +namespace physx +{ + +struct PxSolverBody; +struct PxSolverBodyData; +struct PxSolverConstraintDesc; +struct PxConstraintBatchHeader; + +namespace Dy +{ +struct ThresholdStreamElement; + + +struct ArticulationSolverDesc; +class Articulation; +struct SolverContext; + +typedef void (*WriteBackMethod)(const PxSolverConstraintDesc& desc, SolverContext& cache, PxSolverBodyData& sbd0, PxSolverBodyData& sbd1); +typedef void (*SolveMethod)(const PxSolverConstraintDesc& desc, SolverContext& cache); +typedef void (*SolveBlockMethod)(const PxSolverConstraintDesc* desc, const PxU32 constraintCount, SolverContext& cache); +typedef void (*SolveWriteBackBlockMethod)(const PxSolverConstraintDesc* desc, const PxU32 constraintCount, SolverContext& cache); +typedef void (*WriteBackBlockMethod)(const PxSolverConstraintDesc* desc, const PxU32 constraintCount, SolverContext& cache); + +#define PX_PROFILE_SOLVE_STALLS 0 +#if PX_PROFILE_SOLVE_STALLS +#if PX_WINDOWS +#include + + +PX_FORCE_INLINE PxU64 readTimer() +{ + //return __rdtsc(); + + LARGE_INTEGER i; + QueryPerformanceCounter(&i); + return i.QuadPart; +} + +#endif +#endif + + +#define YIELD_THREADS 1 + +#if YIELD_THREADS + +#define ATTEMPTS_BEFORE_BACKOFF 30000 +#define ATTEMPTS_BEFORE_RETEST 10000 + +#endif + +PX_INLINE void WaitForProgressCount(volatile PxI32* pGlobalIndex, const PxI32 targetIndex) +{ +#if YIELD_THREADS + if(*pGlobalIndex < targetIndex) + { + bool satisfied = false; + PxU32 count = ATTEMPTS_BEFORE_BACKOFF; + do + { + satisfied = true; + while(*pGlobalIndex < targetIndex) + { + if(--count == 0) + { + satisfied = false; + break; + } + } + if(!satisfied) + Ps::Thread::yield(); + count = ATTEMPTS_BEFORE_RETEST; + } + while(!satisfied); + } +#else + while(*pGlobalIndex < targetIndex); +#endif +} + + +#if PX_PROFILE_SOLVE_STALLS +PX_INLINE void WaitForProgressCount(volatile PxI32* pGlobalIndex, const PxI32 targetIndex, PxU64& stallTime) +{ + if(*pGlobalIndex < targetIndex) + { + bool satisfied = false; + PxU32 count = ATTEMPTS_BEFORE_BACKOFF; + do + { + satisfied = true; + PxU64 startTime = readTimer(); + while(*pGlobalIndex < targetIndex) + { + if(--count == 0) + { + satisfied = false; + break; + } + } + PxU64 endTime = readTimer(); + stallTime += (endTime - startTime); + if(!satisfied) + Ps::Thread::yield(); + count = ATTEMPTS_BEFORE_BACKOFF; + } + while(!satisfied); + } +} + +#define WAIT_FOR_PROGRESS(pGlobalIndex, targetIndex) if(*pGlobalIndex < targetIndex) WaitForProgressCount(pGlobalIndex, targetIndex, stallCount) +#else +#define WAIT_FOR_PROGRESS(pGlobalIndex, targetIndex) if(*pGlobalIndex < targetIndex) WaitForProgressCount(pGlobalIndex, targetIndex) +#endif +#define WAIT_FOR_PROGRESS_NO_TIMER(pGlobalIndex, targetIndex) if(*pGlobalIndex < targetIndex) WaitForProgressCount(pGlobalIndex, targetIndex) + + +struct SolverIslandParams +{ + //Default friction model params + PxU32 positionIterations; + PxU32 velocityIterations; + PxSolverBody* PX_RESTRICT bodyListStart; + PxSolverBodyData* PX_RESTRICT bodyDataList; + PxU32 bodyListSize; + PxU32 solverBodyOffset; + ArticulationSolverDesc* PX_RESTRICT articulationListStart; + PxU32 articulationListSize; + PxSolverConstraintDesc* PX_RESTRICT constraintList; + PxConstraintBatchHeader* constraintBatchHeaders; + PxU32 numConstraintHeaders; + PxU32* headersPerPartition; + PxU32 nbPartitions; + Cm::SpatialVector* PX_RESTRICT motionVelocityArray; + PxU32 batchSize; + PxsBodyCore*const* bodyArray; + PxsRigidBody** PX_RESTRICT rigidBodies; + + //Shared state progress counters + PxI32 constraintIndex; + PxI32 constraintIndex2; + PxI32 bodyListIndex; + PxI32 bodyListIndex2; + PxI32 articSolveIndex; + PxI32 articSolveIndex2; + PxI32 bodyIntegrationListIndex; + PxI32 numObjectsIntegrated; + + PxReal dt; + PxReal invDt; + + + //Additional 1d/2d friction model params + PxSolverConstraintDesc* PX_RESTRICT frictionConstraintList; + + PxConstraintBatchHeader* frictionConstraintBatches; + PxU32 numFrictionConstraintHeaders; + PxU32* frictionHeadersPerPartition; + PxU32 nbFrictionPartitions; + + //Additional Shared state progress counters + PxI32 frictionConstraintIndex; + + //Write-back threshold information + ThresholdStreamElement* PX_RESTRICT thresholdStream; + PxU32 thresholdStreamLength; + + PxI32* outThresholdPairs; + + PxU32 mMaxArticulationLinks; + Cm::SpatialVectorF* Z; + Cm::SpatialVectorF* deltaV; +}; + + +/*! +Interface to constraint solver cores + +*/ +class SolverCore +{ +public: + virtual void destroyV() = 0; + virtual ~SolverCore() {} + /* + solves dual problem exactly by GS-iterating until convergence stops + only uses regular velocity vector for storing results, and backs up initial state, which is restored. + the solution forces are saved in a vector. + + state should not be stored, this function is safe to call from multiple threads. + + Returns the total number of constraints that should be solved across all threads. Used for synchronization outside of this method + */ + + virtual PxI32 solveVParallelAndWriteBack + (SolverIslandParams& params, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV) const = 0; + + + virtual void solveV_Blocks + (SolverIslandParams& params) const = 0; + + + virtual void writeBackV + (const PxSolverConstraintDesc* PX_RESTRICT constraintList, const PxU32 constraintListSize, PxConstraintBatchHeader* contactConstraintBatches, const PxU32 numConstraintBatches, + ThresholdStreamElement* PX_RESTRICT thresholdStream, const PxU32 thresholdStreamLength, PxU32& outThresholdPairs, + PxSolverBodyData* atomListData, WriteBackBlockMethod writeBackTable[]) const = 0; +}; + +} + +} + +#endif //DY_SOLVERCORE_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h new file mode 100644 index 000000000..ae5703433 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h @@ -0,0 +1,85 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SOLVEREXTBODY_H +#define DY_SOLVEREXTBODY_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "CmPhysXCommon.h" +#include "CmSpatialVector.h" + +namespace physx +{ + +class PxsRigidBody; +struct PxsBodyCore; +struct PxSolverBody; +struct PxSolverBodyData; + + +namespace Dy +{ + + +class ArticulationV; +struct SolverConstraint1D; + +class SolverExtBody +{ +public: + union + { + const ArticulationV* mArticulation; + const PxSolverBody* mBody; + }; + const PxSolverBodyData* mBodyData; + + PxU16 mLinkIndex; + + SolverExtBody(const void* bodyOrArticulation, const void* bodyData, PxU16 linkIndex): + mBody(reinterpret_cast(bodyOrArticulation)), + mBodyData(reinterpret_cast(bodyData)), + mLinkIndex(linkIndex) + {} + + void getResponse(const PxVec3& linImpulse, const PxVec3& angImpulse, + PxVec3& linDeltaV, PxVec3& angDeltaV, PxReal dominance) const; + + PxReal projectVelocity(const PxVec3& linear, const PxVec3& angular) const; + PxVec3 getLinVel() const; + PxVec3 getAngVel() const; +}; + +} + +} + +#endif //DY_SOLVEREXTBODY_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp new file mode 100644 index 000000000..a7e6c018d --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp @@ -0,0 +1,879 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" + +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DySolverConstraint1D.h" +#include "DySolverConstraintDesc.h" +#include "DyThresholdTable.h" +#include "DySolverContext.h" +#include "PsUtilities.h" +#include "DyConstraint.h" +#include "PsAtomic.h" +#include "DyThresholdTable.h" +#include "DySolverConstraintsShared.h" + +namespace physx +{ + +namespace Dy +{ + +void solveContactCoulomb(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + PxSolverBody& b0 = *desc.bodyA; + PxSolverBody& b1 = *desc.bodyB; + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V linVel1 = V3LoadA(b1.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + Vec3V angState1 = V3LoadA(b1.angularState); + + SolverContactCoulombHeader* PX_RESTRICT firstHeader = reinterpret_cast(desc.constraint); + const PxU8* PX_RESTRICT last = desc.constraint + firstHeader->frictionOffset;//getConstraintLength(desc); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + + //const FloatV zero = FZero(); + + while(currPtr < last) + { + SolverContactCoulombHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactCoulombHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + const Vec3V normal = hdr->getNormal(); + const FloatV invMassDom0 = FLoad(hdr->dominance0); + const FloatV invMassDom1 = FLoad(hdr->dominance1); + const FloatV angD0 = FLoad(hdr->angDom0); + const FloatV angD1 = FLoad(hdr->angDom1); + + + + SolverContactPoint* PX_RESTRICT contacts = reinterpret_cast(currPtr); + currPtr += numNormalConstr * sizeof(SolverContactPoint); + + PxF32* appliedImpulse = reinterpret_cast ((reinterpret_cast(hdr)) + hdr->frictionOffset + sizeof(SolverFrictionHeader)); + Ps::prefetchLine(appliedImpulse); + + solveDynamicContacts(contacts, numNormalConstr, normal, invMassDom0, invMassDom1, + angD0, angD1, linVel0, angState0, linVel1, angState1, appliedImpulse); + } + + // Write back + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(linVel1, b1.linearVelocity); + V3StoreA(angState0, b0.angularState); + V3StoreA(angState1, b1.angularState); + + PX_ASSERT(currPtr == last); +} + +void solveFriction(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + PxSolverBody& b0 = *desc.bodyA; + PxSolverBody& b1 = *desc.bodyB; + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V linVel1 = V3LoadA(b1.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + Vec3V angState1 = V3LoadA(b1.angularState); + + PxU8* PX_RESTRICT ptr = desc.constraint; + PxU8* PX_RESTRICT currPtr = ptr; + + const PxU8* PX_RESTRICT last = ptr + getConstraintLength(desc); + + + while(currPtr < last) + { + const SolverFrictionHeader* PX_RESTRICT frictionHeader = reinterpret_cast(currPtr); + currPtr += sizeof(SolverFrictionHeader); + PxF32* appliedImpulse = reinterpret_cast(currPtr); + currPtr += frictionHeader->getAppliedForcePaddingSize(); + + SolverContactFriction* PX_RESTRICT frictions = reinterpret_cast(currPtr); + const PxU32 numFrictionConstr = frictionHeader->numFrictionConstr; + const PxU32 numNormalConstr = frictionHeader->numNormalConstr; + + const PxU32 numFrictionPerPoint = numFrictionConstr/numNormalConstr; + + currPtr += numFrictionConstr * sizeof(SolverContactFriction); + const FloatV staticFriction = frictionHeader->getStaticFriction(); + + const FloatV invMass0D0 = FLoad(frictionHeader->invMass0D0); + const FloatV invMass1D1 = FLoad(frictionHeader->invMass1D1); + + + const FloatV angD0 = FLoad(frictionHeader->angDom0); + const FloatV angD1 = FLoad(frictionHeader->angDom1); + + for(PxU32 i=0, j = 0;i(desc.constraint); + const PxU8* PX_RESTRICT last = desc.constraint + firstHeader->frictionOffset;//getConstraintLength(desc); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + //const FloatV zero = FZero(); + + while(currPtr < last) + { + SolverContactCoulombHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactCoulombHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + SolverContactPoint* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPoint); + + PxF32* appliedImpulse = reinterpret_cast ((reinterpret_cast(hdr)) + hdr->frictionOffset + sizeof(SolverFrictionHeader)); + Ps::prefetchLine(appliedImpulse); + + const Vec3V normal = hdr->getNormal(); + + const FloatV invMassDom0 = FLoad(hdr->dominance0); + + const FloatV angD0 = FLoad(hdr->angDom0); + + solveStaticContacts(contacts, numNormalConstr, normal, invMassDom0, + angD0, linVel0, angState0, appliedImpulse); + } + + // Write back + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(angState0, b0.angularState); + + PX_ASSERT(currPtr == last); +} + +void solveFriction_BStatic(const PxSolverConstraintDesc& desc, SolverContext& /*cache*/) +{ + PxSolverBody& b0 = *desc.bodyA; + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularState); + + PxU8* PX_RESTRICT currPtr = desc.constraint; + + const PxU8* PX_RESTRICT last = currPtr + getConstraintLength(desc); + + while(currPtr < last) + { + + const SolverFrictionHeader* PX_RESTRICT frictionHeader = reinterpret_cast(currPtr); + const PxU32 numFrictionConstr = frictionHeader->numFrictionConstr; + const PxU32 numNormalConstr = frictionHeader->numNormalConstr; + const PxU32 numFrictionPerPoint = numFrictionConstr/numNormalConstr; + currPtr +=sizeof(SolverFrictionHeader); + PxF32* appliedImpulse = reinterpret_cast(currPtr); + currPtr +=frictionHeader->getAppliedForcePaddingSize(); + + SolverContactFriction* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFriction); + + const FloatV invMass0 = FLoad(frictionHeader->invMass0D0); + const FloatV angD0 = FLoad(frictionHeader->angDom0); + //const FloatV angD1 = FLoad(frictionHeader->angDom1); + + + const FloatV staticFriction = frictionHeader->getStaticFriction(); + + for(PxU32 i=0, j = 0;i(cPtr); + PxU8* PX_RESTRICT last = desc.constraint + firstHeader->frictionOffset;//getConstraintLength(desc); + while(cPtr < last) + { + const SolverContactCoulombHeader* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactCoulombHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + //if(cPtr < last) + //Ps::prefetchLine(cPtr, 512); + Ps::prefetchLine(cPtr,128); + Ps::prefetchLine(cPtr,256); + Ps::prefetchLine(cPtr,384); + + const PxU32 pointStride = hdr->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactPointExt) + : sizeof(SolverContactPoint); + for(PxU32 i=0;i(cPtr); + cPtr += pointStride; + //c->scaledBias = PxMin(c->scaledBias, 0.f); + c->biasedErr = c->unbiasedErr; + } + } + PX_ASSERT(cPtr == last); +} + +void writeBackContactCoulomb(const PxSolverConstraintDesc& desc, SolverContext& cache, + PxSolverBodyData& bd0, PxSolverBodyData& bd1) +{ + + PxReal normalForce = 0.f; + + PxU8* PX_RESTRICT cPtr = desc.constraint; + PxReal* PX_RESTRICT vForceWriteback = reinterpret_cast(desc.writeBack); + const SolverContactCoulombHeader* PX_RESTRICT firstHeader = reinterpret_cast(cPtr); + PxU8* PX_RESTRICT last = desc.constraint + firstHeader->frictionOffset; + + const PxU32 pointStride = firstHeader->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactPointExt) + : sizeof(SolverContactPoint); + + bool hasForceThresholds = false; + while(cPtr < last) + { + const SolverContactCoulombHeader* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactCoulombHeader); + + PxF32* appliedImpulse = reinterpret_cast (const_cast((reinterpret_cast(hdr)) + hdr->frictionOffset + sizeof(SolverFrictionHeader))); + + hasForceThresholds = hdr->flags & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + Ps::prefetchLine(cPtr, 256); + Ps::prefetchLine(cPtr, 384); + + if(vForceWriteback!=NULL) + { + for(PxU32 i=0; i(bd0.reportThreshold, bd1.reportThreshold); + elt.nodeIndexA = IG::NodeIndex(bd0.nodeIndex); + elt.nodeIndexB = IG::NodeIndex(bd1.nodeIndex); + elt.shapeInteraction = (reinterpret_cast(desc.constraint))->shapeInteraction; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + + PX_ASSERT(cache.mThresholdStreamIndex (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveContactCoulomb_BStaticBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveContactCoulomb_BStatic(desc[a], cache); + } +} + +void solveContactCoulomb_BStaticConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveContactCoulomb_BStatic(desc[a], cache); + concludeContactCoulomb(desc[a], cache); + } +} + +void solveContactCoulomb_BStaticBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a].bodyBDataIndex]; + solveContactCoulomb_BStatic(desc[a], cache); + writeBackContactCoulomb(desc[a], cache, bd0, bd1); + } + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Not enough space to write 4 more thresholds back! + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveExtContactCoulomb(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + //We'll need this. +// const FloatV zero = FZero(); +// const FloatV one = FOne(); + + Vec3V linVel0, angVel0, linVel1, angVel1; + + if(desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + linVel0 = V3LoadA(desc.bodyA->linearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + } + + //const PxU8* PX_RESTRICT last = desc.constraint + desc.constraintLengthOver16*16; + + PxU8* PX_RESTRICT currPtr = desc.constraint; + + const SolverContactCoulombHeader* PX_RESTRICT firstHeader = reinterpret_cast(currPtr); + + const PxU8* PX_RESTRICT last = desc.constraint + firstHeader->frictionOffset; + + //hopefully pointer aliasing doesn't bite. + + Vec3V linImpulse0 = V3Zero(), linImpulse1 = V3Zero(), angImpulse0 = V3Zero(), angImpulse1 = V3Zero(); + + while(currPtr < last) + { + const SolverContactCoulombHeader* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactCoulombHeader); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + PxF32* appliedImpulse = reinterpret_cast(const_cast(((reinterpret_cast(hdr)) + hdr->frictionOffset + sizeof(SolverFrictionHeader)))); + Ps::prefetchLine(appliedImpulse); + + SolverContactPointExt* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPointExt); + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + const Vec3V normal = hdr->getNormal(); + + solveExtContacts(contacts, numNormalConstr, normal, linVel0, angVel0, linVel1, angVel1, li0, ai0, li1, ai1, appliedImpulse); + + linImpulse0 = V3ScaleAdd(li0, FLoad(hdr->dominance0), linImpulse0); + angImpulse0 = V3ScaleAdd(ai0, FLoad(hdr->angDom0), angImpulse0); + linImpulse1 = V3NegScaleSub(li1, FLoad(hdr->dominance1), linImpulse1); + angImpulse1 = V3NegScaleSub(ai1, FLoad(hdr->angDom1), angImpulse1); + } + + if(desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularState); + } + else + { + Cm::SpatialVectorF Z[64]; + Cm::SpatialVectorF deltaV[64]; + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, linImpulse0, + angImpulse0, Z, deltaV); + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularState); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, linImpulse1, + angImpulse1, cache.Z, cache.deltaV); + } + + PX_ASSERT(currPtr == last); +} + +void solveExtFriction(const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + Vec3V linVel0, angVel0, linVel1, angVel1; + + if(desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + linVel0 = V3LoadA(desc.bodyA->linearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularState); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + } + + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + const PxU8* PX_RESTRICT last = currPtr + desc.constraintLengthOver16*16; + + Vec3V linImpulse0 = V3Zero(), linImpulse1 = V3Zero(), angImpulse0 = V3Zero(), angImpulse1 = V3Zero(); + + while(currPtr < last) + { + + const SolverFrictionHeader* PX_RESTRICT frictionHeader = reinterpret_cast(currPtr); + currPtr += sizeof(SolverFrictionHeader); + PxF32* appliedImpulse = reinterpret_cast(currPtr); + currPtr += frictionHeader->getAppliedForcePaddingSize(); + + SolverContactFrictionExt* PX_RESTRICT frictions = reinterpret_cast(currPtr); + const PxU32 numFrictionConstr = frictionHeader->numFrictionConstr; + + currPtr += numFrictionConstr * sizeof(SolverContactFrictionExt); + const FloatV staticFriction = frictionHeader->getStaticFriction(); + + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + PxU32 numNormalConstr = frictionHeader->numNormalConstr; + PxU32 nbFrictionsPerPoint = numFrictionConstr/numNormalConstr; + + + + + for(PxU32 i = 0, j = 0; i < numFrictionConstr; j++) + { + for(PxU32 p=0;pinvMass0D0), linImpulse0); + angImpulse0 = V3ScaleAdd(ai0, FLoad(frictionHeader->angDom0), angImpulse0); + linImpulse1 = V3NegScaleSub(li1, FLoad(frictionHeader->invMass1D1), linImpulse1); + angImpulse1 = V3NegScaleSub(ai1, FLoad(frictionHeader->angDom1), angImpulse1); + } + + if(desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularState); + } + else + { + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, + linImpulse0, angImpulse0, cache.Z, cache.deltaV); + } + + if(desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularState); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, + linImpulse1, angImpulse1, cache.Z, cache.deltaV); + } + + PX_ASSERT(currPtr == last); + +} + +void solveExtFrictionBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtFriction(desc[a], cache); + } +} + +void solveExtFrictionConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtFriction(desc[a], cache); + } +} + +void solveExtFrictionBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtFriction(desc[a], cache); + } +} + + +void solveConcludeExtContactCoulomb (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveExtContactCoulomb(desc, cache); + concludeContactCoulomb(desc, cache); +} + +void solveExtContactCoulombBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtContactCoulomb(desc[a], cache); + } +} + +void solveExtContactCoulombConcludeBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + solveExtContactCoulomb(desc[a], cache); + concludeContactCoulomb(desc[a], cache); + } +} + +void solveExtContactCoulombBlockWriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 constraintCount, SolverContext& cache) +{ + for(PxU32 a = 0; a < constraintCount; ++a) + { + PxSolverBodyData& bd0 = cache.solverBodyArray[desc[a].linkIndexA != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyADataIndex]; + PxSolverBodyData& bd1 = cache.solverBodyArray[desc[a].linkIndexB != PxSolverConstraintDesc::NO_LINK ? 0 : desc[a].bodyBDataIndex]; + + solveExtContactCoulomb(desc[a], cache); + writeBackContactCoulomb(desc[a], cache, bd0, bd1); + } + if(cache.mThresholdStreamIndex > 0) + { + //Not enough space to write 4 more thresholds back! + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + + +void solveConcludeContactCoulomb (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveContactCoulomb(desc, cache); + concludeContactCoulomb(desc, cache); +} + + +void solveConcludeContactCoulomb_BStatic (const PxSolverConstraintDesc& desc, SolverContext& cache) +{ + solveContactCoulomb_BStatic(desc, cache); + concludeContactCoulomb(desc, cache); +} + + + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp new file mode 100644 index 000000000..65b28464e --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp @@ -0,0 +1,985 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxPreprocessor.h" +#include "PsVecMath.h" +#include "PsFPU.h" +#include "CmPhysXCommon.h" +#include "DySolverBody.h" +#include "DySolverContactPF4.h" +#include "DySolverConstraint1D.h" +#include "DySolverConstraintDesc.h" +#include "DyThresholdTable.h" +#include "DySolverContext.h" +#include "PsUtilities.h" +#include "DyConstraint.h" +#include "PsAtomic.h" +#include "DySolverContact.h" + +namespace physx +{ + +namespace Dy +{ + +static void solveContactCoulomb4_Block(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& /*cache*/) +{ + PxSolverBody& b00 = *desc[0].bodyA; + PxSolverBody& b01 = *desc[0].bodyB; + PxSolverBody& b10 = *desc[1].bodyA; + PxSolverBody& b11 = *desc[1].bodyB; + PxSolverBody& b20 = *desc[2].bodyA; + PxSolverBody& b21 = *desc[2].bodyB; + PxSolverBody& b30 = *desc[3].bodyA; + PxSolverBody& b31 = *desc[3].bodyB; + + //We'll need this. + const Vec4V vZero = V4Zero(); + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&b01.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularState.x); + Vec4V angState01 = V4LoadA(&b01.angularState.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&b11.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularState.x); + Vec4V angState11 = V4LoadA(&b11.angularState.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&b21.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularState.x); + Vec4V angState21 = V4LoadA(&b21.angularState.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&b31.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularState.x); + Vec4V angState31 = V4LoadA(&b31.angularState.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2, linVel0T3; + Vec4V linVel1T0, linVel1T1, linVel1T2, linVel1T3; + Vec4V angState0T0, angState0T1, angState0T2, angState0T3; + Vec4V angState1T0, angState1T1, angState1T2, angState1T3; + + + PX_TRANSPOSE_44(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2, linVel0T3); + PX_TRANSPOSE_44(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2, linVel1T3); + PX_TRANSPOSE_44(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2, angState0T3); + PX_TRANSPOSE_44(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2, angState1T3); + + + + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + SolverContactCoulombHeader4* PX_RESTRICT firstHeader = reinterpret_cast(currPtr); + + const PxU8* PX_RESTRICT last = desc[0].constraint + firstHeader->frictionOffset; + + //const PxU8* PX_RESTRICT endPtr = desc[0].constraint + getConstraintLength(desc[0]); + + + //TODO - can I avoid this many tests??? + while(currPtr < last) + { + + SolverContactCoulombHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + Vec4V* appliedForceBuffer = reinterpret_cast(currPtr + hdr->frictionOffset + sizeof(SolverFrictionHeader4)); + + //PX_ASSERT((PxU8*)appliedForceBuffer < endPtr); + + currPtr = reinterpret_cast(hdr + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + SolverContact4Dynamic* PX_RESTRICT contacts = reinterpret_cast(currPtr); + //const Vec4V dominance1 = V4Neg(__dominance1); + + currPtr = reinterpret_cast(contacts + numNormalConstr); + + const Vec4V invMass0D0 = hdr->invMassADom; + const Vec4V invMass1D1 = hdr->invMassBDom; + const Vec4V angD0 = hdr->angD0; + const Vec4V angD1 = hdr->angD1; + + const Vec4V normalT0 = hdr->normalX; + const Vec4V normalT1 = hdr->normalY; + const Vec4V normalT2 = hdr->normalZ; + + const Vec4V __normalVel1 = V4Mul(linVel0T0, normalT0); + const Vec4V __normalVel3 = V4Mul(linVel1T0, normalT0); + const Vec4V _normalVel1 = V4MulAdd(linVel0T1, normalT1, __normalVel1); + const Vec4V _normalVel3 = V4MulAdd(linVel1T1, normalT1, __normalVel3); + + Vec4V normalVel1 = V4MulAdd(linVel0T2, normalT2, _normalVel1); + Vec4V normalVel3 = V4MulAdd(linVel1T2, normalT2, _normalVel3); + + Vec4V accumDeltaF = vZero; + + for(PxU32 i=0;i(currPtr); + + const PxU8* PX_RESTRICT last = desc[0].constraint + firstHeader->frictionOffset; + + + //TODO - can I avoid this many tests??? + while(currPtr < last) + { + + SolverContactCoulombHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + Vec4V* appliedForceBuffer = reinterpret_cast(currPtr + hdr->frictionOffset + sizeof(SolverFrictionHeader4)); + + currPtr = reinterpret_cast(hdr + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + SolverContact4Base* PX_RESTRICT contacts = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(contacts + numNormalConstr); + + const Vec4V invMass0D0 = hdr->invMassADom; + const Vec4V angD0 = hdr->angD0; + + const Vec4V normalT0 = hdr->normalX; + const Vec4V normalT1 = hdr->normalY; + const Vec4V normalT2 = hdr->normalZ; + + const Vec4V __normalVel1 = V4Mul(linVel0T0, normalT0); + const Vec4V _normalVel1 = V4MulAdd(linVel0T1, normalT1, __normalVel1); + + Vec4V normalVel1 = V4MulAdd(linVel0T2, normalT2, _normalVel1); + + Vec4V accumDeltaF = vZero; + + for(PxU32 i=0;i(currPtr); + + currPtr = reinterpret_cast(hdr + 1); + + Vec4V* appliedImpulses = reinterpret_cast(currPtr); + + currPtr += hdr->numNormalConstr * sizeof(Vec4V); + + Ps::prefetchLine(currPtr, 128); + Ps::prefetchLine(currPtr,256); + Ps::prefetchLine(currPtr,384); + + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverFriction4Dynamic* PX_RESTRICT frictions = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(frictions + hdr->numFrictionConstr); + + const PxU32 maxFrictionConstr = numFrictionConstr; + + const Vec4V staticFric = hdr->staticFriction; + + const Vec4V invMass0D0 = hdr->invMassADom; + const Vec4V invMass1D1 = hdr->invMassBDom; + + const Vec4V angD0 = hdr->angD0; + const Vec4V angD1 = hdr->angD1; + + for(PxU32 i=0;i>hdr->frictionPerContact]; + + const Vec4V maxFriction = V4Mul(staticFric, appliedImpulse); + + const Vec4V nMaxFriction = V4Neg(maxFriction); + + const Vec4V normalX = f.normalX; + const Vec4V normalY = f.normalY; + const Vec4V normalZ = f.normalZ; + + const Vec4V raXnX = f.raXnX; + const Vec4V raXnY = f.raXnY; + const Vec4V raXnZ = f.raXnZ; + + const Vec4V rbXnX = f.rbXnX; + const Vec4V rbXnY = f.rbXnY; + const Vec4V rbXnZ = f.rbXnZ; + + const Vec4V appliedForce(f.appliedForce); + const Vec4V velMultiplier(f.velMultiplier); + const Vec4V targetVel(f.targetVelocity); + + //4 x 4 Dot3 products encoded as 8 M44 transposes, 4 MulV and 8 MulAdd ops + + const Vec4V __normalVel1 = V4Mul(linVel0T0, normalX); + const Vec4V __normalVel2 = V4Mul(raXnX, angState0T0); + const Vec4V __normalVel3 = V4Mul(linVel1T0, normalX); + const Vec4V __normalVel4 = V4Mul(rbXnX, angState1T0); + + const Vec4V _normalVel1 = V4MulAdd(linVel0T1, normalY, __normalVel1); + const Vec4V _normalVel2 = V4MulAdd(raXnY, angState0T1, __normalVel2); + const Vec4V _normalVel3 = V4MulAdd(linVel1T1, normalY, __normalVel3); + const Vec4V _normalVel4 = V4MulAdd(rbXnY, angState1T1, __normalVel4); + + const Vec4V normalVel1 = V4MulAdd(linVel0T2, normalZ, _normalVel1); + const Vec4V normalVel2 = V4MulAdd(raXnZ, angState0T2, _normalVel2); + const Vec4V normalVel3 = V4MulAdd(linVel1T2, normalZ, _normalVel3); + const Vec4V normalVel4 = V4MulAdd(rbXnZ, angState1T2, _normalVel4); + + + const Vec4V _normalVel = V4Add(normalVel1, normalVel2); + const Vec4V __normalVel = V4Add(normalVel3, normalVel4); + + const Vec4V normalVel = V4Sub(_normalVel, __normalVel ); + + const Vec4V tmp = V4NegMulSub(targetVel, velMultiplier, appliedForce); + Vec4V newAppliedForce = V4MulAdd(normalVel, velMultiplier, tmp); + newAppliedForce = V4Clamp(newAppliedForce,nMaxFriction, maxFriction); + const Vec4V deltaF = V4Sub(newAppliedForce, appliedForce); + + const Vec4V deltaLinF0 = V4Mul(invMass0D0, deltaF); + const Vec4V deltaLinF1 = V4Mul(invMass1D1, deltaF); + + const Vec4V deltaAngF0 = V4Mul(angD0, deltaF); + const Vec4V deltaAngF1 = V4Mul(angD1, deltaF); + + + linVel0T0 = V4MulAdd(normalX, deltaLinF0, linVel0T0); + linVel1T0 = V4NegMulSub(normalX, deltaLinF1, linVel1T0); + angState0T0 = V4MulAdd(raXnX, deltaAngF0, angState0T0); + angState1T0 = V4NegMulSub(rbXnX, deltaAngF1, angState1T0); + + linVel0T1 = V4MulAdd(normalY, deltaLinF0, linVel0T1); + linVel1T1 = V4NegMulSub(normalY, deltaLinF1, linVel1T1); + angState0T1 = V4MulAdd(raXnY, deltaAngF0, angState0T1); + angState1T1 = V4NegMulSub(rbXnY, deltaAngF1, angState1T1); + + linVel0T2 = V4MulAdd(normalZ, deltaLinF0, linVel0T2); + linVel1T2 = V4NegMulSub(normalZ, deltaLinF1, linVel1T2); + angState0T2 = V4MulAdd(raXnZ, deltaAngF0, angState0T2); + angState1T2 = V4NegMulSub(rbXnZ, deltaAngF1, angState1T2); + + f.appliedForce = newAppliedForce; + } + } + + PX_ASSERT(currPtr == endPtr); + + //KS - we need to use PX_TRANSPOSE_44 here instead of the 34_43 variants because the W components are being used to + //store the bodies' progress counters. + + PX_TRANSPOSE_44(linVel0T0, linVel0T1, linVel0T2, linVel0T3, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_44(linVel1T0, linVel1T1, linVel1T2, linVel1T3, linVel01, linVel11, linVel21, linVel31); + PX_TRANSPOSE_44(angState0T0, angState0T1, angState0T2, angState0T3, angState00, angState10, angState20, angState30); + PX_TRANSPOSE_44(angState1T0, angState1T1, angState1T2, angState1T3, angState01, angState11, angState21, angState31); + + + // Write back + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + + V4StoreA(linVel01, &b01.linearVelocity.x); + V4StoreA(linVel11, &b11.linearVelocity.x); + V4StoreA(linVel21, &b21.linearVelocity.x); + V4StoreA(linVel31, &b31.linearVelocity.x); + + V4StoreA(angState00, &b00.angularState.x); + V4StoreA(angState10, &b10.angularState.x); + V4StoreA(angState20, &b20.angularState.x); + V4StoreA(angState30, &b30.angularState.x); + + V4StoreA(angState01, &b01.angularState.x); + V4StoreA(angState11, &b11.angularState.x); + V4StoreA(angState21, &b21.angularState.x); + V4StoreA(angState31, &b31.angularState.x); + +} + + +static void solveFriction4_StaticBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, SolverContext& /*cache*/) +{ + + PxSolverBody& b00 = *desc[0].bodyA; + PxSolverBody& b10 = *desc[1].bodyA; + PxSolverBody& b20 = *desc[2].bodyA; + PxSolverBody& b30 = *desc[3].bodyA; + + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularState.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularState.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularState.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularState.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2, linVel0T3; + Vec4V angState0T0, angState0T1, angState0T2, angState0T3; + + + PX_TRANSPOSE_44(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2, linVel0T3); + PX_TRANSPOSE_44(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2, angState0T3); + + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + PxU8* PX_RESTRICT endPtr = desc[0].constraint + getConstraintLength(desc[0]); + + + while(currPtr < endPtr) + { + SolverFrictionHeader4* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(hdr + 1); + + Vec4V* appliedImpulses = reinterpret_cast(currPtr); + + currPtr += hdr->numNormalConstr * sizeof(Vec4V); + + Ps::prefetchLine(currPtr, 128); + Ps::prefetchLine(currPtr,256); + Ps::prefetchLine(currPtr,384); + + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverFriction4Base* PX_RESTRICT frictions = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(frictions + hdr->numFrictionConstr); + + const PxU32 maxFrictionConstr = numFrictionConstr; + + const Vec4V staticFric = hdr->staticFriction; + + const Vec4V invMass0D0 = hdr->invMassADom; + const Vec4V angD0 = hdr->angD0; + + for(PxU32 i=0;i>hdr->frictionPerContact]; + + const Vec4V maxFriction = V4Mul(staticFric, appliedImpulse); + + const Vec4V nMaxFriction = V4Neg(maxFriction); + + const Vec4V normalX = f.normalX; + const Vec4V normalY = f.normalY; + const Vec4V normalZ = f.normalZ; + + const Vec4V raXnX = f.raXnX; + const Vec4V raXnY = f.raXnY; + const Vec4V raXnZ = f.raXnZ; + + const Vec4V appliedForce(f.appliedForce); + const Vec4V velMultiplier(f.velMultiplier); + const Vec4V targetVel(f.targetVelocity); + + //4 x 4 Dot3 products encoded as 8 M44 transposes, 4 MulV and 8 MulAdd ops + + const Vec4V __normalVel1 = V4Mul(linVel0T0, normalX); + const Vec4V __normalVel2 = V4Mul(raXnX, angState0T0); + + const Vec4V _normalVel1 = V4MulAdd(linVel0T1, normalY, __normalVel1); + const Vec4V _normalVel2 = V4MulAdd(raXnY, angState0T1, __normalVel2); + + const Vec4V normalVel1 = V4MulAdd(linVel0T2, normalZ, _normalVel1); + const Vec4V normalVel2 = V4MulAdd(raXnZ, angState0T2, _normalVel2); + + const Vec4V delLinVel00 = V4Mul(normalX, invMass0D0); + + const Vec4V delLinVel10 = V4Mul(normalY, invMass0D0); + + const Vec4V normalVel = V4Add(normalVel1, normalVel2); + + const Vec4V delLinVel20 = V4Mul(normalZ, invMass0D0); + + const Vec4V tmp = V4NegMulSub(targetVel, velMultiplier, appliedForce); + + Vec4V newAppliedForce = V4MulAdd(normalVel, velMultiplier, tmp); + newAppliedForce = V4Clamp(newAppliedForce,nMaxFriction, maxFriction); + const Vec4V deltaF = V4Sub(newAppliedForce, appliedForce); + + const Vec4V deltaAngF0 = V4Mul(angD0, deltaF); + + linVel0T0 = V4MulAdd(delLinVel00, deltaF, linVel0T0); + angState0T0 = V4MulAdd(raXnX, deltaAngF0, angState0T0); + + linVel0T1 = V4MulAdd(delLinVel10, deltaF, linVel0T1); + angState0T1 = V4MulAdd(raXnY, deltaAngF0, angState0T1); + + linVel0T2 = V4MulAdd(delLinVel20, deltaF, linVel0T2); + angState0T2 = V4MulAdd(raXnZ, deltaAngF0, angState0T2); + + f.appliedForce = newAppliedForce; + } + } + + PX_ASSERT(currPtr == endPtr); + + //KS - we need to use PX_TRANSPOSE_44 here instead of the 34_43 variants because the W components are being used to + //store the bodies' progress counters. + + PX_TRANSPOSE_44(linVel0T0, linVel0T1, linVel0T2, linVel0T3, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_44(angState0T0, angState0T1, angState0T2, angState0T3, angState00, angState10, angState20, angState30); + + // Write back + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + + V4StoreA(angState00, &b00.angularState.x); + V4StoreA(angState10, &b10.angularState.x); + V4StoreA(angState20, &b20.angularState.x); + V4StoreA(angState30, &b30.angularState.x); +} + +static void concludeContactCoulomb4(const PxSolverConstraintDesc* desc, SolverContext& /*cache*/) +{ + PxU8* PX_RESTRICT cPtr = desc[0].constraint; + + const Vec4V zero = V4Zero(); + + const SolverContactCoulombHeader4* PX_RESTRICT firstHeader = reinterpret_cast(cPtr); + PxU8* PX_RESTRICT last = desc[0].constraint + firstHeader->frictionOffset; + + PxU32 pointStride = firstHeader->type == DY_SC_TYPE_BLOCK_RB_CONTACT ? sizeof(SolverContact4Dynamic) : sizeof(SolverContact4Base); + + while(cPtr < last) + { + const SolverContactCoulombHeader4* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactCoulombHeader4); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + //if(cPtr < last) + //Ps::prefetchLine(cPtr, 512); + Ps::prefetchLine(cPtr,128); + Ps::prefetchLine(cPtr,256); + Ps::prefetchLine(cPtr,384); + + for(PxU32 i=0;i(cPtr); + cPtr += pointStride; + c->scaledBias = V4Max(c->scaledBias, zero); + } + } + PX_ASSERT(cPtr == last); +} + +void writeBackContactCoulomb4(const PxSolverConstraintDesc* desc, SolverContext& cache, + const PxSolverBodyData** PX_RESTRICT bd0, const PxSolverBodyData** PX_RESTRICT bd1) +{ + Vec4V normalForceV = V4Zero(); + PxU8* PX_RESTRICT cPtr = desc[0].constraint; + PxReal* PX_RESTRICT vForceWriteback0 = reinterpret_cast(desc[0].writeBack); + PxReal* PX_RESTRICT vForceWriteback1 = reinterpret_cast(desc[1].writeBack); + PxReal* PX_RESTRICT vForceWriteback2 = reinterpret_cast(desc[2].writeBack); + PxReal* PX_RESTRICT vForceWriteback3 = reinterpret_cast(desc[3].writeBack); + + const SolverContactCoulombHeader4* PX_RESTRICT firstHeader = reinterpret_cast(cPtr); + PxU8* PX_RESTRICT last = desc[0].constraint + firstHeader->frictionOffset; + + const PxU32 pointStride = firstHeader->type == DY_SC_TYPE_BLOCK_RB_CONTACT ? sizeof(SolverContact4Dynamic) + : sizeof(SolverContact4Base); + + bool writeBackThresholds[4] = {false, false, false, false}; + + + while(cPtr < last) + { + const SolverContactCoulombHeader4* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactCoulombHeader4); + + writeBackThresholds[0] = hdr->flags[0] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[1] = hdr->flags[1] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[2] = hdr->flags[2] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[3] = hdr->flags[3] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + + const PxU32 numNormalConstr = hdr->numNormalConstr; + + Ps::prefetchLine(cPtr, 256); + Ps::prefetchLine(cPtr, 384); + + + for(PxU32 i=0; i(cPtr); + cPtr += pointStride; + + const Vec4V appliedForce = c->appliedForce; + if(vForceWriteback0 && i < hdr->numNormalConstr0) + FStore(V4GetX(appliedForce), vForceWriteback0++); + if(vForceWriteback1 && i < hdr->numNormalConstr1) + FStore(V4GetY(appliedForce), vForceWriteback1++); + if(vForceWriteback2 && i < hdr->numNormalConstr2) + FStore(V4GetZ(appliedForce), vForceWriteback2++); + if(vForceWriteback3 && i < hdr->numNormalConstr3) + FStore(V4GetW(appliedForce), vForceWriteback3++); + + normalForceV = V4Add(normalForceV, appliedForce); + } + } + PX_ASSERT(cPtr == last); + + PX_ALIGN(16, PxReal nf[4]); + V4StoreA(normalForceV, nf); + + //all constraint pointer in descs are the same constraint + Sc::ShapeInteraction** shapeInteractions = reinterpret_cast(desc[0].constraint)->shapeInteraction; + + for(PxU32 a = 0; a < 4; ++a) + { + if(writeBackThresholds[a] && desc[a].linkIndexA == PxSolverConstraintDesc::NO_LINK && desc[a].linkIndexB == PxSolverConstraintDesc::NO_LINK && + nf[a] !=0.f && (bd0[a]->reportThreshold < PX_MAX_REAL || bd1[a]->reportThreshold < PX_MAX_REAL)) + { + ThresholdStreamElement elt; + elt.normalForce = nf[a]; + elt.threshold = PxMin(bd0[a]->reportThreshold, bd1[a]->reportThreshold); + elt.nodeIndexA = IG::NodeIndex(bd0[a]->nodeIndex); + elt.nodeIndexB = IG::NodeIndex(bd1[a]->nodeIndex); + elt.shapeInteraction = shapeInteractions[a]; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + PX_ASSERT(cache.mThresholdStreamIndex (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveContactCoulombPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveContactCoulomb4_StaticBlock(desc, cache); + const PxSolverBodyData* bd0[4] = { &cache.solverBodyArray[desc[0].bodyADataIndex], + &cache.solverBodyArray[desc[1].bodyADataIndex], + &cache.solverBodyArray[desc[2].bodyADataIndex], + &cache.solverBodyArray[desc[3].bodyADataIndex]}; + + const PxSolverBodyData* bd1[4] = { &cache.solverBodyArray[desc[0].bodyBDataIndex], + &cache.solverBodyArray[desc[1].bodyBDataIndex], + &cache.solverBodyArray[desc[2].bodyBDataIndex], + &cache.solverBodyArray[desc[3].bodyBDataIndex]}; + + writeBackContactCoulomb4(desc, cache, bd0, bd1); + + if(cache.mThresholdStreamIndex > (cache.mThresholdStreamLength - 4)) + { + //Write back to global buffer + PxI32 threshIndex = physx::shdfnd::atomicAdd(cache.mSharedOutThresholdPairs, PxI32(cache.mThresholdStreamIndex)) - PxI32(cache.mThresholdStreamIndex); + for(PxU32 a = 0; a < cache.mThresholdStreamIndex; ++a) + { + cache.mSharedThresholdStream[a + threshIndex] = cache.mThresholdStream[a]; + } + cache.mThresholdStreamIndex = 0; + } +} + +void solveFrictionCoulombPreBlock(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_Block(desc, cache); +} + +void solveFrictionCoulombPreBlock_Static(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_StaticBlock(desc, cache); +} + +void solveFrictionCoulombPreBlock_Conclude(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_Block(desc, cache); +} + +void solveFrictionCoulombPreBlock_ConcludeStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_StaticBlock(desc, cache); +} + +void solveFrictionCoulombPreBlock_WriteBack(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_Block(desc, cache); +} + +void solveFrictionCoulombPreBlock_WriteBackStatic(const PxSolverConstraintDesc* PX_RESTRICT desc, const PxU32 /*constraintCount*/, SolverContext& cache) +{ + solveFriction4_StaticBlock(desc, cache); +} + + +} + +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h b/src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h new file mode 100644 index 000000000..0196bdb97 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_SPATIAL_H +#define DY_SPATIAL_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "PsMathUtils.h" +#include "CmSpatialVector.h" + +namespace physx +{ +namespace Dy +{ +// translate a motion resolved at position p to the origin + + +// should have a 'from' frame and a 'to' frame +class SpInertia +{ +public: + SpInertia() {} + + SpInertia(const PxMat33& ll, const PxMat33& la, const PxMat33& aa): mLL(ll), mLA(la), mAA(aa) + { + } + + static SpInertia getZero() + { + return SpInertia(PxMat33(PxZero), PxMat33(PxZero), + PxMat33(PxZero)); + } + + static SpInertia dyad(const Cm::SpatialVector& column, const Cm::SpatialVector& row) + { + return SpInertia(dyad(column.linear, row.linear), + dyad(column.linear, row.angular), + dyad(column.angular, row.angular)); + } + + + static SpInertia inertia(PxReal mass, const PxVec3& inertia) + { + return SpInertia(PxMat33::createDiagonal(PxVec3(mass,mass,mass)), PxMat33(PxZero), + PxMat33::createDiagonal(inertia)); + } + + + SpInertia operator+(const SpInertia& m) const + { + return SpInertia(mLL+m.mLL, mLA+m.mLA, mAA+m.mAA); + } + + SpInertia operator-(const SpInertia& m) const + { + return SpInertia(mLL-m.mLL, mLA-m.mLA, mAA-m.mAA); + } + + SpInertia operator*(PxReal r) const + { + return SpInertia(mLL*r, mLA*r, mAA*r); + } + + void operator+=(const SpInertia& m) + { + mLL+=m.mLL; + mLA+=m.mLA; + mAA+=m.mAA; + } + + void operator-=(const SpInertia& m) + { + mLL-=m.mLL; + mLA-=m.mLA; + mAA-=m.mAA; + } + + + PX_FORCE_INLINE Cm::SpatialVector operator *(const Cm::SpatialVector& v) const + { + return Cm::SpatialVector(mLL*v.linear +mLA*v.angular, + mLA.transformTranspose(v.linear)+mAA*v.angular); + } + + SpInertia operator *(const SpInertia& v) const + { + return SpInertia(mLL*v.mLL + mLA * v.mLA.getTranspose(), + mLL*v.mLA + mLA * v.mAA, + mLA.getTranspose()*v.mLA + mAA * v.mAA); + } + + + bool isFinite() const + { + return true; +// return mLL.isFinite() && mLA.isFinite() && mAA.isFinite(); + } + + PxMat33 mLL, mLA; // linear force from angular motion, linear force from linear motion + PxMat33 mAA; // angular force from angular motion, mAL = mLA.transpose() + +private: + static PxMat33 dyad(PxVec3 col, PxVec3 row) + { + return PxMat33(col*row.x, col*row.y, col*row.z); + } + + +}; + +} +} + +#endif //DY_SPATIAL_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp new file mode 100644 index 000000000..ee854ae3b --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp @@ -0,0 +1,3381 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "PxSceneDesc.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContact4.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DyDynamics.h" +#include "DyArticulationContactPrep.h" +#include "PxsContactManager.h" +#include "PsFoundation.h" +#include "DyFeatherstoneArticulation.h" +#include "DyFeatherstoneArticulationLink.h" +#include "DyFeatherstoneArticulationJointData.h" + +using namespace physx; +using namespace Gu; + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DyContactPrepShared.h" +#include "DySolverConstraint1D.h" +#include "DyConstraintPrep.h" +#include "DyTGSDynamics.h" + +#include "CmConeLimitHelper.h" + +#include "DySolverContext.h" +#include "DySolverConstraint1DStep.h" + +using namespace Ps::aos; + + +namespace physx +{ +namespace Dy +{ + PX_FORCE_INLINE PxReal safeRecip(const PxReal x) + { + return x > PX_EPS_F32 ? 1.f/x : 0.f; + } + + PX_FORCE_INLINE void computeBlockStreamByteSizesStep(const bool useExtContacts, const CorrelationBuffer& c, + PxU32& _solverConstraintByteSize, PxU32& _frictionPatchByteSize, PxU32& _numFrictionPatches, + PxU32& _axisConstraintCount, PxReal torsionalPatchRadius) + { + PX_ASSERT(0 == _solverConstraintByteSize); + PX_ASSERT(0 == _frictionPatchByteSize); + PX_ASSERT(0 == _numFrictionPatches); + PX_ASSERT(0 == _axisConstraintCount); + + // PT: use local vars to remove LHS + PxU32 solverConstraintByteSize = 0; + PxU32 numFrictionPatches = 0; + PxU32 axisConstraintCount = 0; + + + for (PxU32 i = 0; i < c.frictionPatchCount; i++) + { + //Friction patches. + if (c.correlationListHeads[i] != CorrelationBuffer::LIST_END) + numFrictionPatches++; + + const FrictionPatch& frictionPatch = c.frictionPatches[i]; + + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0; + + //Solver constraint data. + if (c.frictionPatchContactCounts[i] != 0) + { + solverConstraintByteSize += sizeof(SolverContactHeaderStep); + solverConstraintByteSize += useExtContacts ? c.frictionPatchContactCounts[i] * sizeof(SolverContactPointStepExt) + : c.frictionPatchContactCounts[i] * sizeof(SolverContactPointStep); + solverConstraintByteSize += sizeof(PxF32) * ((c.frictionPatchContactCounts[i] + 3)&(~3)); //Add on space for applied impulses + + axisConstraintCount += c.frictionPatchContactCounts[i]; + + if (haveFriction) + { + PxU32 nbAnchors = PxU32(c.frictionPatches[i].anchorCount * 2); + if (torsionalPatchRadius > 0.f && c.frictionPatches[i].anchorCount == 1) + nbAnchors++; + solverConstraintByteSize += useExtContacts ? nbAnchors * sizeof(SolverContactFrictionStepExt) + : nbAnchors * sizeof(SolverContactFrictionStep); + axisConstraintCount += nbAnchors; + + } + } + } + PxU32 frictionPatchByteSize = numFrictionPatches*sizeof(FrictionPatch); + + _numFrictionPatches = numFrictionPatches; + _axisConstraintCount = axisConstraintCount; + + //16-byte alignment. + _frictionPatchByteSize = ((frictionPatchByteSize + 0x0f) & ~0x0f); + _solverConstraintByteSize = ((solverConstraintByteSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); + PX_ASSERT(0 == (_frictionPatchByteSize & 0x0f)); + } + + static bool reserveBlockStreams(const bool useExtContacts, Dy::CorrelationBuffer& cBuffer, + PxU8*& solverConstraint, + FrictionPatch*& _frictionPatches, + PxU32& numFrictionPatches, PxU32& solverConstraintByteSize, + PxU32& axisConstraintCount, PxConstraintAllocator& constraintAllocator, + PxReal torsionalPatchRadius) + { + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(NULL == _frictionPatches); + PX_ASSERT(0 == numFrictionPatches); + PX_ASSERT(0 == solverConstraintByteSize); + PX_ASSERT(0 == axisConstraintCount); + + //From frictionPatchStream we just need to reserve a single buffer. + PxU32 frictionPatchByteSize = 0; + //Compute the sizes of all the buffers. + computeBlockStreamByteSizesStep( + useExtContacts, cBuffer, + solverConstraintByteSize, frictionPatchByteSize, numFrictionPatches, + axisConstraintCount, torsionalPatchRadius); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if (constraintBlockByteSize > 0) + { + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if (0 == constraintBlock || (reinterpret_cast(-1)) == constraintBlock) + { + if (0 == constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock = NULL; + } + } + PX_ASSERT((size_t(constraintBlock) & 0xF) == 0); + } + + FrictionPatch* frictionPatches = NULL; + //If the constraint block reservation didn't fail then reserve the friction buffer too. + if (frictionPatchByteSize > 0 && (0 == constraintBlockByteSize || constraintBlock)) + { + frictionPatches = reinterpret_cast(constraintAllocator.reserveFrictionData(frictionPatchByteSize)); + + if (0 == frictionPatches || (reinterpret_cast(-1)) == frictionPatches) + { + if (0 == frictionPatches) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of friction data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + frictionPatches = NULL; + } + } + } + + _frictionPatches = frictionPatches; + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if (0 == constraintBlockByteSize || constraintBlock) + { + if (solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0 == (uintptr_t(solverConstraint) & 0x0f)); + } + } + + //Return true if neither of the two block reservations failed. + return ((0 == constraintBlockByteSize || constraintBlock) && (0 == frictionPatchByteSize || frictionPatches)); + } + + class SolverExtBodyStep + { + public: + union + { + const ArticulationV* mArticulation; + const PxTGSSolverBodyVel* mBody; + }; + + const PxTGSSolverBodyTxInertia* mTxI; + const PxStepSolverBodyData* mData; + + PxU16 mLinkIndex; + + SolverExtBodyStep(const void* bodyOrArticulation, const PxTGSSolverBodyTxInertia* txI, + const PxStepSolverBodyData* data, PxU16 linkIndex) : + mBody(reinterpret_cast(bodyOrArticulation)), + mTxI(txI), + mData(data), + mLinkIndex(linkIndex) + {} + + PxReal projectVelocity(const PxVec3& linear, const PxVec3& angular) const; + PxVec3 getLinVel() const; + PxVec3 getAngVel() const; + bool isKinematic() const + { + return (mLinkIndex == PxSolverConstraintDesc::NO_LINK) && + mBody->isKinematic; + } + }; + + Cm::SpatialVector createImpulseResponseVector(const PxVec3& linear, const PxVec3& angular, const SolverExtBodyStep& body) + { + if (body.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + return Cm::SpatialVector(linear, body.mTxI->sqrtInvInertia * angular); + } + return Cm::SpatialVector(linear, angular); + } + + + PxReal getImpulseResponse(const SolverExtBodyStep& b0, const Cm::SpatialVector& impulse0, Cm::SpatialVector& deltaV0, PxReal dom0, PxReal angDom0, + const SolverExtBodyStep& b1, const Cm::SpatialVector& impulse1, Cm::SpatialVector& deltaV1, PxReal dom1, PxReal angDom1, + bool allowSelfCollision) + { + PxReal response; + if (allowSelfCollision && b0.mArticulation == b1.mArticulation) + { + Cm::SpatialVectorF Z[64]; + b0.mArticulation->getImpulseSelfResponse(b0.mLinkIndex, b1.mLinkIndex, Z, + impulse0.scale(dom0, angDom0), impulse1.scale(dom1, angDom1), deltaV0, deltaV1); + + response = impulse0.dot(deltaV0) + impulse1.dot(deltaV1); + } + else + { + + if (b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + deltaV0.linear = impulse0.linear * b0.mData->invMass * dom0; + deltaV0.angular =/* b0.mBody->sqrtInvInertia * */impulse0.angular * angDom0; + } + else + { + //ArticulationHelper::getImpulseResponse(*b0.mFsData, b0.mLinkIndex, impulse0.scale(dom0, angDom0), deltaV0); + const ArticulationV* articulation = b0.mArticulation; + Cm::SpatialVectorF Z[64]; + articulation->getImpulseResponse(b0.mLinkIndex, Z, impulse0.scale(dom0, angDom0), deltaV0); + } + + response = impulse0.dot(deltaV0); + if (b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + deltaV1.linear = impulse1.linear * b1.mData->invMass * dom1; + deltaV1.angular = /*b1.mBody->sqrtInvInertia * */impulse1.angular * angDom1; + } + else + { + const ArticulationV* articulation = b1.mArticulation; + Cm::SpatialVectorF Z[64]; + articulation->getImpulseResponse(b1.mLinkIndex, Z, impulse1.scale(dom1, angDom1), deltaV1); + //ArticulationHelper::getImpulseResponse(*b1.mFsData, b1.mLinkIndex, impulse1.scale(dom1, angDom1), deltaV1); + + } + response += impulse1.dot(deltaV1); + } + + return response; + } + + + PxReal SolverExtBodyStep::projectVelocity(const PxVec3& linear, const PxVec3& angular) const + { + if (mLinkIndex == PxSolverConstraintDesc::NO_LINK) + { + return mData->projectVelocity(linear, angular); + } + else + { + Cm::SpatialVectorV velocities = mArticulation->getLinkVelocity(mLinkIndex); + FloatV fv = velocities.dot(Cm::SpatialVector(linear, angular)); + PxF32 f; + FStore(fv, &f); + /*PxF32 f; + FStore(getVelocity(*mFsData)[mLinkIndex].dot(Cm::SpatialVector(linear, angular)), &f); + return f;*/ + return f; + } + } + + + + static FloatV constructContactConstraintStep(const Mat33V& sqrtInvInertia0, const Mat33V& sqrtInvInertia1, const FloatVArg invMassNorLenSq0, + const FloatVArg invMassNorLenSq1, const FloatVArg angD0, const FloatVArg angD1, const Vec3VArg bodyFrame0p, const Vec3VArg bodyFrame1p, + const QuatV& /*bodyFrame0q*/, const QuatV& /*bodyFrame1q*/, + const Vec3VArg normal, const FloatVArg norVel0, const FloatVArg norVel1, const VecCrossV& norCross, const Vec3VArg angVel0, const Vec3VArg angVel1, + const FloatVArg invDt, const FloatVArg invDtp8, const FloatVArg restDistance, const FloatVArg restitution, + const FloatVArg bounceThreshold, const Gu::ContactPoint& contact, SolverContactPointStep& solverContact, + const FloatVArg /*ccdMaxSeparation*/, const bool isKinematic0, const bool isKinematic1) + { + const FloatV zero = FZero(); + const Vec3V point = V3LoadA(contact.point); + const FloatV separation = FLoad(contact.separation); + + const FloatV cTargetVel = V3Dot(normal, V3LoadA(contact.targetVel)); + + const Vec3V ra = V3Sub(point, bodyFrame0p); + const Vec3V rb = V3Sub(point, bodyFrame1p); + + const Vec3V raXn = V3Cross(ra, norCross); + const Vec3V rbXn = V3Cross(rb, norCross); + + const Vec3V raXnInertia = M33MulV3(sqrtInvInertia0, raXn); + const Vec3V rbXnInertia = M33MulV3(sqrtInvInertia1, rbXn); + + const FloatV resp0 = FAdd(invMassNorLenSq0, FMul(V3Dot(raXnInertia, raXnInertia), angD0)); + const FloatV resp1 = FSub(FMul(V3Dot(rbXnInertia, rbXnInertia), angD1), invMassNorLenSq1); + + const FloatV unitResponse = FAdd(resp0, resp1); + + const FloatV vrel1 = FAdd(norVel0, V3Dot(raXn, angVel0)); + const FloatV vrel2 = FAdd(norVel1, V3Dot(rbXn, angVel1)); + const FloatV vrel = FSub(vrel1, vrel2); + + const FloatV penetration = FSub(separation, restDistance); + + const FloatV penetrationInvDt = FMul(penetration, invDt); + + FloatV scaledBias = FNeg(invDtp8); + + const BoolV isGreater2 = BAnd(BAnd(FIsGrtr(restitution, zero), FIsGrtr(bounceThreshold, vrel)), FIsGrtr(FNeg(vrel), penetrationInvDt)); + + const FloatV totalError = penetration; + + const FloatV sumVRel(vrel); + + const FloatV velMultiplier = FSel(FIsGrtr(unitResponse, FZero()), FRecip(unitResponse), FZero()); + + FloatV targetVelocity = FAdd(cTargetVel, FSel(isGreater2, FMul(FNeg(sumVRel), restitution), zero)); + + if(isKinematic0) + targetVelocity = FSub(targetVelocity, vrel1); + if(isKinematic1) + targetVelocity = FAdd(targetVelocity, vrel2); + + //KS - don't store scaled by angD0/angD1 here! It'll corrupt the velocity projection... + V3StoreA(raXnInertia, solverContact.raXnI); + V3StoreA(rbXnInertia, solverContact.rbXnI); + + FStore(velMultiplier, &solverContact.velMultiplier); + + FStore(totalError, &solverContact.separation); + FStore(scaledBias, &solverContact.biasCoefficient); + FStore(targetVelocity, &solverContact.targetVelocity); + + return penetration; + + } + + + static void setupFinalizeSolverConstraints(Sc::ShapeInteraction* shapeInteraction, + const ContactPoint* buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const PxTGSSolverBodyVel& b0, + const PxTGSSolverBodyVel& b1, + const PxTGSSolverBodyTxInertia& txI0, + const PxTGSSolverBodyTxInertia& txI1, + const PxStepSolverBodyData& data0, + const PxStepSolverBodyData& data1, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + bool hasForceThreshold, bool staticOrKinematicBody, + const PxReal restDist, PxU8* frictionDataPtr, + const PxReal maxCCDSeparation, + const bool disableStrongFriction, + const PxReal torsionalPatchRadiusF32, + const PxReal minTorsionalPatchRadiusF32) + { + bool hasTorsionalFriction = torsionalPatchRadiusF32 > 0.f || minTorsionalPatchRadiusF32 > 0.f; + + // NOTE II: the friction patches are sparse (some of them have no contact patches, and + // therefore did not get written back to the cache) but the patch addresses are dense, + // corresponding to valid patches + + const bool isKinematic0 = b0.isKinematic; + const bool isKinematic1 = b1.isKinematic; + + const FloatV ccdMaxSeparation = FLoad(maxCCDSeparation); + + PxU8 flags = PxU8(hasForceThreshold ? SolverContactHeaderStep::eHAS_FORCE_THRESHOLDS : 0); + + PxU8* PX_RESTRICT ptr = workspace; + + PxU8 type = Ps::to8(staticOrKinematicBody ? DY_SC_TYPE_STATIC_CONTACT + : DY_SC_TYPE_RB_CONTACT); + + const FloatV zero = FZero(); + + const FloatV d0 = FLoad(invMassScale0); + const FloatV d1 = FLoad(invMassScale1); + const FloatV angD0 = FLoad(invInertiaScale0); + const FloatV angD1 = FLoad(invInertiaScale1); + + const FloatV nDom1fV = FNeg(d1); + + const FloatV invMass0 = FLoad(data0.invMass); + const FloatV invMass1 = FLoad(data1.invMass); + + const FloatV invMass0_dom0fV = FMul(d0, invMass0); + const FloatV invMass1_dom1fV = FMul(nDom1fV, invMass1); + + Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4Zero(); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, invMass0_dom0fV); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, invMass1_dom1fV); + + const FloatV restDistance = FLoad(restDist); + + const PxReal maxPenBias = PxMax(data0.penBiasClamp, data1.penBiasClamp); + + const QuatV bodyFrame0q = QuatVLoadU(&bodyFrame0.q.x); + const Vec3V bodyFrame0p = V3LoadU(bodyFrame0.p); + + const QuatV bodyFrame1q = QuatVLoadU(&bodyFrame1.q.x); + const Vec3V bodyFrame1p = V3LoadU(bodyFrame1.p); + + + + PxU32 frictionPatchWritebackAddrIndex = 0; + PxU32 contactWritebackCount = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + //const Vec3V linVel0 = V3LoadU_SafeReadW(b0.linearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + //const Vec3V linVel1 = V3LoadU_SafeReadW(b1.linearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + //const Vec3V angVel0 = V3LoadU_SafeReadW(b0.angularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + //const Vec3V angVel1 = V3LoadU_SafeReadW(b1.angularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + + + const Vec3V linVel0 = V3LoadU_SafeReadW(data0.originalLinearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + const Vec3V linVel1 = V3LoadU_SafeReadW(data1.originalLinearVelocity); // PT: safe because 'invMass' follows 'initialLinVel' in PxSolverBodyData + const Vec3V angVel0 = V3LoadU_SafeReadW(data0.originalAngularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + const Vec3V angVel1 = V3LoadU_SafeReadW(data1.originalAngularVelocity); // PT: safe because 'reportThreshold' follows 'initialAngVel' in PxSolverBodyData + + + PX_ALIGN(16, const Mat33V sqrtInvInertia0) + ( + V3LoadU_SafeReadW(txI0.sqrtInvInertia.column0), // PT: safe because 'column1' follows 'column0' in PxMat33 + V3LoadU_SafeReadW(txI0.sqrtInvInertia.column1), // PT: safe because 'column2' follows 'column1' in PxMat33 + V3LoadU(txI0.sqrtInvInertia.column2) + ); + + PX_ALIGN(16, const Mat33V sqrtInvInertia1) + ( + V3LoadU_SafeReadW(txI1.sqrtInvInertia.column0), // PT: safe because 'column1' follows 'column0' in PxMat33 + V3LoadU_SafeReadW(txI1.sqrtInvInertia.column1), // PT: safe because 'column2' follows 'column1' in PxMat33 + V3LoadU(txI1.sqrtInvInertia.column2) + ); + + const FloatV invDt = FLoad(invDtF32); + const FloatV invTotalDt = FLoad(invTotalDtF32); + const FloatV p8 = FLoad(0.8f); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + + const FloatV invDtp8 = FMul(invDt, p8); + + const PxReal frictionBiasScale = disableStrongFriction ? 0.f : invDtF32; + + + for (PxU32 i = 0; irestitution; + + SolverContactHeaderStep* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeaderStep); + + + Ps::prefetchLine(ptr, 128); + Ps::prefetchLine(ptr, 256); + + header->shapeInteraction = shapeInteraction; + header->flags = flags; + FStore(invMass0_dom0fV, &header->invMass0); + FStore(FNeg(invMass1_dom1fV), &header->invMass1); + const FloatV restitution = FLoad(combinedRestitution); + + PxU32 pointStride = sizeof(SolverContactPointStep); + PxU32 frictionStride = sizeof(SolverContactFrictionStep); + + const Vec3V normal = V3LoadA(buffer[c.contactPatches[c.correlationListHeads[i]].start].normal); + const FloatV normalLenSq = V3LengthSq(normal); + const VecCrossV norCross = V3PrepareCross(normal); + //const FloatV norVel = V3SumElems(V3NegMulSub(normal, linVel1, V3Mul(normal, linVel0))); + const FloatV norVel0 = V3Dot(linVel0, normal); + const FloatV norVel1 = V3Dot(linVel1, normal); + + const FloatV invMassNorLenSq0 = FMul(invMass0_dom0fV, normalLenSq); + const FloatV invMassNorLenSq1 = FMul(invMass1_dom1fV, normalLenSq); + + V3StoreA(normal, header->normal); + header->maxPenBias = maxPenBias; + + FloatV maxPenetration = FMax(); + + for (PxU32 patch = c.correlationListHeads[i]; + patch != CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer + c.contactPatches[patch].start; + + PxU8* p = ptr; + + for (PxU32 j = 0; j(p); + p += pointStride; + + maxPenetration = FMin(maxPenetration, constructContactConstraintStep(sqrtInvInertia0, sqrtInvInertia1, invMassNorLenSq0, + invMassNorLenSq1, angD0, angD1, bodyFrame0p, bodyFrame1p, bodyFrame0q, bodyFrame1q, + normal, norVel0, norVel1, norCross, angVel0, angVel1, + invTotalDt, invDtp8, restDistance, restitution, + bounceThreshold, contact, *solverContact, + ccdMaxSeparation, isKinematic0, isKinematic1)); + } + + ptr = p; + } + + contactWritebackCount += contactCount; + + PxF32* forceBuffers = reinterpret_cast(ptr); + PxMemZero(forceBuffers, sizeof(PxF32) * contactCount); + ptr += ((contactCount + 3) & (~3)) * sizeof(PxF32); // jump to next 16-byte boundary + + const PxReal staticFriction = contactBase0->staticFriction; + const PxReal dynamicFriction = contactBase0->dynamicFriction; + const bool disableFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); + + const bool haveFriction = (disableFriction == 0 && frictionPatch.anchorCount != 0);//PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0; + header->numNormalConstr = Ps::to8(contactCount); + header->numFrictionConstr = Ps::to8(haveFriction ? frictionPatch.anchorCount * 2 : 0); + + header->type = type; + + header->staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; + FStore(angD0, &header->angDom0); + FStore(angD1, &header->angDom1); + + header->broken = 0; + + if (haveFriction) + { + + + const Vec3V linVrel = V3Sub(linVel0, linVel1); + //const Vec3V normal = Vec3V_From_PxVec3_Aligned(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); + + const FloatV orthoThreshold = FLoad(0.70710678f); + const FloatV p1 = FLoad(0.1f); + // fallback: normal.cross((1,0,0)) or normal.cross((0,0,1)) + const FloatV normalX = V3GetX(normal); + const FloatV normalY = V3GetY(normal); + const FloatV normalZ = V3GetZ(normal); + + Vec3V t0Fallback1 = V3Merge(zero, FNeg(normalZ), normalY); + Vec3V t0Fallback2 = V3Merge(FNeg(normalY), normalX, zero); + Vec3V t0Fallback = V3Sel(FIsGrtr(orthoThreshold, FAbs(normalX)), t0Fallback1, t0Fallback2); + + Vec3V t0 = V3Sub(linVrel, V3Scale(normal, V3Dot(normal, linVrel))); + t0 = V3Sel(FIsGrtr(V3LengthSq(t0), p1), t0, t0Fallback); + t0 = V3Normalize(t0); + + const VecCrossV t0Cross = V3PrepareCross(t0); + + const Vec3V t1 = V3Cross(norCross, t0Cross); + //const VecCrossV t1Cross = V3PrepareCross(t1); + + + // since we don't even have the body velocities we can't compute the tangent dirs, so + // the only thing we can do right now is to write the geometric information (which is the + // same for both axis constraints of an anchor) We put ra in the raXn field, rb in the rbXn + // field, and the error in the normal field. See corresponding comments in + // completeContactFriction() + + //We want to set the writeBack ptr to point to the broken flag of the friction patch. + //On spu we have a slight problem here because the friction patch array is + //in local store rather than in main memory. The good news is that the address of the friction + //patch array in main memory is stored in the work unit. These two addresses will be equal + //except on spu where one is local store memory and the other is the effective address in main memory. + //Using the value stored in the work unit guarantees that the main memory address is used on all platforms. + PxU8* PX_RESTRICT writeback = frictionDataPtr + frictionPatchWritebackAddrIndex*sizeof(FrictionPatch); + + + + const FloatV norVel00 = V3Dot(linVel0, t0); + const FloatV norVel01 = V3Dot(linVel1, t0); + const FloatV norVel10 = V3Dot(linVel0, t1); + const FloatV norVel11 = V3Dot(linVel1, t1); + + const Vec3V relTr = V3Sub(bodyFrame0p, bodyFrame1p); + + header->frictionBrokenWritebackByte = writeback; + + for (PxU32 j = 0; j < frictionPatch.anchorCount; j++) + { + Ps::prefetchLine(ptr, 256); + Ps::prefetchLine(ptr, 384); + SolverContactFrictionStep* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionStride; + SolverContactFrictionStep* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionStride; + + Vec3V body0Anchor = V3LoadU(frictionPatch.body0Anchors[j]); + Vec3V body1Anchor = V3LoadU(frictionPatch.body1Anchors[j]); + + Vec3V ra = QuatRotate(bodyFrame0q, body0Anchor); + Vec3V rb = QuatRotate(bodyFrame1q, body1Anchor); + + PxU32 index = c.contactID[i][j]; + + index = index == 0xFFFF ? c.contactPatches[c.correlationListHeads[i]].start : index; + + const Vec3V tvel = V3LoadA(buffer[index].targetVel); + + const Vec3V error = V3Add(V3Sub(ra, rb), relTr); + + + + { + const Vec3V raXn = V3Cross(ra, t0); + const Vec3V rbXn = V3Cross(rb, t0); + const Vec3V raXnInertia = M33MulV3(sqrtInvInertia0, raXn); + const Vec3V rbXnInertia = M33MulV3(sqrtInvInertia1, rbXn); + + const FloatV resp0 = FAdd(invMassNorLenSq0, FMul(V3Dot(raXnInertia, raXnInertia), angD0)); + const FloatV resp1 = FSub(FMul(V3Dot(rbXnInertia, rbXnInertia), angD1), invMassNorLenSq1); + + const FloatV unitResponse = FAdd(resp0, resp1); + + const FloatV velMultiplier = FSel(FIsGrtr(unitResponse, zero), FDiv(p8, unitResponse), zero); + + FloatV targetVel = V3Dot(tvel, t0); + + if(isKinematic0) + targetVel = FSub(targetVel, FAdd(norVel00, V3Dot(raXn, angVel0))); + if (isKinematic1) + targetVel = FAdd(targetVel, FAdd(norVel01, V3Dot(rbXn, angVel1))); + + f0->normalXYZ_ErrorW = V4SetW(t0, V3Dot(error, t0)); + //f0->raXnXYZ_targetVelW = V4SetW(body0Anchor, targetVel); + //f0->rbXnXYZ_biasW = V4SetW(body1Anchor, FZero()); + /*f0->raXn_biasScaleW = V4SetW(Vec4V_From_Vec3V(raXn), frictionBiasScale); + f0->rbXn_errorW = V4SetW(rbXn, V3Dot(error, t0));*/ + f0->raXnI_targetVelW = V4SetW(raXnInertia, targetVel); + f0->rbXnI_velMultiplierW = V4SetW(rbXnInertia, velMultiplier); + f0->appliedForce = 0.f; + f0->frictionScale = 1.f; + f0->biasScale = frictionBiasScale; + } + + { + FloatV targetVel = V3Dot(tvel, t1); + + + const Vec3V raXn = V3Cross(ra, t1); + const Vec3V rbXn = V3Cross(rb, t1); + const Vec3V raXnInertia = M33MulV3(sqrtInvInertia0, raXn); + const Vec3V rbXnInertia = M33MulV3(sqrtInvInertia1, rbXn); + + const FloatV resp0 = FAdd(invMassNorLenSq0, FMul(V3Dot(raXnInertia, raXnInertia), angD0)); + const FloatV resp1 = FSub(FMul(V3Dot(rbXnInertia, rbXnInertia), angD1), invMassNorLenSq1); + + const FloatV unitResponse = FAdd(resp0, resp1); + + const FloatV velMultiplier = FSel(FIsGrtr(unitResponse, zero), FDiv(p8, unitResponse), zero); + + if (isKinematic0) + targetVel = FSub(targetVel, FAdd(norVel10, V3Dot(raXn, angVel0))); + if (isKinematic1) + targetVel = FAdd(targetVel, FAdd(norVel11, V3Dot(rbXn, angVel1))); + + + f1->normalXYZ_ErrorW = V4SetW(t1, V3Dot(error, t1)); + //f1->raXnXYZ_targetVelW = V4SetW(body0Anchor, targetVel); + //f1->rbXnXYZ_biasW = V4SetW(body1Anchor, FZero()); + //f1->raXn_biasScaleW = V4SetW(Vec4V_From_Vec3V(V3Cross(ra, t1)), frictionBiasScale); + //f1->rbXn_errorW = V4SetW(V3Cross(rb, t1), V3Dot(error, t1)); + f1->raXnI_targetVelW = V4SetW(raXnInertia, targetVel); + f1->rbXnI_velMultiplierW = V4SetW(rbXnInertia, velMultiplier); + f1->appliedForce = 0.f; + f1->frictionScale = 1.f; + f1->biasScale = frictionBiasScale; + } + } + + if (hasTorsionalFriction && frictionPatch.anchorCount == 1) + { + const FloatV torsionalPatchRadius = FLoad(torsionalPatchRadiusF32); + const FloatV minTorsionalPatchRadius = FLoad(minTorsionalPatchRadiusF32); + const FloatV torsionalFriction = FMax(minTorsionalPatchRadius, FSqrt(FMul(FMax(zero, FNeg(maxPenetration)), torsionalPatchRadius))); + header->numFrictionConstr++; + SolverContactFrictionStep* PX_RESTRICT f = reinterpret_cast(ptr); + ptr += frictionStride; + + //Rotate the oldRelativeQuat into world space to get the new target relative quat + const PxQuat newTargetQ1 = txI0.deltaBody2World.q * frictionPatch.relativeQuat; + //Now we need to find the rotation around "normal" from Q1 to newTargetQ1. This is the error... + const PxQuat deltaQ = newTargetQ1.getConjugate() * txI1.deltaBody2World.q; + + const PxVec3 nTemp = buffer[c.contactPatches[c.correlationListHeads[i]].start].normal; + + PxQuat temp(deltaQ.x*nTemp.x, deltaQ.y*nTemp.y, deltaQ.z*nTemp.z, deltaQ.w); + + const PxReal magnitude = temp.normalize(); + + PxReal angle = PxAtan(physx::intrinsics::fsel(magnitude - 1e-6f, temp.dot(PxQuat(nTemp.x, nTemp.y, nTemp.z, 0.f)) / temp.w, 0.f)); + + //OK. We have old relative quat and new relative quat. Now find difference + + + const Vec3V raXnInertia = M33MulV3(sqrtInvInertia0, normal); + const Vec3V rbXnInertia = M33MulV3(sqrtInvInertia1, normal); + + const FloatV resp0 = FMul(V3Dot(raXnInertia, raXnInertia), angD0); + const FloatV resp1 = FMul(V3Dot(rbXnInertia, rbXnInertia), angD1); + + const FloatV unitResponse = FAdd(resp0, resp1); + + const FloatV velMultiplier = FSel(FIsGrtr(unitResponse, zero), FDiv(p8, unitResponse), zero); + + FloatV targetVel = zero; + + if (isKinematic0) + targetVel = V3Dot(normal, angVel0); + if (isKinematic1) + targetVel = V3Dot(normal, angVel1); + + f->normalXYZ_ErrorW = V4SetW(V3Zero(), FLoad(-angle)); + f->raXnI_targetVelW = V4SetW(raXnInertia, targetVel); + f->rbXnI_velMultiplierW = V4SetW(rbXnInertia, velMultiplier); + f->biasScale = frictionBiasScale; + f->appliedForce = 0.f; + FStore(torsionalFriction, &f->frictionScale); + } + } + + frictionPatchWritebackAddrIndex++; + } + } + + + static FloatV setupExtSolverContactStep(const SolverExtBodyStep& b0, const SolverExtBodyStep& b1, + const PxF32 d0, const PxF32 d1, const PxF32 angD0, const PxF32 angD1, const PxTransform& bodyFrame0, const PxTransform& bodyFrame1, + const Vec3VArg normal, const FloatVArg invDt, const FloatVArg invDtp8, const FloatVArg restDistance, const FloatVArg restitution, + const FloatVArg bounceThreshold, const Gu::ContactPoint& contact, SolverContactPointStepExt& solverContact, const FloatVArg /*ccdMaxSeparation*/, + const bool isKinematic0, const bool isKinematic1) + { + const FloatV zero = FZero(); + const FloatV separation = FLoad(contact.separation); + + const FloatV penetration = FSub(separation, restDistance); + + const PxVec3 ra = contact.point - bodyFrame0.p; + const PxVec3 rb = contact.point - bodyFrame1.p; + + const PxVec3 raXn = ra.cross(contact.normal); + const PxVec3 rbXn = rb.cross(contact.normal); + + Cm::SpatialVector deltaV0, deltaV1; + + const Cm::SpatialVector resp0 = createImpulseResponseVector(contact.normal, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-contact.normal, -rbXn, b1); + + PxReal resp = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, false); + + FloatV unitResponse = FLoad(resp); + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK && + b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + PxReal lr0 = deltaV0.linear.dot(resp0.linear); + PxReal ar0 = deltaV0.angular.dot(resp0.angular) * safeRecip(resp0.angular.magnitude()); + PxReal lr1 = deltaV1.linear.dot(resp1.linear); + PxReal ar1 = deltaV1.angular.dot(resp1.angular)* safeRecip(resp1.angular.magnitude()); + + Cm::SpatialVector rem0(deltaV0.linear - resp0.linear*lr0, deltaV0.angular - resp0.angular*ar0); + Cm::SpatialVector rem1(deltaV1.linear - resp1.linear*lr1, deltaV1.angular - resp1.angular*ar1); + + + PxReal rem = (rem0 - rem1).magnitude(); + + unitResponse = FAdd(unitResponse, FLoad(rem)); + } + + const FloatV vel0 = FLoad(b0.projectVelocity(contact.normal, raXn)); + const FloatV vel1 = FLoad(b1.projectVelocity(contact.normal, rbXn)); + const FloatV vrel = FSub(vel0, vel1); + + + + + FloatV velMultiplier = FSel(FIsGrtr(unitResponse, FEps()), FRecip(unitResponse), zero); + FloatV scaledBias = invDtp8; + const FloatV penetrationInvDt = FMul(penetration, invDt); + + const BoolV isGreater2 = BAnd(BAnd(FIsGrtr(restitution, zero), FIsGrtr(bounceThreshold, vrel)), FIsGrtr(FNeg(vrel), penetrationInvDt)); + + //scaledBias = FSel(isGreater2, FZero(), FNeg(scaledBias)); + scaledBias = FNeg(scaledBias); + + FloatV targetVelocity = FSel(isGreater2, FMul(FNeg(vrel), restitution), zero); + + targetVelocity = FAdd(targetVelocity, V3Dot(V3LoadA(contact.targetVel), normal)); + + if (isKinematic0) + targetVelocity = FSub(targetVelocity, vel0); + if(isKinematic1) + targetVelocity = FAdd(targetVelocity, vel1); + + const FloatV biasedErr = FMul(FAdd(targetVelocity, FMul(penetration, scaledBias)),velMultiplier); + + const FloatV deltaF = FMax(FNegScaleSub(vrel, velMultiplier, biasedErr), zero); + + + FStore(velMultiplier, &solverContact.velMultiplier); + FStore(scaledBias, &solverContact.biasCoefficient); + FStore(penetration, &solverContact.separation); + FStore(targetVelocity, &solverContact.targetVelocity); + + solverContact.raXnI = resp0.angular; + solverContact.rbXnI = -resp1.angular; + solverContact.linDeltaVA = V3LoadA(deltaV0.linear); + solverContact.angDeltaVA = V3LoadA(deltaV0.angular); + solverContact.linDeltaVB = V3LoadA(deltaV1.linear); + solverContact.angDeltaVB = V3LoadA(deltaV1.angular); + + return deltaF; + } + + PX_INLINE void computeFrictionTangents(const PxVec3& vrel, const PxVec3& unitNormal, PxVec3& t0, PxVec3& t1) + { + PX_ASSERT(PxAbs(unitNormal.magnitude() - 1)<1e-3f); + + t0 = vrel - unitNormal * unitNormal.dot(vrel); + PxReal ll = t0.magnitudeSquared(); + + if (ll > 0.1f) //can set as low as 0. + { + t0 *= PxRecipSqrt(ll); + t1 = unitNormal.cross(t0); + } + else + Ps::normalToTangents(unitNormal, t0, t1); //fallback + } + + PxVec3 SolverExtBodyStep::getLinVel() const + { + if (mLinkIndex == PxSolverConstraintDesc::NO_LINK) + return mBody->linearVelocity; + else + { + Cm::SpatialVectorV velocity = mArticulation->getLinkVelocity(mLinkIndex); + PxVec3 result; + V3StoreU(velocity.linear, result); + return result; + } + } + + + + void setupFinalizeExtSolverContactsStep( + const ContactPoint* buffer, + const CorrelationBuffer& c, + const PxTransform& bodyFrame0, + const PxTransform& bodyFrame1, + PxU8* workspace, + const SolverExtBodyStep& b0, + const SolverExtBodyStep& b1, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal invMassScale0, PxReal invInertiaScale0, + PxReal invMassScale1, PxReal invInertiaScale1, + const PxReal restDist, + PxU8* frictionDataPtr, + PxReal ccdMaxContactDist, + const PxReal torsionalPatchRadiusF32, + const PxReal minTorsionalPatchRadiusF32) + { + // NOTE II: the friction patches are sparse (some of them have no contact patches, and + // therefore did not get written back to the cache) but the patch addresses are dense, + // corresponding to valid patches + + /*const bool haveFriction = PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0;*/ + + const bool isKinematic0 = b0.isKinematic(); + const bool isKinematic1 = b1.isKinematic(); + + const FloatV ccdMaxSeparation = FLoad(ccdMaxContactDist); + + bool hasTorsionalFriction = torsionalPatchRadiusF32 > 0.f || minTorsionalPatchRadiusF32 > 0.f; + + PxU8* PX_RESTRICT ptr = workspace; + + const FloatV zero = FZero(); + + //KS - TODO - this should all be done in SIMD to avoid LHS + /*const PxF32 maxPenBias0 = b0.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b0.mData->penBiasClamp : getMaxPenBias(*b0.mFsData)[b0.mLinkIndex]; + const PxF32 maxPenBias1 = b1.mLinkIndex == PxSolverConstraintDesc::NO_LINK ? b1.mData->penBiasClamp : getMaxPenBias(*b1.mFsData)[b1.mLinkIndex];*/ + + PxF32 maxPenBias0 = b0.mData->penBiasClamp; + PxF32 maxPenBias1 = b1.mData->penBiasClamp; + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias0 = b0.mArticulation->getLinkMaxPenBias(b0.mLinkIndex); + } + + if (b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + maxPenBias1 = b1.mArticulation->getLinkMaxPenBias(b1.mLinkIndex); + } + + const PxReal maxPenBias = PxMax(maxPenBias0, maxPenBias1); + + + const PxReal d0 = invMassScale0; + const PxReal d1 = invMassScale1; + + const PxReal angD0 = invInertiaScale0; + const PxReal angD1 = invInertiaScale1; + + Vec4V staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4Zero(); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetZ(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(d0)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetW(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(d1)); + + const FloatV restDistance = FLoad(restDist); + + PxU32 frictionPatchWritebackAddrIndex = 0; + PxU32 contactWritebackCount = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + const FloatV invDt = FLoad(invDtF32); + const FloatV invTotalDt = FLoad(invTotalDtF32); + const FloatV p8 = FLoad(0.8f); + const FloatV bounceThreshold = FLoad(bounceThresholdF32); + + const FloatV invDtp8 = FMul(invDt, p8); + + PxU8 flags = 0; + + for (PxU32 i = 0; irestitution; + + const PxReal staticFriction = contactBase0->staticFriction; + const PxReal dynamicFriction = contactBase0->dynamicFriction; + const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); + staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); + + const PxReal frictionBiasScale = disableStrongFriction ? 0.f : invDtF32; + + SolverContactHeaderStep* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeaderStep); + + + Ps::prefetchLine(ptr + 128); + Ps::prefetchLine(ptr + 256); + Ps::prefetchLine(ptr + 384); + + const bool haveFriction = (disableStrongFriction == 0);//PX_IR(n.staticFriction) > 0 || PX_IR(n.dynamicFriction) > 0; + header->numNormalConstr = Ps::to8(contactCount); + header->numFrictionConstr = Ps::to8(haveFriction ? frictionPatch.anchorCount * 2 : 0); + + header->type = Ps::to8(DY_SC_TYPE_EXT_CONTACT); + + header->flags = flags; + + const FloatV restitution = FLoad(combinedRestitution); + + header->staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W; + + header->angDom0 = angD0; + header->angDom1 = angD1; + + const PxU32 pointStride = sizeof(SolverContactPointStepExt); + const PxU32 frictionStride = sizeof(SolverContactFrictionStepExt); + + const Vec3V normal = V3LoadU(buffer[c.contactPatches[c.correlationListHeads[i]].start].normal); + + V3StoreA(normal, header->normal); + header->maxPenBias = maxPenBias; + + FloatV maxPenetration = FZero(); + + FloatV accumulatedImpulse = FZero(); + + for (PxU32 patch = c.correlationListHeads[i]; + patch != CorrelationBuffer::LIST_END; + patch = c.contactPatches[patch].next) + { + const PxU32 count = c.contactPatches[patch].count; + const Gu::ContactPoint* contactBase = buffer + c.contactPatches[patch].start; + + PxU8* p = ptr; + + for (PxU32 j = 0; j(p); + p += pointStride; + + accumulatedImpulse = FAdd(accumulatedImpulse, setupExtSolverContactStep(b0, b1, d0, d1, angD0, angD1, bodyFrame0, bodyFrame1, normal, invTotalDt, invDtp8, restDistance, restitution, + bounceThreshold, contact, *solverContact, ccdMaxSeparation, isKinematic0, isKinematic1)); + + maxPenetration = FMin(FLoad(contact.separation), maxPenetration); + + } + + ptr = p; + } + contactWritebackCount += contactCount; + + accumulatedImpulse = FDiv(accumulatedImpulse, FLoad(PxF32(contactCount))); + + FStore(accumulatedImpulse, &header->minNormalForce); + + PxF32* forceBuffer = reinterpret_cast(ptr); + PxMemZero(forceBuffer, sizeof(PxF32) * contactCount); + ptr += sizeof(PxF32) * ((contactCount + 3) & (~3)); + + header->broken = 0; + + if (haveFriction) + { + //const Vec3V normal = Vec3V_From_PxVec3(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); + PxVec3 normalS = buffer[c.contactPatches[c.correlationListHeads[i]].start].normal; + + PxVec3 t0, t1; + computeFrictionTangents(b0.getLinVel() - b1.getLinVel(), normalS, t0, t1); + + Vec3V vT0 = V3LoadU(t0); + Vec3V vT1 = V3LoadU(t1); + + //We want to set the writeBack ptr to point to the broken flag of the friction patch. + //On spu we have a slight problem here because the friction patch array is + //in local store rather than in main memory. The good news is that the address of the friction + //patch array in main memory is stored in the work unit. These two addresses will be equal + //except on spu where one is local store memory and the other is the effective address in main memory. + //Using the value stored in the work unit guarantees that the main memory address is used on all platforms. + PxU8* PX_RESTRICT writeback = frictionDataPtr + frictionPatchWritebackAddrIndex * sizeof(FrictionPatch); + + header->frictionBrokenWritebackByte = writeback; + + for (PxU32 j = 0; j < frictionPatch.anchorCount; j++) + { + SolverContactFrictionStepExt* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionStride; + SolverContactFrictionStepExt* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionStride; + + PxVec3 ra = bodyFrame0.q.rotate(frictionPatch.body0Anchors[j]); + PxVec3 rb = bodyFrame1.q.rotate(frictionPatch.body1Anchors[j]); + PxVec3 error = (ra + bodyFrame0.p) - (rb + bodyFrame1.p); + + { + const PxVec3 raXn = ra.cross(t0); + const PxVec3 rbXn = rb.cross(t0); + + Cm::SpatialVector deltaV0, deltaV1; + + const Cm::SpatialVector resp0 = createImpulseResponseVector(t0, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-t0, -rbXn, b1); + PxReal ur = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, false); + FloatV resp = FLoad(ur); + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK && + b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + PxReal lr0 = deltaV0.linear.dot(resp0.linear); + PxReal ar0 = deltaV0.angular.dot(resp0.angular) * safeRecip(resp0.angular.magnitude()); + PxReal lr1 = deltaV1.linear.dot(resp1.linear); + PxReal ar1 = deltaV1.angular.dot(resp1.angular)* safeRecip(resp1.angular.magnitude()); + + Cm::SpatialVector rem0(deltaV0.linear - resp0.linear*lr0, deltaV0.angular - resp0.angular*ar0); + Cm::SpatialVector rem1(deltaV1.linear - resp1.linear*lr1, deltaV1.angular - resp1.angular*ar1); + + + PxReal rem = (rem0 - rem1).magnitude(); + + resp = FAdd(resp, FLoad(rem)); + } + + const FloatV velMultiplier = FSel(FIsGrtr(resp, FEps()), FDiv(p8, resp), zero); + + PxU32 index = c.contactPatches[c.correlationListHeads[i]].start; + PxF32 targetVel = buffer[index].targetVel.dot(t0); + + if(isKinematic0) + targetVel -= b0.projectVelocity(t0, raXn); + if(isKinematic1) + targetVel += b1.projectVelocity(t0, rbXn); + + f0->normalXYZ_ErrorW = V4SetW(vT0, FLoad(error.dot(t0))); + f0->raXnI_targetVelW = V4SetW(V4LoadA(&resp0.angular.x), FLoad(targetVel)); + f0->rbXnI_velMultiplierW = V4SetW(V4Neg(V4LoadA(&resp1.angular.x)), velMultiplier); + f0->appliedForce = 0.f; + f0->biasScale = frictionBiasScale; + f0->linDeltaVA = V3LoadA(deltaV0.linear); + f0->linDeltaVB = V3LoadA(deltaV1.linear); + f0->angDeltaVA = V3LoadA(deltaV0.angular); + f0->angDeltaVB = V3LoadA(deltaV1.angular); + } + + { + + const PxVec3 raXn = ra.cross(t1); + const PxVec3 rbXn = rb.cross(t1); + + Cm::SpatialVector deltaV0, deltaV1; + + + const Cm::SpatialVector resp0 = createImpulseResponseVector(t1, raXn, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-t1, -rbXn, b1); + + PxReal ur = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, false); + FloatV resp = FLoad(ur); + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK && + b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + PxReal lr0 = deltaV0.linear.dot(resp0.linear); + PxReal ar0 = deltaV0.angular.dot(resp0.angular) * safeRecip(resp0.angular.magnitude()); + PxReal lr1 = deltaV1.linear.dot(resp1.linear); + PxReal ar1 = deltaV1.angular.dot(resp1.angular)* safeRecip(resp1.angular.magnitude()); + + Cm::SpatialVector rem0(deltaV0.linear - resp0.linear*lr0, deltaV0.angular - resp0.angular*ar0); + Cm::SpatialVector rem1(deltaV1.linear - resp1.linear*lr1, deltaV1.angular - resp1.angular*ar1); + + + PxReal rem = (rem0 - rem1).magnitude(); + + resp = FAdd(resp, FLoad(rem)); + } + + const FloatV velMultiplier = FSel(FIsGrtr(resp, FEps()), FDiv(p8, resp), zero); + + PxU32 index = c.contactPatches[c.correlationListHeads[i]].start; + PxF32 targetVel = buffer[index].targetVel.dot(t1); + + if (isKinematic0) + targetVel -= b0.projectVelocity(t1, raXn); + if (isKinematic1) + targetVel += b1.projectVelocity(t1, rbXn); + + f1->normalXYZ_ErrorW = V4SetW(vT1, FLoad(error.dot(t1))); + f1->raXnI_targetVelW = V4SetW(V4LoadA(&resp0.angular.x), FLoad(targetVel)); + f1->rbXnI_velMultiplierW = V4SetW(V4Neg(V4LoadA(&resp1.angular.x)), velMultiplier); + f1->appliedForce = 0.f; + f1->biasScale = frictionBiasScale; + f1->linDeltaVA = V3LoadA(deltaV0.linear); + f1->linDeltaVB = V3LoadA(deltaV1.linear); + f1->angDeltaVA = V3LoadA(deltaV0.angular); + f1->angDeltaVB = V3LoadA(deltaV1.angular); + } + } + + if (hasTorsionalFriction && frictionPatch.anchorCount == 1) + { + header->numFrictionConstr++; + + const FloatV torsionalPatchRadius = FLoad(torsionalPatchRadiusF32); + const FloatV minTorsionalPatchRadius = FLoad(minTorsionalPatchRadiusF32); + const FloatV torsionalFriction = FMax(minTorsionalPatchRadius, FSqrt(FMul(FMax(zero, FNeg(maxPenetration)), torsionalPatchRadius))); + header->numFrictionConstr++; + SolverContactFrictionStepExt* PX_RESTRICT f = reinterpret_cast(ptr); + ptr += frictionStride; + + //Rotate the oldRelativeQuat into world space to get the new target relative quat + const PxQuat newTargetQ1 = bodyFrame0.q * frictionPatch.relativeQuat; + //Now we need to find the rotation around "normal" from Q1 to newTargetQ1. This is the error... + const PxQuat deltaQ = newTargetQ1.getConjugate() * bodyFrame1.q; + + const PxVec3 nTemp = buffer[c.contactPatches[c.correlationListHeads[i]].start].normal; + + PxQuat temp(deltaQ.x*nTemp.x, deltaQ.y*nTemp.y, deltaQ.z*nTemp.z, deltaQ.w); + + const PxReal magnitude = temp.normalize(); + + PxReal angle = PxAtan(physx::intrinsics::fsel(magnitude - 1e-6f, temp.dot(PxQuat(nTemp.x, nTemp.y, nTemp.z, 0.f)) / temp.w, 0.f)); + + //OK. We have old relative quat and new relative quat. Now find difference + + const Cm::SpatialVector resp0 = createImpulseResponseVector(PxVec3(0.f), header->normal, b0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(PxVec3(0.f), -header->normal, b1); + + Cm::SpatialVector deltaV0, deltaV1; + + PxReal ur = getImpulseResponse(b0, resp0, deltaV0, d0, angD0, + b1, resp1, deltaV1, d1, angD1, false); + + FloatV resp = FLoad(ur); + + if (b0.mLinkIndex != PxSolverConstraintDesc::NO_LINK && + b1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + PxReal lr0 = deltaV0.linear.dot(resp0.linear); + PxReal ar0 = deltaV0.angular.dot(resp0.angular) * safeRecip(resp0.angular.magnitude()); + PxReal lr1 = deltaV1.linear.dot(resp1.linear); + PxReal ar1 = deltaV1.angular.dot(resp1.angular)* safeRecip(resp1.angular.magnitude()); + + Cm::SpatialVector rem0(deltaV0.linear - resp0.linear*lr0, deltaV0.angular - resp0.angular*ar0); + Cm::SpatialVector rem1(deltaV1.linear - resp1.linear*lr1, deltaV1.angular - resp1.angular*ar1); + + + PxReal rem = (rem0 - rem1).magnitude(); + + resp = FAdd(resp, FLoad(rem)); + } + + + const FloatV velMultiplier = FSel(FIsGrtr(resp, FEps()), FDiv(p8, resp), zero); + + f->normalXYZ_ErrorW = V4SetW(V3Zero(), FLoad(-angle)); + f->raXnI_targetVelW = V4SetW(V3LoadA(resp0.angular), zero); + f->rbXnI_velMultiplierW = V4SetW(V3LoadA(resp1.angular), velMultiplier); + f->biasScale = frictionBiasScale; + f->appliedForce = 0.f; + FStore(torsionalFriction, &f->frictionScale); + f->linDeltaVA = V3LoadA(deltaV0.linear); + f->linDeltaVB = V3LoadA(deltaV1.linear); + f->angDeltaVA = V3LoadA(deltaV0.angular); + f->angDeltaVB = V3LoadA(deltaV1.angular); + + } + + } + + frictionPatchWritebackAddrIndex++; + } + } + + + + + bool createFinalizeSolverContactsStep( + PxTGSSolverContactDesc& contactDesc, + CorrelationBuffer& c, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxConstraintAllocator& constraintAllocator) + { + Ps::prefetchLine(contactDesc.body0); + Ps::prefetchLine(contactDesc.body1); + + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + const bool hasForceThreshold = contactDesc.hasForceThresholds; + const bool staticOrKinematicBody = contactDesc.bodyState1 == PxSolverContactDesc::eKINEMATIC_BODY || contactDesc.bodyState1 == PxSolverContactDesc::eSTATIC_BODY; + + const bool disableStrongFriction = contactDesc.disableStrongFriction; + const bool useExtContacts = ((contactDesc.bodyState0 | contactDesc.bodyState1) & PxSolverContactDesc::eARTICULATION) != 0; + + PxTGSSolverConstraintDesc& desc = *contactDesc.desc; + + desc.constraintLengthOver16 = 0; + + + if (contactDesc.numContacts == 0) + { + contactDesc.frictionPtr = NULL; + contactDesc.frictionCount = 0; + desc.constraint = NULL; + return true; + } + + if (!disableStrongFriction) + { + getFrictionPatches(c, contactDesc.frictionPtr, contactDesc.frictionCount, contactDesc.bodyFrame0, contactDesc.bodyFrame1, correlationDistance); + } + + bool overflow = !createContactPatches(c, contactDesc.contacts, contactDesc.numContacts, PXC_SAME_NORMAL); + overflow = correlatePatches(c, contactDesc.contacts, contactDesc.bodyFrame0, contactDesc.bodyFrame1, PXC_SAME_NORMAL, 0, 0) || overflow; + PX_UNUSED(overflow); + +#if PX_CHECKED + if (overflow) + { + Ps::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Dropping contacts in solver because we exceeded limit of 32 friction patches."); + } +#endif + + growPatches(c, contactDesc.contacts, contactDesc.bodyFrame0, contactDesc.bodyFrame1, correlationDistance, 0, frictionOffsetThreshold + contactDesc.restDistance); + + //PX_ASSERT(patchCount == c.frictionPatchCount); + + FrictionPatch* frictionPatches = NULL; + PxU8* solverConstraint = NULL; + PxU32 numFrictionPatches = 0; + PxU32 solverConstraintByteSize = 0; + PxU32 axisConstraintCount = 0; + + const bool successfulReserve = reserveBlockStreams( + useExtContacts, c, + solverConstraint, frictionPatches, + numFrictionPatches, + solverConstraintByteSize, + axisConstraintCount, + constraintAllocator, + PxMax(contactDesc.torsionalPatchRadius, contactDesc.minTorsionalPatchRadius)); + // initialise the work unit's ptrs to the various buffers. + + contactDesc.frictionPtr = NULL; + contactDesc.frictionCount = 0; + desc.constraint = NULL; + desc.constraintLengthOver16 = 0; + // patch up the work unit with the reserved buffers and set the reserved buffer data as appropriate. + + if (successfulReserve) + { + PxU8* frictionDataPtr = reinterpret_cast(frictionPatches); + contactDesc.frictionPtr = frictionDataPtr; + desc.constraint = solverConstraint; + //output.nbContacts = Ps::to8(numContacts); + contactDesc.frictionCount = Ps::to8(numFrictionPatches); + PX_ASSERT((solverConstraintByteSize & 0xf) == 0); + desc.constraintLengthOver16 = Ps::to16(solverConstraintByteSize / 16); + desc.writeBack = contactDesc.contactForces; + desc.writeBackLengthOver4 = PxU16(contactDesc.contactForces ? contactDesc.numContacts : 0); + + //Initialise friction buffer. + if (frictionPatches) + { + // PT: TODO: revisit this... not very satisfying + //const PxU32 maxSize = numFrictionPatches*sizeof(FrictionPatch); + Ps::prefetchLine(frictionPatches); + Ps::prefetchLine(frictionPatches, 128); + Ps::prefetchLine(frictionPatches, 256); + + for (PxU32 i = 0; i(contactDesc.body0), contactDesc.body0TxI, contactDesc.bodyData0, desc.linkIndexA); + const SolverExtBodyStep b1(reinterpret_cast(contactDesc.body1), contactDesc.body1TxI, contactDesc.bodyData1, desc.linkIndexB); + + setupFinalizeExtSolverContactsStep(contactDesc.contacts, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + b0, b1, invDtF32, invTotalDtF32, bounceThresholdF32, + contactDesc.mInvMassScales.linear0, contactDesc.mInvMassScales.angular0, contactDesc.mInvMassScales.linear1, contactDesc.mInvMassScales.angular1, + contactDesc.restDistance, frictionDataPtr, contactDesc.maxCCDSeparation, contactDesc.torsionalPatchRadius, contactDesc.minTorsionalPatchRadius); + } + else + { + + const PxTGSSolverBodyVel& b0 = *contactDesc.body0; + const PxTGSSolverBodyVel& b1 = *contactDesc.body1; + + setupFinalizeSolverConstraints(contactDesc.shapeInteraction, contactDesc.contacts, c, contactDesc.bodyFrame0, contactDesc.bodyFrame1, solverConstraint, + b0, b1, *contactDesc.body0TxI, *contactDesc.body1TxI, *contactDesc.bodyData0, *contactDesc.bodyData1, invDtF32, invTotalDtF32, bounceThresholdF32, + contactDesc.mInvMassScales.linear0, contactDesc.mInvMassScales.angular0, contactDesc.mInvMassScales.linear1, contactDesc.mInvMassScales.angular1, + hasForceThreshold, staticOrKinematicBody, contactDesc.restDistance, frictionDataPtr, contactDesc.maxCCDSeparation, disableStrongFriction, + contactDesc.torsionalPatchRadius, contactDesc.minTorsionalPatchRadius); + } + //KS - set to 0 so we have a counter for the number of times we solved the constraint + //only going to be used on SPU but might as well set on all platforms because this code is shared + *(reinterpret_cast(solverConstraint + solverConstraintByteSize)) = 0; + } + } + + return successfulReserve; + } + + + + + bool createFinalizeSolverContactsStep(PxTGSSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + const PxReal invTotalDt, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxConstraintAllocator& constraintAllocator) + { + using namespace Gu; + ContactBuffer& buffer = threadContext.mContactBuffer; + + + + buffer.count = 0; + + // We pull the friction patches out of the cache to remove the dependency on how + // the cache is organized. Remember original addrs so we can write them back + // efficiently. + + PxU32 numContacts = 0; + { + PxReal invMassScale0 = 1.f; + PxReal invMassScale1 = 1.f; + PxReal invInertiaScale0 = 1.f; + PxReal invInertiaScale1 = 1.f; + contactDesc.mInvMassScales.angular0 = (contactDesc.bodyState0 != PxSolverContactDesc::eARTICULATION && contactDesc.body0->isKinematic) ? 0.f : contactDesc.mInvMassScales.angular0; + contactDesc.mInvMassScales.angular1 = (contactDesc.bodyState1 != PxSolverContactDesc::eARTICULATION && contactDesc.body1->isKinematic) ? 0.f : contactDesc.mInvMassScales.angular1; + + bool hasMaxImpulse = false, hasTargetVelocity = false; + + numContacts = extractContacts(buffer, output, hasMaxImpulse, hasTargetVelocity, invMassScale0, invMassScale1, + invInertiaScale0, invInertiaScale1, contactDesc.maxImpulse); + + contactDesc.contacts = buffer.contacts; + contactDesc.numContacts = numContacts; + contactDesc.disableStrongFriction = contactDesc.disableStrongFriction || hasTargetVelocity; + contactDesc.hasMaxImpulse = hasMaxImpulse; + contactDesc.mInvMassScales.linear0 *= invMassScale0; + contactDesc.mInvMassScales.linear1 *= invMassScale1; + contactDesc.mInvMassScales.angular0 *= invInertiaScale0; + contactDesc.mInvMassScales.angular1 *= invInertiaScale1; + } + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + + return createFinalizeSolverContactsStep(contactDesc, c, invDtF32, invTotalDt, bounceThresholdF32, frictionOffsetThreshold, correlationDistance, constraintAllocator); + } + + PX_FORCE_INLINE PxU32 getConstraintLength(const PxTGSSolverConstraintDesc& desc) + { + return PxU32(desc.constraintLengthOver16 << 4); + } + + PX_FORCE_INLINE PxU32 getWritebackLength(const PxTGSSolverConstraintDesc& desc) + { + return PxU32(desc.writeBackLengthOver4 << 2); + } + + + + static FloatV solveDynamicContactsStep(SolverContactPointStep* contacts, const PxU32 nbContactPoints, const Vec3VArg contactNormal, + const FloatVArg invMassA, const FloatVArg invMassB, Vec3V& linVel0_, Vec3V& angState0_, + Vec3V& linVel1_, Vec3V& angState1_, PxF32* PX_RESTRICT forceBuffer, + const Vec3V& angMotion0, const Vec3V& angMotion1, + const Vec3V& linRelMotion, const FloatVArg maxPenBias, + const FloatVArg angD0, const FloatVArg angD1, const FloatVArg minPen, + const FloatVArg elapsedTime) + { + Vec3V linVel0 = linVel0_; + Vec3V angState0 = angState0_; + Vec3V linVel1 = linVel1_; + Vec3V angState1 = angState1_; + FloatV accumulatedNormalImpulse = FZero(); + + const Vec3V delLinVel0 = V3Scale(contactNormal, invMassA); + const Vec3V delLinVel1 = V3Scale(contactNormal, invMassB); + + const FloatV deltaV = V3Dot(linRelMotion, contactNormal); + + for (PxU32 i = 0; i(currPtr); + currPtr += sizeof(SolverContactHeaderStep); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverContactPointStep* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPointStep); + + PxF32* forceBuffer = reinterpret_cast(currPtr); + currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + SolverContactFrictionStep* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionStep); + + const FloatV invMassA = FLoad(hdr->invMass0); + const FloatV invMassB = FLoad(hdr->invMass1); + + const FloatV angDom0 = FLoad(hdr->angDom0); + const FloatV angDom1 = FLoad(hdr->angDom1); + + //const FloatV sumMass = FAdd(invMassA, invMassB); + + const Vec3V contactNormal = V3LoadA(hdr->normal); + + const FloatV maxPenBias = FLoad(hdr->maxPenBias); + + const FloatV accumulatedNormalImpulse = solveDynamicContactsStep(contacts, numNormalConstr, contactNormal, invMassA, invMassB, + linVel0, angState0, linVel1, angState1, forceBuffer,angMotion0, angMotion1, relMotion, maxPenBias, angDom0, angDom1, minPen, + elapsedTime); + + if (numFrictionConstr && doFriction) + { + const FloatV staticFrictionCof = hdr->getStaticFriction(); + const FloatV dynamicFrictionCof = hdr->getDynamicFriction(); + const FloatV maxFrictionImpulse = FMul(staticFrictionCof, accumulatedNormalImpulse); + const FloatV maxDynFrictionImpulse = FMul(dynamicFrictionCof, accumulatedNormalImpulse); + const FloatV negMaxDynFrictionImpulse = FNeg(maxDynFrictionImpulse); + + BoolV broken = BFFFF(); + + for (PxU32 i = 0; i maxFrictionImpulse + // clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse] + // (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce] + // set broken flag to true || broken flag + + // FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier); + // FloatV potentialSumF = FAdd(appliedForce, deltaF); + + const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1); + + // On XBox this clamping code uses the vector simple pipe rather than vector float, + // which eliminates a lot of stall cycles + + const BoolV clamp = FIsGrtr(FAbs(totalImpulse), FMul(frictionScale, maxFrictionImpulse)); + + const FloatV totalClamped = FMin(FMul(frictionScale, maxDynFrictionImpulse), FMax(FMul(frictionScale, negMaxDynFrictionImpulse), totalImpulse)); + + const FloatV newAppliedForce = FSel(clamp, totalClamped, totalImpulse); + + broken = BOr(broken, clamp); + + FloatV deltaF = FSub(newAppliedForce, appliedForce); + + // we could get rid of the stall here by calculating and clamping delta separately, but + // the complexity isn't really worth it. + + linVel0 = V3ScaleAdd(delLinVel0, deltaF, linVel0); + linVel1 = V3NegScaleSub(delLinVel1, deltaF, linVel1); + angState0 = V3ScaleAdd(raXnI, FMul(deltaF, angDom0), angState0); + angState1 = V3NegScaleSub(rbXnI, FMul(deltaF, angDom1), angState1); + + f.setAppliedForce(newAppliedForce); + + + } + Store_From_BoolV(broken, &hdr->broken); + } + + } + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularVelocity.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularVelocity.isFinite()); + + // Write back + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(linVel1, b1.linearVelocity); + V3StoreA(angState0, b0.angularVelocity); + V3StoreA(angState1, b1.angularVelocity); + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularVelocity.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularVelocity.isFinite()); + + PX_ASSERT(currPtr == last); + } + + void writeBackContact(const PxTGSSolverConstraintDesc& desc, SolverContext* cache) + { + PX_UNUSED(cache); + PxReal normalForce = 0; + + PxU8* PX_RESTRICT cPtr = desc.constraint; + PxReal* PX_RESTRICT vForceWriteback = reinterpret_cast(desc.writeBack); + PxU8* PX_RESTRICT last = desc.constraint + desc.constraintLengthOver16 * 16; + + bool forceThreshold = false; + + while (cPtr < last) + { + const SolverContactHeaderStep* PX_RESTRICT hdr = reinterpret_cast(cPtr); + cPtr += sizeof(SolverContactHeaderStep); + + forceThreshold = hdr->flags & SolverContactHeaderStep::eHAS_FORCE_THRESHOLDS; + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + //if(cPtr < last) + Ps::prefetchLine(cPtr, 256); + Ps::prefetchLine(cPtr, 384); + + const PxU32 pointStride = hdr->type == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactPointStepExt) + : sizeof(SolverContactPointStep); + + cPtr += pointStride * numNormalConstr; + PxF32* forceBuffer = reinterpret_cast(cPtr); + cPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + if (vForceWriteback != NULL) + { + for (PxU32 i = 0; itype == DY_SC_TYPE_EXT_CONTACT ? sizeof(SolverContactFrictionStepExt) + : sizeof(SolverContactFrictionStep); + + if (hdr->broken && hdr->frictionBrokenWritebackByte != NULL) + { + *hdr->frictionBrokenWritebackByte = 1; + } + + cPtr += frictionStride * numFrictionConstr; + + } + PX_ASSERT(cPtr == last); + + PX_UNUSED(forceThreshold); + +#if 0 + if (cache && forceThreshold && desc.linkIndexA == PxSolverConstraintDesc::NO_LINK && desc.linkIndexB == PxSolverConstraintDesc::NO_LINK && + normalForce != 0 && (desc.bodyA->reportThreshold < PX_MAX_REAL || desc.bodyB->reportThreshold < PX_MAX_REAL)) + { + ThresholdStreamElement elt; + elt.normalForce = normalForce; + elt.threshold = PxMin(desc.bodyA->reportThreshold, desc.bodyB->reportThreshold); + elt.nodeIndexA = desc.bodyA->nodeIndex; + elt.nodeIndexB = desc.bodyB->nodeIndex; + elt.shapeInteraction = reinterpret_cast(desc.constraint)->shapeInteraction; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + PX_ASSERT(cache->mThresholdStreamIndexmThresholdStreamLength); + cache->mThresholdStream[cache->mThresholdStreamIndex++] = elt; + } +#endif + } + + + + + + PX_FORCE_INLINE Vec3V V3FromV4(Vec4V x) { return Vec3V_From_Vec4V(x); } + PX_FORCE_INLINE Vec3V V3FromV4Unsafe(Vec4V x) { return Vec3V_From_Vec4V_WUndefined(x); } + PX_FORCE_INLINE Vec4V V4FromV3(Vec3V x) { return Vec4V_From_Vec3V(x); } + //PX_FORCE_INLINE Vec4V V4ClearW(Vec4V x) { return V4SetW(x, FZero()); } + + + void preprocessRows(Px1DConstraint** sorted, + Px1DConstraint* rows, + PxVec4* angSqrtInvInertia0, + PxVec4* angSqrtInvInertia1, + PxU32 rowCount, + const PxMat33& sqrtInvInertia0F32, + const PxMat33& sqrtInvInertia1F32, + const PxReal invMass0, + const PxReal invMass1, + const PxConstraintInvMassScale& ims, + bool disablePreprocessing, + bool diagonalizeDrive, + bool preprocessLinear = true); + + + +void setSolverConstantsStep(PxReal& error, + PxReal& biasScale, + PxReal& targetVel, + PxReal& maxBias, + PxReal& velMultiplier, + PxReal& impulseMultiplier, + PxReal& rcpResponse, + const Px1DConstraint& c, + PxReal normalVel, + PxReal unitResponse, + PxReal minRowResponse, + PxReal erp, + PxReal dt, + PxReal totalDt, + PxReal biasClamp, + PxReal recipdt, + PxReal recipTotalDt) +{ + PX_UNUSED(dt); + PX_UNUSED(totalDt); + PX_ASSERT(PxIsFinite(unitResponse)); + PxReal recipResponse = unitResponse <= minRowResponse ? 0 : 1.0f / unitResponse; + PX_ASSERT(recipResponse < 1e5f); + PxReal geomError = c.geometricError; + + rcpResponse = recipResponse; + + + + if (c.flags & Px1DConstraintFlag::eSPRING) + { + error = geomError; + + PxReal a = dt * (dt*c.mods.spring.stiffness + c.mods.spring.damping); + PxReal b = dt*(c.mods.spring.damping * c.velocityTarget /*- c.mods.spring.stiffness * geomError*/); + maxBias = biasClamp; + + if (c.flags & Px1DConstraintFlag::eACCELERATION_SPRING) + { + PxReal x = 1.0f / (1.0f + a); + targetVel = x * b; + velMultiplier = -x * a; + biasScale = -x * c.mods.spring.stiffness*dt; + //KS - impulse multiplier for TGS solver is 1 because we consider each pass to be a separate step, with the solver computing the impulse proportional to the step dt. + //Alternatively, we could consider the spring over totalDt and then use an impulseMultiplier of 1-x, but this would require the bias to be constant to allow the constraint + //to produce stiff results if the number of iterations was large + impulseMultiplier = 1.0f; + } + else + { + PxReal x = 1.0f / (1.0f + a*unitResponse); + targetVel = x * b*unitResponse; + velMultiplier = -x*a*unitResponse; + //KS - impulse multiplier for TGS solver is 1 because we consider each pass to be a separate step, with the solver computing the impulse proportional to the step dt. + //Alternatively, we could consider the spring over totalDt and then use an impulseMultiplier of 1-x, but this would require the bias to be constant to allow the constraint + //to produce stiff results if the number of iterations was large + biasScale = -x * c.mods.spring.stiffness*unitResponse*dt; + impulseMultiplier = 1.0f; + } + + } + else + { + velMultiplier = -1.f;// *recipResponse; + impulseMultiplier = 1.0f; + + if (c.flags & Px1DConstraintFlag::eRESTITUTION && -normalVel>c.mods.bounce.velocityThreshold) + { + error = 0.f; + biasScale = 0.f; + + targetVel = c.mods.bounce.restitution*-normalVel; + maxBias = 0.f; + } + else + { + + biasScale = -recipdt*erp;// *recipResponse; + if (c.flags & Px1DConstraintFlag::eDRIVE_ROW) + { + error = 0.f; + targetVel = c.velocityTarget - geomError *recipTotalDt; + } + else + { + error = geomError; + //KS - if there is a velocity target, then we cannot also have bias otherwise the two compete against each-other. + //Therefore, we set the velocity target + targetVel = c.velocityTarget;// *recipResponse; + } + + maxBias = biasClamp;// *recipResponse; + + /*PxReal errorBias = PxClamp(geomError*erp*recipdt, -biasClamp, biasClamp); + + constant = (c.velocityTarget - errorBias) * recipResponse;*/ + + } + } +} + + + + + +PxU32 setupSolverConstraintStep( + const PxTGSSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + const PxReal dt, const PxReal totalDt, const PxReal invdt, const PxReal invTotalDt, + const PxU32 nbSubsteps, + const PxReal lengthScale) +{ + PX_UNUSED(nbSubsteps); + //static const PxU32 MAX_CONSTRAINT_ROWS = 12; + + if (prepDesc.numRows == 0) + { + prepDesc.desc->constraint = NULL; + prepDesc.desc->writeBack = NULL; + prepDesc.desc->constraintLengthOver16 = 0; + prepDesc.desc->writeBackLengthOver4 = 0; + return 0; + } + + PxTGSSolverConstraintDesc& desc = *prepDesc.desc; + + const bool isExtended = desc.linkIndexA != PxTGSSolverConstraintDesc::NO_LINK + || desc.linkIndexB != PxTGSSolverConstraintDesc::NO_LINK; + + const bool isKinematic0 = desc.linkIndexA == PxTGSSolverConstraintDesc::NO_LINK && + desc.bodyA->isKinematic; + + const bool isKinematic1 = desc.linkIndexB == PxTGSSolverConstraintDesc::NO_LINK && + desc.bodyB->isKinematic; + + PxU32 stride = isExtended ? sizeof(SolverConstraint1DExtStep) : sizeof(SolverConstraint1DStep); + const PxU32 constraintLength = sizeof(SolverConstraint1DHeaderStep) + stride * prepDesc.numRows; + + //KS - +16 is for the constraint progress counter, which needs to be the last element in the constraint (so that we + //know SPU DMAs have completed) + PxU8* ptr = allocator.reserveConstraintData(constraintLength + 16u); + if (NULL == ptr || (reinterpret_cast(-1)) == ptr) + { + if (NULL == ptr) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept joints detaching/exploding or increase buffer size allocated for constraint prep by increasing PxSceneDesc::maxNbContactDataBlocks."); + return 0; + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of constraint data. " + "Either accept joints detaching/exploding or simplify constraints."); + ptr = NULL; + return 0; + } + } + desc.constraint = ptr; + + //setConstraintLength(desc, constraintLength); + PX_ASSERT((constraintLength & 0xf) == 0); + desc.constraintLengthOver16 = Ps::to16(constraintLength / 16); + + desc.writeBack = prepDesc.writeback; + desc.writeBackLengthOver4 = Ps::to16(sizeof(ConstraintWriteback)/4); + + memset(desc.constraint, 0, constraintLength); + + SolverConstraint1DHeaderStep* header = reinterpret_cast(desc.constraint); + PxU8* constraints = desc.constraint + sizeof(SolverConstraint1DHeaderStep); + init(*header, Ps::to8(prepDesc.numRows), isExtended, prepDesc.mInvMassScales); + header->body0WorldOffset = prepDesc.body0WorldOffset; + header->linBreakImpulse = prepDesc.linBreakForce * totalDt; + header->angBreakImpulse = prepDesc.angBreakForce * totalDt; + header->breakable = PxU8((prepDesc.linBreakForce != PX_MAX_F32) || (prepDesc.angBreakForce != PX_MAX_F32)); + header->invMass0D0 = prepDesc.bodyData0->invMass * prepDesc.mInvMassScales.linear0; + header->invMass1D1 = prepDesc.bodyData1->invMass * prepDesc.mInvMassScales.linear1; + + header->rAWorld = prepDesc.cA2w - prepDesc.bodyFrame0.p; + header->rBWorld = prepDesc.cB2w - prepDesc.bodyFrame1.p; + + Px1DConstraint* sorted[MAX_CONSTRAINT_ROWS]; + + PX_ALIGN(16, PxVec4) angSqrtInvInertia0[MAX_CONSTRAINT_ROWS]; + PX_ALIGN(16, PxVec4) angSqrtInvInertia1[MAX_CONSTRAINT_ROWS]; + + for (PxU32 i = 0; i < prepDesc.numRows; ++i) + { + if (prepDesc.rows[i].flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT) + { + if (prepDesc.rows[i].solveHint == PxConstraintSolveHint::eEQUALITY) + prepDesc.rows[i].solveHint = PxConstraintSolveHint::eROTATIONAL_EQUALITY; + else if (prepDesc.rows[i].solveHint == PxConstraintSolveHint::eINEQUALITY) + prepDesc.rows[i].solveHint = PxConstraintSolveHint::eROTATIONAL_INEQUALITY; + } + } + + preprocessRows(sorted, prepDesc.rows, angSqrtInvInertia0, angSqrtInvInertia1, prepDesc.numRows, + prepDesc.body0TxI->sqrtInvInertia, prepDesc.body1TxI->sqrtInvInertia, prepDesc.bodyData0->invMass, prepDesc.bodyData1->invMass, + prepDesc.mInvMassScales, isExtended || prepDesc.disablePreprocessing, prepDesc.improvedSlerp, false); + + PxReal erp = 1.f; + PxReal linearErp = 1.f; + + const PxReal recipDt = invdt; + + PxU32 orthoCount = 0; + for (PxU32 i = 0; i(constraints); + Px1DConstraint& c = *sorted[i]; + + PxReal driveScale = c.flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && prepDesc.driveLimitsAreForces ? PxMin(totalDt, 1.0f) : 1.0f; + + PxReal unitResponse; + PxReal normalVel = 0.0f; + + PxReal angSpeedLimit = 100.f; + PxReal linSpeedLimit = 1000.f; + + PxReal vel0, vel1; + + if (!isExtended) + { + const PxVec3 angSqrtInvInertia0V3(angSqrtInvInertia0[i].x, angSqrtInvInertia0[i].y, angSqrtInvInertia0[i].z); + const PxVec3 angSqrtInvInertia1V3(angSqrtInvInertia1[i].x, angSqrtInvInertia1[i].y, angSqrtInvInertia1[i].z); + init(s, c.linear0, c.linear1, c.angular0, c.angular1, c.minImpulse * driveScale, c.maxImpulse * driveScale); + + /*unitResponse = prepDesc.body0->getResponse(c.linear0, c.angular0, s.ang0, prepDesc.mInvMassScales.linear0, prepDesc.mInvMassScales.angular0) + + prepDesc.body1->getResponse(-c.linear1, -c.angular1, s.ang1, prepDesc.mInvMassScales.linear1, prepDesc.mInvMassScales.angular1);*/ + + const PxReal linSumMass = s.lin0.magnitudeSquared() * prepDesc.bodyData0->invMass * prepDesc.mInvMassScales.linear0 + s.lin1.magnitudeSquared() * prepDesc.bodyData1->invMass * prepDesc.mInvMassScales.linear1; + + PxReal resp0 = angSqrtInvInertia0V3.magnitudeSquared() * prepDesc.mInvMassScales.angular0; + PxReal resp1 = angSqrtInvInertia1V3.magnitudeSquared() * prepDesc.mInvMassScales.angular1; + unitResponse = resp0 + resp1 + linSumMass; + + //s.recipResponseOrLinearSumMass = linSumMass; + + vel0 = prepDesc.bodyData0->projectVelocity(s.lin0, s.ang0); + vel1 = prepDesc.bodyData1->projectVelocity(s.lin1, s.ang1); + + normalVel = vel0 - vel1; + + //if (c.solveHint & PxConstraintSolveHint::eEQUALITY) + if(!(c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT)) + { + s.ang0 = PxVec3(0.f); + s.ang1 = PxVec3(0.f); + s.angularErrorScale = 0.f; + } + + } + else + { + linearErp = 0.7f; + erp = 0.7f; + //angSpeedLimit = 25.f; + linSpeedLimit = 200.f; + const SolverExtBodyStep eb0(reinterpret_cast(prepDesc.body0), prepDesc.body0TxI, prepDesc.bodyData0, desc.linkIndexA); + const SolverExtBodyStep eb1(reinterpret_cast(prepDesc.body1), prepDesc.body1TxI, prepDesc.bodyData1, desc.linkIndexB); + const Cm::SpatialVector resp0 = createImpulseResponseVector(c.linear0, c.angular0, eb0); + const Cm::SpatialVector resp1 = createImpulseResponseVector(-c.linear1, -c.angular1, eb1); + + init(s, resp0.linear, -resp1.linear, resp0.angular, -resp1.angular, c.minImpulse * driveScale, c.maxImpulse * driveScale); + SolverConstraint1DExtStep& e = static_cast(s); + + Cm::SpatialVector& delta0 = unsimdRef(e.deltaVA); + Cm::SpatialVector& delta1 = unsimdRef(e.deltaVB); + + unitResponse = getImpulseResponse(eb0, resp0, delta0, prepDesc.mInvMassScales.linear0, prepDesc.mInvMassScales.angular0, + eb1, resp1, delta1, prepDesc.mInvMassScales.linear1, prepDesc.mInvMassScales.angular1, false); + + if (eb0.mLinkIndex != PxSolverConstraintDesc::NO_LINK && + eb1.mLinkIndex != PxSolverConstraintDesc::NO_LINK) + { + PxReal lr0 = delta0.linear.dot(resp0.linear); + PxReal ar0 = delta0.angular.dot(resp0.angular) * safeRecip(resp0.angular.magnitude()); + PxReal lr1 = delta1.linear.dot(resp1.linear); + PxReal ar1 = delta1.angular.dot(resp1.angular)* safeRecip(resp1.angular.magnitude()); + + Cm::SpatialVector rem0(delta0.linear - resp0.linear*lr0, delta0.angular - resp0.angular*ar0); + Cm::SpatialVector rem1(delta1.linear - resp1.linear*lr1, delta1.angular - resp1.angular*ar1); + + + PxReal rem = (rem0 - rem1).magnitude(); + + unitResponse += rem; + } + + + + { + vel0 = eb0.projectVelocity(s.lin0, s.ang0); + vel1 = eb1.projectVelocity(s.lin1, s.ang1); + + normalVel = vel0 - vel1; + } + + if (!(c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT)) + { + s.angularErrorScale = 0.f; + } + } + + PxReal recipResponse = 0.f; + + setSolverConstantsStep(s.error, s.biasScale, s.velTarget, s.maxBias, s.velMultiplier, s.impulseMultiplier, recipResponse, c, + normalVel, unitResponse, isExtended ? 1e-5f : prepDesc.minResponseThreshold, (c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT ? erp : linearErp), dt, totalDt, + c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT ? angSpeedLimit : linSpeedLimit*lengthScale, recipDt, invTotalDt); + + s.recipResponse = recipResponse; + + if(isKinematic0) + s.velTarget -= vel0; + if(isKinematic1) + s.velMultiplier += vel1; + + + + if (c.flags & Px1DConstraintFlag::eOUTPUT_FORCE) + s.flags |= DY_SC_FLAG_OUTPUT_FORCE; + + if ((c.flags & Px1DConstraintFlag::eKEEPBIAS)) + s.flags |= DY_SC_FLAG_KEEP_BIAS; + if (c.solveHint & 1) + s.flags |= DY_SC_FLAG_INEQUALITY; + + if (!(isExtended || prepDesc.disablePreprocessing)) + { + //KS - the code that orthogonalizes constraints on-the-fly only works if the linear and angular constraints have already been pre-orthogonalized + if (c.solveHint == PxConstraintSolveHint::eROTATIONAL_EQUALITY) + { + s.flags |= DY_SC_FLAG_ROT_EQ; + + PX_ASSERT(orthoCount < 3); + + /*angOrtho0[orthoCount] = PxVec3(angSqrtInvInertia0[i].x, angSqrtInvInertia0[i].y, angSqrtInvInertia0[i].z); + angOrtho1[orthoCount] = PxVec3(angSqrtInvInertia1[i].x, angSqrtInvInertia1[i].y, angSqrtInvInertia1[i].z); + recipResponses[orthoCount] = recipResponse;*/ + + header->angOrthoAxis0_recipResponseW[orthoCount] = + PxVec4(angSqrtInvInertia0[i].x*prepDesc.mInvMassScales.angular0, angSqrtInvInertia0[i].y*prepDesc.mInvMassScales.angular0, angSqrtInvInertia0[i].z*prepDesc.mInvMassScales.angular0, recipResponse); + header->angOrthoAxis1_Error[orthoCount].x = angSqrtInvInertia1[i].x*prepDesc.mInvMassScales.angular1; + header->angOrthoAxis1_Error[orthoCount].y = angSqrtInvInertia1[i].y*prepDesc.mInvMassScales.angular1; + header->angOrthoAxis1_Error[orthoCount].z = angSqrtInvInertia1[i].z*prepDesc.mInvMassScales.angular1; + header->angOrthoAxis1_Error[orthoCount].w = c.geometricError; + + orthoCount++; + } + + else if (c.solveHint & PxConstraintSolveHint::eEQUALITY) + s.flags |= DY_SC_FLAG_ORTHO_TARGET; + } + + + + + constraints += stride; + } + + + //KS - Set the solve count at the end to 0 + *(reinterpret_cast(constraints)) = 0; + *(reinterpret_cast(constraints + 4)) = 0; + PX_ASSERT(desc.constraint + getConstraintLength(desc) == constraints); + return prepDesc.numRows; +} + +PxU32 SetupSolverConstraintStep(SolverConstraintShaderPrepDesc& shaderDesc, + PxTGSSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + const PxReal dt, const PxReal totalDt, const PxReal invdt, const PxReal invTotalDt, const PxU32 nbSubsteps, + const PxReal lengthScale) +{ + // LL shouldn't see broken constraints + + PX_ASSERT(!(reinterpret_cast(prepDesc.writeback)->broken)); + + prepDesc.desc->constraintLengthOver16 = 0; + //setConstraintLength(*prepDesc.desc, 0); + + if (!shaderDesc.solverPrep) + return 0; + + //PxU32 numAxisConstraints = 0; + + Px1DConstraint rows[MAX_CONSTRAINT_ROWS]; + + // This is necessary so that there will be sensible defaults and shaders will + // continue to work (albeit with a recompile) if the row format changes. + // It's a bit inefficient because it fills in all constraint rows even if there + // is only going to be one generated. A way around this would be for the shader to + // specify the maximum number of rows it needs, or it could call a subroutine to + // prep the row before it starts filling it it. + + PxMemZero(rows, sizeof(Px1DConstraint)*MAX_CONSTRAINT_ROWS); + + for (PxU32 i = 0; iisKinematic) + prepDesc.mInvMassScales.angular0 = 0.f; + if (prepDesc.bodyState1 != PxSolverContactDesc::eARTICULATION && prepDesc.body1->isKinematic) + prepDesc.mInvMassScales.angular1 = 0.f; + + + return setupSolverConstraintStep(prepDesc, allocator, dt, totalDt, invdt, invTotalDt, nbSubsteps, lengthScale); +} + +//Port of scalar implementation to SIMD maths with some interleaving of instructions +void solveExt1DStep(const PxTGSSolverConstraintDesc& desc, const PxReal elapsedTimeF32, SolverContext& cache, + const PxTGSSolverBodyTxInertia* const txInertias) +{ + PxU8* PX_RESTRICT bPtr = desc.constraint; + //PxU32 length = desc.constraintLength; + + const SolverConstraint1DHeaderStep* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DExtStep* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeaderStep)); + + const FloatV elapsedTime = FLoad(elapsedTimeF32); + + Vec3V linVel0, angVel0, linVel1, angVel1; + Vec3V linMotion0, angMotion0, linMotion1, angMotion1; + + QuatV rotA, rotB; + + if (desc.articulationA == desc.articulationB) + { + Cm::SpatialVectorV v0, v1; + desc.articulationA->pxcFsGetVelocities(desc.linkIndexA, desc.linkIndexB, v0, v1); + linVel0 = v0.linear; + angVel0 = v0.angular; + linVel1 = v1.linear; + angVel1 = v1.angular; + + Cm::SpatialVectorV motionV0 = PxcFsGetMotionVector(*desc.articulationA, desc.linkIndexA); + Cm::SpatialVectorV motionV1 = PxcFsGetMotionVector(*desc.articulationB, desc.linkIndexB); + + linMotion0 = motionV0.linear; + angMotion0 = motionV0.angular; + linMotion1 = motionV1.linear; + angMotion1 = motionV1.angular; + + rotA = Ps::aos::QuatVLoadU(&desc.articulationA->getDeltaQ(desc.linkIndexA).x); + rotB = Ps::aos::QuatVLoadU(&desc.articulationB->getDeltaQ(desc.linkIndexB).x); + } + else + { + + if (desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + linVel0 = V3LoadA(desc.bodyA->linearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularVelocity); + linMotion0 = V3LoadA(desc.bodyA->deltaLinDt); + angMotion0 = V3LoadA(desc.bodyA->deltaAngDt); + rotA = Ps::aos::QuatVLoadA(&txInertias[desc.bodyAIdx].deltaBody2World.q.x); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + rotA = Ps::aos::QuatVLoadU(&desc.articulationA->getDeltaQ(desc.linkIndexA).x); + Cm::SpatialVectorV motionV = PxcFsGetMotionVector(*desc.articulationA, desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + + linMotion0 = motionV.linear; + angMotion0 = motionV.angular; + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularVelocity); + linMotion1 = V3LoadA(desc.bodyB->deltaLinDt); + angMotion1 = V3LoadA(desc.bodyB->deltaAngDt); + rotB = Ps::aos::QuatVLoadA(&txInertias[desc.bodyBIdx].deltaBody2World.q.x); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + rotB = Ps::aos::QuatVLoadU(&desc.articulationB->getDeltaQ(desc.linkIndexB).x); + Cm::SpatialVectorV motionV = PxcFsGetMotionVector(*desc.articulationB, desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + + linMotion1 = motionV.linear; + angMotion1 = motionV.angular; + } + } + + /*PX_ASSERT(FAllGrtr(FLoad(40.f * 40.f), V3Dot(linVel0, linVel0))); + PX_ASSERT(FAllGrtr(FLoad(40.f * 40.f), V3Dot(linVel1, linVel1))); + PX_ASSERT(FAllGrtr(FLoad(20.f * 20.f), V3Dot(angVel0, angVel0))); + PX_ASSERT(FAllGrtr(FLoad(20.f * 20.f), V3Dot(angVel1, angVel1)));*/ + + const Vec3V raPrev = V3LoadA(header->rAWorld); + const Vec3V rbPrev = V3LoadA(header->rBWorld); + + const Vec3V ra = QuatRotate(rotA, V3LoadA(header->rAWorld)); + const Vec3V rb = QuatRotate(rotB, V3LoadA(header->rBWorld)); + + const Vec3V raMotion = V3Sub(V3Add(ra, linMotion0), raPrev); + const Vec3V rbMotion = V3Sub(V3Add(rb, linMotion1), rbPrev); + + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + for (PxU32 i = 0; icount; ++i, base++) + { + Ps::prefetchLine(base + 1); + + SolverConstraint1DExtStep& c = *base; + + const Vec3V clinVel0 = V3LoadA(c.lin0); + const Vec3V clinVel1 = V3LoadA(c.lin1); + + const Vec3V cangVel0 = V3LoadA(c.ang0); + const Vec3V cangVel1 = V3LoadA(c.ang1); + + const FloatV recipResponse = FLoad(c.recipResponse); + + const FloatV targetVel = FLoad(c.velTarget); + + const FloatV deltaAng = FMul(FSub(V3Dot(cangVel0, angMotion0), V3Dot(cangVel1, angMotion1)), FLoad(c.angularErrorScale)); + const FloatV error = FNegScaleSub(targetVel, elapsedTime, FAdd(FAdd(FLoad(c.error), FSub(V3Dot(raMotion, clinVel0), V3Dot(rbMotion, clinVel1))), deltaAng)); + + const FloatV biasScale = FLoad(c.biasScale); + const FloatV maxBias = FLoad(c.maxBias); + + const FloatV vMul = FMul(recipResponse, FLoad(c.velMultiplier)); + const FloatV iMul = FLoad(c.impulseMultiplier); + const FloatV appliedForce = FLoad(c.appliedForce); + + const FloatV unclampedBias = FMul(error, biasScale); + const FloatV minBias = c.flags & DY_SC_FLAG_INEQUALITY ? FNeg(FMax()) : FNeg(maxBias); + const FloatV bias = FClamp(unclampedBias, minBias, maxBias); + + const FloatV constant = FMul(recipResponse, FAdd(bias, targetVel)); + + const FloatV maxImpulse = FLoad(c.maxImpulse); + const FloatV minImpulse = FLoad(c.minImpulse); + + const Vec3V v0 = V3MulAdd(linVel0, clinVel0, V3Mul(angVel0, cangVel0)); + const Vec3V v1 = V3MulAdd(linVel1, clinVel1, V3Mul(angVel1, cangVel1)); + const FloatV normalVel = V3SumElems(V3Sub(v0, v1)); + + const FloatV iM = FMul(iMul, appliedForce); + + const FloatV unclampedForce = FAdd(iM, FScaleAdd(vMul, normalVel, constant)); + const FloatV clampedForce = FMin(maxImpulse, (FMax(minImpulse, unclampedForce))); + const FloatV deltaF = FSub(clampedForce, appliedForce); + + FStore(clampedForce, &c.appliedForce); + + //PX_ASSERT(FAllGrtr(FLoad(1000.f), FAbs(deltaF))); + + + FStore(clampedForce, &base->appliedForce); + li0 = V3ScaleAdd(clinVel0, deltaF, li0); ai0 = V3ScaleAdd(cangVel0, deltaF, ai0); + li1 = V3ScaleAdd(clinVel1, deltaF, li1); ai1 = V3ScaleAdd(cangVel1, deltaF, ai1); + + linVel0 = V3ScaleAdd(base->deltaVA.linear, deltaF, linVel0); angVel0 = V3ScaleAdd(base->deltaVA.angular, deltaF, angVel0); + linVel1 = V3ScaleAdd(base->deltaVB.linear, deltaF, linVel1); angVel1 = V3ScaleAdd(base->deltaVB.angular, deltaF, angVel1); + + /*PX_ASSERT(FAllGrtr(FLoad(40.f * 40.f), V3Dot(linVel0, linVel0))); + PX_ASSERT(FAllGrtr(FLoad(40.f * 40.f), V3Dot(linVel1, linVel1))); + PX_ASSERT(FAllGrtr(FLoad(20.f * 20.f), V3Dot(angVel0, angVel0))); + PX_ASSERT(FAllGrtr(FLoad(20.f * 20.f), V3Dot(angVel1, angVel1)));*/ + } + + if (desc.articulationA == desc.articulationB) + { + desc.articulationA->pxcFsApplyImpulses(desc.linkIndexA, V3Scale(li0, FLoad(header->linearInvMassScale0)), + V3Scale(ai0, FLoad(header->angularInvMassScale0)), desc.linkIndexB, V3Scale(li1, FLoad(header->linearInvMassScale1)), + V3Scale(ai1, FLoad(header->angularInvMassScale1)), cache.Z, cache.deltaV); + } + else + { + + if (desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularVelocity); + } + else + { + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, V3Scale(li0, FLoad(header->linearInvMassScale0)), + V3Scale(ai0, FLoad(header->angularInvMassScale0)), cache.Z, cache.deltaV); + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularVelocity); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, V3Scale(li1, FLoad(header->linearInvMassScale1)), + V3Scale(ai1, FLoad(header->angularInvMassScale1)), cache.Z, cache.deltaV); + } + } +} + + + + +//Port of scalar implementation to SIMD maths with some interleaving of instructions +void solve1DStep(const PxTGSSolverConstraintDesc& desc, const PxTGSSolverBodyTxInertia* const txInertias, const PxReal elapsedTime) +{ + PxU8* PX_RESTRICT bPtr = desc.constraint; + if (bPtr == NULL) + return; + + PxTGSSolverBodyVel& b0 = *desc.bodyA; + PxTGSSolverBodyVel& b1 = *desc.bodyB; + + const FloatV elapsed = FLoad(elapsedTime); + + + const PxTGSSolverBodyTxInertia& txI0 = txInertias[desc.bodyAIdx]; + const PxTGSSolverBodyTxInertia& txI1 = txInertias[desc.bodyBIdx]; + + const SolverConstraint1DHeaderStep* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DStep* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeaderStep)); + + Vec3V linVel0 = V3LoadA(b0.linearVelocity); + Vec3V linVel1 = V3LoadA(b1.linearVelocity); + Vec3V angState0 = V3LoadA(b0.angularVelocity); + Vec3V angState1 = V3LoadA(b1.angularVelocity); + + Mat33V sqrtInvInertia0 = Mat33V(V3LoadU(txI0.sqrtInvInertia.column0), V3LoadU(txI0.sqrtInvInertia.column1), V3LoadU(txI0.sqrtInvInertia.column2)); + Mat33V sqrtInvInertia1 = Mat33V(V3LoadU(txI1.sqrtInvInertia.column0), V3LoadU(txI1.sqrtInvInertia.column1), V3LoadU(txI1.sqrtInvInertia.column2)); + + + const FloatV invMass0 = FLoad(header->invMass0D0); + const FloatV invMass1 = FLoad(header->invMass1D1); + const FloatV invInertiaScale0 = FLoad(header->angularInvMassScale0); + const FloatV invInertiaScale1 = FLoad(header->angularInvMassScale1); + + + const QuatV deltaRotA = Ps::aos::QuatVLoadA(&txI0.deltaBody2World.q.x); + const QuatV deltaRotB = Ps::aos::QuatVLoadA(&txI1.deltaBody2World.q.x); + + const Vec3V raPrev = V3LoadA(header->rAWorld); + const Vec3V rbPrev = V3LoadA(header->rBWorld); + + const Vec3V ra = QuatRotate(deltaRotA, raPrev); + const Vec3V rb = QuatRotate(deltaRotB, rbPrev); + + + const Vec3V ang0 = V3LoadA(b0.deltaAngDt); + const Vec3V ang1 = V3LoadA(b1.deltaAngDt); + + const Vec3V lin0 = V3LoadA(b0.deltaLinDt); + const Vec3V lin1 = V3LoadA(b1.deltaLinDt); + + const Vec3V raMotion = V3Sub(V3Add(ra, lin0), raPrev); + const Vec3V rbMotion = V3Sub(V3Add(rb, lin1), rbPrev); + + const VecCrossV raCross = V3PrepareCross(ra); + const VecCrossV rbCross = V3PrepareCross(rb); + + const Vec4V ang0Ortho0_recipResponseW = V4LoadA(&header->angOrthoAxis0_recipResponseW[0].x); + const Vec4V ang0Ortho1_recipResponseW = V4LoadA(&header->angOrthoAxis0_recipResponseW[1].x); + const Vec4V ang0Ortho2_recipResponseW = V4LoadA(&header->angOrthoAxis0_recipResponseW[2].x); + + const Vec4V ang1Ortho0_Error0 = V4LoadA(&header->angOrthoAxis1_Error[0].x); + const Vec4V ang1Ortho1_Error1 = V4LoadA(&header->angOrthoAxis1_Error[1].x); + const Vec4V ang1Ortho2_Error2 = V4LoadA(&header->angOrthoAxis1_Error[2].x); + + const FloatV recipResponse0 = V4GetW(ang0Ortho0_recipResponseW); + const FloatV recipResponse1 = V4GetW(ang0Ortho1_recipResponseW); + const FloatV recipResponse2 = V4GetW(ang0Ortho2_recipResponseW); + + const Vec3V ang0Ortho0 = Vec3V_From_Vec4V(ang0Ortho0_recipResponseW); + const Vec3V ang0Ortho1 = Vec3V_From_Vec4V(ang0Ortho1_recipResponseW); + const Vec3V ang0Ortho2 = Vec3V_From_Vec4V(ang0Ortho2_recipResponseW); + + const Vec3V ang1Ortho0 = Vec3V_From_Vec4V(ang1Ortho0_Error0); + const Vec3V ang1Ortho1 = Vec3V_From_Vec4V(ang1Ortho1_Error1); + const Vec3V ang1Ortho2 = Vec3V_From_Vec4V(ang1Ortho2_Error2); + + FloatV error0 = FAdd(V4GetW(ang1Ortho0_Error0), FSub(V3Dot(ang0Ortho0, ang0), V3Dot(ang1Ortho0, ang1))); + FloatV error1 = FAdd(V4GetW(ang1Ortho1_Error1), FSub(V3Dot(ang0Ortho1, ang0), V3Dot(ang1Ortho1, ang1))); + FloatV error2 = FAdd(V4GetW(ang1Ortho2_Error2), FSub(V3Dot(ang0Ortho2, ang0), V3Dot(ang1Ortho2, ang1))); + + for (PxU32 i = 0; icount; ++i, base++) + { + Ps::prefetchLine(base + 1); + SolverConstraint1DStep& c = *base; + + const Vec3V clinVel0 = V3LoadA(c.lin0); + const Vec3V clinVel1 = V3LoadA(c.lin1); + + const Vec3V cangVel0_ = V3LoadA(c.ang0); + const Vec3V cangVel1_ = V3LoadA(c.ang1); + + const FloatV angularErrorScale = FLoad(c.angularErrorScale); + + const FloatV biasScale = FLoad(c.biasScale); + const FloatV maxBias = FLoad(c.maxBias); + const FloatV targetVel = FLoad(c.velTarget); + const FloatV iMul = FLoad(c.impulseMultiplier); + const FloatV appliedForce = FLoad(c.appliedForce); + const FloatV velMultiplier = FLoad(c.velMultiplier); + + const FloatV maxImpulse = FLoad(c.maxImpulse); + const FloatV minImpulse = FLoad(c.minImpulse); + + Vec3V cangVel0 = V3Add(cangVel0_, V3Cross(raCross, clinVel0)); + Vec3V cangVel1 = V3Add(cangVel1_, V3Cross(rbCross, clinVel1)); + + + FloatV error = FLoad(c.error); + + const FloatV minBias = (c.flags & DY_SC_FLAG_INEQUALITY) ? FNeg(FMax()) : FNeg(maxBias); + + + Vec3V raXnI = M33MulV3(sqrtInvInertia0, cangVel0); + Vec3V rbXnI = M33MulV3(sqrtInvInertia1, cangVel1); + + if (c.flags & DY_SC_FLAG_ORTHO_TARGET) + { + //Re-orthogonalize the constraints before velocity projection and impulse response calculation + //Can be done in using instruction parallelism because angular locked axes are orthogonal to linear axes! + + const FloatV proj0 = FMul(V3SumElems(V3MulAdd(raXnI, ang0Ortho0, + V3Mul(rbXnI, ang1Ortho0))), recipResponse0); + + const FloatV proj1 = FMul(V3SumElems(V3MulAdd(raXnI, ang0Ortho1, + V3Mul(rbXnI, ang1Ortho1))), recipResponse1); + const FloatV proj2 = FMul(V3SumElems(V3MulAdd(raXnI, ang0Ortho2, + V3Mul(rbXnI, ang1Ortho2))), recipResponse2); + + const Vec3V delta0 = V3ScaleAdd(ang0Ortho0, proj0, V3ScaleAdd(ang0Ortho1, proj1, V3Scale(ang0Ortho2, proj2))); + const Vec3V delta1 = V3ScaleAdd(ang1Ortho0, proj0, V3ScaleAdd(ang1Ortho1, proj1, V3Scale(ang1Ortho2, proj2))); + + raXnI = V3Sub(raXnI, delta0); + rbXnI = V3Sub(rbXnI, delta1); + + error = FSub(error, FScaleAdd(error0, proj0, FScaleAdd(error1, proj1, FMul(error2, proj2)))); + } + + const FloatV deltaAng = FMul(angularErrorScale, FSub(V3Dot(raXnI, ang0), V3Dot(rbXnI, ang1))); + + error = FNegScaleSub(targetVel, elapsed, FAdd(FAdd(error, FSub(V3Dot(raMotion, clinVel0), V3Dot(rbMotion, clinVel1))), deltaAng)); + + const FloatV resp0 = FScaleAdd(invMass0, V3Dot(clinVel0, clinVel0), V3SumElems(V3Mul(V3Scale(raXnI, invInertiaScale0), raXnI))); + const FloatV resp1 = FSub(FMul(invMass1, V3Dot(clinVel1, clinVel1)), V3SumElems(V3Mul(V3Scale(rbXnI, invInertiaScale1), rbXnI))); + + const FloatV response = FAdd(resp0, resp1); + const FloatV recipResponse = FSel(FIsGrtr(response, FZero()), FRecip(response), FZero()); + + const FloatV vMul = FMul(recipResponse, velMultiplier); + + const FloatV unclampedBias = FMul(error, biasScale); + const FloatV bias = FClamp(unclampedBias, minBias, maxBias); + + const FloatV constant = FMul(recipResponse, FAdd(bias, targetVel)); + + const Vec3V v0 = V3MulAdd(linVel0, clinVel0, V3Mul(angState0, raXnI)); + const Vec3V v1 = V3MulAdd(linVel1, clinVel1, V3Mul(angState1, rbXnI)); + const FloatV normalVel = V3SumElems(V3Sub(v0, v1)); + + const FloatV af = FMul(iMul, appliedForce); + + const FloatV unclampedForce = FAdd(af, FScaleAdd(vMul, normalVel, constant)); + const FloatV clampedForce = FClamp(unclampedForce, minImpulse, maxImpulse); + const FloatV deltaF = FSub(clampedForce, appliedForce); + + FStore(clampedForce, &c.appliedForce); + linVel0 = V3ScaleAdd(clinVel0, FMul(deltaF, invMass0), linVel0); + linVel1 = V3NegScaleSub(clinVel1, FMul(deltaF, invMass1), linVel1); + angState0 = V3ScaleAdd(raXnI, FMul(deltaF, invInertiaScale0), angState0); + angState1 = V3ScaleAdd(rbXnI, FMul(deltaF, invInertiaScale1), angState1); + + } + + V3StoreA(linVel0, b0.linearVelocity); + V3StoreA(angState0, b0.angularVelocity); + V3StoreA(linVel1, b1.linearVelocity); + V3StoreA(angState1, b1.angularVelocity); + + PX_ASSERT(b0.linearVelocity.isFinite()); + PX_ASSERT(b0.angularVelocity.isFinite()); + PX_ASSERT(b1.linearVelocity.isFinite()); + PX_ASSERT(b1.angularVelocity.isFinite()); +} + + +//Port of scalar implementation to SIMD maths with some interleaving of instructions +void conclude1DStep(const PxTGSSolverConstraintDesc& desc) +{ + PxU8* PX_RESTRICT bPtr = desc.constraint; + if (bPtr == NULL) + return; + + const SolverConstraint1DHeaderStep* PX_RESTRICT header = reinterpret_cast(bPtr); + PxU8* PX_RESTRICT base = bPtr + sizeof(SolverConstraint1DHeaderStep); + const PxU32 stride = header->type == DY_SC_TYPE_RB_1D ? sizeof(SolverConstraint1DStep) : sizeof(SolverConstraint1DExtStep); + + for (PxU32 i = 0; icount; ++i, base+=stride) + { + SolverConstraint1DStep& c = *reinterpret_cast(base); + Ps::prefetchLine(&c + 1); + if (!(c.flags & DY_SC_FLAG_KEEP_BIAS)) + c.biasScale = 0.f; + } +} + +void concludeContact(const PxTGSSolverConstraintDesc& desc) +{ + PX_UNUSED(desc); + //const PxU8* PX_RESTRICT last = desc.constraint + getConstraintLength(desc); + + ////hopefully pointer aliasing doesn't bite. + //PxU8* PX_RESTRICT currPtr = desc.constraint; + + //SolverContactHeaderStep* PX_RESTRICT firstHdr = reinterpret_cast(currPtr); + + //bool isExtended = firstHdr->type == DY_SC_TYPE_EXT_CONTACT; + + //const PxU32 contactStride = isExtended ? sizeof(SolverContactPointStepExt) : sizeof(SolverContactPointStep); + //const PxU32 frictionStride = isExtended ? sizeof(SolverContactFrictionStepExt) : sizeof(SolverContactFrictionStep); + + + //while (currPtr < last) + //{ + // SolverContactHeaderStep* PX_RESTRICT hdr = reinterpret_cast(currPtr); + // currPtr += sizeof(SolverContactHeaderStep); + + // const PxU32 numNormalConstr = hdr->numNormalConstr; + // const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + // /*PxU8* PX_RESTRICT contacts = currPtr; + // Ps::prefetchLine(contacts);*/ + // currPtr += numNormalConstr * contactStride; + + // //PxF32* forceBuffer = reinterpret_cast(currPtr); + // currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + // PxU8* PX_RESTRICT frictions = currPtr; + // currPtr += numFrictionConstr * frictionStride; + + // /*for (PxU32 i = 0; i < numNormalConstr; ++i) + // { + // SolverContactPointStep& c = *reinterpret_cast(contacts); + // contacts += contactStride; + // if(c.separation <= 0.f) + // c.biasCoefficient = 0.f; + // }*/ + + // for (PxU32 i = 0; i < numFrictionConstr; ++i) + // { + // SolverContactFrictionStep& f = *reinterpret_cast(frictions); + // frictions += frictionStride; + // f.biasScale = 0.f; + // } + //} + + //PX_ASSERT(currPtr == last); +} + + + + +void writeBack1D(const PxTGSSolverConstraintDesc& desc) +{ + ConstraintWriteback* writeback = reinterpret_cast(desc.writeBack); + if (writeback) + { + SolverConstraint1DHeaderStep* header = reinterpret_cast(desc.constraint); + PxU8* base = desc.constraint + sizeof(SolverConstraint1DHeaderStep); + PxU32 stride = header->type == DY_SC_TYPE_EXT_1D ? sizeof(SolverConstraint1DExtStep) : sizeof(SolverConstraint1DStep); + + PxVec3 lin(0), ang(0); + for (PxU32 i = 0; icount; i++) + { + const SolverConstraint1DStep* c = reinterpret_cast(base); + if (c->flags & DY_SC_FLAG_OUTPUT_FORCE) + { + lin += c->lin0 * c->appliedForce; + ang += (c->ang0 + c->lin0.cross(header->rAWorld)) * c->appliedForce; + } + base += stride; + } + + ang -= header->body0WorldOffset.cross(lin); + writeback->linearImpulse = lin; + writeback->angularImpulse = ang; + writeback->broken = header->breakable ? PxU32(lin.magnitude()>header->linBreakImpulse || ang.magnitude()>header->angBreakImpulse) : 0; + + PX_ASSERT(desc.constraint + (desc.constraintLengthOver16 * 16) == base); + } +} + + +PxU32 Articulation::setupSolverConstraintsTGS(const ArticulationSolverDesc& articDesc, + PxcConstraintBlockStream& stream, + PxTGSSolverConstraintDesc* constraintDesc, + PxReal dt, + PxReal invDt, + PxReal, + PxU32& acCount, + PxsConstraintBlockManager& constraintBlockManager, + Cm::SpatialVectorF* /*Z*/) +{ + Articulation* articulation = static_cast(articDesc.articulation); + FsData& fsData = *articulation->getFsDataPtr(); + const PxU32 solverDataSize = articDesc.solverDataSize; + const ArticulationLink* links = articDesc.links; + + PxcFsScratchAllocator allocator(articDesc.scratchMemory, articDesc.scratchMemorySize); + FsInertia* PX_RESTRICT baseInertia = allocator.alloc(articDesc.linkCount); + PX_UNUSED(baseInertia); + ArticulationJointTransforms* PX_RESTRICT jointTransforms = allocator.alloc(articDesc.linkCount); + + PX_UNUSED(dt); + acCount = 0; + + const PxU16 linkCount = fsData.linkCount; + PxU32 descCount = 0; + const PxReal recipDt = invDt; + + const PxConstraintInvMassScale ims(1.0f, 1.0f, 1.0f, 1.0f); + + for (PxU16 i = 1; i(*links[i].inboundJoint); + + if (i + 10 || j.tangentialDamping>0); + + const PxVec3 twistAxis = jointTransforms[i].cB2w.rotate(PxVec3(1.0f, 0, 0)); + const PxReal tqTwistAngle = Ps::tanHalf(twist.x, twist.w); + + const bool twistLowerLimited = j.twistLimited && tqTwistAngle < Cm::tanAdd(j.tanQTwistLow, j.tanQTwistPad); + const bool twistUpperLimited = j.twistLimited && tqTwistAngle > Cm::tanAdd(j.tanQTwistHigh, -j.tanQTwistPad); + + const PxU8 constraintCount = PxU8(swingLimited + tangentialStiffness + twistUpperLimited + twistLowerLimited); + if (!constraintCount) + continue; + + PxTGSSolverConstraintDesc& desc = constraintDesc[descCount++]; + + desc.articulationA = articulation; + desc.linkIndexA = Ps::to16(links[i].parent); + desc.articulationALength = Ps::to16(solverDataSize); + + desc.articulationB = articulation; + desc.linkIndexB = i; + desc.articulationBLength = Ps::to16(solverDataSize); + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeaderStep) + + sizeof(SolverConstraint1DExtStep) * constraintCount; + + PX_ASSERT(0 == (constraintLength & 0x0f)); + desc.constraintLengthOver16 = Ps::to16(constraintLength / 16); + + desc.constraint = stream.reserve(constraintLength + 16u, constraintBlockManager); + + desc.writeBack = NULL; + + SolverConstraint1DHeaderStep* header = reinterpret_cast(desc.constraint); + SolverConstraint1DExtStep* constraints = reinterpret_cast(desc.constraint + sizeof(SolverConstraint1DHeaderStep)); + + init(*header, constraintCount, true, ims); + + header->rAWorld = PxVec3(0.f); + header->rBWorld = PxVec3(0.f); + + PxU32 cIndex = 0; + + if (swingLimited) + { + const PxVec3 normal = jointTransforms[i].cA2w.rotate(swingLimitAxis); + ArticulationHelper::createHardLimitTGS(fsData, links, i, constraints[cIndex++], normal, swingLimitError, recipDt); + if (tangentialStiffness) + { + const PxVec3 tangent = twistAxis.cross(normal).getNormalized(); + ArticulationHelper::createTangentialSpringTGS(fsData, links, i, constraints[cIndex++], tangent, j.tangentialStiffness, j.tangentialDamping, 1.f/recipDt); + } + } + + if (twistUpperLimited) + ArticulationHelper::createHardLimitTGS(fsData, links, i, constraints[cIndex++], twistAxis, (j.tanQTwistHigh - tqTwistAngle) * 4, recipDt); + + if (twistLowerLimited) + ArticulationHelper::createHardLimitTGS(fsData, links, i, constraints[cIndex++], -twistAxis, -(j.tanQTwistLow - tqTwistAngle) * 4, recipDt); + + *(desc.constraint + getConstraintLength(desc)) = 0; + + PX_ASSERT(cIndex == constraintCount); + acCount += constraintCount; + } + + return descCount; + +} + + +void ArticulationHelper::createHardLimitTGS(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal err, + PxReal recipDt) +{ + PxReal error = err; + init(s, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); + + ArticulationHelper::getImpulseSelfResponse(fsData, + links[linkIndex].parent, Cm::SpatialVector(PxVec3(0), axis), s.deltaVA, + linkIndex, Cm::SpatialVector(PxVec3(0), -axis), s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if (unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, joint limit ignored"); + + const PxReal recipResponse = unitResponse>0.0f ? 1.0f / unitResponse : 0.0f; + + s.error = error; + s.biasScale = -recipDt*0.7f; + s.maxBias = PX_MAX_F32; + s.velMultiplier = -1.f; + s.recipResponse = recipResponse; + s.impulseMultiplier = 1.0f; + s.velTarget = 0.f; +} + +void ArticulationHelper::createTangentialSpringTGS(const FsData& fsData, + const ArticulationLink* links, + PxU32 linkIndex, + SolverConstraint1DExtStep& s, + const PxVec3& axis, + PxReal stiffness, + PxReal damping, + PxReal dt) +{ + init(s, PxVec3(0), PxVec3(0), axis, axis, -PX_MAX_F32, PX_MAX_F32); + + Cm::SpatialVector axis6(PxVec3(0), axis); + PxU32 parent = links[linkIndex].parent; + getImpulseSelfResponse(fsData, parent, axis6, s.deltaVA, linkIndex, -axis6, s.deltaVB); + + const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); + if (unitResponse<0.0f) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, tangential spring ignored"); + const PxReal recipResponse = unitResponse>0.0F ? 1.0f / unitResponse : 0.0f; + + // this is a specialization of the spring code in setSolverConstants() for acceleration springs. + // general case is b = dt * (c.mods.spring.damping * c.velocityTarget - c.mods.spring.stiffness * geomError); + // but geomError and velocityTarget are both zero + + const PxReal a = dt * dt * stiffness + dt * damping; + const PxReal x = 1.0f / (1.0f + a); + s.error = 0.f; + s.biasScale = 0.f; + s.maxBias = 0.f; + s.velMultiplier = -x * a; + s.impulseMultiplier = 1.0f - x; + s.velTarget = 0.f; + s.recipResponse = recipResponse; +} + +static FloatV solveExtContactsStep(SolverContactPointStepExt* contacts, const PxU32 nbContactPoints, const Vec3VArg contactNormal, + Vec3V& linVel0, Vec3V& angVel0, + Vec3V& linVel1, Vec3V& angVel1, + Vec3V& li0, Vec3V& ai0, + Vec3V& li1, Vec3V& ai1, + const Vec3V& linDeltaA, const Vec3V& linDeltaB, const Vec3V& angDeltaA, const Vec3V angDeltaB, const FloatV& maxPenBias, + PxF32* PX_RESTRICT appliedForceBuffer, + const FloatV& minPen, + const FloatV& elapsedTime) +{ + + const FloatV deltaV = V3Dot(contactNormal, V3Sub(linDeltaA, linDeltaB)); + + FloatV accumulatedNormalImpulse = FZero(); + for (PxU32 i = 0; ilinearVelocity); + angVel0 = V3LoadA(desc.bodyA->angularVelocity); + linDelta0 = V3LoadA(desc.bodyA->deltaLinDt); + angDelta0 = V3LoadA(desc.bodyA->deltaAngDt); + } + else + { + Cm::SpatialVectorV v = desc.articulationA->pxcFsGetVelocity(desc.linkIndexA); + Cm::SpatialVectorV deltaV = PxcFsGetMotionVector(*desc.articulationA, desc.linkIndexA); + linVel0 = v.linear; + angVel0 = v.angular; + linDelta0 = deltaV.linear; + angDelta0 = deltaV.angular; + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + linVel1 = V3LoadA(desc.bodyB->linearVelocity); + angVel1 = V3LoadA(desc.bodyB->angularVelocity); + linDelta1 = V3LoadA(desc.bodyB->deltaLinDt); + angDelta1 = V3LoadA(desc.bodyB->deltaAngDt); + } + else + { + Cm::SpatialVectorV v = desc.articulationB->pxcFsGetVelocity(desc.linkIndexB); + Cm::SpatialVectorV deltaV = PxcFsGetMotionVector(*desc.articulationB, desc.linkIndexB); + linVel1 = v.linear; + angVel1 = v.angular; + linDelta1 = deltaV.linear; + angDelta1 = deltaV.angular; + } + + /*PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(linVel0, linVel0))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(linVel1, linVel1))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(angVel0, angVel0))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(angVel1, angVel1)));*/ + + + const PxU8* PX_RESTRICT last = desc.constraint + desc.constraintLengthOver16 * 16; + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc.constraint; + + Vec3V linImpulse0 = V3Zero(), linImpulse1 = V3Zero(), angImpulse0 = V3Zero(), angImpulse1 = V3Zero(); + + const Vec3V relMotion = V3Sub(linDelta0, linDelta1); + + while (currPtr < last) + { + SolverContactHeaderStep* PX_RESTRICT hdr = reinterpret_cast(currPtr); + currPtr += sizeof(SolverContactHeaderStep); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + SolverContactPointStepExt* PX_RESTRICT contacts = reinterpret_cast(currPtr); + Ps::prefetchLine(contacts); + currPtr += numNormalConstr * sizeof(SolverContactPointStepExt); + + PxF32* appliedForceBuffer = reinterpret_cast(currPtr); + currPtr += sizeof(PxF32) * ((numNormalConstr + 3) & (~3)); + + SolverContactFrictionStepExt* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionStepExt); + + + + Vec3V li0 = V3Zero(), li1 = V3Zero(), ai0 = V3Zero(), ai1 = V3Zero(); + + const Vec3V contactNormal = V3LoadA(hdr->normal); + + const FloatV accumulatedNormalImpulse = FMax(solveExtContactsStep(contacts, numNormalConstr, contactNormal, linVel0, angVel0, linVel1, + angVel1, li0, ai0, li1, ai1, linDelta0, linDelta1, angDelta0, angDelta1, FLoad(hdr->maxPenBias), appliedForceBuffer, minPen, elapsedTime), + FLoad(hdr->minNormalForce)); + + if(doFriction && numFrictionConstr) + { + Ps::prefetchLine(frictions); + const FloatV maxFrictionImpulse = FMul(hdr->getStaticFriction(), accumulatedNormalImpulse); + const FloatV maxDynFrictionImpulse = FMul(hdr->getDynamicFriction(), accumulatedNormalImpulse); + + BoolV broken = BFFFF(); + + for (PxU32 i = 0; i maxFrictionImpulse + // clamp newAppliedForce + deltaF to [-maxDynFrictionImpulse, maxDynFrictionImpulse] + // (i.e. clamp deltaF to [-maxDynFrictionImpulse-appliedForce, maxDynFrictionImpulse-appliedForce] + // set broken flag to true || broken flag + + // FloatV deltaF = FMul(FAdd(bias, normalVel), minusVelMultiplier); + // FloatV potentialSumF = FAdd(appliedForce, deltaF); + + const FloatV totalImpulse = FNegScaleSub(normalVel, velMultiplier, tmp1); + + // On XBox this clamping code uses the vector simple pipe rather than vector float, + // which eliminates a lot of stall cycles + + const BoolV clamp = FIsGrtr(FAbs(totalImpulse), maxFrictionImpulse); + + const FloatV totalClamped = FMin(maxDynFrictionImpulse, FMax(negMaxDynFrictionImpulse, totalImpulse)); + + const FloatV newAppliedForce = FSel(clamp, totalClamped, totalImpulse); + + broken = BOr(broken, clamp); + + FloatV deltaF = FSub(newAppliedForce, appliedForce); + + linVel0 = V3ScaleAdd(f.linDeltaVA, deltaF, linVel0); + angVel0 = V3ScaleAdd(raXnI, deltaF, angVel0); + linVel1 = V3ScaleAdd(f.linDeltaVB, deltaF, linVel1); + angVel1 = V3ScaleAdd(rbXnI, deltaF, angVel1); + + /*PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(linVel0, linVel0))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(linVel1, linVel1))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(angVel0, angVel0))); + PX_ASSERT(FAllGrtr(FLoad(100.f * 100.f), V3Dot(angVel1, angVel1)));*/ + + li0 = V3ScaleAdd(normal, deltaF, li0); ai0 = V3ScaleAdd(raXn, deltaF, ai0); + li1 = V3ScaleAdd(normal, deltaF, li1); ai1 = V3ScaleAdd(rbXn, deltaF, ai1); + + f.setAppliedForce(newAppliedForce); + } + Store_From_BoolV(broken, &hdr->broken); + } + + linImpulse0 = V3ScaleAdd(li0, hdr->getDominance0(), linImpulse0); + angImpulse0 = V3ScaleAdd(ai0, FLoad(hdr->angDom0), angImpulse0); + linImpulse1 = V3NegScaleSub(li1, hdr->getDominance1(), linImpulse1); + angImpulse1 = V3NegScaleSub(ai1, FLoad(hdr->angDom1), angImpulse1); + } + + if (desc.linkIndexA == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel0, desc.bodyA->linearVelocity); + V3StoreA(angVel0, desc.bodyA->angularVelocity); + } + else + { + desc.articulationA->pxcFsApplyImpulse(desc.linkIndexA, + linImpulse0, angImpulse0, cache.Z, cache.deltaV); + } + + if (desc.linkIndexB == PxSolverConstraintDesc::NO_LINK) + { + V3StoreA(linVel1, desc.bodyB->linearVelocity); + V3StoreA(angVel1, desc.bodyB->angularVelocity); + } + else + { + desc.articulationB->pxcFsApplyImpulse(desc.linkIndexB, linImpulse1, + angImpulse1, cache.Z, cache.deltaV); + } + + PX_ASSERT(currPtr == last); +} + +void solveContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveContact(desc[i], doFriction, minPenetration, elapsedTime); + } +} + +void solve1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxTGSSolverBodyTxInertia* const txInertias, const PxReal elapsedTime) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solve1DStep(desc[i], txInertias, elapsedTime); + } +} + +void solveExtContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime, SolverContext& cache) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveExtContactStep(desc[i], doFriction, minPenetration, elapsedTime, cache); + } +} + +void solveExt1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime, SolverContext& cache, + const PxTGSSolverBodyTxInertia* const txInertias) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveExt1DStep(desc[i], elapsedTime, cache, txInertias); + } +} + +void writeBackContact(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, SolverContext* cache) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + writeBackContact(desc[i], cache); + } +} + +void writeBack1D(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + writeBack1D(desc[i]); + } +} + +void solveConclude1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, + const PxTGSSolverBodyTxInertia* const txInertias, const PxReal elapsedTime) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solve1DStep(desc[i], txInertias, elapsedTime); + conclude1DStep(desc[i]); + } +} + +void solveConclude1DBlockExt(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, + const PxReal elapsedTime, SolverContext& cache, const PxTGSSolverBodyTxInertia* const txInertias) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveExt1DStep(desc[i], elapsedTime, cache, txInertias); + conclude1DStep(desc[i]); + } +} + + +void solveConcludeContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveContact(desc[i], true, -PX_MAX_F32, elapsedTime); + concludeContact(desc[i]); + } +} + +void solveConcludeContactExtBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime, SolverContext& cache) +{ + for (PxU32 i = hdr.mStartIndex, endIdx = hdr.mStartIndex + hdr.mStride; i < endIdx; ++i) + { + solveExtContactStep(desc[i], true, -PX_MAX_F32, elapsedTime, cache); + concludeContact(desc[i]); + } +} + + +} +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp new file mode 100644 index 000000000..9263d1244 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp @@ -0,0 +1,3658 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "PxSceneDesc.h" +#include "PsVecMath.h" +#include "PsMathUtils.h" +#include "DySolverContact.h" +#include "DySolverContact4.h" +#include "DySolverConstraintTypes.h" +#include "PxcNpWorkUnit.h" +#include "DyThreadContext.h" +#include "DyContactPrep.h" +#include "PxcNpContactPrepShared.h" +#include "PxvDynamics.h" +#include "DyCorrelationBuffer.h" +#include "DyDynamics.h" +#include "DyArticulationContactPrep.h" +#include "PxsContactManager.h" +#include "PsFoundation.h" +#include "DyTGSDynamics.h" + +using namespace physx; +using namespace Gu; + + +#include "PsVecMath.h" +#include "PxContactModifyCallback.h" +#include "PxsMaterialManager.h" +#include "PxsMaterialCombiner.h" +#include "DyContactPrepShared.h" +#include "DySolverConstraint1D.h" +#include "DyConstraintPrep.h" + +#include "CmConeLimitHelper.h" + +#include "DySolverContext.h" + +namespace physx +{ +namespace Dy +{ + + inline bool ValidateVec4(const Vec4V v) + { + PX_ALIGN(16, PxVec4 vF); + Ps::aos::V4StoreA(v, &vF.x); + return vF.isFinite(); + } + + PX_FORCE_INLINE void QuatRotate4(const Vec4VArg qx, const Vec4VArg qy, const Vec4VArg qz, const Vec4VArg qw, const Vec4VArg vx, const Vec4VArg vy, const Vec4VArg vz, + Vec4V& rX, Vec4V& rY, Vec4V& rZ) + { + /* + const PxVec3 qv(x,y,z); + return (v*(w*w-0.5f) + (qv.cross(v))*w + qv*(qv.dot(v)))*2; + */ + + const Vec4V two = V4Splat(FLoad(2.f)); + const Vec4V nhalf = V4Splat(FLoad(-0.5f)); + const Vec4V w2 = V4MulAdd(qw, qw, nhalf); + const Vec4V ax = V4Mul(vx, w2); + const Vec4V ay = V4Mul(vy, w2); + const Vec4V az = V4Mul(vz, w2); + + const Vec4V crX = V4NegMulSub(qz, vy, V4Mul(qy, vz)); + const Vec4V crY = V4NegMulSub(qx, vz, V4Mul(qz, vx)); + const Vec4V crZ = V4NegMulSub(qy, vx, V4Mul(qx, vy)); + + const Vec4V tempX = V4MulAdd(crX, qw, ax); + const Vec4V tempY = V4MulAdd(crY, qw, ay); + const Vec4V tempZ = V4MulAdd(crZ, qw, az); + + Vec4V dotuv = V4Mul(qx, vx); + dotuv = V4MulAdd(qy, vy, dotuv); + dotuv = V4MulAdd(qz, vz, dotuv); + + rX = V4Mul(V4MulAdd(qx, dotuv, tempX), two); + rY = V4Mul(V4MulAdd(qy, dotuv, tempY), two); + rZ = V4Mul(V4MulAdd(qz, dotuv, tempZ), two); + } + + +struct SolverContactHeaderStepBlock +{ + enum + { + eHAS_MAX_IMPULSE = 1 << 0, + eHAS_TARGET_VELOCITY = 1 << 1 + }; + + PxU8 type; //Note: mType should be first as the solver expects a type in the first byte. + PxU8 numNormalConstr; + PxU8 numFrictionConstr; + PxU8 flag; + + PxU8 flags[4]; + + //KS - used for write-back only + PxU8 numNormalConstrs[4]; + PxU8 numFrictionConstrs[4]; + + Vec4V restitution; + Vec4V staticFriction; + Vec4V dynamicFriction; + //Technically, these mass properties could be pulled out into a new structure and shared. For multi-manifold contacts, + //this would save 64 bytes per-manifold after the cost of the first manifold + Vec4V invMass0D0; + Vec4V invMass1D1; + Vec4V angDom0; + Vec4V angDom1; + //Normal is shared between all contacts in the batch. This will save some memory! + Vec4V normalX; + Vec4V normalY; + Vec4V normalZ; + + Vec4V maxPenBias; + + Sc::ShapeInteraction* shapeInteraction[4]; //192 or 208 + + BoolV broken; + PxU8* frictionBrokenWritebackByte[4]; +}; + +struct SolverContactPointStepBlock +{ + Vec4V raXnI[3]; + Vec4V rbXnI[3]; + Vec4V separation; + Vec4V velMultiplier; + Vec4V targetVelocity; + Vec4V biasCoefficient; +}; + +//KS - technically, this friction constraint has identical data to the above contact constraint. +//We make them separate structs for clarity +struct SolverContactFrictionStepBlock +{ + Vec4V normal[3]; + Vec4V raXnI[3]; + Vec4V rbXnI[3]; + Vec4V error; + Vec4V velMultiplier; + Vec4V targetVel; + Vec4V biasCoefficient; +}; + +struct SolverConstraint1DHeaderStep4 +{ + PxU8 type; // enum SolverConstraintType - must be first byte + PxU8 pad0[3]; + //These counts are the max of the 4 sets of data. + //When certain pairs have fewer constraints than others, they are padded with 0s so that no work is performed but + //calculations are still shared (afterall, they're computationally free because we're doing 4 things at a time in SIMD) + PxU32 count; + PxU8 counts[4]; + PxU8 breakable[4]; + + Vec4V linBreakImpulse; + Vec4V angBreakImpulse; + Vec4V invMass0D0; + Vec4V invMass1D1; + Vec4V angD0; + Vec4V angD1; + + Vec4V body0WorkOffset[3]; + Vec4V rAWorld[3]; + Vec4V rBWorld[3]; + + Vec4V angOrthoAxis0X[3]; + Vec4V angOrthoAxis0Y[3]; + Vec4V angOrthoAxis0Z[3]; + Vec4V angOrthoAxis1X[3]; + Vec4V angOrthoAxis1Y[3]; + Vec4V angOrthoAxis1Z[3]; + Vec4V angOrthoRecipResponse[3]; + Vec4V angOrthoError[3]; +}; + + +PX_ALIGN_PREFIX(16) +struct SolverConstraint1DStep4 +{ +public: + Vec4V lin0[3]; //!< linear velocity projection (body 0) + Vec4V error; //!< constraint error term - must be scaled by biasScale. Can be adjusted at run-time + + Vec4V lin1[3]; //!< linear velocity projection (body 1) + Vec4V biasScale; //!< constraint constant bias scale. Constant + + Vec4V ang0[3]; //!< angular velocity projection (body 0) + Vec4V velMultiplier; //!< constraint velocity multiplier + + Vec4V ang1[3]; //!< angular velocity projection (body 1) + Vec4V impulseMultiplier; //!< constraint impulse multiplier + + Vec4V velTarget; //!< Scaled target velocity of the constraint drive + + Vec4V minImpulse; //!< Lower bound on impulse magnitude + Vec4V maxImpulse; //!< Upper bound on impulse magnitude + Vec4V appliedForce; //!< applied force to correct velocity+bias + + Vec4V maxBias; + Vec4V angularErrorScale; //Constant + PxU32 flags[4]; +} PX_ALIGN_SUFFIX(16); + + + + +static void setupFinalizeSolverConstraints4Step(PxTGSSolverContactDesc* PX_RESTRICT descs, CorrelationBuffer& c, + PxU8* PX_RESTRICT workspace, const PxReal invDtF32, const PxReal invTotalDtF32, PxReal bounceThresholdF32, const PxReal solverOffsetSlopF32, + const Ps::aos::Vec4VArg invMassScale0, const Ps::aos::Vec4VArg invInertiaScale0, + const Ps::aos::Vec4VArg invMassScale1, const Ps::aos::Vec4VArg invInertiaScale1) +{ + + //OK, we have a workspace of pre-allocated space to store all 4 descs in. We now need to create the constraints in it + + //const Vec4V ccdMaxSeparation = Ps::aos::V4LoadXYZW(descs[0].maxCCDSeparation, descs[1].maxCCDSeparation, descs[2].maxCCDSeparation, descs[3].maxCCDSeparation); + const Vec4V solverOffsetSlop = V4Load(solverOffsetSlopF32); + + const Vec4V zero = V4Zero(); + const BoolV bFalse = BFFFF(); + const FloatV fZero = FZero(); + + PxU8 flags[4] = { PxU8(descs[0].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[1].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[2].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0), + PxU8(descs[3].hasForceThresholds ? SolverContactHeader::eHAS_FORCE_THRESHOLDS : 0) }; + + bool hasMaxImpulse = descs[0].hasMaxImpulse || descs[1].hasMaxImpulse || descs[2].hasMaxImpulse || descs[3].hasMaxImpulse; + + //The block is dynamic if **any** of the constraints have a non-static body B. This allows us to batch static and non-static constraints but we only get a memory/perf + //saving if all 4 are static. This simplifies the constraint partitioning such that it only needs to care about separating contacts and 1D constraints (which it already does) + bool isDynamic = false; + bool hasKinematic = false; + + PxReal kinematicScale0F32[4]; + PxReal kinematicScale1F32[4]; + + for (PxU32 a = 0; a < 4; ++a) + { + isDynamic = isDynamic || (descs[a].bodyState1 == PxSolverContactDesc::eDYNAMIC_BODY); + hasKinematic = hasKinematic || descs[a].bodyState1 == PxSolverContactDesc::eKINEMATIC_BODY; + kinematicScale0F32[a] = descs[a].body0->isKinematic ? 1.f : 0.f; + kinematicScale1F32[a] = descs[a].body1->isKinematic ? 1.f : 0.f; + } + + /*BoolV kinematic0 = BLoad(isKinematic0); + BoolV kinematic1 = BLoad(isKinematic1);*/ + + const Vec4V kinematicScale0 = V4LoadU(kinematicScale0F32); + const Vec4V kinematicScale1 = V4LoadU(kinematicScale1F32); + + const PxU32 constraintSize = sizeof(SolverContactPointStepBlock); + const PxU32 frictionSize = sizeof(SolverContactFrictionStepBlock); + + PxU8* PX_RESTRICT ptr = workspace; + + const Vec4V dom0 = invMassScale0; + const Vec4V dom1 = invMassScale1; + const Vec4V angDom0 = invInertiaScale0; + const Vec4V angDom1 = invInertiaScale1; + + const Vec4V maxPenBias = V4Max(V4LoadXYZW(descs[0].bodyData0->penBiasClamp, descs[1].bodyData0->penBiasClamp, + descs[2].bodyData0->penBiasClamp, descs[3].bodyData0->penBiasClamp), + V4LoadXYZW(descs[0].bodyData1->penBiasClamp, descs[1].bodyData1->penBiasClamp, + descs[2].bodyData1->penBiasClamp, descs[3].bodyData1->penBiasClamp)); + + const Vec4V restDistance = V4LoadXYZW(descs[0].restDistance, descs[1].restDistance, descs[2].restDistance, + descs[3].restDistance); + + + //load up velocities + Vec4V linVel00 = V4LoadA(&descs[0].bodyData0->originalLinearVelocity.x); + Vec4V linVel10 = V4LoadA(&descs[1].bodyData0->originalLinearVelocity.x); + Vec4V linVel20 = V4LoadA(&descs[2].bodyData0->originalLinearVelocity.x); + Vec4V linVel30 = V4LoadA(&descs[3].bodyData0->originalLinearVelocity.x); + + Vec4V linVel01 = V4LoadA(&descs[0].bodyData1->originalLinearVelocity.x); + Vec4V linVel11 = V4LoadA(&descs[1].bodyData1->originalLinearVelocity.x); + Vec4V linVel21 = V4LoadA(&descs[2].bodyData1->originalLinearVelocity.x); + Vec4V linVel31 = V4LoadA(&descs[3].bodyData1->originalLinearVelocity.x); + + Vec4V angVel00 = V4LoadA(&descs[0].bodyData0->originalAngularVelocity.x); + Vec4V angVel10 = V4LoadA(&descs[1].bodyData0->originalAngularVelocity.x); + Vec4V angVel20 = V4LoadA(&descs[2].bodyData0->originalAngularVelocity.x); + Vec4V angVel30 = V4LoadA(&descs[3].bodyData0->originalAngularVelocity.x); + + Vec4V angVel01 = V4LoadA(&descs[0].bodyData1->originalAngularVelocity.x); + Vec4V angVel11 = V4LoadA(&descs[1].bodyData1->originalAngularVelocity.x); + Vec4V angVel21 = V4LoadA(&descs[2].bodyData1->originalAngularVelocity.x); + Vec4V angVel31 = V4LoadA(&descs[3].bodyData1->originalAngularVelocity.x); + + Vec4V linVelT00, linVelT10, linVelT20; + Vec4V linVelT01, linVelT11, linVelT21; + Vec4V angVelT00, angVelT10, angVelT20; + Vec4V angVelT01, angVelT11, angVelT21; + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVelT00, linVelT10, linVelT20); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVelT01, linVelT11, linVelT21); + PX_TRANSPOSE_44_34(angVel00, angVel10, angVel20, angVel30, angVelT00, angVelT10, angVelT20); + PX_TRANSPOSE_44_34(angVel01, angVel11, angVel21, angVel31, angVelT01, angVelT11, angVelT21); + + const Vec4V vrelX = V4Sub(linVelT00, linVelT01); + const Vec4V vrelY = V4Sub(linVelT10, linVelT11); + const Vec4V vrelZ = V4Sub(linVelT20, linVelT21); + + //Load up masses and invInertia + + const Vec4V invMass0 = V4LoadXYZW(descs[0].bodyData0->invMass, descs[1].bodyData0->invMass, descs[2].bodyData0->invMass, descs[3].bodyData0->invMass); + const Vec4V invMass1 = V4LoadXYZW(descs[0].bodyData1->invMass, descs[1].bodyData1->invMass, descs[2].bodyData1->invMass, descs[3].bodyData1->invMass); + + const Vec4V invMass0D0 = V4Mul(dom0, invMass0); + const Vec4V invMass1D1 = V4Mul(dom1, invMass1); + + Vec4V invInertia00X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].body0TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia00Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].body0TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia00Z = Vec4V_From_Vec3V(V3LoadU(descs[0].body0TxI->sqrtInvInertia.column2)); + + Vec4V invInertia10X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].body0TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia10Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].body0TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia10Z = Vec4V_From_Vec3V(V3LoadU(descs[1].body0TxI->sqrtInvInertia.column2)); + + Vec4V invInertia20X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].body0TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia20Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].body0TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia20Z = Vec4V_From_Vec3V(V3LoadU(descs[2].body0TxI->sqrtInvInertia.column2)); + + Vec4V invInertia30X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].body0TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia30Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].body0TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia30Z = Vec4V_From_Vec3V(V3LoadU(descs[3].body0TxI->sqrtInvInertia.column2)); + + Vec4V invInertia01X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].body1TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia01Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[0].body1TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia01Z = Vec4V_From_Vec3V(V3LoadU(descs[0].body1TxI->sqrtInvInertia.column2)); + + Vec4V invInertia11X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].body1TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia11Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[1].body1TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia11Z = Vec4V_From_Vec3V(V3LoadU(descs[1].body1TxI->sqrtInvInertia.column2)); + + Vec4V invInertia21X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].body1TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia21Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[2].body1TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia21Z = Vec4V_From_Vec3V(V3LoadU(descs[2].body1TxI->sqrtInvInertia.column2)); + + Vec4V invInertia31X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].body1TxI->sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia31Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(descs[3].body1TxI->sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia31Z = Vec4V_From_Vec3V(V3LoadU(descs[3].body1TxI->sqrtInvInertia.column2)); + + Vec4V invInertia0X0, invInertia0X1, invInertia0X2; + Vec4V invInertia0Y0, invInertia0Y1, invInertia0Y2; + Vec4V invInertia0Z0, invInertia0Z1, invInertia0Z2; + + Vec4V invInertia1X0, invInertia1X1, invInertia1X2; + Vec4V invInertia1Y0, invInertia1Y1, invInertia1Y2; + Vec4V invInertia1Z0, invInertia1Z1, invInertia1Z2; + + PX_TRANSPOSE_44_34(invInertia00X, invInertia10X, invInertia20X, invInertia30X, invInertia0X0, invInertia0Y0, invInertia0Z0); + PX_TRANSPOSE_44_34(invInertia00Y, invInertia10Y, invInertia20Y, invInertia30Y, invInertia0X1, invInertia0Y1, invInertia0Z1); + PX_TRANSPOSE_44_34(invInertia00Z, invInertia10Z, invInertia20Z, invInertia30Z, invInertia0X2, invInertia0Y2, invInertia0Z2); + + PX_TRANSPOSE_44_34(invInertia01X, invInertia11X, invInertia21X, invInertia31X, invInertia1X0, invInertia1Y0, invInertia1Z0); + PX_TRANSPOSE_44_34(invInertia01Y, invInertia11Y, invInertia21Y, invInertia31Y, invInertia1X1, invInertia1Y1, invInertia1Z1); + PX_TRANSPOSE_44_34(invInertia01Z, invInertia11Z, invInertia21Z, invInertia31Z, invInertia1X2, invInertia1Y2, invInertia1Z2); + + + const FloatV invDt = FLoad(invDtF32); + const FloatV invTotalDt = FLoad(invTotalDtF32); + const FloatV p8 = FLoad(0.8f); + const Vec4V p84 = V4Splat(p8); + const Vec4V bounceThreshold = V4Splat(FLoad(bounceThresholdF32)); + + const Vec4V invDtp8 = V4Splat(FMul(invDt, p8)); + + const Vec3V bodyFrame00p = V3LoadU(descs[0].bodyFrame0.p); + const Vec3V bodyFrame01p = V3LoadU(descs[1].bodyFrame0.p); + const Vec3V bodyFrame02p = V3LoadU(descs[2].bodyFrame0.p); + const Vec3V bodyFrame03p = V3LoadU(descs[3].bodyFrame0.p); + + Vec4V bodyFrame00p4 = Vec4V_From_Vec3V(bodyFrame00p); + Vec4V bodyFrame01p4 = Vec4V_From_Vec3V(bodyFrame01p); + Vec4V bodyFrame02p4 = Vec4V_From_Vec3V(bodyFrame02p); + Vec4V bodyFrame03p4 = Vec4V_From_Vec3V(bodyFrame03p); + + Vec4V bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ; + PX_TRANSPOSE_44_34(bodyFrame00p4, bodyFrame01p4, bodyFrame02p4, bodyFrame03p4, bodyFrame0pX, bodyFrame0pY, bodyFrame0pZ); + + + const Vec3V bodyFrame10p = V3LoadU(descs[0].bodyFrame1.p); + const Vec3V bodyFrame11p = V3LoadU(descs[1].bodyFrame1.p); + const Vec3V bodyFrame12p = V3LoadU(descs[2].bodyFrame1.p); + const Vec3V bodyFrame13p = V3LoadU(descs[3].bodyFrame1.p); + + Vec4V bodyFrame10p4 = Vec4V_From_Vec3V(bodyFrame10p); + Vec4V bodyFrame11p4 = Vec4V_From_Vec3V(bodyFrame11p); + Vec4V bodyFrame12p4 = Vec4V_From_Vec3V(bodyFrame12p); + Vec4V bodyFrame13p4 = Vec4V_From_Vec3V(bodyFrame13p); + + Vec4V bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ; + PX_TRANSPOSE_44_34(bodyFrame10p4, bodyFrame11p4, bodyFrame12p4, bodyFrame13p4, bodyFrame1pX, bodyFrame1pY, bodyFrame1pZ); + + + const QuatV bodyFrame00q = QuatVLoadU(&descs[0].bodyFrame0.q.x); + const QuatV bodyFrame01q = QuatVLoadU(&descs[1].bodyFrame0.q.x); + const QuatV bodyFrame02q = QuatVLoadU(&descs[2].bodyFrame0.q.x); + const QuatV bodyFrame03q = QuatVLoadU(&descs[3].bodyFrame0.q.x); + + const QuatV bodyFrame10q = QuatVLoadU(&descs[0].bodyFrame1.q.x); + const QuatV bodyFrame11q = QuatVLoadU(&descs[1].bodyFrame1.q.x); + const QuatV bodyFrame12q = QuatVLoadU(&descs[2].bodyFrame1.q.x); + const QuatV bodyFrame13q = QuatVLoadU(&descs[3].bodyFrame1.q.x); + + PxU32 frictionPatchWritebackAddrIndex0 = 0; + PxU32 frictionPatchWritebackAddrIndex1 = 0; + PxU32 frictionPatchWritebackAddrIndex2 = 0; + PxU32 frictionPatchWritebackAddrIndex3 = 0; + + Ps::prefetchLine(c.contactID); + Ps::prefetchLine(c.contactID, 128); + + PxU32 frictionIndex0 = 0, frictionIndex1 = 0, frictionIndex2 = 0, frictionIndex3 = 0; + //PxU32 contactIndex0 = 0, contactIndex1 = 0, contactIndex2 = 0, contactIndex3 = 0; + + + //OK, we iterate through all friction patch counts in the constraint patch, building up the constraint list etc. + + PxU32 maxPatches = PxMax(descs[0].numFrictionPatches, PxMax(descs[1].numFrictionPatches, PxMax(descs[2].numFrictionPatches, descs[3].numFrictionPatches))); + + const Vec4V p1 = V4Splat(FLoad(0.1f)); + const Vec4V orthoThreshold = V4Splat(FLoad(0.70710678f)); + + + PxU32 contact0 = 0, contact1 = 0, contact2 = 0, contact3 = 0; + PxU32 patch0 = 0, patch1 = 0, patch2 = 0, patch3 = 0; + + PxU8 flag = 0; + if (hasMaxImpulse) + flag |= SolverContactHeader4::eHAS_MAX_IMPULSE; + + for (PxU32 i = 0; i= descs[0].numFrictionPatches; + const bool hasFinished1 = i >= descs[1].numFrictionPatches; + const bool hasFinished2 = i >= descs[2].numFrictionPatches; + const bool hasFinished3 = i >= descs[3].numFrictionPatches; + + + frictionIndex0 = hasFinished0 ? frictionIndex0 : descs[0].startFrictionPatchIndex + i; + frictionIndex1 = hasFinished1 ? frictionIndex1 : descs[1].startFrictionPatchIndex + i; + frictionIndex2 = hasFinished2 ? frictionIndex2 : descs[2].startFrictionPatchIndex + i; + frictionIndex3 = hasFinished3 ? frictionIndex3 : descs[3].startFrictionPatchIndex + i; + + PxU32 clampedContacts0 = hasFinished0 ? 0 : c.frictionPatchContactCounts[frictionIndex0]; + PxU32 clampedContacts1 = hasFinished1 ? 0 : c.frictionPatchContactCounts[frictionIndex1]; + PxU32 clampedContacts2 = hasFinished2 ? 0 : c.frictionPatchContactCounts[frictionIndex2]; + PxU32 clampedContacts3 = hasFinished3 ? 0 : c.frictionPatchContactCounts[frictionIndex3]; + + PxU32 firstPatch0 = c.correlationListHeads[frictionIndex0]; + PxU32 firstPatch1 = c.correlationListHeads[frictionIndex1]; + PxU32 firstPatch2 = c.correlationListHeads[frictionIndex2]; + PxU32 firstPatch3 = c.correlationListHeads[frictionIndex3]; + + const Gu::ContactPoint* contactBase0 = descs[0].contacts + c.contactPatches[firstPatch0].start; + const Gu::ContactPoint* contactBase1 = descs[1].contacts + c.contactPatches[firstPatch1].start; + const Gu::ContactPoint* contactBase2 = descs[2].contacts + c.contactPatches[firstPatch2].start; + const Gu::ContactPoint* contactBase3 = descs[3].contacts + c.contactPatches[firstPatch3].start; + + const Vec4V restitution = V4Neg(V4LoadXYZW(contactBase0->restitution, contactBase1->restitution, contactBase2->restitution, + contactBase3->restitution)); + + SolverContactHeaderStepBlock* PX_RESTRICT header = reinterpret_cast(ptr); + ptr += sizeof(SolverContactHeaderStepBlock); + + + header->flags[0] = flags[0]; + header->flags[1] = flags[1]; + header->flags[2] = flags[2]; + header->flags[3] = flags[3]; + + header->flag = flag; + + PxU32 totalContacts = PxMax(clampedContacts0, PxMax(clampedContacts1, PxMax(clampedContacts2, clampedContacts3))); + + Vec4V* PX_RESTRICT appliedNormalForces = reinterpret_cast(ptr); + ptr += sizeof(Vec4V)*totalContacts; + + PxMemZero(appliedNormalForces, sizeof(Vec4V) * totalContacts); + + header->numNormalConstr = Ps::to8(totalContacts); + header->numNormalConstrs[0] = Ps::to8(clampedContacts0); + header->numNormalConstrs[1] = Ps::to8(clampedContacts1); + header->numNormalConstrs[2] = Ps::to8(clampedContacts2); + header->numNormalConstrs[3] = Ps::to8(clampedContacts3); + //header->sqrtInvMassA = sqrtInvMass0; + //header->sqrtInvMassB = sqrtInvMass1; + header->invMass0D0 = invMass0D0; + header->invMass1D1 = invMass1D1; + header->angDom0 = angDom0; + header->angDom1 = angDom1; + header->shapeInteraction[0] = descs[0].shapeInteraction; header->shapeInteraction[1] = descs[1].shapeInteraction; + header->shapeInteraction[2] = descs[2].shapeInteraction; header->shapeInteraction[3] = descs[3].shapeInteraction; + + Vec4V* maxImpulse = reinterpret_cast(ptr + constraintSize * totalContacts); + + header->restitution = restitution; + + Vec4V normal0 = V4LoadA(&contactBase0->normal.x); + Vec4V normal1 = V4LoadA(&contactBase1->normal.x); + Vec4V normal2 = V4LoadA(&contactBase2->normal.x); + Vec4V normal3 = V4LoadA(&contactBase3->normal.x); + + Vec4V normalX, normalY, normalZ; + PX_TRANSPOSE_44_34(normal0, normal1, normal2, normal3, normalX, normalY, normalZ); + + PX_ASSERT(ValidateVec4(normalX)); + PX_ASSERT(ValidateVec4(normalY)); + PX_ASSERT(ValidateVec4(normalZ)); + + header->normalX = normalX; + header->normalY = normalY; + header->normalZ = normalZ; + + header->maxPenBias = maxPenBias; + + const Vec4V norVel0 = V4MulAdd(normalZ, linVelT20, V4MulAdd(normalY, linVelT10, V4Mul(normalX, linVelT00))); + const Vec4V norVel1 = V4MulAdd(normalZ, linVelT21, V4MulAdd(normalY, linVelT11, V4Mul(normalX, linVelT01))); + const Vec4V relNorVel = V4Sub(norVel0, norVel1); + + //For all correlation heads - need to pull this out I think + + //OK, we have a counter for all our patches... + PxU32 finished = (PxU32(hasFinished0)) | + ((PxU32(hasFinished1)) << 1) | + ((PxU32(hasFinished2)) << 2) | + ((PxU32(hasFinished3)) << 3); + + CorrelationListIterator iter0(c, firstPatch0); + CorrelationListIterator iter1(c, firstPatch1); + CorrelationListIterator iter2(c, firstPatch2); + CorrelationListIterator iter3(c, firstPatch3); + + //PxU32 contact0, contact1, contact2, contact3; + //PxU32 patch0, patch1, patch2, patch3; + + if (!hasFinished0) + iter0.nextContact(patch0, contact0); + if (!hasFinished1) + iter1.nextContact(patch1, contact1); + if (!hasFinished2) + iter2.nextContact(patch2, contact2); + if (!hasFinished3) + iter3.nextContact(patch3, contact3); + + PxU8* p = ptr; + + PxU32 contactCount = 0; + PxU32 newFinished = + (PxU32(hasFinished0 || !iter0.hasNextContact())) | + ((PxU32(hasFinished1 || !iter1.hasNextContact())) << 1) | + ((PxU32(hasFinished2 || !iter2.hasNextContact())) << 2) | + ((PxU32(hasFinished3 || !iter3.hasNextContact())) << 3); + + while (finished != 0xf) + { + finished = newFinished; + ++contactCount; + Ps::prefetchLine(p, 384); + Ps::prefetchLine(p, 512); + Ps::prefetchLine(p, 640); + + SolverContactPointStepBlock* PX_RESTRICT solverContact = reinterpret_cast(p); + p += constraintSize; + + const Gu::ContactPoint& con0 = descs[0].contacts[c.contactPatches[patch0].start + contact0]; + const Gu::ContactPoint& con1 = descs[1].contacts[c.contactPatches[patch1].start + contact1]; + const Gu::ContactPoint& con2 = descs[2].contacts[c.contactPatches[patch2].start + contact2]; + const Gu::ContactPoint& con3 = descs[3].contacts[c.contactPatches[patch3].start + contact3]; + + //Now we need to splice these 4 contacts into a single structure + + { + Vec4V point0 = V4LoadA(&con0.point.x); + Vec4V point1 = V4LoadA(&con1.point.x); + Vec4V point2 = V4LoadA(&con2.point.x); + Vec4V point3 = V4LoadA(&con3.point.x); + + Vec4V pointX, pointY, pointZ; + PX_TRANSPOSE_44_34(point0, point1, point2, point3, pointX, pointY, pointZ); + + Vec4V cTargetVel0 = V4LoadA(&con0.targetVel.x); + Vec4V cTargetVel1 = V4LoadA(&con1.targetVel.x); + Vec4V cTargetVel2 = V4LoadA(&con2.targetVel.x); + Vec4V cTargetVel3 = V4LoadA(&con3.targetVel.x); + + Vec4V cTargetVelX, cTargetVelY, cTargetVelZ; + PX_TRANSPOSE_44_34(cTargetVel0, cTargetVel1, cTargetVel2, cTargetVel3, cTargetVelX, cTargetVelY, cTargetVelZ); + + const Vec4V separation = V4LoadXYZW(con0.separation, con1.separation, con2.separation, con3.separation); + + const Vec4V cTargetNorVel = V4MulAdd(cTargetVelX, normalX, V4MulAdd(cTargetVelY, normalY, V4Mul(cTargetVelZ, normalZ))); + + const Vec4V raX = V4Sub(pointX, bodyFrame0pX); + const Vec4V raY = V4Sub(pointY, bodyFrame0pY); + const Vec4V raZ = V4Sub(pointZ, bodyFrame0pZ); + + const Vec4V rbX = V4Sub(pointX, bodyFrame1pX); + const Vec4V rbY = V4Sub(pointY, bodyFrame1pY); + const Vec4V rbZ = V4Sub(pointZ, bodyFrame1pZ); + + /*raX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raX)), zero, raX); + raY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raY)), zero, raY); + raZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raZ)), zero, raZ); + + rbX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbX)), zero, rbX); + rbY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbY)), zero, rbY); + rbZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbZ)), zero, rbZ);*/ + + PX_ASSERT(ValidateVec4(raX)); + PX_ASSERT(ValidateVec4(raY)); + PX_ASSERT(ValidateVec4(raZ)); + + PX_ASSERT(ValidateVec4(rbX)); + PX_ASSERT(ValidateVec4(rbY)); + PX_ASSERT(ValidateVec4(rbZ)); + + + //raXn = cross(ra, normal) which = Vec3V( a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x); + + Vec4V raXnX = V4NegMulSub(raZ, normalY, V4Mul(raY, normalZ)); + Vec4V raXnY = V4NegMulSub(raX, normalZ, V4Mul(raZ, normalX)); + Vec4V raXnZ = V4NegMulSub(raY, normalX, V4Mul(raX, normalY)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + + PX_ASSERT(ValidateVec4(delAngVel0X)); + PX_ASSERT(ValidateVec4(delAngVel0Y)); + PX_ASSERT(ValidateVec4(delAngVel0Z)); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0X, delAngVel0X, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0Z, delAngVel0Z))); + const Vec4V relAngVel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4Mul(raXnX, angVelT00))); + + Vec4V unitResponse = V4MulAdd(invMass0D0, angDom0, dotDelAngVel0); + Vec4V vrel0 = V4Add(norVel0, relAngVel0); + Vec4V vrel1 = norVel1; + + Vec4V delAngVel1X = zero; + Vec4V delAngVel1Y = zero; + Vec4V delAngVel1Z = zero; + + //The dynamic-only parts - need to if-statement these up. A branch here shouldn't cost us too much + if (isDynamic) + { + Vec4V rbXnX = V4NegMulSub(rbZ, normalY, V4Mul(rbY, normalZ)); + Vec4V rbXnY = V4NegMulSub(rbX, normalZ, V4Mul(rbZ, normalX)); + Vec4V rbXnZ = V4NegMulSub(rbY, normalX, V4Mul(rbX, normalY)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + delAngVel1X = V4Mul(invInertia1X0, rbXnX); + delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + PX_ASSERT(ValidateVec4(delAngVel1X)); + PX_ASSERT(ValidateVec4(delAngVel1Y)); + PX_ASSERT(ValidateVec4(delAngVel1Z)); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1X, delAngVel1X, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1Z, delAngVel1Z))); + const Vec4V relAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + unitResponse = V4Add(unitResponse, resp1); + + vrel1 = V4Add(vrel1, relAngVel1); + + //These are for dynamic-only contacts. + + + } + else if (hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, normalY, V4Mul(rbY, normalZ)); + const Vec4V rbXnY = V4NegMulSub(rbX, normalZ, V4Mul(rbZ, normalX)); + const Vec4V rbXnZ = V4NegMulSub(rbY, normalX, V4Mul(rbX, normalY)); + + const Vec4V relAngVel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4Mul(rbXnX, angVelT01))); + + vrel1 = V4Add(vrel1, relAngVel1); + } + + Vec4V vrel = V4Sub(vrel0, vrel1); + + solverContact->rbXnI[0] = delAngVel1X; + solverContact->rbXnI[1] = delAngVel1Y; + solverContact->rbXnI[2] = delAngVel1Z; + + const Vec4V velMultiplier = V4Sel(V4IsGrtr(unitResponse, zero), V4Recip(unitResponse), zero); + + const Vec4V penetration = V4Sub(separation, restDistance); + //const Vec4V penInvDtPt8 = V4Max(maxPenBias, V4Scale(penetration, invDtp8)); + //Vec4V scaledBias = invDtPt8; + + const Vec4V penetrationInvDt = V4Scale(penetration, invTotalDt); + + const BoolV isGreater2 = BAnd(BAnd(V4IsGrtr(zero, restitution), V4IsGrtr(bounceThreshold, vrel)), + V4IsGrtr(V4Neg(vrel), penetrationInvDt)); + + + const Vec4V scaledBias = V4Neg(invDtp8); + + Vec4V targetVelocity = V4NegMulSub(vrel0, kinematicScale0, V4MulAdd(vrel1, kinematicScale1, V4Sel(isGreater2, V4Mul(vrel, restitution), zero))); + + + + //Vec4V biasedErr = V4Sel(isGreater2, targetVelocity, scaledBias); + //Vec4V biasedErr = V4Add(targetVelocity, scaledBias); + + //biasedErr = V4NegMulSub(V4Sub(vrel, cTargetNorVel), velMultiplier, biasedErr); + + //These values are present for static and dynamic contacts + solverContact->raXnI[0] = delAngVel0X; + solverContact->raXnI[1] = delAngVel0Y; + solverContact->raXnI[2] = delAngVel0Z; + solverContact->velMultiplier = velMultiplier; + solverContact->targetVelocity = V4Add(cTargetNorVel, targetVelocity); + solverContact->separation = penetration; + solverContact->biasCoefficient = scaledBias; + + if (hasMaxImpulse) + { + maxImpulse[contactCount - 1] = V4Merge(FLoad(con0.maxImpulse), FLoad(con1.maxImpulse), FLoad(con2.maxImpulse), + FLoad(con3.maxImpulse)); + } + } + if (!(finished & 0x1)) + { + iter0.nextContact(patch0, contact0); + newFinished |= PxU32(!iter0.hasNextContact()); + } + + if (!(finished & 0x2)) + { + iter1.nextContact(patch1, contact1); + newFinished |= (PxU32(!iter1.hasNextContact()) << 1); + } + + if (!(finished & 0x4)) + { + iter2.nextContact(patch2, contact2); + newFinished |= (PxU32(!iter2.hasNextContact()) << 2); + } + + if (!(finished & 0x8)) + { + iter3.nextContact(patch3, contact3); + newFinished |= (PxU32(!iter3.hasNextContact()) << 3); + } + } + ptr = p; + if (hasMaxImpulse) + { + ptr += sizeof(Vec4V) * totalContacts; + } + + //OK...friction time :-) + + Vec4V maxImpulseScale = V4One(); + { + const Vec4V staticFriction = V4LoadXYZW(contactBase0->staticFriction, contactBase1->staticFriction, + contactBase2->staticFriction, contactBase3->staticFriction); + + const Vec4V dynamicFriction = V4LoadXYZW(contactBase0->dynamicFriction, contactBase1->dynamicFriction, + contactBase2->dynamicFriction, contactBase3->dynamicFriction); + + PX_ASSERT(totalContacts == contactCount); + header->dynamicFriction = dynamicFriction; + header->staticFriction = staticFriction; + + const FrictionPatch& frictionPatch0 = c.frictionPatches[frictionIndex0]; + const FrictionPatch& frictionPatch1 = c.frictionPatches[frictionIndex1]; + const FrictionPatch& frictionPatch2 = c.frictionPatches[frictionIndex2]; + const FrictionPatch& frictionPatch3 = c.frictionPatches[frictionIndex3]; + + PxU32 anchorCount0 = frictionPatch0.anchorCount; + PxU32 anchorCount1 = frictionPatch1.anchorCount; + PxU32 anchorCount2 = frictionPatch2.anchorCount; + PxU32 anchorCount3 = frictionPatch3.anchorCount; + + PxU32 clampedAnchorCount0 = hasFinished0 || (contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount0; + PxU32 clampedAnchorCount1 = hasFinished1 || (contactBase1->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount1; + PxU32 clampedAnchorCount2 = hasFinished2 || (contactBase2->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount2; + PxU32 clampedAnchorCount3 = hasFinished3 || (contactBase3->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount3; + + const PxU32 maxAnchorCount = PxMax(clampedAnchorCount0, PxMax(clampedAnchorCount1, PxMax(clampedAnchorCount2, clampedAnchorCount3))); + + //if(clampedAnchorCount0 != clampedAnchorCount1 || clampedAnchorCount0 != clampedAnchorCount2 || clampedAnchorCount0 != clampedAnchorCount3) + // Ps::debugBreak(); + + + //const bool haveFriction = maxAnchorCount != 0; + header->numFrictionConstr = Ps::to8(maxAnchorCount * 2); + header->numFrictionConstrs[0] = Ps::to8(clampedAnchorCount0 * 2); + header->numFrictionConstrs[1] = Ps::to8(clampedAnchorCount1 * 2); + header->numFrictionConstrs[2] = Ps::to8(clampedAnchorCount2 * 2); + header->numFrictionConstrs[3] = Ps::to8(clampedAnchorCount3 * 2); + + //KS - TODO - extend this if needed + header->type = Ps::to8(DY_SC_TYPE_BLOCK_RB_CONTACT); + + if (maxAnchorCount) + { + const BoolV cond = V4IsGrtr(orthoThreshold, V4Abs(normalX)); + + const Vec4V t0FallbackX = V4Sel(cond, zero, V4Neg(normalY)); + const Vec4V t0FallbackY = V4Sel(cond, V4Neg(normalZ), normalX); + const Vec4V t0FallbackZ = V4Sel(cond, normalY, zero); + + //const Vec4V dotNormalVrel = V4MulAdd(normalZ, vrelZ, V4MulAdd(normalY, vrelY, V4Mul(normalX, vrelX))); + const Vec4V vrelSubNorVelX = V4NegMulSub(normalX, relNorVel, vrelX); + const Vec4V vrelSubNorVelY = V4NegMulSub(normalY, relNorVel, vrelY); + const Vec4V vrelSubNorVelZ = V4NegMulSub(normalZ, relNorVel, vrelZ); + + const Vec4V lenSqvrelSubNorVelZ = V4MulAdd(vrelSubNorVelX, vrelSubNorVelX, V4MulAdd(vrelSubNorVelY, vrelSubNorVelY, V4Mul(vrelSubNorVelZ, vrelSubNorVelZ))); + + const BoolV bcon2 = V4IsGrtr(lenSqvrelSubNorVelZ, p1); + + Vec4V t0X = V4Sel(bcon2, vrelSubNorVelX, t0FallbackX); + Vec4V t0Y = V4Sel(bcon2, vrelSubNorVelY, t0FallbackY); + Vec4V t0Z = V4Sel(bcon2, vrelSubNorVelZ, t0FallbackZ); + + + //Now normalize this... + const Vec4V recipLen = V4Rsqrt(V4MulAdd(t0Z, t0Z, V4MulAdd(t0Y, t0Y, V4Mul(t0X, t0X)))); + + t0X = V4Mul(t0X, recipLen); + t0Y = V4Mul(t0Y, recipLen); + t0Z = V4Mul(t0Z, recipLen); + + Vec4V t1X = V4NegMulSub(normalZ, t0Y, V4Mul(normalY, t0Z)); + Vec4V t1Y = V4NegMulSub(normalX, t0Z, V4Mul(normalZ, t0X)); + Vec4V t1Z = V4NegMulSub(normalY, t0X, V4Mul(normalX, t0Y)); + + PX_ASSERT((uintptr_t(descs[0].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[1].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[2].frictionPtr) & 0xF) == 0); + PX_ASSERT((uintptr_t(descs[3].frictionPtr) & 0xF) == 0); + + + PxU8* PX_RESTRICT writeback0 = descs[0].frictionPtr + frictionPatchWritebackAddrIndex0 * sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback1 = descs[1].frictionPtr + frictionPatchWritebackAddrIndex1 * sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback2 = descs[2].frictionPtr + frictionPatchWritebackAddrIndex2 * sizeof(FrictionPatch); + PxU8* PX_RESTRICT writeback3 = descs[3].frictionPtr + frictionPatchWritebackAddrIndex3 * sizeof(FrictionPatch); + + PxU32 index0 = 0, index1 = 0, index2 = 0, index3 = 0; + + header->broken = bFalse; + header->frictionBrokenWritebackByte[0] = writeback0; + header->frictionBrokenWritebackByte[1] = writeback1; + header->frictionBrokenWritebackByte[2] = writeback2; + header->frictionBrokenWritebackByte[3] = writeback3; + + + /*header->frictionNormal[0][0] = t0X; + header->frictionNormal[0][1] = t0Y; + header->frictionNormal[0][2] = t0Z; + + header->frictionNormal[1][0] = t1X; + header->frictionNormal[1][1] = t1Y; + header->frictionNormal[1][2] = t1Z;*/ + + Vec4V* PX_RESTRICT appliedForces = reinterpret_cast(ptr); + ptr += sizeof(Vec4V)*header->numFrictionConstr; + + PxMemZero(appliedForces, sizeof(Vec4V) * header->numFrictionConstr); + + for (PxU32 j = 0; j < maxAnchorCount; j++) + { + Ps::prefetchLine(ptr, 384); + Ps::prefetchLine(ptr, 512); + Ps::prefetchLine(ptr, 640); + SolverContactFrictionStepBlock* PX_RESTRICT f0 = reinterpret_cast(ptr); + ptr += frictionSize; + SolverContactFrictionStepBlock* PX_RESTRICT f1 = reinterpret_cast(ptr); + ptr += frictionSize; + + index0 = j < clampedAnchorCount0 ? j : index0; + index1 = j < clampedAnchorCount1 ? j : index1; + index2 = j < clampedAnchorCount2 ? j : index2; + index3 = j < clampedAnchorCount3 ? j : index3; + + if (j >= clampedAnchorCount0) + maxImpulseScale = V4SetX(maxImpulseScale, fZero); + if (j >= clampedAnchorCount1) + maxImpulseScale = V4SetY(maxImpulseScale, fZero); + if (j >= clampedAnchorCount2) + maxImpulseScale = V4SetZ(maxImpulseScale, fZero); + if (j >= clampedAnchorCount3) + maxImpulseScale = V4SetW(maxImpulseScale, fZero); + + t0X = V4Mul(maxImpulseScale, t0X); + t0Y = V4Mul(maxImpulseScale, t0Y); + t0Z = V4Mul(maxImpulseScale, t0Z); + + t1X = V4Mul(maxImpulseScale, t1X); + t1Y = V4Mul(maxImpulseScale, t1Y); + t1Z = V4Mul(maxImpulseScale, t1Z); + + + Vec3V body0Anchor0 = V3LoadU(frictionPatch0.body0Anchors[index0]); + Vec3V body0Anchor1 = V3LoadU(frictionPatch1.body0Anchors[index1]); + Vec3V body0Anchor2 = V3LoadU(frictionPatch2.body0Anchors[index2]); + Vec3V body0Anchor3 = V3LoadU(frictionPatch3.body0Anchors[index3]); + + Vec4V ra0 = Vec4V_From_Vec3V(QuatRotate(bodyFrame00q, body0Anchor0)); + Vec4V ra1 = Vec4V_From_Vec3V(QuatRotate(bodyFrame01q, body0Anchor1)); + Vec4V ra2 = Vec4V_From_Vec3V(QuatRotate(bodyFrame02q, body0Anchor2)); + Vec4V ra3 = Vec4V_From_Vec3V(QuatRotate(bodyFrame03q, body0Anchor3)); + + Vec4V raX, raY, raZ; + PX_TRANSPOSE_44_34(ra0, ra1, ra2, ra3, raX, raY, raZ); + + /*raX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raX)), zero, raX); + raY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raY)), zero, raY); + raZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raZ)), zero, raZ);*/ + + const Vec4V raWorldX = V4Add(raX, bodyFrame0pX); + const Vec4V raWorldY = V4Add(raY, bodyFrame0pY); + const Vec4V raWorldZ = V4Add(raZ, bodyFrame0pZ); + + Vec3V body1Anchor0 = V3LoadU(frictionPatch0.body1Anchors[index0]); + Vec3V body1Anchor1 = V3LoadU(frictionPatch1.body1Anchors[index1]); + Vec3V body1Anchor2 = V3LoadU(frictionPatch2.body1Anchors[index2]); + Vec3V body1Anchor3 = V3LoadU(frictionPatch3.body1Anchors[index3]); + + Vec4V rb0 = Vec4V_From_Vec3V(QuatRotate(bodyFrame10q, body1Anchor0)); + Vec4V rb1 = Vec4V_From_Vec3V(QuatRotate(bodyFrame11q, body1Anchor1)); + Vec4V rb2 = Vec4V_From_Vec3V(QuatRotate(bodyFrame12q, body1Anchor2)); + Vec4V rb3 = Vec4V_From_Vec3V(QuatRotate(bodyFrame13q, body1Anchor3)); + + Vec4V rbX, rbY, rbZ; + PX_TRANSPOSE_44_34(rb0, rb1, rb2, rb3, rbX, rbY, rbZ); + + /*rbX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbX)), zero, rbX); + rbY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbY)), zero, rbY); + rbZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbZ)), zero, rbZ);*/ + + const Vec4V rbWorldX = V4Add(rbX, bodyFrame1pX); + const Vec4V rbWorldY = V4Add(rbY, bodyFrame1pY); + const Vec4V rbWorldZ = V4Add(rbZ, bodyFrame1pZ); + + Vec4V errorX = V4Sub(raWorldX, rbWorldX); + Vec4V errorY = V4Sub(raWorldY, rbWorldY); + Vec4V errorZ = V4Sub(raWorldZ, rbWorldZ); + + /*errorX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorX)), zero, errorX); + errorY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorY)), zero, errorY); + errorZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(errorZ)), zero, errorZ);*/ + + //KS - todo - get this working with per-point friction + PxU32 contactIndex0 = c.contactID[frictionIndex0][index0]; + PxU32 contactIndex1 = c.contactID[frictionIndex1][index1]; + PxU32 contactIndex2 = c.contactID[frictionIndex2][index2]; + PxU32 contactIndex3 = c.contactID[frictionIndex3][index3]; + + //Ensure that the ocntact indices are valid + PX_ASSERT(contactIndex0 == 0xffff || contactIndex0 < descs[0].numContacts); + PX_ASSERT(contactIndex1 == 0xffff || contactIndex1 < descs[1].numContacts); + PX_ASSERT(contactIndex2 == 0xffff || contactIndex2 < descs[2].numContacts); + PX_ASSERT(contactIndex3 == 0xffff || contactIndex3 < descs[3].numContacts); + + Vec4V targetVel0 = V4LoadA(contactIndex0 == 0xFFFF ? &contactBase0->targetVel.x : &descs[0].contacts[contactIndex0].targetVel.x); + Vec4V targetVel1 = V4LoadA(contactIndex1 == 0xFFFF ? &contactBase0->targetVel.x : &descs[1].contacts[contactIndex1].targetVel.x); + Vec4V targetVel2 = V4LoadA(contactIndex2 == 0xFFFF ? &contactBase0->targetVel.x : &descs[2].contacts[contactIndex2].targetVel.x); + Vec4V targetVel3 = V4LoadA(contactIndex3 == 0xFFFF ? &contactBase0->targetVel.x : &descs[3].contacts[contactIndex3].targetVel.x); + + Vec4V targetVelX, targetVelY, targetVelZ; + PX_TRANSPOSE_44_34(targetVel0, targetVel1, targetVel2, targetVel3, targetVelX, targetVelY, targetVelZ); + + + { + Vec4V raXnX = V4NegMulSub(raZ, t0Y, V4Mul(raY, t0Z)); + Vec4V raXnY = V4NegMulSub(raX, t0Z, V4Mul(raZ, t0X)); + Vec4V raXnZ = V4NegMulSub(raY, t0X, V4Mul(raX, t0Y)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + + Vec4V resp = V4MulAdd(dotDelAngVel0, angDom0, invMass0D0); + + const Vec4V tVel0 = V4MulAdd(t0Z, linVelT20, V4MulAdd(t0Y, linVelT10, V4Mul(t0X, linVelT00))); + Vec4V vrel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4MulAdd(raXnX, angVelT00, tVel0))); + + Vec4V delAngVel1X = zero; + Vec4V delAngVel1Y = zero; + Vec4V delAngVel1Z = zero; + + Vec4V vrel1 = zero; + + if (isDynamic) + { + Vec4V rbXnX = V4NegMulSub(rbZ, t0Y, V4Mul(rbY, t0Z)); + Vec4V rbXnY = V4NegMulSub(rbX, t0Z, V4Mul(rbZ, t0X)); + Vec4V rbXnZ = V4NegMulSub(rbY, t0X, V4Mul(rbX, t0Y)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + delAngVel1X = V4Mul(invInertia1X0, rbXnX); + delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + resp = V4Add(resp, resp1); + + const Vec4V tVel1 = V4MulAdd(t0Z, linVelT21, V4MulAdd(t0Y, linVelT11, V4Mul(t0X, linVelT01))); + vrel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + } + else if (hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, t0Y, V4Mul(rbY, t0Z)); + const Vec4V rbXnY = V4NegMulSub(rbX, t0Z, V4Mul(rbZ, t0X)); + const Vec4V rbXnZ = V4NegMulSub(rbY, t0X, V4Mul(rbX, t0Y)); + + const Vec4V tVel1 = V4MulAdd(t0Z, linVelT21, V4MulAdd(t0Y, linVelT11, V4Mul(t0X, linVelT01))); + vrel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + } + + f0->rbXnI[0] = delAngVel1X; + f0->rbXnI[1] = delAngVel1Y; + f0->rbXnI[2] = delAngVel1Z; + + const Vec4V velMultiplier = V4Mul(maxImpulseScale, V4Sel(V4IsGrtr(resp, zero), V4Div(p84, resp), zero)); + + Vec4V error = V4MulAdd(t0Z, errorZ, V4MulAdd(t0Y, errorY, V4Mul(t0X, errorX))); + + Vec4V targetVel = V4NegMulSub(vrel0, kinematicScale0, V4MulAdd(vrel1, kinematicScale1, V4MulAdd(t0Z, targetVelZ, V4MulAdd(t0Y, targetVelY, V4Mul(t0X, targetVelX))))); + + f0->normal[0] = t0X; + f0->normal[1] = t0Y; + f0->normal[2] = t0Z; + f0->raXnI[0] = delAngVel0X; + f0->raXnI[1] = delAngVel0Y; + f0->raXnI[2] = delAngVel0Z; + f0->error = error; + f0->velMultiplier = velMultiplier; + f0->biasCoefficient = V4Splat(invDt); + f0->targetVel = targetVel; + } + + { + Vec4V raXnX = V4NegMulSub(raZ, t1Y, V4Mul(raY, t1Z)); + Vec4V raXnY = V4NegMulSub(raX, t1Z, V4Mul(raZ, t1X)); + Vec4V raXnZ = V4NegMulSub(raY, t1X, V4Mul(raX, t1Y)); + + raXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnX)), zero, raXnX); + raXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnY)), zero, raXnY); + raXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(raXnZ)), zero, raXnZ); + + Vec4V delAngVel0X = V4Mul(invInertia0X0, raXnX); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, raXnX); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, raXnX); + + delAngVel0X = V4MulAdd(invInertia0Y0, raXnY, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, raXnY, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, raXnY, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, raXnZ, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, raXnZ, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, raXnZ, delAngVel0Z); + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0Z, delAngVel0Z, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0X, delAngVel0X))); + + Vec4V resp = V4MulAdd(dotDelAngVel0, angDom0, invMass0D0); + + const Vec4V tVel0 = V4MulAdd(t1Z, linVelT20, V4MulAdd(t1Y, linVelT10, V4Mul(t1X, linVelT00))); + Vec4V vrel0 = V4MulAdd(raXnZ, angVelT20, V4MulAdd(raXnY, angVelT10, V4MulAdd(raXnX, angVelT00, tVel0))); + + Vec4V delAngVel1X = zero; + Vec4V delAngVel1Y = zero; + Vec4V delAngVel1Z = zero; + + Vec4V vrel1 = zero; + + if (isDynamic) + { + + Vec4V rbXnX = V4NegMulSub(rbZ, t1Y, V4Mul(rbY, t1Z)); + Vec4V rbXnY = V4NegMulSub(rbX, t1Z, V4Mul(rbZ, t1X)); + Vec4V rbXnZ = V4NegMulSub(rbY, t1X, V4Mul(rbX, t1Y)); + + rbXnX = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnX)), zero, rbXnX); + rbXnY = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnY)), zero, rbXnY); + rbXnZ = V4Sel(V4IsGrtr(solverOffsetSlop, V4Abs(rbXnZ)), zero, rbXnZ); + + delAngVel1X = V4Mul(invInertia1X0, rbXnX); + delAngVel1Y = V4Mul(invInertia1X1, rbXnX); + delAngVel1Z = V4Mul(invInertia1X2, rbXnX); + + delAngVel1X = V4MulAdd(invInertia1Y0, rbXnY, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, rbXnY, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, rbXnY, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, rbXnZ, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, rbXnZ, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, rbXnZ, delAngVel1Z); + + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1Z, delAngVel1Z, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1X, delAngVel1X))); + + const Vec4V resp1 = V4MulAdd(dotDelAngVel1, angDom1, invMass1D1); + + resp = V4Add(resp, resp1); + + + + const Vec4V tVel1 = V4MulAdd(t1Z, linVelT21, V4MulAdd(t1Y, linVelT11, V4Mul(t1X, linVelT01))); + vrel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + } + else if (hasKinematic) + { + const Vec4V rbXnX = V4NegMulSub(rbZ, t1Y, V4Mul(rbY, t1Z)); + const Vec4V rbXnY = V4NegMulSub(rbX, t1Z, V4Mul(rbZ, t1X)); + const Vec4V rbXnZ = V4NegMulSub(rbY, t1X, V4Mul(rbX, t1Y)); + + const Vec4V tVel1 = V4MulAdd(t1Z, linVelT21, V4MulAdd(t1Y, linVelT11, V4Mul(t1X, linVelT01))); + vrel1 = V4MulAdd(rbXnZ, angVelT21, V4MulAdd(rbXnY, angVelT11, V4MulAdd(rbXnX, angVelT01, tVel1))); + } + + f1->rbXnI[0] = delAngVel1X; + f1->rbXnI[1] = delAngVel1Y; + f1->rbXnI[2] = delAngVel1Z; + + + const Vec4V velMultiplier = V4Mul(maxImpulseScale, V4Sel(V4IsGrtr(resp, zero), V4Div(p84, resp), zero)); + + Vec4V error = V4MulAdd(t1Z, errorZ, V4MulAdd(t1Y, errorY, V4Mul(t1X, errorX))); + + Vec4V targetVel = V4NegMulSub(vrel0, kinematicScale0, V4MulAdd(vrel1, kinematicScale1, V4MulAdd(t1Z, targetVelZ, V4MulAdd(t1Y, targetVelY, V4Mul(t1X, targetVelX))))); + + f1->normal[0] = t1X; + f1->normal[1] = t1Y; + f1->normal[2] = t1Z; + f1->raXnI[0] = delAngVel0X; + f1->raXnI[1] = delAngVel0Y; + f1->raXnI[2] = delAngVel0Z; + f1->error = error; + f1->velMultiplier = velMultiplier; + f1->targetVel = targetVel; + f1->biasCoefficient = V4Splat(invDt); + } + } + + frictionPatchWritebackAddrIndex0++; + frictionPatchWritebackAddrIndex1++; + frictionPatchWritebackAddrIndex2++; + frictionPatchWritebackAddrIndex3++; + } + } + } +} + + + +PX_FORCE_INLINE void computeBlockStreamFrictionByteSizes(const CorrelationBuffer& c, + PxU32& _frictionPatchByteSize, PxU32& _numFrictionPatches, + PxU32 frictionPatchStartIndex, PxU32 frictionPatchEndIndex) +{ + // PT: use local vars to remove LHS + PxU32 numFrictionPatches = 0; + + for (PxU32 i = frictionPatchStartIndex; i < frictionPatchEndIndex; i++) + { + //Friction patches. + if (c.correlationListHeads[i] != CorrelationBuffer::LIST_END) + numFrictionPatches++; + } + PxU32 frictionPatchByteSize = numFrictionPatches * sizeof(FrictionPatch); + + _numFrictionPatches = numFrictionPatches; + + //16-byte alignment. + _frictionPatchByteSize = ((frictionPatchByteSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_frictionPatchByteSize & 0x0f)); +} + + +static bool reserveFrictionBlockStreams(const CorrelationBuffer& c, PxConstraintAllocator& constraintAllocator, PxU32 frictionPatchStartIndex, PxU32 frictionPatchEndIndex, + FrictionPatch*& _frictionPatches, + PxU32& numFrictionPatches) +{ + + //From frictionPatchStream we just need to reserve a single buffer. + PxU32 frictionPatchByteSize = 0; + //Compute the sizes of all the buffers. + + computeBlockStreamFrictionByteSizes(c, frictionPatchByteSize, numFrictionPatches, frictionPatchStartIndex, frictionPatchEndIndex); + + FrictionPatch* frictionPatches = NULL; + //If the constraint block reservation didn't fail then reserve the friction buffer too. + if (frictionPatchByteSize > 0) + { + frictionPatches = reinterpret_cast(constraintAllocator.reserveFrictionData(frictionPatchByteSize)); + + if (0 == frictionPatches || (reinterpret_cast(-1)) == frictionPatches) + { + if (0 == frictionPatches) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of friction data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + frictionPatches = NULL; + } + } + } + + _frictionPatches = frictionPatches; + + //Return true if neither of the two block reservations failed. + return (0 == frictionPatchByteSize || frictionPatches); +} + +//The persistent friction patch correlation/allocation will already have happenned as this is per-pair. +//This function just computes the size of the combined solve data. +void computeBlockStreamByteSizes4(PxTGSSolverContactDesc* descs, + PxU32& _solverConstraintByteSize, PxU32* _axisConstraintCount, + const CorrelationBuffer& c) +{ + PX_ASSERT(0 == _solverConstraintByteSize); + + PxU32 maxPatches = 0; + PxU32 maxFrictionPatches = 0; + PxU32 maxContactCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxU32 maxFrictionCount[CorrelationBuffer::MAX_FRICTION_PATCHES]; + PxMemZero(maxContactCount, sizeof(maxContactCount)); + PxMemZero(maxFrictionCount, sizeof(maxFrictionCount)); + bool hasMaxImpulse = false; + + for (PxU32 a = 0; a < 4; ++a) + { + PxU32 axisConstraintCount = 0; + hasMaxImpulse = hasMaxImpulse || descs[a].hasMaxImpulse; + for (PxU32 i = 0; i < descs[a].numFrictionPatches; i++) + { + PxU32 ind = i + descs[a].startFrictionPatchIndex; + + const FrictionPatch& frictionPatch = c.frictionPatches[ind]; + + const bool haveFriction = (frictionPatch.materialFlags & PxMaterialFlag::eDISABLE_FRICTION) == 0 + && frictionPatch.anchorCount != 0; + //Solver constraint data. + if (c.frictionPatchContactCounts[ind] != 0) + { + maxContactCount[i] = PxMax(c.frictionPatchContactCounts[ind], maxContactCount[i]); + axisConstraintCount += c.frictionPatchContactCounts[ind]; + + if (haveFriction) + { + const PxU32 fricCount = PxU32(c.frictionPatches[ind].anchorCount) * 2; + maxFrictionCount[i] = PxMax(fricCount, maxFrictionCount[i]); + axisConstraintCount += fricCount; + } + } + } + maxPatches = PxMax(descs[a].numFrictionPatches, maxPatches); + _axisConstraintCount[a] = axisConstraintCount; + } + + for (PxU32 a = 0; a < maxPatches; ++a) + { + if (maxFrictionCount[a] > 0) + maxFrictionPatches++; + } + + + PxU32 totalContacts = 0, totalFriction = 0; + for (PxU32 a = 0; a < maxPatches; ++a) + { + totalContacts += maxContactCount[a]; + totalFriction += maxFrictionCount[a]; + } + + //OK, we have a given number of friction patches, contact points and friction constraints so we can calculate how much memory we need + + //Body 2 is considered static if it is either *not dynamic* or *kinematic* + + /*bool hasDynamicBody = false; + for (PxU32 a = 0; a < 4; ++a) + { + hasDynamicBody = hasDynamicBody || ((descs[a].bodyState1 == PxSolverContactDesc::eDYNAMIC_BODY)); + } + + + const bool isStatic = !hasDynamicBody;*/ + + const PxU32 headerSize = sizeof(SolverContactHeaderStepBlock) * maxPatches; + //PxU32 constraintSize = isStatic ? (sizeof(SolverContactBatchPointBase4) * totalContacts) + (sizeof(SolverContactFrictionBase4) * totalFriction) : + // (sizeof(SolverContactBatchPointDynamic4) * totalContacts) + (sizeof(SolverContactFrictionDynamic4) * totalFriction); + + PxU32 constraintSize = (sizeof(SolverContactPointStepBlock) * totalContacts) + (sizeof(SolverContactFrictionStepBlock) * totalFriction); + + //Space for the appliedForce buffer + constraintSize += sizeof(Vec4V)*(totalContacts + totalFriction); + + //If we have max impulse, reserve a buffer for it + if (hasMaxImpulse) + constraintSize += sizeof(Ps::aos::Vec4V) * totalContacts; + + _solverConstraintByteSize = ((constraintSize + headerSize + 0x0f) & ~0x0f); + PX_ASSERT(0 == (_solverConstraintByteSize & 0x0f)); +} + +static SolverConstraintPrepState::Enum reserveBlockStreams4(PxTGSSolverContactDesc* descs, Dy::CorrelationBuffer& c, + PxU8*& solverConstraint, PxU32* axisConstraintCount, + PxU32& solverConstraintByteSize, + PxConstraintAllocator& constraintAllocator) +{ + PX_ASSERT(NULL == solverConstraint); + PX_ASSERT(0 == solverConstraintByteSize); + + //Compute the sizes of all the buffers. + computeBlockStreamByteSizes4(descs, + solverConstraintByteSize, axisConstraintCount, + c); + + //Reserve the buffers. + + //First reserve the accumulated buffer size for the constraint block. + PxU8* constraintBlock = NULL; + const PxU32 constraintBlockByteSize = solverConstraintByteSize; + if (constraintBlockByteSize > 0) + { + if ((constraintBlockByteSize + 16u) > 16384) + return SolverConstraintPrepState::eUNBATCHABLE; + + constraintBlock = constraintAllocator.reserveConstraintData(constraintBlockByteSize + 16u); + + if (0 == constraintBlock || (reinterpret_cast(-1)) == constraintBlock) + { + if (0 == constraintBlock) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept dropped contacts or increase buffer size allocated for narrow phase by increasing PxSceneDesc::maxNbContactDataBlocks."); + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of contact data for a single contact pair in constraint prep. " + "Either accept dropped contacts or simplify collision geometry."); + constraintBlock = NULL; + } + } + } + + //Patch up the individual ptrs to the buffer returned by the constraint block reservation (assuming the reservation didn't fail). + if (0 == constraintBlockByteSize || constraintBlock) + { + if (solverConstraintByteSize) + { + solverConstraint = constraintBlock; + PX_ASSERT(0 == (uintptr_t(solverConstraint) & 0x0f)); + } + } + + return ((0 == constraintBlockByteSize || constraintBlock)) ? SolverConstraintPrepState::eSUCCESS : SolverConstraintPrepState::eOUT_OF_MEMORY; +} + + + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Step( + Dy::CorrelationBuffer& c, + PxTGSSolverContactDesc* blockDescs, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + PX_ALIGN(16, PxReal invMassScale0[4]); + PX_ALIGN(16, PxReal invMassScale1[4]); + PX_ALIGN(16, PxReal invInertiaScale0[4]); + PX_ALIGN(16, PxReal invInertiaScale1[4]); + + c.frictionPatchCount = 0; + c.contactPatchCount = 0; + + for (PxU32 a = 0; a < 4; ++a) + { + PxTGSSolverContactDesc& blockDesc = blockDescs[a]; + + invMassScale0[a] = blockDesc.mInvMassScales.linear0; + invMassScale1[a] = blockDesc.mInvMassScales.linear1; + invInertiaScale0[a] = blockDesc.mInvMassScales.angular0; + invInertiaScale1[a] = blockDesc.mInvMassScales.angular1; + + blockDesc.startFrictionPatchIndex = c.frictionPatchCount; + if (!(blockDesc.disableStrongFriction)) + { + bool valid = getFrictionPatches(c, blockDesc.frictionPtr, blockDesc.frictionCount, + blockDesc.bodyFrame0, blockDesc.bodyFrame1, correlationDistance); + if (!valid) + return SolverConstraintPrepState::eUNBATCHABLE; + } + //Create the contact patches + blockDesc.startContactPatchIndex = c.contactPatchCount; + if (!createContactPatches(c, blockDesc.contacts, blockDesc.numContacts, PXC_SAME_NORMAL)) + return SolverConstraintPrepState::eUNBATCHABLE; + blockDesc.numContactPatches = PxU16(c.contactPatchCount - blockDesc.startContactPatchIndex); + + bool overflow = correlatePatches(c, blockDesc.contacts, blockDesc.bodyFrame0, blockDesc.bodyFrame1, PXC_SAME_NORMAL, + blockDesc.startContactPatchIndex, blockDesc.startFrictionPatchIndex); + + if (overflow) + return SolverConstraintPrepState::eUNBATCHABLE; + + growPatches(c, blockDesc.contacts, blockDesc.bodyFrame0, blockDesc.bodyFrame1, correlationDistance, blockDesc.startFrictionPatchIndex, + frictionOffsetThreshold + blockDescs[a].restDistance); + + //Remove the empty friction patches - do we actually need to do this? + for (PxU32 p = c.frictionPatchCount; p > blockDesc.startFrictionPatchIndex; --p) + { + if (c.correlationListHeads[p - 1] == 0xffff) + { + //We have an empty patch...need to bin this one... + for (PxU32 p2 = p; p2 < c.frictionPatchCount; ++p2) + { + c.correlationListHeads[p2 - 1] = c.correlationListHeads[p2]; + c.frictionPatchContactCounts[p2 - 1] = c.frictionPatchContactCounts[p2]; + } + c.frictionPatchCount--; + } + } + + PxU32 numFricPatches = c.frictionPatchCount - blockDesc.startFrictionPatchIndex; + blockDesc.numFrictionPatches = numFricPatches; + } + + FrictionPatch* frictionPatchArray[4]; + PxU32 frictionPatchCounts[4]; + + for (PxU32 a = 0; a < 4; ++a) + { + PxTGSSolverContactDesc& blockDesc = blockDescs[a]; + + const bool successfulReserve = reserveFrictionBlockStreams(c, constraintAllocator, blockDesc.startFrictionPatchIndex, blockDesc.numFrictionPatches + blockDesc.startFrictionPatchIndex, + frictionPatchArray[a], + frictionPatchCounts[a]); + + //KS - TODO - how can we recover if we failed to allocate this memory? + if (!successfulReserve) + { + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + } + //At this point, all the friction data has been calculated, the correlation has been done. Provided this was all successful, + //we are ready to create the batched constraints + + PxU8* solverConstraint = NULL; + PxU32 solverConstraintByteSize = 0; + + + + { + PxU32 axisConstraintCount[4]; + SolverConstraintPrepState::Enum state = reserveBlockStreams4(blockDescs, c, + solverConstraint, axisConstraintCount, + solverConstraintByteSize, + constraintAllocator); + + if (state != SolverConstraintPrepState::eSUCCESS) + return state; + + + for (PxU32 a = 0; a < 4; ++a) + { + + FrictionPatch* frictionPatches = frictionPatchArray[a]; + + PxTGSSolverContactDesc& blockDesc = blockDescs[a]; + PxTGSSolverConstraintDesc& desc = *blockDesc.desc; + blockDesc.frictionPtr = reinterpret_cast(frictionPatches); + blockDesc.frictionCount = Ps::to8(frictionPatchCounts[a]); + + //Initialise friction buffer. + if (frictionPatches) + { + // PT: TODO: revisit this... not very satisfying + //const PxU32 maxSize = numFrictionPatches*sizeof(FrictionPatch); + Ps::prefetchLine(frictionPatches); + Ps::prefetchLine(frictionPatches, 128); + Ps::prefetchLine(frictionPatches, 256); + + for (PxU32 i = 0; i(solverConstraint + solverConstraintByteSize)) = 0; + } + return SolverConstraintPrepState::eSUCCESS; +} + + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Step( + PxsContactManagerOutput** cmOutputs, + ThreadContext& threadContext, + PxTGSSolverContactDesc* blockDescs, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator) +{ + + for (PxU32 a = 0; a < 4; ++a) + { + blockDescs[a].desc->constraintLengthOver16 = 0; + } + + //PX_ASSERT(cmOutputs[0]->nbContacts && cmOutputs[1]->nbContacts && cmOutputs[2]->nbContacts && cmOutputs[3]->nbContacts); + + + Gu::ContactBuffer& buffer = threadContext.mContactBuffer; + + buffer.count = 0; + + //PxTransform idt = PxTransform(PxIdentity); + + CorrelationBuffer& c = threadContext.mCorrelationBuffer; + + for (PxU32 a = 0; a < 4; ++a) + { + PxTGSSolverContactDesc& blockDesc = blockDescs[a]; + PxTGSSolverConstraintDesc& desc = *blockDesc.desc; + + //blockDesc.startContactIndex = buffer.count; + blockDesc.contacts = buffer.contacts + buffer.count; + + Ps::prefetchLine(desc.bodyA); + Ps::prefetchLine(desc.bodyB); + + + //Unbatchable if we have (a) too many contacts or (b) torsional friction enabled - it just seems easier to handle this on an individual contact basis because it is expected to + //be used relatively rarely + if ((buffer.count + cmOutputs[a]->nbContacts) > 64 || (blockDesc.torsionalPatchRadius != 0.f || blockDesc.minTorsionalPatchRadius != 0.f) ) + { + return SolverConstraintPrepState::eUNBATCHABLE; + } + + bool hasMaxImpulse = false; + bool hasTargetVelocity = false; + + //OK...do the correlation here as well... + Ps::prefetchLine(blockDescs[a].frictionPtr); + Ps::prefetchLine(blockDescs[a].frictionPtr, 64); + Ps::prefetchLine(blockDescs[a].frictionPtr, 128); + + if (a < 3) + { + Ps::prefetchLine(cmOutputs[a]->contactPatches); + Ps::prefetchLine(cmOutputs[a]->contactPoints); + } + + PxReal invMassScale0, invMassScale1, invInertiaScale0, invInertiaScale1; + + const PxReal defaultMaxImpulse = PxMin(blockDesc.bodyData0->maxContactImpulse, blockDesc.bodyData1->maxContactImpulse); + + PxU32 contactCount = extractContacts(buffer, *cmOutputs[a], hasMaxImpulse, hasTargetVelocity, invMassScale0, invMassScale1, + invInertiaScale0, invInertiaScale1, defaultMaxImpulse); + + if (contactCount == 0 || hasTargetVelocity) + return SolverConstraintPrepState::eUNBATCHABLE; + + blockDesc.numContacts = contactCount; + blockDesc.hasMaxImpulse = hasMaxImpulse; + blockDesc.disableStrongFriction = blockDesc.disableStrongFriction || hasTargetVelocity; + + blockDesc.mInvMassScales.linear0 *= invMassScale0; + blockDesc.mInvMassScales.linear1 *= invMassScale1; + blockDesc.mInvMassScales.angular0 *= blockDesc.body0->isKinematic ? 0.f : invInertiaScale0; + blockDesc.mInvMassScales.angular1 *= blockDesc.body1->isKinematic ? 0.f : invInertiaScale1; + } + + return createFinalizeSolverContacts4Step(c, blockDescs, + invDtF32, invTotalDtF32, bounceThresholdF32, frictionOffsetThreshold, + correlationDistance, solverOffsetSlop, constraintAllocator); +} + +PX_FORCE_INLINE PxU32 getConstraintLength(const PxTGSSolverConstraintDesc& desc) +{ + return PxU32(desc.constraintLengthOver16 << 4); +} + +PX_FORCE_INLINE PxU32 getWritebackLength(const PxTGSSolverConstraintDesc& desc) +{ + return PxU32(desc.writeBackLengthOver4 << 2); +} + + + +void preprocessRows(Px1DConstraint** sorted, + Px1DConstraint* rows, + PxVec4* angSqrtInvInertia0, + PxVec4* angSqrtInvInertia1, + PxU32 rowCount, + const PxMat33& sqrtInvInertia0F32, + const PxMat33& sqrtInvInertia1F32, + const PxReal invMass0, + const PxReal invMass1, + const PxConstraintInvMassScale& ims, + bool disablePreprocessing, + bool diagonalizeDrive, + bool preprocessLinear = true); + +void setSolverConstantsStep(PxReal& error, + PxReal& biasScale, + PxReal& targetVel, + PxReal& maxBias, + PxReal& velMultiplier, + PxReal& impulseMultiplier, + PxReal& rcpResponse, + const Px1DConstraint& c, + PxReal normalVel, + PxReal unitResponse, + PxReal minRowResponse, + PxReal erp, + PxReal dt, + PxReal totalDt, + PxReal biasClamp, + PxReal recipdt, + PxReal recipTotalDt); + + +namespace +{ + void setConstants(PxReal& error, PxReal& biasScale, PxReal& targetVel, PxReal& maxBias, PxReal& velMultiplier, PxReal& impulseMultiplier, + PxReal& rcpResponse, const Px1DConstraint& c, PxReal unitResponse, PxReal minRowResponse, PxReal dt, PxReal totalDt, + PxReal recipdt, PxReal recipTotalDt, const bool finished, + const PxReal lengthScale, const PxReal nv, const PxReal nv0, const PxReal nv1, const bool isKinematic0, const bool isKinematic1) + { + PX_UNUSED(dt); + if (finished) + { + error = 0.f; + biasScale = 0.f; + maxBias = 0.f; + velMultiplier = 0.f; + impulseMultiplier = 0.f; + rcpResponse = 0.f; + targetVel = 0.f; + return; + } + + PxReal erp = 1.f; + PxReal linearErp = 1.f; + + //PxReal biasClamp = c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT ? 50.f : 200.f*lengthScale; + PxReal biasClamp = c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT ? 100.f : 1000.f*lengthScale; + + setSolverConstantsStep(error, biasScale, targetVel, maxBias, velMultiplier, impulseMultiplier, rcpResponse, c, nv, unitResponse, + minRowResponse, c.flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT ? erp : linearErp, dt, totalDt, biasClamp, recipdt, recipTotalDt); + + if(isKinematic0) + targetVel -= nv0; + if(isKinematic1) + targetVel += nv1; + } + + void setOrthoData(const PxReal& ang0X, const PxReal& ang0Y, const PxReal& ang0Z, const PxReal& ang1X, const PxReal& ang1Y, const PxReal& ang1Z, + const PxReal& recipResponse, const PxReal& error, PxReal& orthoAng0X, PxReal& orthoAng0Y, PxReal& orthoAng0Z, PxReal& orthoAng1X, PxReal& orthoAng1Y, PxReal& orthoAng1Z, + PxReal& orthoRecipResponse, PxReal& orthoError, const bool disableProcessing, const PxU32 solveHint, PxU32& flags, PxU32& orthoCount, const bool finished) + { + if (!finished && !disableProcessing) + { + if (solveHint == PxConstraintSolveHint::eROTATIONAL_EQUALITY) + { + flags |= DY_SC_FLAG_ROT_EQ; + orthoAng0X = ang0X; orthoAng0Y = ang0Y; orthoAng0Z = ang0Z; + orthoAng1X = ang1X; orthoAng1Y = ang1Y; orthoAng1Z = ang1Z; + orthoRecipResponse = recipResponse; + orthoError = error; + orthoCount++; + } + else if (solveHint & PxConstraintSolveHint::eEQUALITY) + flags |= DY_SC_FLAG_ORTHO_TARGET; + } + } +} + +SolverConstraintPrepState::Enum setupSolverConstraintStep4 +(PxTGSSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal totalDt, const PxReal recipdt, const PxReal recipTotalDt, PxU32& totalRows, + PxConstraintAllocator& allocator, PxU32 maxRows, const PxReal lengthScale); + +SolverConstraintPrepState::Enum setupSolverConstraintStep4 +(SolverConstraintShaderPrepDesc* PX_RESTRICT constraintShaderDescs, + PxTGSSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal totalDt, const PxReal recipdt, const PxReal recipTotalDt, PxU32& totalRows, + PxConstraintAllocator& allocator, const PxReal lengthScale) + +{ + //KS - we will never get here with constraints involving articulations so we don't need to stress about those in here + + totalRows = 0; + + Px1DConstraint allRows[MAX_CONSTRAINT_ROWS * 4]; + + PxU32 numRows = 0; + + PxU32 maxRows = 0; + PxU32 preppedIndex = 0; + + for (PxU32 a = 0; a < 4; ++a) + { + Px1DConstraint* rows = allRows + numRows; + SolverConstraintShaderPrepDesc& shaderDesc = constraintShaderDescs[a]; + PxTGSSolverConstraintPrepDesc& desc = constraintDescs[a]; + + if (!shaderDesc.solverPrep) + return SolverConstraintPrepState::eUNBATCHABLE; + + PxMemZero(rows + preppedIndex, sizeof(Px1DConstraint)*(MAX_CONSTRAINT_ROWS)); + for (PxU32 b = preppedIndex; b < MAX_CONSTRAINT_ROWS; ++b) + { + Px1DConstraint& c = rows[b]; + //Px1DConstraintInit(c); + c.minImpulse = -PX_MAX_REAL; + c.maxImpulse = PX_MAX_REAL; + } + + desc.mInvMassScales.linear0 = desc.mInvMassScales.linear1 = desc.mInvMassScales.angular0 = desc.mInvMassScales.angular1 = 1.f; + + desc.body0WorldOffset = PxVec3(0.f); + + PxU32 constraintCount = (*shaderDesc.solverPrep)(rows, + desc.body0WorldOffset, + MAX_CONSTRAINT_ROWS, + desc.mInvMassScales, + shaderDesc.constantBlock, + desc.bodyFrame0, desc.bodyFrame1, desc.extendedLimits, desc.cA2w, desc.cB2w); + + preppedIndex = MAX_CONSTRAINT_ROWS - constraintCount; + + maxRows = PxMax(constraintCount, maxRows); + + if (constraintCount == 0) + return SolverConstraintPrepState::eUNBATCHABLE; + + desc.rows = rows; + desc.numRows = constraintCount; + numRows += constraintCount; + + if (desc.body0->isKinematic) + desc.mInvMassScales.angular0 = 0.f; + if (desc.body1->isKinematic) + desc.mInvMassScales.angular1 = 0.f; + + } + + return setupSolverConstraintStep4(constraintDescs, dt, totalDt, recipdt, recipTotalDt, totalRows, allocator, maxRows, lengthScale); +} + +SolverConstraintPrepState::Enum setupSolverConstraintStep4 +(PxTGSSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal totalDt, const PxReal recipdt, const PxReal recipTotalDt, PxU32& totalRows, + PxConstraintAllocator& allocator, PxU32 maxRows, + const PxReal lengthScale) +{ + const Vec4V zero = V4Zero(); + Px1DConstraint* allSorted[MAX_CONSTRAINT_ROWS * 4]; + PxU32 startIndex[4]; + PX_ALIGN(16, PxVec4) angSqrtInvInertia0[MAX_CONSTRAINT_ROWS * 4]; + PX_ALIGN(16, PxVec4) angSqrtInvInertia1[MAX_CONSTRAINT_ROWS * 4]; + + PxU32 numRows = 0; + + for (PxU32 a = 0; a < 4; ++a) + { + startIndex[a] = numRows; + PxTGSSolverConstraintPrepDesc& desc = constraintDescs[a]; + Px1DConstraint** sorted = allSorted + numRows; + + for (PxU32 i = 0; i < desc.numRows; ++i) + { + if (desc.rows[i].flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT) + { + if (desc.rows[i].solveHint == PxConstraintSolveHint::eEQUALITY) + desc.rows[i].solveHint = PxConstraintSolveHint::eROTATIONAL_EQUALITY; + else if (desc.rows[i].solveHint == PxConstraintSolveHint::eINEQUALITY) + desc.rows[i].solveHint = PxConstraintSolveHint::eROTATIONAL_INEQUALITY; + } + } + + + preprocessRows(sorted, desc.rows, angSqrtInvInertia0 + numRows, angSqrtInvInertia1 + numRows, desc.numRows, + desc.body0TxI->sqrtInvInertia, desc.body1TxI->sqrtInvInertia, desc.bodyData0->invMass, desc.bodyData1->invMass, + desc.mInvMassScales, desc.disablePreprocessing, desc.improvedSlerp, false); + + numRows += desc.numRows; + } + + + PxU32 stride = sizeof(SolverConstraint1DStep4); + + + const PxU32 constraintLength = sizeof(SolverConstraint1DHeaderStep4) + stride * maxRows; + + //KS - +16 is for the constraint progress counter, which needs to be the last element in the constraint (so that we + //know SPU DMAs have completed) + PxU8* ptr = allocator.reserveConstraintData(constraintLength + 16u); + if (NULL == ptr || (reinterpret_cast(-1)) == ptr) + { + for (PxU32 a = 0; a < 4; ++a) + { + PxTGSSolverConstraintPrepDesc& desc = constraintDescs[a]; + desc.desc->constraint = NULL; + desc.desc->constraintLengthOver16 = 0; + desc.desc->writeBack = desc.writeback; + } + + if (NULL == ptr) + { + PX_WARN_ONCE( + "Reached limit set by PxSceneDesc::maxNbContactDataBlocks - ran out of buffer space for constraint prep. " + "Either accept joints detaching/exploding or increase buffer size allocated for constraint prep by increasing PxSceneDesc::maxNbContactDataBlocks."); + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + else + { + PX_WARN_ONCE( + "Attempting to allocate more than 16K of constraint data. " + "Either accept joints detaching/exploding or simplify constraints."); + ptr = NULL; + return SolverConstraintPrepState::eOUT_OF_MEMORY; + } + } + //desc.constraint = ptr; + + totalRows = numRows; + + const bool isKinematic00 = constraintDescs[0].body0->isKinematic; + const bool isKinematic01 = constraintDescs[0].body1->isKinematic; + const bool isKinematic10 = constraintDescs[1].body0->isKinematic; + const bool isKinematic11 = constraintDescs[1].body1->isKinematic; + const bool isKinematic20 = constraintDescs[2].body0->isKinematic; + const bool isKinematic21 = constraintDescs[2].body1->isKinematic; + const bool isKinematic30 = constraintDescs[3].body0->isKinematic; + const bool isKinematic31 = constraintDescs[3].body1->isKinematic; + + for (PxU32 a = 0; a < 4; ++a) + { + PxTGSSolverConstraintPrepDesc& desc = constraintDescs[a]; + desc.desc->constraint = ptr; + desc.desc->constraintLengthOver16 = PxU16(constraintLength/16); + desc.desc->writeBack = desc.writeback; + } + + { + PxU8* currPtr = ptr; + SolverConstraint1DHeaderStep4* header = reinterpret_cast(currPtr); + currPtr += sizeof(SolverConstraint1DHeaderStep4); + + const PxStepSolverBodyData& bd00 = *constraintDescs[0].bodyData0; + const PxStepSolverBodyData& bd01 = *constraintDescs[1].bodyData0; + const PxStepSolverBodyData& bd02 = *constraintDescs[2].bodyData0; + const PxStepSolverBodyData& bd03 = *constraintDescs[3].bodyData0; + + const PxStepSolverBodyData& bd10 = *constraintDescs[0].bodyData1; + const PxStepSolverBodyData& bd11 = *constraintDescs[1].bodyData1; + const PxStepSolverBodyData& bd12 = *constraintDescs[2].bodyData1; + const PxStepSolverBodyData& bd13 = *constraintDescs[3].bodyData1; + + //Load up masses, invInertia, velocity etc. + + const Vec4V invMassScale0 = V4LoadXYZW(constraintDescs[0].mInvMassScales.linear0, constraintDescs[1].mInvMassScales.linear0, + constraintDescs[2].mInvMassScales.linear0, constraintDescs[3].mInvMassScales.linear0); + const Vec4V invMassScale1 = V4LoadXYZW(constraintDescs[0].mInvMassScales.linear1, constraintDescs[1].mInvMassScales.linear1, + constraintDescs[2].mInvMassScales.linear1, constraintDescs[3].mInvMassScales.linear1); + + + const Vec4V iMass0 = V4LoadXYZW(bd00.invMass, bd01.invMass, bd02.invMass, bd03.invMass); + + const Vec4V iMass1 = V4LoadXYZW(bd10.invMass, bd11.invMass, bd12.invMass, bd13.invMass); + + const Vec4V invMass0 = V4Mul(iMass0, invMassScale0); + const Vec4V invMass1 = V4Mul(iMass1, invMassScale1); + + + const Vec4V invInertiaScale0 = V4LoadXYZW(constraintDescs[0].mInvMassScales.angular0, constraintDescs[1].mInvMassScales.angular0, + constraintDescs[2].mInvMassScales.angular0, constraintDescs[3].mInvMassScales.angular0); + const Vec4V invInertiaScale1 = V4LoadXYZW(constraintDescs[0].mInvMassScales.angular1, constraintDescs[1].mInvMassScales.angular1, + constraintDescs[2].mInvMassScales.angular1, constraintDescs[3].mInvMassScales.angular1); + + + //body world offsets + Vec4V workOffset0 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[0].body0WorldOffset)); + Vec4V workOffset1 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[1].body0WorldOffset)); + Vec4V workOffset2 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[2].body0WorldOffset)); + Vec4V workOffset3 = Vec4V_From_Vec3V(V3LoadU(constraintDescs[3].body0WorldOffset)); + + Vec4V workOffsetX, workOffsetY, workOffsetZ; + + PX_TRANSPOSE_44_34(workOffset0, workOffset1, workOffset2, workOffset3, workOffsetX, workOffsetY, workOffsetZ); + + const FloatV dtV = FLoad(totalDt); + Vec4V linBreakForce = V4LoadXYZW(constraintDescs[0].linBreakForce, constraintDescs[1].linBreakForce, + constraintDescs[2].linBreakForce, constraintDescs[3].linBreakForce); + Vec4V angBreakForce = V4LoadXYZW(constraintDescs[0].angBreakForce, constraintDescs[1].angBreakForce, + constraintDescs[2].angBreakForce, constraintDescs[3].angBreakForce); + + + header->breakable[0] = PxU8((constraintDescs[0].linBreakForce != PX_MAX_F32) || (constraintDescs[0].angBreakForce != PX_MAX_F32)); + header->breakable[1] = PxU8((constraintDescs[1].linBreakForce != PX_MAX_F32) || (constraintDescs[1].angBreakForce != PX_MAX_F32)); + header->breakable[2] = PxU8((constraintDescs[2].linBreakForce != PX_MAX_F32) || (constraintDescs[2].angBreakForce != PX_MAX_F32)); + header->breakable[3] = PxU8((constraintDescs[3].linBreakForce != PX_MAX_F32) || (constraintDescs[3].angBreakForce != PX_MAX_F32)); + + //OK, I think that's everything loaded in + + header->invMass0D0 = invMass0; + header->invMass1D1 = invMass1; + header->angD0 = invInertiaScale0; + header->angD1 = invInertiaScale1; + header->body0WorkOffset[0] = workOffsetX; + header->body0WorkOffset[1] = workOffsetY; + header->body0WorkOffset[2] = workOffsetZ; + + header->count = maxRows; + header->type = DY_SC_TYPE_BLOCK_1D; + header->linBreakImpulse = V4Scale(linBreakForce, dtV); + header->angBreakImpulse = V4Scale(angBreakForce, dtV); + header->counts[0] = Ps::to8(constraintDescs[0].numRows); + header->counts[1] = Ps::to8(constraintDescs[1].numRows); + header->counts[2] = Ps::to8(constraintDescs[2].numRows); + header->counts[3] = Ps::to8(constraintDescs[3].numRows); + + + Vec4V ca2WX, ca2WY, ca2WZ; + Vec4V cb2WX, cb2WY, cb2WZ; + + Vec4V ca2W0 = V4LoadU(&constraintDescs[0].cA2w.x); + Vec4V ca2W1 = V4LoadU(&constraintDescs[1].cA2w.x); + Vec4V ca2W2 = V4LoadU(&constraintDescs[2].cA2w.x); + Vec4V ca2W3 = V4LoadU(&constraintDescs[3].cA2w.x); + + Vec4V cb2W0 = V4LoadU(&constraintDescs[0].cB2w.x); + Vec4V cb2W1 = V4LoadU(&constraintDescs[1].cB2w.x); + Vec4V cb2W2 = V4LoadU(&constraintDescs[2].cB2w.x); + Vec4V cb2W3 = V4LoadU(&constraintDescs[3].cB2w.x); + + PX_TRANSPOSE_44_34(ca2W0, ca2W1, ca2W2, ca2W3, ca2WX, ca2WY, ca2WZ); + PX_TRANSPOSE_44_34(cb2W0, cb2W1, cb2W2, cb2W3, cb2WX, cb2WY, cb2WZ); + + Vec4V pos00 = V4LoadA(&constraintDescs[0].body0TxI->deltaBody2World.p.x); + Vec4V pos01 = V4LoadA(&constraintDescs[0].body1TxI->deltaBody2World.p.x); + Vec4V pos10 = V4LoadA(&constraintDescs[1].body0TxI->deltaBody2World.p.x); + Vec4V pos11 = V4LoadA(&constraintDescs[1].body1TxI->deltaBody2World.p.x); + Vec4V pos20 = V4LoadA(&constraintDescs[2].body0TxI->deltaBody2World.p.x); + Vec4V pos21 = V4LoadA(&constraintDescs[2].body1TxI->deltaBody2World.p.x); + Vec4V pos30 = V4LoadA(&constraintDescs[3].body0TxI->deltaBody2World.p.x); + Vec4V pos31 = V4LoadA(&constraintDescs[3].body1TxI->deltaBody2World.p.x); + + Vec4V pos0X, pos0Y, pos0Z; + Vec4V pos1X, pos1Y, pos1Z; + + PX_TRANSPOSE_44_34(pos00, pos10, pos20, pos30, pos0X, pos0Y, pos0Z); + PX_TRANSPOSE_44_34(pos01, pos11, pos21, pos31, pos1X, pos1Y, pos1Z); + + Vec4V linVel00 = V4LoadA(&constraintDescs[0].bodyData0->originalLinearVelocity.x); + Vec4V linVel01 = V4LoadA(&constraintDescs[0].bodyData1->originalLinearVelocity.x); + Vec4V angState00 = V4LoadA(&constraintDescs[0].bodyData0->originalAngularVelocity.x); + Vec4V angState01 = V4LoadA(&constraintDescs[0].bodyData1->originalAngularVelocity.x); + + Vec4V linVel10 = V4LoadA(&constraintDescs[1].bodyData0->originalLinearVelocity.x); + Vec4V linVel11 = V4LoadA(&constraintDescs[1].bodyData1->originalLinearVelocity.x); + Vec4V angState10 = V4LoadA(&constraintDescs[1].bodyData0->originalAngularVelocity.x); + Vec4V angState11 = V4LoadA(&constraintDescs[1].bodyData1->originalAngularVelocity.x); + + Vec4V linVel20 = V4LoadA(&constraintDescs[2].bodyData0->originalLinearVelocity.x); + Vec4V linVel21 = V4LoadA(&constraintDescs[2].bodyData1->originalLinearVelocity.x); + Vec4V angState20 = V4LoadA(&constraintDescs[2].bodyData0->originalAngularVelocity.x); + Vec4V angState21 = V4LoadA(&constraintDescs[2].bodyData1->originalAngularVelocity.x); + + Vec4V linVel30 = V4LoadA(&constraintDescs[3].bodyData0->originalLinearVelocity.x); + Vec4V linVel31 = V4LoadA(&constraintDescs[3].bodyData1->originalLinearVelocity.x); + Vec4V angState30 = V4LoadA(&constraintDescs[3].bodyData0->originalAngularVelocity.x); + Vec4V angState31 = V4LoadA(&constraintDescs[3].bodyData1->originalAngularVelocity.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2; + Vec4V linVel1T0, linVel1T1, linVel1T2; + Vec4V angState0T0, angState0T1, angState0T2; + Vec4V angState1T0, angState1T1, angState1T2; + + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2); + PX_TRANSPOSE_44_34(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2); + PX_TRANSPOSE_44_34(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2); + + + + const Vec4V raWorldX = V4Sub(ca2WX, pos0X); + const Vec4V raWorldY = V4Sub(ca2WY, pos0Y); + const Vec4V raWorldZ = V4Sub(ca2WZ, pos0Z); + + const Vec4V rbWorldX = V4Sub(cb2WX, pos1X); + const Vec4V rbWorldY = V4Sub(cb2WY, pos1Y); + const Vec4V rbWorldZ = V4Sub(cb2WZ, pos1Z); + + header->rAWorld[0] = raWorldX; + header->rAWorld[1] = raWorldY; + header->rAWorld[2] = raWorldZ; + + header->rBWorld[0] = rbWorldX; + header->rBWorld[1] = rbWorldY; + header->rBWorld[2] = rbWorldZ; + + //Now we loop over the constraints and build the results... + + PxU32 index0 = 0; + PxU32 endIndex0 = constraintDescs[0].numRows - 1; + PxU32 index1 = startIndex[1]; + PxU32 endIndex1 = index1 + constraintDescs[1].numRows - 1; + PxU32 index2 = startIndex[2]; + PxU32 endIndex2 = index2 + constraintDescs[2].numRows - 1; + PxU32 index3 = startIndex[3]; + PxU32 endIndex3 = index3 + constraintDescs[3].numRows - 1; + + const Vec4V one = V4One(); + const FloatV fOne = FOne(); + + PxU32 orthoCount0 = 0, orthoCount1 = 0, orthoCount2 = 0, orthoCount3 = 0; + + for (PxU32 a = 0; a < 3; ++a) + { + header->angOrthoAxis0X[a] = V4Zero(); + header->angOrthoAxis0Y[a] = V4Zero(); + header->angOrthoAxis0Z[a] = V4Zero(); + + header->angOrthoAxis1X[a] = V4Zero(); + header->angOrthoAxis1Y[a] = V4Zero(); + header->angOrthoAxis1Z[a] = V4Zero(); + + header->angOrthoRecipResponse[a] = V4Zero(); + header->angOrthoError[a] = V4Zero(); + } + + for (PxU32 a = 0; a < maxRows; ++a) + { + bool finished[] = { a >= constraintDescs[0].numRows, a >= constraintDescs[1].numRows, a >= constraintDescs[2].numRows, a >= constraintDescs[3].numRows }; + BoolV bFinished = BLoad(finished); + SolverConstraint1DStep4* c = reinterpret_cast(currPtr); + currPtr += stride; + + Px1DConstraint* con0 = allSorted[index0]; + Px1DConstraint* con1 = allSorted[index1]; + Px1DConstraint* con2 = allSorted[index2]; + Px1DConstraint* con3 = allSorted[index3]; + + bool angularConstraint[4] = + { + !!(con0->flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT), + !!(con1->flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT), + !!(con2->flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT), + !!(con3->flags & Px1DConstraintFlag::eANGULAR_CONSTRAINT), + }; + + BoolV bAngularConstraint = BLoad(angularConstraint); + + Vec4V cangDelta00 = V4LoadA(&angSqrtInvInertia0[index0].x); + Vec4V cangDelta01 = V4LoadA(&angSqrtInvInertia0[index1].x); + Vec4V cangDelta02 = V4LoadA(&angSqrtInvInertia0[index2].x); + Vec4V cangDelta03 = V4LoadA(&angSqrtInvInertia0[index3].x); + + Vec4V cangDelta10 = V4LoadA(&angSqrtInvInertia1[index0].x); + Vec4V cangDelta11 = V4LoadA(&angSqrtInvInertia1[index1].x); + Vec4V cangDelta12 = V4LoadA(&angSqrtInvInertia1[index2].x); + Vec4V cangDelta13 = V4LoadA(&angSqrtInvInertia1[index3].x); + + index0 = index0 == endIndex0 ? index0 : index0 + 1; + index1 = index1 == endIndex1 ? index1 : index1 + 1; + index2 = index2 == endIndex2 ? index2 : index2 + 1; + index3 = index3 == endIndex3 ? index3 : index3 + 1; + + Vec4V driveScale = one; + if (con0->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[0].driveLimitsAreForces) + driveScale = V4SetX(driveScale, FMin(fOne, dtV)); + if (con1->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[1].driveLimitsAreForces) + driveScale = V4SetY(driveScale, FMin(fOne, dtV)); + if (con2->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[2].driveLimitsAreForces) + driveScale = V4SetZ(driveScale, FMin(fOne, dtV)); + if (con3->flags&Px1DConstraintFlag::eHAS_DRIVE_LIMIT && constraintDescs[3].driveLimitsAreForces) + driveScale = V4SetW(driveScale, FMin(fOne, dtV)); + + + Vec4V clin00 = V4LoadA(&con0->linear0.x); + Vec4V clin01 = V4LoadA(&con1->linear0.x); + Vec4V clin02 = V4LoadA(&con2->linear0.x); + Vec4V clin03 = V4LoadA(&con3->linear0.x); + + Vec4V clin0X, clin0Y, clin0Z; + + PX_TRANSPOSE_44_34(clin00, clin01, clin02, clin03, clin0X, clin0Y, clin0Z); + + Vec4V cang00 = V4LoadA(&con0->angular0.x); + Vec4V cang01 = V4LoadA(&con1->angular0.x); + Vec4V cang02 = V4LoadA(&con2->angular0.x); + Vec4V cang03 = V4LoadA(&con3->angular0.x); + + Vec4V cang0X, cang0Y, cang0Z; + + PX_TRANSPOSE_44_34(cang00, cang01, cang02, cang03, cang0X, cang0Y, cang0Z); + + Vec4V cang10 = V4LoadA(&con0->angular1.x); + Vec4V cang11 = V4LoadA(&con1->angular1.x); + Vec4V cang12 = V4LoadA(&con2->angular1.x); + Vec4V cang13 = V4LoadA(&con3->angular1.x); + + Vec4V cang1X, cang1Y, cang1Z; + + PX_TRANSPOSE_44_34(cang10, cang11, cang12, cang13, cang1X, cang1Y, cang1Z); + + const Vec4V maxImpulse = V4LoadXYZW(con0->maxImpulse, con1->maxImpulse, con2->maxImpulse, con3->maxImpulse); + const Vec4V minImpulse = V4LoadXYZW(con0->minImpulse, con1->minImpulse, con2->minImpulse, con3->minImpulse); + + Vec4V angDelta0X, angDelta0Y, angDelta0Z; + + PX_TRANSPOSE_44_34(cangDelta00, cangDelta01, cangDelta02, cangDelta03, angDelta0X, angDelta0Y, angDelta0Z); + + c->flags[0] = 0; + c->flags[1] = 0; + c->flags[2] = 0; + c->flags[3] = 0; + + c->lin0[0] = V4Sel(bFinished, zero, clin0X); + c->lin0[1] = V4Sel(bFinished, zero, clin0Y); + c->lin0[2] = V4Sel(bFinished, zero, clin0Z); + c->ang0[0] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang0X, zero); + c->ang0[1] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang0Y, zero); + c->ang0[2] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang0Z, zero); + c->angularErrorScale = V4Sel(bAngularConstraint, one, zero); + + c->minImpulse = V4Mul(minImpulse, driveScale); + c->maxImpulse = V4Mul(maxImpulse, driveScale); + c->appliedForce = zero; + + const Vec4V lin0MagSq = V4MulAdd(clin0Z, clin0Z, V4MulAdd(clin0Y, clin0Y, V4Mul(clin0X, clin0X))); + const Vec4V cang0DotAngDelta = V4MulAdd(angDelta0Z, angDelta0Z, V4MulAdd(angDelta0Y, angDelta0Y, V4Mul(angDelta0X, angDelta0X))); + + Vec4V unitResponse = V4MulAdd(lin0MagSq, invMass0, V4Mul(cang0DotAngDelta, invInertiaScale0)); + + Vec4V clin10 = V4LoadA(&con0->linear1.x); + Vec4V clin11 = V4LoadA(&con1->linear1.x); + Vec4V clin12 = V4LoadA(&con2->linear1.x); + Vec4V clin13 = V4LoadA(&con3->linear1.x); + + Vec4V clin1X, clin1Y, clin1Z; + PX_TRANSPOSE_44_34(clin10, clin11, clin12, clin13, clin1X, clin1Y, clin1Z); + + Vec4V angDelta1X, angDelta1Y, angDelta1Z; + + PX_TRANSPOSE_44_34(cangDelta10, cangDelta11, cangDelta12, cangDelta13, angDelta1X, angDelta1Y, angDelta1Z); + + const Vec4V lin1MagSq = V4MulAdd(clin1Z, clin1Z, V4MulAdd(clin1Y, clin1Y, V4Mul(clin1X, clin1X))); + const Vec4V cang1DotAngDelta = V4MulAdd(angDelta1Z, angDelta1Z, V4MulAdd(angDelta1Y, angDelta1Y, V4Mul(angDelta1X, angDelta1X))); + + c->lin1[0] = V4Sel(bFinished, zero, clin1X); + c->lin1[1] = V4Sel(bFinished, zero, clin1Y); + c->lin1[2] = V4Sel(bFinished, zero, clin1Z); + + c->ang1[0] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang1X, zero); + c->ang1[1] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang1Y, zero); + c->ang1[2] = V4Sel(BAndNot(bAngularConstraint, bFinished), cang1Z, zero); + + unitResponse = V4Add(unitResponse, V4MulAdd(lin1MagSq, invMass1, V4Mul(cang1DotAngDelta, invInertiaScale1))); + + const Vec4V lnormalVel0 = V4MulAdd(clin0X, linVel0T0, V4MulAdd(clin0Y, linVel0T1, V4Mul(clin0Z, linVel0T2))); + const Vec4V lnormalVel1 = V4MulAdd(clin1X, linVel1T0, V4MulAdd(clin1Y, linVel1T1, V4Mul(clin1Z, linVel1T2))); + + const Vec4V angVel0 = V4MulAdd(cang0X, angState0T0, V4MulAdd(cang0Y, angState0T1, V4Mul(cang0Z, angState0T2))); + const Vec4V angVel1 = V4MulAdd(angDelta1X, angState1T0, V4MulAdd(angDelta1Y, angState1T1, V4Mul(angDelta1Z, angState1T2))); + + const Vec4V normalVel0 = V4Add(lnormalVel0, angVel0); + const Vec4V normalVel1 = V4Add(lnormalVel1, angVel1); + + const Vec4V normalVel = V4Sub(normalVel0, normalVel1); + + angDelta0X = V4Mul(angDelta0X, invInertiaScale0); + angDelta0Y = V4Mul(angDelta0Y, invInertiaScale0); + angDelta0Z = V4Mul(angDelta0Z, invInertiaScale0); + + angDelta1X = V4Mul(angDelta1X, invInertiaScale1); + angDelta1Y = V4Mul(angDelta1Y, invInertiaScale1); + angDelta1Z = V4Mul(angDelta1Z, invInertiaScale1); + + + + { + PxReal recipResponse[4]; + const PxVec4& ur = reinterpret_cast(unitResponse); + PxVec4& cBiasScale = reinterpret_cast(c->biasScale); + PxVec4& cError = reinterpret_cast(c->error); + PxVec4& cMaxBias = reinterpret_cast(c->maxBias); + PxVec4& cTargetVel = reinterpret_cast(c->velTarget); + PxVec4& cVelMultiplier = reinterpret_cast(c->velMultiplier); + PxVec4& cImpulseMultiplier = reinterpret_cast(c->impulseMultiplier); + const PxVec4& nVel = reinterpret_cast(normalVel); + const PxVec4& nVel0 = reinterpret_cast(normalVel0); + const PxVec4& nVel1 = reinterpret_cast(normalVel1); + + setConstants(cError.x, cBiasScale.x, cTargetVel.x, cMaxBias.x, cVelMultiplier.x, cImpulseMultiplier.x, + recipResponse[0], *con0, ur.x, constraintDescs[0].minResponseThreshold, dt, totalDt, recipdt, recipTotalDt, + a >= constraintDescs[0].numRows, lengthScale, nVel.x, nVel0.x, nVel1.x, isKinematic00, isKinematic01); + + setConstants(cError.y, cBiasScale.y, cTargetVel.y, cMaxBias.y, cVelMultiplier.y, cImpulseMultiplier.y, + recipResponse[1], *con1, ur.y, constraintDescs[1].minResponseThreshold, dt, totalDt, recipdt, recipTotalDt, + a >= constraintDescs[1].numRows, lengthScale, nVel.y, nVel0.y, nVel1.y, isKinematic10, isKinematic11); + + setConstants(cError.z, cBiasScale.z, cTargetVel.z, cMaxBias.z, cVelMultiplier.z, cImpulseMultiplier.z, + recipResponse[2], *con2, ur.z, constraintDescs[2].minResponseThreshold, dt, totalDt, recipdt, recipTotalDt, + a >= constraintDescs[2].numRows, lengthScale, nVel.z, nVel0.z, nVel1.z, isKinematic20, isKinematic21); + + setConstants(cError.w, cBiasScale.w, cTargetVel.w, cMaxBias.w, cVelMultiplier.w, cImpulseMultiplier.w, + recipResponse[3], *con3, ur.w, constraintDescs[3].minResponseThreshold, dt, totalDt, recipdt, recipTotalDt, + a >= constraintDescs[3].numRows, lengthScale, nVel.w, nVel0.w, nVel1.w, isKinematic30, isKinematic31); + + PxVec4* angOrthoAxes0X = reinterpret_cast(header->angOrthoAxis0X); + PxVec4* angOrthoAxes0Y = reinterpret_cast(header->angOrthoAxis0Y); + PxVec4* angOrthoAxes0Z = reinterpret_cast(header->angOrthoAxis0Z); + PxVec4* angOrthoAxes1X = reinterpret_cast(header->angOrthoAxis1X); + PxVec4* angOrthoAxes1Y = reinterpret_cast(header->angOrthoAxis1Y); + PxVec4* angOrthoAxes1Z = reinterpret_cast(header->angOrthoAxis1Z); + PxVec4* orthoRecipResponse = reinterpret_cast(header->angOrthoRecipResponse); + PxVec4* orthoError = reinterpret_cast(header->angOrthoError); + + const PxVec4& ang0X = reinterpret_cast(angDelta0X); + const PxVec4& ang0Y = reinterpret_cast(angDelta0Y); + const PxVec4& ang0Z = reinterpret_cast(angDelta0Z); + + const PxVec4& ang1X = reinterpret_cast(angDelta1X); + const PxVec4& ang1Y = reinterpret_cast(angDelta1Y); + const PxVec4& ang1Z = reinterpret_cast(angDelta1Z); + + setOrthoData(ang0X.x, ang0Y.x, ang0Z.x, ang1X.x, ang1Y.x, ang1Z.x, recipResponse[0], cError.x, angOrthoAxes0X[orthoCount0].x, + angOrthoAxes0Y[orthoCount0].x, angOrthoAxes0Z[orthoCount0].x, angOrthoAxes1X[orthoCount0].x, angOrthoAxes1Y[orthoCount0].x, + angOrthoAxes1Z[orthoCount0].x, orthoRecipResponse[orthoCount0].x, orthoError[orthoCount0].x, constraintDescs[0].disablePreprocessing, con0->solveHint, + c->flags[0], orthoCount0, a >= constraintDescs[0].numRows); + + setOrthoData(ang0X.y, ang0Y.y, ang0Z.y, ang1X.y, ang1Y.y, ang1Z.y, recipResponse[1], cError.y, angOrthoAxes0X[orthoCount1].y, + angOrthoAxes0Y[orthoCount1].y, angOrthoAxes0Z[orthoCount1].y, angOrthoAxes1X[orthoCount1].y, angOrthoAxes1Y[orthoCount1].y, + angOrthoAxes1Z[orthoCount1].y, orthoRecipResponse[orthoCount1].y, orthoError[orthoCount1].y, constraintDescs[1].disablePreprocessing, con1->solveHint, + c->flags[1], orthoCount1, a >= constraintDescs[1].numRows); + + setOrthoData(ang0X.z, ang0Y.z, ang0Z.z, ang1X.z, ang1Y.z, ang1Z.z, recipResponse[2], cError.z, angOrthoAxes0X[orthoCount2].z, + angOrthoAxes0Y[orthoCount2].z, angOrthoAxes0Z[orthoCount2].z, angOrthoAxes1X[orthoCount2].z, angOrthoAxes1Y[orthoCount2].z, + angOrthoAxes1Z[orthoCount2].z, orthoRecipResponse[orthoCount2].z, orthoError[orthoCount2].z, constraintDescs[2].disablePreprocessing, con2->solveHint, + c->flags[2], orthoCount2, a >= constraintDescs[2].numRows); + + setOrthoData(ang0X.w, ang0Y.w, ang0Z.w, ang1X.w, ang1Y.w, ang1Z.w, recipResponse[3], cError.w, angOrthoAxes0X[orthoCount3].w, + angOrthoAxes0Y[orthoCount3].w, angOrthoAxes0Z[orthoCount3].w, angOrthoAxes1X[orthoCount3].w, angOrthoAxes1Y[orthoCount3].w, + angOrthoAxes1Z[orthoCount3].w, orthoRecipResponse[orthoCount3].w, orthoError[orthoCount3].w, constraintDescs[3].disablePreprocessing, con3->solveHint, + c->flags[3], orthoCount3, a >= constraintDescs[3].numRows); + + } + + if (con0->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[0] |= DY_SC_FLAG_OUTPUT_FORCE; + if (con1->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[1] |= DY_SC_FLAG_OUTPUT_FORCE; + if (con2->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[2] |= DY_SC_FLAG_OUTPUT_FORCE; + if (con3->flags & Px1DConstraintFlag::eOUTPUT_FORCE) + c->flags[3] |= DY_SC_FLAG_OUTPUT_FORCE; + + if ((con0->flags & Px1DConstraintFlag::eKEEPBIAS)) + c->flags[0] |= DY_SC_FLAG_KEEP_BIAS; + if ((con1->flags & Px1DConstraintFlag::eKEEPBIAS)) + c->flags[1] |= DY_SC_FLAG_KEEP_BIAS; + if ((con2->flags & Px1DConstraintFlag::eKEEPBIAS)) + c->flags[2] |= DY_SC_FLAG_KEEP_BIAS; + if ((con3->flags & Px1DConstraintFlag::eKEEPBIAS)) + c->flags[3] |= DY_SC_FLAG_KEEP_BIAS; + + //Flag inequality constraints... + if (con0->solveHint & 1) + c->flags[0] |= DY_SC_FLAG_INEQUALITY; + if (con1->solveHint & 1) + c->flags[1] |= DY_SC_FLAG_INEQUALITY; + if (con2->solveHint & 1) + c->flags[2] |= DY_SC_FLAG_INEQUALITY; + if (con3->solveHint & 1) + c->flags[3] |= DY_SC_FLAG_INEQUALITY; + + } + *(reinterpret_cast(currPtr)) = 0; + *(reinterpret_cast(currPtr + 4)) = 0; + } + + //OK, we're ready to allocate and solve prep these constraints now :-) + return SolverConstraintPrepState::eSUCCESS; +} + + + + + +void solveContact4_Block(const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const bool doFriction, const PxReal minPenetration, + const PxReal elapsedTimeF32) +{ + PxTGSSolverBodyVel& b00 = *desc[0].bodyA; + PxTGSSolverBodyVel& b01 = *desc[0].bodyB; + PxTGSSolverBodyVel& b10 = *desc[1].bodyA; + PxTGSSolverBodyVel& b11 = *desc[1].bodyB; + PxTGSSolverBodyVel& b20 = *desc[2].bodyA; + PxTGSSolverBodyVel& b21 = *desc[2].bodyB; + PxTGSSolverBodyVel& b30 = *desc[3].bodyA; + PxTGSSolverBodyVel& b31 = *desc[3].bodyB; + + const Vec4V minPen = V4Load(minPenetration); + + const Vec4V elapsedTime = V4Load(elapsedTimeF32); + + //We'll need this. + const Vec4V vZero = V4Zero(); + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&b01.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularVelocity.x); + Vec4V angState01 = V4LoadA(&b01.angularVelocity.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&b11.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularVelocity.x); + Vec4V angState11 = V4LoadA(&b11.angularVelocity.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&b21.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularVelocity.x); + Vec4V angState21 = V4LoadA(&b21.angularVelocity.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&b31.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularVelocity.x); + Vec4V angState31 = V4LoadA(&b31.angularVelocity.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2; + Vec4V linVel1T0, linVel1T1, linVel1T2; + Vec4V angState0T0, angState0T1, angState0T2; + Vec4V angState1T0, angState1T1, angState1T2; + + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2); + PX_TRANSPOSE_44_34(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2); + PX_TRANSPOSE_44_34(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2); + + + Vec4V linDelta00 = V4LoadA(&b00.deltaLinDt.x); + Vec4V linDelta01 = V4LoadA(&b01.deltaLinDt.x); + Vec4V angDelta00 = V4LoadA(&b00.deltaAngDt.x); + Vec4V angDelta01 = V4LoadA(&b01.deltaAngDt.x); + + Vec4V linDelta10 = V4LoadA(&b10.deltaLinDt.x); + Vec4V linDelta11 = V4LoadA(&b11.deltaLinDt.x); + Vec4V angDelta10 = V4LoadA(&b10.deltaAngDt.x); + Vec4V angDelta11 = V4LoadA(&b11.deltaAngDt.x); + + Vec4V linDelta20 = V4LoadA(&b20.deltaLinDt.x); + Vec4V linDelta21 = V4LoadA(&b21.deltaLinDt.x); + Vec4V angDelta20 = V4LoadA(&b20.deltaAngDt.x); + Vec4V angDelta21 = V4LoadA(&b21.deltaAngDt.x); + + Vec4V linDelta30 = V4LoadA(&b30.deltaLinDt.x); + Vec4V linDelta31 = V4LoadA(&b31.deltaLinDt.x); + Vec4V angDelta30 = V4LoadA(&b30.deltaAngDt.x); + Vec4V angDelta31 = V4LoadA(&b31.deltaAngDt.x); + + Vec4V linDelta0T0, linDelta0T1, linDelta0T2; + Vec4V linDelta1T0, linDelta1T1, linDelta1T2; + Vec4V angDelta0T0, angDelta0T1, angDelta0T2; + Vec4V angDelta1T0, angDelta1T1, angDelta1T2; + + + PX_TRANSPOSE_44_34(linDelta00, linDelta10, linDelta20, linDelta30, linDelta0T0, linDelta0T1, linDelta0T2); + PX_TRANSPOSE_44_34(linDelta01, linDelta11, linDelta21, linDelta31, linDelta1T0, linDelta1T1, linDelta1T2); + PX_TRANSPOSE_44_34(angDelta00, angDelta10, angDelta20, angDelta30, angDelta0T0, angDelta0T1, angDelta0T2); + PX_TRANSPOSE_44_34(angDelta01, angDelta11, angDelta21, angDelta31, angDelta1T0, angDelta1T1, angDelta1T2); + + + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + Vec4V vMax = V4Splat(FMax()); + + const PxU8* PX_RESTRICT prefetchAddress = currPtr + sizeof(SolverContactHeaderStepBlock) + sizeof(SolverContactPointStepBlock); + + SolverContactHeaderStepBlock* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + const Vec4V invMassA = hdr->invMass0D0; + const Vec4V invMassB = hdr->invMass1D1; + + const Vec4V sumInvMass = V4Add(invMassA, invMassB); + + Vec4V linDeltaX = V4Sub(linDelta0T0, linDelta1T0); + Vec4V linDeltaY = V4Sub(linDelta0T1, linDelta1T1); + Vec4V linDeltaZ = V4Sub(linDelta0T2, linDelta1T2); + + + while (currPtr < last) + { + + hdr = reinterpret_cast(currPtr); + + PX_ASSERT(hdr->type == DY_SC_TYPE_BLOCK_RB_CONTACT); + + currPtr = reinterpret_cast(const_cast(hdr) + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + bool hasMaxImpulse = (hdr->flag & SolverContactHeaderStepBlock::eHAS_MAX_IMPULSE) != 0; + + Vec4V* appliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numNormalConstr; + + SolverContactPointStepBlock* PX_RESTRICT contacts = reinterpret_cast(currPtr); + + Vec4V* maxImpulses; + currPtr = reinterpret_cast(contacts + numNormalConstr); + PxU32 maxImpulseMask = 0; + if (hasMaxImpulse) + { + maxImpulseMask = 0xFFFFFFFF; + maxImpulses = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V) * numNormalConstr; + } + else + { + maxImpulses = &vMax; + } + + + /*SolverFrictionSharedData4* PX_RESTRICT fd = reinterpret_cast(currPtr); + if (numFrictionConstr) + currPtr += sizeof(SolverFrictionSharedData4);*/ + + Vec4V* frictionAppliedForce = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numFrictionConstr; + + const SolverContactFrictionStepBlock* PX_RESTRICT frictions = reinterpret_cast(currPtr); + currPtr += numFrictionConstr * sizeof(SolverContactFrictionStepBlock); + + Vec4V accumulatedNormalImpulse = vZero; + + const Vec4V angD0 = hdr->angDom0; + const Vec4V angD1 = hdr->angDom1; + + const Vec4V _normalT0 = hdr->normalX; + const Vec4V _normalT1 = hdr->normalY; + const Vec4V _normalT2 = hdr->normalZ; + + Vec4V contactNormalVel1 = V4Mul(linVel0T0, _normalT0); + Vec4V contactNormalVel3 = V4Mul(linVel1T0, _normalT0); + contactNormalVel1 = V4MulAdd(linVel0T1, _normalT1, contactNormalVel1); + contactNormalVel3 = V4MulAdd(linVel1T1, _normalT1, contactNormalVel3); + contactNormalVel1 = V4MulAdd(linVel0T2, _normalT2, contactNormalVel1); + contactNormalVel3 = V4MulAdd(linVel1T2, _normalT2, contactNormalVel3); + + const Vec4V maxPenBias = hdr->maxPenBias; + + Vec4V relVel1 = V4Sub(contactNormalVel1, contactNormalVel3); + + Vec4V deltaNormalV = V4Mul(linDeltaX, _normalT0); + deltaNormalV = V4MulAdd(linDeltaY, _normalT1, deltaNormalV); + deltaNormalV = V4MulAdd(linDeltaZ, _normalT2, deltaNormalV); + + Vec4V accumDeltaF = vZero; + + for (PxU32 i = 0; istaticFriction; + const Vec4V dynamicFric = hdr->dynamicFriction; + + const Vec4V maxFrictionImpulse = V4Mul(staticFric, accumulatedNormalImpulse); + const Vec4V maxDynFrictionImpulse = V4Mul(dynamicFric, accumulatedNormalImpulse); + const Vec4V negMaxDynFrictionImpulse = V4Neg(maxDynFrictionImpulse); + //const Vec4V negMaxFrictionImpulse = V4Neg(maxFrictionImpulse); + BoolV broken = BFFFF(); + + + for (PxU32 i = 0; ibroken = broken; + } + } + + PX_TRANSPOSE_34_44(linVel0T0, linVel0T1, linVel0T2, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_34_44(linVel1T0, linVel1T1, linVel1T2, linVel01, linVel11, linVel21, linVel31); + PX_TRANSPOSE_34_44(angState0T0, angState0T1, angState0T2, angState00, angState10, angState20, angState30); + PX_TRANSPOSE_34_44(angState1T0, angState1T1, angState1T2, angState01, angState11, angState21, angState31); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularVelocity.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularVelocity.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularVelocity.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularVelocity.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularVelocity.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularVelocity.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularVelocity.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularVelocity.isFinite()); + + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(angState00, &b00.angularVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(angState10, &b10.angularVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(angState20, &b20.angularVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + V4StoreA(angState30, &b30.angularVelocity.x); + + if (desc[0].bodyBIdx != 0) + { + V4StoreA(linVel01, &b01.linearVelocity.x); + V4StoreA(angState01, &b01.angularVelocity.x); + } + if (desc[1].bodyBIdx != 0) + { + V4StoreA(linVel11, &b11.linearVelocity.x); + V4StoreA(angState11, &b11.angularVelocity.x); + } + if (desc[2].bodyBIdx != 0) + { + V4StoreA(linVel21, &b21.linearVelocity.x); + V4StoreA(angState21, &b21.angularVelocity.x); + } + if (desc[3].bodyBIdx != 0) + { + V4StoreA(linVel31, &b31.linearVelocity.x); + V4StoreA(angState31, &b31.angularVelocity.x); + } + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularVelocity.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularVelocity.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularVelocity.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularVelocity.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularVelocity.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularVelocity.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularVelocity.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularVelocity.isFinite()); +} + + +void writeBackContact4_Block(const PxTGSSolverConstraintDesc* PX_RESTRICT desc, SolverContext* cache) +{ + PX_UNUSED(cache); + const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + //hopefully pointer aliasing doesn't bite. + PxU8* PX_RESTRICT currPtr = desc[0].constraint; + PxReal* PX_RESTRICT vForceWriteback0 = reinterpret_cast(desc[0].writeBack); + PxReal* PX_RESTRICT vForceWriteback1 = reinterpret_cast(desc[1].writeBack); + PxReal* PX_RESTRICT vForceWriteback2 = reinterpret_cast(desc[2].writeBack); + PxReal* PX_RESTRICT vForceWriteback3 = reinterpret_cast(desc[3].writeBack); + + //const PxU8 type = *desc[0].constraint; + const PxU32 contactSize = sizeof(SolverContactPointStepBlock); + const PxU32 frictionSize = sizeof(SolverContactFrictionStepBlock); + + + Vec4V normalForce = V4Zero(); + + + //We'll need this. + //const Vec4V vZero = V4Zero(); + + bool writeBackThresholds[4] = { false, false, false, false }; + + while ((currPtr < last)) + { + SolverContactHeaderStepBlock* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + currPtr = reinterpret_cast(hdr + 1); + + const PxU32 numNormalConstr = hdr->numNormalConstr; + const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + Vec4V* PX_RESTRICT appliedForces = reinterpret_cast(currPtr); + currPtr += sizeof(Vec4V)*numNormalConstr; + + //SolverContactBatchPointBase4* PX_RESTRICT contacts = (SolverContactBatchPointBase4*)currPtr; + currPtr += (numNormalConstr * contactSize); + + bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + if (hasMaxImpulse) + currPtr += sizeof(Vec4V) * numNormalConstr; + + currPtr += sizeof(Vec4V)*numFrictionConstr; + + //SolverContactFrictionBase4* PX_RESTRICT frictions = (SolverContactFrictionBase4*)currPtr; + currPtr += (numFrictionConstr * frictionSize); + + writeBackThresholds[0] = hdr->flags[0] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[1] = hdr->flags[1] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[2] = hdr->flags[2] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + writeBackThresholds[3] = hdr->flags[3] & SolverContactHeader::eHAS_FORCE_THRESHOLDS; + + + for (PxU32 i = 0; inumNormalConstrs[0]) + FStore(appliedForce0, vForceWriteback0++); + if (vForceWriteback1 && i < hdr->numNormalConstrs[1]) + FStore(appliedForce1, vForceWriteback1++); + if (vForceWriteback2 && i < hdr->numNormalConstrs[2]) + FStore(appliedForce2, vForceWriteback2++); + if (vForceWriteback3 && i < hdr->numNormalConstrs[3]) + FStore(appliedForce3, vForceWriteback3++); + } + + if (numFrictionConstr) + { + PX_ALIGN(16, PxU32 broken[4]); + BStoreA(hdr->broken, broken); + + PxU8* frictionCounts = hdr->numNormalConstrs; + + for (PxU32 a = 0; a < 4; ++a) + { + if (frictionCounts[a] && broken[a]) + *hdr->frictionBrokenWritebackByte[a] = 1; // PT: bad L2 miss here + } + } + } + + PX_UNUSED(writeBackThresholds); + +#if 0 + if (cache) + { + + PX_ALIGN(16, PxReal nf[4]); + V4StoreA(normalForce, nf); + + Sc::ShapeInteraction** shapeInteractions = reinterpret_cast(desc[0].constraint)->shapeInteraction; + + for (PxU32 a = 0; a < 4; ++a) + { + if (writeBackThresholds[a] && desc[a].linkIndexA == PxSolverConstraintDesc::NO_LINK && desc[a].linkIndexB == PxSolverConstraintDesc::NO_LINK && + nf[a] != 0.f && (bd0[a]->reportThreshold < PX_MAX_REAL || bd1[a]->reportThreshold < PX_MAX_REAL)) + { + ThresholdStreamElement elt; + elt.normalForce = nf[a]; + elt.threshold = PxMin(bd0[a]->reportThreshold, bd1[a]->reportThreshold); + elt.nodeIndexA = bd0[a]->nodeIndex; + elt.nodeIndexB = bd1[a]->nodeIndex; + elt.shapeInteraction = shapeInteractions[a]; + Ps::order(elt.nodeIndexA, elt.nodeIndexB); + PX_ASSERT(elt.nodeIndexA < elt.nodeIndexB); + PX_ASSERT(cache.mThresholdStreamIndex < cache.mThresholdStreamLength); + cache.mThresholdStream[cache.mThresholdStreamIndex++] = elt; + } + } + } +#endif +} + +void solveContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime) +{ + solveContact4_Block(desc + hdr.mStartIndex, doFriction, minPenetration, elapsedTime); +} + +void writeBackContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, SolverContext* cache) +{ + writeBackContact4_Block(desc + hdr.mStartIndex, cache); +} + +PX_FORCE_INLINE Vec4V V4Dot3(const Vec4V& x0, const Vec4V& y0, const Vec4V& z0, const Vec4V& x1, const Vec4V& y1, const Vec4V& z1) +{ + return V4MulAdd(x0, x1, V4MulAdd(y0, y1, V4Mul(z0, z1))); +} + + +void solve1DStep4(const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxTGSSolverBodyTxInertia* const txInertias, + const PxReal elapsedTimeF32) +{ + PxU8* PX_RESTRICT bPtr = desc->constraint; + if (bPtr == NULL) + return; + + const FloatV elapsedTime = FLoad(elapsedTimeF32); + + PxTGSSolverBodyVel& b00 = *desc[0].bodyA; + PxTGSSolverBodyVel& b01 = *desc[0].bodyB; + PxTGSSolverBodyVel& b10 = *desc[1].bodyA; + PxTGSSolverBodyVel& b11 = *desc[1].bodyB; + PxTGSSolverBodyVel& b20 = *desc[2].bodyA; + PxTGSSolverBodyVel& b21 = *desc[2].bodyB; + PxTGSSolverBodyVel& b30 = *desc[3].bodyA; + PxTGSSolverBodyVel& b31 = *desc[3].bodyB; + + const PxTGSSolverBodyTxInertia& txI00 = txInertias[desc[0].bodyAIdx]; + const PxTGSSolverBodyTxInertia& txI01 = txInertias[desc[0].bodyBIdx]; + const PxTGSSolverBodyTxInertia& txI10 = txInertias[desc[1].bodyAIdx]; + const PxTGSSolverBodyTxInertia& txI11 = txInertias[desc[1].bodyBIdx]; + const PxTGSSolverBodyTxInertia& txI20 = txInertias[desc[2].bodyAIdx]; + const PxTGSSolverBodyTxInertia& txI21 = txInertias[desc[2].bodyBIdx]; + const PxTGSSolverBodyTxInertia& txI30 = txInertias[desc[3].bodyAIdx]; + const PxTGSSolverBodyTxInertia& txI31 = txInertias[desc[3].bodyBIdx]; + + Vec4V linVel00 = V4LoadA(&b00.linearVelocity.x); + Vec4V linVel01 = V4LoadA(&b01.linearVelocity.x); + Vec4V angState00 = V4LoadA(&b00.angularVelocity.x); + Vec4V angState01 = V4LoadA(&b01.angularVelocity.x); + + Vec4V linVel10 = V4LoadA(&b10.linearVelocity.x); + Vec4V linVel11 = V4LoadA(&b11.linearVelocity.x); + Vec4V angState10 = V4LoadA(&b10.angularVelocity.x); + Vec4V angState11 = V4LoadA(&b11.angularVelocity.x); + + Vec4V linVel20 = V4LoadA(&b20.linearVelocity.x); + Vec4V linVel21 = V4LoadA(&b21.linearVelocity.x); + Vec4V angState20 = V4LoadA(&b20.angularVelocity.x); + Vec4V angState21 = V4LoadA(&b21.angularVelocity.x); + + Vec4V linVel30 = V4LoadA(&b30.linearVelocity.x); + Vec4V linVel31 = V4LoadA(&b31.linearVelocity.x); + Vec4V angState30 = V4LoadA(&b30.angularVelocity.x); + Vec4V angState31 = V4LoadA(&b31.angularVelocity.x); + + + Vec4V linVel0T0, linVel0T1, linVel0T2; + Vec4V linVel1T0, linVel1T1, linVel1T2; + Vec4V angState0T0, angState0T1, angState0T2; + Vec4V angState1T0, angState1T1, angState1T2; + + + PX_TRANSPOSE_44_34(linVel00, linVel10, linVel20, linVel30, linVel0T0, linVel0T1, linVel0T2); + PX_TRANSPOSE_44_34(linVel01, linVel11, linVel21, linVel31, linVel1T0, linVel1T1, linVel1T2); + PX_TRANSPOSE_44_34(angState00, angState10, angState20, angState30, angState0T0, angState0T1, angState0T2); + PX_TRANSPOSE_44_34(angState01, angState11, angState21, angState31, angState1T0, angState1T1, angState1T2); + + + Vec4V linDelta00 = V4LoadA(&b00.deltaLinDt.x); + Vec4V linDelta01 = V4LoadA(&b01.deltaLinDt.x); + Vec4V angDelta00 = V4LoadA(&b00.deltaAngDt.x); + Vec4V angDelta01 = V4LoadA(&b01.deltaAngDt.x); + + Vec4V linDelta10 = V4LoadA(&b10.deltaLinDt.x); + Vec4V linDelta11 = V4LoadA(&b11.deltaLinDt.x); + Vec4V angDelta10 = V4LoadA(&b10.deltaAngDt.x); + Vec4V angDelta11 = V4LoadA(&b11.deltaAngDt.x); + + Vec4V linDelta20 = V4LoadA(&b20.deltaLinDt.x); + Vec4V linDelta21 = V4LoadA(&b21.deltaLinDt.x); + Vec4V angDelta20 = V4LoadA(&b20.deltaAngDt.x); + Vec4V angDelta21 = V4LoadA(&b21.deltaAngDt.x); + + Vec4V linDelta30 = V4LoadA(&b30.deltaLinDt.x); + Vec4V linDelta31 = V4LoadA(&b31.deltaLinDt.x); + Vec4V angDelta30 = V4LoadA(&b30.deltaAngDt.x); + Vec4V angDelta31 = V4LoadA(&b31.deltaAngDt.x); + + Vec4V linDelta0T0, linDelta0T1, linDelta0T2; + Vec4V linDelta1T0, linDelta1T1, linDelta1T2; + Vec4V angDelta0T0, angDelta0T1, angDelta0T2; + Vec4V angDelta1T0, angDelta1T1, angDelta1T2; + + + PX_TRANSPOSE_44_34(linDelta00, linDelta10, linDelta20, linDelta30, linDelta0T0, linDelta0T1, linDelta0T2); + PX_TRANSPOSE_44_34(linDelta01, linDelta11, linDelta21, linDelta31, linDelta1T0, linDelta1T1, linDelta1T2); + PX_TRANSPOSE_44_34(angDelta00, angDelta10, angDelta20, angDelta30, angDelta0T0, angDelta0T1, angDelta0T2); + PX_TRANSPOSE_44_34(angDelta01, angDelta11, angDelta21, angDelta31, angDelta1T0, angDelta1T1, angDelta1T2); + + + + + const SolverConstraint1DHeaderStep4* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DStep4* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeaderStep4)); + + + Vec4V invInertia00X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI00.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia00Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI00.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia00Z = Vec4V_From_Vec3V(V3LoadU(txI00.sqrtInvInertia.column2)); + + Vec4V invInertia10X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI10.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia10Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI10.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia10Z = Vec4V_From_Vec3V(V3LoadU(txI10.sqrtInvInertia.column2)); + + Vec4V invInertia20X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI20.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia20Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI20.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia20Z = Vec4V_From_Vec3V(V3LoadU(txI20.sqrtInvInertia.column2)); + + Vec4V invInertia30X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI30.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia30Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI30.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia30Z = Vec4V_From_Vec3V(V3LoadU(txI30.sqrtInvInertia.column2)); + + Vec4V invInertia01X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI01.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia01Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI01.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia01Z = Vec4V_From_Vec3V(V3LoadU(txI01.sqrtInvInertia.column2)); + + Vec4V invInertia11X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI11.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia11Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI11.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia11Z = Vec4V_From_Vec3V(V3LoadU(txI11.sqrtInvInertia.column2)); + + Vec4V invInertia21X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI21.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia21Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI21.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia21Z = Vec4V_From_Vec3V(V3LoadU(txI21.sqrtInvInertia.column2)); + + Vec4V invInertia31X = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI31.sqrtInvInertia.column0)); // PT: safe because 'column1' follows 'column0' in PxMat33 + Vec4V invInertia31Y = Vec4V_From_Vec3V(V3LoadU_SafeReadW(txI31.sqrtInvInertia.column1)); // PT: safe because 'column2' follows 'column1' in PxMat33 + Vec4V invInertia31Z = Vec4V_From_Vec3V(V3LoadU(txI31.sqrtInvInertia.column2)); + + Vec4V invInertia0X0, invInertia0X1, invInertia0X2; + Vec4V invInertia0Y0, invInertia0Y1, invInertia0Y2; + Vec4V invInertia0Z0, invInertia0Z1, invInertia0Z2; + + Vec4V invInertia1X0, invInertia1X1, invInertia1X2; + Vec4V invInertia1Y0, invInertia1Y1, invInertia1Y2; + Vec4V invInertia1Z0, invInertia1Z1, invInertia1Z2; + + PX_TRANSPOSE_44_34(invInertia00X, invInertia10X, invInertia20X, invInertia30X, invInertia0X0, invInertia0Y0, invInertia0Z0); + PX_TRANSPOSE_44_34(invInertia00Y, invInertia10Y, invInertia20Y, invInertia30Y, invInertia0X1, invInertia0Y1, invInertia0Z1); + PX_TRANSPOSE_44_34(invInertia00Z, invInertia10Z, invInertia20Z, invInertia30Z, invInertia0X2, invInertia0Y2, invInertia0Z2); + + PX_TRANSPOSE_44_34(invInertia01X, invInertia11X, invInertia21X, invInertia31X, invInertia1X0, invInertia1Y0, invInertia1Z0); + PX_TRANSPOSE_44_34(invInertia01Y, invInertia11Y, invInertia21Y, invInertia31Y, invInertia1X1, invInertia1Y1, invInertia1Z1); + PX_TRANSPOSE_44_34(invInertia01Z, invInertia11Z, invInertia21Z, invInertia31Z, invInertia1X2, invInertia1Y2, invInertia1Z2); + + const Vec4V invInertiaScale0 = header->angD0; + const Vec4V invInertiaScale1 = header->angD1; + + //KS - todo - load this a bit quicker... + Vec4V rot00 = V4LoadA(&txI00.deltaBody2World.q.x); + Vec4V rot01 = V4LoadA(&txI01.deltaBody2World.q.x); + Vec4V rot10 = V4LoadA(&txI10.deltaBody2World.q.x); + Vec4V rot11 = V4LoadA(&txI11.deltaBody2World.q.x); + Vec4V rot20 = V4LoadA(&txI20.deltaBody2World.q.x); + Vec4V rot21 = V4LoadA(&txI21.deltaBody2World.q.x); + Vec4V rot30 = V4LoadA(&txI30.deltaBody2World.q.x); + Vec4V rot31 = V4LoadA(&txI31.deltaBody2World.q.x); + + Vec4V rot0X, rot0Y, rot0Z, rot0W; + Vec4V rot1X, rot1Y, rot1Z, rot1W; + + PX_TRANSPOSE_44(rot00, rot10, rot20, rot30, rot0X, rot0Y, rot0Z, rot0W); + PX_TRANSPOSE_44(rot01, rot11, rot21, rot31, rot1X, rot1Y, rot1Z, rot1W); + + Vec4V raX, raY, raZ; + Vec4V rbX, rbY, rbZ; + + QuatRotate4(rot0X, rot0Y, rot0Z, rot0W, header->rAWorld[0], header->rAWorld[1], header->rAWorld[2], raX, raY, raZ); + QuatRotate4(rot1X, rot1Y, rot1Z, rot1W, header->rBWorld[0], header->rBWorld[1], header->rBWorld[2], rbX, rbY, rbZ); + + + const Vec4V raMotionX = V4Sub(V4Add(raX, linDelta0T0), header->rAWorld[0]); + const Vec4V raMotionY = V4Sub(V4Add(raY, linDelta0T1), header->rAWorld[1]); + const Vec4V raMotionZ = V4Sub(V4Add(raZ, linDelta0T2), header->rAWorld[2]); + const Vec4V rbMotionX = V4Sub(V4Add(rbX, linDelta1T0), header->rBWorld[0]); + const Vec4V rbMotionY = V4Sub(V4Add(rbY, linDelta1T1), header->rBWorld[1]); + const Vec4V rbMotionZ = V4Sub(V4Add(rbZ, linDelta1T2), header->rBWorld[2]); + + const Vec4V mass0 = header->invMass0D0; + const Vec4V mass1 = header->invMass1D1; + + const VecI32V orthoMask = I4Load(DY_SC_FLAG_ORTHO_TARGET); + const VecI32V limitMask = I4Load(DY_SC_FLAG_INEQUALITY); + const Vec4V zero = V4Zero(); + const Vec4V one = V4One(); + + Vec4V error0 = V4Add(header->angOrthoError[0], + V4Sub(V4Dot3(header->angOrthoAxis0X[0], header->angOrthoAxis0Y[0], header->angOrthoAxis0Z[0], angDelta0T0, angDelta0T1, angDelta0T2), + V4Dot3(header->angOrthoAxis1X[0], header->angOrthoAxis1Y[0], header->angOrthoAxis1Z[0], angDelta1T0, angDelta1T1, angDelta1T2))); + + Vec4V error1 = V4Add(header->angOrthoError[1], + V4Sub(V4Dot3(header->angOrthoAxis0X[1], header->angOrthoAxis0Y[1], header->angOrthoAxis0Z[1], angDelta0T0, angDelta0T1, angDelta0T2), + V4Dot3(header->angOrthoAxis1X[1], header->angOrthoAxis1Y[1], header->angOrthoAxis1Z[1], angDelta1T0, angDelta1T1, angDelta1T2))); + + Vec4V error2 = V4Add(header->angOrthoError[2], + V4Sub(V4Dot3(header->angOrthoAxis0X[2], header->angOrthoAxis0Y[2], header->angOrthoAxis0Z[2], angDelta0T0, angDelta0T1, angDelta0T2), + V4Dot3(header->angOrthoAxis1X[2], header->angOrthoAxis1Y[2], header->angOrthoAxis1Z[2], angDelta1T0, angDelta1T1, angDelta1T2))); + + for (PxU32 i = 0; icount; ++i, base++) + { + Ps::prefetchLine(base + 1); + SolverConstraint1DStep4& c = *base; + + const Vec4V cangVel0X = V4Add(c.ang0[0], V4NegMulSub(raZ, c.lin0[1], V4Mul(raY, c.lin0[2]))); + const Vec4V cangVel0Y = V4Add(c.ang0[1], V4NegMulSub(raX, c.lin0[2], V4Mul(raZ, c.lin0[0]))); + const Vec4V cangVel0Z = V4Add(c.ang0[2], V4NegMulSub(raY, c.lin0[0], V4Mul(raX, c.lin0[1]))); + + const Vec4V cangVel1X = V4Add(c.ang1[0], V4NegMulSub(rbZ, c.lin1[1], V4Mul(rbY, c.lin1[2]))); + const Vec4V cangVel1Y = V4Add(c.ang1[1], V4NegMulSub(rbX, c.lin1[2], V4Mul(rbZ, c.lin1[0]))); + const Vec4V cangVel1Z = V4Add(c.ang1[2], V4NegMulSub(rbY, c.lin1[0], V4Mul(rbX, c.lin1[1]))); + + const VecI32V flags = I4LoadA(reinterpret_cast(c.flags)); + + const BoolV useOrtho = VecI32V_IsEq(VecI32V_And(flags, orthoMask), orthoMask); + + + const Vec4V angOrthoCoefficient = V4Sel(useOrtho, one, zero); + + + Vec4V delAngVel0X = V4Mul(invInertia0X0, cangVel0X); + Vec4V delAngVel0Y = V4Mul(invInertia0X1, cangVel0X); + Vec4V delAngVel0Z = V4Mul(invInertia0X2, cangVel0X); + + delAngVel0X = V4MulAdd(invInertia0Y0, cangVel0Y, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Y1, cangVel0Y, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Y2, cangVel0Y, delAngVel0Z); + + delAngVel0X = V4MulAdd(invInertia0Z0, cangVel0Z, delAngVel0X); + delAngVel0Y = V4MulAdd(invInertia0Z1, cangVel0Z, delAngVel0Y); + delAngVel0Z = V4MulAdd(invInertia0Z2, cangVel0Z, delAngVel0Z); + + Vec4V delAngVel1X = V4Mul(invInertia1X0, cangVel1X); + Vec4V delAngVel1Y = V4Mul(invInertia1X1, cangVel1X); + Vec4V delAngVel1Z = V4Mul(invInertia1X2, cangVel1X); + + delAngVel1X = V4MulAdd(invInertia1Y0, cangVel1Y, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Y1, cangVel1Y, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Y2, cangVel1Y, delAngVel1Z); + + delAngVel1X = V4MulAdd(invInertia1Z0, cangVel1Z, delAngVel1X); + delAngVel1Y = V4MulAdd(invInertia1Z1, cangVel1Z, delAngVel1Y); + delAngVel1Z = V4MulAdd(invInertia1Z2, cangVel1Z, delAngVel1Z); + + Vec4V err = c.error; + { + const Vec4V proj0 = V4Mul(V4MulAdd(header->angOrthoAxis0X[0], delAngVel0X, V4MulAdd(header->angOrthoAxis0Y[0], delAngVel0Y, + V4MulAdd(header->angOrthoAxis0Z[0], delAngVel0Z, V4MulAdd(header->angOrthoAxis1X[0], delAngVel1X, + V4MulAdd(header->angOrthoAxis1Y[0], delAngVel1Y, V4Mul(header->angOrthoAxis1Z[0], delAngVel1Z)))))), header->angOrthoRecipResponse[0]); + + const Vec4V proj1 = V4Mul(V4MulAdd(header->angOrthoAxis0X[1], delAngVel0X, V4MulAdd(header->angOrthoAxis0Y[1], delAngVel0Y, + V4MulAdd(header->angOrthoAxis0Z[1], delAngVel0Z, V4MulAdd(header->angOrthoAxis1X[1], delAngVel1X, + V4MulAdd(header->angOrthoAxis1Y[1], delAngVel1Y, V4Mul(header->angOrthoAxis1Z[1], delAngVel1Z)))))), header->angOrthoRecipResponse[1]); + + const Vec4V proj2 = V4Mul(V4MulAdd(header->angOrthoAxis0X[2], delAngVel0X, V4MulAdd(header->angOrthoAxis0Y[2], delAngVel0Y, + V4MulAdd(header->angOrthoAxis0Z[2], delAngVel0Z, V4MulAdd(header->angOrthoAxis1X[2], delAngVel1X, + V4MulAdd(header->angOrthoAxis1Y[2], delAngVel1Y, V4Mul(header->angOrthoAxis1Z[2], delAngVel1Z)))))), header->angOrthoRecipResponse[2]); + + const Vec4V delta0X = V4MulAdd(header->angOrthoAxis0X[0], proj0, V4MulAdd(header->angOrthoAxis0X[1], proj1, V4Mul(header->angOrthoAxis0X[2], proj2))); + const Vec4V delta0Y = V4MulAdd(header->angOrthoAxis0Y[0], proj0, V4MulAdd(header->angOrthoAxis0Y[1], proj1, V4Mul(header->angOrthoAxis0Y[2], proj2))); + const Vec4V delta0Z = V4MulAdd(header->angOrthoAxis0Z[0], proj0, V4MulAdd(header->angOrthoAxis0Z[1], proj1, V4Mul(header->angOrthoAxis0Z[2], proj2))); + + const Vec4V delta1X = V4MulAdd(header->angOrthoAxis1X[0], proj0, V4MulAdd(header->angOrthoAxis1X[1], proj1, V4Mul(header->angOrthoAxis1X[2], proj2))); + const Vec4V delta1Y = V4MulAdd(header->angOrthoAxis1Y[0], proj0, V4MulAdd(header->angOrthoAxis1Y[1], proj1, V4Mul(header->angOrthoAxis1Y[2], proj2))); + const Vec4V delta1Z = V4MulAdd(header->angOrthoAxis1Z[0], proj0, V4MulAdd(header->angOrthoAxis1Z[1], proj1, V4Mul(header->angOrthoAxis1Z[2], proj2))); + + + delAngVel0X = V4NegMulSub(delta0X, angOrthoCoefficient, delAngVel0X); + delAngVel0Y = V4NegMulSub(delta0Y, angOrthoCoefficient, delAngVel0Y); + delAngVel0Z = V4NegMulSub(delta0Z, angOrthoCoefficient, delAngVel0Z); + + delAngVel1X = V4NegMulSub(delta1X, angOrthoCoefficient, delAngVel1X); + delAngVel1Y = V4NegMulSub(delta1Y, angOrthoCoefficient, delAngVel1Y); + delAngVel1Z = V4NegMulSub(delta1Z, angOrthoCoefficient, delAngVel1Z); + + err = V4Sub(err, V4Mul(V4MulAdd(error0, proj0, V4MulAdd(error1, proj1, V4Mul(error2, proj2))), angOrthoCoefficient)); + } + + Vec4V ang0IX = V4Mul(invInertia0X0, delAngVel0X); + Vec4V ang0IY = V4Mul(invInertia0X1, delAngVel0X); + Vec4V ang0IZ = V4Mul(invInertia0X2, delAngVel0X); + + ang0IX = V4MulAdd(invInertia0Y0, delAngVel0Y, ang0IX); + ang0IY = V4MulAdd(invInertia0Y1, delAngVel0Y, ang0IY); + ang0IZ = V4MulAdd(invInertia0Y2, delAngVel0Y, ang0IZ); + + ang0IX = V4MulAdd(invInertia0Z0, delAngVel0Z, ang0IX); + ang0IY = V4MulAdd(invInertia0Z1, delAngVel0Z, ang0IY); + ang0IZ = V4MulAdd(invInertia0Z2, delAngVel0Z, ang0IZ); + + Vec4V ang1IX = V4Mul(invInertia1X0, delAngVel1X); + Vec4V ang1IY = V4Mul(invInertia1X1, delAngVel1X); + Vec4V ang1IZ = V4Mul(invInertia1X2, delAngVel1X); + + ang1IX = V4MulAdd(invInertia1Y0, delAngVel1Y, ang1IX); + ang1IY = V4MulAdd(invInertia1Y1, delAngVel1Y, ang1IY); + ang1IZ = V4MulAdd(invInertia1Y2, delAngVel1Y, ang1IZ); + + ang1IX = V4MulAdd(invInertia1Z0, delAngVel1Z, ang1IX); + ang1IY = V4MulAdd(invInertia1Z1, delAngVel1Z, ang1IY); + ang1IZ = V4MulAdd(invInertia1Z2, delAngVel1Z, ang1IZ); + + + const Vec4V clinVel0X = c.lin0[0]; + const Vec4V clinVel0Y = c.lin0[1]; + const Vec4V clinVel0Z = c.lin0[2]; + + const Vec4V clinVel1X = c.lin1[0]; + const Vec4V clinVel1Y = c.lin1[1]; + const Vec4V clinVel1Z = c.lin1[2]; + + + const Vec4V clinVel0X_ = c.lin0[0]; + const Vec4V clinVel0Y_ = c.lin0[1]; + const Vec4V clinVel0Z_ = c.lin0[2]; + + const Vec4V clinVel1X_ = c.lin1[0]; + const Vec4V clinVel1Y_ = c.lin1[1]; + const Vec4V clinVel1Z_ = c.lin1[2]; + + + //KS - compute raXnI and effective mass. Unfortunately, the joints are noticeably less stable if we don't do this each + //iteration. It's skippable. If we do that, there's no need for the invInertiaTensors + + + + const Vec4V dotDelAngVel0 = V4MulAdd(delAngVel0X, delAngVel0X, V4MulAdd(delAngVel0Y, delAngVel0Y, V4Mul(delAngVel0Z, delAngVel0Z))); + const Vec4V dotDelAngVel1 = V4MulAdd(delAngVel1X, delAngVel1X, V4MulAdd(delAngVel1Y, delAngVel1Y, V4Mul(delAngVel1Z, delAngVel1Z))); + + const Vec4V dotRbXnAngDelta0 = V4MulAdd(delAngVel0X, angDelta0T0, V4MulAdd(delAngVel0Y, angDelta0T1, V4Mul(delAngVel0Z, angDelta0T2))); + const Vec4V dotRbXnAngDelta1 = V4MulAdd(delAngVel1X, angDelta1T0, V4MulAdd(delAngVel1Y, angDelta1T1, V4Mul(delAngVel1Z, angDelta1T2))); + + const Vec4V dotRaMotion = V4MulAdd(clinVel0X_, raMotionX, V4MulAdd(clinVel0Y_, raMotionY, V4Mul(clinVel0Z_, raMotionZ))); + const Vec4V dotRbMotion = V4MulAdd(clinVel1X_, rbMotionX, V4MulAdd(clinVel1Y_, rbMotionY, V4Mul(clinVel1Z_, rbMotionZ))); + + const Vec4V deltaAng = V4Mul(c.angularErrorScale, V4Sub(dotRbXnAngDelta0, dotRbXnAngDelta1)); + const Vec4V error = V4NegScaleSub(c.velTarget, elapsedTime, V4Add(V4Add(err, V4Sub(dotRaMotion, dotRbMotion)), deltaAng)); + + const Vec4V dotClinVel0 = V4MulAdd(clinVel0X, clinVel0X, V4MulAdd(clinVel0Y, clinVel0Y, V4Mul(clinVel0Z, clinVel0Z))); + const Vec4V dotClinVel1 = V4MulAdd(clinVel1X, clinVel1X, V4MulAdd(clinVel1Y, clinVel1Y, V4Mul(clinVel1Z, clinVel1Z))); + + const Vec4V resp0 = V4MulAdd(mass0, dotClinVel0, V4Mul(invInertiaScale0, dotDelAngVel0)); + const Vec4V resp1 = V4MulAdd(mass1, dotClinVel1, V4Mul(invInertiaScale1, dotDelAngVel1)); + const Vec4V response = V4Add(resp0, resp1); + const Vec4V recipResponse = V4Sel(V4IsGrtr(response, V4Zero()), V4Recip(response), V4Zero()); + + + const Vec4V vMul = V4Mul(recipResponse, c.velMultiplier); + + const BoolV isLimitConstraint = VecI32V_IsEq(VecI32V_And(flags, limitMask), limitMask); + + const Vec4V minBias = V4Sel(isLimitConstraint, V4Neg(Vec4V_From_FloatV(FMax())), V4Neg(c.maxBias)); + const Vec4V unclampedBias = V4Mul(error, c.biasScale); + const Vec4V bias = V4Clamp(unclampedBias, minBias, c.maxBias); + + const Vec4V constant = V4Mul(recipResponse, V4Add(bias, c.velTarget)); + + const Vec4V normalVel0 = V4MulAdd(clinVel0X_, linVel0T0, V4MulAdd(clinVel0Y_, linVel0T1, V4Mul(clinVel0Z_, linVel0T2))); + const Vec4V normalVel1 = V4MulAdd(clinVel1X_, linVel1T0, V4MulAdd(clinVel1Y_, linVel1T1, V4Mul(clinVel1Z_, linVel1T2))); + + const Vec4V angVel0 = V4MulAdd(delAngVel0X, angState0T0, V4MulAdd(delAngVel0Y, angState0T1, V4Mul(delAngVel0Z, angState0T2))); + const Vec4V angVel1 = V4MulAdd(delAngVel1X, angState1T0, V4MulAdd(delAngVel1Y, angState1T1, V4Mul(delAngVel1Z, angState1T2))); + + const Vec4V normalVel = V4Add(V4Sub(normalVel0, normalVel1), V4Sub(angVel0, angVel1)); + + + const Vec4V unclampedForce = V4MulAdd(c.impulseMultiplier, c.appliedForce, V4MulAdd(vMul, normalVel, constant)); + const Vec4V clampedForce = V4Clamp(unclampedForce, c.minImpulse, c.maxImpulse); + const Vec4V deltaF = V4Sub(clampedForce, c.appliedForce); + + c.appliedForce = clampedForce; + + const Vec4V deltaFIM0 = V4Mul(deltaF, mass0); + const Vec4V deltaFIM1 = V4Mul(deltaF, mass1); + const Vec4V angDetaF0 = V4Mul(deltaF, invInertiaScale0); + const Vec4V angDetaF1 = V4Mul(deltaF, invInertiaScale1); + + + linVel0T0 = V4MulAdd(clinVel0X_, deltaFIM0, linVel0T0); + linVel1T0 = V4NegMulSub(clinVel1X_, deltaFIM1, linVel1T0); + angState0T0 = V4MulAdd(delAngVel0X, angDetaF0, angState0T0); + angState1T0 = V4NegMulSub(delAngVel1X, angDetaF1, angState1T0); + + linVel0T1 = V4MulAdd(clinVel0Y_, deltaFIM0, linVel0T1); + linVel1T1 = V4NegMulSub(clinVel1Y_, deltaFIM1, linVel1T1); + angState0T1 = V4MulAdd(delAngVel0Y, angDetaF0, angState0T1); + angState1T1 = V4NegMulSub(delAngVel1Y, angDetaF1, angState1T1); + + linVel0T2 = V4MulAdd(clinVel0Z_, deltaFIM0, linVel0T2); + linVel1T2 = V4NegMulSub(clinVel1Z_, deltaFIM1, linVel1T2); + angState0T2 = V4MulAdd(delAngVel0Z, angDetaF0, angState0T2); + angState1T2 = V4NegMulSub(delAngVel1Z, angDetaF1, angState1T2); + + } + + PX_TRANSPOSE_34_44(linVel0T0, linVel0T1, linVel0T2, linVel00, linVel10, linVel20, linVel30); + PX_TRANSPOSE_34_44(linVel1T0, linVel1T1, linVel1T2, linVel01, linVel11, linVel21, linVel31); + PX_TRANSPOSE_34_44(angState0T0, angState0T1, angState0T2, angState00, angState10, angState20, angState30); + PX_TRANSPOSE_34_44(angState1T0, angState1T1, angState1T2, angState01, angState11, angState21, angState31); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularVelocity.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularVelocity.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularVelocity.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularVelocity.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularVelocity.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularVelocity.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularVelocity.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularVelocity.isFinite()); + + // Write back + V4StoreA(linVel00, &b00.linearVelocity.x); + V4StoreA(angState00, &b00.angularVelocity.x); + V4StoreA(linVel10, &b10.linearVelocity.x); + V4StoreA(angState10, &b10.angularVelocity.x); + V4StoreA(linVel20, &b20.linearVelocity.x); + V4StoreA(angState20, &b20.angularVelocity.x); + V4StoreA(linVel30, &b30.linearVelocity.x); + V4StoreA(angState30, &b30.angularVelocity.x); + + V4StoreA(linVel01, &b01.linearVelocity.x); + V4StoreA(angState01, &b01.angularVelocity.x); + V4StoreA(linVel11, &b11.linearVelocity.x); + V4StoreA(angState11, &b11.angularVelocity.x); + V4StoreA(linVel21, &b21.linearVelocity.x); + V4StoreA(angState21, &b21.angularVelocity.x); + V4StoreA(linVel31, &b31.linearVelocity.x); + V4StoreA(angState31, &b31.angularVelocity.x); + + PX_ASSERT(b00.linearVelocity.isFinite()); + PX_ASSERT(b00.angularVelocity.isFinite()); + PX_ASSERT(b10.linearVelocity.isFinite()); + PX_ASSERT(b10.angularVelocity.isFinite()); + PX_ASSERT(b20.linearVelocity.isFinite()); + PX_ASSERT(b20.angularVelocity.isFinite()); + PX_ASSERT(b30.linearVelocity.isFinite()); + PX_ASSERT(b30.angularVelocity.isFinite()); + + PX_ASSERT(b01.linearVelocity.isFinite()); + PX_ASSERT(b01.angularVelocity.isFinite()); + PX_ASSERT(b11.linearVelocity.isFinite()); + PX_ASSERT(b11.angularVelocity.isFinite()); + PX_ASSERT(b21.linearVelocity.isFinite()); + PX_ASSERT(b21.angularVelocity.isFinite()); + PX_ASSERT(b31.linearVelocity.isFinite()); + PX_ASSERT(b31.angularVelocity.isFinite()); +} + + +void solve1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxTGSSolverBodyTxInertia* const txInertias, + const PxReal elapsedTime) +{ + solve1DStep4(desc + hdr.mStartIndex, txInertias, elapsedTime); +} + +void writeBack1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc) +{ + PX_UNUSED(hdr); + ConstraintWriteback* writeback0 = reinterpret_cast(desc[hdr.mStartIndex].writeBack); + ConstraintWriteback* writeback1 = reinterpret_cast(desc[hdr.mStartIndex + 1].writeBack); + ConstraintWriteback* writeback2 = reinterpret_cast(desc[hdr.mStartIndex + 2].writeBack); + ConstraintWriteback* writeback3 = reinterpret_cast(desc[hdr.mStartIndex + 3].writeBack); + + if (writeback0 || writeback1 || writeback2 || writeback3) + { + SolverConstraint1DHeaderStep4* header = reinterpret_cast(desc[hdr.mStartIndex].constraint); + SolverConstraint1DStep4* base = reinterpret_cast(desc[hdr.mStartIndex].constraint + sizeof(SolverConstraint1DHeaderStep4)); + + const Vec4V zero = V4Zero(); + Vec4V linX(zero), linY(zero), linZ(zero); + Vec4V angX(zero), angY(zero), angZ(zero); + + for (PxU32 i = 0; icount; i++) + { + const SolverConstraint1DStep4* c = base; + + //Load in flags + const VecI32V flags = I4LoadU(reinterpret_cast(&c->flags[0])); + //Work out masks + const VecI32V mask = I4Load(DY_SC_FLAG_OUTPUT_FORCE); + + const VecI32V masked = VecI32V_And(flags, mask); + const BoolV isEq = VecI32V_IsEq(masked, mask); + + const Vec4V appliedForce = V4Sel(isEq, c->appliedForce, zero); + + linX = V4MulAdd(c->lin0[0], appliedForce, linX); + linY = V4MulAdd(c->lin0[1], appliedForce, linY); + linZ = V4MulAdd(c->lin0[2], appliedForce, linZ); + + angX = V4MulAdd(c->ang0[0], appliedForce, angX); + angY = V4MulAdd(c->ang0[1], appliedForce, angY); + angZ = V4MulAdd(c->ang0[2], appliedForce, angZ); + + base++; + } + + //We need to do the cross product now + + angX = V4Sub(angX, V4NegMulSub(header->body0WorkOffset[0], linY, V4Mul(header->body0WorkOffset[1], linZ))); + angY = V4Sub(angY, V4NegMulSub(header->body0WorkOffset[1], linZ, V4Mul(header->body0WorkOffset[2], linX))); + angZ = V4Sub(angZ, V4NegMulSub(header->body0WorkOffset[2], linX, V4Mul(header->body0WorkOffset[0], linY))); + + const Vec4V linLenSq = V4MulAdd(linZ, linZ, V4MulAdd(linY, linY, V4Mul(linX, linX))); + const Vec4V angLenSq = V4MulAdd(angZ, angZ, V4MulAdd(angY, angY, V4Mul(angX, angX))); + + const Vec4V linLen = V4Sqrt(linLenSq); + const Vec4V angLen = V4Sqrt(angLenSq); + + const BoolV broken = BOr(V4IsGrtr(linLen, header->linBreakImpulse), V4IsGrtr(angLen, header->angBreakImpulse)); + + PX_ALIGN(16, PxU32 iBroken[4]); + BStoreA(broken, iBroken); + + Vec4V lin0, lin1, lin2, lin3; + Vec4V ang0, ang1, ang2, ang3; + + PX_TRANSPOSE_34_44(linX, linY, linZ, lin0, lin1, lin2, lin3); + PX_TRANSPOSE_34_44(angX, angY, angZ, ang0, ang1, ang2, ang3); + + if (writeback0) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin0), writeback0->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang0), writeback0->angularImpulse); + writeback0->broken = header->breakable[0] ? PxU32(iBroken[0] != 0) : 0; + } + if (writeback1) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin1), writeback1->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang1), writeback1->angularImpulse); + writeback1->broken = header->breakable[1] ? PxU32(iBroken[1] != 0) : 0; + } + if (writeback2) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin2), writeback2->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang2), writeback2->angularImpulse); + writeback2->broken = header->breakable[2] ? PxU32(iBroken[2] != 0) : 0; + } + if (writeback3) + { + V3StoreU(Vec3V_From_Vec4V_WUndefined(lin3), writeback3->linearImpulse); + V3StoreU(Vec3V_From_Vec4V_WUndefined(ang3), writeback3->angularImpulse); + writeback3->broken = header->breakable[3] ? PxU32(iBroken[3] != 0) : 0; + } + } +} + + +void concludeContact4_Block(const PxTGSSolverConstraintDesc* PX_RESTRICT desc) +{ + PX_UNUSED(desc); + //const PxU8* PX_RESTRICT last = desc[0].constraint + getConstraintLength(desc[0]); + + ////hopefully pointer aliasing doesn't bite. + //PxU8* PX_RESTRICT currPtr = desc[0].constraint; + + //const Vec4V zero = V4Zero(); + + ////const PxU8 type = *desc[0].constraint; + //const PxU32 contactSize = sizeof(SolverContactPointStepBlock); + //const PxU32 frictionSize = sizeof(SolverContactFrictionStepBlock); + + //while ((currPtr < last)) + //{ + // SolverContactHeaderStepBlock* PX_RESTRICT hdr = reinterpret_cast(currPtr); + + // currPtr = reinterpret_cast(hdr + 1); + + // const PxU32 numNormalConstr = hdr->numNormalConstr; + // const PxU32 numFrictionConstr = hdr->numFrictionConstr; + + // //Applied forces + // currPtr += sizeof(Vec4V)*numNormalConstr; + + // //SolverContactPointStepBlock* PX_RESTRICT contacts = reinterpret_cast(currPtr); + // currPtr += (numNormalConstr * contactSize); + + // bool hasMaxImpulse = (hdr->flag & SolverContactHeader4::eHAS_MAX_IMPULSE) != 0; + + // if (hasMaxImpulse) + // currPtr += sizeof(Vec4V) * numNormalConstr; + + // currPtr += sizeof(Vec4V)*numFrictionConstr; + + // SolverContactFrictionStepBlock* PX_RESTRICT frictions = reinterpret_cast(currPtr); + // currPtr += (numFrictionConstr * frictionSize); + + // /*for (PxU32 i = 0; iconstraint; + if (bPtr == NULL) + return; + + + const SolverConstraint1DHeaderStep4* PX_RESTRICT header = reinterpret_cast(bPtr); + SolverConstraint1DStep4* PX_RESTRICT base = reinterpret_cast(bPtr + sizeof(SolverConstraint1DHeaderStep4)); + + const VecI32V mask = I4Load(DY_SC_FLAG_KEEP_BIAS); + const Vec4V zero = V4Zero(); + + for (PxU32 i = 0; icount; ++i, base++) + { + Ps::prefetchLine(base + 1); + SolverConstraint1DStep4& c = *base; + + const VecI32V flags = I4LoadA(reinterpret_cast(c.flags)); + + const BoolV keepBias = VecI32V_IsEq(VecI32V_And(flags, mask), mask); + + c.biasScale = V4Sel(keepBias, c.biasScale, zero); + } +} + + +void solveConcludeContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, + const PxReal elapsedTime) +{ + solveContact4_Block(desc + hdr.mStartIndex, true, -PX_MAX_F32, elapsedTime); + concludeContact4_Block(desc + hdr.mStartIndex); +} + +void solveConclude1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxTGSSolverBodyTxInertia* const txInertias, + const PxReal elapsedTime) +{ + solve1DStep4(desc + hdr.mStartIndex, txInertias, elapsedTime); + conclude1DStep4(desc + hdr.mStartIndex); +} + + + + + + +} +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp new file mode 100644 index 000000000..2c779c754 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp @@ -0,0 +1,3307 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsTime.h" +#include "PsAtomic.h" +#include "PxvDynamics.h" + +#include "common/PxProfileZone.h" +#include "PxsRigidBody.h" +#include "PxsContactManager.h" +#include "DyTGSDynamics.h" +#include "DyBodyCoreIntegrator.h" +#include "DySolverCore.h" +#include "DySolverControl.h" +#include "DySolverContact.h" +#include "DySolverContactPF.h" +#include "DyArticulationContactPrep.h" +#include "DySolverBody.h" + +#include "DyConstraintPrep.h" +#include "DyConstraintPartition.h" +#include "DyArticulation.h" + +#include "CmFlushPool.h" +#include "DyArticulationPImpl.h" +#include "PxsMaterialManager.h" +#include "DySolverContactPF4.h" +#include "DyContactReduction.h" +#include "PxcNpContactPrepShared.h" +#include "DyContactPrep.h" +#include "DySolverControlPF.h" +#include "PxSceneDesc.h" +#include "PxsSimpleIslandManager.h" +#include "PxvNphaseImplementationContext.h" +#include "PxsContactManagerState.h" +#include "PxsDefaultMemoryManager.h" +#include "DyContactPrepShared.h" +#include "DySolverContext.h" +#include "DyDynamics.h" +#include "DySolverConstraint1D.h" +#include "DyTGSPartition.h" +#include "PxvSimStats.h" + +#define PX_USE_BLOCK_SOLVER 1 +#define PX_USE_BLOCK_1D 1 + + +namespace physx +{ +namespace Dy +{ + +Context* createTGSDynamicsContext(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, Cm::FlushPool& taskPool, + PxvSimStats& simStats, PxTaskManager* taskManager, Ps::VirtualAllocatorCallback* allocatorCallback, PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, PxU64 contextID, + const bool enableStabilization, const bool useEnhancedDeterminism, const bool useAdaptiveForce, + const PxReal lengthScale + ) +{ + return DynamicsTGSContext::create(memBlockPool, scratchAllocator, taskPool, simStats, taskManager, allocatorCallback, materialManager, accurateIslandSim, + contextID, enableStabilization, useEnhancedDeterminism, useAdaptiveForce, lengthScale); +} + +// PT: TODO: consider removing this function. We already have "createDynamicsContext". +DynamicsTGSContext* DynamicsTGSContext::create(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocatorCallback, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal lengthScale + ) +{ + // PT: TODO: inherit from UserAllocated, remove placement new + DynamicsTGSContext* dc = reinterpret_cast(PX_ALLOC(sizeof(DynamicsTGSContext), "DynamicsTGSContext")); + if (dc) + { + new(dc)DynamicsTGSContext(memBlockPool, scratchAllocator, taskPool, simStats, taskManager, allocatorCallback, materialManager, accurateIslandSim, contextID, enableStabilization, useEnhancedDeterminism, useAdaptiveForce, lengthScale); + } + return dc; +} + +void DynamicsTGSContext::destroy() +{ + this->~DynamicsTGSContext(); + PX_FREE(this); +} + +void DynamicsTGSContext::resetThreadContexts() +{ + PxcThreadCoherentCacheIterator threadContextIt(mThreadContextPool); + ThreadContext* threadContext = threadContextIt.getNext(); + + while (threadContext != NULL) + { + threadContext->reset(); + threadContext = threadContextIt.getNext(); + } +} + +PX_FORCE_INLINE PxVec3 safeRecip(const PxVec3& v) +{ + return PxVec3(v.x == 0.f ? 0.f : 1.f/v.x, v.y == 0.f ? 0.f : 1.f/v.y, v.z == 0.f ? 0.f : 1.f/v.z); +} + +void copyToSolverBodyDataStep(const PxVec3& linearVelocity, const PxVec3& angularVelocity, const PxReal invMass, const PxVec3& invInertia, const PxTransform& globalPose, + const PxReal maxDepenetrationVelocity, const PxReal maxContactImpulse, const PxU32 nodeIndex, const PxReal reportThreshold, + const PxReal maxAngVelSq, PxU32 lockFlags, bool isKinematic, + PxTGSSolverBodyVel& solverVel, PxTGSSolverBodyTxInertia& solverBodyTxInertia, PxStepSolverBodyData& solverBodyData) +{ + const PxMat33 rotation(globalPose.q); + + const PxVec3 sqrtInvInertia = computeSafeSqrtInertia(invInertia); + + const PxVec3 sqrtBodySpaceInertia = safeRecip(sqrtInvInertia); + + Cm::transformInertiaTensor(sqrtInvInertia, rotation, solverBodyTxInertia.sqrtInvInertia); + + solverBodyTxInertia.deltaBody2World.p = globalPose.p; + solverBodyTxInertia.deltaBody2World.q = PxQuat(PxIdentity); + + PxMat33 sqrtInertia; + Cm::transformInertiaTensor(sqrtBodySpaceInertia, rotation, sqrtInertia); + + PxVec3 lv = linearVelocity; + PxVec3 av = angularVelocity; + + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + lv.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + lv.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + lv.z = 0.f; + + //KS - technically, we can zero the inertia columns and produce stiffer constraints. However, this can cause numerical issues with the + //joint solver, which is fixed by disabling joint preprocessing and setting minResponseThreshold to some reasonable value > 0. However, until + //this is handled automatically, it's probably better not to zero these inertia rows + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + { + av.x = 0.f; + //data.sqrtInvInertia.column0 = PxVec3(0.f); + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + { + av.y = 0.f; + //data.sqrtInvInertia.column1 = PxVec3(0.f); + } + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + { + av.z = 0.f; + //data.sqrtInvInertia.column2 = PxVec3(0.f); + } + } + + solverVel.linearVelocity = lv; + solverVel.angularVelocity = sqrtInertia * av; + solverVel.deltaLinDt = PxVec3(0.f); + solverVel.deltaAngDt = PxVec3(0.f); + solverVel.lockFlags = PxU16(lockFlags); + solverVel.isKinematic = isKinematic; + solverVel.maxAngVel = PxSqrt(maxAngVelSq); + solverVel.partitionMask = 0; + + + + solverBodyData.nodeIndex = nodeIndex; + solverBodyData.invMass = invMass; + solverBodyData.penBiasClamp = maxDepenetrationVelocity; + solverBodyData.maxContactImpulse = maxContactImpulse; + solverBodyData.reportThreshold = reportThreshold; + solverBodyData.originalLinearVelocity = lv; + solverBodyData.originalAngularVelocity = av; + + PX_ASSERT(lv.isFinite()); + PX_ASSERT(av.isFinite()); +} + + + +void copyToSolverBodyDataStepKinematic(const PxVec3& linearVelocity, const PxVec3& angularVelocity, const PxTransform& globalPose, + const PxReal maxDepenetrationVelocity, const PxReal maxContactImpulse, const PxU32 nodeIndex, const PxReal reportThreshold, + const PxReal maxAngVelSq, + PxTGSSolverBodyVel& solverVel, PxTGSSolverBodyTxInertia& solverBodyTxInertia, PxStepSolverBodyData& solverBodyData) +{ + const PxMat33 rotation(globalPose.q); + + solverBodyTxInertia.deltaBody2World.p = globalPose.p; + solverBodyTxInertia.deltaBody2World.q = PxQuat(PxIdentity); + + solverBodyTxInertia.sqrtInvInertia = PxMat33(PxVec3(0.f), PxVec3(0.f), PxVec3(0.f)); + + solverVel.linearVelocity = PxVec3(0.f); + solverVel.angularVelocity = PxVec3(0.f); + solverVel.deltaLinDt = PxVec3(0.f); + solverVel.deltaAngDt = PxVec3(0.f); + solverVel.lockFlags = 0; + solverVel.isKinematic = true; + solverVel.maxAngVel = PxSqrt(maxAngVelSq); + solverVel.partitionMask = 0; + + solverBodyData.nodeIndex = nodeIndex; + solverBodyData.invMass = 0.f; + solverBodyData.penBiasClamp = maxDepenetrationVelocity; + solverBodyData.maxContactImpulse = maxContactImpulse; + solverBodyData.reportThreshold = reportThreshold; + solverBodyData.originalLinearVelocity = linearVelocity; + solverBodyData.originalAngularVelocity = angularVelocity; +} + + +// =========================== Basic methods + + +DynamicsTGSContext::DynamicsTGSContext(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocatorCallback, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal lengthScale + ) : + Dy::Context(accurateIslandSim, allocatorCallback, simStats, enableStabilization, useEnhancedDeterminism, useAdaptiveForce, PX_MAX_F32), + mThreadContextPool(memBlockPool), + mMaterialManager(materialManager), + mLengthScale(lengthScale), + mScratchAllocator(scratchAllocator), + mTaskPool(taskPool), + mTaskManager(taskManager), + mContextID(contextID) +{ + createThresholdStream(*allocatorCallback); + createForceChangeThresholdStream(*allocatorCallback); + mExceededForceThresholdStream[0] = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ExceededForceThresholdStream[0]")), ThresholdStream(*allocatorCallback)); + mExceededForceThresholdStream[1] = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ThresholdStream), PX_DEBUG_EXP("ExceededForceThresholdStream[1]")), ThresholdStream(*allocatorCallback)); + mThresholdStreamOut = 0; + mCurrentIndex = 0; + + PxMemZero(&mWorldSolverBodyVel, sizeof(mWorldSolverBodyVel)); + + mWorldSolverBodyVel.lockFlags = 0; + mWorldSolverBodyVel.isKinematic = false; + mWorldSolverBodyTxInertia.sqrtInvInertia = PxMat33(PxZero); + mWorldSolverBodyTxInertia.deltaBody2World = PxTransform(PxIdentity); + + mWorldSolverBodyData2.penBiasClamp = -PX_MAX_REAL; + mWorldSolverBodyData2.maxContactImpulse = PX_MAX_REAL; + mWorldSolverBodyData2.nodeIndex = IG_INVALID_NODE; + mWorldSolverBodyData2.invMass = 0; + mWorldSolverBodyData2.reportThreshold = PX_MAX_REAL; + mWorldSolverBodyData2.originalLinearVelocity = PxVec3(0.f); + mWorldSolverBodyData2.originalAngularVelocity = PxVec3(0.f); + + +} + +DynamicsTGSContext::~DynamicsTGSContext() +{ + if (mExceededForceThresholdStream[0]) + { + mExceededForceThresholdStream[0]->~ThresholdStream(); + PX_FREE(mExceededForceThresholdStream[0]); + } + mExceededForceThresholdStream[0] = NULL; + + if (mExceededForceThresholdStream[1]) + { + mExceededForceThresholdStream[1]->~ThresholdStream(); + PX_FREE(mExceededForceThresholdStream[1]); + } + mExceededForceThresholdStream[1] = NULL; + +} + + + +void DynamicsTGSContext::setDescFromIndices(PxTGSSolverConstraintDesc& desc, + const PxsIndexedInteraction& constraint, const PxU32 solverBodyOffset, PxTGSSolverBodyVel* solverBodies) +{ + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eBODY == 0); + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eKINEMATIC == 1); + const PxU32 offsetMap[] = { solverBodyOffset, 0 }; + //const PxU32 offsetMap[] = {mKinematicCount, 0}; + + if (constraint.indexType0 == PxsIndexedInteraction::eARTICULATION) + { + ArticulationV* a = getArticulation(constraint.articulation0); + desc.articulationA = a; + desc.articulationALength = Ps::to16(a->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationALength & 0x0f)); + desc.linkIndexA = Ps::to16(a->getLinkIndex(constraint.articulation0)); + desc.bodyAIdx = 0; + } + else + { + desc.bodyA = constraint.indexType0 == PxsIndexedInteraction::eWORLD ? &mWorldSolverBodyVel + : &solverBodies[PxU32(constraint.solverBody0) + offsetMap[constraint.indexType0] + 1]; + + desc.bodyAIdx = constraint.indexType0 == PxsIndexedInteraction::eWORLD ? 0 + : PxU32(constraint.solverBody0) + offsetMap[constraint.indexType0] + 1; + + desc.linkIndexA = PxTGSSolverConstraintDesc::NO_LINK; + } + + if (constraint.indexType1 == PxsIndexedInteraction::eARTICULATION) + { + ArticulationV* b = getArticulation(constraint.articulation1); + desc.articulationB = b; + desc.articulationBLength = Ps::to16(b->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationBLength & 0x0f)); + desc.linkIndexB = Ps::to16(b->getLinkIndex(constraint.articulation1)); + desc.bodyBIdx = 0; + } + else + { + desc.bodyB = constraint.indexType1 == PxsIndexedInteraction::eWORLD ? &mWorldSolverBodyVel + : &solverBodies[PxU32(constraint.solverBody1) + offsetMap[constraint.indexType1] + 1]; + + desc.bodyBIdx = constraint.indexType1 == PxsIndexedInteraction::eWORLD ? 0 + : PxU32(constraint.solverBody1) + offsetMap[constraint.indexType1] + 1; + + desc.linkIndexB = PxTGSSolverConstraintDesc::NO_LINK; + } +} + +void DynamicsTGSContext::setDescFromIndices(PxTGSSolverConstraintDesc& desc, IG::EdgeIndex edgeIndex, const IG::SimpleIslandManager& islandManager, + PxU32* bodyRemap, const PxU32 solverBodyOffset, PxTGSSolverBodyVel* solverBodies) +{ + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eBODY == 0); + PX_COMPILE_TIME_ASSERT(PxsIndexedInteraction::eKINEMATIC == 1); + + const IG::IslandSim& islandSim = islandManager.getAccurateIslandSim(); + + IG::NodeIndex node1 = islandSim.getNodeIndex1(edgeIndex); + if (node1.isStaticBody()) + { + desc.bodyA = &mWorldSolverBodyVel; + desc.bodyAIdx = 0; + desc.linkIndexA = PxTGSSolverConstraintDesc::NO_LINK; + } + else + { + const IG::Node& node = islandSim.getNode(node1); + + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + PX_ASSERT(node1.isArticulation()); + Dy::ArticulationV* a = islandSim.getLLArticulation(node1); + desc.articulationA = a; + desc.articulationALength = Ps::to16(a->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationALength & 0x0f)); + desc.linkIndexA = Ps::to16(node1.articulationLinkId()); + desc.bodyAIdx = 0; + } + else + { + PX_ASSERT(!node1.isArticulation()); + PxU32 activeIndex = islandSim.getActiveNodeIndex(node1); + PxU32 index = node.isKinematic() ? activeIndex : bodyRemap[activeIndex] + solverBodyOffset; + desc.bodyA = &solverBodies[index + 1]; + desc.bodyAIdx = index + 1; + desc.linkIndexA = PxTGSSolverConstraintDesc::NO_LINK; + } + } + + IG::NodeIndex node2 = islandSim.getNodeIndex2(edgeIndex); + if (node2.isStaticBody()) + { + desc.bodyB = &mWorldSolverBodyVel; + desc.bodyBIdx = 0; + desc.linkIndexB = PxTGSSolverConstraintDesc::NO_LINK; + } + else + { + const IG::Node& node = islandSim.getNode(node2); + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + PX_ASSERT(node2.isArticulation()); + Dy::ArticulationV* b = islandSim.getLLArticulation(node2); + desc.articulationB = b; + desc.articulationBLength = Ps::to16(b->getSolverDataSize()); + PX_ASSERT(0 == (desc.articulationBLength & 0x0f)); + desc.linkIndexB = Ps::to16(node2.articulationLinkId()); + desc.bodyBIdx = 0; + } + else + { + PX_ASSERT(!node2.isArticulation()); + PxU32 activeIndex = islandSim.getActiveNodeIndex(node2); + PxU32 index = node.isKinematic() ? activeIndex : bodyRemap[activeIndex] + solverBodyOffset; + desc.bodyB = &solverBodies[index + 1]; + desc.bodyBIdx = index + 1; + desc.linkIndexB = PxTGSSolverConstraintDesc::NO_LINK; + } + } +} + +class DynamicsMergeTask : public Cm::Task +{ + PxBaseTask* mSecondContinuation; + +public: + DynamicsMergeTask(PxU64 contextId) : Cm::Task(contextId), mSecondContinuation(NULL) + { + } + + void setSecondContinuation(PxBaseTask* task) { task->addReference(); mSecondContinuation = task; } + + virtual const char* getName() const { return "MergeTask"; } + + virtual void runInternal() + { + } + + virtual void release() + { + mSecondContinuation->removeReference(); + Cm::Task::release(); + } + +}; + +class KinematicCopyTGSTask : public Cm::Task +{ + const IG::NodeIndex* const mKinematicIndices; + const PxU32 mNbKinematics; + const IG::IslandSim& mIslandSim; + PxTGSSolverBodyVel* mVels; + PxTGSSolverBodyTxInertia* mInertia; + PxStepSolverBodyData* mBodyData; + + PX_NOCOPY(KinematicCopyTGSTask) + +public: + + static const PxU32 NbKinematicsPerTask = 1024; + + KinematicCopyTGSTask(const IG::NodeIndex* const kinematicIndices, + const PxU32 nbKinematics, const IG::IslandSim& islandSim, PxTGSSolverBodyVel* vels, + PxTGSSolverBodyTxInertia* inertias, PxStepSolverBodyData* datas, PxU64 contextID) : Cm::Task(contextID), + mKinematicIndices(kinematicIndices), mNbKinematics(nbKinematics), + mIslandSim(islandSim), mVels(vels), mInertia(inertias), mBodyData(datas) + { + } + + virtual const char* getName() const { return "KinematicCopyTask"; } + + virtual void runInternal() + { + for (PxU32 i = 0; igetCore(); + copyToSolverBodyDataStepKinematic(core.linearVelocity, core.angularVelocity, core.body2World, core.maxPenBias, + core.maxContactImpulse, mKinematicIndices[i].index(), core.contactReportThreshold, core.maxAngularVelocitySq, + mVels[i], mInertia[i], mBodyData[i]); + + rigidBody->saveLastCCDTransform(); + } + } +}; + +class UpdateContinuationTGSTask : public Cm::Task +{ + DynamicsTGSContext& mContext; + IG::SimpleIslandManager& mSimpleIslandManager; + PxBaseTask* mLostTouchTask; + + PX_NOCOPY(UpdateContinuationTGSTask) +public: + + UpdateContinuationTGSTask(DynamicsTGSContext& context, + IG::SimpleIslandManager& simpleIslandManager, + PxBaseTask* lostTouchTask, + PxU64 contextID) : Cm::Task(contextID), mContext(context), mSimpleIslandManager(simpleIslandManager), + mLostTouchTask(lostTouchTask) + { + } + + virtual const char* getName() const { return "UpdateContinuationTask";} + + virtual void runInternal() + { + mContext.updatePostKinematic(mSimpleIslandManager, mCont, mLostTouchTask); + //Allow lost touch task to run once all tasks have be scheduled + mLostTouchTask->removeReference(); + } + +}; + + +void DynamicsTGSContext::update(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask, + PxsContactManager** /*foundPatchManagers*/, PxU32 /*nbFoundPatchManagers*/, + PxsContactManager** /*lostPatchManagers*/, PxU32 /*nbLostPatchManagers*/, + PxU32 /*maxPatchesPerCM*/, + PxsContactManagerOutputIterator& iterator, + PxsContactManagerOutput*, + const PxReal dt, const PxVec3& gravity, const PxU32 /*bitMapWordCounts*/) +{ + PX_PROFILE_ZONE("Dynamics.solverQueueTasks", mContextID); + + PX_UNUSED(simpleIslandManager); + + mOutputIterator = iterator; + + //const PxU32 nbSubsteps = 16; + + mDt = dt; + mInvDt = 1.f / dt; + + mGravity = gravity; + + const IG::IslandSim& islandSim = simpleIslandManager.getAccurateIslandSim(); + + const PxU32 islandCount = islandSim.getNbActiveIslands(); + + const PxU32 activatedContactCount = islandSim.getNbActivatedEdges(IG::Edge::eCONTACT_MANAGER); + const IG::EdgeIndex* const activatingEdges = islandSim.getActivatedEdges(IG::Edge::eCONTACT_MANAGER); + + for (PxU32 a = 0; a < activatedContactCount; ++a) + { + PxsContactManager* cm = simpleIslandManager.getContactManager(activatingEdges[a]); + if (cm) + { + cm->getWorkUnit().frictionPatchCount = 0; //KS - zero the friction patch count on any activating edges + } + } + +#if PX_ENABLE_SIM_STATS + if (islandCount > 0) + { + mSimStats.mNbActiveKinematicBodies = islandSim.getNbActiveKinematics(); + mSimStats.mNbActiveDynamicBodies = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + mSimStats.mNbActiveConstraints = islandSim.getNbActiveEdges(IG::Edge::eCONSTRAINT); + } + else + { + mSimStats.mNbActiveKinematicBodies = islandSim.getNbActiveKinematics(); + mSimStats.mNbActiveDynamicBodies = 0; + mSimStats.mNbActiveConstraints = 0; + } +#endif + + mThresholdStreamOut = 0; + + resetThreadContexts(); + + //If there is no work to do then we can do nothing at all. + if (0 == islandCount) + { + return; + } + + //Block to make sure it doesn't run before stage2 of update! + lostTouchTask->addReference(); + + UpdateContinuationTGSTask* task = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(UpdateContinuationTGSTask)), UpdateContinuationTGSTask) + (*this, simpleIslandManager, lostTouchTask, mContextID); + + task->setContinuation(continuation); + + //KS - test that world solver body's velocities are finite and 0, then set it to 0. + //Technically, the velocity should always be 0 but can be stomped if a NAN creeps into the simulation. + PX_ASSERT(mWorldSolverBodyVel.linearVelocity == PxVec3(0.f)); + PX_ASSERT(mWorldSolverBodyVel.angularVelocity == PxVec3(0.f)); + PX_ASSERT(mWorldSolverBodyVel.linearVelocity.isFinite()); + PX_ASSERT(mWorldSolverBodyVel.angularVelocity.isFinite()); + + mWorldSolverBodyVel.linearVelocity = mWorldSolverBodyVel.angularVelocity = PxVec3(0.f); + + const PxU32 kinematicCount = islandSim.getNbActiveKinematics(); + const IG::NodeIndex* const kinematicIndices = islandSim.getActiveKinematics(); + mKinematicCount = kinematicCount; + + const PxU32 bodyCount = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + PxU32 numArtics = islandSim.getNbActiveNodes(IG::Node::eARTICULATION_TYPE); + + { + if (kinematicCount + bodyCount > mSolverBodyVelPool.capacity()) + { + mSolverBodyRemapTable.reserve((kinematicCount + bodyCount + 31 + 1) & ~31); + mSolverBodyVelPool.reserve((kinematicCount + bodyCount +31 + 1) & ~31); + mSolverBodyTxInertiaPool.reserve((kinematicCount + bodyCount +31 + 1) & ~31); + mSolverBodyDataPool2.reserve((kinematicCount + bodyCount +31 + 1) & ~31); + } + + { + mSolverBodyVelPool.resize(kinematicCount + bodyCount +1); + mSolverBodyTxInertiaPool.resize(kinematicCount + bodyCount +1); + mSolverBodyDataPool2.resize(kinematicCount + bodyCount +1); + mSolverBodyRemapTable.resize(kinematicCount + bodyCount + 1); + } + + // integrate and copy all the kinematics - overkill, since not all kinematics + // need solver bodies + + mSolverBodyVelPool[0] = mWorldSolverBodyVel; + mSolverBodyTxInertiaPool[0] = mWorldSolverBodyTxInertia; + mSolverBodyDataPool2[0] = mWorldSolverBodyData2; + + + { + PX_PROFILE_ZONE("Dynamics.updateKinematics", mContextID); + for (PxU32 i = 0; i < kinematicCount; i+= KinematicCopyTGSTask::NbKinematicsPerTask) + { + const PxU32 nbToProcess = PxMin(kinematicCount - i, KinematicCopyTGSTask::NbKinematicsPerTask); + + KinematicCopyTGSTask* kinematicTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(KinematicCopyTGSTask)), KinematicCopyTGSTask) + (kinematicIndices + i, nbToProcess, islandSim, &mSolverBodyVelPool[i+1], + &mSolverBodyTxInertiaPool[i + 1], &mSolverBodyDataPool2[i + 1], mContextID); + + kinematicTask->setContinuation(task); + kinematicTask->removeReference(); + } + } + } + + PxU32 numArticulationConstraints = numArtics*Dy::DY_ARTICULATION_MAX_SIZE; //Just allocate enough memory to fit worst-case maximum size articulations... + + const PxU32 nbActiveContactManagers = islandSim.getNbActiveEdges(IG::Edge::eCONTACT_MANAGER); + const PxU32 nbActiveConstraints = islandSim.getNbActiveEdges(IG::Edge::eCONSTRAINT); + + PxU32 totalConstraintCount = nbActiveConstraints + nbActiveContactManagers + numArticulationConstraints; + + mSolverConstraintDescPool.forceSize_Unsafe(0); + mSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mOrderedSolverConstraintDescPool.forceSize_Unsafe(0); + mOrderedSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mOrderedSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mContactConstraintBatchHeaders.forceSize_Unsafe(0); + mContactConstraintBatchHeaders.reserve((totalConstraintCount + 63) & (~63)); + mContactConstraintBatchHeaders.forceSize_Unsafe(totalConstraintCount); + + mTempSolverConstraintDescPool.forceSize_Unsafe(0); + mTempSolverConstraintDescPool.reserve((totalConstraintCount + 63) & (~63)); + mTempSolverConstraintDescPool.forceSize_Unsafe(totalConstraintCount); + + mContactList.forceSize_Unsafe(0); + mContactList.reserve((nbActiveContactManagers + 63u) & (~63u)); + mContactList.forceSize_Unsafe(nbActiveContactManagers); + + mMotionVelocityArray.forceSize_Unsafe(0); + mMotionVelocityArray.reserve((bodyCount + 63u) & (~63u)); + mMotionVelocityArray.forceSize_Unsafe(bodyCount); + + mBodyCoreArray.forceSize_Unsafe(0); + mBodyCoreArray.reserve((bodyCount + 63u) & (~63u)); + mBodyCoreArray.forceSize_Unsafe(bodyCount); + + mRigidBodyArray.forceSize_Unsafe(0); + mRigidBodyArray.reserve((bodyCount + 63u) & (~63u)); + mRigidBodyArray.forceSize_Unsafe(bodyCount); + + mArticulationArray.forceSize_Unsafe(0); + mArticulationArray.reserve((numArtics + 63u) & (~63u)); + mArticulationArray.forceSize_Unsafe(numArtics); + + mNodeIndexArray.forceSize_Unsafe(0); + mNodeIndexArray.reserve((bodyCount + 63u) & (~63u)); + mNodeIndexArray.forceSize_Unsafe(bodyCount); + + + + + ThresholdStream& stream = getThresholdStream(); + stream.forceSize_Unsafe(0); + stream.reserve(Ps::nextPowerOfTwo(nbActiveContactManagers != 0 ? nbActiveContactManagers - 1 : nbActiveContactManagers)); + + + + //flip exceeded force threshold buffer + mCurrentIndex = 1 - mCurrentIndex; + + task->removeReference(); + +} + +void DynamicsTGSContext::updatePostKinematic(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask) +{ + const IG::IslandSim& islandSim = simpleIslandManager.getAccurateIslandSim(); + + const IG::IslandId*const islandIds = islandSim.getActiveIslands(); + + DynamicsMergeTask* mergeTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(DynamicsMergeTask)), DynamicsMergeTask)(mContextID); + + mergeTask->setContinuation(continuation); + mergeTask->setSecondContinuation(lostTouchTask); + + //PxReal dt = mDt; + + PxU32 currentIsland = 0; + PxU32 currentBodyIndex = 0; + PxU32 currentArticulation = 0; + PxU32 currentContact = 0; + + PxU32 constraintIndex = 0; + + const PxU32 minIslandSize = mSolverBatchSize; + + const PxU32 islandCount = islandSim.getNbActiveIslands(); + + //while(startremoveReference(); +} + +void DynamicsTGSContext::prepareBodiesAndConstraints(const SolverIslandObjectsStep& objects, + IG::SimpleIslandManager& islandManager, + IslandContextStep& islandContext) +{ + Dy::ThreadContext& mThreadContext = *islandContext.mThreadContext; + + mThreadContext.mMaxSolverPositionIterations = 0; + mThreadContext.mMaxSolverVelocityIterations = 0; + mThreadContext.mAxisConstraintCount = 0; + mThreadContext.mContactDescPtr = mThreadContext.contactConstraintDescArray; + mThreadContext.mFrictionDescPtr = mThreadContext.frictionConstraintDescArray.begin(); + mThreadContext.mNumDifferentBodyConstraints = 0; + mThreadContext.mNumSelfConstraintBlocks = 0; + mThreadContext.mNumSelfConstraints = 0; + mThreadContext.mNumDifferentBodyFrictionConstraints = 0; + mThreadContext.mNumSelfConstraintFrictionBlocks = 0; + mThreadContext.mNumSelfFrictionConstraints = 0; + mThreadContext.numContactConstraintBatches = 0; + mThreadContext.contactDescArraySize = 0; + + mThreadContext.motionVelocityArray = objects.motionVelocities; + mThreadContext.mBodyCoreArray = objects.bodyCoreArray; + mThreadContext.mRigidBodyArray = objects.bodies; + mThreadContext.mArticulationArray = objects.articulations; + mThreadContext.bodyRemapTable = objects.bodyRemapTable; + mThreadContext.mNodeIndexArray = objects.nodeIndexArray; + + const PxU32 frictionConstraintCount = 0; + mThreadContext.resizeArrays(frictionConstraintCount, islandContext.mCounts.articulations); + + PxsBodyCore** PX_RESTRICT bodyArrayPtr = mThreadContext.mBodyCoreArray; + PxsRigidBody** PX_RESTRICT rigidBodyPtr = mThreadContext.mRigidBodyArray; + ArticulationV** PX_RESTRICT articulationPtr = mThreadContext.mArticulationArray; + PxU32* PX_RESTRICT bodyRemapTable = mThreadContext.bodyRemapTable; + PxU32* PX_RESTRICT nodeIndexArray = mThreadContext.mNodeIndexArray; + + PxU32 nbIslands = objects.numIslands; + const IG::IslandId* const islandIds = objects.islandIds; + + const IG::IslandSim& islandSim = islandManager.getAccurateIslandSim(); + + PxU32 bodyIndex = 0, articIndex = 0; + for (PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::NodeIndex currentIndex = island.mRootNode; + + while (currentIndex.isValid()) + { + const IG::Node& node = islandSim.getNode(currentIndex); + + if (node.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + articulationPtr[articIndex++] = node.getArticulation(); + } + else + { + PxsRigidBody* rigid = node.getRigidBody(); + PX_ASSERT(bodyIndex < (islandContext.mCounts.bodies + mKinematicCount + 1)); + rigidBodyPtr[bodyIndex] = rigid; + bodyArrayPtr[bodyIndex] = &rigid->getCore(); + nodeIndexArray[bodyIndex] = currentIndex.index(); + bodyRemapTable[islandSim.getActiveNodeIndex(currentIndex)] = bodyIndex++; + } + + currentIndex = node.mNextNode; + } + } + + + PxsIndexedContactManager* indexedManagers = objects.contactManagers; + + PxU32 currentContactIndex = 0; + for (PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::EdgeIndex contactEdgeIndex = island.mFirstEdge[IG::Edge::eCONTACT_MANAGER]; + + while (contactEdgeIndex != IG_INVALID_EDGE) + { + const IG::Edge& edge = islandSim.getEdge(contactEdgeIndex); + + PxsContactManager* contactManager = islandManager.getContactManager(contactEdgeIndex); + + if (contactManager) + { + const IG::NodeIndex nodeIndex1 = islandSim.getNodeIndex1(contactEdgeIndex); + const IG::NodeIndex nodeIndex2 = islandSim.getNodeIndex2(contactEdgeIndex); + + PxsIndexedContactManager& indexedManager = indexedManagers[currentContactIndex++]; + indexedManager.contactManager = contactManager; + + PX_ASSERT(!nodeIndex1.isStaticBody()); + { + const IG::Node& node1 = islandSim.getNode(nodeIndex1); + + //Is it an articulation or not??? + if (node1.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + indexedManager.indexType0 = PxsIndexedInteraction::eARTICULATION; + indexedManager.solverBody0 = size_t(node1.getArticulation()) | nodeIndex1.articulationLinkId(); + } + else + { + if (node1.isKinematic()) + { + indexedManager.indexType0 = PxsIndexedInteraction::eKINEMATIC; + indexedManager.solverBody0 = islandSim.getActiveNodeIndex(nodeIndex1); + } + else + { + indexedManager.indexType0 = PxsIndexedInteraction::eBODY; + indexedManager.solverBody0 = bodyRemapTable[islandSim.getActiveNodeIndex(nodeIndex1)]; + } + PX_ASSERT(indexedManager.solverBody0 < (islandContext.mCounts.bodies + mKinematicCount + 1)); + } + + } + + if (nodeIndex2.isStaticBody()) + { + indexedManager.indexType1 = PxsIndexedInteraction::eWORLD; + } + else + { + const IG::Node& node2 = islandSim.getNode(nodeIndex2); + + //Is it an articulation or not??? + if (node2.getNodeType() == IG::Node::eARTICULATION_TYPE) + { + indexedManager.indexType1 = PxsIndexedInteraction::eARTICULATION; + indexedManager.solverBody1 = size_t(node2.getArticulation()) | nodeIndex2.articulationLinkId(); + } + else + { + if (node2.isKinematic()) + { + indexedManager.indexType1 = PxsIndexedInteraction::eKINEMATIC; + indexedManager.solverBody1 = islandSim.getActiveNodeIndex(nodeIndex2); + } + else + { + indexedManager.indexType1 = PxsIndexedInteraction::eBODY; + indexedManager.solverBody1 = bodyRemapTable[islandSim.getActiveNodeIndex(nodeIndex2)]; + } + PX_ASSERT(indexedManager.solverBody1 < (islandContext.mCounts.bodies + mKinematicCount + 1)); + } + } + + } + contactEdgeIndex = edge.mNextIslandEdge; + } + } + + islandContext.mCounts.contactManagers = currentContactIndex; +} + +struct ConstraintLess +{ + bool operator()(const PxTGSSolverConstraintDesc& left, const PxTGSSolverConstraintDesc& right) const + { + return reinterpret_cast(left.constraint)->index > reinterpret_cast(right.constraint)->index; + } +}; + +void DynamicsTGSContext::setupDescs(IslandContextStep& mIslandContext, const SolverIslandObjectsStep& mObjects, IG::SimpleIslandManager& mIslandManager, + PxU32* mBodyRemapTable, PxU32 mSolverBodyOffset, PxsContactManagerOutputIterator& outputs) +{ + PX_UNUSED(outputs); + ThreadContext& mThreadContext = *mIslandContext.mThreadContext; + PxTGSSolverConstraintDesc* contactDescPtr = mObjects.constraintDescs; + + //PxU32 constraintCount = mCounts.constraints + mCounts.contactManagers; + + PxU32 nbIslands = mObjects.numIslands; + const IG::IslandId* const islandIds = mObjects.islandIds; + + const IG::IslandSim& islandSim = mIslandManager.getAccurateIslandSim(); + + for (PxU32 i = 0; i < nbIslands; ++i) + { + const IG::Island& island = islandSim.getIsland(islandIds[i]); + + IG::EdgeIndex edgeId = island.mFirstEdge[IG::Edge::eCONSTRAINT]; + + while (edgeId != IG_INVALID_EDGE) + { + PxTGSSolverConstraintDesc& desc = *contactDescPtr; + + const IG::Edge& edge = islandSim.getEdge(edgeId); + Dy::Constraint* constraint = mIslandManager.getConstraint(edgeId); + setDescFromIndices(desc, edgeId, mIslandManager, mBodyRemapTable, mSolverBodyOffset, + mSolverBodyVelPool.begin()); + desc.constraint = reinterpret_cast(constraint); + desc.constraintLengthOver16 = DY_SC_TYPE_RB_1D; + contactDescPtr++; + edgeId = edge.mNextIslandEdge; + } + + } + + Ps::sort(mObjects.constraintDescs, PxU32(contactDescPtr - mObjects.constraintDescs), ConstraintLess()); + + if (mIslandContext.mCounts.contactManagers) + { + { + for (PxU32 a = 0; a < mIslandContext.mCounts.contactManagers; ++a) + { + //PxsContactManagerOutput& output = outputs.getContactManager(mObjects.contactManagers[a].contactManager->getWorkUnit().mNpIndex); + //if (output.nbContacts > 0) + { + PxTGSSolverConstraintDesc& desc = *contactDescPtr; + setDescFromIndices(desc, mObjects.contactManagers[a], mSolverBodyOffset, mSolverBodyVelPool.begin()); + desc.constraint = reinterpret_cast(mObjects.contactManagers[a].contactManager); + desc.constraintLengthOver16 = DY_SC_TYPE_RB_CONTACT; + contactDescPtr++; + } + //else + //{ + // //Clear friction state! + // mObjects.contactManagers[a].contactManager->getWorkUnit().frictionDataPtr = NULL; + // mObjects.contactManagers[a].contactManager->getWorkUnit().frictionPatchCount = 0; + //} + } + + } + } + mThreadContext.contactDescArraySize = PxU32(contactDescPtr - mObjects.constraintDescs); +} + +void DynamicsTGSContext::preIntegrateBodies(PxsBodyCore** bodyArray, PxsRigidBody** originalBodyArray, + PxTGSSolverBodyVel* solverBodyVelPool, PxTGSSolverBodyTxInertia* solverBodyTxInertia, PxStepSolverBodyData* solverBodyDataPool2, PxU32* nodeIndexArray, const PxU32 bodyCount, const PxVec3& gravity, const PxReal dt, PxU32& posIters, PxU32& velIters, PxU32 /*iteration*/) +{ + PX_PROFILE_ZONE("PreIntegrate", 0); + PxU32 localMaxPosIter = 0; + PxU32 localMaxVelIter = 0; + for (PxU32 i = 0; i < bodyCount; ++i) + { + PxsBodyCore& core = *bodyArray[i]; + const PxsRigidBody& rBody = *originalBodyArray[i]; + + PxU16 iterWord = core.solverIterationCounts; + localMaxPosIter = PxMax(PxU32(iterWord & 0xff), localMaxPosIter); + localMaxVelIter = PxMax(PxU32(iterWord >> 8), localMaxVelIter); + + //const Cm::SpatialVector& accel = originalBodyArray[i]->getAccelerationV(); + bodyCoreComputeUnconstrainedVelocity(gravity, dt, core.linearDamping, core.angularDamping, rBody.accelScale, core.maxLinearVelocitySq, core.maxAngularVelocitySq, + core.linearVelocity, core.angularVelocity, !!(rBody.mInternalFlags & PxcRigidBody::eDISABLE_GRAVITY)); + + copyToSolverBodyDataStep(core.linearVelocity, core.angularVelocity, core.inverseMass, core.inverseInertia, core.body2World, core.maxPenBias, core.maxContactImpulse, nodeIndexArray[i], + core.contactReportThreshold, core.maxAngularVelocitySq, core.lockFlags, false, + solverBodyVelPool[i+1], solverBodyTxInertia[i+1], solverBodyDataPool2[i+1]); + } + + posIters = localMaxPosIter; + velIters = localMaxVelIter; +} + +class BlockAllocator : public PxConstraintAllocator +{ + PxsConstraintBlockManager& mConstraintBlockManager; + PxcConstraintBlockStream& mConstraintBlockStream; + FrictionPatchStreamPair& mFrictionPatchStreamPair; + PxU32& mTotalConstraintByteSize; +public: + + BlockAllocator(PxsConstraintBlockManager& constraintBlockManager, PxcConstraintBlockStream& constraintBlockStream, FrictionPatchStreamPair& frictionPatchStreamPair, + PxU32& totalConstraintByteSize) : + mConstraintBlockManager(constraintBlockManager), mConstraintBlockStream(constraintBlockStream), mFrictionPatchStreamPair(frictionPatchStreamPair), + mTotalConstraintByteSize(totalConstraintByteSize) + { + } + + virtual PxU8* reserveConstraintData(const PxU32 size) + { + mTotalConstraintByteSize += size; + return mConstraintBlockStream.reserve(size, mConstraintBlockManager); + } + + virtual PxU8* reserveFrictionData(const PxU32 size) + { + return mFrictionPatchStreamPair.reserve(size); + } + + virtual PxU8* findInputPatches(PxU8* frictionCookie) + { + return frictionCookie; + } + + PX_NOCOPY(BlockAllocator) + +}; + +PxU32 getContactManagerConstraintDesc(const PxsContactManagerOutput& cmOutput, const PxsContactManager& /*cm*/, PxTGSSolverConstraintDesc& desc) +{ + desc.writeBackLengthOver4 = cmOutput.nbContacts; + desc.writeBack = cmOutput.contactForces; + return cmOutput.nbContacts;// cm.getWorkUnit().axisConstraintCount; +} + +bool createFinalizeSolverContactsStep(PxTGSSolverContactDesc& contactDesc, + PxsContactManagerOutput& output, + ThreadContext& threadContext, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxConstraintAllocator& constraintAllocator); + +PxU32 SetupSolverConstraintStep(SolverConstraintShaderPrepDesc& shaderDesc, + PxTGSSolverConstraintPrepDesc& prepDesc, + PxConstraintAllocator& allocator, + const PxReal dt, const PxReal totalDt, const PxReal invdt, const PxReal invTotalDt, const PxU32 nbSubsteps, + const PxReal lengthScale); + +SolverConstraintPrepState::Enum setupSolverConstraintStep4 +(SolverConstraintShaderPrepDesc* PX_RESTRICT constraintShaderDescs, + PxTGSSolverConstraintPrepDesc* PX_RESTRICT constraintDescs, + const PxReal dt, const PxReal totalDt, const PxReal recipdt, const PxReal recipTotalDt, PxU32& totalRows, + PxConstraintAllocator& allocator, const PxReal lengthScale); + +SolverConstraintPrepState::Enum createFinalizeSolverContacts4Step( + PxsContactManagerOutput** cmOutputs, + ThreadContext& threadContext, + PxTGSSolverContactDesc* blockDescs, + const PxReal invDtF32, + const PxReal invTotalDtF32, + PxReal bounceThresholdF32, + PxReal frictionOffsetThreshold, + PxReal correlationDistance, + PxReal solverOffsetSlop, + PxConstraintAllocator& constraintAllocator); + + +void DynamicsTGSContext::createSolverConstraints(PxTGSSolverConstraintDesc* contactDescPtr, PxConstraintBatchHeader* headers, const PxU32 nbHeaders, + PxsContactManagerOutputIterator& outputs, Dy::ThreadContext& islandThreadContext, Dy::ThreadContext& threadContext, PxReal stepDt, PxReal totalDt, PxReal invStepDt, + PxU32 nbSubsteps) +{ + PX_UNUSED(totalDt); + PX_PROFILE_ZONE("CreateConstraints", 0); + PxTransform idt(PxIdentity); + + BlockAllocator blockAllocator(islandThreadContext.mConstraintBlockManager, threadContext.mConstraintBlockStream, threadContext.mFrictionPatchStreamPair, threadContext.mConstraintSize); + + PxTGSSolverBodyTxInertia* txInertias = mSolverBodyTxInertiaPool.begin(); + PxStepSolverBodyData* solverBodyDatas = mSolverBodyDataPool2.begin(); + + const PxReal invTotalDt = 1.f / totalDt; + + for (PxU32 h = 0; h < nbHeaders; ++h) + { + PxConstraintBatchHeader& hdr = headers[h]; + PxU32 startIdx = hdr.mStartIndex; + PxU32 endIdx = startIdx + hdr.mStride; + + if (contactDescPtr[startIdx].constraintLengthOver16 == DY_SC_TYPE_RB_CONTACT) + { + PxTGSSolverContactDesc blockDescs[4]; + PxsContactManagerOutput* cmOutputs[4]; + PxsContactManager* cms[4]; + + for (PxU32 a = startIdx, i = 0; a < endIdx; ++a, i++) + { + PxTGSSolverConstraintDesc& desc = contactDescPtr[a]; + PxsContactManager* cm = reinterpret_cast(desc.constraint); + + PxTGSSolverContactDesc& blockDesc = blockDescs[i]; + + cms[i] = cm; + + PxcNpWorkUnit& unit = cm->getWorkUnit(); + + PxsContactManagerOutput* cmOutput = &outputs.getContactManager(unit.mNpIndex); + + cmOutputs[i] = cmOutput; + + PxTGSSolverBodyVel& b0 = *desc.bodyA; + PxTGSSolverBodyVel& b1 = *desc.bodyB; + + PxTGSSolverBodyTxInertia& txI0 = txInertias[desc.bodyAIdx]; + PxTGSSolverBodyTxInertia& txI1 = txInertias[desc.bodyBIdx]; + + PxStepSolverBodyData& data0 = solverBodyDatas[desc.bodyAIdx]; + PxStepSolverBodyData& data1 = solverBodyDatas[desc.bodyBIdx]; + + blockDesc.body0 = &b0; + blockDesc.body1 = &b1; + + PxU8 flags = unit.rigidCore0->mFlags; + if (unit.rigidCore1) + flags |= PxU8(unit.rigidCore1->mFlags); + + blockDesc.bodyFrame0 = unit.rigidCore0->body2World; + blockDesc.bodyFrame1 = unit.rigidCore1->body2World; + blockDesc.shapeInteraction = cm->getShapeInteraction(); + blockDesc.contactForces = cmOutput->contactForces; + blockDesc.desc = &desc; + blockDesc.body0 = &b0; + blockDesc.body1 = &b1; + blockDesc.body0TxI = &txI0; + blockDesc.body1TxI = &txI1; + blockDesc.bodyData0 = &data0; + blockDesc.bodyData1 = &data1; + blockDesc.hasForceThresholds = !!(unit.flags & PxcNpWorkUnitFlag::eFORCE_THRESHOLD); + blockDesc.disableStrongFriction = !!(unit.flags & PxcNpWorkUnitFlag::eDISABLE_STRONG_FRICTION); + blockDesc.bodyState0 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) ? PxSolverContactDesc::eARTICULATION : PxSolverContactDesc::eDYNAMIC_BODY; + blockDesc.bodyState1 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) ? PxSolverContactDesc::eARTICULATION : (unit.flags & PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR) ? PxSolverContactDesc::eKINEMATIC_BODY : + ((unit.flags & PxcNpWorkUnitFlag::eDYNAMIC_BODY1) ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eSTATIC_BODY); + //blockDesc.flags = unit.flags; + + PxReal maxImpulse0 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY0) ? static_cast(unit.rigidCore0)->maxContactImpulse : data0.maxContactImpulse; + PxReal maxImpulse1 = (unit.flags & PxcNpWorkUnitFlag::eARTICULATION_BODY1) ? static_cast(unit.rigidCore1)->maxContactImpulse : data1.maxContactImpulse; + + PxReal dominance0 = unit.dominance0 ? 1.f : 0.f; + PxReal dominance1 = unit.dominance1 ? 1.f : 0.f; + + blockDesc.mInvMassScales.linear0 = blockDesc.mInvMassScales.angular0 = dominance0; + blockDesc.mInvMassScales.linear1 = blockDesc.mInvMassScales.angular1 = dominance1; + blockDesc.restDistance = unit.restDistance; + blockDesc.frictionPtr = unit.frictionDataPtr; + blockDesc.frictionCount = unit.frictionPatchCount; + blockDesc.maxCCDSeparation = PX_MAX_F32; + blockDesc.maxImpulse = PxMin(maxImpulse0, maxImpulse1); + blockDesc.torsionalPatchRadius = unit.mTorsionalPatchRadius; + blockDesc.minTorsionalPatchRadius = unit.mMinTorsionalPatchRadius; + } + + SolverConstraintPrepState::Enum buildState = SolverConstraintPrepState::eUNBATCHABLE; + +#if PX_USE_BLOCK_SOLVER + if (hdr.mStride == 4) + { + buildState = createFinalizeSolverContacts4Step( + cmOutputs, + threadContext, + blockDescs, + invStepDt, + invTotalDt, + mBounceThreshold, + mFrictionOffsetThreshold, + mCorrelationDistance, + mSolverOffsetSlop, + blockAllocator); + } +#endif + + if (buildState != SolverConstraintPrepState::eSUCCESS) + { + + for (PxU32 a = startIdx, i = 0; a < endIdx; ++a, i++) + { + PxTGSSolverConstraintDesc& desc = contactDescPtr[a]; + PxsContactManager* cm = reinterpret_cast(desc.constraint); + //PxcNpWorkUnit& n = cm->getWorkUnit(); + + PxsContactManagerOutput& output = *cmOutputs[i]; + //PX_ASSERT(output.nbContacts != 0); + + createFinalizeSolverContactsStep(blockDescs[i], output, threadContext, + invStepDt, invTotalDt, mBounceThreshold, mFrictionOffsetThreshold, mCorrelationDistance, blockAllocator); + + getContactManagerConstraintDesc(output, *cm, desc); + + //PX_ASSERT(desc.constraint != NULL); + } + } + + for (PxU32 i = 0; i < hdr.mStride; ++i) + { + PxsContactManager* cm = cms[i]; + + PxcNpWorkUnit& unit = cm->getWorkUnit(); + unit.frictionDataPtr = blockDescs[i].frictionPtr; + unit.frictionPatchCount = blockDescs[i].frictionCount; + + } + + } + else if (contactDescPtr[startIdx].constraintLengthOver16 == DY_SC_TYPE_RB_1D) + { + SolverConstraintShaderPrepDesc shaderPrepDescs[4]; + PxTGSSolverConstraintPrepDesc prepDescs[4]; + + for (PxU32 a = startIdx, i = 0; a < endIdx; ++a, i++) + { + SolverConstraintShaderPrepDesc& shaderPrepDesc = shaderPrepDescs[i]; + PxTGSSolverConstraintPrepDesc& prepDesc = prepDescs[i]; + + PxTransform id(PxIdentity); + + { + PxTGSSolverConstraintDesc& desc = contactDescPtr[a]; + const Constraint* constraint = reinterpret_cast(desc.constraint); + + const PxConstraintSolverPrep solverPrep = constraint->solverPrep; + const void* constantBlock = constraint->constantBlock; + const PxU32 constantBlockByteSize = constraint->constantBlockSize; + const PxTransform& pose0 = (constraint->body0 ? constraint->body0->getPose() : id); + const PxTransform& pose1 = (constraint->body1 ? constraint->body1->getPose() : id); + const PxTGSSolverBodyVel* sbody0 = desc.bodyA; + const PxTGSSolverBodyVel* sbody1 = desc.bodyB; + + PxTGSSolverBodyTxInertia& txI0 = txInertias[desc.bodyAIdx]; + PxTGSSolverBodyTxInertia& txI1 = txInertias[desc.bodyBIdx]; + + PxStepSolverBodyData& data0 = solverBodyDatas[desc.bodyAIdx]; + PxStepSolverBodyData& data1 = solverBodyDatas[desc.bodyBIdx]; + + shaderPrepDesc.constantBlock = constantBlock; + shaderPrepDesc.constantBlockByteSize = constantBlockByteSize; + shaderPrepDesc.constraint = constraint; + shaderPrepDesc.solverPrep = solverPrep; + + prepDesc.desc = &desc; + prepDesc.bodyFrame0 = pose0; + prepDesc.bodyFrame1 = pose1; + prepDesc.body0 = sbody0; + prepDesc.body1 = sbody1; + prepDesc.body0TxI = &txI0; + prepDesc.body1TxI = &txI1; + prepDesc.bodyData0 = &data0; + prepDesc.bodyData1 = &data1; + prepDesc.linBreakForce = constraint->linBreakForce; + prepDesc.angBreakForce = constraint->angBreakForce; + prepDesc.writeback = &getConstraintWriteBackPool()[constraint->index]; + prepDesc.disablePreprocessing = !!(constraint->flags & PxConstraintFlag::eDISABLE_PREPROCESSING); + prepDesc.improvedSlerp = !!(constraint->flags & PxConstraintFlag::eIMPROVED_SLERP); + prepDesc.driveLimitsAreForces = !!(constraint->flags & PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES); + prepDesc.extendedLimits = !!(constraint->flags & PxConstraintFlag::eENABLE_EXTENDED_LIMITS); + prepDesc.minResponseThreshold = constraint->minResponseThreshold; + + prepDesc.bodyState0 = desc.linkIndexA == PxSolverConstraintDesc::NO_LINK ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eARTICULATION; + prepDesc.bodyState1 = desc.linkIndexB == PxSolverConstraintDesc::NO_LINK ? PxSolverContactDesc::eDYNAMIC_BODY : PxSolverContactDesc::eARTICULATION; + } + } + + SolverConstraintPrepState::Enum buildState = SolverConstraintPrepState::eUNBATCHABLE; + +#if PX_USE_BLOCK_SOLVER +#if PX_USE_BLOCK_1D + if (hdr.mStride == 4) + { + PxU32 totalRows; + buildState = setupSolverConstraintStep4 + (shaderPrepDescs, prepDescs, stepDt, totalDt, invStepDt, invTotalDt, totalRows, blockAllocator, mLengthScale); + } +#endif +#endif + + if (buildState != SolverConstraintPrepState::eSUCCESS) + { + for (PxU32 a = startIdx, i = 0; a < endIdx; ++a, i++) + { + PxReal clampedInvDt = invStepDt; + SetupSolverConstraintStep(shaderPrepDescs[i], prepDescs[i], blockAllocator, stepDt, totalDt, clampedInvDt, invTotalDt, nbSubsteps, mLengthScale); + } + } + } + } +} + +void solveContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime); + +void solve1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxTGSSolverBodyTxInertia* const txInertias, const PxReal elapsedTime); + +void solveExtContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime, SolverContext& cache); + +void solveExt1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime, SolverContext& cache, + const PxTGSSolverBodyTxInertia* const txInertias); + +void solveContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const bool doFriction, + const PxReal minPenetration, const PxReal elapsedTime); + +void solve1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxTGSSolverBodyTxInertia* const txInertias, + const PxReal elapsedTime); + + +void DynamicsTGSContext::solveConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, bool doFriction, + PxReal invStepDt, const PxTGSSolverBodyTxInertia* const solverTxInertia, const PxReal elapsedTime, const PxReal minPenetration, SolverContext& cache) +{ + PX_UNUSED(invStepDt); + PX_UNUSED(solverTxInertia); + + for (PxU32 h = 0; h < nbHeaders; ++h) + { + const PxConstraintBatchHeader& hdr = batchHeaders[h]; + + switch (hdr.mConstraintType) + { + case DY_SC_TYPE_RB_CONTACT: + case DY_SC_TYPE_STATIC_CONTACT: + solveContactBlock(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime); + break; + case DY_SC_TYPE_EXT_CONTACT: + solveExtContactBlock(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime, cache); + break; + case DY_SC_TYPE_RB_1D: + solve1DBlock(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + case DY_SC_TYPE_EXT_1D: + solveExt1DBlock(hdr, contactDescPtr, elapsedTime, cache, solverTxInertia); + break; + case DY_SC_TYPE_BLOCK_RB_CONTACT: + solveContact4(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime); + break; + case DY_SC_TYPE_BLOCK_1D: + solve1D4(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + default: + break; + } + } + +} + +void DynamicsTGSContext::parallelSolveConstraints(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, bool doFriction, + PxTGSSolverBodyTxInertia* solverTxInertia, const PxReal elapsedTime, const PxReal minPenetration, + SolverContext& cache) +{ + PX_UNUSED(solverTxInertia); + for (PxU32 h = 0; h < nbHeaders; ++h) + { + const PxConstraintBatchHeader& hdr = batchHeaders[h]; + + + switch (hdr.mConstraintType) + { + case DY_SC_TYPE_RB_CONTACT: + case DY_SC_TYPE_STATIC_CONTACT: + solveContactBlock(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime); + break; + case DY_SC_TYPE_EXT_CONTACT: + solveExtContactBlock(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime, cache); + break; + case DY_SC_TYPE_RB_1D: + solve1DBlock(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + case DY_SC_TYPE_EXT_1D: + solveExt1DBlock(hdr, contactDescPtr, elapsedTime, cache, solverTxInertia); + break; + case DY_SC_TYPE_BLOCK_RB_CONTACT: + solveContact4(hdr, contactDescPtr, doFriction, minPenetration, elapsedTime); + break; + case DY_SC_TYPE_BLOCK_1D: + solve1D4(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + default: + break; + } + } + +} + + +void writeBackContact(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, SolverContext* cache); + + +void writeBack1D(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc); + +void writeBackContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, SolverContext* cache); + +void writeBack1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc); + + +void DynamicsTGSContext::writebackConstraintsIteration(const PxConstraintBatchHeader* const hdrs, const PxTGSSolverConstraintDesc* const contactDescPtr, const PxU32 nbHeaders) +{ + PX_PROFILE_ZONE("Writeback", 0); + + for (PxU32 h = 0; h < nbHeaders; ++h) + { + const PxConstraintBatchHeader& hdr = hdrs[h]; + + switch (hdr.mConstraintType) + { + case DY_SC_TYPE_RB_CONTACT: + case DY_SC_TYPE_STATIC_CONTACT: + case DY_SC_TYPE_EXT_CONTACT: + writeBackContact(hdr, contactDescPtr, NULL); + break; + case DY_SC_TYPE_RB_1D: + case DY_SC_TYPE_EXT_1D: + writeBack1D(hdr, contactDescPtr); + break; + case DY_SC_TYPE_BLOCK_RB_CONTACT: + writeBackContact4(hdr, contactDescPtr, NULL); + break; + case DY_SC_TYPE_BLOCK_1D: + writeBack1D4(hdr, contactDescPtr); + break; + default: + break; + }; + } +} + +void DynamicsTGSContext::parallelWritebackConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders) +{ + for (PxU32 h = 0; h < nbHeaders; ++h) + { + const PxConstraintBatchHeader& hdr = batchHeaders[h]; + + switch (hdr.mConstraintType) + { + case DY_SC_TYPE_RB_CONTACT: + case DY_SC_TYPE_STATIC_CONTACT: + case DY_SC_TYPE_EXT_CONTACT: + writeBackContact(hdr, contactDescPtr, NULL); + break; + case DY_SC_TYPE_RB_1D: + case DY_SC_TYPE_EXT_1D: + writeBack1D(hdr, contactDescPtr); + break; + case DY_SC_TYPE_BLOCK_RB_CONTACT: + writeBackContact4(hdr, contactDescPtr, NULL); + break; + default: + break; + }; + } +} + +void solveConclude1DBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, + const PxTGSSolverBodyTxInertia* const txInertias, const PxReal elapsedTime); + +void solveConcludeContactBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime); + +void solveConcludeContact4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxReal elapsedTime); + +void solveConclude1D4(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* PX_RESTRICT desc, const PxTGSSolverBodyTxInertia* const txInertias, + const PxReal elapsedTime); + +void solveConcludeContactExtBlock(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, const PxReal elapsedTime, SolverContext& cache); + +void solveConclude1DBlockExt(const PxConstraintBatchHeader& hdr, const PxTGSSolverConstraintDesc* desc, + const PxReal elapsedTime, SolverContext& cache, const PxTGSSolverBodyTxInertia* const txInertias); + +void DynamicsTGSContext::solveConcludeConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, + const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, PxTGSSolverBodyTxInertia* solverTxInertia, + const PxReal elapsedTime, SolverContext& cache) +{ + for (PxU32 h = 0; h < nbHeaders; ++h) + { + const PxConstraintBatchHeader& hdr = batchHeaders[h]; + + switch (hdr.mConstraintType) + { + case DY_SC_TYPE_RB_CONTACT: + case DY_SC_TYPE_STATIC_CONTACT: + solveConcludeContactBlock(hdr, contactDescPtr, elapsedTime); + break; + case DY_SC_TYPE_EXT_CONTACT: + solveConcludeContactExtBlock(hdr, contactDescPtr, elapsedTime, cache); + break; + case DY_SC_TYPE_RB_1D: + solveConclude1DBlock(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + case DY_SC_TYPE_EXT_1D: + solveConclude1DBlockExt(hdr, contactDescPtr, elapsedTime, cache, solverTxInertia); + break; + case DY_SC_TYPE_BLOCK_RB_CONTACT: + solveConcludeContact4(hdr, contactDescPtr, elapsedTime); + break; + case DY_SC_TYPE_BLOCK_1D: + solveConclude1D4(hdr, contactDescPtr, solverTxInertia, elapsedTime); + break; + default: + break; + } + } +} + + +static void integrateCoreStep(PxTGSSolverBodyVel& vel, PxTGSSolverBodyTxInertia& txInertia, const PxStepSolverBodyData& /*bodyData*/, const PxF32 dt) +{ + PxU32 lockFlags = vel.lockFlags; + if (lockFlags) + { + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + vel.linearVelocity.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + vel.linearVelocity.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + vel.linearVelocity.z = 0.f; + + //The angular velocity should be 0 because it is now impossible to make it rotate around that axis! + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + vel.angularVelocity.x = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + vel.angularVelocity.y = 0.f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + vel.angularVelocity.z = 0.f; + } + + PxVec3 linearMotionVel = vel.linearVelocity; + const PxVec3 delta = linearMotionVel * dt; + + + PxVec3 unmolestedAngVel = vel.angularVelocity; + PxVec3 angularMotionVel = txInertia.sqrtInvInertia * vel.angularVelocity; + PxReal w2 = angularMotionVel.magnitudeSquared(); + txInertia.deltaBody2World.p += delta; + PX_ASSERT(txInertia.deltaBody2World.p.isFinite()); + + + // Integrate the rotation using closed form quaternion integrator + if (w2 != 0.0f) + { + PxReal w = PxSqrt(w2); + + //KS - we allow a little bit more angular velocity than the default + //maxAngVel member otherwise the simulation feels a little flimsy + /*const PxReal maxW = PxMax(50.f, vel.maxAngVel); + + if (w > maxW) + { + PxReal ratio = maxW / w; + + vel.angularVelocity *= ratio; + }*/ + + const PxReal v = dt * w * 0.5f; + PxReal s, q; + Ps::sincos(v, s, q); + s /= w; + + const PxVec3 pqr = angularMotionVel * s; + const PxQuat quatVel(pqr.x, pqr.y, pqr.z, 0); + PxQuat result = quatVel * txInertia.deltaBody2World.q; + + result += txInertia.deltaBody2World.q * q; + + txInertia.deltaBody2World.q = result.getNormalized(); + PX_ASSERT(txInertia.deltaBody2World.q.isSane()); + PX_ASSERT(txInertia.deltaBody2World.q.isFinite()); + + //Accumulate the angular rotations in a space we can project the angular constraints to + } + + vel.deltaAngDt += unmolestedAngVel * dt; + vel.deltaLinDt += delta; + + /*solverBodyData.body2World = txInertia.body2World; + solverBodyData.deltaLinDt = vel.deltaLinDt; + solverBodyData.deltaAngDt = vel.deltaAngDt;*/ +} + + +void DynamicsTGSContext::integrateBodies(const SolverIslandObjectsStep& /*objects*/, + const PxU32 count, PxTGSSolverBodyVel* PX_RESTRICT vels, PxTGSSolverBodyTxInertia* PX_RESTRICT txInertias, const PxStepSolverBodyData*const PX_RESTRICT bodyDatas, PxReal dt) +{ + for (PxU32 k = 0; k < count; k++) + { + integrateCoreStep(vels[k+1], txInertias[k+1], bodyDatas[k+1], dt); + } +} + +void DynamicsTGSContext::parallelIntegrateBodies(PxTGSSolverBodyVel* vels, PxTGSSolverBodyTxInertia* txInertias, + const PxStepSolverBodyData* const bodyDatas, const PxU32 count, PxReal dt) +{ + for (PxU32 k = 0; k < count; k++) + { + integrateCoreStep(vels[k+1], txInertias[k+1], bodyDatas[k+1], dt); + } +} + +void DynamicsTGSContext::copyBackBodies(const SolverIslandObjectsStep& objects, + PxTGSSolverBodyVel* vels, PxTGSSolverBodyTxInertia* txInertias, + PxStepSolverBodyData* solverBodyDatas, PxReal invDt, IG::IslandSim& islandSim, + PxU32 startIdx, PxU32 endIdx) +{ + for (PxU32 k = startIdx; k < endIdx; k++) + { + //PxStepSolverBody& solverBodyData = solverBodyData2[k + 1]; + PxTGSSolverBodyVel& solverBodyVel = vels[k + 1]; + PxTGSSolverBodyTxInertia& solverBodyTxI = txInertias[k + 1]; + PxStepSolverBodyData& solverBodyData = solverBodyDatas[k + 1]; + + Cm::SpatialVector motionVel(solverBodyVel.deltaLinDt*invDt, solverBodyTxI.sqrtInvInertia*(solverBodyVel.deltaAngDt*invDt)); + + PxsRigidBody& rBody = *objects.bodies[k]; + PxsBodyCore& core = rBody.getCore(); + rBody.mLastTransform = core.body2World; + core.body2World.q = (solverBodyTxI.deltaBody2World.q * core.body2World.q).getNormalized(); + core.body2World.p = solverBodyTxI.deltaBody2World.p; + core.linearVelocity = (solverBodyVel.linearVelocity); + core.angularVelocity = solverBodyTxI.sqrtInvInertia*(solverBodyVel.angularVelocity); + + + bool hasStaticTouch = islandSim.getIslandStaticTouchCount(IG::NodeIndex(solverBodyData.nodeIndex)) != 0; + sleepCheck(&rBody, mDt, invDt, mEnableStabilization, mUseAdaptiveForce, motionVel, + hasStaticTouch); + } +} + + +void DynamicsTGSContext::stepArticulations(Dy::ThreadContext& threadContext, const PxsIslandIndices& counts, PxReal dt) +{ + for (PxU32 a = 0; a < counts.articulations; ++a) + { + ArticulationSolverDesc& d = threadContext.getArticulations()[a]; + //if(d.articulation->numTotalConstraints > 0) + //d.articulation->solveInternalConstraints(dt, 1.f / dt, threadContext.mZVector.begin(), threadContext.mDeltaV.begin(), false); + ArticulationPImpl::updateDeltaMotion(d, dt, threadContext.mDeltaV.begin()); + + } +} + +void DynamicsTGSContext::updateArticulations(Dy::ThreadContext& threadContext, const PxU32 startIdx, const PxU32 endIdx, PxReal dt) +{ + for (PxU32 a = startIdx; a < endIdx; ++a) + { + ArticulationSolverDesc& d = threadContext.getArticulations()[a]; + ArticulationPImpl::updateBodiesTGS(d, dt); + } +} + +class ArticulationTask : public Cm::Task +{ + Dy::DynamicsTGSContext& mContext; + ArticulationSolverDesc* mDescs; + PxU32 mNbDescs; + PxVec3 mGravity; + PxReal mDt; + + PX_NOCOPY(ArticulationTask) + +public: + static const PxU32 MaxNbPerTask = 4; + + ArticulationTask(Dy::DynamicsTGSContext& context, ArticulationSolverDesc* descs, const PxU32 nbDescs, const PxVec3& gravity, + PxReal dt, PxU64 contextId) : Cm::Task(contextId), mContext(context), + mDescs(descs), mNbDescs(nbDescs), mGravity(gravity), mDt(dt) + { + } + + virtual const char* getName() const { return "ArticulationTask"; } + + virtual void runInternal() + { + PxU32 maxLinks = 0; + for (PxU32 i = 0; i < mNbDescs; i++) + { + maxLinks = PxMax(maxLinks, PxU32(mDescs[i].linkCount)); + } + + Dy::ThreadContext& threadContext = *mContext.getThreadContext(); + + threadContext.mZVector.forceSize_Unsafe(0); + threadContext.mZVector.reserve(maxLinks); + threadContext.mZVector.forceSize_Unsafe(maxLinks); + + threadContext.mDeltaV.forceSize_Unsafe(0); + threadContext.mDeltaV.reserve(maxLinks); + threadContext.mDeltaV.forceSize_Unsafe(maxLinks); + + for (PxU32 a = 0; a < mNbDescs; ++a) + { + + ArticulationPImpl::computeUnconstrainedVelocitiesTGS(mDescs[a], mDt, + mGravity, getContextId(), threadContext.mZVector.begin(), threadContext.mDeltaV.begin()); + } + + mContext.putThreadContext(&threadContext); + } + +}; + + +void DynamicsTGSContext::setupArticulations(IslandContextStep& islandContext, const PxVec3& gravity, const PxReal dt, PxU32& posIters, + PxU32& velIters, PxBaseTask* continuation) +{ + Dy::ArticulationV** articulations = islandContext.mThreadContext->mArticulationArray; + PxU32 nbArticulations = islandContext.mCounts.articulations; + + PxU32 maxVelIters = 0; + PxU32 maxPosIters = 0; + PxU32 maxArticulationLength = 0; + PxU32 maxSolverArticLength = 0; + + //PxU32 startIdx = 0; + for (PxU32 a = 0; a < nbArticulations; a+= ArticulationTask::MaxNbPerTask) + { + const PxU32 endIdx = PxMin(nbArticulations, a + ArticulationTask::MaxNbPerTask); + for (PxU32 b = a; b < endIdx; ++b) + { + ArticulationSolverDesc& desc = islandContext.mThreadContext->getArticulations()[b]; + articulations[b]->getSolverDesc(desc); + maxArticulationLength = PxMax(maxArticulationLength, PxU32(desc.totalDataSize)); + maxSolverArticLength = PxMax(maxSolverArticLength, PxU32(desc.solverDataSize)); + + const PxU16 iterWord = articulations[b]->getIterationCounts(); + maxVelIters = PxMax(PxU32(iterWord >> 8), maxVelIters); + maxPosIters = PxMax(PxU32(iterWord & 0xff), maxPosIters); + } + + Dy::ThreadContext* threadContext = islandContext.mThreadContext; + ArticulationTask* task = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(ArticulationTask)), ArticulationTask)(*this, threadContext->getArticulations().begin() + a, + endIdx - a, gravity, dt, getContextId()); + + task->setContinuation(continuation); + task->removeReference(); + + + + //startIdx += descCount; + + } + + velIters = PxMax(maxVelIters, velIters); + posIters = PxMax(maxPosIters, posIters); + +} + +PxU32 DynamicsTGSContext::setupArticulationInternalConstraints(IslandContextStep& islandContext, PxReal dt, PxReal invStepDt, PxTGSSolverConstraintDesc* constraintDescs) +{ + Dy::ArticulationV** articulations = islandContext.mThreadContext->mArticulationArray; + PxU32 nbArticulations = islandContext.mCounts.articulations; + + ThreadContext* threadContext = getThreadContext(); + threadContext->mConstraintBlockStream.reset(); + + PxU32 startIdx = 0; + PxU32 totalDescCount = 0; + Cm::SpatialVectorF Z[64]; + for (PxU32 a = 0; a < nbArticulations; ++a) + { + ArticulationSolverDesc& desc = islandContext.mThreadContext->getArticulations()[a]; + articulations[a]->getSolverDesc(desc); + + PxU32 acCount; + PxU32 descCount = ArticulationPImpl::setupSolverInternalConstraintsTGS(desc, threadContext->mConstraintBlockStream, constraintDescs+startIdx, + islandContext.mStepDt, invStepDt, dt, acCount, islandContext.mThreadContext->mConstraintBlockManager, Z); + + desc.numInternalConstraints = Ps::to8(descCount); + + totalDescCount += descCount; + + startIdx += DY_ARTICULATION_MAX_SIZE; + } + + putThreadContext(threadContext); + + islandContext.mThreadContext->contactDescArraySize += totalDescCount; + + return totalDescCount; +} + +class SetupDescsTask : public Cm::Task +{ + Dy::IslandContextStep& mIslandContext; + const SolverIslandObjectsStep& mObjects; + IG::SimpleIslandManager& mIslandManager; + PxU32* mBodyRemapTable; + PxU32 mSolverBodyOffset; + PxsContactManagerOutputIterator& mOutputs; + DynamicsTGSContext& mContext; + + PX_NOCOPY(SetupDescsTask) + +public: + + SetupDescsTask(IslandContextStep& islandContext, const SolverIslandObjectsStep& objects, IG::SimpleIslandManager& islandManager, + PxU32* bodyRemapTable, PxU32 solverBodyOffset, PxsContactManagerOutputIterator& outputs, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mObjects(objects), mIslandManager(islandManager), mBodyRemapTable(bodyRemapTable), mSolverBodyOffset(solverBodyOffset), + mOutputs(outputs), mContext(context) + { + } + + virtual const char* getName() const { return "SetupDescsTask"; } + + virtual void runInternal() + { + mContext.setupDescs(mIslandContext, mObjects, mIslandManager, mBodyRemapTable, mSolverBodyOffset, mOutputs); + mIslandContext.mArticulationOffset = mIslandContext.mThreadContext->contactDescArraySize; + } +}; + + +class PreIntegrateParallelTask : public Cm::Task +{ + PxsBodyCore** mBodyArray; + PxsRigidBody** mOriginalBodyArray; + PxTGSSolverBodyVel* mSolverBodyVelPool; + PxTGSSolverBodyTxInertia* mSolverBodyTxInertia; + PxStepSolverBodyData* mSolverBodyDataPool2; + PxU32* mNodeIndexArray; + const PxU32 mBodyCount; + const PxVec3& mGravity; + const PxReal mDt; + PxU32& mPosIters; + PxU32& mVelIters; + DynamicsTGSContext& mContext; + + PX_NOCOPY(PreIntegrateParallelTask) + +public: + + PreIntegrateParallelTask(PxsBodyCore** bodyArray, PxsRigidBody** originalBodyArray, + PxTGSSolverBodyVel* solverBodyVelPool, PxTGSSolverBodyTxInertia* solverBodyTxInertia, PxStepSolverBodyData* solverBodyDataPool2, + PxU32* nodeIndexArray, const PxU32 bodyCount, const PxVec3& gravity, const PxReal dt, PxU32& posIters, PxU32& velIters, + DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mBodyArray(bodyArray), mOriginalBodyArray(originalBodyArray), mSolverBodyVelPool(solverBodyVelPool), + mSolverBodyTxInertia(solverBodyTxInertia), mSolverBodyDataPool2(solverBodyDataPool2), mNodeIndexArray(nodeIndexArray), + mBodyCount(bodyCount), mGravity(gravity), mDt(dt), mPosIters(posIters), mVelIters(velIters), mContext(context) + { + } + + virtual const char* getName() const { return "PreIntegrateParallelTask"; } + + virtual void runInternal() + { + PxU32 posIters = 0; + PxU32 velIters = 0; + mContext.preIntegrateBodies(mBodyArray, mOriginalBodyArray, mSolverBodyVelPool, mSolverBodyTxInertia, mSolverBodyDataPool2, mNodeIndexArray, mBodyCount, mGravity, mDt, posIters, velIters, 0); + + Ps::atomicMax(reinterpret_cast(&mPosIters), PxI32(posIters)); + Ps::atomicMax(reinterpret_cast(&mVelIters), PxI32(velIters)); + + } +}; + + +class PreIntegrateTask : public Cm::Task +{ + PxsBodyCore** mBodyArray; + PxsRigidBody** mOriginalBodyArray; + PxTGSSolverBodyVel* mSolverBodyVelPool; + PxTGSSolverBodyTxInertia* mSolverBodyTxInertia; + PxStepSolverBodyData* mSolverBodyDataPool2; + PxU32* mNodeIndexArray; + const PxU32 mBodyCount; + const PxVec3& mGravity; + const PxReal mDt; + PxU32& mPosIters; + PxU32& mVelIters; + DynamicsTGSContext& mContext; + + PX_NOCOPY(PreIntegrateTask) + +public: + + PreIntegrateTask(PxsBodyCore** bodyArray, PxsRigidBody** originalBodyArray, + PxTGSSolverBodyVel* solverBodyVelPool, PxTGSSolverBodyTxInertia* solverBodyTxInertia, PxStepSolverBodyData* solverBodyDataPool2, + PxU32* nodeIndexArray, const PxU32 bodyCount, const PxVec3& gravity, const PxReal dt, PxU32& posIters, PxU32& velIters, + DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mBodyArray(bodyArray), mOriginalBodyArray(originalBodyArray), mSolverBodyVelPool(solverBodyVelPool), + mSolverBodyTxInertia(solverBodyTxInertia), mSolverBodyDataPool2(solverBodyDataPool2), mNodeIndexArray(nodeIndexArray), + mBodyCount(bodyCount), mGravity(gravity), mDt(dt), mPosIters(posIters), mVelIters(velIters), mContext(context) + { + } + + virtual const char* getName() const { return "PreIntegrateTask"; } + + virtual void runInternal() + { + const PxU32 BodiesPerTask = 512; + + if (mBodyCount <= BodiesPerTask) + { + PxU32 posIters = 0; + PxU32 velIters = 0; + mContext.preIntegrateBodies(mBodyArray, mOriginalBodyArray, mSolverBodyVelPool, mSolverBodyTxInertia, mSolverBodyDataPool2, mNodeIndexArray, mBodyCount, mGravity, mDt, posIters, velIters, 0); + + Ps::atomicMax(reinterpret_cast(&mPosIters), PxI32(posIters)); + Ps::atomicMax(reinterpret_cast(&mVelIters), PxI32(velIters)); + } + else + { + for (PxU32 i = 0; i < mBodyCount; i += BodiesPerTask) + { + const PxU32 nbToProcess = PxMin(mBodyCount - i, BodiesPerTask); + PreIntegrateParallelTask* task = PX_PLACEMENT_NEW(mContext.getTaskPool().allocate(sizeof(PreIntegrateParallelTask)), PreIntegrateParallelTask) + (mBodyArray+i, mOriginalBodyArray+i, mSolverBodyVelPool+i, mSolverBodyTxInertia+i, mSolverBodyDataPool2+i,mNodeIndexArray+i, + nbToProcess, mGravity, mDt, mPosIters, mVelIters, mContext); + + task->setContinuation(mCont); + task->removeReference(); + } + } + + + } +}; + +class SetStepperTask : public Cm::Task +{ + Dy::IslandContextStep& mIslandContext; + Dy::DynamicsTGSContext& mContext; + + PxBaseTask* mAdditionalContinuation; + + PX_NOCOPY(SetStepperTask) + +public: + + SetStepperTask(Dy::IslandContextStep& islandContext, + DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mContext(context), + mAdditionalContinuation(NULL) + { + } + + virtual const char* getName() const { return "SetStepperTask"; } + + void setAdditionalContinuation(PxBaseTask* cont) + { + mAdditionalContinuation = cont; + cont->addReference(); + } + + virtual void runInternal() + { + PxReal dt = mContext.getDt(); + + //ML: TGS can't work well with high velocity iteration counts, so we should limit the velocity iteration counts to be DY_MAX_ITERATION_COUNT. However, + //we should put the extra iterations to the position iteration count so the users will see some behaviour improvements + + const PxU32 newVelIters = PxMin(mIslandContext.mVelIters, PxU32(DY_MAX_VELOCITY_COUNT)); + const PxU32 remainVelIters = mIslandContext.mVelIters - newVelIters; + + mIslandContext.mVelIters = newVelIters; + mIslandContext.mPosIters = mIslandContext.mPosIters + remainVelIters; + + mIslandContext.mStepDt = dt / PxReal(mIslandContext.mPosIters); + mIslandContext.mInvStepDt = 1.f/mIslandContext.mStepDt;//PxMin(1000.f, 1.f / mIslandContext.mStepDt); + } + + virtual void release() + { + Cm::Task::release(); + mAdditionalContinuation->removeReference(); + } +}; + +class SetupArticulationTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + const PxVec3& mGravity; + const PxReal mDt; + PxU32& mPosIters; + PxU32& mVelIters; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(SetupArticulationTask) + +public: + + SetupArticulationTask(IslandContextStep& islandContext, const PxVec3& gravity, const PxReal dt, PxU32& posIters, + PxU32& velIters, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mGravity(gravity), mDt(dt), mPosIters(posIters), mVelIters(velIters), mContext(context) + { + } + + virtual const char* getName() const { return "SetupArticulationTask"; } + + virtual void runInternal() + { + PxU32 posIters = 0, velIters = 0; + mContext.setupArticulations(mIslandContext, mGravity, mDt, posIters, velIters, mCont); + + Ps::atomicMax(reinterpret_cast(&mPosIters), PxI32(posIters)); + Ps::atomicMax(reinterpret_cast(&mVelIters), PxI32(velIters)); + } +}; + +class SetupArticulationInternalConstraintsTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + const PxReal mDt; + const PxReal mInvDt; + PxTGSSolverConstraintDesc* mConstraintDescs; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(SetupArticulationInternalConstraintsTask) + +public: + + SetupArticulationInternalConstraintsTask(IslandContextStep& islandContext, PxReal dt, PxReal invDt, PxTGSSolverConstraintDesc* constraintDescs, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mDt(dt), mInvDt(invDt), mConstraintDescs(constraintDescs), mContext(context) + { + } + + virtual const char* getName() const { return "SetupArticulationInternalConstraintsTask"; } + + virtual void runInternal() + { + mContext.setupArticulationInternalConstraints(mIslandContext, mDt, mIslandContext.mInvStepDt, mConstraintDescs + mIslandContext.mArticulationOffset); + } +}; + +class SetupSolverConstraintsSubTask : public Cm::Task +{ + PxTGSSolverConstraintDesc* mContactDescPtr; + PxConstraintBatchHeader* mHeaders; + const PxU32 mNbHeaders; + PxsContactManagerOutputIterator& mOutputs; + PxReal mStepDt; + PxReal mTotalDt; + PxReal mInvStepDt; + PxReal mInvDtTotal; + PxU32 mNbSubsteps; + DynamicsTGSContext& mContext; + ThreadContext& mIslandThreadContext; + + PX_NOCOPY(SetupSolverConstraintsSubTask) + +public: + + static const PxU32 MaxPerTask = 64; + + SetupSolverConstraintsSubTask(PxTGSSolverConstraintDesc* contactDescPtr, PxConstraintBatchHeader* headers, const PxU32 nbHeaders, + PxsContactManagerOutputIterator& outputs, PxReal stepDt, PxReal totalDt, PxReal invStepDt, PxReal invDtTotal, PxU32 nbSubsteps, ThreadContext& islandThreadContext, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mContactDescPtr(contactDescPtr), mHeaders(headers), mNbHeaders(nbHeaders), mOutputs(outputs), mStepDt(stepDt), mTotalDt(totalDt), mInvStepDt(invStepDt), + mInvDtTotal(invDtTotal), mNbSubsteps(nbSubsteps), mContext(context), mIslandThreadContext(islandThreadContext) + { + } + + virtual const char* getName() const { return "SetupSolverConstraintsSubTask"; } + + virtual void runInternal() + { + ThreadContext* tempContext = mContext.getThreadContext(); + tempContext->mConstraintBlockStream.reset(); + mContext.createSolverConstraints(mContactDescPtr, mHeaders, mNbHeaders, mOutputs, mIslandThreadContext, *tempContext, mStepDt, mTotalDt, mInvStepDt, + mNbSubsteps); + mContext.putThreadContext(tempContext); + } + +}; + +class SetupSolverConstraintsTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + PxTGSSolverConstraintDesc* mContactDescPtr; + PxsContactManagerOutputIterator& mOutputs; + Dy::ThreadContext& mThreadContext; + PxReal mTotalDt; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(SetupSolverConstraintsTask) + +public: + + + + SetupSolverConstraintsTask(IslandContextStep& islandContext, PxTGSSolverConstraintDesc* contactDescPtr, + PxsContactManagerOutputIterator& outputs, Dy::ThreadContext& threadContext, PxReal totalDt, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mContactDescPtr(contactDescPtr), mOutputs(outputs), mThreadContext(threadContext), + mTotalDt(totalDt), mContext(context) + { + } + + virtual const char* getName() const { return "SetupSolverConstraintsTask"; } + + virtual void runInternal() + { + Dy::ThreadContext& threadContext = *mIslandContext.mThreadContext; + const PxU32 nbBatches = threadContext.numContactConstraintBatches; + PxConstraintBatchHeader* hdr = mIslandContext.mObjects.constraintBatchHeaders; + //for (PxU32 a = 0; a < mIslandContext.mArticulationOffset; a += SetupSolverConstraintsSubTask::MaxPerTask) + for (PxU32 a = 0; a < nbBatches; a += SetupSolverConstraintsSubTask::MaxPerTask) + { + const PxU32 nbConstraints = PxMin(nbBatches - a, SetupSolverConstraintsSubTask::MaxPerTask); + SetupSolverConstraintsSubTask* task = PX_PLACEMENT_NEW(mContext.mTaskPool.allocate(sizeof(SetupSolverConstraintsSubTask)), SetupSolverConstraintsSubTask) + (mContactDescPtr, hdr + a, nbConstraints, mOutputs, mIslandContext.mStepDt, mTotalDt, mIslandContext.mInvStepDt, mContext.mInvDt, mIslandContext.mPosIters, mThreadContext, mContext); + + task->setContinuation(mCont); + task->removeReference(); + } + } +}; + +static bool isArticulationConstraint(PxTGSSolverConstraintDesc& desc) +{ + return desc.linkIndexA != PxTGSSolverConstraintDesc::NO_LINK || + desc.linkIndexB != PxTGSSolverConstraintDesc::NO_LINK; +} + + +class PartitionTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + PxTGSSolverConstraintDesc* mContactDescPtr; + PxTGSSolverBodyVel* mSolverBodyData; + Dy::ThreadContext& mThreadContext; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(PartitionTask) + +public: + + + + PartitionTask(IslandContextStep& islandContext, PxTGSSolverConstraintDesc* contactDescPtr, PxTGSSolverBodyVel* solverBodyData, + Dy::ThreadContext& threadContext, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mContactDescPtr(contactDescPtr), mSolverBodyData(solverBodyData), mThreadContext(threadContext), + mContext(context) + { + } + + virtual const char* getName() const { return "PartitionTask"; } + + virtual void runInternal() + { + + ArticulationSolverDesc* artics = mThreadContext.getArticulations().begin(); + if (mIslandContext.mCounts.articulations) + { + PxU32 nbArticConstraints = artics[0].numInternalConstraints + mIslandContext.mArticulationOffset; + + PxTGSSolverConstraintDesc* currDesc = mContactDescPtr; + for (PxU32 a = 1, startIdx = mIslandContext.mArticulationOffset+DY_ARTICULATION_MAX_SIZE; a < mIslandContext.mCounts.articulations; ++a, startIdx += DY_ARTICULATION_MAX_SIZE) + { + //Compact pairs... + const PxU32 nbInternalConstraints = artics[a].numInternalConstraints; + const PxU32 endIdx = startIdx + nbInternalConstraints; + + for (PxU32 b = startIdx; b < endIdx; ++b) + { + currDesc[nbArticConstraints++] = currDesc[b]; + } + } + } + + PxU32 totalDescCount = mThreadContext.contactDescArraySize; + + mThreadContext.mConstraintsPerPartition.forceSize_Unsafe(0); + mThreadContext.mConstraintsPerPartition.resize(1); + mThreadContext.mConstraintsPerPartition[0] = 0; + + TGSConstraintPartitionArgs args; + args.enhancedDeterminism = false; + args.mBodies = mSolverBodyData; + args.mArticulationPtrs = artics; + args.mContactConstraintDescriptors = mContactDescPtr; + args.mNumArticulationPtrs = mThreadContext.getArticulations().size(); + args.mNumBodies = mIslandContext.mCounts.bodies; + args.mNumContactConstraintDescriptors = totalDescCount; + args.mOrderedContactConstraintDescriptors = mIslandContext.mObjects.orderedConstraintDescs; + args.mTempContactConstraintDescriptors = mIslandContext.mObjects.tempConstraintDescs; + args.mNumDifferentBodyConstraints = args.mNumSelfConstraints = args.mNumSelfConstraintBlocks = 0; + args.mConstraintsPerPartition = &mThreadContext.mConstraintsPerPartition; + args.mBitField = &mThreadContext.mPartitionNormalizationBitmap; + + //Partition constraints + mThreadContext.mMaxPartitions = partitionContactConstraintsStep(args); + mThreadContext.mNumDifferentBodyConstraints = args.mNumDifferentBodyConstraints; + mThreadContext.mNumSelfConstraints = args.mNumSelfConstraints; + mThreadContext.mNumSelfConstraintBlocks = args.mNumSelfConstraintBlocks; + + + { + PxU32 descCount = mThreadContext.mNumDifferentBodyConstraints; + PxU32 selfConstraintDescCount = mThreadContext.contactDescArraySize - mThreadContext.mNumDifferentBodyConstraints; + + Ps::Array& accumulatedConstraintsPerPartition = mThreadContext.mConstraintsPerPartition; + + PxU32 numHeaders = 0; + PxU32 currentPartition = 0; + PxU32 maxJ = descCount == 0 ? 0 : accumulatedConstraintsPerPartition[0]; + + const PxU32 maxBatchPartition = 0xFFFFFFFF; + + const PxU32 maxBatchSize = args.enhancedDeterminism ? 1u : 4u; + + PxConstraintBatchHeader* batchHeaders = mIslandContext.mObjects.constraintBatchHeaders; + + PxTGSSolverConstraintDesc* orderedConstraints = mIslandContext.mObjects.orderedConstraintDescs; + + PxU32 headersPerPartition = 0; + for (PxU32 a = 0; a < descCount;) + { + + + PxU32 loopMax = PxMin(maxJ - a, maxBatchSize); + PxU16 j = 0; + if (loopMax > 0) + { + PxConstraintBatchHeader& header = batchHeaders[numHeaders++]; + + j = 1; + PxTGSSolverConstraintDesc& desc = orderedConstraints[a]; + if (!isArticulationConstraint(desc) && (desc.constraintLengthOver16 == DY_SC_TYPE_RB_CONTACT || + desc.constraintLengthOver16 == DY_SC_TYPE_RB_1D) && currentPartition < maxBatchPartition) + { + for (; j < loopMax && desc.constraintLengthOver16 == orderedConstraints[a + j].constraintLengthOver16 && + !isArticulationConstraint(orderedConstraints[a + j]); ++j); + } + header.mStartIndex = a; + header.mStride = j; + header.mConstraintType = desc.constraintLengthOver16; + headersPerPartition++; + } + if (maxJ == (a + j) && maxJ != descCount) + { + //Go to next partition! + accumulatedConstraintsPerPartition[currentPartition] = headersPerPartition; + headersPerPartition = 0; + currentPartition++; + maxJ = accumulatedConstraintsPerPartition[currentPartition]; + } + a += j; + } + if (descCount) + accumulatedConstraintsPerPartition[currentPartition] = headersPerPartition; + + + + accumulatedConstraintsPerPartition.forceSize_Unsafe(mThreadContext.mMaxPartitions); + + PxU32 numDifferentBodyBatchHeaders = numHeaders; + + for (PxU32 a = 0; a < selfConstraintDescCount; ++a) + { + PxConstraintBatchHeader& header = batchHeaders[numHeaders++]; + header.mStartIndex = a + descCount; + header.mStride = 1; + header.mConstraintType = DY_SC_TYPE_EXT_1D; + } + + PxU32 numSelfConstraintBatchHeaders = numHeaders - numDifferentBodyBatchHeaders; + + mThreadContext.numDifferentBodyBatchHeaders = numDifferentBodyBatchHeaders; + mThreadContext.numSelfConstraintBatchHeaders = numSelfConstraintBatchHeaders; + mThreadContext.numContactConstraintBatches = numHeaders; + } + + } +}; + +PX_FORCE_INLINE PxU32 getConstraintLength(const PxTGSSolverConstraintDesc& desc) +{ + return PxU32(desc.constraintLengthOver16 << 4); +} + +class ParallelSolveTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + const SolverIslandObjectsStep& mObjects; + const PxsIslandIndices& mCounts; + ThreadContext& mThreadContext; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(ParallelSolveTask) + +public: + + ParallelSolveTask(IslandContextStep& islandContext, const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& threadContext, + DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mObjects(objects), mCounts(counts), mThreadContext(threadContext), mContext(context) + { + } + + virtual const char* getName() const { return "ParallelSolveTask"; } + + virtual void runInternal() + { + mContext.iterativeSolveIslandParallel(mObjects, mCounts, mThreadContext, mIslandContext.mStepDt, mIslandContext.mPosIters, mIslandContext.mVelIters, + &mIslandContext.mSharedSolverIndex, &mIslandContext.mSharedRigidBodyIndex, &mIslandContext.mSharedArticulationIndex, + &mIslandContext.mSolvedCount, &mIslandContext.mRigidBodyIntegratedCount, &mIslandContext.mArticulationIntegratedCount, + 4, 128); + } +}; + + +class SolveIslandTask : public Cm::Task +{ + IslandContextStep& mIslandContext; + const SolverIslandObjectsStep& mObjects; + const PxsIslandIndices& mCounts; + ThreadContext& mThreadContext; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(SolveIslandTask) + +public: + + SolveIslandTask(IslandContextStep& islandContext, const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& threadContext, + DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mIslandContext(islandContext), mObjects(objects), mCounts(counts), mThreadContext(threadContext), mContext(context) + { + } + + virtual const char* getName() const { return "SolveIslandTask"; } + + virtual void runInternal() + { + + PxU32 j = 0, i = 0; + + PxU32 numBatches = 0; + + PxU32 currIndex = 0; + PxU32 totalCount = 0; + + PxTGSSolverConstraintDesc* contactDescBegin = mObjects.orderedConstraintDescs; + PxTGSSolverConstraintDesc* contactDescPtr = contactDescBegin; + PxConstraintBatchHeader* headers = mObjects.constraintBatchHeaders; + + PxU32 totalPartitions = 0; + for (PxU32 a = 0; a < mThreadContext.mConstraintsPerPartition.size(); ++a) + { + PxU32 endIndex = currIndex + mThreadContext.mConstraintsPerPartition[a]; + + PxU32 numBatchesInPartition = 0; + for (PxU32 b = currIndex; b < endIndex; ++b) + { + PxConstraintBatchHeader& _header = headers[b]; + PxU16 stride = _header.mStride, newStride = _header.mStride; + PxU32 startIndex = j; + for (PxU16 c = 0; c < stride; ++c) + { + if (getConstraintLength(contactDescBegin[i]) == 0) + { + newStride--; + i++; + } + else + { + if (i != j) + contactDescBegin[j] = contactDescBegin[i]; + i++; + j++; + contactDescPtr++; + } + } + + if (newStride != 0) + { + headers[numBatches].mStartIndex = startIndex; + headers[numBatches].mStride = newStride; + PxU8 type = *contactDescBegin[startIndex].constraint; + if (type == DY_SC_TYPE_STATIC_CONTACT) + { + //Check if any block of constraints is classified as type static (single) contact constraint. + //If they are, iterate over all constraints grouped with it and switch to "dynamic" contact constraint + //type if there's a dynamic contact constraint in the group. + for (PxU32 c = 1; c < newStride; ++c) + { + if (*contactDescBegin[startIndex + c].constraint == DY_SC_TYPE_RB_CONTACT) + { + type = DY_SC_TYPE_RB_CONTACT; + } + } + } + + headers[numBatches].mConstraintType = type; + numBatches++; + numBatchesInPartition++; + } + } + currIndex += mThreadContext.mConstraintsPerPartition[a]; + mThreadContext.mConstraintsPerPartition[totalPartitions] = numBatchesInPartition; + if (numBatchesInPartition) + totalPartitions++; + totalCount += numBatchesInPartition; + } + + currIndex = totalCount; + //Decision whether to spawn multi-threaded solver or single-threaded solver... + + mThreadContext.mConstraintsPerPartition.forceSize_Unsafe(totalPartitions); + mThreadContext.numContactConstraintBatches = totalCount; + + PxU32 maxLinks = 0; + for (PxU32 a = 0; a < mCounts.articulations; ++a) + { + ArticulationSolverDesc& desc = mThreadContext.getArticulations()[a]; + maxLinks = PxMax(maxLinks, PxU32(desc.linkCount)); + } + + mThreadContext.mZVector.forceSize_Unsafe(0); + mThreadContext.mZVector.reserve(maxLinks); + mThreadContext.mZVector.forceSize_Unsafe(maxLinks); + + mThreadContext.mDeltaV.forceSize_Unsafe(0); + mThreadContext.mDeltaV.reserve(maxLinks); + mThreadContext.mDeltaV.forceSize_Unsafe(maxLinks); + + SolverContext cache; + cache.Z = mThreadContext.mZVector.begin(); + cache.deltaV = mThreadContext.mDeltaV.begin(); + + if (mThreadContext.mConstraintsPerPartition.size()) + { + const PxU32 threadCount = this->getTaskManager()->getCpuDispatcher()->getWorkerCount(); + + const PxU32 nbHeadersPerPartition = ((currIndex + mThreadContext.mConstraintsPerPartition.size() - 1) / mThreadContext.mConstraintsPerPartition.size()); + + const PxU32 NbBatchesPerThread = 8; + + const PxU32 nbIdealThreads = (nbHeadersPerPartition + NbBatchesPerThread-1) / NbBatchesPerThread; + + if (threadCount < 2 || nbIdealThreads < 2) + mContext.iterativeSolveIsland(mObjects, mCounts, mThreadContext, mIslandContext.mStepDt, mIslandContext.mInvStepDt, + mIslandContext.mPosIters, mIslandContext.mVelIters, cache); + else + { + + mIslandContext.mSharedSolverIndex = 0; + mIslandContext.mSolvedCount = 0; + mIslandContext.mSharedRigidBodyIndex = 0; + mIslandContext.mRigidBodyIntegratedCount = 0; + mIslandContext.mSharedArticulationIndex = 0; + mIslandContext.mArticulationIntegratedCount = 0; + + PxU32 nbThreads = PxMin(threadCount, nbIdealThreads); + + ParallelSolveTask* tasks = reinterpret_cast(mContext.getTaskPool().allocate(sizeof(ParallelSolveTask)*nbThreads)); + + for (PxU32 a = 0; a < nbThreads; ++a) + { + PX_PLACEMENT_NEW(&tasks[a], ParallelSolveTask)(mIslandContext, mObjects, mCounts, mThreadContext, mContext); + tasks[a].setContinuation(mCont); + tasks[a].removeReference(); + } + } + } + else + { + mContext.iterativeSolveIsland(mObjects, mCounts, mThreadContext, mIslandContext.mStepDt, + mIslandContext.mInvStepDt, mIslandContext.mPosIters, mIslandContext.mVelIters, cache); + } + } +}; + +class EndIslandTask : public Cm::Task +{ + ThreadContext& mThreadContext; + DynamicsTGSContext& mContext; + + PX_NOCOPY(EndIslandTask) + +public: + + EndIslandTask(ThreadContext& threadContext, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mThreadContext(threadContext), mContext(context) + { + } + + virtual const char* getName() const { return "EndIslandTask"; } + + virtual void runInternal() + { + mContext.endIsland(mThreadContext); + } +}; + +class FinishSolveIslandTask : public Cm::Task +{ + ThreadContext& mThreadContext; + const SolverIslandObjectsStep& mObjects; + const PxsIslandIndices& mCounts; + IG::SimpleIslandManager& mIslandManager; + + DynamicsTGSContext& mContext; + + PX_NOCOPY(FinishSolveIslandTask) + +public: + + FinishSolveIslandTask(ThreadContext& threadContext, const SolverIslandObjectsStep& objects, + const PxsIslandIndices& counts, IG::SimpleIslandManager& islandManager, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mThreadContext(threadContext), mObjects(objects), mCounts(counts), mIslandManager(islandManager), mContext(context) + { + } + + virtual const char* getName() const { return "FinishSolveIslandTask"; } + + virtual void runInternal() + { + mContext.finishSolveIsland(mThreadContext, mObjects, mCounts, mIslandManager, mCont); + } +}; + + + +void DynamicsTGSContext::iterativeSolveIsland(const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& mThreadContext, + const PxReal stepDt, const PxReal invStepDt, const PxU32 posIters, const PxU32 velIters, SolverContext& cache) +{ + PX_PROFILE_ZONE("Dynamics:solveIsland", 0); + PxReal elapsedTime = 0.f; + PxReal recipStepDt = 1.f/stepDt; + + const PxU32 bodyOffset = objects.solverBodyOffset; + + if (mThreadContext.numContactConstraintBatches == 0) + { + for (PxU32 i = 0; i < counts.articulations; ++i) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[i]; + for (PxU32 a = 1; a < posIters; a++) + { + d.articulation->solveInternalConstraints(stepDt, recipStepDt, mThreadContext.mZVector.begin(), mThreadContext.mDeltaV.begin(), false); + stepArticulations(mThreadContext, counts, stepDt); + } + + ArticulationPImpl::saveVelocityTGS(d, mInvDt); + + for (PxU32 a = 0; a < velIters; ++a) + { + d.articulation->solveInternalConstraints(stepDt, recipStepDt, mThreadContext.mZVector.begin(), mThreadContext.mDeltaV.begin(), true); + } + } + + integrateBodies(objects, counts.bodies, mSolverBodyVelPool.begin() + bodyOffset, mSolverBodyTxInertiaPool.begin() + bodyOffset, mSolverBodyDataPool2.begin() + bodyOffset, mDt); + return; + } + + + for (PxU32 a = 1; a < posIters; a++) + { + bool doFriction = true; + + for (PxU32 i = 0; i < counts.articulations; ++i) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[i]; + d.articulation->solveInternalConstraints(stepDt, recipStepDt, mThreadContext.mZVector.begin(), mThreadContext.mDeltaV.begin(), false); + } + + solveConstraintsIteration(objects.orderedConstraintDescs, objects.constraintBatchHeaders, mThreadContext.numContactConstraintBatches, doFriction, invStepDt, + mSolverBodyTxInertiaPool.begin(), elapsedTime, -PX_MAX_F32, cache); + integrateBodies(objects, counts.bodies, mSolverBodyVelPool.begin() + bodyOffset, mSolverBodyTxInertiaPool.begin() + bodyOffset, mSolverBodyDataPool2.begin() + bodyOffset, stepDt); + + stepArticulations(mThreadContext, counts, stepDt); + elapsedTime += stepDt; + } + + for (PxU32 i = 0; i < counts.articulations; ++i) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[i]; + d.articulation->solveInternalConstraints(stepDt, recipStepDt, mThreadContext.mZVector.begin(), mThreadContext.mDeltaV.begin(), false); + } + + solveConcludeConstraintsIteration(objects.orderedConstraintDescs, objects.constraintBatchHeaders, mThreadContext.numContactConstraintBatches, + mSolverBodyTxInertiaPool.begin(), elapsedTime, cache); + elapsedTime += stepDt; + + const PxReal invDt = mInvDt; + + integrateBodies(objects, counts.bodies, mSolverBodyVelPool.begin() + bodyOffset, mSolverBodyTxInertiaPool.begin() + bodyOffset, mSolverBodyDataPool2.begin() + bodyOffset, stepDt); + + stepArticulations(mThreadContext, counts, stepDt); + + for(PxU32 a = 0; a < counts.articulations; ++a) + { + Dy::ArticulationSolverDesc& desc = mThreadContext.getArticulations()[a]; + + //ArticulationPImpl::updateDeltaMotion(desc, stepDt, mThreadContext.mDeltaV.begin()); + ArticulationPImpl::saveVelocityTGS(desc, invDt); + } + + for (PxU32 a = 0; a < velIters; ++a) + { + for (PxU32 i = 0; i < counts.articulations; ++i) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[i]; + d.articulation->solveInternalConstraints(stepDt, recipStepDt, mThreadContext.mZVector.begin(), mThreadContext.mDeltaV.begin(), false); + } + bool doFriction = true; + solveConstraintsIteration(objects.orderedConstraintDescs, objects.constraintBatchHeaders, mThreadContext.numContactConstraintBatches, doFriction, invStepDt, + mSolverBodyTxInertiaPool.begin(), elapsedTime, 0.f, cache); + } + + writebackConstraintsIteration(objects.constraintBatchHeaders, objects.orderedConstraintDescs, mThreadContext.numContactConstraintBatches); +} + + +void DynamicsTGSContext::iterativeSolveIslandParallel(const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& mThreadContext, + const PxReal stepDt, const PxU32 posIters, const PxU32 velIters, PxI32* solverCounts, PxI32* integrationCounts, PxI32* articulationIntegrationCounts, + PxI32* solverProgressCount, PxI32* integrationProgressCount, PxI32* articulationProgressCount, PxU32 solverUnrollSize, PxU32 integrationUnrollSize) +{ + PX_PROFILE_ZONE("Dynamics:solveIslandParallel", 0); + Dy::ThreadContext& threadContext = *getThreadContext(); + PxU32 startSolveIdx = PxU32(Ps::atomicAdd(solverCounts, PxI32(solverUnrollSize))) - solverUnrollSize; + PxU32 nbSolveRemaining = solverUnrollSize; + + PxU32 startIntegrateIdx = PxU32(Ps::atomicAdd(integrationCounts, PxI32(integrationUnrollSize))) - integrationUnrollSize; + PxU32 nbIntegrateRemaining = integrationUnrollSize; + + //For now, just do articulations 1 at a time. Might need to tweak this later depending on performance + PxU32 startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + + PxU32 targetSolverProgressCount = 0, targetIntegrationProgressCount = 0, targetArticulationProgressCount = 0; + + const PxU32 nbSolverBatches = mThreadContext.numContactConstraintBatches; + const PxU32 nbBodies = counts.bodies;// + mKinematicCount; + const PxU32 nbArticulations = counts.articulations; + + PxTGSSolverConstraintDesc* contactDescs = objects.orderedConstraintDescs; + PxConstraintBatchHeader* batchHeaders = objects.constraintBatchHeaders; + + PxTGSSolverBodyVel* solverVels = mSolverBodyVelPool.begin(); + PxTGSSolverBodyTxInertia* solverTxInertias = mSolverBodyTxInertiaPool.begin(); + const PxStepSolverBodyData*const solverBodyData = mSolverBodyDataPool2.begin(); + + PxU32* constraintsPerPartitions = mThreadContext.mConstraintsPerPartition.begin(); + const PxU32 nbPartitions = mThreadContext.mConstraintsPerPartition.size(); + + const PxU32 bodyOffset = objects.solverBodyOffset; + + SolverContext cache; + cache.Z = threadContext.mZVector.begin(); + cache.deltaV = threadContext.mDeltaV.begin(); + + PxReal elapsedTime = 0.f; + + PxReal invStepDt = 1.f/ stepDt; + + for (PxU32 a = 1; a < posIters; ++a, targetIntegrationProgressCount += nbBodies, targetArticulationProgressCount += nbArticulations) + { + WAIT_FOR_PROGRESS(integrationProgressCount, PxI32(targetIntegrationProgressCount)); + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + PxU32 artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + + PxU32 nbArticsProcessed = 0; + while (artIcStartIdx < nbArticulations) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[artIcStartIdx]; + + d.articulation->solveInternalConstraints(stepDt, invStepDt, threadContext.mZVector.begin(), + threadContext.mDeltaV.begin(), false); + ArticulationPImpl::updateDeltaMotion(d, stepDt, cache.deltaV); + + nbArticsProcessed++; + + startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + } + + if (nbArticsProcessed) + Ps::atomicAdd(articulationProgressCount, PxI32(nbArticsProcessed)); + + targetArticulationProgressCount += nbArticulations; + + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + + PxU32 offset = 0; + for (PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + //Find the startIdx in the partition to process + PxU32 startIdx = startSolveIdx - targetSolverProgressCount; + + const PxU32 nbBatches = constraintsPerPartitions[b]; + + PxU32 nbSolved = 0; + while (startIdx < nbBatches) + { + PxU32 nbToSolve = PxMin(nbBatches - startIdx, nbSolveRemaining); + parallelSolveConstraints(contactDescs, batchHeaders + startIdx + offset, nbToSolve, true, + solverTxInertias, elapsedTime, -PX_MAX_F32, cache); + nbSolveRemaining -= nbToSolve; + startSolveIdx += nbToSolve; + startIdx += nbToSolve; + + nbSolved += nbToSolve; + + if (nbSolveRemaining == 0) + { + startSolveIdx = PxU32(Ps::atomicAdd(solverCounts, PxI32(solverUnrollSize))) - solverUnrollSize; + nbSolveRemaining = solverUnrollSize; + startIdx = startSolveIdx - targetSolverProgressCount; + } + } + + if (nbSolved) + Ps::atomicAdd(solverProgressCount, PxI32(nbSolved)); + + targetSolverProgressCount += nbBatches; + offset += nbBatches; + } + + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + + + PxU32 integStartIdx = startIntegrateIdx - targetIntegrationProgressCount; + + PxU32 nbIntegrated = 0; + while (integStartIdx < nbBodies) + { + PxU32 nbToIntegrate = PxMin(nbBodies - integStartIdx, nbIntegrateRemaining); + + parallelIntegrateBodies(solverVels + integStartIdx + bodyOffset, solverTxInertias + integStartIdx + bodyOffset, + solverBodyData + integStartIdx + bodyOffset, nbToIntegrate, stepDt); + + nbIntegrateRemaining -= nbToIntegrate; + startIntegrateIdx += nbToIntegrate; + integStartIdx += nbToIntegrate; + + nbIntegrated += nbToIntegrate; + + if (nbIntegrateRemaining == 0) + { + startIntegrateIdx = PxU32(Ps::atomicAdd(integrationCounts, PxI32(integrationUnrollSize))) - integrationUnrollSize; + nbIntegrateRemaining = integrationUnrollSize; + integStartIdx = startIntegrateIdx - targetIntegrationProgressCount; + } + } + + if (nbIntegrated) + Ps::atomicAdd(integrationProgressCount, PxI32(nbIntegrated)); + + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + + nbArticsProcessed = 0; + while (artIcStartIdx < nbArticulations) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[artIcStartIdx]; + ArticulationPImpl::updateDeltaMotion(d, stepDt, cache.deltaV); + + nbArticsProcessed++; + + startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + } + + if (nbArticsProcessed) + Ps::atomicAdd(articulationProgressCount, PxI32(nbArticsProcessed)); + + elapsedTime += stepDt; + + } + + { + WAIT_FOR_PROGRESS(integrationProgressCount, PxI32(targetIntegrationProgressCount)); + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + PxU32 artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + + PxU32 nbArticsProcessed = 0; + while (artIcStartIdx < nbArticulations) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[artIcStartIdx]; + + d.articulation->solveInternalConstraints(stepDt, invStepDt, threadContext.mZVector.begin(), + threadContext.mDeltaV.begin(), false); + ArticulationPImpl::updateDeltaMotion(d, stepDt, cache.deltaV); + + nbArticsProcessed++; + + startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + } + + if (nbArticsProcessed) + Ps::atomicAdd(articulationProgressCount, PxI32(nbArticsProcessed)); + + targetArticulationProgressCount += nbArticulations; + + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + + PxU32 offset = 0; + for (PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + //Find the startIdx in the partition to process + PxU32 startIdx = startSolveIdx - targetSolverProgressCount; + + const PxU32 nbBatches = constraintsPerPartitions[b]; + + PxU32 nbSolved = 0; + while (startIdx < nbBatches) + { + PxU32 nbToSolve = PxMin(nbBatches - startIdx, nbSolveRemaining); + solveConcludeConstraintsIteration(contactDescs, batchHeaders + startIdx + offset, nbToSolve, + solverTxInertias, elapsedTime, cache); + nbSolveRemaining -= nbToSolve; + startSolveIdx += nbToSolve; + startIdx += nbToSolve; + + nbSolved += nbToSolve; + + if (nbSolveRemaining == 0) + { + startSolveIdx = PxU32(Ps::atomicAdd(solverCounts, PxI32(solverUnrollSize))) - solverUnrollSize; + nbSolveRemaining = solverUnrollSize; + startIdx = startSolveIdx - targetSolverProgressCount; + } + } + + if (nbSolved) + Ps::atomicAdd(solverProgressCount, PxI32(nbSolved)); + + targetSolverProgressCount += nbBatches; + offset += nbBatches; + + + } + + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + + + const PxReal invDt = mInvDt; + //const PxReal invDtPt25 = mInvDt * 0.25f; + PxU32 integStartIdx = startIntegrateIdx - targetIntegrationProgressCount; + + PxU32 nbIntegrated = 0; + while (integStartIdx < nbBodies) + { + PxU32 nbToIntegrate = PxMin(nbBodies - integStartIdx, nbIntegrateRemaining); + + parallelIntegrateBodies(solverVels + integStartIdx + bodyOffset, solverTxInertias + integStartIdx + bodyOffset, + solverBodyData + integStartIdx + bodyOffset, nbToIntegrate, stepDt); + + nbIntegrateRemaining -= nbToIntegrate; + startIntegrateIdx += nbToIntegrate; + integStartIdx += nbToIntegrate; + + nbIntegrated += nbToIntegrate; + + if (nbIntegrateRemaining == 0) + { + startIntegrateIdx = PxU32(Ps::atomicAdd(integrationCounts, PxI32(integrationUnrollSize))) - integrationUnrollSize; + nbIntegrateRemaining = integrationUnrollSize; + integStartIdx = startIntegrateIdx - targetIntegrationProgressCount; + } + } + + if (nbIntegrated) + Ps::atomicAdd(integrationProgressCount, PxI32(nbIntegrated)); + + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + + nbArticsProcessed = 0; + while (artIcStartIdx < nbArticulations) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[artIcStartIdx]; + ArticulationPImpl::updateDeltaMotion(d, stepDt, cache.deltaV); + ArticulationPImpl::saveVelocityTGS(d, invDt); + + nbArticsProcessed++; + + startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + } + + if (nbArticsProcessed) + Ps::atomicAdd(articulationProgressCount, PxI32(nbArticsProcessed)); + + elapsedTime += stepDt; + + targetIntegrationProgressCount += nbBodies; + targetArticulationProgressCount += nbArticulations; + + putThreadContext(&threadContext); + } + + //Write back constraints... + + WAIT_FOR_PROGRESS(integrationProgressCount, PxI32(targetIntegrationProgressCount)); + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + + for (PxU32 a = 0; a < velIters; ++a) + { + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + + PxU32 artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + + PxU32 nbArticsProcessed = 0; + while (artIcStartIdx < nbArticulations) + { + ArticulationSolverDesc& d = mThreadContext.getArticulations()[artIcStartIdx]; + + d.articulation->solveInternalConstraints(stepDt, invStepDt, threadContext.mZVector.begin(), + threadContext.mDeltaV.begin(), false); + ArticulationPImpl::updateDeltaMotion(d, stepDt, cache.deltaV); + + nbArticsProcessed++; + + startArticulationIdx = PxU32(Ps::atomicAdd(articulationIntegrationCounts, PxI32(1))) - 1; + artIcStartIdx = startArticulationIdx - targetArticulationProgressCount; + } + + if (nbArticsProcessed) + Ps::atomicAdd(articulationProgressCount, PxI32(nbArticsProcessed)); + + targetArticulationProgressCount += nbArticulations; + + WAIT_FOR_PROGRESS(articulationProgressCount, PxI32(targetArticulationProgressCount)); + + PxU32 offset = 0; + for (PxU32 b = 0; b < nbPartitions; ++b) + { + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + + //Find the startIdx in the partition to process + PxU32 startIdx = startSolveIdx - targetSolverProgressCount; + + const PxU32 nbBatches = constraintsPerPartitions[b]; + + PxU32 nbSolved = 0; + while (startIdx < nbBatches) + { + PxU32 nbToSolve = PxMin(nbBatches - startIdx, nbSolveRemaining); + parallelSolveConstraints(contactDescs, batchHeaders + startIdx + offset, nbToSolve, true, + solverTxInertias, elapsedTime, 0.f, cache); + nbSolveRemaining -= nbToSolve; + startSolveIdx += nbToSolve; + startIdx += nbToSolve; + + nbSolved += nbToSolve; + + + if (nbSolveRemaining == 0) + { + startSolveIdx = PxU32(Ps::atomicAdd(solverCounts, PxI32(solverUnrollSize))) - solverUnrollSize; + nbSolveRemaining = solverUnrollSize; + + startIdx = startSolveIdx - targetSolverProgressCount; + } + } + + if (nbSolved) + Ps::atomicAdd(solverProgressCount, PxI32(nbSolved)); + + targetSolverProgressCount += nbBatches; + offset += nbBatches; + } + } + + { + WAIT_FOR_PROGRESS(solverProgressCount, PxI32(targetSolverProgressCount)); + { + //Find the startIdx in the partition to process + PxU32 startIdx = startSolveIdx - targetSolverProgressCount; + + const PxU32 nbBatches = nbSolverBatches; + + PxU32 nbSolved = 0; + while (startIdx < nbBatches) + { + PxU32 nbToSolve = PxMin(nbBatches - startIdx, nbSolveRemaining); + parallelWritebackConstraintsIteration(contactDescs, batchHeaders + startIdx, nbToSolve); + nbSolveRemaining -= nbToSolve; + startSolveIdx += nbToSolve; + startIdx += nbToSolve; + + nbSolved += nbToSolve; + + if (nbSolveRemaining == 0) + { + startSolveIdx = PxU32(Ps::atomicAdd(solverCounts, PxI32(solverUnrollSize))) - solverUnrollSize; + nbSolveRemaining = solverUnrollSize; + startIdx = startSolveIdx - targetSolverProgressCount; + } + } + + if (nbSolved) + Ps::atomicAdd(solverProgressCount, PxI32(nbSolved)); + + targetSolverProgressCount += nbBatches; + } + + } + +} + +class CopyBackTask : public Cm::Task +{ + const SolverIslandObjectsStep& mObjects; + PxTGSSolverBodyVel* mVels; + PxTGSSolverBodyTxInertia* mTxInertias; + PxStepSolverBodyData* mSolverBodyDatas; + const PxReal mInvDt; + IG::IslandSim& mIslandSim; + const PxU32 mStartIdx; + const PxU32 mEndIdx; + DynamicsTGSContext& mContext; + + PX_NOCOPY(CopyBackTask) + +public: + + CopyBackTask(const SolverIslandObjectsStep& objects, + PxTGSSolverBodyVel* vels, PxTGSSolverBodyTxInertia* txInertias, + PxStepSolverBodyData* solverBodyDatas, PxReal invDt, IG::IslandSim& islandSim, + PxU32 startIdx, PxU32 endIdx, DynamicsTGSContext& context) : Cm::Task(context.getContextId()), + mObjects(objects), mVels(vels), mTxInertias(txInertias), mSolverBodyDatas(solverBodyDatas), mInvDt(invDt), + mIslandSim(islandSim), mStartIdx(startIdx), mEndIdx(endIdx), mContext(context) + { + } + + virtual const char* getName() const { return "CopyBackTask"; } + + virtual void runInternal() + { + mContext.copyBackBodies(mObjects, mVels, mTxInertias, mSolverBodyDatas, mInvDt, mIslandSim, mStartIdx, mEndIdx); + } +}; + +class UpdateArticTask : public Cm::Task +{ + Dy::ThreadContext& mThreadContext; + PxU32 mStartIdx; + PxU32 mEndIdx; + PxReal mDt; + DynamicsTGSContext& mContext; + + PX_NOCOPY(UpdateArticTask) + +public: + + UpdateArticTask(Dy::ThreadContext& threadContext, PxU32 startIdx, PxU32 endIdx, PxReal dt, DynamicsTGSContext& context) : + Cm::Task(context.getContextId()), + mThreadContext(threadContext), mStartIdx(startIdx), mEndIdx(endIdx), mDt(dt), mContext(context) + { + } + + virtual const char* getName() const { return "UpdateArticTask"; } + + virtual void runInternal() + { + mContext.updateArticulations(mThreadContext, mStartIdx, mEndIdx, mDt); + } +}; + + +void DynamicsTGSContext::finishSolveIsland(ThreadContext& mThreadContext, const SolverIslandObjectsStep& objects, + const PxsIslandIndices& counts, IG::SimpleIslandManager& islandManager, PxBaseTask* continuation) +{ + mThreadContext.mConstraintBlockManager.reset(); + mThreadContext.mConstraintBlockStream.reset(); + + const PxU32 NbBodiesPerTask = 512; + + for (PxU32 a = 0; a < counts.bodies; a += NbBodiesPerTask) + { + CopyBackTask* task = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(CopyBackTask)), CopyBackTask) + (objects, mSolverBodyVelPool.begin() + objects.solverBodyOffset, + mSolverBodyTxInertiaPool.begin() + objects.solverBodyOffset, mSolverBodyDataPool2.begin() + objects.solverBodyOffset, + mInvDt, islandManager.getAccurateIslandSim(), a, PxMin(a + NbBodiesPerTask, counts.bodies),*this); + + task->setContinuation(continuation); + task->removeReference(); + } + + const PxU32 NbArticsPerTask = 2; + + for (PxU32 a = 0; a < counts.articulations; a += NbArticsPerTask) + { + UpdateArticTask* task = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(UpdateArticTask)), UpdateArticTask) + (mThreadContext, a, PxMin(counts.articulations, a+NbArticsPerTask), mDt, *this); + + task->setContinuation(continuation); + task->removeReference(); + } +} + + +void DynamicsTGSContext::endIsland(ThreadContext& mThreadContext) +{ + putThreadContext(&mThreadContext); +} + + +void DynamicsTGSContext::solveIsland(const SolverIslandObjectsStep& objects, + const PxsIslandIndices& counts, + const PxU32 solverBodyOffset, + IG::SimpleIslandManager& islandManager, + PxU32* bodyRemapTable, PxsMaterialManager* materialManager, + PxsContactManagerOutputIterator& iterator, + PxBaseTask* continuation) +{ + PX_UNUSED(continuation); + + PX_UNUSED(iterator); + PX_UNUSED(materialManager); + PX_UNUSED(bodyRemapTable); + PX_UNUSED(islandManager); + + ThreadContext& mThreadContext = *getThreadContext(); + + IslandContextStep& islandContext = *reinterpret_cast(mTaskPool.allocate(sizeof(IslandContextStep))); + islandContext.mThreadContext = &mThreadContext; + islandContext.mCounts = counts; + islandContext.mObjects = objects; + islandContext.mPosIters = 0; + islandContext.mVelIters = 0; + islandContext.mObjects.solverBodyOffset = solverBodyOffset; + + + prepareBodiesAndConstraints(islandContext.mObjects, islandManager, islandContext); + + ////Create task chain... + SetupDescsTask* descTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SetupDescsTask)), SetupDescsTask)(islandContext, islandContext.mObjects, islandManager, + bodyRemapTable, solverBodyOffset, iterator, *this); + PreIntegrateTask* intTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(PreIntegrateTask)), PreIntegrateTask)(islandContext.mObjects.bodyCoreArray, islandContext.mObjects.bodies, + mSolverBodyVelPool.begin() + solverBodyOffset, + mSolverBodyTxInertiaPool.begin() + solverBodyOffset, mSolverBodyDataPool2.begin() + solverBodyOffset, + mThreadContext.mNodeIndexArray, islandContext.mCounts.bodies, mGravity, mDt, islandContext.mPosIters, + islandContext.mVelIters, *this); + SetupArticulationTask* articTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SetupArticulationTask)), SetupArticulationTask)(islandContext, mGravity, + mDt, islandContext.mPosIters, islandContext.mVelIters, *this); + SetStepperTask* stepperTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SetStepperTask)), SetStepperTask)(islandContext, *this); + SetupArticulationInternalConstraintsTask* articConTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SetupArticulationInternalConstraintsTask)), SetupArticulationInternalConstraintsTask) + (islandContext, mDt, mInvDt, islandContext.mObjects.constraintDescs, *this); + PartitionTask* partitionTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(PartitionTask)), PartitionTask) + (islandContext, islandContext.mObjects.constraintDescs, mSolverBodyVelPool.begin()+solverBodyOffset+1, mThreadContext, + *this); + SetupSolverConstraintsTask* constraintTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SetupSolverConstraintsTask)), SetupSolverConstraintsTask) + (islandContext, islandContext.mObjects.orderedConstraintDescs, iterator, mThreadContext, + mDt, *this); + + SolveIslandTask* solveTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(SolveIslandTask)), SolveIslandTask)(islandContext, islandContext.mObjects, islandContext.mCounts, mThreadContext, *this); + + FinishSolveIslandTask* finishTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(FinishSolveIslandTask)), FinishSolveIslandTask)(mThreadContext, islandContext.mObjects, islandContext.mCounts, islandManager, *this); + + EndIslandTask* endTask = PX_PLACEMENT_NEW(mTaskPool.allocate(sizeof(EndIslandTask)), EndIslandTask)(mThreadContext, *this); + + endTask->setContinuation(continuation); + finishTask->setContinuation(endTask); + solveTask->setContinuation(finishTask); + constraintTask->setContinuation(solveTask); + partitionTask->setContinuation(constraintTask); + articConTask->setContinuation(partitionTask); + //Stepper triggers both articCon and constraintTask + stepperTask->setContinuation(articConTask); + stepperTask->setAdditionalContinuation(constraintTask); + + articTask->setContinuation(stepperTask); + intTask->setContinuation(stepperTask); + descTask->setContinuation(stepperTask); + + endTask->removeReference(); + finishTask->removeReference(); + solveTask->removeReference(); + constraintTask->removeReference(); + partitionTask->removeReference(); + articConTask->removeReference(); + stepperTask->removeReference(); + articTask->removeReference(); + intTask->removeReference(); + descTask->removeReference(); + + + //Launch a setupDescs task + //setupDescs(islandContext, objects, islandManager, bodyRemapTable, mKinematicCount, iterator); + // + ////Launch a preIntegrateBodies task + //preIntegrateBodies(objects.bodyCoreArray, objects.bodies, mThreadContext.solverBodyData.begin() + mKinematicCount, + // mThreadContext.mNodeIndexArray, counts.bodies, mGravity, mDt, islandContext.mPosIters, islandContext.mVelIters, 0); + + //const PxU32 articConstraintOffset = mThreadContext.contactDescArraySize; + //islandContext.mArticulationOffset = mThreadContext.contactDescArraySize; + + ////Launch an articulation setup task + //setupArticulations(islandContext, mGravity, mDt, islandContext.mPosIters, islandContext.mVelIters); + + //PxU32 substeps = islandContext.mPosIters; + + //PxReal stepDt = mDt / PxReal(substeps); + //PxReal invStepDt = PxMin(1000.f, mInvDt*PxReal(substeps)); + + //islandContext.mStepDt = stepDt; + //islandContext.mInvStepDt = invStepDt; + + ////Setup articulation constraints - needs to be refactored for compaction etc. + //setupArticulationInternalConstraints(islandContext, mDt, invStepDt, objects.constraintDescs + articConstraintOffset); + + //createSolverConstraints(objects.constraintDescs, articConstraintOffset, mThreadContext.solverBodyData.begin(), iterator, mThreadContext, + // mDt, invStepDt, substeps); + + //iterativeSolveIsland(objects, counts, mThreadContext, stepDt, invStepDt, islandContext.mPosIters, islandContext.mVelIters); + + //endIsland(mThreadContext, objects, counts, islandManager); + +} + +void DynamicsTGSContext::updateBodyCore(PxBaseTask* continuation) +{ + PX_UNUSED(continuation); +} + +void DynamicsTGSContext::mergeResults() +{ +} + + + +} +} + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h new file mode 100644 index 000000000..6d2754074 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h @@ -0,0 +1,637 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_TGS_DYNAMICS_H +#define DY_TGS_DYNAMICS_H + +#include "PxvConfig.h" +#include "CmSpatialVector.h" +#include "CmTask.h" +#include "CmPool.h" +#include "PxcThreadCoherentCache.h" +#include "DyThreadContext.h" +#include "PxcConstraintBlockStream.h" +#include "DySolverBody.h" +#include "DyContext.h" +#include "PxsIslandManagerTypes.h" +#include "PxvNphaseImplementationContext.h" +#include "solver/PxSolverDefs.h" +#include "PxsIslandSim.h" + +namespace physx +{ + + namespace Cm + { + class FlushPool; + } + + namespace IG + { + class SimpleIslandManager; + struct Edge; + } + + class PxsRigidBody; + + class PxsStreamedThresholdTable; + + struct PxsBodyCore; + struct PxsIslandObjects; + class PxsIslandIndices; + struct PxsIndexedInteraction; + class PxsIslandManager; + struct PxsIndexedConstraint; + struct PxsIndexedContactManager; + class PxsHeapMemoryAllocator; + class PxsMemoryManager; + class PxsDefaultMemoryManager; + + + namespace Cm + { + class Bitmap; + class SpatialVector; + } + + namespace Dy + { + class SolverCore; + struct SolverIslandParams; + struct ArticulationSolverDesc; + class ArticulationV; + class DynamicsContext; + struct SolverContext; + + struct PxTGSSolverConstraintDesc + { + static const PxU16 NO_LINK = 0xffff; + + enum ConstraintType + { + eCONTACT_CONSTRAINT, //!< Defines this pair is a contact constraint + eJOINT_CONSTRAINT //!< Defines this pair is a joint constraint + }; + + union + { + PxTGSSolverBodyVel* bodyA; //!< bodyA pointer + Dy::ArticulationV* articulationA; //!< Articulation pointer for body A + }; + + union + { + PxTGSSolverBodyVel* bodyB; //!< BodyB pointer + Dy::ArticulationV* articulationB; //!< Articulation pointer for body B + }; + + PxU16 linkIndexA; + PxU16 linkIndexB; + union + { + PxU16 articulationALength; + PxU32 bodyAIdx; + }; + union + { + PxU16 articulationBLength; + PxU32 bodyBIdx; + }; + + PxU16 writeBackLengthOver4; //!< writeBackLength/4, max writeback length is 256K, allows PxSolverConstraintDesc to fit in 32 bytes + PxU16 constraintLengthOver16; //!< constraintLength/16, max constraint length is 1MB, allows PxSolverConstraintDesc to fit in 32 bytes + + PxU8* constraint; //!< Pointer to the constraint rows to be solved + void* writeBack; //!< Pointer to the writeback structure results for this given constraint are to be written to + + }; + + struct SolverIslandObjectsStep + { + PxsRigidBody** bodies; + ArticulationV** articulations; + ArticulationV** articulationOwners; + PxsIndexedContactManager* contactManagers; + + const IG::IslandId* islandIds; + PxU32 numIslands; + PxU32* bodyRemapTable; + PxU32* nodeIndexArray; + + PxTGSSolverConstraintDesc* constraintDescs; + PxTGSSolverConstraintDesc* orderedConstraintDescs; + PxTGSSolverConstraintDesc* tempConstraintDescs; + PxConstraintBatchHeader* constraintBatchHeaders; + Cm::SpatialVector* motionVelocities; + PxsBodyCore** bodyCoreArray; + + PxU32 solverBodyOffset; + + SolverIslandObjectsStep() : bodies(NULL), articulations(NULL), articulationOwners(NULL), + contactManagers(NULL), islandIds(NULL), numIslands(0), nodeIndexArray(NULL), constraintDescs(NULL), motionVelocities(NULL), bodyCoreArray(NULL), + solverBodyOffset(0) + { + } + }; + + struct IslandContextStep + { + //The thread context for this island (set in in the island start task, released in the island end task) + ThreadContext* mThreadContext; + PxsIslandIndices mCounts; + SolverIslandObjectsStep mObjects; + PxU32 mPosIters; + PxU32 mVelIters; + PxU32 mArticulationOffset; + PxReal mStepDt; + PxReal mInvStepDt; + PxI32 mSharedSolverIndex; + PxI32 mSolvedCount; + PxI32 mSharedRigidBodyIndex; + PxI32 mRigidBodyIntegratedCount; + PxI32 mSharedArticulationIndex; + PxI32 mArticulationIntegratedCount; + }; + + + + + struct PxTGSSolverConstraintPrepDescBase + { + PxConstraintInvMassScale mInvMassScales; //!< In: The local mass scaling for this pair. + + PxTGSSolverConstraintDesc* desc; //!< Output: The PxSolverConstraintDesc filled in by contact prep + + const PxTGSSolverBodyVel* body0; //!< In: The first body. Stores velocity information. Unused unless contact involves articulations. + const PxTGSSolverBodyVel *body1; //!< In: The second body. Stores velocity information. Unused unless contact involves articulations. + + const PxTGSSolverBodyTxInertia* body0TxI; + const PxTGSSolverBodyTxInertia* body1TxI; + + const PxStepSolverBodyData* bodyData0; + const PxStepSolverBodyData* bodyData1; + + PxTransform bodyFrame0; //!< In: The world-space transform of the first body. + PxTransform bodyFrame1; //!< In: The world-space transform of the second body. + + PxSolverContactDesc::BodyState bodyState0; //!< In: Defines what kind of actor the first body is + PxSolverContactDesc::BodyState bodyState1; //!< In: Defines what kind of actor the second body is + + }; + + struct PxTGSSolverConstraintPrepDesc : public PxTGSSolverConstraintPrepDescBase + { + Px1DConstraint* rows; //!< The start of the constraint rows + PxU32 numRows; //!< The number of rows + + PxReal linBreakForce, angBreakForce; //!< Break forces + PxReal minResponseThreshold; //!< The minimum response threshold + void* writeback; //!< Pointer to constraint writeback structure. Reports back joint breaking. If not required, set to NULL. + bool disablePreprocessing; //!< Disable joint pre-processing. Pre-processing can improve stability but under certain circumstances, e.g. when some invInertia rows are zero/almost zero, can cause instabilities. + bool improvedSlerp; //!< Use improved slerp model + bool driveLimitsAreForces; //!< Indicates whether drive limits are forces + bool extendedLimits; //!< Indiciates whether extended limits are used + + PxVec3 body0WorldOffset; //!< Body0 world offset + PxVec3 cA2w; //!< Location of anchor point A in world space + PxVec3 cB2w; //!< Location of anchor point B in world space + }; + + + struct PxTGSSolverContactDesc : public PxTGSSolverConstraintPrepDescBase + { + + Sc::ShapeInteraction* shapeInteraction; //!< Pointer to share interaction. Used for force threshold reports in solver. Set to NULL if using immediate mode. + Gu::ContactPoint* contacts; //!< The start of the contacts for this pair + PxU32 numContacts; //!< The total number of contacs this pair references. + + bool hasMaxImpulse; //!< Defines whether this pairs has maxImpulses clamping enabled + bool disableStrongFriction; //!< Defines whether this pair disables strong friction (sticky friction correlation) + bool hasForceThresholds; //!< Defines whether this pair requires force thresholds + + PxReal restDistance; //!< A distance at which the solver should aim to hold the bodies separated. Default is 0 + PxReal maxCCDSeparation; //!< A distance used to configure speculative CCD behavior. Default is PX_MAX_F32. Set internally in PhysX for bodies with eENABLE_SPECULATIVE_CCD on. Do not set directly! + + PxU8* frictionPtr; //!< InOut: Friction patch correlation data. Set each frame by solver. Can be retained for improved behaviour or discarded each frame. + PxU8 frictionCount; //!< The total number of friction patches in this pair + + PxReal* contactForces; //!< Out: A buffer for the solver to write applied contact forces to. + + PxU32 startFrictionPatchIndex; //!< Start index of friction patch in the correlation buffer. Set by friction correlation + PxU32 numFrictionPatches; //!< Total number of friction patches in this pair. Set by friction correlation + + PxU32 startContactPatchIndex; //!< The start index of this pair's contact patches in the correlation buffer. For internal use only + PxU16 numContactPatches; //!< Total number of contact patches. + PxU16 axisConstraintCount; //!< Axis constraint count. Defines how many constraint rows this pair has produced. Useful for statistical purposes. + + PxReal maxImpulse; + + PxReal torsionalPatchRadius; + PxReal minTorsionalPatchRadius; + }; + + + + struct SolverIslandObjectsStep; + + class SolverBodyVelDataPool : public Ps::Array > > + { + PX_NOCOPY(SolverBodyVelDataPool) + public: + SolverBodyVelDataPool() {} + }; + + class SolverBodyTxInertiaPool : public Ps::Array > > + { + PX_NOCOPY(SolverBodyTxInertiaPool) + public: + SolverBodyTxInertiaPool() {} + }; + + class SolverBodyDataStepPool : public Ps::Array > > + { + PX_NOCOPY(SolverBodyDataStepPool) + public: + SolverBodyDataStepPool() {} + }; + + + + class SolverStepConstraintDescPool : public Ps::Array > > + { + PX_NOCOPY(SolverStepConstraintDescPool) + public: + SolverStepConstraintDescPool() { } + }; + + +#if PX_VC +#pragma warning(push) +#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + + + class DynamicsTGSContext : public Context + { + PX_NOCOPY(DynamicsTGSContext) + public: + + /**PxBaseTask* continuation + \brief Creates a DynamicsContext associated with a PxsContext + \return A pointer to the newly-created DynamicsContext. + */ + static DynamicsTGSContext* create(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocator, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal lengthScale + ); + + /** + \brief Destroys this DynamicsContext + */ + void destroy(); + + /** + \brief Returns the static world solver body + \return The static world solver body. + */ + //PX_FORCE_INLINE PxSolverBody& getWorldSolverBody() { return mWorldSolverBody; } + + PX_FORCE_INLINE Cm::FlushPool& getTaskPool() { return mTaskPool; } + + PX_FORCE_INLINE ThresholdStream& getThresholdStream() { return *mThresholdStream; } + + PX_FORCE_INLINE PxvSimStats& getSimStats() { return mSimStats; } + +#if PX_ENABLE_SIM_STATS + void addThreadStats(const ThreadContext::ThreadSimStats& stats); +#endif + + /** + \brief The entry point for the constraint solver. + \param[in] dt The simulation time-step + \param[in] continuation The continuation task for the solver + + This method is called after the island generation has completed. Its main responsibilities are: + (1) Reserving the solver body pools + (2) Initializing the static and kinematic solver bodies, which are shared resources between islands. + (3) Construct the solver task chains for each island + + Each island is solved as an independent solver task chain in parallel. + + */ + + virtual void update(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, PxBaseTask* lostTouchTask, + PxsContactManager** foundPatchManagers, PxU32 nbFoundPatchManagers, PxsContactManager** lostPatchManagers, PxU32 nbLostPatchManagers, + PxU32 maxPatchesPerCM, PxsContactManagerOutputIterator& iter, PxsContactManagerOutput* gpuOutputs, const PxReal dt, const PxVec3& gravity, const PxU32 bitMapWordCounts); + + void updatePostKinematic(IG::SimpleIslandManager& simpleIslandManager, PxBaseTask* continuation, + PxBaseTask* lostTouchTask); + + virtual void processLostPatches(IG::SimpleIslandManager& /*simpleIslandManager*/, PxsContactManager** /*lostPatchManagers*/, PxU32 /*nbLostPatchManagers*/, PxsContactManagerOutputIterator& /*iterator*/){} + + virtual void updateBodyCore(PxBaseTask* continuation); + + virtual void setSimulationController(PxsSimulationController* simulationController){ mSimulationController = simulationController; } + /** + \brief This method combines the results of several islands, e.g. constructing scene-level simulation statistics and merging together threshold streams for contact notification. + */ + virtual void mergeResults(); + + virtual void getDataStreamBase(void*& /*contactStreamBase*/, void*& /*patchStreamBase*/, void*& /*forceAndIndicesStreamBase*/){} + + /** + \brief Allocates and returns a thread context object. + \return A thread context. + */ + PX_FORCE_INLINE ThreadContext* getThreadContext() + { + return mThreadContextPool.get(); + } + + /** + \brief Returns a thread context to the thread context pool. + \param[in] context The thread context to return to the thread context pool. + */ + void putThreadContext(ThreadContext* context) + { + mThreadContextPool.put(context); + } + + + PX_FORCE_INLINE PxU32 getKinematicCount() const { return mKinematicCount; } + PX_FORCE_INLINE PxU64 getContextId() const { return mContextID; } + + protected: + + /** + \brief Constructor for DynamicsContext + */ + DynamicsTGSContext(PxcNpMemBlockPool* memBlockPool, + PxcScratchAllocator& scratchAllocator, + Cm::FlushPool& taskPool, + PxvSimStats& simStats, + PxTaskManager* taskManager, + Ps::VirtualAllocatorCallback* allocator, + PxsMaterialManager* materialManager, + IG::IslandSim* accurateIslandSim, + PxU64 contextID, + const bool enableStabilization, + const bool useEnhancedDeterminism, + const bool useAdaptiveForce, + const PxReal lengthScale + ); + /** + \brief Destructor for DynamicsContext + */ + virtual ~DynamicsTGSContext(); + + + // Solver helper-methods + /** + \brief Computes the unconstrained velocity for a given PxsRigidBody + \param[in] atom The PxsRigidBody + */ + void computeUnconstrainedVelocity(PxsRigidBody* atom) const; + + /** + \brief fills in a PxSolverConstraintDesc from an indexed interaction + \param[in,out] desc The PxSolverConstraintDesc + \param[in] constraint The PxsIndexedInteraction + */ + void setDescFromIndices(PxTGSSolverConstraintDesc& desc, + const PxsIndexedInteraction& constraint, const PxU32 solverBodyOffset, PxTGSSolverBodyVel* solverBodies); + + + void setDescFromIndices(PxTGSSolverConstraintDesc& desc, IG::EdgeIndex edgeIndex, + const IG::SimpleIslandManager& islandManager, PxU32* bodyRemapTable, const PxU32 solverBodyOffset, PxTGSSolverBodyVel* solverBodies); + + + void solveIsland(const SolverIslandObjectsStep& objects, + const PxsIslandIndices& counts, + const PxU32 solverBodyOffset, + IG::SimpleIslandManager& islandManager, + PxU32* bodyRemapTable, PxsMaterialManager* materialManager, + PxsContactManagerOutputIterator& iterator, + PxBaseTask* continuation); + + void prepareBodiesAndConstraints(const SolverIslandObjectsStep& objects, + IG::SimpleIslandManager& islandManager, + IslandContextStep& islandContext); + + void setupDescs(IslandContextStep& islandContext, const SolverIslandObjectsStep& objects, IG::SimpleIslandManager& mIslandManager, PxU32* mBodyRemapTable, PxU32 mSolverBodyOffset, + PxsContactManagerOutputIterator& outputs); + + void preIntegrateBodies(PxsBodyCore** bodyArray, PxsRigidBody** originalBodyArray, + PxTGSSolverBodyVel* solverBodyVelPool, PxTGSSolverBodyTxInertia* solverBodyTxInertia, PxStepSolverBodyData* solverBodyDataPool2, + PxU32* nodeIndexArray, const PxU32 bodyCount, const PxVec3& gravity, const PxReal dt, PxU32& posIters, PxU32& velIters, PxU32 iteration); + + void setupArticulations(IslandContextStep& islandContext, const PxVec3& gravity, const PxReal dt, PxU32& posIters, PxU32& velIters, PxBaseTask* continuation); + + PxU32 setupArticulationInternalConstraints(IslandContextStep& islandContext, PxReal dt, PxReal invStepDt, PxTGSSolverConstraintDesc* constraintDescs); + + void createSolverConstraints(PxTGSSolverConstraintDesc* contactDescPtr, PxConstraintBatchHeader* headers, const PxU32 nbHeaders, + PxsContactManagerOutputIterator& outputs, Dy::ThreadContext& islandThreadContext, Dy::ThreadContext& threadContext, PxReal stepDt, PxReal totalDt, + PxReal invStepDt, PxU32 nbSubsteps); + + void solveConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, bool doFriction, PxReal invStepDt, + const PxTGSSolverBodyTxInertia* const solverTxInertia, const PxReal elapsedTime, const PxReal minPenetration, SolverContext& cache); + + void solveConcludeConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, + PxTGSSolverBodyTxInertia* solverTxInertia, const PxReal elapsedTime, SolverContext& cache); + + void parallelSolveConstraints(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders, bool doFriction, PxTGSSolverBodyTxInertia* solverTxInertia, + const PxReal elapsedTime, const PxReal minPenetration, SolverContext& cache); + + void writebackConstraintsIteration(const PxConstraintBatchHeader* const hdrs, const PxTGSSolverConstraintDesc* const contactDescPtr, const PxU32 nbHeaders); + + void parallelWritebackConstraintsIteration(const PxTGSSolverConstraintDesc* const contactDescPtr, const PxConstraintBatchHeader* const batchHeaders, const PxU32 nbHeaders); + + void integrateBodies(const SolverIslandObjectsStep& objects, + const PxU32 count, PxTGSSolverBodyVel* vels, + PxTGSSolverBodyTxInertia* txInertias, const PxStepSolverBodyData*const bodyDatas, PxReal dt); + + void parallelIntegrateBodies(PxTGSSolverBodyVel* vels, PxTGSSolverBodyTxInertia* txInertias, + const PxStepSolverBodyData* const bodyDatas, const PxU32 count, PxReal dt); + + void copyBackBodies(const SolverIslandObjectsStep& objects, + PxTGSSolverBodyVel* vels, PxTGSSolverBodyTxInertia* txInertias, + PxStepSolverBodyData* solverBodyData, PxReal invDt, IG::IslandSim& islandSim, + PxU32 startIdx, PxU32 endIdx); + + void updateArticulations(Dy::ThreadContext& threadContext, const PxU32 startIdx, const PxU32 endIdx, PxReal dt); + + void stepArticulations(Dy::ThreadContext& threadContext, const PxsIslandIndices& counts, PxReal dt); + + void iterativeSolveIsland(const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& mThreadContext, + const PxReal stepDt, const PxReal invStepDt, const PxU32 posIters, const PxU32 velIters, SolverContext& cache); + + void iterativeSolveIslandParallel(const SolverIslandObjectsStep& objects, const PxsIslandIndices& counts, ThreadContext& mThreadContext, + const PxReal stepDt, const PxU32 posIters, const PxU32 velIters, PxI32* solverCounts, PxI32* integrationCounts, PxI32* articulationIntegrationCounts, + PxI32* solverProgressCount, PxI32* integrationProgressCount, PxI32* articulationProgressCount, PxU32 solverUnrollSize, PxU32 integrationUnrollSize); + + void endIsland(ThreadContext& mThreadContext); + + void finishSolveIsland(ThreadContext& mThreadContext, const SolverIslandObjectsStep& objects, + const PxsIslandIndices& counts, IG::SimpleIslandManager& islandManager, PxBaseTask* continuation); + + + + + /** + \brief Resets the thread contexts + */ + void resetThreadContexts(); + + /** + \brief Returns the scratch memory allocator. + \return The scratch memory allocator. + */ + PX_FORCE_INLINE PxcScratchAllocator& getScratchAllocator() { return mScratchAllocator; } + + //Data + + PxTGSSolverBodyVel mWorldSolverBodyVel; + PxTGSSolverBodyTxInertia mWorldSolverBodyTxInertia; + PxStepSolverBodyData mWorldSolverBodyData2; + + /** + \brief A thread context pool + */ + PxcThreadCoherentCache mThreadContextPool; + + /** + \brief Solver constraint desc array + */ + SolverStepConstraintDescPool mSolverConstraintDescPool; + + SolverStepConstraintDescPool mOrderedSolverConstraintDescPool; + + SolverStepConstraintDescPool mTempSolverConstraintDescPool; + + Ps::Array mContactConstraintBatchHeaders; + + /** + \brief Array of motion velocities for all bodies in the scene. + */ + Ps::Array mMotionVelocityArray; + + /** + \brief Array of body core pointers for all bodies in the scene. + */ + Ps::Array mBodyCoreArray; + + /** + \brief Array of rigid body pointers for all bodies in the scene. + */ + Ps::Array mRigidBodyArray; + + /** + \brief Array of articulationpointers for all articulations in the scene. + */ + Ps::Array mArticulationArray; + + SolverBodyVelDataPool mSolverBodyVelPool; + + SolverBodyTxInertiaPool mSolverBodyTxInertiaPool; + + SolverBodyDataStepPool mSolverBodyDataPool2; + + + ThresholdStream* mExceededForceThresholdStream[2]; //this store previous and current exceeded force thresholdStream + + Ps::Array mExceededForceThresholdStreamMask; + + Ps::Array mSolverBodyRemapTable; //Remaps from the "active island" index to the index within a solver island + + Ps::Array mNodeIndexArray; //island node index + + Ps::Array mContactList; + + /** + \brief The total number of kinematic bodies in the scene + */ + PxU32 mKinematicCount; + + /** + \brief Atomic counter for the number of threshold stream elements. + */ + PxI32 mThresholdStreamOut; + + + + PxsMaterialManager* mMaterialManager; + + PxsContactManagerOutputIterator mOutputIterator; + + PxReal mLengthScale; + + private: + //private: + PxcScratchAllocator& mScratchAllocator; + Cm::FlushPool& mTaskPool; + PxTaskManager* mTaskManager; + PxU32 mCurrentIndex; // this is the index point to the current exceeded force threshold stream + + PxU64 mContextID; + + friend class SetupDescsTask; + friend class PreIntegrateTask; + friend class SetupArticulationTask; + friend class SetupArticulationInternalConstraintsTask; + friend class SetupSolverConstraintsTask; + friend class SolveIslandTask; + friend class EndIslandTask; + friend class SetupSolverConstraintsSubTask; + friend class ParallelSolveTask; + friend class PreIntegrateParallelTask; + friend class CopyBackTask; + friend class UpdateArticTask; + friend class FinishSolveIslandTask; + }; + +#if PX_VC +#pragma warning(pop) +#endif + + } +} + +#endif + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp new file mode 100644 index 000000000..2b65cafdf --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp @@ -0,0 +1,961 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "DyTGSPartition.h" +#include "DyArticulationUtils.h" +#include "PsAlloca.h" +#include "foundation/PxMemory.h" + +#define INTERLEAVE_SELF_CONSTRAINTS 1 + + +namespace physx +{ + namespace Dy + { + + namespace + { + + PX_FORCE_INLINE PxU32 getArticulationIndex(const uintptr_t eaArticulation, const uintptr_t* eaArticulations, const PxU32 numEas) + { + PxU32 index = 0xffffffff; + for (PxU32 i = 0; ipartitionMask; + bodyBProgress = desc.bodyB->partitionMask; + return desc.bodyAIdx != 0 && desc.bodyBIdx != 0; + } + + PX_FORCE_INLINE void getProgress(const PxTGSSolverConstraintDesc& desc, + PxU32& bodyAProgress, PxU32& bodyBProgress) + { + bodyAProgress = desc.bodyA->partitionMask; + bodyBProgress = desc.bodyB->partitionMask; + } + + PX_FORCE_INLINE void storeProgress(const PxTGSSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress, + const PxU16 availablePartition) + { + desc.bodyA->partitionMask = bodyAProgress; + desc.bodyA->maxDynamicPartition = PxMax(desc.bodyA->maxDynamicPartition, availablePartition); + desc.bodyB->partitionMask = bodyBProgress; + desc.bodyB->maxDynamicPartition = PxMax(desc.bodyB->maxDynamicPartition, availablePartition); + } + + PX_FORCE_INLINE void storeProgress(const PxTGSSolverConstraintDesc& desc, const bool activeA, const bool activeB, + const PxU32 bodyAProgress, const PxU32 bodyBProgress) + { + if(activeA) + desc.bodyA->partitionMask = bodyAProgress; + if(activeB) + desc.bodyB->partitionMask = bodyBProgress; + } + + PX_FORCE_INLINE void recordStaticConstraint(const PxTGSSolverConstraintDesc& desc, bool& activeA, bool& activeB) + { + if (activeA) + desc.bodyA->nbStaticInteractions++; + + if (activeB) + desc.bodyB->nbStaticInteractions++; + + } + + PX_FORCE_INLINE PxU32 getStaticContactWriteIndex(const PxTGSSolverConstraintDesc& desc, + bool activeA, bool activeB) + { + if (activeA) + { + return PxU32(desc.bodyA->maxDynamicPartition + desc.bodyA->nbStaticInteractions++); + + } + else if (activeB) + { + return PxU32(desc.bodyB->maxDynamicPartition + desc.bodyB->nbStaticInteractions++); + } + + return 0xffffffff; + } + + PX_FORCE_INLINE void clearState() + { + for (PxU32 a = 0; a < mNumBodies; ++a) + mBodies[a].partitionMask = 0; + } + + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(Ps::Array& numConstraintsPerPartition) + { + for (PxU32 a = 0; a < mNumBodies; ++a) + { + mBodies[a].partitionMask = 0; + + PxU32 requiredSize = PxU32(mBodies[a].maxDynamicPartition + mBodies[a].nbStaticInteractions); + if (requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for (PxU32 b = 0; b < mBodies[a].nbStaticInteractions; ++b) + { + numConstraintsPerPartition[mBodies[a].maxDynamicPartition + b]++; + } + } + } + }; + + class ExtendedRigidBodyClassification + { + + PxTGSSolverBodyVel* PX_RESTRICT mBodies; + PxU32 mNumBodies; + uintptr_t* PX_RESTRICT mArticulations; + PxU32 mNumArticulations; + + + public: + + ExtendedRigidBodyClassification(PxTGSSolverBodyVel* PX_RESTRICT bodies, PxU32 numBodies, uintptr_t* PX_RESTRICT articulations, + PxU32 numArticulations): mBodies(bodies), mNumBodies(numBodies), + mArticulations(articulations), mNumArticulations(numArticulations) + { + } + + //Returns true if it is a dynamic-dynamic constriant; false if it is a dynamic-static or dynamic-kinematic constraint + PX_FORCE_INLINE bool classifyConstraint(const PxTGSSolverConstraintDesc& desc, uintptr_t& indexA, uintptr_t& indexB, + bool& activeA, bool& activeB, PxU32& bodyAProgress, PxU32& bodyBProgress) const + { + bool staticA = false, staticB = false; + if (PxTGSSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + indexA = uintptr_t(desc.bodyA - mBodies); + activeA = indexA < mNumBodies; + staticA = desc.bodyAIdx == 0; + bodyAProgress = activeA ? desc.bodyA->partitionMask : 0; + } + else + { + ArticulationV* articulationA = desc.articulationA; + indexA = mNumBodies + getArticulationIndex(uintptr_t(articulationA), mArticulations, mNumArticulations); + activeA = true; + bodyAProgress = articulationA->solverProgress; + } + + if (PxTGSSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + indexB = uintptr_t(desc.bodyB - mBodies); + activeB = indexB < mNumBodies; + staticB = desc.bodyBIdx == 0; + bodyBProgress = activeB ? desc.bodyB->partitionMask : 0; + } + else + { + ArticulationV* articulationB = desc.articulationB; + indexB = mNumBodies + getArticulationIndex(uintptr_t(articulationB), mArticulations, mNumArticulations); + activeB = true; + bodyBProgress = articulationB->solverProgress; + } + return !(staticA || staticB); + } + + PX_FORCE_INLINE void getProgress(const PxTGSSolverConstraintDesc& desc, + PxU32& bodyAProgress, PxU32& bodyBProgress) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + bodyAProgress = desc.bodyA->partitionMask; + } + else + { + ArticulationV* articulationA = desc.articulationA; + bodyAProgress = articulationA->solverProgress; + } + + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + bodyBProgress = desc.bodyB->partitionMask; + } + else + { + ArticulationV* articulationB = desc.articulationB; + bodyBProgress = articulationB->solverProgress; + } + } + + //called by classifyConstraintDesc + PX_FORCE_INLINE void storeProgress(const PxTGSSolverConstraintDesc& desc, const PxU32 bodyAProgress, const PxU32 bodyBProgress, + const PxU16 availablePartition) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + desc.bodyA->partitionMask = bodyAProgress; + desc.bodyA->maxDynamicPartition = PxMax(desc.bodyA->maxDynamicPartition, availablePartition); + } + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->solverProgress = bodyAProgress; + articulationA->maxSolverFrictionProgress = PxMax(articulationA->maxSolverFrictionProgress, availablePartition); + } + + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + desc.bodyB->partitionMask = bodyBProgress; + desc.bodyB->maxDynamicPartition = PxMax(desc.bodyB->maxDynamicPartition, availablePartition); + } + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->solverProgress = bodyBProgress; + articulationB->maxSolverFrictionProgress = PxMax(articulationB->maxSolverFrictionProgress, availablePartition); + } + } + + //called by writeConstraintDesc + PX_FORCE_INLINE void storeProgress(const PxTGSSolverConstraintDesc& desc, + const bool activeA, const bool activeB, const PxU32 bodyAProgress, const PxU32 bodyBProgress) + { + if (activeA) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + desc.bodyA->partitionMask = bodyAProgress; + } + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->solverProgress = bodyAProgress; + articulationA->numTotalConstraints++; + } + } + + if (activeB) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + desc.bodyB->partitionMask = bodyBProgress; + } + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->solverProgress = bodyBProgress; + articulationB->numTotalConstraints++; + } + } + } + + //called by classifyConstraintDesc + PX_FORCE_INLINE void recordStaticConstraint(const PxTGSSolverConstraintDesc& desc, bool& activeA, bool& activeB) + { + if (activeA) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + desc.bodyA->nbStaticInteractions++; + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->maxSolverNormalProgress++; + } + } + + if (activeB) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + desc.bodyB->nbStaticInteractions++; + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->maxSolverNormalProgress++; + } + } + } + + //called by writeConstraintDesc + PX_FORCE_INLINE PxU32 getStaticContactWriteIndex(const PxTGSSolverConstraintDesc& desc, + bool activeA, bool activeB) + { + if (activeA) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexA) + { + return PxU32(desc.bodyA->maxDynamicPartition + desc.bodyA->nbStaticInteractions++); + } + else + { + ArticulationV* articulationA = desc.articulationA; + articulationA->numTotalConstraints++; + return PxU32(articulationA->maxSolverFrictionProgress + articulationA->maxSolverNormalProgress++); + } + + } + else if (activeB) + { + if (PxSolverConstraintDesc::NO_LINK == desc.linkIndexB) + { + return PxU32(desc.bodyB->maxDynamicPartition + desc.bodyB->nbStaticInteractions++); + } + else + { + ArticulationV* articulationB = desc.articulationB; + articulationB->numTotalConstraints++; + return PxU32(articulationB->maxSolverFrictionProgress + articulationB->maxSolverNormalProgress++); + } + } + + return 0xffffffff; + } + + PX_FORCE_INLINE void clearState() + { + for (PxU32 a = 0; a < mNumBodies; ++a) + mBodies[a].partitionMask = 0; + + for (PxU32 a = 0; a < mNumArticulations; ++a) + (reinterpret_cast(mArticulations[a]))->solverProgress = 0; + } + + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(Ps::Array& numConstraintsPerPartition) + { + for (PxU32 a = 0; a < mNumBodies; ++a) + { + mBodies[a].partitionMask = 0; + + PxU32 requiredSize = PxU32(mBodies[a].maxDynamicPartition + mBodies[a].nbStaticInteractions); + if (requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for (PxU32 b = 0; b < mBodies[a].nbStaticInteractions; ++b) + { + numConstraintsPerPartition[mBodies[a].maxDynamicPartition + b]++; + } + } + + for (PxU32 a = 0; a < mNumArticulations; ++a) + { + ArticulationV* articulation = reinterpret_cast(mArticulations[a]); + articulation->solverProgress = 0; + + PxU32 requiredSize = PxU32(articulation->maxSolverNormalProgress + articulation->maxSolverFrictionProgress); + if (requiredSize > numConstraintsPerPartition.size()) + { + numConstraintsPerPartition.resize(requiredSize); + } + + for (PxU32 b = 0; b < articulation->maxSolverNormalProgress; ++b) + { + numConstraintsPerPartition[articulation->maxSolverFrictionProgress + b]++; + } + } + } + + }; + + template + void classifyConstraintDesc(const PxTGSSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + Ps::Array& numConstraintsPerPartition, PxTGSSolverConstraintDesc* PX_RESTRICT eaTempConstraintDescriptors) + { + const PxTGSSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxU32 numUnpartitionedConstraints = 0; + PxU32 nbNormalConstraints = 0; + PxU32 nbStaticConstraints = 0; + + numConstraintsPerPartition.forceSize_Unsafe(32); + + PxMemZero(numConstraintsPerPartition.begin(), sizeof(PxU32) * 32); + + for (PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + if (notContainsStatic) + { + nbNormalConstraints++; + /* PxU32 partitionsA = _desc->bodyA->partitionMask; + PxU32 partitionsB = _desc->bodyB->partitionMask;*/ + + PxU32 availablePartition; + { + const PxU32 combinedMask = ((~partitionsA) & (~partitionsB)); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + eaTempConstraintDescriptors[numUnpartitionedConstraints++] = *_desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + PX_ASSERT((partitionBit & partitionsA) == 0); + PX_ASSERT((partitionBit & partitionsB) == 0); + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + classification.storeProgress(*_desc, partitionsA, partitionsB, PxU16(availablePartition)); + } + else + { + nbStaticConstraints++; + + //Just count the number of static constraints and store in maxSolverFrictionProgress... + PX_ASSERT(activeA || activeB); + classification.recordStaticConstraint(*_desc, activeA, activeB); + } + } + + PxU32 partitionStartIndex = 0; + + while (numUnpartitionedConstraints > 0) + { + classification.clearState(); + + partitionStartIndex += 32; + //Keep partitioning the un-partitioned constraints and blat the whole thing to 0! + numConstraintsPerPartition.resize(32 + numConstraintsPerPartition.size()); + PxMemZero(numConstraintsPerPartition.begin() + partitionStartIndex, sizeof(PxU32) * 32); + + PxU32 newNumUnpartitionedConstraints = 0; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + + for (PxU32 i = 0; i < numUnpartitionedConstraints; ++i) + { + const PxTGSSolverConstraintDesc& desc = eaTempConstraintDescriptors[i]; + + classification.classifyConstraint(desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + /*PxU32 partitionsA = desc.bodyA->partitionMask; + PxU32 partitionsB = desc.bodyB->partitionMask;*/ + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + //Need to shuffle around unpartitioned constraints... + eaTempConstraintDescriptors[newNumUnpartitionedConstraints++] = desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + availablePartition += partitionStartIndex; + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + classification.storeProgress(desc, partitionsA, partitionsB, PxU16(availablePartition)); + + } + + numUnpartitionedConstraints = newNumUnpartitionedConstraints; + } + + classification.reserveSpaceForStaticConstraints(numConstraintsPerPartition); + + } + + template + void writeConstraintDesc(const PxTGSSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + Ps::Array& accumulatedConstraintsPerPartition, PxTGSSolverConstraintDesc* eaTempConstraintDescriptors, + PxTGSSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDesc) + { + PX_UNUSED(eaTempConstraintDescriptors); + const PxTGSSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxU32 numUnpartitionedConstraints = 0; + + for (PxU32 i = 0; i < numConstraints; ++i, _desc++) + { + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + if (notContainsStatic) + { + /*PxU32 partitionsA = _desc->bodyA->partitionMask; + PxU32 partitionsB = _desc->bodyB->partitionMask;*/ + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + eaTempConstraintDescriptors[numUnpartitionedConstraints++] = *_desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + + if(activeA) + partitionsA |= partitionBit; + if(activeB) + partitionsB |= partitionBit; + } + + /*_desc->bodyA->partitionMask = partitionsA; + _desc->bodyB->partitionMask = partitionsB; +*/ + classification.storeProgress(*_desc, activeA, activeB, partitionsA, partitionsB); + + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + } + else + { + //Just count the number of static constraints and store in maxSolverFrictionProgress... + /*PxU32 index = 0; + if (activeA) + index = PxU32(_desc->bodyA->maxDynamicPartition + _desc->bodyA->nbStaticInteractions++); + else if (activeB) + index = PxU32(_desc->bodyB->maxDynamicPartition + _desc->bodyB->nbStaticInteractions++);*/ + + PxU32 index = classification.getStaticContactWriteIndex(*_desc, activeA, activeB); + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[index]++] = *_desc; + } + } + + PxU32 partitionStartIndex = 0; + + while (numUnpartitionedConstraints > 0) + { + classification.clearState(); + + partitionStartIndex += 32; + PxU32 newNumUnpartitionedConstraints = 0; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + + for (PxU32 i = 0; i < numUnpartitionedConstraints; ++i) + { + const PxTGSSolverConstraintDesc& desc = eaTempConstraintDescriptors[i]; + + classification.classifyConstraint(desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + /* PxU32 partitionsA = desc.bodyA->partitionMask; + PxU32 partitionsB = desc.bodyB->partitionMask;*/ + + PxU32 availablePartition; + { + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) + { + //Need to shuffle around unpartitioned constraints... + eaTempConstraintDescriptors[newNumUnpartitionedConstraints++] = desc; + continue; + } + + const PxU32 partitionBit = getBit(availablePartition); + + partitionsA |= partitionBit; + partitionsB |= partitionBit; + } + + /*if(activeA) + desc.bodyA->partitionMask = partitionsA; + if(activeB) + desc.bodyB->partitionMask = partitionsB;*/ + classification.storeProgress(desc, activeA, activeB, partitionsA, partitionsB); + availablePartition += partitionStartIndex; + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = desc; + } + + numUnpartitionedConstraints = newNumUnpartitionedConstraints; + } + } + + } + +#define PX_NORMALIZE_PARTITIONS 1 + +#if PX_NORMALIZE_PARTITIONS + + template + PxU32 normalizePartitions(Ps::Array& accumulatedConstraintsPerPartition, PxTGSSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDescriptors, + const PxU32 numConstraintDescriptors, Ps::Array& bitField, const Classification& classification, const PxU32 numBodies, const PxU32 numArticulations) + { + PxU32 numPartitions = 0; + + PxU32 prevAccumulation = 0; + for (; numPartitions < accumulatedConstraintsPerPartition.size() && accumulatedConstraintsPerPartition[numPartitions] > prevAccumulation; + prevAccumulation = accumulatedConstraintsPerPartition[numPartitions++]); + + PxU32 targetSize = (numPartitions == 0 ? 0 : (numConstraintDescriptors) / numPartitions); + + bitField.reserve((numBodies + numArticulations + 31) / 32); + bitField.forceSize_Unsafe((numBodies + numArticulations + 31) / 32); + + for (PxU32 i = numPartitions; i > 0; i--) + { + PxU32 partitionIndex = i - 1; + + //Build the partition mask... + + PxU32 startIndex = partitionIndex == 0 ? 0 : accumulatedConstraintsPerPartition[partitionIndex - 1]; + PxU32 endIndex = accumulatedConstraintsPerPartition[partitionIndex]; + + //If its greater than target size, there's nothing that will be pulled into it from earlier partitions + if ((endIndex - startIndex) >= targetSize) + continue; + + + PxMemZero(bitField.begin(), sizeof(PxU32)*bitField.size()); + + for (PxU32 a = startIndex; a < endIndex; ++a) + { + PxTGSSolverConstraintDesc& desc = eaOrderedConstraintDescriptors[a]; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + classification.classifyConstraint(desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + if (activeA) + bitField[PxU32(indexA) / 32] |= getBit(indexA & 31); + if (activeB) + bitField[PxU32(indexB) / 32] |= getBit(indexB & 31); + } + + bool bTerm = false; + for (PxU32 a = partitionIndex; a > 0 && !bTerm; --a) + { + PxU32 pInd = a - 1; + + PxU32 si = pInd == 0 ? 0 : accumulatedConstraintsPerPartition[pInd - 1]; + PxU32 ei = accumulatedConstraintsPerPartition[pInd]; + + for (PxU32 b = ei; b > si && !bTerm; --b) + { + PxU32 ind = b - 1; + PxTGSSolverConstraintDesc& desc = eaOrderedConstraintDescriptors[ind]; + + uintptr_t indexA, indexB; + bool activeA, activeB; + PxU32 partitionsA, partitionsB; + classification.classifyConstraint(desc, indexA, indexB, + activeA, activeB, partitionsA, partitionsB); + + bool canAdd = true; + + if (activeA && (bitField[PxU32(indexA) / 32] & (getBit(indexA & 31)))) + canAdd = false; + if (activeB && (bitField[PxU32(indexB) / 32] & (getBit(indexB & 31)))) + canAdd = false; + + if (canAdd) + { + PxTGSSolverConstraintDesc tmp = eaOrderedConstraintDescriptors[ind]; + + if (activeA) + bitField[PxU32(indexA) / 32] |= (getBit(indexA & 31)); + if (activeB) + bitField[PxU32(indexB) / 32] |= (getBit(indexB & 31)); + + PxU32 index = ind; + for (PxU32 c = pInd; c < partitionIndex; ++c) + { + PxU32 newIndex = --accumulatedConstraintsPerPartition[c]; + if (index != newIndex) + eaOrderedConstraintDescriptors[index] = eaOrderedConstraintDescriptors[newIndex]; + index = newIndex; + } + + if (index != ind) + eaOrderedConstraintDescriptors[index] = tmp; + + if ((accumulatedConstraintsPerPartition[partitionIndex] - accumulatedConstraintsPerPartition[partitionIndex - 1]) >= targetSize) + { + bTerm = true; + break; + } + } + } + } + } + + PxU32 partitionCount = 0; + PxU32 lastPartitionCount = 0; + for (PxU32 a = 0; a < numPartitions; ++a) + { + const PxU32 constraintCount = accumulatedConstraintsPerPartition[a]; + accumulatedConstraintsPerPartition[partitionCount] = constraintCount; + if (constraintCount != lastPartitionCount) + { + lastPartitionCount = constraintCount; + partitionCount++; + } + } + + accumulatedConstraintsPerPartition.forceSize_Unsafe(partitionCount); + + return partitionCount; + } + +#endif + + PxU32 partitionContactConstraintsStep(TGSConstraintPartitionArgs& args) + { + PxU32 maxPartition = 0; + //Unpack the input data. + const PxU32 numBodies = args.mNumBodies; + PxTGSSolverBodyVel* PX_RESTRICT eaAtoms = args.mBodies; + const PxU32 numArticulations = args.mNumArticulationPtrs; + + const PxU32 numConstraintDescriptors = args.mNumContactConstraintDescriptors; + + PxTGSSolverConstraintDesc* PX_RESTRICT eaConstraintDescriptors = args.mContactConstraintDescriptors; + PxTGSSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDescriptors = args.mOrderedContactConstraintDescriptors; + PxTGSSolverConstraintDesc* PX_RESTRICT eaTempConstraintDescriptors = args.mTempContactConstraintDescriptors; + + Ps::Array& constraintsPerPartition = *args.mConstraintsPerPartition; + constraintsPerPartition.forceSize_Unsafe(0); + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxTGSSolverBodyVel& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.partitionMask = 0; + //We re-use maxSolverFrictionProgress and maxSolverNormalProgress to record the + //maximum partition used by dynamic constraints and the number of static constraints affecting + //a body. We use this to make partitioning much cheaper and be able to support + body.maxDynamicPartition = 0; + body.nbStaticInteractions = 0; + } + + PxU32 numOrderedConstraints = 0; + + PxU32 numSelfConstraintBlocks = 0; + + if (numArticulations == 0) + { + RigidBodyClassification classification(eaAtoms, numBodies); + classifyConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors); + + PxU32 accumulation = 0; + for (PxU32 a = 0; a < constraintsPerPartition.size(); ++a) + { + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; + } + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxTGSSolverBodyVel& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.partitionMask = 0; + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... + body.nbStaticInteractions = 0; + } + + writeConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors, eaOrderedConstraintDescriptors); + + numOrderedConstraints = numConstraintDescriptors; + + if (!args.enhancedDeterminism) + maxPartition = normalizePartitions(constraintsPerPartition, eaOrderedConstraintDescriptors, numConstraintDescriptors, *args.mBitField, + classification, numBodies, 0); + + } + else + { + + ArticulationSolverDesc* articulationDescs = args.mArticulationPtrs; + PX_ALLOCA(_eaArticulations, uintptr_t, numArticulations); + uintptr_t* eaArticulations = _eaArticulations; + for (PxU32 i = 0; isolverProgress = 0; + articulation->maxSolverFrictionProgress = 0; + articulation->maxSolverNormalProgress = 0; + articulation->numTotalConstraints = 0; + } + ExtendedRigidBodyClassification classification(eaAtoms, numBodies, eaArticulations, numArticulations); + + classifyConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, + constraintsPerPartition, eaTempConstraintDescriptors); + + PxU32 accumulation = 0; + for (PxU32 a = 0; a < constraintsPerPartition.size(); ++a) + { + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; + } + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxTGSSolverBodyVel& body = args.mBodies[a]; + Ps::prefetchLine(&args.mBodies[a], 256); + body.partitionMask = 0; + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... + body.nbStaticInteractions = 0; + } + + for (PxU32 a = 0; a < numArticulations; ++a) + { + ArticulationV* articulation = reinterpret_cast(eaArticulations[a]); + articulation->solverProgress = 0; + articulation->maxSolverNormalProgress = 0; + articulation->numTotalConstraints = 0; + } + + writeConstraintDesc(eaConstraintDescriptors, numConstraintDescriptors, classification, constraintsPerPartition, + eaTempConstraintDescriptors, eaOrderedConstraintDescriptors); + + numOrderedConstraints = numConstraintDescriptors; + + if (!args.enhancedDeterminism) + maxPartition = normalizePartitions(constraintsPerPartition, eaOrderedConstraintDescriptors, + numConstraintDescriptors, *args.mBitField, classification, numBodies, numArticulations); + + } + + + + const PxU32 numConstraintsDifferentBodies = numOrderedConstraints; + + PX_ASSERT(numConstraintsDifferentBodies == numConstraintDescriptors); + + //Now handle the articulated self-constraints. + PxU32 totalConstraintCount = numConstraintsDifferentBodies; + + args.mNumSelfConstraintBlocks = numSelfConstraintBlocks; + + args.mNumDifferentBodyConstraints = numConstraintsDifferentBodies; + args.mNumSelfConstraints = totalConstraintCount - numConstraintsDifferentBodies; + + if (args.enhancedDeterminism) + { + PxU32 prevPartitionSize = 0; + maxPartition = 0; + for (PxU32 a = 0; a < constraintsPerPartition.size(); ++a, maxPartition++) + { + if (constraintsPerPartition[a] == prevPartitionSize) + break; + prevPartitionSize = constraintsPerPartition[a]; + } + } + + return maxPartition; + } + + } + +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h new file mode 100644 index 000000000..538309d2a --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_TGS_CONSTRAINTPARTITION_H +#define DY_TGS_CONSTRAINTPARTITION_H + +#include "foundation/PxSimpleTypes.h" +#include "DyTGSDynamics.h" +#include "DyArticulation.h" +#include "PsArray.h" + +namespace physx +{ + + namespace Dy + { + struct TGSConstraintPartitionArgs + { + enum + { + eMAX_NUM_BODIES = 8192 + }; + + //Input + PxTGSSolverBodyVel* mBodies; + PxU32 mNumBodies; + ArticulationSolverDesc* mArticulationPtrs; + PxU32 mNumArticulationPtrs; + PxTGSSolverConstraintDesc* mContactConstraintDescriptors; + PxU32 mNumContactConstraintDescriptors; + //output + PxTGSSolverConstraintDesc* mOrderedContactConstraintDescriptors; + PxTGSSolverConstraintDesc* mTempContactConstraintDescriptors; + PxU32 mNumSelfConstraintBlocks; + PxU32 mNumDifferentBodyConstraints; + PxU32 mNumSelfConstraints; + Ps::Array* mConstraintsPerPartition; + Ps::Array* mBitField; + + bool enhancedDeterminism; + }; + + PxU32 partitionContactConstraintsStep(TGSConstraintPartitionArgs& args); + + } // namespace physx + +} + + + +#endif // DY_CONSTRAINTPARTITION_H + diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp new file mode 100644 index 000000000..8c9e172c3 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "DyThreadContext.h" +#include "PsBitUtils.h" + +namespace physx +{ +namespace Dy +{ + +ThreadContext::ThreadContext(PxcNpMemBlockPool* memBlockPool): + mFrictionPatchStreamPair(*memBlockPool), + mConstraintBlockManager (*memBlockPool), + mConstraintBlockStream (*memBlockPool), + mNumDifferentBodyConstraints(0), + mNumSelfConstraints(0), + mNumSelfConstraintBlocks(0), + mConstraintsPerPartition(PX_DEBUG_EXP("ThreadContext::mConstraintsPerPartition")), + mFrictionConstraintsPerPartition(PX_DEBUG_EXP("ThreadContext::frictionsConstraintsPerPartition")), + mPartitionNormalizationBitmap(PX_DEBUG_EXP("ThreadContext::mPartitionNormalizationBitmap")), + frictionConstraintDescArray(PX_DEBUG_EXP("ThreadContext::solverFrictionConstraintArray")), + frictionConstraintBatchHeaders(PX_DEBUG_EXP("ThreadContext::frictionConstraintBatchHeaders")), + compoundConstraints(PX_DEBUG_EXP("ThreadContext::compoundConstraints")), + orderedContactList(PX_DEBUG_EXP("ThreadContext::orderedContactList")), + tempContactList(PX_DEBUG_EXP("ThreadContext::tempContactList")), + sortIndexArray(PX_DEBUG_EXP("ThreadContext::sortIndexArray")), + mConstraintSize (0), + mAxisConstraintCount(0), + mSelfConstraintBlocks(NULL), + mMaxPartitions(0), + mMaxSolverPositionIterations(0), + mMaxSolverVelocityIterations(0), + mMaxArticulationLength(0), + mContactDescPtr(NULL), + mFrictionDescPtr(NULL), + mArticulations(PX_DEBUG_EXP("ThreadContext::articulations")) + +{ +#if PX_ENABLE_SIM_STATS + mThreadSimStats.clear(); +#endif + //Defaulted to have space for 16384 bodies + mPartitionNormalizationBitmap.reserve(512); + //Defaulted to have space for 128 partitions (should be more-than-enough) + mConstraintsPerPartition.reserve(128); +} + +void ThreadContext::resizeArrays(PxU32 frictionConstraintDescCount, PxU32 articulationCount) +{ + // resize resizes smaller arrays to the exact target size, which can generate a lot of churn + frictionConstraintDescArray.forceSize_Unsafe(0); + frictionConstraintDescArray.reserve((frictionConstraintDescCount+63)&~63); + + mArticulations.forceSize_Unsafe(0); + mArticulations.reserve(PxMax(Ps::nextPowerOfTwo(articulationCount), 16)); + mArticulations.forceSize_Unsafe(articulationCount); + + mContactDescPtr = contactConstraintDescArray; + mFrictionDescPtr = frictionConstraintDescArray.begin(); +} + +void ThreadContext::reset() +{ + // TODO: move these to the PxcNpThreadContext + mFrictionPatchStreamPair.reset(); + mConstraintBlockStream.reset(); + + mContactDescPtr = contactConstraintDescArray; + mFrictionDescPtr = frictionConstraintDescArray.begin(); + + mAxisConstraintCount = 0; + mMaxSolverPositionIterations = 0; + mMaxSolverVelocityIterations = 0; + mNumDifferentBodyConstraints = 0; + mNumSelfConstraints = 0; + mSelfConstraintBlocks = NULL; + mNumSelfConstraintBlocks = 0; + mConstraintSize = 0; +} + +} +} diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h new file mode 100644 index 000000000..7a77339fe --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h @@ -0,0 +1,255 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef DY_THREADCONTEXT_H +#define DY_THREADCONTEXT_H + +#include "foundation/PxTransform.h" +#include "PxvConfig.h" +#include "CmBitMap.h" +#include "CmMatrix34.h" +#include "PxcThreadCoherentCache.h" +#include "DyThresholdTable.h" +#include "PsAllocator.h" +#include "PsAllocator.h" +#include "GuContactBuffer.h" +#include "DySolverConstraintDesc.h" +#include "PxvDynamics.h" +#include "DyArticulation.h" +#include "DyFrictionPatchStreamPair.h" +#include "PxcConstraintBlockStream.h" +#include "DyCorrelationBuffer.h" + +namespace physx +{ +struct PxsIndexedContactManager; + +namespace Dy +{ + + //Needed by all constraints + struct PxTGSSolverBodyVel + { + PX_ALIGN(16, PxVec3) linearVelocity; //12 + PxU16 nbStaticInteractions; //14 Used to accumulate the number of static interactions + PxU16 maxDynamicPartition; //16 Used to accumualte the max partition of dynamic interactions + PxVec3 angularVelocity; //28 + PxU32 partitionMask; //32 Used in partitioning as a bit-field + PxVec3 deltaAngDt; //44 + PxReal maxAngVel; //48 + PxVec3 deltaLinDt; //60 + PxU16 lockFlags; //62 + bool isKinematic; //63 + PxU8 pad; //64 + + PX_FORCE_INLINE PxReal projectVelocity(const PxVec3& lin, const PxVec3& ang) const + { + return linearVelocity.dot(lin) + angularVelocity.dot(ang); + } + + }; + + //Needed only by prep, integration and 1D constraints + struct PxTGSSolverBodyTxInertia + { + PxTransform deltaBody2World; + PxMat33 sqrtInvInertia; + }; + + struct PxStepSolverBodyData + { + PX_ALIGN(16, PxVec3) originalLinearVelocity; + PxReal maxContactImpulse; + PxVec3 originalAngularVelocity; + PxReal penBiasClamp; + + PxReal invMass; + PxU32 nodeIndex; + PxReal reportThreshold; + PxU32 pad; + + PxReal projectVelocity(const PxVec3& linear, const PxVec3& angular) const + { + return originalLinearVelocity.dot(linear) + originalAngularVelocity.dot(angular); + } + }; + +/*! +Cache information specific to the software implementation(non common). + +See PxcgetThreadContext. + +Not thread-safe, so remember to have one object per thread! + +TODO! refactor this and rename(it is a general per thread cache). Move transform cache into its own class. +*/ +class ThreadContext : + public PxcThreadCoherentCache::EntryBase +{ + PX_NOCOPY(ThreadContext) +public: + +#if PX_ENABLE_SIM_STATS + struct ThreadSimStats + { + void clear() + { + + numActiveConstraints = 0; + numActiveDynamicBodies = 0; + numActiveKinematicBodies = 0; + numAxisSolverConstraints = 0; + + } + + PxU32 numActiveConstraints; + PxU32 numActiveDynamicBodies; + PxU32 numActiveKinematicBodies; + PxU32 numAxisSolverConstraints; + + }; +#endif + + //TODO: tune cache size based on number of active objects. + ThreadContext(PxcNpMemBlockPool* memBlockPool); + void reset(); + void resizeArrays(PxU32 frictionConstraintDescCount, PxU32 articulationCount); + + PX_FORCE_INLINE Ps::Array& getArticulations() { return mArticulations; } + + +#if PX_ENABLE_SIM_STATS + PX_FORCE_INLINE ThreadSimStats& getSimStats() + { + return mThreadSimStats; + } +#endif + + Gu::ContactBuffer mContactBuffer; + + // temporary buffer for correlation + PX_ALIGN(16, CorrelationBuffer mCorrelationBuffer); + + FrictionPatchStreamPair mFrictionPatchStreamPair; // patch streams + + PxsConstraintBlockManager mConstraintBlockManager; // for when this thread context is "lead" on an island + PxcConstraintBlockStream mConstraintBlockStream; // constraint block pool + + + // this stuff is just used for reformatting the solver data. Hopefully we should have a more + // sane format for this when the dust settles - so it's just temporary. If we keep this around + // here we should move these from public to private + + PxU32 mNumDifferentBodyConstraints; + PxU32 mNumDifferentBodyFrictionConstraints; + PxU32 mNumSelfConstraints; + PxU32 mNumSelfFrictionConstraints; + PxU32 mNumSelfConstraintBlocks; + PxU32 mNumSelfConstraintFrictionBlocks; + + Ps::Array mConstraintsPerPartition; + Ps::Array mFrictionConstraintsPerPartition; + Ps::Array mPartitionNormalizationBitmap; + PxsBodyCore** mBodyCoreArray; + PxsRigidBody** mRigidBodyArray; + ArticulationV** mArticulationArray; + Cm::SpatialVector* motionVelocityArray; + PxU32* bodyRemapTable; + PxU32* mNodeIndexArray; + + //Constraint info for normal constraint sovler + PxSolverConstraintDesc* contactConstraintDescArray; + PxU32 contactDescArraySize; + PxSolverConstraintDesc* orderedContactConstraints; + PxConstraintBatchHeader* contactConstraintBatchHeaders; + PxU32 numContactConstraintBatches; + + //Constraint info for partitioning + PxSolverConstraintDesc* tempConstraintDescArray; + + //Additional constraint info for 1d/2d friction model + Ps::Array frictionConstraintDescArray; + Ps::Array frictionConstraintBatchHeaders; + + //Info for tracking compound contact managers (temporary data - could use scratch memory!) + Ps::Array compoundConstraints; + + //Used for sorting constraints. Temporary, could use scratch memory + Ps::Array orderedContactList; + Ps::Array tempContactList; + Ps::Array sortIndexArray; + + Ps::Array mZVector; + Ps::Array mDeltaV; + + + PxU32 numDifferentBodyBatchHeaders; + PxU32 numSelfConstraintBatchHeaders; + + + PxU32 mOrderedContactDescCount; + PxU32 mOrderedFrictionDescCount; + + PxU32 mConstraintSize; + + PxU32 mAxisConstraintCount; + SelfConstraintBlock* mSelfConstraintBlocks; + + SelfConstraintBlock* mSelfConstraintFrictionBlocks; + + PxU32 mMaxPartitions; + PxU32 mMaxFrictionPartitions; + PxU32 mMaxSolverPositionIterations; + PxU32 mMaxSolverVelocityIterations; + PxU32 mMaxArticulationLength; + PxU32 mMaxArticulationSolverLength; + PxU32 mMaxArticulationLinks; + + PxSolverConstraintDesc* mContactDescPtr; + PxSolverConstraintDesc* mStartContactDescPtr; + PxSolverConstraintDesc* mFrictionDescPtr; + +private: + + Ps::Array mArticulations; + +#if PX_ENABLE_SIM_STATS + ThreadSimStats mThreadSimStats; +#endif + + public: + +}; + +} + +} + +#endif //DY_THREADCONTEXT_H diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp new file mode 100644 index 000000000..bb4cd3018 --- /dev/null +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "DyThresholdTable.h" +#include "PsHash.h" +#include "PsUtilities.h" +#include "PsAllocator.h" + +namespace physx +{ + namespace Dy + { + bool ThresholdTable::check(const ThresholdStream& stream, const PxU32 nodeIndexA, const PxU32 nodeIndexB, PxReal dt) + { + PxU32* PX_RESTRICT hashes = mHash; + PxU32* PX_RESTRICT nextIndices = mNexts; + Pair* PX_RESTRICT pairs = mPairs; + + /*const PxsRigidBody* b0 = PxMin(body0, body1); + const PxsRigidBody* b1 = PxMax(body0, body1);*/ + + const PxU32 nA = PxMin(nodeIndexA, nodeIndexB); + const PxU32 nB = PxMax(nodeIndexA, nodeIndexB); + + PxU32 hashKey = computeHashKey(nodeIndexA, nodeIndexB, mHashSize); + + PxU32 pairIndex = hashes[hashKey]; + while(NO_INDEX != pairIndex) + { + Pair& pair = pairs[pairIndex]; + const PxU32 thresholdStreamIndex = pair.thresholdStreamIndex; + PX_ASSERT(thresholdStreamIndex < stream.size()); + const ThresholdStreamElement& otherElement = stream[thresholdStreamIndex]; + if(otherElement.nodeIndexA.index()==nA && otherElement.nodeIndexB.index()==nB) + return (pair.accumulatedForce > (otherElement.threshold * dt)); + pairIndex = nextIndices[pairIndex]; + } + return false; + } + } +} diff --git a/src/PhysX/physx/source/physx/src/NpActor.cpp b/src/PhysX/physx/source/physx/src/NpActor.cpp new file mode 100644 index 000000000..910b7958d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpActor.cpp @@ -0,0 +1,486 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpActor.h" +#include "PxRigidActor.h" +#include "NpConstraint.h" +#include "NpFactory.h" +#include "NpShape.h" +#include "NpPhysics.h" +#include "NpAggregate.h" +#include "NpScene.h" +#include "NpRigidStatic.h" +#include "NpRigidDynamic.h" +#include "NpArticulationLink.h" +#include "CmTransformUtils.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////// + +NpActor::NpActor(const char* name) : + mName(name), + mConnectorArray(NULL) +{ +} + +NpActor::~NpActor() +{ +} + + +typedef Ps::HashMap ConnectorMap; +struct NpActorUserData +{ + PxU32 referenceCount; + ConnectorMap* tmpOriginalConnectors; +}; + +void NpActor::exportExtraData(PxSerializationContext& stream) +{ + const PxCollection& collection = stream.getCollection(); + if(mConnectorArray) + { + PxU32 connectorSize = mConnectorArray->size(); + PxU32 missedCount = 0; + for(PxU32 i = 0; i < connectorSize; ++i) + { + NpConnector& c = (*mConnectorArray)[i]; + PxBase* object = c.mObject; + if(!collection.contains(*object)) + { + ++missedCount; + } + } + + NpConnectorArray* exportConnectorArray = mConnectorArray; + + if(missedCount > 0) + { + exportConnectorArray = NpFactory::getInstance().acquireConnectorArray(); + if(missedCount < connectorSize) + { + exportConnectorArray->reserve(connectorSize - missedCount); + for(PxU32 i = 0; i < connectorSize; ++i) + { + NpConnector& c = (*mConnectorArray)[i]; + PxBase* object = c.mObject; + if(collection.contains(*object)) + { + exportConnectorArray->pushBack(c); + } + } + } + } + + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(exportConnectorArray, sizeof(NpConnectorArray)); + Cm::exportInlineArray(*exportConnectorArray, stream); + + if(missedCount > 0) + NpFactory::getInstance().releaseConnectorArray(exportConnectorArray); + } + stream.writeName(mName); +} + +void NpActor::importExtraData(PxDeserializationContext& context) +{ + if(mConnectorArray) + { + mConnectorArray = context.readExtraData(); + new (mConnectorArray) NpConnectorArray(PxEmpty); + + if(mConnectorArray->size() == 0) + mConnectorArray = NULL; + else + Cm::importInlineArray(*mConnectorArray, context); + } + context.readName(mName); +} + +void NpActor::resolveReferences(PxDeserializationContext& context) +{ + // Resolve connector pointers if needed + if(mConnectorArray) + { + const PxU32 nbConnectors = mConnectorArray->size(); + for(PxU32 i=0; isize(); + PxU32 currentIndex = 0; + while(nbConnectors--) + { + NpConnector& connector = (*mConnectorArray)[currentIndex]; + if (connector.mType == NpConnectorType::eConstraint) + { + NpConstraint* c = static_cast(connector.mObject); + c->actorDeleted(&owner); + + NpScene* s = c->getNpScene(); + if (s) + { + s->getScene().removeConstraint(c->getScbConstraint()); + s->removeFromConstraintList(*c); + } + + removeConnector(owner, currentIndex); + } + else + currentIndex++; + } + } +} + +void NpActor::release(PxActor& owner) +{ + if(mConnectorArray) // Need to test again because the code above might purge the connector array if no element remains + { + PX_ASSERT(mConnectorArray->size() == 1); // At this point only the aggregate should remain + PX_ASSERT((*mConnectorArray)[0].mType == NpConnectorType::eAggregate); + + NpAggregate* a = static_cast((*mConnectorArray)[0].mObject); + bool status = a->removeActorAndReinsert(owner, false); + PX_ASSERT(status); + PX_UNUSED(status); + PX_ASSERT(!mConnectorArray); // Remove should happen in aggregate code + } + + PX_ASSERT(!mConnectorArray); // All the connector objects should have been removed at this point +} + +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 NpActor::findConnector(NpConnectorType::Enum type, PxBase* object) const +{ + if(!mConnectorArray) + return 0xffffffff; + + for(PxU32 i=0; i < mConnectorArray->size(); i++) + { + NpConnector& c = (*mConnectorArray)[i]; + if (c.mType == type && c.mObject == object) + return i; + } + + return 0xffffffff; +} + +void NpActor::addConnector(NpConnectorType::Enum type, PxBase* object, const char* errMsg) +{ + if(!mConnectorArray) + mConnectorArray = NpFactory::getInstance().acquireConnectorArray(); + + PX_CHECK_MSG(findConnector(type, object) == 0xffffffff, errMsg); + PX_UNUSED(errMsg); + + if(mConnectorArray->isInUserMemory() && mConnectorArray->size() == mConnectorArray->capacity()) + { + NpConnectorArray* newConnectorArray = NpFactory::getInstance().acquireConnectorArray(); + newConnectorArray->assign(mConnectorArray->begin(), mConnectorArray->end()); + mConnectorArray->~NpConnectorArray(); + mConnectorArray = newConnectorArray; + } + + NpConnector c(type, object); + mConnectorArray->pushBack(c); +} + +void NpActor::removeConnector(PxActor& /*owner*/, PxU32 index) +{ + PX_ASSERT(mConnectorArray); + PX_ASSERT(index < mConnectorArray->size()); + + mConnectorArray->replaceWithLast(index); + + if(mConnectorArray->size() == 0) + { + if(!mConnectorArray->isInUserMemory()) + NpFactory::getInstance().releaseConnectorArray(mConnectorArray); + else + mConnectorArray->~NpConnectorArray(); + mConnectorArray = NULL; + } +} + +void NpActor::removeConnector(PxActor& owner, NpConnectorType::Enum type, PxBase* object, const char* errorMsg) +{ + PX_CHECK_MSG(mConnectorArray, errorMsg); + PX_UNUSED(errorMsg); + + if(mConnectorArray) + { + PxU32 index = findConnector(type, object); + + PX_CHECK_MSG(index != 0xffffffff, errorMsg); + + removeConnector(owner, index); + } +} + +PxU32 NpActor::getNbConnectors(NpConnectorType::Enum type) const +{ + PxU32 nbConnectors = 0; + + if(mConnectorArray) + { + for(PxU32 i=0; i < mConnectorArray->size(); i++) + { + if ((*mConnectorArray)[i].mType == type) + nbConnectors++; + } + } + + return nbConnectors; +} + +/////////////////////////////////////////////////////////////////////////////// + +NpAggregate* NpActor::getNpAggregate(PxU32& index) const +{ + PX_ASSERT(getNbConnectors(NpConnectorType::eAggregate) <= 1); + + if(mConnectorArray) + { + // PT: TODO: sort by type to optimize this... + for(PxU32 i=0; i < mConnectorArray->size(); i++) + { + NpConnector& c = (*mConnectorArray)[i]; + if (c.mType == NpConnectorType::eAggregate) + { + index = i; + return static_cast(c.mObject); + } + } + } + + return NULL; +} + +void NpActor::setAggregate(NpAggregate* np, PxActor& owner) +{ + PxU32 index = 0xffffffff; + NpAggregate* a = getNpAggregate(index); + + if (!a) + { + PX_ASSERT(np); + addConnector(NpConnectorType::eAggregate, np, "NpActor::setAggregate() failed"); + } + else + { + PX_ASSERT(mConnectorArray); + PX_ASSERT(index != 0xffffffff); + if (!np) + removeConnector(owner, index); + else + (*mConnectorArray)[index].mObject = np; + } +} + +PxAggregate* NpActor::getAggregate() const +{ + PxU32 index = 0xffffffff; + NpAggregate* a = getNpAggregate(index); + return static_cast(a); +} + + +/////////////////////////////////////////////////////////////////////////////// + +void NpActor::removeConstraintsFromScene() +{ + NpConnectorIterator iter = getConnectorIterator(NpConnectorType::eConstraint); + while (PxBase* ser = iter.getNext()) + { + NpConstraint* c = static_cast(ser); + + NpScene* s = c->getNpScene(); + + if (s) + { + s->removeFromConstraintList(*c); + s->getScene().removeConstraint(c->getScbConstraint()); + } + } +} + +void NpActor::addConstraintsToSceneInternal() +{ + if(!mConnectorArray) + return; + + NpConnectorIterator iter = getConnectorIterator(NpConnectorType::eConstraint); + while (PxBase* ser = iter.getNext()) + { + NpConstraint* c = static_cast(ser); + PX_ASSERT(c->getNpScene() == NULL); + + c->markDirty(); // PT: "temp" fix for crash when removing/re-adding jointed actor from/to a scene + + NpScene* s = c->getSceneFromActors(); + if (s) + { + s->addToConstraintList(*c); + s->getScene().addConstraint(c->getScbConstraint()); + } + } +} + + + +/////////////////////////////////////////////////////////////////////////////// + +NpShapeManager* NpActor::getShapeManager(PxRigidActor& actor) +{ + // DS: if the performance here becomes an issue we can use the same kind of offset hack as below + + const PxType actorType = actor.getConcreteType(); + + if (actorType == PxConcreteType::eRIGID_DYNAMIC) + return &static_cast(actor).getShapeManager(); + else if(actorType == PxConcreteType::eRIGID_STATIC) + return &static_cast(actor).getShapeManager(); + else if (actorType == PxConcreteType::eARTICULATION_LINK) + return &static_cast(actor).getShapeManager(); + else + { + PX_ASSERT(0); + return NULL; + } +} + +const NpShapeManager* NpActor::getShapeManager(const PxRigidActor& actor) +{ + // DS: if the performance here becomes an issue we can use the same kind of offset hack as below + + const PxType actorType = actor.getConcreteType(); + + if (actorType == PxConcreteType::eRIGID_DYNAMIC) + return &static_cast(actor).getShapeManager(); + else if(actorType == PxConcreteType::eRIGID_STATIC) + return &static_cast(actor).getShapeManager(); + else if (actorType == PxConcreteType::eARTICULATION_LINK) + return &static_cast(actor).getShapeManager(); + else + { + PX_ASSERT(0); + return NULL; + } +} + +void NpActor::getGlobalPose(PxTransform& globalPose, const NpShape& shape, const PxRigidActor& actor) +{ + const Scb::Actor& scbActor = NpActor::getScbFromPxActor(actor); + const Scb::Shape& scbShape = shape.getScbShape(); + + NpActor::getGlobalPose(globalPose, scbShape, scbActor); +} + +void NpActor::getGlobalPose(PxTransform& globalPose, const Scb::Shape& scbShape, const Scb::Actor& scbActor) +{ + const PxTransform& shape2Actor = scbShape.getShape2Actor(); + + // PT: TODO: duplicated from SqBounds.cpp. Refactor. + const ScbType::Enum actorType = scbActor.getScbType(); + if(actorType==ScbType::eRIGID_STATIC) + { + Cm::getStaticGlobalPoseAligned(static_cast(scbActor).getActor2World(), shape2Actor, globalPose); + } + else + { + PX_ASSERT(actorType==ScbType::eBODY || actorType == ScbType::eBODY_FROM_ARTICULATION_LINK); + + const Scb::Body& body = static_cast(scbActor); + PX_ALIGN(16, PxTransform) kinematicTarget; + const PxU16 sqktFlags = PxRigidBodyFlag::eKINEMATIC | PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES; + const bool useTarget = (PxU16(body.getFlags()) & sqktFlags) == sqktFlags; + const PxTransform& body2World = (useTarget && body.getKinematicTarget(kinematicTarget)) ? kinematicTarget : body.getBody2World(); + Cm::getDynamicGlobalPoseAligned(body2World, shape2Actor, body.getBody2Actor(), globalPose); + } +} + +namespace +{ + template NpActor* pxToNpActor(PxActor *p) + { + return static_cast(static_cast(p)); + } +} + +NpActor::Offsets::Offsets() +{ + for(PxU32 i=0;i(addr); + pxActorToNpActor[PxConcreteType::eRIGID_STATIC] = reinterpret_cast(pxToNpActor(n)) - addr; + pxActorToNpActor[PxConcreteType::eRIGID_DYNAMIC] = reinterpret_cast(pxToNpActor(n)) - addr; + pxActorToNpActor[PxConcreteType::eARTICULATION_LINK] = reinterpret_cast(pxToNpActor(n)) - addr; + pxActorToScbActor[PxConcreteType::eRIGID_STATIC] = PX_OFFSET_OF_RT(NpRigidStatic, getScbActorFast()); + pxActorToScbActor[PxConcreteType::eRIGID_DYNAMIC] = PX_OFFSET_OF_RT(NpRigidDynamic, getScbActorFast()); + pxActorToScbActor[PxConcreteType::eARTICULATION_LINK] = PX_OFFSET_OF_RT(NpArticulationLink, getScbActorFast()); +} + +const NpActor::Offsets NpActor::sOffsets; + + +NpScene* NpActor::getOwnerScene(const PxActor& actor) +{ + const Scb::Actor& scbActor = getScbFromPxActor(actor); + Scb::Scene* scbScene = scbActor.getScbScene(); + return scbScene? static_cast(scbScene->getPxScene()) : NULL; +} + +NpScene* NpActor::getAPIScene(const PxActor& actor) +{ + const Scb::Actor& scbActor = getScbFromPxActor(actor); + Scb::Scene* scbScene = scbActor.getScbSceneForAPI(); + return scbScene? static_cast(scbScene->getPxScene()) : NULL; +} + +void NpActor::onActorRelease(PxActor* actor) +{ + NpFactory::getInstance().onActorRelease(actor); +} diff --git a/src/PhysX/physx/source/physx/src/NpActor.h b/src/PhysX/physx/source/physx/src/NpActor.h new file mode 100644 index 000000000..a4151a4c5 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpActor.h @@ -0,0 +1,163 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ACTOR +#define PX_PHYSICS_NP_ACTOR + +#include "NpConnector.h" +#include "ScbActor.h" // DM: without this inclusion PVD-diabled android build fails, lacking Scb::Actor definition + +namespace physx +{ + class NpShapeManager; + class NpAggregate; + class NpScene; + class NpShape; + +class NpActor +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + // We sometimes pass in the PxActor here even though it's always a base class + // of the objects which inherit from this class too. But passing + // context to functions which need it allows this to be purely a mixin containing shared + // utility code rather than an abstract base class. + +public: +// PX_SERIALIZATION + NpActor(const PxEMPTY) {} + + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + NpActor(const char* name); + + void releaseConstraints(PxRigidActor& owner); + void release(PxActor& owner); + + NpAggregate* getNpAggregate(PxU32& index) const; + void setAggregate(NpAggregate* np, PxActor& owner); + PxAggregate* getAggregate() const; + + void removeConstraintsFromScene(); + PX_FORCE_INLINE void addConstraintsToScene() // inline the fast path for addActors() + { + if(mConnectorArray) + addConstraintsToSceneInternal(); + } + + PX_FORCE_INLINE NpConnectorArray** getConnectorArrayAddress() { return &mConnectorArray;} + PxU32 findConnector(NpConnectorType::Enum type, PxBase* object) const; + void addConnector(NpConnectorType::Enum type, PxBase* object, const char* errMsg); + void removeConnector(PxActor& owner, NpConnectorType::Enum type, PxBase* object, const char* errorMsg); + PxU32 getNbConnectors(NpConnectorType::Enum type) const; + + static NpShapeManager* getShapeManager(PxRigidActor& actor); // bit misplaced here, but we don't want a separate subclass just for this + static const NpShapeManager* getShapeManager(const PxRigidActor& actor); // bit misplaced here, but we don't want a separate subclass just for this + + static void getGlobalPose(PxTransform& globalPose, const NpShape& shape, const PxRigidActor& actor); + static void getGlobalPose(PxTransform& globalPose, const Scb::Shape& scbShape, const Scb::Actor& scbActor); + + static NpActor& getFromPxActor(PxActor& actor) { return *Ps::pointerOffset(&actor, ptrdiff_t(sOffsets.pxActorToNpActor[actor.getConcreteType()])); } + static const NpActor& getFromPxActor(const PxActor& actor) { return *Ps::pointerOffset(&actor, ptrdiff_t(sOffsets.pxActorToNpActor[actor.getConcreteType()])); } + + static Scb::Actor& getScbFromPxActor(PxActor& actor) { return *Ps::pointerOffset(&actor, ptrdiff_t(sOffsets.pxActorToScbActor[actor.getConcreteType()])); } + static const Scb::Actor& getScbFromPxActor(const PxActor& actor) { return *Ps::pointerOffset(&actor, ptrdiff_t(sOffsets.pxActorToScbActor[actor.getConcreteType()])); } + + static NpScene* getAPIScene(const PxActor& actor); // the scene the user thinks the actor is in + static NpScene* getOwnerScene(const PxActor& actor); // the scene the user thinks the actor is in, or from which the actor is pending removal + + PX_FORCE_INLINE NpConnectorIterator getConnectorIterator(NpConnectorType::Enum type) + { + if (mConnectorArray) + return NpConnectorIterator(&mConnectorArray->front(), mConnectorArray->size(), type); + else + return NpConnectorIterator(NULL, 0, type); + } + + // a couple of methods that sever include dependencies in NpActor.h + + static void onActorRelease(PxActor* actor); + + + +template +PxU32 getConnectors(NpConnectorType::Enum type, T** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const +{ + PxU32 nbConnectors = 0; + + if(mConnectorArray) + { + for(PxU32 i=0; i < mConnectorArray->size(); i++) + { + NpConnector& c = (*mConnectorArray)[i]; + if (c.mType == type && nbConnectors < bufferSize && i>=startIndex) + { + userBuffer[nbConnectors] = static_cast(c.mObject); + nbConnectors++; + } + } + } + + return nbConnectors; +} + +protected: + ~NpActor(); + const char* mName; + // Lazy-create array for connector objects like constraints, observers, ... + // Most actors have no such objects, so we bias this class accordingly: + NpConnectorArray* mConnectorArray; + +private: + void addConstraintsToSceneInternal(); + void removeConnector(PxActor& owner, PxU32 index); + struct Offsets + { + size_t pxActorToNpActor[PxConcreteType::ePHYSX_CORE_COUNT]; + size_t pxActorToScbActor[PxConcreteType::ePHYSX_CORE_COUNT]; + Offsets(); + }; +public: + static const Offsets sOffsets; +}; + + + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpActorTemplate.h b/src/PhysX/physx/source/physx/src/NpActorTemplate.h new file mode 100644 index 000000000..9f95ee971 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpActorTemplate.h @@ -0,0 +1,254 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ACTOR_TEMPLATE +#define PX_PHYSICS_NP_ACTOR_TEMPLATE + +#include "PsUserAllocated.h" +#include "NpWriteCheck.h" +#include "NpReadCheck.h" +#include "NpActor.h" +#include "ScbActor.h" +#include "NpScene.h" + +namespace physx +{ + +// PT: only API (virtual) functions should be implemented here. Other shared non-virtual functions should go to NpActor. + +/** +This is an API class. API classes run in a different thread than the simulation. +For the sake of simplicity they have their own methods, and they do not call simulation +methods directly. To set simulation state, they also have their own custom set +methods in the implementation classes. + +Changing the data layout of this class breaks the binary serialization format. +See comments for PX_BINARY_SERIAL_VERSION. +*/ +template +class NpActorTemplate : public APIClass, public NpActor, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + PX_NOCOPY(NpActorTemplate) +public: +// PX_SERIALIZATION + NpActorTemplate(PxBaseFlags baseFlags) : APIClass(baseFlags), NpActor(PxEmpty) {} + virtual void exportExtraData(PxSerializationContext& stream) { NpActor::exportExtraData(stream); } + void importExtraData(PxDeserializationContext& context) { NpActor::importExtraData(context); } + virtual void resolveReferences(PxDeserializationContext& context) { NpActor::resolveReferences(context); } +//~PX_SERIALIZATION + + NpActorTemplate(PxType concreteType, PxBaseFlags baseFlags, const char* name, void* userData); + virtual ~NpActorTemplate(); + + //--------------------------------------------------------------------------------- + // PxActor implementation + //--------------------------------------------------------------------------------- + virtual void release() { NpActor::release(*this); } + + // The rule is: If an API method is used somewhere in here, it has to be redeclared, else GCC whines + virtual PxActorType::Enum getType() const = 0; + + virtual PxScene* getScene() const; + + // Debug name + virtual void setName(const char*); + virtual const char* getName() const; + + virtual PxBounds3 getWorldBounds(float inflation=1.01f) const = 0; + + // Flags + virtual void setActorFlag(PxActorFlag::Enum flag, bool value); + virtual void setActorFlags(PxActorFlags inFlags); + virtual PxActorFlags getActorFlags() const; + + // Dominance + virtual void setDominanceGroup(PxDominanceGroup dominanceGroup); + virtual PxDominanceGroup getDominanceGroup() const; + + // Multiclient + virtual void setOwnerClient( PxClientID inClient ); + virtual PxClientID getOwnerClient() const; + + // Aggregates + virtual PxAggregate* getAggregate() const { return NpActor::getAggregate(); } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- +protected: + PX_FORCE_INLINE void setActorFlagInternal(PxActorFlag::Enum flag, bool value); + PX_FORCE_INLINE void setActorFlagsInternal(PxActorFlags inFlags); +}; + +/////////////////////////////////////////////////////////////////////////////// + +template +NpActorTemplate::NpActorTemplate(PxType concreteType, + PxBaseFlags baseFlags, + const char* name, + void* actorUserData) +:APIClass(concreteType, baseFlags), +NpActor(name) +{ + // don't ref Scb actor here, it hasn't been assigned yet + + APIClass::userData = actorUserData; +} + + +template +NpActorTemplate::~NpActorTemplate() +{ + NpActor::onActorRelease(this); +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: this one is very slow +template +PxScene* NpActorTemplate::getScene() const +{ + return NpActor::getAPIScene(*this); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +void NpActorTemplate::setName(const char* debugName) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + mName = debugName; + +#if PX_SUPPORT_PVD + Scb::Scene* scbScene = getScbFromPxActor(*this).getScbSceneForAPI(); + Scb::Actor& scbActor = NpActor::getScbFromPxActor(*this); + //Name changing is not bufferred + if(scbScene) + scbScene->getScenePvdClient().updatePvdProperties(&scbActor); +#endif +} + +template +const char* NpActorTemplate::getName() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mName; +} + +/////////////////////////////////////////////////////////////////////////////// + +template +void NpActorTemplate::setDominanceGroup(PxDominanceGroup dominanceGroup) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + NpActor::getScbFromPxActor(*this).setDominanceGroup(dominanceGroup); +} + + +template +PxDominanceGroup NpActorTemplate::getDominanceGroup() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return NpActor::getScbFromPxActor(*this).getDominanceGroup(); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +void NpActorTemplate::setOwnerClient( PxClientID inId ) +{ + if ( NpActor::getOwnerScene(*this) != NULL ) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Attempt to set the client id when an actor is already in a scene."); + } + else + NpActor::getScbFromPxActor(*this).setOwnerClient( inId ); +} + +template +PxClientID NpActorTemplate::getOwnerClient() const +{ + return NpActor::getScbFromPxActor(*this).getOwnerClient(); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +PX_FORCE_INLINE void NpActorTemplate::setActorFlagInternal(PxActorFlag::Enum flag, bool value) +{ + Scb::Actor& a = NpActor::getScbFromPxActor(*this); + if (value) + a.setActorFlags( a.getActorFlags() | flag ); + else + a.setActorFlags( a.getActorFlags() & (~PxActorFlags(flag)) ); +} + +template +PX_FORCE_INLINE void NpActorTemplate::setActorFlagsInternal(PxActorFlags inFlags) +{ + Scb::Actor& a = NpActor::getScbFromPxActor(*this); + a.setActorFlags( inFlags ); +} + +template +void NpActorTemplate::setActorFlag(PxActorFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + setActorFlagInternal(flag, value); +} + +template +void NpActorTemplate::setActorFlags(PxActorFlags inFlags) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + setActorFlagsInternal(inFlags); +} + +template +PxActorFlags NpActorTemplate::getActorFlags() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return NpActor::getScbFromPxActor(*this).getActorFlags(); +} + +/////////////////////////////////////////////////////////////////////////////// + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpAggregate.cpp b/src/PhysX/physx/source/physx/src/NpAggregate.cpp new file mode 100644 index 000000000..2154fadd4 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpAggregate.cpp @@ -0,0 +1,421 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpAggregate.h" + +/////////////////////////////////////////////////////////////////////////////// + +#include "PxActor.h" +#include "NpRigidStatic.h" +#include "NpRigidDynamic.h" +#include "NpArticulation.h" +#include "NpActor.h" +#include "GuBVHStructure.h" +#include "CmUtils.h" +#include "NpArticulationTemplate.h" + +using namespace physx; + +PX_FORCE_INLINE void setAggregate(NpAggregate* aggregate, PxActor& actor) +{ + NpActor& np = NpActor::getFromPxActor(actor); + np.setAggregate(aggregate, actor); +} + +/////////////////////////////////////////////////////////////////////////////// + + +NpAggregate::NpAggregate(PxU32 maxActors, bool selfCollisions) +: PxAggregate(PxConcreteType::eAGGREGATE, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mAggregate(this, maxActors, selfCollisions) +, mNbActors(0) +{ + mActors = reinterpret_cast(PX_ALLOC(sizeof(PxActor*)*maxActors, "PxActor*")); +} + +NpAggregate::~NpAggregate() +{ + NpFactory::getInstance().onAggregateRelease(this); + if(getBaseFlags()&PxBaseFlag::eOWNS_MEMORY) + PX_FREE(mActors); +} + +void NpAggregate::removeAndReinsert(PxActor& actor, bool reinsert) +{ + NpActor& np = NpActor::getFromPxActor(actor); + Scb::Actor& scb = NpActor::getScbFromPxActor(actor); + + np.setAggregate(NULL, actor); + + mAggregate.removeActor(scb, reinsert); +} + +void NpAggregate::release() +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_SIMD_GUARD; + + NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, NULL); + + /* + "An aggregate should be empty when it gets released. If it isn't, the behavior should be: remove the actors from + the aggregate, then remove the aggregate from the scene (if any) then delete it. I guess that implies the actors + get individually reinserted into the broad phase if the aggregate is in a scene." + */ + for(PxU32 i=0;igetType() == PxActorType::eARTICULATION_LINK) + { + NpArticulationLink* link = static_cast(mActors[i]); + PxArticulationImpl* impl = reinterpret_cast(link->getRoot().getImpl()); + impl->setAggregate(NULL); + } + + removeAndReinsert(*mActors[i], true); + } + + NpScene* s = getAPIScene(); + if(s) + { + s->getScene().removeAggregate(getScbAggregate()); + s->removeFromAggregateList(*this); + } + + mAggregate.destroy(); +} + +void NpAggregate::addActorInternal(PxActor& actor, NpScene& s, const PxBVHStructure* bvhStructure) +{ + if (actor.getType() != PxActorType::eARTICULATION_LINK) + { + Scb::Actor& scb = NpActor::getScbFromPxActor(actor); + + mAggregate.addActor(scb); + + s.addActorInternal(actor, bvhStructure); + } + else if (!actor.getScene()) // This check makes sure that a link of an articulation gets only added once. + { + NpArticulationLink& al = static_cast(actor); + PxArticulationBase& npArt = al.getRoot(); + for(PxU32 i=0; i < npArt.getNbLinks(); i++) + { + PxArticulationLink* link; + npArt.getLinks(&link, 1, i); + mAggregate.addActor(static_cast(link)->getScbActorFast()); + } + + s.addArticulationInternal(npArt); + } +} + +bool NpAggregate::addActor(PxActor& actor, const PxBVHStructure* bvhStructure) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_SIMD_GUARD; + + if(mNbActors==mAggregate.getMaxActorCount()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add actor to aggregate, max number of actors reached"); + return false; + } + + if(actor.getAggregate()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add actor to aggregate, actor already belongs to an aggregate"); + return false; + } + + if(actor.getScene()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add actor to aggregate, actor already belongs to a scene"); + return false; + } + + if(actor.getType() == PxActorType::eARTICULATION_LINK) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add articulation link to aggregate, only whole articulations can be added"); + return false; + } + + setAggregate(this, actor); + + mActors[mNbActors++] = &actor; + + // PT: when an object is added to a aggregate at runtime, i.e. when the aggregate has already been added to the scene, + // we need to immediately add the newcomer to the scene as well. + NpScene* s = getAPIScene(); + if(s) + { + addActorInternal(actor, *s, bvhStructure); + } + else + { + // A.B. if BVH structure is provided we need to keep it stored till the aggregate is inseted into a scene + if(bvhStructure) + { + PxBVHStructure* bvhStructureMutable = const_cast(bvhStructure); + static_cast(bvhStructureMutable)->incRefCount(); + NpActor::getFromPxActor(actor).addConnector(NpConnectorType::eBvhStructure, bvhStructureMutable, "PxBVHStructure already added to the PxActor!"); + } + } + + return true; +} + +bool NpAggregate::removeActorAndReinsert(PxActor& actor, bool reinsert) +{ + for(PxU32 i=0;i(NpConnectorType::eBvhStructure, &bvhStructure, 1)) + { + np.removeConnector(actor, NpConnectorType::eBvhStructure, bvhStructure, "PxBVHStructure connector could not have been removed!"); + bvhStructure->decRefCount(); + } + } + + // PT: there are really 2 cases here: + // a) the user just wants to remove the actor from the aggregate, but the actor is still alive so if the aggregate has been added to a scene, + // we must reinsert the removed actor to that same scene + // b) this is called by the framework when releasing an actor, in which case we don't want to reinsert it anywhere. + // + // We assume that when called by the user, we always want to reinsert. The framework however will call the internal function + // without reinsertion. + return removeActorAndReinsert(actor, true); +} + +bool NpAggregate::addArticulation(PxArticulationBase& art) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_SIMD_GUARD; + + if((mNbActors+art.getNbLinks()) > mAggregate.getMaxActorCount()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add articulation links, max number of actors reached"); + return false; + } + + if(art.getAggregate()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add articulation to aggregate, articulation already belongs to an aggregate"); + return false; + } + + if(art.getScene()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't add articulation to aggregate, articulation already belongs to a scene"); + return false; + } + + PxArticulationImpl* impl = reinterpret_cast(art.getImpl()); + impl->setAggregate(this); + NpArticulationLink* const* links = impl->getLinks(); + for(PxU32 i=0; i < impl->getNbLinks(); i++) + { + NpArticulationLink& l = *links[i]; + + setAggregate(this, l); + + mActors[mNbActors++] = &l; + + mAggregate.addActor(l.getScbActorFast()); + } + + // PT: when an object is added to a aggregate at runtime, i.e. when the aggregate has already been added to the scene, + // we need to immediately add the newcomer to the scene as well. + NpScene* s = getAPIScene(); + if(s) + { + s->addArticulationInternal(art); + } + + return true; +} + +bool NpAggregate::removeArticulationAndReinsert(PxArticulationBase& art, bool reinsert) +{ + bool found = false; + PxU32 idx = 0; + while(idx < mNbActors) + { + if ((mActors[idx]->getType() == PxActorType::eARTICULATION_LINK) && (&static_cast(mActors[idx])->getRoot() == &art)) + { + PxActor* a = mActors[idx]; + mActors[idx] = mActors[--mNbActors]; + removeAndReinsert(*a, reinsert); + found = true; + } + else + idx++; + } + + reinterpret_cast(art.getImpl())->setAggregate(NULL); + + if (!found) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxAggregate: can't remove articulation, articulation doesn't belong to aggregate"); + return found; +} + +bool NpAggregate::removeArticulation(PxArticulationBase& art) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_SIMD_GUARD; + + // see comments in removeActor() + return removeArticulationAndReinsert(art, true); +} + +PxU32 NpAggregate::getNbActors() const +{ + NP_READ_CHECK(getOwnerScene()); + return mNbActors; +} + +PxU32 NpAggregate::getMaxNbActors() const +{ + NP_READ_CHECK(getOwnerScene()); + return mAggregate.getMaxActorCount(); +} + +PxU32 NpAggregate::getActors(PxActor** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(getOwnerScene()); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mActors, getCurrentSizeFast()); +} + +PxScene* NpAggregate::getScene() +{ + return getAPIScene(); +} + +NpScene* NpAggregate::getAPIScene() const +{ + return mAggregate.getScbSceneForAPI() ? static_cast(mAggregate.getScbSceneForAPI()->getPxScene()) : NULL; +} + +NpScene* NpAggregate::getOwnerScene() const +{ + return mAggregate.getScbScene() ? static_cast(mAggregate.getScbScene()->getPxScene()) : NULL; +} + +bool NpAggregate::getSelfCollision() const +{ + NP_READ_CHECK(getOwnerScene()); + return mAggregate.getSelfCollide(); +} + +// PX_SERIALIZATION +void NpAggregate::exportExtraData(PxSerializationContext& stream) +{ + if(mActors) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mActors, mNbActors * sizeof(PxActor*)); + } +} + +void NpAggregate::importExtraData(PxDeserializationContext& context) +{ + if(mActors) + mActors = context.readExtraData(mNbActors); +} + +void NpAggregate::resolveReferences(PxDeserializationContext& context) +{ + // Resolve actor pointers if needed + for(PxU32 i=0; i < mNbActors; i++) + { + context.translatePxBase(mActors[i]); + { + //update aggregate if mActors is in external reference + NpActor& np = NpActor::getFromPxActor(*mActors[i]); + if(np.getAggregate() == NULL) + { + np.setAggregate(this, *mActors[i]); + } + if(mActors[i]->getType() == PxActorType::eARTICULATION_LINK) + { + PxArticulationBase& articulation = static_cast(mActors[i])->getRoot(); + if(!articulation.getAggregate()) + reinterpret_cast(articulation.getImpl())->setAggregate(this); + } + } + } +} + +NpAggregate* NpAggregate::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpAggregate* obj = new (address) NpAggregate(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(NpAggregate); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void NpAggregate::requiresObjects(PxProcessPxBaseCallback& c) +{ + for(PxU32 i=0; i < mNbActors; i++) + { + PxArticulationLink* link = mActors[i]->is(); + if(link) + c.process(link->getArticulation()); + else + c.process(*mActors[i]); + } +} +// ~PX_SERIALIZATION diff --git a/src/PhysX/physx/source/physx/src/NpAggregate.h b/src/PhysX/physx/source/physx/src/NpAggregate.h new file mode 100644 index 000000000..11200dc35 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpAggregate.h @@ -0,0 +1,100 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_AGGREGATE +#define PX_PHYSICS_NP_AGGREGATE + +#include "CmPhysXCommon.h" +#include "PxAggregate.h" +#include "ScbAggregate.h" +#include "PsUserAllocated.h" + +namespace physx +{ + +class NpScene; + +class NpAggregate : public PxAggregate, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpAggregate(PxBaseFlags baseFlags) : PxAggregate(baseFlags), mAggregate(PxEmpty) {} + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback& c); + static NpAggregate* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + NpAggregate(PxU32 maxActors, bool selfCollision); + virtual ~NpAggregate(); + + virtual void release(); + virtual bool addActor(PxActor&, const PxBVHStructure* ); + virtual bool removeActor(PxActor&); + virtual bool addArticulation(PxArticulationBase&); + virtual bool removeArticulation(PxArticulationBase&); + + virtual PxU32 getNbActors() const; + virtual PxU32 getMaxNbActors() const; + virtual PxU32 getActors(PxActor** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + virtual PxScene* getScene(); + virtual bool getSelfCollision() const; + + PX_FORCE_INLINE PxU32 getCurrentSizeFast() const { return mNbActors; } + PX_FORCE_INLINE PxActor* getActorFast(PxU32 i) const { return mActors[i]; } + PX_FORCE_INLINE bool getSelfCollideFast() const { return mAggregate.getSelfCollide(); } + + NpScene* getAPIScene() const; + NpScene* getOwnerScene() const; // the scene the user thinks the actor is in, or from which the actor is pending removal + + void addActorInternal(PxActor& actor, NpScene& s, const PxBVHStructure* bvhStructure); + void removeAndReinsert(PxActor& actor, bool reinsert); + bool removeActorAndReinsert(PxActor& actor, bool reinsert); + bool removeArticulationAndReinsert(PxArticulationBase& art, bool reinsert); + + PX_FORCE_INLINE Scb::Aggregate& getScbAggregate() { return mAggregate; } + +private: + Scb::Aggregate mAggregate; + PxU32 mNbActors; + PxActor** mActors; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulation.cpp b/src/PhysX/physx/source/physx/src/NpArticulation.cpp new file mode 100644 index 000000000..4ce735c67 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulation.cpp @@ -0,0 +1,247 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxTransform.h" +#include "NpArticulation.h" +#include "NpArticulationLink.h" +#include "NpWriteCheck.h" +#include "NpReadCheck.h" +#include "NpFactory.h" +#include "ScbArticulation.h" +#include "NpAggregate.h" +#include "CmUtils.h" +#include "NpArticulationJoint.h" + +namespace physx +{ + +// PX_SERIALIZATION +void NpArticulation::requiresObjects(PxProcessPxBaseCallback& c) +{ + // Collect articulation links + const PxU32 nbLinks = mImpl.mArticulationLinks.size(); + for(PxU32 i=0; iimportExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +NpArticulation::NpArticulation() + : NpArticulationTemplate(PxConcreteType::eARTICULATION, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +{ + PxArticulationBase::userData = NULL; + mType = PxArticulationBase::eMaximumCoordinate; + mImpl.mArticulation.setArticulationType(PxArticulationBase::eMaximumCoordinate); +} + + +NpArticulation::~NpArticulation() +{ + NpFactory::getInstance().onArticulationRelease(this); +} + + +PxU32 NpArticulation::getInternalDriveIterations() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getInternalDriveIterations(); +} + +void NpArticulation::setInternalDriveIterations(PxU32 iterations) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setInternalDriveIterations(iterations); +} + +PxU32 NpArticulation::getExternalDriveIterations() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getExternalDriveIterations(); +} + +void NpArticulation::setExternalDriveIterations(PxU32 iterations) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setExternalDriveIterations(iterations); +} + +PxU32 NpArticulation::getMaxProjectionIterations() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getMaxProjectionIterations(); +} + +void NpArticulation::setMaxProjectionIterations(PxU32 iterations) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setMaxProjectionIterations(iterations); +} + +PxReal NpArticulation::getSeparationTolerance() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getSeparationTolerance(); +} + +void NpArticulation::setSeparationTolerance(PxReal tolerance) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setSeparationTolerance(tolerance); +} + + +PxArticulationDriveCache* NpArticulation::createDriveCache(PxReal compliance, PxU32 driveIterations) const +{ + PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::createDriveCache: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + return reinterpret_cast(mImpl.mArticulation.getScArticulation().createDriveCache(compliance, driveIterations)); +} + + +void NpArticulation::updateDriveCache(PxArticulationDriveCache& cache, PxReal compliance, PxU32 driveIterations) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::updateDriveCache: object must be in a scene"); + + Sc::ArticulationDriveCache& c = reinterpret_cast(cache); + PX_CHECK_AND_RETURN(mImpl.mArticulation.getScArticulation().getCacheLinkCount(c) == mImpl.mArticulationLinks.size(), "PxArticulation::updateDriveCache: Articulation size has changed; drive cache is invalid"); + + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + mImpl.mArticulation.getScArticulation().updateDriveCache(c, compliance, driveIterations); +} + +void NpArticulation::releaseDriveCache(PxArticulationDriveCache&cache ) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::releaseDriveCache: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + mImpl.mArticulation.getScArticulation().releaseDriveCache(reinterpret_cast(cache)); +} + +void NpArticulation::applyImpulse(PxArticulationLink* link, + const PxArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::applyImpulse: object must be in a scene"); + PX_CHECK_AND_RETURN(force.isFinite() && torque.isFinite(), "PxArticulation::applyImpulse: invalid force/torque"); + const Sc::ArticulationDriveCache& c = reinterpret_cast(driveCache); + PX_CHECK_AND_RETURN(mImpl.mArticulation.getScArticulation().getCacheLinkCount(c) == mImpl.mArticulationLinks.size(), "PxArticulation::applyImpulse: Articulation size has changed; drive cache is invalid"); + + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + if(mImpl.isSleeping()) + mImpl.wakeUp(); + + mImpl.mArticulation.getScArticulation().applyImpulse(static_cast(link)->getScbBodyFast().getScBody(), c,force, torque); + for(PxU32 i=0;igetScbBodyFast().getScBody().getLinearVelocity(), + av = mImpl.mArticulationLinks[i]->getScbBodyFast().getScBody().getAngularVelocity(); + mImpl.mArticulationLinks[i]->setLinearVelocity(lv); + mImpl.mArticulationLinks[i]->setAngularVelocity(av); + } +} + +void NpArticulation::computeImpulseResponse(PxArticulationLink* link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const PxArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const +{ + + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeImpulseResponse: object must be in a scene"); + PX_CHECK_AND_RETURN(force.isFinite() && torque.isFinite(), "PxArticulation::computeImpulseResponse: invalid force/torque"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + const Sc::ArticulationDriveCache& c = reinterpret_cast(driveCache); + PX_CHECK_AND_RETURN(mImpl.mArticulation.getScArticulation().getCacheLinkCount(c) == mImpl.mArticulationLinks.size(), "PxArticulation::computeImpulseResponse: Articulation size has changed; drive cache is invalid"); + PX_UNUSED(&c); + + mImpl.mArticulation.getScArticulation().computeImpulseResponse(static_cast(link)->getScbBodyFast().getScBody(), + linearResponse, angularResponse, + reinterpret_cast(driveCache), + force, torque); +} + +Scb::Body* NpArticulationGetRootFromScb(Scb::Articulation&c) +{ + const size_t offset = size_t(&(reinterpret_cast(0)->mImpl.getScbArticulation())); + NpArticulation* np = reinterpret_cast(reinterpret_cast(&c) - offset); + + NpArticulationLink* a = np->mImpl.getRoot(); + + return a ? &a->getScbBodyFast() : NULL; +} + +PxArticulationJointBase* NpArticulation::createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame) +{ + return NpFactory::getInstance().createNpArticulationJoint(static_cast(parent), parentFrame, static_cast(child), childFrame); +} +void NpArticulation::releaseArticulationJoint(PxArticulationJointBase* joint) +{ + NpFactory::getInstance().releaseArticulationJointToPool(*static_cast(joint)); +} + +} diff --git a/src/PhysX/physx/source/physx/src/NpArticulation.h b/src/PhysX/physx/source/physx/src/NpArticulation.h new file mode 100644 index 000000000..8192e4373 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulation.h @@ -0,0 +1,143 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ARTICULATION +#define PX_PHYSICS_NP_ARTICULATION + +#include "PxArticulation.h" +#include "CmPhysXCommon.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +#include "ScbArticulation.h" +#include "NpArticulationLink.h" +#include "NpArticulationTemplate.h" +#include "NpArticulationJoint.h" + +namespace physx +{ + +class NpArticulationLink; +class NpScene; +class NpAggregate; +class PxAggregate; + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4250) +#endif + + +class NpArticulation : public NpArticulationTemplate +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + +public: + virtual ~NpArticulation(); + +// PX_SERIALIZATION + NpArticulation(PxBaseFlags baseFlags) : NpArticulationTemplate(baseFlags) + {} + + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback& c); + static NpArticulation* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + //--------------------------------------------------------------------------------- + // PxArticulation implementation + //--------------------------------------------------------------------------------- + + virtual PxU32 getInternalDriveIterations() const; + virtual void setInternalDriveIterations(PxU32 iterations); + + virtual PxU32 getExternalDriveIterations() const; + virtual void setExternalDriveIterations(PxU32 iterations); + + virtual PxU32 getMaxProjectionIterations() const; + virtual void setMaxProjectionIterations(PxU32 iterations); + + virtual PxReal getSeparationTolerance() const; + virtual void setSeparationTolerance(PxReal tolerance); + + + virtual PxArticulationDriveCache* + createDriveCache(PxReal compliance, PxU32 driveIterations) const; + + virtual void updateDriveCache(PxArticulationDriveCache& cache, PxReal compliance, PxU32 driveIterations) const; + + virtual void releaseDriveCache(PxArticulationDriveCache&) const; + + virtual void applyImpulse(PxArticulationLink*, + const PxArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque); + + virtual void computeImpulseResponse(PxArticulationLink*, + PxVec3& linearResponse, + PxVec3& angularResponse, + const PxArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const; + + virtual const char* getConcreteTypeName() const { return "PxArticulation"; } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpArticulation(); + + + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulation", name) || PxBase::isKindOf(name); } + + virtual PxArticulationJointBase* createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame); + virtual void releaseArticulationJoint(PxArticulationJointBase* joint); +}; + +#if PX_VC +#pragma warning(pop) +#endif + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp b/src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp new file mode 100644 index 000000000..eea8fe4cb --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp @@ -0,0 +1,381 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpCast.h" +#include "NpWriteCheck.h" +#include "NpReadCheck.h" + +namespace physx +{ +// PX_SERIALIZATION +void NpArticulationJoint::resolveReferences(PxDeserializationContext& context) +{ + mImpl.resolveReferences(context); +} + +NpArticulationJoint* NpArticulationJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpArticulationJoint* obj = new (address) NpArticulationJoint(PxBaseFlags(0)); + address += sizeof(NpArticulationJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} +// ~PX_SERIALIZATION + +NpArticulationJoint::NpArticulationJoint(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame) +: NpArticulationJointTemplate(parent, parentFrame, child, childFrame) +{ +} + + +NpArticulationJoint::~NpArticulationJoint() +{ +} + + +void NpArticulationJoint::setTargetOrientation(const PxQuat& p) +{ + PX_CHECK_AND_RETURN(mImpl.getScbArticulationJoint().getDriveType() != PxArticulationJointDriveType::eTARGET || p.isUnit(), "NpArticulationJoint::setTargetOrientation, quat orientation is not valid."); + PX_CHECK_AND_RETURN(mImpl.getScbArticulationJoint().getDriveType() != PxArticulationJointDriveType::eERROR || (p.getImaginaryPart().isFinite() && p.w == 0), "NpArticulationJoint::setTargetOrientation rotation vector orientation is not valid."); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTargetOrientation(p); +} + + +PxQuat NpArticulationJoint::getTargetOrientation() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTargetOrientation(); +} + + +void NpArticulationJoint::setTargetVelocity(const PxVec3& v) +{ + PX_CHECK_AND_RETURN(v.isFinite(), "NpArticulationJoint::setTargetVelocity v is not valid."); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTargetVelocity(v); +} + + +PxArticulationJointDriveType::Enum NpArticulationJoint::getDriveType() const +{ + NP_READ_CHECK(getOwnerScene()); + return mImpl.getScbArticulationJoint().getDriveType(); +} + +void NpArticulationJoint::setDriveType(PxArticulationJointDriveType::Enum driveType) +{ + NP_WRITE_CHECK(getOwnerScene()); + mImpl.getScbArticulationJoint().setDriveType(driveType); +} + + +PxVec3 NpArticulationJoint::getTargetVelocity() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTargetVelocity(); +} + + +void NpArticulationJoint::setStiffness(PxReal s) +{ + PX_CHECK_AND_RETURN(PxIsFinite(s) && s >= 0.0f, "PxArticulationJoint::setStiffness: spring coefficient must be >= 0!"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setStiffness(s); +} + + +PxReal NpArticulationJoint::getStiffness() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getStiffness(); +} + + +void NpArticulationJoint::setDamping(PxReal d) +{ + PX_CHECK_AND_RETURN(PxIsFinite(d) && d >= 0.0f, "PxArticulationJoint::setDamping: damping coefficient must be >= 0!"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setDamping(d); +} + + +PxReal NpArticulationJoint::getDamping() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getDamping(); +} + + +void NpArticulationJoint::setSwingLimitContactDistance(PxReal d) +{ + PX_CHECK_AND_RETURN(PxIsFinite(d) && d >= 0.0f, "PxArticulationJoint::setSwingLimitContactDistance: padding coefficient must be > 0!"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setSwingLimitContactDistance(d); +} + + +PxReal NpArticulationJoint::getSwingLimitContactDistance() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getSwingLimitContactDistance(); +} + + +void NpArticulationJoint::setTwistLimitContactDistance(PxReal d) +{ + PX_CHECK_AND_RETURN(PxIsFinite(d) && d >= 0.0f, "PxArticulationJoint::setTwistLimitContactDistance: padding coefficient must be > 0!"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTwistLimitContactDistance(d); +} + + +PxReal NpArticulationJoint::getTwistLimitContactDistance() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTwistLimitContactDistance(); +} + +PxArticulationJointType::Enum NpArticulationJoint::getJointType() const +{ + NP_READ_CHECK(getOwnerScene()); + return mImpl.getScbArticulationJoint().getJointType(); +} + +void NpArticulationJoint::setJointType(PxArticulationJointType::Enum jointType) +{ + NP_WRITE_CHECK(getOwnerScene()); + mImpl.getScbArticulationJoint().setJointType(jointType); +} + +void NpArticulationJoint::setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) +{ + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setMotion(axis, motion); + + reinterpret_cast(getChild().getArticulation().getImpl())->increaseCacheVersion(); +} + +PxArticulationMotion::Enum NpArticulationJoint::getMotion(PxArticulationAxis::Enum axis) const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getMotion(axis); +} + +void NpArticulationJoint::setFrictionCoefficient(const PxReal coefficient) +{ + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setFrictionCoefficient(coefficient); + +} + +PxReal NpArticulationJoint::getFrictionCoefficient() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getFrictionCoefficient(); +} + +void NpArticulationJoint::setInternalCompliance(PxReal r) +{ + PX_CHECK_AND_RETURN(PxIsFinite(r) && r>0, "PxArticulationJoint::setInternalCompliance: compliance must be > 0"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setInternalCompliance(r); +} + + + +PxReal NpArticulationJoint::getInternalCompliance() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getInternalCompliance(); +} + + +void NpArticulationJoint::setExternalCompliance(PxReal r) +{ + PX_CHECK_AND_RETURN(PxIsFinite(r) && r>0, "PxArticulationJoint::setExternalCompliance: compliance must be > 0"); + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setExternalCompliance(r); +} + + +PxReal NpArticulationJoint::getExternalCompliance() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getExternalCompliance(); +} + + +void NpArticulationJoint::setSwingLimit(PxReal yLimit, PxReal zLimit) +{ + PX_CHECK_AND_RETURN(PxIsFinite(yLimit) && PxIsFinite(zLimit) && yLimit > 0 && zLimit > 0 && yLimit < PxPi && zLimit < PxPi, + "PxArticulationJoint::setSwingLimit: values must be >0 and < Pi"); + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setSwingLimit(yLimit, zLimit); +} + + +void NpArticulationJoint::getSwingLimit(PxReal &yLimit, PxReal &zLimit) const +{ + NP_READ_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().getSwingLimit(yLimit, zLimit); +} + + +void NpArticulationJoint::setTangentialStiffness(PxReal stiffness) +{ + PX_CHECK_AND_RETURN(PxIsFinite(stiffness) && stiffness >= 0, "PxArticulationJoint::setTangentialStiffness: stiffness must be > 0"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTangentialStiffness(stiffness); +} + + +PxReal NpArticulationJoint::getTangentialStiffness() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTangentialStiffness(); +} + + +void NpArticulationJoint::setTangentialDamping(PxReal damping) +{ + PX_CHECK_AND_RETURN(PxIsFinite(damping) && damping >= 0, "PxArticulationJoint::setTangentialDamping: damping must be > 0"); + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTangentialDamping(damping); +} + + +PxReal NpArticulationJoint::getTangentialDamping() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTangentialDamping(); +} + + + +void NpArticulationJoint::setSwingLimitEnabled(bool e) +{ + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setSwingLimitEnabled(e); +} + + +bool NpArticulationJoint::getSwingLimitEnabled() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getSwingLimitEnabled(); +} + + +void NpArticulationJoint::setTwistLimit(PxReal lower, PxReal upper) +{ + PX_CHECK_AND_RETURN(PxIsFinite(lower) && PxIsFinite(upper) && lower-PxPi && upper < PxPi, "PxArticulationJoint::setTwistLimit: illegal parameters"); + + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTwistLimit(lower, upper); +} + + +void NpArticulationJoint::getTwistLimit(PxReal &lower, PxReal &upper) const +{ + NP_READ_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().getTwistLimit(lower, upper); +} + + +void NpArticulationJoint::setTwistLimitEnabled(bool e) +{ + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setTwistLimitEnabled(e); +} + + +bool NpArticulationJoint::getTwistLimitEnabled() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getTwistLimitEnabled(); +} + + +void NpArticulationJointGetBodiesFromScb(Scb::ArticulationJoint&c, Scb::Body*&b0, Scb::Body*&b1) +{ + NpArticulationJoint* np = const_cast(getNpArticulationJoint(&c)); + + NpArticulationLink& a0 = np->getParent(), & a1 = np->getChild(); + b0 = &a0.getScbBodyFast(); + b1 = &a1.getScbBodyFast(); +} + + +} diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJoint.h b/src/PhysX/physx/source/physx/src/NpArticulationJoint.h new file mode 100644 index 000000000..1c4189771 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationJoint.h @@ -0,0 +1,357 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ARTICULATION_JOINT +#define PX_PHYSICS_NP_ARTICULATION_JOINT + +#include "PxArticulationJoint.h" +#include "ScbArticulationJoint.h" +#include "NpArticulationTemplate.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +namespace physx +{ + +class NpScene; +class NpArticulationLink; + +class PxArticulationJointImpl +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + Scb::ArticulationJoint mJoint; + NpArticulationLink* mParent; + NpArticulationLink* mChild; + PxArticulationBase::Enum mType; + + PX_INLINE PxArticulationJointImpl(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame, + PxArticulationBase::Enum type); + +// PX_SERIALIZATION + PX_INLINE PxArticulationJointImpl(const PxEMPTY) : mJoint(PxEmpty) {} +//~PX_SERIALIZATION + + PX_INLINE NpScene* getOwnerScene() const; // the scene the user thinks the actor is in, or from which the actor is pending removal + + PX_INLINE void release(); + + PX_INLINE const Scb::ArticulationJoint& getScbArticulationJoint() const { return mJoint; } + PX_INLINE Scb::ArticulationJoint& getScbArticulationJoint() { return mJoint; } + + PX_INLINE PxArticulationLink& getParentArticulationLink() const; + PX_INLINE PxArticulationLink& getChildArticulationLink() const; + + PX_INLINE PxTransform getParentPose() const; + PX_INLINE void setParentPose(const PxTransform&); + + PX_INLINE PxTransform getChildPose() const; + PX_INLINE void setChildPose(const PxTransform&); + + PX_INLINE const NpArticulationLink& getParent() const { return *mParent; } + PX_INLINE NpArticulationLink& getParent() { return *mParent; } + + PX_INLINE const NpArticulationLink& getChild() const { return *mChild; } + PX_INLINE NpArticulationLink& getChild() { return *mChild; } + + PX_INLINE void resolveReferences(PxDeserializationContext& context); +}; + +template +class NpArticulationJointTemplate : public APIClass, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + +// PX_SERIALIZATION + NpArticulationJointTemplate(PxBaseFlags baseFlags) : APIClass(baseFlags), mImpl(PxEmpty) + { + mImpl.getScbArticulationJoint().getScArticulationJoint().setRoot(this); + } +//~PX_SERIALIZATION + + NpArticulationJointTemplate(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame, + PxArticulationBase::Enum type = PxArticulationBase::eMaximumCoordinate); + + virtual ~NpArticulationJointTemplate() {} + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- +public: + void release(); + + PX_INLINE const Scb::ArticulationJoint& getScbArticulationJoint() const { return mImpl.getScbArticulationJoint(); } + PX_INLINE Scb::ArticulationJoint& getScbArticulationJoint() { return mImpl.getScbArticulationJoint(); } + + virtual PxArticulationLink& getParentArticulationLink() const { return mImpl.getParentArticulationLink(); } + virtual PxArticulationLink& getChildArticulationLink() const { return mImpl.getChildArticulationLink(); } + + virtual PxTransform getParentPose() const { return mImpl.getParentPose(); } + virtual void setParentPose(const PxTransform& t) { mImpl.setParentPose(t); } + + virtual PxTransform getChildPose() const { return mImpl.getChildPose(); } + virtual void setChildPose(const PxTransform& t) { mImpl.setChildPose(t); } + + PX_INLINE const NpArticulationLink& getParent() const { return mImpl.getParent(); } + PX_INLINE NpArticulationLink& getParent() { return mImpl.getParent(); } + + PX_INLINE const NpArticulationLink& getChild() const { return mImpl.getChild(); } + PX_INLINE NpArticulationLink& getChild() { return mImpl.getChild(); } + + virtual PxArticulationJointImpl* getImpl() { return &mImpl; } + virtual const PxArticulationJointImpl* getImpl() const { return &mImpl; } + +protected: + + NpScene* getOwnerScene() const { return mImpl.getOwnerScene(); } + + PxArticulationJointImpl mImpl; +}; + +class NpArticulationJoint : public NpArticulationJointTemplate +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpArticulationJoint(PxBaseFlags baseFlags) : NpArticulationJointTemplate(baseFlags) {} + virtual void resolveReferences(PxDeserializationContext& context); + static NpArticulationJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void exportExtraData(PxSerializationContext&) {} + void importExtraData(PxDeserializationContext&) {} + virtual void requiresObjects(PxProcessPxBaseCallback&){} + virtual bool isSubordinate() const { return true; } +//~PX_SERIALIZATION + NpArticulationJoint(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame); + + virtual ~NpArticulationJoint(); + + //--------------------------------------------------------------------------------- + // PxArticulationJoint implementation + //--------------------------------------------------------------------------------- + // Save + + + virtual void setTargetOrientation(const PxQuat&); + virtual PxQuat getTargetOrientation() const; + + + virtual void setTargetVelocity(const PxVec3&); + virtual PxVec3 getTargetVelocity() const; + + virtual void setDriveType(PxArticulationJointDriveType::Enum driveType); + virtual PxArticulationJointDriveType::Enum + getDriveType() const; + + + virtual void setStiffness(PxReal); + virtual PxReal getStiffness() const; + + virtual void setDamping(PxReal); + virtual PxReal getDamping() const; + + virtual void setInternalCompliance(PxReal); + virtual PxReal getInternalCompliance() const; + + virtual void setExternalCompliance(PxReal); + virtual PxReal getExternalCompliance() const; + + virtual void setSwingLimit(PxReal yLimit, PxReal zLimit); + virtual void getSwingLimit(PxReal &yLimit, PxReal &zLimit) const; + + virtual void setTangentialStiffness(PxReal spring); + virtual PxReal getTangentialStiffness() const; + + virtual void setTangentialDamping(PxReal damping); + virtual PxReal getTangentialDamping() const; + + virtual void setSwingLimitEnabled(bool); + virtual bool getSwingLimitEnabled() const; + + virtual void setSwingLimitContactDistance(PxReal contactDistance); + virtual PxReal getSwingLimitContactDistance() const; + + virtual void setTwistLimit(PxReal lower, PxReal upper); + virtual void getTwistLimit(PxReal &lower, PxReal &upper) const; + + virtual void setTwistLimitEnabled(bool); + virtual bool getTwistLimitEnabled() const; + + virtual void setTwistLimitContactDistance(PxReal contactDistance); + virtual PxReal getTwistLimitContactDistance() const; + + virtual void setJointType(PxArticulationJointType::Enum jointType); + virtual PxArticulationJointType::Enum + getJointType() const; + + virtual void setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion); + virtual PxArticulationMotion::Enum + getMotion(PxArticulationAxis::Enum axis) const; + + virtual void setFrictionCoefficient(const PxReal coefficient); + + virtual PxReal getFrictionCoefficient() const ; +}; + +PX_INLINE PxArticulationLink& PxArticulationJointImpl::getParentArticulationLink() const +{ + return *mParent; +} + + +PX_INLINE PxArticulationLink& PxArticulationJointImpl::getChildArticulationLink() const +{ + return *mChild; +} + + +PX_INLINE PxTransform PxArticulationJointImpl::getParentPose() const +{ + NP_READ_CHECK(getOwnerScene()); + return mJoint.getParentPose(); +} + + +PX_INLINE void PxArticulationJointImpl::setParentPose(const PxTransform& t) +{ + PX_CHECK_AND_RETURN(t.isSane(), "NpArticulationJoint::setParentPose t is not valid."); + + NP_WRITE_CHECK(getOwnerScene()); + if (mParent == NULL) + return; + + mJoint.setParentPose(mParent->getCMassLocalPose().transformInv(t.getNormalized())); +} + + +PX_INLINE PxTransform PxArticulationJointImpl::getChildPose() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mChild->getCMassLocalPose().transform(mJoint.getChildPose()); +} + + +PX_INLINE void PxArticulationJointImpl::setChildPose(const PxTransform& t) +{ + PX_CHECK_AND_RETURN(t.isSane(), "NpArticulationJoint::setChildPose t is not valid."); + + NP_WRITE_CHECK(getOwnerScene()); + + mJoint.setChildPose(mChild->getCMassLocalPose().transformInv(t.getNormalized())); +} + + +PX_INLINE NpScene* PxArticulationJointImpl::getOwnerScene() const +{ + return mJoint.getScbScene() ? static_cast(mJoint.getScbScene()->getPxScene()) : NULL; +} + + +PX_INLINE PxArticulationJointImpl::PxArticulationJointImpl(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame, + PxArticulationBase::Enum type) + : mJoint(parentFrame, childFrame, type) + , mParent(&parent) + , mChild(&child) + , mType(type) +{ + PxArticulationImpl* impl = parent.getRoot().getImpl(); + Scb::Articulation& scbArti = impl->getArticulation(); + mJoint.setScArticulation(&scbArti); +} + +PX_INLINE void PxArticulationJointImpl::release() +{ + if (mJoint.getScbSceneForAPI()) + mJoint.getScbSceneForAPI()->removeArticulationJoint(mJoint); + + mJoint.destroy(); +} + +PX_INLINE void PxArticulationJointImpl::resolveReferences(PxDeserializationContext& context) +{ + context.translatePxBase(mParent); + context.translatePxBase(mChild); +} + +template +void NpArticulationJointTemplate::release() +{ + NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, NULL); + mImpl.release(); +} + + +template +NpArticulationJointTemplate::NpArticulationJointTemplate(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame, + PxArticulationBase::Enum type) + : APIClass(PxConcreteType::eARTICULATION_JOINT, PxBaseFlag::eOWNS_MEMORY), + mImpl(parent, parentFrame, child, childFrame, type) +{ + PX_UNUSED(type); + mImpl.getScbArticulationJoint().getScArticulationJoint().setRoot(this); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp new file mode 100644 index 000000000..41345a0b3 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp @@ -0,0 +1,242 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpCast.h" +#include "NpWriteCheck.h" +#include "NpReadCheck.h" +#include "NpArticulationJointReducedCoordinate.h" + +namespace physx +{ + NpArticulationJointReducedCoordinate* NpArticulationJointReducedCoordinate::createObject(PxU8*& address, PxDeserializationContext& context) + { + NpArticulationJointReducedCoordinate* obj = new (address) NpArticulationJointReducedCoordinate(PxBaseFlags(0)); + address += sizeof(NpArticulationJointReducedCoordinate); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; + } + + void NpArticulationJointReducedCoordinate::getBinaryMetaData(PxOutputStream& stream) + { + // 184 => 200 => 192 => 224 => 208 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpArticulationJointReducedCoordinate) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpArticulationJointReducedCoordinate, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationJointReducedCoordinate, PxArticulationJointImpl, mImpl, 0) + + } + //~PX_SERIALIZATION + NpArticulationJointReducedCoordinate::NpArticulationJointReducedCoordinate(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame) : + NpArticulationJointTemplate(parent, parentFrame, child, childFrame, PxArticulationBase::eReducedCoordinate) + { + + } + + NpArticulationJointReducedCoordinate::~NpArticulationJointReducedCoordinate() + { + } + + + void NpArticulationJointReducedCoordinate::setJointType(PxArticulationJointType::Enum jointType) + { + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(jointType != PxArticulationJointType::eUNDEFINED, "PxArticulationJointReducedCoordinate::setJointType valid joint type(ePRISMATIC, eREVOLUTE, eSPHERICAL, eFIX) need to be set"); + mImpl.getScbArticulationJoint().setJointType(jointType); + } + PxArticulationJointType::Enum NpArticulationJointReducedCoordinate::getJointType() const + { + NP_READ_CHECK(getOwnerScene()); + return mImpl.getScbArticulationJoint().getJointType(); + } + +#if PX_CHECKED + bool NpArticulationJointReducedCoordinate::isValidMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) + { + + PxArticulationJointType::Enum type = getJointType(); + + bool valid = true; + switch (type) + { + case PxArticulationJointType::ePRISMATIC: + { + if (axis < PxArticulationAxis::eX && motion != PxArticulationMotion::eLOCKED) + valid = false; + else if(motion != PxArticulationMotion::eLOCKED) + { + //Check to ensure that we only have zero DOFs already active... + for (PxU32 i = PxArticulationAxis::eX; i <= PxArticulationAxis::eZ; i++) + { + if(i != PxU32(axis) && mImpl.mJoint.getMotion(PxArticulationAxis::Enum(i)) != PxArticulationMotion::eLOCKED) + valid = false; + } + } + break; + } + case PxArticulationJointType::eREVOLUTE: + { + if (axis >= PxArticulationAxis::eX && motion != PxArticulationMotion::eLOCKED) + valid = false; + else if (motion != PxArticulationMotion::eLOCKED) + { + + for (PxU32 i = PxArticulationAxis::eTWIST; i < PxArticulationAxis::eX; i++) + { + if (i != PxU32(axis) && this->mImpl.mJoint.getMotion(PxArticulationAxis::Enum(i)) != PxArticulationMotion::eLOCKED) + valid = false; + } + } + break; + } + case PxArticulationJointType::eSPHERICAL: + { + if (axis >= PxArticulationAxis::eX && motion != PxArticulationMotion::eLOCKED) + valid = false; + break; + } + case PxArticulationJointType::eFIX: + { + if (motion != PxArticulationMotion::eLOCKED) + valid = false; + break; + } + case PxArticulationJointType::eUNDEFINED: + { + valid = false; + break; + } + default: + break; + } + + return valid; + } + + /*bool NpArticulationJointReducedCoordinate::isValidType(PxArticulationJointType::Enum type) + { + bool hasPrismatic = (getMotion(PxArticulationAxis::eX) != PxArticulationMotion::eLOCKED) || + (getMotion(PxArticulationAxis::eY) != PxArticulationMotion::eLOCKED) || + (getMotion(PxArticulationAxis::eZ) != PxArticulationMotion::eLOCKED); + + bool hasRotation = (getMotion(PxArticulationAxis::eTWIST) != PxArticulationMotion::eLOCKED) || + (getMotion(PxArticulationAxis::eSWING1) != PxArticulationMotion::eLOCKED) || + (getMotion(PxArticulationAxis::eSWING2) != PxArticulationMotion::eLOCKED); + + if (type == PxArticulationJointType::eFIX) + return !hasPrismatic && !hasRotation; + if (type == PxArticulationJointType::eREVOLUTE || type == PxArticulationJointType::eSPHERICAL) + return !hasPrismatic; + if (type == PxArticulationJointType::ePRISMATIC) + return !hasRotation; + + return true; + }*/ +#endif + + void NpArticulationJointReducedCoordinate::setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) + { + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(getJointType() != PxArticulationJointType::eUNDEFINED, "PxArticulationJointReducedCoordinate::setMotion valid joint type(ePRISMATIC, eREVOLUTE, eSPHERICAL or eFIX) has to be set before setMotion"); + PX_CHECK_AND_RETURN(isValidMotion(axis, motion), "PxArticulationJointReducedCoordinate::setMotion illegal motion state requested."); + mImpl.getScbArticulationJoint().setMotion(axis, motion); + reinterpret_cast(getChild().getArticulation().getImpl())->increaseCacheVersion(); + } + + PxArticulationMotion::Enum NpArticulationJointReducedCoordinate::getMotion(PxArticulationAxis::Enum axis) const + { + NP_READ_CHECK(getOwnerScene()); + return mImpl.getScbArticulationJoint().getMotion(axis); + } + + void NpArticulationJointReducedCoordinate::setFrictionCoefficient(const PxReal coefficient) + { + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setFrictionCoefficient(coefficient); + } + + PxReal NpArticulationJointReducedCoordinate::getFrictionCoefficient() const + { + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getFrictionCoefficient(); + } + + void NpArticulationJointReducedCoordinate::setMaxJointVelocity(const PxReal maxJointV) + { + NP_WRITE_CHECK(getOwnerScene()); + + mImpl.getScbArticulationJoint().setMaxJointVelocity(maxJointV); + } + + PxReal NpArticulationJointReducedCoordinate::getMaxJointVelocity() const + { + NP_READ_CHECK(getOwnerScene()); + + return mImpl.getScbArticulationJoint().getMaxJointVelocity(); + } + + void NpArticulationJointReducedCoordinate::setLimit(PxArticulationAxis::Enum axis, const PxReal lowLimit, const PxReal highLimit) + { + mImpl.getScbArticulationJoint().setLimit(axis, lowLimit, highLimit); + } + void NpArticulationJointReducedCoordinate::getLimit(PxArticulationAxis::Enum axis, PxReal& lowLimit, PxReal& highLimit) + { + mImpl.getScbArticulationJoint().getLimit(axis, lowLimit, highLimit); + } + void NpArticulationJointReducedCoordinate::setDrive(PxArticulationAxis::Enum axis, const PxReal stiffness, const PxReal damping, const PxReal maxForce, bool isAccelerationDrive) + { + mImpl.getScbArticulationJoint().setDrive(axis, stiffness, damping, maxForce, isAccelerationDrive); + } + void NpArticulationJointReducedCoordinate::getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration) + { + mImpl.getScbArticulationJoint().getDrive(axis, stiffness, damping, maxForce, isAcceleration); + } + void NpArticulationJointReducedCoordinate::setDriveTarget(PxArticulationAxis::Enum axis, const PxReal target) + { + mImpl.getScbArticulationJoint().setDriveTarget(axis, target); + } + void NpArticulationJointReducedCoordinate::setDriveVelocity(PxArticulationAxis::Enum axis, const PxReal targetVel) + { + mImpl.getScbArticulationJoint().setDriveVelocity(axis, targetVel); + } + PxReal NpArticulationJointReducedCoordinate::getDriveTarget(PxArticulationAxis::Enum axis) + { + return mImpl.getScbArticulationJoint().getDriveTarget(axis); + } + PxReal NpArticulationJointReducedCoordinate::getDriveVelocity(PxArticulationAxis::Enum axis) + { + return mImpl.getScbArticulationJoint().getDriveVelocity(axis); + } +} diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h new file mode 100644 index 000000000..f2b1d19cc --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h @@ -0,0 +1,108 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ARTICULATION_JOINT_RC +#define PX_PHYSICS_NP_ARTICULATION_JOINT_RC + +#include "PxArticulationJointReducedCoordinate.h" +#include "ScbArticulationJoint.h" +#include "NpArticulationTemplate.h" +#include "NpArticulationJoint.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +namespace physx +{ + + class NpArticulationJointReducedCoordinate : public NpArticulationJointTemplate + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + // PX_SERIALIZATION + NpArticulationJointReducedCoordinate(PxBaseFlags baseFlags) : NpArticulationJointTemplate(baseFlags) {} + virtual void resolveReferences(PxDeserializationContext& context) { mImpl.resolveReferences(context); } + static NpArticulationJointReducedCoordinate* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void exportExtraData(PxSerializationContext&) {} + void importExtraData(PxDeserializationContext&) {} + virtual void requiresObjects(PxProcessPxBaseCallback&) {} + virtual bool isSubordinate() const { return true; } + //~PX_SERIALIZATION + NpArticulationJointReducedCoordinate(NpArticulationLink& parent, + const PxTransform& parentFrame, + NpArticulationLink& child, + const PxTransform& childFrame); + + virtual ~NpArticulationJointReducedCoordinate(); + + //--------------------------------------------------------------------------------- + // PxArticulationJoint implementation + //--------------------------------------------------------------------------------- + // Save + + + virtual void setJointType(PxArticulationJointType::Enum jointType); + virtual PxArticulationJointType::Enum + getJointType() const; + + virtual void setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion); + virtual PxArticulationMotion::Enum + getMotion(PxArticulationAxis::Enum axis) const; + + virtual void setFrictionCoefficient(const PxReal coefficient); + virtual PxReal getFrictionCoefficient() const; + + virtual void setMaxJointVelocity(const PxReal maxJointV); + virtual PxReal getMaxJointVelocity() const; + + virtual void setLimit(PxArticulationAxis::Enum axis, const PxReal lowLimit, const PxReal highLimit); + virtual void getLimit(PxArticulationAxis::Enum axis, PxReal& lowLimit, PxReal& highLimit); + virtual void setDrive(PxArticulationAxis::Enum axis, const PxReal stiffness, const PxReal damping, const PxReal maxForce, bool isAccelerationDrive); + virtual void getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration); + virtual void setDriveTarget(PxArticulationAxis::Enum axis, const PxReal target); + virtual void setDriveVelocity(PxArticulationAxis::Enum axis, const PxReal targetVel); + virtual PxReal getDriveTarget(PxArticulationAxis::Enum axis); + virtual PxReal getDriveVelocity(PxArticulationAxis::Enum axis); +#if PX_CHECKED + private: + bool isValidMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion); +#endif + }; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulationLink.cpp b/src/PhysX/physx/source/physx/src/NpArticulationLink.cpp new file mode 100644 index 000000000..3d215a7a0 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationLink.cpp @@ -0,0 +1,620 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpArticulationLink.h" +#include "NpArticulationJoint.h" +#include "NpWriteCheck.h" +#include "NpReadCheck.h" +#include "ScbArticulation.h" +#include "CmVisualization.h" +#include "CmConeLimitHelper.h" +#include "CmUtils.h" +#include "NpArticulation.h" + +using namespace physx; + +// PX_SERIALIZATION +void NpArticulationLink::requiresObjects(PxProcessPxBaseCallback& c) +{ + NpArticulationLinkT::requiresObjects(c); + + if(mInboundJoint) + c.process(*mInboundJoint); +} + +void NpArticulationLink::exportExtraData(PxSerializationContext& stream) +{ + NpArticulationLinkT::exportExtraData(stream); + Cm::exportInlineArray(mChildLinks, stream); +} + +void NpArticulationLink::importExtraData(PxDeserializationContext& context) +{ + NpArticulationLinkT::importExtraData(context); + Cm::importInlineArray(mChildLinks, context); +} + +void NpArticulationLink::resolveReferences(PxDeserializationContext& context) +{ + context.translatePxBase(mRoot); + context.translatePxBase(mInboundJoint); + context.translatePxBase(mParent); + + NpArticulationLinkT::resolveReferences(context); + + const PxU32 nbLinks = mChildLinks.size(); + for(PxU32 i=0;iimportExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +NpArticulationLink::NpArticulationLink(const PxTransform& bodyPose, PxArticulationBase& root, NpArticulationLink* parent) +: NpArticulationLinkT(PxConcreteType::eARTICULATION_LINK, PxBaseFlag::eOWNS_MEMORY, PxActorType::eARTICULATION_LINK, bodyPose) +, mRoot(&root) +, mInboundJoint(NULL) +, mParent(parent) +, mLLIndex(0xffffffff) +, mInboundJointDof(0xffffffff) +{ + PX_ASSERT(mBody.getScbType() == ScbType::eBODY); + mBody.setScbType(ScbType::eBODY_FROM_ARTICULATION_LINK); + + if (parent) + parent->addToChildList(*this); +} + + +NpArticulationLink::~NpArticulationLink() +{ +} + +void NpArticulationLink::releaseInternal() +{ + NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, userData); + + NpArticulationLinkT::release(); + + PxArticulationImpl* impl = reinterpret_cast(mRoot->getImpl()); + impl->removeLinkFromList(*this); + + if (mParent) + mParent->removeFromChildList(*this); + + if (mInboundJoint) + mInboundJoint->release(); + + NpScene* npScene = NpActor::getAPIScene(*this); + if (npScene) + npScene->getScene().removeActor(mBody, true, false); + + mBody.destroy(); +} + + +void NpArticulationLink::release() +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + PxArticulationImpl* impl = reinterpret_cast(mRoot->getImpl()); + + PX_UNUSED(impl); + + if (impl->getRoot() == this && NpActor::getOwnerScene(*this) != NULL) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxArticulationLink::release(): root link may not be released while articulation is in a scene"); + return; + } + + //! this function doesn't get called when the articulation root is released + // therefore, put deregistration code etc. into dtor, not here + + if (mChildLinks.empty()) + { + releaseInternal(); + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxArticulationLink::release(): Only leaf articulation links can be released. Release call failed"); + } +} + + + +PxTransform NpArticulationLink::getGlobalPose() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + //!!!AL TODO: Need to start from root and compute along the branch to reflect double buffered state of root link + return getScbBodyFast().getBody2World() * getScbBodyFast().getBody2Actor().getInverse(); +} + +void NpArticulationLink::setLinearDamping(PxReal linearDamping) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(linearDamping), "NpArticulationLink::setLinearDamping: invalid float"); + PX_CHECK_AND_RETURN(linearDamping >= 0, "NpArticulationLink::setLinearDamping: The linear damping must be nonnegative!"); + + getScbBodyFast().setLinearDamping(linearDamping); +} + +PxReal NpArticulationLink::getLinearDamping() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getLinearDamping(); +} + +void NpArticulationLink::setAngularDamping(PxReal angularDamping) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(angularDamping), "NpArticulationLink::setAngularDamping: invalid float"); + PX_CHECK_AND_RETURN(angularDamping >= 0, "NpArticulationLink::setAngularDamping: The angular damping must be nonnegative!") + + getScbBodyFast().setAngularDamping(angularDamping); +} + +PxReal NpArticulationLink::getAngularDamping() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getAngularDamping(); +} + +PxArticulationBase& NpArticulationLink::getArticulation() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return *mRoot; +} + +PxArticulationReducedCoordinate& NpArticulationLink::getArticulationReducedCoordinate() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + //return *mRoot; + PxArticulationReducedCoordinate* ret = NULL; + return *ret; +} + + +PxArticulationJointBase* NpArticulationLink::getInboundJoint() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mInboundJoint; +} + +PxU32 NpArticulationLink::getInboundJointDof() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return mInboundJointDof; +} + +PxU32 NpArticulationLink::getNbChildren() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mChildLinks.size(); +} + + +PxU32 NpArticulationLink::getChildren(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mChildLinks.begin(), mChildLinks.size()); +} + +PxU32 NpArticulationLink::getLinkIndex() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mLLIndex; +} + + +void NpArticulationLink::setCMassLocalPose(const PxTransform& pose) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(pose.isSane(), "PxArticulationLink::setCMassLocalPose: invalid parameter"); + + PxTransform p = pose.getNormalized(); + PxTransform oldpose = getScbBodyFast().getBody2Actor(); + PxTransform comShift = p.transformInv(oldpose); + + NpArticulationLinkT::setCMassLocalPoseInternal(p); + + if(mInboundJoint) + { + Scb::ArticulationJoint &j = mInboundJoint->getImpl()->getScbArticulationJoint(); + j.setChildPose(comShift.transform(j.getChildPose())); + } + + for(PxU32 i=0; i(mChildLinks[i]->getInboundJoint())->getScbArticulationJoint(); + j.setParentPose(comShift.transform(j.getParentPose())); + } +} + + +void NpArticulationLink::addForce(const PxVec3& force, PxForceMode::Enum mode, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_UNUSED(scene); + + PX_CHECK_AND_RETURN(force.isFinite(), "NpArticulationLink::addForce: force is not valid."); + NP_WRITE_CHECK(scene); + PX_CHECK_AND_RETURN(scene, "NpArticulationLink::addForce: articulation link must be in a scene!"); + + addSpatialForce(&force, 0, mode); + + reinterpret_cast(mRoot->getImpl())->wakeUpInternal((!force.isZero()), autowake); +} + + +void NpArticulationLink::addTorque(const PxVec3& torque, PxForceMode::Enum mode, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_UNUSED(scene); + + PX_CHECK_AND_RETURN(torque.isFinite(), "NpArticulationLink::addTorque: force is not valid."); + NP_WRITE_CHECK(scene); + PX_CHECK_AND_RETURN(scene, "NpArticulationLink::addTorque: articulation link must be in a scene!"); + + addSpatialForce(0, &torque, mode); + + reinterpret_cast(mRoot->getImpl())->wakeUpInternal((!torque.isZero()), autowake); +} + +void NpArticulationLink::setForceAndTorque(const PxVec3& force, const PxVec3& torque, PxForceMode::Enum mode) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_UNUSED(scene); + + PX_CHECK_AND_RETURN(torque.isFinite(), "NpArticulationLink::setForceAndTorque: torque is not valid."); + PX_CHECK_AND_RETURN(force.isFinite(), "NpArticulationLink::setForceAndTorque: force is not valid."); + NP_WRITE_CHECK(scene); + PX_CHECK_AND_RETURN(scene, "NpArticulationLink::addTorque: articulation link must be in a scene!"); + + setSpatialForce(&force, &torque, mode); + + reinterpret_cast(mRoot->getImpl())->wakeUpInternal((!torque.isZero()), true); + +} + +void NpArticulationLink::clearForce(PxForceMode::Enum mode) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_UNUSED(scene); + NP_WRITE_CHECK(scene); + PX_CHECK_AND_RETURN(scene, "NpArticulationLink::clearForce: articulation link must be in a scene!"); + + clearSpatialForce(mode, true, false); +} + +void NpArticulationLink::clearTorque(PxForceMode::Enum mode) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_UNUSED(scene); + NP_WRITE_CHECK(scene); + PX_CHECK_AND_RETURN(scene, "NpArticulationLink::clearTorque: articulation link must be in a scene!"); + + clearSpatialForce(mode, false, true); +} + +void NpArticulationLink::setGlobalPoseInternal(const PxTransform& pose, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + + PX_CHECK_AND_RETURN(pose.isValid(), "NpArticulationLink::setGlobalPose pose is not valid."); + + NP_WRITE_CHECK(scene); + +#if PX_CHECKED + if (scene) + scene->checkPositionSanity(*this, pose, "PxArticulationLink::setGlobalPose"); +#endif + + PxTransform body2World = pose * getScbBodyFast().getBody2Actor(); + getScbBodyFast().setBody2World(body2World, false); + + if (scene && autowake) + reinterpret_cast(mRoot->getImpl())->wakeUpInternal(false, true); + + if (scene) + reinterpret_cast(mRoot->getImpl())->setGlobalPose(); +} + +void NpArticulationLink::setGlobalPose(const PxTransform& pose) +{ + // clow: no need to test inputs here, it's done in the setGlobalPose function already + setGlobalPose(pose, true); +} + +void NpArticulationLink::setGlobalPose(const PxTransform& pose, bool autowake) +{ + PX_CHECK_AND_RETURN(mRoot->getType() == PxArticulationBase::eMaximumCoordinate, "NpArticulationLink::setGlobalPose teleport isn't allowed in the reduced coordinate system."); + + setGlobalPoseInternal(pose, autowake); +} + + +void NpArticulationLink::setLinearVelocity(const PxVec3& velocity, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + + PX_CHECK_AND_RETURN(velocity.isFinite(), "NpArticulationLink::setLinearVelocity velocity is not valid."); + + NP_WRITE_CHECK(scene); + + getScbBodyFast().setLinearVelocity(velocity); + + if (scene) + reinterpret_cast(mRoot->getImpl())->wakeUpInternal((!velocity.isZero()), autowake); +} + + +void NpArticulationLink::setAngularVelocity(const PxVec3& velocity, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + + PX_CHECK_AND_RETURN(velocity.isFinite(), "NpArticulationLink::setAngularVelocity velocity is not valid."); + + NP_WRITE_CHECK(scene); + + getScbBodyFast().setAngularVelocity(velocity); + + if (scene) + reinterpret_cast(mRoot->getImpl())->wakeUpInternal((!velocity.isZero()), autowake); +} + +void NpArticulationLink::setMaxAngularVelocity(PxReal maxAngularVelocity) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(maxAngularVelocity), "NpArticulationLink::setMaxAngularVelocity: invalid float"); + PX_CHECK_AND_RETURN(maxAngularVelocity >= 0.0f, "NpArticulationLink::setMaxAngularVelocity: threshold must be non-negative!"); + + getScbBodyFast().setMaxAngVelSq(maxAngularVelocity * maxAngularVelocity); +} + +PxReal NpArticulationLink::getMaxAngularVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return PxSqrt(getScbBodyFast().getMaxAngVelSq()); +} + +void NpArticulationLink::setMaxLinearVelocity(PxReal maxLinearVelocity) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(maxLinearVelocity), "NpArticulationLink::setMaxAngularVelocity: invalid float"); + PX_CHECK_AND_RETURN(maxLinearVelocity >= 0.0f, "NpArticulationLink::setMaxAngularVelocity: threshold must be non-negative!"); + + getScbBodyFast().setMaxLinVelSq(maxLinearVelocity * maxLinearVelocity); +} + +PxReal NpArticulationLink::getMaxLinearVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return PxSqrt(getScbBodyFast().getMaxLinVelSq()); +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +void NpArticulationLink::visualize(Cm::RenderOutput& out, NpScene* scene) +{ + NpArticulationLinkT::visualize(out, scene); + + if (getScbBodyFast().getActorFlags() & PxActorFlag::eVISUALIZATION) + { + PX_ASSERT(getScene()); + PxReal scale = getScene()->getVisualizationParameter(PxVisualizationParameter::eSCALE); + + PxReal massAxes = scale * getScene()->getVisualizationParameter(PxVisualizationParameter::eBODY_MASS_AXES); + if (massAxes != 0) + { + PxU32 color = 0xff; + color = (color<<16 | color<<8 | color); + PxVec3 dims = invertDiagInertia(getScbBodyFast().getInverseInertia()); + dims = getDimsFromBodyInertia(dims, 1.0f / getScbBodyFast().getInverseMass()); + + out << color << getScbBodyFast().getBody2World() << Cm::DebugBox(dims * 0.5f); + } + PxReal frameScale = scale * getScene()->getVisualizationParameter(PxVisualizationParameter::eJOINT_LOCAL_FRAMES); + PxReal limitScale = scale * getScene()->getVisualizationParameter(PxVisualizationParameter::eJOINT_LIMITS); + if ( frameScale != 0.0f || limitScale != 0.0f ) + { + Cm::ConstraintImmediateVisualizer viz( frameScale, limitScale, out ); + visualizeJoint( viz ); + } + } +} + +static PX_FORCE_INLINE PxReal computePhi(const PxQuat& q) +{ + PxQuat twist = q; + twist.normalize(); + + PxReal angle = twist.getAngle(); + if (twist.x<0.0f) + angle = -angle; + return angle; +} + +static PX_FORCE_INLINE float computeSwingAngle(float swingYZ, float swingW) +{ + return 4.0f * PxAtan2(swingYZ, 1.0f + swingW); // tan (t/2) = sin(t)/(1+cos t), so this is the quarter angle +} + +static PX_FORCE_INLINE void separateSwingTwist(const PxQuat& q, PxQuat& twist, PxQuat& swing1, PxQuat& swing2) +{ + twist = q.x != 0.0f ? PxQuat(q.x, 0, 0, q.w).getNormalized() : PxQuat(PxIdentity); + PxQuat swing = q * twist.getConjugate(); + swing1 = swing.y != 0.f ? PxQuat(0.f, swing.y, 0.f, swing.w).getNormalized() : PxQuat(PxIdentity); + swing = swing * swing1.getConjugate(); + swing2 = swing.z != 0.f ? PxQuat(0.f, 0.f, swing.z, swing.w).getNormalized() : PxQuat(PxIdentity); +} + + +void NpArticulationLink::visualizeJoint(PxConstraintVisualizer& jointViz) +{ + NpArticulationLink* parent = getParent(); + if(parent) + { + PxTransform cA2w = getGlobalPose().transform(mInboundJoint->getChildPose()); + PxTransform cB2w = parent->getGlobalPose().transform(mInboundJoint->getParentPose()); + + jointViz.visualizeJointFrames(cA2w, cB2w); + + PxArticulationJointImpl* impl = mInboundJoint->getImpl(); + + if (impl->mType == PxArticulationBase::eMaximumCoordinate) + { + + PxTransform parentFrame = cB2w; + + if (cA2w.q.dot(cB2w.q) < 0) + cB2w.q = -cB2w.q; + + PxTransform cB2cA = cA2w.transformInv(cB2w); + + PxQuat swing, twist; + Ps::separateSwingTwist(cB2cA.q, swing, twist); + + PxMat33 cA2w_m(cA2w.q), cB2w_m(cB2w.q); + + PxReal tqPhi = Ps::tanHalf(twist.x, twist.w); // always support (-pi, +pi) + + PxReal lower, upper, yLimit, zLimit; + + impl->getScbArticulationJoint().getTwistLimit(lower, upper); + impl->getScbArticulationJoint().getSwingLimit(yLimit, zLimit); + PxReal swingPad = impl->getScbArticulationJoint().getSwingLimitContactDistance(), twistPad = impl->getScbArticulationJoint().getTwistLimitContactDistance(); + jointViz.visualizeAngularLimit(parentFrame, lower, upper, PxAbs(tqPhi) > PxTan(upper - twistPad)); + + PxVec3 tanQSwing = PxVec3(0, Ps::tanHalf(swing.z, swing.w), -Ps::tanHalf(swing.y, swing.w)); + Cm::ConeLimitHelper coneHelper(PxTan(yLimit / 4), PxTan(zLimit / 4), PxTan(swingPad / 4)); + jointViz.visualizeLimitCone(parentFrame, PxTan(yLimit / 4), PxTan(zLimit / 4), !coneHelper.contains(tanQSwing)); + } + else + { + //(1) visualize any angular dofs/limits... + + const PxMat33 cA2w_m(cA2w.q), cB2w_m(cB2w.q); + + PxTransform parentFrame = cB2w; + + if (cA2w.q.dot(cB2w.q) < 0) + cB2w.q = -cB2w.q; + + //const PxTransform cB2cA = cA2w.transformInv(cB2w); + + const PxTransform cA2cB = cB2w.transformInv(cA2w); + + Sc::ArticulationJointCore& joint = impl->getScbArticulationJoint().getScArticulationJoint(); + + PxQuat swing1, swing2, twist; + separateSwingTwist(cA2cB.q, twist, swing1, swing2); + + const PxReal pad = 0.01f; + + if(joint.getMotion(PxArticulationAxis::eTWIST)) + { + PxReal lowLimit, highLimit; + + const PxReal angle = computePhi(twist); + joint.getLimit(PxArticulationAxis::Enum(PxArticulationAxis::eTWIST), lowLimit, highLimit); + + bool active = (angle-pad) < lowLimit || (angle+pad) > highLimit; + + PxTransform tmp = parentFrame; + + jointViz.visualizeAngularLimit(tmp, lowLimit, highLimit, active); + } + + if (joint.getMotion(PxArticulationAxis::eSWING1)) + { + PxReal lowLimit, highLimit; + + joint.getLimit(PxArticulationAxis::Enum(PxArticulationAxis::eSWING1), lowLimit, highLimit); + + const PxReal angle = computeSwingAngle(swing1.y, swing1.w); + + bool active = (angle - pad) < lowLimit || (angle + pad) > highLimit; + + PxTransform tmp = parentFrame; + tmp.q = tmp.q * PxQuat(-PxPiDivTwo, PxVec3(0.f, 0.f, 1.f)); + + + jointViz.visualizeAngularLimit(tmp, -highLimit, -lowLimit, active); + } + + if (joint.getMotion(PxArticulationAxis::eSWING2)) + { + PxReal lowLimit, highLimit; + + joint.getLimit(PxArticulationAxis::Enum(PxArticulationAxis::eSWING2), lowLimit, highLimit); + + const PxReal angle = computeSwingAngle(swing2.z, swing2.w); + + bool active = (angle - pad) < lowLimit || (angle + pad) > highLimit; + + PxTransform tmp = parentFrame; + tmp.q = tmp.q * PxQuat(PxPiDivTwo, PxVec3(0.f, 1.f, 0.f)); + + jointViz.visualizeAngularLimit(tmp, -highLimit, -lowLimit, active); + } + + for (PxU32 i = PxArticulationAxis::eX; i <= PxArticulationAxis::eZ; ++i) + { + if (joint.getMotion(PxArticulationAxis::Enum(i)) == PxArticulationMotion::eLIMITED) + { + PxU32 index = i - PxArticulationAxis::eX; + PxReal lowLimit, highLimit; + joint.getLimit(PxArticulationAxis::Enum(i), lowLimit, highLimit); + PxReal ordinate = cA2cB.p[index]; + PxVec3 origin = cB2w.p; + PxVec3 axis = cA2w_m[index]; + const bool active = ordinate < lowLimit || ordinate > highLimit; + const PxVec3 p0 = origin + axis * lowLimit; + const PxVec3 p1 = origin + axis * highLimit; + jointViz.visualizeLine(p0, p1, active ? 0xff0000u : 0xffffffu); + } + } + } + } +} +#endif // PX_ENABLE_DEBUG_VISUALIZATION diff --git a/src/PhysX/physx/source/physx/src/NpArticulationLink.h b/src/PhysX/physx/source/physx/src/NpArticulationLink.h new file mode 100644 index 000000000..d525cb0c6 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationLink.h @@ -0,0 +1,177 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ARTICULATION_LINK +#define PX_PHYSICS_NP_ARTICULATION_LINK + +#include "NpRigidBodyTemplate.h" +#include "PxArticulationLink.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +namespace physx +{ + +class NpArticulation; +class NpArticulationLink; +class NpArticulationJoint; +class PxConstraintVisualizer; + +typedef NpRigidBodyTemplate NpArticulationLinkT; + +class NpArticulationLinkArray : public Ps::InlineArray //!!!AL TODO: check if default of 4 elements makes sense +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpArticulationLinkArray(const PxEMPTY) : Ps::InlineArray (PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + NpArticulationLinkArray() : Ps::InlineArray(PX_DEBUG_EXP("articulationLinkArray")) {} +}; + + + +class NpArticulationLink : public NpArticulationLinkT +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpArticulationLink(PxBaseFlags baseFlags) : NpArticulationLinkT(baseFlags), mChildLinks(PxEmpty) {} + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void registerReferences(PxSerializationContext& stream); + void resolveReferences(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback& c); + virtual bool isSubordinate() const { return true; } + static NpArticulationLink* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + virtual ~NpArticulationLink(); + + //--------------------------------------------------------------------------------- + // PxArticulationLink implementation + //--------------------------------------------------------------------------------- + virtual void release(); + + + virtual PxActorType::Enum getType() const { return PxActorType::eARTICULATION_LINK; } + + // Pose + virtual void setGlobalPose(const PxTransform& pose); + virtual void setGlobalPose(const PxTransform& pose, bool autowake); + virtual PxTransform getGlobalPose() const; + + //damping + virtual void setLinearDamping(PxReal linDamp); + virtual PxReal getLinearDamping() const; + + virtual void setAngularDamping(PxReal angDamp); + virtual PxReal getAngularDamping() const; + + // Velocity + virtual void setLinearVelocity(const PxVec3&, bool autowake = true); + virtual void setAngularVelocity(const PxVec3&, bool autowake = true); + virtual void setMaxAngularVelocity(PxReal); + virtual PxReal getMaxAngularVelocity() const; + virtual void setMaxLinearVelocity(PxReal); + virtual PxReal getMaxLinearVelocity() const; + + virtual PxArticulationBase& getArticulation() const; + + virtual PxArticulationReducedCoordinate& getArticulationReducedCoordinate() const; + + virtual PxArticulationJointBase* getInboundJoint() const; + virtual PxU32 getInboundJointDof() const; + + virtual PxU32 getNbChildren() const; + virtual PxU32 getChildren(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + virtual PxU32 getLinkIndex() const; + virtual void setCMassLocalPose(const PxTransform& pose); + + virtual void addForce(const PxVec3& force, PxForceMode::Enum mode = PxForceMode::eFORCE, bool autowake = true); + virtual void addTorque(const PxVec3& torque, PxForceMode::Enum mode = PxForceMode::eFORCE, bool autowake = true); + virtual void setForceAndTorque(const PxVec3& force, const PxVec3& torque, PxForceMode::Enum mode = PxForceMode::eFORCE); + virtual void clearForce(PxForceMode::Enum mode = PxForceMode::eFORCE); + virtual void clearTorque(PxForceMode::Enum mode = PxForceMode::eFORCE); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpArticulationLink(const PxTransform& bodyPose, PxArticulationBase& root, NpArticulationLink* parent); + + void releaseInternal(); + + PX_INLINE PxArticulationBase& getRoot() { return *mRoot; } + PX_INLINE NpArticulationLink* getParent() { return mParent; } + + PX_INLINE void setInboundJoint(PxArticulationJointBase& joint) { mInboundJoint = &joint; } + + void setGlobalPoseInternal(const PxTransform& pose, bool autowake); + void setLLIndex(const PxU32 index) { mLLIndex = index; } + void setInboundJointDof(const PxU32 index) { mInboundJointDof = index; } +private: + PX_INLINE void addToChildList(NpArticulationLink& link) { mChildLinks.pushBack(&link); } + PX_INLINE void removeFromChildList(NpArticulationLink& link) { PX_ASSERT(mChildLinks.find(&link) != mChildLinks.end()); mChildLinks.findAndReplaceWithLast(&link); } + +public: + PX_INLINE NpArticulationLink* const* getChildren() { return mChildLinks.empty() ? NULL : &mChildLinks.front(); } + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene); + void visualizeJoint(PxConstraintVisualizer& jointViz); +#endif + + +private: + PxArticulationBase* mRoot; //!!!AL TODO: Revisit: Could probably be avoided if registration and deregistration in root is handled differently + PxArticulationJointBase* mInboundJoint; + NpArticulationLink* mParent; //!!!AL TODO: Revisit: Some memory waste but makes things faster + NpArticulationLinkArray mChildLinks; + PxU32 mLLIndex; + PxU32 mInboundJointDof; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp new file mode 100644 index 000000000..765f89c0d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp @@ -0,0 +1,424 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpArticulationReducedCoordinate.h" + +#include "CmPhysXCommon.h" +#include "DyFeatherstoneArticulation.h" +#include "ScArticulationSim.h" +#include "ScConstraintSim.h" + +#include "PsAlignedMalloc.h" +#include "PsFoundation.h" +#include "PsPool.h" + +#include "PxPvdDataStream.h" +#include "extensions/PxJoint.h" + +namespace physx +{ + + class LLReducedArticulationPool : public Ps::Pool > + { + public: + LLReducedArticulationPool() {} + }; + + + NpArticulationReducedCoordinate::NpArticulationReducedCoordinate() + : NpArticulationTemplate(PxConcreteType::eARTICULATION, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) + { + PxArticulationBase::userData = NULL; + mType = PxArticulationBase::eReducedCoordinate; + mImpl.mArticulation.setArticulationType(PxArticulationBase::eReducedCoordinate); + } + + + void NpArticulationReducedCoordinate::setArticulationFlags(PxArticulationFlags flags) + { + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setArticulationFlags(flags); + } + + void NpArticulationReducedCoordinate::setArticulationFlag(PxArticulationFlag::Enum flag, bool value) + { + NP_WRITE_CHECK(mImpl.getOwnerScene()); + PxArticulationFlags flags = mImpl.getArticulation().getArticulationFlags(); + + if(value) + flags |= flag; + else + flags &= (~flag); + + mImpl.getArticulation().setArticulationFlags(flags); + } + + PxArticulationFlags NpArticulationReducedCoordinate::getArticulationFlags() const + { + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getArticulationFlags(); + } + + PxU32 NpArticulationReducedCoordinate::getDofs() const + { + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.mArticulation.getScArticulation().getDofs(); + } + + PxArticulationCache* NpArticulationReducedCoordinate::createCache() const + { + PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::createCache: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + PxArticulationCache* cache = mImpl.mArticulation.getScArticulation().createCache(); + cache->version = mImpl.mCacheVersion; + + return cache; + } + + PxU32 NpArticulationReducedCoordinate::getCacheDataSize() const + { + PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::getCacheDataSize: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + return mImpl.mArticulation.getScArticulation().getCacheDataSize(); + } + + void NpArticulationReducedCoordinate::zeroCache(PxArticulationCache& cache) + { + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + return mImpl.mArticulation.getScArticulation().zeroCache(cache); + } + + void NpArticulationReducedCoordinate::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake) + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::applyCache: object must be in a scene"); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::applyCache : cache is invalid, articulation configuration has changed! ") + + //if we try to do a bulk op when sim is running, return with error + if (static_cast(getScene())->getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "NpArticulation::applyCache() not allowed while simulation is running."); + return; + } + + mImpl.mArticulation.getScArticulation().applyCache(cache, flag); + + if (flag & PxArticulationCache::ePOSITION) + { + const PxU32 linkCount = mImpl.mArticulationLinks.size(); + + for (PxU32 i = 0; i < linkCount; ++i) + { + NpArticulationLink* link = mImpl.mArticulationLinks[i]; + //in the lowlevel articulation, we have already updated bodyCore's body2World + const PxTransform internalPose = link->getScbBodyFast().getScBody().getBody2World(); + link->getScbBodyFast().setBody2World(internalPose, false); + } + } + + mImpl.wakeUpInternal(false, autowake); + + } + + void NpArticulationReducedCoordinate::copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::copyInternalStateToCache: object must be in a scene"); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::applyCache : cache is invalid, articulation configuration has changed! ") + + mImpl.mArticulation.getScArticulation().copyInternalStateToCache(cache, flag); + } + + void NpArticulationReducedCoordinate::releaseCache(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::releaseCache: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + mImpl.mArticulation.getScArticulation().releaseCache(cache); + } + + void NpArticulationReducedCoordinate::packJointData(const PxReal* maximum, PxReal* reduced) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::packJointData: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + mImpl.mArticulation.getScArticulation().packJointData(maximum, reduced); + } + + void NpArticulationReducedCoordinate::unpackJointData(const PxReal* reduced, PxReal* maximum) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::unpackJointData: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + mImpl.mArticulation.getScArticulation().unpackJointData(reduced, maximum); + } + + void NpArticulationReducedCoordinate::commonInit() const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::commonInit: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + mImpl.mArticulation.getScArticulation().commonInit(); + } + + void NpArticulationReducedCoordinate::computeGeneralizedGravityForce(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralisedGravityForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralisedGravityForce : cache is invalid, articulation configuration has changed! "); + + + mImpl.mArticulation.getScArticulation().computeGeneralizedGravityForce(cache); + } + + void NpArticulationReducedCoordinate::computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeCoriolisAndCentrifugalForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeCoriolisAndCentrifugalForce : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeCoriolisAndCentrifugalForce(cache); + } + + void NpArticulationReducedCoordinate::computeGeneralizedExternalForce(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralizedExternalForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralizedExternalForce : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeGeneralizedExternalForce(cache); + } + + void NpArticulationReducedCoordinate::computeJointAcceleration(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeJointAcceleration: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeJointAcceleration : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeJointAcceleration(cache); + } + + void NpArticulationReducedCoordinate::computeJointForce(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeJointForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeJointForce : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeJointForce(cache); + } + + void NpArticulationReducedCoordinate::computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeKenematicJacobian: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeKenematicJacobian : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeKinematicJacobian(linkID, cache); + } + + void NpArticulationReducedCoordinate::computeCoefficentMatrix(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeCoefficentMatrix: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeCoefficentMatrix : cache is invalid, articulation configuration has changed! "); + + for (PxU32 i = 0; i < mLoopJoints.size(); ++i) + { + static_cast(mLoopJoints[i]->getConstraint())->updateConstants(); + } + + mImpl.mArticulation.getScArticulation().computeCoefficentMatrix(cache); + } + + bool NpArticulationReducedCoordinate::computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxU32 maxIter) const + { + + if (!mImpl.getAPIScene()) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxArticulation::computeLambda : object must be in a scened!"); + return false; + } + + NP_READ_CHECK(mImpl.getOwnerScene()); + + if (cache.version != mImpl.mCacheVersion) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxArticulation::computeLambda : cache is invalid, articulation configuration has changed!"); + return false; + } + + return mImpl.mArticulation.getScArticulation().computeLambda(cache, initialState, jointTorque, getScene()->getGravity(), maxIter); + } + + void NpArticulationReducedCoordinate::computeGeneralizedMassMatrix(PxArticulationCache& cache) const + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralizedMassMatrix: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralizedMassMatrix : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeGeneralizedMassMatrix(cache); + } + + void NpArticulationReducedCoordinate::addLoopJoint(PxJoint* joint) + { + + NP_WRITE_CHECK(mImpl.getOwnerScene()); + +#if PX_CHECKED + + PxRigidActor* actor0; + PxRigidActor* actor1; + + joint->getActors(actor0, actor1); + + PxArticulationLink* link0 = NULL; + PxArticulationLink* link1 = NULL; + + if(actor0) + link0 = actor0->is(); + + if(actor1) + link1 = actor1->is(); + + PX_CHECK_AND_RETURN((link0 || link1), "PxArticulation::addLoopJoint : at least one of the PxRigidActors need to be PxArticulationLink! "); + + + PxArticulationBase* base0 = NULL; + PxArticulationBase* base1 = NULL; + if (link0) + base0 = &link0->getArticulation(); + + if (link1) + base1 = &link1->getArticulation(); + + PX_CHECK_AND_RETURN((base0 == this || base1 == this), "PxArticulation::addLoopJoint : at least one of the PxArticulationLink belongs to this articulation! "); + + +#endif + + + const PxU32 size = mLoopJoints.size(); + if (size >= mLoopJoints.capacity()) + { + mLoopJoints.reserve(size * 2 + 1); + } + + mLoopJoints.pushBack(joint); + + Scb::Articulation& scbArt = mImpl.getArticulation(); + Sc::ArticulationSim* scArtSim = scbArt.getScArticulation().getSim(); + + NpConstraint* constraint = static_cast(joint->getConstraint()); + Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); + if(scArtSim) + scArtSim->addLoopConstraint(cSim); + } + + void NpArticulationReducedCoordinate::removeLoopJoint(PxJoint* joint) + { + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + mLoopJoints.findAndReplaceWithLast(joint); + + Scb::Articulation& scbArt = mImpl.getArticulation(); + Sc::ArticulationSim* scArtSim = scbArt.getScArticulation().getSim(); + + NpConstraint* constraint = static_cast(joint->getConstraint()); + Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); + scArtSim->removeLoopConstraint(cSim); + } + + + PxU32 NpArticulationReducedCoordinate::getNbLoopJoints() const + { + NP_READ_CHECK(mImpl.getOwnerScene()); + + return mLoopJoints.size(); + } + + PxU32 NpArticulationReducedCoordinate::getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex) const + { + NP_READ_CHECK(mImpl.getOwnerScene()); + + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mLoopJoints.begin(), mLoopJoints.size()); + } + + PxU32 NpArticulationReducedCoordinate::getCoefficentMatrixSize() const + { + NP_READ_CHECK(mImpl.getOwnerScene()); + + return mImpl.mArticulation.getScArticulation().getCoefficentMatrixSize(); + } + + void NpArticulationReducedCoordinate::teleportRootLink(const PxTransform& pose, bool autowake) + { + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulationReducedCoordinate::teleportRootLink: object must be in a scene"); + + PX_CHECK_AND_RETURN(pose.isValid(), "PxArticulationReducedCoordinate::teleportRootLink pose is not valid."); + + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + NpArticulationLink* root = mImpl.mArticulationLinks[0]; + + root->setGlobalPoseInternal(pose, autowake); + } + + + NpArticulationReducedCoordinate::~NpArticulationReducedCoordinate() + { + NpFactory::getInstance().onArticulationRelease(this); + } + + PxArticulationJointBase* NpArticulationReducedCoordinate::createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame) + { + + return NpFactory::getInstance().createNpArticulationJointRC(static_cast(parent), parentFrame, static_cast(child), childFrame); + } + + void NpArticulationReducedCoordinate::releaseArticulationJoint(PxArticulationJointBase* joint) + { + NpFactory::getInstance().releaseArticulationJointRCToPool(*static_cast(joint)); + } + +} + diff --git a/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h new file mode 100644 index 000000000..718e31fa1 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h @@ -0,0 +1,169 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_ARTICULATION_RC +#define PX_PHYSICS_NP_ARTICULATION_RC + +#include "PxArticulationReducedCoordinate.h" +#include "CmPhysXCommon.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +#include "ScbArticulation.h" +#include "NpArticulationLink.h" +#include "NpArticulationTemplate.h" +#include "NpArticulationJointReducedCoordinate.h" + +namespace physx +{ + + class NpArticulationLink; + class NpScene; + class NpAggregate; + class PxAggregate; + class PxJoint; + + class NpArticulationReducedCoordinate : public NpArticulationTemplate + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + virtual ~NpArticulationReducedCoordinate(); + + //KS - todo - re-enable serialization later... + // PX_SERIALIZATION + NpArticulationReducedCoordinate(PxBaseFlags baseFlags) : NpArticulationTemplate(baseFlags) + { + mType = PxArticulationBase::eReducedCoordinate; + } + + /*virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + virtual void requires(PxProcessPxBaseCallback& c); + static NpArticulation* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream);*/ + //~PX_SERIALIZATION + + //--------------------------------------------------------------------------------- + // PxArticulation implementation + //--------------------------------------------------------------------------------- + + //--------------------------------------------------------------------------------- + // PxArticulationReducedCoordinate implementation + //--------------------------------------------------------------------------------- + virtual void setArticulationFlags(PxArticulationFlags flags); + + virtual void setArticulationFlag(PxArticulationFlag::Enum flag, bool value); + + virtual PxArticulationFlags getArticulationFlags() const; + + virtual PxU32 getDofs() const; + + virtual PxArticulationCache* createCache() const; + + virtual PxU32 getCacheDataSize() const; + + virtual void zeroCache(PxArticulationCache& cache); + + virtual void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake); + + virtual void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const; + + virtual void releaseCache(PxArticulationCache& cache) const; + + virtual void packJointData(const PxReal* maximum, PxReal* reduced) const; + + virtual void unpackJointData(const PxReal* reduced, PxReal* maximum) const; + + virtual void commonInit() const; + + virtual void computeGeneralizedGravityForce(PxArticulationCache& cache) const; + + virtual void computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const; + + virtual void computeGeneralizedExternalForce(PxArticulationCache& cache) const; + + virtual void computeJointAcceleration(PxArticulationCache& cache) const; + + virtual void computeJointForce(PxArticulationCache& cache) const; + + virtual void computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const; + + virtual void computeCoefficentMatrix(PxArticulationCache& cache) const; + + virtual bool computeLambda(PxArticulationCache& cache, PxArticulationCache& rollBackCache, const PxReal* const jointTorque, const PxU32 maxIter) const; + + virtual void computeGeneralizedMassMatrix(PxArticulationCache& cache) const; + + virtual void addLoopJoint(PxJoint* joint); + + virtual void removeLoopJoint(PxJoint* constraint); + + virtual PxU32 getNbLoopJoints() const; + + virtual PxU32 getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex = 0) const; + + virtual PxU32 getCoefficentMatrixSize() const; + + virtual void teleportRootLink(const PxTransform& pose, bool autowake); + + virtual const char* getConcreteTypeName() const { return "PxArticulation"; } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpArticulationReducedCoordinate(); + + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulation", name) || PxBase::isKindOf(name); } + + virtual PxArticulationJointBase* createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame); + virtual void releaseArticulationJoint(PxArticulationJointBase* joint); + + private: + + Ps::Array mLoopJoints; + + friend class NpScene; + }; + + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpArticulationTemplate.h b/src/PhysX/physx/source/physx/src/NpArticulationTemplate.h new file mode 100644 index 000000000..fbe4dc68b --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpArticulationTemplate.h @@ -0,0 +1,568 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_ARTICULATION_TEMPLATE +#define NP_ARTICULATION_TEMPLATE + +#include "CmPhysXCommon.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +#include "ScbArticulation.h" +#include "NpArticulationLink.h" +#include "NpAggregate.h" + +namespace physx +{ + +class NpArticulationLink; +class NpScene; +class NpAggregate; +class PxAggregate; + +class PxArticulationImpl +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + ~PxArticulationImpl() + { + } + + PxArticulationImpl() : + mAggregate(NULL), mName(NULL), mCacheVersion(0) {} + + // PX_SERIALIZATION + PxArticulationImpl(const PxEMPTY) : mArticulation(PxEmpty), mArticulationLinks(PxEmpty) {} + //~PX_SERIALIZATION + + + PX_INLINE PxScene* getScene() const; + + PX_INLINE void setSleepThreshold(PxReal threshold); + PX_INLINE PxReal getSleepThreshold() const; + + PX_INLINE void setStabilizationThreshold(PxReal threshold); + PX_INLINE PxReal getStabilizationThreshold() const; + + PX_INLINE void setWakeCounter(PxReal wakeCounterValue); + PX_INLINE PxReal getWakeCounter() const; + + PX_INLINE void setSolverIterationCounts(PxU32 positionIters, PxU32 velocityIters); + PX_INLINE void getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const; + + PX_INLINE bool isSleeping() const; + PX_INLINE void wakeUp(); + PX_INLINE void putToSleep(); + + PX_INLINE PxU32 getNbLinks() const; + PX_INLINE PxU32 getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + PX_INLINE PxBounds3 getWorldBounds(float inflation = 1.01f) const; + + PX_INLINE PxAggregate* getAggregate() const; + + // Debug name + PX_INLINE void setName(const char*); + PX_INLINE const char* getName() const; + + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + + PX_INLINE void addToLinkList(NpArticulationLink& link) { mArticulationLinks.pushBack(&link); } + PX_INLINE bool removeLinkFromList(NpArticulationLink& link) { PX_ASSERT(mArticulationLinks.find(&link) != mArticulationLinks.end()); return mArticulationLinks.findAndReplaceWithLast(&link); } + PX_FORCE_INLINE NpArticulationLink* const* getLinks() { return &mArticulationLinks.front(); } + + PX_INLINE NpArticulationLink* getRoot(); + + PX_FORCE_INLINE const Scb::Articulation& getArticulation() const { return mArticulation; } + PX_FORCE_INLINE Scb::Articulation& getArticulation() { return mArticulation; } + + PX_INLINE NpScene* getAPIScene() const; + PX_INLINE NpScene* getOwnerScene() const; // the scene the user thinks the actor is in, or from which the actor is pending removal + + PX_FORCE_INLINE void setAggregate(PxAggregate* a) { mAggregate = static_cast(a); } + + PX_INLINE void wakeUpInternal(bool forceWakeUp, bool autowake); + + PX_INLINE void setGlobalPose(); + + PX_FORCE_INLINE Scb::Articulation& getScbArticulation() { return mArticulation; } + PX_FORCE_INLINE const Scb::Articulation& getScbArticulation() const { return mArticulation; } + + void increaseCacheVersion() { mCacheVersion++; } + + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + PX_INLINE void visualize(Cm::RenderOutput& out, NpScene* scene); +#endif + + + Scb::Articulation mArticulation; + NpArticulationLinkArray mArticulationLinks; + NpAggregate* mAggregate; + const char* mName; + PxU32 mCacheVersion; +}; + +template +class NpArticulationTemplate : public APIClass, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + virtual ~NpArticulationTemplate(); + NpArticulationTemplate(PxType concreteType, PxBaseFlags baseFlags) : APIClass(concreteType, baseFlags) {} + + // PX_SERIALIZATION + NpArticulationTemplate(PxBaseFlags baseFlags) : APIClass(baseFlags), mImpl(PxEmpty) {} + //~PX_SERIALIZATION + + + virtual void release(); + + virtual PxScene* getScene() const { return mImpl.getScene(); } + + virtual void setSleepThreshold(PxReal threshold) { mImpl.setSleepThreshold(threshold); } + virtual PxReal getSleepThreshold() const { return mImpl.getSleepThreshold();} + + virtual void setStabilizationThreshold(PxReal threshold) { mImpl.setStabilizationThreshold(threshold); } + virtual PxReal getStabilizationThreshold() const { return mImpl.getStabilizationThreshold(); } + + virtual void setWakeCounter(PxReal wakeCounterValue) { mImpl.setWakeCounter(wakeCounterValue); } + virtual PxReal getWakeCounter() const {return mImpl.getWakeCounter(); } + + virtual void setSolverIterationCounts(PxU32 positionIters, PxU32 velocityIters) { mImpl.setSolverIterationCounts(positionIters, velocityIters); } + virtual void getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const { mImpl.getSolverIterationCounts(positionIters, velocityIters); } + + virtual bool isSleeping() const { return mImpl.isSleeping(); } + virtual void wakeUp() { mImpl.wakeUp(); } + virtual void putToSleep() { mImpl.putToSleep(); } + + virtual PxU32 getNbLinks() const { return mImpl.getNbLinks(); } + virtual PxU32 getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const + { + return mImpl.getLinks(userBuffer, bufferSize, startIndex); + } + + virtual PxBounds3 getWorldBounds(float inflation = 1.01f) const { return mImpl.getWorldBounds(inflation); } + + virtual PxAggregate* getAggregate() const { return mImpl.getAggregate(); } + + // Debug name + virtual void setName(const char* name) { return mImpl.setName(name); } + virtual const char* getName() const { return mImpl.getName(); } + + virtual PxArticulationLink* createLink(PxArticulationLink* parent, const PxTransform& pose); + + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpArticulationTemplate(); + + virtual const PxArticulationImpl* getImpl() const { return &mImpl; } + + virtual PxArticulationImpl* getImpl() { return &mImpl; } + + virtual PxArticulationBase::Enum getType() const { return PxArticulationBase::Enum(mType); } + + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene) { mImpl.visualize(out, scene); } +#endif + +public: + + PxU32 mType; + PxArticulationImpl mImpl; + + +}; + + +template +NpArticulationTemplate::NpArticulationTemplate() + : APIClass(PxConcreteType::eARTICULATION, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +{ + PxArticulationBase::userData = NULL; + mType = PxArticulationBase::eMaximumCoordinate; +} + +template +NpArticulationTemplate::~NpArticulationTemplate() +{ + NpFactory::getInstance().onArticulationRelease(this); +} + +template +void NpArticulationTemplate::release() +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, PxArticulationBase::userData); + + //!!!AL TODO: Order should not matter in this case. Optimize by having a path which does not restrict release to leaf links or + // by using a more advanced data structure + PxU32 idx = 0; + while (mImpl.mArticulationLinks.size()) + { + idx = idx % mImpl.mArticulationLinks.size(); + + if (mImpl.mArticulationLinks[idx]->getNbChildren() == 0) + { + mImpl.mArticulationLinks[idx]->releaseInternal(); // deletes joint, link and removes link from list + } + else + { + idx++; + } + } + + NpScene* npScene = mImpl.getAPIScene(); + if (npScene) + { + npScene->getScene().removeArticulation(mImpl.getArticulation()); + + npScene->removeFromArticulationList(*this); + } + + mImpl.mArticulationLinks.clear(); + + mImpl.mArticulation.destroy(); +} + +template +PxArticulationLink* NpArticulationTemplate::createLink(PxArticulationLink* parent, const PxTransform& pose) +{ + PX_CHECK_AND_RETURN_NULL(pose.isSane(), "NpArticulation::createLink pose is not valid."); + PX_CHECK_AND_RETURN_NULL(mImpl.mArticulationLinks.size()<64, "NpArticulation::createLink: at most 64 links allowed in an articulation"); + + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + if (parent && mImpl.mArticulationLinks.empty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Root articulation link must have NULL parent pointer!"); + return NULL; + } + + if (!parent && !mImpl.mArticulationLinks.empty()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Non-root articulation link must have valid parent pointer!"); + return NULL; + } + + //increase cache version + mImpl.mCacheVersion++; + + NpArticulationLink* parentLink = static_cast(parent); + + NpArticulationLink* link = static_cast(NpFactory::getInstance().createArticulationLink(*this, parentLink, pose.getNormalized())); + + if (link) + { + NpScene* scene = mImpl.getAPIScene(); + if (scene) + scene->addArticulationLink(*link); + + mImpl.addToLinkList(*link); + } + return link; +} + +PxScene* PxArticulationImpl::getScene() const +{ + return getAPIScene(); +} + +void PxArticulationImpl::setSolverIterationCounts(PxU32 positionIters, PxU32 velocityIters) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(positionIters > 0, "Articulation::setSolverIterationCount: positionIters must be more than zero!"); + PX_CHECK_AND_RETURN(positionIters <= 255, "Articulation::setSolverIterationCount: positionIters must be no greater than 255!"); + PX_CHECK_AND_RETURN(velocityIters > 0, "Articulation::setSolverIterationCount: velocityIters must be more than zero!"); + PX_CHECK_AND_RETURN(velocityIters <= 255, "Articulation::setSolverIterationCount: velocityIters must be no greater than 255!"); + + getArticulation().setSolverIterationCounts((velocityIters & 0xff) << 8 | (positionIters & 0xff)); +} + + +void PxArticulationImpl::getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const +{ + NP_READ_CHECK(getOwnerScene()); + PxU16 x = getArticulation().getSolverIterationCounts(); + velocityIters = PxU32(x >> 8); + positionIters = PxU32(x & 0xff); +} + + +void PxArticulationImpl::setGlobalPose() +{ + PX_CHECK_AND_RETURN(getAPIScene(), "PxArticulation::setGlobalPose: object must be in a scene"); + NP_READ_CHECK(getOwnerScene()); + + mArticulation.setGlobalPose(); + + //This code is force PVD to update other links position + if (!mArticulation.isBuffering()) + { + physx::NpArticulationLink*const* links = getLinks(); + + const PxU32 nbLinks = getNbLinks(); + for (PxU32 i = 1; i < nbLinks; ++i) + { + physx::NpArticulationLink* link = links[i]; + //in the lowlevel articulation, we have already updated bodyCore's body2World + const PxTransform internalPose = link->getScbBodyFast().getScBody().getBody2World(); + link->getScbBodyFast().setBody2World(internalPose, false); + } + } +} + + +bool PxArticulationImpl::isSleeping() const +{ + NP_READ_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN_VAL(getAPIScene(), "Articulation::isSleeping: articulation must be in a scene.", true); + + return getArticulation().isSleeping(); +} + + +void PxArticulationImpl::setSleepThreshold(PxReal threshold) +{ + NP_WRITE_CHECK(getOwnerScene()); + getArticulation().setSleepThreshold(threshold); +} + + +PxReal PxArticulationImpl::getSleepThreshold() const +{ + NP_READ_CHECK(getOwnerScene()); + return getArticulation().getSleepThreshold(); +} + + +void PxArticulationImpl::setStabilizationThreshold(PxReal threshold) +{ + NP_WRITE_CHECK(getOwnerScene()); + getArticulation().setFreezeThreshold(threshold); +} + + +PxReal PxArticulationImpl::getStabilizationThreshold() const +{ + NP_READ_CHECK(getOwnerScene()); + return getArticulation().getFreezeThreshold(); +} + + + +void PxArticulationImpl::setWakeCounter(PxReal wakeCounterValue) +{ + NP_WRITE_CHECK(getOwnerScene()); + + for (PxU32 i = 0; i < mArticulationLinks.size(); i++) + { + mArticulationLinks[i]->getScbBodyFast().setWakeCounter(wakeCounterValue); + } + + getArticulation().setWakeCounter(wakeCounterValue); +} + + +PxReal PxArticulationImpl::getWakeCounter() const +{ + NP_READ_CHECK(getOwnerScene()); + return getArticulation().getWakeCounter(); +} + + +void PxArticulationImpl::wakeUpInternal(bool forceWakeUp, bool autowake) +{ + NpScene* scene = getAPIScene(); + PX_ASSERT(scene); + PxReal wakeCounterResetValue = scene->getWakeCounterResetValueInteral(); + + Scb::Articulation& a = getArticulation(); + PxReal wakeCounter = a.getWakeCounter(); + + bool needsWakingUp = isSleeping() && (autowake || forceWakeUp); + if (autowake && (wakeCounter < wakeCounterResetValue)) + { + wakeCounter = wakeCounterResetValue; + needsWakingUp = true; + } + + if (needsWakingUp) + { + for (PxU32 i = 0; i < mArticulationLinks.size(); i++) + { + mArticulationLinks[i]->getScbBodyFast().wakeUpInternal(wakeCounter); + } + + a.wakeUpInternal(wakeCounter); + } +} + + +void PxArticulationImpl::wakeUp() +{ + NpScene* scene = getAPIScene(); + + NP_WRITE_CHECK(getOwnerScene()); // don't use the API scene, since it will be NULL on pending removal + PX_CHECK_AND_RETURN(scene, "Articulation::wakeUp: articulation must be in a scene."); + + for (PxU32 i = 0; i < mArticulationLinks.size(); i++) + { + mArticulationLinks[i]->getScbBodyFast().wakeUpInternal(scene->getWakeCounterResetValueInteral()); + } + + getArticulation().wakeUp(); +} + + +void PxArticulationImpl::putToSleep() +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(getAPIScene(), "Articulation::putToSleep: articulation must be in a scene."); + + for (PxU32 i = 0; i < mArticulationLinks.size(); i++) + { + mArticulationLinks[i]->getScbBodyFast().putToSleepInternal(); + } + + getArticulation().putToSleep(); +} + + +PxU32 PxArticulationImpl::getNbLinks() const +{ + NP_READ_CHECK(getOwnerScene()); + return mArticulationLinks.size(); +} + + +PxU32 PxArticulationImpl::getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(getOwnerScene()); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mArticulationLinks.begin(), mArticulationLinks.size()); +} + + +PxBounds3 PxArticulationImpl::getWorldBounds(float inflation) const +{ + NP_READ_CHECK(getOwnerScene()); + PxBounds3 bounds = PxBounds3::empty(); + + for (PxU32 i = 0; i < mArticulationLinks.size(); i++) + { + bounds.include(mArticulationLinks[i]->getWorldBounds()); + } + PX_ASSERT(bounds.isValid()); + + // PT: unfortunately we can't just scale the min/max vectors, we need to go through center/extents. + const PxVec3 center = bounds.getCenter(); + const PxVec3 inflatedExtents = bounds.getExtents() * inflation; + return PxBounds3::centerExtents(center, inflatedExtents); +} + + +PxAggregate* PxArticulationImpl::getAggregate() const +{ + NP_READ_CHECK(getOwnerScene()); + return mAggregate; +} + + +void PxArticulationImpl::setName(const char* debugName) +{ + NP_WRITE_CHECK(getOwnerScene()); + mName = debugName; +} + + +const char* PxArticulationImpl::getName() const +{ + NP_READ_CHECK(getOwnerScene()); + return mName; +} + + +NpScene* PxArticulationImpl::getAPIScene() const +{ + return static_cast(mArticulation.getScbSceneForAPI() ? mArticulation.getScbSceneForAPI()->getPxScene() : NULL); +} + + +NpScene* PxArticulationImpl::getOwnerScene() const +{ + return static_cast(mArticulation.getScbScene() ? mArticulation.getScbScene()->getPxScene() : NULL); +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION + +void PxArticulationImpl::visualize(Cm::RenderOutput& out, NpScene* scene) +{ + for (PxU32 i = 0; ivisualize(out, scene); +} +#endif // PX_ENABLE_DEBUG_VISUALIZATION + + +NpArticulationLink* PxArticulationImpl::getRoot() +{ + if (!mArticulationLinks.size()) + return NULL; + + PX_ASSERT(mArticulationLinks[0]->getInboundJoint() == NULL); + return mArticulationLinks[0]; +} + +} + +#endif //NP_ARTICULATION_TEMPLATE + diff --git a/src/PhysX/physx/source/physx/src/NpBatchQuery.cpp b/src/PhysX/physx/source/physx/src/NpBatchQuery.cpp new file mode 100644 index 000000000..b31ea85a9 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpBatchQuery.cpp @@ -0,0 +1,603 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpBatchQuery.h" +#include "NpReadCheck.h" +#include "NpActor.h" +#include "NpShapeManager.h" +#include "PsAtomic.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "NpScene.h" +#include "PxGeometryQuery.h" +#include "common/PxProfileZone.h" + +using namespace physx; +using namespace Sq; +using namespace Cm; + +#if !PX_P64_FAMILY +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxRaycastHit)& 0x0f)); +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxSweepHit)& 0x0f)); +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxOverlapHit)& 0x0f)); +#endif + +#define CHECK_RUNNING(QueryMessage) \ + if(Ps::atomicCompareExchange(&mBatchQueryIsRunning, -1, 0) == 1)\ + {\ + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, QueryMessage); return;\ + } + +NpBatchQuery::NpBatchQuery(NpScene& owner, const PxBatchQueryDesc& d) + : mNpScene(&owner), mNbRaycasts(0), mNbOverlaps(0), mNbSweeps(0), mBatchQueryIsRunning(0), mDesc(d), mPrevOffset(PxU32(eTERMINAL)) +{ + mHasMtdSweep = false; +} + +NpBatchQuery::~NpBatchQuery() +{ +} + +void NpBatchQuery::setUserMemory(const PxBatchQueryMemory& userMem) +{ + if(Ps::atomicCompareExchange(&mBatchQueryIsRunning, 0, 0) != 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxBatchQuery::setUserMemory: This batch is still executing, skipping setUserMemory"); + return; + } + + PxBatchQueryDesc& desc = getDesc(); + + desc.queryMemory = userMem; +} + +const PxBatchQueryMemory& NpBatchQuery::getUserMemory() +{ + return getDesc().queryMemory; +} + +// ROS abbreviates Raycast/Overlap/Sweep +struct QTypeROS { enum Enum { eRAYCAST = 0, eOVERLAP = 1, eSWEEP = 2 }; }; // AP: perhaps can be shared with some other code + +namespace physx +{ +struct BatchStreamHeader +{ + BatchStreamHeader( + PxHitFlags aHitFlags, const PxQueryCache* aCache, const PxQueryFilterData& aFd, + void* aUserData, PxU16 aMaxTouchHits, QTypeROS::Enum aHitTypeId) : + hitFlags(aHitFlags), fd(aFd), userData(aUserData), cache(aCache), + maxTouchHits(aMaxTouchHits), hitTypeId(char(aHitTypeId)) + { + nextQueryOffset = PxU32(NpBatchQuery::eTERMINAL); + } + + BatchStreamHeader() {} + + // TODO: possibly maintain 3 separate offset lists in the same array for raycasts/overlaps/sweeps + // offset of a 4-byte ptr to offset in the previously stored batch query in the stream, this is not a ptr because of reallocs + PxU32 nextQueryOffset; + PxHitFlags hitFlags; + PxQueryFilterData fd; + void* userData; + const PxQueryCache* cache; + PxU16 maxTouchHits; + char hitTypeId; // Sq::QTypeROS +}; +} // namespace physx + +static void writeGeom(BatchQueryStream& stream, const PxGeometry& geom) +{ + PxGeometryType::Enum geomType = geom.getType(); + stream.write(PxU32(geomType)); + switch (geomType) + { + case PxGeometryType::eCAPSULE: + stream.write(static_cast(geom)); + break; + case PxGeometryType::eSPHERE: + stream.write(static_cast(geom)); + break; + case PxGeometryType::eCONVEXMESH: + stream.write(static_cast(geom)); + break; + case PxGeometryType::eBOX: + stream.write(static_cast(geom)); + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("Unsupported geometry type in writeGeom"); + } +} + +static PxGeometry* readGeom(BatchQueryStreamReader& reader) +{ + PxU32 geomType = *reader.read(); + PxGeometry* anyGeom = NULL; + switch (geomType) + { + case PxGeometryType::eCAPSULE: + anyGeom = reader.read(); + break; + case PxGeometryType::eSPHERE: + anyGeom = reader.read(); + break; + case PxGeometryType::eCONVEXMESH: + anyGeom = reader.read(); + break; + case PxGeometryType::eBOX: + anyGeom = reader.read(); + break; + default: + PX_ALWAYS_ASSERT_MESSAGE("Unsupported geometry type in readGeom"); + } + + return anyGeom; +} + +static void writeQueryInput(BatchQueryStream& stream, const MultiQueryInput& input) +{ + stream.write(input); + if (input.rayOrigin) + stream.write(input.rayOrigin); + if (input.unitDir) + stream.write(input.unitDir); + if (input.pose) + stream.write(input.pose); + if (input.geometry) + writeGeom(stream, *input.geometry); +} + +static MultiQueryInput* readQueryInput(BatchQueryStreamReader& stream) +{ + MultiQueryInput* input = stream.read(); + if (input->rayOrigin) + input->rayOrigin = stream.read(); + if (input->unitDir) + input->unitDir = stream.read(); + if (input->pose) + input->pose = stream.read(); + if (input->geometry) + input->geometry = readGeom(stream); + + return input; +} + +template +void writeStatus(ResultType* aRes, const PxHitBuffer& hits, void* userData, bool overflow) +{ + ResultType* res = aRes; + res->userData = userData; + res->block = hits.block; + res->hasBlock = hits.hasBlock; + res->nbTouches = hits.nbTouches; + res->queryStatus = PxU8(overflow ? PxBatchQueryStatus::eOVERFLOW : PxBatchQueryStatus::eSUCCESS); + res->touches = (overflow && res->nbTouches == 0) ? NULL : hits.touches; +} + +void NpBatchQuery::resetResultBuffers() +{ + for (PxU32 i = 0; i +struct PxOverflowBuffer : PxHitBuffer +{ + bool overflow; + PxU32 saveNbTouches; + HitType extraHit; + HitType* saveTouches; + bool processCalled; + PxOverflowBuffer(HitType* hits, PxU32 count) : PxHitBuffer(hits, count), overflow(false), processCalled(false) + { + } + + virtual PxAgain processTouches(const HitType* /*hits*/, PxU32 /*count*/) + { + if (processCalled) + return false; + saveTouches = this->touches; + saveNbTouches = this->nbTouches; + processCalled = true; + this->touches = &extraHit; + this->maxNbTouches = 1; + return true; + } + + virtual void finalizeQuery() + { + if (processCalled) + { + overflow = (this->nbTouches > 0); + this->nbTouches = saveNbTouches; + this->touches = saveTouches; + } + } +}; + +void NpBatchQuery::execute() +{ + NP_READ_CHECK(mNpScene); + + if(mNbRaycasts) + { + PX_CHECK_AND_RETURN(mDesc.queryMemory.userRaycastResultBuffer!=NULL, "PxBatchQuery execute: userRaycastResultBuffer is NULL"); + PX_CHECK_AND_RETURN(mDesc.queryMemory.raycastTouchBufferSize > 0 ? + (mDesc.queryMemory.userRaycastTouchBuffer != NULL) : true, "PxBatchQuery execute: userRaycastTouchBuffer is NULL"); + } + if(mNbOverlaps) + { + PX_CHECK_AND_RETURN(mDesc.queryMemory.userOverlapResultBuffer!=NULL, "PxBatchQuery execute: userOverlapResultBuffer is NULL"); + PX_CHECK_AND_RETURN(mDesc.queryMemory.overlapTouchBufferSize > 0 ? + (mDesc.queryMemory.userOverlapTouchBuffer != NULL) : true, "PxBatchQuery execute: userOverlapTouchBuffer is NULL"); + } + if(mNbSweeps) + { + PX_CHECK_AND_RETURN(mDesc.queryMemory.userSweepResultBuffer!=NULL, "PxBatchQuery execute: userSweepResultBuffer is NULL"); + PX_CHECK_AND_RETURN(mDesc.queryMemory.sweepTouchBufferSize > 0 ? + (mDesc.queryMemory.userSweepTouchBuffer != NULL) : true, "PxBatchQuery execute: userSweepTouchBuffer is NULL"); + } + + PX_SIMD_GUARD; + + PX_PROFILE_ZONE("BatchedSceneQuery.execute", mNpScene->getContextId()); + PxI32 ret = Ps::atomicCompareExchange(&mBatchQueryIsRunning, 1, 0); + if(ret == 1) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxBatchQuery::execute: This batch is already executing"); + return; + } + else if(ret == -1) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxBatchQuery::execute: Another thread is still adding queries to this batch"); + return; + } + + resetResultBuffers(); + + // If PVD is connected and IS_PVD_SQ_ENABLED, record the offsets for queries in pvd buffers and run the queries on PPU + bool isSqCollectorLocked = false; + PX_UNUSED(isSqCollectorLocked); + +#if PX_SUPPORT_PVD + PxU32 pvdRayQstartIdx = 0; + PxU32 pvdOverlapQstartIdx = 0; + PxU32 pvdSweepQstartIdx = 0; + + Vd::ScbScenePvdClient& pvdClient = mNpScene->mScene.getScenePvdClient(); + const bool needUpdatePvd = pvdClient.checkPvdDebugFlag() && (pvdClient.getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES); + + if(needUpdatePvd) + { + mNpScene->getBatchedSqCollector().getLock().lock(); + isSqCollectorLocked = true; + + pvdRayQstartIdx = mNpScene->getBatchedSqCollector().mAccumulatedRaycastQueries.size(); + pvdOverlapQstartIdx = mNpScene->getBatchedSqCollector().mAccumulatedOverlapQueries.size(); + pvdSweepQstartIdx = mNpScene->getBatchedSqCollector().mAccumulatedSweepQueries.size(); + } +#endif + + // setup local pointers to user provided output buffers + PxRaycastHit* raycastHits = mDesc.queryMemory.userRaycastTouchBuffer; PX_UNUSED(raycastHits); + PxRaycastQueryResult* raycastResults = mDesc.queryMemory.userRaycastResultBuffer; PX_UNUSED(raycastResults); + PxU32 raycastHitsSize = mDesc.queryMemory.raycastTouchBufferSize; PX_UNUSED(raycastHitsSize); + + PxOverlapHit* overlapHits = mDesc.queryMemory.userOverlapTouchBuffer; PX_UNUSED(overlapHits); + PxOverlapQueryResult* overlapResults = mDesc.queryMemory.userOverlapResultBuffer; PX_UNUSED(overlapResults); + PxU32 overlapHitsSize = mDesc.queryMemory.overlapTouchBufferSize; PX_UNUSED(overlapHitsSize); + + PxSweepHit* sweepHits = mDesc.queryMemory.userSweepTouchBuffer; PX_UNUSED(sweepHits); + PxSweepQueryResult* sweepResults = mDesc.queryMemory.userSweepResultBuffer; PX_UNUSED(sweepResults); + PxU32 sweepHitsSize = mDesc.queryMemory.sweepTouchBufferSize; PX_UNUSED(sweepHitsSize); + + BatchQueryFilterData bfd(mDesc.filterShaderData, mDesc.filterShaderDataSize, mDesc.preFilterShader, mDesc.postFilterShader); + + // data declarations for double buffering the input stream + PxU32 curQueryOffset = 0; // first query starts at 0 + if (mPrevOffset == eTERMINAL) // except if zero queries were queued + { + finalizeExecute(); + return; + } + + PxU32 hitsSpaceLeft; PX_UNUSED(hitsSpaceLeft); + + // ====================== parse and execute the batch query memory stream ====================== + PxU32 queryCount = 0; + do { + // parse a query from the input stream, create a stream reader at current double buffer + BatchQueryStreamReader reader(mStream.begin()+curQueryOffset); + BatchStreamHeader& h = *reader.read(); + + curQueryOffset = h.nextQueryOffset; + Ps::prefetchLine(mStream.begin() + curQueryOffset); + + MultiQueryInput& input = *readQueryInput(reader); + + // ====================== switch over query type - QTypeROS::eRAYCAST, eOVERLAP, eSWEEP ===================== + switch (h.hitTypeId) + { + // =============== Current query is a raycast ===================== + case QTypeROS::eRAYCAST: + { + PxU32 nbRaycastHits = PxU32(raycastHits - mDesc.queryMemory.userRaycastTouchBuffer); + PX_ASSERT(nbRaycastHits <= raycastHitsSize); + hitsSpaceLeft = raycastHitsSize - nbRaycastHits; + PxOverflowBuffer hits(raycastHits, PxMin(h.maxTouchHits, hitsSpaceLeft)); + mNpScene->NpScene::multiQuery(input, hits, h.hitFlags, h.cache, h.fd, NULL, &bfd); + hits.overflow |= (hitsSpaceLeft == 0 && h.maxTouchHits > 0); // report overflow if 0 space left and maxTouchHits>0 + writeStatus(raycastResults++, hits, h.userData, hits.overflow); + raycastHits += hits.nbTouches; + } break; + + // ================ Current query is an overlap ==================== + case QTypeROS::eOVERLAP: + { + PxU32 nbOverlapHits = PxU32(overlapHits - mDesc.queryMemory.userOverlapTouchBuffer); + PX_ASSERT(nbOverlapHits <= overlapHitsSize); + hitsSpaceLeft = overlapHitsSize - nbOverlapHits; + PxOverflowBuffer hits(overlapHits, PxMin(h.maxTouchHits, hitsSpaceLeft)); + mNpScene->NpScene::multiQuery(input, hits, h.hitFlags, h.cache, h.fd, NULL, &bfd); + hits.overflow |= (hitsSpaceLeft == 0 && h.maxTouchHits > 0); // report overflow if 0 space left and maxTouchHits>0 + writeStatus(overlapResults++, hits, h.userData, hits.overflow); + overlapHits += hits.nbTouches; + } break; + + // ================== Current query is a sweep ========================= + case QTypeROS::eSWEEP: + { + + PxU32 nbSweepHits = PxU32(sweepHits - mDesc.queryMemory.userSweepTouchBuffer); + PX_ASSERT(nbSweepHits <= sweepHitsSize); + hitsSpaceLeft = sweepHitsSize - nbSweepHits; + PxOverflowBuffer hits(sweepHits, PxMin(h.maxTouchHits, hitsSpaceLeft)); + mNpScene->NpScene::multiQuery(input, hits, h.hitFlags, h.cache, h.fd, NULL, &bfd); + hits.overflow |= (hitsSpaceLeft == 0 && h.maxTouchHits > 0); // report overflow if 0 space left and maxTouchHits>0 + writeStatus(sweepResults++, hits, h.userData, hits.overflow); + sweepHits += hits.nbTouches; + } break; + default: + PX_ALWAYS_ASSERT_MESSAGE("Unexpected batch query type (raycast/overlap/sweep)."); + } + + if (h.nextQueryOffset == eTERMINAL) // end of stream + // AP: previously also had a break on hitCount==-1 which is aborted due to out of space + // abort stream parsing if we ran into an aborted query (hitCount==-1).. but it was easier to just continue + // the perf implications for aborted queries are not a significant consideration and this allows to avoid + // writing special case code for filling the query buffers after aborted query + break; + #undef MULTIQ + queryCount++; + } while (queryCount < 1000000); + +#if PX_SUPPORT_PVD + if( isSqCollectorLocked && needUpdatePvd) + { + mNpScene->getBatchedSqCollector().collectAllBatchedHits( mDesc.queryMemory.userRaycastResultBuffer, mNbRaycasts, pvdRayQstartIdx, + mDesc.queryMemory.userOverlapResultBuffer, mNbOverlaps, pvdOverlapQstartIdx, + mDesc.queryMemory.userSweepResultBuffer, mNbSweeps, pvdSweepQstartIdx); + } + + // Maybe connection is disconnected after the lock + if( isSqCollectorLocked ) + { + mNpScene->getBatchedSqCollector().getLock().unlock(); + isSqCollectorLocked = false; + } +#endif + + finalizeExecute(); +} + +/////////////////////////////////////////////////////////////////////////////// +void NpBatchQuery::writeBatchHeader(const BatchStreamHeader& h) +{ + PxU32 streamPos = PxU32(mStream.getPos()); // save the stream pos before we write the header + mStream.write(h); + // link into a list as offset + PxU32* prevPtr = (mPrevOffset == eTERMINAL) ? &mPrevOffset : reinterpret_cast(mStream.begin()+mPrevOffset); + PxU32 headerPtr = streamPos+PX_OFFSET_OF(BatchStreamHeader, nextQueryOffset); + *prevPtr = headerPtr; + mPrevOffset = headerPtr; +} + +/////////////////////////////////////////////////////////////////////////////// +void NpBatchQuery::raycast( + const PxVec3& origin, const PxVec3& unitDir, PxReal distance, PxU16 maxTouchHits, + PxHitFlags hitFlags, const PxQueryFilterData& fd, void* userData, const PxQueryCache* cache) +{ + PX_CHECK_AND_RETURN(distance>0, "PxBatchQuery::raycast: The maximum distance must be greater than zero!"); + PX_CHECK_AND_RETURN(unitDir.isNormalized(), "PxBatchQuery::raycast: Direction must be normalized"); + PX_CHECK_AND_RETURN(origin.isFinite(), "PxBatchQuery::raycast: origin is not valid"); + if (mNbRaycasts >= mDesc.queryMemory.getMaxRaycastsPerExecute()) + { + PX_CHECK_AND_RETURN(mNbRaycasts < mDesc.queryMemory.getMaxRaycastsPerExecute(), + "PxBatchQuery: number of raycast() calls exceeds PxBatchQueryMemory::raycastResultBufferSize, query discarded"); + return; + } + CHECK_RUNNING("PxBatchQuery::raycast: This batch is still executing, skipping query."); + mNbRaycasts++; + + writeBatchHeader(BatchStreamHeader(hitFlags, cache, fd, userData, maxTouchHits, QTypeROS::eRAYCAST)); + writeQueryInput(mStream, MultiQueryInput(origin, unitDir, distance)); + + Ps::atomicExchange(&mBatchQueryIsRunning, 0); +} + +/////////////////////////////////////////////////////////////////////////////// +void NpBatchQuery::overlap( + const PxGeometry& geometry, const PxTransform& pose, PxU16 maxTouchHits, + const PxQueryFilterData& fd, void* userData, const PxQueryCache* cache) +{ + PX_CHECK_AND_RETURN(pose.isValid(), "NpBatchQuery::overlapMultiple pose is not valid."); + if (mNbOverlaps >= mDesc.queryMemory.getMaxOverlapsPerExecute()) + { + PX_CHECK_AND_RETURN(mNbOverlaps < mDesc.queryMemory.getMaxOverlapsPerExecute(), + "PxBatchQuery: number of overlap() calls exceeds PxBatchQueryMemory::overlapResultBufferSize, query discarded"); + return; + } + CHECK_RUNNING("PxBatchQuery::overlap: This batch is still executing, skipping query.") + mNbOverlaps++; + + writeBatchHeader(BatchStreamHeader(PxHitFlags(), cache, fd, userData, maxTouchHits, QTypeROS::eOVERLAP)); + writeQueryInput(mStream, MultiQueryInput(&geometry, &pose)); + + Ps::atomicExchange(&mBatchQueryIsRunning, 0); +} + +void NpBatchQuery::sweep( + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, PxU16 maxTouchHits, + PxHitFlags hitFlags, const PxQueryFilterData& fd, void* userData, const PxQueryCache* cache, const PxReal inflation) +{ + PX_CHECK_AND_RETURN(pose.isValid(), "Batch sweep input check: pose is not valid."); + PX_CHECK_AND_RETURN(unitDir.isFinite(), "Batch sweep input check: unitDir is not valid."); + PX_CHECK_AND_RETURN(unitDir.isNormalized(), "Batch sweep input check: direction must be normalized"); + PX_CHECK_AND_RETURN(distance >= 0.0f, "Batch sweep input check: distance cannot be negative"); + PX_CHECK_AND_RETURN(distance != 0.0f || !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP), + "Batch sweep input check: zero-length sweep only valid without the PxHitFlag::eASSUME_NO_INITIAL_OVERLAP flag"); + +#if PX_CHECKED + if(!PxGeometryQuery::isValid(geometry)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry is not valid"); + return; + } +#endif // PX_CHECKED + + if (mNbSweeps >= mDesc.queryMemory.getMaxSweepsPerExecute()) + { + PX_CHECK_AND_RETURN(mNbSweeps < mDesc.queryMemory.getMaxSweepsPerExecute(), + "PxBatchQuery: number of sweep() calls exceeds PxBatchQueryMemory::sweepResultBufferSize, query discarded"); + return; + } + + + CHECK_RUNNING("PxBatchQuery::sweep: This batch is still executing, skipping query.") + mNbSweeps++; + + writeBatchHeader(BatchStreamHeader(hitFlags, cache, fd, userData, maxTouchHits, QTypeROS::eSWEEP)); + + //set the MTD flag + mHasMtdSweep |= !!(hitFlags & PxHitFlag::eMTD); + + if((hitFlags & PxHitFlag::ePRECISE_SWEEP) && (hitFlags & PxHitFlag::eMTD)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support MTD. Perform MTD with default sweep"); + hitFlags &= ~PxHitFlag::ePRECISE_SWEEP; + } + + if((hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP) && (hitFlags & PxHitFlag::eMTD)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " eMTD cannot be used in conjunction with eASSUME_NO_INITIAL_OVERLAP. eASSUME_NO_INITIAL_OVERLAP will be ignored"); + hitFlags &= ~PxHitFlag::eASSUME_NO_INITIAL_OVERLAP; + } + + PxReal realInflation = inflation; + if((hitFlags & PxHitFlag::ePRECISE_SWEEP)&& inflation > 0.0f) + { + realInflation = 0.0f; + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support inflation, inflation will be overwritten to be zero"); + } + + writeQueryInput(mStream, MultiQueryInput(&geometry, &pose, unitDir, distance, realInflation)); + + Ps::atomicExchange(&mBatchQueryIsRunning, 0); +} + +void NpBatchQuery::release() +{ + if(Ps::atomicCompareExchange(&mBatchQueryIsRunning, 0, 0) != 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxBatchQuery::release: This batch is still executing, skipping release"); + return; + } + + mNpScene->releaseBatchQuery(this); +} + +PxBatchQueryPreFilterShader NpBatchQuery::getPreFilterShader() const +{ + return mDesc.preFilterShader; +} + +PxBatchQueryPostFilterShader NpBatchQuery::getPostFilterShader() const +{ + return mDesc.postFilterShader; +} + +const void* NpBatchQuery::getFilterShaderData() const +{ + return mDesc.filterShaderData; +} + +PxU32 NpBatchQuery::getFilterShaderDataSize() const +{ + return mDesc.filterShaderDataSize; +} + + diff --git a/src/PhysX/physx/source/physx/src/NpBatchQuery.h b/src/PhysX/physx/source/physx/src/NpBatchQuery.h new file mode 100644 index 000000000..4bc3c6f9c --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpBatchQuery.h @@ -0,0 +1,166 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_SCENEQUERY +#define PX_PHYSICS_NP_SCENEQUERY +/** \addtogroup physics +@{ */ + +#include "PxBatchQuery.h" +#include "PsArray.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsSync.h" + +namespace physx +{ + +class NpSceneQueryManager; +struct BatchStreamHeader; +class NpScene; + +namespace Sq +{ +class SceneQueryManager; +} + +struct BatchQueryStream : Ps::Array +{ + BatchQueryStream() { rewind(); } + + void rewind() { mPosition = 0; } + + PX_FORCE_INLINE PxI32 getPos() { return PxI32(mPosition); } // signed to avoid casts elsewhere + + // write an object of type T to the stream, copying by value + template + PX_FORCE_INLINE void write(const T* val, PxU32 count = 1) + { + PX_COMPILE_TIME_ASSERT(sizeof(T) > 0); + PxU32 newPosition = mPosition + sizeof(T)*count; + if (newPosition > capacity()) + reserve(newPosition+(newPosition<<1)); + resizeUninitialized(newPosition); + T* dest = reinterpret_cast(begin() + mPosition); + for (PxU32 i = 0; i < count; i++) + { + *dest = *(val+i); + dest++; + } + mPosition = newPosition; + } + + template + PX_FORCE_INLINE void write(const T& val) + { + write(&val, 1); + } + + PX_FORCE_INLINE bool atEnd() const { return mPosition >= size(); } + +protected: + mutable PxU32 mPosition; +}; + +struct BatchQueryStreamReader +{ + BatchQueryStreamReader(char* buffer) : mBuffer(buffer), mReadPos(0) {} + + // read an object of type T from the stream (simply returns a pointer without copying) + template + PX_FORCE_INLINE T* read(PxU32 count = 1) + { + //PX_ASSERT(mPosition+sizeof(T)*count <= size()); + T* result = reinterpret_cast(mBuffer+mReadPos); + mReadPos += sizeof(T)*count; + return result; + } + + char* mBuffer; + PxU32 mReadPos; +}; + +class NpBatchQuery : public PxBatchQuery, public Ps::UserAllocated +{ +public: + NpBatchQuery(NpScene& owner, const PxBatchQueryDesc& d); + virtual ~NpBatchQuery(); + + // PxBatchQuery interface + virtual void execute(); + virtual void release(); + virtual PxBatchQueryPreFilterShader getPreFilterShader() const; + virtual PxBatchQueryPostFilterShader getPostFilterShader() const; + virtual const void* getFilterShaderData() const; + virtual PxU32 getFilterShaderDataSize() const; + virtual void setUserMemory(const PxBatchQueryMemory& ); + virtual const PxBatchQueryMemory& getUserMemory(); + + virtual void raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal distance, PxU16 maxTouchHits, + PxHitFlags hitFlags, const PxQueryFilterData& filterData, + void* userData, const PxQueryCache* cache); + + virtual void overlap(const PxGeometry& geometry, const PxTransform& pose, PxU16 maxTouchHits, + const PxQueryFilterData& filterData, void* userData, + const PxQueryCache* cache); + + virtual void sweep(const PxGeometry& geometry, const PxTransform& pose, + const PxVec3& unitDir, const PxReal distance, PxU16 maxTouchHits, + PxHitFlags hitFlags, const PxQueryFilterData& filterData, + void* userData, const PxQueryCache* cache, const PxReal inflation); + + PxBatchQueryDesc& getDesc() { return mDesc; } + virtual const PxBatchQueryDesc& getDesc() const { return mDesc; } + + enum { eTERMINAL = PxU32(-16) }; // -16 so it's aligned to avoid SPU checks + + // sync object for batch query completion wait + shdfnd::Sync mSync; +private: + void resetResultBuffers(); + void finalizeExecute(); + void writeBatchHeader(const BatchStreamHeader& h); + + NpScene* mNpScene; + BatchQueryStream mStream; + PxU32 mNbRaycasts, mNbOverlaps, mNbSweeps; + volatile PxI32 mBatchQueryIsRunning; + PxBatchQueryDesc mDesc; + // offset in mStream of the offset to the next query for the last header written by BQ query functions + PxU32 mPrevOffset; + bool mHasMtdSweep; + + friend class physx::Sq::SceneQueryManager; +}; + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/physx/src/NpCast.h b/src/PhysX/physx/source/physx/src/NpCast.h new file mode 100644 index 000000000..4ae9476ec --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpCast.h @@ -0,0 +1,103 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NP_CAST +#define PX_PHYSICS_NP_CAST + +#include "PxPhysXConfig.h" +#include "NpScene.h" +#include "NpRigidDynamic.h" +#include "NpRigidStatic.h" +#include "NpArticulation.h" +#include "NpArticulationReducedCoordinate.h" +#include "NpArticulationLink.h" +#include "NpArticulationJoint.h" +#include "NpAggregate.h" + +namespace physx +{ + // PT: Scb-to-Np casts + + PX_FORCE_INLINE const NpScene* getNpScene(const Scb::Scene* scene) + { + const size_t scbOffset = reinterpret_cast(&(reinterpret_cast(0)->getScene())); + return reinterpret_cast(reinterpret_cast(scene) - scbOffset); + } + + PX_FORCE_INLINE const NpRigidDynamic* getNpRigidDynamic(const Scb::Body* scbBody) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbActorFast())); + return reinterpret_cast(reinterpret_cast(scbBody) - offset); + } + + PX_FORCE_INLINE const NpRigidStatic* getNpRigidStatic(const Scb::RigidStatic* scbRigidStatic) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbActorFast())); + return reinterpret_cast(reinterpret_cast(scbRigidStatic) - offset); + } + + PX_FORCE_INLINE const NpShape* getNpShape(const Scb::Shape* scbShape) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbShape())); + return reinterpret_cast(reinterpret_cast(scbShape) - offset); + } + + PX_FORCE_INLINE const NpArticulationLink* getNpArticulationLink(const Scb::Body* scbArticulationLink) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbActorFast())); + return reinterpret_cast(reinterpret_cast(scbArticulationLink) - offset); + } + + PX_FORCE_INLINE const NpArticulation* getNpArticulation(const Scb::Articulation* scbArticulation) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->mImpl.getArticulation())); + return reinterpret_cast(reinterpret_cast(scbArticulation) - offset); + } + + PX_FORCE_INLINE const NpArticulationReducedCoordinate* getNpArticulationRC(const Scb::Articulation* scbArticulation) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->mImpl.getArticulation())); + return reinterpret_cast(reinterpret_cast(scbArticulation) - offset); + } + + PX_FORCE_INLINE const NpArticulationJoint* getNpArticulationJoint(const Scb::ArticulationJoint* scbArticulationJoint) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbArticulationJoint())); + return reinterpret_cast(reinterpret_cast(scbArticulationJoint) - offset); + } + + PX_FORCE_INLINE const NpAggregate* getNpAggregate(const Scb::Aggregate* aggregate) + { + const size_t offset = reinterpret_cast(&(reinterpret_cast(0)->getScbAggregate())); + return reinterpret_cast(reinterpret_cast(aggregate) - offset); + } + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpConnector.h b/src/PhysX/physx/source/physx/src/NpConnector.h new file mode 100644 index 000000000..d404153d8 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpConnector.h @@ -0,0 +1,136 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_CONNECTOR +#define PX_PHYSICS_NP_CONNECTOR + +#include "CmPhysXCommon.h" +#include "PsInlineArray.h" +#include "PxSerialFramework.h" +#include "CmUtils.h" +#include "PsUtilities.h" + +namespace physx +{ + +struct NpConnectorType +{ + enum Enum + { + eConstraint, + eAggregate, + eObserver, + eBvhStructure, + eInvalid + }; +}; + + +class NpConnector +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + NpConnector() : mType(NpConnectorType::eInvalid), mObject(NULL) {} + NpConnector(NpConnectorType::Enum type, PxBase* object) : mType(Ps::to8(type)), mObject(object) {} +// PX_SERIALIZATION + NpConnector(const NpConnector& c) + { + //special copy constructor that initializes padding bytes for meta data verification (PX_CHECKED only) + Cm::markSerializedMem(this, sizeof(NpConnector)); + mType = c.mType; + mObject = c.mObject; + } + + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PxU8 mType; // Revisit whether the type is really necessary or whether the serializable type is enough. + // Since joints might gonna inherit from observers to register for constraint release events, the type + // is necessary because a joint has its own serializable type and could not be detected as observer anymore. + PxU8 mPadding[3]; // PT: padding from prev byte + PxBase* mObject; // So far the serialization framework only supports ptr resolve for PxBase objects. + // However, so far the observers all are PxBase, hence this choice of type. +}; + + +class NpConnectorIterator +{ +public: + PX_FORCE_INLINE NpConnectorIterator(NpConnector* c, PxU32 size, NpConnectorType::Enum type) : mConnectors(c), mSize(size), mIndex(0), mType(type) {} + + PX_FORCE_INLINE PxBase* getNext() + { + PxBase* s = NULL; + while(mIndex < mSize) + { + NpConnector& c = mConnectors[mIndex]; + mIndex++; + if (c.mType == mType) + return c.mObject; + } + return s; + } + +private: + NpConnector* mConnectors; + PxU32 mSize; + PxU32 mIndex; + NpConnectorType::Enum mType; +}; + + +class NpConnectorArray: public Ps::InlineArray +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpConnectorArray(const PxEMPTY) : Ps::InlineArray (PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + NpConnectorArray() : Ps::InlineArray(PX_DEBUG_EXP("connectorArray")) + { + //special default constructor that initializes padding bytes for meta data verification (PX_CHECKED only) + Cm::markSerializedMem(this->mData, 4*sizeof(NpConnector)); + } +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpConstraint.cpp b/src/PhysX/physx/source/physx/src/NpConstraint.cpp new file mode 100644 index 000000000..7e7d3a1e8 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpConstraint.cpp @@ -0,0 +1,407 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxConstraint.h" +#include "NpConstraint.h" +#include "NpPhysics.h" +#include "NpRigidDynamic.h" +#include "NpRigidStatic.h" +#include "NpArticulationLink.h" +#include "ScbConstraint.h" +#include "ScbNpDeps.h" + +using namespace physx; + +void NpConstraint::setConstraintFunctions(PxConstraintConnector& n, const PxConstraintShaderTable& shaders) +{ + mConstraint.getScConstraint().setConstraintFunctions(n, shaders); + + //update mConnectorArray, since mActor0 or mActor1 should be in external reference + bool bNeedUpdate = false; + if(mActor0) + { + NpActor& npActor = NpActor::getFromPxActor(*mActor0); + if(npActor.findConnector(NpConnectorType::eConstraint, this) == 0xffffffff) + { + bNeedUpdate = true; + npActor.addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); + } + } + + if(mActor1) + { + NpActor& npActor = NpActor::getFromPxActor(*mActor1); + if(npActor.findConnector(NpConnectorType::eConstraint, this) == 0xffffffff) + { + bNeedUpdate = true; + npActor.addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); + } + } + + if(bNeedUpdate) + { + NpScene* newScene = getSceneFromActors(mActor0, mActor1); + NpScene* oldScene = getNpScene(); + + if (oldScene != newScene) + { + if (oldScene) + { + oldScene->removeFromConstraintList(*this); + oldScene->getScene().removeConstraint(getScbConstraint()); + } + if (newScene) + { + newScene->addToConstraintList(*this); + newScene->getScene().addConstraint(getScbConstraint()); + } + } + } +} + +NpConstraint::NpConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) +: PxConstraint(PxConcreteType::eCONSTRAINT, PxBaseFlag::eOWNS_MEMORY) +, mActor0 (actor0) +, mActor1 (actor1) +, mConstraint (connector, shaders, dataSize) +, mIsDirty (true) +{ + + mConstraint.setFlags(shaders.flag); + if(actor0) + NpActor::getFromPxActor(*actor0).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); + if(actor1) + NpActor::getFromPxActor(*actor1).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); + + NpScene* s = getSceneFromActors(actor0, actor1); + if (s) + { + s->addToConstraintList(*this); + s->getScene().addConstraint(mConstraint); + } +} + + +NpConstraint::~NpConstraint() +{ + if(getBaseFlags()&PxBaseFlag::eOWNS_MEMORY) + mConstraint.getPxConnector()->onConstraintRelease(); + + NpFactory::getInstance().onConstraintRelease(this); +} + +void NpConstraint::release() +{ + NpScene* npScene = getNpScene(); + NP_WRITE_CHECK(npScene); + + NpPhysics::getInstance().notifyDeletionListenersUserRelease(this, NULL); + + if(mActor0) + NpActor::getFromPxActor(*mActor0).removeConnector(*mActor0, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); + if(mActor1) + NpActor::getFromPxActor(*mActor1).removeConnector(*mActor1, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); + + if (npScene) + { + npScene->removeFromConstraintList(*this); + npScene->getScene().removeConstraint(getScbConstraint()); + } + + mConstraint.destroy(); +} + +// PX_SERIALIZATION +void NpConstraint::resolveReferences(PxDeserializationContext& context) +{ + context.translatePxBase(mActor0); + context.translatePxBase(mActor1); +} + +NpConstraint* NpConstraint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpConstraint* obj = new (address) NpConstraint(PxBaseFlags(0)); + address += sizeof(NpConstraint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// ~PX_SERIALIZATION + +PxScene* NpConstraint::getScene() const +{ + return getNpScene(); +} + +void NpConstraint::getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const +{ + NP_READ_CHECK(getNpScene()); + actor0 = mActor0; + actor1 = mActor1; +} + +void NpConstraint::setActors(PxRigidActor* actor0, PxRigidActor* actor1) +{ + NP_WRITE_CHECK(getNpScene()); + + PX_CHECK_AND_RETURN((actor0 && !actor0->is()) || (actor1 && !actor1->is()), "PxConstraint: at least one actor must be non-static"); + PX_SIMD_GUARD; + + if(mActor0) + NpActor::getFromPxActor(*mActor0).removeConnector(*mActor0, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); + if(mActor1) + NpActor::getFromPxActor(*mActor1).removeConnector(*mActor1, NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); + + if(actor0) + NpActor::getFromPxActor(*actor0).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 0: Constraint already added"); + if(actor1) + NpActor::getFromPxActor(*actor1).addConnector(NpConnectorType::eConstraint, this, "PxConstraint: Add to rigid actor 1: Constraint already added"); + + mActor0 = actor0; + mActor1 = actor1; + + NpScene* newScene = getSceneFromActors(actor0, actor1); + NpScene* oldScene = getNpScene(); + + if (oldScene != newScene) + { + if (oldScene) + { + oldScene->removeFromConstraintList(*this); + oldScene->getScene().removeConstraint(getScbConstraint()); + } + + getScbConstraint().setBodies(getScbRigidObject(actor0), getScbRigidObject(actor1)); + + if (newScene) + { + newScene->addToConstraintList(*this); + newScene->getScene().addConstraint(getScbConstraint()); + } + } + else + getScbConstraint().setBodies(getScbRigidObject(actor0), getScbRigidObject(actor1)); +} + +void NpConstraint::markDirty() +{ + mIsDirty = true; +} + +void NpConstraint::setFlags(PxConstraintFlags flags) +{ + NP_WRITE_CHECK(getNpScene()); + // check for eBROKEN which is a read only flag + PX_CHECK_AND_RETURN(!(flags & PxConstraintFlag::eBROKEN), "PxConstraintFlag::eBROKEN is a read only flag"); + + PX_CHECK_AND_RETURN(!(flags & PxConstraintFlag::eGPU_COMPATIBLE), "PxConstraintFlag::eGPU_COMPATIBLE is an internal flag and is illegal to set via the API"); + + PX_SIMD_GUARD; + + mConstraint.setFlags(flags); +} + +PxConstraintFlags NpConstraint::getFlags() const +{ + NP_READ_CHECK(getNpScene()); + return mConstraint.getFlags(); +} + + +void NpConstraint::setFlag(PxConstraintFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(getNpScene()); + + // check for eBROKEN which is a read only flag + PX_CHECK_AND_RETURN(flag != PxConstraintFlag::eBROKEN, "PxConstraintFlag::eBROKEN is a read only flag"); + + PX_CHECK_AND_RETURN(flag != PxConstraintFlag::eGPU_COMPATIBLE, "PxConstraintFlag::eGPU_COMPATIBLE is an internal flag and is illegal to set via the API"); + + PX_SIMD_GUARD; + + PxConstraintFlags f = mConstraint.getFlags(); + + mConstraint.setFlags(value ? f|flag : f&~flag); +} + + +void NpConstraint::getForce(PxVec3& linear, PxVec3& angular) const +{ + NP_READ_CHECK(getNpScene()); + mConstraint.getForce(linear, angular); +} + +void NpConstraint::updateConstants() +{ + if(!mIsDirty) + return; + + //ML - we can't just set dirty to false because this fails with constraints that were created while the scene was buffering! + if(mConstraint.updateConstants(mConstraint.getPxConnector()->prepareData())) + mIsDirty = false; +#if PX_SUPPORT_PVD + Scb::Scene* scbScene = mConstraint.getScbSceneForAPI(); + //Changed to use the visual scenes update system which respects + //the debugger's connection type flag. + if( scbScene && !scbScene->isPhysicsBuffering() ) + scbScene->getScenePvdClient().updatePvdProperties( &mConstraint ); +#endif +} + +void NpConstraint::setBreakForce(PxReal linear, PxReal angular) +{ + NP_WRITE_CHECK(getNpScene()); + PX_SIMD_GUARD; + mConstraint.setBreakForce(linear, angular); +} + +void NpConstraint::getBreakForce(PxReal& linear, PxReal& angular) const +{ + NP_READ_CHECK(getNpScene()); + PX_SIMD_GUARD; + mConstraint.getBreakForce(linear, angular); +} + + +void NpConstraint::setMinResponseThreshold(PxReal threshold) +{ + PX_CHECK_AND_RETURN(PxIsFinite(threshold) && threshold>=0, "PxConstraint::setMinResponseThreshold: threshold must be non-negative"); + NP_WRITE_CHECK(getNpScene()); + PX_SIMD_GUARD; + mConstraint.setMinResponseThreshold(threshold); +} + +PxReal NpConstraint::getMinResponseThreshold() const +{ + NP_READ_CHECK(getNpScene()); + PX_SIMD_GUARD; + return mConstraint.getMinResponseThreshold(); +} + +bool NpConstraint::isValid() const +{ + NP_READ_CHECK(getNpScene()); + bool isValid0 = mActor0 && !mActor0->is(); + bool isValid1 = mActor1 && !mActor1->is(); + return isValid0 || isValid1; +} + +void* NpConstraint::getExternalReference(PxU32& typeID) +{ + NP_READ_CHECK(getNpScene()); + PxConstraintConnector* connector = mConstraint.getPxConnector(); + return connector->getExternalReference(typeID); +} + +void NpConstraint::comShift(PxRigidActor* actor) +{ + PX_ASSERT(actor == mActor0 || actor == mActor1); + PxConstraintConnector* connector = mConstraint.getPxConnector(); + if(actor == mActor0) + connector->onComShift(0); + if(actor == mActor1) + connector->onComShift(1); +} + +void NpConstraint::actorDeleted(PxRigidActor* actor) +{ + // the actor cannot be deleted without also removing it from the scene, + // which means that the joint will also have been removed from the scene, + // so we can just reset the actor here. + PX_ASSERT(actor == mActor0 || actor == mActor1); + + if(actor == mActor0) + mActor0 = 0; + else + mActor1 = 0; +} + + + +NpScene* NpConstraint::getNpScene() const +{ + return mConstraint.getScbSceneForAPI() ? static_cast(mConstraint.getScbSceneForAPI()->getPxScene()) : NULL; +} + +Scb::RigidObject* NpConstraint::getScbRigidObject(PxRigidActor* a) +{ + if(!a) + return NULL; + + PxType type = a->getConcreteType(); + + if (type == PxConcreteType::eRIGID_DYNAMIC) + return &(static_cast(a)->getScbBodyFast()); + else if (type == PxConcreteType::eARTICULATION_LINK) + return &(static_cast(a)->getScbBodyFast()); + else + { + PX_ASSERT(type == PxConcreteType::eRIGID_STATIC); + return &(static_cast(a)->getScbRigidStaticFast()); + } +} + +void physx::NpConstraintGetRigidObjectsFromScb(const Scb::Constraint&c, Scb::RigidObject*&b0, Scb::RigidObject*&b1) +{ + const size_t offset = size_t(&(reinterpret_cast(0)->getScbConstraint())); + const NpConstraint* np = reinterpret_cast(reinterpret_cast(&c)-offset); + + PxRigidActor* a0, * a1; + np->getActors(a0, a1); + b0 = NpConstraint::getScbRigidObject(a0); + b1 = NpConstraint::getScbRigidObject(a1); +} + +NpScene* NpConstraint::getSceneFromActors() const +{ + return getSceneFromActors(mActor0, mActor1); +} + +PX_FORCE_INLINE NpScene* NpConstraint::getSceneFromActors(const PxRigidActor* actor0, const PxRigidActor* actor1) +{ + NpScene* s0 = NULL; + NpScene* s1 = NULL; + + if (actor0 && (!(actor0->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) + s0 = static_cast(actor0->getScene()); + if (actor1 && (!(actor1->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) + s1 = static_cast(actor1->getScene()); + +#if PX_CHECKED + if ((s0 && s1) && (s0 != s1)) + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Adding constraint to scene: Actors belong to different scenes, undefined behavior expected!"); +#endif + + if ((!actor0 || s0) && (!actor1 || s1)) + return s0 ? s0 : s1; + else + return NULL; +} diff --git a/src/PhysX/physx/source/physx/src/NpConstraint.h b/src/PhysX/physx/source/physx/src/NpConstraint.h new file mode 100644 index 000000000..4738b4cb4 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpConstraint.h @@ -0,0 +1,131 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_CONSTRAINT +#define PX_PHYSICS_NP_CONSTRAINT + +#include "PsUserAllocated.h" +#include "PxConstraint.h" +#include "ScbConstraint.h" + +namespace physx +{ + +class NpScene; +class NpRigidDynamic; + +namespace Scb +{ + class RigidObject; +} + +class NpConstraint : public PxConstraint, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpConstraint(PxBaseFlags baseFlags) : PxConstraint(baseFlags), mConstraint(PxEmpty) {} + virtual void setConstraintFunctions(PxConstraintConnector& n, + const PxConstraintShaderTable &t); + static NpConstraint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void exportExtraData(PxSerializationContext&) {} + void importExtraData(PxDeserializationContext&) { } + void resolveReferences(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback&){} + virtual bool isSubordinate() const { return true; } +//~PX_SERIALIZATION + NpConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize); + ~NpConstraint(); + + virtual void release(); + + virtual PxScene* getScene() const; + + virtual void getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const; + virtual void setActors(PxRigidActor* actor0, PxRigidActor* actor1); + + virtual PxConstraintFlags getFlags() const; + virtual void setFlags(PxConstraintFlags flags); + virtual void setFlag(PxConstraintFlag::Enum flag, bool value); + + virtual void getForce(PxVec3& linear, PxVec3& angular) const; + + virtual void markDirty(); + + virtual void setBreakForce(PxReal linear, PxReal angular); + virtual void getBreakForce(PxReal& linear, PxReal& angular) const; + + virtual void setMinResponseThreshold(PxReal threshold); + virtual PxReal getMinResponseThreshold() const; + + virtual bool isValid() const; + + virtual void* getExternalReference(PxU32& typeID); + + void initialize(const PxConstraintDesc&, Scb::Constraint*); + void updateConstants(); + void comShift(PxRigidActor*); + void actorDeleted(PxRigidActor*); + + + + NpScene* getNpScene() const; + + NpScene* getSceneFromActors() const; + PX_FORCE_INLINE Scb::Constraint& getScbConstraint() { return mConstraint; } + PX_FORCE_INLINE const Scb::Constraint& getScbConstraint() const { return mConstraint; } + PX_FORCE_INLINE bool isDirty() const { return mIsDirty; } + + + static Scb::RigidObject* getScbRigidObject(PxRigidActor*); +private: + PX_FORCE_INLINE static NpScene* getSceneFromActors(const PxRigidActor* actor0, const PxRigidActor* actor1); // Returns the scene if both actors are in the scene, else NULL + + PxRigidActor* mActor0; + PxRigidActor* mActor1; + Scb::Constraint mConstraint; + + // this used to be stored in Scb, but that doesn't really work since Scb::Constraint's + // flags all get cleared on fetchResults. In any case, in order to support O(1) + // insert/remove this really wants to be an index into NpScene's dirty joint array + + bool mIsDirty; + bool mPaddingFromBool[3]; // PT: because of mIsDirty +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpFactory.cpp b/src/PhysX/physx/source/physx/src/NpFactory.cpp new file mode 100644 index 000000000..416bcd125 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpFactory.cpp @@ -0,0 +1,850 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpCast.h" +#include "NpFactory.h" +#include "NpPhysics.h" +#include "ScPhysics.h" +#include "ScbScene.h" +#include "ScbActor.h" +#include "GuHeightField.h" +#include "GuTriangleMesh.h" +#include "GuConvexMesh.h" + +#include "NpConnector.h" +#include "NpPtrTableStorageManager.h" +#include "CmCollection.h" +#include "NpArticulationReducedCoordinate.h" +#include "NpArticulationJointReducedCoordinate.h" + +using namespace physx; + +NpFactory::NpFactory() +: GuMeshFactory() +, mConnectorArrayPool(PX_DEBUG_EXP("connectorArrayPool")) +, mPtrTableStorageManager(PX_NEW(NpPtrTableStorageManager)) +, mMaterialPool(PX_DEBUG_EXP("MaterialPool")) +#if PX_SUPPORT_PVD + , mNpFactoryListener(NULL) +#endif +{ +} + +namespace +{ + template void releaseAll(Ps::HashSet& container) + { + // a bit tricky: release will call the factory back to remove the object from + // the tracking array, immediately invalidating the iterator. Reconstructing the + // iterator per delete can be expensive. So, we use a temporary object. + // + // a coalesced hash would be efficient too, but we only ever iterate over it + // here so it's not worth the 2x remove penalty over the normal hash. + + Ps::Array > tmp; + tmp.reserve(container.size()); + for(typename Ps::HashSet::Iterator iter = container.getIterator(); !iter.done(); ++iter) + tmp.pushBack(*iter); + + PX_ASSERT(tmp.size() == container.size()); + for(PxU32 i=0;irelease(); + } +} + +NpFactory::~NpFactory() +{ + PX_DELETE(mPtrTableStorageManager); +} + +void NpFactory::release() +{ + releaseAll(mAggregateTracking); + releaseAll(mConstraintTracking); + releaseAll(mArticulationTracking); + releaseAll(mActorTracking); + while(mShapeTracking.size()) + static_cast(mShapeTracking.getEntries()[0])->releaseInternal(); + + GuMeshFactory::release(); // deletes the class +} + +void NpFactory::createInstance() +{ + PX_ASSERT(!mInstance); + mInstance = PX_NEW(NpFactory)(); +} + +void NpFactory::destroyInstance() +{ + PX_ASSERT(mInstance); + mInstance->release(); + mInstance = NULL; +} + +NpFactory* NpFactory::mInstance = NULL; + +/////////////////////////////////////////////////////////////////////////////// + +template +static void addToTracking(T1& set, T0* element, Ps::Mutex& mutex, bool lock) +{ + if(!element) + return; + + if(lock) + mutex.lock(); + + set.insert(element); + + if(lock) + mutex.unlock(); +} + +/////////////////////////////////////////////////////////////////////////////// Actors + +void NpFactory::addRigidStatic(PxRigidStatic* npActor, bool lock) +{ + addToTracking(mActorTracking, npActor, mTrackingMutex, lock); +} + +void NpFactory::addRigidDynamic(PxRigidDynamic* npBody, bool lock) +{ + addToTracking(mActorTracking, npBody, mTrackingMutex, lock); +} + +void NpFactory::addShape(PxShape* shape, bool lock) +{ + addToTracking(mShapeTracking, shape, mTrackingMutex, lock); +} + +void NpFactory::onActorRelease(PxActor* a) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mActorTracking.erase(a); +} + +void NpFactory::onShapeRelease(PxShape* a) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mShapeTracking.erase(a); +} + +void NpFactory::addArticulation(PxArticulationBase* npArticulation, bool lock) +{ + addToTracking(mArticulationTracking, npArticulation, mTrackingMutex, lock); +} + +namespace +{ + PxArticulationBase* createArticulation() + { + NpArticulation* npArticulation = NpFactory::getInstance().createNpArticulation(); + if (!npArticulation) + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Articulation initialization failed: returned NULL."); + + return npArticulation; + } + + PxArticulationBase* createArticulationRC() + { + NpArticulationReducedCoordinate* npArticulation = NpFactory::getInstance().createNpArticulationRC(); + if (!npArticulation) + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Articulation initialization failed: returned NULL."); + + return npArticulation; + } + + NpArticulationLink* createArticulationLink(PxArticulationBase&root, NpArticulationLink* parent, const PxTransform& pose) + { + PX_CHECK_AND_RETURN_NULL(pose.isValid(),"Supplied PxArticulation pose is not valid. Articulation link creation method returns NULL."); + PX_CHECK_AND_RETURN_NULL((!parent || (&parent->getRoot() == &root)), "specified parent link is not part of the destination articulation. Articulation link creation method returns NULL."); + + NpArticulationLink* npArticulationLink = NpFactory::getInstance().createNpArticulationLink(root, parent, pose); + if (!npArticulationLink) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Articulation link initialization failed: returned NULL."); + return NULL; + } + + if (parent) + { + PxTransform parentPose = parent->getCMassLocalPose().transformInv(pose); + PxTransform childPose = PxTransform(PxIdentity); + + PxArticulationJointBase* npArticulationJoint = root.createArticulationJoint(*parent, parentPose, *npArticulationLink, childPose); + if (!npArticulationJoint) + { + PX_DELETE(npArticulationLink); + + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Articulation link initialization failed due to joint creation failure: returned NULL."); + return NULL; + } + + npArticulationLink->setInboundJoint(*npArticulationJoint); + } + + return npArticulationLink; + } + + // pointers to functions above, initialized during subsystem registration + static PxArticulationBase* (*sCreateArticulationFn)() = 0; + static PxArticulationBase* (*sCreateArticulationRCFn)() = 0; + static NpArticulationLink* (*sCreateArticulationLinkFn)(PxArticulationBase&, NpArticulationLink* parent, const PxTransform& pose) = 0; +} + +void NpFactory::registerArticulations() +{ + sCreateArticulationFn = &::createArticulation; + sCreateArticulationLinkFn = &::createArticulationLink; +} + +void NpFactory::registerArticulationRCs() +{ + sCreateArticulationRCFn = &::createArticulationRC; + sCreateArticulationLinkFn = &::createArticulationLink; +} + +void NpFactory::releaseArticulationToPool(PxArticulationBase& articulation) +{ + + PX_ASSERT(articulation.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + if (articulation.getType() == PxArticulationBase::eMaximumCoordinate) + { + Ps::Mutex::ScopedLock lock(mArticulationPoolLock); + mArticulationPool.destroy(static_cast(&articulation)); + } + else + { + Ps::Mutex::ScopedLock lock(mArticulationRCPoolLock); + mArticulationRCPool.destroy(static_cast(&articulation)); + } +} + +NpArticulation* NpFactory::createNpArticulation() +{ + Ps::Mutex::ScopedLock lock(mArticulationPoolLock); + return mArticulationPool.construct(); +} + +PxArticulation* NpFactory::createArticulation() +{ + if(!sCreateArticulationFn) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Articulations not registered: returned NULL."); + return NULL; + } + + PxArticulationBase* npArticulation = (*sCreateArticulationFn)(); + if(npArticulation) + addArticulation(npArticulation); + + return static_cast(npArticulation); +} + +PxArticulationReducedCoordinate* NpFactory::createArticulationRC() +{ + if (!sCreateArticulationRCFn) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Articulations not registered: returned NULL."); + return NULL; + } + + PxArticulationBase* npArticulation = (*sCreateArticulationRCFn)(); + if (npArticulation) + addArticulation(npArticulation); + + return static_cast(npArticulation); +} + +NpArticulationReducedCoordinate* NpFactory::createNpArticulationRC() +{ + Ps::Mutex::ScopedLock lock(mArticulationRCPoolLock); + return mArticulationRCPool.construct(); +} + +void NpFactory::onArticulationRelease(PxArticulationBase* a) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mArticulationTracking.erase(a); +} + +NpArticulationLink* NpFactory::createNpArticulationLink(PxArticulationBase&root, NpArticulationLink* parent, const PxTransform& pose) +{ + NpArticulationLink* npArticulationLink; + { + Ps::Mutex::ScopedLock lock(mArticulationLinkPoolLock); + npArticulationLink = mArticulationLinkPool.construct(pose, root, parent); + } + return npArticulationLink; +} + +void NpFactory::releaseArticulationLinkToPool(NpArticulationLink& articulationLink) +{ + PX_ASSERT(articulationLink.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mArticulationLinkPoolLock); + mArticulationLinkPool.destroy(&articulationLink); +} + +PxArticulationLink* NpFactory::createArticulationLink(PxArticulationBase& root, NpArticulationLink* parent, const PxTransform& pose) +{ + if(!sCreateArticulationLinkFn) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Articulations not registered: returned NULL."); + return NULL; + } + + return (*sCreateArticulationLinkFn)(root, parent, pose); +} + +NpArticulationJoint* NpFactory::createNpArticulationJoint(NpArticulationLink& parent, const PxTransform& parentFrame, NpArticulationLink& child, const PxTransform& childFrame) +{ + NpArticulationJoint* npArticulationJoint; + { + Ps::Mutex::ScopedLock lock(mArticulationJointPoolLock); + npArticulationJoint = mArticulationJointPool.construct(parent, parentFrame, child, childFrame); + } + return npArticulationJoint; +} + +void NpFactory::releaseArticulationJointToPool(NpArticulationJoint& articulationJoint) +{ + PX_ASSERT(articulationJoint.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mArticulationJointPoolLock); + mArticulationJointPool.destroy(&articulationJoint); +} + +NpArticulationJointReducedCoordinate* NpFactory::createNpArticulationJointRC(NpArticulationLink& parent, const PxTransform& parentFrame, NpArticulationLink& child, const PxTransform& childFrame) +{ + NpArticulationJointReducedCoordinate* npArticulationJoint; + { + Ps::Mutex::ScopedLock lock(mArticulationJointRCPoolLock); + npArticulationJoint = mArticulationRCJointPool.construct(parent, parentFrame, child, childFrame); + } + return npArticulationJoint; +} + +void NpFactory::releaseArticulationJointRCToPool(NpArticulationJointReducedCoordinate& articulationJoint) +{ + PX_ASSERT(articulationJoint.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mArticulationJointRCPoolLock); + mArticulationRCJointPool.destroy(&articulationJoint); +} + +/////////////////////////////////////////////////////////////////////////////// constraint + +void NpFactory::addConstraint(PxConstraint* npConstraint, bool lock) +{ + addToTracking(mConstraintTracking, npConstraint, mTrackingMutex, lock); +} + +PxConstraint* NpFactory::createConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) +{ + PX_CHECK_AND_RETURN_NULL((actor0 && !actor0->is()) || (actor1 && !actor1->is()), "createConstraint: At least one actor must be dynamic or an articulation link"); + + NpConstraint* npConstraint; + { + Ps::Mutex::ScopedLock lock(mConstraintPoolLock); + npConstraint = mConstraintPool.construct(actor0, actor1, connector, shaders, dataSize); + } + addConstraint(npConstraint); + return npConstraint; +} + +void NpFactory::releaseConstraintToPool(NpConstraint& constraint) +{ + PX_ASSERT(constraint.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mConstraintPoolLock); + mConstraintPool.destroy(&constraint); +} + +void NpFactory::onConstraintRelease(PxConstraint* c) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mConstraintTracking.erase(c); +} + +/////////////////////////////////////////////////////////////////////////////// aggregate + +void NpFactory::addAggregate(PxAggregate* npAggregate, bool lock) +{ + addToTracking(mAggregateTracking, npAggregate, mTrackingMutex, lock); +} + +PxAggregate* NpFactory::createAggregate(PxU32 maxActors, bool selfCollisions) +{ + NpAggregate* npAggregate; + { + Ps::Mutex::ScopedLock lock(mAggregatePoolLock); + npAggregate = mAggregatePool.construct(maxActors, selfCollisions); + } + + addAggregate(npAggregate); + return npAggregate; +} + +void NpFactory::releaseAggregateToPool(NpAggregate& aggregate) +{ + PX_ASSERT(aggregate.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mAggregatePoolLock); + mAggregatePool.destroy(&aggregate); +} + +void NpFactory::onAggregateRelease(PxAggregate* a) +{ + Ps::Mutex::ScopedLock lock(mTrackingMutex); + mAggregateTracking.erase(a); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxMaterial* NpFactory::createMaterial(PxReal staticFriction, PxReal dynamicFriction, PxReal restitution) +{ + PX_CHECK_AND_RETURN_NULL(dynamicFriction >= 0.0f, "createMaterial: dynamicFriction must be >= 0."); + PX_CHECK_AND_RETURN_NULL(staticFriction >= 0.0f, "createMaterial: staticFriction must be >= 0."); + PX_CHECK_AND_RETURN_NULL(restitution >= 0.0f || restitution <= 1.0f, "createMaterial: restitution must be between 0 and 1."); + + Sc::MaterialData data; + data.staticFriction = staticFriction; + data.dynamicFriction = dynamicFriction; + data.restitution = restitution; + + NpMaterial* npMaterial; + { + Ps::Mutex::ScopedLock lock(mMaterialPoolLock); + npMaterial = mMaterialPool.construct(data); + } + return npMaterial; +} + +void NpFactory::releaseMaterialToPool(NpMaterial& material) +{ + PX_ASSERT(material.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mMaterialPoolLock); + mMaterialPool.destroy(&material); +} + +/////////////////////////////////////////////////////////////////////////////// + +NpConnectorArray* NpFactory::acquireConnectorArray() +{ + Ps::MutexT<>::ScopedLock l(mConnectorArrayPoolLock); + return mConnectorArrayPool.construct(); +} + +void NpFactory::releaseConnectorArray(NpConnectorArray* array) +{ + Ps::MutexT<>::ScopedLock l(mConnectorArrayPoolLock); + mConnectorArrayPool.destroy(array); +} + +/////////////////////////////////////////////////////////////////////////////// + +NpShape* NpFactory::createShape(const PxGeometry& geometry, + PxShapeFlags shapeFlags, + PxMaterial*const* materials, + PxU16 materialCount, + bool isExclusive) +{ + switch(geometry.getType()) + { + case PxGeometryType::eBOX: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eSPHERE: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eCAPSULE: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eCONVEXMESH: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::ePLANE: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eHEIGHTFIELD: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eTRIANGLEMESH: + PX_CHECK_AND_RETURN_NULL(static_cast(geometry).isValid(), "Supplied PxGeometry is not valid. Shape creation method returns NULL."); + break; + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ASSERT(0); + } + + // + // Check for invalid material table setups + // + +#if PX_CHECKED + if (!NpShape::checkMaterialSetup(geometry, "Shape creation", materials, materialCount)) + return NULL; +#endif + + Ps::InlineArray materialIndices("NpFactory::TmpMaterialIndexBuffer"); + materialIndices.resize(materialCount); + if(materialCount == 1) + materialIndices[0] = Ps::to16((static_cast(materials[0]))->getHandle()); + else + NpMaterial::getMaterialIndices(materials, materialIndices.begin(), materialCount); + + NpShape* npShape; + { + Ps::Mutex::ScopedLock lock(mShapePoolLock); + PxU16* mi = materialIndices.begin(); // required to placate pool constructor arg passing + npShape = mShapePool.construct(geometry, shapeFlags, mi, materialCount, isExclusive); + } + + if(!npShape) + return NULL; + + for(PxU32 i=0; i < materialCount; i++) + static_cast(npShape->getMaterial(i))->incRefCount(); + + addShape(npShape); + + return npShape; +} + +void NpFactory::releaseShapeToPool(NpShape& shape) +{ + PX_ASSERT(shape.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mShapePoolLock); + mShapePool.destroy(&shape); +} + +PxU32 NpFactory::getNbShapes() const +{ + // PT: TODO: isn't there a lock missing here? See usage in MeshFactory + return mShapeTracking.size(); +} + +PxU32 NpFactory::getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + // PT: TODO: isn't there a lock missing here? See usage in MeshFactory + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mShapeTracking.getEntries(), mShapeTracking.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxRigidStatic* NpFactory::createRigidStatic(const PxTransform& pose) +{ + PX_CHECK_AND_RETURN_NULL(pose.isValid(), "pose is not valid. createRigidStatic returns NULL."); + + NpRigidStatic* npActor; + + { + Ps::Mutex::ScopedLock lock(mRigidStaticPoolLock); + npActor = mRigidStaticPool.construct(pose); + } + + addRigidStatic(npActor); + return npActor; +} + +void NpFactory::releaseRigidStaticToPool(NpRigidStatic& rigidStatic) +{ + PX_ASSERT(rigidStatic.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mRigidStaticPoolLock); + mRigidStaticPool.destroy(&rigidStatic); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxRigidDynamic* NpFactory::createRigidDynamic(const PxTransform& pose) +{ + PX_CHECK_AND_RETURN_NULL(pose.isValid(), "pose is not valid. createRigidDynamic returns NULL."); + + NpRigidDynamic* npBody; + { + Ps::Mutex::ScopedLock lock(mRigidDynamicPoolLock); + npBody = mRigidDynamicPool.construct(pose); + } + addRigidDynamic(npBody); + return npBody; +} + +void NpFactory::releaseRigidDynamicToPool(NpRigidDynamic& rigidDynamic) +{ + PX_ASSERT(rigidDynamic.getBaseFlags() & PxBaseFlag::eOWNS_MEMORY); + Ps::Mutex::ScopedLock lock(mRigidDynamicPoolLock); + mRigidDynamicPool.destroy(&rigidDynamic); +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: this function is here to minimize the amount of locks when deserializing a collection +void NpFactory::addCollection(const Cm::Collection& collection) +{ + + PxU32 nb = collection.getNbObjects(); + const Ps::Pair* entries = collection.internalGetObjects(); + // PT: we take the lock only once, here + Ps::Mutex::ScopedLock lock(mTrackingMutex); + + for(PxU32 i=0;igetConcreteType(); +////////////////////////// + if(serialType==PxConcreteType::eHEIGHTFIELD) + { + Gu::HeightField* gu = static_cast(s); + gu->setMeshFactory(this); + addHeightField(gu, false); + } + else if(serialType==PxConcreteType::eCONVEX_MESH) + { + Gu::ConvexMesh* gu = static_cast(s); + gu->setMeshFactory(this); + addConvexMesh(gu, false); + } + else if(serialType==PxConcreteType::eTRIANGLE_MESH_BVH33 || serialType==PxConcreteType::eTRIANGLE_MESH_BVH34) + { + Gu::TriangleMesh* gu = static_cast(s); + gu->setMeshFactory(this); + addTriangleMesh(gu, false); + } + else if(serialType==PxConcreteType::eRIGID_DYNAMIC) + { + NpRigidDynamic* np = static_cast(s); + addRigidDynamic(np, false); + } + else if(serialType==PxConcreteType::eRIGID_STATIC) + { + NpRigidStatic* np = static_cast(s); + addRigidStatic(np, false); + } + else if(serialType==PxConcreteType::eSHAPE) + { + NpShape* np = static_cast(s); + addShape(np, false); + } + else if(serialType==PxConcreteType::eMATERIAL) + { + } + else if(serialType==PxConcreteType::eCONSTRAINT) + { + NpConstraint* np = static_cast(s); + addConstraint(np, false); + } + else if(serialType==PxConcreteType::eAGGREGATE) + { + NpAggregate* np = static_cast(s); + addAggregate(np, false); + + // PT: TODO: double-check this.... is it correct? + for(PxU32 j=0;jgetCurrentSizeFast();j++) + { + PxBase* actor = np->getActorFast(j); + const PxType serialType1 = actor->getConcreteType(); + + if(serialType1==PxConcreteType::eRIGID_STATIC) + addRigidStatic(static_cast(actor), false); + else if(serialType1==PxConcreteType::eRIGID_DYNAMIC) + addRigidDynamic(static_cast(actor), false); + else if(serialType1==PxConcreteType::eARTICULATION_LINK) + {} + else PX_ASSERT(0); + } + } + else if(serialType==PxConcreteType::eARTICULATION) + { + NpArticulation* np = static_cast(s); + addArticulation(np, false); + } + else if(serialType==PxConcreteType::eARTICULATION_LINK) + { +// NpArticulationLink* np = static_cast(s); + } + else if(serialType==PxConcreteType::eARTICULATION_JOINT) + { +// NpArticulationJoint* np = static_cast(s); + } + else + { +// assert(0); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#if PX_SUPPORT_PVD +void NpFactory::setNpFactoryListener( NpFactoryListener& inListener) +{ + mNpFactoryListener = &inListener; + addFactoryListener(inListener); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// these calls are issued from the Scb layer when buffered deletes are issued. +// TODO: we should really push these down as a virtual interface that is part of Scb's reqs +// to eliminate this link-time dep. + +static void NpDestroyRigidActor(Scb::RigidStatic& scb) +{ + NpRigidStatic* np = const_cast(getNpRigidStatic(&scb)); + + void* ud = np->userData; + + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseRigidStaticToPool(*np); + else + np->~NpRigidStatic(); + + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, ud); +} + +static void NpDestroyRigidDynamic(Scb::Body& scb) +{ + NpRigidDynamic* np = const_cast(getNpRigidDynamic(&scb)); + + void* ud = np->userData; + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseRigidDynamicToPool(*np); + else + np->~NpRigidDynamic(); + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, ud); +} + +static void NpDestroyArticulationLink(Scb::Body& scb) +{ + NpArticulationLink* np = const_cast(getNpArticulationLink(&scb)); + + void* ud = np->userData; + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseArticulationLinkToPool(*np); + else + np->~NpArticulationLink(); + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, ud); +} + +static void NpDestroyArticulationJoint(Scb::ArticulationJoint& scb) +{ + PxArticulationJointBase* np = scb.getScArticulationJoint().getRoot(); + + if (np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + { + PxArticulationJointImpl* impl = np->getImpl(); + if (impl->mType == PxArticulationBase::eMaximumCoordinate) + { + NpFactory::getInstance().releaseArticulationJointToPool(*static_cast(np)); + } + else + { + NpFactory::getInstance().releaseArticulationJointRCToPool(*static_cast(np)); + } + } + else + np->~PxArticulationJointBase(); + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, NULL); +} + +static void NpDestroyArticulation(Scb::Articulation& scb) +{ + PxArticulationBase* artic = const_cast(static_cast(getNpArticulation(&scb))); + + void* ud = artic->PxArticulationBase::userData; + if (artic->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseArticulationToPool(*artic); + else + artic->~PxArticulationBase(); + NpPhysics::getInstance().notifyDeletionListenersMemRelease(artic, ud); +} + +static void NpDestroyAggregate(Scb::Aggregate& scb) +{ + NpAggregate* np = const_cast(getNpAggregate(&scb)); + + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseAggregateToPool(*np); + else + np->~NpAggregate(); + + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, NULL); +} + +static void NpDestroyShape(Scb::Shape& scb) +{ + NpShape* np = const_cast(getNpShape(&scb)); + + void* ud = np->userData; + + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseShapeToPool(*np); + else + np->~NpShape(); + + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, ud); +} + +static void NpDestroyConstraint(Scb::Constraint& scb) +{ + const size_t offset = size_t(&(reinterpret_cast(0)->getScbConstraint())); + NpConstraint* np = reinterpret_cast(reinterpret_cast(&scb)-offset); + if(np->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseConstraintToPool(*np); + else + np->~NpConstraint(); + NpPhysics::getInstance().notifyDeletionListenersMemRelease(np, NULL); +} + +namespace physx +{ + void NpDestroy(Scb::Base& base) + { + switch(base.getScbType()) + { + case ScbType::eSHAPE_EXCLUSIVE: + case ScbType::eSHAPE_SHARED: { NpDestroyShape(static_cast(base)); }break; + case ScbType::eBODY: { NpDestroyRigidDynamic(static_cast(base)); }break; + case ScbType::eBODY_FROM_ARTICULATION_LINK: { NpDestroyArticulationLink(static_cast(base)); }break; + case ScbType::eRIGID_STATIC: { NpDestroyRigidActor(static_cast(base)); }break; + case ScbType::eCONSTRAINT: { NpDestroyConstraint(static_cast(base)); }break; + case ScbType::eARTICULATION: { NpDestroyArticulation(static_cast(base)); }break; + case ScbType::eARTICULATION_JOINT: { NpDestroyArticulationJoint(static_cast(base)); }break; + case ScbType::eAGGREGATE: { NpDestroyAggregate(static_cast(base)); }break; + case ScbType::eUNDEFINED: + case ScbType::eTYPE_COUNT: + PX_ALWAYS_ASSERT_MESSAGE("NpDestroy: missing type!"); + break; + } + } +} + diff --git a/src/PhysX/physx/source/physx/src/NpFactory.h b/src/PhysX/physx/source/physx/src/NpFactory.h new file mode 100644 index 000000000..d36249995 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpFactory.h @@ -0,0 +1,266 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NP_FACTORY +#define PX_PHYSICS_NP_FACTORY + +#include "PsPool.h" +#include "PsMutex.h" +#include "PsHashSet.h" + +#include "GuMeshFactory.h" +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" +#include "PxShape.h" +#include "PxArticulationBase.h" + +namespace physx +{ + +class PxActor; + +class PxRigidActor; + +class PxRigidStatic; +class NpRigidStatic; + +class PxRigidDynamic; +class NpRigidDynamic; + +class NpConnectorArray; + +struct PxConstraintShaderTable; +class PxConstraintConnector; +class PxConstraint; +class NpConstraint; + +class PxArticulation; +class PxArticulationReducedCoordinate; +class NpArticulation; +class NpArticulationReducedCoordinate; +class PxArticulationLink; +class NpArticulationLink; +class NpArticulationJoint; +class NpArticulationJointReducedCoordinate; + +class PxMaterial; +class NpMaterial; + +class PxGeometry; + +class NpShape; + +class NpScene; + +class PxAggregate; +class NpAggregate; + +class NpConnectorArray; +class NpPtrTableStorageManager; + +namespace Cm +{ + class Collection; +} + +namespace Scb +{ + class RigidObject; + class Articulation; +} + +namespace pvdsdk +{ + class PvdDataStream; + class PsPvd; +} + +class NpFactoryListener : public GuMeshFactoryListener +{ +protected: + virtual ~NpFactoryListener(){} +}; + +class NpFactory : public GuMeshFactory +{ + PX_NOCOPY(NpFactory) +public: + NpFactory(); +private: + ~NpFactory(); +public: + static void createInstance(); + static void destroyInstance(); + static void registerArticulations(); + static void registerArticulationRCs(); + + void release(); + + void addCollection(const Cm::Collection& collection); + + PX_INLINE static NpFactory& getInstance() { return *mInstance; } + + // Rigid dynamic + PxRigidDynamic* createRigidDynamic(const PxTransform& pose); + void addRigidDynamic(PxRigidDynamic*, bool lock=true); + void releaseRigidDynamicToPool(NpRigidDynamic&); +// PT: TODO: add missing functions +// PxU32 getNbRigidDynamics() const; +// PxU32 getRigidDynamics(PxRigidDynamic** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Rigid static + PxRigidStatic* createRigidStatic(const PxTransform& pose); + void addRigidStatic(PxRigidStatic*, bool lock=true); + void releaseRigidStaticToPool(NpRigidStatic&); +// PT: TODO: add missing functions +// PxU32 getNbRigidStatics() const; +// PxU32 getRigidStatics(PxRigidStatic** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Shapes + NpShape* createShape(const PxGeometry& geometry, PxShapeFlags shapeFlags, PxMaterial*const* materials, PxU16 materialCount, bool isExclusive); + void addShape(PxShape*, bool lock=true); + void releaseShapeToPool(NpShape&); + PxU32 getNbShapes() const; + PxU32 getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Constraints + PxConstraint* createConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize); + void addConstraint(PxConstraint*, bool lock=true); + void releaseConstraintToPool(NpConstraint&); +// PT: TODO: add missing functions +// PxU32 getNbConstraints() const; +// PxU32 getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Articulations + PxArticulation* createArticulation(); + void addArticulation(PxArticulationBase*, bool lock=true); + void releaseArticulationToPool(PxArticulationBase& articulation); + PxArticulationReducedCoordinate* createArticulationRC(); + NpArticulation* createNpArticulation(); + NpArticulationReducedCoordinate* createNpArticulationRC(); +// PT: TODO: add missing functions +// PxU32 getNbArticulations() const; +// PxU32 getArticulations(PxArticulation** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Articulation links + NpArticulationLink* createNpArticulationLink(PxArticulationBase&root, NpArticulationLink* parent, const PxTransform& pose); + void releaseArticulationLinkToPool(NpArticulationLink& articulation); + PxArticulationLink* createArticulationLink(PxArticulationBase&, NpArticulationLink* parent, const PxTransform& pose); + + // Articulation joints + NpArticulationJoint* createNpArticulationJoint(NpArticulationLink& parent, const PxTransform& parentFrame, NpArticulationLink& child, const PxTransform& childFrame); + void releaseArticulationJointToPool(NpArticulationJoint& articulationJoint); + NpArticulationJointReducedCoordinate* createNpArticulationJointRC(NpArticulationLink& parent, const PxTransform& parentFrame, NpArticulationLink& child, const PxTransform& childFrame); + void releaseArticulationJointRCToPool(NpArticulationJointReducedCoordinate& articulationJoint); + + // Aggregates + PxAggregate* createAggregate(PxU32 maxActors, bool selfCollisions); + void addAggregate(PxAggregate*, bool lock=true); + void releaseAggregateToPool(NpAggregate&); +// PT: TODO: add missing functions +// PxU32 getNbAggregates() const; +// PxU32 getAggregates(PxAggregate** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + // Materials + PxMaterial* createMaterial(PxReal staticFriction, PxReal dynamicFriction, PxReal restitution); + void releaseMaterialToPool(NpMaterial& material); + + // It's easiest to track these uninvasively, so it's OK to use the Px pointers + void onActorRelease(PxActor*); + void onConstraintRelease(PxConstraint*); + void onAggregateRelease(PxAggregate*); + void onArticulationRelease(PxArticulationBase*); + void onShapeRelease(PxShape*); + + NpConnectorArray* acquireConnectorArray(); + void releaseConnectorArray(NpConnectorArray*); + + NpPtrTableStorageManager& getPtrTableStorageManager() { return *mPtrTableStorageManager; } + +#if PX_SUPPORT_PVD + void setNpFactoryListener( NpFactoryListener& ); +#endif + +private: + void releaseExclusiveShapeUserReferences(); + + Ps::Pool mConnectorArrayPool; + Ps::Mutex mConnectorArrayPoolLock; + + NpPtrTableStorageManager* mPtrTableStorageManager; + + Ps::HashSet mAggregateTracking; + Ps::HashSet mArticulationTracking; + Ps::HashSet mConstraintTracking; + Ps::HashSet mActorTracking; + Ps::CoalescedHashSet mShapeTracking; + + Ps::Pool2 mRigidDynamicPool; + Ps::Mutex mRigidDynamicPoolLock; + + Ps::Pool2 mRigidStaticPool; + Ps::Mutex mRigidStaticPoolLock; + + Ps::Pool2 mShapePool; + Ps::Mutex mShapePoolLock; + + Ps::Pool2 mAggregatePool; + Ps::Mutex mAggregatePoolLock; + + Ps::Pool2 mConstraintPool; + Ps::Mutex mConstraintPoolLock; + + Ps::Pool2 mMaterialPool; + Ps::Mutex mMaterialPoolLock; + + Ps::Pool2 mArticulationPool; + Ps::Mutex mArticulationPoolLock; + + Ps::Pool2 mArticulationRCPool; + Ps::Mutex mArticulationRCPoolLock; + + Ps::Pool2 mArticulationLinkPool; + Ps::Mutex mArticulationLinkPoolLock; + + Ps::Pool2 mArticulationJointPool; + Ps::Mutex mArticulationJointPoolLock; + + Ps::Pool2 mArticulationRCJointPool; + Ps::Mutex mArticulationJointRCPoolLock; + + static NpFactory* mInstance; + +#if PX_SUPPORT_PVD + NpFactoryListener* mNpFactoryListener; +#endif +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpMaterial.cpp b/src/PhysX/physx/source/physx/src/NpMaterial.cpp new file mode 100644 index 000000000..b743c15cb --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpMaterial.cpp @@ -0,0 +1,205 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpMaterial.h" +#include "NpPhysics.h" +#include "CmUtils.h" + +using namespace physx; + +NpMaterial::NpMaterial(const Sc::MaterialCore& desc) +: PxMaterial(PxConcreteType::eMATERIAL, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mMaterial(desc) +{ + mMaterial.setNxMaterial(this); // back-reference +} + +NpMaterial::~NpMaterial() +{ + NpPhysics::getInstance().removeMaterialFromTable(*this); +} + +// PX_SERIALIZATION +void NpMaterial::resolveReferences(PxDeserializationContext&) +{ + // ### this one could be automated if NpMaterial would inherit from MaterialCore + // ### well actually in that case the pointer would not even be needed.... + mMaterial.setNxMaterial(this); // Resolve MaterialCore::mNxMaterial + + // Maybe not the best place to do it but it has to be done before the shapes resolve material indices + // since the material index translation table is needed there. This requires that the materials have + // been added to the table already. + NpPhysics::getInstance().addMaterial(this); +} + +void NpMaterial::onRefCountZero() +{ + void* ud = userData; + + if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + NpFactory::getInstance().releaseMaterialToPool(*this); + else + this->~NpMaterial(); + + NpPhysics::getInstance().notifyDeletionListenersMemRelease(this, ud); +} + +NpMaterial* NpMaterial::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpMaterial* obj = new (address) NpMaterial(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(NpMaterial); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +void NpMaterial::release() +{ + decRefCount(); +} + +void NpMaterial::acquireReference() +{ + incRefCount(); +} + +PxU32 NpMaterial::getReferenceCount() const +{ + return getRefCount(); +} + +PX_INLINE void NpMaterial::updateMaterial() +{ + NpPhysics::getInstance().updateMaterial(*this); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setDynamicFriction(PxReal x) +{ + PX_CHECK_AND_RETURN(PxIsFinite(x), "PxMaterial::setDynamicFriction: invalid float"); + mMaterial.dynamicFriction = x; + + updateMaterial(); +} + +PxReal NpMaterial::getDynamicFriction() const +{ + return mMaterial.dynamicFriction; +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setStaticFriction(PxReal x) +{ + PX_CHECK_AND_RETURN(PxIsFinite(x), "PxMaterial::setStaticFriction: invalid float"); + mMaterial.staticFriction = x; + + updateMaterial(); +} + +PxReal NpMaterial::getStaticFriction() const +{ + return mMaterial.staticFriction; +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setRestitution(PxReal x) +{ + PX_CHECK_AND_RETURN(PxIsFinite(x), "PxMaterial::setRestitution: invalid float"); + PX_CHECK_MSG(((x >= 0.0f) && (x <= 1.0f)), "PxMaterial::setRestitution: Restitution value has to be in [0,1]!"); + if ((x < 0.0f) || (x > 1.0f)) + { + PxClamp(x, 0.0f, 1.0f); + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxMaterial::setRestitution: Invalid value %f was clamped to [0,1]!", PxF64(x)); + } + mMaterial.restitution = x; + + updateMaterial(); +} + +PxReal NpMaterial::getRestitution() const +{ + return mMaterial.restitution; +} + +///////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setFlag(PxMaterialFlag::Enum flag, bool value) +{ + if (value) + mMaterial.flags |= flag; + else + mMaterial.flags &= ~PxMaterialFlags(flag); + + updateMaterial(); +} + +void NpMaterial::setFlags(PxMaterialFlags inFlags) +{ + mMaterial.flags = inFlags; + updateMaterial(); +} + +PxMaterialFlags NpMaterial::getFlags() const +{ + return mMaterial.flags; +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setFrictionCombineMode(PxCombineMode::Enum x) +{ + mMaterial.setFrictionCombineMode(x); + + updateMaterial(); +} + +PxCombineMode::Enum NpMaterial::getFrictionCombineMode() const +{ + return mMaterial.getFrictionCombineMode(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpMaterial::setRestitutionCombineMode(PxCombineMode::Enum x) +{ + mMaterial.setRestitutionCombineMode(x); + updateMaterial(); +} + +PxCombineMode::Enum NpMaterial::getRestitutionCombineMode() const +{ + return mMaterial.getRestitutionCombineMode(); +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/physx/src/NpMaterial.h b/src/PhysX/physx/source/physx/src/NpMaterial.h new file mode 100644 index 000000000..722b9b379 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpMaterial.h @@ -0,0 +1,122 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_MATERIAL +#define PX_PHYSICS_NP_MATERIAL + +#include "PxMaterial.h" +#include "ScMaterialCore.h" +#include "PsUserAllocated.h" +#include "CmRefCountable.h" +#include "PsUtilities.h" + +// PX_SERIALIZATION +#include "PxSerialFramework.h" +//~PX_SERIALIZATION + +namespace physx +{ + +// Compared to other objects, materials are special since they belong to the SDK and not to scenes +// (similar to meshes). That's why the NpMaterial does have direct access to the core material instead +// of having a buffered interface for it. Scenes will have copies of the SDK material table and there +// the materials will be buffered. + + +class NpMaterial : public PxMaterial, public Ps::UserAllocated, public Cm::RefCountable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpMaterial(PxBaseFlags baseFlags) : PxMaterial(baseFlags), Cm::RefCountable(PxEmpty), mMaterial(PxEmpty) {} + virtual void onRefCountZero(); + virtual void resolveReferences(PxDeserializationContext& context); + static NpMaterial* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void exportExtraData(PxSerializationContext&) {} + void importExtraData(PxDeserializationContext&) {} + virtual void requiresObjects(PxProcessPxBaseCallback&){} +//~PX_SERIALIZATION + NpMaterial(const Sc::MaterialCore& desc); + ~NpMaterial(); + + virtual void release(); + + virtual void acquireReference(); + virtual PxU32 getReferenceCount() const; + + virtual void setDynamicFriction(PxReal); + virtual PxReal getDynamicFriction() const; + virtual void setStaticFriction(PxReal); + virtual PxReal getStaticFriction() const; + virtual void setRestitution(PxReal); + virtual PxReal getRestitution() const; + virtual void setFlag(PxMaterialFlag::Enum flag, bool value); + virtual void setFlags(PxMaterialFlags inFlags); + virtual PxMaterialFlags getFlags() const; + virtual void setFrictionCombineMode(PxCombineMode::Enum); + virtual PxCombineMode::Enum getFrictionCombineMode() const; + virtual void setRestitutionCombineMode(PxCombineMode::Enum); + virtual PxCombineMode::Enum getRestitutionCombineMode() const; + + PX_INLINE const Sc::MaterialCore& getScMaterial() const { return mMaterial; } + PX_INLINE Sc::MaterialCore& getScMaterial() { return mMaterial; } + PX_INLINE PxU32 getHandle() const { return mMaterial.getMaterialIndex();} + PX_INLINE void setHandle(PxU32 handle) { return mMaterial.setMaterialIndex(handle);} + + PX_FORCE_INLINE static void getMaterialIndices(PxMaterial*const* materials, PxU16* materialIndices, PxU32 materialCount); + +private: + PX_INLINE void updateMaterial(); + +// PX_SERIALIZATION +public: +//~PX_SERIALIZATION + Sc::MaterialCore mMaterial; +}; + + +PX_FORCE_INLINE void NpMaterial::getMaterialIndices(PxMaterial*const* materials, PxU16* materialIndices, PxU32 materialCount) +{ + for(PxU32 i=0; i < materialCount; i++) + { + materialIndices[i] = Ps::to16((static_cast(materials[i]))->getHandle()); + } +} + + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpMaterialManager.h b/src/PhysX/physx/source/physx/src/NpMaterialManager.h new file mode 100644 index 000000000..d3c38a842 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpMaterialManager.h @@ -0,0 +1,164 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef NP_MATERIALMANAGER +#define NP_MATERIALMANAGER + +#include "foundation/PxMemory.h" +#include "NpMaterial.h" +#include "CmIDPool.h" + +namespace physx +{ + class NpMaterialManager + { + public: + NpMaterialManager() + { + const PxU32 matCount = 128; + mMaterials = reinterpret_cast(PX_ALLOC(sizeof(NpMaterial*) * matCount, "NpMaterialManager::initialise")); + mMaxMaterials = matCount; + PxMemZero(mMaterials, sizeof(NpMaterial*)*mMaxMaterials); + } + + ~NpMaterialManager() {} + + void releaseMaterials() + { + for(PxU32 i=0; igetHandle(); + mHandleManager.freeID(handle); + mMaterials[i]->release(); + mMaterials[i] = NULL; + } + } + PX_FREE(mMaterials); + } + + bool setMaterial(NpMaterial& mat) + { + const PxU32 materialIndex = mHandleManager.getNewID(); + + if(materialIndex >= mMaxMaterials) + resize(); + + mMaterials[materialIndex] = &mat; + mat.setHandle(materialIndex); + return true; + } + + void updateMaterial(NpMaterial& mat) + { + mMaterials[mat.getHandle()] = &mat; + } + + PX_FORCE_INLINE PxU32 getNumMaterials() const + { + return mHandleManager.getNumUsedID(); + } + + void removeMaterial(NpMaterial& mat) + { + const PxU32 handle = mat.getHandle(); + if(handle != MATERIAL_INVALID_HANDLE) + { + mMaterials[handle] = NULL; + mHandleManager.freeID(handle); + } + } + + PX_FORCE_INLINE NpMaterial* getMaterial(const PxU32 index) const + { + PX_ASSERT(index < mMaxMaterials); + return mMaterials[index]; + } + + PX_FORCE_INLINE PxU32 getMaxSize() const + { + return mMaxMaterials; + } + + PX_FORCE_INLINE NpMaterial** getMaterials() const + { + return mMaterials; + } + + private: + void resize() + { + const PxU32 numMaterials = mMaxMaterials; + mMaxMaterials = mMaxMaterials*2; + + NpMaterial** mat = reinterpret_cast(PX_ALLOC(sizeof(NpMaterial*)*mMaxMaterials, "NpMaterialManager::resize")); + PxMemZero(mat, sizeof(NpMaterial*)*mMaxMaterials); + for(PxU32 i=0; i 72 => 64 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpMaterial) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpMaterial, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpMaterial, RefCountable) + + PX_DEF_BIN_METADATA_ITEM(stream, NpMaterial, void, userData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpMaterial, MaterialCore, mMaterial, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpConstraint::getBinaryMetaData(PxOutputStream& stream) +{ +// 136 => 140 => 144 => 128 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpConstraint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpConstraint, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, NpConstraint, PxRigidActor, mActor0, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpConstraint, PxRigidActor, mActor1, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpConstraint, Scb::Constraint, mConstraint, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpConstraint, bool, mIsDirty, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, NpConstraint, bool, mPaddingFromBool, PxMetaDataFlag::ePADDING) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpShapeManager::getBinaryMetaData(PxOutputStream& stream) +{ +// 8 bytes + PX_DEF_BIN_METADATA_CLASS(stream, NpShapeManager) + PX_DEF_BIN_METADATA_ITEM(stream, NpShapeManager, PtrTable, mShapes, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpShapeManager, PtrTable, mSceneQueryData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpShapeManager, PxU32, mSqCompoundId, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpShapeManager, Sq::PruningStructure, mPruningStructure, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpShape::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, NpInternalShapeFlags, PxU8) + +// 208 => 224 => 208 => 192 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpShape) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpShape, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpShape, RefCountable) + + // PxShape + PX_DEF_BIN_METADATA_ITEM(stream, NpShape, void, userData, PxMetaDataFlag::ePTR) + + // NpShape + PX_DEF_BIN_METADATA_ITEM(stream, NpShape, PxRigidActor, mActor, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpShape, Scb::Shape, mShape, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpShape, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpShape, PxI32, mExclusiveAndActorCount, 0) + + + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, NpShape, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpRigidStatic::getBinaryMetaData(PxOutputStream& stream) +{ +// 124 => 128 => 124 => 108 => 96 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpRigidStatic) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpRigidStatic, PxBase) +// PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpRigidStatic, NpRigidStaticT) // ### ??? + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpRigidStatic, NpActor) + + DefineMetaData_PxActor(NpRigidStatic) + DefineMetaData_NpRigidActorTemplate(NpRigidStatic) + + // NpRigidStatic + PX_DEF_BIN_METADATA_ITEM(stream, NpRigidStatic, Scb::RigidStatic, mRigidStatic, 0) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, NpRigidStatic, NpConnectorArray, mConnectorArray, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, NpRigidStatic, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpConnector::getBinaryMetaData(PxOutputStream& stream) +{ +// 8 bytes + PX_DEF_BIN_METADATA_CLASS(stream, NpConnector) + PX_DEF_BIN_METADATA_ITEM(stream, NpConnector, PxU8, mType, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, NpConnector, PxU8, mPadding, PxMetaDataFlag::ePADDING) + PX_DEF_BIN_METADATA_ITEM(stream, NpConnector, PxBase, mObject, PxMetaDataFlag::ePTR) +} + +void NpConnectorArray::getBinaryMetaData(PxOutputStream& stream) +{ +// 48 bytes + PX_DEF_BIN_METADATA_CLASS(stream, NpConnectorArray) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, NpConnectorArray, NpConnector, mBuffer, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpConnectorArray, bool, mBufferUsed, 0) + // PT: OMG this is so painful... I can't put the padding explicitly in the template + { PxMetaDataEntry tmp = {"char", "mPadding", 1 + PxU32(PX_OFFSET_OF_RT(NpConnectorArray, mBufferUsed)), 3, 3, 0, PxMetaDataFlag::ePADDING, 0}; PX_STORE_METADATA(stream, tmp); } + PX_DEF_BIN_METADATA_ITEM(stream, NpConnectorArray, NpConnector, mData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpConnectorArray, PxU32, mSize, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpConnectorArray, PxU32, mCapacity, 0) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, NpConnectorArray, NpConnector, mBufferUsed, mCapacity, PxMetaDataFlag::eCONTROL_FLIP|PxMetaDataFlag::eCOUNT_MASK_MSB, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpRigidDynamic::getBinaryMetaData(PxOutputStream& stream) +{ +// 368 => 352 => 304 => 288 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpRigidDynamic) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpRigidDynamic, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpRigidDynamic, NpActor) + + DefineMetaData_PxActor(NpRigidDynamic) + DefineMetaData_NpRigidActorTemplate(NpRigidDynamic) + DefineMetaData_NpRigidBodyTemplate(NpRigidDynamic) + + // NpRigidDynamic + + //------ Extra-data ------ + +// Extra data: +// - inline array from shape manager +// - optional constraint array + +// PX_DEF_BIN_METADATA_ITEM(stream,NpRigidDynamic, NpShapeManager, mShapeManager.mShapes, 0) + +/* + virtual void exportExtraData(PxOutputStream& stream) + { + mShapeManager.exportExtraData(stream); + ActorTemplateClass::exportExtraData(stream); + } +void NpActorTemplate::exportExtraData(PxOutputStream& stream) +{ + if(mConnectorArray) + { + stream.storeBuffer(mConnectorArray, sizeof(NpConnectorArray) + mConnectorArray->exportExtraData(stream); + } +} +*/ + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, NpRigidDynamic, NpConnectorArray, mConnectorArray, PX_SERIAL_ALIGN) +//### missing inline array data here... only works for "buffered" arrays so far +/* + Big issue: we can't output the "offset of" the inline array within the class, since the inline array itself is extra-data. But we need to read + the array itself to know if it's inline or not (the "is buffered" bool). So we need to read from the extra data! +*/ + +/* +[17:41:39] Gordon Yeoman nvidia: PxsBodyCore need to be 16-byte aligned for spu. If it is 128-byte aligned then that is a mistake. Feel free to change it to 16. +*/ + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, NpRigidDynamic, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpArticulationLinkArray::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, NpArticulationLinkArray) + PX_DEF_BIN_METADATA_ITEMS(stream, NpArticulationLinkArray, NpArticulationLink, mBuffer, PxMetaDataFlag::ePTR, 4) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLinkArray, bool, mBufferUsed, 0) + // PT: OMG this is so painful... I can't put the padding explicitely in the template + { PxMetaDataEntry tmp = {"char", "mPadding", 1 + PxU32(PX_OFFSET_OF_RT(NpArticulationLinkArray, mBufferUsed)), 3, 3, 0, PxMetaDataFlag::ePADDING, 0}; PX_STORE_METADATA(stream, tmp); } + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLinkArray, NpArticulationLink, mData, PxMetaDataFlag::ePTR) // ### + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLinkArray, PxU32, mSize, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLinkArray, PxU32, mCapacity, 0) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, NpArticulationLinkArray, NpArticulationLink, mBufferUsed, mCapacity, PxMetaDataFlag::eCONTROL_FLIP|PxMetaDataFlag::eCOUNT_MASK_MSB|PxMetaDataFlag::ePTR, 0) +} + +void NpArticulation::getBinaryMetaData(PxOutputStream& stream) +{ +// 92 => 108 => 104 => 116 => 120 => 104 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpArticulation) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpArticulation, PxBase) + + // PxArticulation + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxArticulationBase::Enum, PxU32) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, void, userData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, PxArticulationBase::Enum, mType, 0) + + // NpArticulation + //PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, PxArticulationImpl, mImpl, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, Scb::Articulation, mImpl.mArticulation, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, NpArticulationLinkArray, mImpl.mArticulationLinks, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, NpAggregate, mImpl.mAggregate, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, char, mImpl.mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, NpArticulation, mImpl.mName, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulation, PxU32, mImpl.mCacheVersion, 0) + +} + +void NpArticulationLink::getBinaryMetaData(PxOutputStream& stream) +{ +// 400 (!) => 352 => 336 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpArticulationLink) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpArticulationLink, PxBase) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpArticulationLink, NpActor) + + DefineMetaData_PxActor(NpArticulationLink) + DefineMetaData_NpRigidActorTemplate(NpArticulationLink) + DefineMetaData_NpRigidBodyTemplate(NpArticulationLink) + + // NpArticulationLink + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, NpArticulation, mRoot, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, NpArticulationJoint, mInboundJoint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, NpArticulationLink, mParent, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, NpArticulationLinkArray, mChildLinks, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, PxU32, mLLIndex, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationLink, PxU32, mInboundJointDof, 0); + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, NpArticulationLink, NpConnectorArray, mConnectorArray, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, NpArticulationLink, mName, 0) +} + +void NpArticulationJoint::getBinaryMetaData(PxOutputStream& stream) +{ +// 184 => 200 => 192 => 224 => 208 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpArticulationJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpArticulationJoint, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationJoint, Scb::ArticulationJoint, mImpl.mJoint, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationJoint, NpArticulationLink, mImpl.mParent, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationJoint, NpArticulationLink, mImpl.mChild, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, NpArticulationJoint, PxArticulationBase::Enum, mImpl.mType, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpAggregate::getBinaryMetaData(PxOutputStream& stream) +{ +// 36 => 56 => 40 bytes + PX_DEF_BIN_METADATA_VCLASS(stream, NpAggregate) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, NpAggregate, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, NpAggregate, Scb::Aggregate, mAggregate, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpAggregate, PxU32, mNbActors, 0) + PX_DEF_BIN_METADATA_ITEM(stream, NpAggregate, PxActor, mActors, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + // mActors + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, NpAggregate, PxActor, mActors, mNbActors, PxMetaDataFlag::ePTR, PX_SERIAL_ALIGN) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PxMeshScale(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxMeshScale) + PX_DEF_BIN_METADATA_ITEM(stream, PxMeshScale, PxVec3, scale, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxMeshScale, PxQuat, rotation, 0) +} + +/////////////////////////////////////////////////////////////////////////////// +namespace physx +{ +void getBinaryMetaData_PxBase(PxOutputStream& stream) +{ + // 8 bytes + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxBaseFlags, PxU16) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxType, PxU16) + PX_DEF_BIN_METADATA_VCLASS(stream, PxBase) + PX_DEF_BIN_METADATA_ITEM(stream, PxBase, PxType, mConcreteType, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxBase, PxBaseFlags, mBaseFlags, 0) +} +} +void RefCountable::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_VCLASS(stream, RefCountable) + PX_DEF_BIN_METADATA_ITEM(stream, RefCountable, PxI32, mRefCount, 0) +} + +static void getFoundationMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxU8, char) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxI8, char) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxU16, short) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxI16, short) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxU32, int) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxI32, int) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxReal, float) + + getBinaryMetaData_PxVec3(stream); + getBinaryMetaData_PxVec4(stream); + getBinaryMetaData_PxQuat(stream); + getBinaryMetaData_PxBounds3(stream); + getBinaryMetaData_PxTransform(stream); + getBinaryMetaData_PxMat33(stream); + getBinaryMetaData_SpatialVectorF(stream); + getBinaryMetaData_BitMap(stream); + Cm::PtrTable::getBinaryMetaData(stream); + getBinaryMetaData_PxPlane(stream); + getBinaryMetaData_PxConstraintInvMassScale(stream); + + getBinaryMetaData_PxBase(stream); + RefCountable::getBinaryMetaData(stream); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PxGetPhysicsBinaryMetaData(PxOutputStream& stream) +{ + getFoundationMetaData(stream); + + getBinaryMetaData_PxMeshScale(stream); + + MaterialIndicesStruct::getBinaryMetaData(stream); + Gu::GeometryUnion::getBinaryMetaData(stream); + Gu::ConvexMesh::getBinaryMetaData(stream); + Gu::TriangleMesh::getBinaryMetaData(stream); + Gu::RTreeTriangleMesh::getBinaryMetaData(stream); + Gu::BV4TriangleMesh::getBinaryMetaData(stream); + Gu::HeightField::getBinaryMetaData(stream); + + Sc::ActorCore::getBinaryMetaData(stream); + Sc::RigidCore::getBinaryMetaData(stream); + Sc::StaticCore::getBinaryMetaData(stream); + Sc::BodyCore::getBinaryMetaData(stream); + Sc::MaterialCore::getBinaryMetaData(stream); + Sc::ShapeCore::getBinaryMetaData(stream); + Sc::ConstraintCore::getBinaryMetaData(stream); + Sc::ArticulationCore::getBinaryMetaData(stream); + Sc::ArticulationJointCore::getBinaryMetaData(stream); + + Scb::Base::getBinaryMetaData(stream); + Scb::Actor::getBinaryMetaData(stream); + Scb::RigidObject::getBinaryMetaData(stream); + Scb::RigidStatic::getBinaryMetaData(stream); + Scb::Body::getBinaryMetaData(stream); + Scb::Shape::getBinaryMetaData(stream); + Scb::Constraint::getBinaryMetaData(stream); + Scb::Articulation::getBinaryMetaData(stream); + Scb::ArticulationJoint::getBinaryMetaData(stream); + Scb::Aggregate::getBinaryMetaData(stream); + + NpConnector::getBinaryMetaData(stream); + NpConnectorArray::getBinaryMetaData(stream); + NpActor::getBinaryMetaData(stream); + NpMaterial::getBinaryMetaData(stream); // NP_MATERIAL + NpRigidDynamic::getBinaryMetaData(stream); // NP_RIGID_DYNAMIC + NpRigidStatic::getBinaryMetaData(stream); // NP_RIGID_STATIC + NpShape::getBinaryMetaData(stream); // NP_SHAPE + NpConstraint::getBinaryMetaData(stream); // NP_CONSTRAINT + NpArticulation::getBinaryMetaData(stream); // NP_ARTICULATION + NpArticulationLink::getBinaryMetaData(stream); // NP_ARTICULATION_LINK + NpArticulationJoint::getBinaryMetaData(stream); // NP_ARTICULATION_JOINT + NpArticulationLinkArray::getBinaryMetaData(stream); + NpShapeManager::getBinaryMetaData(stream); + NpAggregate::getBinaryMetaData(stream); // NP_AGGREGATE +} diff --git a/src/PhysX/physx/source/physx/src/NpPhysics.cpp b/src/PhysX/physx/source/physx/src/NpPhysics.cpp new file mode 100644 index 000000000..8db81f7f5 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPhysics.cpp @@ -0,0 +1,747 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpPhysics.h" + +// PX_SERIALIZATION +#include "foundation/PxProfiler.h" +#include "foundation/PxIO.h" +#include "foundation/PxErrorCallback.h" +#include "PxPhysicsVersion.h" +#include "CmCollection.h" +#include "CmUtils.h" +#include "NpRigidStatic.h" +#include "NpRigidDynamic.h" +#include "NpArticulation.h" +#include "NpArticulationLink.h" +#include "NpArticulationJoint.h" +#include "NpMaterial.h" +#include "GuHeightFieldData.h" +#include "GuHeightField.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "PsIntrinsics.h" +#include "PxTolerancesScale.h" +#include "PxvGlobals.h" // dynamic registration of HFs & articulations in LL +#include "GuOverlapTests.h" // dynamic registration of HFs in Gu +#include "PxDeletionListener.h" +#include "PxPhysicsSerialization.h" +#include "PsString.h" +#include "PvdPhysicsClient.h" +#include "SqPruningStructure.h" + +//~PX_SERIALIZATION + +#include "NpFactory.h" + +#if PX_SWITCH +#include "switch/NpMiddlewareInfo.h" +#endif + +using namespace physx; +using namespace Cm; + +bool NpPhysics::apiReentryLock = false; +NpPhysics* NpPhysics::mInstance = NULL; +PxU32 NpPhysics::mRefCount = 0; + +#if PX_CHECKED +bool NpPhysics::mHeightFieldsRegistered = false; //just for error checking +#endif + +NpPhysics::NpPhysics(const PxTolerancesScale& scale, const PxvOffsetTable& pxvOffsetTable, bool trackOutstandingAllocations, pvdsdk::PsPvd* pvd) : + mSceneArray(PX_DEBUG_EXP("physicsSceneArray")) + , mPhysics(scale, pxvOffsetTable) + , mDeletionListenersExist(false) +#if PX_SUPPORT_GPU_PHYSX + , mNbRegisteredGpuClients(0) +#endif +{ + PX_UNUSED(trackOutstandingAllocations); + + //mMasterMaterialTable.reserve(10); + +#if PX_SUPPORT_PVD + mPvd = pvd; + if(pvd) + { + mPvdPhysicsClient = PX_NEW(Vd::PvdPhysicsClient)(mPvd); + shdfnd::getFoundation().registerErrorCallback(*mPvdPhysicsClient); + shdfnd::getFoundation().registerAllocationListener(*mPvd); + } + else + { + mPvdPhysicsClient = NULL; + } +#else + PX_UNUSED(pvd); +#endif +} + +NpPhysics::~NpPhysics() +{ + // Release all scenes in case the user didn't do it + PxU32 nbScenes = mSceneArray.size(); + NpScene** scenes = mSceneArray.begin(); + for(PxU32 i=0;i 0) + //{ + // // It's done this way since the material destructor removes the material from the table and adjusts indices + + // PX_ASSERT(mMasterMaterialTable[0]->getRefCount() == 1); + // mMasterMaterialTable[0]->decRefCount(); + //} + //mMasterMaterialTable.clear(); + + mMasterMaterialManager.releaseMaterials(); + +#if PX_SUPPORT_PVD + if(mPvd) + { + mPvdPhysicsClient->destroyPvdInstance(this); + mPvd->removeClient(mPvdPhysicsClient); + shdfnd::getFoundation().deregisterErrorCallback(*mPvdPhysicsClient); + PX_DELETE_AND_RESET(mPvdPhysicsClient); + shdfnd::getFoundation().deregisterAllocationListener(*mPvd); + } +#endif + + const DeletionListenerMap::Entry* delListenerEntries = mDeletionListenerMap.getEntries(); + const PxU32 delListenerEntryCount = mDeletionListenerMap.size(); + for(PxU32 i=0; i < delListenerEntryCount; i++) + { + PX_DELETE(delListenerEntries[i].second); + } + mDeletionListenerMap.clear(); +} + +void NpPhysics::initOffsetTables(PxvOffsetTable& pxvOffsetTable) +{ + // init offset tables for Pxs/Sc/Scb/Px conversions + { + Sc::OffsetTable& offsetTable = Sc::gOffsetTable; + offsetTable.scRigidStatic2PxActor = -reinterpret_cast(&(reinterpret_cast(0)->getScbRigidStaticFast())) - static_cast(Scb::RigidStatic::getScOffset()); + offsetTable.scRigidDynamic2PxActor = -reinterpret_cast(&(reinterpret_cast(0)->getScbBodyFast())) - static_cast(Scb::Body::getScOffset()); + offsetTable.scArticulationLink2PxActor = -reinterpret_cast(&(reinterpret_cast(0)->getScbBodyFast())) - static_cast(Scb::Body::getScOffset()); + offsetTable.scArticulation2Px = -reinterpret_cast(&(reinterpret_cast(0)->mImpl.getScbArticulation())) - static_cast(Scb::Articulation::getScOffset()); + offsetTable.scConstraint2Px = -reinterpret_cast(&(reinterpret_cast(0)->getScbConstraint())) - static_cast(Scb::Constraint::getScOffset()); + offsetTable.scShape2Px = -reinterpret_cast(&(reinterpret_cast(0)->getScbShape())) - static_cast(Scb::Shape::getScOffset()); + + for(PxU32 i=0;i(&static_cast(0)->getCore()); + pxvOffsetTable.pxsRigidCore2PxRigidBody = scOffsetTable.scRigidDynamic2PxActor - reinterpret_cast(&static_cast(0)->getCore()); + pxvOffsetTable.pxsRigidCore2PxRigidStatic = scOffsetTable.scRigidStatic2PxActor - reinterpret_cast(&static_cast(0)->getCore()); + } +} + +NpPhysics* NpPhysics::createInstance(PxU32 version, PxFoundation& foundation, const PxTolerancesScale& scale, bool trackOutstandingAllocations, pvdsdk::PsPvd* pvd) +{ + PX_UNUSED(foundation); + +#if PX_SWITCH + NpSetMiddlewareInfo(); // register middleware info such that PhysX usage can be tracked +#endif + + if (version!=PX_PHYSICS_VERSION) + { + char buffer[256]; + Ps::snprintf(buffer, 256, "Wrong version: PhysX version is 0x%08x, tried to create 0x%08x", PX_PHYSICS_VERSION, version); + foundation.getErrorCallback().reportError(PxErrorCode::eINVALID_PARAMETER, buffer, __FILE__, __LINE__); + return NULL; + } + + if (!scale.isValid()) + { + foundation.getErrorCallback().reportError(PxErrorCode::eINVALID_PARAMETER, "Scale invalid.\n", __FILE__, __LINE__); + return NULL; + } + + if(0 == mRefCount) + { + PX_ASSERT(static_cast(&foundation) == &Ps::Foundation::getInstance()); + + Ps::Foundation::incRefCount(); + + // init offset tables for Pxs/Sc/Scb/Px conversions + PxvOffsetTable pxvOffsetTable; + initOffsetTables(pxvOffsetTable); + + //SerialFactory::createInstance(); + mInstance = PX_NEW (NpPhysics)(scale, pxvOffsetTable, trackOutstandingAllocations, pvd); + NpFactory::createInstance(); + +#if PX_SUPPORT_PVD + if(pvd) + { + NpFactory::getInstance().setNpFactoryListener( *mInstance->mPvdPhysicsClient ); + pvd->addClient(mInstance->mPvdPhysicsClient); + } +#endif + + NpFactory::getInstance().addFactoryListener(mInstance->mDeletionMeshListener); + } + ++mRefCount; + + return mInstance; +} + +PxU32 NpPhysics::releaseInstance() +{ + PX_ASSERT(mRefCount > 0); + if (--mRefCount) + return mRefCount; + +#if PX_SUPPORT_PVD + if(mInstance->mPvd) + { + NpFactory::getInstance().removeFactoryListener( *mInstance->mPvdPhysicsClient ); + } +#endif + + NpFactory::destroyInstance(); + + PX_ASSERT(mInstance); + PX_DELETE_AND_RESET(mInstance); + + Ps::Foundation::decRefCount(); + + return mRefCount; +} + +void NpPhysics::release() +{ + NpPhysics::releaseInstance(); +} + +PxScene* NpPhysics::createScene(const PxSceneDesc& desc) +{ + PX_CHECK_AND_RETURN_NULL(desc.isValid(), "Physics::createScene: desc.isValid() is false!"); + + const PxTolerancesScale& scale = mPhysics.getTolerancesScale(); + const PxTolerancesScale& descScale = desc.getTolerancesScale(); + PX_UNUSED(scale); + PX_UNUSED(descScale); + PX_CHECK_AND_RETURN_NULL((descScale.length == scale.length) && (descScale.speed == scale.speed), "Physics::createScene: PxTolerancesScale must be the same as used for creation of PxPhysics!"); + + Ps::Mutex::ScopedLock lock(mSceneAndMaterialMutex); // done here because scene constructor accesses profiling manager of the SDK + + NpScene* npScene = PX_NEW (NpScene)(desc); + if(!npScene) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Unable to create scene."); + return NULL; + } + if(!npScene->getTaskManager()) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Unable to create scene. Task manager creation failed."); + return NULL; + } + + npScene->loadFromDesc(desc); + +#if PX_SUPPORT_PVD + if(mPvd) + { + npScene->mScene.getScenePvdClient().setPsPvd(mPvd); + mPvd->addClient(&npScene->mScene.getScenePvdClient()); + } +#endif + + if (!sendMaterialTable(*npScene) || !npScene->getScene().isValid()) + { + PX_DELETE(npScene); + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Unable to create scene."); + return NULL; + } + + mSceneArray.pushBack(npScene); + return npScene; +} + +void NpPhysics::releaseSceneInternal(PxScene& scene) +{ + NpScene* pScene = static_cast(&scene); + + Ps::Mutex::ScopedLock lock(mSceneAndMaterialMutex); + for(PxU32 i=0;i(mSceneAndMaterialMutex)); + return mSceneArray.size(); +} + +PxU32 NpPhysics::getScenes(PxScene** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(const_cast(mSceneAndMaterialMutex)); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mSceneArray.begin(), mSceneArray.size()); +} + +PxRigidStatic* NpPhysics::createRigidStatic(const PxTransform& globalPose) +{ + PX_CHECK_AND_RETURN_NULL(globalPose.isSane(), "PxPhysics::createRigidStatic: invalid transform"); + return NpFactory::getInstance().createRigidStatic(globalPose.getNormalized()); +} + +PxShape* NpPhysics::createShape(const PxGeometry& geometry, PxMaterial*const * materials, PxU16 materialCount, bool isExclusive, PxShapeFlags shapeFlags) +{ + PX_CHECK_AND_RETURN_NULL(materials, "createShape: material pointer is NULL"); + PX_CHECK_AND_RETURN_NULL(materialCount>0, "createShape: material count is zero"); + +#if PX_CHECKED + const bool isHeightfield = geometry.getType() == PxGeometryType::eHEIGHTFIELD; + if (isHeightfield) + { + PX_CHECK_AND_RETURN_NULL(mHeightFieldsRegistered, "NpPhysics::createShape: Creating Heightfield shape without having called PxRegister[Unified]HeightFields()!"); + } + const bool hasMeshTypeGeom = isHeightfield || (geometry.getType() == PxGeometryType::eTRIANGLEMESH); + PX_CHECK_AND_RETURN_NULL(!(hasMeshTypeGeom && (shapeFlags & PxShapeFlag::eTRIGGER_SHAPE)), "NpPhysics::createShape: triangle mesh and heightfield triggers are not supported!"); + PX_CHECK_AND_RETURN_NULL(!((shapeFlags & PxShapeFlag::eSIMULATION_SHAPE) && (shapeFlags & PxShapeFlag::eTRIGGER_SHAPE)), "NpPhysics::createShape: shapes cannot simultaneously be trigger shapes and simulation shapes."); +#endif + + return NpFactory::getInstance().createShape(geometry, shapeFlags, materials, materialCount, isExclusive); +} + +PxU32 NpPhysics::getNbShapes() const +{ + return NpFactory::getInstance().getNbShapes(); +} + +PxU32 NpPhysics::getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return NpFactory::getInstance().getShapes(userBuffer, bufferSize, startIndex); +} + +PxRigidDynamic* NpPhysics::createRigidDynamic(const PxTransform& globalPose) +{ + PX_CHECK_AND_RETURN_NULL(globalPose.isSane(), "PxPhysics::createRigidDynamic: invalid transform"); + return NpFactory::getInstance().createRigidDynamic(globalPose.getNormalized()); +} + +PxConstraint* NpPhysics::createConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) +{ + return NpFactory::getInstance().createConstraint(actor0, actor1, connector, shaders, dataSize); +} + +PxArticulation* NpPhysics::createArticulation() +{ + return NpFactory::getInstance().createArticulation(); +} + +PxArticulationReducedCoordinate* NpPhysics::createArticulationReducedCoordinate() +{ + return NpFactory::getInstance().createArticulationRC(); +} + +PxAggregate* NpPhysics::createAggregate(PxU32 maxSize, bool selfCollisionEnabled) +{ + return NpFactory::getInstance().createAggregate(maxSize, selfCollisionEnabled); +} + +/////////////////////////////////////////////////////////////////////////////// + +NpMaterial* NpPhysics::addMaterial(NpMaterial* m) +{ + if(!m) + return NULL; + + Ps::Mutex::ScopedLock lock(mSceneAndMaterialMutex); + + //the handle is set inside the setMaterial method + if(mMasterMaterialManager.setMaterial(*m)) + { + // Let all scenes know of the new material + for(PxU32 i=0; i < mSceneArray.size(); i++) + { + NpScene* s = getScene(i); + s->addMaterial(*m); + } + return m; + } + else + { + m->release(); + return NULL; + } +} + +PxMaterial* NpPhysics::createMaterial(PxReal staticFriction, PxReal dynamicFriction, PxReal restitution) +{ + PxMaterial* m = NpFactory::getInstance().createMaterial(staticFriction, dynamicFriction, restitution); + return addMaterial(static_cast(m)); +} + +PxU32 NpPhysics::getNbMaterials() const +{ + Ps::Mutex::ScopedLock lock(const_cast(mSceneAndMaterialMutex)); + return mMasterMaterialManager.getNumMaterials(); +} + +PxU32 NpPhysics::getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Ps::Mutex::ScopedLock lock(const_cast(mSceneAndMaterialMutex)); + NpMaterialManagerIterator iter(mMasterMaterialManager); + PxU32 writeCount =0; + PxU32 index = 0; + NpMaterial* mat; + while(iter.getNextMaterial(mat)) + { + if(index++ < startIndex) + continue; + if(writeCount == bufferSize) + break; + userBuffer[writeCount++] = mat; + } + return writeCount; +} + +void NpPhysics::removeMaterialFromTable(NpMaterial& m) +{ + Ps::Mutex::ScopedLock lock(mSceneAndMaterialMutex); + + // Let all scenes know of the deleted material + for(PxU32 i=0; i < mSceneArray.size(); i++) + { + NpScene* s = getScene(i); + s->removeMaterial(m); + } + + mMasterMaterialManager.removeMaterial(m); +} + +void NpPhysics::updateMaterial(NpMaterial& m) +{ + Ps::Mutex::ScopedLock lock(mSceneAndMaterialMutex); + + // Let all scenes know of the updated material + for(PxU32 i=0; i < mSceneArray.size(); i++) + { + NpScene* s = getScene(i); + s->updateMaterial(m); + } + mMasterMaterialManager.updateMaterial(m); +} + +bool NpPhysics::sendMaterialTable(NpScene& scene) +{ + // note: no lock here because this method gets only called at scene creation and there we do lock + + NpMaterialManagerIterator iter(mMasterMaterialManager); + NpMaterial* mat; + while(iter.getNextMaterial(mat)) + scene.addMaterial(*mat); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +PxTriangleMesh* NpPhysics::createTriangleMesh(PxInputStream& stream) +{ + return NpFactory::getInstance().createTriangleMesh(stream); +} + +PxU32 NpPhysics::getNbTriangleMeshes() const +{ + return NpFactory::getInstance().getNbTriangleMeshes(); +} + +PxU32 NpPhysics::getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return NpFactory::getInstance().getTriangleMeshes(userBuffer, bufferSize, startIndex); +} + +PxHeightField* NpPhysics::createHeightField(PxInputStream& stream) +{ + return NpFactory::getInstance().createHeightField(stream); +} + +PxU32 NpPhysics::getNbHeightFields() const +{ + return NpFactory::getInstance().getNbHeightFields(); +} + +PxU32 NpPhysics::getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return NpFactory::getInstance().getHeightFields(userBuffer, bufferSize, startIndex); +} + +/////////////////////////////////////////////////////////////////////////////// +PxConvexMesh* NpPhysics::createConvexMesh(PxInputStream& stream) +{ + return NpFactory::getInstance().createConvexMesh(stream); +} + +PxU32 NpPhysics::getNbConvexMeshes() const +{ + return NpFactory::getInstance().getNbConvexMeshes(); +} + +PxU32 NpPhysics::getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return NpFactory::getInstance().getConvexMeshes(userBuffer, bufferSize, startIndex); +} + +/////////////////////////////////////////////////////////////////////////////// +PxBVHStructure* NpPhysics::createBVHStructure(PxInputStream& stream) +{ + return NpFactory::getInstance().createBVHStructure(stream); +} + +PxU32 NpPhysics::getNbBVHStructures() const +{ + return NpFactory::getInstance().getNbBVHStructures(); +} + +PxU32 NpPhysics::getBVHStructures(PxBVHStructure** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return NpFactory::getInstance().getBVHStructures(userBuffer, bufferSize, startIndex); +} +/////////////////////////////////////////////////////////////////////////////// + +PxPruningStructure* NpPhysics::createPruningStructure(PxRigidActor*const* actors, PxU32 nbActors) +{ + PX_SIMD_GUARD; + + PX_ASSERT(actors); + PX_ASSERT(nbActors > 0); + + Sq::PruningStructure* ps = PX_NEW(Sq::PruningStructure)(); + if(!ps->build(actors, nbActors)) + { + PX_DELETE_AND_RESET(ps); + } + return ps; +} + +#if PX_SUPPORT_GPU_PHYSX +void NpPhysics::registerPhysXIndicatorGpuClient() +{ + Ps::Mutex::ScopedLock lock(mPhysXIndicatorMutex); + + ++mNbRegisteredGpuClients; + + mPhysXIndicator.setIsGpu(mNbRegisteredGpuClients>0); +} + +void NpPhysics::unregisterPhysXIndicatorGpuClient() +{ + Ps::Mutex::ScopedLock lock(mPhysXIndicatorMutex); + + if (mNbRegisteredGpuClients) + --mNbRegisteredGpuClients; + + mPhysXIndicator.setIsGpu(mNbRegisteredGpuClients>0); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +void NpPhysics::registerDeletionListener(PxDeletionListener& observer, const PxDeletionEventFlags& deletionEvents, bool restrictedObjectSet) +{ + Ps::Mutex::ScopedLock lock(mDeletionListenerMutex); + + const DeletionListenerMap::Entry* entry = mDeletionListenerMap.find(&observer); + if(!entry) + { + NpDelListenerEntry* e = PX_NEW(NpDelListenerEntry)(deletionEvents, restrictedObjectSet); + if (e) + { + if (mDeletionListenerMap.insert(&observer, e)) + mDeletionListenersExist = true; + else + { + PX_DELETE(e); + PX_ALWAYS_ASSERT(); + } + } + } + else + PX_ASSERT(mDeletionListenersExist); +} + +void NpPhysics::unregisterDeletionListener(PxDeletionListener& observer) +{ + Ps::Mutex::ScopedLock lock(mDeletionListenerMutex); + + const DeletionListenerMap::Entry* entry = mDeletionListenerMap.find(&observer); + if(entry) + { + NpDelListenerEntry* e = entry->second; + mDeletionListenerMap.erase(&observer); + PX_DELETE(e); + } + mDeletionListenersExist = mDeletionListenerMap.size()>0; +} + +void NpPhysics::registerDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount) +{ + Ps::Mutex::ScopedLock lock(mDeletionListenerMutex); + + const DeletionListenerMap::Entry* entry = mDeletionListenerMap.find(&observer); + if(entry) + { + NpDelListenerEntry* e = entry->second; + PX_CHECK_AND_RETURN(e->restrictedObjectSet, "PxPhysics::registerDeletionListenerObjects: deletion listener is not configured to receive events from specific objects."); + + e->registeredObjects.reserve(e->registeredObjects.size() + observableCount); + for(PxU32 i=0; i < observableCount; i++) + e->registeredObjects.insert(observables[i]); + } + else + { + PX_CHECK_AND_RETURN(false, "PxPhysics::registerDeletionListenerObjects: deletion listener has to be registered in PxPhysics first."); + } +} + +void NpPhysics::unregisterDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount) +{ + Ps::Mutex::ScopedLock lock(mDeletionListenerMutex); + + const DeletionListenerMap::Entry* entry = mDeletionListenerMap.find(&observer); + if(entry) + { + NpDelListenerEntry* e = entry->second; + if (e->restrictedObjectSet) + { + for(PxU32 i=0; i < observableCount; i++) + e->registeredObjects.erase(observables[i]); + } + else + { + PX_CHECK_AND_RETURN(false, "PxPhysics::unregisterDeletionListenerObjects: deletion listener is not configured to receive events from specific objects."); + } + } + else + { + PX_CHECK_AND_RETURN(false, "PxPhysics::unregisterDeletionListenerObjects: deletion listener has to be registered in PxPhysics first."); + } +} + +void NpPhysics::notifyDeletionListeners(const PxBase* base, void* userData, PxDeletionEventFlag::Enum deletionEvent) +{ + // we don't protect the check for whether there are any listeners, because we don't want to take a hit in the + // common case where there are no listeners. Note the API comments here, that users should not register or + // unregister deletion listeners while deletions are occurring + + if(mDeletionListenersExist) + { + Ps::Mutex::ScopedLock lock(mDeletionListenerMutex); + + const DeletionListenerMap::Entry* delListenerEntries = mDeletionListenerMap.getEntries(); + const PxU32 delListenerEntryCount = mDeletionListenerMap.size(); + for(PxU32 i=0; i < delListenerEntryCount; i++) + { + const NpDelListenerEntry* entry = delListenerEntries[i].second; + + if (entry->flags & deletionEvent) + { + if (entry->restrictedObjectSet) + { + if (entry->registeredObjects.contains(base)) + delListenerEntries[i].first->onRelease(base, userData, deletionEvent); + } + else + delListenerEntries[i].first->onRelease(base, userData, deletionEvent); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +const PxTolerancesScale& NpPhysics::getTolerancesScale() const +{ + return mPhysics.getTolerancesScale(); +} + +PxFoundation& NpPhysics::getFoundation() +{ + return Ps::Foundation::getInstance(); +} + +PxPhysics& PxGetPhysics() +{ + return NpPhysics::getInstance(); +} + +PxPhysics* PxCreateBasePhysics(PxU32 version, PxFoundation& foundation, const PxTolerancesScale& scale, bool trackOutstandingAllocations, PxPvd* pvd) +{ + return NpPhysics::createInstance(version, foundation, scale, trackOutstandingAllocations, static_cast(pvd)); +} + +void PxRegisterArticulations(PxPhysics& physics) +{ + PX_UNUSED(&physics); // for the moment + Dy::PxvRegisterArticulations(); + NpFactory::registerArticulations(); +} + +void PxRegisterArticulationsReducedCoordinate(PxPhysics& physics) +{ + PX_UNUSED(&physics); // for the moment + Dy::PxvRegisterArticulationsReducedCoordinate(); + NpFactory::registerArticulationRCs(); +} + +void PxRegisterHeightFields(PxPhysics& physics) +{ + PX_UNUSED(&physics); // for the moment + PX_CHECK_AND_RETURN(NpPhysics::getInstance().getNumScenes() == 0, "PxRegisterHeightFields: it is illegal to call a heightfield registration function after you have a scene."); + + PxvRegisterHeightFields(); + Gu::registerHeightFields(); +#if PX_CHECKED + NpPhysics::heightfieldsAreRegistered(); +#endif +} + +void PxAddCollectionToPhysics(const PxCollection& collection) +{ + NpFactory& factory = NpFactory::getInstance(); + const Cm::Collection& c = static_cast(collection); + factory.addCollection(c); +} diff --git a/src/PhysX/physx/source/physx/src/NpPhysics.h b/src/PhysX/physx/source/physx/src/NpPhysics.h new file mode 100644 index 000000000..084f3ce0d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPhysics.h @@ -0,0 +1,247 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_PHYSICS +#define PX_PHYSICS_NP_PHYSICS + +#include "PxPhysics.h" +#include "PsUserAllocated.h" +#include "GuMeshFactory.h" +#include "NpMaterial.h" +#include "NpPhysicsInsertionCallback.h" +#include "NpMaterialManager.h" +#include "ScPhysics.h" +#include "PsHashSet.h" +#include "PsHashMap.h" + +#ifdef LINUX +#include +#endif + +#if PX_SUPPORT_GPU_PHYSX +#include "device/PhysXIndicator.h" +#endif + +#include "PsPvd.h" + +namespace physx +{ +#if PX_SUPPORT_PVD +namespace Vd +{ + class PvdPhysicsClient; +} +#endif + struct NpMaterialIndexTranslator + { + NpMaterialIndexTranslator() : indicesNeedTranslation(false) {} + + Ps::HashMap map; + bool indicesNeedTranslation; + }; + + class NpScene; + struct PxvOffsetTable; + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4996) // We have to implement deprecated member functions, do not warn. +#endif + +class NpPhysics : public PxPhysics, public Ps::UserAllocated +{ + NpPhysics& operator=(const NpPhysics&); + NpPhysics(const NpPhysics &); + + struct NpDelListenerEntry : public UserAllocated + { + NpDelListenerEntry(const PxDeletionEventFlags& de, bool restrictedObjSet) + : flags(de) + , restrictedObjectSet(restrictedObjSet) + { + } + + Ps::HashSet registeredObjects; // specifically registered objects for deletion events + PxDeletionEventFlags flags; + bool restrictedObjectSet; + }; + + + NpPhysics( const PxTolerancesScale& scale, + const PxvOffsetTable& pxvOffsetTable, + bool trackOutstandingAllocations, + physx::pvdsdk::PsPvd* pvd); + virtual ~NpPhysics(); + +public: + + static NpPhysics* createInstance( PxU32 version, + PxFoundation& foundation, + const PxTolerancesScale& scale, + bool trackOutstandingAllocations, + physx::pvdsdk::PsPvd* pvd); + + static PxU32 releaseInstance(); + + static NpPhysics& getInstance() { return *mInstance; } + + virtual void release(); + + virtual PxScene* createScene(const PxSceneDesc&); + void releaseSceneInternal(PxScene&); + virtual PxU32 getNbScenes() const; + virtual PxU32 getScenes(PxScene** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxRigidStatic* createRigidStatic(const PxTransform&); + virtual PxRigidDynamic* createRigidDynamic(const PxTransform&); + virtual PxArticulation* createArticulation(); + virtual PxArticulationReducedCoordinate* createArticulationReducedCoordinate(); + virtual PxConstraint* createConstraint(PxRigidActor* actor0, PxRigidActor* actor1, PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize); + virtual PxAggregate* createAggregate(PxU32 maxSize, bool selfCollision); + + virtual PxShape* createShape(const PxGeometry&, PxMaterial*const *, PxU16, bool, PxShapeFlags shapeFlags); + virtual PxU32 getNbShapes() const; + virtual PxU32 getShapes(PxShape** userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + + virtual PxMaterial* createMaterial(PxReal staticFriction, PxReal dynamicFriction, PxReal restitution); + virtual PxU32 getNbMaterials() const; + virtual PxU32 getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxTriangleMesh* createTriangleMesh(PxInputStream&); + virtual PxU32 getNbTriangleMeshes() const; + virtual PxU32 getTriangleMeshes(PxTriangleMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxHeightField* createHeightField(PxInputStream& stream); + virtual PxU32 getNbHeightFields() const; + virtual PxU32 getHeightFields(PxHeightField** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxConvexMesh* createConvexMesh(PxInputStream&); + virtual PxU32 getNbConvexMeshes() const; + virtual PxU32 getConvexMeshes(PxConvexMesh** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual PxBVHStructure* createBVHStructure(PxInputStream&); + virtual PxU32 getNbBVHStructures() const; + virtual PxU32 getBVHStructures(PxBVHStructure** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + +#if PX_SUPPORT_GPU_PHYSX + void registerPhysXIndicatorGpuClient(); + void unregisterPhysXIndicatorGpuClient(); +#else + PX_FORCE_INLINE void registerPhysXIndicatorGpuClient() {} + PX_FORCE_INLINE void unregisterPhysXIndicatorGpuClient() {} +#endif + + virtual PxPruningStructure* createPruningStructure(PxRigidActor*const* actors, PxU32 nbActors); + + virtual const PxTolerancesScale& getTolerancesScale() const; + + virtual PxFoundation& getFoundation(); + + PX_INLINE NpScene* getScene(PxU32 i) const { return mSceneArray[i]; } + PX_INLINE PxU32 getNumScenes() const { return mSceneArray.size(); } +#if PX_CHECKED + static PX_INLINE void heightfieldsAreRegistered() { mHeightFieldsRegistered = true; } +#endif + + virtual void registerDeletionListener(PxDeletionListener& observer, const PxDeletionEventFlags& deletionEvents, bool restrictedObjectSet); + virtual void unregisterDeletionListener(PxDeletionListener& observer); + virtual void registerDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount); + virtual void unregisterDeletionListenerObjects(PxDeletionListener& observer, const PxBase* const* observables, PxU32 observableCount); + + void notifyDeletionListeners(const PxBase*, void* userData, PxDeletionEventFlag::Enum deletionEvent); + PX_FORCE_INLINE void notifyDeletionListenersUserRelease(const PxBase* b, void* userData) { notifyDeletionListeners(b, userData, PxDeletionEventFlag::eUSER_RELEASE); } + PX_FORCE_INLINE void notifyDeletionListenersMemRelease(const PxBase* b, void* userData) { notifyDeletionListeners(b, userData, PxDeletionEventFlag::eMEMORY_RELEASE); } + + virtual PxPhysicsInsertionCallback& getPhysicsInsertionCallback() { return mObjectInsertion; } + + void removeMaterialFromTable(NpMaterial&); + void updateMaterial(NpMaterial&); + bool sendMaterialTable(NpScene&); + + NpMaterialManager& getMaterialManager() { return mMasterMaterialManager; } + + NpMaterial* addMaterial(NpMaterial* np); + + static void initOffsetTables(PxvOffsetTable& pxvOffsetTable); + + static bool apiReentryLock; + +private: + typedef Ps::CoalescedHashMap DeletionListenerMap; + + Ps::Array mSceneArray; + + Sc::Physics mPhysics; + NpMaterialManager mMasterMaterialManager; + + NpPhysicsInsertionCallback mObjectInsertion; + + struct MeshDeletionListener: public GuMeshFactoryListener + { + void onGuMeshFactoryBufferRelease(const PxBase* object, PxType type) + { + PX_UNUSED(type); + NpPhysics::getInstance().notifyDeletionListeners(object, NULL, PxDeletionEventFlag::eMEMORY_RELEASE); + } + }; + + Ps::Mutex mDeletionListenerMutex; + DeletionListenerMap mDeletionListenerMap; + MeshDeletionListener mDeletionMeshListener; + bool mDeletionListenersExist; + + Ps::Mutex mSceneAndMaterialMutex; // guarantees thread safety for API calls related to scene and material containers + +#if PX_SUPPORT_GPU_PHYSX + PhysXIndicator mPhysXIndicator; + PxU32 mNbRegisteredGpuClients; + Ps::Mutex mPhysXIndicatorMutex; +#endif +#if PX_SUPPORT_PVD + physx::pvdsdk::PsPvd* mPvd; + Vd::PvdPhysicsClient* mPvdPhysicsClient; +#endif + + static PxU32 mRefCount; + static NpPhysics* mInstance; + +#if PX_CHECKED + static bool mHeightFieldsRegistered; //just for error checking +#endif + + friend class NpCollection; +}; + +#if PX_VC +#pragma warning(pop) +#endif +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h b/src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h new file mode 100644 index 000000000..aba7297f1 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h @@ -0,0 +1,72 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_PHYSICS_INSERTION_CALLBACK +#define PX_PHYSICS_NP_PHYSICS_INSERTION_CALLBACK + +#include "common/PxPhysicsInsertionCallback.h" +#include "GuTriangleMesh.h" +#include "GuHeightField.h" +#include "GuConvexMesh.h" +#include "NpFactory.h" +#include "PsFoundation.h" + +namespace physx +{ + class NpPhysicsInsertionCallback: public PxPhysicsInsertionCallback + { + public: + NpPhysicsInsertionCallback() {} + + virtual PxBase* buildObjectFromData(PxConcreteType::Enum type, void* data) + { + if(type == PxConcreteType::eTRIANGLE_MESH_BVH33 || type == PxConcreteType::eTRIANGLE_MESH_BVH34) + return NpFactory::getInstance().createTriangleMesh(data); + + if (type == PxConcreteType::eCONVEX_MESH) + return NpFactory::getInstance().createConvexMesh(data); + + if (type == PxConcreteType::eHEIGHTFIELD) + return NpFactory::getInstance().createHeightField(data); + + if (type == PxConcreteType::eBVH_STRUCTURE) + return NpFactory::getInstance().createBVHStructure(data); + + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Inserting object failed: " + "Object type not supported for buildObjectFromData."); + + return NULL; + } + + }; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h b/src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h new file mode 100644 index 000000000..91e66adc0 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h @@ -0,0 +1,105 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_PTRTABLESTORAGEMANAGER_H +#define PX_PHYSICS_NP_PTRTABLESTORAGEMANAGER_H + +#include "CmPhysXCommon.h" +#include "PsMutex.h" +#include "PsUserAllocated.h" +#include "CmPtrTable.h" +#include "PsBitUtils.h" + +namespace physx +{ +class NpPtrTableStorageManager : public Cm::PtrTableStorageManager, public Ps::UserAllocated +{ + PX_NOCOPY(NpPtrTableStorageManager) + +public: + + NpPtrTableStorageManager() {} + ~NpPtrTableStorageManager() {} + + void** allocate(PxU32 capacity) + { + PX_ASSERT(Ps::isPowerOfTwo(capacity)); + + Ps::Mutex::ScopedLock lock(mMutex); + + return capacity<=4*sizeof(void*) ? reinterpret_cast(mPool4.construct()) + : capacity<=16*sizeof(void*) ? reinterpret_cast(mPool16.construct()) + : capacity<=64*sizeof(void*) ? reinterpret_cast(mPool64.construct()) + : reinterpret_cast(PX_ALLOC(capacity*sizeof(void*), "CmPtrTable pointer array")); + } + + void deallocate(void** addr, PxU32 capacity) + { + PX_ASSERT(Ps::isPowerOfTwo(capacity)); + + Ps::Mutex::ScopedLock lock(mMutex); + + if(capacity<=4*sizeof(void*)) mPool4.destroy(reinterpret_cast< PtrBlock<4>*>(addr)); + else if(capacity<=16*sizeof(void*)) mPool16.destroy(reinterpret_cast< PtrBlock<16>*>(addr)); + else if(capacity<=64*sizeof(void*)) mPool64.destroy(reinterpret_cast< PtrBlock<64>*>(addr)); + else PX_FREE(addr); + } + + // originalCapacity is the only way we know which pool the alloc request belongs to, + // so if those are no longer going to match, we need to realloc. + + bool canReuse(PxU32 originalCapacity, PxU32 newCapacity) + { + PX_ASSERT(Ps::isPowerOfTwo(originalCapacity)); + PX_ASSERT(Ps::isPowerOfTwo(newCapacity)); + + return poolId(originalCapacity) == poolId(newCapacity) && newCapacity<=64; + } + +private: + Ps::Mutex mMutex; + + int poolId(PxU32 size) + { + return size<=4 ? 0 + : size<=16 ? 1 + : size<=64 ? 2 + : 3; + } + + template class PtrBlock { void* ptr[N]; }; + + Ps::Pool2, 4096 > mPool4; + Ps::Pool2, 4096 > mPool16; + Ps::Pool2, 4096 > mPool64; +}; + +} +#endif diff --git a/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp new file mode 100644 index 000000000..64de49aae --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp @@ -0,0 +1,182 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpScene.h" + +#if PX_SUPPORT_PVD +using namespace physx; +using namespace Sq; +using namespace Vd; + +static const char* gName_PvdRaycast[2] = { "SceneQueries.Raycasts", "BatchedQueries.Raycasts" }; +static const char* gName_PvdSweep[2] = { "SceneQueries.Sweeps", "BatchedQueries.Sweeps" }; +static const char* gName_PvdOverlap[2] = { "SceneQueries.Overlaps", "BatchedQueries.Overlaps" }; +static const char* gName_PvdSqHit[2] = { "SceneQueries.Hits", "BatchedQueries.Hits" }; +static const char* gName_PxTransform[2] = { "SceneQueries.PoseList", "BatchedQueries.PoseList" }; +static const char* gName_PxFilterData[2] = { "SceneQueries.FilterDataList", "BatchedQueries.FilterDataList" }; +static const char* gName_PxGeometryHolder[2] = { "SceneQueries.GeometryList", "BatchedQueries.GeometryList" }; + +PvdSceneQueryCollector::PvdSceneQueryCollector(Scb::Scene& scene, bool isBatched) : + mAccumulatedRaycastQueries (gName_PvdRaycast), + mAccumulatedSweepQueries (gName_PvdSweep), + mAccumulatedOverlapQueries (gName_PvdOverlap), + mPvdSqHits (gName_PvdSqHit), + mPoses (gName_PxTransform), + mFilterData (gName_PxFilterData), + mScene (scene), + mGeometries0 (gName_PxGeometryHolder), + mGeometries1 (gName_PxGeometryHolder), + mInUse (0), + mIsBatched (isBatched) +{ +} + +void PvdSceneQueryCollector::release() +{ + physx::pvdsdk::PvdDataStream* stream = mScene.getScenePvdClient().getDataStream(); + if(stream && stream->isConnected()) + { + const Ps::Array& geoms = getPrevFrameGeometries(); + for(PxU32 k=0; kdestroyInstance(&geoms[k]); + + clearGeometryArrays(); + } +} + +template +static void collectBatchedHits(const QueryResultT* results, Ps::Array& accumulated, Ps::Array& pvdSqHits, PxU32 nb, PxU32 startIdx, const char* arrayName) +{ + for(PxU32 i=0; i +static void accumulate(PvdHitType& query, Ps::Array& accumulated, const char* arrayName, Ps::Array& dst, const SDKHitType* src, PxU32 nb, const PxQueryFilterData& fd) +{ + query.mFilterFlags = fd.flags; + query.mHits = PvdReference(arrayName, dst.size(), nb); + + PX_ASSERT(PxU32(-1) != nb); + for(PxU32 i=0; i 0 ? 1u : 0; +} + +template static void pushBackT(Ps::Array& array, const Type& item, PvdReference& ref, const char* arrayName) +{ + ref = PvdReference(arrayName, array.size(), 1); + array.pushBack(item); +} + +void PvdSceneQueryCollector::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal distance, const PxRaycastHit* hit, PxU32 hitsNum, const PxQueryFilterData& fd, bool multipleHits) +{ + Ps::Mutex::ScopedLock lock(mMutex); + + PvdRaycast raycastQuery; + raycastQuery.mOrigin = origin; + raycastQuery.mUnitDir = unitDir; + raycastQuery.mDistance = distance; + raycastQuery.mFilterData = fd.data; + if(fd.flags & PxQueryFlag::eANY_HIT) raycastQuery.mType = QueryID::QUERY_RAYCAST_ANY_OBJECT; + else if(multipleHits) raycastQuery.mType = QueryID::QUERY_RAYCAST_ALL_OBJECTS; + else raycastQuery.mType = QueryID::QUERY_RAYCAST_CLOSEST_OBJECT; + clampNbHits(hitsNum, fd, multipleHits); + + accumulate(raycastQuery, mAccumulatedRaycastQueries, getArrayName(mPvdSqHits), mPvdSqHits, hit, hitsNum, fd); +} + +void PvdSceneQueryCollector::sweep(const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, PxReal distance, const PxSweepHit* hit, PxU32 hitsNum, const PxQueryFilterData& fd, bool multipleHits) +{ + Ps::Mutex::ScopedLock lock(mMutex); + + PvdSweep sweepQuery; + pushBackT(getGeometries(mInUse), PxGeometryHolder(geometry), sweepQuery.mGeometries, getArrayName(getGeometries(mInUse))); // PT: TODO: optimize this. We memcopy once to the stack, then again to the array.... + pushBackT(mPoses, pose, sweepQuery.mPoses, getArrayName(mPoses)); + pushBackT(mFilterData, fd.data, sweepQuery.mFilterData, getArrayName(mFilterData)); + + const PxGeometryType::Enum type = geometry.getType(); // PT: TODO: QueryID::QUERY_LINEAR_xxx_SWEEP_ALL_OBJECTS are never used! + if(type==PxGeometryType::eBOX) sweepQuery.mType = QueryID::QUERY_LINEAR_OBB_SWEEP_CLOSEST_OBJECT; + else if(type==PxGeometryType::eSPHERE || type==PxGeometryType::eCAPSULE) sweepQuery.mType = QueryID::QUERY_LINEAR_CAPSULE_SWEEP_CLOSEST_OBJECT; + else if(type==PxGeometryType::eCONVEXMESH) sweepQuery.mType = QueryID::QUERY_LINEAR_CONVEX_SWEEP_CLOSEST_OBJECT; + else PX_ASSERT(0); + sweepQuery.mUnitDir = unitDir; + sweepQuery.mDistance = distance; + clampNbHits(hitsNum, fd, multipleHits); + + accumulate(sweepQuery, mAccumulatedSweepQueries, getArrayName(mPvdSqHits), mPvdSqHits, hit, hitsNum, fd); +} + +void PvdSceneQueryCollector::overlapMultiple(const PxGeometry& geometry, const PxTransform& pose, const PxOverlapHit* hit, PxU32 hitsNum, const PxQueryFilterData& fd) +{ + Ps::Mutex::ScopedLock lock(mMutex); + + PvdOverlap overlapQuery; + pushBackT(getGeometries(mInUse), PxGeometryHolder(geometry), overlapQuery.mGeometries, getArrayName(getGeometries(mInUse))); // PT: TODO: optimize this. We memcopy once to the stack, then again to the array.... + + const PxGeometryType::Enum type = geometry.getType(); + if(type==PxGeometryType::eBOX) overlapQuery.mType = pose.q.isIdentity() ? QueryID::QUERY_OVERLAP_AABB_ALL_OBJECTS : QueryID::QUERY_OVERLAP_OBB_ALL_OBJECTS; + else if(type==PxGeometryType::eSPHERE) overlapQuery.mType = QueryID::QUERY_OVERLAP_SPHERE_ALL_OBJECTS; + else if(type==PxGeometryType::eCAPSULE) overlapQuery.mType = QueryID::QUERY_OVERLAP_CAPSULE_ALL_OBJECTS; + else if(type==PxGeometryType::eCONVEXMESH) overlapQuery.mType = QueryID::QUERY_OVERLAP_CONVEX_ALL_OBJECTS; + else PX_ASSERT(0); + overlapQuery.mPose = pose; + overlapQuery.mFilterData = fd.data; + + accumulate(overlapQuery, mAccumulatedOverlapQueries, getArrayName(mPvdSqHits), mPvdSqHits, hit, hitsNum, fd); +} +#endif diff --git a/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h new file mode 100644 index 000000000..3290c884d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h @@ -0,0 +1,231 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_PVD_SCENEQUERYCOLLECTOR_H +#define NP_PVD_SCENEQUERYCOLLECTOR_H + +#include "CmPhysXCommon.h" +#include "PsArray.h" +#include "PxFiltering.h" +#include "PxGeometryHelpers.h" +#include "PxQueryReport.h" +#include "PxBatchQueryDesc.h" + +#if PX_SUPPORT_PVD + +namespace physx +{ +namespace Scb +{ +class Scene; +} + +namespace Vd +{ +struct PvdReference +{ + PX_FORCE_INLINE PvdReference() {} + PX_FORCE_INLINE PvdReference(const char* arrayName, PxU32 baseIndex, PxU32 count) : mArrayName(arrayName), mBaseIndex(baseIndex), mCount(count) {} + + const char* mArrayName; + PxU32 mBaseIndex; + PxU32 mCount; +}; + +struct PvdRaycast +{ + PxU32 mType; + PxFilterData mFilterData; + PxU32 mFilterFlags; + PxVec3 mOrigin; + PxVec3 mUnitDir; + PxReal mDistance; + PvdReference mHits; +}; + +struct PvdOverlap +{ + PxU32 mType; + PxFilterData mFilterData; + PxU32 mFilterFlags; + PxTransform mPose; + PvdReference mGeometries; + PvdReference mHits; +}; + +struct PvdSweep +{ + PxU32 mType; + PxU32 mFilterFlags; + PxVec3 mUnitDir; + PxReal mDistance; + PvdReference mGeometries; + PvdReference mPoses; + PvdReference mFilterData; + PvdReference mHits; +}; + +struct PvdSqHit +{ + const void* mShape; + const void* mActor; + PxU32 mFaceIndex; + PxU32 mFlags; + PxVec3 mImpact; + PxVec3 mNormal; + PxF32 mDistance; + PxF32 mU; + PxF32 mV; + + PvdSqHit() + { + setDefaults(PxQueryHit()); + } + + explicit PvdSqHit(const PxOverlapHit& hit) + { + setDefaults(hit); + } + + explicit PvdSqHit(const PxRaycastHit& hit) + { + setDefaults(hit); + + mImpact = hit.position; + mNormal = hit.normal; + mDistance = hit.distance; + mFlags = hit.flags; + mU = hit.u; + mV = hit.v; + } + + explicit PvdSqHit(const PxSweepHit& hit) + { + setDefaults(hit); + + mImpact = hit.position; + mNormal = hit.normal; + mDistance = hit.distance; + mFlags = hit.flags; + } + + private: + void setDefaults(const PxQueryHit& hit) + { + mShape = hit.shape; + mActor = hit.actor; + mFaceIndex = hit.faceIndex; + mFlags = 0; + mImpact = mNormal = PxVec3(0.0f); + mDistance = mU = mV = 0.0f; + } +}; + +template +class NamedArray : public Ps::Array +{ + public: + NamedArray(const char* names[2]) { mNames[0] = names[0]; mNames[1] = names[1]; } + + const char* mNames[2]; +}; + +class PvdSceneQueryCollector +{ + PX_NOCOPY(PvdSceneQueryCollector) +public: + PvdSceneQueryCollector(Scb::Scene& scene, bool isBatched); + ~PvdSceneQueryCollector() {} + + void clear() + { + Ps::Mutex::ScopedLock lock(mMutex); + + mAccumulatedRaycastQueries.clear(); + mAccumulatedOverlapQueries.clear(); + mAccumulatedSweepQueries.clear(); + mPvdSqHits.clear(); + mPoses.clear(); + mFilterData.clear(); + } + + void clearGeometryArrays() + { + mGeometries0.clear(); + mGeometries1.clear(); + } + + void release(); + + void raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal distance, const PxRaycastHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData, bool multipleHits); + void sweep(const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, PxReal distance, const PxSweepHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData, bool multipleHits); + void overlapMultiple(const PxGeometry& geometry, const PxTransform& pose, const PxOverlapHit* hit, PxU32 hitsNum, const PxQueryFilterData& filterData); + + void collectAllBatchedHits (const PxRaycastQueryResult* raycastResults, PxU32 nbRaycastResults, PxU32 batchedRayQstartIdx, + const PxOverlapQueryResult* overlapResults, PxU32 nbOverlapResults, PxU32 batchedOverlapQstartIdx, + const PxSweepQueryResult* sweepResults, PxU32 nbSweepResults, PxU32 batchedSweepQstartIdx); + + PX_FORCE_INLINE Ps::Mutex& getLock() { return mMutex; } + + template + PX_FORCE_INLINE const char* getArrayName(const NamedArray& namedArray) const { return namedArray.mNames[mIsBatched]; } + + PX_FORCE_INLINE const NamedArray& getGeometries(PxU32 index) const { return index ? mGeometries1 : mGeometries0; } + PX_FORCE_INLINE NamedArray& getGeometries(PxU32 index) { return index ? mGeometries1 : mGeometries0; } + + PX_FORCE_INLINE const NamedArray& getCurrentFrameGeometries() const { return getGeometries(mInUse); } + PX_FORCE_INLINE const NamedArray& getPrevFrameGeometries() const { return getGeometries(mInUse ^ 1); } + + void prepareNextFrameGeometries() + { + mInUse ^= 1; + getGeometries(mInUse).clear(); + } + + NamedArray mAccumulatedRaycastQueries; + NamedArray mAccumulatedSweepQueries; + NamedArray mAccumulatedOverlapQueries; + NamedArray mPvdSqHits; + NamedArray mPoses; + NamedArray mFilterData; + +private: + Scb::Scene& mScene; + Ps::Mutex mMutex; + NamedArraymGeometries0; + NamedArraymGeometries1; + PxU32 mInUse; + const bool mIsBatched; +}; +} +} + +#endif // PX_SUPPORT_PVD + +#endif // NP_PVD_SCENEQUERYCOLLECTOR_H diff --git a/src/PhysX/physx/source/physx/src/NpQueryShared.h b/src/PhysX/physx/source/physx/src/NpQueryShared.h new file mode 100644 index 000000000..531cf17a7 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpQueryShared.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_QUERYSHARED +#define PX_PHYSICS_NP_QUERYSHARED + +#include "foundation/PxMemory.h" + +namespace physx +{ + +using namespace Cm; + +PX_FORCE_INLINE bool applyFilterEquation(const Scb::Shape& scbShape, const PxFilterData& queryFd) +{ + // if the filterData field is non-zero, and the bitwise-AND value of filterData AND the shape's + // queryFilterData is zero, the shape is skipped. + if(queryFd.word0 | queryFd.word1 | queryFd.word2 | queryFd.word3) + { + const PxFilterData& objFd = scbShape.getScShape().getQueryFilterData(); + const PxU32 keep = (queryFd.word0 & objFd.word0) | (queryFd.word1 & objFd.word1) | (queryFd.word2 & objFd.word2) | (queryFd.word3 & objFd.word3); + if(!keep) + return false; + } + return true; +} + +//======================================================================================================================== +// these partial template specializations are used to generalize the query code to be reused for all permutations of +// hit type=(raycast, overlap, sweep) x query type=(ANY, SINGLE, MULTIPLE) +template struct HitTypeSupport { enum { IsRaycast = 0, IsSweep = 0, IsOverlap = 0 }; }; +template <> struct HitTypeSupport +{ + enum { IsRaycast = 1, IsSweep = 0, IsOverlap = 0 }; + static PX_FORCE_INLINE PxReal getDistance(const PxQueryHit& hit) { return static_cast(hit).distance; } +}; +template <> struct HitTypeSupport +{ + enum { IsRaycast = 0, IsSweep = 1, IsOverlap = 0 }; + static PX_FORCE_INLINE PxReal getDistance(const PxQueryHit& hit) { return static_cast(hit).distance; } +}; +template <> struct HitTypeSupport +{ + enum { IsRaycast = 0, IsSweep = 0, IsOverlap = 1 }; + static PX_FORCE_INLINE PxReal getDistance(const PxQueryHit&) { return -1.0f; } +}; + +#define HITDIST(hit) HitTypeSupport::getDistance(hit) + +template +static PxU32 clipHitsToNewMaxDist(HitType* ppuHits, PxU32 count, PxReal newMaxDist) +{ + PxU32 i=0; + while(i!=count) + { + if(HITDIST(ppuHits[i]) > newMaxDist) + ppuHits[i] = ppuHits[--count]; + else + i++; + } + return count; +} + +} // namespace physx + +#endif // PX_PHYSICS_NP_QUERYSHARED diff --git a/src/PhysX/physx/source/physx/src/NpReadCheck.cpp b/src/PhysX/physx/source/physx/src/NpReadCheck.cpp new file mode 100644 index 000000000..3610823cf --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpReadCheck.cpp @@ -0,0 +1,83 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpReadCheck.h" + +#include "NpScene.h" + +using namespace physx; + +NpReadCheck::NpReadCheck(const NpScene* scene, const char* functionName) + : mScene(scene), mName(functionName), mErrorCount(0) +{ + if (mScene) + { + if (!mScene->startRead()) + { + if (mScene->getScene().getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "An API read call (%s) was made from thread %d but PxScene::lockRead() was not called first, note that " + "when PxSceneFlag::eREQUIRE_RW_LOCK is enabled all API reads and writes must be " + "wrapped in the appropriate locks.", mName, PxU32(Ps::Thread::getId())); + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Overlapping API read and write call detected during %s from thread %d! Note that read operations to " + "the SDK must not be overlapped with write calls, else the resulting behavior is undefined.", mName, PxU32(Ps::Thread::getId())); + } + } + + // Record the NpScene read/write error counter which is + // incremented any time a NpScene::startWrite/startRead fails + // (see destructor for additional error checking based on this count) + mErrorCount = mScene->getReadWriteErrorCount(); + } +} + + +NpReadCheck::~NpReadCheck() +{ + if (mScene) + { + // By checking if the NpScene::mConcurrentErrorCount has been incremented + // we can detect if an erroneous read/write was performed during + // this objects lifetime. In this case we also print this function's + // details so that the user can see which two API calls overlapped + if (mScene->getReadWriteErrorCount() != mErrorCount && !(mScene->getScene().getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Leaving %s on thread %d, an API overlapping write on another thread was detected.", mName, PxU32(Ps::Thread::getId())); + } + + mScene->stopRead(); + } +} diff --git a/src/PhysX/physx/source/physx/src/NpReadCheck.h b/src/PhysX/physx/source/physx/src/NpReadCheck.h new file mode 100644 index 000000000..a3303b905 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpReadCheck.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef NP_READ_CHECK_H +#define NP_READ_CHECK_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + +class NpScene; + +// RAII wrapper around the PxScene::startRead() method, note that this +// object does not acquire any scene locks, it is an error checking only mechanism +class NpReadCheck +{ +public: + NpReadCheck(const NpScene* scene, const char* functionName); + ~NpReadCheck(); +private: + const NpScene* mScene; + const char* mName; + PxU32 mErrorCount; +}; + +#if (PX_DEBUG || PX_CHECKED) + // Creates a scoped read check object that detects whether appropriate scene locks + // have been acquired and checks if reads/writes overlap, this macro should typically + // be placed at the beginning of any const API methods that are not multi-thread safe, + // the error conditions checked can be summarized as: + + // 1. PxSceneFlag::eREQUIRE_RW_LOCK was specified but PxScene::lockRead() was not yet called + // 2. Other threads were already writing, or began writing during the object lifetime + #define NP_READ_CHECK(npScenePtr) NpReadCheck npReadCheck(static_cast(npScenePtr), __FUNCTION__); +#else + #define NP_READ_CHECK(npScenePtr) +#endif + +} + +#endif // NP_Read_CHECK_H diff --git a/src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h b/src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h new file mode 100644 index 000000000..6cacd67cf --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h @@ -0,0 +1,452 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_RIGIDACTOR_TEMPLATE +#define PX_PHYSICS_NP_RIGIDACTOR_TEMPLATE + +#include "NpActorTemplate.h" +#include "NpShapeManager.h" +#include "NpConstraint.h" +#include "NpFactory.h" + +// PX_SERIALIZATION +#include "foundation/PxErrors.h" +//~PX_SERIALIZATION + +namespace physx +{ + +template +class NpRigidActorTemplate : public NpActorTemplate +{ +private: + typedef NpActorTemplate ActorTemplateClass; + +public: +// PX_SERIALIZATION + NpRigidActorTemplate(PxBaseFlags baseFlags) : ActorTemplateClass(baseFlags), mShapeManager(PxEmpty), mIndex(0xFFFFFFFF) {} + virtual void requiresObjects(PxProcessPxBaseCallback& c); + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); +//~PX_SERIALIZATION + virtual ~NpRigidActorTemplate(); + + //--------------------------------------------------------------------------------- + // PxActor implementation + //--------------------------------------------------------------------------------- + virtual void release(); + + // The rule is: If an API method is used somewhere in here, it has to be redeclared, else GCC whines + virtual PxActorType::Enum getType() const = 0; + + virtual PxBounds3 getWorldBounds(float inflation=1.01f) const; + + virtual void setActorFlag(PxActorFlag::Enum flag, bool value); + virtual void setActorFlags(PxActorFlags inFlags); + + //--------------------------------------------------------------------------------- + // PxRigidActor implementation + //--------------------------------------------------------------------------------- + + // Shapes + virtual PxU32 getNbShapes() const; + virtual PxU32 getShapes(PxShape** buffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + // Constraint shaders + virtual PxU32 getNbConstraints() const; + virtual PxU32 getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + // shared shapes + virtual bool attachShape(PxShape& s); + virtual void detachShape(PxShape& s, bool wakeOnLostTouch); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpRigidActorTemplate(PxType concreteType, PxBaseFlags baseFlags); + + // not optimal but the template alternative is hardly more readable and perf is not that critical here + virtual void switchToNoSim() { PX_ASSERT(false); } + virtual void switchFromNoSim() { PX_ASSERT(false); } + + void releaseShape(NpShape& s); + + PX_FORCE_INLINE NpShapeManager& getShapeManager() { return mShapeManager; } + PX_FORCE_INLINE const NpShapeManager& getShapeManager() const { return mShapeManager; } + + void updateShaderComs(); + + PX_FORCE_INLINE PxU32 getRigidActorArrayIndex() const { return mIndex; } + PX_FORCE_INLINE void setRigidActorArrayIndex(const PxU32& index) { mIndex = index; } + + bool resetFiltering(Scb::RigidObject& ro, PxShape*const* shapes, PxU32 shapeCount); + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene); +#endif +protected: + PX_FORCE_INLINE void setActorSimFlag(bool value); + + NpShapeManager mShapeManager; + PxU32 mIndex; // index for the NpScene rigid actor array +}; + +// PX_SERIALIZATION + +template +void NpRigidActorTemplate::requiresObjects(PxProcessPxBaseCallback& c) +{ + // export shapes + PxU32 nbShapes = mShapeManager.getNbShapes(); + for(PxU32 i=0;i +void NpRigidActorTemplate::exportExtraData(PxSerializationContext& stream) +{ + mShapeManager.exportExtraData(stream); + ActorTemplateClass::exportExtraData(stream); +} + +template +void NpRigidActorTemplate::importExtraData(PxDeserializationContext& context) +{ + mShapeManager.importExtraData(context); + ActorTemplateClass::importExtraData(context); +} + +template +void NpRigidActorTemplate::resolveReferences(PxDeserializationContext& context) +{ + const PxU32 nbShapes = mShapeManager.getNbShapes(); + NpShape** shapes = const_cast(mShapeManager.getShapes()); + for(PxU32 j=0;jonActorAttach(*this); + } + + ActorTemplateClass::resolveReferences(context); +} + +//~PX_SERIALIZATION + +template +NpRigidActorTemplate::NpRigidActorTemplate(PxType concreteType, PxBaseFlags baseFlags) +: ActorTemplateClass(concreteType, baseFlags, NULL, NULL) +{ +} + +template +NpRigidActorTemplate::~NpRigidActorTemplate() +{ + // TODO: no mechanism for notifying shaders of actor destruction yet +} + + +template +void NpRigidActorTemplate::release() +{ + NpActor::releaseConstraints(*this); + NpScene* scene = NpActor::getAPIScene(*this); + if(mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidActor::release: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + + mShapeManager.detachAll(scene, *this); + + ActorTemplateClass::release(); // PT: added for PxAggregate +} + + +template +void NpRigidActorTemplate::releaseShape(NpShape& s) +{ + // invalidate the pruning structure if the actor bounds changed + if (mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidActor::releaseShape: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + mShapeManager.detachShape(s, *this); +} + + +template +bool NpRigidActorTemplate::attachShape(PxShape& shape) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN_VAL(!static_cast(shape).isExclusive() || shape.getActor()==NULL, "PxRigidActor::attachShape: shape must be shared or unowned", false); + + PX_SIMD_GUARD + // invalidate the pruning structure if the actor bounds changed + if (mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidActor::attachShape: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + + mShapeManager.attachShape(static_cast(shape), *this); + return true; +} + +template +void NpRigidActorTemplate::detachShape(PxShape& shape, bool wakeOnLostTouch) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + if (mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidActor::detachShape: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + + if(!mShapeManager.detachShape(static_cast(shape), *this, wakeOnLostTouch)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidActor::detachShape: shape is not attached to this actor!"); + } +} + + +template +PxU32 NpRigidActorTemplate::getNbShapes() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mShapeManager.getNbShapes(); +} + + +template +PxU32 NpRigidActorTemplate::getShapes(PxShape** buffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mShapeManager.getShapes(buffer, bufferSize, startIndex); +} + +template +PxU32 NpRigidActorTemplate::getNbConstraints() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return ActorTemplateClass::getNbConnectors(NpConnectorType::eConstraint); +} + + +template +PxU32 NpRigidActorTemplate::getConstraints(PxConstraint** buffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return ActorTemplateClass::template getConnectors(NpConnectorType::eConstraint, buffer, bufferSize, startIndex); // Some people will love me for this one... The syntax is to be standard compliant and + // picky gcc won't compile without it. It is needed if you call a templated member function + // of a templated class +} + +template +PxBounds3 NpRigidActorTemplate::getWorldBounds(float inflation) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + PX_SIMD_GUARD; + + const PxBounds3 bounds = mShapeManager.getWorldBounds(*this); + PX_ASSERT(bounds.isValid()); + + // PT: unfortunately we can't just scale the min/max vectors, we need to go through center/extents. + const PxVec3 center = bounds.getCenter(); + const PxVec3 inflatedExtents = bounds.getExtents() * inflation; + return PxBounds3::centerExtents(center, inflatedExtents); +} + + +template +PX_FORCE_INLINE void NpRigidActorTemplate::setActorSimFlag(bool value) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + + PxActorFlags oldFlags = ActorTemplateClass::getActorFlags(); + bool hadNoSimFlag = oldFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + + PX_CHECK_AND_RETURN((getType() != PxActorType::eARTICULATION_LINK) || (!value && !hadNoSimFlag), "PxActor::setActorFlag: PxActorFlag::eDISABLE_SIMULATION is only supported by PxRigidDynamic and PxRigidStatic objects."); + + if (hadNoSimFlag && (!value)) + { + switchFromNoSim(); + ActorTemplateClass::setActorFlagsInternal(oldFlags & (~PxActorFlag::eDISABLE_SIMULATION)); // needs to be done before the code below to make sure the latest flags get picked up + if (scene) + NpActor::addConstraintsToScene(); + } + else if ((!hadNoSimFlag) && value) + { + if (scene) + NpActor::removeConstraintsFromScene(); + ActorTemplateClass::setActorFlagsInternal(oldFlags | PxActorFlag::eDISABLE_SIMULATION); + switchToNoSim(); + } +} + + +template +void NpRigidActorTemplate::setActorFlag(PxActorFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + if (flag == PxActorFlag::eDISABLE_SIMULATION) + setActorSimFlag(value); + + ActorTemplateClass::setActorFlagInternal(flag, value); +} + + +template +void NpRigidActorTemplate::setActorFlags(PxActorFlags inFlags) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + bool noSim = inFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + setActorSimFlag(noSim); + + ActorTemplateClass::setActorFlagsInternal(inFlags); +} + + +template +void NpRigidActorTemplate::updateShaderComs() +{ + NpConnectorIterator iter = ActorTemplateClass::getConnectorIterator(NpConnectorType::eConstraint); + while (PxBase* ser = iter.getNext()) + { + NpConstraint* c = static_cast(ser); + c->comShift(this); + } +} + + +PX_FORCE_INLINE static void fillResetFilteringShapeList(Scb::RigidObject& ro, Scb::Shape& scb, Scb::Shape** shapes, PxU32& shapeCount) +{ + // It is important to filter out shapes that have been added while the simulation was running because for those there is nothing to do. + + if (!ro.isBuffered(Scb::RigidObjectBuffer::BF_Shapes) || !ro.isAddedShape(scb)) + { + shapes[shapeCount] = &scb; + shapeCount++; + } +} + + +template +bool NpRigidActorTemplate::resetFiltering(Scb::RigidObject& ro, PxShape*const* shapes, PxU32 shapeCount) +{ + PX_CHECK_AND_RETURN_VAL(!(ro.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxScene::resetFiltering(): Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!", false); + for(PxU32 i=0; i < shapeCount; i++) + { +#if PX_CHECKED + PxRigidActor* ra = shapes[i]->getActor(); + if (ra != this) + { + bool found = false; + if (ra == NULL) + { + NpShape*const* sh = mShapeManager.getShapes(); + for(PxU32 j=0; j < mShapeManager.getNbShapes(); j++) + { + if (sh[j] == shapes[i]) + { + found = true; + break; + } + } + } + + PX_CHECK_AND_RETURN_VAL(found, "PxScene::resetFiltering(): specified shape not in actor!", false); + } +#endif + PX_CHECK_AND_RETURN_VAL(static_cast(shapes[i])->getScbShape().getFlags() & (PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE), "PxScene::resetFiltering(): specified shapes not of type eSIMULATION_SHAPE or eTRIGGER_SHAPE!", false); + } + + PxU32 sCount; + if (shapes) + sCount = shapeCount; + else + sCount = mShapeManager.getNbShapes(); + + PX_ALLOCA(scbShapes, Scb::Shape*, sCount); + if (scbShapes) + { + if (shapes) // the user specified the shapes + { + PxU32 sAccepted = 0; + for(PxU32 i=0; i < sCount; i++) + { + Scb::Shape& scb = static_cast(shapes[i])->getScbShape(); + fillResetFilteringShapeList(ro, scb, scbShapes, sAccepted); + } + sCount = sAccepted; + } + else // the user just specified the actor and the shapes are taken from the actor + { + NpShape* const* sh = mShapeManager.getShapes(); + PxU32 sAccepted = 0; + for(PxU32 i=0; i < sCount; i++) + { + Scb::Shape& scb = sh[i]->getScbShape(); + if (scb.getFlags() & (PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) + { + fillResetFilteringShapeList(ro, scb, scbShapes, sAccepted); + } + } + sCount = sAccepted; + } + + if (sCount) + { + ro.resetFiltering(scbShapes, sCount); + } + } + + return true; +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +template +void NpRigidActorTemplate::visualize(Cm::RenderOutput& out, NpScene* scene) +{ + mShapeManager.visualize(out, scene, *this); +} +#endif // PX_ENABLE_DEBUG_VISUALIZATION + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h b/src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h new file mode 100644 index 000000000..f53f334b5 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NP_RIGIDACTOR_TEMPLATE_INTERNAL +#define PX_PHYSICS_NP_RIGIDACTOR_TEMPLATE_INTERNAL + +namespace physx +{ + +template +static PX_FORCE_INLINE void releaseActorT(NpRigidActorTemplate* actor, T2& scbActor) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*actor)); + + NpPhysics::getInstance().notifyDeletionListenersUserRelease(actor, actor->userData); + + Scb::Scene* s = scbActor.getScbSceneForAPI(); + + const bool noSim = scbActor.isSimDisabledInternally(); + // important to check the non-buffered flag because it tells what the current internal state of the object is + // (someone might switch to non-simulation and release all while the sim is running). Reading is fine even if + // the sim is running because actor flags are read-only internally. + if(s && noSim) + { + // need to do it here because the Np-shape buffer will not be valid anymore after the release below + // and unlike simulation objects, there is no shape buffer in the simulation controller + actor->getShapeManager().clearShapesOnRelease(*s, *actor); + } + + actor->NpRigidActorTemplate::release(); + + if(s) + { + s->removeActor(scbActor, true, noSim); + static_cast(s->getPxScene())->removeFromRigidActorList(actor->getRigidActorArrayIndex()); + } + + scbActor.destroy(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h b/src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h new file mode 100644 index 000000000..dea694f13 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h @@ -0,0 +1,631 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_RIGIDBODY_TEMPLATE +#define PX_PHYSICS_NP_RIGIDBODY_TEMPLATE + +#include "NpRigidActorTemplate.h" +#include "ScbBody.h" +#include "NpPhysics.h" +#include "NpShape.h" +#include "NpScene.h" + +namespace physx +{ + +PX_INLINE PxVec3 invertDiagInertia(const PxVec3& m) +{ + return PxVec3( m.x == 0.0f ? 0.0f : 1.0f/m.x, + m.y == 0.0f ? 0.0f : 1.0f/m.y, + m.z == 0.0f ? 0.0f : 1.0f/m.z); +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +/* +given the diagonal of the body space inertia tensor, and the total mass +this returns the body space AABB width, height and depth of an equivalent box +*/ +PX_INLINE PxVec3 getDimsFromBodyInertia(const PxVec3& inertiaMoments, PxReal mass) +{ + const PxVec3 inertia = inertiaMoments * (6.0f/mass); + return PxVec3( PxSqrt(PxAbs(- inertia.x + inertia.y + inertia.z)), + PxSqrt(PxAbs(+ inertia.x - inertia.y + inertia.z)), + PxSqrt(PxAbs(+ inertia.x + inertia.y - inertia.z))); +} +#endif + + +template +class NpRigidBodyTemplate : public NpRigidActorTemplate +{ +private: + typedef NpRigidActorTemplate RigidActorTemplateClass; +public: +// PX_SERIALIZATION + NpRigidBodyTemplate(PxBaseFlags baseFlags) : RigidActorTemplateClass(baseFlags), mBody(PxEmpty) {} +//~PX_SERIALIZATION + virtual ~NpRigidBodyTemplate(); + + //--------------------------------------------------------------------------------- + // PxRigidActor implementation + //--------------------------------------------------------------------------------- + // The rule is: If an API method is used somewhere in here, it has to be redeclared, else GCC whines + virtual PxTransform getGlobalPose() const = 0; + + //--------------------------------------------------------------------------------- + // PxRigidBody implementation + //--------------------------------------------------------------------------------- + + // Center of mass pose + virtual PxTransform getCMassLocalPose() const; + + // Mass + virtual void setMass(PxReal mass); + virtual PxReal getMass() const; + virtual PxReal getInvMass() const; + + virtual void setMassSpaceInertiaTensor(const PxVec3& m); + virtual PxVec3 getMassSpaceInertiaTensor() const; + virtual PxVec3 getMassSpaceInvInertiaTensor() const; + + // Velocity + virtual PxVec3 getLinearVelocity() const; + virtual PxVec3 getAngularVelocity() const; + + virtual bool attachShape(PxShape& shape); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpRigidBodyTemplate(PxType concreteType, PxBaseFlags baseFlags, const PxActorType::Enum type, const PxTransform& bodyPose); + + PX_FORCE_INLINE const Scb::Body& getScbBodyFast() const { return mBody; } // PT: important: keep returning an address here (else update prefetch in SceneQueryManager::addShapes) + PX_FORCE_INLINE Scb::Body& getScbBodyFast() { return mBody; } // PT: important: keep returning an address here (else update prefetch in SceneQueryManager::addShapes) + + PX_FORCE_INLINE Scb::Actor& getScbActorFast() { return mBody; } + PX_FORCE_INLINE const Scb::Actor& getScbActorFast() const { return mBody; } + + // Flags + virtual void setRigidBodyFlag(PxRigidBodyFlag::Enum, bool value); + virtual void setRigidBodyFlags(PxRigidBodyFlags inFlags); + PX_FORCE_INLINE PxRigidBodyFlags getRigidBodyFlagsFast() const + { + return getScbBodyFast().getFlags(); + } + virtual PxRigidBodyFlags getRigidBodyFlags() const + { + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return getRigidBodyFlagsFast(); + } + + virtual void setMinCCDAdvanceCoefficient(PxReal advanceCoefficient); + + virtual PxReal getMinCCDAdvanceCoefficient() const; + + virtual void setMaxDepenetrationVelocity(PxReal maxDepenVel); + + virtual PxReal getMaxDepenetrationVelocity() const; + + virtual void setMaxContactImpulse(PxReal maxDepenVel); + + virtual PxReal getMaxContactImpulse() const; + + virtual PxU32 getInternalIslandNodeIndex() const; + +protected: + void setCMassLocalPoseInternal(const PxTransform&); + + void addSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode); + void clearSpatialForce(PxForceMode::Enum mode, bool force, bool torque); + void setSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode); + + PX_INLINE void updateBody2Actor(const PxTransform& newBody2Actor); + + PX_FORCE_INLINE void setRigidBodyFlagsInternal(const PxRigidBodyFlags& currentFlags, const PxRigidBodyFlags& newFlags); + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene); +#endif + + PX_FORCE_INLINE bool isKinematic() + { + return (APIClass::getConcreteType() == PxConcreteType::eRIGID_DYNAMIC) && (getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC); + } + +protected: + Scb::Body mBody; +}; + + +template +NpRigidBodyTemplate::NpRigidBodyTemplate(PxType concreteType, PxBaseFlags baseFlags, PxActorType::Enum type, const PxTransform& bodyPose) +: RigidActorTemplateClass(concreteType, baseFlags) +, mBody(type, bodyPose) +{ +} + + +template +NpRigidBodyTemplate::~NpRigidBodyTemplate() +{ +} + +namespace +{ + PX_FORCE_INLINE static bool isSimGeom(PxGeometryType::Enum t) + { + return t != PxGeometryType::eTRIANGLEMESH && t != PxGeometryType::ePLANE && t != PxGeometryType::eHEIGHTFIELD; + } +} + +template +bool NpRigidBodyTemplate::attachShape(PxShape& shape) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN_VAL(!(shape.getFlags() & PxShapeFlag::eSIMULATION_SHAPE) + || isSimGeom(shape.getGeometryType()) + || isKinematic(), + "attachShape: Triangle mesh, heightfield or plane geometry shapes configured as eSIMULATION_SHAPE are not supported for non-kinematic PxRigidDynamic instances.", false); + return RigidActorTemplateClass::attachShape(shape); +} + + +template +void NpRigidBodyTemplate::setCMassLocalPoseInternal(const PxTransform& body2Actor) +{ + //the point here is to change the mass distribution w/o changing the actors' pose in the world + + PxTransform newBody2World = getGlobalPose() * body2Actor; + + mBody.setBody2World(newBody2World, true); + mBody.setBody2Actor(body2Actor); + + RigidActorTemplateClass::updateShaderComs(); +} + + +template +PxTransform NpRigidBodyTemplate::getCMassLocalPose() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getBody2Actor(); +} + + +template +void NpRigidBodyTemplate::setMass(PxReal mass) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(mass), "PxRigidDynamic::setMass: invalid float"); + PX_CHECK_AND_RETURN(mass>=0, "PxRigidDynamic::setMass: mass must be non-negative!"); + PX_CHECK_AND_RETURN(this->getType() != PxActorType::eARTICULATION_LINK || mass > 0.0f, "PxRigidDynamic::setMassSpaceInertiaTensor: components must be > 0 for articualtions"); + + getScbBodyFast().setInverseMass(mass > 0.0f ? 1.0f/mass : 0.0f); +} + + +template +PxReal NpRigidBodyTemplate::getMass() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + const PxReal invMass = mBody.getInverseMass(); + + return invMass > 0.0f ? 1.0f/invMass : 0.0f; +} + +template +PxReal NpRigidBodyTemplate::getInvMass() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return mBody.getInverseMass(); +} + + +template +void NpRigidBodyTemplate::setMassSpaceInertiaTensor(const PxVec3& m) +{ + PX_CHECK_AND_RETURN(m.isFinite(), "PxRigidDynamic::setMassSpaceInertiaTensor: invalid inertia"); + PX_CHECK_AND_RETURN(m.x>=0.0f && m.y>=0.0f && m.z>=0.0f, "PxRigidDynamic::setMassSpaceInertiaTensor: components must be non-negative"); + PX_CHECK_AND_RETURN(this->getType() != PxActorType::eARTICULATION_LINK || (m.x > 0.0f && m.y > 0.0f && m.z > 0.0f), "PxRigidDynamic::setMassSpaceInertiaTensor: components must be > 0 for articualtions"); + + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + mBody.setInverseInertia(invertDiagInertia(m)); +} + + +template +PxVec3 NpRigidBodyTemplate::getMassSpaceInertiaTensor() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return invertDiagInertia(mBody.getInverseInertia()); +} + +template +PxVec3 NpRigidBodyTemplate::getMassSpaceInvInertiaTensor() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return mBody.getInverseInertia(); +} + + +template +PxVec3 NpRigidBodyTemplate::getLinearVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return mBody.getLinearVelocity(); +} + + +template +PxVec3 NpRigidBodyTemplate::getAngularVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return mBody.getAngularVelocity(); +} + + +template +void NpRigidBodyTemplate::addSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode) +{ + PX_ASSERT(!(mBody.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + switch (mode) + { + case PxForceMode::eFORCE: + { + PxVec3 linAcc, angAcc; + if (force) + { + linAcc = (*force) * mBody.getInverseMass(); + force = &linAcc; + } + if (torque) + { + angAcc = mBody.getGlobalInertiaTensorInverse() * (*torque); + torque = &angAcc; + } + mBody.addSpatialAcceleration(force, torque); + } + break; + + case PxForceMode::eACCELERATION: + mBody.addSpatialAcceleration(force, torque); + break; + + case PxForceMode::eIMPULSE: + { + PxVec3 linVelDelta, angVelDelta; + if (force) + { + linVelDelta = ((*force) * mBody.getInverseMass()); + force = &linVelDelta; + } + if (torque) + { + angVelDelta = (mBody.getGlobalInertiaTensorInverse() * (*torque)); + torque = &angVelDelta; + } + mBody.addSpatialVelocity(force, torque); + } + break; + + case PxForceMode::eVELOCITY_CHANGE: + mBody.addSpatialVelocity(force, torque); + break; + } +} + +template +void NpRigidBodyTemplate::setSpatialForce(const PxVec3* force, const PxVec3* torque, PxForceMode::Enum mode) +{ + PX_ASSERT(!(mBody.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + switch (mode) + { + case PxForceMode::eFORCE: + { + PxVec3 linAcc, angAcc; + if (force) + { + linAcc = (*force) * mBody.getInverseMass(); + force = &linAcc; + } + if (torque) + { + angAcc = mBody.getGlobalInertiaTensorInverse() * (*torque); + torque = &angAcc; + } + mBody.setSpatialAcceleration(force, torque); + } + break; + + case PxForceMode::eACCELERATION: + mBody.setSpatialAcceleration(force, torque); + break; + + case PxForceMode::eIMPULSE: + { + PxVec3 linVelDelta, angVelDelta; + if (force) + { + linVelDelta = ((*force) * mBody.getInverseMass()); + force = &linVelDelta; + } + if (torque) + { + angVelDelta = (mBody.getGlobalInertiaTensorInverse() * (*torque)); + torque = &angVelDelta; + } + mBody.addSpatialVelocity(force, torque); + } + break; + + case PxForceMode::eVELOCITY_CHANGE: + mBody.addSpatialVelocity(force, torque); + break; + } +} + +template +void NpRigidBodyTemplate::clearSpatialForce(PxForceMode::Enum mode, bool force, bool torque) +{ + PX_ASSERT(!(mBody.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + switch (mode) + { + case PxForceMode::eFORCE: + case PxForceMode::eACCELERATION: + mBody.clearSpatialAcceleration(force, torque); + break; + case PxForceMode::eIMPULSE: + case PxForceMode::eVELOCITY_CHANGE: + mBody.clearSpatialVelocity(force, torque); + break; + } +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +template +void NpRigidBodyTemplate::visualize(Cm::RenderOutput& out, NpScene* scene) +{ + RigidActorTemplateClass::visualize(out, scene); + + if (mBody.getActorFlags() & PxActorFlag::eVISUALIZATION) + { + Scb::Scene& scbScene = scene->getScene(); + const PxReal scale = scbScene.getVisualizationParameter(PxVisualizationParameter::eSCALE); + + //visualize actor frames + const PxReal actorAxes = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eACTOR_AXES); + if (actorAxes != 0.0f) + out << getGlobalPose() << Cm::DebugBasis(PxVec3(actorAxes)); + + const PxReal bodyAxes = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eBODY_AXES); + if (bodyAxes != 0.0f) + out << mBody.getBody2World() << Cm::DebugBasis(PxVec3(bodyAxes)); + + const PxReal linVelocity = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eBODY_LIN_VELOCITY); + if (linVelocity != 0.0f) + { + out << 0xffffff << PxMat44(PxIdentity) << Cm::DebugArrow(mBody.getBody2World().p, + mBody.getLinearVelocity() * linVelocity, 0.2f * linVelocity); + } + + const PxReal angVelocity = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eBODY_ANG_VELOCITY); + if (angVelocity != 0.0f) + { + out << 0x000000 << PxMat44(PxIdentity) << Cm::DebugArrow(mBody.getBody2World().p, + mBody.getAngularVelocity() * angVelocity, 0.2f * angVelocity); + } + } +} +#endif + + +PX_FORCE_INLINE void updateDynamicSceneQueryShapes(NpShapeManager& shapeManager, Sq::SceneQueryManager& sqManager, const PxRigidActor& actor) +{ + shapeManager.markAllSceneQueryForUpdate(sqManager, actor); + sqManager.get(Sq::PruningIndex::eDYNAMIC).invalidateTimestamp(); +} + + +template +PX_FORCE_INLINE void NpRigidBodyTemplate::setRigidBodyFlagsInternal(const PxRigidBodyFlags& currentFlags, const PxRigidBodyFlags& newFlags) +{ + PxRigidBodyFlags filteredNewFlags = newFlags; + //Test to ensure we are not enabling both CCD and kinematic state on a body. This is unsupported + if((filteredNewFlags & PxRigidBodyFlag::eENABLE_CCD) && (filteredNewFlags & PxRigidBodyFlag::eKINEMATIC)) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "RigidBody::setRigidBodyFlag: kinematic bodies with CCD enabled are not supported! CCD will be ignored."); + filteredNewFlags &= PxRigidBodyFlags(~PxRigidBodyFlag::eENABLE_CCD); + } + + if ((filteredNewFlags & PxRigidBodyFlag::eENABLE_CCD) && (filteredNewFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD)) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "RigidBody::setRigidBodyFlag: eENABLE_CCD can't be raised as the same time as eENABLE_SPECULATIVE_CCD! eENABLE_SPECULATIVE_CCD will be ignored."); + filteredNewFlags &= PxRigidBodyFlags(~PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD); + } + + Scb::Body& body = getScbBodyFast(); + NpScene* scene = NpActor::getAPIScene(*this); + + const bool isKinematic = currentFlags & PxRigidBodyFlag::eKINEMATIC; + const bool willBeKinematic = filteredNewFlags & PxRigidBodyFlag::eKINEMATIC; + const bool kinematicSwitchingToDynamic = isKinematic && (!willBeKinematic); + const bool dynamicSwitchingToKinematic = (!isKinematic) && willBeKinematic; + + if(kinematicSwitchingToDynamic) + { + NpShapeManager& shapeManager = this->getShapeManager(); + PxU32 nbShapes = shapeManager.getNbShapes(); + NpShape*const* shapes = shapeManager.getShapes(); + bool hasTriangleMesh = false; + for(PxU32 i=0;igetFlags() & PxShapeFlag::eSIMULATION_SHAPE) && (shapes[i]->getGeometryTypeFast()==PxGeometryType::eTRIANGLEMESH || shapes[i]->getGeometryTypeFast()==PxGeometryType::ePLANE || shapes[i]->getGeometryTypeFast()==PxGeometryType::eHEIGHTFIELD)) + { + hasTriangleMesh = true; + break; + } + } + if(hasTriangleMesh) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "RigidBody::setRigidBodyFlag: dynamic meshes/planes/heightfields are not supported!"); + return; + } + + PxTransform bodyTarget; + if ((currentFlags & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES) && body.getKinematicTarget(bodyTarget) && scene) + { + updateDynamicSceneQueryShapes(shapeManager, scene->getSceneQueryManagerFast(), *this); + } + + body.clearSimStateDataForPendingInsert(); + } + else if (dynamicSwitchingToKinematic) + { + if (this->getType() != PxActorType::eARTICULATION_LINK) + { + body.transitionSimStateDataForPendingInsert(); + } + else + { + //We're an articulation, raise an issue + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "RigidBody::setRigidBodyFlag: kinematic articulation links are not supported!"); + return; + } + } + + const bool kinematicSwitchingUseTargetForSceneQuery = isKinematic && willBeKinematic && + ((currentFlags & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES) != (filteredNewFlags & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES)); + if (kinematicSwitchingUseTargetForSceneQuery) + { + PxTransform bodyTarget; + if (body.getKinematicTarget(bodyTarget) && scene) + { + updateDynamicSceneQueryShapes(this->getShapeManager(), scene->getSceneQueryManagerFast(), *this); + } + } + + body.setFlags(filteredNewFlags); +} + + +template +void NpRigidBodyTemplate::setRigidBodyFlag(PxRigidBodyFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + Scb::Body& body = getScbBodyFast(); + const PxRigidBodyFlags currentFlags = body.getFlags(); + const PxRigidBodyFlags newFlags = value ? currentFlags | flag : currentFlags & (~PxRigidBodyFlags(flag)); + + setRigidBodyFlagsInternal(currentFlags, newFlags); +} + + +template +void NpRigidBodyTemplate::setRigidBodyFlags(PxRigidBodyFlags inFlags) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + Scb::Body& body = getScbBodyFast(); + const PxRigidBodyFlags currentFlags = body.getFlags(); + + setRigidBodyFlagsInternal(currentFlags, inFlags); +} + + +template +void NpRigidBodyTemplate::setMinCCDAdvanceCoefficient(PxReal minCCDAdvanceCoefficient) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + mBody.setMinCCDAdvanceCoefficient(minCCDAdvanceCoefficient); +} + +template +PxReal NpRigidBodyTemplate::getMinCCDAdvanceCoefficient() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mBody.getMinCCDAdvanceCoefficient(); +} + +template +void NpRigidBodyTemplate::setMaxDepenetrationVelocity(PxReal maxDepenVel) +{ + PX_CHECK_AND_RETURN(maxDepenVel > 0.0f, "PxRigidDynamic::setMaxDepenetrationVelocity: maxDepenVel must be greater than zero."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + mBody.setMaxPenetrationBias(-maxDepenVel); +} + +template +PxReal NpRigidBodyTemplate::getMaxDepenetrationVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return -mBody.getMaxPenetrationBias(); +} + +template +void NpRigidBodyTemplate::setMaxContactImpulse(const PxReal maxImpulse) +{ + PX_CHECK_AND_RETURN(maxImpulse >= 0.f, "NpRigidBody::setMaxImpulse: impulse limit must be greater than or equal to zero."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + mBody.setMaxContactImpulse(maxImpulse); +} + +template +PxReal NpRigidBodyTemplate::getMaxContactImpulse() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mBody.getMaxContactImpulse(); +} + +template +PxU32 NpRigidBodyTemplate::getInternalIslandNodeIndex() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mBody.getInternalIslandNodeIndex(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp b/src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp new file mode 100644 index 000000000..f0c4573b9 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp @@ -0,0 +1,566 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpRigidDynamic.h" +#include "NpRigidActorTemplateInternal.h" + +using namespace physx; + +NpRigidDynamic::NpRigidDynamic(const PxTransform& bodyPose) +: NpRigidDynamicT(PxConcreteType::eRIGID_DYNAMIC, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, PxActorType::eRIGID_DYNAMIC, bodyPose) +{} + +NpRigidDynamic::~NpRigidDynamic() +{ +} + +// PX_SERIALIZATION +void NpRigidDynamic::requiresObjects(PxProcessPxBaseCallback& c) +{ + NpRigidDynamicT::requiresObjects(c); +} + +NpRigidDynamic* NpRigidDynamic::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpRigidDynamic* obj = new (address) NpRigidDynamic(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(NpRigidDynamic); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +void NpRigidDynamic::release() +{ + releaseActorT(this, mBody); +} + + +void NpRigidDynamic::setGlobalPose(const PxTransform& pose, bool autowake) +{ + NpScene* scene = NpActor::getAPIScene(*this); + +#if PX_CHECKED + if(scene) + scene->checkPositionSanity(*this, pose, "PxRigidDynamic::setGlobalPose"); +#endif + + PX_CHECK_AND_RETURN(pose.isSane(), "PxRigidDynamic::setGlobalPose: pose is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + const PxTransform newPose = pose.getNormalized(); //AM: added to fix 1461 where users read and write orientations for no reason. + + Scb::Body& b = getScbBodyFast(); + const PxTransform body2World = newPose * b.getBody2Actor(); + b.setBody2World(body2World, false); + + if(scene) + updateDynamicSceneQueryShapes(mShapeManager, scene->getSceneQueryManagerFast(), *this); + + // invalidate the pruning structure if the actor bounds changed + if(mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidDynamic::setGlobalPose: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + + if(scene && autowake && !(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + wakeUpInternal(); +} + + +PX_FORCE_INLINE void NpRigidDynamic::setKinematicTargetInternal(const PxTransform& targetPose) +{ + Scb::Body& b = getScbBodyFast(); + + // The target is actor related. Transform to body related target + const PxTransform bodyTarget = targetPose * b.getBody2Actor(); + + b.setKinematicTarget(bodyTarget); + + NpScene* scene = NpActor::getAPIScene(*this); + if ((b.getFlags() & PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES) && scene) + { + updateDynamicSceneQueryShapes(mShapeManager, scene->getSceneQueryManagerFast(), *this); + } +} + + +void NpRigidDynamic::setKinematicTarget(const PxTransform& destination) +{ + PX_CHECK_AND_RETURN(destination.isSane(), "PxRigidDynamic::setKinematicTarget: destination is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + +#if PX_CHECKED + NpScene* scene = NpActor::getAPIScene(*this); + if(scene) + scene->checkPositionSanity(*this, destination, "PxRigidDynamic::setKinematicTarget"); + + Scb::Body& b = getScbBodyFast(); + PX_CHECK_AND_RETURN((b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::setKinematicTarget: Body must be kinematic!"); + PX_CHECK_AND_RETURN(scene, "PxRigidDynamic::setKinematicTarget: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::setKinematicTarget: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); +#endif + + setKinematicTargetInternal(destination.getNormalized()); +} + + +bool NpRigidDynamic::getKinematicTarget(PxTransform& target) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + const Scb::Body& b = getScbBodyFast(); + if(b.getFlags() & PxRigidBodyFlag::eKINEMATIC) + { + PxTransform bodyTarget; + if(b.getKinematicTarget(bodyTarget)) + { + // The internal target is body related. Transform to actor related target + target = bodyTarget * b.getBody2Actor().getInverse(); + return true; + } + } + return false; +} + +void NpRigidDynamic::setCMassLocalPose(const PxTransform& pose) +{ + PX_CHECK_AND_RETURN(pose.isSane(), "PxRigidDynamic::setCMassLocalPose pose is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + const PxTransform p = pose.getNormalized(); + + const PxTransform oldBody2Actor = getScbBodyFast().getBody2Actor(); + + NpRigidDynamicT::setCMassLocalPoseInternal(p); + + Scb::Body& b = getScbBodyFast(); + if(b.getFlags() & PxRigidBodyFlag::eKINEMATIC) + { + PxTransform bodyTarget; + if(b.getKinematicTarget(bodyTarget)) + { + PxTransform actorTarget = bodyTarget * oldBody2Actor.getInverse(); // get old target pose for the actor from the body target + setKinematicTargetInternal(actorTarget); + } + } +} + + +void NpRigidDynamic::setLinearDamping(PxReal linearDamping) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(linearDamping), "PxRigidDynamic::setLinearDamping: invalid float"); + PX_CHECK_AND_RETURN(linearDamping >=0, "PxRigidDynamic::setLinearDamping: The linear damping must be nonnegative!"); + + getScbBodyFast().setLinearDamping(linearDamping); +} + + +PxReal NpRigidDynamic::getLinearDamping() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getLinearDamping(); +} + + +void NpRigidDynamic::setAngularDamping(PxReal angularDamping) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(angularDamping), "PxRigidDynamic::setAngularDamping: invalid float"); + PX_CHECK_AND_RETURN(angularDamping>=0, "PxRigidDynamic::setAngularDamping: The angular damping must be nonnegative!") + + getScbBodyFast().setAngularDamping(angularDamping); +} + + +PxReal NpRigidDynamic::getAngularDamping() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getAngularDamping(); +} + + +void NpRigidDynamic::setLinearVelocity(const PxVec3& velocity, bool autowake) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(velocity.isFinite(), "PxRigidDynamic::setLinearVelocity: velocity is not valid."); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::setLinearVelocity: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::setLinearVelocity: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + Scb::Body& b = getScbBodyFast(); + b.setLinearVelocity(velocity); + + NpScene* scene = NpActor::getAPIScene(*this); + if(scene) + wakeUpInternalNoKinematicTest(b, (!velocity.isZero()), autowake); +} + + +void NpRigidDynamic::setAngularVelocity(const PxVec3& velocity, bool autowake) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(velocity.isFinite(), "PxRigidDynamic::setAngularVelocity: velocity is not valid."); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::setAngularVelocity: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::setAngularVelocity: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + Scb::Body& b = getScbBodyFast(); + b.setAngularVelocity(velocity); + + NpScene* scene = NpActor::getAPIScene(*this); + if(scene) + wakeUpInternalNoKinematicTest(b, (!velocity.isZero()), autowake); +} + + +void NpRigidDynamic::setMaxAngularVelocity(PxReal maxAngularVelocity) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(maxAngularVelocity), "PxRigidDynamic::setMaxAngularVelocity: invalid float"); + PX_CHECK_AND_RETURN(maxAngularVelocity>=0.0f, "PxRigidDynamic::setMaxAngularVelocity: threshold must be non-negative!"); + + getScbBodyFast().setMaxAngVelSq(maxAngularVelocity * maxAngularVelocity); +} + + +PxReal NpRigidDynamic::getMaxAngularVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return PxSqrt(getScbBodyFast().getMaxAngVelSq()); +} + +void NpRigidDynamic::setMaxLinearVelocity(PxReal maxLinearVelocity) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(maxLinearVelocity), "PxRigidDynamic::setMaxAngularVelocity: invalid float"); + PX_CHECK_AND_RETURN(maxLinearVelocity >= 0.0f, "PxRigidDynamic::setMaxAngularVelocity: threshold must be non-negative!"); + + getScbBodyFast().setMaxLinVelSq(maxLinearVelocity * maxLinearVelocity); +} + +PxReal NpRigidDynamic::getMaxLinearVelocity() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return PxSqrt(getScbBodyFast().getMaxLinVelSq()); +} + + +void NpRigidDynamic::addForce(const PxVec3& force, PxForceMode::Enum mode, bool autowake) +{ + Scb::Body& b = getScbBodyFast(); + + PX_CHECK_AND_RETURN(force.isFinite(), "PxRigidDynamic::addForce: force is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::addForce: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::addForce: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::addForce: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + addSpatialForce(&force, NULL, mode); + + wakeUpInternalNoKinematicTest(b, (!force.isZero()), autowake); +} + +void NpRigidDynamic::setForceAndTorque(const PxVec3& force, const PxVec3& torque, PxForceMode::Enum mode) +{ + Scb::Body& b = getScbBodyFast(); + + PX_CHECK_AND_RETURN(force.isFinite(), "PxRigidDynamic::setForce: force is not valid."); + PX_CHECK_AND_RETURN(torque.isFinite(), "PxRigidDynamic::setForce: force is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::addForce: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::addForce: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::addForce: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + setSpatialForce(&force, &torque, mode); + + wakeUpInternalNoKinematicTest(b, (!force.isZero()), true); +} + + +void NpRigidDynamic::addTorque(const PxVec3& torque, PxForceMode::Enum mode, bool autowake) +{ + Scb::Body& b = getScbBodyFast(); + + PX_CHECK_AND_RETURN(torque.isFinite(), "PxRigidDynamic::addTorque: torque is not valid."); + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::addTorque: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::addTorque: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::addTorque: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + addSpatialForce(NULL, &torque, mode); + + wakeUpInternalNoKinematicTest(b, (!torque.isZero()), autowake); +} + +void NpRigidDynamic::clearForce(PxForceMode::Enum mode) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::clearForce: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::clearForce: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::clearForce: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + clearSpatialForce(mode, true, false); +} + + +void NpRigidDynamic::clearTorque(PxForceMode::Enum mode) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::clearTorque: Body must be in a scene!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::clearTorque: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::clearTorque: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + clearSpatialForce(mode, false, true); +} + + +bool NpRigidDynamic::isSleeping() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN_VAL(NpActor::getAPIScene(*this), "PxRigidDynamic::isSleeping: Body must be in a scene.", true); + + return getScbBodyFast().isSleeping(); +} + + +void NpRigidDynamic::setSleepThreshold(PxReal threshold) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(threshold), "PxRigidDynamic::setSleepThreshold: invalid float."); + PX_CHECK_AND_RETURN(threshold>=0.0f, "PxRigidDynamic::setSleepThreshold: threshold must be non-negative!"); + + getScbBodyFast().setSleepThreshold(threshold); +} + + +PxReal NpRigidDynamic::getSleepThreshold() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getSleepThreshold(); +} + +void NpRigidDynamic::setStabilizationThreshold(PxReal threshold) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(threshold), "PxRigidDynamic::setSleepThreshold: invalid float."); + PX_CHECK_AND_RETURN(threshold>=0.0f, "PxRigidDynamic::setSleepThreshold: threshold must be non-negative!"); + + getScbBodyFast().setFreezeThreshold(threshold); +} + + +PxReal NpRigidDynamic::getStabilizationThreshold() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getFreezeThreshold(); +} + + +void NpRigidDynamic::setWakeCounter(PxReal wakeCounterValue) +{ + Scb::Body& b = getScbBodyFast(); + + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(wakeCounterValue), "PxRigidDynamic::setWakeCounter: invalid float."); + PX_CHECK_AND_RETURN(wakeCounterValue>=0.0f, "PxRigidDynamic::setWakeCounter: wakeCounterValue must be non-negative!"); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::setWakeCounter: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::setWakeCounter: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + b.setWakeCounter(wakeCounterValue); +} + + +PxReal NpRigidDynamic::getWakeCounter() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getWakeCounter(); +} + + +void NpRigidDynamic::wakeUp() +{ + Scb::Body& b = getScbBodyFast(); + + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::wakeUp: Body must be in a scene."); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::wakeUp: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::wakeUp: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + b.wakeUp(); +} + + +void NpRigidDynamic::putToSleep() +{ + Scb::Body& b = getScbBodyFast(); + + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(*this), "PxRigidDynamic::putToSleep: Body must be in a scene."); + PX_CHECK_AND_RETURN(!(b.getFlags() & PxRigidBodyFlag::eKINEMATIC), "PxRigidDynamic::putToSleep: Body must be non-kinematic!"); + PX_CHECK_AND_RETURN(!(b.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION), "PxRigidDynamic::putToSleep: Not allowed if PxActorFlag::eDISABLE_SIMULATION is set!"); + + b.putToSleep(); +} + + +void NpRigidDynamic::setSolverIterationCounts(PxU32 positionIters, PxU32 velocityIters) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(positionIters > 0, "PxRigidDynamic::setSolverIterationCount: positionIters must be more than zero!"); + PX_CHECK_AND_RETURN(positionIters <= 255, "PxRigidDynamic::setSolverIterationCount: positionIters must be no greater than 255!"); + PX_CHECK_AND_RETURN(velocityIters > 0, "PxRigidDynamic::setSolverIterationCount: velocityIters must be more than zero!"); + PX_CHECK_AND_RETURN(velocityIters <= 255, "PxRigidDynamic::setSolverIterationCount: velocityIters must be no greater than 255!"); + + getScbBodyFast().setSolverIterationCounts((velocityIters & 0xff) << 8 | (positionIters & 0xff)); +} + + +void NpRigidDynamic::getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + PxU16 x = getScbBodyFast().getSolverIterationCounts(); + velocityIters = PxU32(x >> 8); + positionIters = PxU32(x & 0xff); +} + + +void NpRigidDynamic::setContactReportThreshold(PxReal threshold) +{ + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + PX_CHECK_AND_RETURN(PxIsFinite(threshold), "PxRigidDynamic::setContactReportThreshold: invalid float."); + PX_CHECK_AND_RETURN(threshold >= 0.0f, "PxRigidDynamic::setContactReportThreshold: Force threshold must be greater than zero!"); + + getScbBodyFast().setContactReportThreshold(threshold<0 ? 0 : threshold); +} + + +PxReal NpRigidDynamic::getContactReportThreshold() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + + return getScbBodyFast().getContactReportThreshold(); +} + + +PxU32 physx::NpRigidDynamicGetShapes(Scb::Body& body, void* const*& shapes, bool* isCompound) +{ + NpRigidDynamic* a = static_cast(body.getScBody().getPxActor()); + NpShapeManager& sm = a->getShapeManager(); + shapes = reinterpret_cast(sm.getShapes()); + if(isCompound) + *isCompound = sm.isSqCompound(); + return sm.getNbShapes(); +} + + +void NpRigidDynamic::switchToNoSim() +{ + getScbBodyFast().switchBodyToNoSim(); +} + + +void NpRigidDynamic::switchFromNoSim() +{ + getScbBodyFast().switchFromNoSim(true); +} + + +void NpRigidDynamic::wakeUpInternalNoKinematicTest(Scb::Body& body, bool forceWakeUp, bool autowake) +{ + NpScene* scene = NpActor::getOwnerScene(*this); + PX_ASSERT(scene); + PxReal wakeCounterResetValue = scene->getWakeCounterResetValueInteral(); + + PxReal wakeCounter = body.getWakeCounter(); + + bool needsWakingUp = body.isSleeping() && (autowake || forceWakeUp); + if (autowake && (wakeCounter < wakeCounterResetValue)) + { + wakeCounter = wakeCounterResetValue; + needsWakingUp = true; + } + + if (needsWakingUp) + body.wakeUpInternal(wakeCounter); +} + +PxRigidDynamicLockFlags NpRigidDynamic::getRigidDynamicLockFlags() const +{ + return mBody.getLockFlags(); +} +void NpRigidDynamic::setRigidDynamicLockFlags(PxRigidDynamicLockFlags flags) +{ + mBody.setLockFlags(flags); +} +void NpRigidDynamic::setRigidDynamicLockFlag(PxRigidDynamicLockFlag::Enum flag, bool value) +{ + PxRigidDynamicLockFlags flags = mBody.getLockFlags(); + if (value) + flags = flags | flag; + else + flags = flags & (~flag); + + mBody.setLockFlags(flags); +} + + +#if PX_ENABLE_DEBUG_VISUALIZATION +void NpRigidDynamic::visualize(Cm::RenderOutput& out, NpScene* npScene) +{ + NpRigidDynamicT::visualize(out, npScene); + + if (getScbBodyFast().getActorFlags() & PxActorFlag::eVISUALIZATION) + { + PX_ASSERT(npScene); + const PxReal scale = npScene->getVisualizationParameter(PxVisualizationParameter::eSCALE); + + const PxReal massAxes = scale * npScene->getVisualizationParameter(PxVisualizationParameter::eBODY_MASS_AXES); + if (massAxes != 0.0f) + { + PxReal sleepTime = getScbBodyFast().getWakeCounter() / npScene->getWakeCounterResetValueInteral(); + PxU32 color = PxU32(0xff * (sleepTime>1.0f ? 1.0f : sleepTime)); + color = getScbBodyFast().isSleeping() ? 0xff0000 : (color<<16 | color<<8 | color); + PxVec3 dims = invertDiagInertia(getScbBodyFast().getInverseInertia()); + dims = getDimsFromBodyInertia(dims, 1.0f / getScbBodyFast().getInverseMass()); + + out << color << getScbBodyFast().getBody2World() << Cm::DebugBox(dims * 0.5f); + } + } +} +#endif diff --git a/src/PhysX/physx/source/physx/src/NpRigidDynamic.h b/src/PhysX/physx/source/physx/src/NpRigidDynamic.h new file mode 100644 index 000000000..ea9aaa914 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidDynamic.h @@ -0,0 +1,176 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_RIGIDDYNAMIC +#define PX_PHYSICS_NP_RIGIDDYNAMIC + +#include "NpRigidBodyTemplate.h" +#include "PxRigidDynamic.h" +#include "ScbBody.h" +#include "PxMetaData.h" + +namespace physx +{ + +class NpRigidDynamic; +typedef NpRigidBodyTemplate NpRigidDynamicT; + +class NpRigidDynamic : public NpRigidDynamicT +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpRigidDynamic(PxBaseFlags baseFlags) : NpRigidDynamicT(baseFlags) {} + + virtual void requiresObjects(PxProcessPxBaseCallback& c); + + static NpRigidDynamic* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + virtual ~NpRigidDynamic(); + + //--------------------------------------------------------------------------------- + // PxActor implementation + //--------------------------------------------------------------------------------- + + virtual void release(); + + //--------------------------------------------------------------------------------- + // PxRigidDynamic implementation + //--------------------------------------------------------------------------------- + + virtual PxActorType::Enum getType() const { return PxActorType::eRIGID_DYNAMIC; } + + // Pose + virtual void setGlobalPose(const PxTransform& pose, bool autowake); + PX_FORCE_INLINE PxTransform getGlobalPoseFast() const + { + const Scb::Body& body=getScbBodyFast(); + return body.getBody2World() * body.getBody2Actor().getInverse(); + } + virtual PxTransform getGlobalPose() const + { + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return getGlobalPoseFast(); + } + + virtual void setKinematicTarget(const PxTransform& destination); + virtual bool getKinematicTarget(PxTransform& target) const; + + // Center of mass pose + virtual void setCMassLocalPose(const PxTransform&); + + // Damping + virtual void setLinearDamping(PxReal); + virtual PxReal getLinearDamping() const; + virtual void setAngularDamping(PxReal); + virtual PxReal getAngularDamping() const; + + // Velocity + virtual void setLinearVelocity(const PxVec3&, bool autowake); + virtual void setAngularVelocity(const PxVec3&, bool autowake); + virtual void setMaxAngularVelocity(PxReal); + virtual PxReal getMaxAngularVelocity() const; + virtual void setMaxLinearVelocity(PxReal); + virtual PxReal getMaxLinearVelocity() const; + + // Force/Torque modifiers + virtual void addForce(const PxVec3&, PxForceMode::Enum mode, bool autowake); + virtual void clearForce(PxForceMode::Enum mode); + virtual void addTorque(const PxVec3&, PxForceMode::Enum mode, bool autowake); + virtual void clearTorque(PxForceMode::Enum mode); + virtual void setForceAndTorque(const PxVec3& force, const PxVec3& torque, PxForceMode::Enum mode = PxForceMode::eFORCE); + + + + // Sleeping + virtual bool isSleeping() const; + virtual PxReal getSleepThreshold() const; + virtual void setSleepThreshold(PxReal threshold); + virtual PxReal getStabilizationThreshold() const; + virtual void setStabilizationThreshold(PxReal threshold); + virtual void setWakeCounter(PxReal wakeCounterValue); + virtual PxReal getWakeCounter() const; + virtual void wakeUp(); + virtual void putToSleep(); + + virtual void setSolverIterationCounts(PxU32 positionIters, PxU32 velocityIters); + virtual void getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const; + + virtual void setContactReportThreshold(PxReal threshold); + virtual PxReal getContactReportThreshold() const; + + virtual PxRigidDynamicLockFlags getRigidDynamicLockFlags() const; + virtual void setRigidDynamicLockFlags(PxRigidDynamicLockFlags flags); + virtual void setRigidDynamicLockFlag(PxRigidDynamicLockFlag::Enum flag, bool value); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpRigidDynamic(const PxTransform& bodyPose); + + virtual void switchToNoSim(); + virtual void switchFromNoSim(); + + PX_FORCE_INLINE void wakeUpInternal(); + void wakeUpInternalNoKinematicTest(Scb::Body& body, bool forceWakeUp, bool autowake); + +private: + PX_FORCE_INLINE void setKinematicTargetInternal(const PxTransform& destination); + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene); +#endif +}; + + + + +PX_FORCE_INLINE void NpRigidDynamic::wakeUpInternal() +{ + PX_ASSERT(NpActor::getOwnerScene(*this)); + + Scb::Body& body = getScbBodyFast(); + const PxRigidBodyFlags currentFlags = body.getFlags(); + + if (!(currentFlags & PxRigidBodyFlag::eKINEMATIC)) // kinematics are only awake when a target is set, else they are asleep + wakeUpInternalNoKinematicTest(body, false, true); +} + + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpRigidStatic.cpp b/src/PhysX/physx/source/physx/src/NpRigidStatic.cpp new file mode 100644 index 000000000..34af3d241 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidStatic.cpp @@ -0,0 +1,163 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "NpRigidStatic.h" +#include "NpPhysics.h" +#include "ScbNpDeps.h" +#include "NpScene.h" +#include "NpRigidActorTemplateInternal.h" + +using namespace physx; + +NpRigidStatic::NpRigidStatic(const PxTransform& pose) +: NpRigidStaticT(PxConcreteType::eRIGID_STATIC, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mRigidStatic(pose) +{ +} + +NpRigidStatic::~NpRigidStatic() +{ +} + +// PX_SERIALIZATION +void NpRigidStatic::requiresObjects(PxProcessPxBaseCallback& c) +{ + NpRigidStaticT::requiresObjects(c); +} + +NpRigidStatic* NpRigidStatic::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpRigidStatic* obj = new (address) NpRigidStatic(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(NpRigidStatic); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +void NpRigidStatic::release() +{ + releaseActorT(this, mRigidStatic); +} + +void NpRigidStatic::setGlobalPose(const PxTransform& pose, bool /*wake*/) +{ + PX_CHECK_AND_RETURN(pose.isSane(), "PxRigidStatic::setGlobalPose: pose is not valid."); + + NP_WRITE_CHECK(NpActor::getOwnerScene(*this)); + + NpScene* npScene = NpActor::getAPIScene(*this); +#if PX_CHECKED + if(npScene) + npScene->checkPositionSanity(*this, pose, "PxRigidStatic::setGlobalPose"); +#endif + + mRigidStatic.setActor2World(pose.getNormalized()); + + if(npScene) + { + mShapeManager.markAllSceneQueryForUpdate(npScene->getSceneQueryManagerFast(), *this); + npScene->getSceneQueryManagerFast().get(Sq::PruningIndex::eSTATIC).invalidateTimestamp(); + } + +#if PX_SUPPORT_PVD + // have to do this here since this call gets not forwarded to Scb::RigidStatic + Scb::Scene* scbScene = NpActor::getScbFromPxActor(*this).getScbSceneForAPI(); + if(scbScene) + scbScene->getScenePvdClient().updatePvdProperties(&mRigidStatic); +#endif + + // invalidate the pruning structure if the actor bounds changed + if (mShapeManager.getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxRigidStatic::setGlobalPose: Actor is part of a pruning structure, pruning structure is now invalid!"); + mShapeManager.getPruningStructure()->invalidate(this); + } + + updateShaderComs(); +} + +PxTransform NpRigidStatic::getGlobalPose() const +{ + NP_READ_CHECK(NpActor::getOwnerScene(*this)); + return mRigidStatic.getActor2World(); +} + +PxU32 physx::NpRigidStaticGetShapes(Scb::RigidStatic& rigid, void* const *&shapes) +{ + NpRigidStatic* a = static_cast(rigid.getScRigidCore().getPxActor()); + NpShapeManager& sm = a->getShapeManager(); + shapes = reinterpret_cast(sm.getShapes()); + return sm.getNbShapes(); +} + +void NpRigidStatic::switchToNoSim() +{ + getScbRigidStaticFast().switchToNoSim(false); +} + +void NpRigidStatic::switchFromNoSim() +{ + getScbRigidStaticFast().switchFromNoSim(false); +} + +#if PX_CHECKED +bool NpRigidStatic::checkConstraintValidity() const +{ + // Perhaps NpConnectorConstIterator would be worth it... + NpConnectorIterator iter = (const_cast(this))->getConnectorIterator(NpConnectorType::eConstraint); + while (PxBase* ser = iter.getNext()) + { + NpConstraint* c = static_cast(ser); + if(!c->NpConstraint::isValid()) + return false; + } + return true; +} +#endif + +#if PX_ENABLE_DEBUG_VISUALIZATION +void NpRigidStatic::visualize(Cm::RenderOutput& out, NpScene* scene) +{ + NpRigidStaticT::visualize(out, scene); + + if (getScbRigidStaticFast().getActorFlags() & PxActorFlag::eVISUALIZATION) + { + Scb::Scene& scbScene = scene->getScene(); + PxReal scale = scbScene.getVisualizationParameter(PxVisualizationParameter::eSCALE); + + //visualize actor frames + PxReal actorAxes = scale * scbScene.getVisualizationParameter(PxVisualizationParameter::eACTOR_AXES); + if (actorAxes != 0) + out << getGlobalPose() << Cm::DebugBasis(PxVec3(actorAxes)); + } +} +#endif + diff --git a/src/PhysX/physx/source/physx/src/NpRigidStatic.h b/src/PhysX/physx/source/physx/src/NpRigidStatic.h new file mode 100644 index 000000000..7da078970 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpRigidStatic.h @@ -0,0 +1,115 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_RIGIDSTATIC +#define PX_PHYSICS_NP_RIGIDSTATIC + +#include "NpRigidActorTemplate.h" +#include "PxRigidStatic.h" +#include "ScbRigidStatic.h" + +#include "PxMetaData.h" + +namespace physx +{ + +namespace Scb +{ + class RigidObject; +} + +class NpRigidStatic; +typedef NpRigidActorTemplate NpRigidStaticT; + +class NpRigidStatic : public NpRigidStaticT +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpRigidStatic(PxBaseFlags baseFlags) : NpRigidStaticT(baseFlags), mRigidStatic(PxEmpty) {} + virtual void requiresObjects(PxProcessPxBaseCallback& c); + static NpRigidStatic* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + virtual ~NpRigidStatic(); + + //--------------------------------------------------------------------------------- + // PxActor implementation + //--------------------------------------------------------------------------------- + virtual void release(); + + virtual PxActorType::Enum getType() const { return PxActorType::eRIGID_STATIC; } + + //--------------------------------------------------------------------------------- + // PxRigidActor implementation + //--------------------------------------------------------------------------------- + + // Pose + virtual void setGlobalPose(const PxTransform& pose, bool wake); + virtual PxTransform getGlobalPose() const; + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + NpRigidStatic(const PxTransform& pose); + + virtual void switchToNoSim(); + virtual void switchFromNoSim(); + +#if PX_CHECKED + bool checkConstraintValidity() const; +#endif + + PX_FORCE_INLINE const Scb::Actor& getScbActorFast() const { return mRigidStatic; } + PX_FORCE_INLINE Scb::Actor& getScbActorFast() { return mRigidStatic; } + + PX_FORCE_INLINE const Scb::RigidStatic& getScbRigidStaticFast() const { return mRigidStatic; } + PX_FORCE_INLINE Scb::RigidStatic& getScbRigidStaticFast() { return mRigidStatic; } + + PX_FORCE_INLINE const PxTransform& getGlobalPoseFast() const { return mRigidStatic.getActor2World(); } + +#if PX_ENABLE_DEBUG_VISUALIZATION +public: + void visualize(Cm::RenderOutput& out, NpScene* scene); +#endif + +private: + Scb::RigidStatic mRigidStatic; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpScene.cpp b/src/PhysX/physx/source/physx/src/NpScene.cpp new file mode 100644 index 000000000..d6f6d9f35 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpScene.cpp @@ -0,0 +1,3022 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxSimulationEventCallback.h" + +#include "NpScene.h" +#include "NpRigidStatic.h" +#include "NpRigidDynamic.h" +#include "NpArticulation.h" +#include "NpArticulationReducedCoordinate.h" +#include "NpArticulationLink.h" +#include "NpArticulationJoint.h" +#include "NpAggregate.h" +#include "NpBatchQuery.h" +#include "SqPruner.h" +#include "SqPruningStructure.h" +#include "SqSceneQueryManager.h" +#include "GuBVHStructure.h" + +#include "ScbNpDeps.h" +#include "ScArticulationSim.h" +#include "ScConstraintSim.h" +#include "CmCollection.h" +#include "CmUtils.h" + + +#if PX_SUPPORT_GPU_PHYSX +#include "task/PxGpuDispatcher.h" +#endif + +#include "extensions/PxJoint.h" + +#include "PxsIslandSim.h" +#include "common/PxProfileZone.h" + +using namespace physx; + +// enable thread checks in all debug builds +#if PX_DEBUG || PX_CHECKED +#define NP_ENABLE_THREAD_CHECKS 1 +#else +#define NP_ENABLE_THREAD_CHECKS 0 +#endif + +using namespace shdfnd; +using namespace Sq; + +/////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE bool removeFromSceneCheck(NpScene* npScene, PxScene* scene, const char* name) +{ + if (scene == static_cast(npScene)) + { + return true; + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "%s not assigned to scene or assigned to another scene. Call will be ignored!", name); + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + + +NpSceneQueries::NpSceneQueries(const PxSceneDesc& desc) : + mScene (desc, getContextId()), + mSQManager (mScene, desc.staticStructure, desc.dynamicStructure, desc.dynamicTreeRebuildRateHint, desc.limits), + mCachedRaycastFuncs (Gu::getRaycastFuncTable()), + mCachedSweepFuncs (Gu::getSweepFuncTable()), + mCachedOverlapFuncs (Gu::getOverlapFuncTable()), + mSceneQueriesStaticPrunerUpdate (getContextId(), 0, "NpSceneQueries.sceneQueriesStaticPrunerUpdate"), + mSceneQueriesDynamicPrunerUpdate(getContextId(), 0, "NpSceneQueries.sceneQueriesDynamicPrunerUpdate"), + mSceneQueryUpdateMode (desc.sceneQueryUpdateMode) +#if PX_SUPPORT_PVD + , mSingleSqCollector (mScene, false), + mBatchedSqCollector (mScene, true) +#endif +{ + mSceneQueriesStaticPrunerUpdate.setObject(this); + mSceneQueriesDynamicPrunerUpdate.setObject(this); +} + +NpScene::NpScene(const PxSceneDesc& desc) : + NpSceneQueries (desc), + mConstraints (PX_DEBUG_EXP("sceneConstraints")), + mRigidActors (PX_DEBUG_EXP("sceneRigidActors")), + mArticulations (PX_DEBUG_EXP("sceneArticulations")), + mAggregates (PX_DEBUG_EXP("sceneAggregates")), + mSanityBounds (desc.sanityBounds), + mNbClients (1), //we always have the default client. + mClientBehaviorFlags (PX_DEBUG_EXP("sceneBehaviorFlags")), + mSceneCompletion (getContextId(), mPhysicsDone), + mCollisionCompletion (getContextId(), mCollisionDone), + mSceneQueriesCompletion (getContextId(), mSceneQueriesDone), + mSceneExecution (getContextId(), 0, "NpScene.execution"), + mSceneCollide (getContextId(), 0, "NpScene.collide"), + mSceneAdvance (getContextId(), 0, "NpScene.solve"), + mControllingSimulation (false), + mSimThreadStackSize (0), + mConcurrentWriteCount (0), + mConcurrentReadCount (0), + mConcurrentErrorCount (0), + mCurrentWriter (0), + mSceneQueriesUpdateRunning (false), + mHasSimulatedOnce (false), + mBetweenFetchResults (false), + mBuildFrozenActors (false) +{ + mSceneExecution.setObject(this); + mSceneCollide.setObject(this); + mSceneAdvance.setObject(this); + + mTaskManager = mScene.getScScene().getTaskManagerPtr(); + mThreadReadWriteDepth = Ps::TlsAlloc(); + + updatePhysXIndicator(); + +} + +NpSceneQueries::~NpSceneQueries() +{ +} + +NpScene::~NpScene() +{ + // PT: we need to do that one first, now that we don't release the objects anymore. Otherwise we end up with a sequence like: + // - actor is part of an aggregate, and part of a scene + // - actor gets removed from the scene. This does *not* remove it from the aggregate. + // - aggregate gets removed from the scene, sees that one contained actor ain't in the scene => we get a warning message + PxU32 aggregateCount = mAggregates.size(); + while(aggregateCount--) + removeAggregate(*mAggregates.getEntries()[aggregateCount], false); + + PxU32 rigidActorCount = mRigidActors.size(); + while(rigidActorCount--) + removeActor(*mRigidActors[rigidActorCount], false); + + PxU32 articCount = mArticulations.size(); + while(articCount--) + removeArticulation(*mArticulations.getEntries()[articCount], false); + + bool unlock = mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK; + +#if PX_SUPPORT_PVD + getSingleSqCollector().release(); + getBatchedSqCollector().release(); +#endif + + // release batch queries + PxU32 numSq = mBatchQueries.size(); + while(numSq--) + PX_DELETE(mBatchQueries[numSq]); + mBatchQueries.clear(); + + mScene.release(); + + // unlock the lock taken in release(), must unlock before + // mRWLock is destroyed otherwise behavior is undefined + if (unlock) + unlockWrite(); + + TlsFree(mThreadReadWriteDepth); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::release() +{ + // need to acquire lock for release, note this is unlocked in the destructor + if (mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) + lockWrite(__FILE__, __LINE__); + + // It will be hard to do a write check here since all object release calls in the scene destructor do it and would mess + // up the test. If we really want it on scene destruction as well, we need to either have internal and external release + // calls or come up with a different approach (for example using thread ID as detector variable). + + if(getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::release(): Scene is still being simulated! PxScene::fetchResults() is called implicitly."); + + if(getSimulationStage() == Sc::SimulationStage::eCOLLIDE) + { + fetchCollision(true); + } + + if(getSimulationStage() == Sc::SimulationStage::eFETCHCOLLIDE) // need to call getSimulationStage() again beacause fetchCollision() might change the value. + { + // this is for split sim + advance(NULL); + } + + fetchResults(true, NULL); + } + NpPhysics::getInstance().releaseSceneInternal(*this); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NpScene::loadFromDesc(const PxSceneDesc& desc) +{ + { + if(desc.limits.maxNbActors) + mRigidActors.reserve(desc.limits.maxNbActors); + + //const PxU32 totalNbShapes = desc.limits.maxNbStaticShapes + desc.limits.maxNbDynamicShapes; + mScene.getScScene().preAllocate(desc.limits.maxNbActors, desc.limits.maxNbBodies, desc.limits.maxNbStaticShapes, desc.limits.maxNbDynamicShapes); + } + + userData = desc.userData; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setGravity(const PxVec3& g) +{ + NP_WRITE_CHECK(this); + mScene.setGravity(g); +} + +PxVec3 NpScene::getGravity() const +{ + NP_READ_CHECK(this); + return mScene.getGravity(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setBounceThresholdVelocity(const PxReal t) +{ + NP_WRITE_CHECK(this); + mScene.setBounceThresholdVelocity(t); +} + +PxReal NpScene::getBounceThresholdVelocity() const +{ + NP_READ_CHECK(this) + return mScene.getBounceThresholdVelocity(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setLimits(const PxSceneLimits& limits) +{ + NP_WRITE_CHECK(this); + + if(limits.maxNbActors) + mRigidActors.reserve(limits.maxNbActors); + + mScene.getScScene().preAllocate(limits.maxNbActors, limits.maxNbBodies, limits.maxNbStaticShapes, limits.maxNbDynamicShapes); + mScene.setLimits(limits); + + mSQManager.preallocate(limits.maxNbStaticShapes, limits.maxNbDynamicShapes); +} + +////////////////////////////////////////////////////////////////////////// + +PxSceneLimits NpScene::getLimits() const +{ + NP_READ_CHECK(this); + + return mScene.getLimits(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setFlag(PxSceneFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(this); + + // this call supports mutable flags only + PX_CHECK_AND_RETURN(PxSceneFlags(flag) & PxSceneFlags(PxSceneFlag::eMUTABLE_FLAGS), + "PxScene::setFlag: This flag is not mutable - you can only set it once in PxSceneDesc at startup!"); + + PxSceneFlags currentFlags = mScene.getFlags(); + + if(value) + currentFlags |= flag; + else + currentFlags &= ~PxSceneFlags(flag); + + mScene.setFlags(currentFlags); +} + +PxSceneFlags NpScene::getFlags() const +{ + NP_READ_CHECK(this); + return mScene.getFlags(); +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: make sure we always add to array and set the array index properly / at the same time +template +static PX_FORCE_INLINE void addRigidActorToArray(T& a, Ps::Array& rigidActors) +{ + a.setRigidActorArrayIndex(rigidActors.size()); + rigidActors.pushBack(&a); +} + +void NpScene::addActor(PxActor& actor, const PxBVHStructure* bvhStructure) +{ + PX_PROFILE_ZONE("API.addActor", getContextId()); + NP_WRITE_CHECK(this); + PX_SIMD_GUARD; + + PxRigidStatic* a = actor.is(); + if(a) + { +#if PX_CHECKED + if(!static_cast(a)->checkConstraintValidity()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): actor has invalid constraint and may not be added to scene"); + return; + } +#endif + if(static_cast(a)->getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); + return; + } + } + + PxRigidDynamic* aD = actor.is(); + if(aD && static_cast(aD)->getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); + return; + } + + const Scb::ControlState::Enum cs = NpActor::getScbFromPxActor(actor).getControlState(); + if((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING) && (NpActor::getOwnerScene(actor) == this))) + addActorInternal(actor, bvhStructure); + else + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActor(): Actor already assigned to a scene. Call will be ignored!"); +} + +void NpScene::addActorInternal(PxActor& actor, const PxBVHStructure* bvhStructure) +{ + // BvhStructure check + if(bvhStructure) + { + const PxRigidActor* rigidActor = actor.is(); + if(!rigidActor || bvhStructure->getNbBounds() == 0 || bvhStructure->getNbBounds() > rigidActor->getNbShapes()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxRigidActor::setBVHStructure structure is empty or does not match shapes in the actor."); + return; + } + } + + switch(actor.getConcreteType()) + { + case PxConcreteType::eRIGID_STATIC: + { + NpRigidStatic& npStatic = static_cast(actor); +#if PX_CHECKED + checkPositionSanity(npStatic, npStatic.getGlobalPose(), "PxScene::addActor or PxScene::addAggregate"); +#endif + addRigidStatic(npStatic, static_cast(bvhStructure)); + } + break; + + case PxConcreteType::eRIGID_DYNAMIC: + { + NpRigidDynamic& npDynamic = static_cast(actor); +#if PX_CHECKED + checkPositionSanity(npDynamic, npDynamic.getGlobalPose(), "PxScene::addActor or PxScene::addAggregate"); +#endif + addRigidDynamic(npDynamic, static_cast(bvhStructure)); + } + break; + + case PxConcreteType::eARTICULATION_LINK: + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addActor(): Individual articulation links can not be added to the scene"); + } + break; + + default: + PX_ASSERT(0); + } +} + +void NpScene::updateScbStateAndSetupSq(const PxRigidActor& rigidActor, Scb::Actor& scbActor, NpShapeManager& shapeManager, bool actorDynamic, const PxBounds3* bounds, bool hasPrunerStructure) +{ + // all the things Scb does in non-buffered insertion + SceneQueryManager& sqManager = getSceneQueryManagerFast(); + + scbActor.setScbScene(&mScene); + scbActor.setControlState(Scb::ControlState::eIN_SCENE); + NpShape*const * shapes = shapeManager.getShapes(); + PxU32 nbShapes = shapeManager.getNbShapes(); + + for(PxU32 i=0;i(body), shapeManager, actorDynamic, bounds, hasPrunerStructure); +} + +void NpScene::addActors(PxActor*const* actors, PxU32 nbActors) +{ + addActorsInternal(actors, nbActors, NULL); +} + +void NpScene::addActors(const PxPruningStructure& ps) +{ + const Sq::PruningStructure& prunerStructure = static_cast(ps); + if(!prunerStructure.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxScene::addActors(): Provided pruning structure is not valid."); + return; + } + addActorsInternal(prunerStructure.getActors(), prunerStructure.getNbActors(), &prunerStructure); +} + +void NpScene::addActorsInternal(PxActor*const* PX_RESTRICT actors, PxU32 nbActors, const Sq::PruningStructure* pS) +{ + PX_PROFILE_ZONE("API.addActors", getContextId()); + NP_WRITE_CHECK(this); + PX_SIMD_GUARD; + + if(getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxScene::addActors() not allowed while simulation is running."); + return; + } + + const bool hasPrunerStructure = pS ? true : false; + Sc::Scene& scScene = mScene.getScScene(); + PxU32 actorsDone; + + Sc::BatchInsertionState scState; + scScene.startBatchInsertion(scState); + + scState.staticActorOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getScbRigidStaticFast().getScStatic()))); + scState.staticShapeTableOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getShapeManager().getShapeTable()))); + scState.dynamicActorOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getScbBodyFast().getScBody()))); + scState.dynamicShapeTableOffset = ptrdiff_t(size_t(&(reinterpret_cast(0)->getShapeManager().getShapeTable()))); + scState.shapeOffset = ptrdiff_t(NpShapeGetScPtrOffset()); + + Ps::InlineArray shapeBounds; + for(actorsDone=0; actorsDonegetConcreteType(); + if(type == PxConcreteType::eRIGID_STATIC) + { + NpRigidStatic& a = *static_cast(actors[actorsDone]); +#if PX_CHECKED + if(!a.checkConstraintValidity()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActors(): actor has invalid constraint and may not be added to scene"); + break; + } + checkPositionSanity(a, a.getGlobalPose(), "PxScene::addActors"); +#endif + if(!hasPrunerStructure && a.getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActors(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); + break; + } + + if(!(a.getScbRigidStaticFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + { + shapeBounds.resizeUninitialized(a.NpRigidStatic::getNbShapes()+1); // PT: +1 for safe reads in addPrunerData/inflateBounds + scScene.addStatic(&a, scState, shapeBounds.begin()); + updateScbStateAndSetupSq(a, a.getScbActorFast(), a.getShapeManager(), false, shapeBounds.begin(), hasPrunerStructure); + addRigidActorToArray(a, mRigidActors); + a.addConstraintsToScene(); + } + else + addRigidStatic(a, NULL, hasPrunerStructure); + } + else if(type == PxConcreteType::eRIGID_DYNAMIC) + { + NpRigidDynamic& a = *static_cast(actors[actorsDone]); +#if PX_CHECKED + checkPositionSanity(a, a.getGlobalPose(), "PxScene::addActors"); +#endif + if(!hasPrunerStructure && a.getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addActors(): actor is in a pruning structure and cannot be added to a scene directly, use addActors(const PxPruningStructure& )"); + break; + } + + if(!(a.getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + { + shapeBounds.resizeUninitialized(a.NpRigidDynamic::getNbShapes()+1); // PT: +1 for safe reads in addPrunerData/inflateBounds + scScene.addBody(&a, scState, shapeBounds.begin(), false); + updateScbStateAndSetupSq(a, a.getScbBodyFast(), a.getShapeManager(), true, shapeBounds.begin(), hasPrunerStructure); + addRigidActorToArray(a, mRigidActors); + a.addConstraintsToScene(); + } + else + addRigidDynamic(a, NULL, hasPrunerStructure); + } + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addRigidActors(): articulation link not permitted"); + break; + } + } + // merge sq PrunerStructure + if(pS) + { + mSQManager.addPruningStructure(*pS); + } + scScene.finishBatchInsertion(scState); + + // if we failed, still complete everything for the successful inserted actors before backing out +#if PX_SUPPORT_PVD + for(PxU32 i=0;igetConcreteType()==PxConcreteType::eRIGID_STATIC) && (!(static_cast(actors[i])->getScbRigidStaticFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) + mScene.getScenePvdClient().addStaticAndShapesToPvd(static_cast(actors[i])->getScbRigidStaticFast()); + else if ((actors[i]->getConcreteType() == PxConcreteType::eRIGID_DYNAMIC) && (!(static_cast(actors[i])->getScbBodyFast().getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))) + mScene.getScenePvdClient().addBodyAndShapesToPvd(static_cast(actors[i])->getScbBodyFast()); + } +#endif + + if(actorsDonegetConcreteType(); + if (!removeFromSceneCheck(this, actors[actorsDone]->getScene(), "PxScene::removeActors(): Actor")) + { + break; + } + + removeState.bufferedShapes.clear(); + removeState.removedShapes.clear(); + + if(type == PxConcreteType::eRIGID_STATIC) + { + NpRigidStatic& actor = *static_cast(actors[actorsDone]); + const PxActorFlags actorFlags = actor.getScbRigidStaticFast().getActorFlags(); + + if(actor.getShapeManager().getNbShapes()) + Ps::prefetch(actor.getShapeManager().getShapes()[0],sizeof(NpShape)); + scScene.prefetchForRemove(actor.getScbRigidStaticFast().getScStatic()); + Ps::prefetch(mRigidActors[mRigidActors.size()-1],sizeof(NpRigidDynamic)); + + const bool noSimBuffered = actorFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + if (!noSimBuffered) + actor.removeConstraintsFromScene(); + + actor.getShapeManager().teardownAllSceneQuery(getSceneQueryManagerFast(), actor); + + Scb::RigidStatic& rs = actor.getScbRigidStaticFast(); + mScene.removeActor(rs, wakeOnLostTouch, rs.isSimDisabledInternally()); + removeFromRigidActorList(actor.getRigidActorArrayIndex()); + } + else if(type == PxConcreteType::eRIGID_DYNAMIC) + { + NpRigidDynamic& actor = *static_cast(actors[actorsDone]); + const PxActorFlags actorFlags = actor.getScbBodyFast().getActorFlags(); + + if(actor.getShapeManager().getNbShapes()) + Ps::prefetch(actor.getShapeManager().getShapes()[0],sizeof(NpShape)); + scScene.prefetchForRemove(actor.getScbBodyFast().getScBody()); + Ps::prefetch(mRigidActors[mRigidActors.size()-1],sizeof(NpRigidDynamic)); + + const bool noSimBuffered = actorFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + if (!noSimBuffered) + actor.removeConstraintsFromScene(); + + actor.getShapeManager().teardownAllSceneQuery(getSceneQueryManagerFast(), actor); + + Scb::Body& b = actor.getScbBodyFast(); + mScene.removeActor(b, wakeOnLostTouch, b.isSimDisabledInternally()); + removeFromRigidActorList(actor.getRigidActorArrayIndex()); + } + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::removeActor(): Individual articulation links can not be removed from the scene"); + break; + } + } + + scScene.setBatchRemove(NULL); +} + +void NpScene::removeActor(PxActor& actor, bool wakeOnLostTouch) +{ + PX_PROFILE_ZONE("API.removeActor", getContextId()); + NP_WRITE_CHECK(this); + if (removeFromSceneCheck(this, actor.getScene(), "PxScene::removeActor(): Actor")) + { + removeActorInternal(actor, wakeOnLostTouch, true); + } +} + +void NpScene::removeActorInternal(PxActor& actor, bool wakeOnLostTouch, bool removeFromAggregate) +{ + switch(actor.getType()) + { + case PxActorType::eRIGID_STATIC: + { + NpRigidStatic& npStatic = static_cast(actor); + removeRigidStatic(npStatic, wakeOnLostTouch, removeFromAggregate); + } + break; + + case PxActorType::eRIGID_DYNAMIC: + { + NpRigidDynamic& npDynamic = static_cast(actor); + removeRigidDynamic(npDynamic, wakeOnLostTouch, removeFromAggregate); + } + break; + + case PxActorType::eARTICULATION_LINK: + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::removeActor(): Individual articulation links can not be removed from the scene"); + } + break; + + case PxActorType::eACTOR_COUNT: + case PxActorType::eACTOR_FORCE_DWORD: + PX_ASSERT(0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +// PT: TODO: inline this one in the header for consistency +void NpScene::removeFromRigidActorList(const PxU32& index) +{ + PX_ASSERT(index != 0xFFFFFFFF); + PX_ASSERT(index < mRigidActors.size()); + + { + const PxU32 size = mRigidActors.size() - 1; + mRigidActors.replaceWithLast(index); + if(size && size != index) + { + PxRigidActor& rigidActor = *mRigidActors[index]; + switch(rigidActor.getType()) + { + case PxActorType::eRIGID_STATIC: + { + NpRigidStatic& npStatic = static_cast(rigidActor); + npStatic.setRigidActorArrayIndex(index); + } + break; + case PxActorType::eRIGID_DYNAMIC: + { + NpRigidDynamic& npDynamic = static_cast(rigidActor); + npDynamic.setRigidActorArrayIndex(index); + } + break; + case PxActorType::eARTICULATION_LINK: + case PxActorType::eACTOR_COUNT: + case PxActorType::eACTOR_FORCE_DWORD: + PX_ASSERT(0); + break; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +template +static PX_FORCE_INLINE void addActorT(T& actor, T2& scbActor, Ps::Array& actors, NpScene* scene, const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure) +{ + const bool noSimBuffered = scbActor.getActorFlags().isSet(PxActorFlag::eDISABLE_SIMULATION); + + PxBounds3 bounds[8+1]; // PT: +1 for safe reads in addPrunerData/inflateBounds + const bool canReuseBounds = !noSimBuffered && !scene->getScene().isPhysicsBuffering() && actor.getShapeManager().getNbShapes()<=8; + PxBounds3* uninflatedBounds = canReuseBounds ? bounds : NULL; + + scene->getScene().addActor(scbActor, noSimBuffered, uninflatedBounds, bvhStructure); + + actor.getShapeManager().setupAllSceneQuery(scene, actor, hasPrunerStructure, uninflatedBounds, bvhStructure); + if(!noSimBuffered) + actor.addConstraintsToScene(); + addRigidActorToArray(actor, actors); +} + +void NpScene::addRigidStatic(NpRigidStatic& actor, const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure) +{ + addActorT(actor, actor.getScbRigidStaticFast(), mRigidActors, this, bvhStructure, hasPrunerStructure); +} + +void NpScene::addRigidDynamic(NpRigidDynamic& body, const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure) +{ + addActorT(body, body.getScbBodyFast(), mRigidActors, this, bvhStructure, hasPrunerStructure); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +static PX_FORCE_INLINE void removeActorT(T& actor, T2& scbActor, NpScene* scene, bool wakeOnLostTouch, bool removeFromAggregate) +{ + PX_ASSERT(NpActor::getAPIScene(actor) == scene); + const bool noSimBuffered = scbActor.getActorFlags().isSet(PxActorFlag::eDISABLE_SIMULATION); + + if(removeFromAggregate) + { + PxU32 index = 0xffffffff; + NpAggregate* aggregate = actor.getNpAggregate(index); + if(aggregate) + { + aggregate->removeActorAndReinsert(actor, false); + PX_ASSERT(!actor.getAggregate()); + } + } + + actor.getShapeManager().teardownAllSceneQuery(scene->getSceneQueryManagerFast(), actor); + if(!noSimBuffered) + actor.removeConstraintsFromScene(); + + scene->getScene().removeActor(scbActor, wakeOnLostTouch, scbActor.isSimDisabledInternally()); + scene->removeFromRigidActorList(actor.getRigidActorArrayIndex()); +} + +void NpScene::removeRigidStatic(NpRigidStatic& actor, bool wakeOnLostTouch, bool removeFromAggregate) +{ + removeActorT(actor, actor.getScbRigidStaticFast(), this, wakeOnLostTouch, removeFromAggregate); +} + +void NpScene::removeRigidDynamic(NpRigidDynamic& body, bool wakeOnLostTouch, bool removeFromAggregate) +{ + removeActorT(body, body.getScbBodyFast(), this, wakeOnLostTouch, removeFromAggregate); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::addArticulation(PxArticulationBase& articulation) +{ + PX_PROFILE_ZONE("API.addArticulation", getContextId()); + NP_WRITE_CHECK(this); + + PX_CHECK_AND_RETURN(articulation.getNbLinks()>0, "PxScene::addArticulation: empty articulations may not be added to simulation."); + PX_SIMD_GUARD; + + if (this->getFlags() & PxSceneFlag::eENABLE_GPU_DYNAMICS) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): Articulations are not currently supported when PxSceneFlag::eENABLE_GPU_DYNAMICS is set!"); + return; + } + + if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE && articulation.getType() == PxArticulationBase::eReducedCoordinate) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): this call is not allowed while the simulation is running. Call will be ignored!"); + return; + } + + PxArticulationImpl& npa = *reinterpret_cast(articulation.getImpl()); + + Scb::Articulation& art = npa.getArticulation(); + const Scb::ControlState::Enum cs = art.getControlState(); + if ((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING) && (art.getScbScene()->getPxScene() == this))) + addArticulationInternal(articulation); + else + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation already assigned to a scene. Call will be ignored!"); +} + +void NpScene::addArticulationInternal(PxArticulationBase& npa) +{ + + // Add root link first + PxU32 nbLinks = npa.getNbLinks(); + PX_ASSERT(nbLinks > 0); + PxArticulationImpl* impl = reinterpret_cast(npa.getImpl()); + NpArticulationLink* rootLink = static_cast(impl->getRoot()); + +#if PX_CHECKED + checkPositionSanity(*rootLink, rootLink->getGlobalPose(), "PxScene::addArticulation or PxScene::addAggregate"); +#endif + if(rootLink->getMass()==0) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero mass added to scene; defaulting mass to 1"); + rootLink->setMass(1.0f); + } + + PxVec3 inertia0 = rootLink->getMassSpaceInertiaTensor(); + if(inertia0.x == 0.0f || inertia0.y == 0.0f || inertia0.z == 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero moment of inertia added to scene; defaulting inertia to (1,1,1)"); + rootLink->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); + } + + bool linkTriggersWakeUp = !rootLink->getScbBodyFast().checkSleepReadinessBesidesWakeCounter(); + + addArticulationLinkBody(*rootLink); + + // Add articulation + PxArticulationImpl* npaImpl = reinterpret_cast(npa.getImpl()); + Scb::Articulation& scbArt = npaImpl->getArticulation(); + scbArt.setArticulationType(npa.getType()); + mScene.addArticulation(scbArt); + + Sc::ArticulationCore& scArtCore = scbArt.getScArticulation(); + Sc::ArticulationSim* scArtSim = scArtCore.getSim(); + + if (scArtSim) + { + PxU32 handle = scArtSim->findBodyIndex(*rootLink->getScbBodyFast().getScBody().getSim()); + rootLink->setLLIndex(handle); + } + rootLink->setInboundJointDof(0); + + addArticulationLinkConstraint(*rootLink); + + // Add links & joints + PX_ALLOCA(linkStack, NpArticulationLink*, nbLinks); + linkStack[0] = rootLink; + PxU32 curLink = 0; + PxU32 stackSize = 1; + while(curLink < (nbLinks-1)) + { + PX_ASSERT(curLink < stackSize); + NpArticulationLink* l = linkStack[curLink]; + NpArticulationLink*const* children = l->getChildren(); + + for(PxU32 i=0; i < l->getNbChildren(); i++) + { + NpArticulationLink* child = children[i]; + +#if PX_CHECKED + checkPositionSanity(*child, child->getGlobalPose(), "PxScene::addArticulation"); +#endif + if(child->getMass()==0) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero mass added to scene; defaulting mass to 1"); + child->setMass(1.0f); + } + + PxVec3 inertia = child->getMassSpaceInertiaTensor(); + if(inertia.x == 0.0f || inertia.y == 0.0f || inertia.z == 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero moment of inertia added to scene; defaulting inertia to (1,1,1)"); + child->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); + } + + linkTriggersWakeUp = linkTriggersWakeUp || (!child->getScbBodyFast().checkSleepReadinessBesidesWakeCounter()); + + addArticulationLink(*child); // Adds joint too + if (scArtSim) + { + PxU32 cHandle = scArtSim->findBodyIndex(*child->getScbBodyFast().getScBody().getSim()); + child->setLLIndex(cHandle); + } + //child->setInboundJointDof(scArtSim->getDof(cHandle)); + + linkStack[stackSize] = child; + stackSize++; + } + + curLink++; + } + + if ((scbArt.getWakeCounter() == 0.0f) && linkTriggersWakeUp) + { + // this is for the buffered insert case, where the articulation needs to wake up, if one of the links triggers activation. + npaImpl->wakeUpInternal(true, false); + } + + mArticulations.insert(&npa); + + if (scArtSim) + { + scArtSim->checkResize(); + + linkStack[0] = rootLink; + curLink = 0; + stackSize = 1; + + while (curLink < (nbLinks - 1)) + { + PX_ASSERT(curLink < stackSize); + NpArticulationLink* l = linkStack[curLink]; + NpArticulationLink*const* children = l->getChildren(); + + for (PxU32 i = 0; i < l->getNbChildren(); i++) + { + NpArticulationLink* child = children[i]; + child->setInboundJointDof(scArtSim->getDof(child->getLinkIndex())); + + linkStack[stackSize] = child; + stackSize++; + } + + curLink++; + } + } + + //add loop joints + if (npa.getType() == PxArticulationBase::eReducedCoordinate) + { + //This method will prepare link data for the gpu + mScene.getScScene().addArticulationSimControl(scbArt.getScArticulation()); + + NpArticulationReducedCoordinate* npaRC = static_cast(&npa); + + for (PxU32 i = 0; i < npaRC->mLoopJoints.size(); ++i) + { + PxJoint* joint = npaRC->mLoopJoints[i]; + NpConstraint* constraint = static_cast(joint->getConstraint()); + Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); + scArtSim->addLoopConstraint(cSim); + } + } +} + +void NpScene::removeArticulation(PxArticulationBase& articulation, bool wakeOnLostTouch) +{ + PX_PROFILE_ZONE("API.removeArticulation", getContextId()); + NP_WRITE_CHECK(this); + + if (removeFromSceneCheck(this, articulation.getScene(), "PxScene::removeArticulation(): Articulation")) + { + NpArticulation& npa = static_cast(articulation); + removeArticulationInternal(npa, wakeOnLostTouch, true); + } +} + +void NpScene::removeArticulationInternal(PxArticulationBase& npa, bool wakeOnLostTouch, bool removeFromAggregate) +{ + PxU32 nbLinks = npa.getNbLinks(); + PX_ASSERT(nbLinks > 0); + + if(removeFromAggregate && npa.getAggregate()) + { + static_cast(npa.getAggregate())->removeArticulationAndReinsert(npa, false); + PX_ASSERT(!npa.getAggregate()); + } + + //!!!AL + // Inefficient. We might want to introduce a LL method to kill the whole LL articulation together with all joints in one go, then + // the order of removing the links/joints does not matter anymore. + + // Remove links & joints + PX_ALLOCA(linkStack, NpArticulationLink*, nbLinks); + linkStack[0] = reinterpret_cast(npa.getImpl())->getLinks()[0]; + PxU32 curLink = 0, stackSize = 1; + + while(curLink < (nbLinks-1)) + { + PX_ASSERT(curLink < stackSize); + NpArticulationLink* l = linkStack[curLink]; + NpArticulationLink*const* children = l->getChildren(); + + for(PxU32 i=0; i < l->getNbChildren(); i++) + { + linkStack[stackSize] = children[i]; + stackSize++; + } + + curLink++; + } + + PxRigidBodyFlags flag; + for(PxI32 j=PxI32(nbLinks); j-- > 0; ) + { + flag |=linkStack[j]->getScbBodyFast().getScBody().getCore().mFlags; + removeArticulationLink(*linkStack[j], wakeOnLostTouch); + } + + if (flag & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + { + PxArticulationImpl* impl = reinterpret_cast(npa.getImpl()); + IG::NodeIndex index = impl->getScbArticulation().getScArticulation().getIslandNodeIndex(); + if (index.isValid()) + mScene.getScScene().resetSpeculativeCCDArticulationLink(index.index()); + } + // Remove articulation + mScene.removeArticulation(reinterpret_cast(npa.getImpl())->getArticulation()); + + + removeFromArticulationList(npa); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::addArticulationLinkBody(NpArticulationLink& link) +{ + mScene.addActor(link.getScbBodyFast(), false, NULL, NULL); + link.getShapeManager().setupAllSceneQuery(this, link, false); +} + +void NpScene::addArticulationLinkConstraint(NpArticulationLink& link) +{ + PxArticulationJointBase* j = link.getInboundJoint(); + if (j) + { + PxArticulationJointImpl* impl = j->getImpl(); + mScene.addArticulationJoint(impl->getScbArticulationJoint()); + } + + link.addConstraintsToScene(); +} + +void NpScene::addArticulationLink(NpArticulationLink& link) +{ + addArticulationLinkBody(link); + addArticulationLinkConstraint(link); +} + +void NpScene::removeArticulationLink(NpArticulationLink& link, bool wakeOnLostTouch) +{ + PxArticulationJointBase* j =link.getInboundJoint(); + + link.removeConstraintsFromScene(); + link.getShapeManager().teardownAllSceneQuery(getSceneQueryManagerFast(), link); + + if (j) + mScene.removeArticulationJoint(j->getImpl()->getScbArticulationJoint()); + + mScene.removeActor(link.getScbBodyFast(), wakeOnLostTouch, false); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::addAggregate(PxAggregate& aggregate) +{ + PX_PROFILE_ZONE("API.addAggregate", getContextId()); + NP_WRITE_CHECK(this); + PX_SIMD_GUARD; + + NpAggregate& np = static_cast(aggregate); + + const PxU32 nb = np.getCurrentSizeFast(); +#if PX_CHECKED + for(PxU32 i=0;iis(); + if(a && !static_cast(a)->checkConstraintValidity()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addAggregate(): Aggregate contains an actor with an invalid constraint!"); + return; + } + } +#endif + + Scb::Aggregate& agg = np.getScbAggregate(); + const Scb::ControlState::Enum cs = agg.getControlState(); + if ((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING) && (agg.getScbScene()->getPxScene() == this))) + { + mScene.addAggregate(agg); + + for(PxU32 i=0;i(NpConnectorType::eBvhStructure, &bvhStructure, 1)) + { + npActor.removeConnector(actor, NpConnectorType::eBvhStructure, bvhStructure, "PxBVHStructure connector could not have been removed!"); + } + + np.addActorInternal(actor, *this, bvhStructure); + + // if a bvh structure was used dec ref count, we increased the ref count when adding the actor connection + if(bvhStructure) + { + bvhStructure->decRefCount(); + } + } + + mAggregates.insert(&aggregate); + } + else + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addAggregate(): Aggregate already assigned to a scene. Call will be ignored!"); +} + +void NpScene::removeAggregate(PxAggregate& aggregate, bool wakeOnLostTouch) +{ + PX_PROFILE_ZONE("API.removeAggregate", getContextId()); + NP_WRITE_CHECK(this); + if(!removeFromSceneCheck(this, aggregate.getScene(), "PxScene::removeAggregate(): Aggregate")) + return; + + NpAggregate& np = static_cast(aggregate); + if(np.getScene()!=this) + return; + + const PxU32 nb = np.getCurrentSizeFast(); + for(PxU32 j=0;jgetType() != PxActorType::eARTICULATION_LINK) + { + Scb::Actor& scb = NpActor::getScbFromPxActor(*a); + + np.getScbAggregate().removeActor(scb, false); // This is only here to make sure the aggregateID gets set to invalid on sync + + removeActorInternal(*a, wakeOnLostTouch, false); + } + else if (a->getScene()) + { + NpArticulationLink& al = static_cast(*a); + PxArticulationBase& npArt = al.getRoot(); + PxArticulationImpl* impl = reinterpret_cast(npArt.getImpl()); + NpArticulationLink* const* links = impl->getLinks(); + for(PxU32 i=0; i < npArt.getNbLinks(); i++) + { + np.getScbAggregate().removeActor(links[i]->getScbActorFast(), false); // This is only here to make sure the aggregateID gets set to invalid on sync + } + + removeArticulationInternal(npArt, wakeOnLostTouch, false); + } + } + + mScene.removeAggregate(np.getScbAggregate()); + + removeFromAggregateList(aggregate); +} + +PxU32 NpScene::getNbAggregates() const +{ + NP_READ_CHECK(this); + return mAggregates.size(); +} + +PxU32 NpScene::getAggregates(PxAggregate** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(this); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mAggregates.getEntries(), mAggregates.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::addCollection(const PxCollection& collection) +{ + PX_PROFILE_ZONE("API.addCollection", getContextId()); + const Cm::Collection& col = static_cast(collection); + + PxU32 nb = col.internalGetNbObjects(); +#if PX_CHECKED + for(PxU32 i=0;iis(); + if(a && !static_cast(a)->checkConstraintValidity()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addCollection(): collection contains an actor with an invalid constraint!"); + return; + } + } +#endif + + Ps::Array actorsToInsert; + actorsToInsert.reserve(nb); + + struct Local + { + static void addActorIfNeeded(PxActor* actor, Ps::Array& actorArray) + { + if(actor->getAggregate()) + return; // The actor will be added when the aggregate is added + actorArray.pushBack(actor); + } + }; + + for(PxU32 i=0;igetConcreteType(); + + //NpArticulationLink, NpArticulationJoint are added with the NpArticulation + //Actors and Articulations that are members of an Aggregate are added with the NpAggregate + + if(serialType==PxConcreteType::eRIGID_DYNAMIC) + { + NpRigidDynamic* np = static_cast(s); + // if pruner structure exists for the actor, actor will be added with the pruner structure + if(!np->getShapeManager().getPruningStructure()) + Local::addActorIfNeeded(np, actorsToInsert); + } + else if(serialType==PxConcreteType::eRIGID_STATIC) + { + NpRigidStatic* np = static_cast(s); + // if pruner structure exists for the actor, actor will be added with the pruner structure + if(!np->getShapeManager().getPruningStructure()) + Local::addActorIfNeeded(np, actorsToInsert); + } + else if(serialType==PxConcreteType::eSHAPE) + { + } + else if(serialType==PxConcreteType::eARTICULATION) + { + NpArticulation* np = static_cast(s); + if (!np->getAggregate()) // The actor will be added when the aggregate is added + { + if (np->mType == PxArticulationBase::eMaximumCoordinate) + addArticulation(static_cast(*np)); + /*else + addArticulation(static_cast(*np));*/ + } + } + else if(serialType==PxConcreteType::eAGGREGATE) + { + NpAggregate* np = static_cast(s); + addAggregate(*np); + } + else if(serialType == PxConcreteType::ePRUNING_STRUCTURE) + { + PxPruningStructure* ps = static_cast(s); + addActors(*ps); + } + } + + if(!actorsToInsert.empty()) + addActorsInternal(&actorsToInsert[0], actorsToInsert.size(), NULL); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 NpScene::getNbActors(PxActorTypeFlags types) const +{ + NP_READ_CHECK(this); + PxU32 nbActors = 0; + + if (types & PxActorTypeFlag::eRIGID_STATIC) + { + for(PxU32 i=mRigidActors.size(); i--;) + { + if (mRigidActors[i]->is()) + nbActors++; + } + } + + if (types & PxActorTypeFlag::eRIGID_DYNAMIC) + { + for(PxU32 i=mRigidActors.size(); i--;) + { + if (mRigidActors[i]->is()) + nbActors++; + } + } + + return nbActors; +} + +PxU32 NpScene::getActors(PxActorTypeFlags types, PxActor** buffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(this); + + PxU32 writeCount = 0; + PxU32 virtualIndex = 0; // PT: virtual index of actor, continuous across different actor containers. + + if(types & (PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC)) + { + const PxU32 size = mRigidActors.size(); + for(PxU32 i=0; (i < size) && (writeCount < bufferSize); i++) + { + if ((types & PxActorTypeFlag::eRIGID_STATIC ) && mRigidActors[i]->is()) + { + if (virtualIndex >= startIndex) + buffer[writeCount++] = mRigidActors[i]; + virtualIndex++; + } + else if ((types & PxActorTypeFlag::eRIGID_DYNAMIC) && mRigidActors[i]->is()) + { + if (virtualIndex >= startIndex) + buffer[writeCount++] = mRigidActors[i]; + virtualIndex++; + } + } + } + + return writeCount; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxActor** NpScene::getActiveActors(PxU32& nbActorsOut) +{ + NP_READ_CHECK(this); + return mScene.getActiveActors(nbActorsOut); +} + +PxActor** NpScene::getFrozenActors(PxU32& nbActorsOut) +{ + NP_READ_CHECK(this); + return mScene.getFrozenActors(nbActorsOut); +} + +void NpScene::setFrozenActorFlag(const bool buildFrozenActors) +{ +#if PX_CHECKED + PxSceneFlags combinedFlag(PxSceneFlag::eENABLE_ACTIVE_ACTORS | PxSceneFlag::eENABLE_STABILIZATION); + + PX_CHECK_AND_RETURN((getFlags() & combinedFlag)== combinedFlag, + "NpScene::setFrozenActorFlag: Cannot raise BuildFrozenActors if PxSceneFlag::eENABLE_STABILIZATION and PxSceneFlag::eENABLE_ACTIVE_ACTORS is not raised!"); +#endif + mBuildFrozenActors = buildFrozenActors; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 NpScene::getNbArticulations() const +{ + NP_READ_CHECK(this); + return mArticulations.size(); +} + +PxU32 NpScene::getArticulations(PxArticulationBase** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(this); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mArticulations.getEntries(), mArticulations.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +PxU32 NpScene::getNbConstraints() const +{ + NP_READ_CHECK(this); + return mConstraints.size(); +} + +PxU32 NpScene::getConstraints(PxConstraint** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(this); + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mConstraints.getEntries(), mConstraints.size()); +} + +/////////////////////////////////////////////////////////////////////////////// + +const PxRenderBuffer& NpScene::getRenderBuffer() +{ + if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + // will be reading the Sc::Scene renderable which is getting written + // during the sim, hence, avoid call while simulation is running. + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxScene::getRenderBuffer() not allowed while simulation is running."); + } + + return mRenderBuffer; +} + +void NpScene::visualize() +{ + NP_READ_CHECK(this); + + PX_PROFILE_ZONE("NpScene::visualize", getContextId()); + + mRenderBuffer.clear(); // clear last frame visualizations + +#if PX_ENABLE_DEBUG_VISUALIZATION + if(getVisualizationParameter(PxVisualizationParameter::eSCALE) == 0.0f) + return; + + Cm::RenderOutput out(mRenderBuffer); + + // Visualize scene axis + const PxReal worldAxes = getVisualizationParameter(PxVisualizationParameter::eWORLD_AXES); + if (worldAxes != 0) + out << Cm::DebugBasis(PxVec3(worldAxes)); + + // Visualize articulations + for(PxU32 i=0;i(mArticulations.getEntries()[i])->visualize(out, this); + + // Visualize rigid actors and rigid bodies + PxRigidActor*const* rigidActors = mRigidActors.begin(); + const PxU32 rigidActorCount = mRigidActors.size(); + + for(PxU32 i=0; i < rigidActorCount; i++) + { + PxRigidActor* a = rigidActors[i]; + if (a->getType() == PxActorType::eRIGID_DYNAMIC) + static_cast(a)->visualize(out, this); + else + static_cast(a)->visualize(out, this); + } + + // Visualize pruning structures + const bool visStatic = getVisualizationParameter(PxVisualizationParameter::eCOLLISION_STATIC) != 0.0f; + const bool visDynamic = getVisualizationParameter(PxVisualizationParameter::eCOLLISION_DYNAMIC) != 0.0f; + //flushQueryUpdates(); // DE7834 + if(visStatic && mSQManager.get(PruningIndex::eSTATIC).pruner()) + mSQManager.get(PruningIndex::eSTATIC).pruner()->visualize(out, PxU32(PxDebugColor::eARGB_BLUE)); + if(visDynamic && mSQManager.get(PruningIndex::eDYNAMIC).pruner()) + mSQManager.get(PruningIndex::eDYNAMIC).pruner()->visualize(out, PxU32(PxDebugColor::eARGB_RED)); + + if(getVisualizationParameter(PxVisualizationParameter::eMBP_REGIONS) != 0.0f) + { + out << PxTransform(PxIdentity); + + const PxU32 nbRegions = mScene.getNbBroadPhaseRegions(); + for(PxU32 i=0;i 0) && (data != NULL)) ), "PxScene::setFilterShaderData(): data pointer must not be NULL unless the specified data size is 0 too and vice versa."); + + mScene.setFilterShaderData(data, dataSize); +} + +const void* NpScene::getFilterShaderData() const +{ + NP_READ_CHECK(this); + return mScene.getFilterShaderData(); +} + +PxU32 NpScene::getFilterShaderDataSize() const +{ + NP_READ_CHECK(this); + return mScene.getFilterShaderDataSize(); +} + +PxSimulationFilterShader NpScene::getFilterShader() const +{ + NP_READ_CHECK(this); + return mScene.getFilterShader(); +} + +PxSimulationFilterCallback* NpScene::getFilterCallback() const +{ + NP_READ_CHECK(this); + return mScene.getFilterCallback(); +} + +void NpScene::resetFiltering(PxActor& actor) +{ + NP_WRITE_CHECK(this); + + PX_CHECK_AND_RETURN(NpActor::getAPIScene(actor) && (NpActor::getAPIScene(actor) == this), "PxScene::resetFiltering(): actor not in scene!"); + + switch(actor.getConcreteType()) + { + case PxConcreteType::eRIGID_STATIC: + { + NpRigidStatic& npStatic = static_cast(actor); + npStatic.resetFiltering(npStatic.getScbRigidStaticFast(), NULL, 0); + } + break; + + case PxConcreteType::eRIGID_DYNAMIC: + { + NpRigidDynamic& npDynamic = static_cast(actor); + if (npDynamic.resetFiltering(npDynamic.getScbBodyFast(), NULL, 0)) + npDynamic.wakeUpInternal(); + } + break; + + case PxConcreteType::eARTICULATION_LINK: + { + NpArticulationLink& npLink = static_cast(actor); + if (npLink.resetFiltering(npLink.getScbBodyFast(), NULL, 0)) + { + PxArticulationImpl* impl = reinterpret_cast(npLink.getRoot().getImpl()); + impl->wakeUpInternal(false, true); + } + } + break; + + default: + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxScene::resetFiltering(): only PxRigidActor supports this operation!"); + } +} + +void NpScene::resetFiltering(PxRigidActor& actor, PxShape*const* shapes, PxU32 shapeCount) +{ + NP_WRITE_CHECK(this); + PX_CHECK_AND_RETURN(NpActor::getAPIScene(actor) && (NpActor::getAPIScene(actor) == this), "PxScene::resetFiltering(): actor not in scene!"); + PX_SIMD_GUARD; + + switch(actor.getConcreteType()) + { + case PxConcreteType::eRIGID_STATIC: + { + NpRigidStatic& npStatic = static_cast(actor); + npStatic.resetFiltering(npStatic.getScbRigidStaticFast(), shapes, shapeCount); + } + break; + + case PxConcreteType::eRIGID_DYNAMIC: + { + NpRigidDynamic& npDynamic = static_cast(actor); + if (npDynamic.resetFiltering(npDynamic.getScbBodyFast(), shapes, shapeCount)) + npDynamic.wakeUpInternal(); + } + break; + + case PxConcreteType::eARTICULATION_LINK: + { + NpArticulationLink& npLink = static_cast(actor); + if (npLink.resetFiltering(npLink.getScbBodyFast(), shapes, shapeCount)) + { + PxArticulationImpl* impl = reinterpret_cast(npLink.getRoot().getImpl()); + impl->wakeUpInternal(false, true); + } + } + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +PxPhysics& NpScene::getPhysics() +{ + return NpPhysics::getInstance(); +} + +void NpScene::updateDirtyShaders() +{ + PX_PROFILE_ZONE("Sim.updateDirtyShaders", getContextId()); + // this should continue to be done in the Np layer even after SC has taken over + // all vital simulation functions, because it needs to complete before simulate() + // returns to the application + + // However, the implementation needs fixing so that it does work proportional to + // the number of dirty shaders + + PxConstraint*const* constraints = mConstraints.getEntries(); + for(PxU32 i=0;i(constraints[i])->updateConstants(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +void NpScene::simulateOrCollide(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation, const char* invalidCallMsg, Sc::SimulationStage::Enum simStage) +{ + PX_SIMD_GUARD; + + { + // write guard must end before simulation kicks off worker threads + // otherwise the simulation callbacks could overlap with this function + // and perform API reads,triggering an error + NP_WRITE_CHECK(this); + + PX_PROFILE_START_CROSSTHREAD("Basic.simulate", getContextId()); + + if(getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + //fetchResult doesn't get called + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, invalidCallMsg); + return; + } + + PX_CHECK_AND_RETURN(elapsedTime > 0, "PxScene::collide/simulate: The elapsed time must be positive!"); + + PX_CHECK_AND_RETURN((reinterpret_cast(scratchBlock)&15) == 0, "PxScene::simulate: scratch block must be 16-byte aligned!"); + + PX_CHECK_AND_RETURN((scratchBlockSize&16383) == 0, "PxScene::simulate: scratch block size must be a multiple of 16K"); + +#if PX_SUPPORT_PVD + //signal the frame is starting. + mScene.getScenePvdClient().frameStart(elapsedTime); +#endif + +#if PX_ENABLE_DEBUG_VISUALIZATION + visualize(); +#endif + + updateDirtyShaders(); + +#if PX_SUPPORT_PVD + mScene.getScenePvdClient().updateJoints(); +#endif + + mScene.getScScene().setScratchBlock(scratchBlock, scratchBlockSize); + + mElapsedTime = elapsedTime; + if (simStage == Sc::SimulationStage::eCOLLIDE) + mScene.getScScene().setElapsedTime(elapsedTime); + + mControllingSimulation = controlSimulation; + + //sync all the material events + NpPhysics& physics = static_cast(this->getPhysics()); + NpMaterialManager& manager = physics.getMaterialManager(); + NpMaterial** materials = manager.getMaterials(); + mScene.updateLowLevelMaterial(materials); + + setSimulationStage(simStage); + mScene.setPhysicsBuffering(true); + mHasSimulatedOnce = true; + } + + { + PX_PROFILE_ZONE("Sim.taskFrameworkSetup", getContextId()); + + if (controlSimulation) + { + { + PX_PROFILE_ZONE("Sim.resetDependencies", getContextId()); + // Only reset dependencies, etc if we own the TaskManager. Will be false + // when an NpScene is controlled by an APEX scene. + mTaskManager->resetDependencies(); + } + mTaskManager->startSimulation(); + } + + if (simStage == Sc::SimulationStage::eCOLLIDE) + { + mCollisionCompletion.setContinuation(*mTaskManager, completionTask); + mSceneCollide.setContinuation(&mCollisionCompletion); + //Initialize scene completion task + mSceneCompletion.setContinuation(*mTaskManager, NULL); + } + else + { + mSceneCompletion.setContinuation(*mTaskManager, completionTask); + mSceneExecution.setContinuation(*mTaskManager, &mSceneCompletion); + } + + if (simStage == Sc::SimulationStage::eCOLLIDE) + { + mCollisionCompletion.removeReference(); + mSceneCollide.removeReference(); + } + else + { + mSceneCompletion.removeReference(); + mSceneExecution.removeReference(); + } + } +} + +void NpScene::simulate(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation) +{ + simulateOrCollide( elapsedTime, completionTask, scratchBlock, scratchBlockSize, controlSimulation, + "PxScene::simulate: Simulation is still processing last simulate call, you should call fetchResults()!", Sc::SimulationStage::eADVANCE); +} + +void NpScene::advance( physx::PxBaseTask* completionTask) +{ + NP_WRITE_CHECK(this); + //issue error if advance() doesn't get called between fetchCollision() and fetchResult() + if(getSimulationStage() != Sc::SimulationStage::eFETCHCOLLIDE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::advance: advance() called illegally! advance() needed to be called after fetchCollision() and before fetchResult()!!"); + return; + } + + //apply buffering for forces, velocities, kinematic targets and wake-up events + mScene.syncWriteThroughProperties(); + + //if mSimulateStage == eFETCHCOLLIDE, which means collide() has been kicked off and finished running, we can run advance() safely + { + //change the mSimulateStaget to eADVANCE to indicate the next stage to run is fetchResult() + setSimulationStage(Sc::SimulationStage::eADVANCE); + + { + PX_PROFILE_ZONE("Sim.taskFrameworkSetup", getContextId()); + + mSceneCompletion.setDependent(completionTask); + mSceneAdvance.setContinuation(*mTaskManager, &mSceneCompletion); + mSceneCompletion.removeReference(); + mSceneAdvance.removeReference(); + } + } +} + +void NpScene::collide(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation) +{ + simulateOrCollide( elapsedTime, + completionTask, + scratchBlock, + scratchBlockSize, + controlSimulation, + "PxScene::collide: collide() called illegally! If it isn't the first frame, collide() needed to be called between fetchResults() and fetchCollision(). Otherwise, collide() needed to be called before fetchCollision()", + Sc::SimulationStage::eCOLLIDE); +} + +bool NpScene::checkResultsInternal(bool block) +{ + PX_PROFILE_ZONE("Basic.checkResults", getContextId()); + return mPhysicsDone.wait(block ? Ps::Sync::waitForever : 0); +} + +bool NpScene::checkCollisionInternal(bool block) +{ + PX_PROFILE_ZONE("Basic.checkCollision", getContextId()); + return mCollisionDone.wait(block ? Ps::Sync::waitForever : 0); +} + +bool NpScene::checkResults(bool block) +{ + return checkResultsInternal(block); +} + +bool NpScene::checkCollision(bool block) +{ + return checkCollisionInternal(block); +} + +void NpScene::fireOutOfBoundsCallbacks() +{ + PX_PROFILE_ZONE("Sim.fireOutOfBoundsCallbacks", getContextId()); + + // Fire broad-phase callbacks + { + Sc::Scene& scene = mScene.getScScene(); + using namespace physx::Sc; + + bool outputWarning = scene.fireOutOfBoundsCallbacks(); + + // Aggregates + { + void** outAgg = scene.getOutOfBoundsAggregates(); + const PxU32 nbOut1 = scene.getNbOutOfBoundsAggregates(); + + PxBroadPhaseCallback* cb = scene.getBroadPhaseCallback(); + + for(PxU32 i=0;i(outAgg[i]); + NpAggregate* np = static_cast(px); + if(np->getScbAggregate().getControlState()==Scb::ControlState::eREMOVE_PENDING) + continue; + + if(cb) + cb->onObjectOutOfBounds(*px); + else + outputWarning = true; + } + scene.clearOutOfBoundsAggregates(); + } + + if(outputWarning) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "At least one object is out of the broadphase bounds. To manage those objects, define a PxBroadPhaseCallback for each used client."); + } +} + +bool NpScene::fetchCollision(bool block) +{ + if(getSimulationStage() != Sc::SimulationStage::eCOLLIDE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::fetchCollision: fetchCollision() should be called after collide() and before advance()!"); + return false; + } + + //if collision isn't finish running (and block is false), then return false + if(!checkCollisionInternal(block)) + return false; + + // take write check *after* collision() finished, otherwise + // we will block fetchCollision() from using the API + NP_WRITE_CHECK_NOREENTRY(this); + + setSimulationStage(Sc::SimulationStage::eFETCHCOLLIDE); + + return true; +} + +class SqRefFinder: public Sc::SqRefFinder +{ +public: + virtual Sq::PrunerHandle find(const PxRigidBody* body, const PxShape* shape) + { + const Sq::PrunerData prunerdata = NpActor::getShapeManager(*body)->findSceneQueryData(*static_cast(shape)); + return Sq::getPrunerHandle(prunerdata); + } +private: +}; + +// The order of the following operations is important! +// 1. Process object deletions which were carried out while the simulation was running (since these effect contact and trigger reports) +// 2. Write contact reports to global stream (taking pending deletions into account), clear some simulation buffers (deleted objects etc.), ... +// 3. Send reports which have to be done before the data is synced (contact & trigger reports etc.) such that the user gets the old state. +// 4. Mark the simulation as not running internally to allow reading data which should not be read otherwise +// 5. Synchronize the simulation and user state +// 6. Fire callbacks which need to reflect the synchronized object state + +void NpScene::fetchResultsPreContactCallbacks() +{ +#if PX_SUPPORT_PVD + mScene.getScenePvdClient().updateContacts(); +#endif + + mScene.prepareOutOfBoundsCallbacks(); + mScene.processPendingRemove(); + mScene.endSimulation(); + + { + PX_PROFILE_ZONE("Sim.fireCallbacksPreSync", getContextId()); + fireOutOfBoundsCallbacks(); // fire out-of-bounds callbacks + mScene.fireBrokenConstraintCallbacks(); + mScene.fireTriggerCallbacks(); + } +} + +void NpScene::fetchResultsPostContactCallbacks() +{ + mScene.postCallbacksPreSync(); + mScene.syncEntireScene(); // double buffering + + SqRefFinder sqRefFinder; + mScene.getScScene().syncSceneQueryBounds(mSQManager.getDynamicBoundsSync(), sqRefFinder); + + mSQManager.updateCompoundActors(mScene.getScScene().getActiveCompoundBodiesArray(), mScene.getScScene().getNumActiveCompoundBodies()); + mSQManager.afterSync(getSceneQueryUpdateModeFast()); + +#if PX_SUPPORT_PVD + mScene.getScenePvdClient().updateSceneQueries(); + + getSingleSqCollector().clear(); + getBatchedSqCollector().clear(); +#endif + + // fire sleep and wake-up events + // we do this after buffer-swapping so that the events have the new state + { + PX_PROFILE_ZONE("Sim.fireCallbacksPostSync", getContextId()); + mScene.fireCallBacksPostSync(); + } + + mScene.postReportsCleanup(); + + // build the list of active actors + { + PX_PROFILE_ZONE("Sim.buildActiveActors", getContextId()); + + const bool buildActiveActors = mScene.getFlags() & PxSceneFlag::eENABLE_ACTIVE_ACTORS; + + if (buildActiveActors && mBuildFrozenActors) + mScene.buildActiveAndFrozenActors(); + else if (buildActiveActors) + mScene.buildActiveActors(); + } + + mRenderBuffer.append(mScene.getScScene().getRenderBuffer()); + + PX_ASSERT(getSimulationStage() != Sc::SimulationStage::eCOMPLETE); + if (mControllingSimulation) + { + mTaskManager->stopSimulation(); + } + + setSimulationStage(Sc::SimulationStage::eCOMPLETE); + + mPhysicsDone.reset(); // allow Physics to run again + mCollisionDone.reset(); +} + +bool NpScene::fetchResults(bool block, PxU32* errorState) +{ + if(getSimulationStage() != Sc::SimulationStage::eADVANCE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::fetchResults: fetchResults() called illegally! It must be called after advance() or simulate()"); + return false; + } + + if(!checkResultsInternal(block)) + return false; + + { + PX_SIMD_GUARD; + + // take write check *after* simulation has finished, otherwise + // we will block simulation callbacks from using the API + // disallow re-entry to detect callbacks making write calls + NP_WRITE_CHECK_NOREENTRY(this); + + // we use cross thread profile here, to show the event in cross thread view + // PT: TODO: why do we want to show it in the cross thread view? + PX_PROFILE_START_CROSSTHREAD("Basic.fetchResults", getContextId()); + PX_PROFILE_ZONE("Sim.fetchResults", getContextId()); + + fetchResultsPreContactCallbacks(); + + { + // PT: TODO: why a cross-thread event here? + PX_PROFILE_START_CROSSTHREAD("Basic.processCallbacks", getContextId()); + mScene.fireQueuedContactCallbacks(); + PX_PROFILE_STOP_CROSSTHREAD("Basic.processCallbacks", getContextId()); + } + + fetchResultsPostContactCallbacks(); + + PX_PROFILE_STOP_CROSSTHREAD("Basic.fetchResults", getContextId()); + PX_PROFILE_STOP_CROSSTHREAD("Basic.simulate", getContextId()); + + if(errorState) + *errorState = 0; + } + +#if PX_SUPPORT_PVD + { + PX_SIMD_GUARD; + mScene.getScenePvdClient().frameEnd(); + } +#endif + return true; +} + +bool NpScene::fetchResultsStart(const PxContactPairHeader*& contactPairs, PxU32& nbContactPairs, bool block) +{ + if (getSimulationStage() != Sc::SimulationStage::eADVANCE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PXScene::fetchResultsStart: fetchResultsStart() called illegally! It must be called after advance() or simulate()"); + return false; + } + + if (!checkResultsInternal(block)) + return false; + + PX_SIMD_GUARD; + NP_WRITE_CHECK(this); + + // we use cross thread profile here, to show the event in cross thread view + PX_PROFILE_START_CROSSTHREAD("Basic.fetchResults", getContextId()); + PX_PROFILE_ZONE("Sim.fetchResultsStart", getContextId()); + + fetchResultsPreContactCallbacks(); + const Ps::Array& pairs = mScene.getQueuedContactPairHeaders(); + nbContactPairs = pairs.size(); + contactPairs = pairs.begin(); + + mBetweenFetchResults = true; + return true; +} + +void NpContactCallbackTask::setData(NpScene* scene, const PxContactPairHeader* contactPairHeaders, const uint32_t nbContactPairHeaders) +{ + mScene = scene; + mContactPairHeaders = contactPairHeaders; + mNbContactPairHeaders = nbContactPairHeaders; +} + +void NpContactCallbackTask::run() +{ + physx::PxSimulationEventCallback* callback = mScene->getSimulationEventCallback(); + if(!callback) + return; + + mScene->lockRead(); + for(uint32_t i=0; ionContact(pairHeader, pairHeader.pairs, pairHeader.nbPairs); + } + mScene->unlockRead(); +} + +void NpScene::processCallbacks(physx::PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Basic.processCallbacks", getContextId()); + PX_PROFILE_ZONE("Sim.processCallbacks", getContextId()); + //ML: because Apex destruction callback isn't thread safe so that we make this run single thread first + const Ps::Array& pairs = mScene.getQueuedContactPairHeaders(); + const PxU32 nbPairs = pairs.size(); + const PxContactPairHeader* contactPairs = pairs.begin(); + const PxU32 nbToProcess = 256; + + Cm::FlushPool* flushPool = mScene.getScScene().getFlushPool(); + + for (PxU32 i = 0; i < nbPairs; i += nbToProcess) + { + NpContactCallbackTask* task = PX_PLACEMENT_NEW(flushPool->allocate(sizeof(NpContactCallbackTask)), NpContactCallbackTask)(); + task->setData(this, contactPairs+i, PxMin(nbToProcess, nbPairs - i)); + task->setContinuation(continuation); + task->removeReference(); + } +} + +void NpScene::fetchResultsFinish(PxU32* errorState) +{ + { + PX_SIMD_GUARD; + PX_PROFILE_STOP_CROSSTHREAD("Basic.processCallbacks", getContextId()); + PX_PROFILE_ZONE("Basic.fetchResultsFinish", getContextId()); + + mBetweenFetchResults = false; + NP_WRITE_CHECK(this); + + fetchResultsPostContactCallbacks(); + + if (errorState) + *errorState = 0; + + PX_PROFILE_STOP_CROSSTHREAD("Basic.fetchResults", getContextId()); + PX_PROFILE_STOP_CROSSTHREAD("Basic.simulate", getContextId()); + } + +#if PX_SUPPORT_PVD + mScene.getScenePvdClient().frameEnd(); +#endif +} + +void NpScene::flushSimulation(bool sendPendingReports) +{ + PX_PROFILE_ZONE("API.flushSimulation", getContextId()); + NP_WRITE_CHECK_NOREENTRY(this); + PX_SIMD_GUARD; + + if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxScene::flushSimulation(): This call is not allowed while the simulation is running. Call will be ignored"); + return; + } + + mScene.flush(sendPendingReports); + mSQManager.flushMemory(); + + //!!! TODO: Shrink all NpObject lists? +} + +void NpScene::flushQueryUpdates() +{ + // DS: how do we profile const methods?????? + PX_PROFILE_ZONE("API.flushQueryUpdates", getContextId()); + NP_READ_CHECK(this); + PX_SIMD_GUARD; + + if (getSimulationStage() != Sc::SimulationStage::eCOMPLETE) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxScene::flushQueryUpdates(): This call is not allowed while the simulation is running. Call will be ignored"); + return; + } + + mSQManager.flushUpdates(); +} + +/* +Replaces finishRun() with the addition of appropriate thread sync(pulled out of PhysicsThread()) + +Note: this function can be called from the application thread or the physics thread, depending on the +scene flags. +*/ +void NpScene::executeScene(PxBaseTask* continuation) +{ + mScene.simulate(mElapsedTime, continuation); +} + +void NpScene::executeCollide(PxBaseTask* continuation) +{ + mScene.collide(mElapsedTime, continuation); +} + +void NpScene::executeAdvance(PxBaseTask* continuation) +{ + mScene.advance(mElapsedTime, continuation); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::addMaterial(const NpMaterial& mat) +{ + mScene.addMaterial(mat.getScMaterial()); +} + +void NpScene::updateMaterial(const NpMaterial& mat) +{ + //PxU32 index = mat.getTableIndex(); + mScene.updateMaterial(mat.getScMaterial()); +} + +void NpScene::removeMaterial(const NpMaterial& mat) +{ + //PxU32 index = mat.getTableIndex(); + mScene.removeMaterial(mat.getScMaterial()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance) +{ + NP_WRITE_CHECK(this); + PX_CHECK_AND_RETURN((group1 < PX_MAX_DOMINANCE_GROUP && group2 < PX_MAX_DOMINANCE_GROUP), + "PxScene::setDominanceGroupPair: invalid params! Groups must be <= 31!"); + //can't change matrix diagonal + PX_CHECK_AND_RETURN(group1 != group2, "PxScene::setDominanceGroupPair: invalid params! Groups must be unequal! Can't change matrix diagonal!"); + PX_CHECK_AND_RETURN( + ((dominance.dominance0) == 1.0f && (dominance.dominance1 == 1.0f)) + || ((dominance.dominance0) == 1.0f && (dominance.dominance1 == 0.0f)) + || ((dominance.dominance0) == 0.0f && (dominance.dominance1 == 1.0f)) + , "PxScene::setDominanceGroupPair: invalid params! dominance must be one of (1,1), (1,0), or (0,1)!"); + + mScene.setDominanceGroupPair(group1, group2, dominance); +} + +PxDominanceGroupPair NpScene::getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const +{ + NP_READ_CHECK(this); + PX_CHECK_AND_RETURN_VAL((group1 < PX_MAX_DOMINANCE_GROUP && group2 < PX_MAX_DOMINANCE_GROUP), + "PxScene::getDominanceGroupPair: invalid params! Groups must be <= 31!", PxDominanceGroupPair(PxU8(1u), PxU8(1u))); + return mScene.getDominanceGroupPair(group1, group2); +} + +/////////////////////////////////////////////////////////////////////////////// + +#if PX_SUPPORT_GPU_PHYSX + +void NpScene::updatePhysXIndicator() +{ + Ps::IntBool isGpu = mScene.getScScene().isUsingGpuRigidBodies(); + + mPhysXIndicator.setIsGpu(isGpu != 0); +} +#endif //PX_SUPPORT_GPU_PHYSX + + +/////////////////////////////////////////////////////////////////////////////// + +void NpScene::setSceneQueryUpdateMode(PxSceneQueryUpdateMode::Enum updateMode) +{ + NP_WRITE_CHECK(this); + mSceneQueryUpdateMode = updateMode; +} + +PxSceneQueryUpdateMode::Enum NpScene::getSceneQueryUpdateMode() const +{ + NP_READ_CHECK(this); + return mSceneQueryUpdateMode; +} + +void NpScene::setDynamicTreeRebuildRateHint(PxU32 dynamicTreeRebuildRateHint) +{ + PX_CHECK_AND_RETURN((dynamicTreeRebuildRateHint >= 4), "PxScene::setDynamicTreeRebuildRateHint(): Param has to be >= 4!"); + mSQManager.setDynamicTreeRebuildRateHint(dynamicTreeRebuildRateHint); +} + +PxU32 NpScene::getDynamicTreeRebuildRateHint() const +{ + NP_READ_CHECK(this); + return mSQManager.getDynamicTreeRebuildRateHint(); +} + +void NpScene::forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure) +{ + PX_PROFILE_ZONE("API.forceDynamicTreeRebuild", getContextId()); + NP_WRITE_CHECK(this); + PX_SIMD_GUARD; + mSQManager.forceDynamicTreeRebuild(rebuildStaticStructure, rebuildDynamicStructure); +} + +void NpScene::setSolverBatchSize(PxU32 solverBatchSize) +{ + NP_WRITE_CHECK(this); + mScene.setSolverBatchSize(solverBatchSize); +} + +PxU32 NpScene::getSolverBatchSize(void) const +{ + NP_READ_CHECK(this); + // get from our local copy + return mScene.getSolverBatchSize(); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NpScene::setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) +{ + NP_WRITE_CHECK(this); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(value), "PxScene::setVisualizationParameter: value is not valid.", false); + + if (param >= PxVisualizationParameter::eNUM_VALUES) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "setVisualizationParameter: parameter out of range."); + return false; + } + else if (value < 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "setVisualizationParameter: value must be larger or equal to 0."); + return false; + } + else + { + mScene.setVisualizationParameter(param, value); + return true; + } +} + +PxReal NpScene::getVisualizationParameter(PxVisualizationParameter::Enum param) const +{ + if (param < PxVisualizationParameter::eNUM_VALUES) + return mScene.getVisualizationParameter(param); + else + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "getVisualizationParameter: param is not an enum."); + + return 0.0f; +} + +void NpScene::setVisualizationCullingBox(const PxBounds3& box) +{ + NP_WRITE_CHECK(this); + PX_CHECK_MSG(box.isValid(), "PxScene::setVisualizationCullingBox(): invalid bounds provided!"); + mScene.setVisualizationCullingBox(box); +} + +PxBounds3 NpScene::getVisualizationCullingBox() const +{ + NP_READ_CHECK(this); + const PxBounds3& bounds = mScene.getVisualizationCullingBox(); + PX_ASSERT(bounds.isValid()); + return bounds; +} + +void NpScene::setNbContactDataBlocks(PxU32 numBlocks) +{ + PX_CHECK_AND_RETURN((getSimulationStage() == Sc::SimulationStage::eCOMPLETE), + "PxScene::setNbContactDataBlock: This call is not allowed while the simulation is running. Call will be ignored!"); + + mScene.getScScene().setNbContactDataBlocks(numBlocks); +} + +PxU32 NpScene::getNbContactDataBlocksUsed() const +{ + PX_CHECK_AND_RETURN_VAL((getSimulationStage() == Sc::SimulationStage::eCOMPLETE), + "PxScene::getNbContactDataBlocksUsed: This call is not allowed while the simulation is running. Returning 0.", 0); + + return mScene.getScScene().getNbContactDataBlocksUsed(); +} + +PxU32 NpScene::getMaxNbContactDataBlocksUsed() const +{ + PX_CHECK_AND_RETURN_VAL((getSimulationStage() == Sc::SimulationStage::eCOMPLETE), + "PxScene::getMaxNbContactDataBlocksUsed: This call is not allowed while the simulation is running. Returning 0.", 0); + + return mScene.getScScene().getMaxNbContactDataBlocksUsed(); +} + +PxU32 NpScene::getTimestamp() const +{ + return mScene.getScScene().getTimeStamp(); +} + +PxU32 NpScene::getSceneQueryStaticTimestamp() const +{ + return mSQManager.get(PruningIndex::eSTATIC).timestamp(); +} + +PxCpuDispatcher* NpScene::getCpuDispatcher() const +{ + return getTaskManager()->getCpuDispatcher(); +} + +PxGpuDispatcher* NpScene::getGpuDispatcher() const +{ + return getTaskManager()->getGpuDispatcher(); +} + +PxPruningStructureType::Enum NpScene::getStaticStructure() const +{ + return mSQManager.get(PruningIndex::eSTATIC).type(); +} + +PxPruningStructureType::Enum NpScene::getDynamicStructure() const +{ + return mSQManager.get(PruningIndex::eDYNAMIC).type(); +} + +PxReal NpScene::getFrictionOffsetThreshold() const +{ + return mScene.getScScene().getFrictionOffsetThreshold(); +} + +PxU32 NpScene::getContactReportStreamBufferSize() const +{ + return mScene.getScScene().getDefaultContactReportStreamBufferSize(); +} + +#if PX_CHECKED +void NpScene::checkPositionSanity(const PxRigidActor& a, const PxTransform& pose, const char* fnName) const +{ + if(!mSanityBounds.contains(pose.p)) + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "%s: actor pose for %lp is outside sanity bounds\n", fnName, &a); +} +#endif + +namespace +{ + struct ThreadReadWriteCount + { + ThreadReadWriteCount(const size_t data) + : readDepth(data & 0xFF), + writeDepth((data >> 8) & 0xFF), + readLockDepth((data >> 16) & 0xFF), + writeLockDepth((data >> 24) & 0xFF) + { + + } + + size_t getData() const { return size_t(writeLockDepth) << 24 | size_t(readLockDepth) << 16 | size_t(writeDepth) << 8 | size_t(readDepth); } + + PxU8 readDepth; // depth of re-entrant reads + PxU8 writeDepth; // depth of re-entrant writes + + PxU8 readLockDepth; // depth of read-locks + PxU8 writeLockDepth; // depth of write-locks + }; +} + +#if NP_ENABLE_THREAD_CHECKS + +NpScene::StartWriteResult::Enum NpScene::startWrite(bool allowReentry) +{ + PX_COMPILE_TIME_ASSERT(sizeof(ThreadReadWriteCount) == 4); + + if (mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) + { + ThreadReadWriteCount localCounts(TlsGetValue(mThreadReadWriteDepth)); + + if (mBetweenFetchResults) + return StartWriteResult::eIN_FETCHRESULTS; + + // ensure we already have the write lock + return localCounts.writeLockDepth > 0 ? StartWriteResult::eOK : StartWriteResult::eNO_LOCK; + } + + { + ThreadReadWriteCount localCounts(TlsGetValue(mThreadReadWriteDepth)); + StartWriteResult::Enum result; + + if (mBetweenFetchResults) + result = StartWriteResult::eIN_FETCHRESULTS; + + // check we are the only thread reading (allows read->write order on a single thread) and no other threads are writing + else if (mConcurrentReadCount != localCounts.readDepth || mConcurrentWriteCount != localCounts.writeDepth) + result = StartWriteResult::eRACE_DETECTED; + + else + result = StartWriteResult::eOK; + + // increment shared write counter + Ps::atomicIncrement(&mConcurrentWriteCount); + + // in the normal case (re-entry is allowed) then we simply increment + // the writeDepth by 1, otherwise (re-entry is not allowed) increment + // by 2 to force subsequent writes to fail by creating a mismatch between + // the concurrent write counter and the local counter, any value > 1 will do + localCounts.writeDepth += allowReentry ? 1 : 2; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + if (result != StartWriteResult::eOK) + Ps::atomicIncrement(&mConcurrentErrorCount); + + return result; + } +} + +void NpScene::stopWrite(bool allowReentry) +{ + if (!(mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK)) + { + Ps::atomicDecrement(&mConcurrentWriteCount); + + // decrement depth of writes for this thread + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + + // see comment in startWrite() + if (allowReentry) + localCounts.writeDepth--; + else + localCounts.writeDepth-=2; + + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + } +} + +bool NpScene::startRead() const +{ + if (mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK) + { + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + + // ensure we already have the write or read lock + return localCounts.writeLockDepth > 0 || localCounts.readLockDepth > 0; + } + else + { + Ps::atomicIncrement(&mConcurrentReadCount); + + // update current threads read depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + localCounts.readDepth++; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + // success if the current thread is already performing a write (API re-entry) or no writes are in progress + bool success = (localCounts.writeDepth > 0 || mConcurrentWriteCount == 0); + + if (!success) + Ps::atomicIncrement(&mConcurrentErrorCount); + + return success; + } +} + +void NpScene::stopRead() const +{ + if (!(mScene.getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK)) + { + Ps::atomicDecrement(&mConcurrentReadCount); + + // update local threads read depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + localCounts.readDepth--; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + } +} + +#else + +NpScene::StartWriteResult::Enum NpScene::startWrite(bool) { PX_ASSERT(0); return NpScene::StartWriteResult::eOK; } +void NpScene::stopWrite(bool) {} + +bool NpScene::startRead() const { PX_ASSERT(0); return false; } +void NpScene::stopRead() const {} + +#endif // NP_ENABLE_THREAD_CHECKS + +void NpScene::lockRead(const char* /*file*/, PxU32 /*line*/) +{ + // increment this threads read depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + localCounts.readLockDepth++; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + // if we are the current writer then increment the reader count but don't actually lock (allow reading from threads with write ownership) + if(localCounts.readLockDepth == 1) + mRWLock.lockReader(mCurrentWriter != Thread::getId()); +} + +void NpScene::unlockRead() +{ + // increment this threads read depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + if(localCounts.readLockDepth < 1) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::unlockRead() called without matching call to PxScene::lockRead(), behaviour will be undefined."); + return; + } + localCounts.readLockDepth--; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + // only unlock on last read + if(localCounts.readLockDepth == 0) + mRWLock.unlockReader(); +} + +void NpScene::lockWrite(const char* file, PxU32 line) +{ + // increment this threads write depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + if (localCounts.writeLockDepth == 0 && localCounts.readLockDepth > 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, file?file:__FILE__, file?int(line):__LINE__, "PxScene::lockWrite() detected after a PxScene::lockRead(), lock upgrading is not supported, behaviour will be undefined."); + return; + } + localCounts.writeLockDepth++; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + // only lock on first call + if (localCounts.writeLockDepth == 1) + mRWLock.lockWriter(); + + PX_ASSERT(mCurrentWriter == 0 || mCurrentWriter == Thread::getId()); + + // set ourselves as the current writer + mCurrentWriter = Thread::getId(); +} + +void NpScene::unlockWrite() +{ + // increment this thread's write depth + ThreadReadWriteCount localCounts (TlsGetValue(mThreadReadWriteDepth)); + if (localCounts.writeLockDepth < 1) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::unlockWrite() called without matching call to PxScene::lockWrite(), behaviour will be undefined."); + return; + } + localCounts.writeLockDepth--; + TlsSetValue(mThreadReadWriteDepth, localCounts.getData()); + + PX_ASSERT(mCurrentWriter == Thread::getId()); + + if (localCounts.writeLockDepth == 0) + { + mCurrentWriter = 0; + mRWLock.unlockWriter(); + } +} + +PxReal NpScene::getWakeCounterResetValue() const +{ + NP_READ_CHECK(this); + + return getWakeCounterResetValueInteral(); +} + +static PX_FORCE_INLINE void shiftRigidActor(PxRigidActor* a, const PxVec3& shift) +{ + PxActorType::Enum t = a->getType(); + if (t == PxActorType::eRIGID_DYNAMIC) + { + NpRigidDynamic* rd = static_cast(a); + rd->getScbBodyFast().onOriginShift(shift); + } + else if (t == PxActorType::eRIGID_STATIC) + { + NpRigidStatic* rs = static_cast(a); + rs->getScbRigidStaticFast().onOriginShift(shift); + } + else + { + PX_ASSERT(t == PxActorType::eARTICULATION_LINK); + NpArticulationLink* al = static_cast(a); + al->getScbBodyFast().onOriginShift(shift); + } +} + +void NpScene::shiftOrigin(const PxVec3& shift) +{ + PX_PROFILE_ZONE("API.shiftOrigin", getContextId()); + NP_WRITE_CHECK(this); + + if(mScene.isPhysicsBuffering()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::shiftOrigin() not allowed while simulation is running. Call will be ignored."); + return; + } + + PX_SIMD_GUARD; + + const PxU32 prefetchLookAhead = 4; + PxU32 rigidCount = mRigidActors.size(); + PxRigidActor*const* rigidActors = mRigidActors.begin(); + PxU32 batchIterCount = rigidCount / prefetchLookAhead; + + PxU32 idx = 0; + for(PxU32 i=0; i < batchIterCount; i++) + { + // prefetch elements for next batch + if (i < (batchIterCount-1)) + { + Ps::prefetchLine(rigidActors[idx + prefetchLookAhead]); + Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead]) + 128); // for the buffered pose + Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 1]); + Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead + 1]) + 128); + Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 2]); + Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead + 2]) + 128); + Ps::prefetchLine(rigidActors[idx + prefetchLookAhead + 3]); + Ps::prefetchLine(reinterpret_cast(rigidActors[idx + prefetchLookAhead + 3]) + 128); + } + else + { + for(PxU32 k=(idx + prefetchLookAhead); k < rigidCount; k++) + { + Ps::prefetchLine(rigidActors[k]); + Ps::prefetchLine(reinterpret_cast(rigidActors[k]) + 128); + } + } + + for(PxU32 j=idx; j < (idx + prefetchLookAhead); j++) + { + shiftRigidActor(rigidActors[j], shift); + } + + idx += prefetchLookAhead; + } + // process remaining objects + for(PxU32 i=idx; i < rigidCount; i++) + { + shiftRigidActor(rigidActors[i], shift); + } + + PxArticulationBase*const* articulations = mArticulations.getEntries(); + for(PxU32 i=0; i < mArticulations.size(); i++) + { + PxArticulationBase* np = (articulations[i]); + + NpArticulationLink*const* links = reinterpret_cast(np->getImpl())->getLinks(); + + for(PxU32 j=0; j < np->getNbLinks(); j++) + { + shiftRigidActor(links[j], shift); + } + } + + + mScene.shiftOrigin(shift); + + + // + // shift scene query related data structures + // + mSQManager.shiftOrigin(shift); + +#if PX_ENABLE_DEBUG_VISUALIZATION + // + // debug visualization + // + mRenderBuffer.shift(-shift); +#endif +} + +#if PX_SUPPORT_PVD +PxPvdSceneClient* NpScene::getScenePvdClient() +{ + return &mScene.getScenePvdClient(); +} +#else +PxPvdSceneClient* NpScene::getScenePvdClient() +{ + return NULL; +} +#endif + +PxsSimulationController* NpScene::getSimulationController() +{ + return mScene.getScScene().getSimulationController(); +} + +void NpScene::setActiveActors(PxActor** actors, PxU32 nbActors) +{ + NP_WRITE_CHECK(this); + mScene.setActiveActors(actors, nbActors); +} + +void NpScene::forceSceneQueryRebuild() +{ + SqRefFinder sqRefFinder; + mScene.getScScene().syncSceneQueryBounds(mSQManager.getDynamicBoundsSync(), sqRefFinder); + + mSQManager.afterSync(getSceneQueryUpdateModeFast()); +} + +void NpScene::sceneQueriesUpdate(physx::PxBaseTask* completionTask, bool controlSimulation) +{ + PX_SIMD_GUARD; + + bool runUpdateTasks[PruningIndex::eCOUNT] = {true, true}; + { + // write guard must end before scene queries tasks kicks off worker threads + NP_WRITE_CHECK(this); + + PX_PROFILE_START_CROSSTHREAD("Basic.sceneQueriesUpdate", getContextId()); + + if(mSceneQueriesUpdateRunning) + { + //fetchSceneQueries doesn't get called + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::fetchSceneQueries was not called!"); + return; + } + + // flush scene queries updates + mSQManager.flushUpdates(); + + // prepare scene queries for build - copy bounds + runUpdateTasks[PruningIndex::eSTATIC] = mSQManager.prepareSceneQueriesUpdate(PruningIndex::eSTATIC); + runUpdateTasks[PruningIndex::eDYNAMIC] = mSQManager.prepareSceneQueriesUpdate(PruningIndex::eDYNAMIC); + + mSceneQueriesUpdateRunning = true; + } + + { + PX_PROFILE_ZONE("Sim.sceneQueriesTaskSetup", getContextId()); + + if (controlSimulation) + { + { + PX_PROFILE_ZONE("Sim.resetDependencies", getContextId()); + // Only reset dependencies, etc if we own the TaskManager. Will be false + // when an NpScene is controlled by an APEX scene. + mTaskManager->resetDependencies(); + } + mTaskManager->startSimulation(); + } + + mSceneQueriesCompletion.setContinuation(*mTaskManager, completionTask); + if(runUpdateTasks[PruningIndex::eSTATIC]) + mSceneQueriesStaticPrunerUpdate.setContinuation(&mSceneQueriesCompletion); + if(runUpdateTasks[PruningIndex::eDYNAMIC]) + mSceneQueriesDynamicPrunerUpdate.setContinuation(&mSceneQueriesCompletion); + + mSceneQueriesCompletion.removeReference(); + if(runUpdateTasks[PruningIndex::eSTATIC]) + mSceneQueriesStaticPrunerUpdate.removeReference(); + if(runUpdateTasks[PruningIndex::eDYNAMIC]) + mSceneQueriesDynamicPrunerUpdate.removeReference(); + } +} + +bool NpScene::checkSceneQueriesInternal(bool block) +{ + PX_PROFILE_ZONE("Basic.checkSceneQueries", getContextId()); + return mSceneQueriesDone.wait(block ? Ps::Sync::waitForever : 0); +} + +bool NpScene::checkQueries(bool block) +{ + return checkSceneQueriesInternal(block); +} + +bool NpScene::fetchQueries(bool block) +{ + if(!mSceneQueriesUpdateRunning) + { + //fetchSceneQueries doesn't get called + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "PxScene::fetchQueries: fetchQueries() called illegally! It must be called after sceneQueriesUpdate()"); + return false; + } + + if(!checkSceneQueriesInternal(block)) + return false; + + { + PX_SIMD_GUARD; + + NP_WRITE_CHECK(this); + + // we use cross thread profile here, to show the event in cross thread view + // PT: TODO: why do we want to show it in the cross thread view? + PX_PROFILE_START_CROSSTHREAD("Basic.fetchQueries", getContextId()); + + // flush updates and commit if work is done + mSQManager.flushUpdates(); + + PX_PROFILE_STOP_CROSSTHREAD("Basic.fetchQueries", getContextId()); + PX_PROFILE_STOP_CROSSTHREAD("Basic.sceneQueriesUpdate", getContextId()); + + mSceneQueriesDone.reset(); + mSceneQueriesUpdateRunning = false; + } + return true; +} + +void NpScene::frameEnd() +{ +#if PX_SUPPORT_PVD + mScene.getScenePvdClient().frameEnd(); +#endif +} + +PxBatchQuery* NpScene::createBatchQuery(const PxBatchQueryDesc& desc) +{ + PX_PROFILE_ZONE("API.createBatchQuery", getContextId()); + PX_CHECK_AND_RETURN_NULL(desc.isValid(),"Supplied PxBatchQueryDesc is not valid. createBatchQuery returns NULL."); + + NpBatchQuery* bq = PX_NEW(NpBatchQuery)(*this, desc); + mBatchQueries.pushBack(bq); + return bq; +} + +void NpScene::releaseBatchQuery(PxBatchQuery* sq) +{ + PX_PROFILE_ZONE("API.releaseBatchQuery", getContextId()); + NpBatchQuery* npsq = static_cast(sq); + bool found = mBatchQueries.findAndReplaceWithLast(npsq); + PX_UNUSED(found); PX_ASSERT(found); + PX_DELETE_AND_RESET(npsq); +} diff --git a/src/PhysX/physx/source/physx/src/NpScene.h b/src/PhysX/physx/source/physx/src/NpScene.h new file mode 100644 index 000000000..eca152c6e --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpScene.h @@ -0,0 +1,496 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_SCENE +#define PX_PHYSICS_NP_SCENE + +#include "PsUserAllocated.h" +#include "PsSync.h" +#include "PsArray.h" +#include "PsThread.h" +#include "PsHashSet.h" +#include "PxPhysXConfig.h" + +#if PX_SUPPORT_GPU_PHYSX +#include "device/PhysXIndicator.h" +#endif + +#include "NpSceneQueries.h" +#include "NpSceneAccessor.h" + +namespace physx +{ + +class PhysicsThread; +class PxBatchQueryDesc; +class NpMaterial; +class NpScene; +class NpArticulation; + +namespace Sc +{ + class Joint; + class ConstraintBreakEvent; +} + +namespace Sq +{ + class SceneQueryManager; +} + +class NpObjectFactory; +class NpRigidStatic; +class NpRigidDynamic; +class NpConstraint; +class NpArticulationLink; +class NpShapeManager; +class NpBatchQuery; + +class PxBatchQuery; + +enum NpProfileZones +{ + NpScene_checkResults, + NpScene_reportContacts, + NpScene_reportProfiling, + NpScene_reportTriggers, + NpScene_stats, + + NpPrNumZones +}; + + +class NpContactCallbackTask : public physx::PxLightCpuTask +{ + NpScene* mScene; + const PxContactPairHeader* mContactPairHeaders; + uint32_t mNbContactPairHeaders; + +public: + + void setData(NpScene* scene, const PxContactPairHeader* contactPairHeaders, const uint32_t nbContactPairHeaders); + + virtual void run(); + + virtual const char* getName() const + { + return "NpContactCallbackTask"; + } +}; + +class NpScene : public NpSceneQueries, public Ps::UserAllocated +{ + //virtual interfaces: + + PX_NOCOPY(NpScene) + public: + + virtual void release(); + + virtual void setFlag(PxSceneFlag::Enum flag, bool value); + virtual PxSceneFlags getFlags() const; + + // implement PxScene: + + virtual void setGravity(const PxVec3&); + virtual PxVec3 getGravity() const; + + virtual void setBounceThresholdVelocity(const PxReal t); + virtual PxReal getBounceThresholdVelocity() const; + + virtual PxReal getFrictionOffsetThreshold() const; + + virtual void setLimits(const PxSceneLimits& limits); + virtual PxSceneLimits getLimits() const; + + virtual void addActor(PxActor& actor, const PxBVHStructure* bvhStructure); + virtual void removeActor(PxActor& actor, bool wakeOnLostTouch); + + virtual PxU32 getNbConstraints() const; + virtual PxU32 getConstraints(PxConstraint** buffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual void addArticulation(PxArticulationBase&); + virtual void removeArticulation(PxArticulationBase&, bool wakeOnLostTouch); + + virtual PxU32 getNbArticulations() const; + virtual PxU32 getArticulations(PxArticulationBase** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + // Aggregates + virtual void addAggregate(PxAggregate&); + virtual void removeAggregate(PxAggregate&, bool wakeOnLostTouch); + virtual PxU32 getNbAggregates() const; + virtual PxU32 getAggregates(PxAggregate** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + virtual void addCollection(const PxCollection& collection); + + // Groups + virtual void setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance); + virtual PxDominanceGroupPair getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const; + + // Actors + virtual PxU32 getNbActors(PxActorTypeFlags types) const; + virtual PxU32 getActors(PxActorTypeFlags types, PxActor** buffer, PxU32 bufferSize, PxU32 startIndex=0) const; + virtual PxActor** getActiveActors(PxU32& nbActorsOut); + + // Run + virtual void getSimulationStatistics(PxSimulationStatistics& s) const; + + // Multiclient + virtual PxClientID createClient(); + + // FrictionModel + virtual void setFrictionType(PxFrictionType::Enum frictionType); + virtual PxFrictionType::Enum getFrictionType() const; + + // Callbacks + virtual void setSimulationEventCallback(PxSimulationEventCallback* callback); + virtual PxSimulationEventCallback* getSimulationEventCallback() const; + virtual void setContactModifyCallback(PxContactModifyCallback* callback); + virtual PxContactModifyCallback* getContactModifyCallback() const; + virtual void setCCDContactModifyCallback(PxCCDContactModifyCallback* callback); + virtual PxCCDContactModifyCallback* getCCDContactModifyCallback() const; + virtual void setBroadPhaseCallback(PxBroadPhaseCallback* callback); + virtual PxBroadPhaseCallback* getBroadPhaseCallback() const; + + //CCD passes + virtual void setCCDMaxPasses(PxU32 ccdMaxPasses); + virtual PxU32 getCCDMaxPasses() const; + + // Collision filtering + virtual void setFilterShaderData(const void* data, PxU32 dataSize); + virtual const void* getFilterShaderData() const; + virtual PxU32 getFilterShaderDataSize() const; + virtual PxSimulationFilterShader getFilterShader() const; + virtual PxSimulationFilterCallback* getFilterCallback() const; + virtual void resetFiltering(PxActor& actor); + virtual void resetFiltering(PxRigidActor& actor, PxShape*const* shapes, PxU32 shapeCount); + + // Get Physics SDK + virtual PxPhysics& getPhysics(); + + // new API methods + virtual void simulate(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation); + virtual void advance(physx::PxBaseTask* completionTask); + virtual void collide(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation = true); + virtual bool checkResults(bool block); + virtual bool checkCollision(bool block); + virtual bool fetchCollision(bool block); + virtual bool fetchResults(bool block, PxU32* errorState); + virtual bool fetchResultsStart(const PxContactPairHeader*& contactPairs, PxU32& nbContactPairs, bool block = false); + virtual void processCallbacks(physx::PxBaseTask* continuation); + virtual void fetchResultsFinish(PxU32* errorState = 0); + + + + virtual void flush(bool sendPendingReports) { flushSimulation(sendPendingReports); } + virtual void flushSimulation(bool sendPendingReports); + virtual void flushQueryUpdates(); + virtual const PxRenderBuffer& getRenderBuffer(); + + virtual PxBatchQuery* createBatchQuery(const PxBatchQueryDesc& desc); + void releaseBatchQuery(PxBatchQuery* bq); + virtual void setDynamicTreeRebuildRateHint(PxU32 dynamicTreeRebuildRateHint); + virtual PxU32 getDynamicTreeRebuildRateHint() const; + virtual void forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure); + virtual void sceneQueriesUpdate(physx::PxBaseTask* completionTask, bool controlSimulation); + virtual bool checkQueries(bool block); + virtual bool fetchQueries(bool block); + virtual void setSceneQueryUpdateMode(PxSceneQueryUpdateMode::Enum updateMode); + virtual PxSceneQueryUpdateMode::Enum getSceneQueryUpdateMode() const; + + virtual void setSolverBatchSize(PxU32 solverBatchSize); + virtual PxU32 getSolverBatchSize(void) const; + + virtual bool setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value); + virtual PxReal getVisualizationParameter(PxVisualizationParameter::Enum param) const; + + virtual void setVisualizationCullingBox(const PxBounds3& box); + virtual PxBounds3 getVisualizationCullingBox() const; + + virtual PxTaskManager* getTaskManager() { return mTaskManager; } + void checkBeginWrite() const {} + + virtual void setNbContactDataBlocks(PxU32 numBlocks); + virtual PxU32 getNbContactDataBlocksUsed() const; + virtual PxU32 getMaxNbContactDataBlocksUsed() const; + + virtual PxU32 getContactReportStreamBufferSize() const; + + virtual PxU32 getTimestamp() const; + virtual PxU32 getSceneQueryStaticTimestamp() const; + + virtual PxCpuDispatcher* getCpuDispatcher() const; + virtual PxGpuDispatcher* getGpuDispatcher() const; + + virtual PxPruningStructureType::Enum getStaticStructure() const; + virtual PxPruningStructureType::Enum getDynamicStructure() const; + + virtual PxBroadPhaseType::Enum getBroadPhaseType() const; + virtual bool getBroadPhaseCaps(PxBroadPhaseCaps& caps) const; + virtual PxU32 getNbBroadPhaseRegions() const; + virtual PxU32 getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + virtual PxU32 addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion); + virtual bool removeBroadPhaseRegion(PxU32 handle); + + virtual void addActors(PxActor*const* actors, PxU32 nbActors); + virtual void addActors(const PxPruningStructure& prunerStructure); + virtual void removeActors(PxActor*const* actors, PxU32 nbActors, bool wakeOnLostTouch); + + virtual void lockRead(const char* file=NULL, PxU32 line=0); + virtual void unlockRead(); + + virtual void lockWrite(const char* file=NULL, PxU32 line=0); + virtual void unlockWrite(); + + virtual PxReal getWakeCounterResetValue() const; + + virtual void shiftOrigin(const PxVec3& shift); + + virtual PxPvdSceneClient* getScenePvdClient(); + + //Implementations for NpSceneAccessor interface! + virtual PxsSimulationController* getSimulationController(); + virtual void setActiveActors(PxActor** actors, PxU32 nbActors); + virtual PxActor** getFrozenActors(PxU32& nbActorsOut); + virtual void setFrozenActorFlag(const bool buildFrozenActors); + virtual void forceSceneQueryRebuild(); + virtual void frameEnd(); + + //internal public methods: + public: + NpScene(const PxSceneDesc& desc); + ~NpScene(); + + PX_FORCE_INLINE PxTaskManager* getTaskManager() const { return mTaskManager; } + + PX_FORCE_INLINE Sc::SimulationStage::Enum getSimulationStage() const { return mScene.getSimulationStage(); } + PX_FORCE_INLINE void setSimulationStage(Sc::SimulationStage::Enum stage) { mScene.setSimulationStage(stage); } + + void addActorInternal(PxActor& actor, const PxBVHStructure* bvhStructure); + void removeActorInternal(PxActor& actor, bool wakeOnLostTouch, bool removeFromAggregate); + void addActorsInternal(PxActor*const* PX_RESTRICT actors, PxU32 nbActors, const Sq::PruningStructure* ps = NULL); + + void addArticulationInternal(PxArticulationBase&); + void removeArticulationInternal(PxArticulationBase&, bool wakeOnLostTouch, bool removeFromAggregate); + // materials + void addMaterial(const NpMaterial& mat); + void updateMaterial(const NpMaterial& mat); + void removeMaterial(const NpMaterial& mat); + + void executeScene(PxBaseTask* continuation); + void executeCollide(PxBaseTask* continuation); + void executeAdvance(PxBaseTask* continuation); + void constraintBreakEventNotify(PxConstraint *const *constraints, PxU32 count); + + bool loadFromDesc(const PxSceneDesc&); + + void removeFromRigidActorList(const PxU32&); + PX_FORCE_INLINE void removeFromArticulationList(PxArticulationBase&); + PX_FORCE_INLINE void removeFromAggregateList(PxAggregate&); + + PX_FORCE_INLINE void addToConstraintList(PxConstraint&); + PX_FORCE_INLINE void removeFromConstraintList(PxConstraint&); + + void addArticulationLink(NpArticulationLink& link); + void addArticulationLinkBody(NpArticulationLink& link); + void addArticulationLinkConstraint(NpArticulationLink& link); + void removeArticulationLink(NpArticulationLink& link, bool wakeOnLostTouch); + + struct StartWriteResult + { + enum Enum { eOK, eNO_LOCK, eIN_FETCHRESULTS, eRACE_DETECTED }; + }; + + StartWriteResult::Enum startWrite(bool allowReentry); + void stopWrite(bool allowReentry); + + bool startRead() const; + void stopRead() const; + + PxU32 getReadWriteErrorCount() const { return PxU32(mConcurrentErrorCount); } + +#if PX_CHECKED + void checkPositionSanity(const PxRigidActor& a, const PxTransform& pose, const char* fnName) const; +#endif + +#if PX_SUPPORT_GPU_PHYSX + void updatePhysXIndicator(); +#else + PX_FORCE_INLINE void updatePhysXIndicator() {} +#endif + + PX_FORCE_INLINE PxReal getWakeCounterResetValueInteral() const { return mScene.getWakeCounterResetValue(); } + +private: + bool checkResultsInternal(bool block); + bool checkCollisionInternal(bool block); + bool checkSceneQueriesInternal(bool block); + void simulateOrCollide(PxReal elapsedTime, physx::PxBaseTask* completionTask, void* scratchBlock, PxU32 scratchBlockSize, bool controlSimulation, const char* invalidCallMsg, Sc::SimulationStage::Enum simStage); + + void addRigidStatic(NpRigidStatic& , const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure = false); + void removeRigidStatic(NpRigidStatic&, bool wakeOnLostTouch, bool removeFromAggregate); + void addRigidDynamic(NpRigidDynamic& , const Gu::BVHStructure* bvhStructure, bool hasPrunerStructure = false); + void removeRigidDynamic(NpRigidDynamic&, bool wakeOnLostTouch, bool removeFromAggregate); + + bool addRigidActorsInternal(PxU32 nbActors, PxActor** PX_RESTRICT actors); + + void visualize(); + + void updateDirtyShaders(); + + void fireOutOfBoundsCallbacks(); + void fetchResultsPreContactCallbacks(); + void fetchResultsPostContactCallbacks(); + + + + void updateScbStateAndSetupSq(const PxRigidActor& rigidActor, Scb::Actor& actor, NpShapeManager& shapeManager, bool actorDynamic, const PxBounds3* bounds, bool hasPrunerStructure); + PX_FORCE_INLINE void updateScbStateAndSetupSq(const PxRigidActor& rigidActor, Scb::Body& body, NpShapeManager& shapeManager, bool actorDynamic, const PxBounds3* bounds, bool hasPrunerStructure); + + Cm::RenderBuffer mRenderBuffer; + + Ps::CoalescedHashSet mConstraints; + Ps::Array mRigidActors; // no hash set used because it would be quite a bit slower when adding a large number of actors + Ps::CoalescedHashSet mArticulations; + Ps::CoalescedHashSet mAggregates; + Ps::Array mBatchQueries; + + PxBounds3 mSanityBounds; +#if PX_SUPPORT_GPU_PHYSX + PhysXIndicator mPhysXIndicator; +#endif + + Ps::Sync mPhysicsDone; // physics thread signals this when update ready + Ps::Sync mCollisionDone; // physics thread signals this when all collisions ready + Ps::Sync mSceneQueriesDone; // physics thread signals this when all scene queries update ready + + + //legacy timing settings: + PxReal mElapsedTime; //needed to transfer the elapsed time param from the user to the sim thread. + + PxU32 mNbClients; // Tracks reserved clients for multiclient support. + Ps::Array mClientBehaviorFlags;// Tracks behavior bits for clients. + + + struct SceneCompletion : public Cm::Task + { + SceneCompletion(PxU64 contextId, Ps::Sync& sync) : Cm::Task(contextId), mSync(sync){} + virtual void runInternal() {} + //ML: As soon as mSync.set is called, and the scene is shutting down, + //the scene may be deleted. That means this running task may also be deleted. + //As such, we call mSync.set() inside release() to avoid a crash because the v-table on this + //task might be deleted between the call to runInternal() and release() in the worker thread. + virtual void release() + { + //We cache the continuation pointer because this class may be deleted + //as soon as mSync.set() is called if the application releases the scene. + PxBaseTask* c = mCont; + //once mSync.set(), fetchResults() will be allowed to run. + mSync.set(); + //Call the continuation task that we cached above. If we use mCont or + //any other member variable of this class, there is a small chance + //that the variables might have become corrupted if the class + //was deleted. + if(c) c->removeReference(); + } + virtual const char* getName() const { return "NpScene.completion"; } + + // //This method just is called in the split sim approach as a way to set continuation after the task has been initialized + void setDependent(PxBaseTask* task){PX_ASSERT(mCont == NULL); mCont = task; if(task)task->addReference();} + Ps::Sync& mSync; + private: + SceneCompletion& operator=(const SceneCompletion&); + }; + + typedef Cm::DelegateTask SceneExecution; + typedef Cm::DelegateTask SceneCollide; + typedef Cm::DelegateTask SceneAdvance; + + + PxTaskManager* mTaskManager; + SceneCompletion mSceneCompletion; + SceneCompletion mCollisionCompletion; + SceneCompletion mSceneQueriesCompletion; + SceneExecution mSceneExecution; + SceneCollide mSceneCollide; + SceneAdvance mSceneAdvance; + bool mControllingSimulation; + + PxU32 mSimThreadStackSize; + + volatile PxI32 mConcurrentWriteCount; + mutable volatile PxI32 mConcurrentReadCount; + mutable volatile PxI32 mConcurrentErrorCount; + + // TLS slot index, keeps track of re-entry depth for this thread + PxU32 mThreadReadWriteDepth; + Ps::Thread::Id mCurrentWriter; + Ps::ReadWriteLock mRWLock; + + bool mSceneQueriesUpdateRunning; + + bool mHasSimulatedOnce; + bool mBetweenFetchResults; + bool mBuildFrozenActors; +}; + + +PX_FORCE_INLINE void NpScene::addToConstraintList(PxConstraint& constraint) +{ + mConstraints.insert(&constraint); +} + + +PX_FORCE_INLINE void NpScene::removeFromConstraintList(PxConstraint& constraint) +{ + const bool exists = mConstraints.erase(&constraint); + PX_ASSERT(exists); + PX_UNUSED(exists); +} + + +PX_FORCE_INLINE void NpScene::removeFromArticulationList(PxArticulationBase& articulation) +{ + const bool exists = mArticulations.erase(&articulation); + PX_ASSERT(exists); + PX_UNUSED(exists); +} + + +PX_FORCE_INLINE void NpScene::removeFromAggregateList(PxAggregate& aggregate) +{ + const bool exists = mAggregates.erase(&aggregate); + PX_ASSERT(exists); + PX_UNUSED(exists); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpSceneAccessor.h b/src/PhysX/physx/source/physx/src/NpSceneAccessor.h new file mode 100644 index 000000000..b729b8c6d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpSceneAccessor.h @@ -0,0 +1,60 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NP_SCENEACCESSOR +#define PX_PHYSICS_NP_SCENEACCESSOR + +#include "PxScene.h" + +namespace physx +{ + class PxsSimulationController; + + class NpSceneAccessor : public PxScene + { + + PX_NOCOPY(NpSceneAccessor) + + public: + NpSceneAccessor(){} + virtual ~NpSceneAccessor(){} + + virtual PxsSimulationController* getSimulationController() = 0; + virtual void setActiveActors(PxActor** actors, PxU32 nbActors) = 0; + virtual PxActor** getFrozenActors(PxU32& nbActorsOut) = 0; + virtual void setFrozenActorFlag(const bool buildFrozenActors) = 0; + + virtual void forceSceneQueryRebuild() = 0; + virtual void frameEnd() = 0; + + }; +} + +#endif + diff --git a/src/PhysX/physx/source/physx/src/NpSceneQueries.cpp b/src/PhysX/physx/source/physx/src/NpSceneQueries.cpp new file mode 100644 index 000000000..9cb098993 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpSceneQueries.cpp @@ -0,0 +1,850 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuIntersectionRayBox.h" +#include "PxGeometryQuery.h" +#include "NpRigidDynamic.h" +#include "NpQueryShared.h" +#include "SqPruner.h" +#include "GuBounds.h" +#include "GuIntersectionRay.h" +#include "common/PxProfileZone.h" + +// Synchronous scene queries + +using namespace physx; +using namespace Sq; +using namespace Gu; + +#if PX_SUPPORT_PVD +#include "NpPvdSceneQueryCollector.h" +#endif + +namespace local +{ + // helper class to encapsulate Scb::Actor and Shape together with PxActorShape + struct ActorShape : PxActorShape + { + const Scb::Shape* scbShape; + const Scb::Actor* scbActor; + + ActorShape() : PxActorShape() {} + + ActorShape(PxRigidActor* eaActor, PxShape* eaShape, Scb::Shape* sShape, Scb::Actor* sActor) : PxActorShape(eaActor, eaShape) + { + scbShape = sShape; + scbActor = sActor; + } + }; + + // fill the helper actor shape + static PX_FORCE_INLINE void populate(const PrunerPayload& payload, ActorShape& as) + { + Scb::Shape* localShape = reinterpret_cast(payload.data[0]); + Scb::Actor* localActor = reinterpret_cast(payload.data[1]); + + as.scbShape = localShape; + as.scbActor = localActor; + + as.actor = static_cast(static_cast(localActor->getActorCore()).getPxActor()); + as.shape = localShape->getScShape().getPxShape(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +bool NpSceneQueries::raycast( + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxHitCallback& hits, PxHitFlags hitFlags, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache) const +{ + PX_PROFILE_ZONE("SceneQuery.raycast", getContextId()); + NP_READ_CHECK(this); + PX_SIMD_GUARD; + + MultiQueryInput input(origin, unitDir, distance); + return multiQuery(input, hits, hitFlags, cache, filterData, filterCall, NULL); +} + +////////////////////////////////////////////////////////////////////////// +bool NpSceneQueries::overlap( + const PxGeometry& geometry, const PxTransform& pose, PxOverlapCallback& hits, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall) const +{ + PX_PROFILE_ZONE("SceneQuery.overlap", getContextId()); + NP_READ_CHECK(this); + PX_SIMD_GUARD; + + MultiQueryInput input(&geometry, &pose); + // we are not supporting cache for overlaps for some reason + return multiQuery(input, hits, PxHitFlags(), NULL, filterData, filterCall, NULL); +} + +/////////////////////////////////////////////////////////////////////////////// +bool NpSceneQueries::sweep( + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxHitCallback& hits, PxHitFlags hitFlags, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache, const PxReal inflation) const +{ + PX_PROFILE_ZONE("SceneQuery.sweep", getContextId()); + NP_READ_CHECK(this); + PX_SIMD_GUARD; + +#if PX_CHECKED + if(!PxGeometryQuery::isValid(geometry)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Provided geometry is not valid"); + return false; + } +#endif // PX_CHECKED + + + if((hitFlags & PxHitFlag::ePRECISE_SWEEP) && (hitFlags & PxHitFlag::eMTD)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support MTD. Perform MTD with default sweep"); + hitFlags &= ~PxHitFlag::ePRECISE_SWEEP; + } + + if((hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP) && (hitFlags & PxHitFlag::eMTD)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " eMTD cannot be used in conjunction with eASSUME_NO_INITIAL_OVERLAP. eASSUME_NO_INITIAL_OVERLAP will be ignored"); + hitFlags &= ~PxHitFlag::eASSUME_NO_INITIAL_OVERLAP; + } + + PxReal realInflation = inflation; + if((hitFlags & PxHitFlag::ePRECISE_SWEEP)&& inflation > 0.f) + { + realInflation = 0.f; + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, " Precise sweep doesn't support inflation, inflation will be overwritten to be zero"); + } + MultiQueryInput input(&geometry, &pose, unitDir, distance, realInflation); + return multiQuery(input, hits, hitFlags, cache, filterData, filterCall, NULL); +} + +/////////////////////////////////////////////////////////////////////////////// +//======================================================================================================================== + +static PX_FORCE_INLINE bool applyAllPreFiltersSQ( + const local::ActorShape* as, PxQueryHitType::Enum& hitType, const PxQueryFlags& inFilterFlags, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + BatchQueryFilterData* bfd, PxHitFlags& queryFlags/*, PxU32 maxNbTouches*/) +{ + // AP: the !bfd clause is here because there's no other way to pass data to BQ pre/post filter shaders + // For normal query the data can be passed with inherited callback instance + // So if for BQ SPU filter shader the user tries to pass data via FD, the equation will always cut it out + // AP scaffold TODO: once SPU is officially phased out we can remove the !bfd clause, fix broken UTs (that are wrong) + // and also remove support for filter shaders + if(!bfd && !applyFilterEquation(*as->scbShape, filterData.data)) + return false; + + if((inFilterFlags & PxQueryFlag::ePREFILTER) && (filterCall || bfd)) + { + PxHitFlags outQueryFlags = queryFlags; + + if(filterCall) + hitType = filterCall->preFilter(filterData.data, as->shape, as->actor, outQueryFlags); + else if(bfd->preFilterShader) + hitType = bfd->preFilterShader( + filterData.data, as->scbShape->getScShape().getQueryFilterData(), + bfd->filterShaderData, bfd->filterShaderDataSize, outQueryFlags); + + // AP: at this point the callback might return eTOUCH but the touch buffer can be empty, the hit will be discarded + //PX_CHECK_MSG(hitType == PxQueryHitType::eTOUCH ? maxNbTouches > 0 : true, + // "SceneQuery: preFilter returned eTOUCH but empty touch buffer was provided, hit discarded."); + + queryFlags = (queryFlags & ~PxHitFlag::eMODIFIABLE_FLAGS) | (outQueryFlags & PxHitFlag::eMODIFIABLE_FLAGS); + } + // test passed, continue to return as; + return true; +} + +//======================================================================================================================== +// performs a single geometry query for any HitType (PxSweepHit, PxOverlapHit, PxRaycastHit) +template +struct GeomQueryAny +{ + static PX_FORCE_INLINE PxU32 geomHit( + const NpSceneQueries& sceneQueries, const MultiQueryInput& input, const ShapeData& sd, + const PxGeometry& sceneGeom, const PxTransform& pose, PxHitFlags hitFlags, + PxU32 maxHits, HitType* hits, const PxReal shrunkMaxDistance, PxBounds3* precomputedBounds) + { + const PxGeometry& geom0 = *input.geometry; + const PxTransform& pose0 = *input.pose; + const PxGeometry& geom1 = sceneGeom; + const PxTransform& pose1 = pose; + + // Handle raycasts + if(HitTypeSupport::IsRaycast) + { + // the test for mesh AABB is archived in //sw/physx/dev/apokrovsky/graveyard/sqMeshAABBTest.cpp + // TODO: investigate performance impact (see US12801) + PX_CHECK_AND_RETURN_VAL(input.getDir().isFinite(), "PxScene::raycast(): rayDir is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(input.getOrigin().isFinite(), "PxScene::raycast(): rayOrigin is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(pose1.isValid(), "PxScene::raycast(): pose is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(shrunkMaxDistance >= 0.0f, "PxScene::raycast(): maxDist is negative.", 0); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(shrunkMaxDistance), "PxScene::raycast(): maxDist is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(PxAbs(input.getDir().magnitudeSquared()-1)<1e-4f, + "PxScene::raycast(): ray direction must be unit vector.", 0); + + // PT: TODO: investigate perf difference + const RaycastFunc func = sceneQueries.mCachedRaycastFuncs[geom1.getType()]; + return func(geom1, pose1, input.getOrigin(), input.getDir(), shrunkMaxDistance, + hitFlags, maxHits, reinterpret_cast(hits)); + } + // Handle sweeps + else if(HitTypeSupport::IsSweep) + { + PX_ASSERT(precomputedBounds != NULL); + // b0 = query shape bounds + // b1 = scene shape bounds + // AP: Here we clip the sweep to bounds with sum of extents. This is needed for GJK stability. + // because sweep is equivalent to a raycast vs a scene shape with inflated bounds. + // This also may (or may not) provide an optimization for meshes because top level of rtree has multiple boxes + // and there is no bounds test for the whole mesh elsewhere + PxBounds3 b0 = *precomputedBounds, b1; + // compute the scene geometry bounds + // PT: TODO: avoid recomputing the bounds here + Gu::computeBounds(b1, sceneGeom, pose, 0.0f, NULL, 1.0f); + const PxVec3 combExt = (b0.getExtents() + b1.getExtents())*1.01f; + + PxF32 tnear, tfar; + if(!intersectRayAABB2(-combExt, combExt, b0.getCenter() - b1.getCenter(), input.getDir(), shrunkMaxDistance, tnear, tfar)) // returns (tneartfar) // this second test is needed because shrunkMaxDistance can be 0 for 0 length sweep + return 0; + PX_ASSERT(input.getDir().isNormalized()); + // tfar is now the t where the ray exits the AABB. input.getDir() is normalized + + const PxVec3& unitDir = input.getDir(); + PxSweepHit& sweepHit = reinterpret_cast(hits[0]); + + // if we don't start inside the AABB box, offset the start pos, because of precision issues with large maxDist + const bool offsetPos = (tnear > GU_RAY_SURFACE_OFFSET); + const PxReal offset = offsetPos ? (tnear - GU_RAY_SURFACE_OFFSET) : 0.0f; + const PxVec3 offsetVec(offsetPos ? (unitDir*offset) : PxVec3(0.0f)); + // we move the geometry we sweep against, so that we avoid the Gu::Capsule/Box recomputation + const PxTransform pose1Offset(pose1.p - offsetVec, pose1.q); + + const PxReal distance = PxMin(tfar, shrunkMaxDistance) - offset; + const PxReal inflation = input.inflation; + PX_CHECK_AND_RETURN_VAL(pose0.isValid(), "PxScene::sweep(): pose0 is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(pose1Offset.isValid(), "PxScene::sweep(): pose1 is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(unitDir.isFinite(), "PxScene::sweep(): unitDir is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(PxIsFinite(distance), "PxScene::sweep(): distance is not valid.", 0); + PX_CHECK_AND_RETURN_VAL((distance >= 0.0f && !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP)) || distance > 0.0f, + "PxScene::sweep(): sweep distance must be >=0 or >0 with eASSUME_NO_INITIAL_OVERLAP.", 0); + + PxU32 retVal = 0; + const GeomSweepFuncs& sf = sceneQueries.mCachedSweepFuncs; + switch(geom0.getType()) + { + case PxGeometryType::eSPHERE: + { + const PxSphereGeometry& sphereGeom = static_cast(geom0); + // PT: TODO: technically this capsule with 0.0 half-height is invalid ("isValid" returns false) + const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f); + const Capsule worldCapsule(pose0.p, pose0.p, sphereGeom.radius); // AP: precompute? + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()]; + retVal = PxU32(func(geom1, pose1Offset, capsuleGeom, pose0, worldCapsule, unitDir, distance, sweepHit, hitFlags, inflation)); + } + break; + + case PxGeometryType::eCAPSULE: + { + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom1.getType()] : sf.capsuleMap[geom1.getType()]; + retVal = PxU32(func(geom1, pose1Offset, static_cast(geom0), pose0, sd.getGuCapsule(), unitDir, distance, sweepHit, hitFlags, inflation)); + } + break; + + case PxGeometryType::eBOX: + { + const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP; + const SweepBoxFunc func = precise ? sf.preciseBoxMap[geom1.getType()] : sf.boxMap[geom1.getType()]; + retVal = PxU32(func(geom1, pose1Offset, static_cast(geom0), pose0, sd.getGuBox(), unitDir, distance, sweepHit, hitFlags, inflation)); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const PxConvexMeshGeometry& convexGeom = static_cast(geom0); + const SweepConvexFunc func = sf.convexMap[geom1.getType()]; + retVal = PxU32(func(geom1, pose1Offset, convexGeom, pose0, unitDir, distance, sweepHit, hitFlags, inflation)); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxScene::sweep(): first geometry object parameter must be sphere, capsule, box or convex geometry."); + break; + } + if (retVal) + { + // we need to offset the distance back + sweepHit.distance += offset; + // we need to offset the hit position back as we moved the geometry we sweep against + sweepHit.position += offsetVec; + } + return retVal; + } + // Handle overlaps + else if(HitTypeSupport::IsOverlap) + { + const GeomOverlapTable* overlapFuncs = sceneQueries.mCachedOverlapFuncs; + return PxU32(Gu::overlap(geom0, pose0, geom1, pose1, overlapFuncs)); + } + else + { + PX_ALWAYS_ASSERT_MESSAGE("Unexpected template expansion in GeomQueryAny::geomHit"); + return 0; + } + } +}; + +// struct to access protected data members in the public PxHitCallback API +template +struct MultiQueryCallback : public PrunerCallback +{ + const NpSceneQueries& mScene; + const MultiQueryInput& mInput; + PxHitCallback& mHitCall; + const PxHitFlags mHitFlags; + const PxQueryFilterData& mFilterData; + PxQueryFilterCallback* mFilterCall; + PxReal mShrunkDistance; + BatchQueryFilterData* mBfd; // only not NULL for batch queries + const PxHitFlags mMeshAnyHitFlags; + bool mReportTouchesAgain; + bool mFarBlockFound; // this is to prevent repeated searches for far block + bool mNoBlock; + const bool mAnyHit; + bool mIsCached; // is this call coming as a callback from the pruner or a single item cached callback? + + // The reason we need these bounds is because we need to know combined(inflated shape) bounds to clip the sweep path + // to be tolerable by GJK precision issues. This test is done for (queryShape vs touchedShapes) + // So it makes sense to cache the bounds for sweep query shape, otherwise we'd have to recompute them every time + // Currently only used for sweeps. + PxBounds3 mQueryShapeBounds; + bool mQueryShapeBoundsValid; + const ShapeData* mShapeData; + + MultiQueryCallback( + const NpSceneQueries& scene, const MultiQueryInput& input, bool anyHit, PxHitCallback& hitCall, PxHitFlags hitFlags, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, PxReal shrunkDistance, BatchQueryFilterData* aBfd) : + mScene (scene), + mInput (input), + mHitCall (hitCall), + mHitFlags (hitFlags), + mFilterData (filterData), + mFilterCall (filterCall), + mShrunkDistance (shrunkDistance), + mBfd (aBfd), + mMeshAnyHitFlags ((hitFlags.isSet(PxHitFlag::eMESH_ANY) || anyHit) ? PxHitFlag::eMESH_ANY : PxHitFlag::Enum(0)), + mReportTouchesAgain (true), + mFarBlockFound (filterData.flags & PxQueryFlag::eNO_BLOCK), + mNoBlock (filterData.flags & PxQueryFlag::eNO_BLOCK), + mAnyHit (anyHit), + mIsCached (false), + mQueryShapeBoundsValid (false), + mShapeData (NULL) + { + } + + virtual PxAgain invoke(PxReal& aDist, const PrunerPayload& aPayload) + { + const PxU32 tempCount = 1; + HitType tempBuf[tempCount]; + + // PT: TODO: do we need actorShape.actor/actorShape.shape immediately? + local::ActorShape actorShape; + local::populate(aPayload, actorShape); + + const PxQueryFlags filterFlags = mFilterData.flags; + + // for no filter callback, default to eTOUCH for MULTIPLE, eBLOCK otherwise + // also always treat as eBLOCK if currently tested shape is cached + // Using eRESERVED flag as a special condition to default to eTOUCH hits while only looking for a single blocking hit + // from a nested query (see other comments containing #LABEL1) + PxQueryHitType::Enum shapeHitType = + ((mHitCall.maxNbTouches || (mFilterData.flags & PxQueryFlag::eRESERVED)) && !mIsCached) + ? PxQueryHitType::eTOUCH + : PxQueryHitType::eBLOCK; + + // apply pre-filter + PxHitFlags filteredHitFlags = mHitFlags; + if(!mIsCached) // don't run filters on single item cache + if(!applyAllPreFiltersSQ(&actorShape, shapeHitType/*in&out*/, filterFlags, mFilterData, mFilterCall, + mBfd, filteredHitFlags/*, mHitCall.maxNbTouches*/)) + return true; // skip this shape from reporting if prefilter said to do so + if(shapeHitType == PxQueryHitType::eNONE) + return true; + + PX_ASSERT(actorShape.actor && actorShape.shape); + const Scb::Shape* shape = actorShape.scbShape; + const Scb::Actor* actor = actorShape.scbActor; + + // compute the global pose for the cached shape and actor + PX_ALIGN(16, PxTransform) globalPose; + NpActor::getGlobalPose(globalPose, *shape, *actor); + + const PxGeometry& shapeGeom = shape->getGeometry(); + + // Here we decide whether to use the user provided buffer in place or a local stack buffer + // see if we have more room left in the callback results buffer than in the parent stack buffer + // if so get subHits in-place in the hit buffer instead of the parent stack buffer + // nbTouches is the number of accumulated touch hits so far + // maxNbTouches is the size of the user buffer + PxU32 maxSubHits1 = mHitCall.maxNbTouches - mHitCall.nbTouches; // how much room is left in the user buffer + HitType* subHits1 = mHitCall.touches + mHitCall.nbTouches; // pointer to the first free hit in the user buffer + if(mHitCall.nbTouches >= mHitCall.maxNbTouches) + // if there's no room left in the user buffer, use a stack buffer + { + // tried using 64 here - causes check stack code to get generated on xbox, perhaps because of guard page + // need this buffer in case the input buffer is full but we still want to correctly merge results from later hits + maxSubHits1 = tempCount; + subHits1 = reinterpret_cast(tempBuf); + } + + // limit number of hits to 1 for meshes if eMESH_MULTIPLE wasn't specified. this tells geomQuery to only look for a closest hit + if(shapeGeom.getType() == PxGeometryType::eTRIANGLEMESH && !(filteredHitFlags & PxHitFlag::eMESH_MULTIPLE)) + maxSubHits1 = 1; // required to only receive 1 hit to pass UTs + // call the geometry specific intersection template + PxU32 nbSubHits = GeomQueryAny::geomHit( + mScene, mInput, *mShapeData, shapeGeom, globalPose, + filteredHitFlags | mMeshAnyHitFlags, + maxSubHits1, subHits1, mShrunkDistance, mQueryShapeBoundsValid ? &mQueryShapeBounds : NULL); + + // ------------------------- iterate over geometry subhits ----------------------------------- + for (PxU32 iSubHit = 0; iSubHit < nbSubHits; iSubHit++) + { + HitType& hit = subHits1[iSubHit]; + hit.actor = actorShape.actor; + hit.shape = actorShape.shape; + + // some additional processing only for sweep hits with initial overlap + if(HitTypeSupport::IsSweep && HITDIST(hit) == 0.0f && !(filteredHitFlags & PxHitFlag::eMTD)) + // PT: necessary as some leaf routines are called with reversed params, thus writing +unitDir there. + // AP: apparently still necessary to also do in Gu because Gu can be used standalone (without SQ) + reinterpret_cast(hit).normal = -mInput.getDir(); + + // start out with hitType for this cached shape set to a pre-filtered hit type + PxQueryHitType::Enum hitType = shapeHitType; + + // run the post-filter if specified in filterFlags and filterCall is non-NULL + if(!mIsCached && (mFilterCall || mBfd) && (filterFlags & PxQueryFlag::ePOSTFILTER)) + { + if(mFilterCall) + hitType = mFilterCall->postFilter(mFilterData.data, hit); + else if(mBfd->postFilterShader) + hitType = mBfd->postFilterShader( + mFilterData.data, actorShape.scbShape->getScShape().getQueryFilterData(), + mBfd->filterShaderData, mBfd->filterShaderDataSize, hit); + } + + // early out on any hit if eANY_HIT was specified, regardless of hit type + if(mAnyHit && hitType != PxQueryHitType::eNONE) + { + // block or touch qualifies for qType=ANY type hit => return it as blocking according to spec. Ignore eNONE. + mHitCall.block = hit; + mHitCall.hasBlock = true; + return false; // found a hit for ANY qType, can early exit now + } + + if(mNoBlock) + hitType = PxQueryHitType::eTOUCH; + + PX_WARN_ONCE_IF(HitTypeSupport::IsOverlap && hitType == PxQueryHitType::eBLOCK, + "eBLOCK returned from user filter for overlap() query. This may cause undesired behavior. " + "Consider using PxQueryFlag::eNO_BLOCK for overlap queries."); + + if(hitType == PxQueryHitType::eTOUCH) + { + // -------------------------- handle eTOUCH hits --------------------------------- + // for qType=multiple, store the hit. For other qTypes ignore it. + // <= is important for initially overlapping sweeps + #if PX_CHECKED + if(mHitCall.maxNbTouches == 0 && !mBfd && !mFilterData.flags.isSet(PxQueryFlag::eRESERVED)) + // issue a warning if eTOUCH was returned by the prefilter, we have 0 touch buffer and not a batch query + // not doing for BQ because the touches buffer can be overflown and thats ok by spec + // eRESERVED to avoid a warning from nested callback (closest blocking hit recursive search) + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "User filter returned PxQueryHitType::eTOUCH but the touches buffer was empty. Hit was discarded."); + #endif + + if(mHitCall.maxNbTouches && mReportTouchesAgain && HITDIST(hit) <= mShrunkDistance) + { + // Buffer full: need to find the closest blocking hit, clip touch hits and flush the buffer + if(mHitCall.nbTouches == mHitCall.maxNbTouches) + { + // issue a second nested query just looking for the closest blocking hit + // could do better perf-wise by saving traversal state (start looking for blocking from this point) + // but this is not a perf critical case because users can provide a bigger buffer + // that covers non-degenerate cases + // far block search doesn't apply to overlaps because overlaps don't work with blocking hits + if(HitTypeSupport::IsOverlap == 0) + { + // AP: the use of eRESERVED is a bit tricky, see other comments containing #LABEL1 + PxQueryFilterData fd1 = mFilterData; fd1.flags |= PxQueryFlag::eRESERVED; + PxHitBuffer buf1; // create a temp callback buffer for a single blocking hit + if(!mFarBlockFound && mHitCall.maxNbTouches > 0 && mScene.NpSceneQueries::multiQuery( + mInput, buf1, mHitFlags, NULL, fd1, mFilterCall, mBfd)) + { + mHitCall.block = buf1.block; + mHitCall.hasBlock = true; + mHitCall.nbTouches = + clipHitsToNewMaxDist(mHitCall.touches, mHitCall.nbTouches, HITDIST(buf1.block)); + mShrunkDistance = HITDIST(buf1.block); + aDist = mShrunkDistance; + } + mFarBlockFound = true; + } + if(mHitCall.nbTouches == mHitCall.maxNbTouches) + { + mReportTouchesAgain = mHitCall.processTouches(mHitCall.touches, mHitCall.nbTouches); + if(!mReportTouchesAgain) + return false; // optimization - buffer is full + else + mHitCall.nbTouches = 0; // reset nbTouches so we can continue accumulating again + } + } + + //if(hitCall.nbTouches < hitCall.maxNbTouches) // can be true if maxNbTouches is 0 + mHitCall.touches[mHitCall.nbTouches++] = hit; + } // if(hitCall.maxNbTouches && reportTouchesAgain && HITDIST(hit) <= shrunkDistance) + } // if(hitType == PxQueryHitType::eTOUCH) + else if(hitType == PxQueryHitType::eBLOCK) + { + // -------------------------- handle eBLOCK hits ---------------------------------- + // only eBLOCK qualifies as a closest hit candidate => compare against best distance and store + // <= is needed for eTOUCH hits to be recorded correctly vs same eBLOCK distance for overlaps + if(HITDIST(hit) <= mShrunkDistance) + { + if(HitTypeSupport::IsOverlap == 0) + { + mShrunkDistance = HITDIST(hit); + aDist = mShrunkDistance; + } + mHitCall.block = hit; + mHitCall.hasBlock = true; + } + } // if(hitType == eBLOCK) + else { + PX_ASSERT(hitType == PxQueryHitType::eNONE); + } + } // for iSubHit + return true; + } + +private: + MultiQueryCallback& operator=(const MultiQueryCallback&); +}; + +//======================================================================================================================== +#if PX_SUPPORT_PVD +template +struct CapturePvdOnReturn : public PxHitCallback +{ + // copy the arguments of multiQuery into a struct, this is strictly for PVD recording + const NpSceneQueries* mSQ; + const MultiQueryInput& mInput; + PxHitFlags mHitFlags; // PT: TODO: this is not used! + const PxQueryCache* mCache; // PT: TODO: this is not used! + const PxQueryFilterData& mFilterData; + PxQueryFilterCallback* mFilterCall; // PT: TODO: this is not used! + BatchQueryFilterData* mBFD; // PT: TODO: check if this is sometimes not NULL + Ps::Array mAllHits; + PxHitCallback& mParentCallback; + + CapturePvdOnReturn( + const NpSceneQueries* sq, const MultiQueryInput& input, PxHitFlags hitFlags, + const PxQueryCache* cache, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + BatchQueryFilterData* bfd, PxHitCallback& parentCallback) : + PxHitCallback (parentCallback.touches, parentCallback.maxNbTouches), + mSQ (sq), + mInput (input), + mHitFlags (hitFlags), + mCache (cache), + mFilterData (filterData), + mFilterCall (filterCall), + mBFD (bfd), + mParentCallback (parentCallback) + {} + + virtual PxAgain processTouches(const HitType* hits, PxU32 nbHits) + { + const PxAgain again = mParentCallback.processTouches(hits, nbHits); + for(PxU32 i=0; igetScene().getScenePvdClient(); + if(!(pvdClient.checkPvdDebugFlag() && (pvdClient.getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES))) + return; + + physx::Vd::PvdSceneQueryCollector& collector = mBFD ? mSQ->getBatchedSqCollector() : mSQ->getSingleSqCollector(); + + if(mParentCallback.nbTouches) + { + for(PxU32 i = 0; i < mParentCallback.nbTouches; i++) + mAllHits.pushBack(mParentCallback.touches[i]); + } + + if(mParentCallback.hasBlock) + mAllHits.pushBack(mParentCallback.block); + + // PT: TODO: why do we need reinterpret_casts below? + if(HitTypeSupport::IsRaycast) + collector.raycast (mInput.getOrigin(), mInput.getDir(), mInput.maxDistance, reinterpret_cast(mAllHits.begin()), mAllHits.size(), mFilterData, this->maxNbTouches!=0); + else if(HitTypeSupport::IsOverlap) + collector.overlapMultiple (*mInput.geometry, *mInput.pose, reinterpret_cast(mAllHits.begin()), mAllHits.size(), mFilterData); + else if(HitTypeSupport::IsSweep) + collector.sweep (*mInput.geometry, *mInput.pose, mInput.getDir(), mInput.maxDistance, reinterpret_cast(mAllHits.begin()), mAllHits.size(), mFilterData, this->maxNbTouches!=0); + } + +private: + CapturePvdOnReturn& operator=(const CapturePvdOnReturn&); +}; +#endif // PX_SUPPORT_PVD + +//======================================================================================================================== +template +struct IssueCallbacksOnReturn +{ + PxHitCallback& hits; + PxAgain again; // query was stopped by previous processTouches. This means that nbTouches is still non-zero + // but we don't need to issue processTouches again + PX_FORCE_INLINE IssueCallbacksOnReturn(PxHitCallback& aHits) : hits(aHits) + { + again = true; + } + + ~IssueCallbacksOnReturn() + { + if(again) + // only issue processTouches if query wasn't stopped + // this is because nbTouches doesn't get reset to 0 in this case (according to spec) + // and the touches in touches array were already processed by the callback + { + if(hits.hasBlock && hits.nbTouches) + hits.nbTouches = clipHitsToNewMaxDist(hits.touches, hits.nbTouches, HITDIST(hits.block)); + if(hits.nbTouches) + { + bool again_ = hits.processTouches(hits.touches, hits.nbTouches); + if(again_) + hits.nbTouches = 0; + } + } + hits.finalizeQuery(); + } + +private: + IssueCallbacksOnReturn& operator=(const IssueCallbacksOnReturn&); +}; + +#undef HITDIST + +//======================================================================================================================== +template +bool NpSceneQueries::multiQuery( + const MultiQueryInput& input, PxHitCallback& hits, PxHitFlags hitFlags, const PxQueryCache* cache, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, BatchQueryFilterData* bfd) const +{ + const bool anyHit = (filterData.flags & PxQueryFlag::eANY_HIT) == PxQueryFlag::eANY_HIT; + + PxI32 retval = 0; PX_UNUSED(retval); + + if(HitTypeSupport::IsRaycast == 0) + { + PX_CHECK_AND_RETURN_VAL(input.pose != NULL, "NpSceneQueries::overlap/sweep pose is NULL.", 0); + PX_CHECK_AND_RETURN_VAL(input.pose->isValid(), "NpSceneQueries::overlap/sweep pose is not valid.", 0); + } + else + { + PX_CHECK_AND_RETURN_VAL(input.getOrigin().isFinite(), "NpSceneQueries::raycast pose is not valid.", 0); + } + + if(HitTypeSupport::IsOverlap == 0) + { + PX_CHECK_AND_RETURN_VAL(input.getDir().isFinite(), "NpSceneQueries multiQuery input check: unitDir is not valid.", 0); + PX_CHECK_AND_RETURN_VAL(input.getDir().isNormalized(), "NpSceneQueries multiQuery input check: direction must be normalized", 0); + } + + if(HitTypeSupport::IsRaycast) + { + PX_CHECK_AND_RETURN_VAL(input.maxDistance > 0.0f, "NpSceneQueries::multiQuery input check: distance cannot be negative or zero", 0); + } + + if(HitTypeSupport::IsOverlap && !anyHit) + { + PX_CHECK_AND_RETURN_VAL(hits.maxNbTouches > 0, "PxScene::overlap() and PxBatchQuery::overlap() calls without eANY_HIT flag require a touch hit buffer for return results.", 0); + } + + if(HitTypeSupport::IsSweep) + { + PX_CHECK_AND_RETURN_VAL(input.maxDistance >= 0.0f, "NpSceneQueries multiQuery input check: distance cannot be negative", 0); + PX_CHECK_AND_RETURN_VAL(input.maxDistance != 0.0f || !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP), + "NpSceneQueries multiQuery input check: zero-length sweep only valid without the PxHitFlag::eASSUME_NO_INITIAL_OVERLAP flag", 0); + } + + PX_CHECK_MSG(!cache || (cache && cache->shape && cache->actor), "Raycast cache specified but shape or actor pointer is NULL!"); + PxU32 cachedCompoundId = INVALID_PRUNERHANDLE; + const PrunerData cacheData = cache ? NpActor::getShapeManager(*cache->actor)->findSceneQueryData(*static_cast(cache->shape), cachedCompoundId) : SQ_INVALID_PRUNER_DATA; + + // this function is logically const for the SDK user, as flushUpdates() will not have an API-visible effect on this object + // internally however, flushUpdates() changes the states of the Pruners in mSQManager + // because here is the only place we need this, const_cast instead of making SQM mutable + const_cast(this)->mSQManager.flushUpdates(); + +#if PX_SUPPORT_PVD + CapturePvdOnReturn pvdCapture(this, input, hitFlags, cache, filterData, filterCall, bfd, hits); +#endif + + IssueCallbacksOnReturn cbr(hits); // destructor will execute callbacks on return from this function + hits.hasBlock = false; + hits.nbTouches = 0; + + PxReal shrunkDistance = HitTypeSupport::IsOverlap ? PX_MAX_REAL : input.maxDistance; // can be progressively shrunk as we go over the list of shapes + if(HitTypeSupport::IsSweep) + shrunkDistance = PxMin(shrunkDistance, PX_MAX_SWEEP_DISTANCE); + MultiQueryCallback pcb(*this, input, anyHit, hits, hitFlags, filterData, filterCall, shrunkDistance, bfd); + + if(cacheData!=SQ_INVALID_PRUNER_DATA && hits.maxNbTouches == 0) // don't use cache for queries that can return touch hits + { + // this block is only executed for single shape cache + const PrunerPayload& cachedPayload = mSQManager.getPayload(cachedCompoundId, cacheData); + pcb.mIsCached = true; + PxReal dummyDist; + + PxAgain againAfterCache; + if(HitTypeSupport::IsSweep) + { + // AP: for sweeps we cache the bounds because we need to know them for the test to clip the sweep to bounds + // otherwise GJK becomes unstable. The bounds can be used multiple times so this is an optimization. + const ShapeData sd(*input.geometry, *input.pose, input.inflation); + pcb.mQueryShapeBounds = sd.getPrunerInflatedWorldAABB(); + pcb.mQueryShapeBoundsValid = true; + pcb.mShapeData = &sd; + againAfterCache = pcb.invoke(dummyDist, cachedPayload); + pcb.mShapeData = NULL; + } else + againAfterCache = pcb.invoke(dummyDist, cachedPayload); + pcb.mIsCached = false; + if(!againAfterCache) // if PxAgain result for cached shape was false (abort query), return here + return hits.hasAnyHits(); + } + + const Pruner* staticPruner = mSQManager.get(PruningIndex::eSTATIC).pruner(); + const Pruner* dynamicPruner = mSQManager.get(PruningIndex::eDYNAMIC).pruner(); + const CompoundPruner* compoundPruner = mSQManager.getCompoundPruner().pruner(); + + const PxU32 doStatics = filterData.flags & PxQueryFlag::eSTATIC; + const PxU32 doDynamics = filterData.flags & PxQueryFlag::eDYNAMIC; + + if(HitTypeSupport::IsRaycast) + { + bool again = doStatics ? staticPruner->raycast(input.getOrigin(), input.getDir(), pcb.mShrunkDistance, pcb) : true; + if(!again) + return hits.hasAnyHits(); + + if(doDynamics) + again = dynamicPruner->raycast(input.getOrigin(), input.getDir(), pcb.mShrunkDistance, pcb); + + if(again) + again = compoundPruner->raycast(input.getOrigin(), input.getDir(), pcb.mShrunkDistance, pcb, filterData.flags); + + cbr.again = again; // update the status to avoid duplicate processTouches() + return hits.hasAnyHits(); + } + else if(HitTypeSupport::IsOverlap) + { + PX_ASSERT(input.geometry); + + const ShapeData sd(*input.geometry, *input.pose, input.inflation); + pcb.mShapeData = &sd; + PxAgain again = doStatics ? staticPruner->overlap(sd, pcb) : true; + if(!again) // && (filterData.flags & PxQueryFlag::eANY_HIT)) + return hits.hasAnyHits(); + + if(doDynamics) + again = dynamicPruner->overlap(sd, pcb); + + if(again) + again = compoundPruner->overlap(sd, pcb, filterData.flags); + + cbr.again = again; // update the status to avoid duplicate processTouches() + return hits.hasAnyHits(); + } + else + { + PX_ASSERT(HitTypeSupport::IsSweep); + PX_ASSERT(input.geometry); + + const ShapeData sd(*input.geometry, *input.pose, input.inflation); + pcb.mQueryShapeBounds = sd.getPrunerInflatedWorldAABB(); + pcb.mQueryShapeBoundsValid = true; + pcb.mShapeData = &sd; + PxAgain again = doStatics ? staticPruner->sweep(sd, input.getDir(), pcb.mShrunkDistance, pcb) : true; + if(!again) + return hits.hasAnyHits(); + + if(doDynamics) + again = dynamicPruner->sweep(sd, input.getDir(), pcb.mShrunkDistance, pcb); + + if(again) + again = compoundPruner->sweep(sd, input.getDir(), pcb.mShrunkDistance, pcb, filterData.flags); + + cbr.again = again; // update the status to avoid duplicate processTouches() + return hits.hasAnyHits(); + } +} + +void NpSceneQueries::sceneQueriesStaticPrunerUpdate(PxBaseTask* ) +{ + PX_PROFILE_ZONE("SceneQuery.sceneQueriesStaticPrunerUpdate", getContextId()); + // run pruner build only, this will build the new tree only, no commit happens + mSQManager.sceneQueryBuildStep(PruningIndex::eSTATIC); +} + +void NpSceneQueries::sceneQueriesDynamicPrunerUpdate(PxBaseTask*) +{ + PX_PROFILE_ZONE("SceneQuery.sceneQueriesDynamicPrunerUpdate", getContextId()); + // run pruner build only, this will build the new tree only, no commit happens + mSQManager.sceneQueryBuildStep(PruningIndex::eDYNAMIC); +} + +//explicit template instantiation +template bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; +template bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; +template bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; + diff --git a/src/PhysX/physx/source/physx/src/NpSceneQueries.h b/src/PhysX/physx/source/physx/src/NpSceneQueries.h new file mode 100644 index 000000000..e07fcfd73 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpSceneQueries.h @@ -0,0 +1,236 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_NP_SCENEQUERIES +#define PX_PHYSICS_NP_SCENEQUERIES + + +#include "PxQueryReport.h" +#include "PsIntrinsics.h" +#include "CmPhysXCommon.h" +#include "SqSceneQueryManager.h" +#include "GuTriangleMesh.h" +#include "GuRaycastTests.h" +#include "GuSweepTests.h" +#include "GuOverlapTests.h" +#include "NpSceneAccessor.h" +#include "ScbScene.h" + +#if PX_SUPPORT_PVD +#include "NpPvdSceneQueryCollector.h" +#endif + +namespace physx { namespace Sq { + + struct QueryID { enum Enum { + QUERY_RAYCAST_ANY_OBJECT, + QUERY_RAYCAST_CLOSEST_OBJECT, + QUERY_RAYCAST_ALL_OBJECTS, + + QUERY_OVERLAP_SPHERE_ALL_OBJECTS, + QUERY_OVERLAP_AABB_ALL_OBJECTS, + QUERY_OVERLAP_OBB_ALL_OBJECTS, + QUERY_OVERLAP_CAPSULE_ALL_OBJECTS, + QUERY_OVERLAP_CONVEX_ALL_OBJECTS, + + QUERY_LINEAR_OBB_SWEEP_CLOSEST_OBJECT, + QUERY_LINEAR_CAPSULE_SWEEP_CLOSEST_OBJECT, + QUERY_LINEAR_CONVEX_SWEEP_CLOSEST_OBJECT + }; }; +} + +struct MultiQueryInput +{ + const PxVec3* rayOrigin; // only valid for raycasts + const PxVec3* unitDir; // only valid for raycasts and sweeps + PxReal maxDistance; // only valid for raycasts and sweeps + const PxGeometry* geometry; // only valid for overlaps and sweeps + const PxTransform* pose; // only valid for overlaps and sweeps + PxReal inflation; // only valid for sweeps + + // Raycast constructor + MultiQueryInput(const PxVec3& aRayOrigin, const PxVec3& aUnitDir, PxReal aMaxDist) + { + Ps::prefetchLine(&aRayOrigin); + Ps::prefetchLine(&aUnitDir); + rayOrigin = &aRayOrigin; + unitDir = &aUnitDir; + maxDistance = aMaxDist; + geometry = NULL; + pose = NULL; + inflation = 0.0f; + } + + // Overlap constructor + MultiQueryInput(const PxGeometry* aGeometry, const PxTransform* aPose) + { + Ps::prefetchLine(aGeometry); + Ps::prefetchLine(aPose); + geometry = aGeometry; + pose = aPose; + inflation = 0.0f; + rayOrigin = unitDir = NULL; + } + + // Sweep constructor + MultiQueryInput( + const PxGeometry* aGeometry, const PxTransform* aPose, + const PxVec3& aUnitDir, const PxReal aMaxDist, const PxReal aInflation) + { + Ps::prefetchLine(aGeometry); + Ps::prefetchLine(aPose); + Ps::prefetchLine(&aUnitDir); + rayOrigin = NULL; + maxDistance = aMaxDist; + unitDir = &aUnitDir; + geometry = aGeometry; + pose = aPose; + inflation = aInflation; + } + + PX_FORCE_INLINE const PxVec3& getDir() const { PX_ASSERT(unitDir); return *unitDir; } + PX_FORCE_INLINE const PxVec3& getOrigin() const { PX_ASSERT(rayOrigin); return *rayOrigin; } +}; + +struct BatchQueryFilterData +{ + void* filterShaderData; + PxU32 filterShaderDataSize; + PxBatchQueryPreFilterShader preFilterShader; + PxBatchQueryPostFilterShader postFilterShader; + #if PX_SUPPORT_PVD + Vd::PvdSceneQueryCollector* collector; // gets set to bq collector + #endif + BatchQueryFilterData(void* fsData, PxU32 fsSize, PxBatchQueryPreFilterShader preFs, PxBatchQueryPostFilterShader postFs) + : filterShaderData(fsData), filterShaderDataSize(fsSize), preFilterShader(preFs), postFilterShader(postFs) + { + #if PX_SUPPORT_PVD + collector = NULL; + #endif + } +}; + +class PxGeometry; + +class NpSceneQueries : public NpSceneAccessor +{ + PX_NOCOPY(NpSceneQueries) + +public: + NpSceneQueries(const PxSceneDesc& desc); + ~NpSceneQueries(); + + template + bool multiQuery( + const MultiQueryInput& in, + PxHitCallback& hits, PxHitFlags hitFlags, const PxQueryCache* cache, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + BatchQueryFilterData* bqFd) const; + + // Synchronous scene queries + virtual bool raycast( + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, // Ray data + PxRaycastCallback& hitCall, PxHitFlags hitFlags, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache) const; + + virtual bool sweep( + const PxGeometry& geometry, const PxTransform& pose, // GeomObject data + const PxVec3& unitDir, const PxReal distance, // Ray data + PxSweepCallback& hitCall, PxHitFlags hitFlags, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache, const PxReal inflation) const; + + virtual bool overlap( + const PxGeometry& geometry, const PxTransform& transform, // GeomObject data + PxOverlapCallback& hitCall, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall) const; + + PX_FORCE_INLINE PxU64 getContextId() const { return PxU64(reinterpret_cast(this)); } + PX_FORCE_INLINE Scb::Scene& getScene() { return mScene; } + PX_FORCE_INLINE const Scb::Scene& getScene() const { return mScene; } + PX_FORCE_INLINE PxU32 getFlagsFast() const { return mScene.getFlags(); } + PX_FORCE_INLINE Sq::SceneQueryManager& getSceneQueryManagerFast() { return mSQManager; } + PX_FORCE_INLINE PxSceneQueryUpdateMode::Enum getSceneQueryUpdateModeFast() const { return mSceneQueryUpdateMode; } + + void sceneQueriesStaticPrunerUpdate(PxBaseTask* continuation); + void sceneQueriesDynamicPrunerUpdate(PxBaseTask* continuation); + + Scb::Scene mScene; + + Sq::SceneQueryManager mSQManager; + + const Gu::GeomRaycastTable& mCachedRaycastFuncs; + const Gu::GeomSweepFuncs& mCachedSweepFuncs; + const Gu::GeomOverlapTable* mCachedOverlapFuncs; + + typedef Cm::DelegateTask SceneQueriesStaticPrunerUpdate; + typedef Cm::DelegateTask SceneQueriesDynamicPrunerUpdate; + SceneQueriesStaticPrunerUpdate mSceneQueriesStaticPrunerUpdate; + SceneQueriesDynamicPrunerUpdate mSceneQueriesDynamicPrunerUpdate; + + PxSceneQueryUpdateMode::Enum mSceneQueryUpdateMode; + +#if PX_SUPPORT_PVD +public: + //Scene query and hits for pvd, collected in current frame + mutable Vd::PvdSceneQueryCollector mSingleSqCollector; + mutable Vd::PvdSceneQueryCollector mBatchedSqCollector; + +PX_FORCE_INLINE Vd::PvdSceneQueryCollector& getSingleSqCollector() const {return mSingleSqCollector;} +PX_FORCE_INLINE Vd::PvdSceneQueryCollector& getBatchedSqCollector() const {return mBatchedSqCollector;} +#endif // PX_SUPPORT_PVD +}; + +#if PX_SUPPORT_EXTERN_TEMPLATE +//explicit template instantiation declaration +extern template +bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; + +extern template +bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; + +extern template +bool NpSceneQueries::multiQuery(const MultiQueryInput&, PxHitCallback&, PxHitFlags, const PxQueryCache*, const PxQueryFilterData&, PxQueryFilterCallback*, BatchQueryFilterData*) const; +#endif + +namespace Sq { class AABBPruner; class AABBTreeRuntimeNode; class AABBTree; } + +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +#if PX_VC + #pragma warning(pop) +#endif + +} // namespace physx, sq + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp b/src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp new file mode 100644 index 000000000..eb48292b2 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp @@ -0,0 +1,182 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuHeightField.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "GuTriangleMeshBV4.h" +#include "GuTriangleMeshRTree.h" + +#include "NpRigidStatic.h" +#include "NpRigidDynamic.h" +#include "NpArticulation.h" +#include "NpArticulationLink.h" +#include "NpArticulationJoint.h" +#include "NpMaterial.h" +#include "NpAggregate.h" +#include "GuHeightFieldData.h" + +#include "SqPruningStructure.h" + +#include "PxBase.h" +#include "PxSerialFramework.h" +#include "PxSerializer.h" +#include "PxPhysicsSerialization.h" + +namespace physx +{ + using namespace physx::Gu; + + template<> + void PxSerializerDefaultAdapter::exportData(PxBase& obj, PxSerializationContext& s) const + { + PxU32 classSize = sizeof(NpRigidDynamic); + NpRigidDynamic& dynamic = static_cast(obj); + + PxsBodyCore serialCore; + size_t address = dynamic.getScbBodyFast().getScBody().getSerialCore(serialCore); + PxU32 offset = PxU32(address - reinterpret_cast(&dynamic)); + PX_ASSERT(offset + sizeof(serialCore) <= classSize); + s.writeData(&dynamic, offset); + s.writeData(&serialCore, sizeof(serialCore)); + void* tail = reinterpret_cast(&dynamic) + offset + sizeof(serialCore); + s.writeData(tail, classSize - offset - sizeof(serialCore)); + } + + template<> + void PxSerializerDefaultAdapter::registerReferences(PxBase& obj, PxSerializationContext& s) const + { + NpRigidDynamic& dynamic = static_cast(obj); + + s.registerReference(obj, PX_SERIAL_REF_KIND_PXBASE, size_t(&obj)); + + struct RequiresCallback : public PxProcessPxBaseCallback + { + RequiresCallback(physx::PxSerializationContext& c) : context(c) {} + RequiresCallback& operator=(const RequiresCallback&) { PX_ASSERT(0); return *this; } //PX_NOCOPY doesn't work for local classes + void process(PxBase& base) + { + context.registerReference(base, PX_SERIAL_REF_KIND_PXBASE, size_t(&base)); + } + PxSerializationContext& context; + }; + + RequiresCallback callback(s); + dynamic.requiresObjects(callback); + } + + template<> + void PxSerializerDefaultAdapter::registerReferences(PxBase& obj, PxSerializationContext& s) const + { + NpShape& shape = static_cast(obj); + + s.registerReference(obj, PX_SERIAL_REF_KIND_PXBASE, size_t(&obj)); + + struct RequiresCallback : public PxProcessPxBaseCallback + { + RequiresCallback(physx::PxSerializationContext& c) : context(c) {} + RequiresCallback &operator=(const RequiresCallback&) { PX_ASSERT(0); return *this; } //PX_NOCOPY doesn't work for local classes + void process(PxBase& base) + { + PxMaterial* pxMaterial = base.is(); + if (!pxMaterial) + { + context.registerReference(base, PX_SERIAL_REF_KIND_PXBASE, size_t(&base)); + } + else + { + //ideally we would move this part to ScShapeCore but we don't yet have a MaterialManager available there. + PxU32 index = static_cast(pxMaterial)->getHandle(); + context.registerReference(base, PX_SERIAL_REF_KIND_MATERIAL_IDX, size_t(index)); + } + } + PxSerializationContext& context; + }; + + RequiresCallback callback(s); + shape.requiresObjects(callback); + } + + template<> + bool PxSerializerDefaultAdapter::isSubordinate() const + { + return true; + } + + template<> + bool PxSerializerDefaultAdapter::isSubordinate() const + { + return true; + } + + template<> + bool PxSerializerDefaultAdapter::isSubordinate() const + { + return true; + } +} + +using namespace physx; + +void PxRegisterPhysicsSerializers(PxSerializationRegistry& sr) +{ + sr.registerSerializer(PxConcreteType::eCONVEX_MESH, PX_NEW_SERIALIZER_ADAPTER(ConvexMesh)); + sr.registerSerializer(PxConcreteType::eTRIANGLE_MESH_BVH33, PX_NEW_SERIALIZER_ADAPTER(RTreeTriangleMesh)); + sr.registerSerializer(PxConcreteType::eTRIANGLE_MESH_BVH34, PX_NEW_SERIALIZER_ADAPTER(BV4TriangleMesh)); + sr.registerSerializer(PxConcreteType::eHEIGHTFIELD, PX_NEW_SERIALIZER_ADAPTER(HeightField)); + sr.registerSerializer(PxConcreteType::eRIGID_DYNAMIC, PX_NEW_SERIALIZER_ADAPTER(NpRigidDynamic)); + sr.registerSerializer(PxConcreteType::eRIGID_STATIC, PX_NEW_SERIALIZER_ADAPTER(NpRigidStatic)); + sr.registerSerializer(PxConcreteType::eSHAPE, PX_NEW_SERIALIZER_ADAPTER(NpShape)); + sr.registerSerializer(PxConcreteType::eMATERIAL, PX_NEW_SERIALIZER_ADAPTER(NpMaterial)); + sr.registerSerializer(PxConcreteType::eCONSTRAINT, PX_NEW_SERIALIZER_ADAPTER(NpConstraint)); + sr.registerSerializer(PxConcreteType::eAGGREGATE, PX_NEW_SERIALIZER_ADAPTER(NpAggregate)); + sr.registerSerializer(PxConcreteType::eARTICULATION, PX_NEW_SERIALIZER_ADAPTER(NpArticulation)); + sr.registerSerializer(PxConcreteType::eARTICULATION_LINK, PX_NEW_SERIALIZER_ADAPTER(NpArticulationLink)); + sr.registerSerializer(PxConcreteType::eARTICULATION_JOINT, PX_NEW_SERIALIZER_ADAPTER(NpArticulationJoint)); + sr.registerSerializer(PxConcreteType::ePRUNING_STRUCTURE, PX_NEW_SERIALIZER_ADAPTER(Sq::PruningStructure)); +} + + +void PxUnregisterPhysicsSerializers(PxSerializationRegistry& sr) +{ + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eCONVEX_MESH)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eTRIANGLE_MESH_BVH33)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eTRIANGLE_MESH_BVH34)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eHEIGHTFIELD)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eRIGID_DYNAMIC)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eRIGID_STATIC)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eSHAPE)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eMATERIAL)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eCONSTRAINT)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eAGGREGATE)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eARTICULATION)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eARTICULATION_LINK)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::eARTICULATION_JOINT)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxConcreteType::ePRUNING_STRUCTURE)); +} diff --git a/src/PhysX/physx/source/physx/src/NpShape.cpp b/src/PhysX/physx/source/physx/src/NpShape.cpp new file mode 100644 index 000000000..fbbe27edc --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpShape.cpp @@ -0,0 +1,823 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpCast.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "ScbNpDeps.h" +#include "GuBounds.h" + +using namespace physx; +using namespace Sq; + +static PX_FORCE_INLINE void updatePvdProperties(const Scb::Shape& shape) +{ +#if PX_SUPPORT_PVD + Scb::Scene* scbScene = shape.getScbSceneForAPI(); + if(scbScene) + scbScene->getScenePvdClient().updatePvdProperties(&shape); +#else + PX_UNUSED(shape); +#endif +} + +NpShape::NpShape(const PxGeometry& geometry, PxShapeFlags shapeFlags, const PxU16* materialIndices, PxU16 materialCount, bool isExclusive) +: PxShape (PxConcreteType::eSHAPE, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +, mActor (NULL) +, mShape (geometry, shapeFlags, materialIndices, materialCount, isExclusive) +, mName (NULL) +, mExclusiveAndActorCount (isExclusive ? EXCLUSIVE_MASK : 0) +{ + PX_ASSERT(mShape.getScShape().getPxShape() == static_cast(this)); + + PxShape::userData = NULL; + + incMeshRefCount(); +} + +NpShape::~NpShape() +{ + decMeshRefCount(); + + PxU32 nbMaterials = mShape.getNbMaterials(); + for (PxU32 i=0; i < nbMaterials; i++) + { + NpMaterial* mat = static_cast(mShape.getMaterial(i)); + mat->decRefCount(); + } +} + +void NpShape::onRefCountZero() +{ + NpFactory::getInstance().onShapeRelease(this); + // see NpShape.h for ref counting semantics for shapes + NpDestroy(getScbShape()); +} + +// PX_SERIALIZATION + +NpShape::NpShape(PxBaseFlags baseFlags) : PxShape(baseFlags), mShape(PxEmpty) +{ + mExclusiveAndActorCount &= EXCLUSIVE_MASK; +} + +void NpShape::exportExtraData(PxSerializationContext& stream) +{ + getScbShape().getScShape().exportExtraData(stream); + stream.writeName(mName); +} + +void NpShape::importExtraData(PxDeserializationContext& context) +{ + getScbShape().getScShape().importExtraData(context); + context.readName(mName); +} + +void NpShape::requiresObjects(PxProcessPxBaseCallback& c) +{ + //meshes + PxBase* mesh = NULL; + switch(mShape.getGeometryType()) + { + case PxGeometryType::eCONVEXMESH: + mesh = static_cast(mShape.getGeometry()).convexMesh; + break; + case PxGeometryType::eHEIGHTFIELD: + mesh = static_cast(mShape.getGeometry()).heightField; + break; + case PxGeometryType::eTRIANGLEMESH: + mesh = static_cast(mShape.getGeometry()).triangleMesh; + break; + case PxGeometryType::eSPHERE: + case PxGeometryType::ePLANE: + case PxGeometryType::eCAPSULE: + case PxGeometryType::eBOX: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + + if(mesh) + c.process(*mesh); + + //material + PxU32 nbMaterials = mShape.getNbMaterials(); + for (PxU32 i=0; i < nbMaterials; i++) + { + NpMaterial* mat = static_cast(mShape.getMaterial(i)); + c.process(*mat); + } +} + +void NpShape::resolveReferences(PxDeserializationContext& context) +{ + // getMaterials() only works after material indices have been patched. + // in order to get to the new material indices, we need access to the new materials. + // this only leaves us with the option of acquiring the material through the context given an old material index (we do have the mapping) + { + PxU32 nbIndices = mShape.getScShape().getNbMaterialIndices(); + const PxU16* indices = mShape.getScShape().getMaterialIndices(); + + for (PxU32 i=0; i < nbIndices; i++) + { + PxBase* base = context.resolveReference(PX_SERIAL_REF_KIND_MATERIAL_IDX, size_t(indices[i])); + PX_ASSERT(base && base->is()); + + NpMaterial& material = *static_cast(base); + getScbShape().getScShape().resolveMaterialReference(i, PxU16(material.getHandle())); + } + } + + context.translatePxBase(mActor); + + getScbShape().getScShape().resolveReferences(context); + + + incMeshRefCount(); + + // Increment materials' refcounts in a second pass. Works better in case of failure above. + PxU32 nbMaterials = mShape.getNbMaterials(); + for (PxU32 i=0; i < nbMaterials; i++) + { + NpMaterial* mat = static_cast(mShape.getMaterial(i)); + mat->incRefCount(); + } +} + +NpShape* NpShape::createObject(PxU8*& address, PxDeserializationContext& context) +{ + NpShape* obj = new (address) NpShape(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(NpShape); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} +//~PX_SERIALIZATION + +PxU32 NpShape::getReferenceCount() const +{ + return getRefCount(); +} + +void NpShape::acquireReference() +{ + incRefCount(); +} + +void NpShape::release() +{ + PX_CHECK_AND_RETURN(getRefCount() > 1 || getActorCount() == 0, "PxShape::release: last reference to a shape released while still attached to an actor!"); + NP_WRITE_CHECK(getOwnerScene()); + releaseInternal(); +} + +void NpShape::releaseInternal() +{ + decRefCount(); +} + +Sc::RigidCore& NpShape::getScRigidObjectExclusive() const +{ + const PxType actorType = mActor->getConcreteType(); + + if (actorType == PxConcreteType::eRIGID_DYNAMIC) + return static_cast(*mActor).getScbBodyFast().getScBody(); + else if (actorType == PxConcreteType::eARTICULATION_LINK) + return static_cast(*mActor).getScbBodyFast().getScBody(); + else + return static_cast(*mActor).getScbRigidStaticFast().getScStatic(); +} + +void NpShape::updateSQ(const char* errorMessage) +{ + if(mActor && (mShape.getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE)) + { + NpScene* scene = NpActor::getAPIScene(*mActor); + NpShapeManager* shapeManager = NpActor::getShapeManager(*mActor); + if(scene) + { + PxU32 compoundId; + const PrunerData sqData = shapeManager->findSceneQueryData(*this, compoundId); + scene->getSceneQueryManagerFast().markForUpdate(compoundId, sqData); + } + + // invalidate the pruning structure if the actor bounds changed + if(shapeManager->getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, errorMessage); + shapeManager->getPruningStructure()->invalidate(mActor); + } + } +} + +PxGeometryType::Enum NpShape::getGeometryType() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getGeometryType(); +} + +void NpShape::setGeometry(const PxGeometry& g) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setGeometry: shared shapes attached to actors are not writable."); + PX_SIMD_GUARD; + + // PT: fixes US2117 + if(g.getType() != getGeometryTypeFast()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxShape::setGeometry(): Invalid geometry type. Changing the type of the shape is not supported."); + return; + } + +#if PX_CHECKED + bool isValid = false; + switch(g.getType()) + { + case PxGeometryType::eSPHERE: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::ePLANE: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eCAPSULE: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eBOX: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eCONVEXMESH: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eTRIANGLEMESH: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eHEIGHTFIELD: + isValid = static_cast(g).isValid(); + break; + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + + if(!isValid) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxShape::setGeometry(): Invalid geometry!"); + return; + } +#endif + + decMeshRefCount(); + + mShape.setGeometry(g); + + incMeshRefCount(); + + updateSQ("PxShape::setGeometry: Shape is a part of pruning structure, pruning structure is now invalid!"); +} + +PxGeometryHolder NpShape::getGeometry() const +{ + PX_COMPILE_TIME_ASSERT(sizeof(Gu::GeometryUnion)>=sizeof(PxGeometryHolder)); + return reinterpret_cast(mShape.getGeometry()); +} + +template +static PX_FORCE_INLINE bool getGeometryT(const NpShape* npShape, PxGeometryType::Enum type, T& geom) +{ + NP_READ_CHECK(npShape->getOwnerScene()); + + if(npShape->getGeometryTypeFast() != type) + return false; + + geom = static_cast(npShape->getScbShape().getGeometry()); + return true; +} + +bool NpShape::getBoxGeometry(PxBoxGeometry& g) const { return getGeometryT(this, PxGeometryType::eBOX, g); } +bool NpShape::getSphereGeometry(PxSphereGeometry& g) const { return getGeometryT(this, PxGeometryType::eSPHERE, g); } +bool NpShape::getCapsuleGeometry(PxCapsuleGeometry& g) const { return getGeometryT(this, PxGeometryType::eCAPSULE, g); } +bool NpShape::getPlaneGeometry(PxPlaneGeometry& g) const { return getGeometryT(this, PxGeometryType::ePLANE, g); } +bool NpShape::getConvexMeshGeometry(PxConvexMeshGeometry& g) const { return getGeometryT(this, PxGeometryType::eCONVEXMESH, g); } +bool NpShape::getTriangleMeshGeometry(PxTriangleMeshGeometry& g) const { return getGeometryT(this, PxGeometryType::eTRIANGLEMESH, g); } +bool NpShape::getHeightFieldGeometry(PxHeightFieldGeometry& g) const { return getGeometryT(this, PxGeometryType::eHEIGHTFIELD, g); } + +PxRigidActor* NpShape::getActor() const +{ + NP_READ_CHECK(getOwnerScene()); + return mActor; +} + +void NpShape::setLocalPose(const PxTransform& newShape2Actor) +{ + PX_CHECK_AND_RETURN(newShape2Actor.isSane(), "PxShape::setLocalPose: pose is not valid."); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setLocalPose: shared shapes attached to actors are not writable."); + NP_WRITE_CHECK(getOwnerScene()); + + mShape.setShape2Actor(newShape2Actor.getNormalized()); + + updateSQ("PxShape::setLocalPose: Shape is a part of pruning structure, pruning structure is now invalid!"); +} + +PxTransform NpShape::getLocalPose() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getShape2Actor(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpShape::setSimulationFilterData(const PxFilterData& data) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setSimulationFilterData: shared shapes attached to actors are not writable."); + mShape.setSimulationFilterData(data); +} + +PxFilterData NpShape::getSimulationFilterData() const +{ + NP_READ_CHECK(getOwnerScene()); + return mShape.getSimulationFilterData(); +} + +void NpShape::setQueryFilterData(const PxFilterData& data) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setQueryFilterData: shared shapes attached to actors are not writable."); + + mShape.getScShape().setQueryFilterData(data); // PT: this one doesn't need double-buffering + + updatePvdProperties(mShape); +} + +PxFilterData NpShape::getQueryFilterData() const +{ + NP_READ_CHECK(getOwnerScene()); + + return getQueryFilterDataFast(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NpShape::setMaterials(PxMaterial*const* materials, PxU16 materialCount) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setMaterials: shared shapes attached to actors are not writable."); + +#if PX_CHECKED + if (!NpShape::checkMaterialSetup(mShape.getGeometry(), "PxShape::setMaterials()", materials, materialCount)) + return; +#endif + + PxU32 oldMaterialCount = mShape.getNbMaterials(); + PX_ALLOCA(oldMaterials, PxMaterial*, oldMaterialCount); + PxU32 tmp = mShape.getMaterials(oldMaterials, oldMaterialCount); + PX_ASSERT(tmp == oldMaterialCount); + PX_UNUSED(tmp); + + if (mShape.setMaterials(materials, materialCount)) + { + for(PxU32 i=0; i < materialCount; i++) + static_cast(materials[i])->incRefCount(); + + for(PxU32 i=0; i < oldMaterialCount; i++) + static_cast(oldMaterials[i])->decRefCount(); + } +} + +PxU16 NpShape::getNbMaterials() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getNbMaterials(); +} + +PxU32 NpShape::getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getMaterials(userBuffer, bufferSize, startIndex); +} + +PxMaterial* NpShape::getMaterialFromInternalFaceIndex(PxU32 faceIndex) const +{ + NP_READ_CHECK(getOwnerScene()); + + bool isHf = (getGeometryType() == PxGeometryType::eHEIGHTFIELD); + bool isMesh = (getGeometryType() == PxGeometryType::eTRIANGLEMESH); + if( faceIndex == 0xFFFFffff && (isHf || isMesh) ) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxShape::getMaterialFromInternalFaceIndex received 0xFFFFffff as input - returning NULL."); + return NULL; + } + + PxMaterialTableIndex hitMatTableId = 0; + + if(isHf) + { + PxHeightFieldGeometry hfGeom; + getHeightFieldGeometry(hfGeom); + + hitMatTableId = hfGeom.heightField->getTriangleMaterialIndex(faceIndex); + } + else if(isMesh) + { + PxTriangleMeshGeometry triGeo; + getTriangleMeshGeometry(triGeo); + + Gu::TriangleMesh* tm = static_cast(triGeo.triangleMesh); + if(tm->hasPerTriangleMaterials()) + hitMatTableId = triGeo.triangleMesh->getTriangleMaterialIndex(faceIndex); + } + + return getMaterial(hitMatTableId); +} + +void NpShape::setContactOffset(PxReal contactOffset) +{ + NP_WRITE_CHECK(getOwnerScene()); + + PX_CHECK_AND_RETURN(PxIsFinite(contactOffset), "PxShape::setContactOffset: invalid float"); + PX_CHECK_AND_RETURN((contactOffset >= 0.0f && contactOffset > mShape.getRestOffset()), "PxShape::setContactOffset: contactOffset should be positive, and greater than restOffset!"); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setContactOffset: shared shapes attached to actors are not writable."); + + mShape.setContactOffset(contactOffset); +} + +PxReal NpShape::getContactOffset() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getContactOffset(); +} + +void NpShape::setRestOffset(PxReal restOffset) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(PxIsFinite(restOffset), "PxShape::setRestOffset: invalid float"); + PX_CHECK_AND_RETURN((restOffset < mShape.getContactOffset()), "PxShape::setRestOffset: restOffset should be less than contactOffset!"); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setRestOffset: shared shapes attached to actors are not writable."); + + mShape.setRestOffset(restOffset); +} + +PxReal NpShape::getRestOffset() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mShape.getRestOffset(); +} + +void NpShape::setTorsionalPatchRadius(PxReal radius) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(PxIsFinite(radius), "PxShape::setTorsionalPatchRadius: invalid float"); + PX_CHECK_AND_RETURN((radius >= 0.f), "PxShape::setTorsionalPatchRadius: must be >= 0.f"); + + mShape.setTorsionalPatchRadius(radius); +} + +PxReal NpShape::getTorsionalPatchRadius() const +{ + NP_READ_CHECK(getOwnerScene()); + return mShape.getTorsionalPatchRadius(); +} + +void NpShape::setMinTorsionalPatchRadius(PxReal radius) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(PxIsFinite(radius), "PxShape::setMinTorsionalPatchRadius: invalid float"); + PX_CHECK_AND_RETURN((radius >= 0.f), "PxShape::setMinTorsionalPatchRadius: must be >= 0.f"); + + mShape.setMinTorsionalPatchRadius(radius); +} + +PxReal NpShape::getMinTorsionalPatchRadius() const +{ + NP_READ_CHECK(getOwnerScene()); + return mShape.getMinTorsionalPatchRadius(); +} + +void NpShape::setFlagsInternal(PxShapeFlags inFlags) +{ + const bool hasMeshTypeGeom = mShape.getGeometryType() == PxGeometryType::eTRIANGLEMESH || mShape.getGeometryType() == PxGeometryType::eHEIGHTFIELD; + + if(hasMeshTypeGeom && (inFlags & PxShapeFlag::eTRIGGER_SHAPE)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxShape::setFlag(s): triangle mesh and heightfield triggers are not supported!"); + return; + } + + if((inFlags & PxShapeFlag::eSIMULATION_SHAPE) && (inFlags & PxShapeFlag::eTRIGGER_SHAPE)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxShape::setFlag(s): shapes cannot simultaneously be trigger shapes and simulation shapes."); + return; + } + + const PxShapeFlags oldFlags = mShape.getFlags(); + + const bool oldIsSimShape = oldFlags & PxShapeFlag::eSIMULATION_SHAPE; + const bool isSimShape = inFlags & PxShapeFlag::eSIMULATION_SHAPE; + + if(mActor) + { + const PxType type = mActor->getConcreteType(); + + // PT: US5732 - support kinematic meshes + bool isKinematic = false; + if(type==PxConcreteType::eRIGID_DYNAMIC) + { + PxRigidDynamic* rigidDynamic = static_cast(mActor); + isKinematic = rigidDynamic->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC; + } + + if((type != PxConcreteType::eRIGID_STATIC) && !isKinematic && isSimShape && !oldIsSimShape && (hasMeshTypeGeom || mShape.getGeometryType() == PxGeometryType::ePLANE)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxShape::setFlag(s): triangle mesh, heightfield and plane shapes can only be simulation shapes if part of a PxRigidStatic!"); + return; + } + } + + const bool oldHasSceneQuery = oldFlags & PxShapeFlag::eSCENE_QUERY_SHAPE; + const bool hasSceneQuery = inFlags & PxShapeFlag::eSCENE_QUERY_SHAPE; + + mShape.setFlags(inFlags); + + if(oldHasSceneQuery != hasSceneQuery && mActor) + { + NpScene* npScene = getAPIScene(); + NpShapeManager* shapeManager = NpActor::getShapeManager(*mActor); + if(npScene) + { + if(hasSceneQuery) + shapeManager->setupSceneQuery(npScene->getSceneQueryManagerFast(), *mActor, *this); + else + shapeManager->teardownSceneQuery(npScene->getSceneQueryManagerFast(), *this); + } + + // invalidate the pruning structure if the actor bounds changed + if(shapeManager->getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxShape::setFlag: Shape is a part of pruning structure, pruning structure is now invalid!"); + shapeManager->getPruningStructure()->invalidate(mActor); + } + } +} + +void NpShape::setFlag(PxShapeFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setFlag: shared shapes attached to actors are not writable."); + PX_SIMD_GUARD; + + PxShapeFlags shapeFlags = mShape.getFlags(); + shapeFlags = value ? shapeFlags | flag : shapeFlags & ~flag; + + setFlagsInternal(shapeFlags); +} + +void NpShape::setFlags(PxShapeFlags inFlags) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setFlags: shared shapes attached to actors are not writable."); + PX_SIMD_GUARD; + + setFlagsInternal(inFlags); +} + +PxShapeFlags NpShape::getFlags() const +{ + NP_READ_CHECK(getOwnerScene()); + return mShape.getFlags(); +} + +bool NpShape::isExclusive() const +{ + NP_READ_CHECK(getOwnerScene()); + return (mExclusiveAndActorCount & EXCLUSIVE_MASK) != 0; +} + +void NpShape::onActorAttach(PxRigidActor& actor) +{ + incRefCount(); + if(isExclusiveFast()) + mActor = &actor; + Ps::atomicIncrement(&mExclusiveAndActorCount); +} + +void NpShape::onActorDetach() +{ + PX_ASSERT(getActorCount() > 0); + Ps::atomicDecrement(&mExclusiveAndActorCount); + if(isExclusiveFast()) + mActor = NULL; + decRefCount(); +} + +void NpShape::setName(const char* debugName) +{ + NP_WRITE_CHECK(getOwnerScene()); + PX_CHECK_AND_RETURN(isWritable(), "PxShape::setName: shared shapes attached to actors are not writable."); + + mName = debugName; + + updatePvdProperties(mShape); +} + +const char* NpShape::getName() const +{ + NP_READ_CHECK(getOwnerScene()); + + return mName; +} + +NpScene* NpShape::getOwnerScene() const +{ + return mActor ? NpActor::getOwnerScene(*mActor) : NULL; +} + +NpScene* NpShape::getAPIScene() const +{ + // gets called when we update SQ structures due to a write - in which case there must be an actor + PX_ASSERT(mActor); + return NpActor::getAPIScene(*mActor); +} + +/////////////////////////////////////////////////////////////////////////////// + +namespace physx +{ +Sc::RigidCore* NpShapeGetScRigidObjectFromScbSLOW(const Scb::Shape& scb) +{ + const NpShape* np = getNpShape(&scb); + return np->NpShape::getActor() ? &np->getScRigidObjectExclusive() : NULL; +} + +size_t NpShapeGetScPtrOffset() +{ + const size_t offset = size_t(&(reinterpret_cast(0)->getScbShape().getScShape())); + return offset; +} + +void NpShapeIncRefCount(Scb::Shape& scb) +{ + NpShape* np = const_cast(getNpShape(&scb)); + np->incRefCount(); +} + +void NpShapeDecRefCount(Scb::Shape& scb) +{ + NpShape* np = const_cast(getNpShape(&scb)); + np->decRefCount(); +} +} + +// see NpConvexMesh.h, NpHeightField.h, NpTriangleMesh.h for details on how ref counting works for meshes +Cm::RefCountable* NpShape::getMeshRefCountable() +{ + switch(mShape.getGeometryType()) + { + case PxGeometryType::eCONVEXMESH: + return static_cast( + static_cast(mShape.getGeometry()).convexMesh); + + case PxGeometryType::eHEIGHTFIELD: + return static_cast( + static_cast(mShape.getGeometry()).heightField); + + case PxGeometryType::eTRIANGLEMESH: + return static_cast( + static_cast(mShape.getGeometry()).triangleMesh); + + case PxGeometryType::eSPHERE: + case PxGeometryType::ePLANE: + case PxGeometryType::eCAPSULE: + case PxGeometryType::eBOX: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + return NULL; +} + +bool NpShape::isWritable() +{ + // a shape is writable if it's exclusive, or it's not connected to any actors (which is true if the ref count is 1 and the user ref is not released.) + return isExclusiveFast() || (getRefCount()==1 && (mBaseFlags & PxBaseFlag::eIS_RELEASABLE)); +} + +void NpShape::incMeshRefCount() +{ + Cm::RefCountable* npMesh = getMeshRefCountable(); + if(npMesh) + npMesh->incRefCount(); +} + +void NpShape::decMeshRefCount() +{ + Cm::RefCountable* npMesh = getMeshRefCountable(); + if(npMesh) + npMesh->decRefCount(); +} + +bool NpShape::checkMaterialSetup(const PxGeometry& geom, const char* errorMsgPrefix, PxMaterial*const* materials, PxU16 materialCount) +{ + for(PxU32 i=0; i 1 && (geom.getType() != PxGeometryType::eHEIGHTFIELD) && (geom.getType() != PxGeometryType::eTRIANGLEMESH)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: multiple materials defined for single material geometry!", errorMsgPrefix); + return false; + } + + // verify we provide all materials required + if (materialCount > 1 && (geom.getType() == PxGeometryType::eTRIANGLEMESH)) + { + const PxTriangleMeshGeometry& meshGeom = static_cast(geom); + const PxTriangleMesh& mesh = *meshGeom.triangleMesh; + if(mesh.getTriangleMaterialIndex(0) != 0xffff) + { + for(PxU32 i = 0; i < mesh.getNbTriangles(); i++) + { + const PxMaterialTableIndex meshMaterialIndex = mesh.getTriangleMaterialIndex(i); + if(meshMaterialIndex >= materialCount) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: PxTriangleMesh material indices reference more materials than provided!", errorMsgPrefix); + break; + } + } + } + } + if (materialCount > 1 && (geom.getType() == PxGeometryType::eHEIGHTFIELD)) + { + const PxHeightFieldGeometry& meshGeom = static_cast(geom); + const PxHeightField& mesh = *meshGeom.heightField; + if(mesh.getTriangleMaterialIndex(0) != 0xffff) + { + const PxU32 nbTris = mesh.getNbColumns()*mesh.getNbRows()*2; + for(PxU32 i = 0; i < nbTris; i++) + { + const PxMaterialTableIndex meshMaterialIndex = mesh.getTriangleMaterialIndex(i); + if(meshMaterialIndex != PxHeightFieldMaterial::eHOLE && meshMaterialIndex >= materialCount) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: PxHeightField material indices reference more materials than provided!", errorMsgPrefix); + break; + } + } + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/physx/src/NpShape.h b/src/PhysX/physx/source/physx/src/NpShape.h new file mode 100644 index 000000000..ccc193ddf --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpShape.h @@ -0,0 +1,219 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_SHAPE +#define PX_PHYSICS_NP_SHAPE + +#include "PxShape.h" +#include "buffering/ScbShape.h" +#include "PxMetaData.h" + +namespace physx +{ + +struct NpInternalShapeFlag +{ + enum Enum + { + eEXCLUSIVE = (1<<0) + }; +}; + +/** +\brief collection of set bits defined in PxShapeFlag. + +@see PxShapeFlag +*/ +typedef PxFlags NpInternalShapeFlags; +PX_FLAGS_OPERATORS(NpInternalShapeFlag::Enum,PxU8) + + +class NpScene; +class NpShapeManager; + +namespace Scb +{ + class Scene; + class RigidObject; +} + +namespace Sc +{ + class MaterialCore; +} + +class NpShape : public PxShape, public Ps::UserAllocated, public Cm::RefCountable +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + NpShape(PxBaseFlags baseFlags); + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback& c); + void resolveReferences(PxDeserializationContext& context); + static NpShape* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + NpShape(const PxGeometry& geometry, + PxShapeFlags shapeFlags, + const PxU16* materialIndices, + PxU16 materialCount, + bool isExclusive); + + virtual ~NpShape(); + + //--------------------------------------------------------------------------------- + // PxShape implementation + //--------------------------------------------------------------------------------- + virtual void release(); //!< call to release from actor + virtual PxU32 getReferenceCount() const; + virtual void acquireReference(); + + virtual PxGeometryType::Enum getGeometryType() const; + + virtual void setGeometry(const PxGeometry&); + virtual PxGeometryHolder getGeometry() const; + virtual bool getBoxGeometry(PxBoxGeometry&) const; + virtual bool getSphereGeometry(PxSphereGeometry&) const; + virtual bool getCapsuleGeometry(PxCapsuleGeometry&) const; + virtual bool getPlaneGeometry(PxPlaneGeometry&) const; + virtual bool getConvexMeshGeometry(PxConvexMeshGeometry& g) const; + virtual bool getTriangleMeshGeometry(PxTriangleMeshGeometry& g) const; + virtual bool getHeightFieldGeometry(PxHeightFieldGeometry& g) const; + + virtual PxRigidActor* getActor() const; + + virtual void setLocalPose(const PxTransform& pose); + virtual PxTransform getLocalPose() const; + + virtual void setSimulationFilterData(const PxFilterData& data); + virtual PxFilterData getSimulationFilterData() const; + virtual void setQueryFilterData(const PxFilterData& data); + virtual PxFilterData getQueryFilterData() const; + + virtual void setMaterials(PxMaterial*const* materials, PxU16 materialCount); + virtual PxU16 getNbMaterials() const; + virtual PxU32 getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + virtual PxMaterial* getMaterialFromInternalFaceIndex(PxU32 faceIndex) const; + + virtual void setContactOffset(PxReal); + virtual PxReal getContactOffset() const; + + virtual void setRestOffset(PxReal); + virtual PxReal getRestOffset() const; + + virtual void setTorsionalPatchRadius(PxReal); + virtual PxReal getTorsionalPatchRadius() const; + + virtual void setMinTorsionalPatchRadius(PxReal); + virtual PxReal getMinTorsionalPatchRadius() const; + + virtual void setFlag(PxShapeFlag::Enum flag, bool value); + virtual void setFlags( PxShapeFlags inFlags ); + virtual PxShapeFlags getFlags() const; + + virtual bool isExclusive() const; + + virtual void setName(const char* debugName); + virtual const char* getName() const; + + //--------------------------------------------------------------------------------- + // RefCountable implementation + //--------------------------------------------------------------------------------- + + // Ref counting for shapes works like this: + // * for exclusive shapes the actor has a counted reference + // * for shared shapes, each actor has a counted reference, and the user has a counted reference + // * for either kind, each instance of the shape in a scene (i.e. each shapeSim) causes the reference count to be incremented by 1. + // Because these semantics aren't clear to users, this reference count should not be exposed in the API + + virtual void onRefCountZero(); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + + void setFlagsInternal( PxShapeFlags inFlags ); + + PX_FORCE_INLINE PxShapeFlags getFlagsFast() const { return mShape.getFlags(); } + PX_FORCE_INLINE PxShapeFlags getFlagsUnbuffered() const { return mShape.getScShape().getFlags(); } + PX_FORCE_INLINE PxGeometryType::Enum getGeometryTypeFast() const { return mShape.getGeometryType(); } + PX_FORCE_INLINE const Gu::GeometryUnion& getGeometryFast() const { return mShape.getGeometryUnion(); } + PX_FORCE_INLINE const PxTransform& getLocalPoseFast() const { return mShape.getShape2Actor(); } + PX_FORCE_INLINE PxU32 getActorCount() const { return PxU32(mExclusiveAndActorCount & ACTOR_COUNT_MASK); } + PX_FORCE_INLINE PxI32 isExclusiveFast() const { return mExclusiveAndActorCount & EXCLUSIVE_MASK; } + + PX_FORCE_INLINE const PxFilterData& getQueryFilterDataFast() const + { + return mShape.getScShape().getQueryFilterData(); // PT: this one doesn't need double-buffering + } + + PX_FORCE_INLINE const Scb::Shape& getScbShape() const { return mShape; } + PX_FORCE_INLINE Scb::Shape& getScbShape() { return mShape; } + + PX_INLINE PxMaterial* getMaterial(PxU32 index) const { return mShape.getMaterial(index); } + static bool checkMaterialSetup(const PxGeometry& geom, const char* errorMsgPrefix, PxMaterial*const* materials, PxU16 materialCount); + + void onActorAttach(PxRigidActor& actor); + void onActorDetach(); + + // These methods are used only for sync'ing, and may only be called on exclusive shapes since only exclusive shapes have buffering + Sc::RigidCore& getScRigidObjectExclusive() const; + void releaseInternal(); + + NpScene* getOwnerScene() const; // same distinctions as for NpActor +private: + NpScene* getAPIScene() const; + + void incMeshRefCount(); + void decMeshRefCount(); + Cm::RefCountable* getMeshRefCountable(); + bool isWritable(); + void updateSQ(const char* errorMessage); + + PxRigidActor* mActor; // Auto-resolving refs breaks DLL loading for some reason + Scb::Shape mShape; + const char* mName; + + static const PxI32 EXCLUSIVE_MASK = 0x80000000; + static const PxI32 ACTOR_COUNT_MASK = 0x7fffffff; + + volatile PxI32 mExclusiveAndActorCount; +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/NpShapeManager.cpp b/src/PhysX/physx/source/physx/src/NpShapeManager.cpp new file mode 100644 index 000000000..0a37f728e --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpShapeManager.cpp @@ -0,0 +1,799 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpShapeManager.h" +#include "NpFactory.h" +#include "ScbRigidObject.h" +#include "NpActor.h" +#include "SqPruningStructure.h" +#include "NpScene.h" +#include "NpPtrTableStorageManager.h" +#include "NpRigidDynamic.h" +#include "NpArticulationLink.h" +#include "ScBodySim.h" +#include "GuBounds.h" +#include "CmUtils.h" +#include "PsAlloca.h" + +using namespace physx; +using namespace Sq; +using namespace Gu; +using namespace Cm; + +static PX_FORCE_INLINE bool isSceneQuery(const NpShape& shape) { return shape.getFlagsFast() & PxShapeFlag::eSCENE_QUERY_SHAPE; } + +NpShapeManager::NpShapeManager() + : mSqCompoundId(INVALID_PRUNERHANDLE), mPruningStructure(NULL) +{ +} + +// PX_SERIALIZATION +NpShapeManager::NpShapeManager(const PxEMPTY) : + mShapes (PxEmpty), + mSceneQueryData (PxEmpty) +{ +} + +NpShapeManager::~NpShapeManager() +{ + PX_ASSERT(!mPruningStructure); + PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); + mShapes.clear(sm); + mSceneQueryData.clear(sm); +} + +void NpShapeManager::exportExtraData(PxSerializationContext& stream) +{ + mShapes.exportExtraData(stream); + mSceneQueryData.exportExtraData(stream); +} + +void NpShapeManager::importExtraData(PxDeserializationContext& context) +{ + mShapes.importExtraData(context); + mSceneQueryData.importExtraData(context); +} +//~PX_SERIALIZATION + +void NpShapeManager::attachShape(NpShape& shape, PxRigidActor& actor) +{ + PX_ASSERT(!mPruningStructure); + + PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); + + const PxU32 index = getNbShapes(); + mShapes.add(&shape, sm); + mSceneQueryData.add(reinterpret_cast(size_t(SQ_INVALID_PRUNER_DATA)), sm); + + NpScene* scene = NpActor::getAPIScene(actor); + if(scene && isSceneQuery(shape)) + setupSceneQuery(scene->getSceneQueryManagerFast(), actor, index); + + Scb::RigidObject& ro = static_cast(NpActor::getScbFromPxActor(actor)); + ro.onShapeAttach(shape.getScbShape()); + + PX_ASSERT(!shape.isExclusive() || shape.getActor()==NULL); + shape.onActorAttach(actor); +} + +bool NpShapeManager::detachShape(NpShape& s, PxRigidActor& actor, bool wakeOnLostTouch) +{ + PX_ASSERT(!mPruningStructure); + + const PxU32 index = mShapes.find(&s); + if(index==0xffffffff) + return false; + + NpScene* scene = NpActor::getAPIScene(actor); + if(scene && isSceneQuery(s)) + { + scene->getSceneQueryManagerFast().removePrunerShape(mSqCompoundId, getPrunerData(index)); + // if this is the last shape of a compound shape, we have to remove the compound id + // and in case of a dynamic actor, remove it from the active list + if(isSqCompound() && (mShapes.getCount() == 1)) + { + mSqCompoundId = INVALID_PRUNERHANDLE; + const PxType actorType = actor.getConcreteType(); + const bool isDynamic = actorType == PxConcreteType::eRIGID_DYNAMIC || actorType == PxConcreteType::eARTICULATION_LINK; + if(isDynamic) + { + // for PxRigidDynamic and PxArticulationLink we need to remove the compound rigid flag and remove them from active list + if(actor.is()) + const_cast(static_cast(actor)).getScbBodyFast().getScBody().getSim()->disableCompound(); + else + { + if(actor.is()) + const_cast(static_cast(actor)).getScbBodyFast().getScBody().getSim()->disableCompound(); + } + } + } + } + + Scb::RigidObject& ro = static_cast(NpActor::getScbFromPxActor(actor)); + ro.onShapeDetach(s.getScbShape(), wakeOnLostTouch, (s.getRefCount() == 1)); + PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); + mShapes.replaceWithLast(index, sm); + mSceneQueryData.replaceWithLast(index, sm); + + s.onActorDetach(); + return true; +} + +void NpShapeManager::detachAll(NpScene* scene, const PxRigidActor& actor) +{ + // assumes all SQ data has been released, which is currently the responsbility of the owning actor + const PxU32 nbShapes = getNbShapes(); + NpShape*const *shapes = getShapes(); + + if(scene) + teardownAllSceneQuery(scene->getSceneQueryManagerFast(), actor); + + // actor cleanup in Scb/Sc will remove any outstanding references corresponding to sim objects, so we don't need to do that here. + for(PxU32 i=0;ionActorDetach(); + + PtrTableStorageManager& sm = NpFactory::getInstance().getPtrTableStorageManager(); + + mShapes.clear(sm); + mSceneQueryData.clear(sm); +} + +PxU32 NpShapeManager::getShapes(PxShape** buffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return getArrayOfPointers(buffer, bufferSize, startIndex, getShapes(), getNbShapes()); +} + +PxBounds3 NpShapeManager::getWorldBounds(const PxRigidActor& actor) const +{ + PxBounds3 bounds(PxBounds3::empty()); + + const PxU32 nbShapes = getNbShapes(); + const PxTransform actorPose = actor.getGlobalPose(); + NpShape*const* PX_RESTRICT shapes = getShapes(); + + for(PxU32 i=0;igetScbShape().getGeometry(), actorPose * shapes[i]->getLocalPoseFast())); + + return bounds; +} + +void NpShapeManager::clearShapesOnRelease(Scb::Scene& s, PxRigidActor& r) +{ + PX_ASSERT(static_cast(NpActor::getScbFromPxActor(r)).isSimDisabledInternally()); + + const PxU32 nbShapes = getNbShapes(); + NpShape*const* PX_RESTRICT shapes = getShapes(); + + for(PxU32 i=0;igetScbShape(); + scbShape.checkUpdateOnRemove(&s); +#if PX_SUPPORT_PVD + s.getScenePvdClient().releasePvdInstance(&scbShape, r); +#else + PX_UNUSED(r); +#endif + } +} + +void NpShapeManager::releaseExclusiveUserReferences() +{ + // when the factory is torn down, release any shape owner refs that are still outstanding + const PxU32 nbShapes = getNbShapes(); + NpShape*const* PX_RESTRICT shapes = getShapes(); + for(PxU32 i=0;iisExclusiveFast() && shapes[i]->getRefCount()>1) + shapes[i]->release(); + } +} + +void NpShapeManager::setupSceneQuery(SceneQueryManager& sqManager, const PxRigidActor& actor, const NpShape& shape) +{ + PX_ASSERT(shape.getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE); + const PxU32 index = mShapes.find(&shape); + PX_ASSERT(index!=0xffffffff); + setupSceneQuery(sqManager, actor, index); +} + +void NpShapeManager::teardownSceneQuery(SceneQueryManager& sqManager, const NpShape& shape) +{ + const PxU32 index = mShapes.find(&shape); + PX_ASSERT(index!=0xffffffff); + teardownSceneQuery(sqManager, index); +} + +void NpShapeManager::setupAllSceneQuery(NpScene* scene, const PxRigidActor& actor, bool hasPrunerStructure, const PxBounds3* bounds, const Gu::BVHStructure* bvhStructure) +{ + PX_ASSERT(scene); // shouldn't get here unless we're in a scene + SceneQueryManager& sqManager = scene->getSceneQueryManagerFast(); + + const PxU32 nbShapes = getNbShapes(); + NpShape*const *shapes = getShapes(); + + // if BVH structure was provided, we add shapes into compound pruner + if(bvhStructure) + { + addBVHStructureShapes(sqManager, actor, bvhStructure); + } + else + { + const PxType actorType = actor.getConcreteType(); + const bool isDynamic = actorType == PxConcreteType::eRIGID_DYNAMIC || actorType == PxConcreteType::eARTICULATION_LINK; + + for(PxU32 i=0;igetNbBounds()); + + mSqCompoundId = static_cast(NpActor::getScbFromPxActor(actor).getActorCore()).getRigidID(); + sqManager.addCompoundShape(*bvhStructure, mSqCompoundId, actor.getGlobalPose(), prunerData, scbShapes, scbActor); + + numSqShapes = 0; + for(PxU32 i = 0; i < nbShapes; i++) + { + const NpShape& shape = *getShapes()[i]; + if(isSceneQuery(shape)) + setPrunerData(i, prunerData[numSqShapes++]); + } +} + +void NpShapeManager::addPrunerShape(SceneQueryManager& sqManager, PxU32 index, const NpShape& shape, const PxRigidActor& actor, bool dynamic, const PxBounds3* bound, bool hasPrunerStructure) +{ + const Scb::Shape& scbShape = shape.getScbShape(); + const Scb::Actor& scbActor = NpActor::getScbFromPxActor(actor); + setPrunerData(index, sqManager.addPrunerShape(scbShape, scbActor, dynamic, mSqCompoundId, bound, hasPrunerStructure)); +} + +void NpShapeManager::setupSceneQuery(SceneQueryManager& sqManager, const PxRigidActor& actor, PxU32 index) +{ + const PxType actorType = actor.getConcreteType(); + const bool isDynamic = actorType == PxConcreteType::eRIGID_DYNAMIC || actorType == PxConcreteType::eARTICULATION_LINK; + addPrunerShape(sqManager, index, *(getShapes()[index]), actor, isDynamic, NULL, false); +} + +void NpShapeManager::teardownSceneQuery(SceneQueryManager& sqManager, PxU32 index) +{ + sqManager.removePrunerShape(mSqCompoundId, getPrunerData(index)); + setPrunerData(index, SQ_INVALID_PRUNER_DATA); +} + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "GuHeightFieldUtil.h" +#include "PxGeometryQuery.h" +#include "PxMeshQuery.h" +#include "GuConvexEdgeFlags.h" +#include "GuMidphaseInterface.h" + +static const PxU32 gCollisionShapeColor = PxU32(PxDebugColor::eARGB_MAGENTA); + +static void visualizeSphere(const PxSphereGeometry& geometry, RenderOutput& out, const PxTransform& absPose) +{ + out << gCollisionShapeColor; // PT: no need to output this for each segment! + + out << absPose << DebugCircle(100, geometry.radius); + + PxMat44 rotPose(absPose); + Ps::swap(rotPose.column1, rotPose.column2); + rotPose.column1 = -rotPose.column1; + out << rotPose << DebugCircle(100, geometry.radius); + + Ps::swap(rotPose.column0, rotPose.column2); + rotPose.column0 = -rotPose.column0; + out << rotPose << DebugCircle(100, geometry.radius); +} + +static void visualizePlane(const PxPlaneGeometry& /*geometry*/, RenderOutput& out, const PxTransform& absPose) +{ + PxMat44 rotPose(absPose); + Ps::swap(rotPose.column1, rotPose.column2); + rotPose.column1 = -rotPose.column1; + + Ps::swap(rotPose.column0, rotPose.column2); + rotPose.column0 = -rotPose.column0; + + out << rotPose << gCollisionShapeColor; // PT: no need to output this for each segment! + for(PxReal radius = 2.0f; radius < 20.0f ; radius += 2.0f) + out << DebugCircle(100, radius*radius); +} + +static void visualizeCapsule(const PxCapsuleGeometry& geometry, RenderOutput& out, const PxTransform& absPose) +{ + out << gCollisionShapeColor; + out.outputCapsule(geometry.radius, geometry.halfHeight, absPose); +} + +static void visualizeBox(const PxBoxGeometry& geometry, RenderOutput& out, const PxTransform& absPose) +{ + out << gCollisionShapeColor; + out << absPose << DebugBox(geometry.halfExtents); +} + +static void visualizeConvexMesh(const PxConvexMeshGeometry& geometry, RenderOutput& out, const PxTransform& absPose) +{ + const ConvexMesh* convexMesh = static_cast(geometry.convexMesh); + const ConvexHullData& hullData = convexMesh->getHull(); + + const PxVec3* vertices = hullData.getHullVertices(); + const PxU8* indexBuffer = hullData.getVertexData8(); + const PxU32 nbPolygons = convexMesh->getNbPolygonsFast(); + + const PxMat44 m44(PxMat33(absPose.q) * geometry.scale.toMat33(), absPose.p); + + out << m44 << gCollisionShapeColor; // PT: no need to output this for each segment! + + for(PxU32 i=0; i(indices); + ref0 = dtriangles[i*3+0]; + ref1 = dtriangles[i*3+1]; + ref2 = dtriangles[i*3+2]; + } + else + { + const PxU16* wtriangles = reinterpret_cast(indices); + ref0 = wtriangles[i*3+0]; + ref1 = wtriangles[i*3+1]; + ref2 = wtriangles[i*3+2]; + } + + wp[0] = vertices[ref0]; + wp[1] = vertices[ref1]; + wp[2] = vertices[ref2]; +} + +static void getTriangle(const Gu::TriangleMesh& mesh, PxU32 i, PxVec3* wp, const PxVec3* vertices, const void* indices, const Matrix34& absPose, bool has16BitIndices) +{ + PxVec3 localVerts[3]; + getTriangle(mesh, i, localVerts, vertices, indices, has16BitIndices); + + wp[0] = absPose.transform(localVerts[0]); + wp[1] = absPose.transform(localVerts[1]); + wp[2] = absPose.transform(localVerts[2]); +} + +static void visualizeActiveEdges(RenderOutput& out, const Gu::TriangleMesh& mesh, PxU32 nbTriangles, const PxU32* results, const Matrix34& absPose) +{ + const PxU8* extraTrigData = mesh.getExtraTrigData(); + PX_ASSERT(extraTrigData); + + const PxVec3* vertices = mesh.getVerticesFast(); + const void* indices = mesh.getTrianglesFast(); + + out << PxU32(PxDebugColor::eARGB_YELLOW); // PT: no need to output this for each segment! + + const bool has16Bit = mesh.has16BitIndices(); + for(PxU32 i=0; i(geometry.triangleMesh); + + const PxMat44 midt(PxIdentity); + const Matrix34 absPose(PxMat33(pose.q) * geometry.scale.toMat33(), pose.p); + + PxU32 nbTriangles = triangleMesh->getNbTrianglesFast(); + const PxU32 nbVertices = triangleMesh->getNbVerticesFast(); + const PxVec3* vertices = triangleMesh->getVerticesFast(); + const void* indices = triangleMesh->getTrianglesFast(); + const bool has16Bit = triangleMesh->has16BitIndices(); + + // PT: TODO: don't render the same edge multiple times + + PxU32* results = NULL; + if(useCullBox) + { + const Gu::Box worldBox( + (cullbox.maximum + cullbox.minimum)*0.5f, + (cullbox.maximum - cullbox.minimum)*0.5f, + PxMat33(PxIdentity)); + + // PT: TODO: use the callback version here to avoid allocating this huge array + results = reinterpret_cast(PX_ALLOC_TEMP(sizeof(PxU32)*nbTriangles, "tmp triangle indices")); + LimitedResults limitedResults(results, nbTriangles, 0); + Midphase::intersectBoxVsMesh(worldBox, *triangleMesh, pose, geometry.scale, &limitedResults); + nbTriangles = limitedResults.mNbResults; + + if(visualizeShapes) + { + const PxU32 scolor = gCollisionShapeColor; + + out << midt << scolor; // PT: no need to output this for each segment! + + PxDebugLine* segments = out.reserveSegments(nbTriangles*3); + for(PxU32 i=0; i(PX_ALLOC(sizeof(PxVec3)*nbVertices, "PxVec3")); + for(PxU32 i=0;igetExtraTrigData()) + visualizeActiveEdges(out, *triangleMesh, nbTriangles, results, absPose); + + if(results) + PX_FREE(results); +} + +static void visualizeHeightField(const PxHeightFieldGeometry& hfGeometry, RenderOutput& out, const PxTransform& absPose, const PxBounds3& cullbox, bool useCullBox) +{ + const HeightField* heightfield = static_cast(hfGeometry.heightField); + + // PT: TODO: the debug viz for HFs is minimal at the moment... + const PxU32 scolor = gCollisionShapeColor; + const PxMat44 midt = PxMat44(PxIdentity); + + HeightFieldUtil hfUtil(hfGeometry); + + const PxU32 nbRows = heightfield->getNbRowsFast(); + const PxU32 nbColumns = heightfield->getNbColumnsFast(); + const PxU32 nbVerts = nbRows * nbColumns; + const PxU32 nbTriangles = 2 * nbVerts; + + out << midt << scolor; // PT: no need to output the same matrix/color for each triangle + + if(useCullBox) + { + const PxTransform pose0((cullbox.maximum + cullbox.minimum)*0.5f); + const PxBoxGeometry boxGeometry((cullbox.maximum - cullbox.minimum)*0.5f); + + PxU32* results = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTriangles, "tmp triangle indices")); + + bool overflow = false; + PxU32 nbTouchedTris = PxMeshQuery::findOverlapHeightField(boxGeometry, pose0, hfGeometry, absPose, results, nbTriangles, 0, overflow); + + PxDebugLine* segments = out.reserveSegments(nbTouchedTris*3); + + for(PxU32 i=0; iisValidTriangle(index) && heightfield->getTriangleMaterial(index) != PxHeightFieldMaterial::eHOLE) + { + outputTriangle(segments, currentTriangle.verts[0], currentTriangle.verts[1], currentTriangle.verts[2], scolor); + segments+=3; + } + } + PX_FREE(results); + } + else + { + // PT: transform vertices only once + PxVec3* tmpVerts = reinterpret_cast(PX_ALLOC(sizeof(PxVec3)*nbVerts, "PxVec3")); + // PT: TODO: optimize the following line + for(PxU32 i=0;igetVertex(i))); + + for(PxU32 i=0; iisValidTriangle(i) && heightfield->getTriangleMaterial(i) != PxHeightFieldMaterial::eHOLE) + { + PxU32 vi0, vi1, vi2; + heightfield->getTriangleVertexIndices(i, vi0, vi1, vi2); + + PxDebugLine* segments = out.reserveSegments(3); + outputTriangle(segments, tmpVerts[vi0], tmpVerts[vi1], tmpVerts[vi2], scolor); + } + } + PX_FREE(tmpVerts); + } +} + +static void visualize(const PxGeometry& geometry, RenderOutput& out, const PxTransform& absPose, const PxBounds3& cullbox, const PxReal fscale, bool visualizeShapes, bool visualizeEdges, bool useCullBox) +{ + // triangle meshes can render active edges or face normals, but for other types we can just early out if there are no collision shapes + if(!visualizeShapes && geometry.getType() != PxGeometryType::eTRIANGLEMESH) + return; + + switch(geometry.getType()) + { + case PxGeometryType::eSPHERE: + visualizeSphere(static_cast(geometry), out, absPose); + break; + case PxGeometryType::eBOX: + visualizeBox(static_cast(geometry), out, absPose); + break; + case PxGeometryType::ePLANE: + visualizePlane(static_cast(geometry), out, absPose); + break; + case PxGeometryType::eCAPSULE: + visualizeCapsule(static_cast(geometry), out, absPose); + break; + case PxGeometryType::eCONVEXMESH: + visualizeConvexMesh(static_cast(geometry), out, absPose); + break; + case PxGeometryType::eTRIANGLEMESH: + visualizeTriangleMesh(static_cast(geometry), out, absPose, cullbox, fscale, visualizeShapes, visualizeEdges, useCullBox); + break; + case PxGeometryType::eHEIGHTFIELD: + visualizeHeightField(static_cast(geometry), out, absPose, cullbox, useCullBox); + break; + case PxGeometryType::eINVALID: + break; + case PxGeometryType::eGEOMETRY_COUNT: + break; + } +} + +void NpShapeManager::visualize(RenderOutput& out, NpScene* scene, const PxRigidActor& actor) +{ + const PxReal scale = scene->getVisualizationParameter(PxVisualizationParameter::eSCALE); + if(!scale) + return; + + const PxU32 nbShapes = getNbShapes(); + NpShape*const* PX_RESTRICT shapes = getShapes(); + + const bool visualizeCompounds = (nbShapes>1) && scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_COMPOUNDS)!=0.0f; + + // PT: moved all these out of the loop, no need to grab them once per shape + const PxBounds3& cullbox = scene->getScene().getVisualizationCullingBox(); + const bool visualizeAABBs = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_AABBS)!=0.0f; + const bool visualizeShapes = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_SHAPES)!=0.0f; + const bool visualizeEdges = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_EDGES)!=0.0f; + const float fNormals = scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_FNORMALS); + const bool visualizeFNormals = fNormals!=0.0f; + const bool visualizeCollision = visualizeShapes || visualizeFNormals || visualizeEdges; + const bool useCullBox = !cullbox.isEmpty(); + const bool needsShapeBounds0 = visualizeCompounds || (visualizeCollision && useCullBox); + const PxReal collisionAxes = scale * scene->getVisualizationParameter(PxVisualizationParameter::eCOLLISION_AXES); + const PxReal fscale = scale * fNormals; + + const PxTransform actorPose = actor.getGlobalPose(); + + PxBounds3 compoundBounds(PxBounds3::empty()); + for(PxU32 i=0;igetScbShape(); + + const PxTransform absPose = actorPose * scbShape.getShape2Actor(); + const PxGeometry& geom = scbShape.getGeometry(); + + const bool shapeDebugVizEnabled = scbShape.getFlags() & PxShapeFlag::eVISUALIZATION; + + const bool needsShapeBounds = needsShapeBounds0 || (visualizeAABBs && shapeDebugVizEnabled); + const PxBounds3 currentShapeBounds = needsShapeBounds ? Gu::computeBounds(geom, absPose) : PxBounds3::empty(); + + if(shapeDebugVizEnabled) + { + if(visualizeAABBs) + out << PxU32(PxDebugColor::eARGB_YELLOW) << PxMat44(PxIdentity) << DebugBox(currentShapeBounds); + + if(collisionAxes != 0.0f) + out << PxMat44(absPose) << DebugBasis(PxVec3(collisionAxes), 0xcf0000, 0x00cf00, 0x0000cf); + + if(visualizeCollision) + { + if(!useCullBox || cullbox.intersects(currentShapeBounds)) + ::visualize(geom, out, absPose, cullbox, fscale, visualizeShapes, visualizeEdges, useCullBox); + } + } + + if(visualizeCompounds) + compoundBounds.include(currentShapeBounds); + } + if(visualizeCompounds && !compoundBounds.isEmpty()) + out << gCollisionShapeColor << PxMat44(PxIdentity) << DebugBox(compoundBounds); +} +#endif // PX_ENABLE_DEBUG_VISUALIZATION diff --git a/src/PhysX/physx/source/physx/src/NpShapeManager.h b/src/PhysX/physx/source/physx/src/NpShapeManager.h new file mode 100644 index 000000000..52a840369 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpShapeManager.h @@ -0,0 +1,134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_NP_SHAPE_MANAGER +#define PX_PHYSICS_NP_SHAPE_MANAGER + +#include "NpShape.h" +#include "CmPtrTable.h" +#include "SqSceneQueryManager.h" +#include "GuBVHStructure.h" + +#if PX_ENABLE_DEBUG_VISUALIZATION +#include "CmRenderOutput.h" +#endif + +namespace physx +{ + +namespace Sq +{ + class SceneQueryManager; + class PruningStructure; +} + +class NpScene; + +class NpShapeManager : public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: +// PX_SERIALIZATION + static void getBinaryMetaData(PxOutputStream& stream); + NpShapeManager(const PxEMPTY); + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); +//~PX_SERIALIZATION + NpShapeManager(); + ~NpShapeManager(); + + PX_FORCE_INLINE PxU32 getNbShapes() const { return mShapes.getCount(); } + PX_FORCE_INLINE NpShape* const* getShapes() const { return reinterpret_cast(mShapes.getPtrs()); } + PxU32 getShapes(PxShape** buffer, PxU32 bufferSize, PxU32 startIndex=0) const; + + void attachShape(NpShape& shape, PxRigidActor& actor); + bool detachShape(NpShape& s, PxRigidActor &actor, bool wakeOnLostTouch); + void detachAll(NpScene *scene, const PxRigidActor& actor); + + void teardownSceneQuery(Sq::SceneQueryManager& sqManager, const NpShape& shape); + void setupSceneQuery(Sq::SceneQueryManager& sqManager, const PxRigidActor& actor, const NpShape& shape); + + void addPrunerShape(Sq::SceneQueryManager& sqManager, PxU32 index, const NpShape& shape, const PxRigidActor& actor, bool dynamic, const PxBounds3* bound, bool hasPrunerStructure); + + PX_FORCE_INLINE void setPrunerData(PxU32 index, Sq::PrunerData data) + { + PX_ASSERT(index(data); + } + + PX_FORCE_INLINE Sq::PrunerData getPrunerData(PxU32 index) const + { + PX_ASSERT(indexstartWrite(mAllowReentry)) + { + case NpScene::StartWriteResult::eOK: + break; + case NpScene::StartWriteResult::eNO_LOCK: + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "An API write call (%s) was made from thread %d but PxScene::lockWrite() was not called first, note that " + "when PxSceneFlag::eREQUIRE_RW_LOCK is enabled all API reads and writes must be " + "wrapped in the appropriate locks.", mName, PxU32(Ps::Thread::getId())); + break; + case NpScene::StartWriteResult::eRACE_DETECTED: + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Concurrent API write call or overlapping API read and write call detected during %s from thread %d! " + "Note that write operations to the SDK must be sequential, i.e., no overlap with " + "other write or read calls, else the resulting behavior is undefined. " + "Also note that API writes during a callback function are not permitted.", mName, PxU32(Ps::Thread::getId())); + break; + + case NpScene::StartWriteResult::eIN_FETCHRESULTS: + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Illegal write call detected in %s from thread %d during split fetchResults! " + "Note that write operations to the SDK are not permitted between the start of fetchResultsStart() and end of fetchResultsFinish(). " + "Behavior will be undefined. ", mName, PxU32(Ps::Thread::getId())); + break; + } + + // Record the NpScene read/write error counter which is + // incremented any time a NpScene::startWrite/startRead fails + // (see destructor for additional error checking based on this count) + mErrorCount = mScene->getReadWriteErrorCount(); + } +} + + +NpWriteCheck::~NpWriteCheck() +{ + if (mScene) + { + // By checking if the NpScene::mConcurrentErrorCount has been incremented + // we can detect if an erroneous read/write was performed during + // this objects lifetime. In this case we also print this function's + // details so that the user can see which two API calls overlapped + if (mScene->getReadWriteErrorCount() != mErrorCount && !(mScene->getScene().getFlags() & PxSceneFlag::eREQUIRE_RW_LOCK)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Leaving %s on thread %d, an overlapping API read or write by another thread was detected.", mName, PxU32(Ps::Thread::getId())); + } + + mScene->stopWrite(mAllowReentry); + } +} diff --git a/src/PhysX/physx/source/physx/src/NpWriteCheck.h b/src/PhysX/physx/source/physx/src/NpWriteCheck.h new file mode 100644 index 000000000..5095a4c49 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/NpWriteCheck.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef NP_WRITE_CHECK_H +#define NP_WRITE_CHECK_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx +{ + +class NpScene; + +// RAII wrapper around the PxScene::startWrite() method, note that this +// object does not acquire any scene locks, it is an error checking only mechanism +class NpWriteCheck +{ +public: + NpWriteCheck(NpScene* scene, const char* functionName, bool allowReentry=true); + ~NpWriteCheck(); + +private: + + NpScene* mScene; + const char* mName; + bool mAllowReentry; + PxU32 mErrorCount; +}; + +#if PX_DEBUG || PX_CHECKED + // Creates a scoped write check object that detects whether appropriate scene locks + // have been acquired and checks if reads/writes overlap, this macro should typically + // be placed at the beginning of any non-const API methods that are not multi-thread safe. + // By default re-entrant write calls by the same thread are allowed, the error conditions + // checked can be summarized as: + + // 1. PxSceneFlag::eREQUIRE_RW_LOCK was specified but PxScene::lockWrite() was not yet called + // 2. Other threads were already reading, or began reading during the object lifetime + // 3. Other threads were already writing, or began writing during the object lifetime + #define NP_WRITE_CHECK(npScenePtr) NpWriteCheck npWriteCheck(npScenePtr, __FUNCTION__); + + // Creates a scoped write check object that disallows re-entrant writes, this is used by + // the NpScene::simulate method to detect when callbacks make write calls to the API + #define NP_WRITE_CHECK_NOREENTRY(npScenePtr) NpWriteCheck npWriteCheck(npScenePtr, __FUNCTION__, false); +#else + #define NP_WRITE_CHECK(npScenePtr) + #define NP_WRITE_CHECK_NOREENTRY(npScenePtr) +#endif + +} + +#endif // NP_WRITE_CHECK_H diff --git a/src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h b/src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h new file mode 100644 index 000000000..552b95a0b --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h @@ -0,0 +1,82 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_META_DATA_PVD_BINDING_DATA_H +#define PX_META_DATA_PVD_BINDING_DATA_H +#if PX_SUPPORT_PVD +#include "foundation/PxSimpleTypes.h" +#include "PsArray.h" +#include "PsHashSet.h" +#include "PsHashMap.h" + +namespace physx +{ +namespace Vd +{ +using namespace physx::shdfnd; + +typedef HashSet OwnerActorsValueType; +typedef HashMap OwnerActorsMap; + +struct PvdMetaDataBindingData : public UserAllocated +{ + Array mTempU8Array; + Array mActors; + Array mArticulations; + Array mArticulationLinks; + HashSet mSleepingActors; + OwnerActorsMap mOwnerActorsMap; + + PvdMetaDataBindingData() + : mTempU8Array(PX_DEBUG_EXP("TempU8Array")) + , mActors(PX_DEBUG_EXP("PxActor")) + , mArticulations(PX_DEBUG_EXP("Articulations")) + , mArticulationLinks(PX_DEBUG_EXP("ArticulationLinks")) + , mSleepingActors(PX_DEBUG_EXP("SleepingActors")) + { + } + + template + TDataType* allocateTemp(PxU32 numItems) + { + mTempU8Array.resize(numItems * sizeof(TDataType)); + if(numItems) + return reinterpret_cast(mTempU8Array.begin()); + else + return NULL; + } + + DataRef tempToRef() + { + return DataRef(mTempU8Array.begin(), mTempU8Array.size()); + } +}; +} +} +#endif // PX_SUPPORT_PVD +#endif diff --git a/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp new file mode 100644 index 000000000..ee812cd5f --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp @@ -0,0 +1,1635 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// suppress LNK4221 +#include "foundation/PxPreprocessor.h" +PX_DUMMY_SYMBOL + +#if PX_SUPPORT_PVD + +#include "foundation/PxSimpleTypes.h" +#include "foundation/Px.h" + +#include "PxMetaDataObjects.h" +#include "PxPvdDataStream.h" +#include "PxScene.h" +#include "ScBodyCore.h" +#include "PvdMetaDataExtensions.h" +#include "PvdMetaDataPropertyVisitor.h" +#include "PvdMetaDataDefineProperties.h" +#include "PvdMetaDataBindingData.h" +#include "PxRigidDynamic.h" +#include "PxArticulation.h" +#include "PxArticulationLink.h" +#include "NpScene.h" +#include "NpPhysics.h" + +#include "PvdTypeNames.h" +#include "PvdMetaDataPvdBinding.h" + +using namespace physx; +using namespace Sc; +using namespace Vd; +using namespace Sq; + +namespace physx +{ +namespace Vd +{ + +struct NameValuePair +{ + const char* mName; + PxU32 mValue; +}; + +static const NameValuePair g_physx_Sq_SceneQueryID__EnumConversion[] = { + { "QUERY_RAYCAST_ANY_OBJECT", PxU32(QueryID::QUERY_RAYCAST_ANY_OBJECT) }, + { "QUERY_RAYCAST_CLOSEST_OBJECT", PxU32(QueryID::QUERY_RAYCAST_CLOSEST_OBJECT) }, + { "QUERY_RAYCAST_ALL_OBJECTS", PxU32(QueryID::QUERY_RAYCAST_ALL_OBJECTS) }, + { "QUERY_OVERLAP_SPHERE_ALL_OBJECTS", PxU32(QueryID::QUERY_OVERLAP_SPHERE_ALL_OBJECTS) }, + { "QUERY_OVERLAP_AABB_ALL_OBJECTS", PxU32(QueryID::QUERY_OVERLAP_AABB_ALL_OBJECTS) }, + { "QUERY_OVERLAP_OBB_ALL_OBJECTS", PxU32(QueryID::QUERY_OVERLAP_OBB_ALL_OBJECTS) }, + { "QUERY_OVERLAP_CAPSULE_ALL_OBJECTS", PxU32(QueryID::QUERY_OVERLAP_CAPSULE_ALL_OBJECTS) }, + { "QUERY_OVERLAP_CONVEX_ALL_OBJECTS", PxU32(QueryID::QUERY_OVERLAP_CONVEX_ALL_OBJECTS) }, + { "QUERY_LINEAR_OBB_SWEEP_CLOSEST_OBJECT", PxU32(QueryID::QUERY_LINEAR_OBB_SWEEP_CLOSEST_OBJECT) }, + { "QUERY_LINEAR_CAPSULE_SWEEP_CLOSEST_OBJECT", PxU32(QueryID::QUERY_LINEAR_CAPSULE_SWEEP_CLOSEST_OBJECT) }, + { "QUERY_LINEAR_CONVEX_SWEEP_CLOSEST_OBJECT", PxU32(QueryID::QUERY_LINEAR_CONVEX_SWEEP_CLOSEST_OBJECT) }, + { NULL, 0 } +}; + +struct SceneQueryIDConvertor +{ + const NameValuePair* NameConversion; + SceneQueryIDConvertor() : NameConversion(g_physx_Sq_SceneQueryID__EnumConversion) + { + } +}; + +PvdMetaDataBinding::PvdMetaDataBinding() : mBindingData(PX_NEW(PvdMetaDataBindingData)()) +{ +} + +PvdMetaDataBinding::~PvdMetaDataBinding() +{ + for(OwnerActorsMap::Iterator iter = mBindingData->mOwnerActorsMap.getIterator(); !iter.done(); iter++) + { + iter->second->~OwnerActorsValueType(); + PX_FREE(iter->second); + } + + PX_DELETE(mBindingData); + mBindingData = NULL; +} + +template +static inline void definePropertyStruct(PvdDataStream& inStream, const char* pushName = NULL) +{ + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoValueStructDefine definitionObj(helper); + bool doPush = pushName && *pushName; + if(doPush) + definitionObj.pushName(pushName); + visitAllPvdProperties(definitionObj); + if(doPush) + definitionObj.popName(); + helper.addPropertyMessage(getPvdNamespacedNameForType(), getPvdNamespacedNameForType(), sizeof(TValueType)); +} + +template +static inline void createClassAndDefineProperties(PvdDataStream& inStream) +{ + inStream.createClass(); + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoDefine definitionObj(helper, getPvdNamespacedNameForType()); + visitAllPvdProperties(definitionObj); +} + +template +static inline void createClassDeriveAndDefineProperties(PvdDataStream& inStream) +{ + inStream.createClass(); + inStream.deriveClass(); + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoDefine definitionObj(helper, getPvdNamespacedNameForType()); + visitInstancePvdProperties(definitionObj); +} + +template +static inline void defineProperty(PvdDataStream& inStream, const char* inPropertyName, const char* semantic) +{ + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + // PxEnumTraits< TValueType > filterFlagsEnum; + TConvertSrc filterFlagsEnum; + const TConvertData* convertor = filterFlagsEnum.NameConversion; + + for(; convertor->mName != NULL; ++convertor) + helper.addNamedValue(convertor->mName, convertor->mValue); + + inStream.createProperty(inPropertyName, semantic, PropertyType::Scalar, helper.getNamedValues()); + helper.clearNamedValues(); +} + +template +static inline void definePropertyFlags(PvdDataStream& inStream, const char* inPropertyName) +{ + defineProperty(inStream, inPropertyName, "Bitflag"); +} +template +static inline void definePropertyEnums(PvdDataStream& inStream, const char* inPropertyName) +{ + defineProperty(inStream, inPropertyName, "Enumeration Value"); +} + +static PX_FORCE_INLINE void registerPvdRaycast(PvdDataStream& inStream) +{ + inStream.createClass(); + definePropertyEnums(inStream, "type"); + inStream.createProperty("filterData"); + definePropertyFlags, PxU32ToName>(inStream, "filterFlags"); + inStream.createProperty("origin"); + inStream.createProperty("unitDir"); + inStream.createProperty("distance"); + inStream.createProperty("hits_arrayName"); + inStream.createProperty("hits_baseIndex"); + inStream.createProperty("hits_count"); +} + +static PX_FORCE_INLINE void registerPvdSweep(PvdDataStream& inStream) +{ + inStream.createClass(); + definePropertyEnums(inStream, "type"); + definePropertyFlags, PxU32ToName>(inStream, "filterFlags"); + inStream.createProperty("unitDir"); + inStream.createProperty("distance"); + inStream.createProperty("geom_arrayName"); + inStream.createProperty("geom_baseIndex"); + inStream.createProperty("geom_count"); + inStream.createProperty("pose_arrayName"); + inStream.createProperty("pose_baseIndex"); + inStream.createProperty("pose_count"); + inStream.createProperty("filterData_arrayName"); + inStream.createProperty("filterData_baseIndex"); + inStream.createProperty("filterData_count"); + inStream.createProperty("hits_arrayName"); + inStream.createProperty("hits_baseIndex"); + inStream.createProperty("hits_count"); +} + +static PX_FORCE_INLINE void registerPvdOverlap(PvdDataStream& inStream) +{ + inStream.createClass(); + definePropertyEnums(inStream, "type"); + inStream.createProperty("filterData"); + definePropertyFlags, PxU32ToName>(inStream, "filterFlags"); + inStream.createProperty("pose"); + inStream.createProperty("geom_arrayName"); + inStream.createProperty("geom_baseIndex"); + inStream.createProperty("geom_count"); + inStream.createProperty("hits_arrayName"); + inStream.createProperty("hits_baseIndex"); + inStream.createProperty("hits_count"); +} + +static PX_FORCE_INLINE void registerPvdSqHit(PvdDataStream& inStream) +{ + inStream.createClass(); + inStream.createProperty("Shape"); + inStream.createProperty("Actor"); + inStream.createProperty("FaceIndex"); + definePropertyFlags, PxU32ToName>(inStream, "Flags"); + inStream.createProperty("Impact"); + inStream.createProperty("Normal"); + inStream.createProperty("Distance"); + inStream.createProperty("U"); + inStream.createProperty("V"); +} + +void PvdMetaDataBinding::registerSDKProperties(PvdDataStream& inStream) +{ + if (inStream.isClassExist()) + return; + // PxPhysics + { + inStream.createClass(); + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoDefine definitionObj(helper, getPvdNamespacedNameForType()); + helper.pushName("TolerancesScale"); + visitAllPvdProperties(definitionObj); + helper.popName(); + inStream.createProperty("Scenes", "children", PropertyType::Array); + inStream.createProperty("SharedShapes", "children", PropertyType::Array); + inStream.createProperty("Materials", "children", PropertyType::Array); + inStream.createProperty("HeightFields", "children", PropertyType::Array); + inStream.createProperty("ConvexMeshes", "children", PropertyType::Array); + inStream.createProperty("TriangleMeshes", "children", PropertyType::Array); + inStream.createProperty("Version.Major"); + inStream.createProperty("Version.Minor"); + inStream.createProperty("Version.Bugfix"); + inStream.createProperty("Version.Build"); + definePropertyStruct(inStream, "TolerancesScale"); + } + { // PxGeometry + inStream.createClass(); + inStream.createProperty("Shape", "parents", PropertyType::Scalar); + } + { // PxBoxGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxSphereGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxCapsuleGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxPlaneGeometry + createClassDeriveAndDefineProperties(inStream); + } + { // PxConvexMeshGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxTriangleMeshGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxHeightFieldGeometry + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + + // PxScene + { + // PT: TODO: why inline this for PvdContact but do PvdRaycast/etc in separate functions? + { // contact information + inStream.createClass(); + inStream.createProperty("Point"); + inStream.createProperty("Axis"); + inStream.createProperty("Shapes[0]"); + inStream.createProperty("Shapes[1]"); + inStream.createProperty("Separation"); + inStream.createProperty("NormalForce"); + inStream.createProperty("InternalFaceIndex[0]"); + inStream.createProperty("InternalFaceIndex[1]"); + inStream.createProperty("NormalForceValid"); + } + + registerPvdSqHit(inStream); + registerPvdRaycast(inStream); + registerPvdSweep(inStream); + registerPvdOverlap(inStream); + + inStream.createClass(); + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoDefine definitionObj(helper, getPvdNamespacedNameForType()); + visitAllPvdProperties(definitionObj); + helper.pushName("SimulationStatistics"); + visitAllPvdProperties(definitionObj); + helper.popName(); + inStream.createProperty("Physics", "parents", PropertyType::Scalar); + inStream.createProperty("Timestamp"); + inStream.createProperty("SimulateElapsedTime"); + definePropertyStruct(inStream); + definePropertyStruct(inStream, "SimulationStatistics"); + inStream.createProperty("Contacts", "", PropertyType::Array); + + inStream.createProperty("SceneQueries.Overlaps", "", PropertyType::Array); + inStream.createProperty("SceneQueries.Sweeps", "", PropertyType::Array); + inStream.createProperty("SceneQueries.Hits", "", PropertyType::Array); + inStream.createProperty("SceneQueries.Raycasts", "", PropertyType::Array); + inStream.createProperty("SceneQueries.PoseList", "", PropertyType::Array); + inStream.createProperty("SceneQueries.FilterDataList", "", PropertyType::Array); + inStream.createProperty("SceneQueries.GeometryList", "", PropertyType::Array); + + inStream.createProperty("BatchedQueries.Overlaps", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.Sweeps", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.Hits", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.Raycasts", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.PoseList", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.FilterDataList", "", PropertyType::Array); + inStream.createProperty("BatchedQueries.GeometryList", "", PropertyType::Array); + + inStream.createProperty("RigidStatics", "children", PropertyType::Array); + inStream.createProperty("RigidDynamics", "children", PropertyType::Array); + inStream.createProperty("Articulations", "children", PropertyType::Array); + inStream.createProperty("Joints", "children", PropertyType::Array); + inStream.createProperty("Aggregates", "children", PropertyType::Array); + } + // PxMaterial + { + createClassAndDefineProperties(inStream); + definePropertyStruct(inStream); + inStream.createProperty("Physics", "parents", PropertyType::Scalar); + } + // PxHeightField + { + { + inStream.createClass(); + inStream.createProperty("Height"); + inStream.createProperty("MaterialIndex[0]"); + inStream.createProperty("MaterialIndex[1]"); + } + + inStream.createClass(); + // It is important the PVD fields match the RepX fields, so this has + // to be hand coded. + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + PvdClassInfoDefine definitionObj(helper, getPvdNamespacedNameForType()); + visitAllPvdProperties(definitionObj); + inStream.createProperty("Samples", "", PropertyType::Array); + inStream.createProperty("Physics", "parents", PropertyType::Scalar); + definePropertyStruct(inStream); + } + // PxConvexMesh + { + { // hull polygon data. + inStream.createClass(); + inStream.createProperty("NumVertices"); + inStream.createProperty("IndexBase"); + } + inStream.createClass(); + inStream.createProperty("Mass"); + inStream.createProperty("LocalInertia"); + inStream.createProperty("LocalCenterOfMass"); + inStream.createProperty("Points", "", PropertyType::Array); + inStream.createProperty("HullPolygons", "", PropertyType::Array); + inStream.createProperty("PolygonIndexes", "", PropertyType::Array); + inStream.createProperty("Physics", "parents", PropertyType::Scalar); + } + // PxTriangleMesh + { + inStream.createClass(); + inStream.createProperty("Points", "", PropertyType::Array); + inStream.createProperty("NbTriangles", "", PropertyType::Scalar); + inStream.createProperty("Triangles", "", PropertyType::Array); + inStream.createProperty("MaterialIndices", "", PropertyType::Array); + inStream.createProperty("Physics", "parents", PropertyType::Scalar); + } + { // PxShape + createClassAndDefineProperties(inStream); + definePropertyStruct(inStream); + inStream.createProperty("Geometry", "children"); + inStream.createProperty("Materials", "children", PropertyType::Array); + inStream.createProperty("Actor", "parents"); + } + // PxActor + { + createClassAndDefineProperties(inStream); + inStream.createProperty("Scene", "parents"); + } + // PxRigidActor + { + createClassDeriveAndDefineProperties(inStream); + inStream.createProperty("Shapes", "children", PropertyType::Array); + inStream.createProperty("Joints", "children", PropertyType::Array); + } + // PxRigidStatic + { + createClassDeriveAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { // PxRigidBody + createClassDeriveAndDefineProperties(inStream); + } + // PxRigidDynamic + { + createClassDeriveAndDefineProperties(inStream); + // If anyone adds a 'getKinematicTarget' to PxRigidDynamic you can remove the line + // below (after the code generator has run). + inStream.createProperty("KinematicTarget"); + definePropertyStruct(inStream); + // Manually define the update struct. + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + /*struct PxRigidDynamicUpdateBlock + { + Transform GlobalPose; + Float3 LinearVelocity; + Float3 AngularVelocity; + PxU8 IsSleeping; + PxU8 padding[3]; + }; + */ + helper.pushName("GlobalPose"); + helper.addPropertyMessageArg(PX_OFFSET_OF_RT(PxRigidDynamicUpdateBlock, GlobalPose)); + helper.popName(); + helper.pushName("LinearVelocity"); + helper.addPropertyMessageArg(PX_OFFSET_OF_RT(PxRigidDynamicUpdateBlock, LinearVelocity)); + helper.popName(); + helper.pushName("AngularVelocity"); + helper.addPropertyMessageArg(PX_OFFSET_OF_RT(PxRigidDynamicUpdateBlock, AngularVelocity)); + helper.popName(); + helper.pushName("IsSleeping"); + helper.addPropertyMessageArg(PX_OFFSET_OF_RT(PxRigidDynamicUpdateBlock, IsSleeping)); + helper.popName(); + helper.addPropertyMessage(); + } + + // PxArticulationBase + { + createClassAndDefineProperties(inStream); + inStream.createProperty("Scene", "parents"); + inStream.createProperty("Links", "children", PropertyType::Array); + definePropertyStruct(inStream); + } + + //{ // PxArticulation + // createClassDeriveAndDefineProperties(inStream); + // definePropertyStruct(inStream); + //} + + { // PxArticulationLink + createClassDeriveAndDefineProperties(inStream); + inStream.createProperty("Parent", "parents"); + inStream.createProperty("Links", "children", PropertyType::Array); + inStream.createProperty("InboundJoint", "children"); + definePropertyStruct(inStream); + + PvdPropertyDefinitionHelper& helper(inStream.getPropertyDefinitionHelper()); + /*struct PxArticulationLinkUpdateBlock + { + Transform GlobalPose; + Float3 LinearVelocity; + Float3 AngularVelocity; + }; + */ + helper.pushName("GlobalPose"); + helper.addPropertyMessageArg(PX_OFFSET_OF(PxArticulationLinkUpdateBlock, GlobalPose)); + helper.popName(); + helper.pushName("LinearVelocity"); + helper.addPropertyMessageArg(PX_OFFSET_OF(PxArticulationLinkUpdateBlock, LinearVelocity)); + helper.popName(); + helper.pushName("AngularVelocity"); + helper.addPropertyMessageArg(PX_OFFSET_OF(PxArticulationLinkUpdateBlock, AngularVelocity)); + helper.popName(); + helper.addPropertyMessage(); + } + { // PxArticulationJoint + createClassAndDefineProperties(inStream); + inStream.createProperty("Link", "parents"); + definePropertyStruct(inStream); + } + { // PxConstraint + createClassAndDefineProperties(inStream); + definePropertyStruct(inStream); + } + { + // PxAggregate + createClassAndDefineProperties(inStream); + inStream.createProperty("Scene", "parents"); + definePropertyStruct(inStream); + inStream.createProperty("Actors", "children", PropertyType::Array); + inStream.createProperty("Articulations", "children", PropertyType::Array); + } +} + +template +static void doSendAllProperties(PvdDataStream& inStream, const TDataType* inDatatype, const void* instanceId) +{ + TValueType theValues(inDatatype); + inStream.setPropertyMessage(instanceId, theValues); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxPhysics& inPhysics) +{ + PxTolerancesScale theScale(inPhysics.getTolerancesScale()); + doSendAllProperties(inStream, &theScale, &inPhysics); + inStream.setPropertyValue(&inPhysics, "Version.Major", PxU32(PX_PHYSICS_VERSION_MAJOR)); + inStream.setPropertyValue(&inPhysics, "Version.Minor", PxU32(PX_PHYSICS_VERSION_MINOR)); + inStream.setPropertyValue(&inPhysics, "Version.Bugfix", PxU32(PX_PHYSICS_VERSION_BUGFIX)); + +#if PX_CHECKED +#if defined(NDEBUG) + // This is a checked build + String buildType = "Checked"; +#elif defined(_DEBUG) + // This is a debug build + String buildType = "Debug"; +#endif +#elif PX_PROFILE + String buildType = "Profile"; +#elif defined(NDEBUG) + // This is a release build + String buildType = "Release"; +#endif + inStream.setPropertyValue(&inPhysics, "Version.Build", buildType); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxScene& inScene) +{ + PxPhysics& physics(const_cast(inScene).getPhysics()); + PxTolerancesScale theScale; + PxSceneDesc theDesc(theScale); + + { + theDesc.gravity = inScene.getGravity(); + + theDesc.simulationEventCallback = inScene.getSimulationEventCallback(); + theDesc.contactModifyCallback = inScene.getContactModifyCallback(); + theDesc.ccdContactModifyCallback = inScene.getCCDContactModifyCallback(); + + theDesc.filterShaderData = inScene.getFilterShaderData(); + theDesc.filterShaderDataSize = inScene.getFilterShaderDataSize(); + theDesc.filterShader = inScene.getFilterShader(); + theDesc.filterCallback = inScene.getFilterCallback(); + + theDesc.broadPhaseType = inScene.getBroadPhaseType(); + theDesc.broadPhaseCallback = inScene.getBroadPhaseCallback(); + + theDesc.limits = inScene.getLimits(); + + theDesc.frictionType = inScene.getFrictionType(); + + theDesc.bounceThresholdVelocity = inScene.getBounceThresholdVelocity(); + + theDesc.frictionOffsetThreshold = inScene.getFrictionOffsetThreshold(); + + theDesc.flags = inScene.getFlags(); + + theDesc.cpuDispatcher = inScene.getCpuDispatcher(); + theDesc.gpuDispatcher = inScene.getGpuDispatcher(); + + theDesc.staticStructure = inScene.getStaticStructure(); + theDesc.dynamicStructure = inScene.getDynamicStructure(); + theDesc.dynamicTreeRebuildRateHint = inScene.getDynamicTreeRebuildRateHint(); + + theDesc.userData = inScene.userData; + + theDesc.solverBatchSize = inScene.getSolverBatchSize(); + + // theDesc.nbContactDataBlocks = inScene.getNbContactDataBlocksUsed(); + // theDesc.maxNbContactDataBlocks = inScene.getMaxNbContactDataBlocksUsed(); + + theDesc.contactReportStreamBufferSize = inScene.getContactReportStreamBufferSize(); + + theDesc.ccdMaxPasses = inScene.getCCDMaxPasses(); + + // theDesc.simulationOrder = inScene.getSimulationOrder(); + + theDesc.wakeCounterResetValue = inScene.getWakeCounterResetValue(); + } + + PxSceneDescGeneratedValues theValues(&theDesc); + inStream.setPropertyMessage(&inScene, theValues); + // Create parent/child relationship. + inStream.setPropertyValue(&inScene, "Physics", reinterpret_cast(&physics)); + inStream.pushBackObjectRef(&physics, "Scenes", &inScene); +} + +void PvdMetaDataBinding::sendBeginFrame(PvdDataStream& inStream, const PxScene* inScene, PxReal simulateElapsedTime) +{ + inStream.beginSection(inScene, "frame"); + inStream.setPropertyValue(inScene, "Timestamp", inScene->getTimestamp()); + inStream.setPropertyValue(inScene, "SimulateElapsedTime", simulateElapsedTime); +} + +template +struct NullConverter +{ + void operator()(TDataType& data, const TDataType& src) + { + data = src; + } +}; + +template > +class ScopedPropertyValueSender +{ + TTargetType mStack[T_NUM_ITEMS]; + TTargetType* mCur; + const TTargetType* mEnd; + PvdDataStream& mStream; + + public: + ScopedPropertyValueSender(PvdDataStream& inStream, const void* inObj, String name) + : mCur(mStack), mEnd(&mStack[T_NUM_ITEMS]), mStream(inStream) + { + mStream.beginSetPropertyValue(inObj, name, getPvdNamespacedNameForType()); + } + + ~ScopedPropertyValueSender() + { + if(mStack != mCur) + { + PxU32 size = sizeof(TTargetType) * PxU32(mCur - mStack); + mStream.appendPropertyValueData(DataRef(reinterpret_cast(mStack), size)); + } + mStream.endSetPropertyValue(); + } + + void append(const TSourceType& data) + { + Converter()(*mCur, data); + if(mCur < mEnd - 1) + ++mCur; + else + { + mStream.appendPropertyValueData(DataRef(reinterpret_cast(mStack), sizeof mStack)); + mCur = mStack; + } + } + + private: + ScopedPropertyValueSender& operator=(const ScopedPropertyValueSender&); +}; + +void PvdMetaDataBinding::sendContacts(PvdDataStream& inStream, const PxScene& inScene) +{ + inStream.setPropertyValue(&inScene, "Contacts", DataRef(), getPvdNamespacedNameForType()); +} + +void PvdMetaDataBinding::sendStats(PvdDataStream& inStream, const PxScene* inScene) +{ + PxSimulationStatistics theStats; + inScene->getSimulationStatistics(theStats); + + PxSimulationStatisticsGeneratedValues values(&theStats); + inStream.setPropertyMessage(inScene, values); +} + +struct PvdContactConverter +{ + void operator()(PvdContact& data, const Sc::Contact& src) + { + data.point = src.point; + data.axis = src.normal; + data.shape0 = src.shape0; + data.shape1 = src.shape1; + data.separation = src.separation; + data.normalForce = src.normalForce; + data.internalFaceIndex0 = src.faceIndex0; + data.internalFaceIndex1 = src.faceIndex1; + data.normalForceAvailable = src.normalForceAvailable; + } +}; + +void PvdMetaDataBinding::sendContacts(PvdDataStream& inStream, const PxScene& inScene, Array& inContacts) +{ + ScopedPropertyValueSender sender(inStream, &inScene, "Contacts"); + + for(PxU32 i = 0; i < inContacts.size(); i++) + { + sender.append(inContacts[i]); + } +} + +void PvdMetaDataBinding::sendEndFrame(PvdDataStream& inStream, const PxScene* inScene) +{ + //flush other client + inStream.endSection(inScene, "frame"); +} + +template +static void addPhysicsGroupProperty(PvdDataStream& inStream, const char* groupName, const TDataType& inData, const PxPhysics& ownerPhysics) +{ + inStream.setPropertyValue(&inData, "Physics", reinterpret_cast(&ownerPhysics)); + inStream.pushBackObjectRef(&ownerPhysics, groupName, &inData); + // Buffer type objects *have* to be flushed directly out once created else scene creation doesn't work. +} + +template +static void removePhysicsGroupProperty(PvdDataStream& inStream, const char* groupName, const TDataType& inData, const PxPhysics& ownerPhysics) +{ + inStream.removeObjectRef(&ownerPhysics, groupName, &inData); + inStream.destroyInstance(&inData); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxMaterial& inMaterial, const PxPhysics& ownerPhysics) +{ + inStream.createInstance(&inMaterial); + sendAllProperties(inStream, inMaterial); + addPhysicsGroupProperty(inStream, "Materials", inMaterial, ownerPhysics); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxMaterial& inMaterial) +{ + PxMaterialGeneratedValues values(&inMaterial); + inStream.setPropertyMessage(&inMaterial, values); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxMaterial& inMaterial, const PxPhysics& ownerPhysics) +{ + removePhysicsGroupProperty(inStream, "Materials", inMaterial, ownerPhysics); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxHeightField& inData) +{ + PxHeightFieldDesc theDesc; + // Save the height field to desc. + theDesc.nbRows = inData.getNbRows(); + theDesc.nbColumns = inData.getNbColumns(); + theDesc.format = inData.getFormat(); + theDesc.samples.stride = inData.getSampleStride(); + theDesc.samples.data = NULL; + theDesc.convexEdgeThreshold = inData.getConvexEdgeThreshold(); + theDesc.flags = inData.getFlags(); + + PxU32 theCellCount = inData.getNbRows() * inData.getNbColumns(); + PxU32 theSampleStride = sizeof(PxHeightFieldSample); + PxU32 theSampleBufSize = theCellCount * theSampleStride; + mBindingData->mTempU8Array.resize(theSampleBufSize); + PxHeightFieldSample* theSamples = reinterpret_cast(mBindingData->mTempU8Array.begin()); + inData.saveCells(theSamples, theSampleBufSize); + theDesc.samples.data = theSamples; + PxHeightFieldDescGeneratedValues values(&theDesc); + inStream.setPropertyMessage(&inData, values); + PxHeightFieldSample* theSampleData = reinterpret_cast(mBindingData->mTempU8Array.begin()); + inStream.setPropertyValue(&inData, "Samples", theSampleData, theCellCount); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxHeightField& inData, const PxPhysics& ownerPhysics) +{ + inStream.createInstance(&inData); + sendAllProperties(inStream, inData); + addPhysicsGroupProperty(inStream, "HeightFields", inData, ownerPhysics); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxHeightField& inData, const PxPhysics& ownerPhysics) +{ + removePhysicsGroupProperty(inStream, "HeightFields", inData, ownerPhysics); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxConvexMesh& inData, const PxPhysics& ownerPhysics) +{ + inStream.createInstance(&inData); + PxReal mass; + PxMat33 localInertia; + PxVec3 localCom; + inData.getMassInformation(mass, reinterpret_cast(localInertia), localCom); + inStream.setPropertyValue(&inData, "Mass", mass); + inStream.setPropertyValue(&inData, "LocalInertia", localInertia); + inStream.setPropertyValue(&inData, "LocalCenterOfMass", localCom); + + // update arrays: + // vertex Array: + { + const PxVec3* vertexPtr = inData.getVertices(); + const PxU32 numVertices = inData.getNbVertices(); + inStream.setPropertyValue(&inData, "Points", vertexPtr, numVertices); + } + + // HullPolyArray: + PxU16 maxIndices = 0; + { + + PxU32 numPolygons = inData.getNbPolygons(); + PvdHullPolygonData* tempData = mBindingData->allocateTemp(numPolygons); + // Get the polygon data stripping the plane equations + for(PxU32 index = 0; index < numPolygons; index++) + { + PxHullPolygon curOut; + inData.getPolygonData(index, curOut); + maxIndices = PxMax(maxIndices, PxU16(curOut.mIndexBase + curOut.mNbVerts)); + tempData[index].mIndexBase = curOut.mIndexBase; + tempData[index].mNumVertices = curOut.mNbVerts; + } + inStream.setPropertyValue(&inData, "HullPolygons", tempData, numPolygons); + } + + // poly index Array: + { + const PxU8* indices = inData.getIndexBuffer(); + inStream.setPropertyValue(&inData, "PolygonIndexes", indices, maxIndices); + } + addPhysicsGroupProperty(inStream, "ConvexMeshes", inData, ownerPhysics); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxConvexMesh& inData, const PxPhysics& ownerPhysics) +{ + removePhysicsGroupProperty(inStream, "ConvexMeshes", inData, ownerPhysics); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxTriangleMesh& inData, const PxPhysics& ownerPhysics) +{ + inStream.createInstance(&inData); + bool hasMatIndex = inData.getTriangleMaterialIndex(0) != 0xffff; + // update arrays: + // vertex Array: + { + const PxVec3* vertexPtr = inData.getVertices(); + const PxU32 numVertices = inData.getNbVertices(); + inStream.setPropertyValue(&inData, "Points", vertexPtr, numVertices); + } + + // index Array: + { + const bool has16BitIndices = inData.getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false; + const PxU32 numTriangles = inData.getNbTriangles(); + + inStream.setPropertyValue(&inData, "NbTriangles", numTriangles); + + const PxU32 numIndexes = numTriangles * 3; + const PxU8* trianglePtr = reinterpret_cast(inData.getTriangles()); + // We declared this type as a 32 bit integer above. + // PVD will automatically unsigned-extend data that is smaller than the target type. + if(has16BitIndices) + inStream.setPropertyValue(&inData, "Triangles", reinterpret_cast(trianglePtr), numIndexes); + else + inStream.setPropertyValue(&inData, "Triangles", reinterpret_cast(trianglePtr), numIndexes); + } + + // material Array: + if(hasMatIndex) + { + PxU32 numMaterials = inData.getNbTriangles(); + PxU16* matIndexData = mBindingData->allocateTemp(numMaterials); + for(PxU32 m = 0; m < numMaterials; m++) + matIndexData[m] = inData.getTriangleMaterialIndex(m); + inStream.setPropertyValue(&inData, "MaterialIndices", matIndexData, numMaterials); + } + addPhysicsGroupProperty(inStream, "TriangleMeshes", inData, ownerPhysics); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxTriangleMesh& inData, const PxPhysics& ownerPhysics) +{ + removePhysicsGroupProperty(inStream, "TriangleMeshes", inData, ownerPhysics); +} + +template +void PvdMetaDataBinding::registrarPhysicsObject(PvdDataStream&, const TDataType&, PsPvd*) +{ +} + +template <> +void PvdMetaDataBinding::registrarPhysicsObject(PvdDataStream& inStream, const PxConvexMeshGeometry& geom, PsPvd* pvd) +{ + if(pvd->registerObject(geom.convexMesh)) + createInstance(inStream, *geom.convexMesh, PxGetPhysics()); +} + +template <> +void PvdMetaDataBinding::registrarPhysicsObject(PvdDataStream& inStream, const PxTriangleMeshGeometry& geom, PsPvd* pvd) +{ + if(pvd->registerObject(geom.triangleMesh)) + createInstance(inStream, *geom.triangleMesh, PxGetPhysics()); +} + +template <> +void PvdMetaDataBinding::registrarPhysicsObject(PvdDataStream& inStream, const PxHeightFieldGeometry& geom, PsPvd* pvd) +{ + if(pvd->registerObject(geom.heightField)) + createInstance(inStream, *geom.heightField, PxGetPhysics()); +} + +template +static void sendGeometry(PvdMetaDataBinding& metaBind, PvdDataStream& inStream, const PxShape& inShape, const TGeomType& geom, PsPvd* pvd) +{ + const void* geomInst = (reinterpret_cast(&inShape)) + 4; + inStream.createInstance(getPvdNamespacedNameForType(), geomInst); + metaBind.registrarPhysicsObject(inStream, geom, pvd); + TGeneratedValuesType values(&geom); + inStream.setPropertyMessage(geomInst, values); + inStream.setPropertyValue(&inShape, "Geometry", geomInst); + inStream.setPropertyValue(geomInst, "Shape", reinterpret_cast(&inShape)); +} + +static void setGeometry(PvdMetaDataBinding& metaBind, PvdDataStream& inStream, const PxShape& inObj, PsPvd* pvd) +{ + switch(inObj.getGeometryType()) + { +#define SEND_PVD_GEOM_TYPE(enumType, geomType, valueType) \ + case PxGeometryType::enumType: \ + { \ + Px##geomType geom; \ + inObj.get##geomType(geom); \ + sendGeometry(metaBind, inStream, inObj, geom, pvd); \ + } \ + break; + SEND_PVD_GEOM_TYPE(eSPHERE, SphereGeometry, PxSphereGeometryGeneratedValues); + // Plane geometries don't have any properties, so this avoids using a property + // struct for them. + case PxGeometryType::ePLANE: + { + PxPlaneGeometry geom; + inObj.getPlaneGeometry(geom); + const void* geomInst = (reinterpret_cast(&inObj)) + 4; + inStream.createInstance(getPvdNamespacedNameForType(), geomInst); + inStream.setPropertyValue(&inObj, "Geometry", geomInst); + inStream.setPropertyValue(geomInst, "Shape", reinterpret_cast(&inObj)); + } + break; + SEND_PVD_GEOM_TYPE(eCAPSULE, CapsuleGeometry, PxCapsuleGeometryGeneratedValues); + SEND_PVD_GEOM_TYPE(eBOX, BoxGeometry, PxBoxGeometryGeneratedValues); + SEND_PVD_GEOM_TYPE(eCONVEXMESH, ConvexMeshGeometry, PxConvexMeshGeometryGeneratedValues); + SEND_PVD_GEOM_TYPE(eTRIANGLEMESH, TriangleMeshGeometry, PxTriangleMeshGeometryGeneratedValues); + SEND_PVD_GEOM_TYPE(eHEIGHTFIELD, HeightFieldGeometry, PxHeightFieldGeometryGeneratedValues); +#undef SEND_PVD_GEOM_TYPE + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ASSERT(false); + break; + } +} + +static void setMaterials(PvdMetaDataBinding& metaBing, PvdDataStream& inStream, const PxShape& inObj, PsPvd* pvd, PvdMetaDataBindingData* mBindingData) +{ + PxU32 numMaterials = inObj.getNbMaterials(); + PxMaterial** materialPtr = mBindingData->allocateTemp(numMaterials); + inObj.getMaterials(materialPtr, numMaterials); + for(PxU32 idx = 0; idx < numMaterials; ++idx) + { + if(pvd->registerObject(materialPtr[idx])) + metaBing.createInstance(inStream, *materialPtr[idx], PxGetPhysics()); + inStream.pushBackObjectRef(&inObj, "Materials", materialPtr[idx]); + } +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxShape& inObj, const PxRigidActor& owner, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + if(!inStream.isInstanceValid(&owner)) + return; + + const OwnerActorsMap::Entry* entry = mBindingData->mOwnerActorsMap.find(&inObj); + if(entry != NULL) + { + if(!mBindingData->mOwnerActorsMap[&inObj]->contains(&owner)) + mBindingData->mOwnerActorsMap[&inObj]->insert(&owner); + } + else + { + OwnerActorsValueType* data = reinterpret_cast( + PX_ALLOC(sizeof(OwnerActorsValueType), "mOwnerActorsMapValue")); //( 1 ); + OwnerActorsValueType* actors = PX_PLACEMENT_NEW(data, OwnerActorsValueType); + actors->insert(&owner); + + mBindingData->mOwnerActorsMap.insert(&inObj, actors); + } + + if(inStream.isInstanceValid(&inObj)) + { + inStream.pushBackObjectRef(&owner, "Shapes", &inObj); + return; + } + + inStream.createInstance(&inObj); + inStream.pushBackObjectRef(&owner, "Shapes", &inObj); + inStream.setPropertyValue(&inObj, "Actor", reinterpret_cast(&owner)); + sendAllProperties(inStream, inObj); + setGeometry(*this, inStream, inObj, pvd); + setMaterials(*this, inStream, inObj, pvd, mBindingData); + if(!inObj.isExclusive()) + inStream.pushBackObjectRef(&ownerPhysics, "SharedShapes", &inObj); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxShape& inObj) +{ + PxShapeGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} + +void PvdMetaDataBinding::releaseAndRecreateGeometry(PvdDataStream& inStream, const PxShape& inObj, PxPhysics& /*ownerPhysics*/, PsPvd* pvd) +{ + const void* geomInst = (reinterpret_cast(&inObj)) + 4; + inStream.destroyInstance(geomInst); + // Quick fix for HF modify, PxConvexMesh and PxTriangleMesh need recook, they should always be new if modified + if(inObj.getGeometryType() == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometry hfGeom; + inObj.getHeightFieldGeometry(hfGeom); + if(inStream.isInstanceValid(hfGeom.heightField)) + sendAllProperties(inStream, *hfGeom.heightField); + } + + setGeometry(*this, inStream, inObj, pvd); + + // Need update actor cause PVD takes actor-shape as a pair. + { + PxRigidActor* actor = inObj.getActor(); + if(actor != NULL) + { + + if(const PxRigidStatic* rgS = actor->is()) + sendAllProperties(inStream, *rgS); + else if(const PxRigidDynamic* rgD = actor->is()) + sendAllProperties(inStream, *rgD); + } + } +} + +void PvdMetaDataBinding::updateMaterials(PvdDataStream& inStream, const PxShape& inObj, PsPvd* pvd) +{ + // Clear the shape's materials array. + inStream.setPropertyValue(&inObj, "Materials", DataRef(), getPvdNamespacedNameForType()); + setMaterials(*this, inStream, inObj, pvd, mBindingData); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxShape& inObj, const PxRigidActor& owner) +{ + if(inStream.isInstanceValid(&inObj)) + { + inStream.removeObjectRef(&owner, "Shapes", &inObj); + + bool bDestroy = true; + const OwnerActorsMap::Entry* entry0 = mBindingData->mOwnerActorsMap.find(&inObj); + if(entry0 != NULL) + { + entry0->second->erase(&owner); + if(entry0->second->size() > 0) + bDestroy = false; + else + { + mBindingData->mOwnerActorsMap[&inObj]->~OwnerActorsValueType(); + PX_FREE(mBindingData->mOwnerActorsMap[&inObj]); + mBindingData->mOwnerActorsMap.erase(&inObj); + } + } + + if(bDestroy) + { + if(!inObj.isExclusive()) + inStream.removeObjectRef(&PxGetPhysics(), "SharedShapes", &inObj); + + const void* geomInst = (reinterpret_cast(&inObj)) + 4; + inStream.destroyInstance(geomInst); + inStream.destroyInstance(&inObj); + + const OwnerActorsMap::Entry* entry = mBindingData->mOwnerActorsMap.find(&inObj); + if(entry != NULL) + { + entry->second->~OwnerActorsValueType(); + PX_FREE(entry->second); + mBindingData->mOwnerActorsMap.erase(&inObj); + } + } + } +} + +template +static void addSceneGroupProperty(PvdDataStream& inStream, const char* groupName, const TDataType& inObj, const PxScene& inScene) +{ + inStream.createInstance(&inObj); + inStream.pushBackObjectRef(&inScene, groupName, &inObj); + inStream.setPropertyValue(&inObj, "Scene", reinterpret_cast(&inScene)); +} + +template +static void removeSceneGroupProperty(PvdDataStream& inStream, const char* groupName, const TDataType& inObj, const PxScene& inScene) +{ + inStream.removeObjectRef(&inScene, groupName, &inObj); + inStream.destroyInstance(&inObj); +} + +static void sendShapes(PvdMetaDataBinding& binding, PvdDataStream& inStream, const PxRigidActor& inObj, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + InlineArray shapeData; + PxU32 nbShapes = inObj.getNbShapes(); + shapeData.resize(nbShapes); + inObj.getShapes(shapeData.begin(), nbShapes); + for(PxU32 idx = 0; idx < nbShapes; ++idx) + binding.createInstance(inStream, *shapeData[idx], inObj, ownerPhysics, pvd); +} + +static void releaseShapes(PvdMetaDataBinding& binding, PvdDataStream& inStream, const PxRigidActor& inObj) +{ + InlineArray shapeData; + PxU32 nbShapes = inObj.getNbShapes(); + shapeData.resize(nbShapes); + inObj.getShapes(shapeData.begin(), nbShapes); + for(PxU32 idx = 0; idx < nbShapes; ++idx) + binding.destroyInstance(inStream, *shapeData[idx], inObj); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxRigidStatic& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + addSceneGroupProperty(inStream, "RigidStatics", inObj, ownerScene); + sendAllProperties(inStream, inObj); + sendShapes(*this, inStream, inObj, ownerPhysics, pvd); +} +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxRigidStatic& inObj) +{ + PxRigidStaticGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxRigidStatic& inObj, const PxScene& ownerScene) +{ + releaseShapes(*this, inStream, inObj); + removeSceneGroupProperty(inStream, "RigidStatics", inObj, ownerScene); +} +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxRigidDynamic& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + addSceneGroupProperty(inStream, "RigidDynamics", inObj, ownerScene); + sendAllProperties(inStream, inObj); + sendShapes(*this, inStream, inObj, ownerPhysics, pvd); +} +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxRigidDynamic& inObj) +{ + PxRigidDynamicGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxRigidDynamic& inObj, const PxScene& ownerScene) +{ + releaseShapes(*this, inStream, inObj); + removeSceneGroupProperty(inStream, "RigidDynamics", inObj, ownerScene); +} + +static void addChild(PvdDataStream& inStream, const void* inParent, const PxArticulationLink& inChild) +{ + inStream.pushBackObjectRef(inParent, "Links", &inChild); + inStream.setPropertyValue(&inChild, "Parent", inParent); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxArticulationBase& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + addSceneGroupProperty(inStream, "Articulations", inObj, ownerScene); + sendAllProperties(inStream, inObj); + PxU32 numLinks = inObj.getNbLinks(); + mBindingData->mArticulationLinks.resize(numLinks); + inObj.getLinks(mBindingData->mArticulationLinks.begin(), numLinks); + // From Dilip Sequiera: + /* + No, there can only be one root, and in all the code I wrote (which is not 100% of the HL code for + articulations), the index of a child is always > the index of the parent. + */ + + // Create all the links + for(PxU32 idx = 0; idx < numLinks; ++idx) + { + if(!inStream.isInstanceValid(mBindingData->mArticulationLinks[idx])) + createInstance(inStream, *mBindingData->mArticulationLinks[idx], ownerPhysics, pvd); + } + + // Setup the link graph + for(PxU32 idx = 0; idx < numLinks; ++idx) + { + PxArticulationLink* link = mBindingData->mArticulationLinks[idx]; + if(idx == 0) + addChild(inStream, &inObj, *link); + + PxU32 numChildren = link->getNbChildren(); + PxArticulationLink** children = mBindingData->allocateTemp(numChildren); + link->getChildren(children, numChildren); + for(PxU32 i = 0; i < numChildren; ++i) + addChild(inStream, link, *children[i]); + } +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxArticulationBase& inObj) +{ + PxArticulationBaseGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxArticulationBase& inObj, const PxScene& ownerScene) +{ + removeSceneGroupProperty(inStream, "Articulations", inObj, ownerScene); +} + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxArticulationLink& inObj, const PxPhysics& ownerPhysics, PsPvd* pvd) +{ + inStream.createInstance(&inObj); + PxArticulationJointBase* joint(inObj.getInboundJoint()); + if(joint) + { + inStream.createInstance(joint); + inStream.setPropertyValue(&inObj, "InboundJoint", reinterpret_cast(joint)); + inStream.setPropertyValue(joint, "Link", reinterpret_cast(&inObj)); + sendAllProperties(inStream, *joint); + } + sendAllProperties(inStream, inObj); + sendShapes(*this, inStream, inObj, ownerPhysics, pvd); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxArticulationLink& inObj) +{ + PxArticulationLinkGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxArticulationLink& inObj) +{ + PxArticulationJointBase* joint(inObj.getInboundJoint()); + if(joint) + inStream.destroyInstance(joint); + releaseShapes(*this, inStream, inObj); + inStream.destroyInstance(&inObj); +} +// These are created as part of the articulation link's creation process, so outside entities don't need to +// create them. +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxArticulationJointBase& inObj) +{ + PxArticulationJointBaseGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} + +void PvdMetaDataBinding::originShift(PvdDataStream& inStream, const PxScene* inScene, PxVec3 shift) +{ + inStream.originShift(inScene, shift); +} + +template +static void updateActor(PvdDataStream& inStream, TActorType** actorGroup, PxU32 numActors, TOperator sleepingOp, PvdMetaDataBindingData& bindingData) +{ + TBlockType theBlock; + if(numActors == 0) + return; + for(PxU32 idx = 0; idx < numActors; ++idx) + { + TActorType* theActor(actorGroup[idx]); + bool sleeping = sleepingOp(theActor, theBlock); + bool wasSleeping = bindingData.mSleepingActors.contains(theActor); + + if(sleeping == false || sleeping != wasSleeping) + { + theBlock.GlobalPose = theActor->getGlobalPose(); + theBlock.AngularVelocity = theActor->getAngularVelocity(); + theBlock.LinearVelocity = theActor->getLinearVelocity(); + inStream.sendPropertyMessageFromGroup(theActor, theBlock); + if(sleeping != wasSleeping) + { + if(sleeping) + bindingData.mSleepingActors.insert(theActor); + else + bindingData.mSleepingActors.erase(theActor); + } + } + } +} + +struct RigidDynamicUpdateOp +{ + bool operator()(PxRigidDynamic* actor, PxRigidDynamicUpdateBlock& block) + { + bool sleeping = actor->isSleeping(); + block.IsSleeping = sleeping; + return sleeping; + } +}; + +struct ArticulationLinkUpdateOp +{ + bool sleeping; + ArticulationLinkUpdateOp(bool s) : sleeping(s) + { + } + bool operator()(PxArticulationLink*, PxArticulationLinkUpdateBlock&) + { + return sleeping; + } +}; + +void PvdMetaDataBinding::updateDynamicActorsAndArticulations(PvdDataStream& inStream, const PxScene* inScene, PvdVisualizer* linkJointViz) +{ + PX_COMPILE_TIME_ASSERT(sizeof(PxRigidDynamicUpdateBlock) == 14 * 4); + { + PxU32 actorCount = inScene->getNbActors(PxActorTypeFlag::eRIGID_DYNAMIC); + if(actorCount) + { + inStream.beginPropertyMessageGroup(); + mBindingData->mActors.resize(actorCount); + PxActor** theActors = mBindingData->mActors.begin(); + inScene->getActors(PxActorTypeFlag::eRIGID_DYNAMIC, theActors, actorCount); + updateActor(inStream, reinterpret_cast(theActors), actorCount, RigidDynamicUpdateOp(), *mBindingData); + inStream.endPropertyMessageGroup(); + } + } + { + PxU32 articulationCount = inScene->getNbArticulations(); + if(articulationCount) + { + mBindingData->mArticulations.resize(articulationCount); + PxArticulationBase** firstArticulation = mBindingData->mArticulations.begin(); + PxArticulationBase** lastArticulation = firstArticulation + articulationCount; + inScene->getArticulations(firstArticulation, articulationCount); + inStream.beginPropertyMessageGroup(); + for(; firstArticulation < lastArticulation; ++firstArticulation) + { + PxU32 linkCount = (*firstArticulation)->getNbLinks(); + bool sleeping = (*firstArticulation)->isSleeping(); + if(linkCount) + { + mBindingData->mArticulationLinks.resize(linkCount); + PxArticulationLink** theLink = mBindingData->mArticulationLinks.begin(); + (*firstArticulation)->getLinks(theLink, linkCount); + updateActor(inStream, theLink, linkCount, ArticulationLinkUpdateOp(sleeping), *mBindingData); + if(linkJointViz) + { + for(PxU32 idx = 0; idx < linkCount; ++idx) + linkJointViz->visualize(*theLink[idx]); + } + } + } + inStream.endPropertyMessageGroup(); + firstArticulation = mBindingData->mArticulations.begin(); + for(; firstArticulation < lastArticulation; ++firstArticulation) + inStream.setPropertyValue(*firstArticulation, "IsSleeping", (*firstArticulation)->isSleeping()); + } + } +} + +template +struct CollectionOperator +{ + Array& mTempArray; + const TObjType& mObject; + PvdDataStream& mStream; + + CollectionOperator(Array& ary, const TObjType& obj, PvdDataStream& stream) + : mTempArray(ary), mObject(obj), mStream(stream) + { + } + void pushName(const char*) + { + } + void popName() + { + } + template + void simpleProperty(PxU32 /*key*/, const TAccessor&) + { + } + template + void flagsProperty(PxU32 /*key*/, const TAccessor&, const PxU32ToName*) + { + } + + template + void handleCollection(const TCollectionProp& prop, NamespacedName dtype, PxU32 countMultiplier = 1) + { + PxU32 count = prop.size(&mObject); + mTempArray.resize(count * sizeof(TDataType)); + TColType* start = reinterpret_cast(mTempArray.begin()); + prop.get(&mObject, start, count * countMultiplier); + mStream.setPropertyValue(&mObject, prop.mName, DataRef(mTempArray.begin(), mTempArray.size()), dtype); + } + template + void handleCollection(const PxReadOnlyCollectionPropertyInfo& prop) + { + handleCollection(prop, getPvdNamespacedNameForType()); + } + // Enumerations or bitflags. + template + void handleCollection(const PxReadOnlyCollectionPropertyInfo& prop, const PxU32ToName*) + { + PX_COMPILE_TIME_ASSERT(sizeof(TColType) == sizeof(PxU32)); + handleCollection(prop, getPvdNamespacedNameForType()); + } + + private: + CollectionOperator& operator=(const CollectionOperator&); +}; + +// per frame update + +#define ENABLE_AGGREGATE_PVD_SUPPORT 1 +#ifdef ENABLE_AGGREGATE_PVD_SUPPORT + +void PvdMetaDataBinding::createInstance(PvdDataStream& inStream, const PxAggregate& inObj, const PxScene& ownerScene) +{ + addSceneGroupProperty(inStream, "Aggregates", inObj, ownerScene); + sendAllProperties(inStream, inObj); +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream& inStream, const PxAggregate& inObj) +{ + PxAggregateGeneratedValues values(&inObj); + inStream.setPropertyMessage(&inObj, values); +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream& inStream, const PxAggregate& inObj, const PxScene& ownerScene) +{ + removeSceneGroupProperty(inStream, "Aggregates", inObj, ownerScene); +} + +class ChangeOjectRefCmd : public PvdDataStream::PvdCommand +{ + ChangeOjectRefCmd& operator=(const ChangeOjectRefCmd&) + { + PX_ASSERT(0); + return *this; + } // PX_NOCOPY doesn't work for local classes + public: + const void* mInstance; + String mPropName; + const void* mPropObj; + const bool mPushBack; + + ChangeOjectRefCmd(const void* inInst, String inName, const void* inObj, bool pushBack) + : mInstance(inInst), mPropName(inName), mPropObj(inObj), mPushBack(pushBack) + { + } + + // Assigned is needed for copying + ChangeOjectRefCmd(const ChangeOjectRefCmd& other) + : PvdDataStream::PvdCommand(other), mInstance(other.mInstance), mPropName(other.mPropName), mPropObj(other.mPropObj), mPushBack(other.mPushBack) + { + } + + virtual bool canRun(PvdInstanceDataStream& inStream) + { + PX_ASSERT(inStream.isInstanceValid(mInstance)); + return inStream.isInstanceValid(mPropObj); + } + virtual void run(PvdInstanceDataStream& inStream) + { + if(!inStream.isInstanceValid(mInstance)) + return; + + if(mPushBack) + { + if(inStream.isInstanceValid(mPropObj)) + inStream.pushBackObjectRef(mInstance, mPropName, mPropObj); + } + else + { + // the called function will assert if propobj is already removed + inStream.removeObjectRef(mInstance, mPropName, mPropObj); + } + } +}; + +static void changeAggregateSubActors(PvdDataStream& inStream, const PxAggregate& inObj, const PxActor& inActor, bool pushBack) +{ + const PxArticulationLink* link = inActor.is(); + String propName = NULL; + const void* object = NULL; + if(link == NULL) + { + propName = "Actors"; + object = &inActor; + } + else if(link->getInboundJoint() == NULL) + { + propName = "Articulations"; + object = &link->getArticulation(); + } + else + return; + + ChangeOjectRefCmd* cmd = PX_PLACEMENT_NEW(inStream.allocateMemForCmd(sizeof(ChangeOjectRefCmd)), ChangeOjectRefCmd)(&inObj, propName, object, pushBack); + + if(cmd->canRun(inStream)) + cmd->run(inStream); + else + inStream.pushPvdCommand(*cmd); +} +void PvdMetaDataBinding::detachAggregateActor(PvdDataStream& inStream, const PxAggregate& inObj, const PxActor& inActor) +{ + changeAggregateSubActors(inStream, inObj, inActor, false); +} + +void PvdMetaDataBinding::attachAggregateActor(PvdDataStream& inStream, const PxAggregate& inObj, const PxActor& inActor) +{ + changeAggregateSubActors(inStream, inObj, inActor, true); +} +#else +void PvdMetaDataBinding::createInstance(PvdDataStream&, const PxAggregate&, const PxScene&, ObjectRegistrar&) +{ +} + +void PvdMetaDataBinding::sendAllProperties(PvdDataStream&, const PxAggregate&) +{ +} + +void PvdMetaDataBinding::destroyInstance(PvdDataStream&, const PxAggregate&, const PxScene&) +{ +} + +void PvdMetaDataBinding::detachAggregateActor(PvdDataStream&, const PxAggregate&, const PxActor&) +{ +} + +void PvdMetaDataBinding::attachAggregateActor(PvdDataStream&, const PxAggregate&, const PxActor&) +{ +} +#endif + +template +static void sendSceneArray(PvdDataStream& inStream, const PxScene& inScene, const Ps::Array& inArray, const char* propName) +{ + if(0 == inArray.size()) + inStream.setPropertyValue(&inScene, propName, DataRef(), getPvdNamespacedNameForType()); + else + { + ScopedPropertyValueSender sender(inStream, &inScene, propName); + for(PxU32 i = 0; i < inArray.size(); ++i) + sender.append(inArray[i]); + } +} + +static void sendSceneArray(PvdDataStream& inStream, const PxScene& inScene, const Ps::Array& inArray, const char* propName) +{ + if(0 == inArray.size()) + inStream.setPropertyValue(&inScene, propName, DataRef(), getPvdNamespacedNameForType()); + else + { + ScopedPropertyValueSender sender(inStream, &inScene, propName); + for(PxU32 i = 0; i < inArray.size(); ++i) + { + if(!inStream.isInstanceValid(inArray[i].mShape) || !inStream.isInstanceValid(inArray[i].mActor)) + { + PvdSqHit hit = inArray[i]; + hit.mShape = NULL; + hit.mActor = NULL; + sender.append(hit); + } + else + sender.append(inArray[i]); + } + } +} + +void PvdMetaDataBinding::sendSceneQueries(PvdDataStream& inStream, const PxScene& inScene, PsPvd* pvd) +{ + if(!inStream.isConnected()) + return; + + const physx::NpScene& scene = static_cast(inScene); + + for(PxU32 i = 0; i < 2; i++) + { + PvdSceneQueryCollector& collector = ((i == 0) ? scene.getSingleSqCollector() : scene.getBatchedSqCollector()); + Ps::Mutex::ScopedLock lock(collector.getLock()); + + String propName = collector.getArrayName(collector.mPvdSqHits); + sendSceneArray(inStream, inScene, collector.mPvdSqHits, propName); + + propName = collector.getArrayName(collector.mPoses); + sendSceneArray(inStream, inScene, collector.mPoses, propName); + + propName = collector.getArrayName(collector.mFilterData); + sendSceneArray(inStream, inScene, collector.mFilterData, propName); + + const NamedArray& geometriesToDestroy = collector.getPrevFrameGeometries(); + propName = collector.getArrayName(geometriesToDestroy); + for(PxU32 k = 0; k < geometriesToDestroy.size(); ++k) + { + const PxGeometryHolder& inObj = geometriesToDestroy[k]; + inStream.removeObjectRef(&inScene, propName, &inObj); + inStream.destroyInstance(&inObj); + } + const Ps::Array& geometriesToCreate = collector.getCurrentFrameGeometries(); + for(PxU32 k = 0; k < geometriesToCreate.size(); ++k) + { + const PxGeometry& geometry = geometriesToCreate[k].any(); + switch(geometry.getType()) + { +#define SEND_PVD_GEOM_TYPE(enumType, TGeomType, TValueType) \ + case enumType: \ + { \ + const TGeomType& inObj = static_cast(geometry); \ + inStream.createInstance(getPvdNamespacedNameForType(), &inObj); \ + registrarPhysicsObject(inStream, inObj, pvd); \ + TValueType values(&inObj); \ + inStream.setPropertyMessage(&inObj, values); \ + inStream.pushBackObjectRef(&inScene, propName, &inObj); \ + } \ + break; + SEND_PVD_GEOM_TYPE(PxGeometryType::eBOX, PxBoxGeometry, PxBoxGeometryGeneratedValues) + SEND_PVD_GEOM_TYPE(PxGeometryType::eSPHERE, PxSphereGeometry, PxSphereGeometryGeneratedValues) + SEND_PVD_GEOM_TYPE(PxGeometryType::eCAPSULE, PxCapsuleGeometry, PxCapsuleGeometryGeneratedValues) + SEND_PVD_GEOM_TYPE(PxGeometryType::eCONVEXMESH, PxConvexMeshGeometry, PxConvexMeshGeometryGeneratedValues) +#undef SEND_PVD_GEOM_TYPE + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported scene query geometry type"); + break; + } + } + collector.prepareNextFrameGeometries(); + + propName = collector.getArrayName(collector.mAccumulatedRaycastQueries); + sendSceneArray(inStream, inScene, collector.mAccumulatedRaycastQueries, propName); + + propName = collector.getArrayName(collector.mAccumulatedOverlapQueries); + sendSceneArray(inStream, inScene, collector.mAccumulatedOverlapQueries, propName); + + propName = collector.getArrayName(collector.mAccumulatedSweepQueries); + sendSceneArray(inStream, inScene, collector.mAccumulatedSweepQueries, propName); + } +} +} +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h new file mode 100644 index 000000000..55df602b2 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h @@ -0,0 +1,151 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_META_DATA_PVD_BINDING_H +#define PX_META_DATA_PVD_BINDING_H + +#if PX_SUPPORT_PVD + +#include "PxPhysXConfig.h" +#include "PsArray.h" + +namespace physx +{ + namespace pvdsdk + { + class PsPvd; + class PvdDataStream; + struct PvdMetaDataBindingData; + } +} + +namespace physx +{ + +namespace Sc +{ +struct Contact; +} + +namespace Vd +{ + +using namespace physx::pvdsdk; + +class PvdVisualizer +{ + protected: + virtual ~PvdVisualizer() + { + } + + public: + virtual void visualize(PxArticulationLink& link) = 0; +}; + +class PvdMetaDataBinding +{ + PvdMetaDataBindingData* mBindingData; + + public: + PvdMetaDataBinding(); + ~PvdMetaDataBinding(); + void registerSDKProperties(PvdDataStream& inStream); + + void sendAllProperties(PvdDataStream& inStream, const PxPhysics& inPhysics); + + void sendAllProperties(PvdDataStream& inStream, const PxScene& inScene); + // per frame update + void sendBeginFrame(PvdDataStream& inStream, const PxScene* inScene, PxReal simulateElapsedTime); + void sendContacts(PvdDataStream& inStream, const PxScene& inScene, shdfnd::Array& inContacts); + void sendContacts(PvdDataStream& inStream, const PxScene& inScene); + void sendStats(PvdDataStream& inStream, const PxScene* inScene); + void sendSceneQueries(PvdDataStream& inStream, const PxScene& inScene, PsPvd* pvd); + void sendEndFrame(PvdDataStream& inStream, const PxScene* inScene); + + void createInstance(PvdDataStream& inStream, const PxMaterial& inMaterial, const PxPhysics& ownerPhysics); + void sendAllProperties(PvdDataStream& inStream, const PxMaterial& inMaterial); + void destroyInstance(PvdDataStream& inStream, const PxMaterial& inMaterial, const PxPhysics& ownerPhysics); + + void createInstance(PvdDataStream& inStream, const PxHeightField& inData, const PxPhysics& ownerPhysics); + void sendAllProperties(PvdDataStream& inStream, const PxHeightField& inData); + void destroyInstance(PvdDataStream& inStream, const PxHeightField& inData, const PxPhysics& ownerPhysics); + + void createInstance(PvdDataStream& inStream, const PxConvexMesh& inData, const PxPhysics& ownerPhysics); + void destroyInstance(PvdDataStream& inStream, const PxConvexMesh& inData, const PxPhysics& ownerPhysics); + + void createInstance(PvdDataStream& inStream, const PxTriangleMesh& inData, const PxPhysics& ownerPhysics); + void destroyInstance(PvdDataStream& inStream, const PxTriangleMesh& inData, const PxPhysics& ownerPhysics); + + void createInstance(PvdDataStream& inStream, const PxRigidStatic& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd); + void sendAllProperties(PvdDataStream& inStream, const PxRigidStatic& inObj); + void destroyInstance(PvdDataStream& inStream, const PxRigidStatic& inObj, const PxScene& ownerScene); + + void createInstance(PvdDataStream& inStream, const PxRigidDynamic& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd); + void sendAllProperties(PvdDataStream& inStream, const PxRigidDynamic& inObj); + void destroyInstance(PvdDataStream& inStream, const PxRigidDynamic& inObj, const PxScene& ownerScene); + + void createInstance(PvdDataStream& inStream, const PxArticulationBase& inObj, const PxScene& ownerScene, const PxPhysics& ownerPhysics, PsPvd* pvd); + void sendAllProperties(PvdDataStream& inStream, const PxArticulationBase& inObj); + void destroyInstance(PvdDataStream& inStream, const PxArticulationBase& inObj, const PxScene& ownerScene); + + void createInstance(PvdDataStream& inStream, const PxArticulationLink& inObj, const PxPhysics& ownerPhysics, PsPvd* pvd); + void sendAllProperties(PvdDataStream& inStream, const PxArticulationLink& inObj); + void destroyInstance(PvdDataStream& inStream, const PxArticulationLink& inObj); + + void createInstance(PvdDataStream& inStream, const PxShape& inObj, const PxRigidActor& owner, const PxPhysics& ownerPhysics, PsPvd* pvd); + void sendAllProperties(PvdDataStream& inStream, const PxShape& inObj); + void releaseAndRecreateGeometry(PvdDataStream& inStream, const PxShape& inObj, PxPhysics& ownerPhysics, PsPvd* pvd); + void updateMaterials(PvdDataStream& inStream, const PxShape& inObj, PsPvd* pvd); + void destroyInstance(PvdDataStream& inStream, const PxShape& inObj, const PxRigidActor& owner); + + // These are created as part of the articulation link's creation process, so outside entities don't need to + // create them. + void sendAllProperties(PvdDataStream& inStream, const PxArticulationJointBase& inObj); + + // per frame update + void updateDynamicActorsAndArticulations(PvdDataStream& inStream, const PxScene* inScene, PvdVisualizer* linkJointViz); + + // Origin Shift + void originShift(PvdDataStream& inStream, const PxScene* inScene, PxVec3 shift); + + void createInstance(PvdDataStream& inStream, const PxAggregate& inObj, const PxScene& ownerScene); + void sendAllProperties(PvdDataStream& inStream, const PxAggregate& inObj); + void destroyInstance(PvdDataStream& inStream, const PxAggregate& inObj, const PxScene& ownerScene); + void detachAggregateActor(PvdDataStream& inStream, const PxAggregate& inObj, const PxActor& inActor); + void attachAggregateActor(PvdDataStream& inStream, const PxAggregate& inObj, const PxActor& inActor); + + template + void registrarPhysicsObject(PvdDataStream&, const TDataType&, PsPvd*); +}; +} +} + +#endif +#endif diff --git a/src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp new file mode 100644 index 000000000..537cee7c2 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp @@ -0,0 +1,216 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// PX_DUMMY_SYMBOL + +#if PX_SUPPORT_PVD + +#include "pvd/PxPvdTransport.h" +#include "PxPhysics.h" +#include "PxPvdClient.h" +#include "PxPvdDataStream.h" +#include "PxPvdObjectModelBaseTypes.h" +#include "PvdPhysicsClient.h" +#include "PvdTypeNames.h" + +using namespace physx; +using namespace physx::Vd; + +PvdPhysicsClient::PvdPhysicsClient(PsPvd* pvd) +: mPvd(pvd), mPvdDataStream(NULL), mIsConnected(false) +{ +} + +PvdPhysicsClient::~PvdPhysicsClient() +{ + mPvd->removeClient(this); +} + +PvdDataStream* PvdPhysicsClient::getDataStream() +{ + return mPvdDataStream; +} + +PvdMetaDataBinding* PvdPhysicsClient::getMetaDataBinding() +{ + return &mMetaDataBinding; +} + +PvdUserRenderer* PvdPhysicsClient::getUserRender() +{ + PX_ASSERT(0); + return NULL; +} + +bool PvdPhysicsClient::isConnected() const +{ + return mIsConnected; +} + +void PvdPhysicsClient::onPvdConnected() +{ + if(mIsConnected || !mPvd) + return; + + mIsConnected = true; + mPvdDataStream = PvdDataStream::create(mPvd); + sendEntireSDK(); +} + +void PvdPhysicsClient::onPvdDisconnected() +{ + if(!mIsConnected) + return; + mIsConnected = false; + + mPvdDataStream->release(); + mPvdDataStream = NULL; +} + +void PvdPhysicsClient::flush() +{ +} + +void PvdPhysicsClient::sendEntireSDK() +{ + PxPhysics& physics = PxGetPhysics(); + + mMetaDataBinding.registerSDKProperties(*mPvdDataStream); + mPvdDataStream->createInstance(&physics); + + mPvdDataStream->setIsTopLevelUIElement(&physics, true); + mMetaDataBinding.sendAllProperties(*mPvdDataStream, physics); + +#define SEND_BUFFER_GROUP(type, name) \ + { \ + physx::shdfnd::Array buffers; \ + PxU32 numBuffers = physics.getNb##name(); \ + buffers.resize(numBuffers); \ + physics.get##name(buffers.begin(), numBuffers); \ + for(PxU32 i = 0; i < numBuffers; i++) \ + { \ + if(mPvd->registerObject(buffers[i])) \ + createPvdInstance(buffers[i]); \ + } \ + } + + SEND_BUFFER_GROUP(PxMaterial, Materials); + SEND_BUFFER_GROUP(PxTriangleMesh, TriangleMeshes); + SEND_BUFFER_GROUP(PxConvexMesh, ConvexMeshes); + SEND_BUFFER_GROUP(PxHeightField, HeightFields); +} + +void PvdPhysicsClient::destroyPvdInstance(const PxPhysics* physics) +{ + if(mPvdDataStream) + mPvdDataStream->destroyInstance(physics); +} + +void PvdPhysicsClient::createPvdInstance(const PxTriangleMesh* triMesh) +{ + mMetaDataBinding.createInstance(*mPvdDataStream, *triMesh, PxGetPhysics()); +} + +void PvdPhysicsClient::destroyPvdInstance(const PxTriangleMesh* triMesh) +{ + mMetaDataBinding.destroyInstance(*mPvdDataStream, *triMesh, PxGetPhysics()); +} + +void PvdPhysicsClient::createPvdInstance(const PxConvexMesh* convexMesh) +{ + mMetaDataBinding.createInstance(*mPvdDataStream, *convexMesh, PxGetPhysics()); +} + +void PvdPhysicsClient::destroyPvdInstance(const PxConvexMesh* convexMesh) +{ + mMetaDataBinding.destroyInstance(*mPvdDataStream, *convexMesh, PxGetPhysics()); +} + +void PvdPhysicsClient::createPvdInstance(const PxHeightField* heightField) +{ + mMetaDataBinding.createInstance(*mPvdDataStream, *heightField, PxGetPhysics()); +} + +void PvdPhysicsClient::destroyPvdInstance(const PxHeightField* heightField) +{ + mMetaDataBinding.destroyInstance(*mPvdDataStream, *heightField, PxGetPhysics()); +} + +void PvdPhysicsClient::createPvdInstance(const PxMaterial* mat) +{ + mMetaDataBinding.createInstance(*mPvdDataStream, *mat, PxGetPhysics()); +} + +void PvdPhysicsClient::updatePvdProperties(const PxMaterial* mat) +{ + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *mat); +} + +void PvdPhysicsClient::destroyPvdInstance(const PxMaterial* mat) +{ + mMetaDataBinding.destroyInstance(*mPvdDataStream, *mat, PxGetPhysics()); +} + +void PvdPhysicsClient::onGuMeshFactoryBufferRelease(const PxBase* object, PxType typeID) +{ + if(!mIsConnected || !mPvd) + return; + + if(mPvd->unRegisterObject(object)) + { + switch(typeID) + { + case PxConcreteType::eHEIGHTFIELD: + destroyPvdInstance(static_cast(object)); + break; + + case PxConcreteType::eCONVEX_MESH: + destroyPvdInstance(static_cast(object)); + break; + + case PxConcreteType::eTRIANGLE_MESH_BVH33: + case PxConcreteType::eTRIANGLE_MESH_BVH34: + destroyPvdInstance(static_cast(object)); + break; + + default: + break; + } + } +} + +void PvdPhysicsClient::reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) +{ + if(mIsConnected) + { + mPvdDataStream->sendErrorMessage(code, message, file, PxU32(line)); + } +} + +#endif // PX_SUPPORT_PVD diff --git a/src/PhysX/physx/source/physx/src/PvdPhysicsClient.h b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.h new file mode 100644 index 000000000..f4704b715 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PVD_PHYSICS_CLIENT_H +#define PVD_PHYSICS_CLIENT_H +#if PX_SUPPORT_PVD +#include "foundation/PxErrorCallback.h" +#include "PxPvdClient.h" +#include "PvdMetaDataPvdBinding.h" +#include "NpFactory.h" +#include "PsHashMap.h" +#include "PsMutex.h" +#include "PsPvd.h" + +namespace physx +{ +class PxProfileMemoryEventBuffer; + +namespace Vd +{ + +class PvdPhysicsClient : public PvdClient, public PxErrorCallback, public NpFactoryListener, public shdfnd::UserAllocated +{ + PX_NOCOPY(PvdPhysicsClient) + public: + PvdPhysicsClient(PsPvd* pvd); + virtual ~PvdPhysicsClient(); + + bool isConnected() const; + void onPvdConnected(); + void onPvdDisconnected(); + void flush(); + + physx::pvdsdk::PvdDataStream* getDataStream(); + PvdMetaDataBinding* getMetaDataBinding(); + PvdUserRenderer* getUserRender(); + + void sendEntireSDK(); + void destroyPvdInstance(const PxPhysics* physics); + + // NpFactoryListener + virtual void onGuMeshFactoryBufferRelease(const PxBase* object, PxType typeID); + /// NpFactoryListener + + // PxErrorCallback + void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line); + + private: + void createPvdInstance(const PxTriangleMesh* triMesh); + void destroyPvdInstance(const PxTriangleMesh* triMesh); + void createPvdInstance(const PxConvexMesh* convexMesh); + void destroyPvdInstance(const PxConvexMesh* convexMesh); + void createPvdInstance(const PxHeightField* heightField); + void destroyPvdInstance(const PxHeightField* heightField); + void createPvdInstance(const PxMaterial* mat); + void destroyPvdInstance(const PxMaterial* mat); + void updatePvdProperties(const PxMaterial* mat); + + PsPvd* mPvd; + PvdDataStream* mPvdDataStream; + PvdMetaDataBinding mMetaDataBinding; + bool mIsConnected; +}; + +} // namespace Vd +} // namespace physx + +#endif // PX_SUPPORT_PVD +#endif // PVD_PHYSICS_CLIENT_H diff --git a/src/PhysX/physx/source/physx/src/PvdTypeNames.h b/src/PhysX/physx/source/physx/src/PvdTypeNames.h new file mode 100644 index 000000000..d7a3b82f6 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/PvdTypeNames.h @@ -0,0 +1,174 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PVD_TYPE_NAMES_H +#define PVD_TYPE_NAMES_H +#if PX_SUPPORT_PVD +#include "PxPvdObjectModelBaseTypes.h" +#include "PxMetaDataObjects.h" +#include "PxHeightFieldSample.h" + +namespace physx +{ +namespace Vd +{ +struct PvdSqHit; +struct PvdRaycast; +struct PvdOverlap; +struct PvdSweep; + +struct PvdHullPolygonData +{ + PxU16 mNumVertices; + PxU16 mIndexBase; + PvdHullPolygonData(PxU16 numVert, PxU16 idxBase) : mNumVertices(numVert), mIndexBase(idxBase) + { + } +}; + + +struct PxArticulationLinkUpdateBlock +{ + PxTransform GlobalPose; + PxVec3 LinearVelocity; + PxVec3 AngularVelocity; +}; +struct PxRigidDynamicUpdateBlock : public PxArticulationLinkUpdateBlock +{ + bool IsSleeping; +}; + +struct PvdContact +{ + PxVec3 point; + PxVec3 axis; + const void* shape0; + const void* shape1; + PxF32 separation; + PxF32 normalForce; + PxU32 internalFaceIndex0; + PxU32 internalFaceIndex1; + bool normalForceAvailable; +}; + +struct PvdPositionAndRadius +{ + PxVec3 position; + PxF32 radius; +}; + +} //Vd +} //physx + +namespace physx +{ +namespace pvdsdk +{ + +#define DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(type) DEFINE_PVD_TYPE_NAME_MAP(physx::type, "physx3", #type) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPhysics) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxScene) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxTolerancesScale) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxTolerancesScaleGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSceneDescGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSceneDesc) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSimulationStatistics) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSimulationStatisticsGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxMaterial) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxMaterialGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightField) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightFieldDesc) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightFieldDescGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxTriangleMesh) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxConvexMesh) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxActor) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidActor) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidBody) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidDynamic) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidDynamicGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidStatic) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRigidStaticGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxShape) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxShapeGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxBoxGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPlaneGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxCapsuleGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSphereGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightFieldGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxTriangleMeshGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxConvexMeshGeometry) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxBoxGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPlaneGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxCapsuleGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSphereGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightFieldGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxTriangleMeshGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxConvexMeshGeometryGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxHeightFieldSample) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxConstraint) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxConstraintGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationBase) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationBaseGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulation) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationLink) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationLinkGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationJointBase) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationJointBaseGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationJoint) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationJointGeneratedValues) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxArticulationJointReducedCoordinate) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxAggregate) +DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxAggregateGeneratedValues) + +#undef DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP + +#define DEFINE_NATIVE_PVD_TYPE_MAP(type) DEFINE_PVD_TYPE_NAME_MAP(physx::Vd::type, "physx3", #type) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdHullPolygonData) +DEFINE_NATIVE_PVD_TYPE_MAP(PxRigidDynamicUpdateBlock) +DEFINE_NATIVE_PVD_TYPE_MAP(PxArticulationLinkUpdateBlock) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdContact) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdRaycast) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdSweep) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdOverlap) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdSqHit) +DEFINE_NATIVE_PVD_TYPE_MAP(PvdPositionAndRadius) + +#undef DEFINE_NATIVE_PVD_TYPE_MAP + + +DEFINE_PVD_TYPE_ALIAS(physx::PxFilterData, U32Array4) + + +} +} + +#endif + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp new file mode 100644 index 000000000..67a921c11 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp @@ -0,0 +1,53 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScbBase.h" + +using namespace physx; +using namespace Scb; + +#include "ScbActor.h" +#include "ScbRigidStatic.h" +#include "ScbBody.h" + +Actor::Offsets::Offsets() +{ + const size_t staticOffset = reinterpret_cast(&(reinterpret_cast(0)->getScStatic())); + const size_t bodyOffset = reinterpret_cast(&(reinterpret_cast(0)->getScBody())); + + scToScb[PxActorType::eRIGID_STATIC] = staticOffset; + scToScb[PxActorType::eRIGID_DYNAMIC] = bodyOffset; + scToScb[PxActorType::eARTICULATION_LINK] = bodyOffset; + + scbToSc[ScbType::eRIGID_STATIC] = staticOffset; + scbToSc[ScbType::eBODY] = bodyOffset; + scbToSc[ScbType::eBODY_FROM_ARTICULATION_LINK] = bodyOffset; +} + +const Actor::Offsets Actor::sOffsets; diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbActor.h b/src/PhysX/physx/source/physx/src/buffering/ScbActor.h new file mode 100644 index 000000000..bba2c17f5 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbActor.h @@ -0,0 +1,207 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_FSACTOR +#define PX_PHYSICS_SCB_FSACTOR + +#include "ScActorCore.h" +#include "PsUtilities.h" +#include "ScbBase.h" +#include "ScbDefs.h" + +#include "PxClient.h" + + +namespace physx +{ +namespace Scb +{ +struct ActorBuffer +{ +#ifdef USE_NEW_SYSTEM + PxActorFlags mActorFlags; + PxDominanceGroup mDominanceGroup; +// PxActorClientBehaviorFlags mClientBehaviorFlags; +#else + template struct Fns {}; + typedef Sc::ActorCore Core; + typedef ActorBuffer Buf; + + SCB_REGULAR_ATTRIBUTE (0, PxActorFlags, ActorFlags) + SCB_REGULAR_ATTRIBUTE (1, PxDominanceGroup, DominanceGroup) +// SCB_REGULAR_ATTRIBUTE (2, PxActorClientBehaviorFlags, ClientBehaviorFlags) +#endif + enum { AttrCount = 3 }; + +protected: + ~ActorBuffer(){} +}; + +class Actor : public Base +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef ActorBuffer Buf; + typedef Sc::ActorCore Core; + +public: +// PX_SERIALIZATION + Actor(const PxEMPTY) : Base(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PX_INLINE Actor() {} + +#ifdef USE_NEW_SYSTEM + SCB_MEMBER(Actor, getActorCore(), ActorFlags, PxActorFlags, 0) + SCB_MEMBER(Actor, getActorCore(), DominanceGroup, PxDominanceGroup, 1) +// SCB_MEMBER(Actor, getActorCore(), ClientBehaviorFlags, PxActorClientBehaviorFlags, 2) +#endif + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::Actor interface + //--------------------------------------------------------------------------------- +#ifndef USE_NEW_SYSTEM + PX_INLINE PxActorFlags getActorFlags() const { return read(); } + PX_INLINE void setActorFlags(PxActorFlags v); + + PX_INLINE PxDominanceGroup getDominanceGroup() const { return read();} + PX_INLINE void setDominanceGroup(PxDominanceGroup v) { write(v); } + +// PX_INLINE PxActorClientBehaviorFlags getClientBehaviorFlags() const { return read(); } +// PX_INLINE void setClientBehaviorFlags(PxActorClientBehaviorFlags v){ write(v); } +#endif + + PX_INLINE void setOwnerClient(PxClientID inId); + PX_INLINE PxClientID getOwnerClient() const { return getActorCore().getOwnerClient(); } //immutable, so this should be fine. + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE const Core& getActorCore() const { return *reinterpret_cast(reinterpret_cast(this) + sOffsets.scbToSc[getScbType()]); } + PX_FORCE_INLINE Core& getActorCore() { return *reinterpret_cast(reinterpret_cast(this) + sOffsets.scbToSc[getScbType()]); } + + PX_FORCE_INLINE static const Actor& fromSc(const Core& a) { return *reinterpret_cast(reinterpret_cast(&a) - sOffsets.scToScb[a.getActorCoreType()]); } + PX_FORCE_INLINE static Actor& fromSc(Core &a) { return *reinterpret_cast(reinterpret_cast(&a) - sOffsets.scToScb[a.getActorCoreType()]); } + + PX_FORCE_INLINE PxActorType::Enum getActorType() const { return getActorCore().getActorCoreType(); } + +protected: + ~Actor() {} + PX_INLINE void syncState(); + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + struct Access: public BufferedAccess {}; + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, getActorCore()); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, getActorCore(), v); } + template PX_FORCE_INLINE void flush(Core& core, const Buf& buf) { Access::flush >(*this, core, buf); } +#endif + + struct Offsets + { + size_t scToScb[PxActorType::eACTOR_COUNT]; + size_t scbToSc[ScbType::eTYPE_COUNT]; + Offsets(); + }; + static const Offsets sOffsets; +}; + +#ifndef USE_NEW_SYSTEM +PX_INLINE void Actor::setActorFlags(PxActorFlags v) +{ + // PT: TODO: move this check out of here, they should be done in Np! +#if PX_CHECKED + const PxActorFlags aFlags = getActorFlags(); + const PxActorType::Enum aType = getActorType(); + if((!aFlags.isSet(PxActorFlag::eDISABLE_SIMULATION)) && v.isSet(PxActorFlag::eDISABLE_SIMULATION) && + (aType != PxActorType::eRIGID_DYNAMIC) && (aType != PxActorType::eRIGID_STATIC)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxActor::setActorFlag: PxActorFlag::eDISABLE_SIMULATION is only supported by PxRigidDynamic and PxRigidStatic objects."); + } +#endif + + write(v); +} +#endif + +PX_INLINE void Actor::setOwnerClient(PxClientID inId) +{ + //This call is only valid if we aren't in a scene. + //Thus we can't be buffering yet + if(!isBuffering()) + { + getActorCore().setOwnerClient(inId); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Attempt to set the client id when an actor is buffering"); + } +} + +PX_INLINE void Actor::syncState() +{ + //this should be called from syncState() of derived classes + + const PxU32 flags = getBufferFlags(); +#ifdef USE_NEW_SYSTEM + if(flags & (BF_ActorFlags|BF_DominanceGroup/*|BF_ClientBehaviorFlags*/)) + { + syncActorFlags(); + syncDominanceGroup(); +// syncClientBehaviorFlags(); + } +#else + if(flags & (Buf::BF_ActorFlags|Buf::BF_DominanceGroup/*|Buf::BF_ClientBehaviorFlags*/)) + { + Core& core = getActorCore(); + const Buf& buffer = *reinterpret_cast(getStream()); + + flush(core, buffer); + flush(core, buffer); +// flush(core, buffer); + } +#endif +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp new file mode 100644 index 000000000..85dd5edf2 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp @@ -0,0 +1,139 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScbAggregate.h" +#include "ScbActor.h" + +using namespace physx; + +void Scb::Aggregate::addActor(Scb::Actor& actor) +{ + const ControlState::Enum state = getControlState(); + + if(!isBufferingSpecial(state)) + { + actor.getActorCore().setAggregateID(mAggregateID); + PvdAttachActorToAggregate( this, &actor ); + PvdUpdateProperties( this ); + } + else if((state != ControlState::eREMOVE_PENDING)) // If the aggregate is pending for deletion, adding/removing an actor should not be double buffered because the aggregateID must not be set for the actors + { + // if available, search in list of removed actors to cover the remove-add case + Scb::AggregateBuffer* PX_RESTRICT bufferedData = getBufferedData(); + if(bufferedData->removeBufferIdx != 0xffffffff) + { + Scb::Actor** removeBuffer = getScbScene()->getActorBuffer(bufferedData->removeBufferIdx); + for(PxU32 i=0; iremoveCount; i++) + { + if(removeBuffer[i] == &actor) + { + removeBuffer[i] = removeBuffer[bufferedData->removeCount - 1]; + PX_ASSERT(bufferedData->removeCount > 0); + bufferedData->removeCount--; + break; + } + } + } + + Scb::Actor** actorBuffer; + if(bufferedData->addBufferIdx == 0xffffffff) + actorBuffer = getScbScene()->allocActorBuffer(mMaxNbActors, bufferedData->addBufferIdx); + else + actorBuffer = getScbScene()->getActorBuffer(bufferedData->addBufferIdx); + + PX_ASSERT(bufferedData->addCount < mMaxNbActors); + actorBuffer[bufferedData->addCount] = &actor; + bufferedData->addCount++; + + if(state != ControlState::eINSERT_PENDING) + markUpdated(BF_ADD_ACTOR); + else + { + // Not a great solution but aggregates are special in the sense that even in the pending insert case, data needs to be double buffered + // (see isBufferingSpecial() for details) + setBufferFlag(BF_ADD_ACTOR); + } + } +} + +void Scb::Aggregate::removeActor(Scb::Actor& actor, bool reinsert) +{ + const ControlState::Enum state = getControlState(); + const ControlState::Enum actorState = actor.getControlState(); + + if(!isBufferingSpecial(state)) + { + Sc::ActorCore& ac = actor.getActorCore(); + ac.setAggregateID(PX_INVALID_U32); + + if(getScbSceneForAPI() && reinsert) + ac.reinsertShapes(); + } + else if((state != ControlState::eREMOVE_PENDING)) + { + // if available, search in list of added actors to cover the add-remove case + Scb::AggregateBuffer* PX_RESTRICT bufferedData = getBufferedData(); + if(bufferedData->addBufferIdx != 0xffffffff) + { + Scb::Actor** addBuffer = getScbScene()->getActorBuffer(bufferedData->addBufferIdx); + for(PxU32 i=0; iaddCount; i++) + { + if(addBuffer[i] == &actor) + { + addBuffer[i] = addBuffer[bufferedData->addCount - 1]; + PX_ASSERT(bufferedData->addCount > 0); + bufferedData->addCount--; + return; // It's fine to abort here because the aggregateID has not been set yet + } + } + } + + Scb::Actor** actorBuffer; + if(bufferedData->removeBufferIdx == 0xffffffff) + actorBuffer = getScbScene()->allocActorBuffer(mMaxNbActors, bufferedData->removeBufferIdx); + else + actorBuffer = getScbScene()->getActorBuffer(bufferedData->removeBufferIdx); + + PX_ASSERT(bufferedData->removeCount < mMaxNbActors); + actorBuffer[bufferedData->removeCount] = &actor; + bufferedData->removeCount++; + + markUpdated(BF_REMOVE_ACTOR); + } + + //Update pvd status if not buffer OR the actor release while aggregate is already in scene + if(!isBufferingSpecial(state) + || ((actorState == ControlState::eIN_SCENE || actorState == ControlState::eREMOVE_PENDING ) + && state != ControlState::eINSERT_PENDING + && !reinsert)) + { + PvdDetachActorFromAggregate( this, &actor ); + PvdUpdateProperties( this ); + } +} diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h new file mode 100644 index 000000000..28cf591cd --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h @@ -0,0 +1,238 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_AGGREGATE +#define PX_PHYSICS_SCB_AGGREGATE + +#include "CmPhysXCommon.h" +#include "PxAggregate.h" +#include "ScbActor.h" +#include "ScbAggregate.h" +#include "ScbBase.h" + +// PX_SERIALIZATION +#include "PxSerialFramework.h" +//~PX_SERIALIZATION + +namespace physx +{ +namespace Scb +{ + +class Actor; + +struct AggregateBuffer +{ + AggregateBuffer() : addBufferIdx(0xffffffff), addCount(0), removeBufferIdx(0xffffffff), removeCount(0) {} + + PxU32 addBufferIdx; + PxU32 addCount; + PxU32 removeBufferIdx; + PxU32 removeCount; +}; + +class Aggregate : public Base +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +private: + enum BufferFlag + { + BF_ADD_ACTOR = (1 << 0), + BF_REMOVE_ACTOR = (1 << 1) + }; + +public: + +// PX_SERIALIZATION + Aggregate(const PxEMPTY) : Base(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + PX_INLINE Aggregate(PxAggregate* px, PxU32 maxActors, bool selfCollision); + PX_INLINE ~Aggregate(); + + void addActor(Scb::Actor&); + void removeActor(Scb::Actor& actor, bool reinsert); + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(Scb::Scene& scene); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE PxU32 getMaxActorCount() const { return mMaxNbActors; } + PX_FORCE_INLINE bool getSelfCollide() const { return mSelfCollide; } + PX_FORCE_INLINE PxU32 getAggregateID() const { return mAggregateID; } + PX_FORCE_INLINE void setAggregateID(PxU32 cid) { mAggregateID = cid; } + + PX_FORCE_INLINE bool isBufferingSpecial(ControlState::Enum state) const; + + PxAggregate* mPxAggregate; // Back pointer +private: + PxU32 mAggregateID; + PxU32 mMaxNbActors; + bool mSelfCollide; + + PX_FORCE_INLINE const Scb::AggregateBuffer* getBufferedData() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Scb::AggregateBuffer* getBufferedData() { return reinterpret_cast(getStream()); } +}; + +PX_INLINE Aggregate::Aggregate(PxAggregate* px, PxU32 maxActors, bool selfCollision) : + mPxAggregate (px), + mAggregateID (PX_INVALID_U32), + mMaxNbActors (maxActors), + mSelfCollide (selfCollision) +{ + setScbType(ScbType::eAGGREGATE); +} + +PX_INLINE Aggregate::~Aggregate() +{ +} + +PX_FORCE_INLINE bool Aggregate::isBufferingSpecial(ControlState::Enum state) const +{ + // A special version of the buffer check is needed for aggregates because it is not fine for adding/removing + // an actor to be not double buffered if the aggregate is pending for insertion. + // For example: Adding an actor can not be processed if the aggregate is pending for insertion because the aggregateID + // is not yet available (an there is no Sc::Aggregate object to store the actors) + + Scb::Scene* scbScene = getScbSceneForAPI(); + return state == ControlState::eREMOVE_PENDING || // pending remove not possible if not buffered + (scbScene && scbScene->isPhysicsBuffering()); +} + +//-------------------------------------------------------------- +// +// PVD Events +// +//-------------------------------------------------------------- + +namespace +{ +#if PX_SUPPORT_PVD + PX_FORCE_INLINE void PvdAttachActorToAggregate(Scb::Aggregate* pAggregate, Scb::Actor* pScbActor) + { + Scb::Scene* scbScene = pAggregate->getScbSceneForAPI(); + if(scbScene/* && scbScene->getScenePvdClient().isInstanceValid(pAggregate)*/) + scbScene->getScenePvdClient().attachAggregateActor( pAggregate, pScbActor ); + } + + PX_FORCE_INLINE void PvdDetachActorFromAggregate(Scb::Aggregate* pAggregate, Scb::Actor* pScbActor) + { + Scb::Scene* scbScene = pAggregate->getScbSceneForAPI(); + if(scbScene/*&& scbScene->getScenePvdClient().isInstanceValid(pAggregate)*/) + scbScene->getScenePvdClient().detachAggregateActor( pAggregate, pScbActor ); + } + + PX_FORCE_INLINE void PvdUpdateProperties(Scb::Aggregate* pAggregate) + { + Scb::Scene* scbScene = pAggregate->getScbSceneForAPI(); + if(scbScene /*&& scbScene->getScenePvdClient().isInstanceValid(pAggregate)*/) + scbScene->getScenePvdClient().updatePvdProperties( pAggregate ); + } +#else +#define PvdAttachActorToAggregate(aggregate, scbActor) {} +#define PvdDetachActorFromAggregate(aggregate, scbActor) {} +#define PvdUpdateProperties(aggregate) {} +#endif +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_INLINE void Aggregate::syncState(Scb::Scene& scene) +{ + const PxU32 flags = getBufferFlags(); + + enum AggregateSyncDirtyType + { + DIRTY_NONE = 0, + DIRTY_ADD_ACTOR = 1<<0, + DIRTY_REMOVE_ACTOR = 1<<1 + }; + + PxU32 dirtyType = DIRTY_NONE; + + if(flags) + { + const Scb::AggregateBuffer* PX_RESTRICT bufferedData = getBufferedData(); + + if(flags & BF_ADD_ACTOR) + { + dirtyType |= DIRTY_ADD_ACTOR; + + Scb::Actor* const* actorBuffer = scene.getActorBuffer(bufferedData->addBufferIdx); + + PX_ASSERT(mAggregateID != PX_INVALID_U32); + for(PxU32 i=0; iaddCount; i++) + { + actorBuffer[i]->getActorCore().setAggregateID(mAggregateID); + PvdAttachActorToAggregate( this, actorBuffer[i] ); + } + } + + if(flags & BF_REMOVE_ACTOR) + { + dirtyType |= DIRTY_REMOVE_ACTOR; + Scb::Actor* const* actorBuffer = scene.getActorBuffer(bufferedData->removeBufferIdx); + + for(PxU32 i=0; iremoveCount; i++) + { + const ControlState::Enum state = actorBuffer[i]->getControlState(); + + Sc::ActorCore& ac = actorBuffer[i]->getActorCore(); + ac.setAggregateID(PX_INVALID_U32); + if(state != ControlState::eREMOVE_PENDING) + PvdDetachActorFromAggregate( this, actorBuffer[i] ); + if(state == ControlState::eINSERT_PENDING || state == ControlState::eIN_SCENE) + ac.reinsertShapes(); + } + } + + if(dirtyType != DIRTY_NONE) + PvdUpdateProperties( this ); + } + + postSyncState(); +} + +}// namespace Scb +}// namespace physx + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h b/src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h new file mode 100644 index 000000000..3d1a13089 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h @@ -0,0 +1,414 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_ARTICULATION +#define PX_PHYSICS_SCB_ARTICULATION + +#include "ScArticulationCore.h" +#include "ScbBase.h" +#include "ScbDefs.h" + +namespace physx +{ +namespace Scb +{ + +struct ArticulationBuffer +{ +#ifdef USE_NEW_SYSTEM + PxU32 mInternalDriveIterations; + PxU32 mExternalDriveIterations; + PxU32 mMaxProjectionIterations; + PxReal mSeparationTolerance; + PxReal mSleepThreshold; + PxU16 mSolverIterationCounts; + PxReal mFreezeThreshold; +#else + template struct Fns {}; + typedef Sc::ArticulationCore Core; + typedef ArticulationBuffer Buf; + + SCB_REGULAR_ATTRIBUTE(0, PxU32, InternalDriveIterations) + SCB_REGULAR_ATTRIBUTE(1, PxU32, ExternalDriveIterations) + SCB_REGULAR_ATTRIBUTE(2, PxU32, MaxProjectionIterations) + SCB_REGULAR_ATTRIBUTE(3, PxReal, SeparationTolerance) + SCB_REGULAR_ATTRIBUTE(4, PxReal, SleepThreshold) + SCB_REGULAR_ATTRIBUTE(5, PxU16, SolverIterationCounts) + SCB_REGULAR_ATTRIBUTE(6, PxReal, FreezeThreshold) +#endif + enum { BF_WakeCounter = 1<<7 }; + enum { BF_PutToSleep = 1<<8 }; + enum { BF_WakeUp = 1<<9 }; + enum { BF_ArticulationFlags = 1 << 19 }; + enum { BF_ArticulationPoseUpdate = 1 << 11 }; + +}; + +class Articulation : public Base +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef ArticulationBuffer Buf; + typedef Sc::ArticulationCore Core; + +// PX_SERIALIZATION +public: + Articulation(const PxEMPTY) : Base(PxEmpty), mArticulation(PxEmpty), mBufferedIsSleeping(1) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + +private: + +public: + PX_INLINE Articulation(); + PX_INLINE ~Articulation() {} + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::Articulation interface + //--------------------------------------------------------------------------------- + +#ifdef USE_NEW_SYSTEM + SCB_MEMBER(Articulation, mArticulation, InternalDriveIterations, PxU32, 0) + SCB_MEMBER(Articulation, mArticulation, ExternalDriveIterations, PxU32, 1) + SCB_MEMBER(Articulation, mArticulation, MaxProjectionIterations, PxU32, 2) + SCB_MEMBER(Articulation, mArticulation, SeparationTolerance, PxReal, 3) + SCB_MEMBER(Articulation, mArticulation, SleepThreshold, PxReal, 4) + SCB_MEMBER(Articulation, mArticulation, SolverIterationCounts, PxU16, 5) + SCB_MEMBER(Articulation, mArticulation, FreezeThreshold, PxReal, 6) +#else + PX_INLINE PxU32 getInternalDriveIterations() const { return read(); } + PX_INLINE void setInternalDriveIterations(const PxU32 v) { write(v); } + + PX_INLINE PxU32 getExternalDriveIterations() const { return read(); } + PX_INLINE void setExternalDriveIterations(const PxU32 v) { write(v); } + + PX_INLINE PxU32 getMaxProjectionIterations() const { return read(); } + PX_INLINE void setMaxProjectionIterations(const PxU32 v) { write(v); } + + PX_INLINE PxU16 getSolverIterationCounts() const { return read(); } + PX_INLINE void setSolverIterationCounts(PxU16 v) { write(v); } + + PX_INLINE PxReal getSeparationTolerance() const { return read(); } + PX_INLINE void setSeparationTolerance(const PxReal v) { write(v); } + + PX_INLINE PxReal getSleepThreshold() const { return read(); } + PX_INLINE void setSleepThreshold(const PxReal v) { write(v); } + + PX_INLINE PxReal getFreezeThreshold() const { return read(); } + PX_INLINE void setFreezeThreshold(const PxReal v) { write(v); } +#endif + PX_INLINE PxReal getWakeCounter() const { return mBufferedWakeCounter; } + PX_INLINE void setWakeCounter(const PxReal v); + + PX_INLINE PxArticulationFlags getArticulationFlags() const { return mBufferedArticulationFlags; } + PX_INLINE void setArticulationFlags(PxArticulationFlags flags); + + PX_INLINE PxArticulationBase::Enum getArticulationType() const { return mArticulation.getArticulationType(); } + PX_INLINE void setArticulationType(PxArticulationBase::Enum type) { mArticulation.setArticulationType(type); } + + PX_FORCE_INLINE void wakeUp(); + PX_FORCE_INLINE void putToSleep(); + PX_FORCE_INLINE bool isSleeping() const { return (mBufferedIsSleeping != 0); } + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(); + + PX_FORCE_INLINE const Sc::ArticulationCore& getScArticulation() const { return mArticulation; } // Only use if you know what you're doing! + PX_FORCE_INLINE Sc::ArticulationCore& getScArticulation() { return mArticulation; } // Only use if you know what you're doing! + + PX_FORCE_INLINE static Articulation& fromSc(Core &a) { return *reinterpret_cast(reinterpret_cast(&a)-getScOffset()); } + PX_FORCE_INLINE static const Articulation& fromSc(const Core &a) { return *reinterpret_cast(reinterpret_cast(&a)-getScOffset()); } + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mArticulation); } + + PX_FORCE_INLINE void wakeUpInternal(PxReal wakeCounter); + + PX_FORCE_INLINE void setGlobalPose(); + + PX_FORCE_INLINE void initBufferedState(); + PX_FORCE_INLINE void clearBufferedState(); + PX_FORCE_INLINE void clearBufferedSleepStateChange(); + +private: + Sc::ArticulationCore mArticulation; + PxReal mBufferedWakeCounter; + PxU8 mBufferedIsSleeping; + PxArticulationFlags mBufferedArticulationFlags; + + PX_FORCE_INLINE const Buf* getArticulationBuffer() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Buf* getArticulationBuffer() { return reinterpret_cast(getStream()); } + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess {}; + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, mArticulation); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, mArticulation, v); } + template PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush >(*this, mArticulation, buf); } +#endif +}; + +Articulation::Articulation() +{ + setScbType(ScbType::eARTICULATION); + mBufferedWakeCounter = mArticulation.getWakeCounter(); + mBufferedIsSleeping = 1; // this is the specified value for free standing objects +} + +PX_INLINE void Articulation::setWakeCounter(PxReal counter) +{ + mBufferedWakeCounter = counter; + + if(!isBuffering()) + { + if(getScbScene() && (counter > 0.0f)) + mBufferedIsSleeping = 0; + + mArticulation.setWakeCounter(counter); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if(counter > 0.0f) + { + mBufferedIsSleeping = 0; + markUpdated(Buf::BF_WakeUp | Buf::BF_WakeCounter); + resetBufferFlag(Buf::BF_PutToSleep); + } + else + markUpdated(Buf::BF_WakeCounter); + } +} + +PX_FORCE_INLINE void Articulation::setArticulationFlags(PxArticulationFlags flags) +{ + mBufferedArticulationFlags = flags; + if (!isBuffering()) + { + mArticulation.setArticulationFlags(flags); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + markUpdated(Buf::BF_ArticulationFlags); + } +} + +PX_FORCE_INLINE void Articulation::wakeUp() +{ + Scene* scene = getScbScene(); + PX_ASSERT(scene); // only allowed for an object in a scene + + wakeUpInternal(scene->getWakeCounterResetValue()); +} + +PX_FORCE_INLINE void Articulation::wakeUpInternal(PxReal wakeCounter) +{ + PX_ASSERT(getScbScene()); + + mBufferedWakeCounter = wakeCounter; + + mBufferedIsSleeping = 0; + if(!isBuffering()) + { + mArticulation.wakeUp(wakeCounter); + } + else + { + markUpdated(Buf::BF_WakeUp | Buf::BF_WakeCounter); + resetBufferFlag(Buf::BF_PutToSleep); + } +} + +PX_INLINE void Articulation::setGlobalPose() +{ + if (!isBuffering()) + { + mArticulation.setGlobalPose(); + } + else + { + markUpdated(Buf::BF_ArticulationPoseUpdate); + } +} + +PX_FORCE_INLINE void Articulation::putToSleep() +{ + mBufferedWakeCounter = 0.0f; + + mBufferedIsSleeping = 1; + if(!isBuffering()) + { + mArticulation.putToSleep(); + } + else + { + markUpdated(Buf::BF_PutToSleep | Buf::BF_WakeCounter); + resetBufferFlag(Buf::BF_WakeUp); + } +} + +PX_FORCE_INLINE void Articulation::initBufferedState() +{ + PX_ASSERT(mBufferedIsSleeping); // this method is only meant to get called when an object is added to the scene + + if(getWakeCounter() == 0.0f) + mBufferedIsSleeping = 1; + else + mBufferedIsSleeping = 0; + // note: to really know whether the articulation is awake/asleep on insertion, we need to go through every link and check whether any of them has + // a parameter setup that keeps it alive. However, the links get added after the articulation, so we do not test those here. After the links + // are added, an additional check will wake the articulation up if necessary. +} + +PX_FORCE_INLINE void Articulation::clearBufferedState() +{ + mBufferedIsSleeping = 1; // the expected state when an object gets removed from the scene +} + +PX_FORCE_INLINE void Articulation::clearBufferedSleepStateChange() +{ + resetBufferFlag(Buf::BF_WakeUp | Buf::BF_PutToSleep); +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_INLINE void Articulation::syncState() +{ + // see comments in Body::syncState + PX_ASSERT( (getControlState() != ControlState::eREMOVE_PENDING) || + (mBufferedIsSleeping && (!isBuffered(Buf::BF_WakeUp | Buf::BF_PutToSleep))) ); + + const PxU32 flags = getBufferFlags(); + + //---- + + if((flags & Buf::BF_WakeCounter) == 0) + mBufferedWakeCounter = mArticulation.getWakeCounter(); + else if (!(flags & (Buf::BF_WakeUp | Buf::BF_PutToSleep))) // if there has been at least one buffered sleep state transition, then there is no use in adjusting the wake counter separately because it will + // get done in the sleep state update. + { + PX_ASSERT(mBufferedWakeCounter == 0.0f); // a wake counter change is always connected to a sleep state change, except one case: if setWakeCounter(0.0f) was called + + mArticulation.setWakeCounter(mBufferedWakeCounter); + } + + //---- + + if((flags & (Buf::BF_WakeUp | Buf::BF_PutToSleep)) == 0) + { + const bool isSimObjectSleeping = mArticulation.isSleeping(); + if(getControlState() != ControlState::eREMOVE_PENDING) // we do not want the simulation sleep state to take effect if the object was removed (free standing objects have buffered state sleeping) + mBufferedIsSleeping = PxU8(isSimObjectSleeping); + else + PX_ASSERT(mBufferedIsSleeping); // this must get set immediately at remove + } + else + { + PX_ASSERT(flags & Buf::BF_WakeCounter); // sleep state transitions always mark the wake counter dirty + PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing an object should clear pending wakeUp/putToSleep operations since the state for a free standing object gets set according to specification. + + if(flags & Buf::BF_PutToSleep) + { + PX_ASSERT(mBufferedIsSleeping); + PX_ASSERT(!(flags & Buf::BF_WakeUp)); + PX_ASSERT(mBufferedWakeCounter == 0.0f); + mArticulation.putToSleep(); + } + else + { + PX_ASSERT(!mBufferedIsSleeping); + PX_ASSERT(flags & Buf::BF_WakeUp); + + mArticulation.wakeUp(mBufferedWakeCounter); + } + } + + //---- + + if(flags&~(Buf::BF_WakeCounter|Buf::BF_WakeUp|Buf::BF_PutToSleep)) // Optimization to avoid all the if-statements below if possible + { +#ifdef USE_NEW_SYSTEM + syncExternalDriveIterations(); + syncInternalDriveIterations(); + syncMaxProjectionIterations(); + syncSeparationTolerance(); + syncSleepThreshold(); + syncSolverIterationCounts(); + syncFreezeThreshold(); +#else + const Buf* PX_RESTRICT buffer = getArticulationBuffer(); + + flush(*buffer); + flush(*buffer); + flush(*buffer); + flush(*buffer); + flush(*buffer); + flush(*buffer); + flush(*buffer); +#endif + } + + //---- + if (flags&(Buf::BF_ArticulationPoseUpdate)) + { + //the root link global pose has been changed. We need to update + //other links's global pose + mArticulation.setGlobalPose(); + } + + // -------------- + // postconditions + // + PX_ASSERT((getControlState() != ControlState::eREMOVE_PENDING) || mBufferedIsSleeping); // nothing in this method should change this + // + // postconditions + // -------------- + + postSyncState(); +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h b/src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h new file mode 100644 index 000000000..67e743665 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h @@ -0,0 +1,578 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_ARTICULATION_JOINT +#define PX_PHYSICS_SCB_ARTICULATION_JOINT + +#include "ScArticulationJointCore.h" +#include "ScbArticulation.h" +#include "ScbBody.h" +#include "ScbBase.h" +#include "ScbDefs.h" + +namespace physx +{ +namespace Scb +{ + +struct ArticulationJointBuffer +{ +#ifdef USE_NEW_SYSTEM + PxTransform mParentPose; + PxTransform mChildPose; + PxQuat mTargetOrientation; + PxVec3 mTargetVelocity; + PxReal mStiffness; + PxReal mDamping; + PxReal mInternalCompliance; + PxReal mExternalCompliance; + PxReal mSwingLimitContactDistance; + bool mSwingLimitEnabled; + PxReal mTangentialStiffness; + PxReal mTangentialDamping; + PxReal mTwistLimitContactDistance; + bool mTwistLimitEnabled; + PxArticulationJointDriveType::Enum mDriveType; +#else + template struct Fns {}; + typedef Sc::ArticulationJointCore Core; + typedef ArticulationJointBuffer Buf; + + SCB_REGULAR_ATTRIBUTE(0, PxTransform, ParentPose) + SCB_REGULAR_ATTRIBUTE(1, PxTransform, ChildPose) + SCB_REGULAR_ATTRIBUTE(2, PxQuat, TargetOrientation) + SCB_REGULAR_ATTRIBUTE(3, PxVec3, TargetVelocity) + SCB_REGULAR_ATTRIBUTE(4, PxReal, Stiffness) + SCB_REGULAR_ATTRIBUTE(5, PxReal, Damping) + SCB_REGULAR_ATTRIBUTE(6, PxReal, FrictionCoefficient) + SCB_REGULAR_ATTRIBUTE(7, PxReal, MaxJointVelocity) + SCB_REGULAR_ATTRIBUTE(8, PxReal, InternalCompliance) + SCB_REGULAR_ATTRIBUTE(9, PxReal, ExternalCompliance) + SCB_REGULAR_ATTRIBUTE(10, PxReal, SwingLimitContactDistance) + SCB_REGULAR_ATTRIBUTE(11, bool, SwingLimitEnabled) + SCB_REGULAR_ATTRIBUTE(12, PxReal, TangentialStiffness) + SCB_REGULAR_ATTRIBUTE(13, PxReal, TangentialDamping) + SCB_REGULAR_ATTRIBUTE(14, PxReal, TwistLimitContactDistance) + SCB_REGULAR_ATTRIBUTE(15, bool, TwistLimitEnabled) + SCB_REGULAR_ATTRIBUTE(16, PxArticulationJointDriveType::Enum, DriveType) + + enum { BF_SwingLimit = 1<<17 }; + enum { BF_TwistLimit = 1<<18 }; +#endif + SCB_REGULAR_ATTRIBUTE(19, PxArticulationJointType::Enum, JointType) + + PxReal mSwingLimitY; + PxReal mSwingLimitZ; + PxReal mTwistLimitLower; + PxReal mTwistLimitUpper; + + Dy::ArticulationLimit mLimits[6]; + Dy::ArticulationDrive mDrives[6]; + PxReal mTargetP[6]; + PxReal mTargetV[6]; + + //Joint limit/drive/whatever flags + enum + { + BF_Limits = 1 << 20, + BF_Drives = 1 << 21, + BF_Targets = 1 << 22, + BF_Motion = 1 << 23 + }; + + PxArticulationMotion::Enum mMotion[PxArticulationAxis::eCOUNT]; + + PxU32 mJointBufferFlags; +}; + +class ArticulationJoint : public Base +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef ArticulationJointBuffer Buf; + typedef Sc::ArticulationJointCore Core; + +public: +// PX_SERIALIZATION + ArticulationJoint(const PxEMPTY) : Base(PxEmpty), mJoint(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PX_INLINE ArticulationJoint( const PxTransform& parentFrame, + const PxTransform& childFrame, + PxArticulationBase::Enum type); + PX_INLINE ~ArticulationJoint(); + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::ArticulationJoint interface + //--------------------------------------------------------------------------------- + +#ifdef USE_NEW_SYSTEM + SCB_MEMBER(ArticulationJoint, mJoint, ParentPose, PxTransform, 0) + SCB_MEMBER(ArticulationJoint, mJoint, ChildPose, PxTransform, 1) + SCB_MEMBER(ArticulationJoint, mJoint, TargetOrientation, PxQuat, 2) + SCB_MEMBER(ArticulationJoint, mJoint, TargetVelocity, PxVec3, 3) + SCB_MEMBER(ArticulationJoint, mJoint, Stiffness, PxReal, 4) + SCB_MEMBER(ArticulationJoint, mJoint, Damping, PxReal, 5) + SCB_MEMBER(ArticulationJoint, mJoint, InternalCompliance, PxReal, 6) + SCB_MEMBER(ArticulationJoint, mJoint, ExternalCompliance, PxReal, 7) + SCB_MEMBER(ArticulationJoint, mJoint, SwingLimitContactDistance, PxReal, 8) + SCB_MEMBER(ArticulationJoint, mJoint, SwingLimitEnabled, bool, 9) + SCB_MEMBER(ArticulationJoint, mJoint, TangentialStiffness, PxReal, 10) + SCB_MEMBER(ArticulationJoint, mJoint, TangentialDamping, PxReal, 11) + SCB_MEMBER(ArticulationJoint, mJoint, TwistLimitContactDistance, PxReal, 12) + SCB_MEMBER(ArticulationJoint, mJoint, TwistLimitEnabled, bool, 13) + SCB_MEMBER(ArticulationJoint, mJoint, DriveType, PxArticulationJointDriveType::Enum, 14) +#else + PX_INLINE PxTransform getParentPose() const { return read(); } + PX_INLINE void setParentPose(const PxTransform& v) { write(v); } + + PX_INLINE PxTransform getChildPose() const { return read(); } + PX_INLINE void setChildPose(const PxTransform& v) { write(v); } + + PX_INLINE PxQuat getTargetOrientation() const { return read(); } + PX_INLINE void setTargetOrientation(const PxQuat& v) { write(v); } + + PX_INLINE PxVec3 getTargetVelocity() const { return read(); } + PX_INLINE void setTargetVelocity(const PxVec3& v) { write(v); } + + PX_INLINE PxReal getStiffness() const { return read(); } + PX_INLINE void setStiffness(PxReal v) { write(v); } + + PX_INLINE PxReal getDamping() const { return read(); } + PX_INLINE void setDamping(PxReal v) { write(v); } + + PX_INLINE PxReal getInternalCompliance() const { return read(); } + PX_INLINE void setInternalCompliance(PxReal v) { write(v); } + + PX_INLINE PxReal getExternalCompliance() const { return read(); } + PX_INLINE void setExternalCompliance(PxReal v) { write(v); } + + PX_INLINE PxReal getTangentialStiffness() const { return read(); } + PX_INLINE void setTangentialStiffness(PxReal v) { write(v); } + + PX_INLINE PxReal getTangentialDamping() const { return read(); } + PX_INLINE void setTangentialDamping(PxReal v) { write(v); } + + PX_INLINE PxReal getSwingLimitContactDistance() const { return read(); } + PX_INLINE void setSwingLimitContactDistance(PxReal v) { write(v); } + + PX_INLINE PxReal getTwistLimitContactDistance() const { return read(); } + PX_INLINE void setTwistLimitContactDistance(PxReal v) { write(v); } + + PX_INLINE PxArticulationJointDriveType::Enum + getDriveType() const { return read(); } + PX_INLINE void setDriveType(PxArticulationJointDriveType::Enum v) + { write(v); } + + PX_INLINE bool getSwingLimitEnabled() const { return read(); } + PX_INLINE void setSwingLimitEnabled(bool v) { write(v); } + + PX_INLINE bool getTwistLimitEnabled() const { return read(); } + PX_INLINE void setTwistLimitEnabled(bool v) { write(v); } +#endif + + PX_INLINE void getSwingLimit(PxReal& yLimit, PxReal& zLimit) const; + PX_INLINE void setSwingLimit(PxReal yLimit, PxReal zLimit); + + PX_INLINE void getTwistLimit(PxReal &lower, PxReal &upper) const; + PX_INLINE void setTwistLimit(PxReal lower, PxReal upper); + + PX_INLINE void setLimit(PxArticulationAxis::Enum axis, PxReal lower, PxReal upper); + PX_INLINE void getLimit(PxArticulationAxis::Enum axis, PxReal& lower, PxReal& upper) const; + + PX_INLINE void setDrive(PxArticulationAxis::Enum axis, PxReal stiffness, PxReal damping, PxReal maxForce, bool isAcceleration); + PX_INLINE void getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration) const; + + PX_INLINE void setDriveTarget(PxArticulationAxis::Enum axis, PxReal targetP); + PX_INLINE PxReal getDriveTarget(PxArticulationAxis::Enum axis) const; + + PX_INLINE void setDriveVelocity(PxArticulationAxis::Enum axis, PxReal targetV); + PX_INLINE PxReal getDriveVelocity(PxArticulationAxis::Enum axis) const; + + PX_INLINE PxArticulationJointType::Enum + getJointType() const { return read(); } + PX_INLINE void setJointType(PxArticulationJointType::Enum v) { write(v); } + + + PX_INLINE PxArticulationMotion::Enum + getMotion(PxArticulationAxis::Enum axis) const; + PX_INLINE void setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion); + + PX_INLINE PxReal getFrictionCoefficient() const { return read(); } + PX_INLINE void setFrictionCoefficient(const PxReal u) { write(u); } + + PX_INLINE PxReal getMaxJointVelocity() const { return read(); } + PX_INLINE void setMaxJointVelocity(const PxReal maxJointV) { write(maxJointV); } + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(); + + PX_FORCE_INLINE const Core& getScArticulationJoint() const { return mJoint; } // Only use if you know what you're doing! + PX_FORCE_INLINE Core& getScArticulationJoint() { return mJoint; } // Only use if you know what you're doing! + + + PX_FORCE_INLINE void setScArticulation(Scb::Articulation* articulation) { mJoint.setArticulation(&articulation->getScArticulation()); } + +private: + Core mJoint; + + PX_FORCE_INLINE const Buf* getBuffer() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Buf* getBuffer() { return reinterpret_cast(getStream()); } + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess {}; + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, mJoint); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, mJoint, v); } + template PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush >(*this, mJoint, buf); } +#endif +}; + +ArticulationJoint::ArticulationJoint(const PxTransform& parentFrame, const PxTransform& childFrame, + PxArticulationBase::Enum type) : + mJoint(parentFrame, childFrame, type) +{ + setScbType(ScbType::eARTICULATION_JOINT); +} + +ArticulationJoint::~ArticulationJoint() +{ +} + +PX_INLINE void ArticulationJoint::getSwingLimit(PxReal &yLimit, PxReal &zLimit) const +{ + if(isBuffered(Buf::BF_SwingLimit)) + { + yLimit = getBuffer()->mSwingLimitY; + zLimit = getBuffer()->mSwingLimitZ; + } + else + mJoint.getSwingLimit(yLimit, zLimit); +} + +PX_INLINE void ArticulationJoint::setSwingLimit(PxReal yLimit, PxReal zLimit) +{ + if(!isBuffering()) + mJoint.setSwingLimit(yLimit, zLimit); + else + { + getBuffer()->mSwingLimitY = yLimit; + getBuffer()->mSwingLimitZ = zLimit; + markUpdated(Buf::BF_SwingLimit); + } +} + +PX_INLINE void ArticulationJoint::getTwistLimit(PxReal &lower, PxReal &upper) const +{ + if(isBuffered(Buf::BF_TwistLimit)) + { + lower = getBuffer()->mTwistLimitLower; + upper = getBuffer()->mTwistLimitUpper; + } + else + mJoint.getTwistLimit(lower, upper); +} + +PX_INLINE void ArticulationJoint::setTwistLimit(PxReal lower, PxReal upper) +{ + if(!isBuffering()) + mJoint.setTwistLimit(lower, upper); + else + { + getBuffer()->mTwistLimitLower = lower; + getBuffer()->mTwistLimitUpper = upper; + markUpdated(Buf::BF_TwistLimit); + } +} + +PX_INLINE void ArticulationJoint::setLimit(PxArticulationAxis::Enum axis, PxReal lower, PxReal upper) +{ + if (!isBuffering()) + mJoint.setLimit(axis, lower, upper); + else + { + if (!isBuffered(Buf::BF_Limits)) + { + Dy::ArticulationLimit* limits = getBuffer()->mLimits; + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.getLimit(PxArticulationAxis::Enum(i), limits[i].low, limits[i].high); + } + } + getBuffer()->mLimits[axis].low = lower; + getBuffer()->mLimits[axis].high = upper; + markUpdated(Buf::BF_Limits); + } +} +PX_INLINE void ArticulationJoint::getLimit(PxArticulationAxis::Enum axis, PxReal& lower, PxReal& upper) const +{ + if (isBuffered(Buf::BF_Limits)) + { + lower = getBuffer()->mLimits[axis].low; + upper = getBuffer()->mLimits[axis].high; + } + else + mJoint.getLimit(axis, lower, upper); +} + +PX_INLINE void ArticulationJoint::setDrive(PxArticulationAxis::Enum axis, PxReal stiffness, PxReal damping, PxReal maxForce, bool isAcceleration) +{ + if (!isBuffering()) + mJoint.setDrive(axis, stiffness, damping, maxForce, isAcceleration); + else + { + if (!isBuffered(Buf::BF_Drives)) + { + Dy::ArticulationDrive* drives = getBuffer()->mDrives; + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.getDrive(PxArticulationAxis::Enum(i), drives[i].stiffness, drives[i].damping, drives[i].maxForce, drives[i].isAcceleration); + } + } + getBuffer()->mDrives[axis].stiffness = stiffness; + getBuffer()->mDrives[axis].damping = damping; + getBuffer()->mDrives[axis].maxForce = maxForce; + getBuffer()->mDrives[axis].isAcceleration = isAcceleration; + markUpdated(Buf::BF_Drives); + } +} +PX_INLINE void ArticulationJoint::getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration) const +{ + if (!isBuffered(Buf::BF_Drives)) + mJoint.getDrive(axis, stiffness, damping, maxForce, isAcceleration); + else + { + stiffness = getBuffer()->mDrives[axis].stiffness; + damping = getBuffer()->mDrives[axis].damping; + maxForce = getBuffer()->mDrives[axis].maxForce; + isAcceleration = getBuffer()->mDrives[axis].isAcceleration; + } +} + +PX_INLINE void ArticulationJoint::setDriveTarget(PxArticulationAxis::Enum axis, PxReal targetP) +{ + if (!isBuffering()) + mJoint.setTargetP(axis, targetP); + else + { + if (!isBuffered(Buf::BF_Targets)) + { + PxReal* targetP_ = getBuffer()->mTargetP; + PxReal* targetV_ = getBuffer()->mTargetV; + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + targetP_[i] = mJoint.getTargetP(PxArticulationAxis::Enum(i)); + targetV_[i] = mJoint.getTargetV(PxArticulationAxis::Enum(i)); + } + } + getBuffer()->mTargetP[axis] = targetP; + markUpdated(Buf::BF_Targets); + } +} +PX_INLINE PxReal ArticulationJoint::getDriveTarget(PxArticulationAxis::Enum axis) const +{ + if (!isBuffered(Buf::BF_Targets)) + return mJoint.getTargetP(axis); + else + { + return getBuffer()->mTargetP[axis]; + } +} + +PX_INLINE void ArticulationJoint::setDriveVelocity(PxArticulationAxis::Enum axis, PxReal targetP) +{ + if (!isBuffering()) + mJoint.setTargetV(axis, targetP); + else + { + if (!isBuffered(Buf::BF_Targets)) + { + PxReal* targetP_ = getBuffer()->mTargetP; + PxReal* targetV_ = getBuffer()->mTargetV; + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + targetP_[i] = mJoint.getTargetP(PxArticulationAxis::Enum(i)); + targetV_[i] = mJoint.getTargetV(PxArticulationAxis::Enum(i)); + } + } + getBuffer()->mTargetV[axis] = targetP; + markUpdated(Buf::BF_Targets); + } +} +PX_INLINE PxReal ArticulationJoint::getDriveVelocity(PxArticulationAxis::Enum axis) const +{ + if (!isBuffered(Buf::BF_Targets)) + return mJoint.getTargetV(axis); + else + { + return getBuffer()->mTargetV[axis]; + } +} + + +PX_INLINE void ArticulationJoint::setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) +{ + if (!isBuffering()) + mJoint.setMotion(axis, motion); + else + { + if (!isBuffered(Buf::BF_Motion)) + { + PxArticulationMotion::Enum* motionAxis = getBuffer()->mMotion; + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + motionAxis[i] = mJoint.getMotion(PxArticulationAxis::Enum(i)); + } + } + + getBuffer()->mMotion[axis] = motion; + markUpdated(PxU32(Buf::BF_Motion)); + + } +} + +PX_INLINE PxArticulationMotion::Enum ArticulationJoint::getMotion(PxArticulationAxis::Enum axis) const +{ + if (isBuffered(PxU32(Buf::BF_Motion))) + { + return getBuffer()->mMotion[axis]; + } + else + return mJoint.getMotion(axis); +} + + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_INLINE void ArticulationJoint::syncState() +{ + const PxU32 flags = getBufferFlags(); + if(flags) // Optimization to avoid all the if-statements below if possible + { + const Buf& buffer = *getBuffer(); + +#ifdef USE_NEW_SYSTEM + syncParentPose(); + syncChildPose(); + syncTargetOrientation(); + syncTargetVelocity(); + syncStiffness(); + syncDamping(); + syncInternalCompliance(); + syncExternalCompliance(); + syncSwingLimitContactDistance(); + syncSwingLimitEnabled(); + syncTwistLimitContactDistance(); + syncTwistLimitEnabled(); + syncTangentialStiffness(); + syncTangentialDamping(); + syncDriveType(); +#else + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); +#endif + + if(isBuffered(Buf::BF_SwingLimit)) + mJoint.setSwingLimit(buffer.mSwingLimitY, buffer.mSwingLimitZ); + + if(isBuffered(Buf::BF_TwistLimit)) + mJoint.setTwistLimit(buffer.mTwistLimitLower, buffer.mTwistLimitUpper); + + if (isBuffered(Buf::BF_Motion)) + { + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.setMotion(PxArticulationAxis::Enum(i), buffer.mMotion[i]); + } + } + + if (isBuffered(Buf::BF_Limits)) + { + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.setLimit(PxArticulationAxis::Enum(i), buffer.mLimits[i].low, buffer.mLimits[i].high); + } + } + + if (isBuffered(Buf::BF_Drives)) + { + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.setDrive(PxArticulationAxis::Enum(i), buffer.mDrives[i].stiffness, buffer.mDrives[i].damping, buffer.mDrives[i].maxForce, buffer.mDrives[i].isAcceleration); + } + } + + if (isBuffered(Buf::BF_Targets)) + { + for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + { + mJoint.setTargetP(PxArticulationAxis::Enum(i), buffer.mTargetP[i]); + mJoint.setTargetV(PxArticulationAxis::Enum(i), buffer.mTargetV[i]); + } + } + } + + postSyncState(); +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp new file mode 100644 index 000000000..6fc476f57 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScbBase.h" +#include "ScbNpDeps.h" + +using namespace physx; +using namespace Scb; + +void Base::destroy() +{ + if(!isBuffering()) + NpDestroy(*this); + else + { + // Schedule for destroy + PX_ASSERT(!(getControlFlags() & ControlFlag::eIS_RELEASED)); + PX_ASSERT(getControlState() == ControlState::eREMOVE_PENDING); + setControlFlag(ControlFlag::eIS_RELEASED); + } +} diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbBase.h b/src/PhysX/physx/source/physx/src/buffering/ScbBase.h new file mode 100644 index 000000000..c8b160edd --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbBase.h @@ -0,0 +1,318 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_BASE +#define PX_PHYSICS_SCB_BASE + +#include "CmPhysXCommon.h" +#include "ScbScene.h" + +namespace physx +{ +#if PX_SUPPORT_PVD + // PT: updatePvdProperties() is overloaded and the compiler needs to know 'this' type to do the right thing. + // Thus we can't just move this as an inlined Base function. + #define UPDATE_PVD_PROPERTIES_OBJECT() { \ + Scb::Scene* scene_ = getScbSceneForAPI(); \ + if(scene_ && !insertPending() ) \ + scene_->getScenePvdClient().updatePvdProperties(this); } +#else + #define UPDATE_PVD_PROPERTIES_OBJECT() {} +#endif + +namespace Scb +{ + struct ControlState + { + enum Enum + { + /** + \brief The object is not in the scene. + + The object has not been added to a scene yet. This is the default when an object gets created. + + In this state... + \li ...user changes get written to Core directly. + \li ...the object can not be in the list of dirty objects. + \li ...the object can not be marked as eIS_UPDATED or eIS_RELEASED. + */ + eNOT_IN_SCENE = 0, + + /** + \brief The object waits to get inserted into the scene internally. + + The object has been added to the scene while the simulation is running and is waiting to get fully registered in all layers. + + In this state... + \li ...user changes get written to Core directly (since the object has not yet registered in the inner layers, hence, will not get accessed internally). + \li ...the object is in the list of dirty objects. + \li ...the object can not be marked as eIS_UPDATED or eIS_RELEASED. + */ + eINSERT_PENDING = 1, + + /** + \brief The object is registered in the scene internally. + + The object has been added to the scene and is fully registered in all layers. + + In this state... + \li ...user changes get written to a buffer object and get synced with Core at a sync point. + \li ...the object can be in the list of dirty objects. + \li ...the object can be marked as eIS_UPDATED but not eIS_RELEASED. + */ + eIN_SCENE = 2, + + /** + \brief The object waits to get removed from the scene internally. + + The object is in the scene and fully registered in all layers but has been removed while the simulation is running and is now + waiting to get unregistered from all layers. + + In this state... + \li ...user changes get written to a buffer object and get synced with Core at a sync point. + \li ...the object is in the list of dirty objects. + \li ...the object can be marked as eIS_UPDATED or eIS_RELEASED. + */ + eREMOVE_PENDING = 3 + }; + }; + + struct ControlFlag + { + enum Enum + { + /** + \brief An object property/state has been changed. + + A property/state of the object has been changed and needs to get synced to Core. Insertion & removal don't count. + */ + eIS_UPDATED = 1, + + /** + \brief The object has been released. + + The object has not just been removed from the scene it has been released as well. The object will get destroyed after the sync has been completed. + */ + eIS_RELEASED = 2 + }; + }; + + /** + \brief Base class for objects that should support buffering. + + This class has members to track the buffering related object state and mark which properties have been changed and need to get synced at sync points. + */ + class Base + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_NOCOPY(Base) + public: +// PX_SERIALIZATION + Base(const PxEMPTY) : + mScene (NULL), + mStreamPtr (NULL) + { + resetAllBufferFlags(); + resetControl(ControlState::eNOT_IN_SCENE); + } + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + Base() : + mScene (NULL), + mStreamPtr (NULL) + { +#if PX_DEBUG + setScbType(ScbType::eUNDEFINED); + resetControl(ControlState::eNOT_IN_SCENE); + resetAllBufferFlags(); + PX_ASSERT(mControlState==0); +#endif + mControlState = 0; + } + + PX_INLINE bool isBuffering() const + { + const ControlState::Enum state = getControlState(); + return state == ControlState::eREMOVE_PENDING || // pending remove not possible if not buffered + (state == ControlState::eIN_SCENE && mScene->isPhysicsBuffering()); + } + + PX_FORCE_INLINE Ps::IntBool insertPending() const { return getControlState() == ControlState::eINSERT_PENDING; } + PX_FORCE_INLINE ScbType::Enum getScbType() const { return ScbType::Enum((mControlState&eTYPE_MASK)>>eTYPE_SHIFT); } + PX_FORCE_INLINE void setScbType(ScbType::Enum type) { mControlState = (mControlState&~eTYPE_MASK)|(type<>eCONTROLFLAG_SHIFT; } + PX_FORCE_INLINE void setControlFlag(ControlFlag::Enum f) { mControlState |= (f<>eCONTROLSTATE_SHIFT);} + PX_FORCE_INLINE void setControlState(ControlState::Enum s) { mControlState = (mControlState&~eCONTROLSTATE_MASK)|(s<scheduleForUpdate(*this); } + + /** + \brief Destroys the object. + + If the simulation is not running, this will release the object, else it will just mark the object as eIS_RELEASED and thus it will get deleted + when the next sync point is reached. This method expects that the object has been removed from the scene first (or waits for removal). + */ + void destroy(); + + /** + \brief Test if a property has been changed by the user. + + \param[in] flag The flag of the property to test for. Has to be within the bounds of eBUFFERFLAG_MASK. + \return Positive value if the property has been changed by the user. + */ + PX_FORCE_INLINE Ps::IntBool isBuffered(PxU32 flag) const + { + PX_ASSERT((flag & eBUFFERFLAG_MASK) == flag); + return Ps::IntBool(mControlState & flag); + } + + PX_FORCE_INLINE PxU8* getStream() { return mStreamPtr ? mStreamPtr : mStreamPtr = mScene->getStream(getScbType()); } + PX_FORCE_INLINE const PxU8* getStream() const { PX_ASSERT(mStreamPtr); return mStreamPtr; } + + /** + \brief Helper method to trigger object tracking after a property change. + + This method will flag the marked property as changed and will add the object to the list of updated objects if it is not + registered already. + + \param[in] flag The flag of the changed property. Has to be within the bounds of eBUFFERFLAG_MASK. + */ + PX_FORCE_INLINE void markUpdated(PxU32 flag) + { + PX_ASSERT((flag & eBUFFERFLAG_MASK) == flag); + scheduleForUpdate(); + mControlState |= flag; + } + protected: + ~Base(){} + + PX_FORCE_INLINE PxU32 getBufferFlags() const { return mControlState & eBUFFERFLAG_MASK; } + PX_FORCE_INLINE void setBufferFlag(PxU32 flag) { PX_ASSERT((flag & eBUFFERFLAG_MASK) == flag); mControlState |= flag; } + PX_FORCE_INLINE void resetBufferFlag(PxU32 flag) { PX_ASSERT((flag & eBUFFERFLAG_MASK) == flag); mControlState &= ~flag; } + PX_FORCE_INLINE void resetAllBufferFlags() { mControlState &=~eBUFFERFLAG_MASK; } + + /** + \brief Cleanup method after the object has been synced. + + Every buffering object should implement a syncState() method where the buffered user changes get synced with Core. Call this method at the end of the + syncState() method to clear all buffer flags and references. + */ + PX_FORCE_INLINE void postSyncState() + { + // DS: this can get called even when mScene == NULL, by removeAggregate (see AggregateFreeStandingCreateDelete test) + // TODO(dsequeira): investigate that when the dust settles on shape refactoring + PX_ASSERT(getControlState()!=ControlState::eNOT_IN_SCENE || mScene == NULL); + PX_ASSERT(getScbType()!=ScbType::eUNDEFINED); + + mStreamPtr = NULL; + resetAllBufferFlags(); +// resetControlFlag(ControlFlag::eIS_UPDATED); + } + private: + enum { eBUFFERFLAG_MASK = (1<<24) - 1, + eTYPE_MASK = 15<<24, + eCONTROLFLAG_MASK = 3<<28, + eCONTROLSTATE_MASK = 3<<30, + eCONTROL_MASK = 15<<28}; + + enum { eTYPE_SHIFT = 24, + eCONTROLFLAG_SHIFT = 28, + eCONTROLSTATE_SHIFT = 30}; + + /** + \brief Scene pointer. + + The scene pointer get set as soon as the user adds an object to the scene. However, it does not get cleared until the object has been + removed from the scene internally, i.e., removing an object while the simulation is running will not set this pointer to NULL immediately. + */ + Scb::Scene* mScene; + + /** + \brief Mix of buffering related states/flags. + + highest lowest + | 2 | 2 | 4 | 24 | + | ControlState | ControlFlag | ScbType | buffer attribute flags | + + The buffer attribute flags mark which of the properties have been updated. The specific implementation of this class defines those flags. + */ + PxU32 mControlState; + + /** + \brief Data buffer to store property/state changes temporarily. + + Pointer to a temporary struct where user changes made while the simulation is running are buffered. The structure is currently as large as necessary to hold all + properties of a buffered object. Even if only a single property of an object gets changed, the whole structure is assigned. The motivation for this was to keep + the implementation complexity low based on the assumption that users will not change all objects in a scene every frame. The temporary buffer gets assigned on demand + and is returned to pools after the data has been synced to Core. The pointer is then set to NULL again. + This kind of buffer can not be used for properties that get written by the simulation (for example, pose, velocity, sleep state). Those need to be permanently buffered + in the specific implementation of this class, else the user might get an inconsistent picture of the scene object state. + + @see postSyncState() + */ + PxU8* mStreamPtr; + }; + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbBody.h b/src/PhysX/physx/source/physx/src/buffering/ScbBody.h new file mode 100644 index 000000000..33bee2a54 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbBody.h @@ -0,0 +1,1134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_BODY +#define PX_PHYSICS_SCB_BODY + +#include "ScBodyCore.h" + +#include "ScbRigidObject.h" +#include "CmUtils.h" +#include "PsUtilities.h" +#include "PxRigidDynamic.h" +#include "ScbDefs.h" +#include "GuSIMDHelpers.h" + +namespace physx +{ +namespace Scb +{ +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +struct BodyBuffer : public RigidObjectBuffer //once RigidObject has its own buffered elements, derive from that instead +{ +#ifdef USE_NEW_SYSTEM + // PT: I think these start from 0 because they're stored in mBodyBufferFlags instead of the base + // regular attributes + PxReal mInverseMass; + PxVec3 mInverseInertia; + PxReal mLinearDamping; + PxReal mAngularDamping; + PxReal mMaxAngVelSq; + PxReal mMaxLinVelSq; + PxReal mSleepThreshold; + PxReal mCCDAdvanceCoefficient; + PxReal mContactReportThreshold; + PxU16 mSolverIterationCounts; + PX_ALIGN(16, PxTransform) mBody2Actor; + PxReal mMaxPenetrationBias; + PxReal mFreezeThreshold; + PxReal mMaxContactImpulse; + PxRigidDynamicLockFlags mRigidDynamicLockFlags; +#else + template struct Fns {}; // TODO: make the base class traits visible + typedef Sc::BodyCore Core; + typedef BodyBuffer Buf; + + // PT: I think these start from 0 because they're stored in mBodyBufferFlags instead of the base + // regular attributes + SCB_REGULAR_ATTRIBUTE(0, PxReal, InverseMass) + SCB_REGULAR_ATTRIBUTE(1, PxVec3, InverseInertia) + SCB_REGULAR_ATTRIBUTE(2, PxReal, LinearDamping) + SCB_REGULAR_ATTRIBUTE(3, PxReal, AngularDamping) + SCB_REGULAR_ATTRIBUTE(4, PxReal, MaxAngVelSq) + SCB_REGULAR_ATTRIBUTE(5, PxReal, MaxLinVelSq) + SCB_REGULAR_ATTRIBUTE(6, PxReal, SleepThreshold) + SCB_REGULAR_ATTRIBUTE(7, PxReal, CCDAdvanceCoefficient) + SCB_REGULAR_ATTRIBUTE(8, PxReal, ContactReportThreshold) + SCB_REGULAR_ATTRIBUTE(9, PxU16, SolverIterationCounts) + SCB_REGULAR_ATTRIBUTE_ALIGNED(10,PxTransform, Body2Actor, 16) + SCB_REGULAR_ATTRIBUTE(11, PxReal, MaxPenetrationBias) + SCB_REGULAR_ATTRIBUTE(12, PxReal, FreezeThreshold) + SCB_REGULAR_ATTRIBUTE(13, PxReal, MaxContactImpulse) + SCB_REGULAR_ATTRIBUTE(14, PxRigidDynamicLockFlags, RigidDynamicLockFlags) +#endif + // irregular attributes + + PX_ALIGN(16, PxTransform) mKinematicTarget; + PxVec3 mLinAcceleration; + PxVec3 mAngAcceleration; + PxVec3 mLinDeltaVelocity; + PxVec3 mAngDeltaVelocity; + + PxRigidBodyFlags mRigidBodyFlags; + + enum + { + BF_RigidBodyFlags = 1u<<14u, + BF_KinematicTarget = 1u<<15u, + BF_AccelerationLinear = 1u<<16u, + BF_AccelerationAngular = 1u<<17u, + BF_Acceleration = BF_AccelerationLinear|BF_AccelerationAngular, + BF_DeltaVelocityLinear = 1u<<18u, + BF_DeltaVelocityAngular = 1u<<19u, + BF_DeltaVelocity = BF_DeltaVelocityLinear|BF_DeltaVelocityAngular, + BF_Body2World = 1u<<20u, + BF_Body2World_CoM = 1u<<21u, // the body pose was adjusted because of a center of mass change only + BF_LinearVelocity = 1u<<22u, + BF_AngularVelocity = 1u<<23u, + BF_WakeCounter = 1u<<24u, + BF_PutToSleep = 1u<<25u, + BF_WakeUp = 1u<<26u, + BF_ClearAccelerationLinear = 1u<<27u, + BF_ClearAccelerationAngular = 1u<<28u, + BF_ClearAcceleration = BF_ClearAccelerationLinear|BF_ClearAccelerationAngular, + BF_ClearDeltaVelocityLinear = 1u<<29u, + BF_ClearDeltaVelocityAngular= 1u<<30u, + BF_ClearDeltaVelocity = BF_ClearDeltaVelocityLinear|BF_ClearDeltaVelocityAngular + }; + + BodyBuffer(): mLinAcceleration(0), mAngAcceleration(0), mLinDeltaVelocity(0), mAngDeltaVelocity(0) {} +}; + +#if PX_VC + #pragma warning(pop) +#endif + +class Body : public Scb::RigidObject +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef BodyBuffer Buf; + typedef Sc::BodyCore Core; + +public: +// PX_SERIALIZATION + Body(const PxEMPTY) : Scb::RigidObject(PxEmpty), mBodyCore(PxEmpty), mBufferedIsSleeping(1) { PX_ASSERT(mBodyBufferFlags == 0); } + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + PX_INLINE Body(PxActorType::Enum type, const PxTransform& bodyPose); + PX_INLINE ~Body() {} + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::BodyCore interface + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE const PxTransform& getBody2World() const { return mBufferedBody2World; } // PT: important: keep returning an address here (else update prefetch in SceneQueryManager::addShapes) + PX_INLINE void setBody2World(const PxTransform& p, bool asPartOfBody2ActorChange); + + PX_FORCE_INLINE const PxVec3& getLinearVelocity() const { return mBufferedLinVelocity; } + PX_INLINE void setLinearVelocity(const PxVec3& v); + PX_FORCE_INLINE const PxVec3& getAngularVelocity() const { return mBufferedAngVelocity; } + PX_INLINE void setAngularVelocity(const PxVec3& v); + + PX_FORCE_INLINE void wakeUp(); + PX_FORCE_INLINE void putToSleep(); + PX_FORCE_INLINE PxReal getWakeCounter() const { return mBufferedWakeCounter; } + PX_INLINE void setWakeCounter(PxReal w); + PX_FORCE_INLINE bool isSleeping() const { return (mBufferedIsSleeping != 0); } + +#ifdef USE_NEW_SYSTEM + SCB_MEMBER(Body, mBodyCore, InverseMass, PxReal, 0) + SCB_MEMBER(Body, mBodyCore, InverseInertia, PxVec3, 1) + SCB_MEMBER(Body, mBodyCore, LinearDamping, PxReal, 2) + SCB_MEMBER(Body, mBodyCore, AngularDamping, PxReal, 3) + SCB_MEMBER(Body, mBodyCore, MaxAngVelSq, PxReal, 4) + SCB_MEMBER(Body, mBodyCore, MaxLinVelSq, PxReal, 5) + SCB_MEMBER(Body, mBodyCore, SleepThreshold, PxReal, 6) + SCB_MEMBER(Body, mBodyCore, CCDAdvanceCoefficient, PxReal, 7) + SCB_MEMBER(Body, mBodyCore, ContactReportThreshold, PxReal, 8) + SCB_MEMBER(Body, mBodyCore, SolverIterationCounts, PxU16, 9) + SCB_MEMBER(Body, mBodyCore, Body2Actor, const PxTransform&, 10) + SCB_MEMBER(Body, mBodyCore, MaxPenetrationBias, PxReal, 11) + SCB_MEMBER(Body, mBodyCore, FreezeThreshold, PxReal, 12) + SCB_MEMBER(Body, mBodyCore, MaxContactImpulse, PxReal, 13) + SCB_MEMBER(Body, mBodyCore, RigidDynamicLockFlags, PxRigidDynamicLockFlags, 14) + + // PT: TODO: fix the inconsistent naming + PX_FORCE_INLINE PxReal getMinCCDAdvanceCoefficient() const { return getCCDAdvanceCoefficient(); } + PX_FORCE_INLINE void setMinCCDAdvanceCoefficient(PxReal v) { setCCDAdvanceCoefficient(v); } + PX_FORCE_INLINE PxRigidDynamicLockFlags getLockFlags() const { return getRigidDynamicLockFlags(); } + PX_FORCE_INLINE void setLockFlags(PxRigidDynamicLockFlags f) { setRigidDynamicLockFlags(f); } +#else + PX_INLINE const PxTransform& getBody2Actor() const { return read(); } + PX_INLINE void setBody2Actor(const PxTransform& m) { write(m); } + + PX_INLINE PxReal getInverseMass() const { return read(); } + PX_INLINE void setInverseMass(PxReal m) { write(m); } + + PX_INLINE PxVec3 getInverseInertia() const { return read(); } + PX_INLINE void setInverseInertia(const PxVec3& i) { write(i); } + + PX_INLINE PxReal getLinearDamping() const { return read(); } + PX_INLINE void setLinearDamping(PxReal d) { write(d); } + + PX_INLINE PxReal getAngularDamping() const { return read(); } + PX_INLINE void setAngularDamping(PxReal d) { write(d); } + + PX_INLINE PxReal getMaxAngVelSq() const { return read(); } + PX_INLINE void setMaxAngVelSq(PxReal v) { write(v); } + + PX_INLINE PxReal getSleepThreshold() const { return read(); } + PX_INLINE void setSleepThreshold(PxReal t) { write(t); } + + PX_INLINE void setMinCCDAdvanceCoefficient(PxReal minCCDAdvanceCoefficient){write(minCCDAdvanceCoefficient);} + PX_INLINE PxReal getMinCCDAdvanceCoefficient() const { return read();} + + PX_INLINE PxReal getContactReportThreshold() const { return read(); } + PX_INLINE void setContactReportThreshold(PxReal t) { write(t); } + + PX_INLINE PxReal getMaxLinVelSq() const { return read(); } + PX_INLINE void setMaxLinVelSq(PxReal v) { write(v); } + + PX_INLINE PxU16 getSolverIterationCounts() const { return Ps::to16(read()); } + PX_INLINE void setSolverIterationCounts(PxU16 c) { write(c); } + + PX_INLINE PxReal getMaxPenetrationBias() const { return read(); } + PX_INLINE void setMaxPenetrationBias(PxReal t) { write(t); } + + PX_INLINE PxReal getFreezeThreshold() const { return read(); } + PX_INLINE void setFreezeThreshold(PxReal t) { write(t); } + + PX_INLINE PxReal getMaxContactImpulse() const { return read(); } + PX_INLINE void setMaxContactImpulse(PxReal t) { write(t); } + + PX_INLINE PxRigidDynamicLockFlags getLockFlags() const { return read(); } + PX_INLINE void setLockFlags(PxRigidDynamicLockFlags f) { write(f); } +#endif + + PX_INLINE PxRigidBodyFlags getFlags() const { return (isBuffered(Buf::BF_RigidBodyFlags)) ? getBodyBuffer()->mRigidBodyFlags : mBodyCore.getFlags(); } + PX_INLINE void setFlags(PxRigidBodyFlags f); + + PX_INLINE PxU32 getInternalIslandNodeIndex() const { return mBodyCore.getInternalIslandNodeIndex(); } + + PX_INLINE void addSpatialAcceleration(const PxVec3* linAcc, const PxVec3* angAcc); + PX_INLINE void setSpatialAcceleration(const PxVec3* linAcc, const PxVec3* angAcc); + PX_INLINE void clearSpatialAcceleration(bool force, bool torque); + PX_INLINE void addSpatialVelocity(const PxVec3* linVelDelta, const PxVec3* angVelDelta); + PX_INLINE void clearSpatialVelocity(bool force, bool torque); + + PX_INLINE bool getKinematicTarget(PxTransform& p) const; + PX_INLINE void setKinematicTarget(const PxTransform& p); + + PX_FORCE_INLINE void onOriginShift(const PxVec3& shift); + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(); + PX_INLINE void syncCollisionWriteThroughState(); + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mBodyCore); } + + /** + \brief Shadowed method of #Scb::Base::markUpdated() to store the buffered property flags in a separate location (ran out of flag space) + */ + PX_FORCE_INLINE void markUpdated(PxU32 flag); + + /** + \brief Shadowed method of #Scb::Base::isBuffered() to check the buffered property flags (ran out of flag space) + */ + PX_FORCE_INLINE Ps::IntBool isBuffered(PxU32 flag) const; + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- +public: + PX_FORCE_INLINE const Sc::BodyCore& getScBody() const { return mBodyCore; } // Only use if you know what you're doing! + PX_FORCE_INLINE Sc::BodyCore& getScBody() { return mBodyCore; } // Only use if you know what you're doing! + + PX_FORCE_INLINE static const Body& fromSc(const Core& a) { return static_cast(Actor::fromSc(a)); } + PX_FORCE_INLINE static Body& fromSc(Core &a) { return static_cast(Actor::fromSc(a)); } + + PX_FORCE_INLINE bool hasKinematicTarget() const; + PX_FORCE_INLINE void clearSimStateDataForPendingInsert(); + PX_FORCE_INLINE void transitionSimStateDataForPendingInsert(); + + PX_INLINE PxMat33 getGlobalInertiaTensorInverse() const; + + PX_FORCE_INLINE bool checkSleepReadinessBesidesWakeCounter(); + PX_FORCE_INLINE void initBufferedState(); + PX_FORCE_INLINE void clearBufferedState(); + PX_FORCE_INLINE void clearBufferedSleepStateChange(); + + PX_INLINE void wakeUpInternal(PxReal wakeCounter); + PX_INLINE void putToSleepInternal(); + + PX_FORCE_INLINE void switchBodyToNoSim(); + +private: + Sc::BodyCore mBodyCore; + //--------------------------------------------------------------------------------- + // Permanently buffered data (simulation written data) + //--------------------------------------------------------------------------------- + PxTransform mBufferedBody2World; + PxVec3 mBufferedLinVelocity; + PxVec3 mBufferedAngVelocity; + PxReal mBufferedWakeCounter; + PxU32 mBufferedIsSleeping; // Note: If the object is not in a scene this value must be true, i.e., positive + // Don't need 4 bytes but otherwise there is padding here anyway. + PxU32 mBodyBufferFlags; // Stores the buffered property flags since there is not enough space in usual location in Scb::Base. + + PX_FORCE_INLINE const Buf* getBodyBuffer() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Buf* getBodyBuffer() { return reinterpret_cast(getStream()); } + + PX_INLINE void accumulate(PxVec3& linear, PxVec3& angular, PxU32 linearFlag, PxU32 angularFlag, const PxVec3* linIncrement, const PxVec3* angIncrement) + { + PxU32 flag = 0; + if(linIncrement) + { + linear += *linIncrement; + flag |= linearFlag; + } + + if(angIncrement) + { + angular += *angIncrement; + flag |= angularFlag; + } + + markUpdated(flag); + } + + PX_INLINE void resetAccumulator(PxVec3& linear, PxVec3& angular, PxU32 linearFlag, PxU32 angularFlag, PxU32 raisedFlagLinear, PxU32 raisedFlagAngular, bool force, bool torque) + { + PxU32 flags = mBodyBufferFlags; + PxU32 raisedFlags = 0; + if(force) + { + linear = PxVec3(0.0f); + flags &= ~linearFlag; + raisedFlags |= raisedFlagLinear; + } + + if(torque) + { + angular = PxVec3(0.0f); + flags &= ~angularFlag; + raisedFlags |= raisedFlagAngular; + } + + //This is for the split sim logic to support write-through spatial accelerations. It is for the condition where a spatial acceleration has been applied prior to + //collide(). However, a clear spatial acceleration command is raised by the user between collide() and fetchCollision(), we need to raised this + //flag to clear the previous applied spatial acceleration in the unbuffered state so that this spatial acceleration can be cleared correctly in fetchCollision(). + flags |= raisedFlags; + mBodyBufferFlags = flags; + scheduleForUpdate(); + } + + PX_FORCE_INLINE void setBufferedParamsForAsleep() // use this in the non-buffered case to set the buffered properties + { + mBufferedIsSleeping = 1; + mBufferedWakeCounter = 0.0f; + mBufferedLinVelocity = PxVec3(0.0f); + mBufferedAngVelocity = PxVec3(0.0f); + // no need to clear forces since that will be the job of the corresponding core/sim methods + } + + PX_FORCE_INLINE void setBufferedParamsForAwake(PxReal wakeCounter) // use this in the non-buffered case to set the buffered properties + { + mBufferedIsSleeping = 0; + mBufferedWakeCounter = wakeCounter; + } + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess {}; + + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, mBodyCore); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, mBodyCore, v); } + template PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush >(*this, mBodyCore, buf); } +#endif +}; + + +PX_INLINE Body::Body(PxActorType::Enum type, const PxTransform& bodyPose) : mBodyCore(type, bodyPose) +{ + setScbType(ScbType::eBODY); + + mBufferedBody2World = mBodyCore.getBody2World(); + mBufferedLinVelocity = mBodyCore.getLinearVelocity(); + mBufferedAngVelocity = mBodyCore.getAngularVelocity(); + mBufferedWakeCounter = mBodyCore.getWakeCounter(); + mBufferedIsSleeping = 1; // this is the specified value for free standing objects + mBodyBufferFlags = 0; +} + +PX_INLINE void Body::setBody2World(const PxTransform& p, bool asPartOfBody2ActorChange) +{ + mBufferedBody2World = p; + + if(!isBuffering()) + { + mBodyCore.setBody2World(p); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if(!asPartOfBody2ActorChange) + { + // call was triggered by a setGlobalPose(). This means the simulated body pose will get + // overwritten by the user value, so we do not need to adjust it. + + mBodyBufferFlags &= ~Buf::BF_Body2World_CoM; + } + else if(!(mBodyBufferFlags & Buf::BF_Body2World)) + { + // there has been no setGlobalPose() on the body yet and the center of mass changes. + // This case needs special treatment because the simulation results for such a body will be based on + // the old center of mass but need to get translated to the new center of mass. + + mBodyBufferFlags |= Buf::BF_Body2World_CoM; + } + + markUpdated(Buf::BF_Body2World); + } +} + +PX_INLINE void Body::setLinearVelocity(const PxVec3& v) +{ + mBufferedLinVelocity = v; + + if(!isBuffering()) + { + mBodyCore.setLinearVelocity(v); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + markUpdated(Buf::BF_LinearVelocity); +} + +PX_INLINE void Body::setAngularVelocity(const PxVec3& v) +{ + mBufferedAngVelocity = v; + + if(!isBuffering()) + { + mBodyCore.setAngularVelocity(v); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + markUpdated(Buf::BF_AngularVelocity); +} + + +PX_INLINE void Body::wakeUpInternal(PxReal wakeCounter) +{ + PX_ASSERT(getScbScene()); + + if(!isBuffering()) + { + setBufferedParamsForAwake(wakeCounter); + mBodyCore.wakeUp(wakeCounter); + } + else + { + mBufferedIsSleeping = 0; + mBufferedWakeCounter = wakeCounter; + markUpdated(Buf::BF_WakeUp | Buf::BF_WakeCounter); + mBodyBufferFlags &= ~Buf::BF_PutToSleep; + } +} + +PX_FORCE_INLINE void Body::wakeUp() +{ + PX_ASSERT(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)); + Scene* scene = getScbScene(); + PX_ASSERT(scene); // only allowed for an object in a scene + + wakeUpInternal(scene->getWakeCounterResetValue()); +} + +PX_INLINE void Body::putToSleepInternal() +{ + if(!isBuffering()) + { + setBufferedParamsForAsleep(); + mBodyCore.putToSleep(); + } + else + { + mBufferedIsSleeping = 1; + mBufferedWakeCounter = 0.0f; + // it is necessary to set the velocities as a buffered operation (not just adjust the buffered velocities) because + // a putToSleep can be followed by a wakeUp in which case only the wakeUp will get processed on sync, however, the velocities + // still need to get set to 0. + setLinearVelocity(PxVec3(0.0f)); + setAngularVelocity(PxVec3(0.0f)); + mBodyBufferFlags &= ~(Buf::BF_Acceleration | Buf::BF_DeltaVelocity | Buf::BF_KinematicTarget); + + markUpdated(Buf::BF_PutToSleep | Buf::BF_WakeCounter); + mBodyBufferFlags &= ~Buf::BF_WakeUp; + } +} + +PX_FORCE_INLINE void Body::putToSleep() +{ + PX_ASSERT(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + putToSleepInternal(); +} + +PX_INLINE void Body::setWakeCounter(PxReal w) +{ + PX_ASSERT(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)); + + mBufferedWakeCounter = w; + + if(!isBuffering()) + { + if(getScbScene() && (w > 0.0f)) + mBufferedIsSleeping = 0; + + mBodyCore.setWakeCounter(w); + + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if(w > 0.0f) + wakeUpInternal(w); + else + markUpdated(Buf::BF_WakeCounter); + } +} + +PX_INLINE void Body::setFlags(PxRigidBodyFlags f) +{ + const PxU32 wasKinematic = getFlags() & PxRigidBodyFlag::eKINEMATIC; + const PxU32 isKinematic = f & PxRigidBodyFlag::eKINEMATIC; + const bool switchToKinematic = ((!wasKinematic) && isKinematic); + const bool switchToDynamic = (wasKinematic && (!isKinematic)); + + if(!isBuffering()) + { + if(switchToKinematic) + setBufferedParamsForAsleep(); + + mBodyCore.setFlags(getScbScene() ? getScbScene()->getScScene().getSimStateDataPool() : NULL, f); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + if(switchToKinematic) + putToSleepInternal(); + else if(switchToDynamic) + mBodyBufferFlags &= ~Buf::BF_KinematicTarget; + + getBodyBuffer()->mRigidBodyFlags = f; + markUpdated(Buf::BF_RigidBodyFlags); + } +} + +PX_INLINE void Body::addSpatialAcceleration(const PxVec3* linAcc, const PxVec3* angAcc) +{ + if(!isBuffering()) + { + mBodyCore.addSpatialAcceleration(getScbScene()->getScScene().getSimStateDataPool(), linAcc, angAcc); + //Spatial acceleration isn't sent to PVD. + } + else + { + Buf* b = getBodyBuffer(); + accumulate(b->mLinAcceleration, b->mAngAcceleration, Buf::BF_AccelerationLinear, Buf::BF_AccelerationAngular, linAcc, angAcc); + } +} + +PX_INLINE void Body::setSpatialAcceleration(const PxVec3* linAcc, const PxVec3* angAcc) +{ + if (!isBuffering()) + { + mBodyCore.setSpatialAcceleration(getScbScene()->getScScene().getSimStateDataPool(), linAcc, angAcc); + //Spatial acceleration isn't sent to PVD. + } + else + { + Buf* b = getBodyBuffer(); + + PxU32 flag = 0; + if (linAcc) + { + b->mLinAcceleration = *linAcc; + flag |= Buf::BF_AccelerationLinear; + } + + if (angAcc) + { + b->mAngAcceleration += *angAcc; + flag |= Buf::BF_AccelerationAngular; + } + + markUpdated(flag); + } +} + +PX_INLINE void Body::clearSpatialAcceleration(bool force, bool torque) +{ + if(!isBuffering()) + { + mBodyCore.clearSpatialAcceleration(force, torque); + //Spatial acceleration isn't sent to PVD. + } + else + { + Buf* b = getBodyBuffer(); + resetAccumulator(b->mLinAcceleration, b->mAngAcceleration, Buf::BF_AccelerationLinear, Buf::BF_AccelerationAngular, Buf::BF_ClearAccelerationLinear, Buf::BF_ClearAccelerationAngular, force, torque); + } +} + +PX_INLINE void Body::addSpatialVelocity(const PxVec3* linVelDelta, const PxVec3* angVelDelta) +{ + if(!isBuffering()) + { + mBodyCore.addSpatialVelocity(getScbScene()->getScScene().getSimStateDataPool(), linVelDelta, angVelDelta); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* b = getBodyBuffer(); + accumulate(b->mLinDeltaVelocity, b->mAngDeltaVelocity, Buf::BF_DeltaVelocityLinear, Buf::BF_DeltaVelocityAngular, linVelDelta, angVelDelta); + } +} + +PX_INLINE void Body::clearSpatialVelocity(bool force, bool torque) +{ + if(!isBuffering()) + { + mBodyCore.clearSpatialVelocity(force, torque); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* b = getBodyBuffer(); + resetAccumulator(b->mLinDeltaVelocity, b->mAngDeltaVelocity, Buf::BF_DeltaVelocityLinear, Buf::BF_DeltaVelocityAngular, Buf::BF_ClearDeltaVelocityLinear, PxU32(Buf::BF_ClearDeltaVelocityAngular), force, torque); + } +} + +PX_INLINE bool Body::getKinematicTarget(PxTransform& p) const +{ + if(isBuffered(Buf::BF_KinematicTarget)) + { + p = getBodyBuffer()->mKinematicTarget; + return true; + } + else if(getControlState() != ControlState::eREMOVE_PENDING) + return mBodyCore.getKinematicTarget(p); + else + return false; +} + +PX_INLINE void Body::setKinematicTarget(const PxTransform& p) +{ + Scene* scene = getScbScene(); + PX_ASSERT(scene); // only allowed for an object in a scene + PxReal wakeCounterResetValue = scene->getWakeCounterResetValue(); + + if(!isBuffering()) + { + mBodyCore.setKinematicTarget(scene->getScScene().getSimStateDataPool(), p, wakeCounterResetValue); + setBufferedParamsForAwake(wakeCounterResetValue); + + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + //Kinematics can now have buffered delta velocities... + //PX_ASSERT((mBodyBufferFlags & (Buf::BF_DeltaVelocity|Buf::BF_Acceleration)) == 0); // switching to kinematic should do that. + getBodyBuffer()->mKinematicTarget = p; + markUpdated(Buf::BF_KinematicTarget); + + wakeUpInternal(wakeCounterResetValue); + } +#if PX_SUPPORT_PVD + if(getControlState() == ControlState::eIN_SCENE) + scene->getScenePvdClient().updateKinematicTarget(this, p); +#endif +} + +PX_FORCE_INLINE void Body::onOriginShift(const PxVec3& shift) +{ + mBufferedBody2World.p -= shift; + mBodyCore.onOriginShift(shift); +} + +//-------------------------------------------------------------- +// +// Miscellaneous +// +//-------------------------------------------------------------- + +PX_FORCE_INLINE bool Body::hasKinematicTarget() const +{ + return (isBuffered(BodyBuffer::BF_KinematicTarget) || mBodyCore.getHasValidKinematicTarget()); +} + +PX_FORCE_INLINE void Body::clearSimStateDataForPendingInsert() +{ + if(insertPending()) + { + // not-so-nice-code to cover the following cases: + // - user adds a kinematic to the scene, sets a target and removes the kinematic from scene again (all while the sim is running) + // - same as above but instead of removing the kinematic it gets switched to dynamic + // - user adds a dynamic to the scene, sets a target and removes the dynamic from scene again (all while the sim is running) + + Sc::BodyCore& core = mBodyCore; + if(core.getSimStateData(true)) + core.tearDownSimStateData(getScbScene()->getScScene().getSimStateDataPool(), true); + else if(core.getSimStateData(false)) + core.tearDownSimStateData(getScbScene()->getScScene().getSimStateDataPool(), false); + } +} + +PX_FORCE_INLINE void Body::transitionSimStateDataForPendingInsert() +{ + if(insertPending()) + { + // not-so-nice-code to cover the following case: + // - user adds a dynamic, adds force, then switches to kinematic (all while the sim is running) + + Sc::BodyCore& core = mBodyCore; + if(core.getSimStateData(false)) + core.setupSimStateData(getScbScene()->getScScene().getSimStateDataPool(), true); // note: this won't allocate the memory twice + } +} + +PX_INLINE PxMat33 Body::getGlobalInertiaTensorInverse() const +{ + PxMat33 inverseInertiaWorldSpace; + Cm::transformInertiaTensor(getInverseInertia(), Gu::PxMat33Padded(getBody2World().q), inverseInertiaWorldSpace); + return inverseInertiaWorldSpace; +} + +PX_FORCE_INLINE bool Body::checkSleepReadinessBesidesWakeCounter() +{ + return (getLinearVelocity().isZero() && getAngularVelocity().isZero()); + // no need to test for pending force updates yet since currently this is not supported on scene insertion +} + +PX_FORCE_INLINE void Body::initBufferedState() +{ + PX_ASSERT(mBufferedIsSleeping); // this method is only meant to get called when an object is added to the scene + + if((getWakeCounter() == 0.0f) && checkSleepReadinessBesidesWakeCounter()) + mBufferedIsSleeping = 1; + else + mBufferedIsSleeping = 0; +} + +PX_FORCE_INLINE void Body::clearBufferedState() +{ + if(!(getFlags() & PxRigidBodyFlag::eKINEMATIC)) + { + mBufferedIsSleeping = 1; // the expected state when an object gets removed from the scene. + mBodyBufferFlags &= ~(Buf::BF_Acceleration | Buf::BF_DeltaVelocity); + } + else + { + // make sure the buffered properties for a kinematic that is not in a scene are set according to the specification + + // necessary to use the putToSleep method because buffered re-insertion case needs to be covered as well. Currently, re-insertion + // just clears the remove operation. This would prevent the core object parameters to get updated. Thus the operations need + // to be buffered and putToSleepInternal takes care of that. + putToSleepInternal(); + } + + RigidObject::clearBufferedState(); +} + +PX_FORCE_INLINE void Body::clearBufferedSleepStateChange() +{ + mBodyBufferFlags &= ~(Buf::BF_WakeUp | Buf::BF_PutToSleep); +} + +PX_FORCE_INLINE void Body::switchBodyToNoSim() +{ + Scb::Scene* scene = getScbScene(); + + switchToNoSim(true); + + if((!scene) || (!getScbScene()->isPhysicsBuffering())) + { + setBufferedParamsForAsleep(); + mBodyCore.putToSleep(); + } + else + putToSleepInternal(); + + if(scene) + clearSimStateDataForPendingInsert(); +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_FORCE_INLINE void Body::markUpdated(PxU32 flag) +{ + scheduleForUpdate(); + mBodyBufferFlags |= flag; +} + +PX_FORCE_INLINE Ps::IntBool Body::isBuffered(PxU32 flag) const +{ + return Ps::IntBool(mBodyBufferFlags & flag); +} + +PX_INLINE void Body::syncCollisionWriteThroughState() +{ + PxU32 bufferFlags = mBodyBufferFlags; + + //---- + if ((bufferFlags & Buf::BF_LinearVelocity) == 0) + mBufferedLinVelocity = mBodyCore.getLinearVelocity(); + else + { + PX_ASSERT((mBufferedIsSleeping && mBufferedLinVelocity.isZero()) || + (!mBufferedIsSleeping) || + (getControlState() == ControlState::eREMOVE_PENDING)); + PX_ASSERT(mBufferedLinVelocity.isZero() || + ((!mBufferedLinVelocity.isZero()) && (!mBufferedIsSleeping)) || + (getControlState() == ControlState::eREMOVE_PENDING)); + + mBodyCore.setLinearVelocity(mBufferedLinVelocity); + //clear the flag + bufferFlags &= ~Buf::BF_LinearVelocity; + } + + //---- + + if ((bufferFlags & Buf::BF_AngularVelocity) == 0) + mBufferedAngVelocity = mBodyCore.getAngularVelocity(); + else + { + PX_ASSERT((mBufferedIsSleeping && mBufferedAngVelocity.isZero()) || + (!mBufferedIsSleeping) || + (getControlState() == ControlState::eREMOVE_PENDING)); + PX_ASSERT(mBufferedAngVelocity.isZero() || + ((!mBufferedAngVelocity.isZero()) && (!mBufferedIsSleeping)) || + (getControlState() == ControlState::eREMOVE_PENDING)); + + mBodyCore.setAngularVelocity(mBufferedAngVelocity); + //clear the flag + bufferFlags &= ~Buf::BF_AngularVelocity; + } + + //---- + + if(bufferFlags & Buf::BF_KinematicTarget) + { + //don't apply kinematic target unless the actor is kinematic already. setKinematicTarget is write-through properties for split sim but setRigidBodyFlag transition from rigid body to kinematic isn't write-through + if(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC) + { + Buf& buffer = *getBodyBuffer(); + PX_ASSERT(mBufferedWakeCounter > 0.0f); // that is the expected behavior + + mBodyCore.setKinematicTarget(getScbScene()->getScScene().getSimStateDataPool(), buffer.mKinematicTarget, mBufferedWakeCounter); + //clear the flag + bufferFlags &= ~Buf::BF_KinematicTarget; + } + } + + //---- + //in case user call addForce(), collide(), clearForce(), which we need to clear the acclearation in the low-level + if(bufferFlags & Buf::BF_ClearAcceleration) + { + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping); + mBodyCore.clearSpatialAcceleration((bufferFlags & Buf::BF_ClearAccelerationLinear)!=0, (bufferFlags & Buf::BF_ClearAccelerationAngular)!=0); + + //clear the flag, we don't clear the buffered acceleration, because the user might call addForce() again after calling clearForce() + bufferFlags &= ~Buf::BF_ClearAcceleration; + } + + //---- + + //apply addForce/clearForce, addTorque/clearTorque + if(bufferFlags & Buf::BF_Acceleration) + { + Buf& buffer = *getBodyBuffer(); + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping || (buffer.mLinAcceleration.isZero() && buffer.mAngAcceleration.isZero())); + mBodyCore.addSpatialAcceleration(getScbScene()->getScScene().getSimStateDataPool(), &buffer.mLinAcceleration, &buffer.mAngAcceleration); + + //clear the flag + bufferFlags &= ~Buf::BF_Acceleration; + buffer.mLinAcceleration = PxVec3(0.0f); + buffer.mAngAcceleration = PxVec3(0.0f); + } + + //---- + + if(bufferFlags & Buf::BF_ClearDeltaVelocity) + { + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping ); + mBodyCore.clearSpatialVelocity((bufferFlags & Buf::BF_ClearDeltaVelocityLinear)!=0, (bufferFlags & Buf::BF_ClearDeltaVelocityAngular)!=0); + + //clear the flag, we don't clear the buffered velocity, because the user might call addForce() again after calling clearForce() + bufferFlags &= ~Buf::BF_ClearDeltaVelocity; + } + + //---- + + if(bufferFlags & Buf::BF_DeltaVelocity) + { + Buf& buffer = *getBodyBuffer(); + PX_ASSERT(!(mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC)); + PX_ASSERT(!mBufferedIsSleeping || (buffer.mLinDeltaVelocity.isZero() && buffer.mAngDeltaVelocity.isZero())); + mBodyCore.addSpatialVelocity(getScbScene()->getScScene().getSimStateDataPool(), &buffer.mLinDeltaVelocity, &buffer.mAngDeltaVelocity); + + //clear the flag + bufferFlags &= ~Buf::BF_DeltaVelocity; + buffer.mLinDeltaVelocity = PxVec3(0.0f); + buffer.mAngDeltaVelocity = PxVec3(0.0f); + } + + //---- + + if((bufferFlags & Buf::BF_WakeCounter) == 0) + mBufferedWakeCounter = mBodyCore.getWakeCounter(); + else if(!(bufferFlags & (Buf::BF_WakeUp | Buf::BF_PutToSleep))) // if there has been at least one buffered sleep state transition, then there is no use in adjusting the wake counter separately because it will + // get done in the sleep state update. + { + PX_ASSERT((getControlState() == ControlState::eREMOVE_PENDING) || (mBufferedWakeCounter == 0.0f)); // a wake counter change is always connected to a sleep state change, except if setWakeCounter(0.0f) was called or an object gets removed from the scene after it was woken up. + + mBodyCore.setWakeCounter(mBufferedWakeCounter); + + bufferFlags &= ~Buf::BF_WakeCounter; + } + else if(bufferFlags & Buf::BF_WakeUp) + { + Buf& buffer = *getBodyBuffer(); + //Because in the split sim, transition from rigid body to kinematic isn't a write through properties. However, when the user call setKinematicTarget, the SDK wake up the actor so we want to avoid waking up the + //actor if the actor is transitioning from rigid body to kinematic or vice versa. + PxRigidBodyFlags changeFlags= mBodyCore.getFlags() ^ buffer.mRigidBodyFlags; + if(!((bufferFlags & Buf::BF_RigidBodyFlags) && (changeFlags & PxRigidBodyFlag::eKINEMATIC))) + { + PX_ASSERT(bufferFlags & Buf::BF_WakeCounter); // sleep state transitions always mark the wake counter dirty + PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing an object should clear pending wakeUp/putToSleep operations since the state for a free standing object gets set according to specification. + + // The sleep state ends up with the proper result that reflects the order of the original buffered operations because... + // - every operation that affects the sleep state makes a buffered call to wakeUp/putToSleep, hence, the buffered sleep transition + // will always reflect the latest change + // - a user triggered sleep state transition (wakeUp/putToSleep) always adjusts the affected properties (velocity, force, wake counter...) through separate + // buffered calls, hence, those properties will get adjusted to the correct values in the end + // - sleep state sync runs after all calls that have side effects on the sleep state. + // + PX_ASSERT(!mBufferedIsSleeping); + PX_ASSERT(bufferFlags & Buf::BF_WakeUp); + + // can not assert for any values here since it is possible, for example, to end up waking something up with a 0 wakeCounter here (as a consequence of a buffered wakeUp() followed by a setWakeCounter(0)) + + mBodyCore.wakeUp(mBufferedWakeCounter); + + bufferFlags &= ~(Buf::BF_WakeUp | Buf::BF_WakeCounter); + } + } + + //---- + + mBodyBufferFlags = bufferFlags; +} + +PX_INLINE void Body::syncState() +{ + // if the body was removed from the scene, we expect... + // ...it to be marked as sleeping (that is the specification) + // ...no pending sleep state change (because wakeUp/putTosleep is illegal for a free standing object and we clear such operations if pending). + // Note: a sleep state change might have happened before the removal but the corresponding wake counter change is then covered through the BF_WakeCounter dirty flag. + PX_ASSERT( (getControlState() != ControlState::eREMOVE_PENDING) || + (mBufferedIsSleeping && (!isBuffered(Buf::BF_WakeUp | Buf::BF_PutToSleep))) ); + + // + // IMPORTANT: Since we ran out of space for buffered property flags, the Scb::Body property related flags are stored in mBodyBufferFlags. + // To get the buffer flags from the base classes, use getBufferFlags() + // + const PxU32 bufferFlags = mBodyBufferFlags; + const PxU32 baseBufferFlags = getBufferFlags(); + + if((bufferFlags & Buf::BF_Body2World) == 0) + mBufferedBody2World = mBodyCore.getBody2World(); + else if((bufferFlags & Buf::BF_Body2World_CoM) == 0) + mBodyCore.setBody2World(mBufferedBody2World); + else + { + // IMPORTANT: Do this before adjusting body2Actor +#ifdef USE_NEW_SYSTEM + PX_ASSERT(bufferFlags & BF_Body2Actor); +#else + PX_ASSERT(bufferFlags & Buf::BF_Body2Actor); +#endif + Buf& buffer = *getBodyBuffer(); + const PxTransform newBody2oldBody = mBodyCore.getBody2Actor().transformInv(buffer.mBody2Actor); + + PxTransform b2w = mBodyCore.getBody2World(); + b2w = b2w.transform(newBody2oldBody); // translate simulation result from old CoM to new CoM + + mBufferedBody2World = b2w; + mBodyCore.setBody2World(b2w); + } + + //---- + +#ifdef USE_NEW_SYSTEM + if(baseBufferFlags & BF_ActorFlags) +#else + if(baseBufferFlags & Buf::BF_ActorFlags) +#endif + syncNoSimSwitch(*getBodyBuffer(), mBodyCore, true); + + //---- + + if(bufferFlags & ~( Buf::BF_WakeCounter|Buf::BF_Body2World|Buf::BF_LinearVelocity|Buf::BF_AngularVelocity + |Buf::BF_WakeUp|Buf::BF_PutToSleep)) // Optimization to avoid all the if-statements below if possible + { + const Buf& buffer = *getBodyBuffer(); +#ifdef USE_NEW_SYSTEM + syncInverseMass(); + syncInverseInertia(); + syncLinearDamping(); + syncAngularDamping(); + syncMaxAngVelSq(); + syncMaxLinVelSq(); + syncSleepThreshold(); + syncSolverIterationCounts(); + syncContactReportThreshold(); + syncBody2Actor(); + syncFreezeThreshold(); + syncMaxPenetrationBias(); + syncMaxContactImpulse(); + syncCCDAdvanceCoefficient(); +#else + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); +#endif + if(bufferFlags & Buf::BF_RigidBodyFlags) + mBodyCore.setFlags(getScbScene()->getScScene().getSimStateDataPool(), buffer.mRigidBodyFlags); + } + + //This method sync all the write through properties in collision and is called in fetchCollision() + syncCollisionWriteThroughState(); + + //---- + + if((bufferFlags & (Buf::BF_PutToSleep)) == 0) + { + const bool isSimObjectSleeping = mBodyCore.isSleeping(); + if(getControlState() != ControlState::eREMOVE_PENDING) // we do not want to sync the simulation sleep state if the object was removed (free standing objects have buffered state sleeping) + mBufferedIsSleeping = PxU32(isSimObjectSleeping); + else + PX_ASSERT(mBufferedIsSleeping); // this must get set immediately at remove + } + else + { + PX_ASSERT(bufferFlags & Buf::BF_WakeCounter); // sleep state transitions always mark the wake counter dirty + PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing an object should clear pending wakeUp/putToSleep operations since the state for a free standing object gets set according to specification. + + // The sleep state ends up with the proper result that reflects the order of the original buffered operations because... + // - every operation that affects the sleep state makes a buffered call to wakeUp/putToSleep, hence, the buffered sleep transition + // will always reflect the latest change + // - a user triggered sleep state transition (wakeUp/putToSleep) always adjusts the affected properties (velocity, force, wake counter...) through separate + // buffered calls, hence, those properties will get adjusted to the correct values in the end + // - sleep state sync runs after all calls that have side effects on the sleep state. + // + + PX_ASSERT(mBufferedIsSleeping); + PX_ASSERT(!(bufferFlags & Buf::BF_WakeUp)); + PX_ASSERT(mBufferedWakeCounter == 0.0f); + PX_ASSERT(mBufferedLinVelocity.isZero()); + PX_ASSERT(mBufferedAngVelocity.isZero()); + PX_ASSERT(!(bufferFlags & Buf::BF_Acceleration)); + PX_ASSERT(!(bufferFlags & Buf::BF_DeltaVelocity)); + + mBodyCore.putToSleep(); + } + + // PT: we must call this even when there's no buffered data + RigidObject::syncState(); + + // -------------- + // postconditions + // + PX_ASSERT((getControlState() != ControlState::eREMOVE_PENDING) || mBufferedIsSleeping); // nothing in this method should change this +#ifdef _DEBUG + // make sure that for a removed kinematic, the buffered params hold the values as defined in our specification + if((mBodyCore.getFlags() & PxRigidBodyFlag::eKINEMATIC) && (getControlState() == ControlState::eREMOVE_PENDING)) + { + PX_ASSERT(mBufferedLinVelocity.isZero()); + PX_ASSERT(mBufferedAngVelocity.isZero()); + PX_ASSERT(mBufferedWakeCounter == 0.0f); + } +#endif + // + // postconditions + // -------------- + + postSyncState(); + mBodyBufferFlags = 0; +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h b/src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h new file mode 100644 index 000000000..129f68f8b --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h @@ -0,0 +1,313 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_CONSTRAINTSHADER +#define PX_PHYSICS_SCB_CONSTRAINTSHADER + +#include "CmPhysXCommon.h" +#include "../../../simulationcontroller/include/ScConstraintCore.h" + +#include "ScbBody.h" + +namespace physx +{ + +namespace Sc +{ + class RigidCore; +} + +namespace Scb +{ + +struct ConstraintBuffer +{ +public: + Sc::RigidCore* rigids[2]; + PxReal linBreakForce; + PxReal angBreakForce; + PxConstraintFlags flags; + PxReal minResponseThreshold; +}; + +enum ConstraintBufferFlag +{ + BF_BODIES = (1 << 0), + BF_BREAK_IMPULSE = (1 << 1), + BF_FLAGS = (1 << 2), + BF_MIN_RESPONSE_THRESHOLD = (1 << 3) +}; + +class Constraint : public Base, public Ps::UserAllocated +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + +public: + + typedef ConstraintBuffer Buf; + typedef Sc::ConstraintCore Core; + +// PX_SERIALIZATION + Constraint(const PxEMPTY) : Base(PxEmpty), mConstraint(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + PX_INLINE Constraint(PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize); + PX_INLINE ~Constraint() {} + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::ConstraintCore interface + //--------------------------------------------------------------------------------- + + PX_INLINE PxConstraint* getPxConstraint() const; + PX_INLINE PxConstraintConnector* getPxConnector() const; + + PX_INLINE void setFlags(PxConstraintFlags f); + PX_INLINE PxConstraintFlags getFlags() const; + + PX_INLINE void setBodies(Scb::RigidObject* r0, Scb::RigidObject* r1); + + PX_INLINE void getForce(PxVec3& force, PxVec3& torque) const; + + PX_INLINE void setBreakForce(PxReal linear, PxReal angular); + PX_INLINE void getBreakForce(PxReal& linear, PxReal& angular) const; + + PX_INLINE void setMinResponseThreshold(PxReal threshold); + PX_INLINE PxReal getMinResponseThreshold() const; + + PX_INLINE bool updateConstants(void* addr); + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void prepareForActorRemoval(); + PX_INLINE void syncState(); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE const Core& getScConstraint() const { return mConstraint; } // Only use if you know what you're doing! + PX_FORCE_INLINE Core& getScConstraint() { return mConstraint; } // Only use if you know what you're doing! + + PX_FORCE_INLINE static Constraint& fromSc(Core &a) { return *reinterpret_cast(reinterpret_cast(&a)-getScOffset()); } + PX_FORCE_INLINE static const Constraint& fromSc(const Core &a) { return *reinterpret_cast(reinterpret_cast(&a)-getScOffset()); } + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mConstraint); } + +private: + Core mConstraint; + + //--------------------------------------------------------------------------------- + // Permanently buffered data (simulation written data) + //--------------------------------------------------------------------------------- + PxVec3 mBufferedForce; + PxVec3 mBufferedTorque; + PxConstraintFlags mBrokenFlag; + + PX_FORCE_INLINE const Buf* getBufferedData() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Buf* getBufferedData() { return reinterpret_cast(getStream()); } +}; + +} // namespace Scb + +PX_INLINE Scb::Constraint::Constraint(PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) : + mConstraint (connector, shaders, dataSize), + mBufferedForce (0.0f), + mBufferedTorque (0.0f), + mBrokenFlag (0) +{ + setScbType(ScbType::eCONSTRAINT); +} + +PX_INLINE PxConstraintConnector* Scb::Constraint::getPxConnector() const +{ + return mConstraint.getPxConnector(); +} + +PX_INLINE void Scb::Constraint::setFlags(PxConstraintFlags f) +{ + if(!isBuffering()) + { + mConstraint.setFlags(f); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + getBufferedData()->flags = f; + markUpdated(BF_FLAGS); + } +} + +PX_INLINE PxConstraintFlags Scb::Constraint::getFlags() const +{ + return isBuffered(BF_FLAGS) ? getBufferedData()->flags & (~(PxConstraintFlag::eBROKEN | PxConstraintFlag::eGPU_COMPATIBLE) | mBrokenFlag) + : mConstraint.getFlags() & (~(PxConstraintFlag::eBROKEN | PxConstraintFlag::eGPU_COMPATIBLE) | mBrokenFlag); +} + +PX_INLINE void Scb::Constraint::setBodies(Scb::RigidObject* r0, Scb::RigidObject* r1) +{ + Sc::RigidCore* scR0 = r0 ? &r0->getScRigidCore() : NULL; + Sc::RigidCore* scR1 = r1 ? &r1->getScRigidCore() : NULL; + + if(!isBuffering()) + { + mConstraint.prepareForSetBodies(); + mConstraint.setBodies(scR0, scR1); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* PX_RESTRICT bufferedData = getBufferedData(); + bufferedData->rigids[0] = scR0; + bufferedData->rigids[1] = scR1; + markUpdated(BF_BODIES); + } + + mBufferedForce = PxVec3(0.0f); + mBufferedTorque = PxVec3(0.0f); +} + +PX_INLINE void Scb::Constraint::getForce(PxVec3& force, PxVec3& torque) const +{ + force = mBufferedForce; + torque = mBufferedTorque; +} + +PX_INLINE void Scb::Constraint::setBreakForce(PxReal linear, PxReal angular) +{ + if(!isBuffering()) + { + mConstraint.setBreakForce(linear, angular); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* PX_RESTRICT bufferedData = getBufferedData(); + bufferedData->linBreakForce = linear; + bufferedData->angBreakForce = angular; + markUpdated(BF_BREAK_IMPULSE); + } +} + +PX_INLINE void Scb::Constraint::getBreakForce(PxReal& linear, PxReal& angular) const +{ + if(isBuffered(BF_BREAK_IMPULSE)) + { + const Buf* PX_RESTRICT bufferedData = getBufferedData(); + linear = bufferedData->linBreakForce; + angular = bufferedData->angBreakForce; + } + else + mConstraint.getBreakForce(linear, angular); +} + +PX_INLINE void Scb::Constraint::setMinResponseThreshold(PxReal threshold) +{ + if(!isBuffering()) + { + mConstraint.setMinResponseThreshold(threshold); + UPDATE_PVD_PROPERTIES_OBJECT() + } + else + { + Buf* PX_RESTRICT bufferedData = getBufferedData(); + bufferedData->minResponseThreshold = threshold; + markUpdated(BF_MIN_RESPONSE_THRESHOLD); + } +} + +PX_INLINE PxReal Scb::Constraint::getMinResponseThreshold() const +{ + if(isBuffered(BF_MIN_RESPONSE_THRESHOLD)) + { + const Buf* PX_RESTRICT bufferedData = getBufferedData(); + return bufferedData->minResponseThreshold; + } + else + return mConstraint.getMinResponseThreshold(); +} + +PX_INLINE bool Scb::Constraint::updateConstants(void* addr) +{ + PX_ASSERT(!getScbScene()->isPhysicsBuffering()); + + return mConstraint.updateConstants(addr); +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- + +PX_INLINE void Scb::Constraint::prepareForActorRemoval() +{ + // when the bodies of a constraint have been changed during buffering, it's possible the + // attached actor is going to get deleted. Sc expects that all interactions with that actor + // will have been removed, so we give the Sc::Constraint a chance to ensure that before + // the actors go away. + if(getBufferFlags() & BF_BODIES) + mConstraint.prepareForSetBodies(); +} + +PX_INLINE void Scb::Constraint::syncState() +{ + //!!! Force has to be synced every frame (might want to have a list of active constraint shaders?) + mConstraint.getForce(mBufferedForce, mBufferedTorque); + + mBrokenFlag = mConstraint.getFlags() & PxConstraintFlag::eBROKEN; + + const PxU32 flags = getBufferFlags(); + if(flags) + { + const Buf* PX_RESTRICT bufferedData = getBufferedData(); + + if(flags & BF_BODIES) + mConstraint.setBodies(bufferedData->rigids[0], bufferedData->rigids[1]); + + if(flags & BF_BREAK_IMPULSE) + mConstraint.setBreakForce(bufferedData->linBreakForce, bufferedData->angBreakForce); + + if(flags & BF_MIN_RESPONSE_THRESHOLD) + mConstraint.setMinResponseThreshold(bufferedData->minResponseThreshold); + + if(flags & BF_FLAGS) + mConstraint.setFlags(bufferedData->flags | mBrokenFlag); + } + + postSyncState(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbDefs.h b/src/PhysX/physx/source/physx/src/buffering/ScbDefs.h new file mode 100644 index 000000000..d8c8c1111 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbDefs.h @@ -0,0 +1,210 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_DEFS +#define PX_PHYSICS_SCB_DEFS + +#include "ScbBase.h" + +//#define USE_NEW_SYSTEM + +#ifdef USE_NEW_SYSTEM +namespace physx +{ +namespace Scb +{ + template + void setValueT(ValueType value, CoreType& core, SCBType& scb) + { + if(!scb.isBuffering()) + { + AccessType::setCore(core, value); +#if PX_SUPPORT_PVD + if(scb.getControlState() == ControlState::eIN_SCENE) + { + Scb::Scene* scene = scb.getScbScene(); + PX_ASSERT(scene); + scene->getScenePvdClient().updatePvdProperties(&scb); + } +#endif + } + else + { + AccessType::setBuffered(scb, value); + scb.markUpdated(1< + ValueType getValueT(const CoreType& core, const SCBType& scb) + { + if(!scb.isBuffered(1< + void syncT(CoreType& core, SCBType& scb) + { + if(scb.isBuffered(1<(getStream())->m##_name = v; } \ + PX_INLINE _type getBuffered##_name() const { return reinterpret_cast(getStream())->m##_name; } \ + struct Fns_##_name \ + { \ + static PX_INLINE void setCore(Core& core, _type v) { core.set##_name(v); } \ + static PX_INLINE void setBuffered(_scb& scb, _type v) { scb.setBuffered##_name(v); } \ + static PX_INLINE _type getCore(const Core& core) { return core.get##_name(); } \ + static PX_INLINE _type getBuffered(const _scb& scb) { return scb.getBuffered##_name(); } \ + }; \ + PX_INLINE void set##_name(_type v) { setValueT<_type, Core, _scb, Fns_##_name, _id>(v, _getCore, *this); } \ + PX_INLINE _type get##_name() const { return getValueT<_type, Core, _scb, Fns_##_name, _id>(_getCore, *this); } \ + PX_INLINE void sync##_name() { syncT(_getCore, *this); } +} +} +#else + +// a Regular attribute of type T is one for which +// * the SC method takes a single argument of type ArgType (defined below) +// * Scb either passes that argument through, or dumps it in a buffer to flush later. +// * PVD is notified when the variable changes +// +// For each such, we can define static methods to read and write the core and buffered variables, +// and capture the buffering logic in the BufferedAccess class. +// +// The dummy arg is necessary here because ISO permits partial specialization of member templates +// but not full specialization. +// +// putting just accessors and mutators here allows us to change the behavior just by varying the +// BufferAccess template (e.g. to compile without buffering), and also to size-reduce that template +// by passing function pointers if necessary + +#define SCB_REGULAR_ATTRIBUTE(_val, _type, _name) \ +enum { BF_##_name = 1<<(_val) }; \ +_type m##_name; \ +template struct Fns<1<<(_val),Dummy> \ +{ \ + typedef typename ArgType<_type>::Type Arg; \ + enum { flag = 1<<(_val) }; \ + static PX_FORCE_INLINE Arg getBuffered(const Buf& buf) { return Arg(buf.m##_name);} \ + static PX_FORCE_INLINE void setBuffered(Buf& buf, Arg v) { buf.m##_name = v;} \ + static PX_FORCE_INLINE Arg getCore(const Core& core) { return Arg(core.get##_name());} \ + static PX_FORCE_INLINE void setCore(Core& core, Arg v) { core.set##_name(v);} \ +}; + +#define SCB_REGULAR_ATTRIBUTE_ALIGNED(_val, _type, _name, _alignment) \ +enum { BF_##_name = 1<<(_val) }; \ +PX_ALIGN(_alignment, _type) m##_name; \ +template struct Fns<1<<(_val),Dummy> \ +{ \ + typedef typename ArgType<_type>::Type Arg; \ + enum { flag = 1<<(_val) }; \ + static PX_FORCE_INLINE Arg getBuffered(const Buf& buf) { return buf.m##_name;} \ + static PX_FORCE_INLINE void setBuffered(Buf& buf, Arg v) { buf.m##_name = v;} \ + static PX_FORCE_INLINE Arg getCore(const Core& core) { return core.get##_name();} \ + static PX_FORCE_INLINE void setCore(Core& core, Arg v) { core.set##_name(v);} \ +}; + +namespace physx +{ +namespace Scb +{ +class Scene; + +template struct ArgType { typedef T Type; }; +template<> struct ArgType { typedef const PxVec3& Type; }; +template<> struct ArgType { typedef const PxTransform& Type; }; +template<> struct ArgType { typedef const PxQuat& Type; }; +template<> struct ArgType { typedef const PxPlane& Type; }; +template<> struct ArgType { typedef const PxFilterData& Type; }; + +// TODO: should be able to size-reduce this if necessary by just generating one set per +// arg type instead of one per arg, by passing function pointers to the accessors/mutators/flag +// instead of instancing per type. + +template // BaseClass: introduced to have Scb::Body use custom location for storing buffered property flags +struct BufferedAccess +{ + template + static PX_FORCE_INLINE typename Fns::Arg read(const BaseClass& base, const Core& core) + { + /*return base.isBuffered(Fns::flag) ? Fns::getBuffered(*reinterpret_cast(base.getStream())) + : Fns::getCore(core);*/ + + if (base.isBuffered(Fns::flag)) + { + return Fns::getBuffered(*reinterpret_cast(base.getStream())); + } + return Fns::getCore(core); + + } + + template + static PX_FORCE_INLINE void write(BaseClass& base, Core& core, typename Fns::Arg v) + { + if(!base.isBuffering()) + { + Fns::setCore(core, v); +#if PX_SUPPORT_PVD + if(base.getControlState() == ControlState::eIN_SCENE) + { + Scb::Scene* scene = base.getScbScene(); + PX_ASSERT(scene); + scene->getScenePvdClient().updatePvdProperties(static_cast(&base)); + } +#endif + } + else + { + Fns::setBuffered(*reinterpret_cast(base.getStream()), v); + base.markUpdated(Fns::flag); + } + } + + template + static PX_FORCE_INLINE void flush(const BaseClass& base, Core& core, const Buf& buf) + { + if(base.isBuffered(Fns::flag)) + Fns::setCore(core, Fns::getBuffered(buf)); + } +}; + +} +} +#endif + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp new file mode 100644 index 000000000..fe498ee04 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp @@ -0,0 +1,169 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "ScbShape.h" +#include "ScbBody.h" +#include "ScbRigidStatic.h" +#include "ScbConstraint.h" +#include "ScbArticulation.h" +#include "ScbArticulationJoint.h" +#include "ScbAggregate.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Base::getBinaryMetaData(PxOutputStream& stream) +{ + // 28 => 12 bytes + PX_DEF_BIN_METADATA_TYPEDEF(stream, ScbType::Enum, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Base) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Base, Scb::Scene, mScene, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Base, PxU32, mControlState, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Base, PxU8*, mStreamPtr, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Shape::getBinaryMetaData(PxOutputStream& stream) +{ + // 176 => 160 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Shape) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Shape, Scb::Base) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Shape, ShapeCore, mShape, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Actor::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Actor) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Actor, Scb::Base) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::RigidObject::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::RigidObject) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::RigidObject, Scb::Actor) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Body::getBinaryMetaData(PxOutputStream& stream) +{ + // 240 => 224 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Body) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Body, Scb::RigidObject) + +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxU32, mPaddingScbBody1, PxMetaDataFlag::ePADDING) +#endif + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, Sc::BodyCore, mBodyCore, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxTransform, mBufferedBody2World, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxVec3, mBufferedLinVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxVec3, mBufferedAngVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxReal, mBufferedWakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxU32, mBufferedIsSleeping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Body, PxU32, mBodyBufferFlags, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::RigidStatic::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::RigidStatic) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::RigidStatic, Scb::RigidObject) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::RigidStatic, Sc::StaticCore, mStatic, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Articulation::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Articulation) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Articulation, Scb::Base) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Articulation, ArticulationCore, mArticulation, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Articulation, PxReal, mBufferedWakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Articulation, PxU8, mBufferedIsSleeping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Articulation, PxArticulationFlags, mBufferedArticulationFlags, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::ArticulationJoint::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::ArticulationJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::ArticulationJoint, Scb::Base) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::ArticulationJoint, ArticulationJointCore, mJoint, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Constraint::getBinaryMetaData(PxOutputStream& stream) +{ + // 120 => 108 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Constraint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Constraint, Scb::Base) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Constraint, ConstraintCore, mConstraint, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Constraint, PxVec3, mBufferedForce, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Constraint, PxVec3, mBufferedTorque, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Constraint, PxConstraintFlags, mBrokenFlag, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Constraint, PxU16, mPaddingFromBrokenFlags, PxMetaDataFlag::ePADDING) +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Aggregate::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Scb::Aggregate) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Scb::Aggregate, Scb::Base) + + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Aggregate, PxAggregate,mPxAggregate, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Aggregate, PxU32, mAggregateID, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Aggregate, PxU32, mMaxNbActors, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Scb::Aggregate, bool, mSelfCollide, 0) + +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Scb::Aggregate, bool, mPaddingFromBool, PxMetaDataFlag::ePADDING) +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h b/src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h new file mode 100644 index 000000000..e0868a82d --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCB_NPDEPS +#define PX_PHYSICS_SCB_NPDEPS + +namespace physx +{ + +// The Scb layer needs to delete the owning Np objects, but we don't want to include the Np headers +// necessary to find their addresses. So we use link-level dependencies instead. + +namespace Scb +{ + class Base; + class Shape; + class RigidObject; + class Constraint; + class Scene; + class ArticulationJoint; + class Articulation; + class RigidStatic; + class Body; +} + +namespace Sc +{ + class RigidCore; +} + +class PxScene; + +extern void NpDestroy(Scb::Base&); + +// we want to get the pointer to the rigid object that owns a shape, and the two actor pointers for a constraint, so that we don't +// duplicate the scene graph in Scb + +extern PxU32 NpRigidStaticGetShapes(Scb::RigidStatic& rigid, void* const *&shapes); +extern PxU32 NpRigidDynamicGetShapes(Scb::Body& body, void* const *&shapes, bool* isCompound = NULL); +extern size_t NpShapeGetScPtrOffset(); +extern void NpShapeIncRefCount(Scb::Shape& shape); +extern void NpShapeDecRefCount(Scb::Shape& shape); + +extern Sc::RigidCore* NpShapeGetScRigidObjectFromScbSLOW(const Scb::Shape &); +extern void NpConstraintGetRigidObjectsFromScb(const Scb::Constraint&, Scb::RigidObject*&, Scb::RigidObject*&); +extern void NpArticulationJointGetBodiesFromScb(Scb::ArticulationJoint&, Scb::Body*&, Scb::Body*&); +extern Scb::Body* NpArticulationGetRootFromScb(Scb::Articulation&); +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h b/src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h new file mode 100644 index 000000000..f4cc38003 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h @@ -0,0 +1,511 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_RIGID_OBJECT +#define PX_PHYSICS_SCB_RIGID_OBJECT + +#include "../../simulationcontroller/include/ScRigidCore.h" +#include "ScbScene.h" +#include "ScbActor.h" +#include "ScbShape.h" +#include "PsInlineArray.h" + +namespace physx +{ + +// base class for dynamic and static rigid objects, so that shapes can have something to refer to + +namespace Scb +{ +struct RemovedShape +{ + RemovedShape() : mShape(NULL), mWakeTouching(0) {} + RemovedShape(Scb::Shape* s, PxU8 wakeTouching) : mShape(s), mWakeTouching(wakeTouching) {} + + PX_FORCE_INLINE bool operator == (const RemovedShape& other) const + { + return (mShape == other.mShape); + } + + PX_FORCE_INLINE bool operator != (const RemovedShape& other) const + { + return (mShape != other.mShape); + } + + Scb::Shape* mShape; + PxU8 mWakeTouching; +}; + +struct RigidObjectBuffer : public ActorBuffer //once RigidObject has its own buffered elements, derive from that instead +{ + RigidObjectBuffer(): mResetFilterShape(0), mResetFilterShapeCount(0) {} + + // TODO(dsequeira): ideally we would use an allocator that allocates from the buffered memory stream + Ps::InlineArray mAddedShapes; + Ps::InlineArray mRemovedShapes; + union + { + PxU32 mResetFilterShapesIdx; + Scb::Shape* mResetFilterShape; + }; + PxU32 mResetFilterShapeCount; + + enum { BF_Base = ActorBuffer::AttrCount }; + + enum + { + BF_Shapes = 1<mRemovedShapes.size();i++) + { + RemovedShape& rs = b->mRemovedShapes[i]; + Shape& shape = *rs.mShape; + shape.setControlStateIfExclusive(NULL, Scb::ControlState::eNOT_IN_SCENE); + + Sc::RigidCore& rc = getScRigidCore(); + Scb::Scene* scene = getScbScene(); +#if PX_SUPPORT_PVD + scene->getScenePvdClient().releasePvdInstance(&shape, pxActor); +#endif + if(!isSimDisabledInternally()) + { + rc.removeShapeFromScene(shape.getScShape(), (rs.mWakeTouching != 0)); + + shape.checkUpdateOnRemove(scene); + + NpShapeDecRefCount(shape); + } + } + } + + // The array of removed shapes must be reset to avoid memory leaks. + b->mRemovedShapes.reset(); + } + } + + PX_INLINE void syncState() + { + const PxU32 bufferFlags = getBufferFlags(); + + if(bufferFlags & Buf::BF_ResetFiltering) + { + PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing the actor should have cleared BF_ResetFiltering + + Scb::Scene* scene = getScbScene(); + Sc::RigidCore& scCore = getScRigidCore(); + RigidObjectBuffer* b = getBuffer(); + Scb::Shape* const* shapes = (b->mResetFilterShapeCount == 1) ? &b->mResetFilterShape : scene->getShapeBuffer(b->mResetFilterShapesIdx); + for(PxU32 i=0; imResetFilterShapeCount; i++) + { + Sc::ShapeCore& scShape = shapes[i]->getScShape(); + + // do not process the call if the shape will not be a broadphase shape any longer + if(shapes[i]->getFlags() & (PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) + scCore.onShapeChange(scShape, Sc::ShapeChangeNotifyFlag::eRESET_FILTERING, PxShapeFlags()); + } + } + + if(bufferFlags & Buf::BF_Shapes) + { + RigidObjectBuffer* b = getBuffer(); + ControlState::Enum cs = getControlState(); +#if PX_SUPPORT_PVD + PxActor& pxActor = *getScRigidCore().getPxActor(); +#endif + for(PxU32 i=0;imAddedShapes.size();i++) + { + Shape& shape = *b->mAddedShapes[i]; + + // it can happen that a shape gets attached while the sim is running but then the actor is removed from the scene, + // so we need to distinguish those two cases + if(cs != ControlState::eREMOVE_PENDING) + { + shape.setControlStateIfExclusive(getScbScene(), Scb::ControlState::eIN_SCENE); + + if(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) // important to use the buffered flags since we want the new state. + { + getScRigidCore().addShapeToScene(shape.getScShape()); + NpShapeIncRefCount(shape); + } +#if PX_SUPPORT_PVD + getScbScene()->getScenePvdClient().createPvdInstance(&shape, pxActor); +#endif + } + else + shape.setControlStateIfExclusive(getScbScene(), Scb::ControlState::eNOT_IN_SCENE); + } + + // reset the arrays, because destructors don't run on buffers + b->mAddedShapes.reset(); + } + + Actor::syncState(); + } + + PX_FORCE_INLINE void scheduleForWakeTouching() + { + PX_ASSERT(getScbScene() && getScbScene()->isPhysicsBuffering()); + setBufferFlag(RigidObjectBuffer::BF_WakeTouching); + } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- +public: + PX_INLINE const Sc::RigidCore& getScRigidCore() const { return static_cast(getActorCore()); } // Only use if you know what you're doing! + PX_INLINE Sc::RigidCore& getScRigidCore() { return static_cast(getActorCore()); } // Only use if you know what you're doing! + + PX_INLINE void onShapeAttach(Scb::Shape& shape) + { + // there are two things to do here: add the shape to the sim (if unbuffered) or set it up for + // * if unbuffered, add the shape to the sim and PVD and increment its refcount, else set it up for buffered insertion, + // * if the shape is exclusive, set its Scb control state appropriately. + + ControlState::Enum cs = getControlState(); + if(cs==ControlState::eNOT_IN_SCENE) + return; + + Scene* scbScene = getScbScene(); + if(!scbScene->isPhysicsBuffering()) + { + if(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + { + NpShapeIncRefCount(shape); + getScRigidCore().addShapeToScene(shape.getScShape()); + } + +#if PX_SUPPORT_PVD + scbScene->getScenePvdClient().createPvdInstance(&shape, *getScRigidCore().getPxActor()); +#endif + shape.setControlStateIfExclusive(scbScene, ControlState::eIN_SCENE); + return; + } + else if(cs == ControlState::eINSERT_PENDING) + { + shape.setControlStateIfExclusive(scbScene, ControlState::eINSERT_PENDING); + return; + } + + RigidObjectBuffer* b = getBuffer(); + if(!b->mRemovedShapes.findAndReplaceWithLast(RemovedShape(&shape, 0))) + b->mAddedShapes.pushBack(&shape); + markUpdated(Buf::BF_Shapes); + + shape.setControlStateIfExclusive(scbScene, ControlState::eINSERT_PENDING); + } + + PX_INLINE void onShapeDetach(Scb::Shape& shape, bool wakeOnLostTouch, bool toBeReleased) + { + // see comments in onShapeAttach + ControlState::Enum cs = getControlState(); + if(cs==ControlState::eNOT_IN_SCENE) + return; + + Scene* scbScene = getScbScene(); + if(!scbScene->isPhysicsBuffering()) + { +#if PX_SUPPORT_PVD + scbScene->getScenePvdClient().releasePvdInstance(&shape, *getScRigidCore().getPxActor()); +#endif + if(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + { + getScRigidCore().removeShapeFromScene(shape.getScShape(), wakeOnLostTouch); + NpShapeDecRefCount(shape); + } + + shape.setControlStateIfExclusive(NULL, ControlState::eNOT_IN_SCENE); + return; + } + else if(cs == ControlState::eINSERT_PENDING) + { + shape.setControlStateIfExclusive(NULL, ControlState::eNOT_IN_SCENE); + return; + } + + RigidObjectBuffer* b = getBuffer(); + + // remove from the resetFiltering list + const PxU32 bufferFlags = getBufferFlags(); + if(bufferFlags & Buf::BF_ResetFiltering) + { + if(b->mResetFilterShapeCount == 1) + { + if(b->mResetFilterShape == &shape) + { + b->mResetFilterShapeCount = 0; + b->mResetFilterShape = 0; + resetBufferFlag(Buf::BF_ResetFiltering); + } + } + else + { + Scb::Shape** shapes = scbScene->getShapeBuffer(b->mResetFilterShapesIdx); + PxU32 idx = 0; + PxU32 lastIdx = b->mResetFilterShapeCount; + for(PxU32 k=0; k < b->mResetFilterShapeCount; k++) // need to iterate over whole list, same shape can be in there multiple times + { + if(shapes[idx] != &shape) + idx++; + else + { + lastIdx--; + shapes[idx] = shapes[lastIdx]; + } + } + b->mResetFilterShapeCount = idx; + if(idx == 0) + { + b->mResetFilterShape = 0; + resetBufferFlag(Buf::BF_ResetFiltering); + } + else if(idx == 1) + b->mResetFilterShape = shapes[0]; + } + } + + if(b->mAddedShapes.findAndReplaceWithLast(&shape)) + shape.setControlStateIfExclusive(scbScene, ControlState::eIN_SCENE); + else + { + if(!isSimDisabledInternally()) + { + b->mRemovedShapes.pushBack(RemovedShape(&shape, PxU8(wakeOnLostTouch ? 1 : 0))); + } + else + { + PX_ASSERT(scbScene); + PX_ASSERT(scbScene->isPhysicsBuffering()); + if(toBeReleased) + { + shape.checkUpdateOnRemove(scbScene); +#if PX_SUPPORT_PVD + scbScene->getScenePvdClient().releasePvdInstance(&shape, *getScRigidCore().getPxActor()); +#endif + } + else + b->mRemovedShapes.pushBack(RemovedShape(&shape, 0)); + } + shape.setControlStateIfExclusive(scbScene, ControlState::eREMOVE_PENDING); + } + markUpdated(Buf::BF_Shapes); + } + + PX_INLINE bool isAddedShape(Scb::Shape&); // check whether the specified shape is pending for insertion. Only call this method if you know that there are pending shape adds/removes. + + PX_FORCE_INLINE void switchToNoSim(bool isDynamic); + PX_FORCE_INLINE void switchFromNoSim(bool isDynamic); + PX_FORCE_INLINE void syncNoSimSwitch(const Buf& buf, Sc::RigidCore& rc, bool isDynamic); + + // IMPORTANT: This is the non-buffered state, for the case where it is important to know what the current internal state is. + // Reading is fine even if the sim is running because actor flags are read-only internally. + PX_FORCE_INLINE bool isSimDisabledInternally() const { return getScRigidCore().getActorFlags().isSet(PxActorFlag::eDISABLE_SIMULATION); } + + PX_FORCE_INLINE void clearBufferedState() { resetBufferFlag(Buf::BF_ResetFiltering); } + + PX_FORCE_INLINE static const RigidObject& fromSc(const Sc::RigidCore& a) { return static_cast(Actor::fromSc(a)); } + PX_FORCE_INLINE static RigidObject& fromSc(Sc::RigidCore &a) { return static_cast(Actor::fromSc(a)); } +protected: + ~RigidObject() {} +private: + Buf* getBuffer() { return reinterpret_cast(getStream()); } + + PX_FORCE_INLINE void copyResetFilterShapes(Scb::Shape** shapePtrs, Scb::Shape*const* oldShapes, PxU32 oldShapeCount, Scb::Shape*const* newShapes, PxU32 newShapeCount); +}; + +PX_INLINE void RigidObject::resetFiltering(Scb::Shape*const* shapes, PxU32 shapeCount) +{ + PX_ASSERT(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)); + + if(!isBuffering()) + { + for(PxU32 i=0; i < shapeCount; i++) + getScRigidCore().onShapeChange(shapes[i]->getScShape(), Sc::ShapeChangeNotifyFlag::eRESET_FILTERING, PxShapeFlags()); + } + else + { + RigidObjectBuffer* b = getBuffer(); + + if(b->mResetFilterShapeCount == 0) + { + if(shapeCount == 1) + { + b->mResetFilterShape = shapes[0]; + b->mResetFilterShapeCount = 1; + markUpdated(Buf::BF_ResetFiltering); + } + else + { + PxU32 bufferIdx; + Scb::Shape** shapePtrs = getScbScene()->allocShapeBuffer(shapeCount, bufferIdx); + if(shapePtrs) + { + for(PxU32 i=0; i < shapeCount; i++) + shapePtrs[i] = shapes[i]; + b->mResetFilterShapesIdx = bufferIdx; + b->mResetFilterShapeCount = shapeCount; + markUpdated(Buf::BF_ResetFiltering); + } + } + } + else + { + PxU32 newCount = b->mResetFilterShapeCount + shapeCount; + PxU32 bufferIdx; + Scb::Shape** shapePtrs = getScbScene()->allocShapeBuffer(newCount, bufferIdx); + if(shapePtrs) + { + if(b->mResetFilterShapeCount == 1) + copyResetFilterShapes(shapePtrs, &b->mResetFilterShape, 1, shapes, shapeCount); + else + copyResetFilterShapes(shapePtrs, getScbScene()->getShapeBuffer(b->mResetFilterShapesIdx), b->mResetFilterShapeCount, shapes, shapeCount); + b->mResetFilterShapesIdx = bufferIdx; + b->mResetFilterShapeCount = newCount; + markUpdated(Buf::BF_ResetFiltering); + } + } + } +} + +PX_INLINE bool RigidObject::isAddedShape(Scb::Shape& shape) +{ + PX_ASSERT(isBuffered(Buf::BF_Shapes)); + + if(shape.isExclusive()) + { + return (shape.getControlState() == Scb::ControlState::eINSERT_PENDING); + } + else + { + // For shared shapes it is not clear from the shape alone whether it has been added while the simulation was running. + + RigidObjectBuffer* buf = getBuffer(); + PX_ASSERT(buf); + const PxU32 addedShapeCount = buf->mAddedShapes.size(); + for(PxU32 k=0; k < addedShapeCount; k++) + { + if(&shape == buf->mAddedShapes[k]) + return true; + } + return false; + } +} + +PX_FORCE_INLINE void RigidObject::switchToNoSim(bool isDynamic) +{ + Scb::Scene* scene = getScbScene(); + + if(scene && (!scene->isPhysicsBuffering())) + scene->switchRigidToNoSim(*this, isDynamic); +} + +PX_FORCE_INLINE void RigidObject::switchFromNoSim(bool isDynamic) +{ + Scb::Scene* scene = getScbScene(); + + if(scene && (!scene->isPhysicsBuffering())) + scene->switchRigidFromNoSim(*this, isDynamic); +} + +PX_FORCE_INLINE void RigidObject::syncNoSimSwitch(const Buf& buf, Sc::RigidCore& rc, bool isDynamic) +{ + const PxActorFlags oldFlags = rc.getActorFlags(); + const bool oldNoSim = oldFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + const bool newNoSim = buf.mActorFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); + + if(oldNoSim && (!newNoSim)) + getScbScene()->switchRigidFromNoSim(*this, isDynamic); + else if((!oldNoSim) && newNoSim) + getScbScene()->switchRigidToNoSim(*this, isDynamic); +} + +PX_FORCE_INLINE void RigidObject::copyResetFilterShapes(Scb::Shape** shapePtrs, Scb::Shape*const* oldShapes, PxU32 oldShapeCount, Scb::Shape*const* newShapes, PxU32 newShapeCount) +{ + for(PxU32 i=0; i < oldShapeCount; i++) + shapePtrs[i] = oldShapes[i]; + for(PxU32 i=0; i < newShapeCount; i++) + shapePtrs[i+oldShapeCount] = newShapes[i]; +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h b/src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h new file mode 100644 index 000000000..7589faab3 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h @@ -0,0 +1,163 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_RIGID_STATIC +#define PX_PHYSICS_SCB_RIGID_STATIC + +#include "ScStaticCore.h" +#include "ScbScene.h" +#include "ScbActor.h" +#include "ScbRigidObject.h" + +namespace physx +{ +namespace Scb +{ +#if PX_VC + #pragma warning(push) + #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif + +struct RigidStaticBuffer : public RigidObjectBuffer +{ +#ifdef USE_NEW_SYSTEM + PX_ALIGN(16, PxTransform) mActor2World; +#else + template struct Fns {}; // TODO: make the base class traits visible + typedef Sc::StaticCore Core; + typedef RigidStaticBuffer Buf; + + // regular attributes + enum { BF_Base = RigidObjectBuffer::AttrCount }; +// SCB_REGULAR_ATTRIBUTE(BF_Base, PxTransform, Actor2World) + SCB_REGULAR_ATTRIBUTE_ALIGNED(BF_Base, PxTransform, Actor2World, 16) +#endif +}; + +#if PX_VC + #pragma warning(pop) +#endif + +class RigidStatic : public Scb::RigidObject +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef RigidStaticBuffer Buf; + typedef Sc::StaticCore Core; + +public: +// PX_SERIALIZATION + RigidStatic(const PxEMPTY) : Scb::RigidObject(PxEmpty), mStatic(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + PX_INLINE RigidStatic(const PxTransform& actor2World); + PX_INLINE ~RigidStatic() {} + +#ifdef USE_NEW_SYSTEM + SCB_MEMBER(RigidStatic, mStatic, Actor2World, const PxTransform&, RigidObjectBuffer::AttrCount) +#else + PX_INLINE const PxTransform& getActor2World() const { return read(); } + PX_INLINE void setActor2World(const PxTransform& m) { write(m); } +#endif + + PX_FORCE_INLINE void onOriginShift(const PxVec3& shift) { mStatic.onOriginShift(shift); } + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + PX_INLINE void syncState(); + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mStatic); } + + PX_FORCE_INLINE Sc::StaticCore& getScStatic() { return mStatic; } + + PX_FORCE_INLINE void initBufferedState() {} + +private: + Sc::StaticCore mStatic; + + PX_FORCE_INLINE const Buf* getRigidActorBuffer() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Buf* getRigidActorBuffer() { return reinterpret_cast(getStream()); } + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess {}; + + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, mStatic); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, mStatic, v); } + template PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush >(*this, mStatic, buf); } +#endif +}; + +RigidStatic::RigidStatic(const PxTransform& actor2World) : + mStatic(actor2World) +{ + setScbType(ScbType::eRIGID_STATIC); +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- +PX_INLINE void RigidStatic::syncState() +{ + const PxU32 bufferFlags = getBufferFlags(); + +#ifdef USE_NEW_SYSTEM + if(bufferFlags & BF_ActorFlags) +#else + if(bufferFlags & Buf::BF_ActorFlags) +#endif + syncNoSimSwitch(*getRigidActorBuffer(), mStatic, false); + + RigidObject::syncState(); + +#ifdef USE_NEW_SYSTEM + syncActor2World(); +#else + if(bufferFlags & Buf::BF_Actor2World) + flush(*getRigidActorBuffer()); +#endif + postSyncState(); +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp new file mode 100644 index 000000000..751af5837 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp @@ -0,0 +1,1334 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "NpCast.h" +#include "ScbScene.h" +#include "ScbRigidStatic.h" +#include "ScbBody.h" +#include "ScbShape.h" +#include "ScbConstraint.h" +#include "ScbArticulation.h" +#include "ScbArticulationJoint.h" +#include "ScbNpDeps.h" +#include "ScbAggregate.h" + +#include "PsFoundation.h" +#include "PxArticulation.h" +#include "common/PxProfileZone.h" + +namespace physx +{ + class NpMaterial; +} + +using namespace physx; + +// constants to make boolean template parameters more readable +static const bool tSimRunning = true; +static const bool tAdd = true; +static const bool tDynamic = true; +static const bool tNonSimObject = true; +static const bool tSyncOnRemove = true; +static const bool tWakeOnLostTouchCheck = true; + +void Scb::ObjectTracker::scheduleForInsert(Scb::Base& element) +{ + ControlState::Enum state = element.getControlState(); + PxU32 flags = element.getControlFlags(); + PX_ASSERT(!(flags & ControlFlag::eIS_RELEASED)); + PX_ASSERT(state == ControlState::eNOT_IN_SCENE || state == ControlState::eREMOVE_PENDING); + + if(state == ControlState::eREMOVE_PENDING) + { + element.setControlState(ControlState::eIN_SCENE); + if(!(flags & ControlFlag::eIS_UPDATED)) + remove(element); + } + else + { + PX_ASSERT(!(flags & ControlFlag::eIS_UPDATED)); + element.setControlState(ControlState::eINSERT_PENDING); + insert(element); + } +} + +void Scb::ObjectTracker::scheduleForRemove(Scb::Base& element) +{ + ControlState::Enum state = element.getControlState(); + PxU32 flags = element.getControlFlags(); + + PX_ASSERT(!(flags & ControlFlag::eIS_RELEASED)); + + if(state == ControlState::eINSERT_PENDING) + { + // if it's inserted this frame, just remove it - it can't be dirty + //ML: this assert wont' work because buffered insert raises this flag. We have a unit test which called TEST_F(ObserverTest, OnRelease) to verify it + //PX_ASSERT(!(flags & ControlFlag::eIS_UPDATED)); + element.setControlState(ControlState::eNOT_IN_SCENE); + remove(element); + } + else if(state == ControlState::eIN_SCENE) + { + element.setControlState(ControlState::eREMOVE_PENDING); + if(!(flags & ControlFlag::eIS_UPDATED)) + insert(element); + } + else + { + PX_ALWAYS_ASSERT_MESSAGE("Trying to remove element not in scene."); + } +} + +void Scb::ObjectTracker::scheduleForUpdate(Scb::Base& element) +{ + ControlState::Enum state = element.getControlState(); + PxU32 flags = element.getControlFlags(); + + PX_ASSERT(!(flags & ControlFlag::eIS_RELEASED)); + PX_ASSERT(state == ControlState::eIN_SCENE || state == ControlState::eREMOVE_PENDING || state == ControlState::eINSERT_PENDING); + + if(!(flags & ControlFlag::eIS_UPDATED)) + { + element.setControlFlag(ControlFlag::eIS_UPDATED); + if(state == ControlState::eIN_SCENE) + insert(element); + } +} + +void Scb::ObjectTracker::clear() +{ + Scb::Base *const * elements = mBuffered.getEntries(); + for(PxU32 i=0;igetControlState(); + PxU32 flags = elements[i]->getControlFlags(); + + if(state == ControlState::eIN_SCENE || state == ControlState::eINSERT_PENDING) + elements[i]->resetControl(ControlState::eIN_SCENE); + else + { + elements[i]->resetControl(ControlState::eNOT_IN_SCENE); + elements[i]->setScbScene(NULL); + } + + if(flags & ControlFlag::eIS_RELEASED) + NpDestroy(*elements[i]); + } + mBuffered.clear(); +} + +void Scb::ObjectTracker::insert(Scb::Base& element) +{ + PX_ASSERT(!mBuffered.contains(&element)); + mBuffered.insert(&element); +} + +void Scb::ObjectTracker::remove(Scb::Base& element) +{ + mBuffered.erase(&element); +} + +/////////////////////////////////////////////////////////////////////////////// + +template +PX_FORCE_INLINE static void addOrRemoveRigidObject(Sc::Scene& s, T& rigidObject, bool wakeOnLostTouch, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure); + +template struct ScSceneFns {}; + +template<> struct ScSceneFns +{ + static PX_FORCE_INLINE void insert(Sc::Scene& s, Scb::Articulation& v, PxBounds3*, const Gu::BVHStructure*) + { + Scb::Body* b = NpArticulationGetRootFromScb(v); + s.addArticulation(v.getScArticulation(), b->getScBody()); + } + static PX_FORCE_INLINE void remove(Sc::Scene& s, Scb::Articulation& v, bool wakeOnLostTouch) + { + PX_UNUSED(wakeOnLostTouch); + + v.clearBufferedSleepStateChange(); // see comment in remove code of Scb::Body + + s.removeArticulation(v.getScArticulation()); + } +}; + +template<> struct ScSceneFns +{ + static PX_FORCE_INLINE void insert(Sc::Scene& s, Scb::ArticulationJoint& v, PxBounds3*, const Gu::BVHStructure*) + { + Scb::Body* scb0, * scb1; + NpArticulationJointGetBodiesFromScb(v, scb0, scb1); + s.addArticulationJoint(v.getScArticulationJoint(), scb0->getScBody(), scb1->getScBody()); + } + static PX_FORCE_INLINE void remove(Sc::Scene& s, Scb::ArticulationJoint& v, bool wakeOnLostTouch) + { + PX_UNUSED(wakeOnLostTouch); + s.removeArticulationJoint(v.getScArticulationJoint()); + } +}; + +template<> struct ScSceneFns +{ + static PX_FORCE_INLINE void insert(Sc::Scene& s, Scb::Constraint& v, PxBounds3*, const Gu::BVHStructure*) + { + Scb::RigidObject* scb0, * scb1; + NpConstraintGetRigidObjectsFromScb(v, scb0, scb1); + + PX_ASSERT((!scb0) || (!(scb0->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))); + PX_ASSERT((!scb1) || (!(scb1->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION))); + + s.addConstraint(v.getScConstraint(), scb0 ? &scb0->getScRigidCore() : NULL, scb1 ? &scb1->getScRigidCore() : NULL); + } + static PX_FORCE_INLINE void remove(Sc::Scene& s, Scb::Constraint& v, bool wakeOnLostTouch) + { + PX_UNUSED(wakeOnLostTouch); + s.removeConstraint(v.getScConstraint()); + } +}; + +template<> struct ScSceneFns +{ + static PX_FORCE_INLINE void insert(Sc::Scene& s, Scb::RigidStatic& v, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) + { + // important to use the buffered flags because for a pending insert those describe the end state the + // user expects. + + if (!(v.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + addOrRemoveRigidObject(s, v, false, uninflatedBounds, bvhStructure); + else + addOrRemoveRigidObject(s, v, false, uninflatedBounds, bvhStructure); + } + static PX_FORCE_INLINE void remove(Sc::Scene& s, Scb::RigidStatic& v, bool wakeOnLostTouch) + { + // important to use the original flags because for a pending removal those describe the original state that needs + // to get cleaned up. + + if (!v.isSimDisabledInternally()) + addOrRemoveRigidObject(s, v, wakeOnLostTouch, NULL, NULL); + else + addOrRemoveRigidObject(s, v, false, NULL, NULL); + } +}; + +template<> struct ScSceneFns +{ + static PX_FORCE_INLINE void insert(Sc::Scene& s, Scb::Body& v, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) + { + // see comments in rigid static case + if (!(v.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) + addOrRemoveRigidObject(s, v, false, uninflatedBounds, bvhStructure); + else + addOrRemoveRigidObject(s, v, false, uninflatedBounds, bvhStructure); + } + static PX_FORCE_INLINE void remove(Sc::Scene& s, Scb::Body& v, bool wakeOnLostTouch) + { + // strictly speaking, the following is only necessary for pending removes but it does not have a + // functional side effect if applied all the time. + // When an object gets removed from the scene, pending wakeUp/putToSleep events should be ignored because + // the internal sleep state for a free standing object is specified as sleeping. All the other parameter changes + // that go along with a sleep state change should still get processed though (zero vel, zero wake counter on + // putToSleep for example). Those are not affected because they are tracked through buffered updates + // of the velocity and wake counter. + // The clearing happens here because only here we are sure that the object does get removed for real. At earlier + // stages someone might remove and then re-insert and object and for such cases it is important to keep the + // sleep state change buffered. + v.clearBufferedSleepStateChange(); + + // see comments in rigid static case + if (!v.isSimDisabledInternally()) + addOrRemoveRigidObject(s, v, wakeOnLostTouch, NULL, NULL); + else + addOrRemoveRigidObject(s, v, false, NULL, NULL); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +#if PX_SUPPORT_PVD +template struct PvdFns +{ + // PT: in the following functions, checkPvdDebugFlag() is done by the callers to save time when functions are called from a loop. + + static void createInstance(Scb::Scene& scene, Vd::ScbScenePvdClient& d, T* v) + { + PX_PROFILE_ZONE("PVD.createPVDInstance", scene.getContextId()); + PX_UNUSED(scene); + d.createPvdInstance(v); + } + + static void updateInstance(Scb::Scene& scene, Vd::ScbScenePvdClient& d, T* v) + { + PX_UNUSED(scene); + if(((v->getControlFlags() & Scb::ControlFlag::eIS_RELEASED) == 0) && (v->getControlState() != Scb::ControlState::eREMOVE_PENDING)) + { + PX_PROFILE_ZONE("PVD.updatePVDProperties", scene.getContextId()); + d.updatePvdProperties(v); + } + } + + static void releaseInstance(Scb::Scene& scene, Vd::ScbScenePvdClient& d, T* v) + { + PX_UNUSED(scene); + PX_PROFILE_ZONE("PVD.releasePVDInstance", scene.getContextId()); + d.releasePvdInstance(v); + } +}; + + #define CREATE_PVD_INSTANCE(obj) \ + { \ + if(mScenePvdClient.checkPvdDebugFlag()) \ + { \ + PX_PROFILE_ZONE("PVD.createPVDInstance", getContextId());\ + mScenePvdClient.createPvdInstance(obj); \ + } \ + } + #define RELEASE_PVD_INSTANCE(obj) \ + { \ + if(mScenePvdClient.checkPvdDebugFlag()) \ + { \ + PX_PROFILE_ZONE("PVD.releasePVDInstance", getContextId());\ + mScenePvdClient.releasePvdInstance(obj); \ + } \ + } + #define UPDATE_PVD_PROPERTIES(obj) \ + { \ + if(mScenePvdClient.checkPvdDebugFlag()) \ + { \ + PX_PROFILE_ZONE("PVD.updatePVDProperties", getContextId());\ + mScenePvdClient.updatePvdProperties(obj); \ + } \ + } + #define PVD_ORIGIN_SHIFT(shift) \ + { \ + if(mScenePvdClient.checkPvdDebugFlag()) \ + { \ + PX_PROFILE_ZONE("PVD.originShift", getContextId());\ + mScenePvdClient.originShift(shift); \ + } \ + } +#else + #define CREATE_PVD_INSTANCE(obj) {} + #define RELEASE_PVD_INSTANCE(obj) {} + #define UPDATE_PVD_PROPERTIES(obj) {} + #define PVD_ORIGIN_SHIFT(shift){} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +Scb::Scene::Scene(const PxSceneDesc& desc, PxU64 contextID) : + mScene (desc, contextID), + mSimulationRunning (false), + mIsBuffering (false), + mStream (16384), + mShapeMaterialBuffer (PX_DEBUG_EXP("shapeMaterialBuffer")), + mShapePtrBuffer (PX_DEBUG_EXP("shapePtrBuffer")), + mActorPtrBuffer (PX_DEBUG_EXP("actorPtrBuffer")), +#if PX_SUPPORT_PVD + mScenePvdClient (*this), +#endif + mWakeCounterResetValue (desc.wakeCounterResetValue), + mBufferFlags (0) +{ +} + +void Scb::Scene::release() +{ +#if PX_SUPPORT_PVD + mScenePvdClient.releasePvdInstance(); +#endif + mScene.release(); + mShapeMaterialBuffer.clear(); + mShapePtrBuffer.clear(); + mActorPtrBuffer.clear(); + mStream.clear(); +} + +// PT: TODO: inline this +PxScene* Scb::Scene::getPxScene() +{ + return const_cast(getNpScene(this)); +} + +/////////////////////////////////////////////////////////////////////////////// +template +void Scb::Scene::add(T& v, ObjectTracker &tracker, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) +{ + v.setScbScene(this); + + if (!mIsBuffering) + { + v.resetControl(ControlState::eIN_SCENE); + ScSceneFns::insert(mScene, v, uninflatedBounds, bvhStructure); +#if PX_SUPPORT_PVD + if(mScenePvdClient.checkPvdDebugFlag()) + PvdFns::createInstance(*this, mScenePvdClient, &v); +#endif + } + else + tracker.scheduleForInsert(v); +} + +template +void Scb::Scene::remove(T& v, ObjectTracker &tracker, bool wakeOnLostTouch) +{ + if (!mIsBuffering) + { + ScSceneFns::remove(mScene, v, wakeOnLostTouch); +#if PX_SUPPORT_PVD + if(mScenePvdClient.checkPvdDebugFlag()) + PvdFns::releaseInstance(*this, mScenePvdClient, &v); +#endif + v.resetControl(ControlState::eNOT_IN_SCENE); + v.setScbScene(NULL); + } + else + { + tracker.scheduleForRemove(v); + } +} + + +/////////////////////////////////////////////////////////////////////////////// + +template +void Scb::Scene::addRigidNoSim(T& v, ObjectTracker &tracker, const Gu::BVHStructure* bvhStructure) +{ + PX_ASSERT(v.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION); + v.setScbScene(this); + + if (!mIsBuffering) + { + v.resetControl(ControlState::eIN_SCENE); +#if PX_SUPPORT_PVD + if(mScenePvdClient.checkPvdDebugFlag()) + PvdFns::createInstance(*this, mScenePvdClient, &v); +#endif + addOrRemoveRigidObject(mScene, v, false, NULL, bvhStructure); + } + else + { + tracker.scheduleForInsert(v); + addOrRemoveRigidObject(mScene, v, false, NULL, bvhStructure); + } +} + + +template +void Scb::Scene::removeRigidNoSim(T& v, ObjectTracker &tracker) +{ + PX_ASSERT(v.isSimDisabledInternally()); + + if (!mIsBuffering) + { + addOrRemoveRigidObject(mScene, v, false, NULL, NULL); +#if PX_SUPPORT_PVD + if(mScenePvdClient.checkPvdDebugFlag()) + PvdFns::releaseInstance(*this, mScenePvdClient, &v); +#endif + v.resetControl(ControlState::eNOT_IN_SCENE); + v.setScbScene(NULL); + } + else + { + tracker.scheduleForRemove(v); + addOrRemoveRigidObject(mScene, v, false, NULL, NULL); + } +} + + +void Scb::Scene::switchRigidToNoSim(Scb::RigidObject& r, bool isDynamic) +{ + PX_ASSERT(!mIsBuffering); + + // when a simulation objects has a pending remove and then gets switched to a non-simulation object, + // we must not process the code below. On sync the object will get removed before this call. + if (r.getControlState() == ControlState::eIN_SCENE) + { + size_t ptrOffset = -Scb::Shape::getScOffset(); + Ps::InlineArray scShapes; + + if (isDynamic) + mScene.removeBody(static_cast(r.getScRigidCore()), scShapes, true); + else + mScene.removeStatic(static_cast(r.getScRigidCore()), scShapes, true); + + // not in simulation anymore -> decrement shape ref-counts + void* const* shapes = reinterpret_cast(const_cast(scShapes.begin())); + for(PxU32 i=0; i < scShapes.size(); i++) + { + Scb::Shape& scbShape = *Ps::pointerOffset(shapes[i], ptrdiff_t(ptrOffset)); + NpShapeDecRefCount(scbShape); + } + } +} + + +void Scb::Scene::switchRigidFromNoSim(Scb::RigidObject& r, bool isDynamic) +{ + PX_ASSERT(!mIsBuffering); + + // when a non-simulation objects has a pending remove and then gets switched to a simulation object, + // we must not process the code below. On sync the object will get removed before this call. + if (r.getControlState() == ControlState::eIN_SCENE) + { + void* const* shapes; + size_t shapePtrOffset = NpShapeGetScPtrOffset(); + size_t ptrOffset = shapePtrOffset - Scb::Shape::getScOffset(); + + PxU32 nbShapes; + if (isDynamic) + { + bool isCompound; + nbShapes = NpRigidDynamicGetShapes(static_cast(r), shapes, &isCompound); + mScene.addBody(static_cast(r.getScRigidCore()), shapes, nbShapes, shapePtrOffset, NULL, isCompound); + } + else + { + nbShapes = NpRigidStaticGetShapes(static_cast(r), shapes); + mScene.addStatic(static_cast(r.getScRigidCore()), shapes, nbShapes, shapePtrOffset, NULL); + } + + // add to simulation -> increment shape ref-counts + for(PxU32 i=0; i < nbShapes; i++) + { + Scb::Shape& scbShape = *Ps::pointerOffset(shapes[i], ptrdiff_t(ptrOffset)); + NpShapeIncRefCount(scbShape); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// + +// PT: TODO: +// - consider making these templates not scene member functions + +template +PX_FORCE_INLINE void Scb::Scene::addActorT(T& actor, Scb::ObjectTracker& tracker, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) +{ + PX_PROFILE_ZONE("API.addActorToSim", getContextId()); + if(!noSim) + { + add(actor, tracker, uninflatedBounds, bvhStructure); + actor.initBufferedState(); + + // copy buffer control state from rigid object to shapes and set scene + if(mIsBuffering) + addOrRemoveRigidObject(mScene, actor, false, NULL, bvhStructure); + } + else + { + addRigidNoSim(actor, tracker, bvhStructure); + actor.initBufferedState(); + } +} + +void Scb::Scene::addActor(Scb::RigidStatic& rigidStatic, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) +{ + addActorT(rigidStatic, mRigidStaticManager, noSim, uninflatedBounds, bvhStructure); +} + +void Scb::Scene::addActor(Scb::Body& body, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) +{ + addActorT(body, mBodyManager, noSim, uninflatedBounds, bvhStructure); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::removeActor(Scb::RigidStatic& rigidStatic, bool wakeOnLostTouch, bool noSim) +{ + PX_PROFILE_ZONE("API.removeActorFromSim", getContextId()); + if (!noSim) + { + remove(rigidStatic, mRigidStaticManager, wakeOnLostTouch); + + // copy buffer control state from rigid object to shapes and set scene + if (mIsBuffering) + { + if (wakeOnLostTouch) + rigidStatic.scheduleForWakeTouching(); + addOrRemoveRigidObject(mScene, rigidStatic, wakeOnLostTouch, NULL, NULL); + } + } + else + { + removeRigidNoSim(rigidStatic, mRigidStaticManager); + } + + rigidStatic.clearBufferedState(); +} + +void Scb::Scene::removeActor(Scb::Body& body, bool wakeOnLostTouch, bool noSim) +{ + PX_PROFILE_ZONE("API.removeActorFromSim", getContextId()); + if (!noSim) + { + body.clearSimStateDataForPendingInsert(); + + remove(body, mBodyManager, wakeOnLostTouch); + body.clearBufferedState(); + + // copy buffer control state from rigid object to shapes and set scene + if (mIsBuffering) + { + if (wakeOnLostTouch) + body.scheduleForWakeTouching(); + addOrRemoveRigidObject(mScene, body, wakeOnLostTouch, NULL, NULL); + } + } + else + { + removeRigidNoSim(body, mBodyManager); + + // note: "noSim" refers to the internal state here. The following asserts only apply if the bufferd state has not switched to "sim". + PX_ASSERT(!(body.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION) || body.isSleeping()); + PX_ASSERT(!(body.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION) || !body.isBuffered(BodyBuffer::BF_KinematicTarget | BodyBuffer::BF_Acceleration | BodyBuffer::BF_DeltaVelocity)); + // What about velocity, wakeCounter, ...? + // Those are not allowed on a no-sim object, however, they might still be necessary due to complex buffering scenarios: + // Imagine the following operation flow (all buffered): + // - dynamic sim object awake with velocities + // - switch to no-sim -> needs to clear velocities, wake counter, put to sleep, ... + // - switch back to sim -> the velocities, wake counter, ... still need to get cleared and it needs to be asleep (that would be the non-buffered behavior of the operations) + + body.clearBufferedState(); // this also covers the buffered case where a noSim object gets switched to a sim object, followed by a wakeUp() call and then a remove. + // If we checked whether the buffered object is still a noSim object then only body.RigidObject::clearBufferedState() would be necessary. + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::addConstraint(Scb::Constraint& constraint) +{ + add(constraint, mConstraintManager, NULL, NULL); +} + +void Scb::Scene::removeConstraint(Scb::Constraint& constraint) +{ + if (!mIsBuffering) + { + mScene.removeConstraint(constraint.getScConstraint()); + + // Release pvd constraint immediately since delayed removal with already released ext::joints does not work, can't call callback. + if(constraint.getControlState() != ControlState::eINSERT_PENDING) + RELEASE_PVD_INSTANCE(&constraint) + + constraint.resetControl(ControlState::eNOT_IN_SCENE); + constraint.setScbScene(NULL); + } + else + { + mConstraintManager.scheduleForRemove(constraint); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::addArticulation(Scb::Articulation& articulation) +{ + add(articulation, mArticulationManager, NULL, NULL); + articulation.initBufferedState(); +} + +void Scb::Scene::removeArticulation(Scb::Articulation& articulation) +{ + remove(articulation, mArticulationManager); + articulation.clearBufferedState(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::addArticulationJoint(Scb::ArticulationJoint& joint) +{ + add(joint, mArticulationJointManager, NULL, NULL); +} + +void Scb::Scene::removeArticulationJoint(Scb::ArticulationJoint& joint) +{ + remove(joint, mArticulationJointManager); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::addAggregate(Scb::Aggregate& agg) +{ + agg.setScbScene(this); + + if (!mIsBuffering) + { + PxU32 aggregateID = mScene.createAggregate(agg.mPxAggregate, agg.getSelfCollide()); + agg.setAggregateID(aggregateID); + agg.resetControl(ControlState::eIN_SCENE); +#if PX_SUPPORT_PVD + //Sending pvd events after all aggregates's actors are inserted into scene + mScenePvdClient.createPvdInstance(&agg); +#endif + } + else + mAggregateManager.scheduleForInsert(agg); +} + + +void Scb::Scene::removeAggregate(Scb::Aggregate& agg) +{ + if (!mIsBuffering) + { + mScene.deleteAggregate(agg.getAggregateID()); + agg.resetControl(ControlState::eNOT_IN_SCENE); + agg.setScbScene(NULL); +#if PX_SUPPORT_PVD + mScenePvdClient.releasePvdInstance(&agg); +#endif + } + else + { + mAggregateManager.scheduleForRemove(agg); + } +} + + +void Scb::Scene::addMaterial(const Sc::MaterialCore& material) +{ + Ps::Mutex::ScopedLock lock(mSceneMaterialBufferLock); + + mSceneMaterialBuffer.pushBack(MaterialEvent(material.getMaterialIndex(), MATERIAL_ADD)); + + CREATE_PVD_INSTANCE(&material) +} + +void Scb::Scene::updateMaterial(const Sc::MaterialCore& material) +{ + Ps::Mutex::ScopedLock lock(mSceneMaterialBufferLock); + + mSceneMaterialBuffer.pushBack(MaterialEvent(material.getMaterialIndex(), MATERIAL_UPDATE)); + + UPDATE_PVD_PROPERTIES(&material) +} + +void Scb::Scene::removeMaterial(const Sc::MaterialCore& material) +{ + if(material.getMaterialIndex() == MATERIAL_INVALID_HANDLE) + return; + + Ps::Mutex::ScopedLock lock(mSceneMaterialBufferLock); + + mSceneMaterialBuffer.pushBack(MaterialEvent(material.getMaterialIndex(), MATERIAL_REMOVE)); + + RELEASE_PVD_INSTANCE(&material); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Scb::Scene::updateLowLevelMaterial(NpMaterial** masterMaterial) +{ + Ps::Mutex::ScopedLock lock(mSceneMaterialBufferLock); + + //sync all the material events + PxsMaterialManager& manager = mScene.getMaterialManager(); + for(PxU32 i=0; i< mSceneMaterialBuffer.size(); ++i) + { + const MaterialEvent& event = mSceneMaterialBuffer[i]; + const NpMaterial* masMat = masterMaterial[event.mHandle]; + switch(event.mType) + { + case MATERIAL_ADD: + if(masMat) + { + Sc::MaterialCore* materialCore = &masterMaterial[event.mHandle]->getScMaterial(); + manager.setMaterial(materialCore); + mScene.registerMaterialInNP(*materialCore); + } + break; + case MATERIAL_UPDATE: + if(masMat) + { + Sc::MaterialCore* materialCore = &masterMaterial[event.mHandle]->getScMaterial(); + manager.updateMaterial(materialCore); + mScene.updateMaterialInNP(*materialCore); + } + break; + case MATERIAL_REMOVE: + if (event.mHandle < manager.getMaxSize()) // materials might get added and then removed again immediately. However, the add does not get processed (see case MATERIAL_ADD above), + { // so the remove might end up reading out of bounds memory unless checked. + PxsMaterialCore* materialCore = manager.getMaterial(event.mHandle); + if (materialCore->getMaterialIndex() == event.mHandle) + { + mScene.unregisterMaterialInNP(*materialCore); + manager.removeMaterial(materialCore); + } + } + break; + }; + } + + mSceneMaterialBuffer.resize(0); +} + +//-------------------------------------------------------------- +// +// Data synchronization +// +//-------------------------------------------------------------- +void Scb::Scene::syncState() +{ + //process client creation -- must be done before BF_CLIENT_BEHAVIOR_FLAGS processing in the below block: + while(mBufferedData.mNumClientsCreated) + { + mScene.createClient(); + mBufferedData.mNumClientsCreated--; + } + + if(mBufferFlags) + { + if(isBuffered(BF_GRAVITY)) + mScene.setGravity(mBufferedData.mGravity); + + if(isBuffered(BF_BOUNCETHRESHOLDVELOCITY)) + mScene.setBounceThresholdVelocity(mBufferedData.mBounceThresholdVelocity); + + if(isBuffered(BF_FLAGS)) + mScene.setPublicFlags(mBufferedData.mFlags); + + if(isBuffered(BF_DOMINANCE_PAIRS)) + mBufferedData.syncDominancePairs(mScene); + + if(isBuffered(BF_SOLVER_BATCH_SIZE)) + mScene.setSolverBatchSize(mBufferedData.mSolverBatchSize); + + if(isBuffered(BF_VISUALIZATION)) + { + for(PxU32 i=0; i +void Scb::Scene::processUserUpdates(ObjectTracker &tracker) +{ +#if PX_SUPPORT_PVD + bool isPvdValid = mScenePvdClient.checkPvdDebugFlag(); +#endif + Base*const * buffered = tracker.getBuffered(); + for(PxU32 i=0; i < tracker.getBufferedCount(); i++) + { + T& v = *static_cast(buffered[i]); + if (v.getControlState() == ControlState::eINSERT_PENDING) + { + ScSceneFns::insert(mScene, v, NULL, NULL); +#if PX_SUPPORT_PVD + if(isPvdValid) + PvdFns::createInstance(*this, mScenePvdClient, &v); +#endif + } + else if(v.getControlFlags() & ControlFlag::eIS_UPDATED) + { + v.syncState(); +#if PX_SUPPORT_PVD + if(isPvdValid) + PvdFns::updateInstance(*this, mScenePvdClient, &v); +#endif + } + } +} + +template +void Scb::Scene::processSimUpdates(S*const * scObjects, PxU32 nbObjects) +{ +#if PX_SUPPORT_PVD + bool isPvdValid = mScenePvdClient.checkPvdDebugFlag(); +#endif + for(PxU32 i=0;i::updateInstance(*this, mScenePvdClient, &v); +#endif + } + } +} + +#define ENABLE_PVD_ORIGINSHIFT_EVENT +void Scb::Scene::shiftOrigin(const PxVec3& shift) +{ + PX_ASSERT(!isPhysicsBuffering()); + mScene.shiftOrigin(shift); + +#ifdef ENABLE_PVD_ORIGINSHIFT_EVENT + PVD_ORIGIN_SHIFT(shift); +#endif +} + +void Scb::Scene::syncWriteThroughProperties() +{ + mStream.lock(); + + Base*const * buffered = mBodyManager.getBuffered(); + PxU32 count = mBodyManager.getBufferedCount(); + for(PxU32 i=0; i < count; i++) + { + Scb::Body& bufferedBody = *static_cast(buffered[i]); + bufferedBody.syncCollisionWriteThroughState(); + } + + mStream.unlock(); +} + +void Scb::Scene::syncEntireScene() +{ + PX_PROFILE_ZONE("Sim.syncState", getContextId()); + + setPhysicsBuffering(false); // Clear the buffering flag to allow buffered writes to execute immediately. Once collision detection is running, buffering is automatically forced on + + mStream.lock(); + syncState(); + // + // Process aggregates (needs to be done before adding actors because the actor's aggregateID needs to get set) + // + + for(PxU32 i=0; i < mAggregateManager.getBufferedCount(); i++) + { + Aggregate* a = static_cast(mAggregateManager.getBuffered()[i]); + if (a->getControlState() == ControlState::eINSERT_PENDING) + { + PxU32 aggregateID = mScene.createAggregate(a->mPxAggregate, a->getSelfCollide()); + a->setAggregateID(aggregateID); +#if PX_SUPPORT_PVD + mScenePvdClient.createPvdInstance(a); +#endif + a->syncState(*this); // Necessary to set the aggregate ID for all actors of the aggregate + } + else if(a->getControlFlags() & ControlFlag::eIS_UPDATED) + { + a->syncState(*this); + } + } + mAggregateManager.clear(); + mActorPtrBuffer.clear(); + + + // rigid statics + processUserUpdates(mRigidStaticManager); + mRigidStaticManager.clear(); + + + // rigid dynamics and articulation links + // + // 1) Sync simulation changed data + { + PX_PROFILE_ZONE("SyncActiveBodies", getContextId()); + Sc::BodyCore*const* activeBodies = mScene.getActiveBodiesArray(); + PxU32 nbActiveBodies = mScene.getNumActiveBodies(); + while(nbActiveBodies--) + { + Sc::BodyCore* bodyCore = *activeBodies++; + Scb::Body& bufferedBody = Scb::Body::fromSc(*bodyCore); + if (!(bufferedBody.getControlFlags() & ControlFlag::eIS_UPDATED)) // Else the data will be synced further below + bufferedBody.syncState(); + } + } + + // 2) Sync data of rigid dynamics which were put to sleep by the simulation + + PxU32 nbSleepingBodies; + Sc::BodyCore* const* sleepingBodies = mScene.getSleepBodiesArray(nbSleepingBodies); + processSimUpdates(sleepingBodies, nbSleepingBodies); + + // user updates + processUserUpdates(mBodyManager); + mBodyManager.clear(); + mShapePtrBuffer.clear(); + + + // rigid body shapes + // + // IMPORTANT: This has to run after the material update + // + // Sync user changed data. Inserts and removes are handled in actor sync + for(PxU32 i=0; i < mShapeManager.getBufferedCount(); i++) + { + Scb::Shape* s = static_cast(mShapeManager.getBuffered()[i]); + + if(s->getControlFlags() & ControlFlag::eIS_UPDATED) + { + s->syncState(); + UPDATE_PVD_PROPERTIES(s) + } + } + + mShapeManager.clear(); + mShapeMaterialBuffer.clear(); + + // constraints (get break force and broken status from sim) + + processSimUpdates(mScene.getConstraints(), mScene.getNbConstraints()); + processUserUpdates(mConstraintManager); + mConstraintManager.clear(); + + // articulations (get sleep state from sim) + processSimUpdates(mScene.getArticulations(), mScene.getNbArticulations()); + processUserUpdates(mArticulationManager); + mArticulationManager.clear(); + + + // Process articulation joints + processUserUpdates(mArticulationJointManager); + mArticulationJointManager.clear(); + + + mStream.clearNotThreadSafe(); + mStream.unlock(); +} + + + +template +void Scb::Scene::processRemoves(ObjectTracker& tracker) +{ +#if PX_SUPPORT_PVD + bool isPvdValid = mScenePvdClient.checkPvdDebugFlag(); +#endif + typedef ScSceneFns Fns; + for(PxU32 i=0; i < tracker.getBufferedCount(); i++) + { + T* v = static_cast(tracker.getBuffered()[i]); + if(v->getControlState() == ControlState::eREMOVE_PENDING) + { + bool wakeOnLostTouch = false; + if (wakeOnLostTouchCheck) + { + PX_ASSERT( (v->getScbType() == ScbType::eBODY) || + (v->getScbType() == ScbType::eBODY_FROM_ARTICULATION_LINK) || + (v->getScbType() == ScbType::eRIGID_STATIC) ); + wakeOnLostTouch = (v->Base::isBuffered(RigidObjectBuffer::BF_WakeTouching) != 0); // important to use Scb::Base::isBuffered() because Scb::Body, for example, has a shadowed implementation of this method + } + + Fns::remove(mScene, *v, wakeOnLostTouch); + + // if no object param has been updated, the state sync still needs to be processed to write simulation results + // back to the permanently buffered params. + if (syncOnRemove && !(v->getControlFlags() & ControlFlag::eIS_UPDATED)) + v->syncState(); +#if PX_SUPPORT_PVD + if(isPvdValid) + PvdFns::releaseInstance(*this, mScenePvdClient, v); +#endif + } + } +} + +template +void Scb::Scene::processShapeRemoves(ObjectTracker& tracker) +{ + for(PxU32 i=0; i < tracker.getBufferedCount(); i++) + { + T* v = static_cast(tracker.getBuffered()[i]); + v->processShapeRemoves(); + } +} + +void Scb::Scene::processPendingRemove() +{ + processShapeRemoves(mRigidStaticManager); + processShapeRemoves(mBodyManager); + + processRemoves(mConstraintManager); + + Scb::Base *const * buffered = mConstraintManager.getBuffered(); + for(PxU32 i=0; i < mConstraintManager.getBufferedCount(); i++) + { + Scb::Constraint* constraint = static_cast(buffered[i]); + + if(constraint->getControlFlags() & ControlFlag::eIS_UPDATED) + constraint->prepareForActorRemoval(); // see comments in Scb::Constraint + } + + processRemoves (mArticulationJointManager); + processRemoves (mRigidStaticManager); + processRemoves (mBodyManager); + processRemoves (mArticulationManager); + + // Do after actors have been removed (coumpound can only be removed after all its elements are gone) + for(PxU32 i=0; i < mAggregateManager.getBufferedCount(); i++) + { + Aggregate* a = static_cast(mAggregateManager.getBuffered()[i]); + + if(a->getControlState() == ControlState::eREMOVE_PENDING) + { + a->syncState(*this); // Clears the aggregate ID for all actors of the aggregate + mScene.deleteAggregate(a->getAggregateID()); + +#if PX_SUPPORT_PVD + mScenePvdClient.releasePvdInstance(a); +#endif + } + } +} + +void Scb::Scene::scheduleForUpdate(Scb::Base& object) +{ + switch(object.getScbType()) + { + case ScbType::eSHAPE_EXCLUSIVE: + case ScbType::eSHAPE_SHARED: { mShapeManager.scheduleForUpdate(object); }break; + case ScbType::eBODY: { mBodyManager.scheduleForUpdate(object); }break; + case ScbType::eBODY_FROM_ARTICULATION_LINK: { mBodyManager.scheduleForUpdate(object); }break; + case ScbType::eRIGID_STATIC: { mRigidStaticManager.scheduleForUpdate(object); }break; + case ScbType::eCONSTRAINT: { mConstraintManager.scheduleForUpdate(object); }break; + case ScbType::eARTICULATION: { mArticulationManager.scheduleForUpdate(object); }break; + case ScbType::eARTICULATION_JOINT: { mArticulationJointManager.scheduleForUpdate(object); }break; + case ScbType::eAGGREGATE: { mAggregateManager.scheduleForUpdate(object); }break; + case ScbType::eUNDEFINED: + case ScbType::eTYPE_COUNT: + PX_ALWAYS_ASSERT_MESSAGE( "scheduleForUpdate: missing type!"); + break; + } +} + +PxU8* Scb::Scene::getStream(ScbType::Enum type) +{ + PxU8* memory = NULL; + switch(type) + { + case ScbType::eSHAPE_EXCLUSIVE: + case ScbType::eSHAPE_SHARED: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::ShapeBuffer))); new (memory) Scb::ShapeBuffer; }break; + case ScbType::eBODY: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::BodyBuffer))); new (memory) Scb::BodyBuffer; }break; + case ScbType::eBODY_FROM_ARTICULATION_LINK: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::BodyBuffer))); new (memory) Scb::BodyBuffer; }break; + case ScbType::eRIGID_STATIC: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::RigidStaticBuffer))); new (memory) Scb::RigidStaticBuffer; }break; + case ScbType::eCONSTRAINT: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::ConstraintBuffer))); new (memory) Scb::ConstraintBuffer; }break; + case ScbType::eARTICULATION: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::ArticulationBuffer))); new (memory) Scb::ArticulationBuffer; }break; + case ScbType::eARTICULATION_JOINT: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::ArticulationJointBuffer))); new (memory) Scb::ArticulationJointBuffer; }break; + case ScbType::eAGGREGATE: { memory = reinterpret_cast(mStream.allocateNotThreadSafe(sizeof(Scb::AggregateBuffer))); new (memory) Scb::AggregateBuffer; }break; + case ScbType::eUNDEFINED: + case ScbType::eTYPE_COUNT: + PX_ALWAYS_ASSERT_MESSAGE("getStream: missing type!"); + return NULL; + } + return memory; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxBroadPhaseType::Enum Scb::Scene::getBroadPhaseType() const +{ + return mScene.getBroadPhaseType(); +} + +bool Scb::Scene::getBroadPhaseCaps(PxBroadPhaseCaps& caps) const +{ + return mScene.getBroadPhaseCaps(caps); +} + +PxU32 Scb::Scene::getNbBroadPhaseRegions() const +{ + return mScene.getNbBroadPhaseRegions(); +} + +PxU32 Scb::Scene::getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + return mScene.getBroadPhaseRegions(userBuffer, bufferSize, startIndex); +} + +PxU32 Scb::Scene::addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion) +{ + if(!isPhysicsBuffering()) + return mScene.addBroadPhaseRegion(region, populateRegion); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addBroadPhaseRegion() not allowed while simulation is running. Call will be ignored."); + return 0xffffffff; +} + +bool Scb::Scene::removeBroadPhaseRegion(PxU32 handle) +{ + if(!isPhysicsBuffering()) + return mScene.removeBroadPhaseRegion(handle); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::removeBroadPhaseRegion() not allowed while simulation is running. Call will be ignored."); + return false; +} + +////////////////////////////////////////////////////////////////////////// + +// +// To avoid duplication of a lot of similar code, the following templated method was introduced. Its main purpose is to +// take care of all the operations related to adding/removing a rigid object to/from the scene. Depending on the type +// of rigid object and the simulation state, there are slight changes to the code flow necessary. Among the operations are: +// +// - Add/remove rigid object to/from scene +// - Add/remove shapes from PVD +// - Adjust buffer control state of shapes +// - Adjust ref-count of shapes +// +template +PX_FORCE_INLINE static void addOrRemoveRigidObject(Sc::Scene& s, T& rigidObject, bool wakeOnLostTouch, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure) +{ + PX_ASSERT(TIsDynamic || (rigidObject.getScbType() == ScbType::eRIGID_STATIC)); + if (TSimRunning && TIsNonSimObject && TAdd) + PX_ASSERT(rigidObject.getActorFlags() & PxActorFlag::eDISABLE_SIMULATION); + if (TSimRunning && TIsNonSimObject&& !TAdd) + PX_ASSERT(rigidObject.isSimDisabledInternally()); + if (!TSimRunning && TIsNonSimObject) + PX_ASSERT(rigidObject.isSimDisabledInternally()); // when the simulation flag gets cleared on an object with pending removal, only the core flag knows that internally it is still a non-simulation object. + PX_ASSERT(!uninflatedBounds || (TAdd && !TSimRunning && !TIsNonSimObject)); + + Ps::InlineArray localShapes; + Ps::InlineArray& scShapes = s.getBatchRemove() ? s.getBatchRemove()->removedShapes : localShapes; + + PxActor* pxActor = NULL; + void* const* shapes; + PxU32 nbShapes; + size_t shapePtrOffset = NpShapeGetScPtrOffset(); + + Scb::Body& dynamicObject = reinterpret_cast(rigidObject); + Scb::RigidStatic& staticObject = reinterpret_cast(rigidObject); + + if (!TSimRunning) + { + if (TIsDynamic) + pxActor = dynamicObject.getScBody().getPxActor(); + else + pxActor = staticObject.getScStatic().getPxActor(); + } + PX_UNUSED(pxActor); + PX_UNUSED(bvhStructure); + size_t ptrOffset; + if (TAdd || TSimRunning || TIsNonSimObject) + { + // Np buffers are still valid when the object gets removed while the sim is running. + // Furthermore, for non-simulation objects, there exists no shape buffer in the simulation controller + // and we need to fetch from Np all the time. + + ptrOffset = shapePtrOffset - Scb::Shape::getScOffset(); + + if (TIsDynamic) + nbShapes = NpRigidDynamicGetShapes(dynamicObject, shapes); + else + nbShapes = NpRigidStaticGetShapes(staticObject, shapes); + } + + if ((!TSimRunning) && (!TIsNonSimObject)) + { + if (TAdd) + { + if (TIsDynamic) + { + const bool isCompound = bvhStructure ? true : false; + s.addBody(dynamicObject.getScBody(), shapes, nbShapes, shapePtrOffset, uninflatedBounds, isCompound); + } + else + s.addStatic(staticObject.getScStatic(), shapes, nbShapes, shapePtrOffset, uninflatedBounds); + } + else + { + ptrOffset = -Scb::Shape::getScOffset(); + + if (TIsDynamic) + s.removeBody(dynamicObject.getScBody(), scShapes, wakeOnLostTouch); + else + s.removeStatic(staticObject.getScStatic(), scShapes, wakeOnLostTouch); + + shapes = reinterpret_cast(const_cast(scShapes.begin())); + nbShapes = scShapes.size(); + } + } + + Scb::Scene* scbScene = rigidObject.getScbScene(); + Scb::Scene* shapeScenePtr = scbScene; + Scb::ControlState::Enum controlState = rigidObject.getControlState(); + + if (!TSimRunning) + { + // hacky: in the non-buffered case the rigid objects might not have been updated properly at this point, so it's done explicitly. + + if (TAdd) + { + PX_ASSERT(shapeScenePtr == scbScene); + controlState = Scb::ControlState::eIN_SCENE; + } + else + { + shapeScenePtr = NULL; + controlState = Scb::ControlState::eNOT_IN_SCENE; + } + } + + for(PxU32 i=0; i < nbShapes; i++) + { + Scb::Shape& scbShape = *Ps::pointerOffset(shapes[i], ptrdiff_t(ptrOffset)); + + if (!TSimRunning) + { + PX_ASSERT(pxActor); + PX_ASSERT(scbScene); + + if (TAdd) + { + scbShape.setControlStateIfExclusive(shapeScenePtr, controlState); + + if (!TIsNonSimObject) + NpShapeIncRefCount(scbShape); // simulation increases the refcount to avoid that shapes get destroyed while the sim is running + +#if PX_SUPPORT_PVD + scbScene->getScenePvdClient().createPvdInstance(&scbShape, *pxActor); +#endif + } + else + { + scbShape.checkUpdateOnRemove(scbScene); + +#if PX_SUPPORT_PVD + scbScene->getScenePvdClient().releasePvdInstance(&scbShape, *pxActor); +#endif + scbShape.setControlStateIfExclusive(shapeScenePtr, controlState); + + if (!TIsNonSimObject) + NpShapeDecRefCount(scbShape); // see comment in the "TAdd" section above + } + } + else + scbShape.setControlStateIfExclusive(shapeScenePtr, controlState); + } +} diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbScene.h b/src/PhysX/physx/source/physx/src/buffering/ScbScene.h new file mode 100644 index 000000000..af3934ca7 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbScene.h @@ -0,0 +1,753 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_SCENE +#define PX_PHYSICS_SCB_SCENE + +#include "ScScene.h" + +#include "ScbSceneBuffer.h" +#include "ScbType.h" +#include "PsFoundation.h" +#include "PsMutex.h" +#include "PsHashSet.h" + +#if PX_SUPPORT_PVD +#include "PxPhysics.h" +#include "ScbScenePvdClient.h" +#endif + +namespace physx +{ + +class NpMaterial; + +namespace Sc +{ + class BodyDesc; +} + +namespace Gu +{ + class BVHStructure; +} + +namespace Scb +{ + class Base; + class RigidObject; + class RigidStatic; + class Body; + class Actor; + class Shape; + class Constraint; + class Material; + class Articulation; + class ArticulationJoint; + class Aggregate; + + struct ShapeBuffer; + + /** + \brief Helper class to track inserted/removed/updated buffering objects. + */ + class ObjectTracker + { + public: + ObjectTracker() {} + + /** + \brief An object has been inserted while the simulation was running -> track it for insertion at sync point + */ + void scheduleForInsert(Base& element); + + /** + \brief An object has been removed while the simulation was running -> track it for removal at sync point + */ + void scheduleForRemove(Base& element); + + /** + \brief An object has been changed while the simulation was running -> track it for update at sync point + */ + void scheduleForUpdate(Base& element); + + /** + \brief Get the list of dirty objects that require processing at a sync point. + */ + Base*const * getBuffered() { return mBuffered.getEntries(); } + + /** + \brief Get number of dirty objects that require processing at a sync point. + */ + PxU32 getBufferedCount() const { return mBuffered.size(); } + + /** + \brief Cleanup dirty objects after sync point. + + \li Transition pending insertion objects from eINSERT_PENDING to eIN_SCENE. + \li Transition pending removal objects from eREMOVE_PENDING to eNOT_IN_SCENE. + \li Destroy objects marked as eIS_RELEASED. + \li Clear dirty list. + */ + void clear(); + + void insert(Base& element); + void remove(Base& element); + + private: + Ps::CoalescedHashSet mBuffered; + }; + + typedef ObjectTracker ShapeManager; + typedef ObjectTracker RigidStaticManager; + typedef ObjectTracker BodyManager; + typedef ObjectTracker ArticulationManager; + typedef ObjectTracker ConstraintManager; + typedef ObjectTracker ArticulationJointManager; + typedef ObjectTracker AggregateManager; + + enum MATERIAL_EVENT + { + MATERIAL_ADD, + MATERIAL_UPDATE, + MATERIAL_REMOVE + }; + + class MaterialEvent + { + public: + PX_FORCE_INLINE MaterialEvent(PxU32 handle, MATERIAL_EVENT type) : mHandle(handle), mType(type) {} + PX_FORCE_INLINE MaterialEvent() {} + + PxU32 mHandle;//handle to the master material table + MATERIAL_EVENT mType; + }; + + class Scene : public Ps::UserAllocated + { + PX_NOCOPY(Scene) + public: + enum BufferFlag + { + BF_GRAVITY = (1 << 0), + BF_BOUNCETHRESHOLDVELOCITY = (1 << 1), + BF_FLAGS = (1 << 2), + BF_DOMINANCE_PAIRS = (1 << 3), + BF_SOLVER_BATCH_SIZE = (1 << 4), + BF_VISUALIZATION = (1 << 5), + BF_CULLING_BOX = (1 << 6) + }; + + public: + Scene(const PxSceneDesc& desc, PxU64 contextID); + ~Scene() {} //use release() plz. + + //--------------------------------------------------------------------------------- + // Wrapper for Sc::Scene interface + //--------------------------------------------------------------------------------- + void release(); + + PxScene* getPxScene(); + + PX_INLINE void setGravity(const PxVec3& gravity); + PX_INLINE PxVec3 getGravity() const; + + PX_INLINE void setBounceThresholdVelocity(const PxReal t); + PX_INLINE PxReal getBounceThresholdVelocity() const; + + PX_INLINE void setFlags(PxSceneFlags flags); + PX_INLINE PxSceneFlags getFlags() const; + + PX_INLINE void setFrictionType(PxFrictionType::Enum type) { mScene.setFrictionType(type); } + PX_INLINE PxFrictionType::Enum getFrictionType() const { return mScene.getFrictionType(); } + + void addActor(Scb::RigidStatic&, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure); + void removeActor(Scb::RigidStatic&, bool wakeOnLostTouch, bool noSim); + + void addActor(Scb::Body&, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure); + void removeActor(Scb::Body&, bool wakeOnLostTouch, bool noSim); + + void addConstraint(Scb::Constraint&); + void removeConstraint(Scb::Constraint&); + + void addArticulation(Scb::Articulation&); + void removeArticulation(Scb::Articulation&); + + void addArticulationJoint(Scb::ArticulationJoint&); + void removeArticulationJoint(Scb::ArticulationJoint&); + + void addAggregate(Scb::Aggregate&); + void removeAggregate(Scb::Aggregate&); + + void addMaterial(const Sc::MaterialCore& mat); + void updateMaterial(const Sc::MaterialCore& mat); + void removeMaterial(const Sc::MaterialCore& mat); + void updateLowLevelMaterial(NpMaterial** masterMaterials); + // These methods are only to be called at fetchResults! + PX_INLINE PxU32 getNumActiveBodies() const { return mScene.getNumActiveBodies(); } + PX_INLINE Sc::BodyCore* const* getActiveBodiesArray() const { return mScene.getActiveBodiesArray(); } + + PX_INLINE PxSimulationEventCallback* getSimulationEventCallback() const; + PX_INLINE void setSimulationEventCallback(PxSimulationEventCallback* callback); + PX_INLINE PxContactModifyCallback* getContactModifyCallback() const; + PX_INLINE void setContactModifyCallback(PxContactModifyCallback* callback); + PX_INLINE PxCCDContactModifyCallback* getCCDContactModifyCallback() const; + PX_INLINE void setCCDContactModifyCallback(PxCCDContactModifyCallback* callback); + PX_INLINE PxU32 getCCDMaxPasses() const; + PX_INLINE void setCCDMaxPasses(PxU32 ccdMaxPasses); + PX_INLINE void setBroadPhaseCallback(PxBroadPhaseCallback* callback); + PX_INLINE PxBroadPhaseCallback* getBroadPhaseCallback() const; + PxBroadPhaseType::Enum getBroadPhaseType() const; + bool getBroadPhaseCaps(PxBroadPhaseCaps& caps) const; + PxU32 getNbBroadPhaseRegions() const; + PxU32 getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + PxU32 addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion); + bool removeBroadPhaseRegion(PxU32 handle); + + // Collision filtering + PX_INLINE void setFilterShaderData(const void* data, PxU32 dataSize); + PX_INLINE const void* getFilterShaderData() const; + PX_INLINE PxU32 getFilterShaderDataSize() const; + PX_INLINE PxSimulationFilterShader getFilterShader() const; + PX_INLINE PxSimulationFilterCallback* getFilterCallback() const; + + // Groups + PX_INLINE void setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance); + PX_INLINE PxDominanceGroupPair getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const; + + PX_INLINE void setSolverBatchSize(PxU32 solverBatchSize); + PX_INLINE PxU32 getSolverBatchSize() const; + + PX_INLINE void simulate(PxReal timeStep, PxBaseTask* continuation) { mScene.simulate(timeStep, continuation); } + PX_INLINE void collide(PxReal timeStep, PxBaseTask* continuation) { mScene.collide(timeStep, continuation); } + PX_INLINE void advance(PxReal timeStep, PxBaseTask* continuation) { mScene.advance(timeStep, continuation); } + PX_INLINE void endSimulation() { mScene.endSimulation(); } + + PX_INLINE void flush(bool sendPendingReports); + PX_INLINE void fireBrokenConstraintCallbacks() { mScene.fireBrokenConstraintCallbacks(); } + PX_INLINE void fireTriggerCallbacks() { mScene.fireTriggerCallbacks(); } + PX_INLINE void fireQueuedContactCallbacks() { mScene.fireQueuedContactCallbacks(false); } + PX_INLINE const Ps::Array& + getQueuedContactPairHeaders() { return mScene.getQueuedContactPairHeaders(); } + PX_FORCE_INLINE void postCallbacksPreSync() { mScene.postCallbacksPreSync(); } //cleanup tasks after the pre-sync callbacks have fired + + PX_INLINE void fireCallBacksPostSync() { mScene.fireCallbacksPostSync(); } //callbacks that are fired on the core side, after the buffers get synced + PX_INLINE void postReportsCleanup(); + + PX_INLINE const PxSceneLimits& getLimits() const { return mScene.getLimits(); } + PX_INLINE void setLimits(const PxSceneLimits& limits) { mScene.setLimits(limits); } + + PX_INLINE void getStats(PxSimulationStatistics& stats) const; + + PX_INLINE void buildActiveActors(); //build the list of active actors + PX_INLINE void buildActiveAndFrozenActors(); //build the list of active and frozen actors + + PX_INLINE PxActor** getActiveActors(PxU32& nbActorsOut); + PX_INLINE void setActiveActors(PxActor** actors, PxU32 nbActors); + PX_INLINE PxActor** getFrozenActors(PxU32& nbActorsOut); + + + PX_INLINE PxClientID createClient(); + + PX_INLINE void setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value); + PX_INLINE PxReal getVisualizationParameter(PxVisualizationParameter::Enum param) const; + + PX_INLINE void setVisualizationCullingBox(const PxBounds3& box); + PX_INLINE const PxBounds3& getVisualizationCullingBox() const; + + void shiftOrigin(const PxVec3& shift); + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + public: + void syncWriteThroughProperties(); + void syncEntireScene(); + void processPendingRemove(); + + PX_FORCE_INLINE PxU16* allocShapeMaterialBuffer(PxU32 count, PxU32& startIdx) { return allocArrayBuffer(mShapeMaterialBuffer, count, startIdx); } + PX_FORCE_INLINE const PxU16* getShapeMaterialBuffer(PxU32 startIdx) const { return &mShapeMaterialBuffer[startIdx]; } + PX_FORCE_INLINE Scb::Shape** allocShapeBuffer(PxU32 count, PxU32& startIdx) { return allocArrayBuffer(mShapePtrBuffer, count, startIdx); } + PX_FORCE_INLINE Scb::Shape** getShapeBuffer(PxU32 startIdx) { return &mShapePtrBuffer[startIdx]; } + PX_FORCE_INLINE Scb::Actor** allocActorBuffer(PxU32 count, PxU32& startIdx) { return allocArrayBuffer(mActorPtrBuffer, count, startIdx); } + PX_FORCE_INLINE Scb::Actor** getActorBuffer(PxU32 startIdx) { return &mActorPtrBuffer[startIdx]; } + + void scheduleForUpdate(Scb::Base& object); + PxU8* getStream(ScbType::Enum type); + + PX_FORCE_INLINE void removeShapeFromPendingUpdateList(Scb::Base& shape) { mShapeManager.remove(shape); } + + PX_FORCE_INLINE const Sc::Scene& getScScene() const { return mScene; } + PX_FORCE_INLINE Sc::Scene& getScScene() { return mScene; } + PX_FORCE_INLINE void prepareOutOfBoundsCallbacks() { mScene.prepareOutOfBoundsCallbacks(); } + + private: + void syncState(); + PX_FORCE_INLINE Ps::IntBool isBuffered(BufferFlag f) const { return Ps::IntBool(mBufferFlags& f); } + PX_FORCE_INLINE void markUpdated(BufferFlag f) { mBufferFlags |= f; } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + public: + + PX_FORCE_INLINE bool isPhysicsBuffering() const { return mIsBuffering; } + PX_FORCE_INLINE void setPhysicsBuffering(bool buffering) { mIsBuffering = buffering; } + + PX_FORCE_INLINE Sc::SimulationStage::Enum getSimulationStage() const { return mScene.getSimulationStage(); } + PX_FORCE_INLINE void setSimulationStage(Sc::SimulationStage::Enum stage) { mScene.setSimulationStage(stage); } + + PX_FORCE_INLINE bool isValid() const { return mScene.isValid(); } + + PX_FORCE_INLINE PxReal getWakeCounterResetValue() const { return mWakeCounterResetValue; } + + void switchRigidToNoSim(Scb::RigidObject&, bool isDynamic); + void switchRigidFromNoSim(Scb::RigidObject&, bool isDynamic); + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mScene); } + +#if PX_SUPPORT_PVD + PX_FORCE_INLINE Vd::ScbScenePvdClient& getScenePvdClient() { return mScenePvdClient; } + PX_FORCE_INLINE const Vd::ScbScenePvdClient& getScenePvdClient() const { return mScenePvdClient; } +#endif + PX_FORCE_INLINE PxU64 getContextId() const { return mScene.getContextId(); } + + private: + void addShapeInternal(Scb::Shape&); + void addShapesInternal(PxU32 nbShapes, PxShape** PX_RESTRICT shapes, size_t scbOffset, PxActor** PX_RESTRICT owners, PxU32 offsetNpToCore, bool isDynamic); + + template T* allocArrayBuffer(Ps::Array& buffer, PxU32 count, PxU32& startIdx); + + private: + + template + PX_FORCE_INLINE void addActorT(T& actor, ObjectTracker& tracker, bool noSim, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure); + + template void add(T& v, ObjectTracker& tracker, PxBounds3* uninflatedBounds, const Gu::BVHStructure* bvhStructure); + template void remove(T& v, ObjectTracker& tracker, bool wakeOnLostTouch = false); + template void addRigidNoSim(T& v, ObjectTracker& tracker, const Gu::BVHStructure* bvhStructure); + template void removeRigidNoSim(T& v, ObjectTracker& tracker); + template void processSimUpdates(S*const * scObjects, PxU32 nbObjects); + template void processUserUpdates(ObjectTracker& tracker); + template void processRemoves(ObjectTracker& tracker); + template void processShapeRemoves(ObjectTracker& tracker); + + Sc::Scene mScene; + + Ps::Array mSceneMaterialBuffer; + Ps::Mutex mSceneMaterialBufferLock; + + bool mSimulationRunning; + bool mIsBuffering; + + Cm::FlushPool mStream; // Pool for temporarily buffering user changes on objects + ShapeManager mShapeManager; + Ps::Array mShapeMaterialBuffer; // Buffered setMaterial() call might need to track list of materials (for multi material shapes) + Ps::Array mShapePtrBuffer; // List of shape pointers to track buffered calls to resetFiltering(), for example + Ps::Array mActorPtrBuffer; + RigidStaticManager mRigidStaticManager; + BodyManager mBodyManager; + ConstraintManager mConstraintManager; + ArticulationManager mArticulationManager; + ArticulationJointManager mArticulationJointManager; + AggregateManager mAggregateManager; +#if PX_SUPPORT_PVD + Vd::ScbScenePvdClient mScenePvdClient; +#endif + + PX_FORCE_INLINE void updatePvdProperties() + { +#if PX_SUPPORT_PVD + // PT: TODO: shouldn't we test PxPvdInstrumentationFlag::eDEBUG here? + if(mScenePvdClient.isConnected()) + mScenePvdClient.updatePvdProperties(); +#endif + } + + PxReal mWakeCounterResetValue; + + // note: If deletion of rigid objects is done before the sync of the simulation data then we + // might wanna consider having a separate list for deleted rigid objects (for performance + // reasons) + + //--------------------------------------------------------------------------------- + // On demand buffered data (simulation read-only data) + //--------------------------------------------------------------------------------- + Scb::SceneBuffer mBufferedData; + PxU32 mBufferFlags; + }; + +} // namespace Scb + +template +T* Scb::Scene::allocArrayBuffer(Ps::Array& buffer, PxU32 count, PxU32& startIdx) +{ + PxU32 oldSize = buffer.size(); + buffer.resize(oldSize + count); + startIdx = oldSize; + return &buffer[oldSize]; +} + +PX_INLINE void Scb::Scene::setGravity(const PxVec3& gravity) +{ + if(!isPhysicsBuffering()) + { + mScene.setGravity(gravity); + updatePvdProperties(); + } + else + { + mBufferedData.mGravity = gravity; + markUpdated(BF_GRAVITY); + } +} + +PX_INLINE PxVec3 Scb::Scene::getGravity() const +{ + if(isBuffered(BF_GRAVITY)) + return mBufferedData.mGravity; + else + return mScene.getGravity(); +} + +void Scb::Scene::setBounceThresholdVelocity(const PxReal t) +{ + if(!isPhysicsBuffering()) + { + mScene.setBounceThresholdVelocity(t); + updatePvdProperties(); + } + else + { + mBufferedData.mBounceThresholdVelocity = t; + markUpdated(BF_BOUNCETHRESHOLDVELOCITY); + } +} + +PxReal Scb::Scene::getBounceThresholdVelocity() const +{ + if(isBuffered(BF_BOUNCETHRESHOLDVELOCITY)) + return mBufferedData.mBounceThresholdVelocity; + else + return mScene.getBounceThresholdVelocity(); +} + +PX_INLINE void Scb::Scene::setFlags(PxSceneFlags flags) +{ + if(!isPhysicsBuffering()) + { + mScene.setPublicFlags(flags); + const bool pcm = (flags & PxSceneFlag::eENABLE_PCM); + mScene.setPCM(pcm); + const bool contactCache = !(flags & PxSceneFlag::eDISABLE_CONTACT_CACHE); + mScene.setContactCache(contactCache); + updatePvdProperties(); + } + else + { + mBufferedData.mFlags = flags; + markUpdated(BF_FLAGS); + } +} + +PX_INLINE PxSceneFlags Scb::Scene::getFlags() const +{ + if(isBuffered(BF_FLAGS)) + return mBufferedData.mFlags; + else + return mScene.getPublicFlags(); +} + +/////////////////////////////////////////////////////////////////////////////// + +PX_INLINE PxSimulationEventCallback* Scb::Scene::getSimulationEventCallback() const +{ + return mScene.getSimulationEventCallback(); +} + +PX_INLINE void Scb::Scene::setSimulationEventCallback(PxSimulationEventCallback* callback) +{ + if(!isPhysicsBuffering()) + mScene.setSimulationEventCallback(callback); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::setSimulationEventCallback() not allowed while simulation is running. Call will be ignored."); +} + +PX_INLINE PxContactModifyCallback* Scb::Scene::getContactModifyCallback() const +{ + return mScene.getContactModifyCallback(); +} + +PX_INLINE void Scb::Scene::setContactModifyCallback(PxContactModifyCallback* callback) +{ + if(!isPhysicsBuffering()) + mScene.setContactModifyCallback(callback); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::setContactModifyCallback() not allowed while simulation is running. Call will be ignored."); +} + +PX_INLINE PxCCDContactModifyCallback* Scb::Scene::getCCDContactModifyCallback() const +{ + return mScene.getCCDContactModifyCallback(); +} + +PX_INLINE void Scb::Scene::setCCDContactModifyCallback(PxCCDContactModifyCallback* callback) +{ + if(!isPhysicsBuffering()) + mScene.setCCDContactModifyCallback(callback); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::setCCDContactModifyCallback() not allowed while simulation is running. Call will be ignored."); +} + +PX_INLINE PxU32 Scb::Scene::getCCDMaxPasses() const +{ + return mScene.getCCDMaxPasses(); +} + +PX_INLINE void Scb::Scene::setCCDMaxPasses(PxU32 ccdMaxPasses) +{ + if(!isPhysicsBuffering()) + mScene.setCCDMaxPasses(ccdMaxPasses); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::setCCDMaxPasses() not allowed while simulation is running. Call will be ignored."); +} + +PX_INLINE PxBroadPhaseCallback* Scb::Scene::getBroadPhaseCallback() const +{ + return mScene.getBroadPhaseCallback(); +} + +PX_INLINE void Scb::Scene::setBroadPhaseCallback(PxBroadPhaseCallback* callback) +{ + if(!isPhysicsBuffering()) + mScene.setBroadPhaseCallback(callback); + else + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::setBroadPhaseCallback() not allowed while simulation is running. Call will be ignored."); +} + +/////////////////////////////////////////////////////////////////////////////// + +PX_INLINE void Scb::Scene::setFilterShaderData(const void* data, PxU32 dataSize) +{ + if(!isPhysicsBuffering()) + mScene.setFilterShaderData(data, dataSize); + else + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::setFilterShaderData() not allowed while simulation is running. Call will be ignored."); +} + +PX_INLINE const void* Scb::Scene::getFilterShaderData() const +{ + return mScene.getFilterShaderDataFast(); +} + +PX_INLINE PxU32 Scb::Scene::getFilterShaderDataSize() const +{ + return mScene.getFilterShaderDataSizeFast(); +} + +PX_INLINE PxSimulationFilterShader Scb::Scene::getFilterShader() const +{ + return mScene.getFilterShaderFast(); +} + +PX_INLINE PxSimulationFilterCallback* Scb::Scene::getFilterCallback() const +{ + return mScene.getFilterCallbackFast(); +} + +PX_INLINE void Scb::Scene::flush(bool sendPendingReports) +{ + PX_ASSERT(!isPhysicsBuffering()); + + mShapeMaterialBuffer.reset(); + mShapePtrBuffer.reset(); + mActorPtrBuffer.reset(); + + //!!! TODO: Clear all buffers used for double buffering changes (see ObjectTracker::mBufferPool) + + mScene.flush(sendPendingReports); +} + +PX_INLINE void Scb::Scene::postReportsCleanup() +{ + PX_ASSERT(!isPhysicsBuffering()); + mScene.postReportsCleanup(); +} + +PX_INLINE void Scb::Scene::setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance) +{ + if(!isPhysicsBuffering()) + { + mScene.setDominanceGroupPair(group1, group2, dominance); + updatePvdProperties(); + } + else + { + mBufferedData.setDominancePair(group1, group2, dominance); + markUpdated(BF_DOMINANCE_PAIRS); + } +} + +PX_INLINE PxDominanceGroupPair Scb::Scene::getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const +{ + if(isBuffered(BF_DOMINANCE_PAIRS)) + { + PxDominanceGroupPair dominance(0, 0); + if(mBufferedData.getDominancePair(group1, group2, dominance)) + return dominance; + } + + return mScene.getDominanceGroupPair(group1, group2); +} + +PX_INLINE void Scb::Scene::setSolverBatchSize(PxU32 solverBatchSize) +{ + if(!isPhysicsBuffering()) + { + mScene.setSolverBatchSize(solverBatchSize); + updatePvdProperties(); + } + else + { + mBufferedData.mSolverBatchSize = solverBatchSize; + markUpdated(BF_SOLVER_BATCH_SIZE); + } +} + +PX_INLINE PxU32 Scb::Scene::getSolverBatchSize() const +{ + if(isBuffered(BF_SOLVER_BATCH_SIZE)) + return mBufferedData.mSolverBatchSize; + else + return mScene.getSolverBatchSize(); +} + +PX_INLINE void Scb::Scene::getStats(PxSimulationStatistics& stats) const +{ + PX_ASSERT(!isPhysicsBuffering()); + + mScene.getStats(stats); +} + +PX_INLINE void Scb::Scene::buildActiveActors() +{ + PX_ASSERT(!isPhysicsBuffering()); + + mScene.buildActiveActors(); +} + +PX_INLINE void Scb::Scene::buildActiveAndFrozenActors() +{ + PX_ASSERT(!isPhysicsBuffering()); + + mScene.buildActiveAndFrozenActors(); +} + +PX_INLINE PxActor** Scb::Scene::getActiveActors(PxU32& nbActorsOut) +{ + if(!isPhysicsBuffering()) + return mScene.getActiveActors(nbActorsOut); + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::getActiveActors() not allowed while simulation is running. Call will be ignored."); + nbActorsOut = 0; + return NULL; + } +} + +PX_INLINE void Scb::Scene::setActiveActors(PxActor** actors, PxU32 nbActors) +{ + mScene.setActiveActors(actors, nbActors); +} + +PX_INLINE PxActor** Scb::Scene::getFrozenActors(PxU32& nbActorsOut) +{ + if(!isPhysicsBuffering()) + return mScene.getFrozenActors(nbActorsOut); + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::getFrozenActors() not allowed while simulation is running. Call will be ignored."); + nbActorsOut = 0; + return NULL; + } +} + +PX_INLINE PxClientID Scb::Scene::createClient() +{ + if(!isPhysicsBuffering()) + return mScene.createClient(); + else + return PxClientID(mScene.getNbClients() + mBufferedData.mNumClientsCreated++); +} + +PX_INLINE void Scb::Scene::setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) +{ + if(!isPhysicsBuffering()) + mScene.setVisualizationParameter(param, value); + else + { + PX_ASSERT(param < PxVisualizationParameter::eNUM_VALUES); + mBufferedData.mVisualizationParamChanged[param] = 1; + mBufferedData.mVisualizationParam[param] = value; + markUpdated(BF_VISUALIZATION); + } +} + +PX_INLINE PxReal Scb::Scene::getVisualizationParameter(PxVisualizationParameter::Enum param) const +{ + PX_ASSERT(param < PxVisualizationParameter::eNUM_VALUES); + + if(isBuffered(BF_VISUALIZATION) && mBufferedData.mVisualizationParamChanged[param]) + return mBufferedData.mVisualizationParam[param]; + else + return mScene.getVisualizationParameter(param); +} + +PX_INLINE void Scb::Scene::setVisualizationCullingBox(const PxBounds3& box) +{ + if(!isPhysicsBuffering()) + mScene.setVisualizationCullingBox(box); + else + { + mBufferedData.mVisualizationCullingBox = box; + markUpdated(BF_CULLING_BOX); + } +} + +PX_INLINE const PxBounds3& Scb::Scene::getVisualizationCullingBox() const +{ + if(isBuffered(BF_CULLING_BOX)) + return mBufferedData.mVisualizationCullingBox; + else + return mScene.getVisualizationCullingBox(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h b/src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h new file mode 100644 index 000000000..1fa8ad9d0 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h @@ -0,0 +1,153 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCB_SCENE_BUFFER +#define PX_PHYSICS_SCB_SCENE_BUFFER + +#include "CmPhysXCommon.h" + +#include "ScScene.h" + +namespace physx +{ +namespace Scb +{ + +struct SceneBuffer +{ +public: + static const PxU32 sMaxNbDominanceGroups = 32; + + PX_INLINE SceneBuffer(); + + PX_INLINE void clearDominanceBuffer(); + PX_INLINE void setDominancePair(PxU32 group1, PxU32 group2, const PxDominanceGroupPair& dominance); + PX_INLINE bool getDominancePair(PxU32 group1, PxU32 group2, PxDominanceGroupPair& dominance) const; + PX_INLINE void syncDominancePairs(Sc::Scene& scene); + + PX_INLINE void clearVisualizationParams(); + + PxReal mVisualizationParam[PxVisualizationParameter::eNUM_VALUES]; + PxU8 mVisualizationParamChanged[PxVisualizationParameter::eNUM_VALUES]; + PxBounds3 mVisualizationCullingBox; +private: + PxU32 mDominancePairFlag[sMaxNbDominanceGroups - 1]; + PxU32 mDominancePairValues[sMaxNbDominanceGroups]; +public: + PxVec3 mGravity; + PxReal mBounceThresholdVelocity; + PxSceneFlags mFlags; + PxU32 mSolverBatchSize; + PxU32 mNumClientsCreated; +}; + +PX_INLINE SceneBuffer::SceneBuffer() : + mNumClientsCreated (0) +{ + clearDominanceBuffer(); + clearVisualizationParams(); +} + +PX_FORCE_INLINE void SceneBuffer::clearDominanceBuffer() +{ + PxMemZero(&mDominancePairFlag, (sMaxNbDominanceGroups - 1) * sizeof(PxU32)); +} + +PX_FORCE_INLINE void SceneBuffer::clearVisualizationParams() +{ + PxMemZero(mVisualizationParamChanged, PxVisualizationParameter::eNUM_VALUES * sizeof(PxU8)); +} + +PX_INLINE void SceneBuffer::setDominancePair(PxU32 group1, PxU32 group2, const PxDominanceGroupPair& dominance) +{ + PX_ASSERT(group1 != group2); + PX_ASSERT(group1 < sMaxNbDominanceGroups); + PX_ASSERT(group2 < sMaxNbDominanceGroups); + + if(group1 < group2) + mDominancePairFlag[group1] |= (1 << group2); + else + mDominancePairFlag[group2] |= (1 << group1); + + if(dominance.dominance0 != 0.0f) + mDominancePairValues[group1] |= (1 << group2); + else + mDominancePairValues[group1] &= ~(1 << group2); + + if(dominance.dominance1 != 0.0f) + mDominancePairValues[group2] |= (1 << group1); + else + mDominancePairValues[group2] &= ~(1 << group1); +} + +PX_INLINE bool SceneBuffer::getDominancePair(PxU32 group1, PxU32 group2, PxDominanceGroupPair& dominance) const +{ + PX_ASSERT(group1 != group2); + PX_ASSERT(group1 < sMaxNbDominanceGroups); + PX_ASSERT(group2 < sMaxNbDominanceGroups); + + PxU32 isBuffered; + if(group1 < group2) + isBuffered = mDominancePairFlag[group1] & (1 << group2); + else + isBuffered = mDominancePairFlag[group2] & (1 << group1); + + if(!isBuffered) + return false; + + dominance.dominance0 = PxU8((mDominancePairValues[group1] & (1 << group2)) >> group2); + dominance.dominance1 = PxU8((mDominancePairValues[group2] & (1 << group1)) >> group1); + return true; +} + +PX_INLINE void SceneBuffer::syncDominancePairs(Sc::Scene& scene) +{ + for(PxU32 i=0; i<(sMaxNbDominanceGroups - 1); i++) + { + if(mDominancePairFlag[i]) + { + for(PxU32 j=(i+1); j(&(reinterpret_cast(0)->getScConstraint())); + const size_t scbOffset = reinterpret_cast(&(reinterpret_cast(0)->getScbConstraint())); + return reinterpret_cast(reinterpret_cast(scConstraint) - scOffset - scbOffset); + } + + /////////////////////////////////////////////////////////////////////////////// + + PX_FORCE_INLINE static const PxActor* getPxActor(const Scb::Actor* scbActor) + { + const PxActorType::Enum type = scbActor->getActorCore().getActorCoreType(); + if(type == PxActorType::eRIGID_DYNAMIC) + return getNpRigidDynamic(static_cast(scbActor)); + else if(type == PxActorType::eRIGID_STATIC) + return getNpRigidStatic(static_cast(scbActor)); + else if(type == PxActorType::eARTICULATION_LINK) + return getNpArticulationLink(static_cast(scbActor)); + + return NULL; + } + + struct CreateOp + { + CreateOp& operator=(const CreateOp&); + physx::pvdsdk::PvdDataStream& mStream; + PvdMetaDataBinding& mBinding; + PsPvd* mPvd; + PxScene& mScene; + CreateOp(physx::pvdsdk::PvdDataStream& str, PvdMetaDataBinding& bind, PsPvd* pvd, PxScene& scene) + : mStream(str), mBinding(bind), mPvd(pvd), mScene(scene) + { + } + template + void operator()(const TDataType& dtype) + { + mBinding.createInstance(mStream, dtype, mScene, PxGetPhysics(), mPvd); + } + void operator()(const PxArticulationLink&) + { + } + }; + + struct UpdateOp + { + UpdateOp& operator=(const UpdateOp&); + physx::pvdsdk::PvdDataStream& mStream; + PvdMetaDataBinding& mBinding; + UpdateOp(physx::pvdsdk::PvdDataStream& str, PvdMetaDataBinding& bind) : mStream(str), mBinding(bind) + { + } + template + void operator()(const TDataType& dtype) + { + mBinding.sendAllProperties(mStream, dtype); + } + }; + + struct DestroyOp + { + DestroyOp& operator=(const DestroyOp&); + physx::pvdsdk::PvdDataStream& mStream; + PvdMetaDataBinding& mBinding; + PxScene& mScene; + DestroyOp(physx::pvdsdk::PvdDataStream& str, PvdMetaDataBinding& bind, PxScene& scene) + : mStream(str), mBinding(bind), mScene(scene) + { + } + template + void operator()(const TDataType& dtype) + { + mBinding.destroyInstance(mStream, dtype, mScene); + } + void operator()(const PxArticulationLink& dtype) + { + mBinding.destroyInstance(mStream, dtype); + } + }; + + template + inline void BodyTypeOperation(const Scb::Body* scbBody, TOperator op) + { + bool isArticulationLink = scbBody->getActorType() == PxActorType::eARTICULATION_LINK; + if(isArticulationLink) + { + const NpArticulationLink* link = getNpArticulationLink(scbBody); + op(*static_cast(link)); + } + else + { + const NpRigidDynamic* npRigidDynamic = getNpRigidDynamic(scbBody); + op(*static_cast(npRigidDynamic)); + } + } + + template + inline void ActorTypeOperation(const PxActor* actor, TOperator op) + { + switch(actor->getType()) + { + case PxActorType::eRIGID_STATIC: + op(*static_cast(actor)); + break; + case PxActorType::eRIGID_DYNAMIC: + op(*static_cast(actor)); + break; + case PxActorType::eARTICULATION_LINK: + op(*static_cast(actor)); + break; + case PxActorType::eACTOR_COUNT: + case PxActorType::eACTOR_FORCE_DWORD: + PX_ASSERT(false); + break; + }; + } + + namespace + { + struct PvdConstraintVisualizer : public PxConstraintVisualizer + { + PX_NOCOPY(PvdConstraintVisualizer) + public: + physx::pvdsdk::PvdUserRenderer& mRenderer; + PvdConstraintVisualizer(const void* id, physx::pvdsdk::PvdUserRenderer& r) : mRenderer(r) + { + mRenderer.setInstanceId(id); + } + virtual void visualizeJointFrames(const PxTransform& parent, const PxTransform& child) + { + mRenderer.visualizeJointFrames(parent, child); + } + + virtual void visualizeLinearLimit(const PxTransform& t0, const PxTransform& t1, PxReal value, bool active) + { + mRenderer.visualizeLinearLimit(t0, t1, PxF32(value), active); + } + + virtual void visualizeAngularLimit(const PxTransform& t0, PxReal lower, PxReal upper, bool active) + { + mRenderer.visualizeAngularLimit(t0, PxF32(lower), PxF32(upper), active); + } + + virtual void visualizeLimitCone(const PxTransform& t, PxReal tanQSwingY, PxReal tanQSwingZ, bool active) + { + mRenderer.visualizeLimitCone(t, PxF32(tanQSwingY), PxF32(tanQSwingZ), active); + } + + virtual void visualizeDoubleCone(const PxTransform& t, PxReal angle, bool active) + { + mRenderer.visualizeDoubleCone(t, PxF32(angle), active); + } + + virtual void visualizeLine( const PxVec3& p0, const PxVec3& p1, PxU32 color) + { + const PvdDebugLine line(p0, p1, color); + mRenderer.drawLines(&line, 1); + } + }; + } + + class SceneRendererClient : public RendererEventClient, public physx::shdfnd::UserAllocated + { + PX_NOCOPY(SceneRendererClient) + public: + SceneRendererClient(PvdUserRenderer* renderer, PxPvd* pvd):mRenderer(renderer) + { + mStream = PvdDataStream::create(pvd); + mStream->createInstance(renderer); + } + + ~SceneRendererClient() + { + mStream->destroyInstance(mRenderer); + mStream->release(); + } + + virtual void handleBufferFlush(const uint8_t* inData, uint32_t inLength) + { + mStream->setPropertyValue(mRenderer, "events", inData, inLength); + } + + private: + + PvdUserRenderer* mRenderer; + PvdDataStream* mStream; + }; + +} // namespace + +ScbScenePvdClient::ScbScenePvdClient(Scb::Scene& scene) : + mPvd (NULL), + mScbScene (scene), + mPvdDataStream (NULL), + mUserRender (NULL), + mRenderClient (NULL), + mIsConnected (false) +{ +} + +ScbScenePvdClient::~ScbScenePvdClient() +{ + if(mPvd) + mPvd->removeClient(this); +} + +void ScbScenePvdClient::updateCamera(const char* name, const PxVec3& origin, const PxVec3& up, const PxVec3& target) +{ + if(mIsConnected) + mPvdDataStream->updateCamera(name, origin, up, target); +} + +void ScbScenePvdClient::drawPoints(const PvdDebugPoint* points, PxU32 count) +{ + if(mUserRender) + mUserRender->drawPoints(points, count); +} + +void ScbScenePvdClient::drawLines(const PvdDebugLine* lines, PxU32 count) +{ + if(mUserRender) + mUserRender->drawLines(lines, count); +} + +void ScbScenePvdClient::drawTriangles(const PvdDebugTriangle* triangles, PxU32 count) +{ + if(mUserRender) + mUserRender->drawTriangles(triangles, count); +} + +void ScbScenePvdClient::drawText(const PvdDebugText& text) +{ + if(mUserRender) + mUserRender->drawText(text); +} + +void ScbScenePvdClient::setScenePvdFlag(PxPvdSceneFlag::Enum flag, bool value) +{ + if(value) + mFlags |= flag; + else + mFlags &= ~flag; +} + +void ScbScenePvdClient::onPvdConnected() +{ + if(mIsConnected || !mPvd) + return; + + mIsConnected = true; + + mPvdDataStream = PvdDataStream::create(mPvd); + + mUserRender = PvdUserRenderer::create(); + mRenderClient = PX_NEW(SceneRendererClient)(mUserRender, mPvd); + mUserRender->setClient(mRenderClient); + + sendEntireScene(); +} + +void ScbScenePvdClient::onPvdDisconnected() +{ + if(!mIsConnected) + return; + mIsConnected = false; + + PX_DELETE(mRenderClient); + mRenderClient = NULL; + mUserRender->release(); + mUserRender = NULL; + mPvdDataStream->release(); + mPvdDataStream = NULL; +} + +void ScbScenePvdClient::updatePvdProperties() +{ + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *mScbScene.getPxScene()); +} + +void ScbScenePvdClient::releasePvdInstance() +{ + if(mPvdDataStream) + { + PxScene* theScene = mScbScene.getPxScene(); + // remove from parent + mPvdDataStream->removeObjectRef(&PxGetPhysics(), "Scenes", theScene); + mPvdDataStream->destroyInstance(theScene); + } +} + +// PT: this is only called once, from "onPvdConnected" +void ScbScenePvdClient::sendEntireScene() +{ + NpScene* npScene = static_cast(mScbScene.getPxScene()); + + if(npScene->getFlagsFast() & PxSceneFlag::eREQUIRE_RW_LOCK) // getFlagsFast() will trigger a warning of lock check + npScene->lockRead(__FILE__, __LINE__); + + PxPhysics& physics = PxGetPhysics(); + { + PxScene* theScene = mScbScene.getPxScene(); + mPvdDataStream->createInstance(theScene); + updatePvdProperties(); + + // Create parent/child relationship. + mPvdDataStream->setPropertyValue(theScene, "Physics", reinterpret_cast(&physics)); + mPvdDataStream->pushBackObjectRef(&physics, "Scenes", theScene); + } + + // materials: + { + PxsMaterialManager& manager = mScbScene.getScScene().getMaterialManager(); + PxsMaterialManagerIterator iter(manager); + PxsMaterialCore* mat; + while(iter.getNextMaterial(mat)) + { + const PxMaterial* theMaterial = mat->getNxMaterial(); + if(mPvd->registerObject(theMaterial)) + mMetaDataBinding.createInstance(*mPvdDataStream, *theMaterial, physics); + }; + } + + if(mPvd->getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG) + { + Ps::Array actorArray; + + // RBs + // static: + { + PxU32 numActors = npScene->getNbActors(PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC); + actorArray.resize(numActors); + npScene->getActors(PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC, actorArray.begin(), + actorArray.size()); + for(PxU32 i = 0; i < numActors; i++) + { + PxActor* pxActor = actorArray[i]; + if(pxActor->is()) + mMetaDataBinding.createInstance(*mPvdDataStream, *static_cast(pxActor), *npScene, physics, mPvd); + else + mMetaDataBinding.createInstance(*mPvdDataStream, *static_cast(pxActor), *npScene, physics, mPvd); + } + } + // articulations & links + { + Ps::Array articulations; + PxU32 numArticulations = npScene->getNbArticulations(); + articulations.resize(numArticulations); + npScene->getArticulations(articulations.begin(), articulations.size()); + for(PxU32 i = 0; i < numArticulations; i++) + mMetaDataBinding.createInstance(*mPvdDataStream, *articulations[i], *npScene, physics, mPvd); + } + + // joints + { + Sc::ConstraintCore*const * constraints = mScbScene.getScScene().getConstraints(); + PxU32 nbConstraints = mScbScene.getScScene().getNbConstraints(); + for(PxU32 i = 0; i < nbConstraints; i++) + { + updateConstraint(*constraints[i], PxPvdUpdateType::CREATE_INSTANCE); + updateConstraint(*constraints[i], PxPvdUpdateType::UPDATE_ALL_PROPERTIES); + } + } + } + + if(npScene->getFlagsFast() & PxSceneFlag::eREQUIRE_RW_LOCK) + npScene->unlockRead(); +} + +void ScbScenePvdClient::updateConstraint(const Sc::ConstraintCore& scConstraint, PxU32 updateType) +{ + PxConstraintConnector* conn = scConstraint.getPxConnector(); + if(conn && checkPvdDebugFlag()) + conn->updatePvdProperties(*mPvdDataStream, scConstraint.getPxConstraint(), PxPvdUpdateType::Enum(updateType)); +} + +void ScbScenePvdClient::createPvdInstance(const PxActor* actor) +{ + if(checkPvdDebugFlag()) + ActorTypeOperation(actor, CreateOp(*mPvdDataStream, mMetaDataBinding, mPvd, *mScbScene.getPxScene())); +} + +void ScbScenePvdClient::updatePvdProperties(const PxActor* actor) +{ + if(checkPvdDebugFlag()) + ActorTypeOperation(actor, UpdateOp(*mPvdDataStream, mMetaDataBinding)); +} + +void ScbScenePvdClient::releasePvdInstance(const PxActor* actor) +{ + if(checkPvdDebugFlag()) + ActorTypeOperation(actor, DestroyOp(*mPvdDataStream, mMetaDataBinding, *mScbScene.getPxScene())); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Actor* actor) +{ + // PT: why not UPDATE_PVD_PROPERTIES_CHECK() here? + createPvdInstance(getPxActor(actor)); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Actor* actor) +{ + // PT: why not UPDATE_PVD_PROPERTIES_CHECK() here? + updatePvdProperties(getPxActor(actor)); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::Actor* actor) +{ + // PT: why not UPDATE_PVD_PROPERTIES_CHECK() here? + releasePvdInstance(getPxActor(actor)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Body* body) +{ + if(checkPvdDebugFlag() && body->getActorType() != PxActorType::eARTICULATION_LINK) + BodyTypeOperation(body, CreateOp(*mPvdDataStream, mMetaDataBinding, mPvd, *mScbScene.getPxScene())); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Body* body) +{ + if(checkPvdDebugFlag()) + BodyTypeOperation(body, UpdateOp(*mPvdDataStream, mMetaDataBinding)); +} + +void ScbScenePvdClient::updateKinematicTarget(const Scb::Body* body, const PxTransform& p) +{ + if(checkPvdDebugFlag()) + mPvdDataStream->setPropertyValue(getNpRigidDynamic(body), "KinematicTarget", p); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::RigidStatic* rigidStatic) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.createInstance(*mPvdDataStream, *getNpRigidStatic(rigidStatic), *mScbScene.getPxScene(), PxGetPhysics(), mPvd); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::RigidStatic* rigidStatic) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *getNpRigidStatic(rigidStatic)); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::RigidObject* rigidObject) +{ + releasePvdInstance(getPxActor(rigidObject)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Constraint* constraint) +{ + if(checkPvdDebugFlag()) + updateConstraint(constraint->getScConstraint(), PxPvdUpdateType::CREATE_INSTANCE); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Constraint* constraint) +{ + if(checkPvdDebugFlag()) + updateConstraint(constraint->getScConstraint(), PxPvdUpdateType::UPDATE_ALL_PROPERTIES); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::Constraint* constraint) +{ + const Sc::ConstraintCore& scConstraint = constraint->getScConstraint(); + PxConstraintConnector* conn; + if(checkPvdDebugFlag() && (conn = scConstraint.getPxConnector()) != NULL) + conn->updatePvdProperties(*mPvdDataStream, scConstraint.getPxConstraint(), PxPvdUpdateType::RELEASE_INSTANCE); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Articulation* articulation) +{ + if (checkPvdDebugFlag()) + { + if(articulation->getArticulationType() == PxArticulationBase::eMaximumCoordinate) + mMetaDataBinding.createInstance(*mPvdDataStream, *getNpArticulation(articulation), *mScbScene.getPxScene(), PxGetPhysics(), mPvd); + else + mMetaDataBinding.createInstance(*mPvdDataStream, *getNpArticulationRC(articulation), *mScbScene.getPxScene(), PxGetPhysics(), mPvd); + } +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Articulation* articulation) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *getNpArticulation(articulation)); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::Articulation* articulation) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.destroyInstance(*mPvdDataStream, *getNpArticulation(articulation), *mScbScene.getPxScene()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::ArticulationJoint* articulationJoint) +{ + PX_UNUSED(articulationJoint); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::ArticulationJoint* articulationJoint) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *getNpArticulationJoint(articulationJoint)); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::ArticulationJoint* articulationJoint) +{ + PX_UNUSED(articulationJoint); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Sc::MaterialCore* materialCore) +{ + if(checkPvdDebugFlag()) + { + const PxMaterial* theMaterial = materialCore->getNxMaterial(); + if(mPvd->registerObject(theMaterial)) + mMetaDataBinding.createInstance(*mPvdDataStream, *theMaterial, PxGetPhysics()); + } +} + +void ScbScenePvdClient::updatePvdProperties(const Sc::MaterialCore* materialCore) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *materialCore->getNxMaterial()); +} + +void ScbScenePvdClient::releasePvdInstance(const Sc::MaterialCore* materialCore) +{ + if(checkPvdDebugFlag() && mPvd->unRegisterObject(materialCore->getNxMaterial() ) ) + mMetaDataBinding.destroyInstance(*mPvdDataStream, *materialCore->getNxMaterial(), PxGetPhysics()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Shape* shape, PxActor& owner) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.createPVDInstance", getContextId(mScbScene)); + const PxShape* npShape = getNpShape(shape); + mMetaDataBinding.createInstance(*mPvdDataStream, *npShape, static_cast(owner), PxGetPhysics(), mPvd); + } +} + +static void addShapesToPvd(PxU32 nbShapes, void* const* shapes, const size_t offset, PxActor& pxActor, PsPvd* pvd, PvdDataStream& stream, PvdMetaDataBinding& binding) +{ + PxPhysics& physics = PxGetPhysics(); + for(PxU32 i=0;i(reinterpret_cast(shapes[i]) + offset); + const PxShape* npShape = getNpShape(shape); + binding.createInstance(stream, *npShape, static_cast(pxActor), physics, pvd); + } +} + +void ScbScenePvdClient::addBodyAndShapesToPvd(Scb::Body& b) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.createPVDInstance", getContextId(mScbScene)); + createPvdInstance(&b); + + const size_t offset = NpShapeGetScPtrOffset() - Scb::Shape::getScOffset(); + PxActor& pxActor = *b.getScBody().getPxActor(); + + void* const* shapes; + const PxU32 nbShapes = NpRigidDynamicGetShapes(b, shapes); + addShapesToPvd(nbShapes, shapes, offset, pxActor, mPvd, *mPvdDataStream, mMetaDataBinding); + } +} + +void ScbScenePvdClient::addStaticAndShapesToPvd(Scb::RigidStatic& s) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.createPVDInstance", getContextId(mScbScene)); + createPvdInstance(&s); + + const size_t offset = NpShapeGetScPtrOffset() - Scb::Shape::getScOffset(); + PxActor& pxActor = *s.getScStatic().getPxActor(); + + void* const* shapes; + const PxU32 nbShapes = NpRigidStaticGetShapes(s, shapes); + addShapesToPvd(nbShapes, shapes, offset, pxActor, mPvd, *mPvdDataStream, mMetaDataBinding); + } +} + +void ScbScenePvdClient::updateMaterials(const Scb::Shape* shape) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.updateMaterials(*mPvdDataStream, *getNpShape(const_cast(shape)), mPvd); +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Shape* shape) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *getNpShape(const_cast(shape))); +} + +void ScbScenePvdClient::releaseAndRecreateGeometry(const Scb::Shape* shape) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.releaseAndRecreateGeometry(*mPvdDataStream, *getNpShape(const_cast(shape)), + NpPhysics::getInstance(), mPvd); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::Shape* shape, PxActor& owner) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.releasePVDInstance", getContextId(mScbScene)); + + const NpShape* npShape = getNpShape(shape); + mMetaDataBinding.destroyInstance(*mPvdDataStream, *npShape, static_cast(owner)); + + const PxU32 numMaterials = npShape->getNbMaterials(); + PX_ALLOCA(materialPtr, PxMaterial*, numMaterials); + npShape->getMaterials(materialPtr, numMaterials); + + for(PxU32 idx = 0; idx < numMaterials; ++idx) + releasePvdInstance(&(static_cast(materialPtr[idx])->getScMaterial())); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::originShift(PxVec3 shift) +{ + mMetaDataBinding.originShift(*mPvdDataStream, mScbScene.getPxScene(), shift); +} + +void ScbScenePvdClient::frameStart(PxReal simulateElapsedTime) +{ + PX_PROFILE_ZONE("Basic.pvdFrameStart", mScbScene.getContextId()); + + if(!mIsConnected) + return; + + mPvdDataStream->flushPvdCommand(); + mMetaDataBinding.sendBeginFrame(*mPvdDataStream, mScbScene.getPxScene(), simulateElapsedTime); +} + +void ScbScenePvdClient::frameEnd() +{ + PX_PROFILE_ZONE("Basic.pvdFrameEnd", mScbScene.getContextId()); + + if(!mIsConnected) + { + if(mPvd) + mPvd->flush(); // Even if we aren't connected, we may need to flush buffered events. + return; + } + + PxScene* theScene = mScbScene.getPxScene(); + + mMetaDataBinding.sendStats(*mPvdDataStream, theScene); + + // flush our data to the main connection + mPvd->flush(); + + // End the frame *before* we send the dynamic object current data. + // This ensures that contacts end up synced with the rest of the system. + // Note that contacts were sent much earler in NpScene::fetchResults. + mMetaDataBinding.sendEndFrame(*mPvdDataStream, mScbScene.getPxScene()); + + if(mPvd->getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG) + { + PX_PROFILE_ZONE("PVD.sceneUpdate", getContextId(mScbScene)); + + PvdVisualizer* vizualizer = NULL; + const bool visualizeJoints = getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS; + if(visualizeJoints) + vizualizer = this; + + mMetaDataBinding.updateDynamicActorsAndArticulations(*mPvdDataStream, theScene, vizualizer); + } + + // frame end moved to update contacts to have them in the previous frame. +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::createPvdInstance(const Scb::Aggregate* aggregate) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.createPVDInstance", getContextId(mScbScene)); + const NpAggregate* npAggregate = getNpAggregate(aggregate); + mMetaDataBinding.createInstance(*mPvdDataStream, *npAggregate, *mScbScene.getPxScene()); + } +} + +void ScbScenePvdClient::updatePvdProperties(const Scb::Aggregate* aggregate) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.sendAllProperties(*mPvdDataStream, *getNpAggregate(aggregate)); +} + +void ScbScenePvdClient::attachAggregateActor(const Scb::Aggregate* aggregate, Scb::Actor* actor) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.attachAggregateActor(*mPvdDataStream, *getNpAggregate(aggregate), *getPxActor(actor)); +} + +void ScbScenePvdClient::detachAggregateActor(const Scb::Aggregate* aggregate, Scb::Actor* actor) +{ + if(checkPvdDebugFlag()) + mMetaDataBinding.detachAggregateActor(*mPvdDataStream, *getNpAggregate(aggregate), *getPxActor(actor)); +} + +void ScbScenePvdClient::releasePvdInstance(const Scb::Aggregate* aggregate) +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.releasePVDInstance", getContextId(mScbScene)); + const NpAggregate* npAggregate = getNpAggregate(aggregate); + mMetaDataBinding.destroyInstance(*mPvdDataStream, *npAggregate, *mScbScene.getPxScene()); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScbScenePvdClient::updateJoints() +{ + if(checkPvdDebugFlag()) + { + PX_PROFILE_ZONE("PVD.updateJoints", getContextId(mScbScene)); + + const bool visualizeJoints = getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS; + + Sc::ConstraintCore*const * constraints = mScbScene.getScScene().getConstraints(); + const PxU32 nbConstraints = mScbScene.getScScene().getNbConstraints(); + PxI64 constraintCount = 0; + + for(PxU32 i=0; iisDirty() + ? PxPvdUpdateType::UPDATE_ALL_PROPERTIES + : PxPvdUpdateType::UPDATE_SIM_PROPERTIES; + updateConstraint(*constraint, updateType); + PxConstraintConnector* conn = constraint->getPxConnector(); + // visualization is updated here + { + PxU32 typeId = 0; + void* joint = NULL; + if(conn) + joint = conn->getExternalReference(typeId); + // visualize: + Sc::ConstraintSim* sim = constraint->getSim(); + if(visualizeJoints && sim && sim->getConstantsLL() && joint && constraint->getVisualize()) + { + Sc::BodySim* b0 = sim->getBody(0); + Sc::BodySim* b1 = sim->getBody(1); + PxTransform t0 = b0 ? b0->getBody2World() : PxTransform(PxIdentity); + PxTransform t1 = b1 ? b1->getBody2World() : PxTransform(PxIdentity); + PvdConstraintVisualizer viz(joint, *mUserRender); + (*constraint->getVisualize())(viz, sim->getConstantsLL(), t0, t1, 0xffffFFFF); + } + } + ++constraintCount; + } + + mUserRender->flushRenderEvents(); + } +} + +void ScbScenePvdClient::updateContacts() +{ + if(!checkPvdDebugFlag()) + return; + + PX_PROFILE_ZONE("PVD.updateContacts", getContextId(mScbScene)); + + // if contacts are disabled, send empty array and return + const PxScene* theScene(mScbScene.getPxScene()); + if(!(getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_CONTACTS)) + { + mMetaDataBinding.sendContacts(*mPvdDataStream, *theScene); + return; + } + + PxsContactManagerOutputIterator outputIter; + + Sc::ContactIterator contactIter; + mScbScene.getScScene().initContactsIterator(contactIter, outputIter); + Sc::ContactIterator::Pair* pair; + Sc::Contact* contact; + Ps::Array contacts; + while ((pair = contactIter.getNextPair()) != NULL) + { + while ((contact = pair->getNextContact()) != NULL) + contacts.pushBack(*contact); + } + + mMetaDataBinding.sendContacts(*mPvdDataStream, *theScene, contacts); +} + +void ScbScenePvdClient::updateSceneQueries() +{ + if(checkPvdDebugFlag() && (getScenePvdFlagsFast() & PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES)) + mMetaDataBinding.sendSceneQueries(*mPvdDataStream, *mScbScene.getPxScene(), mPvd); +} + +void ScbScenePvdClient::setCreateContactReports(bool b) +{ + mScbScene.getScScene().setCreateContactReports(b); +} + +void ScbScenePvdClient::visualize(PxArticulationLink& link) +{ + NpArticulationLink& npLink = static_cast(link); + const void* itemId = npLink.getInboundJoint(); + if(itemId && mUserRender) + { + PvdConstraintVisualizer viz(itemId, *mUserRender); + npLink.visualizeJoint(viz); + } +} + +void ScbScenePvdClient::visualize(const PxRenderBuffer& debugRenderable) +{ + if(mUserRender) + { + // PT: I think the mUserRender object can contain extra data (including things coming from the user), because the various + // draw functions are exposed e.g. in PxPvdSceneClient.h. So I suppose we have to keep the render buffer around regardless + // of the connection flags. Thus I only skip the "drawRenderbuffer" call, for minimal intrusion into this file. + if(checkPvdDebugFlag()) + { + mUserRender->drawRenderbuffer( + reinterpret_cast(debugRenderable.getPoints()), debugRenderable.getNbPoints(), + reinterpret_cast(debugRenderable.getLines()), debugRenderable.getNbLines(), + reinterpret_cast(debugRenderable.getTriangles()), debugRenderable.getNbTriangles()); + } + mUserRender->flushRenderEvents(); + } +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h b/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h new file mode 100644 index 000000000..d84414b2b --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h @@ -0,0 +1,202 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SCB_SCENE_PVD_CLIENT_H +#define SCB_SCENE_PVD_CLIENT_H + +#include "PxPhysXConfig.h" + +#if PX_SUPPORT_PVD + +#include "foundation/PxStrideIterator.h" +#include "pvd/PxPvdTransport.h" + +#include "PxPvdSceneClient.h" +#include "PvdMetaDataPvdBinding.h" + +#include "CmBitMap.h" + +#include "PxPvdClient.h" +#include "PxPvdUserRenderer.h" +#include "PsPvd.h" + +namespace physx +{ +class PxScene; +class PxActor; +class PxShape; +class PxGeometryHolder; +class PxArticulationLink; +class PxRenderBuffer; + +namespace Scb +{ +class Scene; +class Actor; +class Body; +class RigidStatic; +class RigidObject; +class Shape; +class Constraint; +class Articulation; +class ArticulationJoint; +class Aggregate; +class SoftBody; +} + +namespace Sc +{ +class MaterialCore; +class ConstraintCore; +} + +namespace Vd +{ +class ScbScenePvdClient : public PxPvdSceneClient, public PvdClient, public PvdVisualizer +{ + PX_NOCOPY(ScbScenePvdClient) + public: + ScbScenePvdClient(Scb::Scene& scene); + virtual ~ScbScenePvdClient(); + + // PxPvdSceneClient + virtual void setScenePvdFlag(PxPvdSceneFlag::Enum flag, bool value); + virtual void setScenePvdFlags(PxPvdSceneFlags flags) { mFlags = flags; } + virtual PxPvdSceneFlags getScenePvdFlags() const { return mFlags; } + virtual void updateCamera(const char* name, const PxVec3& origin, const PxVec3& up, const PxVec3& target); + virtual void drawPoints(const PvdDebugPoint* points, PxU32 count); + virtual void drawLines(const PvdDebugLine* lines, PxU32 count); + virtual void drawTriangles(const PvdDebugTriangle* triangles, PxU32 count); + virtual void drawText(const PvdDebugText& text); + virtual PvdClient* getClientInternal() { return this; } + //~PxPvdSceneClient + + // pvdClient + virtual PvdDataStream* getDataStream() { return mPvdDataStream; } + virtual PvdMetaDataBinding* getMetaDataBinding() { return &mMetaDataBinding; } + virtual PvdUserRenderer* getUserRender() { return mUserRender; } + virtual bool isConnected() const { return mIsConnected; } + virtual void onPvdConnected(); + virtual void onPvdDisconnected(); + virtual void flush() {} + //~pvdClient + + PX_FORCE_INLINE bool checkPvdDebugFlag() const + { + return mIsConnected && (mPvd->getInstrumentationFlags() & PxPvdInstrumentationFlag::eDEBUG); + } + + PX_FORCE_INLINE PxPvdSceneFlags getScenePvdFlagsFast() const { return mFlags; } + PX_FORCE_INLINE void setPsPvd(PsPvd* pvd) { mPvd = pvd; } + + void frameStart(PxReal simulateElapsedTime); + void frameEnd(); + + void updatePvdProperties(); + void releasePvdInstance(); + + void createPvdInstance (const PxActor* actor); // temporary for deformables and particle systems - sschirm: deformables and particles are gone... + void updatePvdProperties(const PxActor* actor); + void releasePvdInstance (const PxActor* actor); // temporary for deformables and particle systems - sschirm: deformables and particles are gone... + + void createPvdInstance (const Scb::Actor* actor); // temporary for deformables and particle systems - sschirm: deformables and particles are gone... + void updatePvdProperties(const Scb::Actor* actor); + void releasePvdInstance (const Scb::Actor* actor); // temporary for deformables and particle systems - sschirm: deformables and particles are gone... + + void createPvdInstance (const Scb::Body* body); + void updatePvdProperties (const Scb::Body* body); + void updateKinematicTarget (const Scb::Body* body, const PxTransform& p); + + void createPvdInstance (const Scb::RigidStatic* rigidStatic); + void updatePvdProperties (const Scb::RigidStatic* rigidStatic); + + void releasePvdInstance (const Scb::RigidObject* rigidObject); + + void createPvdInstance (const Scb::Constraint* constraint); + void updatePvdProperties(const Scb::Constraint* constraint); + void releasePvdInstance (const Scb::Constraint* constraint); + + void createPvdInstance (const Scb::Articulation* articulation); + void updatePvdProperties(const Scb::Articulation* articulation); + void releasePvdInstance (const Scb::Articulation* articulation); + + void createPvdInstance (const Scb::ArticulationJoint* articulationJoint); + void updatePvdProperties(const Scb::ArticulationJoint* articulationJoint); + void releasePvdInstance (const Scb::ArticulationJoint* articulationJoint); + + void createPvdInstance (const Sc::MaterialCore* materialCore); + void updatePvdProperties(const Sc::MaterialCore* materialCore); + void releasePvdInstance (const Sc::MaterialCore* materialCore); + + void createPvdInstance (const Scb::Shape* shape, PxActor& owner); + void updateMaterials (const Scb::Shape* shape); + void updatePvdProperties (const Scb::Shape* shape); + void releaseAndRecreateGeometry (const Scb::Shape* shape); + void releasePvdInstance (const Scb::Shape* shape, PxActor& owner); + void addBodyAndShapesToPvd (Scb::Body& b); + void addStaticAndShapesToPvd (Scb::RigidStatic& s); + + void createPvdInstance (const Scb::Aggregate* aggregate); + void updatePvdProperties (const Scb::Aggregate* aggregate); + void attachAggregateActor (const Scb::Aggregate* aggregate, Scb::Actor* actor); + void detachAggregateActor (const Scb::Aggregate* aggregate, Scb::Actor* actor); + void releasePvdInstance (const Scb::Aggregate* aggregate); + + void originShift(PxVec3 shift); + void updateJoints(); + void updateContacts(); + void updateSceneQueries(); + + // PvdVisualizer + void visualize(PxArticulationLink& link); + void visualize(const PxRenderBuffer& debugRenderable); + + private: + + void sendEntireScene(); + void updateConstraint(const Sc::ConstraintCore& scConstraint, PxU32 updateType); + void setCreateContactReports(bool b); + + PxPvdSceneFlags mFlags; + PsPvd* mPvd; + Scb::Scene& mScbScene; + + PvdDataStream* mPvdDataStream; + PvdMetaDataBinding mMetaDataBinding; + PvdUserRenderer* mUserRender; + RendererEventClient* mRenderClient; + bool mIsConnected; +}; + +} // pvd + +} // physx +#endif // PX_SUPPORT_PVD + +#endif // SCB_SCENE_PVD_CLIENT_H diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp new file mode 100644 index 000000000..ccae62a06 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp @@ -0,0 +1,144 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScbShape.h" + +using namespace physx; + +bool Scb::Shape::setMaterialsHelper(PxMaterial* const* materials, PxU16 materialCount) +{ + PX_ASSERT(!isBuffering()); + + if(materialCount == 1) + { + const PxU16 materialIndex = Ps::to16((static_cast(materials[0]))->getHandle()); + + mShape.setMaterialIndices(&materialIndex, 1); + } + else + { + PX_ASSERT(materialCount > 1); + + PX_ALLOCA(materialIndices, PxU16, materialCount); + + if(materialIndices) + { + NpMaterial::getMaterialIndices(materials, materialIndices, materialCount); + mShape.setMaterialIndices(materialIndices, materialCount); + } + else + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, + "PxShape::setMaterials() failed. Out of memory. Call will be ignored."); + return false; + } + } + + Scb::Scene* sc = getScbScene(); + if(sc) + sc->getScScene().notifyNphaseOnUpdateShapeMaterial(mShape); + + return true; +} + +void Scb::Shape::syncState() +{ + const PxU32 flags = getBufferFlags(); + if(flags) + { + const PxShapeFlags oldShapeFlags = mShape.getFlags(); + + const Scb::ShapeBuffer& buffer = *getBufferedData(); + + Scb::Scene* scbScene = getScbScene(); // PT: can be NULL. See e.g. RbShapeTest.ReleaseShapeWithPendingUpdate UT. + + if(flags & Buf::BF_Geometry) + { + if(scbScene) + scbScene->getScScene().unregisterShapeFromNphase(mShape); + + mShape.setGeometry(buffer.geometry.getGeometry()); + + if(scbScene) + scbScene->getScScene().registerShapeInNphase(mShape); + +#if PX_SUPPORT_PVD + if(getControlState() == ControlState::eIN_SCENE) + { + PX_ASSERT(scbScene); + scbScene->getScenePvdClient().releaseAndRecreateGeometry(this); + } +#endif + } + + if(flags & Buf::BF_Material) + { + // PT: not sure if this is correct. Added the check for PX-800 but "getMaterialBuffer" doesn't always need the scene pointer... + if(scbScene) + { + const PxU16* materialIndices = getMaterialBuffer(*scbScene, buffer); + mShape.setMaterialIndices(materialIndices, buffer.materialCount); + scbScene->getScScene().notifyNphaseOnUpdateShapeMaterial(mShape); + } + UPDATE_PVD_MATERIALS() + // TODO: So far we did not bother to fail gracefully in the case of running out of memory. If that should change then this + // method is somewhat problematic. The material ref counters have been adjusted at the time when the public API was called. + // Could be that one of the old materials was deleted afterwards. The problem now is what to do if this method fails? + // We can't adjust the material ref counts any longer since some of the old materials might have been deleted. + // One solution could be that this class allocates an array of material pointers when the buffered method is called. + // This array is then passed into the core object and is used by the core object, i.e., the core object does not allocate the + // buffer itself. + } + +#ifdef USE_NEW_SYSTEM + syncShape2Actor(); + syncSimulationFilterData(); + syncContactOffset(); + syncRestOffset(); + syncFlags(); + syncTorsionalPatchRadius(); + syncMinTorsionalPatchRadius(); +#else + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); + flush(buffer); +#endif + + Sc::RigidCore* scRigidCore = NpShapeGetScRigidObjectFromScbSLOW(*this); + + if(scRigidCore) // may be NULL for exclusive shapes because of pending shape updates after buffered release of actor. + scRigidCore->onShapeChange(mShape, Sc::ShapeChangeNotifyFlags(flags), oldShapeFlags, true); + } + + postSyncState(); +} diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbShape.h b/src/PhysX/physx/source/physx/src/buffering/ScbShape.h new file mode 100644 index 000000000..1652c477e --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbShape.h @@ -0,0 +1,481 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_SHAPE +#define PX_PHYSICS_SCB_SHAPE + +#include "NpMaterial.h" +#include "NpPhysics.h" +#include "ScbNpDeps.h" +#include "ScShapeCore.h" +#include "ScRigidCore.h" + +#include "PsUtilities.h" + +// PX_SERIALIZATION +#include "PxSerialFramework.h" +//~PX_SERIALIZATION + +#include "ScbDefs.h" + +namespace physx +{ +#if PX_SUPPORT_PVD + #define UPDATE_PVD_MATERIALS() \ + if(getControlState() == ControlState::eIN_SCENE) \ + getScbScene()->getScenePvdClient().updateMaterials(this); +#else + #define UPDATE_PVD_MATERIALS() {} +#endif + +namespace Scb +{ + +class RigidObject; + +struct ShapeBuffer +{ +#ifndef USE_NEW_SYSTEM + template struct Fns {}; // TODO: make the base class traits visible + typedef Sc::ShapeCore Core; + typedef ShapeBuffer Buf; +#endif + + ShapeBuffer() : materialBufferIndex(0), materialCount(0) {} + +#ifdef USE_NEW_SYSTEM + PX_ALIGN(16, PxTransform) mShape2Actor; + PxFilterData mSimulationFilterData; + PxReal mContactOffset; + PxReal mRestOffset; + PxShapeFlags mFlags; + PxReal mTorsionalPatchRadius; + PxReal mMinTorsionalPatchRadius; +#else + // PT: I think we start with "2" (instead of 0) because the two first bits are reserved + // below, for geometry & materials. + SCB_REGULAR_ATTRIBUTE_ALIGNED(2, PxTransform, Shape2Actor, 16) +// SCB_REGULAR_ATTRIBUTE(2, PxTransform, Shape2Actor) + SCB_REGULAR_ATTRIBUTE(3, PxFilterData, SimulationFilterData) + SCB_REGULAR_ATTRIBUTE(4, PxReal, ContactOffset) + SCB_REGULAR_ATTRIBUTE(5, PxReal, RestOffset) + SCB_REGULAR_ATTRIBUTE(6, PxShapeFlags, Flags) + SCB_REGULAR_ATTRIBUTE(7, PxReal, TorsionalPatchRadius) + SCB_REGULAR_ATTRIBUTE(8, PxReal, MinTorsionalPatchRadius) +#endif + Gu::GeometryUnion geometry; + + union + { + PxU16 materialIndex; // for single material shapes + PxU32 materialBufferIndex; // for multi material shapes + }; + PxU16 materialCount; + + enum + { + BF_Geometry = 1<<0, + BF_Material = 1<<1 + }; +}; + +class Shape : public Base +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== + + typedef Sc::ShapeCore Core; + typedef ShapeBuffer Buf; +public: +// PX_SERIALIZATION + Shape(const PxEMPTY) : Base(PxEmpty), mShape(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PX_INLINE Shape(const PxGeometry& geometry, + PxShapeFlags shapeFlags, + const PxU16* materialIndices, + PxU16 materialCount, + bool isExclusive); + + PX_INLINE PxGeometryType::Enum getGeometryType() const; + + PX_INLINE const PxGeometry& getGeometry() const; + PX_INLINE const Gu::GeometryUnion&getGeometryUnion() const; + PX_INLINE Scb::ShapeBuffer* setGeometry(const PxGeometry& geom); + + PX_INLINE PxU16 getNbMaterials() const; + PX_INLINE PxMaterial* getMaterial(PxU32 index) const; + PX_INLINE PxU32 getMaterials(PxMaterial** buffer, PxU32 bufferSize, PxU32 startIndex=0) const; + PX_INLINE bool setMaterials(PxMaterial*const* materials, PxU16 materialCount); + +#ifdef USE_NEW_SYSTEM + template + void setValueT(ValueType value, CoreType& core, SCBType& scb) + { + if(!scb.isBuffering()) + { + const PxShapeFlags oldShapeFlags = core.getFlags(); + AccessType::setCore(core, value); + + // shared shapes return NULL. But shared shapes aren't mutable when attached to an actor, so no notification needed. + Sc::RigidCore* rigidCore = NpShapeGetScRigidObjectFromScbSLOW(scb); + if(rigidCore && scb.getControlState() != ControlState::eINSERT_PENDING) + rigidCore->onShapeChange(core, Sc::ShapeChangeNotifyFlags(1<getScenePvdClient().updatePvdProperties(&scb); +#endif + } + else + { + AccessType::setBuffered(scb, value); + scb.markUpdated(1<(); } + PX_INLINE void setShape2Actor(const PxTransform& v) { write(v); } + + PX_INLINE PxFilterData getSimulationFilterData() const { return read(); } + PX_INLINE void setSimulationFilterData(const PxFilterData& v) { write(v); } + + PX_INLINE PxReal getContactOffset() const { return read(); } + PX_INLINE void setContactOffset(PxReal v) { write(v); } + + PX_INLINE PxReal getRestOffset() const { return read(); } + PX_INLINE void setRestOffset(PxReal v) { write(v); } + + PX_INLINE PxReal getTorsionalPatchRadius() const { return read(); } + PX_INLINE void setTorsionalPatchRadius(PxReal v) { write(v); } + + PX_INLINE PxReal getMinTorsionalPatchRadius() const { return read(); } + PX_INLINE void setMinTorsionalPatchRadius(PxReal v) { write(v); } + + PX_INLINE PxShapeFlags getFlags() const { return read(); } + PX_INLINE void setFlags(PxShapeFlags v) { write(v); } +#endif + + //--------------------------------------------------------------------------------- + // Data synchronization + //--------------------------------------------------------------------------------- + void syncState(); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE const PxU16* getScMaterialIndices() const { return mShape.getMaterialIndices(); } // Only use if you know what you're doing! + + PX_FORCE_INLINE Sc::ShapeCore& getScShape() { return mShape; } // Only use if you know what you're doing! + PX_FORCE_INLINE const Sc::ShapeCore& getScShape() const { return mShape; } + + PX_FORCE_INLINE bool isExclusive() const { return getScbType() == ScbType::eSHAPE_EXCLUSIVE; } + PX_FORCE_INLINE void setControlStateIfExclusive(Scene* s, ControlState::Enum cs); // for exclusive shapes + + template PX_FORCE_INLINE void checkUpdateOnRemove(Scene* s); + + static size_t getScOffset() { return reinterpret_cast(&reinterpret_cast(0)->mShape); } + +private: + bool setMaterialsHelper(PxMaterial* const* materials, PxU16 materialCount); + + Sc::ShapeCore mShape; + + PX_FORCE_INLINE const Scb::ShapeBuffer* getBufferedData() const { return reinterpret_cast(getStream()); } + PX_FORCE_INLINE Scb::ShapeBuffer* getBufferedData() { return reinterpret_cast(getStream()); } + + PX_FORCE_INLINE const PxU16* getMaterialBuffer(const Scb::Scene& scene, const Scb::ShapeBuffer& sb) const + { + if(sb.materialCount == 1) + return &sb.materialIndex; + else + return scene.getShapeMaterialBuffer(sb.materialBufferIndex); + } + +#ifndef USE_NEW_SYSTEM + //--------------------------------------------------------------------------------- + // Infrastructure for regular attributes + //--------------------------------------------------------------------------------- + + struct Access: public BufferedAccess + { + template + static PX_FORCE_INLINE void write(Shape& base, Core& core, typename Fns::Arg v) + { + if(!base.isBuffering()) + { + PxShapeFlags oldShapeFlags = core.getFlags(); + Fns::setCore(core, v); + + // shared shapes return NULL. But shared shapes aren't mutable when attached to an actor, so no notification needed. + Sc::RigidCore* rigidCore = NpShapeGetScRigidObjectFromScbSLOW(base); + if(rigidCore && base.getControlState() != ControlState::eINSERT_PENDING) + rigidCore->onShapeChange(core, Sc::ShapeChangeNotifyFlags(Fns::flag), oldShapeFlags); +#if PX_SUPPORT_PVD + Scb::Scene* scene = base.getScbSceneForAPI(); // shared shapes also return zero here + if(scene && !base.insertPending()) + scene->getScenePvdClient().updatePvdProperties(&base); +#endif + } + else + { + Fns::setBuffered(*reinterpret_cast(base.getStream()), v); + base.markUpdated(Fns::flag); + } + } + }; + + template PX_FORCE_INLINE typename Buf::Fns::Arg read() const { return Access::read >(*this, mShape); } + template PX_FORCE_INLINE void write(typename Buf::Fns::Arg v) { Access::write >(*this, mShape, v); } + template PX_FORCE_INLINE void flush(const Buf& buf) { Access::flush >(*this, mShape, buf); } +#endif +}; + +PX_INLINE Shape::Shape(const PxGeometry& geometry, + PxShapeFlags shapeFlags, + const PxU16* materialIndices, + PxU16 materialCount, + bool isExclusive) : + mShape(geometry, shapeFlags, materialIndices, materialCount) +{ + // paranoia: the notify flags in Sc have to match up +#ifdef USE_NEW_SYSTEM + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Geometry) == PxU32(Sc::ShapeChangeNotifyFlag::eGEOMETRY)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Material) == PxU32(Sc::ShapeChangeNotifyFlag::eMATERIAL)); + PX_COMPILE_TIME_ASSERT(PxU32(BF_Shape2Actor) == PxU32(Sc::ShapeChangeNotifyFlag::eSHAPE2BODY)); + PX_COMPILE_TIME_ASSERT(PxU32(BF_SimulationFilterData) == PxU32(Sc::ShapeChangeNotifyFlag::eFILTERDATA)); + PX_COMPILE_TIME_ASSERT(PxU32(BF_ContactOffset) == PxU32(Sc::ShapeChangeNotifyFlag::eCONTACTOFFSET)); + PX_COMPILE_TIME_ASSERT(PxU32(BF_RestOffset) == PxU32(Sc::ShapeChangeNotifyFlag::eRESTOFFSET)); + PX_COMPILE_TIME_ASSERT(PxU32(BF_Flags) == PxU32(Sc::ShapeChangeNotifyFlag::eFLAGS)); +#else + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Geometry) == PxU32(Sc::ShapeChangeNotifyFlag::eGEOMETRY)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Material) == PxU32(Sc::ShapeChangeNotifyFlag::eMATERIAL)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Shape2Actor) == PxU32(Sc::ShapeChangeNotifyFlag::eSHAPE2BODY)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_SimulationFilterData) == PxU32(Sc::ShapeChangeNotifyFlag::eFILTERDATA)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_ContactOffset) == PxU32(Sc::ShapeChangeNotifyFlag::eCONTACTOFFSET)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_RestOffset) == PxU32(Sc::ShapeChangeNotifyFlag::eRESTOFFSET)); + PX_COMPILE_TIME_ASSERT(PxU32(ShapeBuffer::BF_Flags) == PxU32(Sc::ShapeChangeNotifyFlag::eFLAGS)); +#endif + + if(isExclusive) + setScbType(ScbType::eSHAPE_EXCLUSIVE); + else + setScbType(ScbType::eSHAPE_SHARED); +} + +PX_INLINE PxGeometryType::Enum Shape::getGeometryType() const +{ + return mShape.getGeometryType(); +} + +PX_INLINE const PxGeometry& Shape::getGeometry() const +{ + if(isBuffered(Buf::BF_Geometry)) + return getBufferedData()->geometry.getGeometry(); + else + return mShape.getGeometry(); +} + +PX_INLINE const Gu::GeometryUnion& Shape::getGeometryUnion() const +{ + if(isBuffered(Buf::BF_Geometry)) + return getBufferedData()->geometry; + else + return mShape.getGeometryUnion(); +} + +PX_INLINE Scb::ShapeBuffer* Shape::setGeometry(const PxGeometry& geom) +{ + Scb::ShapeBuffer* shapeBuffer = NULL; + if (!isBuffering()) + { + Scb::Scene* sc = getScbScene(); + + if(sc) + sc->getScScene().unregisterShapeFromNphase(mShape); + + mShape.setGeometry(geom); + + if(sc) + sc->getScScene().registerShapeInNphase(mShape); + + Sc::RigidCore* rigidCore = NpShapeGetScRigidObjectFromScbSLOW(*this); + if(rigidCore) + rigidCore->onShapeChange(mShape, Sc::ShapeChangeNotifyFlag::eGEOMETRY, PxShapeFlags()); + +#if PX_SUPPORT_PVD + Scb::Scene* scbScene = getScbSceneForAPI(); + if(scbScene) + scbScene->getScenePvdClient().releaseAndRecreateGeometry(this); +#endif + } + else + { + markUpdated(Buf::BF_Geometry); + shapeBuffer = getBufferedData(); + shapeBuffer->geometry.set(geom); + } + + return shapeBuffer; +} + +PX_INLINE PxU16 Shape::getNbMaterials() const +{ + if(isBuffered(Buf::BF_Material)) + return getBufferedData()->materialCount; + else + return mShape.getNbMaterialIndices(); +} + +PX_INLINE PxMaterial* Shape::getMaterial(PxU32 index) const +{ + PX_ASSERT(index < getNbMaterials()); + + NpMaterialManager& matManager = NpPhysics::getInstance().getMaterialManager(); + if(isBuffered(Buf::BF_Material)) + { + const PxU16* materialIndices = getMaterialBuffer(*getScbScene(), *getBufferedData()); + return matManager.getMaterial(materialIndices[index]); + } + else + { + PxU16 matTableIndex = mShape.getMaterialIndices()[index]; + return matManager.getMaterial(matTableIndex); + } +} + +PX_INLINE PxU32 Shape::getMaterials(PxMaterial** buffer, PxU32 bufferSize, PxU32 startIndex) const +{ + const PxU16* materialIndices; + PxU32 matCount; + NpMaterialManager& matManager = NpPhysics::getInstance().getMaterialManager(); + if(isBuffered(Buf::BF_Material)) + { + // IMPORTANT: + // As long as the material pointers get copied to a user buffer, this works fine. + // Never give direct access to the internal material buffer because in the + // double buffered case the pointer changes on resize. + + const Scb::ShapeBuffer* PX_RESTRICT bufferedData = getBufferedData(); + + materialIndices = getMaterialBuffer(*getScbScene(), *bufferedData); + matCount = bufferedData->materialCount; + } + else + { + materialIndices = mShape.getMaterialIndices(); + matCount = mShape.getNbMaterialIndices(); + } + + // PT: this is copied from Cm::getArrayOfPointers(). We cannot use the Cm function here + // because of the extra indirection needed to access the materials. + PxU32 size = matCount; + const PxU32 remainder = PxU32(PxMax(PxI32(size - startIndex), 0)); + const PxU32 writeCount = PxMin(remainder, bufferSize); + materialIndices += startIndex; + for(PxU32 i=0;imaterialIndex; + else + { + PxU32 bufferIdx; + materialIndices = getScbScene()->allocShapeMaterialBuffer(materialCount, bufferIdx); + bufferedData->materialBufferIndex = bufferIdx; + } + bufferedData->materialCount = materialCount; + + NpMaterial::getMaterialIndices(materials, materialIndices, materialCount); + + markUpdated(Buf::BF_Material); + + return true; + } +} + +PX_FORCE_INLINE void Shape::setControlStateIfExclusive(Scene* s, ControlState::Enum cs) +{ + if(isExclusive()) + { + setControlState(cs); + setScbScene(s); + } +} + +template +PX_FORCE_INLINE void Shape::checkUpdateOnRemove(Scene* s) +{ + // special code to cover the case where a shape has a pending update and gets released. The following operations have to be done + // before the ref-counter of the shape gets decremented because that could cause the shape to be deleted in which case it must not + // be in the pending update list any longer. + if(getControlFlags() & Scb::ControlFlag::eIS_UPDATED) + { + if(sync) + syncState(); + s->removeShapeFromPendingUpdateList(*this); + + resetControlFlag(ControlFlag::eIS_UPDATED); + } +} + +} // namespace Scb + +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbType.h b/src/PhysX/physx/source/physx/src/buffering/ScbType.h new file mode 100644 index 000000000..f1108ae61 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/buffering/ScbType.h @@ -0,0 +1,54 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCB_TYPE +#define PX_PHYSICS_SCB_TYPE + +namespace physx +{ + struct ScbType + { + enum Enum + { + eUNDEFINED, + eSHAPE_EXCLUSIVE, + eSHAPE_SHARED, + eBODY, + eBODY_FROM_ARTICULATION_LINK, + eRIGID_STATIC, + eCONSTRAINT, + eARTICULATION, + eARTICULATION_JOINT, + eAGGREGATE, + eTYPE_COUNT + }; + }; +} + +#endif diff --git a/src/PhysX/physx/source/physx/src/device/PhysXIndicator.h b/src/PhysX/physx/source/physx/src/device/PhysXIndicator.h new file mode 100644 index 000000000..6bb76f239 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/device/PhysXIndicator.h @@ -0,0 +1,56 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef __PHYSXINDICATOR_H__ +#define __PHYSXINDICATOR_H__ + +#include "foundation/PxPreprocessor.h" + +namespace physx +{ + struct NvPhysXToDrv_Data_V1_; + + class PhysXIndicator + { + public: + PhysXIndicator(bool isGpu = false); + ~PhysXIndicator(); + + void setIsGpu(bool isGpu); + + private: + void updateCounter(int delta); + + NvPhysXToDrv_Data_V1_* mPhysXDataPtr; + void* mFileHandle; + bool mIsGpu; + }; +} + +#endif // __PHYSXINDICATOR_H__ diff --git a/src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp b/src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp new file mode 100644 index 000000000..c21905982 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp @@ -0,0 +1,51 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PhysXIndicator.h" +#include "nvPhysXtoDrv.h" + +physx::PhysXIndicator::PhysXIndicator(bool isGpu) +: mPhysXDataPtr(0), mFileHandle(0), mIsGpu(isGpu) +{ + +} + +physx::PhysXIndicator::~PhysXIndicator() +{ +} + +void physx::PhysXIndicator::setIsGpu(bool isGpu) +{ + PX_UNUSED(isGpu); +} + +PX_INLINE void physx::PhysXIndicator::updateCounter(int delta) +{ + PX_UNUSED(delta); +} diff --git a/src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h b/src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h new file mode 100644 index 000000000..cbffc8292 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef __NVPHYSXTODRV_H__ +#define __NVPHYSXTODRV_H__ + +// The puprose of this interface is to provide graphics drivers with information +// about PhysX state to draw PhysX visual indicator + +// We share information between modules using a memory section object. PhysX creates +// such object, graphics drivers try to open it. The name of the object has +// fixed part (NvPhysXToDrv_SectionName) followed by the process id. This allows +// each process to have its own communication channel. + +namespace physx +{ + +#define NvPhysXToDrv_SectionName "PH71828182845_" + +// Vista apps cannot create stuff in Global\\ namespace when NOT elevated, so use local scope +#define NvPhysXToDrv_Build_SectionName(PID, buf) sprintf(buf, NvPhysXToDrv_SectionName "%x", static_cast(PID)) +#define NvPhysXToDrv_Build_SectionNameXP(PID, buf) sprintf(buf, "Global\\" NvPhysXToDrv_SectionName "%x", static_cast(PID)) + +typedef struct NvPhysXToDrv_Header_ +{ + int signature; // header interface signature + int version; // version of the interface + int size; // size of the structure + int reserved; // reserved, must be zero +} NvPhysXToDrv_Header; + +// this structure describes layout of data in the shared memory section +typedef struct NvPhysXToDrv_Data_V1_ +{ + NvPhysXToDrv_Header header; // keep this member first in all versions of the interface. + + int bCpuPhysicsPresent; // nonzero if cpu physics is initialized + int bGpuPhysicsPresent; // nonzero if gpu physics is initialized + +} NvPhysXToDrv_Data_V1; + +// some random magic number as our interface signature +#define NvPhysXToDrv_Header_Signature 0xA7AB + +// use the macro to setup the header to the latest version of the interface +// update the macro when a new verson of the interface is added +#define NvPhysXToDrv_Header_Init(header) \ +{ \ + header.signature = NvPhysXToDrv_Header_Signature; \ + header.version = 1; \ + header.size = sizeof(NvPhysXToDrv_Data_V1); \ + header.reserved = 0; \ +} + +// validate the header against all known interface versions +// add validation checks when new interfaces are added +#define NvPhysXToDrv_Header_Validate(header, curVersion) \ + ( \ + (header.signature == NvPhysXToDrv_Header_Signature) && \ + (header.version == curVersion) && \ + (curVersion == 1) && \ + (header.size == sizeof(NvPhysXToDrv_Data_V1)) \ + ) + +} + +#endif // __NVPHYSXTODRV_H__ diff --git a/src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp b/src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp new file mode 100644 index 000000000..6357e936f --- /dev/null +++ b/src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp @@ -0,0 +1,131 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PhysXIndicator.h" +#include "nvPhysXtoDrv.h" + +#pragma warning (push) +#pragma warning (disable : 4668) //'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' +#include +#pragma warning (pop) + +#include + +#if _MSC_VER >= 1800 +#include +#endif + +// Scope-based to indicate to NV driver that CPU PhysX is happening +physx::PhysXIndicator::PhysXIndicator(bool isGpu) +: mPhysXDataPtr(0), mFileHandle(0), mIsGpu(isGpu) +{ + // Get the windows version (we can only create Global\\ namespace objects in XP) + /** + Operating system Version number + ---------------- -------------- + Windows 7 6.1 + Windows Server 2008 R2 6.1 + Windows Server 2008 6.0 + Windows Vista 6.0 + Windows Server 2003 R2 5.2 + Windows Server 2003 5.2 + Windows XP 5.1 + Windows 2000 5.0 + **/ + + char configName[128]; + +#if _MSC_VER >= 1800 + if (!IsWindowsVistaOrGreater()) +#else + OSVERSIONINFOEX windowsVersionInfo; + windowsVersionInfo.dwOSVersionInfoSize = sizeof (windowsVersionInfo); + GetVersionEx((LPOSVERSIONINFO)&windowsVersionInfo); + + if (windowsVersionInfo.dwMajorVersion < 6) +#endif + NvPhysXToDrv_Build_SectionNameXP(GetCurrentProcessId(), configName); + else + NvPhysXToDrv_Build_SectionName(GetCurrentProcessId(), configName); + + mFileHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE, 0, sizeof(NvPhysXToDrv_Data_V1), configName); + + if (!mFileHandle || mFileHandle == INVALID_HANDLE_VALUE) + return; + + bool alreadyExists = ERROR_ALREADY_EXISTS == GetLastError(); + + mPhysXDataPtr = (physx::NvPhysXToDrv_Data_V1*)MapViewOfFile(mFileHandle, + FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(NvPhysXToDrv_Data_V1)); + + if(!mPhysXDataPtr) + return; + + if (!alreadyExists) + { + mPhysXDataPtr->bCpuPhysicsPresent = 0; + mPhysXDataPtr->bGpuPhysicsPresent = 0; + } + + updateCounter(1); + + // init header last to prevent race conditions + // this must be done because the driver may have already created the shared memory block, + // thus alreadyExists may be true, even if PhysX hasn't been initialized + NvPhysXToDrv_Header_Init(mPhysXDataPtr->header); +} + +physx::PhysXIndicator::~PhysXIndicator() +{ + if(mPhysXDataPtr) + { + updateCounter(-1); + UnmapViewOfFile(mPhysXDataPtr); + } + + if(mFileHandle && mFileHandle != INVALID_HANDLE_VALUE) + CloseHandle(mFileHandle); +} + +void physx::PhysXIndicator::setIsGpu(bool isGpu) +{ + if(!mPhysXDataPtr) + return; + + updateCounter(-1); + mIsGpu = isGpu; + updateCounter(1); +} + +PX_INLINE void physx::PhysXIndicator::updateCounter(int delta) +{ + (mIsGpu ? mPhysXDataPtr->bGpuPhysicsPresent + : mPhysXDataPtr->bCpuPhysicsPresent) += delta; +} diff --git a/src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp b/src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp new file mode 100644 index 000000000..6f8e5f3a5 --- /dev/null +++ b/src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxPhysXConfig.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "PxGpu.h" + +#ifndef PX_PHYSX_GPU_STATIC +namespace grid +{ + class Server; + class ClientContextPredictionManager; +} + +namespace physx +{ + //forward declare stuff from PxPhysXGpuModuleLoader.cpp + void PxLoadPhysxGPUModule(const char* appGUID); + typedef physx::PxCudaContextManager* (PxCreateCudaContextManager_FUNC)(physx::PxFoundation& foundation, const physx::PxCudaContextManagerDesc& desc); + typedef int (PxGetSuggestedCudaDeviceOrdinal_FUNC)(physx::PxErrorCallback& errc); + typedef grid::ClientContextPredictionManager* (PxCreateClientContextManager_FUNC)(grid::Server* server, physx::PxU32 maxNbSleepMsg); + extern PxCreateCudaContextManager_FUNC* g_PxCreateCudaContextManager_Func; + extern PxGetSuggestedCudaDeviceOrdinal_FUNC* g_PxGetSuggestedCudaDeviceOrdinal_Func; + extern PxCreateClientContextManager_FUNC* g_CreateClientContextManager_Func; + +} // end physx namespace + + + +physx::PxCudaContextManager* PxCreateCudaContextManager(physx::PxFoundation& foundation, const physx::PxCudaContextManagerDesc& desc) +{ + if (!physx::g_PxCreateCudaContextManager_Func) + physx::PxLoadPhysxGPUModule(desc.appGUID); + + if (physx::g_PxCreateCudaContextManager_Func) + return physx::g_PxCreateCudaContextManager_Func(foundation, desc); + else + return NULL; +} + +int PxGetSuggestedCudaDeviceOrdinal(physx::PxErrorCallback& errc) +{ + if (!physx::g_PxGetSuggestedCudaDeviceOrdinal_Func) + physx::PxLoadPhysxGPUModule(NULL); + + if (physx::g_PxGetSuggestedCudaDeviceOrdinal_Func) + return physx::g_PxGetSuggestedCudaDeviceOrdinal_Func(errc); + else + return -1; +} + +PX_C_EXPORT PX_PHYSX_CORE_API grid::ClientContextPredictionManager* PX_CALL_CONV PxCreateCudaClientContextManager(grid::Server* server, physx::PxU32 maxNbSleepMsg); + +grid::ClientContextPredictionManager* PxCreateCudaClientContextManager(grid::Server* server, physx::PxU32 maxNbSleepMsg) +{ + if (!physx::g_CreateClientContextManager_Func) + physx::PxLoadPhysxGPUModule(NULL); + + if (physx::g_CreateClientContextManager_Func) + return physx::g_CreateClientContextManager_Func(server, maxNbSleepMsg); + else + return NULL; +} +#endif + +#endif // PX_SUPPORT_GPU_PHYSX + diff --git a/src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp b/src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp new file mode 100644 index 000000000..288d812da --- /dev/null +++ b/src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp @@ -0,0 +1,194 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxPhysXConfig.h" + +#if PX_SUPPORT_GPU_PHYSX + +#include "foundation/Px.h" +#include "PsFoundation.h" +#include "PxPhysics.h" +#include "PxGpu.h" + +#include "cudamanager/PxCudaContextManager.h" + +#if PX_WINDOWS +#include "windows/PsWindowsInclude.h" +#include "windows/PxWindowsDelayLoadHook.h" +#include "windows/CmWindowsModuleUpdateLoader.h" +#elif PX_LINUX +#include +#endif // ~PX_LINUX + +namespace physx +{ + // alias shared foundation to something usable + namespace Ps = shdfnd; +} + +#define STRINGIFY(x) #x +#define GETSTRING(x) STRINGIFY(x) + +#define PHYSX_GPU_SHARED_LIB_NAME GETSTRING(PX_PHYSX_GPU_SHARED_LIB_NAME) +static const char* gPhysXGpuLibraryName = PHYSX_GPU_SHARED_LIB_NAME; + +#undef GETSTRING +#undef STRINGIFY + +#if PX_WINDOWS +namespace physx +{ + const PxDelayLoadHook* PxGetPhysXDelayLoadHook(); +} +#endif + +void PxSetPhysXGpuLoadHook(const PxGpuLoadHook* hook) +{ + gPhysXGpuLibraryName = hook->getPhysXGpuDllName(); +} + +namespace grid +{ + class Server; + class ClientContextPredictionManager; +} + +namespace physx +{ +#if PX_VC +#pragma warning(disable: 4191) //'operator/operation' : unsafe conversion from 'type of expression' to 'type required' +#endif + + class PxFoundation; + class PxPhysXGpu; + + typedef physx::PxPhysXGpu* (PxCreatePhysXGpu_FUNC)(); + typedef physx::PxCudaContextManager* (PxCreateCudaContextManager_FUNC)(physx::PxFoundation& foundation, const physx::PxCudaContextManagerDesc& desc); + typedef int (PxGetSuggestedCudaDeviceOrdinal_FUNC)(physx::PxErrorCallback& errc); + typedef grid::ClientContextPredictionManager* (PxCreateClientContextManager_FUNC)(grid::Server* server, physx::PxU32 maxNbSleepMsg); + + PxCreatePhysXGpu_FUNC* g_PxCreatePhysXGpu_Func = NULL; + PxCreateCudaContextManager_FUNC* g_PxCreateCudaContextManager_Func = NULL; + PxGetSuggestedCudaDeviceOrdinal_FUNC* g_PxGetSuggestedCudaDeviceOrdinal_Func = NULL; + + PxCreateClientContextManager_FUNC* g_CreateClientContextManager_Func = NULL; + +#if PX_WINDOWS + + typedef void (PxSetPhysXGpuDelayLoadHook_FUNC)(const PxDelayLoadHook* delayLoadHook); + PxSetPhysXGpuDelayLoadHook_FUNC* g_PxSetPhysXGpuDelayLoadHook_Func = NULL; + +#define DEFAULT_PHYSX_GPU_GUID "D79FA4BF-177C-4841-8091-4375D311D6A3" + + void PxLoadPhysxGPUModule(const char* appGUID) + { + static HMODULE s_library; + + if (s_library == NULL) + s_library = GetModuleHandle(gPhysXGpuLibraryName); + + if (s_library == NULL) + { + Cm::CmModuleUpdateLoader moduleLoader(UPDATE_LOADER_DLL_NAME); + s_library = moduleLoader.LoadModule(gPhysXGpuLibraryName, appGUID == NULL ? DEFAULT_PHYSX_GPU_GUID : appGUID); + } + + if (s_library) + { + g_PxSetPhysXGpuDelayLoadHook_Func = (PxSetPhysXGpuDelayLoadHook_FUNC*)GetProcAddress(s_library, "PxSetPhysXGpuDelayLoadHook"); + g_PxCreatePhysXGpu_Func = (PxCreatePhysXGpu_FUNC*)GetProcAddress(s_library, "PxCreatePhysXGpu"); + g_PxCreateCudaContextManager_Func = (PxCreateCudaContextManager_FUNC*)GetProcAddress(s_library, "PxCreateCudaContextManager"); + g_PxGetSuggestedCudaDeviceOrdinal_Func = (PxGetSuggestedCudaDeviceOrdinal_FUNC*)GetProcAddress(s_library, "PxGetSuggestedCudaDeviceOrdinal"); + g_CreateClientContextManager_Func = (PxCreateClientContextManager_FUNC*)GetProcAddress(s_library, "PxCreateCudaClientContextManager"); + } + + // Check for errors + if (s_library == NULL) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Failed to load PhysXGpu dll!"); + return; + } + + if (g_PxSetPhysXGpuDelayLoadHook_Func == NULL || g_PxCreatePhysXGpu_Func == NULL || g_PxCreateCudaContextManager_Func == NULL || g_PxGetSuggestedCudaDeviceOrdinal_Func == NULL) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "PhysXGpu dll is incompatible with this version of PhysX!"); + return; + } + + g_PxSetPhysXGpuDelayLoadHook_Func(PxGetPhysXDelayLoadHook()); + + } + +#elif PX_LINUX + + void PxLoadPhysxGPUModule(const char*) + { + static void* s_library; + + if (s_library == NULL) + { + // load libcuda.so here since gcc configured with --as-needed won't link to it + // if there is no call from the binary to it. + void* hLibCuda = dlopen("libcuda.so", RTLD_NOW | RTLD_GLOBAL); + if (hLibCuda) + { + s_library = dlopen(gPhysXGpuLibraryName, RTLD_NOW); + } + else + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "libcuda.so!"); + return; + } + } + + // no UpdateLoader + if (s_library) + { + *reinterpret_cast(&g_PxCreatePhysXGpu_Func) = dlsym(s_library, "PxCreatePhysXGpu"); + *reinterpret_cast(&g_PxCreateCudaContextManager_Func) = dlsym(s_library, "PxCreateCudaContextManager"); + *reinterpret_cast(&g_PxGetSuggestedCudaDeviceOrdinal_Func) = dlsym(s_library, "PxGetSuggestedCudaDeviceOrdinal"); + *reinterpret_cast(&g_CreateClientContextManager_Func ) = dlsym(s_library, "PxCreateCudaClientContextManager"); + } + + // Check for errors + if (s_library == NULL) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Failed to load %s.", gPhysXGpuLibraryName); + return; + } + if (g_PxCreatePhysXGpu_Func == NULL || g_PxCreateCudaContextManager_Func == NULL || g_PxGetSuggestedCudaDeviceOrdinal_Func == NULL) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "%s is incompatible with this version of PhysX!", gPhysXGpuLibraryName); + return; + } + } + +#endif // PX_LINUX + +} // end physx namespace + +#endif // PX_SUPPORT_GPU_PHYSX diff --git a/src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp b/src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp new file mode 100644 index 000000000..203a12cca --- /dev/null +++ b/src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "windows/PxWindowsDelayLoadHook.h" +#include "windows/PsWindowsInclude.h" +#include "windows/CmWindowsLoadLibrary.h" + +// Prior to Visual Studio 2015 Update 3, these hooks were non-const. +#define DELAYIMP_INSECURE_WRITABLE_HOOKS +#include + +namespace physx +{ + static const PxDelayLoadHook* gDelayLoadHook = NULL; + + void PxSetPhysXDelayLoadHook(const PxDelayLoadHook* hook) + { + gDelayLoadHook = hook; + } + + const PxDelayLoadHook* PxGetPhysXDelayLoadHook() + { + return gDelayLoadHook; + } +} + +using namespace physx; + +#pragma comment(lib, "delayimp") + +FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli) +{ + switch (dliNotify) { + case dliStartProcessing : + break; + + case dliNotePreLoadLibrary : + { + return Cm::physXCommonDliNotePreLoadLibrary(pdli->szDll,gDelayLoadHook); + } + break; + + case dliNotePreGetProcAddress : + break; + + case dliFailLoadLib : + break; + + case dliFailGetProc : + break; + + case dliNoteEndProcessing : + break; + + default : + + return NULL; + } + + return NULL; +} + +PfnDliHook __pfnDliNotifyHook2 = delayHook; diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp new file mode 100644 index 000000000..f025526bb --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp @@ -0,0 +1,197 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxController.h" +#include "CctBoxController.h" +#include "CctCharacterControllerManager.h" +#include "PxBoxGeometry.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" + +using namespace physx; +using namespace Cct; + +static PX_FORCE_INLINE PxVec3 CCTtoProxyExtents(PxF32 halfHeight, PxF32 halfSideExtent, PxF32 halfForwardExtent, PxF32 coeff) +{ + // PT: because we now orient the box CCT using the same quat as for capsules... + // i.e. the identity quat corresponds to a up dir = 1,0,0 (which is like the worst choice we could have made, of course) + return PxVec3(halfHeight * coeff, halfSideExtent * coeff, halfForwardExtent * coeff); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +BoxController::BoxController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* s) : Controller(desc, s) +{ + mType = PxControllerShapeType::eBOX; + + const PxBoxControllerDesc& bc = static_cast(desc); + + mHalfHeight = bc.halfHeight; + mHalfSideExtent = bc.halfSideExtent; + mHalfForwardExtent = bc.halfForwardExtent; + + // Create kinematic actor under the hood + PxBoxGeometry boxGeom; + boxGeom.halfExtents = CCTtoProxyExtents(bc.halfHeight, bc.halfSideExtent, bc.halfForwardExtent, mProxyScaleCoeff); + + createProxyActor(sdk, boxGeom, *desc.material); +} + +BoxController::~BoxController() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BoxController::invalidateCache() +{ + if(mManager->mLockingEnabled) + mWriteLock.lock(); + + mCctModule.voidTestCache(); + + if(mManager->mLockingEnabled) + mWriteLock.unlock(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool BoxController::getWorldBox(PxExtendedBounds3& box) const +{ + setCenterExtents(box, mPosition, PxVec3(mHalfHeight, mHalfSideExtent, mHalfForwardExtent)); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxF32 BoxController::getHalfHeight() const +{ + return mHalfHeight; +} + +PxF32 BoxController::getHalfSideExtent() const +{ + return mHalfSideExtent; +} + +PxF32 BoxController::getHalfForwardExtent() const +{ + return mHalfForwardExtent; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool BoxController::updateKinematicProxy() +{ + // Set extents for kinematic proxy + if(mKineActor) + { + PxShape* shape = getKineShape(); + + PX_ASSERT(shape->getGeometryType() == PxGeometryType::eBOX); + PxBoxGeometry bg; + shape->getBoxGeometry(bg); + + bg.halfExtents = CCTtoProxyExtents(mHalfHeight, mHalfSideExtent, mHalfForwardExtent, mProxyScaleCoeff); + shape->setGeometry(bg); + } + return true; +} + +bool BoxController::setHalfHeight(PxF32 halfHeight) +{ + if(halfHeight<=0.0f) + return false; + + mHalfHeight = halfHeight; + return updateKinematicProxy(); +} + +bool BoxController::setHalfSideExtent(PxF32 halfSideExtent) +{ + if(halfSideExtent<=0.0f) + return false; + + mHalfSideExtent = halfSideExtent; + return updateKinematicProxy(); +} + +bool BoxController::setHalfForwardExtent(PxF32 halfForwardExtent) +{ + if(halfForwardExtent<=0.0f) + return false; + + mHalfForwardExtent = halfForwardExtent; + return updateKinematicProxy(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxExtendedVec3 BoxController::getFootPosition() const +{ + PxExtendedVec3 groundPosition = mPosition; // Middle of the CCT + groundPosition -= mUserParams.mUpDirection * (mHalfHeight + mUserParams.mContactOffset); // Ground + return groundPosition; +} + +bool BoxController::setFootPosition(const PxExtendedVec3& position) +{ + PxExtendedVec3 centerPosition = position; + centerPosition += mUserParams.mUpDirection * (mHalfHeight + mUserParams.mContactOffset); + return setPosition(centerPosition); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BoxController::getOBB(PxExtendedBox& obb) const +{ + // PT: TODO: optimize this + PxExtendedBounds3 worldBox; + getWorldBox(worldBox); + + getCenter(worldBox, obb.center); + getExtents(worldBox, obb.extents); + obb.rot = mUserParams.mQuatFromUp; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BoxController::resize(PxReal height) +{ + const float oldHeight = getHalfHeight(); + setHalfHeight(height); + + const float delta = height - oldHeight; + PxExtendedVec3 pos = getPosition(); + pos += mUserParams.mUpDirection * delta; + setPosition(pos); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h new file mode 100644 index 000000000..46f6d238d --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h @@ -0,0 +1,112 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_BOX_CONTROLLER +#define CCT_BOX_CONTROLLER + +/* Exclude from documentation */ +/** \cond */ + +#include "CctController.h" +#include "PxBoxController.h" + +namespace physx +{ + +class PxPhysics; + +namespace Cct +{ + + class BoxController : public PxBoxController, public Controller + { + public: + BoxController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* scene); + virtual ~BoxController(); + + // Controller + virtual PxF32 getHalfHeightInternal() const { return mHalfHeight; } + virtual bool getWorldBox(PxExtendedBounds3& box) const; + virtual PxController* getPxController() { return this; } + //~Controller + + // PxController + virtual PxControllerShapeType::Enum getType() const { return mType; } + virtual void release() { releaseInternal(); } + virtual PxControllerCollisionFlags move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles); + virtual bool setPosition(const PxExtendedVec3& position) { return setPos(position); } + virtual const PxExtendedVec3& getPosition() const { return mPosition; } + virtual bool setFootPosition(const PxExtendedVec3& position); + virtual PxExtendedVec3 getFootPosition() const; + virtual PxRigidDynamic* getActor() const { return mKineActor; } + virtual void setStepOffset(const float offset) { if(offset>0.0f) + mUserParams.mStepOffset = offset; } + virtual PxF32 getStepOffset() const { return mUserParams.mStepOffset; } + virtual void setNonWalkableMode(PxControllerNonWalkableMode::Enum flag) { mUserParams.mNonWalkableMode = flag; } + virtual PxControllerNonWalkableMode::Enum getNonWalkableMode() const { return mUserParams.mNonWalkableMode; } + virtual PxF32 getContactOffset() const { return mUserParams.mContactOffset; } + virtual void setContactOffset(PxF32 offset) { if(offset>0.0f) + mUserParams.mContactOffset = offset;} + virtual PxVec3 getUpDirection() const { return mUserParams.mUpDirection; } + virtual void setUpDirection(const PxVec3& up) { setUpDirectionInternal(up); } + virtual PxF32 getSlopeLimit() const { return mUserParams.mSlopeLimit; } + virtual void setSlopeLimit(PxF32 slopeLimit) { if(slopeLimit>0.0f) + mUserParams.mSlopeLimit = slopeLimit;} + virtual void invalidateCache(); + virtual PxScene* getScene() { return mScene; } + virtual void* getUserData() const { return mUserData; } + virtual void setUserData(void* userData) { mUserData = userData; } + virtual void getState(PxControllerState& state) const { return getInternalState(state); } + virtual void getStats(PxControllerStats& stats) const { return getInternalStats(stats); } + virtual void resize(PxReal height); + //~PxController + + // PxBoxController + virtual PxF32 getHalfHeight() const; + virtual PxF32 getHalfSideExtent() const; + virtual PxF32 getHalfForwardExtent() const; + virtual bool setHalfHeight(PxF32 halfHeight); + virtual bool setHalfSideExtent(PxF32 halfSideExtent); + virtual bool setHalfForwardExtent(PxF32 halfForwardExtent); + //~ PxBoxController + + PxF32 mHalfHeight; + PxF32 mHalfSideExtent; + PxF32 mHalfForwardExtent; + + bool updateKinematicProxy(); + void getOBB(PxExtendedBox& obb) const; + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp new file mode 100644 index 000000000..ea96dfb56 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp @@ -0,0 +1,192 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxController.h" +#include "CctCapsuleController.h" +#include "CctCharacterControllerManager.h" +#include "PxCapsuleGeometry.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" + +using namespace physx; +using namespace Cct; + +static PX_FORCE_INLINE float CCTtoProxyRadius(float r, PxF32 coeff) { return r * coeff; } +static PX_FORCE_INLINE float CCTtoProxyHeight(float h, PxF32 coeff) { return 0.5f * h * coeff; } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +CapsuleController::CapsuleController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* s) : Controller(desc, s) +{ + mType = PxControllerShapeType::eCAPSULE; + + const PxCapsuleControllerDesc& cc = static_cast(desc); + + mRadius = cc.radius; + mHeight = cc.height; + mClimbingMode = cc.climbingMode; + + // Create kinematic actor under the hood + PxCapsuleGeometry capsGeom; + capsGeom.radius = CCTtoProxyRadius(cc.radius, mProxyScaleCoeff); + capsGeom.halfHeight = CCTtoProxyHeight(cc.height, mProxyScaleCoeff); + + createProxyActor(sdk, capsGeom, *desc.material); +} + +CapsuleController::~CapsuleController() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CapsuleController::invalidateCache() +{ + if(mManager->mLockingEnabled) + mWriteLock.lock(); + + mCctModule.voidTestCache(); + + if(mManager->mLockingEnabled) + mWriteLock.unlock(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool CapsuleController::getWorldBox(PxExtendedBounds3& box) const +{ + setCenterExtents(box, mPosition, PxVec3(mRadius, mRadius+mHeight*0.5f, mRadius)); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool CapsuleController::setRadius(PxF32 r) +{ + // Set radius for CCT volume + mRadius = r; + + // Set radius for kinematic proxy + if(mKineActor) + { + PxShape* shape = getKineShape(); + + PX_ASSERT(shape->getGeometryType() == PxGeometryType::eCAPSULE); + PxCapsuleGeometry cg; + shape->getCapsuleGeometry(cg); + + cg.radius = CCTtoProxyRadius(r, mProxyScaleCoeff); + shape->setGeometry(cg); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool CapsuleController::setHeight(PxF32 h) +{ + // Set height for CCT volume + mHeight = h; + + // Set height for kinematic proxy + if(mKineActor) + { + PxShape* shape = getKineShape(); + + PX_ASSERT(shape->getGeometryType() == PxGeometryType::eCAPSULE); + PxCapsuleGeometry cg; + shape->getCapsuleGeometry(cg); + + cg.halfHeight = CCTtoProxyHeight(h, mProxyScaleCoeff); + shape->setGeometry(cg); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxCapsuleClimbingMode::Enum CapsuleController::getClimbingMode() const +{ + return mClimbingMode; +} + +bool CapsuleController::setClimbingMode(PxCapsuleClimbingMode::Enum mode) +{ + if(mode>=PxCapsuleClimbingMode::eLAST) + return false; + mClimbingMode = mode; + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxExtendedVec3 CapsuleController::getFootPosition() const +{ + PxExtendedVec3 groundPosition = mPosition; // Middle of the CCT + groundPosition -= mUserParams.mUpDirection * (mUserParams.mContactOffset+mRadius+mHeight*0.5f); // Ground + return groundPosition; +} + +bool CapsuleController::setFootPosition(const PxExtendedVec3& position) +{ + PxExtendedVec3 centerPosition = position; + centerPosition += mUserParams.mUpDirection * (mUserParams.mContactOffset+mRadius+mHeight*0.5f); + return setPosition(centerPosition); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CapsuleController::getCapsule(PxExtendedCapsule& capsule) const +{ + // PT: TODO: optimize this + PxExtendedVec3 p0 = mPosition; + PxExtendedVec3 p1 = mPosition; + const PxVec3 extents = mUserParams.mUpDirection*mHeight*0.5f; + p0 -= extents; + p1 += extents; + + capsule.p0 = p0; + capsule.p1 = p1; + capsule.radius = mRadius; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CapsuleController::resize(PxReal height) +{ + const float oldHeight = getHeight(); + setHeight(height); + + const float delta = height - oldHeight; + PxExtendedVec3 pos = getPosition(); + pos += mUserParams.mUpDirection * delta * 0.5f; + setPosition(pos); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h new file mode 100644 index 000000000..cefab7c38 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h @@ -0,0 +1,111 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_CAPSULE_CONTROLLER +#define CCT_CAPSULE_CONTROLLER + +/* Exclude from documentation */ +/** \cond */ + +#include "CctController.h" +#include "PxCapsuleController.h" + +namespace physx +{ + +class PxPhysics; + +namespace Cct +{ + + class CapsuleController : public PxCapsuleController, public Controller + { + public: + CapsuleController(const PxControllerDesc& desc, PxPhysics& sdk, PxScene* scene); + virtual ~CapsuleController(); + + // Controller + virtual PxF32 getHalfHeightInternal() const { return mRadius+mHeight*0.5f; } + virtual bool getWorldBox(PxExtendedBounds3& box) const; + virtual PxController* getPxController() { return this; } + //~Controller + + // PxController + virtual PxControllerShapeType::Enum getType() const { return mType; } + virtual void release() { releaseInternal(); } + virtual PxControllerCollisionFlags move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles); + virtual bool setPosition(const PxExtendedVec3& position) { return setPos(position); } + virtual const PxExtendedVec3& getPosition() const { return mPosition; } + virtual bool setFootPosition(const PxExtendedVec3& position); + virtual PxExtendedVec3 getFootPosition() const; + virtual PxRigidDynamic* getActor() const { return mKineActor; } + virtual void setStepOffset(const float offset) { if(offset>0.0f) + mUserParams.mStepOffset = offset; } + virtual PxF32 getStepOffset() const { return mUserParams.mStepOffset; } + virtual void setNonWalkableMode(PxControllerNonWalkableMode::Enum flag) { mUserParams.mNonWalkableMode = flag; } + virtual PxControllerNonWalkableMode::Enum getNonWalkableMode() const { return mUserParams.mNonWalkableMode; } + virtual PxF32 getContactOffset() const { return mUserParams.mContactOffset; } + virtual void setContactOffset(PxF32 offset) { if(offset>0.0f) + mUserParams.mContactOffset = offset;} + virtual PxVec3 getUpDirection() const { return mUserParams.mUpDirection; } + virtual void setUpDirection(const PxVec3& up) { setUpDirectionInternal(up); } + virtual PxF32 getSlopeLimit() const { return mUserParams.mSlopeLimit; } + virtual void setSlopeLimit(PxF32 slopeLimit) { if(slopeLimit>0.0f) + mUserParams.mSlopeLimit = slopeLimit;} + virtual void invalidateCache(); + virtual PxScene* getScene() { return mScene; } + virtual void* getUserData() const { return mUserData; } + virtual void setUserData(void* userData) { mUserData = userData; } + virtual void getState(PxControllerState& state) const { return getInternalState(state); } + virtual void getStats(PxControllerStats& stats) const { return getInternalStats(stats); } + virtual void resize(PxReal height); + //~PxController + + // PxCapsuleController + virtual PxF32 getRadius() const { return mRadius; } + virtual PxF32 getHeight() const { return mHeight; } + virtual PxCapsuleClimbingMode::Enum getClimbingMode() const; + virtual bool setRadius(PxF32 radius); + virtual bool setHeight(PxF32 height); + virtual bool setClimbingMode(PxCapsuleClimbingMode::Enum); + //~ PxCapsuleController + + void getCapsule(PxExtendedCapsule& capsule) const; + + PxF32 mRadius; + PxF32 mHeight; + PxCapsuleClimbingMode::Enum mClimbingMode; + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp new file mode 100644 index 000000000..510ad5c0c --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp @@ -0,0 +1,2552 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "CctCharacterController.h" +#include "CctCharacterControllerManager.h" +#include "CctSweptBox.h" +#include "CctSweptCapsule.h" +#include "CctObstacleContext.h" +#include "PxRigidDynamic.h" +#include "CmRenderOutput.h" +#include "PsMathUtils.h" +#include "GuIntersectionBoxBox.h" +#include "GuDistanceSegmentBox.h" +#include "PxMeshQuery.h" +#include "PsFPU.h" + +// PT: TODO: remove those includes.... shouldn't be allowed from here +#include "PxControllerObstacles.h" // (*) +#include "CctInternalStructs.h" // (*) +#include "PxControllerManager.h" // (*) +#include "PxControllerBehavior.h" // (*) + +//#define DEBUG_MTD +#ifdef DEBUG_MTD + #include +#endif + +#define MAX_ITER 10 + +using namespace physx; +using namespace Cct; +using namespace Gu; +using namespace Cm; + +static const PxU32 gObstacleDebugColor = PxU32(PxDebugColor::eARGB_CYAN); +//static const PxU32 gCCTBoxDebugColor = PxU32(PxDebugColor::eARGB_YELLOW); +static const PxU32 gTBVDebugColor = PxU32(PxDebugColor::eARGB_MAGENTA); +static const bool gUsePartialUpdates = true; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxHitFlags getSweepHitFlags(const CCTParams& params) +{ + PxHitFlags sweepHitFlags = PxHitFlag::eDEFAULT/*|PxHitFlag::eMESH_BOTH_SIDES*/; +// sweepHitFlags |= PxHitFlag::eASSUME_NO_INITIAL_OVERLAP; + if(params.mPreciseSweeps) + sweepHitFlags |= PxHitFlag::ePRECISE_SWEEP; + return sweepHitFlags; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool shouldApplyRecoveryModule(const PxRigidActor& rigidActor) +{ + // PT: we must let the dynamic objects go through the CCT for proper 2-way interactions. + // But we should still apply the recovery module for kinematics. + + const PxType type = rigidActor.getConcreteType(); + if(type==PxConcreteType::eRIGID_STATIC) + return true; + + if(type!=PxConcreteType::eRIGID_DYNAMIC) + return false; + + return static_cast(rigidActor).getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static const bool gUseLocalSpace = true; +static PxVec3 worldToLocal(const PxObstacle& obstacle, const PxExtendedVec3& worldPos) +{ + const PxTransform tr(toVec3(obstacle.mPos), obstacle.mRot); + return tr.transformInv(toVec3(worldPos)); +} + +static PxVec3 localToWorld(const PxObstacle& obstacle, const PxVec3& localPos) +{ + const PxTransform tr(toVec3(obstacle.mPos), obstacle.mRot); + return tr.transform(localPos); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef PX_BIG_WORLDS + typedef PxExtendedBounds3 PxCCTBounds3; + typedef PxExtendedVec3 PxCCTVec3; +#else + typedef PxBounds3 PxCCTBounds3; + typedef PxVec3 PxCCTVec3; +#endif + +static PX_INLINE void scale(PxCCTBounds3& b, const PxVec3& scale) +{ + PxCCTVec3 center; getCenter(b, center); + PxVec3 extents; getExtents(b, extents); + extents.x *= scale.x; + extents.y *= scale.y; + extents.z *= scale.z; + setCenterExtents(b, center, extents); +} + +static PX_INLINE void computeReflexionVector(PxVec3& reflected, const PxVec3& incomingDir, const PxVec3& outwardNormal) +{ + reflected = incomingDir - outwardNormal * 2.0f * (incomingDir.dot(outwardNormal)); +} + +static PX_INLINE void collisionResponse(PxExtendedVec3& targetPosition, const PxExtendedVec3& currentPosition, const PxVec3& currentDir, const PxVec3& hitNormal, PxF32 bump, PxF32 friction, bool normalize=false) +{ + // Compute reflect direction + PxVec3 reflectDir; + computeReflexionVector(reflectDir, currentDir, hitNormal); + reflectDir.normalize(); + + // Decompose it + PxVec3 normalCompo, tangentCompo; + Ps::decomposeVector(normalCompo, tangentCompo, reflectDir, hitNormal); + + // Compute new destination position + const PxF32 amplitude = (targetPosition - currentPosition).magnitude(); + + targetPosition = currentPosition; + if(bump!=0.0f) + { + if(normalize) + normalCompo.normalize(); + targetPosition += normalCompo*bump*amplitude; + } + if(friction!=0.0f) + { + if(normalize) + tangentCompo.normalize(); + targetPosition += tangentCompo*friction*amplitude; + } +} + +static PX_INLINE void relocateBox(PxBoxGeometry& boxGeom, PxTransform& pose, const PxExtendedVec3& center, const PxVec3& extents, const PxExtendedVec3& origin, const PxQuat& quatFromUp) +{ + boxGeom.halfExtents = extents; + + pose.p.x = float(center.x - origin.x); + pose.p.y = float(center.y - origin.y); + pose.p.z = float(center.z - origin.z); + + pose.q = quatFromUp; +} + +static PX_INLINE void relocateBox(PxBoxGeometry& boxGeom, PxTransform& pose, const TouchedUserBox& userBox) +{ + relocateBox(boxGeom, pose, userBox.mBox.center, userBox.mBox.extents, userBox.mOffset, userBox.mBox.rot); +} + +static PX_INLINE void relocateBox(PxBoxGeometry& boxGeom, PxTransform& pose, const TouchedBox& box) +{ + boxGeom.halfExtents = box.mExtents; + + pose.p = box.mCenter; + pose.q = box.mRot; +} + +static PX_INLINE void relocateCapsule( + PxCapsuleGeometry& capsuleGeom, PxTransform& pose, const SweptCapsule* sc, + const PxQuat& quatFromUp, + const PxExtendedVec3& center, const PxExtendedVec3& origin) +{ + capsuleGeom.radius = sc->mRadius; + capsuleGeom.halfHeight = 0.5f * sc->mHeight; + + pose.p.x = float(center.x - origin.x); + pose.p.y = float(center.y - origin.y); + pose.p.z = float(center.z - origin.z); + + pose.q = quatFromUp; +} + +static PX_INLINE void relocateCapsule(PxCapsuleGeometry& capsuleGeom, PxTransform& pose, const PxVec3& p0, const PxVec3& p1, PxReal radius) +{ + capsuleGeom.radius = radius; + pose = PxTransformFromSegment(p0, p1, &capsuleGeom.halfHeight); + if(capsuleGeom.halfHeight==0.0f) + capsuleGeom.halfHeight = FLT_EPSILON; +} + +static PX_INLINE void relocateCapsule(PxCapsuleGeometry& capsuleGeom, PxTransform& pose, const TouchedUserCapsule& userCapsule) +{ + PxVec3 p0, p1; + p0.x = float(userCapsule.mCapsule.p0.x - userCapsule.mOffset.x); + p0.y = float(userCapsule.mCapsule.p0.y - userCapsule.mOffset.y); + p0.z = float(userCapsule.mCapsule.p0.z - userCapsule.mOffset.z); + p1.x = float(userCapsule.mCapsule.p1.x - userCapsule.mOffset.x); + p1.y = float(userCapsule.mCapsule.p1.y - userCapsule.mOffset.y); + p1.z = float(userCapsule.mCapsule.p1.z - userCapsule.mOffset.z); + + relocateCapsule(capsuleGeom, pose, p0, p1, userCapsule.mCapsule.radius); +} + +static bool SweepBoxUserBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_BOX); + const SweptBox* SB = static_cast(volume); + const TouchedUserBox* TC = static_cast(geom); + + PxBoxGeometry boxGeom0; + PxTransform boxPose0; + // To precompute + relocateBox(boxGeom0, boxPose0, center, SB->mExtents, TC->mOffset, test->mUserParams.mQuatFromUp); + + PxBoxGeometry boxGeom1; + PxTransform boxPose1; + relocateBox(boxGeom1, boxPose1, *TC); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom0, boxPose0, boxGeom1, boxPose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mWorldNormal = sweepHit.normal; + impact.mDistance = sweepHit.distance; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TC->mOffset); + return true; +} + +static bool SweepBoxUserCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_CAPSULE); + const SweptBox* SB = static_cast(volume); + const TouchedUserCapsule* TC = static_cast(geom); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, center, SB->mExtents, TC->mOffset, test->mUserParams.mQuatFromUp); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, *TC); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom, boxPose, capsuleGeom, capsulePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + // ### check this + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(Capsule, Box0.center, Box0.extents, Box0.rot, &t, &p); + Box0.rot.multiply(p,p); + impact.mWorldPos.x = p.x + Box0.center.x + TC->mOffset.x; + impact.mWorldPos.y = p.y + Box0.center.y + TC->mOffset.y; + impact.mWorldPos.z = p.z + Box0.center.z + TC->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TC->mOffset); + } + return true; +} + +static bool sweepVolumeVsMesh( const SweepTest* sweepTest, const TouchedMesh* touchedMesh, SweptContact& impact, + const PxVec3& unitDir, const PxGeometry& geom, const PxTransform& pose, + PxU32 nbTris, const PxTriangle* triangles, + PxU32 cachedIndex) +{ + PxSweepHit sweepHit; + if(PxMeshQuery::sweep(unitDir, impact.mDistance, geom, pose, nbTris, triangles, sweepHit, getSweepHitFlags(sweepTest->mUserParams), &cachedIndex)) + { + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.setWorldPos(sweepHit.position, touchedMesh->mOffset); + + // Returned index is only between 0 and nbTris, i.e. it indexes the array of cached triangles, not the original mesh. + PX_ASSERT(sweepHit.faceIndex < nbTris); + sweepTest->mCachedTriIndex[sweepTest->mCachedTriIndexIndex] = sweepHit.faceIndex; + + // The CCT loop will use the index from the start of the cache... + impact.mInternalIndex = sweepHit.faceIndex + touchedMesh->mIndexWorldTriangles; + const PxU32* triangleIndices = &sweepTest->mTriangleIndices[touchedMesh->mIndexWorldTriangles]; + impact.mTriangleIndex = triangleIndices[sweepHit.faceIndex]; + return true; + } + return false; +} + +static bool SweepBoxMesh(const SweepTest* sweep_test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eMESH); + const SweptBox* SB = static_cast(volume); + const TouchedMesh* TM = static_cast(geom); + + PxU32 nbTris = TM->mNbTris; + if(!nbTris) + return false; + + // Fetch triangle data for current mesh (the stream may contain triangles from multiple meshes) + const PxTriangle* T = &sweep_test->mWorldTriangles.getTriangle(TM->mIndexWorldTriangles); + + // PT: this only really works when the CCT collides with a single mesh, but that's the most common case. When it doesn't, there's just no speedup but it still works. + PxU32 CachedIndex = sweep_test->mCachedTriIndex[sweep_test->mCachedTriIndexIndex]; + if(CachedIndex>=nbTris) + CachedIndex=0; + + PxBoxGeometry boxGeom; + boxGeom.halfExtents = SB->mExtents; + PxTransform boxPose(PxVec3(float(center.x - TM->mOffset.x), float(center.y - TM->mOffset.y), float(center.z - TM->mOffset.z)), sweep_test->mUserParams.mQuatFromUp); // Precompute + return sweepVolumeVsMesh(sweep_test, TM, impact, dir, boxGeom, boxPose, nbTris, T, CachedIndex); +} + +static bool SweepCapsuleMesh( + const SweepTest* sweep_test, const SweptVolume* volume, const TouchedGeom* geom, + const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eMESH); + const SweptCapsule* SC = static_cast(volume); + const TouchedMesh* TM = static_cast(geom); + + PxU32 nbTris = TM->mNbTris; + if(!nbTris) + return false; + + // Fetch triangle data for current mesh (the stream may contain triangles from multiple meshes) + const PxTriangle* T = &sweep_test->mWorldTriangles.getTriangle(TM->mIndexWorldTriangles); + + // PT: this only really works when the CCT collides with a single mesh, but that's the most common case. + // When it doesn't, there's just no speedup but it still works. + PxU32 CachedIndex = sweep_test->mCachedTriIndex[sweep_test->mCachedTriIndexIndex]; + if(CachedIndex>=nbTris) + CachedIndex=0; + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, sweep_test->mUserParams.mQuatFromUp, center, TM->mOffset); + + return sweepVolumeVsMesh(sweep_test, TM, impact, dir, capsuleGeom, capsulePose, nbTris, T, CachedIndex); +} + +static bool SweepBoxBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eBOX); + const SweptBox* SB = static_cast(volume); + const TouchedBox* TB = static_cast(geom); + + PxBoxGeometry boxGeom0; + PxTransform boxPose0; + // To precompute + relocateBox(boxGeom0, boxPose0, center, SB->mExtents, TB->mOffset, test->mUserParams.mQuatFromUp); + + PxBoxGeometry boxGeom1; + PxTransform boxPose1; + relocateBox(boxGeom1, boxPose1, *TB); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom0, boxPose0, boxGeom1, boxPose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mWorldNormal = sweepHit.normal; + impact.mDistance = sweepHit.distance; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TB->mOffset); + return true; +} + +static bool SweepBoxSphere(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eSPHERE); + const SweptBox* SB = static_cast(volume); + const TouchedSphere* TS = static_cast(geom); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, center, SB->mExtents, TS->mOffset, test->mUserParams.mQuatFromUp); + + PxSphereGeometry sphereGeom; + sphereGeom.radius = TS->mRadius; + PxTransform spherePose; + spherePose.p = TS->mCenter; + spherePose.q = PxQuat(PxIdentity); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom, boxPose, sphereGeom, spherePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /* + { + // The sweep test doesn't compute the impact point automatically, so we have to do it here. + PxVec3 NewSphereCenter = TS->mSphere.center - d * dir; + PxVec3 Closest; + gUtilLib->PxPointOBBSqrDist(NewSphereCenter, Box0.center, Box0.extents, Box0.rot, &Closest); + // Compute point on the box, after sweep + Box0.rot.multiply(Closest, Closest); + impact.mWorldPos.x = TS->mOffset.x + Closest.x + Box0.center.x + d * dir.x; + impact.mWorldPos.y = TS->mOffset.y + Closest.y + Box0.center.y + d * dir.y; + impact.mWorldPos.z = TS->mOffset.z + Closest.z + Box0.center.z + d * dir.z; + + impact.mWorldNormal = -impact.mWorldNormal; + }*/ + { + impact.setWorldPos(sweepHit.position, TS->mOffset); + } + return true; +} + +static bool SweepBoxCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eBOX); + PX_ASSERT(geom->mType==TouchedGeomType::eCAPSULE); + const SweptBox* SB = static_cast(volume); + const TouchedCapsule* TC = static_cast(geom); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, center, SB->mExtents, TC->mOffset, test->mUserParams.mQuatFromUp); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, TC->mP0, TC->mP1, TC->mRadius); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, boxGeom, boxPose, capsuleGeom, capsulePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(TC->mCapsule, Box0.center, Box0.extents, Box0.rot, &t, &p); + Box0.rot.multiply(p,p); + impact.mWorldPos.x = p.x + Box0.center.x + TC->mOffset.x; + impact.mWorldPos.y = p.y + Box0.center.y + TC->mOffset.y; + impact.mWorldPos.z = p.z + Box0.center.z + TC->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TC->mOffset); + } + return true; +} + +static bool SweepCapsuleBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eBOX); + const SweptCapsule* SC = static_cast(volume); + const TouchedBox* TB = static_cast(geom); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, test->mUserParams.mQuatFromUp, center, TB->mOffset); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + // To precompute + relocateBox(boxGeom, boxPose, *TB); + + // The box and capsule coordinates are relative to the center of the cached bounding box + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom, capsulePose, boxGeom, boxPose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(Capsule, TB->mBox.center, TB->mBox.extents, TB->mBox.rot, &t, &p); + TB->mBox.rot.multiply(p,p); + p += TB->mBox.center; + impact.mWorldPos.x = p.x + TB->mOffset.x; + impact.mWorldPos.y = p.y + TB->mOffset.y; + impact.mWorldPos.z = p.z + TB->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TB->mOffset); + } + return true; +} + +static bool SweepCapsuleSphere(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eSPHERE); + const SweptCapsule* SC = static_cast(volume); + const TouchedSphere* TS = static_cast(geom); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, test->mUserParams.mQuatFromUp, center, TS->mOffset); + + PxSphereGeometry sphereGeom; + sphereGeom.radius = TS->mRadius; + PxTransform spherePose; + spherePose.p = TS->mCenter; + spherePose.q = PxQuat(PxIdentity); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom, capsulePose, sphereGeom, spherePose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TS->mOffset); + return true; +} + +static bool SweepCapsuleCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eCAPSULE); + const SweptCapsule* SC = static_cast(volume); + const TouchedCapsule* TC = static_cast(geom); + + PxCapsuleGeometry capsuleGeom0; + PxTransform capsulePose0; + relocateCapsule(capsuleGeom0, capsulePose0, SC, test->mUserParams.mQuatFromUp, center, TC->mOffset); + + PxCapsuleGeometry capsuleGeom1; + PxTransform capsulePose1; + relocateCapsule(capsuleGeom1, capsulePose1, TC->mP0, TC->mP1, TC->mRadius); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom0, capsulePose0, capsuleGeom1, capsulePose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TC->mOffset); + return true; +} + +static bool SweepCapsuleUserCapsule(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_CAPSULE); + const SweptCapsule* SC = static_cast(volume); + const TouchedUserCapsule* TC = static_cast(geom); + + PxCapsuleGeometry capsuleGeom0; + PxTransform capsulePose0; + relocateCapsule(capsuleGeom0, capsulePose0, SC, test->mUserParams.mQuatFromUp, center, TC->mOffset); + + PxCapsuleGeometry capsuleGeom1; + PxTransform capsulePose1; + relocateCapsule(capsuleGeom1, capsulePose1, *TC); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom0, capsulePose0, capsuleGeom1, capsulePose1, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.setWorldPos(sweepHit.position, TC->mOffset); + return true; +} + +static bool SweepCapsuleUserBox(const SweepTest* test, const SweptVolume* volume, const TouchedGeom* geom, const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact) +{ + PX_ASSERT(volume->getType()==SweptVolumeType::eCAPSULE); + PX_ASSERT(geom->mType==TouchedGeomType::eUSER_BOX); + const SweptCapsule* SC = static_cast(volume); + const TouchedUserBox* TB = static_cast(geom); + + PxCapsuleGeometry capsuleGeom; + PxTransform capsulePose; + relocateCapsule(capsuleGeom, capsulePose, SC, test->mUserParams.mQuatFromUp, center, TB->mOffset); + + PxBoxGeometry boxGeom; + PxTransform boxPose; + relocateBox(boxGeom, boxPose, *TB); + + PxSweepHit sweepHit; + if(!PxGeometryQuery::sweep(dir, impact.mDistance, capsuleGeom, capsulePose, boxGeom, boxPose, sweepHit, getSweepHitFlags(test->mUserParams))) + return false; + + if(sweepHit.distance >= impact.mDistance) + return false; + + impact.mDistance = sweepHit.distance; + impact.mWorldNormal = sweepHit.normal; + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + + //TO CHECK: Investigate whether any significant performance improvement can be achieved through + // making the impact point computation optional in the sweep calls and compute it later + /*{ + // ### check this + float t; + PxVec3 p; + float d = gUtilLib->PxSegmentOBBSqrDist(Capsule, Box.center, Box.extents, Box.rot, &t, &p); + p += Box.center; + impact.mWorldPos.x = p.x + TB->mOffset.x; + impact.mWorldPos.y = p.y + TB->mOffset.y; + impact.mWorldPos.z = p.z + TB->mOffset.z; + }*/ + { + impact.setWorldPos(sweepHit.position, TB->mOffset); + } + return true; +} + +typedef bool (*SweepFunc) (const SweepTest*, const SweptVolume*, const TouchedGeom*, const PxExtendedVec3&, const PxVec3&, SweptContact&); + +static SweepFunc gSweepMap[SweptVolumeType::eLAST][TouchedGeomType::eLAST] = { + // Box funcs + { + SweepBoxUserBox, + SweepBoxUserCapsule, + SweepBoxMesh, + SweepBoxBox, + SweepBoxSphere, + SweepBoxCapsule + }, + + // Capsule funcs + { + SweepCapsuleUserBox, + SweepCapsuleUserCapsule, + SweepCapsuleMesh, + SweepCapsuleBox, + SweepCapsuleSphere, + SweepCapsuleCapsule + } +}; + +PX_COMPILE_TIME_ASSERT(sizeof(gSweepMap)==SweptVolumeType::eLAST*TouchedGeomType::eLAST*sizeof(SweepFunc)); + +static const PxU32 GeomSizes[] = +{ + sizeof(TouchedUserBox), + sizeof(TouchedUserCapsule), + sizeof(TouchedMesh), + sizeof(TouchedBox), + sizeof(TouchedSphere), + sizeof(TouchedCapsule), +}; + +static const TouchedGeom* CollideGeoms( + const SweepTest* sweep_test, const SweptVolume& volume, const IntArray& geom_stream, + const PxExtendedVec3& center, const PxVec3& dir, SweptContact& impact, bool discardInitialOverlap) +{ + impact.mInternalIndex = PX_INVALID_U32; + impact.mTriangleIndex = PX_INVALID_U32; + impact.mGeom = NULL; + + const PxU32* Data = geom_stream.begin(); + const PxU32* Last = geom_stream.end(); + while(Data!=Last) + { + const TouchedGeom* CurrentGeom = reinterpret_cast(Data); + + SweepFunc ST = gSweepMap[volume.getType()][CurrentGeom->mType]; + if(ST) + { + SweptContact C; + C.mDistance = impact.mDistance; // Initialize with current best distance + C.mInternalIndex = PX_INVALID_U32; + C.mTriangleIndex = PX_INVALID_U32; + if((ST)(sweep_test, &volume, CurrentGeom, center, dir, C)) + { + if(C.mDistance==0.0f) + { + if(!discardInitialOverlap) + { + if(CurrentGeom->mType==TouchedGeomType::eUSER_BOX || CurrentGeom->mType==TouchedGeomType::eUSER_CAPSULE) + { + } + else + { + const PxRigidActor* touchedActor = CurrentGeom->mActor; + PX_ASSERT(touchedActor); + + if(shouldApplyRecoveryModule(*touchedActor)) + { + impact = C; + impact.mGeom = const_cast(CurrentGeom); + return CurrentGeom; + } + } + } + } +/* else + if(discardInitialOverlap && C.mDistance==0.0f) + { + // PT: we previously used eINITIAL_OVERLAP without eINITIAL_OVERLAP_KEEP, i.e. initially overlapping shapes got ignored. + // So we replicate this behavior here. + }*/ + else if(C.mDistance(CurrentGeom); + if(C.mDistance <= 0.0f) // there is no point testing for closer hits + return CurrentGeom; // since we are touching a shape already + } + } + } + + const PxU8* ptr = reinterpret_cast(Data); + ptr += GeomSizes[CurrentGeom->mType]; + Data = reinterpret_cast(ptr); + } + return impact.mGeom; +} + +static PxVec3 computeMTD(const SweepTest* sweep_test, const SweptVolume& volume, const IntArray& geom_stream, const PxExtendedVec3& center, float contactOffset) +{ + PxVec3 p = toVec3(center); + +// contactOffset += 0.01f; + + const PxU32 maxIter = 4; + PxU32 nbIter = 0; + bool isValid = true; + while(isValid && nbIter(Data); + + if(CurrentGeom->mType==TouchedGeomType::eUSER_BOX || CurrentGeom->mType==TouchedGeomType::eUSER_CAPSULE) + { + } + else + { + const PxRigidActor* touchedActor = CurrentGeom->mActor; + PX_ASSERT(touchedActor); + + if(shouldApplyRecoveryModule(*touchedActor)) + { + const PxShape* touchedShape = reinterpret_cast(CurrentGeom->mTGUserData); + PX_ASSERT(touchedShape); + + const PxGeometryHolder gh = touchedShape->getGeometry(); + const PxTransform globalPose = getShapeGlobalPose(*touchedShape, *touchedActor); + + PxVec3 mtd; + PxF32 depth; + + const PxTransform volumePose(p, sweep_test->mUserParams.mQuatFromUp); + if(volume.getType()==SweptVolumeType::eCAPSULE) + { + const SweptCapsule& sc = static_cast(volume); + const PxCapsuleGeometry capsuleGeom(sc.mRadius+contactOffset, sc.mHeight*0.5f); + isValid = PxGeometryQuery::computePenetration(mtd, depth, capsuleGeom, volumePose, gh.any(), globalPose); + } + else + { + PX_ASSERT(volume.getType()==SweptVolumeType::eBOX); + const SweptBox& sb = static_cast(volume); + const PxBoxGeometry boxGeom(sb.mExtents+PxVec3(contactOffset)); + isValid = PxGeometryQuery::computePenetration(mtd, depth, boxGeom, volumePose, gh.any(), globalPose); + } + + if(isValid) + { + nbIter++; + PX_ASSERT(depth>=0.0f); + PX_ASSERT(mtd.isFinite()); + PX_ASSERT(PxIsFinite(depth)); +#ifdef DEBUG_MTD + PX_ASSERT(depth<=1.0f); + if(depth>1.0f || !mtd.isFinite() || !PxIsFinite(depth)) + { + int stop=1; + (void)stop; + } + printf("Depth: %f\n", depth); + printf("mtd: %f %f %f\n", mtd.x, mtd.y, mtd.z); +#endif + p += mtd * depth; + } + } + } + + const PxU8* ptr = reinterpret_cast(Data); + ptr += GeomSizes[CurrentGeom->mType]; + Data = reinterpret_cast(ptr); + } + } + return p; +} + + + +static bool ParseGeomStream(const void* object, const IntArray& geom_stream) +{ + const PxU32* Data = geom_stream.begin(); + const PxU32* Last = geom_stream.end(); + while(Data!=Last) + { + const TouchedGeom* CurrentGeom = reinterpret_cast(Data); + if(CurrentGeom->mTGUserData==object) + return true; + + const PxU8* ptr = reinterpret_cast(Data); + ptr += GeomSizes[CurrentGeom->mType]; + Data = reinterpret_cast(ptr); + } + return false; +} + +CCTParams::CCTParams() : + mNonWalkableMode (PxControllerNonWalkableMode::ePREVENT_CLIMBING), + mQuatFromUp (PxQuat(PxIdentity)), + mUpDirection (PxVec3(0.0f)), + mSlopeLimit (0.0f), + mContactOffset (0.0f), + mStepOffset (0.0f), + mInvisibleWallHeight (0.0f), + mMaxJumpHeight (0.0f), + mMaxEdgeLength2 (0.0f), + mTessellation (false), + mHandleSlope (false), + mOverlapRecovery (false), + mPreciseSweeps (true), + mPreventVerticalSlidingAgainstCeiling (false) +{ +} + +SweepTest::SweepTest(bool registerDeletionListener) : + mRenderBuffer (NULL), + mRenderFlags (0), + mTriangleIndices (PX_DEBUG_EXP("sweepTestTriangleIndices")), + mGeomStream (PX_DEBUG_EXP("sweepTestStream")), + mTouchedShape (registerDeletionListener), + mTouchedActor (registerDeletionListener), + mSQTimeStamp (0xffffffff), + mNbFullUpdates (0), + mNbPartialUpdates (0), + mNbTessellation (0), + mNbIterations (0), + mFlags (0), + mRegisterDeletionListener(registerDeletionListener), + mCctManager (NULL) +{ + mCacheBounds.setEmpty(); + mCachedTriIndexIndex = 0; + mCachedTriIndex[0] = mCachedTriIndex[1] = mCachedTriIndex[2] = 0; + mNbCachedStatic = 0; + mNbCachedT = 0; + + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + mTouchedPos = PxVec3(0); + mTouchedPosShape_Local = PxVec3(0); + mTouchedPosShape_World = PxVec3(0); + mTouchedPosObstacle_Local = PxVec3(0); + mTouchedPosObstacle_World = PxVec3(0); + +// mVolumeGrowth = 1.2f; // Must be >1.0f and not too big + mVolumeGrowth = 1.5f; // Must be >1.0f and not too big +// mVolumeGrowth = 2.0f; // Must be >1.0f and not too big + + mContactNormalDownPass = PxVec3(0.0f); + mContactNormalSidePass = PxVec3(0.0f); + mTouchedTriMin = 0.0f; + mTouchedTriMax = 0.0f; +} + + +SweepTest::~SweepTest() +{ + // set the TouchedObject to NULL so we unregister the actor/shape + mTouchedShape = NULL; + mTouchedActor = NULL; +} + +void SweepTest::voidTestCache() +{ + mTouchedShape = NULL; + mTouchedActor = NULL; + mCacheBounds.setEmpty(); + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; +} + +void SweepTest::onRelease(const PxBase& observed) +{ + if (mTouchedActor == &observed) + { + mTouchedShape = NULL; + mTouchedActor = NULL; + return; + } + + if(ParseGeomStream(&observed, mGeomStream)) + mCacheBounds.setEmpty(); + + if (mTouchedShape == &observed) + mTouchedShape = NULL; +} + +void SweepTest::updateCachedShapesRegistration(PxU32 startIndex, bool unregister) +{ + if(!mRegisterDeletionListener) + return; + + if(!mGeomStream.size() || startIndex == mGeomStream.size()) + return; + + PX_ASSERT(startIndex <= mGeomStream.size()); + + const PxU32* data = &mGeomStream[startIndex]; + const PxU32* last = mGeomStream.end(); + while (data != last) + { + const TouchedGeom* CurrentGeom = reinterpret_cast(data); + if (CurrentGeom->mActor) + { + if(unregister) + mCctManager->unregisterObservedObject(reinterpret_cast(CurrentGeom->mTGUserData)); + else + mCctManager->registerObservedObject(reinterpret_cast(CurrentGeom->mTGUserData)); + } + else + { + // we can early exit, the rest of the data are user obstacles + return; + } + + const PxU8* ptr = reinterpret_cast(data); + ptr += GeomSizes[CurrentGeom->mType]; + data = reinterpret_cast(ptr); + } +} + +void SweepTest::onObstacleAdded(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance ) +{ + if(mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE) + { + // check if new obstacle is closer + const ObstacleContext* obstContext = static_cast (context); + PxRaycastHit obstacleHit; + const PxObstacle* obst = obstContext->raycastSingle(obstacleHit,index,origin,unitDir,distance); + + if(obst && (obstacleHit.position.dot(unitDir))<(mTouchedPosObstacle_World.dot(unitDir))) + { + PX_ASSERT(obstacleHit.distance<=distance); + mTouchedObstacleHandle = index; + if(!gUseLocalSpace) + { + mTouchedPos = toVec3(obst->mPos); + } + else + { + mTouchedPosObstacle_World = obstacleHit.position; + mTouchedPosObstacle_Local = worldToLocal(*obst, PxExtendedVec3(PxExtended(obstacleHit.position.x),PxExtended(obstacleHit.position.y),PxExtended(obstacleHit.position.z))); + } + } + } +} + +void SweepTest::onObstacleRemoved(ObstacleHandle index) +{ + if(index == mTouchedObstacleHandle) + { + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + } +} + +void SweepTest::onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance) +{ + if(index == mTouchedObstacleHandle) + { + // check if updated obstacle is still closest + const ObstacleContext* obstContext = static_cast (context); + PxRaycastHit obstacleHit; + ObstacleHandle closestHandle = INVALID_OBSTACLE_HANDLE; + const PxObstacle* obst = obstContext->raycastSingle(obstacleHit,origin,unitDir,distance,closestHandle); + + if(mTouchedObstacleHandle == closestHandle) + return; + + if(obst) + { + PX_ASSERT(obstacleHit.distance<=distance); + mTouchedObstacleHandle = closestHandle; + if(!gUseLocalSpace) + { + mTouchedPos = toVec3(obst->mPos); + } + else + { + mTouchedPosObstacle_World = obstacleHit.position; + mTouchedPosObstacle_Local = worldToLocal(*obst, PxExtendedVec3(PxExtended(obstacleHit.position.x),PxExtended(obstacleHit.position.y),PxExtended(obstacleHit.position.z))); + } + } + } +} + +void SweepTest::onOriginShift(const PxVec3& shift) +{ + mCacheBounds.minimum -= shift; + mCacheBounds.maximum -= shift; + + if(mTouchedShape) + { + const PxRigidActor* rigidActor = mTouchedActor.get(); + if(rigidActor->getConcreteType() != PxConcreteType::eRIGID_STATIC) + { + mTouchedPosShape_World -= shift; + } + } + else if (mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE) + { + if(!gUseLocalSpace) + { + mTouchedPos -= shift; + } + else + { + mTouchedPosObstacle_World -= shift; + } + } + + // adjust cache + PxU32* data = mGeomStream.begin(); + PxU32* last = mGeomStream.end(); + while(data != last) + { + TouchedGeom* currentGeom = reinterpret_cast(data); + + currentGeom->mOffset -= shift; + + PxU8* ptr = reinterpret_cast(data); + ptr += GeomSizes[currentGeom->mType]; + data = reinterpret_cast(ptr); + } +} + +static PxBounds3 getBounds3(const PxExtendedBounds3& extended) +{ + return PxBounds3(toVec3(extended.minimum), toVec3(extended.maximum)); // LOSS OF ACCURACY +} + +// PT: finds both touched CCTs and touched user-defined obstacles +void SweepTest::findTouchedObstacles(const UserObstacles& userObstacles, const PxExtendedBounds3& worldBox) +{ + PxExtendedVec3 Origin; // Will be TouchedGeom::mOffset + getCenter(worldBox, Origin); + + { + const PxU32 nbBoxes = userObstacles.mNbBoxes; + const PxExtendedBox* boxes = userObstacles.mBoxes; + const void** boxUserData = userObstacles.mBoxUserData; + + const PxBounds3 singlePrecisionWorldBox = getBounds3(worldBox); + + // Find touched boxes, i.e. other box controllers + for(PxU32 i=0;i(reserveContainerMemory(mGeomStream, sizeof(TouchedUserBox)/sizeof(PxU32))); + UserBox->mType = TouchedGeomType::eUSER_BOX; + UserBox->mTGUserData = boxUserData[i]; + UserBox->mActor = NULL; + UserBox->mOffset = Origin; + UserBox->mBox = boxes[i]; + } + } + + { + // Find touched capsules, i.e. other capsule controllers + const PxU32 nbCapsules = userObstacles.mNbCapsules; + const PxExtendedCapsule* capsules = userObstacles.mCapsules; + const void** capsuleUserData = userObstacles.mCapsuleUserData; + + PxExtendedVec3 Center; + PxVec3 Extents; + getCenter(worldBox, Center); + getExtents(worldBox, Extents); + + for(PxU32 i=0;i worldBox.maximum.x) || (worldBox.minimum.x > capMaxx + PxExtended(r))) continue; + + const PxExtended capMiny = PxMin(capsules[i].p0.y, capsules[i].p1.y); + const PxExtended capMaxy = PxMax(capsules[i].p0.y, capsules[i].p1.y); + if((capMiny - PxExtended(r) > worldBox.maximum.y) || (worldBox.minimum.y > capMaxy + PxExtended(r))) continue; + + const PxExtended capMinz = PxMin(capsules[i].p0.z, capsules[i].p1.z); + const PxExtended capMaxz = PxMax(capsules[i].p0.z, capsules[i].p1.z); + if((capMinz - PxExtended(r) > worldBox.maximum.z) || (worldBox.minimum.z > capMaxz + PxExtended(r))) continue; + + // PT: more accurate capsule-box test. Not strictly necessary but worth doing if available + const PxReal d2 = Gu::distanceSegmentBoxSquared(toVec3(capsules[i].p0), toVec3(capsules[i].p1), toVec3(Center), Extents, PxMat33(PxIdentity)); + if(d2>r*r) + continue; + + TouchedUserCapsule* UserCapsule = reinterpret_cast(reserveContainerMemory(mGeomStream, sizeof(TouchedUserCapsule)/sizeof(PxU32))); + UserCapsule->mType = TouchedGeomType::eUSER_CAPSULE; + UserCapsule->mTGUserData = capsuleUserData[i]; + UserCapsule->mActor = NULL; + UserCapsule->mOffset = Origin; + UserCapsule->mCapsule = capsules[i]; + } + } +} + +void SweepTest::updateTouchedGeoms( const InternalCBData_FindTouchedGeom* userData, const UserObstacles& userObstacles, + const PxExtendedBounds3& worldTemporalBox, const PxControllerFilters& filters, const PxVec3& sideVector) +{ + /* + - if this is the first iteration (new frame) we have to redo the dynamic objects & the CCTs. The static objects can + be cached. + - if this is not, we can cache everything + */ + + // PT: using "worldTemporalBox" instead of "mCacheBounds" seems to produce TTP 6207 +//#define DYNAMIC_BOX worldTemporalBox +#define DYNAMIC_BOX mCacheBounds + + bool newCachedBox = false; + + CCTFilter filter; + filter.mFilterData = filters.mFilterData; + filter.mFilterCallback = filters.mFilterCallback; + filter.mPreFilter = filters.mFilterFlags & PxQueryFlag::ePREFILTER; + filter.mPostFilter = filters.mFilterFlags & PxQueryFlag::ePOSTFILTER; + + // PT: detect changes to the static pruning structure + bool sceneHasChanged = false; + { + const PxU32 currentTimestamp = getSceneTimestamp(userData); + if(currentTimestamp!=mSQTimeStamp) + { + mSQTimeStamp = currentTimestamp; + sceneHasChanged = true; + } + } + + // If the input box is inside the cached box, nothing to do + if(gUsePartialUpdates && !sceneHasChanged && worldTemporalBox.isInside(mCacheBounds)) + { + //printf("CACHEIN%d\n", mFirstUpdate); + if(mFlags & STF_FIRST_UPDATE) + { + mFlags &= ~STF_FIRST_UPDATE; + + // Only redo the dynamic + updateCachedShapesRegistration(mNbCachedStatic, true); + mGeomStream.forceSize_Unsafe(mNbCachedStatic); + mWorldTriangles.forceSize_Unsafe(mNbCachedT); + mTriangleIndices.forceSize_Unsafe(mNbCachedT); + + filter.mStaticShapes = false; + if(filters.mFilterFlags & PxQueryFlag::eDYNAMIC) + filter.mDynamicShapes = true; + findTouchedGeometry(userData, DYNAMIC_BOX, mWorldTriangles, mTriangleIndices, mGeomStream, filter, mUserParams, mNbTessellation); + updateCachedShapesRegistration(mNbCachedStatic, false); + + findTouchedObstacles(userObstacles, DYNAMIC_BOX); + + mNbPartialUpdates++; + } + } + else + { + //printf("CACHEOUTNS=%d\n", mNbCachedStatic); + newCachedBox = true; + + // Cache BV used for the query + mCacheBounds = worldTemporalBox; + + // Grow the volume a bit. The temporal box here doesn't take sliding & collision response into account. + // In bad cases it is possible to eventually touch a portion of space not covered by this volume. Just + // in case, we grow the initial volume slightly. Then, additional tests are performed within the loop + // to make sure the TBV is always correct. There's a tradeoff between the original (artificial) growth + // of the volume, and the number of TBV recomputations performed at runtime... + scale(mCacheBounds, PxVec3(mVolumeGrowth)); +// scale(mCacheBounds, PxVec3(mVolumeGrowth, 1.0f, mVolumeGrowth)); + + if(1 && !sideVector.isZero()) + { + const PxVec3 sn = sideVector.getNormalized(); + float dp0 = PxAbs((worldTemporalBox.maximum - worldTemporalBox.minimum).dot(sn)); + float dp1 = PxAbs((mCacheBounds.maximum - mCacheBounds.minimum).dot(sn)); + dp1 -= dp0; + dp1 *= 0.5f * 0.9f; + const PxVec3 offset = sn * dp1; +// printf("%f %f %f\n", offset.x, offset.y, offset.z); + mCacheBounds.minimum += offset; + mCacheBounds.maximum += offset; + add(mCacheBounds, worldTemporalBox); + PX_ASSERT(worldTemporalBox.isInside(mCacheBounds)); + } + + updateCachedShapesRegistration(0, true); + + // Gather triangles touched by this box. This covers multiple meshes. + mWorldTriangles.clear(); + mTriangleIndices.clear(); + mGeomStream.clear(); +// mWorldTriangles.reset(); +// mTriangleIndices.reset(); +// mGeomStream.reset(); + + mCachedTriIndexIndex = 0; + mCachedTriIndex[0] = mCachedTriIndex[1] = mCachedTriIndex[2] = 0; + + mNbFullUpdates++; + + if(filters.mFilterFlags & PxQueryFlag::eSTATIC) + filter.mStaticShapes = true; + filter.mDynamicShapes = false; + findTouchedGeometry(userData, mCacheBounds, mWorldTriangles, mTriangleIndices, mGeomStream, filter, mUserParams, mNbTessellation); + + mNbCachedStatic = mGeomStream.size(); + mNbCachedT = mWorldTriangles.size(); + PX_ASSERT(mTriangleIndices.size()==mNbCachedT); + + filter.mStaticShapes = false; + if(filters.mFilterFlags & PxQueryFlag::eDYNAMIC) + filter.mDynamicShapes = true; + findTouchedGeometry(userData, DYNAMIC_BOX, mWorldTriangles, mTriangleIndices, mGeomStream, filter, mUserParams, mNbTessellation); + // We can't early exit when no tris are touched since we also have to handle the boxes + updateCachedShapesRegistration(0, false); + + findTouchedObstacles(userObstacles, DYNAMIC_BOX); + + mFlags &= ~STF_FIRST_UPDATE; + //printf("CACHEOUTNSDONE=%d\n", mNbCachedStatic); + } + + if(mRenderBuffer) + { + // PT: worldTemporalBox = temporal BV for this frame + RenderOutput out(*mRenderBuffer); + + if(mRenderFlags & PxControllerDebugRenderFlag::eTEMPORAL_BV) + { + out << gTBVDebugColor; + out << DebugBox(getBounds3(worldTemporalBox)); + } + + if(mRenderFlags & PxControllerDebugRenderFlag::eCACHED_BV) + { + if(newCachedBox) + out << PxU32(PxDebugColor::eARGB_RED); + else + out << PxU32(PxDebugColor::eARGB_GREEN); + out << DebugBox(getBounds3(mCacheBounds)); + } + } +} + +// This is the generic sweep test for all swept volumes, but not character-controller specific +bool SweepTest::doSweepTest(const InternalCBData_FindTouchedGeom* userData, + InternalCBData_OnHit* userHitData, + const UserObstacles& userObstacles, + SweptVolume& swept_volume, + const PxVec3& direction, const PxVec3& sideVector, PxU32 max_iter, PxU32* nb_collisions, + float min_dist, const PxControllerFilters& filters, SweepPass sweepPass, + const PxRigidActor*& touchedActorOut, const PxShape*& touchedShapeOut, PxU64 contextID) +{ + // Early exit when motion is zero. Since the motion is decomposed into several vectors + // and this function is called for each of them, it actually happens quite often. + if(direction.isZero()) + return false; + + PX_PROFILE_ZONE("CharacterController.doSweepTest", contextID); + PX_UNUSED(contextID); + + bool hasMoved = false; + mFlags &= ~(STF_VALIDATE_TRIANGLE_DOWN|STF_TOUCH_OTHER_CCT|STF_TOUCH_OBSTACLE); + touchedShapeOut = NULL; + touchedActorOut = NULL; + mTouchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + + PxExtendedVec3 currentPosition = swept_volume.mCenter; + PxExtendedVec3 targetOrientation = swept_volume.mCenter; + targetOrientation += direction; + + PxU32 NbCollisions = 0; + while(max_iter--) + { + mNbIterations++; + // Compute current direction + PxVec3 currentDirection = targetOrientation - currentPosition; + + // Make sure the new TBV is still valid + { + // Compute temporal bounding box. We could use a capsule or an OBB instead: + // - the volume would be smaller + // - but the query would be slower + // Overall it's unclear whether it's worth it or not. + // TODO: optimize this part ? + PxExtendedBounds3 temporalBox; + swept_volume.computeTemporalBox(*this, temporalBox, currentPosition, currentDirection); + + // Gather touched geoms + updateTouchedGeoms(userData, userObstacles, temporalBox, filters, sideVector); + } + + const float Length = currentDirection.magnitude(); + if(Length<=min_dist) //Use <= to handle the case where min_dist is zero. + break; + + currentDirection /= Length; + + // From Quake2: "if velocity is against the original velocity, stop dead to avoid tiny occilations in sloping corners" + if((currentDirection.dot(direction)) <= 0.0f) + break; + + // From this point, we're going to update the position at least once + hasMoved = true; + + // Find closest collision + SweptContact C; + C.mDistance = Length + mUserParams.mContactOffset; + + if(!CollideGeoms(this, swept_volume, mGeomStream, currentPosition, currentDirection, C, !mUserParams.mOverlapRecovery)) + { + // no collision found => move to desired position + currentPosition = targetOrientation; + break; + } + + PX_ASSERT(C.mGeom); // If we reach this point, we must have touched a geom + + if(mUserParams.mOverlapRecovery && C.mDistance==0.0f) + { +/* SweptContact C; + C.mDistance = 10.0f; + CollideGeoms(this, swept_volume, mGeomStream, currentPosition, -currentDirection, C, true); + currentPosition -= currentDirection*C.mDistance; + + C.mDistance = 10.0f; + CollideGeoms(this, swept_volume, mGeomStream, currentPosition, currentDirection, C, true); + const float DynSkin = mUserParams.mContactOffset; + if(C.mDistance>DynSkin) + currentPosition += currentDirection*(C.mDistance-DynSkin);*/ + + const PxVec3 mtd = computeMTD(this, swept_volume, mGeomStream, currentPosition, mUserParams.mContactOffset); + + NbCollisions++; + + if(nb_collisions) + *nb_collisions = NbCollisions; + +#ifdef DEBUG_MTD + printf("MTD FIXUP: %f %f %f\n", mtd.x - swept_volume.mCenter.x, mtd.y - swept_volume.mCenter.y, mtd.z - swept_volume.mCenter.z); +#endif + swept_volume.mCenter.x = PxExtended(mtd.x); + swept_volume.mCenter.y = PxExtended(mtd.y); + swept_volume.mCenter.z = PxExtended(mtd.z); + return hasMoved; +// currentPosition.x = mtd.x; +// currentPosition.y = mtd.y; +// currentPosition.z = mtd.z; +// continue; + } + + bool preventVerticalMotion = false; + bool stopSliding = true; + if(C.mGeom->mType==TouchedGeomType::eUSER_BOX || C.mGeom->mType==TouchedGeomType::eUSER_CAPSULE) + { + if(sweepPass!=SWEEP_PASS_SENSOR) + { + // We touched a user object, typically another CCT, but can also be a user-defined obstacle + + // PT: TODO: technically lines marked with (*) shouldn't be here... revisit later + + const PxObstacle* touchedObstacle = NULL; // (*) + ObstacleHandle touchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + // if(mValidateCallback) + { + PxInternalCBData_OnHit* internalData = static_cast(userHitData); // (*) + internalData->touchedObstacle = NULL; // (*) + internalData->touchedObstacleHandle = INVALID_OBSTACLE_HANDLE; + const PxU32 behaviorFlags = userHitCallback(userHitData, C, currentDirection, Length); + stopSliding = (behaviorFlags & PxControllerBehaviorFlag::eCCT_SLIDE)==0; // (*) + touchedObstacle = internalData->touchedObstacle; // (*) + touchedObstacleHandle = internalData->touchedObstacleHandle; + } + // printf("INTERNAL: %d\n", int(touchedObstacle)); + + if(sweepPass==SWEEP_PASS_DOWN) + { + // (*) + if(touchedObstacle) + { + mFlags |= STF_TOUCH_OBSTACLE; + + mTouchedObstacleHandle = touchedObstacleHandle; + if(!gUseLocalSpace) + { + mTouchedPos = toVec3(touchedObstacle->mPos); + } + else + { + mTouchedPosObstacle_World = toVec3(C.mWorldPos); + mTouchedPosObstacle_Local = worldToLocal(*touchedObstacle, C.mWorldPos); + } + } + else + { + mFlags |= STF_TOUCH_OTHER_CCT; + } + } + } + } + else + { + const PxShape* touchedShape = reinterpret_cast(C.mGeom->mTGUserData); + PX_ASSERT(touchedShape); + const PxRigidActor* touchedActor = C.mGeom->mActor; + PX_ASSERT(touchedActor); + + // We touched a normal object + if(sweepPass==SWEEP_PASS_DOWN) + { + mFlags &= ~(STF_TOUCH_OTHER_CCT|STF_TOUCH_OBSTACLE); + +#ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST + mFlags |= STF_VALIDATE_TRIANGLE_DOWN; + mContactNormalDownPass = C.mWorldNormal; +#else + + // Work out if the shape is attached to a static or dynamic actor. + // The slope limit is currently only considered when walking on static actors. + // It is ignored for shapes attached attached to dynamics and kinematics. + // TODO: 1. should we treat stationary kinematics the same as statics. + // 2. should we treat all kinematics the same as statics. + // 3. should we treat no kinematics the same as statics. + if((touchedActor->getConcreteType() == PxConcreteType::eRIGID_STATIC) && (C.mInternalIndex!=PX_INVALID_U32)) + { + mFlags |= STF_VALIDATE_TRIANGLE_DOWN; + const PxTriangle& touchedTri = mWorldTriangles.getTriangle(C.mInternalIndex); + const PxVec3& upDirection = mUserParams.mUpDirection; + const float dp0 = touchedTri.verts[0].dot(upDirection); + const float dp1 = touchedTri.verts[1].dot(upDirection); + const float dp2 = touchedTri.verts[2].dot(upDirection); + float dpmin = dp0; + dpmin = physx::intrinsics::selectMin(dpmin, dp1); + dpmin = physx::intrinsics::selectMin(dpmin, dp2); + float dpmax = dp0; + dpmax = physx::intrinsics::selectMax(dpmax, dp1); + dpmax = physx::intrinsics::selectMax(dpmax, dp2); + + PxExtendedVec3 cacheCenter; + getCenter(mCacheBounds, cacheCenter); + const float offset = upDirection.dot(toVec3(cacheCenter)); + mTouchedTriMin = dpmin + offset; + mTouchedTriMax = dpmax + offset; + + touchedTri.normal(mContactNormalDownPass); + } +#endif + // Update touched shape in down pass + touchedShapeOut = const_cast(touchedShape); + touchedActorOut = touchedActor; +// mTouchedPos = getShapeGlobalPose(*touchedShape).p; + const PxTransform shapeTransform = getShapeGlobalPose(*touchedShape, *touchedActor); + const PxVec3 worldPos = toVec3(C.mWorldPos); + mTouchedPosShape_World = worldPos; + mTouchedPosShape_Local = shapeTransform.transformInv(worldPos); + } + else if(sweepPass==SWEEP_PASS_SIDE || sweepPass==SWEEP_PASS_SENSOR) + { + if((touchedActor->getConcreteType() == PxConcreteType::eRIGID_STATIC) && (C.mInternalIndex!=PX_INVALID_U32)) + { + mFlags |= STF_VALIDATE_TRIANGLE_SIDE; + const PxTriangle& touchedTri = mWorldTriangles.getTriangle(C.mInternalIndex); + touchedTri.normal(mContactNormalSidePass); +// printf("%f | %f | %f\n", mContactNormalSidePass.x, mContactNormalSidePass.y, mContactNormalSidePass.z); + if(mUserParams.mPreventVerticalSlidingAgainstCeiling && mContactNormalSidePass.dot(mUserParams.mUpDirection)<0.0f) + preventVerticalMotion = true; + } + } + + if(sweepPass!=SWEEP_PASS_SENSOR) +// if(mValidateCallback) + { + const PxU32 behaviorFlags = shapeHitCallback(userHitData, C, currentDirection, Length); + stopSliding = (behaviorFlags & PxControllerBehaviorFlag::eCCT_SLIDE)==0; // (*) + } + } + + if(sweepPass==SWEEP_PASS_DOWN && !stopSliding) + { + // Trying to solve the following problem: + // - by default, the CCT "friction" is infinite, i.e. a CCT will not slide on a slope (this is by design) + // - this produces bad results when a capsule CCT stands on top of another capsule CCT, without sliding. Visually it looks + // like the character is standing on the other character's head, it looks bad. So, here, we would like to let the CCT + // slide away, i.e. we don't want friction. + // So here we simply increase the number of iterations (== let the CCT slide) when the first down collision is with another CCT. + if(!NbCollisions) + max_iter += 9; +// max_iter += 1; + } + + NbCollisions++; +// mContactPointHeight = (float)C.mWorldPos[mUserParams.mUpDirection]; // UBI + mContactPointHeight = toVec3(C.mWorldPos).dot(mUserParams.mUpDirection); // UBI + + const float DynSkin = mUserParams.mContactOffset; + + if(C.mDistance>DynSkin/*+0.01f*/) + currentPosition += currentDirection*(C.mDistance-DynSkin); +// DE6513 +/* else if(sweepPass==SWEEP_PASS_SIDE) + { + // Might be better to do a proper sweep pass here, in the opposite direction + currentPosition += currentDirection*(C.mDistance-DynSkin); + }*/ +//~DE6513 + + PxVec3 WorldNormal = C.mWorldNormal; + if(preventVerticalMotion || ((mFlags & STF_WALK_EXPERIMENT) && (mUserParams.mNonWalkableMode!=PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING))) + { + // Make sure the auto-step doesn't bypass this ! + // PT: cancel out normal compo +// WorldNormal[mUserParams.mUpDirection]=0.0f; +// WorldNormal.normalize(); + PxVec3 normalCompo, tangentCompo; + Ps::decomposeVector(normalCompo, tangentCompo, WorldNormal, mUserParams.mUpDirection); + WorldNormal = tangentCompo; + WorldNormal.normalize(); + } + + const float Bump = 0.0f; // ### doesn't work when !=0 because of Quake2 hack! + const float Friction = 1.0f; + collisionResponse(targetOrientation, currentPosition, currentDirection, WorldNormal, Bump, Friction, (mFlags & STF_NORMALIZE_RESPONSE)!=0); + } + + if(nb_collisions) + *nb_collisions = NbCollisions; + + // Final box position that should be reflected in the graphics engine + swept_volume.mCenter = currentPosition; + + // If we didn't move, don't update the box position at all (keeping possible lazy-evaluated structures valid) + return hasMoved; +} + +// ### have a return code to tell if we really moved or not + +// Using swept code & direct position update (no physics engine) +// This function is the generic character controller logic, valid for all swept volumes +PxControllerCollisionFlags SweepTest::moveCharacter( + const InternalCBData_FindTouchedGeom* userData, + InternalCBData_OnHit* userHitData, + SweptVolume& volume, + const PxVec3& direction, + const UserObstacles& userObstacles, + float min_dist, + const PxControllerFilters& filters, + bool constrainedClimbingMode, + bool standingOnMoving, + const PxRigidActor*& touchedActor, + const PxShape*& touchedShape, + PxU64 contextID) +{ + PX_PROFILE_ZONE("CharacterController.moveCharacter", contextID); + PX_UNUSED(contextID); + + bool standingOnMovingUp = standingOnMoving; + + mFlags &= ~STF_HIT_NON_WALKABLE; + PxControllerCollisionFlags CollisionFlags = PxControllerCollisionFlags(0); + const PxU32 maxIter = MAX_ITER; // 1 for "collide and stop" + const PxU32 maxIterSides = maxIter; + const PxU32 maxIterDown = ((mFlags & STF_WALK_EXPERIMENT) && mUserParams.mNonWalkableMode==PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING) ? maxIter : 1; +// const PxU32 maxIterDown = 1; + + // ### this causes the artificial gap on top of chars + float stepOffset = mUserParams.mStepOffset; // Default step offset can be cancelled in some cases. + + // Save initial height + const PxVec3& upDirection = mUserParams.mUpDirection; + const PxExtended originalHeight = volume.mCenter.dot(upDirection); + const PxExtended originalBottomPoint = originalHeight - PxExtended(volume.mHalfHeight); // UBI + + // TEST! Disable auto-step when flying. Not sure this is really useful. +// if(direction[upDirection]>0.0f) + const float dir_dot_up = direction.dot(upDirection); +//printf("%f\n", dir_dot_up); + if(dir_dot_up>0.0f) + { + mFlags |= STF_IS_MOVING_UP; + + // PT: this makes it fail on a platform moving up when jumping + // However if we don't do that a jump when moving up a slope doesn't work anymore! + // Not doing this also creates jittering when a capsule CCT jumps against another capsule CCT + if(!standingOnMovingUp) // PT: if we're standing on something moving up it's safer to do the up motion anyway, even though this won't work well before we add the flag in TA13542 + { +// static int count=0; printf("Cancelling step offset... %d\n", count++); + stepOffset = 0.0f; + } + } + else + { + mFlags &= ~STF_IS_MOVING_UP; + } + + // Decompose motion into 3 independent motions: up, side, down + // - if the motion is purely down (gravity only), the up part is needed to fight accuracy issues. For example if the + // character is already touching the geometry a bit, the down sweep test might have troubles. If we first move it above + // the geometry, the problems disappear. + // - if the motion is lateral (character moving forward under normal gravity) the decomposition provides the autostep feature + // - if the motion is purely up, the down part can be skipped + + PxVec3 UpVector(0.0f, 0.0f, 0.0f); + PxVec3 DownVector(0.0f, 0.0f, 0.0f); + + PxVec3 normal_compo, tangent_compo; + Ps::decomposeVector(normal_compo, tangent_compo, direction, upDirection); + +// if(direction[upDirection]<0.0f) + if(dir_dot_up<=0.0f) +// DownVector[upDirection] = direction[upDirection]; + DownVector = normal_compo; + else +// UpVector[upDirection] = direction[upDirection]; + UpVector = normal_compo; + +// PxVec3 SideVector = direction; +// SideVector[upDirection] = 0.0f; + PxVec3 SideVector = tangent_compo; + + // If the side motion is zero, i.e. if the character is not really moving, disable auto-step. + // This is important to prevent the CCT from automatically climbing on small objects that move + // against it. We should climb over those only if there's a valid side motion from the player. + const bool sideVectorIsZero = !standingOnMovingUp && Ps::isAlmostZero(SideVector); // We can't use PxVec3::isZero() safely with arbitrary up vectors + // #### however if we do this the up pass is disabled, with bad consequences when the CCT is on a dynamic object!! + // ### this line makes it possible to push other CCTs by jumping on them +// const bool sideVectorIsZero = false; +// printf("sideVectorIsZero: %d\n", sideVectorIsZero); + +// if(!SideVector.isZero()) + if(!sideVectorIsZero) +// UpVector[upDirection] += stepOffset; + UpVector += upDirection*stepOffset; +// printf("stepOffset: %f\n", stepOffset); + + // ==========[ Initial volume query ]=========================== + // PT: the main difference between this initial query and subsequent ones is that we use the + // full direction vector here, not the components along each axis. So there is a good chance + // that this initial query will contain all the motion we need, and thus subsequent queries + // will be skipped. + { + PxExtendedBounds3 temporalBox; + volume.computeTemporalBox(*this, temporalBox, volume.mCenter, direction); + + // Gather touched geoms + updateTouchedGeoms(userData, userObstacles, temporalBox, filters, SideVector); + } + + // ==========[ UP PASS ]=========================== + + mCachedTriIndexIndex = 0; + const bool performUpPass = true; + PxU32 NbCollisions=0; + + PxU32 maxIterUp; + if(mUserParams.mPreventVerticalSlidingAgainstCeiling) + maxIterUp = 1; + else + maxIterUp = Ps::isAlmostZero(SideVector) ? maxIter : 1; + + if(performUpPass) + { +// printf("%f | %f | %f\n", UpVector.x, UpVector.y, UpVector.z); + + // Prevent user callback for up motion. This up displacement is artificial, and only needed for auto-stepping. + // If we call the user for this, we might eventually apply upward forces to objects resting on top of us, even + // if we visually don't move. This produces weird-looking motions. +// mValidateCallback = false; + // PT: actually I think the previous comment is wrong. It's not only needed for auto-stepping: when the character + // jumps there's a legit up motion and the lack of callback in that case could need some object can't be pushed + // by the character's 'head' (for example). So I now think it's better to use the callback all the time, and + // let users figure out what to do using the available state (like "isMovingUp", etc). +// mValidateCallback = true; + + // In the walk-experiment we explicitly want to ban any up motions, to avoid characters climbing slopes they shouldn't climb. + // So let's bypass the whole up pass. + if(!(mFlags & STF_WALK_EXPERIMENT)) + { + // ### maxIter here seems to "solve" the V bug + if(doSweepTest(userData, userHitData, userObstacles, volume, UpVector, SideVector, maxIterUp, &NbCollisions, min_dist, filters, SWEEP_PASS_UP, touchedActor, touchedShape, contextID)) + { + if(NbCollisions) + { + CollisionFlags |= PxControllerCollisionFlag::eCOLLISION_UP; + + // Clamp step offset to make sure we don't undo more than what we did + float Delta = float(volume.mCenter.dot(upDirection) - originalHeight); + + if(Delta(volume).mRadius; + + const float sideM = SideVector.magnitude(); + if(sideM originalBottomPoint + PxExtended(stepOffset)) + { + mFlags |= STF_HIT_NON_WALKABLE; + if(!(mFlags & STF_WALK_EXPERIMENT)) + return CollisionFlags; + // printf("Contrained\n"); + } + } + //~constrainedClimbingMode + } + } + } + //printf("AD:%.2f %.2f %.2f NS=%d\n", volume.mCenter.x, volume.mCenter.y, volume.mCenter.z, mNbCachedStatic); +// printf("%d\n", mTouchOtherCCT); + + // TEST: do another down pass if we're on a non-walkable poly + // ### kind of works but still not perfect + // ### could it be because we zero the Y impulse later? + // ### also check clamped response vectors +// if(mUserParams.mHandleSlope && mValidateTriangle && direction[upDirection]<0.0f) +// if(mUserParams.mHandleSlope && !mTouchOtherCCT && !mTouchObstacle && mValidateTriangle && dir_dot_up<0.0f) + if(mUserParams.mHandleSlope && !(mFlags & (STF_TOUCH_OTHER_CCT|STF_TOUCH_OBSTACLE)) && (mFlags & STF_VALIDATE_TRIANGLE_DOWN) && dir_dot_up<=0.0f) + { + PxVec3 Normal; + #ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST + Normal = mContactNormalDownPass; + #else + //mTouchedTriangle.normal(Normal); + Normal = mContactNormalDownPass; + #endif + + const float touchedTriHeight = float(PxExtended(mTouchedTriMax) - originalBottomPoint); + +/* if(touchedTriHeight>mUserParams.mStepOffset) + { + if(constrainedClimbingMode && mContactPointHeight > originalBottomPoint + stepOffset) + { + mFlags |= STF_HIT_NON_WALKABLE; + if(!(mFlags & STF_WALK_EXPERIMENT)) + return CollisionFlags; + } + }*/ + + if(touchedTriHeight>mUserParams.mStepOffset && testSlope(Normal, upDirection, mUserParams.mSlopeLimit)) + { + mFlags |= STF_HIT_NON_WALKABLE; + // Early exit if we're going to run this again anyway... + if(!(mFlags & STF_WALK_EXPERIMENT)) + return CollisionFlags; + /* CatchScene()->GetRenderer()->AddLine(mTouchedTriangle.mVerts[0], mTouched.mVerts[1], ARGB_YELLOW); + CatchScene()->GetRenderer()->AddLine(mTouchedTriangle.mVerts[0], mTouched.mVerts[2], ARGB_YELLOW); + CatchScene()->GetRenderer()->AddLine(mTouchedTriangle.mVerts[1], mTouched.mVerts[2], ARGB_YELLOW); + */ + + // ==========[ WALK EXPERIMENT ]=========================== + + mFlags |= STF_NORMALIZE_RESPONSE; + + const PxExtended tmp = volume.mCenter.dot(upDirection); + float Delta = tmp > originalHeight ? float(tmp - originalHeight) : 0.0f; + Delta += fabsf(direction.dot(upDirection)); + float Recover = Delta; + + NbCollisions=0; + const float MD = Recover < min_dist ? Recover/float(maxIter) : min_dist; + + PxVec3 RecoverPoint(0,0,0); + RecoverPoint = -upDirection*Recover; + + // PT: we pass "SWEEP_PASS_UP" for compatibility with previous code, but it's technically wrong (this is a 'down' pass) + if(doSweepTest(userData, userHitData, userObstacles, volume, RecoverPoint, SideVector, maxIter, &NbCollisions, MD, filters, SWEEP_PASS_UP, touchedActor, touchedShape, contextID)) + { + // if(NbCollisions) CollisionFlags |= COLLISION_Y_DOWN; + // PT: why did we do this ? Removed for now. It creates a bug (non registered event) when we land on a steep poly. + // However this might have been needed when we were sliding on those polygons, and we didn't want the land anim to + // start while we were sliding. + // if(NbCollisions) CollisionFlags &= ~PxControllerCollisionFlag::eCOLLISION_DOWN; + } + mFlags &= ~STF_NORMALIZE_RESPONSE; + } + } + } + + return CollisionFlags; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// This is an interface between NX users and the internal character controller module. + +#include "CctInternalStructs.h" +#include "CctBoxController.h" +#include "CctCapsuleController.h" +#include "CctCharacterControllerManager.h" +#include "PxActor.h" +#include "PxScene.h" +#include "PxControllerBehavior.h" +#include "CctObstacleContext.h" + + // PT: we use a local class instead of making "Controller" a PxQueryFilterCallback, since it would waste more memory. + // Ideally we'd have a C-style callback and a user-data pointer, instead of being forced to create a class. + class ControllerFilter : public PxQueryFilterCallback + { + public: + PxQueryHitType::Enum preFilter(const PxFilterData& filterData, const PxShape* shape, const PxRigidActor* actor, PxHitFlags& queryFlags) + { + // PT: ignore triggers + if(shape->getFlags() & physx::PxShapeFlag::eTRIGGER_SHAPE) + return PxQueryHitType::eNONE; + + // PT: we want to discard our own internal shapes only + if(mShapeHashSet->contains(const_cast(shape))) + return PxQueryHitType::eNONE; + + // PT: otherwise we revert to the user-callback, if it exists, and if users enabled that call + if(mUserFilterCallback && (mUserFilterFlags | PxQueryFlag::ePREFILTER)) + return mUserFilterCallback->preFilter(filterData, shape, actor, queryFlags); + + return PxQueryHitType::eBLOCK; + } + + PxQueryHitType::Enum postFilter(const PxFilterData& filterData, const PxQueryHit& hit) + { + // PT: we may get called if users have asked for such a callback + if(mUserFilterCallback && (mUserFilterFlags | PxQueryFlag::ePOSTFILTER)) + return mUserFilterCallback->postFilter(filterData, hit); + + PX_ASSERT(0); // PT: otherwise we shouldn't have been called + return PxQueryHitType::eNONE; + } + + Ps::HashSet* mShapeHashSet; + PxQueryFilterCallback* mUserFilterCallback; + PxQueryFlags mUserFilterFlags; + }; + + +bool Controller::filterTouchedShape(const PxControllerFilters& filters) +{ + PxQueryFlags filterFlags = PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER; + PxQueryFilterData filterData(filters.mFilterData ? *filters.mFilterData : PxFilterData(), filterFlags); + PxHitFlags hitFlags = PxHitFlags(0); + + if(filters.mFilterCallback && (filters.mFilterFlags | PxQueryFlag::ePREFILTER)) + { + PxQueryHitType::Enum retVal = filters.mFilterCallback->preFilter(filterData.data, mCctModule.mTouchedShape.get(), mCctModule.mTouchedActor.get(), hitFlags); + if(retVal != PxQueryHitType::eNONE) + return true; + else + return false; + } + + return true; +} + +void Controller::findTouchedObject(const PxControllerFilters& filters, const PxObstacleContext* obstacleContext, const PxVec3& upDirection) +{ + PX_ASSERT(!mCctModule.mTouchedShape && (mCctModule.mTouchedObstacleHandle == INVALID_OBSTACLE_HANDLE)); + + // PT: the CCT works perfectly on statics without this extra mechanism, so we only raycasts against dynamics. + // The pre-filter callback is used to filter out our own proxy actor shapes. We need to make sure our own filter + // doesn't disturb the user-provided filter(s). + + // PT: for starter, if user doesn't want to collide against dynamics, we can skip the whole thing + if(filters.mFilterFlags & PxQueryFlag::eDYNAMIC) + { + ControllerFilter preFilter; + preFilter.mShapeHashSet = &mManager->mCCTShapes; + preFilter.mUserFilterCallback = filters.mFilterCallback; + preFilter.mUserFilterFlags = filters.mFilterFlags; + + // PT: for our own purpose we just want dynamics & pre-filter + PxQueryFlags filterFlags = PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER; + // PT: but we may need the post-filter callback as well if users want it + if(filters.mFilterFlags & PxQueryFlag::ePOSTFILTER) + filterFlags |= PxQueryFlag::ePOSTFILTER; + + PxQueryFilterData filterData(filters.mFilterData ? *filters.mFilterData : PxFilterData(), filterFlags); + + const PxF32 probeLength = getHalfHeightInternal(); // Distance to feet + const PxF32 extra = 0.0f;//probeLength * 0.1f; + + const PxVec3 rayOrigin = toVec3(mPosition); + + PxRaycastBuffer hit; + hit.block.distance = FLT_MAX; + if(mScene->raycast(rayOrigin, -upDirection, probeLength+extra, hit, PxHitFlags(0), filterData, &preFilter)) + { + // copy touching hit to blocking so that the rest of the code works with .block + hit.block = hit.getAnyHit(0); + PX_ASSERT(hit.block.shape); + PX_ASSERT(hit.block.actor); + PX_ASSERT(hit.block.distance<=probeLength+extra); + mCctModule.mTouchedShape = hit.block.shape; + mCctModule.mTouchedActor = hit.block.actor; +// mCctModule.mTouchedPos = getShapeGlobalPose(*hit.shape).p - upDirection*(probeLength-hit.distance); + // PT: we only care about the up delta here + const PxTransform shapeTransform = getShapeGlobalPose(*hit.block.shape, *hit.block.actor); + mCctModule.mTouchedPosShape_World = PxVec3(0) - upDirection*(probeLength-hit.block.distance); + mCctModule.mTouchedPosShape_Local = shapeTransform.transformInv(PxVec3(0)); + + mPreviousSceneTimestamp = mScene->getTimestamp()-1; // PT: just make sure cached timestamp is different + } + + if(obstacleContext) + { + const ObstacleContext* obstacles = static_cast(obstacleContext); + PxRaycastHit obstacleHit; + ObstacleHandle obstacleHandle; + const PxObstacle* touchedObstacle = obstacles->raycastSingle(obstacleHit, rayOrigin, -upDirection, probeLength+extra, obstacleHandle); +// printf("Touched raycast obstacle: %d\n", int(touchedObstacle)); + if(touchedObstacle && obstacleHit.distancemPos) - upDirection*(probeLength-obstacleHit.distance); + } + else + { + // PT: we only care about the up delta here + mCctModule.mTouchedPosObstacle_World = PxVec3(0) - upDirection*(probeLength-obstacleHit.distance); + mCctModule.mTouchedPosObstacle_Local = worldToLocal(*touchedObstacle, PxExtendedVec3(0,0,0)); + } + } + } + } +} + +bool Controller::rideOnTouchedObject(SweptVolume& volume, const PxVec3& upDirection, PxVec3& disp, const PxObstacleContext* obstacleContext) +{ + PX_ASSERT(mCctModule.mTouchedShape || (mCctModule.mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE)); + + bool standingOnMoving = false; + + bool canDoUpdate = true; // Always true on obstacles + PxU32 behaviorFlags = 0; // Default on shapes + PxVec3 delta(0); + float timeCoeff = 1.0f; + + if(mCctModule.mTouchedShape) + { + // PT: riding on a shape + + // PT: it is important to skip this stuff for static meshes, + // otherwise accuracy issues create bugs like TA14007. + const PxRigidActor& rigidActor = *mCctModule.mTouchedActor.get(); + if(rigidActor.getConcreteType()!=PxConcreteType::eRIGID_STATIC) + { + // PT: we only do the update when the timestamp has changed, otherwise "delta" will be zero + // even if the underlying shape is moving. + const PxU32 timestamp = mScene->getTimestamp(); +// printf("TimeStamp: %d\n", timestamp); + canDoUpdate = timestamp!=mPreviousSceneTimestamp; + if(canDoUpdate) + { + mPreviousSceneTimestamp = timestamp; + + timeCoeff = computeTimeCoeff(); + + if(mBehaviorCallback) + behaviorFlags = mBehaviorCallback->getBehaviorFlags(*mCctModule.mTouchedShape.get(), *mCctModule.mTouchedActor.get()); + +// delta = getShapeGlobalPose(*mCctModule.mTouchedShape).p - mCctModule.mTouchedPos; + const PxTransform shapeTransform = getShapeGlobalPose(*mCctModule.mTouchedShape.get(), rigidActor); + const PxVec3 posPreviousFrame = mCctModule.mTouchedPosShape_World; + const PxVec3 posCurrentFrame = shapeTransform.transform(mCctModule.mTouchedPosShape_Local); + delta = posCurrentFrame - posPreviousFrame; + } + } + } + else + { + // PT: riding on an obstacle + behaviorFlags = PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT; // Default on obstacles + + timeCoeff = computeTimeCoeff(); + + const PxObstacle* touchedObstacle = obstacleContext->getObstacleByHandle(mCctModule.mTouchedObstacleHandle); + PX_ASSERT(touchedObstacle); + + if(mBehaviorCallback) + behaviorFlags = mBehaviorCallback->getBehaviorFlags(*touchedObstacle); + + if(!gUseLocalSpace) + { + delta = toVec3(touchedObstacle->mPos) - mCctModule.mTouchedPos; + } + else + { + PxVec3 posPreviousFrame = mCctModule.mTouchedPosObstacle_World; + PxVec3 posCurrentFrame = localToWorld(*touchedObstacle, mCctModule.mTouchedPosObstacle_Local); + delta = posCurrentFrame - posPreviousFrame; + } + } + + if(canDoUpdate && !(behaviorFlags & PxControllerBehaviorFlag::eCCT_USER_DEFINED_RIDE)) + { + // PT: amazingly enough even isAlmostZero doesn't solve this one. + // Moving on a static mesh sometimes produces delta bigger than 1e-6f! + // This may also explain the drift on some rotating platforms. It looks + // like this delta computation is not very accurate. +// standingOnMoving = !delta.isZero(); + standingOnMoving = !Ps::isAlmostZero(delta); + mCachedStandingOnMoving = standingOnMoving; +//printf("%f %f %f\n", delta.x, delta.y, delta.z); + if(standingOnMoving) + { + const float dir_dot_up = delta.dot(upDirection); + const bool deltaMovingUp = dir_dot_up>0.0f; + + PxVec3 deltaUpDisp, deltaSideDisp; + Ps::decomposeVector(deltaUpDisp, deltaSideDisp, delta, upDirection); + + if(deltaMovingUp) + { + volume.mCenter.x += PxExtended(deltaUpDisp.x); + volume.mCenter.y += PxExtended(deltaUpDisp.y); + volume.mCenter.z += PxExtended(deltaUpDisp.z); + } + else + { + disp += deltaUpDisp; + } + + if(behaviorFlags & PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT) + disp += deltaSideDisp; + } +// printf("delta in: %f %f %f (%f)\n", delta.x, delta.y, delta.z, 1.0f/timeCoeff); + mDeltaXP = delta * timeCoeff; + } + else + { + standingOnMoving = mCachedStandingOnMoving; + } +// mDelta = delta; + + return standingOnMoving; +} + +PxControllerCollisionFlags Controller::move(SweptVolume& volume, const PxVec3& originalDisp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacleContext, bool constrainedClimbingMode) +{ + const bool lockWrite = mManager->mLockingEnabled; + if(lockWrite) + mWriteLock.lock(); + + mGlobalTime += PxF64(elapsedTime); + + // Init CCT with per-controller settings + RenderBuffer* renderBuffer = mManager->mRenderBuffer; + const PxU32 debugRenderFlags = mManager->mDebugRenderingFlags; + mCctModule.mRenderBuffer = renderBuffer; + mCctModule.mRenderFlags = debugRenderFlags; + mCctModule.mUserParams = mUserParams; + mCctModule.mFlags |= STF_FIRST_UPDATE; + mCctModule.mUserParams.mMaxEdgeLength2 = mManager->mMaxEdgeLength * mManager->mMaxEdgeLength; + mCctModule.mUserParams.mTessellation = mManager->mTessellation; + mCctModule.mUserParams.mOverlapRecovery = mManager->mOverlapRecovery; + mCctModule.mUserParams.mPreciseSweeps = mManager->mPreciseSweeps; + mCctModule.mUserParams.mPreventVerticalSlidingAgainstCeiling = mManager->mPreventVerticalSlidingAgainstCeiling; + mCctModule.resetStats(); + + const PxVec3& upDirection = mUserParams.mUpDirection; + + /////////// + + PxVec3 disp = originalDisp + mOverlapRecover; + mOverlapRecover = PxVec3(0.0f); + + bool standingOnMoving = false; // PT: whether the CCT is currently standing on a moving object + //printf("Touched shape: %d\n", int(mCctModule.mTouchedShape)); +//standingOnMoving=true; +// printf("Touched obstacle: %d\n", int(mCctModule.mTouchedObstacle)); + + if(mCctModule.mTouchedActor && mCctModule.mTouchedShape) + { + PxU32 nbShapes = mCctModule.mTouchedActor->getNbShapes(); + bool found = false; + for(PxU32 i=0;igetShapes(&shape, 1, i); + if(mCctModule.mTouchedShape==shape) + { + found = true; + break; + } + } + + if(!found) + { + mCctModule.mTouchedActor = NULL; + mCctModule.mTouchedShape = NULL; + } + else + { + // check if we are still in the same scene + if(mCctModule.mTouchedActor->getScene() != mScene) + { + mCctModule.mTouchedShape = NULL; + mCctModule.mTouchedActor = NULL; + } + else + { + // check if the shape still does have the sq flag + if(!(mCctModule.mTouchedShape->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE)) + { + mCctModule.mTouchedShape = NULL; + mCctModule.mTouchedActor = NULL; + } + else + { + // invoke the CCT filtering for the shape + if(!filterTouchedShape(filters)) + { + mCctModule.mTouchedShape = NULL; + mCctModule.mTouchedActor = NULL; + } + } + } + } + } + + if(!mCctModule.mTouchedShape && (mCctModule.mTouchedObstacleHandle == INVALID_OBSTACLE_HANDLE)) + findTouchedObject(filters, obstacleContext, upDirection); + + if(mCctModule.mTouchedShape || (mCctModule.mTouchedObstacleHandle != INVALID_OBSTACLE_HANDLE)) + { + standingOnMoving = rideOnTouchedObject(volume, upDirection, disp, obstacleContext); + } + else + { + mCachedStandingOnMoving = false; + mDeltaXP = PxVec3(0.0f); + } +// printf("standingOnMoving: %d\n", standingOnMoving); + + /////////// + Ps::Array& boxUserData = mManager->mBoxUserData; + Ps::Array& boxes = mManager->mBoxes; + Ps::Array& capsuleUserData = mManager->mCapsuleUserData; + Ps::Array& capsules = mManager->mCapsules; + PX_ASSERT(!boxUserData.size()); + PX_ASSERT(!boxes.size()); + PX_ASSERT(!capsuleUserData.size()); + PX_ASSERT(!capsules.size()); + + { + PX_PROFILE_ZONE("CharacterController.filterCandidateControllers", getContextId()); + + // Experiment - to do better + const PxU32 nbControllers = mManager->getNbControllers(); + Controller** controllers = mManager->getControllers(); + + for(PxU32 i=0;ifilter(*getPxController(), *currentController->getPxController()); + + if(keepController) + { + if(currentController->mType==PxControllerShapeType::eBOX) + { + // PT: TODO: optimize this + BoxController* BC = static_cast(currentController); + PxExtendedBox obb; + BC->getOBB(obb); + + boxes.pushBack(obb); + +#ifdef REMOVED + if(renderBuffer /*&& (debugRenderFlags & PxControllerDebugRenderFlag::eOBSTACLES)*/) + { + RenderOutput out(*renderBuffer); + out << gCCTBoxDebugColor; + + out << PxTransform(toVec3(obb.center), obb.rot); + + out << DebugBox(obb.extents, true); + } +#endif + const size_t code = encodeUserObject(i, USER_OBJECT_CCT); + boxUserData.pushBack(reinterpret_cast(code)); + } + else if(currentController->mType==PxControllerShapeType::eCAPSULE) + { + CapsuleController* CC = static_cast(currentController); + + // PT: TODO: optimize this + PxExtendedCapsule worldCapule; + CC->getCapsule(worldCapule); + capsules.pushBack(worldCapule); + + const size_t code = encodeUserObject(i, USER_OBJECT_CCT); + capsuleUserData.pushBack(reinterpret_cast(code)); + } + else PX_ASSERT(0); + } + } + } + + const ObstacleContext* obstacles = NULL; + if(obstacleContext) + { + obstacles = static_cast(obstacleContext); + + // PT: TODO: optimize this + const PxU32 nbExtraBoxes = obstacles->mBoxObstacles.size(); + for(PxU32 i=0;imBoxObstacles[i].mData; + + PxExtendedBox extraBox; + extraBox.center = userBoxObstacle.mPos; + extraBox.extents = userBoxObstacle.mHalfExtents; + extraBox.rot = userBoxObstacle.mRot; + boxes.pushBack(extraBox); + + const size_t code = encodeUserObject(i, USER_OBJECT_BOX_OBSTACLE); + boxUserData.pushBack(reinterpret_cast(code)); + + if(renderBuffer && (debugRenderFlags & PxControllerDebugRenderFlag::eOBSTACLES)) + { + RenderOutput out(*renderBuffer); + out << gObstacleDebugColor; + + out << PxTransform(toVec3(userBoxObstacle.mPos), userBoxObstacle.mRot); + + out << DebugBox(userBoxObstacle.mHalfExtents, true); + } + } + + const PxU32 nbExtraCapsules = obstacles->mCapsuleObstacles.size(); + for(PxU32 i=0;imCapsuleObstacles[i].mData; + + PxExtendedCapsule extraCapsule; + const PxVec3 capsuleAxis = userCapsuleObstacle.mRot.getBasisVector0() * userCapsuleObstacle.mHalfHeight; + extraCapsule.p0 = PxExtendedVec3( userCapsuleObstacle.mPos.x - PxExtended(capsuleAxis.x), + userCapsuleObstacle.mPos.y - PxExtended(capsuleAxis.y), + userCapsuleObstacle.mPos.z - PxExtended(capsuleAxis.z)); + extraCapsule.p1 = PxExtendedVec3( userCapsuleObstacle.mPos.x + PxExtended(capsuleAxis.x), + userCapsuleObstacle.mPos.y + PxExtended(capsuleAxis.y), + userCapsuleObstacle.mPos.z + PxExtended(capsuleAxis.z)); + + extraCapsule.radius = userCapsuleObstacle.mRadius; + capsules.pushBack(extraCapsule); + const size_t code = encodeUserObject(i, USER_OBJECT_CAPSULE_OBSTACLE); + capsuleUserData.pushBack(reinterpret_cast(code)); + + if(renderBuffer && (debugRenderFlags & PxControllerDebugRenderFlag::eOBSTACLES)) + { + RenderOutput out(*renderBuffer); + out << gObstacleDebugColor; + out.outputCapsule(userCapsuleObstacle.mRadius, userCapsuleObstacle.mHalfHeight, PxTransform(toVec3(userCapsuleObstacle.mPos), userCapsuleObstacle.mRot)); + } + } + } + + + UserObstacles userObstacles; + + const PxU32 nbBoxes = boxes.size(); + userObstacles.mNbBoxes = nbBoxes; + userObstacles.mBoxes = nbBoxes ? boxes.begin() : NULL; + userObstacles.mBoxUserData = nbBoxes ? boxUserData.begin() : NULL; + + const PxU32 nbCapsules = capsules.size(); + userObstacles.mNbCapsules = nbCapsules; + userObstacles.mCapsules = nbCapsules ? capsules.begin() : NULL; + userObstacles.mCapsuleUserData = nbCapsules ? capsuleUserData.begin() : NULL; + + PxInternalCBData_OnHit userHitData; + userHitData.controller = this; + userHitData.obstacles = obstacles; + + /////////// + + PxControllerCollisionFlags collisionFlags = PxControllerCollisionFlags(0); + + PxInternalCBData_FindTouchedGeom findGeomData; + findGeomData.scene = mScene; + findGeomData.renderBuffer = renderBuffer; + findGeomData.cctShapeHashSet = &mManager->mCCTShapes; + + mCctModule.mFlags &= ~STF_WALK_EXPERIMENT; + + // store new touched actor/shape. Then set new actor/shape to avoid register/unregister for same objects + const PxRigidActor* touchedActor = NULL; + const PxShape* touchedShape = NULL; + PxExtendedVec3 Backup = volume.mCenter; + collisionFlags = mCctModule.moveCharacter(&findGeomData, &userHitData, volume, disp, userObstacles, minDist, filters, constrainedClimbingMode, standingOnMoving, touchedActor, touchedShape, getContextId()); + + if(mCctModule.mFlags & STF_HIT_NON_WALKABLE) + { + // A bit slow, but everything else I tried was less convincing... + mCctModule.mFlags |= STF_WALK_EXPERIMENT; + volume.mCenter = Backup; + + PxVec3 xpDisp; + if(mUserParams.mNonWalkableMode==PxControllerNonWalkableMode::ePREVENT_CLIMBING_AND_FORCE_SLIDING) + { + PxVec3 tangent_compo; + Ps::decomposeVector(xpDisp, tangent_compo, disp, upDirection); + } + else xpDisp = disp; + + collisionFlags = mCctModule.moveCharacter(&findGeomData, &userHitData, volume, xpDisp, userObstacles, minDist, filters, constrainedClimbingMode, standingOnMoving, touchedActor, touchedShape, getContextId()); + + mCctModule.mFlags &= ~STF_WALK_EXPERIMENT; + } + mCctModule.mTouchedActor = touchedActor; + mCctModule.mTouchedShape = touchedShape; + + mCollisionFlags = collisionFlags; + + // Copy results back + mPosition = volume.mCenter; + + // Update kinematic actor + if(mKineActor) + { + const PxVec3 delta = Backup - volume.mCenter; + const PxF32 deltaM2 = delta.magnitudeSquared(); + if(deltaM2!=0.0f) + { + PxTransform targetPose = mKineActor->getGlobalPose(); + targetPose.p = toVec3(mPosition); + targetPose.q = mUserParams.mQuatFromUp; + mKineActor->setKinematicTarget(targetPose); + } + } + + mManager->resetObstaclesBuffers(); + + if (lockWrite) + mWriteLock.unlock(); + + return collisionFlags; +} + + +PxControllerCollisionFlags BoxController::move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles) +{ + PX_PROFILE_ZONE("CharacterController.move", getContextId()); + + PX_SIMD_GUARD; + + // Create internal swept box + SweptBox sweptBox; + sweptBox.mCenter = mPosition; + sweptBox.mExtents = PxVec3(mHalfHeight, mHalfSideExtent, mHalfForwardExtent); + sweptBox.mHalfHeight = mHalfHeight; // UBI + return Controller::move(sweptBox, disp, minDist, elapsedTime, filters, obstacles, false); +} + +PxControllerCollisionFlags CapsuleController::move(const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles) +{ + PX_PROFILE_ZONE("CharacterController.move", getContextId()); + + PX_SIMD_GUARD; + + // Create internal swept capsule + SweptCapsule sweptCapsule; + sweptCapsule.mCenter = mPosition; + sweptCapsule.mRadius = mRadius; + sweptCapsule.mHeight = mHeight; + sweptCapsule.mHalfHeight = mHeight*0.5f + mRadius; // UBI + return Controller::move(sweptCapsule, disp, minDist, elapsedTime, filters, obstacles, mClimbingMode==PxCapsuleClimbingMode::eCONSTRAINED); +} + diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h new file mode 100644 index 000000000..b075a0ca3 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h @@ -0,0 +1,481 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_CHARACTER_CONTROLLER +#define CCT_CHARACTER_CONTROLLER + +//#define USE_CONTACT_NORMAL_FOR_SLOPE_TEST + +#include "PxController.h" +#include "PxControllerObstacles.h" +#include "CctCharacterControllerManager.h" +#include "CctUtils.h" +#include "PxTriangle.h" +#include "PsArray.h" +#include "PsHashSet.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +struct PxFilterData; +class PxQueryFilterCallback; +class PxObstacle; + +namespace Cm +{ + class RenderBuffer; +} + +namespace Cct +{ + struct CCTParams + { + CCTParams(); + + PxControllerNonWalkableMode::Enum mNonWalkableMode; + PxQuat mQuatFromUp; + PxVec3 mUpDirection; + PxF32 mSlopeLimit; + PxF32 mContactOffset; + PxF32 mStepOffset; + PxF32 mInvisibleWallHeight; + PxF32 mMaxJumpHeight; + PxF32 mMaxEdgeLength2; + bool mTessellation; + bool mHandleSlope; // True to handle walkable parts according to slope + bool mOverlapRecovery; + bool mPreciseSweeps; + bool mPreventVerticalSlidingAgainstCeiling; + }; + +// typedef Ps::Array TriArray; + typedef Ps::Array IntArray; + + // PT: using private inheritance to control access, and make sure allocations are SIMD friendly + class TriArray : private Ps::Array + { + public: + + PX_FORCE_INLINE PxTriangle* reserve(PxU32 nbTris) + { + // PT: customized version of "reserveContainerMemory" + + const PxU32 maxNbEntries = Ps::Array::capacity(); + const PxU32 realRequiredSize = Ps::Array::size() + nbTris; + // PT: allocate one more tri to make sure we can safely V4Load the last one... + const PxU32 requiredSize = realRequiredSize + 1; + + if(requiredSize>maxNbEntries) + { + // PT: ok so the commented out growing policy was introduced by PX-837 but it produces + // large memory usage regressions (see PX-881) while not actually making things run + // faster. Our benchmarks show no performance difference, but up to +38% more memory + // used with this "standard" growing policy. So for now we just go back to the initial + // growing policy. It should be fine since PX-837 was not actually reported by a customer, + // it was just a concern that appeared while looking at the code. Ideally we'd use a pool + // with fixed-size slabs to get the best of both worlds but it would make iterating over + // triangles more complicated and would need more refactoring. So for now we don't bother, + // but we'll keep this note here for the next time this problem shows up. + // PT: new August 2018: turns out PX-837 was correct. Not doing this produces very large + // performance problems (like: the app freezes!) in SampleCCT. We didn't see it because + // it's an internal sample that it rarely used these days... + const PxU32 naturalGrowthSize = maxNbEntries ? maxNbEntries*2 : 2; + const PxU32 newSize = PxMax(requiredSize, naturalGrowthSize); +// const PxU32 newSize = requiredSize; + + Ps::Array::reserve(newSize); + } + + PxTriangle* buf = Ps::Array::end(); + // ...but we still want the size to reflect the correct number + Ps::Array::forceSize_Unsafe(realRequiredSize); + return buf; + } + + PX_FORCE_INLINE void pushBack(const PxTriangle& tri) + { + PxTriangle* memory = reserve(1); + memory->verts[0] = tri.verts[0]; + memory->verts[1] = tri.verts[1]; + memory->verts[2] = tri.verts[2]; + } + + PX_FORCE_INLINE PxU32 size() const + { + return Ps::Array::size(); + } + + PX_FORCE_INLINE const PxTriangle* begin() const + { + return Ps::Array::begin(); + } + + PX_FORCE_INLINE void clear() + { + Ps::Array::clear(); + } + + PX_FORCE_INLINE void forceSize_Unsafe(PxU32 size) + { + Ps::Array::forceSize_Unsafe(size); + } + + PX_FORCE_INLINE const PxTriangle& getTriangle(PxU32 index) const + { + return (*this)[index]; + } + + }; + + /* Exclude from documentation */ + /** \cond */ + + struct TouchedGeomType + { + enum Enum + { + eUSER_BOX, + eUSER_CAPSULE, + eMESH, + eBOX, + eSPHERE, + eCAPSULE, + + eLAST, + + eFORCE_DWORD = 0x7fffffff + }; + }; + + class SweptVolume; + + // PT: apparently .Net aligns some of them on 8-bytes boundaries for no good reason. This is bad. + // Whenever a variable points to a field of a specially aligned struct, it has to be declared with __packed (see GHS docu, Structure Packing, page 111). + // Every reference to such a field needs the __packed declaration: all function parameters and assignment operators etc. +#pragma pack(push,4) + + struct TouchedGeom + { + TouchedGeomType::Enum mType; + const void* mTGUserData; // PxController or PxShape pointer + const PxRigidActor* mActor; // PxActor for PxShape pointers (mandatory with shared shapes) + PxExtendedVec3 mOffset; // Local origin, typically the center of the world bounds around the character. We translate both + // touched shapes & the character so that they are nearby this PxVec3, then add the offset back to + // computed "world" impacts. + protected: + ~TouchedGeom(){} + }; + + struct TouchedUserBox : public TouchedGeom + { + PxExtendedBox mBox; + }; + PX_COMPILE_TIME_ASSERT(sizeof(TouchedUserBox)==sizeof(TouchedGeom)+sizeof(PxExtendedBox)); + + struct TouchedUserCapsule : public TouchedGeom + { + PxExtendedCapsule mCapsule; + }; + PX_COMPILE_TIME_ASSERT(sizeof(TouchedUserCapsule)==sizeof(TouchedGeom)+sizeof(PxExtendedCapsule)); + + struct TouchedMesh : public TouchedGeom + { + PxU32 mNbTris; + PxU32 mIndexWorldTriangles; + }; + + struct TouchedBox : public TouchedGeom + { + PxVec3 mCenter; + PxVec3 mExtents; + PxQuat mRot; + }; + + struct TouchedSphere : public TouchedGeom + { + PxVec3 mCenter; //!< Sphere's center + PxF32 mRadius; //!< Sphere's radius + }; + + struct TouchedCapsule : public TouchedGeom + { + PxVec3 mP0; //!< Start of segment + PxVec3 mP1; //!< End of segment + PxF32 mRadius; //!< Capsule's radius + }; + +#pragma pack(pop) + + struct SweptContact + { + PxExtendedVec3 mWorldPos; // Contact position in world space + PxVec3 mWorldNormal; // Contact normal in world space + PxF32 mDistance; // Contact distance + PxU32 mInternalIndex; // Reserved for internal usage + PxU32 mTriangleIndex; // Triangle index for meshes/heightfields + TouchedGeom* mGeom; + + PX_FORCE_INLINE void setWorldPos(const PxVec3& localImpact, const PxExtendedVec3& offset) + { + mWorldPos.x = PxExtended(localImpact.x) + offset.x; + mWorldPos.y = PxExtended(localImpact.y) + offset.y; + mWorldPos.z = PxExtended(localImpact.z) + offset.z; + } + }; + + // PT: user-defined obstacles. Note that "user" is from the SweepTest class' point of view, + // i.e. the PhysX CCT module is the user in this case. This is to limit coupling between the + // core CCT module and the PhysX classes. + struct UserObstacles// : PxObstacleContext + { + PxU32 mNbBoxes; + const PxExtendedBox* mBoxes; + const void** mBoxUserData; + + PxU32 mNbCapsules; + const PxExtendedCapsule* mCapsules; + const void** mCapsuleUserData; + }; + + struct InternalCBData_OnHit{}; + struct InternalCBData_FindTouchedGeom{}; + + enum SweepTestFlag + { + STF_HIT_NON_WALKABLE = (1<<0), + STF_WALK_EXPERIMENT = (1<<1), + STF_VALIDATE_TRIANGLE_DOWN = (1<<2), // Validate touched triangle data (down pass) + STF_VALIDATE_TRIANGLE_SIDE = (1<<3), // Validate touched triangle data (side pass) + STF_TOUCH_OTHER_CCT = (1<<4), // Are we standing on another CCT or not? (only updated for down pass) + STF_TOUCH_OBSTACLE = (1<<5), // Are we standing on an obstacle or not? (only updated for down pass) + STF_NORMALIZE_RESPONSE = (1<<6), + STF_FIRST_UPDATE = (1<<7), + STF_IS_MOVING_UP = (1<<8) + }; + + enum SweepPass + { + SWEEP_PASS_UP, + SWEEP_PASS_SIDE, + SWEEP_PASS_DOWN, + SWEEP_PASS_SENSOR + }; + + class Controller; + + template + struct TouchedObject + { + TouchedObject(bool regDl) + : mTouchedObject(NULL), mRegisterDeletionListener(regDl), mCctManager(NULL) + { + } + + PX_FORCE_INLINE const T* operator->() const { return mTouchedObject; } + PX_FORCE_INLINE bool operator==(const TouchedObject& otherObject) { return mTouchedObject == otherObject.mTouchedObject; } + PX_FORCE_INLINE bool operator==(const T* otherObject) { return mTouchedObject == otherObject; } + PX_FORCE_INLINE bool operator==(const PxBase* otherObject) { return mTouchedObject == otherObject; } + PX_FORCE_INLINE operator bool() const { return mTouchedObject != NULL; } + PX_FORCE_INLINE TouchedObject& operator=(const T* assignedObject) + { + if(mRegisterDeletionListener && (mTouchedObject != assignedObject)) + { + if(mTouchedObject) + mCctManager->unregisterObservedObject(mTouchedObject); + + if(assignedObject) + mCctManager->registerObservedObject(assignedObject); + } + mTouchedObject = assignedObject; + return *this; + } + + const T* get() const { return mTouchedObject; } + + void setCctManager(CharacterControllerManager* cm) { mCctManager = cm; } + + private: + TouchedObject& operator=(const TouchedObject&); + + const T* mTouchedObject; + bool mRegisterDeletionListener; + CharacterControllerManager* mCctManager; + }; + + class SweepTest + { + public: + SweepTest(bool registerDeletionListener); + ~SweepTest(); + + PxControllerCollisionFlags moveCharacter( const InternalCBData_FindTouchedGeom* userData, + InternalCBData_OnHit* user_data2, + SweptVolume& volume, + const PxVec3& direction, + const UserObstacles& userObstacles, + PxF32 min_dist, + const PxControllerFilters& filters, + bool constrainedClimbingMode, + bool standingOnMoving, + const PxRigidActor*& touchedActor, + const PxShape*& touchedShape, + PxU64 contextID); + + bool doSweepTest(const InternalCBData_FindTouchedGeom* userDataTouchedGeom, + InternalCBData_OnHit* userDataOnHit, + const UserObstacles& userObstacles, + SweptVolume& swept_volume, + const PxVec3& direction, const PxVec3& sideVector, PxU32 max_iter, + PxU32* nb_collisions, PxF32 min_dist, const PxControllerFilters& filters, SweepPass sweepPass, + const PxRigidActor*& touchedActor, const PxShape*& touchedShape, PxU64 contextID); + + void findTouchedObstacles(const UserObstacles& userObstacles, const PxExtendedBounds3& world_box); + + void voidTestCache(); + void onRelease(const PxBase& observed); + void updateCachedShapesRegistration(PxU32 startIndex, bool unregister); + + // observer notifications + void onObstacleRemoved(ObstacleHandle index); + void onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance); + void onObstacleAdded(ObstacleHandle index, const PxObstacleContext* context, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance); + + void onOriginShift(const PxVec3& shift); + + Cm::RenderBuffer* mRenderBuffer; + PxU32 mRenderFlags; + TriArray mWorldTriangles; + IntArray mTriangleIndices; + IntArray mGeomStream; + PxExtendedBounds3 mCacheBounds; + PxU32 mCachedTriIndexIndex; + mutable PxU32 mCachedTriIndex[3]; + PxU32 mNbCachedStatic; + PxU32 mNbCachedT; + public: +#ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST + PxVec3 mContactNormalDownPass; +#else + PxVec3 mContactNormalDownPass; + PxVec3 mContactNormalSidePass; + float mTouchedTriMin; + float mTouchedTriMax; + //PxTriangle mTouchedTriangle; +#endif + // + TouchedObject mTouchedShape; // Shape on which the CCT is standing + TouchedObject mTouchedActor; // Actor from touched shape + ObstacleHandle mTouchedObstacleHandle; // Obstacle on which the CCT is standing + PxVec3 mTouchedPos; // Last known position of mTouchedShape/mTouchedObstacle + // PT: TODO: union those + PxVec3 mTouchedPosShape_Local; + PxVec3 mTouchedPosShape_World; + PxVec3 mTouchedPosObstacle_Local; + PxVec3 mTouchedPosObstacle_World; + // + CCTParams mUserParams; + PxF32 mVolumeGrowth; // Must be >1.0f and not too big + PxF32 mContactPointHeight; // UBI + PxU32 mSQTimeStamp; + PxU16 mNbFullUpdates; + PxU16 mNbPartialUpdates; + PxU16 mNbTessellation; + PxU16 mNbIterations; + PxU32 mFlags; + bool mRegisterDeletionListener; + + PX_FORCE_INLINE void resetStats() + { + mNbFullUpdates = 0; + mNbPartialUpdates = 0; + mNbTessellation = 0; + mNbIterations = 0; + } + + void setCctManager(CharacterControllerManager* cm) + { + mCctManager = cm; + mTouchedActor.setCctManager(cm); + mTouchedShape.setCctManager(cm); + } + + private: + void updateTouchedGeoms( const InternalCBData_FindTouchedGeom* userData, const UserObstacles& userObstacles, + const PxExtendedBounds3& worldBox, const PxControllerFilters& filters, const PxVec3& sideVector); + + CharacterControllerManager* mCctManager; + SweepTest(const SweepTest&); + SweepTest& operator=(const SweepTest& ); + }; + + class CCTFilter // PT: internal filter data, could be replaced with PxControllerFilters eventually + { + public: + PX_FORCE_INLINE CCTFilter() : + mFilterData (NULL), + mFilterCallback (NULL), + mStaticShapes (false), + mDynamicShapes (false), + mPreFilter (false), + mPostFilter (false), + mCCTShapes (NULL) + { + } + const PxFilterData* mFilterData; + PxQueryFilterCallback* mFilterCallback; + bool mStaticShapes; + bool mDynamicShapes; + bool mPreFilter; + bool mPostFilter; + Ps::HashSet* mCCTShapes; + }; + + PxU32 getSceneTimestamp(const InternalCBData_FindTouchedGeom* userData); + + void findTouchedGeometry(const InternalCBData_FindTouchedGeom* userData, + const PxExtendedBounds3& world_aabb, + + TriArray& world_triangles, + IntArray& triIndicesArray, + IntArray& geomStream, + + const CCTFilter& filter, + const CCTParams& params, + PxU16& nbTessellation); + + PxU32 shapeHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, PxF32 length); + PxU32 userHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, PxF32 length); + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp new file mode 100644 index 000000000..893849a9c --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp @@ -0,0 +1,1106 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "CctInternalStructs.h" +#include "PxScene.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxBoxGeometry.h" +#include "PxConvexMesh.h" +#include "PxMeshQuery.h" +#include "extensions/PxTriangleMeshExt.h" +#include "PxTriangleMeshGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "CmRenderOutput.h" +#include "GuIntersectionTriangleBox.h" +#include "PsMathUtils.h" +#include "GuSIMDHelpers.h" + +static const bool gVisualizeTouchedTris = false; +static const float gDebugVisOffset = 0.01f; + +using namespace physx; +using namespace Cct; +using namespace Gu; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: HINT: disable gUsePartialUpdates for easier visualization +static void visualizeTouchedTriangles(PxU32 nbTrisToRender, PxU32 startIndex, const PxTriangle* triangles, RenderBuffer* renderBuffer, const PxVec3& offset, const PxVec3& upDirection) +{ + if(!renderBuffer) + return; + + PxVec3 yy = -offset; + yy += upDirection * gDebugVisOffset; // PT: move slightly in the up direction + + for(PxU32 i=0; inbTessellation++; + + // PT: this one is safe, v0/v1/v2 can always be V4Loaded + if(!intersectTriangleBox_Unsafe(tp->cullingBoxCenter, tp->cullingBoxExtents, v0, v1, v2)) + return; + + PxU32 code; + { + const PxVec3 edge0 = v0 - v1; + const PxVec3 edge1 = v1 - v2; + const PxVec3 edge2 = v2 - v0; + const float maxEdgeLength2 = tp->maxEdgeLength2; + const bool split0 = edge0.magnitudeSquared()>maxEdgeLength2; + const bool split1 = edge1.magnitudeSquared()>maxEdgeLength2; + const bool split2 = edge2.magnitudeSquared()>maxEdgeLength2; + code = (PxU32(split2)<<2)|(PxU32(split1)<<1)|PxU32(split0); + } + + // PT: using Vec3p to make sure we can safely V4LoadU these vertices + const Vec3p m0 = (v0 + v1)*0.5f; + const Vec3p m1 = (v1 + v2)*0.5f; + const Vec3p m2 = (v2 + v0)*0.5f; + + switch(code) + { + case 0: // 000: no split + { + tp->worldTriangles->pushBack(PxTriangle(v0, v1, v2)); + tp->triIndicesArray->pushBack(tp->index); + tp->nbNewTris++; + } + break; + case 1: // 001: split edge0 + { + tessellateTriangleRecursive(tp, v0, m0, v2); + tessellateTriangleRecursive(tp, m0, v1, v2); + } + break; + case 2: // 010: split edge1 + { + tessellateTriangleRecursive(tp, v0, v1, m1); + tessellateTriangleRecursive(tp, v0, m1, v2); + } + break; + case 3: // 011: split edge0/edge1 + { + tessellateTriangleRecursive(tp, v0, m0, m1); + tessellateTriangleRecursive(tp, v0, m1, v2); + tessellateTriangleRecursive(tp, m0, v1, m1); + } + break; + case 4: // 100: split edge2 + { + tessellateTriangleRecursive(tp, v0, v1, m2); + tessellateTriangleRecursive(tp, v1, v2, m2); + } + break; + case 5: // 101: split edge0/edge2 + { + tessellateTriangleRecursive(tp, v0, m0, m2); + tessellateTriangleRecursive(tp, m0, v1, m2); + tessellateTriangleRecursive(tp, m2, v1, v2); + } + break; + case 6: // 110: split edge1/edge2 + { + tessellateTriangleRecursive(tp, v0, v1, m1); + tessellateTriangleRecursive(tp, v0, m1, m2); + tessellateTriangleRecursive(tp, m2, m1, v2); + } + break; + case 7: // 111: split edge0/edge1/edge2 + { + tessellateTriangleRecursive(tp, v0, m0, m2); + tessellateTriangleRecursive(tp, m0, v1, m1); + tessellateTriangleRecursive(tp, m2, m1, v2); + tessellateTriangleRecursive(tp, m0, m1, m2); + } + break; + }; +} + +static void tessellateTriangle(PxU32& nbNewTris, const TrianglePadded& tr, PxU32 index, TriArray& worldTriangles, IntArray& triIndicesArray, const PxBounds3& cullingBox, const CCTParams& params, PxU16& nbTessellation) +{ + TessParams tp; + tp.nbNewTris = 0; + tp.index = index; + tp.worldTriangles = &worldTriangles; + tp.triIndicesArray = &triIndicesArray; + tp.cullingBoxCenter = cullingBox.getCenter(); + tp.cullingBoxExtents = cullingBox.getExtents(); + tp.maxEdgeLength2 = params.mMaxEdgeLength2; + tp.nbTessellation = 0; + tessellateTriangleRecursive(&tp, tr.verts[0], tr.verts[1], tr.verts[2]); + + nbNewTris += tp.nbNewTris; + nbTessellation += tp.nbTessellation; +// nbTessellation += PxU16(tp.nbNewTris); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputPlaneToStream(PxShape* planeShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer) +{ + PX_ASSERT(planeShape->getGeometryType() == PxGeometryType::ePLANE); + + const PxF32 length = (tmpBounds.maximum - tmpBounds.minimum).magnitude(); + PxVec3 center = toVec3(origin); + + const PxPlane plane = PxPlaneEquationFromTransform(globalPose); + + PxVec3 right, up; + Ps::computeBasis(plane.n, right, up); + right *= length; + up *= length; + + const PxVec3 p = plane.project(center); + const PxVec3 p0 = p - right + up; + const PxVec3 p1 = p - right - up; + const PxVec3 p2 = p + right - up; + const PxVec3 p3 = p + right + up; + + const PxU32 nbTouchedTris = 2; + + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + + TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = planeShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mNbTris = nbTouchedTris; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(nbTouchedTris); + + triIndicesArray.pushBack(0); + triIndicesArray.pushBack(1); + + TouchedTriangles[0].verts[0] = p0 + offset; + TouchedTriangles[0].verts[1] = p1 + offset; + TouchedTriangles[0].verts[2] = p2 + offset; + + TouchedTriangles[1].verts[0] = p0 + offset; + TouchedTriangles[1].verts[1] = p2 + offset; + TouchedTriangles[1].verts[2] = p3 + offset; + + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); +} + +static void outputSphereToStream(PxShape* sphereShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, const PxExtendedVec3& origin) +{ + PX_ASSERT(sphereShape->getGeometryType() == PxGeometryType::eSPHERE); + PxExtendedSphere WorldSphere; + { + PxSphereGeometry sg; + sphereShape->getSphereGeometry(sg); + + WorldSphere.radius = sg.radius; + WorldSphere.center.x = PxExtended(globalPose.p.x); + WorldSphere.center.y = PxExtended(globalPose.p.y); + WorldSphere.center.z = PxExtended(globalPose.p.z); + } + + TouchedSphere* PX_RESTRICT touchedSphere = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedSphere)/sizeof(PxU32))); + touchedSphere->mType = TouchedGeomType::eSPHERE; + touchedSphere->mTGUserData = sphereShape; + touchedSphere->mActor = actor; + touchedSphere->mOffset = origin; + touchedSphere->mRadius = WorldSphere.radius; + touchedSphere->mCenter.x = float(WorldSphere.center.x - origin.x); + touchedSphere->mCenter.y = float(WorldSphere.center.y - origin.y); + touchedSphere->mCenter.z = float(WorldSphere.center.z - origin.z); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputCapsuleToStream(PxShape* capsuleShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, const PxExtendedVec3& origin) +{ + PX_ASSERT(capsuleShape->getGeometryType() == PxGeometryType::eCAPSULE); + PxExtendedCapsule WorldCapsule; + { + PxCapsuleGeometry cg; + capsuleShape->getCapsuleGeometry(cg); + + PxVec3 p0 = cg.halfHeight * globalPose.q.getBasisVector0(); + PxVec3 p1 = -p0; + p0 += globalPose.p; + p1 += globalPose.p; + + WorldCapsule.radius = cg.radius; + WorldCapsule.p0.x = PxExtended(p0.x); + WorldCapsule.p0.y = PxExtended(p0.y); + WorldCapsule.p0.z = PxExtended(p0.z); + WorldCapsule.p1.x = PxExtended(p1.x); + WorldCapsule.p1.y = PxExtended(p1.y); + WorldCapsule.p1.z = PxExtended(p1.z); + } + + TouchedCapsule* PX_RESTRICT touchedCapsule = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedCapsule)/sizeof(PxU32))); + touchedCapsule->mType = TouchedGeomType::eCAPSULE; + touchedCapsule->mTGUserData = capsuleShape; + touchedCapsule->mActor = actor; + touchedCapsule->mOffset = origin; + touchedCapsule->mRadius = WorldCapsule.radius; + touchedCapsule->mP0.x = float(WorldCapsule.p0.x - origin.x); + touchedCapsule->mP0.y = float(WorldCapsule.p0.y - origin.y); + touchedCapsule->mP0.z = float(WorldCapsule.p0.z - origin.z); + touchedCapsule->mP1.x = float(WorldCapsule.p1.x - origin.x); + touchedCapsule->mP1.y = float(WorldCapsule.p1.y - origin.y); + touchedCapsule->mP1.z = float(WorldCapsule.p1.z - origin.z); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputBoxToStream( PxShape* boxShape, const PxRigidActor* actor, const PxTransform& globalPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, PxU16& nbTessellation) +{ + PX_ASSERT(boxShape->getGeometryType() == PxGeometryType::eBOX); + PxBoxGeometry bg; + boxShape->getBoxGeometry(bg); + + //8 verts in local space. + const PxF32 dx = bg.halfExtents.x; + const PxF32 dy = bg.halfExtents.y; + const PxF32 dz = bg.halfExtents.z; + PxVec3 boxVerts[8]= + { + PxVec3(-dx,-dy,-dz), + PxVec3(+dx,-dy,-dz), + PxVec3(+dx,+dy,-dz), + PxVec3(-dx,+dy,-dz), + PxVec3(-dx,-dy,+dz), + PxVec3(+dx,-dy,+dz), + PxVec3(+dx,+dy,+dz), + PxVec3(-dx,+dy,+dz) + }; + //Transform verts into world space. + const PxVec3 pxOrigin = toVec3(origin); + for(PxU32 i = 0; i < 8; i++) + { + boxVerts[i] = globalPose.transform(boxVerts[i]) - pxOrigin; + } + + //Index of triangles. + const PxU32 boxTris[12][3]= + { + {0,2,1}, + {2,0,3}, //0,1,2,3 + + {3,6,2}, + {6,3,7}, //3,2,6,7 + + {7,5,6}, + {5,7,4}, //7,6,5,4 + + {4,1,5}, + {1,4,0}, //4,5,1,0 + + {0,7,3}, + {7,0,4}, //0,3,7,4 + + {2,5,1}, + {5,2,6} //2,1,5,6 + }; + + TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = boxShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + if(params.mTessellation) + { + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + const PxBounds3 cullingBox = PxBounds3::centerExtents(tmpBounds.getCenter() + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i<12; i++) + { + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + currentTriangle.verts[0] = boxVerts[boxTris[i][0]]; + currentTriangle.verts[1] = boxVerts[boxTris[i][1]]; + currentTriangle.verts[2] = boxVerts[boxTris[i][2]]; + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, PX_INVALID_U32, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + touchedMesh->mNbTris = 12; + + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(12); + + for(PxU32 i=0; i<12; i++) + { + PxTriangle& currentTriangle = TouchedTriangles[i]; + currentTriangle.verts[0] = boxVerts[boxTris[i][0]]; + currentTriangle.verts[1] = boxVerts[boxTris[i][1]]; + currentTriangle.verts[2] = boxVerts[boxTris[i][2]]; + + triIndicesArray.pushBack(PX_INVALID_U32); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PxU32 createInvisibleWalls(const CCTParams& params, const PxTriangle& currentTriangle, TriArray& worldTriangles, IntArray& triIndicesArray) +{ + const PxF32 wallHeight = params.mInvisibleWallHeight; + if(wallHeight==0.0f) + return 0; + + PxU32 nbNewTris = 0; // Number of newly created tris + + const PxVec3& upDirection = params.mUpDirection; + + PxVec3 normal; + currentTriangle.normal(normal); + if(testSlope(normal, upDirection, params.mSlopeLimit)) + { + const PxVec3 upWall = upDirection*wallHeight; + PxVec3 v0p = currentTriangle.verts[0] + upWall; + PxVec3 v1p = currentTriangle.verts[1] + upWall; + PxVec3 v2p = currentTriangle.verts[2] + upWall; + + // Extrude edge 0-1 + PxVec3 faceNormal01; + { + // 0-1-0p + const PxTriangle tri0_1_0p(currentTriangle.verts[0], currentTriangle.verts[1], v0p); + worldTriangles.pushBack(tri0_1_0p); + + // 0p-1-1p + const PxTriangle tri0p_1_1p(v0p, currentTriangle.verts[1], v1p); + worldTriangles.pushBack(tri0p_1_1p); + + tri0p_1_1p.normal(faceNormal01); + } + + // Extrude edge 1-2 + PxVec3 faceNormal12; + { + // 1p-1-2p + const PxTriangle tri1p_1_2p(v1p, currentTriangle.verts[1], v2p); + worldTriangles.pushBack(tri1p_1_2p); + + // 2p-1-2 + const PxTriangle tri2p_1_2(v2p, currentTriangle.verts[1], currentTriangle.verts[2]); + worldTriangles.pushBack(tri2p_1_2); + + tri2p_1_2.normal(faceNormal12); + } + + // Extrude edge 2-0 + PxVec3 faceNormal20; + { + // 0p-2-0 + const PxTriangle tri0p_2_0(v0p, currentTriangle.verts[2], currentTriangle.verts[0]); + worldTriangles.pushBack(tri0p_2_0); + + // 0p-2p-2 + const PxTriangle tri0p_2p_2(v0p, v2p, currentTriangle.verts[2]); + worldTriangles.pushBack(tri0p_2p_2); + + tri0p_2p_2.normal(faceNormal20); + } + + const PxU32 triIndex = PX_INVALID_U32; + for(PxU32 i=0;i<6;i++) + triIndicesArray.pushBack(triIndex); + + nbNewTris += 6; + } + return nbNewTris; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputMeshToStream( PxShape* meshShape, const PxRigidActor* actor, const PxTransform& meshPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer, PxU16& nbTessellation) +{ + PX_ASSERT(meshShape->getGeometryType() == PxGeometryType::eTRIANGLEMESH); + // Do AABB-mesh query + + PxTriangleMeshGeometry triGeom; + meshShape->getTriangleMeshGeometry(triGeom); + + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxTransform boxPose(tmpBounds.getCenter(), PxQuat(PxIdentity)); + + // Collide AABB against current mesh + PxMeshOverlapUtil overlapUtil; + const PxU32 nbTouchedTris = overlapUtil.findOverlap(boxGeom, boxPose, triGeom, meshPose); + + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + + TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = meshShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mNbTris = nbTouchedTris; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + const PxU32* PX_RESTRICT indices = overlapUtil.getResults(); + + if(params.mSlopeLimit!=0.0f) + { + if(!params.mTessellation) + { + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + const PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { + worldTriangles.pushBack(currentTriangle); + + triIndicesArray.pushBack(triangleIndex); + nbCreatedTris++; + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { +/* worldTriangles.pushBack(currentTriangle); + triIndicesArray.pushBack(triangleIndex); + nbCreatedTris++;*/ + + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; +// printf("Tesselate: %d new tris\n", nbNewTris); + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + else + { + if(!params.mTessellation) + { + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(nbTouchedTris); + + // Loop through touched triangles + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + PxTriangle& currentTriangle = *TouchedTriangles++; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + triIndicesArray.pushBack(triangleIndex); + } + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(triGeom, meshPose, triangleIndex, currentTriangle); + + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); +// printf("Tesselate: %d new tris\n", nbNewTris); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputHeightFieldToStream( PxShape* hfShape, const PxRigidActor* actor, const PxTransform& heightfieldPose, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer, PxU16& nbTessellation) +{ + PX_ASSERT(hfShape->getGeometryType() == PxGeometryType::eHEIGHTFIELD); + // Do AABB-mesh query + + PxHeightFieldGeometry hfGeom; + hfShape->getHeightFieldGeometry(hfGeom); + + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxTransform boxPose(tmpBounds.getCenter(), PxQuat(PxIdentity)); + + // Collide AABB against current heightfield + PxMeshOverlapUtil overlapUtil; + const PxU32 nbTouchedTris = overlapUtil.findOverlap(boxGeom, boxPose, hfGeom, heightfieldPose); + + const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); + + TouchedMesh* touchedMesh = reinterpret_cast(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; // ptchernev: seems to work + touchedMesh->mTGUserData = hfShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mNbTris = nbTouchedTris; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + const PxU32* PX_RESTRICT indices = overlapUtil.getResults(); + + if(params.mSlopeLimit!=0.0f) + { + if(!params.mTessellation) + { + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + const PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { + worldTriangles.pushBack(currentTriangle); + + triIndicesArray.pushBack(triangleIndex); + nbCreatedTris++; + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + // Loop through touched triangles + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = createInvisibleWalls(params, currentTriangle, worldTriangles, triIndicesArray); + nbCreatedTris += nbNewTris; + if(!nbNewTris) + { + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; +// printf("Tesselate: %d new tris\n", nbNewTris); + } + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + else + { + if(!params.mTessellation) + { + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(nbTouchedTris); + + // Loop through touched triangles + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + PxTriangle& currentTriangle = *TouchedTriangles++; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + triIndicesArray.pushBack(triangleIndex); + } + } + else + { + const PxBounds3 cullingBox = PxBounds3::centerExtents(boxPose.p + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + for(PxU32 i=0; i < nbTouchedTris; i++) + { + const PxU32 triangleIndex = indices[i]; + + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + PxMeshQuery::getTriangle(hfGeom, heightfieldPose, triangleIndex, currentTriangle); + + currentTriangle.verts[0] += offset; + currentTriangle.verts[1] += offset; + currentTriangle.verts[2] += offset; + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, triangleIndex, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); +// printf("Tesselate: %d new tris\n", nbNewTris); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + } + + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static void outputConvexToStream(PxShape* convexShape, const PxRigidActor* actor, const PxTransform& absPose_, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, + const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, RenderBuffer* renderBuffer, PxU16& nbTessellation) +{ + PX_ASSERT(convexShape->getGeometryType() == PxGeometryType::eCONVEXMESH); + PxConvexMeshGeometry cg; + convexShape->getConvexMeshGeometry(cg); + PX_ASSERT(cg.convexMesh); + + // Do AABB-mesh query + + PxU32* TF; + + // Collide AABB against current mesh + // The overlap function doesn't exist for convexes so let's just dump all tris + PxConvexMesh& cm = *cg.convexMesh; + + // PT: convex triangles are not exposed anymore so we need to access convex polygons & triangulate them + + // PT: TODO: this is copied from "DrawObjects", move this to a shared place. Actually a helper directly in PxConvexMesh would be useful. + PxU32 Nb = 0; + { + const PxU32 nbPolys = cm.getNbPolygons(); + const PxU8* polygons = cm.getIndexBuffer(); + + for(PxU32 i=0;i(PxAlloca(sizeof(PxU32)*Nb*3)); + PxU32* t = TF; + for(PxU32 i=0;i(reserveContainerMemory(geomStream, sizeof(TouchedMesh)/sizeof(PxU32))); + touchedMesh->mType = TouchedGeomType::eMESH; + touchedMesh->mTGUserData = convexShape; + touchedMesh->mActor = actor; + touchedMesh->mOffset = origin; + touchedMesh->mIndexWorldTriangles = worldTriangles.size(); + + const PxVec3* verts = cm.getVertices(); + // Loop through touched triangles + if(params.mTessellation) + { + const PxBoxGeometry boxGeom(tmpBounds.getExtents()); + const PxBounds3 cullingBox = PxBounds3::centerExtents(tmpBounds.getCenter() + offset, boxGeom.halfExtents); + + PxU32 nbCreatedTris = 0; + while(Nb--) + { + // Compute triangle in world space, add to array + TrianglePadded currentTriangle; + + const PxU32 vref0 = *TF++; + const PxU32 vref1 = *TF++; + const PxU32 vref2 = *TF++; + + currentTriangle.verts[0] = MeshOffset + absPose.rotate(verts[vref0]); + currentTriangle.verts[1] = MeshOffset + absPose.rotate(verts[vref1]); + currentTriangle.verts[2] = MeshOffset + absPose.rotate(verts[vref2]); + + PxU32 nbNewTris = 0; + tessellateTriangle(nbNewTris, currentTriangle, PX_INVALID_U32, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); + nbCreatedTris += nbNewTris; + } + touchedMesh->mNbTris = nbCreatedTris; + } + else + { + // Reserve memory for incoming triangles + PxTriangle* TouchedTriangles = worldTriangles.reserve(Nb); + + touchedMesh->mNbTris = Nb; + while(Nb--) + { + // Compute triangle in world space, add to array + PxTriangle& currentTriangle = *TouchedTriangles++; + + const PxU32 vref0 = *TF++; + const PxU32 vref1 = *TF++; + const PxU32 vref2 = *TF++; + + currentTriangle.verts[0] = MeshOffset + absPose.rotate(verts[vref0]); + currentTriangle.verts[1] = MeshOffset + absPose.rotate(verts[vref1]); + currentTriangle.verts[2] = MeshOffset + absPose.rotate(verts[vref2]); + + triIndicesArray.pushBack(PX_INVALID_U32); + } + } + if(gVisualizeTouchedTris) + visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, worldTriangles.begin(), renderBuffer, offset, params.mUpDirection); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxU32 Cct::getSceneTimestamp(const InternalCBData_FindTouchedGeom* userData) +{ + PX_ASSERT(userData); + const PxInternalCBData_FindTouchedGeom* internalData = static_cast(userData); + PxScene* scene = internalData->scene; + return scene->getSceneQueryStaticTimestamp(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Cct::findTouchedGeometry( + const InternalCBData_FindTouchedGeom* userData, + const PxExtendedBounds3& worldBounds, // ### we should also accept other volumes + + TriArray& worldTriangles, + IntArray& triIndicesArray, + IntArray& geomStream, + + const CCTFilter& filter, + const CCTParams& params, + PxU16& nbTessellation) +{ + PX_ASSERT(userData); + + const PxInternalCBData_FindTouchedGeom* internalData = static_cast(userData); + PxScene* scene = internalData->scene; + + PX_PROFILE_ZONE("CharacterController.findTouchedGeometry", PxU64(reinterpret_cast(scene))); + + RenderBuffer* renderBuffer = internalData->renderBuffer; + + PxExtendedVec3 Origin; // Will be TouchedGeom::mOffset + getCenter(worldBounds, Origin); + + // Find touched *boxes* i.e. touched objects' AABBs in the world + // We collide against dynamic shapes too, to get back dynamic boxes/etc + // TODO: add active groups in interface! + PxQueryFlags sqFilterFlags; + if(filter.mStaticShapes) sqFilterFlags |= PxQueryFlag::eSTATIC; + if(filter.mDynamicShapes) sqFilterFlags |= PxQueryFlag::eDYNAMIC; + if(filter.mFilterCallback) + { + if(filter.mPreFilter) + sqFilterFlags |= PxQueryFlag::ePREFILTER; + if(filter.mPostFilter) + sqFilterFlags |= PxQueryFlag::ePOSTFILTER; + } + + // ### this one is dangerous + const PxBounds3 tmpBounds(toVec3(worldBounds.minimum), toVec3(worldBounds.maximum)); // LOSS OF ACCURACY + + // PT: unfortunate conversion forced by the PxGeometry API + const PxVec3 center = tmpBounds.getCenter(); + const PxVec3 extents = tmpBounds.getExtents(); + + const PxU32 size = 100; + PxOverlapHit hits[size]; + + PxQueryFilterData sceneQueryFilterData = filter.mFilterData ? PxQueryFilterData(*filter.mFilterData, sqFilterFlags) : PxQueryFilterData(sqFilterFlags); + + PxOverlapBuffer hitBuffer(hits, size); + sceneQueryFilterData.flags |= PxQueryFlag::eNO_BLOCK; // fix for DE8255 + scene->overlap(PxBoxGeometry(extents), PxTransform(center), hitBuffer, sceneQueryFilterData, filter.mFilterCallback); + PxU32 numberHits = hitBuffer.getNbAnyHits(); + for(PxU32 i = 0; i < numberHits; i++) + { + const PxOverlapHit& hit = hitBuffer.getAnyHit(i); + PxShape* shape = hit.shape; + PxRigidActor* actor = hit.actor; + if(!shape || !actor) + continue; + + // Filtering + + // Discard all CCT shapes, i.e. kinematic actors we created ourselves. We don't need to collide with them since they're surrounded + // by the real CCT volume - and collisions with those are handled elsewhere. + if(internalData->cctShapeHashSet->contains(shape)) + continue; + + // Ubi (EA) : Discarding Triggers : + if(shape->getFlags() & PxShapeFlag::eTRIGGER_SHAPE) + continue; + + // PT: here you might want to disable kinematic objects. + + // Output shape to stream + const PxTransform globalPose = getShapeGlobalPose(*shape, *actor); + + const PxGeometryType::Enum type = shape->getGeometryType(); // ### VIRTUAL! + if(type==PxGeometryType::eSPHERE) outputSphereToStream (shape, actor, globalPose, geomStream, Origin); + else if(type==PxGeometryType::eCAPSULE) outputCapsuleToStream (shape, actor, globalPose, geomStream, Origin); + else if(type==PxGeometryType::eBOX) outputBoxToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, nbTessellation); + else if(type==PxGeometryType::eTRIANGLEMESH) outputMeshToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); + else if(type==PxGeometryType::eHEIGHTFIELD) outputHeightFieldToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); + else if(type==PxGeometryType::eCONVEXMESH) outputConvexToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer, nbTessellation); + else if(type==PxGeometryType::ePLANE) outputPlaneToStream (shape, actor, globalPose, geomStream, worldTriangles, triIndicesArray, Origin, tmpBounds, params, renderBuffer); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "CctCharacterControllerManager.h" +#include "CctObstacleContext.h" +#include "PxControllerBehavior.h" + +// #### hmmm, in the down case, isn't reported length too big ? It contains our artificial up component, +// that might confuse the user + +static void fillCCTHit(PxControllerHit& hit, const SweptContact& contact, const PxVec3& dir, float length, Controller* controller) +{ + hit.controller = controller->getPxController(); + hit.worldPos = contact.mWorldPos; + hit.worldNormal = contact.mWorldNormal; + hit.dir = dir; + hit.length = length; +} + +static const PxU32 defaultBehaviorFlags = 0; + +PxU32 Cct::shapeHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, float length) +{ + Controller* controller = static_cast(userData)->controller; + + PxControllerShapeHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + hit.shape = const_cast(reinterpret_cast(contact.mGeom->mTGUserData)); + hit.actor = const_cast(contact.mGeom->mActor); + hit.triangleIndex = contact.mTriangleIndex; + + if(controller->mReportCallback) + controller->mReportCallback->onShapeHit(hit); + + PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; + return behaviorCB ? behaviorCB->getBehaviorFlags(*hit.shape, *hit.actor) : defaultBehaviorFlags; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxU32 handleObstacleHit(const PxObstacle& touchedObstacle, const ObstacleHandle& obstacleHandle, PxControllerObstacleHit& hit, const PxInternalCBData_OnHit* internalData, Controller* controller) +{ + hit.userData = touchedObstacle.mUserData; + const_cast(internalData)->touchedObstacle = &touchedObstacle; // (*) PT: TODO: revisit + const_cast(internalData)->touchedObstacleHandle = obstacleHandle; + + if(controller->mReportCallback) + controller->mReportCallback->onObstacleHit(hit); + + PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; + return behaviorCB ? behaviorCB->getBehaviorFlags(touchedObstacle) : defaultBehaviorFlags; +} + +PxU32 Cct::userHitCallback(const InternalCBData_OnHit* userData, const SweptContact& contact, const PxVec3& dir, float length) +{ + const PxInternalCBData_OnHit* internalData = static_cast(userData); + Controller* controller = internalData->controller; + + const PxU32 objectCode = PxU32(size_t(contact.mGeom->mTGUserData)); + const UserObjectType type = decodeType(objectCode); + const PxU32 index = decodeIndex(objectCode); + + if(type==USER_OBJECT_CCT) + { + PX_ASSERT(indexgetCctManager()->getNbControllers()); + Controller** controllers = controller->getCctManager()->getControllers(); + Controller* other = controllers[index]; + + PxControllersHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + hit.other = other->getPxController(); + + if(controller->mReportCallback) + controller->mReportCallback->onControllerHit(hit); + + PxControllerBehaviorCallback* behaviorCB = controller->mBehaviorCallback; + return behaviorCB ? behaviorCB->getBehaviorFlags(*hit.other) : defaultBehaviorFlags; + } + else if(type==USER_OBJECT_BOX_OBSTACLE) + { + PX_ASSERT(internalData->obstacles); + PX_ASSERT(indexobstacles->mBoxObstacles.size()); + + PxControllerObstacleHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + const ObstacleContext::InternalBoxObstacle& obstacle = internalData->obstacles->mBoxObstacles[index]; + const PxBoxObstacle& touchedObstacle = obstacle.mData; + return handleObstacleHit(touchedObstacle, obstacle.mHandle , hit, internalData, controller); + } + else if(type==USER_OBJECT_CAPSULE_OBSTACLE) + { + PX_ASSERT(internalData->obstacles); + PX_ASSERT(indexobstacles->mCapsuleObstacles.size()); + + PxControllerObstacleHit hit; + fillCCTHit(hit, contact, dir, length, controller); + + const ObstacleContext::InternalCapsuleObstacle& obstacle = internalData->obstacles->mCapsuleObstacles[index]; + const PxCapsuleObstacle& touchedObstacle = obstacle.mData; + return handleObstacleHit(touchedObstacle, obstacle.mHandle, hit, internalData, controller); + } + else PX_ASSERT(0); + + return defaultBehaviorFlags; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp new file mode 100644 index 000000000..007b50603 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp @@ -0,0 +1,661 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CctCharacterControllerManager.h" +#include "CctBoxController.h" +#include "CctCapsuleController.h" +#include "CctObstacleContext.h" +#include "CmBoxPruning.h" +#include "GuDistanceSegmentSegment.h" +#include "GuDistanceSegmentBox.h" +#include "PsUtilities.h" +#include "PsMathUtils.h" +#include "PxRigidDynamic.h" +#include "PxScene.h" +#include "PxPhysics.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Cct; + +static const PxF32 gMaxOverlapRecover = 4.0f; // PT: TODO: expose this + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +CharacterControllerManager::CharacterControllerManager(PxScene& scene, bool lockingEnabled) : + mScene (scene), + mRenderBuffer (NULL), + mDebugRenderingFlags (0), + mMaxEdgeLength (1.0f), + mTessellation (false), + mOverlapRecovery (true), + mPreciseSweeps (true), + mPreventVerticalSlidingAgainstCeiling (false), + mLockingEnabled (lockingEnabled) +{ + // PT: register ourself as a deletion listener, to be called by the SDK whenever an object is deleted + PxPhysics& physics = scene.getPhysics(); + physics.registerDeletionListener(*this, PxDeletionEventFlag::eUSER_RELEASE); +} + +CharacterControllerManager::~CharacterControllerManager() +{ + if(mRenderBuffer) + { + delete mRenderBuffer; + mRenderBuffer = 0; + } +} + +void CharacterControllerManager::release() +{ + // PT: TODO: use non virtual calls & move to dtor + while(getNbControllers()!= 0) + releaseController(*getController(0)); + + while(getNbObstacleContexts()!= 0) + mObstacleContexts[0]->release(); + + PxPhysics& physics = mScene.getPhysics(); + physics.unregisterDeletionListener(*this); + + delete this; + + Ps::Foundation::decRefCount(); +} + +PxScene& CharacterControllerManager::getScene() const +{ + return mScene; +} + +PxRenderBuffer& CharacterControllerManager::getRenderBuffer() +{ + if(!mRenderBuffer) + mRenderBuffer = PX_NEW(Cm::RenderBuffer); + + return *mRenderBuffer; +} + +void CharacterControllerManager::setDebugRenderingFlags(PxControllerDebugRenderFlags flags) +{ + mDebugRenderingFlags = flags; + + if(!flags) + { + if(mRenderBuffer) + { + delete mRenderBuffer; + mRenderBuffer = 0; + } + } +} + +PxU32 CharacterControllerManager::getNbControllers() const +{ + return mControllers.size(); +} + +Controller** CharacterControllerManager::getControllers() +{ + return mControllers.begin(); +} + +PxController* CharacterControllerManager::getController(PxU32 index) +{ + if(index>=mControllers.size()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxControllerManager::getController(): out-of-range index"); + return NULL; + } + + PX_ASSERT(mControllers[index]); + return mControllers[index]->getPxController(); +} + +PxController* CharacterControllerManager::createController(const PxControllerDesc& desc) +{ + if(!desc.isValid()) + return NULL; + + Controller* newController = NULL; + + PxController* N = NULL; + if(desc.getType()==PxControllerShapeType::eBOX) + { + BoxController* boxController = PX_NEW(BoxController)(desc, mScene.getPhysics(), &mScene); + newController = boxController; + N = boxController; + } + else if(desc.getType()==PxControllerShapeType::eCAPSULE) + { + CapsuleController* capsuleController = PX_NEW(CapsuleController)(desc, mScene.getPhysics(), &mScene); + newController = capsuleController; + N = capsuleController; + } + else PX_ALWAYS_ASSERT_MESSAGE( "INTERNAL ERROR - invalid CCT type, should have been caught by isValid()."); + + if(newController) + { + mControllers.pushBack(newController); + newController->setCctManager(this); + + PxShape* shape = NULL; + PxU32 nb = N->getActor()->getShapes(&shape, 1); + PX_ASSERT(nb==1); + PX_UNUSED(nb); + mCCTShapes.insert(shape); + } + + return N; +} + +void CharacterControllerManager::releaseController(PxController& controller) +{ + for(PxU32 i = 0; igetPxController() == &controller) + { + mControllers.replaceWithLast(i); + break; + } + } + + PxShape* shape = NULL; + PxU32 nb = controller.getActor()->getShapes(&shape, 1); + PX_ASSERT(nb==1); + PX_UNUSED(nb); + mCCTShapes.erase(shape); + + if(controller.getType() == PxControllerShapeType::eCAPSULE) + { + CapsuleController* cc = static_cast(&controller); + PX_DELETE(cc); + } + else if(controller.getType() == PxControllerShapeType::eBOX) + { + BoxController* bc = static_cast(&controller); + PX_DELETE(bc); + } + else PX_ASSERT(0); +} + +void CharacterControllerManager::purgeControllers() +{ + while(mControllers.size()) + releaseController(*mControllers[0]->getPxController()); +} + +void CharacterControllerManager::onRelease(const PxBase* observed, void* , PxDeletionEventFlag::Enum deletionEvent) +{ + PX_ASSERT(deletionEvent == PxDeletionEventFlag::eUSER_RELEASE); // the only type we registered for + PX_UNUSED(deletionEvent); + + if(!(observed->getConcreteType()==PxConcreteType:: eRIGID_DYNAMIC || observed->getConcreteType()==PxConcreteType:: eRIGID_STATIC || + observed->getConcreteType()==PxConcreteType::eSHAPE)) + return; + + // check if object was registered + if(mLockingEnabled) + mWriteLock.lock(); + + const ObservedRefCountMap::Entry* releaseEntry = mObservedRefCountMap.find(observed); + if(mLockingEnabled) + mWriteLock.unlock(); + + if(releaseEntry) + { + for (PxU32 i = 0; i < mControllers.size(); i++) + { + Controller* controller = mControllers[i]; + if(mLockingEnabled) + controller->mWriteLock.lock(); + + controller->onRelease(*observed); + + if(mLockingEnabled) + controller->mWriteLock.unlock(); + } + } +} + +void CharacterControllerManager::registerObservedObject(const PxBase* obj) +{ + if(mLockingEnabled) + mWriteLock.lock(); + + mObservedRefCountMap[obj].refCount++; + + if(mLockingEnabled) + mWriteLock.unlock(); +} + +void CharacterControllerManager::unregisterObservedObject(const PxBase* obj) +{ + if(mLockingEnabled) + mWriteLock.lock(); + + ObservedRefCounter& refCounter = mObservedRefCountMap[obj]; + PX_ASSERT(refCounter.refCount); + refCounter.refCount--; + if(!refCounter.refCount) + mObservedRefCountMap.erase(obj); + + if(mLockingEnabled) + mWriteLock.unlock(); +} + +PxU32 CharacterControllerManager::getNbObstacleContexts() const +{ + return mObstacleContexts.size(); +} + +PxObstacleContext* CharacterControllerManager::getObstacleContext(PxU32 index) +{ + if(index>=mObstacleContexts.size()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxControllerManager::getObstacleContext(): out-of-range index"); + return NULL; + } + + PX_ASSERT(mObstacleContexts[index]); + return mObstacleContexts[index]; +} + +PxObstacleContext* CharacterControllerManager::createObstacleContext() +{ + ObstacleContext* oc = PX_NEW(ObstacleContext)(*this); + + mObstacleContexts.pushBack(oc); + + return oc; +} + +void CharacterControllerManager::releaseObstacleContext(ObstacleContext& oc) +{ + PX_ASSERT(mObstacleContexts.find(&oc) != mObstacleContexts.end()); + mObstacleContexts.findAndReplaceWithLast(&oc); + + PX_DELETE(&oc); +} + +void CharacterControllerManager::onObstacleRemoved(ObstacleHandle index) const +{ + for(PxU32 i = 0; imCctModule.onObstacleRemoved(index); + } +} + +void CharacterControllerManager::onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* context) const +{ + for(PxU32 i = 0; imCctModule.onObstacleUpdated(index,context, toVec3(mControllers[i]->mPosition), -mControllers[i]->mUserParams.mUpDirection, mControllers[i]->getHalfHeightInternal()); + } +} + +void CharacterControllerManager::onObstacleAdded(ObstacleHandle index, const PxObstacleContext* context) const +{ + for(PxU32 i = 0; imCctModule.onObstacleAdded(index,context, toVec3(mControllers[i]->mPosition), -mControllers[i]->mUserParams.mUpDirection, mControllers[i]->getHalfHeightInternal()); + } +} + + +// PT: TODO: move to array class? +template +void resetOrClear(T& a) +{ + const PxU32 c = a.capacity(); + if(!c) + return; + const PxU32 s = a.size(); + if(s>c/2) + a.clear(); + else + a.reset(); +} + +void CharacterControllerManager::resetObstaclesBuffers() +{ + resetOrClear(mBoxUserData); + resetOrClear(mBoxes); + resetOrClear(mCapsuleUserData); + resetOrClear(mCapsules); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CharacterControllerManager::setTessellation(bool flag, float maxEdgeLength) +{ + mTessellation = flag; + mMaxEdgeLength = maxEdgeLength; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CharacterControllerManager::setOverlapRecoveryModule(bool flag) +{ + mOverlapRecovery = flag; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CharacterControllerManager::setPreciseSweeps(bool flag) +{ + mPreciseSweeps = flag; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CharacterControllerManager::setPreventVerticalSlidingAgainstCeiling(bool flag) +{ + mPreventVerticalSlidingAgainstCeiling = flag; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CharacterControllerManager::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i=0; i < mControllers.size(); i++) + { + mControllers[i]->onOriginShift(shift); + } + + for(PxU32 i=0; i < mObstacleContexts.size(); i++) + { + mObstacleContexts[i]->onOriginShift(shift); + } + + if (mRenderBuffer) + mRenderBuffer->shift(-shift); + + // assumption is that these are just used for temporary stuff + PX_ASSERT(!mBoxes.size()); + PX_ASSERT(!mCapsules.size()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool computeMTD(PxVec3& mtd, PxF32& depth, const PxVec3& e0, const PxVec3& c0, const PxMat33& r0, const PxVec3& e1, const PxVec3& c1, const PxMat33& r1) +{ + // Translation, in parent frame + const PxVec3 v = c1 - c0; + // Translation, in A's frame + const PxVec3 T(v.dot(r0[0]), v.dot(r0[1]), v.dot(r0[2])); + + // B's basis with respect to A's local frame + PxReal R[3][3]; + PxReal FR[3][3]; + PxReal ra, rb, t, d; + + PxReal overlap[15]; + + // Calculate rotation matrix + for(PxU32 i=0;i<3;i++) + { + for(PxU32 k=0;k<3;k++) + { + R[i][k] = r0[i].dot(r1[k]); + FR[i][k] = 1e-6f + PxAbs(R[i][k]); // Precompute fabs matrix + } + } + + // A's basis vectors + for(PxU32 i=0;i<3;i++) + { + ra = e0[i]; + rb = e1[0]*FR[i][0] + e1[1]*FR[i][1] + e1[2]*FR[i][2]; + t = PxAbs(T[i]); + + d = ra + rb - t; + if(d<0.0f) + return false; + + overlap[i] = d; + } + + // B's basis vectors + for(PxU32 k=0;k<3;k++) + { + ra = e0[0]*FR[0][k] + e0[1]*FR[1][k] + e0[2]*FR[2][k]; + rb = e1[k]; + t = PxAbs(T[0]*R[0][k] + T[1]*R[1][k] + T[2]*R[2][k]); + + d = ra + rb - t; + if(d<0.0f) + return false; + + overlap[k+3] = d; + } + + // PT: edge-edge tests are skipped, by design + + PxU32 minIndex=0; + PxReal minD = overlap[0]; + for(PxU32 i=1;i<6;i++) + { + if(overlap[i]mType>entity1->mType) + Ps::swap(entity0, entity1); + + if(entity0->mType==PxControllerShapeType::eCAPSULE && entity1->mType==PxControllerShapeType::eCAPSULE) + { + CapsuleController* cc0 = static_cast(entity0); + CapsuleController* cc1 = static_cast(entity1); + + PxExtendedCapsule capsule0; + cc0->getCapsule(capsule0); + + PxExtendedCapsule capsule1; + cc1->getCapsule(capsule1); + + const PxF32 r = capsule0.radius + capsule1.radius; + + const PxVec3 p00 = toVec3(capsule0.p0); + const PxVec3 p01 = toVec3(capsule0.p1); + const PxVec3 p10 = toVec3(capsule1.p0); + const PxVec3 p11 = toVec3(capsule1.p1); + + PxF32 s,t; + const PxF32 d = sqrtf(Gu::distanceSegmentSegmentSquared(p00, p01 - p00, p10, p11 - p10, &s, &t)); + if(dmCctModule.mUserParams.mUpDirection; + dir = fixDir(center0 - center1, up); + overlap = r - d; + } + } + else if(entity0->mType==PxControllerShapeType::eBOX && entity1->mType==PxControllerShapeType::eCAPSULE) + { + BoxController* cc0 = static_cast(entity0); + CapsuleController* cc1 = static_cast(entity1); + + PxExtendedBox obb; + cc0->getOBB(obb); + + PxExtendedCapsule capsule; + cc1->getCapsule(capsule); + const PxVec3 p0 = toVec3(capsule.p0); + const PxVec3 p1 = toVec3(capsule.p1); + + PxF32 t; + PxVec3 p; + const PxMat33 M(obb.rot); + const PxVec3 boxCenter = toVec3(obb.center); + const PxF32 d = sqrtf(Gu::distanceSegmentBoxSquared(p0, p1, boxCenter, obb.extents, M, &t, &p)); + if(dmCctModule.mUserParams.mUpDirection; + dir = fixDir(center0 - center1, up); + overlap = capsule.radius - d; + } + } + else + { + PX_ASSERT(entity0->mType==PxControllerShapeType::eBOX); + PX_ASSERT(entity1->mType==PxControllerShapeType::eBOX); + BoxController* cc0 = static_cast(entity0); + BoxController* cc1 = static_cast(entity1); + + PxExtendedBox obb0; + cc0->getOBB(obb0); + + PxExtendedBox obb1; + cc1->getOBB(obb1); + + PxVec3 mtd; + PxF32 depth; + if(computeMTD( mtd, depth, + obb0.extents, toVec3(obb0.center), PxMat33(obb0.rot), + obb1.extents, toVec3(obb1.center), PxMat33(obb1.rot))) + { + const PxVec3 center0 = toVec3(obb0.center); + const PxVec3 center1 = toVec3(obb1.center); + const PxVec3 witness = center0 - center1; + if(mtd.dot(witness)<0.0f) + dir = -mtd; + else + dir = mtd; + const PxVec3 up = entity0->mCctModule.mUserParams.mUpDirection; + dir = fixDir(dir, up); + overlap = depth; + } + } + + if(overlap!=0.0f) + { + // We want to limit this to some reasonable amount, to avoid obvious "popping". + const PxF32 maxOverlap = gMaxOverlapRecover * elapsedTime; + if(overlap>maxOverlap) + overlap=maxOverlap; + + const PxVec3 sep = dir * overlap * 0.5f; + entity0->mOverlapRecover += sep; + entity1->mOverlapRecover -= sep; + } +} + +void CharacterControllerManager::computeInteractions(PxF32 elapsedTime, PxControllerFilterCallback* cctFilterCb) +{ + PxU32 nbControllers = mControllers.size(); + Controller** controllers = mControllers.begin(); + + PxBounds3* boxes = reinterpret_cast(PX_ALLOC_TEMP(sizeof(PxBounds3)*nbControllers, "CharacterControllerManager::computeInteractions")); // PT: TODO: get rid of alloc + PxBounds3* runningBoxes = boxes; + + while(nbControllers--) + { + Controller* current = *controllers++; + + PxExtendedBounds3 extBox; + current->getWorldBox(extBox); + + *runningBoxes++ = PxBounds3(toVec3(extBox.minimum), toVec3(extBox.maximum)); // ### LOSS OF ACCURACY + } + + // + + const PxU32 nbEntities = PxU32(runningBoxes - boxes); + + Ps::Array pairs; // PT: TODO: get rid of alloc + Cm::CompleteBoxPruning(boxes, nbEntities, pairs, Gu::Axes(physx::Gu::AXES_XZY)); // PT: TODO: revisit for variable up axis + + PxU32 nbPairs = pairs.size()>>1; + const PxU32* indices = pairs.begin(); + while(nbPairs--) + { + const PxU32 index0 = *indices++; + const PxU32 index1 = *indices++; + Controller* ctrl0 = mControllers[index0]; + Controller* ctrl1 = mControllers[index1]; + + bool keep=true; + if(cctFilterCb) + keep = cctFilterCb->filter(*ctrl0->getPxController(), *ctrl1->getPxController()); + + if(keep) + InteractionCharacterCharacter(ctrl0, ctrl1, elapsedTime); + } + + PX_FREE(boxes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//Public factory methods + +PX_C_EXPORT PX_PHYSX_CHARACTER_API PxControllerManager* PX_CALL_CONV PxCreateControllerManager(PxScene& scene, bool lockingEnabled) +{ + Ps::Foundation::incRefCount(); + return PX_NEW(CharacterControllerManager)(scene, lockingEnabled); +} diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h new file mode 100644 index 000000000..94a64dfeb --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h @@ -0,0 +1,147 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_CHARACTER_CONTROLLER_MANAGER +#define CCT_CHARACTER_CONTROLLER_MANAGER + +//Exclude file from docs +/** \cond */ + +#include "PxControllerManager.h" +#include "PxControllerObstacles.h" +#include "PxMeshQuery.h" +#include "PxDeletionListener.h" +#include "CmRenderOutput.h" +#include "CctUtils.h" +#include "PsHashSet.h" +#include "PsHashMap.h" +#include "PsMutex.h" + +namespace physx +{ +namespace Cct +{ + class Controller; + class ObstacleContext; + + struct ObservedRefCounter + { + ObservedRefCounter(): refCount(0) + { + } + + PxU32 refCount; + }; + + typedef Ps::HashMap ObservedRefCountMap; + + //Implements the PxControllerManager interface, this class used to be called ControllerManager + class CharacterControllerManager : public PxControllerManager , public Ps::UserAllocated, public PxDeletionListener + { + public: + CharacterControllerManager(PxScene& scene, bool lockingEnabled = false); + virtual ~CharacterControllerManager(); + + // PxControllerManager + virtual void release(); + virtual PxScene& getScene() const; + virtual PxU32 getNbControllers() const; + virtual PxController* getController(PxU32 index); + virtual PxController* createController(const PxControllerDesc& desc); + + virtual void purgeControllers(); + virtual PxRenderBuffer& getRenderBuffer(); + virtual void setDebugRenderingFlags(PxControllerDebugRenderFlags flags); + virtual PxU32 getNbObstacleContexts() const; + virtual PxObstacleContext* getObstacleContext(PxU32 index); + virtual PxObstacleContext* createObstacleContext(); + virtual void computeInteractions(PxF32 elapsedTime, PxControllerFilterCallback* cctFilterCb); + virtual void setTessellation(bool flag, float maxEdgeLength); + virtual void setOverlapRecoveryModule(bool flag); + virtual void setPreciseSweeps(bool flag); + virtual void setPreventVerticalSlidingAgainstCeiling(bool flag); + virtual void shiftOrigin(const PxVec3& shift); + //~PxControllerManager + + // PxDeletionListener + virtual void onRelease(const PxBase* observed, void* userData, PxDeletionEventFlag::Enum deletionEvent); + //~PxDeletionListener + void registerObservedObject(const PxBase* obj); + void unregisterObservedObject(const PxBase* obj); + + // ObstacleContextNotifications + void onObstacleRemoved(ObstacleHandle index) const; + void onObstacleUpdated(ObstacleHandle index, const PxObstacleContext* ) const; + void onObstacleAdded(ObstacleHandle index, const PxObstacleContext*) const; + + void releaseController(PxController& controller); + Controller** getControllers(); + void releaseObstacleContext(ObstacleContext& oc); + void resetObstaclesBuffers(); + + PxScene& mScene; + + Cm::RenderBuffer* mRenderBuffer; + PxControllerDebugRenderFlags mDebugRenderingFlags; + // Shared buffers for obstacles + Ps::Array mBoxUserData; + Ps::Array mBoxes; + + Ps::Array mCapsuleUserData; + Ps::Array mCapsules; + + Ps::Array mControllers; + Ps::HashSet mCCTShapes; + + Ps::Array mObstacleContexts; + + float mMaxEdgeLength; + bool mTessellation; + + bool mOverlapRecovery; + bool mPreciseSweeps; + bool mPreventVerticalSlidingAgainstCeiling; + + bool mLockingEnabled; + + protected: + CharacterControllerManager &operator=(const CharacterControllerManager &); + CharacterControllerManager(const CharacterControllerManager& ); + + private: + ObservedRefCountMap mObservedRefCountMap; + mutable Ps::Mutex mWriteLock; // Lock used for guarding pointers in observedrefcountmap + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif //CCT_CHARACTER_CONTROLLER_MANAGER diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp new file mode 100644 index 000000000..96cced109 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp @@ -0,0 +1,235 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxController.h" +#include "CctController.h" +#include "CctBoxController.h" +#include "CctCharacterControllerManager.h" +#include "PxScene.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" +#include "extensions/PxRigidBodyExt.h" +#include "foundation/PxMathUtils.h" +#include "PsUtilities.h" +#include "PxPhysics.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Cct; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Controller::Controller(const PxControllerDesc& desc, PxScene* s) : + mCctModule (desc.registerDeletionListener), + mScene (s), + mPreviousSceneTimestamp (0xffffffff), + mGlobalTime (0.0), + mPreviousGlobalTime (0.0), + mProxyDensity (0.0f), + mProxyScaleCoeff (0.0f), + mCollisionFlags (0), + mCachedStandingOnMoving (false), + mManager (NULL) +{ + mType = PxControllerShapeType::eFORCE_DWORD; + + mUserParams.mNonWalkableMode = desc.nonWalkableMode; + mUserParams.mSlopeLimit = desc.slopeLimit; + mUserParams.mContactOffset = desc.contactOffset; + mUserParams.mStepOffset = desc.stepOffset; + mUserParams.mInvisibleWallHeight = desc.invisibleWallHeight; + mUserParams.mMaxJumpHeight = desc.maxJumpHeight; + mUserParams.mHandleSlope = desc.slopeLimit!=0.0f; + + mReportCallback = desc.reportCallback; + mBehaviorCallback = desc.behaviorCallback; + mUserData = desc.userData; + + mKineActor = NULL; + mPosition = desc.position; + mProxyDensity = desc.density; + mProxyScaleCoeff = desc.scaleCoeff; + + mCctModule.mVolumeGrowth = desc.volumeGrowth; + + mRegisterDeletionListener = desc.registerDeletionListener; + + mDeltaXP = PxVec3(0); + mOverlapRecover = PxVec3(0); + + mUserParams.mUpDirection = PxVec3(0.0f); + setUpDirectionInternal(desc.upDirection); +} + +Controller::~Controller() +{ + if(mScene) + { + if(mKineActor) + mKineActor->release(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::onRelease(const PxBase& observed) +{ + mCctModule.onRelease(observed); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::onOriginShift(const PxVec3& shift) +{ + mPosition -= shift; + + if(mManager && mManager->mLockingEnabled) + mWriteLock.lock(); + + mCctModule.onOriginShift(shift); + + if(mManager && mManager->mLockingEnabled) + mWriteLock.unlock(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::setUpDirectionInternal(const PxVec3& up) +{ + PX_CHECK_MSG(up.isNormalized(), "CCT: up direction must be normalized"); + + if(mUserParams.mUpDirection==up) + return; + + const PxQuat q = PxShortestRotation(PxVec3(1.0f, 0.0f, 0.0f), up); + + mUserParams.mQuatFromUp = q; + mUserParams.mUpDirection = up; + + // Update kinematic actor + /*if(mKineActor) + { + PxTransform pose = mKineActor->getGlobalPose(); + pose.q = q; + mKineActor->setGlobalPose(pose); + }*/ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::releaseInternal() +{ + mManager->releaseController(*getPxController()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::getInternalState(PxControllerState& state) const +{ + if(mManager->mLockingEnabled) + mWriteLock.lock(); + + state.deltaXP = mDeltaXP; + state.touchedShape = const_cast(mCctModule.mTouchedShape.get()); + state.touchedActor = const_cast(mCctModule.mTouchedActor.get()); + state.touchedObstacleHandle = mCctModule.mTouchedObstacleHandle; + state.standOnAnotherCCT = (mCctModule.mFlags & STF_TOUCH_OTHER_CCT)!=0; + state.standOnObstacle = (mCctModule.mFlags & STF_TOUCH_OBSTACLE)!=0; + state.isMovingUp = (mCctModule.mFlags & STF_IS_MOVING_UP)!=0; + state.collisionFlags = mCollisionFlags; + + if(mManager->mLockingEnabled) + mWriteLock.unlock(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Controller::getInternalStats(PxControllerStats& stats) const +{ + stats.nbFullUpdates = mCctModule.mNbFullUpdates; + stats.nbPartialUpdates = mCctModule.mNbPartialUpdates; + stats.nbIterations = mCctModule.mNbIterations; + stats.nbTessellation = mCctModule.mNbTessellation; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Controller::setPos(const PxExtendedVec3& pos) +{ + mPosition = pos; + + // Update kinematic actor + if(mKineActor) + { + PxTransform targetPose = mKineActor->getGlobalPose(); + targetPose.p = toVec3(mPosition); // LOSS OF ACCURACY + targetPose.q = mUserParams.mQuatFromUp; + mKineActor->setKinematicTarget(targetPose); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Controller::createProxyActor(PxPhysics& sdk, const PxGeometry& geometry, const PxMaterial& material) +{ + // PT: we don't disable raycasting or CD because: + // - raycasting is needed for visibility queries (the SDK otherwise doesn't know about the CCTS) + // - collision is needed because the only reason we create actors there is to handle collisions with dynamic shapes + // So it's actually wrong to disable any of those. + + PxTransform globalPose; + globalPose.p = toVec3(mPosition); // LOSS OF ACCURACY + globalPose.q = mUserParams.mQuatFromUp; + + mKineActor = sdk.createRigidDynamic(globalPose); + if(!mKineActor) + return false; + + PxShape* shape = sdk.createShape(geometry, material, true); + mKineActor->attachShape(*shape); + shape->release(); + mKineActor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true); + + PxRigidBodyExt::updateMassAndInertia(*mKineActor, mProxyDensity); + mScene->addActor(*mKineActor); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxShape* Controller::getKineShape() const +{ + // PT: TODO: cache this and avoid the virtual call + PxShape* shape = NULL; + PxU32 nb = mKineActor->getShapes(&shape, 1); + PX_ASSERT(nb==1); + PX_UNUSED(nb); + return shape; +} diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h new file mode 100644 index 000000000..ab6a4c539 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_CONTROLLER +#define CCT_CONTROLLER + +/* Exclude from documentation */ +/** \cond */ + +#include "CctCharacterController.h" +#include "PsUserAllocated.h" +#include "PsMutex.h" + +namespace physx +{ + +class PxPhysics; +class PxScene; +class PxRigidDynamic; +class PxGeometry; +class PxMaterial; + +namespace Cct +{ + class CharacterControllerManager; + + class Controller : public Ps::UserAllocated + { + PX_NOCOPY(Controller) + public: + Controller(const PxControllerDesc& desc, PxScene* scene); + virtual ~Controller(); + + void releaseInternal(); + void getInternalState(PxControllerState& state) const; + void getInternalStats(PxControllerStats& stats) const; + + virtual PxF32 getHalfHeightInternal() const = 0; + virtual bool getWorldBox(PxExtendedBounds3& box) const = 0; + virtual PxController* getPxController() = 0; + + void onOriginShift(const PxVec3& shift); + + void onRelease(const PxBase& observed); + + void setCctManager(CharacterControllerManager* cm) + { + mManager = cm; + mCctModule.setCctManager(cm); + } + + PX_FORCE_INLINE CharacterControllerManager* getCctManager() { return mManager; } + PX_FORCE_INLINE PxU64 getContextId() const { return PxU64(reinterpret_cast(mScene)); } + + PxControllerShapeType::Enum mType; + // User params + CCTParams mUserParams; + PxUserControllerHitReport* mReportCallback; + PxControllerBehaviorCallback* mBehaviorCallback; + void* mUserData; + // Internal data + SweepTest mCctModule; // Internal CCT object. Optim test for Ubi. + PxRigidDynamic* mKineActor; // Associated kinematic actor + PxExtendedVec3 mPosition; // Current position + PxVec3 mDeltaXP; + PxVec3 mOverlapRecover; + PxScene* mScene; // Handy scene owner + PxU32 mPreviousSceneTimestamp; + PxF64 mGlobalTime; + PxF64 mPreviousGlobalTime; + PxF32 mProxyDensity; // Density for proxy actor + PxF32 mProxyScaleCoeff; // Scale coeff for proxy actor + PxControllerCollisionFlags mCollisionFlags; // Last known collision flags (PxControllerCollisionFlag) + bool mCachedStandingOnMoving; + bool mRegisterDeletionListener; + mutable Ps::Mutex mWriteLock; // Lock used for guarding touched pointers and cache data from overwriting + // during onRelease call. + protected: + // Internal methods + void setUpDirectionInternal(const PxVec3& up); + PxShape* getKineShape() const; + bool createProxyActor(PxPhysics& sdk, const PxGeometry& geometry, const PxMaterial& material); + bool setPos(const PxExtendedVec3& pos); + void findTouchedObject(const PxControllerFilters& filters, const PxObstacleContext* obstacleContext, const PxVec3& upDirection); + bool rideOnTouchedObject(SweptVolume& volume, const PxVec3& upDirection, PxVec3& disp, const PxObstacleContext* obstacleContext); + PxControllerCollisionFlags move(SweptVolume& volume, const PxVec3& disp, PxF32 minDist, PxF32 elapsedTime, const PxControllerFilters& filters, const PxObstacleContext* obstacles, bool constrainedClimbingMode); + bool filterTouchedShape(const PxControllerFilters& filters); + + PX_FORCE_INLINE float computeTimeCoeff() + { + const float elapsedTime = float(mGlobalTime - mPreviousGlobalTime); + mPreviousGlobalTime = mGlobalTime; + return 1.0f / elapsedTime; + } + + CharacterControllerManager* mManager; // Owner manager + }; + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h new file mode 100644 index 000000000..71452aa23 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h @@ -0,0 +1,86 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CHARACTER_INTERNAL_STRUCTS_H +#define PX_CHARACTER_INTERNAL_STRUCTS_H + +#include "CctController.h" + +namespace physx +{ + class PxObstacle; // (*) + +namespace Cct +{ + class ObstacleContext; + + enum UserObjectType + { + USER_OBJECT_CCT = 0, + USER_OBJECT_BOX_OBSTACLE = 1, + USER_OBJECT_CAPSULE_OBSTACLE = 2 + }; + + PX_FORCE_INLINE PxU32 encodeUserObject(PxU32 index, UserObjectType type) + { + PX_ASSERT(index<=0xffff); + PX_ASSERT(PxU32(type)<=0xffff); + return (PxU16(index)<<16)|PxU32(type); + } + + PX_FORCE_INLINE UserObjectType decodeType(PxU32 code) + { + return UserObjectType(code & 0xffff); + } + + PX_FORCE_INLINE PxU32 decodeIndex(PxU32 code) + { + return code>>16; + } + + struct PxInternalCBData_OnHit : InternalCBData_OnHit + { + Controller* controller; + const ObstacleContext* obstacles; + const PxObstacle* touchedObstacle; + ObstacleHandle touchedObstacleHandle; + }; + + struct PxInternalCBData_FindTouchedGeom : InternalCBData_FindTouchedGeom + { + PxScene* scene; + Cm::RenderBuffer* renderBuffer; // Render buffer from controller manager, not the one from the scene + + Ps::HashSet* cctShapeHashSet; + }; +} +} + +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp new file mode 100644 index 000000000..36575cd79 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp @@ -0,0 +1,538 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "CctObstacleContext.h" +#include "CctCharacterControllerManager.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Cct; + +//! Initial list size +#define DEFAULT_HANDLEMANAGER_SIZE 2 + +HandleManager::HandleManager() : mCurrentNbObjects(0), mNbFreeIndices(0) +{ + mMaxNbObjects = DEFAULT_HANDLEMANAGER_SIZE; + mObjects = reinterpret_cast(PX_ALLOC(sizeof(void*)*mMaxNbObjects, "HandleManager")); + mOutToIn = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); + mInToOut = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); + mStamps = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); + PxMemSet(mOutToIn, 0xff, mMaxNbObjects*sizeof(PxU16)); + PxMemSet(mInToOut, 0xff, mMaxNbObjects*sizeof(PxU16)); + PxMemZero(mStamps, mMaxNbObjects*sizeof(PxU16)); +} + +HandleManager::~HandleManager() +{ + SetupLists(); +} + +bool HandleManager::SetupLists(void** objects, PxU16* oti, PxU16* ito, PxU16* stamps) +{ + // Release old data + PX_FREE_AND_RESET(mStamps); + PX_FREE_AND_RESET(mInToOut); + PX_FREE_AND_RESET(mOutToIn); + PX_FREE_AND_RESET(mObjects); + // Assign new data + mObjects = objects; + mOutToIn = oti; + mInToOut = ito; + mStamps = stamps; + return true; +} + +Handle HandleManager::Add(void* object) +{ + // Are there any free indices I should recycle? + if(mNbFreeIndices) + { + const PxU16 FreeIndex = mInToOut[mCurrentNbObjects];// Get the recycled virtual index + mObjects[mCurrentNbObjects] = object; // The physical location is always at the end of the list (it never has holes). + mOutToIn[FreeIndex] = Ps::to16(mCurrentNbObjects++); // Update virtual-to-physical remapping table. + mNbFreeIndices--; + return Handle((mStamps[FreeIndex]<<16)|FreeIndex); // Return virtual index (handle) to the client app + } + else + { + PX_ASSERT_WITH_MESSAGE(mCurrentNbObjects<0xffff ,"Internal error - 64K objects in HandleManager!"); + + // Is the array large enough for another entry? + if(mCurrentNbObjects==mMaxNbObjects) + { + // Nope! Resize all arrays (could be avoided with linked lists... one day) + mMaxNbObjects<<=1; // The more you eat, the more you're given + if(mMaxNbObjects>0xffff) mMaxNbObjects = 0xffff; // Clamp to 64K + void** NewList = reinterpret_cast(PX_ALLOC(sizeof(void*)*mMaxNbObjects, "HandleManager")); // New physical list + PxU16* NewOTI = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New remapping table + PxU16* NewITO = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New remapping table + PxU16* NewStamps = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New stamps + PxMemCopy(NewList, mObjects, mCurrentNbObjects*sizeof(void*)); // Copy old data + PxMemCopy(NewOTI, mOutToIn, mCurrentNbObjects*sizeof(PxU16)); // Copy old data + PxMemCopy(NewITO, mInToOut, mCurrentNbObjects*sizeof(PxU16)); // Copy old data + PxMemCopy(NewStamps, mStamps, mCurrentNbObjects*sizeof(PxU16)); // Copy old data + PxMemSet(NewOTI+mCurrentNbObjects, 0xff, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16)); + PxMemSet(NewITO+mCurrentNbObjects, 0xff, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16)); + PxMemZero(NewStamps+mCurrentNbObjects, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16)); + SetupLists(NewList, NewOTI, NewITO, NewStamps); + } + + mObjects[mCurrentNbObjects] = object; // Store object at mCurrentNbObjects = physical index = virtual index + mOutToIn[mCurrentNbObjects] = Ps::to16(mCurrentNbObjects); // Update virtual-to-physical remapping table. + mInToOut[mCurrentNbObjects] = Ps::to16(mCurrentNbObjects); // Update physical-to-virtual remapping table. + PxU32 tmp = mCurrentNbObjects++; + return (mStamps[tmp]<<16)|tmp; // Return virtual index (handle) to the client app + } +} + +void HandleManager::Remove(Handle handle) +{ + const PxU16 VirtualIndex = PxU16(handle); + if(VirtualIndex>=mMaxNbObjects) return; // Invalid handle + const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get the physical index + if(PhysicalIndex==0xffff) return; // Value has already been deleted + if(PhysicalIndex>=mMaxNbObjects) return; // Invalid index + + // There must be at least one valid entry. + if(mCurrentNbObjects) + { + if(mStamps[VirtualIndex]!=handle>>16) return; // Stamp mismatch => index has been recycled + + // Update list so that there's no hole + mObjects[PhysicalIndex] = mObjects[--mCurrentNbObjects];// Move the real object so that the array has no holes. + mOutToIn[mInToOut[mCurrentNbObjects]] = PhysicalIndex; // Update virtual-to-physical remapping table. + mInToOut[PhysicalIndex] = mInToOut[mCurrentNbObjects]; // Update physical-to-virtual remapping table. + // Keep track of the recyclable virtual index + mInToOut[mCurrentNbObjects] = VirtualIndex; // Store the free virtual index/handle at the end of mInToOut + mOutToIn[VirtualIndex] = 0xffff; // Invalidate the entry + mNbFreeIndices++; // One more free index + mStamps[VirtualIndex]++; // Update stamp + } +} + +void* HandleManager::GetObject(Handle handle) const +{ + const PxU16 VirtualIndex = PxU16(handle); + if(VirtualIndex>=mMaxNbObjects) return NULL; // Invalid handle + const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get physical index + if(PhysicalIndex==0xffff) return NULL; // Object has been deleted + if(PhysicalIndex>=mMaxNbObjects) return NULL; // Index is invalid + if(mStamps[VirtualIndex]!=handle>>16) return NULL; // Index has been recycled + return mObjects[PhysicalIndex]; // Returns stored pointer +} + +bool HandleManager::UpdateObject(Handle handle, void* object) +{ + const PxU16 VirtualIndex = PxU16(handle); + if(VirtualIndex>=mMaxNbObjects) return false; // Invalid handle + const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get physical index + if(PhysicalIndex==0xffff) return false; // Object has been deleted + if(PhysicalIndex>=mMaxNbObjects) return false; // Index is invalid + if(mStamps[VirtualIndex]!=handle>>16) return false; // Index has been recycled + mObjects[PhysicalIndex] = object; // Updates stored pointer + return true; +} + + +// PT: please do not expose these functions outside of this class, +// as we want to retain the ability to easily modify the handle format if necessary. + +#define NEW_ENCODING +#ifdef NEW_ENCODING +static PX_FORCE_INLINE void* encodeInternalHandle(PxU32 index, PxGeometryType::Enum type) +{ + PX_ASSERT(index<=0xffff); + PX_ASSERT(PxU32(type)<=0xffff); + // PT: we do type+1 to ban internal handles with a 0 value, since it's reserved for NULL pointers + return reinterpret_cast((size_t(index)<<16)|(size_t(type)+1)); +} + +static PX_FORCE_INLINE PxGeometryType::Enum decodeInternalType(void* handle) +{ + return PxGeometryType::Enum((PxU32(reinterpret_cast(handle)) & 0xffff)-1); +} + +static PX_FORCE_INLINE PxU32 decodeInternalIndex(void* handle) +{ + return (PxU32(size_t(handle)))>>16; +} +#else +static PX_FORCE_INLINE ObstacleHandle encodeHandle(PxU32 index, PxGeometryType::Enum type) +{ + PX_ASSERT(index<=0xffff); + PX_ASSERT(type<=0xffff); + return (PxU16(index)<<16)|PxU32(type); +} + +static PX_FORCE_INLINE PxGeometryType::Enum decodeType(ObstacleHandle handle) +{ + return PxGeometryType::Enum(handle & 0xffff); +} + +static PX_FORCE_INLINE PxU32 decodeIndex(ObstacleHandle handle) +{ + return handle>>16; +} +#endif + +ObstacleContext::ObstacleContext(CharacterControllerManager& cctMan) + : mCCTManager(cctMan) +{ +} + +ObstacleContext::~ObstacleContext() +{ +} + +void ObstacleContext::release() +{ + mCCTManager.releaseObstacleContext(*this); +} + +PxControllerManager& ObstacleContext::getControllerManager() const +{ + return mCCTManager; +} + +ObstacleHandle ObstacleContext::addObstacle(const PxObstacle& obstacle) +{ + const PxGeometryType::Enum type = obstacle.getType(); + if(type==PxGeometryType::eBOX) + { + const PxU32 index = mBoxObstacles.size(); +#ifdef NEW_ENCODING + const ObstacleHandle handle = mHandleManager.Add(encodeInternalHandle(index, type)); +#else + const ObstacleHandle handle = encodeHandle(index, type); +#endif + mBoxObstacles.pushBack(InternalBoxObstacle(handle, static_cast(obstacle))); + mCCTManager.onObstacleAdded(handle, this); + return handle; + } + else if(type==PxGeometryType::eCAPSULE) + { + const PxU32 index = mCapsuleObstacles.size(); +#ifdef NEW_ENCODING + const ObstacleHandle handle = mHandleManager.Add(encodeInternalHandle(index, type)); +#else + const ObstacleHandle handle = encodeHandle(index, type); +#endif + mCapsuleObstacles.pushBack(InternalCapsuleObstacle(handle, static_cast(obstacle))); + mCCTManager.onObstacleAdded(handle, this); + return handle; + } + else return INVALID_OBSTACLE_HANDLE; +} + +#ifdef NEW_ENCODING +template +static PX_FORCE_INLINE void remove(HandleManager& manager, void* object, ObstacleHandle handle, PxU32 index, PxU32 size, const Ps::Array& obstacles) +{ + manager.Remove(handle); + if(index!=size-1) + { + bool status = manager.UpdateObject(obstacles[size-1].mHandle, object); + PX_ASSERT(status); + PX_UNUSED(status); + } +} +#endif + +bool ObstacleContext::removeObstacle(ObstacleHandle handle) +{ +#ifdef NEW_ENCODING + void* object = mHandleManager.GetObject(handle); + if(!object) + return false; + const PxGeometryType::Enum type = decodeInternalType(object); + const PxU32 index = decodeInternalIndex(object); +#else + const PxGeometryType::Enum type = decodeType(handle); + const PxU32 index = decodeIndex(handle); +#endif + + if(type==PxGeometryType::eBOX) + { + const PxU32 size = mBoxObstacles.size(); + PX_ASSERT(index=size) + return false; + +#ifdef NEW_ENCODING + remove(mHandleManager, object, handle, index, size, mBoxObstacles); +#endif + mBoxObstacles.replaceWithLast(index); +#ifdef NEW_ENCODING + mCCTManager.onObstacleRemoved(handle); +#else + mCCTManager.onObstacleRemoved(handle, encodeHandle(size-1, type)); +#endif + return true; + } + else if(type==PxGeometryType::eCAPSULE) + { + const PxU32 size = mCapsuleObstacles.size(); + PX_ASSERT(index=size) + return false; + +#ifdef NEW_ENCODING + remove(mHandleManager, object, handle, index, size, mCapsuleObstacles); +#endif + + mCapsuleObstacles.replaceWithLast(index); +#ifdef NEW_ENCODING + mCCTManager.onObstacleRemoved(handle); +#else + mCCTManager.onObstacleRemoved(handle, encodeHandle(size-1, type)); +#endif + return true; + } + else return false; +} + +bool ObstacleContext::updateObstacle(ObstacleHandle handle, const PxObstacle& obstacle) +{ +#ifdef NEW_ENCODING + void* object = mHandleManager.GetObject(handle); + if(!object) + return false; + const PxGeometryType::Enum type = decodeInternalType(object); + PX_ASSERT(type==obstacle.getType()); + if(type!=obstacle.getType()) + return false; + const PxU32 index = decodeInternalIndex(object); +#else + const PxGeometryType::Enum type = decodeType(handle); + PX_ASSERT(type==obstacle.getType()); + if(type!=obstacle.getType()) + return false; + const PxU32 index = decodeIndex(handle); +#endif + + if(type==PxGeometryType::eBOX) + { + const PxU32 size = mBoxObstacles.size(); + PX_ASSERT(index=size) + return false; + + mBoxObstacles[index].mData = static_cast(obstacle); + mCCTManager.onObstacleUpdated(handle,this); + return true; + } + else if(type==PxGeometryType::eCAPSULE) + { + const PxU32 size = mCapsuleObstacles.size(); + PX_ASSERT(index=size) + return false; + + mCapsuleObstacles[index].mData = static_cast(obstacle); + mCCTManager.onObstacleUpdated(handle,this); + return true; + } + else return false; +} + +PxU32 ObstacleContext::getNbObstacles() const +{ + return mBoxObstacles.size() + mCapsuleObstacles.size(); +} + +const PxObstacle* ObstacleContext::getObstacle(PxU32 i) const +{ + const PxU32 nbBoxes = mBoxObstacles.size(); + if(i=size) + return NULL; + PX_ASSERT(mBoxObstacles[index].mHandle==handle); + return &mBoxObstacles[index].mData; + } + else if(type==PxGeometryType::eCAPSULE) + { + const PxU32 size = mCapsuleObstacles.size(); + if(index>=size) + return NULL; + PX_ASSERT(mCapsuleObstacles[index].mHandle==handle); + return &mCapsuleObstacles[index].mData; + } + else return NULL; +} + +#include "GuRaycastTests.h" +#include "PxBoxGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PsMathUtils.h" +using namespace Gu; +const PxObstacle* ObstacleContext::raycastSingle(PxRaycastHit& hit, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, ObstacleHandle& obstacleHandle) const +{ + PxRaycastHit localHit; + PxF32 t = FLT_MAX; + const PxObstacle* touchedObstacle = NULL; + + const PxHitFlags hitFlags = PxHitFlags(0); + + { + const RaycastFunc raycastFunc = Gu::getRaycastFuncTable()[PxGeometryType::eBOX]; + PX_ASSERT(raycastFunc); + + const PxU32 nbExtraBoxes = mBoxObstacles.size(); + for(PxU32 i=0;i mBoxObstacles; + + struct InternalCapsuleObstacle + { + InternalCapsuleObstacle(ObstacleHandle handle, const PxCapsuleObstacle& data) : mHandle(handle), mData(data) {} + + ObstacleHandle mHandle; + PxCapsuleObstacle mData; + }; + Ps::Array mCapsuleObstacles; + + private: + ObstacleContext& operator=(const ObstacleContext&); + HandleManager mHandleManager; + CharacterControllerManager& mCCTManager; + }; + + +} // namespace Cct + +} + +/** \endcond */ +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp new file mode 100644 index 000000000..d6731e02a --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp @@ -0,0 +1,50 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CctSweptBox.h" +#include "CctCharacterController.h" + +using namespace physx; +using namespace Cct; + +SweptBox::SweptBox() +{ + mType = SweptVolumeType::eBOX; +} + +SweptBox::~SweptBox() +{ +} + +void SweptBox::computeTemporalBox(const SweepTest& test, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const +{ + const float radius = PxMax(mExtents.y, mExtents.z); + const float height = 2.0f * mExtents.x; + Cct::computeTemporalBox(box, radius, height, test.mUserParams.mContactOffset, test.mUserParams.mMaxJumpHeight, test.mUserParams.mUpDirection, center, direction); +} diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h new file mode 100644 index 000000000..48ab0d954 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h @@ -0,0 +1,55 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_SWEPT_BOX +#define CCT_SWEPT_BOX + +#include "CctSweptVolume.h" + +namespace physx +{ +namespace Cct +{ + + class SweptBox : public SweptVolume + { + public: + SweptBox(); + virtual ~SweptBox(); + + virtual void computeTemporalBox(const SweepTest&, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const; + + PxVec3 mExtents; + }; + +} // namespace Cct + +} + +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp new file mode 100644 index 000000000..f4d735d84 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp @@ -0,0 +1,48 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CctSweptCapsule.h" +#include "CctCharacterController.h" + +using namespace physx; +using namespace Cct; + +SweptCapsule::SweptCapsule() +{ + mType = SweptVolumeType::eCAPSULE; +} + +SweptCapsule::~SweptCapsule() +{ +} + +void SweptCapsule::computeTemporalBox(const SweepTest& test, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const +{ + Cct::computeTemporalBox(box, mRadius, mHeight, test.mUserParams.mContactOffset, test.mUserParams.mMaxJumpHeight, test.mUserParams.mUpDirection, center, direction); +} diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h new file mode 100644 index 000000000..8c9a2d8a0 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h @@ -0,0 +1,56 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_SWEPT_CAPSULE +#define CCT_SWEPT_CAPSULE + +#include "CctSweptVolume.h" + +namespace physx +{ +namespace Cct +{ + + class SweptCapsule : public SweptVolume + { + public: + SweptCapsule(); + virtual ~SweptCapsule(); + + virtual void computeTemporalBox(const SweepTest&, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const; + + PxF32 mRadius; + PxF32 mHeight; + }; + +} // namespace Cct + +} + +#endif diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp new file mode 100644 index 000000000..1d846b460 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CctSweptVolume.h" + +using namespace physx; +using namespace Cct; + +SweptVolume::SweptVolume() +{ + mType = SweptVolumeType::eLAST; +} + +SweptVolume::~SweptVolume() +{ +} + +void Cct::computeTemporalBox(PxExtendedBounds3& _box, float radius, float height, float contactOffset, float maxJumpHeight, const PxVec3& upDirection, const PxExtendedVec3& center, const PxVec3& direction) +{ + const float r = radius + contactOffset; + PxVec3 extents(r); + const float halfHeight = height*0.5f; + extents.x += fabsf(upDirection.x)*halfHeight; + extents.y += fabsf(upDirection.y)*halfHeight; + extents.z += fabsf(upDirection.z)*halfHeight; + + PxExtendedBounds3 box; + setCenterExtents(box, center, extents); + + { + PxExtendedBounds3 destBox; + PxExtendedVec3 tmp = center; + tmp += direction; + setCenterExtents(destBox, tmp, extents); + add(box, destBox); + } + + if(maxJumpHeight!=0.0f) + { + PxExtendedBounds3 destBox; + PxExtendedVec3 tmp = center; + tmp -= upDirection * maxJumpHeight; + setCenterExtents(destBox, tmp, extents); + add(box, destBox); + } + + _box = box; +} diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h new file mode 100644 index 000000000..a29435b04 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef CCT_SWEPT_VOLUME +#define CCT_SWEPT_VOLUME + +#include "CctUtils.h" + +namespace physx +{ +namespace Cct +{ + + struct SweptVolumeType + { + enum Enum + { + eBOX, + eCAPSULE, + + eLAST + }; + }; + + class SweepTest; + + class SweptVolume + { + public: + SweptVolume(); + virtual ~SweptVolume(); + + virtual void computeTemporalBox(const SweepTest& test, PxExtendedBounds3& box, const PxExtendedVec3& center, const PxVec3& direction) const = 0; + + PX_FORCE_INLINE SweptVolumeType::Enum getType() const { return mType; } + + PxExtendedVec3 mCenter; + PxF32 mHalfHeight; // UBI + protected: + SweptVolumeType::Enum mType; + }; + + void computeTemporalBox(PxExtendedBounds3& _box, float radius, float height, float contactOffset, float maxJumpHeight, const PxVec3& upDirection, const PxExtendedVec3& center, const PxVec3& direction); + +} // namespace Cct + +} + +#endif + diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h new file mode 100644 index 000000000..8ac1e7227 --- /dev/null +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h @@ -0,0 +1,214 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef CCT_UTILS +#define CCT_UTILS + +#include "PxExtended.h" +#include "PxShapeExt.h" + +namespace physx +{ + +PX_FORCE_INLINE bool testSlope(const PxVec3& normal, const PxVec3& upDirection, PxF32 slopeLimit) +{ + const float dp = normal.dot(upDirection); + return dp>=0.0f && dp minimum.x) return false; + if(box.minimum.y > minimum.y) return false; + if(box.minimum.z > minimum.z) return false; + if(box.maximum.x < maximum.x) return false; + if(box.maximum.y < maximum.y) return false; + if(box.maximum.z < maximum.z) return false; + return true; + } + PxExtendedVec3 minimum, maximum; + }; + + PX_INLINE void getCenter(const PxExtendedBounds3& b, PxExtendedVec3& center) + { + center = b.minimum + b.maximum; + center *= 0.5; + } + + PX_INLINE void getExtents(const PxExtendedBounds3& b, PxVec3& extents) + { + extents = b.maximum - b.minimum; + extents *= 0.5f; + } + + PX_INLINE void setCenterExtents(PxExtendedBounds3& b, const PxExtendedVec3& c, const PxVec3& e) + { + b.minimum = c; b.minimum -= e; + b.maximum = c; b.maximum += e; + } + + PX_INLINE void add(PxExtendedBounds3& b, const PxExtendedBounds3& b2) + { + // - if we're empty, minimum = MAX,MAX,MAX => minimum will be b2 in all cases => it will copy b2, ok + // - if b2 is empty, the opposite happens => keep us unchanged => ok + // => same behaviour as before, automatically + b.minimum.minimum(b2.minimum); + b.maximum.maximum(b2.maximum); + } +#else + + #include "foundation/PxBounds3.h" + #include "GuBox.h" + #include "GuCapsule.h" + #include "GuPlane.h" + + typedef Gu::Box PxExtendedBox; + typedef Gu::Sphere PxExtendedSphere; + typedef Gu::Segment PxExtendedSegment; + typedef Gu::Capsule PxExtendedCapsule; + typedef PxBounds3 PxExtendedBounds3; + + PX_INLINE PxExtended distance(const PxVec3& v2, const PxVec3& v) + { + const PxExtended dx = v2.x - v.x; + const PxExtended dy = v2.y - v.y; + const PxExtended dz = v2.z - v.z; + return PxSqrt(dx * dx + dy * dy + dz * dz); + } + + PX_INLINE void getCenter(const PxBounds3& b, PxVec3& center) + { + center = b.minimum + b.maximum; + center *= 0.5; + } + + PX_INLINE void getExtents(const PxBounds3& b, PxVec3& extents) + { + extents = b.maximum - b.minimum; + extents *= 0.5f; + } + + PX_INLINE void setCenterExtents(PxBounds3& b, const PxVec3& center, const PxVec3& extents) + { + b.minimum = center - extents; + b.maximum = center + extents; + } + + PX_INLINE void add(PxBounds3& b, const PxBounds3& b2) + { + // - if we're empty, minimum = MAX,MAX,MAX => minimum will be b2 in all cases => it will copy b2, ok + // - if b2 is empty, the opposite happens => keep us unchanged => ok + // => same behaviour as before, automatically + b.minimum.minimum(b2.minimum); + b.maximum.maximum(b2.maximum); + } +#endif + +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp b/src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp new file mode 100644 index 000000000..4db1c96da --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp @@ -0,0 +1,712 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "EdgeList.h" +#include "Adjacencies.h" +#include "CmRadixSortBuffered.h" +#include "GuSerialize.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Flips the winding. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AdjTriangle::Flip() +{ +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + // Call the Triangle method + IndexedTriangle::Flip(); +#endif + + // Flip links. We flipped vertex references 1 & 2, i.e. links 0 & 1. + physx::shdfnd::swap(mATri[0], mATri[1]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the number of boundary edges in a triangle. + * \return the number of boundary edges. (0 => 3) + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PxU32 AdjTriangle::ComputeNbBoundaryEdges() const +{ + // Look for boundary edges + PxU32 Nb = 0; + if(IS_BOUNDARY(mATri[0])) Nb++; + if(IS_BOUNDARY(mATri[1])) Nb++; + if(IS_BOUNDARY(mATri[2])) Nb++; + return Nb; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the number of valid neighbors. + * \return the number of neighbors. (0 => 3) + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PxU32 AdjTriangle::ComputeNbNeighbors() const +{ + PxU32 Nb = 0; + if(!IS_BOUNDARY(mATri[0])) Nb++; + if(!IS_BOUNDARY(mATri[1])) Nb++; + if(!IS_BOUNDARY(mATri[2])) Nb++; + return Nb; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the triangle has a particular neighbor or not. + * \param tref [in] the triangle reference to look for + * \param index [out] the corresponding index in the triangle (NULL if not needed) + * \return true if the triangle has the given neighbor + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AdjTriangle::HasNeighbor(PxU32 tref, PxU32* index) const +{ + // ### could be optimized + if(!IS_BOUNDARY(mATri[0]) && MAKE_ADJ_TRI(mATri[0])==tref) { if(index) *index = 0; return true; } + if(!IS_BOUNDARY(mATri[1]) && MAKE_ADJ_TRI(mATri[1])==tref) { if(index) *index = 1; return true; } + if(!IS_BOUNDARY(mATri[2]) && MAKE_ADJ_TRI(mATri[2])==tref) { if(index) *index = 2; return true; } + return false; +} + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Adjacencies::Adjacencies() : mNbFaces(0), mFaces(NULL) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Adjacencies::~Adjacencies() +{ + PX_DELETE_ARRAY(mFaces); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the number of boundary edges. + * \return the number of boundary edges. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PxU32 Adjacencies::ComputeNbBoundaryEdges() const +{ + // Checking + if(!mFaces) return 0; + + // Look for boundary edges + PxU32 Nb = 0; + for(PxU32 i=0;iComputeNbBoundaryEdges(); + } + return Nb; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the boundary vertices. A boundary vertex is defined as a vertex shared by at least one boundary edge. + * \param nb_verts [in] the number of vertices + * \param bound_status [out] a user-provided array of bool + * \return true if success. The user-array is filled with true or false (boundary vertex / not boundary vertex) + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY +bool Adjacencies::GetBoundaryVertices(PxU32 nb_verts, bool* bound_status) const +#else +bool Adjacencies::GetBoundaryVertices(PxU32 nb_verts, bool* bound_status, const Gu::TriangleT* faces) const +#endif +{ + // We need the adjacencies + if(!mFaces || !bound_status || !nb_verts) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacencies::GetBoundaryVertices: NULL parameter!"); + return false; + } + +#ifndef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + if(!faces) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacencies::GetBoundaryVertices: NULL parameter!"); + return false; + } +#endif + + // Init + PxMemZero(bound_status, nb_verts*sizeof(bool)); + + // Loop through faces + for(PxU32 i=0;imATri[0])) + { + // Two boundary vertices: 0 - 1 +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + PxU32 VRef0 = CurTri->v[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = CurTri->v[1]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#else + PxU32 VRef0 = faces[i].v[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = faces[i].v[1]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#endif + } + if(IS_BOUNDARY(CurTri->mATri[1])) + { + // Two boundary vertices: 0 - 2 +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + PxU32 VRef0 = CurTri->v[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = CurTri->v[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#else + PxU32 VRef0 = faces[i].v[0]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = faces[i].v[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#endif + } + if(IS_BOUNDARY(CurTri->mATri[2])) + { + // Two boundary vertices: 1 - 2 +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + PxU32 VRef0 = CurTri->v[1]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = CurTri->v[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#else + PxU32 VRef0 = faces[i].v[1]; if(VRef0>=nb_verts) return false; bound_status[VRef0] = true; + PxU32 VRef1 = faces[i].v[2]; if(VRef1>=nb_verts) return false; bound_status[VRef1] = true; +#endif + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Assigns a new edge code to the counterpart link of a given link. + * \param link [in] the link to modify - shouldn't be a boundary link + * \param edge_nb [in] the new edge number + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Adjacencies::AssignNewEdgeCode(PxU32 link, PxU8 edge_nb) +{ + if(!IS_BOUNDARY(link)) + { + PxU32 Id = MAKE_ADJ_TRI(link); // Triangle ID + PxU32 Edge = GET_EDGE_NB(link); // Counterpart edge ID + AdjTriangle* Tri = &mFaces[Id]; // Adjacent triangle + + // Get link whose edge code is invalid + PxU32 AdjLink = Tri->mATri[Edge]; // Link to ourself (i.e. to 'link') + SET_EDGE_NB(AdjLink, edge_nb); // Assign new edge code + Tri->mATri[Edge] = AdjLink; // Put link back + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Modifies the existing database so that reference 'vref' of triangle 'curtri' becomes the last one. + * Provided reference must already exist in provided triangle. + * \param cur_tri [in] the triangle + * \param vref [in] the reference + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY +bool Adjacencies::MakeLastRef(AdjTriangle& cur_tri, PxU32 vref) +#else +bool Adjacencies::MakeLastRef(AdjTriangle& cur_tri, PxU32 vref, Gu::TriangleT* cur_topo) +#endif +{ +#ifndef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + // Checkings + if(!cur_topo) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacencies::MakeLastRef: NULL parameter!"); + return false; + } +#endif + // We want pattern (x y vref) + // Edge 0-1 is (x y) + // Edge 0-2 is (x vref) + // Edge 1-2 is (y vref) + + // First thing is to scroll the existing references in order for vref to become the last one. Scrolling assures winding order is conserved. + + // Edge code need fixing as well: + // The two MSB for each link encode the counterpart edge in adjacent triangle. We swap the link positions, but adjacent triangles remain the + // same. In other words, edge codes are still valid for current triangle since counterpart edges have not been swapped. *BUT* edge codes of + // the three possible adjacent triangles *are* now invalid. We need to fix edge codes, but for adjacent triangles... + +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + if(cur_tri.v[0]==vref) +#else + if(cur_topo->v[0]==vref) +#endif + { + // Pattern is (vref x y) + // Edge 0-1 is (vref x) + // Edge 0-2 is (vref y) + // Edge 1-2 is (x y) + + // Catch original data +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + PxU32 Ref0 = cur_tri.v[0]; PxU32 Link01 = cur_tri.mATri[0]; + PxU32 Ref1 = cur_tri.v[1]; PxU32 Link02 = cur_tri.mATri[1]; + PxU32 Ref2 = cur_tri.v[2]; PxU32 Link12 = cur_tri.mATri[2]; + + // Swap + cur_tri.v[0] = Ref1; + cur_tri.v[1] = Ref2; + cur_tri.v[2] = Ref0; +#else + PxU32 Ref0 = cur_topo->v[0]; PxU32 Link01 = cur_tri.mATri[0]; + PxU32 Ref1 = cur_topo->v[1]; PxU32 Link02 = cur_tri.mATri[1]; + PxU32 Ref2 = cur_topo->v[2]; PxU32 Link12 = cur_tri.mATri[2]; + + // Swap + cur_topo->v[0] = Ref1; + cur_topo->v[1] = Ref2; + cur_topo->v[2] = Ref0; +#endif + cur_tri.mATri[0] = Link12; // Edge 0-1 now encodes Ref1-Ref2, i.e. previous Link12 + cur_tri.mATri[1] = Link01; // Edge 0-2 now encodes Ref1-Ref0, i.e. previous Link01 + cur_tri.mATri[2] = Link02; // Edge 1-2 now encodes Ref2-Ref0, i.e. previous Link02 + + // Fix edge codes + AssignNewEdgeCode(Link01, 1); + AssignNewEdgeCode(Link02, 2); + AssignNewEdgeCode(Link12, 0); + + return true; + } +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + else if(cur_tri.v[1]==vref) +#else + else if(cur_topo->v[1]==vref) +#endif + { + // Pattern is (x vref y) + // Edge 0-1 is (x vref) + // Edge 0-2 is (x y) + // Edge 1-2 is (vref y) + + // Catch original data +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + PxU32 Ref0 = cur_tri.v[0]; PxU32 Link01 = cur_tri.mATri[0]; + PxU32 Ref1 = cur_tri.v[1]; PxU32 Link02 = cur_tri.mATri[1]; + PxU32 Ref2 = cur_tri.v[2]; PxU32 Link12 = cur_tri.mATri[2]; + + // Swap + cur_tri.v[0] = Ref2; + cur_tri.v[1] = Ref0; + cur_tri.v[2] = Ref1; +#else + PxU32 Ref0 = cur_topo->v[0]; PxU32 Link01 = cur_tri.mATri[0]; + PxU32 Ref1 = cur_topo->v[1]; PxU32 Link02 = cur_tri.mATri[1]; + PxU32 Ref2 = cur_topo->v[2]; PxU32 Link12 = cur_tri.mATri[2]; + + // Swap + cur_topo->v[0] = Ref2; + cur_topo->v[1] = Ref0; + cur_topo->v[2] = Ref1; +#endif + cur_tri.mATri[0] = Link02; // Edge 0-1 now encodes Ref2-Ref0, i.e. previous Link02 + cur_tri.mATri[1] = Link12; // Edge 0-2 now encodes Ref2-Ref1, i.e. previous Link12 + cur_tri.mATri[2] = Link01; // Edge 1-2 now encodes Ref0-Ref1, i.e. previous Link01 + + // Fix edge codes + AssignNewEdgeCode(Link01, 2); + AssignNewEdgeCode(Link02, 0); + AssignNewEdgeCode(Link12, 1); + + return true; + } +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + else if(cur_tri.v[2]==vref) +#else + else if(cur_topo->v[2]==vref) +#endif + { + // Nothing to do, provided reference already is the last one + return true; + } + + // Here the provided reference doesn't belong to the provided triangle. + return false; +} + +bool Adjacencies::Load(PxInputStream& stream) +{ + // Import header + PxU32 Version; + bool Mismatch; + if(!ReadHeader('A', 'D', 'J', 'A', Version, Mismatch, stream)) + return false; + + // Import adjacencies + mNbFaces = readDword(Mismatch, stream); + mFaces = PX_NEW(AdjTriangle)[mNbFaces]; + stream.read(mFaces, sizeof(AdjTriangle)*mNbFaces); + + return true; +} + +//#ifdef PX_COOKING + + //! An edge class used to compute the adjacency structures. + class AdjEdge : public Gu::EdgeData, public Ps::UserAllocated + { + public: + //! Constructor + PX_INLINE AdjEdge() {} + //! Destructor + PX_INLINE ~AdjEdge() {} + + PxU32 mFaceNb; //!< Owner face + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Adds a new edge to the database. + * \param ref0 [in] vertex reference for the new edge + * \param ref1 [in] vertex reference for the new edge + * \param face [in] owner face + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + static void AddEdge(PxU32 ref0, PxU32 ref1, PxU32 face, PxU32& nb_edges, AdjEdge* edges) + { + // Store edge data + edges[nb_edges].Ref0 = ref0; + edges[nb_edges].Ref1 = ref1; + edges[nb_edges].mFaceNb = face; + nb_edges++; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Adds a new triangle to the database. + * \param ref0 [in] vertex reference for the new triangle + * \param ref1 [in] vertex reference for the new triangle + * \param ref2 [in] vertex reference for the new triangle + * \param id [in] triangle index + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + static void AddTriangle(PxU32 ref0, PxU32 ref1, PxU32 ref2, PxU32 id, AdjTriangle* faces, PxU32& nb_edges, AdjEdge* edges) + { +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + // Store vertex-references + faces[id].v[0] = ref0; + faces[id].v[1] = ref1; + faces[id].v[2] = ref2; +#endif + // Reset links + faces[id].mATri[0] = PX_INVALID_U32; + faces[id].mATri[1] = PX_INVALID_U32; + faces[id].mATri[2] = PX_INVALID_U32; + + // Add edge 01 to database + if(ref0 FirstTri, SecondTri; + if(create.DFaces) + { + FirstTri.v[0] = create.DFaces[first_tri*3+0]; + FirstTri.v[1] = create.DFaces[first_tri*3+1]; + FirstTri.v[2] = create.DFaces[first_tri*3+2]; + SecondTri.v[0] = create.DFaces[second_tri*3+0]; + SecondTri.v[1] = create.DFaces[second_tri*3+1]; + SecondTri.v[2] = create.DFaces[second_tri*3+2]; + } + if(create.WFaces) + { + FirstTri.v[0] = create.WFaces[first_tri*3+0]; + FirstTri.v[1] = create.WFaces[first_tri*3+1]; + FirstTri.v[2] = create.WFaces[first_tri*3+2]; + SecondTri.v[0] = create.WFaces[second_tri*3+0]; + SecondTri.v[1] = create.WFaces[second_tri*3+1]; + SecondTri.v[2] = create.WFaces[second_tri*3+2]; + } + + // Get the edge IDs. 0xff means input references are wrong. + const PxU8 EdgeNb0 = FirstTri.findEdge(ref0, ref1); + const PxU8 EdgeNb1 = SecondTri.findEdge(ref0, ref1); + if(EdgeNb0==0xff || EdgeNb1==0xff) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Adjacencies::UpdateLink: invalid edge reference"); + return false; + } + + // Update links. The two most significant bits contain the counterpart edge's ID. + faces[first_tri].mATri[EdgeNb0] = second_tri |(PxU32(EdgeNb1)<<30); + faces[second_tri].mATri[EdgeNb1] = first_tri |(PxU32(EdgeNb0)<<30); +#endif + return true; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Creates the adjacency structures. + * \return true if success. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + static bool CreateDatabase(AdjTriangle* faces, PxU32 nb_edges, const AdjEdge* edges) +#else + static bool CreateDatabase(AdjTriangle* faces, PxU32 nb_edges, const AdjEdge* edges, const ADJACENCIESCREATE& create) +#endif + { + Cm::RadixSortBuffered Core; + { + // Multiple sorts - this rewritten version uses less ram + // PT: TTP 2994: the mesh has 343000+ edges, so yeah, sure, allocating more than 1mb on the stack causes overflow... + PxU32* VRefs = PX_NEW_TEMP(PxU32)[nb_edges]; + + // Sort according to mRef0, then mRef1 + PxU32 i; + for(i=0;i edge is a boundary edge: it belongs to a single triangle. + // Hence there's no need to update a link to an adjacent triangle. +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + if(!UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces)) return false; +#else + if(!UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces, create)) return false; +#endif + } + // Reset for next edge + Count = 0; + TmpBuffer[Count++] = Face; + LastRef0 = Ref0; + LastRef1 = Ref1; + } + } + bool Status = true; +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + if(Count==2) Status = UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces); +#else + if(Count==2) Status = UpdateLink(TmpBuffer[0], TmpBuffer[1], LastRef0, LastRef1, faces, create); +#endif + return Status; + } + +AdjacenciesBuilder::AdjacenciesBuilder() +{ +} + +AdjacenciesBuilder::~AdjacenciesBuilder() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes the component. + * \param create [in] the creation structure + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AdjacenciesBuilder::Init(const ADJACENCIESCREATE& create) +{ + // Checkings + if(!create.NbFaces) return false; + + // Get some bytes + mNbFaces = create.NbFaces; + mFaces = PX_NEW(AdjTriangle)[mNbFaces]; + + AdjEdge* Edges = PX_NEW_TEMP(AdjEdge)[mNbFaces*3]; + PxU32 NbEdges=0; + + // Feed me with triangles..... + for(PxU32 i=0;i>30) //!< Transforms a link into a counterpart edge ID. +// #define IS_BOUNDARY(x) (x==PX_INVALID_U32) //!< Returns true for boundary edges. + #define IS_BOUNDARY(x) ((x & ADJ_TRIREF_MASK)==ADJ_TRIREF_MASK) //!< Returns true for boundary edges. + + // Forward declarations + class Adjacencies; + + enum SharedEdgeIndex + { + EDGE01 = 0, + EDGE02 = 1, + EDGE12 = 2 + }; + +/* PX_INLINE void GetEdgeIndices(SharedEdgeIndex edge_index, PxU32& id0, PxU32& id1) + { + if(edge_index==0) + { + id0 = 0; + id1 = 1; + } + else if(edge_index==1) + { + id0 = 0; + id1 = 2; + } + else if(edge_index==2) + { + id0 = 1; + id1 = 2; + } + }*/ + + //! Sets a new edge code + #define SET_EDGE_NB(link, code) \ + link&=ADJ_TRIREF_MASK; \ + link|=code<<30; \ + + //! A triangle class used to compute the adjacency structures. + class AdjTriangle +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + : public IndexedTriangle +#else + : public Ps::UserAllocated +#endif + { + public: + //! Constructor + PX_INLINE AdjTriangle() {} + //! Destructor + PX_INLINE ~AdjTriangle() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the number of boundary edges in a triangle. + * \return the number of boundary edges. (0 => 3) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PxU32 ComputeNbBoundaryEdges() const; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the number of valid neighbors. + * \return the number of neighbors. (0 => 3) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PxU32 ComputeNbNeighbors() const; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the triangle has a particular neighbor or not. + * \param tref [in] the triangle reference to look for + * \param index [out] the corresponding index in the triangle (NULL if not needed) + * \return true if the triangle has the given neighbor + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool HasNeighbor(PxU32 tref, PxU32* index=NULL) const; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Flips the winding. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Flip(); + + // Data access + PX_INLINE PxU32 GetLink(SharedEdgeIndex edge_index) const { return mATri[edge_index]; } + PX_INLINE PxU32 GetAdjTri(SharedEdgeIndex edge_index) const { return MAKE_ADJ_TRI(mATri[edge_index]); } + PX_INLINE PxU32 GetAdjEdge(SharedEdgeIndex edge_index) const { return GET_EDGE_NB(mATri[edge_index]); } + PX_INLINE Ps::IntBool IsBoundaryEdge(SharedEdgeIndex edge_index) const { return IS_BOUNDARY(mATri[edge_index]); } +#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS + PX_INLINE Ps::IntBool HasActiveEdge01() const { return Ps::IntBool(IS_CONVEX_EDGE(mATri[EDGE01])); } + PX_INLINE Ps::IntBool HasActiveEdge20() const { return Ps::IntBool(IS_CONVEX_EDGE(mATri[EDGE02])); } + PX_INLINE Ps::IntBool HasActiveEdge12() const { return Ps::IntBool(IS_CONVEX_EDGE(mATri[EDGE12])); } + PX_INLINE Ps::IntBool HasActiveEdge(PxU32 i) const { return Ps::IntBool(IS_CONVEX_EDGE(mATri[i])); } +#endif +// private: + //! Links/References of adjacent triangles. The 2 most significant bits contains the counterpart edge in the adjacent triangle. + //! mATri[0] refers to edge 0-1 + //! mATri[1] refers to edge 0-2 + //! mATri[2] refers to edge 1-2 + PxU32 mATri[3]; + }; + + //! The adjacencies creation structure. + struct ADJACENCIESCREATE + { + //! Constructor + ADJACENCIESCREATE() : NbFaces(0), DFaces(NULL), WFaces(NULL) + { +#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS + Verts = NULL; + Epsilon = 0.1f; +// Epsilon = 0.001f; +#endif + } + + PxU32 NbFaces; //!< Number of faces in source topo + const PxU32* DFaces; //!< List of faces (dwords) or NULL + const PxU16* WFaces; //!< List of faces (words) or NULL +#ifdef MSH_ADJACENCIES_INCLUDE_CONVEX_BITS + const PxVec3* Verts; + float Epsilon; +#endif + }; + + class Adjacencies : public Ps::UserAllocated + { + public: + Adjacencies(); + ~Adjacencies(); + + PxU32 mNbFaces; //!< Number of faces involved in the computation. + AdjTriangle* mFaces; //!< A list of AdjTriangles (one/face) + + bool Load(PxInputStream& stream); + // Basic mesh walking + PX_INLINE const AdjTriangle* GetAdjacentFace(const AdjTriangle& current_tri, SharedEdgeIndex edge_nb) const + { + // No checkings here, make sure mFaces has been created + + // Catch the link + PxU32 Link = current_tri.GetLink(edge_nb); + + // Returns NULL for boundary edges + if(IS_BOUNDARY(Link)) return NULL; + + // Else transform into face index + PxU32 Id = MAKE_ADJ_TRI(Link); + + // Possible counterpart edge is: + // PxU32 Edge = GET_EDGE_NB(Link); + + // And returns adjacent triangle + return &mFaces[Id]; + } + // Helpers + PxU32 ComputeNbBoundaryEdges() const; +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + bool GetBoundaryVertices(PxU32 nb_verts, bool* bound_status) const; +#else + bool GetBoundaryVertices(PxU32 nb_verts, bool* bound_status, const Gu::TriangleT* faces) const; +#endif + // +#ifdef MSH_ADJACENCIES_INCLUDE_TOPOLOGY + bool MakeLastRef(AdjTriangle& cur_tri, PxU32 vref); +#else + bool MakeLastRef(AdjTriangle& cur_tri, PxU32 vref, Gu::TriangleT* cur_topo); +#endif + private: + // New edge codes assignment + void AssignNewEdgeCode(PxU32 link, PxU8 edge_nb); + }; + +//#ifdef PX_COOKING + class AdjacenciesBuilder : public Adjacencies + { + public: + AdjacenciesBuilder(); + ~AdjacenciesBuilder(); + + bool Init(const ADJACENCIESCREATE& create); +// bool Save(Stream& stream) const; + }; +//#endif + +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp new file mode 100644 index 000000000..77e9e5f3a --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxIO.h" +#include "BVHStructureBuilder.h" +#include "GuAABBTreeBuild.h" +#include "GuSerialize.h" +#include "GuBVHStructure.h" + +using namespace physx; +using namespace Gu; + +// A.B. move this to some common place? +#define NB_OBJECTS_PER_NODE 4 + +// PT: TODO: - check that this is compatible with Gu::computeBounds(..., SQ_PRUNER_INFLATION, ...) +// PT: TODO: - refactor with "inflateBounds" in GuBounds.cpp if possible +// PT: TODO: - use SQ_PRUNER_INFLATION instead of hardcoding "0.01f" +// A.B. move to common place +PX_FORCE_INLINE void inflateBounds(PxBounds3& dst, const PxBounds3& src) +{ + using namespace physx::shdfnd::aos; + + const Vec4V minV = V4LoadU(&src.minimum.x); + const Vec4V maxV = V4LoadU(&src.maximum.x); + const Vec4V eV = V4Scale(V4Sub(maxV, minV), FLoad(0.5f * 0.01f)); + + V4StoreU(V4Sub(minV, eV), &dst.minimum.x); + PX_ALIGN(16, PxVec4) max4; + V4StoreA(V4Add(maxV, eV), &max4.x); + dst.maximum = PxVec3(max4.x, max4.y, max4.z); +} + +void flatten(const NodeAllocator& nodeAllocator, BVHNode* dest) +{ + // PT: gathers all build nodes allocated so far and flatten them to a linear destination array of smaller runtime nodes + PxU32 offset = 0; + const PxU32 nbSlabs = nodeAllocator.mSlabs.size(); + for(PxU32 s=0;s= nodeAllocator.mSlabs[j].mPool && pool[i].mPos < nodeAllocator.mSlabs[j].mPool + nodeAllocator.mSlabs[j].mNbUsedNodes) + { + localNodeIndex = PxU32(pool[i].mPos - nodeAllocator.mSlabs[j].mPool); + break; + } + nodeBase += nodeAllocator.mSlabs[j].mNbUsedNodes; + } + const PxU32 nodeIndex = nodeBase + localNodeIndex; + dest[offset].mData = nodeIndex<<1; + } + offset++; + } + } +} + +BVHStructureBuilder::BVHStructureBuilder(): + mBounds(NULL), + mNumVolumes(0), + mNumNodes(0), + mNodes(NULL), + mIndices(NULL) +{ +} + +BVHStructureBuilder::~BVHStructureBuilder() +{ + PX_FREE_AND_RESET(mBounds); + PX_FREE_AND_RESET(mNodes); + PX_FREE_AND_RESET(mIndices); +} + +bool BVHStructureBuilder::loadFromDesc(const PxBVHStructureDesc& desc) +{ + PX_ASSERT(desc.isValid()); + + const PxU32 numPrimitives = desc.bounds.count; + + // allocate one more for safe SIMD vec4 load + mBounds = reinterpret_cast(PX_ALLOC((numPrimitives + 1) * sizeof(PxBounds3), "PxBounds3")); + + const PxU8* sB = reinterpret_cast(desc.bounds.data); + + for(PxU32 i = 0; i < numPrimitives; i++) + { + inflateBounds(mBounds[i], *reinterpret_cast(sB)); + + sB += desc.bounds.stride; + } + mNumVolumes = numPrimitives; + + // build the BVH + AABBTreeBuildParams params; + params.mNbPrimitives = desc.bounds.count; + params.mAABBArray = mBounds; + params.mLimit = NB_OBJECTS_PER_NODE; + BuildStats stats; + NodeAllocator nodeAllocator; + + const bool buildStatus = buildAABBTree(params, nodeAllocator, stats, mIndices); + PX_UNUSED(buildStatus); + PX_ASSERT(buildStatus); + + // store the computed hierarchy + mNumNodes = stats.getCount(); + mNodes = reinterpret_cast(PX_ALLOC(sizeof(BVHNode)*mNumNodes, "AABB tree nodes")); + PX_ASSERT(mNumNodes==nodeAllocator.mTotalNbNodes); + + // store the results into BVHNode list + flatten(nodeAllocator, mNodes); + nodeAllocator.release(); + + return true; +} + +// A.B. move to load code +#define PX_BVH_STRUCTURE_VERSION 1 + +bool BVHStructureBuilder::save(PxOutputStream& stream, bool endian) const +{ + // write header + if(!writeHeader('B', 'V', 'H', 'S', PX_BVH_STRUCTURE_VERSION, endian, stream)) + return false; + + // write mData members + writeDword(mNumVolumes, endian, stream); + writeDword(mNumNodes, endian, stream); + + // write indices and bounds + for(PxU32 i = 0; i < mNumVolumes; i++) + { + writeDword(mIndices[i], endian, stream); + } + + for(PxU32 i = 0; i < mNumVolumes; i++) + { + writeFloatBuffer(&mBounds[i].minimum.x, 3, endian, stream); + writeFloatBuffer(&mBounds[i].maximum.x, 3, endian, stream); + } + + // write nodes + for(PxU32 i = 0; i < mNumNodes; i++) + { + writeDword(mNodes[i].mData, endian, stream); + + writeFloatBuffer(&mNodes[i].mBV.minimum.x, 3, endian, stream); + writeFloatBuffer(&mNodes[i].mBV.maximum.x, 3, endian, stream); + } + + return true; +} + +void BVHStructureBuilder::moveData(Gu::BVHStructureData& bvhData) +{ + bvhData.mBounds = mBounds; + bvhData.mIndices = mIndices; + bvhData.mNodes = mNodes; + bvhData.mNumNodes = mNumNodes; + bvhData.mNumVolumes = mNumVolumes; + + // set pointers to NULL so we do not release the memory that has been passed to physics create + mBounds = NULL; + mIndices = NULL; + mNodes = NULL; + mNumNodes = 0; + mNumVolumes = 0; +} diff --git a/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h new file mode 100644 index 000000000..93bf575b4 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_BVH_STRUCTURE_BUILDER_H +#define PX_BVH_STRUCTURE_BUILDER_H + +#include "PsUserAllocated.h" +#include "PxCooking.h" +#include "GuBVHStructure.h" + +namespace physx +{ + namespace Gu + { + struct BVHStructureData; + } + + class BVHStructureBuilder + { + public: + BVHStructureBuilder(); + ~BVHStructureBuilder(); + + bool loadFromDesc(const PxBVHStructureDesc& desc); + + bool save(PxOutputStream& stream, bool platformMismatch) const; + + void moveData(Gu::BVHStructureData& bvhData); + + private: + PxBounds3* mBounds; + PxU32 mNumVolumes; + PxU32 mNumNodes; + Gu::BVHNode* mNodes; + PxU32* mIndices; + }; + +} + +#endif + diff --git a/src/PhysX/physx/source/physxcooking/src/Cooking.cpp b/src/PhysX/physx/source/physxcooking/src/Cooking.cpp new file mode 100644 index 000000000..a56376309 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/Cooking.cpp @@ -0,0 +1,554 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxErrorCallback.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "PsFPU.h" +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" +#include "PxSimpleTriangleMesh.h" +#include "PxTriangleMeshDesc.h" +#include "PxConvexMeshDesc.h" +#include "PxCooking.h" +#include "Cooking.h" +#include "mesh/TriangleMeshBuilder.h" +#include "GuConvexMesh.h" +#include "ConvexMeshBuilder.h" +#include "BVHStructureBuilder.h" +#include "QuickHullConvexHullLib.h" +#include "CmIO.h" +#include "PxHeightFieldDesc.h" +#include "GuHeightField.h" +#include "HeightFieldCooking.h" +#include "common/PxPhysicsInsertionCallback.h" +#include "CmUtils.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using namespace physx; +using namespace Gu; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Cooking::setParams(const PxCookingParams& params) +{ + mParams = params; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const PxCookingParams& Cooking::getParams() const +{ + return mParams; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Cooking::platformMismatch() const +{ + // Get current endianness (the one for the platform where cooking is performed) + const PxI8 currentEndian = Ps::littleEndian(); + + const bool mismatch = currentEndian!=1; // The files must be little endian - we don't have big endian platforms anymore. + return mismatch; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Cooking::release() +{ + delete this; + + Ps::Foundation::decRefCount(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Cooking::validateTriangleMesh(const PxTriangleMeshDesc& desc) const +{ + // cooking code does lots of float bitwise reinterpretation that generates exceptions + PX_FPU_GUARD; + + if(!desc.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::validateTriangleMesh: user-provided triangle mesh descriptor is invalid!"); + return false; + } + + // PT: validation code doesn't look at midphase data, so ideally we wouldn't build the midphase structure at all here. + BV4TriangleMeshBuilder builder(mParams); + return builder.loadFromDesc(desc, NULL, true /*doValidate*/); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Cooking::cookTriangleMesh(TriangleMeshBuilder& builder, const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition) const +{ + // cooking code does lots of float bitwise reinterpretation that generates exceptions + PX_FPU_GUARD; + + if (condition) + *condition = PxTriangleMeshCookingResult::eSUCCESS; + if(!builder.loadFromDesc(desc, condition, false)) + { + return false; + } + + builder.save(stream, platformMismatch(), mParams); + return true; +} + +bool Cooking::cookTriangleMesh(const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition) const +{ + if(mParams.midphaseDesc.getType() == PxMeshMidPhase::eBVH33) + { + RTreeTriangleMeshBuilder builder(mParams); + return cookTriangleMesh(builder, desc, stream, condition); + } + else + { + BV4TriangleMeshBuilder builder(mParams); + return cookTriangleMesh(builder, desc, stream, condition); + } +} + +PxTriangleMesh* Cooking::createTriangleMesh(TriangleMeshBuilder& builder, const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition) const +{ + // cooking code does lots of float bitwise reinterpretation that generates exceptions + PX_FPU_GUARD; + + if (condition) + *condition = PxTriangleMeshCookingResult::eSUCCESS; + if(!builder.loadFromDesc(desc, condition, false)) + { + return NULL; + } + + // check if the indices can be moved from 32bits to 16bits + if(!(mParams.meshPreprocessParams & PxMeshPreprocessingFlag::eFORCE_32BIT_INDICES)) + builder.checkMeshIndicesSize(); + + PxConcreteType::Enum type; + if(builder.getMidphaseID()==PxMeshMidPhase::eBVH33) + type = PxConcreteType::eTRIANGLE_MESH_BVH33; + else + type = PxConcreteType::eTRIANGLE_MESH_BVH34; + + return static_cast(insertionCallback.buildObjectFromData(type, &builder.getMeshData())); +} + +PxTriangleMesh* Cooking::createTriangleMesh(const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition) const +{ + if(mParams.midphaseDesc.getType() == PxMeshMidPhase::eBVH33) + { + RTreeTriangleMeshBuilder builder(mParams); + return createTriangleMesh(builder, desc, insertionCallback, condition); + } + else + { + BV4TriangleMeshBuilder builder(mParams); + return createTriangleMesh(builder, desc, insertionCallback, condition); + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cook convex mesh from given desc, internal function to be shared between create/cook convex mesh +bool Cooking::cookConvexMeshInternal(const PxConvexMeshDesc& desc_, ConvexMeshBuilder& meshBuilder, ConvexHullLib* hullLib, + PxConvexMeshCookingResult::Enum* condition) const +{ + if (condition) + *condition = PxConvexMeshCookingResult::eFAILURE; + + if (!desc_.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::cookConvexMesh: user-provided convex mesh descriptor is invalid!"); + return false; + } + + if (mParams.areaTestEpsilon <= 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::cookConvexMesh: provided cooking parameter areaTestEpsilon is invalid!"); + return false; + } + + if(mParams.planeTolerance < 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::cookConvexMesh: provided cooking parameter planeTolerance is invalid!"); + return false; + } + + PxConvexMeshDesc desc = desc_; + bool polygonsLimitReached = false; + + // the convex will be cooked from provided points + if (desc_.flags & PxConvexFlag::eCOMPUTE_CONVEX) + { + PX_ASSERT(hullLib); + + // clean up the indices information, it could have been set by accident + desc.flags &= ~PxConvexFlag::e16_BIT_INDICES; + desc.indices.count = 0; + desc.indices.data = NULL; + desc.indices.stride = 0; + desc.polygons.count = 0; + desc.polygons.data = NULL; + desc.polygons.stride = 0; + + PxConvexMeshCookingResult::Enum res = hullLib->createConvexHull(); + if (res == PxConvexMeshCookingResult::eSUCCESS || res == PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED) + { + if (res == PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED) + polygonsLimitReached = true; + + hullLib->fillConvexMeshDesc(desc); + } + else + { + if (res == PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED) + { + *condition = PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED; + } + + return false; + } + } + + if (desc.points.count >= 256) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Cooking::cookConvexMesh: user-provided hull must have less than 256 vertices!"); + return false; + } + + if (!meshBuilder.build(desc, mParams.gaussMapLimit, false, hullLib)) + { + return false; + } + + if (condition) + { + *condition = polygonsLimitReached ? PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED : PxConvexMeshCookingResult::eSUCCESS; + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// cook convex mesh from given desc, save the results into stream +bool Cooking::cookConvexMesh(const PxConvexMeshDesc& desc_, PxOutputStream& stream, PxConvexMeshCookingResult::Enum* condition) const +{ + PX_FPU_GUARD; + // choose cooking library if needed + ConvexHullLib* hullLib = NULL; + PxConvexMeshDesc desc = desc_; + + if(desc_.flags & PxConvexFlag::eCOMPUTE_CONVEX) + { + const PxU16 gpuMaxVertsLimit = 64; + + // GRB supports 64 verts max + if(desc_.flags & PxConvexFlag::eGPU_COMPATIBLE) + { + desc.vertexLimit = PxMin(desc.vertexLimit, gpuMaxVertsLimit); + } + + hullLib = PX_NEW(QuickHullConvexHullLib) (desc, mParams); + } + + ConvexMeshBuilder meshBuilder(mParams.buildGPUData); + if(!cookConvexMeshInternal(desc,meshBuilder,hullLib , condition)) + { + if(hullLib) + PX_DELETE(hullLib); + return false; + } + + // save the cooked results into stream + if(!meshBuilder.save(stream, platformMismatch())) + { + if (condition) + { + *condition = PxConvexMeshCookingResult::eFAILURE; + } + if(hullLib) + PX_DELETE(hullLib); + return false; + } + + if(hullLib) + PX_DELETE(hullLib); + return true; +} + +////////////////////////////////////////////////////////////////////////// +// cook convex mesh from given desc, copy the results into internal convex mesh +// and insert the mesh into PxPhysics +PxConvexMesh* Cooking::createConvexMesh(const PxConvexMeshDesc& desc_, PxPhysicsInsertionCallback& insertionCallback, PxConvexMeshCookingResult::Enum* condition) const +{ + PX_FPU_GUARD; + // choose cooking library if needed + ConvexHullLib* hullLib = NULL; + PxConvexMeshDesc desc = desc_; + + if(desc.flags & PxConvexFlag::eCOMPUTE_CONVEX) + { + const PxU16 gpuMaxVertsLimit = 64; + + // GRB supports 64 verts max + if(desc_.flags & PxConvexFlag::eGPU_COMPATIBLE) + { + desc.vertexLimit = PxMin(desc.vertexLimit, gpuMaxVertsLimit); + } + + hullLib = PX_NEW(QuickHullConvexHullLib) (desc, mParams); + } + + // cook the mesh + ConvexMeshBuilder meshBuilder(mParams.buildGPUData); + if (!cookConvexMeshInternal(desc, meshBuilder, hullLib, condition)) + { + if(hullLib) + PX_DELETE(hullLib); + return NULL; + } + + // copy the constructed data into the new mesh + + PxU32 nb = 0; + Gu::ConvexHullData meshData; + meshBuilder.copy(meshData, nb); + + // insert into physics + Gu::ConvexMesh* convexMesh = static_cast(insertionCallback.buildObjectFromData(PxConcreteType::eCONVEX_MESH, &meshData)); + if (!convexMesh) + { + if(condition) + *condition = PxConvexMeshCookingResult::eFAILURE; + if (hullLib) + PX_DELETE(hullLib); + return NULL; + } + + convexMesh->setNb(nb); + convexMesh->setMass(meshBuilder.getMass()); + convexMesh->setInertia(meshBuilder.getInertia()); + if(meshBuilder.getBigConvexData()) + { + convexMesh->setBigConvexData(meshBuilder.getBigConvexData()); + meshBuilder.setBigConvexData(NULL); + } + + if(hullLib) + PX_DELETE(hullLib); + return convexMesh; +} + +////////////////////////////////////////////////////////////////////////// + +bool Cooking::validateConvexMesh(const PxConvexMeshDesc& desc) const +{ + ConvexMeshBuilder mesh(mParams.buildGPUData); + return mesh.build(desc, mParams.gaussMapLimit, true); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Cooking::computeHullPolygons(const PxSimpleTriangleMesh& mesh, PxAllocatorCallback& inCallback,PxU32& nbVerts, PxVec3*& vertices, + PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& hullPolygons) const +{ + PxVec3* geometry = reinterpret_cast(PxAlloca(sizeof(PxVec3)*mesh.points.count)); + Cooking::gatherStrided(mesh.points.data, geometry, mesh.points.count, sizeof(PxVec3), mesh.points.stride); + + PxU32* topology = reinterpret_cast(PxAlloca(sizeof(PxU32)*3*mesh.triangles.count)); + if (mesh.flags & PxMeshFlag::e16_BIT_INDICES) + { + // conversion; 16 bit index -> 32 bit index & stride + PxU32* dest = topology; + const PxU32* pastLastDest = topology + 3*mesh.triangles.count; + const PxU8* source = reinterpret_cast(mesh.triangles.data); + while (dest < pastLastDest) + { + const PxU16 * trig16 = reinterpret_cast(source); + *dest++ = trig16[0]; + *dest++ = trig16[1]; + *dest++ = trig16[2]; + source += mesh.triangles.stride; + } + } + else + { + Cooking::gatherStrided(mesh.triangles.data, topology, mesh.triangles.count, sizeof(PxU32) * 3, mesh.triangles.stride); + } + + ConvexMeshBuilder meshBuilder(mParams.buildGPUData); + if(!meshBuilder.computeHullPolygons(mesh.points.count,geometry,mesh.triangles.count,topology,inCallback, nbVerts, vertices,nbIndices,indices,nbPolygons,hullPolygons)) + return false; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Cooking::cookHeightField(const PxHeightFieldDesc& desc, PxOutputStream& stream) const +{ + PX_FPU_GUARD; + + if(!desc.isValid()) + { + #if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!"); + #endif + return false; + } + + Gu::HeightField hf(NULL); + + if(!hf.loadFromDesc(desc)) + { + hf.releaseMemory(); + return false; + } + + if (!saveHeightField(hf, stream, platformMismatch())) + { + hf.releaseMemory(); + return false; + } + + hf.releaseMemory(); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxHeightField* Cooking::createHeightField(const PxHeightFieldDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const +{ + PX_FPU_GUARD; + + if(!desc.isValid()) + { + #if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!"); + #endif + return NULL; + } + + Gu::HeightField* hf; + PX_NEW_SERIALIZED(hf, Gu::HeightField)(NULL); + + if(!hf->loadFromDesc(desc)) + { + PX_DELETE(hf); + return NULL; + } + + // create heightfield and set the HF data + Gu::HeightField* heightField = static_cast(insertionCallback.buildObjectFromData(PxConcreteType::eHEIGHTFIELD, &hf->mData)); + if(!heightField) + { + PX_DELETE(hf); + return NULL; + } + + // copy the Gu::HeightField variables + heightField->mSampleStride = hf->mSampleStride; + heightField->mNbSamples = hf->mNbSamples; + heightField->mMinHeight = hf->mMinHeight; + heightField->mMaxHeight = hf->mMaxHeight; + heightField->mModifyCount = hf->mModifyCount; + + PX_DELETE(hf); + return heightField; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool Cooking::cookBVHStructure(const PxBVHStructureDesc& desc, PxOutputStream& stream) const +{ + PX_FPU_GUARD; + + if(!desc.isValid()) + { + #if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!"); + #endif + return false; + } + + BVHStructureBuilder builder; + if(!builder.loadFromDesc(desc)) + { + return false; + } + + builder.save(stream, platformMismatch()); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxBVHStructure* Cooking::createBVHStructure(const PxBVHStructureDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const +{ + PX_FPU_GUARD; + + if(!desc.isValid()) + { + #if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Cooking::createHeightField: user-provided heightfield descriptor is invalid!"); + #endif + return NULL; + } + + BVHStructureBuilder builder; + if(!builder.loadFromDesc(desc)) + { + return NULL; + } + + Gu::BVHStructureData bvhData; + builder.moveData(bvhData); + Gu::BVHStructure* bvhStructure = static_cast(insertionCallback.buildObjectFromData(PxConcreteType::eBVH_STRUCTURE, &bvhData)); + + return bvhStructure; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxCooking* PxCreateCooking(PxU32 /*version*/, PxFoundation& foundation, const PxCookingParams& params) +{ + PX_ASSERT(static_cast(&foundation) == &Ps::Foundation::getInstance()); + PX_UNUSED(foundation); + + Ps::Foundation::incRefCount(); + + return PX_NEW(Cooking)(params); +} + diff --git a/src/PhysX/physx/source/physxcooking/src/Cooking.h b/src/PhysX/physx/source/physxcooking/src/Cooking.h new file mode 100644 index 000000000..7ee48ba51 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/Cooking.h @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PSCOOKING_H +#define PX_PSCOOKING_H + +#include "foundation/PxMemory.h" +#include "PxCooking.h" +#include "PsUserAllocated.h" + +namespace physx +{ +class TriangleMeshBuilder; +class ConvexMeshBuilder; +class ConvexHullLib; + +class Cooking: public PxCooking, public Ps::UserAllocated +{ +public: + Cooking(const PxCookingParams& params): mParams(params) {} + + virtual void release(); + virtual void setParams(const PxCookingParams& params); + virtual const PxCookingParams& getParams() const; + virtual bool platformMismatch() const; + virtual bool cookTriangleMesh(const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition = NULL) const; + virtual PxTriangleMesh* createTriangleMesh(const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition = NULL) const; + virtual bool validateTriangleMesh(const PxTriangleMeshDesc& desc) const; + + virtual bool cookConvexMesh(const PxConvexMeshDesc& desc, PxOutputStream& stream, PxConvexMeshCookingResult::Enum* condition) const; + virtual PxConvexMesh* createConvexMesh(const PxConvexMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxConvexMeshCookingResult::Enum* condition) const; + virtual bool validateConvexMesh(const PxConvexMeshDesc& desc) const; + virtual bool computeHullPolygons(const PxSimpleTriangleMesh& mesh, PxAllocatorCallback& inCallback,PxU32& nbVerts, PxVec3*& vertices, + PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& hullPolygons) const; + virtual bool cookHeightField(const PxHeightFieldDesc& desc, PxOutputStream& stream) const; + virtual PxHeightField* createHeightField(const PxHeightFieldDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const; + virtual bool cookBVHStructure(const PxBVHStructureDesc& desc, PxOutputStream& stream) const; + virtual PxBVHStructure* createBVHStructure(const PxBVHStructureDesc& desc, PxPhysicsInsertionCallback& insertionCallback) const; + + PX_FORCE_INLINE static void gatherStrided(const void* src, void* dst, PxU32 nbElem, PxU32 elemSize, PxU32 stride) + { + const PxU8* s = reinterpret_cast(src); + PxU8* d = reinterpret_cast(dst); + while(nbElem--) + { + PxMemCopy(d, s, elemSize); + d += elemSize; + s += stride; + } + } + +private: + bool cookConvexMeshInternal(const PxConvexMeshDesc& desc, ConvexMeshBuilder& meshBuilder, ConvexHullLib* hullLib, PxConvexMeshCookingResult::Enum* condition) const; + bool cookTriangleMesh(TriangleMeshBuilder& builder, const PxTriangleMeshDesc& desc, PxOutputStream& stream, PxTriangleMeshCookingResult::Enum* condition) const; + + PxTriangleMesh* createTriangleMesh(TriangleMeshBuilder& builder, const PxTriangleMeshDesc& desc, PxPhysicsInsertionCallback& insertionCallback, PxTriangleMeshCookingResult::Enum* condition) const; + +private: + PxCookingParams mParams; + +}; + +} +#endif //#define PX_PSCOOKING_H diff --git a/src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp b/src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp new file mode 100644 index 000000000..ed50a5c9d --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMath.h" +#include "CookingUtils.h" +#include "CmRadixSortBuffered.h" +#include "PsAllocator.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Cm; + +ReducedVertexCloud::ReducedVertexCloud(const PxVec3* verts, PxU32 nb_verts) : mNbRVerts(0), mRVerts(NULL), mXRef(NULL) +{ + mVerts = verts; + mNbVerts = nb_verts; +} + +ReducedVertexCloud::~ReducedVertexCloud() +{ + Clean(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Frees used ram. +* \return Self-reference +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +ReducedVertexCloud& ReducedVertexCloud::Clean() +{ + PX_DELETE_POD(mXRef); + PX_FREE_AND_RESET(mRVerts); + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Reduction method. Use this to create a minimal vertex cloud. +* \param rc [out] result structure +* \return true if success +* \warning This is not about welding nearby vertices, here we look for real redundant ones. +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool ReducedVertexCloud::Reduce(REDUCEDCLOUD* rc) +{ + Clean(); + + mXRef = PX_NEW(PxU32)[mNbVerts]; + + float* f = PX_NEW_TEMP(float)[mNbVerts]; + + for(PxU32 i=0;i(f), mNbVerts, RADIX_UNSIGNED); + + for(PxU32 i=0;i(f), mNbVerts, RADIX_UNSIGNED); + + for(PxU32 i=0;i(f), mNbVerts, RADIX_UNSIGNED).GetRanks(); + + PX_DELETE_POD(f); + + mNbRVerts = 0; + const PxU32 Junk[] = {PX_INVALID_U32, PX_INVALID_U32, PX_INVALID_U32}; + const PxU32* Previous = Junk; + mRVerts = reinterpret_cast(PX_ALLOC(sizeof(PxVec3) * mNbVerts, "PxVec3")); + PxU32 Nb = mNbVerts; + while(Nb--) + { + const PxU32 Vertex = *Sorted++; // Vertex number + + const PxU32* current = reinterpret_cast(&mVerts[Vertex]); + if(current[0]!=Previous[0] || current[1]!=Previous[1] || current[2]!=Previous[2]) + mRVerts[mNbRVerts++] = mVerts[Vertex]; + + Previous = current; + + mXRef[Vertex] = mNbRVerts-1; + } + + if(rc) + { + rc->CrossRef = mXRef; + rc->NbRVerts = mNbRVerts; + rc->RVerts = mRVerts; + } + return true; +} diff --git a/src/PhysX/physx/source/physxcooking/src/CookingUtils.h b/src/PhysX/physx/source/physxcooking/src/CookingUtils.h new file mode 100644 index 000000000..7f5544b71 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/CookingUtils.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COOKINGUTILS +#define PX_COOKINGUTILS + +#include "foundation/PxVec3.h" +#include "PxPhysXConfig.h" + +namespace physx +{ + //! Vertex cloud reduction result structure + struct REDUCEDCLOUD + { + // Out + PxVec3* RVerts; //!< Reduced list + PxU32 NbRVerts; //!< Reduced number of vertices + PxU32* CrossRef; //!< nb_verts remapped indices + }; + + class ReducedVertexCloud + { + public: + // Constructors/destructor + ReducedVertexCloud(const PxVec3* verts, PxU32 nb_verts); + ~ReducedVertexCloud(); + // Free used bytes + ReducedVertexCloud& Clean(); + // Cloud reduction + bool Reduce(REDUCEDCLOUD* rc=NULL); + // Data access + PX_INLINE PxU32 GetNbVerts() const { return mNbVerts; } + PX_INLINE PxU32 GetNbReducedVerts() const { return mNbRVerts; } + PX_INLINE const PxVec3* GetReducedVerts() const { return mRVerts; } + PX_INLINE const PxVec3& GetReducedVertex(PxU32 i) const { return mRVerts[i]; } + PX_INLINE const PxU32* GetCrossRefTable() const { return mXRef; } + + private: + // Original vertex cloud + PxU32 mNbVerts; //!< Number of vertices + const PxVec3* mVerts; //!< List of vertices (pointer copy) + + // Reduced vertex cloud + PxU32 mNbRVerts; //!< Reduced number of vertices + PxVec3* mRVerts; //!< Reduced list of vertices + PxU32* mXRef; //!< Cross-reference table (used to remap topologies) + }; + +} + +#endif + diff --git a/src/PhysX/physx/source/physxcooking/src/EdgeList.cpp b/src/PhysX/physx/source/physxcooking/src/EdgeList.cpp new file mode 100644 index 000000000..aca9db0d8 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/EdgeList.cpp @@ -0,0 +1,753 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxMemory.h" +#include "EdgeList.h" +#include "PxTriangle.h" +#include "PsMathUtils.h" +#include "CmRadixSortBuffered.h" +#include "GuSerialize.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Gu; + +Gu::EdgeList::EdgeList() +{ + mData.mNbEdges = 0; + mData.mEdgeFaces = NULL; + mData.mEdges = NULL; + mData.mEdgeToTriangles = NULL; + mData.mFacesByEdges = NULL; +} + +Gu::EdgeList::~EdgeList() +{ + PX_FREE_AND_RESET(mData.mFacesByEdges); + PX_FREE_AND_RESET(mData.mEdgeToTriangles); + PX_FREE_AND_RESET(mData.mEdges); + PX_DELETE_POD(mData.mEdgeFaces); +} + +bool Gu::EdgeList::load(PxInputStream& stream) +{ + // Import header + PxU32 Version; + bool Mismatch; + if(!ReadHeader('E', 'D', 'G', 'E', Version, Mismatch, stream)) + return false; + + // Import edges + mData.mNbEdges = readDword(Mismatch, stream); + //mEdges = ICE_NEW_MEM(Edge[mNbEdges],Edge); + mData.mEdges = reinterpret_cast(PX_ALLOC(sizeof(EdgeData)*mData.mNbEdges, "EdgeData")); + stream.read(mData.mEdges, sizeof(EdgeData)*mData.mNbEdges); + + mData.mNbFaces = readDword(Mismatch, stream); + //mEdgeFaces = ICE_NEW_MEM(EdgeTriangle[mNbFaces],EdgeTriangle); + mData.mEdgeFaces = reinterpret_cast(PX_ALLOC(sizeof(EdgeTriangleData)*mData.mNbFaces, "EdgeTriangleData")); + stream.read(mData.mEdgeFaces, sizeof(EdgeTriangleData)*mData.mNbFaces); + + //mEdgeToTriangles = ICE_NEW_MEM(EdgeDesc[mNbEdges],EdgeDesc); + mData.mEdgeToTriangles = reinterpret_cast(PX_ALLOC(sizeof(EdgeDescData)*mData.mNbEdges, "EdgeDescData")); + stream.read(mData.mEdgeToTriangles, sizeof(EdgeDescData)*mData.mNbEdges); + + + PxU32 LastOffset = mData.mEdgeToTriangles[mData.mNbEdges-1].Offset + mData.mEdgeToTriangles[mData.mNbEdges-1].Count; + mData.mFacesByEdges = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*LastOffset, "EdgeList FacesByEdges")); + stream.read(mData.mFacesByEdges, sizeof(PxU32)*LastOffset); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes the edge-list. + * \param create [in] edge-list creation structure + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Gu::EdgeListBuilder::init(const EDGELISTCREATE& create) +{ + bool FacesToEdges = create.Verts ? true : create.FacesToEdges; + bool EdgesToFaces = create.Verts ? true : create.EdgesToFaces; + + // "FacesToEdges" maps each face to three edges. + if(FacesToEdges && !createFacesToEdges(create.NbFaces, create.DFaces, create.WFaces)) + return false; + + // "EdgesToFaces" maps each edge to the set of faces sharing this edge + if(EdgesToFaces && !createEdgesToFaces(create.NbFaces, create.DFaces, create.WFaces)) + return false; + + // Create active edges + if(create.Verts && !computeActiveEdges(create.NbFaces, create.DFaces, create.WFaces, create.Verts, create.Epsilon)) + return false; + + // Get rid of useless data + if(!create.FacesToEdges) + { + PX_FREE_AND_RESET(mData.mEdgeFaces); + } + if(!create.EdgesToFaces) + { + PX_FREE_AND_RESET(mData.mEdgeToTriangles); + PX_FREE_AND_RESET(mData.mFacesByEdges); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes FacesToEdges. + * After the call: + * - mNbEdges is updated with the number of non-redundant edges + * - mEdges is a list of mNbEdges edges (one edge is 2 vertex-references) + * - mEdgesRef is a list of nbfaces structures with 3 indexes in mEdges for each face + * + * \param nb_faces [in] a number of triangles + * \param dfaces [in] list of triangles with PxU32 vertex references (or NULL) + * \param wfaces [in] list of triangles with PxU16 vertex references (or NULL) + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Gu::EdgeListBuilder::createFacesToEdges(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces) +{ + // Checkings + if(!nb_faces || (!dfaces && !wfaces)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "EdgeList::CreateFacesToEdges: NULL parameter!"); + return false; + } + + if(mData.mEdgeFaces) + return true; // Already computed! + + // 1) Get some bytes: I need one EdgesRefs for each face, and some temp buffers + mData.mEdgeFaces = PX_NEW(EdgeTriangleData)[nb_faces]; // Link faces to edges + PxU32* VRefs0 = PX_NEW_TEMP(PxU32)[nb_faces*3]; // Temp storage + PxU32* VRefs1 = PX_NEW_TEMP(PxU32)[nb_faces*3]; // Temp storage + EdgeData* Buffer = PX_NEW_TEMP(EdgeData)[nb_faces*3]; // Temp storage + + // 2) Create a full redundant list of 3 edges / face. + for(PxU32 i=0;i stored in temp buffer + Buffer[mData.mNbEdges].Ref0 = SortedRef0; + Buffer[mData.mNbEdges].Ref1 = SortedRef1; + mData.mNbEdges++; + } + PreviousRef0 = SortedRef0; + PreviousRef1 = SortedRef1; + + // Create mEdgesRef on the fly + mData.mEdgeFaces[Face/3].mLink[ID] = mData.mNbEdges-1; + } + + // 5) Here, mNbEdges==#non redundant edges + mData.mEdges = reinterpret_cast(PX_ALLOC(sizeof(EdgeData)*mData.mNbEdges, "EdgeData")); + + // Create real edges-list. + PxMemCopy(mData.mEdges, Buffer, mData.mNbEdges*sizeof(EdgeData)); + + // 6) Free ram and exit + PX_DELETE_POD(Buffer); + PX_DELETE_POD(VRefs1); + PX_DELETE_POD(VRefs0); + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes EdgesToFaces. + * After the call: + * - mEdgeToTriangles is created + * - mFacesByEdges is created + * + * \param nb_faces [in] a number of triangles + * \param dfaces [in] list of triangles with PxU32 vertex references (or NULL) + * \param wfaces [in] list of triangles with PxU16 vertex references (or NULL) + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Gu::EdgeListBuilder::createEdgesToFaces(PxU32 nb_faces, const PxU32* dfaces, const PxU16* wfaces) +{ + // 1) I need FacesToEdges ! + if(!createFacesToEdges(nb_faces, dfaces, wfaces)) + return false; + + // 2) Get some bytes: one Pair structure / edge + mData.mEdgeToTriangles = reinterpret_cast(PX_ALLOC(sizeof(EdgeDescData)*mData.mNbEdges, "EdgeDescData")); + PxMemZero(mData.mEdgeToTriangles, sizeof(EdgeDescData)*mData.mNbEdges); + + // 3) Create Counters, ie compute the #faces sharing each edge + for(PxU32 i=0;i(PX_ALLOC(sizeof(PxU32)*LastOffset, "EdgeListBuilder FacesByEdges")); + + // 5) Create mFacesByEdges + for(PxU32 i=0;i(PX_ALLOC_TEMP(sizeof(bool)*NbEdges, "bool")); + + // Loop through edges and look for convex ones + bool* CurrentMark = ActiveEdges; + + while(NbEdges--) + { + // Get number of triangles sharing current edge + PxU32 Count = ED->Count; + // Boundary edges are active => keep them (actually they're silhouette edges directly) + // Internal edges can be active => test them + // Singular edges ? => discard them + bool Active = false; + if(Count==1) + { + Active = true; + } + else if(Count==2) + { + PxU32 FaceIndex0 = FBE[ED->Offset+0]*3; + PxU32 FaceIndex1 = FBE[ED->Offset+1]*3; + + PxU32 VRef00, VRef01, VRef02; + PxU32 VRef10, VRef11, VRef12; + + if(dfaces) + { + VRef00 = dfaces[FaceIndex0+0]; + VRef01 = dfaces[FaceIndex0+1]; + VRef02 = dfaces[FaceIndex0+2]; + VRef10 = dfaces[FaceIndex1+0]; + VRef11 = dfaces[FaceIndex1+1]; + VRef12 = dfaces[FaceIndex1+2]; + } + else //if(wfaces) + { + PX_ASSERT(wfaces); + VRef00 = wfaces[FaceIndex0+0]; + VRef01 = wfaces[FaceIndex0+1]; + VRef02 = wfaces[FaceIndex0+2]; + VRef10 = wfaces[FaceIndex1+0]; + VRef11 = wfaces[FaceIndex1+1]; + VRef12 = wfaces[FaceIndex1+2]; + } + + { + // We first check the opposite vertex against the plane + + PxU32 Op = OppositeVertex(VRef00, VRef01, VRef02, Edges->Ref0, Edges->Ref1); + + PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]); + + if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges + { + PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); + PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); + + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + const float a = Ps::angle(N0, N1); + + if(fabsf(a)>epsilon) Active = true; + } + else + { + PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); + PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + + if(N0.dot(N1) < -0.999f) + { + Active = true; + } + + } + } + + } + else + { + //Connected to more than 2 + //We need to loop through the triangles and count the number of unique triangles (considering back-face triangles as non-unique). If we end up with more than 2 unique triangles, + //then by definition this is an inactive edge. However, if we end up with 2 unique triangles (say like a double-sided tesselated surface), then it depends on the same rules as above + + PxU32 FaceInd0 = FBE[ED->Offset]*3; + PxU32 VRef00, VRef01, VRef02; + PxU32 VRef10=0, VRef11=0, VRef12=0; + if(dfaces) + { + VRef00 = dfaces[FaceInd0+0]; + VRef01 = dfaces[FaceInd0+1]; + VRef02 = dfaces[FaceInd0+2]; + } + else //if(wfaces) + { + PX_ASSERT(wfaces); + VRef00 = wfaces[FaceInd0+0]; + VRef01 = wfaces[FaceInd0+1]; + VRef02 = wfaces[FaceInd0+2]; + } + + + PxU32 numUniqueTriangles = 1; + bool doubleSided0 = false; + bool doubleSided1 = 0; + + for(PxU32 a = 1; a < Count; ++a) + { + PxU32 FaceInd = FBE[ED->Offset+a]*3; + + PxU32 VRef0, VRef1, VRef2; + if(dfaces) + { + VRef0 = dfaces[FaceInd+0]; + VRef1 = dfaces[FaceInd+1]; + VRef2 = dfaces[FaceInd+2]; + } + else //if(wfaces) + { + PX_ASSERT(wfaces); + VRef0 = wfaces[FaceInd+0]; + VRef1 = wfaces[FaceInd+1]; + VRef2 = wfaces[FaceInd+2]; + } + + if(((VRef0 != VRef00) && (VRef0 != VRef01) && (VRef0 != VRef02)) || + ((VRef1 != VRef00) && (VRef1 != VRef01) && (VRef1 != VRef02)) || + ((VRef2 != VRef00) && (VRef2 != VRef01) && (VRef2 != VRef02))) + { + //Not the same as trig 0 + if(numUniqueTriangles == 2) + { + if(((VRef0 != VRef10) && (VRef0 != VRef11) && (VRef0 != VRef12)) || + ((VRef1 != VRef10) && (VRef1 != VRef11) && (VRef1 != VRef12)) || + ((VRef2 != VRef10) && (VRef2 != VRef11) && (VRef2 != VRef12))) + { + //Too many unique triangles - terminate and mark as inactive + numUniqueTriangles++; + break; + } + else + { + PxTriangle T0(verts[VRef10], verts[VRef11], verts[VRef12]); + PxTriangle T1(verts[VRef0], verts[VRef1], verts[VRef2]); + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + + if(N0.dot(N1) < -0.999f) + doubleSided1 = true; + } + } + else + { + VRef10 = VRef0; + VRef11 = VRef1; + VRef12 = VRef2; + numUniqueTriangles++; + } + } + else + { + //Check for double sided... + PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); + PxTriangle T1(verts[VRef0], verts[VRef1], verts[VRef2]); + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + + if(N0.dot(N1) < -0.999f) + doubleSided0 = true; + } + } + + if(numUniqueTriangles == 1) + Active = true; + if(numUniqueTriangles == 2) + { + //Potentially active. Let's check the angles between the surfaces... + + if(doubleSided0 || doubleSided1) + { + + // Plane PL1 = faces[FBE[ED->Offset+1]].PlaneEquation(verts); + PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]); + + // if(PL1.Distance(verts[Op])<-epsilon) Active = true; + //if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges + //KS - can't test signed distance for concave edges. This is a double-sided poly + { + PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); + PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); + + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + const float a = Ps::angle(N0, N1); + + if(fabsf(a)>epsilon) + Active = true; + } + } + else + { + + //Not double sided...must have had a bunch of duplicate triangles!!!! + //Treat as normal + PxU32 Op = OppositeVertex(VRef00, VRef01, VRef02, Edges->Ref0, Edges->Ref1); + + // Plane PL1 = faces[FBE[ED->Offset+1]].PlaneEquation(verts); + PxPlane PL1(verts[VRef10], verts[VRef11], verts[VRef12]); + + // if(PL1.Distance(verts[Op])<-epsilon) Active = true; + if(PL1.distance(verts[Op])<0.0f) // If opposite vertex is below the plane, i.e. we discard concave edges + { + PxTriangle T0(verts[VRef00], verts[VRef01], verts[VRef02]); + PxTriangle T1(verts[VRef10], verts[VRef11], verts[VRef12]); + + PxVec3 N0, N1; + T0.normal(N0); + T1.normal(N1); + const float a = Ps::angle(N0, N1); + + if(fabsf(a)>epsilon) + Active = true; + } + } + } + else + { + //Lots of triangles all smooshed together. Just activate the edge in this case + Active = true; + } + + } + + *CurrentMark++ = Active; + ED++; + Edges++; + } + + + // Now copy bits back into already existing edge structures + // - first in edge triangles + for(PxU32 i=0;iMaxIndex) MaxIndex = VRef0; + if(VRef1>MaxIndex) MaxIndex = VRef1; + if(VRef2>MaxIndex) MaxIndex = VRef2; + } + + MaxIndex++; + bool* ActiveVerts = reinterpret_cast(PX_ALLOC_TEMP(sizeof(bool)*MaxIndex, "bool")); + PxMemZero(ActiveVerts, MaxIndex*sizeof(bool)); + + PX_ASSERT(dfaces || wfaces); + for(PxU32 i=0;i mark edge vertices as active + PxU32 r0, r1; + if(j==0) { r0=0; r1=1; } + else if(j==1) { r0=1; r1=2; } + else /*if(j==2)*/ { PX_ASSERT(j==2); r0=0; r1=2; } + ActiveVerts[VRef[r0]] = ActiveVerts[VRef[r1]] = true; + } + } + } + +/* for(PxU32 i=0;i mark edge vertices as inactive + PxU32 r0, r1; + if(j==0) { r0=0; r1=1; } + if(j==1) { r0=1; r1=2; } + if(j==2) { r0=0; r1=2; } + ActiveVerts[VRef[r0]] = ActiveVerts[VRef[r1]] = false; + } + } + }*/ + + // Now stuff this into the structure + for(PxU32 i=0;i(&v.x); + const PxU32 f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0 + return (f>>22)^(f>>12)^(f); +} + +static PX_FORCE_INLINE PxU32 getHashValue(const Indices& v) +{ +// const PxU32* h = v.mRef; +// const PxU32 f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0 +// return (f>>22)^(f>>12)^(f); + + PxU32 a = v.mRef[0]; + PxU32 b = v.mRef[1]; + PxU32 c = v.mRef[2]; + a=a-b; a=a-c; a=a^(c >> 13); + b=b-c; b=b-a; b=b^(a << 8); + c=c-a; c=c-b; c=c^(b >> 13); + a=a-b; a=a-c; a=a^(c >> 12); + b=b-c; b=b-a; b=b^(a << 16); + c=c-a; c=c-b; c=c^(b >> 5); + a=a-b; a=a-c; a=a^(c >> 3); + b=b-c; b=b-a; b=b^(a << 10); + c=c-a; c=c-b; c=c^(b >> 15); + return c; +} + +MeshCleaner::MeshCleaner(PxU32 nbVerts, const PxVec3* srcVerts, PxU32 nbTris, const PxU32* srcIndices, PxF32 meshWeldTolerance) +{ + PxVec3* cleanVerts = reinterpret_cast(PX_ALLOC(sizeof(PxVec3)*nbVerts, "MeshCleaner")); + PX_ASSERT(cleanVerts); + + PxU32* indices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTris*3, "MeshCleaner")); + + PxU32* remapTriangles = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbTris, "MeshCleaner")); + + PxU32* vertexIndices = NULL; + if(meshWeldTolerance!=0.0f) + { + vertexIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbVerts, "MeshCleaner")); + const PxF32 weldTolerance = 1.0f / meshWeldTolerance; + // snap to grid + for(PxU32 i=0; i(PX_ALLOC(sizeof(PxU32)*(hashSize + maxNbElems), "MeshCleaner")); + PX_ASSERT(hashTable); + memset(hashTable, 0xff, hashSize * sizeof(PxU32)); + PxU32* const next = hashTable + hashSize; + + PxU32* remapVerts = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*nbVerts, "MeshCleaner")); + memset(remapVerts, 0xff, nbVerts * sizeof(PxU32)); + + for(PxU32 i=0;i=nbVerts || vref1>=nbVerts || vref2>=nbVerts) + continue; + + // PT: you can still get zero-area faces when the 3 vertices are perfectly aligned + const PxVec3& p0 = srcVerts[vref0]; + const PxVec3& p1 = srcVerts[vref1]; + const PxVec3& p2 = srcVerts[vref2]; + const float area2 = ((p0 - p1).cross(p0 - p2)).magnitudeSquared(); + if(area2==0.0f) + continue; + + vref0 = remapVerts[vref0]; + vref1 = remapVerts[vref1]; + vref2 = remapVerts[vref2]; + if(vref0==vref1 || vref1==vref2 || vref2==vref0) + continue; + + indices[nbCleanedTris*3+0] = vref0; + indices[nbCleanedTris*3+1] = vref1; + indices[nbCleanedTris*3+2] = vref2; + remapTriangles[nbCleanedTris] = i; + nbCleanedTris++; + } + PX_FREE(remapVerts); + + PxU32 nbToGo = nbCleanedTris; + nbCleanedTris = 0; + memset(hashTable, 0xff, hashSize * sizeof(PxU32)); + + Indices* const I = reinterpret_cast(indices); + bool idtRemap = true; + for(PxU32 i=0;i +PxU32 kmeans_cluster(const Vec *input, + PxU32 inputCount, + PxU32 clumpCount, + Vec *clusters, + PxU32 *outputIndices, + Type threshold, // controls how long it works to converge towards a least errors solution. + Type collapseDistance) // distance between clumps to consider them to be essentially equal. +{ + PxU32 convergeCount = 64; // maximum number of iterations attempting to converge to a solution.. + PxU32* counts = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxU32)*clumpCount, "PxU32")); + Type error=0; + if ( inputCount <= clumpCount ) // if the number of input points is less than our clumping size, just return the input points. + { + clumpCount = inputCount; + for (PxU32 i=0; i (PX_ALLOC_TEMP(sizeof(PxVec3)*clumpCount, "PxVec3")); + + // Take a sampling of the input points as initial centroid estimates. + for (PxU32 i=0; i threshold ); // keep going until the error is reduced by this threshold amount. + + PX_FREE(centroids); + } + + // ok..now we prune the clumps if necessary. + // The rules are; first, if a clump has no 'counts' then we prune it as it's unused. + // The second, is if the centroid of this clump is essentially the same (based on the distance tolerance) + // as an existing clump, then it is pruned and all indices which used to point to it, now point to the one + // it is closest too. + PxU32 outCount = 0; // number of clumps output after pruning performed. + Type d2 = collapseDistance*collapseDistance; // squared collapse distance. + for (PxU32 i=0; i(input, inputSize, clumpCount, outputClusters, outputIndices, errorThreshold, collapseDistance); +} + +class QuantizerImpl : public Quantizer, public Ps::UserAllocated +{ +public: + QuantizerImpl(void) + { + mScale = PxVec3(1.0f, 1.0f, 1.0f); + mCenter = PxVec3(0.0f, 0.0f, 0.0f); + } + + // Use the k-means quantizer, similar results, but much slower. + virtual const PxVec3 * kmeansQuantize3D(PxU32 vcount, + const PxVec3 *vertices, + PxU32 stride, + bool denormalizeResults, + PxU32 maxVertices, + PxU32 &outVertsCount) + { + const PxVec3 *ret = NULL; + outVertsCount = 0; + mNormalizedInput.clear(); + mQuantizedOutput.clear(); + + if ( vcount > 0 ) + { + normalizeInput(vcount,vertices, stride); + + PxVec3* quantizedOutput = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxVec3)*vcount, "PxVec3")); + PxU32* quantizedIndices = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxU32)*vcount, "PxU32")); + outVertsCount = kmeans_cluster3d(&mNormalizedInput[0], vcount, maxVertices, quantizedOutput, quantizedIndices, 0.01f, 0.0001f ); + if ( outVertsCount > 0 ) + { + if ( denormalizeResults ) + { + for (PxU32 i=0; i (vertices); + mNormalizedInput.clear(); + mQuantizedOutput.clear(); + PxBounds3 bounds; + bounds.setEmpty(); + for (PxU32 i=0; i (vtx); + vtx += stride; + + bounds.include(v); + } + + mCenter = bounds.getCenter(); + + PxVec3 dim = bounds.getDimensions(); + dim *= 1.001f; + mScale = dim*0.5f; + + for (PxU32 i = 0; i < 3; i++) + { + if(dim[i] == 0) + mScale[i] = 1.0f; + } + + PxVec3 recip; + recip.x = 1.0f / mScale.x; + recip.y = 1.0f / mScale.y; + recip.z = 1.0f / mScale.z; + + vtx = reinterpret_cast (vertices); + for (PxU32 i=0; i (vtx); + vtx += stride; + + v = (v - mCenter).multiply(recip); + + mNormalizedInput.pushBack(v); + } + } + + virtual ~QuantizerImpl(void) + { + + } + + private: + PxVec3 mScale; + PxVec3 mCenter; + Ps::Array mNormalizedInput; + Ps::Array mQuantizedOutput; +}; + +Quantizer * physx::createQuantizer(void) +{ + QuantizerImpl *m = PX_NEW(QuantizerImpl); + return static_cast< Quantizer *>(m); +} diff --git a/src/PhysX/physx/source/physxcooking/src/Quantizer.h b/src/PhysX/physx/source/physxcooking/src/Quantizer.h new file mode 100644 index 000000000..cbab0e12e --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/Quantizer.h @@ -0,0 +1,76 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef QUANTIZER_H +#define QUANTIZER_H + +#include "foundation/Px.h" + +namespace physx +{ + + ////////////////////////////////////////////////////////////////////////// + // K-means quantization class + // see http://en.wikipedia.org/wiki/K-means_clustering + // implementation from John Ratcliff http://codesuppository.blogspot.ch/2010/12/k-means-clustering-algorithm.html + class Quantizer + { + public: + // quantize the input vertices + virtual const PxVec3* kmeansQuantize3D(PxU32 vcount, + const PxVec3 *vertices, + PxU32 stride, + bool denormalizeResults, + PxU32 maxVertices, + PxU32 &outVertsCount) = 0; + + // returns the denormalized scale + virtual const PxVec3& getDenormalizeScale(void) const = 0; + + // returns the denormalized center + virtual const PxVec3& getDenormalizeCenter(void) const = 0; + + // release internal data + virtual void release(void) = 0; + + + protected: + virtual ~Quantizer(void) + { + + } + }; + + // creates the quantizer class + Quantizer * createQuantizer(void); + +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp new file mode 100644 index 000000000..5db4c6abe --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp @@ -0,0 +1,358 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsUserAllocated.h" +#include "PsUtilities.h" +#include "PsMathUtils.h" +#include "PsVecMath.h" + +#include "PxCooking.h" + +#include "GuConvexMeshData.h" +#include "GuBigConvexData2.h" +#include "GuIntersectionRayPlane.h" +#include "GuSerialize.h" + +#include "BigConvexDataBuilder.h" +#include "EdgeList.h" + +#include "ConvexHullBuilder.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +static const PxU32 gSupportVersion = 0; +static const PxU32 gVersion = 0; + +BigConvexDataBuilder::BigConvexDataBuilder(const Gu::ConvexHullData* hull, BigConvexData* gm, const PxVec3* hullVerts) : mHullVerts(hullVerts) +{ + mSVM = gm; + mHull = hull; +} + +BigConvexDataBuilder::~BigConvexDataBuilder() +{ +} + +bool BigConvexDataBuilder::initialize() +{ + mSVM->mData.mSamples = PX_NEW(PxU8)[mSVM->mData.mNbSamples*2u]; + +#if PX_DEBUG +// printf("SVM: %d bytes\n", mNbSamples*sizeof(PxU8)*2); +#endif + + return true; +} + +bool BigConvexDataBuilder::save(PxOutputStream& stream, bool platformMismatch) const +{ + // Export header + if(!WriteHeader('S', 'U', 'P', 'M', gSupportVersion, platformMismatch, stream)) + return false; + + // Save base gaussmap +// if(!GaussMapBuilder::Save(stream, platformMismatch)) return false; + // Export header + if(!WriteHeader('G', 'A', 'U', 'S', gVersion, platformMismatch, stream)) + return false; + + // Export basic info + // stream.StoreDword(mSubdiv); + writeDword(mSVM->mData.mSubdiv, platformMismatch, stream); // PT: could now write Word here + // stream.StoreDword(mNbSamples); + writeDword(mSVM->mData.mNbSamples, platformMismatch, stream); // PT: could now write Word here + + // Save map data + // It's an array of bytes so we don't care about 'PlatformMismatch' + stream.write(mSVM->mData.mSamples, sizeof(PxU8)*mSVM->mData.mNbSamples*2); + + if(!saveValencies(stream, platformMismatch)) + return false; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// compute valencies for each vertex +// we dont compute the edges again here, we have them temporary stored in mHullDataFacesByAllEdges8 structure +bool BigConvexDataBuilder::computeValencies(const ConvexHullBuilder& meshBuilder) +{ + // Create valencies + const PxU32 numVertices = meshBuilder.mHull->mNbHullVertices; + mSVM->mData.mNbVerts = numVertices; + + // Get ram for valencies and adjacent verts + const PxU32 numAlignedVerts = (numVertices+3)&~3; + const PxU32 TotalSize = sizeof(Gu::Valency)*numAlignedVerts + sizeof(PxU8)*meshBuilder.mHull->mNbEdges*2u; + mSVM->mVBuffer = PX_ALLOC(TotalSize, "BigConvexData data"); + mSVM->mData.mValencies = reinterpret_cast(mSVM->mVBuffer); + mSVM->mData.mAdjacentVerts = (reinterpret_cast(mSVM->mVBuffer)) + sizeof(Gu::Valency)*numAlignedVerts; + + PxMemZero(mSVM->mData.mValencies, numVertices*sizeof(Gu::Valency)); + PxU8 vertexMarker[256]; + PxMemZero(vertexMarker,numVertices); + + // Compute valencies + for (PxU32 i = 0; i < meshBuilder.mHull->mNbPolygons; i++) + { + const PxU32 numVerts = meshBuilder.mHullDataPolygons[i].mNbVerts; + const PxU8* Data = meshBuilder.mHullDataVertexData8 + meshBuilder.mHullDataPolygons[i].mVRef8; + for (PxU32 j = 0; j < numVerts; j++) + { + mSVM->mData.mValencies[Data[j]].mCount++; + PX_ASSERT(mSVM->mData.mValencies[Data[j]].mCount != 0xffff); + } + } + + // Create offsets + mSVM->CreateOffsets(); + + // mNbAdjVerts = mOffsets[mNbVerts-1] + mValencies[mNbVerts-1]; + mSVM->mData.mNbAdjVerts = PxU32(mSVM->mData.mValencies[mSVM->mData.mNbVerts - 1].mOffset + mSVM->mData.mValencies[mSVM->mData.mNbVerts - 1].mCount); + PX_ASSERT(mSVM->mData.mNbAdjVerts == PxU32(meshBuilder.mHull->mNbEdges * 2)); + + // Create adjacent vertices + // parse the polygons and its vertices + for (PxU32 i = 0; i < meshBuilder.mHull->mNbPolygons; i++) + { + PxU32 numVerts = meshBuilder.mHullDataPolygons[i].mNbVerts; + const PxU8* Data = meshBuilder.mHullDataVertexData8 + meshBuilder.mHullDataPolygons[i].mVRef8; + for (PxU32 j = 0; j < numVerts; j++) + { + const PxU8 vertexIndex = Data[j]; + PxU8 numAdj = 0; + // if we did not parsed this vertex, traverse to the adjacent face and then + // again to next till we hit back the original polygon + if(vertexMarker[vertexIndex] == 0) + { + PxU8 prevIndex = Data[(j+1)%numVerts]; + mSVM->mData.mAdjacentVerts[mSVM->mData.mValencies[vertexIndex].mOffset++] = prevIndex; + numAdj++; + // now traverse the neighbors + const PxU16 edgeIndex = PxU16(meshBuilder.mEdgeData16[meshBuilder.mHullDataPolygons[i].mVRef8 + j]*2); + PxU8 n0 = meshBuilder.mHullDataFacesByEdges8[edgeIndex]; + PxU8 n1 = meshBuilder.mHullDataFacesByEdges8[edgeIndex + 1]; + + PxU32 neighborPolygon = n0 == i ? n1 : n0; + while (neighborPolygon != i) + { + PxU32 numNeighborVerts = meshBuilder.mHullDataPolygons[neighborPolygon].mNbVerts; + const PxU8* neighborData = meshBuilder.mHullDataVertexData8 + meshBuilder.mHullDataPolygons[neighborPolygon].mVRef8; + PxU32 nextEdgeIndex = 0; + // search in the neighbor face for the tested vertex + for (PxU32 k = 0; k < numNeighborVerts; k++) + { + // search the vertexIndex + if(neighborData[k] == vertexIndex) + { + const PxU8 nextIndex = neighborData[(k+1)%numNeighborVerts]; + // next index already there, pick the previous + if(nextIndex == prevIndex) + { + prevIndex = k == 0 ? neighborData[numNeighborVerts - 1] : neighborData[k-1]; + nextEdgeIndex = k == 0 ? numNeighborVerts - 1 : k-1; + } + else + { + prevIndex = nextIndex; + nextEdgeIndex = k; + } + mSVM->mData.mAdjacentVerts[mSVM->mData.mValencies[vertexIndex].mOffset++] = prevIndex; + numAdj++; + break; + } + } + + // now move to next neighbor + const PxU16 edgeIndex2 = PxU16(meshBuilder.mEdgeData16[(meshBuilder.mHullDataPolygons[neighborPolygon].mVRef8 + nextEdgeIndex)]*2); + n0 = meshBuilder.mHullDataFacesByEdges8[edgeIndex2]; + n1 = meshBuilder.mHullDataFacesByEdges8[edgeIndex2 + 1]; + + neighborPolygon = n0 == neighborPolygon ? n1 : n0; + } + vertexMarker[vertexIndex] = numAdj; + } + } + } + + // Recreate offsets + mSVM->CreateOffsets(); + return true; +} + +////////////////////////////////////////////////////////////////////////// +// compute the min dot product from the verts for given dir +void BigConvexDataBuilder::precomputeSample(const PxVec3& dir, PxU8& startIndex_, float negativeDir) +{ + PxU8 startIndex = startIndex_; + + const PxVec3* verts = mHullVerts; + const Valency* valency = mSVM->mData.mValencies; + const PxU8* adjacentVerts = mSVM->mData.mAdjacentVerts; + + // we have only 256 verts + PxU32 smallBitMap[8] = {0,0,0,0,0,0,0,0}; + + float minimum = negativeDir * verts[startIndex].dot(dir); + PxU32 initialIndex = startIndex; + do + { + initialIndex = startIndex; + const PxU32 numNeighbours = valency[startIndex].mCount; + const PxU32 offset = valency[startIndex].mOffset; + + for (PxU32 a = 0; a < numNeighbours; ++a) + { + const PxU8 neighbourIndex = adjacentVerts[offset + a]; + const float dist = negativeDir * verts[neighbourIndex].dot(dir); + if (dist < minimum) + { + const PxU32 ind = PxU32(neighbourIndex >> 5); + const PxU32 mask = PxU32(1 << (neighbourIndex & 31)); + if ((smallBitMap[ind] & mask) == 0) + { + smallBitMap[ind] |= mask; + minimum = dist; + startIndex = neighbourIndex; + } + } + } + + } while (startIndex != initialIndex); + + startIndex_ = startIndex; +} + +////////////////////////////////////////////////////////////////////////// +// Precompute the min/max vertices for cube directions. +bool BigConvexDataBuilder::precompute(PxU32 subdiv) +{ + mSVM->mData.mSubdiv = Ps::to16(subdiv); + mSVM->mData.mNbSamples = Ps::to16(6 * subdiv*subdiv); + + if (!initialize()) + return false; + + PxU8 startIndex[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + PxU8 startIndex2[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + const float halfSubdiv = float(subdiv - 1) * 0.5f; + for (PxU32 j = 0; j < subdiv; j++) + { + for (PxU32 i = j; i < subdiv; i++) + { + const float iSubDiv = 1.0f - i / halfSubdiv; + const float jSubDiv = 1.0f - j / halfSubdiv; + + PxVec3 tempDir(1.0f, iSubDiv, jSubDiv); + // we need to normalize only once, then we permute the components + // as before for each i,j and j,i face direction + tempDir.normalize(); + + const PxVec3 dirs[12] = { + PxVec3(-tempDir.x, tempDir.y, tempDir.z), + PxVec3(tempDir.x, tempDir.y, tempDir.z), + + PxVec3(tempDir.z, -tempDir.x, tempDir.y), + PxVec3(tempDir.z, tempDir.x, tempDir.y), + + PxVec3(tempDir.y, tempDir.z, -tempDir.x), + PxVec3(tempDir.y, tempDir.z, tempDir.x), + + PxVec3(-tempDir.x, tempDir.z, tempDir.y), + PxVec3(tempDir.x, tempDir.z, tempDir.y), + + PxVec3(tempDir.y, -tempDir.x, tempDir.z), + PxVec3(tempDir.y, tempDir.x, tempDir.z), + + PxVec3(tempDir.z, tempDir.y, -tempDir.x), + PxVec3(tempDir.z, tempDir.y, tempDir.x) + }; + + // compute in each direction + negative/positive dot, we have + // then two start indexes, which are used then for hill climbing + for (PxU32 dStep = 0; dStep < 12; dStep++) + { + precomputeSample(dirs[dStep], startIndex[dStep], 1.0f); + precomputeSample(dirs[dStep], startIndex2[dStep], -1.0f); + } + + // decompose the vector results into face directions + for (PxU32 k = 0; k < 6; k++) + { + const PxU32 ksub = k*subdiv*subdiv; + const PxU32 offset = j + i*subdiv + ksub; + const PxU32 offset2 = i + j*subdiv + ksub; + PX_ASSERT(offset < mSVM->mData.mNbSamples); + PX_ASSERT(offset2 < mSVM->mData.mNbSamples); + + mSVM->mData.mSamples[offset] = startIndex[k]; + mSVM->mData.mSamples[offset + mSVM->mData.mNbSamples] = startIndex2[k]; + + mSVM->mData.mSamples[offset2] = startIndex[k + 6]; + mSVM->mData.mSamples[offset2 + mSVM->mData.mNbSamples] = startIndex2[k + 6]; + } + } + } + return true; +} + +static const PxU32 gValencyVersion = 2; + +////////////////////////////////////////////////////////////////////////// + +bool BigConvexDataBuilder::saveValencies(PxOutputStream& stream, bool platformMismatch) const +{ + // Export header + if(!WriteHeader('V', 'A', 'L', 'E', gValencyVersion, platformMismatch, stream)) + return false; + + writeDword(mSVM->mData.mNbVerts, platformMismatch, stream); + writeDword(mSVM->mData.mNbAdjVerts, platformMismatch, stream); + + { + PxU16* temp = PX_NEW_TEMP(PxU16)[mSVM->mData.mNbVerts]; + for(PxU32 i=0;imData.mNbVerts;i++) + temp[i] = mSVM->mData.mValencies[i].mCount; + + const PxU32 maxIndex = computeMaxIndex(temp, mSVM->mData.mNbVerts); + writeDword(maxIndex, platformMismatch, stream); + StoreIndices(Ps::to16(maxIndex), mSVM->mData.mNbVerts, temp, stream, platformMismatch); + + PX_DELETE_POD(temp); + } + stream.write(mSVM->mData.mAdjacentVerts, mSVM->mData.mNbAdjVerts); + + return true; +} diff --git a/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h new file mode 100644 index 000000000..3317fc2b6 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h @@ -0,0 +1,100 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef BIG_CONVEX_DATA_BUILDER_H +#define BIG_CONVEX_DATA_BUILDER_H + +#include "foundation/PxMemory.h" +#include "PsVecMath.h" + +namespace physx +{ + struct HullTriangleData; + class BigConvexData; + class ConvexHullBuilder; + + ////////////////////////////////////////////////////////////////////////// + //! Valencies creation structure + struct ValenciesCreate + { + //! Constructor + ValenciesCreate() { PxMemZero(this, sizeof(*this)); } + + PxU32 nbVerts; //!< Number of vertices + PxU32 nbFaces; //!< Number of faces + const PxU32* dFaces; //!< List of faces (triangle list) + const PxU16* wFaces; //!< List of faces (triangle list) + bool adjacentList; //!< Compute list of adjacent vertices or not + }; + + ////////////////////////////////////////////////////////////////////////// + + class BigConvexDataBuilder : public Ps::UserAllocated + { + public: + BigConvexDataBuilder(const Gu::ConvexHullData* hull, BigConvexData* gm, const PxVec3* hullVerts); + ~BigConvexDataBuilder(); + // Support vertex map + bool precompute(PxU32 subdiv); + + bool initialize(); + + bool save(PxOutputStream& stream, bool platformMismatch) const; + + bool computeValencies(const ConvexHullBuilder& meshBuilder); + //~Support vertex map + + // Valencies + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes valencies and adjacent vertices. + * After the call, get results with the appropriate accessors. + * + * \param vc [in] creation structure + * \return true if success. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool compute(const ValenciesCreate& vc) const; + + bool saveValencies(PxOutputStream& stream, bool platformMismatch) const; + //~Valencies + protected: + PX_FORCE_INLINE void precomputeSample(const PxVec3& dir, PxU8& startIndex, float negativeDir); + + private: + const Gu::ConvexHullData* mHull; + BigConvexData* mSVM; + const PxVec3* mHullVerts; + + }; + +} + +#endif // BIG_CONVEX_DATA_BUILDER_H diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp new file mode 100644 index 000000000..c7c43414e --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp @@ -0,0 +1,755 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "foundation/PxMemory.h" +#include "EdgeList.h" +#include "GuTriangle32.h" +#include "GuConvexMesh.h" +#include "PxCooking.h" +#include "CookingUtils.h" +#include "ConvexHullBuilder.h" +#include "ConvexHullLib.h" +#include "CmRadixSortBuffered.h" +#include "MeshCleaner.h" +#include "PsArray.h" +#include "PsFoundation.h" +#include "PsVecMath.h" + + +// 7: added mHullDataFacesByVertices8 +// 8: added mEdges +static const physx::PxU32 gVersion = 8; + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +#define USE_PRECOMPUTED_HULL_PROJECTION + +////////////////////////////////////////////////////////////////////////// +// default constructor +ConvexHullBuilder::ConvexHullBuilder(Gu::ConvexHullData* hull, const bool buildGRBData) : + mHullDataHullVertices (NULL), + mHullDataPolygons (NULL), + mHullDataVertexData8 (NULL), + mHullDataFacesByEdges8 (NULL), + mHullDataFacesByVertices8 (NULL), + mEdgeData16 (NULL), + mEdges (NULL), + mHull (hull), + mBuildGRBData (buildGRBData) +{ +} + +////////////////////////////////////////////////////////////////////////// +// default destructor +ConvexHullBuilder::~ConvexHullBuilder() +{ + PX_DELETE_POD(mEdgeData16); + PX_DELETE_POD(mEdges); + + PX_DELETE_POD(mHullDataHullVertices); + PX_DELETE_POD(mHullDataPolygons); + PX_DELETE_POD(mHullDataVertexData8); + PX_DELETE_POD(mHullDataFacesByEdges8); + PX_DELETE_POD(mHullDataFacesByVertices8); +} + +////////////////////////////////////////////////////////////////////////// +// initialize the convex hull +// \param nbVerts [in] number of vertices used +// \param verts [in] vertices array +// \param indices [in] indices array +// \param nbPolygons [in] number of polygons +// \param hullPolygons [in] polygons array +// \param doValidation [in] specifies whether we should run the validation code +// \param hullLib [in] if hullLib is provided, we can reuse the hull create data, hulllib is NULL in case of user provided polygons +bool ConvexHullBuilder::init(PxU32 nbVerts, const PxVec3* verts, const PxU32* indices, const PxU32 nbIndices, + const PxU32 nbPolygons, const PxHullPolygon* hullPolygons, bool doValidation, ConvexHullLib* hullLib) +{ + PX_ASSERT(indices); + PX_ASSERT(verts); + PX_ASSERT(hullPolygons); + PX_ASSERT(nbVerts); + PX_ASSERT(nbPolygons); + + mHullDataHullVertices = NULL; + mHullDataPolygons = NULL; + mHullDataVertexData8 = NULL; + mHullDataFacesByEdges8 = NULL; + mHullDataFacesByVertices8 = NULL; + + mEdges = NULL; + mEdgeData16 = NULL; + + mHull->mNbHullVertices = Ps::to8(nbVerts); + // allocate additional vec3 for V4 safe load in VolumeInteration + mHullDataHullVertices = reinterpret_cast(PX_ALLOC(sizeof(PxVec3) * mHull->mNbHullVertices + 1, "PxVec3")); + PxMemCopy(mHullDataHullVertices, verts, mHull->mNbHullVertices*sizeof(PxVec3)); + + // Cleanup + mHull->mNbPolygons = 0; + PX_DELETE_POD(mHullDataVertexData8); + PX_FREE_AND_RESET(mHullDataPolygons); + + if(nbPolygons>255) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder::init: convex hull has more than 255 polygons!"); + return false; + } + + // Precompute hull polygon structures + mHull->mNbPolygons = Ps::to8(nbPolygons); + mHullDataPolygons = reinterpret_cast(PX_ALLOC(sizeof(Gu::HullPolygonData)*mHull->mNbPolygons, "Gu::HullPolygonData")); + + mHullDataVertexData8 = PX_NEW(PxU8)[nbIndices]; + PxU8* dest = mHullDataVertexData8; + for(PxU32 i=0;i=3); // Else something very wrong happened... + mHullDataPolygons[i].mNbVerts = Ps::to8(numVerts); + + for (PxU32 j = 0; j < numVerts; j++) + { + dest[j] = Ps::to8(indices[inPolygon.mIndexBase + j]); + } + + mHullDataPolygons[i].mPlane = PxPlane(inPolygon.mPlane[0],inPolygon.mPlane[1],inPolygon.mPlane[2],inPolygon.mPlane[3]); + + // Next one + dest += numVerts; + } + + if(!calculateVertexMapTable(nbPolygons, (hullLib != NULL) ? false : true)) + return false; + + // moved create edge list here from save, copy. This is a part of the validation process and + // we need to create the edge list anyway + if(!hullLib || !hullLib->createEdgeList(nbIndices, mHullDataVertexData8, &mHullDataFacesByEdges8, &mEdgeData16, &mEdges)) + { + if (!createEdgeList(doValidation, nbIndices)) + return false; + } + else + { + mHull->mNbEdges = PxU16(nbIndices/2); + } + +#ifdef USE_PRECOMPUTED_HULL_PROJECTION + // Loop through polygons + for (PxU32 j = 0; j < nbPolygons; j++) + { + // Precompute hull projection along local polygon normal + PxU32 NbVerts = mHull->mNbHullVertices; + const PxVec3* Verts = mHullDataHullVertices; + Gu::HullPolygonData& polygon = mHullDataPolygons[j]; + PxReal min = PX_MAX_F32; + PxU8 minIndex = 0xff; + for (PxU8 i = 0; i < NbVerts; i++) + { + float dp = (*Verts++).dot(polygon.mPlane.n); + if (dp < min) + { + min = dp; + minIndex = i; + } + } + polygon.mMinIndex = minIndex; + } +#endif + + if(doValidation) + return checkHullPolygons(); + else + return true; +} + +////////////////////////////////////////////////////////////////////////// +// hull polygons check +bool ConvexHullBuilder::checkHullPolygons() const +{ + const PxVec3* hullVerts = mHullDataHullVertices; + const PxU8* vertexData = mHullDataVertexData8; + Gu::HullPolygonData* hullPolygons = mHullDataPolygons; + + // Check hull validity + if(!hullVerts || !hullPolygons) + return false; + + if(mHull->mNbPolygons<4) + return false; + + PxVec3 max(-FLT_MAX,-FLT_MAX,-FLT_MAX); + + PxVec3 hullMax = hullVerts[0]; + PxVec3 hullMin = hullVerts[0]; + + for(PxU32 j=0;jmNbHullVertices;j++) + { + const PxVec3& hullVert = hullVerts[j]; + if(fabsf(hullVert.x) > max.x) + max.x = fabsf(hullVert.x); + + if(fabsf(hullVert.y) > max.y) + max.y = fabsf(hullVert.y); + + if(fabsf(hullVert.z) > max.z) + max.z = fabsf(hullVert.z); + + if (hullVert.x > hullMax.x) + { + hullMax.x = hullVert.x; + } + else if (hullVert.x < hullMin.x) + { + hullMin.x = hullVert.x; + } + + if (hullVert.y > hullMax.y) + { + hullMax.y = hullVert.y; + } + else if (hullVert.y < hullMin.y) + { + hullMin.y = hullVert.y; + } + + if (hullVert.z > hullMax.z) + { + hullMax.z = hullVert.z; + } + else if (hullVert.z < hullMin.z) + { + hullMin.z = hullVert.z; + } + } + + max += PxVec3(0.02f,0.02f,0.02f); + + PxVec3 testVectors[8]; + bool foundPlane[8]; + for (PxU32 i = 0; i < 8; i++) + { + foundPlane[i] = false; + } + + testVectors[0] = PxVec3(max.x,max.y,max.z); + testVectors[1] = PxVec3(max.x,-max.y,-max.z); + testVectors[2] = PxVec3(max.x,max.y,-max.z); + testVectors[3] = PxVec3(max.x,-max.y,max.z); + testVectors[4] = PxVec3(-max.x,max.y,max.z); + testVectors[5] = PxVec3(-max.x,-max.y,max.z); + testVectors[6] = PxVec3(-max.x,max.y,-max.z); + testVectors[7] = PxVec3(-max.x,-max.y,-max.z); + + + // Extra convex hull validity check. This is less aggressive than previous convex decomposer! + // Loop through polygons + for(PxU32 i=0;imNbPolygons;i++) + { + const PxPlane& P = hullPolygons[i].mPlane; + + for (PxU32 k = 0; k < 8; k++) + { + if(!foundPlane[k]) + { + const float d = P.distance(testVectors[k]); + if(d >= 0) + { + foundPlane[k] = true; + } + } + } + + // Test hull vertices against polygon plane + // compute the test epsilon the same way we construct the hull, verts are considered coplanar within this epsilon + const float planeTolerance = 0.02f; + const float testEpsilon = PxMax(planeTolerance * (PxMax(PxAbs(hullMax.x), PxAbs(hullMin.x)) + + PxMax(PxAbs(hullMax.y), PxAbs(hullMin.y)) + + PxMax(PxAbs(hullMax.z), PxAbs(hullMin.z))), planeTolerance); + + for(PxU32 j=0;jmNbHullVertices;j++) + { + // Don't test vertex if it belongs to plane (to prevent numerical issues) + PxU32 nb = hullPolygons[i].mNbVerts; + bool discard=false; + for(PxU32 k=0;k0.0001f) + //if(d>0.02f) + if(d > testEpsilon) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::ConvexMesh::checkHullPolygons: Some hull vertices seems to be too far from hull planes."); + return false; + } + } + } + } + + for (PxU32 i = 0; i < 8; i++) + { + if(!foundPlane[i]) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::ConvexMesh::checkHullPolygons: Hull seems to have opened volume or do (some) faces have reversed winding?"); + return false; + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Computes the center of the hull. It should be inside it ! +* \param center [out] hull center +* \return true if success +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool ConvexHullBuilder::computeGeomCenter(PxVec3& center, PxU32 numFaces, HullTriangleData* faces) const +{ + // Checkings + const PxVec3* PX_RESTRICT hullVerts = mHullDataHullVertices; + if (!mHull->mNbHullVertices || !hullVerts) return false; + + // Use the topological method + float totalArea = 0.0f; + center = PxVec3(0); + for (PxU32 i = 0; i < numFaces; i++) + { + Gu::TriangleT curTri(faces[i].mRef[0], faces[i].mRef[1], faces[i].mRef[2]); + const float area = curTri.area(hullVerts); + PxVec3 curCenter; curTri.center(hullVerts, curCenter); + center += area * curCenter; + totalArea += area; + } + center /= totalArea; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// hull data store +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeDescData)==8); +PX_COMPILE_TIME_ASSERT(sizeof(Gu::EdgeData)==8); +bool ConvexHullBuilder::save(PxOutputStream& stream, bool platformMismatch) const +{ + // Export header + if(!WriteHeader('C', 'L', 'H', 'L', gVersion, platformMismatch, stream)) + return false; + + // Export header + if(!WriteHeader('C', 'V', 'H', 'L', gVersion, platformMismatch, stream)) + return false; + + // Export figures + + //embed grb flag into mNbEdges + PxU16 hasGRBData = PxU16(mBuildGRBData); + hasGRBData = PxU16(hasGRBData << 15); + PX_ASSERT(mHull->mNbEdges <( (1 << 15) - 1)); + const PxU16 nbEdges = PxU16(mHull->mNbEdges | hasGRBData); + writeDword(mHull->mNbHullVertices, platformMismatch, stream); + writeDword(nbEdges, platformMismatch, stream); + writeDword(computeNbPolygons(), platformMismatch, stream); // Use accessor to lazy-build + PxU32 nb=0; + for(PxU32 i=0;imNbPolygons;i++) + nb += mHullDataPolygons[i].mNbVerts; + writeDword(nb, platformMismatch, stream); + + // Export triangles + + writeFloatBuffer(&mHullDataHullVertices->x, PxU32(mHull->mNbHullVertices*3), platformMismatch, stream); + + // Export polygons + // TODO: allow lazy-evaluation + // We can't really store the buffer in one run anymore! + for(PxU32 i=0;imNbPolygons;i++) + { + Gu::HullPolygonData tmpCopy = mHullDataPolygons[i]; + if(platformMismatch) + flipData(tmpCopy); + + stream.write(&tmpCopy, sizeof(Gu::HullPolygonData)); + } + + // PT: why not storeBuffer here? + for(PxU32 i=0;imNbEdges*2)); + stream.write(mHullDataFacesByVertices8, PxU32(mHull->mNbHullVertices*3)); + + if (mBuildGRBData) + writeWordBuffer(mEdges, PxU32(mHull->mNbEdges * 2), platformMismatch, stream); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool ConvexHullBuilder::copy(ConvexHullData& hullData, PxU32& mNb) +{ + // set the numbers + hullData.mNbHullVertices = mHull->mNbHullVertices; + PxU16 hasGRBData = PxU16(mBuildGRBData); + hasGRBData = PxU16(hasGRBData << 15); + PX_ASSERT(mHull->mNbEdges <((1 << 15) - 1)); + hullData.mNbEdges = PxU16(mHull->mNbEdges | hasGRBData);; + hullData.mNbPolygons = Ps::to8(computeNbPolygons()); + PxU32 nb = 0; + for (PxU32 i = 0; i < mHull->mNbPolygons; i++) + nb += mHullDataPolygons[i].mNbVerts; + + mNb = nb; + + PxU32 bytesNeeded = Gu::computeBufferSize(hullData, nb); + + // allocate the memory first. + void* dataMemory = PX_ALLOC(bytesNeeded, "ConvexHullData data"); + + PxU8* address = reinterpret_cast(dataMemory); + + // set data pointers + hullData.mPolygons = reinterpret_cast(address); address += sizeof(Gu::HullPolygonData) * hullData.mNbPolygons; + PxVec3* dataHullVertices = reinterpret_cast(address); address += sizeof(PxVec3) * hullData.mNbHullVertices; + PxU8* dataFacesByEdges8 = reinterpret_cast(address); address += sizeof(PxU8) * hullData.mNbEdges * 2; + PxU8* dataFacesByVertices8 = reinterpret_cast(address); address += sizeof(PxU8) * hullData.mNbHullVertices * 3; + PxU16* dataEdges = reinterpret_cast(address); address += hullData.mNbEdges.isBitSet() ? sizeof(PxU16) *hullData.mNbEdges * 2 : 0; + PxU8* dataVertexData8 = reinterpret_cast(address); address += sizeof(PxU8) * nb; // PT: leave that one last, so that we don't need to serialize "Nb" + + PX_ASSERT(!(size_t(dataHullVertices) % sizeof(PxReal))); + PX_ASSERT(!(size_t(hullData.mPolygons) % sizeof(PxReal))); + PX_ASSERT(size_t(address) <= size_t(dataMemory) + bytesNeeded); + + PX_ASSERT(mHullDataHullVertices); + PX_ASSERT(mHullDataPolygons); + PX_ASSERT(mHullDataVertexData8); + PX_ASSERT(mHullDataFacesByEdges8); + PX_ASSERT(mHullDataFacesByVertices8); + + // copy the data + PxMemCopy(dataHullVertices, &mHullDataHullVertices->x, PxU32(mHull->mNbHullVertices * 3)*sizeof(float)); + PxMemCopy(hullData.mPolygons, mHullDataPolygons , hullData.mNbPolygons*sizeof(Gu::HullPolygonData)); + PxMemCopy(dataVertexData8, mHullDataVertexData8, nb); + PxMemCopy(dataFacesByEdges8,mHullDataFacesByEdges8, PxU32(mHull->mNbEdges * 2)); + if (mBuildGRBData) + PxMemCopy(dataEdges, mEdges, PxU32(mHull->mNbEdges * 2) * sizeof(PxU16)); + PxMemCopy(dataFacesByVertices8, mHullDataFacesByVertices8, PxU32(mHull->mNbHullVertices * 3)); + return true; +} + +////////////////////////////////////////////////////////////////////////// +// calculate vertex map table +bool ConvexHullBuilder::calculateVertexMapTable(PxU32 nbPolygons, bool userPolygons) +{ + mHullDataFacesByVertices8 = PX_NEW(PxU8)[mHull->mNbHullVertices*3u]; + PxU8 vertexMarker[256]; + PxMemSet(vertexMarker, 0, mHull->mNbHullVertices); + + for (PxU32 i = 0; i < nbPolygons; i++) + { + const Gu::HullPolygonData& polygon = mHullDataPolygons[i]; + for (PxU32 k = 0; k < polygon.mNbVerts; ++k) + { + const PxU8 index = mHullDataVertexData8[polygon.mVRef8 + k]; + if (vertexMarker[index] < 3) + { + //Found a polygon + mHullDataFacesByVertices8[index*3 + vertexMarker[index]++] = Ps::to8(i); + } + } + } + + bool noPlaneShift = false; + for (PxU32 i = 0; i < mHull->mNbHullVertices; ++i) + { + if(vertexMarker[i] != 3) + noPlaneShift = true; + } + + if (noPlaneShift) + { + //PCM will use the original shape, which means it will have a huge performance drop + if (!userPolygons) + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder: convex hull does not have vertex-to-face info! Try to use different convex mesh cooking settings."); + else + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder: convex hull does not have vertex-to-face info! Some of the vertices have less than 3 neighbor polygons. The vertex is most likely inside a polygon or on an edge between 2 polygons, please remove those vertices."); + for (PxU32 i = 0; i < mHull->mNbHullVertices; ++i) + { + mHullDataFacesByVertices8[i * 3 + 0] = 0xFF; + mHullDataFacesByVertices8[i * 3 + 1] = 0xFF; + mHullDataFacesByVertices8[i * 3 + 2] = 0xFF; + } + return false; + } + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +// create edge list +bool ConvexHullBuilder::createEdgeList(bool doValidation, PxU32 nbEdges) +{ + // Code below could be greatly simplified if we assume manifold meshes! + + //feodorb: ok, let's assume manifold meshes, since the code before this change + //would fail on non-maniflold meshes anyways + + // We need the adjacency graph for hull polygons, similar to what we have for triangles. + // - sort the polygon edges and walk them in order + // - each edge should appear exactly twice since a convex is a manifold mesh without boundary edges + // - the polygon index is implicit when we walk the sorted list => get the 2 polygons back and update adjacency graph + // + // Two possible structures: + // - polygon to edges: needed for local search (actually: polygon to polygons) + // - edge to polygons: needed to compute edge normals on-the-fly + + // Below is largely copied from the edge-list code + + // Polygon to edges: + // + // We're dealing with convex polygons made of N vertices, defining N edges. For each edge we want the edge in + // an edge array. + // + // Edges to polygon: + // + // For each edge in the array, we want two polygon indices - ie an edge. + + // 0) Compute the total size needed for "polygon to edges" + const PxU32 nbPolygons = mHull->mNbPolygons; + PxU32 nbEdgesUnshared = nbEdges; + + // in a manifold mesh, each edge is repeated exactly twice as it shares exactly 2 faces + if (nbEdgesUnshared % 2 != 0) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Cooking::cookConvexMesh: non-manifold mesh cannot be used, invalid mesh!"); + return false; + } + + // 1) Get some bytes: I need one EdgesRefs for each face, and some temp buffers + + // Face indices by edge indices. First face is the one where the edge is ordered from tail to head. + PX_DELETE_POD(mHullDataFacesByEdges8); + mHullDataFacesByEdges8 = PX_NEW(PxU8)[nbEdgesUnshared]; + + PxU32* tempBuffer = PX_NEW_TEMP(PxU32)[nbEdgesUnshared*8]; // Temp storage + PxU32* bufferAdd = tempBuffer; + PxU32* PX_RESTRICT vRefs0 = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* PX_RESTRICT vRefs1 = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* polyIndex = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* vertexIndex = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* polyIndex2 = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* vertexIndex2 = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* edgeIndex = tempBuffer; tempBuffer += nbEdgesUnshared; + PxU32* edgeData = tempBuffer; tempBuffer += nbEdgesUnshared; + + // TODO avoroshilov: use the same "tempBuffer" + bool* flippedVRefs = PX_NEW_TEMP(bool)[nbEdgesUnshared]; // Temp storage + + PxU32* run0 = vRefs0; + PxU32* run1 = vRefs1; + PxU32* run2 = polyIndex; + PxU32* run3 = vertexIndex; + bool* run4 = flippedVRefs; + + // 2) Create a full redundant list of edges + PxU32 edgeCounter = 0; + for(PxU32 i=0;ivRef1; + + if (flipped) + physx::shdfnd::swap(vRef0, vRef1); + + *run0++ = vRef0; + *run1++ = vRef1; + *run2++ = i; + *run3++ = j; + *run4++ = flipped; + edgeData[edgeCounter] = edgeCounter; + edgeCounter++; + } + } + PX_ASSERT(PxU32(run0-vRefs0)==nbEdgesUnshared); + PX_ASSERT(PxU32(run1-vRefs1)==nbEdgesUnshared); + + // 3) Sort the list according to both keys (VRefs0 and VRefs1) + Cm::RadixSortBuffered sorter; + const PxU32* PX_RESTRICT sorted = sorter.Sort(vRefs1, nbEdgesUnshared,Cm::RADIX_UNSIGNED).Sort(vRefs0, nbEdgesUnshared,Cm::RADIX_UNSIGNED).GetRanks(); + + PX_DELETE_POD(mEdges); + // Edges by their tail and head VRefs. NbEdgesUnshared == nbEdges * 2 + // mEdges[edgeIdx*2 + 0] = tailVref, mEdges[edgeIdx*2 + 1] = headVref + // Tails and heads should be consistent with face refs, so that the edge is given in the order of + // his first face and opposite to the order of his second face + mEdges = PX_NEW(PxU16)[nbEdgesUnshared]; + + PX_DELETE_POD(mEdgeData16); + // Face to edge mapping + mEdgeData16 = PX_NEW(PxU16)[nbEdgesUnshared]; + + // TODO avoroshilov: remove this comment + //mHull->mNbEdges = Ps::to16(nbEdgesUnshared / 2); // #non-redundant edges + + mHull->mNbEdges = 0; // #non-redundant edges + + // 4) Loop through all possible edges + // - clean edges list by removing redundant edges + // - create EdgesRef list + // mNbFaces = nbFaces; + + // TODO avoroshilov: + PxU32 numFacesPerEdgeVerificationCounter = 0; + + PxU16* edgeVertOutput = mEdges; + + PxU32 previousRef0 = PX_INVALID_U32; + PxU32 previousRef1 = PX_INVALID_U32; + PxU32 previousPolyId = PX_INVALID_U32; + + PxU16 nbHullEdges = 0; + for (PxU32 i = 0; i < nbEdgesUnshared; i++) + { + const PxU32 sortedIndex = sorted[i]; // Between 0 and Nb + const PxU32 polyID = polyIndex[sortedIndex]; // Poly index + const PxU32 vertexID = vertexIndex[sortedIndex]; // Poly index + PxU32 sortedRef0 = vRefs0[sortedIndex]; // (SortedRef0, SortedRef1) is the sorted edge + PxU32 sortedRef1 = vRefs1[sortedIndex]; + bool flipped = flippedVRefs[sortedIndex]; + + if (sortedRef0 != previousRef0 || sortedRef1 != previousRef1) + { + // TODO avoroshilov: remove this? + if (i != 0 && numFacesPerEdgeVerificationCounter != 1) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Cooking::cookConvexMesh: non-manifold mesh cannot be used, invalid mesh!"); + return false; + } + numFacesPerEdgeVerificationCounter = 0; + + // ### TODO: change this in edge list as well + previousRef0 = sortedRef0; + previousRef1 = sortedRef1; + previousPolyId = polyID; + + //feodorb:restore the original order of VRefs (tail and head) + if (flipped) + physx::shdfnd::swap(sortedRef0, sortedRef1); + + *edgeVertOutput++ = Ps::to16(sortedRef0); + *edgeVertOutput++ = Ps::to16(sortedRef1); + + nbHullEdges++; + } + else + { + mHullDataFacesByEdges8[(nbHullEdges - 1) * 2] = Ps::to8(previousPolyId); + mHullDataFacesByEdges8[(nbHullEdges - 1) * 2 + 1] = Ps::to8(polyID); + + ++numFacesPerEdgeVerificationCounter; + } + + mEdgeData16[mHullDataPolygons[polyID].mVRef8 + vertexID] = Ps::to16(i / 2); + + // Create mEdgesRef on the fly + + polyIndex2[i] = polyID; + vertexIndex2[i] = vertexID; + edgeIndex[i] = PxU32(nbHullEdges - 1); + } + + mHull->mNbEdges = nbHullEdges; + + ////////////////////// + + // 2) Get some bytes: one Pair structure / edge + // create this structure only for validation purpose + // 3) Create Counters, ie compute the #faces sharing each edge + if(doValidation) + { + // + sorted = sorter.Sort(vertexIndex2, nbEdgesUnshared, Cm::RADIX_UNSIGNED).Sort(polyIndex2, nbEdgesUnshared, Cm::RADIX_UNSIGNED).GetRanks(); + + for (PxU32 i = 0; i < nbEdgesUnshared; i++) edgeData[i] = edgeIndex[sorted[i]]; + + Gu::EdgeDescData* edgeToTriangles = PX_NEW(Gu::EdgeDescData)[PxU16(mHull->mNbEdges)]; + PxMemZero(edgeToTriangles, sizeof(Gu::EdgeDescData)*mHull->mNbEdges); + + PxU32* data = edgeData; + for(PxU32 i=0;imNbEdges; i++) + { + if (edgeToTriangles[i].Count != 2) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Cooking::cookConvexMesh: non-manifold mesh cannot be used, invalid mesh!"); + return false; + } + } + PX_DELETE_POD(edgeToTriangles); + } + + // ### free temp ram + PX_DELETE_POD(bufferAdd); + + // TODO avoroshilov: use the same "tempBuffer" + PX_DELETE_POD(flippedVRefs); + + return true; +} + diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h new file mode 100644 index 000000000..e68f5ed0f --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONVEXHULLBUILDER_H +#define PX_CONVEXHULLBUILDER_H + +#include "GuConvexMeshData.h" +#include "PsUserAllocated.h" +#include "PxCooking.h" + +namespace physx +{ + struct PxHullPolygon; + class ConvexHullLib; + + namespace Gu + { + struct EdgeDescData; + struct ConvexHullData; + } // namespace Gu + + struct HullTriangleData + { + PxU32 mRef[3]; + }; + + class ConvexHullBuilder : public Ps::UserAllocated + { + public: + ConvexHullBuilder(Gu::ConvexHullData* hull, const bool buildGRBData); + ~ConvexHullBuilder(); + + bool init(PxU32 nbVerts, const PxVec3* verts, const PxU32* indices, const PxU32 nbIndices, const PxU32 nbPolygons, + const PxHullPolygon* hullPolygons, bool doValidation = true, ConvexHullLib* hullLib = NULL); + + bool save(PxOutputStream& stream, bool platformMismatch) const; + bool copy(Gu::ConvexHullData& hullData, PxU32& nb); + + bool createEdgeList(bool doValidation, PxU32 nbEdges); + bool checkHullPolygons() const; + + bool calculateVertexMapTable(PxU32 nbPolygons, bool userPolygons = false); + + PX_INLINE PxU32 computeNbPolygons() const + { + PX_ASSERT(mHull->mNbPolygons); + return mHull->mNbPolygons; + } + + PxVec3* mHullDataHullVertices; + Gu::HullPolygonData* mHullDataPolygons; + PxU8* mHullDataVertexData8; + PxU8* mHullDataFacesByEdges8; + PxU8* mHullDataFacesByVertices8; + + PxU16* mEdgeData16; //!< Edge indices indexed by hull polygons + PxU16* mEdges; //!< Edge to vertex mapping + + Gu::ConvexHullData* mHull; + bool mBuildGRBData; + + protected: + bool computeGeomCenter(PxVec3& , PxU32 numFaces, HullTriangleData* faces) const; + }; +} + +#endif // PX_CONVEXHULLBUILDER_H + diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp new file mode 100644 index 000000000..f5b1033f3 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp @@ -0,0 +1,352 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ConvexHullLib.h" +#include "Quantizer.h" +#include "PsAllocator.h" +#include "foundation/PxBounds3.h" +#include "foundation/PxMemory.h" + +using namespace physx; + +namespace local +{ + ////////////////////////////////////////////////////////////////////////// + // constants + static const float DISTANCE_EPSILON = 0.000001f; // close enough to consider two floating point numbers to be 'the same'. + static const float NORMAL_DISTANCE_EPSILON = 0.001f; // close enough to consider two floating point numbers to be 'the same' in normalized points cloud. + static const float RESIZE_VALUE = 0.01f; // if the provided points AABB is very thin resize it to this size + + ////////////////////////////////////////////////////////////////////////// + // checks if points form a valid AABB cube, if not construct a default CUBE + static bool checkPointsAABBValidity(PxU32 numPoints, const PxVec3* points, PxU32 stride , float distanceEpsilon, + float resizeValue, PxVec3& center, PxVec3& scale, PxU32& vcount, PxVec3* vertices, bool fCheck = false) + { + const char* vtx = reinterpret_cast (points); + PxBounds3 bounds; + bounds.setEmpty(); + + // get the bounding box + for (PxU32 i = 0; i < numPoints; i++) + { + const PxVec3& p = *reinterpret_cast (vtx); + vtx += stride; + + bounds.include(p); + } + + PxVec3 dim = bounds.getDimensions(); + center = bounds.getCenter(); + + // special case, the AABB is very thin or user provided us with only input 2 points + // we construct an AABB cube and return it + if ( dim.x < distanceEpsilon || dim.y < distanceEpsilon || dim.z < distanceEpsilon || numPoints < 3 ) + { + float len = FLT_MAX; + + // pick the shortest size bigger than the distance epsilon + if ( dim.x > distanceEpsilon && dim.x < len ) + len = dim.x; + if ( dim.y > distanceEpsilon && dim.y < len ) + len = dim.y; + if ( dim.z > distanceEpsilon && dim.z < len ) + len = dim.z; + + // if the AABB is small in all dimensions, resize it + if ( len == FLT_MAX ) + { + dim = PxVec3(resizeValue); + } + // if one edge is small, set to 1/5th the shortest non-zero edge. + else + { + if ( dim.x < distanceEpsilon ) + dim.x = len * 0.05f; + else + dim.x *= 0.5f; + if ( dim.y < distanceEpsilon ) + dim.y = len * 0.05f; + else + dim.y *= 0.5f; + if ( dim.z < distanceEpsilon ) + dim.z = len * 0.05f; + else + dim.z *= 0.5f; + } + + // construct the AABB + const PxVec3 extPos = center + dim; + const PxVec3 extNeg = center - dim; + + if(fCheck) + vcount = 0; + + vertices[vcount++] = extNeg; + vertices[vcount++] = PxVec3(extPos.x,extNeg.y,extNeg.z); + vertices[vcount++] = PxVec3(extPos.x,extPos.y,extNeg.z); + vertices[vcount++] = PxVec3(extNeg.x,extPos.y,extNeg.z); + vertices[vcount++] = PxVec3(extNeg.x,extNeg.y,extPos.z); + vertices[vcount++] = PxVec3(extPos.x,extNeg.y,extPos.z); + vertices[vcount++] = extPos; + vertices[vcount++] = PxVec3(extNeg.x,extPos.y,extPos.z); + return true; // return cube + } + else + { + scale = dim; + } + return false; + } + +} + + +////////////////////////////////////////////////////////////////////////// +// shift vertices around origin and normalize point cloud, remove duplicates! +bool ConvexHullLib::shiftAndcleanupVertices(PxU32 svcount, const PxVec3* svertices, PxU32 stride, + PxU32& vcount, PxVec3* vertices, PxVec3& scale, PxVec3& center) +{ + mShiftedVerts = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxVec3)*svcount, "PxVec3")); + const char* vtx = reinterpret_cast (svertices); + PxBounds3 bounds; + bounds.setEmpty(); + + // get the bounding box + for (PxU32 i = 0; i < svcount; i++) + { + const PxVec3& p = *reinterpret_cast (vtx); + vtx += stride; + + bounds.include(p); + } + mOriginShift = bounds.getCenter(); + vtx = reinterpret_cast (svertices); + for (PxU32 i = 0; i < svcount; i++) + { + const PxVec3& p = *reinterpret_cast (vtx); + vtx += stride; + + mShiftedVerts[i] = p - mOriginShift; + } + return cleanupVertices(svcount, mShiftedVerts, sizeof(PxVec3), vcount, vertices, scale, center); +} + +////////////////////////////////////////////////////////////////////////// +// Shift verts/planes in the desc back +void ConvexHullLib::shiftConvexMeshDesc(PxConvexMeshDesc& desc) +{ + PX_ASSERT(mConvexMeshDesc.flags & PxConvexFlag::eSHIFT_VERTICES); + + PxVec3* points = reinterpret_cast(const_cast(desc.points.data)); + for(PxU32 i = 0; i < desc.points.count; i++) + { + points[i] = points[i] + mOriginShift; + } + + PxHullPolygon* polygons = reinterpret_cast(const_cast(desc.polygons.data)); + for(PxU32 i = 0; i < desc.polygons.count; i++) + { + polygons[i].mPlane[3] -= PxVec3(polygons[i].mPlane[0], polygons[i].mPlane[1], polygons[i].mPlane[2]).dot(mOriginShift); + } +} + +////////////////////////////////////////////////////////////////////////// +// normalize point cloud, remove duplicates! +bool ConvexHullLib::cleanupVertices(PxU32 svcount, const PxVec3* svertices, PxU32 stride, + PxU32& vcount, PxVec3* vertices, PxVec3& scale, PxVec3& center) +{ + if (svcount == 0) + return false; + + const PxVec3* verticesToClean = svertices; + PxU32 numVerticesToClean = svcount; + Quantizer* quantizer = NULL; + + // if quantization is enabled, parse the input vertices and produce new qantized vertices, + // that will be then cleaned the same way + if (mConvexMeshDesc.flags & PxConvexFlag::eQUANTIZE_INPUT) + { + quantizer = createQuantizer(); + PxU32 vertsOutCount; + const PxVec3* vertsOut = quantizer->kmeansQuantize3D(svcount, svertices, stride,true, mConvexMeshDesc.quantizedCount, vertsOutCount); + + if (vertsOut) + { + numVerticesToClean = vertsOutCount; + verticesToClean = vertsOut; + } + } + + const float distanceEpsilon = local::DISTANCE_EPSILON * mCookingParams.scale.length; + const float resizeValue = local::RESIZE_VALUE * mCookingParams.scale.length; + const float normalEpsilon = local::NORMAL_DISTANCE_EPSILON; // used to determine if 2 points are the same + + vcount = 0; + PxVec3 recip; + + scale = PxVec3(1.0f); + + // check for the AABB from points, if its very tiny return a resized CUBE + if (local::checkPointsAABBValidity(numVerticesToClean, verticesToClean, stride, distanceEpsilon, resizeValue, center, scale, vcount, vertices, false)) + { + if (quantizer) + quantizer->release(); + return true; + } + + recip[0] = 1 / scale[0]; + recip[1] = 1 / scale[1]; + recip[2] = 1 / scale[2]; + + center = center.multiply(recip); + + // normalize the point cloud + const char * vtx = reinterpret_cast (verticesToClean); + for (PxU32 i = 0; i(vtx); + vtx+=stride; + + PxVec3 normalizedP = p.multiply(recip); // normalize + + PxU32 j; + + // parse the already stored vertices and check the distance + for (j=0; j dist2 ) + { + v = normalizedP; + } + break; + } + } + + // we dont have that vertex in the output, add it + if ( j == vcount ) + { + vertices[vcount] = normalizedP; + vcount++; + } + } + + // scale the verts back + for (PxU32 i = 0; i < vcount; i++) + { + vertices[i] = vertices[i].multiply(scale); + } + + // ok..now make sure we didn't prune so many vertices it is now invalid. + // note, that the output vertices are again scaled, we need to scale them back then + local::checkPointsAABBValidity(vcount, vertices, sizeof(PxVec3), distanceEpsilon, resizeValue, center, scale, vcount, vertices, true); + + if (quantizer) + quantizer->release(); + return true; +} + +void ConvexHullLib::swapLargestFace(PxConvexMeshDesc& desc) +{ + const PxHullPolygon* polygons = reinterpret_cast(desc.polygons.data); + PxHullPolygon* polygonsOut = const_cast(polygons); + + PxU32 largestFace = 0; + for (PxU32 i = 1; i < desc.polygons.count; i++) + { + if(polygons[largestFace].mNbVerts < polygons[i].mNbVerts) + largestFace = i; + } + + // early exit if no swap needs to be done + if(largestFace == 0) + return; + + const PxU32* indices = reinterpret_cast(desc.indices.data); + mSwappedIndices = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxU32)*desc.indices.count, "PxU32")); + + PxHullPolygon replacedPolygon = polygons[0]; + PxHullPolygon largestPolygon = polygons[largestFace]; + polygonsOut[0] = polygons[largestFace]; + polygonsOut[largestFace] = replacedPolygon; + + // relocate indices + PxU16 indexBase = 0; + for (PxU32 i = 0; i < desc.polygons.count; i++) + { + if(i == 0) + { + PxMemCopy(mSwappedIndices, &indices[largestPolygon.mIndexBase],sizeof(PxU32)*largestPolygon.mNbVerts); + polygonsOut[0].mIndexBase = indexBase; + indexBase += largestPolygon.mNbVerts; + } + else + { + if(i == largestFace) + { + PxMemCopy(&mSwappedIndices[indexBase], &indices[replacedPolygon.mIndexBase], sizeof(PxU32)*replacedPolygon.mNbVerts); + polygonsOut[i].mIndexBase = indexBase; + indexBase += replacedPolygon.mNbVerts; + } + else + { + PxMemCopy(&mSwappedIndices[indexBase], &indices[polygons[i].mIndexBase], sizeof(PxU32)*polygons[i].mNbVerts); + polygonsOut[i].mIndexBase = indexBase; + indexBase += polygons[i].mNbVerts; + } + } + } + + PX_ASSERT(indexBase == desc.indices.count); + + desc.indices.data = mSwappedIndices; +} + +ConvexHullLib::~ConvexHullLib() +{ + if (mSwappedIndices) + PX_FREE(mSwappedIndices); + + if(mShiftedVerts) + PX_FREE(mShiftedVerts); +} diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h new file mode 100644 index 000000000..4d250f7c2 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONVEXHULLLIB_H +#define PX_CONVEXHULLLIB_H + +#include "PxConvexMeshDesc.h" +#include "PxCooking.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + ////////////////////////////////////////////////////////////////////////// + // base class for the convex hull libraries - inflation based and quickhull + class ConvexHullLib + { + PX_NOCOPY(ConvexHullLib) + public: + // functions + ConvexHullLib(const PxConvexMeshDesc& desc, const PxCookingParams& params) + : mConvexMeshDesc(desc), mCookingParams(params), mSwappedIndices(NULL), + mShiftedVerts(NULL) + { + } + + virtual ~ConvexHullLib(); + + // computes the convex hull from provided points + virtual PxConvexMeshCookingResult::Enum createConvexHull() = 0; + + // fills the PxConvexMeshDesc with computed hull data + virtual void fillConvexMeshDesc(PxConvexMeshDesc& desc) = 0; + + // compute the edge list information if possible + virtual bool createEdgeList(const PxU32 nbIndices, const PxU8* indices, PxU8** hullDataFacesByEdges8, PxU16** edgeData16, PxU16** edges) = 0; + + static const PxU32 gpuMaxVertsPerFace = 32; + + protected: + + // clean input vertices from duplicates, normalize etc. + bool cleanupVertices(PxU32 svcount, // input vertex count + const PxVec3* svertices, // vertices + PxU32 stride, // stride + PxU32& vcount, // output number of vertices + PxVec3* vertices, // location to store the results. + PxVec3& scale, // scale + PxVec3& center); // center + + // shift vertices around origin and clean input vertices from duplicates, normalize etc. + bool shiftAndcleanupVertices(PxU32 svcount, // input vertex count + const PxVec3* svertices, // vertices + PxU32 stride, // stride + PxU32& vcount, // output number of vertices + PxVec3* vertices, // location to store the results. + PxVec3& scale, // scale + PxVec3& center); // center + + void swapLargestFace(PxConvexMeshDesc& desc); + + void shiftConvexMeshDesc(PxConvexMeshDesc& desc); + + protected: + const PxConvexMeshDesc& mConvexMeshDesc; + const PxCookingParams& mCookingParams; + PxU32* mSwappedIndices; + PxVec3 mOriginShift; + PxVec3* mShiftedVerts; + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp new file mode 100644 index 000000000..89309da59 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp @@ -0,0 +1,925 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxBounds3.h" +#include "foundation/PxMathUtils.h" + +#include "ConvexHullUtils.h" +#include "VolumeIntegration.h" +#include "PsUtilities.h" +#include "PsVecMath.h" +#include "GuBox.h" +#include "GuConvexMeshData.h" + +using namespace physx; +using namespace Ps::aos; + +namespace local +{ + static const float MIN_ADJACENT_ANGLE = 3.0f; // in degrees - result wont have two adjacent facets within this angle of each other. + static const float MAXDOT_MINANG = cosf(Ps::degToRad(MIN_ADJACENT_ANGLE)); // adjacent angle for dot product tests + + ////////////////////////////////////////////////////////////////////////// + // helper class for ConvexHullCrop + class VertFlag + { + public: + PxU8 planetest; + PxU8 undermap; + PxU8 overmap; + }; + + //////////////////////////////////////////////////////////////////////////| + // helper class for ConvexHullCrop + class EdgeFlag + { + public: + PxI16 undermap; + }; + + //////////////////////////////////////////////////////////////////////////| + // helper class for ConvexHullCrop + class Coplanar + { + public: + PxU16 ea; + PxU8 v0; + PxU8 v1; + }; + + ////////////////////////////////////////////////////////////////////////// + // plane test + enum PlaneTestResult + { + eCOPLANAR = 0, + eUNDER = 1 << 0, + eOVER = 1 << 1 + }; + + ////////////////////////////////////////////////////////////////////////// + // test where vertex lies in respect to the plane + static PlaneTestResult planeTest(const PxPlane& p, const PxVec3& v, float epsilon) + { + const float a = v.dot(p.n) + p.d; + PlaneTestResult flag = (a > epsilon) ? eOVER : ((a < -epsilon) ? eUNDER : eCOPLANAR); + return flag; + } + + // computes the OBB for this set of points relative to this transform matrix. SIMD version + void computeOBBSIMD(PxU32 vcount, const Vec4V* points, Vec4V& sides, const QuatV& rot, Vec4V& trans) + { + PX_ASSERT(vcount); + + Vec4V minV = V4Load(FLT_MAX); + Vec4V maxV = V4Load(FLT_MIN); + for (PxU32 i = 0; i < vcount; i++) + { + const Vec4V& vertexV = points[i]; + const Vec4V t = V4Sub(vertexV, trans); + const Vec4V v = Vec4V_From_Vec3V(QuatRotateInv(rot, Vec3V_From_Vec4V(t))); + + minV = V4Min(minV, v); + maxV = V4Max(maxV, v); + } + + sides = V4Sub(maxV, minV); + + Mat33V tmpMat; + QuatGetMat33V(rot, tmpMat.col0, tmpMat.col1, tmpMat.col2); + const FloatV coe = FLoad(0.5f); + + const Vec4V deltaVec = V4Sub(maxV, V4Scale(sides, coe)); + + const Vec4V t0 = V4Scale(Vec4V_From_Vec3V(tmpMat.col0), V4GetX(deltaVec)); + trans = V4Add(trans, t0); + + const Vec4V t1 = V4Scale(Vec4V_From_Vec3V(tmpMat.col1), V4GetY(deltaVec)); + trans = V4Add(trans, t1); + + const Vec4V t2 = V4Scale(Vec4V_From_Vec3V(tmpMat.col2), V4GetZ(deltaVec)); + trans = V4Add(trans, t2); + } +} + +////////////////////////////////////////////////////////////////////////// +// construct the base cube from given min/max +ConvexHull::ConvexHull(const PxVec3& bmin, const PxVec3& bmax, const Ps::Array& inPlanes) +: mInputPlanes(inPlanes) +{ + // min max verts of the cube - 8 verts + mVertices.pushBack(PxVec3(bmin.x, bmin.y, bmin.z)); // --- + mVertices.pushBack(PxVec3(bmin.x, bmin.y, bmax.z)); // --+ + mVertices.pushBack(PxVec3(bmin.x, bmax.y, bmin.z)); // -+- + mVertices.pushBack(PxVec3(bmin.x, bmax.y, bmax.z)); // -++ + mVertices.pushBack(PxVec3(bmax.x, bmin.y, bmin.z)); // +-- + mVertices.pushBack(PxVec3(bmax.x, bmin.y, bmax.z)); // +-+ + mVertices.pushBack(PxVec3(bmax.x, bmax.y, bmin.z)); // ++- + mVertices.pushBack(PxVec3(bmax.x, bmax.y, bmax.z)); // +++ + + // cube planes - 6 planes + mFacets.pushBack(PxPlane(PxVec3(-1.f, 0, 0), bmin.x)); // 0,1,3,2 + mFacets.pushBack(PxPlane(PxVec3(1.f, 0, 0), -bmax.x)); // 6,7,5,4 + mFacets.pushBack(PxPlane(PxVec3(0, -1.f, 0), bmin.y)); // 0,4,5,1 + mFacets.pushBack(PxPlane(PxVec3(0, 1.f, 0), -bmax.y)); // 3,7,6,2 + mFacets.pushBack(PxPlane(PxVec3(0, 0, -1.f), bmin.z)); // 0,2,6,4 + mFacets.pushBack(PxPlane(PxVec3(0, 0, 1.f), -bmax.z)); // 1,5,7,3 + + // cube edges - 24 edges + mEdges.pushBack(HalfEdge(11, 0, 0)); + mEdges.pushBack(HalfEdge(23, 1, 0)); + mEdges.pushBack(HalfEdge(15, 3, 0)); + mEdges.pushBack(HalfEdge(16, 2, 0)); + + mEdges.pushBack(HalfEdge(13, 6, 1)); + mEdges.pushBack(HalfEdge(21, 7, 1)); + mEdges.pushBack(HalfEdge(9, 5, 1)); + mEdges.pushBack(HalfEdge(18, 4, 1)); + + mEdges.pushBack(HalfEdge(19, 0, 2)); + mEdges.pushBack(HalfEdge(6, 4, 2)); + mEdges.pushBack(HalfEdge(20, 5, 2)); + mEdges.pushBack(HalfEdge(0, 1, 2)); + + mEdges.pushBack(HalfEdge(22, 3, 3)); + mEdges.pushBack(HalfEdge(4, 7, 3)); + mEdges.pushBack(HalfEdge(17, 6, 3)); + mEdges.pushBack(HalfEdge(2, 2, 3)); + + mEdges.pushBack(HalfEdge(3, 0, 4)); + mEdges.pushBack(HalfEdge(14, 2, 4)); + mEdges.pushBack(HalfEdge(7, 6, 4)); + mEdges.pushBack(HalfEdge(8, 4, 4)); + + mEdges.pushBack(HalfEdge(10, 1, 5)); + mEdges.pushBack(HalfEdge(5, 5, 5)); + mEdges.pushBack(HalfEdge(12, 7, 5)); + mEdges.pushBack(HalfEdge(1, 3, 5)); +} + +////////////////////////////////////////////////////////////////////////// +// create the initial convex hull from given OBB +ConvexHull::ConvexHull(const PxVec3& extent, const PxTransform& transform, const Ps::Array& inPlanes) + : mInputPlanes(inPlanes) +{ + // get the OBB corner points + PxVec3 extentPoints[8]; + PxMat33 rot(transform.q); + Gu::computeOBBPoints(extentPoints, transform.p, extent, rot.column0, rot.column1, rot.column2); + + mVertices.pushBack(PxVec3(extentPoints[0].x, extentPoints[0].y, extentPoints[0].z)); // --- + mVertices.pushBack(PxVec3(extentPoints[4].x, extentPoints[4].y, extentPoints[4].z)); // --+ + mVertices.pushBack(PxVec3(extentPoints[3].x, extentPoints[3].y, extentPoints[3].z)); // -+- + mVertices.pushBack(PxVec3(extentPoints[7].x, extentPoints[7].y, extentPoints[7].z)); // -++ + mVertices.pushBack(PxVec3(extentPoints[1].x, extentPoints[1].y, extentPoints[1].z)); // +-- + mVertices.pushBack(PxVec3(extentPoints[5].x, extentPoints[5].y, extentPoints[5].z)); // +-+ + mVertices.pushBack(PxVec3(extentPoints[2].x, extentPoints[2].y, extentPoints[2].z)); // ++- + mVertices.pushBack(PxVec3(extentPoints[6].x, extentPoints[6].y, extentPoints[6].z)); // +++ + + // cube planes - 6 planes + PxPlane plane0(extentPoints[0], extentPoints[4], extentPoints[7]); // 0,1,3,2 + mFacets.pushBack(PxPlane(plane0.n, plane0.d)); + + PxPlane plane1(extentPoints[2], extentPoints[6], extentPoints[5]); // 6,7,5,4 + mFacets.pushBack(PxPlane(plane1.n, plane1.d)); + + PxPlane plane2(extentPoints[0], extentPoints[1], extentPoints[5]); // 0,4,5,1 + mFacets.pushBack(PxPlane(plane2.n, plane2.d)); + + PxPlane plane3(extentPoints[7], extentPoints[6], extentPoints[2]); // 3,7,6,2 + mFacets.pushBack(PxPlane(plane3.n, plane3.d)); + + PxPlane plane4(extentPoints[0], extentPoints[3], extentPoints[2]); // 0,2,6,4 + mFacets.pushBack(PxPlane(plane4.n, plane4.d)); + + PxPlane plane5(extentPoints[4], extentPoints[5], extentPoints[6]); // 1,5,7,3 + mFacets.pushBack(PxPlane(plane5.n, plane5.d)); + + // cube edges - 24 edges + mEdges.pushBack(HalfEdge(11, 0, 0)); + mEdges.pushBack(HalfEdge(23, 1, 0)); + mEdges.pushBack(HalfEdge(15, 3, 0)); + mEdges.pushBack(HalfEdge(16, 2, 0)); + + mEdges.pushBack(HalfEdge(13, 6, 1)); + mEdges.pushBack(HalfEdge(21, 7, 1)); + mEdges.pushBack(HalfEdge(9, 5, 1)); + mEdges.pushBack(HalfEdge(18, 4, 1)); + + mEdges.pushBack(HalfEdge(19, 0, 2)); + mEdges.pushBack(HalfEdge(6, 4, 2)); + mEdges.pushBack(HalfEdge(20, 5, 2)); + mEdges.pushBack(HalfEdge(0, 1, 2)); + + mEdges.pushBack(HalfEdge(22, 3, 3)); + mEdges.pushBack(HalfEdge(4, 7, 3)); + mEdges.pushBack(HalfEdge(17, 6, 3)); + mEdges.pushBack(HalfEdge(2, 2, 3)); + + mEdges.pushBack(HalfEdge(3, 0, 4)); + mEdges.pushBack(HalfEdge(14, 2, 4)); + mEdges.pushBack(HalfEdge(7, 6, 4)); + mEdges.pushBack(HalfEdge(8, 4, 4)); + + mEdges.pushBack(HalfEdge(10, 1, 5)); + mEdges.pushBack(HalfEdge(5, 5, 5)); + mEdges.pushBack(HalfEdge(12, 7, 5)); + mEdges.pushBack(HalfEdge(1, 3, 5)); +} + +////////////////////////////////////////////////////////////////////////// +// finds the candidate plane, returns -1 otherwise +PxI32 ConvexHull::findCandidatePlane(float planeTestEpsilon, float epsilon) const +{ + PxI32 p = -1; + float md = 0.0f; + PxU32 i, j; + for (i = 0; i < mInputPlanes.size(); i++) + { + float d = 0.0f; + float dmax = 0.0f; + float dmin = 0.0f; + for (j = 0; j < mVertices.size(); j++) + { + dmax = PxMax(dmax, mVertices[j].dot(mInputPlanes[i].n) + mInputPlanes[i].d); + dmin = PxMin(dmin, mVertices[j].dot(mInputPlanes[i].n) + mInputPlanes[i].d); + } + + float dr = dmax - dmin; + if (dr < planeTestEpsilon) + dr = 1.0f; // shouldn't happen. + d = dmax / dr; + // we have a better candidate try another one + if (d <= md) + continue; + // check if we dont have already that plane or if the normals are nearly the same + for (j = 0; j local::MAXDOT_MINANG) + { + for (PxU32 k = 0; k < mEdges.size(); k++) + { + if (mEdges[k].p != j) + continue; + if (mVertices[mEdges[k].v].dot(mInputPlanes[i].n) + mInputPlanes[i].d < 0) + { + d = 0; // so this plane wont get selected. + break; + } + } + } + } + if (d>md) + { + p = PxI32(i); + md = d; + } + } + return (md > epsilon) ? p : -1; +} + +////////////////////////////////////////////////////////////////////////// +// internal hull check +bool ConvexHull::assertIntact(float epsilon) const +{ + PxU32 i; + PxU32 estart = 0; + for (i = 0; i < mEdges.size(); i++) + { + if (mEdges[estart].p != mEdges[i].p) + { + estart = i; + } + PxU32 inext = i + 1; + if (inext >= mEdges.size() || mEdges[inext].p != mEdges[i].p) + { + inext = estart; + } + PX_ASSERT(mEdges[inext].p == mEdges[i].p); + PxI16 nb = mEdges[i].ea; + if (nb == 255 || nb == -1) + return false; + PX_ASSERT(nb != -1); + PX_ASSERT(i == PxU32(mEdges[PxU32(nb)].ea)); + // Check that the vertex of the next edge is the vertex of the adjacent half edge. + // Otherwise the two half edges are not really adjacent and we have a hole. + PX_ASSERT(mEdges[PxU32(nb)].v == mEdges[inext].v); + if (!(mEdges[PxU32(nb)].v == mEdges[inext].v)) + return false; + } + + for (i = 0; i < mEdges.size(); i++) + { + PX_ASSERT(local::eCOPLANAR == local::planeTest(mFacets[mEdges[i].p], mVertices[mEdges[i].v], epsilon)); + if (local::eCOPLANAR != local::planeTest(mFacets[mEdges[i].p], mVertices[mEdges[i].v], epsilon)) + return false; + if (mEdges[estart].p != mEdges[i].p) + { + estart = i; + } + PxU32 i1 = i + 1; + if (i1 >= mEdges.size() || mEdges[i1].p != mEdges[i].p) { + i1 = estart; + } + PxU32 i2 = i1 + 1; + if (i2 >= mEdges.size() || mEdges[i2].p != mEdges[i].p) { + i2 = estart; + } + if (i == i2) + continue; // i sliced tangent to an edge and created 2 meaningless edges + + // check the face normal against the triangle from edges + PxVec3 localNormal = (mVertices[mEdges[i1].v] - mVertices[mEdges[i].v]).cross(mVertices[mEdges[i2].v] - mVertices[mEdges[i1].v]); + const float m = localNormal.magnitude(); + if (m == 0.0f) + localNormal = PxVec3(1.f, 0.0f, 0.0f); + localNormal *= (1.0f / m); + if (localNormal.dot(mFacets[mEdges[i].p].n) <= 0.0f) + return false; + } + return true; +} + +// returns the maximum number of vertices on a face +PxU32 ConvexHull::maxNumVertsPerFace() const +{ + PxU32 maxVerts = 0; + PxU32 currentVerts = 0; + PxU32 estart = 0; + for (PxU32 i = 0; i < mEdges.size(); i++) + { + if (mEdges[estart].p != mEdges[i].p) + { + if(currentVerts > maxVerts) + { + maxVerts = currentVerts + 1; + } + currentVerts = 0; + estart = i; + } + else + { + currentVerts++; + } + } + return maxVerts; +} + +////////////////////////////////////////////////////////////////////////// +// slice the input convexHull with the slice plane +ConvexHull* physx::convexHullCrop(const ConvexHull& convex, const PxPlane& slice, float planeTestEpsilon) +{ + static const PxU8 invalidIndex = PxU8(-1); + PxU32 i; + PxU32 vertCountUnder = 0; // Running count of the vertices UNDER the slicing plane. + + PX_ASSERT(convex.getEdges().size() < 480); + + // Arrays of mapping information associated with features in the input convex. + // edgeflag[i].undermap - output index of input edge convex->edges[i] + // vertflag[i].undermap - output index of input vertex convex->vertices[i] + // vertflag[i].planetest - the side-of-plane classification of convex->vertices[i] + // (There are other members but they are unused.) + local::EdgeFlag edgeFlag[512]; + local::VertFlag vertFlag[256]; + + // Lists of output features. Populated during clipping. + // Coplanar edges have one sibling in tmpunderedges and one in coplanaredges. + // coplanaredges holds the sibling that belong to the new polygon created from slicing. + ConvexHull::HalfEdge tmpUnderEdges[512]; // The output edge list. + PxPlane tmpUnderPlanes[128]; // The output plane list. + local::Coplanar coplanarEdges[512]; // The coplanar edge list. + + PxU32 coplanarEdgesNum = 0; // Running count of coplanar edges. + + // Created vertices on the slicing plane (stored for output after clipping). + Ps::Array createdVerts; + + // Logical OR of individual vertex flags. + PxU32 convexClipFlags = 0; + + // Classify each vertex against the slicing plane as OVER | COPLANAR | UNDER. + // OVER - Vertex is over (outside) the slicing plane. Will not be output. + // COPLANAR - Vertex is on the slicing plane. A copy will be output. + // UNDER - Vertex is under (inside) the slicing plane. Will be output. + // We keep an array of information structures for each vertex in the input convex. + // vertflag[i].undermap - The (computed) index of convex->vertices[i] in the output. + // invalidIndex for OVER vertices - they are not output. + // initially invalidIndex for COPLANAR vertices - set later. + // vertflag[i].overmap - Unused - we don't care about the over part. + // vertflag[i].planetest - The classification (clip flag) of convex->vertices[i]. + for (i = 0; i < convex.getVertices().size(); i++) + { + local::PlaneTestResult vertexClipFlag = local::planeTest(slice, convex.getVertices()[i], planeTestEpsilon); + switch (vertexClipFlag) + { + case local::eOVER: + case local::eCOPLANAR: + vertFlag[i].undermap = invalidIndex; // Initially invalid for COPLANAR + vertFlag[i].overmap = invalidIndex; + break; + case local::eUNDER: + vertFlag[i].undermap = Ps::to8(vertCountUnder++); + vertFlag[i].overmap = invalidIndex; + break; + } + vertFlag[i].planetest = PxU8(vertexClipFlag); + convexClipFlags |= vertexClipFlag; + } + + // Check special case: everything UNDER or COPLANAR. + // This way we know we wont end up with silly faces / edges later on. + if ((convexClipFlags & local::eOVER) == 0) + { + // Just return a copy of the same convex. + ConvexHull* dst = PX_NEW_TEMP(ConvexHull)(convex); + return dst; + } + + PxU16 underEdgeCount = 0; // Running count of output edges. + PxU16 underPlanesCount = 0; // Running count of output planes. + + // Clipping Loop + // ============= + // + // for each plane + // + // for each edge + // + // if first UNDER & second !UNDER + // output current edge -> tmpunderedges + // if we have done the sibling + // connect current edge to its sibling + // set vout = first vertex of sibling + // else if second is COPLANAR + // if we havent already copied it + // copy second -> createdverts + // set vout = index of created vertex + // else + // generate a new vertex -> createdverts + // set vout = index of created vertex + // if vin is already set and vin != vout (non-trivial edge) + // output coplanar edge -> tmpunderedges (one sibling) + // set coplanaredge to new edge index (for connecting the other sibling) + // + // else if first !UNDER & second UNDER + // if we have done the sibling + // connect current edge to its sibling + // set vin = second vertex of sibling (this is a bit of a pain) + // else if first is COPLANAR + // if we havent already copied it + // copy first -> createdverts + // set vin = index of created vertex + // else + // generate a new vertex -> createdverts + // set vin = index of created vertex + // if vout is already set and vin != vout (non-trivial edge) + // output coplanar edge -> tmpunderedges (one sibling) + // set coplanaredge to new edge index (for connecting the other sibling) + // output current edge -> tmpunderedges + // + // else if first UNDER & second UNDER + // output current edge -> tmpunderedges + // + // next edge + // + // if part of current plane was UNDER + // output current plane -> tmpunderplanes + // + // if coplanaredge is set + // output coplanar edge -> coplanaredges + // + // next plane + // + + // Indexing is a bit tricky here: + // + // e0 - index of the current edge + // e1 - index of the next edge + // estart - index of the first edge in the current plane + // currentplane - index of the current plane + // enextface - first edge of next plane + + PxU32 e0 = 0; + + for (PxU32 currentplane = 0; currentplane < convex.getFacets().size(); currentplane++) + { + + PxU32 eStart = e0; + PxU32 eNextFace = 0xffffffff; + PxU32 e1 = e0 + 1; + + PxU8 vout = invalidIndex; + PxU8 vin = invalidIndex; + + PxU32 coplanarEdge = invalidIndex; + + // Logical OR of individual vertex flags in the current plane. + PxU32 planeSide = 0; + + do{ + + // Next edge modulo logic + if (e1 >= convex.getEdges().size() || convex.getEdges()[e1].p != currentplane) + { + eNextFace = e1; + e1 = eStart; + } + + const ConvexHull::HalfEdge& edge0 = convex.getEdges()[e0]; + const ConvexHull::HalfEdge& edge1 = convex.getEdges()[e1]; + const ConvexHull::HalfEdge& edgea = convex.getEdges()[PxU32(edge0.ea)]; + + planeSide |= vertFlag[edge0.v].planetest; + + if (vertFlag[edge0.v].planetest == local::eUNDER && vertFlag[edge1.v].planetest != local::eUNDER) + { + // first is UNDER, second is COPLANAR or OVER + + // Output current edge. + edgeFlag[e0].undermap = short(underEdgeCount); + tmpUnderEdges[underEdgeCount].v = vertFlag[edge0.v].undermap; + tmpUnderEdges[underEdgeCount].p = PxU8(underPlanesCount); + PX_ASSERT(tmpUnderEdges[underEdgeCount].v != invalidIndex); + + if (PxU32(edge0.ea) < e0) + { + // We have already done the sibling. + // Connect current edge to its sibling. + PX_ASSERT(edgeFlag[edge0.ea].undermap != invalidIndex); + tmpUnderEdges[underEdgeCount].ea = edgeFlag[edge0.ea].undermap; + tmpUnderEdges[edgeFlag[edge0.ea].undermap].ea = short(underEdgeCount); + // Set vout = first vertex of (output, clipped) sibling. + vout = tmpUnderEdges[edgeFlag[edge0.ea].undermap].v; + } + else if (vertFlag[edge1.v].planetest == local::eCOPLANAR) + { + // Boundary case. + // We output coplanar vertices once. + if (vertFlag[edge1.v].undermap == invalidIndex) + { + createdVerts.pushBack(convex.getVertices()[edge1.v]); + // Remember the index so we don't output it again. + vertFlag[edge1.v].undermap = Ps::to8(vertCountUnder++); + } + vout = vertFlag[edge1.v].undermap; + } + else + { + // Add new vertex. + const PxPlane& p0 = convex.getFacets()[edge0.p]; + const PxPlane& pa = convex.getFacets()[edgea.p]; + createdVerts.pushBack(threePlaneIntersection(p0, pa, slice)); + vout = Ps::to8(vertCountUnder++); + } + + // We added an edge, increment the counter + underEdgeCount++; + + if (vin != invalidIndex && vin != vout) + { + // We already have vin and a non-trivial edge + // Output coplanar edge + PX_ASSERT(vout != invalidIndex); + coplanarEdge = underEdgeCount; + tmpUnderEdges[underEdgeCount].v = vout; + tmpUnderEdges[underEdgeCount].p = PxU8(underPlanesCount); + tmpUnderEdges[underEdgeCount].ea = invalidIndex; + underEdgeCount++; + } + } + else if (vertFlag[edge0.v].planetest != local::eUNDER && vertFlag[edge1.v].planetest == local::eUNDER) + { + // First is OVER or COPLANAR, second is UNDER. + + if (PxU32(edge0.ea) < e0) + { + // We have already done the sibling. + // We need the second vertex of the sibling. + // Which is the vertex of the next edge in the adjacent poly. + int nea = edgeFlag[edge0.ea].undermap + 1; + int p = tmpUnderEdges[edgeFlag[edge0.ea].undermap].p; + if (nea >= underEdgeCount || tmpUnderEdges[nea].p != p) + { + // End of polygon, next edge is first edge + nea -= 2; + while (nea > 0 && tmpUnderEdges[nea - 1].p == p) + nea--; + } + vin = tmpUnderEdges[nea].v; + PX_ASSERT(vin < vertCountUnder); + } + else if (vertFlag[edge0.v].planetest == local::eCOPLANAR) + { + // Boundary case. + // We output coplanar vertices once. + if (vertFlag[edge0.v].undermap == invalidIndex) + { + createdVerts.pushBack(convex.getVertices()[edge0.v]); + // Remember the index so we don't output it again. + vertFlag[edge0.v].undermap = Ps::to8(vertCountUnder++); + } + vin = vertFlag[edge0.v].undermap; + } + else + { + // Add new vertex. + const PxPlane& p0 = convex.getFacets()[edge0.p]; + const PxPlane& pa = convex.getFacets()[edgea.p]; + createdVerts.pushBack(threePlaneIntersection(p0, pa, slice)); + vin = Ps::to8(vertCountUnder++); + } + + if (vout != invalidIndex && vin != vout) + { + // We have been in and out, Add the coplanar edge + coplanarEdge = underEdgeCount; + tmpUnderEdges[underEdgeCount].v = vout; + tmpUnderEdges[underEdgeCount].p = Ps::to8(underPlanesCount); + tmpUnderEdges[underEdgeCount].ea = invalidIndex; + underEdgeCount++; + } + + // Output current edge. + tmpUnderEdges[underEdgeCount].v = vin; + tmpUnderEdges[underEdgeCount].p = Ps::to8(underPlanesCount); + edgeFlag[e0].undermap = short(underEdgeCount); + + if (PxU32(edge0.ea) < e0) + { + // We have already done the sibling. + // Connect current edge to its sibling. + PX_ASSERT(edgeFlag[edge0.ea].undermap != invalidIndex); + tmpUnderEdges[underEdgeCount].ea = edgeFlag[edge0.ea].undermap; + tmpUnderEdges[edgeFlag[edge0.ea].undermap].ea = short(underEdgeCount); + } + + PX_ASSERT(edgeFlag[e0].undermap == underEdgeCount); + underEdgeCount++; + } + else if (vertFlag[edge0.v].planetest == local::eUNDER && vertFlag[edge1.v].planetest == local::eUNDER) + { + // Both UNDER + + // Output current edge. + edgeFlag[e0].undermap = short(underEdgeCount); + tmpUnderEdges[underEdgeCount].v = vertFlag[edge0.v].undermap; + tmpUnderEdges[underEdgeCount].p = Ps::to8(underPlanesCount); + if (PxU32(edge0.ea) < e0) + { + // We have already done the sibling. + // Connect current edge to its sibling. + PX_ASSERT(edgeFlag[edge0.ea].undermap != invalidIndex); + tmpUnderEdges[underEdgeCount].ea = edgeFlag[edge0.ea].undermap; + tmpUnderEdges[edgeFlag[edge0.ea].undermap].ea = short(underEdgeCount); + } + underEdgeCount++; + } + + e0 = e1; + e1++; // do the modulo at the beginning of the loop + + } while (e0 != eStart); + + e0 = eNextFace; + + if (planeSide & local::eUNDER) + { + // At least part of current plane is UNDER. + // Output current plane. + tmpUnderPlanes[underPlanesCount] = convex.getFacets()[currentplane]; + underPlanesCount++; + } + + if (coplanarEdge != invalidIndex) + { + // We have a coplanar edge. + // Add to coplanaredges for later processing. + // (One sibling is in place but one is missing) + PX_ASSERT(vin != invalidIndex); + PX_ASSERT(vout != invalidIndex); + PX_ASSERT(coplanarEdge != 511); + coplanarEdges[coplanarEdgesNum].ea = PxU8(coplanarEdge); + coplanarEdges[coplanarEdgesNum].v0 = vin; + coplanarEdges[coplanarEdgesNum].v1 = vout; + coplanarEdgesNum++; + } + + // Reset coplanar edge infos for next poly + vin = invalidIndex; + vout = invalidIndex; + coplanarEdge = invalidIndex; + } + + // Add the new plane to the mix: + if (coplanarEdgesNum > 0) + { + tmpUnderPlanes[underPlanesCount++] = slice; + } + + // Sort the coplanar edges in winding order. + for (i = 0; i < coplanarEdgesNum - 1; i++) + { + if (coplanarEdges[i].v1 != coplanarEdges[i + 1].v0) + { + PxU32 j = 0; + for (j = i + 2; j < coplanarEdgesNum; j++) + { + if (coplanarEdges[i].v1 == coplanarEdges[j].v0) + { + local::Coplanar tmp = coplanarEdges[i + 1]; + coplanarEdges[i + 1] = coplanarEdges[j]; + coplanarEdges[j] = tmp; + break; + } + } + if (j >= coplanarEdgesNum) + { + // PX_ASSERT(j(desc.points.data)); + const PxU32* ind = (reinterpret_cast(desc.indices.data)); + const PxHullPolygon* polygons = (reinterpret_cast(desc.polygons.data)); + PxVec3 mean(0.0f); + for (PxU32 i = 0; i < desc.points.count; i++) + mean += verts[i]; + mean *= (1.0f / desc.points.count); + + PxU8* indices = reinterpret_cast (PX_ALLOC_TEMP(sizeof(PxU8)*desc.indices.count, "PxU8")); + for (PxU32 i = 0; i < desc.indices.count; i++) + { + indices[i] = Ps::to8(ind[i]); + } + // we need to move the polygon data to internal format + Gu::HullPolygonData* polygonData = reinterpret_cast (PX_ALLOC_TEMP(sizeof(Gu::HullPolygonData)*desc.polygons.count, "Gu::HullPolygonData")); + for (PxU32 i = 0; i < desc.polygons.count; i++) + { + polygonData[i].mPlane = PxPlane(polygons[i].mPlane[0], polygons[i].mPlane[1], polygons[i].mPlane[2], polygons[i].mPlane[3]); + polygonData[i].mNbVerts = Ps::to8(polygons[i].mNbVerts); + polygonData[i].mVRef8 = polygons[i].mIndexBase; + } + + PxConvexMeshDesc inDesc; + inDesc.points.data = desc.points.data; + inDesc.points.count = desc.points.count; + + inDesc.polygons.data = polygonData; + inDesc.polygons.count = desc.polygons.count; + + inDesc.indices.data = indices; + inDesc.indices.count = desc.indices.count; + + // compute volume integrals to get basis axis + bool status = (desc.flags & PxConvexFlag::eFAST_INERTIA_COMPUTATION) ? + computeVolumeIntegralsEberlySIMD(inDesc, 1.0f, integrals, mean) : computeVolumeIntegralsEberly(inDesc, 1.0f, integrals, mean); + if (status) + { + Vec4V* pointsV = reinterpret_cast (PX_ALLOC_TEMP(sizeof(Vec4V)*desc.points.count, "Vec4V")); + for (PxU32 i = 0; i < desc.points.count; i++) + { + // safe to V4 load, same as volume integration - we allocate one more vector + pointsV[i] = V4LoadU(&verts[i].x); + } + + PxMat33 inertia; + integrals.getOriginInertia(inertia); + PxQuat inertiaQuat; + PxDiagonalize(inertia, inertiaQuat); + PxMat33 baseAxis(inertiaQuat); + Vec4V center = V4LoadU(&integrals.COM.x); + + const PxU32 numSteps = 20; + const float subStep = Ps::degToRad(float(360/numSteps)); + + float bestVolume = 1e9; + + for (PxU32 axis = 0; axis < 3; axis++) + { + for (PxU32 iStep = 0; iStep < numSteps; iStep++) + { + PxQuat quat(iStep*subStep, baseAxis[axis]); + + Vec4V transV = center; + Vec4V psidesV; + + const QuatV rotV = QuatVLoadU(&quat.x); + local::computeOBBSIMD(desc.points.count, pointsV, psidesV, rotV, transV); + + PxVec3 psides; + V3StoreU(Vec3V_From_Vec4V(psidesV), psides); + + const float volume = psides[0] * psides[1] * psides[2]; // the volume of the cube + + if (volume <= bestVolume) + { + bestVolume = volume; + sides = psides; + + V4StoreU(rotV, &matrix.q.x); + V3StoreU(Vec3V_From_Vec4V(transV), matrix.p); + } + } + } + + PX_FREE_AND_RESET(pointsV); + } + else + { + PX_FREE_AND_RESET(indices); + PX_FREE_AND_RESET(polygonData); + return false; + } + + PX_FREE_AND_RESET(indices); + PX_FREE_AND_RESET(polygonData); + return true; +} diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h new file mode 100644 index 000000000..c97ff2b61 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h @@ -0,0 +1,177 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONVEXHULLUTILS_H +#define PX_CONVEXHULLUTILS_H + +#include "foundation/PxMemory.h" +#include "foundation/PxPlane.h" + +#include "CmPhysXCommon.h" + +#include "PsUserAllocated.h" +#include "PsArray.h" +#include "PsMathUtils.h" + +#include "PxConvexMeshDesc.h" + +namespace physx +{ + + ////////////////////////////////////////////////////////////////////////// + // helper class for hull construction, holds the vertices and planes together + // while cropping the hull with planes + class ConvexHull : public Ps::UserAllocated + { + public: + + // Helper class for halfedge representation + class HalfEdge + { + public: + PxI16 ea; // the other half of the edge (index into edges list) + PxU8 v; // the vertex at the start of this edge (index into vertices list) + PxU8 p; // the facet on which this edge lies (index into facets list) + HalfEdge(){} + HalfEdge(PxI16 _ea, PxU8 _v, PxU8 _p) :ea(_ea), v(_v), p(_p){} + }; + + ConvexHull& operator = (const ConvexHull&); + + // construct the base cube hull from given max/min AABB + ConvexHull(const PxVec3& bmin, const PxVec3& bmax, const Ps::Array& inPlanes); + + // construct the base cube hull from given OBB + ConvexHull(const PxVec3& extent, const PxTransform& transform, const Ps::Array& inPlanes); + + // copy constructor + ConvexHull(const ConvexHull& srcHull) + : mInputPlanes(srcHull.getInputPlanes()) + { + copyHull(srcHull); + } + + // construct plain hull + ConvexHull(const Ps::Array& inPlanes) + : mInputPlanes(inPlanes) + { + } + + // finds the candidate plane, returns -1 otherwise + PxI32 findCandidatePlane(float planetestepsilon, float epsilon) const; + + // internal check of the hull integrity + bool assertIntact(float epsilon) const; + + // return vertices + const Ps::Array& getVertices() const + { + return mVertices; + } + + // return edges + const Ps::Array& getEdges() const + { + return mEdges; + } + + // return faces + const Ps::Array& getFacets() const + { + return mFacets; + } + + // return input planes + const Ps::Array& getInputPlanes() const + { + return mInputPlanes; + } + + // return vertices + Ps::Array& getVertices() + { + return mVertices; + } + + // return edges + Ps::Array& getEdges() + { + return mEdges; + } + + // return faces + Ps::Array& getFacets() + { + return mFacets; + } + + // returns the maximum number of vertices on a face + PxU32 maxNumVertsPerFace() const; + + // copy the hull from source + void copyHull(const ConvexHull& src) + { + mVertices.resize(src.getVertices().size()); + mEdges.resize(src.getEdges().size()); + mFacets.resize(src.getFacets().size()); + + PxMemCopy(mVertices.begin(), src.getVertices().begin(), src.getVertices().size()*sizeof(PxVec3)); + PxMemCopy(mEdges.begin(), src.getEdges().begin(), src.getEdges().size()*sizeof(HalfEdge)); + PxMemCopy(mFacets.begin(), src.getFacets().begin(), src.getFacets().size()*sizeof(PxPlane)); + } + + private: + Ps::Array mVertices; + Ps::Array mEdges; + Ps::Array mFacets; + const Ps::Array& mInputPlanes; + }; + + //////////////////////////////////////////////////////////////////////////| + // Crops the hull with a provided plane and with given epsilon + // returns new hull if succeeded + ConvexHull* convexHullCrop(const ConvexHull& convex, const PxPlane& slice, float planetestepsilon); + + //////////////////////////////////////////////////////////////////////////| + // three planes intersection + PX_FORCE_INLINE PxVec3 threePlaneIntersection(const PxPlane& p0, const PxPlane& p1, const PxPlane& p2) + { + PxMat33 mp = (PxMat33(p0.n, p1.n, p2.n)).getTranspose(); + PxMat33 mi = (mp).getInverse(); + PxVec3 b(p0.d, p1.d, p2.d); + return -mi.transform(b); + } + + ////////////////////////////////////////////////////////////////////////// + // Compute OBB around given convex hull + bool computeOBBFromConvex(const PxConvexMeshDesc& desc, PxVec3& sides, PxTransform& matrix); +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp new file mode 100644 index 000000000..c484be77d --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp @@ -0,0 +1,502 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "GuConvexMesh.h" +#include "PsFoundation.h" +#include "PsMathUtils.h" +#include "Cooking.h" + +#include "GuHillClimbing.h" +#include "GuBigConvexData2.h" +#include "GuInternal.h" +#include "GuSerialize.h" +#include "VolumeIntegration.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "VolumeIntegration.h" +#include "ConvexHullBuilder.h" +#include "ConvexMeshBuilder.h" +#include "BigConvexDataBuilder.h" + +#include "CmUtils.h" +#include "PsVecMath.h" + +using namespace physx; +using namespace Gu; +using namespace Ps::aos; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +ConvexMeshBuilder::ConvexMeshBuilder(const bool buildGRBData) : hullBuilder(&mHullData, buildGRBData), mBigConvexData(NULL), mMass(0.0f), mInertia(PxIdentity) +{ +} + +ConvexMeshBuilder::~ConvexMeshBuilder() +{ + PX_DELETE_AND_RESET(mBigConvexData); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// load the mesh data from given polygons +bool ConvexMeshBuilder::build(const PxConvexMeshDesc& desc, PxU32 gaussMapVertexLimit, bool validateOnly, ConvexHullLib* hullLib) +{ + if(!desc.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Gu::ConvexMesh::loadFromDesc: desc.isValid() failed!"); + return false; + } + + if(!loadConvexHull(desc, hullLib)) + return false; + + // Compute local bounds (*after* hull has been created) + PxBounds3 minMaxBounds; + computeBoundsAroundVertices(minMaxBounds, mHullData.mNbHullVertices, hullBuilder.mHullDataHullVertices); + mHullData.mAABB = CenterExtents(minMaxBounds); + + if(mHullData.mNbHullVertices > gaussMapVertexLimit) + { + if(!computeGaussMaps()) + { + return false; + } + } + + if(validateOnly) + return true; + +// TEST_INTERNAL_OBJECTS + computeInternalObjects(); +//~TEST_INTERNAL_OBJECTS + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PX_COMPILE_TIME_ASSERT(sizeof(PxMaterialTableIndex)==sizeof(PxU16)); +bool ConvexMeshBuilder::save(PxOutputStream& stream, bool platformMismatch) const +{ + // Export header + if(!writeHeader('C', 'V', 'X', 'M', PX_CONVEX_VERSION, platformMismatch, stream)) + return false; + + // Export serialization flags + PxU32 serialFlags = 0; + + writeDword(serialFlags, platformMismatch, stream); + + if(!hullBuilder.save(stream, platformMismatch)) + return false; + + // Export local bounds +// writeFloat(geomEpsilon, platformMismatch, stream); + writeFloat(0.0f, platformMismatch, stream); + writeFloat(mHullData.mAABB.getMin(0), platformMismatch, stream); + writeFloat(mHullData.mAABB.getMin(1), platformMismatch, stream); + writeFloat(mHullData.mAABB.getMin(2), platformMismatch, stream); + writeFloat(mHullData.mAABB.getMax(0), platformMismatch, stream); + writeFloat(mHullData.mAABB.getMax(1), platformMismatch, stream); + writeFloat(mHullData.mAABB.getMax(2), platformMismatch, stream); + + // Export mass info + writeFloat(mMass, platformMismatch, stream); + writeFloatBuffer(reinterpret_cast(&mInertia), 9, platformMismatch, stream); + writeFloatBuffer(&mHullData.mCenterOfMass.x, 3, platformMismatch, stream); + + // Export gaussmaps + if(mBigConvexData) + { + writeFloat(1.0f, platformMismatch, stream); //gauss map flag true + BigConvexDataBuilder SVMB(&mHullData, mBigConvexData, hullBuilder.mHullDataHullVertices); + SVMB.save(stream, platformMismatch); + } + else + writeFloat(-1.0f, platformMismatch, stream); //gauss map flag false + +// TEST_INTERNAL_OBJECTS + writeFloat(mHullData.mInternal.mRadius, platformMismatch, stream); + writeFloat(mHullData.mInternal.mExtents[0], platformMismatch, stream); + writeFloat(mHullData.mInternal.mExtents[1], platformMismatch, stream); + writeFloat(mHullData.mInternal.mExtents[2], platformMismatch, stream); +//~TEST_INTERNAL_OBJECTS + return true; +} + +////////////////////////////////////////////////////////////////////////// +// instead of saving the data into stream, we copy the mesh data +// into internal Gu::ConvexMesh. +bool ConvexMeshBuilder::copy(Gu::ConvexHullData& hullData, PxU32& nb) +{ + // hull builder data copy + hullBuilder.copy(hullData, nb); + + // mass props + hullData.mAABB = mHullData.mAABB; + hullData.mCenterOfMass = mHullData.mCenterOfMass; + + // big convex data + if(mBigConvexData) + { + hullData.mBigConvexRawData = &mBigConvexData->mData; + } + else + hullData.mBigConvexRawData = NULL; + + // internal data + hullData.mInternal.mRadius = mHullData.mInternal.mRadius; + hullData.mInternal.mExtents[0] = mHullData.mInternal.mExtents[0]; + hullData.mInternal.mExtents[1] = mHullData.mInternal.mExtents[1]; + hullData.mInternal.mExtents[2] = mHullData.mInternal.mExtents[2]; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// compute mass and inertia of the convex mesh +void ConvexMeshBuilder::computeMassInfo(bool lowerPrecision) +{ + if(mMass <= 0.0f) //not yet computed. + { + PxIntegrals integrals; + PxConvexMeshDesc meshDesc; + meshDesc.points.count = mHullData.mNbHullVertices; + meshDesc.points.data = hullBuilder.mHullDataHullVertices; + meshDesc.points.stride = sizeof(PxVec3); + + meshDesc.polygons.data = hullBuilder.mHullDataPolygons; + meshDesc.polygons.stride = sizeof(Gu::HullPolygonData); + meshDesc.polygons.count = hullBuilder.mHull->mNbPolygons; + + meshDesc.indices.data = hullBuilder.mHullDataVertexData8; + + // using the centroid of the convex for the volume integration solved accuracy issues in cases where the inertia tensor + // ended up close to not being positive definite and after a few further transforms the diagonalized inertia tensor ended + // up with negative values. + PxVec3 mean(0.0f); + for(PxU32 i=0; i < mHullData.mNbHullVertices; i++) + mean += hullBuilder.mHullDataHullVertices[i]; + mean *= (1.0f / mHullData.mNbHullVertices); + + bool status = lowerPrecision ? + computeVolumeIntegralsEberlySIMD(meshDesc, 1.0f, integrals, mean) : computeVolumeIntegralsEberly(meshDesc, 1.0f, integrals, mean); + if(status) + { + + integrals.getOriginInertia(reinterpret_cast(mInertia)); + mHullData.mCenterOfMass = integrals.COM; + + //note: the mass will be negative for an inside-out mesh! + if(mInertia.column0.isFinite() && mInertia.column1.isFinite() && mInertia.column2.isFinite() + && mHullData.mCenterOfMass.isFinite() && PxIsFinite(PxReal(integrals.mass))) + { + if (integrals.mass < 0) + { + Ps::getFoundation().error(PX_WARN, "Gu::ConvexMesh: Mesh has a negative volume! Is it open or do (some) faces have reversed winding? (Taking absolute value.)"); + integrals.mass = -integrals.mass; + mInertia = -mInertia; + } + + mMass = PxReal(integrals.mass); //set mass to valid value. + return; + } + } + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::ConvexMesh: Error computing mesh mass properties!\n"); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4996) // permitting use of gatherStrided until we have a replacement. +#endif + +bool ConvexMeshBuilder::loadConvexHull(const PxConvexMeshDesc& desc, ConvexHullLib* hullLib) +{ + // gather points + PxVec3* geometry = reinterpret_cast(PxAlloca(sizeof(PxVec3)*desc.points.count)); + Cooking::gatherStrided(desc.points.data, geometry, desc.points.count, sizeof(PxVec3), desc.points.stride); + + PxU32* topology = NULL; + + // gather indices + // store the indices into topology if we have the polygon data + if(desc.indices.data) + { + topology = reinterpret_cast(PxAlloca(sizeof(PxU32)*desc.indices.count)); + if (desc.flags & PxConvexFlag::e16_BIT_INDICES) + { + // conversion; 16 bit index -> 32 bit index & stride + PxU32* dest = topology; + const PxU32* pastLastDest = topology + desc.indices.count; + const PxU8* source = reinterpret_cast(desc.indices.data); + while (dest < pastLastDest) + { + const PxU16 * trig16 = reinterpret_cast(source); + *dest++ = *trig16; + source += desc.indices.stride; + } + } + else + { + Cooking::gatherStrided(desc.indices.data, topology, desc.indices.count, sizeof(PxU32), desc.indices.stride); + } + } + + // gather polygons + PxHullPolygon* hullPolygons = NULL; + if(desc.polygons.data) + { + hullPolygons = reinterpret_cast(PxAlloca(sizeof(PxHullPolygon)*desc.polygons.count)); + Cooking::gatherStrided(desc.polygons.data,hullPolygons,desc.polygons.count,sizeof(PxHullPolygon),desc.polygons.stride); + + // if user polygons, make sure the largest one is the first one + if (!hullLib) + { + PxU32 largestPolygon = 0; + for (PxU32 i = 1; i < desc.polygons.count; i++) + { + if(hullPolygons[i].mNbVerts > hullPolygons[largestPolygon].mNbVerts) + largestPolygon = i; + } + if(largestPolygon != 0) + { + PxHullPolygon movedPolygon = hullPolygons[0]; + hullPolygons[0] = hullPolygons[largestPolygon]; + hullPolygons[largestPolygon] = movedPolygon; + } + } + } + + const bool doValidation = desc.flags & PxConvexFlag::eDISABLE_MESH_VALIDATION ? false : true; + if(!hullBuilder.init(desc.points.count, geometry, topology, desc.indices.count, desc.polygons.count, hullPolygons, doValidation, hullLib)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Gu::ConvexMesh::loadConvexHull: convex hull init failed!"); + return false; + } + computeMassInfo(desc.flags & PxConvexFlag::eFAST_INERTIA_COMPUTATION); + + return true; +} + +#if PX_VC +#pragma warning(pop) +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// compute polygons from given triangles. This is support function used in extensions. We do not accept triangles as an input for convex mesh desc. +bool ConvexMeshBuilder::computeHullPolygons(const PxU32& nbVerts,const PxVec3* verts, const PxU32& nbTriangles, const PxU32* triangles, PxAllocatorCallback& inAllocator, + PxU32& outNbVerts, PxVec3*& outVertices , PxU32& nbIndices, PxU32*& indices, PxU32& nbPolygons, PxHullPolygon*& polygons) +{ + if(!hullBuilder.computeHullPolygons(nbVerts,verts,nbTriangles,triangles)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexMeshBuilder::computeHullPolygons: compute convex hull polygons failed. Provided triangles dont form a convex hull."); + return false; + } + + outNbVerts = hullBuilder.mHull->mNbHullVertices; + nbPolygons = hullBuilder.mHull->mNbPolygons; + + outVertices = reinterpret_cast(inAllocator.allocate(outNbVerts*sizeof(PxVec3),"PxVec3",__FILE__,__LINE__)); + PxMemCopy(outVertices,hullBuilder.mHullDataHullVertices,outNbVerts*sizeof(PxVec3)); + + nbIndices = 0; + for (PxU32 i = 0; i < nbPolygons; i++) + { + nbIndices += hullBuilder.mHullDataPolygons[i].mNbVerts; + } + + indices = reinterpret_cast(inAllocator.allocate(nbIndices*sizeof(PxU32),"PxU32",__FILE__,__LINE__)); + for (PxU32 i = 0; i < nbIndices; i++) + { + indices[i] = hullBuilder.mHullDataVertexData8[i]; + } + + polygons = reinterpret_cast(inAllocator.allocate(nbPolygons*sizeof(PxHullPolygon),"PxHullPolygon",__FILE__,__LINE__)); + + for (PxU32 i = 0; i < nbPolygons; i++) + { + const Gu::HullPolygonData& polygonData = hullBuilder.mHullDataPolygons[i]; + PxHullPolygon& outPolygon = polygons[i]; + outPolygon.mPlane[0] = polygonData.mPlane.n.x; + outPolygon.mPlane[1] = polygonData.mPlane.n.y; + outPolygon.mPlane[2] = polygonData.mPlane.n.z; + outPolygon.mPlane[3] = polygonData.mPlane.d; + + outPolygon.mNbVerts = polygonData.mNbVerts; + outPolygon.mIndexBase = polygonData.mVRef8; + + for (PxU32 j = 0; j < polygonData.mNbVerts; j++) + { + PX_ASSERT(indices[outPolygon.mIndexBase + j] == hullBuilder.mHullDataVertexData8[polygonData.mVRef8+j]); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// compute big convex data +bool ConvexMeshBuilder::computeGaussMaps() +{ + // The number of polygons is limited to 256 because the gaussmap encode 256 polys maximum + + PxU32 density = 16; + // density = 64; + // density = 8; + // density = 2; + + PX_DELETE(mBigConvexData); + PX_NEW_SERIALIZED(mBigConvexData,BigConvexData); + BigConvexDataBuilder SVMB(&mHullData, mBigConvexData, hullBuilder.mHullDataHullVertices); + // valencies we need to compute first, they are needed for min/max precompute + SVMB.computeValencies(hullBuilder); + SVMB.precompute(density); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TEST_INTERNAL_OBJECTS + +static void ComputeInternalExtent(Gu::ConvexHullData& data, const Gu::HullPolygonData* hullPolys) +{ + const PxVec3 e = data.mAABB.getMax() - data.mAABB.getMin(); + + // PT: For that formula, see %SDKRoot%\InternalDocumentation\Cooking\InternalExtents.png + const float r = data.mInternal.mRadius / sqrtf(3.0f); + + const float epsilon = 1E-7f; + + const PxU32 largestExtent = Ps::largestAxis(e); + PxU32 e0 = Ps::getNextIndex3(largestExtent); + PxU32 e1 = Ps::getNextIndex3(e0); + if(e[e0] < e[e1]) + Ps::swap(e0,e1); + + data.mInternal.mExtents[0] = FLT_MAX; + data.mInternal.mExtents[1] = FLT_MAX; + data.mInternal.mExtents[2] = FLT_MAX; + + // PT: the following code does ray-vs-plane raycasts. + + // find the largest box along the largest extent, with given internal radius + for(PxU32 i = 0; i < data.mNbPolygons; i++) + { + // concurrent with search direction + const float d = hullPolys[i].mPlane.n[largestExtent]; + if((-epsilon < d && d < epsilon)) + continue; + + const float numBase = -hullPolys[i].mPlane.d - hullPolys[i].mPlane.n.dot(data.mCenterOfMass); + const float denBase = 1.0f/hullPolys[i].mPlane.n[largestExtent]; + const float numn0 = r * hullPolys[i].mPlane.n[e0]; + const float numn1 = r * hullPolys[i].mPlane.n[e1]; + + float num = numBase - numn0 - numn1; + float ext = PxMax(fabsf(num*denBase), r); + if(ext < data.mInternal.mExtents[largestExtent]) + data.mInternal.mExtents[largestExtent] = ext; + + num = numBase - numn0 + numn1; + ext = PxMax(fabsf(num *denBase), r); + if(ext < data.mInternal.mExtents[largestExtent]) + data.mInternal.mExtents[largestExtent] = ext; + + num = numBase + numn0 + numn1; + ext = PxMax(fabsf(num *denBase), r); + if(ext < data.mInternal.mExtents[largestExtent]) + data.mInternal.mExtents[largestExtent] = ext; + + num = numBase + numn0 - numn1; + ext = PxMax(fabsf(num *denBase), r); + if(ext < data.mInternal.mExtents[largestExtent]) + data.mInternal.mExtents[largestExtent] = ext; + } + + // Refine the box along e0,e1 + for(PxU32 i = 0; i < data.mNbPolygons; i++) + { + const float denumAdd = hullPolys[i].mPlane.n[e0] + hullPolys[i].mPlane.n[e1]; + const float denumSub = hullPolys[i].mPlane.n[e0] - hullPolys[i].mPlane.n[e1]; + + const float numBase = -hullPolys[i].mPlane.d - hullPolys[i].mPlane.n.dot(data.mCenterOfMass); + const float numn0 = data.mInternal.mExtents[largestExtent] * hullPolys[i].mPlane.n[largestExtent]; + + if(!(-epsilon < denumAdd && denumAdd < epsilon)) + { + float num = numBase - numn0; + float ext = PxMax(fabsf(num/ denumAdd), r); + if(ext < data.mInternal.mExtents[e0]) + data.mInternal.mExtents[e0] = ext; + + num = numBase + numn0; + ext = PxMax(fabsf(num / denumAdd), r); + if(ext < data.mInternal.mExtents[e0]) + data.mInternal.mExtents[e0] = ext; + } + + if(!(-epsilon < denumSub && denumSub < epsilon)) + { + float num = numBase - numn0; + float ext = PxMax(fabsf(num / denumSub), r); + if(ext < data.mInternal.mExtents[e0]) + data.mInternal.mExtents[e0] = ext; + + num = numBase + numn0; + ext = PxMax(fabsf(num / denumSub), r); + if(ext < data.mInternal.mExtents[e0]) + data.mInternal.mExtents[e0] = ext; + } + } + data.mInternal.mExtents[e1] = data.mInternal.mExtents[e0]; +} + +////////////////////////////////////////////////////////////////////////// +// compute internal objects, get the internal extent and radius +void ConvexMeshBuilder::computeInternalObjects() +{ + const Gu::HullPolygonData* hullPolys = hullBuilder.mHullDataPolygons; + Gu::ConvexHullData& data = mHullData; + + // compute the internal radius + data.mInternal.mRadius = FLT_MAX; + for(PxU32 i=0;i +PX_INLINE PxPlane PlaneEquation(const T& t, const PxVec3* verts) +{ + const PxVec3& p0 = verts[t.v[0]]; + const PxVec3& p1 = verts[t.v[1]]; + const PxVec3& p2 = verts[t.v[2]]; + return PxPlane(p0, p1, p2); +} + +////////////////////////////////////////////////////////////////////////// +// negate plane +static PX_FORCE_INLINE void negatePlane(Gu::HullPolygonData& data) +{ + data.mPlane.n = -data.mPlane.n; + data.mPlane.d = -data.mPlane.d; +} + +////////////////////////////////////////////////////////////////////////// +// Inverse a buffer in-place +static bool inverseBuffer(PxU32 nbEntries, PxU8* entries) +{ + if(!nbEntries || !entries) return false; + + for(PxU32 i=0; i < (nbEntries>>1); i++) + Ps::swap(entries[i], entries[nbEntries-1-i]); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// Extracts a line-strip from a list of non-sorted line-segments (slow) +static bool findLineStrip(Ps::Array& lineStrip, const Ps::Array& lineSegments) +{ + // Ex: + // + // 4-2 + // 0-1 + // 2-3 + // 4-0 + // 7-3 + // 7-1 + // + // => 0-1-7-3-2-4-0 + + // 0-0-1-1-2-2-3-3-4-4-7-7 + + // 0-1 + // 0-4 + // 1-7 + // 2-3 + // 2-4 + // 3-7 + + // Naive implementation below + + Ps::Array Copy(lineSegments); + +RunAgain: + { + PxU32 nbSegments = Copy.size(); + for(PxU32 j=0;j remove both + PX_ASSERT(Copy.size()>=2); + Copy.remove(i); + Copy.remove(j); + goto RunAgain; + } + } + } + // Goes through when everything's fine + } + + PxU32 ref0 = 0xffffffff; + PxU32 ref1 = 0xffffffff; + if(Copy.size()>=1) + { + Pair* Segments = Copy.begin(); + if(Segments) + { + ref0 = Segments->id0; + ref1 = Segments->id1; + lineStrip.pushBack(ref0); + lineStrip.pushBack(ref1); + PX_ASSERT(Copy.size()>=1); + Copy.remove(0); + } + } + +Wrap: + // Look for same vertex ref in remaining segments + PxU32 nb = Copy.size(); + if(!nb) + { + // ### check the line is actually closed? + return true; + } + + for(PxU32 i=0;i r1 - x + lineStrip.pushBack(newRef0); // Output the other reference + ref0 = newRef1; + ref1 = newRef0; + Copy.remove(i); + goto Wrap; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +// Test for duplicate triangles +PX_COMPILE_TIME_ASSERT(sizeof(Gu::TriangleT)==sizeof(PxVec3)); // ... +static bool TestDuplicateTriangles(PxU32& nbFaces, Gu::TriangleT* faces, bool repair) +{ + if(!nbFaces || !faces) + return true; + + Gu::TriangleT* indices32 = reinterpret_cast*>(PxAlloca(nbFaces*sizeof(Gu::TriangleT))); + for(PxU32 i=0;i(indices32), nbFaces); + REDUCEDCLOUD rc; + reducer.Reduce(&rc); + if(rc.NbRVerts* curTri = reinterpret_cast*>(&rc.RVerts[i]); + faces[i].v[0] = curTri->v[0]; + faces[i].v[1] = curTri->v[1]; + faces[i].v[2] = curTri->v[2]; + } + } + return false; // Test failed + } + return true; // Test succeeded +} + +////////////////////////////////////////////////////////////////////////// +// plane culling test +static PX_FORCE_INLINE bool testCulling(const Gu::TriangleT& triangle, const PxVec3* verts, const PxVec3& center) +{ + const PxPlane plane(verts[triangle.v[0]], verts[triangle.v[1]], verts[triangle.v[2]]); + return plane.distance(center)>0.0f; +} + +////////////////////////////////////////////////////////////////////////// +// face normals test +static bool TestUnifiedNormals(PxU32 nbVerts, const PxVec3* verts, PxU32 nbFaces, Gu::TriangleT* faces, bool repair) +{ + if(!nbVerts || !verts || !nbFaces || !faces) + return false; + + // Unify normals so that all hull faces are well oriented + + // Compute geometric center - we need a vertex inside the hull + const float coeff = 1.0f / float(nbVerts); + PxVec3 geomCenter(0.0f, 0.0f, 0.0f); + for(PxU32 i=0;i* faces, PxU32& nbVerts, PxVec3* verts) +{ + // Brute force mesh cleaning. + // PT: I added this back on Feb-18-05 because it fixes bugs with hulls from QHull. + MeshCleaner cleaner(nbVerts, verts, nbFaces, faces->v, 0.0f); + if (!cleaner.mNbTris) + return false; + + nbVerts = cleaner.mNbVerts; + nbFaces = cleaner.mNbTris; + + PxMemCopy(verts, cleaner.mVerts, cleaner.mNbVerts*sizeof(PxVec3)); + + for (PxU32 i = 0; i < cleaner.mNbTris; i++) + { + faces[i].v[0] = cleaner.mIndices[i * 3 + 0]; + faces[i].v[1] = cleaner.mIndices[i * 3 + 1]; + faces[i].v[2] = cleaner.mIndices[i * 3 + 2]; + } + + // Get rid of duplicates + TestDuplicateTriangles(nbFaces, faces, true); + + // Unify normals + TestUnifiedNormals(nbVerts, verts, nbFaces, faces, true); + + // Remove zero-area triangles + // TestZeroAreaTriangles(nbFaces, faces, verts, true); + + // Unify normals again + TestUnifiedNormals(nbVerts, verts, nbFaces, faces, true); + + // Get rid of duplicates again + TestDuplicateTriangles(nbFaces, faces, true); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// check the newly constructed faces +static bool CheckFaces(PxU32 nbFaces, const Gu::TriangleT* faces, PxU32 nbVerts, const PxVec3* verts) +{ + // Remove const since we use functions that can do both testing & repairing. But we won't change the data. + Gu::TriangleT* f = const_cast*>(faces); + + // Test duplicate faces + if(!TestDuplicateTriangles(nbFaces, f, false)) + return false; + + // Test unified normals + if(!TestUnifiedNormals(nbVerts, verts, nbFaces, f, false)) + return false; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// compute the newell plane from the face verts +static bool computeNewellPlane(PxPlane& plane, PxU32 nbVerts, const PxU8* indices, const PxVec3* verts) +{ + if(!nbVerts || !indices || !verts) + return false; + + PxVec3 centroid(0,0,0), normal(0,0,0); + for(PxU32 i=nbVerts-1, j=0; j& polygon_data, const ConvexPolygonsBuilder& hull, Ps::Array& triangle_data, Ps::Array& redundantVertices) +{ + const PxU32* dFaces = reinterpret_cast(hull.getFaces()); + bool needToSplitPolygons = false; + + bool* polygonMarkers = reinterpret_cast(PxAlloca(nb_polygons*sizeof(bool))); + PxMemZero(polygonMarkers, nb_polygons*sizeof(bool)); + + bool* redundancyMarkers = reinterpret_cast(PxAlloca(redundantVertices.size()*sizeof(bool))); + PxMemZero(redundancyMarkers, redundantVertices.size()*sizeof(bool)); + + // parse through the redundant vertices and if we cannot remove them split just the actual polygon if possible + Ps::Array polygonsContainer; + PxU32 numEntries = 0; + for (PxU32 i = redundantVertices.size(); i--;) + { + numEntries = 0; + polygonsContainer.clear(); + // go through polygons, if polygons does have only 3 verts we cannot remove any vertex from it, try to decompose the second one + PxU32* Data = polygon_data.begin(); + for(PxU32 t=0;t=3); // Else something very wrong happened... + + for(PxU32 j=0;j newPolygon_data; + Ps::Array newTriangle_data; + PxU32 newNb_polygons = 0; + + PxU32* data = polygon_data.begin(); + PxU32* triData = triangle_data.begin(); + for(PxU32 i=0;i& polygon_data, const ConvexHull& hull) +* \param nb_polygons [out] number of extracted polygons +* \param polygon_data [out] polygon data: (Nb indices, index 0, index 1... index N)(Nb indices, index 0, index 1... index N)(...) +* \param hull [in] convex hull +* \param triangle_data [out] triangle data +* \param rendundantVertices [out] redundant vertices found inside the polygons - we want to remove them because of PCM +* \return true if success +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static bool extractHullPolygons(PxU32& nb_polygons, Ps::Array& polygon_data, const ConvexPolygonsBuilder& hull, Ps::Array* triangle_data, Ps::Array& rendundantVertices) +{ + PxU32 nbFaces = hull.getNbFaces(); + const PxVec3* hullVerts = hull.mHullDataHullVertices; + const PxU32 nbVertices = hull.mHull->mNbHullVertices; + + const PxU16* wFaces = NULL; + const PxU32* dFaces = reinterpret_cast(hull.getFaces()); + PX_ASSERT(wFaces || dFaces); + + ADJACENCIESCREATE create; + create.NbFaces = nbFaces; + create.DFaces = dFaces; + create.WFaces = wFaces; + create.Verts = hullVerts; + //Create.Epsilon = 0.01f; // PT: trying to fix Rob Elam bug. Also fixes TTP 2467 + // Create.Epsilon = 0.001f; // PT: for "Bruno's bug" + create.Epsilon = 0.005f; // PT: middle-ground seems to fix both. Expose this param? + + + AdjacenciesBuilder adj; + if(!adj.Init(create)) return false; + + PxU32 nbBoundaryEdges = adj.ComputeNbBoundaryEdges(); + if(nbBoundaryEdges) return false; // A valid hull shouldn't have open edges!! + + bool* markers = reinterpret_cast(PxAlloca(nbFaces*sizeof(bool))); + PxMemZero(markers, nbFaces*sizeof(bool)); + + PxU8* vertexMarkers = reinterpret_cast(PxAlloca(nbVertices*sizeof(PxU8))); + PxMemZero(vertexMarkers, nbVertices*sizeof(PxU8)); + + PxU32 currentFace = 0; // Start with first triangle + nb_polygons = 0; + do + { + currentFace = 0; + while(currentFace& indices, const AdjTriangle* faces, PxU32 current, bool* inMarkers) + { + if(inMarkers[current]) return; + inMarkers[current] = true; + + indices.pushBack(current); + const AdjTriangle& AT = faces[current]; + + // We can floodfill through inactive edges since the mesh is convex (inactive==planar) + if(!AT.HasActiveEdge01()) FloodFill(indices, faces, AT.GetAdjTri(EDGE01), inMarkers); + if(!AT.HasActiveEdge20()) FloodFill(indices, faces, AT.GetAdjTri(EDGE02), inMarkers); + if(!AT.HasActiveEdge12()) FloodFill(indices, faces, AT.GetAdjTri(EDGE12), inMarkers); + } + + static bool GetNeighborFace(PxU32 index,PxU32 triangleIndex,const AdjTriangle* faces, const PxU32* dfaces, PxU32& neighbor, PxU32& current) + { + PxU32 currentIndex = index; + PxU32 previousIndex = index; + bool firstFace = true; + bool next = true; + while (next) + { + const AdjTriangle& currentAT = faces[currentIndex]; + PxU32 refTr0 = dfaces[currentIndex*3 + 0]; + PxU32 refTr1 = dfaces[currentIndex*3 + 1]; + + PxU32 edge[2]; + edge[0] = 1; + edge[1] = 2; + if(triangleIndex == refTr0) + { + edge[0] = 0; + edge[1] = 1; + } + else + { + if(triangleIndex == refTr1) + { + edge[0] = 0; + edge[1] = 2; + } + } + + if(currentAT.HasActiveEdge(edge[0]) && currentAT.HasActiveEdge(edge[1])) + { + return false; + } + + if(!currentAT.HasActiveEdge(edge[0]) && !currentAT.HasActiveEdge(edge[1])) + { + // not interested in testing transition vertices + if(currentIndex == index) + { + return false; + } + + // transition one + for (PxU32 i = 0; i < 2; i++) + { + PxU32 testIndex = currentAT.GetAdjTri(SharedEdgeIndex(edge[i])); + + // exit if we circle around the vertex back to beginning + if(testIndex == index && previousIndex != index) + { + return false; + } + + if(testIndex != previousIndex) + { + // move to next + previousIndex = currentIndex; + currentIndex = testIndex; + break; + } + } + } + else + { + if(!currentAT.HasActiveEdge(edge[0])) + { + PxU32 t = edge[0]; + edge[0] = edge[1]; + edge[1] = t; + } + + if(currentAT.HasActiveEdge(edge[0])) + { + PxU32 testIndex = currentAT.GetAdjTri(SharedEdgeIndex(edge[0])); + if(firstFace) + { + firstFace = false; + } + else + { + neighbor = testIndex; + current = currentIndex; + return true; + } + } + + if(!currentAT.HasActiveEdge(edge[1])) + { + PxU32 testIndex = currentAT.GetAdjTri(SharedEdgeIndex(edge[1])); + if(testIndex != index) + { + previousIndex = currentIndex; + currentIndex = testIndex; + } + } + } + + } + + return false; + } + + static bool CheckFloodFillFace(PxU32 index,const AdjTriangle* faces, const PxU32* dfaces) + { + if(!dfaces) + return true; + + const AdjTriangle& checkedAT = faces[index]; + + PxU32 refTr0 = dfaces[index*3 + 0]; + PxU32 refTr1 = dfaces[index*3 + 1]; + PxU32 refTr2 = dfaces[index*3 + 2]; + + for (PxU32 i = 0; i < 3; i++) + { + if(!checkedAT.HasActiveEdge(i)) + { + PxU32 testTr0 = refTr1; + PxU32 testTr1 = refTr2; + PxU32 testIndex0 = 0; + PxU32 testIndex1 = 1; + if(i == 0) + { + testTr0 = refTr0; + testTr1 = refTr1; + testIndex0 = 1; + testIndex1 = 2; + } + else + { + if(i == 1) + { + testTr0 = refTr0; + testTr1 = refTr2; + testIndex0 = 0; + testIndex1 = 2; + } + } + + PxU32 adjFaceTested = checkedAT.GetAdjTri(SharedEdgeIndex(testIndex0)); + + PxU32 neighborIndex00; + PxU32 neighborIndex01; + bool found0 = GetNeighborFace(index,testTr0,faces,dfaces, neighborIndex00, neighborIndex01); + PxU32 neighborIndex10; + PxU32 neighborIndex11; + bool found1 = GetNeighborFace(adjFaceTested,testTr0,faces,dfaces, neighborIndex10, neighborIndex11); + + if(found0 && found1 && neighborIndex00 == neighborIndex11 && neighborIndex01 == neighborIndex10) + { + return false; + } + + adjFaceTested = checkedAT.GetAdjTri(SharedEdgeIndex(testIndex1)); + found0 = GetNeighborFace(index,testTr1,faces,dfaces,neighborIndex00,neighborIndex01); + found1 = GetNeighborFace(adjFaceTested,testTr1,faces,dfaces,neighborIndex10,neighborIndex11); + + if(found0 && found1 && neighborIndex00 == neighborIndex11 && neighborIndex01 == neighborIndex10) + { + return false; + } + + } + } + + return true; + } + + static bool CheckFloodFill(Ps::Array& indices,AdjTriangle* faces,bool* inMarkers, const PxU32* dfaces) + { + bool valid = true; + + for(PxU32 i=0;i indices; // Indices of triangles forming hull polygon + + bool doFill = true; + while (doFill) + { + Local::FloodFill(indices, adj.mFaces, currentFace, markers); + + doFill = Local::CheckFloodFill(indices,adj.mFaces,markers, dFaces); + } + + // Now it would be nice to recreate a closed linestrip, similar to silhouette extraction. The line is composed of active edges, this time. + + + Ps::Array activeSegments; + //Container ActiveSegments; + // Loop through triangles composing the polygon + for(PxU32 i=0;i lineStrip; + if(findLineStrip(lineStrip, activeSegments)) + { + PxU32 nb = lineStrip.size(); + if(nb) + { + const PxU32* entries = lineStrip.begin(); + PX_ASSERT(entries[0] == entries[nb-1]); // findLineStrip() is designed that way. Might not be what we want! + + // We get rid of the last (duplicated) index + polygon_data.pushBack(nb-1); + for (PxU32 i = 0; i < nb-1; i++) + { + vertexMarkers[entries[i]]++; + polygon_data.pushBack(entries[i]); + } + nb_polygons++; + + // Loop through vertices composing the line strip polygon end mark the redundant vertices inside the polygon + for(PxU32 i=0;ipushBack(indices.size()); + for (PxU32 j = 0; j < indices.size(); j++) + triangle_data->pushBack(indices[j]); + } + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Meshmerizer::extractHullPolygons: line strip extraction failed"); + return false; + } + } + } + while(currentFace!=nbFaces); + + for (PxU32 i = 0; i < nbVertices; i++) + { + if(vertexMarkers[i] < 3) + { + if(rendundantVertices.find(i) == rendundantVertices.end()) + rendundantVertices.pushBack(i); + } + } + + if(rendundantVertices.size() > 0 && triangle_data) + checkRedundantVertices(nb_polygons,polygon_data,hull,*triangle_data,rendundantVertices); + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +ConvexPolygonsBuilder::ConvexPolygonsBuilder(Gu::ConvexHullData* hull, const bool buildGRBData) + : ConvexHullBuilder(hull, buildGRBData), mNbHullFaces(0), mFaces(NULL) +{ +} + +////////////////////////////////////////////////////////////////////////// + +ConvexPolygonsBuilder::~ConvexPolygonsBuilder() +{ + PX_DELETE_POD(mFaces); +} + +////////////////////////////////////////////////////////////////////////// +// compute hull polygons from given hull triangles +bool ConvexPolygonsBuilder::computeHullPolygons(const PxU32& nbVerts,const PxVec3* verts, const PxU32& nbTriangles, const PxU32* triangles) +{ + PX_ASSERT(triangles); + PX_ASSERT(verts); + + mHullDataHullVertices = NULL; + mHullDataPolygons = NULL; + mHullDataVertexData8 = NULL; + mHullDataFacesByEdges8 = NULL; + mHullDataFacesByVertices8 = NULL; + + mNbHullFaces = nbTriangles; + mHull->mNbHullVertices = Ps::to8(nbVerts); + // allocate additional vec3 for V4 safe load in VolumeInteration + mHullDataHullVertices = reinterpret_cast(PX_ALLOC(sizeof(PxVec3) * mHull->mNbHullVertices + 1, "PxVec3")); + PxMemCopy(mHullDataHullVertices, verts, mHull->mNbHullVertices*sizeof(PxVec3)); + + mFaces = PX_NEW(HullTriangleData)[mNbHullFaces]; + for(PxU32 i=0;i* hullAsIndexedTriangle = reinterpret_cast*>(mFaces); + + // We don't trust the user at all... So, clean the hull. + PxU32 nbHullVerts = mHull->mNbHullVertices; + CleanFaces(mNbHullFaces, hullAsIndexedTriangle, nbHullVerts, mHullDataHullVertices); + PX_ASSERT(nbHullVerts<256); + mHull->mNbHullVertices = Ps::to8(nbHullVerts); + + // ...and then run the full tests again. + if(!CheckFaces(mNbHullFaces, hullAsIndexedTriangle, mHull->mNbHullVertices, mHullDataHullVertices)) + return false; + + // Transform triangles-to-polygons + if(!createPolygonData()) + return false; + + return checkHullPolygons(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* Computes polygon data. +* \return true if success +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool ConvexPolygonsBuilder::createPolygonData() +{ + // Cleanup + mHull->mNbPolygons = 0; + PX_DELETE_POD(mHullDataVertexData8); + PX_DELETE_POD(mHullDataFacesByVertices8); + PX_FREE_AND_RESET(mHullDataPolygons); + + // Extract polygon data from triangle data + Ps::Array temp; + Ps::Array temp2; + Ps::Array rendundantVertices; + PxU32 nbPolygons; + if(!extractHullPolygons(nbPolygons, temp, *this, &temp2,rendundantVertices)) + return false; + + PxVec3* reducedHullDataHullVertices = mHullDataHullVertices; + PxU8 numReducedHullDataVertices = mHull->mNbHullVertices; + + if(rendundantVertices.size() > 0) + { + numReducedHullDataVertices = Ps::to8(mHull->mNbHullVertices - rendundantVertices.size()); + reducedHullDataHullVertices = static_cast (PX_ALLOC_TEMP(sizeof(PxVec3)*numReducedHullDataVertices,"Reduced vertices hull data")); + PxU8* remapTable = PX_NEW(PxU8)[mHull->mNbHullVertices]; + + PxU8 currentIndex = 0; + for (PxU8 i = 0; i < mHull->mNbHullVertices; i++) + { + if(rendundantVertices.find(i) == rendundantVertices.end()) + { + PX_ASSERT(currentIndex < numReducedHullDataVertices); + reducedHullDataHullVertices[currentIndex] = mHullDataHullVertices[i]; + remapTable[i] = currentIndex; + currentIndex++; + } + else + { + remapTable[i] = 0xFF; + } + } + + PxU32* data = temp.begin(); + for(PxU32 i=0;i=3); // Else something very wrong happened... + + for(PxU32 j=0;jmNbHullVertices); + data[j] = remapTable[data[j]]; + } + + data += nbVerts; + } + + PX_DELETE_POD(remapTable); + } + + if(nbPolygons>255) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder: convex hull has more than 255 polygons!"); + return false; + } + + // Precompute hull polygon structures + mHull->mNbPolygons = Ps::to8(nbPolygons); + mHullDataPolygons = reinterpret_cast(PX_ALLOC(sizeof(Gu::HullPolygonData)*mHull->mNbPolygons, "Gu::HullPolygonData")); + PxMemZero(mHullDataPolygons, sizeof(Gu::HullPolygonData)*mHull->mNbPolygons); + + // The winding hasn't been preserved so we need to handle this. Basically we need to "unify normals" + // exactly as we did at hull creation time - except this time we work on polygons + PxVec3 geomCenter; + computeGeomCenter(geomCenter, mNbHullFaces, mFaces); + + // Loop through polygons + // We have N polygons => remove N entries for number of vertices + PxU32 tmp = temp.size() - nbPolygons; + mHullDataVertexData8 = PX_NEW(PxU8)[tmp]; + PxU8* dest = mHullDataVertexData8; + const PxU32* data = temp.begin(); + const PxU32* triData = temp2.begin(); + for(PxU32 i=0;i=3); // Else something very wrong happened... + mHullDataPolygons[i].mNbVerts = Ps::to8(nbVerts); + + PxU32 index = 0; + for(PxU32 j=0;j& T = reinterpret_cast&>(mFaces[triIndex]); + const PxPlane PL = PlaneEquation(T, mHullDataHullVertices); + if(k==0 && PL.n.dot(mHullDataPolygons[i].mPlane.n) < 0.0f) + { + flip = true; + } + } + if(flip) + { + negatePlane(mHullDataPolygons[i]); + inverseBuffer(mHullDataPolygons[i].mNbVerts, dest); + } + + for(PxU32 j=0;jmNbHullVertices;j++) + { + float d = - (mHullDataPolygons[i].mPlane.n).dot(mHullDataHullVertices[j]); + if(d0.0f) + { + inverseBuffer(mHullDataPolygons[i].mNbVerts, dest); + + negatePlane(mHullDataPolygons[i]); + PX_ASSERT(mHullDataPolygons[i].mPlane.distance(geomCenter)<=0.0f); + } + + // Next one + data += nbVerts; // Skip vertex indices + dest += mHullDataPolygons[i].mNbVerts; + } + + if(reducedHullDataHullVertices != mHullDataHullVertices) + { + PxMemCopy(mHullDataHullVertices,reducedHullDataHullVertices,sizeof(PxVec3)*numReducedHullDataVertices); + PX_FREE(reducedHullDataHullVertices); + + mHull->mNbHullVertices = numReducedHullDataVertices; + } + + //calculate the vertex map table + if(!calculateVertexMapTable(nbPolygons)) + return false; + +#ifdef USE_PRECOMPUTED_HULL_PROJECTION + // Loop through polygons + for(PxU32 j=0;jmNbHullVertices; + const PxVec3* verts = mHullDataHullVertices; + Gu::HullPolygonData& polygon = mHullDataPolygons[j]; + PxReal min = PX_MAX_F32; + PxU8 minIndex = 0xff; + for (PxU8 i = 0; i < nbVerts; i++) + { + float dp = (*verts++).dot(polygon.mPlane.n); + if(dp < min) + { + min = dp; + minIndex = i; + } + } + polygon.mMinIndex = minIndex; + } +#endif + + // Triangulate newly created polygons to recreate a clean vertex cloud. + return createTrianglesFromPolygons(); +} + +////////////////////////////////////////////////////////////////////////// +// create back triangles from polygons +bool ConvexPolygonsBuilder::createTrianglesFromPolygons() +{ + if (!mHull->mNbPolygons || !mHullDataPolygons) return false; + + PxU32 maxNbTriangles = 0; + for (PxU32 i = 0; i < mHull->mNbPolygons; i++) + { + if (mHullDataPolygons[i].mNbVerts < 3) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "ConvexHullBuilder::CreateTrianglesFromPolygons: convex hull has a polygon with less than 3 vertices!"); + return false; + } + maxNbTriangles += mHullDataPolygons[i].mNbVerts - 2; + } + + HullTriangleData* tmpFaces = PX_NEW(HullTriangleData)[maxNbTriangles]; + + HullTriangleData* currFace = tmpFaces; + PxU32 nbTriangles = 0; + const PxU8* vertexData = mHullDataVertexData8; + const PxVec3* hullVerts = mHullDataHullVertices; + for (PxU32 i = 0; i < mHull->mNbPolygons; i++) + { + const PxU8* data = vertexData + mHullDataPolygons[i].mVRef8; + PxU32 nbVerts = mHullDataPolygons[i].mNbVerts; + + // Triangulate the polygon such that all all generated triangles have one and the same vertex + // in common. + // + // Make sure to avoid creating zero area triangles. Imagine the following polygon: + // + // 4 3 + // *------------------* + // | | + // *---*----*----*----* + // 5 6 0 1 2 + // + // Choosing vertex 0 as the shared vertex, the following zero area triangles will be created: + // [0 1 2], [0 5 6] + // + // Check for these triangles and discard them + // Note: Such polygons should only occur if the user defines the convex hull, i.e., the triangles + // of the convex shape, himself. If the convex hull is built from the vertices only, the + // hull algorithm removes the useless vertices. + // + for (PxU32 j = 0; j < nbVerts - 2; j++) + { + currFace->mRef[0] = data[0]; + currFace->mRef[1] = data[(j + 1) % nbVerts]; + currFace->mRef[2] = data[(j + 2) % nbVerts]; + + const PxVec3& p0 = hullVerts[currFace->mRef[0]]; + const PxVec3& p1 = hullVerts[currFace->mRef[1]]; + const PxVec3& p2 = hullVerts[currFace->mRef[2]]; + + const float area = ((p1 - p0).cross(p2 - p0)).magnitudeSquared(); + + if (area != 0.0f) // Else discard the triangle + { + nbTriangles++; + currFace++; + } + } + } + + PX_DELETE_POD(mFaces); + HullTriangleData* faces; + PX_ASSERT(nbTriangles <= maxNbTriangles); + if (maxNbTriangles == nbTriangles) + { + // No zero area triangles, hence the face buffer has correct size and can be used directly. + faces = tmpFaces; + } + else + { + // Resize face buffer because some triangles were discarded. + faces = PX_NEW(HullTriangleData)[nbTriangles]; + if (!faces) + { + PX_DELETE_POD(tmpFaces); + return false; + } + PxMemCopy(faces, tmpFaces, sizeof(HullTriangleData)*nbTriangles); + PX_DELETE_POD(tmpFaces); + } + mFaces = faces; + mNbHullFaces = nbTriangles; + // TODO: at this point useless vertices should be removed from the hull. The current fix is to initialize + // support vertices to known valid vertices, but it's not really convincing. + + // Re-unify normals + PxVec3 geomCenter; + computeGeomCenter(geomCenter, mNbHullFaces, mFaces); + + for (PxU32 i = 0; i < mNbHullFaces; i++) + { + const PxPlane P(hullVerts[mFaces[i].mRef[0]], + hullVerts[mFaces[i].mRef[1]], + hullVerts[mFaces[i].mRef[2]]); + if (P.distance(geomCenter) > 0.0f) + { + Flip(mFaces[i]); + } + } + return true; +} + diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h new file mode 100644 index 000000000..ee2f67452 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_CONVEXPOLYGONSBUILDER_H +#define PX_CONVEXPOLYGONSBUILDER_H + +#include "ConvexHullBuilder.h" + +namespace physx +{ + ////////////////////////////////////////////////////////////////////////// + // extended convex hull builder for a case where we build polygons from input triangles + class ConvexPolygonsBuilder : public ConvexHullBuilder + { + public: + ConvexPolygonsBuilder(Gu::ConvexHullData* hull, const bool buildGRBData); + ~ConvexPolygonsBuilder(); + + bool computeHullPolygons(const PxU32& nbVerts,const PxVec3* verts, const PxU32& nbTriangles, const PxU32* triangles); + + PX_INLINE PxU32 getNbFaces()const { return mNbHullFaces; } + PX_INLINE const HullTriangleData* getFaces() const { return mFaces; } + + + private: + bool createPolygonData(); + bool createTrianglesFromPolygons(); + + private: + PxU32 mNbHullFaces; //!< Number of faces in the convex hull + HullTriangleData* mFaces; //!< Triangles. + + }; +} + +#endif // PX_CONVEXHULLBUILDER_H + diff --git a/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp new file mode 100644 index 000000000..9b280d9e4 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp @@ -0,0 +1,2581 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "QuickHullConvexHullLib.h" +#include "ConvexHullUtils.h" + +#include "PsAllocator.h" +#include "PsUserAllocated.h" +#include "PsSort.h" +#include "PsMathUtils.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "PsBitUtils.h" + +#include "foundation/PxMath.h" +#include "foundation/PxPlane.h" +#include "foundation/PxBounds3.h" +#include "foundation/PxMemory.h" + +using namespace physx; + +namespace local +{ + ////////////////////////////////////////////////////////////////////////// + static const float MIN_ADJACENT_ANGLE = 3.0f; // in degrees - result wont have two adjacent facets within this angle of each other. + static const float PLANE_THICKNES = 3.0f * PX_EPS_F32; // points within this distance are considered on a plane + static const float MAXDOT_MINANG = cosf(Ps::degToRad(MIN_ADJACENT_ANGLE)); // adjacent angle for dot product tests + + ////////////////////////////////////////////////////////////////////////// + + struct QuickHullFace; + class ConvexHull; + class HullPlanes; + + ////////////////////////////////////////////////////////////////////////// + template + class MemBlock + { + public: + MemBlock(PxU32 preallocateSize) + : mPreallocateSize(preallocateSize), mCurrentBlock(0), mCurrentIndex(0) + { + PX_ASSERT(preallocateSize); + T* block = reinterpret_cast(PX_ALLOC_TEMP(sizeof(T)*preallocateSize, "Quickhull MemBlock")); + mBlocks.pushBack(block); + } + + MemBlock() + : mPreallocateSize(0), mCurrentBlock(0), mCurrentIndex(0) + { + } + + void init(PxU32 preallocateSize) + { + PX_ASSERT(preallocateSize); + mPreallocateSize = preallocateSize; + T* block = reinterpret_cast(PX_ALLOC_TEMP(sizeof(T)*preallocateSize, "Quickhull MemBlock")); + if(useIndexing) + { + for (PxU32 i = 0; i < mPreallocateSize; i++) + { + // placement new to index data + PX_PLACEMENT_NEW(&block[i], T)(i); + } + } + mBlocks.pushBack(block); + } + + ~MemBlock() + { + for (PxU32 i = 0; i < mBlocks.size(); i++) + { + PX_FREE(mBlocks[i]); + } + mBlocks.clear(); + } + + void reset() + { + for (PxU32 i = 0; i < mBlocks.size(); i++) + { + PX_FREE(mBlocks[i]); + } + mBlocks.clear(); + + mCurrentBlock = 0; + mCurrentIndex = 0; + + init(mPreallocateSize); + } + + T* getItem(PxU32 index) + { + const PxU32 block = index/mPreallocateSize; + const PxU32 itemIndex = index % mPreallocateSize; + PX_ASSERT(block <= mCurrentBlock); + PX_ASSERT(itemIndex < mPreallocateSize); + return &(mBlocks[block])[itemIndex]; + } + + T* getFreeItem() + { + PX_ASSERT(mPreallocateSize); + // check if we have enough space in block, otherwise allocate new block + if(mCurrentIndex < mPreallocateSize) + { + return &(mBlocks[mCurrentBlock])[mCurrentIndex++]; + } + else + { + T* block = reinterpret_cast(PX_ALLOC_TEMP(sizeof(T)*mPreallocateSize, "Quickhull MemBlock")); + mCurrentBlock++; + if (useIndexing) + { + for (PxU32 i = 0; i < mPreallocateSize; i++) + { + // placement new to index data + PX_PLACEMENT_NEW(&block[i], T)(mCurrentBlock*mPreallocateSize + i); + } + } + mBlocks.pushBack(block); + mCurrentIndex = 0; + return &(mBlocks[mCurrentBlock])[mCurrentIndex++]; + } + } + + private: + PxU32 mPreallocateSize; + PxU32 mCurrentBlock; + PxU32 mCurrentIndex; + Ps::Array mBlocks; + }; + + ////////////////////////////////////////////////////////////////////////// + // representation of quick hull vertex + struct QuickHullVertex + { + PxVec3 point; // point vector + PxU32 index; // point index for compare + float dist; // distance from plane if necessary + + QuickHullVertex* next; // link to next vertex, linked list used for conflict list + + PX_FORCE_INLINE bool operator==(const QuickHullVertex& vertex) const + { + return index == vertex.index ? true : false; + } + + PX_FORCE_INLINE bool operator <(const QuickHullVertex& vertex) const + { + return dist < vertex.dist ? true : false; + } + }; + + ////////////////////////////////////////////////////////////////////////// + // representation of quick hull half edge + struct QuickHullHalfEdge + { + QuickHullHalfEdge() : prev(NULL), next(NULL), twin(NULL), face(NULL), edgeIndex(0xFFFFFFFF) + { + } + + QuickHullHalfEdge(PxU32 ) + : prev(NULL), next(NULL), twin(NULL), face(NULL), edgeIndex(0xFFFFFFFF) + { + } + + QuickHullVertex tail; // tail vertex, head vertex is the tail of the twin + + QuickHullHalfEdge* prev; // previous edge + QuickHullHalfEdge* next; // next edge + QuickHullHalfEdge* twin; // twin/opposite edge + + QuickHullFace* face; // face where the edge belong + + PxU32 edgeIndex; // edge index used for edge creation + + PX_FORCE_INLINE const QuickHullVertex& getTail() const + { + return tail; + } + + PX_FORCE_INLINE const QuickHullVertex& getHead() const + { + PX_ASSERT(twin); + return twin->tail; + } + + PX_FORCE_INLINE void setTwin(QuickHullHalfEdge* edge) + { + twin = edge; + edge->twin = this; + } + + PX_FORCE_INLINE QuickHullFace* getOppositeFace() const + { + return twin->face; + } + + float getOppositeFaceDistance() const; + }; + + ////////////////////////////////////////////////////////////////////////// + + typedef Ps::Array QuickHullVertexArray; + typedef Ps::Array QuickHullHalfEdgeArray; + typedef Ps::Array QuickHullFaceArray; + + ////////////////////////////////////////////////////////////////////////// + // representation of quick hull face + struct QuickHullFace + { + enum FaceState + { + eVISIBLE, + eDELETED, + eNON_CONVEX + }; + + QuickHullHalfEdge* edge; // starting edge + PxU16 numEdges; // num edges on the face + QuickHullVertex* conflictList; // conflict list, used to determine unclaimed vertices + + PxVec3 normal; // Newell plane normal + float area; // face area + PxVec3 centroid; // face centroid + + float planeOffset; // Newell plane offset + float expandOffset; // used for plane expansion if vertex limit reached + + FaceState state; // face validity state + + QuickHullFace* nextFace; // used to indicate next free face in faceList + PxU32 index; // face index for compare identification + PxU8 outIndex; // face index used for output descriptor + + public: + QuickHullFace() + : edge(NULL), numEdges(0), conflictList(NULL), area(0.0f), planeOffset(0.0f), expandOffset(-FLT_MAX), + state(eVISIBLE), nextFace(NULL), outIndex(0) + { + } + + QuickHullFace(PxU32 ind) + : edge(NULL), numEdges(0), conflictList(NULL), area(0.0f), planeOffset(0.0f), expandOffset(-FLT_MAX), + state(eVISIBLE), nextFace(NULL), index(ind), outIndex(0) + { + } + + ~QuickHullFace() + { + } + + // get edge on index + PX_FORCE_INLINE QuickHullHalfEdge* getEdge(PxU32 i) const + { + QuickHullHalfEdge* he = edge; + while (i > 0) + { + he = he->next; + i--; + } + return he; + } + + // distance from a plane to provided point + PX_FORCE_INLINE float distanceToPlane(const PxVec3 p) const + { + return normal.dot(p) - planeOffset; + } + + // compute face normal and centroid + PX_FORCE_INLINE void computeNormalAndCentroid() + { + PX_ASSERT(edge); + normal = PxVec3(PxZero); + numEdges = 1; + + QuickHullHalfEdge* testEdge = edge; + QuickHullHalfEdge* startEdge = NULL; + float maxDist = 0.0f; + for (PxU32 i = 0; i < 3; i++) + { + const float d = (testEdge->tail.point - testEdge->next->tail.point).magnitudeSquared(); + if (d > maxDist) + { + maxDist = d; + startEdge = testEdge; + } + testEdge = testEdge->next; + } + PX_ASSERT(startEdge); + + QuickHullHalfEdge* he = startEdge->next; + const PxVec3& p0 = startEdge->tail.point; + const PxVec3 d = he->tail.point - p0; + centroid = startEdge->tail.point; + + do + { + numEdges++; + centroid += he->tail.point; + + normal += d.cross(he->next->tail.point - p0); + + he = he->next; + } while (he != startEdge); + + area = normal.normalize(); + centroid *= (1.0f / float(numEdges)); + + planeOffset = normal.dot(centroid); + } + + // merge adjacent face + bool mergeAdjacentFace(QuickHullHalfEdge* halfEdge, QuickHullFaceArray& discardedFaces); + + // check face consistency + bool checkFaceConsistency(); + + private: + // connect halfedges + QuickHullFace* connectHalfEdges(QuickHullHalfEdge* hedgePrev, QuickHullHalfEdge* hedge); + + // check if the face does have only 3 vertices + PX_FORCE_INLINE bool isTriangle() const + { + return numEdges == 3 ? true : false; + } + + }; + + ////////////////////////////////////////////////////////////////////////// + struct QuickHullResult + { + enum Enum + { + eSUCCESS, // ok + eZERO_AREA_TEST_FAILED, // area test failed for simplex + eVERTEX_LIMIT_REACHED, // vertex limit reached need to expand hull + ePOLYGONS_LIMIT_REACHED, // polygons hard limit reached + eFAILURE // general failure + }; + }; + + ////////////////////////////////////////////////////////////////////////// + // Quickhull base class holding the hull during construction + class QuickHull : public Ps::UserAllocated + { + PX_NOCOPY(QuickHull) + public: + + QuickHull(const PxCookingParams& params, const PxConvexMeshDesc& desc); + + ~QuickHull(); + + // preallocate the edges, faces, vertices + void preallocate(PxU32 numVertices); + + // parse the input verts, store them into internal format + void parseInputVertices(const PxVec3* verts, PxU32 numVerts); + + // release the hull and data + void releaseHull(); + + // sets the precomputed min/max data + void setPrecomputedMinMax(const QuickHullVertex* minVertex,const QuickHullVertex* maxVertex, const float tolerance,const float planeTolerance); + + // main entry function to build the hull from provided points + QuickHullResult::Enum buildHull(); + + PxU32 maxNumVertsPerFace() const; + + protected: + // compute min max verts + void computeMinMaxVerts(); + + // find the initial simplex + bool findSimplex(); + + // add the initial simplex + void addSimplex(QuickHullVertex* simplex, bool flipTriangle); + + // finds next point to add + QuickHullVertex* nextPointToAdd(QuickHullFace*& eyeFace); + + // adds point to the hull + bool addPointToHull(const QuickHullVertex* vertex, QuickHullFace& face, bool& addFailed); + + // creates new face from given triangles + QuickHullFace* createTriangle(const QuickHullVertex& v0, const QuickHullVertex& v1, const QuickHullVertex& v2); + + // adds point to the face conflict list + void addPointToFace(QuickHullFace& face, QuickHullVertex* vertex, float dist); + + // removes eye point from the face conflict list + void removeEyePointFromFace(QuickHullFace& face, const QuickHullVertex* vertex); + + // calculate the horizon fro the eyePoint against a given face + void calculateHorizon(const PxVec3& eyePoint, QuickHullHalfEdge* edge, QuickHullFace& face, QuickHullHalfEdgeArray& horizon, QuickHullFaceArray& removedFaces); + + // adds new faces from given horizon and eyePoint + void addNewFacesFromHorizon(const QuickHullVertex* eyePoint, const QuickHullHalfEdgeArray& horizon, QuickHullFaceArray& newFaces); + + // merge adjacent face + bool doAdjacentMerge(QuickHullFace& face, bool mergeWrtLargeFace, bool& mergeFailed); + + // merge adjacent face doing normal test + bool doPostAdjacentMerge(QuickHullFace& face, const float minAngle); + + // delete face points + void deleteFacePoints(QuickHullFace& faceToDelete, QuickHullFace* absorbingFace); + + // resolve unclaimed points + void resolveUnclaimedPoints(const QuickHullFaceArray& newFaces); + + // merges polygons with similar normals + void postMergeHull(); + + // check if 2 faces can be merged + bool canMergeFaces(const QuickHullHalfEdge& he); + + // get next free face + PX_FORCE_INLINE QuickHullFace* getFreeHullFace() + { + return mFreeFaces.getFreeItem(); + } + + // get next free half edge + PX_FORCE_INLINE QuickHullHalfEdge* getFreeHullHalfEdge() + { + return mFreeHalfEdges.getFreeItem(); + } + + PX_FORCE_INLINE PxU32 getNbHullVerts() { return mOutputNumVertices; } + + protected: + friend class physx::QuickHullConvexHullLib; + + const PxCookingParams& mCookingParams; // cooking params + const PxConvexMeshDesc& mConvexDesc; // convex desc + + PxVec3 mInteriorPoint; // interior point for int/ext tests + + PxU32 mMaxVertices; // maximum number of vertices (can be different as we may add vertices during the cleanup + PxU32 mNumVertices; // actual number of input vertices + PxU32 mOutputNumVertices; // num vertices of the computed hull + PxU32 mTerminalVertex; // in case we failed to generate hull in a regular run we set the terminal vertex and rerun + + QuickHullVertex* mVerticesList; // vertices list preallocated + MemBlock mFreeHalfEdges; // free half edges + MemBlock mFreeFaces; // free faces + + QuickHullFaceArray mHullFaces; // actual hull faces, contains also invalid and not used faces + PxU32 mNumHullFaces; // actual number of hull faces + + bool mPrecomputedMinMax; // if we got the precomputed min/max values + QuickHullVertex mMinVertex[3]; // min vertex + QuickHullVertex mMaxVertex[3]; // max vertex + float mTolerance; // hull tolerance, used for plane thickness and merge strategy + float mPlaneTolerance; // used for post merge stage + + QuickHullVertexArray mUnclaimedPoints; // holds temp unclaimed points + + QuickHullHalfEdgeArray mHorizon; // array for horizon computation + QuickHullFaceArray mNewFaces; // new faces created during horizon computation + QuickHullFaceArray mRemovedFaces; // removd faces during horizon computation + QuickHullFaceArray mDiscardedFaces; // discarded faces during face merging + }; + + ////////////////////////////////////////////////////////////////////////// + // return the distance from opposite face + float QuickHullHalfEdge::getOppositeFaceDistance() const + { + PX_ASSERT(face); + PX_ASSERT(twin); + return face->distanceToPlane(twin->face->centroid); + } + + ////////////////////////////////////////////////////////////////////////// + // merge adjacent face from provided half edge. + // 1. set new half edges + // 2. connect the new half edges - check we did not produced redundant triangles, discard them + // 3. recompute the plane and check consistency + // Returns false if merge failed + bool QuickHullFace::mergeAdjacentFace(QuickHullHalfEdge* hedgeAdj, QuickHullFaceArray& discardedFaces) + { + QuickHullFace* oppFace = hedgeAdj->getOppositeFace(); + + discardedFaces.pushBack(oppFace); + oppFace->state = QuickHullFace::eDELETED; + + QuickHullHalfEdge* hedgeOpp = hedgeAdj->twin; + + QuickHullHalfEdge* hedgeAdjPrev = hedgeAdj->prev; + QuickHullHalfEdge* hedgeAdjNext = hedgeAdj->next; + QuickHullHalfEdge* hedgeOppPrev = hedgeOpp->prev; + QuickHullHalfEdge* hedgeOppNext = hedgeOpp->next; + + // check if we are lining up with the face in adjPrev dir + QuickHullHalfEdge* breakEdge = hedgeAdjPrev; + while (hedgeAdjPrev->getOppositeFace() == oppFace) + { + hedgeAdjPrev = hedgeAdjPrev->prev; + hedgeOppNext = hedgeOppNext->next; + + // Edge case merge face is degenerated and we need to abort merging + if (hedgeAdjPrev == breakEdge) + { + return false; + } + } + + // check if we are lining up with the face in adjNext dir + breakEdge = hedgeAdjNext; + while (hedgeAdjNext->getOppositeFace() == oppFace) + { + hedgeOppPrev = hedgeOppPrev->prev; + hedgeAdjNext = hedgeAdjNext->next; + + // Edge case merge face is degenerated and we need to abort merging + if (hedgeAdjNext == breakEdge) + { + return false; + } + } + + QuickHullHalfEdge* hedge; + + // set new face owner for the line up edges + for (hedge = hedgeOppNext; hedge != hedgeOppPrev->next; hedge = hedge->next) + { + hedge->face = this; + } + + // if we are about to delete the shared edge, check if its not the starting edge of the face + if (hedgeAdj == edge) + { + edge = hedgeAdjNext; + } + + // handle the half edges at the head + QuickHullFace* discardedFace; + discardedFace = connectHalfEdges(hedgeOppPrev, hedgeAdjNext); + if (discardedFace != NULL) + { + discardedFaces.pushBack(discardedFace); + } + + // handle the half edges at the tail + discardedFace = connectHalfEdges(hedgeAdjPrev, hedgeOppNext); + if (discardedFace != NULL) + { + discardedFaces.pushBack(discardedFace); + } + + computeNormalAndCentroid(); + PX_ASSERT(checkFaceConsistency()); + + return true; + } + + ////////////////////////////////////////////////////////////////////////// + // connect half edges of 2 adjacent faces + // if we find redundancy - edges are in a line, we drop the addional face if it is just a skinny triangle + QuickHullFace* QuickHullFace::connectHalfEdges(QuickHullHalfEdge* hedgePrev, QuickHullHalfEdge* hedge) + { + QuickHullFace* discardedFace = NULL; + + // redundant edge - can be in a line + if (hedgePrev->getOppositeFace() == hedge->getOppositeFace()) + { + // then there is a redundant edge that we can get rid off + QuickHullFace* oppFace = hedge->getOppositeFace(); + QuickHullHalfEdge* hedgeOpp; + + if (hedgePrev == edge) + { + edge = hedge; + } + + // check if its not a skinny face with just 3 vertices - 3 edges + if (oppFace->isTriangle()) + { + // then we can get rid of the opposite face altogether + hedgeOpp = hedge->twin->prev->twin; + + oppFace->state = QuickHullFace::eDELETED; + discardedFace = oppFace; + } + else + { + // if not triangle, merge the 2 opposite halfedges into one + hedgeOpp = hedge->twin->next; + + if (oppFace->edge == hedgeOpp->prev) + { + oppFace->edge = hedgeOpp; + } + hedgeOpp->prev = hedgeOpp->prev->prev; + hedgeOpp->prev->next = hedgeOpp; + } + + hedge->prev = hedgePrev->prev; + hedge->prev->next = hedge; + + hedge->twin = hedgeOpp; + hedgeOpp->twin = hedge; + + // oppFace was modified, so need to recompute + oppFace->computeNormalAndCentroid(); + } + else + { + // just merge the halfedges + hedgePrev->next = hedge; + hedge->prev = hedgePrev; + } + return discardedFace; + } + + ////////////////////////////////////////////////////////////////////////// + // check face consistency + bool QuickHullFace::checkFaceConsistency() + { + // do a sanity check on the face + QuickHullHalfEdge* hedge = edge; + PxU32 numv = 0; + + // check degenerate face + do + { + numv++; + hedge = hedge->next; + } while (hedge != edge); + + // degenerate face found + PX_ASSERT(numv > 2); + + numv = 0; + hedge = edge; + do + { + QuickHullHalfEdge* hedgeOpp = hedge->twin; + + // check if we have twin set + PX_ASSERT(hedgeOpp != NULL); + + // twin for the twin must be the original edge + PX_ASSERT(hedgeOpp->twin == hedge); + + QuickHullFace* oppFace = hedgeOpp->face; + + PX_UNUSED(oppFace); + + // opposite edge face must be set and valid + PX_ASSERT(oppFace != NULL); + PX_ASSERT(oppFace->state != QuickHullFace::eDELETED); + + // edges face must be this one + PX_ASSERT(hedge->face == this); + + hedge = hedge->next; + } while (hedge != edge); + + return true; + } + + ////////////////////////////////////////////////////////////////////////// + + QuickHull::QuickHull(const PxCookingParams& params, const PxConvexMeshDesc& desc) + : mCookingParams(params), mConvexDesc(desc), mOutputNumVertices(0), mTerminalVertex(0xFFFFFFFF), mVerticesList(NULL), mNumHullFaces(0), mPrecomputedMinMax(false), + mTolerance(-1.0f), mPlaneTolerance(-1.0f) + { + } + + ////////////////////////////////////////////////////////////////////////// + + QuickHull::~QuickHull() + { + } + + ////////////////////////////////////////////////////////////////////////// + // sets the precomputed min/max values + void QuickHull::setPrecomputedMinMax(const QuickHullVertex* minVertex,const QuickHullVertex* maxVertex, const float tolerance,const float planeTolerance) + { + for (PxU32 i = 0; i < 3; i++) + { + mMinVertex[i] = minVertex[i]; + mMaxVertex[i] = maxVertex[i]; + } + + mTolerance = tolerance; + mPlaneTolerance = planeTolerance; + + mPrecomputedMinMax = true; + } + + ////////////////////////////////////////////////////////////////////////// + // preallocate internal buffers + void QuickHull::preallocate(PxU32 numVertices) + { + PX_ASSERT(numVertices > 0); + + // max num vertices = numVertices + mMaxVertices = PxMax(PxU32(8), numVertices); // 8 is min, since we can expand to AABB during the clean vertices phase + mVerticesList = reinterpret_cast (PX_ALLOC_TEMP(sizeof(QuickHullVertex)*mMaxVertices, "QuickHullVertex")); + + // estimate the max half edges + PxU32 maxHalfEdges = (3 * mMaxVertices - 6) * 3; + mFreeHalfEdges.init(maxHalfEdges); + + // estimate the max faces + PxU32 maxFaces = (2 * mMaxVertices - 4); + mFreeFaces.init(maxFaces*2); + + mHullFaces.reserve(maxFaces); + mUnclaimedPoints.reserve(numVertices); + + mNewFaces.reserve(32); + mRemovedFaces.reserve(32); + mDiscardedFaces.reserve(32); + mHorizon.reserve(PxMin(numVertices,PxU32(128))); + } + + ////////////////////////////////////////////////////////////////////////// + // release internal buffers + void QuickHull::releaseHull() + { + if (mVerticesList) + { + PX_FREE_AND_RESET(mVerticesList); + } + mHullFaces.clear(); + } + + ////////////////////////////////////////////////////////////////////////// + // returns the maximum number of vertices on a face + PxU32 QuickHull::maxNumVertsPerFace() const + { + PxU32 numFaces = mHullFaces.size(); + PxU32 maxVerts = 0; + for (PxU32 i = 0; i < numFaces; i++) + { + const local::QuickHullFace& face = *mHullFaces[i]; + if (face.state == local::QuickHullFace::eVISIBLE) + { + if (face.numEdges > maxVerts) + maxVerts = face.numEdges; + } + } + return maxVerts; + } + + ////////////////////////////////////////////////////////////////////////// + // parse the input vertices and store them in the hull + void QuickHull::parseInputVertices(const PxVec3* verts, PxU32 numVerts) + { + PX_ASSERT(verts); + PX_ASSERT(numVerts <= mMaxVertices); + + mNumVertices = numVerts; + for (PxU32 i = 0; i < numVerts; i++) + { + mVerticesList[i].point = verts[i]; + mVerticesList[i].index = i; + } + } + + ////////////////////////////////////////////////////////////////////////// + // compute min max verts + void QuickHull::computeMinMaxVerts() + { + for (PxU32 i = 0; i < 3; i++) + { + mMinVertex[i] = mVerticesList[0]; + mMaxVertex[i] = mVerticesList[0]; + } + + PxVec3 max = mVerticesList[0].point; + PxVec3 min = mVerticesList[0].point; + + // get the max min vertices along the x,y,z + for (PxU32 i = 1; i < mNumVertices; i++) + { + const QuickHullVertex& testVertex = mVerticesList[i]; + const PxVec3& testPoint = testVertex.point; + if (testPoint.x > max.x) + { + max.x = testPoint.x; + mMaxVertex[0] = testVertex; + } + else if (testPoint.x < min.x) + { + min.x = testPoint.x; + mMinVertex[0] = testVertex; + } + + if (testPoint.y > max.y) + { + max.y = testPoint.y; + mMaxVertex[1] = testVertex; + } + else if (testPoint.y < min.y) + { + min.y = testPoint.y; + mMinVertex[1] = testVertex; + } + + if (testPoint.z > max.z) + { + max.z = testPoint.z; + mMaxVertex[2] = testVertex; + } + else if (testPoint.z < min.z) + { + min.z = testPoint.z; + mMinVertex[2] = testVertex; + } + } + + const float sizeTol = (max.x-min.x + max.y - min.y + max.z - min.z)*0.5f; + mTolerance = PxMax(local::PLANE_THICKNES * sizeTol, local::PLANE_THICKNES); + mPlaneTolerance = PxMax(mCookingParams.planeTolerance * sizeTol, mCookingParams.planeTolerance); + } + + ////////////////////////////////////////////////////////////////////////// + // find the initial simplex + // 1. search in max axis from compute min,max + // 2. 3rd point is the furthest vertex from the initial line + // 3. 4th vertex is along the line, 3rd vertex normal + bool QuickHull::findSimplex() + { + float max = 0; + PxU32 imax = 0; + + for (PxU32 i = 0; i < 3; i++) + { + float diff = mMaxVertex[i].point[i] - mMinVertex[i].point[i]; + if (diff > max) + { + max = diff; + imax = i; + } + } + + if (max <= mTolerance) + { + // should not happen as we clear the vertices before and expand them if they are really close to each other + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "QuickHullConvexHullLib::findSimplex: Simplex input points appers to be almost at the same place"); + return false; + } + + QuickHullVertex simplex[4]; + + // set first two vertices to be those with the greatest + // one dimensional separation + simplex[0] = mMaxVertex[imax]; + simplex[1] = mMinVertex[imax]; + + // set third vertex to be the vertex farthest from + // the line between simplex[0] and simplex[1] + PxVec3 normal; + float maxDist = 0; + PxVec3 u01 = (simplex[1].point - simplex[0].point); + u01.normalize(); + + for (PxU32 i = 0; i < mNumVertices; i++) + { + const QuickHullVertex& testVert = mVerticesList[i]; + const PxVec3& testPoint = testVert.point; + const PxVec3 diff = testPoint - simplex[0].point; + const PxVec3 xprod = u01.cross(diff); + const float lenSqr = xprod.magnitudeSquared(); + if (lenSqr > maxDist && testVert.index != simplex[0].index && testVert.index != simplex[1].index) + { + maxDist = lenSqr; + simplex[2] = testVert; + normal = xprod; + } + } + + if (PxSqrt(maxDist) <= mTolerance) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "QuickHullConvexHullLib::findSimplex: Simplex input points appers to be colinear."); + return false; + } + normal.normalize(); + + // set the forth vertex in the normal direction + const float d0 = simplex[2].point.dot(normal); + maxDist = 0.0f; + for (PxU32 i = 0; i < mNumVertices; i++) + { + const QuickHullVertex& testVert = mVerticesList[i]; + const PxVec3& testPoint = testVert.point; + const float dist = PxAbs(testPoint.dot(normal) - d0); + if (dist > maxDist && testVert.index != simplex[0].index && + testVert.index != simplex[1].index && testVert.index != simplex[2].index) + { + maxDist = dist; + simplex[3] = testVert; + } + } + + if (PxAbs(maxDist) <= mTolerance) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "QuickHullConvexHullLib::findSimplex: Simplex input points appers to be coplanar."); + return false; + } + + // now create faces from those triangles + addSimplex(&simplex[0], simplex[3].point.dot(normal) - d0 < 0); + + return true; + } + + ////////////////////////////////////////////////////////////////////////// + // create triangle from given vertices, produce new face and connect the half edges + QuickHullFace* QuickHull::createTriangle(const QuickHullVertex& v0, const QuickHullVertex& v1, const QuickHullVertex& v2) + { + QuickHullFace* face = getFreeHullFace(); + + QuickHullHalfEdge* he0 = getFreeHullHalfEdge(); + he0->face = face; + he0->tail = v0; + + QuickHullHalfEdge* he1 = getFreeHullHalfEdge(); + he1->face = face; + he1->tail = v1; + + QuickHullHalfEdge* he2 = getFreeHullHalfEdge(); + he2->face = face; + he2->tail = v2; + + he0->prev = he2; + he0->next = he1; + he1->prev = he0; + he1->next = he2; + he2->prev = he1; + he2->next = he0; + + face->edge = he0; + face->nextFace = NULL; + + // compute the normal and offset + face->computeNormalAndCentroid(); + return face; + } + + + ////////////////////////////////////////////////////////////////////////// + // add initial simplex to the quickhull + // construct triangles from the simplex points and connect them with half edges + void QuickHull::addSimplex(QuickHullVertex* simplex, bool flipTriangle) + { + PX_ASSERT(simplex); + + // get interior point + PxVec3 vectorSum = simplex[0].point; + for (PxU32 i = 1; i < 4; i++) + { + vectorSum += simplex[i].point; + } + mInteriorPoint = vectorSum / 4.0f; + + QuickHullFace* tris[4]; + // create the triangles from the initial simplex + if (flipTriangle) + { + tris[0] = createTriangle(simplex[0], simplex[1], simplex[2]); + tris[1] = createTriangle(simplex[3], simplex[1], simplex[0]); + tris[2] = createTriangle(simplex[3], simplex[2], simplex[1]); + tris[3] = createTriangle(simplex[3], simplex[0], simplex[2]); + + for (PxU32 i = 0; i < 3; i++) + { + PxU32 k = (i + 1) % 3; + tris[i + 1]->getEdge(1)->setTwin(tris[k + 1]->getEdge(0)); + tris[i + 1]->getEdge(2)->setTwin(tris[0]->getEdge(k)); + } + } + else + { + tris[0] = createTriangle(simplex[0], simplex[2], simplex[1]); + tris[1] = createTriangle(simplex[3], simplex[0], simplex[1]); + tris[2] = createTriangle(simplex[3], simplex[1], simplex[2]); + tris[3] = createTriangle(simplex[3], simplex[2], simplex[0]); + + for (PxU32 i = 0; i < 3; i++) + { + PxU32 k = (i + 1) % 3; + tris[i + 1]->getEdge(0)->setTwin(tris[k + 1]->getEdge(1)); + tris[i + 1]->getEdge(2)->setTwin(tris[0]->getEdge((3 - i) % 3)); + } + } + + // push back the first 4 faces created from the simplex + for (PxU32 i = 0; i < 4; i++) + { + mHullFaces.pushBack(tris[i]); + } + mNumHullFaces = 4; + + // go through points and add point to faces if they are on the plane + for (PxU32 i = 0; i < mNumVertices; i++) + { + const QuickHullVertex& v = mVerticesList[i]; + + if (v == simplex[0] || v == simplex[1] || v == simplex[2] || v == simplex[3]) + { + continue; + } + + float maxDist = mTolerance; + QuickHullFace* maxFace = NULL; + for (PxU32 k = 0; k < 4; k++) + { + const float dist = tris[k]->distanceToPlane(v.point); + if (dist > maxDist) + { + maxFace = tris[k]; + maxDist = dist; + } + } + + if (maxFace != NULL) + { + addPointToFace(*maxFace, &mVerticesList[i], maxDist); + } + } + } + + ////////////////////////////////////////////////////////////////////////// + // adds a point to the conflict list + // the trick here is to store the most furthest point as the last, thats the only one we care about + // the rest is not important, we just need to store them and claim to new faces later, if the + // faces most furthest point is the current global maximum + void QuickHull::addPointToFace(QuickHullFace& face, QuickHullVertex* vertex, float dist) + { + // if we dont have a conflict list, store the vertex as the first one in the conflict list + vertex->dist = dist; + if(!face.conflictList) + { + face.conflictList = vertex; + vertex->dist = dist; + vertex->next = NULL; + return; + } + + PX_ASSERT(face.conflictList); + + // this is not the furthest vertex, store it as next in the linked list + if (face.conflictList->dist > dist) + { + vertex->next = face.conflictList->next; + face.conflictList->next = vertex; + } + else + { + // this is the furthest vertex, store it as first in the linked list + vertex->next = face.conflictList; + face.conflictList = vertex; + } + } + + ////////////////////////////////////////////////////////////////////////// + // removes eye point from a conflict list + // we know that the vertex must the last, as we store it at the back, so just popback() + void QuickHull::removeEyePointFromFace(QuickHullFace& face, const QuickHullVertex* vertex) + { + PX_UNUSED(vertex); + // the picked vertex should always be the first in the linked list + PX_ASSERT(face.conflictList == vertex); + + face.conflictList = face.conflictList->next; + } + + ////////////////////////////////////////////////////////////////////////// + // merge polygons with similar normals + void QuickHull::postMergeHull() + { + // merge faces with similar normals + for (PxU32 i = 0; i < mHullFaces.size(); i++) + { + QuickHullFace& face = *mHullFaces[i]; + + if (face.state == QuickHullFace::eVISIBLE) + { + PX_ASSERT(face.checkFaceConsistency()); + while (doPostAdjacentMerge(face, local::MAXDOT_MINANG)); + } + } + } + + ////////////////////////////////////////////////////////////////////////// + // builds the hull + // 1. find the initial simplex + // 2. check if simplex has a valid area + // 3. add vertices to the hull. We add vertex most furthest from the hull + // 4. terminate if hull limit reached or we have added all vertices + QuickHullResult::Enum QuickHull::buildHull() + { + QuickHullVertex* eyeVtx = NULL; + QuickHullFace* eyeFace; + + // compute the vertex min max along x,y,z + if(!mPrecomputedMinMax) + computeMinMaxVerts(); + + // find the initial simplex of the hull + if (!findSimplex()) + { + return QuickHullResult::eFAILURE; + } + + // simplex area test + const bool useAreaTest = mConvexDesc.flags & PxConvexFlag::eCHECK_ZERO_AREA_TRIANGLES ? true : false; + const float areaEpsilon = mCookingParams.areaTestEpsilon * 2.0f; + if (useAreaTest) + { + for (PxU32 i = 0; i < mHullFaces.size(); i++) + { + if (mHullFaces[i]->area < areaEpsilon) + { + return QuickHullResult::eZERO_AREA_TEST_FAILED; + } + } + } + + // add points to the hull + PxU32 numVerts = 4; // initial vertex count - simplex vertices + while ((eyeVtx = nextPointToAdd(eyeFace)) != NULL && eyeVtx->index != mTerminalVertex) + { + // if plane shifting vertex limit, we need the reduced hull + if((mConvexDesc.flags & PxConvexFlag::ePLANE_SHIFTING) && (numVerts >= mConvexDesc.vertexLimit)) + break; + + bool addFailed = false; + PX_ASSERT(eyeFace); + if (!addPointToHull(eyeVtx, *eyeFace, addFailed)) + { + mOutputNumVertices = numVerts; + // we hit the polygons hard limit + return QuickHullResult::ePOLYGONS_LIMIT_REACHED; + } + // We failed to add the vertex, store the vertex as terminal vertex and re run the hull generator + if(addFailed) + { + // set the terminal vertex + mTerminalVertex = eyeVtx->index; + + // reset the edges/faces memory + mFreeHalfEdges.reset(); + mFreeFaces.reset(); + + // reset the hull state + mHullFaces.clear(); + mNumHullFaces = 0; + mUnclaimedPoints.clear(); + mHorizon.clear(); + mNewFaces.clear(); + mRemovedFaces.clear(); + mDiscardedFaces.clear(); + + // rerun the hull generator + return buildHull(); + } + numVerts++; + } + mOutputNumVertices = numVerts; + + // vertex limit has been reached. We did not stopped the iteration, since we + // will use the produced hull to compute OBB from it and use the planes + // to slice the initial OBB + if (numVerts > mConvexDesc.vertexLimit) + { + return QuickHullResult::eVERTEX_LIMIT_REACHED; + } + + return QuickHullResult::eSUCCESS; + } + + ////////////////////////////////////////////////////////////////////////// + // finds the best point to add to the hull + // go through the faces conflict list and pick the global maximum + QuickHullVertex* QuickHull::nextPointToAdd(QuickHullFace*& eyeFace) + { + QuickHullVertex* eyeVtx = NULL; + QuickHullFace* eyeF = NULL; + float maxDist = mPlaneTolerance; + for (PxU32 i = 0; i < mHullFaces.size(); i++) + { + if (mHullFaces[i]->state == QuickHullFace::eVISIBLE && mHullFaces[i]->conflictList) + { + const float dist = mHullFaces[i]->conflictList->dist; + if (maxDist < dist) + { + maxDist = dist; + eyeVtx = mHullFaces[i]->conflictList; + eyeF = mHullFaces[i]; + } + } + } + + eyeFace = eyeF; + return eyeVtx; + } + + ////////////////////////////////////////////////////////////////////////// + // adds vertex to the hull + // sets addFailed to true if we failed to add a point because the merging failed + // this can happen as the face plane equation changes and some faces might become concave + // returns false if the new faces count would hit the hull face hard limit (255) + bool QuickHull::addPointToHull(const QuickHullVertex* eyeVtx, QuickHullFace& eyeFace, bool& addFailed) + { + addFailed = false; + + // removes the eyePoint from the conflict list + removeEyePointFromFace(eyeFace, eyeVtx); + + // calculates the horizon from the eyePoint + calculateHorizon(eyeVtx->point, NULL, eyeFace, mHorizon, mRemovedFaces); + + // check if we dont hit the polygons hard limit + if (mNumHullFaces + mHorizon.size() > 255) + { + // make the faces visible again and quit + for (PxU32 i = 0; i < mRemovedFaces.size(); i++) + { + mRemovedFaces[i]->state = QuickHullFace::eVISIBLE; + } + mNumHullFaces += mRemovedFaces.size(); + return false; + } + + // adds new faces from given horizon and eyePoint + addNewFacesFromHorizon(eyeVtx, mHorizon, mNewFaces); + + bool mergeFailed = false; + // first merge pass ... merge faces which are non-convex + // as determined by the larger face + for (PxU32 i = 0; i < mNewFaces.size(); i++) + { + QuickHullFace& face = *mNewFaces[i]; + + if (face.state == QuickHullFace::eVISIBLE) + { + PX_ASSERT(face.checkFaceConsistency()); + while (doAdjacentMerge(face, true, mergeFailed)); + } + } + if (mergeFailed) + { + addFailed = true; + return true; + } + + // second merge pass ... merge faces which are non-convex + // wrt either face + for (PxU32 i = 0; i < mNewFaces.size(); i++) + { + QuickHullFace& face = *mNewFaces[i]; + if (face.state == QuickHullFace::eNON_CONVEX) + { + face.state = QuickHullFace::eVISIBLE; + while (doAdjacentMerge(face, false, mergeFailed)); + } + } + if (mergeFailed) + { + addFailed = true; + return true; + } + + resolveUnclaimedPoints(mNewFaces); + + mHorizon.clear(); + mNewFaces.clear(); + mRemovedFaces.clear(); + + return true; + } + + ////////////////////////////////////////////////////////////////////////// + // merge adjacent faces + // We merge 2 adjacent faces if they lie on the same thick plane defined by the mTolerance + // we do this in 2 steps to ensure we dont leave non-convex faces + bool QuickHull::doAdjacentMerge(QuickHullFace& face, bool mergeWrtLargeFace, bool& mergeFailed) + { + QuickHullHalfEdge* hedge = face.edge; + mergeFailed = false; + + bool convex = true; + do + { + const QuickHullFace& oppFace = *hedge->getOppositeFace(); + bool merge = false; + + if (mergeWrtLargeFace) + { + // merge faces if they are parallel or non-convex + // wrt to the larger face; otherwise, just mark + // the face non-convex for the second pass. + if (face.area > oppFace.area) + { + if (hedge->getOppositeFaceDistance() > -mTolerance) + { + merge = true; + } + else if (hedge->twin->getOppositeFaceDistance() > -mTolerance) + { + convex = false; + } + } + else + { + if (hedge->twin->getOppositeFaceDistance() > -mTolerance) + { + merge = true; + } + else if (hedge->getOppositeFaceDistance() > -mTolerance) + { + convex = false; + } + } + } + else + { + // then merge faces if they are definitively non-convex + if (hedge->getOppositeFaceDistance() > -mTolerance || + hedge->twin->getOppositeFaceDistance() > -mTolerance) + { + merge = true; + } + } + + if (merge) + { + mDiscardedFaces.clear(); + if (!face.mergeAdjacentFace(hedge, mDiscardedFaces)) + { + mergeFailed = true; + return false; + } + mNumHullFaces -= mDiscardedFaces.size(); + for (PxU32 i = 0; i < mDiscardedFaces.size(); i++) + { + deleteFacePoints(*mDiscardedFaces[i], &face); + } + PX_ASSERT(face.checkFaceConsistency()); + return true; + } + hedge = hedge->next; + } while (hedge != face.edge); + + if (!convex) + { + face.state = QuickHullFace::eNON_CONVEX; + } + return false; + } + + ////////////////////////////////////////////////////////////////////////// + // merge adjacent faces doing normal test + // we try to merge more aggressively 2 faces with the same normal. + bool QuickHull::doPostAdjacentMerge(QuickHullFace& face, const float maxdot_minang) + { + QuickHullHalfEdge* hedge = face.edge; + + do + { + const QuickHullFace& oppFace = *hedge->getOppositeFace(); + bool merge = false; + const PxVec3& ni = face.normal; + const PxVec3& nj = oppFace.normal; + const float dotP = ni.dot(nj); + + if (dotP > maxdot_minang) + { + if (face.area >= oppFace.area) + { + // check if we can merge the 2 faces + merge = canMergeFaces(*hedge); + } + } + + if (merge) + { + QuickHullFaceArray discardedFaces; + face.mergeAdjacentFace(hedge, discardedFaces); + mNumHullFaces -= discardedFaces.size(); + for (PxU32 i = 0; i < discardedFaces.size(); i++) + { + deleteFacePoints(*discardedFaces[i], &face); + } + PX_ASSERT(face.checkFaceConsistency()); + return true; + } + hedge = hedge->next; + } while (hedge != face.edge); + + return false; + } + + ////////////////////////////////////////////////////////////////////////// + // checks if 2 adjacent faces can be merged + // 1. creates a face with merged vertices + // 2. computes new normal and centroid + // 3. checks that all verts are not too far away from the plane + // 4. checks that the new polygon is still convex + // 5. checks if we are about to merge only 2 neighbor faces, we dont + // want to merge additional faces, that might corrupt the convexity + bool QuickHull::canMergeFaces(const QuickHullHalfEdge& he) + { + const QuickHullFace& face1 = *he.face; + const QuickHullFace& face2 = *he.twin->face; + + // construct the merged face + PX_ALLOCA(edges, QuickHullHalfEdge, (face1.numEdges + face2.numEdges)); + PxMemSet(edges, 0, (face1.numEdges + face2.numEdges)*sizeof(QuickHullHalfEdge)); + QuickHullFace mergedFace; + mergedFace.edge = &edges[0]; + + // copy the first face edges + PxU32 currentEdge = 0; + const QuickHullHalfEdge* heTwin = NULL; + const QuickHullHalfEdge* heCopy = NULL; + const QuickHullHalfEdge* startEdge = (face1.edge != &he) ? face1.edge : face1.edge->next; + const QuickHullHalfEdge* copyHe = startEdge; + do + { + edges[currentEdge].face = &mergedFace; + edges[currentEdge].tail = copyHe->tail; + if(copyHe == &he) + { + heTwin = copyHe->twin; + heCopy = &edges[currentEdge]; + } + const PxU32 nextIndex = (copyHe->next == startEdge) ? 0 : currentEdge + 1; + const PxU32 prevIndex = (currentEdge == 0) ? face1.numEdges - 1 : currentEdge - 1; + edges[currentEdge].next = &edges.mPointer[nextIndex]; + edges[currentEdge].prev = &edges.mPointer[prevIndex]; + + currentEdge++; + copyHe = copyHe->next; + } while (copyHe != startEdge); + + // copy the second face edges + copyHe = face2.edge; + do + { + edges[currentEdge].face = &mergedFace; + edges[currentEdge].tail = copyHe->tail; + if(heTwin == copyHe) + heTwin = &edges[currentEdge]; + const PxU32 nextIndex = (copyHe->next == face2.edge) ? face1.numEdges : currentEdge + 1; + const PxU32 prevIndex = (currentEdge == face1.numEdges) ? face1.numEdges + face2.numEdges - 1 : currentEdge - 1; + edges[currentEdge].next = &edges.mPointer[nextIndex]; + edges[currentEdge].prev = &edges.mPointer[prevIndex]; + + currentEdge++; + copyHe = copyHe->next; + } while (copyHe != face2.edge); + + PX_ASSERT(heTwin); + + QuickHullHalfEdge* hedgeAdjPrev = heCopy->prev; + QuickHullHalfEdge* hedgeAdjNext = heCopy->next; + QuickHullHalfEdge* hedgeOppPrev = heTwin->prev; + QuickHullHalfEdge* hedgeOppNext = heTwin->next; + + hedgeOppPrev->next = hedgeAdjNext; + hedgeAdjNext->prev = hedgeOppPrev; + + hedgeAdjPrev->next = hedgeOppNext; + hedgeOppNext->prev = hedgeAdjPrev; + + // compute normal and centroid + mergedFace.computeNormalAndCentroid(); + + // test the vertex distance + const float maxDist = mPlaneTolerance; + for(PxU32 iVerts=0; iVerts< mNumVertices; iVerts++) + { + const QuickHullVertex& vertex = mVerticesList[iVerts]; + const float dist = mergedFace.distanceToPlane(vertex.point); + if (dist > maxDist) + { + return false; + } + } + + // check the convexity + QuickHullHalfEdge* qhe = mergedFace.edge; + do + { + const QuickHullVertex& vertex = qhe->tail; + const QuickHullVertex& nextVertex = qhe->next->tail; + + PxVec3 edgeVector = nextVertex.point - vertex.point; + edgeVector.normalize(); + const PxVec3 outVector = -mergedFace.normal.cross(edgeVector); + + QuickHullHalfEdge* testHe = qhe->next; + do + { + const QuickHullVertex& testVertex = testHe->tail; + const float dist = (testVertex.point - vertex.point).dot(outVector); + + if (dist > mTolerance) + return false; + + testHe = testHe->next; + } while (testHe != qhe->next); + + qhe = qhe->next; + } while (qhe != mergedFace.edge); + + + const QuickHullFace* oppFace = he.getOppositeFace(); + + QuickHullHalfEdge* hedgeOpp = he.twin; + + hedgeAdjPrev = he.prev; + hedgeAdjNext = he.next; + hedgeOppPrev = hedgeOpp->prev; + hedgeOppNext = hedgeOpp->next; + + // check if we are lining up with the face in adjPrev dir + while (hedgeAdjPrev->getOppositeFace() == oppFace) + { + hedgeAdjPrev = hedgeAdjPrev->prev; + hedgeOppNext = hedgeOppNext->next; + } + + // check if we are lining up with the face in adjNext dir + while (hedgeAdjNext->getOppositeFace() == oppFace) + { + hedgeOppPrev = hedgeOppPrev->prev; + hedgeAdjNext = hedgeAdjNext->next; + } + + // no redundant merges, just clean merge of 2 neighbour faces + if (hedgeOppPrev->getOppositeFace() == hedgeAdjNext->getOppositeFace()) + { + return false; + } + + if (hedgeAdjPrev->getOppositeFace() == hedgeOppNext->getOppositeFace()) + { + return false; + } + + return true; + } + + ////////////////////////////////////////////////////////////////////////// + // delete face points and store them as unclaimed, so we can add them back to new faces later + void QuickHull::deleteFacePoints(QuickHullFace& face, QuickHullFace* absorbingFace) + { + // no conflict list for this face + if(!face.conflictList) + return; + + QuickHullVertex* unclaimedVertex = face.conflictList; + QuickHullVertex* vertexToClaim = NULL; + while (unclaimedVertex) + { + vertexToClaim = unclaimedVertex; + unclaimedVertex = unclaimedVertex->next; + vertexToClaim->next = NULL; + if (!absorbingFace) + { + mUnclaimedPoints.pushBack(vertexToClaim); + } + else + { + const float dist = absorbingFace->distanceToPlane(vertexToClaim->point); + if (dist > mTolerance) + { + addPointToFace(*absorbingFace, vertexToClaim, dist); + } + else + { + mUnclaimedPoints.pushBack(vertexToClaim); + } + } + } + + face.conflictList = NULL; + } + + ////////////////////////////////////////////////////////////////////////// + // calculate the horizon from the eyePoint against a given face + void QuickHull::calculateHorizon(const PxVec3& eyePoint, QuickHullHalfEdge* edge0, QuickHullFace& face, QuickHullHalfEdgeArray& horizon, QuickHullFaceArray& removedFaces) + { + deleteFacePoints(face, NULL); + face.state = QuickHullFace::eDELETED; + removedFaces.pushBack(&face); + mNumHullFaces--; + QuickHullHalfEdge* edge; + if (edge0 == NULL) + { + edge0 = face.getEdge(0); + edge = edge0; + } + else + { + edge = edge0->next; + } + + do + { + QuickHullFace* oppFace = edge->getOppositeFace(); + if (oppFace->state == QuickHullFace::eVISIBLE) + { + const float dist = oppFace->distanceToPlane(eyePoint); + if (dist > mTolerance) + { + calculateHorizon(eyePoint, edge->twin, *oppFace, horizon, removedFaces); + } + else + { + horizon.pushBack(edge); + } + } + edge = edge->next; + } while (edge != edge0); + } + + ////////////////////////////////////////////////////////////////////////// + // adds new faces from given horizon and eyePoint + void QuickHull::addNewFacesFromHorizon(const QuickHullVertex* eyePoint, const QuickHullHalfEdgeArray& horizon, QuickHullFaceArray& newFaces) + { + QuickHullHalfEdge* hedgeSidePrev = NULL; + QuickHullHalfEdge* hedgeSideBegin = NULL; + + for (PxU32 i = 0; i < horizon.size(); i++) + { + const QuickHullHalfEdge& horizonHe = *horizon[i]; + + QuickHullFace* face = createTriangle(*eyePoint, horizonHe.getHead(), horizonHe.getTail()); + mHullFaces.pushBack(face); + mNumHullFaces++; + face->getEdge(2)->setTwin(horizonHe.twin); + + QuickHullHalfEdge* hedgeSide = face->edge; + if (hedgeSidePrev != NULL) + { + hedgeSide->next->setTwin(hedgeSidePrev); + } + else + { + hedgeSideBegin = hedgeSide; + } + newFaces.pushBack(face); + hedgeSidePrev = hedgeSide; + } + hedgeSideBegin->next->setTwin(hedgeSidePrev); + } + + ////////////////////////////////////////////////////////////////////////// + // resolve unclaimed points + void QuickHull::resolveUnclaimedPoints(const QuickHullFaceArray& newFaces) + { + for (PxU32 i = 0; i < mUnclaimedPoints.size(); i++) + { + QuickHullVertex* vtx = mUnclaimedPoints[i]; + + float maxDist = mTolerance; + QuickHullFace* maxFace = NULL; + for (PxU32 j = 0; j < newFaces.size(); j++) + { + const QuickHullFace& newFace = *newFaces[j]; + if (newFace.state == QuickHullFace::eVISIBLE) + { + const float dist = newFace.distanceToPlane(vtx->point); + if (dist > maxDist) + { + maxDist = dist; + maxFace = newFaces[j]; + } + } + } + if (maxFace != NULL) + { + addPointToFace(*maxFace, vtx, maxDist); + } + } + + mUnclaimedPoints.clear(); + } + + ////////////////////////////////////////////////////////////////////////// + // helper struct for hull expand point + struct ExpandPoint + { + PxPlane plane[3]; // the 3 planes that will give us the point + PxU32 planeIndex[3]; // index of the planes for identification + + bool operator==(const ExpandPoint& expPoint) const + { + if (expPoint.planeIndex[0] == planeIndex[0] && expPoint.planeIndex[1] == planeIndex[1] && + expPoint.planeIndex[2] == planeIndex[2]) + return true; + else + return false; +} + }; + +////////////////////////////////////////////////////////////////////////// + // gets the half edge neighbors and form the expand point + void getExpandPoint(const QuickHullHalfEdge& he, ExpandPoint& expandPoint, const Ps::Array* translationTable = NULL) + { + // set the first 2 - the edge face and the twin face + expandPoint.planeIndex[0] = (translationTable) ? ((*translationTable)[he.face->index]) : (he.face->index); + + PxU32 index = translationTable ? ((*translationTable)[he.twin->face->index]) : he.twin->face->index; + if (index < expandPoint.planeIndex[0]) + { + expandPoint.planeIndex[1] = expandPoint.planeIndex[0]; + expandPoint.planeIndex[0] = index; + } + else + { + expandPoint.planeIndex[1] = index; + } + + // now the 3rd one is the next he twin index + index = translationTable ? (*translationTable)[he.next->twin->face->index] : he.next->twin->face->index; + if (index < expandPoint.planeIndex[0]) + { + expandPoint.planeIndex[2] = expandPoint.planeIndex[1]; + expandPoint.planeIndex[1] = expandPoint.planeIndex[0]; + expandPoint.planeIndex[0] = index; + } + else + { + if (index < expandPoint.planeIndex[1]) + { + expandPoint.planeIndex[2] = expandPoint.planeIndex[1]; + expandPoint.planeIndex[1] = index; + } + else + { + expandPoint.planeIndex[2] = index; + } + } + } + + ////////////////////////////////////////////////////////////////////////// + // adds the expand point, don't add similar point + void addExpandPoint(const ExpandPoint& expandPoint, Ps::Array& expandPoints) + { + for (PxU32 i = expandPoints.size(); i--;) + { + if (expandPoint == expandPoints[i]) + { + return; + } + } + + expandPoints.pushBack(expandPoint); + } + + ////////////////////////////////////////////////////////////////////////// + // helper for 3 planes intersection + static PxVec3 threePlaneIntersection(const PxPlane &p0, const PxPlane &p1, const PxPlane &p2) + { + PxMat33 mp = (PxMat33(p0.n, p1.n, p2.n)).getTranspose(); + PxMat33 mi = (mp).getInverse(); + PxVec3 b(p0.d, p1.d, p2.d); + return -mi.transform(b); + } +} + +////////////////////////////////////////////////////////////////////////// + +QuickHullConvexHullLib::QuickHullConvexHullLib(const PxConvexMeshDesc& desc, const PxCookingParams& params) + : ConvexHullLib(desc, params),mQuickHull(NULL), mCropedConvexHull(NULL), mOutMemoryBuffer(NULL), mFaceTranslateTable(NULL) +{ + mQuickHull = PX_NEW_TEMP(local::QuickHull)(params, desc); + mQuickHull->preallocate(desc.points.count); +} + +////////////////////////////////////////////////////////////////////////// + +QuickHullConvexHullLib::~QuickHullConvexHullLib() +{ + mQuickHull->releaseHull(); + PX_DELETE(mQuickHull); + + if(mCropedConvexHull) + { + PX_DELETE(mCropedConvexHull); + } + + PX_FREE(mOutMemoryBuffer); + mFaceTranslateTable = NULL; // memory is a part of mOutMemoryBuffer +} + +////////////////////////////////////////////////////////////////////////// +// create the hull +// 1. clean the input vertices +// 2. check we can construct the simplex, if not expand the input verts +// 3. prepare the quickhull - preallocate, parse input verts +// 4. construct the hull +// 5. post merge faces if limit not reached +// 6. if limit reached, expand the hull +PxConvexMeshCookingResult::Enum QuickHullConvexHullLib::createConvexHull() +{ + PxConvexMeshCookingResult::Enum res = PxConvexMeshCookingResult::eFAILURE; + + PxU32 vcount = mConvexMeshDesc.points.count; + if ( vcount < 8 ) + vcount = 8; + + PxVec3* outvsource = reinterpret_cast (PX_ALLOC_TEMP( sizeof(PxVec3)*vcount, "PxVec3")); + PxVec3 scale; + PxVec3 center; + PxU32 outvcount; + + // cleanup the vertices first + if(mConvexMeshDesc.flags & PxConvexFlag::eSHIFT_VERTICES) + { + if(!shiftAndcleanupVertices(mConvexMeshDesc.points.count, reinterpret_cast (mConvexMeshDesc.points.data), mConvexMeshDesc.points.stride, + outvcount, outvsource, scale, center )) + { + PX_FREE(outvsource); + return res; + } + } + else + { + if(!cleanupVertices(mConvexMeshDesc.points.count, reinterpret_cast (mConvexMeshDesc.points.data), mConvexMeshDesc.points.stride, + outvcount, outvsource, scale, center )) + { + PX_FREE(outvsource); + return res; + } + } + + // scale vertices back to their original size. + // move the vertices to the origin + for (PxU32 i=0; i< outvcount; i++) + { + PxVec3& v = outvsource[i]; + v.multiply(scale); + } + + local::QuickHullVertex minimumVertex[3]; + local::QuickHullVertex maximumVertex[3]; + float tolerance; + float planeTolerance; + bool canReuse = cleanupForSimplex(outvsource, outvcount, &minimumVertex[0], &maximumVertex[0], tolerance, planeTolerance); + + mQuickHull->parseInputVertices(outvsource,outvcount); + + if(canReuse) + { + mQuickHull->setPrecomputedMinMax(minimumVertex, maximumVertex, tolerance, planeTolerance); + } + + local::QuickHullResult::Enum qhRes = mQuickHull->buildHull(); + + switch(qhRes) + { + case local::QuickHullResult::eZERO_AREA_TEST_FAILED: + res = PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED; + break; + case local::QuickHullResult::eSUCCESS: + mQuickHull->postMergeHull(); + res = PxConvexMeshCookingResult::eSUCCESS; + break; + case local::QuickHullResult::ePOLYGONS_LIMIT_REACHED: + if(mQuickHull->getNbHullVerts() > mConvexMeshDesc.vertexLimit) + { + // expand the hull + if(mConvexMeshDesc.flags & PxConvexFlag::ePLANE_SHIFTING) + res = expandHull(); + else + res = expandHullOBB(); + } + res = PxConvexMeshCookingResult::ePOLYGONS_LIMIT_REACHED; + break; + case local::QuickHullResult::eVERTEX_LIMIT_REACHED: + { + // expand the hull + if(mConvexMeshDesc.flags & PxConvexFlag::ePLANE_SHIFTING) + res = expandHull(); + else + res = expandHullOBB(); + } + break; + case local::QuickHullResult::eFAILURE: + break; + }; + + // check if we need to build GRB compatible mesh + // if hull was cropped we already have a compatible mesh, if not check + // the max verts per face + if((mConvexMeshDesc.flags & PxConvexFlag::eGPU_COMPATIBLE) && !mCropedConvexHull && + res == PxConvexMeshCookingResult::eSUCCESS) + { + PX_ASSERT(mQuickHull); + // if we hit the vertex per face limit, expand the hull by cropping OBB + if(mQuickHull->maxNumVertsPerFace() > gpuMaxVertsPerFace) + { + res = expandHullOBB(); + } + } + + PX_FREE(outvsource); + return res; +} + +////////////////////////////////////////////////////////////////////////// +// fixup the input vertices to be not colinear or coplanar for the initial simplex find +bool QuickHullConvexHullLib::cleanupForSimplex(PxVec3* vertices, PxU32 vertexCount, local::QuickHullVertex* minimumVertex, + local::QuickHullVertex* maximumVertex, float& tolerance, float& planeTolerance) +{ + bool retVal = true; + + for (PxU32 i = 0; i < 3; i++) + { + minimumVertex[i].point = vertices[0]; + minimumVertex[i].index = 0; + maximumVertex[i].point = vertices[0]; + maximumVertex[i].index = 0; + + } + + PxVec3 max = vertices[0]; + PxVec3 min = vertices[0]; + + // get the max min vertices along the x,y,z + for (PxU32 i = 1; i < vertexCount; i++) + { + const PxVec3& testPoint = vertices[i]; + if (testPoint.x > max.x) + { + max.x = testPoint.x; + maximumVertex[0].point = testPoint; + maximumVertex[0].index = i; + } + else if (testPoint.x < min.x) + { + min.x = testPoint.x; + minimumVertex[0].point = testPoint; + minimumVertex[0].index = i; + } + + if (testPoint.y > max.y) + { + max.y = testPoint.y; + maximumVertex[1].point = testPoint; + maximumVertex[1].index = i; + } + else if (testPoint.y < min.y) + { + min.y = testPoint.y; + minimumVertex[1].point = testPoint; + minimumVertex[1].index = i; + } + + if (testPoint.z > max.z) + { + max.z = testPoint.z; + maximumVertex[2].point = testPoint; + maximumVertex[2].index = i; + } + else if (testPoint.z < min.z) + { + min.z = testPoint.z; + minimumVertex[2].point = testPoint; + minimumVertex[2].index = i; + } + } + + const float sizeTol = (max.x-min.x + max.y - min.y + max.z - min.z)*0.5f; + tolerance = PxMax(local::PLANE_THICKNES * sizeTol, local::PLANE_THICKNES); + planeTolerance = PxMax(mCookingParams.planeTolerance *sizeTol, mCookingParams.planeTolerance); + + float fmax = 0; + PxU32 imax = 0; + + for (PxU32 i = 0; i < 3; i++) + { + float diff = (maximumVertex[i].point)[i] - (minimumVertex[i].point)[i]; + if (diff > fmax) + { + fmax = diff; + imax = i; + } + } + + PxVec3 simplex[4]; + + // set first two vertices to be those with the greatest + // one dimensional separation + simplex[0] = maximumVertex[imax].point; + simplex[1] = minimumVertex[imax].point; + + // set third vertex to be the vertex farthest from + // the line between simplex[0] and simplex[1] + PxVec3 normal; + float maxDist = 0; + imax = 0; + PxVec3 u01 = (simplex[1] - simplex[0]); + u01.normalize(); + + for (PxU32 i = 0; i < vertexCount; i++) + { + const PxVec3& testPoint = vertices[i]; + const PxVec3 diff = testPoint - simplex[0]; + const PxVec3 xprod = u01.cross(diff); + const float lenSqr = xprod.magnitudeSquared(); + if (lenSqr > maxDist) + { + maxDist = lenSqr; + simplex[2] = testPoint; + normal = xprod; + imax = i; + } + } + + if (PxSqrt(maxDist) < tolerance) + { + // points are collinear, we have to move the point further + PxVec3 u02 = simplex[2] - simplex[0]; + float fT = u02.dot(u01); + const float sqrLen = u01.magnitudeSquared(); + fT /= sqrLen; + PxVec3 n = u02 - fT*u01; + n.normalize(); + const PxVec3 mP = simplex[2] + n * tolerance; + simplex[2] = mP; + vertices[imax] = mP; + retVal = false; + } + normal.normalize(); + + // set the forth vertex in the normal direction + float d0 = simplex[2].dot(normal); + maxDist = 0.0f; + imax = 0; + for (PxU32 i = 0; i < vertexCount; i++) + { + const PxVec3& testPoint = vertices[i]; + float dist = PxAbs(testPoint.dot(normal) - d0); + if (dist > maxDist) + { + maxDist = dist; + simplex[3] = testPoint; + imax = i; + } + } + + if (PxAbs(maxDist) < tolerance) + { + float dist = (vertices[imax].dot(normal) - d0); + if (dist > 0) + vertices[imax] = vertices[imax] + normal * tolerance; + else + vertices[imax] = vertices[imax] - normal * tolerance; + retVal = false; + } + + return retVal; +} + +////////////////////////////////////////////////////////////////////////// +// expand the hull with the from the limited triangles set +// expand hull will do following steps: +// 1. get expand points from hull that form the best hull with given vertices +// 2. expand the planes to have all vertices inside the planes volume +// 3. compute new points by 3 adjacency planes intersections +// 4. take those points and create the hull from them +PxConvexMeshCookingResult::Enum QuickHullConvexHullLib::expandHull() +{ + Ps::Array expandPoints; + expandPoints.reserve(mQuickHull->mNumVertices); + + // go over faces and gather expand points + for (PxU32 i = 0; i < mQuickHull->mHullFaces.size(); i++) + { + const local::QuickHullFace& face = *mQuickHull->mHullFaces[i]; + if(face.state == local::QuickHullFace::eVISIBLE) + { + local::ExpandPoint expandPoint; + local::QuickHullHalfEdge* he = face.edge; + local::getExpandPoint(*he, expandPoint); + local::addExpandPoint(expandPoint, expandPoints); + he = he->next; + while (he != face.edge) + { + local::getExpandPoint(*he, expandPoint); + local::addExpandPoint(expandPoint, expandPoints); + he = he->next; + } + } + } + + + // go over the planes now and expand them + for(PxU32 iVerts=0;iVerts< mQuickHull->mNumVertices;iVerts++) + { + const local::QuickHullVertex& vertex = mQuickHull->mVerticesList[iVerts]; + + for (PxU32 i = 0; i < mQuickHull->mHullFaces.size(); i++) + { + local::QuickHullFace& face = *mQuickHull->mHullFaces[i]; + if(face.state == local::QuickHullFace::eVISIBLE) + { + const float dist = face.distanceToPlane(vertex.point); + if(dist > 0 && dist > face.expandOffset) + { + face.expandOffset = dist; + } + } + } + } + + // fill the expand points planes + for(PxU32 i=0;imFreeFaces.getItem(expandPoint.planeIndex[k]); + PX_ASSERT(face.index == expandPoint.planeIndex[k]); + PxPlane plane; + plane.n = face.normal; + plane.d = -face.planeOffset; + if(face.expandOffset > 0.0f) + plane.d -= face.expandOffset; + expandPoint.plane[k] = plane; + } + } + + // now find the plane intersection + PX_ALLOCA(vertices,PxVec3,expandPoints.size()); + for(PxU32 i=0;imCookingParams, mQuickHull->mConvexDesc); + newHull->preallocate(expandPoints.size()); + newHull->parseInputVertices(vertices,expandPoints.size()); + + local::QuickHullResult::Enum qhRes = newHull->buildHull(); + switch(qhRes) + { + case local::QuickHullResult::eZERO_AREA_TEST_FAILED: + { + newHull->releaseHull(); + PX_DELETE(newHull); + return PxConvexMeshCookingResult::eZERO_AREA_TEST_FAILED; + } + case local::QuickHullResult::eSUCCESS: + case local::QuickHullResult::eVERTEX_LIMIT_REACHED: + case local::QuickHullResult::ePOLYGONS_LIMIT_REACHED: + { + mQuickHull->releaseHull(); + PX_DELETE(mQuickHull); + mQuickHull = newHull; + } + break; + case local::QuickHullResult::eFAILURE: + { + newHull->releaseHull(); + PX_DELETE(newHull); + return PxConvexMeshCookingResult::eFAILURE; + } + }; + + return PxConvexMeshCookingResult::eSUCCESS; +} + +////////////////////////////////////////////////////////////////////////// +// expand the hull from the limited triangles set +// 1. collect all planes +// 2. create OBB from the input verts +// 3. slice the OBB with the planes +// 5. iterate till vlimit is reached +PxConvexMeshCookingResult::Enum QuickHullConvexHullLib::expandHullOBB() +{ + Ps::Array expandPlanes; + expandPlanes.reserve(mQuickHull->mHullFaces.size()); + + // collect expand planes + for (PxU32 i = 0; i < mQuickHull->mHullFaces.size(); i++) + { + local::QuickHullFace& face = *mQuickHull->mHullFaces[i]; + if (face.state == local::QuickHullFace::eVISIBLE) + { + PxPlane plane; + plane.n = face.normal; + plane.d = -face.planeOffset; + if (face.expandOffset > 0.0f) + plane.d -= face.expandOffset; + + expandPlanes.pushBack(plane); + } + } + + + PxTransform obbTransform; + PxVec3 sides; + + // compute the OBB + PxConvexMeshDesc convexDesc; + fillConvexMeshDescFromQuickHull(convexDesc); + convexDesc.flags = mConvexMeshDesc.flags; + computeOBBFromConvex(convexDesc, sides, obbTransform); + + // free the memory used for the convex mesh desc + PX_FREE_AND_RESET(mOutMemoryBuffer); + mFaceTranslateTable = NULL; + + // crop the OBB + PxU32 maxplanes = PxMin(PxU32(256), expandPlanes.size()); + + ConvexHull* c = PX_NEW_TEMP(ConvexHull)(sides*0.5f,obbTransform, expandPlanes); + + const float planeTolerance = mQuickHull->mPlaneTolerance; + const float epsilon = mQuickHull->mTolerance; + + PxI32 k; + while (maxplanes-- && (k = c->findCandidatePlane(planeTolerance, epsilon)) >= 0) + { + ConvexHull* tmp = c; + c = convexHullCrop(*tmp, expandPlanes[PxU32(k)], planeTolerance); + if (c == NULL) + { + c = tmp; + break; + } // might want to debug this case better!!! + if (!c->assertIntact(planeTolerance)) + { + PX_DELETE(c); + c = tmp; + break; + } // might want to debug this case better too!!! + + // check for vertex limit + if (c->getVertices().size() > mConvexMeshDesc.vertexLimit) + { + PX_DELETE(c); + c = tmp; + maxplanes = 0; + break; + } + // check for vertex limit per face if necessary, GRB supports max 32 verts per face + if ((mConvexMeshDesc.flags & PxConvexFlag::eGPU_COMPATIBLE) && c->maxNumVertsPerFace() > gpuMaxVertsPerFace) + { + PX_DELETE(c); + c = tmp; + maxplanes = 0; + break; + } + PX_DELETE(tmp); + } + + PX_ASSERT(c->assertIntact(planeTolerance)); + + mCropedConvexHull = c; + + return PxConvexMeshCookingResult::eSUCCESS; +} + +////////////////////////////////////////////////////////////////////////// + +bool QuickHullConvexHullLib::createEdgeList(const PxU32 nbIndices, const PxU8* indices, PxU8** outHullDataFacesByEdges8, PxU16** outEdgeData16, PxU16** outEdges) +{ + // if we croped hull, we dont have the edge information, early exit + if (mCropedConvexHull) + return false; + + PX_ASSERT(mQuickHull); + + // Make sure we did recieved empty buffers + PX_ASSERT(*outHullDataFacesByEdges8 == NULL); + PX_ASSERT(*outEdges == NULL); + PX_ASSERT(*outEdgeData16 == NULL); + + // Allocated the out bufferts + PxU8* hullDataFacesByEdges8 = PX_NEW(PxU8)[nbIndices]; + PxU16* edges = PX_NEW(PxU16)[nbIndices]; + PxU16* edgeData16 = PX_NEW(PxU16)[nbIndices]; + + *outHullDataFacesByEdges8 = hullDataFacesByEdges8; + *outEdges = edges; + *outEdgeData16 = edgeData16; + + PxU16 edgeIndex = 0; + PxU32 edgeOffset = 0; + for(PxU32 i = 0; i < mQuickHull->mNumHullFaces; i++) + { + const local::QuickHullFace& face = *mQuickHull->mHullFaces[mFaceTranslateTable[i]]; + + // Face must be visible + PX_ASSERT(face.state == local::QuickHullFace::eVISIBLE); + + // parse the edges + const PxU32 startEdgeOffset = edgeOffset; + local::QuickHullHalfEdge* hedge = face.edge; + do + { + // check if hedge has been stored + if(hedge->edgeIndex == 0xFFFFFFFF) + { + edges[edgeIndex*2] = indices[edgeOffset]; + edges[edgeIndex*2 + 1] = indices[(hedge->next != face.edge) ? edgeOffset + 1 : startEdgeOffset]; + + hullDataFacesByEdges8[edgeIndex*2] = hedge->face->outIndex; + hullDataFacesByEdges8[edgeIndex*2 + 1] = hedge->next->twin->face->outIndex; + + edgeData16[edgeOffset] = edgeIndex; + + hedge->edgeIndex = edgeIndex; + hedge->next->twin->prev->edgeIndex = edgeIndex; + + edgeIndex++; + } + else + { + edgeData16[edgeOffset] = Ps::to16(hedge->edgeIndex); + } + + hedge = hedge->next; + edgeOffset++; + } while (hedge != face.edge); + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// fill the descriptor with computed verts, indices and polygons +void QuickHullConvexHullLib::fillConvexMeshDesc(PxConvexMeshDesc& desc) +{ + if (mCropedConvexHull) + fillConvexMeshDescFromCroppedHull(desc); + else + fillConvexMeshDescFromQuickHull(desc); + + if(mConvexMeshDesc.flags & PxConvexFlag::eSHIFT_VERTICES) + shiftConvexMeshDesc(desc); +} + +////////////////////////////////////////////////////////////////////////// +// fill the descriptor with computed verts, indices and polygons from quickhull convex +void QuickHullConvexHullLib::fillConvexMeshDescFromQuickHull(PxConvexMeshDesc& desc) +{ + // get the number of indices needed + PxU32 numIndices = 0; + PxU32 numFaces = mQuickHull->mHullFaces.size(); + PxU32 numFacesOut = 0; + PxU32 largestFace = 0; // remember the largest face, we store it as the first face, required for GRB test (max 32 vers per face supported) + for (PxU32 i = 0; i < numFaces; i++) + { + const local::QuickHullFace& face = *mQuickHull->mHullFaces[i]; + if(face.state == local::QuickHullFace::eVISIBLE) + { + numFacesOut++; + numIndices += face.numEdges; + if(face.numEdges > mQuickHull->mHullFaces[largestFace]->numEdges) + largestFace = i; + } + } + + // allocate out buffers + const PxU32 indicesBufferSize = sizeof(PxU32)*numIndices; + const PxU32 verticesBufferSize = sizeof(PxVec3)*(mQuickHull->mNumVertices + 1); + const PxU32 facesBufferSize = sizeof(PxHullPolygon)*numFacesOut; + const PxU32 faceTranslationTableSize = sizeof(PxU16)*numFacesOut; + const PxU32 translationTableSize = sizeof(PxU32)*mQuickHull->mNumVertices; + const PxU32 bufferMemorySize = indicesBufferSize + verticesBufferSize + facesBufferSize + faceTranslationTableSize + translationTableSize; + mOutMemoryBuffer = reinterpret_cast(PX_ALLOC_TEMP(bufferMemorySize, "ConvexMeshDesc")); + + PxU32* indices = reinterpret_cast (mOutMemoryBuffer); + PxVec3* vertices = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize); + PxHullPolygon* polygons = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize + verticesBufferSize); + mFaceTranslateTable = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize + verticesBufferSize + facesBufferSize); + PxI32* translateTable = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize + verticesBufferSize + facesBufferSize + faceTranslationTableSize); + PxMemSet(translateTable,-1,mQuickHull->mNumVertices*sizeof(PxU32)); + + // go over the hullPolygons and mark valid vertices, create translateTable + PxU32 numVertices = 0; + for (PxU32 i = 0; i < numFaces; i++) + { + const local::QuickHullFace& face = *mQuickHull->mHullFaces[i]; + if(face.state == local::QuickHullFace::eVISIBLE) + { + local::QuickHullHalfEdge* he = face.edge; + if(translateTable[he->tail.index] == -1) + { + vertices[numVertices] = he->tail.point; + translateTable[he->tail.index] = PxI32(numVertices); + numVertices++; + } + he = he->next; + while (he != face.edge) + { + if(translateTable[he->tail.index] == -1) + { + vertices[numVertices] = he->tail.point; + translateTable[he->tail.index] = PxI32(numVertices); + numVertices++; + } + he = he->next; + } + } + } + + + desc.points.count = numVertices; + desc.points.data = vertices; + desc.points.stride = sizeof(PxVec3); + + desc.indices.count = numIndices; + desc.indices.data = indices; + desc.indices.stride = sizeof(PxU32); + + desc.polygons.count = numFacesOut; + desc.polygons.data = polygons; + desc.polygons.stride = sizeof(PxHullPolygon); + + PxU16 indexOffset = 0; + numFacesOut = 0; + for (PxU32 i = 0; i < numFaces; i++) + { + // faceIndex - store the largest face first then the rest + PxU32 faceIndex; + if(i == 0) + { + faceIndex = largestFace; + } + else + { + faceIndex = (i == largestFace) ? 0 : i; + } + + local::QuickHullFace& face = *mQuickHull->mHullFaces[faceIndex]; + if(face.state == local::QuickHullFace::eVISIBLE) + { + //create index data + local::QuickHullHalfEdge* he = face.edge; + PxU32 index = 0; + he->edgeIndex = 0xFFFFFFFF; + indices[index + indexOffset] = PxU32(translateTable[he->tail.index]); + index++; + he = he->next; + while (he != face.edge) + { + indices[index + indexOffset] = PxU32(translateTable[he->tail.index]); + index++; + he->edgeIndex = 0xFFFFFFFF; + he = he->next; + } + + // create polygon + PxHullPolygon polygon; + polygon.mPlane[0] = face.normal[0]; + polygon.mPlane[1] = face.normal[1]; + polygon.mPlane[2] = face.normal[2]; + polygon.mPlane[3] = -face.planeOffset; + + polygon.mIndexBase = indexOffset; + polygon.mNbVerts = face.numEdges; + indexOffset += face.numEdges; + polygons[numFacesOut] = polygon; + mFaceTranslateTable[numFacesOut] = Ps::to16(faceIndex); + face.outIndex = Ps::to8(numFacesOut); + numFacesOut++; + } + } + + PX_ASSERT(mQuickHull->mNumHullFaces == numFacesOut); +} + +////////////////////////////////////////////////////////////////////////// +// fill the desc from cropped hull data +void QuickHullConvexHullLib::fillConvexMeshDescFromCroppedHull(PxConvexMeshDesc& outDesc) +{ + PX_ASSERT(mCropedConvexHull); + + // allocate the output buffers + const PxU32 numIndices = mCropedConvexHull->getEdges().size(); + const PxU32 numPolygons = mCropedConvexHull->getFacets().size(); + const PxU32 numVertices = mCropedConvexHull->getVertices().size(); + const PxU32 indicesBufferSize = sizeof(PxU32)*numIndices; + const PxU32 facesBufferSize = sizeof(PxHullPolygon)*numPolygons; + const PxU32 verticesBufferSize = sizeof(PxVec3)*(numVertices + 1); // allocate additional vec3 for V4 safe load in VolumeInteration + const PxU32 bufferMemorySize = indicesBufferSize + verticesBufferSize + facesBufferSize; + mOutMemoryBuffer = reinterpret_cast(PX_ALLOC_TEMP(bufferMemorySize, "ConvexMeshDesc")); + + // parse the hullOut and fill the result with vertices and polygons + PxU32* indicesOut = reinterpret_cast (mOutMemoryBuffer); + PxHullPolygon* polygonsOut = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize); + PxVec3* vertsOut = reinterpret_cast (mOutMemoryBuffer + indicesBufferSize + facesBufferSize); + PxMemCopy(vertsOut, mCropedConvexHull->getVertices().begin(), sizeof(PxVec3)*numVertices); + + PxU32 i = 0; + PxU32 k = 0; + PxU32 j = 1; + while (i < mCropedConvexHull->getEdges().size()) + { + j = 1; + PxHullPolygon& polygon = polygonsOut[k]; + // get num indices per polygon + while (j + i < mCropedConvexHull->getEdges().size() && mCropedConvexHull->getEdges()[i].p == mCropedConvexHull->getEdges()[i + j].p) + { + j++; + } + polygon.mNbVerts = Ps::to16(j); + polygon.mIndexBase = Ps::to16(i); + + // get the plane + polygon.mPlane[0] = mCropedConvexHull->getFacets()[k].n[0]; + polygon.mPlane[1] = mCropedConvexHull->getFacets()[k].n[1]; + polygon.mPlane[2] = mCropedConvexHull->getFacets()[k].n[2]; + + polygon.mPlane[3] = mCropedConvexHull->getFacets()[k].d; + + while (j--) + { + indicesOut[i] = mCropedConvexHull->getEdges()[i].v; + i++; + } + k++; + } + + PX_ASSERT(k == mCropedConvexHull->getFacets().size()); + + outDesc.indices.count = numIndices; + outDesc.indices.stride = sizeof(PxU32); + outDesc.indices.data = indicesOut; + + outDesc.points.count = numVertices; + outDesc.points.stride = sizeof(PxVec3); + outDesc.points.data = vertsOut; + + outDesc.polygons.count = numPolygons; + outDesc.polygons.stride = sizeof(PxHullPolygon); + outDesc.polygons.data = polygonsOut; + + swapLargestFace(outDesc); +} + diff --git a/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h new file mode 100644 index 000000000..7d576ed1f --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_QUICKHULL_CONVEXHULLLIB_H +#define PX_QUICKHULL_CONVEXHULLLIB_H + +#include "ConvexHullLib.h" +#include "Ps.h" +#include "PsArray.h" +#include "PsUserAllocated.h" + +namespace local +{ + class QuickHull; + struct QuickHullVertex; +} + +namespace physx +{ + class ConvexHull; + + ////////////////////////////////////////////////////////////////////////// + // Quickhull lib constructs the hull from given input points. The resulting hull + // will only contain a subset of the input points. The algorithm does incrementally + // adds most furthest vertices to the starting simplex. The produced hulls are build with high precision + // and produce more stable and correct results, than the legacy algorithm. + class QuickHullConvexHullLib: public ConvexHullLib, public Ps::UserAllocated + { + PX_NOCOPY(QuickHullConvexHullLib) + public: + + // functions + QuickHullConvexHullLib(const PxConvexMeshDesc& desc, const PxCookingParams& params); + + ~QuickHullConvexHullLib(); + + // computes the convex hull from provided points + virtual PxConvexMeshCookingResult::Enum createConvexHull(); + + // fills the convexmeshdesc with computed hull data + virtual void fillConvexMeshDesc(PxConvexMeshDesc& desc); + + // provide the edge list information + virtual bool createEdgeList(const PxU32, const PxU8* , PxU8** , PxU16** , PxU16** ); + + protected: + // if vertex limit reached we need to expand the hull using the OBB slicing + PxConvexMeshCookingResult::Enum expandHullOBB(); + + // if vertex limit reached we need to expand the hull using the plane shifting + PxConvexMeshCookingResult::Enum expandHull(); + + // checks for collinearity and co planarity + // returns true if the simplex was ok, we can reuse the computed tolerances and min/max values + bool cleanupForSimplex(PxVec3* vertices, PxU32 vertexCount, local::QuickHullVertex* minimumVertex, + local::QuickHullVertex* maximumVertex, float& tolerance, float& planeTolerance); + + // fill the result desc from quick hull convex + void fillConvexMeshDescFromQuickHull(PxConvexMeshDesc& desc); + + // fill the result desc from cropped hull convex + void fillConvexMeshDescFromCroppedHull(PxConvexMeshDesc& desc); + + private: + local::QuickHull* mQuickHull; // the internal quick hull representation + ConvexHull* mCropedConvexHull; //the hull cropped from OBB, used for vertex limit path + + PxU8* mOutMemoryBuffer; // memory buffer used for output data + PxU16* mFaceTranslateTable; // translation table mapping output faces to internal quick hull table + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp new file mode 100644 index 000000000..0c739bc90 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp @@ -0,0 +1,797 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +//#ifdef PX_COOKING + +/* +* This code computes volume integrals needed to compute mass properties of polyhedral bodies. +* Based on public domain code by Brian Mirtich. +*/ +#include "foundation/PxMemory.h" +#include "VolumeIntegration.h" +#include "PxSimpleTriangleMesh.h" +#include "PxConvexMeshDesc.h" +#include "GuConvexMeshData.h" +#include "PsUtilities.h" +#include "PsVecMath.h" + + +namespace physx +{ + + using namespace Ps::aos; + +namespace +{ + + class VolumeIntegrator + { + public: + VolumeIntegrator(PxSimpleTriangleMesh mesh, PxF64 mDensity); + ~VolumeIntegrator(); + bool computeVolumeIntegrals(PxIntegrals& ir); + private: + struct Normal + { + PxVec3 normal; + PxF32 w; + }; + + struct Face + { + PxF64 Norm[3]; + PxF64 w; + PxU32 Verts[3]; + }; + + // Data structures + PxF64 mMass; //!< Mass + PxF64 mDensity; //!< Density + PxSimpleTriangleMesh mesh; + //Normal * faceNormals; //!< temp face normal data structure + + + + + unsigned int mA; //!< Alpha + unsigned int mB; //!< Beta + unsigned int mC; //!< Gamma + + // Projection integrals + PxF64 mP1; + PxF64 mPa; //!< Pi Alpha + PxF64 mPb; //!< Pi Beta + PxF64 mPaa; //!< Pi Alpha^2 + PxF64 mPab; //!< Pi AlphaBeta + PxF64 mPbb; //!< Pi Beta^2 + PxF64 mPaaa; //!< Pi Alpha^3 + PxF64 mPaab; //!< Pi Alpha^2Beta + PxF64 mPabb; //!< Pi AlphaBeta^2 + PxF64 mPbbb; //!< Pi Beta^3 + + // Face integrals + PxF64 mFa; //!< FAlpha + PxF64 mFb; //!< FBeta + PxF64 mFc; //!< FGamma + PxF64 mFaa; //!< FAlpha^2 + PxF64 mFbb; //!< FBeta^2 + PxF64 mFcc; //!< FGamma^2 + PxF64 mFaaa; //!< FAlpha^3 + PxF64 mFbbb; //!< FBeta^3 + PxF64 mFccc; //!< FGamma^3 + PxF64 mFaab; //!< FAlpha^2Beta + PxF64 mFbbc; //!< FBeta^2Gamma + PxF64 mFcca; //!< FGamma^2Alpha + + // The 10 volume integrals + PxF64 mT0; //!< ~Total mass + PxF64 mT1[3]; //!< Location of the center of mass + PxF64 mT2[3]; //!< Moments of inertia + PxF64 mTP[3]; //!< Products of inertia + + // Internal methods + // bool Init(); + PxVec3 computeCenterOfMass(); + void computeInertiaTensor(PxF64* J); + void computeCOMInertiaTensor(PxF64* J); + void computeFaceNormal(Face & f, PxU32 * indices); + + void computeProjectionIntegrals(const Face& f); + void computeFaceIntegrals(const Face& f); + }; + + #define X 0u + #define Y 1u + #define Z 2u + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Constructor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + VolumeIntegrator::VolumeIntegrator(PxSimpleTriangleMesh mesh_, PxF64 density) + { + mDensity = density; + mMass = 0.0; + this->mesh = mesh_; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Destructor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + VolumeIntegrator::~VolumeIntegrator() + { + } + + void VolumeIntegrator::computeFaceNormal(Face & f, PxU32 * indices) + { + const PxU8 * vertPointer = reinterpret_cast(mesh.points.data); + + //two edges + PxVec3 d1 = (*reinterpret_cast(vertPointer + mesh.points.stride * indices[1] )) - (*reinterpret_cast(vertPointer + mesh.points.stride * indices[0] )); + PxVec3 d2 = (*reinterpret_cast(vertPointer + mesh.points.stride * indices[2] )) - (*reinterpret_cast(vertPointer + mesh.points.stride * indices[1] )); + + + PxVec3 normal = d1.cross(d2); + + normal.normalize(); + + f.w = - PxF64(normal.dot( (*reinterpret_cast(vertPointer + mesh.points.stride * indices[0] )) )); + + f.Norm[0] = PxF64(normal.x); + f.Norm[1] = PxF64(normal.y); + f.Norm[2] = PxF64(normal.z); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes volume integrals for a polyhedron by summing surface integrals over its faces. + * \param ir [out] a result structure. + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool VolumeIntegrator::computeVolumeIntegrals(PxIntegrals& ir) + { + // Clear all integrals + mT0 = mT1[X] = mT1[Y] = mT1[Z] = mT2[X] = mT2[Y] = mT2[Z] = mTP[X] = mTP[Y] = mTP[Z] = 0; + + Face f; + const PxU8 * trigPointer = reinterpret_cast(mesh.triangles.data); + for(PxU32 i=0;i(trigPointer))[0]; + f.Verts[1] = (reinterpret_cast(trigPointer))[1]; + f.Verts[2] = (reinterpret_cast(trigPointer))[2]; + } + else + { + f.Verts[0] = (reinterpret_cast(trigPointer)[0]); + f.Verts[1] = (reinterpret_cast(trigPointer)[1]); + f.Verts[2] = (reinterpret_cast(trigPointer)[2]); + } + + if (mesh.flags & PxMeshFlag::eFLIPNORMALS) + { + PxU32 t = f.Verts[1]; + f.Verts[1] = f.Verts[2]; + f.Verts[2] = t; + } + + //compute face normal: + computeFaceNormal(f,f.Verts); + + // Compute alpha/beta/gamma as the right-handed permutation of (x,y,z) that maximizes |n| + PxF64 nx = fabs(f.Norm[X]); + PxF64 ny = fabs(f.Norm[Y]); + PxF64 nz = fabs(f.Norm[Z]); + if (nx > ny && nx > nz) mC = X; + else mC = (ny > nz) ? Y : Z; + mA = (mC + 1) % 3; + mB = (mA + 1) % 3; + + // Compute face contribution + computeFaceIntegrals(f); + + // Update integrals + mT0 += f.Norm[X] * ((mA == X) ? mFa : ((mB == X) ? mFb : mFc)); + + mT1[mA] += f.Norm[mA] * mFaa; + mT1[mB] += f.Norm[mB] * mFbb; + mT1[mC] += f.Norm[mC] * mFcc; + + mT2[mA] += f.Norm[mA] * mFaaa; + mT2[mB] += f.Norm[mB] * mFbbb; + mT2[mC] += f.Norm[mC] * mFccc; + + mTP[mA] += f.Norm[mA] * mFaab; + mTP[mB] += f.Norm[mB] * mFbbc; + mTP[mC] += f.Norm[mC] * mFcca; + } + + mT1[X] /= 2; mT1[Y] /= 2; mT1[Z] /= 2; + mT2[X] /= 3; mT2[Y] /= 3; mT2[Z] /= 3; + mTP[X] /= 2; mTP[Y] /= 2; mTP[Z] /= 2; + + // Fill result structure + ir.COM = computeCenterOfMass(); + computeInertiaTensor(reinterpret_cast(ir.inertiaTensor)); + computeCOMInertiaTensor(reinterpret_cast(ir.COMInertiaTensor)); + ir.mass = mMass; + return true; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the center of mass. + * \return The center of mass. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + PxVec3 VolumeIntegrator::computeCenterOfMass() + { + // Compute center of mass + PxVec3 COM(0.0f, 0.0f, 0.0f); + if(mT0!=0.0) + { + COM.x = float(mT1[X] / mT0); + COM.y = float(mT1[Y] / mT0); + COM.z = float(mT1[Z] / mT0); + } + return COM; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups the inertia tensor relative to the origin. + * \param it [out] the returned inertia tensor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void VolumeIntegrator::computeInertiaTensor(PxF64* it) + { + PxF64 J[3][3]; + + // Compute inertia tensor + J[X][X] = mDensity * (mT2[Y] + mT2[Z]); + J[Y][Y] = mDensity * (mT2[Z] + mT2[X]); + J[Z][Z] = mDensity * (mT2[X] + mT2[Y]); + + J[X][Y] = J[Y][X] = - mDensity * mTP[X]; + J[Y][Z] = J[Z][Y] = - mDensity * mTP[Y]; + J[Z][X] = J[X][Z] = - mDensity * mTP[Z]; + + PxMemCopy(it, J, 9*sizeof(PxF64)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups the inertia tensor relative to the COM. + * \param it [out] the returned inertia tensor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void VolumeIntegrator::computeCOMInertiaTensor(PxF64* it) + { + PxF64 J[3][3]; + + mMass = mDensity * mT0; + + const PxVec3 COM = computeCenterOfMass(); + const PxVec3 MassCOM(PxF32(mMass) * COM); + const PxVec3 MassCOM2(MassCOM.x * COM.x, MassCOM.y * COM.y, MassCOM.z * COM.z); + + // Compute initial inertia tensor + computeInertiaTensor(reinterpret_cast(J)); + + // Translate inertia tensor to center of mass + // Huyghens' theorem: + // Jx'x' = Jxx - m*(YG^2+ZG^2) + // Jy'y' = Jyy - m*(ZG^2+XG^2) + // Jz'z' = Jzz - m*(XG^2+YG^2) + // XG, YG, ZG = new origin + // YG^2+ZG^2 = dx^2 + J[X][X] -= PxF64(MassCOM2.y + MassCOM2.z); + J[Y][Y] -= PxF64(MassCOM2.z + MassCOM2.x); + J[Z][Z] -= PxF64(MassCOM2.x + MassCOM2.y); + + // Huyghens' theorem: + // Jx'y' = Jxy - m*XG*YG + // Jy'z' = Jyz - m*YG*ZG + // Jz'x' = Jzx - m*ZG*XG + // ### IS THE SIGN CORRECT ? + J[X][Y] = J[Y][X] += PxF64(MassCOM.x * COM.y); + J[Y][Z] = J[Z][Y] += PxF64(MassCOM.y * COM.z); + J[Z][X] = J[X][Z] += PxF64(MassCOM.z * COM.x); + + PxMemCopy(it, J, 9*sizeof(PxF64)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes integrals over a face projection from the coordinates of the projections vertices. + * \param f [in] a face structure. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void VolumeIntegrator::computeProjectionIntegrals(const Face& f) + { + mP1 = mPa = mPb = mPaa = mPab = mPbb = mPaaa = mPaab = mPabb = mPbbb = 0.0; + + const PxU8* vertPointer = reinterpret_cast(mesh.points.data); + for(PxU32 i=0;i<3;i++) + { + const PxVec3& p0 = *reinterpret_cast(vertPointer + mesh.points.stride * (f.Verts[i]) ); + const PxVec3& p1 = *reinterpret_cast(vertPointer + mesh.points.stride * (f.Verts[(i+1) % 3]) ); + + + PxF64 a0 = PxF64(p0[mA]); + PxF64 b0 = PxF64(p0[mB]); + PxF64 a1 = PxF64(p1[mA]); + PxF64 b1 = PxF64(p1[mB]); + + PxF64 da = a1 - a0; // DeltaA + PxF64 db = b1 - b0; // DeltaB + + PxF64 a0_2 = a0 * a0; // Alpha0^2 + PxF64 a0_3 = a0_2 * a0; // ... + PxF64 a0_4 = a0_3 * a0; + + PxF64 b0_2 = b0 * b0; + PxF64 b0_3 = b0_2 * b0; + PxF64 b0_4 = b0_3 * b0; + + PxF64 a1_2 = a1 * a1; + PxF64 a1_3 = a1_2 * a1; + + PxF64 b1_2 = b1 * b1; + PxF64 b1_3 = b1_2 * b1; + + PxF64 C1 = a1 + a0; + + PxF64 Ca = a1*C1 + a0_2; + PxF64 Caa = a1*Ca + a0_3; + PxF64 Caaa = a1*Caa + a0_4; + + PxF64 Cb = b1*(b1 + b0) + b0_2; + PxF64 Cbb = b1*Cb + b0_3; + PxF64 Cbbb = b1*Cbb + b0_4; + + PxF64 Cab = 3*a1_2 + 2*a1*a0 + a0_2; + PxF64 Kab = a1_2 + 2*a1*a0 + 3*a0_2; + + PxF64 Caab = a0*Cab + 4*a1_3; + PxF64 Kaab = a1*Kab + 4*a0_3; + + PxF64 Cabb = 4*b1_3 + 3*b1_2*b0 + 2*b1*b0_2 + b0_3; + PxF64 Kabb = b1_3 + 2*b1_2*b0 + 3*b1*b0_2 + 4*b0_3; + + mP1 += db*C1; + mPa += db*Ca; + mPaa += db*Caa; + mPaaa += db*Caaa; + mPb += da*Cb; + mPbb += da*Cbb; + mPbbb += da*Cbbb; + mPab += db*(b1*Cab + b0*Kab); + mPaab += db*(b1*Caab + b0*Kaab); + mPabb += da*(a1*Cabb + a0*Kabb); + } + + mP1 /= 2.0; + mPa /= 6.0; + mPaa /= 12.0; + mPaaa /= 20.0; + mPb /= -6.0; + mPbb /= -12.0; + mPbbb /= -20.0; + mPab /= 24.0; + mPaab /= 60.0; + mPabb /= -60.0; + } + + #define SQR(x) ((x)*(x)) //!< Returns x square + #define CUBE(x) ((x)*(x)*(x)) //!< Returns x cube + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes surface integrals over a polyhedral face from the integrals over its projection. + * \param f [in] a face structure. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void VolumeIntegrator::computeFaceIntegrals(const Face& f) + { + computeProjectionIntegrals(f); + + PxF64 w = f.w; + const PxF64* n = f.Norm; + PxF64 k1 = 1 / n[mC]; + PxF64 k2 = k1 * k1; + PxF64 k3 = k2 * k1; + PxF64 k4 = k3 * k1; + + mFa = k1 * mPa; + mFb = k1 * mPb; + mFc = -k2 * (n[mA]*mPa + n[mB]*mPb + w*mP1); + + mFaa = k1 * mPaa; + mFbb = k1 * mPbb; + mFcc = k3 * (SQR(n[mA])*mPaa + 2*n[mA]*n[mB]*mPab + SQR(n[mB])*mPbb + w*(2*(n[mA]*mPa + n[mB]*mPb) + w*mP1)); + + mFaaa = k1 * mPaaa; + mFbbb = k1 * mPbbb; + mFccc = -k4 * (CUBE(n[mA])*mPaaa + 3*SQR(n[mA])*n[mB]*mPaab + + 3*n[mA]*SQR(n[mB])*mPabb + CUBE(n[mB])*mPbbb + + 3*w*(SQR(n[mA])*mPaa + 2*n[mA]*n[mB]*mPab + SQR(n[mB])*mPbb) + + w*w*(3*(n[mA]*mPa + n[mB]*mPb) + w*mP1)); + + mFaab = k1 * mPaab; + mFbbc = -k2 * (n[mA]*mPabb + n[mB]*mPbbb + w*mPbb); + mFcca = k3 * (SQR(n[mA])*mPaaa + 2*n[mA]*n[mB]*mPaab + SQR(n[mB])*mPabb + w*(2*(n[mA]*mPaa + n[mB]*mPab) + w*mPa)); + } + + /* + * This code computes volume integrals needed to compute mass properties of polyhedral bodies. + * Based on public domain code by David Eberly. + */ + + class VolumeIntegratorEberly + { + public: + VolumeIntegratorEberly(const PxConvexMeshDesc& mesh, PxF64 mDensity); + ~VolumeIntegratorEberly(); + bool computeVolumeIntegralsSIMD(PxIntegrals& ir, const PxVec3& origin); + bool computeVolumeIntegrals(PxIntegrals& ir, const PxVec3& origin); + + private: + VolumeIntegratorEberly& operator=(const VolumeIntegratorEberly&); + const PxConvexMeshDesc& mDesc; + PxF64 mMass; + PxReal mMassR; + PxF64 mDensity; + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Constructor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + VolumeIntegratorEberly::VolumeIntegratorEberly(const PxConvexMeshDesc& desc, PxF64 density) + : mDesc(desc), mMass(0), mMassR(0), mDensity(density) + { + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Destructor. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + VolumeIntegratorEberly::~VolumeIntegratorEberly() + { + } + + PX_FORCE_INLINE void subexpressions(PxF64 w0, PxF64 w1, PxF64 w2, PxF64& f1, PxF64& f2, PxF64& f3, PxF64& g0, PxF64& g1, PxF64& g2) + { + PxF64 temp0 = w0 + w1; + f1 = temp0 + w2; + PxF64 temp1 = w0*w0; + PxF64 temp2 = temp1 + w1*temp0; + f2 = temp2 + w2*f1; + f3 = w0*temp1 + w1*temp2 + w2*f2; + g0 = f2 + w0*(f1 + w0); + g1 = f2 + w1*(f1 + w1); + g2 = f2 + w2*(f1 + w2); + } + + PX_FORCE_INLINE void subexpressionsSIMD(const Vec4V& w0, const Vec4V& w1, const Vec4V& w2, + Vec4V& f1, Vec4V& f2, Vec4V& f3, Vec4V& g0, Vec4V& g1, Vec4V& g2) + { + const Vec4V temp0 = V4Add(w0, w1); + f1 = V4Add(temp0, w2); + const Vec4V temp1 = V4Mul(w0,w0); + const Vec4V temp2 = V4MulAdd(w1, temp0, temp1); + f2 = V4MulAdd(w2, f1, temp2); + + // f3 = w0.multiply(temp1) + w1.multiply(temp2) + w2.multiply(f2); + const Vec4V ad0 = V4Mul(w0, temp1); + const Vec4V ad1 = V4MulAdd(w1, temp2, ad0); + f3 = V4MulAdd(w2, f2, ad1); + + g0 = V4MulAdd(w0, V4Add(f1, w0), f2); // f2 + w0.multiply(f1 + w0); + g1 = V4MulAdd(w1, V4Add(f1, w1), f2); // f2 + w1.multiply(f1 + w1); + g2 = V4MulAdd(w2, V4Add(f1, w2), f2); // f2 + w2.multiply(f1 + w2); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes volume integrals for a polyhedron by summing surface integrals over its faces. SIMD version + * \param ir [out] a result structure. + * \param origin [in] the origin of the mesh vertices. All vertices will be shifted accordingly prior to computing the volume integrals. + Can improve accuracy, for example, if the centroid is used in the case of a convex mesh. Note: the returned inertia will not be relative to this origin but relative to (0,0,0). + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool VolumeIntegratorEberly::computeVolumeIntegralsSIMD(PxIntegrals& ir, const PxVec3& origin) + { + FloatV mult = FLoad(1.0f/6.0f); + const Vec4V multV = V4Load(1.0f/24.0f); + const Vec4V multV2 = V4Load(1.0f/60.0f); + const Vec4V multVV = V4Load(1.0f/120.0f); + + // order: 1, x, y, z, x^2, y^2, z^2, xy, yz, zx + FloatV intg = FLoad(0.0f); + Vec4V intgV = V4Load(0.0f); + Vec4V intgV2 = V4Load(0.0f); + Vec4V intgVV = V4Load(0.0f); + + const Vec4V originV = Vec4V_From_PxVec3_WUndefined(origin); + const FloatV zeroV = FLoad(0.0f); + + const PxVec3* hullVerts = static_cast (mDesc.points.data); + const Gu::HullPolygonData* hullPolygons = static_cast (mDesc.polygons.data); + + for (PxU32 i = 0; i < mDesc.polygons.count; i++) + { + const Gu::HullPolygonData& polygon = hullPolygons[i]; + const PxU8* data = static_cast(mDesc.indices.data) + polygon.mVRef8; + const PxU32 nbVerts = polygon.mNbVerts; + + PX_ASSERT(nbVerts > 2); + + const Vec4V normalV = V4LoadU(&polygon.mPlane.n.x); + + for (PxU32 j = 0; j < nbVerts - 2; j++) + { + // Should be safe to V4Load, we allocate one more vertex each time + const Vec4V vertex0 = V4LoadU(&hullVerts[data[0]].x); + const Vec4V vertex1 = V4LoadU(&hullVerts[data[j + 1]].x); + const Vec4V vertex2 = V4LoadU(&hullVerts[data[j + 2]].x); + + const Vec4V p0 = V4Sub(vertex0, originV); + Vec4V p1 = V4Sub(vertex1, originV); + Vec4V p2 = V4Sub(vertex2, originV); + + const Vec4V p0YZX = V4PermYZXW(p0); + const Vec4V p1YZX = V4PermYZXW(p1); + const Vec4V p2YZX = V4PermYZXW(p2); + + // get edges and cross product of edges + Vec4V d = V4Cross(V4Sub(p1, p0), V4Sub(p2, p0)); // (p1 - p0).cross(p2 - p0); + + const FloatV dist = V4Dot3(d, normalV); + //if(cp.dot(normalV) < 0) + if(FAllGrtr(zeroV, dist)) + { + d = V4Neg(d); + Vec4V temp = p1; + p1 = p2; + p2 = temp; + } + + // compute integral terms + Vec4V f1; Vec4V f2; Vec4V f3; Vec4V g0; Vec4V g1; Vec4V g2; + + subexpressionsSIMD(p0, p1, p2, f1, f2, f3, g0, g1, g2); + + // update integrals + intg = FScaleAdd(V4GetX(d), V4GetX(f1), intg); //intg += d.x*f1.x; + + intgV = V4MulAdd(d, f2, intgV); // intgV +=d.multiply(f2); + intgV2 = V4MulAdd(d, f3, intgV2); // intgV2 += d.multiply(f3); + + const Vec4V ad0 = V4Mul(p0YZX, g0); + const Vec4V ad1 = V4MulAdd(p1YZX, g1, ad0); + const Vec4V ad2 = V4MulAdd(p2YZX, g2, ad1); + intgVV = V4MulAdd(d, ad2, intgVV); //intgVV += d.multiply(p0YZX.multiply(g0) + p1YZX.multiply(g1) + p2YZX.multiply(g2)); + } + } + + intg = FMul(intg, mult); // intg *= mult; + intgV = V4Mul(intgV, multV); + intgV2 = V4Mul(intgV2, multV2); + intgVV = V4Mul(intgVV, multVV); + + // center of mass ir.COM = intgV/mMassR; + const Vec4V comV = V4ScaleInv(intgV, intg); + // we rewrite the mass, but then we set it back + V4StoreU(comV, &ir.COM.x); + + FStore(intg, &mMassR); + ir.mass = PxF64(mMassR); // = intg; + + PxVec3 intg2; + V3StoreU(Vec3V_From_Vec4V(intgV2), intg2); + + PxVec3 intVV; + V3StoreU(Vec3V_From_Vec4V(intgVV), intVV); + + // inertia tensor relative to the provided origin parameter + ir.inertiaTensor[0][0] = PxF64(intg2.y + intg2.z); + ir.inertiaTensor[1][1] = PxF64(intg2.x + intg2.z); + ir.inertiaTensor[2][2] = PxF64(intg2.x + intg2.y); + ir.inertiaTensor[0][1] = ir.inertiaTensor[1][0] = PxF64(-intVV.x); + ir.inertiaTensor[1][2] = ir.inertiaTensor[2][1] = PxF64(-intVV.y); + ir.inertiaTensor[0][2] = ir.inertiaTensor[2][0] = PxF64(-intVV.z); + + // inertia tensor relative to center of mass + ir.COMInertiaTensor[0][0] = ir.inertiaTensor[0][0] -PxF64(mMassR*(ir.COM.y*ir.COM.y+ir.COM.z*ir.COM.z)); + ir.COMInertiaTensor[1][1] = ir.inertiaTensor[1][1] -PxF64(mMassR*(ir.COM.z*ir.COM.z+ir.COM.x*ir.COM.x)); + ir.COMInertiaTensor[2][2] = ir.inertiaTensor[2][2] -PxF64(mMassR*(ir.COM.x*ir.COM.x+ir.COM.y*ir.COM.y)); + ir.COMInertiaTensor[0][1] = ir.COMInertiaTensor[1][0] = (ir.inertiaTensor[0][1] +PxF64(mMassR*ir.COM.x*ir.COM.y)); + ir.COMInertiaTensor[1][2] = ir.COMInertiaTensor[2][1] = (ir.inertiaTensor[1][2] +PxF64(mMassR*ir.COM.y*ir.COM.z)); + ir.COMInertiaTensor[0][2] = ir.COMInertiaTensor[2][0] = (ir.inertiaTensor[0][2] +PxF64(mMassR*ir.COM.z*ir.COM.x)); + + // inertia tensor relative to (0,0,0) + if (!origin.isZero()) + { + PxVec3 sum = ir.COM + origin; + ir.inertiaTensor[0][0] -= PxF64(mMassR*((ir.COM.y*ir.COM.y+ir.COM.z*ir.COM.z) - (sum.y*sum.y+sum.z*sum.z))); + ir.inertiaTensor[1][1] -= PxF64(mMassR*((ir.COM.z*ir.COM.z+ir.COM.x*ir.COM.x) - (sum.z*sum.z+sum.x*sum.x))); + ir.inertiaTensor[2][2] -= PxF64(mMassR*((ir.COM.x*ir.COM.x+ir.COM.y*ir.COM.y) - (sum.x*sum.x+sum.y*sum.y))); + ir.inertiaTensor[0][1] = ir.inertiaTensor[1][0] = ir.inertiaTensor[0][1] + PxF64(mMassR*((ir.COM.x*ir.COM.y) - (sum.x*sum.y))); + ir.inertiaTensor[1][2] = ir.inertiaTensor[2][1] = ir.inertiaTensor[1][2] + PxF64(mMassR*((ir.COM.y*ir.COM.z) - (sum.y*sum.z))); + ir.inertiaTensor[0][2] = ir.inertiaTensor[2][0] = ir.inertiaTensor[0][2] + PxF64(mMassR*((ir.COM.z*ir.COM.x) - (sum.z*sum.x))); + ir.COM = sum; + } + + return true; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes volume integrals for a polyhedron by summing surface integrals over its faces. + * \param ir [out] a result structure. + * \param origin [in] the origin of the mesh vertices. All vertices will be shifted accordingly prior to computing the volume integrals. + Can improve accuracy, for example, if the centroid is used in the case of a convex mesh. Note: the returned inertia will not be relative to this origin but relative to (0,0,0). + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool VolumeIntegratorEberly::computeVolumeIntegrals(PxIntegrals& ir, const PxVec3& origin) + { + const PxF64 mult[10] = {1.0/6.0,1.0/24.0,1.0/24.0,1.0/24.0,1.0/60.0,1.0/60.0,1.0/60.0,1.0/120.0,1.0/120.0,1.0/120.0}; + PxF64 intg[10] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; // order: 1, x, y, z, x^2, y^2, z^2, xy, yz, zx + const PxVec3* hullVerts = static_cast (mDesc.points.data); + + for (PxU32 i = 0; i < mDesc.polygons.count; i++) + { + const Gu::HullPolygonData& polygon = (static_cast (mDesc.polygons.data))[i]; + const PxU8* Data = static_cast(mDesc.indices.data) + polygon.mVRef8; + const PxU32 NbVerts = polygon.mNbVerts; + for (PxU32 j = 0; j < NbVerts - 2; j++) + { + const PxVec3 p0 = hullVerts[Data[0]] - origin; + PxVec3 p1 = hullVerts[Data[(j + 1) % NbVerts]] - origin; + PxVec3 p2 = hullVerts[Data[(j + 2) % NbVerts]] - origin; + + PxVec3 cp = (p1 - p0).cross(p2 - p0); + + if(cp.dot(polygon.mPlane.n) < 0) + { + cp = -cp; + Ps::swap(p1,p2); + } + + PxF64 x0 = PxF64(p0.x); PxF64 y0 = PxF64(p0.y); PxF64 z0 = PxF64(p0.z); + PxF64 x1 = PxF64(p1.x); PxF64 y1 = PxF64(p1.y); PxF64 z1 = PxF64(p1.z); + PxF64 x2 = PxF64(p2.x); PxF64 y2 = PxF64(p2.y); PxF64 z2 = PxF64(p2.z); + + // get edges and cross product of edges + PxF64 d0 = PxF64(cp.x); PxF64 d1 = PxF64(cp.y); PxF64 d2 = PxF64(cp.z); + + // compute integral terms + PxF64 f1x; PxF64 f2x; PxF64 f3x; PxF64 g0x; PxF64 g1x; PxF64 g2x; + PxF64 f1y; PxF64 f2y; PxF64 f3y; PxF64 g0y; PxF64 g1y; PxF64 g2y; + PxF64 f1z; PxF64 f2z; PxF64 f3z; PxF64 g0z; PxF64 g1z; PxF64 g2z; + + subexpressions(x0, x1, x2, f1x, f2x, f3x, g0x, g1x, g2x); + subexpressions(y0, y1, y2, f1y, f2y, f3y, g0y, g1y, g2y); + subexpressions(z0, z1, z2, f1z, f2z, f3z, g0z, g1z, g2z); + + // update integrals + intg[0] += d0*f1x; + intg[1] += d0*f2x; intg[2] += d1*f2y; intg[3] += d2*f2z; + intg[4] += d0*f3x; intg[5] += d1*f3y; intg[6] += d2*f3z; + intg[7] += d0*(y0*g0x + y1*g1x + y2*g2x); + intg[8] += d1*(z0*g0y + z1*g1y + z2*g2y); + intg[9] += d2*(x0*g0z + x1*g1z + x2*g2z); + + } + } + + for (PxU32 i = 0; i < 10; i++) + { + intg[i] *= mult[i]; + } + + ir.mass = mMass = intg[0]; + // center of mass + ir.COM.x = PxReal(intg[1]/mMass); + ir.COM.y = PxReal(intg[2]/mMass); + ir.COM.z = PxReal(intg[3]/mMass); + + // inertia tensor relative to the provided origin parameter + ir.inertiaTensor[0][0] = intg[5]+intg[6]; + ir.inertiaTensor[1][1] = intg[4]+intg[6]; + ir.inertiaTensor[2][2] = intg[4]+intg[5]; + ir.inertiaTensor[0][1] = ir.inertiaTensor[1][0] = -intg[7]; + ir.inertiaTensor[1][2] = ir.inertiaTensor[2][1] = -intg[8]; + ir.inertiaTensor[0][2] = ir.inertiaTensor[2][0] = -intg[9]; + + // inertia tensor relative to center of mass + ir.COMInertiaTensor[0][0] = ir.inertiaTensor[0][0] -mMass*PxF64((ir.COM.y*ir.COM.y+ir.COM.z*ir.COM.z)); + ir.COMInertiaTensor[1][1] = ir.inertiaTensor[1][1] -mMass*PxF64((ir.COM.z*ir.COM.z+ir.COM.x*ir.COM.x)); + ir.COMInertiaTensor[2][2] = ir.inertiaTensor[2][2] -mMass*PxF64((ir.COM.x*ir.COM.x+ir.COM.y*ir.COM.y)); + ir.COMInertiaTensor[0][1] = ir.COMInertiaTensor[1][0] = (ir.inertiaTensor[0][1] +mMass*PxF64(ir.COM.x*ir.COM.y)); + ir.COMInertiaTensor[1][2] = ir.COMInertiaTensor[2][1] = (ir.inertiaTensor[1][2] +mMass*PxF64(ir.COM.y*ir.COM.z)); + ir.COMInertiaTensor[0][2] = ir.COMInertiaTensor[2][0] = (ir.inertiaTensor[0][2] +mMass*PxF64(ir.COM.z*ir.COM.x)); + + // inertia tensor relative to (0,0,0) + if (!origin.isZero()) + { + PxVec3 sum = ir.COM + origin; + ir.inertiaTensor[0][0] -= mMass*PxF64((ir.COM.y*ir.COM.y+ir.COM.z*ir.COM.z) - (sum.y*sum.y+sum.z*sum.z)); + ir.inertiaTensor[1][1] -= mMass*PxF64((ir.COM.z*ir.COM.z+ir.COM.x*ir.COM.x) - (sum.z*sum.z+sum.x*sum.x)); + ir.inertiaTensor[2][2] -= mMass*PxF64((ir.COM.x*ir.COM.x+ir.COM.y*ir.COM.y) - (sum.x*sum.x+sum.y*sum.y)); + ir.inertiaTensor[0][1] = ir.inertiaTensor[1][0] = ir.inertiaTensor[0][1] + mMass*PxF64((ir.COM.x*ir.COM.y) - (sum.x*sum.y)); + ir.inertiaTensor[1][2] = ir.inertiaTensor[2][1] = ir.inertiaTensor[1][2] + mMass*PxF64((ir.COM.y*ir.COM.z) - (sum.y*sum.z)); + ir.inertiaTensor[0][2] = ir.inertiaTensor[2][0] = ir.inertiaTensor[0][2] + mMass*PxF64((ir.COM.z*ir.COM.x) - (sum.z*sum.x)); + ir.COM = sum; + } + + return true; + } +} // namespace + +// Wrapper +bool computeVolumeIntegrals(const PxSimpleTriangleMesh& mesh, PxReal density, PxIntegrals& integrals) +{ + VolumeIntegrator v(mesh, PxF64(density)); + return v.computeVolumeIntegrals(integrals); +} + +// Wrapper +bool computeVolumeIntegralsEberly(const PxConvexMeshDesc& mesh, PxReal density, PxIntegrals& integrals, const PxVec3& origin) +{ + VolumeIntegratorEberly v(mesh, PxF64(density)); + v.computeVolumeIntegrals(integrals, origin); + return true; +} + +// Wrapper +bool computeVolumeIntegralsEberlySIMD(const PxConvexMeshDesc& mesh, PxReal density, PxIntegrals& integrals, const PxVec3& origin) +{ + VolumeIntegratorEberly v(mesh, PxF64(density)); + v.computeVolumeIntegralsSIMD(integrals, origin); + return true; +} + +} + +//#endif diff --git a/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h new file mode 100644 index 000000000..b8ae55beb --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h @@ -0,0 +1,102 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_FOUNDATION_NXVOLUMEINTEGRATION +#define PX_FOUNDATION_NXVOLUMEINTEGRATION +/** \addtogroup foundation + @{ +*/ + + +#include "foundation/Px.h" +#include "foundation/PxVec3.h" +#include "foundation/PxMat33.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +class PxSimpleTriangleMesh; +class PxConvexMeshDesc; + +/** +\brief Data structure used to store mass properties. +*/ +struct PxIntegrals + { + PxVec3 COM; //!< Center of mass + PxF64 mass; //!< Total mass + PxF64 inertiaTensor[3][3]; //!< Inertia tensor (mass matrix) relative to the origin + PxF64 COMInertiaTensor[3][3]; //!< Inertia tensor (mass matrix) relative to the COM + + /** + \brief Retrieve the inertia tensor relative to the center of mass. + + \param inertia Inertia tensor. + */ + void getInertia(PxMat33& inertia) + { + for(PxU32 j=0;j<3;j++) + { + for(PxU32 i=0;i<3;i++) + { + inertia(i,j) = PxF32(COMInertiaTensor[i][j]); + } + } + } + + /** + \brief Retrieve the inertia tensor relative to the origin. + + \param inertia Inertia tensor. + */ + void getOriginInertia(PxMat33& inertia) + { + for(PxU32 j=0;j<3;j++) + { + for(PxU32 i=0;i<3;i++) + { + inertia(i,j) = PxF32(inertiaTensor[i][j]); + } + } + } + }; + + bool computeVolumeIntegrals(const PxSimpleTriangleMesh& mesh, PxReal density, PxIntegrals& integrals); + + // specialized method taking polygons directly, so we don't need to compute and store triangles for each polygon + bool computeVolumeIntegralsEberly(const PxConvexMeshDesc& mesh, PxReal density, PxIntegrals& integrals, const PxVec3& origin); // Eberly simplified method + + // specialized method taking polygons directly, so we don't need to compute and store triangles for each polygon, SIMD version + bool computeVolumeIntegralsEberlySIMD(const PxConvexMeshDesc& mesh, PxReal density, PxIntegrals& integrals, const PxVec3& origin); // Eberly simplified method +} + + /** @} */ +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp new file mode 100644 index 000000000..c0f4beeea --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp @@ -0,0 +1,29 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h new file mode 100644 index 000000000..5cc742b7a --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h @@ -0,0 +1,337 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_GRBTRIANGLEMESHCOOKING +#define PX_COLLISION_GRBTRIANGLEMESHCOOKING + +#include "GuMeshData.h" +#include "cooking/PxCooking.h" + +namespace physx +{ + namespace Gu + { + + } + +// TODO avoroshilov: remove duplicate definitions +static const PxU32 BOUNDARY = 0xffffffff; +static const PxU32 NONCONVEX_FLAG = 0x80000000; + +struct EdgeTriLookup +{ + PxU32 edgeId0, edgeId1; + PxU32 triId; + + bool operator < (const EdgeTriLookup& edge1) const + { + return edgeId0 < edge1.edgeId0 || (edgeId0 == edge1.edgeId0 && edgeId1 < edge1.edgeId1); + } + + bool operator <=(const EdgeTriLookup& edge1) const + { + return edgeId0 < edge1.edgeId0 || (edgeId0 == edge1.edgeId0 && edgeId1 <= edge1.edgeId1); + } +}; + + +static PxU32 binarySearch(const EdgeTriLookup* __restrict data, const PxU32 numElements, const EdgeTriLookup& value) +{ + PxU32 left = 0; + PxU32 right = numElements; + + while ((right - left) > 1) + { + PxU32 pos = (left + right) / 2; + const EdgeTriLookup& element = data[pos]; + if (element <= value) + { + left = pos; + } + else + { + right = pos; + } + } + + return left; +} + +// slightly different behavior from collide2: boundary edges are filtered out + +static PxU32 findAdjacent(const PxVec3* triVertices, const PxVec3* triNormals, const uint3* triIndices, + PxU32 nbTris, PxU32 i0, PxU32 i1, const PxPlane& plane, + EdgeTriLookup* triLookups, PxU32 triangleIndex) +{ + PxU32 result = BOUNDARY; + PxReal bestCos = -FLT_MAX; + + EdgeTriLookup lookup; + lookup.edgeId0 = PxMin(i0, i1); + lookup.edgeId1 = PxMax(i0, i1); + + PxU32 startIndex = binarySearch(triLookups, nbTris * 3, lookup); + + for (PxU32 a = startIndex; a > 0; --a) + { + if (triLookups[a - 1].edgeId0 == lookup.edgeId0 && triLookups[a - 1].edgeId1 == lookup.edgeId1) + startIndex = a - 1; + else + break; + } + + for (PxU32 a = startIndex; a < nbTris * 3; ++a) + { + EdgeTriLookup& edgeTri = triLookups[a]; + + if (edgeTri.edgeId0 != lookup.edgeId0 || edgeTri.edgeId1 != lookup.edgeId1) + break; + + if (edgeTri.triId == triangleIndex) + continue; + + const uint3& triIdx = triIndices[edgeTri.triId]; + PxU32 vIdx0 = triIdx.x; + PxU32 vIdx1 = triIdx.y; + PxU32 vIdx2 = triIdx.z; + + PxU32 other = vIdx0 + vIdx1 + vIdx2 - (i0 + i1); + + if (plane.distance(triVertices[other]) >= 0) + return NONCONVEX_FLAG | edgeTri.triId; + + PxReal c = plane.n.dot(triNormals[edgeTri.triId]); + if (c>bestCos) + { + bestCos = c; + result = edgeTri.triId; + } + + } + + return result; +} + + +static void buildAdjacencies(uint4* triAdjacencies, PxVec3* tempNormalsPerTri_prealloc, const PxVec3* triVertices, const uint3* triIndices, PxU32 nbTris) +{ + //PxVec3 * triNormals = new PxVec3[nbTris]; + + EdgeTriLookup* edgeLookups = reinterpret_cast(PX_ALLOC(sizeof(EdgeTriLookup) * nbTris * 3, PX_DEBUG_EXP("edgeLookups"))); + + + for (PxU32 i = 0; i < nbTris; i++) + { + const uint3& triIdx = triIndices[i]; + PxU32 vIdx0 = triIdx.x; + PxU32 vIdx1 = triIdx.y; + PxU32 vIdx2 = triIdx.z; + + tempNormalsPerTri_prealloc[i] = (triVertices[vIdx1] - triVertices[vIdx0]).cross(triVertices[vIdx2] - triVertices[vIdx0]).getNormalized(); + + edgeLookups[i * 3].edgeId0 = PxMin(vIdx0, vIdx1); + edgeLookups[i * 3].edgeId1 = PxMax(vIdx0, vIdx1); + edgeLookups[i * 3].triId = i; + + edgeLookups[i * 3 + 1].edgeId0 = PxMin(vIdx1, vIdx2); + edgeLookups[i * 3 + 1].edgeId1 = PxMax(vIdx1, vIdx2); + edgeLookups[i * 3 + 1].triId = i; + + edgeLookups[i * 3 + 2].edgeId0 = PxMin(vIdx0, vIdx2); + edgeLookups[i * 3 + 2].edgeId1 = PxMax(vIdx0, vIdx2); + edgeLookups[i * 3 + 2].triId = i; + } + + Ps::sort(edgeLookups, PxU32(nbTris * 3)); + + for (PxU32 i = 0; i < nbTris; i++) + { + const uint3& triIdx = triIndices[i]; + PxU32 vIdx0 = triIdx.x; + PxU32 vIdx1 = triIdx.y; + PxU32 vIdx2 = triIdx.z; + + PxPlane triPlane(triVertices[vIdx0], tempNormalsPerTri_prealloc[i]); + uint4 triAdjIdx; + + triAdjIdx.x = findAdjacent(triVertices, tempNormalsPerTri_prealloc, triIndices, nbTris, vIdx0, vIdx1, triPlane, edgeLookups, i); + triAdjIdx.y = findAdjacent(triVertices, tempNormalsPerTri_prealloc, triIndices, nbTris, vIdx1, vIdx2, triPlane, edgeLookups, i); + triAdjIdx.z = findAdjacent(triVertices, tempNormalsPerTri_prealloc, triIndices, nbTris, vIdx2, vIdx0, triPlane, edgeLookups, i); + triAdjIdx.w = 0; + + triAdjacencies[i] = triAdjIdx; + } + + + PX_FREE(edgeLookups); +} + +static bool isEdgeNonconvex(PxU32 adjEdgeIndex) +{ + return (adjEdgeIndex != BOUNDARY) && (adjEdgeIndex & NONCONVEX_FLAG); +} + +PX_INLINE void buildVertexConnection_p1(PxU32 * vertValency, PxU32 * vertNeighborStart, PxU32 & tempNumAdjVertices, const float4 * /*triVertices*/, const uint4 * triIndices, const uint4 * triAdjacencies, PxU32 nbVerts, PxU32 nbTris) +{ + tempNumAdjVertices = 0; + memset(vertValency, 0, nbVerts*sizeof(PxU32)); + + // Calculate max num of adjVerts + for (PxU32 i = 0; i < nbTris; i++) + { + uint4 triIdx = triIndices[i]; + PxU32 vi0 = triIdx.x; + PxU32 vi1 = triIdx.y; + PxU32 vi2 = triIdx.z; + + uint4 triAdjIdx = triAdjacencies[i]; + + PxU32 totalVertsAdded = 0; + if (!isEdgeNonconvex(triAdjIdx.x)) + { + ++vertValency[vi0]; + ++vertValency[vi1]; + totalVertsAdded += 2; + } + if (!isEdgeNonconvex(triAdjIdx.y)) + { + ++vertValency[vi1]; + ++vertValency[vi2]; + totalVertsAdded += 2; + } + if (!isEdgeNonconvex(triAdjIdx.z)) + { + ++vertValency[vi2]; + ++vertValency[vi0]; + totalVertsAdded += 2; + } + tempNumAdjVertices += totalVertsAdded; + } + PxU32 offset = 0; + for (PxU32 i = 0; i < nbVerts; i++) + { + vertNeighborStart[i] = offset; + offset += vertValency[i]; + } + + memset(vertValency, 0, nbVerts*sizeof(PxU32)); +} + +PX_INLINE PxU32 buildVertexConnection_p2(PxU32 * vertValency, PxU32 * vertNeighborStart, PxU32 * vertNeighboringPairs_prealloc, PxU32 tempNumAdjVertices, const float4 * /*triVertices*/, const uint4 * triIndices, const uint4 * triAdjacencies, PxU32 /*nbVerts*/, PxU32 nbTris) +{ + memset(vertNeighboringPairs_prealloc, 0xff, tempNumAdjVertices*2*sizeof(PxU32)); + + PxU32 newAdjVertsNum = 0; + for (PxU32 i = 0; i < nbTris; i++) + { + uint4 triIdx = triIndices[i]; + PxU32 vi[3] = + { + triIdx.x, + triIdx.y, + triIdx.z + }; + uint4 triAdjIdx = triAdjacencies[i]; + PxU32 ta[3] = + { + triAdjIdx.x, + triAdjIdx.y, + triAdjIdx.z + }; + + for (int tvi = 0; tvi < 3; ++tvi) + { + PxU32 curIdx = vi[tvi]; + PxU32 nextIdx = vi[(tvi+1)%3]; + if (!isEdgeNonconvex(ta[tvi])) + { + bool matchFound = false; + for (PxU32 valIdx = vertNeighborStart[curIdx], valIdxEnd = vertNeighborStart[curIdx] + vertValency[curIdx]; valIdx < valIdxEnd; ++valIdx) + { + if (vertNeighboringPairs_prealloc[valIdx*2+1] == nextIdx) + { + matchFound = true; + break; + } + } + + if (!matchFound) + { + PxU32 curPairIdx; + + curPairIdx = vertNeighborStart[curIdx] + vertValency[curIdx]; + vertNeighboringPairs_prealloc[curPairIdx*2+0] = curIdx; + vertNeighboringPairs_prealloc[curPairIdx*2+1] = nextIdx; + ++vertValency[curIdx]; + + curPairIdx = vertNeighborStart[nextIdx] + vertValency[nextIdx]; + vertNeighboringPairs_prealloc[curPairIdx*2+0] = nextIdx; + vertNeighboringPairs_prealloc[curPairIdx*2+1] = curIdx; + ++vertValency[nextIdx]; + + newAdjVertsNum += 2; + } + } + } + } + + return newAdjVertsNum; +} + +PX_INLINE void buildVertexConnection_p3(PxU32 * vertNeighbors, PxU32 * /*vertValency*/, PxU32 * vertNeighborStart, PxU32 * vertNeighboringPairs_prealloc, PxU32 tempNumAdjVertices, PxU32 newNumAdjVertices, const float4 * /*triVertices*/, const uint4 * /*triIndices*/, const uint4 * /*triAdjacencies*/, PxU32 /*nbVerts*/, PxU32 /*nbTris*/) +{ + PX_UNUSED(newNumAdjVertices); + PxU32 prevVertex = 0xFFffFFff; + PxU32 writingIndex = 0; + for (PxU32 i = 0; i < tempNumAdjVertices; ++i) + { + PxU32 curPairIdx0 = vertNeighboringPairs_prealloc[i*2+0]; + if (curPairIdx0 == 0xFFffFFff) + { + continue; + } + + PxU32 curPairIdx1 = vertNeighboringPairs_prealloc[i*2+1]; + vertNeighbors[writingIndex] = curPairIdx1; + if (curPairIdx0 != prevVertex) + { + vertNeighborStart[curPairIdx0] = writingIndex; + } + prevVertex = curPairIdx0; + + ++writingIndex; + } + + PX_ASSERT(writingIndex == newNumAdjVertices); +} + +} // namespace physx + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp new file mode 100644 index 000000000..2b6056b83 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp @@ -0,0 +1,84 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "GuHeightField.h" +#include "GuSerialize.h" + +using namespace physx; +using namespace Gu; + +namespace physx +{ + +bool saveHeightField(const HeightField& hf, PxOutputStream& stream, bool endian) +{ + // write header + if(!writeHeader('H', 'F', 'H', 'F', PX_HEIGHTFIELD_VERSION, endian, stream)) + return false; + + const Gu::HeightFieldData& hfData = hf.getData(); + + // write mData members + writeDword(hfData.rows, endian, stream); + writeDword(hfData.columns, endian, stream); + writeFloat(hfData.rowLimit, endian, stream); + writeFloat(hfData.colLimit, endian, stream); + writeFloat(hfData.nbColumns, endian, stream); + writeFloat(0.0f, endian, stream); // thickness + writeFloat(hfData.convexEdgeThreshold, endian, stream); + writeWord(hfData.flags, endian, stream); + writeDword(hfData.format, endian, stream); + + writeFloat(hfData.mAABB.getMin(0), endian, stream); + writeFloat(hfData.mAABB.getMin(1), endian, stream); + writeFloat(hfData.mAABB.getMin(2), endian, stream); + writeFloat(hfData.mAABB.getMax(0), endian, stream); + writeFloat(hfData.mAABB.getMax(1), endian, stream); + writeFloat(hfData.mAABB.getMax(2), endian, stream); + + // write this-> members + writeDword(hf.mSampleStride, endian, stream); + writeDword(hf.mNbSamples, endian, stream); + writeFloat(hf.mMinHeight, endian, stream); + writeFloat(hf.mMaxHeight, endian, stream); + + // write samples + for(PxU32 i = 0; i < hf.mNbSamples; i++) + { + const PxHeightFieldSample& s = hfData.samples[i]; + writeWord(PxU16(s.height), endian, stream); + stream.write(&s.materialIndex0, sizeof(s.materialIndex0)); + stream.write(&s.materialIndex1, sizeof(s.materialIndex1)); + } + + return true; +} + +} diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h new file mode 100644 index 000000000..e3d5ef89f --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h @@ -0,0 +1,35 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "GuHeightField.h" + +namespace physx +{ + bool saveHeightField(const Gu::HeightField& hf, PxOutputStream& stream, bool endianSwap); +} diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp new file mode 100644 index 000000000..4da4ad3af --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "MeshBuilder.h" +#include "GuInternal.h" + +namespace physx +{ + //#define PROFILE_BOUNDS +#ifdef PROFILE_BOUNDS +#include +#pragma comment(lib, "winmm.lib") +#endif + + void MeshBulider::computeLocalBounds(Gu::MeshDataBase& meshData) + { +#ifdef PROFILE_BOUNDS + int time = timeGetTime(); +#endif + + PxBounds3& localBounds = meshData.mAABB; + Gu::computeBoundsAroundVertices(localBounds, meshData.mNbVertices, meshData.mVertices); + + // Derive a good geometric epsilon from local bounds. We must do this before bounds extrusion for heightfields. + // + // From Charles Bloom: + // "Epsilon must be big enough so that the consistency condition abs(D(Hit)) + // <= Epsilon is satisfied for all queries. You want the smallest epsilon + // you can have that meets that constraint. Normal floats have a 24 bit + // mantissa. When you do any float addition, you may have round-off error + // that makes the result off by roughly 2^-24 * result. Our result is + // scaled by the position values. If our world is strictly required to be + // in a box of world size W (each coordinate in -W to W), then the maximum + // error is 2^-24 * W. Thus Epsilon must be at least >= 2^-24 * W. If + // you're doing coordinate transforms, that may scale your error up by some + // amount, so you'll need a bigger epsilon. In general something like + // 2^-22*W is reasonable. If you allow scaled transforms, it needs to be + // something like 2^-22*W*MAX_SCALE." + // PT: TODO: runtime checkings for this + PxReal geomEpsilon = 0.0f; + for (PxU32 i = 0; i < 3; i++) + geomEpsilon = PxMax(geomEpsilon, PxMax(PxAbs(localBounds.maximum[i]), PxAbs(localBounds.minimum[i]))); + geomEpsilon *= powf(2.0f, -22.0f); + meshData.mGeomEpsilon = geomEpsilon; + +#ifdef PROFILE_BOUNDS + int deltaTime = timeGetTime() - time; + printf("Bounds time: %f\n", float(deltaTime)*0.001f); +#endif + } + +} diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h new file mode 100644 index 000000000..b7cb901c4 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h @@ -0,0 +1,45 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_MESH_BUILDER +#define PX_COLLISION_MESH_BUILDER + +#include "GuMeshData.h" + +namespace physx +{ + class MeshBulider + { + protected: + void computeLocalBounds(Gu::MeshDataBase& meshData); + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h b/src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h new file mode 100644 index 000000000..e108f701a --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h @@ -0,0 +1,114 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef QUICKSELECT_H +#define QUICKSELECT_H + +#include "foundation/PxSimpleTypes.h" + +// Google "wikipedia QuickSelect" for algorithm explanation +namespace physx { namespace quickSelect { + + + #define SWAP32(x, y) { PxU32 tmp = y; y = x; x = tmp; } + + // left is the index of the leftmost element of the subarray + // right is the index of the rightmost element of the subarray (inclusive) + // number of elements in subarray = right-left+1 + template + PxU32 partition(PxU32* PX_RESTRICT a, PxU32 left, PxU32 right, PxU32 pivotIndex, const LtEq& cmpLtEq) + { + PX_ASSERT(pivotIndex >= left && pivotIndex <= right); + PxU32 pivotValue = a[pivotIndex]; + SWAP32(a[pivotIndex], a[right]) // Move pivot to end + PxU32 storeIndex = left; + for (PxU32 i = left; i < right; i++) // left <= i < right + if (cmpLtEq(a[i], pivotValue)) + { + SWAP32(a[i], a[storeIndex]); + storeIndex++; + } + SWAP32(a[storeIndex], a[right]); // Move pivot to its final place + for (PxU32 i = left; i < storeIndex; i++) + PX_ASSERT(cmpLtEq(a[i], a[storeIndex])); + for (PxU32 i = storeIndex+1; i <= right; i++) + PX_ASSERT(cmpLtEq(a[storeIndex], a[i])); + return storeIndex; + } + + // left is the index of the leftmost element of the subarray + // right is the index of the rightmost element of the subarray (inclusive) + // number of elements in subarray = right-left+1 + // recursive version + template + void quickFindFirstK(PxU32* PX_RESTRICT a, PxU32 left, PxU32 right, PxU32 k, const LtEq& cmpLtEq) + { + PX_ASSERT(k <= right-left+1); + if (right > left) + { + // select pivotIndex between left and right + PxU32 pivotIndex = (left + right) >> 1; + PxU32 pivotNewIndex = partition(a, left, right, pivotIndex, cmpLtEq); + // now all elements to the left of pivotNewIndex are < old value of a[pivotIndex] (bottom half values) + if (pivotNewIndex > left + k) // new condition + quickFindFirstK(a, left, pivotNewIndex-1, k, cmpLtEq); + if (pivotNewIndex < left + k) + quickFindFirstK(a, pivotNewIndex+1, right, k+left-pivotNewIndex-1, cmpLtEq); + } + } + + // non-recursive version + template + void quickSelectFirstK(PxU32* PX_RESTRICT a, PxU32 left, PxU32 right, PxU32 k, const LtEq& cmpLtEq) + { + PX_ASSERT(k <= right-left+1); + for (;;) + { + PxU32 pivotIndex = (left+right) >> 1; + PxU32 pivotNewIndex = partition(a, left, right, pivotIndex, cmpLtEq); + PxU32 pivotDist = pivotNewIndex - left + 1; + if (pivotDist == k) + return; + else if (k < pivotDist) + { + PX_ASSERT(pivotNewIndex > 0); + right = pivotNewIndex - 1; + } + else + { + k = k - pivotDist; + left = pivotNewIndex+1; + } + } + } + +} } // namespace quickSelect, physx + +#endif + diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp new file mode 100644 index 000000000..f42a1c3c3 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp @@ -0,0 +1,894 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxBounds3.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "RTreeCooking.h" +#include "PsSort.h" +#include "PsMathUtils.h" +#include "PsAllocator.h" +#include "PsVecMath.h" +#include "PxTolerancesScale.h" +#include "QuickSelect.h" +#include "PsInlineArray.h" +#include "GuRTree.h" + +#define PRINT_RTREE_COOKING_STATS 0 // AP: keeping this frequently used macro for diagnostics/benchmarking + +#if PRINT_RTREE_COOKING_STATS +#include +#endif + +using namespace physx::Gu; +using namespace physx::shdfnd; +using namespace physx::shdfnd::aos; + +namespace physx +{ + +// Intermediate non-quantized representation for RTree node in a page (final format is SIMD transposed page) +struct RTreeNodeNQ +{ + PxBounds3 bounds; + PxI32 childPageFirstNodeIndex; // relative to the beginning of all build tree nodes array + PxI32 leafCount; // -1 for empty nodes, 0 for non-terminal nodes, number of enclosed tris if non-zero (LeafTriangles), also means a terminal node + + struct U {}; // selector struct for uninitialized constructor + RTreeNodeNQ(U) {} // uninitialized constructor + RTreeNodeNQ() : bounds(PxBounds3::empty()), childPageFirstNodeIndex(-1), leafCount(0) {} +}; + +// SIMD version of bounds class +struct PxBounds3V +{ + struct U {}; // selector struct for uninitialized constructor + Vec3V mn, mx; + PxBounds3V(Vec3VArg mn_, Vec3VArg mx_) : mn(mn_), mx(mx_) {} + PxBounds3V(U) {} // uninitialized constructor + + PX_FORCE_INLINE Vec3V getExtents() const { return V3Sub(mx, mn); } + PX_FORCE_INLINE void include(const PxBounds3V& other) { mn = V3Min(mn, other.mn); mx = V3Max(mx, other.mx); } + + // convert vector extents to PxVec3 + PX_FORCE_INLINE const PxVec3 getMinVec3() const { PxVec3 ret; V3StoreU(mn, ret); return ret; } + PX_FORCE_INLINE const PxVec3 getMaxVec3() const { PxVec3 ret; V3StoreU(mx, ret); return ret; } +}; + +static void buildFromBounds( + Gu::RTree& resultTree, const PxBounds3V* allBounds, PxU32 numBounds, + Array& resultPermute, RTreeCooker::RemapCallback* rc, Vec3VArg allMn, Vec3VArg allMx, + PxReal sizePerfTradeOff, PxMeshCookingHint::Enum hint); + +///////////////////////////////////////////////////////////////////////// +void RTreeCooker::buildFromTriangles( + Gu::RTree& result, const PxVec3* verts, PxU32 numVerts, const PxU16* tris16, const PxU32* tris32, PxU32 numTris, + Array& resultPermute, RTreeCooker::RemapCallback* rc, PxReal sizePerfTradeOff01, PxMeshCookingHint::Enum hint) +{ + PX_UNUSED(numVerts); + Array allBounds; + allBounds.reserve(numTris); + Vec3V allMn = Vec3V_From_FloatV(FMax()), allMx = Vec3V_From_FloatV(FNegMax()); + Vec3V eps = V3Splat(FLoad(5e-4f)); // AP scaffold: use PxTolerancesScale here? + + // build RTree AABB bounds from triangles, conservative bound inflation is also performed here + for(PxU32 i = 0; i < numTris; i ++) + { + PxU32 i0, i1, i2; + PxU32 i3 = i*3; + if(tris16) + { + i0 = tris16[i3]; i1 = tris16[i3+1]; i2 = tris16[i3+2]; + } else + { + i0 = tris32[i3]; i1 = tris32[i3+1]; i2 = tris32[i3+2]; + } + PX_ASSERT_WITH_MESSAGE(i0 < numVerts && i1 < numVerts && i2 < numVerts ,"Input mesh triangle's vertex index exceeds specified numVerts."); + Vec3V v0 = V3LoadU(verts[i0]), v1 = V3LoadU(verts[i1]), v2 = V3LoadU(verts[i2]); + Vec3V mn = V3Sub(V3Min(V3Min(v0, v1), v2), eps); // min over 3 verts, subtract eps to inflate + Vec3V mx = V3Add(V3Max(V3Max(v0, v1), v2), eps); // max over 3 verts, add eps to inflate + allMn = V3Min(allMn, mn); allMx = V3Max(allMx, mx); + allBounds.pushBack(PxBounds3V(mn, mx)); + } + + buildFromBounds(result, allBounds.begin(), numTris, resultPermute, rc, allMn, allMx, sizePerfTradeOff01, hint); +} + +///////////////////////////////////////////////////////////////////////// +// Fast but lower quality 4-way split sorting using repeated application of quickselect + +// comparator template struct for sortin gbounds centers given a coordinate index (x,y,z=0,1,2) +struct BoundsLTE +{ + PxU32 coordIndex; + const PxVec3* PX_RESTRICT boundCenters; // AP: precomputed centers are faster than recomputing the centers + BoundsLTE(PxU32 coordIndex_, const PxVec3* boundCenters_) + : coordIndex(coordIndex_), boundCenters(boundCenters_) + {} + + PX_FORCE_INLINE bool operator()(const PxU32 & idx1, const PxU32 & idx2) const + { + PxF32 center1 = boundCenters[idx1][coordIndex]; + PxF32 center2 = boundCenters[idx2][coordIndex]; + return (center1 <= center2); + } +}; + +// ====================================================================== +// Quick sorting method +// recursive sorting procedure: +// 1. find min and max extent along each axis for the current cluster +// 2. split input cluster into two 3 times using quickselect, splitting off a quarter of the initial cluster size each time +// 3. the axis is potentialy different for each split using the following +// approximate splitting heuristic - reduce max length by some estimated factor to encourage split along other axis +// since we cut off between a quarter to a half of elements in this direction per split +// the reduction for first split should be *0.75f but we use 0.8 +// to account for some node overlap. This is somewhat of an arbitrary choice and there's room for improvement. +// 4. recurse on new clusters (goto step 1) +// +struct SubSortQuick +{ + static const PxReal reductionFactors[RTREE_N-1]; + + enum { NTRADEOFF = 9 }; + static const PxU32 stopAtTrisPerLeaf1[NTRADEOFF]; // presets for PxCookingParams::meshSizePerformanceTradeoff implementation + + const PxU32* permuteEnd; + const PxU32* permuteStart; + const PxBounds3V* allBounds; + Array boundCenters; + PxU32 maxBoundsPerLeafPage; + + // initialize the context for the sorting routine + SubSortQuick(PxU32* permute, const PxBounds3V* allBounds_, PxU32 allBoundsSize, PxReal sizePerfTradeOff01) + : allBounds(allBounds_) + { + permuteEnd = permute + allBoundsSize; + permuteStart = permute; + PxU32 boundsCount = allBoundsSize; + boundCenters.reserve(boundsCount); // AP - measured that precomputing centers helps with perf significantly (~20% on 1k verts) + for(PxU32 i = 0; i < boundsCount; i++) + boundCenters.pushBack( allBounds[i].getMinVec3() + allBounds[i].getMaxVec3() ); + PxU32 iTradeOff = PxMin( PxU32(PxMax(0.0f, sizePerfTradeOff01)*NTRADEOFF), NTRADEOFF-1 ); + maxBoundsPerLeafPage = stopAtTrisPerLeaf1[iTradeOff]; + } + + // implements the sorting/splitting procedure + void sort4( + PxU32* PX_RESTRICT permute, const PxU32 clusterSize, // beginning and size of current recursively processed cluster + Array& resultTree, PxU32& maxLevels, + PxBounds3V& subTreeBound, PxU32 level = 0) + { + if(level == 0) + maxLevels = 1; + else + maxLevels = PxMax(maxLevels, level+1); + + PX_ASSERT(permute + clusterSize <= permuteEnd); + PX_ASSERT(maxBoundsPerLeafPage >= RTREE_N-1); + + const PxU32 cluster4 = PxMax(clusterSize/RTREE_N, 1); + + PX_ASSERT(clusterSize > 0); + // find min and max world bound for current cluster + Vec3V mx = allBounds[permute[0]].mx, mn = allBounds[permute[0]].mn; PX_ASSERT(permute[0] < boundCenters.size()); + for(PxU32 i = 1; i < clusterSize; i ++) + { + PX_ASSERT(permute[i] < boundCenters.size()); + mx = V3Max(mx, allBounds[permute[i]].mx); + mn = V3Min(mn, allBounds[permute[i]].mn); + } + PX_ALIGN_PREFIX(16) PxReal maxElem[4] PX_ALIGN_SUFFIX(16); + V3StoreA(V3Sub(mx, mn), *reinterpret_cast(maxElem)); // compute the dimensions and store into a scalar maxElem array + + // split along the longest axis + const PxU32 maxDiagElement = PxU32(maxElem[0] > maxElem[1] && maxElem[0] > maxElem[2] ? 0 : (maxElem[1] > maxElem[2] ? 1 : 2)); + BoundsLTE cmpLte(maxDiagElement, boundCenters.begin()); + + const PxU32 startNodeIndex = resultTree.size(); + resultTree.resizeUninitialized(startNodeIndex+RTREE_N); // at each recursion level we add 4 nodes to the tree + + PxBounds3V childBound( (PxBounds3V::U()) ); // start off uninitialized for performance + const PxI32 leftover = PxMax(PxI32(clusterSize - cluster4*(RTREE_N-1)), 0); + PxU32 totalCount = 0; + for(PxU32 i = 0; i < RTREE_N; i++) + { + // split off cluster4 count nodes out of the entire cluster for each i + const PxU32 clusterOffset = cluster4*i; + PxU32 count1; // cluster4 or leftover depending on whether it's the last cluster + if(i < RTREE_N-1) + { + // only need to so quickSelect for the first pagesize-1 clusters + if(clusterOffset <= clusterSize-1) + { + quickSelect::quickSelectFirstK(permute, clusterOffset, clusterSize-1, cluster4, cmpLte); + // approximate heuristic - reduce max length by some estimated factor to encourage split along other axis + // since we cut off a quarter of elements in this direction the reduction should be *0.75f but we use 0.8 + // to account for some node overlap. This is somewhat of an arbitrary choice though + maxElem[cmpLte.coordIndex] *= reductionFactors[i]; + // recompute cmpLte.coordIndex from updated maxElements + cmpLte.coordIndex = PxU32(maxElem[0] > maxElem[1] && maxElem[0] > maxElem[2] ? 0 : (maxElem[1] > maxElem[2] ? 1 : 2)); + } + count1 = cluster4; + } else + { + count1 = PxU32(leftover); + // verify that leftover + sum of previous clusters adds up to clusterSize or leftover is 0 + // leftover can be 0 if clusterSize higher index = better perf +static const PxU32 NTRADEOFF = 15; + // % -24 -23 -17 -15 -10 -8 -5 -3 0 +3 +3 +5 +7 +8 +9 - % raycast MeshSurface*Random benchmark perf + // K 717 734 752 777 793 811 824 866 903 939 971 1030 1087 1139 1266 - testzone size in K + // # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - preset number +static const PxU32 stopAtTrisPerPage[NTRADEOFF] = { 64, 60, 56, 48, 46, 44, 40, 36, 32, 28, 24, 20, 16, 12, 12}; +static const PxU32 stopAtTrisPerLeaf[NTRADEOFF] = { 16, 14, 12, 10, 9, 8, 8, 6, 5, 5, 5, 4, 4, 4, 2}; // capped at 2 anyway + +///////////////////////////////////////////////////////////////////////// +// comparator struct for sorting the bounds along a specified coordIndex (coordIndex=0,1,2 for X,Y,Z) +struct SortBoundsPredicate +{ + PxU32 coordIndex; + const PxBounds3V* allBounds; + SortBoundsPredicate(PxU32 coordIndex_, const PxBounds3V* allBounds_) : coordIndex(coordIndex_), allBounds(allBounds_) + {} + + bool operator()(const PxU32 & idx1, const PxU32 & idx2) const + { + // using the bounds center for comparison + PxF32 center1 = V3ReadXYZ(allBounds[idx1].mn)[coordIndex] + V3ReadXYZ(allBounds[idx1].mx)[coordIndex]; + PxF32 center2 = V3ReadXYZ(allBounds[idx2].mn)[coordIndex] + V3ReadXYZ(allBounds[idx2].mx)[coordIndex]; + return (center1 < center2); + } +}; + + +///////////////////////////////////////////////////////////////////////// +// auxiliary class for SAH build (SAH = surface area heuristic) +struct Interval +{ + PxU32 start, count; + Interval(PxU32 s, PxU32 c) : start(s), count(c) {} +}; + +// SAH function - returns surface area for given AABB extents +static PX_FORCE_INLINE void PxSAH(const Vec3VArg v, PxF32& sah) +{ + FStore(V3Dot(v, V3PermZXY(v)), &sah); // v.x*v.y + v.y*v.z + v.x*v.z; +} + +struct SubSortSAH +{ + PxU32* PX_RESTRICT permuteStart, *PX_RESTRICT tempPermute; + const PxBounds3V* PX_RESTRICT allBounds; + PxF32* PX_RESTRICT metricL; + PxF32* PX_RESTRICT metricR; + const PxU32* PX_RESTRICT xOrder, *PX_RESTRICT yOrder, *PX_RESTRICT zOrder; + const PxU32* PX_RESTRICT xRanks, *PX_RESTRICT yRanks, *PX_RESTRICT zRanks; + PxU32* PX_RESTRICT tempRanks; + PxU32 nbTotalBounds; + PxU32 iTradeOff; + + // precompute various values used during sort + SubSortSAH( + PxU32* permute, const PxBounds3V* allBounds_, PxU32 numBounds, + const PxU32* xOrder_, const PxU32* yOrder_, const PxU32* zOrder_, + const PxU32* xRanks_, const PxU32* yRanks_, const PxU32* zRanks_, PxReal sizePerfTradeOff01) + : permuteStart(permute), allBounds(allBounds_), + xOrder(xOrder_), yOrder(yOrder_), zOrder(zOrder_), + xRanks(xRanks_), yRanks(yRanks_), zRanks(zRanks_), nbTotalBounds(numBounds) + { + metricL = reinterpret_cast(PX_ALLOC(sizeof(PxF32)*numBounds, PX_DEBUG_EXP("metricL"))); + metricR = reinterpret_cast(PX_ALLOC(sizeof(PxF32)*numBounds, PX_DEBUG_EXP("metricR"))); + tempPermute = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*(numBounds*2+1), PX_DEBUG_EXP("tempPermute"))); + tempRanks = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*numBounds, PX_DEBUG_EXP("tempRanks"))); + iTradeOff = PxMin( PxU32(PxMax(0.0f, sizePerfTradeOff01)*NTRADEOFF), NTRADEOFF-1 ); + } + + ~SubSortSAH() // release temporarily used memory + { + PX_FREE_AND_RESET(metricL); + PX_FREE_AND_RESET(metricR); + PX_FREE_AND_RESET(tempPermute); + PX_FREE_AND_RESET(tempRanks); + } + + //////////////////////////////////////////////////////////////////// + // returns split position for second array start relative to permute ptr + PxU32 split(PxU32* permute, PxU32 clusterSize) + { + if(clusterSize <= 1) + return 0; + if(clusterSize == 2) + return 1; + + PxI32 minCount = clusterSize >= 4 ? 2 : 1; + PxI32 splitStartL = minCount; // range=[startL->endL) + PxI32 splitEndL = PxI32(clusterSize-minCount); + PxI32 splitStartR = PxI32(clusterSize-splitStartL); // range=(endR<-startR], startR > endR + PxI32 splitEndR = PxI32(clusterSize-splitEndL); + PX_ASSERT(splitEndL-splitStartL == splitStartR-splitEndR); + PX_ASSERT(splitStartL <= splitEndL); + PX_ASSERT(splitStartR >= splitEndR); + PX_ASSERT(splitEndR >= 1); + PX_ASSERT(splitEndL < PxI32(clusterSize)); + + // pick the best axis with some splitting metric + // axis index is X=0, Y=1, Z=2 + PxF32 minMetric[3]; + PxU32 minMetricSplit[3]; + const PxU32* ranks3[3] = { xRanks, yRanks, zRanks }; + const PxU32* orders3[3] = { xOrder, yOrder, zOrder }; + for(PxU32 coordIndex = 0; coordIndex <= 2; coordIndex++) + { + SortBoundsPredicate sortPredicateLR(coordIndex, allBounds); + + const PxU32* rank = ranks3[coordIndex]; + const PxU32* order = orders3[coordIndex]; + + // build ranks in tempPermute + if(clusterSize == nbTotalBounds) // AP: about 4% perf gain from this optimization + { + // if this is a full cluster sort, we already have it done + for(PxU32 i = 0; i < clusterSize; i ++) + tempPermute[i] = order[i]; + } else + { + // sort the tempRanks + for(PxU32 i = 0; i < clusterSize; i ++) + tempRanks[i] = rank[permute[i]]; + Ps::sort(tempRanks, clusterSize); + for(PxU32 i = 0; i < clusterSize; i ++) // convert back from ranks to indices + tempPermute[i] = order[tempRanks[i]]; + } + + // we consider overlapping intervals for minimum sum of metrics + // left interval is from splitStartL up to splitEndL + // right interval is from splitStartR down to splitEndR + + + // first compute the array metricL + Vec3V boundsLmn = allBounds[tempPermute[0]].mn; // init with 0th bound + Vec3V boundsLmx = allBounds[tempPermute[0]].mx; // init with 0th bound + PxI32 ii; + for(ii = 1; ii < splitStartL; ii++) // sweep right to include all bounds up to splitStartL-1 + { + boundsLmn = V3Min(boundsLmn, allBounds[tempPermute[ii]].mn); + boundsLmx = V3Max(boundsLmx, allBounds[tempPermute[ii]].mx); + } + + PxU32 countL0 = 0; + for(ii = splitStartL; ii <= splitEndL; ii++) // compute metric for inclusive bounds from splitStartL to splitEndL + { + boundsLmn = V3Min(boundsLmn, allBounds[tempPermute[ii]].mn); + boundsLmx = V3Max(boundsLmx, allBounds[tempPermute[ii]].mx); + PxSAH(V3Sub(boundsLmx, boundsLmn), metricL[countL0++]); + } + // now we have metricL + + // now compute the array metricR + Vec3V boundsRmn = allBounds[tempPermute[clusterSize-1]].mn; // init with last bound + Vec3V boundsRmx = allBounds[tempPermute[clusterSize-1]].mx; // init with last bound + for(ii = PxI32(clusterSize-2); ii > splitStartR; ii--) // include bounds to the left of splitEndR down to splitStartR + { + boundsRmn = V3Min(boundsRmn, allBounds[tempPermute[ii]].mn); + boundsRmx = V3Max(boundsRmx, allBounds[tempPermute[ii]].mx); + } + + PxU32 countR0 = 0; + for(ii = splitStartR; ii >= splitEndR; ii--) // continue sweeping left, including bounds and recomputing the metric + { + boundsRmn = V3Min(boundsRmn, allBounds[tempPermute[ii]].mn); + boundsRmx = V3Max(boundsRmx, allBounds[tempPermute[ii]].mx); + PxSAH(V3Sub(boundsRmx, boundsRmn), metricR[countR0++]); + } + + PX_ASSERT((countL0 == countR0) && (countL0 == PxU32(splitEndL-splitStartL+1))); + + // now iterate over splitRange and compute the minimum sum of SAHLeft*countLeft + SAHRight*countRight + PxU32 minMetricSplitPosition = 0; + PxF32 minMetricLocal = PX_MAX_REAL; + const PxI32 hsI32 = PxI32(clusterSize/2); + const PxI32 splitRange = (splitEndL-splitStartL+1); + for(ii = 0; ii < splitRange; ii++) + { + PxF32 countL = PxF32(ii+minCount); // need to add minCount since ii iterates over splitRange + PxF32 countR = PxF32(splitRange-ii-1+minCount); + PX_ASSERT(PxU32(countL + countR) == clusterSize); + + const PxF32 metric = (countL*metricL[ii] + countR*metricR[splitRange-ii-1]); + const PxU32 splitPos = PxU32(ii+splitStartL); + if(metric < minMetricLocal || + (metric <= minMetricLocal && // same metric but more even split + PxAbs(PxI32(splitPos)-hsI32) < PxAbs(PxI32(minMetricSplitPosition)-hsI32))) + { + minMetricLocal = metric; + minMetricSplitPosition = splitPos; + } + } + + minMetric[coordIndex] = minMetricLocal; + minMetricSplit[coordIndex] = minMetricSplitPosition; + + // sum of axis lengths for both left and right AABBs + } + + PxU32 winIndex = 2; + if(minMetric[0] <= minMetric[1] && minMetric[0] <= minMetric[2]) + winIndex = 0; + else if(minMetric[1] <= minMetric[2]) + winIndex = 1; + + const PxU32* rank = ranks3[winIndex]; + const PxU32* order = orders3[winIndex]; + if(clusterSize == nbTotalBounds) // AP: about 4% gain from this special case optimization + { + // if this is a full cluster sort, we already have it done + for(PxU32 i = 0; i < clusterSize; i ++) + permute[i] = order[i]; + } else + { + // sort the tempRanks + for(PxU32 i = 0; i < clusterSize; i ++) + tempRanks[i] = rank[permute[i]]; + Ps::sort(tempRanks, clusterSize); + for(PxU32 i = 0; i < clusterSize; i ++) + permute[i] = order[tempRanks[i]]; + } + + PxU32 splitPoint = minMetricSplit[winIndex]; + if(clusterSize == 3 && splitPoint == 0) + splitPoint = 1; // special case due to rounding + return splitPoint; + } + + // compute surface area for a given split + PxF32 computeSA(const PxU32* permute, const Interval& split) // both permute and i are relative + { + PX_ASSERT(split.count >= 1); + Vec3V bmn = allBounds[permute[split.start]].mn; + Vec3V bmx = allBounds[permute[split.start]].mx; + for(PxU32 i = 1; i < split.count; i++) + { + const PxBounds3V& b1 = allBounds[permute[split.start+i]]; + bmn = V3Min(bmn, b1.mn); bmx = V3Max(bmx, b1.mx); + } + + PxF32 ret; PxSAH(V3Sub(bmx, bmn), ret); + return ret; + } + + //////////////////////////////////////////////////////////////////// + // main SAH sort routine + void sort4(PxU32* permute, PxU32 clusterSize, + Array& resultTree, PxU32& maxLevels, PxU32 level = 0, RTreeNodeNQ* parentNode = NULL) + { + PX_UNUSED(parentNode); + + if(level == 0) + maxLevels = 1; + else + maxLevels = PxMax(maxLevels, level+1); + + PxU32 splitPos[RTREE_N]; + for(PxU32 j = 0; j < RTREE_N; j++) + splitPos[j] = j+1; + + if(clusterSize >= RTREE_N) + { + // split into RTREE_N number of regions via RTREE_N-1 subsequent splits + // each split is represented as a current interval + // we iterate over currently active intervals and compute it's surface area + // then we split the interval with maximum surface area + // AP scaffold: possible optimization - seems like computeSA can be cached for unchanged intervals + InlineArray splits; + splits.pushBack(Interval(0, clusterSize)); + for(PxU32 iSplit = 0; iSplit < RTREE_N-1; iSplit++) + { + PxF32 maxSAH = -FLT_MAX; + PxU32 maxSplit = 0xFFFFffff; + for(PxU32 i = 0; i < splits.size(); i++) + { + if(splits[i].count == 1) + continue; + PxF32 SAH = computeSA(permute, splits[i])*splits[i].count; + if(SAH > maxSAH) + { + maxSAH = SAH; + maxSplit = i; + } + } + PX_ASSERT(maxSplit != 0xFFFFffff); + + // maxSplit is now the index of the interval in splits array with maximum surface area + // we now split it into 2 using the split() function + Interval old = splits[maxSplit]; + PX_ASSERT(old.count > 1); + PxU32 splitLocal = split(permute+old.start, old.count); // relative split pos + + PX_ASSERT(splitLocal >= 1); + PX_ASSERT(old.count-splitLocal >= 1); + splits.pushBack(Interval(old.start, splitLocal)); + splits.pushBack(Interval(old.start+splitLocal, old.count-splitLocal)); + splits.replaceWithLast(maxSplit); + splitPos[iSplit] = old.start+splitLocal; + } + + // verification code, make sure split counts add up to clusterSize + PX_ASSERT(splits.size() == RTREE_N); + PxU32 sum = 0; + for(PxU32 j = 0; j < RTREE_N; j++) + sum += splits[j].count; + PX_ASSERT(sum == clusterSize); + } + else // clusterSize < RTREE_N + { + // make it so splitCounts based on splitPos add up correctly for small cluster sizes + for(PxU32 i = clusterSize; i < RTREE_N-1; i++) + splitPos[i] = clusterSize; + } + + // sort splitPos index array using quicksort (just a few values) + Ps::sort(splitPos, RTREE_N-1); + splitPos[RTREE_N-1] = clusterSize; // splitCount[n] is computed as splitPos[n+1]-splitPos[n], so we need to add this last value + + // now compute splitStarts and splitCounts from splitPos[] array. Also perform a bunch of correctness verification + PxU32 splitStarts[RTREE_N]; + PxU32 splitCounts[RTREE_N]; + splitStarts[0] = 0; + splitCounts[0] = splitPos[0]; + PxU32 sumCounts = splitCounts[0]; + for(PxU32 j = 1; j < RTREE_N; j++) + { + splitStarts[j] = splitPos[j-1]; + PX_ASSERT(splitStarts[j-1]<=splitStarts[j]); + splitCounts[j] = splitPos[j]-splitPos[j-1]; + PX_ASSERT(splitCounts[j] > 0 || clusterSize < RTREE_N); + sumCounts += splitCounts[j]; + PX_ASSERT(splitStarts[j-1]+splitCounts[j-1]<=splitStarts[j]); + } + PX_ASSERT(sumCounts == clusterSize); + PX_ASSERT(splitStarts[RTREE_N-1]+splitCounts[RTREE_N-1]<=clusterSize); + + // mark this cluster as terminal based on clusterSize <= stopAtTrisPerPage parameter for current iTradeOff user specified preset + bool terminalClusterByTotalCount = (clusterSize <= stopAtTrisPerPage[iTradeOff]); + // iterate over splitCounts for the current cluster, if any of counts exceed 16 (which is the maximum supported by LeafTriangles + // we cannot mark this cluster as terminal (has to be split more) + for(PxU32 s = 0; s < RTREE_N; s++) + if(splitCounts[s] > 16) // LeafTriangles doesn't support > 16 tris + terminalClusterByTotalCount = false; + + // iterate over all the splits + for(PxU32 s = 0; s < RTREE_N; s++) + { + RTreeNodeNQ rtn; + PxU32 splitCount = splitCounts[s]; + if(splitCount > 0) // splits shouldn't be empty generally + { + // sweep left to right and compute min and max SAH for each individual bound in current split + PxBounds3V b = allBounds[permute[splitStarts[s]]]; + PxF32 sahMin; PxSAH(b.getExtents(), sahMin); + PxF32 sahMax = sahMin; + // AP scaffold - looks like this could be optimized (we are recomputing bounds top down) + for(PxU32 i = 1; i < splitCount; i++) + { + PxU32 localIndex = i + splitStarts[s]; + const PxBounds3V& b1 = allBounds[permute[localIndex]]; + PxF32 sah1; PxSAH(b1.getExtents(), sah1); + sahMin = PxMin(sahMin, sah1); + sahMax = PxMax(sahMax, sah1); + b.include(b1); + } + + rtn.bounds.minimum = V3ReadXYZ(b.mn); + rtn.bounds.maximum = V3ReadXYZ(b.mx); + + // if bounds differ widely (according to some heuristic preset), we continue splitting + // this is important for a mixed cluster with large and small triangles + bool okSAH = (sahMax/sahMin < 40.0f); + if(!okSAH) + terminalClusterByTotalCount = false; // force splitting this cluster + + bool stopSplitting = // compute the final splitting criterion + splitCount <= 2 || (okSAH && splitCount <= 3) // stop splitting at 2 nodes or if SAH ratio is OK and splitCount <= 3 + || terminalClusterByTotalCount || splitCount <= stopAtTrisPerLeaf[iTradeOff]; + if(stopSplitting) + { + // this is a terminal page then, mark as such + // first node index is relative to the top level input array beginning + rtn.childPageFirstNodeIndex = PxI32(splitStarts[s]+(permute-permuteStart)); + rtn.leafCount = PxI32(splitCount); + PX_ASSERT(splitCount <= 16); // LeafTriangles doesn't support more + } + else + { + // this is not a terminal page, we will recompute this later, after we recurse on subpages (label ZZZ) + rtn.childPageFirstNodeIndex = -1; + rtn.leafCount = 0; + } + } + else // splitCount == 0 at this point, this is an empty paddding node (with current presets it's very rare) + { + PX_ASSERT(splitCount == 0); + rtn.bounds.setEmpty(); + rtn.childPageFirstNodeIndex = -1; + rtn.leafCount = -1; + } + resultTree.pushBack(rtn); // push the new node into the resultTree array + } + + if(terminalClusterByTotalCount) // abort recursion if terminal cluster + return; + + // recurse on subpages + PxU32 parentIndex = resultTree.size() - RTREE_N; // save the parentIndex as specified (array can be resized during recursion) + for(PxU32 s = 0; sleafCount == 0) // only split pages that were marked as non-terminal during splitting (see "label ZZZ" above) + { + // all child nodes will be pushed inside of this recursive call, + // so we set the child pointer for parent node to resultTree.size() + sParent->childPageFirstNodeIndex = PxI32(resultTree.size()); + sort4(permute+splitStarts[s], splitCounts[s], resultTree, maxLevels, level+1, sParent); + } + } + } +}; + + + + +///////////////////////////////////////////////////////////////////////// +// initializes the input permute array with identity permutation +// and shuffles it so that new sorted index, newIndex = resultPermute[oldIndex] +static void buildFromBounds( + Gu::RTree& result, const PxBounds3V* allBounds, PxU32 numBounds, + Array& permute, RTreeCooker::RemapCallback* rc, Vec3VArg allMn, Vec3VArg allMx, + PxReal sizePerfTradeOff01, PxMeshCookingHint::Enum hint) +{ + PX_UNUSED(sizePerfTradeOff01); + PxBounds3V treeBounds(allMn, allMx); + + // start off with an identity permutation + permute.resize(0); + permute.reserve(numBounds+1); + for(PxU32 j = 0; j < numBounds; j ++) + permute.pushBack(j); + const PxU32 sentinel = 0xABCDEF01; + permute.pushBack(sentinel); + + // load sorted nodes into an RTreeNodeNQ tree representation + // build the tree structure from sorted nodes + const PxU32 pageSize = RTREE_N; + Array resultTree; + resultTree.reserve(numBounds*2); + + PxU32 maxLevels = 0; + if(hint == PxMeshCookingHint::eSIM_PERFORMANCE) // use high quality SAH build + { + Array xRanks(numBounds), yRanks(numBounds), zRanks(numBounds), xOrder(numBounds), yOrder(numBounds), zOrder(numBounds); + PxMemCopy(xOrder.begin(), permute.begin(), sizeof(xOrder[0])*numBounds); + PxMemCopy(yOrder.begin(), permute.begin(), sizeof(yOrder[0])*numBounds); + PxMemCopy(zOrder.begin(), permute.begin(), sizeof(zOrder[0])*numBounds); + // sort by shuffling the permutation, precompute sorted ranks for x,y,z-orders + Ps::sort(xOrder.begin(), xOrder.size(), SortBoundsPredicate(0, allBounds)); + for(PxU32 i = 0; i < numBounds; i++) xRanks[xOrder[i]] = i; + Ps::sort(yOrder.begin(), yOrder.size(), SortBoundsPredicate(1, allBounds)); + for(PxU32 i = 0; i < numBounds; i++) yRanks[yOrder[i]] = i; + Ps::sort(zOrder.begin(), zOrder.size(), SortBoundsPredicate(2, allBounds)); + for(PxU32 i = 0; i < numBounds; i++) zRanks[zOrder[i]] = i; + + SubSortSAH ss(permute.begin(), allBounds, numBounds, + xOrder.begin(), yOrder.begin(), zOrder.begin(), xRanks.begin(), yRanks.begin(), zRanks.begin(), sizePerfTradeOff01); + ss.sort4(permute.begin(), numBounds, resultTree, maxLevels); + } else + { // use fast cooking path + PX_ASSERT(hint == PxMeshCookingHint::eCOOKING_PERFORMANCE); + SubSortQuick ss(permute.begin(), allBounds, numBounds, sizePerfTradeOff01); + PxBounds3V discard((PxBounds3V::U())); + ss.sort4(permute.begin(), permute.size()-1, resultTree, maxLevels, discard); // AP scaffold: need to implement build speed/runtime perf slider + } + + PX_ASSERT(permute[numBounds] == sentinel); // verify we didn't write past the array + permute.popBack(); // discard the sentinel value + + #if PRINT_RTREE_COOKING_STATS // stats code + PxU32 totalLeafTris = 0; + PxU32 numLeaves = 0; + PxI32 maxLeafTris = 0; + PxU32 numEmpty = 0; + for(PxU32 i = 0; i < resultTree.size(); i++) + { + PxI32 leafCount = resultTree[i].leafCount; + numEmpty += (resultTree[i].bounds.isEmpty()); + if(leafCount > 0) + { + numLeaves++; + totalLeafTris += leafCount; + if(leafCount > maxLeafTris) + maxLeafTris = leafCount; + } + } + + printf("AABBs total/empty=%d/%d\n", resultTree.size(), numEmpty); + printf("numTris=%d, numLeafAABBs=%d, avgTrisPerLeaf=%.2f, maxTrisPerLeaf = %d\n", + numBounds, numLeaves, PxF32(totalLeafTris)/numLeaves, maxLeafTris); + #endif + + PX_ASSERT(RTREE_N*sizeof(RTreeNodeQ) == sizeof(RTreePage)); // needed for nodePtrMultiplier computation to be correct + const int nodePtrMultiplier = sizeof(RTreeNodeQ); // convert offset as count in qnodes to page ptr + + // Quantize the tree. AP scaffold - might be possible to merge this phase with the page pass below this loop + Array qtreeNodes; + PxU32 firstEmptyIndex = PxU32(-1); + PxU32 resultCount = resultTree.size(); + qtreeNodes.reserve(resultCount); + + for(PxU32 i = 0; i < resultCount; i++) // AP scaffold - eliminate this pass + { + RTreeNodeNQ & u = resultTree[i]; + RTreeNodeQ q; + q.setLeaf(u.leafCount > 0); // set the leaf flag + if(u.childPageFirstNodeIndex == -1) // empty node? + { + if(firstEmptyIndex == PxU32(-1)) + firstEmptyIndex = qtreeNodes.size(); + q.minx = q.miny = q.minz = FLT_MAX; // AP scaffold improvement - use empty 1e30 bounds instead and reference a valid leaf + q.maxx = q.maxy = q.maxz = -FLT_MAX; // that will allow to remove the empty node test from the runtime + + q.ptr = firstEmptyIndex*nodePtrMultiplier; PX_ASSERT((q.ptr & 1) == 0); + q.setLeaf(true); // label empty node as leaf node + } else + { + // non-leaf node + q.minx = u.bounds.minimum.x; + q.miny = u.bounds.minimum.y; + q.minz = u.bounds.minimum.z; + q.maxx = u.bounds.maximum.x; + q.maxy = u.bounds.maximum.y; + q.maxz = u.bounds.maximum.z; + if(u.leafCount > 0) + { + q.ptr = PxU32(u.childPageFirstNodeIndex); + rc->remap(&q.ptr, q.ptr, PxU32(u.leafCount)); + PX_ASSERT(q.isLeaf()); // remap is expected to set the isLeaf bit + } + else + { + // verify that all children bounds are included in the parent bounds + for(PxU32 s = 0; s < RTREE_N; s++) + { + const RTreeNodeNQ& child = resultTree[u.childPageFirstNodeIndex+s]; + PX_UNUSED(child); + // is a sentinel node or is inside parent's bounds + PX_ASSERT(child.leafCount == -1 || child.bounds.isInside(u.bounds)); + } + + q.ptr = PxU32(u.childPageFirstNodeIndex * nodePtrMultiplier); + PX_ASSERT(q.ptr % RTREE_N == 0); + q.setLeaf(false); + } + } + qtreeNodes.pushBack(q); + } + + // build the final rtree image + result.mInvDiagonal = PxVec4(1.0f); + PX_ASSERT(qtreeNodes.size() % RTREE_N == 0); + result.mTotalNodes = qtreeNodes.size(); + result.mTotalPages = result.mTotalNodes / pageSize; + result.mPages = static_cast( + Ps::AlignedAllocator<128>().allocate(sizeof(RTreePage)*result.mTotalPages, __FILE__, __LINE__)); + result.mBoundsMin = PxVec4(V3ReadXYZ(treeBounds.mn), 0.0f); + result.mBoundsMax = PxVec4(V3ReadXYZ(treeBounds.mx), 0.0f); + result.mDiagonalScaler = (result.mBoundsMax - result.mBoundsMin) / 65535.0f; + result.mPageSize = pageSize; + result.mNumLevels = maxLevels; + PX_ASSERT(result.mTotalNodes % pageSize == 0); + result.mNumRootPages = 1; + + for(PxU32 j = 0; j < result.mTotalPages; j++) + { + RTreePage& page = result.mPages[j]; + for(PxU32 k = 0; k < RTREE_N; k ++) + { + const RTreeNodeQ& n = qtreeNodes[j*RTREE_N+k]; + page.maxx[k] = n.maxx; + page.maxy[k] = n.maxy; + page.maxz[k] = n.maxz; + page.minx[k] = n.minx; + page.miny[k] = n.miny; + page.minz[k] = n.minz; + page.ptrs[k] = n.ptr; + } + } + + //printf("Tree size=%d\n", result.mTotalPages*sizeof(RTreePage)); +#if PX_DEBUG + result.validate(); // make sure the child bounds are included in the parent and other validation +#endif +} + +} // namespace physx diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h new file mode 100644 index 000000000..6faf2f674 --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h @@ -0,0 +1,51 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CmPhysXCommon.h" +#include "GuMeshData.h" +#include "PxCooking.h" +#include "PsArray.h" +#include "GuRTree.h" + +namespace physx +{ + struct RTreeCooker + { + struct RemapCallback // a callback to convert indices from triangle to LeafTriangles or other uses + { + virtual ~RemapCallback() {} + virtual void remap(PxU32* rtreePtr, PxU32 start, PxU32 leafCount) = 0; + }; + + // triangles will be remapped so that newIndex = resultPermute[oldIndex] + static void buildFromTriangles( + Gu::RTree& resultTree, const PxVec3* verts, PxU32 numVerts, const PxU16* tris16, const PxU32* tris32, PxU32 numTris, + Ps::Array& resultPermute, RemapCallback* rc, PxReal sizePerfTradeOff01, PxMeshCookingHint::Enum hint); + }; +} diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp new file mode 100644 index 000000000..67cacd35f --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp @@ -0,0 +1,1385 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "RTreeCooking.h" +#include "TriangleMeshBuilder.h" +#include "EdgeList.h" +#include "MeshCleaner.h" +#include "GuConvexEdgeFlags.h" +#include "PxTriangleMeshDesc.h" +#include "GuSerialize.h" +#include "Cooking.h" +#include "GuMeshData.h" +#include "GuTriangle32.h" +#include "GuRTree.h" +#include "GuInternal.h" +#include "GuBV4Build.h" +#include "GuBV32Build.h" +#include "PsFoundation.h" +#include "PsHashMap.h" +#include "PsSort.h" + +namespace physx { + +struct int3 +{ + int x, y, z; +}; + +struct uint3 +{ + unsigned int x, y, z; +}; + +PX_ALIGN_PREFIX(16) +struct uint4 +{ + unsigned int x, y, z, w; +} +PX_ALIGN_SUFFIX(16); + +PX_ALIGN_PREFIX(16) +struct float4 +{ + float x, y, z, w; +} +PX_ALIGN_SUFFIX(16); + +} + +#include "GrbTriangleMeshCooking.h" + +using namespace physx; +using namespace Gu; +using namespace Ps; + +namespace physx { + +TriangleMeshBuilder::TriangleMeshBuilder(TriangleMeshData& m, const PxCookingParams& params) : + edgeList (NULL), + mParams (params), + mMeshData (m) +{ +} + +TriangleMeshBuilder::~TriangleMeshBuilder() +{ + releaseEdgeList(); +} + +void TriangleMeshBuilder::remapTopology(const PxU32* order) +{ + if(!mMeshData.mNbTriangles) + return; + + // Remap one array at a time to limit memory usage + + Gu::TriangleT* newTopo = reinterpret_cast*>(PX_ALLOC(mMeshData.mNbTriangles * sizeof(Gu::TriangleT), "Gu::TriangleT")); + for(PxU32 i=0;i*>(mMeshData.mTriangles)[order[i]]; + PX_FREE_AND_RESET(mMeshData.mTriangles); + mMeshData.mTriangles = newTopo; + + if(mMeshData.mMaterialIndices) + { + PxMaterialTableIndex* newMat = PX_NEW(PxMaterialTableIndex)[mMeshData.mNbTriangles]; + for(PxU32 i=0;i(mMeshData.mTriangles), meshWeldTolerance); + if(!cleaner.mNbTris) + return false; + + if(validate) + { + // if we do only validate, we check if cleaning did not remove any verts or triangles. + // such a mesh can be then directly used for cooking without clean flag + if((cleaner.mNbVerts != mMeshData.mNbVertices) || (cleaner.mNbTris != mMeshData.mNbTriangles)) + { + return false; + } + } + + // PT: deal with the remap table + { + // PT: TODO: optimize this + if(cleaner.mRemap) + { + const PxU32 newNbTris = cleaner.mNbTris; + + // Remap material array + if(mMeshData.mMaterialIndices) + { + PxMaterialTableIndex* tmp = PX_NEW(PxMaterialTableIndex)[newNbTris]; + for(PxU32 i=0;i*>(mMeshData.mTriangles)[i].v[0] = vref0; + reinterpret_cast*>(mMeshData.mTriangles)[i].v[1] = vref1; + reinterpret_cast*>(mMeshData.mTriangles)[i].v[2] = vref2; + + if( (v[vref0] - v[vref1]).magnitudeSquared() >= testLength + || (v[vref1] - v[vref2]).magnitudeSquared() >= testLength + || (v[vref2] - v[vref0]).magnitudeSquared() >= testLength + ) + bigTriangle = true; + } + if(bigTriangle) + { + if(condition) + *condition = PxTriangleMeshCookingResult::eLARGE_TRIANGLE; + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "TriangleMesh: triangles are too big, reduce their size to increase simulation stability!"); + } + } + + return true; +} + +void TriangleMeshBuilder::createSharedEdgeData(bool buildAdjacencies, bool buildActiveEdges) +{ + if(buildAdjacencies) // building edges is required if buildAdjacencies is requested + buildActiveEdges = true; + + PX_ASSERT(mMeshData.mExtraTrigData == NULL); + PX_ASSERT(mMeshData.mAdjacencies == NULL); + + if(!buildActiveEdges) + return; + + const PxU32 nTrigs = mMeshData.mNbTriangles; + + mMeshData.mExtraTrigData = PX_NEW(PxU8)[nTrigs]; + memset(mMeshData.mExtraTrigData, 0, sizeof(PxU8)*nTrigs); + + const Gu::TriangleT* trigs = reinterpret_cast*>(mMeshData.mTriangles); + if(0x40000000 <= nTrigs) + { + //mesh is too big for this algo, need to be able to express trig indices in 30 bits, and still have an index reserved for "unused": + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "TriangleMesh: mesh is too big for this algo!"); + return; + } + + createEdgeList(); + if(edgeList) + { + PX_ASSERT(edgeList->getNbFaces()==mMeshData.mNbTriangles); + if(edgeList->getNbFaces()==mMeshData.mNbTriangles) + { + for(PxU32 i=0;igetNbFaces();i++) + { + const Gu::EdgeTriangleData& ET = edgeList->getEdgeTriangle(i); + // Replicate flags + if(Gu::EdgeTriangleAC::HasActiveEdge01(ET)) mMeshData.mExtraTrigData[i] |= Gu::ETD_CONVEX_EDGE_01; + if(Gu::EdgeTriangleAC::HasActiveEdge12(ET)) mMeshData.mExtraTrigData[i] |= Gu::ETD_CONVEX_EDGE_12; + if(Gu::EdgeTriangleAC::HasActiveEdge20(ET)) mMeshData.mExtraTrigData[i] |= Gu::ETD_CONVEX_EDGE_20; + } + } + } + + // fill the adjacencies + if(buildAdjacencies) + { + mMeshData.mAdjacencies = PX_NEW(PxU32)[nTrigs*3]; + memset(mMeshData.mAdjacencies, 0xFFFFffff, sizeof(PxU32)*nTrigs*3); + + PxU32 NbEdges = edgeList->getNbEdges(); + const Gu::EdgeDescData* ED = edgeList->getEdgeToTriangles(); + const Gu::EdgeData* Edges = edgeList->getEdges(); + const PxU32* FBE = edgeList->getFacesByEdges(); + + while(NbEdges--) + { + // Get number of triangles sharing current edge + PxU32 Count = ED->Count; + + if(Count > 1) + { + PxU32 FaceIndex0 = FBE[ED->Offset+0]; + PxU32 FaceIndex1 = FBE[ED->Offset+1]; + + const Gu::EdgeData& edgeData = *Edges; + const Gu::TriangleT& T0 = trigs[FaceIndex0]; + const Gu::TriangleT& T1 = trigs[FaceIndex1]; + + PxU32 offset0 = T0.findEdgeCCW(edgeData.Ref0,edgeData.Ref1); + PxU32 offset1 = T1.findEdgeCCW(edgeData.Ref0,edgeData.Ref1); + + mMeshData.setTriangleAdjacency(FaceIndex0, FaceIndex1, offset0); + mMeshData.setTriangleAdjacency(FaceIndex1, FaceIndex0, offset1); + } + ED++; + Edges++; + } + } + +#if PX_DEBUG + for(PxU32 i=0;i& T = trigs[i]; + PX_UNUSED(T); + const Gu::EdgeTriangleData& ET = edgeList->getEdgeTriangle(i); + PX_ASSERT((Gu::EdgeTriangleAC::HasActiveEdge01(ET) && (mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_01)) || (!Gu::EdgeTriangleAC::HasActiveEdge01(ET) && !(mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_01))); + PX_ASSERT((Gu::EdgeTriangleAC::HasActiveEdge12(ET) && (mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_12)) || (!Gu::EdgeTriangleAC::HasActiveEdge12(ET) && !(mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_12))); + PX_ASSERT((Gu::EdgeTriangleAC::HasActiveEdge20(ET) && (mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_20)) || (!Gu::EdgeTriangleAC::HasActiveEdge20(ET) && !(mMeshData.mExtraTrigData[i] & Gu::ETD_CONVEX_EDGE_20))); + } +#endif + return; +} + +namespace GrbTrimeshCookerHelper +{ + +struct SortedNeighbor +{ + PxU32 v, a; // vertex and adjacent vertex + bool boundary; + + SortedNeighbor(PxU32 v_, PxU32 a_, bool b_): v(v_), a(a_), boundary(b_) {} + + // sort boundary edges to the front so that they are kept when duplicates are removed + bool operator<(const SortedNeighbor& b) const + { + return (v mLocalToMesh; + Ps::HashMap mMeshToLocal; +}; + +#include + + +void findSharpVertices( + Ps::Array& pairList, + Ps::Array& edgeRanges, + /*const Ps::Array& triangles,*/ + const uint3* triIndices, + const uint4* triAdjacencies, + PxU32 nbTris, + PxU32 nbVerts + ) +{ + // sort the edges which are sharp or boundary + for(PxU32 i=0;i=3) + edgeRanges[pairList[p].v] = SharpEdgeRange(p, u-p); + } +} + +#if 0 +PxU32 buildVertexConnectionNew_p1( + Ps::Array & pairList, + Ps::Array & edgeRanges, + LocalIndexer & vertexMap, + + const uint4 * triIndices, + const uint4 * triAdjacencies, + + PxU32 nbTris, + PxU32 nbVerts + ) +{ + findSharpVertices( + pairList, + edgeRanges, + triIndices, + triAdjacencies, + nbTris, + nbVerts + ); + + // add all the original triangles and vertices and record how big the core is + for(PxU32 i=0; i& pairList, + Ps::Array& edgeRanges, + LocalIndexer & vertexMap, + + const uint4 * /*triIndices*/, + const uint4 * /*triAdjacencies*/, + + PxU32 /*nbTris*/, + PxU32 nbVerts, + PxU32 /*nbNeighbors*/ + ) +{ + PxU32 n = 0; + for(PxU32 i=0;i & pairList, + Ps::Array & edgeRanges, + + const uint3* triIndices, + const uint4 * triAdjacencies, + + PxU32 nbTris, + PxU32 nbVerts + ) +{ + findSharpVertices( + pairList, + edgeRanges, + triIndices, + triAdjacencies, + nbTris, + nbVerts + ); + + + // add the neighbors of the sharp vertices + PxU32 nbNeighbors = 0; + for (PxU32 i = 0; i& pairList, + Ps::Array& edgeRanges, + PxU32 nbVerts + ) +{ + PxU32 n = 0; + for (PxU32 i = 0; i(PX_ALLOC(numTris * sizeof(PxVec3), PX_DEBUG_EXP("tempNormalsPerTri_prealloc"))); + + mMeshData.mGRB_primAdjacencies = PX_ALLOC(sizeof(uint4)*numTris, PX_DEBUG_EXP("GRB_triAdjacencies")); + + + buildAdjacencies( + reinterpret_cast(mMeshData.mGRB_primAdjacencies), + tempNormalsPerTri_prealloc, + mMeshData.mVertices, + reinterpret_cast(mMeshData.mGRB_primIndices), + numTris + ); + + + PX_FREE(tempNormalsPerTri_prealloc); + +} + +void TriangleMeshBuilder::createGRBMidPhaseAndData(const PxU32 originalTriangleCount) +{ + if (mParams.buildGPUData) + { + + PX_ASSERT(!(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES)); + + BV32Tree* bv32Tree = PX_NEW(BV32Tree); + mMeshData.mGRB_BV32Tree = bv32Tree; + + BV32TriangleMeshBuilder::createMidPhaseStructure(mParams, mMeshData, *bv32Tree); + + createGRBData(); + + //create a remap table from GPU to CPU remap table + PxU32* orignalToRemap = PX_NEW(PxU32)[originalTriangleCount]; + + PX_ASSERT(mMeshData.mFaceRemap); + + + for (PxU32 i = 0; i < mMeshData.mNbTriangles; ++i) + { + const PxU32 index = mMeshData.mFaceRemap[i]; + PX_ASSERT(index < originalTriangleCount); + orignalToRemap[index] = i; + } + + + //map CPU remap triangle index to GPU remap triangle index + for (PxU32 i = 0; i < mMeshData.mNbTriangles; ++i) + { + const PxU32 index = mMeshData.mGRB_faceRemap[i]; + mMeshData.mGRB_faceRemap[i] = orignalToRemap[index]; + } + +#if BV32_VALIDATE + IndTri32* grbTriIndices = reinterpret_cast(mMeshData.mGRB_triIndices); + IndTri32* cpuTriIndices = reinterpret_cast(mMeshData.mTriangles); + //map CPU remap triangle index to GPU remap triangle index + for (PxU32 i = 0; i < mMeshData.mNbTriangles; ++i) + { + PX_ASSERT(grbTriIndices[i].mRef[0] == cpuTriIndices[mMeshData.mGRB_faceRemap[i]].mRef[0]); + PX_ASSERT(grbTriIndices[i].mRef[1] == cpuTriIndices[mMeshData.mGRB_faceRemap[i]].mRef[1]); + PX_ASSERT(grbTriIndices[i].mRef[2] == cpuTriIndices[mMeshData.mGRB_faceRemap[i]].mRef[2]); + } +#endif + + if (orignalToRemap) + PX_DELETE_POD(orignalToRemap); + + } +} + +void TriangleMeshBuilder::createEdgeList() +{ + Gu::EDGELISTCREATE create; + create.NbFaces = mMeshData.mNbTriangles; + if(mMeshData.has16BitIndices()) + { + create.DFaces = NULL; + create.WFaces = reinterpret_cast(mMeshData.mTriangles); + } + else + { + create.DFaces = reinterpret_cast(mMeshData.mTriangles); + create.WFaces = NULL; + } + create.FacesToEdges = true; + create.EdgesToFaces = true; + create.Verts = mMeshData.mVertices; + //create.Epsilon = 0.1f; + // create.Epsilon = convexEdgeThreshold; + edgeList = PX_NEW(Gu::EdgeListBuilder); + if(!edgeList->init(create)) + { + PX_DELETE(edgeList); + edgeList = 0; + } +} + +void TriangleMeshBuilder::releaseEdgeList() +{ + PX_DELETE_AND_RESET(edgeList); +} + +// +// When suppressTriangleMeshRemapTable is true, the face remap table is not created. This saves a significant amount of memory, +// but the SDK will not be able to provide information about which mesh triangle is hit in collisions, sweeps or raycasts hits. +// +// The sequence is as follows: + +bool TriangleMeshBuilder::loadFromDesc(const PxTriangleMeshDesc& _desc, PxTriangleMeshCookingResult::Enum* condition, bool validateMesh) +{ + const PxU32 originalTriangleCount = _desc.triangles.count; + if(!_desc.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "TriangleMesh::loadFromDesc: desc.isValid() failed!"); + return false; + } + + // verify the mesh params + if(!mParams.midphaseDesc.isValid()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "TriangleMesh::loadFromDesc: mParams.midphaseDesc.isValid() failed!"); + return false; + } + + // Create a local copy that we can modify + PxTriangleMeshDesc desc = _desc; + + // Save simple params + { + // Handle implicit topology + PxU32* topology = NULL; + if(!desc.triangles.data) + { + // We'll create 32-bit indices + desc.flags &= ~PxMeshFlag::e16_BIT_INDICES; + desc.triangles.stride = sizeof(PxU32)*3; + + { + // Non-indexed mesh => create implicit topology + desc.triangles.count = desc.points.count/3; + // Create default implicit topology + topology = PX_NEW_TEMP(PxU32)[desc.points.count]; + for(PxU32 i=0;i* tris = reinterpret_cast*>(mMeshData.mTriangles); + for(PxU32 i=0;imaxIndex) maxIndex = tris[i].v[0]; + if(tris[i].v[1]>maxIndex) maxIndex = tris[i].v[1]; + if(tris[i].v[2]>maxIndex) maxIndex = tris[i].v[2]; + } + + bool force32 = (params.meshPreprocessParams & PxMeshPreprocessingFlag::eFORCE_32BIT_INDICES); + if (maxIndex <= 0xFFFF && !force32) + serialFlags |= (maxIndex <= 0xFF ? Gu::IMSF_8BIT_INDICES : Gu::IMSF_16BIT_INDICES); + writeDword(serialFlags, platformMismatch, stream); + + // Export mesh + writeDword(mMeshData.mNbVertices, platformMismatch, stream); + writeDword(mMeshData.mNbTriangles, platformMismatch, stream); + writeFloatBuffer(&mMeshData.mVertices->x, mMeshData.mNbVertices*3, platformMismatch, stream); + if(serialFlags & Gu::IMSF_8BIT_INDICES) + { + const PxU32* indices = tris->v; + for(PxU32 i=0;iv; + for(PxU32 i=0;iv, mMeshData.mNbTriangles*3, platformMismatch, stream); + + if(mMeshData.mMaterialIndices) + writeWordBuffer(mMeshData.mMaterialIndices, mMeshData.mNbTriangles, platformMismatch, stream); + + if(mMeshData.mFaceRemap) + { + PxU32 maxId = computeMaxIndex(mMeshData.mFaceRemap, mMeshData.mNbTriangles); + writeDword(maxId, platformMismatch, stream); + storeIndices(maxId, mMeshData.mNbTriangles, mMeshData.mFaceRemap, stream, platformMismatch); +// writeIntBuffer(mMeshData.mFaceRemap, mMeshData.mNbTriangles, platformMismatch, stream); + } + + if(mMeshData.mAdjacencies) + writeIntBuffer(mMeshData.mAdjacencies, mMeshData.mNbTriangles*3, platformMismatch, stream); + + // Export midphase structure + saveMidPhaseStructure(stream, platformMismatch); + + // Export local bounds + writeFloat(mMeshData.mGeomEpsilon, platformMismatch, stream); + + writeFloat(mMeshData.mAABB.minimum.x, platformMismatch, stream); + writeFloat(mMeshData.mAABB.minimum.y, platformMismatch, stream); + writeFloat(mMeshData.mAABB.minimum.z, platformMismatch, stream); + writeFloat(mMeshData.mAABB.maximum.x, platformMismatch, stream); + writeFloat(mMeshData.mAABB.maximum.y, platformMismatch, stream); + writeFloat(mMeshData.mAABB.maximum.z, platformMismatch, stream); + + if(mMeshData.mExtraTrigData) + { + writeDword(mMeshData.mNbTriangles, platformMismatch, stream); + // No need to convert those bytes + stream.write(mMeshData.mExtraTrigData, mMeshData.mNbTriangles*sizeof(PxU8)); + } + else + writeDword(0, platformMismatch, stream); + + // GRB write ----------------------------------------------------------------- + if (params.buildGPUData) + { + const PxU32* indices = reinterpret_cast(mMeshData.mGRB_primIndices); + if (serialFlags & Gu::IMSF_8BIT_INDICES) + { + for (PxU32 i = 0; i(mMeshData.mGRB_triIndices), , mMeshData.mNbTriangles*3, platformMismatch, stream); + + //writeIntBuffer(reinterpret_cast(mMeshData.mGRB_triIndices), mMeshData.mNbTriangles*4, platformMismatch, stream); + + writeIntBuffer(reinterpret_cast(mMeshData.mGRB_primAdjacencies), mMeshData.mNbTriangles*4, platformMismatch, stream); + writeIntBuffer(mMeshData.mGRB_faceRemap, mMeshData.mNbTriangles, platformMismatch, stream); + + //Export GPU midphase structure + BV32Tree* bv32Tree = reinterpret_cast(mMeshData.mGRB_BV32Tree); + BV32TriangleMeshBuilder::saveMidPhaseStructure(bv32Tree, stream, platformMismatch); + } + + // End of GRB write ---------------------------------------------------------- + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4996) // permitting use of gatherStrided until we have a replacement. +#endif + +bool TriangleMeshBuilder::importMesh(const PxTriangleMeshDesc& desc,const PxCookingParams& params,PxTriangleMeshCookingResult::Enum* condition, bool validate) +{ + //convert and clean the input mesh + //this is where the mesh data gets copied from user mem to our mem + + PxVec3* verts = mMeshData.allocateVertices(desc.points.count); + Gu::TriangleT* tris = reinterpret_cast*>(mMeshData.allocateTriangles(desc.triangles.count, true, PxU32(params.buildGPUData))); + + //copy, and compact to get rid of strides: + Cooking::gatherStrided(desc.points.data, verts, mMeshData.mNbVertices, sizeof(PxVec3), desc.points.stride); + +#if PX_CHECKED + // PT: check all input vertices are valid + for(PxU32 i=0;i* dest = tris; + const Gu::TriangleT* pastLastDest = tris + mMeshData.mNbTriangles; + const PxU8* source = reinterpret_cast(desc.triangles.data); + + //4 combos of 16 vs 32 and flip vs no flip + PxU32 c = (desc.flags & PxMeshFlag::eFLIPNORMALS)?PxU32(1):0; + if (desc.flags & PxMeshFlag::e16_BIT_INDICES) + { + //index stride conversion is also needed, I don't think flexicopy can do that for us. + while (dest < pastLastDest) + { + const PxU16 * trig16 = reinterpret_cast(source); + dest->v[0] = trig16[0]; + dest->v[1] = trig16[1+c]; + dest->v[2] = trig16[2-c]; + dest ++; + source += desc.triangles.stride; + } + } + else + { + while (dest < pastLastDest) + { + const PxU32 * trig32 = reinterpret_cast(source); + dest->v[0] = trig32[0]; + dest->v[1] = trig32[1+c]; + dest->v[2] = trig32[2-c]; + dest ++; + source += desc.triangles.stride; + } + } + + //copy the material index list if any: + if(desc.materialIndices.data) + { + PxMaterialTableIndex* materials = mMeshData.allocateMaterials(); + Cooking::gatherStrided(desc.materialIndices.data, materials, mMeshData.mNbTriangles, sizeof(PxMaterialTableIndex), desc.materialIndices.stride); + + // Check material indices + for(PxU32 i=0;i it works with a clean mesh + + if (!(params.meshPreprocessParams & PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH) || validate) + { + if(!cleanMesh(validate, condition)) + { + if(!validate) + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "cleaning the mesh failed"); + return false; + } + } + else + { + // we need to fill the remap table if no cleaning was done + if(params.suppressTriangleMeshRemapTable == false) + { + PX_ASSERT(mMeshData.mFaceRemap == NULL); + mMeshData.mFaceRemap = PX_NEW(PxU32)[mMeshData.mNbTriangles]; + for (PxU32 i = 0; i < mMeshData.mNbTriangles; i++) + mMeshData.mFaceRemap[i] = i; + } + } + return true; +} + +#if PX_VC +#pragma warning(pop) +#endif +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void TriangleMeshBuilder::checkMeshIndicesSize() +{ + Gu::TriangleMeshData& m = mMeshData; + + // check if we can change indices from 32bits to 16bits + if(m.mNbVertices <= 0xffff && !m.has16BitIndices()) + { + const PxU32 numTriangles = m.mNbTriangles; + PxU32* PX_RESTRICT indices32 = reinterpret_cast (m.mTriangles); + PxU32* PX_RESTRICT grbIndices32 = reinterpret_cast(m.mGRB_primIndices); + + m.mTriangles = 0; // force a realloc + m.allocateTriangles(numTriangles, false, grbIndices32 != NULL ? 1u : 0u); + PX_ASSERT(m.has16BitIndices()); // realloc'ing without the force32bit flag changed it. + + PxU16* PX_RESTRICT indices16 = reinterpret_cast (m.mTriangles); + for (PxU32 i = 0; i < numTriangles * 3; i++) + indices16[i] = Ps::to16(indices32[i]); + + PX_FREE(indices32); + + if (grbIndices32) + { + PxU16* PX_RESTRICT grbIndices16 = reinterpret_cast (m.mGRB_primIndices); + for (PxU32 i = 0; i < numTriangles * 3; i++) + grbIndices16[i] = Ps::to16(grbIndices32[i]); + } + + PX_FREE(grbIndices32); + + onMeshIndexFormatChange(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +BV4TriangleMeshBuilder::BV4TriangleMeshBuilder(const PxCookingParams& params) : TriangleMeshBuilder(mData, params) +{ +} + +BV4TriangleMeshBuilder::~BV4TriangleMeshBuilder() +{ +} + +void BV4TriangleMeshBuilder::onMeshIndexFormatChange() +{ + IndTri32* triangles32 = NULL; + IndTri16* triangles16 = NULL; + if(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) + triangles16 = reinterpret_cast(mMeshData.mTriangles); + else + triangles32 = reinterpret_cast(mMeshData.mTriangles); + + mData.mMeshInterface.setPointers(triangles32, triangles16, mMeshData.mVertices); +} + +void BV4TriangleMeshBuilder::createMidPhaseStructure() +{ + const float gBoxEpsilon = 2e-4f; +// const float gBoxEpsilon = 0.1f; + mData.mMeshInterface.initRemap(); + mData.mMeshInterface.setNbVertices(mMeshData.mNbVertices); + mData.mMeshInterface.setNbTriangles(mMeshData.mNbTriangles); + + IndTri32* triangles32 = NULL; + IndTri16* triangles16 = NULL; + if (mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) + { + triangles16 = reinterpret_cast(mMeshData.mTriangles); + } + else + { + triangles32 = reinterpret_cast(mMeshData.mTriangles); + } + + mData.mMeshInterface.setPointers(triangles32, triangles16, mMeshData.mVertices); + + const PxU32 nbTrisPerLeaf = (mParams.midphaseDesc.getType() == PxMeshMidPhase::eBVH34) ? mParams.midphaseDesc.mBVH34Desc.numPrimsPerLeaf : 4; + + if(!BuildBV4Ex(mData.mBV4Tree, mData.mMeshInterface, gBoxEpsilon, nbTrisPerLeaf)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV4 tree failed to build."); + return; + } + +// remapTopology(mData.mMeshInterface); + + const PxU32* order = mData.mMeshInterface.getRemap(); + if(mMeshData.mMaterialIndices) + { + PxMaterialTableIndex* newMat = PX_NEW(PxMaterialTableIndex)[mMeshData.mNbTriangles]; + for(PxU32 i=0;i1 we now do the same as for other structures in the SDK: the data is + // exported either as little or big-endian depending on the passed parameter. + const PxU32 bv4StructureVersion = 3; + + writeChunk('B', 'V', '4', ' ', stream); + writeDword(bv4StructureVersion, mismatch, stream); + + writeFloat(mData.mBV4Tree.mLocalBounds.mCenter.x, mismatch, stream); + writeFloat(mData.mBV4Tree.mLocalBounds.mCenter.y, mismatch, stream); + writeFloat(mData.mBV4Tree.mLocalBounds.mCenter.z, mismatch, stream); + writeFloat(mData.mBV4Tree.mLocalBounds.mExtentsMagnitude, mismatch, stream); + + writeDword(mData.mBV4Tree.mInitData, mismatch, stream); + + writeFloat(mData.mBV4Tree.mCenterOrMinCoeff.x, mismatch, stream); + writeFloat(mData.mBV4Tree.mCenterOrMinCoeff.y, mismatch, stream); + writeFloat(mData.mBV4Tree.mCenterOrMinCoeff.z, mismatch, stream); + writeFloat(mData.mBV4Tree.mExtentsOrMaxCoeff.x, mismatch, stream); + writeFloat(mData.mBV4Tree.mExtentsOrMaxCoeff.y, mismatch, stream); + writeFloat(mData.mBV4Tree.mExtentsOrMaxCoeff.z, mismatch, stream); + + // PT: version 3 + writeDword(PxU32(mData.mBV4Tree.mQuantized), mismatch, stream); + + writeDword(mData.mBV4Tree.mNbNodes, mismatch, stream); + +#ifdef GU_BV4_USE_SLABS + // PT: we use BVDataPacked to get the size computation right, but we're dealing with BVDataSwizzled here! + #ifdef GU_BV4_COMPILE_NON_QUANTIZED_TREE + const PxU32 NodeSize = mData.mBV4Tree.mQuantized ? sizeof(BVDataPackedQ) : sizeof(BVDataPackedNQ); + #else + const PxU32 NodeSize = sizeof(BVDataPackedQ); + #endif + stream.write(mData.mBV4Tree.mNodes, NodeSize*mData.mBV4Tree.mNbNodes); + PX_ASSERT(!mismatch); +#else + #error Not implemented +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void BV32TriangleMeshBuilder::createMidPhaseStructure(const PxCookingParams& params, Gu::TriangleMeshData& meshData, Gu::BV32Tree& bv32Tree) +{ + const float gBoxEpsilon = 2e-4f; + + Gu::SourceMesh meshInterface; + // const float gBoxEpsilon = 0.1f; + meshInterface.initRemap(); + meshInterface.setNbVertices(meshData.mNbVertices); + meshInterface.setNbTriangles(meshData.mNbTriangles); + + PX_ASSERT(!(meshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES)); + + IndTri32* triangles32 = reinterpret_cast(meshData.mGRB_primIndices); + + meshInterface.setPointers(triangles32, NULL, meshData.mVertices); + + PxU32 nbTrisPerLeaf = 32; + + if (!BuildBV32Ex(bv32Tree, meshInterface, gBoxEpsilon, nbTrisPerLeaf)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "BV32 tree failed to build."); + return; + } + + const PxU32* order = meshInterface.getRemap(); + + if (!params.suppressTriangleMeshRemapTable || params.buildGPUData) + { + PxU32* newMap = PX_NEW(PxU32)[meshData.mNbTriangles]; + for (PxU32 i = 0; i1 we now do the same as for other structures in the SDK: the data is + // exported either as little or big-endian depending on the passed parameter. + const PxU32 bv32StructureVersion = 2; + + writeChunk('B', 'V', '3', '2', stream); + writeDword(bv32StructureVersion, mismatch, stream); + + writeFloat(bv32Tree->mLocalBounds.mCenter.x, mismatch, stream); + writeFloat(bv32Tree->mLocalBounds.mCenter.y, mismatch, stream); + writeFloat(bv32Tree->mLocalBounds.mCenter.z, mismatch, stream); + writeFloat(bv32Tree->mLocalBounds.mExtentsMagnitude, mismatch, stream); + + writeDword(bv32Tree->mInitData, mismatch, stream); + + writeDword(bv32Tree->mNbPackedNodes, mismatch, stream); + + PX_ASSERT(bv32Tree->mNbPackedNodes > 0); + for (PxU32 i = 0; i < bv32Tree->mNbPackedNodes; ++i) + { + BV32DataPacked& node = bv32Tree->mPackedNodes[i]; + + const PxU32 nbElements = node.mNbNodes * 4; + writeDword(node.mNbNodes, mismatch, stream); + WriteDwordBuffer(node.mData, node.mNbNodes, mismatch, stream); + writeFloatBuffer(&node.mCenter[0].x, nbElements, mismatch, stream); + writeFloatBuffer(&node.mExtents[0].x, nbElements, mismatch, stream); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +RTreeTriangleMeshBuilder::RTreeTriangleMeshBuilder(const PxCookingParams& params) : TriangleMeshBuilder(mData, params) +{ +} + +RTreeTriangleMeshBuilder::~RTreeTriangleMeshBuilder() +{ +} + +struct RTreeCookerRemap : RTreeCooker::RemapCallback +{ + PxU32 mNbTris; + RTreeCookerRemap(PxU32 numTris) : mNbTris(numTris) + { + } + + virtual void remap(PxU32* val, PxU32 start, PxU32 leafCount) + { + PX_ASSERT(leafCount > 0); + PX_ASSERT(leafCount <= 16); // sanity check + PX_ASSERT(start < mNbTris); + PX_ASSERT(start+leafCount <= mNbTris); + PX_ASSERT(val); + LeafTriangles lt; + // here we remap from ordered leaf index in the rtree to index in post-remap in triangles + // this post-remap will happen later + lt.SetData(leafCount, start); + *val = lt.Data; + } +}; + +void RTreeTriangleMeshBuilder::createMidPhaseStructure() +{ + const PxReal meshSizePerformanceTradeOff = mParams.midphaseDesc.mBVH33Desc.meshSizePerformanceTradeOff; + const PxMeshCookingHint::Enum meshCookingHint = mParams.midphaseDesc.mBVH33Desc.meshCookingHint; + + Array resultPermute; + RTreeCookerRemap rc(mMeshData.mNbTriangles); + RTreeCooker::buildFromTriangles( + mData.mRTree, + mMeshData.mVertices, mMeshData.mNbVertices, + (mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? reinterpret_cast(mMeshData.mTriangles) : NULL, + !(mMeshData.mFlags & PxTriangleMeshFlag::e16_BIT_INDICES) ? reinterpret_cast(mMeshData.mTriangles) : NULL, + mMeshData.mNbTriangles, resultPermute, &rc, meshSizePerformanceTradeOff, meshCookingHint); + + PX_ASSERT(resultPermute.size() == mMeshData.mNbTriangles); + + remapTopology(resultPermute.begin()); +} + +void RTreeTriangleMeshBuilder::saveMidPhaseStructure(PxOutputStream& stream, bool mismatch) const +{ + // PT: in version 1 we defined "mismatch" as: + // const bool mismatch = (littleEndian() == 1); + // i.e. the data was *always* saved to file in big-endian format no matter what. + // In version>1 we now do the same as for other structures in the SDK: the data is + // exported either as little or big-endian depending on the passed parameter. + const PxU32 rtreeStructureVersion = 2; + + // save the RTree root structure followed immediately by RTreePage pages to an output stream + writeChunk('R', 'T', 'R', 'E', stream); + + writeDword(rtreeStructureVersion, mismatch, stream); + const RTree& d = mData.mRTree; + writeFloatBuffer(&d.mBoundsMin.x, 4, mismatch, stream); + writeFloatBuffer(&d.mBoundsMax.x, 4, mismatch, stream); + writeFloatBuffer(&d.mInvDiagonal.x, 4, mismatch, stream); + writeFloatBuffer(&d.mDiagonalScaler.x, 4, mismatch, stream); + writeDword(d.mPageSize, mismatch, stream); + writeDword(d.mNumRootPages, mismatch, stream); + writeDword(d.mNumLevels, mismatch, stream); + writeDword(d.mTotalNodes, mismatch, stream); + writeDword(d.mTotalPages, mismatch, stream); + PxU32 unused = 0; writeDword(unused, mismatch, stream); // backwards compatibility + for (PxU32 j = 0; j < d.mTotalPages; j++) + { + writeFloatBuffer(d.mPages[j].minx, RTREE_N, mismatch, stream); + writeFloatBuffer(d.mPages[j].miny, RTREE_N, mismatch, stream); + writeFloatBuffer(d.mPages[j].minz, RTREE_N, mismatch, stream); + writeFloatBuffer(d.mPages[j].maxx, RTREE_N, mismatch, stream); + writeFloatBuffer(d.mPages[j].maxy, RTREE_N, mismatch, stream); + writeFloatBuffer(d.mPages[j].maxz, RTREE_N, mismatch, stream); + WriteDwordBuffer(d.mPages[j].ptrs, RTREE_N, mismatch, stream); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +} diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h new file mode 100644 index 000000000..4b187ec9e --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h @@ -0,0 +1,123 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_TriangleMeshBUILDER +#define PX_COLLISION_TriangleMeshBUILDER + +#include "GuMeshData.h" +#include "cooking/PxCooking.h" +#include "MeshBuilder.h" + +namespace physx +{ + namespace Gu + { + class EdgeListBuilder; + } + + class TriangleMeshBuilder : public MeshBulider + { + public: + TriangleMeshBuilder(Gu::TriangleMeshData& mesh, const PxCookingParams& params); + virtual ~TriangleMeshBuilder(); + + virtual PxMeshMidPhase::Enum getMidphaseID() const = 0; + // Called by base code when midphase structure should be built + virtual void createMidPhaseStructure() = 0; + + // Called by base code when midphase structure should be saved + virtual void saveMidPhaseStructure(PxOutputStream& stream, bool mismatch) const = 0; + // Called by base code when mesh index format has changed and the change should be reflected in midphase structure + virtual void onMeshIndexFormatChange() {} + + bool cleanMesh(bool validate, PxTriangleMeshCookingResult::Enum* condition); + void remapTopology(const PxU32* order); + + void createSharedEdgeData(bool buildAdjacencies, bool buildActiveEdges); + + void recordTriangleIndices(); + void createGRBMidPhaseAndData(const PxU32 originalTriangleCount); + void createGRBData(); + + bool loadFromDesc(const PxTriangleMeshDesc&, PxTriangleMeshCookingResult::Enum* condition, bool validate = false); + bool save(PxOutputStream& stream, bool platformMismatch, const PxCookingParams& params) const; + void checkMeshIndicesSize(); + PX_FORCE_INLINE Gu::TriangleMeshData& getMeshData() { return mMeshData; } + protected: + //void computeLocalBounds(); + bool importMesh(const PxTriangleMeshDesc& desc, const PxCookingParams& params, PxTriangleMeshCookingResult::Enum* condition, bool validate = false); + + TriangleMeshBuilder& operator=(const TriangleMeshBuilder&); + Gu::EdgeListBuilder* edgeList; + const PxCookingParams& mParams; + Gu::TriangleMeshData& mMeshData; + + void releaseEdgeList(); + void createEdgeList(); + }; + + class RTreeTriangleMeshBuilder : public TriangleMeshBuilder + { + public: + RTreeTriangleMeshBuilder(const PxCookingParams& params); + virtual ~RTreeTriangleMeshBuilder(); + + virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH33; } + virtual void createMidPhaseStructure(); + virtual void saveMidPhaseStructure(PxOutputStream& stream, bool mismatch) const; + + Gu::RTreeTriangleData mData; + }; + + class BV4TriangleMeshBuilder : public TriangleMeshBuilder + { + public: + BV4TriangleMeshBuilder(const PxCookingParams& params); + virtual ~BV4TriangleMeshBuilder(); + + virtual PxMeshMidPhase::Enum getMidphaseID() const { return PxMeshMidPhase::eBVH34; } + virtual void createMidPhaseStructure(); + virtual void saveMidPhaseStructure(PxOutputStream& stream, bool mismatch) const; + virtual void onMeshIndexFormatChange(); + + Gu::BV4TriangleData mData; + }; + + class BV32TriangleMeshBuilder + { + public: + static void createMidPhaseStructure(const PxCookingParams& params, Gu::TriangleMeshData& meshData, Gu::BV32Tree& bv32Tree); + static void saveMidPhaseStructure(Gu::BV32Tree* tree, PxOutputStream& stream, bool mismatch); + }; + + +} + +#endif diff --git a/src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp b/src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp new file mode 100644 index 000000000..177dbe40b --- /dev/null +++ b/src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp @@ -0,0 +1,82 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "windows/PxWindowsDelayLoadHook.h" +#include "windows/PsWindowsInclude.h" +#include "windows/CmWindowsLoadLibrary.h" + +// Prior to Visual Studio 2015 Update 3, these hooks were non-const. +#define DELAYIMP_INSECURE_WRITABLE_HOOKS +#include + +static const physx::PxDelayLoadHook* gDelayLoadHook = NULL; + +void physx::PxSetPhysXCookingDelayLoadHook(const physx::PxDelayLoadHook* hook) +{ + gDelayLoadHook = hook; +} + +using namespace physx; + +#pragma comment(lib, "delayimp") + +FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli) +{ + switch (dliNotify) { + case dliStartProcessing : + break; + + case dliNotePreLoadLibrary : + { + return Cm::physXCommonDliNotePreLoadLibrary(pdli->szDll,gDelayLoadHook); + } + break; + + case dliNotePreGetProcAddress : + break; + + case dliFailLoadLib : + break; + + case dliFailGetProc : + break; + + case dliNoteEndProcessing : + break; + + default : + + return NULL; + } + + return NULL; +} + +PfnDliHook __pfnDliNotifyHook2 = delayHook; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp b/src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp new file mode 100644 index 000000000..ae638fb47 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxBounds3.h" +#include "PxBroadPhaseExt.h" +#include "PsFoundation.h" +#include "CmPhysXCommon.h" + +using namespace physx; + +PxU32 PxBroadPhaseExt::createRegionsFromWorldBounds(PxBounds3* regions, const PxBounds3& globalBounds, PxU32 nbSubdiv, PxU32 upAxis) +{ + PX_CHECK_MSG(globalBounds.isValid(), "PxBroadPhaseExt::createRegionsFromWorldBounds(): invalid bounds provided!"); + PX_CHECK_MSG(upAxis<3, "PxBroadPhaseExt::createRegionsFromWorldBounds(): invalid up-axis provided!"); + + const PxVec3& min = globalBounds.minimum; + const PxVec3& max = globalBounds.maximum; + const float dx = (max.x - min.x) / float(nbSubdiv); + const float dy = (max.y - min.y) / float(nbSubdiv); + const float dz = (max.z - min.z) / float(nbSubdiv); + PxU32 nbRegions = 0; + PxVec3 currentMin, currentMax; + for(PxU32 j=0;j releasableObjects; + + for (PxU32 i = 0; i < collection.getNbObjects(); ++i) + { + PxBase* s = &collection.getObject(i); + // pruning structure must be released before its actors + if(s->is()) + { + if(!releasableObjects.empty()) + { + PxBase* first = releasableObjects[0]; + releasableObjects.pushBack(first); + releasableObjects[0] = s; + } + } + else + { + if (s->isReleasable() && (releaseExclusiveShapes || !s->is() || !s->is()->isExclusive())) + releasableObjects.pushBack(s); + } + } + + for (PxU32 i = 0; i < releasableObjects.size(); ++i) + releasableObjects[i]->release(); + + while (collection.getNbObjects() > 0) + collection.remove(collection.getObject(0)); +} + + +void PxCollectionExt::remove(PxCollection& collection, PxType concreteType, PxCollection* to) +{ + shdfnd::Array removeObjects; + + for (PxU32 i = 0; i < collection.getNbObjects(); i++) + { + PxBase& object = collection.getObject(i); + if(concreteType == object.getConcreteType()) + { + if(to) + to->add(object); + + removeObjects.pushBack(&object); + } + } + + for (PxU32 i = 0; i < removeObjects.size(); ++i) + collection.remove(*removeObjects[i]); +} + +PxCollection* PxCollectionExt::createCollection(PxPhysics& physics) +{ + PxCollection* collection = PxCreateCollection(); + if (!collection) + return NULL; + + // Collect convexes + { + shdfnd::Array objects(physics.getNbConvexMeshes()); + const PxU32 nb = physics.getConvexMeshes(objects.begin(), objects.size()); + PX_ASSERT(nb == objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + // Collect triangle meshes + { + shdfnd::Array objects(physics.getNbTriangleMeshes()); + const PxU32 nb = physics.getTriangleMeshes(objects.begin(), objects.size()); + + PX_ASSERT(nb == objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + // Collect heightfields + { + shdfnd::Array objects(physics.getNbHeightFields()); + const PxU32 nb = physics.getHeightFields(objects.begin(), objects.size()); + + PX_ASSERT(nb == objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + // Collect materials + { + shdfnd::Array objects(physics.getNbMaterials()); + const PxU32 nb = physics.getMaterials(objects.begin(), objects.size()); + + PX_ASSERT(nb == objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + // Collect shapes + { + shdfnd::Array objects(physics.getNbShapes()); + const PxU32 nb = physics.getShapes(objects.begin(), objects.size()); + + PX_ASSERT(nb == objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + return collection; +} + +PxCollection* PxCollectionExt::createCollection(PxScene& scene) +{ + PxCollection* collection = PxCreateCollection(); + if (!collection) + return NULL; + + // Collect actors + { + PxActorTypeFlags selectionFlags = PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC; + + + shdfnd::Array objects(scene.getNbActors(selectionFlags)); + const PxU32 nb = scene.getActors(selectionFlags, objects.begin(), objects.size()); + + PX_ASSERT(nb==objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + + // Collect constraints + { + shdfnd::Array objects(scene.getNbConstraints()); + const PxU32 nb = scene.getConstraints(objects.begin(), objects.size()); + + PX_ASSERT(nb==objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;i(objects[i]->getExternalReference(typeId)); + if(typeId == PxConstraintExtIDs::eJOINT) + collection->add(*joint); + } + } + + // Collect articulations + { + shdfnd::Array objects(scene.getNbArticulations()); + const PxU32 nb = scene.getArticulations(objects.begin(), objects.size()); + + PX_ASSERT(nb==objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + // Collect aggregates + { + shdfnd::Array objects(scene.getNbAggregates()); + const PxU32 nb = scene.getAggregates(objects.begin(), objects.size()); + + PX_ASSERT(nb==objects.size()); + PX_UNUSED(nb); + + for(PxU32 i=0;iadd(*objects[i]); + } + + return collection; +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h b/src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h new file mode 100644 index 000000000..f9cdb45a2 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h @@ -0,0 +1,387 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_CONSTRAINT_HELPER_H +#define NP_CONSTRAINT_HELPER_H + +#include "foundation/PxAssert.h" +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" +#include "PxJointLimit.h" +#include "ExtJoint.h" + +namespace physx +{ +namespace Ext +{ + namespace joint + { + PX_INLINE void computeJointFrames(PxTransform& cA2w, PxTransform& cB2w, const JointData& data, const PxTransform& bA2w, const PxTransform& bB2w) + { + PX_ASSERT(bA2w.isValid() && bB2w.isValid()); + + cA2w = bA2w.transform(data.c2b[0]); + cB2w = bB2w.transform(data.c2b[1]); + + PX_ASSERT(cA2w.isValid() && cB2w.isValid()); + } + + PX_INLINE void computeDerived(const JointData& data, + const PxTransform& bA2w, const PxTransform& bB2w, + PxTransform& cA2w, PxTransform& cB2w, PxTransform& cB2cA, + bool useShortestPath=true) + { + computeJointFrames(cA2w, cB2w, data, bA2w, bB2w); + + if(useShortestPath) + { + if(cA2w.q.dot(cB2w.q)<0.0f) // minimum error quat + cB2w.q = -cB2w.q; + } + + cB2cA = cA2w.transformInv(cB2w); + PX_ASSERT(cB2cA.isValid()); + } + + PX_INLINE PxVec3 truncateLinear(const PxVec3& in, PxReal tolerance, bool& truncated) + { + const PxReal m = in.magnitudeSquared(); + truncated = m>tolerance * tolerance; + return truncated ? in * PxRecipSqrt(m) * tolerance : in; + } + + PX_INLINE PxQuat truncateAngular(const PxQuat& in, PxReal sinHalfTol, PxReal cosHalfTol, bool& truncated) + { + truncated = false; + + if(sinHalfTol > 0.9999f) // fixes numerical tolerance issue of projecting because quat is not exactly normalized + return in; + + const PxQuat q = in.w>=0.0f ? in : -in; + + const PxVec3 im = q.getImaginaryPart(); + const PxReal m = im.magnitudeSquared(); + truncated = m>sinHalfTol*sinHalfTol; + if(!truncated) + return in; + + const PxVec3 outV = im * sinHalfTol * PxRecipSqrt(m); + return PxQuat(outV.x, outV.y, outV.z, cosHalfTol); + } + + PX_FORCE_INLINE void projectTransforms(PxTransform& bA2w, PxTransform& bB2w, + const PxTransform& cA2w, const PxTransform& cB2w, + const PxTransform& cB2cA, const JointData& data, bool projectToA) + { + PX_ASSERT(cB2cA.isValid()); + + // normalization here is unfortunate: long chains of projected constraints can result in + // accumulation of error in the quaternion which eventually leaves the quaternion + // magnitude outside the validation range. The approach here is slightly overconservative + // in that we could just normalize the quaternions which are out of range, but since we + // regard projection as an occasional edge case it shouldn't be perf-sensitive, and + // this way we maintain the invariant (also maintained by the dynamics integrator) that + // body quats are properly normalized up to FP error. + + if(projectToA) + { + bB2w = cA2w.transform(cB2cA.transform(data.c2b[1].getInverse())); + bB2w.q.normalize(); + } + else + { + bA2w = cB2w.transform(cB2cA.transformInv(data.c2b[0].getInverse())); + bA2w.q.normalize(); + } + + PX_ASSERT(bA2w.isValid()); + PX_ASSERT(bB2w.isValid()); + } + + PX_INLINE void computeJacobianAxes(PxVec3 row[3], const PxQuat& qa, const PxQuat& qb) + { + // Compute jacobian matrix for (qa* qb) [[* means conjugate in this expr]] + // d/dt (qa* qb) = 1/2 L(qa*) R(qb) (omega_b - omega_a) + // result is L(qa*) R(qb), where L(q) and R(q) are left/right q multiply matrix + + const PxReal wa = qa.w, wb = qb.w; + const PxVec3 va(qa.x,qa.y,qa.z), vb(qb.x,qb.y,qb.z); + + const PxVec3 c = vb*wa + va*wb; + const PxReal d0 = wa*wb; + const PxReal d1 = va.dot(vb); + const PxReal d = d0 - d1; + + row[0] = (va * vb.x + vb * va.x + PxVec3(d, c.z, -c.y)) * 0.5f; + row[1] = (va * vb.y + vb * va.y + PxVec3(-c.z, d, c.x)) * 0.5f; + row[2] = (va * vb.z + vb * va.z + PxVec3(c.y, -c.x, d)) * 0.5f; + + if((d0 + d1) != 0.0f) // check if relative rotation is 180 degrees which can lead to singular matrix + return; + else + { + row[0].x += PX_EPS_F32; + row[1].y += PX_EPS_F32; + row[2].z += PX_EPS_F32; + } + } + + PX_FORCE_INLINE Px1DConstraint* _linear(const PxVec3& axis, const PxVec3& ra, const PxVec3& rb, PxReal posErr, PxConstraintSolveHint::Enum hint, Px1DConstraint* c) + { + c->solveHint = PxU16(hint); + c->linear0 = axis; + c->angular0 = ra.cross(axis); + c->linear1 = axis; + c->angular1 = rb.cross(axis); + c->geometricError = posErr; + PX_ASSERT(c->linear0.isFinite()); + PX_ASSERT(c->linear1.isFinite()); + PX_ASSERT(c->angular0.isFinite()); + PX_ASSERT(c->angular1.isFinite()); + return c; + } + + PX_FORCE_INLINE Px1DConstraint* _angular(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint, Px1DConstraint* c) + { + c->solveHint = PxU16(hint); + c->linear0 = PxVec3(0.0f); + c->angular0 = axis; + c->linear1 = PxVec3(0.0f); + c->angular1 = axis; + c->geometricError = posErr; + c->flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT; + return c; + } + + class ConstraintHelper + { + Px1DConstraint* mConstraints; + Px1DConstraint* mCurrent; + PxVec3 mRa, mRb; + PxVec3 mCA2w, mCB2w; + + public: + ConstraintHelper(Px1DConstraint* c, const PxVec3& ra, const PxVec3& rb) + : mConstraints(c), mCurrent(c), mRa(ra), mRb(rb) {} + + ConstraintHelper(Px1DConstraint* c, PxConstraintInvMassScale& invMassScale, + PxTransform& cA2w, PxTransform& cB2w, + PxVec3& body0WorldOffset, + const JointData& data, const PxTransform& bA2w, const PxTransform& bB2w) + : mConstraints(c), mCurrent(c) + { + invMassScale = data.invMassScale; + + computeJointFrames(cA2w, cB2w, data, bA2w, bB2w); + + body0WorldOffset = cB2w.p - bA2w.p; + + mRa = cB2w.p - bA2w.p; + mRb = cB2w.p - bB2w.p; + + mCA2w = cA2w.p; + mCB2w = cB2w.p; + } + + PX_FORCE_INLINE const PxVec3& getRa() const { return mRa; } + PX_FORCE_INLINE const PxVec3& getRb() const { return mRb; } + + // hard linear & angular + PX_FORCE_INLINE void linearHard(const PxVec3& axis, PxReal posErr) + { + Px1DConstraint* c = linear(axis, posErr, PxConstraintSolveHint::eEQUALITY); + c->flags |= Px1DConstraintFlag::eOUTPUT_FORCE; + } + + PX_FORCE_INLINE void angularHard(const PxVec3& axis, PxReal posErr) + { + Px1DConstraint* c = angular(axis, posErr, PxConstraintSolveHint::eEQUALITY); + c->flags |= Px1DConstraintFlag::eOUTPUT_FORCE; + } + + // limited linear & angular + PX_FORCE_INLINE void linearLimit(const PxVec3& axis, PxReal ordinate, PxReal limitValue, const PxJointLimitParameters& limit) + { + const PxReal pad = limit.isSoft() ? 0.0f : limit.contactDistance; + + if(ordinate + pad > limitValue) + addLimit(linear(axis, limitValue - ordinate, PxConstraintSolveHint::eNONE), limit); + } + + PX_FORCE_INLINE void angularLimit(const PxVec3& axis, PxReal ordinate, PxReal limitValue, PxReal pad, const PxJointLimitParameters& limit) + { + if(limit.isSoft()) + pad = 0.0f; + + if(ordinate + pad > limitValue) + addLimit(angular(axis, limitValue - ordinate, PxConstraintSolveHint::eNONE), limit); + } + + PX_FORCE_INLINE void angularLimit(const PxVec3& axis, PxReal error, const PxJointLimitParameters& limit) + { + addLimit(angular(axis, error, PxConstraintSolveHint::eNONE), limit); + } + + PX_FORCE_INLINE void anglePair(PxReal angle, PxReal lower, PxReal upper, PxReal pad, const PxVec3& axis, const PxJointLimitParameters& limit) + { + PX_ASSERT(lower upper-pad) + angularLimit(axis, (upper - angle), limit); + } + + // driven linear & angular + + PX_FORCE_INLINE void linear(const PxVec3& axis, PxReal velTarget, PxReal error, const PxD6JointDrive& drive) + { + addDrive(linear(axis, error, PxConstraintSolveHint::eNONE), velTarget, drive); + } + + PX_FORCE_INLINE void angular(const PxVec3& axis, PxReal velTarget, PxReal error, const PxD6JointDrive& drive, PxConstraintSolveHint::Enum hint = PxConstraintSolveHint::eNONE) + { + addDrive(angular(axis, error, hint), velTarget, drive); + } + + PX_FORCE_INLINE PxU32 getCount() const { return PxU32(mCurrent - mConstraints); } + + void prepareLockedAxes(const PxQuat& qA, const PxQuat& qB, const PxVec3& cB2cAp, PxU32 lin, PxU32 ang, + PxVec3& raOut, PxVec3& rbOut) + { + Px1DConstraint* current = mCurrent; + + PxVec3 errorVector(0.f); + + PxVec3 ra = mRa; + PxVec3 rb = mRb; + if(lin) + { + const PxMat33 axes(qA); + + if(lin&1) errorVector -= axes.column0 * cB2cAp.x; + if(lin&2) errorVector -= axes.column1 * cB2cAp.y; + if(lin&4) errorVector -= axes.column2 * cB2cAp.z; + + ra += errorVector; + + if(lin&1) _linear(axes.column0, ra, rb, -cB2cAp.x, PxConstraintSolveHint::eEQUALITY, current++); + if(lin&2) _linear(axes.column1, ra, rb, -cB2cAp.y, PxConstraintSolveHint::eEQUALITY, current++); + if(lin&4) _linear(axes.column2, ra, rb, -cB2cAp.z, PxConstraintSolveHint::eEQUALITY, current++); + + + } + + if (ang) + { + const PxQuat qB2qA = qA.getConjugate() * qB; + + PxVec3 row[3]; + computeJacobianAxes(row, qA, qB); + if (ang & 1) _angular(row[0], -qB2qA.x, PxConstraintSolveHint::eEQUALITY, current++); + if (ang & 2) _angular(row[1], -qB2qA.y, PxConstraintSolveHint::eEQUALITY, current++); + if (ang & 4) _angular(row[2], -qB2qA.z, PxConstraintSolveHint::eEQUALITY, current++); + } + + raOut = ra; + rbOut = rb; + + for(Px1DConstraint* front = mCurrent; front < current; front++) + front->flags |= Px1DConstraintFlag::eOUTPUT_FORCE; + + mCurrent = current; + } + + PX_FORCE_INLINE Px1DConstraint* getConstraintRow() + { + return mCurrent++; + } + + private: + PX_FORCE_INLINE Px1DConstraint* linear(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint) + { + return _linear(axis, mRa, mRb, posErr, hint, mCurrent++); + } + + PX_FORCE_INLINE Px1DConstraint* angular(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint) + { + return _angular(axis, posErr, hint, mCurrent++); + } + + void addLimit(Px1DConstraint* c, const PxJointLimitParameters& limit) + { + PxU16 flags = PxU16(c->flags | Px1DConstraintFlag::eOUTPUT_FORCE); + + if(limit.isSoft()) + { + flags |= Px1DConstraintFlag::eSPRING; + c->mods.spring.stiffness = limit.stiffness; + c->mods.spring.damping = limit.damping; + } + else + { + c->solveHint = PxConstraintSolveHint::eINEQUALITY; + c->mods.bounce.restitution = limit.restitution; + c->mods.bounce.velocityThreshold = limit.bounceThreshold; + if(c->geometricError>0.0f) + flags |= Px1DConstraintFlag::eKEEPBIAS; + if(limit.restitution>0.0f) + flags |= Px1DConstraintFlag::eRESTITUTION; + } + + c->flags = flags; + c->minImpulse = 0.0f; + } + + void addDrive(Px1DConstraint* c, PxReal velTarget, const PxD6JointDrive& drive) + { + c->velocityTarget = velTarget; + + PxU16 flags = PxU16(c->flags | Px1DConstraintFlag::eSPRING | Px1DConstraintFlag::eHAS_DRIVE_LIMIT); + if(drive.flags & PxD6JointDriveFlag::eACCELERATION) + flags |= Px1DConstraintFlag::eACCELERATION_SPRING; + c->flags = flags; + c->mods.spring.stiffness = drive.stiffness; + c->mods.spring.damping = drive.damping; + + c->minImpulse = -drive.forceLimit; + c->maxImpulse = drive.forceLimit; + + PX_ASSERT(c->linear0.isFinite()); + PX_ASSERT(c->angular0.isFinite()); + } + }; + } +} // namespace + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp new file mode 100644 index 000000000..449eb6724 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp @@ -0,0 +1,250 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtContactJoint.h" +#include "ExtConstraintHelper.h" +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxContactJoint* physx::PxContactJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxContactJointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxContactJointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxContactJointCreate: actors must be different"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxContactJointCreate: at least one actor must be dynamic"); + + ContactJoint* j; + PX_NEW_SERIALIZED(j, ContactJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + if (j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +PxVec3 ContactJoint::getContact() const +{ + return data().contact; +} + +void ContactJoint::setContact(const PxVec3& contact) +{ + PX_CHECK_AND_RETURN(contact.isFinite(), "PxContactJoint::setContact: invalid parameter"); + data().contact = contact; + markDirty(); +} + +PxVec3 ContactJoint::getContactNormal() const +{ + return data().normal; +} + +void ContactJoint::setContactNormal(const PxVec3& normal) +{ + PX_CHECK_AND_RETURN(normal.isFinite(), "PxContactJoint::setContactNormal: invalid parameter"); + data().normal = normal; + markDirty(); +} + +PxReal ContactJoint::getPenetration() const +{ + return data().penetration; +} + +void ContactJoint::setPenetration(PxReal penetration) +{ + PX_CHECK_AND_RETURN(PxIsFinite(penetration), "ContactJoint::setPenetration: invalid parameter"); + data().penetration = penetration; + markDirty(); +} + + +PxReal ContactJoint::getResititution() const +{ + return data().restitution; +} + +void ContactJoint::setResititution(const PxReal restitution) +{ + PX_CHECK_AND_RETURN(PxIsFinite(restitution) && restitution >= 0.f && restitution <= 1.f, "ContactJoint::setResititution: invalid parameter"); + data().restitution = restitution; + markDirty(); +} + +PxReal ContactJoint::getBounceThreshold() const +{ + return data().bounceThreshold; +} + +void ContactJoint::setBounceThreshold(const PxReal bounceThreshold) +{ + PX_CHECK_AND_RETURN(PxIsFinite(bounceThreshold) && bounceThreshold > 0.f, "ContactJoint::setBounceThreshold: invalid parameter"); + data().bounceThreshold = bounceThreshold; + markDirty(); +} + +bool ContactJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(ContactJointData)); + return mPxConstraint != NULL; +} + + +void ContactJoint::exportExtraData(PxSerializationContext& stream) +{ + if (mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(ContactJointData)); + } + stream.writeName(mName); +} + +void ContactJoint::importExtraData(PxDeserializationContext& context) +{ + if (mData) + mData = context.readExtraData(); + + context.readName(mName); +} + +void ContactJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +void ContactJoint::computeJacobians(PxJacobianRow* jacobian) const +{ + const PxVec3 cp = data().contact; + const PxVec3 normal = data().normal; + + PxRigidActor* actor0, *actor1; + this->getActors(actor0, actor1); + + PxVec3 raXn(0.f), rbXn(0.f); + + if (actor0 && actor0->is()) + { + PxRigidBody* dyn = actor0->is(); + PxTransform cmassPose = dyn->getGlobalPose() * dyn->getCMassLocalPose(); + raXn = (cp - cmassPose.p).cross(normal); + } + + if (actor1 && actor1->is()) + { + PxRigidBody* dyn = actor1->is(); + PxTransform cmassPose = dyn->getGlobalPose() * dyn->getCMassLocalPose(); + rbXn = (cp - cmassPose.p).cross(normal); + } + + jacobian->linear0 = normal; + jacobian->angular0 = raXn; + jacobian->linear1 = -normal; + jacobian->angular1 = -rbXn; + +} +PxU32 ContactJoint::getNbJacobianRows() const +{ + return 1; +} + + +ContactJoint* ContactJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + ContactJoint* obj = new (address) ContactJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(ContactJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetContactJointShaderTable() +{ + return &ContactJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void ContactJointProject(const void* /*constantBlock*/, PxTransform& /*bodyAToWorld*/, PxTransform& /*bodyBToWorld*/, bool /*projectToA*/) +{ + // Not required +} + +static void ContactJointVisualize(PxConstraintVisualizer& /*viz*/, const void* /*constantBlock*/, const PxTransform& /*body0Transform*/, const PxTransform& /*body1Transform*/, PxU32 /*flags*/) +{ + //TODO +} + +static PxU32 ContactJointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& /*invMassScale*/, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const ContactJointData& data = *reinterpret_cast(constantBlock); + + const PxVec3& contact = data.contact; + const PxVec3& normal = data.normal; + + cA2wOut = contact; + cB2wOut = contact; + + const PxVec3 ra = contact - bA2w.p; + const PxVec3 rb = contact - bB2w.p; + + body0WorldOffset = PxVec3(0.f); + + Px1DConstraint& con = constraints[0]; + con.linear0 = normal; + con.linear1 = normal; + con.angular0 = ra.cross(normal); + con.angular1 = rb.cross(normal); + + con.geometricError = data.penetration; + con.minImpulse = 0.f; + con.maxImpulse = PX_MAX_F32; + + con.velocityTarget = 0.f; + con.forInternalUse = 0.f; + con.solveHint = 0; + con.flags = Px1DConstraintFlag::eOUTPUT_FORCE; + con.mods.bounce.restitution = data.restitution; + con.mods.bounce.velocityThreshold = data.bounceThreshold; + + return 1; +} + +PxConstraintShaderTable Ext::ContactJoint::sShaders = { ContactJointSolverPrep, ContactJointProject, ContactJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h new file mode 100644 index 000000000..08ad31d10 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h @@ -0,0 +1,122 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef NP_CONTACTJOINTCONSTRAINT_H +#define NP_CONTACTJOINTCONSTRAINT_H + +#include "PsUserAllocated.h" +#include "ExtJoint.h" +#include "PxContactJoint.h" +#include "PxTolerancesScale.h" +#include "CmUtils.h" + +namespace physx +{ +struct PxContactJointGeneratedValues; +namespace Ext +{ + struct ContactJointData : public JointData + { + PxVec3 contact; + PxVec3 normal; + PxReal penetration; + PxReal restitution; + PxReal bounceThreshold; + }; + + typedef Joint ContactJointT; + class ContactJoint : public ContactJointT + { + public: + // PX_SERIALIZATION + ContactJoint(PxBaseFlags baseFlags) : ContactJointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static ContactJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + ContactJoint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + ContactJointT(PxJointConcreteType::eCONTACT, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(ContactJointData), "ContactJointData") + { + PX_UNUSED(scale); + + ContactJointData* data = static_cast(mData); + + data->contact = PxVec3(0.f); + data->normal = PxVec3(0.f); + data->penetration = 0.f; + data->restitution = 0.f; + data->bounceThreshold = 0.f; + } + + // PxContactJoint + virtual PxVec3 getContact() const; + virtual void setContact(const PxVec3& contact); + virtual PxVec3 getContactNormal() const; + virtual void setContactNormal(const PxVec3& normal); + virtual PxReal getPenetration() const; + virtual void setPenetration(const PxReal penetration); + virtual PxReal getResititution() const; + virtual void setResititution(const PxReal resititution); + virtual PxReal getBounceThreshold() const; + virtual void setBounceThreshold(const PxReal bounceThreshold); + virtual void computeJacobians(PxJacobianRow* jacobian) const; + virtual PxU32 getNbJacobianRows() const; + + //~PxContactJoint + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep()const { return sShaders.solverPrep; } + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE ContactJointData& data() const + { + return *static_cast(mData); + } + }; + + +}// namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetContactJointShaderTable(); +} + +}//physx +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp new file mode 100644 index 000000000..97174e1e6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp @@ -0,0 +1,91 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxConvexMeshExt.h" +#include "PxConvexMeshGeometry.h" +#include "PxConvexMesh.h" +#include "foundation/PxTransform.h" + +using namespace physx; + +static const PxReal gEpsilon = .01f; + +PxU32 physx::PxFindFaceIndex(const PxConvexMeshGeometry& convexGeom, const PxTransform& pose, + const PxVec3& impactPos, const PxVec3& unitDir) +{ + PX_ASSERT(unitDir.isFinite()); + PX_ASSERT(unitDir.isNormalized()); + PX_ASSERT(impactPos.isFinite()); + PX_ASSERT(pose.isFinite()); + + const PxVec3 impact = impactPos - unitDir * gEpsilon; + + const PxVec3 localPoint = pose.transformInv(impact); + const PxVec3 localDir = pose.rotateInv(unitDir); + + // Create shape to vertex scale transformation matrix + const PxMeshScale& meshScale = convexGeom.scale; + const PxMat33 rot(meshScale.rotation); + PxMat33 shape2VertexSkew = rot.getTranspose(); + const PxMat33 diagonal = PxMat33::createDiagonal(PxVec3(1.0f / meshScale.scale.x, 1.0f / meshScale.scale.y, 1.0f / meshScale.scale.z)); + shape2VertexSkew = shape2VertexSkew * diagonal; + shape2VertexSkew = shape2VertexSkew * rot; + + const PxU32 nbPolys = convexGeom.convexMesh->getNbPolygons(); + PxU32 minIndex = 0; + PxReal minD = PX_MAX_REAL; + for (PxU32 j = 0; j < nbPolys; j++) + { + PxHullPolygon hullPolygon; + convexGeom.convexMesh->getPolygonData(j, hullPolygon); + + // transform hull plane into shape space + PxPlane plane; + const PxVec3 tmp = shape2VertexSkew.transformTranspose(PxVec3(hullPolygon.mPlane[0],hullPolygon.mPlane[1],hullPolygon.mPlane[2])); + const PxReal denom = 1.0f / tmp.magnitude(); + plane.n = tmp * denom; + plane.d = hullPolygon.mPlane[3] * denom; + + PxReal d = plane.distance(localPoint); + if (d < 0.0f) + continue; + + const PxReal tweak = plane.n.dot(localDir) * gEpsilon; + d += tweak; + + if (d < minD) + { + minIndex = j; + minD = d; + } + } + return minIndex; +} + diff --git a/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp new file mode 100644 index 000000000..30e17c35a --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp @@ -0,0 +1,106 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "task/PxTask.h" +#include "ExtCpuWorkerThread.h" +#include "ExtDefaultCpuDispatcher.h" +#include "ExtTaskQueueHelper.h" +#include "PsFPU.h" + +using namespace physx; + +Ext::CpuWorkerThread::CpuWorkerThread() +: mQueueEntryPool(EXT_TASK_QUEUE_ENTRY_POOL_SIZE), + mThreadId(0) +{ +} + + +Ext::CpuWorkerThread::~CpuWorkerThread() +{ +} + + +void Ext::CpuWorkerThread::initialize(DefaultCpuDispatcher* ownerDispatcher) +{ + mOwner = ownerDispatcher; +} + + +bool Ext::CpuWorkerThread::tryAcceptJobToLocalQueue(PxBaseTask& task, Ps::Thread::Id taskSubmitionThread) +{ + if(taskSubmitionThread == mThreadId) + { + SharedQueueEntry* entry = mQueueEntryPool.getEntry(&task); + if (entry) + { + mLocalJobList.push(*entry); + return true; + } + else + return false; + } + + return false; +} + + +PxBaseTask* Ext::CpuWorkerThread::giveUpJob() +{ + return TaskQueueHelper::fetchTask(mLocalJobList, mQueueEntryPool); +} + + +void Ext::CpuWorkerThread::execute() +{ + mThreadId = getId(); + + while (!quitIsSignalled()) + { + mOwner->resetWakeSignal(); + + PxBaseTask* task = TaskQueueHelper::fetchTask(mLocalJobList, mQueueEntryPool); + + if(!task) + task = mOwner->fetchNextTask(); + + if (task) + { + mOwner->runTask(*task); + task->release(); + } + else + { + mOwner->waitForWork(); + } + } + + quit(); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h new file mode 100644 index 000000000..e3f37b3f4 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_NP_CPU_WORKER_THREAD_H +#define PX_PHYSICS_EXTENSIONS_NP_CPU_WORKER_THREAD_H + +#include "CmPhysXCommon.h" +#include "PsThread.h" +#include "ExtDefaultCpuDispatcher.h" +#include "ExtSharedQueueEntryPool.h" + + +namespace physx +{ +namespace Ext +{ +class DefaultCpuDispatcher; + + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif // Because of the SList member I assume + + class CpuWorkerThread : public Ps::Thread + { + public: + CpuWorkerThread(); + ~CpuWorkerThread(); + + void initialize(DefaultCpuDispatcher* ownerDispatcher); + void execute(); + bool tryAcceptJobToLocalQueue(PxBaseTask& task, Ps::Thread::Id taskSubmitionThread); + PxBaseTask* giveUpJob(); + Ps::Thread::Id getWorkerThreadId() const { return mThreadId; } + + protected: + SharedQueueEntryPool<> mQueueEntryPool; + DefaultCpuDispatcher* mOwner; + Ps::SList mLocalJobList; + Ps::Thread::Id mThreadId; + }; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Ext + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp new file mode 100644 index 000000000..ac2e5fcea --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp @@ -0,0 +1,1089 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtD6Joint.h" +#include "ExtConstraintHelper.h" +#include "CmRenderOutput.h" +#include "CmConeLimitHelper.h" +#include "PxTolerancesScale.h" +#include "CmUtils.h" +#include "PxConstraint.h" + +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxD6Joint* physx::PxD6JointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxD6JointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxD6JointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxD6JointCreate: actors must be different"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxD6JointCreate: at least one actor must be dynamic"); + + D6Joint* j; + PX_NEW_SERIALIZED(j, D6Joint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +D6Joint::D6Joint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + D6JointT(PxJointConcreteType::eD6, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(D6JointData), "D6JointData"), + mRecomputeMotion (true) +{ + D6JointData* data = static_cast(mData); + + for(PxU32 i=0;i<6;i++) + data->motion[i] = PxD6Motion::eLOCKED; + + data->twistLimit = PxJointAngularLimitPair(-PxPi/2, PxPi/2); + data->swingLimit = PxJointLimitCone(PxPi/2, PxPi/2); + data->pyramidSwingLimit = PxJointLimitPyramid(-PxPi/2, PxPi/2, -PxPi/2, PxPi/2); + data->distanceLimit = PxJointLinearLimit(scale, PX_MAX_F32); + data->distanceMinDist = 1e-6f*scale.length; + + data->linearLimitX = PxJointLinearLimitPair(scale); + data->linearLimitY = PxJointLinearLimitPair(scale); + data->linearLimitZ = PxJointLinearLimitPair(scale); + + for(PxU32 i=0;idrive[i] = PxD6JointDrive(); + + data->drivePosition = PxTransform(PxIdentity); + data->driveLinearVelocity = PxVec3(0.0f); + data->driveAngularVelocity = PxVec3(0.0f); + + data->projectionLinearTolerance = 1e10f; + data->projectionAngularTolerance = PxPi; + + data->mUseDistanceLimit = false; + data->mUseNewLinearLimits = false; + data->mUseConeLimit = false; + data->mUsePyramidLimits = false; +} + +PxD6Motion::Enum D6Joint::getMotion(PxD6Axis::Enum index) const +{ + return data().motion[index]; +} + +void D6Joint::setMotion(PxD6Axis::Enum index, PxD6Motion::Enum t) +{ + data().motion[index] = t; + mRecomputeMotion = true; + markDirty(); +} + +PxReal D6Joint::getTwistAngle() const +{ + return getTwistAngle_Internal(); +} + +PxReal D6Joint::getSwingYAngle() const +{ + return getSwingYAngle_Internal(); +} + +PxReal D6Joint::getSwingZAngle() const +{ + return getSwingZAngle_Internal(); +} + +PxD6JointDrive D6Joint::getDrive(PxD6Drive::Enum index) const +{ + return data().drive[index]; +} + +void D6Joint::setDrive(PxD6Drive::Enum index, const PxD6JointDrive& d) +{ + PX_CHECK_AND_RETURN(d.isValid(), "PxD6Joint::setDrive: drive is invalid"); + + data().drive[index] = d; + mRecomputeMotion = true; + markDirty(); +} + +void D6Joint::setDistanceLimit(const PxJointLinearLimit& l) +{ + PX_CHECK_AND_RETURN(l.isValid(), "PxD6Joint::setDistanceLimit: limit invalid"); + data().distanceLimit = l; + data().mUseDistanceLimit = true; + markDirty(); +} + +PxJointLinearLimit D6Joint::getDistanceLimit() const +{ + return data().distanceLimit; +} + +void D6Joint::setLinearLimit(PxD6Axis::Enum axis, const PxJointLinearLimitPair& limit) +{ + PX_CHECK_AND_RETURN(axis>=PxD6Axis::eX && axis<=PxD6Axis::eZ, "PxD6Joint::setLinearLimit: invalid axis value"); + PX_CHECK_AND_RETURN(limit.isValid(), "PxD6Joint::setLinearLimit: limit invalid"); + D6JointData& d = data(); + if(axis==PxD6Axis::eX) + d.linearLimitX = limit; + else if(axis==PxD6Axis::eY) + d.linearLimitY = limit; + else if(axis==PxD6Axis::eZ) + d.linearLimitZ = limit; + else + return; + d.mUseNewLinearLimits = true; + markDirty(); +} + +PxJointLinearLimitPair D6Joint::getLinearLimit(PxD6Axis::Enum axis) const +{ + PX_CHECK_AND_RETURN_VAL(axis>=PxD6Axis::eX && axis<=PxD6Axis::eZ, "PxD6Joint::getLinearLimit: invalid axis value", PxJointLinearLimitPair(PxTolerancesScale(), 0.0f, 0.0f)); + const D6JointData& d = data(); + if(axis==PxD6Axis::eX) + return d.linearLimitX; + else if(axis==PxD6Axis::eY) + return d.linearLimitY; + else if(axis==PxD6Axis::eZ) + return d.linearLimitZ; + return PxJointLinearLimitPair(PxTolerancesScale(), 0.0f, 0.0f); +} + +PxJointAngularLimitPair D6Joint::getTwistLimit() const +{ + return data().twistLimit; +} + +void D6Joint::setTwistLimit(const PxJointAngularLimitPair& l) +{ + PX_CHECK_AND_RETURN(l.isValid(), "PxD6Joint::setTwistLimit: limit invalid"); + // PT: the tangent version is not compatible with the double-cover feature, since the potential limit extent in that case is 4*PI. + // i.e. we'd potentially take the tangent of something equal to PI/2. So the tangent stuff makes the limits less accurate, and it + // also reduces the available angular range for the joint. All that for questionable performance gains. + PX_CHECK_AND_RETURN(l.lower>-PxTwoPi && l.upper=0 && tolerance <= PxPi, "PxD6Joint::setProjectionAngularTolerance: tolerance invalid"); + data().projectionAngularTolerance = tolerance; + markDirty(); +} + +PxReal D6Joint::getProjectionAngularTolerance() const +{ + return data().projectionAngularTolerance; +} + +void D6Joint::setProjectionLinearTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxD6Joint::setProjectionLinearTolerance: invalid parameter"); + data().projectionLinearTolerance = tolerance; + markDirty(); +} + +PxReal D6Joint::getProjectionLinearTolerance() const +{ + return data().projectionLinearTolerance; +} + +void* D6Joint::prepareData() +{ + D6JointData& d = data(); + + if(mRecomputeMotion) + { + mRecomputeMotion = false; + + d.driving = 0; + d.limited = 0; + d.locked = 0; + + for(PxU32 i=0;iD6JointT::prepareData(); + + return mData; +} + +bool D6Joint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(D6JointData)); + return mPxConstraint!=NULL; +} + +void D6Joint::exportExtraData(PxSerializationContext& stream) +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(D6JointData)); + } + stream.writeName(mName); +} + +void D6Joint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + + context.readName(mName); +} + +void D6Joint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +D6Joint* D6Joint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + D6Joint* obj = new (address) D6Joint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(D6Joint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetD6JointShaderTable() +{ + return &D6Joint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +// Notes: +/* + +This used to be in the linear drive model: + + if(motion[PxD6Axis::eX+i] == PxD6Motion::eLIMITED) + { + if(data.driveLinearVelocity[i] < 0.0f && cB2cA.p[i] < -mLimits[PxD6Limit::eLINEAR].mValue || + data.driveLinearVelocity[i] > 0.0f && cB2cA.p[i] > mLimits[PxD6Limit::eLINEAR].mValue) + continue; + } + +it doesn't seem like a good idea though, because it turns off drive altogether, despite the fact that positional +drive might pull us back in towards the limit. Might be better to make the drive unilateral so it can only pull +us in from the limit + +This used to be in angular locked: + + // Angular locked + //TODO fix this properly. + if(PxAbs(cB2cA.q.x) < 0.0001f) cB2cA.q.x = 0; + if(PxAbs(cB2cA.q.y) < 0.0001f) cB2cA.q.y = 0; + if(PxAbs(cB2cA.q.z) < 0.0001f) cB2cA.q.z = 0; + if(PxAbs(cB2cA.q.w) < 0.0001f) cB2cA.q.w = 0; +*/ + +static PxQuat truncate(const PxQuat& qIn, PxReal minCosHalfTol, bool& truncated) +{ + const PxQuat q = qIn.w >= 0.0f ? qIn : -qIn; + truncated = q.w < minCosHalfTol; + if(!truncated) + return q; + const PxVec3 v = q.getImaginaryPart().getNormalized() * PxSqrt(1.0f - minCosHalfTol * minCosHalfTol); + return PxQuat(v.x, v.y, v.z, minCosHalfTol); +} + +// we decompose the quaternion as q1 * q2, where q1 is a rotation orthogonal to the unit axis, and q2 a rotation around it. +// (so for example if 'axis' is the twist axis, this is separateSwingTwist). +static PxQuat project(const PxQuat& q, const PxVec3& axis, PxReal cosHalfTol, bool& truncated) +{ + const PxReal a = q.getImaginaryPart().dot(axis); + const PxQuat q2 = PxAbs(a) >= 1e-6f ? PxQuat(a*axis.x, a*axis.y, a*axis.z, q.w).getNormalized() : PxQuat(PxIdentity); + const PxQuat q1 = q * q2.getConjugate(); + + PX_ASSERT(PxAbs(q1.getImaginaryPart().dot(q2.getImaginaryPart())) < 1e-6f); + + return truncate(q1, cosHalfTol, truncated) * q2; +} + +// Here's how the angular part works: +// * if no DOFs are locked, there's nothing to do. +// * if all DOFs are locked, we just truncate the rotation +// * if two DOFs are locked +// * we decompose the rotation into swing * twist, where twist is a rotation around the free DOF and swing is a rotation around an axis orthogonal to the free DOF +// * then we truncate swing +// The case of one locked DOF is currently unimplemented, but one option would be: +// * if one DOF is locked (the tricky case), we define the 'free' axis as follows (as the velocity solver prep function does) +// TWIST: cB[0] +// SWING1: cB[0].cross(cA[2]) +// SWING2: cB[0].cross(cA[1]) +// then, as above, we decompose into swing * free, and truncate the free rotation + +//export this in the physx namespace so we can unit test it +namespace physx +{ +PxQuat angularProject(PxU32 lockedDofs, const PxQuat& q, PxReal cosHalfTol, bool& truncated) +{ + PX_ASSERT(lockedDofs <= 7); + truncated = false; + + switch(lockedDofs) + { + case 0: return q; + case 1: return q; // currently unimplemented + case 2: return q; // currently unimplemented + case 3: return project(q, PxVec3(0.0f, 0.0f, 1.0f), cosHalfTol, truncated); + case 4: return q; // currently unimplemented + case 5: return project(q, PxVec3(0.0f, 1.0f, 0.0f), cosHalfTol, truncated); + case 6: return project(q, PxVec3(1.0f, 0.0f, 0.0f), cosHalfTol, truncated); + case 7: return truncate(q, cosHalfTol, truncated); + default: return PxQuat(PxIdentity); + } +} +} + +static void D6JointProject(const void* constantBlock, PxTransform& bodyAToWorld, PxTransform& bodyBToWorld, bool projectToA) +{ + const D6JointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w, cB2cA, projected; + joint::computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA, false); + + const PxVec3 v(data.locked & 1 ? cB2cA.p.x : 0.0f, + data.locked & 2 ? cB2cA.p.y : 0.0f, + data.locked & 4 ? cB2cA.p.z : 0.0f); + + bool linearTrunc, angularTrunc = false; + projected.p = joint::truncateLinear(v, data.projectionLinearTolerance, linearTrunc) + (cB2cA.p - v); + + projected.q = angularProject(data.locked >> 3, cB2cA.q, PxCos(data.projectionAngularTolerance / 2), angularTrunc); + + if(linearTrunc || angularTrunc) + joint::projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA); +} + +static PX_FORCE_INLINE PxReal computePhi(const PxQuat& q) +{ + PxQuat twist = q; + twist.normalize(); + + PxReal angle = twist.getAngle(); + if(twist.x<0.0f) + angle = -angle; + return angle; +} + +static void visualizeAngularLimit(PxConstraintVisualizer& viz, const D6JointData& data, const PxTransform& t, float swingYZ, float swingW, float swingLimitYZ) +{ + bool active = PxAbs(computeSwingAngle(swingYZ, swingW)) > swingLimitYZ - data.swingLimit.contactDistance; + viz.visualizeAngularLimit(t, -swingLimitYZ, swingLimitYZ, active); +} + +static void visualizeDoubleCone(PxConstraintVisualizer& viz, const D6JointData& data, const PxTransform& t, float sin, float swingLimitYZ) +{ + const PxReal angle = PxAsin(sin); + const PxReal pad = data.swingLimit.contactDistance; + const PxReal low = -swingLimitYZ; + const PxReal high = swingLimitYZ; + + const bool active = isLimitActive(data.swingLimit, pad, angle, low, high); + viz.visualizeDoubleCone(t, swingLimitYZ, active); +} + +// PT: TODO: refactor with spherical joint code +static void visualizeCone(PxConstraintVisualizer& viz, const D6JointData& data, const PxQuat& swing, const PxTransform& cA2w) +{ + const PxVec3 swingAngle(0.0f, computeSwingAngle(swing.y, swing.w), computeSwingAngle(swing.z, swing.w)); + const PxReal pad = data.swingLimit.isSoft() ? 0.0f : data.swingLimit.contactDistance; + Cm::ConeLimitHelperTanLess coneHelper(data.swingLimit.yAngle, data.swingLimit.zAngle, pad); + viz.visualizeLimitCone(cA2w, PxTan(data.swingLimit.zAngle / 4), PxTan(data.swingLimit.yAngle / 4), !coneHelper.contains(swingAngle)); +} + +static PX_FORCE_INLINE bool isLinearLimitActive(const PxJointLinearLimitPair& limit, float ordinate) +{ + const PxReal pad = limit.isSoft() ? 0.0f : limit.contactDistance; + return (ordinate < limit.lower + pad) || (ordinate > limit.upper - pad); +} + +static void visualizeLine(PxConstraintVisualizer& viz, const PxVec3& origin, const PxVec3& axis, const PxJointLinearLimitPair& limit, float ordinate) +{ + const bool active = isLinearLimitActive(limit, ordinate); + const PxVec3 p0 = origin + axis * limit.lower; + const PxVec3 p1 = origin + axis * limit.upper; + viz.visualizeLine(p0, p1, active ? 0xff0000u : 0xffffffu); +} + +static void visualizeQuad(PxConstraintVisualizer& viz, const PxVec3& origin, const PxVec3& axis0, const PxJointLinearLimitPair& limit0, float ordinate0, + const PxVec3& axis1, const PxJointLinearLimitPair& limit1, float ordinate1) +{ + const bool active0 = isLinearLimitActive(limit0, ordinate0); + const bool active1 = isLinearLimitActive(limit1, ordinate1); + const PxU32 color = (active0 || active1) ? 0xff0000u : 0xffffffu; + + const PxVec3 l0 = axis0 * limit0.lower; + const PxVec3 u0 = axis0 * limit0.upper; + const PxVec3 l1 = axis1 * limit1.lower; + const PxVec3 u1 = axis1 * limit1.upper; + + const PxVec3 p0 = origin + l0 + l1; + const PxVec3 p1 = origin + u0 + l1; + const PxVec3 p2 = origin + u0 + u1; + const PxVec3 p3 = origin + l0 + u1; + + viz.visualizeLine(p0, p1, color); + viz.visualizeLine(p1, p2, color); + viz.visualizeLine(p2, p3, color); + viz.visualizeLine(p3, p0, color); +} + +static void visualizeBox(PxConstraintVisualizer& viz, const PxVec3& origin, const PxVec3& axis0, const PxJointLinearLimitPair& limit0, float ordinate0, + const PxVec3& axis1, const PxJointLinearLimitPair& limit1, float ordinate1, + const PxVec3& axis2, const PxJointLinearLimitPair& limit2, float ordinate2) +{ + const bool active0 = isLinearLimitActive(limit0, ordinate0); + const bool active1 = isLinearLimitActive(limit1, ordinate1); + const bool active2 = isLinearLimitActive(limit2, ordinate2); + const PxU32 color = (active0 || active1 || active2) ? 0xff0000u : 0xffffffu; + + const PxVec3 l0 = axis0 * limit0.lower; + const PxVec3 u0 = axis0 * limit0.upper; + const PxVec3 l1 = axis1 * limit1.lower; + const PxVec3 u1 = axis1 * limit1.upper; + const PxVec3 l2 = axis2 * limit2.lower; + const PxVec3 u2 = axis2 * limit2.upper; + + const PxVec3 p0 = origin + l0 + l1 + l2; + const PxVec3 p1 = origin + u0 + l1 + l2; + const PxVec3 p2 = origin + u0 + u1 + l2; + const PxVec3 p3 = origin + l0 + u1 + l2; + const PxVec3 p0b = origin + l0 + l1 + u2; + const PxVec3 p1b = origin + u0 + l1 + u2; + const PxVec3 p2b = origin + u0 + u1 + u2; + const PxVec3 p3b = origin + l0 + u1 + u2; + + viz.visualizeLine(p0, p1, color); + viz.visualizeLine(p1, p2, color); + viz.visualizeLine(p2, p3, color); + viz.visualizeLine(p3, p0, color); + viz.visualizeLine(p0b, p1b, color); + viz.visualizeLine(p1b, p2b, color); + viz.visualizeLine(p2b, p3b, color); + viz.visualizeLine(p3b, p0b, color); + viz.visualizeLine(p0, p0b, color); + viz.visualizeLine(p1, p1b, color); + viz.visualizeLine(p2, p2b, color); + viz.visualizeLine(p3, p3b, color); +} + +static float computeLimitedDistance(const D6JointData& data, const PxTransform& cB2cA, const PxMat33& cA2w_m, PxVec3& _limitDir) +{ + PxVec3 limitDir(0.0f); + + for(PxU32 i = 0; i<3; i++) + { + if(data.limited & (1 << (PxD6Axis::eX + i))) + limitDir += cA2w_m[i] * cB2cA.p[i]; + } + + _limitDir = limitDir; + return limitDir.magnitude(); +} + +void _setRotY(PxMat33& m, PxReal angle) +{ + m = PxMat33(PxIdentity); + + const PxReal cos = cosf(angle); + const PxReal sin = sinf(angle); + + m[0][0] = m[2][2] = cos; + m[0][2] = -sin; + m[2][0] = sin; +} + +void _setRotZ(PxMat33& m, PxReal angle) +{ + m = PxMat33(PxIdentity); + + const PxReal cos = cosf(angle); + const PxReal sin = sinf(angle); + + m[0][0] = m[1][1] = cos; + m[0][1] = sin; + m[1][0] = -sin; +} + +PxQuat _getRotYQuat(float angle) +{ + PxMat33 m; + _setRotY(m, angle); + return PxQuat(m); +} + +PxQuat _getRotZQuat(float angle) +{ + PxMat33 m; + _setRotZ(m, angle); + return PxQuat(m); +} + +void _setRotX(PxMat33& m, PxReal angle) +{ + m = PxMat33(PxIdentity); + + const PxReal cos = cosf(angle); + const PxReal sin = sinf(angle); + + m[1][1] = m[2][2] = cos; + m[1][2] = sin; + m[2][1] = -sin; +} + +PxQuat _getRotXQuat(float angle) +{ + PxMat33 m; + _setRotX(m, angle); + return PxQuat(m); +} + +static void drawPyramid(PxConstraintVisualizer& viz, const D6JointData& data, const PxTransform& cA2w, const PxQuat& swing, bool useY, bool useZ) +{ + struct Local + { + static void drawArc(PxConstraintVisualizer& _viz, const PxTransform& _cA2w, float ymin, float ymax, float zmin, float zmax, PxU32 color) + { + // PT: we use 32 segments for the cone case, so that's 32/4 segments per arc in the pyramid case + const PxU32 nb = 32/4; + PxVec3 prev(0.0f); + for(PxU32 i=0;i(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + viz.visualizeJointFrames(cA2w, cB2w); + + if(flags & PxConstraintVisualizationFlag::eLIMITS) + { + // PT: it is a mistake to use the neighborhood operator since it + // prevents us from using the quat's double-cover feature. +// if(cA2w.q.dot(cB2w.q)<0.0f) +// cB2w.q = -cB2w.q; + + const PxTransform cB2cA = cA2w.transformInv(cB2w); + const PxMat33 cA2w_m(cA2w.q), cB2w_m(cB2w.q); + + if(data.mUseNewLinearLimits) + { + switch(data.limited) + { + case 1< data.distanceMinDist) + { + PxU32 color = 0x00ff00; + if(distance>data.distanceLimit.value) + color = 0xff0000; + + viz.visualizeLine(cA2w.p, cB2w.p, color); + } + } + + PxQuat swing, twist; + Ps::separateSwingTwist(cB2cA.q, swing, twist); + + const PxVec3& bX = cB2w_m.column0; + const PxVec3& aY = cA2w_m.column1; + const PxVec3& aZ = cA2w_m.column2; + + if(data.limited&TWIST_FLAG) + { + const PxReal angle = computePhi(twist); + const PxReal pad = data.twistLimit.contactDistance; + const PxReal low = data.twistLimit.lower; + const PxReal high = data.twistLimit.upper; + + const bool active = isLimitActive(data.twistLimit, pad, angle, low, high); + viz.visualizeAngularLimit(cA2w, data.twistLimit.lower, data.twistLimit.upper, active); + } + + const bool swing1Limited = (data.limited & SWING1_FLAG)!=0, swing2Limited = (data.limited & SWING2_FLAG)!=0; + + if(swing1Limited && swing2Limited) + { + if(data.mUseConeLimit) + visualizeCone(viz, data, swing, cA2w); + + if(data.mUsePyramidLimits) + drawPyramid(viz, data, cA2w, swing, true, true); + } + else if(swing1Limited ^ swing2Limited) + { + const PxTransform yToX(PxVec3(0.0f), PxQuat(-PxPi/2.0f, PxVec3(0.0f, 0.0f, 1.0f))); + const PxTransform zToX(PxVec3(0.0f), PxQuat(PxPi/2.0f, PxVec3(0.0f, 1.0f, 0.0f))); + + if(swing1Limited) + { + if(data.locked & SWING2_FLAG) + { + if(data.mUsePyramidLimits) + drawPyramid(viz, data, cA2w, swing, true, false); + else + visualizeAngularLimit(viz, data, cA2w * yToX, swing.y, swing.w, data.swingLimit.yAngle); // PT: swing Y limited, swing Z locked + } + else + if(!data.mUsePyramidLimits) + visualizeDoubleCone(viz, data, cA2w * zToX, aZ.dot(bX), data.swingLimit.yAngle); // PT: swing Y limited, swing Z free + } + else + { + if(data.locked & SWING1_FLAG) + { + if(data.mUsePyramidLimits) + drawPyramid(viz, data, cA2w, swing, false, true); + else + visualizeAngularLimit(viz, data, cA2w * zToX, swing.z, swing.w, data.swingLimit.zAngle); // PT: swing Z limited, swing Y locked + } + else + if(!data.mUsePyramidLimits) + visualizeDoubleCone(viz, data, cA2w * yToX, aY.dot(bX), data.swingLimit.zAngle); // PT: swing Z limited, swing Y free + } + } + } +} + +static PX_FORCE_INLINE void setupSingleSwingLimit(joint::ConstraintHelper& ch, const D6JointData& data, const PxVec3& axis, float swingYZ, float swingW, float swingLimitYZ) +{ + ch.anglePair(computeSwingAngle(swingYZ, swingW), -swingLimitYZ, swingLimitYZ, data.swingLimit.contactDistance, axis, data.swingLimit); +} + +static PX_FORCE_INLINE void setupDualConeSwingLimits(joint::ConstraintHelper& ch, const D6JointData& data, const PxVec3& axis, float sin, float swingLimitYZ) +{ + ch.anglePair(PxAsin(sin), -swingLimitYZ, swingLimitYZ, data.swingLimit.contactDistance, axis.getNormalized(), data.swingLimit); +} + +// PT: TODO: refactor with spherical joint code +static void setupConeSwingLimits(joint::ConstraintHelper& ch, const D6JointData& data, const PxQuat& swing, const PxTransform& cA2w) +{ + PxVec3 axis; + PxReal error; + const PxReal pad = data.swingLimit.isSoft() ? 0.0f : data.swingLimit.contactDistance; + const Cm::ConeLimitHelperTanLess coneHelper(data.swingLimit.yAngle, data.swingLimit.zAngle, pad); + bool active = coneHelper.getLimit(swing, axis, error); + if(active) + ch.angularLimit(cA2w.rotate(axis), error, data.swingLimit); +} + +static void setupPyramidSwingLimits(joint::ConstraintHelper& ch, const D6JointData& data, const PxQuat& swing, const PxTransform& cA2w, bool useY, bool useZ) +{ + const PxQuat q = cA2w.q * swing; + const PxJointLimitPyramid& l = data.pyramidSwingLimit; + if(useY) + ch.anglePair(computeSwingAngle(swing.y, swing.w), l.yAngleMin, l.yAngleMax, l.contactDistance, q.getBasisVector1(), l); + if(useZ) + ch.anglePair(computeSwingAngle(swing.z, swing.w), l.zAngleMin, l.zAngleMax, l.contactDistance, q.getBasisVector2(), l); +} + +static void setupLinearLimit(joint::ConstraintHelper& ch, const PxJointLinearLimitPair& limit, const float origin, const PxVec3& axis) +{ + ch.linearLimit(axis, origin, limit.upper, limit); + ch.linearLimit(-axis, -origin, -limit.lower, limit); +} + +static PxU32 D6JointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool useExtendedLimits, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const D6JointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + const PxU32 SWING1_FLAG = 1<0.0f ? data.drivePosition.q : -data.drivePosition.q; + + const PxVec3& v = data.driveAngularVelocity; + const PxQuat delta = d2cA_q.getConjugate() * cB2cA.q; + + if(driving & (1< data.distanceMinDist) + ch.linearLimit(limitDir * (1.0f/distance), distance, data.distanceLimit.value, data.distanceLimit); + } + + if(data.mUseNewLinearLimits) // PT: new asymmetric linear limits + { + const PxVec3& bOriginInA = cB2cA.p; + + // PT: TODO: we check that the DOFs are not "locked" to be consistent with the prismatic joint, but it + // doesn't look like this case is possible, since it would be caught by the "isValid" check when setting + // the limits. And in fact the "distance" linear limit above doesn't do this check. + if((limited & (1<>3, ra, rb); + + cA2wOut = ra + bA2w.p; + cB2wOut = rb + bB2w.p; + + /*cA2wOut = cA2w.p; + cB2wOut = cB2w.p;*/ + + // PT: TODO: check the number cannot be too high now + return ch.getCount(); +} + +PxConstraintShaderTable Ext::D6Joint::sShaders = { D6JointSolverPrep, D6JointProject, D6JointVisualize, /*PxConstraintFlag::Enum(0)*/PxConstraintFlag::eGPU_COMPATIBLE }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h new file mode 100644 index 000000000..93d3da2c7 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h @@ -0,0 +1,200 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_D6JOINTCONSTRAINT_H +#define NP_D6JOINTCONSTRAINT_H + +#include "ExtJoint.h" +#include "PxD6Joint.h" +#include "PsMathUtils.h" + +namespace physx +{ +struct PxD6JointGeneratedValues; +namespace Ext +{ + struct D6JointData : public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxD6Motion::Enum motion[6]; + PxJointLinearLimit distanceLimit; + PxJointLinearLimitPair linearLimitX; + PxJointLinearLimitPair linearLimitY; + PxJointLinearLimitPair linearLimitZ; + PxJointAngularLimitPair twistLimit; + PxJointLimitCone swingLimit; + PxJointLimitPyramid pyramidSwingLimit; + + PxD6JointDrive drive[PxD6Drive::eCOUNT]; + + PxTransform drivePosition; + PxVec3 driveLinearVelocity; + PxVec3 driveAngularVelocity; + + // derived quantities + + PxU32 locked; // bitmap of locked DOFs + PxU32 limited; // bitmap of limited DOFs + PxU32 driving; // bitmap of active drives (implies driven DOFs not locked) + + PxReal distanceMinDist; // distance limit minimum distance to get a good direction + + // projection quantities + PxReal projectionLinearTolerance; + PxReal projectionAngularTolerance; + + // PT: the PxD6Motion values are now shared for both kind of linear limits, so we need + // an extra bool to know which one(s) should be actually used. + bool mUseDistanceLimit; + bool mUseNewLinearLimits; + + // PT: the swing limits can now be a cone or a pyramid, so we need + // an extra bool to know which one(s) should be actually used. + bool mUseConeLimit; + bool mUsePyramidLimits; + + // forestall compiler complaints about not being able to generate a constructor + private: + D6JointData(const PxJointLinearLimit& distance, + const PxJointLinearLimitPair& linearX, + const PxJointLinearLimitPair& linearY, + const PxJointLinearLimitPair& linearZ, + const PxJointAngularLimitPair& twist, + const PxJointLimitCone& swing, + const PxJointLimitPyramid& pyramid) : + distanceLimit (distance), + linearLimitX (linearX), + linearLimitY (linearY), + linearLimitZ (linearZ), + twistLimit (twist), + swingLimit (swing), + pyramidSwingLimit (pyramid), + mUseDistanceLimit (false), + mUseNewLinearLimits (false), + mUseConeLimit (false), + mUsePyramidLimits (false) + {} + }; + + typedef Joint D6JointT; + + class D6Joint : public Joint + { + public: +// PX_SERIALIZATION + D6Joint(PxBaseFlags baseFlags) : D6JointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static D6Joint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + D6Joint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1); + + // PxD6Joint + virtual void setMotion(PxD6Axis::Enum index, PxD6Motion::Enum t); + virtual PxD6Motion::Enum getMotion(PxD6Axis::Enum index) const; + virtual PxReal getTwistAngle() const; + virtual PxReal getSwingYAngle() const; + virtual PxReal getSwingZAngle() const; + virtual void setDistanceLimit(const PxJointLinearLimit& l); + virtual PxJointLinearLimit getDistanceLimit() const; + virtual void setLinearLimit(PxD6Axis::Enum axis, const PxJointLinearLimitPair& limit); + virtual PxJointLinearLimitPair getLinearLimit(PxD6Axis::Enum axis) const; + virtual void setTwistLimit(const PxJointAngularLimitPair& l); + virtual PxJointAngularLimitPair getTwistLimit() const; + virtual void setSwingLimit(const PxJointLimitCone& l); + virtual PxJointLimitCone getSwingLimit() const; + virtual void setPyramidSwingLimit(const PxJointLimitPyramid& limit); + virtual PxJointLimitPyramid getPyramidSwingLimit() const; + virtual void setDrive(PxD6Drive::Enum index, const PxD6JointDrive& d); + virtual PxD6JointDrive getDrive(PxD6Drive::Enum index) const; + virtual void setDrivePosition(const PxTransform& pose, bool autowake = true); + virtual PxTransform getDrivePosition() const; + virtual void setDriveVelocity(const PxVec3& linear, const PxVec3& angular, bool autowake = true); + virtual void getDriveVelocity(PxVec3& linear, PxVec3& angular) const; + virtual void setProjectionLinearTolerance(PxReal tolerance); + virtual PxReal getProjectionLinearTolerance() const; + virtual void setProjectionAngularTolerance(PxReal tolerance); + virtual PxReal getProjectionAngularTolerance() const; + //~PxD6Joint + + void visualize(PxRenderBuffer& out, + const void* constantBlock, + const PxTransform& body0Transform, + const PxTransform& body1Transform, + PxReal frameScale, + PxReal limitScale, + PxU32 flags) const; + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep() const { return sShaders.solverPrep; } + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE D6JointData& data() const + { + return *static_cast(mData); + } + + bool active(const PxD6Drive::Enum index) const + { + PxD6JointDrive& d = data().drive[index]; + return d.stiffness!=0 || d.damping != 0; + } + + void* prepareData(); + + bool mRecomputeMotion; + bool mPadding[3]; // PT: padding from prev bool + }; + +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetD6JointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp b/src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp new file mode 100644 index 000000000..dc4e7205d --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp @@ -0,0 +1,289 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxD6JointCreate.h" +#include "PxD6Joint.h" +#include "PxFixedJoint.h" +#include "PxRevoluteJoint.h" +#include "PxSphericalJoint.h" +#include "PxPrismaticJoint.h" +#include "PxDistanceJoint.h" +#include "PxPhysics.h" +#include "foundation/PxMathUtils.h" + +using namespace physx; + +static const PxVec3 gX(1.0f, 0.0f, 0.0f); + +static void setRotY(PxMat33& m, PxReal angle) +{ + m = PxMat33(PxIdentity); + + const PxReal cos = cosf(angle); + const PxReal sin = sinf(angle); + + m[0][0] = m[2][2] = cos; + m[0][2] = -sin; + m[2][0] = sin; +} + +static void setRotZ(PxMat33& m, PxReal angle) +{ + m = PxMat33(PxIdentity); + + const PxReal cos = cosf(angle); + const PxReal sin = sinf(angle); + + m[0][0] = m[1][1] = cos; + m[0][1] = sin; + m[1][0] = -sin; +} + +static PxQuat getRotYQuat(float angle) +{ + PxMat33 m; + setRotY(m, angle); + return PxQuat(m); +} + +static PxQuat getRotZQuat(float angle) +{ + PxMat33 m; + setRotZ(m, angle); + return PxQuat(m); +} + +PxJoint* physx::PxD6JointCreate_Fixed(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, bool useD6) +{ + const PxTransform jointFrame0(localPos0); + const PxTransform jointFrame1(localPos1); + if(useD6) + // PT: by default all D6 axes are locked, i.e. it is a fixed joint. + return PxD6JointCreate(physics, actor0, jointFrame0, actor1, jointFrame1); + else + return PxFixedJointCreate(physics, actor0, jointFrame0, actor1, jointFrame1); +} + +PxJoint* physx::PxD6JointCreate_Distance(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, float maxDist, bool useD6) +{ + const PxTransform localFrame0(localPos0); + const PxTransform localFrame1(localPos1); + + if(useD6) + { + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE); + + j->setMotion(PxD6Axis::eX, PxD6Motion::eLIMITED); + j->setMotion(PxD6Axis::eY, PxD6Motion::eLIMITED); + j->setMotion(PxD6Axis::eZ, PxD6Motion::eLIMITED); + j->setDistanceLimit(PxJointLinearLimit(PxTolerancesScale(), maxDist)); + return j; + } + else + { + PxDistanceJoint* j = PxDistanceJointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setDistanceJointFlag(PxDistanceJointFlag::eMAX_DISTANCE_ENABLED, true); + j->setMaxDistance(maxDist); + return j; + } +} + +PxJoint* physx::PxD6JointCreate_Prismatic(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float minLimit, float maxLimit, bool useD6) +{ + const PxQuat q = PxShortestRotation(gX, axis); + const PxTransform localFrame0(localPos0, q); + const PxTransform localFrame1(localPos1, q); + + const PxJointLinearLimitPair limit(PxTolerancesScale(), minLimit, maxLimit); + + if(useD6) + { + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setMotion(PxD6Axis::eX, PxD6Motion::eFREE); + if(minLimit==maxLimit) + j->setMotion(PxD6Axis::eX, PxD6Motion::eLOCKED); + else if(minLimit>maxLimit) + j->setMotion(PxD6Axis::eX, PxD6Motion::eFREE); + else// if(minLimitsetMotion(PxD6Axis::eX, PxD6Motion::eLIMITED); + j->setLinearLimit(PxD6Axis::eX, limit); + } + return j; + } + else + { + PxPrismaticJoint* j = PxPrismaticJointCreate(physics, actor0, localFrame0, actor1, localFrame1); + if(minLimitsetPrismaticJointFlag(PxPrismaticJointFlag::eLIMIT_ENABLED, true); + j->setLimit(limit); + } + return j; + } +} + +PxJoint* physx::PxD6JointCreate_Revolute(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float minLimit, float maxLimit, bool useD6) +{ + const PxQuat q = PxShortestRotation(gX, axis); + const PxTransform localFrame0(localPos0, q); + const PxTransform localFrame1(localPos1, q); + + const PxJointAngularLimitPair limit(minLimit, maxLimit); + + if(useD6) + { + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + if(minLimit==maxLimit) + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eLOCKED); + else if(minLimit>maxLimit) + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); + else// if(minLimitsetMotion(PxD6Axis::eTWIST, PxD6Motion::eLIMITED); + j->setTwistLimit(limit); + } + return j; + } + else + { + PxRevoluteJoint* j = PxRevoluteJointCreate(physics, actor0, localFrame0, actor1, localFrame1); + if(minLimitsetRevoluteJointFlag(PxRevoluteJointFlag::eLIMIT_ENABLED, true); + j->setLimit(limit); + } + return j; + } +} + +PxJoint* physx::PxD6JointCreate_Spherical(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, float limit1, float limit2, bool useD6) +{ + const PxQuat q = PxShortestRotation(gX, axis); + const PxTransform localFrame0(localPos0, q); + const PxTransform localFrame1(localPos1, q); + + const PxJointLimitCone limit(limit1, limit2); + + if(useD6) + { + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); + if(limit1>0.0f && limit2>0.0f) + { + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eLIMITED); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eLIMITED); + j->setSwingLimit(limit); + } + else + { + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE); + } + return j; + } + else + { + PxSphericalJoint* j = PxSphericalJointCreate(physics, actor0, localFrame0, actor1, localFrame1); + if(limit1>0.0f && limit2>0.0f) + { + j->setSphericalJointFlag(PxSphericalJointFlag::eLIMIT_ENABLED, true); + j->setLimitCone(limit); + } + return j; + } +} + +PxJoint* physx::PxD6JointCreate_GenericCone(float& apiroty, float& apirotz, PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, float minLimit1, float maxLimit1, float minLimit2, float maxLimit2, bool useD6) +{ + const float DesiredMinSwingY = minLimit1; + const float DesiredMaxSwingY = maxLimit1; + const float DesiredMinSwingZ = minLimit2; + const float DesiredMaxSwingZ = maxLimit2; + const float APIMaxY = (DesiredMaxSwingY - DesiredMinSwingY)*0.5f; + const float APIMaxZ = (DesiredMaxSwingZ - DesiredMinSwingZ)*0.5f; + const float APIRotY = (DesiredMaxSwingY + DesiredMinSwingY)*0.5f; + const float APIRotZ = (DesiredMaxSwingZ + DesiredMinSwingZ)*0.5f; + apiroty = APIRotY; + apirotz = APIRotZ; + + const PxQuat RotY = getRotYQuat(APIRotY); + const PxQuat RotZ = getRotZQuat(APIRotZ); + const PxQuat Rot = RotY * RotZ; + + const PxTransform localFrame0(localPos0, Rot); + const PxTransform localFrame1(localPos1); + + const PxJointLimitCone limit(APIMaxY, APIMaxZ); + + if(useD6) + { + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eLIMITED); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eLIMITED); + j->setSwingLimit(limit); + return j; + } + else + { + PxSphericalJoint* j = PxSphericalJointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setSphericalJointFlag(PxSphericalJointFlag::eLIMIT_ENABLED, true); + j->setLimitCone(limit); + return j; + } +} + +PxJoint* physx::PxD6JointCreate_Pyramid(PxPhysics& physics, PxRigidActor* actor0, const PxVec3& localPos0, PxRigidActor* actor1, const PxVec3& localPos1, const PxVec3& axis, + float minLimit1, float maxLimit1, float minLimit2, float maxLimit2) +{ + const PxQuat q = PxShortestRotation(gX, axis); + const PxTransform localFrame0(localPos0, q); + const PxTransform localFrame1(localPos1, q); + + const PxJointLimitPyramid limit(minLimit1, maxLimit1, minLimit2, maxLimit2); + + PxD6Joint* j = PxD6JointCreate(physics, actor0, localFrame0, actor1, localFrame1); + j->setMotion(PxD6Axis::eTWIST, PxD6Motion::eFREE); + if(limit.isValid()) + { + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eLIMITED); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eLIMITED); + j->setPyramidSwingLimit(limit); + } + else + { + j->setMotion(PxD6Axis::eSWING1, PxD6Motion::eFREE); + j->setMotion(PxD6Axis::eSWING2, PxD6Motion::eFREE); + } + return j; +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp new file mode 100644 index 000000000..e8a1470f4 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp @@ -0,0 +1,205 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtDefaultCpuDispatcher.h" +#include "ExtCpuWorkerThread.h" +#include "ExtTaskQueueHelper.h" +#include "PsString.h" + +using namespace physx; + +PxDefaultCpuDispatcher* physx::PxDefaultCpuDispatcherCreate(PxU32 numThreads, PxU32* affinityMasks) +{ + return PX_NEW(Ext::DefaultCpuDispatcher)(numThreads, affinityMasks); +} + +#if !PX_PS4 && !PX_XBOXONE && !PX_SWITCH +void Ext::DefaultCpuDispatcher::getAffinityMasks(PxU32* affinityMasks, PxU32 threadCount) +{ + for(PxU32 i=0; i < threadCount; i++) + { + affinityMasks[i] = 0; + } +} +#endif + +Ext::DefaultCpuDispatcher::DefaultCpuDispatcher(PxU32 numThreads, PxU32* affinityMasks) + : mQueueEntryPool(EXT_TASK_QUEUE_ENTRY_POOL_SIZE, "QueueEntryPool"), mNumThreads(numThreads), mShuttingDown(false) +#if PX_PROFILE + ,mRunProfiled(true) +#else + ,mRunProfiled(false) +#endif +{ + PxU32* defaultAffinityMasks = NULL; + + if(!affinityMasks) + { + defaultAffinityMasks = reinterpret_cast(PX_ALLOC(numThreads * sizeof(PxU32), "ThreadAffinityMasks")); + getAffinityMasks(defaultAffinityMasks, numThreads); + affinityMasks = defaultAffinityMasks; + } + + // initialize threads first, then start + + mWorkerThreads = reinterpret_cast(PX_ALLOC(numThreads * sizeof(CpuWorkerThread), "CpuWorkerThread")); + const PxU32 nameLength = 32; + mThreadNames = reinterpret_cast(PX_ALLOC(nameLength * numThreads, "CpuWorkerThreadName")); + + if (mWorkerThreads) + { + for(PxU32 i = 0; i < numThreads; ++i) + { + PX_PLACEMENT_NEW(mWorkerThreads+i, CpuWorkerThread)(); + mWorkerThreads[i].initialize(this); + } + + for(PxU32 i = 0; i < numThreads; ++i) + { + if (mThreadNames) + { + char* threadName = reinterpret_cast(mThreadNames + (i*nameLength)); + Ps::snprintf(threadName, nameLength, "PxWorker%02d", i); + mWorkerThreads[i].setName(threadName); + } + + mWorkerThreads[i].setAffinityMask(affinityMasks[i]); + mWorkerThreads[i].start(Ps::Thread::getDefaultStackSize()); + } + + if (defaultAffinityMasks) + PX_FREE(defaultAffinityMasks); + } + else + { + mNumThreads = 0; + } +} + +Ext::DefaultCpuDispatcher::~DefaultCpuDispatcher() +{ + for(PxU32 i = 0; i < mNumThreads; ++i) + mWorkerThreads[i].signalQuit(); + + mShuttingDown = true; + mWorkReady.set(); + for(PxU32 i = 0; i < mNumThreads; ++i) + mWorkerThreads[i].waitForQuit(); + + for(PxU32 i = 0; i < mNumThreads; ++i) + mWorkerThreads[i].~CpuWorkerThread(); + + PX_FREE(mWorkerThreads); + + if (mThreadNames) + PX_FREE(mThreadNames); +} + +void Ext::DefaultCpuDispatcher::submitTask(PxBaseTask& task) +{ + if(!mNumThreads) + { + // no worker threads, run directly + runTask(task); + task.release(); + return; + } + + // TODO: Could use TLS to make this more efficient + const Ps::Thread::Id currentThread = Ps::Thread::getId(); + for(PxU32 i = 0; i < mNumThreads; ++i) + { + if(mWorkerThreads[i].tryAcceptJobToLocalQueue(task, currentThread)) + return mWorkReady.set(); + } + + SharedQueueEntry* entry = mQueueEntryPool.getEntry(&task); + if (entry) + { + mJobList.push(*entry); + mWorkReady.set(); + } +} + +PxBaseTask* Ext::DefaultCpuDispatcher::fetchNextTask() +{ + PxBaseTask* task = getJob(); + + if(!task) + task = stealJob(); + + return task; +} + +void Ext::DefaultCpuDispatcher::release() +{ + PX_DELETE(this); +} + +PxBaseTask* Ext::DefaultCpuDispatcher::getJob(void) +{ + return TaskQueueHelper::fetchTask(mJobList, mQueueEntryPool); +} + +PxBaseTask* Ext::DefaultCpuDispatcher::stealJob() +{ + PxBaseTask* ret = NULL; + + for(PxU32 i = 0; i < mNumThreads; ++i) + { + ret = mWorkerThreads[i].giveUpJob(); + + if(ret != NULL) + break; + } + + return ret; +} + +void Ext::DefaultCpuDispatcher::resetWakeSignal() +{ + mWorkReady.reset(); + + // The code below is necessary to avoid deadlocks on shut down. + // A thread usually loops as follows: + // while quit is not signaled + // 1) reset wake signal + // 2) fetch work + // 3) if work -> process + // 4) else -> wait for wake signal + // + // If a thread reaches 1) after the thread pool signaled wake up, + // the wake up sync gets reset and all other threads which have not + // passed 4) already will wait forever. + // The code below makes sure that on shutdown, the wake up signal gets + // sent again after it was reset + // + if (mShuttingDown) + mWorkReady.set(); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h new file mode 100644 index 000000000..fce239635 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h @@ -0,0 +1,122 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_EXTENSIONS_NP_DEFAULT_CPU_DISPATCHER_H +#define PX_PHYSICS_EXTENSIONS_NP_DEFAULT_CPU_DISPATCHER_H + +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PsSync.h" +#include "PsSList.h" +#include "PxDefaultCpuDispatcher.h" +#include "ExtSharedQueueEntryPool.h" +#include "task/PxTask.h" +#include "common/PxProfileZone.h" + +namespace physx +{ + +namespace Ext +{ + class CpuWorkerThread; + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif // Because of the SList member I assume + + class DefaultCpuDispatcher : public PxDefaultCpuDispatcher, public Ps::UserAllocated + { + friend class TaskQueueHelper; + + private: + DefaultCpuDispatcher() : mQueueEntryPool(0) {} + ~DefaultCpuDispatcher(); + public: + DefaultCpuDispatcher(PxU32 numThreads, PxU32* affinityMasks); + + //--------------------------------------------------------------------------------- + // PxCpuDispatcher implementation + //--------------------------------------------------------------------------------- + virtual void submitTask(PxBaseTask& task); + virtual PxU32 getWorkerCount() const { return mNumThreads; } + + //--------------------------------------------------------------------------------- + // PxDefaultCpuDispatcher implementation + //--------------------------------------------------------------------------------- + virtual void release(); + + virtual void setRunProfiled(bool runProfiled) { mRunProfiled = runProfiled; } + + virtual bool getRunProfiled() const { return mRunProfiled; } + + //--------------------------------------------------------------------------------- + // DefaultCpuDispatcher + //--------------------------------------------------------------------------------- + PxBaseTask* getJob(); + PxBaseTask* stealJob(); + PxBaseTask* fetchNextTask(); + + PX_FORCE_INLINE void runTask(PxBaseTask& task) + { +#if PX_SUPPORT_PXTASK_PROFILING + if(mRunProfiled) + { + PX_PROFILE_ZONE(task.getName(), task.getContextId()); + task.run(); + } + else +#endif + task.run(); + } + + void waitForWork() { mWorkReady.wait(); } + void resetWakeSignal(); + + static void getAffinityMasks(PxU32* affinityMasks, PxU32 threadCount); + + protected: + CpuWorkerThread* mWorkerThreads; + SharedQueueEntryPool<> mQueueEntryPool; + Ps::SList mJobList; + Ps::Sync mWorkReady; + PxU8* mThreadNames; + PxU32 mNumThreads; + bool mShuttingDown; + bool mRunProfiled; + }; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Ext +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp new file mode 100644 index 000000000..d938a28b0 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp @@ -0,0 +1,104 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxAssert.h" +#include "PxDefaultErrorCallback.h" +#include "PsString.h" +#include "PsThread.h" +#include + +using namespace physx; + + +PxDefaultErrorCallback::PxDefaultErrorCallback() +{ +} + +PxDefaultErrorCallback::~PxDefaultErrorCallback() +{ +} + +void PxDefaultErrorCallback::reportError(PxErrorCode::Enum e, const char* message, const char* file, int line) +{ + const char* errorCode = NULL; + + switch (e) + { + case PxErrorCode::eNO_ERROR: + errorCode = "no error"; + break; + case PxErrorCode::eINVALID_PARAMETER: + errorCode = "invalid parameter"; + break; + case PxErrorCode::eINVALID_OPERATION: + errorCode = "invalid operation"; + break; + case PxErrorCode::eOUT_OF_MEMORY: + errorCode = "out of memory"; + break; + case PxErrorCode::eDEBUG_INFO: + errorCode = "info"; + break; + case PxErrorCode::eDEBUG_WARNING: + errorCode = "warning"; + break; + case PxErrorCode::ePERF_WARNING: + errorCode = "performance warning"; + break; + case PxErrorCode::eABORT: + errorCode = "abort"; + break; + case PxErrorCode::eINTERNAL_ERROR: + errorCode = "internal error"; + break; + case PxErrorCode::eMASK_ALL: + errorCode = "unknown error"; + break; + } + + PX_ASSERT(errorCode); + if(errorCode) + { + char buffer[1024]; + sprintf(buffer, "%s (%d) : %s : %s\n", file, line, errorCode, message); + + physx::shdfnd::printString(buffer); + + // in debug builds halt execution for abort codes + PX_ASSERT(e != PxErrorCode::eABORT); + + // in release builds we also want to halt execution + // and make sure that the error message is flushed + while (e == PxErrorCode::eABORT) + { + physx::shdfnd::printString(buffer); + physx::shdfnd::Thread::sleep(1000); + } + } +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp new file mode 100644 index 000000000..16e28fba5 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp @@ -0,0 +1,336 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxDefaultSimulationFilterShader.h" +#include "PsIntrinsics.h" +#include "PxRigidActor.h" +#include "PxShape.h" +#include "PsAllocator.h" +#include "CmPhysXCommon.h" +#include "PsInlineArray.h" +#include "PsFoundation.h" + +using namespace physx; + +namespace +{ + #define GROUP_SIZE 32 + + struct PxCollisionBitMap + { + PX_INLINE PxCollisionBitMap() : enable(true) {} + + bool operator()() const { return enable; } + bool& operator= (const bool &v) { enable = v; return enable; } + + private: + bool enable; + }; + + PxCollisionBitMap gCollisionTable[GROUP_SIZE][GROUP_SIZE]; + + PxFilterOp::Enum gFilterOps[3] = { PxFilterOp::PX_FILTEROP_AND, PxFilterOp::PX_FILTEROP_AND, PxFilterOp::PX_FILTEROP_AND }; + + PxGroupsMask gFilterConstants[2]; + + bool gFilterBool = false; + + static void gAND(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(mask0.bits0 & mask1.bits0); + results.bits1 = PxU16(mask0.bits1 & mask1.bits1); + results.bits2 = PxU16(mask0.bits2 & mask1.bits2); + results.bits3 = PxU16(mask0.bits3 & mask1.bits3); + } + static void gOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(mask0.bits0 | mask1.bits0); + results.bits1 = PxU16(mask0.bits1 | mask1.bits1); + results.bits2 = PxU16(mask0.bits2 | mask1.bits2); + results.bits3 = PxU16(mask0.bits3 | mask1.bits3); + } + static void gXOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(mask0.bits0 ^ mask1.bits0); + results.bits1 = PxU16(mask0.bits1 ^ mask1.bits1); + results.bits2 = PxU16(mask0.bits2 ^ mask1.bits2); + results.bits3 = PxU16(mask0.bits3 ^ mask1.bits3); + } + static void gNAND(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(~(mask0.bits0 & mask1.bits0)); + results.bits1 = PxU16(~(mask0.bits1 & mask1.bits1)); + results.bits2 = PxU16(~(mask0.bits2 & mask1.bits2)); + results.bits3 = PxU16(~(mask0.bits3 & mask1.bits3)); + } + static void gNOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(~(mask0.bits0 | mask1.bits0)); + results.bits1 = PxU16(~(mask0.bits1 | mask1.bits1)); + results.bits2 = PxU16(~(mask0.bits2 | mask1.bits2)); + results.bits3 = PxU16(~(mask0.bits3 | mask1.bits3)); + } + static void gNXOR(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(~(mask0.bits0 ^ mask1.bits0)); + results.bits1 = PxU16(~(mask0.bits1 ^ mask1.bits1)); + results.bits2 = PxU16(~(mask0.bits2 ^ mask1.bits2)); + results.bits3 = PxU16(~(mask0.bits3 ^ mask1.bits3)); + } + + static void gSWAP_AND(PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1) + { + results.bits0 = PxU16(mask0.bits0 & mask1.bits2); + results.bits1 = PxU16(mask0.bits1 & mask1.bits3); + results.bits2 = PxU16(mask0.bits2 & mask1.bits0); + results.bits3 = PxU16(mask0.bits3 & mask1.bits1); + } + + typedef void (*FilterFunction) (PxGroupsMask& results, const PxGroupsMask& mask0, const PxGroupsMask& mask1); + + FilterFunction const gTable[] = { gAND, gOR, gXOR, gNAND, gNOR, gNXOR, gSWAP_AND }; + + static PxFilterData convert(const PxGroupsMask& mask) + { + PxFilterData fd; + + fd.word2 = PxU32(mask.bits0 | (mask.bits1 << 16)); + fd.word3 = PxU32(mask.bits2 | (mask.bits3 << 16)); + + return fd; + } + + static PxGroupsMask convert(const PxFilterData& fd) + { + PxGroupsMask mask; + + mask.bits0 = PxU16((fd.word2 & 0xffff)); + mask.bits1 = PxU16((fd.word2 >> 16)); + mask.bits2 = PxU16((fd.word3 & 0xffff)); + mask.bits3 = PxU16((fd.word3 >> 16)); + + return mask; + } + + static bool getFilterData(const PxActor& actor, PxFilterData& fd) + { + PxActorType::Enum aType = actor.getType(); + switch (aType) + { + case PxActorType::eRIGID_DYNAMIC: + case PxActorType::eRIGID_STATIC: + case PxActorType::eARTICULATION_LINK: + { + const PxRigidActor& rActor = static_cast(actor); + PX_CHECK_AND_RETURN_VAL(rActor.getNbShapes() >= 1,"There must be a shape in actor", false); + + PxShape* shape = NULL; + rActor.getShapes(&shape, 1); + + fd = shape->getSimulationFilterData(); + } + break; + + case PxActorType::eACTOR_COUNT: + case PxActorType::eACTOR_FORCE_DWORD: + break; + } + + return true; + } + + PX_FORCE_INLINE static void adjustFilterData(bool groupsMask, const PxFilterData& src, PxFilterData& dst) + { + if (groupsMask) + { + dst.word2 = src.word2; + dst.word3 = src.word3; + } + else + dst.word0 = src.word0; + } + + template + static void setFilterData(PxActor& actor, const PxFilterData& fd) + { + PxActorType::Enum aType = actor.getType(); + switch (aType) + { + case PxActorType::eRIGID_DYNAMIC: + case PxActorType::eRIGID_STATIC: + case PxActorType::eARTICULATION_LINK: + { + const PxRigidActor& rActor = static_cast(actor); + + PxShape* shape; + for(PxU32 i=0; i < rActor.getNbShapes(); i++) + { + rActor.getShapes(&shape, 1, i); + + // retrieve current group mask + PxFilterData resultFd = shape->getSimulationFilterData(); + + adjustFilterData(TGroupsMask, fd, resultFd); + + // set new filter data + shape->setSimulationFilterData(resultFd); + } + } + break; + case PxActorType::eACTOR_COUNT: + case PxActorType::eACTOR_FORCE_DWORD: + break; + } + } +} + +PxFilterFlags physx::PxDefaultSimulationFilterShader( + PxFilterObjectAttributes attributes0, + PxFilterData filterData0, + PxFilterObjectAttributes attributes1, + PxFilterData filterData1, + PxPairFlags& pairFlags, + const void* constantBlock, + PxU32 constantBlockSize) +{ + PX_UNUSED(constantBlock); + PX_UNUSED(constantBlockSize); + + // let triggers through + if(PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1)) + { + pairFlags = PxPairFlag::eTRIGGER_DEFAULT; + return PxFilterFlags(); + } + + // Collision Group + if (!gCollisionTable[filterData0.word0][filterData1.word0]()) + { + return PxFilterFlag::eSUPPRESS; + } + + // Filter function + PxGroupsMask g0 = convert(filterData0); + PxGroupsMask g1 = convert(filterData1); + + PxGroupsMask g0k0; gTable[gFilterOps[0]](g0k0, g0, gFilterConstants[0]); + PxGroupsMask g1k1; gTable[gFilterOps[1]](g1k1, g1, gFilterConstants[1]); + PxGroupsMask final; gTable[gFilterOps[2]](final, g0k0, g1k1); + + bool r = final.bits0 || final.bits1 || final.bits2 || final.bits3; + if (r != gFilterBool) + { + return PxFilterFlag::eSUPPRESS; + } + + pairFlags = PxPairFlag::eCONTACT_DEFAULT; + + return PxFilterFlags(); +} + +bool physx::PxGetGroupCollisionFlag(const PxU16 group1, const PxU16 group2) +{ + PX_CHECK_AND_RETURN_NULL(group1 < 32 && group2 < 32, "Group must be less than 32"); + + return gCollisionTable[group1][group2](); +} + +void physx::PxSetGroupCollisionFlag(const PxU16 group1, const PxU16 group2, const bool enable) +{ + PX_CHECK_AND_RETURN(group1 < 32 && group2 < 32, "Group must be less than 32"); + + gCollisionTable[group1][group2] = enable; + gCollisionTable[group2][group1] = enable; +} + +PxU16 physx::PxGetGroup(const PxActor& actor) +{ + PxFilterData fd; + getFilterData(actor, fd); + return PxU16(fd.word0); +} + +void physx::PxSetGroup(PxActor& actor, const PxU16 collisionGroup) +{ + PX_CHECK_AND_RETURN(collisionGroup < 32,"Collision group must be less than 32"); + PxFilterData fd(collisionGroup, 0, 0, 0); + setFilterData(actor, fd); +} + +void physx::PxGetFilterOps(PxFilterOp::Enum& op0, PxFilterOp::Enum& op1, PxFilterOp::Enum& op2) +{ + op0 = gFilterOps[0]; + op1 = gFilterOps[1]; + op2 = gFilterOps[2]; +} + +void physx::PxSetFilterOps(const PxFilterOp::Enum& op0, const PxFilterOp::Enum& op1, const PxFilterOp::Enum& op2) +{ + gFilterOps[0] = op0; + gFilterOps[1] = op1; + gFilterOps[2] = op2; +} + +bool physx::PxGetFilterBool() +{ + return gFilterBool; +} + +void physx::PxSetFilterBool(const bool enable) +{ + gFilterBool = enable; +} + +void physx::PxGetFilterConstants(PxGroupsMask& c0, PxGroupsMask& c1) +{ + c0 = gFilterConstants[0]; + c1 = gFilterConstants[1]; +} + +void physx::PxSetFilterConstants(const PxGroupsMask& c0, const PxGroupsMask& c1) +{ + gFilterConstants[0] = c0; + gFilterConstants[1] = c1; +} + +PxGroupsMask physx::PxGetGroupsMask(const PxActor& actor) +{ + PxFilterData fd; + if (getFilterData(actor, fd)) + return convert(fd); + else + return PxGroupsMask(); +} + +void physx::PxSetGroupsMask(PxActor& actor, const PxGroupsMask& mask) +{ + PxFilterData fd = convert(mask); + setFilterData(actor, fd); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp new file mode 100644 index 000000000..44e5fb9c9 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp @@ -0,0 +1,194 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxAssert.h" +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxMemory.h" + +#include +#include "PsFoundation.h" +#include "PxDefaultStreams.h" +#include "SnFile.h" +#include "CmPhysXCommon.h" +#include "PsUtilities.h" +#include "PsBitUtils.h" + +using namespace physx; + +PxDefaultMemoryOutputStream::PxDefaultMemoryOutputStream(PxAllocatorCallback &allocator) +: mAllocator (allocator) +, mData (NULL) +, mSize (0) +, mCapacity (0) +{ +} + +PxDefaultMemoryOutputStream::~PxDefaultMemoryOutputStream() +{ + if(mData) + mAllocator.deallocate(mData); +} + +PxU32 PxDefaultMemoryOutputStream::write(const void* src, PxU32 size) +{ + PxU32 expectedSize = mSize + size; + if(expectedSize > mCapacity) + { + mCapacity = PxMax(Ps::nextPowerOfTwo(expectedSize), 4096u); + + PxU8* newData = reinterpret_cast(mAllocator.allocate(mCapacity,"PxDefaultMemoryOutputStream",__FILE__,__LINE__)); + PX_ASSERT(newData!=NULL); + + PxMemCopy(newData, mData, mSize); + if(mData) + mAllocator.deallocate(mData); + + mData = newData; + } + PxMemCopy(mData+mSize, src, size); + mSize += size; + return size; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxDefaultMemoryInputData::PxDefaultMemoryInputData(PxU8* data, PxU32 length) : + mSize (length), + mData (data), + mPos (0) +{ +} + +PxU32 PxDefaultMemoryInputData::read(void* dest, PxU32 count) +{ + PxU32 length = PxMin(count, mSize-mPos); + PxMemCopy(dest, mData+mPos, length); + mPos += length; + return length; +} + +PxU32 PxDefaultMemoryInputData::getLength() const +{ + return mSize; +} + +void PxDefaultMemoryInputData::seek(PxU32 offset) +{ + mPos = PxMin(mSize, offset); +} + +PxU32 PxDefaultMemoryInputData::tell() const +{ + return mPos; +} + +PxDefaultFileOutputStream::PxDefaultFileOutputStream(const char* filename) +{ + mFile = NULL; + sn::fopen_s(&mFile, filename, "wb"); + // PT: when this fails, check that: + // - the path is correct + // - the file does not already exist. If it does, check that it is not write protected. + if(NULL == mFile) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "Unable to open file %s, errno 0x%x\n",filename,errno); + } + PX_ASSERT(mFile); +} + +PxDefaultFileOutputStream::~PxDefaultFileOutputStream() +{ + if(mFile) + fclose(mFile); +} + +PxU32 PxDefaultFileOutputStream::write(const void* src, PxU32 count) +{ + return mFile ? PxU32(fwrite(src, 1, count, mFile)) : 0; +} + +bool PxDefaultFileOutputStream::isValid() +{ + return mFile != NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +PxDefaultFileInputData::PxDefaultFileInputData(const char* filename) +{ + mFile = NULL; + sn::fopen_s(&mFile, filename, "rb"); + + if(mFile) + { + fseek(mFile, 0, SEEK_END); + mLength = PxU32(ftell(mFile)); + fseek(mFile, 0, SEEK_SET); + } + else + { + mLength = 0; + } +} + +PxDefaultFileInputData::~PxDefaultFileInputData() +{ + if(mFile) + fclose(mFile); +} + +PxU32 PxDefaultFileInputData::read(void* dest, PxU32 count) +{ + PX_ASSERT(mFile); + const size_t size = fread(dest, 1, count, mFile); + // there should be no assert here since by spec of PxInputStream we can read fewer bytes than expected + return PxU32(size); +} + +PxU32 PxDefaultFileInputData::getLength() const +{ + return mLength; +} + +void PxDefaultFileInputData::seek(PxU32 pos) +{ + fseek(mFile, long(pos), SEEK_SET); +} + +PxU32 PxDefaultFileInputData::tell() const +{ + return PxU32(ftell(mFile)); +} + +bool PxDefaultFileInputData::isValid() const +{ + return mFile != NULL; +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp new file mode 100644 index 000000000..33e08fe1e --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp @@ -0,0 +1,334 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtDistanceJoint.h" +#include "ExtConstraintHelper.h" +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxDistanceJoint* physx::PxDistanceJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxDistanceJointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxDistanceJointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxDistanceJointCreate: actors must be different"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxD6JointCreate: at least one actor must be dynamic"); + + DistanceJoint* j; + PX_NEW_SERIALIZED(j, DistanceJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +PxReal DistanceJoint::getDistance() const +{ + return getRelativeTransform().p.magnitude(); +} + +void DistanceJoint::setMinDistance(PxReal distance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(distance), "PxDistanceJoint::setMinDistance: invalid parameter"); + data().minDistance = distance; + markDirty(); +} + +PxReal DistanceJoint::getMinDistance() const +{ + return data().minDistance; +} + +void DistanceJoint::setMaxDistance(PxReal distance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(distance), "PxDistanceJoint::setMaxDistance: invalid parameter"); + data().maxDistance = distance; + markDirty(); +} + +PxReal DistanceJoint::getMaxDistance() const +{ + return data().maxDistance; +} + +void DistanceJoint::setTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance), "PxDistanceJoint::setTolerance: invalid parameter"); + data().tolerance = tolerance; + markDirty(); +} + +PxReal DistanceJoint::getTolerance() const +{ + return data().tolerance; +} + +void DistanceJoint::setStiffness(PxReal stiffness) +{ + PX_CHECK_AND_RETURN(PxIsFinite(stiffness), "PxDistanceJoint::setStiffness: invalid parameter"); + data().stiffness = stiffness; + markDirty(); +} + +PxReal DistanceJoint::getStiffness() const +{ + return data().stiffness; +} + +void DistanceJoint::setDamping(PxReal damping) +{ + PX_CHECK_AND_RETURN(PxIsFinite(damping), "PxDistanceJoint::setDamping: invalid parameter"); + data().damping = damping; + markDirty(); +} + +PxReal DistanceJoint::getDamping() const +{ + return data().damping; +} + +PxDistanceJointFlags DistanceJoint::getDistanceJointFlags(void) const +{ + return data().jointFlags; +} + +void DistanceJoint::setDistanceJointFlags(PxDistanceJointFlags flags) +{ + data().jointFlags = flags; + markDirty(); +} + +void DistanceJoint::setDistanceJointFlag(PxDistanceJointFlag::Enum flag, bool value) +{ + if(value) + data().jointFlags |= flag; + else + data().jointFlags &= ~flag; + markDirty(); +} + +bool DistanceJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(DistanceJointData)); + return mPxConstraint!=NULL; +} + +void DistanceJoint::exportExtraData(PxSerializationContext& stream) +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(DistanceJointData)); + } + stream.writeName(mName); +} + +void DistanceJoint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + + context.readName(mName); +} + +void DistanceJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +DistanceJoint* DistanceJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + DistanceJoint* obj = new (address) DistanceJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(DistanceJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetDistanceJointShaderTable() +{ + return &DistanceJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void DistanceJointProject(const void* /*constantBlock*/, PxTransform& /*bodyAToWorld*/, PxTransform& /*bodyBToWorld*/, bool /*projectToA*/) +{ + // TODO +} + +static void DistanceJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags) +{ + const DistanceJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + viz.visualizeJointFrames(cA2w, cB2w); + + // PT: we consider the following is part of the joint's "limits" since that's the only available flag we have + if(flags & PxConstraintVisualizationFlag::eLIMITS) + { + const bool enforceMax = (data.jointFlags & PxDistanceJointFlag::eMAX_DISTANCE_ENABLED); + const bool enforceMin = (data.jointFlags & PxDistanceJointFlag::eMIN_DISTANCE_ENABLED); + if(!enforceMin && !enforceMax) + return; + + PxVec3 dir = cB2w.p - cA2w.p; + const float currentDist = dir.normalize(); + + PxU32 color = 0x00ff00; + if(enforceMax && currentDist>data.maxDistance) + color = 0xff0000; + if(enforceMin && currentDist(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + cA2wOut = cB2w.p; + cB2wOut = cB2w.p; + + PxVec3 direction = cA2w.p - cB2w.p; + const PxReal distance = direction.normalize(); + + const bool enforceMax = (data.jointFlags & PxDistanceJointFlag::eMAX_DISTANCE_ENABLED); + const bool enforceMin = (data.jointFlags & PxDistanceJointFlag::eMIN_DISTANCE_ENABLED); + +#define EPS_REAL 1.192092896e-07F + + if(distance < EPS_REAL) + direction = PxVec3(1.0f, 0.0f, 0.0f); + + Px1DConstraint* c = constraints; + + const PxVec3 angular0 = ch.getRa().cross(direction); + const PxVec3 angular1 = ch.getRb().cross(direction); + + setupContraint(*c, direction, angular0, angular1, data); + + //add tolerance so we don't have contact-style jitter problem. + + if(data.minDistance == data.maxDistance && enforceMin && enforceMax) + { + const PxReal error = distance - data.maxDistance; + c->geometricError = error > data.tolerance ? error - data.tolerance : + error < -data.tolerance ? error + data.tolerance : 0.0f; + } + else if(enforceMax && distance > data.maxDistance) + { + c->geometricError = distance - data.maxDistance - data.tolerance; + c->maxImpulse = 0.0f; + } + else if(enforceMin && distance < data.minDistance) + { + c->geometricError = distance - data.minDistance + data.tolerance; + c->minImpulse = 0.0f; + } + else + { + if(enforceMin && enforceMax) + { + // since we dont know the current rigid velocity, we need to insert row for both limits + Px1DConstraint* minConstraint = constraints; + minConstraint->geometricError = distance - data.minDistance; + minConstraint->minImpulse = 0.0f; + minConstraint->maxImpulse = FLT_MAX; + minConstraint->flags |= Px1DConstraintFlag::eKEEPBIAS; + + Px1DConstraint* maxConstraint = constraints; + maxConstraint++; + + setupContraint(*maxConstraint, direction, angular0, angular1, data); + + maxConstraint->geometricError = distance - data.maxDistance; + maxConstraint->minImpulse = -FLT_MAX; + maxConstraint->maxImpulse = 0.0f; + maxConstraint->flags |= Px1DConstraintFlag::eKEEPBIAS; + + return 2; + } + else if(enforceMax) + { + c->geometricError = distance - data.maxDistance; + c->minImpulse = -FLT_MAX; + c->maxImpulse = 0.0f; + c->flags |= Px1DConstraintFlag::eKEEPBIAS; + return 0; + } + else if(enforceMin) + { + c->geometricError = distance - data.minDistance; + c->minImpulse = 0.0f; + c->maxImpulse = FLT_MAX; + c->flags |= Px1DConstraintFlag::eKEEPBIAS; + return 0; + } + } + + return 1; +} + +PxConstraintShaderTable Ext::DistanceJoint::sShaders = { DistanceJointSolverPrep, DistanceJointProject, DistanceJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h new file mode 100644 index 000000000..09c136434 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h @@ -0,0 +1,139 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef NP_DISTANCEJOINTCONSTRAINT_H +#define NP_DISTANCEJOINTCONSTRAINT_H + +#include "PsUserAllocated.h" +#include "ExtJoint.h" +#include "PxDistanceJoint.h" +#include "PxTolerancesScale.h" +#include "CmUtils.h" + +namespace physx +{ +struct PxDistanceJointGeneratedValues; +namespace Ext +{ + + struct DistanceJointData : public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxReal minDistance; + PxReal maxDistance; + PxReal tolerance; + PxReal stiffness; + PxReal damping; + + PxDistanceJointFlags jointFlags; + }; + + typedef Joint DistanceJointT; + class DistanceJoint : public DistanceJointT + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + // PX_SERIALIZATION + DistanceJoint(PxBaseFlags baseFlags) : DistanceJointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static DistanceJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + //~PX_SERIALIZATION + + DistanceJoint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + DistanceJointT(PxJointConcreteType::eDISTANCE, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(DistanceJointData), "DistanceJointData") + { + DistanceJointData* data = static_cast(mData); + + data->stiffness = 0.0f; + data->damping = 0.0f; + data->minDistance = 0.0f; + data->maxDistance = 0.0f; + data->tolerance = 0.025f * scale.length; + data->jointFlags = PxDistanceJointFlag::eMAX_DISTANCE_ENABLED; + } + + // PxDistanceJoint + virtual PxReal getDistance() const; + virtual void setMinDistance(PxReal distance); + virtual PxReal getMinDistance() const; + virtual void setMaxDistance(PxReal distance); + virtual PxReal getMaxDistance() const; + virtual void setTolerance(PxReal tolerance); + virtual PxReal getTolerance() const; + virtual void setStiffness(PxReal spring); + virtual PxReal getStiffness() const; + virtual void setDamping(PxReal damping); + virtual PxReal getDamping() const; + virtual void setDistanceJointFlags(PxDistanceJointFlags flags); + virtual void setDistanceJointFlag(PxDistanceJointFlag::Enum flag, bool value); + virtual PxDistanceJointFlags getDistanceJointFlags() const; + //~PxDistanceJoint + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep()const {return sShaders.solverPrep;} + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE DistanceJointData& data() const + { + return *static_cast(mData); + } + }; + +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetDistanceJointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp b/src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp new file mode 100644 index 000000000..05cd12411 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp @@ -0,0 +1,193 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "PxExtensionsAPI.h" +#include "PsFoundation.h" +#include "PxMetaData.h" +#include "ExtDistanceJoint.h" +#include "ExtD6Joint.h" +#include "ExtFixedJoint.h" +#include "ExtPrismaticJoint.h" +#include "ExtRevoluteJoint.h" +#include "ExtSphericalJoint.h" +#include "PxRepXSerializer.h" +#include "SnRepXCoreSerializer.h" +#include "SnJointRepXSerializer.h" +#include "PxExtensionMetaDataObjects.h" +#include "PxSerializer.h" +#include "ExtSerialization.h" + +#if PX_SUPPORT_PVD +#include "ExtPvd.h" +#include "PxPvdDataStream.h" +#include "PxPvdClient.h" +#include "PsPvd.h" +#endif + +using namespace physx; +using namespace physx::pvdsdk; + +#if PX_SUPPORT_PVD +struct JointConnectionHandler : public PvdClient +{ + JointConnectionHandler() : mPvd(NULL),mConnected(false){} + + PvdDataStream* getDataStream() + { + return NULL; + } + PvdUserRenderer* getUserRender() + { + return NULL; + } + + void onPvdConnected() + { + PvdDataStream* stream = PvdDataStream::create(mPvd); + if(stream) + { + mConnected = true; + Ext::Pvd::sendClassDescriptions(*stream); + stream->release(); + } + } + + bool isConnected() const + { + return mConnected; + } + + void onPvdDisconnected() + { + mConnected = false; + } + + void flush() + { + } + + PsPvd* mPvd; + bool mConnected; +}; + +static JointConnectionHandler gPvdHandler; +#endif + +bool PxInitExtensions(PxPhysics& physics, PxPvd* pvd) +{ + PX_ASSERT(static_cast(&physics.getFoundation()) == &Ps::Foundation::getInstance()); + PX_UNUSED(physics); + PX_UNUSED(pvd); + Ps::Foundation::incRefCount(); + +#if PX_SUPPORT_PVD + if(pvd) + { + gPvdHandler.mPvd = static_cast(pvd); + gPvdHandler.mPvd->addClient(&gPvdHandler); + } +#endif + + return true; +} + +void PxCloseExtensions(void) +{ + Ps::Foundation::decRefCount(); + +#if PX_SUPPORT_PVD + if(gPvdHandler.mConnected) + { + PX_ASSERT(gPvdHandler.mPvd); + gPvdHandler.mPvd->removeClient(&gPvdHandler); + gPvdHandler.mPvd = NULL; + } +#endif +} + +void Ext::RegisterExtensionsSerializers(PxSerializationRegistry& sr) +{ + //for repx serialization + sr.registerRepXSerializer(PxConcreteType::eMATERIAL, PX_NEW_REPX_SERIALIZER( PxMaterialRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eSHAPE, PX_NEW_REPX_SERIALIZER( PxShapeRepXSerializer )); +// sr.registerRepXSerializer(PxConcreteType::eTRIANGLE_MESH, PX_NEW_REPX_SERIALIZER( PxTriangleMeshRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH33, PX_NEW_REPX_SERIALIZER( PxBVH33TriangleMeshRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH34, PX_NEW_REPX_SERIALIZER( PxBVH34TriangleMeshRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eHEIGHTFIELD, PX_NEW_REPX_SERIALIZER( PxHeightFieldRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eCONVEX_MESH, PX_NEW_REPX_SERIALIZER( PxConvexMeshRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eRIGID_STATIC, PX_NEW_REPX_SERIALIZER( PxRigidStaticRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eRIGID_DYNAMIC, PX_NEW_REPX_SERIALIZER( PxRigidDynamicRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eARTICULATION, PX_NEW_REPX_SERIALIZER( PxArticulationRepXSerializer )); + sr.registerRepXSerializer(PxConcreteType::eAGGREGATE, PX_NEW_REPX_SERIALIZER( PxAggregateRepXSerializer )); + + sr.registerRepXSerializer(PxJointConcreteType::eFIXED, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + sr.registerRepXSerializer(PxJointConcreteType::eDISTANCE, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + sr.registerRepXSerializer(PxJointConcreteType::eD6, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + sr.registerRepXSerializer(PxJointConcreteType::ePRISMATIC, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + sr.registerRepXSerializer(PxJointConcreteType::eREVOLUTE, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + sr.registerRepXSerializer(PxJointConcreteType::eSPHERICAL, PX_NEW_REPX_SERIALIZER( PxJointRepXSerializer )); + + //for binary serialization + sr.registerSerializer(PxJointConcreteType::eFIXED, PX_NEW_SERIALIZER_ADAPTER( FixedJoint )); + sr.registerSerializer(PxJointConcreteType::eDISTANCE, PX_NEW_SERIALIZER_ADAPTER( DistanceJoint )); + sr.registerSerializer(PxJointConcreteType::eD6, PX_NEW_SERIALIZER_ADAPTER( D6Joint) ); + sr.registerSerializer(PxJointConcreteType::ePRISMATIC, PX_NEW_SERIALIZER_ADAPTER( PrismaticJoint )); + sr.registerSerializer(PxJointConcreteType::eREVOLUTE, PX_NEW_SERIALIZER_ADAPTER( RevoluteJoint )); + sr.registerSerializer(PxJointConcreteType::eSPHERICAL, PX_NEW_SERIALIZER_ADAPTER( SphericalJoint )); +} + +void Ext::UnregisterExtensionsSerializers(PxSerializationRegistry& sr) +{ + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eFIXED)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eDISTANCE)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eD6 )); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::ePRISMATIC)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eREVOLUTE)); + PX_DELETE_SERIALIZER_ADAPTER(sr.unregisterSerializer(PxJointConcreteType::eSPHERICAL)); + + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eMATERIAL)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eSHAPE)); +// PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eTRIANGLE_MESH)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH33)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eTRIANGLE_MESH_BVH34)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eHEIGHTFIELD)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eCONVEX_MESH)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eRIGID_STATIC)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eRIGID_DYNAMIC)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eARTICULATION)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxConcreteType::eAGGREGATE)); + + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eFIXED)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eDISTANCE)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eD6)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::ePRISMATIC)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eREVOLUTE)); + PX_DELETE_REPX_SERIALIZER(sr.unregisterRepXSerializer(PxJointConcreteType::eSPHERICAL)); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp new file mode 100644 index 000000000..ab9538df9 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtFixedJoint.h" +#include "ExtConstraintHelper.h" +#include "CmVisualization.h" + +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxFixedJoint* physx::PxFixedJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxFixedJointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxFixedJointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxFixedJointCreate: at least one actor must be dynamic"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxFixedJointCreate: actors must be different"); + + FixedJoint* j; + PX_NEW_SERIALIZED(j, FixedJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +PxReal FixedJoint::getProjectionLinearTolerance() const +{ + return data().projectionLinearTolerance; +} + +void FixedJoint::setProjectionLinearTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxFixedJoint::setProjectionLinearTolerance: invalid parameter"); + data().projectionLinearTolerance = tolerance; + markDirty(); +} + +PxReal FixedJoint::getProjectionAngularTolerance() const +{ + return data().projectionAngularTolerance; +} + +void FixedJoint::setProjectionAngularTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0 && tolerance <= PxPi, "PxFixedJoint::setProjectionAngularTolerance: invalid parameter"); + data().projectionAngularTolerance = tolerance; markDirty(); +} + +bool FixedJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(FixedJointData)); + return mPxConstraint!=NULL; +} + +void FixedJoint::exportExtraData(PxSerializationContext& stream) const +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(FixedJointData)); + } + stream.writeName(mName); +} + +void FixedJoint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + context.readName(mName); +} + +void FixedJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +FixedJoint* FixedJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + FixedJoint* obj = new (address) FixedJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(FixedJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetFixedJointShaderTable() +{ + return &FixedJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void FixedJointProject(const void* constantBlock, PxTransform& bodyAToWorld, PxTransform& bodyBToWorld, bool projectToA) +{ + const FixedJointData &data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w, cB2cA, projected; + joint::computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA); + + bool linearTrunc, angularTrunc; + projected.p = joint::truncateLinear(cB2cA.p, data.projectionLinearTolerance, linearTrunc); + projected.q = joint::truncateAngular(cB2cA.q, PxSin(data.projectionAngularTolerance/2), PxCos(data.projectionAngularTolerance/2), angularTrunc); + + if(linearTrunc || angularTrunc) + joint::projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA); +} + +static void FixedJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags) +{ + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + { + const FixedJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + viz.visualizeJointFrames(cA2w, cB2w); + } +} + +static PxU32 FixedJointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool /*useExtendedLimits*/, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const FixedJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + if (cA2w.q.dot(cB2w.q)<0.0f) // minimum dist quat (equiv to flipping cB2bB.q, which we don't use anywhere) + cB2w.q = -cB2w.q; + + PxVec3 ra, rb; + ch.prepareLockedAxes(cA2w.q, cB2w.q, cA2w.transformInv(cB2w.p), 7, 7, ra, rb); + cA2wOut = ra + bA2w.p; + cB2wOut = rb + bB2w.p; + + + return ch.getCount(); +} + +PxConstraintShaderTable Ext::FixedJoint::sShaders = { FixedJointSolverPrep, FixedJointProject, FixedJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h new file mode 100644 index 000000000..34b8e3397 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_FIXEDJOINTCONSTRAINT_H +#define NP_FIXEDJOINTCONSTRAINT_H + +#include "ExtJoint.h" +#include "PxFixedJoint.h" +#include "CmUtils.h" + +namespace physx +{ +struct PxFixedJointGeneratedValues; +namespace Ext +{ + + struct FixedJointData : public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxReal projectionLinearTolerance; + PxReal projectionAngularTolerance; + }; + + typedef Joint FixedJointT; + + class FixedJoint : public FixedJointT + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + FixedJoint(PxBaseFlags baseFlags) : FixedJointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context) const; + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static FixedJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + // PxFixedJoint + virtual void setProjectionLinearTolerance(PxReal tolerance); + virtual PxReal getProjectionLinearTolerance() const; + virtual void setProjectionAngularTolerance(PxReal tolerance); + virtual PxReal getProjectionAngularTolerance() const; + //~PxFixedJoint + + FixedJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + FixedJointT(PxJointConcreteType::eFIXED, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(FixedJointData), "FixedJointData") + { + FixedJointData* data = static_cast(mData); + + data->projectionLinearTolerance = 1e10f; + data->projectionAngularTolerance = PxPi; + } + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep() const { return sShaders.solverPrep; } + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE FixedJointData& data() const + { + return *static_cast(mData); + } + }; +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetFixedJointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h b/src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h new file mode 100644 index 000000000..dcef2415c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h @@ -0,0 +1,404 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_INERTIATENSOR_H +#define PX_PHYSICS_EXTENSIONS_INERTIATENSOR_H + +#include "foundation/PxMat33.h" +#include "foundation/PxMathUtils.h" +#include "CmPhysXCommon.h" +#include "PsMathUtils.h" + +namespace physx +{ +namespace Ext +{ + class InertiaTensorComputer + { + public: + InertiaTensorComputer(bool initTozero = true); + InertiaTensorComputer(const PxMat33& inertia, const PxVec3& com, PxReal mass); + ~InertiaTensorComputer(); + + PX_INLINE void zero(); //sets to zero mass + PX_INLINE void setDiagonal(PxReal mass, const PxVec3& diagonal); //sets as a diagonal tensor + PX_INLINE void rotate(const PxMat33& rot); //rotates the mass + void translate(const PxVec3& t); //translates the mass + PX_INLINE void transform(const PxTransform& transform); //transforms the mass + PX_INLINE void scaleDensity(PxReal densityScale); //scales by a density factor + PX_INLINE void add(const InertiaTensorComputer& it); //adds a mass + PX_INLINE void center(); //recenters inertia around center of mass + + void setBox(const PxVec3& halfWidths); //sets as an axis aligned box + PX_INLINE void setBox(const PxVec3& halfWidths, const PxTransform* pose); //sets as an oriented box + + void setSphere(PxReal radius); + PX_INLINE void setSphere(PxReal radius, const PxTransform* pose); + + void setCylinder(int dir, PxReal r, PxReal l); + PX_INLINE void setCylinder(int dir, PxReal r, PxReal l, const PxTransform* pose); + + void setCapsule(int dir, PxReal r, PxReal l); + PX_INLINE void setCapsule(int dir, PxReal r, PxReal l, const PxTransform* pose); + void addCapsule(PxReal density, int dir, PxReal r, PxReal l, const PxTransform* pose = 0); + + void setEllipsoid(PxReal rx, PxReal ry, PxReal rz); + PX_INLINE void setEllipsoid(PxReal rx, PxReal ry, PxReal rz, const PxTransform* pose); + + PX_INLINE PxVec3 getCenterOfMass() const { return mG; } + PX_INLINE PxReal getMass() const { return mMass; } + PX_INLINE PxMat33 getInertia() const { return mI; } + + private: + PxMat33 mI; + PxVec3 mG; + PxReal mMass; + }; + + + //-------------------------------------------------------------- + // + // Helper routines + // + //-------------------------------------------------------------- + + // Special version allowing 2D quads + PX_INLINE PxReal volume(const PxVec3& extents) + { + PxReal v = 1.0f; + if(extents.x != 0.0f) v*=extents.x; + if(extents.y != 0.0f) v*=extents.y; + if(extents.z != 0.0f) v*=extents.z; + return v; + } + + // Sphere + PX_INLINE PxReal computeSphereRatio(PxReal radius) { return (4.0f/3.0f) * PxPi * radius * radius * radius; } + PxReal computeSphereMass(PxReal radius, PxReal density) { return density * computeSphereRatio(radius); } + PxReal computeSphereDensity(PxReal radius, PxReal mass) { return mass / computeSphereRatio(radius); } + + // Box + PX_INLINE PxReal computeBoxRatio(const PxVec3& extents) { return volume(extents); } + PxReal computeBoxMass(const PxVec3& extents, PxReal density) { return density * computeBoxRatio(extents); } + PxReal computeBoxDensity(const PxVec3& extents, PxReal mass) { return mass / computeBoxRatio(extents); } + + // Ellipsoid + PX_INLINE PxReal computeEllipsoidRatio(const PxVec3& extents) { return (4.0f/3.0f) * PxPi * volume(extents); } + PxReal computeEllipsoidMass(const PxVec3& extents, PxReal density) { return density * computeEllipsoidRatio(extents); } + PxReal computeEllipsoidDensity(const PxVec3& extents, PxReal mass) { return mass / computeEllipsoidRatio(extents); } + + // Cylinder + PX_INLINE PxReal computeCylinderRatio(PxReal r, PxReal l) { return PxPi * r * r * (2.0f*l); } + PxReal computeCylinderMass(PxReal r, PxReal l, PxReal density) { return density * computeCylinderRatio(r, l); } + PxReal computeCylinderDensity(PxReal r, PxReal l, PxReal mass) { return mass / computeCylinderRatio(r, l); } + + // Capsule + PX_INLINE PxReal computeCapsuleRatio(PxReal r, PxReal l) { return computeSphereRatio(r) + computeCylinderRatio(r, l);} + PxReal computeCapsuleMass(PxReal r, PxReal l, PxReal density) { return density * computeCapsuleRatio(r, l); } + PxReal computeCapsuleDensity(PxReal r, PxReal l, PxReal mass) { return mass / computeCapsuleRatio(r, l); } + + // Cone + PX_INLINE PxReal computeConeRatio(PxReal r, PxReal l) { return PxPi * r * r * PxAbs(l)/3.0f; } + PxReal computeConeMass(PxReal r, PxReal l, PxReal density) { return density * computeConeRatio(r, l); } + PxReal computeConeDensity(PxReal r, PxReal l, PxReal mass) { return mass / computeConeRatio(r, l); } + + void computeBoxInertiaTensor(PxVec3& inertia, PxReal mass, PxReal xlength, PxReal ylength, PxReal zlength); + void computeSphereInertiaTensor(PxVec3& inertia, PxReal mass, PxReal radius, bool hollow); + bool jacobiTransform(PxI32 n, PxF64 a[], PxF64 w[]); + bool diagonalizeInertiaTensor(const PxMat33& denseInertia, PxVec3& diagonalInertia, PxMat33& rotation); + +} // namespace Ext + +void Ext::computeBoxInertiaTensor(PxVec3& inertia, PxReal mass, PxReal xlength, PxReal ylength, PxReal zlength) +{ + //to model a hollow block, one would have to multiply coeff by up to two. + const PxReal coeff = mass/12; + inertia.x = coeff * (ylength*ylength + zlength*zlength); + inertia.y = coeff * (xlength*xlength + zlength*zlength); + inertia.z = coeff * (xlength*xlength + ylength*ylength); + + PX_ASSERT(inertia.x != 0.0f); + PX_ASSERT(inertia.y != 0.0f); + PX_ASSERT(inertia.z != 0.0f); + PX_ASSERT(inertia.isFinite()); +} + + +void Ext::computeSphereInertiaTensor(PxVec3& inertia, PxReal mass, PxReal radius, bool hollow) +{ + inertia.x = mass * radius * radius; + if (hollow) + inertia.x *= PxReal(2 / 3.0); + else + inertia.x *= PxReal(2 / 5.0); + + inertia.z = inertia.y = inertia.x; + PX_ASSERT(inertia.isFinite()); +} + +//-------------------------------------------------------------- +// +// InertiaTensorComputer implementation +// +//-------------------------------------------------------------- + +Ext::InertiaTensorComputer::InertiaTensorComputer(bool initTozero) +{ + if (initTozero) + zero(); +} + + +Ext::InertiaTensorComputer::InertiaTensorComputer(const PxMat33& inertia, const PxVec3& com, PxReal mass) : + mI(inertia), + mG(com), + mMass(mass) +{ +} + + +Ext::InertiaTensorComputer::~InertiaTensorComputer() +{ + //nothing +} + + +PX_INLINE void Ext::InertiaTensorComputer::zero() +{ + mMass = 0.0f; + mI = PxMat33(PxZero); + mG = PxVec3(0); +} + + +PX_INLINE void Ext::InertiaTensorComputer::setDiagonal(PxReal mass, const PxVec3& diag) +{ + mMass = mass; + mI = PxMat33::createDiagonal(diag); + mG = PxVec3(0); + PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite()); + PX_ASSERT(PxIsFinite(mMass)); +} + + +void Ext::InertiaTensorComputer::setBox(const PxVec3& halfWidths) +{ + // Setup inertia tensor for a cube with unit density + const PxReal mass = 8.0f * computeBoxRatio(halfWidths); + const PxReal s =(1.0f/3.0f) * mass; + + const PxReal x = halfWidths.x*halfWidths.x; + const PxReal y = halfWidths.y*halfWidths.y; + const PxReal z = halfWidths.z*halfWidths.z; + + setDiagonal(mass, PxVec3(y+z, z+x, x+y) * s); +} + + +PX_INLINE void Ext::InertiaTensorComputer::rotate(const PxMat33& rot) +{ + //well known inertia tensor rotation expression is: RIR' -- this could be optimized due to symmetry, see code to do that in Body::updateGlobalInverseInertia + mI = rot * mI * rot.getTranspose(); + PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite()); + //com also needs to be rotated + mG = rot * mG; + PX_ASSERT(mG.isFinite()); +} + + +void Ext::InertiaTensorComputer::translate(const PxVec3& t) +{ + if (!t.isZero()) //its common for this to be zero + { + PxMat33 t1, t2; + + t1.column0 = PxVec3(0, mG.z, -mG.y); + t1.column1 = PxVec3(-mG.z, 0, mG.x); + t1.column2 = PxVec3(mG.y, -mG.x, 0); + + PxVec3 sum = mG + t; + if (sum.isZero()) + { + mI += (t1 * t1)*mMass; + } + else + { + t2.column0 = PxVec3(0, sum.z, -sum.y); + t2.column1 = PxVec3(-sum.z, 0, sum.x); + t2.column2 = PxVec3(sum.y, -sum.x, 0); + mI += (t1 * t1 - t2 * t2)*mMass; + } + + //move center of mass + mG += t; + + PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite()); + PX_ASSERT(mG.isFinite()); + } +} + + +PX_INLINE void Ext::InertiaTensorComputer::transform(const PxTransform& transform) +{ + rotate(PxMat33(transform.q)); + translate(transform.p); +} + + +PX_INLINE void Ext::InertiaTensorComputer::setBox(const PxVec3& halfWidths, const PxTransform* pose) +{ + setBox(halfWidths); + if (pose) + transform(*pose); + +} + + +PX_INLINE void Ext::InertiaTensorComputer::scaleDensity(PxReal densityScale) +{ + mI *= densityScale; + mMass *= densityScale; + PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite()); + PX_ASSERT(PxIsFinite(mMass)); +} + + +PX_INLINE void Ext::InertiaTensorComputer::add(const InertiaTensorComputer& it) +{ + const PxReal TotalMass = mMass + it.mMass; + mG = (mG * mMass + it.mG * it.mMass) / TotalMass; + + mMass = TotalMass; + mI += it.mI; + PX_ASSERT(mI.column0.isFinite() && mI.column1.isFinite() && mI.column2.isFinite()); + PX_ASSERT(mG.isFinite()); + PX_ASSERT(PxIsFinite(mMass)); +} + + +PX_INLINE void Ext::InertiaTensorComputer::center() +{ + PxVec3 center = -mG; + translate(center); +} + + +void Ext::InertiaTensorComputer::setSphere(PxReal radius) +{ + // Compute mass of the sphere + const PxReal m = computeSphereRatio(radius); + // Compute moment of inertia + const PxReal s = m * radius * radius * (2.0f/5.0f); + setDiagonal(m,PxVec3(s,s,s)); +} + + +PX_INLINE void Ext::InertiaTensorComputer::setSphere(PxReal radius, const PxTransform* pose) +{ + setSphere(radius); + if (pose) + transform(*pose); +} + + +void Ext::InertiaTensorComputer::setCylinder(int dir, PxReal r, PxReal l) +{ + // Compute mass of cylinder + const PxReal m = computeCylinderRatio(r, l); + + const PxReal i1 = r*r*m/2.0f; + const PxReal i2 = (3.0f*r*r+4.0f*l*l)*m/12.0f; + + switch(dir) + { + case 0: setDiagonal(m,PxVec3(i1,i2,i2)); break; + case 1: setDiagonal(m,PxVec3(i2,i1,i2)); break; + default: setDiagonal(m,PxVec3(i2,i2,i1)); break; + } +} + + +PX_INLINE void Ext::InertiaTensorComputer::setCylinder(int dir, PxReal r, PxReal l, const PxTransform* pose) +{ + setCylinder(dir, r, l); + if (pose) + transform(*pose); +} + + +void Ext::InertiaTensorComputer::setCapsule(int dir, PxReal r, PxReal l) +{ + // Compute mass of capsule + const PxReal m = computeCapsuleRatio(r, l); + + const PxReal t = PxPi * r * r; + const PxReal i1 = t * ((r*r*r * 8.0f/15.0f) + (l*r*r)); + const PxReal i2 = t * ((r*r*r * 8.0f/15.0f) + (l*r*r * 3.0f/2.0f) + (l*l*r * 4.0f/3.0f) + (l*l*l * 2.0f/3.0f)); + + switch(dir) + { + case 0: setDiagonal(m,PxVec3(i1,i2,i2)); break; + case 1: setDiagonal(m,PxVec3(i2,i1,i2)); break; + default: setDiagonal(m,PxVec3(i2,i2,i1)); break; + } +} + + +PX_INLINE void Ext::InertiaTensorComputer::setCapsule(int dir, PxReal r, PxReal l, const PxTransform* pose) +{ + setCapsule(dir, r, l); + if (pose) + transform(*pose); +} + + +void Ext::InertiaTensorComputer::setEllipsoid(PxReal rx, PxReal ry, PxReal rz) +{ + // Compute mass of ellipsoid + const PxReal m = computeEllipsoidRatio(PxVec3(rx, ry, rz)); + + // Compute moment of inertia + const PxReal s = m * (2.0f/5.0f); + + // Setup inertia tensor for an ellipsoid centered at the origin + setDiagonal(m,PxVec3(ry*rz,rz*rx,rx*ry)*s); +} + + +PX_INLINE void Ext::InertiaTensorComputer::setEllipsoid(PxReal rx, PxReal ry, PxReal rz, const PxTransform* pose) +{ + setEllipsoid(rx,ry,rz); + if (pose) + transform(*pose); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp new file mode 100644 index 000000000..32ee392b4 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp @@ -0,0 +1,134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "foundation/PxMat33.h" +#include "PxConstraint.h" +#include "PxJoint.h" +#include "ExtJoint.h" + +using namespace physx; +using namespace Ext; + +PxConstraint* physx::resolveConstraintPtr(PxDeserializationContext& v, + PxConstraint* old, + PxConstraintConnector* connector, + PxConstraintShaderTable &shaders) +{ + v.translatePxBase(old); + PxConstraint* new_nx = static_cast(old); + new_nx->setConstraintFunctions(*connector, shaders); + return new_nx; +} +//~PX_SERIALIZATION + + +static void normalToTangents(const PxVec3& n, PxVec3& t1, PxVec3& t2) +{ + const PxReal m_sqrt1_2 = PxReal(0.7071067811865475244008443621048490); + if(fabsf(n.z) > m_sqrt1_2) + { + const PxReal a = n.y*n.y + n.z*n.z; + const PxReal k = PxReal(1.0)/PxSqrt(a); + t1 = PxVec3(0,-n.z*k,n.y*k); + t2 = PxVec3(a*k,-n.x*t1.z,n.x*t1.y); + } + else + { + const PxReal a = n.x*n.x + n.y*n.y; + const PxReal k = PxReal(1.0)/PxSqrt(a); + t1 = PxVec3(-n.y*k,n.x*k,0); + t2 = PxVec3(-n.z*t1.y,n.z*t1.x,a*k); + } + t1.normalize(); + t2.normalize(); +} + +void PxSetJointGlobalFrame(PxJoint& joint, const PxVec3* wsAnchor, const PxVec3* axisIn) +{ + PxRigidActor* actors[2]; + joint.getActors(actors[0], actors[1]); + + PxTransform localPose[2]; + for(PxU32 i=0; i<2; i++) + localPose[i] = PxTransform(PxIdentity); + + // 1) global anchor + if(wsAnchor) + { + //transform anchorPoint to local space + for(PxU32 i=0; i<2; i++) + localPose[i].p = actors[i] ? actors[i]->getGlobalPose().transformInv(*wsAnchor) : *wsAnchor; + } + + // 2) global axis + if(axisIn) + { + PxVec3 localAxis[2], localNormal[2]; + + //find 2 orthogonal vectors. + //gotta do this in world space, if we choose them + //separately in local space they won't match up in worldspace. + PxVec3 axisw = *axisIn; + axisw.normalize(); + + PxVec3 normalw, binormalw; + ::normalToTangents(axisw, binormalw, normalw); + //because axis is supposed to be the Z axis of a frame with the other two being X and Y, we need to negate + //Y to make the frame right handed. Note that the above call makes a right handed frame if we pass X --> Y,Z, so + //it need not be changed. + + for(PxU32 i=0; i<2; i++) + { + if(actors[i]) + { + const PxTransform& m = actors[i]->getGlobalPose(); + PxMat33 mM(m.q); + localAxis[i] = mM.transformTranspose(axisw); + localNormal[i] = mM.transformTranspose(normalw); + } + else + { + localAxis[i] = axisw; + localNormal[i] = normalw; + } + + PxMat33 rot(localAxis[i], localNormal[i], localAxis[i].cross(localNormal[i])); + + localPose[i].q = PxQuat(rot); + localPose[i].q.normalize(); + } + } + + for(PxU32 i=0; i<2; i++) + joint.setLocalPose(static_cast( i ), localPose[i]); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtJoint.h new file mode 100644 index 000000000..0bce87e3b --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtJoint.h @@ -0,0 +1,640 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_JOINTCONSTRAINT_H +#define NP_JOINTCONSTRAINT_H + +#include "PsAllocator.h" +#include "PsUtilities.h" +#include "PsMathUtils.h" +#include "PxConstraint.h" +#include "PxConstraintExt.h" +#include "PxJoint.h" +#include "PxD6Joint.h" +#include "PxRigidDynamic.h" +#include "PxRigidStatic.h" +#include "PxScene.h" +#include "ExtPvd.h" +#include "PxMetaData.h" +#include "CmRenderOutput.h" +#include "PxPhysics.h" +#include "PsFoundation.h" + +#if PX_SUPPORT_PVD +#include "PxScene.h" +#include "PxPvdClient.h" +#include "PxPvdSceneClient.h" +#endif + +// PX_SERIALIZATION + +namespace physx +{ + +PxConstraint* resolveConstraintPtr(PxDeserializationContext& v, PxConstraint* old, PxConstraintConnector* connector, PxConstraintShaderTable& shaders); + +// ~PX_SERIALIZATION + +namespace Ext +{ + PX_FORCE_INLINE float computeSwingAngle(float swingYZ, float swingW) + { + return 4.0f * PxAtan2(swingYZ, 1.0f + swingW); // tan (t/2) = sin(t)/(1+cos t), so this is the quarter angle + } + + struct JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + PxConstraintInvMassScale invMassScale; //16 + PxTransform c2b[2]; //72 + PxU32 pading[2]; //80 + protected: + ~JointData(){} + }; + + template + class Joint : public Base, + public PxConstraintConnector, + public Ps::UserAllocated + { + + public: +// PX_SERIALIZATION + Joint(PxBaseFlags baseFlags) : Base(baseFlags) {} + virtual void requiresObjects(PxProcessPxBaseCallback& c) + { + c.process(*mPxConstraint); + + { + PxRigidActor* a0 = NULL; + PxRigidActor* a1 = NULL; + mPxConstraint->getActors(a0,a1); + + if (a0) + { + c.process(*a0); + } + if (a1) + { + c.process(*a1); + } + } + } +//~PX_SERIALIZATION + +#if PX_SUPPORT_PVD + // PxConstraintConnector + virtual bool updatePvdProperties(physx::pvdsdk::PvdDataStream& pvdConnection, const PxConstraint* c, PxPvdUpdateType::Enum updateType) const + { + if(updateType == PxPvdUpdateType::UPDATE_SIM_PROPERTIES) + { + Ext::Pvd::simUpdate(pvdConnection, *this); + return true; + } + else if(updateType == PxPvdUpdateType::UPDATE_ALL_PROPERTIES) + { + Ext::Pvd::updatePvdProperties(pvdConnection, *this); + return true; + } + else if(updateType == PxPvdUpdateType::CREATE_INSTANCE) + { + Ext::Pvd::createPvdInstance(pvdConnection, *c, *this); + return true; + } + else if(updateType == PxPvdUpdateType::RELEASE_INSTANCE) + { + Ext::Pvd::releasePvdInstance(pvdConnection, *c, *this); + return true; + } + return false; + } +#else + virtual bool updatePvdProperties(physx::pvdsdk::PvdDataStream&, const PxConstraint*, PxPvdUpdateType::Enum) const + { + return false; + } +#endif + + // PxJoint + virtual void setActors(PxRigidActor* actor0, PxRigidActor* actor1) + { + //TODO SDK-DEV + //You can get the debugger stream from the NpScene + //Ext::Pvd::setActors( stream, this, mPxConstraint, actor0, actor1 ); + PX_CHECK_AND_RETURN(actor0 != actor1, "PxJoint::setActors: actors must be different"); + PX_CHECK_AND_RETURN((actor0 && !actor0->is()) || (actor1 && !actor1->is()), "PxJoint::setActors: at least one actor must be non-static"); + +#if PX_SUPPORT_PVD + PxScene* scene = getScene(); + if(scene) + { + //if pvd not connect data stream is NULL + physx::pvdsdk::PvdDataStream* conn = scene->getScenePvdClient()->getClientInternal()->getDataStream(); + if( conn != NULL ) + Ext::Pvd::setActors( + *conn, + *this, + *mPxConstraint, + actor0, + actor1 + ); + } +#endif + mPxConstraint->setActors(actor0, actor1); + mData->c2b[0] = getCom(actor0).transformInv(mLocalPose[0]); + mData->c2b[1] = getCom(actor1).transformInv(mLocalPose[1]); + mPxConstraint->markDirty(); + } + + // PxJoint + virtual void getActors(PxRigidActor*& actor0, PxRigidActor*& actor1) const + { + if ( mPxConstraint ) mPxConstraint->getActors(actor0,actor1); + else + { + actor0 = NULL; + actor1 = NULL; + } + } + + // this is the local pose relative to the actor, and we store internally the local + // pose relative to the body + + // PxJoint + virtual void setLocalPose(PxJointActorIndex::Enum actor, const PxTransform& pose) + { + PX_CHECK_AND_RETURN(pose.isSane(), "PxJoint::setLocalPose: transform is invalid"); + PxTransform p = pose.getNormalized(); + mLocalPose[actor] = p; + mData->c2b[actor] = getCom(actor).transformInv(p); + mPxConstraint->markDirty(); + } + + // PxJoint + virtual PxTransform getLocalPose(PxJointActorIndex::Enum actor) const + { + return mLocalPose[actor]; + } + + static PxTransform getGlobalPose(const PxRigidActor* actor) + { + if(!actor) + return PxTransform(PxIdentity); + return actor->getGlobalPose(); + } + + static void getActorVelocity(const PxRigidActor* actor, PxVec3& linear, PxVec3& angular) + { + if(!actor || actor->is()) + { + linear = angular = PxVec3(0.0f); + return; + } + + linear = static_cast(actor)->getLinearVelocity(); + angular = static_cast(actor)->getAngularVelocity(); + } + + // PxJoint + virtual PxTransform getRelativeTransform() const + { + PxRigidActor* actor0, * actor1; + mPxConstraint->getActors(actor0, actor1); + const PxTransform t0 = getGlobalPose(actor0) * mLocalPose[0]; + const PxTransform t1 = getGlobalPose(actor1) * mLocalPose[1]; + return t0.transformInv(t1); + } + + // PxJoint + virtual PxVec3 getRelativeLinearVelocity() const + { + PxRigidActor* actor0, * actor1; + PxVec3 l0, a0, l1, a1; + mPxConstraint->getActors(actor0, actor1); + + PxTransform t0 = getCom(actor0), t1 = getCom(actor1); + getActorVelocity(actor0, l0, a0); + getActorVelocity(actor1, l1, a1); + + PxVec3 p0 = t0.q.rotate(mLocalPose[0].p), + p1 = t1.q.rotate(mLocalPose[1].p); + return t0.transformInv(l1 - a1.cross(p1) - l0 + a0.cross(p0)); + } + + // PxJoint + virtual PxVec3 getRelativeAngularVelocity() const + { + PxRigidActor* actor0, * actor1; + PxVec3 l0, a0, l1, a1; + mPxConstraint->getActors(actor0, actor1); + + PxTransform t0 = getCom(actor0); + getActorVelocity(actor0, l0, a0); + getActorVelocity(actor1, l1, a1); + + return t0.transformInv(a1 - a0); + } + + // PxJoint + virtual void setBreakForce(PxReal force, PxReal torque) + { + PX_CHECK_AND_RETURN(PxIsFinite(force) && PxIsFinite(torque), "NpJoint::setBreakForce: invalid float"); + mPxConstraint->setBreakForce(force,torque); + } + + // PxJoint + virtual void getBreakForce(PxReal& force, PxReal& torque) const + { + mPxConstraint->getBreakForce(force,torque); + } + + // PxJoint + virtual void setConstraintFlags(PxConstraintFlags flags) + { + mPxConstraint->setFlags(flags); + } + + // PxJoint + virtual void setConstraintFlag(PxConstraintFlag::Enum flag, bool value) + { + mPxConstraint->setFlag(flag, value); + } + + // PxJoint + virtual PxConstraintFlags getConstraintFlags() const + { + return mPxConstraint->getFlags(); + } + + // PxJoint + virtual void setInvMassScale0(PxReal invMassScale) + { + PX_CHECK_AND_RETURN(PxIsFinite(invMassScale) && invMassScale>=0, "PxJoint::setInvMassScale0: scale must be non-negative"); + mData->invMassScale.linear0 = invMassScale; + mPxConstraint->markDirty(); + } + + // PxJoint + virtual PxReal getInvMassScale0() const + { + return mData->invMassScale.linear0; + } + + // PxJoint + virtual void setInvInertiaScale0(PxReal invInertiaScale) + { + PX_CHECK_AND_RETURN(PxIsFinite(invInertiaScale) && invInertiaScale>=0, "PxJoint::setInvInertiaScale0: scale must be non-negative"); + mData->invMassScale.angular0 = invInertiaScale; + mPxConstraint->markDirty(); + } + + // PxJoint + virtual PxReal getInvInertiaScale0() const + { + return mData->invMassScale.angular0; + } + + // PxJoint + virtual void setInvMassScale1(PxReal invMassScale) + { + PX_CHECK_AND_RETURN(PxIsFinite(invMassScale) && invMassScale>=0, "PxJoint::setInvMassScale1: scale must be non-negative"); + mData->invMassScale.linear1 = invMassScale; + mPxConstraint->markDirty(); + } + + // PxJoint + virtual PxReal getInvMassScale1() const + { + return mData->invMassScale.linear1; + } + + // PxJoint + virtual void setInvInertiaScale1(PxReal invInertiaScale) + { + PX_CHECK_AND_RETURN(PxIsFinite(invInertiaScale) && invInertiaScale>=0, "PxJoint::setInvInertiaScale: scale must be non-negative"); + mData->invMassScale.angular1 = invInertiaScale; + mPxConstraint->markDirty(); + } + + // PxJoint + virtual PxReal getInvInertiaScale1() const + { + return mData->invMassScale.angular1; + } + + // PxJoint + virtual PxConstraint* getConstraint() const + { + return mPxConstraint; + } + + // PxJoint + virtual void setName(const char* name) + { + mName = name; + } + + // PxJoint + virtual const char* getName() const + { + return mName; + } + + // PxJoint + virtual void release() + { + mPxConstraint->release(); + } + + // PxJoint + virtual PxScene* getScene() const + { + return mPxConstraint ? mPxConstraint->getScene() : NULL; + } + + // PxConstraintConnector + virtual void onComShift(PxU32 actor) + { + mData->c2b[actor] = getCom(actor).transformInv(mLocalPose[actor]); + markDirty(); + } + + // PxConstraintConnector + virtual void onOriginShift(const PxVec3& shift) + { + PxRigidActor* a[2]; + mPxConstraint->getActors(a[0], a[1]); + + if (!a[0]) + { + mLocalPose[0].p -= shift; + mData->c2b[0].p -= shift; + markDirty(); + } + else if (!a[1]) + { + mLocalPose[1].p -= shift; + mData->c2b[1].p -= shift; + markDirty(); + } + } + + // PxConstraintConnector + virtual void* prepareData() + { + return mData; + } + + // PxConstraintConnector + virtual void* getExternalReference(PxU32& typeID) + { + typeID = PxConstraintExtIDs::eJOINT; + return static_cast( this ); + } + + // PxConstraintConnector + virtual PxBase* getSerializable() + { + return this; + } + + // PxConstraintConnector + virtual void onConstraintRelease() + { + PX_FREE_AND_RESET(mData); + delete this; + } + + // PxConstraintConnector + virtual const void* getConstantBlock() const + { + return mData; + } + + private: + PxTransform getCom(PxU32 index) const + { + PxRigidActor* a[2]; + mPxConstraint->getActors(a[0],a[1]); + return getCom(a[index]); + } + + PxTransform getCom(PxRigidActor* actor) const + { + if (!actor) + return PxTransform(PxIdentity); + else if (actor->getType() == PxActorType::eRIGID_DYNAMIC || actor->getType() == PxActorType::eARTICULATION_LINK) + return static_cast(actor)->getCMassLocalPose(); + else + { + PX_ASSERT(actor->getType() == PxActorType::eRIGID_STATIC); + return static_cast(actor)->getGlobalPose().getInverse(); + } + } + + protected: + + Joint(PxType concreteType, PxBaseFlags baseFlags, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1, PxU32 size, const char* name) : + Base (concreteType, baseFlags), + mName (NULL), + mPxConstraint (NULL) + { + PX_UNUSED(name); + Base::userData = NULL; + + JointData* data = reinterpret_cast(PX_ALLOC(size, name)); + Cm::markSerializedMem(data, size); + + mLocalPose[0] = localFrame0.getNormalized(); + mLocalPose[1] = localFrame1.getNormalized(); + data->c2b[0] = getCom(actor0).transformInv(localFrame0); + data->c2b[1] = getCom(actor1).transformInv(localFrame1); + data->invMassScale.linear0 = 1.0f; + data->invMassScale.angular0 = 1.0f; + data->invMassScale.linear1 = 1.0f; + data->invMassScale.angular1 = 1.0f; + + mData = data; + } + + virtual ~Joint() + { + if(Base::getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + PX_FREE_AND_RESET(mData); + } + + PX_FORCE_INLINE void markDirty() + { + mPxConstraint->markDirty(); + } + + PX_FORCE_INLINE PxConstraintConnector* getConnector() + { + return this; + } + + PX_FORCE_INLINE PxConstraint* getPxConstraint() + { + return mPxConstraint; + } + + PX_FORCE_INLINE void setPxConstraint(PxConstraint* pxConstraint) + { + mPxConstraint = pxConstraint; + } + + void wakeUpActors() + { + PxRigidActor* a[2]; + mPxConstraint->getActors(a[0], a[1]); + for(PxU32 i = 0; i < 2; i++) + { + if(a[i] && a[i]->getScene() && a[i]->getType() == PxActorType::eRIGID_DYNAMIC) + { + PxRigidDynamic* rd = static_cast(a[i]); + if(!(rd->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC)) + { + const PxScene* scene = rd->getScene(); + const PxReal wakeCounterResetValue = scene->getWakeCounterResetValue(); + + PxReal wakeCounter = rd->getWakeCounter(); + + bool needsWakingUp = rd->isSleeping(); + if (wakeCounter < wakeCounterResetValue) + { + wakeCounter = wakeCounterResetValue; + needsWakingUp = true; + } + + if (needsWakingUp) + { + rd->wakeUp(); + rd->setWakeCounter(wakeCounter); + } + } + } + } + } + + PX_FORCE_INLINE PxQuat getTwistOrSwing(bool needTwist) const + { + const PxQuat q = getRelativeTransform().q; + // PT: TODO: we don't need to compute both quats here + PxQuat swing, twist; + Ps::separateSwingTwist(q, swing, twist); + return needTwist ? twist : swing; + } + + PxReal getTwistAngle_Internal() const + { + const PxQuat twist = getTwistOrSwing(true); + + // PT: the angle-axis formulation creates the quat like this: + // + // const float a = angleRadians * 0.5f; + // const float s = PxSin(a); + // w = PxCos(a); + // x = unitAxis.x * s; + // y = unitAxis.y * s; + // z = unitAxis.z * s; + // + // With the twist axis = (1;0;0) this gives: + // + // w = PxCos(angleRadians * 0.5f); + // x = PxSin(angleRadians * 0.5f); + // y = 0.0f; + // z = 0.0f; + // + // Thus the quat's "getAngle" function returns: + // + // angle = PxAcos(w) * 2.0f; + // + // PxAcos will return an angle between 0 and PI in radians, so "getAngle" will return an angle between 0 and PI*2. + + PxReal angle = twist.getAngle(); + + if(twist.x<0.0f) + angle = -angle; + + return angle; + } + + PxReal getSwingYAngle_Internal() const + { + PxQuat swing = getTwistOrSwing(false); + + if(swing.w < 0.0f) // choose the shortest rotation + swing = -swing; + + const PxReal angle = computeSwingAngle(swing.y, swing.w); + PX_ASSERT(angle>-PxPi && angle<=PxPi); // since |y| < w+1, the atan magnitude is < PI/4 + return angle; + } + + PxReal getSwingZAngle_Internal() const + { + PxQuat swing = getTwistOrSwing(false); + + if(swing.w < 0.0f) // choose the shortest rotation + swing = -swing; + + const PxReal angle = computeSwingAngle(swing.z, swing.w); + PX_ASSERT(angle>-PxPi && angle <= PxPi); // since |y| < w+1, the atan magnitude is < PI/4 + return angle; + } + + const char* mName; + PxTransform mLocalPose[2]; + PxConstraint* mPxConstraint; + JointData* mData; + }; + + PX_FORCE_INLINE bool isLimitActive(const PxJointLimitParameters& limit, PxReal pad, PxReal angle, PxReal low, PxReal high) + { + PX_ASSERT(low high - pad) + active = true; + return active; + } + +} // namespace Ext + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h b/src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h new file mode 100644 index 000000000..8c35c211d --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h @@ -0,0 +1,65 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef EXT_JOINT_META_DATA_EXTENSIONS_H +#define EXT_JOINT_META_DATA_EXTENSIONS_H +#include "PvdMetaDataExtensions.h" + +namespace physx +{ + +namespace pvdsdk +{ + + +struct PxExtensionPvdOnlyProperties +{ + enum Enum + { + FirstProp = PxExtensionsPropertyInfoName::LastPxPropertyInfoName, + DEFINE_ENUM_RANGE( PxJoint_Actors, 2 ), + DEFINE_ENUM_RANGE( PxJoint_BreakForce, 2 ), + DEFINE_ENUM_RANGE( PxD6Joint_DriveVelocity, 2 ), + DEFINE_ENUM_RANGE( PxD6Joint_Motion, PxD6Axis::eCOUNT ), + DEFINE_ENUM_RANGE( PxD6Joint_Drive, PxD6Drive::eCOUNT * ( PxExtensionsPropertyInfoName::PxD6JointDrive_PropertiesStop - PxExtensionsPropertyInfoName::PxD6JointDrive_PropertiesStart ) ), + DEFINE_ENUM_RANGE( PxD6Joint_LinearLimit, 100 ), + DEFINE_ENUM_RANGE( PxD6Joint_SwingLimit, 100 ), + DEFINE_ENUM_RANGE( PxD6Joint_TwistLimit, 100 ), + DEFINE_ENUM_RANGE( PxPrismaticJoint_Limit, 100 ), + DEFINE_ENUM_RANGE( PxRevoluteJoint_Limit, 100 ), + DEFINE_ENUM_RANGE( PxSphericalJoint_LimitCone, 100 ), + DEFINE_ENUM_RANGE( PxJoint_LocalPose, 2 ) + }; +}; + +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp b/src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp new file mode 100644 index 000000000..411ede6b3 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp @@ -0,0 +1,445 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "PxJoint.h" +#include "ExtJoint.h" +#include "ExtD6Joint.h" +#include "ExtFixedJoint.h" +#include "ExtSphericalJoint.h" +#include "ExtDistanceJoint.h" +#include "ExtContactJoint.h" +#include "ExtSphericalJoint.h" +#include "ExtRevoluteJoint.h" +#include "ExtPrismaticJoint.h" +#include "serialization/SnSerializationRegistry.h" +#include "serialization/Binary/SnSerializationContext.h" + +using namespace physx; +using namespace Cm; +using namespace Ext; + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_JointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, JointData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, JointData, PxTransform, c2b, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, JointData, PxU32, paddingFromFlags, PxMetaDataFlag::ePADDING) +#endif + PX_DEF_BIN_METADATA_ITEM(stream, JointData, PxConstraintInvMassScale, invMassScale, 0) +} + +static void getBinaryMetaData_PxD6JointDrive(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxD6JointDriveFlags, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, PxD6JointDrive) + PX_DEF_BIN_METADATA_ITEM(stream, PxD6JointDrive, PxReal, stiffness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxD6JointDrive, PxReal, damping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxD6JointDrive, PxReal, forceLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxD6JointDrive, PxD6JointDriveFlags, flags, 0) +} + +static void getBinaryMetaData_PxJointLimitParameters(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitParameters, PxReal, restitution, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitParameters, PxReal, bounceThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitParameters, PxReal, stiffness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitParameters, PxReal, damping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitParameters, PxReal, contactDistance, 0) +} + +static void getBinaryMetaData_PxJointLinearLimit(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointLinearLimit) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJointLinearLimit, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLinearLimit, PxReal, value, 0) +} + +static void getBinaryMetaData_PxJointLinearLimitPair(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointLinearLimitPair) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJointLinearLimitPair, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLinearLimitPair, PxReal, upper, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLinearLimitPair, PxReal, lower, 0) +} + +static void getBinaryMetaData_PxJointAngularLimitPair(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointAngularLimitPair) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJointAngularLimitPair, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointAngularLimitPair, PxReal, upper, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointAngularLimitPair, PxReal, lower, 0) +} + +static void getBinaryMetaData_PxJointLimitCone(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointLimitCone) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJointLimitCone, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitCone, PxReal, yAngle, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitCone, PxReal, zAngle, 0) +} + +static void getBinaryMetaData_PxJointLimitPyramid(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxJointLimitPyramid) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJointLimitPyramid, PxJointLimitParameters) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitPyramid, PxReal, yAngleMin, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitPyramid, PxReal, yAngleMax, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitPyramid, PxReal, zAngleMin, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxJointLimitPyramid, PxReal, zAngleMax, 0) +} + +void PxJoint::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_VCLASS(stream, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxJoint, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, PxJoint, void, userData, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_RevoluteJointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxRevoluteJointFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, RevoluteJointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, RevoluteJointData, JointData) + + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxReal, driveVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxReal, driveForceLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxReal, driveGearRatio, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxJointAngularLimitPair,limit, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxReal, projectionLinearTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxReal, projectionAngularTolerance, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxRevoluteJointFlags, jointFlags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJointData, PxU16, paddingFromFlags, PxMetaDataFlag::ePADDING) +#endif +} + +void RevoluteJoint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_RevoluteJointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, RevoluteJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, RevoluteJoint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, RevoluteJoint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJoint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, RevoluteJoint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJoint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, RevoluteJoint, JointData, mData, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, RevoluteJoint, RevoluteJointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, RevoluteJoint, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_SphericalJointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxSphericalJointFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, SphericalJointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, SphericalJointData, JointData) + + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJointData, PxJointLimitCone, limit, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJointData, PxReal, projectionLinearTolerance, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJointData, PxSphericalJointFlags, jointFlags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJointData, PxU16, paddingFromFlags, PxMetaDataFlag::ePADDING) +#endif +} + +void SphericalJoint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_SphericalJointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, SphericalJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, SphericalJoint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, SphericalJoint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJoint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, SphericalJoint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJoint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, SphericalJoint, JointData, mData, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, SphericalJoint, SphericalJointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, SphericalJoint, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_DistanceJointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxDistanceJointFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, DistanceJointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, DistanceJointData, JointData) + + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxReal, minDistance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxReal, maxDistance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxReal, tolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxReal, stiffness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxReal, damping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxDistanceJointFlags, jointFlags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJointData, PxU16, paddingFromFlags, PxMetaDataFlag::ePADDING) +#endif +} + +void DistanceJoint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_DistanceJointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, DistanceJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, DistanceJoint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, DistanceJoint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJoint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, DistanceJoint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJoint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, DistanceJoint, JointData, mData, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, DistanceJoint, DistanceJointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, DistanceJoint, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_D6JointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxD6Motion::Enum, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, D6JointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, D6JointData, JointData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, D6JointData, PxD6Motion::Enum, motion, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLinearLimit, distanceLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLinearLimitPair, linearLimitX, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLinearLimitPair, linearLimitY, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLinearLimitPair, linearLimitZ, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointAngularLimitPair, twistLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLimitCone, swingLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxJointLimitPyramid, pyramidSwingLimit, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, D6JointData, PxD6JointDrive, drive, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxTransform, drivePosition, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxVec3, driveLinearVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxVec3, driveAngularVelocity, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxU32, locked, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxU32, limited, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxU32, driving, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxReal, distanceMinDist, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxReal, projectionLinearTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, PxReal, projectionAngularTolerance, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, bool, mUseDistanceLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, bool, mUseNewLinearLimits, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, bool, mUseConeLimit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6JointData, bool, mUsePyramidLimits, 0) +} + +void D6Joint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_D6JointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, D6Joint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, D6Joint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, D6Joint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, D6Joint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, D6Joint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, D6Joint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, D6Joint, JointData, mData, PxMetaDataFlag::ePTR) + + PX_DEF_BIN_METADATA_ITEM(stream, D6Joint, bool, mRecomputeMotion, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, D6Joint, bool, mPadding, PxMetaDataFlag::ePADDING) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, D6Joint, D6JointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, D6Joint, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PrismaticJointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxPrismaticJointFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, PrismaticJointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PrismaticJointData, JointData) + + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJointData, PxJointLinearLimitPair, limit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJointData, PxReal, projectionLinearTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJointData, PxReal, projectionAngularTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJointData, PxPrismaticJointFlags, jointFlags, 0) +#ifdef EXPLICIT_PADDING_METADATA + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJointData, PxU16, paddingFromFlags, PxMetaDataFlag::ePADDING) +#endif +} + +void PrismaticJoint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PrismaticJointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, PrismaticJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PrismaticJoint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PrismaticJoint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJoint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PrismaticJoint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJoint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PrismaticJoint, JointData, mData, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, PrismaticJoint, PrismaticJointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, PrismaticJoint, mName, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_FixedJointData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, FixedJointData) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, FixedJointData, JointData) + + PX_DEF_BIN_METADATA_ITEM(stream, FixedJointData, PxReal, projectionLinearTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, FixedJointData, PxReal, projectionAngularTolerance, 0) +} + +void FixedJoint::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_FixedJointData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, FixedJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, FixedJoint, PxJoint) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, FixedJoint, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, FixedJoint, char, mName, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, FixedJoint, PxTransform, mLocalPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, FixedJoint, PxConstraint, mPxConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, FixedJoint, JointData, mData, PxMetaDataFlag::ePTR) + + //------ Extra-data ------ + + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, FixedJoint, FixedJointData, mData, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_NAME(stream, FixedJoint, mName, 0) +} + + +void getBinaryMetaData_SerializationContext(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxSerialObjectId, PxU64) + PX_DEF_BIN_METADATA_TYPEDEF(stream, SerialObjectIndex, PxU32) + + PX_DEF_BIN_METADATA_CLASS(stream, Sn::ManifestEntry) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ManifestEntry, PxU32, offset, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ManifestEntry, PxType, type, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, Sn::ImportReference) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ImportReference, PxSerialObjectId, id, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ImportReference, PxType, type, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, Sn::ExportReference) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ExportReference, PxSerialObjectId, id, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::ExportReference, SerialObjectIndex, objIndex, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, Sn::InternalReferencePtr) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferencePtr, void, reference, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferencePtr, PxU32, kind, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferencePtr, SerialObjectIndex, objIndex, 0) + + PX_DEF_BIN_METADATA_CLASS(stream, Sn::InternalReferenceIdx) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferenceIdx, PxU32, reference, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferenceIdx, PxU32, kind, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sn::InternalReferenceIdx, SerialObjectIndex, objIndex, 0) +} + +namespace physx +{ +namespace Ext +{ +void GetExtensionsBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_VCLASS(stream,PxConstraintConnector) + + getBinaryMetaData_JointData(stream); + getBinaryMetaData_PxD6JointDrive(stream); + getBinaryMetaData_PxJointLimitParameters(stream); + getBinaryMetaData_PxJointLimitCone(stream); + getBinaryMetaData_PxJointLimitPyramid(stream); + getBinaryMetaData_PxJointLinearLimit(stream); + getBinaryMetaData_PxJointLinearLimitPair(stream); + getBinaryMetaData_PxJointAngularLimitPair(stream); + + PxJoint::getBinaryMetaData(stream); + RevoluteJoint::getBinaryMetaData(stream); + SphericalJoint::getBinaryMetaData(stream); + DistanceJoint::getBinaryMetaData(stream); + D6Joint::getBinaryMetaData(stream); + PrismaticJoint::getBinaryMetaData(stream); + FixedJoint::getBinaryMetaData(stream); + + getBinaryMetaData_SerializationContext(stream); +} + +} +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPlatform.h b/src/PhysX/physx/source/physxextensions/src/ExtPlatform.h new file mode 100644 index 000000000..8a7701970 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPlatform.h @@ -0,0 +1,39 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PLATFORM_H +#define PLATFORM_H + +#include +#include "foundation/Px.h" +#include "PsThread.h" +#include "CmPhysXCommon.h" + +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp new file mode 100644 index 000000000..86b9eaff5 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp @@ -0,0 +1,236 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtPrismaticJoint.h" +#include "ExtConstraintHelper.h" +#include "CmRenderOutput.h" +#include "CmVisualization.h" + +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxPrismaticJoint* physx::PxPrismaticJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxPrismaticJointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxPrismaticJointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxPrismaticJointCreate: at least one actor must be dynamic"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxPrismaticJointCreate: actors must be different"); + + PrismaticJoint* j; + PX_NEW_SERIALIZED(j, PrismaticJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +void PrismaticJoint::setProjectionAngularTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0 && tolerance <= PxPi, "PxPrismaticJoint::setProjectionAngularTolerance: invalid parameter"); + data().projectionAngularTolerance = tolerance; + markDirty(); +} + +PxReal PrismaticJoint::getProjectionAngularTolerance() const +{ + return data().projectionAngularTolerance; +} + +void PrismaticJoint::setProjectionLinearTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxPrismaticJoint::setProjectionLinearTolerance: invalid parameter"); + data().projectionLinearTolerance = tolerance; + markDirty(); +} + +PxReal PrismaticJoint::getProjectionLinearTolerance() const +{ + return data().projectionLinearTolerance; +} + +PxPrismaticJointFlags PrismaticJoint::getPrismaticJointFlags(void) const +{ + return data().jointFlags; +} + +void PrismaticJoint::setPrismaticJointFlags(PxPrismaticJointFlags flags) +{ + data().jointFlags = flags; markDirty(); +} + +void PrismaticJoint::setPrismaticJointFlag(PxPrismaticJointFlag::Enum flag, bool value) +{ + if(value) + data().jointFlags |= flag; + else + data().jointFlags &= ~flag; + markDirty(); +} + +PxJointLinearLimitPair PrismaticJoint::getLimit() const +{ + return data().limit; +} + +void PrismaticJoint::setLimit(const PxJointLinearLimitPair& limit) +{ + PX_CHECK_AND_RETURN(limit.isValid(), "PxPrismaticJoint::setLimit: invalid parameter"); + data().limit = limit; + markDirty(); +} + +bool PrismaticJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(PrismaticJointData)); + return mPxConstraint!=NULL; +} + +void PrismaticJoint::exportExtraData(PxSerializationContext& stream) +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(PrismaticJointData)); + } + stream.writeName(mName); +} + +void PrismaticJoint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + context.readName(mName); +} + +void PrismaticJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +PrismaticJoint* PrismaticJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PrismaticJoint* obj = new (address) PrismaticJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PrismaticJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetPrismaticJointShaderTable() +{ + return &PrismaticJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void PrismaticJointProject(const void* constantBlock, PxTransform& bodyAToWorld, PxTransform& bodyBToWorld, bool projectToA) +{ + const PrismaticJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w, cB2cA, projected; + joint::computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA); + + const PxVec3 v(0.0f, cB2cA.p.y, cB2cA.p.z); + bool linearTrunc, angularTrunc; + projected.p = joint::truncateLinear(v, data.projectionLinearTolerance, linearTrunc); + projected.q = joint::truncateAngular(cB2cA.q, PxSin(data.projectionAngularTolerance/2), PxCos(data.projectionAngularTolerance/2), angularTrunc); + + if(linearTrunc || angularTrunc) + { + projected.p.x = cB2cA.p.x; + joint::projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA); + } +} + +static void PrismaticJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags) +{ + const PrismaticJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + viz.visualizeJointFrames(cA2w, cB2w); + + if((flags & PxConstraintVisualizationFlag::eLIMITS) && (data.jointFlags & PxPrismaticJointFlag::eLIMIT_ENABLED)) + { + const PxVec3 bOriginInA = cA2w.transformInv(cB2w.p); + const PxReal ordinate = bOriginInA.x; + + const PxReal pad = data.limit.isSoft() ? 0.0f : data.limit.contactDistance; + viz.visualizeLinearLimit(cA2w, cB2w, data.limit.lower, ordinate < data.limit.lower + pad); + viz.visualizeLinearLimit(cA2w, cB2w, data.limit.upper, ordinate > data.limit.upper - pad); + } +} + +static PxU32 PrismaticJointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool /*useExtendedLimits*/, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const PrismaticJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + if (cA2w.q.dot(cB2w.q)<0.0f) // minimum dist quat (equiv to flipping cB2bB.q, which we don't use anywhere) + cB2w.q = -cB2w.q; + + const bool limitEnabled = data.jointFlags & PxPrismaticJointFlag::eLIMIT_ENABLED; + const PxJointLinearLimitPair& limit = data.limit; + const bool limitIsLocked = limitEnabled && limit.lower >= limit.upper; + + const PxVec3 bOriginInA = cA2w.transformInv(cB2w.p); + PxVec3 ra, rb; + ch.prepareLockedAxes(cA2w.q, cB2w.q, bOriginInA, limitIsLocked ? 7ul : 6ul, 7ul, ra, rb); + cA2wOut = ra + bA2w.p; + cB2wOut = rb + bB2w.p; + + if(limitEnabled && !limitIsLocked) + { + const PxVec3 axis = cA2w.rotate(PxVec3(1.0f, 0.0f, 0.0f)); // PT: TODO: this has already been computed as part of the quat-to-matrix transform within prepareLockedAxes + const PxReal ordinate = bOriginInA.x; + + ch.linearLimit(axis, ordinate, limit.upper, limit); + ch.linearLimit(-axis, -ordinate, -limit.lower, limit); + } + + return ch.getCount(); +} + +PxConstraintShaderTable Ext::PrismaticJoint::sShaders = { PrismaticJointSolverPrep, PrismaticJointProject, PrismaticJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h new file mode 100644 index 000000000..f03fca2e6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_PRISMATICJOINTCONSTRAINT_H +#define NP_PRISMATICJOINTCONSTRAINT_H + +#include "ExtJoint.h" +#include "PxPrismaticJoint.h" +#include "PxTolerancesScale.h" +#include "CmUtils.h" + +namespace physx +{ +struct PxPrismaticJointGeneratedValues; +namespace Ext +{ + struct PrismaticJointData : public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxJointLinearLimitPair limit; + PxReal projectionLinearTolerance; + PxReal projectionAngularTolerance; + + PxPrismaticJointFlags jointFlags; + // forestall compiler complaints about not being able to generate a constructor + private: + PrismaticJointData(const PxJointLinearLimitPair &pair): + limit(pair) {} + }; + + typedef Joint PrismaticJointT; + + class PrismaticJoint : public PrismaticJointT + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + PrismaticJoint(PxBaseFlags baseFlags) : PrismaticJointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static PrismaticJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + PrismaticJoint(const PxTolerancesScale& scale, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + PrismaticJointT(PxJointConcreteType::ePRISMATIC, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(PrismaticJointData), "PrismaticJointData") + { + PrismaticJointData* data = static_cast(mData); + + data->limit = PxJointLinearLimitPair(scale); + data->projectionLinearTolerance = 1e10f; + data->projectionAngularTolerance = PxPi; + data->jointFlags = PxPrismaticJointFlags(); + } + + // PxPrismaticJoint + virtual PxReal getPosition() const { return getRelativeTransform().p.x; } + virtual PxReal getVelocity() const { return getRelativeLinearVelocity().x; } + virtual void setLimit(const PxJointLinearLimitPair& limit); + virtual PxJointLinearLimitPair getLimit() const; + virtual void setPrismaticJointFlags(PxPrismaticJointFlags flags); + virtual void setPrismaticJointFlag(PxPrismaticJointFlag::Enum flag, bool value); + virtual PxPrismaticJointFlags getPrismaticJointFlags() const; + virtual void setProjectionLinearTolerance(PxReal tolerance); + virtual PxReal getProjectionLinearTolerance() const; + virtual void setProjectionAngularTolerance(PxReal tolerance); + virtual PxReal getProjectionAngularTolerance() const; + //~PxPrismaticJoint + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep() const { return sShaders.solverPrep; } + + private: + PX_FORCE_INLINE PrismaticJointData& data() const + { + return *static_cast(mData); + } + + static PxConstraintShaderTable sShaders; + }; +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetPrismaticJointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp b/src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp new file mode 100644 index 000000000..ca1f40670 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp @@ -0,0 +1,165 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// suppress LNK4221 +#include "foundation/PxPreprocessor.h" +PX_DUMMY_SYMBOL + +#if PX_SUPPORT_PVD +#include "ExtPvd.h" +#include "PxExtensionMetaDataObjects.h" + +#include "ExtD6Joint.h" +#include "ExtFixedJoint.h" +#include "ExtSphericalJoint.h" +#include "ExtDistanceJoint.h" +#include "ExtSphericalJoint.h" +#include "ExtRevoluteJoint.h" +#include "ExtPrismaticJoint.h" +#include "ExtJointMetaDataExtensions.h" +#include "PvdMetaDataPropertyVisitor.h" +#include "PvdMetaDataDefineProperties.h" + +namespace physx +{ +namespace Ext +{ + using namespace physx::Vd; + + template + inline void visitPvdInstanceProperties( TOperator inOperator ) + { + PxClassInfoTraits().Info.visitInstanceProperties( makePvdPropertyFilter( inOperator ), 0 ); + } + + template + inline void visitPvdProperties( TOperator inOperator ) + { + PvdPropertyFilter theFilter( makePvdPropertyFilter( inOperator ) ); + PxU32 thePropCount = PxClassInfoTraits().Info.visitBaseProperties( theFilter ); + PxClassInfoTraits().Info.visitInstanceProperties( theFilter, thePropCount ); + } + + Pvd::PvdNameSpace::PvdNameSpace(physx::pvdsdk::PvdDataStream& conn, const char* /*name*/) + : mConnection(conn) + { + } + + Pvd::PvdNameSpace::~PvdNameSpace() + { + } + + void Pvd::releasePvdInstance(physx::pvdsdk::PvdDataStream& pvdConnection, const PxConstraint& c, const PxJoint& joint) + { + if(!pvdConnection.isConnected()) + return; + //remove from scene and from any attached actors. + PxRigidActor* actor0, *actor1; + c.getActors( actor0, actor1 ); + PxScene* scene = c.getScene(); + if(scene) pvdConnection.removeObjectRef( scene, "Joints", &joint ); + if ( actor0 && actor0->getScene() ) pvdConnection.removeObjectRef( actor0, "Joints", &joint ); + if ( actor1 && actor1->getScene()) pvdConnection.removeObjectRef( actor1, "Joints", &joint ); + pvdConnection.destroyInstance(&joint); + } + + template + void registerProperties( PvdDataStream& inStream ) + { + inStream.createClass(); + PvdPropertyDefinitionHelper& theHelper( inStream.getPropertyDefinitionHelper() ); + PvdClassInfoDefine theDefinitionObj( theHelper, getPvdNamespacedNameForType() ); + visitPvdInstanceProperties( theDefinitionObj ); + } + + template + void registerPropertiesAndValueStruct( PvdDataStream& inStream ) + { + inStream.createClass(); + inStream.deriveClass(); + PvdPropertyDefinitionHelper& theHelper( inStream.getPropertyDefinitionHelper() ); + { + PvdClassInfoDefine theDefinitionObj( theHelper, getPvdNamespacedNameForType() ); + visitPvdInstanceProperties( theDefinitionObj ); + } + { + PvdClassInfoValueStructDefine theDefinitionObj( theHelper ); + visitPvdProperties( theDefinitionObj ); + theHelper.addPropertyMessage(); + } + } + + void Pvd::sendClassDescriptions(physx::pvdsdk::PvdDataStream& inStream) + { + if (inStream.isClassExist()) + return; + + { //PxJoint + registerProperties( inStream ); + inStream.createProperty( "Parent", "parents" ); + registerPropertiesAndValueStruct( inStream); + registerPropertiesAndValueStruct(inStream); + registerPropertiesAndValueStruct( inStream); + registerPropertiesAndValueStruct( inStream); + registerPropertiesAndValueStruct( inStream); + registerPropertiesAndValueStruct( inStream); + registerPropertiesAndValueStruct( inStream); + } + } + + void Pvd::setActors( physx::pvdsdk::PvdDataStream& inStream, const PxJoint& inJoint, const PxConstraint& c, const PxActor* newActor0, const PxActor* newActor1 ) + { + PxRigidActor* actor0, *actor1; + c.getActors( actor0, actor1 ); + if ( actor0 ) + inStream.removeObjectRef( actor0, "Joints", &inJoint ); + if ( actor1 ) + inStream.removeObjectRef( actor1, "Joints", &inJoint ); + + if ( newActor0 && newActor0->getScene()) + inStream.pushBackObjectRef( newActor0, "Joints", &inJoint ); + if ( newActor1 && newActor1->getScene()) + inStream.pushBackObjectRef( newActor1, "Joints", &inJoint ); + + inStream.setPropertyValue( &inJoint, "Actors.actor0", reinterpret_cast(newActor0) ); + inStream.setPropertyValue( &inJoint, "Actors.actor1", reinterpret_cast(newActor1) ); + const void* parent = newActor0 ? newActor0 : newActor1; + inStream.setPropertyValue( &inJoint, "Parent", parent ); + + if((newActor0 && !newActor0->getScene()) || (newActor1 && !newActor1->getScene())) + { + inStream.removeObjectRef( c.getScene(), "Joints", &inJoint ); + } + + } +} + +} + +#endif // PX_SUPPORT_PVD diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPvd.h b/src/PhysX/physx/source/physxextensions/src/ExtPvd.h new file mode 100644 index 000000000..1f9d5796b --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPvd.h @@ -0,0 +1,191 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef EXT_PVD_H +#define EXT_PVD_H + +#if PX_SUPPORT_PVD + +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PxJoint.h" +#include "PxPvdDataStream.h" +#include "PxExtensionMetaDataObjects.h" +#include "PvdTypeNames.h" +#include "PxPvdObjectModelBaseTypes.h" + +namespace physx +{ + +class PxJoint; +class PxD6Joint; +class PxDistanceJoint; +class PxFixedJoint; +class PxPrismaticJoint; +class PxRevoluteJoint; +class PxSphericalJoint; +class PxContactJoint; +} + +#define JOINT_GROUP 3 +namespace physx +{ +namespace pvdsdk { + #define DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP( type ) DEFINE_PVD_TYPE_NAME_MAP( physx::type, "physx3", #type ) + + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxFixedJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxFixedJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxDistanceJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxDistanceJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxContactJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxContactJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPrismaticJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxPrismaticJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRevoluteJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxRevoluteJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSphericalJoint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxSphericalJointGeneratedValues) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxD6Joint) + DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP(PxD6JointGeneratedValues) +#undef DEFINE_NATIVE_PVD_PHYSX3_TYPE_MAP +} //pvdsdk +} // physx + +namespace physx +{ +namespace Ext +{ + using namespace physx::pvdsdk; + + class Pvd: public physx::shdfnd::UserAllocated + { + Pvd& operator=(const Pvd&); + public: + class PvdNameSpace + { + + public: + PvdNameSpace(PvdDataStream& conn, const char* name); + ~PvdNameSpace(); + private: + PvdNameSpace& operator=(const PvdNameSpace&); + PvdDataStream& mConnection; + }; + + static void setActors( PvdDataStream& PvdDataStream, + const PxJoint& inJoint, const PxConstraint& c, const PxActor* newActor0, const PxActor* newActor1 ); + + template + static void createInstance( PvdDataStream& inStream, const PxConstraint& c, const TObjType& inSource ) + { + inStream.createInstance( &inSource ); + inStream.pushBackObjectRef( c.getScene(), "Joints", &inSource ); + + class ConstraintUpdateCmd : public PvdDataStream::PvdCommand + { + ConstraintUpdateCmd &operator=(const ConstraintUpdateCmd&) { PX_ASSERT(0); return *this; } //PX_NOCOPY doesn't work for local classes + public: + + const PxConstraint& mConstraint; + const PxJoint& mJoint; + + PxRigidActor* actor0, *actor1; + ConstraintUpdateCmd(const PxConstraint& constraint, const PxJoint& joint):PvdDataStream::PvdCommand(), mConstraint(constraint), mJoint(joint) + { + mConstraint.getActors( actor0, actor1 ); + } + + //Assigned is needed for copying + ConstraintUpdateCmd(const ConstraintUpdateCmd& cmd) + :PvdDataStream::PvdCommand(), mConstraint(cmd.mConstraint), mJoint(cmd.mJoint) + { + } + + virtual bool canRun(PvdInstanceDataStream &inStream_ ) + { + PX_ASSERT(inStream_.isInstanceValid(&mJoint)); + //When run this command, the constraint maybe buffer removed + return ((actor0 == NULL) || inStream_.isInstanceValid(actor0)) + && ((actor1 == NULL) || inStream_.isInstanceValid(actor1)); + } + virtual void run( PvdInstanceDataStream &inStream_ ) + { + //When run this command, the constraint maybe buffer removed + if(!inStream_.isInstanceValid(&mJoint)) + return; + + PxRigidActor* actor0_, *actor1_; + mConstraint.getActors( actor0_, actor1_ ); + + if ( actor0_ && (inStream_.isInstanceValid(actor0_)) ) + inStream_.pushBackObjectRef( actor0_, "Joints", &mJoint ); + if ( actor1_ && (inStream_.isInstanceValid(actor1_)) ) + inStream_.pushBackObjectRef( actor1_, "Joints", &mJoint ); + const void* parent = actor0_ ? actor0_ : actor1_; + inStream_.setPropertyValue( &mJoint, "Parent", parent ); + } + }; + + ConstraintUpdateCmd* cmd = PX_PLACEMENT_NEW(inStream.allocateMemForCmd(sizeof(ConstraintUpdateCmd)), + ConstraintUpdateCmd)(c, inSource); + + if(cmd->canRun( inStream )) + cmd->run( inStream ); + else + inStream.pushPvdCommand( *cmd ); + } + + template + static void updatePvdProperties(PvdDataStream& pvdConnection, const jointtype& joint) + { + structValue theValueStruct( &joint ); + pvdConnection.setPropertyMessage( &joint, theValueStruct ); + } + + template + static void simUpdate(PvdDataStream& /*pvdConnection*/, const jointtype& /*joint*/) {} + + template + static void createPvdInstance(PvdDataStream& pvdConnection, const PxConstraint& c, const jointtype& joint) + { + createInstance( pvdConnection, c, joint ); + } + + static void releasePvdInstance(PvdDataStream& pvdConnection, const PxConstraint& c, const PxJoint& joint); + static void sendClassDescriptions(PvdDataStream& pvdConnection); + }; +} // ext + +} // physx + +#endif // PX_SUPPORT_PVD +#endif // EXT_PVD_H diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp b/src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp new file mode 100644 index 000000000..8d640a4ae --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxAllocatorCallback.h" +#include "PxStringTableExt.h" +#include "PxProfileAllocatorWrapper.h" //tools for using a custom allocator +#include "PsString.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + using namespace physx::profile; + + class PxStringTableImpl : public PxStringTable, public Ps::UserAllocated + { + typedef PxProfileHashMap THashMapType; + PxProfileAllocatorWrapper mWrapper; + THashMapType mHashMap; + public: + + PxStringTableImpl( PxAllocatorCallback& inAllocator ) + : mWrapper ( inAllocator ) + , mHashMap ( mWrapper ) + { + } + + virtual ~PxStringTableImpl() + { + for ( THashMapType::Iterator iter = mHashMap.getIterator(); + iter.done() == false; + ++iter ) + PX_PROFILE_DELETE( mWrapper, const_cast( iter->first ) ); + mHashMap.clear(); + } + + + virtual const char* allocateStr( const char* inSrc ) + { + if ( inSrc == NULL ) + inSrc = ""; + const THashMapType::Entry* existing( mHashMap.find( inSrc ) ); + if ( existing == NULL ) + { + size_t len( strlen( inSrc ) ); + len += 1; + char* newMem = reinterpret_cast(mWrapper.getAllocator().allocate( len, "PxStringTableImpl: const char*", __FILE__, __LINE__ )); + physx::shdfnd::strlcpy( newMem, len, inSrc ); + mHashMap.insert( newMem, 1 ); + return newMem; + } + else + { + ++const_cast(existing)->second; + return existing->first; + } + } + + /** + * Release the string table and all the strings associated with it. + */ + virtual void release() + { + PX_PROFILE_DELETE( mWrapper.getAllocator(), this ); + } + }; + + PxStringTable& PxStringTableExt::createStringTable( PxAllocatorCallback& inAllocator ) + { + return *PX_PROFILE_NEW( inAllocator, PxStringTableImpl )( inAllocator ); + } +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp new file mode 100644 index 000000000..64a97800d --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp @@ -0,0 +1,305 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxRaycastCCD.h" + +using namespace physx; + +#include "geometry/PxBoxGeometry.h" +#include "geometry/PxSphereGeometry.h" +#include "geometry/PxCapsuleGeometry.h" +#include "geometry/PxConvexMeshGeometry.h" +#include "geometry/PxConvexMesh.h" +#include "PxScene.h" +#include "PxRigidDynamic.h" +#include "extensions/PxShapeExt.h" +#include "PsArray.h" + +namespace physx +{ +class RaycastCCDManagerInternal +{ + PX_NOCOPY(RaycastCCDManagerInternal) + public: + RaycastCCDManagerInternal(PxScene* scene) : mScene(scene) {} + ~RaycastCCDManagerInternal(){} + + bool registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape); + + void doRaycastCCD(bool doDynamicDynamicCCD); + + struct CCDObject + { + PX_FORCE_INLINE CCDObject(PxRigidDynamic* actor, PxShape* shape, const PxVec3& witness) : mActor(actor), mShape(shape), mWitness(witness) {} + PxRigidDynamic* mActor; + PxShape* mShape; + PxVec3 mWitness; + }; + + private: + PxScene* mScene; + physx::shdfnd::Array mObjects; +}; +} + +static PxVec3 getShapeCenter(PxShape* shape, const PxTransform& pose) +{ + PxVec3 offset(0.0f); + if(shape->getGeometryType()==PxGeometryType::eCONVEXMESH) + { + PxConvexMeshGeometry geometry; + bool status = shape->getConvexMeshGeometry(geometry); + PX_UNUSED(status); + PX_ASSERT(status); + + PxReal mass; + PxMat33 localInertia; + PxVec3 localCenterOfMass; + geometry.convexMesh->getMassInformation(mass, localInertia, localCenterOfMass); + + offset += localCenterOfMass; + } + return pose.transform(offset); +} + +static PX_FORCE_INLINE PxVec3 getShapeCenter(PxRigidActor* actor, PxShape* shape) +{ + const PxTransform pose = PxShapeExt::getGlobalPose(*shape, *actor); + return getShapeCenter(shape, pose); +} + +static PxReal computeInternalRadius(PxRigidActor* actor, PxShape* shape, const PxVec3& dir) +{ + const PxBounds3 bounds = PxShapeExt::getWorldBounds(*shape, *actor); + const PxReal diagonal = (bounds.maximum - bounds.minimum).magnitude(); + const PxReal offsetFromOrigin = diagonal * 2.0f; + + PxTransform pose = PxShapeExt::getGlobalPose(*shape, *actor); + + PxReal internalRadius = 0.0f; + const PxReal length = offsetFromOrigin*2.0f; + + switch(shape->getGeometryType()) + { + case PxGeometryType::eSPHERE: + { + PxSphereGeometry geometry; + bool status = shape->getSphereGeometry(geometry); + PX_UNUSED(status); + PX_ASSERT(status); + + internalRadius = geometry.radius; + } + break; + + case PxGeometryType::eBOX: + case PxGeometryType::eCAPSULE: + { + pose.p = PxVec3(0.0f); + const PxVec3 virtualOrigin = pose.p + dir * offsetFromOrigin; + + PxRaycastHit hit; + PxU32 nbHits = PxGeometryQuery::raycast(virtualOrigin, -dir, shape->getGeometry().any(), pose, length, PxHitFlags(0), 1, &hit); + PX_UNUSED(nbHits); + PX_ASSERT(nbHits); + + internalRadius = offsetFromOrigin - hit.distance; + } + break; + + case PxGeometryType::eCONVEXMESH: + { + PxVec3 shapeCenter = getShapeCenter(shape, pose); + shapeCenter -= pose.p; + pose.p = PxVec3(0.0f); + + const PxVec3 virtualOrigin = shapeCenter + dir * offsetFromOrigin; + PxRaycastHit hit; + PxU32 nbHits = PxGeometryQuery::raycast(virtualOrigin, -dir, shape->getGeometry().any(), pose, length, PxHitFlags(0), 1, &hit); + PX_UNUSED(nbHits); + PX_ASSERT(nbHits); + + internalRadius = offsetFromOrigin - hit.distance; + } + break; + + case PxGeometryType::ePLANE: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + } + return internalRadius; +} + +class CCDRaycastFilterCallback : public PxQueryFilterCallback +{ +public: + CCDRaycastFilterCallback(PxRigidActor* actor, PxShape* shape) : mActor(actor), mShape(shape){} + + PxRigidActor* mActor; + PxShape* mShape; + + virtual PxQueryHitType::Enum preFilter(const PxFilterData&, const PxShape* shape, const PxRigidActor* actor, PxHitFlags&) + { + if(mActor==actor && mShape==shape) + return PxQueryHitType::eNONE; + return PxQueryHitType::eBLOCK; + } + + virtual PxQueryHitType::Enum postFilter(const PxFilterData&, const PxQueryHit&) + { + return PxQueryHitType::eNONE; + } +}; + +static bool CCDRaycast(PxScene* scene, PxRigidActor* actor, PxShape* shape, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, PxRaycastHit& hit, bool dyna_dyna) +{ + const PxQueryFlags qf(dyna_dyna ? PxQueryFlags(PxQueryFlag::eSTATIC|PxQueryFlag::eDYNAMIC|PxQueryFlag::ePREFILTER) : PxQueryFlags(PxQueryFlag::eSTATIC)); + const PxQueryFilterData filterData(PxFilterData(), qf); + + CCDRaycastFilterCallback CB(actor, shape); + + PxRaycastBuffer buf1; + scene->raycast(origin, unitDir, distance, buf1, PxHitFlags(0), filterData, &CB); + hit = buf1.block; + return buf1.hasBlock; +} + +static PxRigidDynamic* canDoCCD(PxRigidActor& actor, PxShape* /*shape*/) +{ + if(actor.getConcreteType()!=PxConcreteType::eRIGID_DYNAMIC) + return NULL; // PT: no need to do it for statics + PxRigidDynamic* dyna = static_cast(&actor); + + const PxU32 nbShapes = dyna->getNbShapes(); + if(nbShapes!=1) + return NULL; // PT: only works with simple actors for now + + if(dyna->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC) + return NULL; // PT: no need to do it for kinematics + + return dyna; +} + +static bool doRaycastCCD(PxScene* scene, const RaycastCCDManagerInternal::CCDObject& object, PxTransform& newPose, PxVec3& newShapeCenter, bool dyna_dyna) +{ + PxRigidDynamic* dyna = canDoCCD(*object.mActor, object.mShape); + if(!dyna) + return true; + + bool updateCCDWitness = true; + + const PxVec3 offset = newPose.p - newShapeCenter; + const PxVec3& origin = object.mWitness; + const PxVec3& dest = newShapeCenter; + + PxVec3 dir = dest - origin; + const PxReal length = dir.magnitude(); + if(length!=0.0f) + { + dir /= length; + + const PxReal internalRadius = computeInternalRadius(object.mActor, object.mShape, dir); + + PxRaycastHit hit; + if(internalRadius!=0.0f && CCDRaycast(scene, object.mActor, object.mShape, origin, dir, length, hit, dyna_dyna)) + { + updateCCDWitness = false; + + const PxReal radiusLimit = internalRadius * 0.75f; + if(hit.distance>radiusLimit) + { + newShapeCenter = origin + dir * (hit.distance - radiusLimit); + } + else + { + if(hit.actor->getConcreteType()==PxConcreteType::eRIGID_DYNAMIC) + return true; + + newShapeCenter = origin; + } + + newPose.p = offset + newShapeCenter; + const PxTransform shapeLocalPose = object.mShape->getLocalPose(); + const PxTransform inverseShapeLocalPose = shapeLocalPose.getInverse(); + const PxTransform newGlobalPose = newPose * inverseShapeLocalPose; + dyna->setGlobalPose(newGlobalPose); + } + } + return updateCCDWitness; +} + +bool RaycastCCDManagerInternal::registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape) +{ + if(!actor || !shape) + return false; + + mObjects.pushBack(CCDObject(actor, shape, getShapeCenter(actor, shape))); + return true; +} + +void RaycastCCDManagerInternal::doRaycastCCD(bool doDynamicDynamicCCD) +{ + const PxU32 nbObjects = mObjects.size(); + for(PxU32 i=0;iisSleeping()) + continue; + + PxTransform newPose = PxShapeExt::getGlobalPose(*object.mShape, *object.mActor); + PxVec3 newShapeCenter = getShapeCenter(object.mShape, newPose); + + if(::doRaycastCCD(mScene, object, newPose, newShapeCenter, doDynamicDynamicCCD)) + object.mWitness = newShapeCenter; + } +} + +RaycastCCDManager::RaycastCCDManager(PxScene* scene) +{ + mImpl = new RaycastCCDManagerInternal(scene); +} + +RaycastCCDManager::~RaycastCCDManager() +{ + delete mImpl; +} + +bool RaycastCCDManager::registerRaycastCCDObject(PxRigidDynamic* actor, PxShape* shape) +{ + return mImpl->registerRaycastCCDObject(actor, shape); +} + +void RaycastCCDManager::doRaycastCCD(bool doDynamicDynamicCCD) +{ + mImpl->doRaycastCCD(doDynamicDynamicCCD); +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp new file mode 100644 index 000000000..39819b6a3 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp @@ -0,0 +1,345 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ExtRevoluteJoint.h" +#include "PsUtilities.h" +#include "ExtConstraintHelper.h" +#include "CmRenderOutput.h" +#include "PsMathUtils.h" +#include "CmVisualization.h" +#include "CmUtils.h" + +#include "common/PxSerialFramework.h" + +using namespace physx; +using namespace Ext; + +PxRevoluteJoint* physx::PxRevoluteJointCreate(PxPhysics& physics, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) +{ + PX_CHECK_AND_RETURN_NULL(localFrame0.isSane(), "PxRevoluteJointCreate: local frame 0 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(localFrame1.isSane(), "PxRevoluteJointCreate: local frame 1 is not a valid transform"); + PX_CHECK_AND_RETURN_NULL(actor0 != actor1, "PxRevoluteJointCreate: actors must be different"); + PX_CHECK_AND_RETURN_NULL((actor0 && actor0->is()) || (actor1 && actor1->is()), "PxRevoluteJointCreate: at least one actor must be dynamic"); + + RevoluteJoint* j; + PX_NEW_SERIALIZED(j, RevoluteJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +PxReal RevoluteJoint::getAngle() const +{ + return getTwistAngle_Internal(); +} + +PxReal RevoluteJoint::getVelocity() const +{ + return getRelativeAngularVelocity().magnitude(); +} + +PxJointAngularLimitPair RevoluteJoint::getLimit() const +{ + return data().limit; +} + +void RevoluteJoint::setLimit(const PxJointAngularLimitPair& limit) +{ + PX_CHECK_AND_RETURN(limit.isValid(), "PxRevoluteJoint::setLimit: limit invalid"); + PX_CHECK_AND_RETURN(limit.lower>-PxTwoPi && limit.upper0, "PxRevoluteJoint::setDriveGearRatio: invalid parameter"); + data().driveGearRatio = gearRatio; + markDirty(); +} + +void RevoluteJoint::setProjectionAngularTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance>=0 && tolerance<=PxPi, "PxRevoluteJoint::setProjectionAngularTolerance: invalid parameter"); + data().projectionAngularTolerance = tolerance; + markDirty(); +} + +PxReal RevoluteJoint::getProjectionAngularTolerance() const +{ + return data().projectionAngularTolerance; +} + +void RevoluteJoint::setProjectionLinearTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxRevoluteJoint::setProjectionLinearTolerance: invalid parameter"); + data().projectionLinearTolerance = tolerance; + markDirty(); +} + +PxReal RevoluteJoint::getProjectionLinearTolerance() const +{ + return data().projectionLinearTolerance; +} + +PxRevoluteJointFlags RevoluteJoint::getRevoluteJointFlags(void) const +{ + return data().jointFlags; +} + +void RevoluteJoint::setRevoluteJointFlags(PxRevoluteJointFlags flags) +{ + data().jointFlags = flags; +} + +void RevoluteJoint::setRevoluteJointFlag(PxRevoluteJointFlag::Enum flag, bool value) +{ + if(value) + data().jointFlags |= flag; + else + data().jointFlags &= ~flag; + markDirty(); +} + +bool RevoluteJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(RevoluteJointData)); + return mPxConstraint!=NULL; +} + +void RevoluteJoint::exportExtraData(PxSerializationContext& stream) +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(RevoluteJointData)); + } + stream.writeName(mName); +} + +void RevoluteJoint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + context.readName(mName); +} + +void RevoluteJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +RevoluteJoint* RevoluteJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + RevoluteJoint* obj = new (address) RevoluteJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(RevoluteJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetRevoluteJointShaderTable() +{ + return &RevoluteJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void RevoluteJointProject(const void* constantBlock, PxTransform& bodyAToWorld, PxTransform& bodyBToWorld, bool projectToA) +{ + const RevoluteJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w, cB2cA, projected; + joint::computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA, false); + + bool linearTrunc, angularTrunc; + projected.p = joint::truncateLinear(cB2cA.p, data.projectionLinearTolerance, linearTrunc); + + PxQuat swing, twist, projSwing; + Ps::separateSwingTwist(cB2cA.q, swing, twist); + projSwing = joint::truncateAngular(swing, PxSin(data.projectionAngularTolerance/2), PxCos(data.projectionAngularTolerance/2), angularTrunc); + + if(linearTrunc || angularTrunc) + { + projected.q = projSwing * twist; + joint::projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA); + } +} + +static PxQuat computeTwist(const PxTransform& cA2w, const PxTransform& cB2w) +{ + // PT: following code is the same as this part of the "getAngle" function: + // const PxQuat q = getRelativeTransform().q; + // PxQuat swing, twist; + // Ps::separateSwingTwist(q, swing, twist); + // But it's done a little bit more efficiently since we don't need the swing quat. + + // PT: rotation part of "const PxTransform cB2cA = cA2w.transformInv(cB2w);" + const PxQuat cB2cAq = cA2w.q.getConjugate() * cB2w.q; + + // PT: twist part of "Ps::separateSwingTwist(cB2cAq,swing,twist)" (more or less) + return PxQuat(cB2cAq.x, 0.0f, 0.0f, cB2cAq.w); +} + +// PT: this version is similar to the "getAngle" function, but the twist is computed slightly differently. +static PX_FORCE_INLINE PxReal computePhi(const PxTransform& cA2w, const PxTransform& cB2w) +{ + PxQuat twist = computeTwist(cA2w, cB2w); + twist.normalize(); + + PxReal angle = twist.getAngle(); + if(twist.x<0.0f) + angle = -angle; + return angle; +} + +static void RevoluteJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags) +{ + const RevoluteJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + viz.visualizeJointFrames(cA2w, cB2w); + + if((data.jointFlags & PxRevoluteJointFlag::eLIMIT_ENABLED) && (flags & PxConstraintVisualizationFlag::eLIMITS)) + { + const PxReal angle = computePhi(cA2w, cB2w); + const PxReal pad = data.limit.contactDistance; + const PxReal low = data.limit.lower; + const PxReal high = data.limit.upper; + + const bool active = isLimitActive(data.limit, pad, angle, low, high); + viz.visualizeAngularLimit(cA2w, data.limit.lower, data.limit.upper, active); + } +} + +static PxU32 RevoluteJointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool useExtendedLimits, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const RevoluteJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + const PxJointAngularLimitPair& limit = data.limit; + + const bool limitEnabled = data.jointFlags & PxRevoluteJointFlag::eLIMIT_ENABLED; + const bool limitIsLocked = limitEnabled && limit.lower >= limit.upper; + + // PT: it is a mistake to use the neighborhood operator since it + // prevents us from using the quat's double-cover feature. + if(!useExtendedLimits && cB2w.q.dot(cA2w.q)<0.0f) + cB2w.q = -cB2w.q; + + PxVec3 ra, rb; + ch.prepareLockedAxes(cA2w.q, cB2w.q, cA2w.transformInv(cB2w.p), 7, PxU32(limitIsLocked ? 7 : 6), ra, rb); + cA2wOut = ra + bA2w.p; + cB2wOut = rb + bB2w.p; + + if(limitIsLocked) + return ch.getCount(); + + const PxVec3 axis = cA2w.rotate(PxVec3(1.0f, 0.0f, 0.0f)); + + if(data.jointFlags & PxRevoluteJointFlag::eDRIVE_ENABLED) + { + Px1DConstraint* c = ch.getConstraintRow(); + + c->solveHint = PxConstraintSolveHint::eNONE; + c->linear0 = PxVec3(0.0f); + c->angular0 = -axis; + c->linear1 = PxVec3(0.0f); + c->angular1 = -axis * data.driveGearRatio; + c->velocityTarget = data.driveVelocity; + c->minImpulse = -data.driveForceLimit; + c->maxImpulse = data.driveForceLimit; + c->flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT; + if(data.jointFlags & PxRevoluteJointFlag::eDRIVE_FREESPIN) + { + if(data.driveVelocity > 0.0f) + c->minImpulse = 0.0f; + if(data.driveVelocity < 0.0f) + c->maxImpulse = 0.0f; + } + c->flags |= Px1DConstraintFlag::eHAS_DRIVE_LIMIT; + } + + if(limitEnabled) + { + const PxReal phi = computePhi(cA2w, cB2w); + ch.anglePair(phi, data.limit.lower, data.limit.upper, data.limit.contactDistance, axis, limit); + } + + return ch.getCount(); +} + +PxConstraintShaderTable Ext::RevoluteJoint::sShaders = { RevoluteJointSolverPrep, RevoluteJointProject, RevoluteJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h new file mode 100644 index 000000000..36fa1eefe --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h @@ -0,0 +1,153 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_REVOLUTEJOINTCONSTRAINT_H +#define NP_REVOLUTEJOINTCONSTRAINT_H + +#include "ExtJoint.h" +#include "PxRevoluteJoint.h" +#include "PsIntrinsics.h" +#include "CmUtils.h" + +namespace physx +{ + +class PxConstraintSolverPrepKernel; +class PxConstraintProjectionKernel; +struct PxRevoluteJointGeneratedValues; + +namespace Ext +{ + struct RevoluteJointData : public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PxReal driveVelocity; + PxReal driveForceLimit; + PxReal driveGearRatio; + + PxJointAngularLimitPair limit; + + PxReal projectionLinearTolerance; + PxReal projectionAngularTolerance; + + PxRevoluteJointFlags jointFlags; + // forestall compiler complaints about not being able to generate a constructor + private: + RevoluteJointData(const PxJointAngularLimitPair &pair): + limit(pair) {} + }; + + typedef Joint RevoluteJointT; + + class RevoluteJoint : public RevoluteJointT + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + RevoluteJoint(PxBaseFlags baseFlags) : RevoluteJointT(baseFlags) {} + void resolveReferences(PxDeserializationContext& context); + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + static RevoluteJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + RevoluteJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + RevoluteJointT(PxJointConcreteType::eREVOLUTE, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(RevoluteJointData), "RevoluteJointData") + { + RevoluteJointData* data = static_cast(mData); + + data->projectionLinearTolerance = 1e10f; + data->projectionAngularTolerance = PxPi; + data->driveForceLimit = PX_MAX_F32; + data->driveVelocity = 0.0f; + data->driveGearRatio = 1.0f; + data->limit = PxJointAngularLimitPair(-PxPi/2, PxPi/2); + data->jointFlags = PxRevoluteJointFlags(); + } + + // PxRevoluteJoint + virtual PxReal getAngle() const; + virtual PxReal getVelocity() const; + virtual void setLimit(const PxJointAngularLimitPair& limit); + virtual PxJointAngularLimitPair getLimit() const; + virtual void setDriveVelocity(PxReal velocity, bool autowake = true); + virtual PxReal getDriveVelocity() const; + virtual void setDriveForceLimit(PxReal forceLimit); + virtual PxReal getDriveForceLimit() const; + virtual void setDriveGearRatio(PxReal gearRatio); + virtual PxReal getDriveGearRatio() const; + virtual void setRevoluteJointFlags(PxRevoluteJointFlags flags); + virtual void setRevoluteJointFlag(PxRevoluteJointFlag::Enum flag, bool value); + virtual PxRevoluteJointFlags getRevoluteJointFlags() const; + virtual void setProjectionLinearTolerance(PxReal distance); + virtual PxReal getProjectionLinearTolerance() const; + virtual void setProjectionAngularTolerance(PxReal tolerance); + virtual PxReal getProjectionAngularTolerance() const; + //~PxRevoluteJoint + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep() const { return sShaders.solverPrep; } + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE RevoluteJointData& data() const + { + return *static_cast(mData); + } + + }; + +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetRevoluteJointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp new file mode 100644 index 000000000..acfe736f6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp @@ -0,0 +1,71 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxRigidActorExt.h" +#include "PsAllocator.h" +#include "PsInlineArray.h" +#include "CmPhysXCommon.h" +#include "PxShape.h" +#include "PxGeometryQuery.h" + +using namespace physx; + +PxBounds3* PxRigidActorExt::getRigidActorShapeLocalBoundsList(const PxRigidActor& actor, PxU32& numBounds) +{ + const PxU32 numShapes = actor.getNbShapes(); + if(numShapes == 0) + return NULL; + + Ps::InlineArray shapes("PxShape*"); + shapes.resize(numShapes); + + actor.getShapes(shapes.begin(), shapes.size()); + + PxU32 numSqShapes = 0; + for(PxU32 i = 0; i < numShapes; i++) + { + if(shapes[i]->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE) + numSqShapes++; + } + + PxBounds3* bounds = reinterpret_cast(PX_ALLOC(numSqShapes * sizeof(PxBounds3), "PxBounds3")); + + numSqShapes = 0; + for(PxU32 i = 0; i < numShapes; i++) + { + if(shapes[i]->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE) + { + bounds[numSqShapes++] = PxGeometryQuery::getWorldBounds(shapes[i]->getGeometry().any(), shapes[i]->getLocalPose(), 1.0f); + } + } + + numBounds = numSqShapes; + return bounds; +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp new file mode 100644 index 000000000..3f8e79259 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp @@ -0,0 +1,648 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxRigidBodyExt.h" +#include "PxShapeExt.h" +#include "PxMassProperties.h" + +#include "ExtInertiaTensor.h" +#include "PsAllocator.h" +#include "PsFoundation.h" + +#include "PxShape.h" +#include "PxScene.h" + +#include "PxBoxGeometry.h" +#include "PxSphereGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxTriangleMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "PxGeometryHelpers.h" + +#include "PxConvexMesh.h" + +#include "PxBatchQuery.h" + +#include "PxRigidDynamic.h" +#include "PxRigidStatic.h" +#include "CmUtils.h" + +using namespace physx; +using namespace Cm; + +static bool computeMassAndDiagInertia(Ext::InertiaTensorComputer& inertiaComp, + PxVec3& diagTensor, PxQuat& orient, PxReal& massOut, PxVec3& coM, bool lockCOM, const PxRigidBody& body, const char* errorStr) +{ + // The inertia tensor and center of mass is relative to the actor at this point. Transform to the + // body frame directly if CoM is specified, else use computed center of mass + if (lockCOM) + { + inertiaComp.translate(-coM); // base the tensor on user's desired center of mass. + } + else + { + //get center of mass - has to be done BEFORE centering. + coM = inertiaComp.getCenterOfMass(); + + //the computed result now needs to be centered around the computed center of mass: + inertiaComp.center(); + } + // The inertia matrix is now based on the body's center of mass desc.massLocalPose.p + + massOut = inertiaComp.getMass(); + diagTensor = PxDiagonalize(inertiaComp.getInertia(), orient); + + if ((diagTensor.x > 0.0f) && (diagTensor.y > 0.0f) && (diagTensor.z > 0.0f)) + return true; + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "%s: inertia tensor has negative components (ill-conditioned input expected). Approximation for inertia tensor will be used instead.", errorStr); + + // keep center of mass but use the AABB as a crude approximation for the inertia tensor + PxBounds3 bounds = body.getWorldBounds(); + PxTransform pose = body.getGlobalPose(); + bounds = PxBounds3::transformFast(pose.getInverse(), bounds); + Ext::InertiaTensorComputer it(false); + it.setBox(bounds.getExtents()); + it.scaleDensity(massOut / it.getMass()); + PxMat33 inertia = it.getInertia(); + diagTensor = PxVec3(inertia.column0.x, inertia.column1.y, inertia.column2.z); + orient = PxQuat(PxIdentity); + + return true; + } +} + +static bool computeMassAndInertia(bool multipleMassOrDensity, PxRigidBody& body, const PxReal* densities, const PxReal* masses, PxU32 densityOrMassCount, bool includeNonSimShapes, Ext::InertiaTensorComputer& computer) +{ + PX_ASSERT(!densities || !masses); + PX_ASSERT((densities || masses) && (densityOrMassCount > 0)); + + Ext::InertiaTensorComputer inertiaComp(true); + + Ps::InlineArray shapes("PxShape*"); shapes.resize(body.getNbShapes()); + + body.getShapes(shapes.begin(), shapes.size()); + + PxU32 validShapeIndex = 0; + PxReal currentMassOrDensity; + const PxReal* massOrDensityArray; + if (densities) + { + massOrDensityArray = densities; + currentMassOrDensity = densities[0]; + } + else + { + massOrDensityArray = masses; + currentMassOrDensity = masses[0]; + } + if (!PxIsFinite(currentMassOrDensity)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "computeMassAndInertia: Provided mass or density has no valid value"); + return false; + } + + for(PxU32 i=0; i < shapes.size(); i++) + { + if ((!(shapes[i]->getFlags() & PxShapeFlag::eSIMULATION_SHAPE)) && (!includeNonSimShapes)) + continue; + + if (multipleMassOrDensity) + { + if (validShapeIndex < densityOrMassCount) + { + currentMassOrDensity = massOrDensityArray[validShapeIndex]; + + if (!PxIsFinite(currentMassOrDensity)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "computeMassAndInertia: Provided mass or density has no valid value"); + return false; + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "computeMassAndInertia: Not enough mass/density values provided for all (simulation) shapes"); + return false; + } + } + + Ext::InertiaTensorComputer it(false); + + switch(shapes[i]->getGeometryType()) + { + case PxGeometryType::eSPHERE : + { + PxSphereGeometry g; + bool ok = shapes[i]->getSphereGeometry(g); + PX_ASSERT(ok); + PX_UNUSED(ok); + PxTransform temp(shapes[i]->getLocalPose()); + + it.setSphere(g.radius, &temp); + } + break; + + case PxGeometryType::eBOX : + { + PxBoxGeometry g; + bool ok = shapes[i]->getBoxGeometry(g); + PX_ASSERT(ok); + PX_UNUSED(ok); + PxTransform temp(shapes[i]->getLocalPose()); + + it.setBox(g.halfExtents, &temp); + } + break; + + case PxGeometryType::eCAPSULE : + { + PxCapsuleGeometry g; + bool ok = shapes[i]->getCapsuleGeometry(g); + PX_ASSERT(ok); + PX_UNUSED(ok); + PxTransform temp(shapes[i]->getLocalPose()); + + it.setCapsule(0, g.radius, g.halfHeight, &temp); + } + break; + + case PxGeometryType::eCONVEXMESH : + { + PxConvexMeshGeometry g; + bool ok = shapes[i]->getConvexMeshGeometry(g); + PX_ASSERT(ok); + PX_UNUSED(ok); + PxConvexMesh& convMesh = *g.convexMesh; + + PxReal convMass; + PxMat33 convInertia; + PxVec3 convCoM; + convMesh.getMassInformation(convMass, reinterpret_cast(convInertia), convCoM); + + if (!g.scale.isIdentity()) + { + //scale the mass properties + convMass *= (g.scale.scale.x * g.scale.scale.y * g.scale.scale.z); + convCoM = g.scale.rotation.rotateInv(g.scale.scale.multiply(g.scale.rotation.rotate(convCoM))); + convInertia = PxMassProperties::scaleInertia(convInertia, g.scale.rotation, g.scale.scale); + } + + it = Ext::InertiaTensorComputer(convInertia, convCoM, convMass); + it.transform(shapes[i]->getLocalPose()); + } + break; + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eINVALID: + case PxGeometryType::eGEOMETRY_COUNT: + { + + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "computeMassAndInertia: Dynamic actor with illegal collision shapes"); + return false; + } + } + + if (densities) + it.scaleDensity(currentMassOrDensity); + else if (multipleMassOrDensity) // mass per shape -> need to scale density per shape + it.scaleDensity(currentMassOrDensity / it.getMass()); + + inertiaComp.add(it); + + validShapeIndex++; + } + + if (validShapeIndex && masses && (!multipleMassOrDensity)) // at least one simulation shape and single mass for all shapes -> scale density at the end + { + inertiaComp.scaleDensity(currentMassOrDensity / inertiaComp.getMass()); + } + + computer = inertiaComp; + return true; +} + +static bool updateMassAndInertia(bool multipleMassOrDensity, PxRigidBody& body, const PxReal* densities, PxU32 densityCount, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + bool success; + + // default values in case there were no shapes + PxReal massOut = 1.0f; + PxVec3 diagTensor(1.f,1.f,1.f); + PxQuat orient = PxQuat(PxIdentity); + bool lockCom = massLocalPose != NULL; + PxVec3 com = lockCom ? *massLocalPose : PxVec3(0); + const char* errorStr = "PxRigidBodyExt::updateMassAndInertia"; + + if (densities && densityCount) + { + Ext::InertiaTensorComputer inertiaComp(true); + if(computeMassAndInertia(multipleMassOrDensity, body, densities, NULL, densityCount, includeNonSimShapes, inertiaComp)) + { + if(inertiaComp.getMass()!=0 && computeMassAndDiagInertia(inertiaComp, diagTensor, orient, massOut, com, lockCom, body, errorStr)) + success = true; + else + success = false; // body with no shapes provided or computeMassAndDiagInertia() failed + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: Mass and inertia computation failed, setting mass to 1 and inertia to (1,1,1)", errorStr); + + success = false; + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: No density specified, setting mass to 1 and inertia to (1,1,1)", errorStr); + + success = false; + } + + PX_ASSERT(orient.isFinite()); + PX_ASSERT(diagTensor.isFinite()); + PX_ASSERT(PxIsFinite(massOut)); + + body.setMass(massOut); + body.setMassSpaceInertiaTensor(diagTensor); + body.setCMassLocalPose(PxTransform(com, orient)); + + return success; +} + +bool PxRigidBodyExt::updateMassAndInertia(PxRigidBody& body, const PxReal* densities, PxU32 densityCount, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + return ::updateMassAndInertia(true, body, densities, densityCount, massLocalPose, includeNonSimShapes); +} + +bool PxRigidBodyExt::updateMassAndInertia(PxRigidBody& body, PxReal density, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + return ::updateMassAndInertia(false, body, &density, 1, massLocalPose, includeNonSimShapes); +} + +static bool setMassAndUpdateInertia(bool multipleMassOrDensity, PxRigidBody& body, const PxReal* masses, PxU32 massCount, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + bool success; + + // default values in case there were no shapes + PxReal massOut = 1.0f; + PxVec3 diagTensor(1.0f,1.0f,1.0f); + PxQuat orient = PxQuat(PxIdentity); + bool lockCom = massLocalPose != NULL; + PxVec3 com = lockCom ? *massLocalPose : PxVec3(0); + const char* errorStr = "PxRigidBodyExt::setMassAndUpdateInertia"; + + if(masses && massCount) + { + Ext::InertiaTensorComputer inertiaComp(true); + if(computeMassAndInertia(multipleMassOrDensity, body, NULL, masses, massCount, includeNonSimShapes, inertiaComp)) + { + success = true; + + if (inertiaComp.getMass()!=0 && !computeMassAndDiagInertia(inertiaComp, diagTensor, orient, massOut, com, lockCom, body, errorStr)) + success = false; // computeMassAndDiagInertia() failed (mass zero?) + + if (massCount == 1) + massOut = masses[0]; // to cover special case where body has no simulation shape + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: Mass and inertia computation failed, setting mass to 1 and inertia to (1,1,1)", errorStr); + + success = false; + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "%s: No mass specified, setting mass to 1 and inertia to (1,1,1)", errorStr); + success = false; + } + + PX_ASSERT(orient.isFinite()); + PX_ASSERT(diagTensor.isFinite()); + + body.setMass(massOut); + body.setMassSpaceInertiaTensor(diagTensor); + body.setCMassLocalPose(PxTransform(com, orient)); + + return success; +} + +bool PxRigidBodyExt::setMassAndUpdateInertia(PxRigidBody& body, const PxReal* masses, PxU32 massCount, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + return ::setMassAndUpdateInertia(true, body, masses, massCount, massLocalPose, includeNonSimShapes); +} + +bool PxRigidBodyExt::setMassAndUpdateInertia(PxRigidBody& body, PxReal mass, const PxVec3* massLocalPose, bool includeNonSimShapes) +{ + return ::setMassAndUpdateInertia(false, body, &mass, 1, massLocalPose, includeNonSimShapes); +} + +PxMassProperties PxRigidBodyExt::computeMassPropertiesFromShapes(const PxShape* const* shapes, PxU32 shapeCount) +{ + Ps::InlineArray massProps; + massProps.reserve(shapeCount); + Ps::InlineArray localTransforms; + localTransforms.reserve(shapeCount); + + for(PxU32 shapeIdx=0; shapeIdx < shapeCount; shapeIdx++) + { + const PxShape* shape = shapes[shapeIdx]; + PxMassProperties mp(shape->getGeometry().any()); + massProps.pushBack(mp); + localTransforms.pushBack(shape->getLocalPose()); + } + + return PxMassProperties::sum(massProps.begin(), localTransforms.begin(), shapeCount); +} + +PX_INLINE void addForceAtPosInternal(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup) +{ + if(mode == PxForceMode::eACCELERATION || mode == PxForceMode::eVELOCITY_CHANGE) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxRigidBodyExt::addForce methods do not support eACCELERATION or eVELOCITY_CHANGE modes"); + return; + } + + const PxTransform globalPose = body.getGlobalPose(); + const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p); + + const PxVec3 torque = (pos - centerOfMass).cross(force); + body.addForce(force, mode, wakeup); + body.addTorque(torque, mode, wakeup); +} + +void PxRigidBodyExt::addForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup) +{ + addForceAtPosInternal(body, force, pos, mode, wakeup); +} + +void PxRigidBodyExt::addForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup) +{ + //transform pos to world space + const PxVec3 globalForcePos = body.getGlobalPose().transform(pos); + + addForceAtPosInternal(body, force, globalForcePos, mode, wakeup); +} + +void PxRigidBodyExt::addLocalForceAtPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup) +{ + const PxVec3 globalForce = body.getGlobalPose().rotate(force); + + addForceAtPosInternal(body, globalForce, pos, mode, wakeup); +} + +void PxRigidBodyExt::addLocalForceAtLocalPos(PxRigidBody& body, const PxVec3& force, const PxVec3& pos, PxForceMode::Enum mode, bool wakeup) +{ + const PxTransform globalPose = body.getGlobalPose(); + const PxVec3 globalForcePos = globalPose.transform(pos); + const PxVec3 globalForce = globalPose.rotate(force); + + addForceAtPosInternal(body, globalForce, globalForcePos, mode, wakeup); +} + +PX_INLINE PxVec3 getVelocityAtPosInternal(const PxRigidBody& body, const PxVec3& point) +{ + PxVec3 velocity = body.getLinearVelocity(); + velocity += body.getAngularVelocity().cross(point); + + return velocity; +} + +PxVec3 PxRigidBodyExt::getVelocityAtPos(const PxRigidBody& body, const PxVec3& point) +{ + const PxTransform globalPose = body.getGlobalPose(); + const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p); + const PxVec3 rpoint = point - centerOfMass; + + return getVelocityAtPosInternal(body, rpoint); +} + +PxVec3 PxRigidBodyExt::getLocalVelocityAtLocalPos(const PxRigidBody& body, const PxVec3& point) +{ + const PxTransform globalPose = body.getGlobalPose(); + const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p); + const PxVec3 rpoint = globalPose.transform(point) - centerOfMass; + + return getVelocityAtPosInternal(body, rpoint); +} + +PxVec3 PxRigidBodyExt::getVelocityAtOffset(const PxRigidBody& body, const PxVec3& point) +{ + const PxTransform globalPose = body.getGlobalPose(); + const PxVec3 centerOfMass = globalPose.rotate(body.getCMassLocalPose().p); + const PxVec3 rpoint = point - centerOfMass; + + return getVelocityAtPosInternal(body, rpoint); +} + +void PxRigidBodyExt::computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale, + const PxReal invInertiaScale, PxVec3& linearVelocityChange, PxVec3& angularVelocityChange) +{ + const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p); + const PxReal invMass = body.getInvMass() * invMassScale; + const PxVec3 invInertiaMS = body.getMassSpaceInvInertiaTensor() * invInertiaScale; + + PxMat33 invInertia; + transformInertiaTensor(invInertiaMS, PxMat33(globalPose.q), invInertia); + linearVelocityChange = impulse * invMass; + const PxVec3 rXI = (point - centerOfMass).cross(impulse); + angularVelocityChange = invInertia * rXI; +} + +void PxRigidBodyExt::computeLinearAngularImpulse(const PxRigidBody& body, const PxTransform& globalPose, const PxVec3& point, const PxVec3& impulse, const PxReal invMassScale, + const PxReal invInertiaScale, PxVec3& linearImpulse, PxVec3& angularImpulse) +{ + const PxVec3 centerOfMass = globalPose.transform(body.getCMassLocalPose().p); + linearImpulse = impulse * invMassScale; + angularImpulse = (point - centerOfMass).cross(impulse) * invInertiaScale; +} + + + +//================================================================================= +// Single closest hit compound sweep +bool PxRigidBodyExt::linearSweepSingle( + PxRigidBody& body, PxScene& scene, const PxVec3& unitDir, const PxReal distance, + PxHitFlags outputFlags, PxSweepHit& closestHit, PxU32& shapeIndex, + const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache, const PxReal inflation) +{ + shapeIndex = 0xFFFFffff; + PxReal closestDist = distance; + PxU32 nbShapes = body.getNbShapes(); + for(PxU32 i=0; i < nbShapes; i++) + { + PxShape* shape = NULL; + body.getShapes(&shape, 1, i); + PX_ASSERT(shape != NULL); + PxTransform pose = PxShapeExt::getGlobalPose(*shape, body); + PxQueryFilterData fd; + fd.flags = filterData.flags; + PxU32 or4 = (filterData.data.word0 | filterData.data.word1 | filterData.data.word2 | filterData.data.word3); + fd.data = or4 ? filterData.data : shape->getQueryFilterData(); + PxGeometryHolder anyGeom = shape->getGeometry(); + + PxSweepBuffer subHit; // touching hits are not allowed to be returned from the filters + scene.sweep(anyGeom.any(), pose, unitDir, distance, subHit, outputFlags, fd, filterCall, cache, inflation); + if (subHit.hasBlock && subHit.block.distance < closestDist) + { + closestDist = subHit.block.distance; + closestHit = subHit.block; + shapeIndex = i; + } + } + + return (shapeIndex != 0xFFFFffff); +} + +//================================================================================= +// Multiple hits compound sweep +// AP: we might be able to improve the return results API but no time for it in 3.3 +PxU32 PxRigidBodyExt::linearSweepMultiple( + PxRigidBody& body, PxScene& scene, const PxVec3& unitDir, const PxReal distance, PxHitFlags outputFlags, + PxSweepHit* hitBuffer, PxU32* hitShapeIndices, PxU32 hitBufferSize, PxSweepHit& block, PxI32& blockingHitShapeIndex, + bool& overflow, const PxQueryFilterData& filterData, PxQueryFilterCallback* filterCall, + const PxQueryCache* cache, const PxReal inflation) +{ + overflow = false; + blockingHitShapeIndex = -1; + + for (PxU32 i = 0; i < hitBufferSize; i++) + hitShapeIndices[i] = 0xFFFFffff; + + PxI32 sumNbResults = 0; + + PxU32 nbShapes = body.getNbShapes(); + PxF32 shrunkMaxDistance = distance; + for(PxU32 i=0; i < nbShapes; i++) + { + PxShape* shape = NULL; + body.getShapes(&shape, 1, i); + PX_ASSERT(shape != NULL); + PxTransform pose = PxShapeExt::getGlobalPose(*shape, body); + PxQueryFilterData fd; + fd.flags = filterData.flags; + PxU32 or4 = (filterData.data.word0 | filterData.data.word1 | filterData.data.word2 | filterData.data.word3); + fd.data = or4 ? filterData.data : shape->getQueryFilterData(); + PxGeometryHolder anyGeom = shape->getGeometry(); + + PxU32 bufSizeLeft = hitBufferSize-sumNbResults; + PxSweepHit extraHit; + PxSweepBuffer buffer(bufSizeLeft == 0 ? &extraHit : hitBuffer+sumNbResults, bufSizeLeft == 0 ? 1 : hitBufferSize-sumNbResults); + scene.sweep(anyGeom.any(), pose, unitDir, shrunkMaxDistance, buffer, outputFlags, fd, filterCall, cache, inflation); + + // Check and abort on overflow. Assume overflow if result count is bufSize. + PxU32 nbNewResults = buffer.getNbTouches(); + overflow |= (nbNewResults >= bufSizeLeft); + if (bufSizeLeft == 0) // this is for when we used the extraHit buffer + nbNewResults = 0; + + // set hitShapeIndices for each new non-blocking hit + for (PxU32 j = 0; j < nbNewResults; j++) + if (sumNbResults + PxU32(j) < hitBufferSize) + hitShapeIndices[sumNbResults+j] = i; + + if (buffer.hasBlock) // there's a blocking hit in the most recent sweepMultiple results + { + // overwrite the return result blocking hit with the new blocking hit if under + if (blockingHitShapeIndex == -1 || buffer.block.distance < block.distance) + { + blockingHitShapeIndex = PxI32(i); + block = buffer.block; + } + + // Remove all the old touching hits below the new maxDist + // sumNbResults is not updated yet at this point + // and represents the count accumulated so far excluding the very last query + PxI32 nbNewResultsSigned = PxI32(nbNewResults); // need a signed version, see nbNewResultsSigned-- below + for (PxI32 j = sumNbResults-1; j >= 0; j--) // iterate over "old" hits (up to shapeIndex-1) + if (buffer.block.distance < hitBuffer[j].distance) + { + // overwrite with last "new" hit + PxI32 sourceIndex = PxI32(sumNbResults)+nbNewResultsSigned-1; PX_ASSERT(sourceIndex >= j); + hitBuffer[j] = hitBuffer[sourceIndex]; + hitShapeIndices[j] = hitShapeIndices[sourceIndex]; + nbNewResultsSigned--; // can get negative, that means we are shifting the last results array + } + + sumNbResults += nbNewResultsSigned; + } else // if there was no new blocking hit we don't need to do anything special, simply append all results to touch array + sumNbResults += nbNewResults; + + PX_ASSERT(sumNbResults >= 0 && sumNbResults <= PxI32(hitBufferSize)); + } + + return PxU32(sumNbResults); +} + +void PxRigidBodyExt::computeVelocityDeltaFromImpulse(const PxRigidBody& body, const PxVec3& impulsiveForce, const PxVec3& impulsiveTorque, PxVec3& deltaLinearVelocity, PxVec3& deltaAngularVelocity) +{ + { + const PxF32 recipMass = body.getInvMass(); + deltaLinearVelocity = impulsiveForce*recipMass; + } + + { + const PxTransform globalPose = body.getGlobalPose(); + const PxTransform cmLocalPose = body.getCMassLocalPose(); + const PxTransform body2World = globalPose*cmLocalPose; + PxMat33 M(body2World.q); + + const PxVec3 recipInertiaBodySpace = body.getMassSpaceInvInertiaTensor(); + + PxMat33 recipInertiaWorldSpace; + const float axx = recipInertiaBodySpace.x*M(0,0), axy = recipInertiaBodySpace.x*M(1,0), axz = recipInertiaBodySpace.x*M(2,0); + const float byx = recipInertiaBodySpace.y*M(0,1), byy = recipInertiaBodySpace.y*M(1,1), byz = recipInertiaBodySpace.y*M(2,1); + const float czx = recipInertiaBodySpace.z*M(0,2), czy = recipInertiaBodySpace.z*M(1,2), czz = recipInertiaBodySpace.z*M(2,2); + recipInertiaWorldSpace(0,0) = axx*M(0,0) + byx*M(0,1) + czx*M(0,2); + recipInertiaWorldSpace(1,1) = axy*M(1,0) + byy*M(1,1) + czy*M(1,2); + recipInertiaWorldSpace(2,2) = axz*M(2,0) + byz*M(2,1) + czz*M(2,2); + recipInertiaWorldSpace(0,1) = recipInertiaWorldSpace(1,0) = axx*M(1,0) + byx*M(1,1) + czx*M(1,2); + recipInertiaWorldSpace(0,2) = recipInertiaWorldSpace(2,0) = axx*M(2,0) + byx*M(2,1) + czx*M(2,2); + recipInertiaWorldSpace(1,2) = recipInertiaWorldSpace(2,1) = axy*M(2,0) + byy*M(2,1) + czy*M(2,2); + + deltaAngularVelocity = recipInertiaWorldSpace*(impulsiveTorque); + } +} + + diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp new file mode 100644 index 000000000..577f2ef34 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp @@ -0,0 +1,188 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxSceneQueryExt.h" + +using namespace physx; + +bool PxSceneQueryExt::raycastAny( const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryHit& hit, const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache) +{ + PxSceneQueryFilterData fdAny = filterData; + fdAny.flags |= PxQueryFlag::eANY_HIT; + PxRaycastBuffer buf; + scene.raycast(origin, unitDir, distance, buf, PxHitFlags(), fdAny, filterCall, cache); + hit = buf.block; + return buf.hasBlock; +} + +bool PxSceneQueryExt::raycastSingle(const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, PxRaycastHit& hit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache) +{ + PxRaycastBuffer buf; + PxQueryFilterData fd1 = filterData; + scene.raycast(origin, unitDir, distance, buf, outputFlags, fd1, filterCall, cache); + hit = buf.block; + return buf.hasBlock; +} + +PxI32 PxSceneQueryExt::raycastMultiple( const PxScene& scene, + const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, + PxRaycastHit* hitBuffer, PxU32 hitBufferSize, bool& blockingHit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache) +{ + PxRaycastBuffer buf(hitBuffer, hitBufferSize); + PxQueryFilterData fd1 = filterData; + scene.raycast(origin, unitDir, distance, buf, outputFlags, fd1, filterCall, cache); + blockingHit = buf.hasBlock; + if(blockingHit) + { + if(buf.nbTouches < hitBufferSize) + { + hitBuffer[buf.nbTouches] = buf.block; + return PxI32(buf.nbTouches+1); + } + else // overflow, drop the last touch + { + hitBuffer[hitBufferSize-1] = buf.block; + return -1; + } + } else + // no block + return PxI32(buf.nbTouches); +} + +bool PxSceneQueryExt::sweepAny( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags queryFlags, + PxSceneQueryHit& hit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, + const PxSceneQueryCache* cache, + PxReal inflation) +{ + PxSceneQueryFilterData fdAny = filterData; + fdAny.flags |= PxQueryFlag::eANY_HIT; + PxSweepBuffer buf; + scene.sweep(geometry, pose, unitDir, distance, buf, queryFlags, fdAny, filterCall, cache, inflation); + hit = buf.block; + return buf.hasBlock; +} + +bool PxSceneQueryExt::sweepSingle( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, + PxSweepHit& hit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, + const PxSceneQueryCache* cache, + PxReal inflation) +{ + PxSweepBuffer buf; + PxQueryFilterData fd1 = filterData; + scene.sweep(geometry, pose, unitDir, distance, buf, outputFlags, fd1, filterCall, cache, inflation); + hit = buf.block; + return buf.hasBlock; +} + +PxI32 PxSceneQueryExt::sweepMultiple( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, const PxVec3& unitDir, const PxReal distance, + PxSceneQueryFlags outputFlags, PxSweepHit* hitBuffer, PxU32 hitBufferSize, bool& blockingHit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall, const PxSceneQueryCache* cache, + PxReal inflation) +{ + PxQueryFilterData fd1 = filterData; + PxSweepBuffer buf(hitBuffer, hitBufferSize); + scene.sweep(geometry, pose, unitDir, distance, buf, outputFlags, fd1, filterCall, cache, inflation); + blockingHit = buf.hasBlock; + if(blockingHit) + { + if(buf.nbTouches < hitBufferSize) + { + hitBuffer[buf.nbTouches] = buf.block; + return PxI32(buf.nbTouches+1); + } + else // overflow, drop the last touch + { + hitBuffer[hitBufferSize-1] = buf.block; + return -1; + } + } else + // no block + return PxI32(buf.nbTouches); +} + +PxI32 PxSceneQueryExt::overlapMultiple( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, + PxOverlapHit* hitBuffer, PxU32 hitBufferSize, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall) +{ + PxQueryFilterData fd1 = filterData; + fd1.flags |= PxQueryFlag::eNO_BLOCK; + PxOverlapBuffer buf(hitBuffer, hitBufferSize); + scene.overlap(geometry, pose, buf, fd1, filterCall); + if(buf.hasBlock) + { + if(buf.nbTouches < hitBufferSize) + { + hitBuffer[buf.nbTouches] = buf.block; + return PxI32(buf.nbTouches+1); + } + else // overflow, drop the last touch + { + hitBuffer[hitBufferSize-1] = buf.block; + return -1; + } + } else + // no block + return PxI32(buf.nbTouches); +} + +bool PxSceneQueryExt::overlapAny( const PxScene& scene, + const PxGeometry& geometry, const PxTransform& pose, + PxOverlapHit& hit, + const PxSceneQueryFilterData& filterData, + PxSceneQueryFilterCallback* filterCall) +{ + PxSceneQueryFilterData fdAny = filterData; + fdAny.flags |= (PxQueryFlag::eANY_HIT | PxQueryFlag::eNO_BLOCK); + PxOverlapBuffer buf; + scene.overlap(geometry, pose, buf, fdAny, filterCall); + hit = buf.block; + return buf.hasBlock; +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSerialization.h b/src/PhysX/physx/source/physxextensions/src/ExtSerialization.h new file mode 100644 index 000000000..387566bf6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSerialization.h @@ -0,0 +1,44 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef EXT_SERIALIZATION_H +#define EXT_SERIALIZATION_H + +namespace physx +{ +namespace Ext +{ + void RegisterExtensionsSerializers(PxSerializationRegistry& sr); + void UnregisterExtensionsSerializers(PxSerializationRegistry& sr); + void GetExtensionsBinaryMetaData(PxOutputStream& stream); +} +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h b/src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h new file mode 100644 index 000000000..9728df4d2 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h @@ -0,0 +1,156 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_NP_SHARED_QUEUE_ENTRY_POOL_H +#define PX_PHYSICS_EXTENSIONS_NP_SHARED_QUEUE_ENTRY_POOL_H + +#include "CmPhysXCommon.h" +#include "PsAllocator.h" +#include "PsArray.h" +#include "PsSList.h" + +namespace physx +{ +namespace Ext +{ + class SharedQueueEntry : public Ps::SListEntry + { + public: + SharedQueueEntry(void* objectRef) : mObjectRef(objectRef), mPooledEntry(false) {} + SharedQueueEntry() : mObjectRef(NULL), mPooledEntry(true) {} + + public: + void* mObjectRef; + bool mPooledEntry; // True if the entry was preallocated in a pool + }; + +#if PX_VC +#pragma warning(push) +#pragma warning(disable:4324) // Padding was added at the end of a structure because of a __declspec(align) value. +#endif // Because of the SList member I assume*/ + + template::Type > + class SharedQueueEntryPool : private Alloc + { + public: + SharedQueueEntryPool(PxU32 poolSize, const Alloc& alloc = Alloc(PX_DEBUG_EXP("SharedQueueEntryPool"))); + ~SharedQueueEntryPool(); + + SharedQueueEntry* getEntry(void* objectRef); + void putEntry(SharedQueueEntry& entry); + + private: + SharedQueueEntry* mTaskEntryPool; + Ps::SList mTaskEntryPtrPool; + }; + +#if PX_VC +#pragma warning(pop) +#endif + +} // namespace Ext + + +template +Ext::SharedQueueEntryPool::SharedQueueEntryPool(PxU32 poolSize, const Alloc& alloc) + : Alloc(alloc) +{ + Ps::AlignedAllocator alignedAlloc("SharedQueueEntryPool"); + + mTaskEntryPool = poolSize ? reinterpret_cast(alignedAlloc.allocate(sizeof(SharedQueueEntry) * poolSize, __FILE__, __LINE__)) : NULL; + + if (mTaskEntryPool) + { + for(PxU32 i=0; i < poolSize; i++) + { + PX_ASSERT((size_t(&mTaskEntryPool[i]) & (PX_SLIST_ALIGNMENT-1)) == 0); // The SList entry must be aligned according to PX_SLIST_ALIGNMENT + + PX_PLACEMENT_NEW(&mTaskEntryPool[i], SharedQueueEntry)(); + PX_ASSERT(mTaskEntryPool[i].mPooledEntry == true); + mTaskEntryPtrPool.push(mTaskEntryPool[i]); + } + } +} + + +template +Ext::SharedQueueEntryPool::~SharedQueueEntryPool() +{ + if (mTaskEntryPool) + { + Ps::AlignedAllocator alignedAlloc("SharedQueueEntryPool"); + alignedAlloc.deallocate(mTaskEntryPool); + } +} + + +template +Ext::SharedQueueEntry* Ext::SharedQueueEntryPool::getEntry(void* objectRef) +{ + SharedQueueEntry* e = static_cast(mTaskEntryPtrPool.pop()); + if (e) + { + PX_ASSERT(e->mPooledEntry == true); + e->mObjectRef = objectRef; + return e; + } + else + { + Ps::AlignedAllocator alignedAlloc; + e = reinterpret_cast(alignedAlloc.allocate(sizeof(SharedQueueEntry), __FILE__, __LINE__)); + if (e) + { + PX_PLACEMENT_NEW(e, SharedQueueEntry)(objectRef); + PX_ASSERT(e->mPooledEntry == false); + } + + return e; + } +} + + +template +void Ext::SharedQueueEntryPool::putEntry(Ext::SharedQueueEntry& entry) +{ + if (entry.mPooledEntry) + { + entry.mObjectRef = NULL; + mTaskEntryPtrPool.push(entry); + } + else + { + Ps::AlignedAllocator alignedAlloc; + alignedAlloc.deallocate(&entry); + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp b/src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp new file mode 100644 index 000000000..35f899e21 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp @@ -0,0 +1,384 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMathUtils.h" +#include "foundation/PxQuat.h" +#include "CmPhysXCommon.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "PsInlineArray.h" +#include "PxSimpleFactory.h" +#include "PxRigidStatic.h" +#include "PxSphereGeometry.h" +#include "PxBoxGeometry.h" +#include "PxCapsuleGeometry.h" +#include "PxConvexMeshGeometry.h" +#include "PxPlaneGeometry.h" +#include "PxRigidBodyExt.h" +#include "PxRigidStatic.h" +#include "PxScene.h" +#include "PxShape.h" +#include "PxRigidDynamic.h" +#include "CmPhysXCommon.h" +#include "PxPhysics.h" + +using namespace physx; +using namespace shdfnd; + +namespace +{ + +bool isDynamicGeometry(PxGeometryType::Enum type) +{ + return type == PxGeometryType::eBOX + || type == PxGeometryType::eSPHERE + || type == PxGeometryType::eCAPSULE + || type == PxGeometryType::eCONVEXMESH; +} +} + +namespace physx +{ +PxRigidDynamic* PxCreateDynamic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape, + PxReal density) +{ + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateDynamic: transform is not valid."); + + PxRigidDynamic* actor = sdk.createRigidDynamic(transform); + if(actor) + { + actor->attachShape(shape); + PxRigidBodyExt::updateMassAndInertia(*actor, density); + } + return actor; +} + +PxRigidDynamic* PxCreateDynamic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + PxReal density, + const PxTransform& shapeOffset) +{ + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateDynamic: transform is not valid."); + PX_CHECK_AND_RETURN_NULL(shapeOffset.isValid(), "PxCreateDynamic: shapeOffset is not valid."); + + if(!isDynamicGeometry(geometry.getType()) || density <= 0.0f) + return NULL; + + PxShape* shape = sdk.createShape(geometry, material, true); + if(!shape) + return NULL; + + shape->setLocalPose(shapeOffset); + + PxRigidDynamic* body = shape ? PxCreateDynamic(sdk, transform, *shape, density) : NULL; + shape->release(); + return body; +} + + + +PxRigidDynamic* PxCreateKinematic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape, + PxReal density) +{ + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateKinematic: transform is not valid."); + + bool isDynGeom = isDynamicGeometry(shape.getGeometryType()); + if(isDynGeom && density <= 0.0f) + return NULL; + + PxRigidDynamic* actor = sdk.createRigidDynamic(transform); + if(actor) + { + actor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true); + if(!isDynGeom) + shape.setFlag(PxShapeFlag::eSIMULATION_SHAPE, false); + + actor->attachShape(shape); + + if(isDynGeom) + PxRigidBodyExt::updateMassAndInertia(*actor, density); + else + { + actor->setMass(1.f); + actor->setMassSpaceInertiaTensor(PxVec3(1.f,1.f,1.f)); + } + } + + return actor; +} + + +PxRigidDynamic* PxCreateKinematic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + PxReal density, + const PxTransform& shapeOffset) +{ + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateKinematic: transform is not valid."); + PX_CHECK_AND_RETURN_NULL(shapeOffset.isValid(), "PxCreateKinematic: shapeOffset is not valid."); + + bool isDynGeom = isDynamicGeometry(geometry.getType()); + if(isDynGeom && density <= 0.0f) + return NULL; + + PxShape* shape = sdk.createShape(geometry, material, true); + if(!shape) + return NULL; + + shape->setLocalPose(shapeOffset); + + PxRigidDynamic* body = PxCreateKinematic(sdk, transform, *shape, density); + shape->release(); + return body; +} + + + + +PxRigidStatic* PxCreateStatic(PxPhysics& sdk, + const PxTransform& transform, + PxShape& shape) +{ + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateStatic: transform is not valid."); + + PxRigidStatic* s = sdk.createRigidStatic(transform); + if(s) + s->attachShape(shape); + return s; +} + +PxRigidStatic* PxCreateStatic(PxPhysics& sdk, + const PxTransform& transform, + const PxGeometry& geometry, + PxMaterial& material, + const PxTransform& shapeOffset) +{ + + PX_CHECK_AND_RETURN_NULL(transform.isValid(), "PxCreateStatic: transform is not valid."); + PX_CHECK_AND_RETURN_NULL(shapeOffset.isValid(), "PxCreateStatic: shapeOffset is not valid."); + + PxShape* shape = sdk.createShape(geometry, material, true); + if(!shape) + return NULL; + + shape->setLocalPose(shapeOffset); + + PxRigidStatic* s = PxCreateStatic(sdk, transform, *shape); + shape->release(); + return s; +} + + + + +PxRigidStatic* PxCreatePlane(PxPhysics& sdk, + const PxPlane& plane, + PxMaterial& material) +{ + PX_CHECK_AND_RETURN_NULL(plane.n.isFinite(), "PxCreatePlane: plane normal is not valid."); + + if (!plane.n.isNormalized()) + return NULL; + + return PxCreateStatic(sdk, PxTransformFromPlaneEquation(plane), PxPlaneGeometry(), material); +} + + +PxShape* PxCloneShape(PxPhysics &physics, const PxShape& from, bool isExclusive) +{ + Ps::InlineArray materials; + PxU16 materialCount = from.getNbMaterials(); + materials.resize(materialCount); + from.getMaterials(materials.begin(), materialCount); + + PxShape* to = physics.createShape(from.getGeometry().any(), materials.begin(), materialCount, isExclusive, from.getFlags()); + + to->setLocalPose(from.getLocalPose()); + to->setContactOffset(from.getContactOffset()); + to->setRestOffset(from.getRestOffset()); + to->setSimulationFilterData(from.getSimulationFilterData()); + to->setQueryFilterData(from.getQueryFilterData()); + return to; +} + + +namespace +{ + void copyStaticProperties(PxPhysics& physics, PxRigidActor& to, const PxRigidActor& from) + { + Ps::InlineArray shapes; + shapes.resize(from.getNbShapes()); + + PxU32 shapeCount = from.getNbShapes(); + from.getShapes(shapes.begin(), shapeCount); + + for(PxU32 i = 0; i < shapeCount; i++) + { + PxShape* s = shapes[i]; + if(!s->isExclusive()) + to.attachShape(*s); + else + { + PxShape* newShape = physx::PxCloneShape(physics, *s, true); + to.attachShape(*newShape); + newShape->release(); + } + } + + to.setActorFlags(from.getActorFlags()); + to.setOwnerClient(from.getOwnerClient()); + to.setDominanceGroup(from.getDominanceGroup()); + } +} + +PxRigidStatic* PxCloneStatic(PxPhysics& physicsSDK, + const PxTransform& transform, + const PxRigidActor& from) +{ + PxRigidStatic* to = physicsSDK.createRigidStatic(transform); + if(!to) + return NULL; + + copyStaticProperties(physicsSDK, *to, from); + + return to; +} + +PxRigidDynamic* PxCloneDynamic(PxPhysics& physicsSDK, + const PxTransform& transform, + const PxRigidDynamic& from) +{ + PxRigidDynamic* to = physicsSDK.createRigidDynamic(transform); + if(!to) + return NULL; + + copyStaticProperties(physicsSDK, *to, from); + + + to->setRigidBodyFlags(from.getRigidBodyFlags()); + + to->setMass(from.getMass()); + to->setMassSpaceInertiaTensor(from.getMassSpaceInertiaTensor()); + to->setCMassLocalPose(from.getCMassLocalPose()); + + to->setLinearVelocity(from.getLinearVelocity()); + to->setAngularVelocity(from.getAngularVelocity()); + + to->setLinearDamping(from.getAngularDamping()); + to->setAngularDamping(from.getAngularDamping()); + + PxU32 posIters, velIters; + from.getSolverIterationCounts(posIters, velIters); + to->setSolverIterationCounts(posIters, velIters); + + to->setMaxAngularVelocity(from.getMaxAngularVelocity()); + to->setMaxDepenetrationVelocity(from.getMaxDepenetrationVelocity()); + to->setSleepThreshold(from.getSleepThreshold()); + to->setStabilizationThreshold(from.getStabilizationThreshold()); + to->setMinCCDAdvanceCoefficient(from.getMinCCDAdvanceCoefficient()); + to->setContactReportThreshold(from.getContactReportThreshold()); + to->setMaxContactImpulse(from.getMaxContactImpulse()); + + return to; +} + +namespace +{ + PxTransform scalePosition(const PxTransform& t, PxReal scale) + { + return PxTransform(t.p*scale, t.q); + } +} + +void PxScaleRigidActor(PxRigidActor& actor, PxReal scale, bool scaleMassProps) +{ + PX_CHECK_AND_RETURN(scale > 0, + "PxScaleRigidActor requires that the scale parameter is greater than zero"); + + Ps::InlineArray shapes; + shapes.resize(actor.getNbShapes()); + actor.getShapes(shapes.begin(), shapes.size()); + + for(PxU32 i=0;isetLocalPose(scalePosition(shapes[i]->getLocalPose(), scale)); + PxGeometryHolder h = shapes[i]->getGeometry(); + + switch(h.getType()) + { + case PxGeometryType::eSPHERE: + h.sphere().radius *= scale; + break; + case PxGeometryType::ePLANE: + break; + case PxGeometryType::eCAPSULE: + h.capsule().halfHeight *= scale; + h.capsule().radius *= scale; + break; + case PxGeometryType::eBOX: + h.box().halfExtents *= scale; + break; + case PxGeometryType::eCONVEXMESH: + h.convexMesh().scale.scale *= scale; + break; + case PxGeometryType::eTRIANGLEMESH: + h.triangleMesh().scale.scale *= scale; + break; + case PxGeometryType::eHEIGHTFIELD: + h.heightField().heightScale *= scale; + h.heightField().rowScale *= scale; + h.heightField().columnScale *= scale; + break; + case PxGeometryType::eINVALID: + case PxGeometryType::eGEOMETRY_COUNT: + PX_ASSERT(0); + } + shapes[i]->setGeometry(h.any()); + } + + if(!scaleMassProps) + return; + + PxRigidDynamic* dynamic = (&actor)->is(); + if(!dynamic) + return; + + PxReal scale3 = scale*scale*scale; + dynamic->setMass(dynamic->getMass()*scale3); + dynamic->setMassSpaceInertiaTensor(dynamic->getMassSpaceInertiaTensor()*scale3*scale*scale); + dynamic->setCMassLocalPose(scalePosition(dynamic->getCMassLocalPose(), scale)); +} +} diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp b/src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp new file mode 100644 index 000000000..fe2034ede --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp @@ -0,0 +1,145 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "foundation/PxMemory.h" +#include "PxSmoothNormals.h" +#include "PsMathUtils.h" +#include "PsUserAllocated.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" + +using namespace physx; + +static PxReal computeAngle(const PxVec3* verts, const PxU32* refs, PxU32 vref) +{ + PxU32 e0=0,e2=0; + if(vref==refs[0]) + { + e0 = 2; + e2 = 1; + } + else if(vref==refs[1]) + { + e0 = 2; + e2 = 0; + } + else if(vref==refs[2]) + { + e0 = 0; + e2 = 1; + } + else + { + PX_ASSERT(0); + } + const PxVec3 edge0 = verts[refs[e0]] - verts[vref]; + const PxVec3 edge1 = verts[refs[e2]] - verts[vref]; + + return Ps::angle(edge0, edge1); +} + +bool PxBuildSmoothNormals(PxU32 nbTris, PxU32 nbVerts, const PxVec3* verts, const PxU32* dFaces, const PxU16* wFaces, PxVec3* normals, bool flip) +{ + if(!verts || !normals || !nbTris || !nbVerts) + return false; + + // Get correct destination buffers + // - if available, write directly to user-provided buffers + // - else get some ram and keep track of it + PxVec3* FNormals = reinterpret_cast(PX_ALLOC_TEMP(sizeof(PxVec3)*nbTris, "PxVec3")); + if(!FNormals) return false; + + // Compute face normals + const PxU32 c = PxU32(flip!=0); + for(PxU32 i=0; i(PX_ALLOC_TEMP(sizeof(PxVec3)*nbVerts, "PxVec3")); + PxMemSet(TmpNormals, 0, nbVerts*sizeof(PxVec3)); + for(PxU32 i=0;iis()) || (actor1 && actor1->is()), "PxSphericalJointCreate: at least one actor must be dynamic"); + + SphericalJoint* j; + PX_NEW_SERIALIZED(j, SphericalJoint)(physics.getTolerancesScale(), actor0, localFrame0, actor1, localFrame1); + + if(j->attach(physics, actor0, actor1)) + return j; + + PX_DELETE(j); + return NULL; +} + +void SphericalJoint::setProjectionLinearTolerance(PxReal tolerance) +{ + PX_CHECK_AND_RETURN(PxIsFinite(tolerance) && tolerance >=0, "PxSphericalJoint::setProjectionLinearTolerance: invalid parameter"); + data().projectionLinearTolerance = tolerance; + markDirty(); +} + +PxReal SphericalJoint::getProjectionLinearTolerance() const +{ + return data().projectionLinearTolerance; +} + +void SphericalJoint::setLimitCone(const PxJointLimitCone &limit) +{ + PX_CHECK_AND_RETURN(limit.isValid(), "PxSphericalJoint::setLimit: invalid parameter"); + data().limit = limit; + markDirty(); +} + +PxJointLimitCone SphericalJoint::getLimitCone() const +{ + return data().limit; +} + +PxSphericalJointFlags SphericalJoint::getSphericalJointFlags(void) const +{ + return data().jointFlags; +} + +void SphericalJoint::setSphericalJointFlags(PxSphericalJointFlags flags) +{ + data().jointFlags = flags; +} + +void SphericalJoint::setSphericalJointFlag(PxSphericalJointFlag::Enum flag, bool value) +{ + if(value) + data().jointFlags |= flag; + else + data().jointFlags &= ~flag; + markDirty(); +} + +PxReal SphericalJoint::getSwingYAngle() const +{ + return getSwingYAngle_Internal(); +} + +PxReal SphericalJoint::getSwingZAngle() const +{ + return getSwingZAngle_Internal(); +} + +bool SphericalJoint::attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1) +{ + mPxConstraint = physics.createConstraint(actor0, actor1, *this, sShaders, sizeof(SphericalJointData)); + return mPxConstraint!=NULL; +} + +void SphericalJoint::exportExtraData(PxSerializationContext& stream) +{ + if(mData) + { + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mData, sizeof(SphericalJointData)); + } + stream.writeName(mName); +} + +void SphericalJoint::importExtraData(PxDeserializationContext& context) +{ + if(mData) + mData = context.readExtraData(); + context.readName(mName); +} + +void SphericalJoint::resolveReferences(PxDeserializationContext& context) +{ + setPxConstraint(resolveConstraintPtr(context, getPxConstraint(), getConnector(), sShaders)); +} + +SphericalJoint* SphericalJoint::createObject(PxU8*& address, PxDeserializationContext& context) +{ + SphericalJoint* obj = new (address) SphericalJoint(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(SphericalJoint); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +// global function to share the joint shaders with API capture +const PxConstraintShaderTable* Ext::GetSphericalJointShaderTable() +{ + return &SphericalJoint::getConstraintShaderTable(); +} + +//~PX_SERIALIZATION + +static void SphericalJointProject(const void* constantBlock, PxTransform& bodyAToWorld, PxTransform& bodyBToWorld, bool projectToA) +{ + const SphericalJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w, cB2cA, projected; + joint::computeDerived(data, bodyAToWorld, bodyBToWorld, cA2w, cB2w, cB2cA); + + bool linearTrunc; + projected.p = joint::truncateLinear(cB2cA.p, data.projectionLinearTolerance, linearTrunc); + + if(linearTrunc) + { + projected.q = cB2cA.q; + joint::projectTransforms(bodyAToWorld, bodyBToWorld, cA2w, cB2w, projected, data, projectToA); + } +} + +static void SphericalJointVisualize(PxConstraintVisualizer& viz, const void* constantBlock, const PxTransform& body0Transform, const PxTransform& body1Transform, PxU32 flags) +{ + const SphericalJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::computeJointFrames(cA2w, cB2w, data, body0Transform, body1Transform); + if(flags & PxConstraintVisualizationFlag::eLOCAL_FRAMES) + viz.visualizeJointFrames(cA2w, cB2w); + + if((flags & PxConstraintVisualizationFlag::eLIMITS) && (data.jointFlags & PxSphericalJointFlag::eLIMIT_ENABLED)) + { + if(cA2w.q.dot(cB2w.q)<0.0f) + cB2w.q = -cB2w.q; + + const PxTransform cB2cA = cA2w.transformInv(cB2w); + PxQuat swing, twist; + Ps::separateSwingTwist(cB2cA.q,swing,twist); + + // PT: TODO: refactor with D6 joint code + const PxReal pad = data.limit.isSoft() ? 0.0f : data.limit.contactDistance; + const PxVec3 swingAngle(0.0f, computeSwingAngle(swing.y, swing.w), computeSwingAngle(swing.z, swing.w)); + Cm::ConeLimitHelperTanLess coneHelper(data.limit.yAngle, data.limit.zAngle, pad); + viz.visualizeLimitCone(cA2w, PxTan(data.limit.zAngle/4), PxTan(data.limit.yAngle/4), !coneHelper.contains(swingAngle)); + } +} + +static PxU32 SphericalJointSolverPrep(Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 /*maxConstraints*/, + PxConstraintInvMassScale& invMassScale, + const void* constantBlock, + const PxTransform& bA2w, + const PxTransform& bB2w, + bool /*useExtendedLimits*/, + PxVec3& cA2wOut, PxVec3& cB2wOut) +{ + const SphericalJointData& data = *reinterpret_cast(constantBlock); + + PxTransform cA2w, cB2w; + joint::ConstraintHelper ch(constraints, invMassScale, cA2w, cB2w, body0WorldOffset, data, bA2w, bB2w); + + if(cB2w.q.dot(cA2w.q)<0.0f) + cB2w.q = -cB2w.q; + + if(data.jointFlags & PxSphericalJointFlag::eLIMIT_ENABLED) + { + PxQuat swing, twist; + Ps::separateSwingTwist(cA2w.q.getConjugate() * cB2w.q, swing, twist); + PX_ASSERT(PxAbs(swing.x)<1e-6f); + + // PT: TODO: refactor with D6 joint code + PxVec3 axis; + PxReal error; + const PxReal pad = data.limit.isSoft() ? 0.0f : data.limit.contactDistance; + const Cm::ConeLimitHelperTanLess coneHelper(data.limit.yAngle, data.limit.zAngle, pad); + const bool active = coneHelper.getLimit(swing, axis, error); + if(active) + ch.angularLimit(cA2w.rotate(axis), error, data.limit); + } + + PxVec3 ra, rb; + ch.prepareLockedAxes(cA2w.q, cB2w.q, cA2w.transformInv(cB2w.p), 7, 0, ra, rb); + cA2wOut = ra + bA2w.p; + cB2wOut = rb + bB2w.p; + + return ch.getCount(); +} + +PxConstraintShaderTable Ext::SphericalJoint::sShaders = { SphericalJointSolverPrep, SphericalJointProject, SphericalJointVisualize, PxConstraintFlag::Enum(0) }; diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h new file mode 100644 index 000000000..81694b529 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h @@ -0,0 +1,129 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef NP_SPHERICALJOINTCONSTRAINT_H +#define NP_SPHERICALJOINTCONSTRAINT_H + +#include "ExtJoint.h" +#include "PxSphericalJoint.h" +#include "CmUtils.h" + +namespace physx +{ +struct PxSphericalJointGeneratedValues; +namespace Ext +{ + struct SphericalJointData: public JointData + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + PxJointLimitCone limit; + + PxReal projectionLinearTolerance; + + PxSphericalJointFlags jointFlags; + // forestall compiler complaints about not being able to generate a constructor + private: + SphericalJointData(const PxJointLimitCone &cone): + limit(cone) {} + }; + + typedef Joint SphericalJointT; + + class SphericalJoint : public SphericalJointT + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + SphericalJoint(PxBaseFlags baseFlags) : SphericalJointT(baseFlags) {} + virtual void exportExtraData(PxSerializationContext& context); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static SphericalJoint* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + + SphericalJoint(const PxTolerancesScale& /*scale*/, PxRigidActor* actor0, const PxTransform& localFrame0, PxRigidActor* actor1, const PxTransform& localFrame1) : + SphericalJointT(PxJointConcreteType::eSPHERICAL, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE, actor0, localFrame0, actor1, localFrame1, sizeof(SphericalJointData), "SphericalJointData") + { + SphericalJointData* data = static_cast(mData); + + data->projectionLinearTolerance = 1e10f; + data->limit = PxJointLimitCone(PxPi/2, PxPi/2); + data->jointFlags = PxSphericalJointFlags(); + } + + // PxSphericalJoint + virtual void setLimitCone(const PxJointLimitCone &limit); + virtual PxJointLimitCone getLimitCone() const; + virtual void setSphericalJointFlags(PxSphericalJointFlags flags); + virtual void setSphericalJointFlag(PxSphericalJointFlag::Enum flag, bool value); + virtual PxSphericalJointFlags getSphericalJointFlags(void) const; + virtual void setProjectionLinearTolerance(PxReal distance); + virtual PxReal getProjectionLinearTolerance() const; + virtual PxReal getSwingYAngle() const; + virtual PxReal getSwingZAngle() const; + //~PxSphericalJoint + + bool attach(PxPhysics &physics, PxRigidActor* actor0, PxRigidActor* actor1); + + static const PxConstraintShaderTable& getConstraintShaderTable() { return sShaders; } + + virtual PxConstraintSolverPrep getPrep() const { return sShaders.solverPrep; } + + private: + + static PxConstraintShaderTable sShaders; + + PX_FORCE_INLINE SphericalJointData& data() const + { + return *static_cast(mData); + } + }; + +} // namespace Ext + +namespace Ext +{ + // global function to share the joint shaders with API capture + extern "C" const PxConstraintShaderTable* GetSphericalJointShaderTable(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h b/src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h new file mode 100644 index 000000000..450382c73 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_EXTENSIONS_NP_TASK_QUEUE_HELPER_H +#define PX_PHYSICS_EXTENSIONS_NP_TASK_QUEUE_HELPER_H + +#include "task/PxTask.h" +#include "CmPhysXCommon.h" +#include "ExtSharedQueueEntryPool.h" + +namespace physx +{ + +#define EXT_TASK_QUEUE_ENTRY_POOL_SIZE 128 + +namespace Ext +{ + class TaskQueueHelper + { + public: + static PxBaseTask* fetchTask(Ps::SList& taskQueue, Ext::SharedQueueEntryPool<>& entryPool) + { + SharedQueueEntry* entry = static_cast(taskQueue.pop()); + if (entry) + { + PxBaseTask* task = reinterpret_cast(entry->mObjectRef); + entryPool.putEntry(*entry); + return task; + } + else + return NULL; + } + }; + +} // namespace Ext + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp new file mode 100644 index 000000000..6559d6346 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp @@ -0,0 +1,164 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxTriangleMeshExt.h" +#include "PxMeshQuery.h" +#include "PxGeometryQuery.h" +#include "PxTriangleMeshGeometry.h" +#include "PxHeightFieldGeometry.h" +#include "PxTriangleMesh.h" +#include "PsAllocator.h" + +using namespace physx; + +PxMeshOverlapUtil::PxMeshOverlapUtil() : mResultsMemory(mResults), mNbResults(0), mMaxNbResults(256) +{ +} + +PxMeshOverlapUtil::~PxMeshOverlapUtil() +{ + if(mResultsMemory != mResults) + PX_FREE(mResultsMemory); +} + +PxU32 PxMeshOverlapUtil::findOverlap(const PxGeometry& geom, const PxTransform& geomPose, const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose) +{ + bool overflow; + PxU32 nbTouchedTris = PxMeshQuery::findOverlapTriangleMesh(geom, geomPose, meshGeom, meshPose, mResultsMemory, mMaxNbResults, 0, overflow); + + if(overflow) + { + const PxU32 maxNbTris = meshGeom.triangleMesh->getNbTriangles(); + if(!maxNbTris) + { + mNbResults = 0; + return 0; + } + + if(mMaxNbResults(PX_ALLOC(sizeof(PxU32)*maxNbTris, "PxMeshOverlapUtil::findOverlap")); + mMaxNbResults = maxNbTris; + } + nbTouchedTris = PxMeshQuery::findOverlapTriangleMesh(geom, geomPose, meshGeom, meshPose, mResultsMemory, mMaxNbResults, 0, overflow); + PX_ASSERT(nbTouchedTris); + PX_ASSERT(!overflow); + } + mNbResults = nbTouchedTris; + return nbTouchedTris; +} + +PxU32 PxMeshOverlapUtil::findOverlap(const PxGeometry& geom, const PxTransform& geomPose, const PxHeightFieldGeometry& hfGeom, const PxTransform& hfPose) +{ + bool overflow = true; + PxU32 nbTouchedTris = 0; + do + { + nbTouchedTris = PxMeshQuery::findOverlapHeightField(geom, geomPose, hfGeom, hfPose, mResultsMemory, mMaxNbResults, 0, overflow); + if(overflow) + { + const PxU32 maxNbTris = mMaxNbResults * 2; + + if(mResultsMemory != mResults) + PX_FREE(mResultsMemory); + + mResultsMemory = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*maxNbTris, "PxMeshOverlapUtil::findOverlap")); + mMaxNbResults = maxNbTris; + } + }while(overflow); + + mNbResults = nbTouchedTris; + return nbTouchedTris; +} +namespace +{ +template +bool computeMeshPenetrationT(PxVec3& direction, + PxReal& depth, + const PxGeometry& geom, + const PxTransform& geomPose, + const MeshGeometry& meshGeom, + const PxTransform& meshPose, + PxU32 maxIter, + PxU32* nbIterOut) +{ + PxU32 nbIter = 0; + PxTransform pose = geomPose; + for (; nbIter < maxIter; nbIter++) + { + PxVec3 currentDir; + PxF32 currentDepth; + + if (!PxGeometryQuery::computePenetration(currentDir, currentDepth, geom, pose, meshGeom, meshPose)) + break; + + pose.p += currentDir * currentDepth; + } + + if(nbIterOut) + *nbIterOut = nbIter; + + PxVec3 diff = pose.p - geomPose.p; + depth = diff.magnitude(); + + if (depth>0) + direction = diff / depth; + + return nbIter!=0; +} +} + +bool physx::PxComputeTriangleMeshPenetration(PxVec3& direction, + PxReal& depth, + const PxGeometry& geom, + const PxTransform& geomPose, + const PxTriangleMeshGeometry& meshGeom, + const PxTransform& meshPose, + PxU32 maxIter, + PxU32* nbIter) +{ + return computeMeshPenetrationT(direction, depth, geom, geomPose, meshGeom, meshPose, maxIter, nbIter); +} + +bool physx::PxComputeHeightFieldPenetration(PxVec3& direction, + PxReal& depth, + const PxGeometry& geom, + const PxTransform& geomPose, + const PxHeightFieldGeometry& hfGeom, + const PxTransform& meshPose, + PxU32 maxIter, + PxU32* nbIter) +{ + return computeMeshPenetrationT(direction, depth, geom, geomPose, hfGeom, meshPose, maxIter, nbIter); +} + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp new file mode 100644 index 000000000..ecb87b4ca --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp @@ -0,0 +1,305 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsHash.h" +#include "PsHashMap.h" +#include "PsFoundation.h" +#include "CmIO.h" +#include "SnFile.h" +#include "PsString.h" +#include "extensions/PxSerialization.h" +#include "PxPhysics.h" +#include "PxPhysicsSerialization.h" +#include "SnSerializationContext.h" +#include "PxSerializer.h" +#include "serialization/SnSerializationRegistry.h" +#include "serialization/SnSerialUtils.h" +#include "CmCollection.h" +#include "SnConvX_Align.h" + +using namespace physx; +using namespace Sn; + +namespace +{ + PX_INLINE PxU8* alignPtr(PxU8* ptr, PxU32 alignment = PX_SERIAL_ALIGN) + { + if(!alignment) + return ptr; + + const PxU32 padding = getPadding(size_t(ptr), alignment); + PX_ASSERT(!getPadding(size_t(ptr + padding), alignment)); + return ptr + padding; + } + + PX_FORCE_INLINE PxU32 read32(PxU8*& address) + { + const PxU32 value = *reinterpret_cast(address); + address += sizeof(PxU32); + return value; + } + + bool readHeader(PxU8*& address, PxU32& version) + { + const PxU32 header = read32(address); + PX_UNUSED(header); + + version = read32(address); + + const PxU32 binaryVersion = read32(address); + PX_UNUSED(binaryVersion); + const PxU32 buildNumber = read32(address); + PX_UNUSED(buildNumber); + const PxU32 platformTag = read32(address); + PX_UNUSED(platformTag); + const PxU32 markedPadding = read32(address); + PX_UNUSED(markedPadding); + +#if PX_CHECKED + if (header != PX_MAKE_FOURCC('S','E','B','D')) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "Buffer contains data with wrong header indicating invalid binary data."); + return false; + } + + if (!checkCompatibility(version, binaryVersion)) + { + char buffer[512]; + getCompatibilityVersionsStr(buffer, 512); + + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "Buffer contains data version (%x-%d) is incompatible with this PhysX sdk.\n These versions would be compatible: %s", + version, binaryVersion, buffer); + return false; + } + + if (platformTag != getBinaryPlatformTag()) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "Buffer contains data with platform mismatch:\nExpected: %s \nActual: %s\n", + getBinaryPlatformName(getBinaryPlatformTag()), + getBinaryPlatformName(platformTag)); + return false; + } +#endif + return true; + } + + bool checkImportReferences(const ImportReference* importReferences, PxU32 nbImportReferences, const Cm::Collection* externalRefs) + { + if (!externalRefs) + { + if (nbImportReferences > 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxSerialization::createCollectionFromBinary: External references needed but no externalRefs collection specified."); + return false; + } + } + else + { + for (PxU32 i=0; ifind(id); + if (!referencedObject) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxSerialization::createCollectionFromBinary: External reference %" PX_PRIu64 " expected in externalRefs collection but not found.", id); + return false; + } + if (referencedObject->getConcreteType() != type) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxSerialization::createCollectionFromBinary: External reference %d type mismatch. Expected %d but found %d in externalRefs collection.", type, referencedObject->getConcreteType()); + return false; + } + } + } + return true; + } + +} + +PxCollection* PxSerialization::createCollectionFromBinary(void* memBlock, PxSerializationRegistry& sr, const PxCollection* pxExternalRefs) +{ +#if PX_CHECKED + if(size_t(memBlock) & (PX_SERIAL_FILE_ALIGN-1)) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Buffer must be 128-bytes aligned."); + return NULL; + } +#endif + PxU8* address = reinterpret_cast(memBlock); + const Cm::Collection* externalRefs = static_cast(pxExternalRefs); + + PxU32 version; + if (!readHeader(address, version)) + { + return NULL; + } + + ManifestEntry* manifestTable; + PxU32 nbObjectsInCollection; + PxU32 objectDataEndOffset; + + // read number of objects in collection + address = alignPtr(address); + nbObjectsInCollection = read32(address); + + // read manifest (PxU32 offset, PxConcreteType type) + { + address = alignPtr(address); + PxU32 nbManifestEntries = read32(address); + PX_ASSERT(*reinterpret_cast(address) == 0); //first offset is always 0 + manifestTable = (nbManifestEntries > 0) ? reinterpret_cast(address) : NULL; + address += nbManifestEntries*sizeof(ManifestEntry); + objectDataEndOffset = read32(address); + } + + ImportReference* importReferences; + PxU32 nbImportReferences; + // read import references + { + address = alignPtr(address); + nbImportReferences = read32(address); + importReferences = (nbImportReferences > 0) ? reinterpret_cast(address) : NULL; + address += nbImportReferences*sizeof(ImportReference); + } + + if (!checkImportReferences(importReferences, nbImportReferences, externalRefs)) + { + return NULL; + } + + ExportReference* exportReferences; + PxU32 nbExportReferences; + // read export references + { + address = alignPtr(address); + nbExportReferences = read32(address); + exportReferences = (nbExportReferences > 0) ? reinterpret_cast(address) : NULL; + address += nbExportReferences*sizeof(ExportReference); + } + + // read internal references arrays + PxU32 nbInternalPtrReferences = 0; + PxU32 nbInternalIdxReferences = 0; + InternalReferencePtr* internalPtrReferences = NULL; + InternalReferenceIdx* internalIdxReferences = NULL; + { + address = alignPtr(address); + + nbInternalPtrReferences = read32(address); + internalPtrReferences = (nbInternalPtrReferences > 0) ? reinterpret_cast(address) : NULL; + address += nbInternalPtrReferences*sizeof(InternalReferencePtr); + + nbInternalIdxReferences = read32(address); + internalIdxReferences = (nbInternalIdxReferences > 0) ? reinterpret_cast(address) : NULL; + address += nbInternalIdxReferences*sizeof(InternalReferenceIdx); + } + + // create internal references map + PxF32 loadFactor = 0.75f; + PxF32 _loadFactor = 1.0f / loadFactor; + PxU32 hashSize = PxU32((nbInternalPtrReferences + nbInternalIdxReferences + 1)*_loadFactor); + InternalRefMap internalReferencesMap(hashSize, loadFactor); + { + //create hash (we should load the hashes directly from memory) + for (PxU32 i=0;i(sr); + Cm::Collection* collection = static_cast(PxCreateCollection()); + PX_ASSERT(collection); + collection->mObjects.reserve(PxU32(nbObjectsInCollection*_loadFactor) + 1); + if(nbExportReferences > 0) + collection->mIds.reserve(PxU32(nbExportReferences*_loadFactor) + 1); + + PxU8* addressObjectData = alignPtr(address); + PxU8* addressExtraData = alignPtr(addressObjectData + objectDataEndOffset); + + DeserializationContext context(manifestTable, importReferences, addressObjectData, internalReferencesMap, externalRefs, addressExtraData, version); + + // iterate over memory containing PxBase objects, create the instances, resolve the addresses, import the external data, add to collection. + { + PxU32 nbObjects = nbObjectsInCollection; + + while(nbObjects--) + { + address = alignPtr(address); + context.alignExtraData(); + + // read PxBase header with type and get corresponding serializer. + PxBase* header = reinterpret_cast(address); + const PxType classType = header->getConcreteType(); + const PxSerializer* serializer = sn.getSerializer(classType); + PX_ASSERT(serializer); + + PxBase* instance = serializer->createObject(address, context); + if (!instance) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "Cannot create class instance for concrete type %d.", classType); + collection->release(); + return NULL; + } + + collection->internalAdd(instance); + } + } + + PX_ASSERT(nbObjectsInCollection == collection->internalGetNbObjects()); + + // update new collection with export references + { + PX_ASSERT(addressObjectData != NULL); + for (PxU32 i=0;i(addressObjectData + manifestTable[manifestIndex].offset); + collection->mIds.insertUnique(exportReferences[i].id, obj); + collection->mObjects[obj] = exportReferences[i].id; + } + } + + PxAddCollectionToPhysics(*collection); + return collection; +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp new file mode 100644 index 000000000..4c4d279d2 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp @@ -0,0 +1,402 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsHash.h" +#include "PsHashMap.h" +#include "CmIO.h" +#include "SnFile.h" +#include "PsString.h" +#include "PsIntrinsics.h" +#include "extensions/PxSerialization.h" +#include "SnSerializationContext.h" +#include "PxSerializer.h" +#include "serialization/SnSerialUtils.h" +#include "serialization/SnSerializationRegistry.h" +#include "SnConvX_Align.h" +#include "PxDefaultStreams.h" +#include "CmCollection.h" +#include "PxPhysicsVersion.h" +#include "PsUtilities.h" + +using namespace physx; +using namespace Cm; +using namespace Sn; + +//------------------------------------------------------------------------------------ +//// Binary Serialized PxCollection, format documentation +//------------------------------------------------------------------------------------ +// +// +//------------------------------------------------------------------------------------ +//// overview: +//// header information +//// manifest table +//// import references +//// export references +//// internal references +//// object data +//// extra data +//------------------------------------------------------------------------------------ +// +// +//------------------------------------------------------------------------------------ +//// header information: +//// header tag plus various version and platform information +//------------------------------------------------------------------------------------ +// header SEBD +// PX_PHYSICS_VERSION +// PX_BINARY_SERIAL_VERSION +// PX_BUILD_NUMBER (or 0 if not defined) +// platform tag +// markedPadding (on for PX_CHECKED) +// nbObjectsInCollection +// +// +//------------------------------------------------------------------------------------ +//// manifest table: +//// one entry per collected object +//// offsets relative to object data buffer +//------------------------------------------------------------------------------------ +// alignment +// PxU32 size +// (PxU32 offset, PxType type)*size +// PxU32 endOffset +// +// +//------------------------------------------------------------------------------------ +//// import references: +//// one entry per required reference to external collection +//------------------------------------------------------------------------------------ +// alignment +// PxU32 size +// (PxSerialObjectId id, PxType type)*size +// +// +//------------------------------------------------------------------------------------ +//// export references: +//// one entry per object in the collection with id +//// object indices point into the manifest table (objects in the same collection) +//------------------------------------------------------------------------------------ +// alignment +// PxU32 size +// (PxSerialObjectId id, SerialObjectIndex objIndex)*size +// +// +//------------------------------------------------------------------------------------ +//// internal references: +//// one entry per reference, kind pair +//// object indices point either into the manifest table or into the import references +//// depending on whether the entry references the same collection or the external one +//// one section for pointer type references and one for index type references. +//------------------------------------------------------------------------------------ +// alignment +// PxU32 sizePtrs; +// (size_t reference, PxU32 kind, SerialObjectIndex objIndex)*sizePtrs +// PxU32 sizeIdx; +// (PxU32 reference, PxU32 kind, SerialObjectIndex objIndex)*sizePtrs +// +// +//------------------------------------------------------------------------------------ +//// object data: +//// serialized PxBase derived class instances +//// each object size depends on specific class +//// offsets are stored in manifest table +//------------------------------------------------------------------------------------ +// alignment +// (PxConcreteType type, -----) +// alignment +// (PxConcreteType type, --------) +// alignment +// (PxConcreteType type, --) +// . +// . +// +// +// ----------------------------------------------------------------------------------- +//// extra data: +//// extra data memory block +//// serialized and deserialized by PxBase implementations +////---------------------------------------------------------------------------------- +// extra data +// +//------------------------------------------------------------------------------------ + +namespace +{ + + class LegacySerialStream : public PxSerializationContext + { + public: + LegacySerialStream(OutputStreamWriter& writer, + const PxCollection& collection, + bool exportNames) : mWriter(writer), mCollection(collection), mExportNames(exportNames) {} + void writeData(const void* buffer, PxU32 size) { mWriter.write(buffer, size); } + PxU32 getTotalStoredSize() { return mWriter.getStoredSize(); } + void alignData(PxU32 alignment) + { + if(!alignment) + return; + + PxI32 bytesToPad = PxI32(getPadding(getTotalStoredSize(), alignment)); + static const PxI32 BUFSIZE = 64; + char buf[BUFSIZE]; + PxMemSet(buf, 0, bytesToPad < BUFSIZE ? PxU32(bytesToPad) : PxU32(BUFSIZE)); + while(bytesToPad > 0) + { + writeData(buf, bytesToPad < BUFSIZE ? PxU32(bytesToPad) : PxU32(BUFSIZE)); + bytesToPad -= BUFSIZE; + } + PX_ASSERT(!getPadding(getTotalStoredSize(), alignment)); + } + + virtual void registerReference(PxBase&, PxU32, size_t) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Cannot register references during exportData, exportExtraData."); + } + + virtual const PxCollection& getCollection() const + { + return mCollection; + } + virtual void writeName(const char* name) + { + PxU32 len = name && mExportNames ? PxU32(strlen(name)) + 1 : 0; + writeData(&len, sizeof(len)); + if(len) writeData(name, len); + } + + private: + LegacySerialStream& operator=(const LegacySerialStream&); + OutputStreamWriter& mWriter; + const PxCollection& mCollection; + bool mExportNames; + }; + + void writeHeader(PxSerializationContext& stream, bool hasDeserializedAssets) + { + PX_UNUSED(hasDeserializedAssets); + struct Header + { + PxU32 header; + PxU32 version; + PxU32 binaryVersion; + PxU32 buildNumber; + PxU32 platformTag; + PxU32 markedPadding; + PxU32 materialOffset; + }; + + //serialized binary data. + const PxU32 header = PX_MAKE_FOURCC('S','E','B','D'); + stream.writeData(&header, sizeof(PxU32)); + + PxU32 version = PX_PHYSICS_VERSION; + stream.writeData(&version, sizeof(PxU32)); + + PxU32 binaryVersion = PX_BINARY_SERIAL_VERSION; + stream.writeData(&binaryVersion, sizeof(PxU32)); + + PxU32 buildNumber = 0; +#if defined(PX_BUILD_NUMBER) + buildNumber = PX_BUILD_NUMBER; +#endif + stream.writeData(&buildNumber, sizeof(PxU32)); + + PxU32 platformTag = getBinaryPlatformTag(); + stream.writeData(&platformTag, sizeof(PxU32)); + + PxU32 markedPadding = 0; +#if PX_CHECKED + if(!hasDeserializedAssets) + markedPadding = 1; +#endif + stream.writeData(&markedPadding, sizeof(PxU32)); + } +} + +bool PxSerialization::serializeCollectionToBinary(PxOutputStream& outputStream, PxCollection& pxCollection, PxSerializationRegistry& sr, const PxCollection* pxExternalRefs, bool exportNames) +{ + if(!PxSerialization::isSerializable(pxCollection, sr, pxExternalRefs)) + return false; + + Collection& collection = static_cast(pxCollection); + const Collection* externalRefs = static_cast(pxExternalRefs); + + //temporary memory stream which allows fixing up data up stream + + SerializationRegistry& sn = static_cast(sr); + + // sort collection by "order" value (this will be the order in which they get serialized) + sortCollection(collection, sn, false); + + //initialized the context with the sorted collection. + SerializationContext context(collection, externalRefs); + + // gather reference information + bool hasDeserializedAssets = false; + { + const PxU32 nb = collection.internalGetNbObjects(); + for(PxU32 i=0;igetConcreteType()); +#if PX_CHECKED + //can't guarantee marked padding for deserialized instances + if(!(s->getBaseFlags() & PxBaseFlag::eOWNS_MEMORY)) + hasDeserializedAssets = true; +#endif + const PxSerializer* serializer = sn.getSerializer(s->getConcreteType()); + PX_ASSERT(serializer); + serializer->registerReferences(*s, context); + } + } + + // now start the actual serialization into the output stream + OutputStreamWriter writer(outputStream); + LegacySerialStream stream(writer, collection, exportNames); + + writeHeader(stream, hasDeserializedAssets); + + // write size of collection + stream.alignData(PX_SERIAL_ALIGN); + PxU32 nbObjectsInCollection = collection.internalGetNbObjects(); + stream.writeData(&nbObjectsInCollection, sizeof(PxU32)); + + // write the manifest table (PxU32 offset, PxConcreteType type) + { + Ps::Array manifestTable(collection.internalGetNbObjects()); + PxU32 headerOffset = 0; + for(PxU32 i=0;igetConcreteType()); + PxType concreteType = s->getConcreteType(); + const PxSerializer* serializer = sn.getSerializer(concreteType); + PX_ASSERT(serializer); + manifestTable[i] = ManifestEntry(headerOffset, concreteType); + PxU32 classSize = PxU32(serializer->getClassSize()); + headerOffset += getPadding(classSize, PX_SERIAL_ALIGN) + classSize; + } + stream.alignData(PX_SERIAL_ALIGN); + const PxU32 nb = manifestTable.size(); + stream.writeData(&nb, sizeof(PxU32)); + stream.writeData(manifestTable.begin(), manifestTable.size()*sizeof(ManifestEntry)); + + //store offset for end of object buffer (PxU32 offset) + stream.writeData(&headerOffset, sizeof(PxU32)); + } + + // write import references + { + const Ps::Array& importReferences = context.getImportReferences(); + stream.alignData(PX_SERIAL_ALIGN); + const PxU32 nb = importReferences.size(); + stream.writeData(&nb, sizeof(PxU32)); + stream.writeData(importReferences.begin(), importReferences.size()*sizeof(ImportReference)); + } + + // write export references + { + PxU32 nbIds = collection.getNbIds(); + Ps::Array exportReferences(nbIds); + //we can't get quickly from id to object index in collection. + //if we only need this here, its not worth to build a hash + nbIds = 0; + for (PxU32 i=0;i internalReferencesPtr(internalReferencesPtrMap.size()); + PxU32 nbInternalPtrReferences = 0; + for(InternalRefMap::Iterator iter = internalReferencesPtrMap.getIterator(); !iter.done(); ++iter) + internalReferencesPtr[nbInternalPtrReferences++] = InternalReferencePtr(iter->first.first, iter->first.second, iter->second); + + InternalRefMap& internalReferencesIdxMap = context.getInternalReferencesIdxMap(); + Ps::Array internalReferencesIdx(internalReferencesIdxMap.size()); + PxU32 nbInternalIdxReferences = 0; + for(InternalRefMap::Iterator iter = internalReferencesIdxMap.getIterator(); !iter.done(); ++iter) + internalReferencesIdx[nbInternalIdxReferences++] = InternalReferenceIdx(Ps::to32(iter->first.first), iter->first.second, iter->second); + + stream.alignData(PX_SERIAL_ALIGN); + + stream.writeData(&nbInternalPtrReferences, sizeof(PxU32)); + stream.writeData(internalReferencesPtr.begin(), internalReferencesPtr.size()*sizeof(InternalReferencePtr)); + + stream.writeData(&nbInternalIdxReferences, sizeof(PxU32)); + stream.writeData(internalReferencesIdx.begin(), internalReferencesIdx.size()*sizeof(InternalReferenceIdx)); + } + + // write object data + { + stream.alignData(PX_SERIAL_ALIGN); + const PxU32 nb = collection.internalGetNbObjects(); + for(PxU32 i=0;igetConcreteType()); + const PxSerializer* serializer = sn.getSerializer(s->getConcreteType()); + PX_ASSERT(serializer); + stream.alignData(PX_SERIAL_ALIGN); + serializer->exportData(*s, stream); + } + } + + // write extra data + { + const PxU32 nb = collection.internalGetNbObjects(); + for(PxU32 i=0;igetConcreteType()); + + const PxSerializer* serializer = sn.getSerializer(s->getConcreteType()); + PX_ASSERT(serializer); + + stream.alignData(PX_SERIAL_ALIGN); + serializer->exportExtraData(*s, stream); + } + } + + return true; +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp new file mode 100644 index 000000000..10ca76946 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp @@ -0,0 +1,166 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +/* +- get rid of STL + +- NpArticulationLinkArray with more than 4 entries +- big convexes Xbox => PC + +- put a cache in "convertClass" function +- remove MD one at a time and check what happens in the converter. Make it robust/report errors +- for xbox, compare against source xbox file if it exists +- maybe put some info in the header to display "File generated by ConvX 1.xx" on screen (to debug) +- report inconsistent naming convention in each class!!!! +- try to automatically discover padding bytes? use "0xcd" pattern? +* do last param of XXXX_ITEMS macro automatically +- what if source files are 64bits? can we safely convert a 64bit ptr to 32bit? +*/ + +#include "foundation/PxErrorCallback.h" +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxIO.h" +#include "SnConvX.h" +#include "serialization/SnSerializationRegistry.h" +#include +#include "PsFoundation.h" + +using namespace physx; + +Sn::ConvX::ConvX() : + mMetaData_Src (NULL), + mMetaData_Dst (NULL), + mOutStream (NULL), + mMustFlip (false), + mOutputSize (0), + mSrcPtrSize (0), + mDstPtrSize (0), + mNullPtr (false), + mNoOutput (false), + mMarkedPadding (false), + mNbErrors (0), + mNbWarnings (0), + mReportMode (PxConverterReportMode::eNORMAL), + mPerformConversion (true) +{ + // memset(mZeros, 0, CONVX_ZERO_BUFFER_SIZE); + memset(mZeros, 0x42, CONVX_ZERO_BUFFER_SIZE); +} + +Sn::ConvX::~ConvX() +{ + resetNbErrors(); + resetConvexFlags(); + releaseMetaData(); + resetUnions(); +} + +void Sn::ConvX::release() +{ + delete this; +} + +bool Sn::ConvX::setMetaData(PxInputStream& inputStream, MetaDataType type) +{ + resetNbErrors(); + return (loadMetaData(inputStream, type) != NULL); +} + +bool Sn::ConvX::setMetaData(PxInputStream& srcMetaData, PxInputStream& dstMetaData) +{ + releaseMetaData(); + resetUnions(); + + if(!setMetaData(srcMetaData, META_DATA_SRC)) + return false; + if(!setMetaData(dstMetaData, META_DATA_DST)) + return false; + + return true; +} + +bool Sn::ConvX::compareMetaData() const +{ + if (!mMetaData_Src || !mMetaData_Dst) { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "PxBinaryConverter: metadata not defined. Call PxBinaryConverter::setMetaData first.\n"); + return false; + } + + return mMetaData_Src->compare(*mMetaData_Dst); +} + +bool Sn::ConvX::convert(PxInputStream& srcStream, PxU32 srcSize, PxOutputStream& targetStream) +{ + if(!mMetaData_Src || !mMetaData_Dst) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "PxBinaryConverter: metadata not defined. Call PxBinaryConverter::setMetaData first.\n"); + return false; + } + + resetConvexFlags(); + resetNbErrors(); + + bool conversionStatus = false; + if(mPerformConversion) + { + if(srcSize == 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxBinaryConverter: source serialized data size is zero.\n"); + return false; + } + + void* memory = PX_ALLOC_TEMP(srcSize+ALIGN_FILE, "ConvX source file"); + void* memoryA = reinterpret_cast((size_t(memory) + ALIGN_FILE)&~(ALIGN_FILE-1)); + + const PxU32 nbBytesRead = srcStream.read(memoryA, srcSize); + if(nbBytesRead != srcSize) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxBinaryConverter: failure on reading source serialized data.\n"); + PX_FREE(memory); + return false; + } + + displayMessage(PxErrorCode::eDEBUG_INFO, "\n\nConverting...\n\n"); + + { + if(!initOutput(targetStream)) + { + PX_FREE(memory); + return false; + } + conversionStatus = convert(memoryA, int(srcSize)); + closeOutput(); + } + + PX_FREE(memory); + } + return conversionStatus; +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h new file mode 100644 index 000000000..9b07a101c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h @@ -0,0 +1,183 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PX_CONVX_H +#define PX_CONVX_H + +#include "foundation/PxErrors.h" +#include "PxBinaryConverter.h" +#include "PxTypeInfo.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PsArray.h" +#include "SnConvX_Common.h" +#include "SnConvX_Union.h" +#include "SnConvX_MetaData.h" +#include "SnConvX_Align.h" + +#define CONVX_ZERO_BUFFER_SIZE 256 + +namespace physx { + +class PxSerializationRegistry; + +namespace Sn { + + struct HeightFieldData; + class PointerRemap + { + public: + PointerRemap(); + ~PointerRemap(); + + bool checkRefIsNotUsed(PxU32 ref) const; + void setObjectRef(PxU64 object64, PxU32 ref); + bool getObjectRef(PxU64 object64, PxU32& ref) const; + + struct InternalData + { + PxU64 object; + PxU32 id; + }; + + Ps::Array mData; + }; + + class ConvX : public physx::PxBinaryConverter, public shdfnd::UserAllocated + { + public: + ConvX(); + virtual ~ConvX(); + + virtual void release(); + virtual void setReportMode(PxConverterReportMode::Enum mode) { mReportMode = mode; } + PX_FORCE_INLINE bool silentMode() const { return mReportMode==PxConverterReportMode::eNONE; } + PX_FORCE_INLINE bool verboseMode() const { return mReportMode==PxConverterReportMode::eVERBOSE; } + + virtual bool setMetaData(PxInputStream& srcMetaData, PxInputStream& dstMetaData); + virtual bool compareMetaData() const; + virtual bool convert(PxInputStream& srcStream, PxU32 srcSize, PxOutputStream& targetStream); + + private: + ConvX& operator=(const ConvX&); + bool setMetaData(PxInputStream& inputStream, MetaDataType type); + + // Meta-data + void releaseMetaData(); + const MetaData* loadMetaData(PxInputStream& inputStream, MetaDataType type); + const MetaData* getBinaryMetaData(MetaDataType type); + int getNbMetaClasses(MetaDataType type); + MetaClass* getMetaClass(unsigned int i, MetaDataType type) const; + MetaClass* getMetaClass(const char* name, MetaDataType type) const; + MetaClass* getMetaClass(PxConcreteType::Enum concreteType, MetaDataType type); + MetaData* mMetaData_Src; + MetaData* mMetaData_Dst; + + // Convert + + bool convert(const void* buffer, int fileSize); + void resetConvexFlags(); + void _enumerateFields(const MetaClass* mc, ExtraDataEntry2* entries, int& nb, int baseOffset, MetaDataType type) const; + void _enumerateExtraData(const char* address, const MetaClass* mc, ExtraDataEntry* entries, int& nb, int offset, MetaDataType type) const; + PxU64 read64(const void*& buffer); + int read32(const void*& buffer); + short read16(const void*& buffer); + bool convertClass(const char* buffer, const MetaClass* mc, int offset); + const char* convertExtraData_Array(const char* Address, const char* lastAddress, const char* objectAddress, const ExtraDataEntry& ed); + const char* convertExtraData_Ptr(const char* Address, const char* lastAddress, const PxMetaDataEntry& entry, int count, int ptrSize_Src, int ptrSize_Dst); + int getConcreteType(const char* buffer); + bool convertCollection(const void* buffer, int fileSize, int nbObjects); + const void* convertManifestTable(const void* buffer, int& fileSize); + const void* convertImportReferences(const void* buffer, int& fileSize); + const void* convertExportReferences(const void* buffer, int& fileSize); + const void* convertInternalReferences(const void* buffer, int& fileSize); + const void* convertReferenceTables(const void* buffer, int& fileSize, int& nbObjectsInCollection); + bool checkPaddingBytes(const char* buffer, int byteCount); + + // ---- big convex surgery ---- + PsArray mConvexFlags; + // Align + const char* alignStream(const char* buffer, int alignment=ALIGN_DEFAULT); + void alignTarget(int alignment); + + char mZeros[CONVX_ZERO_BUFFER_SIZE]; + // Unions + bool registerUnion(const char* name); + bool registerUnionType(const char* unionName, const char* typeName, int typeValue); + const char* getTypeName(const char* unionName, int typeValue); + void resetUnions(); + PsArray mUnions; + // Output + void setNullPtr(bool); + void setNoOutput(bool); + bool initOutput(PxOutputStream& targetStream); + void closeOutput(); + int getCurrentOutputSize(); + void output(short value); + void output(int value); + void output(PxU64 value); + void output(const char* buffer, int nbBytes); + void convert8 (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convertPad8 (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convert16 (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convert32 (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convert64 (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convertFloat(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + void convertPtr (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + PxOutputStream* mOutStream; + bool mMustFlip; + int mOutputSize; + int mSrcPtrSize; + int mDstPtrSize; + bool mNullPtr; + bool mNoOutput; + bool mMarkedPadding; + + // Errors + void resetNbErrors(); + int getNbErrors() const; + void displayMessage(physx::PxErrorCode::Enum code, const char* format, ...); + int mNbErrors; + int mNbWarnings; + + // Settings + PxConverterReportMode::Enum mReportMode; + bool mPerformConversion; + + // Remap pointers + void exportIntAsPtr(int value); + void exportInt(int value); + void exportInt64(PxU64 value); + PointerRemap mRemap; + PointerRemap* mActiveRemap; + PxU32 mPointerRemapCounter; + + friend class MetaData; + friend struct MetaClass; + }; +} } +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp new file mode 100644 index 000000000..45579ba90 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "SnConvX.h" +#include "SnConvX_Align.h" +#include +using namespace physx; + +void Sn::ConvX::alignTarget(int alignment) +{ + const int outputSize = getCurrentOutputSize(); + const PxU32 outPadding = getPadding(size_t(outputSize), PxU32(alignment)); + if(outPadding) + { + assert(outPadding + +using namespace physx; +using namespace physx::Sn; +using namespace Cm; + +void Sn::ConvX::resetConvexFlags() +{ + mConvexFlags.clear(); +} + +void Sn::ConvX::_enumerateFields(const MetaClass* mc, ExtraDataEntry2* entries, int& nb, int baseOffset, MetaDataType type) const +{ + PxU32 nbFields = mc->mFields.size(); + int offsetCheck = baseOffset; + for(PxU32 j=0;jmFields[j]; + if(entry.mFlags & PxMetaDataFlag::eCLASS || entry.mFlags & PxMetaDataFlag::eEXTRA_DATA) + continue; + + assert(offsetCheck == baseOffset + entry.mOffset); + + int currentOffset = baseOffset + entry.mOffset; + + //for(int c=0;cmCallback) + { + entries[nb].entry = entry; + entries[nb].offset = currentOffset; + entries[nb].cb = fieldType->mCallback; + nb++; + } + else + { + for(int c=0;cmFields.size(); + for(PxU32 j=0;jmFields[j]; + if(entry.mFlags & PxMetaDataFlag::eCLASS /*|| entry.mFlags & PxMetaDataFlag::ePTR*/ || entry.mFlags & PxMetaDataFlag::eTYPEDEF) + continue; + + const char* entryType = entry.mType; + + // + // Insanely Twisted Shadow GeometryUnion + // + // Special code is needed as long as there are no meta data tags to describe our unions properly. The way it is done here is + // not future-proof at all. There should be a tag to describe where the union type can be found and the number of bytes + // this type id needs. Then a mapping needs to get added from each union type id to the proper meta class name. + // + if (entry.mFlags & PxMetaDataFlag::eUNION) + { + if (!mc->mClassName || strcmp(mc->mClassName, "Gu::GeometryUnion")!=0) + continue; + else + { + // ### hardcoded bit here, will only work when union type is the first int of the struct + const int* tmp = reinterpret_cast(address + offset); + const int unionType = *tmp; + + ConvX* tmpConv = const_cast(this); // ... don't ask + const char* typeName = tmpConv->getTypeName(entry.mType, unionType); + assert(typeName); + + bool isTriMesh = (strcmp(typeName, "PxTriangleMeshGeometryLL") == 0); + bool isHeightField = (strcmp(typeName, "PxHeightFieldGeometryLL") == 0); + if (!isTriMesh && !isHeightField) + { + continue; + } + else + { + entryType = typeName; + } + } + } + + // MetaClass* extraDataType = getMetaClass(entry.mType, type); + // if(!extraDataType) + // continue; + + if(entry.mFlags & PxMetaDataFlag::eEXTRA_DATA) + { + entries[nb].entry = entry; + entries[nb].offset = offset+entry.mOffset; + nb++; + } + else + { + if(entry.mFlags & PxMetaDataFlag::ePTR) + continue; + + MetaClass* extraDataType = getMetaClass(entryType, type); + if(!extraDataType) + continue; + + if(!extraDataType->mCallback) + _enumerateExtraData(address, extraDataType, entries, nb, offset+entry.mOffset, type); + } + } +} + +PxU64 Sn::ConvX::read64(const void*& buffer) +{ + const PxU64* buf64 = reinterpret_cast(buffer); + buffer = reinterpret_cast(size_t(buffer) + sizeof(PxU64)); + PxU64 value = *buf64; + output(value); + return value; +} + +int Sn::ConvX::read32(const void*& buffer) +{ + const int* buf32 = reinterpret_cast(buffer); + buffer = reinterpret_cast(size_t(buffer) + sizeof(int)); + int value = *buf32; + output(value); + return value; +} + +short Sn::ConvX::read16(const void*& buffer) +{ + const short* buf16 = reinterpret_cast(buffer); + buffer = reinterpret_cast(size_t(buffer) + sizeof(short)); + short value = *buf16; + output(value); + return value; +} + +#if PX_CHECKED +extern const char* gVTable; +static bool compareEntries(const ExtraDataEntry2& e0, const ExtraDataEntry2& e1) +{ + if(e0.entry.isVTablePtr() && e1.entry.isVTablePtr()) + return true; + + if((e0.entry.mFlags & PxMetaDataFlag::eUNION) && (e1.entry.mFlags & PxMetaDataFlag::eUNION)) + { + if(e0.entry.mType && e1.entry.mType) + { + // We can't compare the ptrs since they index different string tables + if(strcmp(e0.entry.mType, e1.entry.mType)==0) + return true; + } + return false; + } + + if(e0.entry.mName && e1.entry.mName) + { + // We can't compare the ptrs since they index different string tables + if(strcmp(e0.entry.mName, e1.entry.mName)==0) + return true; + } + return false; +} +#endif + +// TODO: optimize this +bool Sn::ConvX::convertClass(const char* buffer, const MetaClass* mc, int offset) +{ + // ---- big convex surgery ---- + bool convexSurgery = false; + bool foundNbVerts = false; + bool removeBigData = false; + + // force reference + (void)foundNbVerts; + + displayMessage(PxErrorCode::eDEBUG_INFO, "%s\n", mc->mClassName); + displayMessage(PxErrorCode::eDEBUG_INFO, "+++++++++++++++++++++++++++++++++++++++++++++\n"); + + if(strcmp(mc->mClassName, "ConvexMesh")==0) + { + convexSurgery = true; + } + // ---- big convex surgery ---- + + int nbSrcEntries = 0; + PX_ALLOCA(srcEntries, ExtraDataEntry2, 256); // ### painful ctors here + int nbDstEntries = 0; + PX_ALLOCA(dstEntries, ExtraDataEntry2, 256); // ### painful ctors here + // Find corresponding meta-class for target platform + const MetaClass* target_mc = getMetaClass(mc->mClassName, META_DATA_DST); + assert(target_mc); + + if(mc->mCallback) + { + srcEntries[0].cb = mc->mCallback; + srcEntries[0].offset = offset; + srcEntries[0].entry.mType = mc->mClassName; + srcEntries[0].entry.mName = mc->mClassName; + srcEntries[0].entry.mOffset = offset; + srcEntries[0].entry.mSize = mc->mSize; + srcEntries[0].entry.mCount = 1; + srcEntries[0].entry.mFlags = 0; + nbSrcEntries = 1; + + assert(target_mc->mCallback); + dstEntries[0].cb = target_mc->mCallback; + dstEntries[0].offset = offset; + dstEntries[0].entry.mType = target_mc->mClassName; + dstEntries[0].entry.mName = target_mc->mClassName; + dstEntries[0].entry.mOffset = offset; + dstEntries[0].entry.mSize = target_mc->mSize; + dstEntries[0].entry.mCount = 1; + dstEntries[0].entry.mFlags = 0; + nbDstEntries = 1; + } + else + { + nbSrcEntries = 0; + _enumerateFields(mc, srcEntries, nbSrcEntries, 0, META_DATA_SRC); + assert(nbSrcEntries<256); + + nbDstEntries = 0; + _enumerateFields(target_mc, dstEntries, nbDstEntries, 0, META_DATA_DST); + assert(nbDstEntries<256); + + // nb = mc->mNbEntries; + // assert(nb>=0); + // memcpy(entries, mc->mEntries, nb*sizeof(ExtraDataEntry2)); + } + + int srcOffsetCheck = 0; + int dstOffsetCheck = 0; + int j = 0; + // Track cases where the vtable pointer location is different for different platforms. + // The variables indicate whether a platform has a vtable pointer entry that has not been converted yet + // and they will remember the index of the corrssponding entry. This works because there can only + // be one open vtable pointer entry at a time. + int srcOpenVTablePtrEntry = -1; + int dstOpenVTablePtrEntry = -1; + + //if the src and dst platform place the vtable pointers at different locations some fiddling with the iteration count can be necessary. + int addVTablePtrShiftIteration = 0; + const int maxNb = nbSrcEntries > nbDstEntries ? nbSrcEntries : nbDstEntries; + for(int i=0; i < (maxNb + addVTablePtrShiftIteration); i++) + { + + if (i < nbSrcEntries) + { + displayMessage(PxErrorCode::eDEBUG_INFO, "\t0x%p\t%02x\t%d\t%d\t%s", buffer + srcOffsetCheck, + static_cast(buffer[srcOffsetCheck]), srcOffsetCheck, srcEntries[i].entry.mOffset, srcEntries[i].entry.mName); + for (int byteCount = 1; byteCount < srcEntries[i].entry.mSize; ++byteCount) + displayMessage(PxErrorCode::eDEBUG_INFO, "\t0x%p\t%02x\t%d\t%d\t.", buffer + srcOffsetCheck + byteCount, + static_cast(buffer[srcOffsetCheck + byteCount]), srcOffsetCheck + byteCount, srcEntries[i].entry.mOffset + byteCount); + } + + bool handlePadding = true; + bool skipLoop = false; + while(handlePadding) + { + const int pad0 = i0) + { + displayMessage(PxErrorCode::eDEBUG_WARNING, + "PxBinaryConverter warning: Bytes after %s::%s don't look like padding bytes. Likely mismatch between binary data and metadata.\n", + mc->mClassName, srcEntries[i-1].entry.mName ); + } + else + displayMessage(PxErrorCode::eDEBUG_WARNING, + "PxBinaryConverter warning: Bytes after %s don't look like padding bytes. Likely mismatch between binary data and metadata.\n", + mc->mClassName); + + } +#endif + if(pad1) + { + // Both have padding + // ### check sizes, output bytes + if(srcEntries[i].entry.mSize==dstEntries[j].entry.mSize) + { + // I guess we can just go on with the normal code here + handlePadding = false; + } + else + { + // Output padding + assert(srcEntries[i].cb); + assert(srcEntries[i].offset == srcOffsetCheck); + + const int padSize = dstEntries[j].entry.mSize; + char* paddingBytes = reinterpret_cast(PX_ALLOC(sizeof(char)*padSize, "paddingByte")); + memset(paddingBytes, 0, size_t(padSize)); + assert(dstEntries[j].cb); + (this->*dstEntries[j].cb)(paddingBytes, dstEntries[j].entry, dstEntries[j].entry); + assert(dstOffsetCheck==dstEntries[j].offset); + dstOffsetCheck += padSize; + PX_FREE(paddingBytes); + + // srcEntries[i].cb(buffer+srcOffsetCheck, srcEntries[i].entry, dstEntries[j].entry); + // assert(dstOffsetCheck==dstEntries[j].offset); + // dstOffsetCheck += dstEntries[j].entry.mSize; + srcOffsetCheck += srcEntries[i].entry.mSize; + + // Skip dest padding field + j++; + + // continue; // ### BUG, doesn't go back to the "for" + skipLoop = true; + handlePadding = false; + } + } + else + { + // Src has padding, dst has not => skip conversion + // Don't increase j + skipLoop = true; + handlePadding = false; + srcOffsetCheck += srcEntries[i].entry.mSize; + } + } + else + { + if(pad1) + { + // Dst has padding, src has not + + // Output padding + const int padSize = dstEntries[j].entry.mSize; + char* paddingBytes = reinterpret_cast(PX_ALLOC(sizeof(char)*padSize, "paddingByte")); + memset(paddingBytes, 0, size_t(padSize)); + assert(dstEntries[j].cb); + (this->*dstEntries[j].cb)(paddingBytes, dstEntries[j].entry, dstEntries[j].entry); + assert(dstOffsetCheck==dstEntries[j].offset); + dstOffsetCheck += padSize; + PX_FREE(paddingBytes); + + // Skip dest padding field, keep same src field + j++; + } + else + { + assert(0); + } + } + } + else handlePadding = false; + } + + if(skipLoop) + continue; + + int modSrcOffsetCheck = srcOffsetCheck; + const ExtraDataEntry2* srcEntryPtr = &srcEntries[i]; + const ExtraDataEntry2* dstEntryPtr = &dstEntries[j]; + + bool isSrcVTablePtr = (i < nbSrcEntries) ? srcEntryPtr->entry.isVTablePtr() : false; + if (isSrcVTablePtr && (dstOpenVTablePtrEntry != -1)) + { + // vtable ptr position mismatch: + // this check is necessary to align src and dst index again when the + // dst vtable pointer has been written already and the src vtable ptr + // element is reached. + // + // i + // src: | a | b | vt-ptr | c | ... + // dst: | vt-ptr | a | b | c | ... + // j + // + // it needs special treatment because the following case fails otherwise + // i + // src: | a | b | vt-ptr | c | vt-ptr | ... + // dst: | vt-ptr | a | b | vt-ptr | c | ... + // j + + // + // This entry has been written already -> advance to next src entry + // + srcOffsetCheck += srcEntryPtr->entry.mSize; + i++; + isSrcVTablePtr = (i < nbSrcEntries) ? srcEntryPtr->entry.isVTablePtr() : false; + PX_ASSERT(dstOpenVTablePtrEntry < nbDstEntries); + PX_ASSERT(dstEntries[dstOpenVTablePtrEntry].entry.isVTablePtr()); + dstOpenVTablePtrEntry = -1; + PX_ASSERT(addVTablePtrShiftIteration == 0); + } + bool isDstVTablePtr = (j < nbDstEntries) ? dstEntryPtr->entry.isVTablePtr() : false; + if (isDstVTablePtr && (srcOpenVTablePtrEntry != -1)) + { + // i + // src: | vt-ptr | a | b | c | ... + // dst: | a | b | vt-ptr | c | ... + // j + + i--; // next iteration the current element should get processed + isSrcVTablePtr = true; + PX_ASSERT(srcOpenVTablePtrEntry < nbSrcEntries); + srcEntryPtr = &srcEntries[srcOpenVTablePtrEntry]; + PX_ASSERT(srcEntryPtr->entry.isVTablePtr()); + modSrcOffsetCheck = srcEntryPtr->offset; + srcOffsetCheck -= srcEntryPtr->entry.mSize; // to make sure total change is 0 after this iteration + srcOpenVTablePtrEntry = -1; + PX_ASSERT(addVTablePtrShiftIteration == 1); + addVTablePtrShiftIteration = 0; + } + + if(i==nbSrcEntries && j==nbDstEntries) + { + PX_ASSERT((srcOpenVTablePtrEntry == -1) && (dstOpenVTablePtrEntry == -1)); + break; + } + + if (isSrcVTablePtr || isDstVTablePtr) + { + if (!isSrcVTablePtr) + { + // i + // src: | a | b | vt-ptr | c | ... + // dst: | vt-ptr | a | b | c | ... + // j + + PX_ASSERT(dstOpenVTablePtrEntry == -1); // the other case should be detected and treated earlier + PX_ASSERT(srcOpenVTablePtrEntry == -1); + PX_ASSERT(addVTablePtrShiftIteration == 0); + + int k; + for(k=i+1; k < nbSrcEntries; k++) + { + if (srcEntries[k].entry.isVTablePtr()) + break; + } + PX_ASSERT(k < nbSrcEntries); + + srcEntryPtr = &srcEntries[k]; + modSrcOffsetCheck = srcEntryPtr->offset; + srcOffsetCheck -= srcEntryPtr->entry.mSize; // to make sure total change is 0 after this iteration + + dstOpenVTablePtrEntry = j; + i--; // to make sure the original entry gets processed in the next iteration + } + else if (!isDstVTablePtr) + { + // i ---> i + // src: | vt-ptr | a | b | c | ... + // dst: | a | b | vt-ptr | c | ... + // j + + PX_ASSERT(srcOpenVTablePtrEntry == -1); // the other case should be detected and treated earlier + PX_ASSERT(dstOpenVTablePtrEntry == -1); + PX_ASSERT(addVTablePtrShiftIteration == 0); + + srcOffsetCheck += srcEntryPtr->entry.mSize; + modSrcOffsetCheck = srcOffsetCheck; + srcOpenVTablePtrEntry = i; + i++; + srcEntryPtr = &srcEntries[i]; + + addVTablePtrShiftIteration = 1; // additional iteration might be needed to process vtable pointer at the end of a class + + PX_ASSERT((i < nbSrcEntries) && ((srcEntryPtr->entry.mFlags & PxMetaDataFlag::ePADDING) == 0)); + // if the second check fails, this whole section might have to be done before the padding bytes get processed. Not sure + // what other consequences that might have though. + } + } +#if PX_CHECKED + else + { + if(!compareEntries(*srcEntryPtr, *dstEntryPtr)) + { + displayMessage(PxErrorCode::eINVALID_PARAMETER, "\rConvX::convertClass: %s, src meta data and dst meta data don't match!", mc->mClassName); + return false; + } + } +#endif + + const ExtraDataEntry2& srcEntry = *srcEntryPtr; + const ExtraDataEntry2& dstEntry = *dstEntryPtr; + + if(srcEntry.entry.mFlags & PxMetaDataFlag::eUNION) + { + // ### hardcoded bit here, will only work when union type is the first int of the struct + const int* tmp = reinterpret_cast(buffer + modSrcOffsetCheck); + const int unionType = *tmp; + + const char* typeName = getTypeName(srcEntry.entry.mType, unionType); + assert(typeName); + + MetaClass* unionMC = getMetaClass(typeName, META_DATA_SRC); + assert(unionMC); + + convertClass(buffer + modSrcOffsetCheck, unionMC, 0); // ### recurse + + dstOffsetCheck += dstEntry.entry.mSize; + + MetaClass* targetUnionMC = getMetaClass(typeName, META_DATA_DST); + assert(targetUnionMC); + + const int delta = dstEntry.entry.mSize - targetUnionMC->mSize; + char* deltaBytes = reinterpret_cast(PX_ALLOC(sizeof(char)*delta, "deltaBytes")); + memset(deltaBytes, 0, size_t(delta)); + output(deltaBytes, delta); // Skip unused bytes at the end of the union + PX_FREE(deltaBytes); + srcOffsetCheck += srcEntry.entry.mSize; // do not use modSrcOffsetCheck here! + } + else + { + assert(srcEntry.cb); + assert(srcEntry.offset == modSrcOffsetCheck); + + // ---- big convex surgery ---- + if(convexSurgery) + { + if(strcmp(srcEntry.entry.mName, "mNbHullVertices")==0) + { + assert(srcEntry.entry.mSize==1); + const int nbVerts = int(*(buffer+modSrcOffsetCheck)); + assert(!foundNbVerts); + foundNbVerts = true; + + const int gaussMapLimit = getBinaryMetaData(META_DATA_DST)->getGaussMapLimit(); + if(nbVerts > gaussMapLimit) + { + // We need a gauss map and we have one => keep it + } + else + { + // We don't need a gauss map and we have one => remove it + removeBigData = true; + } + } + else + { + if(removeBigData) + { + const bool isBigConvexData = strcmp(srcEntry.entry.mType, "BigConvexData")==0 || + strcmp(srcEntry.entry.mType, "BigConvexRawData")==0; + if(isBigConvexData) + { + assert(foundNbVerts); + setNullPtr(true); + } + } + } + } + // ---- big convex surgery ---- + + (this->*srcEntry.cb)(buffer+modSrcOffsetCheck, srcEntry.entry, dstEntry.entry); + assert(dstOffsetCheck==dstEntry.offset); + dstOffsetCheck += dstEntry.entry.mSize; + srcOffsetCheck += srcEntry.entry.mSize; // do not use modSrcOffsetCheck here! + + // ---- big convex surgery ---- + if(convexSurgery && removeBigData) + setNullPtr(false); + // ---- big convex surgery ---- + } + + j++; + } + + displayMessage(PxErrorCode::eDEBUG_INFO, "---------------------------------------------\n"); + + while(jmSize); + assert(srcOffsetCheck==mc->mSize); + + // ---- big convex surgery ---- + if(convexSurgery) + mConvexFlags.pushBack(removeBigData); + // ---- big convex surgery ---- + + return true; +} + +// Handles data defined with PX_DEF_BIN_METADATA_EXTRA_ARRAY +const char* Sn::ConvX::convertExtraData_Array(const char* Address, const char* lastAddress, const char* objectAddress, + const ExtraDataEntry& ed) +{ + (void)lastAddress; + MetaClass* mc = getMetaClass(ed.entry.mType, META_DATA_SRC); + assert(mc); + + // PT: safe to cast to int here since we're reading a count. + const int count = int(peek(ed.entry.mSize, objectAddress + ed.offset, ed.entry.mFlags)); + + // if(ed.entry.mCount) // Reused as align value + if(ed.entry.mAlignment) + { + Address = alignStream(Address, ed.entry.mAlignment); + // Address = alignStream(Address, ed.entry.mCount); + assert(Address<=lastAddress); + } + + for(int c=0;cmSize; + assert(Address<=lastAddress); + } + return Address; +} + +const char* Sn::ConvX::convertExtraData_Ptr(const char* Address, const char* lastAddress, const PxMetaDataEntry& entry, int count, + int ptrSize_Src, int ptrSize_Dst) +{ + (void)lastAddress; + + PxMetaDataEntry tmpSrc = entry; + tmpSrc.mCount = count; + tmpSrc.mSize = count * ptrSize_Src; + + PxMetaDataEntry tmpDst = entry; + tmpDst.mCount = count; + tmpDst.mSize = count * ptrSize_Dst; + + + displayMessage(PxErrorCode::eDEBUG_INFO, "extra data ptrs\n"); + displayMessage(PxErrorCode::eDEBUG_INFO, "+++++++++++++++++++++++++++++++++++++++++++++\n"); + displayMessage(PxErrorCode::eDEBUG_INFO, "\t0x%p\t%02x\t\t\t%s", Address, static_cast(Address[0]), entry.mName); + for (int byteCount = 1; byteCount < ptrSize_Src*count; ++byteCount) + displayMessage(PxErrorCode::eDEBUG_INFO, "\t0x%p\t%02x\t\t\t.", Address + byteCount, static_cast(Address[byteCount])); + + convertPtr(Address, tmpSrc, tmpDst); + Address += count * ptrSize_Src; + assert(Address<=lastAddress); + return Address; +} + +static bool decodeControl(PxU64 control, const ExtraDataEntry& ed, PxU64 controlMask = 0) +{ + if(ed.entry.mFlags & PxMetaDataFlag::eCONTROL_FLIP) + { + if(controlMask) + { + return (control & controlMask) ? false : true; + } + else + { + return control==0; + } + } + else + { + if(controlMask) + { + return (control & controlMask) ? true : false; + } + else + { + return control!=0; + } + } + +} + +// ### currently hardcoded, should change +int Sn::ConvX::getConcreteType(const char* buffer) +{ + MetaClass* mc = getMetaClass("PxBase", META_DATA_SRC); + assert(mc); + PxMetaDataEntry entry; + if(mc->getFieldByType("PxType", entry)) + { + // PT: safe to cast to int here since we're reading our own PxType + return int(peek(entry.mSize, buffer + entry.mOffset)); + } + assert(0); + return 0xffffffff; +} + +struct Item : public shdfnd::UserAllocated +{ + MetaClass* mc; + const char* address; +}; + +bool Sn::ConvX::convertCollection(const void* buffer, int fileSize, int nbObjects) +{ + const char* lastAddress = reinterpret_cast(buffer) + fileSize; + const char* Address = alignStream(reinterpret_cast(buffer)); + + const int ptrSize_Src = mSrcPtrSize; + const int ptrSize_Dst = mDstPtrSize; + Item* objects = PX_NEW(Item)[PxU32(nbObjects)]; + + for(PxU32 i=0;imSize; + assert(Address<=lastAddress); + } + + // Fields / extra data + if(1) + { + // ---- big convex surgery ---- + unsigned int nbConvexes = 0; + // ---- big convex surgery ---- + //const char* StartAddress2 = Address; + //int startDstSize2 = getCurrentOutputSize(); + for(int i=0;imClassName); + // if(strcmp(mc->mClassName, "TriangleMesh")==0) + // if(strcmp(mc->mClassName, "NpRigidDynamic")==0) + if(strcmp(mc0->mClassName, "HybridModel")==0) + { + int stop=1; + (void)(stop); + } + + // ### we actually need to collect all extra data for this class, including data from embedded members. + + PX_ALLOCA(entries, ExtraDataEntry, 256); + int nbEntries = 0; + _enumerateExtraData(objectAddress, mc0, entries, nbEntries, 0, META_DATA_SRC); + assert(nbEntries<256); + + Address = alignStream(Address); + assert(Address<=lastAddress); + + for(int j=0;jmSize; + assert(Address<=lastAddress); + + // Enumerate extra data for this optional class, and convert it too. + // This assumes the extra data for the optional class is always appended to the class itself, + // which is something we'll need to enforce in the SDK. So far this is only to handle optional + // inline arrays. + + // ### this should probably be recursive eventually + PX_ALLOCA(entries2, ExtraDataEntry, 256); + int nbEntries2 = 0; + _enumerateExtraData(objectAddress, extraDataType, entries2, nbEntries2, 0, META_DATA_SRC); + assert(nbEntries2<256); + for(int k=0;k> 16; + } + + if(decodeControl(controlValue2, ed2, controlMask)) + { + // PT: safe to cast to int here since we're reading a count + int count = int(peek(countSize, classAddress + countOffset, ed2.entry.mFlags)); + + if(ed2.entry.mAlignment) + { + assert(0); // Never tested + Address = alignStream(Address, ed2.entry.mAlignment); + assert(Address<=lastAddress); + } + + if(ed2.entry.mFlags & PxMetaDataFlag::ePTR) + { + assert(0); // Never tested + } + else + { + MetaClass* mc = getMetaClass(ed2.entry.mType, META_DATA_SRC); + assert(mc); + + while(count--) + { + convertClass(Address, mc, 0); + Address += mc->mSize; + assert(Address<=lastAddress); + } + } + + } + } + else + { + if( (ed2.entry.mFlags & PxMetaDataFlag::eALIGNMENT) && ed2.entry.mAlignment) + { + Address = alignStream(Address, ed2.entry.mAlignment); + assert(Address<=lastAddress); + } + else + { + // We assume it's an normal array, e.g. the ones from "big convexes" + assert(!(ed2.entry.mFlags & PxMetaDataFlag::eEXTRA_ITEM)); + + Address = convertExtraData_Array(Address, lastAddress, classAddress, ed2); + } + } + } + + } + else + { + int stop = 0; + (void)(stop); + } + + // ---- big convex surgery ---- + setNoOutput(false); + // ---- big convex surgery ---- + } + else if(ed.entry.mFlags & PxMetaDataFlag::eEXTRA_ITEMS) + { + // PX_DEF_BIN_METADATA_EXTRA_ITEMS + int reloc = ed.offset - ed.entry.mOffset; // ### because the enum code only fixed the "controlOffset"! + const int controlOffset = ed.entry.mOffset; + const int controlSize = ed.entry.mSize; + const int countOffset = ed.entry.mCount; + const int countSize = ed.entry.mOffsetSize; + + // const int controlValue2 = peek(controlSize, objectAddress + controlOffset); + const PxU64 controlValue2 = peek(controlSize, objectAddress + controlOffset + reloc); + + PxU64 controlMask = 0; + if(ed.entry.mFlags & PxMetaDataFlag::eCONTROL_MASK) + { + controlMask = PxU64(ed.entry.mFlags & (PxMetaDataFlag::eCONTROL_MASK_RANGE << 16)); + controlMask = controlMask >> 16; + } + + if(decodeControl(controlValue2, ed, controlMask)) + { + // PT: safe to cast to int here since we're reading a count + // int count = peek(countSize, objectAddress + countOffset); // ### + int count = int(peek(countSize, objectAddress + countOffset + reloc, ed.entry.mFlags)); // ### + + if(ed.entry.mAlignment) + { + Address = alignStream(Address, ed.entry.mAlignment); + assert(Address<=lastAddress); + } + + if(ed.entry.mFlags & PxMetaDataFlag::ePTR) + { + Address = convertExtraData_Ptr(Address, lastAddress, ed.entry, count, ptrSize_Src, ptrSize_Dst); + } + else + { + MetaClass* mc = getMetaClass(ed.entry.mType, META_DATA_SRC); + assert(mc); + + while(count--) + { + convertClass(Address, mc, 0); + Address += mc->mSize; + assert(Address<=lastAddress); + } + } + } + + } + else if(ed.entry.mFlags & PxMetaDataFlag::eALIGNMENT) + { + if(ed.entry.mAlignment) + { + displayMessage(PxErrorCode::eDEBUG_INFO, " align to %d bytes\n", ed.entry.mAlignment); + displayMessage(PxErrorCode::eDEBUG_INFO, "---------------------------------------------\n"); + + Address = alignStream(Address, ed.entry.mAlignment); + assert(Address<=lastAddress); + } + } + else if(ed.entry.mFlags & PxMetaDataFlag::eEXTRA_NAME) + { + if(ed.entry.mAlignment) + { + Address = alignStream(Address, ed.entry.mAlignment); + assert(Address<=lastAddress); + } + + //get string count + MetaClass* mc = getMetaClass("PxU32", META_DATA_SRC); + assert(mc); + //safe to cast to int here since we're reading a count. + const int count = int(peek(mc->mSize, Address, 0)); + + displayMessage(PxErrorCode::eDEBUG_INFO, " convert %d bytes string\n", count); + + convertClass(Address, mc, 0); + Address += mc->mSize; + + mc = getMetaClass(ed.entry.mType, META_DATA_SRC); + assert(mc); + + for(int c=0;cmSize; + assert(Address<=lastAddress); + } + } + else + { + Address = convertExtraData_Array(Address, lastAddress, objectAddress, ed); + } + } + } + PX_DELETE_ARRAY(objects); + assert(nbConvexes==mConvexFlags.size()); + } + + assert(Address==lastAddress); + + return true; +} + +bool Sn::ConvX::convert(const void* buffer, int fileSize) +{ + // Test initial alignment + if(size_t(buffer) & (ALIGN_DEFAULT-1)) + { + assert(0); + return false; + } + + const int header = read32(buffer); fileSize -= 4; (void)header; + + if (header != PX_MAKE_FOURCC('S','E','B','D')) + { + displayMessage(physx::PxErrorCode::eINVALID_PARAMETER, + "PxBinaryConverter: Buffer contains data with bad header indicating invalid serialized data."); + return false; + } + + const int version = read32(buffer); fileSize -= 4; (void)version; + + const int binaryVersion = read32(buffer); fileSize -= 4; + + if (!checkCompatibility(PxU32(version), PxU32(binaryVersion))) + { + char buf[512]; + getCompatibilityVersionsStr(buf, 512); + + displayMessage(physx::PxErrorCode::eINVALID_PARAMETER, + "PxBinaryConverter: Buffer contains data version (%x-%d) is incompatible with this PhysX sdk.\n These versions would be compatible: %s", + version, binaryVersion, buf); + return false; + } + const int buildNumber = read32(buffer); fileSize -= 4; (void)buildNumber; + + //read src platform tag and write dst platform tag according dst meta data + const int srcPlatformTag = *reinterpret_cast(buffer); + buffer = reinterpret_cast(size_t(buffer) + 4); + fileSize -= 4; + const int dstPlatformTag = mMetaData_Dst->getPlatformTag(); + output(dstPlatformTag); + + if (srcPlatformTag != mMetaData_Src->getPlatformTag()) + { + displayMessage(physx::PxErrorCode::eINVALID_PARAMETER, + "PxBinaryConverter: Mismatch of platform tags of binary data and metadata:\n Binary Data: %s\n MetaData: %s\n", + getBinaryPlatformName(PxU32(srcPlatformTag)), + getBinaryPlatformName(PxU32(mMetaData_Src->getPlatformTag()))); + return false; + } + + //read whether input data has marked padding, and set it for the output data (since 0xcd is written into pads on conversion) + const int srcMarkedPadding = *reinterpret_cast(buffer); + buffer = reinterpret_cast(size_t(buffer) + 4); + fileSize -= 4; + mMarkedPadding = srcMarkedPadding != 0; + const int dstMarkedPadding = 1; + output(dstMarkedPadding); + + int nbObjectsInCollection; + + buffer = convertReferenceTables(buffer, fileSize, nbObjectsInCollection); + if(!buffer) + return false; + + bool ret = convertCollection(buffer, fileSize, nbObjectsInCollection); + mMarkedPadding = false; + return ret; +} + +// PT: code below added to support 64bit-to-32bit conversions +void Sn::ConvX::exportIntAsPtr(int value) +{ + const int ptrSize_Src = mSrcPtrSize; + const int ptrSize_Dst = mDstPtrSize; + + PxMetaDataEntry entry; + + const char* address = NULL; + const PxU32 value32 = PxU32(value); + const PxU64 value64 = PxU64(value)&0xffffffff; + + if(ptrSize_Src==4) + { + address = reinterpret_cast(&value32); + } + else if(ptrSize_Src==8) + { + address = reinterpret_cast(&value64); + } + else assert(0); + + convertExtraData_Ptr(address, address + ptrSize_Src, entry, 1, ptrSize_Src, ptrSize_Dst); +} + +void Sn::ConvX::exportInt(int value) +{ + output(value); +} + +void Sn::ConvX::exportInt64(PxU64 value) +{ + output(value); +} + +PointerRemap::PointerRemap() +{ +} + +PointerRemap::~PointerRemap() +{ +} + +bool PointerRemap::checkRefIsNotUsed(PxU32 ref) const +{ + const PxU32 size = mData.size(); + for(PxU32 i=0;i(buffer)); + fileSize -= padding; + int nb = read32(buffer); + fileSize -= 4; + + MetaClass* mc_src = getMetaClass("Sn::ManifestEntry", META_DATA_SRC); + assert(mc_src); + + MetaClass* mc_dst = getMetaClass("Sn::ManifestEntry", META_DATA_DST); + assert(mc_dst); + + bool mdOk; + PxMetaDataEntry srcTypeField; + mdOk = mc_src->getFieldByName("type", srcTypeField); + PX_UNUSED(mdOk); + PX_ASSERT(mdOk); + + PxMetaDataEntry dstOffsetField; + mdOk = mc_dst->getFieldByName("offset", dstOffsetField); + PX_ASSERT(mdOk); + + const char* address = reinterpret_cast(buffer); + PxU32 headerOffset = 0; + for(int i=0;imSize)); + + //restore output state + mOutStream = outStream; + mOutputSize = int(outputSize); + } + + //output patched offset + PX_ASSERT(dstOffsetField.mOffset == 0); //assuming offset is the first data + output(int(headerOffset)); + + //output rest of ManifestEntry + PxU32 restSize = PxU32(mc_dst->mSize - dstOffsetField.mSize); + mOutStream->write(tmpStream.getData() + dstOffsetField.mSize, restSize); + mOutputSize += restSize; + + //increment source stream + address += mc_src->mSize; + fileSize -= mc_src->mSize; + assert(fileSize>=0); + + //update headerOffset using the type and dst meta data of the type + MetaClass* mc_classType_dst = getMetaClass(classType, META_DATA_DST); + if(!mc_classType_dst) + return NULL; + headerOffset += getPadding(size_t(mc_classType_dst->mSize), PX_SERIAL_ALIGN) + mc_classType_dst->mSize; + } + + output(int(headerOffset)); //endoffset + buffer = address + 4; + fileSize -= 4; + return buffer; +} + +const void* Sn::ConvX::convertImportReferences(const void* buffer, int& fileSize) +{ + PxU32 padding = getPadding(size_t(buffer), ALIGN_DEFAULT); + buffer = alignStream(reinterpret_cast(buffer)); + fileSize -= padding; + int nb = read32(buffer); + fileSize -= 4; + + if(!nb) + return buffer; + + MetaClass* mc = getMetaClass("Sn::ImportReference", META_DATA_SRC); + assert(mc); + + const char* address = reinterpret_cast(buffer); + for(int i=0;imSize; + fileSize -= mc->mSize; + assert(fileSize>=0); + } + return address; +} + +const void* Sn::ConvX::convertExportReferences(const void* buffer, int& fileSize) +{ + PxU32 padding = getPadding(size_t(buffer), ALIGN_DEFAULT); + buffer = alignStream(reinterpret_cast(buffer)); + fileSize -= padding; + int nb = read32(buffer); + fileSize -= 4; + + if(!nb) + return buffer; + + MetaClass* mc = getMetaClass("Sn::ExportReference", META_DATA_SRC); + assert(mc); + + const char* address = reinterpret_cast(buffer); + for(int i=0;imSize; + fileSize -= mc->mSize; + assert(fileSize>=0); + } + return address; +} + +const void* Sn::ConvX::convertInternalReferences(const void* buffer, int& fileSize) +{ + PxU32 padding = getPadding(size_t(buffer), ALIGN_DEFAULT); + buffer = alignStream(reinterpret_cast(buffer)); + fileSize -= padding; + + //pointer references + int nbPtrReferences = read32(buffer); + fileSize -= 4; + if(nbPtrReferences) + { + const char* address = reinterpret_cast(buffer); + MetaClass* mc = getMetaClass("Sn::InternalReferencePtr", META_DATA_SRC); + assert(mc); + for(int i=0;imSize; + fileSize -= mc->mSize; + assert(fileSize>=0); + } + buffer = address; + } + + //index references + int nbIdxReferences = read32(buffer); + fileSize -= 4; + if (nbIdxReferences) + { + const char* address = reinterpret_cast(buffer); + MetaClass* mc = getMetaClass("Sn::InternalReferenceIdx", META_DATA_SRC); + assert(mc); + for(int i=0;imSize; + fileSize -= mc->mSize; + assert(fileSize>=0); + } + buffer = address; + } + return buffer; +} + + +const void* Sn::ConvX::convertReferenceTables(const void* buffer, int& fileSize, int& nbObjectsInCollection) +{ + // PT: the map should not be used while creating it, so use one indirection + mActiveRemap = NULL; + mRemap.mData.clear(); + mPointerRemapCounter = 0; + + PxU32 padding = getPadding(size_t(buffer), ALIGN_DEFAULT); + buffer = alignStream(reinterpret_cast(buffer)); + fileSize -= padding; + + nbObjectsInCollection = read32(buffer); + if (nbObjectsInCollection == 0) + displayMessage(PxErrorCode::eDEBUG_INFO, "\n\nConverting empty collection!\n\n"); + fileSize -= 4; + + buffer = convertManifestTable(buffer, fileSize); + + if(!buffer) + return NULL; + + buffer = convertImportReferences(buffer, fileSize); + buffer = convertExportReferences(buffer, fileSize); + buffer = convertInternalReferences(buffer, fileSize); + + // PT: the map can now be used + mActiveRemap = &mRemap; + + return buffer; +} + +bool Sn::ConvX::checkPaddingBytes(const char* buffer, int byteCount) +{ + const unsigned char* src = reinterpret_cast(buffer); + + int i = 0; + while ((i < byteCount) && (src[i] == 0xcd)) + i++; + return (i == byteCount); +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp new file mode 100644 index 000000000..f67666f88 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "foundation/PxErrorCallback.h" +#include "SnConvX.h" +#include +#include "PsString.h" +#include "PsFoundation.h" + +#define MAX_DISPLAYED_ISSUES 10 + +using namespace physx; + +void Sn::ConvX::resetNbErrors() +{ + mNbErrors = 0; + mNbWarnings = 0; +} + +int Sn::ConvX::getNbErrors() const +{ + return mNbErrors; +} + +void Sn::ConvX::displayMessage(PxErrorCode::Enum code, const char* format, ...) +{ + if(silentMode()) + return; + + int sum = mNbWarnings + mNbErrors; + if(sum >= MAX_DISPLAYED_ISSUES) + return; + + bool display = false; + + if(code==PxErrorCode::eINTERNAL_ERROR || code==PxErrorCode::eINVALID_OPERATION || code==PxErrorCode::eINVALID_PARAMETER) + { + mNbErrors++; + display = true; + } + else if(code == PxErrorCode::eDEBUG_WARNING) + { + mNbWarnings++; + display = true; + } + + if(display || ((sum == 0) && verboseMode()) ) + { + va_list va; + va_start(va, format); + Ps::getFoundation().errorImpl(code, __FILE__, __LINE__, format, va); + va_end(va); + } + + if(display) + { + if( sum == 0) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_INFO, __FILE__, __LINE__, "Hit warnings or errors: skipping further verbose output.\n"); + } + else if(sum == MAX_DISPLAYED_ISSUES-1) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_INFO, __FILE__, __LINE__, "Exceeding 10 warnings or errors: skipping further output.\n"); + } + } + + return; +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp new file mode 100644 index 000000000..64fbb942a --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp @@ -0,0 +1,960 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "foundation/PxIO.h" +#include "foundation/PxMemory.h" +#include "SnConvX.h" +#include "common/PxSerialFramework.h" +#include "serialization/SnSerialUtils.h" +#include + +using namespace physx; +using namespace physx::Sn; + +//#define REMOVE_EXPLICIT_PADDING + +static const char gVTablePtr[] = "v-table ptr"; +static const char gAutoPadding[] = "auto-generated padding"; +static const char gByte[] = "paddingByte"; + +/////////////////////////////////////////////////////////////////////////////// + +bool PxMetaDataEntry::isVTablePtr() const +{ + return mType==gVTablePtr; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool MetaClass::getFieldByType(const char* type, PxMetaDataEntry& entry) const +{ + assert(type); + PxU32 nbFields = mFields.size(); + for(PxU32 i=0;i(PX_ALLOC(sizeof(bool)*mSize, "bool")); + memset(map, 0, size_t(mSize)); + + const PxU32 nbFields = mFields.size(); + for(PxU32 i=0;i=0 && byteStart=0 && byteEnd<=mSize); + + int startOffset = -1; + int nbBytes = 0; + for(int j=byteStart;jmSize*current.mCount==current.mSize); + } + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +MetaData::MetaData(ConvX& convx) : + mConvX (convx), + mType (META_DATA_NONE), + mNbEntries (0), + mEntries (NULL), + mStringTable (NULL), + mVersion (0), + mBuildNumber (0), + mSizeOfPtr (0), + mPlatformTag (0), + mGaussMapLimit (0), + mFlip (false) +{ +} + +MetaData::~MetaData() +{ + PxU32 nbMetaClasses = mMetaClasses.size(); + for(PxU32 i=0;imClassName, name)==0) + { + while(current->mMaster) + current = current->mMaster; + return current; + } + } + return NULL; +} + +MetaClass* MetaData::getMetaClass(PxConcreteType::Enum concreteType) const +{ + for(PxU32 i=0; i< mConcreteTypeTable.size(); i++) + { + if(mConcreteTypeTable[i].first == concreteType) + { + const char* className = offsetToText(reinterpret_cast(size_t(mConcreteTypeTable[i].second))); + return getMetaClass(className); + } + } + return NULL; +} + +MetaClass* MetaData::addNewClass(const char* name, int size, MetaClass* master, ConvertCallback callback) +{ + // PT: if you reach this assert, you used PX_DEF_BIN_METADATA_TYPEDEF twice on the same type + assert(!getMetaClass(name)); + MetaClass* mc = PX_NEW(MetaClass); + mc->mCallback = callback; + mc->mMaster = master; + mc->mClassName = name; + mc->mSize = size; + mc->mDepth = 0; + mc->mProcessed = false; +// mc->mNbEntries = -1; + + mMetaClasses.pushBack(mc); + + return mc; +} + +bool MetaData::load(PxInputStream& inputStream, MetaDataType type) +{ + assert(type!=META_DATA_NONE); + + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, "Loading %s meta-data...\n", type==META_DATA_SRC ? "source" : "target"); + + mType = type; + + mFlip = false; + { + int header; + inputStream.read(&header, 4); + if(header==PX_MAKE_FOURCC('M','E','T','A')) + { + mFlip = false; + } + else if(header==PX_MAKE_FOURCC('A','T','E','M')) + { + mFlip = true; + } + else + { + mConvX.displayMessage(PxErrorCode::eINVALID_PARAMETER, "PxBinaryConverter: invalid meta-data file!\n"); + return false; + } + + if (type == META_DATA_SRC && mFlip) + { + mConvX.displayMessage(PxErrorCode::eINVALID_PARAMETER, + "PxBinaryConverter: source meta data needs to match endianness with current system!"); + return false; + } + + inputStream.read(&mVersion, 4); + inputStream.read(&mBinaryVersion, 4); + if(mFlip) + { + flip(mVersion); + flip(mBinaryVersion); + } + + if (!checkCompatibility(PxU32(mVersion), PxU32(mBinaryVersion))) + { + char buffer[512]; + getCompatibilityVersionsStr(buffer, 512); + + mConvX.displayMessage(PxErrorCode::eINVALID_PARAMETER, + "PxBinaryConverter: data version (%x-%d) is incompatible with this PhysX sdk.\n These versions would be compatible: %s", + mVersion, mBinaryVersion, buffer); + + return false; + } + inputStream.read(&mBuildNumber, 4); + if(mFlip) + flip(mBuildNumber); + + + inputStream.read(&mSizeOfPtr, 4); + if(mFlip) + flip(mSizeOfPtr); + + inputStream.read(&mPlatformTag, 4); + if(mFlip) + flip(mPlatformTag); + + if (!Sn::isBinaryPlatformTagValid(PxU32(mPlatformTag))) + { + mConvX.displayMessage(PxErrorCode::eINVALID_PARAMETER, "PxBinaryConverter: Unknown meta data platform tag"); + return false; + } + + inputStream.read(&mGaussMapLimit, 4); + if(mFlip) + flip(mGaussMapLimit); + + inputStream.read(&mNbEntries, 4); + if(mFlip) + flip(mNbEntries); + + mEntries = PX_NEW(PxMetaDataEntry)[PxU32(mNbEntries)]; + if(mSizeOfPtr==8) + { + for(int i=0;i(size_t(tmp.mType)); + mEntries[i].mName = reinterpret_cast(size_t(tmp.mName)); + mEntries[i].mOffset = tmp.mOffset; + mEntries[i].mSize = tmp.mSize; + mEntries[i].mCount = tmp.mCount; + mEntries[i].mOffsetSize = tmp.mOffsetSize; + mEntries[i].mFlags = tmp.mFlags; + mEntries[i].mAlignment = tmp.mAlignment; + } + } + else + { + assert(mSizeOfPtr==4); +// inputStream.read(mEntries, mNbEntries*sizeof(PxMetaDataEntry)); + for(int i=0;i(size_t(tmp.mType)); + mEntries[i].mName = reinterpret_cast(size_t(tmp.mName)); + mEntries[i].mOffset = tmp.mOffset; + mEntries[i].mSize = tmp.mSize; + mEntries[i].mCount = tmp.mCount; + mEntries[i].mOffsetSize = tmp.mOffsetSize; + mEntries[i].mFlags = tmp.mFlags; + mEntries[i].mAlignment = tmp.mAlignment; + } + } + + if(mFlip) + { + for(int i=0;i(PxConcreteType::Enum(concreteType), nameOffset) ); + } + + int tableSize; + inputStream.read(&tableSize, 4); + if(mFlip) + flip(tableSize); + + mStringTable = reinterpret_cast(PX_ALLOC(sizeof(char)*tableSize, "MetaData StringTable")); + inputStream.read(mStringTable, PxU32(tableSize)); + } + + // Register atomic types + { + addNewClass("bool", 1, NULL, &ConvX::convert8); + addNewClass("char", 1, NULL, &ConvX::convert8); + addNewClass("short", 2, NULL, &ConvX::convert16); + addNewClass("int", 4, NULL, &ConvX::convert32); + addNewClass("PxU64", 8, NULL, &ConvX::convert64); + addNewClass("float", 4, NULL, &ConvX::convertFloat); + + addNewClass("paddingByte", 1, NULL, &ConvX::convertPad8); + } + + { + MetaClass* currentClass = NULL; + for(int i=0;i %s\n", mEntries[i].mName, mEntries[i].mType); + MetaClass* mc = getMetaClass(mEntries[i].mName); + if(mc) + addNewClass(mEntries[i].mType, mc->mSize, mc, mc->mCallback); + else + mConvX.displayMessage(PxErrorCode::eINTERNAL_ERROR, + "PxBinaryConverter: Invalid typedef - Missing metadata for: %s, please check the source metadata.\n" + , mEntries[i].mName); + } + else if(mEntries[i].mFlags & PxMetaDataFlag::eCLASS) + { + if(!mEntries[i].mName) + { + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, "Found class: %s\n", mEntries[i].mType); + currentClass = addNewClass(mEntries[i].mType, mEntries[i].mSize); + + if(mEntries[i].mFlags & PxMetaDataFlag::eVIRTUAL) + { + PxMetaDataEntry vtable; + vtable.mType = gVTablePtr; + vtable.mName = gVTablePtr; + vtable.mOffset = 0; + vtable.mSize = mSizeOfPtr; + vtable.mCount = 1; + vtable.mFlags = PxMetaDataFlag::ePTR; + currentClass->mFields.pushBack(vtable); + } + } + else + { + assert(currentClass); + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, " - inherits from: %s\n", mEntries[i].mName); + currentClass->mBaseClasses.pushBack(mEntries[i]); + } + } + else + { + const int isUnion = mEntries[i].mFlags & PxMetaDataFlag::eUNION; + + if(isUnion && !mEntries[i].mSize) + { + mConvX.registerUnionType(mEntries[i].mType, mEntries[i].mName, mEntries[i].mOffset); + } + else + { + if(isUnion) + { + mConvX.registerUnion(mEntries[i].mType); + } + + const int isPadding = mEntries[i].mFlags & PxMetaDataFlag::ePADDING; + + assert(currentClass); +#ifdef REMOVE_EXPLICIT_PADDING + if(!isPadding) +#endif + currentClass->mFields.pushBack(mEntries[i]); + + if(isPadding) + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, + " - contains padding: %s - %s\n", mEntries[i].mType, mEntries[i].mName); + else if(mEntries[i].mFlags & PxMetaDataFlag::eEXTRA_DATA) + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, + " - contains extra data: %s%s\n", mEntries[i].mType, mEntries[i].mFlags & PxMetaDataFlag::ePTR ? "*" : ""); + else + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, + " - contains field: %s%s\n", mEntries[i].mType, mEntries[i].mFlags & PxMetaDataFlag::ePTR ? "*" : ""); + + } + } + } + } + + // Sort classes by depth + struct Local + { + static bool _computeDepth(const MetaData& md, MetaClass* current, int currentDepth, int& maxDepth) + { + if(currentDepth>maxDepth) + maxDepth = currentDepth; + + PxU32 nbBases = current->mBaseClasses.size(); + for(PxU32 i=0;imBaseClasses[i]; + MetaClass* baseClass = md.getMetaClass(baseClassEntry.mName); + if(!baseClass) + { + md.mConvX.displayMessage(PxErrorCode::eINTERNAL_ERROR, + "PxBinaryConverter: Can't find class %s metadata, please check the source metadata.\n", baseClassEntry.mName); + return false; + } + if (!_computeDepth(md, baseClass, currentDepth+1, maxDepth)) + return false; + } + return true; + } + + static int compareClasses(const void* c0, const void* c1) + { + MetaClass** mc0 = reinterpret_cast(const_cast(c0)); + MetaClass** mc1 = reinterpret_cast(const_cast(c1)); +// return (*mc0)->mSize - (*mc1)->mSize; + return (*mc0)->mDepth - (*mc1)->mDepth; + } + + static int compareEntries(const void* c0, const void* c1) + { + PxMetaDataEntry* mc0 = reinterpret_cast(const_cast(c0)); + PxMetaDataEntry* mc1 = reinterpret_cast(const_cast(c1)); + //mOffset is used to access control information for extra data, and not for offsets of the data itself. + assert(!(mc0->mFlags & PxMetaDataFlag::eEXTRA_DATA)); + assert(!(mc1->mFlags & PxMetaDataFlag::eEXTRA_DATA)); + return mc0->mOffset - mc1->mOffset; + } + }; + { + // Compute depths + const PxU32 nbMetaClasses = mMetaClasses.size(); + for(PxU32 i=0;imDepth = maxDepth; + } + + // Sort by depth + MetaClass** metaClasses = &mMetaClasses[0]; + qsort(metaClasses, size_t(nbMetaClasses), sizeof(MetaClass*), Local::compareClasses); + } + + // Replicate fields from base classes + { + PxU32 nbMetaClasses = mMetaClasses.size(); + for(PxU32 k=0;kmBaseClasses.size(); + + // merge entries of base classes and current class in the right order + // this is needed for extra data ordering, which is not covered by the mOffset sort + // in the next stage below + PsArray mergedEntries; + + for(PxU32 i=0;imBaseClasses[i]; + MetaClass* baseClass = getMetaClass(baseClassEntry.mName); + assert(baseClass); + assert(baseClass->mBaseClasses.size()==0 || baseClass->mProcessed); + + PxU32 nbBaseFields = baseClass->mFields.size(); + for(PxU32 j=0;jmFields[j]; + // Don't merge primary v-tables to avoid redundant v-table entries. + // It means the base v-table won't be inherited & needs to be explicitly defined in the metadata. Seems reasonable. + // Could be done better though. + + if(f.mType==gVTablePtr && !f.mOffset && !baseClassEntry.mOffset) + continue; + + f.mOffset += baseClassEntry.mOffset; + mergedEntries.pushBack(f); + } + current->mProcessed = true; + } + + //append current fields to base class fields + for (PxU32 i = 0; i < current->mFields.size(); i++) + { + mergedEntries.pushBack(current->mFields[i]); + } + current->mFields.clear(); + current->mFields.assign(mergedEntries.begin(), mergedEntries.end()); + } + } + + // Check classes + { + PxU32 nbMetaClasses = mMetaClasses.size(); + for(PxU32 i=0;icheck(*this)) + return false; + } + } + + // Sort meta-data by offset + { + PxU32 nbMetaClasses = mMetaClasses.size(); + for(PxU32 i=0;imFields.size(); + if(nbFields<2) + continue; + PxMetaDataEntry* entries = ¤t->mFields[0]; + + PxMetaDataEntry* newEntries = PX_NEW(PxMetaDataEntry)[nbFields]; + PxU32 nb = 0; + for(PxU32 j=0;jmClassName); + + if (mcDst == nullptr) + { + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, "dst is missing meta class %s", mcSrc->mClassName); + } + } + + //find classes missing in src + for (PxU32 i = 0; imClassName); + + if (mcSrc == nullptr) + { + mConvX.displayMessage(PxErrorCode::eDEBUG_INFO, "dst is missing meta class %s", mcSrc->mClassName); + } + } + + //compare classes present in src and dst + for (PxU32 i = 0; imClassName; + MetaClass* mcSrc = getMetaClass(className); + MetaClass* mcDst = dst.getMetaClass(className); + if (mcSrc != nullptr && mcDst != nullptr) + { + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mCallback) + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mMaster) //should be 0 for both anyway + COMPARE_METADATA_STRING_MD(MetaClass, *mcSrc, *mcDst, mClassName) + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mSize) + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mDepth) + + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mBaseClasses.size()) + if (mcSrc->mBaseClasses.size() == mcDst->mBaseClasses.size()) + { + for (PxU32 b = 0; b < mcSrc->mBaseClasses.size(); b++) + { + COMPARE_METADATA_STRING_MD(PxMetaDataEntry, mcSrc->mBaseClasses[b], mcDst->mBaseClasses[b], mName); + } + } + + COMPARE_METADATA_INT_MD(MetaClass, *mcSrc, *mcDst, mFields.size()) + if (mcSrc->mFields.size() == mcDst->mFields.size()) + { + for (PxU32 f = 0; f < mcSrc->mFields.size(); f++) + { + PxMetaDataEntry srcMde = mcSrc->mFields[f]; + PxMetaDataEntry dstMde = mcDst->mFields[f]; + + COMPARE_METADATA_STRING_MD(PxMetaDataEntry, srcMde, dstMde, mType) + COMPARE_METADATA_STRING_MD(PxMetaDataEntry, srcMde, dstMde, mName) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mOffset) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mSize) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mCount) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mOffsetSize) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mFlags) + COMPARE_METADATA_INT_MD(PxMetaDataEntry, srcMde, dstMde, mAlignment) + } + } + } + } + return isEquivalent; +} + +#undef COMPARE_METADATA_BOOL_MD +#undef COMPARE_METADATA_INT_MD +#undef COMPARE_METADATA_STRING_MD + +/////////////////////////////////////////////////////////////////////////////// + +void ConvX::releaseMetaData() +{ + DELETESINGLE(mMetaData_Dst); + DELETESINGLE(mMetaData_Src); +} + +const MetaData* ConvX::loadMetaData(PxInputStream& inputStream, MetaDataType type) +{ + if (type != META_DATA_SRC && type != META_DATA_DST) + { + displayMessage(PxErrorCode::eINTERNAL_ERROR, + "PxBinaryConverter: Wrong meta data type, please check the source metadata.\n"); + return NULL; + } + + PX_ASSERT(type == META_DATA_SRC || type == META_DATA_DST); + + MetaData*& metaDataPtr = (type == META_DATA_SRC) ? mMetaData_Src : mMetaData_Dst; + metaDataPtr = PX_NEW(MetaData)(*this); + if(!(metaDataPtr)->load(inputStream, type)) + DELETESINGLE(metaDataPtr); + return metaDataPtr; +} + +const MetaData* ConvX::getBinaryMetaData(MetaDataType type) +{ + if(type==META_DATA_SRC) + return mMetaData_Src; + if(type==META_DATA_DST) + return mMetaData_Dst; + PX_ASSERT(0); + return NULL; +} + +int ConvX::getNbMetaClasses(MetaDataType type) +{ + if(type==META_DATA_SRC) + return mMetaData_Src->getNbMetaClasses(); + if(type==META_DATA_DST) + return mMetaData_Dst->getNbMetaClasses(); + PX_ASSERT(0); + return 0; +} + +MetaClass* ConvX::getMetaClass(unsigned int i, MetaDataType type) const +{ + if(type==META_DATA_SRC) + return mMetaData_Src->getMetaClass(i); + if(type==META_DATA_DST) + return mMetaData_Dst->getMetaClass(i); + PX_ASSERT(0); + return NULL; +} + +MetaClass* ConvX::getMetaClass(const char* name, MetaDataType type) const +{ + if(type==META_DATA_SRC) + return mMetaData_Src->getMetaClass(name); + if(type==META_DATA_DST) + return mMetaData_Dst->getMetaClass(name); + PX_ASSERT(0); + return NULL; +} + +MetaClass* ConvX::getMetaClass(PxConcreteType::Enum concreteType, MetaDataType type) +{ + MetaClass* metaClass = NULL; + if(type==META_DATA_SRC) + metaClass = mMetaData_Src->getMetaClass(concreteType); + if(type==META_DATA_DST) + metaClass = mMetaData_Dst->getMetaClass(concreteType); + + if(!metaClass) + { + displayMessage(PxErrorCode::eINTERNAL_ERROR, + "PxBinaryConverter: Missing concreteType %d metadata! serialized a class without dumping metadata. Please check the metadata.", + concreteType); + return NULL; + } + + return metaClass; +} + +/////////////////////////////////////////////////////////////////////////////// + +// Peek & poke, yes sir. +PxU64 physx::Sn::peek(int size, const char* buffer, int flags) +{ + const int maskMSB = flags & PxMetaDataFlag::eCOUNT_MASK_MSB; + const int skipIfOne = flags & PxMetaDataFlag::eCOUNT_SKIP_IF_ONE; + switch(size) + { + case 1: + { + unsigned char value = *(reinterpret_cast(buffer)); + if(maskMSB) + value &= 0x7f; + if(skipIfOne && value==1) + return 0; + return PxU64(value); + } + case 2: + { + unsigned short value = *(reinterpret_cast(buffer)); + if(maskMSB) + value &= 0x7fff; + if(skipIfOne && value==1) + return 0; + return PxU64(value); + } + case 4: + { + unsigned int value = *(reinterpret_cast(buffer)); + if(maskMSB) + value &= 0x7fffffff; + if(skipIfOne && value==1) + return 0; + return PxU64(value); + } + case 8: + { + PxU64 value = *(reinterpret_cast(buffer)); + if(maskMSB) + value &= (PxU64(-1))>>1; + if(skipIfOne && value==1) + return 0; + return value; + } + }; + PX_ASSERT(0); + return PxU64(-1); +} + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h new file mode 100644 index 000000000..8b1e5a1b6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h @@ -0,0 +1,188 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PX_CONVX_METADATA_H +#define PX_CONVX_METADATA_H + +#include "SnConvX_Output.h" +#include "PxMetaDataFlags.h" + +namespace physx { namespace Sn { + +#if PX_VC +#pragma warning (push) +#pragma warning (disable : 4371) //layout of class may have changed from a previous version of the compiler due to better packing of member +#endif + + + // PT: beware, must match corresponding structure in PxMetaData.h + struct PxMetaDataEntry : public shdfnd::UserAllocated + { + PxMetaDataEntry() + { + memset(this, 0, sizeof(*this)); + } + bool isVTablePtr() const; + + const char* mType; //!< Field type (bool, byte, quaternion, etc) + const char* mName; //!< Field name (appears exactly as in the source file) + int mOffset; //!< Offset from the start of the class (ie from "this", field is located at "this"+Offset) + int mSize; //!< sizeof(Type) + int mCount; //!< Number of items of type Type (0 for dynamic sizes) + int mOffsetSize; //!< Offset of dynamic size param, for dynamic arrays + int mFlags; //!< Field parameters + int mAlignment; //!< Explicit alignment added for DE1340 + }; + + struct MetaDataEntry32 + { + PxI32 mType; //!< Field type (bool, byte, quaternion, etc) + PxI32 mName; //!< Field name (appears exactly as in the source file) + int mOffset; //!< Offset from the start of the class (ie from "this", field is located at "this"+Offset) + int mSize; //!< sizeof(Type) + int mCount; //!< Number of items of type Type (0 for dynamic sizes) + int mOffsetSize; //!< Offset of dynamic size param, for dynamic arrays + int mFlags; //!< Field parameters + int mAlignment; //!< Explicit alignment added for DE1340 + }; + + struct MetaDataEntry64 + { + PxI64 mType; //!< Field type (bool, byte, quaternion, etc) + PxI64 mName; //!< Field name (appears exactly as in the source file) + int mOffset; //!< Offset from the start of the class (ie from "this", field is located at "this"+Offset) + int mSize; //!< sizeof(Type) + int mCount; //!< Number of items of type Type (0 for dynamic sizes) + int mOffsetSize; //!< Offset of dynamic size param, for dynamic arrays + int mFlags; //!< Field parameters + int mAlignment; //!< Explicit alignment added for DE1340 + }; + + struct ExtraDataEntry + { + PxMetaDataEntry entry; + int offset; + }; + + struct ExtraDataEntry2 : ExtraDataEntry + { + ConvertCallback cb; + }; + + class MetaData; + + struct MetaClass : public shdfnd::UserAllocated + { + bool getFieldByType(const char* type, PxMetaDataEntry& entry) const; + bool getFieldByName(const char* name, PxMetaDataEntry& entry) const; + bool check(const MetaData& owner); + + ConvertCallback mCallback; + MetaClass* mMaster; + const char* mClassName; + int mSize; + int mDepth; + PsArray mBaseClasses; + PsArray mFields; + bool mProcessed; + +// int mNbEntries; +// ExtraDataEntry2 mEntries[256]; + + private: + void checkAndCompleteClass(const MetaData& owner, int& startOffset, int& nbBytes); + }; + + enum MetaDataType + { + META_DATA_NONE, + META_DATA_SRC, + META_DATA_DST + }; + + class ConvX; + class MetaData : public shdfnd::UserAllocated + { + public: + MetaData(Sn::ConvX&); + ~MetaData(); + + bool load(PxInputStream& inputStream, MetaDataType type); + + inline_ MetaDataType getType() const { return mType; } + inline_ int getVersion() const { return mVersion; } + inline_ int getBuildNumber() const { return mBuildNumber; } + inline_ int getPtrSize() const { return mSizeOfPtr; } + inline_ int getPlatformTag() const { return mPlatformTag; } + inline_ int getGaussMapLimit() const { return mGaussMapLimit; } + inline_ int getNbMetaClasses() const { return int(mMetaClasses.size()); } + inline_ MetaClass* getMetaClass(unsigned int i) const { return mMetaClasses[i]; } + inline_ bool getFlip() const { return mFlip; } + + MetaClass* getMetaClass(const char* name) const; + MetaClass* getMetaClass(PxConcreteType::Enum concreteType) const; + MetaClass* addNewClass(const char* name, int size, MetaClass* master=NULL, ConvertCallback callback=NULL); + + bool compare(const MetaData& candidate) const; + private: + MetaData& operator=(const MetaData&); + Sn::ConvX& mConvX; + MetaDataType mType; + int mNbEntries; + PxMetaDataEntry* mEntries; + char* mStringTable; + PsArray mMetaClasses; + int mVersion; + int mBinaryVersion; + int mBuildNumber; + int mSizeOfPtr; + int mPlatformTag; + int mGaussMapLimit; + bool mFlip; + + PsArray< Ps::Pair > mConcreteTypeTable; + + inline_ const char* offsetToText(const char* text) const + { + const size_t offset = size_t(text); + const PxU32 offset32 = PxU32(offset); +// if(offset==-1) + if(offset32==0xffffffff) + return NULL; + return mStringTable + offset32; + } + friend struct MetaClass; + }; + + PxU64 peek(int size, const char* buffer, int flags=0); + +#if PX_VC +#pragma warning (pop) +#endif +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp new file mode 100644 index 000000000..d9fb8dada --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp @@ -0,0 +1,451 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "foundation/PxIO.h" +#include "foundation/PxErrorCallback.h" +#include "SnConvX.h" + +#if PX_VC +#pragma warning(disable:4389) // signed/unsigned mismatch +#endif + +using namespace physx; + +void Sn::ConvX::setNullPtr(bool flag) +{ + mNullPtr = flag; +} + +void Sn::ConvX::setNoOutput(bool flag) +{ + mNoOutput = flag; +} + +bool Sn::ConvX::initOutput(PxOutputStream& targetStream) +{ + mOutStream = &targetStream; + + mOutputSize = 0; + mNullPtr = false; + mNoOutput = false; + + const MetaData* srcMetaData = getBinaryMetaData(META_DATA_SRC); + PX_ASSERT(srcMetaData); + const MetaData* dstMetaData = getBinaryMetaData(META_DATA_DST); + PX_ASSERT(dstMetaData); + + mSrcPtrSize = srcMetaData->getPtrSize(); + mDstPtrSize = dstMetaData->getPtrSize(); + + PX_ASSERT(!srcMetaData->getFlip()); + mMustFlip = dstMetaData->getFlip(); + return true; +} + +void Sn::ConvX::closeOutput() +{ + mOutStream = NULL; +} + +int Sn::ConvX::getCurrentOutputSize() +{ + return mOutputSize; +} + +void Sn::ConvX::output(short value) +{ + if(mNoOutput) + return; + + if(mMustFlip) + flip(value); + + PX_ASSERT(mOutStream); + const size_t size = mOutStream->write(&value, 2); + PX_ASSERT(size==2); + mOutputSize += int(size); +} + +void Sn::ConvX::output(int value) +{ + if(mNoOutput) + return; + + if(mMustFlip) + flip(value); + + PX_ASSERT(mOutStream); + const size_t size = mOutStream->write(&value, 4); + PX_ASSERT(size==4); + mOutputSize += int(size); +} + +//ntohll is a macro on apple yosemite +static PxU64 ntohll_internal(const PxU64 value) +{ + union + { + PxU64 ull; + PxU8 c[8]; + } x; + + x.ull = value; + + PxU8 c = 0; + c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c; + c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c; + c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c; + c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c; + + return x.ull; +} + +void Sn::ConvX::output(PxU64 value) +{ + if(mNoOutput) + return; + + if(mMustFlip) +// flip(value); + value = ntohll_internal(value); + + PX_ASSERT(mOutStream); + const size_t size = mOutStream->write(&value, 8); + PX_ASSERT(size==8); + mOutputSize += int(size); +} + +void Sn::ConvX::output(const char* buffer, int nbBytes) +{ + if(mNoOutput) + return; + + if(!nbBytes) + return; + + PX_ASSERT(mOutStream); + const PxU32 size = mOutStream->write(buffer, PxU32(nbBytes)); + PX_ASSERT(size== PxU32(nbBytes)); + mOutputSize += int(size); +} + +void Sn::ConvX::convert8(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==1*entry.mCount); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + const PxU32 size = mOutStream->write(src, PxU32(entry.mSize)); + PX_ASSERT(size== PxU32(entry.mSize)); + mOutputSize += int(size); +} + +// This is called to convert auto-generated "padding bytes" (or so we think). +// We use a special converter to check the input bytes and issue warnings when it doesn't look like padding +void Sn::ConvX::convertPad8(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + (void)src; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize); + PX_ASSERT(entry.mSize==1*entry.mCount); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + // PT: we don't output the source data on purpose, to catch missing meta-data + // sschirm: changed that to 0xcd, so we can mark the output as "having marked pads" + const unsigned char b = 0xcd; + for(int i=0;iwrite(&b, 1); + (void)size; + } + mOutputSize += entry.mSize; +} + +void Sn::ConvX::convert16(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==int(sizeof(short)*entry.mCount)); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + const short* data = reinterpret_cast(src); + for(int i=0;iwrite(&value, sizeof(short)); + PX_ASSERT(size==sizeof(short)); + mOutputSize += int(size); + } +} + +void Sn::ConvX::convert32(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==int(sizeof(int)*entry.mCount)); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + const int* data = reinterpret_cast(src); + for(int i=0;iwrite(&value, sizeof(int)); + PX_ASSERT(size==sizeof(int)); + mOutputSize += int(size); + } +} + +void Sn::ConvX::convert64(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==int(sizeof(PxU64)*entry.mCount)); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + const PxU64* data = reinterpret_cast(src); + for(int i=0;iwrite(&value, sizeof(PxU64)); + PX_ASSERT(size==sizeof(PxU64)); + mOutputSize += int(size); + } +} + +void Sn::ConvX::convertFloat(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==int(sizeof(float)*entry.mCount)); + PX_ASSERT(mOutStream); + PX_ASSERT(entry.mSize==dstEntry.mSize); + + const float* data = reinterpret_cast(src); + for(int i=0;iwrite(&value, sizeof(float)); + PX_ASSERT(size==sizeof(float)); + mOutputSize += int(size); + } +} + +void Sn::ConvX::convertPtr(const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry) +{ + (void)dstEntry; + if(mNoOutput) + return; + + PX_ASSERT(entry.mSize==mSrcPtrSize*entry.mCount); + PX_ASSERT(mOutStream); + + char buffer[16]; + for(int i=0;i(src); + PxU32 value = *data++; + src = reinterpret_cast(data); + + if(mActiveRemap) + { + PxU32 ref; + if(mActiveRemap->getObjectRef(value, ref)) + { + value = ref; + } + else if(value) + { + // value = 0; + //We use the pointer of mName for its length + // PT: on serialization mName is transformed to an index by the name manager, so we should not modify its value. + if(!entry.mName || strcmp(entry.mName, "mName")) + value=0x12345678; + } + } + else + { + //we should only get here during convertReferenceTables to build up the pointer map + PxU32 ref; + if (mRemap.getObjectRef(value, ref)) + { + value = ref; + } + else if(value) + { + const PxU32 remappedRef = 0x80000000 | (mPointerRemapCounter++ +1); + mRemap.setObjectRef(value, remappedRef); + value = remappedRef; + } + } + + if(mMustFlip) + flip(value); + + if(mNullPtr) + value = 0; + + *reinterpret_cast(buffer) = value; + } + else + { + PX_ASSERT(mSrcPtrSize==8); + PX_ASSERT(sizeof(PxU64)==8); + const PxU64* data = reinterpret_cast(src); + PxU64 value = *data++; + src = reinterpret_cast(data); + + if(mActiveRemap) + { + PxU32 ref; + if(mActiveRemap->getObjectRef(value, ref)) + { + value = ref; + } + else if(value) + { + // value = 0; + //We use the pointer of mName for its length + // PT: on serialization mName is transformed to an index by the name manager, so we should not modify its value. + if(!entry.mName || strcmp(entry.mName, "mName")) + value=0x12345678; + } + } + else + { + //we should only get here during convertReferenceTables to build up the pointer map + PxU32 ref; + if (mRemap.getObjectRef(value, ref)) + { + value = ref; + } + else if(value) + { + const PxU32 remappedRef = 0x80000000 | (mPointerRemapCounter++ +1); + mRemap.setObjectRef(value, remappedRef); + value = remappedRef; + } + } + +// PX_ASSERT(!mMustFlip); +// if(mMustFlip) +// flip(value); + + if(mNullPtr) + value = 0; + + testValue = value; + + *reinterpret_cast(buffer) = value; + } + + if(mSrcPtrSize==mDstPtrSize) + { + const size_t size = mOutStream->write(buffer, PxU32(mSrcPtrSize)); + PX_ASSERT(size==PxU32(mSrcPtrSize)); + mOutputSize += int(size); + } + else + { + if(mDstPtrSize>mSrcPtrSize) + { + // 32bit to 64bit + PX_ASSERT(mDstPtrSize==8); + PX_ASSERT(mSrcPtrSize==4); + + // We need to output the lower 32bits first for PC. Might be different on a 64bit console.... + + // Output src ptr for the lower 32bits + const size_t size = mOutStream->write(buffer, PxU32(mSrcPtrSize)); + PX_ASSERT(size==PxU32(mSrcPtrSize)); + mOutputSize += int(size); + + // Output zeros for the higher 32bits + const int zero = 0; + const size_t size0 = mOutStream->write(&zero, 4); + PX_ASSERT(size0==4); + mOutputSize += int(size0); + } + else + { + // 64bit to 32bit + PX_ASSERT(mSrcPtrSize==8); + PX_ASSERT(mDstPtrSize==4); + + // Not sure how we can safely convert 64bit ptrs to 32bit... just drop the high 32 bits?!? + + PxU32 ptr32 = *reinterpret_cast(buffer); + (void)ptr32; + PxU32 ptr32b = PxU32(testValue); + (void)ptr32b; + + if(mMustFlip) + flip(ptr32b); + + // Output src ptr for the lower 32bits + const size_t size = mOutStream->write(&ptr32b, 4); + PX_ASSERT(size==4); + mOutputSize += int(size); + } + } + } +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h new file mode 100644 index 000000000..18d03fc9f --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h @@ -0,0 +1,112 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PX_CONVX_OUTPUT_H +#define PX_CONVX_OUTPUT_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx { namespace Sn { + + struct PxMetaDataEntry; + class ConvX; + + typedef void (Sn::ConvX::*ConvertCallback) (const char* src, const PxMetaDataEntry& entry, const PxMetaDataEntry& dstEntry); + + inline_ void flip(PxI16& v) + { + PxI8* b = reinterpret_cast(&v); + PxI8 temp = b[0]; + b[0] = b[1]; + b[1] = temp; + } + + inline_ void flip(PxU16& v) + { + flip(reinterpret_cast(v)); + } + + inline_ void flip32(PxI8* b) + { + PxI8 temp = b[0]; + b[0] = b[3]; + b[3] = temp; + temp = b[1]; + b[1] = b[2]; + b[2] = temp; + } + + inline_ void flip(PxI32& v) + { + PxI8* b = reinterpret_cast(&v); + flip32(b); + } + + inline_ void flip(PxU32& v) + { + PxI8* b = reinterpret_cast(&v); + flip32(b); + } + + inline_ void flip(PxI64& v) + { + PxI8* b = reinterpret_cast(&v); + + PxI8 temp = b[0]; + b[0] = b[7]; + b[7] = temp; + temp = b[1]; + b[1] = b[6]; + b[6] = temp; + temp = b[2]; + b[2] = b[5]; + b[5] = temp; + temp = b[3]; + b[3] = b[4]; + b[4] = temp; + } + + inline_ void flip(PxF32& v) + { + PxI8* b = reinterpret_cast(&v); + flip32(b); + } + + inline_ void flip(void*& v) + { + PxI8* b = reinterpret_cast(&v); + flip32(b); + } + + inline_ void flip(const PxI8*& v) + { + PxI8* b = const_cast(v); + flip32(b); + } +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp new file mode 100644 index 000000000..d8dc6bdb1 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "SnConvX.h" +#include + +using namespace physx; + +void Sn::ConvX::resetUnions() +{ + mUnions.clear(); +} + +bool Sn::ConvX::registerUnion(const char* name) +{ + displayMessage(PxErrorCode::eDEBUG_INFO, "Registering union: %s\n", name); + + Sn::Union u; + u.mName = name; + + mUnions.pushBack(u); + return true; +} + +bool Sn::ConvX::registerUnionType(const char* unionName, const char* typeName, int typeValue) +{ + const PxU32 nb = mUnions.size(); + for(PxU32 i=0;i mTypes; + }; +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp new file mode 100644 index 000000000..e7ed9d827 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp @@ -0,0 +1,94 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxBase.h" +#include "SnSerializationContext.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Sn; + +PxBase* DeserializationContext::resolveReference(PxU32 kind, size_t reference) const +{ + const InternalRefMap::Entry* entry0 = mInternalReferencesMap.find(InternalRefKey(reference, kind)); + PX_ASSERT(entry0); + SerialObjectIndex objIndex = entry0->second; + bool isExternal; + PxU32 index = objIndex.getIndex(isExternal); + PxBase* base = NULL; + if (isExternal) + { + const ImportReference& entry = mImportReferences[index]; + base = mExternalRefs->find(entry.id); + } + else + { + const ManifestEntry& entry = mManifestTable[index]; + base = reinterpret_cast(mObjectDataAddress + entry.offset); + } + PX_ASSERT(base); + return base; +} + +void SerializationContext::registerReference(PxBase& serializable, PxU32 kind, size_t reference) +{ +#if PX_CHECKED + if ((kind & PX_SERIAL_REF_KIND_PTR_TYPE_BIT) == 0 && reference > 0xffffffff) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxSerializationContext::registerReference: only 32 bit indices supported."); + return; + } +#endif + + bool isExternal = mExternalRefs && mExternalRefs->contains(serializable); + PxU32 index; + if (isExternal) + { + PxSerialObjectId id = mExternalRefs->getId(serializable); + PX_ASSERT(id != PX_SERIAL_OBJECT_ID_INVALID); + if (const Ps::HashMap::Entry* entry = mImportReferencesMap.find(id)) + { + index = entry->second; + } + else + { + index = mImportReferences.size(); + mImportReferencesMap.insert(id, index); + mImportReferences.pushBack(ImportReference(id, serializable.getConcreteType())); + } + } + else + { + PX_ASSERT(mCollection.contains(serializable)); + index = mObjToCollectionIndexMap[&serializable]; + } + + InternalRefMap& targetMap = (kind & PX_SERIAL_REF_KIND_PTR_TYPE_BIT) ? mInternalReferencesPtrMap : mInternalReferencesIdxMap; + targetMap[InternalRefKey(reference, kind)] = SerialObjectIndex(index, isExternal); +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h new file mode 100644 index 000000000..624b50f79 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h @@ -0,0 +1,303 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SN_SERIALIZATION_CONTEXT +#define PX_PHYSICS_SN_SERIALIZATION_CONTEXT + +#include "foundation/PxAssert.h" +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "PsHash.h" +#include "PsUserAllocated.h" +#include "PxSerialFramework.h" +#include "CmCollection.h" +#include "CmUtils.h" +#include "PxDefaultStreams.h" +#include "PsFoundation.h" +#include "SnConvX_Align.h" + +namespace physx +{ + namespace Sn + { + + struct ManifestEntry + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_FORCE_INLINE ManifestEntry(PxU32 _offset, PxType _type) + { + Cm::markSerializedMem(this, sizeof(ManifestEntry)); + offset = _offset; + type = _type; + } + PX_FORCE_INLINE ManifestEntry() { Cm::markSerializedMem(this, sizeof(ManifestEntry)); } + PX_FORCE_INLINE void operator =(const ManifestEntry& m) + { + PxMemCopy(this, &m, sizeof(ManifestEntry)); + } + + PxU32 offset; + PxType type; + }; + + struct ImportReference + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_FORCE_INLINE ImportReference(PxSerialObjectId _id, PxType _type) + { + Cm::markSerializedMem(this, sizeof(ImportReference)); + id = _id; + type = _type; + } + PX_FORCE_INLINE ImportReference() { Cm::markSerializedMem(this, sizeof(ImportReference)); } + PX_FORCE_INLINE void operator =(const ImportReference& m) + { + PxMemCopy(this, &m, sizeof(ImportReference)); + } + PxSerialObjectId id; + PxType type; + }; + +#define SERIAL_OBJECT_INDEX_TYPE_BIT (1u<<31) + struct SerialObjectIndex + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_FORCE_INLINE SerialObjectIndex(PxU32 index, bool external) { setIndex(index, external); } + PX_FORCE_INLINE SerialObjectIndex(const SerialObjectIndex& objIndex) : mObjIndex(objIndex.mObjIndex) {} + PX_FORCE_INLINE SerialObjectIndex() : mObjIndex(PX_INVALID_U32) {} + + PX_FORCE_INLINE void setIndex(PxU32 index, bool external) + { + PX_ASSERT((index & SERIAL_OBJECT_INDEX_TYPE_BIT) == 0); + mObjIndex = index | (external ? SERIAL_OBJECT_INDEX_TYPE_BIT : 0); + } + + PX_FORCE_INLINE PxU32 getIndex(bool& isExternal) + { + PX_ASSERT(mObjIndex != PX_INVALID_U32); + isExternal = (mObjIndex & SERIAL_OBJECT_INDEX_TYPE_BIT) > 0; + return mObjIndex & ~SERIAL_OBJECT_INDEX_TYPE_BIT; + } + + private: + PxU32 mObjIndex; + }; + + struct ExportReference + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_FORCE_INLINE ExportReference(PxSerialObjectId _id, SerialObjectIndex _objIndex) + { + Cm::markSerializedMem(this, sizeof(ExportReference)); + id = _id; + objIndex = _objIndex; + } + PX_FORCE_INLINE ExportReference() { Cm::markSerializedMem(this, sizeof(ExportReference)); } + PX_FORCE_INLINE void operator =(const ExportReference& m) + { + PxMemCopy(this, &m, sizeof(ExportReference)); + } + PxSerialObjectId id; + SerialObjectIndex objIndex; + }; + + template + struct InternalReference + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + PX_FORCE_INLINE InternalReference(ReferenceType _reference, PxU32 _kind, SerialObjectIndex _objIndex) + { + Cm::markSerializedMem(this, sizeof(InternalReference)); + reference = _reference; + kind = _kind; + objIndex = _objIndex; + } + PX_FORCE_INLINE InternalReference() { Cm::markSerializedMem(this, sizeof(InternalReference)); } + PX_FORCE_INLINE void operator =(const InternalReference& m) + { + PxMemCopy(this, &m, sizeof(InternalReference)); + } + ReferenceType reference; + PxU32 kind; + SerialObjectIndex objIndex; + }; + + typedef InternalReference InternalReferencePtr; + typedef InternalReference InternalReferenceIdx; + + typedef shdfnd::Pair InternalRefKey; + typedef Cm::CollectionHashMap InternalRefMap; + + class DeserializationContext : public PxDeserializationContext, public Ps::UserAllocated + { + PX_NOCOPY(DeserializationContext) + + public: + DeserializationContext(const ManifestEntry* manifestTable, + const ImportReference* importReferences, + PxU8* objectDataAddress, + const InternalRefMap& internalReferencesMap, + const Cm::Collection* externalRefs, + PxU8* extraData, + PxU32 physxVersion) + : mManifestTable(manifestTable) + , mImportReferences(importReferences) + , mObjectDataAddress(objectDataAddress) + , mInternalReferencesMap(internalReferencesMap) + , mExternalRefs(externalRefs) + , mPhysXVersion(physxVersion) + { + mExtraDataAddress = extraData; + } + + virtual PxBase* resolveReference(PxU32 kind, size_t reference) const; + + PxU32 getPhysXVersion() const { return mPhysXVersion; } + private: + //various pointers to deserialized data + const ManifestEntry* mManifestTable; + const ImportReference* mImportReferences; + PxU8* mObjectDataAddress; + + //internal references map for resolving references. + const InternalRefMap& mInternalReferencesMap; + + //external collection for resolving import references. + const Cm::Collection* mExternalRefs; + const PxU32 mPhysXVersion; + }; + + class SerializationContext : public PxSerializationContext, public Ps::UserAllocated + { + PX_NOCOPY(SerializationContext) + public: + SerializationContext(const Cm::Collection& collection, const Cm::Collection* externalRefs) + : mCollection(collection) + , mExternalRefs(externalRefs) + { + // fill object to collection index map (same ordering as manifest) + for (PxU32 i=0;i 0) + { + mMemStream.write(buf, bytesToPad < BUFSIZE ? PxU32(bytesToPad) : PxU32(BUFSIZE)); + bytesToPad -= BUFSIZE; + } + PX_ASSERT(!getPadding(getTotalStoredSize(), alignment)); + } + + virtual void writeName(const char*) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, + "Cannot export names during exportData."); + } + + const PxCollection& getCollection() const { return mCollection; } + + virtual void registerReference(PxBase& serializable, PxU32 kind, size_t reference); + + const Ps::Array& getImportReferences() { return mImportReferences; } + InternalRefMap& getInternalReferencesPtrMap() { return mInternalReferencesPtrMap; } + InternalRefMap& getInternalReferencesIdxMap() { return mInternalReferencesIdxMap; } + + PxU32 getSize() const { return mMemStream.getSize(); } + PxU8* getData() const { return mMemStream.getData(); } + + + + private: + //import reference map for unique registration of import references and corresponding buffer. + Ps::HashMap mImportReferencesMap; + Ps::Array mImportReferences; + + //maps for unique registration of internal references + InternalRefMap mInternalReferencesPtrMap; + InternalRefMap mInternalReferencesIdxMap; + + //map for quick lookup of manifest index. + Ps::HashMap mObjToCollectionIndexMap; + + //collection and externalRefs collection for assigning references. + const Cm::Collection& mCollection; + const Cm::Collection* mExternalRefs; + + PxDefaultMemoryOutputStream mMemStream; + + }; + + } // namespace Sn +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h b/src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h new file mode 100644 index 000000000..a4a08373c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h @@ -0,0 +1,85 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SN_FILE_H +#define SN_FILE_H + +// fopen_s - returns 0 on success, non-zero on failure + +#if PX_MICROSOFT_FAMILY + +#include + +namespace physx +{ +namespace sn +{ +PX_INLINE PxI32 fopen_s(FILE** file, const char* name, const char* mode) +{ + static const PxU32 MAX_LEN = 300; + char buf[MAX_LEN+1]; + + PxU32 i; + for(i = 0; i + +namespace physx +{ +namespace sn +{ +PX_INLINE PxI32 fopen_s(FILE** file, const char* name, const char* mode) +{ + FILE* fp = ::fopen(name, mode); + if(fp) + { + *file = fp; + return PxI32(0); + } + return -1; +} +} // namespace sn +} // namespace physx +#else +#error "Platform not supported!" +#endif + +#endif //SN_FILE_H + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp new file mode 100644 index 000000000..a7c73ce78 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp @@ -0,0 +1,159 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "SnSerialUtils.h" +#include "PsString.h" +#include "PxSerialization.h" +#include "PxPhysicsVersion.h" +#include "PsBasicTemplates.h" + +using namespace physx; + +namespace +{ + +#define SN_NUM_BINARY_PLATFORMS 13 +const PxU32 sBinaryPlatformTags[SN_NUM_BINARY_PLATFORMS] = +{ + PX_MAKE_FOURCC('W','_','3','2'), + PX_MAKE_FOURCC('W','_','6','4'), + PX_MAKE_FOURCC('L','_','3','2'), + PX_MAKE_FOURCC('L','_','6','4'), + PX_MAKE_FOURCC('M','_','3','2'), + PX_MAKE_FOURCC('M','_','6','4'), + PX_MAKE_FOURCC('M','O','C','A'), + PX_MAKE_FOURCC('A','N','D','R'), + PX_MAKE_FOURCC('A','I','O','S'), + PX_MAKE_FOURCC('A','A','6','4'), + PX_MAKE_FOURCC('X','O','N','E'), + PX_MAKE_FOURCC('N','X','3','2'), + PX_MAKE_FOURCC('N','X','6','4') +}; + +const char* sBinaryPlatformNames[SN_NUM_BINARY_PLATFORMS] = +{ + "win32", + "win64", + "linux32", + "linux64", + "macOSX32", + "macOSX64", + "ps4", + "android", + "ios", + "ios64", + "xboxone", + "switch32", + "switch64" +}; + +#define SN_NUM_BINARY_COMPATIBLE_VERSIONS 1 + +// +// Important: if you adjust the following structure, please adjust the comment for PX_BINARY_SERIAL_VERSION as well +// +const Ps::Pair sBinaryCompatibleVersions[SN_NUM_BINARY_COMPATIBLE_VERSIONS] = +{ + Ps::Pair(PX_PHYSICS_VERSION, PX_BINARY_SERIAL_VERSION) +}; + +} + +namespace physx { namespace Sn { + +PxU32 getBinaryPlatformTag() +{ +#if PX_WINDOWS && PX_X86 + return sBinaryPlatformTags[0]; +#elif PX_WINDOWS && PX_X64 + return sBinaryPlatformTags[1]; +#elif PX_LINUX && (PX_X86 || PX_ARM) + return sBinaryPlatformTags[2]; +#elif PX_LINUX && (PX_X64 || PX_A64) + return sBinaryPlatformTags[3]; +#elif PX_OSX && PX_X86 + return sBinaryPlatformTags[4]; +#elif PX_OSX && PX_X64 + return sBinaryPlatformTags[5]; +#elif PX_PS4 + return sBinaryPlatformTags[6]; +#elif PX_ANDROID + return sBinaryPlatformTags[7]; +#elif PX_IOS && PX_ARM + return sBinaryPlatformTags[8]; +#elif PX_IOS && PX_A64 + return sBinaryPlatformTags[9]; +#elif PX_XBOXONE + return sBinaryPlatformTags[10]; +#elif PX_SWITCH && !PX_A64 + return sBinaryPlatformTags[11]; +#elif PX_SWITCH && PX_A64 + return sBinaryPlatformTags[12]; +#else + #error Unknown binary platform +#endif +} + +bool isBinaryPlatformTagValid(physx::PxU32 platformTag) +{ + PxU32 platformIndex = 0; + while (platformIndex < SN_NUM_BINARY_PLATFORMS && platformTag != sBinaryPlatformTags[platformIndex]) platformIndex++; + return platformIndex < SN_NUM_BINARY_PLATFORMS; +} + +const char* getBinaryPlatformName(physx::PxU32 platformTag) +{ + PxU32 platformIndex = 0; + while (platformIndex < SN_NUM_BINARY_PLATFORMS && platformTag != sBinaryPlatformTags[platformIndex]) platformIndex++; + return (platformIndex == SN_NUM_BINARY_PLATFORMS) ? "unknown" : sBinaryPlatformNames[platformIndex]; +} + +bool checkCompatibility(const PxU32 version, const PxU32 binaryVersion) +{ + for(PxU32 i =0; icontains(base))) + return; + if(!required.contains(base)) + required.add(base); + } + + PxCollection& required; + PxCollection& complete; + const PxCollection* external; + PX_NOCOPY(CompleteCallback) + }; + + void getRequiresCollection(PxCollection& required, PxCollection& collection, PxCollection& complete, const PxCollection* external, PxSerializationRegistry& sr, bool followJoints) + { + CompleteCallback callback(required, complete, external); + for (PxU32 i = 0; i < collection.getNbObjects(); ++i) + { + PxBase& s = collection.getObject(i); + const PxSerializer* serializer = sr.getSerializer(s.getConcreteType()); + PX_ASSERT(serializer); + serializer->requiresObjects(s, callback); + + if(followJoints) + { + PxRigidActor* actor = s.is(); + if(actor) + { + Ps::Array objects(actor->getNbConstraints()); + actor->getConstraints(objects.begin(), objects.size()); + + for(PxU32 j=0;j(objects[j]->getExternalReference(typeId)); + if(typeId == PxConstraintExtIDs::eJOINT) + { + const PxSerializer* sj = sr.getSerializer(joint->getConcreteType()); + PX_ASSERT(sj); + sj->requiresObjects(*joint, callback); + if(!required.contains(*joint)) + required.add(*joint); + } + } + } + } + } + } +} + +bool PxSerialization::isSerializable(PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* externalReferences) +{ + PxCollection* subordinateCollection = PxCreateCollection(); + PX_ASSERT(subordinateCollection); + + for(PxU32 i = 0; i < collection.getNbObjects(); ++i) + { + PxBase& s = collection.getObject(i); + const PxSerializer* serializer = sr.getSerializer(s.getConcreteType()); + PX_ASSERT(serializer); + if(serializer->isSubordinate()) + subordinateCollection->add(s); + + if(externalReferences) + { + PxSerialObjectId id = collection.getId(s); + if(id != PX_SERIAL_OBJECT_ID_INVALID) + { + PxBase* object = externalReferences->find(id); + if(object && (object != &s)) + { + subordinateCollection->release(); + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: Reference id %" PX_PRIu64 " used both in current collection and in externalReferences. " + "Please use unique identifiers.", id); + return false; + } + } + } + } + + PxCollection* requiresCollection = PxCreateCollection(); + PX_ASSERT(requiresCollection); + + RequiresCallback requiresCallback0(*requiresCollection); + + for (PxU32 i = 0; i < collection.getNbObjects(); ++i) + { + PxBase& s = collection.getObject(i); + const PxSerializer* serializer = sr.getSerializer(s.getConcreteType()); + PX_ASSERT(serializer); + serializer->requiresObjects(s, requiresCallback0); + + Cm::Collection* cmRequiresCollection = static_cast(requiresCollection); + + for(PxU32 j = 0; j < cmRequiresCollection->getNbObjects(); ++j) + { + PxBase& s0 = cmRequiresCollection->getObject(j); + + if(subordinateCollection->contains(s0)) + { + subordinateCollection->remove(s0); + continue; + } + + bool requiredIsInCollection = collection.contains(s0); + if(!requiredIsInCollection) + { + if(externalReferences) + { + if(!externalReferences->contains(s0)) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: Object of type %s references a missing object of type %s. " + "The missing object needs to be added to either the current collection or the externalReferences collection.", + s.getConcreteTypeName(), s0.getConcreteTypeName()); + } + else if(externalReferences->getId(s0) == PX_SERIAL_OBJECT_ID_INVALID) + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: Object of type %s in externalReferences collection requires an id.", + s0.getConcreteTypeName()); + } + else + continue; + } + else + { + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: Object of type %s references a missing serial object of type %s. " + "Please completed the collection or specify an externalReferences collection containing the object.", + s.getConcreteTypeName(), s0.getConcreteTypeName()); + } + subordinateCollection->release(); + requiresCollection->release(); + return false; + } + } + cmRequiresCollection->mObjects.clear(); + } + requiresCollection->release(); + + PxU32 numOrphans = subordinateCollection->getNbObjects(); + + for(PxU32 j = 0; j < numOrphans; ++j) + { + PxBase& subordinate = subordinateCollection->getObject(j); + + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: An object of type %s is subordinate but not required " + "by other objects in the collection (orphan). Please remove the object from the collection or add its owner.", + subordinate.getConcreteTypeName()); + } + + subordinateCollection->release(); + + if(numOrphans>0) + return false; + + if(externalReferences) + { + PxCollection* oppositeRequiresCollection = PxCreateCollection(); + PX_ASSERT(oppositeRequiresCollection); + + RequiresCallback requiresCallback(*oppositeRequiresCollection); + + for (PxU32 i = 0; i < externalReferences->getNbObjects(); ++i) + { + PxBase& s = externalReferences->getObject(i); + const PxSerializer* serializer = sr.getSerializer(s.getConcreteType()); + PX_ASSERT(serializer); + serializer->requiresObjects(s, requiresCallback); + + Cm::Collection* cmCollection = static_cast(oppositeRequiresCollection); + + for(PxU32 j = 0; j < cmCollection->getNbObjects(); ++j) + { + PxBase& s0 = cmCollection->getObject(j); + + if(collection.contains(s0)) + { + oppositeRequiresCollection->release(); + Ps::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::isSerializable: Object of type %s in externalReferences references an object " + "of type %s in collection (circular dependency).", + s.getConcreteTypeName(), s0.getConcreteTypeName()); + return false; + } + } + cmCollection->mObjects.clear(); + } + oppositeRequiresCollection->release(); + } + + return true; +} + +void PxSerialization::complete(PxCollection& collection, PxSerializationRegistry& sr, const PxCollection* exceptFor, bool followJoints) +{ + PxCollection* curCollection = PxCreateCollection(); + PX_ASSERT(curCollection); + curCollection->add(collection); + + PxCollection* requiresCollection = PxCreateCollection(); + PX_ASSERT(requiresCollection); + + do + { + getRequiresCollection(*requiresCollection, *curCollection, collection, exceptFor, sr, followJoints); + + collection.add(*requiresCollection); + PxCollection* swap = curCollection; + curCollection = requiresCollection; + requiresCollection = swap; + (static_cast(requiresCollection))->mObjects.clear(); + + }while(curCollection->getNbObjects() > 0); + + requiresCollection->release(); + curCollection->release(); + +} + +void PxSerialization::createSerialObjectIds(PxCollection& collection, const PxSerialObjectId base) +{ + PxSerialObjectId localBase = base; + PxU32 nbObjects = collection.getNbObjects(); + + for (PxU32 i = 0; i < nbObjects; ++i) + { + while(collection.find(localBase)) + { + localBase++; + } + + PxBase& s = collection.getObject(i); + if(PX_SERIAL_OBJECT_ID_INVALID == collection.getId(s)) + { + collection.addId(s, localBase); + localBase++; + } + } +} + +namespace physx { namespace Sn +{ + static PxU32 addToStringTable(physx::shdfnd::Array& stringTable, const char* str) + { + if(!str) + return 0xffffffff; + + PxI32 length = PxI32(stringTable.size()); + const char* table = stringTable.begin(); + const char* start = table; + while(length) + { + if(strcmp(table, str)==0) + return PxU32(table - start); + + const char* saved = table; + while(*table++); + length -= PxU32(table - saved); + PX_ASSERT(length>=0); + } + + const PxU32 offset = stringTable.size(); + + while(*str) + stringTable.pushBack(*str++); + stringTable.pushBack(0); + return offset; + } +} } + +void PxSerialization::dumpBinaryMetaData(PxOutputStream& outputStream, PxSerializationRegistry& sr) +{ + class MetaDataStream : public PxOutputStream + { + public: + bool addNewType(const char* typeName) + { + for(PxU32 i=0;i(src); + if(( entry->flags & PxMetaDataFlag::eTYPEDEF) || ((entry->flags & PxMetaDataFlag::eCLASS) && (!entry->name)) ) + newType = addNewType(entry->type); + if(newType) + metaData.pushBack(*entry); + return count; + } + shdfnd::Array metaData; + shdfnd::Array types; + bool newType; + }s; + + SerializationRegistry& sn = static_cast(sr); + sn.getBinaryMetaData(s); + + shdfnd::Array stringTable; + + PxU32 nb = s.metaData.size(); + PxMetaDataEntry* entries = s.metaData.begin(); + for(PxU32 i=0;i(size_t(addToStringTable(stringTable, entries[i].type))); + entries[i].name = reinterpret_cast(size_t(addToStringTable(stringTable, entries[i].name))); + } + + PxU32 platformTag = getBinaryPlatformTag(); + + const PxU32 gaussMapLimit = 32; + + const PxU32 header = PX_MAKE_FOURCC('M','E','T','A'); + const PxU32 version = PX_PHYSICS_VERSION; + const PxU32 binaryVersion = PX_BINARY_SERIAL_VERSION; + const PxU32 ptrSize = sizeof(void*); + PxU32 buildNumber = 0; +#if defined(PX_BUILD_NUMBER) + buildNumber = PX_BUILD_NUMBER; +#endif + outputStream.write(&header, 4); + outputStream.write(&version, 4); + outputStream.write(&binaryVersion, 4); + outputStream.write(&buildNumber, 4); + outputStream.write(&ptrSize, 4); + outputStream.write(&platformTag, 4); + outputStream.write(&gaussMapLimit, 4); + + outputStream.write(&nb, 4); + outputStream.write(entries, nb*sizeof(PxMetaDataEntry)); + + //concreteTypes to name + PxU32 num = sn.getNbSerializers(); + outputStream.write(&num, 4); + for(PxU32 i=0; i Object; + + class Element + { + public: + Object object; + Ps::Array children; + bool isFinished; + + Element(PxBase* obj = NULL) : object(obj, PX_SERIAL_OBJECT_ID_INVALID), isFinished(false) {} + }; + + public: + CollectionSorter(Cm::Collection& collection, Sn::SerializationRegistry& sr, bool isRepx) : mCollection(collection), mSr(sr), mIsRepx(isRepx) {} + virtual ~CollectionSorter(){} + + void process(PxBase& base) + { + addChild(&base); + //ArticulationLink is not a repx serializer, so should require Articulation here + if( mIsRepx && PxConcreteType::eARTICULATION_LINK == base.getConcreteType() ) + { + PxArticulationLink* link = static_cast(&base); + PxBase* a = reinterpret_cast(&link->getArticulation()); + if(mCurElement->object.first != a ) //don't require itself + addChild(a); + } + } + + void sort() + { + Element element; + PxU32 i; + + PxU32 nbObject = mCollection.internalGetNbObjects(); + const Cm::Collection::ObjectToIdMap::Entry* objectdatas = mCollection.internalGetObjects(); + for( i = 0; i < nbObject; ++i ) + { + element.object.first = objectdatas[i].first; + element.object.second = objectdatas[i].second; + mObjToIdMap.insert(objectdatas[i].first, mElements.size()); + mElements.pushBack(element); + } + + for( i = 0; i < nbObject; ++i ) + { + mCurElement = &mElements[i]; + const PxSerializer* serializer = mSr.getSerializer(mCurElement->object.first->getConcreteType()); + PX_ASSERT(serializer); + serializer->requiresObjects(*mCurElement->object.first, *this); + } + + for( i = 0; i < nbObject; ++i ) + { + if( mElements[i].isFinished ) + continue; + + AddElement(mElements[i]); + } + + mCollection.mObjects.clear(); + for(Ps::Array::Iterator o = mSorted.begin(); o != mSorted.end(); ++o ) + { + mCollection.internalAdd(o->first, o->second); + } + } + + void AddElement(Element& e) + { + if( !e.isFinished ) + { + for( Ps::Array::Iterator child = e.children.begin(); child != e.children.end(); ++child ) + { + AddElement(mElements[*child]); + } + mSorted.pushBack(e.object); + e.isFinished = true; + } + } + + private: + PX_INLINE void addChild(PxBase* base) + { + PX_ASSERT(mCurElement); + const Ps::HashMap::Entry* entry = mObjToIdMap.find(base); + if(entry) + mCurElement->children.pushBack(entry->second); + } + + CollectionSorter& operator=(const CollectionSorter&); + Ps::HashMap mObjToIdMap; + Ps::Array mElements; + Cm::Collection& mCollection; + Sn::SerializationRegistry& mSr; + Ps::Array mSorted; + Element* mCurElement; + bool mIsRepx; + }; +} + +namespace physx { namespace Sn { + +SerializationRegistry::SerializationRegistry(PxPhysics& physics) + : mPhysics(physics) +{ + PxRegisterPhysicsSerializers(*this); + Ext::RegisterExtensionsSerializers(*this); + + registerBinaryMetaDataCallback(PxGetPhysicsBinaryMetaData); + registerBinaryMetaDataCallback(Ext::GetExtensionsBinaryMetaData); +} + +SerializationRegistry::~SerializationRegistry() +{ + PxUnregisterPhysicsSerializers(*this); + Ext::UnregisterExtensionsSerializers(*this); + + if(mSerializers.size() > 0) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::release(): some registered PxSerializer instances were not unregistered"); + } + + if(mRepXSerializers.size() > 0) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::release(): some registered PxRepXSerializer instances were not unregistered"); + } +} + +void SerializationRegistry::registerSerializer(PxType type, PxSerializer& serializer) +{ + if(mSerializers.find(type)) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::registerSerializer: Type %d has already been registered", type); + } + + mSerializers.insert(type, &serializer); +} + +PxSerializer* SerializationRegistry::unregisterSerializer(PxType type) +{ + const SerializerMap::Entry* e = mSerializers.find(type); + PxSerializer* s = e ? e->second : NULL; + + if(!mSerializers.erase(type)) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::unregisterSerializer: failed to find PxSerializer instance for type %d", type); + } + return s; +} + +const PxSerializer* SerializationRegistry::getSerializer(PxType type) const +{ + const SerializerMap::Entry* e = mSerializers.find(type); +#if PX_CHECKED + if (!e) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::getSerializer: failed to find PxSerializer instance for type %d", type); + } +#endif + return e ? e->second : NULL; +} + +PxType SerializationRegistry::getSerializerType(PxU32 index) const +{ + PX_ASSERT(index < mSerializers.size()); + return mSerializers.getEntries()[index].first; +} + +const char* SerializationRegistry::getSerializerName(PxU32 index) const +{ + PX_ASSERT(index < mSerializers.size()); + return mSerializers.getEntries()[index].second->getConcreteTypeName(); +} + +void SerializationRegistry::registerBinaryMetaDataCallback(PxBinaryMetaDataCallback callback) +{ + mMetaDataCallbacks.pushBack(callback); +} + +void SerializationRegistry::getBinaryMetaData(PxOutputStream& stream) const +{ + for(PxU32 i = 0; i < mMetaDataCallbacks.size(); i++) + { + mMetaDataCallbacks[i](stream); + } +} + +void SerializationRegistry::registerRepXSerializer(PxType type, PxRepXSerializer& serializer) +{ + if(mRepXSerializers.find(type)) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::registerRepXSerializer: Type %d has already been registered", type); + } + + mRepXSerializers.insert(type, &serializer); +} + +PxRepXSerializer* SerializationRegistry::getRepXSerializer(const char* typeName) const +{ + SerializationRegistry* sr = const_cast(this); + for( RepXSerializerMap::Iterator iter = sr->mRepXSerializers.getIterator(); !iter.done(); ++iter) + { + if ( physx::shdfnd::stricmp( iter->second->getTypeName(), typeName ) == 0 ) + return iter->second; + } + return NULL; +} + +PxRepXSerializer* SerializationRegistry::unregisterRepXSerializer(PxType type) +{ + const RepXSerializerMap::Entry* e = mRepXSerializers.find(type); + PxRepXSerializer* s = e ? e->second : NULL; + + if(!mRepXSerializers.erase(type)) + { + shdfnd::getFoundation().error(physx::PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "PxSerializationRegistry::unregisterRepXSerializer: failed to find PxRepXSerializer instance for type %d", type); + } + return s; +} + +void sortCollection(Cm::Collection& collection, SerializationRegistry& sr, bool isRepx) +{ + CollectionSorter sorter(collection, sr, isRepx); + sorter.sort(); +} + +} // Sn + +PxSerializationRegistry* PxSerialization::createSerializationRegistry(PxPhysics& physics) +{ + return PX_NEW(Sn::SerializationRegistry)(physics); +} + +} // physx + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h new file mode 100644 index 000000000..8c660d294 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h @@ -0,0 +1,91 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SN_SERIALIZATION_REGISTRY +#define PX_PHYSICS_SN_SERIALIZATION_REGISTRY + +#include "PxSerialization.h" +#include "PxRepXSerializer.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "PsArray.h" +#include "PsHashMap.h" + +namespace physx +{ + +namespace Cm { class Collection; } + +namespace Sn { + + class SerializationRegistry : public PxSerializationRegistry, public Ps::UserAllocated + { + public: + SerializationRegistry(PxPhysics& physics); + virtual ~SerializationRegistry(); + + virtual void release(){ PX_DELETE(this); } + + PxPhysics& getPhysics() const { return mPhysics; } + + //binary + void registerSerializer(PxType type, PxSerializer& serializer); + PxSerializer* unregisterSerializer(PxType type); + void registerBinaryMetaDataCallback(PxBinaryMetaDataCallback callback); + void getBinaryMetaData(PxOutputStream& stream) const; + const PxSerializer* getSerializer(PxType type) const; + const char* getSerializerName(PxU32 index) const; + PxType getSerializerType(PxU32 index) const; + PxU32 getNbSerializers() const { return mSerializers.size(); } + //repx + void registerRepXSerializer(PxType type, PxRepXSerializer& serializer); + PxRepXSerializer* getRepXSerializer(const char* typeName) const; + PxRepXSerializer* unregisterRepXSerializer(PxType type); + + protected: + SerializationRegistry &operator=(const SerializationRegistry &); + private: + typedef Ps::CoalescedHashMap SerializerMap; + typedef Ps::HashMap RepXSerializerMap; + + PxPhysics& mPhysics; + SerializerMap mSerializers; + RepXSerializerMap mRepXSerializers; + Ps::Array mMetaDataCallbacks; + }; + + void sortCollection(Cm::Collection& collection, SerializationRegistry& sr, bool isRepx); +} // Sn + +} // physx + + + +#endif + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp new file mode 100644 index 000000000..924740be6 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp @@ -0,0 +1,145 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#include "PxMetaDataObjects.h" +#include "PxExtensionMetaDataObjects.h" +#include "ExtJointMetaDataExtensions.h" +#include "SnJointRepXSerializer.h" + +namespace physx { + + template + inline TJointType* createJoint( PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1 ) + { + PX_UNUSED(physics); + PX_UNUSED(actor0); + PX_UNUSED(actor1); + PX_UNUSED(localFrame0); + PX_UNUSED(localFrame1); + return NULL; + } + + template<> + inline PxD6Joint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxD6JointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxDistanceJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxDistanceJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxContactJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxContactJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxFixedJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxFixedJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxPrismaticJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxPrismaticJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxRevoluteJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxRevoluteJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template<> + inline PxSphericalJoint* createJoint(PxPhysics& physics, + PxRigidActor* actor0, const PxTransform& localFrame0, + PxRigidActor* actor1, const PxTransform& localFrame1) + { + return PxSphericalJointCreate( physics, actor0, localFrame0, actor1, localFrame1 ); + } + + template + PxRepXObject PxJointRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + PxRigidActor* actor0 = NULL; + PxRigidActor* actor1 = NULL; + PxTransform localPose0 = PxTransform(PxIdentity); + PxTransform localPose1 = PxTransform(PxIdentity); + bool ok = true; + if ( inReader.gotoChild( "Actors" ) ) + { + ok = readReference( inReader, *inCollection, "actor0", actor0 ); + ok &= readReference( inReader, *inCollection, "actor1", actor1 ); + inReader.leaveChild(); + } + TJointType* theJoint = !ok ? NULL : createJoint( inArgs.physics, actor0, localPose0, actor1, localPose1 ); + + if ( theJoint ) + { + PxConstraint* constraint = theJoint->getConstraint(); + PX_ASSERT( constraint ); + inCollection->add( *constraint ); + this->fileToObjectImpl( theJoint, inReader, inAllocator, inArgs, inCollection ); + } + return PxCreateRepXObject(theJoint); + } + + template + void PxJointRepXSerializer::objectToFileImpl( const TJointType* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& ) + { + writeAllProperties( inObj, inWriter, inTempBuffer, *inCollection ); + } + + // explicit template instantiations + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; + template struct PxJointRepXSerializer; +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h new file mode 100644 index 000000000..3dd215690 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef SN_JOINT_REPX_SERIALIZER_H +#define SN_JOINT_REPX_SERIALIZER_H +/** \addtogroup RepXSerializers + @{ +*/ + +#include "extensions/PxRepXSimpleType.h" +#include "SnRepXSerializerImpl.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class XmlReader; + class XmlMemoryAllocator; + class XmlWriter; + class MemoryBuffer; + + template + struct PxJointRepXSerializer : public RepXSerializerImpl + { + PxJointRepXSerializer(PxAllocatorCallback& inAllocator) : RepXSerializerImpl(inAllocator) {} + virtual PxRepXObject fileToObject(XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection); + virtual void objectToFileImpl(const TJointType* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs&); + virtual TJointType* allocateObject(PxRepXInstantiationArgs&) { return NULL; } + }; + +#if PX_SUPPORT_EXTERN_TEMPLATE + // explicit template instantiations declarations + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; + extern template struct PxJointRepXSerializer; +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h new file mode 100644 index 000000000..fbc666854 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h @@ -0,0 +1,134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_PXSTREAMOPERATORS_H +#define PX_PXSTREAMOPERATORS_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxBounds3.h" +#include "PxFiltering.h" + +#include "PsString.h" + +namespace physx +{ + static inline PxU32 strLenght( const char* inStr ) + { + return inStr ? PxU32(strlen(inStr)) : 0; + } +} + +namespace physx // ADL requires we put the operators in the same namespace as the underlying type of PxOutputStream +{ + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const char* inString ) + { + if ( inString && *inString ) + { + ioStream.write( inString, PxU32(strlen(inString)) ); + } + return ioStream; + } + + template + inline PxOutputStream& toStream( PxOutputStream& ioStream, const char* inFormat, const TDataType inData ) + { + char buffer[128] = { 0 }; + Ps::snprintf( buffer, 128, inFormat, inData ); + ioStream << buffer; + return ioStream; + } + + struct endl_obj {}; + //static endl_obj endl; + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, bool inData ) { ioStream << (inData ? "true" : "false"); return ioStream; } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxI32 inData ) { return toStream( ioStream, "%d", inData ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU16 inData ) { return toStream( ioStream, "%u", PxU32(inData) ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU8 inData ) { return toStream( ioStream, "%u", PxU32(inData) ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, char inData ) { return toStream( ioStream, "%c", inData ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU32 inData ) { return toStream( ioStream, "%u", inData ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxU64 inData ) { return toStream( ioStream, "%" PX_PRIu64, inData ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const void* inData ) { return ioStream << static_cast(reinterpret_cast(inData)); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxF32 inData ) { return toStream( ioStream, "%g", PxF64(inData) ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, PxF64 inData ) { return toStream( ioStream, "%g", inData ); } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, endl_obj) { return ioStream << "\n"; } + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxVec3& inData ) + { + ioStream << inData[0]; + ioStream << " "; + ioStream << inData[1]; + ioStream << " "; + ioStream << inData[2]; + return ioStream; + } + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxQuat& inData ) + { + ioStream << inData.x; + ioStream << " "; + ioStream << inData.y; + ioStream << " "; + ioStream << inData.z; + ioStream << " "; + ioStream << inData.w; + return ioStream; + } + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxTransform& inData ) + { + ioStream << inData.q; + ioStream << " "; + ioStream << inData.p; + return ioStream; + } + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxBounds3& inData ) + { + ioStream << inData.minimum; + ioStream << " "; + ioStream << inData.maximum; + return ioStream; + } + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, const PxFilterData& inData ) + { + ioStream << inData.word0 << " " << inData.word1 << " " << inData.word2 << " " << inData.word3; + return ioStream; + } + + inline PxOutputStream& operator << ( PxOutputStream& ioStream, struct PxMetaDataPlane& inData ) + { + ioStream << inData.normal; + ioStream << " "; + ioStream << inData.distance; + return ioStream; + } +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h new file mode 100644 index 000000000..4c8cd665e --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h @@ -0,0 +1,245 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + DEFINE_REPX_DEFAULT_PROPERTY("PxBoxGeometry.HalfExtents", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphereGeometry.Radius", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.ConvexMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.MeshFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.TriangleMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightField", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.RowScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.ColumnScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightFieldFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Length", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Mass", "1000" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Speed", "10" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFrictionV", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFrictionV", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DirOfAnisotropy", "1 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.FrictionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.RestitutionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.LocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.SimulationFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.QueryFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.ContactOffset", "0.02" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.RestOffset", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Flags", "eSIMULATION_SHAPE|eSCENE_QUERY_SHAPE|eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Mass", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MassSpaceInertiaTensor", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearDamping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularDamping", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MaxAngularVelocity", "7" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SleepEnergyThreshold", "0.005" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ContactReportThreshold", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.RigidDynamicFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ParentPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ChildPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetOrientation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.InternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ExternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.yLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.zLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.lower", "-0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.upper", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Mass", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.MassSpaceInertiaTensor", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.MaxProjectionIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SeparationTolerance", "0.1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.InternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.ExternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eX", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eY", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eZ", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eTWIST", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING1", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING2", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Value", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DrivePosition", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.linear", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.angular", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MinDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MaxDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Tolerance", "0.025" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.DistanceJointFlags", "eMAX_DISTANCE_ENABLED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveVelocity", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveGearRatio", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.RevoluteJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.ContactDistance", "0.01" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Upper", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Lower", "-3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.PrismaticJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.UserData", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.SphericalJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("THEEND", "false" ) diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h new file mode 100644 index 000000000..ed175fc4a --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h @@ -0,0 +1,274 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Length", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Mass", "1000" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Speed", "10" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxBoxGeometry.HalfExtents", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphereGeometry.Radius", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.ConvexMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.MeshFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.TriangleMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightField", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.RowScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.ColumnScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightFieldFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFrictionV", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFrictionV", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DirOfAnisotropy", "1 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.FrictionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.RestitutionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.LocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.SimulationFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.QueryFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.ContactOffset", "0.02" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.RestOffset", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Flags", "eSIMULATION_SHAPE|eSCENE_QUERY_SHAPE|eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Mass", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MassSpaceInertiaTensor", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearDamping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularDamping", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MaxAngularVelocity", "7" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SleepThreshold", "0.005" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ContactReportThreshold", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.RigidDynamicFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ParentPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ChildPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetOrientation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.InternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ExternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.yLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.zLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialSpring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialDamping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.lower", "-0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.upper", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Mass", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.MassSpaceInertiaTensor", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.MaxProjectionIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SeparationTolerance", "0.1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.InternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.ExternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SleepThreshold", "0.005" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eX", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eY", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eZ", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eTWIST", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING1", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING2", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Value", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DrivePosition", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.linear", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.angular", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MinDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MaxDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Tolerance", "0.025" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.DistanceJointFlags", "eMAX_DISTANCE_ENABLED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveVelocity", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveGearRatio", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.RevoluteJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.ContactDistance", "0.01" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Upper", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Lower", "-3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.PrismaticJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.SphericalJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.scale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.bias", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ExternalAcceleration", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DampingCoefficient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SolverFrequency", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SleepLinearVelocity", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("THEEND", "false" ) diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h new file mode 100644 index 000000000..ac656bfad --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h @@ -0,0 +1,313 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Length", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Mass", "1000" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTolerancesScale.Speed", "10" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxBoxGeometry.HalfExtents", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphereGeometry.Radius", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxConvexMeshGeometry.ConvexMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Scale", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.Scale.Rotation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.MeshFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxTriangleMeshGeometry.TriangleMesh", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightField", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.RowScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.ColumnScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxHeightFieldGeometry.HeightFieldFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.DynamicFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.StaticFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.FrictionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxMaterial.RestitutionCombineMode", "eAVERAGE" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.LocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.SimulationFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.QueryFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.ContactOffset", "0.02" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.RestOffset", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Flags", "eSIMULATION_SHAPE|eSCENE_QUERY_SHAPE|eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxShape.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidStatic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.Mass", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MassSpaceInertiaTensor", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.LinearDamping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.AngularDamping", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.MaxAngularVelocity", "7" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SleepThreshold", "0.005" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.ContactReportThreshold", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRigidDynamic.RigidDynamicFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ParentPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ChildPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetOrientation", "0 0 0 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TargetVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.InternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.ExternalCompliance", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.yLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimit.zLimit", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialSpring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TangentialDamping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.SwingLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.lower", "-0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimit.upper", "0.78539816339" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitEnabled", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationJoint.TwistLimitContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.CMassLocalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.Mass", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.MassSpaceInertiaTensor", "1 1 1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.LinearVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulationLink.AngularVelocity", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.MaxProjectionIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SeparationTolerance", "0.1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.InternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.ExternalDriveIterations", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minPositionIters", "4" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SolverIterationCounts.minVelocityIters", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.SleepThreshold", "0.005" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxArticulation.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eX", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eY", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eZ", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eTWIST", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING1", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Motion.eSWING2", "eLOCKED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.LinearLimit.Value", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.TwistLimit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.SwingLimit.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eX.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eY.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eZ.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSWING.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eTWIST.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.ForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.Drive.eSLERP.Flags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DrivePosition", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.linear", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.DriveVelocity.angular", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxD6Joint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxFixedJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MinDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.MaxDistance", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Tolerance", "0.025" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxDistanceJoint.DistanceJointFlags", "eMAX_DISTANCE_ENABLED" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Upper", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.Limit.Lower", "-1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveVelocity", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveForceLimit", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.DriveGearRatio", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.RevoluteJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxRevoluteJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.ContactDistance", "0.01" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Upper", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.Limit.Lower", "-3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.PrismaticJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxPrismaticJoint.ProjectionAngularTolerance", "3.14159" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor0", "8887040" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Actors.actor1", "8887456" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR0", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LocalPose.eACTOR1", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.force", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.BreakForce.torque", "3.40282e+038" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ConstraintFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Restitution", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Spring", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ContactDistance", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.YAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.LimitCone.ZAngle", "1.5708" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.SphericalJointFlags", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxSphericalJoint.ProjectionLinearTolerance", "1e+010" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.Name", "" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ClientBehaviorBits", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.scale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.MotionConstraintScaleBias.bias", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.GlobalPose", "0 0 0 1 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.ExternalAcceleration", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DampingCoefficient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SolverFrequency", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.SleepLinearVelocity", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.InertiaScale", "1" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.FrictionCoefficient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.DragCoefficient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxCloth.CollisionMassScale", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ExternalAcceleration", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ParticleMass", "0.001" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.Restitution", "0.5" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.DynamicFriction", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.StaticFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.SimulationFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.MaxMotionDistance", "0.06" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.RestOffset", "0.004" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ContactOffset", "0.008" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.GridSize", "0.96" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ProjectionPlane", "0 0 1 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ParticleReadDataFlags", "ePOSITION_BUFFER|eFLAGS_BUFFER" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleSystem.ParticleBaseFlags", "eCOLLISION_WITH_DYNAMIC_ACTORS|eENABLED|ePER_PARTICLE_REST_OFFSET|ePER_PARTICLE_COLLISION_CACHE_HINT") + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ActorFlags", "eVISUALIZATION" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.DominanceGroup", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.OwnerClient", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Damping", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ExternalAcceleration", "0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ParticleMass", "0.001" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Restitution", "0.5" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.DynamicFriction", "0.05" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.StaticFriction", "0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.SimulationFilterData", "0 0 0 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.MaxMotionDistance", "0.06" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.RestOffset", "0.004" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ContactOffset", "0.008" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.GridSize", "0.64" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ProjectionPlane", "0 0 1 0" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Stiffness", "20" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.Viscosity", "6" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.RestParticleDistance", "0.02" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ParticleReadDataFlags", "ePOSITION_BUFFER|eFLAGS_BUFFER" ) + DEFINE_REPX_DEFAULT_PROPERTY("PxParticleFluid.ParticleBaseFlags", "eCOLLISION_WITH_DYNAMIC_ACTORS|eENABLED|ePER_PARTICLE_REST_OFFSET|ePER_PARTICLE_COLLISION_CACHE_HINT") + DEFINE_REPX_DEFAULT_PROPERTY("PxAggregate.SelfCollision", "false" ) + DEFINE_REPX_DEFAULT_PROPERTY("THEEND", "false" ) diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h new file mode 100644 index 000000000..56e2a4b0a --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h @@ -0,0 +1,173 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_REPXCOLLECTION_H +#define PX_REPXCOLLECTION_H + +#include "common/PxTolerancesScale.h" +#include "PxRepXSerializer.h" + +namespace physx { namespace Sn { + + struct XmlNode; + + struct RepXCollectionItem + { + PxRepXObject liveObject; + XmlNode* descriptor; + RepXCollectionItem( PxRepXObject inItem = PxRepXObject(), XmlNode* inDescriptor = NULL ) + : liveObject( inItem ) + , descriptor( inDescriptor ) + { + } + }; + + struct RepXDefaultEntry + { + const char* name; + const char* value; + RepXDefaultEntry( const char* pn, const char* val ) : name( pn ), value( val ){} + }; + + /** + * The result of adding an object to the collection. + */ + struct RepXAddToCollectionResult + { + enum Enum + { + Success, + SerializerNotFound, + InvalidParameters, //Null data passed in. + AlreadyInCollection + }; + + PxSerialObjectId collectionId; + Enum result; + + RepXAddToCollectionResult( Enum inResult = Success, const PxSerialObjectId inId = 0 ) + : collectionId( inId ) + , result( inResult ) + { + } + bool isValid() { return result == Success && collectionId != 0; } + }; + /** + * A RepX collection contains a set of static data objects that can be transformed + * into live objects. It uses RepX serializer to do two transformations: + * live object <-> collection object (descriptor) + * collection object <-> file system. + * + * A live object is considered to be something live in the physics + * world such as a material or a rigidstatic. + * + * A collection object is a piece of data from which a live object + * of identical characteristics can be created. + * + * Clients need to pass PxCollection so that objects can resolve + * references. In addition, objects must be added in an order such that + * references can be resolved in the first place. So objects must be added + * to the collection *after* objects they are dependent upon. + * + * When deserializing from a file, the collection will allocate char*'s that will + * not be freed when the collection itself is freed. The user must be responsible + * for these character allocations. + */ + class RepXCollection + { + protected: + virtual ~RepXCollection(){} + + public: + virtual void destroy() = 0; + + /** + * Set the scale on this collection. The scale is saved with the collection. + * + * If the scale wasn't set, it will be invalid. + */ + virtual void setTolerancesScale( const PxTolerancesScale& inScale ) = 0; + + /** + * Get the scale that was set at collection creation time or at load time. + * If this is a loaded file and the source data does not contain a scale + * this value will be invalid (PxTolerancesScale::isValid()). + */ + virtual PxTolerancesScale getTolerancesScale() const = 0; + + /** + * Set the up vector on this collection. The up vector is saved with the collection. + * + * If the up vector wasn't set, it will be (0,0,0). + */ + virtual void setUpVector( const PxVec3& inUpVector ) = 0; + + /** + * If the up vector wasn't set, it will be (0,0,0). Else this will be the up vector + * optionally set when the collection was created. + */ + virtual PxVec3 getUpVector() const = 0; + + virtual const char* getVersion() = 0; + static const char* getLatestVersion(); + + //Necessary accessor functions for translation/upgrading. + virtual const RepXCollectionItem* begin() const = 0; + virtual const RepXCollectionItem* end() const = 0; + + + //Performs a deep copy of the repx node. + virtual XmlNode* copyRepXNode( const XmlNode* srcNode ) = 0; + + virtual void addCollectionItem( RepXCollectionItem inItem ) = 0; + + //Create a new repx node with this name. Its value is unset. + virtual XmlNode& createRepXNode( const char* name ) = 0; + + virtual RepXCollection& createCollection( const char* inVersionStr ) = 0; + //Release this when finished. + virtual XmlReaderWriter& createNodeEditor() = 0; + + virtual PxAllocatorCallback& getAllocator() = 0; + + virtual bool instantiateCollection( PxRepXInstantiationArgs& inArgs, PxCollection& inPxCollection ) = 0; + + + virtual RepXAddToCollectionResult addRepXObjectToCollection( const PxRepXObject& inObject, PxCollection* inCollection, PxRepXInstantiationArgs& inArgs ) = 0; + + /** + * Save this collection out to a file stream. Uses the RepX serialize to perform + * collection object->file conversions. + * + * /param[in] inStream Write-only stream to save collection out to. + */ + virtual void save( PxOutputStream& inStream ) = 0; + }; +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp new file mode 100644 index 000000000..eb0ab6e8c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp @@ -0,0 +1,553 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPhysicsAPI.h" +#include "PxMetaDataObjects.h" +#include "CmIO.h" +#include "SnPxStreamOperators.h" +#include "PsUtilities.h" +#include "SnXmlImpl.h" +#include "SnXmlSerializer.h" +#include "SnXmlDeserializer.h" +#include "SnRepXCoreSerializer.h" + +using namespace physx::Sn; +namespace physx { + typedef PxReadOnlyPropertyInfo TIncomingJointPropType; + + //************************************************************* + // Actual RepXSerializer implementations for PxMaterial + //************************************************************* + PxMaterial* PxMaterialRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs ) + { + return inArgs.physics.createMaterial(0, 0, 0); + } + + PxRepXObject PxShapeRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + PxProfileAllocatorWrapper wrapper( inAllocator.getAllocator() ); + TReaderNameStack names( wrapper ); + PxProfileArray contexts( wrapper ); + bool hadError = false; + RepXVisitorReader theVisitor( names, contexts, inArgs, inReader, NULL, inAllocator, *inCollection, hadError ); + + Ps::Array materials; + PxGeometry* geometry = NULL; + parseShape( theVisitor, geometry, materials ); + if(hadError) + return PxRepXObject(); + PxShape *theShape = inArgs.physics.createShape( *geometry, materials.begin(), Ps::to16(materials.size()) ); + + switch(geometry->getType()) + { + case PxGeometryType::eSPHERE : + static_cast(geometry)->~PxSphereGeometry(); + break; + case PxGeometryType::ePLANE : + static_cast(geometry)->~PxPlaneGeometry(); + break; + case PxGeometryType::eCAPSULE : + static_cast(geometry)->~PxCapsuleGeometry(); + break; + case PxGeometryType::eBOX : + static_cast(geometry)->~PxBoxGeometry(); + break; + case PxGeometryType::eCONVEXMESH : + static_cast(geometry)->~PxConvexMeshGeometry(); + break; + case PxGeometryType::eTRIANGLEMESH : + static_cast(geometry)->~PxTriangleMeshGeometry(); + break; + case PxGeometryType::eHEIGHTFIELD : + static_cast(geometry)->~PxHeightFieldGeometry(); + break; + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ASSERT(0); + } + inAllocator.getAllocator().deallocate(geometry); + + bool ret = readAllProperties( inArgs, inReader, theShape, inAllocator, *inCollection ); + + return ret ? PxCreateRepXObject(theShape) : PxRepXObject(); + } + + //************************************************************* + // Actual RepXSerializer implementations for PxTriangleMesh + //************************************************************* + + template + inline void writeTriangle( MemoryBuffer& inTempBuffer, const Triangle& inTriangle ) + { + inTempBuffer << inTriangle.mIdx0 + << " " << inTriangle.mIdx1 + << " " << inTriangle.mIdx2; + } + + PxU32 materialAccess( const PxTriangleMesh* inMesh, PxU32 inIndex ) { return inMesh->getTriangleMaterialIndex( inIndex ); } + template + void writeDatatype( MemoryBuffer& inTempBuffer, const TDataType& inType ) { inTempBuffer << inType; } + + void PxBVH33TriangleMeshRepXSerializer::objectToFileImpl( const PxBVH33TriangleMesh* mesh, PxCollection* /*inCollection*/, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs ) + { + bool hasMatIndex = mesh->getTriangleMaterialIndex(0) != 0xffff; + PxU32 numVertices = mesh->getNbVertices(); + const PxVec3* vertices = mesh->getVertices(); + writeBuffer( inWriter, inTempBuffer, 2, vertices, numVertices, "Points", writePxVec3 ); + bool isU16 = mesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false; + PxU32 triCount = mesh->getNbTriangles(); + const void* indices = mesh->getTriangles(); + if ( isU16 ) + writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast* >( indices ), triCount, "Triangles", writeTriangle ); + else + writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast* >( indices ), triCount, "Triangles", writeTriangle ); + if ( hasMatIndex ) + writeBuffer( inWriter, inTempBuffer, 6, mesh, materialAccess, triCount, "materialIndices", writeDatatype ); + + //Cooked stream + PxTriangleMeshDesc meshDesc; + meshDesc.points.count = numVertices; + meshDesc.points.data = vertices; + meshDesc.points.stride = sizeof(PxVec3); + meshDesc.triangles.count = triCount; + meshDesc.triangles.data = indices; + meshDesc.triangles.stride = isU16?3*sizeof(PxU16):3*sizeof(PxU32); + + if(isU16) + { + meshDesc.triangles.stride = sizeof(PxU16)*3; + meshDesc.flags |= PxMeshFlag::e16_BIT_INDICES; + } + else + { + meshDesc.triangles.stride = sizeof(PxU32)*3; + } + + if(hasMatIndex) + { + PxMaterialTableIndex* materialIndices = new PxMaterialTableIndex[triCount]; + for(PxU32 i = 0; i < triCount; i++) + materialIndices[i] = mesh->getTriangleMaterialIndex(i); + + meshDesc.materialIndices.data = materialIndices; + meshDesc.materialIndices.stride = sizeof(PxMaterialTableIndex); + + } + + if(inArgs.cooker != NULL) + { + TMemoryPoolManager theManager(mAllocator); + MemoryBuffer theTempBuf( &theManager ); + theTempBuf.clear(); + inArgs.cooker->cookTriangleMesh( meshDesc, theTempBuf ); + + writeBuffer( inWriter, inTempBuffer, 16, theTempBuf.mBuffer, theTempBuf.mWriteOffset, "CookedData", writeDatatype ); + } + + delete []meshDesc.materialIndices.data; + } + + PxRepXObject PxBVH33TriangleMeshRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* /*inCollection*/ ) + { + //We can't do a simple inverse; we *have* to cook data to get a mesh. + PxTriangleMeshDesc theDesc; + readStridedBufferProperty( inReader, "points", theDesc.points, inAllocator); + readStridedBufferProperty >( inReader, "triangles", theDesc.triangles, inAllocator); + PxU32 triCount; + readStridedBufferProperty( inReader, "materialIndices", theDesc.materialIndices, triCount, inAllocator); + PxStridedData cookedData; + cookedData.stride = sizeof(PxU8); + PxU32 dataSize; + readStridedBufferProperty( inReader, "CookedData", cookedData, dataSize, inAllocator); + + TMemoryPoolManager theManager(inAllocator.getAllocator()); + MemoryBuffer theTempBuf( &theManager ); + +// PxTriangleMesh* theMesh = NULL; + PxBVH33TriangleMesh* theMesh = NULL; + + if(dataSize != 0) + { + theTempBuf.write(cookedData.data, dataSize*sizeof(PxU8)); +// theMesh = inArgs.physics.createTriangleMesh( theTempBuf ); + theMesh = static_cast(inArgs.physics.createTriangleMesh( theTempBuf )); + } + + if(theMesh == NULL) + { + PX_ASSERT(inArgs.cooker); + theTempBuf.clear(); + + { + PxCookingParams params = inArgs.cooker->getParams(); + params.midphaseDesc = PxMeshMidPhase::eBVH33; + inArgs.cooker->setParams(params); + } + + inArgs.cooker->cookTriangleMesh( theDesc, theTempBuf ); +// theMesh = inArgs.physics.createTriangleMesh( theTempBuf ); + theMesh = static_cast(inArgs.physics.createTriangleMesh( theTempBuf )); + } + + return PxCreateRepXObject( theMesh ); + } + + void PxBVH34TriangleMeshRepXSerializer::objectToFileImpl( const PxBVH34TriangleMesh* mesh, PxCollection* /*inCollection*/, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs ) + { + bool hasMatIndex = mesh->getTriangleMaterialIndex(0) != 0xffff; + PxU32 numVertices = mesh->getNbVertices(); + const PxVec3* vertices = mesh->getVertices(); + writeBuffer( inWriter, inTempBuffer, 2, vertices, numVertices, "Points", writePxVec3 ); + bool isU16 = mesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false; + PxU32 triCount = mesh->getNbTriangles(); + const void* indices = mesh->getTriangles(); + if ( isU16 ) + writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast* >( indices ), triCount, "Triangles", writeTriangle ); + else + writeBuffer( inWriter, inTempBuffer, 2, reinterpret_cast* >( indices ), triCount, "Triangles", writeTriangle ); + if ( hasMatIndex ) + writeBuffer( inWriter, inTempBuffer, 6, mesh, materialAccess, triCount, "materialIndices", writeDatatype ); + + //Cooked stream + PxTriangleMeshDesc meshDesc; + meshDesc.points.count = numVertices; + meshDesc.points.data = vertices; + meshDesc.points.stride = sizeof(PxVec3); + meshDesc.triangles.count = triCount; + meshDesc.triangles.data = indices; + meshDesc.triangles.stride = isU16?3*sizeof(PxU16):3*sizeof(PxU32); + + if(isU16) + { + meshDesc.triangles.stride = sizeof(PxU16)*3; + meshDesc.flags |= PxMeshFlag::e16_BIT_INDICES; + } + else + { + meshDesc.triangles.stride = sizeof(PxU32)*3; + } + + if(hasMatIndex) + { + PxMaterialTableIndex* materialIndices = new PxMaterialTableIndex[triCount]; + for(PxU32 i = 0; i < triCount; i++) + materialIndices[i] = mesh->getTriangleMaterialIndex(i); + + meshDesc.materialIndices.data = materialIndices; + meshDesc.materialIndices.stride = sizeof(PxMaterialTableIndex); + + } + + if(inArgs.cooker != NULL) + { + TMemoryPoolManager theManager(mAllocator); + MemoryBuffer theTempBuf( &theManager ); + theTempBuf.clear(); + inArgs.cooker->cookTriangleMesh( meshDesc, theTempBuf ); + + writeBuffer( inWriter, inTempBuffer, 16, theTempBuf.mBuffer, theTempBuf.mWriteOffset, "CookedData", writeDatatype ); + } + + delete []meshDesc.materialIndices.data; + } + + PxRepXObject PxBVH34TriangleMeshRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* /*inCollection*/ ) + { + //We can't do a simple inverse; we *have* to cook data to get a mesh. + PxTriangleMeshDesc theDesc; + readStridedBufferProperty( inReader, "points", theDesc.points, inAllocator); + readStridedBufferProperty >( inReader, "triangles", theDesc.triangles, inAllocator); + PxU32 triCount; + readStridedBufferProperty( inReader, "materialIndices", theDesc.materialIndices, triCount, inAllocator); + PxStridedData cookedData; + cookedData.stride = sizeof(PxU8); + PxU32 dataSize; + readStridedBufferProperty( inReader, "CookedData", cookedData, dataSize, inAllocator); + + TMemoryPoolManager theManager(inAllocator.getAllocator()); + MemoryBuffer theTempBuf( &theManager ); + +// PxTriangleMesh* theMesh = NULL; + PxBVH34TriangleMesh* theMesh = NULL; + + if(dataSize != 0) + { + theTempBuf.write(cookedData.data, dataSize*sizeof(PxU8)); +// theMesh = inArgs.physics.createTriangleMesh( theTempBuf ); + theMesh = static_cast(inArgs.physics.createTriangleMesh( theTempBuf )); + } + + if(theMesh == NULL) + { + PX_ASSERT(inArgs.cooker); + theTempBuf.clear(); + + { + PxCookingParams params = inArgs.cooker->getParams(); + params.midphaseDesc = PxMeshMidPhase::eBVH34; + inArgs.cooker->setParams(params); + } + + inArgs.cooker->cookTriangleMesh( theDesc, theTempBuf ); +// theMesh = inArgs.physics.createTriangleMesh( theTempBuf ); + theMesh = static_cast(inArgs.physics.createTriangleMesh( theTempBuf )); + } + + return PxCreateRepXObject(theMesh); + } + + + //************************************************************* + // Actual RepXSerializer implementations for PxHeightField + //************************************************************* + void PxHeightFieldRepXSerializer::objectToFileImpl( const PxHeightField* inHeightField, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/) + { + PxHeightFieldDesc theDesc; + + theDesc.nbRows = inHeightField->getNbRows(); + theDesc.nbColumns = inHeightField->getNbColumns(); + theDesc.format = inHeightField->getFormat(); + theDesc.samples.stride = inHeightField->getSampleStride(); + theDesc.samples.data = NULL; + theDesc.convexEdgeThreshold = inHeightField->getConvexEdgeThreshold(); + theDesc.flags = inHeightField->getFlags(); + + PxU32 theCellCount = inHeightField->getNbRows() * inHeightField->getNbColumns(); + PxU32 theSampleStride = sizeof( PxHeightFieldSample ); + PxU32 theSampleBufSize = theCellCount * theSampleStride; + PxHeightFieldSample* theSamples = reinterpret_cast< PxHeightFieldSample*> ( inTempBuffer.mManager->allocate( theSampleBufSize ) ); + inHeightField->saveCells( theSamples, theSampleBufSize ); + theDesc.samples.data = theSamples; + writeAllProperties( &theDesc, inWriter, inTempBuffer, *inCollection ); + writeStridedBufferProperty( inWriter, inTempBuffer, "samples", theDesc.samples, theDesc.nbRows * theDesc.nbColumns, 6, writeHeightFieldSample); + inTempBuffer.mManager->deallocate( reinterpret_cast(theSamples) ); + } + + PxRepXObject PxHeightFieldRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + PX_ASSERT(inArgs.cooker); + PxHeightFieldDesc theDesc; + readAllProperties( inArgs, inReader, &theDesc, inAllocator, *inCollection ); + //Now read the data... + PxU32 count = 0; //ignored becaues numRows and numColumns tells the story + readStridedBufferProperty( inReader, "samples", theDesc.samples, count, inAllocator); + PxHeightField* retval = inArgs.cooker->createHeightField( theDesc, inArgs.physics.getPhysicsInsertionCallback() ); + return PxCreateRepXObject(retval); + } + + //************************************************************* + // Actual RepXSerializer implementations for PxConvexMesh + //************************************************************* + void PxConvexMeshRepXSerializer::objectToFileImpl( const PxConvexMesh* mesh, PxCollection* /*inCollection*/, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs ) + { + writeBuffer( inWriter, inTempBuffer, 2, mesh->getVertices(), mesh->getNbVertices(), "points", writePxVec3 ); + + if(inArgs.cooker != NULL) + { + //Cache cooked Data + PxConvexMeshDesc theDesc; + theDesc.points.data = mesh->getVertices(); + theDesc.points.stride = sizeof(PxVec3); + theDesc.points.count = mesh->getNbVertices(); + + theDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX; + TMemoryPoolManager theManager(mAllocator); + MemoryBuffer theTempBuf( &theManager ); + inArgs.cooker->cookConvexMesh( theDesc, theTempBuf ); + + writeBuffer( inWriter, inTempBuffer, 16, theTempBuf.mBuffer, theTempBuf.mWriteOffset, "CookedData", writeDatatype ); + } + + } + + //Conversion from scene object to descriptor. + PxRepXObject PxConvexMeshRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* /*inCollection*/) + { + PxConvexMeshDesc theDesc; + readStridedBufferProperty( inReader, "points", theDesc.points, inAllocator); + theDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX; + + PxStridedData cookedData; + cookedData.stride = sizeof(PxU8); + PxU32 dataSize; + readStridedBufferProperty( inReader, "CookedData", cookedData, dataSize, inAllocator); + + TMemoryPoolManager theManager(inAllocator.getAllocator()); + MemoryBuffer theTempBuf( &theManager ); + + PxConvexMesh* theMesh = NULL; + + if(dataSize != 0) + { + theTempBuf.write(cookedData.data, dataSize*sizeof(PxU8)); + theMesh = inArgs.physics.createConvexMesh( theTempBuf ); + } + + if(theMesh == NULL) + { + PX_ASSERT(inArgs.cooker); + theTempBuf.clear(); + + inArgs.cooker->cookConvexMesh( theDesc, theTempBuf ); + theMesh = inArgs.physics.createConvexMesh( theTempBuf ); + } + + return PxCreateRepXObject(theMesh); + } + + //************************************************************* + // Actual RepXSerializer implementations for PxRigidStatic + //************************************************************* + PxRigidStatic* PxRigidStaticRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs ) + { + return inArgs.physics.createRigidStatic( PxTransform(PxIdentity) ); + } + + //************************************************************* + // Actual RepXSerializer implementations for PxRigidDynamic + //************************************************************* + PxRigidDynamic* PxRigidDynamicRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs ) + { + return inArgs.physics.createRigidDynamic( PxTransform(PxIdentity) ); + } + + //************************************************************* + // Actual RepXSerializer implementations for PxArticulation + //************************************************************* + void PxArticulationRepXSerializer::objectToFileImpl( const PxArticulation* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/) + { + TNameStack nameStack( inTempBuffer.mManager->mWrapper ); + Sn::TArticulationLinkLinkMap linkMap( inTempBuffer.mManager->mWrapper ); + RepXVisitorWriter writer( nameStack, inWriter, inObj, inTempBuffer, *inCollection, &linkMap ); + RepXPropertyFilter > theOp( writer ); + visitAllProperties( theOp ); + } + PxArticulation* PxArticulationRepXSerializer::allocateObject( PxRepXInstantiationArgs& inArgs ) { return inArgs.physics.createArticulation(); } + + //************************************************************* + // Actual RepXSerializer implementations for PxAggregate + //************************************************************* + void PxAggregateRepXSerializer::objectToFileImpl( const PxAggregate* data, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/) + { + PxArticulationLink *link = NULL; + inWriter.addAndGotoChild( "Actors" ); + for(PxU32 i = 0; i < data->getNbActors(); ++i) + { + PxActor* actor; + + if(data->getActors(&actor, 1, i)) + { + link = actor->is(); + } + + if(link && !link->getInboundJoint() ) + { + writeProperty( inWriter, *inCollection, inTempBuffer, "PxArticulationRef", &link->getArticulation()); + } + else if( !link ) + { + PxSerialObjectId theId = 0; + + theId = inCollection->getId( *actor ); + if( theId == 0 ) + theId = static_cast(reinterpret_cast(actor)); + + writeProperty( inWriter, *inCollection, inTempBuffer, "PxActorRef", theId ); + } + } + + inWriter.leaveChild( ); + + writeProperty( inWriter, *inCollection, inTempBuffer, "NumActors", data->getNbActors() ); + writeProperty( inWriter, *inCollection, inTempBuffer, "MaxNbActors", data->getMaxNbActors() ); + writeProperty( inWriter, *inCollection, inTempBuffer, "SelfCollision", data->getSelfCollision() ); + + writeAllProperties( data, inWriter, inTempBuffer, *inCollection ); + } + + PxRepXObject PxAggregateRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + PxU32 numActors; + readProperty( inReader, "NumActors", numActors ); + PxU32 maxNbActors; + readProperty( inReader, "MaxNbActors", maxNbActors ); + + bool selfCollision; + bool ret = readProperty( inReader, "SelfCollision", selfCollision ); + + PxAggregate* theAggregate = inArgs.physics.createAggregate(maxNbActors, selfCollision); + ret &= readAllProperties( inArgs, inReader, theAggregate, inAllocator, *inCollection ); + + inReader.pushCurrentContext(); + if ( inReader.gotoChild( "Actors" ) ) + { + inReader.pushCurrentContext(); + for( bool matSuccess = inReader.gotoFirstChild(); matSuccess; + matSuccess = inReader.gotoNextSibling() ) + { + const char* actorType = inReader.getCurrentItemName(); + if ( 0 == physx::shdfnd::stricmp( actorType, "PxActorRef" ) ) + { + PxActor *actor = NULL; + ret &= readReference( inReader, *inCollection, actor ); + + if(actor) + { + PxScene *currScene = actor->getScene(); + if(currScene) + { + currScene->removeActor(*actor); + } + theAggregate->addActor(*actor); + } + } + else if ( 0 == physx::shdfnd::stricmp( actorType, "PxArticulationRef" ) ) + { + PxArticulation* articulation = NULL; + ret &= readReference( inReader, *inCollection, articulation ); + if(articulation) + { + PxScene *currScene = articulation->getScene(); + if(currScene) + { + currScene->removeArticulation(*articulation); + } + theAggregate->addArticulation(*articulation); + } + } + } + inReader.popCurrentContext(); + inReader.leaveChild(); + } + inReader.popCurrentContext(); + + return ret ? PxCreateRepXObject(theAggregate) : PxRepXObject(); + } +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h new file mode 100644 index 000000000..d04c46cd3 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h @@ -0,0 +1,125 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef SN_REPX_CORE_SERIALIZER_H +#define SN_REPX_CORE_SERIALIZER_H +/** \addtogroup RepXSerializers + @{ +*/ +#include "foundation/PxSimpleTypes.h" +#include "SnRepXSerializerImpl.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + + class XmlReader; + class XmlMemoryAllocator; + class XmlWriter; + class MemoryBuffer; + + struct PxMaterialRepXSerializer : RepXSerializerImpl + { + PxMaterialRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual PxMaterial* allocateObject( PxRepXInstantiationArgs& ); + }; + + struct PxShapeRepXSerializer : public RepXSerializerImpl + { + PxShapeRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxShape* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + + struct PxBVH33TriangleMeshRepXSerializer : public RepXSerializerImpl + { + PxBVH33TriangleMeshRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxBVH33TriangleMesh*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxBVH33TriangleMesh* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + struct PxBVH34TriangleMeshRepXSerializer : public RepXSerializerImpl + { + PxBVH34TriangleMeshRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxBVH34TriangleMesh*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxBVH34TriangleMesh* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + + struct PxHeightFieldRepXSerializer : public RepXSerializerImpl + { + PxHeightFieldRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxHeightField*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxHeightField* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + + struct PxConvexMeshRepXSerializer : public RepXSerializerImpl + { + PxConvexMeshRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxConvexMesh*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxConvexMesh* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + + struct PxRigidStaticRepXSerializer : public RepXSerializerImpl + { + PxRigidStaticRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual PxRigidStatic* allocateObject( PxRepXInstantiationArgs& ); + }; + + struct PxRigidDynamicRepXSerializer : public RepXSerializerImpl + { + PxRigidDynamicRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual PxRigidDynamic* allocateObject( PxRepXInstantiationArgs& ); + }; + + struct PxArticulationRepXSerializer : public RepXSerializerImpl + { + PxArticulationRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxArticulation*, PxCollection*, XmlWriter&, MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxArticulation* allocateObject( PxRepXInstantiationArgs& ); + }; + + struct PxAggregateRepXSerializer : public RepXSerializerImpl + { + PxAggregateRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual void objectToFileImpl( const PxAggregate*, PxCollection*, XmlWriter& , MemoryBuffer&, PxRepXInstantiationArgs& ); + virtual PxRepXObject fileToObject( XmlReader&, XmlMemoryAllocator&, PxRepXInstantiationArgs&, PxCollection* ); + virtual PxAggregate* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif +/** @} */ + diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h new file mode 100644 index 000000000..95f7a3448 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h @@ -0,0 +1,90 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_REPX_SERIALIZER_IMPL_H +#define PX_REPX_SERIALIZER_IMPL_H + +#include "PsUserAllocated.h" +#include "SnXmlVisitorWriter.h" +#include "SnXmlVisitorReader.h" + +namespace physx { + using namespace Sn; + + /** + * The repx serializer impl takes the raw, untyped repx extension interface + * and implements the simpler functions plus does the reinterpret-casts required + * for any object to implement the serializer safely. + */ + template + struct RepXSerializerImpl : public PxRepXSerializer, shdfnd::UserAllocated + { + protected: + RepXSerializerImpl( const RepXSerializerImpl& inOther ); + RepXSerializerImpl& operator=( const RepXSerializerImpl& inOther ); + + public: + PxAllocatorCallback& mAllocator; + + RepXSerializerImpl( PxAllocatorCallback& inAllocator ) + : mAllocator( inAllocator ) + { + } + + virtual const char* getTypeName() { return PxTypeInfo::name(); } + + virtual void objectToFile( const PxRepXObject& inLiveObject, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& inArgs ) + { + const TLiveType* theObj = reinterpret_cast( inLiveObject.serializable ); + objectToFileImpl( theObj, inCollection, inWriter, inTempBuffer, inArgs ); + } + + virtual PxRepXObject fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + TLiveType* theObj( allocateObject( inArgs ) ); + if ( theObj ) + if(fileToObjectImpl( theObj, inReader, inAllocator, inArgs, inCollection )) + return PxCreateRepXObject(theObj); + return PxRepXObject(); + } + + virtual void objectToFileImpl( const TLiveType* inObj, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/) + { + writeAllProperties( inObj, inWriter, inTempBuffer, *inCollection ); + } + + virtual bool fileToObjectImpl( TLiveType* inObj, XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + return readAllProperties( inArgs, inReader, inObj, inAllocator, *inCollection ); + } + + virtual TLiveType* allocateObject( PxRepXInstantiationArgs& inArgs ) = 0; + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp new file mode 100644 index 000000000..978e98b2e --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp @@ -0,0 +1,465 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "CmPhysXCommon.h" +#include "SnXmlImpl.h" +#include "SnXmlReader.h" +#include "SnXmlMemoryAllocator.h" +#include "PsFoundation.h" +#include "SnRepXCollection.h" +#include "SnRepXUpgrader.h" + +using namespace physx::profile; + +namespace physx { namespace Sn { + + #define DEFINE_REPX_DEFAULT_PROPERTY( name, val ) RepXDefaultEntry( name, val ), + + static RepXDefaultEntry gRepX1_0Defaults[] = { + #include "SnRepX1_0Defaults.h" + }; + static PxU32 gNumRepX1_0Default = sizeof( gRepX1_0Defaults ) / sizeof ( *gRepX1_0Defaults ); + + static RepXDefaultEntry gRepX3_1Defaults[] = { + #include "SnRepX3_1Defaults.h" + }; + static PxU32 gNumRepX3_1Defaults = sizeof( gRepX3_1Defaults ) / sizeof ( *gRepX3_1Defaults ); + + static RepXDefaultEntry gRepX3_2Defaults[] = { + #include "SnRepX3_2Defaults.h" + }; + static PxU32 gNumRepX3_2Defaults = sizeof( gRepX3_2Defaults ) / sizeof ( *gRepX3_2Defaults ); + + inline const char* nextPeriod( const char* str ) + { + for( ++str; str && *str && *str != '.'; ++str ); //empty loop intentional + return str; + } + + inline bool safeStrEq(const char* lhs, const char* rhs) + { + if (lhs == rhs) + return true; + //If they aren't equal, and one of them is null, + //then they can't be equal. + //This is assuming that the null char* is not equal to + //the empty "" char*. + if (!lhs || !rhs) + return false; + + return ::strcmp(lhs, rhs) == 0; + } + + typedef PxProfileHashMap TNameOffsetMap; + + void setMissingPropertiesToDefault( XmlNode* topNode, XmlReaderWriter& editor, const RepXDefaultEntry* defaults, PxU32 numDefaults, TNameOffsetMap& map ) + { + for ( XmlNode* child = topNode->mFirstChild; child != NULL; child = child->mNextSibling ) + setMissingPropertiesToDefault( child, editor, defaults, numDefaults, map ); + + const TNameOffsetMap::Entry* entry( map.find( topNode->mName ) ); + if ( entry ) + { + XmlReaderWriter& theReader( editor ); + theReader.setNode( *topNode ); + char nameBuffer[512] = {0}; + size_t nameLen = strlen( topNode->mName ); + //For each default property entry for this node type. + for ( const RepXDefaultEntry* item = defaults + entry->second; strncmp( item->name, topNode->mName, nameLen ) == 0; ++item ) + { + bool childAdded = false; + const char* nameStart = item->name + nameLen; + ++nameStart; + theReader.pushCurrentContext(); + const char* str = nameStart; + while( *str ) + { + const char *period = nextPeriod( str ); + size_t len = size_t(PxMin( period - str, ptrdiff_t(1023) )); //can't be too careful these days. + PxMemCopy( nameBuffer, str, PxU32(len) ); + nameBuffer[len] = 0; + if ( theReader.gotoChild( nameBuffer ) == false ) + { + childAdded = true; + theReader.addOrGotoChild( nameBuffer ); + } + if (*period ) + str = period + 1; + else + str = period; + } + if ( childAdded ) + theReader.setCurrentItemValue( item->value ); + theReader.popCurrentContext(); + } + } + } + + + static void setMissingPropertiesToDefault( RepXCollection& collection, XmlReaderWriter& editor, const RepXDefaultEntry* defaults, PxU32 numDefaults ) + { + PxProfileAllocatorWrapper wrapper( collection.getAllocator() ); + //Release all strings at once, instead of piece by piece + XmlMemoryAllocatorImpl alloc( collection.getAllocator() ); + //build a hashtable of the initial default value strings. + TNameOffsetMap nameOffsets( wrapper ); + for ( PxU32 idx = 0; idx < numDefaults; ++idx ) + { + const RepXDefaultEntry& item( defaults[idx] ); + size_t nameLen = 0; + const char* periodPtr = nextPeriod (item.name); + for ( ; periodPtr && *periodPtr; ++periodPtr ) if( *periodPtr == '.' ) break; + if ( periodPtr == NULL || *periodPtr != '.' ) continue; + nameLen = size_t(periodPtr - item.name); + char* newMem = reinterpret_cast(alloc.allocate( PxU32(nameLen + 1) )); + PxMemCopy( newMem, item.name, PxU32(nameLen) ); + newMem[nameLen] = 0; + + if ( nameOffsets.find( newMem ) ) + alloc.deallocate( reinterpret_cast(newMem) ); + else + nameOffsets.insert( newMem, idx ); + } + //Run through each collection item, and recursively find it and its children + //If an object's name is in the hash map, check and add any properties that don't exist. + //else return. + for ( const RepXCollectionItem* item = collection.begin(), *end = collection.end(); item != end; ++ item ) + { + RepXCollectionItem theItem( *item ); + setMissingPropertiesToDefault( theItem.descriptor, editor, defaults, numDefaults, nameOffsets ); + } + } + + struct RecursiveTraversal + { + RecursiveTraversal(XmlReaderWriter& editor): mEditor(editor) {} + void traverse() + { + mEditor.pushCurrentContext(); + updateNode(); + for(bool exists = mEditor.gotoFirstChild(); exists; exists = mEditor.gotoNextSibling()) + traverse(); + mEditor.popCurrentContext(); + } + virtual void updateNode() = 0; + virtual ~RecursiveTraversal() {} + XmlReaderWriter& mEditor; + protected: + RecursiveTraversal& operator=(const RecursiveTraversal&){return *this;} + }; + + + RepXCollection& RepXUpgrader::upgrade10CollectionTo3_1Collection(RepXCollection& src) + { + XmlReaderWriter& editor( src.createNodeEditor() ); + setMissingPropertiesToDefault(src, editor, gRepX1_0Defaults, gNumRepX1_0Default ); + + + RepXCollection* dest = &src.createCollection("3.1.1"); + + for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item ) + { + //either src or dest could do the copy operation, it doesn't matter who does it. + RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) ); + editor.setNode( *const_cast( newItem.descriptor ) ); + //Some old files have this name in their system. + editor.renameProperty( "MassSpaceInertia", "MassSpaceInertiaTensor" ); + editor.renameProperty( "SleepEnergyThreshold", "SleepThreshold" ); + + if ( strstr( newItem.liveObject.typeName, "Joint" ) || strstr( newItem.liveObject.typeName, "joint" ) ) + { + //Joints changed format a bit. old joints looked like: + /* + 1627536 + 1628368 + 0 0 0 1 0.5 0.5 0.5 + 0 0 0 1 0.3 0.3 0.3*/ + //New joints look like: + /* + + 58320336 + 56353568 + + + 0 0 0 1 0.5 0.5 0.5 + 0 0 0 1 0.3 0.3 0.3 + + */ + const char* actor0, *actor1, *lp0, *lp1; + editor.readAndRemoveProperty( "Actor0", actor0 ); + editor.readAndRemoveProperty( "Actor1", actor1 ); + editor.readAndRemoveProperty( "LocalPose0", lp0 ); + editor.readAndRemoveProperty( "LocalPose1", lp1 ); + + editor.addOrGotoChild( "Actors" ); + editor.writePropertyIfNotEmpty( "actor0", actor0 ); + editor.writePropertyIfNotEmpty( "actor1", actor1 ); + editor.leaveChild(); + + editor.addOrGotoChild( "LocalPose" ); + editor.writePropertyIfNotEmpty( "eACTOR0", lp0 ); + editor.writePropertyIfNotEmpty( "eACTOR1", lp1 ); + editor.leaveChild(); + } + + + + //now desc owns the new node. Collections share a single allocation pool, however, + //which will get destroyed when all the collections referencing it are destroyed themselves. + //Data on nodes is shared between nodes, but the node structure itself is allocated. + dest->addCollectionItem( newItem ); + } + editor.release(); + src.destroy(); + return *dest; + } + + RepXCollection& RepXUpgrader::upgrade3_1CollectionTo3_2Collection(RepXCollection& src) + { + XmlReaderWriter& editor( src.createNodeEditor() ); + setMissingPropertiesToDefault(src, editor, gRepX3_1Defaults, gNumRepX3_1Defaults ); + + RepXCollection* dest = &src.createCollection("3.2.0"); + + for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item ) + { + //either src or dest could do the copy operation, it doesn't matter who does it. + RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) ); + editor.setNode( *const_cast( newItem.descriptor ) ); + + if ( strstr( newItem.liveObject.typeName, "PxMaterial" ) ) + { + editor.removeChild( "DynamicFrictionV" ); + editor.removeChild( "StaticFrictionV" ); + editor.removeChild( "dirOfAnisotropy" ); + } + //now desc owns the new node. Collections share a single allocation pool, however, + //which will get destroyed when all the collections referencing it are destroyed themselves. + //Data on nodes is shared between nodes, but the node structure itself is allocated. + dest->addCollectionItem( newItem ); + } + editor.release(); + src.destroy(); + return *dest; + } + + RepXCollection& RepXUpgrader::upgrade3_2CollectionTo3_3Collection(RepXCollection& src) + { + XmlReaderWriter& editor( src.createNodeEditor() ); + setMissingPropertiesToDefault(src, editor, gRepX3_2Defaults, gNumRepX3_2Defaults ); + + RepXCollection* dest = &src.createCollection("3.3.0"); + + + + struct RenameSpringToStiffness : public RecursiveTraversal + { + RenameSpringToStiffness(XmlReaderWriter& editor_): RecursiveTraversal(editor_) {} + + void updateNode() + { + mEditor.renameProperty("Spring", "Stiffness"); + mEditor.renameProperty("TangentialSpring", "TangentialStiffness"); + } + }; + + + struct UpdateArticulationSwingLimit : public RecursiveTraversal + { + UpdateArticulationSwingLimit(XmlReaderWriter& editor_): RecursiveTraversal(editor_) {} + + void updateNode() + { + if(!Ps::stricmp(mEditor.getCurrentItemName(), "yLimit") && !Ps::stricmp(mEditor.getCurrentItemValue(), "0")) + mEditor.setCurrentItemValue("0.785398"); + + if(!Ps::stricmp(mEditor.getCurrentItemName(), "zLimit") && !Ps::stricmp(mEditor.getCurrentItemValue(), "0")) + mEditor.setCurrentItemValue("0.785398"); + + if(!Ps::stricmp(mEditor.getCurrentItemName(), "TwistLimit")) + { + mEditor.gotoFirstChild(); + PxReal lower = PxReal(strtod(mEditor.getCurrentItemValue(), NULL)); + mEditor.gotoNextSibling(); + PxReal upper = PxReal(strtod(mEditor.getCurrentItemValue(), NULL)); + mEditor.leaveChild(); + if(lower>=upper) + { + mEditor.writePropertyIfNotEmpty("lower", "-0.785398"); + mEditor.writePropertyIfNotEmpty("upper", "0.785398"); + } + } + } + }; + + + for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item ) + { + //either src or dest could do the copy operation, it doesn't matter who does it. + RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) ); + + if ( strstr( newItem.liveObject.typeName, "PxCloth" ) || strstr( newItem.liveObject.typeName, "PxClothFabric" ) ) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Didn't suppot PxCloth upgrate from 3.2 to 3.3! "); + continue; + } + + if ( strstr( newItem.liveObject.typeName, "PxParticleSystem" ) || strstr( newItem.liveObject.typeName, "PxParticleFluid" ) ) + { + editor.setNode( *const_cast( newItem.descriptor ) ); + editor.renameProperty( "PositionBuffer", "Positions" ); + editor.renameProperty( "VelocityBuffer", "Velocities" ); + editor.renameProperty( "RestOffsetBuffer", "RestOffsets" ); + } + + if(strstr(newItem.liveObject.typeName, "PxPrismaticJoint" ) + || strstr(newItem.liveObject.typeName, "PxRevoluteJoint") + || strstr(newItem.liveObject.typeName, "PxSphericalJoint") + || strstr(newItem.liveObject.typeName, "PxD6Joint") + || strstr(newItem.liveObject.typeName, "PxArticulation")) + { + editor.setNode( *const_cast( newItem.descriptor ) ); + RenameSpringToStiffness(editor).traverse(); + } + + if(strstr(newItem.liveObject.typeName, "PxArticulation")) + { + editor.setNode( *const_cast( newItem.descriptor ) ); + UpdateArticulationSwingLimit(editor).traverse(); + } + + + + //now dest owns the new node. Collections share a single allocation pool, however, + //which will get destroyed when all the collections referencing it are destroyed themselves. + //Data on nodes is shared between nodes, but the node structure itself is allocated. + + dest->addCollectionItem( newItem ); + + } + editor.release(); + src.destroy(); + + return *dest; + } + + RepXCollection& RepXUpgrader::upgrade3_3CollectionTo3_4Collection(RepXCollection& src) + { + RepXCollection* dest = &src.createCollection("3.4.0"); + + for ( const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++ item ) + { + if(strstr(item->liveObject.typeName, "PxTriangleMesh")) + { + PxRepXObject newMeshRepXObj("PxBVH33TriangleMesh", item->liveObject.serializable, item->liveObject.id); + XmlNode* newMeshNode = src.copyRepXNode( item->descriptor ); + newMeshNode->mName = "PxBVH33TriangleMesh"; + RepXCollectionItem newMeshItem(newMeshRepXObj, newMeshNode); + dest->addCollectionItem( newMeshItem ); + continue; + } + + RepXCollectionItem newItem( item->liveObject, src.copyRepXNode( item->descriptor ) ); + dest->addCollectionItem( newItem ); + } + src.destroy(); + return *dest; + } + + RepXCollection& RepXUpgrader::upgrade3_4CollectionTo4_0Collection(RepXCollection& src) + { + RepXCollection* dest = &src.createCollection("4.0.0"); + + for (const RepXCollectionItem* item = src.begin(), *end = src.end(); item != end; ++item) + { + if (strstr(item->liveObject.typeName, "PxParticleFluid") || + strstr(item->liveObject.typeName, "PxParticleSystem") || + strstr(item->liveObject.typeName, "PxClothFabric") || + strstr(item->liveObject.typeName, "PxCloth")) + { + continue; + } + + RepXCollectionItem newItem(item->liveObject, src.copyRepXNode(item->descriptor)); + dest->addCollectionItem(newItem); + } + src.destroy(); + return *dest; + } + + RepXCollection& RepXUpgrader::upgradeCollection(RepXCollection& src) + { + const char* srcVersion = src.getVersion(); + if( safeStrEq( srcVersion, RepXCollection::getLatestVersion() )) + return src; + + typedef RepXCollection& (*UPGRADE_FUNCTION)(RepXCollection& src); + + struct Upgrade { const char* versionString; UPGRADE_FUNCTION upgradeFunction; }; + + static const Upgrade upgradeTable[] = + { + { "1.0", upgrade10CollectionTo3_1Collection }, + { "3.1", NULL }, + { "3.1.1", upgrade3_1CollectionTo3_2Collection }, + { "3.2.0", upgrade3_2CollectionTo3_3Collection }, + { "3.3.0", NULL }, + { "3.3.1", NULL }, + { "3.3.2", NULL }, + { "3.3.3", NULL }, + { "3.3.4", upgrade3_3CollectionTo3_4Collection }, + { "3.4.0", NULL }, + { "3.4.1", NULL }, + { "3.4.2", upgrade3_4CollectionTo4_0Collection } + }; //increasing order and complete + + const PxU32 upgradeTableSize = sizeof(upgradeTable)/sizeof(upgradeTable[0]); + + PxU32 repxVersion = UINT16_MAX; + + for (PxU32 i=0; i + class SimpleXmlWriterImpl : public SimpleXmlWriter + { + PxProfileAllocatorWrapper mWrapper; + TStreamType& mStream; + SimpleXmlWriterImpl( const SimpleXmlWriterImpl& inOther ); + SimpleXmlWriterImpl& operator=( const SimpleXmlWriterImpl& inOther ); + PxProfileArray mTags; + bool mTagOpen; + PxU32 mInitialTagDepth; + public: + + SimpleXmlWriterImpl( TStreamType& inStream, PxAllocatorCallback& inAllocator, PxU32 inInitialTagDepth = 0 ) + : mWrapper( inAllocator ) + , mStream( inStream ) + , mTags( mWrapper ) + , mTagOpen( false ) + , mInitialTagDepth( inInitialTagDepth ) + { + } + virtual ~SimpleXmlWriterImpl() + { + while( mTags.size() ) + endTag(); + } + PxU32 tabCount() { return mTags.size() + mInitialTagDepth; } + + void writeTabs( PxU32 inSize ) + { + inSize += mInitialTagDepth; + for ( PxU32 idx =0; idx < inSize; ++idx ) + mStream << "\t"; + } + void beginTag( const char* inTagname ) + { + closeTag(); + writeTabs(mTags.size()); + mTags.pushBack( inTagname ); + mStream << "<" << inTagname; + mTagOpen = true; + } + void addAttribute( const char* inName, const char* inValue ) + { + PX_ASSERT( mTagOpen ); + mStream << " " << inName << "=" << "\"" << inValue << "\""; + } + void closeTag(bool useNewline = true) + { + if ( mTagOpen ) + { + mStream << " " << ">"; + if (useNewline ) + mStream << "\n"; + } + mTagOpen = false; + } + void doEndOpenTag() + { + mStream << "" << "\n"; + } + void endTag() + { + PX_ASSERT( mTags.size() ); + if ( mTagOpen ) + mStream << " " << "/>" << "\n"; + else + { + writeTabs(mTags.size()-1); + doEndOpenTag(); + } + mTagOpen = false; + mTags.popBack(); + } + + static bool IsNormalizableWhitespace(char c) { return c == 0x9 || c == 0xA || c == 0xD; } + static bool IsValidXmlCharacter(char c) { return IsNormalizableWhitespace(c) || c >= 0x20; } + + void addContent( const char* inContent ) + { + closeTag(false); + //escape xml + for( ; *inContent; inContent++ ) + { + switch (*inContent) + { + case '<': + mStream << "<"; + break; + case '>': + mStream << ">"; + break; + case '&': + mStream << "&"; + break; + case '\'': + mStream << "'"; + break; + case '"': + mStream << """; + break; + default: + if (IsValidXmlCharacter(*inContent)) { + if (IsNormalizableWhitespace(*inContent)) + { + char s[32]; + Ps::snprintf(s, 32, "&#x%02X;", unsigned(*inContent)); + mStream << s; + } + else + mStream << *inContent; + } + break; + } + } + } + + void writeContentTag( const char* inTag, const char* inContent ) + { + beginTag( inTag ); + addContent( inContent ); + doEndOpenTag(); + mTags.popBack(); + } + void insertXml( const char* inXml ) + { + closeTag(); + mStream << inXml; + } + }; + + struct BeginTag + { + const char* mTagName; + BeginTag( const char* inTagName ) + : mTagName( inTagName ) { } + }; + + struct EndTag + { + EndTag() {} + }; + + struct Att + { + const char* mAttName; + const char* mAttValue; + Att( const char* inAttName, const char* inAttValue ) + : mAttName( inAttName ) + , mAttValue( inAttValue ) { } + }; + + struct Content + { + const char* mContent; + Content( const char* inContent ) + : mContent( inContent ) { } + }; + + + struct ContentTag + { + const char* mTagName; + const char* mContent; + ContentTag( const char* inTagName, const char* inContent ) + : mTagName( inTagName ) + , mContent( inContent ) { } + }; + + inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const BeginTag& inTag ) { inWriter.beginTag( inTag.mTagName ); return inWriter; } + inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const EndTag& inTag ) { PX_UNUSED(inTag); inWriter.endTag(); return inWriter; } + inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const Att& inTag ) { inWriter.addAttribute(inTag.mAttName, inTag.mAttValue); return inWriter; } + inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const Content& inTag ) { inWriter.addContent(inTag.mContent); return inWriter; } + inline SimpleXmlWriter& operator<<( SimpleXmlWriter& inWriter, const ContentTag& inTag ) { inWriter.writeContentTag(inTag.mTagName, inTag.mContent); return inWriter; } + + inline void writeProperty( SimpleXmlWriter& inWriter, MemoryBuffer& tempBuffer, const char* inPropName ) + { + PxU8 data = 0; + tempBuffer.write( &data, sizeof(PxU8) ); + inWriter.writeContentTag( inPropName, reinterpret_cast( tempBuffer.mBuffer ) ); + tempBuffer.clear(); + } + + template + inline void writeProperty( SimpleXmlWriter& inWriter, MemoryBuffer& tempBuffer, const char* inPropName, TDataType inValue ) + { + tempBuffer << inValue; + writeProperty( inWriter, tempBuffer, inPropName ); + } +} } +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h new file mode 100644 index 000000000..cfad99207 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h @@ -0,0 +1,197 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_DESERIALIZER_H +#define PX_XML_DESERIALIZER_H + +#include "SnXmlVisitorReader.h" + +namespace physx { namespace Sn { + + //Definitions needed internally in the Serializer headers. + template + struct Triangle + { + TTriIndexElem mIdx0; + TTriIndexElem mIdx1; + TTriIndexElem mIdx2; + Triangle( TTriIndexElem inIdx0 = 0, TTriIndexElem inIdx1 = 0, TTriIndexElem inIdx2 = 0) + : mIdx0( inIdx0 ) + , mIdx1( inIdx1 ) + , mIdx2( inIdx2 ) + { + } + }; + + struct XmlMemoryAllocateMemoryPoolAllocator + { + XmlMemoryAllocator* mAllocator; + XmlMemoryAllocateMemoryPoolAllocator( XmlMemoryAllocator* inAlloc ) : mAllocator( inAlloc ) {} + + PxU8* allocate( PxU32 inSize ) { return mAllocator->allocate( inSize ); } + void deallocate( PxU8* inMem ) { mAllocator->deallocate( inMem ); } + }; + + inline bool isEmpty(const char *s) + { + while (*s != '\0') + { + if (!isspace(*s)) + return false; + s++; + } + return true; + } + + inline void strtoLong( Triangle& ioDatatype,const char*& ioData ) + { + strto( ioDatatype.mIdx0, ioData ); + strto( ioDatatype.mIdx1, ioData ); + strto( ioDatatype.mIdx2, ioData ); + } + + inline void strtoLong( PxHeightFieldSample& ioDatatype,const char*& ioData ) + { + PxU32 tempData; + strto( tempData, ioData ); + if ( isBigEndian() ) + { + PxU32& theItem(tempData); + PxU32 theDest = 0; + PxU8* theReadPtr( reinterpret_cast< PxU8* >( &theItem ) ); + PxU8* theWritePtr( reinterpret_cast< PxU8* >( &theDest ) ); + //A height field sample is a 16 bit number + //followed by two bytes. + + //We write this out as a 32 bit integer, LE. + //Thus, on a big endian, we need to move the bytes + //around a bit. + //LE - 1 2 3 4 + //BE - 4 3 2 1 - after convert from xml number + //Correct BE - 2 1 3 4, just like LE but with the 16 number swapped + theWritePtr[0] = theReadPtr[2]; + theWritePtr[1] = theReadPtr[3]; + theWritePtr[2] = theReadPtr[1]; + theWritePtr[3] = theReadPtr[0]; + theItem = theDest; + } + ioDatatype = *reinterpret_cast( &tempData ); + } + + template + inline void readStridedFlagsProperty( XmlReader& ioReader, const char* inPropName, TDataType*& outData, PxU32& outStride, PxU32& outCount, XmlMemoryAllocator& inAllocator, + const PxU32ToName* inConversions) + { + const char* theSrcData; + outStride = sizeof( TDataType ); + outData = NULL; + outCount = 0; + if ( ioReader.read( inPropName, theSrcData ) ) + { + XmlMemoryAllocateMemoryPoolAllocator tempAllocator( &inAllocator ); + MemoryBufferBase tempBuffer( &tempAllocator ); + + if ( theSrcData ) + { + static PxU32 theCount = 0; + ++theCount; + char* theStartData = const_cast< char*>( copyStr( &tempAllocator, theSrcData ) ); + char* aData = strtok(theStartData, " \n"); + while( aData ) + { + TDataType tempValue; + stringToFlagsType( aData, inAllocator, tempValue, inConversions ); + aData = strtok(NULL," \n"); + tempBuffer.write( &tempValue, sizeof(TDataType) ); + } + outData = reinterpret_cast< TDataType* >( tempBuffer.mBuffer ); + outCount = tempBuffer.mWriteOffset / sizeof( TDataType ); + tempAllocator.deallocate( reinterpret_cast(theStartData) ); + } + tempBuffer.releaseBuffer(); + } + } + + template + inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, TDataType*& outData, PxU32& outStride, PxU32& outCount, XmlMemoryAllocator& inAllocator) + { + const char* theSrcData; + outStride = sizeof( TDataType ); + outData = NULL; + outCount = 0; + if ( ioReader.read( inPropName, theSrcData ) ) + { + XmlMemoryAllocateMemoryPoolAllocator tempAllocator( &inAllocator ); + MemoryBufferBase tempBuffer( &tempAllocator ); + + if ( theSrcData ) + { + static PxU32 theCount = 0; + ++theCount; + char* theStartData = const_cast< char*>( copyStr( &tempAllocator, theSrcData ) ); + const char* theData = theStartData; + while( !isEmpty(theData) ) + { + //These buffers are whitespace delimited. + TDataType theType; + strtoLong( theType, theData ); + tempBuffer.write( &theType, sizeof(theType) ); + } + outData = reinterpret_cast< TDataType* >( tempBuffer.mBuffer ); + outCount = tempBuffer.mWriteOffset / sizeof( TDataType ); + tempAllocator.deallocate( reinterpret_cast(theStartData) ); + } + tempBuffer.releaseBuffer(); + } + } + + template + inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, PxStridedData& ioData, PxU32& outCount, XmlMemoryAllocator& inAllocator) + { + TDataType* tempData = NULL; + readStridedBufferProperty( ioReader, inPropName, tempData, ioData.stride, outCount, inAllocator ); + ioData.data = tempData; + } + + template + inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, PxTypedStridedData& ioData, PxU32& outCount, XmlMemoryAllocator& inAllocator) + { + TDataType* tempData = NULL; + readStridedBufferProperty( ioReader, inPropName, tempData, ioData.stride, outCount, inAllocator ); + ioData.data = reinterpret_cast( tempData ); + } + + template + inline void readStridedBufferProperty( XmlReader& ioReader, const char* inPropName, PxBoundedData& ioData, XmlMemoryAllocator& inAllocator) + { + return readStridedBufferProperty( ioReader, inPropName, ioData, ioData.count, inAllocator ); + } + +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h new file mode 100644 index 000000000..261ab313a --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h @@ -0,0 +1,243 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_IMPL_H +#define PX_XML_IMPL_H + +#include "SnXmlMemoryPool.h" +#include "PsString.h" +#include "foundation/PxMemory.h" + +namespace physx { namespace Sn { + +typedef CMemoryPoolManager TMemoryPoolManager; + +namespace snXmlImpl { + + inline PxU32 strLen( const char* inStr ) + { + PxU32 len = 0; + if ( inStr ) + { + while ( *inStr ) + { + ++len; + ++inStr; + } + } + return len; + } +} + inline const char* copyStr( PxAllocatorCallback& inAllocator, const char* inStr ) + { + if ( inStr && *inStr ) + { + PxU32 theLen = snXmlImpl::strLen( inStr ); + //The memory will never be released by repx. If you want it released, you need to pass in a custom allocator + //that tracks all allocations and releases unreleased allocations yourself. + char* dest = reinterpret_cast( inAllocator.allocate( theLen + 1, "Repx::const char*", __FILE__, __LINE__ ) ); + PxMemCopy( dest, inStr, theLen ); + dest[theLen] = 0; + return dest; + } + return ""; + } + + template + inline const char* copyStr( TManagerType* inMgr, const char* inStr ) + { + if ( inStr && *inStr ) + { + PxU32 theLen = snXmlImpl::strLen( inStr ); + char* dest = reinterpret_cast( inMgr->allocate( theLen + 1 ) ); + PxMemCopy( dest, inStr, theLen ); + dest[theLen] = 0; + return dest; + } + return ""; + } + + inline void releaseStr( TMemoryPoolManager* inMgr, const char* inStr, PxU32 ) + { + if ( inStr && *inStr ) + { + inMgr->deallocate( reinterpret_cast< PxU8* >( const_cast( inStr ) ) ); + } + } + + inline void releaseStr( TMemoryPoolManager* inMgr, const char* inStr ) + { + if ( inStr && *inStr ) + { + PxU32 theLen = snXmlImpl::strLen( inStr ); + releaseStr( inMgr, inStr, theLen ); + } + } + + struct XmlNode + { + const char* mName; //Never released until all collections are released + const char* mData; //Never released until all collections are released + + XmlNode* mNextSibling; + XmlNode* mPreviousSibling; + XmlNode* mFirstChild; + XmlNode* mParent; + XmlNode( const XmlNode& ); + XmlNode& operator=( const XmlNode& ); + + PX_INLINE void initPtrs() + { + mNextSibling = NULL; + mPreviousSibling = NULL; + mFirstChild = NULL; + mParent = NULL; + } + + PX_INLINE XmlNode( const char* inName = "", const char* inData = "" ) + : mName( inName ) + , mData( inData ) + { initPtrs(); } + + void addChild( XmlNode* inItem ) + { + inItem->mParent = this; + if ( mFirstChild == NULL ) + mFirstChild = inItem; + else + { + XmlNode* theNode = mFirstChild; + //Follow the chain till the end. + while( theNode->mNextSibling != NULL ) + theNode = theNode->mNextSibling; + theNode->mNextSibling = inItem; + inItem->mPreviousSibling = theNode; + } + } + + PX_INLINE XmlNode* findChildByName( const char* inName ) + { + for ( XmlNode* theNode = mFirstChild; theNode; theNode = theNode->mNextSibling ) + { + XmlNode* theRepXNode = theNode; + if ( physx::shdfnd::stricmp( theRepXNode->mName, inName ) == 0 ) + return theNode; + } + return NULL; + } + + PX_INLINE void orphan() + { + if ( mParent ) + { + if ( mParent->mFirstChild == this ) + mParent->mFirstChild = mNextSibling; + } + if ( mPreviousSibling ) + mPreviousSibling->mNextSibling = mNextSibling; + if ( mNextSibling ) + mNextSibling->mPreviousSibling = mPreviousSibling; + if ( mFirstChild ) + mFirstChild->mParent = NULL; + initPtrs(); + } + }; + + inline XmlNode* allocateRepXNode( TMemoryPoolManager* inManager, const char* inName, const char* inData ) + { + XmlNode* retval = inManager->allocate(); + retval->mName = copyStr( inManager, inName ); + retval->mData = copyStr( inManager, inData ); + return retval; + } + + inline void release( TMemoryPoolManager* inManager, XmlNode* inNode ) + { + //We *don't* release the strings associated with the node + //because they could be shared. Instead, we just let them 'leak' + //in some sense, at least until the memory manager itself is deleted. + //DO NOT UNCOMMENT THE LINES BELOW!! + //releaseStr( inManager, inNode->mName ); + //releaseStr( inManager, inNode->mData ); + inManager->deallocate( inNode ); + } + + static PX_INLINE void releaseNodeAndChildren( TMemoryPoolManager* inManager, XmlNode* inNode ) + { + if ( inNode->mFirstChild ) + { + XmlNode* childNode( inNode->mFirstChild ); + while( childNode ) + { + XmlNode* _node( childNode ); + childNode = _node->mNextSibling; + releaseNodeAndChildren( inManager, _node ); + } + } + inNode->orphan(); + release( inManager, inNode ); + } + + static XmlNode* copyRepXNodeAndSiblings( TMemoryPoolManager* inManager, const XmlNode* inNode, XmlNode* inParent ); + + static XmlNode* copyRepXNode( TMemoryPoolManager* inManager, const XmlNode* inNode, XmlNode* inParent = NULL ) + { + XmlNode* newNode( allocateRepXNode( inManager, NULL, NULL ) ); + newNode->mName = inNode->mName; //Some light structural sharing + newNode->mData = inNode->mData; //Some light structural sharing + newNode->mParent = inParent; + if ( inNode->mFirstChild ) + newNode->mFirstChild = copyRepXNodeAndSiblings( inManager, inNode->mFirstChild, newNode ); + return newNode; + } + + static XmlNode* copyRepXNodeAndSiblings( TMemoryPoolManager* inManager, const XmlNode* inNode, XmlNode* inParent ) + { + XmlNode* sibling = inNode->mNextSibling; + if ( sibling ) sibling = copyRepXNodeAndSiblings( inManager, sibling, inParent ); + XmlNode* newNode = copyRepXNode( inManager, inNode, inParent ); + newNode->mNextSibling = sibling; + if ( sibling ) sibling->mPreviousSibling = newNode; + return newNode; + } + + inline bool isBigEndian() { int i = 1; return *(reinterpret_cast(&i))==0; } + + + struct NameStackEntry + { + const char* mName; + bool mOpen; + NameStackEntry( const char* nm ) : mName( nm ), mOpen( false ) {} + }; + + typedef PxProfileArray TNameStack; +} } + + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h new file mode 100644 index 000000000..a81e6141c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h @@ -0,0 +1,129 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_MEMORY_ALLOCATOR_H +#define PX_XML_MEMORY_ALLOCATOR_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx { + + class XmlMemoryAllocator + { + protected: + virtual ~XmlMemoryAllocator(){} + public: + virtual PxU8* allocate(PxU32 inSize) = 0; + virtual void deallocate( PxU8* inMem ) = 0; + virtual PxAllocatorCallback& getAllocator() = 0; + template + TObjectType* allocate() + { + TObjectType* retval = reinterpret_cast< TObjectType* >( allocate( sizeof( TObjectType ) ) ); + new (retval) TObjectType(); + return retval; + } + + template + TObjectType* allocate(const TArgType &arg) + { + TObjectType* retval = reinterpret_cast< TObjectType* >( allocate( sizeof( TObjectType ) ) ); + new (retval) TObjectType(arg); + return retval; + } + + template + void deallocate( TObjectType* inObject ) + { + deallocate( reinterpret_cast( inObject ) ); + } + template + inline TObjectType* batchAllocate(PxU32 inCount ) + { + TObjectType* retval = reinterpret_cast( allocate( sizeof(TObjectType) * inCount ) ); + for ( PxU32 idx = 0; idx < inCount; ++idx ) + { + new (retval + idx) TObjectType(); + } + return retval; + } + + template + inline TObjectType* batchAllocate(PxU32 inCount, const TArgType &arg) + { + TObjectType* retval = reinterpret_cast( allocate( sizeof(TObjectType) * inCount ) ); + for ( PxU32 idx = 0; idx < inCount; ++idx ) + { + new (retval + idx) TObjectType(arg); + } + return retval; + } + + + //Duplicate function definition for gcc. + template + inline TObjectType* batchAllocate(TObjectType*, PxU32 inCount ) + { + TObjectType* retval = reinterpret_cast( allocate( sizeof(TObjectType) * inCount ) ); + for ( PxU32 idx = 0; idx < inCount; ++idx ) + { + new (retval + idx) TObjectType(); + } + return retval; + } + }; + + struct XmlMemoryAllocatorImpl : public XmlMemoryAllocator + { + Sn::TMemoryPoolManager mManager; + + XmlMemoryAllocatorImpl( PxAllocatorCallback& inAllocator ) + : mManager( inAllocator ) + { + } + XmlMemoryAllocatorImpl &operator=(const XmlMemoryAllocatorImpl &); + virtual PxAllocatorCallback& getAllocator() + { + return mManager.getWrapper().getAllocator(); + } + + virtual PxU8* allocate(PxU32 inSize ) + { + if ( !inSize ) + return NULL; + + return mManager.allocate( inSize ); + } + virtual void deallocate( PxU8* inMem ) + { + if ( inMem ) + mManager.deallocate( inMem ); + } + }; +} +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h new file mode 100644 index 000000000..537ffb5ad --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h @@ -0,0 +1,373 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_MEMORYPOOL_H +#define PX_XML_MEMORYPOOL_H + +#include "foundation/PxAssert.h" +#include "PsArray.h" +#include "PxProfileAllocatorWrapper.h" + +namespace physx { + + using namespace physx::profile; + + /** + * Linked list used to store next node ptr. + */ + struct SMemPoolNode + { + SMemPoolNode* mNextNode; + }; + + /** + * Template arguments are powers of two. + * A very fast memory pool that is not memory efficient. It contains a vector of pointers + * to blocks of memory along with a linked list of free sections. All sections are + * of the same size so allocating memory is very fast, there isn't a linear search + * through blocks of indeterminate size. It also means there is memory wasted + * when objects aren't sized to powers of two. + */ + template + class CMemoryPool + { + typedef PxProfileArray TPxU8PtrList; + + PxProfileAllocatorWrapper& mWrapper; + TPxU8PtrList mAllMemory; + SMemPoolNode* mFirstFreeNode; + public: + CMemoryPool(PxProfileAllocatorWrapper& inWrapper) + : mWrapper( inWrapper ) + , mAllMemory( inWrapper ) + , mFirstFreeNode( NULL ) + {} + ~CMemoryPool() + { + TPxU8PtrList::ConstIterator theEnd = mAllMemory.end(); + for ( TPxU8PtrList::ConstIterator theIter = mAllMemory.begin(); + theIter != theEnd; + ++theIter ) + { + PxU8* thePtr = *theIter; + mWrapper.getAllocator().deallocate( thePtr ); + } + mAllMemory.clear(); + mFirstFreeNode = NULL; + } + //Using deallocated memory to hold the pointers to the next amount of memory. + PxU8* allocate() + { + if ( mFirstFreeNode ) + { + PxU8* retval = reinterpret_cast(mFirstFreeNode); + mFirstFreeNode = mFirstFreeNode->mNextNode; + return retval; + } + PxU32 itemSize = GetItemSize(); + PxU32 itemCount = 1 << TItemCount; + //No free nodes, make some more. + PxU8* retval = reinterpret_cast(mWrapper.getAllocator().allocate( itemCount * itemSize, "RepX fixed-size memory pool", __FILE__, __LINE__ )); + PxU8* dataPtr = retval + itemSize; + //Free extra chunks + for( PxU32 idx = 1; idx < itemCount; ++idx, dataPtr += itemSize ) + deallocate( dataPtr ); + mAllMemory.pushBack(retval); + return retval; + } + void deallocate( PxU8* inData ) + { + SMemPoolNode* nodePtr = reinterpret_cast(inData); + nodePtr->mNextNode = mFirstFreeNode; + mFirstFreeNode = nodePtr; + } + //We have to have at least a pointer's worth of memory + inline PxU32 GetItemSize() { return sizeof(SMemPoolNode) << TItemSize; } + }; + + typedef PxU32 TMemAllocSizeType; + + struct SVariableMemPoolNode : SMemPoolNode + { + TMemAllocSizeType mSize; + SVariableMemPoolNode* NextNode() { return static_cast< SVariableMemPoolNode* >( mNextNode ); } + }; + + /** + * Manages variable sized allocations. + * Keeps track of freed allocations in a insertion sorted + * list. Allocating new memory traverses the list linearly. + * This object will split nodes if the node is more than + * twice as large as the request memory allocation. + */ + class CVariableMemoryPool + { + typedef PxProfileHashMap TFreeNodeMap; + typedef PxProfileArray TPxU8PtrList; + PxProfileAllocatorWrapper& mWrapper; + TPxU8PtrList mAllMemory; + TFreeNodeMap mFreeNodeMap; + PxU32 mMinAllocationSize; + + CVariableMemoryPool &operator=(const CVariableMemoryPool &); + + public: + CVariableMemoryPool(PxProfileAllocatorWrapper& inWrapper, PxU32 inMinAllocationSize = 0x20 ) + : mWrapper( inWrapper ) + , mAllMemory( inWrapper ) + , mFreeNodeMap( inWrapper) + , mMinAllocationSize( inMinAllocationSize ) + {} + + ~CVariableMemoryPool() + { + TPxU8PtrList::ConstIterator theEnd = mAllMemory.end(); + for ( TPxU8PtrList::ConstIterator theIter = mAllMemory.begin(); + theIter != theEnd; + ++theIter ) + { + PxU8* thePtr = *theIter; + mWrapper.getAllocator().deallocate( thePtr ); + } + mAllMemory.clear(); + mFreeNodeMap.clear(); + } + PxU8* MarkMem( PxU8* inMem, TMemAllocSizeType inSize ) + { + PX_ASSERT( inSize >= sizeof( SVariableMemPoolNode ) ); + SVariableMemPoolNode* theMem = reinterpret_cast( inMem ); + theMem->mSize = inSize; + return reinterpret_cast< PxU8* >( theMem + 1 ); + } + //Using deallocated memory to hold the pointers to the next amount of memory. + PxU8* allocate( PxU32 size ) + { + //Ensure we can place the size of the memory at the start + //of the memory block. + //Kai: to reduce the size of hash map, the requested size is aligned to 128 bytes + PxU32 theRequestedSize = (size + sizeof(SVariableMemPoolNode) + 127) & ~127; + + TFreeNodeMap::Entry* entry = const_cast( mFreeNodeMap.find( theRequestedSize ) ); + if ( NULL != entry ) + { + SVariableMemPoolNode* theNode = entry->second; + PX_ASSERT( NULL != theNode ); + PX_ASSERT( theNode->mSize == theRequestedSize ); + entry->second = theNode->NextNode(); + if (entry->second == NULL) + mFreeNodeMap.erase( theRequestedSize ); + + return reinterpret_cast< PxU8* >( theNode + 1 ); + } + + if ( theRequestedSize < mMinAllocationSize ) + theRequestedSize = mMinAllocationSize; + + //No large enough free nodes, make some more. + PxU8* retval = reinterpret_cast(mWrapper.getAllocator().allocate( size_t(theRequestedSize), "RepX variable sized memory pool", __FILE__, __LINE__ )); + //If we allocated it, we free it. + mAllMemory.pushBack( retval ); + return MarkMem( retval, theRequestedSize ); + } + + //The size is stored at the beginning of the memory block. + void deallocate( PxU8* inData ) + { + SVariableMemPoolNode* theData = reinterpret_cast< SVariableMemPoolNode* >( inData ) - 1; + TMemAllocSizeType theSize = theData->mSize; + AddFreeMem( reinterpret_cast< PxU8* >( theData ), theSize ); + } + + void CheckFreeListInvariant( SVariableMemPoolNode* inNode ) + { + if ( inNode && inNode->mNextNode ) + { + PX_ASSERT( inNode->mSize <= inNode->NextNode()->mSize ); + } + } + + void AddFreeMem( PxU8* inMemory, TMemAllocSizeType inSize ) + { + PX_ASSERT( inSize >= sizeof( SVariableMemPoolNode ) ); + SVariableMemPoolNode* theNewNode = reinterpret_cast< SVariableMemPoolNode* >( inMemory ); + theNewNode->mNextNode = NULL; + theNewNode->mSize = inSize; + TFreeNodeMap::Entry* entry = const_cast( mFreeNodeMap.find( inSize ) ); + if (NULL != entry) + { + theNewNode->mNextNode = entry->second; + entry->second = theNewNode; + } + else + { + mFreeNodeMap.insert( inSize, theNewNode ); + } + } + }; + + /** + * The manager keeps a list of memory pools for different sizes of allocations. + * Anything too large simply gets allocated using the new operator. + * This doesn't mark the memory with the size of the allocated memory thus + * allowing much more efficient allocation of small items. For large enough + * allocations, it does mark the size. + * + * When using as a general memory manager, you need to wrap this class with + * something that actually does mark the returned allocation with the size + * of the allocation. + */ + class CMemoryPoolManager + { + CMemoryPoolManager &operator=(const CMemoryPoolManager &); + + public: + PxProfileAllocatorWrapper mWrapper; + + //CMemoryPool<0,8> m0ItemPool; + //CMemoryPool<1,8> m1ItemPool; + //CMemoryPool<2,8> m2ItemPool; + //CMemoryPool<3,8> m3ItemPool; + //CMemoryPool<4,8> m4ItemPool; + //CMemoryPool<5,8> m5ItemPool; + //CMemoryPool<6,8> m6ItemPool; + //CMemoryPool<7,8> m7ItemPool; + //CMemoryPool<8,8> m8ItemPool; + CVariableMemoryPool mVariablePool; + CMemoryPoolManager( PxAllocatorCallback& inAllocator ) + : mWrapper( inAllocator ) + //, m0ItemPool( mWrapper ) + //, m1ItemPool( mWrapper ) + //, m2ItemPool( mWrapper ) + //, m3ItemPool( mWrapper ) + //, m4ItemPool( mWrapper ) + //, m5ItemPool( mWrapper ) + //, m6ItemPool( mWrapper ) + //, m7ItemPool( mWrapper ) + //, m8ItemPool( mWrapper ) + , mVariablePool( mWrapper ) + { + } + PxProfileAllocatorWrapper& getWrapper() { return mWrapper; } + inline PxU8* allocate( PxU32 inSize ) + { + /* + if ( inSize <= m0ItemPool.GetItemSize() ) + return m0ItemPool.allocate(); + if ( inSize <= m1ItemPool.GetItemSize() ) + return m1ItemPool.allocate(); + if ( inSize <= m2ItemPool.GetItemSize() ) + return m2ItemPool.allocate(); + if ( inSize <= m3ItemPool.GetItemSize() ) + return m3ItemPool.allocate(); + if ( inSize <= m4ItemPool.GetItemSize() ) + return m4ItemPool.allocate(); + if ( inSize <= m5ItemPool.GetItemSize() ) + return m5ItemPool.allocate(); + if ( inSize <= m6ItemPool.GetItemSize() ) + return m6ItemPool.allocate(); + if ( inSize <= m7ItemPool.GetItemSize() ) + return m7ItemPool.allocate(); + if ( inSize <= m8ItemPool.GetItemSize() ) + return m8ItemPool.allocate(); + */ + return mVariablePool.allocate( inSize ); + } + inline void deallocate( PxU8* inMemory ) + { + if ( inMemory == NULL ) + return; + /* + if ( inSize <= m0ItemPool.GetItemSize() ) + m0ItemPool.deallocate(inMemory); + else if ( inSize <= m1ItemPool.GetItemSize() ) + m1ItemPool.deallocate(inMemory); + else if ( inSize <= m2ItemPool.GetItemSize() ) + m2ItemPool.deallocate(inMemory); + else if ( inSize <= m3ItemPool.GetItemSize() ) + m3ItemPool.deallocate(inMemory); + else if ( inSize <= m4ItemPool.GetItemSize() ) + m4ItemPool.deallocate(inMemory); + else if ( inSize <= m5ItemPool.GetItemSize() ) + m5ItemPool.deallocate(inMemory); + else if ( inSize <= m6ItemPool.GetItemSize() ) + m6ItemPool.deallocate(inMemory); + else if ( inSize <= m7ItemPool.GetItemSize() ) + m7ItemPool.deallocate(inMemory); + else if ( inSize <= m8ItemPool.GetItemSize() ) + m8ItemPool.deallocate(inMemory); + else + */ + mVariablePool.deallocate(inMemory); + } + /** + * allocate an object. Calls constructor on the new memory. + */ + template + inline TObjectType* allocate() + { + TObjectType* retval = reinterpret_cast( allocate( sizeof(TObjectType) ) ); + new (retval)TObjectType(); + return retval; + } + + /** + * deallocate an object calling the destructor on the object. + * This *must* be the concrete type, it cannot be a generic type. + */ + template + inline void deallocate( TObjectType* inObject ) + { + inObject->~TObjectType(); + deallocate( reinterpret_cast(inObject) ); + } + + /** + * allocate an object. Calls constructor on the new memory. + */ + template + inline TObjectType* BatchAllocate(PxU32 inCount ) + { + TObjectType* retval = reinterpret_cast( allocate( sizeof(TObjectType) * inCount ) ); + return retval; + } + + /** + * deallocate an object calling the destructor on the object. + * This *must* be the concrete type, it cannot be a generic type. + */ + template + inline void BatchDeallocate( TObjectType* inObject, PxU32 inCount ) + { + PX_UNUSED(inCount); + deallocate( reinterpret_cast(inObject) ); + } + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h new file mode 100644 index 000000000..a1e65dabd --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h @@ -0,0 +1,173 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_MEMORY_POOL_STREAMS_H +#define PX_XML_MEMORY_POOL_STREAMS_H + +#include "foundation/PxTransform.h" +#include "foundation/PxIO.h" +#include "SnXmlMemoryPool.h" +#include "CmPhysXCommon.h" + +namespace physx { + + template + struct XmlDefaultValue + { + bool force_compile_error; + }; + + +#define XML_DEFINE_DEFAULT_VALUE(type, defVal ) \ + template<> \ + struct XmlDefaultValue \ + { \ + type getDefaultValue() { return type(defVal); } \ + }; + + XML_DEFINE_DEFAULT_VALUE(PxU8, 0) + XML_DEFINE_DEFAULT_VALUE(PxI8, 0) + XML_DEFINE_DEFAULT_VALUE(PxU16, 0) + XML_DEFINE_DEFAULT_VALUE(PxI16, 0) + XML_DEFINE_DEFAULT_VALUE(PxU32, 0) + XML_DEFINE_DEFAULT_VALUE(PxI32, 0) + XML_DEFINE_DEFAULT_VALUE(PxU64, 0) + XML_DEFINE_DEFAULT_VALUE(PxI64, 0) + XML_DEFINE_DEFAULT_VALUE(PxF32, 0) + XML_DEFINE_DEFAULT_VALUE(PxF64, 0) + +#undef XML_DEFINE_DEFAULT_VALUE + + template<> + struct XmlDefaultValue + { + PxVec3 getDefaultValue() { return PxVec3( 0,0,0 ); } + }; + + template<> + struct XmlDefaultValue + { + PxTransform getDefaultValue() { return PxTransform(PxIdentity); } + }; + + template<> + struct XmlDefaultValue + { + PxQuat getDefaultValue() { return PxQuat(PxIdentity); } + }; + +/** + * Mapping of PxOutputStream to a memory pool manager. + * Allows write-then-read semantics of a set of + * data. Can safely write up to 4GB of data; then you + * will silently fail... + */ + +template +struct MemoryBufferBase : public PxOutputStream, public PxInputStream +{ + TAllocatorType* mManager; + mutable PxU32 mWriteOffset; + mutable PxU32 mReadOffset; + PxU8* mBuffer; + PxU32 mCapacity; + + + MemoryBufferBase( TAllocatorType* inManager ) + : mManager( inManager ) + , mWriteOffset( 0 ) + , mReadOffset( 0 ) + , mBuffer( NULL ) + , mCapacity( 0 ) + { + } + virtual ~MemoryBufferBase() + { + mManager->deallocate( mBuffer ); + } + PxU8* releaseBuffer() + { + clear(); + mCapacity = 0; + PxU8* retval(mBuffer); + mBuffer = NULL; + return retval; + } + void clear() + { + mWriteOffset = mReadOffset = 0; + } + + virtual PxU32 read(void* dest, PxU32 count) + { + bool fits = ( mReadOffset + count ) <= mWriteOffset; + PX_ASSERT( fits ); + if ( fits ) + { + PxMemCopy( dest, mBuffer + mReadOffset, count ); + mReadOffset += count; + return count; + } + return 0; + } + + inline void checkCapacity( PxU32 inNewCapacity ) + { + if ( mCapacity < inNewCapacity ) + { + PxU32 newCapacity = 32; + while( newCapacity < inNewCapacity ) + newCapacity = newCapacity << 1; + + PxU8* newData( mManager->allocate( newCapacity ) ); + if ( mWriteOffset ) + PxMemCopy( newData, mBuffer, mWriteOffset ); + mManager->deallocate( mBuffer ); + mBuffer = newData; + mCapacity = newCapacity; + } + } + + virtual PxU32 write(const void* src, PxU32 count) + { + checkCapacity( mWriteOffset + count ); + PxMemCopy( mBuffer + mWriteOffset, src, count ); + mWriteOffset += count; + return count; + } +}; + +class MemoryBuffer : public MemoryBufferBase +{ +public: + MemoryBuffer( CMemoryPoolManager* inManager ) : MemoryBufferBase( inManager ) {} +}; + +} + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h new file mode 100644 index 000000000..d5caa465b --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_READER_H +#define PX_XML_READER_H + +#include "foundation/PxSimpleTypes.h" +#include "extensions/PxRepXSimpleType.h" + +namespace physx { + namespace Sn { struct XmlNode; } + + /** + * Reader used to read data out of the repx format. + */ + class XmlReader + { + protected: + virtual ~XmlReader(){} + public: + /** Read a key-value pair out of the database */ + virtual bool read( const char* inName, const char*& outData ) = 0; + /** Read an object id out of the database */ + virtual bool read( const char* inName, PxSerialObjectId& outId ) = 0; + /** Goto a child element by name. That child becomes this reader's context */ + virtual bool gotoChild( const char* inName ) = 0; + /** Goto the first child regardless of name */ + virtual bool gotoFirstChild() = 0; + /** Goto the next sibling regardless of name */ + virtual bool gotoNextSibling() = 0; + /** Count all children of the current object */ + virtual PxU32 countChildren() = 0; + /** Get the name of the current item */ + virtual const char* getCurrentItemName() = 0; + /** Get the value of the current item */ + virtual const char* getCurrentItemValue() = 0; + /** Leave the current child */ + virtual bool leaveChild() = 0; + /** Get reader for the parental object */ + virtual XmlReader* getParentReader() = 0; + + /** + * Ensures we don't leave the reader in an odd state + * due to not leaving a given child + */ + virtual void pushCurrentContext() = 0; + /** Pop the current context back to where it during push*/ + virtual void popCurrentContext() = 0; + }; + + //Used when upgrading a repx collection + class XmlReaderWriter : public XmlReader + { + public: + //Clears the stack of nodes (push/pop current node reset) + //and sets the current node to inNode. + virtual void setNode( Sn::XmlNode& node ) = 0; + //If the child exists, add it. + //the either way goto that child. + virtual void addOrGotoChild( const char* name ) = 0; + //Value is copied into the collection, inValue has no further references + //to it. + virtual void setCurrentItemValue( const char* value ) = 0; + //Removes the child but does not release the char* name or char* data ptrs. + //Those pointers are never released and are shared among collections. + //Thus copying nodes is cheap and safe. + virtual bool removeChild( const char* name ) = 0; + virtual void release() = 0; + + bool renameProperty( const char* oldName, const char* newName ) + { + if ( gotoChild( oldName ) ) + { + const char* value = getCurrentItemValue(); + leaveChild(); + removeChild( oldName ); + addOrGotoChild( newName ); + setCurrentItemValue( value ); + leaveChild(); + return true; + } + return false; + } + bool readAndRemoveProperty( const char* name, const char*& outValue ) + { + bool retval = read( name, outValue ); + if ( retval ) removeChild( name ); + return retval; + } + + bool writePropertyIfNotEmpty( const char* name, const char* value ) + { + if ( value && *value ) + { + addOrGotoChild( name ); + setCurrentItemValue( value ); + leaveChild(); + return true; + } + return false; + } + }; + +} +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp new file mode 100644 index 000000000..dc9f7a87c --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp @@ -0,0 +1,834 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#include "SnXmlImpl.h" +#include "PsHash.h" +#include "PsHashMap.h" +#include "SnSimpleXmlWriter.h" +#include "PsSort.h" +#include "PsFastXml.h" +#include "PsString.h" +#include "SnXmlMemoryPool.h" +#include "PxExtensionMetaDataObjects.h" +#include "SnXmlVisitorWriter.h" +#include "SnXmlVisitorReader.h" +#include "SnXmlMemoryAllocator.h" +#include "SnXmlStringToType.h" +#include "PsString.h" +#include "SnRepXCollection.h" +#include "SnRepXUpgrader.h" +#include "../SnSerializationRegistry.h" +#include "PsFoundation.h" +#include "CmCollection.h" + +using namespace physx; +using namespace Sn; + +using namespace physx::profile; //for the foundation wrapper system. + +namespace physx { namespace Sn { + + class XmlNodeWriter : public SimpleXmlWriter + { + XmlMemoryAllocatorImpl& mParseAllocator; + XmlNode* mCurrentNode; + XmlNode* mTopNode; + PxU32 mTabCount; + + public: + XmlNodeWriter( XmlMemoryAllocatorImpl& inAllocator, PxU32 inTabCount = 0 ) + : mParseAllocator( inAllocator ) + , mCurrentNode( NULL ) + , mTopNode( NULL ) + , mTabCount( inTabCount ) + {} + XmlNodeWriter& operator=(const XmlNodeWriter&); + virtual ~XmlNodeWriter(){} + void onNewNode( XmlNode* newNode ) + { + if ( mCurrentNode != NULL ) + mCurrentNode->addChild( newNode ); + if ( mTopNode == NULL ) + mTopNode = newNode; + mCurrentNode = newNode; + ++mTabCount; + } + + XmlNode* getTopNode() const { return mTopNode; } + + virtual void beginTag( const char* inTagname ) + { + onNewNode( allocateRepXNode( &mParseAllocator.mManager, inTagname, NULL ) ); + } + virtual void endTag() + { + if ( mCurrentNode ) + mCurrentNode = mCurrentNode->mParent; + if ( mTabCount ) + --mTabCount; + } + virtual void addAttribute( const char*, const char* ) + { + PX_ASSERT( false ); + } + virtual void writeContentTag( const char* inTag, const char* inContent ) + { + onNewNode( allocateRepXNode( &mParseAllocator.mManager, inTag, inContent ) ); + endTag(); + } + virtual void addContent( const char* inContent ) + { + if ( mCurrentNode->mData ) + releaseStr( &mParseAllocator.mManager, mCurrentNode->mData ); + mCurrentNode->mData = copyStr( &mParseAllocator.mManager, inContent ); + } + virtual PxU32 tabCount() { return mTabCount; } + }; + + struct XmlWriterImpl : public XmlWriter + { + PxU32 mTagDepth; + SimpleXmlWriter* mWriter; + MemoryBuffer* mMemBuffer; + + XmlWriterImpl( SimpleXmlWriter* inWriter, MemoryBuffer* inMemBuffer ) + : mTagDepth( 0 ) + , mWriter( inWriter ) + , mMemBuffer( inMemBuffer ) + { + } + ~XmlWriterImpl() + { + while( mTagDepth ) + { + --mTagDepth; + mWriter->endTag(); + } + } + virtual void write( const char* inName, const char* inData ) + { + mWriter->writeContentTag( inName, inData ); + } + virtual void write( const char* inName, const PxRepXObject& inLiveObject ) + { + (*mMemBuffer) << inLiveObject.id; + writeProperty( *mWriter, *mMemBuffer, inName ); + } + virtual void addAndGotoChild( const char* inName ) + { + mWriter->beginTag( inName ); + mTagDepth++; + } + virtual void leaveChild() + { + if ( mTagDepth ) + { + mWriter->endTag(); + --mTagDepth; + } + } + }; + + struct XmlParseArgs + { + XmlMemoryAllocatorImpl* mAllocator; + PxProfileArray* mCollection; + + XmlParseArgs( XmlMemoryAllocatorImpl* inAllocator + , PxProfileArray* inCollection) + : mAllocator( inAllocator ) + , mCollection( inCollection ) + { + } + }; + + struct XmlNodeReader : public XmlReaderWriter + { + PxProfileAllocatorWrapper mWrapper; + CMemoryPoolManager& mManager; + XmlNode* mCurrentNode; + XmlNode* mTopNode; + PxProfileArray mContext; + XmlNodeReader( XmlNode* inCurrentNode, PxAllocatorCallback& inAllocator, CMemoryPoolManager& nodePoolManager ) + : mWrapper( inAllocator ) + , mManager( nodePoolManager ) + , mCurrentNode( inCurrentNode ) + , mTopNode( inCurrentNode ) + , mContext( mWrapper ) + { + } + + //Does this node exist as data in the format. + virtual bool read( const char* inName, const char*& outData ) + { + XmlNode* theChild( mCurrentNode->findChildByName( inName ) ); + if ( theChild ) + { + outData = theChild->mData; + return outData && *outData; + } + return false; + } + + virtual bool read( const char* inName, PxSerialObjectId& outId ) + { + XmlNode* theChild( mCurrentNode->findChildByName( inName ) ); + if ( theChild ) + { + const char* theValue( theChild->mData ); + strto( outId, theValue ); + return true; + } + return false; + } + + virtual bool gotoChild( const char* inName ) + { + XmlNode* theChild( mCurrentNode->findChildByName( inName ) ); + if ( theChild ) + { + mCurrentNode =theChild; + return true; + } + return false; + } + virtual bool gotoFirstChild() + { + if ( mCurrentNode->mFirstChild ) + { + mCurrentNode = mCurrentNode->mFirstChild; + return true; + } + return false; + } + virtual bool gotoNextSibling() + { + if ( mCurrentNode->mNextSibling ) + { + mCurrentNode = mCurrentNode->mNextSibling; + return true; + } + return false; + } + virtual PxU32 countChildren() + { + PxU32 retval= 0; + for ( XmlNode* theChild = mCurrentNode->mFirstChild; theChild != NULL; theChild = theChild->mNextSibling ) + ++retval; + return retval; + } + virtual const char* getCurrentItemName() + { + return mCurrentNode->mName; + } + virtual const char* getCurrentItemValue() + { + return mCurrentNode->mData; + } + + virtual bool leaveChild() + { + if ( mCurrentNode != mTopNode && mCurrentNode->mParent ) + { + mCurrentNode = mCurrentNode->mParent; + return true; + } + return false; + } + + virtual void pushCurrentContext() + { + mContext.pushBack( mCurrentNode ); + } + virtual void popCurrentContext() + { + if ( mContext.size() ) + { + mCurrentNode = mContext.back(); + mContext.popBack(); + } + } + + virtual void setNode( XmlNode& inNode ) + { + mContext.clear(); + mCurrentNode = &inNode; + mTopNode = mCurrentNode; + } + + virtual XmlReader* getParentReader() + { + XmlReader* retval = PX_PLACEMENT_NEW((mWrapper.getAllocator().allocate(sizeof(XmlNodeReader), "createNodeEditor", __FILE__, __LINE__ )), XmlNodeReader) + ( mTopNode, mWrapper.getAllocator(), mManager ); + return retval; + } + + virtual void addOrGotoChild( const char* inName ) + { + if ( gotoChild( inName )== false ) + { + XmlNode* newNode = allocateRepXNode( &mManager, inName, NULL ); + mCurrentNode->addChild( newNode ); + mCurrentNode = newNode; + } + } + virtual void setCurrentItemValue( const char* inValue ) + { + mCurrentNode->mData = copyStr( &mManager, inValue ); + } + virtual bool removeChild( const char* name ) + { + XmlNode* theChild( mCurrentNode->findChildByName( name ) ); + if ( theChild ) + { + releaseNodeAndChildren( &mManager, theChild ); + return true; + } + return false; + } + virtual void release() { this->~XmlNodeReader(); mWrapper.getAllocator().deallocate(this); } + + private: + XmlNodeReader& operator=(const XmlNodeReader&); + }; + + PX_INLINE void freeNodeAndChildren( XmlNode* tempNode, TMemoryPoolManager& inManager ) + { + for( XmlNode* theNode = tempNode->mFirstChild; theNode != NULL; theNode = theNode->mNextSibling ) + freeNodeAndChildren( theNode, inManager ); + tempNode->orphan(); + release( &inManager, tempNode ); + } + + class XmlParser : public Ps::FastXml::Callback + { + XmlParseArgs mParseArgs; + //For parse time only allocations + XmlMemoryAllocatorImpl& mParseAllocator; + XmlNode* mCurrentNode; + XmlNode* mTopNode; + + public: + XmlParser( XmlParseArgs inArgs, XmlMemoryAllocatorImpl& inParseAllocator ) + : mParseArgs( inArgs ) + , mParseAllocator( inParseAllocator ) + , mCurrentNode( NULL ) + , mTopNode( NULL ) + { + } + + virtual ~XmlParser(){} + + virtual bool processComment(const char* /*comment*/) { return true; } + // 'element' is the name of the element that is being closed. + // depth is the recursion depth of this element. + // Return true to continue processing the XML file. + // Return false to stop processing the XML file; leaves the read pointer of the stream right after this close tag. + // The bool 'isError' indicates whether processing was stopped due to an error, or intentionally canceled early. + virtual bool processClose(const char* /*element*/,physx::PxU32 /*depth*/,bool& /*isError*/) + { + mCurrentNode = mCurrentNode->mParent; + return true; + } + + // return true to continue processing the XML document, false to skip. + virtual bool processElement( + const char *elementName, // name of the element + const char *elementData, // element data, null if none + const Ps::FastXml::AttributePairs& attr, // attributes + PxI32 /*lineno*/) + { + XmlNode* newNode = allocateRepXNode( &mParseAllocator.mManager, elementName, elementData ); + if ( mCurrentNode ) + mCurrentNode->addChild( newNode ); + mCurrentNode = newNode; + //Add the elements as children. + for( PxI32 item = 0; item < attr.getNbAttr(); item ++ ) + { + XmlNode* node = allocateRepXNode( &mParseAllocator.mManager, attr.getKey(PxU32(item)), attr.getValue(PxU32(item)) ); + mCurrentNode->addChild( node ); + } + if ( mTopNode == NULL ) mTopNode = newNode; + return true; + } + + XmlNode* getTopNode() { return mTopNode; } + + virtual void * allocate(PxU32 size) + { + if ( size ) + return mParseAllocator.allocate(size); + return NULL; + } + virtual void deallocate(void *mem) + { + if ( mem ) + mParseAllocator.deallocate(reinterpret_cast(mem)); + } + + private: + XmlParser& operator=(const XmlParser&); + }; + + struct RepXCollectionSharedData + { + PxProfileAllocatorWrapper mWrapper; + XmlMemoryAllocatorImpl mAllocator; + PxU32 mRefCount; + + RepXCollectionSharedData( PxAllocatorCallback& inAllocator ) + : mWrapper( inAllocator ) + , mAllocator( inAllocator ) + , mRefCount( 0 ) + { + } + ~RepXCollectionSharedData() {} + + void addRef() { ++mRefCount;} + void release() + { + if ( mRefCount ) --mRefCount; + if ( !mRefCount ) { this->~RepXCollectionSharedData(); mWrapper.getAllocator().deallocate(this);} + } + }; + + struct SharedDataPtr + { + RepXCollectionSharedData* mData; + SharedDataPtr( RepXCollectionSharedData* inData ) + : mData( inData ) + { + mData->addRef(); + } + SharedDataPtr( const SharedDataPtr& inOther ) + : mData( inOther.mData ) + { + mData->addRef(); + } + SharedDataPtr& operator=( const SharedDataPtr& inOther ); + ~SharedDataPtr() + { + mData->release(); + mData = NULL; + } + RepXCollectionSharedData* operator->() { return mData; } + const RepXCollectionSharedData* operator->() const { return mData; } + }; + + class RepXCollectionImpl : public RepXCollection, public Ps::UserAllocated + { + SharedDataPtr mSharedData; + + XmlMemoryAllocatorImpl& mAllocator; + PxSerializationRegistry& mSerializationRegistry; + PxProfileArray mCollection; + TMemoryPoolManager mSerializationManager; + MemoryBuffer mPropertyBuffer; + PxTolerancesScale mScale; + PxVec3 mUpVector; + const char* mVersionStr; + PxCollection* mPxCollection; + + public: + RepXCollectionImpl( PxSerializationRegistry& inRegistry, PxAllocatorCallback& inAllocator, PxCollection& inPxCollection ) + : mSharedData( &PX_NEW_REPX_SERIALIZER( RepXCollectionSharedData )) + , mAllocator( mSharedData->mAllocator ) + , mSerializationRegistry( inRegistry ) + , mCollection( mSharedData->mWrapper ) + , mSerializationManager( inAllocator ) + , mPropertyBuffer( &mSerializationManager ) + , mUpVector( 0,0,0 ) + , mVersionStr( getLatestVersion() ) + , mPxCollection( &inPxCollection ) + { + memset( &mScale, 0, sizeof( PxTolerancesScale ) ); + PX_ASSERT( mScale.isValid() == false ); + } + + RepXCollectionImpl( PxSerializationRegistry& inRegistry, const RepXCollectionImpl& inSrc, const char* inNewVersion ) + : mSharedData( inSrc.mSharedData ) + , mAllocator( mSharedData->mAllocator ) + , mSerializationRegistry( inRegistry ) + , mCollection( mSharedData->mWrapper ) + , mSerializationManager( mSharedData->mWrapper.getAllocator() ) + , mPropertyBuffer( &mSerializationManager ) + , mScale( inSrc.mScale ) + , mUpVector( inSrc.mUpVector ) + , mVersionStr( inNewVersion ) + , mPxCollection( NULL ) + { + } + + virtual ~RepXCollectionImpl() + { + PxU32 numItems = mCollection.size(); + for ( PxU32 idx = 0; idx < numItems; ++idx ) + { + XmlNode* theNode = mCollection[idx].descriptor; + releaseNodeAndChildren( &mAllocator.mManager, theNode ); + } + } + RepXCollectionImpl& operator=(const RepXCollectionImpl&); + + virtual void destroy() + { + PxProfileAllocatorWrapper tempWrapper( mSharedData->mWrapper.getAllocator() ); + this->~RepXCollectionImpl(); + tempWrapper.getAllocator().deallocate(this); + } + + virtual void setTolerancesScale(const PxTolerancesScale& inScale) { mScale = inScale; } + virtual PxTolerancesScale getTolerancesScale() const { return mScale; } + virtual void setUpVector( const PxVec3& inUpVector ) { mUpVector = inUpVector; } + virtual PxVec3 getUpVector() const { return mUpVector; } + + + PX_INLINE RepXCollectionItem findItemBySceneItem( const PxRepXObject& inObject ) const + { + //See if the object is in the collection + for ( PxU32 idx =0; idx < mCollection.size(); ++idx ) + if ( mCollection[idx].liveObject.serializable == inObject.serializable ) + return mCollection[idx]; + return RepXCollectionItem(); + } + + virtual RepXAddToCollectionResult addRepXObjectToCollection( const PxRepXObject& inObject, PxCollection* inCollection, PxRepXInstantiationArgs& inArgs ) + { + PX_ASSERT( inObject.serializable ); + PX_ASSERT( inObject.id ); + if ( inObject.serializable == NULL || inObject.id == 0 ) + return RepXAddToCollectionResult( RepXAddToCollectionResult::InvalidParameters ); + + PxRepXSerializer* theSerializer = mSerializationRegistry.getRepXSerializer( inObject.typeName ); + if ( theSerializer == NULL ) + return RepXAddToCollectionResult( RepXAddToCollectionResult::SerializerNotFound ); + + RepXCollectionItem existing = findItemBySceneItem( inObject ); + if ( existing.liveObject.serializable ) + return RepXAddToCollectionResult( RepXAddToCollectionResult::AlreadyInCollection, existing.liveObject.id ); + + XmlNodeWriter theXmlWriter( mAllocator, 1 ); + XmlWriterImpl theRepXWriter( &theXmlWriter, &mPropertyBuffer ); + { + SimpleXmlWriter::STagWatcher theWatcher( theXmlWriter, inObject.typeName ); + writeProperty( theXmlWriter, mPropertyBuffer, "Id", inObject.id ); + theSerializer->objectToFile( inObject, inCollection, theRepXWriter, mPropertyBuffer,inArgs ); + } + mCollection.pushBack( RepXCollectionItem( inObject, theXmlWriter.getTopNode() ) ); + return RepXAddToCollectionResult( RepXAddToCollectionResult::Success, inObject.id ); + } + + virtual bool instantiateCollection( PxRepXInstantiationArgs& inArgs, PxCollection& inCollection ) + { + for ( PxU32 idx =0; idx < mCollection.size(); ++idx ) + { + RepXCollectionItem theItem( mCollection[idx] ); + PxRepXSerializer* theSerializer = mSerializationRegistry.getRepXSerializer( theItem.liveObject.typeName ); + if (theSerializer ) + { + XmlNodeReader theReader( theItem.descriptor, mAllocator.getAllocator(), mAllocator.mManager ); + XmlMemoryAllocatorImpl instantiationAllocator( mAllocator.getAllocator() ); + PxRepXObject theLiveObject = theSerializer->fileToObject( theReader, instantiationAllocator, inArgs, &inCollection ); + if (theLiveObject.isValid()) + { + const PxBase* s = reinterpret_cast( theLiveObject.serializable ) ; + inCollection.add( *const_cast(s), PxSerialObjectId( theItem.liveObject.id )); + } + else + return false; + } + else + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PxSerialization::createCollectionFromXml: " + "PxRepXSerializer missing for type %s", theItem.liveObject.typeName); + return false; + } + } + + return true; + } + + void saveXmlNode( XmlNode* inNode, SimpleXmlWriter& inWriter ) + { + XmlNode* theNode( inNode ); + if ( theNode->mData && *theNode->mData && theNode->mFirstChild == NULL ) + inWriter.writeContentTag( theNode->mName, theNode->mData ); + else + { + inWriter.beginTag( theNode->mName ); + if ( theNode->mData && *theNode->mData ) + inWriter.addContent( theNode->mData ); + for ( XmlNode* theChild = theNode->mFirstChild; + theChild != NULL; + theChild = theChild->mNextSibling ) + saveXmlNode( theChild, inWriter ); + inWriter.endTag(); + } + } + + virtual void save( PxOutputStream& inStream ) + { + SimpleXmlWriterImpl theWriter( inStream, mAllocator.getAllocator() ); + theWriter.beginTag( "PhysX30Collection" ); + theWriter.addAttribute( "version", mVersionStr ); + { + XmlWriterImpl theRepXWriter( &theWriter, &mPropertyBuffer ); + writeProperty( theWriter, mPropertyBuffer, "UpVector", mUpVector ); + theRepXWriter.addAndGotoChild( "Scale" ); + writeAllProperties( &mScale, theRepXWriter, mPropertyBuffer, *mPxCollection); + theRepXWriter.leaveChild(); + } + for ( PxU32 idx =0; idx < mCollection.size(); ++idx ) + { + RepXCollectionItem theItem( mCollection[idx] ); + XmlNode* theNode( theItem.descriptor ); + saveXmlNode( theNode, theWriter ); + } + } + + void load( PxInputData& inFileBuf, SerializationRegistry& s ) + { + inFileBuf.seek(0); + XmlParser theParser( XmlParseArgs( &mAllocator, &mCollection ), mAllocator ); + Ps::FastXml* theFastXml = Ps::createFastXml( &theParser ); + theFastXml->processXml( inFileBuf ); + XmlNode* theTopNode = theParser.getTopNode(); + if ( theTopNode != NULL ) + { + { + + XmlMemoryAllocatorImpl instantiationAllocator( mAllocator.getAllocator() ); + XmlNodeReader theReader( theTopNode, mAllocator.getAllocator(), mAllocator.mManager ); + readProperty( theReader, "UpVector", mUpVector ); + if ( theReader.gotoChild( "Scale" ) ) + { + readAllProperties( PxRepXInstantiationArgs( s.getPhysics() ), theReader, &mScale, instantiationAllocator, *mPxCollection); + theReader.leaveChild(); + } + const char* verStr = NULL; + if ( theReader.read( "version", verStr ) ) + mVersionStr = verStr; + } + for ( XmlNode* theChild = theTopNode->mFirstChild; + theChild != NULL; + theChild = theChild->mNextSibling ) + { + if ( physx::shdfnd::stricmp( theChild->mName, "scale" ) == 0 + || physx::shdfnd::stricmp( theChild->mName, "version" ) == 0 + || physx::shdfnd::stricmp( theChild->mName, "upvector" ) == 0 ) + continue; + XmlNodeReader theReader( theChild, mAllocator.getAllocator(), mAllocator.mManager ); + PxRepXObject theObject; + theObject.typeName = theChild->mName; + theObject.serializable = NULL; + PxSerialObjectId theId = 0; + theReader.read( "Id", theId ); + theObject.id = theId; + mCollection.pushBack( RepXCollectionItem( theObject, theChild ) ); + } + } + else + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "Cannot parse any object from the input buffer, please check the input repx data."); + } + theFastXml->release(); + } + + virtual const char* getVersion() { return mVersionStr; } + + virtual const RepXCollectionItem* begin() const + { + return mCollection.begin(); + } + virtual const RepXCollectionItem* end() const + { + return mCollection.end(); + } + + virtual RepXCollection& createCollection( const char* inVersionStr ) + { + PxAllocatorCallback& allocator = mSharedData->mWrapper.getAllocator(); + RepXCollectionImpl* retval = PX_PLACEMENT_NEW((allocator.allocate(sizeof(RepXCollectionImpl), "createCollection", __FILE__, __LINE__ )), RepXCollectionImpl) ( mSerializationRegistry, *this, inVersionStr ); + + return *retval; + } + + //Performs a deep copy of the repx node. + virtual XmlNode* copyRepXNode( const XmlNode* srcNode ) + { + return physx::Sn::copyRepXNode( &mAllocator.mManager, srcNode ); + } + + virtual void addCollectionItem( RepXCollectionItem inItem ) + { + mCollection.pushBack( inItem ); + } + + virtual PxAllocatorCallback& getAllocator() { return mSharedData->mAllocator.getAllocator(); } + //Create a new repx node with this name. Its value is unset. + virtual XmlNode& createRepXNode( const char* name ) + { + XmlNode* newNode = allocateRepXNode( &mSharedData->mAllocator.mManager, name, NULL ); + return *newNode; + } + + //Release this when finished. + virtual XmlReaderWriter& createNodeEditor() + { + PxAllocatorCallback& allocator = mSharedData->mWrapper.getAllocator(); + XmlReaderWriter* retval = PX_PLACEMENT_NEW((allocator.allocate(sizeof(XmlNodeReader), "createNodeEditor", __FILE__, __LINE__ )), XmlNodeReader) ( NULL, allocator, mAllocator.mManager ); + return *retval; + } + }; + + const char* RepXCollection::getLatestVersion() + { +#define TOSTR_(x) #x +#define CONCAT_(a, b, c) TOSTR_(a.##b.##c) +#define MAKE_VERSION_STR(a,b,c) CONCAT_(a, b, c) + + return MAKE_VERSION_STR(PX_PHYSICS_VERSION_MAJOR,PX_PHYSICS_VERSION_MINOR,PX_PHYSICS_VERSION_BUGFIX); + + } + + static RepXCollection* create(SerializationRegistry& s, PxAllocatorCallback& inAllocator, PxCollection& inCollection ) + { + return PX_PLACEMENT_NEW((inAllocator.allocate(sizeof(RepXCollectionImpl), "RepXCollection::create", __FILE__, __LINE__ )), RepXCollectionImpl) ( s, inAllocator, inCollection ); + } + + static RepXCollection* create(SerializationRegistry& s, PxInputData &data, PxAllocatorCallback& inAllocator, PxCollection& inCollection ) + { + RepXCollectionImpl* theCollection = static_cast( create(s, inAllocator, inCollection ) ); + theCollection->load( data, s ); + return theCollection; + } +} + + bool PxSerialization::serializeCollectionToXml( PxOutputStream& outputStream, PxCollection& collection, PxSerializationRegistry& sr, PxCooking* cooking, const PxCollection* externalRefs, PxXmlMiscParameter* inArgs ) + { + if( !PxSerialization::isSerializable(collection, sr, const_cast(externalRefs)) ) + return false; + + bool bRet = true; + + SerializationRegistry& sn = static_cast(sr); + PxRepXInstantiationArgs args( sn.getPhysics(), cooking ); + + PxCollection* tmpCollection = PxCreateCollection(); + PX_ASSERT(tmpCollection); + + tmpCollection->add( collection ); + if(externalRefs) + { + tmpCollection->add(*const_cast(externalRefs)); + } + + PxAllocatorCallback& allocator = PxGetFoundation().getAllocatorCallback(); + Sn::RepXCollection* theRepXCollection = Sn::create(sn, allocator, *tmpCollection ); + + if(inArgs != NULL) + { + theRepXCollection->setTolerancesScale(inArgs->scale); + theRepXCollection->setUpVector(inArgs->upVector); + } + + PxU32 nbObjects = collection.getNbObjects(); + if( nbObjects ) + { + sortCollection( static_cast(collection), sn, true); + + for( PxU32 i = 0; i < nbObjects; i++ ) + { + PxBase& s = collection.getObject(i); + if( PxConcreteType::eSHAPE == s.getConcreteType() ) + { + PxShape& shape = static_cast(s); + if( shape.isExclusive() ) + continue; + } + + PxSerialObjectId id = collection.getId(s); + if(id == PX_SERIAL_OBJECT_ID_INVALID) + id = static_cast( reinterpret_cast( &s )); + + PxRepXObject ro = PxCreateRepXObject( &s, id ); + if ( ro.serializable == NULL || ro.id == 0 ) + { + bRet = false; + break; + } + + theRepXCollection->addRepXObjectToCollection( ro, tmpCollection, args ); + } + } + tmpCollection->release(); + + theRepXCollection->save(outputStream); + theRepXCollection->destroy(); + + + return bRet; + } + + PxCollection* PxSerialization::createCollectionFromXml(PxInputData& inputData, PxCooking& cooking, PxSerializationRegistry& sr, const PxCollection* externalRefs, PxStringTable* stringTable, PxXmlMiscParameter* outArgs) + { + SerializationRegistry& sn = static_cast(sr); + PxCollection* collection = PxCreateCollection(); + PX_ASSERT(collection); + + if( externalRefs ) + collection->add(*const_cast(externalRefs)); + + PxAllocatorCallback& allocator = PxGetFoundation().getAllocatorCallback(); + Sn::RepXCollection* theRepXCollection = Sn::create(sn, inputData, allocator, *collection); + theRepXCollection = &Sn::RepXUpgrader::upgradeCollection( *theRepXCollection ); + + PxRepXInstantiationArgs args( sn.getPhysics(), &cooking, stringTable ); + if( !theRepXCollection->instantiateCollection(args, *collection) ) + { + collection->release(); + theRepXCollection->destroy(); + return NULL; + } + + if( externalRefs ) + collection->remove(*const_cast(externalRefs)); + + if(outArgs != NULL) + { + outArgs->upVector = theRepXCollection->getUpVector(); + outArgs->scale = theRepXCollection->getTolerancesScale(); + } + + theRepXCollection->destroy(); + + return collection; + } +} diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h new file mode 100644 index 000000000..caf71e286 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h @@ -0,0 +1,117 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_SERIALIZER_H +#define PX_XML_SERIALIZER_H + +#include "PxExtensionMetaDataObjects.h" +#include "SnXmlVisitorWriter.h" + +namespace physx { + +namespace Sn { + + void writeHeightFieldSample( PxOutputStream& inStream, const PxHeightFieldSample& inSample ) + { + PxU32 retval = 0; + PxU8* writePtr( reinterpret_cast< PxU8*>( &retval ) ); + const PxU8* inPtr( reinterpret_cast( &inSample ) ); + if ( isBigEndian() ) + { + //Height field samples are a + //16 bit integer followed by two bytes. + //right now, data is 2 1 3 4 + //We need a 32 bit integer that + //when read in by a LE system is 4 3 2 1. + //Thus, we need a BE integer that looks like: + //4 3 2 1 + + writePtr[0] = inPtr[3]; + writePtr[1] = inPtr[2]; + writePtr[2] = inPtr[0]; + writePtr[3] = inPtr[1]; + } + else + { + writePtr[0] = inPtr[0]; + writePtr[1] = inPtr[1]; + writePtr[2] = inPtr[2]; + writePtr[3] = inPtr[3]; + } + inStream << retval; + } + + + + template + inline void writeStridedBufferProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, const void* inData, PxU32 inStride, PxU32 inCount, PxU32 inItemsPerLine, TWriteOperator inOperator ) + { + PX_ASSERT( inStride == 0 || inStride == sizeof( TDataType ) ); + PX_UNUSED( inStride ); + writeBuffer( writer, tempBuffer + , inItemsPerLine, reinterpret_cast( inData ) + , inCount, inPropName, inOperator ); + } + + template + inline void writeStridedBufferProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, const PxStridedData& inData, PxU32 inCount, PxU32 inItemsPerLine, TWriteOperator inOperator ) + { + writeStridedBufferProperty( writer, tempBuffer, inPropName, inData.data, inData.stride, inCount, inItemsPerLine, inOperator ); + } + + template + inline void writeStridedBufferProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, const PxTypedStridedData& inData, PxU32 inCount, PxU32 inItemsPerLine, TWriteOperator inOperator ) + { + writeStridedBufferProperty( writer, tempBuffer, inPropName, inData.data, inData.stride, inCount, inItemsPerLine, inOperator ); + } + + template + inline void writeStridedBufferProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, const PxBoundedData& inData, PxU32 inItemsPerLine, TWriteOperator inWriteOperator ) + { + writeStridedBufferProperty( writer, tempBuffer, inPropName, inData, inData.count, inItemsPerLine, inWriteOperator ); + } + + template + inline void writeStridedBufferProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, PxStrideIterator& inData, PxU32 inCount, PxU32 inItemsPerLine, TWriteOperator inWriteOperator ) + { + writeStrideBuffer(writer, tempBuffer + , inItemsPerLine, inData, PtrAccess + , inCount, inPropName, inData.stride(), inWriteOperator ); + } + + template + inline void writeStridedFlagsProperty( XmlWriter& writer, MemoryBuffer& tempBuffer, const char* inPropName, PxStrideIterator& inData, PxU32 inCount, PxU32 inItemsPerLine, const PxU32ToName* inTable ) + { + writeStrideFlags(writer, tempBuffer + , inItemsPerLine, inData, PtrAccess + , inCount, inPropName, inTable ); + } + +} +} +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h new file mode 100644 index 000000000..3de9049eb --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_SIMPLEXMLWRITER_H +#define PX_SIMPLEXMLWRITER_H + +#include "PsArray.h" +#include "SnXmlMemoryPoolStreams.h" +#include "CmPhysXCommon.h" + +namespace physx { namespace Sn { + class XmlWriter + { + public: + + struct STagWatcher + { + typedef XmlWriter TXmlWriterType; + TXmlWriterType& mWriter; + STagWatcher( const STagWatcher& inOther ); + STagWatcher& operator-( const STagWatcher& inOther ); + STagWatcher( TXmlWriterType& inWriter, const char* inTagName ) + : mWriter( inWriter ) + { + mWriter.beginTag( inTagName ); + } + ~STagWatcher() { mWriter.endTag(); } + }; + + virtual ~XmlWriter(){} + virtual void beginTag( const char* inTagname ) = 0; + virtual void endTag() = 0; + virtual void addAttribute( const char* inName, const char* inValue ) = 0; + virtual void writeContentTag( const char* inTag, const char* inContent ) = 0; + virtual void addContent( const char* inContent ) = 0; + virtual PxU32 tabCount() = 0; + }; + + template + class XmlWriterImpl : public XmlWriter + { + PxProfileAllocatorWrapper mWrapper; + TStreamType& mStream; + XmlWriterImpl( const XmlWriterImpl& inOther ); + XmlWriterImpl& operator=( const XmlWriterImpl& inOther ); + PxProfileArray mTags; + bool mTagOpen; + PxU32 mInitialTagDepth; + public: + + XmlWriterImpl( TStreamType& inStream, PxAllocatorCallback& inAllocator, PxU32 inInitialTagDepth = 0 ) + : mWrapper( inAllocator ) + , mStream( inStream ) + , mTags( mWrapper ) + , mTagOpen( false ) + , mInitialTagDepth( inInitialTagDepth ) + { + } + virtual ~XmlWriterImpl() + { + while( mTags.size() ) + endTag(); + } + PxU32 tabCount() { return mTags.size() + mInitialTagDepth; } + + void writeTabs( PxU32 inSize ) + { + inSize += mInitialTagDepth; + for ( PxU32 idx =0; idx < inSize; ++idx ) + mStream << "\t"; + } + void beginTag( const char* inTagname ) + { + closeTag(); + writeTabs(mTags.size()); + mTags.pushBack( inTagname ); + mStream << "<" << inTagname; + mTagOpen = true; + } + void addAttribute( const char* inName, const char* inValue ) + { + PX_ASSERT( mTagOpen ); + mStream << " " << inName << "=" << "\"" << inValue << "\""; + } + void closeTag(bool useNewline = true) + { + if ( mTagOpen ) + { + mStream << " " << ">"; + if (useNewline ) + mStream << "\n"; + } + mTagOpen = false; + } + void doEndOpenTag() + { + mStream << "" << "\n"; + } + void endTag() + { + PX_ASSERT( mTags.size() ); + if ( mTagOpen ) + mStream << " " << "/>" << "\n"; + else + { + writeTabs(mTags.size()-1); + doEndOpenTag(); + } + mTagOpen = false; + mTags.popBack(); + } + void addContent( const char* inContent ) + { + closeTag(false); + mStream << inContent; + } + void writeContentTag( const char* inTag, const char* inContent ) + { + beginTag( inTag ); + addContent( inContent ); + doEndOpenTag(); + mTags.popBack(); + } + void insertXml( const char* inXml ) + { + closeTag(); + mStream << inXml; + } + }; + + struct BeginTag + { + const char* mTagName; + BeginTag( const char* inTagName ) + : mTagName( inTagName ) { } + }; + + struct EndTag + { + EndTag() {} + }; + + struct Att + { + const char* mAttName; + const char* mAttValue; + Att( const char* inAttName, const char* inAttValue ) + : mAttName( inAttName ) + , mAttValue( inAttValue ) { } + }; + + struct Content + { + const char* mContent; + Content( const char* inContent ) + : mContent( inContent ) { } + }; + + + struct ContentTag + { + const char* mTagName; + const char* mContent; + ContentTag( const char* inTagName, const char* inContent ) + : mTagName( inTagName ) + , mContent( inContent ) { } + }; + + inline XmlWriter& operator<<( XmlWriter& inWriter, const BeginTag& inTag ) { inWriter.beginTag( inTag.mTagName ); return inWriter; } + inline XmlWriter& operator<<( XmlWriter& inWriter, const EndTag& inTag ) { inWriter.endTag(); return inWriter; } + inline XmlWriter& operator<<( XmlWriter& inWriter, const Att& inTag ) { inWriter.addAttribute(inTag.mAttName, inTag.mAttValue); return inWriter; } + inline XmlWriter& operator<<( XmlWriter& inWriter, const Content& inTag ) { inWriter.addContent(inTag.mContent); return inWriter; } + inline XmlWriter& operator<<( XmlWriter& inWriter, const ContentTag& inTag ) { inWriter.writeContentTag(inTag.mTagName, inTag.mContent); return inWriter; } + + inline void writeProperty( XmlWriter& inWriter, MemoryBuffer& tempBuffer, const char* inPropName ) + { + PxU8 data = 0; + tempBuffer.write( &data, sizeof(PxU8) ); + inWriter.writeContentTag( inPropName, reinterpret_cast( tempBuffer.mBuffer ) ); + tempBuffer.clear(); + } + + template + inline void writeProperty( XmlWriter& inWriter, MemoryBuffer& tempBuffer, const char* inPropName, TDataType inValue ) + { + tempBuffer << inValue; + writeProperty( inWriter, tempBuffer, inPropName ); + } +} } +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h new file mode 100644 index 000000000..aa594d868 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h @@ -0,0 +1,275 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_STRINGTOTYPE_H +#define PX_XML_STRINGTOTYPE_H + +#include +#include +#include "PsString.h" +#include "PxCoreUtilityTypes.h" +#include "PxFiltering.h" + +//Remapping function name for gcc-based systems. +#ifndef _MSC_VER +#define _strtoui64 strtoull +#endif + + +namespace physx { namespace Sn { + + template + struct StrToImpl + { + bool compile_error; + }; + + template<> struct StrToImpl { + //Id's (void ptrs) are written to file as unsigned + //64 bit integers, so this method gets called more + //often than one might think. + PX_INLINE void strto( PxU64& ioDatatype,const char*& ioData ) + { + ioDatatype = _strtoui64( ioData, const_cast(&ioData), 10 ); + } + }; + + PX_INLINE PxF32 strToFloat(const char *str,const char **nextScan) + { + PxF32 ret; + while ( *str && isspace(static_cast(*str))) str++; // skip leading whitespace + char temp[256] = ""; + char *dest = temp; + char *end = &temp[255]; + const char *begin = str; + while ( *str && !isspace(static_cast(*str)) && dest < end ) // copy the number up to the first whitespace or eos + { + *dest++ = *str++; + } + *dest = 0; + ret = PxF32(strtod(temp,&end)); + if ( nextScan ) + { + *nextScan = begin+(end-temp); + } + return ret; + } + + + template<> struct StrToImpl { + PX_INLINE void strto( PxU32& ioDatatype,const char*& ioData ) + { + ioDatatype = static_cast( strtoul( ioData,const_cast(&ioData), 10 ) ); + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxI32& ioDatatype,const char*& ioData ) + { + ioDatatype = static_cast( strtoul( ioData,const_cast(&ioData), 10 ) ); + } + }; + + + template<> struct StrToImpl { + PX_INLINE void strto( PxU16& ioDatatype,const char*& ioData ) + { + ioDatatype = static_cast( strtoul( ioData,const_cast(&ioData), 10 ) ); + } + }; + + PX_INLINE void eatwhite(const char*& ioData ) + { + if ( ioData ) + { + while( isspace( static_cast(*ioData) ) ) + ++ioData; + } + } + + // copy the source data to the dest buffer until the first whitespace is encountered. + // Do not overflow the buffer based on the bufferLen provided. + // Advance the input 'ioData' pointer so that it sits just at the next whitespace + PX_INLINE void nullTerminateWhite(const char*& ioData,char *buffer,PxU32 bufferLen) + { + if ( ioData ) + { + char *eof = buffer+(bufferLen-1); + char *dest = buffer; + while( *ioData && !isspace(static_cast(*ioData)) && dest < eof ) + { + *dest++ = *ioData++; + } + *dest = 0; + } + } + + inline void nonNullTerminateWhite(const char*& ioData ) + { + if ( ioData ) + { + while( *ioData && !isspace( static_cast(*ioData) ) ) + ++ioData; + } + } + + template<> struct StrToImpl { + inline void strto( PxF32& ioDatatype,const char*& ioData ) + { + ioDatatype = strToFloat(ioData,&ioData); + } + + }; + + + template<> struct StrToImpl { + inline void strto( void*& ioDatatype,const char*& ioData ) + { + PxU64 theData; + StrToImpl().strto( theData, ioData ); + ioDatatype = reinterpret_cast( static_cast( theData ) ); + } + }; + + + template<> struct StrToImpl { + inline void strto( physx::PxVec3& ioDatatype,const char*& ioData ) + { + StrToImpl().strto( ioDatatype[0], ioData ); + StrToImpl().strto( ioDatatype[1], ioData ); + StrToImpl().strto( ioDatatype[2], ioData ); + } + }; + + template<> struct StrToImpl { + inline void strto( PxU8*& /*ioDatatype*/,const char*& /*ioData*/) + { + } + }; + + template<> struct StrToImpl { + inline void strto( bool& ioType,const char*& inValue ) + { + ioType = physx::shdfnd::stricmp( inValue, "true" ) == 0 ? true : false; + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxU8& ioType,const char* & inValue) + { + ioType = static_cast( strtoul( inValue,const_cast(&inValue), 10 ) ); + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxFilterData& ioType,const char*& inValue) + { + ioType.word0 = static_cast( strtoul( inValue,const_cast(&inValue), 10 ) ); + ioType.word1 = static_cast( strtoul( inValue,const_cast(&inValue), 10 ) ); + ioType.word2 = static_cast( strtoul( inValue,const_cast(&inValue), 10 ) ); + ioType.word3 = static_cast( strtoul( inValue, NULL, 10 ) ); + } + }; + + + template<> struct StrToImpl { + PX_INLINE void strto( PxQuat& ioType,const char*& inValue ) + { + ioType.x = static_cast( strToFloat( inValue, &inValue ) ); + ioType.y = static_cast( strToFloat( inValue, &inValue ) ); + ioType.z = static_cast( strToFloat( inValue, &inValue ) ); + ioType.w = static_cast( strToFloat( inValue, &inValue ) ); + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxTransform& ioType,const char*& inValue) + { + ioType.q.x = static_cast( strToFloat( inValue, &inValue ) ); + ioType.q.y = static_cast( strToFloat( inValue, &inValue ) ); + ioType.q.z = static_cast( strToFloat( inValue, &inValue ) ); + ioType.q.w = static_cast( strToFloat( inValue, &inValue ) ); + + ioType.p[0] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.p[1] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.p[2] = static_cast( strToFloat( inValue, &inValue ) ); + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxBounds3& ioType,const char*& inValue) + { + ioType.minimum[0] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.minimum[1] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.minimum[2] = static_cast( strToFloat( inValue, &inValue ) ); + + ioType.maximum[0] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.maximum[1] = static_cast( strToFloat( inValue, &inValue ) ); + ioType.maximum[2] = static_cast( strToFloat( inValue, &inValue ) ); + } + }; + + template<> struct StrToImpl { + PX_INLINE void strto( PxMetaDataPlane& ioType,const char*& inValue) + { + ioType.normal.x = static_cast( strToFloat( inValue, &inValue ) ); + ioType.normal.y = static_cast( strToFloat( inValue, &inValue ) ); + ioType.normal.z = static_cast( strToFloat( inValue, &inValue ) ); + ioType.distance = static_cast( strToFloat( inValue, &inValue ) ); + } + }; + + + template<> struct StrToImpl { + PX_INLINE void strto( PxRigidDynamic*& /*ioDatatype*/,const char*& /*ioData*/) + { + } + }; + + template + inline void strto( TDataType& ioType,const char*& ioData ) + { + if ( ioData && *ioData ) StrToImpl().strto( ioType, ioData ); + } + + template + inline void strtoLong( TDataType& ioType,const char*& ioData ) + { + if ( ioData && *ioData ) StrToImpl().strto( ioType, ioData ); + } + + template + inline void stringToType( const char* inValue, TDataType& ioType ) + { + const char* theValue( inValue ); + return strto( ioType, theValue ); + } + +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h new file mode 100644 index 000000000..461be83b3 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h @@ -0,0 +1,907 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_VISITOR_READER_H +#define PX_XML_VISITOR_READER_H + +#include "PsArray.h" +#include "PsUtilities.h" +#include "RepXMetaDataPropertyVisitor.h" +#include "SnPxStreamOperators.h" +#include "SnXmlMemoryPoolStreams.h" +#include "SnXmlReader.h" +#include "SnXmlImpl.h" +#include "SnXmlMemoryAllocator.h" +#include "SnXmlStringToType.h" + +namespace physx { namespace Sn { + + + inline PxU32 findEnumByName( const char* inName, const PxU32ToName* inTable ) + { + for ( PxU32 idx = 0; inTable[idx].mName != NULL; ++idx ) + { + if ( physx::shdfnd::stricmp( inTable[idx].mName, inName ) == 0 ) + return inTable[idx].mValue; + } + return 0; + } + + PX_INLINE void stringToFlagsType( const char* strData, XmlMemoryAllocator& alloc, PxU32& ioType, const PxU32ToName* inTable ) + { + if ( inTable == NULL ) + return; + ioType = 0; + if ( strData && *strData) + { + //Destructively parse the string to get out the different flags. + char* theValue = const_cast( copyStr( &alloc, strData ) ); + char* theMarker = theValue; + char* theNext = theValue; + while( theNext && *theNext ) + { + ++theNext; + if( *theNext == '|' ) + { + *theNext = 0; + ++theNext; + ioType |= static_cast< PxU32 > ( findEnumByName( theMarker, inTable ) ); + theMarker = theNext; + } + } + if ( theMarker && *theMarker ) + ioType |= static_cast< PxU32 > ( findEnumByName( theMarker, inTable ) ); + alloc.deallocate( reinterpret_cast( theValue ) ); + } + } + + template + PX_INLINE void stringToEnumType( const char* strData, TDataType& ioType, const PxU32ToName* inTable ) + { + ioType = static_cast( findEnumByName( strData, inTable ) ); + } + + template + PX_INLINE bool readProperty( XmlReader& inReader, const char* pname, TDataType& ioType ) + { + const char* value; + if ( inReader.read( pname, value ) ) + { + stringToType( value, ioType ); + return true; + } + return false; + } + + template + inline TObjType* findReferencedObject( PxCollection& collection, PxSerialObjectId id) + { + PX_ASSERT(id > 0); + TObjType* outObject = static_cast(const_cast(collection.find(id))); + if (outObject == NULL) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::createCollectionFromXml: " + "Reference to ID %d cannot be resolved. Make sure externalRefs collection is specified if required and " + "check Xml file for completeness.", + id); + } + return outObject; + } + + template + inline bool readReference( XmlReader& inReader, PxCollection& collection, TObjType*& outObject ) + { + PxSerialObjectId theId; + const char* theValue = inReader.getCurrentItemValue(); + strto( theId, theValue ); + if( theId == 0) + { + // the NULL pointer is a valid pointer if the input id is 0 + outObject = NULL; + return true; + } + else + { + outObject = findReferencedObject(collection, theId); + return outObject != NULL; + } + } + + template + inline bool readReference( XmlReader& inReader, PxCollection& inCollection, const char* pname, TObjType*& outObject ) + { + outObject = NULL; + PxSerialObjectId theId = 0; + if (readProperty ( inReader, pname, theId ) && theId ) + { + outObject = findReferencedObject(inCollection, theId); + } + // the NULL pointer is a valid pointer if the input id is 0 + return (outObject != NULL) || 0 == theId; + } + + template + inline bool readFlagsProperty( XmlReader& reader, XmlMemoryAllocator& allocator, const char* pname, const PxU32ToName* inConversions, PxFlags& outFlags ) + { + const char* value; + if ( reader.read( pname, value ) ) + { + PxU32 tempValue = 0; + stringToFlagsType( value, allocator, tempValue, inConversions ); + outFlags = PxFlags(Ps::to16(tempValue) ); + return true; + } + return false; + } + + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj, TInfoType& info); + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj); + + template + inline PxGeometry* parseGeometry( TReaderType& reader, TGeomType& /*inGeom*/) + { + PxAllocatorCallback& inAllocator = reader.mAllocator.getAllocator(); + + TGeomType* shape = PX_PLACEMENT_NEW((inAllocator.allocate(sizeof(TGeomType), "parseGeometry", __FILE__, __LINE__ )), TGeomType); + PxClassInfoTraits info; + readComplexObj( reader, shape); + return shape; + } + + + template + inline void parseShape( TReaderType& visitor, PxGeometry*& outResult, Ps::Array& outMaterials) + { + XmlReader& theReader( visitor.mReader ); + PxCollection& collection = visitor.mCollection; + visitor.pushCurrentContext(); + if ( visitor.gotoTopName() ) + { + visitor.pushCurrentContext(); + if ( visitor.gotoChild( "Materials" ) ) + { + for( bool matSuccess = visitor.gotoFirstChild(); matSuccess; + matSuccess = visitor.gotoNextSibling() ) + { + PxMaterial* material = NULL; + if(!readReference( theReader, collection, material )) + visitor.mHadError = true; + if ( material ) + outMaterials.pushBack( material ); + } + } + visitor.popCurrentContext(); + visitor.pushCurrentContext(); + + PxPlaneGeometry plane; + PxHeightFieldGeometry heightField; + PxSphereGeometry sphere; + PxTriangleMeshGeometry mesh; + PxConvexMeshGeometry convex; + PxBoxGeometry box; + PxCapsuleGeometry capsule; + if ( visitor.gotoChild( "Geometry" ) ) + { + if ( visitor.gotoFirstChild() ) + { + const char* geomTypeName = visitor.getCurrentItemName(); + + if ( physx::shdfnd::stricmp( geomTypeName, "PxSphereGeometry" ) == 0 ) outResult = parseGeometry(visitor, sphere); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxPlaneGeometry" ) == 0 ) outResult = parseGeometry(visitor, plane); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxCapsuleGeometry" ) == 0 ) outResult = parseGeometry(visitor, capsule); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxBoxGeometry" ) == 0 ) outResult = parseGeometry(visitor, box); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxConvexMeshGeometry" ) == 0 ) outResult = parseGeometry(visitor, convex); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxTriangleMeshGeometry" ) == 0 ) outResult = parseGeometry(visitor, mesh); + else if ( physx::shdfnd::stricmp( geomTypeName, "PxHeightFieldGeometry" ) == 0 ) outResult = parseGeometry(visitor, heightField); + else + PX_ASSERT( false ); + } + } + visitor.popCurrentContext(); + } + visitor.popCurrentContext(); + + return; + } + + template + inline void readShapesProperty( TReaderType& visitor, TObjType* inObj, const PxRigidActorShapeCollection* inProp = NULL, bool isSharedShape = false ) + { + PX_UNUSED(isSharedShape); + PX_UNUSED(inProp); + + XmlReader& theReader( visitor.mReader ); + PxCollection& collection( visitor.mCollection ); + + visitor.pushCurrentContext(); + if ( visitor.gotoTopName() ) + { + //uggh working around the shape collection api. + //read out materials and geometry + for ( bool success = visitor.gotoFirstChild(); success; + success = visitor.gotoNextSibling() ) + { + if( 0 == physx::shdfnd::stricmp( visitor.getCurrentItemName(), "PxShapeRef" ) ) + { + PxShape* shape = NULL; + if(!readReference( theReader, collection, shape )) + visitor.mHadError = true; + if(shape) + inObj->attachShape( *shape ); + } + else + { + Ps::Array materials; + PxGeometry* geometry = NULL; + parseShape( visitor, geometry, materials); + PxShape* theShape = NULL; + if ( materials.size() ) + { + theShape = visitor.mArgs.physics.createShape( *geometry, materials.begin(), Ps::to16(materials.size()), true ); + if ( theShape ) + { + readComplexObj( visitor, theShape ); + + if(theShape) + { + inObj->attachShape(*theShape); + collection.add( *theShape ); + } + } + } + + switch(geometry->getType()) + { + case PxGeometryType::eSPHERE : + static_cast(geometry)->~PxSphereGeometry(); + break; + case PxGeometryType::ePLANE : + static_cast(geometry)->~PxPlaneGeometry(); + break; + case PxGeometryType::eCAPSULE : + static_cast(geometry)->~PxCapsuleGeometry(); + break; + case PxGeometryType::eBOX : + static_cast(geometry)->~PxBoxGeometry(); + break; + case PxGeometryType::eCONVEXMESH : + static_cast(geometry)->~PxConvexMeshGeometry(); + break; + case PxGeometryType::eTRIANGLEMESH : + static_cast(geometry)->~PxTriangleMeshGeometry(); + break; + case PxGeometryType::eHEIGHTFIELD : + static_cast(geometry)->~PxHeightFieldGeometry(); + break; + + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ASSERT(0); + } + visitor.mAllocator.getAllocator().deallocate(geometry); + } + } + } + visitor.popCurrentContext(); + } + + struct ReaderNameStackEntry : NameStackEntry + { + bool mValid; + ReaderNameStackEntry( const char* nm, bool valid ) : NameStackEntry(nm), mValid(valid) {} + }; + + typedef PxProfileArray TReaderNameStack; + + template + struct RepXVisitorReaderBase + { + + protected: + RepXVisitorReaderBase& operator=(const RepXVisitorReaderBase&); + public: + TReaderNameStack& mNames; + PxProfileArray& mContexts; + PxRepXInstantiationArgs mArgs; + XmlReader& mReader; + TObjType* mObj; + XmlMemoryAllocator& mAllocator; + PxCollection& mCollection; + bool mValid; + bool& mHadError; + + RepXVisitorReaderBase( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, TObjType* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& hadError ) + : mNames( names ) + , mContexts( contexts ) + , mArgs( args ) + , mReader( reader ) + , mObj( obj ) + , mAllocator( alloc ) + , mCollection( collection ) + , mValid( true ) + , mHadError(hadError) + { + } + RepXVisitorReaderBase( const RepXVisitorReaderBase& other ) + : mNames( other.mNames ) + , mContexts( other.mContexts ) + , mArgs( other.mArgs ) + , mReader( other.mReader ) + , mObj( other.mObj ) + , mAllocator( other.mAllocator ) + , mCollection( other.mCollection ) + , mValid( other.mValid ) + , mHadError( other.mHadError ) + { + } + + + void pushName( const char* name ) + { + gotoTopName(); + mNames.pushBack( ReaderNameStackEntry( name, mValid ) ); + } + void pushBracketedName( const char* name ) { pushName( name ); } + void popName() + { + if ( mNames.size() ) + { + if ( mNames.back().mOpen && mNames.back().mValid ) + mReader.leaveChild(); + mNames.popBack(); + } + mValid =true; + if ( mNames.size() && mNames.back().mValid == false ) + mValid = false; + } + + void pushCurrentContext() + { + mContexts.pushBack( static_cast( mNames.size() ) ); + } + void popCurrentContext() + { + if ( mContexts.size() ) + { + PxU32 depth = mContexts.back(); + PX_ASSERT( mNames.size() >= depth ); + while( mNames.size() > depth ) + popName(); + mContexts.popBack(); + } + } + + bool updateLastEntryAfterOpen() + { + mNames.back().mValid = mValid; + mNames.back().mOpen = mValid; + return mValid; + } + + bool gotoTopName() + { + if ( mNames.size() && mNames.back().mOpen == false ) + { + if ( mValid ) + mValid = mReader.gotoChild( mNames.back().mName ); + updateLastEntryAfterOpen(); + } + return mValid; + } + + bool isValid() const { return mValid; } + + bool gotoChild( const char* name ) + { + pushName( name ); + return gotoTopName(); + } + + bool gotoFirstChild() + { + pushName( "__child" ); + if ( mValid ) mValid = mReader.gotoFirstChild(); + return updateLastEntryAfterOpen(); + } + + bool gotoNextSibling() + { + bool retval = mValid; + if ( mValid ) retval = mReader.gotoNextSibling(); + return retval; + } + + const char* getCurrentItemName() { if (mValid ) return mReader.getCurrentItemName(); return ""; } + + const char* topName() const + { + if ( mNames.size() ) return mNames.back().mName; + PX_ASSERT( false ); + return "bad__repx__name"; + } + + const char* getCurrentValue() + { + const char* value = NULL; + if ( isValid() && mReader.read( topName(), value ) ) + return value; + return NULL; + } + + template + bool readProperty(TDataType& outType) + { + const char* value = getCurrentValue(); + if ( value && *value ) + { + stringToType( value, outType ); + return true; + } + return false; + } + + template + bool readExtendedIndexProperty(TDataType& outType) + { + const char* value = mReader.getCurrentItemValue(); + if ( value && *value ) + { + stringToType( value, outType ); + return true; + } + return false; + } + + + template + bool readReference(TRefType*& outRef) + { + return physx::Sn::readReference( mReader, mCollection, topName(), outRef ); + } + + inline bool readProperty(const char*& outProp ) + { + outProp = ""; + const char* value = getCurrentValue(); + if ( value && *value && mArgs.stringTable ) + { + outProp = mArgs.stringTable->allocateStr( value ); + return true; + } + return false; + } + + inline bool readProperty(PxConvexMesh*& outProp ) + { + return readReference( outProp ); + } + + inline bool readProperty(PxTriangleMesh*& outProp ) + { + return readReference( outProp ); + } + inline bool readProperty(PxBVH33TriangleMesh*& outProp ) + { + return readReference( outProp ); + } + inline bool readProperty(PxBVH34TriangleMesh*& outProp ) + { + return readReference( outProp ); + } + + inline bool readProperty(PxHeightField*& outProp ) + { + return readReference( outProp ); + } + + inline bool readProperty( PxRigidActor *& outProp ) + { + return readReference( outProp ); + } + + template + void simpleProperty( PxU32 /*key*/, TAccessorType& inProp ) + { + typedef typename TAccessorType::prop_type TPropertyType; + TPropertyType value; + if ( readProperty( value ) ) + inProp.set( mObj, value ); + } + + template + void enumProperty( PxU32 /*key*/, TAccessorType& inProp, const PxU32ToName* inConversions ) + { + typedef typename TAccessorType::prop_type TPropertyType; + const char* strVal = getCurrentValue(); + if ( strVal && *strVal ) + { + TPropertyType pval; + stringToEnumType( strVal, pval, inConversions ); + inProp.set( mObj, pval ); + } + } + + template + void flagsProperty( PxU32 /*key*/, const TAccessorType& inProp, const PxU32ToName* inConversions ) + { + typedef typename TAccessorType::prop_type TPropertyType; + typedef typename TPropertyType::InternalType TInternalType; + + const char* strVal = getCurrentValue(); + if ( strVal && *strVal ) + { + PxU32 tempValue = 0; + stringToFlagsType( strVal, mAllocator, tempValue, inConversions ); + inProp.set( mObj, TPropertyType(TInternalType( tempValue ))); + } + } + + template + void complexProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + if ( gotoTopName() ) + { + TPropertyType propVal = inProp.get( mObj ); + readComplexObj( *this, &propVal, inInfo ); + inProp.set( mObj, propVal ); + } + } + + template + void bufferCollectionProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + Ps::InlineArray theData; + + this->pushCurrentContext(); + if ( this->gotoTopName() ) + { + for ( bool success = this->gotoFirstChild(); success; + success = this->gotoNextSibling() ) + { + TPropertyType propVal; + readComplexObj( *this, &propVal, inInfo ); + theData.pushBack(propVal); + } + } + this->popCurrentContext(); + + inProp.set( mObj, theData.begin(), theData.size() ); + + } + + template + void extendedIndexedProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + + this->pushCurrentContext(); + if ( this->gotoTopName() ) + { + PxU32 index = 0; + for ( bool success = this->gotoFirstChild(); success; + success = this->gotoNextSibling() ) + { + TPropertyType propVal; + readComplexObj( *this, &propVal, inInfo ); + inProp.set(mObj, index, propVal); + ++index; + } + } + this->popCurrentContext(); + } + + template + void PxFixedSizeLookupTableProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + const_cast(inProp).clear( mObj ); + + this->pushCurrentContext(); + if ( this->gotoTopName() ) + { + for ( bool success = this->gotoFirstChild(); success; + success = this->gotoNextSibling() ) + { + TPropertyType propXVal; + readComplexObj( *this, &propXVal, inInfo ); + + if(this->gotoNextSibling()) + { + TPropertyType propYVal; + readComplexObj( *this, &propYVal, inInfo ); + const_cast(inProp).addPair(mObj, propXVal, propYVal); + } + } + } + this->popCurrentContext(); + } + + void handleShapes( const PxRigidActorShapeCollection& inProp ) + { + physx::Sn::readShapesProperty( *this, mObj, &inProp ); + } + }; + + template + struct RepXVisitorReader : public RepXVisitorReaderBase + { + RepXVisitorReader( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, TObjType* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& ret) + : RepXVisitorReaderBase( names, contexts, args, reader, obj, alloc, collection, ret) + { + } + RepXVisitorReader( const RepXVisitorReader& other ) + : RepXVisitorReaderBase( other ) + { + } + }; + + // Specialized template to load dynamic rigid, to determine the kinematic state first + template<> + struct RepXVisitorReader : public RepXVisitorReaderBase + { + RepXVisitorReader( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, PxRigidDynamic* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& ret) + : RepXVisitorReaderBase( names, contexts, args, reader, obj, alloc, collection, ret) + { + } + RepXVisitorReader( const RepXVisitorReader& other ) + : RepXVisitorReaderBase( other ) + { + } + + void handleShapes( const PxRigidActorShapeCollection& inProp ) + { + // Need to read the parental actor to check if actor is kinematic + // in that case we need to apply the kinematic flag before a shape is set + XmlReaderWriter* parentReader = static_cast(mReader.getParentReader()); + if(mObj) + { + const char* value; + if (parentReader->read( "RigidBodyFlags", value )) + { + if(strstr(value, "eKINEMATIC")) + { + mObj->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true); + } + } + } + physx::Sn::readShapesProperty( *this, mObj, &inProp ); + parentReader->release(); + } + + + template + void simpleProperty( PxU32 /*key*/, TAccessorType& inProp ) + { + typedef typename TAccessorType::prop_type TPropertyType; + TPropertyType value; + if (readProperty(value)) + { + // If the rigid body is kinematic, we cannot set the LinearVelocity or AngularVelocity + const bool kinematic = (mObj->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC); + if(kinematic && (inProp.mProperty.mKey == PxPropertyInfoName::PxRigidBody_LinearVelocity || inProp.mProperty.mKey == PxPropertyInfoName::PxRigidBody_AngularVelocity)) + return; + + inProp.set(mObj, value ); + } + } + private: + RepXVisitorReader& operator=(const RepXVisitorReader&); + }; + + template<> + struct RepXVisitorReader : public RepXVisitorReaderBase + { + RepXVisitorReader( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, PxShape* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& ret ) + : RepXVisitorReaderBase( names, contexts, args, reader, obj, alloc, collection, ret ) + { + } + RepXVisitorReader( const RepXVisitorReader& other ) + : RepXVisitorReaderBase( other ) + { + } + void handleShapeMaterials( const PxShapeMaterialsProperty& ) //these were handled during construction. + { + } + void handleGeometryProperty( const PxShapeGeometryProperty& ) + { + } + private: + RepXVisitorReader& operator=(const RepXVisitorReader&); + }; + + template<> + struct RepXVisitorReader : public RepXVisitorReaderBase + { + RepXVisitorReader( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, PxArticulationLink* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& ret ) + : RepXVisitorReaderBase( names, contexts, args, reader, obj, alloc, collection, ret ) + { + } + RepXVisitorReader( const RepXVisitorReader& other ) + : RepXVisitorReaderBase( other ) + { + } + void handleIncomingJoint( const TIncomingJointPropType& prop ) + { + pushName( "Joint" ); + if ( gotoTopName() ) + { + PxArticulationBase::Enum type = mObj->getArticulation().getType(); + if (type == PxArticulationBase::eMaximumCoordinate) + { + PxArticulationJoint* theJoint = static_cast((prop.get(mObj))); + + readComplexObj(*this, theJoint); + + //Add joint to PxCollection, since PxArticulation requires PxArticulationLink and joint. + mCollection.add(*theJoint); + + } + else + { + PxArticulationJointReducedCoordinate* theJoint = static_cast((prop.get(mObj))); + + readComplexObj(*this, theJoint); + + //Add joint to PxCollection, since PxArticulation requires PxArticulationLink and joint. + mCollection.add(*theJoint); + } + + + + } + popName(); + } + private: + RepXVisitorReader& operator=(const RepXVisitorReader&); + }; + + inline void readProperty( RepXVisitorReaderBase& inSerializer, PxArticulation* inObj, const PxArticulationLinkCollectionProp& /*inProp*/) + { + PxProfileAllocatorWrapper theWrapper( inSerializer.mAllocator.getAllocator() ); + PxCollection& collection( inSerializer.mCollection ); + + TArticulationLinkLinkMap linkRemapMap( theWrapper ); + inSerializer.pushCurrentContext(); + if( inSerializer.gotoTopName() ) + { + for ( bool links = inSerializer.gotoFirstChild(); + links != false; + links = inSerializer.gotoNextSibling() ) + { + //Need enough information to create the link... + PxSerialObjectId theParentPtr = 0; + const PxArticulationLink* theParentLink = NULL; + if ( inSerializer.mReader.read( "Parent", theParentPtr ) ) + { + const TArticulationLinkLinkMap::Entry* theRemappedParent( linkRemapMap.find( theParentPtr ) ); + //If we have a valid at write time, we had better have a valid parent at read time. + PX_ASSERT( theRemappedParent ); + theParentLink = theRemappedParent->second; + } + PxArticulationLink* newLink = inObj->createLink( const_cast( theParentLink ), PxTransform(PxIdentity) ); + PxSerialObjectId theIdPtr = 0; + inSerializer.mReader.read( "Id", theIdPtr ); + + linkRemapMap.insert( theIdPtr, newLink ); + readComplexObj( inSerializer, newLink ); + + //Add link to PxCollection, since PxArticulation requires PxArticulationLink and joint. + collection.add( *newLink, theIdPtr ); + } + } + inSerializer.popCurrentContext(); + } + + template<> + struct RepXVisitorReader : public RepXVisitorReaderBase + { + RepXVisitorReader( TReaderNameStack& names, PxProfileArray& contexts, const PxRepXInstantiationArgs& args, XmlReader& reader, PxArticulation* obj + , XmlMemoryAllocator& alloc, PxCollection& collection, bool& ret) + : RepXVisitorReaderBase( names, contexts, args, reader, obj, alloc, collection, ret) + { + } + RepXVisitorReader( const RepXVisitorReader& other ) + : RepXVisitorReaderBase( other ) + { + } + + void handleArticulationLinks( const PxArticulationLinkCollectionProp& inProp ) + { + physx::Sn::readProperty( *this, mObj, inProp ); + } + }; + + template + inline bool readAllProperties( PxRepXInstantiationArgs args, TReaderNameStack& names, PxProfileArray& contexts, XmlReader& reader, TObjType* obj, XmlMemoryAllocator& alloc, PxCollection& collection, TInfoType& info ) + { + bool hadError = false; + RepXVisitorReader theReader( names, contexts, args, reader, obj, alloc, collection, hadError); + RepXPropertyFilter > theOp( theReader ); + info.visitBaseProperties( theOp ); + info.visitInstanceProperties( theOp ); + return !hadError; + } + + + template + inline bool readAllProperties( PxRepXInstantiationArgs args, XmlReader& reader, TObjType* obj, XmlMemoryAllocator& alloc, PxCollection& collection ) + { + PxProfileAllocatorWrapper wrapper( alloc.getAllocator() ); + TReaderNameStack names( wrapper ); + PxProfileArray contexts( wrapper ); + PxClassInfoTraits info; + return readAllProperties( args, names, contexts, reader, obj, alloc, collection, info.Info ); + } + + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj, TInfoType& info) + { + if(!readAllProperties( oldVisitor.mArgs, oldVisitor.mNames, oldVisitor.mContexts, oldVisitor.mReader, inObj, oldVisitor.mAllocator, oldVisitor.mCollection, info )) + oldVisitor.mHadError = true; + } + + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj, const TInfoType& info) + { + if(!readAllProperties( oldVisitor.mArgs, oldVisitor.mNames, oldVisitor.mContexts, oldVisitor.mReader, inObj, oldVisitor.mAllocator, oldVisitor.mCollection, info )) + oldVisitor.mHadError = true; + } + + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj, const PxUnknownClassInfo& /*info*/) + { + const char* value = oldVisitor.mReader.getCurrentItemValue(); + if ( value && *value ) + { + stringToType( value, *inObj ); + return; + } + oldVisitor.mHadError = true; + } + + template + inline void readComplexObj( TReaderType& oldVisitor, TObjType* inObj) + { + PxClassInfoTraits info; + if(!readAllProperties( oldVisitor.mArgs, oldVisitor.mNames, oldVisitor.mContexts, oldVisitor.mReader, inObj, oldVisitor.mAllocator, oldVisitor.mCollection, info.Info )) + oldVisitor.mHadError = true; + } + +} } + +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h new file mode 100644 index 000000000..5caacaf2d --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h @@ -0,0 +1,801 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_VISITOR_WRITER_H +#define PX_XML_VISITOR_WRITER_H + +#include "PsInlineArray.h" +#include "CmPhysXCommon.h" +#include "RepXMetaDataPropertyVisitor.h" +#include "SnPxStreamOperators.h" +#include "SnXmlMemoryPoolStreams.h" +#include "SnXmlWriter.h" +#include "SnXmlImpl.h" +#include "PsFoundation.h" +#include "foundation/PxStrideIterator.h" + +namespace physx { namespace Sn { + + template + inline void writeReference( XmlWriter& writer, PxCollection& inCollection, const char* inPropName, const TDataType* inDatatype ) + { + const PxBase* s = static_cast( inDatatype ) ; + if( inDatatype && !inCollection.contains( *const_cast(s) )) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, + "PxSerialization::serializeCollectionToXml: Reference \"%s\" could not be resolved.", inPropName); + } + + PxSerialObjectId theId = 0; + if( s ) + { + theId = inCollection.getId( *s ); + if( theId == 0 ) + theId = static_cast(reinterpret_cast(inDatatype)); + } + + writer.write( inPropName, PxCreateRepXObject( inDatatype, theId ) ); + } + + inline void writeProperty( XmlWriter& inWriter, MemoryBuffer& inBuffer, const char* inProp ) + { + PxU8 data = 0; + inBuffer.write( &data, sizeof(PxU8) ); + inWriter.write( inProp, reinterpret_cast( inBuffer.mBuffer ) ); + inBuffer.clear(); + } + + template + inline void writeProperty( XmlWriter& inWriter, PxCollection&, MemoryBuffer& inBuffer, const char* inPropName, TDataType inValue ) + { + inBuffer << inValue; + writeProperty( inWriter, inBuffer, inPropName ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxConvexMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxConvexMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxTriangleMesh* inDatatype ) + { + if (inDatatype->getConcreteType() == PxConcreteType::eTRIANGLE_MESH_BVH33) + { + const PxBVH33TriangleMesh* dataType = inDatatype->is(); + writeReference(writer, inCollection, inPropName, dataType); + } + else if (inDatatype->getConcreteType() == PxConcreteType::eTRIANGLE_MESH_BVH34) + { + const PxBVH34TriangleMesh* dataType = inDatatype->is(); + writeReference(writer, inCollection, inPropName, dataType); + } + else + { + PX_ASSERT(0); + } + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxTriangleMesh* inDatatype ) + { + if (inDatatype->getConcreteType() == PxConcreteType::eTRIANGLE_MESH_BVH33) + { + PxBVH33TriangleMesh* dataType = inDatatype->is(); + writeReference(writer, inCollection, inPropName, dataType); + } + else if (inDatatype->getConcreteType() == PxConcreteType::eTRIANGLE_MESH_BVH34) + { + PxBVH34TriangleMesh* dataType = inDatatype->is(); + writeReference(writer, inCollection, inPropName, dataType); + } + else + { + PX_ASSERT(0); + } + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxBVH33TriangleMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxBVH33TriangleMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxBVH34TriangleMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxBVH34TriangleMesh* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxHeightField* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxHeightField* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, const PxRigidActor* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxArticulation* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeProperty( XmlWriter& writer, PxCollection& inCollection, MemoryBuffer& /*inBuffer*/, const char* inPropName, PxRigidActor* inDatatype ) + { + writeReference( writer, inCollection, inPropName, inDatatype ); + } + + inline void writeFlagsProperty( XmlWriter& inWriter, MemoryBuffer& tempBuf, const char* inPropName, PxU32 inFlags, const PxU32ToName* inTable ) + { + if ( inTable ) + { + PxU32 flagValue( inFlags ); + if ( flagValue ) + { + for ( PxU32 idx =0; inTable[idx].mName != NULL; ++idx ) + { + if ( (inTable[idx].mValue & flagValue) == inTable[idx].mValue ) + { + if ( tempBuf.mWriteOffset != 0 ) + tempBuf << "|"; + tempBuf << inTable[idx].mName; + } + } + writeProperty( inWriter, tempBuf, inPropName ); + } + } + } + + inline void writeFlagsBuffer( MemoryBuffer& tempBuf, PxU32 flagValue, const PxU32ToName* inTable ) + { + PX_ASSERT(inTable); + bool added = false; + + if ( flagValue ) + { + for ( PxU32 item =0; inTable[item].mName != NULL; ++item ) + { + if ( (inTable[item].mValue & flagValue) != 0 ) + { + if ( added ) + tempBuf << "|"; + tempBuf << inTable[item].mName; + added = true; + } + } + } + } + + inline void writePxVec3( PxOutputStream& inStream, const PxVec3& inVec ) { inStream << inVec; } + + + template + inline const TDataType& PtrAccess( const TDataType* inPtr, PxU32 inIndex ) + { + return inPtr[inIndex]; + } + + template + inline void BasicDatatypeWrite( PxOutputStream& inStream, const TDataType& item ) { inStream << item; } + + template + inline void writeBuffer( XmlWriter& inWriter, MemoryBuffer& inTempBuffer + , PxU32 inObjPerLine, const TObjType* inObjType, TAccessOperator inAccessOperator + , PxU32 inBufSize, const char* inPropName, TWriteOperator inOperator ) + { + if ( inBufSize && inObjType ) + { + for ( PxU32 idx = 0; idx < inBufSize; ++idx ) + { + if ( idx && ( idx % inObjPerLine == 0 ) ) + inTempBuffer << "\n\t\t\t"; + else + inTempBuffer << " "; + inOperator( inTempBuffer, inAccessOperator( inObjType, idx ) ); + } + writeProperty( inWriter, inTempBuffer, inPropName ); + } + } + + template + inline void writeStrideBuffer( XmlWriter& inWriter, MemoryBuffer& inTempBuffer + , PxU32 inObjPerLine, PxStrideIterator& inData, TAccessOperator inAccessOperator + , PxU32 inBufSize, const char* inPropName, PxU32 /*inStride*/, TWriteOperator inOperator ) + { +#if PX_SWITCH + const auto *dat = &inData[0]; + if (inBufSize && dat != NULL) +#else + if ( inBufSize && &inData[0]) +#endif + { + for ( PxU32 idx = 0; idx < inBufSize; ++idx ) + { + if ( idx && ( idx % inObjPerLine == 0 ) ) + inTempBuffer << "\n\t\t\t"; + else + inTempBuffer << " "; + + inOperator( inTempBuffer, inAccessOperator( &inData[idx], 0 ) ); + } + writeProperty( inWriter, inTempBuffer, inPropName ); + } + } + + + template + inline void writeStrideFlags( XmlWriter& inWriter, MemoryBuffer& inTempBuffer + , PxU32 inObjPerLine, PxStrideIterator& inData, TAccessOperator /*inAccessOperator*/ + , PxU32 inBufSize, const char* inPropName, const PxU32ToName* inTable) + { +#if PX_SWITCH + const auto *dat = &inData[0]; + if (inBufSize && dat != NULL) +#else + if ( inBufSize && &inData[0]) +#endif + { + for ( PxU32 idx = 0; idx < inBufSize; ++idx ) + { + writeFlagsBuffer(inTempBuffer, inData[idx], inTable); + + if ( idx && ( idx % inObjPerLine == 0 ) ) + inTempBuffer << "\n\t\t\t"; + else + inTempBuffer << " "; + } + writeProperty( inWriter, inTempBuffer, inPropName ); + } + } + + template + inline void writeBuffer( XmlWriter& inWriter, MemoryBuffer& inTempBuffer + , PxU32 inObjPerLine, const TDataType* inBuffer + , PxU32 inBufSize, const char* inPropName, TWriteOperator inOperator ) + { + writeBuffer( inWriter, inTempBuffer, inObjPerLine, inBuffer, PtrAccess, inBufSize, inPropName, inOperator ); + } + + template + inline void writeEnumProperty( XmlWriter& inWriter, const char* inPropName, TEnumType inEnumValue, const PxU32ToName* inConversions ) + { + PxU32 theValue = static_cast( inEnumValue ); + for ( const PxU32ToName* conv = inConversions; conv->mName != NULL; ++conv ) + if ( conv->mValue == theValue ) inWriter.write( inPropName, conv->mName ); + } + + + + template + inline void handleComplexObj( TWriterType& oldVisitor, const TObjType* inObj, TInfoType& info); + + template + void handleComplexCollection( TVisitor& visitor, const TPropType& inProp, const char* childName, TInfoType& inInfo ) + { + PxU32 count( inProp.size( visitor.mObj ) ); + if ( count ) + { + Ps::InlineArray theData; + theData.resize( count ); + inProp.get( visitor.mObj, theData.begin(), count ); + for( PxU32 idx =0; idx < count; ++idx ) + { + visitor.pushName( childName ); + handleComplexObj( visitor, theData[idx], inInfo ); + visitor.popName(); + } + } + } + + template + void handleBufferCollection( TVisitor& visitor, const TPropType& inProp, const char* childName, TInfoType& inInfo ) + { + PxU32 count( inProp.size( visitor.mObj ) ); + if ( count ) + { + Ps::InlineArray theData; + theData.resize( count ); + inProp.get( visitor.mObj, theData.begin()); + + for( PxU32 idx =0; idx < count; ++idx ) + { + visitor.pushName( childName ); + handleComplexObj( visitor, theData[idx], inInfo ); + visitor.popName(); + } + } + } + + template + void handleShapes( TVisitor& visitor, const PxRigidActorShapeCollection& inProp ) + { + PxShapeGeneratedInfo theInfo; + + PxU32 count( inProp.size( visitor.mObj ) ); + if ( count ) + { + Ps::InlineArray theData; + theData.resize( count ); + inProp.get( visitor.mObj, theData.begin(), count ); + for( PxU32 idx = 0; idx < count; ++idx ) + { + const PxShape* shape = theData[idx]; + visitor.pushName( "PxShape" ); + + if( !shape->isExclusive() ) + { + writeReference( visitor.mWriter, visitor.mCollection, "PxShapeRef", shape ); + } + else + { + handleComplexObj( visitor, shape, theInfo ); + } + visitor.popName(); + } + } + } + + template + void handleShapeMaterials( TVisitor& visitor, const PxShapeMaterialsProperty& inProp ) + { + PxU32 count( inProp.size( visitor.mObj ) ); + if ( count ) + { + Ps::InlineArray theData; + theData.resize( count ); + inProp.get( visitor.mObj, theData.begin(), count ); + visitor.pushName( "PxMaterialRef" ); + + for( PxU32 idx =0; idx < count; ++idx ) + writeReference( visitor.mWriter, visitor.mCollection, "PxMaterialRef", theData[idx] ); + visitor.popName(); + } + } + + template + struct RepXVisitorWriterBase + { + TNameStack& mNameStack; + XmlWriter& mWriter; + const TObjType* mObj; + MemoryBuffer& mTempBuffer; + PxCollection& mCollection; + + RepXVisitorWriterBase( TNameStack& ns, XmlWriter& writer, const TObjType* obj, MemoryBuffer& buf, PxCollection& collection ) + : mNameStack( ns ) + , mWriter( writer ) + , mObj( obj ) + , mTempBuffer( buf ) + , mCollection( collection ) + { + } + + RepXVisitorWriterBase( const RepXVisitorWriterBase& other ) + : mNameStack( other.mNameStack ) + , mWriter( other.mWriter ) + , mObj( other.mObj ) + , mTempBuffer( other.mTempBuffer ) + , mCollection( other.mCollection ) + { + } + + RepXVisitorWriterBase& operator=( const RepXVisitorWriterBase& ){ PX_ASSERT( false ); return *this; } + + void gotoTopName() + { + if ( mNameStack.size() && mNameStack.back().mOpen == false ) + { + mWriter.addAndGotoChild( mNameStack.back().mName ); + mNameStack.back().mOpen = true; + } + } + + void pushName( const char* inName ) + { + gotoTopName(); + mNameStack.pushBack( inName ); + } + + void pushBracketedName( const char* inName ) { pushName( inName ); } + void popName() + { + if ( mNameStack.size() ) + { + if ( mNameStack.back().mOpen ) + mWriter.leaveChild(); + mNameStack.popBack(); + } + } + + const char* topName() const + { + if ( mNameStack.size() ) return mNameStack.back().mName; + PX_ASSERT( false ); + return "bad__repx__name"; + } + + template + void simpleProperty( PxU32 /*key*/, TAccessorType& inProp ) + { + typedef typename TAccessorType::prop_type TPropertyType; + TPropertyType propVal = inProp.get( mObj ); + writeProperty( mWriter, mCollection, mTempBuffer, topName(), propVal ); + } + + template + void enumProperty( PxU32 /*key*/, TAccessorType& inProp, const PxU32ToName* inConversions ) + { + writeEnumProperty( mWriter, topName(), inProp.get( mObj ), inConversions ); + } + + template + void flagsProperty( PxU32 /*key*/, const TAccessorType& inProp, const PxU32ToName* inConversions ) + { + writeFlagsProperty( mWriter, mTempBuffer, topName(), inProp.get( mObj ), inConversions ); + } + + template + void complexProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + TPropertyType propVal = inProp.get( mObj ); + handleComplexObj( *this, &propVal, inInfo ); + } + + template + void bufferCollectionProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + + PxU32 count( inProp.size( mObj ) ); + Ps::InlineArray theData; + theData.resize( count ); + + PxClassInfoTraits theTraits; + PX_UNUSED(theTraits); + PxU32 numItems = inProp.get( mObj, theData.begin(), count ); + PX_ASSERT( numItems == count ); + for( PxU32 idx =0; idx < numItems; ++idx ) + { + pushName( inProp.name() ); + handleComplexObj( *this, &theData[idx], inInfo ); + popName(); + } + } + + template + void extendedIndexedProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& /*inInfo */) + { + typedef typename TAccessorType::prop_type TPropertyType; + + PxU32 count( inProp.size( mObj ) ); + Ps::InlineArray theData; + theData.resize( count ); + + for(PxU32 i = 0; i < count; ++i) + { + char buffer[32] = { 0 }; + sprintf( buffer, "id_%u", i ); + pushName( buffer ); + + TPropertyType propVal = inProp.get( mObj, i ); + TInfoType& infoType = PxClassInfoTraits().Info; + handleComplexObj(*this, &propVal, infoType); + popName(); + } + } + + template + void PxFixedSizeLookupTableProperty( PxU32* /*key*/, TAccessorType& inProp, TInfoType& /*inInfo */) + { + typedef typename TAccessorType::prop_type TPropertyType; + PxU32 count( inProp.size( mObj ) ); + + PxU32 index = 0; + for(PxU32 i = 0; i < count; ++i) + { + char buffer[32] = { 0 }; + sprintf( buffer, "id_%u", index++ ); + pushName( buffer ); + TPropertyType propVal = inProp.getX( mObj , i); + writeProperty( mWriter, mCollection, mTempBuffer, topName(), propVal ); + popName(); + + sprintf( buffer, "id_%u", index++ ); + pushName( buffer ); + propVal = inProp.getY( mObj , i); + writeProperty( mWriter, mCollection, mTempBuffer, topName(), propVal ); + popName(); + } + } + + + void handleShapes( const PxRigidActorShapeCollection& inProp ) + { + physx::Sn::handleShapes( *this, inProp ); + } + + void handleShapeMaterials( const PxShapeMaterialsProperty& inProp ) + { + physx::Sn::handleShapeMaterials( *this, inProp ); + } + }; + + template + struct RepXVisitorWriter : RepXVisitorWriterBase + { + RepXVisitorWriter( TNameStack& ns, XmlWriter& writer, const TObjType* obj, MemoryBuffer& buf, PxCollection& collection ) + : RepXVisitorWriterBase( ns, writer, obj, buf, collection ) + { + } + + RepXVisitorWriter( const RepXVisitorWriter& other ) + : RepXVisitorWriterBase( other ) + { + } + }; + + template<> + struct RepXVisitorWriter : RepXVisitorWriterBase + { + RepXVisitorWriter( TNameStack& ns, XmlWriter& writer, const PxArticulationLink* obj, MemoryBuffer& buf, PxCollection& collection ) + : RepXVisitorWriterBase( ns, writer, obj, buf, collection ) + { + } + + RepXVisitorWriter( const RepXVisitorWriter& other ) + : RepXVisitorWriterBase( other ) + { + } + + void handleIncomingJoint( const TIncomingJointPropType& prop ) + { + const PxArticulationJointBase* theJoint( prop.get( mObj ) ); + if ( theJoint ) + { + PxArticulationJointGeneratedInfo info; + pushName( "Joint" ); + handleComplexObj( *this, theJoint, info ); + popName(); + } + } + }; + + typedef PxProfileHashMap< const PxSerialObjectId, const PxArticulationLink* > TArticulationLinkLinkMap; + + template<> + struct RepXVisitorWriter : RepXVisitorWriterBase + { + TArticulationLinkLinkMap& mArticulationLinkParents; + + RepXVisitorWriter( TNameStack& ns, XmlWriter& writer, const PxArticulation* inArticulation, MemoryBuffer& buf, PxCollection& collection, TArticulationLinkLinkMap* artMap = NULL ) + : RepXVisitorWriterBase( ns, writer, inArticulation, buf, collection ) + , mArticulationLinkParents( *artMap ) + { + Ps::InlineArray > linkList( PxProfileWrapperReflectionAllocator( buf.mManager->getWrapper() ) ); + PxU32 numLinks = inArticulation->getNbLinks(); + linkList.resize( numLinks ); + inArticulation->getLinks( linkList.begin(), numLinks ); + for ( PxU32 idx = 0; idx < numLinks; ++idx ) + { + const PxArticulationLink* theLink( linkList[idx] ); + + Ps::InlineArray theChildList; + PxU32 numChildren = theLink->getNbChildren(); + theChildList.resize( numChildren ); + theLink->getChildren( theChildList.begin(), numChildren ); + for ( PxU32 childIdx = 0; childIdx < numChildren; ++childIdx ) + mArticulationLinkParents.insert(static_cast(reinterpret_cast(theChildList[childIdx])), theLink ); + } + } + + RepXVisitorWriter( const RepXVisitorWriter& other ) + : RepXVisitorWriterBase( other ) + , mArticulationLinkParents( other.mArticulationLinkParents ) + { + } + template + void complexProperty( PxU32* /*key*/, const TAccessorType& inProp, TInfoType& inInfo ) + { + typedef typename TAccessorType::prop_type TPropertyType; + TPropertyType propVal = inProp.get( mObj ); + handleComplexObj( *this, &propVal, inInfo ); + } + + void writeArticulationLink( const PxArticulationLink* inLink ) + { + pushName( "PxArticulationLink" ); + gotoTopName(); + + const TArticulationLinkLinkMap::Entry* theParentPtr = mArticulationLinkParents.find(static_cast(reinterpret_cast(inLink)) ); + if ( theParentPtr != NULL ) + writeProperty( mWriter, mCollection, mTempBuffer, "Parent", theParentPtr->second ); + writeProperty( mWriter, mCollection, mTempBuffer, "Id", inLink ); + + PxArticulationLinkGeneratedInfo info; + handleComplexObj( *this, inLink, info ); + popName(); + } + + void recurseAddLinkAndChildren( const PxArticulationLink* inLink, Ps::InlineArray& ioLinks ) + { + ioLinks.pushBack( inLink ); + Ps::InlineArray theChildren; + PxU32 childCount( inLink->getNbChildren() ); + theChildren.resize( childCount ); + inLink->getChildren( theChildren.begin(), childCount ); + for ( PxU32 idx = 0; idx < childCount; ++idx ) + recurseAddLinkAndChildren( theChildren[idx], ioLinks ); + } + + void handleArticulationLinks( const PxArticulationLinkCollectionProp& inProp ) + { + //topologically sort the links as per my discussion with Dilip because + //links aren't guaranteed to have the parents before the children in the + //overall link list and it is unlikely to be done by beta 1. + PxU32 count( inProp.size( mObj ) ); + if ( count ) + { + Ps::InlineArray theLinks; + theLinks.resize( count ); + inProp.get( mObj, theLinks.begin(), count ); + + Ps::InlineArray theSortedLinks; + for ( PxU32 idx = 0; idx < count; ++idx ) + { + const PxArticulationLink* theLink( theLinks[idx] ); + if ( mArticulationLinkParents.find(static_cast(reinterpret_cast(theLink)) ) == NULL ) + recurseAddLinkAndChildren( theLink, theSortedLinks ); + } + PX_ASSERT( theSortedLinks.size() == count ); + for ( PxU32 idx = 0; idx < count; ++idx ) + writeArticulationLink( theSortedLinks[idx] ); + popName(); + } + } + private: + RepXVisitorWriter& operator=(const RepXVisitorWriter&); + }; + + template<> + struct RepXVisitorWriter : RepXVisitorWriterBase + { + RepXVisitorWriter( TNameStack& ns, XmlWriter& writer, const PxShape* obj, MemoryBuffer& buf, PxCollection& collection ) + : RepXVisitorWriterBase( ns, writer, obj, buf, collection ) + { + } + + RepXVisitorWriter( const RepXVisitorWriter& other ) + : RepXVisitorWriterBase( other ) + { + } + + template + inline void writeGeometryProperty( const PxShapeGeometryProperty& inProp, const char* inTypeName ) + { + pushName( "Geometry" ); + pushName( inTypeName ); + GeometryType theType; + inProp.getGeometry( mObj, theType ); + PxClassInfoTraits theTraits; + PxU32 count = theTraits.Info.totalPropertyCount(); + if(count) + { + handleComplexObj( *this, &theType, theTraits.Info); + } + else + { + writeProperty(mWriter, mTempBuffer, inTypeName); + } + popName(); + popName(); + } + + void handleGeometryProperty( const PxShapeGeometryProperty& inProp ) + { + switch( mObj->getGeometryType() ) + { + case PxGeometryType::eSPHERE: writeGeometryProperty( inProp, "PxSphereGeometry" ); break; + case PxGeometryType::ePLANE: writeGeometryProperty( inProp, "PxPlaneGeometry" ); break; + case PxGeometryType::eCAPSULE: writeGeometryProperty( inProp, "PxCapsuleGeometry" ); break; + case PxGeometryType::eBOX: writeGeometryProperty( inProp, "PxBoxGeometry" ); break; + case PxGeometryType::eCONVEXMESH: writeGeometryProperty( inProp, "PxConvexMeshGeometry" ); break; + case PxGeometryType::eTRIANGLEMESH: writeGeometryProperty( inProp, "PxTriangleMeshGeometry" ); break; + case PxGeometryType::eHEIGHTFIELD: writeGeometryProperty( inProp, "PxHeightFieldGeometry" ); break; + case PxGeometryType::eINVALID: + case PxGeometryType::eGEOMETRY_COUNT: + PX_ASSERT( false ); + } + } + }; + + template + inline void writeAllProperties( TNameStack& inNameStack, const TObjType* inObj, XmlWriter& writer, MemoryBuffer& buffer, PxCollection& collection ) + { + RepXVisitorWriter newVisitor( inNameStack, writer, inObj, buffer, collection ); + RepXPropertyFilter > theOp( newVisitor ); + PxClassInfoTraits info; + info.Info.visitBaseProperties( theOp ); + info.Info.visitInstanceProperties( theOp ); + } + + template + inline void writeAllProperties( TNameStack& inNameStack, TObjType* inObj, XmlWriter& writer, MemoryBuffer& buffer, PxCollection& collection ) + { + RepXVisitorWriter newVisitor( inNameStack, writer, inObj, buffer, collection ); + RepXPropertyFilter > theOp( newVisitor ); + PxClassInfoTraits info; + info.Info.visitBaseProperties( theOp ); + info.Info.visitInstanceProperties( theOp ); + } + + template + inline void writeAllProperties( const TObjType* inObj, XmlWriter& writer, MemoryBuffer& buffer, PxCollection& collection ) + { + TNameStack theNames( buffer.mManager->getWrapper() ); + writeAllProperties( theNames, inObj, writer, buffer, collection ); + } + + template + inline void handleComplexObj( TWriterType& oldVisitor, const TObjType* inObj, const TInfoType& /*info*/) + { + writeAllProperties( oldVisitor.mNameStack, inObj, oldVisitor.mWriter, oldVisitor.mTempBuffer, oldVisitor.mCollection ); + } + + template + inline void handleComplexObj( TWriterType& oldVisitor, const TObjType* inObj, TInfoType& /*info*/) + { + writeAllProperties( oldVisitor.mNameStack, inObj, oldVisitor.mWriter, oldVisitor.mTempBuffer, oldVisitor.mCollection ); + } + + template + inline void handleComplexObj( TWriterType& oldVisitor, const TObjType* inObj, const PxUnknownClassInfo& /*info*/) + { + writeProperty( oldVisitor.mWriter, oldVisitor.mCollection, oldVisitor.mTempBuffer, oldVisitor.topName(), *inObj ); + } + +} } +#endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h new file mode 100644 index 000000000..666374573 --- /dev/null +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_XML_WRITER_H +#define PX_XML_WRITER_H + +#include "foundation/PxSimpleTypes.h" + +namespace physx { + + struct PxRepXObject; + + /** + * Writer used by extensions to write elements to a file or database + */ + class XmlWriter + { + protected: + virtual ~XmlWriter(){} + public: + /** Write a key-value pair into the current item */ + virtual void write( const char* inName, const char* inData ) = 0; + /** Write an object id into the current item */ + virtual void write( const char* inName, const PxRepXObject& inLiveObject ) = 0; + /** Add a child that then becomes the current context */ + virtual void addAndGotoChild( const char* inName ) = 0; + /** Leave the current child */ + virtual void leaveChild() = 0; + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h b/src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h new file mode 100644 index 000000000..57fb564d2 --- /dev/null +++ b/src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h @@ -0,0 +1,189 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSX_GPU_H +#define PX_PHYSX_GPU_H + +#include "task/PxTask.h" + +#include "Ps.h" +#include "PsArray.h" +#include "foundation/PxBounds3.h" +#include "common/PxPhysXCommonConfig.h" +#include "CmPhysXCommon.h" +#include "PxSceneDesc.h" + + +namespace physx +{ + +class PxFoundation; +class PxCudaContextManagerDesc; +class PxvNphaseImplementationContext; +class PxsContext; +class PxsKernelWranglerManager; +class PxvNphaseImplementationFallback; +struct PxgDynamicsMemoryConfig; +class PxsMemoryManager; +class PxsHeapMemoryAllocatorManager; +class PxsSimulationController; +class PxsSimulationControllerCallback; +class PxDelayLoadHook; + +struct PxvSimStats; + +namespace Bp +{ + class BroadPhase; +} + +namespace Dy +{ + class Context; +} + +namespace IG +{ + class IslandSim; + class SimpleIslandManager; +} + +namespace Cm +{ + class FlushPool; +} + +/** +\brief Interface to create and run CUDA enabled PhysX features. + +The methods of this interface are expected not to be called concurrently. +Also they are expected to not be called concurrently with any tasks spawned before the end pipeline ... TODO make clear. +*/ +class PxPhysXGpu +{ +public: + /** + \brief Closes this instance of the interface. + */ + virtual void release() = 0; + + /** + Create GPU memory manager. + */ + virtual PxsMemoryManager* createGpuMemoryManager(PxGpuDispatcher* gpuDispatcher, class PxGraphicsContextManager* graphicsContextManager) = 0; + + virtual PxsHeapMemoryAllocatorManager* createGpuHeapMemoryAllocatorManager( + const PxU32 heapCapacity, + PxsMemoryManager* memoryManager, + const PxU32 gpuComputeVersion) = 0; + + /** + Create GPU kernel wrangler manager. + */ + virtual PxsKernelWranglerManager* createGpuKernelWranglerManager( + PxGpuDispatcher* gpuDispatcher, + PxErrorCallback& errorCallback, + const PxU32 gpuComputeVersion) = 0; + + /** + Create GPU broadphase. + */ + virtual Bp::BroadPhase* createGpuBroadPhase( + PxsKernelWranglerManager* gpuKernelWrangler, + PxGpuDispatcher* gpuDispatch, + PxGraphicsContextManager* graphicsContext, + const PxU32 gpuComputeVersion, + const PxgDynamicsMemoryConfig& config, + PxsHeapMemoryAllocatorManager* heapMemoryManager) = 0; + + /** + Create GPU narrow phase context. + */ + virtual PxvNphaseImplementationContext* createGpuNphaseImplementationContext(PxsContext& context, + PxsKernelWranglerManager* gpuKernelWrangler, + PxvNphaseImplementationFallback* fallbackForUnsupportedCMs, + const PxgDynamicsMemoryConfig& gpuDynamicsConfig, void* contactStreamBase, void* patchStreamBase, void* forceAndIndiceStreamBase, + Ps::Array& bounds, IG::IslandSim* islandSim, + physx::Dy::Context* dynamicsContext, const PxU32 gpuComputeVersion, PxsHeapMemoryAllocatorManager* heapMemoryManager) = 0; + + /** + Create GPU simulation controller. + */ + virtual PxsSimulationController* createGpuSimulationController(PxsKernelWranglerManager* gpuWranglerManagers, + PxGpuDispatcher* gpuDispatcher, PxGraphicsContextManager* graphicsContextManager, + Dy::Context* dynamicContext, PxvNphaseImplementationContext* npContext, Bp::BroadPhase* bp, + const bool useGpuBroadphase, IG::SimpleIslandManager* simpleIslandSim, + PxsSimulationControllerCallback* callback, const PxU32 gpuComputeVersion, PxsHeapMemoryAllocatorManager* heapMemoryManager) = 0; + + /** + Create GPU dynamics context. + */ + virtual Dy::Context* createGpuDynamicsContext(Cm::FlushPool& taskPool, PxsKernelWranglerManager* gpuKernelWragler, + PxGpuDispatcher* gpuDispatcher, PxGraphicsContextManager* graphicsContextManager, + const PxgDynamicsMemoryConfig& config, IG::IslandSim* accurateIslandSim, const PxU32 maxNumPartitions, + const bool enableStabilization, const bool useEnhancedDeterminism, const bool useAdaptiveForce, const PxReal maxBiasCoefficient, + const PxU32 gpuComputeVersion, PxvSimStats& simStats, PxsHeapMemoryAllocatorManager* heapMemoryManager, + const bool frictionEveryIteration, PxSolverType::Enum solverType) = 0; + +}; + +} + +#if PX_WINDOWS +/** +Sets delay load hook instance for PhysXGpu dll. +*/ +PX_C_EXPORT PX_PHYSX_GPU_API void PX_CALL_CONV PxSetPhysXGpuDelayLoadHook(const physx::PxDelayLoadHook* hook); +#endif + +/** +Create PxPhysXGpu interface class. +*/ +PX_C_EXPORT PX_PHYSX_GPU_API physx::PxPhysXGpu* PX_CALL_CONV PxCreatePhysXGpu(); + +/** +Create a cuda context manager. +*/ +PX_C_EXPORT PX_PHYSX_GPU_API physx::PxCudaContextManager* PX_CALL_CONV PxCreateCudaContextManager(physx::PxFoundation& foundation, const physx::PxCudaContextManagerDesc& desc); + +/** +Query the device ordinal - depends on control panel settings. +*/ +PX_C_EXPORT PX_PHYSX_GPU_API int PX_CALL_CONV PxGetSuggestedCudaDeviceOrdinal(physx::PxErrorCallback& errc); + +namespace grid +{ + class ServerImpl; + class ClientContextPredictionManager; +} + +PX_C_EXPORT PX_PHYSX_GPU_API grid::ClientContextPredictionManager* PX_CALL_CONV PxCreateCudaClientContextManager(grid::ServerImpl* server, physx::PxU32 maxNbSleepMsg); + +#endif // PX_PHYSX_GPU_H diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h new file mode 100644 index 000000000..27933da36 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h @@ -0,0 +1,352 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PVD_META_DATA_DEFINE_PROPERTIES_H +#define PVD_META_DATA_DEFINE_PROPERTIES_H + +#if PX_SUPPORT_PVD + +#include "PvdMetaDataPropertyVisitor.h" +#include "PxPvdDataStreamHelpers.h" +#include "PxPvdDataStream.h" +#include "PxCoreUtilityTypes.h" + + +namespace physx +{ +namespace Vd +{ + using namespace physx::shdfnd; + using namespace physx::pvdsdk; + + template + struct PropertyDefinitionOp + { + void defineProperty( PvdPropertyDefinitionHelper& mHelper, NamespacedName mClassKey ) + { + mHelper.createProperty( mClassKey, "", getPvdNamespacedNameForType(), PropertyType::Scalar ); + } + }; + template<> + struct PropertyDefinitionOp + { + void defineProperty( PvdPropertyDefinitionHelper& mHelper, NamespacedName mClassKey ) + { + mHelper.createProperty( mClassKey, "", getPvdNamespacedNameForType(), PropertyType::Scalar ); + } + }; +#define DEFINE_PROPERTY_DEFINITION_OP_NOP( type ) \ + template<> struct PropertyDefinitionOp { void defineProperty( PvdPropertyDefinitionHelper&, NamespacedName ){} }; + + //NOP out these two types. + DEFINE_PROPERTY_DEFINITION_OP_NOP( PxStridedData ) + DEFINE_PROPERTY_DEFINITION_OP_NOP( PxBoundedData ) + +#define DEFINE_PROPERTY_DEFINITION_OBJECT_REF( type ) \ + template<> struct PropertyDefinitionOp { \ + void defineProperty( PvdPropertyDefinitionHelper& mHelper, NamespacedName mClassKey) \ + { \ + mHelper.createProperty( mClassKey, "", getPvdNamespacedNameForType(), PropertyType::Scalar ); \ + } \ + }; + + DEFINE_PROPERTY_DEFINITION_OBJECT_REF( PxTriangleMesh* ) + DEFINE_PROPERTY_DEFINITION_OBJECT_REF( PxBVH33TriangleMesh* ) + DEFINE_PROPERTY_DEFINITION_OBJECT_REF( PxBVH34TriangleMesh* ) + DEFINE_PROPERTY_DEFINITION_OBJECT_REF( PxConvexMesh* ) + DEFINE_PROPERTY_DEFINITION_OBJECT_REF( PxHeightField* ) + + +struct PvdClassInfoDefine +{ + PvdPropertyDefinitionHelper& mHelper; + NamespacedName mClassKey; + + PvdClassInfoDefine( PvdPropertyDefinitionHelper& info, NamespacedName inClassName ) + : mHelper( info ) + , mClassKey( inClassName ) { } + + PvdClassInfoDefine( const PvdClassInfoDefine& other ) + : mHelper( other.mHelper ) + , mClassKey( other.mClassKey ) + { + } + + void defineProperty( NamespacedName inDtype, const char* semantic = "", PropertyType::Enum inPType = PropertyType::Scalar ) + { + mHelper.createProperty( mClassKey, semantic, inDtype, inPType ); + } + + void pushName( const char* inName ) + { + mHelper.pushName( inName ); + } + + void pushBracketedName( const char* inName) + { + mHelper.pushBracketedName( inName ); + } + + void popName() + { + mHelper.popName(); + } + + inline void defineNameValueDefs( const PxU32ToName* theConversions ) + { + while( theConversions->mName != NULL ) + { + mHelper.addNamedValue( theConversions->mName, theConversions->mValue ); + ++theConversions; + } + } + + template + void simpleProperty( PxU32, TAccessorType& /*inProp */) + { + typedef typename TAccessorType::prop_type TPropertyType; + PropertyDefinitionOp().defineProperty( mHelper, mClassKey ); + } + + template + void extendedIndexedProperty( PxU32* key, const TAccessorType& inProp, TInfoType& ) + { + simpleProperty(*key, inProp); + } + + template + static NamespacedName getNameForEnumType() + { + size_t s = sizeof( TDataType ); + switch(s) + { + case 1: return getPvdNamespacedNameForType(); + case 2: return getPvdNamespacedNameForType(); + case 4: return getPvdNamespacedNameForType(); + default: return getPvdNamespacedNameForType(); + } + } + + template + void enumProperty( PxU32 /*key*/, TAccessorType& /*inProp*/, const PxU32ToName* inConversions ) + { + typedef typename TAccessorType::prop_type TPropType; + defineNameValueDefs( inConversions ); + defineProperty( getNameForEnumType(), "Enumeration Value" ); + } + + template + void flagsProperty( PxU32 /*key*/, const TAccessorType& /*inAccessor*/, const PxU32ToName* inConversions ) + { + typedef typename TAccessorType::prop_type TPropType; + defineNameValueDefs( inConversions ); + defineProperty( getNameForEnumType(), "Bitflag" ); + } + + template + void complexProperty( PxU32* key, const TAccessorType& inAccessor, TInfoType& inInfo ) + { + PxU32 theOffset = inAccessor.mOffset; + inInfo.visitBaseProperties( makePvdPropertyFilter( *this, key, &theOffset ) ); + inInfo.visitInstanceProperties( makePvdPropertyFilter( *this, key, &theOffset ) ); + } + + template + void bufferCollectionProperty( PxU32* key, const TAccessorType& inAccessor, TInfoType& inInfo ) + { + complexProperty(key, inAccessor, inInfo); + } + + template + void handleBuffer( const PxBufferPropertyInfo&, TEnableFlag>& inProp ) + { + mHelper.pushName( inProp.mName ); + defineProperty( getPvdNamespacedNameForType(), "", PropertyType::Array ); + mHelper.popName(); + } + + template + void handleCollection( const PxReadOnlyCollectionPropertyInfo& inProp ) + { + mHelper.pushName( inProp.mName ); + defineProperty( getPvdNamespacedNameForType(), "", PropertyType::Array ); + mHelper.popName(); + } + + template + void handleCollection( const PxReadOnlyCollectionPropertyInfo& inProp, const PxU32ToName* inConversions ) + { + mHelper.pushName( inProp.mName ); + defineNameValueDefs( inConversions ); + defineProperty( getNameForEnumType(), "Enumeration Value", PropertyType::Array ); + mHelper.popName(); + } + +private: + PvdClassInfoDefine& operator=(const PvdClassInfoDefine&); +}; + +template +struct SimplePropertyValueStructOp +{ + void addPropertyMessageArg( PvdPropertyDefinitionHelper& mHelper, PxU32 inOffset ) + { + mHelper.addPropertyMessageArg( inOffset ); + } +}; + +#define DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_OP_NOP( type ) \ +template<> struct SimplePropertyValueStructOp { void addPropertyMessageArg( PvdPropertyDefinitionHelper&, PxU32 ){}}; + +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_OP_NOP( PxStridedData ) +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_OP_NOP( PxBoundedData ) + +#define DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( type ) \ +template<> struct SimplePropertyValueStructOp { \ +void addPropertyMessageArg( PvdPropertyDefinitionHelper& mHelper, PxU32 inOffset ) \ +{ \ + mHelper.addPropertyMessageArg( inOffset ); \ +} \ +}; + +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( PxTriangleMesh* ) +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( PxBVH33TriangleMesh* ) +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( PxBVH34TriangleMesh* ) +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( PxConvexMesh* ) +DEFINE_SIMPLE_PROPERTY_VALUE_STRUCT_VOIDPTR_OP( PxHeightField* ) + + +struct PvdClassInfoValueStructDefine +{ +private: + PvdClassInfoValueStructDefine& operator=(const PvdClassInfoValueStructDefine&); +public: + + PvdPropertyDefinitionHelper& mHelper; + + PvdClassInfoValueStructDefine( PvdPropertyDefinitionHelper& info ) + : mHelper( info ) + { } + + PvdClassInfoValueStructDefine( const PvdClassInfoValueStructDefine& other ) + : mHelper( other.mHelper ) + { + } + + void defineValueStructOffset( const ValueStructOffsetRecord& inProp, PxU32 inPropSize ) + { + if( inProp.mHasValidOffset ) + { + switch( inPropSize ) + { + case 8: mHelper.addPropertyMessageArg( inProp.mOffset ); break; + case 4: mHelper.addPropertyMessageArg( inProp.mOffset ); break; + case 2: mHelper.addPropertyMessageArg( inProp.mOffset ); break; + default: + PX_ASSERT(1 == inPropSize); + mHelper.addPropertyMessageArg( inProp.mOffset ); break; + } + } + } + + void pushName( const char* inName ) + { + mHelper.pushName( inName ); + } + + void pushBracketedName( const char* inName) + { + mHelper.pushBracketedName( inName ); + } + + void popName() + { + mHelper.popName(); + } + + template + void bufferCollectionProperty( PxU32* /*key*/, const TAccessorType& /*inAccessor*/, TInfoType& /*inInfo*/ ) + { + //complexProperty(key, inAccessor, inInfo); + } + + template + void simpleProperty( PxU32 /*key*/, TAccessorType& inProp ) + { + typedef typename TAccessorType::prop_type TPropertyType; + if ( inProp.mHasValidOffset ) + { + SimplePropertyValueStructOp().addPropertyMessageArg( mHelper, inProp.mOffset ); + } + } + + template + void enumProperty( PxU32 /*key*/, TAccessorType& inAccessor, const PxU32ToName* /*inConversions */) + { + typedef typename TAccessorType::prop_type TPropType; + defineValueStructOffset( inAccessor, sizeof( TPropType ) ); + } + + template + void flagsProperty( PxU32 /*key*/, const TAccessorType& inAccessor, const PxU32ToName* /*inConversions */) + { + typedef typename TAccessorType::prop_type TPropType; + defineValueStructOffset( inAccessor, sizeof( TPropType ) ); + } + + template + void complexProperty( PxU32* key, const TAccessorType& inAccessor, TInfoType& inInfo ) + { + PxU32 theOffset = inAccessor.mOffset; + inInfo.visitBaseProperties( makePvdPropertyFilter( *this, key, &theOffset ) ); + inInfo.visitInstanceProperties( makePvdPropertyFilter( *this, key, &theOffset ) ); + } + + template + void handleCollection( const PxReadOnlyCollectionPropertyInfo& /*prop*/ ) + { + } + + template + void handleCollection( const PxReadOnlyCollectionPropertyInfo& /*prop*/, const PxU32ToName* /*inConversions */) + { + } + + template + void handleCollection( const PxBufferCollectionPropertyInfo& /*prop*/, const TInfoType& /*inInfo */) + { + } +}; + +} + +} + +#endif +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h new file mode 100644 index 000000000..2e067ab49 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h @@ -0,0 +1,318 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_META_DATA_EXTENSIONS_H +#define PX_META_DATA_EXTENSIONS_H +#include "PxMetaDataObjects.h" + +#if PX_SUPPORT_PVD +#include "PxPvdObjectModelBaseTypes.h" + +namespace physx { namespace pvdsdk { + + template<> PX_INLINE NamespacedName getPvdNamespacedNameForType() { return getPvdNamespacedNameForType(); } + template<> PX_INLINE NamespacedName getPvdNamespacedNameForType() { return getPvdNamespacedNameForType(); } + +}} +#endif + +namespace physx +{ +namespace Vd +{ +//Additional properties that exist only in pvd land. +struct PxPvdOnlyProperties +{ + enum Enum + { + FirstProp = PxPropertyInfoName::LastPxPropertyInfoName, + PxScene_Frame, + PxScene_Contacts, + PxScene_SimulateElapsedTime, +#define DEFINE_ENUM_RANGE( stem, count ) \ + stem##Begin, \ + stem##End = stem##Begin + count + + //I can't easily add up the number of required property entries, but it is large due to the below + //geometry count squared properties. Thus I punt and allocate way more than I need right now. + DEFINE_ENUM_RANGE( PxScene_SimulationStatistics, 1000 ), + DEFINE_ENUM_RANGE( PxSceneDesc_Limits, PxPropertyInfoName::PxSceneLimits_PropertiesStop - PxPropertyInfoName::PxSceneLimits_PropertiesStart ), + DEFINE_ENUM_RANGE( PxSimulationStatistics_NumShapes, PxGeometryType::eGEOMETRY_COUNT ), + DEFINE_ENUM_RANGE( PxSimulationStatistics_NumDiscreteContactPairs, PxGeometryType::eGEOMETRY_COUNT * PxGeometryType::eGEOMETRY_COUNT ), + DEFINE_ENUM_RANGE( PxSimulationStatistics_NumModifiedContactPairs, PxGeometryType::eGEOMETRY_COUNT * PxGeometryType::eGEOMETRY_COUNT ), + DEFINE_ENUM_RANGE( PxSimulationStatistics_NumSweptIntegrationPairs, PxGeometryType::eGEOMETRY_COUNT * PxGeometryType::eGEOMETRY_COUNT ), + DEFINE_ENUM_RANGE( PxSimulationStatistics_NumTriggerPairs, PxGeometryType::eGEOMETRY_COUNT * PxGeometryType::eGEOMETRY_COUNT ), + DEFINE_ENUM_RANGE( PxRigidDynamic_SolverIterationCounts, 2 ), + DEFINE_ENUM_RANGE( PxArticulation_SolverIterationCounts, 2 ), + DEFINE_ENUM_RANGE( PxArticulationJoint_SwingLimit, 2 ), + DEFINE_ENUM_RANGE( PxArticulationJoint_TwistLimit, 2 ), + DEFINE_ENUM_RANGE( PxConvexMeshGeometry_Scale, PxPropertyInfoName::PxMeshScale_PropertiesStop - PxPropertyInfoName::PxMeshScale_PropertiesStart ), + DEFINE_ENUM_RANGE( PxTriangleMeshGeometry_Scale, PxPropertyInfoName::PxMeshScale_PropertiesStop - PxPropertyInfoName::PxMeshScale_PropertiesStart ), + + LastPxPvdOnlyProperty + }; +}; + +template +struct PxBufferPropertyInfo : PxReadOnlyPropertyInfo< TKey, TObjectType, TPropertyType > +{ + typedef PxReadOnlyPropertyInfo< TKey, TObjectType, TPropertyType > TBaseType; + typedef typename TBaseType::TGetterType TGetterType; + PxBufferPropertyInfo( const char* inName, TGetterType inGetter ) + : TBaseType( inName, inGetter ) + { + } + bool isEnabled( PxU32 inFlags ) const { return (inFlags & TEnableFlag) > 0; } +}; + + +#define DECLARE_BUFFER_PROPERTY( objectType, baseType, propType, propName, fieldName, flagName ) \ +typedef PxBufferPropertyInfo< PxPvdOnlyProperties::baseType##_##propName, objectType, propType, flagName > T##objectType##propName##Base; \ +inline propType get##propName( const objectType* inData ) { return inData->fieldName; } \ +struct baseType##propName##Property : T##objectType##propName##Base \ +{ \ + baseType##propName##Property() : T##objectType##propName##Base( #propName, get##propName ){} \ +}; + +template +struct IndexerToNameMap +{ + PxEnumTraits Converter; +}; + +struct ValueStructOffsetRecord +{ + mutable bool mHasValidOffset; + mutable PxU32 mOffset; + ValueStructOffsetRecord() : mHasValidOffset( false ), mOffset( 0 ) {} + void setupValueStructOffset( PxU32 inValue ) const + { + mHasValidOffset = true; + mOffset = inValue; + } +}; + +template +struct PxPvdReadOnlyPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxReadOnlyPropertyInfo TPropertyInfoType; + typedef TPropertyType prop_type; + + const TPropertyInfoType mProperty; + PxPvdReadOnlyPropertyAccessor( const TPropertyInfoType& inProp ) + : mProperty( inProp ) + { + } + prop_type get( const TObjectType* inObj ) const { return mProperty.get( inObj ); } + +private: + PxPvdReadOnlyPropertyAccessor& operator=(const PxPvdReadOnlyPropertyAccessor&); +}; + +template +struct PxBufferCollectionPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxBufferCollectionPropertyInfo< TKey, TObjectType, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + const TPropertyInfoType& mProperty; + const char* mName; + + PxBufferCollectionPropertyAccessor( const TPropertyInfoType& inProp, const char* inName ) + : mProperty( inProp ) + , mName( inName ) + { + } + + const char* name() const { return mName; } + PxU32 size( const TObjectType* inObj ) const { return mProperty.size( inObj ); } + PxU32 get( const TObjectType* inObj, prop_type* buffer, PxU32 inNumItems) const { return mProperty.get( inObj, buffer, inNumItems); } + void set( TObjectType* inObj, prop_type* inBuffer, PxU32 inNumItems ) const { mProperty.set( inObj, inBuffer, inNumItems ); } +}; + +template +struct PxPvdIndexedPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxIndexedPropertyInfo< TKey, TObjectType, TIndexType, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + TIndexType mIndex; + const TPropertyInfoType& mProperty; + PxPvdIndexedPropertyAccessor( const TPropertyInfoType& inProp, PxU32 inIndex ) + : mIndex( static_cast( inIndex ) ) + , mProperty( inProp ) + { + } + prop_type get( const TObjectType* inObj ) const { return mProperty.get( inObj, mIndex ); } + void set( TObjectType* inObj, prop_type val ) const { mProperty.set( inObj, mIndex, val ); } + + void operator = (PxPvdIndexedPropertyAccessor&) {} +}; + +template +struct PxPvdExtendedIndexedPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxExtendedIndexedPropertyInfo< TKey, TObjectType, TIndexType, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + TIndexType mIndex; + const TPropertyInfoType& mProperty; + PxPvdExtendedIndexedPropertyAccessor( const TPropertyInfoType& inProp, PxU32 inIndex ) + : mIndex( static_cast( inIndex ) ) + , mProperty( inProp ) + { + } + + PxU32 size( const TObjectType* inObj ) const { return mProperty.size( inObj ); } + prop_type get( const TObjectType* inObj, TIndexType index ) const { return mProperty.get( inObj, index ); } + void set( TObjectType* inObj, TIndexType index, prop_type val ) const { mProperty.set( inObj, index, val ); } + + void operator = (PxPvdExtendedIndexedPropertyAccessor&) {} +}; + +template +struct PxPvdFixedSizeLookupTablePropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxFixedSizeLookupTablePropertyInfo< TKey, TObjectType, TIndexType, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + TIndexType mIndex; + + const TPropertyInfoType& mProperty; + PxPvdFixedSizeLookupTablePropertyAccessor( const TPropertyInfoType& inProp, const PxU32 inIndex3 ) + : mIndex( static_cast( inIndex3 ) ) + , mProperty( inProp ) + { + } + + PxU32 size( const TObjectType* inObj ) const { return mProperty.size( inObj ); } + prop_type getX( const TObjectType* inObj, const TIndexType index ) const { return mProperty.getX( inObj, index ); } + prop_type getY( const TObjectType* inObj, const TIndexType index ) const { return mProperty.getY( inObj, index ); } + void addPair( TObjectType* inObj, const PxReal x, const PxReal y ) { const_cast(mProperty).addPair( inObj, x, y ); } + void clear( TObjectType* inObj ) { const_cast(mProperty).clear( inObj ); } + void operator = (PxPvdFixedSizeLookupTablePropertyAccessor&) {} +}; + +template +struct PxPvdDualIndexedPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxDualIndexedPropertyInfo< TKey, TObjectType, TIdx0Type, TIdx1Type, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + TIdx0Type mIdx0; + TIdx1Type mIdx1; + const TPropertyInfoType& mProperty; + + PxPvdDualIndexedPropertyAccessor( const TPropertyInfoType& inProp, PxU32 idx0, PxU32 idx1 ) + : mIdx0( static_cast( idx0 ) ) + , mIdx1( static_cast( idx1 ) ) + , mProperty( inProp ) + { + } + prop_type get( const TObjectType* inObj ) const { return mProperty.get( inObj, mIdx0, mIdx1 ); } + void set( TObjectType* inObj, prop_type val ) const { mProperty.set( inObj, mIdx0, mIdx1, val ); } + +private: + PxPvdDualIndexedPropertyAccessor& operator = (const PxPvdDualIndexedPropertyAccessor&); +}; + +template +struct PxPvdExtendedDualIndexedPropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxExtendedDualIndexedPropertyInfo< TKey, TObjectType, TIdx0Type, TIdx1Type, TPropertyType > TPropertyInfoType; + typedef TPropertyType prop_type; + TIdx0Type mIdx0; + TIdx1Type mIdx1; + const TPropertyInfoType& mProperty; + + PxPvdExtendedDualIndexedPropertyAccessor( const TPropertyInfoType& inProp, PxU32 idx0, PxU32 idx1 ) + : mIdx0( static_cast( idx0 ) ) + , mIdx1( static_cast( idx1 ) ) + , mProperty( inProp ) + { + } + prop_type get( const TObjectType* inObj ) const { return mProperty.get( inObj, mIdx0, mIdx1 ); } + void set( TObjectType* inObj, prop_type val ) const { mProperty.set( inObj, mIdx0, mIdx1, val ); } + +private: + PxPvdExtendedDualIndexedPropertyAccessor& operator = (const PxPvdExtendedDualIndexedPropertyAccessor&); +}; + +template +struct PxPvdRangePropertyAccessor : public ValueStructOffsetRecord +{ + typedef PxRangePropertyInfo TPropertyInfoType; + typedef TPropertyType prop_type; + bool mFirstValue; + const TPropertyInfoType& mProperty; + + PxPvdRangePropertyAccessor( const TPropertyInfoType& inProp, bool inFirstValue ) + : mFirstValue( inFirstValue ) + , mProperty( inProp ) + { + } + + prop_type get( const TObjType* inObj ) const { + prop_type first,second; + mProperty.get( inObj, first, second ); + return mFirstValue ? first : second; + } + void set( TObjType* inObj, prop_type val ) const + { + prop_type first,second; + mProperty.get( inObj, first, second ); + if ( mFirstValue ) mProperty.set( inObj, val, second ); + else mProperty.set( inObj, first, val ); + } + + void operator = (PxPvdRangePropertyAccessor&) {} +}; + + +template +struct IsFlagsType +{ + bool FlagData; +}; + +template +struct IsFlagsType > +{ + const PxU32ToName* FlagData; + IsFlagsType > () : FlagData( PxEnumTraits().NameConversion ) {} +}; + + + +template +struct PvdClassForType +{ + bool Unknown; +}; + +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h new file mode 100644 index 000000000..6509ef818 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h @@ -0,0 +1,539 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_META_DATA_PROPERTY_VISITOR_H +#define PX_META_DATA_PROPERTY_VISITOR_H + +#include +#include "PvdMetaDataExtensions.h" +namespace physx +{ + +namespace Vd +{ + +//PVD only deals with read-only properties, indexed, and properties like this by in large. +//so we have a filter that expands properties to a level where we can get to them and expands them into +//named functions that make more sense and are easier to read than operator() +template +struct PvdPropertyFilter +{ + +private: + PvdPropertyFilter& operator=(const PvdPropertyFilter&); + +public: + + TOperatorType mOperator; + PxU32* mKeyOverride; + PxU32* mOffsetOverride; + + PvdPropertyFilter( TOperatorType& inOperator ) + : mOperator( inOperator ) + , mKeyOverride( 0 ) + , mOffsetOverride( 0 ) {} + + PvdPropertyFilter( TOperatorType& inOperator, PxU32* inKeyOverride, PxU32* inOffsetOverride ) + : mOperator( inOperator ) + , mKeyOverride( inKeyOverride ) + , mOffsetOverride( inOffsetOverride ) {} + + PvdPropertyFilter( const PvdPropertyFilter& inOther ) : mOperator( inOther.mOperator ), mKeyOverride( inOther.mKeyOverride ), mOffsetOverride( inOther.mOffsetOverride ) {} + + template + void dispatchAccessor( PxU32 inKey, const TAccessorType& inAccessor, bool, bool, bool) + { + mOperator.simpleProperty(inKey, inAccessor ); + } + + template + void dispatchAccessor( PxU32 inKey, const TAccessorType& inAccessor, bool, bool, const PxU32ToName* inConversions ) + { + mOperator.flagsProperty(inKey, inAccessor, inConversions ); + } + + template + void dispatchAccessor( PxU32 inKey, const TAccessorType& inAccessor, const PxU32ToName* inConversions, bool, bool ) + { + mOperator.enumProperty( inKey, inAccessor, inConversions ); + } + + template + void dispatchAccessor(PxU32, const TAccessorType& inAccessor, bool, const TInfoType* inInfo, bool ) + { + PxU32 rangeStart = TKey; + PxU32& propIdx = mKeyOverride == NULL ? rangeStart : *mKeyOverride; + mOperator.complexProperty( &propIdx, inAccessor, *inInfo ); + } + + PxU32 getKeyValue( PxU32 inPropertyKey ) + { + PxU32 retval = inPropertyKey; + if ( mKeyOverride ) + { + retval = *mKeyOverride; + (*mKeyOverride)++; + } + return retval; + } + + + void setupValueStructOffset( const ValueStructOffsetRecord&, bool, PxU32* ) {} + void setupValueStructOffset( const ValueStructOffsetRecord& inAccessor, PxU32 inOffset, PxU32* inAdditionalOffset ) + { + //This allows us to nest properties correctly. + if ( inAdditionalOffset ) inOffset += *inAdditionalOffset; + inAccessor.setupValueStructOffset( inOffset ); + } + + template + void handleAccessor( PxU32 inKey, const TAccessorType& inAccessor ) + { + typedef typename TAccessorType::prop_type TPropertyType; + dispatchAccessor( inKey + , inAccessor + , PxEnumTraits().NameConversion + , PxClassInfoTraits().getInfo() + , IsFlagsType().FlagData ); + } + + + template + void handleAccessor( const TAccessorType& inAccessor ) + { + setupValueStructOffset( inAccessor, PxPropertyToValueStructMemberMap().Offset, mOffsetOverride ); + handleAccessor( getKeyValue( TKey ), inAccessor ); + } + + template + void operator()( const PxReadOnlyPropertyInfo& inProperty, PxU32 ) + { + PxPvdReadOnlyPropertyAccessor< TKey, TObjType, TPropertyType > theAccessor( inProperty ); + mOperator.pushName( inProperty.mName ); + handleAccessor( theAccessor ); + mOperator.popName(); + } + + //We don't handle unbounded indexed properties + template + void indexedProperty( PxU32, const PxIndexedPropertyInfo&, bool, TValueConversionType, const TInfoType& ) {} + + + template + void indexedProperty( PxU32, const PxIndexedPropertyInfo& inProp, const PxU32ToName* theConversions, const PxUnknownClassInfo& ) + { + mOperator.pushName( inProp.mName ); + PxU32 rangeStart = TKey; + PxU32& propIdx = mKeyOverride == NULL ? rangeStart : *mKeyOverride; + PxU32 theOffset = 0; + if ( mOffsetOverride ) theOffset = *mOffsetOverride; + + while( theConversions->mName != NULL ) + { + mOperator.pushBracketedName( theConversions->mName ); + PxPvdIndexedPropertyAccessor theAccessor( inProp, theConversions->mValue ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + handleAccessor( propIdx, theAccessor ); + mOperator.popName(); + ++propIdx; + ++theConversions; + theOffset += sizeof( TPropertyType ); + } + mOperator.popName(); + } + + template + void indexedProperty( PxU32, const PxIndexedPropertyInfo& inProp, const PxU32ToName* theConversions, const TInfoType& inInfo ) + { + //ouch, not nice. Indexed complex property. + mOperator.pushName( inProp.mName ); + PxU32 propIdx = TKey; + PxU32 theOffset = 0; + if ( mOffsetOverride ) theOffset = *mOffsetOverride; + + while( theConversions->mName != NULL ) + { + mOperator.pushBracketedName( theConversions->mName ); + PxPvdIndexedPropertyAccessor theAccessor( inProp, theConversions->mValue ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + PX_ASSERT( theAccessor.mHasValidOffset ); + mOperator.complexProperty( &propIdx, theAccessor, inInfo ); + mOperator.popName(); + ++theConversions; + theOffset += sizeof( TPropertyType ); + } + mOperator.popName(); + } + + static char* myStrcat(const char* a,const char * b) + { + size_t len = strlen(a)+strlen(b); + char* result = new char[len+1]; + + strcpy(result,a); + strcat(result,b); + result[len] = 0; + + return result; + } + + template + void handleBufferCollectionProperty(PxU32 , const PxBufferCollectionPropertyInfo& inProp, const TInfoType& inInfo) + { + //append 'Collection' to buffer properties + char* name = myStrcat(inProp.mName, "Collection"); + + mOperator.pushName(name); + PxU32 propIdx = TKey; + PxU32 theOffset = 0; + + PxBufferCollectionPropertyAccessor theAccessor( inProp, inProp.mName ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + + mOperator.bufferCollectionProperty( &propIdx, theAccessor, inInfo ); + mOperator.popName(); + delete []name; + } + + template + void operator()( const PxIndexedPropertyInfo& inProp, PxU32 inIndex ) + { + indexedProperty( inIndex, inProp, IndexerToNameMap().Converter.NameConversion + , PxClassInfoTraits().Info ); + } + + template + void handleExtendedIndexProperty(PxU32 inIndex, const PxExtendedIndexedPropertyInfo& inProp, const TInfoType& inInfo) + { + mOperator.pushName(inProp.mName); + PxU32 propIdx = TKey; + PxU32 theOffset = 0; + + PxPvdExtendedIndexedPropertyAccessor theAccessor( inProp, inIndex ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + + mOperator.extendedIndexedProperty( &propIdx, theAccessor, inInfo ); + mOperator.popName(); + } + + template + void handlePxFixedSizeLookupTableProperty( const PxU32 inIndex, const PxFixedSizeLookupTablePropertyInfo& inProp, const TInfoType& inInfo) + { + mOperator.pushName(inProp.mName); + + PxU32 propIdx = TKey; + PxU32 theOffset = 0; + + PxPvdFixedSizeLookupTablePropertyAccessor theAccessor( inProp, inIndex ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + + mOperator.PxFixedSizeLookupTableProperty( &propIdx, theAccessor, inInfo ); + + mOperator.popName(); + } + + template + void operator()( const PxExtendedIndexedPropertyInfo& inProp, PxU32 inIndex ) + { + handleExtendedIndexProperty( inIndex, inProp, PxClassInfoTraits().Info ); + } + + template + void operator()( const PxFixedSizeLookupTablePropertyInfo& inProp, PxU32 inIndex) + { + handlePxFixedSizeLookupTableProperty(inIndex, inProp, PxClassInfoTraits().Info); + } + + //We don't handle unbounded indexed properties + template + void dualIndexedProperty( PxU32 idx, const PxDualIndexedPropertyInfo&, TNameConv, TNameConv2 ) { PX_UNUSED(idx); } + + template + void dualIndexedProperty( PxU32 /*idx*/, const PxDualIndexedPropertyInfo& inProp, const PxU32ToName* c1, const PxU32ToName* c2 ) + { + mOperator.pushName( inProp.mName ); + PxU32 rangeStart = TKey; + PxU32& propIdx = mKeyOverride == NULL ? rangeStart : *mKeyOverride; + PxU32 theOffset = 0; + if ( mOffsetOverride ) theOffset = *mOffsetOverride; + while( c1->mName != NULL ) + { + mOperator.pushBracketedName( c1->mName ); + const PxU32ToName* c2Idx = c2; + while( c2Idx->mName != NULL ) + { + mOperator.pushBracketedName( c2Idx->mName ); + PxPvdDualIndexedPropertyAccessor theAccessor( inProp, c1->mValue, c2Idx->mValue ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + handleAccessor( propIdx, theAccessor ); + mOperator.popName(); + ++propIdx; + ++c2Idx; + theOffset += sizeof( TPropertyType ); + } + mOperator.popName(); + ++c1; + } + mOperator.popName(); + } + + template + void extendedDualIndexedProperty( PxU32 /*idx*/, const PxExtendedDualIndexedPropertyInfo& inProp, PxU32 id0Count, PxU32 id1Count ) + { + mOperator.pushName( inProp.mName ); + PxU32 rangeStart = TKey; + PxU32& propIdx = mKeyOverride == NULL ? rangeStart : *mKeyOverride; + PxU32 theOffset = 0; + if ( mOffsetOverride ) theOffset = *mOffsetOverride; + for(PxU32 i = 0; i < id0Count; ++i) + { + char buffer1[32] = { 0 }; + sprintf( buffer1, "eId1_%u", i ); + + mOperator.pushBracketedName( buffer1 ); + for(PxU32 j = 0; j < id1Count; ++j) + { + char buffer2[32] = { 0 }; + sprintf( buffer2, "eId2_%u", j ); + + mOperator.pushBracketedName( buffer2 ); + PxPvdExtendedDualIndexedPropertyAccessor theAccessor( inProp, i, j ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + handleAccessor( propIdx, theAccessor ); + mOperator.popName(); + ++propIdx; + theOffset += sizeof( TPropertyType ); + } + mOperator.popName(); + } + mOperator.popName(); + } + + template + void operator()( const PxDualIndexedPropertyInfo& inProp, PxU32 idx ) + { + dualIndexedProperty( idx, inProp + , IndexerToNameMap().Converter.NameConversion + , IndexerToNameMap().Converter.NameConversion ); + } + + template + void operator()( const PxExtendedDualIndexedPropertyInfo& inProp, PxU32 idx ) + { + extendedDualIndexedProperty( idx, inProp, inProp.mId0Count, inProp.mId1Count ); + } + + template + void operator()( const PxRangePropertyInfo& inProperty, PxU32 /*idx*/) + { + PxU32 rangeStart = TKey; + PxU32& propIdx = mKeyOverride == NULL ? rangeStart : *mKeyOverride; + PxU32 theOffset = 0; + if ( mOffsetOverride ) theOffset = *mOffsetOverride; + + mOperator.pushName( inProperty.mName ); + mOperator.pushName( inProperty.mArg0Name ); + PxPvdRangePropertyAccessor theAccessor( inProperty, true ); + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + handleAccessor( propIdx, theAccessor ); + ++propIdx; + theOffset += sizeof( TPropertyType ); + mOperator.popName(); + mOperator.pushName( inProperty.mArg1Name ); + theAccessor.mFirstValue = false; + setupValueStructOffset( theAccessor, PxPropertyToValueStructMemberMap().Offset, &theOffset ); + handleAccessor( propIdx, theAccessor ); + mOperator.popName(); + mOperator.popName(); + } + + template + void operator()( const PxBufferCollectionPropertyInfo& inProp, PxU32 count ) + { + handleBufferCollectionProperty( count, inProp, PxClassInfoTraits().Info ); + } + + template + void handleBuffer( const PxBufferPropertyInfo& inProp ) + { + mOperator.handleBuffer( inProp ); + } + + template + void operator()( const PxBufferPropertyInfo& inProp, PxU32 ) + { + handleBuffer( inProp ); + } + + template + void operator()( const PxReadOnlyCollectionPropertyInfo& prop, PxU32 ) + { + mOperator.handleCollection( prop ); + } + + template + void operator()( const PxReadOnlyCollectionPropertyInfo& prop, PxU32 ) + { + mOperator.handleCollection( prop ); + } + + template + void operator()( const PxWriteOnlyPropertyInfo&, PxU32 ) {} + + template + void operator()( const PxReadOnlyCollectionPropertyInfo&, PxU32 ) {} + + template + void operator()( const PxReadOnlyFilteredCollectionPropertyInfo&, PxU32 ) {} + + + //We don't deal with these property datatypes. +#define DEFINE_PVD_PROPERTY_NOP(datatype) \ + template \ + void operator()( const PxReadOnlyPropertyInfo& inProperty, PxU32 ){PX_UNUSED(inProperty); } + + DEFINE_PVD_PROPERTY_NOP( const void* ) + DEFINE_PVD_PROPERTY_NOP( void* ) + DEFINE_PVD_PROPERTY_NOP( PxSimulationFilterCallback * ) + DEFINE_PVD_PROPERTY_NOP( physx::PxTaskManager * ) + DEFINE_PVD_PROPERTY_NOP( PxSimulationFilterShader * ) + DEFINE_PVD_PROPERTY_NOP( PxSimulationFilterShader) + DEFINE_PVD_PROPERTY_NOP( PxContactModifyCallback * ) + DEFINE_PVD_PROPERTY_NOP( PxCCDContactModifyCallback * ) + DEFINE_PVD_PROPERTY_NOP( PxSimulationEventCallback * ) + DEFINE_PVD_PROPERTY_NOP( physx::PxGpuDispatcher* ) + DEFINE_PVD_PROPERTY_NOP( physx::PxCpuDispatcher * ) + DEFINE_PVD_PROPERTY_NOP( PxRigidActor ) + DEFINE_PVD_PROPERTY_NOP( const PxRigidActor ) + DEFINE_PVD_PROPERTY_NOP( PxRigidActor& ) + DEFINE_PVD_PROPERTY_NOP( const PxRigidActor& ) + DEFINE_PVD_PROPERTY_NOP( PxScene* ) + DEFINE_PVD_PROPERTY_NOP( PxConstraint ) + DEFINE_PVD_PROPERTY_NOP( PxConstraint* ) + DEFINE_PVD_PROPERTY_NOP( PxConstraint& ) + DEFINE_PVD_PROPERTY_NOP( const PxConstraint& ) + DEFINE_PVD_PROPERTY_NOP( PxAggregate * ) + DEFINE_PVD_PROPERTY_NOP( PxArticulation& ) + DEFINE_PVD_PROPERTY_NOP( PxArticulationBase& ) + DEFINE_PVD_PROPERTY_NOP( PxArticulationReducedCoordinate&) + DEFINE_PVD_PROPERTY_NOP( const PxArticulationLink * ) + DEFINE_PVD_PROPERTY_NOP( const PxRigidDynamic * ) + DEFINE_PVD_PROPERTY_NOP( const PxRigidStatic * ) + DEFINE_PVD_PROPERTY_NOP( PxArticulationJointBase * ) + DEFINE_PVD_PROPERTY_NOP( PxArticulationJointReducedCoordinate * ) + DEFINE_PVD_PROPERTY_NOP( PxArticulationJoint * ) + DEFINE_PVD_PROPERTY_NOP( PxBroadPhaseCallback * ) + DEFINE_PVD_PROPERTY_NOP( const PxBroadPhaseRegion * ) + DEFINE_PVD_PROPERTY_NOP( PxU32 * ) + +}; + +template +inline PvdPropertyFilter makePvdPropertyFilter( TOperator inOperator ) +{ + return PvdPropertyFilter( inOperator ); +} + +template +inline PvdPropertyFilter makePvdPropertyFilter( TOperator inOperator, PxU32* inKey, PxU32* inOffset ) +{ + return PvdPropertyFilter( inOperator, inKey, inOffset ); +} + +template +inline void visitWithPvdFilter( TOperator inOperator, TFuncType inFuncType ) +{ + PX_UNUSED(inFuncType); + TFuncType( makePvdPropertyFilter( inOperator ) ); +} + +template +inline void visitInstancePvdProperties( TOperator inOperator ) +{ + visitInstanceProperties( makePvdPropertyFilter( inOperator ) ); +} + +template +struct PvdAllPropertyVisitor +{ + TOperator mOperator; + PvdAllPropertyVisitor( TOperator op ) : mOperator( op ) {} + template + bool operator()( const TObjectType* ) { visitInstancePvdProperties( mOperator ); return false; } +}; + + +template +struct PvdAllInfoVisitor +{ + TOperator mOperator; + PvdAllInfoVisitor( TOperator op ) : mOperator( op ) {} + template + void operator()( TInfoType inInfo ) + { + inInfo.template visitType( PvdAllPropertyVisitor( mOperator ) ); + inInfo.visitBases( *this ); + } +}; + + +template +inline void visitAllPvdProperties( TOperator inOperator ) +{ + visitAllProperties( makePvdPropertyFilter( inOperator ) ); +} + + + +template +inline void visitRigidDynamicPerFrameProperties( TOperator inOperator ) +{ + PvdPropertyFilter theFilter( inOperator ); + PxRigidDynamicGeneratedInfo theInfo; + theFilter( theInfo.GlobalPose, 0 ); + theFilter( theInfo.LinearVelocity, 1 ); + theFilter( theInfo.AngularVelocity, 2 ); + theFilter( theInfo.IsSleeping, 3 ); +} + +template +inline void visitArticulationLinkPerFrameProperties( TOperator inOperator ) +{ + PvdPropertyFilter theFilter( inOperator ); + PxArticulationLinkGeneratedInfo theInfo; + theFilter( theInfo.GlobalPose, 0 ); + theFilter( theInfo.LinearVelocity, 1 ); + theFilter( theInfo.AngularVelocity, 2 ); +} + +} + +} + +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h new file mode 100644 index 000000000..f8c538f75 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h @@ -0,0 +1,373 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +PxPhysics_PropertiesStart, +PxPhysics_TolerancesScale, +PxPhysics_TriangleMeshes, +PxPhysics_HeightFields, +PxPhysics_ConvexMeshes, +PxPhysics_BVHStructures, +PxPhysics_Scenes, +PxPhysics_Shapes, +PxPhysics_Materials, +PxPhysics_PropertiesStop, +PxMaterial_PropertiesStart, +PxMaterial_ReferenceCount, +PxMaterial_DynamicFriction, +PxMaterial_StaticFriction, +PxMaterial_Restitution, +PxMaterial_Flags, +PxMaterial_FrictionCombineMode, +PxMaterial_RestitutionCombineMode, +PxMaterial_ConcreteTypeName, +PxMaterial_UserData, +PxMaterial_PropertiesStop, +PxActor_PropertiesStart, +PxActor_Scene, +PxActor_Name, +PxActor_ActorFlags, +PxActor_DominanceGroup, +PxActor_OwnerClient, +PxActor_Aggregate, +PxActor_UserData, +PxActor_PropertiesStop, +PxRigidActor_PropertiesStart, +PxRigidActor_GlobalPose, +PxRigidActor_Shapes, +PxRigidActor_Constraints, +PxRigidActor_PropertiesStop, +PxRigidBody_PropertiesStart, +PxRigidBody_CMassLocalPose, +PxRigidBody_Mass, +PxRigidBody_InvMass, +PxRigidBody_MassSpaceInertiaTensor, +PxRigidBody_MassSpaceInvInertiaTensor, +PxRigidBody_LinearDamping, +PxRigidBody_AngularDamping, +PxRigidBody_LinearVelocity, +PxRigidBody_AngularVelocity, +PxRigidBody_MaxAngularVelocity, +PxRigidBody_MaxLinearVelocity, +PxRigidBody_RigidBodyFlags, +PxRigidBody_MinCCDAdvanceCoefficient, +PxRigidBody_MaxDepenetrationVelocity, +PxRigidBody_MaxContactImpulse, +PxRigidBody_PropertiesStop, +PxRigidDynamic_PropertiesStart, +PxRigidDynamic_IsSleeping, +PxRigidDynamic_SleepThreshold, +PxRigidDynamic_StabilizationThreshold, +PxRigidDynamic_RigidDynamicLockFlags, +PxRigidDynamic_WakeCounter, +PxRigidDynamic_SolverIterationCounts, +PxRigidDynamic_ContactReportThreshold, +PxRigidDynamic_ConcreteTypeName, +PxRigidDynamic_PropertiesStop, +PxRigidStatic_PropertiesStart, +PxRigidStatic_ConcreteTypeName, +PxRigidStatic_PropertiesStop, +PxArticulationLink_PropertiesStart, +PxArticulationLink_InboundJoint, +PxArticulationLink_InboundJointDof, +PxArticulationLink_LinkIndex, +PxArticulationLink_Children, +PxArticulationLink_ConcreteTypeName, +PxArticulationLink_PropertiesStop, +PxArticulationJointBase_PropertiesStart, +PxArticulationJointBase_ParentPose, +PxArticulationJointBase_ChildPose, +PxArticulationJointBase_PropertiesStop, +PxArticulationJoint_PropertiesStart, +PxArticulationJoint_TargetOrientation, +PxArticulationJoint_TargetVelocity, +PxArticulationJoint_DriveType, +PxArticulationJoint_Stiffness, +PxArticulationJoint_Damping, +PxArticulationJoint_InternalCompliance, +PxArticulationJoint_ExternalCompliance, +PxArticulationJoint_SwingLimit, +PxArticulationJoint_TangentialStiffness, +PxArticulationJoint_TangentialDamping, +PxArticulationJoint_SwingLimitContactDistance, +PxArticulationJoint_SwingLimitEnabled, +PxArticulationJoint_TwistLimit, +PxArticulationJoint_TwistLimitEnabled, +PxArticulationJoint_TwistLimitContactDistance, +PxArticulationJoint_ConcreteTypeName, +PxArticulationJoint_PropertiesStop, +PxArticulationBase_PropertiesStart, +PxArticulationBase_Scene, +PxArticulationBase_SolverIterationCounts, +PxArticulationBase_IsSleeping, +PxArticulationBase_SleepThreshold, +PxArticulationBase_StabilizationThreshold, +PxArticulationBase_WakeCounter, +PxArticulationBase_Links, +PxArticulationBase_Name, +PxArticulationBase_Aggregate, +PxArticulationBase_Type, +PxArticulationBase_UserData, +PxArticulationBase_PropertiesStop, +PxArticulation_PropertiesStart, +PxArticulation_MaxProjectionIterations, +PxArticulation_SeparationTolerance, +PxArticulation_InternalDriveIterations, +PxArticulation_ExternalDriveIterations, +PxArticulation_PropertiesStop, +PxAggregate_PropertiesStart, +PxAggregate_MaxNbActors, +PxAggregate_Actors, +PxAggregate_SelfCollision, +PxAggregate_ConcreteTypeName, +PxAggregate_PropertiesStop, +PxConstraint_PropertiesStart, +PxConstraint_Scene, +PxConstraint_Actors, +PxConstraint_Flags, +PxConstraint_IsValid, +PxConstraint_BreakForce, +PxConstraint_MinResponseThreshold, +PxConstraint_ConcreteTypeName, +PxConstraint_PropertiesStop, +PxShape_PropertiesStart, +PxShape_ReferenceCount, +PxShape_GeometryType, +PxShape_Geometry, +PxShape_LocalPose, +PxShape_SimulationFilterData, +PxShape_QueryFilterData, +PxShape_Materials, +PxShape_ContactOffset, +PxShape_RestOffset, +PxShape_TorsionalPatchRadius, +PxShape_MinTorsionalPatchRadius, +PxShape_Flags, +PxShape_IsExclusive, +PxShape_Name, +PxShape_ConcreteTypeName, +PxShape_UserData, +PxShape_PropertiesStop, +PxPruningStructure_PropertiesStart, +PxPruningStructure_RigidActors, +PxPruningStructure_ConcreteTypeName, +PxPruningStructure_PropertiesStop, +PxTolerancesScale_PropertiesStart, +PxTolerancesScale_IsValid, +PxTolerancesScale_Length, +PxTolerancesScale_Speed, +PxTolerancesScale_PropertiesStop, +PxGeometry_PropertiesStart, +PxGeometry_PropertiesStop, +PxBoxGeometry_PropertiesStart, +PxBoxGeometry_HalfExtents, +PxBoxGeometry_PropertiesStop, +PxCapsuleGeometry_PropertiesStart, +PxCapsuleGeometry_Radius, +PxCapsuleGeometry_HalfHeight, +PxCapsuleGeometry_PropertiesStop, +PxMeshScale_PropertiesStart, +PxMeshScale_Scale, +PxMeshScale_Rotation, +PxMeshScale_PropertiesStop, +PxConvexMeshGeometry_PropertiesStart, +PxConvexMeshGeometry_Scale, +PxConvexMeshGeometry_ConvexMesh, +PxConvexMeshGeometry_MeshFlags, +PxConvexMeshGeometry_PropertiesStop, +PxSphereGeometry_PropertiesStart, +PxSphereGeometry_Radius, +PxSphereGeometry_PropertiesStop, +PxPlaneGeometry_PropertiesStart, +PxPlaneGeometry_PropertiesStop, +PxTriangleMeshGeometry_PropertiesStart, +PxTriangleMeshGeometry_Scale, +PxTriangleMeshGeometry_MeshFlags, +PxTriangleMeshGeometry_TriangleMesh, +PxTriangleMeshGeometry_PropertiesStop, +PxHeightFieldGeometry_PropertiesStart, +PxHeightFieldGeometry_HeightField, +PxHeightFieldGeometry_HeightScale, +PxHeightFieldGeometry_RowScale, +PxHeightFieldGeometry_ColumnScale, +PxHeightFieldGeometry_HeightFieldFlags, +PxHeightFieldGeometry_PropertiesStop, +PxHeightFieldDesc_PropertiesStart, +PxHeightFieldDesc_NbRows, +PxHeightFieldDesc_NbColumns, +PxHeightFieldDesc_Format, +PxHeightFieldDesc_Samples, +PxHeightFieldDesc_ConvexEdgeThreshold, +PxHeightFieldDesc_Flags, +PxHeightFieldDesc_PropertiesStop, +PxArticulationJointReducedCoordinate_PropertiesStart, +PxArticulationJointReducedCoordinate_JointType, +PxArticulationJointReducedCoordinate_Motion, +PxArticulationJointReducedCoordinate_FrictionCoefficient, +PxArticulationJointReducedCoordinate_ConcreteTypeName, +PxArticulationJointReducedCoordinate_MaxJointVelocity, +PxArticulationJointReducedCoordinate_PropertiesStop, +PxScene_PropertiesStart, +PxScene_Flags, +PxScene_Limits, +PxScene_Timestamp, +PxScene_Actors, +PxScene_Articulations, +PxScene_Constraints, +PxScene_Aggregates, +PxScene_CpuDispatcher, +PxScene_GpuDispatcher, +PxScene_SimulationEventCallback, +PxScene_ContactModifyCallback, +PxScene_CCDContactModifyCallback, +PxScene_BroadPhaseCallback, +PxScene_FilterShaderDataSize, +PxScene_FilterShader, +PxScene_FilterCallback, +PxScene_Gravity, +PxScene_BounceThresholdVelocity, +PxScene_CCDMaxPasses, +PxScene_FrictionOffsetThreshold, +PxScene_FrictionType, +PxScene_VisualizationCullingBox, +PxScene_StaticStructure, +PxScene_DynamicStructure, +PxScene_DynamicTreeRebuildRateHint, +PxScene_SceneQueryUpdateMode, +PxScene_SceneQueryStaticTimestamp, +PxScene_BroadPhaseType, +PxScene_BroadPhaseRegions, +PxScene_TaskManager, +PxScene_NbContactDataBlocks, +PxScene_MaxNbContactDataBlocksUsed, +PxScene_ContactReportStreamBufferSize, +PxScene_SolverBatchSize, +PxScene_WakeCounterResetValue, +PxScene_UserData, +PxScene_SimulationStatistics, +PxScene_PropertiesStop, +PxSceneLimits_PropertiesStart, +PxSceneLimits_MaxNbActors, +PxSceneLimits_MaxNbBodies, +PxSceneLimits_MaxNbStaticShapes, +PxSceneLimits_MaxNbDynamicShapes, +PxSceneLimits_MaxNbAggregates, +PxSceneLimits_MaxNbConstraints, +PxSceneLimits_MaxNbRegions, +PxSceneLimits_MaxNbBroadPhaseOverlaps, +PxSceneLimits_PropertiesStop, +PxgDynamicsMemoryConfig_PropertiesStart, +PxgDynamicsMemoryConfig_ConstraintBufferCapacity, +PxgDynamicsMemoryConfig_ContactBufferCapacity, +PxgDynamicsMemoryConfig_TempBufferCapacity, +PxgDynamicsMemoryConfig_ContactStreamSize, +PxgDynamicsMemoryConfig_PatchStreamSize, +PxgDynamicsMemoryConfig_ForceStreamCapacity, +PxgDynamicsMemoryConfig_HeapCapacity, +PxgDynamicsMemoryConfig_FoundLostPairsCapacity, +PxgDynamicsMemoryConfig_PropertiesStop, +PxSceneDesc_PropertiesStart, +PxSceneDesc_ToDefault, +PxSceneDesc_Gravity, +PxSceneDesc_SimulationEventCallback, +PxSceneDesc_ContactModifyCallback, +PxSceneDesc_CcdContactModifyCallback, +PxSceneDesc_FilterShaderData, +PxSceneDesc_FilterShaderDataSize, +PxSceneDesc_FilterShader, +PxSceneDesc_FilterCallback, +PxSceneDesc_KineKineFilteringMode, +PxSceneDesc_StaticKineFilteringMode, +PxSceneDesc_BroadPhaseType, +PxSceneDesc_BroadPhaseCallback, +PxSceneDesc_Limits, +PxSceneDesc_FrictionType, +PxSceneDesc_SolverType, +PxSceneDesc_BounceThresholdVelocity, +PxSceneDesc_FrictionOffsetThreshold, +PxSceneDesc_CcdMaxSeparation, +PxSceneDesc_SolverOffsetSlop, +PxSceneDesc_Flags, +PxSceneDesc_CpuDispatcher, +PxSceneDesc_GpuDispatcher, +PxSceneDesc_StaticStructure, +PxSceneDesc_DynamicStructure, +PxSceneDesc_DynamicTreeRebuildRateHint, +PxSceneDesc_SceneQueryUpdateMode, +PxSceneDesc_UserData, +PxSceneDesc_SolverBatchSize, +PxSceneDesc_NbContactDataBlocks, +PxSceneDesc_MaxNbContactDataBlocks, +PxSceneDesc_MaxBiasCoefficient, +PxSceneDesc_ContactReportStreamBufferSize, +PxSceneDesc_CcdMaxPasses, +PxSceneDesc_WakeCounterResetValue, +PxSceneDesc_SanityBounds, +PxSceneDesc_GpuDynamicsConfig, +PxSceneDesc_GpuMaxNumPartitions, +PxSceneDesc_GpuComputeVersion, +PxSceneDesc_PropertiesStop, +PxSimulationStatistics_PropertiesStart, +PxSimulationStatistics_NbActiveConstraints, +PxSimulationStatistics_NbActiveDynamicBodies, +PxSimulationStatistics_NbActiveKinematicBodies, +PxSimulationStatistics_NbStaticBodies, +PxSimulationStatistics_NbDynamicBodies, +PxSimulationStatistics_NbAggregates, +PxSimulationStatistics_NbArticulations, +PxSimulationStatistics_NbAxisSolverConstraints, +PxSimulationStatistics_CompressedContactSize, +PxSimulationStatistics_RequiredContactConstraintMemory, +PxSimulationStatistics_PeakConstraintMemory, +PxSimulationStatistics_NbDiscreteContactPairsTotal, +PxSimulationStatistics_NbDiscreteContactPairsWithCacheHits, +PxSimulationStatistics_NbDiscreteContactPairsWithContacts, +PxSimulationStatistics_NbNewPairs, +PxSimulationStatistics_NbLostPairs, +PxSimulationStatistics_NbNewTouches, +PxSimulationStatistics_NbLostTouches, +PxSimulationStatistics_NbPartitions, +PxSimulationStatistics_NbBroadPhaseAdds, +PxSimulationStatistics_NbBroadPhaseRemoves, +PxSimulationStatistics_NbDiscreteContactPairs, +PxSimulationStatistics_NbModifiedContactPairs, +PxSimulationStatistics_NbCCDPairs, +PxSimulationStatistics_NbTriggerPairs, +PxSimulationStatistics_NbShapes, +PxSimulationStatistics_PropertiesStop, + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h new file mode 100644 index 000000000..2f8fd805a --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h @@ -0,0 +1,2888 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +#define PX_PROPERTY_INFO_NAME PxPropertyInfoName + static PxU32ToName g_physx__PxShapeFlag__EnumConversion[] = { + { "eSIMULATION_SHAPE", static_cast( physx::PxShapeFlag::eSIMULATION_SHAPE ) }, + { "eSCENE_QUERY_SHAPE", static_cast( physx::PxShapeFlag::eSCENE_QUERY_SHAPE ) }, + { "eTRIGGER_SHAPE", static_cast( physx::PxShapeFlag::eTRIGGER_SHAPE ) }, + { "eVISUALIZATION", static_cast( physx::PxShapeFlag::eVISUALIZATION ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxShapeFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxShapeFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxPhysics; + struct PxPhysicsGeneratedValues + { + PxTolerancesScale TolerancesScale; + PX_PHYSX_CORE_API PxPhysicsGeneratedValues( const PxPhysics* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPhysics, TolerancesScale, PxPhysicsGeneratedValues) + struct PxPhysicsGeneratedInfo + + { + static const char* getClassName() { return "PxPhysics"; } + PxReadOnlyPropertyInfo TolerancesScale; + PxFactoryCollectionPropertyInfo TriangleMeshes; + PxFactoryCollectionPropertyInfo HeightFields; + PxFactoryCollectionPropertyInfo ConvexMeshes; + PxFactoryCollectionPropertyInfo BVHStructures; + PxFactoryCollectionPropertyInfo Scenes; + PxReadOnlyCollectionPropertyInfo Shapes; + PxReadOnlyCollectionPropertyInfo Materials; + + PX_PHYSX_CORE_API PxPhysicsGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 8; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( TolerancesScale, inStartIndex + 0 );; + inOperator( TriangleMeshes, inStartIndex + 1 );; + inOperator( HeightFields, inStartIndex + 2 );; + inOperator( ConvexMeshes, inStartIndex + 3 );; + inOperator( BVHStructures, inStartIndex + 4 );; + inOperator( Scenes, inStartIndex + 5 );; + inOperator( Shapes, inStartIndex + 6 );; + inOperator( Materials, inStartIndex + 7 );; + return 8 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxPhysicsGeneratedInfo Info; + const PxPhysicsGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxMaterialFlag__EnumConversion[] = { + { "eDISABLE_FRICTION", static_cast( physx::PxMaterialFlag::eDISABLE_FRICTION ) }, + { "eDISABLE_STRONG_FRICTION", static_cast( physx::PxMaterialFlag::eDISABLE_STRONG_FRICTION ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxMaterialFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxMaterialFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxCombineMode__EnumConversion[] = { + { "eAVERAGE", static_cast( physx::PxCombineMode::eAVERAGE ) }, + { "eMIN", static_cast( physx::PxCombineMode::eMIN ) }, + { "eMULTIPLY", static_cast( physx::PxCombineMode::eMULTIPLY ) }, + { "eMAX", static_cast( physx::PxCombineMode::eMAX ) }, + { "eN_VALUES", static_cast( physx::PxCombineMode::eN_VALUES ) }, + { "ePAD_32", static_cast( physx::PxCombineMode::ePAD_32 ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxCombineMode::Enum > { PxEnumTraits() : NameConversion( g_physx__PxCombineMode__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxMaterial; + struct PxMaterialGeneratedValues + { + PxU32 ReferenceCount; + PxReal DynamicFriction; + PxReal StaticFriction; + PxReal Restitution; + PxMaterialFlags Flags; + PxCombineMode::Enum FrictionCombineMode; + PxCombineMode::Enum RestitutionCombineMode; + const char * ConcreteTypeName; + void * UserData; + PX_PHYSX_CORE_API PxMaterialGeneratedValues( const PxMaterial* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, ReferenceCount, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, DynamicFriction, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, StaticFriction, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, Restitution, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, Flags, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, FrictionCombineMode, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, RestitutionCombineMode, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, ConcreteTypeName, PxMaterialGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMaterial, UserData, PxMaterialGeneratedValues) + struct PxMaterialGeneratedInfo + + { + static const char* getClassName() { return "PxMaterial"; } + PxReadOnlyPropertyInfo ReferenceCount; + PxPropertyInfo DynamicFriction; + PxPropertyInfo StaticFriction; + PxPropertyInfo Restitution; + PxPropertyInfo Flags; + PxPropertyInfo FrictionCombineMode; + PxPropertyInfo RestitutionCombineMode; + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo UserData; + + PX_PHYSX_CORE_API PxMaterialGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 9; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ReferenceCount, inStartIndex + 0 );; + inOperator( DynamicFriction, inStartIndex + 1 );; + inOperator( StaticFriction, inStartIndex + 2 );; + inOperator( Restitution, inStartIndex + 3 );; + inOperator( Flags, inStartIndex + 4 );; + inOperator( FrictionCombineMode, inStartIndex + 5 );; + inOperator( RestitutionCombineMode, inStartIndex + 6 );; + inOperator( ConcreteTypeName, inStartIndex + 7 );; + inOperator( UserData, inStartIndex + 8 );; + return 9 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxMaterialGeneratedInfo Info; + const PxMaterialGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxActorType__EnumConversion[] = { + { "eRIGID_STATIC", static_cast( physx::PxActorType::eRIGID_STATIC ) }, + { "eRIGID_DYNAMIC", static_cast( physx::PxActorType::eRIGID_DYNAMIC ) }, + { "eARTICULATION_LINK", static_cast( physx::PxActorType::eARTICULATION_LINK ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxActorType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxActorType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxActorFlag__EnumConversion[] = { + { "eVISUALIZATION", static_cast( physx::PxActorFlag::eVISUALIZATION ) }, + { "eDISABLE_GRAVITY", static_cast( physx::PxActorFlag::eDISABLE_GRAVITY ) }, + { "eSEND_SLEEP_NOTIFIES", static_cast( physx::PxActorFlag::eSEND_SLEEP_NOTIFIES ) }, + { "eDISABLE_SIMULATION", static_cast( physx::PxActorFlag::eDISABLE_SIMULATION ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxActorFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxActorFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxActor; + struct PxActorGeneratedValues + { + PxScene * Scene; + const char * Name; + PxActorFlags ActorFlags; + PxDominanceGroup DominanceGroup; + PxClientID OwnerClient; + PxAggregate * Aggregate; + void * UserData; + PX_PHYSX_CORE_API PxActorGeneratedValues( const PxActor* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, Scene, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, Name, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, ActorFlags, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, DominanceGroup, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, OwnerClient, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, Aggregate, PxActorGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxActor, UserData, PxActorGeneratedValues) + struct PxActorGeneratedInfo + + { + static const char* getClassName() { return "PxActor"; } + PxReadOnlyPropertyInfo Scene; + PxPropertyInfo Name; + PxPropertyInfo ActorFlags; + PxPropertyInfo DominanceGroup; + PxPropertyInfo OwnerClient; + PxReadOnlyPropertyInfo Aggregate; + PxPropertyInfo UserData; + + PX_PHYSX_CORE_API PxActorGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 7; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scene, inStartIndex + 0 );; + inOperator( Name, inStartIndex + 1 );; + inOperator( ActorFlags, inStartIndex + 2 );; + inOperator( DominanceGroup, inStartIndex + 3 );; + inOperator( OwnerClient, inStartIndex + 4 );; + inOperator( Aggregate, inStartIndex + 5 );; + inOperator( UserData, inStartIndex + 6 );; + return 7 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxActorGeneratedInfo Info; + const PxActorGeneratedInfo* getInfo() { return &Info; } + }; + + class PxRigidActor; + struct PxRigidActorGeneratedValues + : PxActorGeneratedValues { + PxTransform GlobalPose; + PX_PHYSX_CORE_API PxRigidActorGeneratedValues( const PxRigidActor* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidActor, GlobalPose, PxRigidActorGeneratedValues) + struct PxRigidActorGeneratedInfo + : PxActorGeneratedInfo + { + static const char* getClassName() { return "PxRigidActor"; } + PxPropertyInfo GlobalPose; + PxRigidActorShapeCollection Shapes; + PxReadOnlyCollectionPropertyInfo Constraints; + + PX_PHYSX_CORE_API PxRigidActorGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxActorGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxActorGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxActorGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( GlobalPose, inStartIndex + 0 );; + inOperator( Shapes, inStartIndex + 1 );; + inOperator( Constraints, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxRigidActorGeneratedInfo Info; + const PxRigidActorGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxForceMode__EnumConversion[] = { + { "eFORCE", static_cast( physx::PxForceMode::eFORCE ) }, + { "eIMPULSE", static_cast( physx::PxForceMode::eIMPULSE ) }, + { "eVELOCITY_CHANGE", static_cast( physx::PxForceMode::eVELOCITY_CHANGE ) }, + { "eACCELERATION", static_cast( physx::PxForceMode::eACCELERATION ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxForceMode::Enum > { PxEnumTraits() : NameConversion( g_physx__PxForceMode__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxRigidBodyFlag__EnumConversion[] = { + { "eKINEMATIC", static_cast( physx::PxRigidBodyFlag::eKINEMATIC ) }, + { "eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES", static_cast( physx::PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES ) }, + { "eENABLE_CCD", static_cast( physx::PxRigidBodyFlag::eENABLE_CCD ) }, + { "eENABLE_CCD_FRICTION", static_cast( physx::PxRigidBodyFlag::eENABLE_CCD_FRICTION ) }, + { "eENABLE_POSE_INTEGRATION_PREVIEW", static_cast( physx::PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW ) }, + { "eENABLE_SPECULATIVE_CCD", static_cast( physx::PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD ) }, + { "eENABLE_CCD_MAX_CONTACT_IMPULSE", static_cast( physx::PxRigidBodyFlag::eENABLE_CCD_MAX_CONTACT_IMPULSE ) }, + { "eRETAIN_ACCELERATIONS", static_cast( physx::PxRigidBodyFlag::eRETAIN_ACCELERATIONS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxRigidBodyFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxRigidBodyFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxRigidBody; + struct PxRigidBodyGeneratedValues + : PxRigidActorGeneratedValues { + PxTransform CMassLocalPose; + PxReal Mass; + PxReal InvMass; + PxVec3 MassSpaceInertiaTensor; + PxVec3 MassSpaceInvInertiaTensor; + PxReal LinearDamping; + PxReal AngularDamping; + PxVec3 LinearVelocity; + PxVec3 AngularVelocity; + PxReal MaxAngularVelocity; + PxReal MaxLinearVelocity; + PxRigidBodyFlags RigidBodyFlags; + PxReal MinCCDAdvanceCoefficient; + PxReal MaxDepenetrationVelocity; + PxReal MaxContactImpulse; + PX_PHYSX_CORE_API PxRigidBodyGeneratedValues( const PxRigidBody* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, CMassLocalPose, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, Mass, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, InvMass, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MassSpaceInertiaTensor, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MassSpaceInvInertiaTensor, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, LinearDamping, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, AngularDamping, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, LinearVelocity, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, AngularVelocity, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MaxAngularVelocity, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MaxLinearVelocity, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, RigidBodyFlags, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MinCCDAdvanceCoefficient, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MaxDepenetrationVelocity, PxRigidBodyGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidBody, MaxContactImpulse, PxRigidBodyGeneratedValues) + struct PxRigidBodyGeneratedInfo + : PxRigidActorGeneratedInfo + { + static const char* getClassName() { return "PxRigidBody"; } + PxPropertyInfo CMassLocalPose; + PxPropertyInfo Mass; + PxReadOnlyPropertyInfo InvMass; + PxPropertyInfo MassSpaceInertiaTensor; + PxReadOnlyPropertyInfo MassSpaceInvInertiaTensor; + PxPropertyInfo LinearDamping; + PxPropertyInfo AngularDamping; + PxPropertyInfo LinearVelocity; + PxPropertyInfo AngularVelocity; + PxPropertyInfo MaxAngularVelocity; + PxPropertyInfo MaxLinearVelocity; + PxPropertyInfo RigidBodyFlags; + PxPropertyInfo MinCCDAdvanceCoefficient; + PxPropertyInfo MaxDepenetrationVelocity; + PxPropertyInfo MaxContactImpulse; + + PX_PHYSX_CORE_API PxRigidBodyGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxRigidActorGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxRigidActorGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 15; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxRigidActorGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( CMassLocalPose, inStartIndex + 0 );; + inOperator( Mass, inStartIndex + 1 );; + inOperator( InvMass, inStartIndex + 2 );; + inOperator( MassSpaceInertiaTensor, inStartIndex + 3 );; + inOperator( MassSpaceInvInertiaTensor, inStartIndex + 4 );; + inOperator( LinearDamping, inStartIndex + 5 );; + inOperator( AngularDamping, inStartIndex + 6 );; + inOperator( LinearVelocity, inStartIndex + 7 );; + inOperator( AngularVelocity, inStartIndex + 8 );; + inOperator( MaxAngularVelocity, inStartIndex + 9 );; + inOperator( MaxLinearVelocity, inStartIndex + 10 );; + inOperator( RigidBodyFlags, inStartIndex + 11 );; + inOperator( MinCCDAdvanceCoefficient, inStartIndex + 12 );; + inOperator( MaxDepenetrationVelocity, inStartIndex + 13 );; + inOperator( MaxContactImpulse, inStartIndex + 14 );; + return 15 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxRigidBodyGeneratedInfo Info; + const PxRigidBodyGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxRigidDynamicLockFlag__EnumConversion[] = { + { "eLOCK_LINEAR_X", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_X ) }, + { "eLOCK_LINEAR_Y", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_Y ) }, + { "eLOCK_LINEAR_Z", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_Z ) }, + { "eLOCK_ANGULAR_X", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_X ) }, + { "eLOCK_ANGULAR_Y", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y ) }, + { "eLOCK_ANGULAR_Z", static_cast( physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxRigidDynamicLockFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxRigidDynamicLockFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxRigidDynamic; + struct PxRigidDynamicGeneratedValues + : PxRigidBodyGeneratedValues { + _Bool IsSleeping; + PxReal SleepThreshold; + PxReal StabilizationThreshold; + PxRigidDynamicLockFlags RigidDynamicLockFlags; + PxReal WakeCounter; + PxU32 SolverIterationCounts[2]; + PxReal ContactReportThreshold; + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxRigidDynamicGeneratedValues( const PxRigidDynamic* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, IsSleeping, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, SleepThreshold, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, StabilizationThreshold, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, RigidDynamicLockFlags, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, WakeCounter, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, SolverIterationCounts, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, ContactReportThreshold, PxRigidDynamicGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidDynamic, ConcreteTypeName, PxRigidDynamicGeneratedValues) + struct PxRigidDynamicGeneratedInfo + : PxRigidBodyGeneratedInfo + { + static const char* getClassName() { return "PxRigidDynamic"; } + PxReadOnlyPropertyInfo IsSleeping; + PxPropertyInfo SleepThreshold; + PxPropertyInfo StabilizationThreshold; + PxPropertyInfo RigidDynamicLockFlags; + PxPropertyInfo WakeCounter; + PxRangePropertyInfo SolverIterationCounts; + PxPropertyInfo ContactReportThreshold; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxRigidDynamicGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxRigidBodyGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxRigidBodyGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 8; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxRigidBodyGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( IsSleeping, inStartIndex + 0 );; + inOperator( SleepThreshold, inStartIndex + 1 );; + inOperator( StabilizationThreshold, inStartIndex + 2 );; + inOperator( RigidDynamicLockFlags, inStartIndex + 3 );; + inOperator( WakeCounter, inStartIndex + 4 );; + inOperator( SolverIterationCounts, inStartIndex + 5 );; + inOperator( ContactReportThreshold, inStartIndex + 6 );; + inOperator( ConcreteTypeName, inStartIndex + 7 );; + return 8 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxRigidDynamicGeneratedInfo Info; + const PxRigidDynamicGeneratedInfo* getInfo() { return &Info; } + }; + + class PxRigidStatic; + struct PxRigidStaticGeneratedValues + : PxRigidActorGeneratedValues { + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxRigidStaticGeneratedValues( const PxRigidStatic* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRigidStatic, ConcreteTypeName, PxRigidStaticGeneratedValues) + struct PxRigidStaticGeneratedInfo + : PxRigidActorGeneratedInfo + { + static const char* getClassName() { return "PxRigidStatic"; } + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxRigidStaticGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxRigidActorGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxRigidActorGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxRigidActorGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ConcreteTypeName, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxRigidStaticGeneratedInfo Info; + const PxRigidStaticGeneratedInfo* getInfo() { return &Info; } + }; + + class PxArticulationLink; + struct PxArticulationLinkGeneratedValues + : PxRigidBodyGeneratedValues { + PxArticulationJointBase * InboundJoint; + PxU32 InboundJointDof; + PxU32 LinkIndex; + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxArticulationLinkGeneratedValues( const PxArticulationLink* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationLink, InboundJoint, PxArticulationLinkGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationLink, InboundJointDof, PxArticulationLinkGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationLink, LinkIndex, PxArticulationLinkGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationLink, ConcreteTypeName, PxArticulationLinkGeneratedValues) + struct PxArticulationLinkGeneratedInfo + : PxRigidBodyGeneratedInfo + { + static const char* getClassName() { return "PxArticulationLink"; } + PxReadOnlyPropertyInfo InboundJoint; + PxReadOnlyPropertyInfo InboundJointDof; + PxReadOnlyPropertyInfo LinkIndex; + PxReadOnlyCollectionPropertyInfo Children; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxArticulationLinkGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxRigidBodyGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxRigidBodyGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxRigidBodyGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( InboundJoint, inStartIndex + 0 );; + inOperator( InboundJointDof, inStartIndex + 1 );; + inOperator( LinkIndex, inStartIndex + 2 );; + inOperator( Children, inStartIndex + 3 );; + inOperator( ConcreteTypeName, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationLinkGeneratedInfo Info; + const PxArticulationLinkGeneratedInfo* getInfo() { return &Info; } + }; + + class PxArticulationJointBase; + struct PxArticulationJointBaseGeneratedValues + { + PxTransform ParentPose; + PxTransform ChildPose; + PX_PHYSX_CORE_API PxArticulationJointBaseGeneratedValues( const PxArticulationJointBase* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointBase, ParentPose, PxArticulationJointBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointBase, ChildPose, PxArticulationJointBaseGeneratedValues) + struct PxArticulationJointBaseGeneratedInfo + + { + static const char* getClassName() { return "PxArticulationJointBase"; } + PxPropertyInfo ParentPose; + PxPropertyInfo ChildPose; + + PX_PHYSX_CORE_API PxArticulationJointBaseGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ParentPose, inStartIndex + 0 );; + inOperator( ChildPose, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationJointBaseGeneratedInfo Info; + const PxArticulationJointBaseGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxArticulationJointDriveType__EnumConversion[] = { + { "eTARGET", static_cast( physx::PxArticulationJointDriveType::eTARGET ) }, + { "eERROR", static_cast( physx::PxArticulationJointDriveType::eERROR ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxArticulationJointDriveType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxArticulationJointDriveType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxArticulationJoint; + struct PxArticulationJointGeneratedValues + : PxArticulationJointBaseGeneratedValues { + PxQuat TargetOrientation; + PxVec3 TargetVelocity; + PxArticulationJointDriveType::Enum DriveType; + PxReal Stiffness; + PxReal Damping; + PxReal InternalCompliance; + PxReal ExternalCompliance; + PxReal SwingLimit[2]; + PxReal TangentialStiffness; + PxReal TangentialDamping; + PxReal SwingLimitContactDistance; + _Bool SwingLimitEnabled; + PxReal TwistLimit[2]; + _Bool TwistLimitEnabled; + PxReal TwistLimitContactDistance; + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxArticulationJointGeneratedValues( const PxArticulationJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TargetOrientation, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TargetVelocity, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, DriveType, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, Stiffness, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, Damping, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, InternalCompliance, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, ExternalCompliance, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, SwingLimit, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TangentialStiffness, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TangentialDamping, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, SwingLimitContactDistance, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, SwingLimitEnabled, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TwistLimit, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TwistLimitEnabled, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, TwistLimitContactDistance, PxArticulationJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJoint, ConcreteTypeName, PxArticulationJointGeneratedValues) + struct PxArticulationJointGeneratedInfo + : PxArticulationJointBaseGeneratedInfo + { + static const char* getClassName() { return "PxArticulationJoint"; } + PxPropertyInfo TargetOrientation; + PxPropertyInfo TargetVelocity; + PxPropertyInfo DriveType; + PxPropertyInfo Stiffness; + PxPropertyInfo Damping; + PxPropertyInfo InternalCompliance; + PxPropertyInfo ExternalCompliance; + PxRangePropertyInfo SwingLimit; + PxPropertyInfo TangentialStiffness; + PxPropertyInfo TangentialDamping; + PxPropertyInfo SwingLimitContactDistance; + PxPropertyInfo SwingLimitEnabled; + PxRangePropertyInfo TwistLimit; + PxPropertyInfo TwistLimitEnabled; + PxPropertyInfo TwistLimitContactDistance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxArticulationJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxArticulationJointBaseGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxArticulationJointBaseGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 16; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxArticulationJointBaseGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( TargetOrientation, inStartIndex + 0 );; + inOperator( TargetVelocity, inStartIndex + 1 );; + inOperator( DriveType, inStartIndex + 2 );; + inOperator( Stiffness, inStartIndex + 3 );; + inOperator( Damping, inStartIndex + 4 );; + inOperator( InternalCompliance, inStartIndex + 5 );; + inOperator( ExternalCompliance, inStartIndex + 6 );; + inOperator( SwingLimit, inStartIndex + 7 );; + inOperator( TangentialStiffness, inStartIndex + 8 );; + inOperator( TangentialDamping, inStartIndex + 9 );; + inOperator( SwingLimitContactDistance, inStartIndex + 10 );; + inOperator( SwingLimitEnabled, inStartIndex + 11 );; + inOperator( TwistLimit, inStartIndex + 12 );; + inOperator( TwistLimitEnabled, inStartIndex + 13 );; + inOperator( TwistLimitContactDistance, inStartIndex + 14 );; + inOperator( ConcreteTypeName, inStartIndex + 15 );; + return 16 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationJointGeneratedInfo Info; + const PxArticulationJointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxArticulationBase__EnumConversion[] = { + { "eReducedCoordinate", static_cast( physx::PxArticulationBase::eReducedCoordinate ) }, + { "eMaximumCoordinate", static_cast( physx::PxArticulationBase::eMaximumCoordinate ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxArticulationBase::Enum > { PxEnumTraits() : NameConversion( g_physx__PxArticulationBase__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxArticulationBase; + struct PxArticulationBaseGeneratedValues + { + PxScene * Scene; + PxU32 SolverIterationCounts[2]; + _Bool IsSleeping; + PxReal SleepThreshold; + PxReal StabilizationThreshold; + PxReal WakeCounter; + const char * Name; + PxAggregate * Aggregate; + PxArticulationBase::Enum Type; + void * UserData; + PX_PHYSX_CORE_API PxArticulationBaseGeneratedValues( const PxArticulationBase* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, Scene, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, SolverIterationCounts, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, IsSleeping, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, SleepThreshold, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, StabilizationThreshold, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, WakeCounter, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, Name, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, Aggregate, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, Type, PxArticulationBaseGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationBase, UserData, PxArticulationBaseGeneratedValues) + struct PxArticulationBaseGeneratedInfo + + { + static const char* getClassName() { return "PxArticulationBase"; } + PxReadOnlyPropertyInfo Scene; + PxRangePropertyInfo SolverIterationCounts; + PxReadOnlyPropertyInfo IsSleeping; + PxPropertyInfo SleepThreshold; + PxPropertyInfo StabilizationThreshold; + PxPropertyInfo WakeCounter; + PxArticulationLinkCollectionProp Links; + PxPropertyInfo Name; + PxReadOnlyPropertyInfo Aggregate; + PxReadOnlyPropertyInfo Type; + PxPropertyInfo UserData; + + PX_PHYSX_CORE_API PxArticulationBaseGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 11; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scene, inStartIndex + 0 );; + inOperator( SolverIterationCounts, inStartIndex + 1 );; + inOperator( IsSleeping, inStartIndex + 2 );; + inOperator( SleepThreshold, inStartIndex + 3 );; + inOperator( StabilizationThreshold, inStartIndex + 4 );; + inOperator( WakeCounter, inStartIndex + 5 );; + inOperator( Links, inStartIndex + 6 );; + inOperator( Name, inStartIndex + 7 );; + inOperator( Aggregate, inStartIndex + 8 );; + inOperator( Type, inStartIndex + 9 );; + inOperator( UserData, inStartIndex + 10 );; + return 11 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationBaseGeneratedInfo Info; + const PxArticulationBaseGeneratedInfo* getInfo() { return &Info; } + }; + + class PxArticulation; + struct PxArticulationGeneratedValues + : PxArticulationBaseGeneratedValues { + PxU32 MaxProjectionIterations; + PxReal SeparationTolerance; + PxU32 InternalDriveIterations; + PxU32 ExternalDriveIterations; + PX_PHYSX_CORE_API PxArticulationGeneratedValues( const PxArticulation* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulation, MaxProjectionIterations, PxArticulationGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulation, SeparationTolerance, PxArticulationGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulation, InternalDriveIterations, PxArticulationGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulation, ExternalDriveIterations, PxArticulationGeneratedValues) + struct PxArticulationGeneratedInfo + : PxArticulationBaseGeneratedInfo + { + static const char* getClassName() { return "PxArticulation"; } + PxPropertyInfo MaxProjectionIterations; + PxPropertyInfo SeparationTolerance; + PxPropertyInfo InternalDriveIterations; + PxPropertyInfo ExternalDriveIterations; + + PX_PHYSX_CORE_API PxArticulationGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxArticulationBaseGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxArticulationBaseGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxArticulationBaseGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MaxProjectionIterations, inStartIndex + 0 );; + inOperator( SeparationTolerance, inStartIndex + 1 );; + inOperator( InternalDriveIterations, inStartIndex + 2 );; + inOperator( ExternalDriveIterations, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationGeneratedInfo Info; + const PxArticulationGeneratedInfo* getInfo() { return &Info; } + }; + + class PxAggregate; + struct PxAggregateGeneratedValues + { + PxU32 MaxNbActors; + _Bool SelfCollision; + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxAggregateGeneratedValues( const PxAggregate* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxAggregate, MaxNbActors, PxAggregateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxAggregate, SelfCollision, PxAggregateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxAggregate, ConcreteTypeName, PxAggregateGeneratedValues) + struct PxAggregateGeneratedInfo + + { + static const char* getClassName() { return "PxAggregate"; } + PxReadOnlyPropertyInfo MaxNbActors; + PxReadOnlyCollectionPropertyInfo Actors; + PxReadOnlyPropertyInfo SelfCollision; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxAggregateGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MaxNbActors, inStartIndex + 0 );; + inOperator( Actors, inStartIndex + 1 );; + inOperator( SelfCollision, inStartIndex + 2 );; + inOperator( ConcreteTypeName, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxAggregateGeneratedInfo Info; + const PxAggregateGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxConstraintFlag__EnumConversion[] = { + { "eBROKEN", static_cast( physx::PxConstraintFlag::eBROKEN ) }, + { "ePROJECT_TO_ACTOR0", static_cast( physx::PxConstraintFlag::ePROJECT_TO_ACTOR0 ) }, + { "ePROJECT_TO_ACTOR1", static_cast( physx::PxConstraintFlag::ePROJECT_TO_ACTOR1 ) }, + { "ePROJECTION", static_cast( physx::PxConstraintFlag::ePROJECTION ) }, + { "eCOLLISION_ENABLED", static_cast( physx::PxConstraintFlag::eCOLLISION_ENABLED ) }, + { "eVISUALIZATION", static_cast( physx::PxConstraintFlag::eVISUALIZATION ) }, + { "eDRIVE_LIMITS_ARE_FORCES", static_cast( physx::PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES ) }, + { "eIMPROVED_SLERP", static_cast( physx::PxConstraintFlag::eIMPROVED_SLERP ) }, + { "eDISABLE_PREPROCESSING", static_cast( physx::PxConstraintFlag::eDISABLE_PREPROCESSING ) }, + { "eENABLE_EXTENDED_LIMITS", static_cast( physx::PxConstraintFlag::eENABLE_EXTENDED_LIMITS ) }, + { "eGPU_COMPATIBLE", static_cast( physx::PxConstraintFlag::eGPU_COMPATIBLE ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxConstraintFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxConstraintFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxConstraint; + struct PxConstraintGeneratedValues + { + PxScene * Scene; + PxRigidActor * Actors[2]; + PxConstraintFlags Flags; + _Bool IsValid; + PxReal BreakForce[2]; + PxReal MinResponseThreshold; + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxConstraintGeneratedValues( const PxConstraint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, Scene, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, Actors, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, Flags, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, IsValid, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, BreakForce, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, MinResponseThreshold, PxConstraintGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConstraint, ConcreteTypeName, PxConstraintGeneratedValues) + struct PxConstraintGeneratedInfo + + { + static const char* getClassName() { return "PxConstraint"; } + PxReadOnlyPropertyInfo Scene; + PxRangePropertyInfo Actors; + PxPropertyInfo Flags; + PxReadOnlyPropertyInfo IsValid; + PxRangePropertyInfo BreakForce; + PxPropertyInfo MinResponseThreshold; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxConstraintGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 7; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scene, inStartIndex + 0 );; + inOperator( Actors, inStartIndex + 1 );; + inOperator( Flags, inStartIndex + 2 );; + inOperator( IsValid, inStartIndex + 3 );; + inOperator( BreakForce, inStartIndex + 4 );; + inOperator( MinResponseThreshold, inStartIndex + 5 );; + inOperator( ConcreteTypeName, inStartIndex + 6 );; + return 7 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxConstraintGeneratedInfo Info; + const PxConstraintGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxGeometryType__EnumConversion[] = { + { "eSPHERE", static_cast( physx::PxGeometryType::eSPHERE ) }, + { "ePLANE", static_cast( physx::PxGeometryType::ePLANE ) }, + { "eCAPSULE", static_cast( physx::PxGeometryType::eCAPSULE ) }, + { "eBOX", static_cast( physx::PxGeometryType::eBOX ) }, + { "eCONVEXMESH", static_cast( physx::PxGeometryType::eCONVEXMESH ) }, + { "eTRIANGLEMESH", static_cast( physx::PxGeometryType::eTRIANGLEMESH ) }, + { "eHEIGHTFIELD", static_cast( physx::PxGeometryType::eHEIGHTFIELD ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxGeometryType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxGeometryType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxShape; + struct PxShapeGeneratedValues + { + PxU32 ReferenceCount; + PxGeometryType::Enum GeometryType; + PxGeometryHolder Geometry; + PxTransform LocalPose; + PxFilterData SimulationFilterData; + PxFilterData QueryFilterData; + PxReal ContactOffset; + PxReal RestOffset; + PxReal TorsionalPatchRadius; + PxReal MinTorsionalPatchRadius; + PxShapeFlags Flags; + _Bool IsExclusive; + const char * Name; + const char * ConcreteTypeName; + void * UserData; + PX_PHYSX_CORE_API PxShapeGeneratedValues( const PxShape* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, ReferenceCount, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, GeometryType, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, Geometry, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, LocalPose, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, SimulationFilterData, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, QueryFilterData, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, ContactOffset, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, RestOffset, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, TorsionalPatchRadius, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, MinTorsionalPatchRadius, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, Flags, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, IsExclusive, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, Name, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, ConcreteTypeName, PxShapeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxShape, UserData, PxShapeGeneratedValues) + struct PxShapeGeneratedInfo + + { + static const char* getClassName() { return "PxShape"; } + PxReadOnlyPropertyInfo ReferenceCount; + PxReadOnlyPropertyInfo GeometryType; + PxShapeGeometryProperty Geometry; + PxPropertyInfo LocalPose; + PxPropertyInfo SimulationFilterData; + PxPropertyInfo QueryFilterData; + PxShapeMaterialsProperty Materials; + PxPropertyInfo ContactOffset; + PxPropertyInfo RestOffset; + PxPropertyInfo TorsionalPatchRadius; + PxPropertyInfo MinTorsionalPatchRadius; + PxPropertyInfo Flags; + PxReadOnlyPropertyInfo IsExclusive; + PxPropertyInfo Name; + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo UserData; + + PX_PHYSX_CORE_API PxShapeGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 16; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ReferenceCount, inStartIndex + 0 );; + inOperator( GeometryType, inStartIndex + 1 );; + inOperator( Geometry, inStartIndex + 2 );; + inOperator( LocalPose, inStartIndex + 3 );; + inOperator( SimulationFilterData, inStartIndex + 4 );; + inOperator( QueryFilterData, inStartIndex + 5 );; + inOperator( Materials, inStartIndex + 6 );; + inOperator( ContactOffset, inStartIndex + 7 );; + inOperator( RestOffset, inStartIndex + 8 );; + inOperator( TorsionalPatchRadius, inStartIndex + 9 );; + inOperator( MinTorsionalPatchRadius, inStartIndex + 10 );; + inOperator( Flags, inStartIndex + 11 );; + inOperator( IsExclusive, inStartIndex + 12 );; + inOperator( Name, inStartIndex + 13 );; + inOperator( ConcreteTypeName, inStartIndex + 14 );; + inOperator( UserData, inStartIndex + 15 );; + return 16 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxShapeGeneratedInfo Info; + const PxShapeGeneratedInfo* getInfo() { return &Info; } + }; + + class PxPruningStructure; + struct PxPruningStructureGeneratedValues + { + const char * ConcreteTypeName; + PX_PHYSX_CORE_API PxPruningStructureGeneratedValues( const PxPruningStructure* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPruningStructure, ConcreteTypeName, PxPruningStructureGeneratedValues) + struct PxPruningStructureGeneratedInfo + + { + static const char* getClassName() { return "PxPruningStructure"; } + PxReadOnlyCollectionPropertyInfo RigidActors; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PX_PHYSX_CORE_API PxPruningStructureGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( RigidActors, inStartIndex + 0 );; + inOperator( ConcreteTypeName, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxPruningStructureGeneratedInfo Info; + const PxPruningStructureGeneratedInfo* getInfo() { return &Info; } + }; + + class PxTolerancesScale; + struct PxTolerancesScaleGeneratedValues + { + _Bool IsValid; + PxReal Length; + PxReal Speed; + PX_PHYSX_CORE_API PxTolerancesScaleGeneratedValues( const PxTolerancesScale* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTolerancesScale, IsValid, PxTolerancesScaleGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTolerancesScale, Length, PxTolerancesScaleGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTolerancesScale, Speed, PxTolerancesScaleGeneratedValues) + struct PxTolerancesScaleGeneratedInfo + + { + static const char* getClassName() { return "PxTolerancesScale"; } + PxReadOnlyPropertyInfo IsValid; + PxPropertyInfo Length; + PxPropertyInfo Speed; + + PX_PHYSX_CORE_API PxTolerancesScaleGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( IsValid, inStartIndex + 0 );; + inOperator( Length, inStartIndex + 1 );; + inOperator( Speed, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxTolerancesScaleGeneratedInfo Info; + const PxTolerancesScaleGeneratedInfo* getInfo() { return &Info; } + }; + + class PxGeometry; + struct PxGeometryGeneratedValues + { + PX_PHYSX_CORE_API PxGeometryGeneratedValues( const PxGeometry* inSource ); + }; + struct PxGeometryGeneratedInfo + + { + static const char* getClassName() { return "PxGeometry"; } + + PX_PHYSX_CORE_API PxGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 0; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return 0 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxGeometryGeneratedInfo Info; + const PxGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxBoxGeometry; + struct PxBoxGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxVec3 HalfExtents; + PX_PHYSX_CORE_API PxBoxGeometryGeneratedValues( const PxBoxGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxBoxGeometry, HalfExtents, PxBoxGeometryGeneratedValues) + struct PxBoxGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxBoxGeometry"; } + PxPropertyInfo HalfExtents; + + PX_PHYSX_CORE_API PxBoxGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( HalfExtents, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxBoxGeometryGeneratedInfo Info; + const PxBoxGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxCapsuleGeometry; + struct PxCapsuleGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxReal Radius; + PxReal HalfHeight; + PX_PHYSX_CORE_API PxCapsuleGeometryGeneratedValues( const PxCapsuleGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxCapsuleGeometry, Radius, PxCapsuleGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxCapsuleGeometry, HalfHeight, PxCapsuleGeometryGeneratedValues) + struct PxCapsuleGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxCapsuleGeometry"; } + PxPropertyInfo Radius; + PxPropertyInfo HalfHeight; + + PX_PHYSX_CORE_API PxCapsuleGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Radius, inStartIndex + 0 );; + inOperator( HalfHeight, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxCapsuleGeometryGeneratedInfo Info; + const PxCapsuleGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxMeshScale; + struct PxMeshScaleGeneratedValues + { + PxVec3 Scale; + PxQuat Rotation; + PX_PHYSX_CORE_API PxMeshScaleGeneratedValues( const PxMeshScale* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMeshScale, Scale, PxMeshScaleGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxMeshScale, Rotation, PxMeshScaleGeneratedValues) + struct PxMeshScaleGeneratedInfo + + { + static const char* getClassName() { return "PxMeshScale"; } + PxPropertyInfo Scale; + PxPropertyInfo Rotation; + + PX_PHYSX_CORE_API PxMeshScaleGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scale, inStartIndex + 0 );; + inOperator( Rotation, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxMeshScaleGeneratedInfo Info; + const PxMeshScaleGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxConvexMeshGeometryFlag__EnumConversion[] = { + { "eTIGHT_BOUNDS", static_cast( physx::PxConvexMeshGeometryFlag::eTIGHT_BOUNDS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxConvexMeshGeometryFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxConvexMeshGeometryFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxConvexMeshGeometry; + struct PxConvexMeshGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxMeshScale Scale; + PxConvexMesh * ConvexMesh; + PxConvexMeshGeometryFlags MeshFlags; + PX_PHYSX_CORE_API PxConvexMeshGeometryGeneratedValues( const PxConvexMeshGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConvexMeshGeometry, Scale, PxConvexMeshGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConvexMeshGeometry, ConvexMesh, PxConvexMeshGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxConvexMeshGeometry, MeshFlags, PxConvexMeshGeometryGeneratedValues) + struct PxConvexMeshGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxConvexMeshGeometry"; } + PxPropertyInfo Scale; + PxPropertyInfo ConvexMesh; + PxPropertyInfo MeshFlags; + + PX_PHYSX_CORE_API PxConvexMeshGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scale, inStartIndex + 0 );; + inOperator( ConvexMesh, inStartIndex + 1 );; + inOperator( MeshFlags, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxConvexMeshGeometryGeneratedInfo Info; + const PxConvexMeshGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxSphereGeometry; + struct PxSphereGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxReal Radius; + PX_PHYSX_CORE_API PxSphereGeometryGeneratedValues( const PxSphereGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphereGeometry, Radius, PxSphereGeometryGeneratedValues) + struct PxSphereGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxSphereGeometry"; } + PxPropertyInfo Radius; + + PX_PHYSX_CORE_API PxSphereGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Radius, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSphereGeometryGeneratedInfo Info; + const PxSphereGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxPlaneGeometry; + struct PxPlaneGeometryGeneratedValues + : PxGeometryGeneratedValues { + PX_PHYSX_CORE_API PxPlaneGeometryGeneratedValues( const PxPlaneGeometry* inSource ); + }; + struct PxPlaneGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxPlaneGeometry"; } + + PX_PHYSX_CORE_API PxPlaneGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 0; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return 0 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxPlaneGeometryGeneratedInfo Info; + const PxPlaneGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxMeshGeometryFlag__EnumConversion[] = { + { "eDOUBLE_SIDED", static_cast( physx::PxMeshGeometryFlag::eDOUBLE_SIDED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxMeshGeometryFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxMeshGeometryFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxTriangleMeshGeometry; + struct PxTriangleMeshGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxMeshScale Scale; + PxMeshGeometryFlags MeshFlags; + PxTriangleMesh * TriangleMesh; + PX_PHYSX_CORE_API PxTriangleMeshGeometryGeneratedValues( const PxTriangleMeshGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTriangleMeshGeometry, Scale, PxTriangleMeshGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTriangleMeshGeometry, MeshFlags, PxTriangleMeshGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxTriangleMeshGeometry, TriangleMesh, PxTriangleMeshGeometryGeneratedValues) + struct PxTriangleMeshGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxTriangleMeshGeometry"; } + PxPropertyInfo Scale; + PxPropertyInfo MeshFlags; + PxPropertyInfo TriangleMesh; + + PX_PHYSX_CORE_API PxTriangleMeshGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Scale, inStartIndex + 0 );; + inOperator( MeshFlags, inStartIndex + 1 );; + inOperator( TriangleMesh, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxTriangleMeshGeometryGeneratedInfo Info; + const PxTriangleMeshGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + class PxHeightFieldGeometry; + struct PxHeightFieldGeometryGeneratedValues + : PxGeometryGeneratedValues { + PxHeightField * HeightField; + PxReal HeightScale; + PxReal RowScale; + PxReal ColumnScale; + PxMeshGeometryFlags HeightFieldFlags; + PX_PHYSX_CORE_API PxHeightFieldGeometryGeneratedValues( const PxHeightFieldGeometry* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldGeometry, HeightField, PxHeightFieldGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldGeometry, HeightScale, PxHeightFieldGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldGeometry, RowScale, PxHeightFieldGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldGeometry, ColumnScale, PxHeightFieldGeometryGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldGeometry, HeightFieldFlags, PxHeightFieldGeometryGeneratedValues) + struct PxHeightFieldGeometryGeneratedInfo + : PxGeometryGeneratedInfo + { + static const char* getClassName() { return "PxHeightFieldGeometry"; } + PxPropertyInfo HeightField; + PxPropertyInfo HeightScale; + PxPropertyInfo RowScale; + PxPropertyInfo ColumnScale; + PxPropertyInfo HeightFieldFlags; + + PX_PHYSX_CORE_API PxHeightFieldGeometryGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxGeometryGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxGeometryGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxGeometryGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( HeightField, inStartIndex + 0 );; + inOperator( HeightScale, inStartIndex + 1 );; + inOperator( RowScale, inStartIndex + 2 );; + inOperator( ColumnScale, inStartIndex + 3 );; + inOperator( HeightFieldFlags, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxHeightFieldGeometryGeneratedInfo Info; + const PxHeightFieldGeometryGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxHeightFieldFormat__EnumConversion[] = { + { "eS16_TM", static_cast( physx::PxHeightFieldFormat::eS16_TM ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxHeightFieldFormat::Enum > { PxEnumTraits() : NameConversion( g_physx__PxHeightFieldFormat__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxHeightFieldFlag__EnumConversion[] = { + { "eNO_BOUNDARY_EDGES", static_cast( physx::PxHeightFieldFlag::eNO_BOUNDARY_EDGES ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxHeightFieldFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxHeightFieldFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxHeightFieldDesc; + struct PxHeightFieldDescGeneratedValues + { + PxU32 NbRows; + PxU32 NbColumns; + PxHeightFieldFormat::Enum Format; + PxStridedData Samples; + PxReal ConvexEdgeThreshold; + PxHeightFieldFlags Flags; + PX_PHYSX_CORE_API PxHeightFieldDescGeneratedValues( const PxHeightFieldDesc* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, NbRows, PxHeightFieldDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, NbColumns, PxHeightFieldDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, Format, PxHeightFieldDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, Samples, PxHeightFieldDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, ConvexEdgeThreshold, PxHeightFieldDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxHeightFieldDesc, Flags, PxHeightFieldDescGeneratedValues) + struct PxHeightFieldDescGeneratedInfo + + { + static const char* getClassName() { return "PxHeightFieldDesc"; } + PxPropertyInfo NbRows; + PxPropertyInfo NbColumns; + PxPropertyInfo Format; + PxPropertyInfo Samples; + PxPropertyInfo ConvexEdgeThreshold; + PxPropertyInfo Flags; + + PX_PHYSX_CORE_API PxHeightFieldDescGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 6; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( NbRows, inStartIndex + 0 );; + inOperator( NbColumns, inStartIndex + 1 );; + inOperator( Format, inStartIndex + 2 );; + inOperator( Samples, inStartIndex + 3 );; + inOperator( ConvexEdgeThreshold, inStartIndex + 4 );; + inOperator( Flags, inStartIndex + 5 );; + return 6 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxHeightFieldDescGeneratedInfo Info; + const PxHeightFieldDescGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxArticulationJointType__EnumConversion[] = { + { "ePRISMATIC", static_cast( physx::PxArticulationJointType::ePRISMATIC ) }, + { "eREVOLUTE", static_cast( physx::PxArticulationJointType::eREVOLUTE ) }, + { "eSPHERICAL", static_cast( physx::PxArticulationJointType::eSPHERICAL ) }, + { "eFIX", static_cast( physx::PxArticulationJointType::eFIX ) }, + { "eUNDEFINED", static_cast( physx::PxArticulationJointType::eUNDEFINED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxArticulationJointType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxArticulationJointType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxArticulationAxis__EnumConversion[] = { + { "eTWIST", static_cast( physx::PxArticulationAxis::eTWIST ) }, + { "eSWING1", static_cast( physx::PxArticulationAxis::eSWING1 ) }, + { "eSWING2", static_cast( physx::PxArticulationAxis::eSWING2 ) }, + { "eX", static_cast( physx::PxArticulationAxis::eX ) }, + { "eY", static_cast( physx::PxArticulationAxis::eY ) }, + { "eZ", static_cast( physx::PxArticulationAxis::eZ ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxArticulationAxis::Enum > { PxEnumTraits() : NameConversion( g_physx__PxArticulationAxis__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxArticulationMotion__EnumConversion[] = { + { "eLOCKED", static_cast( physx::PxArticulationMotion::eLOCKED ) }, + { "eLIMITED", static_cast( physx::PxArticulationMotion::eLIMITED ) }, + { "eFREE", static_cast( physx::PxArticulationMotion::eFREE ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxArticulationMotion::Enum > { PxEnumTraits() : NameConversion( g_physx__PxArticulationMotion__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxArticulationJointReducedCoordinate; + struct PxArticulationJointReducedCoordinateGeneratedValues + : PxArticulationJointBaseGeneratedValues { + PxArticulationJointType::Enum JointType; + PxArticulationMotion::Enum Motion[physx::PxArticulationAxis::eCOUNT]; + PxReal FrictionCoefficient; + const char * ConcreteTypeName; + PxReal MaxJointVelocity; + PX_PHYSX_CORE_API PxArticulationJointReducedCoordinateGeneratedValues( const PxArticulationJointReducedCoordinate* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointReducedCoordinate, JointType, PxArticulationJointReducedCoordinateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointReducedCoordinate, Motion, PxArticulationJointReducedCoordinateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointReducedCoordinate, FrictionCoefficient, PxArticulationJointReducedCoordinateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointReducedCoordinate, ConcreteTypeName, PxArticulationJointReducedCoordinateGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxArticulationJointReducedCoordinate, MaxJointVelocity, PxArticulationJointReducedCoordinateGeneratedValues) + struct PxArticulationJointReducedCoordinateGeneratedInfo + : PxArticulationJointBaseGeneratedInfo + { + static const char* getClassName() { return "PxArticulationJointReducedCoordinate"; } + PxPropertyInfo JointType; + PxIndexedPropertyInfo Motion; + PxPropertyInfo FrictionCoefficient; + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MaxJointVelocity; + + PX_PHYSX_CORE_API PxArticulationJointReducedCoordinateGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxArticulationJointBaseGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxArticulationJointBaseGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxArticulationJointBaseGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( JointType, inStartIndex + 0 );; + inOperator( Motion, inStartIndex + 1 );; + inOperator( FrictionCoefficient, inStartIndex + 2 );; + inOperator( ConcreteTypeName, inStartIndex + 3 );; + inOperator( MaxJointVelocity, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxArticulationJointReducedCoordinateGeneratedInfo Info; + const PxArticulationJointReducedCoordinateGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxSceneFlag__EnumConversion[] = { + { "eENABLE_ACTIVE_ACTORS", static_cast( physx::PxSceneFlag::eENABLE_ACTIVE_ACTORS ) }, + { "eENABLE_CCD", static_cast( physx::PxSceneFlag::eENABLE_CCD ) }, + { "eDISABLE_CCD_RESWEEP", static_cast( physx::PxSceneFlag::eDISABLE_CCD_RESWEEP ) }, + { "eADAPTIVE_FORCE", static_cast( physx::PxSceneFlag::eADAPTIVE_FORCE ) }, + { "eENABLE_KINEMATIC_STATIC_PAIRS", static_cast( physx::PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS ) }, + { "eENABLE_KINEMATIC_PAIRS", static_cast( physx::PxSceneFlag::eENABLE_KINEMATIC_PAIRS ) }, + { "eENABLE_PCM", static_cast( physx::PxSceneFlag::eENABLE_PCM ) }, + { "eDISABLE_CONTACT_REPORT_BUFFER_RESIZE", static_cast( physx::PxSceneFlag::eDISABLE_CONTACT_REPORT_BUFFER_RESIZE ) }, + { "eDISABLE_CONTACT_CACHE", static_cast( physx::PxSceneFlag::eDISABLE_CONTACT_CACHE ) }, + { "eREQUIRE_RW_LOCK", static_cast( physx::PxSceneFlag::eREQUIRE_RW_LOCK ) }, + { "eENABLE_STABILIZATION", static_cast( physx::PxSceneFlag::eENABLE_STABILIZATION ) }, + { "eENABLE_AVERAGE_POINT", static_cast( physx::PxSceneFlag::eENABLE_AVERAGE_POINT ) }, + { "eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS", static_cast( physx::PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS ) }, + { "eENABLE_GPU_DYNAMICS", static_cast( physx::PxSceneFlag::eENABLE_GPU_DYNAMICS ) }, + { "eENABLE_ENHANCED_DETERMINISM", static_cast( physx::PxSceneFlag::eENABLE_ENHANCED_DETERMINISM ) }, + { "eENABLE_FRICTION_EVERY_ITERATION", static_cast( physx::PxSceneFlag::eENABLE_FRICTION_EVERY_ITERATION ) }, + { "eMUTABLE_FLAGS", static_cast( physx::PxSceneFlag::eMUTABLE_FLAGS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxSceneFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxSceneFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxActorTypeFlag__EnumConversion[] = { + { "eRIGID_STATIC", static_cast( physx::PxActorTypeFlag::eRIGID_STATIC ) }, + { "eRIGID_DYNAMIC", static_cast( physx::PxActorTypeFlag::eRIGID_DYNAMIC ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxActorTypeFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxActorTypeFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxFrictionType__EnumConversion[] = { + { "ePATCH", static_cast( physx::PxFrictionType::ePATCH ) }, + { "eONE_DIRECTIONAL", static_cast( physx::PxFrictionType::eONE_DIRECTIONAL ) }, + { "eTWO_DIRECTIONAL", static_cast( physx::PxFrictionType::eTWO_DIRECTIONAL ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxFrictionType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxFrictionType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxVisualizationParameter__EnumConversion[] = { + { "eSCALE", static_cast( physx::PxVisualizationParameter::eSCALE ) }, + { "eWORLD_AXES", static_cast( physx::PxVisualizationParameter::eWORLD_AXES ) }, + { "eBODY_AXES", static_cast( physx::PxVisualizationParameter::eBODY_AXES ) }, + { "eBODY_MASS_AXES", static_cast( physx::PxVisualizationParameter::eBODY_MASS_AXES ) }, + { "eBODY_LIN_VELOCITY", static_cast( physx::PxVisualizationParameter::eBODY_LIN_VELOCITY ) }, + { "eBODY_ANG_VELOCITY", static_cast( physx::PxVisualizationParameter::eBODY_ANG_VELOCITY ) }, + { "eCONTACT_POINT", static_cast( physx::PxVisualizationParameter::eCONTACT_POINT ) }, + { "eCONTACT_NORMAL", static_cast( physx::PxVisualizationParameter::eCONTACT_NORMAL ) }, + { "eCONTACT_ERROR", static_cast( physx::PxVisualizationParameter::eCONTACT_ERROR ) }, + { "eCONTACT_FORCE", static_cast( physx::PxVisualizationParameter::eCONTACT_FORCE ) }, + { "eACTOR_AXES", static_cast( physx::PxVisualizationParameter::eACTOR_AXES ) }, + { "eCOLLISION_AABBS", static_cast( physx::PxVisualizationParameter::eCOLLISION_AABBS ) }, + { "eCOLLISION_SHAPES", static_cast( physx::PxVisualizationParameter::eCOLLISION_SHAPES ) }, + { "eCOLLISION_AXES", static_cast( physx::PxVisualizationParameter::eCOLLISION_AXES ) }, + { "eCOLLISION_COMPOUNDS", static_cast( physx::PxVisualizationParameter::eCOLLISION_COMPOUNDS ) }, + { "eCOLLISION_FNORMALS", static_cast( physx::PxVisualizationParameter::eCOLLISION_FNORMALS ) }, + { "eCOLLISION_EDGES", static_cast( physx::PxVisualizationParameter::eCOLLISION_EDGES ) }, + { "eCOLLISION_STATIC", static_cast( physx::PxVisualizationParameter::eCOLLISION_STATIC ) }, + { "eCOLLISION_DYNAMIC", static_cast( physx::PxVisualizationParameter::eCOLLISION_DYNAMIC ) }, + { "eDEPRECATED_COLLISION_PAIRS", static_cast( physx::PxVisualizationParameter::eDEPRECATED_COLLISION_PAIRS ) }, + { "eJOINT_LOCAL_FRAMES", static_cast( physx::PxVisualizationParameter::eJOINT_LOCAL_FRAMES ) }, + { "eJOINT_LIMITS", static_cast( physx::PxVisualizationParameter::eJOINT_LIMITS ) }, + { "eCULL_BOX", static_cast( physx::PxVisualizationParameter::eCULL_BOX ) }, + { "eMBP_REGIONS", static_cast( physx::PxVisualizationParameter::eMBP_REGIONS ) }, + { "eNUM_VALUES", static_cast( physx::PxVisualizationParameter::eNUM_VALUES ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxVisualizationParameter::Enum > { PxEnumTraits() : NameConversion( g_physx__PxVisualizationParameter__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxPruningStructureType__EnumConversion[] = { + { "eNONE", static_cast( physx::PxPruningStructureType::eNONE ) }, + { "eDYNAMIC_AABB_TREE", static_cast( physx::PxPruningStructureType::eDYNAMIC_AABB_TREE ) }, + { "eSTATIC_AABB_TREE", static_cast( physx::PxPruningStructureType::eSTATIC_AABB_TREE ) }, + { "eLAST", static_cast( physx::PxPruningStructureType::eLAST ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxPruningStructureType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxPruningStructureType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxSceneQueryUpdateMode__EnumConversion[] = { + { "eBUILD_ENABLED_COMMIT_ENABLED", static_cast( physx::PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED ) }, + { "eBUILD_ENABLED_COMMIT_DISABLED", static_cast( physx::PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_DISABLED ) }, + { "eBUILD_DISABLED_COMMIT_DISABLED", static_cast( physx::PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxSceneQueryUpdateMode::Enum > { PxEnumTraits() : NameConversion( g_physx__PxSceneQueryUpdateMode__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxHitFlag__EnumConversion[] = { + { "ePOSITION", static_cast( physx::PxHitFlag::ePOSITION ) }, + { "eNORMAL", static_cast( physx::PxHitFlag::eNORMAL ) }, + { "eUV", static_cast( physx::PxHitFlag::eUV ) }, + { "eASSUME_NO_INITIAL_OVERLAP", static_cast( physx::PxHitFlag::eASSUME_NO_INITIAL_OVERLAP ) }, + { "eMESH_MULTIPLE", static_cast( physx::PxHitFlag::eMESH_MULTIPLE ) }, + { "eMESH_ANY", static_cast( physx::PxHitFlag::eMESH_ANY ) }, + { "eMESH_BOTH_SIDES", static_cast( physx::PxHitFlag::eMESH_BOTH_SIDES ) }, + { "ePRECISE_SWEEP", static_cast( physx::PxHitFlag::ePRECISE_SWEEP ) }, + { "eMTD", static_cast( physx::PxHitFlag::eMTD ) }, + { "eFACE_INDEX", static_cast( physx::PxHitFlag::eFACE_INDEX ) }, + { "eDEFAULT", static_cast( physx::PxHitFlag::eDEFAULT ) }, + { "eMODIFIABLE_FLAGS", static_cast( physx::PxHitFlag::eMODIFIABLE_FLAGS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxHitFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxHitFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxBroadPhaseType__EnumConversion[] = { + { "eSAP", static_cast( physx::PxBroadPhaseType::eSAP ) }, + { "eMBP", static_cast( physx::PxBroadPhaseType::eMBP ) }, + { "eABP", static_cast( physx::PxBroadPhaseType::eABP ) }, + { "eGPU", static_cast( physx::PxBroadPhaseType::eGPU ) }, + { "eLAST", static_cast( physx::PxBroadPhaseType::eLAST ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxBroadPhaseType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxBroadPhaseType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxScene; + struct PxSceneGeneratedValues + { + PxSceneFlags Flags; + PxSceneLimits Limits; + PxU32 Timestamp; + PxCpuDispatcher * CpuDispatcher; + PxGpuDispatcher * GpuDispatcher; + PxSimulationEventCallback * SimulationEventCallback; + PxContactModifyCallback * ContactModifyCallback; + PxCCDContactModifyCallback * CCDContactModifyCallback; + PxBroadPhaseCallback * BroadPhaseCallback; + PxU32 FilterShaderDataSize; + PxSimulationFilterShader FilterShader; + PxSimulationFilterCallback * FilterCallback; + PxVec3 Gravity; + PxReal BounceThresholdVelocity; + PxU32 CCDMaxPasses; + PxReal FrictionOffsetThreshold; + PxFrictionType::Enum FrictionType; + PxBounds3 VisualizationCullingBox; + PxPruningStructureType::Enum StaticStructure; + PxPruningStructureType::Enum DynamicStructure; + PxU32 DynamicTreeRebuildRateHint; + PxSceneQueryUpdateMode::Enum SceneQueryUpdateMode; + PxU32 SceneQueryStaticTimestamp; + PxBroadPhaseType::Enum BroadPhaseType; + PxTaskManager * TaskManager; + PxU32 MaxNbContactDataBlocksUsed; + PxU32 ContactReportStreamBufferSize; + PxU32 SolverBatchSize; + PxReal WakeCounterResetValue; + void * UserData; + PxSimulationStatistics SimulationStatistics; + PX_PHYSX_CORE_API PxSceneGeneratedValues( const PxScene* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, Flags, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, Limits, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, Timestamp, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, CpuDispatcher, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, GpuDispatcher, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, SimulationEventCallback, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, ContactModifyCallback, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, CCDContactModifyCallback, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, BroadPhaseCallback, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, FilterShaderDataSize, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, FilterShader, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, FilterCallback, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, Gravity, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, BounceThresholdVelocity, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, CCDMaxPasses, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, FrictionOffsetThreshold, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, FrictionType, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, VisualizationCullingBox, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, StaticStructure, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, DynamicStructure, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, DynamicTreeRebuildRateHint, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, SceneQueryUpdateMode, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, SceneQueryStaticTimestamp, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, BroadPhaseType, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, TaskManager, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, MaxNbContactDataBlocksUsed, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, ContactReportStreamBufferSize, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, SolverBatchSize, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, WakeCounterResetValue, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, UserData, PxSceneGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxScene, SimulationStatistics, PxSceneGeneratedValues) + struct PxSceneGeneratedInfo + + { + static const char* getClassName() { return "PxScene"; } + PxReadOnlyPropertyInfo Flags; + PxPropertyInfo Limits; + PxReadOnlyPropertyInfo Timestamp; + PxReadOnlyFilteredCollectionPropertyInfo Actors; + PxReadOnlyCollectionPropertyInfo Articulations; + PxReadOnlyCollectionPropertyInfo Constraints; + PxReadOnlyCollectionPropertyInfo Aggregates; + PxReadOnlyPropertyInfo CpuDispatcher; + PxReadOnlyPropertyInfo GpuDispatcher; + PxPropertyInfo SimulationEventCallback; + PxPropertyInfo ContactModifyCallback; + PxPropertyInfo CCDContactModifyCallback; + PxPropertyInfo BroadPhaseCallback; + PxReadOnlyPropertyInfo FilterShaderDataSize; + PxReadOnlyPropertyInfo FilterShader; + PxReadOnlyPropertyInfo FilterCallback; + PxPropertyInfo Gravity; + PxPropertyInfo BounceThresholdVelocity; + PxPropertyInfo CCDMaxPasses; + PxReadOnlyPropertyInfo FrictionOffsetThreshold; + PxPropertyInfo FrictionType; + PxPropertyInfo VisualizationCullingBox; + PxReadOnlyPropertyInfo StaticStructure; + PxReadOnlyPropertyInfo DynamicStructure; + PxPropertyInfo DynamicTreeRebuildRateHint; + PxPropertyInfo SceneQueryUpdateMode; + PxReadOnlyPropertyInfo SceneQueryStaticTimestamp; + PxReadOnlyPropertyInfo BroadPhaseType; + PxReadOnlyCollectionPropertyInfo BroadPhaseRegions; + PxReadOnlyPropertyInfo TaskManager; + PxWriteOnlyPropertyInfo NbContactDataBlocks; + PxReadOnlyPropertyInfo MaxNbContactDataBlocksUsed; + PxReadOnlyPropertyInfo ContactReportStreamBufferSize; + PxPropertyInfo SolverBatchSize; + PxReadOnlyPropertyInfo WakeCounterResetValue; + PxPropertyInfo UserData; + SimulationStatisticsProperty SimulationStatistics; + + PX_PHYSX_CORE_API PxSceneGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 37; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Flags, inStartIndex + 0 );; + inOperator( Limits, inStartIndex + 1 );; + inOperator( Timestamp, inStartIndex + 2 );; + inOperator( Actors, inStartIndex + 3 );; + inOperator( Articulations, inStartIndex + 4 );; + inOperator( Constraints, inStartIndex + 5 );; + inOperator( Aggregates, inStartIndex + 6 );; + inOperator( CpuDispatcher, inStartIndex + 7 );; + inOperator( GpuDispatcher, inStartIndex + 8 );; + inOperator( SimulationEventCallback, inStartIndex + 9 );; + inOperator( ContactModifyCallback, inStartIndex + 10 );; + inOperator( CCDContactModifyCallback, inStartIndex + 11 );; + inOperator( BroadPhaseCallback, inStartIndex + 12 );; + inOperator( FilterShaderDataSize, inStartIndex + 13 );; + inOperator( FilterShader, inStartIndex + 14 );; + inOperator( FilterCallback, inStartIndex + 15 );; + inOperator( Gravity, inStartIndex + 16 );; + inOperator( BounceThresholdVelocity, inStartIndex + 17 );; + inOperator( CCDMaxPasses, inStartIndex + 18 );; + inOperator( FrictionOffsetThreshold, inStartIndex + 19 );; + inOperator( FrictionType, inStartIndex + 20 );; + inOperator( VisualizationCullingBox, inStartIndex + 21 );; + inOperator( StaticStructure, inStartIndex + 22 );; + inOperator( DynamicStructure, inStartIndex + 23 );; + inOperator( DynamicTreeRebuildRateHint, inStartIndex + 24 );; + inOperator( SceneQueryUpdateMode, inStartIndex + 25 );; + inOperator( SceneQueryStaticTimestamp, inStartIndex + 26 );; + inOperator( BroadPhaseType, inStartIndex + 27 );; + inOperator( BroadPhaseRegions, inStartIndex + 28 );; + inOperator( TaskManager, inStartIndex + 29 );; + inOperator( NbContactDataBlocks, inStartIndex + 30 );; + inOperator( MaxNbContactDataBlocksUsed, inStartIndex + 31 );; + inOperator( ContactReportStreamBufferSize, inStartIndex + 32 );; + inOperator( SolverBatchSize, inStartIndex + 33 );; + inOperator( WakeCounterResetValue, inStartIndex + 34 );; + inOperator( UserData, inStartIndex + 35 );; + inOperator( SimulationStatistics, inStartIndex + 36 );; + return 37 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSceneGeneratedInfo Info; + const PxSceneGeneratedInfo* getInfo() { return &Info; } + }; + + class PxSceneLimits; + struct PxSceneLimitsGeneratedValues + { + PxU32 MaxNbActors; + PxU32 MaxNbBodies; + PxU32 MaxNbStaticShapes; + PxU32 MaxNbDynamicShapes; + PxU32 MaxNbAggregates; + PxU32 MaxNbConstraints; + PxU32 MaxNbRegions; + PxU32 MaxNbBroadPhaseOverlaps; + PX_PHYSX_CORE_API PxSceneLimitsGeneratedValues( const PxSceneLimits* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbActors, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbBodies, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbStaticShapes, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbDynamicShapes, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbAggregates, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbConstraints, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbRegions, PxSceneLimitsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneLimits, MaxNbBroadPhaseOverlaps, PxSceneLimitsGeneratedValues) + struct PxSceneLimitsGeneratedInfo + + { + static const char* getClassName() { return "PxSceneLimits"; } + PxPropertyInfo MaxNbActors; + PxPropertyInfo MaxNbBodies; + PxPropertyInfo MaxNbStaticShapes; + PxPropertyInfo MaxNbDynamicShapes; + PxPropertyInfo MaxNbAggregates; + PxPropertyInfo MaxNbConstraints; + PxPropertyInfo MaxNbRegions; + PxPropertyInfo MaxNbBroadPhaseOverlaps; + + PX_PHYSX_CORE_API PxSceneLimitsGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 8; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MaxNbActors, inStartIndex + 0 );; + inOperator( MaxNbBodies, inStartIndex + 1 );; + inOperator( MaxNbStaticShapes, inStartIndex + 2 );; + inOperator( MaxNbDynamicShapes, inStartIndex + 3 );; + inOperator( MaxNbAggregates, inStartIndex + 4 );; + inOperator( MaxNbConstraints, inStartIndex + 5 );; + inOperator( MaxNbRegions, inStartIndex + 6 );; + inOperator( MaxNbBroadPhaseOverlaps, inStartIndex + 7 );; + return 8 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSceneLimitsGeneratedInfo Info; + const PxSceneLimitsGeneratedInfo* getInfo() { return &Info; } + }; + + struct PxgDynamicsMemoryConfig; + struct PxgDynamicsMemoryConfigGeneratedValues + { + PxU32 ConstraintBufferCapacity; + PxU32 ContactBufferCapacity; + PxU32 TempBufferCapacity; + PxU32 ContactStreamSize; + PxU32 PatchStreamSize; + PxU32 ForceStreamCapacity; + PxU32 HeapCapacity; + PxU32 FoundLostPairsCapacity; + PX_PHYSX_CORE_API PxgDynamicsMemoryConfigGeneratedValues( const PxgDynamicsMemoryConfig* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, ConstraintBufferCapacity, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, ContactBufferCapacity, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, TempBufferCapacity, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, ContactStreamSize, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, PatchStreamSize, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, ForceStreamCapacity, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, HeapCapacity, PxgDynamicsMemoryConfigGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxgDynamicsMemoryConfig, FoundLostPairsCapacity, PxgDynamicsMemoryConfigGeneratedValues) + struct PxgDynamicsMemoryConfigGeneratedInfo + + { + static const char* getClassName() { return "PxgDynamicsMemoryConfig"; } + PxPropertyInfo ConstraintBufferCapacity; + PxPropertyInfo ContactBufferCapacity; + PxPropertyInfo TempBufferCapacity; + PxPropertyInfo ContactStreamSize; + PxPropertyInfo PatchStreamSize; + PxPropertyInfo ForceStreamCapacity; + PxPropertyInfo HeapCapacity; + PxPropertyInfo FoundLostPairsCapacity; + + PX_PHYSX_CORE_API PxgDynamicsMemoryConfigGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 8; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ConstraintBufferCapacity, inStartIndex + 0 );; + inOperator( ContactBufferCapacity, inStartIndex + 1 );; + inOperator( TempBufferCapacity, inStartIndex + 2 );; + inOperator( ContactStreamSize, inStartIndex + 3 );; + inOperator( PatchStreamSize, inStartIndex + 4 );; + inOperator( ForceStreamCapacity, inStartIndex + 5 );; + inOperator( HeapCapacity, inStartIndex + 6 );; + inOperator( FoundLostPairsCapacity, inStartIndex + 7 );; + return 8 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxgDynamicsMemoryConfigGeneratedInfo Info; + const PxgDynamicsMemoryConfigGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxPairFilteringMode__EnumConversion[] = { + { "eKEEP", static_cast( physx::PxPairFilteringMode::eKEEP ) }, + { "eSUPPRESS", static_cast( physx::PxPairFilteringMode::eSUPPRESS ) }, + { "eKILL", static_cast( physx::PxPairFilteringMode::eKILL ) }, + { "eDEFAULT", static_cast( physx::PxPairFilteringMode::eDEFAULT ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxPairFilteringMode::Enum > { PxEnumTraits() : NameConversion( g_physx__PxPairFilteringMode__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxSolverType__EnumConversion[] = { + { "ePGS", static_cast( physx::PxSolverType::ePGS ) }, + { "eTGS", static_cast( physx::PxSolverType::eTGS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxSolverType::Enum > { PxEnumTraits() : NameConversion( g_physx__PxSolverType__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxSceneDesc; + struct PxSceneDescGeneratedValues + { + PxVec3 Gravity; + PxSimulationEventCallback * SimulationEventCallback; + PxContactModifyCallback * ContactModifyCallback; + PxCCDContactModifyCallback * CcdContactModifyCallback; + const void * FilterShaderData; + PxU32 FilterShaderDataSize; + PxSimulationFilterShader FilterShader; + PxSimulationFilterCallback * FilterCallback; + PxPairFilteringMode::Enum KineKineFilteringMode; + PxPairFilteringMode::Enum StaticKineFilteringMode; + PxBroadPhaseType::Enum BroadPhaseType; + PxBroadPhaseCallback * BroadPhaseCallback; + PxSceneLimits Limits; + PxFrictionType::Enum FrictionType; + PxSolverType::Enum SolverType; + PxReal BounceThresholdVelocity; + PxReal FrictionOffsetThreshold; + PxReal CcdMaxSeparation; + PxReal SolverOffsetSlop; + PxSceneFlags Flags; + PxCpuDispatcher * CpuDispatcher; + PxGpuDispatcher * GpuDispatcher; + PxPruningStructureType::Enum StaticStructure; + PxPruningStructureType::Enum DynamicStructure; + PxU32 DynamicTreeRebuildRateHint; + PxSceneQueryUpdateMode::Enum SceneQueryUpdateMode; + void * UserData; + PxU32 SolverBatchSize; + PxU32 NbContactDataBlocks; + PxU32 MaxNbContactDataBlocks; + PxReal MaxBiasCoefficient; + PxU32 ContactReportStreamBufferSize; + PxU32 CcdMaxPasses; + PxReal WakeCounterResetValue; + PxBounds3 SanityBounds; + PxgDynamicsMemoryConfig GpuDynamicsConfig; + PxU32 GpuMaxNumPartitions; + PxU32 GpuComputeVersion; + PX_PHYSX_CORE_API PxSceneDescGeneratedValues( const PxSceneDesc* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, Gravity, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SimulationEventCallback, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, ContactModifyCallback, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, CcdContactModifyCallback, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FilterShaderData, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FilterShaderDataSize, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FilterShader, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FilterCallback, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, KineKineFilteringMode, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, StaticKineFilteringMode, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, BroadPhaseType, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, BroadPhaseCallback, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, Limits, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FrictionType, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SolverType, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, BounceThresholdVelocity, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, FrictionOffsetThreshold, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, CcdMaxSeparation, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SolverOffsetSlop, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, Flags, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, CpuDispatcher, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, GpuDispatcher, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, StaticStructure, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, DynamicStructure, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, DynamicTreeRebuildRateHint, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SceneQueryUpdateMode, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, UserData, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SolverBatchSize, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, NbContactDataBlocks, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, MaxNbContactDataBlocks, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, MaxBiasCoefficient, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, ContactReportStreamBufferSize, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, CcdMaxPasses, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, WakeCounterResetValue, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SanityBounds, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, GpuDynamicsConfig, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, GpuMaxNumPartitions, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, GpuComputeVersion, PxSceneDescGeneratedValues) + struct PxSceneDescGeneratedInfo + + { + static const char* getClassName() { return "PxSceneDesc"; } + PxWriteOnlyPropertyInfo ToDefault; + PxPropertyInfo Gravity; + PxPropertyInfo SimulationEventCallback; + PxPropertyInfo ContactModifyCallback; + PxPropertyInfo CcdContactModifyCallback; + PxPropertyInfo FilterShaderData; + PxPropertyInfo FilterShaderDataSize; + PxPropertyInfo FilterShader; + PxPropertyInfo FilterCallback; + PxPropertyInfo KineKineFilteringMode; + PxPropertyInfo StaticKineFilteringMode; + PxPropertyInfo BroadPhaseType; + PxPropertyInfo BroadPhaseCallback; + PxPropertyInfo Limits; + PxPropertyInfo FrictionType; + PxPropertyInfo SolverType; + PxPropertyInfo BounceThresholdVelocity; + PxPropertyInfo FrictionOffsetThreshold; + PxPropertyInfo CcdMaxSeparation; + PxPropertyInfo SolverOffsetSlop; + PxPropertyInfo Flags; + PxPropertyInfo CpuDispatcher; + PxPropertyInfo GpuDispatcher; + PxPropertyInfo StaticStructure; + PxPropertyInfo DynamicStructure; + PxPropertyInfo DynamicTreeRebuildRateHint; + PxPropertyInfo SceneQueryUpdateMode; + PxPropertyInfo UserData; + PxPropertyInfo SolverBatchSize; + PxPropertyInfo NbContactDataBlocks; + PxPropertyInfo MaxNbContactDataBlocks; + PxPropertyInfo MaxBiasCoefficient; + PxPropertyInfo ContactReportStreamBufferSize; + PxPropertyInfo CcdMaxPasses; + PxPropertyInfo WakeCounterResetValue; + PxPropertyInfo SanityBounds; + PxPropertyInfo GpuDynamicsConfig; + PxPropertyInfo GpuMaxNumPartitions; + PxPropertyInfo GpuComputeVersion; + + PX_PHYSX_CORE_API PxSceneDescGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 39; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ToDefault, inStartIndex + 0 );; + inOperator( Gravity, inStartIndex + 1 );; + inOperator( SimulationEventCallback, inStartIndex + 2 );; + inOperator( ContactModifyCallback, inStartIndex + 3 );; + inOperator( CcdContactModifyCallback, inStartIndex + 4 );; + inOperator( FilterShaderData, inStartIndex + 5 );; + inOperator( FilterShaderDataSize, inStartIndex + 6 );; + inOperator( FilterShader, inStartIndex + 7 );; + inOperator( FilterCallback, inStartIndex + 8 );; + inOperator( KineKineFilteringMode, inStartIndex + 9 );; + inOperator( StaticKineFilteringMode, inStartIndex + 10 );; + inOperator( BroadPhaseType, inStartIndex + 11 );; + inOperator( BroadPhaseCallback, inStartIndex + 12 );; + inOperator( Limits, inStartIndex + 13 );; + inOperator( FrictionType, inStartIndex + 14 );; + inOperator( SolverType, inStartIndex + 15 );; + inOperator( BounceThresholdVelocity, inStartIndex + 16 );; + inOperator( FrictionOffsetThreshold, inStartIndex + 17 );; + inOperator( CcdMaxSeparation, inStartIndex + 18 );; + inOperator( SolverOffsetSlop, inStartIndex + 19 );; + inOperator( Flags, inStartIndex + 20 );; + inOperator( CpuDispatcher, inStartIndex + 21 );; + inOperator( GpuDispatcher, inStartIndex + 22 );; + inOperator( StaticStructure, inStartIndex + 23 );; + inOperator( DynamicStructure, inStartIndex + 24 );; + inOperator( DynamicTreeRebuildRateHint, inStartIndex + 25 );; + inOperator( SceneQueryUpdateMode, inStartIndex + 26 );; + inOperator( UserData, inStartIndex + 27 );; + inOperator( SolverBatchSize, inStartIndex + 28 );; + inOperator( NbContactDataBlocks, inStartIndex + 29 );; + inOperator( MaxNbContactDataBlocks, inStartIndex + 30 );; + inOperator( MaxBiasCoefficient, inStartIndex + 31 );; + inOperator( ContactReportStreamBufferSize, inStartIndex + 32 );; + inOperator( CcdMaxPasses, inStartIndex + 33 );; + inOperator( WakeCounterResetValue, inStartIndex + 34 );; + inOperator( SanityBounds, inStartIndex + 35 );; + inOperator( GpuDynamicsConfig, inStartIndex + 36 );; + inOperator( GpuMaxNumPartitions, inStartIndex + 37 );; + inOperator( GpuComputeVersion, inStartIndex + 38 );; + return 39 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSceneDescGeneratedInfo Info; + const PxSceneDescGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxSimulationStatistics__RbPairStatsTypeConversion[] = { + { "eDISCRETE_CONTACT_PAIRS", static_cast( physx::PxSimulationStatistics::eDISCRETE_CONTACT_PAIRS ) }, + { "eCCD_PAIRS", static_cast( physx::PxSimulationStatistics::eCCD_PAIRS ) }, + { "eMODIFIED_CONTACT_PAIRS", static_cast( physx::PxSimulationStatistics::eMODIFIED_CONTACT_PAIRS ) }, + { "eTRIGGER_PAIRS", static_cast( physx::PxSimulationStatistics::eTRIGGER_PAIRS ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxSimulationStatistics::RbPairStatsType > { PxEnumTraits() : NameConversion( g_physx__PxSimulationStatistics__RbPairStatsTypeConversion ) {} const PxU32ToName* NameConversion; }; + class PxSimulationStatistics; + struct PxSimulationStatisticsGeneratedValues + { + PxU32 NbActiveConstraints; + PxU32 NbActiveDynamicBodies; + PxU32 NbActiveKinematicBodies; + PxU32 NbStaticBodies; + PxU32 NbDynamicBodies; + PxU32 NbAggregates; + PxU32 NbArticulations; + PxU32 NbAxisSolverConstraints; + PxU32 CompressedContactSize; + PxU32 RequiredContactConstraintMemory; + PxU32 PeakConstraintMemory; + PxU32 NbDiscreteContactPairsTotal; + PxU32 NbDiscreteContactPairsWithCacheHits; + PxU32 NbDiscreteContactPairsWithContacts; + PxU32 NbNewPairs; + PxU32 NbLostPairs; + PxU32 NbNewTouches; + PxU32 NbLostTouches; + PxU32 NbPartitions; + PxU32 NbBroadPhaseAdds; + PxU32 NbBroadPhaseRemoves; + PxU32 NbDiscreteContactPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 NbModifiedContactPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 NbCCDPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 NbTriggerPairs[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT]; + PxU32 NbShapes[PxGeometryType::eGEOMETRY_COUNT]; + PX_PHYSX_CORE_API PxSimulationStatisticsGeneratedValues( const PxSimulationStatistics* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbActiveConstraints, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbActiveDynamicBodies, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbActiveKinematicBodies, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbStaticBodies, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbDynamicBodies, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbAggregates, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbArticulations, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbAxisSolverConstraints, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, CompressedContactSize, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, RequiredContactConstraintMemory, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, PeakConstraintMemory, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbDiscreteContactPairsTotal, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbDiscreteContactPairsWithCacheHits, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbDiscreteContactPairsWithContacts, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbNewPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbLostPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbNewTouches, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbLostTouches, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbPartitions, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbBroadPhaseAdds, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbBroadPhaseRemoves, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbDiscreteContactPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbModifiedContactPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbCCDPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbTriggerPairs, PxSimulationStatisticsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSimulationStatistics, NbShapes, PxSimulationStatisticsGeneratedValues) + struct PxSimulationStatisticsGeneratedInfo + + { + static const char* getClassName() { return "PxSimulationStatistics"; } + PxPropertyInfo NbActiveConstraints; + PxPropertyInfo NbActiveDynamicBodies; + PxPropertyInfo NbActiveKinematicBodies; + PxPropertyInfo NbStaticBodies; + PxPropertyInfo NbDynamicBodies; + PxPropertyInfo NbAggregates; + PxPropertyInfo NbArticulations; + PxPropertyInfo NbAxisSolverConstraints; + PxPropertyInfo CompressedContactSize; + PxPropertyInfo RequiredContactConstraintMemory; + PxPropertyInfo PeakConstraintMemory; + PxPropertyInfo NbDiscreteContactPairsTotal; + PxPropertyInfo NbDiscreteContactPairsWithCacheHits; + PxPropertyInfo NbDiscreteContactPairsWithContacts; + PxPropertyInfo NbNewPairs; + PxPropertyInfo NbLostPairs; + PxPropertyInfo NbNewTouches; + PxPropertyInfo NbLostTouches; + PxPropertyInfo NbPartitions; + PxPropertyInfo NbBroadPhaseAdds; + PxPropertyInfo NbBroadPhaseRemoves; + NbDiscreteContactPairsProperty NbDiscreteContactPairs; + NbModifiedContactPairsProperty NbModifiedContactPairs; + NbCCDPairsProperty NbCCDPairs; + NbTriggerPairsProperty NbTriggerPairs; + NbShapesProperty NbShapes; + + PX_PHYSX_CORE_API PxSimulationStatisticsGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 26; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( NbActiveConstraints, inStartIndex + 0 );; + inOperator( NbActiveDynamicBodies, inStartIndex + 1 );; + inOperator( NbActiveKinematicBodies, inStartIndex + 2 );; + inOperator( NbStaticBodies, inStartIndex + 3 );; + inOperator( NbDynamicBodies, inStartIndex + 4 );; + inOperator( NbAggregates, inStartIndex + 5 );; + inOperator( NbArticulations, inStartIndex + 6 );; + inOperator( NbAxisSolverConstraints, inStartIndex + 7 );; + inOperator( CompressedContactSize, inStartIndex + 8 );; + inOperator( RequiredContactConstraintMemory, inStartIndex + 9 );; + inOperator( PeakConstraintMemory, inStartIndex + 10 );; + inOperator( NbDiscreteContactPairsTotal, inStartIndex + 11 );; + inOperator( NbDiscreteContactPairsWithCacheHits, inStartIndex + 12 );; + inOperator( NbDiscreteContactPairsWithContacts, inStartIndex + 13 );; + inOperator( NbNewPairs, inStartIndex + 14 );; + inOperator( NbLostPairs, inStartIndex + 15 );; + inOperator( NbNewTouches, inStartIndex + 16 );; + inOperator( NbLostTouches, inStartIndex + 17 );; + inOperator( NbPartitions, inStartIndex + 18 );; + inOperator( NbBroadPhaseAdds, inStartIndex + 19 );; + inOperator( NbBroadPhaseRemoves, inStartIndex + 20 );; + inOperator( NbDiscreteContactPairs, inStartIndex + 21 );; + inOperator( NbModifiedContactPairs, inStartIndex + 22 );; + inOperator( NbCCDPairs, inStartIndex + 23 );; + inOperator( NbTriggerPairs, inStartIndex + 24 );; + inOperator( NbShapes, inStartIndex + 25 );; + return 26 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSimulationStatisticsGeneratedInfo Info; + const PxSimulationStatisticsGeneratedInfo* getInfo() { return &Info; } + }; + + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON +#undef PX_PROPERTY_INFO_NAME diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h new file mode 100644 index 000000000..bdadf8d63 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h @@ -0,0 +1,388 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_METADATACOMPARE_H +#define PX_METADATACOMPARE_H +#include "PxMetaDataObjects.h" +#include "PsInlineArray.h" + +//Implement a basic equality comparison system based on the meta data system. +//if you implement a particular areequal specialized to exactly your type +//before including this file it will be called in preference to the completely +//generic one shown here. + + +//If you don't care about the failure prop name you are welcome to pass in 'null', +template +bool areEqual( const TBaseObjType& lhs, const TBaseObjType& rhs, const char** outFailurePropName ); +//We don't have the ability right now to handle these types. +inline bool areEqual( const PxAggregate&, const PxAggregate& ) { return true; } +inline bool areEqual( const PxSimulationFilterShader&, const PxSimulationFilterShader& ) { return true; } +inline bool areEqual( const PxSimulationFilterCallback&, const PxSimulationFilterCallback& ) { return true; } +inline bool areEqual( const PxConvexMesh&, const PxConvexMesh& ) { return true; } +inline bool areEqual( const PxTriangleMesh&, const PxTriangleMesh& ) { return true; } +inline bool areEqual( const PxBVH33TriangleMesh&, const PxBVH33TriangleMesh& ) { return true; } +inline bool areEqual( const PxBVH34TriangleMesh&, const PxBVH34TriangleMesh& ) { return true; } +inline bool areEqual( const PxHeightField&, const PxHeightField& ) { return true; } +inline bool areEqual( const void* inLhs, const void* inRhs ) { return inLhs == inRhs; } +inline bool areEqual( void* inLhs, void* inRhs ) { return inLhs == inRhs; } + +//Operators are copied, so this object needs to point +//to the important data rather than reference or own it. +template +struct EqualityOp +{ + bool* mVal; + const TBaseObjType* mLhs; + const TBaseObjType* mRhs; + const char** mFailurePropName; + + EqualityOp( bool& inVal, const TBaseObjType& inLhs, const TBaseObjType& inRhs, const char*& inFailurePropName ) + : mVal( &inVal ) + , mLhs( &inLhs ) + , mRhs( &inRhs ) + , mFailurePropName( &inFailurePropName ) + { + } + + bool hasFailed() { return *mVal == false; } + //Ensure failure propagates the result a ways. + void update( bool inResult, const char* inName ) + { + *mVal = *mVal && inResult; + if ( hasFailed() ) + *mFailurePropName = inName; + } + + //ignore any properties pointering back to the scene. + template + void operator()( const PxReadOnlyPropertyInfo & inProp, PxU32 ) {} + + template + void operator()( const PxReadOnlyPropertyInfo & inProp, PxU32 ) {} + + //ignore any properties pointering back to the impl. + template + void operator()(const PxReadOnlyPropertyInfo & inProp, PxU32) {} + + template + void operator()(const PxReadOnlyPropertyInfo & inProp, PxU32) {} + + //ignore all of these properties because they just point back to the 'this' object and cause + //a stack overflow. + + //Children is unnecessary and articulation points back to the source. + void operator()( const PxReadOnlyCollectionPropertyInfo& inProp, PxU32 ) {} + void operator()( const PxReadOnlyCollectionPropertyInfo& inProp, PxU32 ){} + void operator()( const PxReadOnlyCollectionPropertyInfo& inProp, PxU32 ) {} + + template + void operator()( const PxBufferCollectionPropertyInfo & inProp, PxU32 ) + { + + } + + + template + void operator()( const PxReadOnlyPropertyInfo & inProp, PxU32 ) + { + if ( hasFailed() ) return; + TGetPropType lhs( inProp.get( mLhs ) ); + TGetPropType rhs( inProp.get( mRhs ) ); + update( areEqual( lhs, rhs, NULL ), inProp.mName ); + } + + template + void operator()( const PxRangePropertyInfo & inProp, PxU32 ) + { + if ( hasFailed() ) return; + TPropType lhsl, lhsr, rhsl, rhsr; + inProp.get( mLhs, lhsl, lhsr ); + inProp.get( mRhs, rhsl, rhsr ); + update( areEqual( lhsl, rhsl, NULL ), inProp.mName ); + update( areEqual( lhsr, rhsr, NULL ), inProp.mName ); + } + + //Indexed properties where we don't know the range of index types are ignored + template + void compareIndex( const PxIndexedPropertyInfo &, bool ) {} + + template + void compareIndex( const PxIndexedPropertyInfo &inProp, const PxU32ToName* inNames ) + { + for ( const PxU32ToName* theName = inNames; + theName->mName != NULL && !hasFailed(); + ++theName ) + { + TIndexType theIndex( static_cast( theName->mValue ) ); + update( areEqual( inProp.get( mLhs, theIndex ), inProp.get( mRhs, theIndex ), NULL ), inProp.mName ); + } + } + + template + void operator()( const PxIndexedPropertyInfo & inProp, PxU32 ) + { + if ( hasFailed() ) return; + compareIndex( inProp, PxEnumTraits().NameConversion ); + } + + template + void operator()( const PxReadOnlyCollectionPropertyInfo & inProp, PxU32 ) + { + if ( hasFailed() ) return; + physx::shdfnd::InlineArray lhsArray; + physx::shdfnd::InlineArray rhsArray; + PxU32 size = inProp.size( mLhs ); + if ( size != inProp.size( mRhs ) ) + update( false, inProp.mName ); + else + { + lhsArray.resize( size ); + rhsArray.resize( size ); + inProp.get( mLhs, lhsArray.begin(), size ); + inProp.get( mRhs, rhsArray.begin(), size ); + for ( PxU32 idx =0; idx < size && !hasFailed(); ++idx ) + update( areEqual( lhsArray[idx], rhsArray[idx], NULL ), inProp.mName ); + } + } + + //Filtered collections where we can't know the range of filter values are ignored. + template + void compare( const PxReadOnlyFilteredCollectionPropertyInfo< TKey, TObjType, TFilterType, TCollectionType >&, bool ) {} + + template + void compare( const PxReadOnlyFilteredCollectionPropertyInfo< TKey, TObjType, TFilterType, TCollectionType >& inProp, const PxU32ToName* inNames ) + { + //Exaustively compare all items. + physx::shdfnd::InlineArray lhsArray; + physx::shdfnd::InlineArray rhsArray; + for ( const PxU32ToName* theName = inNames; + theName->mName != NULL && !hasFailed(); + ++theName ) + { + TFilterType theFilter( static_cast( theName->mValue ) ); + PxU32 size = inProp.size( mLhs, theFilter ); + if ( size != inProp.size( mRhs, theFilter ) ) + update( false, inProp.mName ); + else + { + lhsArray.resize( size ); + rhsArray.resize( size ); + inProp.get( mLhs, theFilter, lhsArray.begin(), size ); + inProp.get( mRhs, theFilter, rhsArray.begin(), size ); + for ( PxU32 idx =0; idx < size && !hasFailed(); ++idx ) + update( areEqual( lhsArray[idx], rhsArray[idx], NULL ), inProp.mName ); + } + } + } + + template + void operator()( const PxReadOnlyFilteredCollectionPropertyInfo< TKey, TObjType, TFilterType, TCollectionType >& inProp, PxU32 ) + { + if ( hasFailed() ) return; + compare( inProp, PxEnumTraits().NameConversion ); + } + + template + void compareGeometry( const TPropertyType& inProp ) + { + TGeometryType lhs; + TGeometryType rhs; + bool lsuc = inProp.getGeometry( mLhs, lhs ); + bool rsuc = inProp.getGeometry( mRhs, rhs ); + if ( !( lsuc && rsuc ) ) + update( false, inProp.mName ); + else + update( areEqual( lhs, rhs, NULL ), inProp.mName ); + } + + void operator()( const PxShapeGeometryProperty& inProp, PxU32 ) + { + if ( hasFailed() ) return; + PxGeometryType::Enum lhsType( inProp.getGeometryType( mLhs ) ); + PxGeometryType::Enum rhsType( inProp.getGeometryType( mRhs ) ); + if ( lhsType != rhsType ) + update( false, inProp.mName ); + else + { + switch( lhsType ) + { + case PxGeometryType::eSPHERE: compareGeometry(inProp); break; + case PxGeometryType::ePLANE: compareGeometry(inProp); break; + case PxGeometryType::eCAPSULE: compareGeometry(inProp); break; + case PxGeometryType::eBOX: compareGeometry(inProp); break; + case PxGeometryType::eCONVEXMESH: compareGeometry(inProp); break; + case PxGeometryType::eTRIANGLEMESH: compareGeometry(inProp); break; + case PxGeometryType::eHEIGHTFIELD: compareGeometry(inProp); break; + default: PX_ASSERT( false ); break; + } + } + } +}; + +inline bool areEqual( const char* lhs, const char* rhs, const char**, const PxUnknownClassInfo& ) +{ + if ( lhs && rhs ) return strcmp( lhs, rhs ) == 0; + if ( lhs || rhs ) return false; + return true; +} + +inline bool areEqual( PxReal inLhs, PxReal inRhs ) +{ + return PxAbs( inLhs - inRhs ) < 1e-5f; +} + +inline bool areEqual( PxVec3& lhs, PxVec3& rhs ) +{ + return areEqual( lhs.x, rhs.x ) + && areEqual( lhs.y, rhs.y ) + && areEqual( lhs.z, rhs.z ); +} + +inline bool areEqual( const PxVec3& lhs, const PxVec3& rhs ) +{ + return areEqual( lhs.x, rhs.x ) + && areEqual( lhs.y, rhs.y ) + && areEqual( lhs.z, rhs.z ); +} + +inline bool areEqual( const PxVec4& lhs, const PxVec4& rhs ) +{ + return areEqual( lhs.x, rhs.x ) + && areEqual( lhs.y, rhs.y ) + && areEqual( lhs.z, rhs.z ) + && areEqual( lhs.w, rhs.w ); +} + +inline bool areEqual( const PxQuat& lhs, const PxQuat& rhs ) +{ + return areEqual( lhs.x, rhs.x ) + && areEqual( lhs.y, rhs.y ) + && areEqual( lhs.z, rhs.z ) + && areEqual( lhs.w, rhs.w ); +} + + +inline bool areEqual( const PxTransform& lhs, const PxTransform& rhs ) +{ + return areEqual(lhs.p, rhs.p) && areEqual(lhs.q, rhs.q); +} + + +inline bool areEqual( const PxBounds3& inLhs, const PxBounds3& inRhs ) +{ + return areEqual(inLhs.minimum,inRhs.minimum) + && areEqual(inLhs.maximum,inRhs.maximum); +} + +inline bool areEqual( const PxMetaDataPlane& lhs, const PxMetaDataPlane& rhs ) +{ + return areEqual( lhs.normal.x, rhs.normal.x ) + && areEqual( lhs.normal.y, rhs.normal.y ) + && areEqual( lhs.normal.z, rhs.normal.z ) + && areEqual( lhs.distance, rhs.distance ); +} + +template +inline bool areEqual( const TBaseObjType& lhs, const TBaseObjType& rhs ) +{ + return lhs == rhs; +} + +//If we don't know the class type, we must result in == operator +template +inline bool areEqual( const TBaseObjType& lhs, const TBaseObjType& rhs, const char**, const PxUnknownClassInfo& ) +{ + return areEqual( lhs, rhs ); +} + +//If we don't know the class type, we must result in == operator +template +inline bool areEqual( const TBaseObjType& lhs, const TBaseObjType& rhs, const char** outFailurePropName, const TTraitsType& ) +{ + const char* theFailureName = NULL; + bool result = true; + static int i = 0; + ++i; + visitAllProperties( EqualityOp( result, lhs, rhs, theFailureName ) ); + if ( outFailurePropName != NULL && theFailureName ) + *outFailurePropName = theFailureName; + return result; +} + + +template +inline bool areEqualPointerCheck( const TBaseObjType& lhs, const TBaseObjType& rhs, const char** outFailurePropName, int ) +{ + return areEqual( lhs, rhs, outFailurePropName, PxClassInfoTraits().Info ); +} + +inline bool areEqualPointerCheck( const void* lhs, const void* rhs, const char**, bool ) +{ + return lhs == rhs; +} + +inline bool areEqualPointerCheck( const char* lhs, const char* rhs, const char** outFailurePropName, bool ) +{ + bool bRet = true; + if ( lhs && rhs ) bRet = strcmp( lhs, rhs ) == 0; + else if ( lhs || rhs ) bRet = false; + + return bRet; +} + +inline bool areEqualPointerCheck( void* lhs, void* rhs, const char**, bool ) +{ + return lhs == rhs; +} + +template +inline bool areEqualPointerCheck( const TBaseObjType& lhs, const TBaseObjType& rhs, const char** outFailurePropName, bool ) +{ + if ( lhs && rhs ) + return areEqual( *lhs, *rhs, outFailurePropName ); + if ( lhs || rhs ) + return false; + return true; +} + +template < typename Tp > +struct is_pointer { static const int val = 0; }; + +template < typename Tp > +struct is_pointer { static const bool val = true; }; + + +template +inline bool areEqual( const TBaseObjType& lhs, const TBaseObjType& rhs, const char** outFailurePropName ) +{ + return areEqualPointerCheck( lhs, rhs, outFailurePropName, is_pointer::val ); +} + +#endif \ No newline at end of file diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h new file mode 100644 index 000000000..c6190e35f --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h @@ -0,0 +1,37 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_META_DATA_CPP_PREFIX_H +#define PX_META_DATA_CPP_PREFIX_H + +//Header that is only included by the clang-generated cpp files. +//Used to change compilation settings where necessary for only those files. + + +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h new file mode 100644 index 000000000..b4cc65bdf --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h @@ -0,0 +1,662 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_METADATAOBJECTS_H +#define PX_METADATAOBJECTS_H +#include "foundation/PxMemory.h" + +// include the base headers instead of the PxPhysicsAPI.h +//Geometry Library +#include "geometry/PxBoxGeometry.h" +#include "geometry/PxCapsuleGeometry.h" +#include "geometry/PxConvexMesh.h" +#include "geometry/PxConvexMeshGeometry.h" +#include "geometry/PxGeometry.h" +#include "geometry/PxGeometryHelpers.h" +#include "geometry/PxGeometryQuery.h" +#include "geometry/PxHeightField.h" +#include "geometry/PxHeightFieldDesc.h" +#include "geometry/PxHeightFieldFlag.h" +#include "geometry/PxHeightFieldGeometry.h" +#include "geometry/PxHeightFieldSample.h" +#include "geometry/PxMeshQuery.h" +#include "geometry/PxMeshScale.h" +#include "geometry/PxPlaneGeometry.h" +#include "geometry/PxSimpleTriangleMesh.h" +#include "geometry/PxSphereGeometry.h" +#include "geometry/PxTriangle.h" +#include "geometry/PxTriangleMesh.h" +#include "geometry/PxTriangleMeshGeometry.h" + +// PhysX Core SDK +#include "PxActor.h" +#include "PxAggregate.h" +#include "PxArticulation.h" +#include "PxArticulationJoint.h" +#include "PxArticulationReducedCoordinate.h" +#include "PxArticulationJointReducedCoordinate.h" +#include "PxArticulationLink.h" +#include "PxBatchQuery.h" +#include "PxBatchQueryDesc.h" +#include "PxClient.h" +#include "PxConstraint.h" +#include "PxConstraintDesc.h" +#include "PxContact.h" +#include "PxContactModifyCallback.h" +#include "PxDeletionListener.h" +#include "PxFiltering.h" +#include "PxForceMode.h" +#include "PxLockedData.h" +#include "PxMaterial.h" +#include "PxPhysics.h" +#include "PxPhysicsVersion.h" +#include "PxPhysXConfig.h" +#include "PxQueryFiltering.h" +#include "PxQueryReport.h" +#include "PxRigidActor.h" +#include "PxRigidBody.h" +#include "PxRigidDynamic.h" +#include "PxRigidStatic.h" +#include "PxScene.h" +#include "PxSceneDesc.h" +#include "PxSceneLock.h" +#include "PxShape.h" +#include "PxSimulationEventCallback.h" +#include "PxSimulationStatistics.h" +#include "PxVisualizationParameter.h" +#include "PxPruningStructure.h" + + +/** \addtogroup physics +@{ +*/ + +namespace physx +{ + +class PxArticulationLink; +class PxArticulationJoint; +class PxArticulationJointReducedCoordinate; +class PxArticulationJointBase; + +struct PxPropertyInfoName +{ + enum Enum + { + Unnamed = 0, +#include "PxAutoGeneratedMetaDataObjectNames.h" + LastPxPropertyInfoName + }; +}; + +struct PxU32ToName +{ + const char* mName; + PxU32 mValue; +}; + +struct PxPropertyInfoBase +{ + const char* mName; + PxU32 mKey; + PxPropertyInfoBase( const char* n, PxU32 inKey ) + : mName( n ) + , mKey( inKey ) + { + } +}; + +template +struct PxPropertyInfoParameterizedBase : public PxPropertyInfoBase +{ + PxPropertyInfoParameterizedBase( const char* inName ) + : PxPropertyInfoBase( inName, TKey ) {} +}; + +template +struct PxReadOnlyPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef TPropertyType (*TGetterType)( const TObjType* ); + TGetterType mGetter; + PxReadOnlyPropertyInfo( const char* inName, TGetterType inGetter ) + : PxPropertyInfoParameterizedBase( inName ) + , mGetter( inGetter ) {} + TPropertyType get( const TObjType* inObj ) const { return mGetter( inObj ); } +}; + +template +struct PxWriteOnlyPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef void(*TSetterType)( TObjType*, TPropertyType inArg ); + TSetterType mSetter; + PxWriteOnlyPropertyInfo( const char* inName, TSetterType inSetter ) + : PxPropertyInfoParameterizedBase( inName ) + , mSetter( inSetter ) {} + void set( TObjType* inObj, TPropertyType inArg ) const { mSetter( inObj, inArg ); } +}; + + +//Define the property types on the auto-generated objects. +template +struct PxPropertyInfo : public PxReadOnlyPropertyInfo +{ + typedef typename PxReadOnlyPropertyInfo::TGetterType TGetterType; + typedef void(*TSetterType)( TObjType*, TSetPropType inArg ); + TSetterType mSetter; + + PxPropertyInfo( const char* inName, TSetterType inSetter, TGetterType inGetter ) + : PxReadOnlyPropertyInfo( inName, inGetter ) + , mSetter( inSetter ) {} + void set( TObjType* inObj, TSetPropType inArg ) const { mSetter( inObj, inArg ); } +}; + +template +struct PxRangePropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef void (*TSetterType)( TObjType*,TPropertyType,TPropertyType); + typedef void (*TGetterType)( const TObjType*,TPropertyType&,TPropertyType&); + + const char* mArg0Name; + const char* mArg1Name; + + TSetterType mSetter; + TGetterType mGetter; + + PxRangePropertyInfo( const char* name, const char* arg0Name, const char* arg1Name + , TSetterType setter, TGetterType getter ) + : PxPropertyInfoParameterizedBase( name ) + , mArg0Name( arg0Name ) + , mArg1Name( arg1Name ) + , mSetter( setter ) + , mGetter( getter ) + { + } + void set( TObjType* inObj, TPropertyType arg0, TPropertyType arg1 ) const { mSetter( inObj, arg0, arg1 ); } + void get( const TObjType* inObj, TPropertyType& arg0, TPropertyType& arg1 ) const { mGetter( inObj, arg0, arg1 ); } +}; + +template +struct PxIndexedPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef void (*TSetterType)( TObjType*, TIndexType, TPropertyType ); + typedef TPropertyType (*TGetterType)( const TObjType* inObj, TIndexType ); + + TSetterType mSetter; + TGetterType mGetter; + + PxIndexedPropertyInfo( const char* name, TSetterType setter, TGetterType getter ) + : PxPropertyInfoParameterizedBase( name ) + , mSetter( setter ) + , mGetter( getter ) + { + } + void set( TObjType* inObj, TIndexType inIndex, TPropertyType arg ) const { mSetter( inObj, inIndex, arg ); } + TPropertyType get( const TObjType* inObj, TIndexType inIndex ) const { return mGetter( inObj, inIndex ); } +}; + +template +struct PxExtendedIndexedPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef PxU32 (*TNbObjectsMember)( const TObjType* ); + typedef void (*TSetterType)( TObjType*, TIndexType, TPropertyType ); + typedef TPropertyType (*TGetterType)( const TObjType* inObj, TIndexType ); + + TSetterType mSetter; + TGetterType mGetter; + PxU32 mCount; + TNbObjectsMember mNbObjectsMember; + + PxExtendedIndexedPropertyInfo( const char* name, TGetterType getter, TNbObjectsMember inNb, TSetterType setter) + : PxPropertyInfoParameterizedBase( name ) + , mSetter( setter ) + , mGetter( getter ) + , mNbObjectsMember( inNb ) + { + } + + PxU32 size( const TObjType* inObj ) const { return mNbObjectsMember( inObj ); } + void set( TObjType* inObj, TIndexType inIndex, TPropertyType arg ) const { mSetter( inObj, inIndex, arg ); } + TPropertyType get( const TObjType* inObj, TIndexType inIndex ) const { return mGetter( inObj, inIndex ); } +}; + +template +struct PxDualIndexedPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef void (*TSetterType)( TObjType*, TIndex1Type, TIndex2Type, TPropertyType ); + typedef TPropertyType (*TGetterType)( const TObjType* inObj, TIndex1Type, TIndex2Type ); + + TSetterType mSetter; + TGetterType mGetter; + + PxDualIndexedPropertyInfo( const char* name, TSetterType setter, TGetterType getter ) + : PxPropertyInfoParameterizedBase( name ) + , mSetter( setter ) + , mGetter( getter ) + { + } + void set( TObjType* inObj, TIndex1Type inIdx1, TIndex2Type inIdx2, TPropertyType arg ) const { mSetter( inObj, inIdx1, inIdx2, arg ); } + TPropertyType get( const TObjType* inObj, TIndex1Type inIdx1, TIndex2Type inIdx2 ) const { return mGetter( inObj, inIdx1, inIdx2 ); } +}; + +template +struct PxExtendedDualIndexedPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef void (*TSetterType)( TObjType*, TIndex1Type, TIndex2Type, TPropertyType ); + typedef TPropertyType (*TGetterType)( const TObjType* inObj, TIndex1Type, TIndex2Type ); + + TSetterType mSetter; + TGetterType mGetter; + PxU32 mId0Count; + PxU32 mId1Count; + + PxExtendedDualIndexedPropertyInfo( const char* name, TSetterType setter, TGetterType getter, PxU32 id0Count, PxU32 id1Count ) + : PxPropertyInfoParameterizedBase( name ) + , mSetter( setter ) + , mGetter( getter ) + , mId0Count( id0Count ) + , mId1Count( id1Count ) + { + } + + void set( TObjType* inObj, TIndex1Type inIdx1, TIndex2Type inIdx2, TPropertyType arg ) const { mSetter( inObj, inIdx1, inIdx2, arg ); } + TPropertyType get( const TObjType* inObj, TIndex1Type inIdx1, TIndex2Type inIdx2 ) const { return mGetter( inObj, inIdx1, inIdx2 ); } +}; + +template +struct PxBufferCollectionPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef PxU32 (*TNbObjectsMember)( const TObjType* ); + typedef PxU32 (*TGetObjectsMember)( const TObjType*, TCollectionType*, PxU32 ); + typedef void (*TSetObjectsMember)( TObjType*, TCollectionType*, PxU32 ); + + TGetObjectsMember mGetObjectsMember; + TNbObjectsMember mNbObjectsMember; + TSetObjectsMember mSetObjectsMember; + + PxBufferCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb, TSetObjectsMember inSet ) + : PxPropertyInfoParameterizedBase( inName ) + , mGetObjectsMember( inGetter ) + , mNbObjectsMember( inNb ) + , mSetObjectsMember( inSet ) + { + } + PxU32 size( const TObjType* inObj ) const { return mNbObjectsMember( inObj ); } + PxU32 get( const TObjType* inObj, TCollectionType* inBuffer, PxU32 inNumItems ) const { return mGetObjectsMember( inObj, inBuffer, inNumItems ); } + void set( TObjType* inObj, TCollectionType* inBuffer, PxU32 inNumItems ) const { mSetObjectsMember( inObj, inBuffer, inNumItems); } +}; + +template +struct PxFixedSizeLookupTablePropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef PxU32 (*TNbObjectsMember)( const TObjType* ); + typedef PxReal (*TGetXMember)( const TObjType*, PxU32 ); + typedef PxReal (*TGetYMember)( const TObjType*, PxU32 ); + typedef void (*TAddPairMember)( TObjType*, PxReal, PxReal ); + typedef void (*TClearMember)( TObjType* ); + + TGetXMember mGetXMember; + TGetYMember mGetYMember; + TNbObjectsMember mNbObjectsMember; + TAddPairMember mAddPairMember; + TClearMember mClearMember; + + PxFixedSizeLookupTablePropertyInfo( const char* inName, TGetXMember inGetterX, TGetYMember inGetterY, TNbObjectsMember inNb, TAddPairMember inAddPair, TClearMember inClear ) + : PxPropertyInfoParameterizedBase( inName ) + , mGetXMember( inGetterX ) + , mGetYMember( inGetterY ) + , mNbObjectsMember( inNb ) + , mAddPairMember( inAddPair ) + , mClearMember( inClear ) + + { + } + PxU32 size( const TObjType* inObj ) const { return mNbObjectsMember( inObj ); } + PxReal getX( const TObjType* inObj, const PxU32 index ) const { return mGetXMember( inObj, index ); } + PxReal getY( const TObjType* inObj, const PxU32 index ) const { return mGetYMember( inObj, index ); } + void addPair( TObjType* inObj, const PxReal x, const PxReal y ) { mAddPairMember( inObj, x, y ); } + void clear( TObjType* inObj ) { mClearMember( inObj ); } +}; + +template +struct PxReadOnlyCollectionPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef PxU32 (*TNbObjectsMember)( const TObjType* ); + typedef PxU32 (*TGetObjectsMember)( const TObjType*, TCollectionType*, PxU32 ); + + TGetObjectsMember mGetObjectsMember; + TNbObjectsMember mNbObjectsMember; + + PxReadOnlyCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb ) + : PxPropertyInfoParameterizedBase( inName ) + , mGetObjectsMember( inGetter ) + , mNbObjectsMember( inNb ) + { + } + PxU32 size( const TObjType* inObj ) const { return mNbObjectsMember( inObj ); } + PxU32 get( const TObjType* inObj, TCollectionType* inBuffer, PxU32 inBufSize ) const { return mGetObjectsMember( inObj, inBuffer, inBufSize); } +}; + + +template +struct PxReadOnlyFilteredCollectionPropertyInfo : public PxPropertyInfoParameterizedBase +{ + typedef PxU32 (*TNbObjectsMember)( const TObjType*, TFilterType ); + typedef PxU32 (*TGetObjectsMember)( const TObjType*, TFilterType, TCollectionType*, PxU32 ); + + TGetObjectsMember mGetObjectsMember; + TNbObjectsMember mNbObjectsMember; + + PxReadOnlyFilteredCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb ) + : PxPropertyInfoParameterizedBase( inName ) + , mGetObjectsMember( inGetter ) + , mNbObjectsMember( inNb ) + { + } + + PxU32 size( const TObjType* inObj, TFilterType inFilter ) const { return mNbObjectsMember( inObj, inFilter ); } + PxU32 get( const TObjType* inObj, TFilterType inFilter, TCollectionType* inBuffer, PxU32 inBufSize ) const { return mGetObjectsMember( inObj, inFilter, inBuffer, inBufSize); } +}; + +template +struct PxFactoryCollectionPropertyInfo : public PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType > +{ + typedef typename PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >::TGetObjectsMember TGetObjectsMember; + typedef typename PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >::TNbObjectsMember TNbObjectsMember; + typedef TCollectionType (*TCreateMember)( TObjType*, TCreateArg ); + + TCreateMember mCreateMember; + PxFactoryCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb, TCreateMember inMember ) + : PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >( inName, inGetter, inNb ) + , mCreateMember( inMember ) + { + } + TCollectionType create( TObjType* inObj, TCreateArg inArg ) const { return mCreateMember( inObj, inArg ); } +}; + + +template +struct PxCollectionPropertyInfo : public PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType > +{ + typedef typename PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >::TGetObjectsMember TGetObjectsMember; + typedef typename PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >::TNbObjectsMember TNbObjectsMember; + typedef void (*TAddMember)(TObjType*, TCollectionType&); + typedef void (*TRemoveMember)(TObjType*, TCollectionType&); + + TAddMember mAddMember; + TRemoveMember mRemoveMember; + + PxCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb, TAddMember inMember, TRemoveMember inRemoveMember ) + : PxReadOnlyCollectionPropertyInfo< TKey, TObjType, TCollectionType >( inName, inGetter, inNb ) + , mAddMember( inMember ) + , mRemoveMember( inRemoveMember ) + { + } + void add( TObjType* inObj, TCollectionType& inArg ) const { mAddMember(inObj, inArg ); } + void remove( TObjType* inObj, TCollectionType& inArg ) const { mRemoveMember( inObj, inArg ); } +}; + +template +struct PxFilteredCollectionPropertyInfo : public PxReadOnlyFilteredCollectionPropertyInfo +{ + typedef typename PxReadOnlyFilteredCollectionPropertyInfo< TKey, TObjType, TCollectionType, TFilterType >::TGetObjectsMember TGetObjectsMember; + typedef typename PxReadOnlyFilteredCollectionPropertyInfo< TKey, TObjType, TCollectionType, TFilterType >::TNbObjectsMember TNbObjectsMember; + typedef void (*TAddMember)(TObjType*, TCollectionType&); + typedef void (*TRemoveMember)(TObjType*, TCollectionType&); + + TAddMember mAddMember; + TRemoveMember mRemoveMember; + + PxFilteredCollectionPropertyInfo( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb, TAddMember inMember, TRemoveMember inRemoveMember ) + : PxReadOnlyFilteredCollectionPropertyInfo( inName, inGetter, inNb ) + , mAddMember( inMember ) + , mRemoveMember( inRemoveMember ) + { + } + void add( TObjType* inObj, TCollectionType& inArg ) const { mAddMember(inObj, inArg ); } + void remove( TObjType* inObj, TCollectionType& inArg ) const { mRemoveMember( inObj, inArg ); } +}; + +//create a default info class for when we can't match +//the type correctly. +struct PxUnknownClassInfo +{ + static const char* getClassName() { return "__unknown_class"; } + template + TReturnType visitType( TOperator ) + { + return TReturnType(); + } + template + void visitBases( TOperator ) + { + } + template + PxU32 visitBaseProperties( TOperator, PxU32 inStartIndex = 0 ) const + { + return inStartIndex; + } + template + PxU32 visitInstanceProperties( TOperator, PxU32 inStartIndex = 0 ) const + { + return inStartIndex; + } +}; + +template +struct PxClassInfoTraits +{ + PxUnknownClassInfo Info; + static bool getInfo() { return false;} +}; + +//move the bool typedef to the global namespace. +typedef bool _Bool; + + +template +struct PxPropertyToValueStructMemberMap +{ + bool Offset; +}; + + +#define DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( type, prop, valueStruct ) \ + template<> struct PxPropertyToValueStructMemberMap< PxPropertyInfoName::type##_##prop > \ + { \ + PxU32 Offset; \ + PxPropertyToValueStructMemberMap< PxPropertyInfoName::type##_##prop >() : Offset( PX_OFFSET_OF_RT( valueStruct, prop ) ) {} \ + template void visitProp( TOperator inOperator, valueStruct& inStruct ) { inOperator( inStruct.prop ); } \ + }; + + + +struct PxShapeGeometryPropertyHelper +{ + PX_PHYSX_CORE_API PxGeometryType::Enum getGeometryType(const PxShape* inShape) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxBoxGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxSphereGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxCapsuleGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxPlaneGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxConvexMeshGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxTriangleMeshGeometry& geometry) const; + PX_PHYSX_CORE_API bool getGeometry(const PxShape* inShape, PxHeightFieldGeometry& geometry) const; +}; + + +struct PxShapeGeometryProperty : public PxWriteOnlyPropertyInfo< PxPropertyInfoName::PxShape_Geometry, PxShape, const PxGeometry & > + , public PxShapeGeometryPropertyHelper +{ + typedef PxWriteOnlyPropertyInfo< PxPropertyInfoName::PxShape_Geometry, PxShape, const PxGeometry & >::TSetterType TSetterType; + typedef PxGeometryHolder (*TGetterType)( const PxShape* inObj ); + PxShapeGeometryProperty( const char* inName, TSetterType inSetter, TGetterType ) + : PxWriteOnlyPropertyInfo< PxPropertyInfoName::PxShape_Geometry, PxShape, const PxGeometry & >( inName, inSetter ) + { + } +}; + +struct PxShapeMaterialsPropertyHelper +{ + PX_PHYSX_CORE_API void setMaterials(PxShape* inShape, PxMaterial*const* materials, PxU16 materialCount) const; +}; + +struct PxShapeMaterialsProperty : public PxReadOnlyCollectionPropertyInfo + , public PxShapeMaterialsPropertyHelper +{ + typedef PxReadOnlyCollectionPropertyInfo< PxPropertyInfoName::PxShape_Materials, PxShape, PxMaterial* >::TGetObjectsMember TGetObjectsMember; + typedef PxReadOnlyCollectionPropertyInfo< PxPropertyInfoName::PxShape_Materials, PxShape, PxMaterial* >::TNbObjectsMember TNbObjectsMember; + PxShapeMaterialsProperty( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb ) + : PxReadOnlyCollectionPropertyInfo( inName, inGetter, inNb ) + { + } +}; + +struct PxRigidActorShapeCollectionHelper +{ + PX_PHYSX_CORE_API PxShape* createShape(PxRigidActor* inActor, const PxGeometry& geometry, PxMaterial& material, PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) const; + PX_PHYSX_CORE_API PxShape* createShape(PxRigidActor* inActor, const PxGeometry& geometry, PxMaterial *const* materials, PxU16 materialCount, PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) const; +}; + +struct PxRigidActorShapeCollection : public PxReadOnlyCollectionPropertyInfo + , public PxRigidActorShapeCollectionHelper +{ + typedef PxReadOnlyCollectionPropertyInfo< PxPropertyInfoName::PxRigidActor_Shapes, PxRigidActor, PxShape* >::TGetObjectsMember TGetObjectsMember; + typedef PxReadOnlyCollectionPropertyInfo< PxPropertyInfoName::PxRigidActor_Shapes, PxRigidActor, PxShape* >::TNbObjectsMember TNbObjectsMember; + PxRigidActorShapeCollection( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb ) + : PxReadOnlyCollectionPropertyInfo( inName, inGetter, inNb ) + { + } +}; + +struct PxArticulationLinkCollectionPropHelper +{ + PX_PHYSX_CORE_API PxArticulationLink* createLink(PxArticulation* inArticulation, PxArticulationLink* parent, const PxTransform& pose) const; +}; + +struct PxArticulationLinkCollectionProp : public PxReadOnlyCollectionPropertyInfo + , public PxArticulationLinkCollectionPropHelper +{ + PxArticulationLinkCollectionProp( const char* inName, TGetObjectsMember inGetter, TNbObjectsMember inNb ) + : PxReadOnlyCollectionPropertyInfo( inName, inGetter, inNb ) + { + } +}; + +template +struct PxEnumTraits { PxEnumTraits() : NameConversion( false ) {} bool NameConversion; }; + +struct NbShapesProperty : public PxIndexedPropertyInfo +{ + PX_PHYSX_CORE_API NbShapesProperty(); +}; + + +struct NbDiscreteContactPairsProperty : public PxDualIndexedPropertyInfo +{ + PX_PHYSX_CORE_API NbDiscreteContactPairsProperty(); +}; +struct NbModifiedContactPairsProperty : public PxDualIndexedPropertyInfo +{ + PX_PHYSX_CORE_API NbModifiedContactPairsProperty(); +}; + +struct NbCCDPairsProperty : public PxDualIndexedPropertyInfo +{ + PX_PHYSX_CORE_API NbCCDPairsProperty(); +}; + +struct NbTriggerPairsProperty : public PxDualIndexedPropertyInfo +{ + PX_PHYSX_CORE_API NbTriggerPairsProperty(); +}; + + +struct SimulationStatisticsProperty : public PxReadOnlyPropertyInfo +{ + PX_PHYSX_CORE_API SimulationStatisticsProperty(); +}; + +struct PxMetaDataPlane +{ + PxVec3 normal; + PxReal distance; + PxMetaDataPlane( PxVec3 n = PxVec3( 0, 0, 0 ), PxReal d = 0 ) + : normal( n ) + , distance( d ) + { + } +}; + +#include "PxAutoGeneratedMetaDataObjects.h" + +#undef DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP + +static PxU32ToName g_physx__PxQueryFlag__EnumConversion[] = { + { "eSTATIC", static_cast( PxQueryFlag::eSTATIC ) }, + { "eDYNAMIC", static_cast( PxQueryFlag::eDYNAMIC ) }, + { "ePREFILTER", static_cast( PxQueryFlag::ePREFILTER ) }, + { "ePOSTFILTER", static_cast( PxQueryFlag::ePOSTFILTER ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits { PxEnumTraits() : NameConversion( g_physx__PxQueryFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + + +template +inline PxU32 visitAllProperties( TOperator inOperator ) +{ + PxU32 thePropCount = PxClassInfoTraits().Info.visitBaseProperties( inOperator ); + return PxClassInfoTraits().Info.visitInstanceProperties( inOperator, thePropCount ); +} + +template +inline void visitInstanceProperties( TOperator inOperator ) +{ + PxClassInfoTraits().Info.visitInstanceProperties( inOperator, 0 ); +} + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h b/src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h new file mode 100644 index 000000000..062300b3b --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h @@ -0,0 +1,213 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_REPX_META_DATA_PROPERTY_VISITOR_H +#define PX_REPX_META_DATA_PROPERTY_VISITOR_H +#include "PvdMetaDataPropertyVisitor.h" + +namespace physx { + + template + struct PxRepXPropertyAccessor : public Vd::ValueStructOffsetRecord + { + typedef PxPropertyInfo TPropertyInfoType; + typedef TPropertyType prop_type; + + const TPropertyInfoType mProperty; + PxRepXPropertyAccessor( const TPropertyInfoType& inProp ) + : mProperty( inProp ) + { + } + prop_type get( const TObjectType* inObj ) const { return mProperty.get( inObj ); } + void set( TObjectType* inObj, prop_type val ) const { return mProperty.set( inObj, val ); } + + private: + PxRepXPropertyAccessor& operator=(const PxRepXPropertyAccessor&); + }; + + template + struct PxRepXPropertyAccessor : public Vd::ValueStructOffsetRecord + { + typedef PxPropertyInfo TPropertyInfoType; + typedef TPropertyType prop_type; + + const TPropertyInfoType mProperty; + PxRepXPropertyAccessor( const TPropertyInfoType& inProp ) + : mProperty( inProp ) + { + } + prop_type get( const PxRigidDynamic* inObj ) const { return mProperty.get( inObj ); } + void set( PxRigidDynamic* inObj, prop_type val ) const + { + PX_UNUSED(val); + PxRigidBodyFlags flags = inObj->getRigidBodyFlags(); + if( !(flags & PxRigidBodyFlag::eKINEMATIC) ) + return mProperty.set( inObj, val ); + } + private: + PxRepXPropertyAccessor& operator=(const PxRepXPropertyAccessor&); + }; + + typedef PxReadOnlyPropertyInfo TIncomingJointPropType; + + + //RepX cares about fewer property types than PVD does, + //but I want to reuse the accessor architecture as it + //greatly simplifies clients dealing with complex datatypes + template + struct RepXPropertyFilter + { + RepXPropertyFilter &operator=(const RepXPropertyFilter &); + + Vd::PvdPropertyFilter mFilter; + RepXPropertyFilter( TOperatorType op ) : mFilter( op ) {} + RepXPropertyFilter( const RepXPropertyFilter& other ) : mFilter( other.mFilter ) {} + + template + void operator()( const PxReadOnlyPropertyInfo&, PxU32 ) {} //repx ignores read only and write only properties + template + void operator()( const PxWriteOnlyPropertyInfo&, PxU32 ) {} + template + void operator()( const PxReadOnlyCollectionPropertyInfo&, PxU32 ) {} + + template + void operator()( const PxReadOnlyFilteredCollectionPropertyInfo&, PxU32 ) {} + //forward these properties verbatim. + template + void operator()( const PxIndexedPropertyInfo& inProp, PxU32 idx ) + { + mFilter( inProp, idx ); + } + + template + void operator()( const PxFixedSizeLookupTablePropertyInfo& inProp, PxU32 idx ) + { + mFilter( inProp, idx ); + } + + template + void operator()( const PxExtendedIndexedPropertyInfo& inProp, PxU32 idx) + { + mFilter( inProp, idx); + } + + template + void operator()( const PxDualIndexedPropertyInfo& inProp, PxU32 idx ) + { + mFilter( inProp, idx ); + } + + template + void operator()( const PxExtendedDualIndexedPropertyInfo& inProp, PxU32 idx ) + { + mFilter( inProp, idx ); + } + + template + void operator()( const PxRangePropertyInfo& inProp, PxU32 idx ) + { + mFilter( inProp, idx ); + } + + template + void operator()( const PxBufferCollectionPropertyInfo& inProp, PxU32 count ) + { + mFilter( inProp, count ); + } + + template + void operator()( const PxPropertyInfo& prop, PxU32 ) + { + PxRepXPropertyAccessor< TKey, TObjType, TSetPropType, TPropertyType > theAccessor( prop ); + mFilter.mOperator.pushName( prop.mName ); + mFilter.template handleAccessor( theAccessor ); + mFilter.mOperator.popName(); + } + + void operator()( const PxRigidActorShapeCollection& inProp, PxU32 ) + { + mFilter.mOperator.pushName( "Shapes" ); + mFilter.mOperator.handleShapes( inProp ); + mFilter.mOperator.popName(); + } + + void operator()( const PxArticulationLinkCollectionProp& inProp, PxU32 ) + { + mFilter.mOperator.pushName( "Links" ); + mFilter.mOperator.handleArticulationLinks( inProp ); + mFilter.mOperator.popName(); + } + + void operator()( const PxShapeMaterialsProperty& inProp, PxU32 ) + { + mFilter.mOperator.pushName( "Materials" ); + mFilter.mOperator.handleShapeMaterials( inProp ); + mFilter.mOperator.popName(); + } + + void operator()( const TIncomingJointPropType& inProp, PxU32 ) + { + mFilter.mOperator.handleIncomingJoint( inProp ); + } + + void operator()( const PxShapeGeometryProperty& inProp, PxU32 ) + { + mFilter.mOperator.handleGeometryProperty( inProp ); + } + +#define DEFINE_REPX_PROPERTY_NOP(datatype) \ + template \ + void operator()( const PxPropertyInfo&, PxU32 ){} + + DEFINE_REPX_PROPERTY_NOP( const void* ) + DEFINE_REPX_PROPERTY_NOP( void* ) + DEFINE_REPX_PROPERTY_NOP( PxSimulationFilterCallback * ) + DEFINE_REPX_PROPERTY_NOP( physx::PxTaskManager * ) + DEFINE_REPX_PROPERTY_NOP( PxSimulationFilterShader * ) + DEFINE_REPX_PROPERTY_NOP( PxSimulationFilterShader) + DEFINE_REPX_PROPERTY_NOP( PxContactModifyCallback * ) + DEFINE_REPX_PROPERTY_NOP( PxCCDContactModifyCallback * ) + DEFINE_REPX_PROPERTY_NOP( PxSimulationEventCallback * ) + DEFINE_REPX_PROPERTY_NOP( physx::PxGpuDispatcher* ) + DEFINE_REPX_PROPERTY_NOP( physx::PxCpuDispatcher * ) + DEFINE_REPX_PROPERTY_NOP( PxRigidActor ) + DEFINE_REPX_PROPERTY_NOP( const PxRigidActor ) + DEFINE_REPX_PROPERTY_NOP( PxRigidActor& ) + DEFINE_REPX_PROPERTY_NOP( const PxRigidActor& ) + DEFINE_REPX_PROPERTY_NOP( PxScene* ) + DEFINE_REPX_PROPERTY_NOP( PxAggregate * ) + DEFINE_REPX_PROPERTY_NOP( PxArticulation& ) + DEFINE_REPX_PROPERTY_NOP( const PxArticulationLink * ) + DEFINE_REPX_PROPERTY_NOP( const PxRigidDynamic * ) + DEFINE_REPX_PROPERTY_NOP( const PxRigidStatic * ) + DEFINE_REPX_PROPERTY_NOP( PxStridedData ) //These are handled in a custom fasion. + }; +} + +#endif diff --git a/src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp b/src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp new file mode 100644 index 000000000..b23f1cf6f --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp @@ -0,0 +1,1236 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#include "PxMetaDataObjects.h" +#include "PxMetaDataCppPrefix.h" +using namespace physx; +const PxTolerancesScale getPxPhysics_TolerancesScale( const PxPhysics* inObj ) { return inObj->getTolerancesScale(); } +PxU32 getPxPhysics_TriangleMeshes( const PxPhysics* inObj, PxTriangleMesh ** outBuffer, PxU32 inBufSize ) { return inObj->getTriangleMeshes( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_TriangleMeshes( const PxPhysics* inObj ) { return inObj->getNbTriangleMeshes( ); } +PxTriangleMesh * createPxPhysics_TriangleMeshes( PxPhysics* inObj, PxInputStream & inCreateParam ){ return inObj->createTriangleMesh( inCreateParam ); } +PxU32 getPxPhysics_HeightFields( const PxPhysics* inObj, PxHeightField ** outBuffer, PxU32 inBufSize ) { return inObj->getHeightFields( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_HeightFields( const PxPhysics* inObj ) { return inObj->getNbHeightFields( ); } +PxHeightField * createPxPhysics_HeightFields( PxPhysics* inObj, PxInputStream & inCreateParam ){ return inObj->createHeightField( inCreateParam ); } +PxU32 getPxPhysics_ConvexMeshes( const PxPhysics* inObj, PxConvexMesh ** outBuffer, PxU32 inBufSize ) { return inObj->getConvexMeshes( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_ConvexMeshes( const PxPhysics* inObj ) { return inObj->getNbConvexMeshes( ); } +PxConvexMesh * createPxPhysics_ConvexMeshes( PxPhysics* inObj, PxInputStream & inCreateParam ){ return inObj->createConvexMesh( inCreateParam ); } +PxU32 getPxPhysics_BVHStructures( const PxPhysics* inObj, PxBVHStructure ** outBuffer, PxU32 inBufSize ) { return inObj->getBVHStructures( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_BVHStructures( const PxPhysics* inObj ) { return inObj->getNbBVHStructures( ); } +PxBVHStructure * createPxPhysics_BVHStructures( PxPhysics* inObj, PxInputStream & inCreateParam ){ return inObj->createBVHStructure( inCreateParam ); } +PxU32 getPxPhysics_Scenes( const PxPhysics* inObj, PxScene ** outBuffer, PxU32 inBufSize ) { return inObj->getScenes( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_Scenes( const PxPhysics* inObj ) { return inObj->getNbScenes( ); } +PxScene * createPxPhysics_Scenes( PxPhysics* inObj, const PxSceneDesc & inCreateParam ){ return inObj->createScene( inCreateParam ); } +PxU32 getPxPhysics_Shapes( const PxPhysics* inObj, PxShape ** outBuffer, PxU32 inBufSize ) { return inObj->getShapes( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_Shapes( const PxPhysics* inObj ) { return inObj->getNbShapes( ); } +PxU32 getPxPhysics_Materials( const PxPhysics* inObj, PxMaterial ** outBuffer, PxU32 inBufSize ) { return inObj->getMaterials( outBuffer, inBufSize ); } +PxU32 getNbPxPhysics_Materials( const PxPhysics* inObj ) { return inObj->getNbMaterials( ); } +PX_PHYSX_CORE_API PxPhysicsGeneratedInfo::PxPhysicsGeneratedInfo() + : TolerancesScale( "TolerancesScale", getPxPhysics_TolerancesScale) + , TriangleMeshes( "TriangleMeshes", getPxPhysics_TriangleMeshes, getNbPxPhysics_TriangleMeshes, createPxPhysics_TriangleMeshes ) + , HeightFields( "HeightFields", getPxPhysics_HeightFields, getNbPxPhysics_HeightFields, createPxPhysics_HeightFields ) + , ConvexMeshes( "ConvexMeshes", getPxPhysics_ConvexMeshes, getNbPxPhysics_ConvexMeshes, createPxPhysics_ConvexMeshes ) + , BVHStructures( "BVHStructures", getPxPhysics_BVHStructures, getNbPxPhysics_BVHStructures, createPxPhysics_BVHStructures ) + , Scenes( "Scenes", getPxPhysics_Scenes, getNbPxPhysics_Scenes, createPxPhysics_Scenes ) + , Shapes( "Shapes", getPxPhysics_Shapes, getNbPxPhysics_Shapes ) + , Materials( "Materials", getPxPhysics_Materials, getNbPxPhysics_Materials ) +{} +PX_PHYSX_CORE_API PxPhysicsGeneratedValues::PxPhysicsGeneratedValues( const PxPhysics* inSource ) + :TolerancesScale( getPxPhysics_TolerancesScale( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxU32 getPxMaterial_ReferenceCount( const PxMaterial* inObj ) { return inObj->getReferenceCount(); } +void setPxMaterial_DynamicFriction( PxMaterial* inObj, PxReal inArg){ inObj->setDynamicFriction( inArg ); } +PxReal getPxMaterial_DynamicFriction( const PxMaterial* inObj ) { return inObj->getDynamicFriction(); } +void setPxMaterial_StaticFriction( PxMaterial* inObj, PxReal inArg){ inObj->setStaticFriction( inArg ); } +PxReal getPxMaterial_StaticFriction( const PxMaterial* inObj ) { return inObj->getStaticFriction(); } +void setPxMaterial_Restitution( PxMaterial* inObj, PxReal inArg){ inObj->setRestitution( inArg ); } +PxReal getPxMaterial_Restitution( const PxMaterial* inObj ) { return inObj->getRestitution(); } +void setPxMaterial_Flags( PxMaterial* inObj, PxMaterialFlags inArg){ inObj->setFlags( inArg ); } +PxMaterialFlags getPxMaterial_Flags( const PxMaterial* inObj ) { return inObj->getFlags(); } +void setPxMaterial_FrictionCombineMode( PxMaterial* inObj, PxCombineMode::Enum inArg){ inObj->setFrictionCombineMode( inArg ); } +PxCombineMode::Enum getPxMaterial_FrictionCombineMode( const PxMaterial* inObj ) { return inObj->getFrictionCombineMode(); } +void setPxMaterial_RestitutionCombineMode( PxMaterial* inObj, PxCombineMode::Enum inArg){ inObj->setRestitutionCombineMode( inArg ); } +PxCombineMode::Enum getPxMaterial_RestitutionCombineMode( const PxMaterial* inObj ) { return inObj->getRestitutionCombineMode(); } +const char * getPxMaterial_ConcreteTypeName( const PxMaterial* inObj ) { return inObj->getConcreteTypeName(); } +inline void * getPxMaterialUserData( const PxMaterial* inOwner ) { return inOwner->userData; } +inline void setPxMaterialUserData( PxMaterial* inOwner, void * inData) { inOwner->userData = inData; } +PX_PHYSX_CORE_API PxMaterialGeneratedInfo::PxMaterialGeneratedInfo() + : ReferenceCount( "ReferenceCount", getPxMaterial_ReferenceCount) + , DynamicFriction( "DynamicFriction", setPxMaterial_DynamicFriction, getPxMaterial_DynamicFriction) + , StaticFriction( "StaticFriction", setPxMaterial_StaticFriction, getPxMaterial_StaticFriction) + , Restitution( "Restitution", setPxMaterial_Restitution, getPxMaterial_Restitution) + , Flags( "Flags", setPxMaterial_Flags, getPxMaterial_Flags) + , FrictionCombineMode( "FrictionCombineMode", setPxMaterial_FrictionCombineMode, getPxMaterial_FrictionCombineMode) + , RestitutionCombineMode( "RestitutionCombineMode", setPxMaterial_RestitutionCombineMode, getPxMaterial_RestitutionCombineMode) + , ConcreteTypeName( "ConcreteTypeName", getPxMaterial_ConcreteTypeName) + , UserData( "UserData", setPxMaterialUserData, getPxMaterialUserData ) +{} +PX_PHYSX_CORE_API PxMaterialGeneratedValues::PxMaterialGeneratedValues( const PxMaterial* inSource ) + :ReferenceCount( getPxMaterial_ReferenceCount( inSource ) ) + ,DynamicFriction( getPxMaterial_DynamicFriction( inSource ) ) + ,StaticFriction( getPxMaterial_StaticFriction( inSource ) ) + ,Restitution( getPxMaterial_Restitution( inSource ) ) + ,Flags( getPxMaterial_Flags( inSource ) ) + ,FrictionCombineMode( getPxMaterial_FrictionCombineMode( inSource ) ) + ,RestitutionCombineMode( getPxMaterial_RestitutionCombineMode( inSource ) ) + ,ConcreteTypeName( getPxMaterial_ConcreteTypeName( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); +} +PxScene * getPxActor_Scene( const PxActor* inObj ) { return inObj->getScene(); } +void setPxActor_Name( PxActor* inObj, const char * inArg){ inObj->setName( inArg ); } +const char * getPxActor_Name( const PxActor* inObj ) { return inObj->getName(); } +void setPxActor_ActorFlags( PxActor* inObj, PxActorFlags inArg){ inObj->setActorFlags( inArg ); } +PxActorFlags getPxActor_ActorFlags( const PxActor* inObj ) { return inObj->getActorFlags(); } +void setPxActor_DominanceGroup( PxActor* inObj, PxDominanceGroup inArg){ inObj->setDominanceGroup( inArg ); } +PxDominanceGroup getPxActor_DominanceGroup( const PxActor* inObj ) { return inObj->getDominanceGroup(); } +void setPxActor_OwnerClient( PxActor* inObj, PxClientID inArg){ inObj->setOwnerClient( inArg ); } +PxClientID getPxActor_OwnerClient( const PxActor* inObj ) { return inObj->getOwnerClient(); } +PxAggregate * getPxActor_Aggregate( const PxActor* inObj ) { return inObj->getAggregate(); } +inline void * getPxActorUserData( const PxActor* inOwner ) { return inOwner->userData; } +inline void setPxActorUserData( PxActor* inOwner, void * inData) { inOwner->userData = inData; } +PX_PHYSX_CORE_API PxActorGeneratedInfo::PxActorGeneratedInfo() + : Scene( "Scene", getPxActor_Scene) + , Name( "Name", setPxActor_Name, getPxActor_Name) + , ActorFlags( "ActorFlags", setPxActor_ActorFlags, getPxActor_ActorFlags) + , DominanceGroup( "DominanceGroup", setPxActor_DominanceGroup, getPxActor_DominanceGroup) + , OwnerClient( "OwnerClient", setPxActor_OwnerClient, getPxActor_OwnerClient) + , Aggregate( "Aggregate", getPxActor_Aggregate) + , UserData( "UserData", setPxActorUserData, getPxActorUserData ) +{} +PX_PHYSX_CORE_API PxActorGeneratedValues::PxActorGeneratedValues( const PxActor* inSource ) + :Scene( getPxActor_Scene( inSource ) ) + ,Name( getPxActor_Name( inSource ) ) + ,ActorFlags( getPxActor_ActorFlags( inSource ) ) + ,DominanceGroup( getPxActor_DominanceGroup( inSource ) ) + ,OwnerClient( getPxActor_OwnerClient( inSource ) ) + ,Aggregate( getPxActor_Aggregate( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); +} +void setPxRigidActor_GlobalPose( PxRigidActor* inObj, const PxTransform & inArg){ inObj->setGlobalPose( inArg ); } +PxTransform getPxRigidActor_GlobalPose( const PxRigidActor* inObj ) { return inObj->getGlobalPose(); } +PxU32 getPxRigidActor_Shapes( const PxRigidActor* inObj, PxShape ** outBuffer, PxU32 inBufSize ) { return inObj->getShapes( outBuffer, inBufSize ); } +PxU32 getNbPxRigidActor_Shapes( const PxRigidActor* inObj ) { return inObj->getNbShapes( ); } +PxU32 getPxRigidActor_Constraints( const PxRigidActor* inObj, PxConstraint ** outBuffer, PxU32 inBufSize ) { return inObj->getConstraints( outBuffer, inBufSize ); } +PxU32 getNbPxRigidActor_Constraints( const PxRigidActor* inObj ) { return inObj->getNbConstraints( ); } +PX_PHYSX_CORE_API PxRigidActorGeneratedInfo::PxRigidActorGeneratedInfo() + : GlobalPose( "GlobalPose", setPxRigidActor_GlobalPose, getPxRigidActor_GlobalPose) + , Shapes( "Shapes", getPxRigidActor_Shapes, getNbPxRigidActor_Shapes ) + , Constraints( "Constraints", getPxRigidActor_Constraints, getNbPxRigidActor_Constraints ) +{} +PX_PHYSX_CORE_API PxRigidActorGeneratedValues::PxRigidActorGeneratedValues( const PxRigidActor* inSource ) + :PxActorGeneratedValues( inSource ) + ,GlobalPose( getPxRigidActor_GlobalPose( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxRigidBody_CMassLocalPose( PxRigidBody* inObj, const PxTransform & inArg){ inObj->setCMassLocalPose( inArg ); } +PxTransform getPxRigidBody_CMassLocalPose( const PxRigidBody* inObj ) { return inObj->getCMassLocalPose(); } +void setPxRigidBody_Mass( PxRigidBody* inObj, PxReal inArg){ inObj->setMass( inArg ); } +PxReal getPxRigidBody_Mass( const PxRigidBody* inObj ) { return inObj->getMass(); } +PxReal getPxRigidBody_InvMass( const PxRigidBody* inObj ) { return inObj->getInvMass(); } +void setPxRigidBody_MassSpaceInertiaTensor( PxRigidBody* inObj, const PxVec3 & inArg){ inObj->setMassSpaceInertiaTensor( inArg ); } +PxVec3 getPxRigidBody_MassSpaceInertiaTensor( const PxRigidBody* inObj ) { return inObj->getMassSpaceInertiaTensor(); } +PxVec3 getPxRigidBody_MassSpaceInvInertiaTensor( const PxRigidBody* inObj ) { return inObj->getMassSpaceInvInertiaTensor(); } +void setPxRigidBody_LinearDamping( PxRigidBody* inObj, PxReal inArg){ inObj->setLinearDamping( inArg ); } +PxReal getPxRigidBody_LinearDamping( const PxRigidBody* inObj ) { return inObj->getLinearDamping(); } +void setPxRigidBody_AngularDamping( PxRigidBody* inObj, PxReal inArg){ inObj->setAngularDamping( inArg ); } +PxReal getPxRigidBody_AngularDamping( const PxRigidBody* inObj ) { return inObj->getAngularDamping(); } +void setPxRigidBody_LinearVelocity( PxRigidBody* inObj, const PxVec3 & inArg){ inObj->setLinearVelocity( inArg ); } +PxVec3 getPxRigidBody_LinearVelocity( const PxRigidBody* inObj ) { return inObj->getLinearVelocity(); } +void setPxRigidBody_AngularVelocity( PxRigidBody* inObj, const PxVec3 & inArg){ inObj->setAngularVelocity( inArg ); } +PxVec3 getPxRigidBody_AngularVelocity( const PxRigidBody* inObj ) { return inObj->getAngularVelocity(); } +void setPxRigidBody_MaxAngularVelocity( PxRigidBody* inObj, PxReal inArg){ inObj->setMaxAngularVelocity( inArg ); } +PxReal getPxRigidBody_MaxAngularVelocity( const PxRigidBody* inObj ) { return inObj->getMaxAngularVelocity(); } +void setPxRigidBody_MaxLinearVelocity( PxRigidBody* inObj, PxReal inArg){ inObj->setMaxLinearVelocity( inArg ); } +PxReal getPxRigidBody_MaxLinearVelocity( const PxRigidBody* inObj ) { return inObj->getMaxLinearVelocity(); } +void setPxRigidBody_RigidBodyFlags( PxRigidBody* inObj, PxRigidBodyFlags inArg){ inObj->setRigidBodyFlags( inArg ); } +PxRigidBodyFlags getPxRigidBody_RigidBodyFlags( const PxRigidBody* inObj ) { return inObj->getRigidBodyFlags(); } +void setPxRigidBody_MinCCDAdvanceCoefficient( PxRigidBody* inObj, PxReal inArg){ inObj->setMinCCDAdvanceCoefficient( inArg ); } +PxReal getPxRigidBody_MinCCDAdvanceCoefficient( const PxRigidBody* inObj ) { return inObj->getMinCCDAdvanceCoefficient(); } +void setPxRigidBody_MaxDepenetrationVelocity( PxRigidBody* inObj, PxReal inArg){ inObj->setMaxDepenetrationVelocity( inArg ); } +PxReal getPxRigidBody_MaxDepenetrationVelocity( const PxRigidBody* inObj ) { return inObj->getMaxDepenetrationVelocity(); } +void setPxRigidBody_MaxContactImpulse( PxRigidBody* inObj, PxReal inArg){ inObj->setMaxContactImpulse( inArg ); } +PxReal getPxRigidBody_MaxContactImpulse( const PxRigidBody* inObj ) { return inObj->getMaxContactImpulse(); } +PX_PHYSX_CORE_API PxRigidBodyGeneratedInfo::PxRigidBodyGeneratedInfo() + : CMassLocalPose( "CMassLocalPose", setPxRigidBody_CMassLocalPose, getPxRigidBody_CMassLocalPose) + , Mass( "Mass", setPxRigidBody_Mass, getPxRigidBody_Mass) + , InvMass( "InvMass", getPxRigidBody_InvMass) + , MassSpaceInertiaTensor( "MassSpaceInertiaTensor", setPxRigidBody_MassSpaceInertiaTensor, getPxRigidBody_MassSpaceInertiaTensor) + , MassSpaceInvInertiaTensor( "MassSpaceInvInertiaTensor", getPxRigidBody_MassSpaceInvInertiaTensor) + , LinearDamping( "LinearDamping", setPxRigidBody_LinearDamping, getPxRigidBody_LinearDamping) + , AngularDamping( "AngularDamping", setPxRigidBody_AngularDamping, getPxRigidBody_AngularDamping) + , LinearVelocity( "LinearVelocity", setPxRigidBody_LinearVelocity, getPxRigidBody_LinearVelocity) + , AngularVelocity( "AngularVelocity", setPxRigidBody_AngularVelocity, getPxRigidBody_AngularVelocity) + , MaxAngularVelocity( "MaxAngularVelocity", setPxRigidBody_MaxAngularVelocity, getPxRigidBody_MaxAngularVelocity) + , MaxLinearVelocity( "MaxLinearVelocity", setPxRigidBody_MaxLinearVelocity, getPxRigidBody_MaxLinearVelocity) + , RigidBodyFlags( "RigidBodyFlags", setPxRigidBody_RigidBodyFlags, getPxRigidBody_RigidBodyFlags) + , MinCCDAdvanceCoefficient( "MinCCDAdvanceCoefficient", setPxRigidBody_MinCCDAdvanceCoefficient, getPxRigidBody_MinCCDAdvanceCoefficient) + , MaxDepenetrationVelocity( "MaxDepenetrationVelocity", setPxRigidBody_MaxDepenetrationVelocity, getPxRigidBody_MaxDepenetrationVelocity) + , MaxContactImpulse( "MaxContactImpulse", setPxRigidBody_MaxContactImpulse, getPxRigidBody_MaxContactImpulse) +{} +PX_PHYSX_CORE_API PxRigidBodyGeneratedValues::PxRigidBodyGeneratedValues( const PxRigidBody* inSource ) + :PxRigidActorGeneratedValues( inSource ) + ,CMassLocalPose( getPxRigidBody_CMassLocalPose( inSource ) ) + ,Mass( getPxRigidBody_Mass( inSource ) ) + ,InvMass( getPxRigidBody_InvMass( inSource ) ) + ,MassSpaceInertiaTensor( getPxRigidBody_MassSpaceInertiaTensor( inSource ) ) + ,MassSpaceInvInertiaTensor( getPxRigidBody_MassSpaceInvInertiaTensor( inSource ) ) + ,LinearDamping( getPxRigidBody_LinearDamping( inSource ) ) + ,AngularDamping( getPxRigidBody_AngularDamping( inSource ) ) + ,LinearVelocity( getPxRigidBody_LinearVelocity( inSource ) ) + ,AngularVelocity( getPxRigidBody_AngularVelocity( inSource ) ) + ,MaxAngularVelocity( getPxRigidBody_MaxAngularVelocity( inSource ) ) + ,MaxLinearVelocity( getPxRigidBody_MaxLinearVelocity( inSource ) ) + ,RigidBodyFlags( getPxRigidBody_RigidBodyFlags( inSource ) ) + ,MinCCDAdvanceCoefficient( getPxRigidBody_MinCCDAdvanceCoefficient( inSource ) ) + ,MaxDepenetrationVelocity( getPxRigidBody_MaxDepenetrationVelocity( inSource ) ) + ,MaxContactImpulse( getPxRigidBody_MaxContactImpulse( inSource ) ) +{ + PX_UNUSED(inSource); +} +_Bool getPxRigidDynamic_IsSleeping( const PxRigidDynamic* inObj ) { return inObj->isSleeping(); } +void setPxRigidDynamic_SleepThreshold( PxRigidDynamic* inObj, PxReal inArg){ inObj->setSleepThreshold( inArg ); } +PxReal getPxRigidDynamic_SleepThreshold( const PxRigidDynamic* inObj ) { return inObj->getSleepThreshold(); } +void setPxRigidDynamic_StabilizationThreshold( PxRigidDynamic* inObj, PxReal inArg){ inObj->setStabilizationThreshold( inArg ); } +PxReal getPxRigidDynamic_StabilizationThreshold( const PxRigidDynamic* inObj ) { return inObj->getStabilizationThreshold(); } +void setPxRigidDynamic_RigidDynamicLockFlags( PxRigidDynamic* inObj, PxRigidDynamicLockFlags inArg){ inObj->setRigidDynamicLockFlags( inArg ); } +PxRigidDynamicLockFlags getPxRigidDynamic_RigidDynamicLockFlags( const PxRigidDynamic* inObj ) { return inObj->getRigidDynamicLockFlags(); } +void setPxRigidDynamic_WakeCounter( PxRigidDynamic* inObj, PxReal inArg){ inObj->setWakeCounter( inArg ); } +PxReal getPxRigidDynamic_WakeCounter( const PxRigidDynamic* inObj ) { return inObj->getWakeCounter(); } +void setPxRigidDynamic_SolverIterationCounts( PxRigidDynamic* inObj, PxU32 inArg0, PxU32 inArg1 ) { inObj->setSolverIterationCounts( inArg0, inArg1 ); } +void getPxRigidDynamic_SolverIterationCounts( const PxRigidDynamic* inObj, PxU32& inArg0, PxU32& inArg1 ) { inObj->getSolverIterationCounts( inArg0, inArg1 ); } +void setPxRigidDynamic_ContactReportThreshold( PxRigidDynamic* inObj, PxReal inArg){ inObj->setContactReportThreshold( inArg ); } +PxReal getPxRigidDynamic_ContactReportThreshold( const PxRigidDynamic* inObj ) { return inObj->getContactReportThreshold(); } +const char * getPxRigidDynamic_ConcreteTypeName( const PxRigidDynamic* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxRigidDynamicGeneratedInfo::PxRigidDynamicGeneratedInfo() + : IsSleeping( "IsSleeping", getPxRigidDynamic_IsSleeping) + , SleepThreshold( "SleepThreshold", setPxRigidDynamic_SleepThreshold, getPxRigidDynamic_SleepThreshold) + , StabilizationThreshold( "StabilizationThreshold", setPxRigidDynamic_StabilizationThreshold, getPxRigidDynamic_StabilizationThreshold) + , RigidDynamicLockFlags( "RigidDynamicLockFlags", setPxRigidDynamic_RigidDynamicLockFlags, getPxRigidDynamic_RigidDynamicLockFlags) + , WakeCounter( "WakeCounter", setPxRigidDynamic_WakeCounter, getPxRigidDynamic_WakeCounter) + , SolverIterationCounts( "SolverIterationCounts", "minPositionIters", "minVelocityIters", setPxRigidDynamic_SolverIterationCounts, getPxRigidDynamic_SolverIterationCounts) + , ContactReportThreshold( "ContactReportThreshold", setPxRigidDynamic_ContactReportThreshold, getPxRigidDynamic_ContactReportThreshold) + , ConcreteTypeName( "ConcreteTypeName", getPxRigidDynamic_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxRigidDynamicGeneratedValues::PxRigidDynamicGeneratedValues( const PxRigidDynamic* inSource ) + :PxRigidBodyGeneratedValues( inSource ) + ,IsSleeping( getPxRigidDynamic_IsSleeping( inSource ) ) + ,SleepThreshold( getPxRigidDynamic_SleepThreshold( inSource ) ) + ,StabilizationThreshold( getPxRigidDynamic_StabilizationThreshold( inSource ) ) + ,RigidDynamicLockFlags( getPxRigidDynamic_RigidDynamicLockFlags( inSource ) ) + ,WakeCounter( getPxRigidDynamic_WakeCounter( inSource ) ) + ,ContactReportThreshold( getPxRigidDynamic_ContactReportThreshold( inSource ) ) + ,ConcreteTypeName( getPxRigidDynamic_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); + getPxRigidDynamic_SolverIterationCounts( inSource, SolverIterationCounts[0], SolverIterationCounts[1] ); +} +const char * getPxRigidStatic_ConcreteTypeName( const PxRigidStatic* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxRigidStaticGeneratedInfo::PxRigidStaticGeneratedInfo() + : ConcreteTypeName( "ConcreteTypeName", getPxRigidStatic_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxRigidStaticGeneratedValues::PxRigidStaticGeneratedValues( const PxRigidStatic* inSource ) + :PxRigidActorGeneratedValues( inSource ) + ,ConcreteTypeName( getPxRigidStatic_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxArticulationJointBase * getPxArticulationLink_InboundJoint( const PxArticulationLink* inObj ) { return inObj->getInboundJoint(); } +PxU32 getPxArticulationLink_InboundJointDof( const PxArticulationLink* inObj ) { return inObj->getInboundJointDof(); } +PxU32 getPxArticulationLink_LinkIndex( const PxArticulationLink* inObj ) { return inObj->getLinkIndex(); } +PxU32 getPxArticulationLink_Children( const PxArticulationLink* inObj, PxArticulationLink ** outBuffer, PxU32 inBufSize ) { return inObj->getChildren( outBuffer, inBufSize ); } +PxU32 getNbPxArticulationLink_Children( const PxArticulationLink* inObj ) { return inObj->getNbChildren( ); } +const char * getPxArticulationLink_ConcreteTypeName( const PxArticulationLink* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxArticulationLinkGeneratedInfo::PxArticulationLinkGeneratedInfo() + : InboundJoint( "InboundJoint", getPxArticulationLink_InboundJoint) + , InboundJointDof( "InboundJointDof", getPxArticulationLink_InboundJointDof) + , LinkIndex( "LinkIndex", getPxArticulationLink_LinkIndex) + , Children( "Children", getPxArticulationLink_Children, getNbPxArticulationLink_Children ) + , ConcreteTypeName( "ConcreteTypeName", getPxArticulationLink_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxArticulationLinkGeneratedValues::PxArticulationLinkGeneratedValues( const PxArticulationLink* inSource ) + :PxRigidBodyGeneratedValues( inSource ) + ,InboundJoint( getPxArticulationLink_InboundJoint( inSource ) ) + ,InboundJointDof( getPxArticulationLink_InboundJointDof( inSource ) ) + ,LinkIndex( getPxArticulationLink_LinkIndex( inSource ) ) + ,ConcreteTypeName( getPxArticulationLink_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxArticulationJointBase_ParentPose( PxArticulationJointBase* inObj, const PxTransform & inArg){ inObj->setParentPose( inArg ); } +PxTransform getPxArticulationJointBase_ParentPose( const PxArticulationJointBase* inObj ) { return inObj->getParentPose(); } +void setPxArticulationJointBase_ChildPose( PxArticulationJointBase* inObj, const PxTransform & inArg){ inObj->setChildPose( inArg ); } +PxTransform getPxArticulationJointBase_ChildPose( const PxArticulationJointBase* inObj ) { return inObj->getChildPose(); } +PX_PHYSX_CORE_API PxArticulationJointBaseGeneratedInfo::PxArticulationJointBaseGeneratedInfo() + : ParentPose( "ParentPose", setPxArticulationJointBase_ParentPose, getPxArticulationJointBase_ParentPose) + , ChildPose( "ChildPose", setPxArticulationJointBase_ChildPose, getPxArticulationJointBase_ChildPose) +{} +PX_PHYSX_CORE_API PxArticulationJointBaseGeneratedValues::PxArticulationJointBaseGeneratedValues( const PxArticulationJointBase* inSource ) + :ParentPose( getPxArticulationJointBase_ParentPose( inSource ) ) + ,ChildPose( getPxArticulationJointBase_ChildPose( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxArticulationJoint_TargetOrientation( PxArticulationJoint* inObj, const PxQuat & inArg){ inObj->setTargetOrientation( inArg ); } +PxQuat getPxArticulationJoint_TargetOrientation( const PxArticulationJoint* inObj ) { return inObj->getTargetOrientation(); } +void setPxArticulationJoint_TargetVelocity( PxArticulationJoint* inObj, const PxVec3 & inArg){ inObj->setTargetVelocity( inArg ); } +PxVec3 getPxArticulationJoint_TargetVelocity( const PxArticulationJoint* inObj ) { return inObj->getTargetVelocity(); } +void setPxArticulationJoint_DriveType( PxArticulationJoint* inObj, PxArticulationJointDriveType::Enum inArg){ inObj->setDriveType( inArg ); } +PxArticulationJointDriveType::Enum getPxArticulationJoint_DriveType( const PxArticulationJoint* inObj ) { return inObj->getDriveType(); } +void setPxArticulationJoint_Stiffness( PxArticulationJoint* inObj, PxReal inArg){ inObj->setStiffness( inArg ); } +PxReal getPxArticulationJoint_Stiffness( const PxArticulationJoint* inObj ) { return inObj->getStiffness(); } +void setPxArticulationJoint_Damping( PxArticulationJoint* inObj, PxReal inArg){ inObj->setDamping( inArg ); } +PxReal getPxArticulationJoint_Damping( const PxArticulationJoint* inObj ) { return inObj->getDamping(); } +void setPxArticulationJoint_InternalCompliance( PxArticulationJoint* inObj, PxReal inArg){ inObj->setInternalCompliance( inArg ); } +PxReal getPxArticulationJoint_InternalCompliance( const PxArticulationJoint* inObj ) { return inObj->getInternalCompliance(); } +void setPxArticulationJoint_ExternalCompliance( PxArticulationJoint* inObj, PxReal inArg){ inObj->setExternalCompliance( inArg ); } +PxReal getPxArticulationJoint_ExternalCompliance( const PxArticulationJoint* inObj ) { return inObj->getExternalCompliance(); } +void setPxArticulationJoint_SwingLimit( PxArticulationJoint* inObj, PxReal inArg0, PxReal inArg1 ) { inObj->setSwingLimit( inArg0, inArg1 ); } +void getPxArticulationJoint_SwingLimit( const PxArticulationJoint* inObj, PxReal& inArg0, PxReal& inArg1 ) { inObj->getSwingLimit( inArg0, inArg1 ); } +void setPxArticulationJoint_TangentialStiffness( PxArticulationJoint* inObj, PxReal inArg){ inObj->setTangentialStiffness( inArg ); } +PxReal getPxArticulationJoint_TangentialStiffness( const PxArticulationJoint* inObj ) { return inObj->getTangentialStiffness(); } +void setPxArticulationJoint_TangentialDamping( PxArticulationJoint* inObj, PxReal inArg){ inObj->setTangentialDamping( inArg ); } +PxReal getPxArticulationJoint_TangentialDamping( const PxArticulationJoint* inObj ) { return inObj->getTangentialDamping(); } +void setPxArticulationJoint_SwingLimitContactDistance( PxArticulationJoint* inObj, PxReal inArg){ inObj->setSwingLimitContactDistance( inArg ); } +PxReal getPxArticulationJoint_SwingLimitContactDistance( const PxArticulationJoint* inObj ) { return inObj->getSwingLimitContactDistance(); } +void setPxArticulationJoint_SwingLimitEnabled( PxArticulationJoint* inObj, _Bool inArg){ inObj->setSwingLimitEnabled( inArg ); } +_Bool getPxArticulationJoint_SwingLimitEnabled( const PxArticulationJoint* inObj ) { return inObj->getSwingLimitEnabled(); } +void setPxArticulationJoint_TwistLimit( PxArticulationJoint* inObj, PxReal inArg0, PxReal inArg1 ) { inObj->setTwistLimit( inArg0, inArg1 ); } +void getPxArticulationJoint_TwistLimit( const PxArticulationJoint* inObj, PxReal& inArg0, PxReal& inArg1 ) { inObj->getTwistLimit( inArg0, inArg1 ); } +void setPxArticulationJoint_TwistLimitEnabled( PxArticulationJoint* inObj, _Bool inArg){ inObj->setTwistLimitEnabled( inArg ); } +_Bool getPxArticulationJoint_TwistLimitEnabled( const PxArticulationJoint* inObj ) { return inObj->getTwistLimitEnabled(); } +void setPxArticulationJoint_TwistLimitContactDistance( PxArticulationJoint* inObj, PxReal inArg){ inObj->setTwistLimitContactDistance( inArg ); } +PxReal getPxArticulationJoint_TwistLimitContactDistance( const PxArticulationJoint* inObj ) { return inObj->getTwistLimitContactDistance(); } +const char * getPxArticulationJoint_ConcreteTypeName( const PxArticulationJoint* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxArticulationJointGeneratedInfo::PxArticulationJointGeneratedInfo() + : TargetOrientation( "TargetOrientation", setPxArticulationJoint_TargetOrientation, getPxArticulationJoint_TargetOrientation) + , TargetVelocity( "TargetVelocity", setPxArticulationJoint_TargetVelocity, getPxArticulationJoint_TargetVelocity) + , DriveType( "DriveType", setPxArticulationJoint_DriveType, getPxArticulationJoint_DriveType) + , Stiffness( "Stiffness", setPxArticulationJoint_Stiffness, getPxArticulationJoint_Stiffness) + , Damping( "Damping", setPxArticulationJoint_Damping, getPxArticulationJoint_Damping) + , InternalCompliance( "InternalCompliance", setPxArticulationJoint_InternalCompliance, getPxArticulationJoint_InternalCompliance) + , ExternalCompliance( "ExternalCompliance", setPxArticulationJoint_ExternalCompliance, getPxArticulationJoint_ExternalCompliance) + , SwingLimit( "SwingLimit", "zLimit", "yLimit", setPxArticulationJoint_SwingLimit, getPxArticulationJoint_SwingLimit) + , TangentialStiffness( "TangentialStiffness", setPxArticulationJoint_TangentialStiffness, getPxArticulationJoint_TangentialStiffness) + , TangentialDamping( "TangentialDamping", setPxArticulationJoint_TangentialDamping, getPxArticulationJoint_TangentialDamping) + , SwingLimitContactDistance( "SwingLimitContactDistance", setPxArticulationJoint_SwingLimitContactDistance, getPxArticulationJoint_SwingLimitContactDistance) + , SwingLimitEnabled( "SwingLimitEnabled", setPxArticulationJoint_SwingLimitEnabled, getPxArticulationJoint_SwingLimitEnabled) + , TwistLimit( "TwistLimit", "lower", "upper", setPxArticulationJoint_TwistLimit, getPxArticulationJoint_TwistLimit) + , TwistLimitEnabled( "TwistLimitEnabled", setPxArticulationJoint_TwistLimitEnabled, getPxArticulationJoint_TwistLimitEnabled) + , TwistLimitContactDistance( "TwistLimitContactDistance", setPxArticulationJoint_TwistLimitContactDistance, getPxArticulationJoint_TwistLimitContactDistance) + , ConcreteTypeName( "ConcreteTypeName", getPxArticulationJoint_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxArticulationJointGeneratedValues::PxArticulationJointGeneratedValues( const PxArticulationJoint* inSource ) + :PxArticulationJointBaseGeneratedValues( inSource ) + ,TargetOrientation( getPxArticulationJoint_TargetOrientation( inSource ) ) + ,TargetVelocity( getPxArticulationJoint_TargetVelocity( inSource ) ) + ,DriveType( getPxArticulationJoint_DriveType( inSource ) ) + ,Stiffness( getPxArticulationJoint_Stiffness( inSource ) ) + ,Damping( getPxArticulationJoint_Damping( inSource ) ) + ,InternalCompliance( getPxArticulationJoint_InternalCompliance( inSource ) ) + ,ExternalCompliance( getPxArticulationJoint_ExternalCompliance( inSource ) ) + ,TangentialStiffness( getPxArticulationJoint_TangentialStiffness( inSource ) ) + ,TangentialDamping( getPxArticulationJoint_TangentialDamping( inSource ) ) + ,SwingLimitContactDistance( getPxArticulationJoint_SwingLimitContactDistance( inSource ) ) + ,SwingLimitEnabled( getPxArticulationJoint_SwingLimitEnabled( inSource ) ) + ,TwistLimitEnabled( getPxArticulationJoint_TwistLimitEnabled( inSource ) ) + ,TwistLimitContactDistance( getPxArticulationJoint_TwistLimitContactDistance( inSource ) ) + ,ConcreteTypeName( getPxArticulationJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); + getPxArticulationJoint_SwingLimit( inSource, SwingLimit[0], SwingLimit[1] ); + getPxArticulationJoint_TwistLimit( inSource, TwistLimit[0], TwistLimit[1] ); +} +PxScene * getPxArticulationBase_Scene( const PxArticulationBase* inObj ) { return inObj->getScene(); } +void setPxArticulationBase_SolverIterationCounts( PxArticulationBase* inObj, PxU32 inArg0, PxU32 inArg1 ) { inObj->setSolverIterationCounts( inArg0, inArg1 ); } +void getPxArticulationBase_SolverIterationCounts( const PxArticulationBase* inObj, PxU32& inArg0, PxU32& inArg1 ) { inObj->getSolverIterationCounts( inArg0, inArg1 ); } +_Bool getPxArticulationBase_IsSleeping( const PxArticulationBase* inObj ) { return inObj->isSleeping(); } +void setPxArticulationBase_SleepThreshold( PxArticulationBase* inObj, PxReal inArg){ inObj->setSleepThreshold( inArg ); } +PxReal getPxArticulationBase_SleepThreshold( const PxArticulationBase* inObj ) { return inObj->getSleepThreshold(); } +void setPxArticulationBase_StabilizationThreshold( PxArticulationBase* inObj, PxReal inArg){ inObj->setStabilizationThreshold( inArg ); } +PxReal getPxArticulationBase_StabilizationThreshold( const PxArticulationBase* inObj ) { return inObj->getStabilizationThreshold(); } +void setPxArticulationBase_WakeCounter( PxArticulationBase* inObj, PxReal inArg){ inObj->setWakeCounter( inArg ); } +PxReal getPxArticulationBase_WakeCounter( const PxArticulationBase* inObj ) { return inObj->getWakeCounter(); } +PxU32 getPxArticulationBase_Links( const PxArticulationBase* inObj, PxArticulationLink ** outBuffer, PxU32 inBufSize ) { return inObj->getLinks( outBuffer, inBufSize ); } +PxU32 getNbPxArticulationBase_Links( const PxArticulationBase* inObj ) { return inObj->getNbLinks( ); } +void setPxArticulationBase_Name( PxArticulationBase* inObj, const char * inArg){ inObj->setName( inArg ); } +const char * getPxArticulationBase_Name( const PxArticulationBase* inObj ) { return inObj->getName(); } +PxAggregate * getPxArticulationBase_Aggregate( const PxArticulationBase* inObj ) { return inObj->getAggregate(); } +PxArticulationBase::Enum getPxArticulationBase_Type( const PxArticulationBase* inObj ) { return inObj->getType(); } +inline void * getPxArticulationBaseUserData( const PxArticulationBase* inOwner ) { return inOwner->userData; } +inline void setPxArticulationBaseUserData( PxArticulationBase* inOwner, void * inData) { inOwner->userData = inData; } +PX_PHYSX_CORE_API PxArticulationBaseGeneratedInfo::PxArticulationBaseGeneratedInfo() + : Scene( "Scene", getPxArticulationBase_Scene) + , SolverIterationCounts( "SolverIterationCounts", "minPositionIters", "minVelocityIters", setPxArticulationBase_SolverIterationCounts, getPxArticulationBase_SolverIterationCounts) + , IsSleeping( "IsSleeping", getPxArticulationBase_IsSleeping) + , SleepThreshold( "SleepThreshold", setPxArticulationBase_SleepThreshold, getPxArticulationBase_SleepThreshold) + , StabilizationThreshold( "StabilizationThreshold", setPxArticulationBase_StabilizationThreshold, getPxArticulationBase_StabilizationThreshold) + , WakeCounter( "WakeCounter", setPxArticulationBase_WakeCounter, getPxArticulationBase_WakeCounter) + , Links( "Links", getPxArticulationBase_Links, getNbPxArticulationBase_Links ) + , Name( "Name", setPxArticulationBase_Name, getPxArticulationBase_Name) + , Aggregate( "Aggregate", getPxArticulationBase_Aggregate) + , Type( "Type", getPxArticulationBase_Type) + , UserData( "UserData", setPxArticulationBaseUserData, getPxArticulationBaseUserData ) +{} +PX_PHYSX_CORE_API PxArticulationBaseGeneratedValues::PxArticulationBaseGeneratedValues( const PxArticulationBase* inSource ) + :Scene( getPxArticulationBase_Scene( inSource ) ) + ,IsSleeping( getPxArticulationBase_IsSleeping( inSource ) ) + ,SleepThreshold( getPxArticulationBase_SleepThreshold( inSource ) ) + ,StabilizationThreshold( getPxArticulationBase_StabilizationThreshold( inSource ) ) + ,WakeCounter( getPxArticulationBase_WakeCounter( inSource ) ) + ,Name( getPxArticulationBase_Name( inSource ) ) + ,Aggregate( getPxArticulationBase_Aggregate( inSource ) ) + ,Type( getPxArticulationBase_Type( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); + getPxArticulationBase_SolverIterationCounts( inSource, SolverIterationCounts[0], SolverIterationCounts[1] ); +} +void setPxArticulation_MaxProjectionIterations( PxArticulation* inObj, PxU32 inArg){ inObj->setMaxProjectionIterations( inArg ); } +PxU32 getPxArticulation_MaxProjectionIterations( const PxArticulation* inObj ) { return inObj->getMaxProjectionIterations(); } +void setPxArticulation_SeparationTolerance( PxArticulation* inObj, PxReal inArg){ inObj->setSeparationTolerance( inArg ); } +PxReal getPxArticulation_SeparationTolerance( const PxArticulation* inObj ) { return inObj->getSeparationTolerance(); } +void setPxArticulation_InternalDriveIterations( PxArticulation* inObj, PxU32 inArg){ inObj->setInternalDriveIterations( inArg ); } +PxU32 getPxArticulation_InternalDriveIterations( const PxArticulation* inObj ) { return inObj->getInternalDriveIterations(); } +void setPxArticulation_ExternalDriveIterations( PxArticulation* inObj, PxU32 inArg){ inObj->setExternalDriveIterations( inArg ); } +PxU32 getPxArticulation_ExternalDriveIterations( const PxArticulation* inObj ) { return inObj->getExternalDriveIterations(); } +PX_PHYSX_CORE_API PxArticulationGeneratedInfo::PxArticulationGeneratedInfo() + : MaxProjectionIterations( "MaxProjectionIterations", setPxArticulation_MaxProjectionIterations, getPxArticulation_MaxProjectionIterations) + , SeparationTolerance( "SeparationTolerance", setPxArticulation_SeparationTolerance, getPxArticulation_SeparationTolerance) + , InternalDriveIterations( "InternalDriveIterations", setPxArticulation_InternalDriveIterations, getPxArticulation_InternalDriveIterations) + , ExternalDriveIterations( "ExternalDriveIterations", setPxArticulation_ExternalDriveIterations, getPxArticulation_ExternalDriveIterations) +{} +PX_PHYSX_CORE_API PxArticulationGeneratedValues::PxArticulationGeneratedValues( const PxArticulation* inSource ) + :PxArticulationBaseGeneratedValues( inSource ) + ,MaxProjectionIterations( getPxArticulation_MaxProjectionIterations( inSource ) ) + ,SeparationTolerance( getPxArticulation_SeparationTolerance( inSource ) ) + ,InternalDriveIterations( getPxArticulation_InternalDriveIterations( inSource ) ) + ,ExternalDriveIterations( getPxArticulation_ExternalDriveIterations( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxU32 getPxAggregate_MaxNbActors( const PxAggregate* inObj ) { return inObj->getMaxNbActors(); } +PxU32 getPxAggregate_Actors( const PxAggregate* inObj, PxActor ** outBuffer, PxU32 inBufSize ) { return inObj->getActors( outBuffer, inBufSize ); } +PxU32 getNbPxAggregate_Actors( const PxAggregate* inObj ) { return inObj->getNbActors( ); } +_Bool getPxAggregate_SelfCollision( const PxAggregate* inObj ) { return inObj->getSelfCollision(); } +const char * getPxAggregate_ConcreteTypeName( const PxAggregate* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxAggregateGeneratedInfo::PxAggregateGeneratedInfo() + : MaxNbActors( "MaxNbActors", getPxAggregate_MaxNbActors) + , Actors( "Actors", getPxAggregate_Actors, getNbPxAggregate_Actors ) + , SelfCollision( "SelfCollision", getPxAggregate_SelfCollision) + , ConcreteTypeName( "ConcreteTypeName", getPxAggregate_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxAggregateGeneratedValues::PxAggregateGeneratedValues( const PxAggregate* inSource ) + :MaxNbActors( getPxAggregate_MaxNbActors( inSource ) ) + ,SelfCollision( getPxAggregate_SelfCollision( inSource ) ) + ,ConcreteTypeName( getPxAggregate_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxScene * getPxConstraint_Scene( const PxConstraint* inObj ) { return inObj->getScene(); } +void setPxConstraint_Actors( PxConstraint* inObj, PxRigidActor * inArg0, PxRigidActor * inArg1 ) { inObj->setActors( inArg0, inArg1 ); } +void getPxConstraint_Actors( const PxConstraint* inObj, PxRigidActor *& inArg0, PxRigidActor *& inArg1 ) { inObj->getActors( inArg0, inArg1 ); } +void setPxConstraint_Flags( PxConstraint* inObj, PxConstraintFlags inArg){ inObj->setFlags( inArg ); } +PxConstraintFlags getPxConstraint_Flags( const PxConstraint* inObj ) { return inObj->getFlags(); } +_Bool getPxConstraint_IsValid( const PxConstraint* inObj ) { return inObj->isValid(); } +void setPxConstraint_BreakForce( PxConstraint* inObj, PxReal inArg0, PxReal inArg1 ) { inObj->setBreakForce( inArg0, inArg1 ); } +void getPxConstraint_BreakForce( const PxConstraint* inObj, PxReal& inArg0, PxReal& inArg1 ) { inObj->getBreakForce( inArg0, inArg1 ); } +void setPxConstraint_MinResponseThreshold( PxConstraint* inObj, PxReal inArg){ inObj->setMinResponseThreshold( inArg ); } +PxReal getPxConstraint_MinResponseThreshold( const PxConstraint* inObj ) { return inObj->getMinResponseThreshold(); } +const char * getPxConstraint_ConcreteTypeName( const PxConstraint* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxConstraintGeneratedInfo::PxConstraintGeneratedInfo() + : Scene( "Scene", getPxConstraint_Scene) + , Actors( "Actors", "actor0", "actor1", setPxConstraint_Actors, getPxConstraint_Actors) + , Flags( "Flags", setPxConstraint_Flags, getPxConstraint_Flags) + , IsValid( "IsValid", getPxConstraint_IsValid) + , BreakForce( "BreakForce", "linear", "angular", setPxConstraint_BreakForce, getPxConstraint_BreakForce) + , MinResponseThreshold( "MinResponseThreshold", setPxConstraint_MinResponseThreshold, getPxConstraint_MinResponseThreshold) + , ConcreteTypeName( "ConcreteTypeName", getPxConstraint_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxConstraintGeneratedValues::PxConstraintGeneratedValues( const PxConstraint* inSource ) + :Scene( getPxConstraint_Scene( inSource ) ) + ,Flags( getPxConstraint_Flags( inSource ) ) + ,IsValid( getPxConstraint_IsValid( inSource ) ) + ,MinResponseThreshold( getPxConstraint_MinResponseThreshold( inSource ) ) + ,ConcreteTypeName( getPxConstraint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); + getPxConstraint_Actors( inSource, Actors[0], Actors[1] ); + getPxConstraint_BreakForce( inSource, BreakForce[0], BreakForce[1] ); +} +PxU32 getPxShape_ReferenceCount( const PxShape* inObj ) { return inObj->getReferenceCount(); } +PxGeometryType::Enum getPxShape_GeometryType( const PxShape* inObj ) { return inObj->getGeometryType(); } +void setPxShape_Geometry( PxShape* inObj, const PxGeometry & inArg){ inObj->setGeometry( inArg ); } +PxGeometryHolder getPxShape_Geometry( const PxShape* inObj ) { return inObj->getGeometry(); } +void setPxShape_LocalPose( PxShape* inObj, const PxTransform & inArg){ inObj->setLocalPose( inArg ); } +PxTransform getPxShape_LocalPose( const PxShape* inObj ) { return inObj->getLocalPose(); } +void setPxShape_SimulationFilterData( PxShape* inObj, const PxFilterData & inArg){ inObj->setSimulationFilterData( inArg ); } +PxFilterData getPxShape_SimulationFilterData( const PxShape* inObj ) { return inObj->getSimulationFilterData(); } +void setPxShape_QueryFilterData( PxShape* inObj, const PxFilterData & inArg){ inObj->setQueryFilterData( inArg ); } +PxFilterData getPxShape_QueryFilterData( const PxShape* inObj ) { return inObj->getQueryFilterData(); } +PxU32 getPxShape_Materials( const PxShape* inObj, PxMaterial ** outBuffer, PxU32 inBufSize ) { return inObj->getMaterials( outBuffer, inBufSize ); } +PxU32 getNbPxShape_Materials( const PxShape* inObj ) { return inObj->getNbMaterials( ); } +void setPxShape_ContactOffset( PxShape* inObj, PxReal inArg){ inObj->setContactOffset( inArg ); } +PxReal getPxShape_ContactOffset( const PxShape* inObj ) { return inObj->getContactOffset(); } +void setPxShape_RestOffset( PxShape* inObj, PxReal inArg){ inObj->setRestOffset( inArg ); } +PxReal getPxShape_RestOffset( const PxShape* inObj ) { return inObj->getRestOffset(); } +void setPxShape_TorsionalPatchRadius( PxShape* inObj, PxReal inArg){ inObj->setTorsionalPatchRadius( inArg ); } +PxReal getPxShape_TorsionalPatchRadius( const PxShape* inObj ) { return inObj->getTorsionalPatchRadius(); } +void setPxShape_MinTorsionalPatchRadius( PxShape* inObj, PxReal inArg){ inObj->setMinTorsionalPatchRadius( inArg ); } +PxReal getPxShape_MinTorsionalPatchRadius( const PxShape* inObj ) { return inObj->getMinTorsionalPatchRadius(); } +void setPxShape_Flags( PxShape* inObj, PxShapeFlags inArg){ inObj->setFlags( inArg ); } +PxShapeFlags getPxShape_Flags( const PxShape* inObj ) { return inObj->getFlags(); } +_Bool getPxShape_IsExclusive( const PxShape* inObj ) { return inObj->isExclusive(); } +void setPxShape_Name( PxShape* inObj, const char * inArg){ inObj->setName( inArg ); } +const char * getPxShape_Name( const PxShape* inObj ) { return inObj->getName(); } +const char * getPxShape_ConcreteTypeName( const PxShape* inObj ) { return inObj->getConcreteTypeName(); } +inline void * getPxShapeUserData( const PxShape* inOwner ) { return inOwner->userData; } +inline void setPxShapeUserData( PxShape* inOwner, void * inData) { inOwner->userData = inData; } +PX_PHYSX_CORE_API PxShapeGeneratedInfo::PxShapeGeneratedInfo() + : ReferenceCount( "ReferenceCount", getPxShape_ReferenceCount) + , GeometryType( "GeometryType", getPxShape_GeometryType) + , Geometry( "Geometry", setPxShape_Geometry, getPxShape_Geometry) + , LocalPose( "LocalPose", setPxShape_LocalPose, getPxShape_LocalPose) + , SimulationFilterData( "SimulationFilterData", setPxShape_SimulationFilterData, getPxShape_SimulationFilterData) + , QueryFilterData( "QueryFilterData", setPxShape_QueryFilterData, getPxShape_QueryFilterData) + , Materials( "Materials", getPxShape_Materials, getNbPxShape_Materials ) + , ContactOffset( "ContactOffset", setPxShape_ContactOffset, getPxShape_ContactOffset) + , RestOffset( "RestOffset", setPxShape_RestOffset, getPxShape_RestOffset) + , TorsionalPatchRadius( "TorsionalPatchRadius", setPxShape_TorsionalPatchRadius, getPxShape_TorsionalPatchRadius) + , MinTorsionalPatchRadius( "MinTorsionalPatchRadius", setPxShape_MinTorsionalPatchRadius, getPxShape_MinTorsionalPatchRadius) + , Flags( "Flags", setPxShape_Flags, getPxShape_Flags) + , IsExclusive( "IsExclusive", getPxShape_IsExclusive) + , Name( "Name", setPxShape_Name, getPxShape_Name) + , ConcreteTypeName( "ConcreteTypeName", getPxShape_ConcreteTypeName) + , UserData( "UserData", setPxShapeUserData, getPxShapeUserData ) +{} +PX_PHYSX_CORE_API PxShapeGeneratedValues::PxShapeGeneratedValues( const PxShape* inSource ) + :ReferenceCount( getPxShape_ReferenceCount( inSource ) ) + ,GeometryType( getPxShape_GeometryType( inSource ) ) + ,Geometry( getPxShape_Geometry( inSource ) ) + ,LocalPose( getPxShape_LocalPose( inSource ) ) + ,SimulationFilterData( getPxShape_SimulationFilterData( inSource ) ) + ,QueryFilterData( getPxShape_QueryFilterData( inSource ) ) + ,ContactOffset( getPxShape_ContactOffset( inSource ) ) + ,RestOffset( getPxShape_RestOffset( inSource ) ) + ,TorsionalPatchRadius( getPxShape_TorsionalPatchRadius( inSource ) ) + ,MinTorsionalPatchRadius( getPxShape_MinTorsionalPatchRadius( inSource ) ) + ,Flags( getPxShape_Flags( inSource ) ) + ,IsExclusive( getPxShape_IsExclusive( inSource ) ) + ,Name( getPxShape_Name( inSource ) ) + ,ConcreteTypeName( getPxShape_ConcreteTypeName( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); +} +PxU32 getPxPruningStructure_RigidActors( const PxPruningStructure* inObj, PxRigidActor ** outBuffer, PxU32 inBufSize ) { return inObj->getRigidActors( outBuffer, inBufSize ); } +PxU32 getNbPxPruningStructure_RigidActors( const PxPruningStructure* inObj ) { return inObj->getNbRigidActors( ); } +const char * getPxPruningStructure_ConcreteTypeName( const PxPruningStructure* inObj ) { return inObj->getConcreteTypeName(); } +PX_PHYSX_CORE_API PxPruningStructureGeneratedInfo::PxPruningStructureGeneratedInfo() + : RigidActors( "RigidActors", getPxPruningStructure_RigidActors, getNbPxPruningStructure_RigidActors ) + , ConcreteTypeName( "ConcreteTypeName", getPxPruningStructure_ConcreteTypeName) +{} +PX_PHYSX_CORE_API PxPruningStructureGeneratedValues::PxPruningStructureGeneratedValues( const PxPruningStructure* inSource ) + :ConcreteTypeName( getPxPruningStructure_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +_Bool getPxTolerancesScale_IsValid( const PxTolerancesScale* inObj ) { return inObj->isValid(); } +inline PxReal getPxTolerancesScaleLength( const PxTolerancesScale* inOwner ) { return inOwner->length; } +inline void setPxTolerancesScaleLength( PxTolerancesScale* inOwner, PxReal inData) { inOwner->length = inData; } +inline PxReal getPxTolerancesScaleSpeed( const PxTolerancesScale* inOwner ) { return inOwner->speed; } +inline void setPxTolerancesScaleSpeed( PxTolerancesScale* inOwner, PxReal inData) { inOwner->speed = inData; } +PX_PHYSX_CORE_API PxTolerancesScaleGeneratedInfo::PxTolerancesScaleGeneratedInfo() + : IsValid( "IsValid", getPxTolerancesScale_IsValid) + , Length( "Length", setPxTolerancesScaleLength, getPxTolerancesScaleLength ) + , Speed( "Speed", setPxTolerancesScaleSpeed, getPxTolerancesScaleSpeed ) +{} +PX_PHYSX_CORE_API PxTolerancesScaleGeneratedValues::PxTolerancesScaleGeneratedValues( const PxTolerancesScale* inSource ) + :IsValid( getPxTolerancesScale_IsValid( inSource ) ) + ,Length( inSource->length ) + ,Speed( inSource->speed ) +{ + PX_UNUSED(inSource); +} +PX_PHYSX_CORE_API PxGeometryGeneratedInfo::PxGeometryGeneratedInfo() +{} +PX_PHYSX_CORE_API PxGeometryGeneratedValues::PxGeometryGeneratedValues( const PxGeometry* inSource ) +{ + PX_UNUSED(inSource); +} +inline PxVec3 getPxBoxGeometryHalfExtents( const PxBoxGeometry* inOwner ) { return inOwner->halfExtents; } +inline void setPxBoxGeometryHalfExtents( PxBoxGeometry* inOwner, PxVec3 inData) { inOwner->halfExtents = inData; } +PX_PHYSX_CORE_API PxBoxGeometryGeneratedInfo::PxBoxGeometryGeneratedInfo() + : HalfExtents( "HalfExtents", setPxBoxGeometryHalfExtents, getPxBoxGeometryHalfExtents ) +{} +PX_PHYSX_CORE_API PxBoxGeometryGeneratedValues::PxBoxGeometryGeneratedValues( const PxBoxGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,HalfExtents( inSource->halfExtents ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxCapsuleGeometryRadius( const PxCapsuleGeometry* inOwner ) { return inOwner->radius; } +inline void setPxCapsuleGeometryRadius( PxCapsuleGeometry* inOwner, PxReal inData) { inOwner->radius = inData; } +inline PxReal getPxCapsuleGeometryHalfHeight( const PxCapsuleGeometry* inOwner ) { return inOwner->halfHeight; } +inline void setPxCapsuleGeometryHalfHeight( PxCapsuleGeometry* inOwner, PxReal inData) { inOwner->halfHeight = inData; } +PX_PHYSX_CORE_API PxCapsuleGeometryGeneratedInfo::PxCapsuleGeometryGeneratedInfo() + : Radius( "Radius", setPxCapsuleGeometryRadius, getPxCapsuleGeometryRadius ) + , HalfHeight( "HalfHeight", setPxCapsuleGeometryHalfHeight, getPxCapsuleGeometryHalfHeight ) +{} +PX_PHYSX_CORE_API PxCapsuleGeometryGeneratedValues::PxCapsuleGeometryGeneratedValues( const PxCapsuleGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,Radius( inSource->radius ) + ,HalfHeight( inSource->halfHeight ) +{ + PX_UNUSED(inSource); +} +inline PxVec3 getPxMeshScaleScale( const PxMeshScale* inOwner ) { return inOwner->scale; } +inline void setPxMeshScaleScale( PxMeshScale* inOwner, PxVec3 inData) { inOwner->scale = inData; } +inline PxQuat getPxMeshScaleRotation( const PxMeshScale* inOwner ) { return inOwner->rotation; } +inline void setPxMeshScaleRotation( PxMeshScale* inOwner, PxQuat inData) { inOwner->rotation = inData; } +PX_PHYSX_CORE_API PxMeshScaleGeneratedInfo::PxMeshScaleGeneratedInfo() + : Scale( "Scale", setPxMeshScaleScale, getPxMeshScaleScale ) + , Rotation( "Rotation", setPxMeshScaleRotation, getPxMeshScaleRotation ) +{} +PX_PHYSX_CORE_API PxMeshScaleGeneratedValues::PxMeshScaleGeneratedValues( const PxMeshScale* inSource ) + :Scale( inSource->scale ) + ,Rotation( inSource->rotation ) +{ + PX_UNUSED(inSource); +} +inline PxMeshScale getPxConvexMeshGeometryScale( const PxConvexMeshGeometry* inOwner ) { return inOwner->scale; } +inline void setPxConvexMeshGeometryScale( PxConvexMeshGeometry* inOwner, PxMeshScale inData) { inOwner->scale = inData; } +inline PxConvexMesh * getPxConvexMeshGeometryConvexMesh( const PxConvexMeshGeometry* inOwner ) { return inOwner->convexMesh; } +inline void setPxConvexMeshGeometryConvexMesh( PxConvexMeshGeometry* inOwner, PxConvexMesh * inData) { inOwner->convexMesh = inData; } +inline PxConvexMeshGeometryFlags getPxConvexMeshGeometryMeshFlags( const PxConvexMeshGeometry* inOwner ) { return inOwner->meshFlags; } +inline void setPxConvexMeshGeometryMeshFlags( PxConvexMeshGeometry* inOwner, PxConvexMeshGeometryFlags inData) { inOwner->meshFlags = inData; } +PX_PHYSX_CORE_API PxConvexMeshGeometryGeneratedInfo::PxConvexMeshGeometryGeneratedInfo() + : Scale( "Scale", setPxConvexMeshGeometryScale, getPxConvexMeshGeometryScale ) + , ConvexMesh( "ConvexMesh", setPxConvexMeshGeometryConvexMesh, getPxConvexMeshGeometryConvexMesh ) + , MeshFlags( "MeshFlags", setPxConvexMeshGeometryMeshFlags, getPxConvexMeshGeometryMeshFlags ) +{} +PX_PHYSX_CORE_API PxConvexMeshGeometryGeneratedValues::PxConvexMeshGeometryGeneratedValues( const PxConvexMeshGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,Scale( inSource->scale ) + ,ConvexMesh( inSource->convexMesh ) + ,MeshFlags( inSource->meshFlags ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxSphereGeometryRadius( const PxSphereGeometry* inOwner ) { return inOwner->radius; } +inline void setPxSphereGeometryRadius( PxSphereGeometry* inOwner, PxReal inData) { inOwner->radius = inData; } +PX_PHYSX_CORE_API PxSphereGeometryGeneratedInfo::PxSphereGeometryGeneratedInfo() + : Radius( "Radius", setPxSphereGeometryRadius, getPxSphereGeometryRadius ) +{} +PX_PHYSX_CORE_API PxSphereGeometryGeneratedValues::PxSphereGeometryGeneratedValues( const PxSphereGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,Radius( inSource->radius ) +{ + PX_UNUSED(inSource); +} +PX_PHYSX_CORE_API PxPlaneGeometryGeneratedInfo::PxPlaneGeometryGeneratedInfo() +{} +PX_PHYSX_CORE_API PxPlaneGeometryGeneratedValues::PxPlaneGeometryGeneratedValues( const PxPlaneGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) +{ + PX_UNUSED(inSource); +} +inline PxMeshScale getPxTriangleMeshGeometryScale( const PxTriangleMeshGeometry* inOwner ) { return inOwner->scale; } +inline void setPxTriangleMeshGeometryScale( PxTriangleMeshGeometry* inOwner, PxMeshScale inData) { inOwner->scale = inData; } +inline PxMeshGeometryFlags getPxTriangleMeshGeometryMeshFlags( const PxTriangleMeshGeometry* inOwner ) { return inOwner->meshFlags; } +inline void setPxTriangleMeshGeometryMeshFlags( PxTriangleMeshGeometry* inOwner, PxMeshGeometryFlags inData) { inOwner->meshFlags = inData; } +inline PxTriangleMesh * getPxTriangleMeshGeometryTriangleMesh( const PxTriangleMeshGeometry* inOwner ) { return inOwner->triangleMesh; } +inline void setPxTriangleMeshGeometryTriangleMesh( PxTriangleMeshGeometry* inOwner, PxTriangleMesh * inData) { inOwner->triangleMesh = inData; } +PX_PHYSX_CORE_API PxTriangleMeshGeometryGeneratedInfo::PxTriangleMeshGeometryGeneratedInfo() + : Scale( "Scale", setPxTriangleMeshGeometryScale, getPxTriangleMeshGeometryScale ) + , MeshFlags( "MeshFlags", setPxTriangleMeshGeometryMeshFlags, getPxTriangleMeshGeometryMeshFlags ) + , TriangleMesh( "TriangleMesh", setPxTriangleMeshGeometryTriangleMesh, getPxTriangleMeshGeometryTriangleMesh ) +{} +PX_PHYSX_CORE_API PxTriangleMeshGeometryGeneratedValues::PxTriangleMeshGeometryGeneratedValues( const PxTriangleMeshGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,Scale( inSource->scale ) + ,MeshFlags( inSource->meshFlags ) + ,TriangleMesh( inSource->triangleMesh ) +{ + PX_UNUSED(inSource); +} +inline PxHeightField * getPxHeightFieldGeometryHeightField( const PxHeightFieldGeometry* inOwner ) { return inOwner->heightField; } +inline void setPxHeightFieldGeometryHeightField( PxHeightFieldGeometry* inOwner, PxHeightField * inData) { inOwner->heightField = inData; } +inline PxReal getPxHeightFieldGeometryHeightScale( const PxHeightFieldGeometry* inOwner ) { return inOwner->heightScale; } +inline void setPxHeightFieldGeometryHeightScale( PxHeightFieldGeometry* inOwner, PxReal inData) { inOwner->heightScale = inData; } +inline PxReal getPxHeightFieldGeometryRowScale( const PxHeightFieldGeometry* inOwner ) { return inOwner->rowScale; } +inline void setPxHeightFieldGeometryRowScale( PxHeightFieldGeometry* inOwner, PxReal inData) { inOwner->rowScale = inData; } +inline PxReal getPxHeightFieldGeometryColumnScale( const PxHeightFieldGeometry* inOwner ) { return inOwner->columnScale; } +inline void setPxHeightFieldGeometryColumnScale( PxHeightFieldGeometry* inOwner, PxReal inData) { inOwner->columnScale = inData; } +inline PxMeshGeometryFlags getPxHeightFieldGeometryHeightFieldFlags( const PxHeightFieldGeometry* inOwner ) { return inOwner->heightFieldFlags; } +inline void setPxHeightFieldGeometryHeightFieldFlags( PxHeightFieldGeometry* inOwner, PxMeshGeometryFlags inData) { inOwner->heightFieldFlags = inData; } +PX_PHYSX_CORE_API PxHeightFieldGeometryGeneratedInfo::PxHeightFieldGeometryGeneratedInfo() + : HeightField( "HeightField", setPxHeightFieldGeometryHeightField, getPxHeightFieldGeometryHeightField ) + , HeightScale( "HeightScale", setPxHeightFieldGeometryHeightScale, getPxHeightFieldGeometryHeightScale ) + , RowScale( "RowScale", setPxHeightFieldGeometryRowScale, getPxHeightFieldGeometryRowScale ) + , ColumnScale( "ColumnScale", setPxHeightFieldGeometryColumnScale, getPxHeightFieldGeometryColumnScale ) + , HeightFieldFlags( "HeightFieldFlags", setPxHeightFieldGeometryHeightFieldFlags, getPxHeightFieldGeometryHeightFieldFlags ) +{} +PX_PHYSX_CORE_API PxHeightFieldGeometryGeneratedValues::PxHeightFieldGeometryGeneratedValues( const PxHeightFieldGeometry* inSource ) + :PxGeometryGeneratedValues( inSource ) + ,HeightField( inSource->heightField ) + ,HeightScale( inSource->heightScale ) + ,RowScale( inSource->rowScale ) + ,ColumnScale( inSource->columnScale ) + ,HeightFieldFlags( inSource->heightFieldFlags ) +{ + PX_UNUSED(inSource); +} +inline PxU32 getPxHeightFieldDescNbRows( const PxHeightFieldDesc* inOwner ) { return inOwner->nbRows; } +inline void setPxHeightFieldDescNbRows( PxHeightFieldDesc* inOwner, PxU32 inData) { inOwner->nbRows = inData; } +inline PxU32 getPxHeightFieldDescNbColumns( const PxHeightFieldDesc* inOwner ) { return inOwner->nbColumns; } +inline void setPxHeightFieldDescNbColumns( PxHeightFieldDesc* inOwner, PxU32 inData) { inOwner->nbColumns = inData; } +inline PxHeightFieldFormat::Enum getPxHeightFieldDescFormat( const PxHeightFieldDesc* inOwner ) { return inOwner->format; } +inline void setPxHeightFieldDescFormat( PxHeightFieldDesc* inOwner, PxHeightFieldFormat::Enum inData) { inOwner->format = inData; } +inline PxStridedData getPxHeightFieldDescSamples( const PxHeightFieldDesc* inOwner ) { return inOwner->samples; } +inline void setPxHeightFieldDescSamples( PxHeightFieldDesc* inOwner, PxStridedData inData) { inOwner->samples = inData; } +inline PxReal getPxHeightFieldDescConvexEdgeThreshold( const PxHeightFieldDesc* inOwner ) { return inOwner->convexEdgeThreshold; } +inline void setPxHeightFieldDescConvexEdgeThreshold( PxHeightFieldDesc* inOwner, PxReal inData) { inOwner->convexEdgeThreshold = inData; } +inline PxHeightFieldFlags getPxHeightFieldDescFlags( const PxHeightFieldDesc* inOwner ) { return inOwner->flags; } +inline void setPxHeightFieldDescFlags( PxHeightFieldDesc* inOwner, PxHeightFieldFlags inData) { inOwner->flags = inData; } +PX_PHYSX_CORE_API PxHeightFieldDescGeneratedInfo::PxHeightFieldDescGeneratedInfo() + : NbRows( "NbRows", setPxHeightFieldDescNbRows, getPxHeightFieldDescNbRows ) + , NbColumns( "NbColumns", setPxHeightFieldDescNbColumns, getPxHeightFieldDescNbColumns ) + , Format( "Format", setPxHeightFieldDescFormat, getPxHeightFieldDescFormat ) + , Samples( "Samples", setPxHeightFieldDescSamples, getPxHeightFieldDescSamples ) + , ConvexEdgeThreshold( "ConvexEdgeThreshold", setPxHeightFieldDescConvexEdgeThreshold, getPxHeightFieldDescConvexEdgeThreshold ) + , Flags( "Flags", setPxHeightFieldDescFlags, getPxHeightFieldDescFlags ) +{} +PX_PHYSX_CORE_API PxHeightFieldDescGeneratedValues::PxHeightFieldDescGeneratedValues( const PxHeightFieldDesc* inSource ) + :NbRows( inSource->nbRows ) + ,NbColumns( inSource->nbColumns ) + ,Format( inSource->format ) + ,Samples( inSource->samples ) + ,ConvexEdgeThreshold( inSource->convexEdgeThreshold ) + ,Flags( inSource->flags ) +{ + PX_UNUSED(inSource); +} +void setPxArticulationJointReducedCoordinate_JointType( PxArticulationJointReducedCoordinate* inObj, PxArticulationJointType::Enum inArg){ inObj->setJointType( inArg ); } +PxArticulationJointType::Enum getPxArticulationJointReducedCoordinate_JointType( const PxArticulationJointReducedCoordinate* inObj ) { return inObj->getJointType(); } +void setPxArticulationJointReducedCoordinate_Motion( PxArticulationJointReducedCoordinate* inObj, PxArticulationAxis::Enum inIndex, PxArticulationMotion::Enum inArg ){ inObj->setMotion( inIndex, inArg ); } +PxArticulationMotion::Enum getPxArticulationJointReducedCoordinate_Motion( const PxArticulationJointReducedCoordinate* inObj, PxArticulationAxis::Enum inIndex ) { return inObj->getMotion( inIndex ); } +void setPxArticulationJointReducedCoordinate_FrictionCoefficient( PxArticulationJointReducedCoordinate* inObj, const PxReal inArg){ inObj->setFrictionCoefficient( inArg ); } +PxReal getPxArticulationJointReducedCoordinate_FrictionCoefficient( const PxArticulationJointReducedCoordinate* inObj ) { return inObj->getFrictionCoefficient(); } +const char * getPxArticulationJointReducedCoordinate_ConcreteTypeName( const PxArticulationJointReducedCoordinate* inObj ) { return inObj->getConcreteTypeName(); } +void setPxArticulationJointReducedCoordinate_MaxJointVelocity( PxArticulationJointReducedCoordinate* inObj, const PxReal inArg){ inObj->setMaxJointVelocity( inArg ); } +PxReal getPxArticulationJointReducedCoordinate_MaxJointVelocity( const PxArticulationJointReducedCoordinate* inObj ) { return inObj->getMaxJointVelocity(); } +PX_PHYSX_CORE_API PxArticulationJointReducedCoordinateGeneratedInfo::PxArticulationJointReducedCoordinateGeneratedInfo() + : JointType( "JointType", setPxArticulationJointReducedCoordinate_JointType, getPxArticulationJointReducedCoordinate_JointType) + , Motion( "Motion", setPxArticulationJointReducedCoordinate_Motion, getPxArticulationJointReducedCoordinate_Motion) + , FrictionCoefficient( "FrictionCoefficient", setPxArticulationJointReducedCoordinate_FrictionCoefficient, getPxArticulationJointReducedCoordinate_FrictionCoefficient) + , ConcreteTypeName( "ConcreteTypeName", getPxArticulationJointReducedCoordinate_ConcreteTypeName) + , MaxJointVelocity( "MaxJointVelocity", setPxArticulationJointReducedCoordinate_MaxJointVelocity, getPxArticulationJointReducedCoordinate_MaxJointVelocity) +{} +PX_PHYSX_CORE_API PxArticulationJointReducedCoordinateGeneratedValues::PxArticulationJointReducedCoordinateGeneratedValues( const PxArticulationJointReducedCoordinate* inSource ) + :PxArticulationJointBaseGeneratedValues( inSource ) + ,JointType( getPxArticulationJointReducedCoordinate_JointType( inSource ) ) + ,FrictionCoefficient( getPxArticulationJointReducedCoordinate_FrictionCoefficient( inSource ) ) + ,ConcreteTypeName( getPxArticulationJointReducedCoordinate_ConcreteTypeName( inSource ) ) + ,MaxJointVelocity( getPxArticulationJointReducedCoordinate_MaxJointVelocity( inSource ) ) +{ + PX_UNUSED(inSource); + for ( PxU32 idx = 0; idx < static_cast( physx::PxArticulationAxis::eCOUNT ); ++idx ) + Motion[idx] = getPxArticulationJointReducedCoordinate_Motion( inSource, static_cast< PxArticulationAxis::Enum >( idx ) ); +} +PxSceneFlags getPxScene_Flags( const PxScene* inObj ) { return inObj->getFlags(); } +void setPxScene_Limits( PxScene* inObj, const PxSceneLimits & inArg){ inObj->setLimits( inArg ); } +PxSceneLimits getPxScene_Limits( const PxScene* inObj ) { return inObj->getLimits(); } +PxU32 getPxScene_Timestamp( const PxScene* inObj ) { return inObj->getTimestamp(); } +PxU32 getPxScene_Actors( const PxScene* inObj, PxActorTypeFlags inFilter, PxActor ** outBuffer, PxU32 inBufSize ) { return inObj->getActors( inFilter, outBuffer, inBufSize ); } +PxU32 getNbPxScene_Actors( const PxScene* inObj, PxActorTypeFlags inFilter ) { return inObj->getNbActors( inFilter ); } +PxU32 getPxScene_Articulations( const PxScene* inObj, PxArticulationBase ** outBuffer, PxU32 inBufSize ) { return inObj->getArticulations( outBuffer, inBufSize ); } +PxU32 getNbPxScene_Articulations( const PxScene* inObj ) { return inObj->getNbArticulations( ); } +PxU32 getPxScene_Constraints( const PxScene* inObj, PxConstraint ** outBuffer, PxU32 inBufSize ) { return inObj->getConstraints( outBuffer, inBufSize ); } +PxU32 getNbPxScene_Constraints( const PxScene* inObj ) { return inObj->getNbConstraints( ); } +PxU32 getPxScene_Aggregates( const PxScene* inObj, PxAggregate ** outBuffer, PxU32 inBufSize ) { return inObj->getAggregates( outBuffer, inBufSize ); } +PxU32 getNbPxScene_Aggregates( const PxScene* inObj ) { return inObj->getNbAggregates( ); } +PxCpuDispatcher * getPxScene_CpuDispatcher( const PxScene* inObj ) { return inObj->getCpuDispatcher(); } +PxGpuDispatcher * getPxScene_GpuDispatcher( const PxScene* inObj ) { return inObj->getGpuDispatcher(); } +void setPxScene_SimulationEventCallback( PxScene* inObj, PxSimulationEventCallback * inArg){ inObj->setSimulationEventCallback( inArg ); } +PxSimulationEventCallback * getPxScene_SimulationEventCallback( const PxScene* inObj ) { return inObj->getSimulationEventCallback(); } +void setPxScene_ContactModifyCallback( PxScene* inObj, PxContactModifyCallback * inArg){ inObj->setContactModifyCallback( inArg ); } +PxContactModifyCallback * getPxScene_ContactModifyCallback( const PxScene* inObj ) { return inObj->getContactModifyCallback(); } +void setPxScene_CCDContactModifyCallback( PxScene* inObj, PxCCDContactModifyCallback * inArg){ inObj->setCCDContactModifyCallback( inArg ); } +PxCCDContactModifyCallback * getPxScene_CCDContactModifyCallback( const PxScene* inObj ) { return inObj->getCCDContactModifyCallback(); } +void setPxScene_BroadPhaseCallback( PxScene* inObj, PxBroadPhaseCallback * inArg){ inObj->setBroadPhaseCallback( inArg ); } +PxBroadPhaseCallback * getPxScene_BroadPhaseCallback( const PxScene* inObj ) { return inObj->getBroadPhaseCallback(); } +PxU32 getPxScene_FilterShaderDataSize( const PxScene* inObj ) { return inObj->getFilterShaderDataSize(); } +PxSimulationFilterShader getPxScene_FilterShader( const PxScene* inObj ) { return inObj->getFilterShader(); } +PxSimulationFilterCallback * getPxScene_FilterCallback( const PxScene* inObj ) { return inObj->getFilterCallback(); } +void setPxScene_Gravity( PxScene* inObj, const PxVec3 & inArg){ inObj->setGravity( inArg ); } +PxVec3 getPxScene_Gravity( const PxScene* inObj ) { return inObj->getGravity(); } +void setPxScene_BounceThresholdVelocity( PxScene* inObj, const PxReal inArg){ inObj->setBounceThresholdVelocity( inArg ); } +PxReal getPxScene_BounceThresholdVelocity( const PxScene* inObj ) { return inObj->getBounceThresholdVelocity(); } +void setPxScene_CCDMaxPasses( PxScene* inObj, PxU32 inArg){ inObj->setCCDMaxPasses( inArg ); } +PxU32 getPxScene_CCDMaxPasses( const PxScene* inObj ) { return inObj->getCCDMaxPasses(); } +PxReal getPxScene_FrictionOffsetThreshold( const PxScene* inObj ) { return inObj->getFrictionOffsetThreshold(); } +void setPxScene_FrictionType( PxScene* inObj, PxFrictionType::Enum inArg){ inObj->setFrictionType( inArg ); } +PxFrictionType::Enum getPxScene_FrictionType( const PxScene* inObj ) { return inObj->getFrictionType(); } +void setPxScene_VisualizationCullingBox( PxScene* inObj, const PxBounds3 & inArg){ inObj->setVisualizationCullingBox( inArg ); } +PxBounds3 getPxScene_VisualizationCullingBox( const PxScene* inObj ) { return inObj->getVisualizationCullingBox(); } +PxPruningStructureType::Enum getPxScene_StaticStructure( const PxScene* inObj ) { return inObj->getStaticStructure(); } +PxPruningStructureType::Enum getPxScene_DynamicStructure( const PxScene* inObj ) { return inObj->getDynamicStructure(); } +void setPxScene_DynamicTreeRebuildRateHint( PxScene* inObj, PxU32 inArg){ inObj->setDynamicTreeRebuildRateHint( inArg ); } +PxU32 getPxScene_DynamicTreeRebuildRateHint( const PxScene* inObj ) { return inObj->getDynamicTreeRebuildRateHint(); } +void setPxScene_SceneQueryUpdateMode( PxScene* inObj, PxSceneQueryUpdateMode::Enum inArg){ inObj->setSceneQueryUpdateMode( inArg ); } +PxSceneQueryUpdateMode::Enum getPxScene_SceneQueryUpdateMode( const PxScene* inObj ) { return inObj->getSceneQueryUpdateMode(); } +PxU32 getPxScene_SceneQueryStaticTimestamp( const PxScene* inObj ) { return inObj->getSceneQueryStaticTimestamp(); } +PxBroadPhaseType::Enum getPxScene_BroadPhaseType( const PxScene* inObj ) { return inObj->getBroadPhaseType(); } +PxU32 getPxScene_BroadPhaseRegions( const PxScene* inObj, PxBroadPhaseRegionInfo* outBuffer, PxU32 inBufSize ) { return inObj->getBroadPhaseRegions( outBuffer, inBufSize ); } +PxU32 getNbPxScene_BroadPhaseRegions( const PxScene* inObj ) { return inObj->getNbBroadPhaseRegions( ); } +PxTaskManager * getPxScene_TaskManager( const PxScene* inObj ) { return inObj->getTaskManager(); } +void setPxScene_NbContactDataBlocks( PxScene* inObj, PxU32 inArg){ inObj->setNbContactDataBlocks( inArg ); } +PxU32 getPxScene_MaxNbContactDataBlocksUsed( const PxScene* inObj ) { return inObj->getMaxNbContactDataBlocksUsed(); } +PxU32 getPxScene_ContactReportStreamBufferSize( const PxScene* inObj ) { return inObj->getContactReportStreamBufferSize(); } +void setPxScene_SolverBatchSize( PxScene* inObj, PxU32 inArg){ inObj->setSolverBatchSize( inArg ); } +PxU32 getPxScene_SolverBatchSize( const PxScene* inObj ) { return inObj->getSolverBatchSize(); } +PxReal getPxScene_WakeCounterResetValue( const PxScene* inObj ) { return inObj->getWakeCounterResetValue(); } +inline void * getPxSceneUserData( const PxScene* inOwner ) { return inOwner->userData; } +inline void setPxSceneUserData( PxScene* inOwner, void * inData) { inOwner->userData = inData; } +PX_PHYSX_CORE_API PxSceneGeneratedInfo::PxSceneGeneratedInfo() + : Flags( "Flags", getPxScene_Flags) + , Limits( "Limits", setPxScene_Limits, getPxScene_Limits) + , Timestamp( "Timestamp", getPxScene_Timestamp) + , Actors( "Actors", getPxScene_Actors, getNbPxScene_Actors ) + , Articulations( "Articulations", getPxScene_Articulations, getNbPxScene_Articulations ) + , Constraints( "Constraints", getPxScene_Constraints, getNbPxScene_Constraints ) + , Aggregates( "Aggregates", getPxScene_Aggregates, getNbPxScene_Aggregates ) + , CpuDispatcher( "CpuDispatcher", getPxScene_CpuDispatcher) + , GpuDispatcher( "GpuDispatcher", getPxScene_GpuDispatcher) + , SimulationEventCallback( "SimulationEventCallback", setPxScene_SimulationEventCallback, getPxScene_SimulationEventCallback) + , ContactModifyCallback( "ContactModifyCallback", setPxScene_ContactModifyCallback, getPxScene_ContactModifyCallback) + , CCDContactModifyCallback( "CCDContactModifyCallback", setPxScene_CCDContactModifyCallback, getPxScene_CCDContactModifyCallback) + , BroadPhaseCallback( "BroadPhaseCallback", setPxScene_BroadPhaseCallback, getPxScene_BroadPhaseCallback) + , FilterShaderDataSize( "FilterShaderDataSize", getPxScene_FilterShaderDataSize) + , FilterShader( "FilterShader", getPxScene_FilterShader) + , FilterCallback( "FilterCallback", getPxScene_FilterCallback) + , Gravity( "Gravity", setPxScene_Gravity, getPxScene_Gravity) + , BounceThresholdVelocity( "BounceThresholdVelocity", setPxScene_BounceThresholdVelocity, getPxScene_BounceThresholdVelocity) + , CCDMaxPasses( "CCDMaxPasses", setPxScene_CCDMaxPasses, getPxScene_CCDMaxPasses) + , FrictionOffsetThreshold( "FrictionOffsetThreshold", getPxScene_FrictionOffsetThreshold) + , FrictionType( "FrictionType", setPxScene_FrictionType, getPxScene_FrictionType) + , VisualizationCullingBox( "VisualizationCullingBox", setPxScene_VisualizationCullingBox, getPxScene_VisualizationCullingBox) + , StaticStructure( "StaticStructure", getPxScene_StaticStructure) + , DynamicStructure( "DynamicStructure", getPxScene_DynamicStructure) + , DynamicTreeRebuildRateHint( "DynamicTreeRebuildRateHint", setPxScene_DynamicTreeRebuildRateHint, getPxScene_DynamicTreeRebuildRateHint) + , SceneQueryUpdateMode( "SceneQueryUpdateMode", setPxScene_SceneQueryUpdateMode, getPxScene_SceneQueryUpdateMode) + , SceneQueryStaticTimestamp( "SceneQueryStaticTimestamp", getPxScene_SceneQueryStaticTimestamp) + , BroadPhaseType( "BroadPhaseType", getPxScene_BroadPhaseType) + , BroadPhaseRegions( "BroadPhaseRegions", getPxScene_BroadPhaseRegions, getNbPxScene_BroadPhaseRegions ) + , TaskManager( "TaskManager", getPxScene_TaskManager) + , NbContactDataBlocks( "NbContactDataBlocks", setPxScene_NbContactDataBlocks) + , MaxNbContactDataBlocksUsed( "MaxNbContactDataBlocksUsed", getPxScene_MaxNbContactDataBlocksUsed) + , ContactReportStreamBufferSize( "ContactReportStreamBufferSize", getPxScene_ContactReportStreamBufferSize) + , SolverBatchSize( "SolverBatchSize", setPxScene_SolverBatchSize, getPxScene_SolverBatchSize) + , WakeCounterResetValue( "WakeCounterResetValue", getPxScene_WakeCounterResetValue) + , UserData( "UserData", setPxSceneUserData, getPxSceneUserData ) +{} +PX_PHYSX_CORE_API PxSceneGeneratedValues::PxSceneGeneratedValues( const PxScene* inSource ) + :Flags( getPxScene_Flags( inSource ) ) + ,Limits( getPxScene_Limits( inSource ) ) + ,Timestamp( getPxScene_Timestamp( inSource ) ) + ,CpuDispatcher( getPxScene_CpuDispatcher( inSource ) ) + ,GpuDispatcher( getPxScene_GpuDispatcher( inSource ) ) + ,SimulationEventCallback( getPxScene_SimulationEventCallback( inSource ) ) + ,ContactModifyCallback( getPxScene_ContactModifyCallback( inSource ) ) + ,CCDContactModifyCallback( getPxScene_CCDContactModifyCallback( inSource ) ) + ,BroadPhaseCallback( getPxScene_BroadPhaseCallback( inSource ) ) + ,FilterShaderDataSize( getPxScene_FilterShaderDataSize( inSource ) ) + ,FilterShader( getPxScene_FilterShader( inSource ) ) + ,FilterCallback( getPxScene_FilterCallback( inSource ) ) + ,Gravity( getPxScene_Gravity( inSource ) ) + ,BounceThresholdVelocity( getPxScene_BounceThresholdVelocity( inSource ) ) + ,CCDMaxPasses( getPxScene_CCDMaxPasses( inSource ) ) + ,FrictionOffsetThreshold( getPxScene_FrictionOffsetThreshold( inSource ) ) + ,FrictionType( getPxScene_FrictionType( inSource ) ) + ,VisualizationCullingBox( getPxScene_VisualizationCullingBox( inSource ) ) + ,StaticStructure( getPxScene_StaticStructure( inSource ) ) + ,DynamicStructure( getPxScene_DynamicStructure( inSource ) ) + ,DynamicTreeRebuildRateHint( getPxScene_DynamicTreeRebuildRateHint( inSource ) ) + ,SceneQueryUpdateMode( getPxScene_SceneQueryUpdateMode( inSource ) ) + ,SceneQueryStaticTimestamp( getPxScene_SceneQueryStaticTimestamp( inSource ) ) + ,BroadPhaseType( getPxScene_BroadPhaseType( inSource ) ) + ,TaskManager( getPxScene_TaskManager( inSource ) ) + ,MaxNbContactDataBlocksUsed( getPxScene_MaxNbContactDataBlocksUsed( inSource ) ) + ,ContactReportStreamBufferSize( getPxScene_ContactReportStreamBufferSize( inSource ) ) + ,SolverBatchSize( getPxScene_SolverBatchSize( inSource ) ) + ,WakeCounterResetValue( getPxScene_WakeCounterResetValue( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); + inSource->getSimulationStatistics(SimulationStatistics); +} +inline PxU32 getPxSceneLimitsMaxNbActors( const PxSceneLimits* inOwner ) { return inOwner->maxNbActors; } +inline void setPxSceneLimitsMaxNbActors( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbActors = inData; } +inline PxU32 getPxSceneLimitsMaxNbBodies( const PxSceneLimits* inOwner ) { return inOwner->maxNbBodies; } +inline void setPxSceneLimitsMaxNbBodies( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbBodies = inData; } +inline PxU32 getPxSceneLimitsMaxNbStaticShapes( const PxSceneLimits* inOwner ) { return inOwner->maxNbStaticShapes; } +inline void setPxSceneLimitsMaxNbStaticShapes( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbStaticShapes = inData; } +inline PxU32 getPxSceneLimitsMaxNbDynamicShapes( const PxSceneLimits* inOwner ) { return inOwner->maxNbDynamicShapes; } +inline void setPxSceneLimitsMaxNbDynamicShapes( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbDynamicShapes = inData; } +inline PxU32 getPxSceneLimitsMaxNbAggregates( const PxSceneLimits* inOwner ) { return inOwner->maxNbAggregates; } +inline void setPxSceneLimitsMaxNbAggregates( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbAggregates = inData; } +inline PxU32 getPxSceneLimitsMaxNbConstraints( const PxSceneLimits* inOwner ) { return inOwner->maxNbConstraints; } +inline void setPxSceneLimitsMaxNbConstraints( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbConstraints = inData; } +inline PxU32 getPxSceneLimitsMaxNbRegions( const PxSceneLimits* inOwner ) { return inOwner->maxNbRegions; } +inline void setPxSceneLimitsMaxNbRegions( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbRegions = inData; } +inline PxU32 getPxSceneLimitsMaxNbBroadPhaseOverlaps( const PxSceneLimits* inOwner ) { return inOwner->maxNbBroadPhaseOverlaps; } +inline void setPxSceneLimitsMaxNbBroadPhaseOverlaps( PxSceneLimits* inOwner, PxU32 inData) { inOwner->maxNbBroadPhaseOverlaps = inData; } +PX_PHYSX_CORE_API PxSceneLimitsGeneratedInfo::PxSceneLimitsGeneratedInfo() + : MaxNbActors( "MaxNbActors", setPxSceneLimitsMaxNbActors, getPxSceneLimitsMaxNbActors ) + , MaxNbBodies( "MaxNbBodies", setPxSceneLimitsMaxNbBodies, getPxSceneLimitsMaxNbBodies ) + , MaxNbStaticShapes( "MaxNbStaticShapes", setPxSceneLimitsMaxNbStaticShapes, getPxSceneLimitsMaxNbStaticShapes ) + , MaxNbDynamicShapes( "MaxNbDynamicShapes", setPxSceneLimitsMaxNbDynamicShapes, getPxSceneLimitsMaxNbDynamicShapes ) + , MaxNbAggregates( "MaxNbAggregates", setPxSceneLimitsMaxNbAggregates, getPxSceneLimitsMaxNbAggregates ) + , MaxNbConstraints( "MaxNbConstraints", setPxSceneLimitsMaxNbConstraints, getPxSceneLimitsMaxNbConstraints ) + , MaxNbRegions( "MaxNbRegions", setPxSceneLimitsMaxNbRegions, getPxSceneLimitsMaxNbRegions ) + , MaxNbBroadPhaseOverlaps( "MaxNbBroadPhaseOverlaps", setPxSceneLimitsMaxNbBroadPhaseOverlaps, getPxSceneLimitsMaxNbBroadPhaseOverlaps ) +{} +PX_PHYSX_CORE_API PxSceneLimitsGeneratedValues::PxSceneLimitsGeneratedValues( const PxSceneLimits* inSource ) + :MaxNbActors( inSource->maxNbActors ) + ,MaxNbBodies( inSource->maxNbBodies ) + ,MaxNbStaticShapes( inSource->maxNbStaticShapes ) + ,MaxNbDynamicShapes( inSource->maxNbDynamicShapes ) + ,MaxNbAggregates( inSource->maxNbAggregates ) + ,MaxNbConstraints( inSource->maxNbConstraints ) + ,MaxNbRegions( inSource->maxNbRegions ) + ,MaxNbBroadPhaseOverlaps( inSource->maxNbBroadPhaseOverlaps ) +{ + PX_UNUSED(inSource); +} +inline PxU32 getPxgDynamicsMemoryConfigConstraintBufferCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->constraintBufferCapacity; } +inline void setPxgDynamicsMemoryConfigConstraintBufferCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->constraintBufferCapacity = inData; } +inline PxU32 getPxgDynamicsMemoryConfigContactBufferCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->contactBufferCapacity; } +inline void setPxgDynamicsMemoryConfigContactBufferCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->contactBufferCapacity = inData; } +inline PxU32 getPxgDynamicsMemoryConfigTempBufferCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->tempBufferCapacity; } +inline void setPxgDynamicsMemoryConfigTempBufferCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->tempBufferCapacity = inData; } +inline PxU32 getPxgDynamicsMemoryConfigContactStreamSize( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->contactStreamSize; } +inline void setPxgDynamicsMemoryConfigContactStreamSize( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->contactStreamSize = inData; } +inline PxU32 getPxgDynamicsMemoryConfigPatchStreamSize( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->patchStreamSize; } +inline void setPxgDynamicsMemoryConfigPatchStreamSize( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->patchStreamSize = inData; } +inline PxU32 getPxgDynamicsMemoryConfigForceStreamCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->forceStreamCapacity; } +inline void setPxgDynamicsMemoryConfigForceStreamCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->forceStreamCapacity = inData; } +inline PxU32 getPxgDynamicsMemoryConfigHeapCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->heapCapacity; } +inline void setPxgDynamicsMemoryConfigHeapCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->heapCapacity = inData; } +inline PxU32 getPxgDynamicsMemoryConfigFoundLostPairsCapacity( const PxgDynamicsMemoryConfig* inOwner ) { return inOwner->foundLostPairsCapacity; } +inline void setPxgDynamicsMemoryConfigFoundLostPairsCapacity( PxgDynamicsMemoryConfig* inOwner, PxU32 inData) { inOwner->foundLostPairsCapacity = inData; } +PX_PHYSX_CORE_API PxgDynamicsMemoryConfigGeneratedInfo::PxgDynamicsMemoryConfigGeneratedInfo() + : ConstraintBufferCapacity( "ConstraintBufferCapacity", setPxgDynamicsMemoryConfigConstraintBufferCapacity, getPxgDynamicsMemoryConfigConstraintBufferCapacity ) + , ContactBufferCapacity( "ContactBufferCapacity", setPxgDynamicsMemoryConfigContactBufferCapacity, getPxgDynamicsMemoryConfigContactBufferCapacity ) + , TempBufferCapacity( "TempBufferCapacity", setPxgDynamicsMemoryConfigTempBufferCapacity, getPxgDynamicsMemoryConfigTempBufferCapacity ) + , ContactStreamSize( "ContactStreamSize", setPxgDynamicsMemoryConfigContactStreamSize, getPxgDynamicsMemoryConfigContactStreamSize ) + , PatchStreamSize( "PatchStreamSize", setPxgDynamicsMemoryConfigPatchStreamSize, getPxgDynamicsMemoryConfigPatchStreamSize ) + , ForceStreamCapacity( "ForceStreamCapacity", setPxgDynamicsMemoryConfigForceStreamCapacity, getPxgDynamicsMemoryConfigForceStreamCapacity ) + , HeapCapacity( "HeapCapacity", setPxgDynamicsMemoryConfigHeapCapacity, getPxgDynamicsMemoryConfigHeapCapacity ) + , FoundLostPairsCapacity( "FoundLostPairsCapacity", setPxgDynamicsMemoryConfigFoundLostPairsCapacity, getPxgDynamicsMemoryConfigFoundLostPairsCapacity ) +{} +PX_PHYSX_CORE_API PxgDynamicsMemoryConfigGeneratedValues::PxgDynamicsMemoryConfigGeneratedValues( const PxgDynamicsMemoryConfig* inSource ) + :ConstraintBufferCapacity( inSource->constraintBufferCapacity ) + ,ContactBufferCapacity( inSource->contactBufferCapacity ) + ,TempBufferCapacity( inSource->tempBufferCapacity ) + ,ContactStreamSize( inSource->contactStreamSize ) + ,PatchStreamSize( inSource->patchStreamSize ) + ,ForceStreamCapacity( inSource->forceStreamCapacity ) + ,HeapCapacity( inSource->heapCapacity ) + ,FoundLostPairsCapacity( inSource->foundLostPairsCapacity ) +{ + PX_UNUSED(inSource); +} +void setPxSceneDesc_ToDefault( PxSceneDesc* inObj, const PxTolerancesScale & inArg){ inObj->setToDefault( inArg ); } +inline PxVec3 getPxSceneDescGravity( const PxSceneDesc* inOwner ) { return inOwner->gravity; } +inline void setPxSceneDescGravity( PxSceneDesc* inOwner, PxVec3 inData) { inOwner->gravity = inData; } +inline PxSimulationEventCallback * getPxSceneDescSimulationEventCallback( const PxSceneDesc* inOwner ) { return inOwner->simulationEventCallback; } +inline void setPxSceneDescSimulationEventCallback( PxSceneDesc* inOwner, PxSimulationEventCallback * inData) { inOwner->simulationEventCallback = inData; } +inline PxContactModifyCallback * getPxSceneDescContactModifyCallback( const PxSceneDesc* inOwner ) { return inOwner->contactModifyCallback; } +inline void setPxSceneDescContactModifyCallback( PxSceneDesc* inOwner, PxContactModifyCallback * inData) { inOwner->contactModifyCallback = inData; } +inline PxCCDContactModifyCallback * getPxSceneDescCcdContactModifyCallback( const PxSceneDesc* inOwner ) { return inOwner->ccdContactModifyCallback; } +inline void setPxSceneDescCcdContactModifyCallback( PxSceneDesc* inOwner, PxCCDContactModifyCallback * inData) { inOwner->ccdContactModifyCallback = inData; } +inline const void * getPxSceneDescFilterShaderData( const PxSceneDesc* inOwner ) { return inOwner->filterShaderData; } +inline void setPxSceneDescFilterShaderData( PxSceneDesc* inOwner, const void * inData) { inOwner->filterShaderData = inData; } +inline PxU32 getPxSceneDescFilterShaderDataSize( const PxSceneDesc* inOwner ) { return inOwner->filterShaderDataSize; } +inline void setPxSceneDescFilterShaderDataSize( PxSceneDesc* inOwner, PxU32 inData) { inOwner->filterShaderDataSize = inData; } +inline PxSimulationFilterShader getPxSceneDescFilterShader( const PxSceneDesc* inOwner ) { return inOwner->filterShader; } +inline void setPxSceneDescFilterShader( PxSceneDesc* inOwner, PxSimulationFilterShader inData) { inOwner->filterShader = inData; } +inline PxSimulationFilterCallback * getPxSceneDescFilterCallback( const PxSceneDesc* inOwner ) { return inOwner->filterCallback; } +inline void setPxSceneDescFilterCallback( PxSceneDesc* inOwner, PxSimulationFilterCallback * inData) { inOwner->filterCallback = inData; } +inline PxPairFilteringMode::Enum getPxSceneDescKineKineFilteringMode( const PxSceneDesc* inOwner ) { return inOwner->kineKineFilteringMode; } +inline void setPxSceneDescKineKineFilteringMode( PxSceneDesc* inOwner, PxPairFilteringMode::Enum inData) { inOwner->kineKineFilteringMode = inData; } +inline PxPairFilteringMode::Enum getPxSceneDescStaticKineFilteringMode( const PxSceneDesc* inOwner ) { return inOwner->staticKineFilteringMode; } +inline void setPxSceneDescStaticKineFilteringMode( PxSceneDesc* inOwner, PxPairFilteringMode::Enum inData) { inOwner->staticKineFilteringMode = inData; } +inline PxBroadPhaseType::Enum getPxSceneDescBroadPhaseType( const PxSceneDesc* inOwner ) { return inOwner->broadPhaseType; } +inline void setPxSceneDescBroadPhaseType( PxSceneDesc* inOwner, PxBroadPhaseType::Enum inData) { inOwner->broadPhaseType = inData; } +inline PxBroadPhaseCallback * getPxSceneDescBroadPhaseCallback( const PxSceneDesc* inOwner ) { return inOwner->broadPhaseCallback; } +inline void setPxSceneDescBroadPhaseCallback( PxSceneDesc* inOwner, PxBroadPhaseCallback * inData) { inOwner->broadPhaseCallback = inData; } +inline PxSceneLimits getPxSceneDescLimits( const PxSceneDesc* inOwner ) { return inOwner->limits; } +inline void setPxSceneDescLimits( PxSceneDesc* inOwner, PxSceneLimits inData) { inOwner->limits = inData; } +inline PxFrictionType::Enum getPxSceneDescFrictionType( const PxSceneDesc* inOwner ) { return inOwner->frictionType; } +inline void setPxSceneDescFrictionType( PxSceneDesc* inOwner, PxFrictionType::Enum inData) { inOwner->frictionType = inData; } +inline PxSolverType::Enum getPxSceneDescSolverType( const PxSceneDesc* inOwner ) { return inOwner->solverType; } +inline void setPxSceneDescSolverType( PxSceneDesc* inOwner, PxSolverType::Enum inData) { inOwner->solverType = inData; } +inline PxReal getPxSceneDescBounceThresholdVelocity( const PxSceneDesc* inOwner ) { return inOwner->bounceThresholdVelocity; } +inline void setPxSceneDescBounceThresholdVelocity( PxSceneDesc* inOwner, PxReal inData) { inOwner->bounceThresholdVelocity = inData; } +inline PxReal getPxSceneDescFrictionOffsetThreshold( const PxSceneDesc* inOwner ) { return inOwner->frictionOffsetThreshold; } +inline void setPxSceneDescFrictionOffsetThreshold( PxSceneDesc* inOwner, PxReal inData) { inOwner->frictionOffsetThreshold = inData; } +inline PxReal getPxSceneDescCcdMaxSeparation( const PxSceneDesc* inOwner ) { return inOwner->ccdMaxSeparation; } +inline void setPxSceneDescCcdMaxSeparation( PxSceneDesc* inOwner, PxReal inData) { inOwner->ccdMaxSeparation = inData; } +inline PxReal getPxSceneDescSolverOffsetSlop( const PxSceneDesc* inOwner ) { return inOwner->solverOffsetSlop; } +inline void setPxSceneDescSolverOffsetSlop( PxSceneDesc* inOwner, PxReal inData) { inOwner->solverOffsetSlop = inData; } +inline PxSceneFlags getPxSceneDescFlags( const PxSceneDesc* inOwner ) { return inOwner->flags; } +inline void setPxSceneDescFlags( PxSceneDesc* inOwner, PxSceneFlags inData) { inOwner->flags = inData; } +inline PxCpuDispatcher * getPxSceneDescCpuDispatcher( const PxSceneDesc* inOwner ) { return inOwner->cpuDispatcher; } +inline void setPxSceneDescCpuDispatcher( PxSceneDesc* inOwner, PxCpuDispatcher * inData) { inOwner->cpuDispatcher = inData; } +inline PxGpuDispatcher * getPxSceneDescGpuDispatcher( const PxSceneDesc* inOwner ) { return inOwner->gpuDispatcher; } +inline void setPxSceneDescGpuDispatcher( PxSceneDesc* inOwner, PxGpuDispatcher * inData) { inOwner->gpuDispatcher = inData; } +inline PxPruningStructureType::Enum getPxSceneDescStaticStructure( const PxSceneDesc* inOwner ) { return inOwner->staticStructure; } +inline void setPxSceneDescStaticStructure( PxSceneDesc* inOwner, PxPruningStructureType::Enum inData) { inOwner->staticStructure = inData; } +inline PxPruningStructureType::Enum getPxSceneDescDynamicStructure( const PxSceneDesc* inOwner ) { return inOwner->dynamicStructure; } +inline void setPxSceneDescDynamicStructure( PxSceneDesc* inOwner, PxPruningStructureType::Enum inData) { inOwner->dynamicStructure = inData; } +inline PxU32 getPxSceneDescDynamicTreeRebuildRateHint( const PxSceneDesc* inOwner ) { return inOwner->dynamicTreeRebuildRateHint; } +inline void setPxSceneDescDynamicTreeRebuildRateHint( PxSceneDesc* inOwner, PxU32 inData) { inOwner->dynamicTreeRebuildRateHint = inData; } +inline PxSceneQueryUpdateMode::Enum getPxSceneDescSceneQueryUpdateMode( const PxSceneDesc* inOwner ) { return inOwner->sceneQueryUpdateMode; } +inline void setPxSceneDescSceneQueryUpdateMode( PxSceneDesc* inOwner, PxSceneQueryUpdateMode::Enum inData) { inOwner->sceneQueryUpdateMode = inData; } +inline void * getPxSceneDescUserData( const PxSceneDesc* inOwner ) { return inOwner->userData; } +inline void setPxSceneDescUserData( PxSceneDesc* inOwner, void * inData) { inOwner->userData = inData; } +inline PxU32 getPxSceneDescSolverBatchSize( const PxSceneDesc* inOwner ) { return inOwner->solverBatchSize; } +inline void setPxSceneDescSolverBatchSize( PxSceneDesc* inOwner, PxU32 inData) { inOwner->solverBatchSize = inData; } +inline PxU32 getPxSceneDescNbContactDataBlocks( const PxSceneDesc* inOwner ) { return inOwner->nbContactDataBlocks; } +inline void setPxSceneDescNbContactDataBlocks( PxSceneDesc* inOwner, PxU32 inData) { inOwner->nbContactDataBlocks = inData; } +inline PxU32 getPxSceneDescMaxNbContactDataBlocks( const PxSceneDesc* inOwner ) { return inOwner->maxNbContactDataBlocks; } +inline void setPxSceneDescMaxNbContactDataBlocks( PxSceneDesc* inOwner, PxU32 inData) { inOwner->maxNbContactDataBlocks = inData; } +inline PxReal getPxSceneDescMaxBiasCoefficient( const PxSceneDesc* inOwner ) { return inOwner->maxBiasCoefficient; } +inline void setPxSceneDescMaxBiasCoefficient( PxSceneDesc* inOwner, PxReal inData) { inOwner->maxBiasCoefficient = inData; } +inline PxU32 getPxSceneDescContactReportStreamBufferSize( const PxSceneDesc* inOwner ) { return inOwner->contactReportStreamBufferSize; } +inline void setPxSceneDescContactReportStreamBufferSize( PxSceneDesc* inOwner, PxU32 inData) { inOwner->contactReportStreamBufferSize = inData; } +inline PxU32 getPxSceneDescCcdMaxPasses( const PxSceneDesc* inOwner ) { return inOwner->ccdMaxPasses; } +inline void setPxSceneDescCcdMaxPasses( PxSceneDesc* inOwner, PxU32 inData) { inOwner->ccdMaxPasses = inData; } +inline PxReal getPxSceneDescWakeCounterResetValue( const PxSceneDesc* inOwner ) { return inOwner->wakeCounterResetValue; } +inline void setPxSceneDescWakeCounterResetValue( PxSceneDesc* inOwner, PxReal inData) { inOwner->wakeCounterResetValue = inData; } +inline PxBounds3 getPxSceneDescSanityBounds( const PxSceneDesc* inOwner ) { return inOwner->sanityBounds; } +inline void setPxSceneDescSanityBounds( PxSceneDesc* inOwner, PxBounds3 inData) { inOwner->sanityBounds = inData; } +inline PxgDynamicsMemoryConfig getPxSceneDescGpuDynamicsConfig( const PxSceneDesc* inOwner ) { return inOwner->gpuDynamicsConfig; } +inline void setPxSceneDescGpuDynamicsConfig( PxSceneDesc* inOwner, PxgDynamicsMemoryConfig inData) { inOwner->gpuDynamicsConfig = inData; } +inline PxU32 getPxSceneDescGpuMaxNumPartitions( const PxSceneDesc* inOwner ) { return inOwner->gpuMaxNumPartitions; } +inline void setPxSceneDescGpuMaxNumPartitions( PxSceneDesc* inOwner, PxU32 inData) { inOwner->gpuMaxNumPartitions = inData; } +inline PxU32 getPxSceneDescGpuComputeVersion( const PxSceneDesc* inOwner ) { return inOwner->gpuComputeVersion; } +inline void setPxSceneDescGpuComputeVersion( PxSceneDesc* inOwner, PxU32 inData) { inOwner->gpuComputeVersion = inData; } +PX_PHYSX_CORE_API PxSceneDescGeneratedInfo::PxSceneDescGeneratedInfo() + : ToDefault( "ToDefault", setPxSceneDesc_ToDefault) + , Gravity( "Gravity", setPxSceneDescGravity, getPxSceneDescGravity ) + , SimulationEventCallback( "SimulationEventCallback", setPxSceneDescSimulationEventCallback, getPxSceneDescSimulationEventCallback ) + , ContactModifyCallback( "ContactModifyCallback", setPxSceneDescContactModifyCallback, getPxSceneDescContactModifyCallback ) + , CcdContactModifyCallback( "CcdContactModifyCallback", setPxSceneDescCcdContactModifyCallback, getPxSceneDescCcdContactModifyCallback ) + , FilterShaderData( "FilterShaderData", setPxSceneDescFilterShaderData, getPxSceneDescFilterShaderData ) + , FilterShaderDataSize( "FilterShaderDataSize", setPxSceneDescFilterShaderDataSize, getPxSceneDescFilterShaderDataSize ) + , FilterShader( "FilterShader", setPxSceneDescFilterShader, getPxSceneDescFilterShader ) + , FilterCallback( "FilterCallback", setPxSceneDescFilterCallback, getPxSceneDescFilterCallback ) + , KineKineFilteringMode( "KineKineFilteringMode", setPxSceneDescKineKineFilteringMode, getPxSceneDescKineKineFilteringMode ) + , StaticKineFilteringMode( "StaticKineFilteringMode", setPxSceneDescStaticKineFilteringMode, getPxSceneDescStaticKineFilteringMode ) + , BroadPhaseType( "BroadPhaseType", setPxSceneDescBroadPhaseType, getPxSceneDescBroadPhaseType ) + , BroadPhaseCallback( "BroadPhaseCallback", setPxSceneDescBroadPhaseCallback, getPxSceneDescBroadPhaseCallback ) + , Limits( "Limits", setPxSceneDescLimits, getPxSceneDescLimits ) + , FrictionType( "FrictionType", setPxSceneDescFrictionType, getPxSceneDescFrictionType ) + , SolverType( "SolverType", setPxSceneDescSolverType, getPxSceneDescSolverType ) + , BounceThresholdVelocity( "BounceThresholdVelocity", setPxSceneDescBounceThresholdVelocity, getPxSceneDescBounceThresholdVelocity ) + , FrictionOffsetThreshold( "FrictionOffsetThreshold", setPxSceneDescFrictionOffsetThreshold, getPxSceneDescFrictionOffsetThreshold ) + , CcdMaxSeparation( "CcdMaxSeparation", setPxSceneDescCcdMaxSeparation, getPxSceneDescCcdMaxSeparation ) + , SolverOffsetSlop( "SolverOffsetSlop", setPxSceneDescSolverOffsetSlop, getPxSceneDescSolverOffsetSlop ) + , Flags( "Flags", setPxSceneDescFlags, getPxSceneDescFlags ) + , CpuDispatcher( "CpuDispatcher", setPxSceneDescCpuDispatcher, getPxSceneDescCpuDispatcher ) + , GpuDispatcher( "GpuDispatcher", setPxSceneDescGpuDispatcher, getPxSceneDescGpuDispatcher ) + , StaticStructure( "StaticStructure", setPxSceneDescStaticStructure, getPxSceneDescStaticStructure ) + , DynamicStructure( "DynamicStructure", setPxSceneDescDynamicStructure, getPxSceneDescDynamicStructure ) + , DynamicTreeRebuildRateHint( "DynamicTreeRebuildRateHint", setPxSceneDescDynamicTreeRebuildRateHint, getPxSceneDescDynamicTreeRebuildRateHint ) + , SceneQueryUpdateMode( "SceneQueryUpdateMode", setPxSceneDescSceneQueryUpdateMode, getPxSceneDescSceneQueryUpdateMode ) + , UserData( "UserData", setPxSceneDescUserData, getPxSceneDescUserData ) + , SolverBatchSize( "SolverBatchSize", setPxSceneDescSolverBatchSize, getPxSceneDescSolverBatchSize ) + , NbContactDataBlocks( "NbContactDataBlocks", setPxSceneDescNbContactDataBlocks, getPxSceneDescNbContactDataBlocks ) + , MaxNbContactDataBlocks( "MaxNbContactDataBlocks", setPxSceneDescMaxNbContactDataBlocks, getPxSceneDescMaxNbContactDataBlocks ) + , MaxBiasCoefficient( "MaxBiasCoefficient", setPxSceneDescMaxBiasCoefficient, getPxSceneDescMaxBiasCoefficient ) + , ContactReportStreamBufferSize( "ContactReportStreamBufferSize", setPxSceneDescContactReportStreamBufferSize, getPxSceneDescContactReportStreamBufferSize ) + , CcdMaxPasses( "CcdMaxPasses", setPxSceneDescCcdMaxPasses, getPxSceneDescCcdMaxPasses ) + , WakeCounterResetValue( "WakeCounterResetValue", setPxSceneDescWakeCounterResetValue, getPxSceneDescWakeCounterResetValue ) + , SanityBounds( "SanityBounds", setPxSceneDescSanityBounds, getPxSceneDescSanityBounds ) + , GpuDynamicsConfig( "GpuDynamicsConfig", setPxSceneDescGpuDynamicsConfig, getPxSceneDescGpuDynamicsConfig ) + , GpuMaxNumPartitions( "GpuMaxNumPartitions", setPxSceneDescGpuMaxNumPartitions, getPxSceneDescGpuMaxNumPartitions ) + , GpuComputeVersion( "GpuComputeVersion", setPxSceneDescGpuComputeVersion, getPxSceneDescGpuComputeVersion ) +{} +PX_PHYSX_CORE_API PxSceneDescGeneratedValues::PxSceneDescGeneratedValues( const PxSceneDesc* inSource ) + :Gravity( inSource->gravity ) + ,SimulationEventCallback( inSource->simulationEventCallback ) + ,ContactModifyCallback( inSource->contactModifyCallback ) + ,CcdContactModifyCallback( inSource->ccdContactModifyCallback ) + ,FilterShaderData( inSource->filterShaderData ) + ,FilterShaderDataSize( inSource->filterShaderDataSize ) + ,FilterShader( inSource->filterShader ) + ,FilterCallback( inSource->filterCallback ) + ,KineKineFilteringMode( inSource->kineKineFilteringMode ) + ,StaticKineFilteringMode( inSource->staticKineFilteringMode ) + ,BroadPhaseType( inSource->broadPhaseType ) + ,BroadPhaseCallback( inSource->broadPhaseCallback ) + ,Limits( inSource->limits ) + ,FrictionType( inSource->frictionType ) + ,SolverType( inSource->solverType ) + ,BounceThresholdVelocity( inSource->bounceThresholdVelocity ) + ,FrictionOffsetThreshold( inSource->frictionOffsetThreshold ) + ,CcdMaxSeparation( inSource->ccdMaxSeparation ) + ,SolverOffsetSlop( inSource->solverOffsetSlop ) + ,Flags( inSource->flags ) + ,CpuDispatcher( inSource->cpuDispatcher ) + ,GpuDispatcher( inSource->gpuDispatcher ) + ,StaticStructure( inSource->staticStructure ) + ,DynamicStructure( inSource->dynamicStructure ) + ,DynamicTreeRebuildRateHint( inSource->dynamicTreeRebuildRateHint ) + ,SceneQueryUpdateMode( inSource->sceneQueryUpdateMode ) + ,UserData( inSource->userData ) + ,SolverBatchSize( inSource->solverBatchSize ) + ,NbContactDataBlocks( inSource->nbContactDataBlocks ) + ,MaxNbContactDataBlocks( inSource->maxNbContactDataBlocks ) + ,MaxBiasCoefficient( inSource->maxBiasCoefficient ) + ,ContactReportStreamBufferSize( inSource->contactReportStreamBufferSize ) + ,CcdMaxPasses( inSource->ccdMaxPasses ) + ,WakeCounterResetValue( inSource->wakeCounterResetValue ) + ,SanityBounds( inSource->sanityBounds ) + ,GpuDynamicsConfig( inSource->gpuDynamicsConfig ) + ,GpuMaxNumPartitions( inSource->gpuMaxNumPartitions ) + ,GpuComputeVersion( inSource->gpuComputeVersion ) +{ + PX_UNUSED(inSource); +} +inline PxU32 getPxSimulationStatisticsNbActiveConstraints( const PxSimulationStatistics* inOwner ) { return inOwner->nbActiveConstraints; } +inline void setPxSimulationStatisticsNbActiveConstraints( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbActiveConstraints = inData; } +inline PxU32 getPxSimulationStatisticsNbActiveDynamicBodies( const PxSimulationStatistics* inOwner ) { return inOwner->nbActiveDynamicBodies; } +inline void setPxSimulationStatisticsNbActiveDynamicBodies( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbActiveDynamicBodies = inData; } +inline PxU32 getPxSimulationStatisticsNbActiveKinematicBodies( const PxSimulationStatistics* inOwner ) { return inOwner->nbActiveKinematicBodies; } +inline void setPxSimulationStatisticsNbActiveKinematicBodies( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbActiveKinematicBodies = inData; } +inline PxU32 getPxSimulationStatisticsNbStaticBodies( const PxSimulationStatistics* inOwner ) { return inOwner->nbStaticBodies; } +inline void setPxSimulationStatisticsNbStaticBodies( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbStaticBodies = inData; } +inline PxU32 getPxSimulationStatisticsNbDynamicBodies( const PxSimulationStatistics* inOwner ) { return inOwner->nbDynamicBodies; } +inline void setPxSimulationStatisticsNbDynamicBodies( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbDynamicBodies = inData; } +inline PxU32 getPxSimulationStatisticsNbAggregates( const PxSimulationStatistics* inOwner ) { return inOwner->nbAggregates; } +inline void setPxSimulationStatisticsNbAggregates( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbAggregates = inData; } +inline PxU32 getPxSimulationStatisticsNbArticulations( const PxSimulationStatistics* inOwner ) { return inOwner->nbArticulations; } +inline void setPxSimulationStatisticsNbArticulations( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbArticulations = inData; } +inline PxU32 getPxSimulationStatisticsNbAxisSolverConstraints( const PxSimulationStatistics* inOwner ) { return inOwner->nbAxisSolverConstraints; } +inline void setPxSimulationStatisticsNbAxisSolverConstraints( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbAxisSolverConstraints = inData; } +inline PxU32 getPxSimulationStatisticsCompressedContactSize( const PxSimulationStatistics* inOwner ) { return inOwner->compressedContactSize; } +inline void setPxSimulationStatisticsCompressedContactSize( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->compressedContactSize = inData; } +inline PxU32 getPxSimulationStatisticsRequiredContactConstraintMemory( const PxSimulationStatistics* inOwner ) { return inOwner->requiredContactConstraintMemory; } +inline void setPxSimulationStatisticsRequiredContactConstraintMemory( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->requiredContactConstraintMemory = inData; } +inline PxU32 getPxSimulationStatisticsPeakConstraintMemory( const PxSimulationStatistics* inOwner ) { return inOwner->peakConstraintMemory; } +inline void setPxSimulationStatisticsPeakConstraintMemory( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->peakConstraintMemory = inData; } +inline PxU32 getPxSimulationStatisticsNbDiscreteContactPairsTotal( const PxSimulationStatistics* inOwner ) { return inOwner->nbDiscreteContactPairsTotal; } +inline void setPxSimulationStatisticsNbDiscreteContactPairsTotal( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbDiscreteContactPairsTotal = inData; } +inline PxU32 getPxSimulationStatisticsNbDiscreteContactPairsWithCacheHits( const PxSimulationStatistics* inOwner ) { return inOwner->nbDiscreteContactPairsWithCacheHits; } +inline void setPxSimulationStatisticsNbDiscreteContactPairsWithCacheHits( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbDiscreteContactPairsWithCacheHits = inData; } +inline PxU32 getPxSimulationStatisticsNbDiscreteContactPairsWithContacts( const PxSimulationStatistics* inOwner ) { return inOwner->nbDiscreteContactPairsWithContacts; } +inline void setPxSimulationStatisticsNbDiscreteContactPairsWithContacts( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbDiscreteContactPairsWithContacts = inData; } +inline PxU32 getPxSimulationStatisticsNbNewPairs( const PxSimulationStatistics* inOwner ) { return inOwner->nbNewPairs; } +inline void setPxSimulationStatisticsNbNewPairs( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbNewPairs = inData; } +inline PxU32 getPxSimulationStatisticsNbLostPairs( const PxSimulationStatistics* inOwner ) { return inOwner->nbLostPairs; } +inline void setPxSimulationStatisticsNbLostPairs( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbLostPairs = inData; } +inline PxU32 getPxSimulationStatisticsNbNewTouches( const PxSimulationStatistics* inOwner ) { return inOwner->nbNewTouches; } +inline void setPxSimulationStatisticsNbNewTouches( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbNewTouches = inData; } +inline PxU32 getPxSimulationStatisticsNbLostTouches( const PxSimulationStatistics* inOwner ) { return inOwner->nbLostTouches; } +inline void setPxSimulationStatisticsNbLostTouches( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbLostTouches = inData; } +inline PxU32 getPxSimulationStatisticsNbPartitions( const PxSimulationStatistics* inOwner ) { return inOwner->nbPartitions; } +inline void setPxSimulationStatisticsNbPartitions( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbPartitions = inData; } +inline PxU32 getPxSimulationStatisticsNbBroadPhaseAdds( const PxSimulationStatistics* inOwner ) { return inOwner->nbBroadPhaseAdds; } +inline void setPxSimulationStatisticsNbBroadPhaseAdds( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbBroadPhaseAdds = inData; } +inline PxU32 getPxSimulationStatisticsNbBroadPhaseRemoves( const PxSimulationStatistics* inOwner ) { return inOwner->nbBroadPhaseRemoves; } +inline void setPxSimulationStatisticsNbBroadPhaseRemoves( PxSimulationStatistics* inOwner, PxU32 inData) { inOwner->nbBroadPhaseRemoves = inData; } +PX_PHYSX_CORE_API PxSimulationStatisticsGeneratedInfo::PxSimulationStatisticsGeneratedInfo() + : NbActiveConstraints( "NbActiveConstraints", setPxSimulationStatisticsNbActiveConstraints, getPxSimulationStatisticsNbActiveConstraints ) + , NbActiveDynamicBodies( "NbActiveDynamicBodies", setPxSimulationStatisticsNbActiveDynamicBodies, getPxSimulationStatisticsNbActiveDynamicBodies ) + , NbActiveKinematicBodies( "NbActiveKinematicBodies", setPxSimulationStatisticsNbActiveKinematicBodies, getPxSimulationStatisticsNbActiveKinematicBodies ) + , NbStaticBodies( "NbStaticBodies", setPxSimulationStatisticsNbStaticBodies, getPxSimulationStatisticsNbStaticBodies ) + , NbDynamicBodies( "NbDynamicBodies", setPxSimulationStatisticsNbDynamicBodies, getPxSimulationStatisticsNbDynamicBodies ) + , NbAggregates( "NbAggregates", setPxSimulationStatisticsNbAggregates, getPxSimulationStatisticsNbAggregates ) + , NbArticulations( "NbArticulations", setPxSimulationStatisticsNbArticulations, getPxSimulationStatisticsNbArticulations ) + , NbAxisSolverConstraints( "NbAxisSolverConstraints", setPxSimulationStatisticsNbAxisSolverConstraints, getPxSimulationStatisticsNbAxisSolverConstraints ) + , CompressedContactSize( "CompressedContactSize", setPxSimulationStatisticsCompressedContactSize, getPxSimulationStatisticsCompressedContactSize ) + , RequiredContactConstraintMemory( "RequiredContactConstraintMemory", setPxSimulationStatisticsRequiredContactConstraintMemory, getPxSimulationStatisticsRequiredContactConstraintMemory ) + , PeakConstraintMemory( "PeakConstraintMemory", setPxSimulationStatisticsPeakConstraintMemory, getPxSimulationStatisticsPeakConstraintMemory ) + , NbDiscreteContactPairsTotal( "NbDiscreteContactPairsTotal", setPxSimulationStatisticsNbDiscreteContactPairsTotal, getPxSimulationStatisticsNbDiscreteContactPairsTotal ) + , NbDiscreteContactPairsWithCacheHits( "NbDiscreteContactPairsWithCacheHits", setPxSimulationStatisticsNbDiscreteContactPairsWithCacheHits, getPxSimulationStatisticsNbDiscreteContactPairsWithCacheHits ) + , NbDiscreteContactPairsWithContacts( "NbDiscreteContactPairsWithContacts", setPxSimulationStatisticsNbDiscreteContactPairsWithContacts, getPxSimulationStatisticsNbDiscreteContactPairsWithContacts ) + , NbNewPairs( "NbNewPairs", setPxSimulationStatisticsNbNewPairs, getPxSimulationStatisticsNbNewPairs ) + , NbLostPairs( "NbLostPairs", setPxSimulationStatisticsNbLostPairs, getPxSimulationStatisticsNbLostPairs ) + , NbNewTouches( "NbNewTouches", setPxSimulationStatisticsNbNewTouches, getPxSimulationStatisticsNbNewTouches ) + , NbLostTouches( "NbLostTouches", setPxSimulationStatisticsNbLostTouches, getPxSimulationStatisticsNbLostTouches ) + , NbPartitions( "NbPartitions", setPxSimulationStatisticsNbPartitions, getPxSimulationStatisticsNbPartitions ) + , NbBroadPhaseAdds( "NbBroadPhaseAdds", setPxSimulationStatisticsNbBroadPhaseAdds, getPxSimulationStatisticsNbBroadPhaseAdds ) + , NbBroadPhaseRemoves( "NbBroadPhaseRemoves", setPxSimulationStatisticsNbBroadPhaseRemoves, getPxSimulationStatisticsNbBroadPhaseRemoves ) +{} +PX_PHYSX_CORE_API PxSimulationStatisticsGeneratedValues::PxSimulationStatisticsGeneratedValues( const PxSimulationStatistics* inSource ) + :NbActiveConstraints( inSource->nbActiveConstraints ) + ,NbActiveDynamicBodies( inSource->nbActiveDynamicBodies ) + ,NbActiveKinematicBodies( inSource->nbActiveKinematicBodies ) + ,NbStaticBodies( inSource->nbStaticBodies ) + ,NbDynamicBodies( inSource->nbDynamicBodies ) + ,NbAggregates( inSource->nbAggregates ) + ,NbArticulations( inSource->nbArticulations ) + ,NbAxisSolverConstraints( inSource->nbAxisSolverConstraints ) + ,CompressedContactSize( inSource->compressedContactSize ) + ,RequiredContactConstraintMemory( inSource->requiredContactConstraintMemory ) + ,PeakConstraintMemory( inSource->peakConstraintMemory ) + ,NbDiscreteContactPairsTotal( inSource->nbDiscreteContactPairsTotal ) + ,NbDiscreteContactPairsWithCacheHits( inSource->nbDiscreteContactPairsWithCacheHits ) + ,NbDiscreteContactPairsWithContacts( inSource->nbDiscreteContactPairsWithContacts ) + ,NbNewPairs( inSource->nbNewPairs ) + ,NbLostPairs( inSource->nbLostPairs ) + ,NbNewTouches( inSource->nbNewTouches ) + ,NbLostTouches( inSource->nbLostTouches ) + ,NbPartitions( inSource->nbPartitions ) + ,NbBroadPhaseAdds( inSource->nbBroadPhaseAdds ) + ,NbBroadPhaseRemoves( inSource->nbBroadPhaseRemoves ) +{ + PX_UNUSED(inSource); + PxMemCopy( NbDiscreteContactPairs, inSource->nbDiscreteContactPairs, sizeof( NbDiscreteContactPairs ) ); + PxMemCopy( NbModifiedContactPairs, inSource->nbModifiedContactPairs, sizeof( NbModifiedContactPairs ) ); + PxMemCopy( NbCCDPairs, inSource->nbCCDPairs, sizeof( NbCCDPairs ) ); + PxMemCopy( NbTriggerPairs, inSource->nbTriggerPairs, sizeof( NbTriggerPairs ) ); + PxMemCopy( NbShapes, inSource->nbShapes, sizeof( NbShapes ) ); +} diff --git a/src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp b/src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp new file mode 100644 index 000000000..2717e54ae --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp @@ -0,0 +1,148 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" + +#include "PxMetaDataObjects.h" + +using namespace physx; + +PX_PHYSX_CORE_API PxGeometryType::Enum PxShapeGeometryPropertyHelper::getGeometryType(const PxShape* inShape) const { return inShape->getGeometryType(); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxBoxGeometry& geometry) const { return inShape->getBoxGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxSphereGeometry& geometry) const { return inShape->getSphereGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxCapsuleGeometry& geometry) const { return inShape->getCapsuleGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxPlaneGeometry& geometry) const { return inShape->getPlaneGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxConvexMeshGeometry& geometry) const { return inShape->getConvexMeshGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxTriangleMeshGeometry& geometry) const { return inShape->getTriangleMeshGeometry( geometry ); } +PX_PHYSX_CORE_API bool PxShapeGeometryPropertyHelper::getGeometry(const PxShape* inShape, PxHeightFieldGeometry& geometry) const { return inShape->getHeightFieldGeometry( geometry ); } + +PX_PHYSX_CORE_API void PxShapeMaterialsPropertyHelper::setMaterials(PxShape* inShape, PxMaterial*const* materials, PxU16 materialCount) const +{ + inShape->setMaterials( materials, materialCount ); +} + +PX_PHYSX_CORE_API PxShape* PxRigidActorShapeCollectionHelper::createShape(PxRigidActor* inActor, const PxGeometry& geometry, PxMaterial& material, + PxShapeFlags shapeFlags ) const +{ + PxMaterial* materialPtr = const_cast(&material); + PxShape* shape = PxGetPhysics().createShape(geometry, &materialPtr, 1, true, shapeFlags); + if (shape) + { + inActor->attachShape(*shape); // attach can fail, if e.g. we try and attach a trimesh simulation shape to a dynamic actor + shape->release(); // if attach fails, we hold the only counted reference, and so this cleans up properly + } + return shape; +} +PX_PHYSX_CORE_API PxShape* PxRigidActorShapeCollectionHelper::createShape(PxRigidActor* inActor, const PxGeometry& geometry, PxMaterial *const* materials, + PxU16 materialCount, PxShapeFlags shapeFlags ) const +{ + PxShape* shape = PxGetPhysics().createShape(geometry, materials, materialCount, true, shapeFlags); + if (shape) + { + inActor->attachShape(*shape); // attach can fail, if e.g. we try and attach a trimesh simulation shape to a dynamic actor + shape->release(); // if attach fails, we hold the only counted reference, and so this cleans up properly + } + return shape; +} + +PX_PHYSX_CORE_API PxArticulationLink* PxArticulationLinkCollectionPropHelper::createLink(PxArticulation* inArticulation, PxArticulationLink* parent, + const PxTransform& pose) const +{ + PX_CHECK_AND_RETURN_NULL(pose.isValid(), "PxArticulationLinkCollectionPropHelper::createLink pose is not valid."); + return inArticulation->createLink(parent, pose ); +} + + + + +inline void SetNbShape( PxSimulationStatistics* inStats, PxGeometryType::Enum data, PxU32 val ) { inStats->nbShapes[data] = val; } +inline PxU32 GetNbShape( const PxSimulationStatistics* inStats, PxGeometryType::Enum data) { return inStats->nbShapes[data]; } + + +PX_PHYSX_CORE_API NbShapesProperty::NbShapesProperty() + : PxIndexedPropertyInfo ( "NbShapes", SetNbShape, GetNbShape ) +{ +} + + +inline void SetNbDiscreteContactPairs( PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2, PxU32 val ) { inStats->nbDiscreteContactPairs[idx1][idx2] = val; } +inline PxU32 GetNbDiscreteContactPairs( const PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2 ) { return inStats->nbDiscreteContactPairs[idx1][idx2]; } +PX_PHYSX_CORE_API NbDiscreteContactPairsProperty::NbDiscreteContactPairsProperty() + : PxDualIndexedPropertyInfo ( "NbDiscreteContactPairs", SetNbDiscreteContactPairs, GetNbDiscreteContactPairs ) +{ +} + +inline void SetNbModifiedContactPairs( PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2, PxU32 val ) { inStats->nbModifiedContactPairs[idx1][idx2] = val; } +inline PxU32 GetNbModifiedContactPairs( const PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2 ) { return inStats->nbModifiedContactPairs[idx1][idx2]; } +PX_PHYSX_CORE_API NbModifiedContactPairsProperty::NbModifiedContactPairsProperty() + : PxDualIndexedPropertyInfo ( "NbModifiedContactPairs", SetNbModifiedContactPairs, GetNbModifiedContactPairs ) +{ +} + +inline void SetNbCCDPairs( PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2, PxU32 val ) { inStats->nbCCDPairs[idx1][idx2] = val; } +inline PxU32 GetNbCCDPairs( const PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2 ) { return inStats->nbCCDPairs[idx1][idx2]; } +PX_PHYSX_CORE_API NbCCDPairsProperty::NbCCDPairsProperty() + : PxDualIndexedPropertyInfo ( "NbCCDPairs", SetNbCCDPairs, GetNbCCDPairs ) +{ +} + +inline void SetNbTriggerPairs( PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2, PxU32 val ) { inStats->nbTriggerPairs[idx1][idx2] = val; } +inline PxU32 GetNbTriggerPairs( const PxSimulationStatistics* inStats, PxGeometryType::Enum idx1, PxGeometryType::Enum idx2 ) { return inStats->nbTriggerPairs[idx1][idx2]; } +PX_PHYSX_CORE_API NbTriggerPairsProperty::NbTriggerPairsProperty() + : PxDualIndexedPropertyInfo ( "NbTriggerPairs", SetNbTriggerPairs, GetNbTriggerPairs ) +{ +} + +inline PxSimulationStatistics GetStats( const PxScene* inScene ) { PxSimulationStatistics stats; inScene->getSimulationStatistics( stats ); return stats; } +PX_PHYSX_CORE_API SimulationStatisticsProperty::SimulationStatisticsProperty() + : PxReadOnlyPropertyInfo( "SimulationStatistics", GetStats ) +{ +} + diff --git a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h new file mode 100644 index 000000000..1de957676 --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h @@ -0,0 +1,162 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +PxJoint_PropertiesStart, +PxJoint_Actors, +PxJoint_LocalPose, +PxJoint_RelativeTransform, +PxJoint_RelativeLinearVelocity, +PxJoint_RelativeAngularVelocity, +PxJoint_BreakForce, +PxJoint_ConstraintFlags, +PxJoint_InvMassScale0, +PxJoint_InvInertiaScale0, +PxJoint_InvMassScale1, +PxJoint_InvInertiaScale1, +PxJoint_Constraint, +PxJoint_Name, +PxJoint_Scene, +PxJoint_UserData, +PxJoint_PropertiesStop, +PxContactJoint_PropertiesStart, +PxContactJoint_Contact, +PxContactJoint_ContactNormal, +PxContactJoint_Penetration, +PxContactJoint_Resititution, +PxContactJoint_BounceThreshold, +PxContactJoint_ConcreteTypeName, +PxContactJoint_PropertiesStop, +PxD6Joint_PropertiesStart, +PxD6Joint_Motion, +PxD6Joint_TwistAngle, +PxD6Joint_Twist, +PxD6Joint_SwingYAngle, +PxD6Joint_SwingZAngle, +PxD6Joint_DistanceLimit, +PxD6Joint_LinearLimit, +PxD6Joint_TwistLimit, +PxD6Joint_SwingLimit, +PxD6Joint_PyramidSwingLimit, +PxD6Joint_Drive, +PxD6Joint_DrivePosition, +PxD6Joint_ProjectionLinearTolerance, +PxD6Joint_ProjectionAngularTolerance, +PxD6Joint_ConcreteTypeName, +PxD6Joint_PropertiesStop, +PxDistanceJoint_PropertiesStart, +PxDistanceJoint_Distance, +PxDistanceJoint_MinDistance, +PxDistanceJoint_MaxDistance, +PxDistanceJoint_Tolerance, +PxDistanceJoint_Stiffness, +PxDistanceJoint_Damping, +PxDistanceJoint_DistanceJointFlags, +PxDistanceJoint_ConcreteTypeName, +PxDistanceJoint_PropertiesStop, +PxFixedJoint_PropertiesStart, +PxFixedJoint_ProjectionLinearTolerance, +PxFixedJoint_ProjectionAngularTolerance, +PxFixedJoint_ConcreteTypeName, +PxFixedJoint_PropertiesStop, +PxPrismaticJoint_PropertiesStart, +PxPrismaticJoint_Position, +PxPrismaticJoint_Velocity, +PxPrismaticJoint_Limit, +PxPrismaticJoint_PrismaticJointFlags, +PxPrismaticJoint_ProjectionLinearTolerance, +PxPrismaticJoint_ProjectionAngularTolerance, +PxPrismaticJoint_ConcreteTypeName, +PxPrismaticJoint_PropertiesStop, +PxRevoluteJoint_PropertiesStart, +PxRevoluteJoint_Angle, +PxRevoluteJoint_Velocity, +PxRevoluteJoint_Limit, +PxRevoluteJoint_DriveVelocity, +PxRevoluteJoint_DriveForceLimit, +PxRevoluteJoint_DriveGearRatio, +PxRevoluteJoint_RevoluteJointFlags, +PxRevoluteJoint_ProjectionLinearTolerance, +PxRevoluteJoint_ProjectionAngularTolerance, +PxRevoluteJoint_ConcreteTypeName, +PxRevoluteJoint_PropertiesStop, +PxSphericalJoint_PropertiesStart, +PxSphericalJoint_LimitCone, +PxSphericalJoint_SwingYAngle, +PxSphericalJoint_SwingZAngle, +PxSphericalJoint_SphericalJointFlags, +PxSphericalJoint_ProjectionLinearTolerance, +PxSphericalJoint_ConcreteTypeName, +PxSphericalJoint_PropertiesStop, +PxJointLimitParameters_PropertiesStart, +PxJointLimitParameters_Restitution, +PxJointLimitParameters_BounceThreshold, +PxJointLimitParameters_Stiffness, +PxJointLimitParameters_Damping, +PxJointLimitParameters_ContactDistance, +PxJointLimitParameters_PropertiesStop, +PxJointLinearLimit_PropertiesStart, +PxJointLinearLimit_Value, +PxJointLinearLimit_PropertiesStop, +PxJointLinearLimitPair_PropertiesStart, +PxJointLinearLimitPair_Upper, +PxJointLinearLimitPair_Lower, +PxJointLinearLimitPair_PropertiesStop, +PxJointAngularLimitPair_PropertiesStart, +PxJointAngularLimitPair_Upper, +PxJointAngularLimitPair_Lower, +PxJointAngularLimitPair_PropertiesStop, +PxJointLimitCone_PropertiesStart, +PxJointLimitCone_YAngle, +PxJointLimitCone_ZAngle, +PxJointLimitCone_PropertiesStop, +PxJointLimitPyramid_PropertiesStart, +PxJointLimitPyramid_YAngleMin, +PxJointLimitPyramid_YAngleMax, +PxJointLimitPyramid_ZAngleMin, +PxJointLimitPyramid_ZAngleMax, +PxJointLimitPyramid_PropertiesStop, +PxSpring_PropertiesStart, +PxSpring_Stiffness, +PxSpring_Damping, +PxSpring_PropertiesStop, +PxD6JointDrive_PropertiesStart, +PxD6JointDrive_ForceLimit, +PxD6JointDrive_Flags, +PxD6JointDrive_PropertiesStop, + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h new file mode 100644 index 000000000..ed59c9e9f --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h @@ -0,0 +1,1229 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +#define PX_PROPERTY_INFO_NAME PxExtensionsPropertyInfoName + static PxU32ToName g_physx__PxJointActorIndex__EnumConversion[] = { + { "eACTOR0", static_cast( physx::PxJointActorIndex::eACTOR0 ) }, + { "eACTOR1", static_cast( physx::PxJointActorIndex::eACTOR1 ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxJointActorIndex::Enum > { PxEnumTraits() : NameConversion( g_physx__PxJointActorIndex__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxJoint; + struct PxJointGeneratedValues + { + PxRigidActor * Actors[2]; + PxTransform LocalPose[physx::PxJointActorIndex::COUNT]; + PxTransform RelativeTransform; + PxVec3 RelativeLinearVelocity; + PxVec3 RelativeAngularVelocity; + PxReal BreakForce[2]; + PxConstraintFlags ConstraintFlags; + PxReal InvMassScale0; + PxReal InvInertiaScale0; + PxReal InvMassScale1; + PxReal InvInertiaScale1; + PxConstraint * Constraint; + const char * Name; + PxScene * Scene; + void * UserData; + PxJointGeneratedValues( const PxJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, Actors, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, LocalPose, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, RelativeTransform, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, RelativeLinearVelocity, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, RelativeAngularVelocity, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, BreakForce, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, ConstraintFlags, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, InvMassScale0, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, InvInertiaScale0, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, InvMassScale1, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, InvInertiaScale1, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, Constraint, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, Name, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, Scene, PxJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJoint, UserData, PxJointGeneratedValues) + struct PxJointGeneratedInfo + + { + static const char* getClassName() { return "PxJoint"; } + PxRangePropertyInfo Actors; + PxIndexedPropertyInfo LocalPose; + PxReadOnlyPropertyInfo RelativeTransform; + PxReadOnlyPropertyInfo RelativeLinearVelocity; + PxReadOnlyPropertyInfo RelativeAngularVelocity; + PxRangePropertyInfo BreakForce; + PxPropertyInfo ConstraintFlags; + PxPropertyInfo InvMassScale0; + PxPropertyInfo InvInertiaScale0; + PxPropertyInfo InvMassScale1; + PxPropertyInfo InvInertiaScale1; + PxReadOnlyPropertyInfo Constraint; + PxPropertyInfo Name; + PxReadOnlyPropertyInfo Scene; + PxPropertyInfo UserData; + + PxJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 15; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Actors, inStartIndex + 0 );; + inOperator( LocalPose, inStartIndex + 1 );; + inOperator( RelativeTransform, inStartIndex + 2 );; + inOperator( RelativeLinearVelocity, inStartIndex + 3 );; + inOperator( RelativeAngularVelocity, inStartIndex + 4 );; + inOperator( BreakForce, inStartIndex + 5 );; + inOperator( ConstraintFlags, inStartIndex + 6 );; + inOperator( InvMassScale0, inStartIndex + 7 );; + inOperator( InvInertiaScale0, inStartIndex + 8 );; + inOperator( InvMassScale1, inStartIndex + 9 );; + inOperator( InvInertiaScale1, inStartIndex + 10 );; + inOperator( Constraint, inStartIndex + 11 );; + inOperator( Name, inStartIndex + 12 );; + inOperator( Scene, inStartIndex + 13 );; + inOperator( UserData, inStartIndex + 14 );; + return 15 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointGeneratedInfo Info; + const PxJointGeneratedInfo* getInfo() { return &Info; } + }; + + class PxContactJoint; + struct PxContactJointGeneratedValues + : PxJointGeneratedValues { + PxVec3 Contact; + PxVec3 ContactNormal; + PxReal Penetration; + PxReal Resititution; + PxReal BounceThreshold; + const char * ConcreteTypeName; + PxContactJointGeneratedValues( const PxContactJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, Contact, PxContactJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, ContactNormal, PxContactJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, Penetration, PxContactJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, Resititution, PxContactJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, BounceThreshold, PxContactJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxContactJoint, ConcreteTypeName, PxContactJointGeneratedValues) + struct PxContactJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxContactJoint"; } + PxPropertyInfo Contact; + PxPropertyInfo ContactNormal; + PxPropertyInfo Penetration; + PxPropertyInfo Resititution; + PxPropertyInfo BounceThreshold; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxContactJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 6; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Contact, inStartIndex + 0 );; + inOperator( ContactNormal, inStartIndex + 1 );; + inOperator( Penetration, inStartIndex + 2 );; + inOperator( Resititution, inStartIndex + 3 );; + inOperator( BounceThreshold, inStartIndex + 4 );; + inOperator( ConcreteTypeName, inStartIndex + 5 );; + return 6 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxContactJointGeneratedInfo Info; + const PxContactJointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxD6Axis__EnumConversion[] = { + { "eX", static_cast( physx::PxD6Axis::eX ) }, + { "eY", static_cast( physx::PxD6Axis::eY ) }, + { "eZ", static_cast( physx::PxD6Axis::eZ ) }, + { "eTWIST", static_cast( physx::PxD6Axis::eTWIST ) }, + { "eSWING1", static_cast( physx::PxD6Axis::eSWING1 ) }, + { "eSWING2", static_cast( physx::PxD6Axis::eSWING2 ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxD6Axis::Enum > { PxEnumTraits() : NameConversion( g_physx__PxD6Axis__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxD6Motion__EnumConversion[] = { + { "eLOCKED", static_cast( physx::PxD6Motion::eLOCKED ) }, + { "eLIMITED", static_cast( physx::PxD6Motion::eLIMITED ) }, + { "eFREE", static_cast( physx::PxD6Motion::eFREE ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxD6Motion::Enum > { PxEnumTraits() : NameConversion( g_physx__PxD6Motion__EnumConversion ) {} const PxU32ToName* NameConversion; }; + static PxU32ToName g_physx__PxD6Drive__EnumConversion[] = { + { "eX", static_cast( physx::PxD6Drive::eX ) }, + { "eY", static_cast( physx::PxD6Drive::eY ) }, + { "eZ", static_cast( physx::PxD6Drive::eZ ) }, + { "eSWING", static_cast( physx::PxD6Drive::eSWING ) }, + { "eTWIST", static_cast( physx::PxD6Drive::eTWIST ) }, + { "eSLERP", static_cast( physx::PxD6Drive::eSLERP ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxD6Drive::Enum > { PxEnumTraits() : NameConversion( g_physx__PxD6Drive__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxD6Joint; + struct PxD6JointGeneratedValues + : PxJointGeneratedValues { + PxD6Motion::Enum Motion[physx::PxD6Axis::eCOUNT]; + PxReal TwistAngle; + PxReal Twist; + PxReal SwingYAngle; + PxReal SwingZAngle; + PxJointLinearLimit DistanceLimit; + PxJointLinearLimit LinearLimit; + PxJointAngularLimitPair TwistLimit; + PxJointLimitCone SwingLimit; + PxJointLimitPyramid PyramidSwingLimit; + PxD6JointDrive Drive[physx::PxD6Drive::eCOUNT]; + PxTransform DrivePosition; + PxReal ProjectionLinearTolerance; + PxReal ProjectionAngularTolerance; + const char * ConcreteTypeName; + PxD6JointGeneratedValues( const PxD6Joint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, Motion, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, TwistAngle, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, Twist, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, SwingYAngle, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, SwingZAngle, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, DistanceLimit, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, LinearLimit, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, TwistLimit, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, SwingLimit, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, PyramidSwingLimit, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, Drive, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, DrivePosition, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, ProjectionLinearTolerance, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, ProjectionAngularTolerance, PxD6JointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6Joint, ConcreteTypeName, PxD6JointGeneratedValues) + struct PxD6JointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxD6Joint"; } + PxIndexedPropertyInfo Motion; + PxReadOnlyPropertyInfo TwistAngle; + PxReadOnlyPropertyInfo Twist; + PxReadOnlyPropertyInfo SwingYAngle; + PxReadOnlyPropertyInfo SwingZAngle; + PxPropertyInfo DistanceLimit; + PxPropertyInfo LinearLimit; + PxPropertyInfo TwistLimit; + PxPropertyInfo SwingLimit; + PxPropertyInfo PyramidSwingLimit; + PxIndexedPropertyInfo Drive; + PxPropertyInfo DrivePosition; + PxPropertyInfo ProjectionLinearTolerance; + PxPropertyInfo ProjectionAngularTolerance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxD6JointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 15; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Motion, inStartIndex + 0 );; + inOperator( TwistAngle, inStartIndex + 1 );; + inOperator( Twist, inStartIndex + 2 );; + inOperator( SwingYAngle, inStartIndex + 3 );; + inOperator( SwingZAngle, inStartIndex + 4 );; + inOperator( DistanceLimit, inStartIndex + 5 );; + inOperator( LinearLimit, inStartIndex + 6 );; + inOperator( TwistLimit, inStartIndex + 7 );; + inOperator( SwingLimit, inStartIndex + 8 );; + inOperator( PyramidSwingLimit, inStartIndex + 9 );; + inOperator( Drive, inStartIndex + 10 );; + inOperator( DrivePosition, inStartIndex + 11 );; + inOperator( ProjectionLinearTolerance, inStartIndex + 12 );; + inOperator( ProjectionAngularTolerance, inStartIndex + 13 );; + inOperator( ConcreteTypeName, inStartIndex + 14 );; + return 15 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxD6JointGeneratedInfo Info; + const PxD6JointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxDistanceJointFlag__EnumConversion[] = { + { "eMAX_DISTANCE_ENABLED", static_cast( physx::PxDistanceJointFlag::eMAX_DISTANCE_ENABLED ) }, + { "eMIN_DISTANCE_ENABLED", static_cast( physx::PxDistanceJointFlag::eMIN_DISTANCE_ENABLED ) }, + { "eSPRING_ENABLED", static_cast( physx::PxDistanceJointFlag::eSPRING_ENABLED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxDistanceJointFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxDistanceJointFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxDistanceJoint; + struct PxDistanceJointGeneratedValues + : PxJointGeneratedValues { + PxReal Distance; + PxReal MinDistance; + PxReal MaxDistance; + PxReal Tolerance; + PxReal Stiffness; + PxReal Damping; + PxDistanceJointFlags DistanceJointFlags; + const char * ConcreteTypeName; + PxDistanceJointGeneratedValues( const PxDistanceJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, Distance, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, MinDistance, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, MaxDistance, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, Tolerance, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, Stiffness, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, Damping, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, DistanceJointFlags, PxDistanceJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxDistanceJoint, ConcreteTypeName, PxDistanceJointGeneratedValues) + struct PxDistanceJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxDistanceJoint"; } + PxReadOnlyPropertyInfo Distance; + PxPropertyInfo MinDistance; + PxPropertyInfo MaxDistance; + PxPropertyInfo Tolerance; + PxPropertyInfo Stiffness; + PxPropertyInfo Damping; + PxPropertyInfo DistanceJointFlags; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxDistanceJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 8; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Distance, inStartIndex + 0 );; + inOperator( MinDistance, inStartIndex + 1 );; + inOperator( MaxDistance, inStartIndex + 2 );; + inOperator( Tolerance, inStartIndex + 3 );; + inOperator( Stiffness, inStartIndex + 4 );; + inOperator( Damping, inStartIndex + 5 );; + inOperator( DistanceJointFlags, inStartIndex + 6 );; + inOperator( ConcreteTypeName, inStartIndex + 7 );; + return 8 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxDistanceJointGeneratedInfo Info; + const PxDistanceJointGeneratedInfo* getInfo() { return &Info; } + }; + + class PxFixedJoint; + struct PxFixedJointGeneratedValues + : PxJointGeneratedValues { + PxReal ProjectionLinearTolerance; + PxReal ProjectionAngularTolerance; + const char * ConcreteTypeName; + PxFixedJointGeneratedValues( const PxFixedJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxFixedJoint, ProjectionLinearTolerance, PxFixedJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxFixedJoint, ProjectionAngularTolerance, PxFixedJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxFixedJoint, ConcreteTypeName, PxFixedJointGeneratedValues) + struct PxFixedJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxFixedJoint"; } + PxPropertyInfo ProjectionLinearTolerance; + PxPropertyInfo ProjectionAngularTolerance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxFixedJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ProjectionLinearTolerance, inStartIndex + 0 );; + inOperator( ProjectionAngularTolerance, inStartIndex + 1 );; + inOperator( ConcreteTypeName, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxFixedJointGeneratedInfo Info; + const PxFixedJointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxPrismaticJointFlag__EnumConversion[] = { + { "eLIMIT_ENABLED", static_cast( physx::PxPrismaticJointFlag::eLIMIT_ENABLED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxPrismaticJointFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxPrismaticJointFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxPrismaticJoint; + struct PxPrismaticJointGeneratedValues + : PxJointGeneratedValues { + PxReal Position; + PxReal Velocity; + PxJointLinearLimitPair Limit; + PxPrismaticJointFlags PrismaticJointFlags; + PxReal ProjectionLinearTolerance; + PxReal ProjectionAngularTolerance; + const char * ConcreteTypeName; + PxPrismaticJointGeneratedValues( const PxPrismaticJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, Position, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, Velocity, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, Limit, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, PrismaticJointFlags, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, ProjectionLinearTolerance, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, ProjectionAngularTolerance, PxPrismaticJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxPrismaticJoint, ConcreteTypeName, PxPrismaticJointGeneratedValues) + struct PxPrismaticJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxPrismaticJoint"; } + PxReadOnlyPropertyInfo Position; + PxReadOnlyPropertyInfo Velocity; + PxPropertyInfo Limit; + PxPropertyInfo PrismaticJointFlags; + PxPropertyInfo ProjectionLinearTolerance; + PxPropertyInfo ProjectionAngularTolerance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxPrismaticJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 7; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Position, inStartIndex + 0 );; + inOperator( Velocity, inStartIndex + 1 );; + inOperator( Limit, inStartIndex + 2 );; + inOperator( PrismaticJointFlags, inStartIndex + 3 );; + inOperator( ProjectionLinearTolerance, inStartIndex + 4 );; + inOperator( ProjectionAngularTolerance, inStartIndex + 5 );; + inOperator( ConcreteTypeName, inStartIndex + 6 );; + return 7 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxPrismaticJointGeneratedInfo Info; + const PxPrismaticJointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxRevoluteJointFlag__EnumConversion[] = { + { "eLIMIT_ENABLED", static_cast( physx::PxRevoluteJointFlag::eLIMIT_ENABLED ) }, + { "eDRIVE_ENABLED", static_cast( physx::PxRevoluteJointFlag::eDRIVE_ENABLED ) }, + { "eDRIVE_FREESPIN", static_cast( physx::PxRevoluteJointFlag::eDRIVE_FREESPIN ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxRevoluteJointFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxRevoluteJointFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxRevoluteJoint; + struct PxRevoluteJointGeneratedValues + : PxJointGeneratedValues { + PxReal Angle; + PxReal Velocity; + PxJointAngularLimitPair Limit; + PxReal DriveVelocity; + PxReal DriveForceLimit; + PxReal DriveGearRatio; + PxRevoluteJointFlags RevoluteJointFlags; + PxReal ProjectionLinearTolerance; + PxReal ProjectionAngularTolerance; + const char * ConcreteTypeName; + PxRevoluteJointGeneratedValues( const PxRevoluteJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, Angle, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, Velocity, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, Limit, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, DriveVelocity, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, DriveForceLimit, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, DriveGearRatio, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, RevoluteJointFlags, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, ProjectionLinearTolerance, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, ProjectionAngularTolerance, PxRevoluteJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxRevoluteJoint, ConcreteTypeName, PxRevoluteJointGeneratedValues) + struct PxRevoluteJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxRevoluteJoint"; } + PxReadOnlyPropertyInfo Angle; + PxReadOnlyPropertyInfo Velocity; + PxPropertyInfo Limit; + PxPropertyInfo DriveVelocity; + PxPropertyInfo DriveForceLimit; + PxPropertyInfo DriveGearRatio; + PxPropertyInfo RevoluteJointFlags; + PxPropertyInfo ProjectionLinearTolerance; + PxPropertyInfo ProjectionAngularTolerance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxRevoluteJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 10; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Angle, inStartIndex + 0 );; + inOperator( Velocity, inStartIndex + 1 );; + inOperator( Limit, inStartIndex + 2 );; + inOperator( DriveVelocity, inStartIndex + 3 );; + inOperator( DriveForceLimit, inStartIndex + 4 );; + inOperator( DriveGearRatio, inStartIndex + 5 );; + inOperator( RevoluteJointFlags, inStartIndex + 6 );; + inOperator( ProjectionLinearTolerance, inStartIndex + 7 );; + inOperator( ProjectionAngularTolerance, inStartIndex + 8 );; + inOperator( ConcreteTypeName, inStartIndex + 9 );; + return 10 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxRevoluteJointGeneratedInfo Info; + const PxRevoluteJointGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxSphericalJointFlag__EnumConversion[] = { + { "eLIMIT_ENABLED", static_cast( physx::PxSphericalJointFlag::eLIMIT_ENABLED ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxSphericalJointFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxSphericalJointFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxSphericalJoint; + struct PxSphericalJointGeneratedValues + : PxJointGeneratedValues { + PxJointLimitCone LimitCone; + PxReal SwingYAngle; + PxReal SwingZAngle; + PxSphericalJointFlags SphericalJointFlags; + PxReal ProjectionLinearTolerance; + const char * ConcreteTypeName; + PxSphericalJointGeneratedValues( const PxSphericalJoint* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, LimitCone, PxSphericalJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, SwingYAngle, PxSphericalJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, SwingZAngle, PxSphericalJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, SphericalJointFlags, PxSphericalJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, ProjectionLinearTolerance, PxSphericalJointGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSphericalJoint, ConcreteTypeName, PxSphericalJointGeneratedValues) + struct PxSphericalJointGeneratedInfo + : PxJointGeneratedInfo + { + static const char* getClassName() { return "PxSphericalJoint"; } + PxPropertyInfo LimitCone; + PxReadOnlyPropertyInfo SwingYAngle; + PxReadOnlyPropertyInfo SwingZAngle; + PxPropertyInfo SphericalJointFlags; + PxPropertyInfo ProjectionLinearTolerance; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxSphericalJointGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 6; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( LimitCone, inStartIndex + 0 );; + inOperator( SwingYAngle, inStartIndex + 1 );; + inOperator( SwingZAngle, inStartIndex + 2 );; + inOperator( SphericalJointFlags, inStartIndex + 3 );; + inOperator( ProjectionLinearTolerance, inStartIndex + 4 );; + inOperator( ConcreteTypeName, inStartIndex + 5 );; + return 6 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSphericalJointGeneratedInfo Info; + const PxSphericalJointGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointLimitParameters; + struct PxJointLimitParametersGeneratedValues + { + PxReal Restitution; + PxReal BounceThreshold; + PxReal Stiffness; + PxReal Damping; + PxReal ContactDistance; + PxJointLimitParametersGeneratedValues( const PxJointLimitParameters* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitParameters, Restitution, PxJointLimitParametersGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitParameters, BounceThreshold, PxJointLimitParametersGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitParameters, Stiffness, PxJointLimitParametersGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitParameters, Damping, PxJointLimitParametersGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitParameters, ContactDistance, PxJointLimitParametersGeneratedValues) + struct PxJointLimitParametersGeneratedInfo + + { + static const char* getClassName() { return "PxJointLimitParameters"; } + PxPropertyInfo Restitution; + PxPropertyInfo BounceThreshold; + PxPropertyInfo Stiffness; + PxPropertyInfo Damping; + PxPropertyInfo ContactDistance; + + PxJointLimitParametersGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Restitution, inStartIndex + 0 );; + inOperator( BounceThreshold, inStartIndex + 1 );; + inOperator( Stiffness, inStartIndex + 2 );; + inOperator( Damping, inStartIndex + 3 );; + inOperator( ContactDistance, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointLimitParametersGeneratedInfo Info; + const PxJointLimitParametersGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointLinearLimit; + struct PxJointLinearLimitGeneratedValues + : PxJointLimitParametersGeneratedValues { + PxReal Value; + PxJointLinearLimitGeneratedValues( const PxJointLinearLimit* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLinearLimit, Value, PxJointLinearLimitGeneratedValues) + struct PxJointLinearLimitGeneratedInfo + : PxJointLimitParametersGeneratedInfo + { + static const char* getClassName() { return "PxJointLinearLimit"; } + PxPropertyInfo Value; + + PxJointLinearLimitGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointLimitParametersGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Value, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointLinearLimitGeneratedInfo Info; + const PxJointLinearLimitGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointLinearLimitPair; + struct PxJointLinearLimitPairGeneratedValues + : PxJointLimitParametersGeneratedValues { + PxReal Upper; + PxReal Lower; + PxJointLinearLimitPairGeneratedValues( const PxJointLinearLimitPair* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLinearLimitPair, Upper, PxJointLinearLimitPairGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLinearLimitPair, Lower, PxJointLinearLimitPairGeneratedValues) + struct PxJointLinearLimitPairGeneratedInfo + : PxJointLimitParametersGeneratedInfo + { + static const char* getClassName() { return "PxJointLinearLimitPair"; } + PxPropertyInfo Upper; + PxPropertyInfo Lower; + + PxJointLinearLimitPairGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointLimitParametersGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Upper, inStartIndex + 0 );; + inOperator( Lower, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointLinearLimitPairGeneratedInfo Info; + const PxJointLinearLimitPairGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointAngularLimitPair; + struct PxJointAngularLimitPairGeneratedValues + : PxJointLimitParametersGeneratedValues { + PxReal Upper; + PxReal Lower; + PxJointAngularLimitPairGeneratedValues( const PxJointAngularLimitPair* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointAngularLimitPair, Upper, PxJointAngularLimitPairGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointAngularLimitPair, Lower, PxJointAngularLimitPairGeneratedValues) + struct PxJointAngularLimitPairGeneratedInfo + : PxJointLimitParametersGeneratedInfo + { + static const char* getClassName() { return "PxJointAngularLimitPair"; } + PxPropertyInfo Upper; + PxPropertyInfo Lower; + + PxJointAngularLimitPairGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointLimitParametersGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Upper, inStartIndex + 0 );; + inOperator( Lower, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointAngularLimitPairGeneratedInfo Info; + const PxJointAngularLimitPairGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointLimitCone; + struct PxJointLimitConeGeneratedValues + : PxJointLimitParametersGeneratedValues { + PxReal YAngle; + PxReal ZAngle; + PxJointLimitConeGeneratedValues( const PxJointLimitCone* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitCone, YAngle, PxJointLimitConeGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitCone, ZAngle, PxJointLimitConeGeneratedValues) + struct PxJointLimitConeGeneratedInfo + : PxJointLimitParametersGeneratedInfo + { + static const char* getClassName() { return "PxJointLimitCone"; } + PxPropertyInfo YAngle; + PxPropertyInfo ZAngle; + + PxJointLimitConeGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointLimitParametersGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( YAngle, inStartIndex + 0 );; + inOperator( ZAngle, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointLimitConeGeneratedInfo Info; + const PxJointLimitConeGeneratedInfo* getInfo() { return &Info; } + }; + + class PxJointLimitPyramid; + struct PxJointLimitPyramidGeneratedValues + : PxJointLimitParametersGeneratedValues { + PxReal YAngleMin; + PxReal YAngleMax; + PxReal ZAngleMin; + PxReal ZAngleMax; + PxJointLimitPyramidGeneratedValues( const PxJointLimitPyramid* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitPyramid, YAngleMin, PxJointLimitPyramidGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitPyramid, YAngleMax, PxJointLimitPyramidGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitPyramid, ZAngleMin, PxJointLimitPyramidGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxJointLimitPyramid, ZAngleMax, PxJointLimitPyramidGeneratedValues) + struct PxJointLimitPyramidGeneratedInfo + : PxJointLimitParametersGeneratedInfo + { + static const char* getClassName() { return "PxJointLimitPyramid"; } + PxPropertyInfo YAngleMin; + PxPropertyInfo YAngleMax; + PxPropertyInfo ZAngleMin; + PxPropertyInfo ZAngleMax; + + PxJointLimitPyramidGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxJointLimitParametersGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxJointLimitParametersGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( YAngleMin, inStartIndex + 0 );; + inOperator( YAngleMax, inStartIndex + 1 );; + inOperator( ZAngleMin, inStartIndex + 2 );; + inOperator( ZAngleMax, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxJointLimitPyramidGeneratedInfo Info; + const PxJointLimitPyramidGeneratedInfo* getInfo() { return &Info; } + }; + + class PxSpring; + struct PxSpringGeneratedValues + { + PxReal Stiffness; + PxReal Damping; + PxSpringGeneratedValues( const PxSpring* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSpring, Stiffness, PxSpringGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSpring, Damping, PxSpringGeneratedValues) + struct PxSpringGeneratedInfo + + { + static const char* getClassName() { return "PxSpring"; } + PxPropertyInfo Stiffness; + PxPropertyInfo Damping; + + PxSpringGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Stiffness, inStartIndex + 0 );; + inOperator( Damping, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxSpringGeneratedInfo Info; + const PxSpringGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxD6JointDriveFlag__EnumConversion[] = { + { "eACCELERATION", static_cast( physx::PxD6JointDriveFlag::eACCELERATION ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxD6JointDriveFlag::Enum > { PxEnumTraits() : NameConversion( g_physx__PxD6JointDriveFlag__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxD6JointDrive; + struct PxD6JointDriveGeneratedValues + : PxSpringGeneratedValues { + PxReal ForceLimit; + PxD6JointDriveFlags Flags; + PxD6JointDriveGeneratedValues( const PxD6JointDrive* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6JointDrive, ForceLimit, PxD6JointDriveGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxD6JointDrive, Flags, PxD6JointDriveGeneratedValues) + struct PxD6JointDriveGeneratedInfo + : PxSpringGeneratedInfo + { + static const char* getClassName() { return "PxD6JointDrive"; } + PxPropertyInfo ForceLimit; + PxPropertyInfo Flags; + + PxD6JointDriveGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxSpringGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxSpringGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxSpringGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ForceLimit, inStartIndex + 0 );; + inOperator( Flags, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxD6JointDriveGeneratedInfo Info; + const PxD6JointDriveGeneratedInfo* getInfo() { return &Info; } + }; + + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON +#undef PX_PROPERTY_INFO_NAME diff --git a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h new file mode 100644 index 000000000..12be4126c --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h @@ -0,0 +1,72 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_EXTENSION_METADATAOBJECTS_H +#define PX_EXTENSION_METADATAOBJECTS_H +#include "PxPhysicsAPI.h" +#include "extensions/PxExtensionsAPI.h" +#include "PxMetaDataObjects.h" + +/** \addtogroup physics +@{ +*/ + +namespace physx +{ + +class PxD6Joint; +class PxJointLimitPair; +class PxJointLimitCone; + +struct PxExtensionsPropertyInfoName +{ + enum Enum + { + Unnamed = PxPropertyInfoName::LastPxPropertyInfoName, +#include "PxExtensionAutoGeneratedMetaDataObjectNames.h" + LastPxPropertyInfoName + }; +}; + +#define DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( type, prop, valueStruct ) \ + template<> struct PxPropertyToValueStructMemberMap< PxExtensionsPropertyInfoName::type##_##prop > \ + { \ + PxU32 Offset; \ + PxPropertyToValueStructMemberMap< PxExtensionsPropertyInfoName::type##_##prop >() : Offset( PX_OFFSET_OF_RT( valueStruct, prop ) ) {} \ + template void visitProp( TOperator inOperator, valueStruct& inStruct ) { inOperator( inStruct.prop ); } \ + }; + +#include "PxExtensionAutoGeneratedMetaDataObjects.h" + +#undef DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp b/src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp new file mode 100644 index 000000000..3a9f74b1d --- /dev/null +++ b/src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp @@ -0,0 +1,488 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#include "PxExtensionMetaDataObjects.h" +#include "PxMetaDataCppPrefix.h" +#include "PxExtensionsAPI.h" +using namespace physx; +void setPxJoint_Actors( PxJoint* inObj, PxRigidActor * inArg0, PxRigidActor * inArg1 ) { inObj->setActors( inArg0, inArg1 ); } +void getPxJoint_Actors( const PxJoint* inObj, PxRigidActor *& inArg0, PxRigidActor *& inArg1 ) { inObj->getActors( inArg0, inArg1 ); } +void setPxJoint_LocalPose( PxJoint* inObj, PxJointActorIndex::Enum inIndex, PxTransform inArg ){ inObj->setLocalPose( inIndex, inArg ); } +PxTransform getPxJoint_LocalPose( const PxJoint* inObj, PxJointActorIndex::Enum inIndex ) { return inObj->getLocalPose( inIndex ); } +PxTransform getPxJoint_RelativeTransform( const PxJoint* inObj ) { return inObj->getRelativeTransform(); } +PxVec3 getPxJoint_RelativeLinearVelocity( const PxJoint* inObj ) { return inObj->getRelativeLinearVelocity(); } +PxVec3 getPxJoint_RelativeAngularVelocity( const PxJoint* inObj ) { return inObj->getRelativeAngularVelocity(); } +void setPxJoint_BreakForce( PxJoint* inObj, PxReal inArg0, PxReal inArg1 ) { inObj->setBreakForce( inArg0, inArg1 ); } +void getPxJoint_BreakForce( const PxJoint* inObj, PxReal& inArg0, PxReal& inArg1 ) { inObj->getBreakForce( inArg0, inArg1 ); } +void setPxJoint_ConstraintFlags( PxJoint* inObj, PxConstraintFlags inArg){ inObj->setConstraintFlags( inArg ); } +PxConstraintFlags getPxJoint_ConstraintFlags( const PxJoint* inObj ) { return inObj->getConstraintFlags(); } +void setPxJoint_InvMassScale0( PxJoint* inObj, PxReal inArg){ inObj->setInvMassScale0( inArg ); } +PxReal getPxJoint_InvMassScale0( const PxJoint* inObj ) { return inObj->getInvMassScale0(); } +void setPxJoint_InvInertiaScale0( PxJoint* inObj, PxReal inArg){ inObj->setInvInertiaScale0( inArg ); } +PxReal getPxJoint_InvInertiaScale0( const PxJoint* inObj ) { return inObj->getInvInertiaScale0(); } +void setPxJoint_InvMassScale1( PxJoint* inObj, PxReal inArg){ inObj->setInvMassScale1( inArg ); } +PxReal getPxJoint_InvMassScale1( const PxJoint* inObj ) { return inObj->getInvMassScale1(); } +void setPxJoint_InvInertiaScale1( PxJoint* inObj, PxReal inArg){ inObj->setInvInertiaScale1( inArg ); } +PxReal getPxJoint_InvInertiaScale1( const PxJoint* inObj ) { return inObj->getInvInertiaScale1(); } +PxConstraint * getPxJoint_Constraint( const PxJoint* inObj ) { return inObj->getConstraint(); } +void setPxJoint_Name( PxJoint* inObj, const char * inArg){ inObj->setName( inArg ); } +const char * getPxJoint_Name( const PxJoint* inObj ) { return inObj->getName(); } +PxScene * getPxJoint_Scene( const PxJoint* inObj ) { return inObj->getScene(); } +inline void * getPxJointUserData( const PxJoint* inOwner ) { return inOwner->userData; } +inline void setPxJointUserData( PxJoint* inOwner, void * inData) { inOwner->userData = inData; } + PxJointGeneratedInfo::PxJointGeneratedInfo() + : Actors( "Actors", "actor0", "actor1", setPxJoint_Actors, getPxJoint_Actors) + , LocalPose( "LocalPose", setPxJoint_LocalPose, getPxJoint_LocalPose) + , RelativeTransform( "RelativeTransform", getPxJoint_RelativeTransform) + , RelativeLinearVelocity( "RelativeLinearVelocity", getPxJoint_RelativeLinearVelocity) + , RelativeAngularVelocity( "RelativeAngularVelocity", getPxJoint_RelativeAngularVelocity) + , BreakForce( "BreakForce", "force", "torque", setPxJoint_BreakForce, getPxJoint_BreakForce) + , ConstraintFlags( "ConstraintFlags", setPxJoint_ConstraintFlags, getPxJoint_ConstraintFlags) + , InvMassScale0( "InvMassScale0", setPxJoint_InvMassScale0, getPxJoint_InvMassScale0) + , InvInertiaScale0( "InvInertiaScale0", setPxJoint_InvInertiaScale0, getPxJoint_InvInertiaScale0) + , InvMassScale1( "InvMassScale1", setPxJoint_InvMassScale1, getPxJoint_InvMassScale1) + , InvInertiaScale1( "InvInertiaScale1", setPxJoint_InvInertiaScale1, getPxJoint_InvInertiaScale1) + , Constraint( "Constraint", getPxJoint_Constraint) + , Name( "Name", setPxJoint_Name, getPxJoint_Name) + , Scene( "Scene", getPxJoint_Scene) + , UserData( "UserData", setPxJointUserData, getPxJointUserData ) +{} + PxJointGeneratedValues::PxJointGeneratedValues( const PxJoint* inSource ) + :RelativeTransform( getPxJoint_RelativeTransform( inSource ) ) + ,RelativeLinearVelocity( getPxJoint_RelativeLinearVelocity( inSource ) ) + ,RelativeAngularVelocity( getPxJoint_RelativeAngularVelocity( inSource ) ) + ,ConstraintFlags( getPxJoint_ConstraintFlags( inSource ) ) + ,InvMassScale0( getPxJoint_InvMassScale0( inSource ) ) + ,InvInertiaScale0( getPxJoint_InvInertiaScale0( inSource ) ) + ,InvMassScale1( getPxJoint_InvMassScale1( inSource ) ) + ,InvInertiaScale1( getPxJoint_InvInertiaScale1( inSource ) ) + ,Constraint( getPxJoint_Constraint( inSource ) ) + ,Name( getPxJoint_Name( inSource ) ) + ,Scene( getPxJoint_Scene( inSource ) ) + ,UserData( inSource->userData ) +{ + PX_UNUSED(inSource); + getPxJoint_Actors( inSource, Actors[0], Actors[1] ); + for ( PxU32 idx = 0; idx < static_cast( physx::PxJointActorIndex::COUNT ); ++idx ) + LocalPose[idx] = getPxJoint_LocalPose( inSource, static_cast< PxJointActorIndex::Enum >( idx ) ); + getPxJoint_BreakForce( inSource, BreakForce[0], BreakForce[1] ); +} +void setPxContactJoint_Contact( PxContactJoint* inObj, const PxVec3 & inArg){ inObj->setContact( inArg ); } +PxVec3 getPxContactJoint_Contact( const PxContactJoint* inObj ) { return inObj->getContact(); } +void setPxContactJoint_ContactNormal( PxContactJoint* inObj, const PxVec3 & inArg){ inObj->setContactNormal( inArg ); } +PxVec3 getPxContactJoint_ContactNormal( const PxContactJoint* inObj ) { return inObj->getContactNormal(); } +void setPxContactJoint_Penetration( PxContactJoint* inObj, const PxReal inArg){ inObj->setPenetration( inArg ); } +PxReal getPxContactJoint_Penetration( const PxContactJoint* inObj ) { return inObj->getPenetration(); } +void setPxContactJoint_Resititution( PxContactJoint* inObj, const PxReal inArg){ inObj->setResititution( inArg ); } +PxReal getPxContactJoint_Resititution( const PxContactJoint* inObj ) { return inObj->getResititution(); } +void setPxContactJoint_BounceThreshold( PxContactJoint* inObj, const PxReal inArg){ inObj->setBounceThreshold( inArg ); } +PxReal getPxContactJoint_BounceThreshold( const PxContactJoint* inObj ) { return inObj->getBounceThreshold(); } +const char * getPxContactJoint_ConcreteTypeName( const PxContactJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxContactJointGeneratedInfo::PxContactJointGeneratedInfo() + : Contact( "Contact", setPxContactJoint_Contact, getPxContactJoint_Contact) + , ContactNormal( "ContactNormal", setPxContactJoint_ContactNormal, getPxContactJoint_ContactNormal) + , Penetration( "Penetration", setPxContactJoint_Penetration, getPxContactJoint_Penetration) + , Resititution( "Resititution", setPxContactJoint_Resititution, getPxContactJoint_Resititution) + , BounceThreshold( "BounceThreshold", setPxContactJoint_BounceThreshold, getPxContactJoint_BounceThreshold) + , ConcreteTypeName( "ConcreteTypeName", getPxContactJoint_ConcreteTypeName) +{} + PxContactJointGeneratedValues::PxContactJointGeneratedValues( const PxContactJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,Contact( getPxContactJoint_Contact( inSource ) ) + ,ContactNormal( getPxContactJoint_ContactNormal( inSource ) ) + ,Penetration( getPxContactJoint_Penetration( inSource ) ) + ,Resititution( getPxContactJoint_Resititution( inSource ) ) + ,BounceThreshold( getPxContactJoint_BounceThreshold( inSource ) ) + ,ConcreteTypeName( getPxContactJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxD6Joint_Motion( PxD6Joint* inObj, PxD6Axis::Enum inIndex, PxD6Motion::Enum inArg ){ inObj->setMotion( inIndex, inArg ); } +PxD6Motion::Enum getPxD6Joint_Motion( const PxD6Joint* inObj, PxD6Axis::Enum inIndex ) { return inObj->getMotion( inIndex ); } +PxReal getPxD6Joint_TwistAngle( const PxD6Joint* inObj ) { return inObj->getTwistAngle(); } +PxReal getPxD6Joint_Twist( const PxD6Joint* inObj ) { return inObj->getTwist(); } +PxReal getPxD6Joint_SwingYAngle( const PxD6Joint* inObj ) { return inObj->getSwingYAngle(); } +PxReal getPxD6Joint_SwingZAngle( const PxD6Joint* inObj ) { return inObj->getSwingZAngle(); } +void setPxD6Joint_DistanceLimit( PxD6Joint* inObj, const PxJointLinearLimit & inArg){ inObj->setDistanceLimit( inArg ); } +PxJointLinearLimit getPxD6Joint_DistanceLimit( const PxD6Joint* inObj ) { return inObj->getDistanceLimit(); } +void setPxD6Joint_LinearLimit( PxD6Joint* inObj, const PxJointLinearLimit & inArg){ inObj->setLinearLimit( inArg ); } +PxJointLinearLimit getPxD6Joint_LinearLimit( const PxD6Joint* inObj ) { return inObj->getLinearLimit(); } +void setPxD6Joint_TwistLimit( PxD6Joint* inObj, const PxJointAngularLimitPair & inArg){ inObj->setTwistLimit( inArg ); } +PxJointAngularLimitPair getPxD6Joint_TwistLimit( const PxD6Joint* inObj ) { return inObj->getTwistLimit(); } +void setPxD6Joint_SwingLimit( PxD6Joint* inObj, const PxJointLimitCone & inArg){ inObj->setSwingLimit( inArg ); } +PxJointLimitCone getPxD6Joint_SwingLimit( const PxD6Joint* inObj ) { return inObj->getSwingLimit(); } +void setPxD6Joint_PyramidSwingLimit( PxD6Joint* inObj, const PxJointLimitPyramid & inArg){ inObj->setPyramidSwingLimit( inArg ); } +PxJointLimitPyramid getPxD6Joint_PyramidSwingLimit( const PxD6Joint* inObj ) { return inObj->getPyramidSwingLimit(); } +void setPxD6Joint_Drive( PxD6Joint* inObj, PxD6Drive::Enum inIndex, PxD6JointDrive inArg ){ inObj->setDrive( inIndex, inArg ); } +PxD6JointDrive getPxD6Joint_Drive( const PxD6Joint* inObj, PxD6Drive::Enum inIndex ) { return inObj->getDrive( inIndex ); } +void setPxD6Joint_DrivePosition( PxD6Joint* inObj, const PxTransform & inArg){ inObj->setDrivePosition( inArg ); } +PxTransform getPxD6Joint_DrivePosition( const PxD6Joint* inObj ) { return inObj->getDrivePosition(); } +void setPxD6Joint_ProjectionLinearTolerance( PxD6Joint* inObj, PxReal inArg){ inObj->setProjectionLinearTolerance( inArg ); } +PxReal getPxD6Joint_ProjectionLinearTolerance( const PxD6Joint* inObj ) { return inObj->getProjectionLinearTolerance(); } +void setPxD6Joint_ProjectionAngularTolerance( PxD6Joint* inObj, PxReal inArg){ inObj->setProjectionAngularTolerance( inArg ); } +PxReal getPxD6Joint_ProjectionAngularTolerance( const PxD6Joint* inObj ) { return inObj->getProjectionAngularTolerance(); } +const char * getPxD6Joint_ConcreteTypeName( const PxD6Joint* inObj ) { return inObj->getConcreteTypeName(); } + PxD6JointGeneratedInfo::PxD6JointGeneratedInfo() + : Motion( "Motion", setPxD6Joint_Motion, getPxD6Joint_Motion) + , TwistAngle( "TwistAngle", getPxD6Joint_TwistAngle) + , Twist( "Twist", getPxD6Joint_Twist) + , SwingYAngle( "SwingYAngle", getPxD6Joint_SwingYAngle) + , SwingZAngle( "SwingZAngle", getPxD6Joint_SwingZAngle) + , DistanceLimit( "DistanceLimit", setPxD6Joint_DistanceLimit, getPxD6Joint_DistanceLimit) + , LinearLimit( "LinearLimit", setPxD6Joint_LinearLimit, getPxD6Joint_LinearLimit) + , TwistLimit( "TwistLimit", setPxD6Joint_TwistLimit, getPxD6Joint_TwistLimit) + , SwingLimit( "SwingLimit", setPxD6Joint_SwingLimit, getPxD6Joint_SwingLimit) + , PyramidSwingLimit( "PyramidSwingLimit", setPxD6Joint_PyramidSwingLimit, getPxD6Joint_PyramidSwingLimit) + , Drive( "Drive", setPxD6Joint_Drive, getPxD6Joint_Drive) + , DrivePosition( "DrivePosition", setPxD6Joint_DrivePosition, getPxD6Joint_DrivePosition) + , ProjectionLinearTolerance( "ProjectionLinearTolerance", setPxD6Joint_ProjectionLinearTolerance, getPxD6Joint_ProjectionLinearTolerance) + , ProjectionAngularTolerance( "ProjectionAngularTolerance", setPxD6Joint_ProjectionAngularTolerance, getPxD6Joint_ProjectionAngularTolerance) + , ConcreteTypeName( "ConcreteTypeName", getPxD6Joint_ConcreteTypeName) +{} + PxD6JointGeneratedValues::PxD6JointGeneratedValues( const PxD6Joint* inSource ) + :PxJointGeneratedValues( inSource ) + ,TwistAngle( getPxD6Joint_TwistAngle( inSource ) ) + ,Twist( getPxD6Joint_Twist( inSource ) ) + ,SwingYAngle( getPxD6Joint_SwingYAngle( inSource ) ) + ,SwingZAngle( getPxD6Joint_SwingZAngle( inSource ) ) + ,DistanceLimit( getPxD6Joint_DistanceLimit( inSource ) ) + ,LinearLimit( getPxD6Joint_LinearLimit( inSource ) ) + ,TwistLimit( getPxD6Joint_TwistLimit( inSource ) ) + ,SwingLimit( getPxD6Joint_SwingLimit( inSource ) ) + ,PyramidSwingLimit( getPxD6Joint_PyramidSwingLimit( inSource ) ) + ,DrivePosition( getPxD6Joint_DrivePosition( inSource ) ) + ,ProjectionLinearTolerance( getPxD6Joint_ProjectionLinearTolerance( inSource ) ) + ,ProjectionAngularTolerance( getPxD6Joint_ProjectionAngularTolerance( inSource ) ) + ,ConcreteTypeName( getPxD6Joint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); + for ( PxU32 idx = 0; idx < static_cast( physx::PxD6Axis::eCOUNT ); ++idx ) + Motion[idx] = getPxD6Joint_Motion( inSource, static_cast< PxD6Axis::Enum >( idx ) ); + for ( PxU32 idx = 0; idx < static_cast( physx::PxD6Drive::eCOUNT ); ++idx ) + Drive[idx] = getPxD6Joint_Drive( inSource, static_cast< PxD6Drive::Enum >( idx ) ); +} +PxReal getPxDistanceJoint_Distance( const PxDistanceJoint* inObj ) { return inObj->getDistance(); } +void setPxDistanceJoint_MinDistance( PxDistanceJoint* inObj, PxReal inArg){ inObj->setMinDistance( inArg ); } +PxReal getPxDistanceJoint_MinDistance( const PxDistanceJoint* inObj ) { return inObj->getMinDistance(); } +void setPxDistanceJoint_MaxDistance( PxDistanceJoint* inObj, PxReal inArg){ inObj->setMaxDistance( inArg ); } +PxReal getPxDistanceJoint_MaxDistance( const PxDistanceJoint* inObj ) { return inObj->getMaxDistance(); } +void setPxDistanceJoint_Tolerance( PxDistanceJoint* inObj, PxReal inArg){ inObj->setTolerance( inArg ); } +PxReal getPxDistanceJoint_Tolerance( const PxDistanceJoint* inObj ) { return inObj->getTolerance(); } +void setPxDistanceJoint_Stiffness( PxDistanceJoint* inObj, PxReal inArg){ inObj->setStiffness( inArg ); } +PxReal getPxDistanceJoint_Stiffness( const PxDistanceJoint* inObj ) { return inObj->getStiffness(); } +void setPxDistanceJoint_Damping( PxDistanceJoint* inObj, PxReal inArg){ inObj->setDamping( inArg ); } +PxReal getPxDistanceJoint_Damping( const PxDistanceJoint* inObj ) { return inObj->getDamping(); } +void setPxDistanceJoint_DistanceJointFlags( PxDistanceJoint* inObj, PxDistanceJointFlags inArg){ inObj->setDistanceJointFlags( inArg ); } +PxDistanceJointFlags getPxDistanceJoint_DistanceJointFlags( const PxDistanceJoint* inObj ) { return inObj->getDistanceJointFlags(); } +const char * getPxDistanceJoint_ConcreteTypeName( const PxDistanceJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxDistanceJointGeneratedInfo::PxDistanceJointGeneratedInfo() + : Distance( "Distance", getPxDistanceJoint_Distance) + , MinDistance( "MinDistance", setPxDistanceJoint_MinDistance, getPxDistanceJoint_MinDistance) + , MaxDistance( "MaxDistance", setPxDistanceJoint_MaxDistance, getPxDistanceJoint_MaxDistance) + , Tolerance( "Tolerance", setPxDistanceJoint_Tolerance, getPxDistanceJoint_Tolerance) + , Stiffness( "Stiffness", setPxDistanceJoint_Stiffness, getPxDistanceJoint_Stiffness) + , Damping( "Damping", setPxDistanceJoint_Damping, getPxDistanceJoint_Damping) + , DistanceJointFlags( "DistanceJointFlags", setPxDistanceJoint_DistanceJointFlags, getPxDistanceJoint_DistanceJointFlags) + , ConcreteTypeName( "ConcreteTypeName", getPxDistanceJoint_ConcreteTypeName) +{} + PxDistanceJointGeneratedValues::PxDistanceJointGeneratedValues( const PxDistanceJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,Distance( getPxDistanceJoint_Distance( inSource ) ) + ,MinDistance( getPxDistanceJoint_MinDistance( inSource ) ) + ,MaxDistance( getPxDistanceJoint_MaxDistance( inSource ) ) + ,Tolerance( getPxDistanceJoint_Tolerance( inSource ) ) + ,Stiffness( getPxDistanceJoint_Stiffness( inSource ) ) + ,Damping( getPxDistanceJoint_Damping( inSource ) ) + ,DistanceJointFlags( getPxDistanceJoint_DistanceJointFlags( inSource ) ) + ,ConcreteTypeName( getPxDistanceJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxFixedJoint_ProjectionLinearTolerance( PxFixedJoint* inObj, PxReal inArg){ inObj->setProjectionLinearTolerance( inArg ); } +PxReal getPxFixedJoint_ProjectionLinearTolerance( const PxFixedJoint* inObj ) { return inObj->getProjectionLinearTolerance(); } +void setPxFixedJoint_ProjectionAngularTolerance( PxFixedJoint* inObj, PxReal inArg){ inObj->setProjectionAngularTolerance( inArg ); } +PxReal getPxFixedJoint_ProjectionAngularTolerance( const PxFixedJoint* inObj ) { return inObj->getProjectionAngularTolerance(); } +const char * getPxFixedJoint_ConcreteTypeName( const PxFixedJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxFixedJointGeneratedInfo::PxFixedJointGeneratedInfo() + : ProjectionLinearTolerance( "ProjectionLinearTolerance", setPxFixedJoint_ProjectionLinearTolerance, getPxFixedJoint_ProjectionLinearTolerance) + , ProjectionAngularTolerance( "ProjectionAngularTolerance", setPxFixedJoint_ProjectionAngularTolerance, getPxFixedJoint_ProjectionAngularTolerance) + , ConcreteTypeName( "ConcreteTypeName", getPxFixedJoint_ConcreteTypeName) +{} + PxFixedJointGeneratedValues::PxFixedJointGeneratedValues( const PxFixedJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,ProjectionLinearTolerance( getPxFixedJoint_ProjectionLinearTolerance( inSource ) ) + ,ProjectionAngularTolerance( getPxFixedJoint_ProjectionAngularTolerance( inSource ) ) + ,ConcreteTypeName( getPxFixedJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxReal getPxPrismaticJoint_Position( const PxPrismaticJoint* inObj ) { return inObj->getPosition(); } +PxReal getPxPrismaticJoint_Velocity( const PxPrismaticJoint* inObj ) { return inObj->getVelocity(); } +void setPxPrismaticJoint_Limit( PxPrismaticJoint* inObj, const PxJointLinearLimitPair & inArg){ inObj->setLimit( inArg ); } +PxJointLinearLimitPair getPxPrismaticJoint_Limit( const PxPrismaticJoint* inObj ) { return inObj->getLimit(); } +void setPxPrismaticJoint_PrismaticJointFlags( PxPrismaticJoint* inObj, PxPrismaticJointFlags inArg){ inObj->setPrismaticJointFlags( inArg ); } +PxPrismaticJointFlags getPxPrismaticJoint_PrismaticJointFlags( const PxPrismaticJoint* inObj ) { return inObj->getPrismaticJointFlags(); } +void setPxPrismaticJoint_ProjectionLinearTolerance( PxPrismaticJoint* inObj, PxReal inArg){ inObj->setProjectionLinearTolerance( inArg ); } +PxReal getPxPrismaticJoint_ProjectionLinearTolerance( const PxPrismaticJoint* inObj ) { return inObj->getProjectionLinearTolerance(); } +void setPxPrismaticJoint_ProjectionAngularTolerance( PxPrismaticJoint* inObj, PxReal inArg){ inObj->setProjectionAngularTolerance( inArg ); } +PxReal getPxPrismaticJoint_ProjectionAngularTolerance( const PxPrismaticJoint* inObj ) { return inObj->getProjectionAngularTolerance(); } +const char * getPxPrismaticJoint_ConcreteTypeName( const PxPrismaticJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxPrismaticJointGeneratedInfo::PxPrismaticJointGeneratedInfo() + : Position( "Position", getPxPrismaticJoint_Position) + , Velocity( "Velocity", getPxPrismaticJoint_Velocity) + , Limit( "Limit", setPxPrismaticJoint_Limit, getPxPrismaticJoint_Limit) + , PrismaticJointFlags( "PrismaticJointFlags", setPxPrismaticJoint_PrismaticJointFlags, getPxPrismaticJoint_PrismaticJointFlags) + , ProjectionLinearTolerance( "ProjectionLinearTolerance", setPxPrismaticJoint_ProjectionLinearTolerance, getPxPrismaticJoint_ProjectionLinearTolerance) + , ProjectionAngularTolerance( "ProjectionAngularTolerance", setPxPrismaticJoint_ProjectionAngularTolerance, getPxPrismaticJoint_ProjectionAngularTolerance) + , ConcreteTypeName( "ConcreteTypeName", getPxPrismaticJoint_ConcreteTypeName) +{} + PxPrismaticJointGeneratedValues::PxPrismaticJointGeneratedValues( const PxPrismaticJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,Position( getPxPrismaticJoint_Position( inSource ) ) + ,Velocity( getPxPrismaticJoint_Velocity( inSource ) ) + ,Limit( getPxPrismaticJoint_Limit( inSource ) ) + ,PrismaticJointFlags( getPxPrismaticJoint_PrismaticJointFlags( inSource ) ) + ,ProjectionLinearTolerance( getPxPrismaticJoint_ProjectionLinearTolerance( inSource ) ) + ,ProjectionAngularTolerance( getPxPrismaticJoint_ProjectionAngularTolerance( inSource ) ) + ,ConcreteTypeName( getPxPrismaticJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxReal getPxRevoluteJoint_Angle( const PxRevoluteJoint* inObj ) { return inObj->getAngle(); } +PxReal getPxRevoluteJoint_Velocity( const PxRevoluteJoint* inObj ) { return inObj->getVelocity(); } +void setPxRevoluteJoint_Limit( PxRevoluteJoint* inObj, const PxJointAngularLimitPair & inArg){ inObj->setLimit( inArg ); } +PxJointAngularLimitPair getPxRevoluteJoint_Limit( const PxRevoluteJoint* inObj ) { return inObj->getLimit(); } +void setPxRevoluteJoint_DriveVelocity( PxRevoluteJoint* inObj, PxReal inArg){ inObj->setDriveVelocity( inArg ); } +PxReal getPxRevoluteJoint_DriveVelocity( const PxRevoluteJoint* inObj ) { return inObj->getDriveVelocity(); } +void setPxRevoluteJoint_DriveForceLimit( PxRevoluteJoint* inObj, PxReal inArg){ inObj->setDriveForceLimit( inArg ); } +PxReal getPxRevoluteJoint_DriveForceLimit( const PxRevoluteJoint* inObj ) { return inObj->getDriveForceLimit(); } +void setPxRevoluteJoint_DriveGearRatio( PxRevoluteJoint* inObj, PxReal inArg){ inObj->setDriveGearRatio( inArg ); } +PxReal getPxRevoluteJoint_DriveGearRatio( const PxRevoluteJoint* inObj ) { return inObj->getDriveGearRatio(); } +void setPxRevoluteJoint_RevoluteJointFlags( PxRevoluteJoint* inObj, PxRevoluteJointFlags inArg){ inObj->setRevoluteJointFlags( inArg ); } +PxRevoluteJointFlags getPxRevoluteJoint_RevoluteJointFlags( const PxRevoluteJoint* inObj ) { return inObj->getRevoluteJointFlags(); } +void setPxRevoluteJoint_ProjectionLinearTolerance( PxRevoluteJoint* inObj, PxReal inArg){ inObj->setProjectionLinearTolerance( inArg ); } +PxReal getPxRevoluteJoint_ProjectionLinearTolerance( const PxRevoluteJoint* inObj ) { return inObj->getProjectionLinearTolerance(); } +void setPxRevoluteJoint_ProjectionAngularTolerance( PxRevoluteJoint* inObj, PxReal inArg){ inObj->setProjectionAngularTolerance( inArg ); } +PxReal getPxRevoluteJoint_ProjectionAngularTolerance( const PxRevoluteJoint* inObj ) { return inObj->getProjectionAngularTolerance(); } +const char * getPxRevoluteJoint_ConcreteTypeName( const PxRevoluteJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxRevoluteJointGeneratedInfo::PxRevoluteJointGeneratedInfo() + : Angle( "Angle", getPxRevoluteJoint_Angle) + , Velocity( "Velocity", getPxRevoluteJoint_Velocity) + , Limit( "Limit", setPxRevoluteJoint_Limit, getPxRevoluteJoint_Limit) + , DriveVelocity( "DriveVelocity", setPxRevoluteJoint_DriveVelocity, getPxRevoluteJoint_DriveVelocity) + , DriveForceLimit( "DriveForceLimit", setPxRevoluteJoint_DriveForceLimit, getPxRevoluteJoint_DriveForceLimit) + , DriveGearRatio( "DriveGearRatio", setPxRevoluteJoint_DriveGearRatio, getPxRevoluteJoint_DriveGearRatio) + , RevoluteJointFlags( "RevoluteJointFlags", setPxRevoluteJoint_RevoluteJointFlags, getPxRevoluteJoint_RevoluteJointFlags) + , ProjectionLinearTolerance( "ProjectionLinearTolerance", setPxRevoluteJoint_ProjectionLinearTolerance, getPxRevoluteJoint_ProjectionLinearTolerance) + , ProjectionAngularTolerance( "ProjectionAngularTolerance", setPxRevoluteJoint_ProjectionAngularTolerance, getPxRevoluteJoint_ProjectionAngularTolerance) + , ConcreteTypeName( "ConcreteTypeName", getPxRevoluteJoint_ConcreteTypeName) +{} + PxRevoluteJointGeneratedValues::PxRevoluteJointGeneratedValues( const PxRevoluteJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,Angle( getPxRevoluteJoint_Angle( inSource ) ) + ,Velocity( getPxRevoluteJoint_Velocity( inSource ) ) + ,Limit( getPxRevoluteJoint_Limit( inSource ) ) + ,DriveVelocity( getPxRevoluteJoint_DriveVelocity( inSource ) ) + ,DriveForceLimit( getPxRevoluteJoint_DriveForceLimit( inSource ) ) + ,DriveGearRatio( getPxRevoluteJoint_DriveGearRatio( inSource ) ) + ,RevoluteJointFlags( getPxRevoluteJoint_RevoluteJointFlags( inSource ) ) + ,ProjectionLinearTolerance( getPxRevoluteJoint_ProjectionLinearTolerance( inSource ) ) + ,ProjectionAngularTolerance( getPxRevoluteJoint_ProjectionAngularTolerance( inSource ) ) + ,ConcreteTypeName( getPxRevoluteJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxSphericalJoint_LimitCone( PxSphericalJoint* inObj, const PxJointLimitCone & inArg){ inObj->setLimitCone( inArg ); } +PxJointLimitCone getPxSphericalJoint_LimitCone( const PxSphericalJoint* inObj ) { return inObj->getLimitCone(); } +PxReal getPxSphericalJoint_SwingYAngle( const PxSphericalJoint* inObj ) { return inObj->getSwingYAngle(); } +PxReal getPxSphericalJoint_SwingZAngle( const PxSphericalJoint* inObj ) { return inObj->getSwingZAngle(); } +void setPxSphericalJoint_SphericalJointFlags( PxSphericalJoint* inObj, PxSphericalJointFlags inArg){ inObj->setSphericalJointFlags( inArg ); } +PxSphericalJointFlags getPxSphericalJoint_SphericalJointFlags( const PxSphericalJoint* inObj ) { return inObj->getSphericalJointFlags(); } +void setPxSphericalJoint_ProjectionLinearTolerance( PxSphericalJoint* inObj, PxReal inArg){ inObj->setProjectionLinearTolerance( inArg ); } +PxReal getPxSphericalJoint_ProjectionLinearTolerance( const PxSphericalJoint* inObj ) { return inObj->getProjectionLinearTolerance(); } +const char * getPxSphericalJoint_ConcreteTypeName( const PxSphericalJoint* inObj ) { return inObj->getConcreteTypeName(); } + PxSphericalJointGeneratedInfo::PxSphericalJointGeneratedInfo() + : LimitCone( "LimitCone", setPxSphericalJoint_LimitCone, getPxSphericalJoint_LimitCone) + , SwingYAngle( "SwingYAngle", getPxSphericalJoint_SwingYAngle) + , SwingZAngle( "SwingZAngle", getPxSphericalJoint_SwingZAngle) + , SphericalJointFlags( "SphericalJointFlags", setPxSphericalJoint_SphericalJointFlags, getPxSphericalJoint_SphericalJointFlags) + , ProjectionLinearTolerance( "ProjectionLinearTolerance", setPxSphericalJoint_ProjectionLinearTolerance, getPxSphericalJoint_ProjectionLinearTolerance) + , ConcreteTypeName( "ConcreteTypeName", getPxSphericalJoint_ConcreteTypeName) +{} + PxSphericalJointGeneratedValues::PxSphericalJointGeneratedValues( const PxSphericalJoint* inSource ) + :PxJointGeneratedValues( inSource ) + ,LimitCone( getPxSphericalJoint_LimitCone( inSource ) ) + ,SwingYAngle( getPxSphericalJoint_SwingYAngle( inSource ) ) + ,SwingZAngle( getPxSphericalJoint_SwingZAngle( inSource ) ) + ,SphericalJointFlags( getPxSphericalJoint_SphericalJointFlags( inSource ) ) + ,ProjectionLinearTolerance( getPxSphericalJoint_ProjectionLinearTolerance( inSource ) ) + ,ConcreteTypeName( getPxSphericalJoint_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointLimitParametersRestitution( const PxJointLimitParameters* inOwner ) { return inOwner->restitution; } +inline void setPxJointLimitParametersRestitution( PxJointLimitParameters* inOwner, PxReal inData) { inOwner->restitution = inData; } +inline PxReal getPxJointLimitParametersBounceThreshold( const PxJointLimitParameters* inOwner ) { return inOwner->bounceThreshold; } +inline void setPxJointLimitParametersBounceThreshold( PxJointLimitParameters* inOwner, PxReal inData) { inOwner->bounceThreshold = inData; } +inline PxReal getPxJointLimitParametersStiffness( const PxJointLimitParameters* inOwner ) { return inOwner->stiffness; } +inline void setPxJointLimitParametersStiffness( PxJointLimitParameters* inOwner, PxReal inData) { inOwner->stiffness = inData; } +inline PxReal getPxJointLimitParametersDamping( const PxJointLimitParameters* inOwner ) { return inOwner->damping; } +inline void setPxJointLimitParametersDamping( PxJointLimitParameters* inOwner, PxReal inData) { inOwner->damping = inData; } +inline PxReal getPxJointLimitParametersContactDistance( const PxJointLimitParameters* inOwner ) { return inOwner->contactDistance; } +inline void setPxJointLimitParametersContactDistance( PxJointLimitParameters* inOwner, PxReal inData) { inOwner->contactDistance = inData; } + PxJointLimitParametersGeneratedInfo::PxJointLimitParametersGeneratedInfo() + : Restitution( "Restitution", setPxJointLimitParametersRestitution, getPxJointLimitParametersRestitution ) + , BounceThreshold( "BounceThreshold", setPxJointLimitParametersBounceThreshold, getPxJointLimitParametersBounceThreshold ) + , Stiffness( "Stiffness", setPxJointLimitParametersStiffness, getPxJointLimitParametersStiffness ) + , Damping( "Damping", setPxJointLimitParametersDamping, getPxJointLimitParametersDamping ) + , ContactDistance( "ContactDistance", setPxJointLimitParametersContactDistance, getPxJointLimitParametersContactDistance ) +{} + PxJointLimitParametersGeneratedValues::PxJointLimitParametersGeneratedValues( const PxJointLimitParameters* inSource ) + :Restitution( inSource->restitution ) + ,BounceThreshold( inSource->bounceThreshold ) + ,Stiffness( inSource->stiffness ) + ,Damping( inSource->damping ) + ,ContactDistance( inSource->contactDistance ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointLinearLimitValue( const PxJointLinearLimit* inOwner ) { return inOwner->value; } +inline void setPxJointLinearLimitValue( PxJointLinearLimit* inOwner, PxReal inData) { inOwner->value = inData; } + PxJointLinearLimitGeneratedInfo::PxJointLinearLimitGeneratedInfo() + : Value( "Value", setPxJointLinearLimitValue, getPxJointLinearLimitValue ) +{} + PxJointLinearLimitGeneratedValues::PxJointLinearLimitGeneratedValues( const PxJointLinearLimit* inSource ) + :PxJointLimitParametersGeneratedValues( inSource ) + ,Value( inSource->value ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointLinearLimitPairUpper( const PxJointLinearLimitPair* inOwner ) { return inOwner->upper; } +inline void setPxJointLinearLimitPairUpper( PxJointLinearLimitPair* inOwner, PxReal inData) { inOwner->upper = inData; } +inline PxReal getPxJointLinearLimitPairLower( const PxJointLinearLimitPair* inOwner ) { return inOwner->lower; } +inline void setPxJointLinearLimitPairLower( PxJointLinearLimitPair* inOwner, PxReal inData) { inOwner->lower = inData; } + PxJointLinearLimitPairGeneratedInfo::PxJointLinearLimitPairGeneratedInfo() + : Upper( "Upper", setPxJointLinearLimitPairUpper, getPxJointLinearLimitPairUpper ) + , Lower( "Lower", setPxJointLinearLimitPairLower, getPxJointLinearLimitPairLower ) +{} + PxJointLinearLimitPairGeneratedValues::PxJointLinearLimitPairGeneratedValues( const PxJointLinearLimitPair* inSource ) + :PxJointLimitParametersGeneratedValues( inSource ) + ,Upper( inSource->upper ) + ,Lower( inSource->lower ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointAngularLimitPairUpper( const PxJointAngularLimitPair* inOwner ) { return inOwner->upper; } +inline void setPxJointAngularLimitPairUpper( PxJointAngularLimitPair* inOwner, PxReal inData) { inOwner->upper = inData; } +inline PxReal getPxJointAngularLimitPairLower( const PxJointAngularLimitPair* inOwner ) { return inOwner->lower; } +inline void setPxJointAngularLimitPairLower( PxJointAngularLimitPair* inOwner, PxReal inData) { inOwner->lower = inData; } + PxJointAngularLimitPairGeneratedInfo::PxJointAngularLimitPairGeneratedInfo() + : Upper( "Upper", setPxJointAngularLimitPairUpper, getPxJointAngularLimitPairUpper ) + , Lower( "Lower", setPxJointAngularLimitPairLower, getPxJointAngularLimitPairLower ) +{} + PxJointAngularLimitPairGeneratedValues::PxJointAngularLimitPairGeneratedValues( const PxJointAngularLimitPair* inSource ) + :PxJointLimitParametersGeneratedValues( inSource ) + ,Upper( inSource->upper ) + ,Lower( inSource->lower ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointLimitConeYAngle( const PxJointLimitCone* inOwner ) { return inOwner->yAngle; } +inline void setPxJointLimitConeYAngle( PxJointLimitCone* inOwner, PxReal inData) { inOwner->yAngle = inData; } +inline PxReal getPxJointLimitConeZAngle( const PxJointLimitCone* inOwner ) { return inOwner->zAngle; } +inline void setPxJointLimitConeZAngle( PxJointLimitCone* inOwner, PxReal inData) { inOwner->zAngle = inData; } + PxJointLimitConeGeneratedInfo::PxJointLimitConeGeneratedInfo() + : YAngle( "YAngle", setPxJointLimitConeYAngle, getPxJointLimitConeYAngle ) + , ZAngle( "ZAngle", setPxJointLimitConeZAngle, getPxJointLimitConeZAngle ) +{} + PxJointLimitConeGeneratedValues::PxJointLimitConeGeneratedValues( const PxJointLimitCone* inSource ) + :PxJointLimitParametersGeneratedValues( inSource ) + ,YAngle( inSource->yAngle ) + ,ZAngle( inSource->zAngle ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxJointLimitPyramidYAngleMin( const PxJointLimitPyramid* inOwner ) { return inOwner->yAngleMin; } +inline void setPxJointLimitPyramidYAngleMin( PxJointLimitPyramid* inOwner, PxReal inData) { inOwner->yAngleMin = inData; } +inline PxReal getPxJointLimitPyramidYAngleMax( const PxJointLimitPyramid* inOwner ) { return inOwner->yAngleMax; } +inline void setPxJointLimitPyramidYAngleMax( PxJointLimitPyramid* inOwner, PxReal inData) { inOwner->yAngleMax = inData; } +inline PxReal getPxJointLimitPyramidZAngleMin( const PxJointLimitPyramid* inOwner ) { return inOwner->zAngleMin; } +inline void setPxJointLimitPyramidZAngleMin( PxJointLimitPyramid* inOwner, PxReal inData) { inOwner->zAngleMin = inData; } +inline PxReal getPxJointLimitPyramidZAngleMax( const PxJointLimitPyramid* inOwner ) { return inOwner->zAngleMax; } +inline void setPxJointLimitPyramidZAngleMax( PxJointLimitPyramid* inOwner, PxReal inData) { inOwner->zAngleMax = inData; } + PxJointLimitPyramidGeneratedInfo::PxJointLimitPyramidGeneratedInfo() + : YAngleMin( "YAngleMin", setPxJointLimitPyramidYAngleMin, getPxJointLimitPyramidYAngleMin ) + , YAngleMax( "YAngleMax", setPxJointLimitPyramidYAngleMax, getPxJointLimitPyramidYAngleMax ) + , ZAngleMin( "ZAngleMin", setPxJointLimitPyramidZAngleMin, getPxJointLimitPyramidZAngleMin ) + , ZAngleMax( "ZAngleMax", setPxJointLimitPyramidZAngleMax, getPxJointLimitPyramidZAngleMax ) +{} + PxJointLimitPyramidGeneratedValues::PxJointLimitPyramidGeneratedValues( const PxJointLimitPyramid* inSource ) + :PxJointLimitParametersGeneratedValues( inSource ) + ,YAngleMin( inSource->yAngleMin ) + ,YAngleMax( inSource->yAngleMax ) + ,ZAngleMin( inSource->zAngleMin ) + ,ZAngleMax( inSource->zAngleMax ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxSpringStiffness( const PxSpring* inOwner ) { return inOwner->stiffness; } +inline void setPxSpringStiffness( PxSpring* inOwner, PxReal inData) { inOwner->stiffness = inData; } +inline PxReal getPxSpringDamping( const PxSpring* inOwner ) { return inOwner->damping; } +inline void setPxSpringDamping( PxSpring* inOwner, PxReal inData) { inOwner->damping = inData; } + PxSpringGeneratedInfo::PxSpringGeneratedInfo() + : Stiffness( "Stiffness", setPxSpringStiffness, getPxSpringStiffness ) + , Damping( "Damping", setPxSpringDamping, getPxSpringDamping ) +{} + PxSpringGeneratedValues::PxSpringGeneratedValues( const PxSpring* inSource ) + :Stiffness( inSource->stiffness ) + ,Damping( inSource->damping ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxD6JointDriveForceLimit( const PxD6JointDrive* inOwner ) { return inOwner->forceLimit; } +inline void setPxD6JointDriveForceLimit( PxD6JointDrive* inOwner, PxReal inData) { inOwner->forceLimit = inData; } +inline PxD6JointDriveFlags getPxD6JointDriveFlags( const PxD6JointDrive* inOwner ) { return inOwner->flags; } +inline void setPxD6JointDriveFlags( PxD6JointDrive* inOwner, PxD6JointDriveFlags inData) { inOwner->flags = inData; } + PxD6JointDriveGeneratedInfo::PxD6JointDriveGeneratedInfo() + : ForceLimit( "ForceLimit", setPxD6JointDriveForceLimit, getPxD6JointDriveForceLimit ) + , Flags( "Flags", setPxD6JointDriveFlags, getPxD6JointDriveFlags ) +{} + PxD6JointDriveGeneratedValues::PxD6JointDriveGeneratedValues( const PxD6JointDrive* inSource ) + :PxSpringGeneratedValues( inSource ) + ,ForceLimit( inSource->forceLimit ) + ,Flags( inSource->flags ) +{ + PX_UNUSED(inSource); +} diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp new file mode 100644 index 000000000..12f67eab3 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp @@ -0,0 +1,222 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleComponents.h" +#include "PxVehicleDefaults.h" +#include "CmPhysXCommon.h" +#include "CmBitMap.h" +#include "PsFoundation.h" + +namespace physx +{ + +bool PxVehicleChassisData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mMOI.x>0.0f && mMOI.y>0.0f && mMOI.z>0.0f, "Illegal PxVehicleChassisData.mMOI - each element of the chassis moi needs to non-zero", false); + PX_CHECK_AND_RETURN_VAL(mMass>0.0f, "Ilegal PxVehicleChassisData.mMass - chassis mass needs to be non-zero", false); + return true; +} + + +bool PxVehicleEngineData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mPeakTorque>0.0f, "PxVehicleEngineData.mPeakTorque must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mMaxOmega>0.0f, "PxVehicleEngineData.mMaxOmega must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mDampingRateFullThrottle>0.0f, "PxVehicleEngineData.mDampingRateFullThrottle must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mDampingRateZeroThrottleClutchEngaged>0.0f, "PxVehicleEngineData.mDampingRateZeroThrottleClutchEngaged must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mDampingRateZeroThrottleClutchDisengaged>0.0f, "PxVehicleEngineData.mDampingRateZeroThrottleClutchDisengaged must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mRecipMOI>0.0f, "PxVehicleEngineData.mRecipMOI must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mRecipMaxOmega>0.0f, "PxVehicleEngineData.mRecipMaxOmega must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/mMaxOmega)-mRecipMaxOmega) <= 0.001f, "PxVehicleEngineData.mMaxOmega and PxVehicleEngineData.mRecipMaxOmega don't match", false); + return true; +} + +bool PxVehicleGearsData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mFinalRatio>0, "PxVehicleGearsData.mFinalRatio must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mNbRatios>=1, "PxVehicleGearsData.mNbRatios must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mSwitchTime>=0.0f, "PxVehicleGearsData.mSwitchTime must be greater than or equal to zero", false); + + PX_CHECK_AND_RETURN_VAL(mRatios[PxVehicleGearsData::eREVERSE]<0.0f, "PxVehicleGearsData.mRatios[PxVehicleGearsData::eREVERSE] must be less than zero", false); + PX_CHECK_AND_RETURN_VAL(mRatios[PxVehicleGearsData::eNEUTRAL]==0.0f, "PxVehicleGearsData.mRatios[PxVehicleGearsData::eNEUTRAL] must be zero", false); + for(PxU32 i=PxVehicleGearsData::eFIRST;i0.0f, "Forward gear ratios must be greater than zero", false); + } + for(PxU32 i=PxVehicleGearsData::eSECOND;i=0.0f, "PxVehicleAutoBoxData.mUpRatios must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mDownRatios[i]>=0.0f, "PxVehicleAutoBoxData.mDownRatios must be greater than or equal to zero", false); + } + return true; +} + +bool PxVehicleDifferential4WData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mFrontRearSplit<=1.0f && mFrontRearSplit>=0.0f, "PxVehicleDifferential4WData.mFrontRearSplit must be in range (0,1)", false); + PX_CHECK_AND_RETURN_VAL(mFrontLeftRightSplit<=1.0f && mFrontLeftRightSplit>=0.0f, "PxVehicleDifferential4WData.mFrontLeftRightSplit must be in range (0,1)", false); + PX_CHECK_AND_RETURN_VAL(mRearLeftRightSplit<=1.0f || mRearLeftRightSplit>=0.0f, "PxVehicleDifferential4WData.mRearLeftRightSplit must be in range (0,1)", false); + PX_CHECK_AND_RETURN_VAL(mCentreBias>=1.0f, "PxVehicleDifferential4WData.mCentreBias must be greater than or equal to 1.0f", false); + PX_CHECK_AND_RETURN_VAL(mFrontBias>=1.0f, "PxVehicleDifferential4WData.mFrontBias must be greater than or equal to 1.0f", false); + PX_CHECK_AND_RETURN_VAL(mRearBias>=1.0f, "PxVehicleDifferential4WData.mRearBias must be greater than or equal to 1.0f", false); + PX_CHECK_AND_RETURN_VAL(mType> 5); + PxU32 numDrivenWheels=mNbDrivenWheels; + if(drivenState) + { + if(!bitmap.test(wheelId)) + { + numDrivenWheels++; + bitmap.set(wheelId); + mInvNbDrivenWheels=1.0f/(1.0f*numDrivenWheels); + } + } + else if(bitmap.test(wheelId)) + { + numDrivenWheels--; + bitmap.reset(wheelId); + mInvNbDrivenWheels = numDrivenWheels>0.0f ? 1.0f/(1.0f*numDrivenWheels) : 0.0f; + } + mNbDrivenWheels=numDrivenWheels; +} + +bool PxVehicleDifferentialNWData::getIsDrivenWheel(const PxU32 wheelId) const +{ + Cm::BitMap bitmap; + bitmap.setWords(const_cast(mBitmapBuffer),((PX_MAX_NB_WHEELS + 31) & ~31) >> 5); + return (bitmap.test(wheelId) ? true : false); +} + +PxU32 PxVehicleDifferentialNWData::getDrivenWheelStatus() const +{ + PX_ASSERT(((PX_MAX_NB_WHEELS + 31) & ~31) >> 5 == 1); + return mBitmapBuffer[0]; +} + +void PxVehicleDifferentialNWData::setDrivenWheelStatus(PxU32 status) +{ + PX_ASSERT(((PX_MAX_NB_WHEELS + 31) & ~31) >> 5 == 1); + + Cm::BitMap bitmap; + bitmap.setWords(&status, 1); + + for(PxU32 i = 0; i < PX_MAX_NB_WHEELS; ++i) + { + setDrivenWheel(i, !!bitmap.test(i)); + } +} + +bool PxVehicleDifferentialNWData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mNbDrivenWheels<=PX_MAX_NB_WHEELS, "PxVehicleDifferentialNWData.mNbDrivenWheels must be in range (0,20)", false); + return true; +} + +bool PxVehicleAckermannGeometryData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mAccuracy>=0.0f && mAccuracy<=1.0f, "PxVehicleAckermannGeometryData.mAccuracy must be in range (0,1)", false); + PX_CHECK_AND_RETURN_VAL(mFrontWidth>0.0f, "PxVehicleAckermannGeometryData.mFrontWidth must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mRearWidth>0.0f, "PxVehicleAckermannGeometryData.mRearWidth must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mAxleSeparation>0.0f, "PxVehicleAckermannGeometryData.mAxleSeparation must be greater than zero", false); + return true; +} + +bool PxVehicleClutchData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mStrength>0, "PxVehicleClutchData.mStrength must be greater than zero", false); + return true; +} + +bool PxVehicleTireLoadFilterData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mMaxNormalisedLoad>=mMinNormalisedLoad, "PxVehicleTireLoadFilterData.mMaxNormalisedLoad must be greater than or equal to PxVehicleTireLoadFilterData.mMinNormalisedLoad", false); + PX_CHECK_AND_RETURN_VAL(mMaxFilteredNormalisedLoad>0, "PxVehicleTireLoadFilterData.mMaxFilteredNormalisedLoad must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/(mMaxNormalisedLoad - mMinNormalisedLoad)) - mDenominator) < 0.001f, "PxVehicleTireLoadFilterData.mMaxFilteredNormalisedLoad, PxVehicleTireLoadFilterData.mMinNormalisedLoad, and PxVehicleTireLoadFilterData.mDenominator don't match", false); + return true; +} + +bool PxVehicleWheelData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mRadius>0.0f, "PxVehicleWheelData.mRadius must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mWidth>0.0f, "PxVehicleWheelData.mWidth must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mMass>0.0f, "PxVehicleWheelData.mMass must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mMOI>0.0f, "PxVehicleWheelData.mMOI must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mDampingRate>0.0f, "PxVehicleWheelData.mDampingRate must be greater than zero", false); + PX_CHECK_AND_RETURN_VAL(mMaxBrakeTorque>=0.0f, "PxVehicleWheelData.mMaxBrakeTorque must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mMaxHandBrakeTorque>=0.0f, "PxVehicleWheelData.mMaxHandBrakeTorque must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mToeAngle<=PxPi, "PxVehicleWheelData.mToeAngle must be less than Pi", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/mRadius) - mRecipRadius) < 0.001f, "PxVehicleWheelData.mRadius and PxVehicleWheelData.mRecipRadius don't match", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/mMOI) - mRecipMOI) < 0.001f, "PxVehicleWheelData.mMOI and PxVehicleWheelData.mRecipMOI don't match", false); + return true; +} + +bool PxVehicleSuspensionData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mSpringStrength>=0.0f, "PxVehicleSuspensionData.mSpringStrength must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mSpringDamperRate>=0.0f, "PxVehicleSuspensionData.mSpringDamperRate must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mMaxCompression>=0.0f, "PxVehicleSuspensionData.mMaxCompression must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mMaxDroop>=0.0f, "PxVehicleSuspensionData.mMaxDroop must be greater than or equal to zero", false); + PX_CHECK_AND_RETURN_VAL(mSprungMass>=0.0f, "PxVehicleSuspensionData.mSprungMass must be greater than or equal to zero", false); + return true; +} + +bool PxVehicleAntiRollBarData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(((mWheel0< PX_MAX_NB_WHEELS) && (mWheel1 < PX_MAX_NB_WHEELS)), "PxVehicleAntiRoll.mWheel0 and PxVehicleAntiRoll.mWheel1 are an illegal pair", false); + PX_CHECK_AND_RETURN_VAL((mWheel0 != mWheel1), "PxVehicleAntiRoll.mWheel0 == PxVehicleAntiRoll.mWheel1", false); + PX_CHECK_AND_RETURN_VAL(mStiffness>=0, "PxVehicleAntiRoll::mStiffness must be greater than or equal to zero", false); + return true; +} + +bool PxVehicleTireData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mFrictionVsSlipGraph[0][0]>=0.0f && mFrictionVsSlipGraph[0][1]>=0.0f, "Illegal values for mFrictionVsSlipGraph[0]", false); + PX_CHECK_AND_RETURN_VAL(mFrictionVsSlipGraph[1][0]>=0.0f && mFrictionVsSlipGraph[1][1]>=0.0f, "Illegal values for mFrictionVsSlipGraph[1]", false); + PX_CHECK_AND_RETURN_VAL(mFrictionVsSlipGraph[2][0]>=0.0f && mFrictionVsSlipGraph[2][1]>=0.0f, "Illegal values for mFrictionVsSlipGraph[2]", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/(mFrictionVsSlipGraph[1][0]-mFrictionVsSlipGraph[0][0])) - mFrictionVsSlipGraphRecipx1Minusx0) < 0.001f, "PxVehicleTireData.mFrictionVsSlipGraphRecipx1Minusx0 not set up", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/(mFrictionVsSlipGraph[2][0]-mFrictionVsSlipGraph[1][0])) - mFrictionVsSlipGraphRecipx2Minusx1) < 0.001f, "PxVehicleTireData.mFrictionVsSlipGraphRecipx2Minusx1 not set up", false); + return true; +} +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h new file mode 100644 index 000000000..d823f380a --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_DEFAULTS_H +#define PX_VEHICLE_DEFAULTS_H +/** \addtogroup vehicle + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_DEFAULTS_H diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp new file mode 100644 index 000000000..e92047d90 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp @@ -0,0 +1,213 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleDrive.h" +#include "PxVehicleSDK.h" +#include "PxVehicleDefaults.h" +#include "PxRigidDynamic.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +bool PxVehicleDriveSimData::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mEngine.isValid(), "Invalid PxVehicleCoreSimulationData.mEngine", false); + PX_CHECK_AND_RETURN_VAL(mGears.isValid(), "Invalid PxVehicleCoreSimulationData.mGears", false); + PX_CHECK_AND_RETURN_VAL(mClutch.isValid(), "Invalid PxVehicleCoreSimulationData.mClutch", false); + PX_CHECK_AND_RETURN_VAL(mAutoBox.isValid(), "Invalid PxVehicleCoreSimulationData.mAutoBox", false); + return true; +} + +void PxVehicleDriveSimData::setEngineData(const PxVehicleEngineData& engine) +{ + PX_CHECK_AND_RETURN(engine.mTorqueCurve.getNbDataPairs()>0, "Engine torque curve must specify at least one entry"); + PX_CHECK_AND_RETURN(engine.mPeakTorque>0, "Engine peak torque must be greater than zero"); + PX_CHECK_AND_RETURN(engine.mMaxOmega>0, "Engine max omega must be greater than zero"); + PX_CHECK_AND_RETURN(engine.mDampingRateFullThrottle>=0, "Full throttle damping rate must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(engine.mDampingRateZeroThrottleClutchEngaged>=0, "Zero throttle clutch engaged damping rate must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(engine.mDampingRateZeroThrottleClutchDisengaged>=0, "Zero throttle clutch disengaged damping rate must be greater than or equal to zero"); + + mEngine=engine; + mEngine.mRecipMOI=1.0f/engine.mMOI; + mEngine.mRecipMaxOmega=1.0f/engine.mMaxOmega; +} + +void PxVehicleDriveSimData::setGearsData(const PxVehicleGearsData& gears) +{ + PX_CHECK_AND_RETURN(gears.mRatios[PxVehicleGearsData::eREVERSE]<0, "Reverse gear ratio must be negative"); + PX_CHECK_AND_RETURN(gears.mRatios[PxVehicleGearsData::eNEUTRAL]==0, "Neutral gear ratio must be zero"); + PX_CHECK_AND_RETURN(gears.mRatios[PxVehicleGearsData::eFIRST]>0, "First gear ratio must be positive"); + PX_CHECK_AND_RETURN(PxVehicleGearsData::eSECOND>=gears.mNbRatios || (gears.mRatios[PxVehicleGearsData::eSECOND]>0 && gears.mRatios[PxVehicleGearsData::eSECOND] < gears.mRatios[PxVehicleGearsData::eFIRST]), "Second gear ratio must be positive and less than first gear ratio"); + PX_CHECK_AND_RETURN(PxVehicleGearsData::eTHIRD>=gears.mNbRatios || (gears.mRatios[PxVehicleGearsData::eTHIRD]>0 && gears.mRatios[PxVehicleGearsData::eTHIRD] < gears.mRatios[PxVehicleGearsData::eSECOND]), "Third gear ratio must be positive and less than second gear ratio"); + PX_CHECK_AND_RETURN(PxVehicleGearsData::eFOURTH>=gears.mNbRatios || (gears.mRatios[PxVehicleGearsData::eFOURTH]>0 && gears.mRatios[PxVehicleGearsData::eFOURTH] < gears.mRatios[PxVehicleGearsData::eTHIRD]), "Fourth gear ratio must be positive and less than third gear ratio"); + PX_CHECK_AND_RETURN(PxVehicleGearsData::eFIFTH>=gears.mNbRatios || (gears.mRatios[PxVehicleGearsData::eFIFTH]>0 && gears.mRatios[PxVehicleGearsData::eFIFTH] < gears.mRatios[PxVehicleGearsData::eFOURTH]), "Fifth gear ratio must be positive and less than fourth gear ratio"); + PX_CHECK_AND_RETURN(PxVehicleGearsData::eSIXTH>=gears.mNbRatios || (gears.mRatios[PxVehicleGearsData::eSIXTH]>0 && gears.mRatios[PxVehicleGearsData::eSIXTH] < gears.mRatios[PxVehicleGearsData::eFIFTH]), "Sixth gear ratio must be positive and less than fifth gear ratio"); + PX_CHECK_AND_RETURN(gears.mFinalRatio>0, "Final gear ratio must be greater than zero"); + PX_CHECK_AND_RETURN(gears.mNbRatios>=3, "Number of gear ratios must be at least 3 - we need at least reverse, neutral, and a forward gear"); + + mGears=gears; +} + +void PxVehicleDriveSimData::setClutchData(const PxVehicleClutchData& clutch) +{ + PX_CHECK_AND_RETURN(clutch.mStrength>0, "Clutch strength must be greater than zero"); + PX_CHECK_AND_RETURN(PxVehicleClutchAccuracyMode::eBEST_POSSIBLE==clutch.mAccuracyMode || clutch.mEstimateIterations > 0, "Clutch mEstimateIterations must be greater than zero in eESTIMATE mode."); + + mClutch=clutch; +} + +void PxVehicleDriveSimData::setAutoBoxData(const PxVehicleAutoBoxData& autobox) +{ + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eREVERSE]>=0, "Autobox gearup ratio in reverse must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eNEUTRAL]>=0, "Autobox gearup ratio in neutral must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eFIRST]>=0, "Autobox gearup ratio in first must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eSECOND]>=0, "Autobox gearup ratio in second must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eTHIRD]>=0, "Autobox gearup ratio in third must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eFOURTH]>=0, "Autobox gearup ratio in fourth must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mUpRatios[PxVehicleGearsData::eFIFTH]>=0, "Autobox gearup ratio in fifth must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eREVERSE]>=0, "Autobox geardown ratio in reverse must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eNEUTRAL]>=0, "Autobox geardown ratio in neutral must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eFIRST]>=0, "Autobox geardown ratio in first must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eSECOND]>=0, "Autobox geardown ratio in second must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eTHIRD]>=0, "Autobox geardown ratio in third must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eFOURTH]>=0, "Autobox geardown ratio in fourth must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eFIFTH]>=0, "Autobox geardown ratio in fifth must be greater than zero"); + PX_CHECK_AND_RETURN(autobox.mDownRatios[PxVehicleGearsData::eSIXTH]>=0, "Autobox geardown ratio in fifth must be greater than zero"); + + mAutoBox=autobox; +} + +/////////////////////////////////// + +PxVehicleDriveDynData::PxVehicleDriveDynData() + : mUseAutoGears(false), + mGearUpPressed(false), + mGearDownPressed(false), + mCurrentGear(PxVehicleGearsData::eNEUTRAL), + mTargetGear(PxVehicleGearsData::eNEUTRAL), + mEnginespeed(0.0f), + mGearSwitchTime(0.0f), + mAutoBoxSwitchTime(0.0f) +{ + for(PxU32 i=0;i=-1.01f && analogVal<=1.01f, "PxVehicleDriveDynData::setAnalogInput - analogVal must be in range (-1,1)"); + PX_CHECK_AND_RETURN(type=0 && diff.mFrontRearSplit<=1.0f), "Diff torque split between front and rear must be in range (0,1)"); + PX_CHECK_AND_RETURN(diff.mType!=PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD || (diff.mCentreBias>=1), "Diff centre bias must be greater than or equal to 1"); + PX_CHECK_AND_RETURN((diff.mType!=PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD && diff.mType!=PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD) || (diff.mFrontBias>=1), "Diff front bias must be greater than or equal to 1"); + PX_CHECK_AND_RETURN((diff.mType!=PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD && diff.mType!=PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD) || (diff.mRearBias>=1), "Diff rear bias must be greater than or equal to 1"); + PX_CHECK_AND_RETURN(diff.mType 0, "Illegal ackermannData.mFrontWidth - must be greater than zero"); + PX_CHECK_AND_RETURN(ackermannData.mRearWidth > 0, "Illegal ackermannData.mRearWidth - must be greater than zero"); + PX_CHECK_AND_RETURN(ackermannData.mAxleSeparation > 0, "Illegal ackermannData.mAxleSeparation - must be greater than zero"); + + mAckermannGeometry = ackermannData; +} + +/////////////////////////////////// + +bool PxVehicleDrive4W::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(PxVehicleDrive::isValid(), "invalid PxVehicleDrive", false); + PX_CHECK_AND_RETURN_VAL(mDriveSimData.isValid(), "Invalid PxVehicleNW.mCoreSimData", false); + return true; +} + +PxVehicleDrive4W* PxVehicleDrive4W::allocate(const PxU32 numWheels) +{ + PX_CHECK_AND_RETURN_NULL(numWheels>=4, "PxVehicleDrive4W::allocate - needs to have at least 4 wheels"); + PX_CHECK_AND_RETURN_NULL(gToleranceScaleLength > 0, "PxVehicleDrive4W::allocate - need to call PxInitVehicleSDK"); + + //Compute the bytes needed. + const PxU32 byteSize = sizeof(PxVehicleDrive4W) + PxVehicleDrive::computeByteSize(numWheels); + + //Allocate the memory. + PxVehicleDrive4W* veh = static_cast(PX_ALLOC(byteSize, "PxVehicleDrive4W")); + Cm::markSerializedMem(veh, byteSize); + new(veh) PxVehicleDrive4W(); + + //Patch up the pointers. + PxU8* ptr = reinterpret_cast(veh) + sizeof(PxVehicleDrive4W); + ptr=PxVehicleDrive::patchupPointers(numWheels, veh, ptr); + + //Initialise wheels. + veh->init(numWheels); + + //Set the vehicle type. + veh->mType = PxVehicleTypes::eDRIVE4W; + + return veh; +} + +void PxVehicleDrive4W::free() +{ + PxVehicleDrive::free(); +} + +void PxVehicleDrive4W::setup +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, + const PxU32 numNonDrivenWheels) +{ + PX_CHECK_AND_RETURN(driveData.isValid(), "PxVehicleDrive4W::setup - invalid driveData"); + PX_CHECK_AND_RETURN(wheelsData.getNbWheels() >= 4, "PxVehicleDrive4W::setup - needs to have at least 4 wheels"); + + //Set up the wheels. + PxVehicleDrive::setup(physics,vehActor,wheelsData,4,numNonDrivenWheels); + + //Start setting up the drive. + PX_CHECK_MSG(driveData.isValid(), "PxVehicle4WDrive - invalid driveData"); + + //Copy the simulation data. + mDriveSimData = driveData; +} + +PxVehicleDrive4W* PxVehicleDrive4W::create +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, + const PxU32 numNonDrivenWheels) +{ + PxVehicleDrive4W* veh4W=PxVehicleDrive4W::allocate(4+numNonDrivenWheels); + veh4W->setup(physics,vehActor,wheelsData,driveData,numNonDrivenWheels); + return veh4W; +} + + +void PxVehicleDrive4W::setToRestState() +{ + //Set core to rest state. + PxVehicleDrive::setToRestState(); +} +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp new file mode 100644 index 000000000..8ca91c6f9 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleDriveNW.h" +#include "PxVehicleDrive.h" +#include "PxVehicleSDK.h" +#include "PxVehicleSuspWheelTire4.h" +#include "PxVehicleSuspLimitConstraintShader.h" +#include "PxVehicleDefaults.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" +#include "PxScene.h" +#include "CmUtils.h" + + +namespace physx +{ + +extern PxF32 gToleranceScaleLength; + +void PxVehicleDriveSimDataNW::setDiffData(const PxVehicleDifferentialNWData& diff) +{ + PX_CHECK_AND_RETURN(diff.isValid(), "Invalid PxVehicleCoreSimulationData.mDiff"); + mDiff=diff; +} + +bool PxVehicleDriveSimDataNW::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mDiff.isValid(), "Invalid PxVehicleDifferentialNWData", false); + PX_CHECK_AND_RETURN_VAL(PxVehicleDriveSimData::isValid(), "Invalid PxVehicleDriveSimDataNW", false); + return true; +} + +/////////////////////////////////// + +bool PxVehicleDriveNW::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(PxVehicleDrive::isValid(), "invalid PxVehicleDrive", false); + PX_CHECK_AND_RETURN_VAL(mDriveSimData.isValid(), "Invalid PxVehicleNW.mCoreSimData", false); + return true; +} + +PxVehicleDriveNW* PxVehicleDriveNW::allocate(const PxU32 numWheels) +{ + PX_CHECK_AND_RETURN_NULL(numWheels>0, "Cars with zero wheels are illegal"); + PX_CHECK_AND_RETURN_NULL(gToleranceScaleLength > 0, "PxVehicleDriveNW::allocate - need to call PxInitVehicleSDK"); + + //Compute the bytes needed. + const PxU32 byteSize = sizeof(PxVehicleDriveNW) + PxVehicleDrive::computeByteSize(numWheels); + + //Allocate the memory. + PxVehicleDriveNW* veh = static_cast(PX_ALLOC(byteSize, "PxVehicleDriveNW")); + Cm::markSerializedMem(veh, byteSize); + new(veh) PxVehicleDriveNW(); + + //Patch up the pointers. + PxU8* ptr = reinterpret_cast(veh) + sizeof(PxVehicleDriveNW); + ptr=PxVehicleDrive::patchupPointers(numWheels, veh, ptr); + + //Initialise + veh->init(numWheels); + + //Set the vehicle type. + veh->mType = PxVehicleTypes::eDRIVENW; + + return veh; +} + +void PxVehicleDriveNW::free() +{ + PxVehicleDrive::free(); +} + +void PxVehicleDriveNW::setup +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimDataNW& driveData, + const PxU32 numWheels) +{ + PX_CHECK_AND_RETURN(driveData.isValid(), "PxVehicleDriveNW::setup - invalid driveData"); + + //Set up the wheels. + PxVehicleDrive::setup(physics,vehActor,wheelsData,numWheels,0); + + //Start setting up the drive. + PX_CHECK_MSG(driveData.isValid(), "PxVehicleNWDrive - invalid driveData"); + + //Copy the simulation data. + mDriveSimData = driveData; +} + +PxVehicleDriveNW* PxVehicleDriveNW::create +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimDataNW& driveData, + const PxU32 numWheels) +{ + PxVehicleDriveNW* vehNW=PxVehicleDriveNW::allocate(numWheels); + vehNW->setup(physics,vehActor,wheelsData,driveData,numWheels); + return vehNW; +} + + +void PxVehicleDriveNW::setToRestState() +{ + //Set core to rest state. + PxVehicleDrive::setToRestState(); +} + + + + + + + + + + + + +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp new file mode 100644 index 000000000..30cb281d8 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleDriveTank.h" +#include "PxVehicleWheels.h" +#include "PxVehicleSDK.h" +#include "PxVehicleDefaults.h" +#include "PxRigidDynamic.h" +#include "CmPhysXCommon.h" +#include "CmUtils.h" +#include "PsFoundation.h" + +namespace physx +{ + +extern PxF32 gToleranceScaleLength; + +bool PxVehicleDriveTank::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(PxVehicleDrive::isValid(), "invalid PxVehicleDrive", false); + PX_CHECK_AND_RETURN_VAL(mDriveSimData.isValid(), "Invalid PxVehicleDriveTank.mCoreSimData", false); + return true; +} + +PxVehicleDriveTank* PxVehicleDriveTank::allocate(const PxU32 numWheels) +{ + PX_CHECK_AND_RETURN_NULL(numWheels>0, "Cars with zero wheels are illegal"); + PX_CHECK_AND_RETURN_NULL(0 == (numWheels % 2), "PxVehicleDriveTank::allocate - needs to have even number of wheels"); + PX_CHECK_AND_RETURN_NULL(gToleranceScaleLength > 0, "PxVehicleDriveTank::allocate - need to call PxInitVehicleSDK"); + + //Compute the bytes needed. + const PxU32 byteSize = sizeof(PxVehicleDriveTank) + PxVehicleDrive::computeByteSize(numWheels); + + //Allocate the memory. + PxVehicleDriveTank* veh = static_cast(PX_ALLOC(byteSize, "PxVehicleDriveTank")); + Cm::markSerializedMem(veh, byteSize); + new(veh) PxVehicleDriveTank(); + + //Patch up the pointers. + PxU8* ptr = reinterpret_cast(veh) + sizeof(PxVehicleDriveTank); + PxVehicleDrive::patchupPointers(numWheels, veh, ptr); + + //Initialise. + veh->init(numWheels); + + //Set the vehicle type. + veh->mType = PxVehicleTypes::eDRIVETANK; + + //Set the default drive model. + veh->mDriveModel = PxVehicleDriveTankControlModel::eSTANDARD; + + return veh; +} + +void PxVehicleDriveTank::free() +{ + PxVehicleDrive::free(); +} + +void PxVehicleDriveTank::setup +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData& driveData, + const PxU32 numDrivenWheels) +{ + PX_CHECK_AND_RETURN(driveData.isValid(), "PxVehicleDriveTank::setup - illegal drive data"); + + //Set up the wheels. + PxVehicleDrive::setup(physics,vehActor,wheelsData,numDrivenWheels,0); + + //Start setting up the drive. + PX_CHECK_MSG(driveData.isValid(), "PxVehicle4WDrive - invalid driveData"); + + //Copy the simulation data. + mDriveSimData = driveData; +} + +PxVehicleDriveTank* PxVehicleDriveTank::create +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData& driveData, + const PxU32 numDrivenWheels) +{ + PxVehicleDriveTank* tank=PxVehicleDriveTank::allocate(numDrivenWheels); + tank->setup(physics,vehActor,wheelsData,driveData,numDrivenWheels); + return tank; +} + + +void PxVehicleDriveTank::setToRestState() +{ + //Set core to rest state. + PxVehicleDrive::setToRestState(); +} +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h new file mode 100644 index 000000000..84c08f726 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h @@ -0,0 +1,433 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_LINEAR_MATH_H +#define PX_VEHICLE_LINEAR_MATH_H +/** \addtogroup vehicle + @{ +*/ + +#include "PxVehicleSDK.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +#define MAX_VECTORN_SIZE (PX_MAX_NB_WHEELS+3) + +class VectorN +{ +public: + + VectorN(const PxU32 size) + : mSize(size) + { + PX_ASSERT(mSize <= MAX_VECTORN_SIZE); + } + ~VectorN() + { + } + + VectorN(const VectorN& src) + { + for(PxU32 i = 0; i < src.mSize; i++) + { + mValues[i] = src.mValues[i]; + } + mSize = src.mSize; + } + + PX_FORCE_INLINE VectorN& operator=(const VectorN& src) + { + for(PxU32 i = 0; i < src.mSize; i++) + { + mValues[i] = src.mValues[i]; + } + mSize = src.mSize; + return *this; + } + + PX_FORCE_INLINE PxF32& operator[] (const PxU32 i) + { + PX_ASSERT(i < mSize); + return (mValues[i]); + } + + PX_FORCE_INLINE const PxF32& operator[] (const PxU32 i) const + { + PX_ASSERT(i < mSize); + return (mValues[i]); + } + + PX_FORCE_INLINE PxU32 getSize() const {return mSize;} + +private: + + PxF32 mValues[MAX_VECTORN_SIZE]; + PxU32 mSize; +}; + +class MatrixNN +{ +public: + + MatrixNN() + : mSize(0) + { + } + MatrixNN(const PxU32 size) + : mSize(size) + { + PX_ASSERT(mSize <= MAX_VECTORN_SIZE); + } + MatrixNN(const MatrixNN& src) + { + for(PxU32 i = 0; i < src.mSize; i++) + { + for(PxU32 j = 0; j < src.mSize; j++) + { + mValues[i][j] = src.mValues[i][j]; + } + } + mSize=src.mSize; + } + ~MatrixNN() + { + } + + PX_FORCE_INLINE MatrixNN& operator=(const MatrixNN& src) + { + for(PxU32 i = 0;i < src.mSize; i++) + { + for(PxU32 j = 0;j < src.mSize; j++) + { + mValues[i][j] = src.mValues[i][j]; + } + } + mSize = src.mSize; + return *this; + } + + PX_FORCE_INLINE PxF32 get(const PxU32 i, const PxU32 j) const + { + PX_ASSERT(i < mSize); + PX_ASSERT(j < mSize); + return mValues[i][j]; + } + PX_FORCE_INLINE void set(const PxU32 i, const PxU32 j, const PxF32 val) + { + PX_ASSERT(i < mSize); + PX_ASSERT(j < mSize); + mValues[i][j] = val; + } + + PX_FORCE_INLINE PxU32 getSize() const {return mSize;} + + PX_FORCE_INLINE void setSize(const PxU32 size) + { + PX_ASSERT(size <= MAX_VECTORN_SIZE); + mSize = size; + } + +public: + + PxF32 mValues[MAX_VECTORN_SIZE][MAX_VECTORN_SIZE]; + PxU32 mSize; +}; + + +/* + LUPQ decomposition + + Based upon "Outer Product LU with Complete Pivoting," from Matrix Computations (4th Edition), Golub and Van Loan + + Solve A*x = b using: + + MatrixNNLUSolver solver; + solver.decomposeLU(A); + solver.solve(b, x); +*/ +class MatrixNNLUSolver +{ +private: + + MatrixNN mLU; + PxU32 mP[MAX_VECTORN_SIZE-1]; // Row permutation + PxU32 mQ[MAX_VECTORN_SIZE-1]; // Column permutation + PxF32 mdetM; + +public: + + MatrixNNLUSolver(){} + ~MatrixNNLUSolver(){} + + PxF32 getDet() const {return mdetM;} + + void decomposeLU(const MatrixNN& A) + { + const PxU32 D = A.mSize; + + mLU = A; + + mdetM = 1.0f; + + for (PxU32 k = 0; k < D-1; ++k) + { + PxU32 pivot_row = k; + PxU32 pivot_col = k; + float abs_pivot_elem = 0.0f; + for (PxU32 c = k; c < D; ++c) + { + for (PxU32 r = k; r < D; ++r) + { + const PxF32 abs_elem = PxAbs(mLU.get(r,c)); + if (abs_elem > abs_pivot_elem) + { + abs_pivot_elem = abs_elem; + pivot_row = r; + pivot_col = c; + } + } + } + + mP[k] = pivot_row; + if (pivot_row != k) + { + mdetM = -mdetM; + for (PxU32 c = 0; c < D; ++c) + { + //swap(m_LU(k,c), m_LU(pivot_row,c)); + const PxF32 pivotrowc = mLU.get(pivot_row, c); + mLU.set(pivot_row, c, mLU.get(k, c)); + mLU.set(k, c, pivotrowc); + } + } + + mQ[k] = pivot_col; + if (pivot_col != k) + { + mdetM = -mdetM; + for (PxU32 r = 0; r < D; ++r) + { + //swap(m_LU(r,k), m_LU(r,pivot_col)); + const PxF32 rpivotcol = mLU.get(r, pivot_col); + mLU.set(r,pivot_col, mLU.get(r,k)); + mLU.set(r, k, rpivotcol); + } + } + + mdetM *= mLU.get(k,k); + + if (mLU.get(k,k) != 0.0f) + { + for (PxU32 r = k+1; r < D; ++r) + { + mLU.set(r, k, mLU.get(r,k) / mLU.get(k,k)); + for (PxU32 c = k+1; c < D; ++c) + { + //m_LU(r,c) -= m_LU(r,k)*m_LU(k,c); + const PxF32 rc = mLU.get(r, c); + const PxF32 rk = mLU.get(r, k); + const PxF32 kc = mLU.get(k, c); + mLU.set(r, c, rc - rk*kc); + } + } + } + } + + mdetM *= mLU.get(D-1,D-1); + } + + //Given a matrix A and a vector b find x that satisfies Ax = b, where the matrix A is the matrix that was passed to decomposeLU. + //Returns true if the lu decomposition indicates that the matrix has an inverse and x was successfully computed. + //Returns false if the lu decomposition resulted in zero determinant ie the matrix has no inverse and no solution exists for x. + //Returns false if the size of either b or x doesn't match the size of the matrix passed to decomposeLU. + //If false is returned then each relevant element of x is set to zero. + bool solve(const VectorN& b, VectorN& x) const + { + const PxU32 D = x.getSize(); + + if((b.getSize() != x.getSize()) || (b.getSize() != mLU.getSize()) || (0.0f == mdetM)) + { + for(PxU32 i = 0; i < D; i++) + { + x[i] = 0.0f; + } + return false; + } + + x = b; + + // Perform row permutation to get Pb + for(PxU32 i = 0; i < D-1; ++i) + { + //swap(x(i), x(m_P[i])); + const PxF32 xp = x[mP[i]]; + x[mP[i]] = x[i]; + x[i] = xp; + } + + // Forward substitute to get (L^-1)Pb + for (PxU32 r = 1; r < D; ++r) + { + for (PxU32 i = 0; i < r; ++i) + { + x[r] -= mLU.get(r,i)*x[i]; + } + } + + // Back substitute to get (U^-1)(L^-1)Pb + for (PxU32 r = D; r-- > 0;) + { + for (PxU32 i = r+1; i < D; ++i) + { + x[r] -= mLU.get(r,i)*x[i]; + } + x[r] /= mLU.get(r,r); + } + + // Perform column permutation to get the solution (Q^T)(U^-1)(L^-1)Pb + for (PxU32 i = D-1; i-- > 0;) + { + //swap(x(i), x(m_Q[i])); + const PxF32 xq = x[mQ[i]]; + x[mQ[i]] = x[i]; + x[i] = xq; + } + + return true; + } + +}; + + +class MatrixNGaussSeidelSolver +{ +public: + + void solve(const PxU32 maxIterations, const PxF32 tolerance, const MatrixNN& A, const VectorN& b, VectorN& result) const + { + const PxU32 N = A.getSize(); + + VectorN DInv(N); + PxF32 bLength2 = 0.0f; + for(PxU32 i = 0; i < N; i++) + { + DInv[i] = 1.0f/A.get(i,i); + bLength2 += (b[i] * b[i]); + } + + PxU32 iteration = 0; + PxF32 error = PX_MAX_F32; + while(iteration < maxIterations && tolerance < error) + { + for(PxU32 i = 0; i < N; i++) + { + PxF32 l = 0.0f; + for(PxU32 j = 0; j < i; j++) + { + l += A.get(i,j) * result[j]; + } + + PxF32 u = 0.0f; + for(PxU32 j = i + 1; j < N; j++) + { + u += A.get(i,j) * result[j]; + } + + result[i] = DInv[i] * (b[i] - l - u); + } + + //Compute the error. + PxF32 rLength2 = 0; + for(PxU32 i = 0; i < N; i++) + { + PxF32 e = -b[i]; + for(PxU32 j = 0; j < N; j++) + { + e += A.get(i,j) * result[j]; + } + rLength2 += e * e; + } + error = (rLength2 / (bLength2 + 1e-10f)); + + iteration++; + } + } +}; + +class Matrix33Solver +{ +public: + + bool solve(const MatrixNN& _A_, const VectorN& _b_, VectorN& result) const + { + const PxF32 a = _A_.get(0,0); + const PxF32 b = _A_.get(0,1); + const PxF32 c = _A_.get(0,2); + + const PxF32 d = _A_.get(1,0); + const PxF32 e = _A_.get(1,1); + const PxF32 f = _A_.get(1,2); + + const PxF32 g = _A_.get(2,0); + const PxF32 h = _A_.get(2,1); + const PxF32 k = _A_.get(2,2); + + const PxF32 detA = a*(e*k - f*h) - b*(k*d - f*g) + c*(d*h - e*g); + if(0.0f == detA) + { + return false; + } + const PxF32 detAInv = 1.0f/detA; + + const PxF32 A = (e*k - f*h); + const PxF32 D = -(b*k - c*h); + const PxF32 G = (b*f - c*e); + const PxF32 B = -(d*k - f*g); + const PxF32 E = (a*k - c*g); + const PxF32 H = -(a*f - c*d); + const PxF32 C = (d*h - e*g); + const PxF32 F = -(a*h - b*g); + const PxF32 K = (a*e - b*d); + + result[0] = detAInv*(A*_b_[0] + D*_b_[1] + G*_b_[2]); + result[1] = detAInv*(B*_b_[0] + E*_b_[1] + H*_b_[2]); + result[2] = detAInv*(C*_b_[0] + F*_b_[1] + K*_b_[2]); + + return true; + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#endif //PX_VEHICLE_LINEAR_MATH_H diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp new file mode 100644 index 000000000..172b5f123 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp @@ -0,0 +1,541 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleComponents.h" +#include "PxVehicleDrive.h" +#include "PxVehicleNoDrive.h" +#include "PxVehicleDrive4W.h" +#include "PxVehicleDriveNW.h" +#include "PxVehicleDriveTank.h" +#include "PxVehicleSuspWheelTire4.h" +#include "PxVehicleSuspLimitConstraintShader.h" + +#include "PxMetaData.h" + +using namespace physx; + +namespace +{ + typedef PxFixedSizeLookupTable PxVehicleEngineTable; + class ShadowLookupTable : public PxVehicleEngineTable + { + public: + static void getBinaryMetaData(PxOutputStream& stream_) + { + PX_DEF_BIN_METADATA_CLASS(stream_, ShadowLookupTable) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream_, PxVehicleEngineTable, PxReal, mDataPairs, 0) + PX_DEF_BIN_METADATA_ITEM(stream_, PxVehicleEngineTable, PxU32, mNbDataPairs, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream_, PxVehicleEngineTable, PxU32, mPad, PxMetaDataFlag::ePADDING) + } + }; +} + +static void getBinaryMetaData_PxFixedSizeLookupTable(PxOutputStream& stream) +{ + ShadowLookupTable::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxFixedSizeLookupTable, ShadowLookupTable) +} + +void PxVehicleDriveSimData::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PxFixedSizeLookupTable(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxVehicleClutchAccuracyMode::Enum, PxU32) + + //PxVehicleEngineData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleEngineData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, ShadowLookupTable, mTorqueCurve, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mMOI, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mPeakTorque, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mMaxOmega, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mDampingRateFullThrottle, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mDampingRateZeroThrottleClutchEngaged, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mDampingRateZeroThrottleClutchDisengaged, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mRecipMOI, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleEngineData, PxReal, mRecipMaxOmega, 0) + + + //PxVehicleGearsData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleGearsData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleGearsData, PxReal, mRatios, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleGearsData, PxReal, mFinalRatio, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleGearsData, PxU32, mNbRatios, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleGearsData, PxReal, mSwitchTime, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleGearsData, PxReal, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleClutchData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleClutchData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleClutchData, PxReal, mStrength, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleClutchData, PxVehicleClutchAccuracyMode::Enum, mAccuracyMode, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleClutchData, PxU32, mEstimateIterations, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleClutchData, PxU8, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleAutoBoxData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleAutoBoxData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleAutoBoxData, PxReal, mUpRatios, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleAutoBoxData, PxReal, mDownRatios, 0) + + //PxVehicleDriveSimData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDriveSimData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData, PxVehicleEngineData, mEngine, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData, PxVehicleGearsData, mGears, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData, PxVehicleClutchData, mClutch, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData, PxVehicleAutoBoxData, mAutoBox, 0) +} + +void PxVehicleDrive::getBinaryMetaData(PxOutputStream& stream) +{ + //PxVehicleDriveDynData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDriveDynData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleDriveDynData, PxReal, mControlAnalogVals, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, bool, mUseAutoGears, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, bool, mGearUpPressed, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, bool, mGearDownPressed, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, PxU32, mCurrentGear, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, PxU32, mTargetGear, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, PxReal, mEnginespeed, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, PxReal, mGearSwitchTime, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveDynData, PxReal, mAutoBoxSwitchTime, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleDriveDynData, PxU32, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleDrive + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleDrive) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDrive, PxVehicleWheels) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDrive, PxVehicleDriveDynData, mDriveDynData, 0) +} + +void PxVehicleDriveSimData4W::getBinaryMetaData(PxOutputStream& stream) +{ + //PxVehicleDifferential4WData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDifferential4WData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mFrontRearSplit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mFrontLeftRightSplit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mRearLeftRightSplit, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mCentreBias, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mFrontBias, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxReal, mRearBias, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferential4WData, PxU32, mType, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleDifferential4WData, PxReal, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleAckermannGeometryData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleAckermannGeometryData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAckermannGeometryData, PxReal, mAccuracy, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAckermannGeometryData, PxReal, mFrontWidth, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAckermannGeometryData, PxReal, mRearWidth, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAckermannGeometryData, PxReal, mAxleSeparation, 0) + + //PxVehicleDriveSimData4W + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDriveSimData4W) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDriveSimData4W, PxVehicleDriveSimData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData4W, PxVehicleDifferential4WData, mDiff, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimData4W, PxVehicleAckermannGeometryData, mAckermannGeometry, 0) +} + +void PxVehicleDriveSimDataNW::getBinaryMetaData(PxOutputStream& stream) +{ + //PxVehicleDifferentialWData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDifferentialNWData) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleDifferentialNWData, PxU32, mBitmapBuffer, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferentialNWData, PxU32, mNbDrivenWheels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferentialNWData, PxReal, mInvNbDrivenWheels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDifferentialNWData, PxU32, mPad, 0) + + //PxVehicleDriveSimDataNW + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleDriveSimDataNW) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDriveSimDataNW, PxVehicleDriveSimData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveSimDataNW, PxVehicleDifferentialNWData, mDiff, 0) +} + +void PxVehicleNoDrive::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleDrive::getBinaryMetaData(stream); + PxVehicleWheels::getBinaryMetaData(stream); + PxVehicleDriveSimData::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleNoDrive) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleNoDrive, PxVehicleWheels) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleNoDrive, PxReal, mSteerAngles, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleNoDrive, PxReal, mDriveTorques, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleNoDrive, PxReal, mBrakeTorques, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleNoDrive, PxU32, mPad, PxMetaDataFlag::ePADDING) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mSteerAngles, mWheelsSimData.mNbWheels4, 0, PX_SERIAL_ALIGN) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mSteerAngles, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mSteerAngles, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mSteerAngles, mWheelsSimData.mNbWheels4, 0, 0) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mDriveTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mDriveTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mDriveTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mDriveTorques, mWheelsSimData.mNbWheels4, 0, 0) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mBrakeTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mBrakeTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mBrakeTorques, mWheelsSimData.mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleNoDrive, PxReal, mBrakeTorques, mWheelsSimData.mNbWheels4, 0, 0) +} + +void PxVehicleNoDrive::exportExtraData(PxSerializationContext& stream) +{ + PxVehicleWheels::exportExtraData(stream); + + PxU32 size = sizeof(PxReal)*4*mWheelsSimData.mNbWheels4; + stream.alignData(); + stream.writeData(mSteerAngles, size); + stream.writeData(mDriveTorques, size); + stream.writeData(mBrakeTorques, size); +} + +void PxVehicleNoDrive::importExtraData(PxDeserializationContext& context) +{ + PxVehicleWheels::importExtraData(context); + + context.alignExtraData(); + mSteerAngles = context.readExtraData(4*mWheelsSimData.mNbWheels4); + mDriveTorques = context.readExtraData(4*mWheelsSimData.mNbWheels4); + mBrakeTorques = context.readExtraData(4*mWheelsSimData.mNbWheels4); +} + +PxVehicleNoDrive* PxVehicleNoDrive::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PxVehicleNoDrive* obj = new (address) PxVehicleNoDrive(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PxVehicleNoDrive); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void PxVehicleDrive4W::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleDrive::getBinaryMetaData(stream); + PxVehicleWheels::getBinaryMetaData(stream); + PxVehicleDriveSimData::getBinaryMetaData(stream); + PxVehicleDriveSimData4W::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleDrive4W) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDrive4W, PxVehicleDrive) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDrive4W, PxVehicleDriveSimData4W, mDriveSimData, 0) +} + +PxVehicleDrive4W* PxVehicleDrive4W::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PxVehicleDrive4W* obj = new (address) PxVehicleDrive4W(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PxVehicleDrive4W); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void PxVehicleDriveNW::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleDriveSimDataNW::getBinaryMetaData(stream); + + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleDriveNW) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDriveNW, PxVehicleDrive) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveNW, PxVehicleDriveSimDataNW, mDriveSimData, 0) +} + +PxVehicleDriveNW* PxVehicleDriveNW::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PxVehicleDriveNW* obj = new (address) PxVehicleDriveNW(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PxVehicleDriveNW); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void PxVehicleDriveTank::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleDriveTank) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleDriveTank, PxVehicleDrive) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveTank, PxVehicleDriveSimData, mDriveSimData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleDriveTank, PxU32, mDriveModel, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleDriveTank, PxU32, mPad, PxMetaDataFlag::ePADDING) +} + +PxVehicleDriveTank* PxVehicleDriveTank::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PxVehicleDriveTank* obj = new (address) PxVehicleDriveTank(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PxVehicleDriveTank); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +void PxVehicleWheelsSimData::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleWheels4SimData::getBinaryMetaData(stream); + //PxVehicleTireLoadFilterData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleTireLoadFilterData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireLoadFilterData, PxReal, mMinNormalisedLoad, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireLoadFilterData, PxReal, mMinFilteredNormalisedLoad, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireLoadFilterData, PxReal, mMaxNormalisedLoad, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireLoadFilterData, PxReal, mMaxFilteredNormalisedLoad, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireLoadFilterData, PxReal, mDenominator, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleTireLoadFilterData, PxU32, mPad, PxMetaDataFlag::ePADDING) + + //Add anti-roll here to save us having to add an extra function. + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleAntiRollBarData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAntiRollBarData, PxU32, mWheel0, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAntiRollBarData, PxU32, mWheel1, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleAntiRollBarData, PxReal, mStiffness, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleAntiRollBarData, PxU32, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleWheelsSimData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleWheelsSimData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxVehicleTireLoadFilterData, mNormalisedLoadFilter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxVehicleWheels4SimData, mWheels4SimData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mNbWheels4, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mNbActiveWheels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxVehicleAntiRollBarData, mAntiRollBars, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mNbAntiRollBars4, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mNbActiveAntiRollBars, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mActiveWheelsBitmapBuffer, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxReal, mThresholdLongitudinalSpeed, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mLowForwardSpeedSubStepCount, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mHighForwardSpeedSubStepCount, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsSimData, PxU32, mMinLongSlipDenominator, 0) + +#if PX_P64_FAMILY + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheelsSimData, PxU32, mPad, PxMetaDataFlag::ePADDING) +#else + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheelsSimData, PxU32, mPad, PxMetaDataFlag::ePADDING) +#endif + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsSimData, PxVehicleWheels4SimData, mWheels4SimData, mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsSimData, PxVehicleAntiRollBarData, mAntiRollBars, mNbAntiRollBars4, 0, 0) +} + +void PxVehicleWheelsDynData::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleWheels4DynData::getBinaryMetaData(stream); + PxVehicleConstraintShader::getBinaryMetaData(stream); + + //PxVehicleTireForceCalculator + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleTireForceCalculator) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireForceCalculator, void*, mShaderData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireForceCalculator, PxU32, mShader, PxMetaDataFlag::ePTR) + +#if !PX_P64_FAMILY + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleTireForceCalculator, PxU32, mPad, PxMetaDataFlag::ePADDING) +#endif + + //PxVehicleWheelsDynData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleWheelsDynData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsDynData, PxVehicleWheels4DynData, mWheels4DynData, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsDynData, PxVehicleTireForceCalculator, mTireForceCalculators, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsDynData, PxU32, mUserDatas, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsDynData, PxU32, mNbWheels4, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelsDynData, PxU32, mNbActiveWheels, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheelsDynData, PxU32, mPad, PxMetaDataFlag::ePADDING) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, PxVehicleWheels4DynData, mWheels4DynData, mNbWheels4, 0, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEM(stream, PxVehicleWheelsDynData, PxVehicleTireForceCalculator, mTireForceCalculators, 0) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mTireForceCalculators, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mTireForceCalculators, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mTireForceCalculators, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mTireForceCalculators, mNbWheels4, PxMetaDataFlag::ePTR, 0) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mUserDatas, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mUserDatas, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mUserDatas, mNbWheels4, PxMetaDataFlag::ePTR, 0) + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, void, mUserDatas, mNbWheels4, PxMetaDataFlag::ePTR, 0) + + PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, PxVehicleWheelsDynData, PxVehicleConstraintShader, mWheels4DynData, mNbWheels4, 0, 0) +} + +void PxVehicleWheels::exportExtraData(PxSerializationContext& stream) +{ + PxU32 size = computeByteSize(mWheelsSimData.mNbActiveWheels); + stream.alignData(); + stream.writeData(mWheelsSimData.mWheels4SimData, size); +} + +void PxVehicleWheels::importExtraData(PxDeserializationContext& context) +{ + PxU32 size = computeByteSize(mWheelsSimData.mNbActiveWheels); + PxU8* ptr = context.readExtraData(size); + patchupPointers(mWheelsSimData.mNbActiveWheels, this, ptr); +} + +void PxVehicleWheels::getBinaryMetaData(PxOutputStream& stream) +{ + PxVehicleWheelsSimData::getBinaryMetaData(stream); + PxVehicleWheelsDynData::getBinaryMetaData(stream); + + //PxVehicleWheels + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleWheels) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleWheels, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxVehicleWheelsSimData, mWheelsSimData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxVehicleWheelsDynData, mWheelsDynData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxRigidDynamic, mActor, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxU32, mNbNonDrivenWheels, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxU8, mOnConstraintReleaseCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels, PxU8, mType, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels, PxU8, mPad0, PxMetaDataFlag::ePADDING) +} + +void PxVehicleConstraintShader::getBinaryMetaData(PxOutputStream& stream) +{ + //SuspLimitConstraintData + PX_DEF_BIN_METADATA_CLASS(stream, SuspLimitConstraintData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, SuspLimitConstraintData, PxVec3, mCMOffsets, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, SuspLimitConstraintData, PxVec3, mDirs, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, SuspLimitConstraintData, PxReal, mErrors, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, SuspLimitConstraintData, bool, mActiveFlags, 0) + + //StickyTireConstraintData + PX_DEF_BIN_METADATA_CLASS(stream, StickyTireConstraintData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, StickyTireConstraintData, PxVec3, mCMOffsets, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, StickyTireConstraintData, PxVec3, mDirs, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, StickyTireConstraintData, PxReal, mTargetSpeeds, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, StickyTireConstraintData, bool, mActiveFlags, 0) + + //VehicleConstraintData + PX_DEF_BIN_METADATA_CLASS(stream, VehicleConstraintData) + PX_DEF_BIN_METADATA_ITEM(stream, VehicleConstraintData, SuspLimitConstraintData, mSuspLimitData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, VehicleConstraintData, StickyTireConstraintData, mStickyTireForwardData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, VehicleConstraintData, StickyTireConstraintData, mStickyTireSideData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, VehicleConstraintData, PxQuat, mCMassRotation, 0) + + //PxVehicleConstraintShader + PX_DEF_BIN_METADATA_VCLASS(stream, PxVehicleConstraintShader) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxVehicleConstraintShader, PxConstraintConnector) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleConstraintShader, VehicleConstraintData, mData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleConstraintShader, PxConstraint, mConstraint, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleConstraintShader, PxVehicleWheels, mVehicle, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleConstraintShader, PxU32, mPad, PxMetaDataFlag::ePADDING) +} + +void PxVehicleWheels4SimData::getBinaryMetaData(PxOutputStream& stream) +{ + //PxVehicleSuspensionData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleSuspensionData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mSpringStrength, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mSpringDamperRate, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mMaxCompression, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mMaxDroop, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mSprungMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mCamberAtRest, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mCamberAtMaxCompression, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mCamberAtMaxDroop, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mRecipMaxCompression, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleSuspensionData, PxReal, mRecipMaxDroop, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleSuspensionData, PxU32, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleWheelData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleWheelData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mRadius, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mWidth, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mMOI, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mDampingRate, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mMaxBrakeTorque, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mMaxHandBrakeTorque, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mMaxSteer, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mToeAngle, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mRecipRadius, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheelData, PxReal, mRecipMOI, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheelData, PxReal, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleTireData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleTireData) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mLatStiffX, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mLatStiffY, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mLongitudinalStiffnessPerUnitGravity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mCamberStiffnessPerUnitGravity, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleTireData, PxReal, mFrictionVsSlipGraph, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxU32, mType, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mRecipLongitudinalStiffnessPerUnitGravity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mFrictionVsSlipGraphRecipx1Minusx0, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleTireData, PxReal, mFrictionVsSlipGraphRecipx2Minusx1, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleTireData, PxReal, mPad, PxMetaDataFlag::ePADDING) + + //PxVehicleWheels4SimData + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleWheels4SimData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVehicleSuspensionData, mSuspensions, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVehicleWheelData, mWheels, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVehicleTireData, mTires, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVec3, mSuspDownwardTravelDirections, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVec3, mSuspForceAppPointOffsets, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVec3, mTireForceAppPointOffsets, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxVec3, mWheelCentreOffsets, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxReal, mTireRestLoads, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxReal, mRecipTireRestLoads, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxFilterData, mSqFilterData, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxU8, mWheelShapeMap, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4SimData, PxU32, mPad, PxMetaDataFlag::ePADDING) +} + +/* +void PxVehicleAntiRollBar::getBinaryMetaData(PxOutputStream& stream) +{ +} +*/ + +void PxVehicleWheels4DynData::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxVehicleWheels4DynData) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mWheelSpeeds, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mCorrectedWheelSpeeds, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mWheelRotationAngles, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mTireLowForwardSpeedTimers, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mTireLowSideSpeedTimers, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxU8, mQueryOrCachedHitResults, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxReal, mJounces, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels4DynData, PxVehicleConstraintShader, mVehicleConstraints, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels4DynData, PxRaycastQueryResult, mRaycastResults, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels4DynData, PxSweepQueryResult, mSweepResults, PxMetaDataFlag::ePTR) + + PX_DEF_BIN_METADATA_ITEM(stream, PxVehicleWheels4DynData, bool, mHasCachedRaycastHitPlane, 0) +#if PX_P64_FAMILY + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, PxVehicleWheels4DynData, PxU32, mPad, PxMetaDataFlag::ePADDING) +#endif +} diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp new file mode 100644 index 000000000..a8a87f5fb --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "PxVehicleNoDrive.h" +#include "PxVehicleWheels.h" +#include "PxVehicleDefaults.h" +#include "PxRigidDynamic.h" +#include "CmPhysXCommon.h" +#include "CmUtils.h" +#include "PsFoundation.h" + +namespace physx +{ + +extern PxF32 gToleranceScaleLength; + +bool PxVehicleNoDrive::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(PxVehicleWheels::isValid(), "invalid PxVehicleDrive", false); + return true; +} + +PxVehicleNoDrive* PxVehicleNoDrive::allocate(const PxU32 numWheels) +{ + PX_CHECK_AND_RETURN_NULL(numWheels>0, "Cars with zero wheels are illegal"); + PX_CHECK_AND_RETURN_NULL(gToleranceScaleLength > 0, "PxVehicleNoDrive::allocate - need to call PxInitVehicleSDK"); + + //Compute the bytes needed. + const PxU32 numWheels4 = (((numWheels + 3) & ~3) >> 2); + const PxU32 inputByteSize16 = sizeof(PxReal)*numWheels4*4; + const PxU32 byteSize = sizeof(PxVehicleNoDrive) + 3*inputByteSize16 + PxVehicleWheels::computeByteSize(numWheels); + + //Allocate the memory. + PxVehicleNoDrive* veh = static_cast(PX_ALLOC(byteSize, "PxVehicleNoDrive")); + Cm::markSerializedMem(veh, byteSize); + new(veh) PxVehicleNoDrive(); + + //Patch up the pointers. + PxU8* ptr = reinterpret_cast(veh) + sizeof(PxVehicleNoDrive); + veh->mSteerAngles = reinterpret_cast(ptr); + ptr += inputByteSize16; + veh->mDriveTorques = reinterpret_cast(ptr); + ptr += inputByteSize16; + veh->mBrakeTorques = reinterpret_cast(ptr); + ptr += inputByteSize16; + ptr = PxVehicleWheels::patchupPointers(numWheels, veh, ptr); + + //Initialise. + PxMemZero(veh->mSteerAngles, inputByteSize16); + PxMemZero(veh->mDriveTorques, inputByteSize16); + PxMemZero(veh->mBrakeTorques, inputByteSize16); + veh->init(numWheels); + + //Set the vehicle type. + veh->mType = PxVehicleTypes::eNODRIVE; + + return veh; +} + +void PxVehicleNoDrive::free() +{ + PxVehicleWheels::free(); +} + +void PxVehicleNoDrive::setup +(PxPhysics* physics, PxRigidDynamic* vehActor, const PxVehicleWheelsSimData& wheelsData) +{ + //Set up the wheels. + PxVehicleWheels::setup(physics,vehActor,wheelsData,0,wheelsData.getNbWheels()); +} + +PxVehicleNoDrive* PxVehicleNoDrive::create +(PxPhysics* physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData) +{ + PxVehicleNoDrive* veh=PxVehicleNoDrive::allocate(wheelsData.getNbWheels()); + veh->setup(physics,vehActor,wheelsData); + return veh; +} + +void PxVehicleNoDrive::setToRestState() +{ + const PxU32 numWheels4 = (((mWheelsSimData.getNbWheels() + 3) & ~3) >> 2); + const PxU32 inputByteSize = sizeof(PxReal)*numWheels4*4; + const PxU32 inputByteSize16 = (inputByteSize + 15) & ~15; + PxMemZero(mSteerAngles, 3*inputByteSize16); + + //Set core to rest state. + PxVehicleWheels::setToRestState(); +} + +void PxVehicleNoDrive::setBrakeTorque(const PxU32 id, const PxReal brakeTorque) +{ + PX_CHECK_AND_RETURN(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::setBrakeTorque - Illegal wheel"); + PX_CHECK_AND_RETURN(brakeTorque>=0, "PxVehicleNoDrive::setBrakeTorque - negative brake torques are illegal"); + mBrakeTorques[id] = brakeTorque; +} + +void PxVehicleNoDrive::setDriveTorque(const PxU32 id, const PxReal driveTorque) +{ + PX_CHECK_AND_RETURN(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::setDriveTorque - Illegal wheel"); + mDriveTorques[id] = driveTorque; +} + +void PxVehicleNoDrive::setSteerAngle(const PxU32 id, const PxReal steerAngle) +{ + PX_CHECK_AND_RETURN(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::setSteerAngle - Illegal wheel"); + mSteerAngles[id] = steerAngle; +} + +PxReal PxVehicleNoDrive::getBrakeTorque(const PxU32 id) const +{ + PX_CHECK_AND_RETURN_VAL(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::getBrakeTorque - Illegal wheel", 0); + return mBrakeTorques[id]; +} + +PxReal PxVehicleNoDrive::getDriveTorque(const PxU32 id) const +{ + PX_CHECK_AND_RETURN_VAL(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::getDriveTorque - Illegal wheel",0); + return mDriveTorques[id]; +} + +PxReal PxVehicleNoDrive::getSteerAngle(const PxU32 id) const +{ + PX_CHECK_AND_RETURN_VAL(id < mWheelsSimData.getNbWheels(), "PxVehicleNoDrive::getSteerAngle - Illegal wheel",0); + return mSteerAngles[id]; +} + +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp new file mode 100644 index 000000000..18310f07e --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp @@ -0,0 +1,115 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleSDK.h" +#include "PxPhysics.h" +#include "PxTolerancesScale.h" +#include "CmPhysXCommon.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "PxVehicleDrive4W.h" +#include "PxVehicleMetaDataObjects.h" +#include "PxVehicleSerialization.h" +#include "SnRepXSerializerImpl.h" +#include "PxSerializer.h" +#include "PxVehicleDriveTank.h" +#include "PxVehicleNoDrive.h" +#include "PxVehicleDriveNW.h" + +namespace physx +{ + +void setVehicleToleranceScale(const PxTolerancesScale& ts); +void resetVehicleToleranceScale(); +void setSerializationRegistryPtr(const PxSerializationRegistry* sr); +const PxSerializationRegistry* resetSerializationRegistryPtr(); +void setVehicleDefaults(); + +bool PxInitVehicleSDK(PxPhysics& physics, PxSerializationRegistry* sr) +{ + PX_ASSERT(static_cast(&physics.getFoundation()) == &Ps::Foundation::getInstance()); + Ps::Foundation::incRefCount(); + setVehicleToleranceScale(physics.getTolerancesScale()); + + setVehicleDefaults(); + + setSerializationRegistryPtr(sr); + if(sr) + { + sr->registerRepXSerializer(PxVehicleConcreteType::eVehicleDrive4W, PX_NEW_REPX_SERIALIZER(PxVehicleRepXSerializer)); + sr->registerRepXSerializer(PxVehicleConcreteType::eVehicleDriveTank, PX_NEW_REPX_SERIALIZER(PxVehicleRepXSerializer)); + sr->registerRepXSerializer(PxVehicleConcreteType::eVehicleDriveNW, PX_NEW_REPX_SERIALIZER(PxVehicleRepXSerializer)); + sr->registerRepXSerializer(PxVehicleConcreteType::eVehicleNoDrive, PX_NEW_REPX_SERIALIZER(PxVehicleRepXSerializer)); + + sr->registerSerializer(PxVehicleConcreteType::eVehicleDrive4W, PX_NEW_SERIALIZER_ADAPTER(PxVehicleDrive4W)); + sr->registerSerializer(PxVehicleConcreteType::eVehicleDriveTank, PX_NEW_SERIALIZER_ADAPTER(PxVehicleDriveTank)); + sr->registerSerializer(PxVehicleConcreteType::eVehicleNoDrive, PX_NEW_SERIALIZER_ADAPTER(PxVehicleNoDrive)); + sr->registerSerializer(PxVehicleConcreteType::eVehicleDriveNW, PX_NEW_SERIALIZER_ADAPTER(PxVehicleDriveNW)); + + sr->registerBinaryMetaDataCallback(PxVehicleDrive4W::getBinaryMetaData); + sr->registerBinaryMetaDataCallback(PxVehicleDriveTank::getBinaryMetaData); + sr->registerBinaryMetaDataCallback(PxVehicleNoDrive::getBinaryMetaData); + sr->registerBinaryMetaDataCallback(PxVehicleDriveNW::getBinaryMetaData); + } + return true; +} + +void PxCloseVehicleSDK(PxSerializationRegistry* sr) +{ + Ps::Foundation::decRefCount(); + resetVehicleToleranceScale(); + + setVehicleDefaults(); + + if (sr != resetSerializationRegistryPtr()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxCloseVehicleSDK called with different PxSerializationRegistry instance than PxInitVehicleSDK."); + return; + } + + if(sr) + { + PX_DELETE_SERIALIZER_ADAPTER(sr->unregisterSerializer(PxVehicleConcreteType::eVehicleDrive4W)); + PX_DELETE_SERIALIZER_ADAPTER(sr->unregisterSerializer(PxVehicleConcreteType::eVehicleDriveTank)); + PX_DELETE_SERIALIZER_ADAPTER(sr->unregisterSerializer(PxVehicleConcreteType::eVehicleNoDrive)); + PX_DELETE_SERIALIZER_ADAPTER(sr->unregisterSerializer(PxVehicleConcreteType::eVehicleDriveNW)); + + PX_DELETE_REPX_SERIALIZER(sr->unregisterRepXSerializer(PxVehicleConcreteType::eVehicleDrive4W)); + PX_DELETE_REPX_SERIALIZER(sr->unregisterRepXSerializer(PxVehicleConcreteType::eVehicleDriveTank)); + PX_DELETE_REPX_SERIALIZER(sr->unregisterRepXSerializer(PxVehicleConcreteType::eVehicleNoDrive)); + PX_DELETE_REPX_SERIALIZER(sr->unregisterRepXSerializer(PxVehicleConcreteType::eVehicleDriveNW)); + } +} +///////////////////////// + + + + +}//physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp new file mode 100644 index 000000000..dbb616c9b --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp @@ -0,0 +1,203 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxRepXSimpleType.h" +#include "PxBase.h" +#include "PxCollection.h" +#include "PxVehicleMetaDataObjects.h" +#include "SnRepXSerializerImpl.h" +#include "PxVehicleSerialization.h" +#include "PxVehicleSuspWheelTire4.h" +#include "PxVehicleSuspLimitConstraintShader.h" +#include "PsFPU.h" + + +namespace physx +{ + using namespace Sn; + + template + inline void* createVehicle( PxPhysics& physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, const PxVehicleDriveSimDataNW& driveDataNW, + const PxU32 numWheels, const PxU32 numNonDrivenWheels) + { + PX_UNUSED(physics); + PX_UNUSED(vehActor); + PX_UNUSED(wheelsData); + PX_UNUSED(driveData); + PX_UNUSED(driveDataNW); + PX_UNUSED(numWheels); + PX_UNUSED(numNonDrivenWheels); + return NULL; + } + + template<> + inline void* createVehicle(PxPhysics& physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, const PxVehicleDriveSimDataNW& /*driveDataNW*/, + const PxU32 numWheels, const PxU32 numNonDrivenWheels) + { + PxVehicleDrive4W* vehDrive4W = PxVehicleDrive4W::allocate(numWheels); + vehDrive4W->setup(&physics, vehActor->is(), wheelsData, driveData, numNonDrivenWheels); + return vehDrive4W; + } + + template<> + inline void* createVehicle(PxPhysics& physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& driveData, const PxVehicleDriveSimDataNW& /*driveDataNW*/, + const PxU32 numWheels, const PxU32 numNonDrivenWheels) + { + PxVehicleDriveTank* tank = PxVehicleDriveTank::allocate(numWheels); + tank->setup(&physics, vehActor->is(), wheelsData, driveData, numWheels - numNonDrivenWheels); + return tank; + } + + template<> + inline void* createVehicle(PxPhysics& physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& /*driveData*/, const PxVehicleDriveSimDataNW& driveDataNW, + const PxU32 numWheels, const PxU32 numNonDrivenWheels) + { + PxVehicleDriveNW* vehDriveNW = PxVehicleDriveNW::allocate(numWheels); + vehDriveNW->setup(&physics, vehActor->is(), wheelsData, driveDataNW, numWheels - numNonDrivenWheels); + return vehDriveNW; + } + + template<> + inline void* createVehicle(PxPhysics& physics, PxRigidDynamic* vehActor, + const PxVehicleWheelsSimData& wheelsData, const PxVehicleDriveSimData4W& /*driveData*/, const PxVehicleDriveSimDataNW& /*driveDataNW*/, + const PxU32 numWheels, const PxU32 /*numNonDrivenWheels*/) + { + PxVehicleNoDrive* vehNoDrive = PxVehicleNoDrive::allocate(numWheels); + vehNoDrive->setup(&physics, vehActor->is(), wheelsData); + return vehNoDrive; + } + + template + PxRepXObject PxVehicleRepXSerializer::fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ) + { + PxRigidActor* vehActor = NULL; + readReference( inReader, *inCollection, "PxRigidDynamicRef", vehActor ); + if ( vehActor == NULL ) + return PxRepXObject(); + + PxU32 numWheels = 0; + readProperty( inReader, "NumWheels", numWheels ); + if( numWheels == 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, + "PxSerialization::createCollectionFromXml: PxVehicleRepXSerializer: Xml field NumWheels is zero!"); + return PxRepXObject(); + } + + PxU32 numNonDrivenWheels = 0; + readProperty( inReader, "NumNonDrivenWheels", numNonDrivenWheels ); + + //change to numwheel + PxVehicleWheelsSimData* wheelsSimData=PxVehicleWheelsSimData::allocate(numWheels); + { + inReader.pushCurrentContext(); + if ( inReader.gotoChild( "MWheelsSimData" ) ) + { + readAllProperties( inArgs, inReader, wheelsSimData, inAllocator, *inCollection ); + } + + inReader.popCurrentContext(); + } + + PxVehicleDriveSimData4W driveSimData; + { + inReader.pushCurrentContext(); + if ( inReader.gotoChild( "MDriveSimData" ) ) + { + readAllProperties( inArgs, inReader, &driveSimData, inAllocator, *inCollection ); + } + + inReader.popCurrentContext(); + } + + PxVehicleDriveSimDataNW nmSimData; + { + inReader.pushCurrentContext(); + if ( inReader.gotoChild( "MDriveSimDataNW" ) ) + { + readAllProperties( inArgs, inReader, &driveSimData, inAllocator, *inCollection ); + } + inReader.popCurrentContext(); + } + TVehicleType* drive = static_cast(createVehicle(inArgs.physics, vehActor->is(), *wheelsSimData, driveSimData, nmSimData, numWheels, numNonDrivenWheels)); + readAllProperties( inArgs, inReader, drive, inAllocator, *inCollection ); + + PxVehicleWheels4DynData* wheel4DynData = drive->mWheelsDynData.getWheel4DynData(); + PX_ASSERT( wheel4DynData ); + for(PxU32 i=0;igetNbWheels4();i++) + { + PxConstraint* constraint = wheel4DynData[i].getVehicletConstraintShader().getPxConstraint(); + if( constraint ) + inCollection->add(*constraint); + } + + if( wheelsSimData ) + wheelsSimData->free(); + + return PxCreateRepXObject(drive); + } + + template + void PxVehicleRepXSerializer::objectToFileImpl( const TVehicleType* drive, PxCollection* inCollection, XmlWriter& inWriter, MemoryBuffer& inTempBuffer, PxRepXInstantiationArgs& /*inArgs*/ ) + { + PX_SIMD_GUARD; // denorm exception triggered in PxVehicleGearsDataGeneratedInfo::visitInstanceProperties on osx + writeReference( inWriter, *inCollection, "PxRigidDynamicRef", drive->getRigidDynamicActor() ); + writeProperty( inWriter, *inCollection, inTempBuffer, "NumWheels", drive->mWheelsSimData.getNbWheels() ); + writeProperty( inWriter, *inCollection, inTempBuffer, "NumNonDrivenWheels", drive->getNbNonDrivenWheels()); + writeAllProperties( drive, inWriter, inTempBuffer, *inCollection ); + } + + PxVehicleNoDrive::PxVehicleNoDrive() + : PxVehicleWheels(PxVehicleConcreteType::eVehicleNoDrive, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) + {} + + PxVehicleDrive4W::PxVehicleDrive4W() + : PxVehicleDrive(PxVehicleConcreteType::eVehicleDrive4W, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) + {} + + PxVehicleDriveNW::PxVehicleDriveNW() + : PxVehicleDrive(PxVehicleConcreteType::eVehicleDriveNW, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) + {} + + PxVehicleDriveTank::PxVehicleDriveTank() + : PxVehicleDrive(PxVehicleConcreteType::eVehicleDriveTank, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) + , mDriveModel(PxVehicleDriveTankControlModel::eSTANDARD) + {} + + // explicit template instantiations + template struct PxVehicleRepXSerializer; + template struct PxVehicleRepXSerializer; + template struct PxVehicleRepXSerializer; + template struct PxVehicleRepXSerializer; + +} diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h new file mode 100644 index 000000000..8e5914887 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_SERIALIZATION_H +#define PX_VEHICLE_SERIALIZATION_H + +#include "extensions/PxRepXSimpleType.h" +#include "SnRepXSerializerImpl.h" + +namespace physx +{ + class PxRepXSerializer; + class PxSerializationRegistry; + class XmlReader; + class XmlMemoryAllocator; + class XmlWriter; + class MemoryBuffer; + + PX_DEFINE_TYPEINFO(PxVehicleNoDrive, PxVehicleConcreteType::eVehicleNoDrive) + PX_DEFINE_TYPEINFO(PxVehicleDrive4W, PxVehicleConcreteType::eVehicleDrive4W) + PX_DEFINE_TYPEINFO(PxVehicleDriveNW, PxVehicleConcreteType::eVehicleDriveNW) + PX_DEFINE_TYPEINFO(PxVehicleDriveTank, PxVehicleConcreteType::eVehicleDriveTank) + + template + struct PxVehicleRepXSerializer : public RepXSerializerImpl + { + PxVehicleRepXSerializer( PxAllocatorCallback& inCallback ) : RepXSerializerImpl( inCallback ) {} + virtual PxRepXObject fileToObject( XmlReader& inReader, XmlMemoryAllocator& inAllocator, PxRepXInstantiationArgs& inArgs, PxCollection* inCollection ); + virtual void objectToFileImpl( const TVehicleType* , PxCollection* , XmlWriter& , MemoryBuffer& , PxRepXInstantiationArgs& ); + virtual TVehicleType* allocateObject( PxRepXInstantiationArgs& ) { return NULL; } + }; + +#if PX_SUPPORT_EXTERN_TEMPLATE + // explicit template instantiation declarations + extern template struct PxVehicleRepXSerializer; + extern template struct PxVehicleRepXSerializer; + extern template struct PxVehicleRepXSerializer; + extern template struct PxVehicleRepXSerializer; +#endif + +} + + +#endif//PX_VEHICLE_REPX_SERIALIZER_H diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h new file mode 100644 index 000000000..d8864054d --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h @@ -0,0 +1,317 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_SUSP_LIMIT_CONSTRAINT_SHADER_H +#define PX_VEHICLE_SUSP_LIMIT_CONSTRAINT_SHADER_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxTransform.h" +#include "extensions/PxConstraintExt.h" +#include "PxConstraintDesc.h" +#include "PxConstraint.h" +#include "PsAllocator.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxVehicleConstraintShader : public PxConstraintConnector +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleWheels; + + PxVehicleConstraintShader(PxVehicleWheels* vehicle, PxConstraint* constraint = NULL) + : mConstraint(constraint), + mVehicle(vehicle) + { + } + PxVehicleConstraintShader(){} + ~PxVehicleConstraintShader() + { + } + + static void getBinaryMetaData(PxOutputStream& stream); + + void release() + { + if(mConstraint) + { + mConstraint->release(); + } + } + + virtual void onComShift(PxU32 actor) { PX_UNUSED(actor); } + + virtual void onOriginShift(const PxVec3& shift) { PX_UNUSED(shift); } + + virtual void* prepareData() + { + return &mData; + } + + virtual bool updatePvdProperties(pvdsdk::PvdDataStream& pvdConnection, + const PxConstraint* c, + PxPvdUpdateType::Enum updateType) const { PX_UNUSED(c); PX_UNUSED(updateType); PX_UNUSED(&pvdConnection); return true;} + + virtual void onConstraintRelease() + { + mVehicle->mOnConstraintReleaseCounter--; + if(0==mVehicle->mOnConstraintReleaseCounter) + { + PX_FREE(mVehicle); + } + } + + virtual void* getExternalReference(PxU32& typeID) { typeID = PxConstraintExtIDs::eVEHICLE_SUSP_LIMIT; return this; } + virtual PxBase* getSerializable() { return NULL; } + + + static PxU32 vehicleSuspLimitConstraintSolverPrep( + Px1DConstraint* constraints, + PxVec3& body0WorldOffset, + PxU32 maxConstraints, + PxConstraintInvMassScale&, + const void* constantBlock, + const PxTransform& bodyAToWorld, + const PxTransform& bodyBToWorld, + bool, + PxVec3& cA2w, PxVec3& cB2w + ) + { + PX_UNUSED(maxConstraints); + PX_UNUSED(body0WorldOffset); + PX_UNUSED(bodyBToWorld); + PX_ASSERT(bodyAToWorld.isValid()); PX_ASSERT(bodyBToWorld.isValid()); + + const VehicleConstraintData* data = static_cast(constantBlock); + PxU32 numActive=0; + const PxQuat bodyRotation = bodyAToWorld.q * data->mCMassRotation.getConjugate(); + + //KS - the substep solver will use raXn to try to add to the angular part of the linear constraints. + //We overcome this by setting the ra and rb offsets to be 0. In addition, we will + cA2w = bodyAToWorld.p; + cB2w = bodyBToWorld.p; + + //Susp limit constraints. + for(PxU32 i=0;i<4;i++) + { + if(data->mSuspLimitData.mActiveFlags[i]) + { + Px1DConstraint& p=constraints[numActive]; + p.linear0 = bodyRotation.rotate(data->mSuspLimitData.mDirs[i]); + p.angular0 = bodyRotation.rotate(data->mSuspLimitData.mCMOffsets[i].cross(data->mSuspLimitData.mDirs[i])); + p.geometricError=data->mSuspLimitData.mErrors[i]; + p.linear1=PxVec3(0); + p.angular1=PxVec3(0); + p.minImpulse=-FLT_MAX; + p.maxImpulse=0; + p.velocityTarget=0; + p.flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT; + numActive++; + } + } + + //Sticky tire friction constraints. + for(PxU32 i=0;i<4;i++) + { + if(data->mStickyTireForwardData.mActiveFlags[i]) + { + Px1DConstraint& p=constraints[numActive]; + p.linear0=data->mStickyTireForwardData.mDirs[i]; + p.angular0=data->mStickyTireForwardData.mCMOffsets[i].cross(data->mStickyTireForwardData.mDirs[i]); + p.geometricError=0.0f; + p.linear1=PxVec3(0); + p.angular1=PxVec3(0); + p.minImpulse=-FLT_MAX; + p.maxImpulse=FLT_MAX; + p.velocityTarget=data->mStickyTireForwardData.mTargetSpeeds[i]; + p.mods.spring.damping = 1000.0f; + p.flags = Px1DConstraintFlag::eSPRING | Px1DConstraintFlag::eACCELERATION_SPRING; + p.flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT; + numActive++; + } + } + + //Sticky tire friction constraints. + for(PxU32 i=0;i<4;i++) + { + if(data->mStickyTireSideData.mActiveFlags[i]) + { + Px1DConstraint& p=constraints[numActive]; + p.linear0=data->mStickyTireSideData.mDirs[i]; + p.angular0=data->mStickyTireSideData.mCMOffsets[i].cross(data->mStickyTireSideData.mDirs[i]); + p.geometricError=0.0f; + p.linear1=PxVec3(0); + p.angular1=PxVec3(0); + p.minImpulse=-FLT_MAX; + p.maxImpulse=FLT_MAX; + p.velocityTarget=data->mStickyTireSideData.mTargetSpeeds[i]; + p.mods.spring.damping = 1000.0f; + p.flags = Px1DConstraintFlag::eSPRING | Px1DConstraintFlag::eACCELERATION_SPRING; + p.flags |= Px1DConstraintFlag::eANGULAR_CONSTRAINT; + numActive++; + } + } + + + return numActive; + } + + static void visualiseConstraint(PxConstraintVisualizer &viz, + const void* constantBlock, + const PxTransform& body0Transform, + const PxTransform& body1Transform, + PxU32 flags){ PX_UNUSED(&viz); PX_UNUSED(constantBlock); PX_UNUSED(body0Transform); + PX_UNUSED(body1Transform); PX_UNUSED(flags); + PX_ASSERT(body0Transform.isValid()); PX_ASSERT(body1Transform.isValid()); } + +public: + + struct SuspLimitConstraintData + { + PxVec3 mCMOffsets[4]; + PxVec3 mDirs[4]; + PxReal mErrors[4]; + bool mActiveFlags[4]; + }; + struct StickyTireConstraintData + { + PxVec3 mCMOffsets[4]; + PxVec3 mDirs[4]; + PxReal mTargetSpeeds[4]; + bool mActiveFlags[4]; + }; + + struct VehicleConstraintData + { + SuspLimitConstraintData mSuspLimitData; + StickyTireConstraintData mStickyTireForwardData; + StickyTireConstraintData mStickyTireSideData; + PxQuat mCMassRotation; + }; + VehicleConstraintData mData; + + PxConstraint* mConstraint; + + PX_INLINE void setPxConstraint(PxConstraint* pxConstraint) + { + mConstraint = pxConstraint; + } + + PX_INLINE PxConstraint* getPxConstraint() + { + return mConstraint; + } + + PxConstraintConnector* getConnector() + { + return this; + } + + virtual PxConstraintSolverPrep getPrep()const { return NULL; /*KS - TODO. Figure out what to return here. Probably nothing is OK*/ } + virtual const void* getConstantBlock()const { return NULL; /*KS - TODO. Figure out what to return here. Probably nothing is OK*/ } + +private: + + PxVehicleWheels* mVehicle; + +#if !PX_P64_FAMILY + PxU32 mPad[2]; +#else + PxU32 mPad[1]; +#endif +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleConstraintShader)& 0x0f)); + + +/** +\brief Default implementation of PxVehicleComputeTireForce +@see PxVehicleComputeTireForce, PxVehicleTireForceCalculator +*/ +void PxVehicleComputeTireForceDefault + (const void* shaderData, + const PxF32 tireFriction, + const PxF32 longSlip, const PxF32 latSlip, const PxF32 camber, + const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 recipWheelRadius, + const PxF32 restTireLoad, const PxF32 normalisedTireLoad, const PxF32 tireLoad, + const PxF32 gravity, const PxF32 recipGravity, + PxF32& wheelTorque, PxF32& tireLongForceMag, PxF32& tireLatForceMag, PxF32& tireAlignMoment); + + +/** +\brief Structure containing shader data for each tire of a vehicle and a shader function that computes individual tire forces +*/ +class PxVehicleTireForceCalculator +{ +public: + + PxVehicleTireForceCalculator() + : mShader(PxVehicleComputeTireForceDefault) + { + } + + /** + \brief Array of shader data - one data entry per tire. + Default values are pointers to PxVehicleTireData (stored in PxVehicleWheelsSimData) and are set in PxVehicleDriveTank::setup or PxVehicleDrive4W::setup + @see PxVehicleComputeTireForce, PxVehicleComputeTireForceDefault, PxVehicleWheelsSimData, PxVehicleDriveTank::setup, PxVehicleDrive4W::setup + */ + const void** mShaderData; + + /** + \brief Shader function. + Default value is PxVehicleComputeTireForceDefault and is set in PxVehicleDriveTank::setup or PxVehicleDrive4W::setup + @see PxVehicleComputeTireForce, PxVehicleComputeTireForceDefault, PxVehicleWheelsSimData, PxVehicleDriveTank::setup, PxVehicleDrive4W::setup + */ + PxVehicleComputeTireForce mShader; + +#if !PX_P64_FAMILY + PxU32 mPad[2]; +#endif +}; + +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleTireForceCalculator) & 15)); + + +#if !PX_DOXYGEN +} // namespace physx +#endif + + +/** @} */ +#endif //PX_VEHICLE_SUSP_LIMIT_CONSTRAINT_SHADER_H diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp new file mode 100644 index 000000000..61b61f176 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp @@ -0,0 +1,191 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleSuspWheelTire4.h" +#include "PxVehicleDefaults.h" +#include "PsFoundation.h" +#include "CmPhysXCommon.h" +#include "PsUtilities.h" + +namespace physx +{ + +PxVehicleWheels4SimData::PxVehicleWheels4SimData() +{ + for(PxU32 i=0;i<4;i++) + { + mSuspDownwardTravelDirections[i]=PxVec3(0,0,0); //Must be filled out + mSuspForceAppPointOffsets[i]=PxVec3(0,0,0); //Must be filled out + mTireForceAppPointOffsets[i]=PxVec3(0,0,0); //Must be filled out + mWheelCentreOffsets[i]=PxVec3(0,0,0); //Must be filled out + + mTireRestLoads[i]=20.0f + 1500.0f; + mRecipTireRestLoads[i]=1.0f/mTireRestLoads[i]; + } +} + +bool PxVehicleWheels4SimData::isValid(const PxU32 id) const +{ + PX_ASSERT(id<4); + PX_CHECK_AND_RETURN_VAL(mSuspensions[id].isValid(), "Invalid PxVehicleSuspWheelTire4SimulationData.mSuspensions", false); + PX_CHECK_AND_RETURN_VAL(mWheels[id].isValid(), "Invalid PxVehicleSuspWheelTire4SimulationData.mWheels", false); + PX_CHECK_AND_RETURN_VAL(mTires[id].isValid(), "Invalid PxVehicleSuspWheelTire4SimulationData.mTires", false); + PX_CHECK_AND_RETURN_VAL(mSuspDownwardTravelDirections[id].magnitude()>=0.999f && mSuspDownwardTravelDirections[id].magnitude()<=1.001f, "Invalid PxVehicleSuspWheelTire4SimulationData.mSuspDownwardTravelDirections", false); + PX_CHECK_AND_RETURN_VAL(mSuspForceAppPointOffsets[id].magnitude()!=0.0f, "Invalid PxVehicleSuspWheelTire4SimulationData.mSuspForceAppPointOffsets.mSuspForceAppPointOffsets", false); + PX_CHECK_AND_RETURN_VAL(mTireForceAppPointOffsets[id].magnitude()!=0.0f, "Invalid PxVehicleSuspWheelTire4SimulationData.mTireForceAppPointOffsets.mTireForceAppPointOffsets", false); + PX_CHECK_AND_RETURN_VAL(mWheelCentreOffsets[id].magnitude()!=0.0f, "Invalid PxVehicleSuspWheelTire4SimulationData.mWheelCentreOffsets.mWheelCentreOffsets", false); + PX_CHECK_AND_RETURN_VAL(mTireRestLoads[id]>0.0f, "Invalid PxVehicleSuspWheelTire4SimulationData.mTireRestLoads", false); + PX_CHECK_AND_RETURN_VAL(PxAbs((1.0f/mTireRestLoads[id]) - mRecipTireRestLoads[id]) <= 0.001f, "Invalid PxVehicleSuspWheelTire4SimulationData.mRecipTireRestLoads", false); + PX_UNUSED(id); + return true; +} + +void PxVehicleWheels4SimData::setSuspensionData(const PxU32 id, const PxVehicleSuspensionData& susp) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal suspension id"); + PX_CHECK_AND_RETURN(susp.mSpringStrength>0, "Susp spring strength must be greater than zero"); + PX_CHECK_AND_RETURN(susp.mSpringDamperRate>=0, "Susp spring damper rate must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(susp.mMaxCompression>=0, "Susp max compression must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(susp.mMaxDroop>=0, "Susp max droop must be greater than or equal to zero"); + PX_CHECK_AND_RETURN(susp.mMaxDroop>0 || susp.mMaxCompression>0, "Either one of max droop or max compression must be greater than zero"); + PX_CHECK_AND_RETURN(susp.mSprungMass>0, "Susp spring mass must be greater than zero"); + + mSuspensions[id]=susp; + mSuspensions[id].mRecipMaxCompression = 1.0f/((susp.mMaxCompression > 0.0f) ? susp.mMaxCompression : 1.0f); + mSuspensions[id].mRecipMaxDroop = 1.0f/((susp.mMaxDroop > 0.0f) ? susp.mMaxDroop : 1.0f); + + mTireRestLoads[id]=mWheels[id].mMass+mSuspensions[id].mSprungMass; + mRecipTireRestLoads[id]=1.0f/mTireRestLoads[id]; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setWheelData(const PxU32 id, const PxVehicleWheelData& wheel) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal wheel id"); + PX_CHECK_AND_RETURN(wheel.mRadius>0, "Wheel radius must be greater than zero"); + PX_CHECK_AND_RETURN(wheel.mMaxBrakeTorque>=0, "Wheel brake torque must be zero or be a positive value"); + PX_CHECK_AND_RETURN(wheel.mMaxHandBrakeTorque>=0, "Wheel handbrake torque must be zero or be a positive value"); + PX_CHECK_AND_RETURN(PxAbs(wheel.mMaxSteer)0, "Wheel mass must be greater than zero"); + PX_CHECK_AND_RETURN(wheel.mMOI>0, "Wheel moi must be greater than zero"); + PX_CHECK_AND_RETURN(wheel.mToeAngle>-PxHalfPi && wheel.mToeAngle0, "Wheel width must be greater than zero"); + PX_CHECK_AND_RETURN(wheel.mDampingRate>=0, "Wheel damping rate must be greater than or equal to zero"); + + mWheels[id]=wheel; + mWheels[id].mRecipRadius=1.0f/mWheels[id].mRadius; + mWheels[id].mRecipMOI=1.0f/mWheels[id].mMOI; + + mTireRestLoads[id]=mWheels[id].mMass+mSuspensions[id].mSprungMass; + mRecipTireRestLoads[id]=1.0f/mTireRestLoads[id]; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setTireData(const PxU32 id, const PxVehicleTireData& tire) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal tire id"); + PX_CHECK_AND_RETURN(tire.mLatStiffX>0, "Tire mLatStiffX must greater than zero"); + PX_CHECK_AND_RETURN(tire.mLatStiffY>0, "Tire mLatStiffY must greater than zero"); + PX_CHECK_AND_RETURN(tire.mLongitudinalStiffnessPerUnitGravity>0, "Tire longitudinal stiffness must greater than zero"); + PX_CHECK_AND_RETURN(tire.mCamberStiffnessPerUnitGravity>=0, "Tire camber stiffness must greater than or equal to zero"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[0][0]==0, "mFrictionVsSlipGraph[0][0] must be zero"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[0][1]>0, "mFrictionVsSlipGraph[0][0] must be greater than zero"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[1][0]>0, "mFrictionVsSlipGraph[1][0] must be greater than zero"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[1][1]>=tire.mFrictionVsSlipGraph[0][1], "mFrictionVsSlipGraph[1][1] must be greater than mFrictionVsSlipGraph[0][1]"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[2][0]> tire.mFrictionVsSlipGraph[1][0], "mFrictionVsSlipGraph[2][0] must be greater than mFrictionVsSlipGraph[1][0]"); + PX_CHECK_AND_RETURN(tire.mFrictionVsSlipGraph[2][1]<=tire.mFrictionVsSlipGraph[1][1], "mFrictionVsSlipGraph[2][1] must be less than or equal to mFrictionVsSlipGraph[1][1]"); + + mTires[id]=tire; + mTires[id].mRecipLongitudinalStiffnessPerUnitGravity=1.0f/mTires[id].mLongitudinalStiffnessPerUnitGravity; + mTires[id].mFrictionVsSlipGraphRecipx1Minusx0=1.0f/(mTires[id].mFrictionVsSlipGraph[1][0]-mTires[id].mFrictionVsSlipGraph[0][0]); + mTires[id].mFrictionVsSlipGraphRecipx2Minusx1=1.0f/(mTires[id].mFrictionVsSlipGraph[2][0]-mTires[id].mFrictionVsSlipGraph[1][0]); +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setSuspTravelDirection(const PxU32 id, const PxVec3& dir) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal suspension id"); + PX_CHECK_AND_RETURN(dir.magnitude()>0.999f && dir.magnitude()<1.0001f, "Suspension travel dir must be unit vector"); + + mSuspDownwardTravelDirections[id]=dir; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setSuspForceAppPointOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal suspension id"); + PX_CHECK_AND_RETURN(offset.magnitude()>0, "Susp force app point must be offset from centre of mass"); + + mSuspForceAppPointOffsets[id]=offset; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setTireForceAppPointOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal tire id"); + PX_CHECK_AND_RETURN(offset.magnitude()>0, "Tire force app point must be offset from centre of mass"); + + mTireForceAppPointOffsets[id]=offset; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setWheelCentreOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal wheel id"); + PX_CHECK_AND_RETURN(offset.magnitude()>0, "Tire force app point must be offset from centre of mass"); + + mWheelCentreOffsets[id]=offset; +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setWheelShapeMapping(const PxU32 id, const PxI32 shapeId) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal wheel id"); + PX_CHECK_AND_RETURN((-1==shapeId) || (PxU32(shapeId) < PX_MAX_U8), "Illegal shapeId: must be -1 or less than PX_MAX_U8"); + mWheelShapeMap[id] = Ps::to8(-1!=shapeId ? shapeId : PX_MAX_U8); +} + +///////////////////////////// + +void PxVehicleWheels4SimData::setSceneQueryFilterData(const PxU32 id, const PxFilterData& sqFilterData) +{ + PX_CHECK_AND_RETURN(id<4, "Illegal wheel id"); + mSqFilterData[id]=sqFilterData; +} + + +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h new file mode 100644 index 000000000..776bb345d --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h @@ -0,0 +1,393 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_SUSPWHEELTIRE_H +#define PX_VEHICLE_SUSPWHEELTIRE_H +/** \addtogroup vehicle + @{ +*/ + +#include "foundation/PxSimpleTypes.h" +#include "foundation/PxVec3.h" +#include "foundation/PxVec4.h" +#include "foundation/PxTransform.h" +#include "foundation/PxIO.h" +#include "geometry/PxGeometryHelpers.h" +#include "PxVehicleComponents.h" +#include "PxBatchQueryDesc.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxVehicleConstraintShader; +class PxMaterial; +class PxShape; + + + +class PxVehicleWheels4SimData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + PxVehicleWheels4SimData(); + + bool isValid(const PxU32 id) const; + + static void getBinaryMetaData(PxOutputStream& stream); + +public: + + PX_FORCE_INLINE const PxVehicleSuspensionData& getSuspensionData(const PxU32 id) const {return mSuspensions[id];} + PX_FORCE_INLINE const PxVehicleWheelData& getWheelData(const PxU32 id) const {return mWheels[id];} + PX_FORCE_INLINE const PxVehicleTireData& getTireData(const PxU32 id) const {return mTires[id];} + PX_FORCE_INLINE const PxVec3& getSuspTravelDirection(const PxU32 id) const {return mSuspDownwardTravelDirections[id];} + PX_FORCE_INLINE const PxVec3& getSuspForceAppPointOffset(const PxU32 id) const {return mSuspForceAppPointOffsets[id];} + PX_FORCE_INLINE const PxVec3& getTireForceAppPointOffset(const PxU32 id) const {return mTireForceAppPointOffsets[id];} + PX_FORCE_INLINE const PxVec3& getWheelCentreOffset(const PxU32 id) const {return mWheelCentreOffsets[id];} + PX_FORCE_INLINE PxI32 getWheelShapeMapping(const PxU32 id) const {return (PX_MAX_U8 != mWheelShapeMap[id]) ? mWheelShapeMap[id] : -1;} + PX_FORCE_INLINE const PxFilterData& getSceneQueryFilterData(const PxU32 id) const {return mSqFilterData[id];} + PX_FORCE_INLINE const PxReal* getTireRestLoadsArray() const {return mTireRestLoads;} + PX_FORCE_INLINE const PxReal* getRecipTireRestLoadsArray() const {return mRecipTireRestLoads;} + + void setSuspensionData (const PxU32 id, const PxVehicleSuspensionData& susp); + void setWheelData (const PxU32 id, const PxVehicleWheelData& susp); + void setTireData (const PxU32 id, const PxVehicleTireData& tire); + void setSuspTravelDirection (const PxU32 id, const PxVec3& dir); + void setSuspForceAppPointOffset (const PxU32 id, const PxVec3& offset); + void setTireForceAppPointOffset (const PxU32 id, const PxVec3& offset); + void setWheelCentreOffset (const PxU32 id, const PxVec3& offset); + void setWheelShapeMapping (const PxU32 id, const PxI32 shapeId); + void setSceneQueryFilterData (const PxU32 id, const PxFilterData& sqFilterData); + +private: + + /** + \brief Suspension simulation data + @see setSuspensionData, getSuspensionData + */ + PxVehicleSuspensionData mSuspensions[4]; + + /** + \brief Wheel simulation data + @see setWheelData, getWheelData + */ + PxVehicleWheelData mWheels[4]; + + /** + \brief Tire simulation data + @see setTireData, getTireData + */ + PxVehicleTireData mTires[4]; + + /** + \brief Direction of suspension travel, pointing downwards. + */ + PxVec3 mSuspDownwardTravelDirections[4]; + + /** + \brief Application point of suspension force specified as an offset from the rigid body centre of mass. + */ + PxVec3 mSuspForceAppPointOffsets[4]; //Offset from cm + + /** + \brief Application point of tire forces specified as an offset from the rigid body centre of mass. + */ + PxVec3 mTireForceAppPointOffsets[4]; //Offset from cm + + /** + \brief Position of wheel center specified as an offset from the rigid body centre of mass. + */ + PxVec3 mWheelCentreOffsets[4]; //Offset from cm + + /** + \brief Normalized tire load on each tire (load/rest load) at zero suspension jounce under gravity. + */ + PxReal mTireRestLoads[4]; + + /** + \brief Reciprocal normalized tire load on each tire at zero suspension jounce under gravity. + */ + PxReal mRecipTireRestLoads[4]; + + /** + \brief Scene query filter data used by each suspension line. + Anything relating to the actor belongs in PxVehicleWheels. + */ + PxFilterData mSqFilterData[4]; + + /** + \brief Mapping between wheel id and shape id. + The PxShape that corresponds to the ith wheel can be found with + If mWheelShapeMap[i]<0 then the wheel has no corresponding shape. + Otherwise, the shape corresponds to: + PxShape* shapeBuffer[1]; + mActor->getShapes(shapeBuffer,1,mWheelShapeMap[i]); + Anything relating to the actor belongs in PxVehicleWheels. + */ + PxU8 mWheelShapeMap[4]; + + PxU32 mPad[3]; +}; +PX_COMPILE_TIME_ASSERT(0 == (sizeof(PxVehicleWheels4SimData) & 15)); + +class PxVehicleWheels4DynData +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + + friend class PxVehicleUpdate; + + PxVehicleWheels4DynData() + : mRaycastResults(NULL), + mSweepResults(NULL) + { + setToRestState(); + } + ~PxVehicleWheels4DynData() + { + } + + bool isValid() const {return true;} + + static void getBinaryMetaData(PxOutputStream& stream); + + void setToRestState() + { + for(PxU32 i=0;i<4;i++) + { + mWheelSpeeds[i]=0.0f; + mCorrectedWheelSpeeds[i]=0.0f; + mWheelRotationAngles[i]=0.0f; + mTireLowForwardSpeedTimers[i]=0.0f; + mTireLowSideSpeedTimers[i]=0.0f; + mJounces[i] = PX_MAX_F32; + } + PxMemZero(mQueryOrCachedHitResults, sizeof(SuspLineSweep)); + + mRaycastResults = NULL; + mSweepResults = NULL; + mHasCachedRaycastHitPlane = false; + } + + void setInternalDynamicsToZero() + { + for(PxU32 i=0;i<4;i++) + { + mWheelSpeeds[i] = 0.0f; + mCorrectedWheelSpeeds[i] = 0.0f; + mJounces[i] = PX_MAX_F32; //Ensure that the jounce speed is zero when the car wakes up again. + } + } + + /** + \brief Rotation speeds of wheels + @see PxVehicle4WSetToRestState, PxVehicle4WGetWheelRotationSpeed, PxVehicle4WGetEngineRotationSpeed + */ + PxReal mWheelSpeeds[4]; + + /** + \brief Rotation speeds of wheels used to update the wheel rotation angles. + */ + PxReal mCorrectedWheelSpeeds[4]; + + /** + \brief Reported rotation angle about rolling axis. + @see PxVehicle4WSetToRestState, PxVehicle4WGetWheelRotationAngle + */ + PxReal mWheelRotationAngles[4]; + + /** + \brief Timers used to trigger sticky friction to hold the car perfectly at rest. + \note Used only internally. + */ + PxReal mTireLowForwardSpeedTimers[4]; + + /** + \brief Timers used to trigger sticky friction to hold the car perfectly at rest. + \note Used only internally. + */ + PxReal mTireLowSideSpeedTimers[4]; + + /** + \brief Previous suspension jounce. + \note Used only internally to compute the jounce speed by comparing cached jounce and latest jounce. + */ + PxReal mJounces[4]; + + struct SuspLineSweep + { + /** + \brief Geometry suspension line sweep used in most recent scene query. + @see PxVehicleSuspensionSweeps + */ + PxGeometryHolder mGometries[4]; + + /** + \brief Start poses of suspension line sweep used in most recent scene query. + @see PxVehicleSuspensionSweeps + */ + PxTransform mStartPose[4]; + + /** + \brief Directions of suspension line sweeps used in most recent scene query. + @see PxVehicleSuspensionSweeps + */ + PxVec3 mDirs[4]; + + /** + \brief Lengths of suspension line sweeps used in most recent scene query. + @see PxVehicleSuspensionSweeps + */ + PxReal mLengths[4]; + }; + + struct SuspLineRaycast + { + /** + \brief Start point of suspension line raycasts used in most recent scene query. + @see PxVehicleSuspensionRaycasts + */ + PxVec3 mStarts[4]; + + /** + \brief Directions of suspension line raycasts used in most recent scene query. + @see PxVehicleSuspensionRaycasts + */ + PxVec3 mDirs[4]; + + /** + \brief Lengths of suspension line raycasts used in most recent scene query. + @see PxVehicleSuspensionRaycasts + */ + PxReal mLengths[4]; + + PxU32 mPad[16]; + }; + + struct CachedSuspLineSceneQuerytHitResult + { + /** + \brief Cached raycast hit planes. These are the planes found from the last scene queries. + @see PxVehicleSuspensionRaycasts, PxVehicleSuspensionSweeps + */ + PxVec4 mPlanes[4]; + + /** + \brief Cached friction. + @see PxVehicleSuspensionRaycasts, PxVehicleSuspensionSweeps + */ + PxF32 mFrictionMultipliers[4]; + + /** + \brief Cached raycast hit distance. These are the hit distances found from the last scene queries. + */ + PxF32 mDistances[4]; + + /** + \brief Cached raycast hit counts. These are the hit counts found from the last scene queries. + @see PxVehicleSuspensionRaycasts, , PxVehicleSuspensionSweeps + */ + PxU16 mCounts[4]; + + /** + \brief Store 0 if cached results are from raycasts, store 1 if cached results are from sweeps. + */ + PxU16 mQueryTypes[4]; + + PxU32 mPad1[16]; + }; + + /** + \brief We either have a fresh raycast that was just performed or a cached raycast result that will be used if no raycast was just performed. + */ + PxU8 mQueryOrCachedHitResults[sizeof(SuspLineSweep)]; + + /** + \brief Used only internally. + */ + void setVehicleConstraintShader(PxVehicleConstraintShader* shader) {mVehicleConstraints=shader;} + PxVehicleConstraintShader& getVehicletConstraintShader() const {return *mVehicleConstraints;} + +private: + + //Susp limits and sticky tire friction for all wheels. + PxVehicleConstraintShader* mVehicleConstraints; + +public: + + /** + \brief Set by PxVehicleSuspensionRaycasts + @see PxVehicleSuspensionRaycasts + */ + const PxRaycastQueryResult* mRaycastResults; + + /** + \brief Set by PxVehicleSuspensionSweeps + @see PxVehicleSuspensionSweeps + */ + const PxSweepQueryResult* mSweepResults; + + /** + \brief Set true if a raycast hit plane has been recorded and cached. + This requires a raycast to be performed and then followed by PxVehicleUpdates + at least once. Reset to false in setToRestState. + */ + bool mHasCachedRaycastHitPlane; + + +#if PX_P64_FAMILY + PxU32 mPad[12]; +#endif +}; +PX_COMPILE_TIME_ASSERT(0==(sizeof(PxVehicleWheels4DynData) & 15)); +PX_COMPILE_TIME_ASSERT((0 == (sizeof(PxVehicleWheels4DynData::SuspLineSweep) & 0x0f))); +PX_COMPILE_TIME_ASSERT(sizeof(PxVehicleWheels4DynData::SuspLineRaycast) <= sizeof(PxVehicleWheels4DynData::SuspLineSweep)); +PX_COMPILE_TIME_ASSERT(sizeof(PxVehicleWheels4DynData::CachedSuspLineSceneQuerytHitResult) <= sizeof(PxVehicleWheels4DynData::SuspLineSweep)); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif //PX_VEHICLE_SUSPWHEELTIRE_H diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp new file mode 100644 index 000000000..57a5df790 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "PxVehicleTireFriction.h" +#include "CmPhysXCommon.h" +#include "PsFoundation.h" + +namespace physx +{ + +PX_FORCE_INLINE PxU32 computeByteSize(const PxU32 maxNbTireTypes, const PxU32 maxNbSurfaceTypes) +{ + PxU32 byteSize = ((sizeof(PxU32)*(maxNbTireTypes*maxNbSurfaceTypes) + 15) & ~15); + byteSize += ((sizeof(PxMaterial*)*maxNbSurfaceTypes + 15) & ~15); + byteSize += ((sizeof(PxVehicleDrivableSurfaceType)*maxNbSurfaceTypes + 15) & ~15); + byteSize += ((sizeof(PxVehicleDrivableSurfaceToTireFrictionPairs) + 15) & ~ 15); + return byteSize; +} + +PxVehicleDrivableSurfaceToTireFrictionPairs* PxVehicleDrivableSurfaceToTireFrictionPairs::allocate +(const PxU32 maxNbTireTypes, const PxU32 maxNbSurfaceTypes) +{ + PX_CHECK_AND_RETURN_VAL(maxNbSurfaceTypes <= eMAX_NB_SURFACE_TYPES, "maxNbSurfaceTypes must be less than eMAX_NB_SURFACE_TYPES", NULL); + + PxU32 byteSize = computeByteSize(maxNbTireTypes, maxNbSurfaceTypes); + PxU8* ptr = static_cast(PX_ALLOC(byteSize, "PxVehicleDrivableSurfaceToTireFrictionPairs")); + PxMemSet(ptr, 0, byteSize); + PxVehicleDrivableSurfaceToTireFrictionPairs* pairs = reinterpret_cast(ptr); + + pairs->mPairs = NULL; + pairs->mDrivableSurfaceMaterials = NULL; + pairs->mDrivableSurfaceTypes = NULL; + pairs->mNbTireTypes = 0; + pairs->mMaxNbTireTypes = maxNbTireTypes; + pairs->mNbSurfaceTypes = 0; + pairs->mMaxNbSurfaceTypes = maxNbSurfaceTypes; + + return pairs; +} + +void PxVehicleDrivableSurfaceToTireFrictionPairs::setup +(const PxU32 numTireTypes, const PxU32 numSurfaceTypes, const PxMaterial** drivableSurfaceMaterials, const PxVehicleDrivableSurfaceType* drivableSurfaceTypes) +{ + PX_CHECK_AND_RETURN(numTireTypes <= mMaxNbTireTypes, "numTireTypes must be less than mMaxNumSurfaceTypes"); + PX_CHECK_AND_RETURN(numSurfaceTypes <= mMaxNbSurfaceTypes, "numSurfaceTypes must be less than mMaxNumSurfaceTypes"); + + PxU8* ptr = reinterpret_cast(this); + + const PxU32 maxNbTireTypes = mMaxNbTireTypes; + const PxU32 maxNbSurfaceTypes = mMaxNbSurfaceTypes; + PxU32 byteSize = computeByteSize(mMaxNbTireTypes, mMaxNbSurfaceTypes); + PxMemSet(ptr, 0, byteSize); + mMaxNbTireTypes = maxNbTireTypes; + mMaxNbSurfaceTypes = maxNbSurfaceTypes; + + PxVehicleDrivableSurfaceToTireFrictionPairs* pairs = reinterpret_cast(ptr); + ptr += ((sizeof(PxVehicleDrivableSurfaceToTireFrictionPairs) + 15) & ~ 15); + + mPairs = reinterpret_cast(ptr); + ptr += ((sizeof(PxU32)*(numTireTypes*numSurfaceTypes) + 15) & ~15); + mDrivableSurfaceMaterials = reinterpret_cast(ptr); + ptr += ((sizeof(PxMaterial*)*numSurfaceTypes + 15) & ~15); + mDrivableSurfaceTypes = reinterpret_cast(ptr); + ptr += ((sizeof(PxVehicleDrivableSurfaceType)*numSurfaceTypes +15) & ~15); + + for(PxU32 i=0;imNbTireTypes=numTireTypes; + pairs->mNbSurfaceTypes=numSurfaceTypes; +} + +void PxVehicleDrivableSurfaceToTireFrictionPairs::release() +{ + PX_FREE(this); +} + +void PxVehicleDrivableSurfaceToTireFrictionPairs::setTypePairFriction(const PxU32 surfaceType, const PxU32 tireType, const PxReal value) +{ + PX_CHECK_AND_RETURN(tireType 0.0f && pointRejectAngle < PxPi, "PxVehicleSetSweepHitRejectionAngles - pointRejectAngle must be in range (0, Pi)"); + PX_CHECK_AND_RETURN(normalRejectAngle > 0.0f && normalRejectAngle < PxPi, "PxVehicleSetSweepHitRejectionAngles - normalRejectAngle must be in range (0, Pi)"); + gPointRejectAngleThreshold = PxCos(pointRejectAngle); + gNormalRejectAngleThreshold = PxCos(normalRejectAngle); +} + +//////////////////////////////////////////////////////////////////////////// +//Implementation of public api function PxVehicleSetSweepHitRejectionAngles +//////////////////////////////////////////////////////////////////////////// + +const PxF32 gMaxHitActorAccelerationDefault = PX_MAX_REAL; +PxF32 gMaxHitActorAcceleration; + +void PxVehicleSetMaxHitActorAcceleration(const PxF32 maxHitActorAcceleration) +{ + PX_CHECK_AND_RETURN(maxHitActorAcceleration >= 0.0f, "PxVehicleSetMaxHitActorAcceleration - maxHitActorAcceleration must be greater than or equal to zero"); + gMaxHitActorAcceleration = maxHitActorAcceleration; +} + +//////////////////////////////////////////////////////////////////////////// +//Set all defaults from PxVehicleInitSDK +//////////////////////////////////////////////////////////////////////////// + +void setVehicleDefaults() +{ + gRight = gRightDefault; + gUp = gUpDefault; + gForward = gForwardDefault; + + gApplyForces = gApplyForcesDefault; + + gPointRejectAngleThreshold = gPointRejectAngleThresholdDefault; + gNormalRejectAngleThreshold = gNormalRejectAngleThresholdDefault; + + gMaxHitActorAcceleration = gMaxHitActorAccelerationDefault; +} + +//////////////////////////////////////////////////////////////////////////// +//Called from PxVehicleInitSDK/PxCloseVehicleSDK +//////////////////////////////////////////////////////////////////////////// + +//*********************** + +PxF32 gThresholdForwardSpeedForWheelAngleIntegration=0; +PxF32 gRecipThresholdForwardSpeedForWheelAngleIntegration=0; +PxF32 gMinLatSpeedForTireModel=0; +PxF32 gStickyTireFrictionThresholdSpeed=0; +PxF32 gToleranceScaleLength=0; +PxF32 gMinimumSlipThreshold=0; + +void setVehicleToleranceScale(const PxTolerancesScale& ts) +{ + gThresholdForwardSpeedForWheelAngleIntegration=5.0f*ts.length; + gRecipThresholdForwardSpeedForWheelAngleIntegration=1.0f/gThresholdForwardSpeedForWheelAngleIntegration; + + gMinLatSpeedForTireModel=1.0f*ts.length; + + gStickyTireFrictionThresholdSpeed=0.2f*ts.length; + + gToleranceScaleLength=ts.length; + + gMinimumSlipThreshold = 1e-5f; +} + +void resetVehicleToleranceScale() +{ + gThresholdForwardSpeedForWheelAngleIntegration=0; + gRecipThresholdForwardSpeedForWheelAngleIntegration=0; + + gMinLatSpeedForTireModel=0; + + gStickyTireFrictionThresholdSpeed=0; + + gToleranceScaleLength=0; + + gMinimumSlipThreshold=0; +} + +//*********************** + +const PxSerializationRegistry* gSerializationRegistry=NULL; + +void setSerializationRegistryPtr(const PxSerializationRegistry* sr) +{ + gSerializationRegistry = sr; +} + +const PxSerializationRegistry* resetSerializationRegistryPtr() +{ + const PxSerializationRegistry* tmp = gSerializationRegistry; + gSerializationRegistry = NULL; + return tmp; +} + +//////////////////////////////////////////////////////////////////////////// +//Global values used to trigger and control sticky tire friction constraints. +//////////////////////////////////////////////////////////////////////////// + +const PxF32 gStickyTireFrictionForwardDamping=0.01f; +const PxF32 gStickyTireFrictionSideDamping=0.1f; +const PxF32 gLowForwardSpeedThresholdTime=1.0f; +const PxF32 gLowSideSpeedThresholdTime=1.0f; + +//////////////////////////////////////////////////////////////////////////// +//Global values used to control max iteration count if estimate mode is chosen +//////////////////////////////////////////////////////////////////////////// + +const PxF32 gSolverTolerance = 1e-10f; + +//////////////////////////////////////////////////////////////////////////// +//Compute the sprung masses that satisfy the centre of mass and sprung mass coords. +//////////////////////////////////////////////////////////////////////////// + +#define DETERMINANT_THRESHOLD (1e-6f) + +void computeSprungMasses(const PxU32 numSprungMasses, const PxVec3* sprungMassCoordinates, const PxVec3& centreOfMass, const PxReal totalMass, const PxU32 gravityDirection, PxReal* sprungMasses) +{ +#if PX_CHECKED + PX_CHECK_AND_RETURN(numSprungMasses > 0, "PxVehicleComputeSprungMasses: numSprungMasses must be greater than zero"); + PX_CHECK_AND_RETURN(numSprungMasses <= PX_MAX_NB_WHEELS, "PxVehicleComputeSprungMasses: numSprungMasses must be less than or equal to 20"); + for(PxU32 i=0;i 0.0f, "PxVehicleComputeSprungMasses: totalMass must be greater than zero"); + PX_CHECK_AND_RETURN(gravityDirection<=2, "PxVehicleComputeSprungMasses: gravityDirection must be 0 or 1 or 2"); + PX_CHECK_AND_RETURN(sprungMasses, "PxVehicleComputeSprungMasses: sprungMasses must be a non-null pointer"); +#endif + + if(1==numSprungMasses) + { + sprungMasses[0]=totalMass; + } + else if(2==numSprungMasses) + { + PxVec3 v=sprungMassCoordinates[0]; + v[gravityDirection]=0; + PxVec3 w=sprungMassCoordinates[1]-sprungMassCoordinates[0]; + w[gravityDirection]=0; + w.normalize(); + + PxVec3 cm=centreOfMass; + cm[gravityDirection]=0; + PxF32 t=w.dot(cm-v); + PxVec3 p=v+w*t; + + PxVec3 x0=sprungMassCoordinates[0]; + x0[gravityDirection]=0; + PxVec3 x1=sprungMassCoordinates[1]; + x1[gravityDirection]=0; + const PxF32 r0=(x0-p).dot(w); + const PxF32 r1=(x1-p).dot(w); + + PX_CHECK_AND_RETURN(PxAbs(r0-r1) > DETERMINANT_THRESHOLD, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); + + const PxF32 m0=totalMass*r1/(r1-r0); + const PxF32 m1=totalMass-m0; + + sprungMasses[0]=m0; + sprungMasses[1]=m1; + } + else if(3==numSprungMasses) + { + const PxU32 d0=(gravityDirection+1)%3; + const PxU32 d1=(gravityDirection+2)%3; + + MatrixNN A(3); + VectorN b(3); + A.set(0,0,sprungMassCoordinates[0][d0]); + A.set(0,1,sprungMassCoordinates[1][d0]); + A.set(0,2,sprungMassCoordinates[2][d0]); + A.set(1,0,sprungMassCoordinates[0][d1]); + A.set(1,1,sprungMassCoordinates[1][d1]); + A.set(1,2,sprungMassCoordinates[2][d1]); + A.set(2,0,1.f); + A.set(2,1,1.f); + A.set(2,2,1.f); + b[0]=totalMass*centreOfMass[d0]; + b[1]=totalMass*centreOfMass[d1]; + b[2]=totalMass; + + VectorN result(3); + MatrixNNLUSolver solver; + solver.decomposeLU(A); + PX_CHECK_AND_RETURN(PxAbs(solver.getDet()) > DETERMINANT_THRESHOLD, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); + solver.solve(b,result); + + sprungMasses[0]=result[0]; + sprungMasses[1]=result[1]; + sprungMasses[2]=result[2]; + } + else if(numSprungMasses>=4) + { + const PxU32 d0=(gravityDirection+1)%3; + const PxU32 d1=(gravityDirection+2)%3; + + const PxF32 mbar = totalMass/(numSprungMasses*1.0f); + + //See http://en.wikipedia.org/wiki/Lagrange_multiplier + //particularly the section on multiple constraints. + + //3 Constraint equations. + //g0 = sum_ xi*mi=xcm + //g1 = sum_ zi*mi=zcm + //g2 = sum_ mi = totalMass + //Minimisation function to achieve solution with minimum mass variance. + //f = sum_ (mi - mave)^2 + //Lagrange terms (N equations, N+3 unknowns) + //2*mi - xi*lambda0 - zi*lambda1 - 1*lambda2 = 2*mave + + MatrixNN A(numSprungMasses+3); + VectorN b(numSprungMasses+3); + + //g0, g1, g2 + for(PxU32 i=0;i DETERMINANT_THRESHOLD, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); + + for(PxU32 i=0;i= 0, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); + cm += sprungMassCoordinates[i]*sprungMasses[i]; + m += sprungMasses[i]; + } + cm *= (1.0f/totalMass); + PX_CHECK_AND_RETURN((PxAbs(totalMass - m)/totalMass) < 1e-3f, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); + PxVec3 diff = cm - centreOfMass; + diff[gravityDirection]=0; + const PxF32 diffMag = diff.magnitude(); + PX_CHECK_AND_RETURN(numSprungMasses <=2 || diffMag < 1e-3f, "PxVehicleComputeSprungMasses: Unable to determine sprung masses. Please check the values in sprungMassCoordinates."); +#endif +} + +//////////////////////////////////////////////////////////////////////////// +//Work out if all wheels are in the air. +//////////////////////////////////////////////////////////////////////////// + +bool PxVehicleIsInAir(const PxVehicleWheelQueryResult& vehWheelQueryResults) +{ + if(!vehWheelQueryResults.wheelQueryResults) + { + return true; + } + + for(PxU32 i=0;igetGlobalPose(); + vehWheelTransform = contactModifyPair.transform[0]; + isOtherDynamic = contactModifyPair.actor[1] && contactModifyPair.actor[1]->is(); + } + else + { + PX_ASSERT(contactModifyPair.actor[1] == vehActor); + normalMultiplier = -1.0f; + targetVelMultiplier = -1.0f; + vehActorTransform = contactModifyPair.actor[1]->getGlobalPose(); + vehWheelTransform = contactModifyPair.transform[1]; + isOtherDynamic = contactModifyPair.actor[0] && contactModifyPair.actor[0]->is(); + } + + //Compute the "right" vector of the transform. + const PxVec3 right = vehWheelTransform.q.rotate(gRight); + + //The wheel transform includes rotation about the rolling axis. + //We want to compute the wheel transform at zero wheel rotation angle. + PxTransform correctedWheelShapeTransform; + { + const PxF32 wheelRotationAngle = vehicle.mWheelsDynData.getWheelRotationAngle(wheelId); + const PxQuat wheelRotateQuat(-wheelRotationAngle, right); + correctedWheelShapeTransform = PxTransform(vehWheelTransform.p, wheelRotateQuat*vehWheelTransform.q); + } + + //Construct a plane for the wheel + //n.p + d = 0 + PxPlane wheelPlane; + wheelPlane.n = right; + wheelPlane.d = -(right.dot(correctedWheelShapeTransform.p)); + + //Compute the suspension travel vector. + const PxVec3 suspTravelDir = correctedWheelShapeTransform.rotate(vehicle.mWheelsSimData.getSuspTravelDirection(wheelId)); + + //Get the wheel centre. + const PxVec3 wheelCentre = correctedWheelShapeTransform.p; + + //Test each point. + PxContactSet& contactSet = contactModifyPair.contacts; + const PxU32 numContacts = contactSet.size(); + for(PxU32 i = 0; i < numContacts; i++) + { + //Get the next contact point to analyse. + const PxVec3& contactPoint = contactSet.getPoint(i); + bool ignorePoint = false; + + //Project the contact point on to the wheel plane. + const PxF32 distanceToPlane = wheelPlane.n.dot(contactPoint) + wheelPlane.d; + const PxVec3 contactPointOnPlane = contactPoint - wheelPlane.n*distanceToPlane; + + //Construct a vector from the wheel centre to the contact point on the plane. + PxVec3 dir = contactPointOnPlane - wheelCentre; + const PxF32 effectiveRadius = dir.normalize(); + + if(!ignorePoint && rejectPoints) + { + //Work out the dot product of the suspension direction and the direction from wheel centre to contact point. + const PxF32 dotProduct = dir.dot(suspTravelDir); + if (dotProduct > gPointRejectAngleThreshold) + { + ignorePoint = true; + numIgnoredContacts++; + contactSet.ignore(i); + } + } + + //Ignore contact normals that are near the raycast direction. + if(!ignorePoint && rejectNormals) + { + const PxVec3& contactNormal = contactSet.getNormal(i) * normalMultiplier; + const PxF32 dotProduct = -(contactNormal.dot(suspTravelDir)); + if(dotProduct > gNormalRejectAngleThreshold) + { + ignorePoint = true; + numIgnoredContacts++; + contactSet.ignore(i); + } + } + + //For points that remain we want to modify the contact speed to account for the spinning wheel. + //We also want the applied impulse to go through the suspension geometry so we set the contact point to be the suspension force + //application point. + if(!ignorePoint) + { + //Compute the tangent velocity. + //Retain only the component that lies perpendicular to the contact normal. + const PxF32 wheelRotationSpeed = vehicle.mWheelsDynData.getWheelRotationSpeed(wheelId); + const PxVec3 tangentVelocityDir = right.cross(dir); + PxVec3 tangentVelocity = tangentVelocityDir*(effectiveRadius*wheelRotationSpeed); + tangentVelocity -= contactSet.getNormal(i)*(tangentVelocity.dot(contactSet.getNormal(i))); + + //We want to add velocity in the opposite direction to the tangent velocity. + const PxVec3 targetTangentVelocity = -wheelTangentVelocityMultiplier*tangentVelocity; + + //Relative velocity is computed from actor0 - actor1 + //If vehicle is actor 0 we want to add to the target velocity: targetVelocity = [(vel0 + targetTangentVelocity) - vel1] = vel0 - vel1 + targetTangentVelocity + //If vehicle is actor 1 we want to subtract from the target velocity: targetVelocity = [vel0 - (vel1 + targetTangentVelocity)] = vel0 - vel1 - targetTangentVelocity + const PxVec3 targetVelocity = targetTangentVelocity*targetVelMultiplier; + + //Add the target velocity. + contactSet.setTargetVelocity(i, targetVelocity); + + //Set the max impulse that can be applied. + //Only apply this if the wheel has hit a dynamic. + if (isOtherDynamic) + { + contactSet.setMaxImpulse(i, maxImpulse); + } + + //Set the contact point to be the suspension force application point because all forces applied through the wheel go through the suspension geometry. + const PxVec3 suspAppPoint = vehActorTransform.transform(vehActor->getCMassLocalPose().p + vehicle.mWheelsSimData.getSuspForceAppPointOffset(wheelId)); + contactSet.setPoint(i, suspAppPoint); + } + } + + return numIgnoredContacts; +} + +//////////////////////////////////////////////////////////////////////////// +//Enable a 4W vehicle in either tadpole or delta configuration. +//////////////////////////////////////////////////////////////////////////// + +void computeDirection(PxU32& rightDirection, PxU32& upDirection) +{ + //Work out the up and right vectors. + rightDirection = 0xffffffff; + if(gRight == PxVec3(1.f,0,0) || gRight == PxVec3(-1.f,0,0)) + { + rightDirection = 0; + } + else if(gRight == PxVec3(0,1.f,0) || gRight == PxVec3(0,-1.f,0)) + { + rightDirection = 1; + } + else if(gRight == PxVec3(0,0,1.f) || gRight == PxVec3(0,0,-1.f)) + { + rightDirection = 2; + } + upDirection = 0xffffffff; + if(gUp== PxVec3(1.f,0,0) || gUp == PxVec3(-1.f,0,0)) + { + upDirection = 0; + } + else if(gUp == PxVec3(0,1.f,0) || gUp == PxVec3(0,-1.f,0)) + { + upDirection = 1; + } + else if(gUp == PxVec3(0,0,1.f) || gUp == PxVec3(0,0,-1.f)) + { + upDirection = 2; + } +} + +void enable3WMode(const PxU32 rightDirection, const PxU32 upDirection, const bool removeFrontWheel, PxVehicleWheelsSimData& wheelsSimData, PxVehicleWheelsDynData& wheelsDynData, PxVehicleDriveSimData4W& driveSimData) +{ + PX_ASSERT(rightDirection < 3); + PX_ASSERT(upDirection < 3); + + const PxU32 wheelToRemove = removeFrontWheel ? PxVehicleDrive4WWheelOrder::eFRONT_LEFT : PxVehicleDrive4WWheelOrder::eREAR_LEFT; + const PxU32 wheelToModify = removeFrontWheel ? PxVehicleDrive4WWheelOrder::eFRONT_RIGHT : PxVehicleDrive4WWheelOrder::eREAR_RIGHT; + + //Disable the wheel. + wheelsSimData.disableWheel(wheelToRemove); + + //Make sure the wheel's corresponding PxShape does not get posed again. + wheelsSimData.setWheelShapeMapping(wheelToRemove, -1); + + //Set the angular speed to 0.0f + wheelsDynData.setWheelRotationSpeed(wheelToRemove, 0.0f); + + //Disable Ackermann steering. + //If the front wheel is to be removed and the front wheels can steer then disable Ackermann correction. + //If the rear wheel is to be removed and the rear wheels can steer then disable Ackermann correction. + if(removeFrontWheel && + (wheelsSimData.getWheelData(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT).mMaxSteer!=0.0f || + wheelsSimData.getWheelData(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).mMaxSteer!=0.0f)) + { + PxVehicleAckermannGeometryData ackermannData=driveSimData.getAckermannGeometryData(); + ackermannData.mAccuracy=0.0f; + driveSimData.setAckermannGeometryData(ackermannData); + } + if(!removeFrontWheel && + (wheelsSimData.getWheelData(PxVehicleDrive4WWheelOrder::eREAR_RIGHT).mMaxSteer!=0.0f || + wheelsSimData.getWheelData(PxVehicleDrive4WWheelOrder::eREAR_LEFT).mMaxSteer!=0.0f)) + { + PxVehicleAckermannGeometryData ackermannData=driveSimData.getAckermannGeometryData(); + ackermannData.mAccuracy=0.0f; + driveSimData.setAckermannGeometryData(ackermannData); + } + + //We need to set up the differential to make sure that no drive torque is delivered to the disabled wheel. + PxVehicleDifferential4WData diffData =driveSimData.getDiffData(); + if(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT==wheelToModify) + { + diffData.mFrontBias=PX_MAX_F32; + diffData.mFrontLeftRightSplit=0.0f; + } + else + { + diffData.mRearBias=PX_MAX_F32; + diffData.mRearLeftRightSplit=0.0f; + } + driveSimData.setDiffData(diffData); + + //Now reposition the disabled wheel so that it lies at the center of its axle. + //The following assumes that the front and rear axles lie along the x-axis. + { + PxVec3 wheelCentreOffset=wheelsSimData.getWheelCentreOffset(wheelToModify); + wheelCentreOffset[rightDirection]=0.0f; + wheelsSimData.setWheelCentreOffset(wheelToModify,wheelCentreOffset); + + PxVec3 suspOffset=wheelsSimData.getSuspForceAppPointOffset(wheelToModify); + suspOffset[rightDirection]=0; + wheelsSimData.setSuspForceAppPointOffset(wheelToModify,suspOffset); + + PxVec3 tireOffset=wheelsSimData.getTireForceAppPointOffset(wheelToModify); + tireOffset[rightDirection]=0; + wheelsSimData.setTireForceAppPointOffset(wheelToModify,tireOffset); + } + + //Redistribute the mass supported by 4 wheels among the 3 remaining enabled wheels. + //Compute the total mass supported by all 4 wheels. + const PxF32 totalMass = + wheelsSimData.getSuspensionData(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).mSprungMass + + wheelsSimData.getSuspensionData(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT).mSprungMass + + wheelsSimData.getSuspensionData(PxVehicleDrive4WWheelOrder::eREAR_LEFT).mSprungMass + + wheelsSimData.getSuspensionData(PxVehicleDrive4WWheelOrder::eREAR_RIGHT).mSprungMass; + //Get the wheel cm offsets of the 3 enabled wheels. + PxVec3 cmOffsets[3]= + { + wheelsSimData.getWheelCentreOffset((wheelToRemove+1) % 4), + wheelsSimData.getWheelCentreOffset((wheelToRemove+2) % 4), + wheelsSimData.getWheelCentreOffset((wheelToRemove+3) % 4), + }; + //Re-compute the sprung masses. + PxF32 sprungMasses[3]; + computeSprungMasses(3, cmOffsets, PxVec3(0,0,0), totalMass, upDirection, sprungMasses); + + //Now set the new sprung masses. + //Do this in a way that preserves the natural frequency and damping ratio of the spring. + for(PxU32 i=0;i<3;i++) + { + PxVehicleSuspensionData suspData = wheelsSimData.getSuspensionData((wheelToRemove+1+i) % 4); + + const PxF32 oldSprungMass = suspData.mSprungMass; + const PxF32 oldStrength = suspData.mSpringStrength; + const PxF32 oldDampingRate = suspData.mSpringDamperRate; + const PxF32 oldNaturalFrequency = PxSqrt(oldStrength/oldSprungMass); + + const PxF32 newSprungMass = sprungMasses[i]; + const PxF32 newStrength = oldNaturalFrequency*oldNaturalFrequency*newSprungMass; + const PxF32 newDampingRate = oldDampingRate; + + suspData.mSprungMass = newSprungMass; + suspData.mSpringStrength = newStrength; + suspData.mSpringDamperRate = newDampingRate; + wheelsSimData.setSuspensionData((wheelToRemove+1+i) % 4, suspData); + } +} + + +//////////////////////////////////////////////////////////////////////////// +//Maximum number of allowed blocks of 4 wheels +//////////////////////////////////////////////////////////////////////////// + +#define PX_MAX_NB_SUSPWHEELTIRE4 (PX_MAX_NB_WHEELS >>2) + + +//////////////////////////////////////////////////////////////////////////// +//Pointers to telemetry data. +//Set to NULL if no telemetry data is to be recorded during a vehicle update. +//Functions used throughout vehicle update to record specific vehicle data. +//////////////////////////////////////////////////////////////////////////// + +#if PX_DEBUG_VEHICLE_ON + +//Render data. +PxVec3* gCarTireForceAppPoints=NULL; +PxVec3* gCarSuspForceAppPoints=NULL; + +//Graph data +PxF32* gCarWheelGraphData[PX_MAX_NB_WHEELS]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; +PxF32* gCarEngineGraphData=NULL; + +PX_FORCE_INLINE void updateGraphDataInternalWheelDynamics(const PxU32 startIndex, const PxF32* carWheelSpeeds) +{ + //Grab the internal rotation speeds for graphing before we update them. + if(gCarWheelGraphData[startIndex]) + { + for(PxU32 i=0;i<4;i++) + { + PX_ASSERT((startIndex+i) < PX_MAX_NB_WHEELS); + PX_ASSERT(gCarWheelGraphData[startIndex+i]); + gCarWheelGraphData[startIndex+i][PxVehicleWheelGraphChannel::eWHEEL_OMEGA]=carWheelSpeeds[i]; + } + } +} + +PX_FORCE_INLINE void updateGraphDataInternalEngineDynamics(const PxF32 carEngineSpeed) +{ + if(gCarEngineGraphData) + gCarEngineGraphData[PxVehicleDriveGraphChannel::eENGINE_REVS]=carEngineSpeed; +} + +PX_FORCE_INLINE void updateGraphDataControlInputs(const PxF32 accel, const PxF32 brake, const PxF32 handbrake, const PxF32 steerLeft, const PxF32 steerRight) +{ + if(gCarEngineGraphData) + { + gCarEngineGraphData[PxVehicleDriveGraphChannel::eACCEL_CONTROL]=accel; + gCarEngineGraphData[PxVehicleDriveGraphChannel::eBRAKE_CONTROL]=brake; + gCarEngineGraphData[PxVehicleDriveGraphChannel::eHANDBRAKE_CONTROL]=handbrake; + gCarEngineGraphData[PxVehicleDriveGraphChannel::eSTEER_LEFT_CONTROL]=steerLeft; + gCarEngineGraphData[PxVehicleDriveGraphChannel::eSTEER_RIGHT_CONTROL]=steerRight; + } +} +PX_FORCE_INLINE void updateGraphDataGearRatio(const PxF32 G) +{ + if(gCarEngineGraphData) + gCarEngineGraphData[PxVehicleDriveGraphChannel::eGEAR_RATIO]=G; +} +PX_FORCE_INLINE void updateGraphDataEngineDriveTorque(const PxF32 engineDriveTorque) +{ + if(gCarEngineGraphData) + gCarEngineGraphData[PxVehicleDriveGraphChannel::eENGINE_DRIVE_TORQUE]=engineDriveTorque; +} +PX_FORCE_INLINE void updateGraphDataClutchSlip(const PxF32* wheelSpeeds, const PxF32* aveWheelSpeedContributions, const PxF32 engineSpeed, const PxF32 G) +{ + if(gCarEngineGraphData) + { + PxF32 averageWheelSpeed=0; + for(PxU32 i=0;i<4;i++) + { + averageWheelSpeed+=wheelSpeeds[i]*aveWheelSpeedContributions[i]; + } + averageWheelSpeed*=G; + gCarEngineGraphData[PxVehicleDriveGraphChannel::eCLUTCH_SLIP]=averageWheelSpeed-engineSpeed; + } +} +PX_FORCE_INLINE void updateGraphDataClutchSlipNW(const PxU32 numWheels4, const PxVehicleWheels4DynData* wheelsDynData, const PxF32* aveWheelSpeedContributions, const PxF32 engineSpeed, const PxF32 G) +{ + if(gCarEngineGraphData) + { + PxF32 averageWheelSpeed=0; + for(PxU32 i=0;i0) + { + //Compute the number of bits to right-shift that gives the maximum number of unique hashes. + //Keep searching until we find either a set of completely unique hashes or a peak count of unique hashes. + PxU32 prevShift=0; + PxU32 shift=2; + PxU32 prevNumUniqueHashes=0; + PxU32 currNumUniqueHashes=0; + while( ((currNumUniqueHashes=computeNumUniqueHashes(shift)) > prevNumUniqueHashes) && currNumUniqueHashes!=mNbEntries) + { + prevNumUniqueHashes=currNumUniqueHashes; + prevShift=shift; + shift = (shift << 1); + } + if(currNumUniqueHashes!=mNbEntries) + { + //Stopped searching because we have gone past the peak number of unqiue hashes. + mShift = prevShift; + } + else + { + //Stopped searching because we found a unique hash for each key. + mShift = shift; + } + + //Compute the hash values with the optimum shift. + for(PxU32 i=0;i> shift); + const uintptr_t hash = (ptr & (eHASH_SIZE-1)); + return PxU32(hash); + } + + PxU32 computeNumUniqueHashes(const PxU32 shift) const + { + PxU32 words[eHASH_SIZE >>5]; + PxU8* bitmapBuffer[sizeof(BitMap)]; + BitMap* bitmap=reinterpret_cast(bitmapBuffer); + bitmap->setWords(words, eHASH_SIZE >>5); + + PxU32 numUniqueHashes=0; + PxMemZero(words, sizeof(PxU32)*(eHASH_SIZE >>5)); + for(PxU32 i=0;itest(hash)) + { + bitmap->set(hash); + numUniqueHashes++; + } + } + return numUniqueHashes; + } + + enum + { + eHASH_SIZE=PxVehicleDrivableSurfaceToTireFrictionPairs::eMAX_NB_SURFACE_TYPES + }; + PxU32 mHeadIds[eHASH_SIZE]; + enum + { + eMAX_NB_KEYS=PxVehicleDrivableSurfaceToTireFrictionPairs::eMAX_NB_SURFACE_TYPES + }; + PxU32 mNextIds[eMAX_NB_KEYS]; + + PxU32 mShift; +}; + + +//////////////////////////////////////////////////////////////////////////// +//Compute the suspension line raycast start point and direction. +//////////////////////////////////////////////////////////////////////////// + +PX_INLINE void computeSuspensionRaycast +(const PxTransform& carChassisTrnsfm, const PxVec3& bodySpaceWheelCentreOffset, const PxVec3& bodySpaceSuspTravelDir, const PxF32 radius, const PxF32 maxBounce, + PxVec3& suspLineStart, PxVec3& suspLineDir) +{ + //Direction of raycast. + suspLineDir=carChassisTrnsfm.rotate(bodySpaceSuspTravelDir); + + //Position at top of wheel at maximum compression. + suspLineStart=carChassisTrnsfm.transform(bodySpaceWheelCentreOffset); + suspLineStart-=suspLineDir*(radius+maxBounce); +} + +PX_INLINE void computeSuspensionSweep +(const PxTransform& carChassisTrnsfm, + const PxQuat& wheelLocalPoseRotation, const PxF32 wheelTheta, + const PxVec3 bodySpaceWheelCentreOffset, const PxVec3& bodySpaceSuspTravelDir, const PxF32 radius, const PxF32 maxBounce, + PxTransform& suspStartPose, PxVec3& suspLineDir) +{ + //Direction of raycast. + suspLineDir=carChassisTrnsfm.rotate(bodySpaceSuspTravelDir); + + //Position of wheel at maximum compression. + suspStartPose.p = carChassisTrnsfm.transform(bodySpaceWheelCentreOffset); + suspStartPose.p -= suspLineDir*(radius + maxBounce); + + //Rotation of wheel. + const PxVec3 right = wheelLocalPoseRotation.rotate(gRight); + const PxQuat negativeRotation(-wheelTheta, right); + suspStartPose.q = carChassisTrnsfm.q*(negativeRotation*wheelLocalPoseRotation); +} + + +//////////////////////////////////////////////////////////////////////////// +//Functions used to integrate rigid body transform and velocity. +//The sub-step system divides a specified timestep into N equal sub-steps +//and integrates the velocity and transform each sub-step. +//After all sub-steps are complete the velocity required to move the +//associated PxRigidBody from the start transform to the transform at the end +//of the timestep is computed and set. If the update mode is chosen to be +//acceleration then the acceleration is computed/set that will move the rigid body +//from the start to end transform. The PxRigidBody never actually has its transform +//updated and only has its velocity or acceleration set at the very end of the timestep. +//////////////////////////////////////////////////////////////////////////// + +PX_INLINE void integrateBody +(const PxF32 inverseMass, const PxVec3& invInertia, const PxVec3& force, const PxVec3& torque, const PxF32 dt, + PxVec3& v, PxVec3& w, PxTransform& t) +{ + //Integrate linear velocity. + v+=force*(inverseMass*dt); + + //Integrate angular velocity. + PxMat33 inverseInertia; + transformInertiaTensor(invInertia, PxMat33(t.q), inverseInertia); + w+=inverseInertia*(torque*dt); + + //Integrate position. + t.p+=v*dt; + + //Integrate quaternion. + PxQuat wq(w.x,w.y,w.z,0.0f); + PxQuat q=t.q; + PxQuat qdot=wq*q*(dt*0.5f); + q+=qdot; + q.normalize(); + t.q=q; +} + +/* +PX_INLINE void computeVelocity(const PxTransform& t1, const PxTransform& t2, const PxF32 invDT, PxVec3& v, PxVec3& w) +{ + //Linear velocity. + v = (t2.p - t1.p)*invDT; + + //Angular velocity. + PxQuat qw = (t2.q - t1.q)*t1.q.getConjugate()*(2.0f*invDT); + w.x=qw.x; + w.y=qw.y; + w.z=qw.z; +} +*/ + + +///////////////////////////////////////////////////////////////////////////////////////// +//Use fsel to compute the sign of a float: +1 for positive values, -1 for negative values +///////////////////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE PxF32 computeSign(const PxF32 f) +{ + return physx::intrinsics::fsel(f, physx::intrinsics::fsel(-f, 0.0f, 1.0f), -1.0f); +} + + +///////////////////////////////////////////////////////////////////////////////////////// +//Get the accel/brake/handbrake as floats in range (0,1) from the inputs stored in the vehicle. +//Equivalent for tank involving thrustleft/thrustright and brakeleft/brakeright. +///////////////////////////////////////////////////////////////////////////////////////// + + +PX_FORCE_INLINE void getVehicle4WControlValues(const PxVehicleDriveDynData& driveDynData, PxF32& accel, PxF32& brake, PxF32& handbrake, PxF32& steerLeft, PxF32& steerRight) +{ + accel=driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]; + brake=driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE]; + handbrake=driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE]; + steerLeft=driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT]; + steerRight=driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]; +} + +PX_FORCE_INLINE void getVehicleNWControlValues(const PxVehicleDriveDynData& driveDynData, PxF32& accel, PxF32& brake, PxF32& handbrake, PxF32& steerLeft, PxF32& steerRight) +{ + accel=driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_ACCEL]; + brake=driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_BRAKE]; + handbrake=driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_HANDBRAKE]; + steerLeft=driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_LEFT]; + steerRight=driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_RIGHT]; +} + +PX_FORCE_INLINE void getTankControlValues(const PxVehicleDriveDynData& driveDynData, PxF32& accel, PxF32& brakeLeft, PxF32& brakeRight, PxF32& thrustLeft, PxF32& thrustRight) +{ + accel=driveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL]; + brakeLeft=driveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT]; + brakeRight=driveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT]; + thrustLeft=driveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]; + thrustRight=driveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]; +} + + +//////////////////////////////////////////////////////////////////////////// +//Process autobox to initiate automatic gear changes. +//The autobox can be turned off and simulated externally by setting +//the target gear as required prior to calling PxVehicleUpdates. +//////////////////////////////////////////////////////////////////////////// + + +PX_FORCE_INLINE PxF32 processAutoBox(const PxU32 accelIndex, const PxF32 timestep, const PxVehicleDriveSimData& vehCoreSimData, PxVehicleDriveDynData& vehDynData) +{ + PX_ASSERT(vehDynData.getUseAutoGears()); + + PxF32 autoboxCompensatedAnalogAccel = vehDynData.mControlAnalogVals[accelIndex]; + + //If still undergoing a gear change triggered by the autobox + //then turn off the accelerator pedal. This happens in autoboxes + //to stop the driver revving the engine crazily then damaging the + //clutch when the clutch re-engages at the end of the gear change. + const PxU32 currentGear=vehDynData.getCurrentGear(); + const PxU32 targetGear=vehDynData.getTargetGear(); + if(targetGear!=currentGear && PxVehicleGearsData::eNEUTRAL==currentGear) + { + autoboxCompensatedAnalogAccel = 0; + } + + //Only process the autobox if no gear change is underway and the time passed since the last autobox + //gear change is greater than the autobox latency. + PxF32 autoBoxSwitchTime=vehDynData.getAutoBoxSwitchTime(); + const PxF32 autoBoxLatencyTime=vehCoreSimData.getAutoBoxData().mDownRatios[PxVehicleGearsData::eREVERSE]; + if(targetGear==currentGear && autoBoxSwitchTime > autoBoxLatencyTime) + { + //Work out if the autobox wants to switch up or down. + const PxF32 normalisedEngineOmega=vehDynData.getEngineRotationSpeed()*vehCoreSimData.getEngineData().getRecipMaxOmega(); + const PxVehicleAutoBoxData& autoBoxData=vehCoreSimData.getAutoBoxData(); + + bool gearUp=false; + if(normalisedEngineOmega > autoBoxData.mUpRatios[currentGear] && PxVehicleGearsData::eREVERSE!=currentGear) + { + //If revs too high and not in reverse and not undergoing a gear change then switch up. + gearUp=true; + } + + bool gearDown=false; + if(normalisedEngineOmega < autoBoxData.mDownRatios[currentGear] && currentGear > PxVehicleGearsData::eFIRST) + { + //If revs too low and in gear greater than first and not undergoing a gear change then change down. + gearDown=true; + } + + //Start the gear change and reset the time since the last autobox gear change. + if(gearUp || gearDown) + { + vehDynData.setGearUp(gearUp); + vehDynData.setGearDown(gearDown); + vehDynData.setAutoBoxSwitchTime(0.f); + } + } + else + { + autoBoxSwitchTime+=timestep; + vehDynData.setAutoBoxSwitchTime(autoBoxSwitchTime); + } + + return autoboxCompensatedAnalogAccel; +} + + +//////////////////////////////////////////////////////////////////////////// +//Process gear changes. +//If target gear not equal to current gear then a gear change needs to start. +//The gear change process begins by switching immediately in neutral and +//staying there for a specified time. The process ends by setting current +//gear equal to target gear when the gear switch time has passed. +//This can be bypassed by always forcing target gear = current gear and then +//externally managing gear changes prior to calling PxVehicleUpdates. +//////////////////////////////////////////////////////////////////////////// + +void processGears(const PxF32 timestep, const PxVehicleGearsData& gears, PxVehicleDriveDynData& car) +{ + //const PxVehicleGearsData& gears=car.mVehicleSimData.getGearsData(); + + //Process the gears. + if(car.getGearUp() && gears.mNbRatios-1!=car.getCurrentGear() && car.getCurrentGear()==car.getTargetGear()) + { + //Car wants to go up a gear and can go up a gear and not already undergoing a gear change. + if(PxVehicleGearsData::eREVERSE==car.getCurrentGear()) + { + //In reverse so switch to first through neutral. + car.setGearSwitchTime(0); + car.setTargetGear(PxVehicleGearsData::eFIRST); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + else if(PxVehicleGearsData::eNEUTRAL==car.getCurrentGear()) + { + //In neutral so switch to first and stay in neutral. + car.setGearSwitchTime(0); + car.setTargetGear(PxVehicleGearsData::eFIRST); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + else + { + //Switch up a gear through neutral. + car.setGearSwitchTime(0); + car.setTargetGear(car.getCurrentGear() + 1); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + } + if(car.getGearDown() && PxVehicleGearsData::eREVERSE!=car.getCurrentGear() && car.getCurrentGear()==car.getTargetGear()) + { + //Car wants to go down a gear and can go down a gear and not already undergoing a gear change + if(PxVehicleGearsData::eFIRST==car.getCurrentGear()) + { + //In first so switch to reverse through neutral. + car.setGearSwitchTime(0); + car.setTargetGear(PxVehicleGearsData::eREVERSE); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + else if(PxVehicleGearsData::eNEUTRAL==car.getCurrentGear()) + { + //In neutral so switch to reverse and stay in neutral. + car.setGearSwitchTime(0); + car.setTargetGear(PxVehicleGearsData::eREVERSE); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + else + { + //Switch down a gear through neutral. + car.setGearSwitchTime(0); + car.setTargetGear(car.getCurrentGear() - 1); + car.setCurrentGear(PxVehicleGearsData::eNEUTRAL); + } + } + if(car.getCurrentGear()!=car.getTargetGear()) + { + if(car.getGearSwitchTime()>gears.mSwitchTime) + { + car.setCurrentGear(car.getTargetGear()); + car.setGearSwitchTime(0); + car.setGearDown(false); + car.setGearUp(false); + } + else + { + car.setGearSwitchTime(car.getGearSwitchTime() + timestep); + } + } +} + +//////////////////////////////////////////////////////////////////////////// +//Helper functions to compute +//1. the gear ratio from the current gear. +//2. the drive torque from the state of the accelerator pedal and torque curve of available torque against engine speed. +//3. engine damping rate (a blend between a rate when not accelerating and a rate when fully accelerating). +//4. clutch strength (rate at which clutch will regulate difference between engine and averaged wheel speed). +//////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE PxF32 computeGearRatio(const PxVehicleGearsData& gearsData, const PxU32 currentGear) +{ + const PxF32 gearRatio=gearsData.mRatios[currentGear]*gearsData.mFinalRatio; + return gearRatio; +} + +PX_FORCE_INLINE PxF32 computeEngineDriveTorque(const PxVehicleEngineData& engineData, const PxF32 omega, const PxF32 accel) +{ + const PxF32 engineDriveTorque=accel*engineData.mPeakTorque*engineData.mTorqueCurve.getYVal(omega*engineData.getRecipMaxOmega()); + return engineDriveTorque; +} + +PX_FORCE_INLINE PxF32 computeEngineDampingRate(const PxVehicleEngineData& engineData, const PxU32 gear, const PxF32 accel) +{ + const PxF32 fullThrottleDamping = engineData.mDampingRateFullThrottle; + const PxF32 zeroThrottleDamping = (PxVehicleGearsData::eNEUTRAL!=gear) ? engineData.mDampingRateZeroThrottleClutchEngaged : engineData.mDampingRateZeroThrottleClutchDisengaged; + const PxF32 engineDamping = zeroThrottleDamping + (fullThrottleDamping-zeroThrottleDamping)*accel; + return engineDamping; +} + +PX_FORCE_INLINE PxF32 computeClutchStrength(const PxVehicleClutchData& clutchData, const PxU32 currentGear) +{ + return ((PxVehicleGearsData::eNEUTRAL!=currentGear) ? clutchData.mStrength : 0.0f); +} + + +//////////////////////////////////////////////////////////////////////////// +//Limited slip differential. +//Split the available drive torque as a fraction of the total between up to 4 driven wheels. +//Compute the fraction that each wheel contributes to the averages wheel speed at the clutch. +//////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE void splitTorque +(const PxF32 w1, const PxF32 w2, const PxF32 diffBias, const PxF32 defaultSplitRatio, + PxF32* t1, PxF32* t2) +{ + PX_ASSERT(computeSign(w1)==computeSign(w2) && 0.0f!=computeSign(w1)); + const PxF32 w1Abs=PxAbs(w1); + const PxF32 w2Abs=PxAbs(w2); + const PxF32 omegaMax=PxMax(w1Abs,w2Abs); + const PxF32 omegaMin=PxMin(w1Abs,w2Abs); + const PxF32 delta=omegaMax-diffBias*omegaMin; + const PxF32 deltaTorque=physx::intrinsics::fsel(delta, delta/omegaMax , 0.0f); + const PxF32 f1=physx::intrinsics::fsel(w1Abs-w2Abs, defaultSplitRatio*(1.0f-deltaTorque), defaultSplitRatio*(1.0f+deltaTorque)); + const PxF32 f2=physx::intrinsics::fsel(w1Abs-w2Abs, (1.0f-defaultSplitRatio)*(1.0f+deltaTorque), (1.0f-defaultSplitRatio)*(1.0f-deltaTorque)); + const PxF32 denom=1.0f/(f1+f2); + *t1=f1*denom; + *t2=f2*denom; + PX_ASSERT((*t1 + *t2) >=0.999f && (*t1 + *t2) <=1.001f); +} + +PX_FORCE_INLINE void computeDiffTorqueRatios +(const PxVehicleDifferential4WData& diffData, const PxF32 handbrake, const PxF32* PX_RESTRICT wheelOmegas, PxF32* PX_RESTRICT diffTorqueRatios) +{ + //If the handbrake is on only deliver torque to the front wheels. + PxU32 type=diffData.mType; + if(handbrake>0) + { + switch(diffData.mType) + { + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD: + type=PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD: + type=PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD: + case PxVehicleDifferential4WData::eMAX_NB_DIFF_TYPES: + break; + } + } + + const PxF32 wfl=wheelOmegas[PxVehicleDrive4WWheelOrder::eFRONT_LEFT]; + const PxF32 wfr=wheelOmegas[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT]; + const PxF32 wrl=wheelOmegas[PxVehicleDrive4WWheelOrder::eREAR_LEFT]; + const PxF32 wrr=wheelOmegas[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]; + + const PxF32 centreBias=diffData.mCentreBias; + const PxF32 frontBias=diffData.mFrontBias; + const PxF32 rearBias=diffData.mRearBias; + + const PxF32 frontRearSplit=diffData.mFrontRearSplit; + const PxF32 frontLeftRightSplit=diffData.mFrontLeftRightSplit; + const PxF32 rearLeftRightSplit=diffData.mRearLeftRightSplit; + + const PxF32 oneMinusFrontRearSplit=1.0f-diffData.mFrontRearSplit; + const PxF32 oneMinusFrontLeftRightSplit=1.0f-diffData.mFrontLeftRightSplit; + const PxF32 oneMinusRearLeftRightSplit=1.0f-diffData.mRearLeftRightSplit; + + const PxF32 swfl=computeSign(wfl); + + //Split a torque of 1 between front and rear. + //Then split that torque between left and right. + PxF32 torqueFrontLeft=0; + PxF32 torqueFrontRight=0; + PxF32 torqueRearLeft=0; + PxF32 torqueRearRight=0; + switch(type) + { + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD: + if(0.0f!=swfl && swfl==computeSign(wfr) && swfl==computeSign(wrl) && swfl==computeSign(wrr)) + { + PxF32 torqueFront,torqueRear; + const PxF32 omegaFront=PxAbs(wfl+wfr); + const PxF32 omegaRear=PxAbs(wrl+wrr); + splitTorque(omegaFront,omegaRear,centreBias,frontRearSplit,&torqueFront,&torqueRear); + splitTorque(wfl,wfr,frontBias,frontLeftRightSplit,&torqueFrontLeft,&torqueFrontRight); + splitTorque(wrl,wrr,rearBias,rearLeftRightSplit,&torqueRearLeft,&torqueRearRight); + torqueFrontLeft*=torqueFront; + torqueFrontRight*=torqueFront; + torqueRearLeft*=torqueRear; + torqueRearRight*=torqueRear; + } + else + { + //TODO: need to handle this case. + torqueFrontLeft=frontRearSplit*frontLeftRightSplit; + torqueFrontRight=frontRearSplit*oneMinusFrontLeftRightSplit; + torqueRearLeft=oneMinusFrontRearSplit*rearLeftRightSplit; + torqueRearRight=oneMinusFrontRearSplit*oneMinusRearLeftRightSplit; + } + break; + + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD: + if(0.0f!=swfl && swfl==computeSign(wfr)) + { + splitTorque(wfl,wfr,frontBias,frontLeftRightSplit,&torqueFrontLeft,&torqueFrontRight); + } + else + { + torqueFrontLeft=frontLeftRightSplit; + torqueFrontRight=oneMinusFrontLeftRightSplit; + } + break; + + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD: + + if(0.0f!=computeSign(wrl) && computeSign(wrl)==computeSign(wrr)) + { + splitTorque(wrl,wrr,rearBias,rearLeftRightSplit,&torqueRearLeft,&torqueRearRight); + } + else + { + torqueRearLeft=rearLeftRightSplit; + torqueRearRight=oneMinusRearLeftRightSplit; + } + break; + + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD: + torqueFrontLeft=frontRearSplit*frontLeftRightSplit; + torqueFrontRight=frontRearSplit*oneMinusFrontLeftRightSplit; + torqueRearLeft=oneMinusFrontRearSplit*rearLeftRightSplit; + torqueRearRight=oneMinusFrontRearSplit*oneMinusRearLeftRightSplit; + break; + + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD: + torqueFrontLeft=frontLeftRightSplit; + torqueFrontRight=oneMinusFrontLeftRightSplit; + break; + + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD: + torqueRearLeft=rearLeftRightSplit; + torqueRearRight=oneMinusRearLeftRightSplit; + break; + + default: + PX_ASSERT(false); + break; + } + + diffTorqueRatios[PxVehicleDrive4WWheelOrder::eFRONT_LEFT]=torqueFrontLeft; + diffTorqueRatios[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT]=torqueFrontRight; + diffTorqueRatios[PxVehicleDrive4WWheelOrder::eREAR_LEFT]=torqueRearLeft; + diffTorqueRatios[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]=torqueRearRight; + + PX_ASSERT(((torqueFrontLeft+torqueFrontRight+torqueRearLeft+torqueRearRight) >= 0.999f) && ((torqueFrontLeft+torqueFrontRight+torqueRearLeft+torqueRearRight) <= 1.001f)); +} + +PX_FORCE_INLINE void computeDiffAveWheelSpeedContributions +(const PxVehicleDifferential4WData& diffData, const PxF32 handbrake, PxF32* PX_RESTRICT diffAveWheelSpeedContributions) +{ + PxU32 type=diffData.mType; + + //If the handbrake is on only deliver torque to the front wheels. + if(handbrake>0) + { + switch(diffData.mType) + { + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD: + type=PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD: + type=PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD: + case PxVehicleDifferential4WData::eMAX_NB_DIFF_TYPES: + break; + } + } + + const PxF32 frontRearSplit=diffData.mFrontRearSplit; + const PxF32 frontLeftRightSplit=diffData.mFrontLeftRightSplit; + const PxF32 rearLeftRightSplit=diffData.mRearLeftRightSplit; + + const PxF32 oneMinusFrontRearSplit=1.0f-diffData.mFrontRearSplit; + const PxF32 oneMinusFrontLeftRightSplit=1.0f-diffData.mFrontLeftRightSplit; + const PxF32 oneMinusRearLeftRightSplit=1.0f-diffData.mRearLeftRightSplit; + + switch(type) + { + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD: + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_LEFT]=frontRearSplit*frontLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT]=frontRearSplit*oneMinusFrontLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_LEFT]=oneMinusFrontRearSplit*rearLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]=oneMinusFrontRearSplit*oneMinusRearLeftRightSplit; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD: + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_LEFT]=frontLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT]=oneMinusFrontLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_LEFT]=0.0f; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]=0.0f; + break; + case PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD: + case PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD: + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_LEFT]=0.0f; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT]=0.0f; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_LEFT]=rearLeftRightSplit; + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]=oneMinusRearLeftRightSplit; + break; + default: + PX_ASSERT(false); + break; + } + + PX_ASSERT((diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_LEFT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_LEFT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]) >= 0.999f && + (diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_LEFT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_LEFT] + + diffAveWheelSpeedContributions[PxVehicleDrive4WWheelOrder::eREAR_RIGHT]) <= 1.001f); +} + + +/////////////////////////////////////////////////////// +//Tank differential +/////////////////////////////////////////////////////// + +void computeTankDiff +(const PxF32 thrustLeft, const PxF32 thrustRight, + const PxU32 numActiveWheels, const bool* activeWheelStates, + PxF32* aveWheelSpeedContributions, PxF32* diffTorqueRatios, PxF32* wheelGearings) +{ + const PxF32 thrustLeftAbs=PxAbs(thrustLeft); + const PxF32 thrustRightAbs=PxAbs(thrustRight); + + //Work out now many left wheels are enabled. + PxF32 numLeftWheels=0.0f; + for(PxU32 i=0;i 0 ? 1.0f/numLeftWheels : 0.0f; + + //Work out now many right wheels are enabled. + PxF32 numRightWheels=0.0f; + for(PxU32 i=1;i 0 ? 1.0f/numRightWheels : 0.0f; + + //Split the diff torque between left and right. + PxF32 diffTorqueRatioLeft=0.5f; + PxF32 diffTorqueRatioRight=0.5f; + if((thrustLeftAbs + thrustRightAbs) > 1e-3f) + { + const PxF32 thrustDiff = 0.5f*(thrustLeftAbs - thrustRightAbs)/(thrustLeftAbs + thrustRightAbs); + diffTorqueRatioLeft += thrustDiff; + diffTorqueRatioRight -= thrustDiff; + + } + diffTorqueRatioLeft *= invNumEnabledWheelsLeft; + diffTorqueRatioRight *= invNumEnabledWheelsRight; + + //Compute the per wheel gearing. + PxF32 wheelGearingLeft=1.0f; + PxF32 wheelGearingRight=1.0f; + if((thrustLeftAbs + thrustRightAbs) > 1e-3f) + { + wheelGearingLeft=computeSign(thrustLeft); + wheelGearingRight=computeSign(thrustRight); + } + + //Compute the contribution of each wheel to the average speed at the clutch. + const PxF32 aveWheelSpeedContributionLeft = 0.5f*invNumEnabledWheelsLeft; + const PxF32 aveWheelSpeedContributionRight = 0.5f*invNumEnabledWheelsRight; + + //Set all the left wheels. + for(PxU32 i=0;i=-1.01f && steer<=1.01f); + PX_ASSERT(steerGain> 5); + + if(bm.test(startId + 0)) + { + activeStates[0]=true; + } + if(bm.test(startId + 1)) + { + activeStates[1]=true; + } + if(bm.test(startId + 2)) + { + activeStates[2]=true; + } + if(bm.test(startId + 3)) + { + activeStates[3]=true; + } +} + + +//////////////////////////////////////////////////////////////////////////// +//Compute the brake and handbrake torques for different vehicle types. +//Also compute a boolean for each tire to know if the brake is applied or not. +//Can't use a single function for all types because not all vehicle types have +//handbrakes and the brake control mechanism is different for different vehicle +//types. +//////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE void computeNoDriveBrakeTorques +(const PxVehicleWheelData* PX_RESTRICT wheelDatas, const PxF32* PX_RESTRICT wheelOmegas, const PxF32* PX_RESTRICT rawBrakeTroques, + PxF32* PX_RESTRICT brakeTorques, bool* PX_RESTRICT isBrakeApplied) +{ + PX_UNUSED(wheelDatas); + + const PxF32 sign0=computeSign(wheelOmegas[0]); + brakeTorques[0]=(-sign0*rawBrakeTroques[0]); + isBrakeApplied[0]=(rawBrakeTroques[0]!=0); + + const PxF32 sign1=computeSign(wheelOmegas[1]); + brakeTorques[1]=(-sign1*rawBrakeTroques[1]); + isBrakeApplied[1]=(rawBrakeTroques[1]!=0); + + const PxF32 sign2=computeSign(wheelOmegas[2]); + brakeTorques[2]=(-sign2*rawBrakeTroques[2]); + isBrakeApplied[2]=(rawBrakeTroques[2]!=0); + + const PxF32 sign3=computeSign(wheelOmegas[3]); + brakeTorques[3]=(-sign3*rawBrakeTroques[3]); + isBrakeApplied[3]=(rawBrakeTroques[3]!=0); +} + +PX_FORCE_INLINE void computeBrakeAndHandBrakeTorques +(const PxVehicleWheelData* PX_RESTRICT wheelDatas, const PxF32* PX_RESTRICT wheelOmegas, const PxF32 brake, const PxF32 handbrake, + PxF32* PX_RESTRICT brakeTorques, bool* isBrakeApplied) +{ + //At zero speed offer no brake torque allowed. + + const PxF32 sign0=computeSign(wheelOmegas[0]); + brakeTorques[0]=(-brake*sign0*wheelDatas[0].mMaxBrakeTorque-handbrake*sign0*wheelDatas[0].mMaxHandBrakeTorque); + isBrakeApplied[0]=((brake*wheelDatas[0].mMaxBrakeTorque+handbrake*wheelDatas[0].mMaxHandBrakeTorque)!=0); + + const PxF32 sign1=computeSign(wheelOmegas[1]); + brakeTorques[1]=(-brake*sign1*wheelDatas[1].mMaxBrakeTorque-handbrake*sign1*wheelDatas[1].mMaxHandBrakeTorque); + isBrakeApplied[1]=((brake*wheelDatas[1].mMaxBrakeTorque+handbrake*wheelDatas[1].mMaxHandBrakeTorque)!=0); + + const PxF32 sign2=computeSign(wheelOmegas[2]); + brakeTorques[2]=(-brake*sign2*wheelDatas[2].mMaxBrakeTorque-handbrake*sign2*wheelDatas[2].mMaxHandBrakeTorque); + isBrakeApplied[2]=((brake*wheelDatas[2].mMaxBrakeTorque+handbrake*wheelDatas[2].mMaxHandBrakeTorque)!=0); + + const PxF32 sign3=computeSign(wheelOmegas[3]); + brakeTorques[3]=(-brake*sign3*wheelDatas[3].mMaxBrakeTorque-handbrake*sign3*wheelDatas[3].mMaxHandBrakeTorque); + isBrakeApplied[3]=((brake*wheelDatas[3].mMaxBrakeTorque+handbrake*wheelDatas[3].mMaxHandBrakeTorque)!=0); +} + +PX_FORCE_INLINE void computeTankBrakeTorques +(const PxVehicleWheelData* PX_RESTRICT wheelDatas, const PxF32* PX_RESTRICT wheelOmegas, const PxF32 brakeLeft, const PxF32 brakeRight, + PxF32* PX_RESTRICT brakeTorques, bool* isBrakeApplied) +{ + //At zero speed offer no brake torque allowed. + + const PxF32 sign0=computeSign(wheelOmegas[0]); + brakeTorques[0]=(-brakeLeft*sign0*wheelDatas[0].mMaxBrakeTorque); + isBrakeApplied[0]=((brakeLeft*wheelDatas[0].mMaxBrakeTorque)!=0); + + const PxF32 sign1=computeSign(wheelOmegas[1]); + brakeTorques[1]=(-brakeRight*sign1*wheelDatas[1].mMaxBrakeTorque); + isBrakeApplied[1]=((brakeRight*wheelDatas[1].mMaxBrakeTorque)!=0); + + const PxF32 sign2=computeSign(wheelOmegas[2]); + brakeTorques[2]=(-brakeLeft*sign2*wheelDatas[2].mMaxBrakeTorque); + isBrakeApplied[2]=((brakeLeft*wheelDatas[2].mMaxBrakeTorque)!=0); + + const PxF32 sign3=computeSign(wheelOmegas[3]); + brakeTorques[3]=(-brakeRight*sign3*wheelDatas[3].mMaxBrakeTorque); + isBrakeApplied[3]=((brakeRight*wheelDatas[3].mMaxBrakeTorque)!=0); +} + + +//////////////////////////////////////////////////////////////////////////// +//Functions to compute inputs to tire force calculation. +//1. Filter the normalised tire load to smooth any spikes in load. +//2. Compute the tire lat and long directions in the ground plane. +//3. Compute the tire lat and long slips. +//4. Compute the friction from a graph of friction vs slip. +//////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE PxF32 computeFilteredNormalisedTireLoad(const PxVehicleTireLoadFilterData& filterData, const PxF32 normalisedLoad) +{ + if(normalisedLoad <= filterData.mMinNormalisedLoad) + { + return filterData.mMinFilteredNormalisedLoad; + } + else if(normalisedLoad >= filterData.mMaxNormalisedLoad) + { + return filterData.mMaxFilteredNormalisedLoad; + } + else + { + const PxF32 x=normalisedLoad; + const PxF32 xmin=filterData.mMinNormalisedLoad; + const PxF32 ymin=filterData.mMinFilteredNormalisedLoad; + const PxF32 ymax=filterData.mMaxFilteredNormalisedLoad; + const PxF32 recipXmaxMinusXMin=filterData.getDenominator(); + return (ymin + (x-xmin)*(ymax-ymin)*recipXmaxMinusXMin); + } +} + +PX_FORCE_INLINE void computeTireDirs(const PxVec3& chassisLatDir, const PxVec3& hitNorm, const PxF32 wheelSteerAngle, PxVec3& tireLongDir, PxVec3& tireLatDir) +{ + PX_ASSERT(chassisLatDir.magnitude()>0.999f && chassisLatDir.magnitude()<1.001f); + PX_ASSERT(hitNorm.magnitude()>0.999f && hitNorm.magnitude()<1.001f); + + //Compute the tire axes in the ground plane. + PxVec3 tzRaw=chassisLatDir.cross(hitNorm); + PxVec3 txRaw=hitNorm.cross(tzRaw); + tzRaw.normalize(); + txRaw.normalize(); + //Rotate the tire using the steer angle. + const PxF32 cosWheelSteer=PxCos(wheelSteerAngle); + const PxF32 sinWheelSteer=PxSin(wheelSteerAngle); + const PxVec3 tz=tzRaw*cosWheelSteer + txRaw*sinWheelSteer; + const PxVec3 tx=txRaw*cosWheelSteer - tzRaw*sinWheelSteer; + tireLongDir=tz; + tireLatDir=tx; +} + +PX_FORCE_INLINE void computeTireSlips +(const PxF32 longSpeed, const PxF32 latSpeed, const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 maxDenominator, + const bool isAccelApplied, const bool isBrakeApplied, + const bool isTank, + PxF32& longSlip, PxF32& latSlip) +{ + PX_ASSERT(maxDenominator>=0.0f); + + const PxF32 longSpeedAbs=PxAbs(longSpeed); + const PxF32 wheelSpeed=wheelOmega*wheelRadius; + const PxF32 wheelSpeedAbs=PxAbs(wheelSpeed); + + //Lateral slip is easy. + latSlip = PxAtan(latSpeed/(longSpeedAbs+gMinLatSpeedForTireModel));//TODO: do we really use PxAbs(vz) as denominator? + + //If nothing is moving just avoid a divide by zero and set the long slip to zero. + if(longSpeed==0 && wheelOmega==0) + { + longSlip=0.0f; + return; + } + + //Longitudinal slip is a bit harder because we can end up wtih zero on the denominator. + if(isTank) + { + if(isBrakeApplied || isAccelApplied) + { + //Wheel experiencing an applied torque. + //Use the raw denominator value plus an offset to avoid anything approaching a divide by zero. + //When accelerating from rest the small denominator will generate really quite large + //slip values, which will, in turn, generate large longitudinal forces. With large + //time-steps this might lead to a temporary oscillation in longSlip direction and an + //oscillation in wheel speed direction. The amplitude of the oscillation should be low + //unless the timestep is really large. + //There's not really an obvious solution to this without setting the denominator offset higher + //(or decreasing the timestep). Setting the denominator higher affects handling everywhere so + //settling for a potential temporary oscillation is probably the least worst compromise. + //Note that we always use longSpeedAbs as denominator because in order to turn on the spot the + //tank needs to get strong longitudinal force when it isn't moving but the wheels are slipping. + longSlip = (wheelSpeed - longSpeed)/(longSpeedAbs + 0.1f*gToleranceScaleLength); + } + else + { + //Wheel not experiencing an applied torque. + //If the denominator becomes too small then the longSlip becomes large and the longitudinal force + //can overshoot zero at large timesteps. This can be really noticeable so it's harder to justify + //not taking action. Further, the car isn't being actually driven so there is a strong case to fiddle + //with the denominator because it doesn't really affect active handling. + //Don't let the denominator fall below a user-specified value. This can be tuned upwards until the + //oscillation in the sign of longSlip disappears. + longSlip = (wheelSpeed - longSpeed)/(PxMax(maxDenominator, PxMax(longSpeedAbs,wheelSpeedAbs))); + } + } + else + { + if(isBrakeApplied || isAccelApplied) + { + //Wheel experiencing an applied torque. + //Use the raw denominator value plus an offset to avoid anything approaching a divide by zero. + //When accelerating from rest the small denominator will generate really quite large + //slip values, which will, in turn, generate large longitudinal forces. With large + //time-steps this might lead to a temporary oscillation in longSlip direction and an + //oscillation in wheel speed direction. The amplitude of the oscillation should be low + //unless the timestep is really large. + //There's not really an obvious solution to this without setting the denominator offset higher + //(or decreasing the timestep). Setting the denominator higher affects handling everywhere so + //settling for a potential temporary oscillation is probably the least worst compromise. + longSlip = (wheelSpeed - longSpeed)/(PxMax(longSpeedAbs,wheelSpeedAbs)+0.1f*gToleranceScaleLength); + } + else + { + //Wheel not experiencing an applied torque. + //If the denominator becomes too small then the longSlip becomes large and the longitudinal force + //can overshoot zero at large timesteps. This can be really noticeable so it's harder to justify + //not taking action. Further, the car isn't being actually driven so there is a strong case to fiddle + //with the denominator because it doesn't really affect active handling. + //Don't let the denominator fall below a user-specified value. This can be tuned upwards until the + //oscillation in the sign of longSlip disappears. + longSlip = (wheelSpeed - longSpeed)/(PxMax(maxDenominator,PxMax(longSpeedAbs,wheelSpeedAbs))); + } + } +} + +PX_FORCE_INLINE void computeTireFriction(const PxVehicleTireData& tireData, const PxF32 longSlip, const PxF32 frictionMultiplier, PxF32& friction) +{ + const PxF32 x0=tireData.mFrictionVsSlipGraph[0][0]; + const PxF32 y0=tireData.mFrictionVsSlipGraph[0][1]; + const PxF32 x1=tireData.mFrictionVsSlipGraph[1][0]; + const PxF32 y1=tireData.mFrictionVsSlipGraph[1][1]; + const PxF32 x2=tireData.mFrictionVsSlipGraph[2][0]; + const PxF32 y2=tireData.mFrictionVsSlipGraph[2][1]; + const PxF32 recipx1Minusx0=tireData.getFrictionVsSlipGraphRecipx1Minusx0(); + const PxF32 recipx2Minusx1=tireData.getFrictionVsSlipGraphRecipx2Minusx1(); + const PxF32 longSlipAbs=PxAbs(longSlip); + PxF32 mu; + if(longSlipAbs=0); + friction=mu*frictionMultiplier; +} + +//////////////////////////////////////////////////////////////////////////// +//Sticky tire constraints. +//Increment a timer each update that a tire has a very low longitudinal speed. +//Activate a sticky constraint when the tire has had an unbroken low long speed +//for at least a threshold time. +//The longer the sticky constraint is active, the slower the target constraint speed +//along the long dir. Quickly tends towards zero. +//When the sticky constraint is activated set the long slip to zero and let +//the sticky constraint take over. +//////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE void updateLowForwardSpeedTimer +(const PxF32 longSpeed, const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 recipWheelRadius, const bool isIntentionToAccelerate, + const PxF32 timestep, PxF32& lowForwardSpeedTime) +{ + PX_UNUSED(wheelRadius); + PX_UNUSED(recipWheelRadius); + + //If the tire is rotating slowly and the forward speed is slow then increment the slow forward speed timer. + //If the intention of the driver is to accelerate the vehicle then reset the timer because the intention has been signalled NOT to bring + //the wheel to rest. + PxF32 longSpeedAbs=PxAbs(longSpeed); + if((longSpeedAbsgLowForwardSpeedThresholdTime) + { + stickyTireActiveFlag=true; + stickyTireTargetSpeed=longSpeed*gStickyTireFrictionForwardDamping; + } +} + +PX_FORCE_INLINE void activateStickyFrictionSideConstraint +(const PxF32 latSpeed, const PxF32 lowSpeedForwardTimer, const PxF32 lowSideSpeedTimer, const bool isIntentionToAccelerate, + bool& stickyTireActiveFlag, PxF32& stickyTireTargetSpeed) +{ + PX_UNUSED(latSpeed); + PX_UNUSED(isIntentionToAccelerate); + + //Setup the sticky friction constraint to bring the vehicle to rest at the tire contact point. + //Only do this if we can guarantee that the intention is to bring the car to rest (no accel pedal applied). + //Smoothly reduce error to zero to avoid bringing car immediately to rest. This avoids graphical glitchiness. + //We're going to replace the lateral tire force with the sticky friction so set the lat slip to zero to ensure zero lat force. + //Apply sticky friction to this tire if + //(1) the low forward speed timer is > 0. + //(2) the accumulated time of low forward speed is greater than a threshold. + stickyTireActiveFlag=false; + stickyTireTargetSpeed=0.0f; + if((lowSpeedForwardTimer > 0) && lowSideSpeedTimer>gLowSideSpeedThresholdTime) + { + stickyTireActiveFlag=true; + stickyTireTargetSpeed=latSpeed*gStickyTireFrictionSideDamping; + } +} + + + +//////////////////////////////////////////////////////////////////////////// +//Default tire force shader function. +//Taken from Michigan tire model. +//Computes tire long and lat forces plus the aligning moment arising from +//the lat force and the torque to apply back to the wheel arising from the +//long force (application of Newton's 3rd law). +//////////////////////////////////////////////////////////////////////////// + + +#define ONE_TWENTYSEVENTH 0.037037f +#define ONE_THIRD 0.33333f +PX_FORCE_INLINE PxF32 smoothingFunction1(const PxF32 K) +{ + //Equation 20 in CarSimEd manual Appendix F. + //Looks a bit like a curve of sqrt(x) for 0=0.0f); + return PxMin(1.0f, K - ONE_THIRD*K*K + ONE_TWENTYSEVENTH*K*K*K); +} +PX_FORCE_INLINE PxF32 smoothingFunction2(const PxF32 K) +{ + //Equation 21 in CarSimEd manual Appendix F. + //Rises to a peak at K=0.75 and falls back to zero by K=3 + PX_ASSERT(K>=0.0f); + return (K - K*K + ONE_THIRD*K*K*K - ONE_TWENTYSEVENTH*K*K*K*K); +} + +void PxVehicleComputeTireForceDefault +(const void* tireShaderData, + const PxF32 tireFriction, + const PxF32 longSlipUnClamped, const PxF32 latSlipUnClamped, const PxF32 camberUnclamped, + const PxF32 wheelOmega, const PxF32 wheelRadius, const PxF32 recipWheelRadius, + const PxF32 restTireLoad, const PxF32 normalisedTireLoad, const PxF32 tireLoad, + const PxF32 gravity, const PxF32 recipGravity, + PxF32& wheelTorque, PxF32& tireLongForceMag, PxF32& tireLatForceMag, PxF32& tireAlignMoment) +{ + PX_UNUSED(wheelOmega); + PX_UNUSED(recipWheelRadius); + + const PxVehicleTireData& tireData=*reinterpret_cast(tireShaderData); + + PX_ASSERT(tireFriction>0); + PX_ASSERT(tireLoad>0); + + wheelTorque=0.0f; + tireLongForceMag=0.0f; + tireLatForceMag=0.0f; + tireAlignMoment=0.0f; + + //Clamp the slips to a minimum value. + const PxF32 latSlip = PxAbs(latSlipUnClamped) >= gMinimumSlipThreshold ? latSlipUnClamped : 0.0f; + const PxF32 longSlip = PxAbs(longSlipUnClamped) >= gMinimumSlipThreshold ? longSlipUnClamped : 0.0f; + const PxF32 camber = PxAbs(camberUnclamped) >= gMinimumSlipThreshold ? camberUnclamped : 0.0f; + + //If long slip/lat slip/camber are all zero than there will be zero tire force. + if((0==latSlip)&&(0==longSlip)&&(0==camber)) + { + return; + } + + //Compute the lateral stiffness + const PxF32 latStiff=restTireLoad*tireData.mLatStiffY*smoothingFunction1(normalisedTireLoad*3.0f/tireData.mLatStiffX); + + //Get the longitudinal stiffness + const PxF32 longStiff=tireData.mLongitudinalStiffnessPerUnitGravity*gravity; + const PxF32 recipLongStiff=tireData.getRecipLongitudinalStiffnessPerUnitGravity()*recipGravity; + + //Get the camber stiffness. + const PxF32 camberStiff=tireData.mCamberStiffnessPerUnitGravity*gravity; + + //Carry on and compute the forces. + const PxF32 TEff = PxTan(latSlip - camber*camberStiff/latStiff); + const PxF32 K = PxSqrt(latStiff*TEff*latStiff*TEff + longStiff*longSlip*longStiff*longSlip) /(tireFriction*tireLoad); + //const PxF32 KAbs=PxAbs(K); + PxF32 FBar = smoothingFunction1(K);//K - ONE_THIRD*PxAbs(K)*K + ONE_TWENTYSEVENTH*K*K*K; + PxF32 MBar = smoothingFunction2(K); //K - KAbs*K + ONE_THIRD*K*K*K - ONE_TWENTYSEVENTH*KAbs*K*K*K; + //Mbar = PxMin(Mbar, 1.0f); + PxF32 nu=1; + if(K <= 2.0f*PxPi) + { + const PxF32 latOverlLong=latStiff*recipLongStiff; + nu = 0.5f*(1.0f + latOverlLong - (1.0f - latOverlLong)*PxCos(K*0.5f)); + } + const PxF32 FZero = tireFriction*tireLoad / (PxSqrt(longSlip*longSlip + nu*TEff*nu*TEff)); + const PxF32 fz = longSlip*FBar*FZero; + const PxF32 fx = -nu*TEff*FBar*FZero; + //TODO: pneumatic trail. + const PxF32 pneumaticTrail=1.0f; + const PxF32 fMy= nu * pneumaticTrail * TEff * MBar * FZero; + + //We can add the torque to the wheel. + wheelTorque=-fz*wheelRadius; + tireLongForceMag=fz; + tireLatForceMag=fx; + tireAlignMoment=fMy; +} + + +//////////////////////////////////////////////////////////////////////////// +//Functions required to intersect the wheel with the hit plane +//We support raycasts and sweeps. +//////////////////////////////////////////////////////////////////////////// + +bool intersectRayPlane +(const PxTransform& carChassisTrnsfm, + const PxVec3& bodySpaceWheelCentreOffset, const PxVec3& bodySpaceSuspTravelDir, + const PxF32 width, const PxF32 radius, const PxF32 maxCompression, + const PxVec4& hitPlane, + PxF32& jounce, PxVec3& wheelBottomPos) +{ + PX_UNUSED(width); + + //Compute the raycast start pos and direction. + PxVec3 v, w; + computeSuspensionRaycast(carChassisTrnsfm, bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, radius, maxCompression, v, w); + + //If the raycast starts inside the hit plane then return false + if(hitPlane.x*v.x + hitPlane.y*v.y + hitPlane.z*v.z + hitPlane.w < 0.0f) + { + return false; + } + + //Store a point through the centre of the wheel. + //We'll use this later to compute a position at the bottom of the wheel. + const PxVec3 pos = v; + + //Remove this code because we handle tire width with sweeps now. + //Work out if the inner or outer disc is deeper in the plane. + //const PxVec3 latDir = carChassisTrnsfm.rotate(gRight); + //const PxF32 signDot = computeSign(hitNorm.dot(latDir)); + //v -= latDir*(signDot*0.5f*width); + + //Work out the point on the susp line that touches the intersection plane. + //n.(v+wt)+d=0 where n,d describe the plane; v,w describe the susp ray; t is the point on the susp line. + //t=-(n.v + d)/n.w + const PxF32 hitD = hitPlane.w; + + const PxVec3 n = PxVec3(hitPlane.x, hitPlane.y, hitPlane.z); + const PxF32 d = hitD; + const PxF32 T=-(n.dot(v) + d)/(n.dot(w)); + + //The rest pos of the susp line is 2*radius + maxBounce. + const PxF32 restT = 2.0f*radius+maxCompression; + + //Compute the spring compression ie the difference between T and restT. + //+ve means that the spring is compressed + //-ve means that the spring is elongated. + jounce = restT-T; + + //Compute the bottom of the wheel. + //Always choose a point through the centre of the wheel. + wheelBottomPos = pos + w*(restT - jounce); + + return true; +} + +bool intersectPlanes(const PxVec4& a, const PxVec4& b, PxVec3& v, PxVec3& w) +{ + const PxF32 n1x = a.x; + const PxF32 n1y = a.y; + const PxF32 n1z = a.z; + const PxF32 n1d = a.w; + + const PxF32 n2x = b.x; + const PxF32 n2y = b.y; + const PxF32 n2z = b.z; + const PxF32 n2d = b.w; + + PxF32 dx = (n1y * n2z) - (n1z * n2y); + PxF32 dy = (n1z * n2x) - (n1x * n2z); + PxF32 dz = (n1x * n2y) - (n1y * n2x); + + const PxF32 dx2 = dx * dx; + const PxF32 dy2 = dy * dy; + const PxF32 dz2 = dz * dz; + + PxF32 px, py, pz; + bool success = true; + if ((dz2 > dy2) && (dz2 > dx2) && (dz2 > 0)) + { + px = ((n1y * n2d) - (n2y * n1d)) / dz; + py = ((n2x * n1d) - (n1x * n2d)) / dz; + pz = 0; + } + else if ((dy2 > dx2) && (dy2 > 0)) + { + px = -((n1z * n2d) - (n2z * n1d)) / dy; + py = 0; + pz = -((n2x * n1d) - (n1x * n2d)) / dy; + } + else if (dx2 > 0) + { + px = 0; + py = ((n1z * n2d) - (n2z * n1d)) / dx; + pz = ((n2y * n1d) - (n1y * n2d)) / dx; + } + else + { + px=0; + py=0; + pz=0; + success=false; + } + + const PxF32 ld = PxSqrt(dx2 + dy2 + dz2); + + dx /= ld; + dy /= ld; + dz /= ld; + + w = PxVec3(dx,dy,dz); + v = PxVec3(px,py,pz); + + return success; +} + +bool intersectCylinderPlane +(const PxTransform& wheelPoseAtZeroJounce, const PxVec3 suspDir, + const PxF32 width, const PxF32 radius, const PxF32 maxCompression, + const PxVec4& hitPlane, + const bool rejectFromThresholds, + PxF32& jounce, PxVec3& wheelBottomPos) +{ + PX_UNUSED(maxCompression); + PX_UNUSED(width); + + //Reject based on the contact normal. + if (rejectFromThresholds) + { + if (suspDir.dot(-hitPlane.getXYZ()) < gNormalRejectAngleThreshold) + { + return false; + } + } + + //Construct the wheel plane that contains the wheel disc. + PxVec4 wheelPlane; + { + const PxVec3 n = wheelPoseAtZeroJounce.rotate(gRight); + const PxF32 d = - n.dot(wheelPoseAtZeroJounce.p); + wheelPlane.x = n.x; + wheelPlane.y = n.y; + wheelPlane.z = n.z; + wheelPlane.w = d; + } + + //Intersect the plane of the wheel with the hit plane. + //This generates an intersection edge. + PxVec3 intersectionEdgeV, intersectionEdgeW; + const bool intersectPlaneSuccess = intersectPlanes(wheelPlane, hitPlane, intersectionEdgeV, intersectionEdgeW); + if(!intersectPlaneSuccess) + { + jounce = 0.0f; + wheelBottomPos = PxVec3(0,0,0); + return false; + } + + //Compute the position on the intersection edge that is closest to the wheel centre. + PxVec3 closestPointOnIntersectionEdge; + { + const PxVec3& p = wheelPoseAtZeroJounce.p; + const PxVec3& w = intersectionEdgeW; + const PxVec3& v = intersectionEdgeV; + const PxF32 t = (p - v).dot(w); + closestPointOnIntersectionEdge = v + w*t; + } + + //Compute the vector that joins the wheel centre to the intersection edge; + PxVec3 dir; + { + const PxF32 wheelCentreD = hitPlane.x*wheelPoseAtZeroJounce.p.x + hitPlane.y*wheelPoseAtZeroJounce.p.y + hitPlane.z*wheelPoseAtZeroJounce.p.z + hitPlane.w; + dir = ((wheelCentreD >= 0) ? closestPointOnIntersectionEdge - wheelPoseAtZeroJounce.p : wheelPoseAtZeroJounce.p - closestPointOnIntersectionEdge); + dir.normalize(); + } + + //Now work out if we accept the hit. + //Compare dir with the suspension direction. + if (rejectFromThresholds) + { + if (suspDir.dot(dir) < gPointRejectAngleThreshold) + { + return false; + } + } + + //Compute the point on the disc diameter that will be the closest to the hit plane or the deepest inside the hit plane. + PxVec3 pos; + { + pos = wheelPoseAtZeroJounce.p + dir*radius; + } + + //If the sweep started inside the hit plane then return false + const PxVec3 startPos = pos - suspDir*(radius + maxCompression); + if(hitPlane.x*startPos.x + hitPlane.y*startPos.y + hitPlane.z*startPos.z + hitPlane.w < 0.0f) + { + return false; + } + + //Now compute the maximum depth of the inside and outside discs against the plane. + PxF32 depth; + { + const PxVec3 latDir = wheelPoseAtZeroJounce.rotate(gRight); + const PxF32 signDot = computeSign(hitPlane.x*latDir.x + hitPlane.y*latDir.y + hitPlane.z*latDir.z); + const PxVec3 deepestPos = pos - latDir*(signDot*0.5f*width); + depth = hitPlane.x*deepestPos.x + hitPlane.y*deepestPos.y + hitPlane.z*deepestPos.z + hitPlane.w; + } + + + //How far along the susp dir do we have to move to place the wheel exactly on the plane. + const PxF32 t = -depth/(hitPlane.x*suspDir.x + hitPlane.y*suspDir.y + hitPlane.z*suspDir.z); + + //+ve means that the spring is compressed + //-ve means that the spring is elongated. + jounce = -t; + + //Compute a point at the bottom of the wheel that is at the centre. + wheelBottomPos = pos + suspDir*t; + + //Finished. + return true; +} + +bool intersectCylinderPlane +(const PxTransform& carChassisTrnsfm, + const PxQuat& wheelLocalPoseRotation, const PxF32 wheelTheta, + const PxVec3& bodySpaceWheelCentreOffset, const PxVec3& bodySpaceSuspTravelDir, const PxF32 width, const PxF32 radius, const PxF32 maxCompression, + const PxVec4& hitPlane, + const bool rejectFromThresholds, + PxF32& jounce, PxVec3& wheelBottomPos) +{ + //Compute the pose of the wheel + PxTransform wheelPostsAtZeroJounce; + PxVec3 suspDir; + computeSuspensionSweep( + carChassisTrnsfm, + wheelLocalPoseRotation, wheelTheta, + bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, 0.0f, 0.0f, + wheelPostsAtZeroJounce, suspDir); + + //Perform the intersection. + return intersectCylinderPlane + (wheelPostsAtZeroJounce, suspDir, + width, radius, maxCompression, + hitPlane, + rejectFromThresholds, + jounce, wheelBottomPos); +} + +//////////////////////////////////////////////////////////////////////////// +//Structures used to process blocks of 4 wheels: process the raycast result, +//compute the suspension and tire force, store a number of report variables +//such as tire slip, hit shape, hit material, friction etc. +//////////////////////////////////////////////////////////////////////////// + +class PxVehicleTireForceCalculator4 +{ +public: + + const void* mShaderData[4]; + PxVehicleComputeTireForce mShader; +private: +}; + +//This data structure is passed to processSuspTireWheels +//and represents the data that is logically constant across all sub-steps of each dt update. +struct ProcessSuspWheelTireConstData +{ + //We are integrating dt over N sub-steps. + //timeFraction is 1/N. + PxF32 timeFraction; + //We are integrating dt over N sub-steps. + //subTimeStep is dt/N. + PxF32 subTimeStep; + PxF32 recipSubTimeStep; + + //Gravitational acceleration vector + PxVec3 gravity; + //Length of gravitational acceleration vector (saves a square root each time we need it) + PxF32 gravityMagnitude; + //Reciprocal length of gravitational acceleration vector (saves a square root and divide each time we need it). + PxF32 recipGravityMagnitude; + + //True for tanks, false for all other vehicle types. + //Used when computing the longitudinal and lateral slips. + bool isTank; + + //Minimum denominator allowed in longitudinal slip computation. + PxF32 minLongSlipDenominator; + + //Pointer to physx actor that represents the vehicle. + const PxRigidDynamic* vehActor; + + //Pointer to table of friction values for each combination of material and tire type. + const PxVehicleDrivableSurfaceToTireFrictionPairs* frictionPairs; +}; + +//This data structure is passed to processSuspTireWheels +//and represents the data that is physically constant across each sub-steps of each dt update. +struct ProcessSuspWheelTireInputData +{ +public: + + //True if the driver intends to pass drive torque to any wheel of the vehicle, + //even if none of the wheels in the block of 4 wheels processed in processSuspTireWheels are given drive torque. + //False if the driver does not intend the vehicle to accelerate. + //If the player intends to accelerate then no wheel will be given a sticky tire constraint. + //This data is actually logically constant. + bool isIntentionToAccelerate; + + //True if a wheel has a non-zero diff torque, false if a wheel has zero diff torque. + //This data is actually logically constant. + const bool* isAccelApplied; + + //True if a wheel has a non-zero brake torque, false if a wheel has zero brake torque. + //This data is actually logically constant. + const bool* isBrakeApplied; + + //Steer angles of each wheel in radians. + //This data is actually logically constant. + const PxF32* steerAngles; + + //True if the wheel is not disabled, false if wheel is disabled. + //This data is actually logically constant. + bool* activeWheelStates; + + //Properties of the rigid body - transform. + //This data is actually logically constant. + PxTransform carChassisTrnsfm; + //Properties of the rigid body - linear velocity. + //This data is actually logically constant. + PxVec3 carChassisLinVel; + //Properties of the rigid body - angular velocity + //This data is actually logically constant. + PxVec3 carChassisAngVel; + + //Properties of the wheel shapes at the last sweep. + const PxQuat* wheelLocalPoseRotations; + const PxF32* wheelThetas; + + //Simulation data for the 4 wheels being processed in processSuspTireWheels + //This data is actually logically constant. + const PxVehicleWheels4SimData* vehWheels4SimData; + + //Dynamics data for the 4 wheels being processed in processSuspTireWheels + //This data is a mixture of logically and physically constant. + //We could update some of the data in vehWheels4DynData in processSuspTireWheels + //but we choose to do it after. By specifying the non-constant data members explicitly + //in ProcessSuspWheelTireOutputData we are able to more easily keep a track of the + //constant and non-constant data members. After processSuspTireWheels is complete + //we explicitly transfer the updated data in ProcessSuspWheelTireOutputData to vehWheels4DynData. + //Examples are low long and lat forward speed timers. + const PxVehicleWheels4DynData* vehWheels4DynData; + + //Shaders to calculate the tire forces. + //This data is actually logically constant. + const PxVehicleTireForceCalculator4* vehWheels4TireForceCalculator; + + //Filter function to filter tire load. + //This data is actually logically constant. + const PxVehicleTireLoadFilterData* vehWheels4TireLoadFilterData; + + //How many of the 4 wheels are real wheels (eg a 6-wheeled car has a + //block of 4 wheels then a 2nd block of 4 wheels with only 2 active wheels) + //This data is actually logically constant. + PxU32 numActiveWheels; +}; + +struct ProcessSuspWheelTireOutputData +{ +public: + + ProcessSuspWheelTireOutputData() + { + PxMemZero(this, sizeof(ProcessSuspWheelTireOutputData)); + for(PxU32 i=0;i<4;i++) + { + isInAir[i]=true; + tireSurfaceTypes[i]=PxU32(PxVehicleDrivableSurfaceType::eSURFACE_TYPE_UNKNOWN); + } + } + + + //////////////////////////////////////////////////////////////////////////////////////////// + //The following data is stored so that it may be later passed to PxVehicleWheelQueryResult + ///////////////////////////////////////////////////////////////////////////////////////////// + + //Raycast start [most recent raycast start coord or (0,0,0) if using a cached raycast] + PxVec3 suspLineStarts[4]; + //Raycast start [most recent raycast direction or (0,0,0) if using a cached raycast] + PxVec3 suspLineDirs[4]; + //Raycast start [most recent raycast length or 0 if using a cached raycast] + PxF32 suspLineLengths[4]; + //False if wheel cannot touch the ground. + bool isInAir[4]; + //Actor hit by most recent raycast, NULL if using a cached raycast. + PxActor* tireContactActors[4]; + //Shape hit by most recent raycast, NULL if using a cached raycast. + PxShape* tireContactShapes[4]; + //Material hit by most recent raycast, NULL if using a cached raycast. + PxMaterial* tireSurfaceMaterials[4]; + //Surface type of material hit by most recent raycast, eSURFACE_TYPE_UNKNOWN if using a cached raycast. + PxU32 tireSurfaceTypes[4]; + //Contact point of raycast against either fresh contact plane from fresh raycast or cached contact plane. + PxVec3 tireContactPoints[4]; + //Contact normal of raycast against either fresh contact plane from fresh raycast or cached contact plane. + PxVec3 tireContactNormals[4]; + //Friction experienced by tire (value from friction table for surface/tire type combos multiplied by friction vs slip graph) + PxF32 frictions[4]; + //Jounce experienced by suspension against fresh or cached contact plane. + PxF32 jounces[4]; + //Suspension force to be applied to rigid body. + PxF32 suspensionSpringForces[4]; + //Longitudinal direction of tire in the ground contact plane. + PxVec3 tireLongitudinalDirs[4]; + //Lateral direction of tire in the ground contact plane. + PxVec3 tireLateralDirs[4]; + //Longitudinal slip. + PxF32 longSlips[4]; + //Lateral slip. + PxF32 latSlips[4]; + + //Forward speed of rigid body along tire longitudinal direction at tire base. + //Used later to blend the integrated wheel rotation angle between rolling speed and computed speed + //when the wheel rotation speeds become unreliable at low forward speeds. + PxF32 forwardSpeeds[4]; + + //Torque to be applied to wheel as 1d rigid body. Taken from the longitudinal tire force. + //(Newton's 3rd law means the longitudinal tire force must have an equal and opposite force). + //(The lateral tire force is assumed to be absorbed by the suspension geometry). + PxF32 tireTorques[4]; + + //Force to be applied to rigid body (accumulated across all 4 wheels/tires/suspensions). + PxVec3 chassisForce; + //Torque to be applied to rigid body (accumulated across all 4 wheels/tires/suspensions). + PxVec3 chassisTorque; + + //Updated time spend at low forward speed. + //Needs copied back to vehWheels4DynData + PxF32 newLowForwardSpeedTimers[4]; + //Updated time spend at low lateral speed. + //Needs copied back to vehWheels4DynData + PxF32 newLowSideSpeedTimers[4]; + + //Constraint data for sticky tire constraints and suspension limit constraints. + //Needs copied back to vehWheels4DynData + PxVehicleConstraintShader::VehicleConstraintData vehConstraintData; + + //Store the details of the raycast hit results so that they may be re-used + //next update in the event that no raycast is performed. + //If no raycast was performed then the cached values are just re-copied here + //so that they can be recycled without having to do further tests on whether + //raycasts were performed or not. + //Needs copied back to vehWheels4DynData after the last call to processSuspTireWheels. + //The union of cached hit data and susp raycast data means we don't want to overwrite the + //raycast data until we don't need it any more. + PxU32 cachedHitCounts[4]; + PxVec4 cachedHitPlanes[4]; + PxF32 cachedHitDistances[4]; + PxF32 cachedFrictionMultipliers[4]; + PxU16 cachedHitQueryTypes[4]; + + //Store the details of the force applied to any dynamic actor hit by wheel raycasts. + PxRigidDynamic* hitActors[4]; + PxVec3 hitActorForces[4]; + PxVec3 hitActorForcePositions[4]; +}; + + + +//////////////////////////////////////////////////////////////////////////// +//Monster function to +//1. compute the tire/susp forces +//2. compute the torque to apply to the 1D rigid body wheel arising from the long tire force +//3. process the sticky tire friction constraints +// (monitor and increment the low long + lat speed timers, compute data for the sticky tire constraint if necessary) +//4. process the suspension limit constraints +// (monitor the suspension jounce versus the suspension travel limit, compute the data for the suspension limit constraint if necessary). +//5. record the contact plane so that it may be re-used in future updates in the absence of fresh raycasts. +//6. record telemetry data (if necessary) and record data for reporting such as hit material, hit normal etc. +//////////////////////////////////////////////////////////////////////////// + + +void storeHit +(const ProcessSuspWheelTireConstData& constData, const ProcessSuspWheelTireInputData& inputData, + const PxU16 hitQueryType, + const PxLocationHit& hit, const PxVec4& hitPlane, + const PxU32 i, + PxU32* hitCounts4, + PxF32* hitDistances4, + PxVec4* hitPlanes4, + PxF32* hitFrictionMultipliers4, + PxU16* hitQueryTypes4, + PxShape** hitContactShapes4, + PxRigidActor** hitContactActors4, + PxMaterial** hitContactMaterials4, + PxU32* hitSurfaceTypes4, + PxVec3* hitContactPoints4, + PxVec3* hitContactNormals4, + PxU32* cachedHitCounts, + PxVec4* cachedHitPlanes, + PxF32* cachedHitDistances, + PxF32* cachedFrictionMultipliers, + PxU16* cachedHitQueryTypes) +{ + //Hit count. + hitCounts4[i] = 1; + + //Hit distance. + hitDistances4[i] = hit.distance; + + //Hit plane. + hitPlanes4[i] = hitPlane; + + //Hit friction. + PxU32 surfaceType = 0; + PxMaterial* material = NULL; + { + //Only get the material if the raycast started outside the hit shape. + material = (hit.distance != 0.0f) ? hit.shape->getMaterialFromInternalFaceIndex(hit.faceIndex) : NULL; + } + //Hash table for quick lookup of drivable surface type from material. + const PxVehicleDrivableSurfaceToTireFrictionPairs* PX_RESTRICT frictionPairs = constData.frictionPairs; + VehicleSurfaceTypeHashTable surfaceTypeHashTable(*constData.frictionPairs); + if (NULL != material) + { + surfaceType = surfaceTypeHashTable.get(material); + } + const PxVehicleTireData& tire = inputData.vehWheels4SimData->getTireData(i); + const PxF32 frictionMultiplier = frictionPairs->getTypePairFriction(surfaceType, tire.mType); + PX_ASSERT(frictionMultiplier >= 0); + hitFrictionMultipliers4[i] = frictionMultiplier; + + //Hit type. + hitQueryTypes4[i] = hitQueryType; + + //Hit report. + hitContactShapes4[i] = hit.shape; + hitContactActors4[i] = hit.actor; + hitContactMaterials4[i] = material; + hitSurfaceTypes4[i] = surfaceType; + hitContactPoints4[i] = hit.position; + hitContactNormals4[i] = hit.normal; + + //When we're finished here we need to copy this back to the vehicle. + cachedHitCounts[i] = 1; + cachedHitPlanes[i] = hitPlane; + cachedHitDistances[i] = hit.distance; + cachedFrictionMultipliers[i] = frictionMultiplier; + cachedHitQueryTypes[i] = hitQueryType; +} + +void processSuspTireWheels +(const PxU32 startWheelIndex, + const ProcessSuspWheelTireConstData& constData, const ProcessSuspWheelTireInputData& inputData, + ProcessSuspWheelTireOutputData& outputData) +{ + PX_SIMD_GUARD; //tzRaw.normalize(); in computeTireDirs threw a denorm exception on osx + +#if PX_DEBUG_VEHICLE_ON + PX_ASSERT(0==(startWheelIndex & 3)); +#endif + +#if PX_DEBUG_VEHICLE_ON + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eJOUNCE); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eSUSPFORCE); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eTIRELOAD); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eNORMALIZED_TIRELOAD); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eNORM_TIRE_LONG_FORCE); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eNORM_TIRE_LAT_FORCE); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eTIRE_LONG_SLIP); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eTIRE_LAT_SLIP); + zeroGraphDataWheels(startWheelIndex,PxVehicleWheelGraphChannel::eTIRE_FRICTION); +#endif + + //Unpack the logically constant data. + const PxVec3& gravity=constData.gravity; + const PxF32 timeFraction=constData.timeFraction; + const PxF32 timeStep=constData.subTimeStep; + const PxF32 recipTimeStep=constData.recipSubTimeStep; + const PxF32 recipGravityMagnitude=constData.recipGravityMagnitude; + const PxF32 gravityMagnitude=constData.gravityMagnitude; + const bool isTank=constData.isTank; + const PxF32 minLongSlipDenominator=constData.minLongSlipDenominator; + + //Unpack the input data (physically constant data). + const PxVehicleWheels4SimData& wheelsSimData=*inputData.vehWheels4SimData; + const PxVehicleWheels4DynData& wheelsDynData=*inputData.vehWheels4DynData; + const PxVehicleTireForceCalculator4& tireForceCalculator=*inputData.vehWheels4TireForceCalculator; + const PxVehicleTireLoadFilterData& tireLoadFilterData=*inputData.vehWheels4TireLoadFilterData; + //More constant data describing the 4 wheels under consideration. + const PxF32* PX_RESTRICT tireRestLoads=wheelsSimData.getTireRestLoadsArray(); + const PxF32* PX_RESTRICT recipTireRestLoads=wheelsSimData.getRecipTireRestLoadsArray(); + //Compute the right direction for later. + const PxTransform& carChassisTrnsfm=inputData.carChassisTrnsfm; + const PxVec3 latDir=inputData.carChassisTrnsfm.rotate(gRight); + //Unpack the linear and angular velocity of the rigid body. + const PxVec3& carChassisLinVel=inputData.carChassisLinVel; + const PxVec3& carChassisAngVel=inputData.carChassisAngVel; + //Wheel local poses + const PxQuat* PX_RESTRICT wheelLocalPoseRotations = inputData.wheelLocalPoseRotations; + const PxF32* PX_RESTRICT wheelThetas = inputData.wheelThetas; + //Inputs (accel, steer, brake). + const bool isIntentionToAccelerate=inputData.isIntentionToAccelerate; + const PxF32* steerAngles=inputData.steerAngles; + const bool* isBrakeApplied=inputData.isBrakeApplied; + const bool* isAccelApplied=inputData.isAccelApplied; + //Disabled/enabled wheel states. + const bool* activeWheelStates=inputData.activeWheelStates; + //Current low forward/side speed timers. Note that the updated timers + //are stored in newLowForwardSpeedTimers and newLowSideSpeedTimers. + const PxF32* PX_RESTRICT lowForwardSpeedTimers=wheelsDynData.mTireLowForwardSpeedTimers; + const PxF32* PX_RESTRICT lowSideSpeedTimers=wheelsDynData.mTireLowSideSpeedTimers; + //Susp jounces and speeds from previous call to processSuspTireWheels. + const PxF32* PX_RESTRICT prevJounces=wheelsDynData.mJounces; + + //Unpack the output data (the data we are going to compute). + //Start with the data stored for reporting to PxVehicleWheelQueryResult. + //PxVec3* suspLineStarts=outputData.suspLineStarts; + //PxVec3* suspLineDirs=outputData.suspLineDirs; + //PxF32* suspLineLengths=outputData.suspLineLengths; + bool* isInAirs=outputData.isInAir; + PxActor** tireContactActors=outputData.tireContactActors; + PxShape** tireContactShapes=outputData.tireContactShapes; + PxMaterial** tireSurfaceMaterials=outputData.tireSurfaceMaterials; + PxU32* tireSurfaceTypes=outputData.tireSurfaceTypes; + PxVec3* tireContactPoints=outputData.tireContactPoints; + PxVec3* tireContactNormals=outputData.tireContactNormals; + PxF32* frictions=outputData.frictions; + PxF32* jounces=outputData.jounces; + PxF32* suspensionSpringForces=outputData.suspensionSpringForces; + PxVec3* tireLongitudinalDirs=outputData.tireLongitudinalDirs; + PxVec3* tireLateralDirs=outputData.tireLateralDirs; + PxF32* longSlips=outputData.longSlips; + PxF32* latSlips=outputData.latSlips; + //Now unpack the forward speeds that are used later to blend the integrated wheel + //rotation angle between rolling speed and computed speed when the wheel rotation + //speeds become unreliable at low forward speeds. + PxF32* forwardSpeeds=outputData.forwardSpeeds; + //Unpack the real outputs of this function (wheel torques to apply to 1d rigid body wheel and forces/torques + //to apply to 3d rigid body chassis). + PxF32* tireTorques=outputData.tireTorques; + PxVec3& chassisForce=outputData.chassisForce; + PxVec3& chassisTorque=outputData.chassisTorque; + //Unpack the low speed timers that will be computed. + PxF32* newLowForwardSpeedTimers=outputData.newLowForwardSpeedTimers; + PxF32* newLowSideSpeedTimers=outputData.newLowSideSpeedTimers; + //Unpack the constraint data for suspensions limit and sticky tire constraints. + //Susp limits. + bool* suspLimitActiveFlags=outputData.vehConstraintData.mSuspLimitData.mActiveFlags; + PxVec3* suspLimitDirs=outputData.vehConstraintData.mSuspLimitData.mDirs; + PxVec3* suspLimitCMOffsets=outputData.vehConstraintData.mSuspLimitData.mCMOffsets; + PxF32* suspLimitErrors=outputData.vehConstraintData.mSuspLimitData.mErrors; + //Longitudinal sticky tires. + bool* stickyTireForwardActiveFlags=outputData.vehConstraintData.mStickyTireForwardData.mActiveFlags; + PxVec3* stickyTireForwardDirs=outputData.vehConstraintData.mStickyTireForwardData.mDirs; + PxVec3* stickyTireForwardCMOffsets=outputData.vehConstraintData.mStickyTireForwardData.mCMOffsets; + PxF32* stickyTireForwardTargetSpeeds=outputData.vehConstraintData.mStickyTireForwardData.mTargetSpeeds; + //Lateral sticky tires. + bool* stickyTireSideActiveFlags=outputData.vehConstraintData.mStickyTireSideData.mActiveFlags; + PxVec3* stickyTireSideDirs=outputData.vehConstraintData.mStickyTireSideData.mDirs; + PxVec3* stickyTireSideCMOffsets=outputData.vehConstraintData.mStickyTireSideData.mCMOffsets; + PxF32* stickyTireSideTargetSpeeds=outputData.vehConstraintData.mStickyTireSideData.mTargetSpeeds; + //Hit data. Store the contact data so it can be reused. + PxU32* cachedHitCounts=outputData.cachedHitCounts; + PxVec4* cachedHitPlanes=outputData.cachedHitPlanes; + PxF32* cachedHitDistances=outputData.cachedHitDistances; + PxF32* cachedFrictionMultipliers=outputData.cachedFrictionMultipliers; + PxU16* cachedHitQueryTypes=outputData.cachedHitQueryTypes; + //Hit actor data. + PxRigidDynamic** hitActors=outputData.hitActors; + PxVec3* hitActorForces=outputData.hitActorForces; + PxVec3* hitActorForcePositions=outputData.hitActorForcePositions; + + //Set the cmass rotation straight away (we might need this, we might not but we don't know that yet so just set it). + outputData.vehConstraintData.mCMassRotation = constData.vehActor->getCMassLocalPose().q; + + //Compute all the hit data (counts, distances, planes, frictions, actors, shapes, materials etc etc). + //If we just did a raycast/sweep then we need to compute all this from the hit reports. + //If we are using cached raycast/sweep results then just copy the cached hit result data. + PxU32 hitCounts4[4]; + PxF32 hitDistances4[4]; + PxVec4 hitPlanes4[4]; + PxF32 hitFrictionMultipliers4[4]; + PxU16 hitQueryTypes4[4]; + PxShape* hitContactShapes4[4]; + PxRigidActor* hitContactActors4[4]; + PxMaterial* hitContactMaterials4[4]; + PxU32 hitSurfaceTypes4[4]; + PxVec3 hitContactPoints4[4]; + PxVec3 hitContactNormals4[4]; + const PxRaycastQueryResult* PX_RESTRICT raycastResults=inputData.vehWheels4DynData->mRaycastResults; + const PxSweepQueryResult* PX_RESTRICT sweepResults=inputData.vehWheels4DynData->mSweepResults; + if(raycastResults || sweepResults) + { + const PxU16 queryType = raycastResults ? 0u : 1u; + + //If we have a blocking hit then always take that. + //If we don't have a blocking hit then search for the "best" hit from all the touches. + for(PxU32 i=0;i(raycastResults[i].block) : static_cast(sweepResults[i].block); + + //Test that the hit actor isn't the vehicle itself. + PX_CHECK_AND_RETURN(constData.vehActor != hit.actor, "Vehicle raycast has hit itself. Please check the filter data to avoid this."); + + //Reject if the sweep started inside the hit shape. + if (hit.distance != 0) + { + //Compute the plane of the hit. + const PxVec3 hitPos = hit.position; + const PxVec3 hitNorm = hit.normal; + const PxF32 hitD = -hitNorm.dot(hitPos); + PxVec4 hitPlane(hitNorm, hitD); + + //Store the hit data in the various arrays. + storeHit(constData, inputData, + queryType, + hit, hitPlane, + i, + hitCounts4, + hitDistances4, + hitPlanes4, + hitFrictionMultipliers4, + hitQueryTypes4, + hitContactShapes4, + hitContactActors4, + hitContactMaterials4, + hitSurfaceTypes4, + hitContactPoints4, + hitContactNormals4, + cachedHitCounts, + cachedHitPlanes, + cachedHitDistances, + cachedFrictionMultipliers, + cachedHitQueryTypes); + + hitCount = 1; + } + } + else if (sweepResults && sweepResults[i].nbTouches) + { + //We need wheel info so that we can analyse the hit and reject/accept it. + //Get what we need now. + const PxVehicleWheelData& wheel = wheelsSimData.getWheelData(i); + const PxVehicleSuspensionData& susp = wheelsSimData.getSuspensionData(i); + const PxVec3& bodySpaceWheelCentreOffset = wheelsSimData.getWheelCentreOffset(i); + const PxVec3& bodySpaceSuspTravelDir = wheelsSimData.getSuspTravelDirection(i); + const PxQuat& wheelLocalPoseRotation = wheelLocalPoseRotations[i]; + const PxF32 wheelTheta = wheelThetas[i]; + const PxF32 width = wheel.mWidth; + const PxF32 radius = wheel.mRadius; + const PxF32 maxBounce = susp.mMaxCompression; + + //Compute the global pose of the wheel at zero jounce. + PxTransform suspPose; + PxVec3 suspDir; + computeSuspensionSweep( + carChassisTrnsfm, + wheelLocalPoseRotation, wheelTheta, + bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, 0.0f, 0.0f, + suspPose, suspDir); + + //Iterate over all touches and cache the deepest hit that we accept. + PxF32 bestTouchDistance = -PX_MAX_F32; + for (PxU32 j = 0; j < sweepResults[i].nbTouches; j++) + { + //Get the next candidate hit. + const PxLocationHit& hit = sweepResults[i].touches[j]; + + //Test that the hit actor isn't the vehicle itself. + PX_CHECK_AND_RETURN(constData.vehActor != hit.actor, "Vehicle raycast has hit itself. Please check the filter data to avoid this."); + + //Reject if the sweep started inside the hit shape. + if (hit.distance != 0.0f) + { + //Compute the plane of the hit. + const PxVec3 hitPos = hit.position; + const PxVec3 hitNorm = hit.normal; + const PxF32 hitD = -hitNorm.dot(hitPos); + PxVec4 hitPlane(hitNorm, hitD); + + //Intersect the wheel disc with the hit plane and compute the jounce required to move the wheel free of the hit plane. + PxF32 dx; + PxVec3 wheelBottomPos; + bool successIntersection = + intersectCylinderPlane + (suspPose, suspDir, + width, radius, maxBounce, + hitPlane, + true, + dx, wheelBottomPos); + + //If we accept the intersection and it requires more jounce than previously encountered then + //store the hit. + if (successIntersection && dx > bestTouchDistance) + { + storeHit(constData, inputData, + queryType, + hit, hitPlane, + i, + hitCounts4, + hitDistances4, + hitPlanes4, + hitFrictionMultipliers4, + hitQueryTypes4, + hitContactShapes4, + hitContactActors4, + hitContactMaterials4, + hitSurfaceTypes4, + hitContactPoints4, + hitContactNormals4, + cachedHitCounts, + cachedHitPlanes, + cachedHitDistances, + cachedFrictionMultipliers, + cachedHitQueryTypes); + + bestTouchDistance = dx; + + hitCount = 1; + } + } + } + } + + if(0 == hitCount) + { + hitCounts4[i]=0; + hitDistances4[i]=0; + hitPlanes4[i]=PxVec4(0,0,0,0); + hitFrictionMultipliers4[i]=0; + hitQueryTypes4[i]=0u; + hitContactShapes4[i]=NULL; + hitContactActors4[i]=NULL; + hitContactMaterials4[i]=NULL; + hitSurfaceTypes4[i]=PxU32(PxVehicleDrivableSurfaceType::eSURFACE_TYPE_UNKNOWN); + hitContactPoints4[i]=PxVec3(0,0,0); + hitContactNormals4[i]=PxVec3(0,0,0); + + //When we're finished here we need to copy this back to the vehicle. + cachedHitCounts[i]=0; + cachedHitPlanes[i]=PxVec4(0,0,0,0); + cachedHitDistances[i]=0; + cachedFrictionMultipliers[i]=0; + cachedHitQueryTypes[i] = 0u; + } + } + } + else + { + //If we have no sq results then we must have a cached raycast hit result. + const PxVehicleWheels4DynData::CachedSuspLineSceneQuerytHitResult& cachedHitResult = + reinterpret_cast(inputData.vehWheels4DynData->mQueryOrCachedHitResults); + + for(PxU32 i=0;i 0 && hitDistances4[i] != 0.0f && hitNorm.dot(w) < 0.0f) + { + //Get the friction multiplier from the combination of surface type and tire type. + const PxF32 frictionMultiplier=hitFrictionMultipliers4[i]; + PX_ASSERT(frictionMultiplier>=0); + + PxF32 dx; + PxVec3 wheelBottomPos; + bool successIntersection = true; + if(0 == hitQueryTypes4[i]) + { + successIntersection = intersectRayPlane + (carChassisTrnsfm, + bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, wheel.mWidth, wheel.mRadius, susp.mMaxCompression, + hitPlanes4[i], + dx, wheelBottomPos); + } + else + { + PX_ASSERT(1 == hitQueryTypes4[i]); + successIntersection = intersectCylinderPlane + (carChassisTrnsfm, + wheelLocalPoseRotations[i], wheelThetas[i], + bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, wheel.mWidth, wheel.mRadius, susp.mMaxCompression, + hitPlanes4[i], + false, + dx, wheelBottomPos); + } + + //If the spring is elongated past its max droop then the wheel isn't touching the ground. + //In this case the spring offers zero force and provides no support for the chassis/sprung mass. + //Only carry on computing the spring force if the wheel is touching the ground. + PX_ASSERT(susp.mMaxCompression>=0); + PX_ASSERT(susp.mMaxDroop>=0); + if(dx > -susp.mMaxDroop && successIntersection) + { + //We can record the hit shape, hit actor, hit material, hit surface type, hit point, and hit normal now because we've got a hit. + tireContactShapes[i]=hitContactShapes4[i]; + tireContactActors[i]=hitContactActors4[i]; + tireSurfaceMaterials[i]=hitContactMaterials4[i]; + tireSurfaceTypes[i]=hitSurfaceTypes4[i]; + tireContactPoints[i]=hitContactPoints4[i]; + tireContactNormals[i]=hitContactNormals4[i]; + + //We know that the vehicle is not in the air. + isInAirs[i]=false; + + //Clamp the spring compression so that it is never greater than the max bounce. + //Apply the susp limit constraint if the spring compression is greater than the max bounce. + suspLimitErrors[i] = (w.dot(hitNorm))*(-dx + susp.mMaxCompression); + suspLimitActiveFlags[i] = (dx > susp.mMaxCompression); + suspLimitCMOffsets[i] = bodySpaceWheelCentreOffset; + suspLimitDirs[i] = bodySpaceSuspTravelDir; + jounce=PxMin(dx,susp.mMaxCompression); + + //Store the jounce (having a local copy avoids lhs). + jounces[i]=jounce; + + //Store the jounce in the graph. +#if PX_DEBUG_VEHICLE_ON + updateGraphDataSuspJounce(startWheelIndex, i,jounce); +#endif + + //Compute the speed of the rigid body along the suspension travel dir at the + //bottom of the wheel. + const PxVec3 r=wheelBottomPos-carChassisTrnsfm.p; + PxVec3 wheelBottomVel=carChassisLinVel; + wheelBottomVel+=carChassisAngVel.cross(r); + + //Modify the relative velocity at the wheel contact point if the hit actor is a dynamic. + PxRigidDynamic* dynamicHitActor=NULL; + PxVec3 hitActorVelocity(0,0,0); + if(hitContactActors4[i] && ((dynamicHitActor = hitContactActors4[i]->is()) != NULL)) + { + hitActorVelocity = PxRigidBodyExt::getVelocityAtPos(*dynamicHitActor,wheelBottomPos); + wheelBottomVel -= hitActorVelocity; + } + + //Get the speed of the jounce. + const PxF32 jounceSpeed = (PX_MAX_F32 != prevJounces[i]) ? (jounce - prevJounces[i])*recipTimeStep : 0.0f; + + //Decompose gravity into a term along w and a term perpendicular to w + //gravity = w*alpha + T*beta + //where T is a unit vector perpendicular to w; alpha and beta are scalars. + //The vector w*alpha*mass is the component of gravitational force that acts along the spring direction. + //The vector T*beta*mass is the component of gravitational force that will be resisted by the spring + //because the spring only supports a single degree of freedom along w. + //We only really need to know T*beta so don't bother calculating T or beta. + const PxF32 alpha = PxMax(0.0f, gravity.dot(w)); + const PxVec3 TTimesBeta = (0.0f != alpha) ? gravity - w*alpha : PxVec3(0,0,0); + //Compute the magnitude of the force along w. + PxF32 suspensionForceW = + PxMax(0.0f, + susp.mSprungMass*alpha + //force to support sprung mass at zero jounce + susp.mSpringStrength*jounce); //linear spring + suspensionForceW += susp.mSpringDamperRate*jounceSpeed; //damping + //Compute the total force acting on the suspension. + //Remember that the spring force acts along -w. + //Remember to account for the term perpendicular to w and that it acts along -TTimesBeta + PxF32 suspensionForceMag = hitNorm.dot(-w*suspensionForceW - TTimesBeta*susp.mSprungMass); + + //Apply the opposite force to the hit object. + //Clamp suspensionForceMag if required. + if (dynamicHitActor && !(dynamicHitActor->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC)) + { + const PxF32 dynamicActorInvMass = dynamicHitActor->getInvMass(); + const PxF32 dynamicActorMass = dynamicHitActor->getMass(); + const PxF32 forceSign = computeSign(suspensionForceMag); + const PxF32 forceMag = PxAbs(suspensionForceMag); + const PxF32 clampedAccelMag = PxMin(forceMag*dynamicActorInvMass, gMaxHitActorAcceleration); + const PxF32 clampedForceMag = clampedAccelMag*dynamicActorMass*forceSign; + PX_ASSERT(clampedForceMag*suspensionForceMag >= 0.0f); + + suspensionForceMag = clampedForceMag; + + hitActors[i] = dynamicHitActor; + hitActorForces[i] = hitNorm*(-clampedForceMag*timeFraction); + hitActorForcePositions[i] = hitContactPoints4[i]; + } + + //Store the spring force now (having a local copy avoids lhs). + suspensionSpringForces[i] = suspensionForceMag; + + //Store the spring force in the graph. +#if PX_DEBUG_VEHICLE_ON + updateGraphDataSuspForce(startWheelIndex, i, suspensionForceMag); +#endif + + //Suspension force can be computed now. + const PxVec3 suspensionForce = hitNorm*suspensionForceMag; + + //Torque from spring force. + const PxVec3 suspForceCMOffset = carChassisTrnsfm.rotate(wheelsSimData.getSuspForceAppPointOffset(i)); + const PxVec3 suspensionTorque = suspForceCMOffset.cross(suspensionForce); + + //Add the suspension force/torque to the chassis force/torque. + chassisForce+=suspensionForce; + chassisTorque+=suspensionTorque; + + //Now compute the tire load. + const PxF32 tireLoad = suspensionForceMag; + + //Normalize the tire load + //Now work out the normalized tire load. + const PxF32 normalisedTireLoad=tireLoad*recipGravityMagnitude*recipTireRestLoads[i]; + //Filter the normalized tire load and compute the filtered tire load too. + const PxF32 filteredNormalisedTireLoad=computeFilteredNormalisedTireLoad(tireLoadFilterData,normalisedTireLoad); + const PxF32 filteredTireLoad=filteredNormalisedTireLoad*gravityMagnitude*tireRestLoads[i]; + +#if PX_DEBUG_VEHICLE_ON + updateGraphDataTireLoad(startWheelIndex,i,filteredTireLoad); + updateGraphDataNormTireLoad(startWheelIndex,i,filteredNormalisedTireLoad); +#endif + + //Compute the lateral and longitudinal tire axes in the ground plane. + PxVec3 tireLongDir; + PxVec3 tireLatDir; + computeTireDirs(latDir,hitNorm,steerAngles[i],tireLongDir,tireLatDir); + + //Store the tire long and lat dirs now (having a local copy avoids lhs). + tireLongitudinalDirs[i]= tireLongDir; + tireLateralDirs[i]=tireLatDir; + + //Now compute the speeds along each of the tire axes. + const PxF32 tireLongSpeed=wheelBottomVel.dot(tireLongDir); + const PxF32 tireLatSpeed=wheelBottomVel.dot(tireLatDir); + + //Store the forward speed (having a local copy avoids lhs). + forwardSpeeds[i]=tireLongSpeed; + + //Now compute the slips along each axes. + const bool hasAccel=isAccelApplied[i]; + const bool hasBrake=isBrakeApplied[i]; + const PxF32 wheelOmega=wheelsDynData.mWheelSpeeds[i]; + const PxF32 wheelRadius=wheel.mRadius; + PxF32 longSlip; + PxF32 latSlip; + computeTireSlips + (tireLongSpeed,tireLatSpeed,wheelOmega,wheelRadius,minLongSlipDenominator, + hasAccel,hasBrake, + isTank, + longSlip,latSlip); + + //Store the lat and long slip (having local copies avoids lhs). + longSlips[i]=longSlip; + latSlips[i]=latSlip; + + //Camber angle. + PxF32 camber=susp.mCamberAtRest; + if(jounce>0) + { + camber += jounce*susp.mCamberAtMaxCompression*susp.getRecipMaxCompression(); + } + else + { + camber -= jounce*susp.mCamberAtMaxDroop*susp.getRecipMaxDroop(); + } + + //Compute the friction that will be experienced by the tire. + PxF32 friction; + computeTireFriction(tire,longSlip,frictionMultiplier,friction); + + //Store the friction (having a local copy avoids lhs). + frictions[i]=friction; + + if(filteredTireLoad*frictionMultiplier>0) + { + //Either tire forces or sticky tire friction constraint will be applied here. + const PxVec3 tireForceCMOffset = carChassisTrnsfm.rotate(wheelsSimData.getTireForceAppPointOffset(i)); + + PxF32 newLowForwardSpeedTimer; + { + //check the accel value here + //Update low forward speed timer. + const PxF32 recipWheelRadius=wheel.getRecipRadius(); + newLowForwardSpeedTimer=newLowForwardSpeedTimers[i]; + updateLowForwardSpeedTimer(tireLongSpeed,wheelOmega,wheelRadius,recipWheelRadius,isIntentionToAccelerate,timeStep,newLowForwardSpeedTimer); + + //Activate sticky tire forward friction constraint if required. + //If sticky tire friction is active then set the longitudinal slip to zero because + //the sticky tire constraint will take care of the longitudinal component of motion. + bool stickyTireForwardActiveFlag=false; + PxF32 stickyTireForwardTargetSpeed=0.0f; + activateStickyFrictionForwardConstraint(tireLongSpeed,wheelOmega,newLowForwardSpeedTimer,isIntentionToAccelerate,stickyTireForwardActiveFlag,stickyTireForwardTargetSpeed); + stickyTireForwardTargetSpeed += hitActorVelocity.dot(tireLongDir); + + //Store the sticky tire data (having local copies avoids lhs). + newLowForwardSpeedTimers[i] = newLowForwardSpeedTimer; + stickyTireForwardActiveFlags[i]=stickyTireForwardActiveFlag; + stickyTireForwardTargetSpeeds[i]=stickyTireForwardTargetSpeed; + stickyTireForwardDirs[i]=tireLongDir; + stickyTireForwardCMOffsets[i]=tireForceCMOffset; + + //Deactivate the long slip if sticky tire constraint is active. + longSlip=(!stickyTireForwardActiveFlag ? longSlip : 0.0f); + + //Store the long slip (having local copies avoids lhs). + longSlips[i]=longSlip; + } + + PxF32 newLowSideSpeedTimer; + { + //check the accel value here + //Update low side speed timer. + newLowSideSpeedTimer=newLowSideSpeedTimers[i]; + updateLowSideSpeedTimer(tireLatSpeed,isIntentionToAccelerate,timeStep,newLowSideSpeedTimer); + + //Activate sticky tire side friction constraint if required. + //If sticky tire friction is active then set the lateral slip to zero because + //the sticky tire constraint will take care of the lateral component of motion. + bool stickyTireSideActiveFlag=false; + PxF32 stickyTireSideTargetSpeed=0.0f; + activateStickyFrictionSideConstraint(tireLatSpeed,newLowForwardSpeedTimer,newLowSideSpeedTimer,isIntentionToAccelerate,stickyTireSideActiveFlag,stickyTireSideTargetSpeed); + stickyTireSideTargetSpeed += hitActorVelocity.dot(tireLatDir); + + //Store the sticky tire data (having local copies avoids lhs). + newLowSideSpeedTimers[i] = newLowSideSpeedTimer; + stickyTireSideActiveFlags[i]=stickyTireSideActiveFlag; + stickyTireSideTargetSpeeds[i]=stickyTireSideTargetSpeed; + stickyTireSideDirs[i]=tireLatDir; + stickyTireSideCMOffsets[i]=tireForceCMOffset; + + //Deactivate the lat slip if sticky tire constraint is active. + latSlip=(!stickyTireSideActiveFlag ? latSlip : 0.0f); + + //Store the long slip (having local copies avoids lhs). + latSlips[i]=latSlip; + } + + //Compute the various tire torques. + PxF32 wheelTorque=0; + PxF32 tireLongForceMag=0; + PxF32 tireLatForceMag=0; + PxF32 tireAlignMoment=0; + const PxF32 restTireLoad=gravityMagnitude*tireRestLoads[i]; + const PxF32 recipWheelRadius=wheel.getRecipRadius(); + tireForceCalculator.mShader( + tireForceCalculator.mShaderData[i], + friction, + longSlip,latSlip,camber, + wheelOmega,wheelRadius,recipWheelRadius, + restTireLoad,filteredNormalisedTireLoad,filteredTireLoad, + gravityMagnitude, recipGravityMagnitude, + wheelTorque,tireLongForceMag,tireLatForceMag,tireAlignMoment); + + //Store the tire torque ((having a local copy avoids lhs). + tireTorques[i]=wheelTorque; + + //Apply the torque to the chassis. + //Compute the tire force to apply to the chassis. + const PxVec3 tireLongForce=tireLongDir*tireLongForceMag; + const PxVec3 tireLatForce=tireLatDir*tireLatForceMag; + const PxVec3 tireForce=tireLongForce+tireLatForce; + //Compute the torque to apply to the chassis. + const PxVec3 tireTorque=tireForceCMOffset.cross(tireForce); + //Add all the forces/torques together. + chassisForce+=tireForce; + chassisTorque+=tireTorque; + + //Graph all the data we just computed. +#if PX_DEBUG_VEHICLE_ON + if(gCarTireForceAppPoints) + gCarTireForceAppPoints[i]=carChassisTrnsfm.p + tireForceCMOffset; + if(gCarSuspForceAppPoints) + gCarSuspForceAppPoints[i]=carChassisTrnsfm.p + suspForceCMOffset; + + if(gCarWheelGraphData[0]) + { + updateGraphDataNormLongTireForce(startWheelIndex, i, PxAbs(tireLongForceMag)*normalisedTireLoad/tireLoad); + updateGraphDataNormLatTireForce(startWheelIndex, i, PxAbs(tireLatForceMag)*normalisedTireLoad/tireLoad); + updateGraphDataNormTireAligningMoment(startWheelIndex, i, tireAlignMoment*normalisedTireLoad/tireLoad); + updateGraphDataLongTireSlip(startWheelIndex, i,longSlips[i]); + updateGraphDataLatTireSlip(startWheelIndex, i,latSlips[i]); + updateGraphDataTireFriction(startWheelIndex, i,frictions[i]); + } +#endif + }//filteredTireLoad*frictionMultiplier>0 + }//if(dx > -susp.mMaxCompression) + }//if(numHits>0) + }//i +} + + +void procesAntiRollSuspension +(const PxVehicleWheelsSimData& wheelsSimData, + const PxTransform& carChassisTransform, const PxWheelQueryResult* wheelQueryResults, + PxVec3& chassisTorque) +{ + const PxU32 numAntiRollBars = wheelsSimData.getNbAntiRollBars(); + for(PxU32 i = 0; i < numAntiRollBars; i++) + { + const PxVehicleAntiRollBarData& antiRoll = wheelsSimData.getAntiRollBarData(i); + const PxU32 w0 = antiRoll.mWheel0; + const PxU32 w1 = antiRoll.mWheel1; + + //At least one wheel must be on the ground for the anti-roll to work. + const bool w0InAir = wheelQueryResults[w0].isInAir; + const bool w1InAir = wheelQueryResults[w1].isInAir; + if(!w0InAir || !w1InAir) + { + //Compute the difference in jounce and compute the force. + const PxF32 w0Jounce = wheelQueryResults[w0].suspJounce; + const PxF32 w1Jounce = wheelQueryResults[w1].suspJounce; + const PxF32 antiRollForceMag = (w0Jounce - w1Jounce)*antiRoll.mStiffness; + + //Apply the antiRollForce postiviely to wheel0, negatively to wheel 1 + PxU32 wheelIds[2] = {0xffffffff, 0xffffffff}; + PxF32 antiRollForceMags[2]; + PxU32 numWheelIds = 0; + if(!w0InAir) + { + wheelIds[numWheelIds] = w0; + antiRollForceMags[numWheelIds] = -antiRollForceMag; + numWheelIds++; + } + if(!w1InAir) + { + wheelIds[numWheelIds] = w1; + antiRollForceMags[numWheelIds] = +antiRollForceMag; + numWheelIds++; + } + + for(PxU32 j = 0; j < numWheelIds; j++) + { + const PxU32 wheelId = wheelIds[j]; + + //Force + const PxVec3 suspDir = carChassisTransform.q.rotate(wheelsSimData.getSuspTravelDirection(wheelId)); + const PxVec3 antiRollForce = suspDir*antiRollForceMags[j]; + //Torque + const PxVec3 r = carChassisTransform.q.rotate(wheelsSimData.getSuspForceAppPointOffset(wheelId)); + const PxVec3 antiRollTorque = r.cross(antiRollForce); + chassisTorque += antiRollTorque; + } + } + } +} + + + +//////////////////////////////////////////////////////////////////////////// +//Set the low long speed timers computed in processSuspTireWheels +//Call immediately after completing processSuspTireWheels. +//////////////////////////////////////////////////////////////////////////// + +void updateLowSpeedTimers(const PxF32* PX_RESTRICT newLowSpeedTimers, PxF32* PX_RESTRICT lowSpeedTimers) +{ + for(PxU32 i=0;i<4;i++) + { + lowSpeedTimers[i]=(newLowSpeedTimers[i]!=lowSpeedTimers[i] ? newLowSpeedTimers[i] : 0.0f); + } +} + +//////////////////////////////////////////////////////////////////////////// +//Set the jounce values computed in processSuspTireWheels +//Call immediately after completing processSuspTireWheels. +//////////////////////////////////////////////////////////////////////////// +void updateJounces(const PxF32* PX_RESTRICT jounces, PxF32* PX_RESTRICT prevJounces) +{ + for(PxU32 i=0;i<4;i++) + { + prevJounces[i] = jounces[i]; + } +} + +/////////////////////////////////////////////////////////////////////////////// +//Set the hit plane, hit distance and hit friction multplier computed in processSuspTireWheels +//Call immediately after completing processSuspTireWheels. +//////////////////////////////////////////////////////////////////////////// + +void updateCachedHitData +(const PxU32* PX_RESTRICT cachedHitCounts, const PxVec4* PX_RESTRICT cachedHitPlanes, const PxF32* PX_RESTRICT cachedHitDistances, const PxF32* PX_RESTRICT cachedFrictionMultipliers, const PxU16* cachedQueryTypes, + PxVehicleWheels4DynData* wheels4DynData) +{ + if(wheels4DynData->mRaycastResults || wheels4DynData->mSweepResults) + { + wheels4DynData->mHasCachedRaycastHitPlane = true; + } + + PxVehicleWheels4DynData::CachedSuspLineSceneQuerytHitResult* cachedRaycastHitResults = + reinterpret_cast(wheels4DynData->mQueryOrCachedHitResults); + + + for(PxU32 i=0;i<4;i++) + { + cachedRaycastHitResults->mCounts[i]=Ps::to16(cachedHitCounts[i]); + cachedRaycastHitResults->mPlanes[i]=cachedHitPlanes[i]; + cachedRaycastHitResults->mDistances[i]=cachedHitDistances[i]; + cachedRaycastHitResults->mFrictionMultipliers[i]=cachedFrictionMultipliers[i]; + cachedRaycastHitResults->mQueryTypes[i] = cachedQueryTypes[i]; + } +} + + + +//////////////////////////////////////////////////////////////////////////// +//Solve the system of engine speed + wheel rotation speeds using an implicit integrator. +//The following functions only compute the speed of wheels connected to the diff. +//Worth going to the length of the implicit integrator because after gear changes +//the difference in speed at the clutch can be hard to integrate. +//Separate functions for 4W, NW and tank because the differential works in slightly +//different ways. With driveNW we end up with (N+1)*(N+1) problem, with drive4W we end up +//with 5*5 and with tanks we end up with just 3*3. Tanks use the method of least squares +//to apply the rule that all left/right wheels have the same speed. +//Remember that the following functions don't integrate wheels not connected to the diff +//so these need integrated separately. +//////////////////////////////////////////////////////////////////////////// + +#if PX_CHECKED +bool isValid(const MatrixNN& A, const VectorN& b, const VectorN& result) +{ + PX_ASSERT(A.getSize()==b.getSize()); + PX_ASSERT(A.getSize()==result.getSize()); + const PxU32 size=A.getSize(); + + //r=A*result-b + VectorN r(size); + for(PxU32 i=0;i 0, false if brakeTorques[i]==0 + const bool* isBrakeApplied; + + //Tire torques to apply to each 1d rigid body wheel. + const PxF32* tireTorques; + + //Sim and dyn data. + PxU32 numWheels4; + PxU32 numActiveWheels; + const PxVehicleWheels4SimData* wheels4SimData; + const PxVehicleDriveSimData* driveSimData; +}; + +struct ImplicitSolverOutput +{ + PxVehicleWheels4DynData* wheelsDynData; + PxVehicleDriveDynData* driveDynData; +}; + +void solveDrive4WInternaDynamicsEnginePlusDrivenWheels +(const ImplicitSolverInput& input, ImplicitSolverOutput* output) +{ + const PxF32 subTimestep = input.subTimeStep; + const PxF32 K = input.K; + const PxF32 G = input.G; + const PxVehicleClutchAccuracyMode::Enum accuracyMode = input.accuracyMode; + const PxU32 maxIterations = input.maxNumIterations; + const PxF32 engineDriveTorque = input.engineDriveTorque; + const PxF32 engineDampingRate = input.engineDampingRate; + const PxF32* PX_RESTRICT diffTorqueRatios = input.diffTorqueRatios; + const PxF32* PX_RESTRICT aveWheelSpeedContributions = input.aveWheelSpeedContributions; + const PxF32* PX_RESTRICT brakeTorques = input.brakeTorques; + const bool* PX_RESTRICT isBrakeApplied = input.isBrakeApplied; + const PxF32* PX_RESTRICT tireTorques = input.tireTorques; + const PxVehicleWheels4SimData& wheels4SimData = *input.wheels4SimData; + const PxVehicleDriveSimData4W& driveSimData = *static_cast(input.driveSimData); + + PxVehicleDriveDynData* driveDynData = output->driveDynData; + PxVehicleWheels4DynData* wheels4DynData = output->wheelsDynData; + + const PxF32 KG=K*G; + const PxF32 KGG=K*G*G; + + MatrixNN A(4+1); + VectorN b(4+1); + VectorN result(4+1); + + const PxVehicleEngineData& engineData=driveSimData.getEngineData(); + + const PxF32* PX_RESTRICT wheelSpeeds=wheels4DynData->mWheelSpeeds; + const PxF32 engineOmega=driveDynData->getEngineRotationSpeed(); + + // + //torque at clutch: + //tc = K*{G*[alpha0*w0 + alpha1*w1 + alpha2*w2 + ..... alpha(N-1)*w(N-1)] - wEng} + //where + //(i) G is the gearing ratio, + //(ii) alphai is the fractional contribution of the ith wheel to the average wheel speed at the clutch (alpha(i) is zero for undriven wheels) + //(iii) wi is the angular speed of the ith wheel + //(iv) K is the clutch strength + //(v) wEng is the angular speed of the engine + + //torque applied to ith wheel is + //ti = G*gammai*tc + bt(i) + tt(i) + //where + //gammai is the fractional proportion of the clutch torque that the differential delivers to the ith wheel + //bt(i) is the brake torque applied to the ith wheel + //tt(i) is the tire torque applied to the ith wheel + + //acceleration applied to ith wheel is + //ai = G*gammai*K*{G*[alpha0*w0 + alpha1*w1 alpha2*w2 + ..... alpha(N-1)*w(N-1)] - wEng}/Ii + (bt(i) + tt(i))/Ii + //wheer Ii is the moi of the ith wheel + + //express ai as + //ai = [wi(t+dt) - wi(t)]/dt + //and rearrange + //wi(t+dt) - wi(t)] = dt*G*gammai*K*{G*[alpha0*w0(t+dt) + alpha1*w1(t+dt) + alpha2*w2(t+dt) + ..... alpha(N-1)*w(N-1)(t+dt)] - wEng(t+dt)}/Ii + dt*(bt(i) + tt(i))/Ii + + //Do the same for tEng (torque applied to engine) + //tEng = -tc + engineDriveTorque + //where engineDriveTorque is the drive torque applied to the engine + //Assuming the engine has unit mass then + //wEng(t+dt) -wEng(t) = -dt*K*{G*[alpha0*w0(t+dt) + alpha1*w1(t+dt) + alpha2*w2(t+dt) + ..... alpha(N-1)*w(N-1(t+dt))] - wEng(t+dt)}/Ieng + dt*engineDriveTorque]/IEng + + //Introduce the vector w=(w0,w1,w2....w(N-1), wEng) + //and re-express as a matrix after collecting all unknowns at (t+dt) and knowns at time t. + //A*w(t+dt)=b(t); + + //Wheels. + { + for(PxU32 i=0;i<4;i++) + { + const PxF32 dt=subTimestep*wheels4SimData.getWheelData(i).getRecipMOI(); + const PxF32 R=diffTorqueRatios[i]; + const PxF32 dtKGGR=dt*KGG*R; + A.set(i,0,dtKGGR*aveWheelSpeedContributions[0]); + A.set(i,1,dtKGGR*aveWheelSpeedContributions[1]); + A.set(i,2,dtKGGR*aveWheelSpeedContributions[2]); + A.set(i,3,dtKGGR*aveWheelSpeedContributions[3]); + A.set(i,i,1.0f+dtKGGR*aveWheelSpeedContributions[i]+dt*wheels4SimData.getWheelData(i).mDampingRate); + A.set(i,4,-dt*KG*R); + b[i] = wheelSpeeds[i] + dt*(brakeTorques[i]+tireTorques[i]); + result[i] = wheelSpeeds[i]; + } + } + + //Engine. + { + const PxF32 dt=subTimestep*driveSimData.getEngineData().getRecipMOI(); + const PxF32 dtKG=dt*K*G; + A.set(4,0,-dtKG*aveWheelSpeedContributions[0]); + A.set(4,1,-dtKG*aveWheelSpeedContributions[1]); + A.set(4,2,-dtKG*aveWheelSpeedContributions[2]); + A.set(4,3,-dtKG*aveWheelSpeedContributions[3]); + A.set(4,4,1.0f + dt*(K+engineDampingRate)); + b[4] = engineOmega + dt*engineDriveTorque; + result[4] = engineOmega; + } + + //Solve Aw=b + if(PxVehicleClutchAccuracyMode::eBEST_POSSIBLE == accuracyMode) + { + MatrixNNLUSolver solver; + solver.decomposeLU(A); + solver.solve(b,result); + PX_WARN_ONCE_IF(!isValid(A,b,result), "Unable to compute new PxVehicleDrive4W internal rotation speeds. Please check vehicle sim data, especially clutch strength; engine moi and damping; wheel moi and damping"); + } + else + { + MatrixNGaussSeidelSolver solver; + solver.solve(maxIterations, gSolverTolerance, A, b, result); + } + + //Check for sanity in the resultant internal rotation speeds. + //If the brakes are on and the wheels have switched direction then lock them at zero. + //A consequence of this quick fix is that locked wheels remain locked until the brake is entirely released. + //This isn't strictly mathematically or physically correct - a more accurate solution would either formulate the + //brake as a lcp problem or repeatedly solve with constraints that locked wheels remain at zero rotation speed. + //The physically correct solution will certainly be more expensive so let's live with the restriction that + //locked wheels remain locked until the brake is released. + //newOmega=result[i], oldOmega=wheelSpeeds[i], if newOmega*oldOmega<=0 and isBrakeApplied then lock wheel. + result[0]=(isBrakeApplied[0] && (wheelSpeeds[0]*result[0]<=0)) ? 0.0f : result[0]; + result[1]=(isBrakeApplied[1] && (wheelSpeeds[1]*result[1]<=0)) ? 0.0f : result[1]; + result[2]=(isBrakeApplied[2] && (wheelSpeeds[2]*result[2]<=0)) ? 0.0f : result[2]; + result[3]=(isBrakeApplied[3] && (wheelSpeeds[3]*result[3]<=0)) ? 0.0f : result[3]; + //Clamp the engine revs. + //Again, this is not physically or mathematically correct but the loss in behaviour will be hard to notice. + //The alternative would be to add constraints to the solver, which would be much more expensive. + result[4]=PxClamp(result[4],0.0f,engineData.mMaxOmega); + + //Copy back to the car's internal rotation speeds. + wheels4DynData->mWheelSpeeds[0]=result[0]; + wheels4DynData->mWheelSpeeds[1]=result[1]; + wheels4DynData->mWheelSpeeds[2]=result[2]; + wheels4DynData->mWheelSpeeds[3]=result[3]; + driveDynData->setEngineRotationSpeed(result[4]); +} + +void solveDriveNWInternalDynamicsEnginePlusDrivenWheels +(const ImplicitSolverInput& input, ImplicitSolverOutput* output) +{ + const PxF32 subTimestep = input.subTimeStep; + //const PxF32 brake = input.brake; + //const PxF32 handbrake = input.handBrake; + const PxF32 K = input.K; + const PxF32 G = input.G; + const PxVehicleClutchAccuracyMode::Enum accuracyMode = input.accuracyMode; + const PxU32 maxIterations = input.maxNumIterations; + const PxF32 engineDriveTorque = input.engineDriveTorque; + const PxF32 engineDampingRate = input.engineDampingRate; + const PxF32* PX_RESTRICT diffTorqueRatios = input.diffTorqueRatios; + const PxF32* PX_RESTRICT aveWheelSpeedContributions = input.aveWheelSpeedContributions; + const PxF32* PX_RESTRICT brakeTorques = input.brakeTorques; + const bool* PX_RESTRICT isBrakeApplied = input.isBrakeApplied; + const PxF32* PX_RESTRICT tireTorques = input.tireTorques; + //const PxU32 numWheels4 = input.numWheels4; + const PxU32 numActiveWheels = input.numActiveWheels; + const PxVehicleWheels4SimData* PX_RESTRICT wheels4SimDatas = input.wheels4SimData; + const PxVehicleDriveSimDataNW& driveSimData = *static_cast(input.driveSimData); + + PxVehicleDriveDynData* driveDynData = output->driveDynData; + PxVehicleWheels4DynData* wheels4DynDatas = output->wheelsDynData; + + const PxF32 KG=K*G; + const PxF32 KGG=K*G*G; + + MatrixNN A(numActiveWheels+1); + VectorN b(numActiveWheels+1); + VectorN result(numActiveWheels+1); + + const PxVehicleEngineData& engineData=driveSimData.getEngineData(); + const PxF32 engineOmega=driveDynData->getEngineRotationSpeed(); + + // + //torque at clutch: + //tc = K*{G*[alpha0*w0 + alpha1*w1 + alpha2*w2 + ..... alpha(N-1)*w(N-1)] - wEng} + //where + //(i) G is the gearing ratio, + //(ii) alphai is the fractional contribution of the ith wheel to the average wheel speed at the clutch (alpha(i) is zero for undriven wheels) + //(iii) wi is the angular speed of the ith wheel + //(iv) K is the clutch strength + //(v) wEng is the angular speed of the engine + + //torque applied to ith wheel is + //ti = G*gammai*tc + bt(i) + tt(i) + //where + //gammai is the fractional proportion of the clutch torque that the differential delivers to the ith wheel + //bt(i) is the brake torque applied to the ith wheel + //tt(i) is the tire torque applied to the ith wheel + + //acceleration applied to ith wheel is + //ai = G*gammai*K*{G*[alpha0*w0 + alpha1*w1 alpha2*w2 + ..... alpha(N-1)*w(N-1)] - wEng}/Ii + (bt(i) + tt(i))/Ii + //wheer Ii is the moi of the ith wheel. + + //express ai as + //ai = [wi(t+dt) - wi(t)]/dt + //and rearrange + //wi(t+dt) - wi(t)] = dt*G*gammai*K*{G*[alpha0*w0(t+dt) + alpha1*w1(t+dt) + alpha2*w2(t+dt) + ..... alpha(N-1)*w(N-1)(t+dt)] - wEng(t+dt)}/Ii + dt*(bt(i) + tt(i))/Ii + + //Do the same for tEng (torque applied to engine) + //tEng = -tc + engineDriveTorque + //where engineDriveTorque is the drive torque applied to the engine + //Assuming the engine has unit mass then + //wEng(t+dt) -wEng(t) = -dt*K*{G*[alpha0*w0(t+dt) + alpha1*w1(t+dt) + alpha2*w2(t+dt) + ..... alpha(N-1)*w(N-1(t+dt))] - wEng(t+dt)}/Ieng + dt*engineDriveTorque/Ieng + + //Introduce the vector w=(w0,w1,w2....w(N-1), wEng) + //and re-express as a matrix after collecting all unknowns at (t+dt) and knowns at time t. + //A*w(t+dt)=b(t); + + //Wheels. + for(PxU32 i=0;i>2].getWheelData(i&3).getRecipMOI(); + const PxF32 R=diffTorqueRatios[i]; + const PxF32 dtKGGR=dt*KGG*R; + + for(PxU32 j=0;j>2].getWheelData(i&3).mDampingRate); + A.set(i,numActiveWheels,-dt*KG*R); + b[i] = wheels4DynDatas[i>>2].mWheelSpeeds[i&3] + dt*(brakeTorques[i]+tireTorques[i]); + result[i] = wheels4DynDatas[i>>2].mWheelSpeeds[i&3]; + } + + //Engine. + { + const PxF32 dt=subTimestep*driveSimData.getEngineData().getRecipMOI(); + const PxF32 dtKG=dt*K*G; + for(PxU32 i=0;i>2].mWheelSpeeds[i&3]*result[i]<=0)) ? 0.0f : result[i]; + } + //Clamp the engine revs. + //Again, this is not physically or mathematically correct but the loss in behaviour will be hard to notice. + result[numActiveWheels]=PxClamp(result[numActiveWheels],0.0f,engineData.mMaxOmega); + + //Copy back to the car's internal rotation speeds. + for(PxU32 i=0;i>2].mWheelSpeeds[i&3]=result[i]; + } + driveDynData->setEngineRotationSpeed(result[numActiveWheels]); +} + + +void solveTankInternaDynamicsEnginePlusDrivenWheels +(const ImplicitSolverInput& input, const bool* PX_RESTRICT activeWheelStates, const PxF32* PX_RESTRICT wheelGearings, ImplicitSolverOutput* output) +{ + PX_SIMD_GUARD; // denormal exception triggered at oldOmega*newOmega on osx + const PxF32 subTimestep = input.subTimeStep; + const PxF32 K = input.K; + const PxF32 G = input.G; + const PxF32 engineDriveTorque = input.engineDriveTorque; + const PxF32 engineDampingRate = input.engineDampingRate; + const PxF32* PX_RESTRICT diffTorqueRatios = input.diffTorqueRatios; + const PxF32* PX_RESTRICT aveWheelSpeedContributions = input.aveWheelSpeedContributions; + const PxF32* PX_RESTRICT brakeTorques = input.brakeTorques; + const bool* PX_RESTRICT isBrakeApplied = input.isBrakeApplied; + const PxF32* PX_RESTRICT tireTorques = input.tireTorques; + const PxU32 numWheels4 = input.numWheels4; + const PxU32 numActiveWheels = input.numActiveWheels; + const PxVehicleWheels4SimData* PX_RESTRICT wheels4SimDatas = input.wheels4SimData; + const PxVehicleDriveSimData& driveSimData = *input.driveSimData; + + PxVehicleWheels4DynData* PX_RESTRICT wheels4DynDatas = output->wheelsDynData; + PxVehicleDriveDynData* driveDynData = output->driveDynData; + + const PxF32 KG=K*G; + const PxF32 KGG=K*G*G; + + //Rearrange data in a single array rather than scattered in blocks of 4. + //This makes it easier later on. + PxF32 recipMOI[PX_MAX_NB_WHEELS]; + PxF32 dampingRates[PX_MAX_NB_WHEELS]; + PxF32 wheelSpeeds[PX_MAX_NB_WHEELS]; + PxF32 wheelRecipRadii[PX_MAX_NB_WHEELS]; + + for(PxU32 i=0;igetEngineRotationSpeed(); + + const PxF32 dt=subTimestep*driveSimData.getEngineData().getRecipMOI(); + const PxF32 dtKG=dt*K*G; + for(PxU32 i=0;isetEngineRotationSpeed(newEngineOmega); +} + +//////////////////////////////////////////////////////////////////////////// +//Integrate wheel rotation speeds of wheels not connected to the differential. +//Obviously, no wheels in a PxVehicleNoDrive are connected to a diff so all require +//direct integration. +//Only the first 4 wheels of a PxVehicleDrive4W are connected to the diff so +//any extra wheels need direct integration. +//All tank wheels are connected to the diff so none need integrated in a separate pass. +//What about undriven wheels in a PxVehicleDriveNW? This vehicle type treats all +//wheels as being connected to the diff but sets the diff contribution to zero for +//all undriven wheels. No wheels from a PxVehicleNW need integrated in a separate pass. +//////////////////////////////////////////////////////////////////////////// + +void integrateNoDriveWheelSpeeds +(const PxF32 subTimestep, + const PxF32* PX_RESTRICT brakeTorques, const bool* PX_RESTRICT isBrakeApplied, const PxF32* driveTorques, const PxF32* PX_RESTRICT tireTorques, const PxF32* PX_RESTRICT dampingRates, + const PxVehicleWheels4SimData& vehSuspWheelTire4SimData, PxVehicleWheels4DynData& vehSuspWheelTire4) +{ + //w(t+dt) = w(t) + (1/inertia)*(brakeTorque + driveTorque + tireTorque)*dt - (1/inertia)*damping*w(t)*dt ) (1) + //Apply implicit trick and rearrange. + //w(t+dt)[1 + (1/inertia)*damping*dt] = w(t) + (1/inertia)*(brakeTorque + driveTorque + tireTorque)*dt (2) + + //Introduce (1/inertia)*dt to avoid duplication in (2) + PxF32 subTimeSteps[4] = + { + subTimestep*vehSuspWheelTire4SimData.getWheelData(0).getRecipMOI(), + subTimestep*vehSuspWheelTire4SimData.getWheelData(1).getRecipMOI(), + subTimestep*vehSuspWheelTire4SimData.getWheelData(2).getRecipMOI(), + subTimestep*vehSuspWheelTire4SimData.getWheelData(3).getRecipMOI() + }; + + //Integrate. + //w += torque*dt/inertia - damping*dt*w + //Use implicit integrate trick and rearrange + //w(t+dt) = [w(t) + torque*dt/inertia]/[1 + damping*dt] + const PxF32* PX_RESTRICT wheelSpeeds=vehSuspWheelTire4.mWheelSpeeds; + PxF32 result[4]= + { + (wheelSpeeds[0] + subTimeSteps[0]*(tireTorques[0] + driveTorques[0] + brakeTorques[0]))/(1.0f + dampingRates[0]*subTimeSteps[0]), + (wheelSpeeds[1] + subTimeSteps[1]*(tireTorques[1] + driveTorques[1] + brakeTorques[1]))/(1.0f + dampingRates[1]*subTimeSteps[1]), + (wheelSpeeds[2] + subTimeSteps[2]*(tireTorques[2] + driveTorques[2] + brakeTorques[2]))/(1.0f + dampingRates[2]*subTimeSteps[2]), + (wheelSpeeds[3] + subTimeSteps[3]*(tireTorques[3] + driveTorques[3] + brakeTorques[3]))/(1.0f + dampingRates[3]*subTimeSteps[3]), + }; + + //Check for sanity in the resultant internal rotation speeds. + //If the brakes are on and the wheels have switched direction then lock them at zero. + //newOmega=result[i], oldOmega=wheelSpeeds[i], if newOmega*oldOmega<=0 and isBrakeApplied then lock wheel. + result[0]=(isBrakeApplied[0] && (wheelSpeeds[0]*result[0]<=0)) ? 0.0f : result[0]; + result[1]=(isBrakeApplied[1] && (wheelSpeeds[1]*result[1]<=0)) ? 0.0f : result[1]; + result[2]=(isBrakeApplied[2] && (wheelSpeeds[2]*result[2]<=0)) ? 0.0f : result[2]; + result[3]=(isBrakeApplied[3] && (wheelSpeeds[3]*result[3]<=0)) ? 0.0f : result[3]; + + //Copy back to the car's internal rotation speeds. + vehSuspWheelTire4.mWheelSpeeds[0]=result[0]; + vehSuspWheelTire4.mWheelSpeeds[1]=result[1]; + vehSuspWheelTire4.mWheelSpeeds[2]=result[2]; + vehSuspWheelTire4.mWheelSpeeds[3]=result[3]; +} + +void integrateUndriveWheelRotationSpeeds +(const PxF32 subTimestep, + const PxF32 brake, const PxF32 handbrake, const PxF32* PX_RESTRICT tireTorques, const PxF32* PX_RESTRICT brakeTorques, + const PxVehicleWheels4SimData& vehSuspWheelTire4SimData, PxVehicleWheels4DynData& vehSuspWheelTire4) +{ + for(PxU32 i=0;i<4;i++) + { + //Compute the new angular speed of the wheel. + const PxF32 oldOmega=vehSuspWheelTire4.mWheelSpeeds[i]; + const PxF32 dtI = subTimestep*vehSuspWheelTire4SimData.getWheelData(i).getRecipMOI(); + const PxF32 gamma = vehSuspWheelTire4SimData.getWheelData(i).mDampingRate; + const PxF32 newOmega=(oldOmega+dtI*(tireTorques[i]+brakeTorques[i]))/(1.0f + gamma*dtI); + + //Has the brake been applied? It's hard to tell from brakeTorques[j] because that + //will be zero if the wheel is locked. Work it out from the brake and handbrake data. + const PxF32 brakeGain=vehSuspWheelTire4SimData.getWheelData(i).mMaxBrakeTorque; + const PxF32 handbrakeGain=vehSuspWheelTire4SimData.getWheelData(i).mMaxHandBrakeTorque; + + //Work out if the wheel should be locked. + const bool brakeApplied=((brake*brakeGain + handbrake*handbrakeGain)!=0.0f); + const bool wheelReversed=(oldOmega*newOmega <=0); + const bool wheelLocked=(brakeApplied && wheelReversed); + + //Lock the wheel or apply its new angular speed. + if(!wheelLocked) + { + vehSuspWheelTire4.mWheelSpeeds[i]=newOmega; + } + else + { + vehSuspWheelTire4.mWheelSpeeds[i]=0.0f; + } + } +} + + +//////////////////////////////////////////////////////////////////////////// +//Pose the wheels. +//First integrate the wheel rotation angles and clamp them to a range (-10*pi, 10*pi) +//PxVehicleNoDrive has a different way of telling if a wheel is driven by a drive torque so has a separate function. +//Use the wheel steer/rotation/camber angle and suspension jounce to compute the local transform of each wheel. +//////////////////////////////////////////////////////////////////////////// + +void integrateWheelRotationAngles +(const PxF32 timestep, + const PxF32 K, const PxF32 G, const PxF32 engineDriveTorque, + const PxF32* PX_RESTRICT jounces, const PxF32* PX_RESTRICT diffTorqueRatios, const PxF32* PX_RESTRICT forwardSpeeds, const bool* isBrakeApplied, + const PxVehicleDriveSimData& vehCoreSimData, const PxVehicleWheels4SimData& vehSuspWheelTire4SimData, + PxVehicleDriveDynData& vehCore, PxVehicleWheels4DynData& vehSuspWheelTire4) +{ + PX_SIMD_GUARD; //denorm exception on newRotAngle=wheelRotationAngles[j]+wheelOmega*timestep; on osx + + PX_UNUSED(vehCore); + PX_UNUSED(vehCoreSimData); + + const PxF32 KG=K*G; + + PxF32* PX_RESTRICT wheelSpeeds=vehSuspWheelTire4.mWheelSpeeds; + PxF32* PX_RESTRICT wheelRotationAngles=vehSuspWheelTire4.mWheelRotationAngles; + PxF32* PX_RESTRICT correctedWheelSpeeds = vehSuspWheelTire4.mCorrectedWheelSpeeds; + + for(PxU32 j=0;j<4;j++) + { + //At low vehicle forward speeds we have some numerical difficulties getting the + //wheel rotation speeds to be correct due to the tire model's difficulties at low vz. + //The solution is to blend between the rolling speed at the wheel and the wheel's actual rotation speed. + //If the wheel is + //(i) in the air or, + //(ii) under braking torque or, + //(iii) driven by the engine through the gears and diff + //then always use the wheel's actual rotation speed. + //Just to be clear, this means we will blend when the wheel + //(i) is on the ground and + //(ii) has no brake applied and + //(iii) has no drive torque applied from the clutch and + //(iv) is at low forward speed + PxF32 wheelOmega=wheelSpeeds[j]; + if(jounces[j] > -vehSuspWheelTire4SimData.getSuspensionData(j).mMaxDroop && //(i) wheel touching ground + false==isBrakeApplied[j] && //(ii) no brake applied + 0.0f==diffTorqueRatios[j]*KG*engineDriveTorque && //(iii) no drive torque applied + PxAbs(forwardSpeeds[j]) -vehSuspWheelTire4SimData.getSuspensionData(j).mMaxDroop && //(i) wheel touching ground + false==isBrakeApplied[j] && //(ii) no brake applied + 0.0f==driveTorques[j] && //(iii) no drive torque applied + PxAbs(forwardSpeeds[j]) 0.0f) + { + camberAngle += jounce*suspData.mCamberAtMaxCompression*suspData.getRecipMaxCompression(); + } + else + { + camberAngle -= jounce*suspData.mCamberAtMaxDroop*suspData.getRecipMaxDroop(); + } + + //Compute the transform of the wheel shapes. + const PxVec3 pos=cmOffset+wheelsSimData.getWheelCentreOffset(i)-wheelsSimData.getSuspTravelDirection(i)*jounce; + const PxQuat quat(wheelQueryResults[i].steerAngle, gUp); + const PxQuat quat2(camberAngle, quat.rotate(forward)); + const PxQuat quat3=quat2*quat; + const PxQuat quat4(rotAngles[i],quat3.rotate(gRight)); + const PxTransform t(pos,quat4*quat3); + localPoses[i] = t; + } +} + +void poseWheels +(const PxVehicleWheels4SimData& wheelsSimData, + const PxTransform* localPoses, + const PxU32 numWheelsToPose, + PxRigidDynamic* vehActor) +{ + PxShape* shapeBuffer[128]; + vehActor->getShapes(shapeBuffer,128,0); + + for(PxU32 i=0;igetShapes(shapeBuffer2,1,PxU32(shapeIndex)); + currShape = shapeBuffer2[0]; + } + PX_ASSERT(currShape); + currShape->setLocalPose(localPoses[i]); + } + } +} + + + +//////////////////////////////////////////////////////////////////////////// +//Update each vehicle type with a special function +//////////////////////////////////////////////////////////////////////////// + +class PxVehicleUpdate +{ +public: + +#if PX_DEBUG_VEHICLE_ON + static void updateSingleVehicleAndStoreTelemetryData( + const PxF32 timestep, const PxVec3& gravity, const PxVehicleDrivableSurfaceToTireFrictionPairs& vehicleDrivableSurfaceToTireFrictionPairs, + PxVehicleWheels* focusVehicle, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleTelemetryData& telemetryData); +#endif + + static void update( + const PxF32 timestep, const PxVec3& gravity, const PxVehicleDrivableSurfaceToTireFrictionPairs& vehicleDrivableSurfaceToTireFrictionPairs, + const PxU32 numVehicles, PxVehicleWheels** vehicles, PxVehicleWheelQueryResult* wheelQueryResults, PxVehicleConcurrentUpdateData* vehicleConcurrentUpdates); + + static void updatePost( + const PxVehicleConcurrentUpdateData* vehicleConcurrentUpdates, const PxU32 numVehicles, PxVehicleWheels** vehicles); + + static void suspensionRaycasts( + PxBatchQuery* batchQuery, + const PxU32 numVehicles, PxVehicleWheels** vehicles, const PxU32 numSceneQueryResults, PxRaycastQueryResult* sceneQueryResults, + const bool* vehiclesToRaycast); + + static void suspensionSweeps( + PxBatchQuery* batchQuery, + const PxU32 numVehicles, PxVehicleWheels** vehicles, + const PxU32 numSceneQueryResults, PxSweepQueryResult* sceneQueryResults, const PxU16 nbHitsPerQuery, + const bool* vehiclesToRaycast, + const PxF32 sweepWidthScale, const PxF32 sweepRadiusScale); + + static void updateDrive4W( + const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleDrive4W* vehDrive4W, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates); + + static void updateDriveNW( + const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleDriveNW* vehDriveNW, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates); + + static void updateTank( + const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleDriveTank* vehDriveTank, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates); + + static void updateNoDrive( + const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleNoDrive* vehDriveTank, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates); + + static PxU32 computeNumberOfSubsteps(const PxVehicleWheelsSimData& wheelsSimData, const PxVec3& linVel, const PxTransform& globalPose, const PxVec3& forward) + { + const PxVec3 z=globalPose.q.rotate(forward); + const PxF32 vz=PxAbs(linVel.dot(z)); + const PxF32 thresholdVz=wheelsSimData.mThresholdLongitudinalSpeed; + const PxU32 lowCount=wheelsSimData.mLowForwardSpeedSubStepCount; + const PxU32 highCount=wheelsSimData.mHighForwardSpeedSubStepCount; + const PxU32 count=(vz> 2; + PxVehicleWheels4DynData* wheels4 = wheels.getWheel4DynData(); + for(PxU32 i = 0; i < nbWheels4; i++) + { + wheels4[i].setInternalDynamicsToZero(); + } + } + + PX_INLINE static void setInternalDynamicsToZero(PxVehicleDriveDynData& drive) + { + drive.setEngineRotationSpeed(0.0f); + } + + PX_INLINE static void setInternalDynamicsToZero(PxVehicleNoDrive* veh) + { + setInternalDynamicsToZero(veh->mWheelsDynData); + } + + PX_INLINE static void setInternalDynamicsToZero(PxVehicleDrive4W* veh) + { + setInternalDynamicsToZero(veh->mWheelsDynData); + setInternalDynamicsToZero(veh->mDriveDynData); + } + + PX_INLINE static void setInternalDynamicsToZero(PxVehicleDriveNW* veh) + { + veh->mDriveDynData.setEngineRotationSpeed(0.0f); + setInternalDynamicsToZero(veh->mWheelsDynData); + setInternalDynamicsToZero(veh->mDriveDynData); + } + + PX_INLINE static void setInternalDynamicsToZero(PxVehicleDriveTank* veh) + { + setInternalDynamicsToZero(veh->mWheelsDynData); + setInternalDynamicsToZero(veh->mDriveDynData); + } + + + PX_INLINE static bool isOnDynamicActor(const PxVehicleWheelsSimData& wheelsSimData, const PxVehicleWheelsDynData& wheelsDynData) + { + const PxU32 numWheels4 = wheelsSimData.mNbWheels4; + const PxVehicleWheels4DynData* PX_RESTRICT wheels4DynDatas = wheelsDynData.mWheels4DynData; + + for(PxU32 i=0;i(raycastResults[j].block) : static_cast(sweepResults[j].block); + if(hitCount && hit.actor && hit.actor->is()) + { + return true; + } + } + } + } + + return false; + } + + PX_INLINE static void storeRaycasts(const PxVehicleWheels4DynData& dynData, PxWheelQueryResult* wheelQueryResults) + { + if(dynData.mRaycastResults) + { + for(PxU32 i=0;i<4;i++) + { + const PxVehicleWheels4DynData::SuspLineRaycast& raycast = + reinterpret_cast(dynData.mQueryOrCachedHitResults); + + wheelQueryResults[i].suspLineStart=raycast.mStarts[i]; + wheelQueryResults[i].suspLineDir=raycast.mDirs[i]; + wheelQueryResults[i].suspLineLength=raycast.mLengths[i]; + } + } + else if(dynData.mSweepResults) + { + for(PxU32 i=0;i<4;i++) + { + const PxVehicleWheels4DynData::SuspLineSweep& sweep = + reinterpret_cast(dynData.mQueryOrCachedHitResults); + + wheelQueryResults[i].suspLineStart=sweep.mStartPose[i].p; + wheelQueryResults[i].suspLineDir=sweep.mDirs[i]; + wheelQueryResults[i].suspLineLength=sweep.mLengths[i]; + } + } + else + { + for(PxU32 i=0;i<4;i++) + { + wheelQueryResults[i].suspLineStart=PxVec3(0,0,0); + wheelQueryResults[i].suspLineDir=PxVec3(0,0,0); + wheelQueryResults[i].suspLineLength=0; + } + } + } + + PX_INLINE static void storeSuspWheelTireResults + (const ProcessSuspWheelTireOutputData& outputData, const PxF32* steerAngles, PxWheelQueryResult* wheelQueryResults, const PxU32 numWheels) + { + for(PxU32 i=0;imDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]>-0.01f && + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]<1.01f, + "Illegal vehicle control value - accel must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE]>-0.01f && + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE]<1.01f, + "Illegal vehicle control value - brake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE]>-0.01f && + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE]<1.01f, + "Illegal vehicle control value - handbrake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT]>-1.01f && + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT]<1.01f, + "Illegal vehicle control value - left steer must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]>-1.01f && + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]<1.01f, + "Illegal vehicle control value - right steer must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + PxAbs(vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]- + vehDrive4W->mDriveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT])<1.01f, + "Illegal vehicle control value - right steer value minus left steer value must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + !(vehDrive4W->getRigidDynamicActor()->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC), + "Attempting to update a drive4W with a kinematic actor - this isn't allowed"); + PX_CHECK_AND_RETURN( + NULL==vehWheelQueryResults || vehWheelQueryResults->nbWheelQueryResults >= vehDrive4W->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + PX_CHECK_AND_RETURN( + NULL==vehConcurrentUpdates || vehConcurrentUpdates->nbConcurrentWheelUpdates >= vehDrive4W->mWheelsSimData.getNbWheels(), + "vehConcurrentUpdates->nbConcurrentWheelUpdates must always be greater than or equal to number of wheels in corresponding vehicle"); + +#if PX_CHECKED + { + //Check that the sense of left/right and forward/rear is true. + const PxVec3 fl=vehDrive4W->mWheelsSimData.mWheels4SimData[0].getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT); + const PxVec3 fr=vehDrive4W->mWheelsSimData.mWheels4SimData[0].getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT); + const PxVec3 rl=vehDrive4W->mWheelsSimData.mWheels4SimData[0].getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT); + const PxVec3 rr=vehDrive4W->mWheelsSimData.mWheels4SimData[0].getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_RIGHT); + const PxVec3 right=gRight; + const PxF32 s0=computeSign((fr-fl).dot(right)); + const PxF32 s1=computeSign((rr-rl).dot(right)); + PX_CHECK_AND_RETURN(0==s0 || 0==s1 || s0==s1, "PxVehicle4W does not obey the rule that the eFRONT_RIGHT/eREAR_RIGHT wheels are to the right of the eFRONT_LEFT/eREAR_LEFT wheels"); + } +#endif + + END_TIMER(TIMER_ADMIN); + START_TIMER(TIMER_GRAPHS); + +#if PX_DEBUG_VEHICLE_ON + for(PxU32 i=0;imWheelsSimData.mNbWheels4;i++) + { + updateGraphDataInternalWheelDynamics(4*i,vehDrive4W->mWheelsDynData.mWheels4DynData[i].mWheelSpeeds); + } + updateGraphDataInternalEngineDynamics(vehDrive4W->mDriveDynData.getEngineRotationSpeed()); +#endif + + END_TIMER(TIMER_GRAPHS); + START_TIMER(TIMER_ADMIN); + + //Unpack the vehicle. + //Unpack the 4W simulation and instanced dynamics components. + const PxVehicleWheels4SimData* wheels4SimDatas=vehDrive4W->mWheelsSimData.mWheels4SimData; + const PxVehicleTireLoadFilterData& tireLoadFilterData=vehDrive4W->mWheelsSimData.mNormalisedLoadFilter; + PxVehicleWheels4DynData* wheels4DynDatas=vehDrive4W->mWheelsDynData.mWheels4DynData; + const PxU32 numWheels4=vehDrive4W->mWheelsSimData.mNbWheels4; + const PxU32 numActiveWheels=vehDrive4W->mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=4-(4*numWheels4 - numActiveWheels); + const PxVehicleDriveSimData4W driveSimData=vehDrive4W->mDriveSimData; + PxVehicleDriveDynData& driveDynData=vehDrive4W->mDriveDynData; + PxRigidDynamic* vehActor=vehDrive4W->mActor; + + //We need to store that data we are going to write to actors so we can do this at the end in one go with fewer write locks. + PxVehicleWheelConcurrentUpdateData wheelConcurrentUpdates[PX_MAX_NB_WHEELS]; + PxVehicleConcurrentUpdateData vehicleConcurrentUpdates; + vehicleConcurrentUpdates.nbConcurrentWheelUpdates = numActiveWheels; + vehicleConcurrentUpdates.concurrentWheelUpdates = wheelConcurrentUpdates; + + //Test if a non-zero drive torque was applied or if a non-zero steer angle was applied. + bool finiteInputApplied=false; + if(0!=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT) || + 0!=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT) || + 0!=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL) || + driveDynData.getGearDown() || driveDynData.getGearUp()) + { + finiteInputApplied=true; + } + + //Awake or sleep. + if(vehActor->isSleeping()) + { + if(finiteInputApplied) + { + //Driving inputs so we need the actor to start moving. + vehicleConcurrentUpdates.wakeup = true; + } + else if(isOnDynamicActor(vehDrive4W->mWheelsSimData, vehDrive4W->mWheelsDynData)) + { + //Driving on dynamic so we need to keep moving. + vehicleConcurrentUpdates.wakeup = true; + } + else + { + //No driving inputs and the actor is asleep. + //Set internal dynamics to zero. + setInternalDynamicsToZero(vehDrive4W); + if(vehConcurrentUpdates) vehConcurrentUpdates->staySleeping = true; + return; + } + } + + //In each block of 4 wheels record how many wheels are active. + PxU32 numActiveWheelsPerBlock4[PX_MAX_NB_SUSPWHEELTIRE4]={0,0,0,0,0}; + numActiveWheelsPerBlock4[0]=PxMin(numActiveWheels,PxU32(4)); + for(PxU32 i=1;imWheelsDynData.mTireForceCalculators->mShaderData[4*i+0]; + tires4ForceCalculators[i].mShaderData[1]=vehDrive4W->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+1]; + tires4ForceCalculators[i].mShaderData[2]=vehDrive4W->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+2]; + tires4ForceCalculators[i].mShaderData[3]=vehDrive4W->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+3]; + tires4ForceCalculators[i].mShader=vehDrive4W->mWheelsDynData.mTireForceCalculators->mShader; + } + + //Mark the constraints as dirty to force them to be updated in the sdk. + for(PxU32 i=0;imarkDirty(); + } + + //We need to store data to pose the wheels at the end. + PxWheelQueryResult wheelQueryResults[PX_MAX_NB_WHEELS]; + + END_TIMER(TIMER_ADMIN); + START_TIMER(TIMER_COMPONENTS_UPDATE); + + //Center of mass local pose. + PxTransform carChassisCMLocalPose; + //Compute the transform of the center of mass. + PxTransform origCarChassisTransform; + PxTransform carChassisTransform; + //Inverse mass and inertia to apply the tire/suspension forces as impulses. + PxF32 inverseChassisMass; + PxVec3 inverseInertia; + //Linear and angular velocity. + PxVec3 carChassisLinVel; + PxVec3 carChassisAngVel; + { + carChassisCMLocalPose = vehActor->getCMassLocalPose(); + carChassisCMLocalPose.q = PxQuat(PxIdentity); + origCarChassisTransform = vehActor->getGlobalPose().transform(carChassisCMLocalPose); + carChassisTransform = origCarChassisTransform; + const PxF32 chassisMass = vehActor->getMass(); + inverseChassisMass = 1.0f/chassisMass; + inverseInertia = vehActor->getMassSpaceInvInertiaTensor(); + carChassisLinVel = vehActor->getLinearVelocity(); + carChassisAngVel = vehActor->getAngularVelocity(); + } + + //Get the local poses of the wheel shapes. + //These are the poses from the last frame and equal to the poses used for the raycast we will process. + PxQuat wheelLocalPoseRotations[PX_MAX_NB_WHEELS]; + PxF32 wheelThetas[PX_MAX_NB_WHEELS]; + { + for (PxU32 i = 0; i < numActiveWheels; i++) + { + const PxI32 shapeId = vehDrive4W->mWheelsSimData.getWheelShapeMapping(i); + if (-1 != shapeId) + { + PxShape* shape = NULL; + vehActor->getShapes(&shape, 1, PxU32(shapeId)); + wheelLocalPoseRotations[i] = shape->getLocalPose().q; + wheelThetas[i] = vehDrive4W->mWheelsDynData.getWheelRotationAngle(i); + } + } + } + + //Update the auto-box and decide whether to change gear up or down. + PxF32 autoboxCompensatedAnalogAccel = driveDynData.mControlAnalogVals[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]; + if(driveDynData.getUseAutoGears()) + { + autoboxCompensatedAnalogAccel = processAutoBox(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL,timestep,driveSimData,driveDynData); + } + + //Process gear-up/gear-down commands. + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + processGears(timestep,gearsData,driveDynData); + } + + //Clutch strength; + PxF32 K; + { + const PxVehicleClutchData& clutchData=driveSimData.getClutchData(); + const PxU32 currentGear=driveDynData.getCurrentGear(); + K=computeClutchStrength(clutchData, currentGear); + } + + //Clutch accuracy + PxVehicleClutchAccuracyMode::Enum clutchAccuracyMode; + PxU32 clutchMaxIterations; + { + const PxVehicleClutchData& clutchData=driveSimData.getClutchData(); + clutchAccuracyMode = clutchData.mAccuracyMode; + clutchMaxIterations = clutchData.mEstimateIterations; + } + + //Gear ratio. + PxF32 G; + PxU32 currentGear; + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + currentGear=driveDynData.getCurrentGear(); + G=computeGearRatio(gearsData,currentGear); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataGearRatio(G); +#endif + } + + //Retrieve control values from vehicle controls. + PxF32 accel,brake,handbrake,steerLeft,steerRight; + PxF32 steer; + bool isIntentionToAccelerate; + { + getVehicle4WControlValues(driveDynData,accel,brake,handbrake,steerLeft,steerRight); + steer=steerRight-steerLeft; + accel=autoboxCompensatedAnalogAccel; + isIntentionToAccelerate = (accel>0.0f && 0.0f==brake && 0.0f==handbrake && PxVehicleGearsData::eNEUTRAL != currentGear); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataControlInputs(accel,brake,handbrake,steerLeft,steerRight); +#endif + } + + //Active wheels (wheels which have not been disabled). + bool activeWheelStates[4]={false,false,false,false}; + { + computeWheelActiveStates(4*0, vehDrive4W->mWheelsSimData.mActiveWheelsBitmapBuffer, activeWheelStates); + } + + //Get the drive wheels (the first 4 wheels are the drive wheels). + const PxVehicleWheels4SimData& wheels4SimData=wheels4SimDatas[0]; + PxVehicleWheels4DynData& wheels4DynData=wheels4DynDatas[0]; + const PxVehicleTireForceCalculator4& tires4ForceCalculator=tires4ForceCalculators[0]; + + //Contribution of each driven wheel to average wheel speed at clutch. + //With 4 driven wheels the average wheel speed at clutch is + //wAve = alpha0*w0 + alpha1*w1 + alpha2*w2 + alpha3*w3. + //This next bit of code computes alpha0,alpha1,alpha2,alpha3. + //For rear wheel drive alpha0=alpha1=0 + //For front wheel drive alpha2=alpha3=0 + PxF32 aveWheelSpeedContributions[4]={0.0f,0.0f,0.0f,0.0f}; + { + const PxVehicleDifferential4WData& diffData=driveSimData.getDiffData(); + computeDiffAveWheelSpeedContributions(diffData,handbrake,aveWheelSpeedContributions); + +#if PX_DEBUG_VEHICLE_ON + updateGraphDataClutchSlip(wheels4DynData.mWheelSpeeds,aveWheelSpeedContributions,driveDynData.getEngineRotationSpeed(),G); +#endif + + PX_CHECK_AND_RETURN( + (activeWheelStates[0] || 0.0f==aveWheelSpeedContributions[0]) && + (activeWheelStates[1] || 0.0f==aveWheelSpeedContributions[1]) && + (activeWheelStates[2] || 0.0f==aveWheelSpeedContributions[2]) && + (activeWheelStates[3] || 0.0f==aveWheelSpeedContributions[3]), + "PxVehicleDifferential4WData must be configured so that no torque is delivered to a disabled wheel"); + } + + //Compute a per-wheel accelerator pedal value. + bool isAccelApplied[4]={false,false,false,false}; + if(isIntentionToAccelerate) + { + PX_ASSERT(accel>0); + computeIsAccelApplied(aveWheelSpeedContributions, isAccelApplied); + } + + //Ackermann-corrected steering angles. + //http://en.wikipedia.org/wiki/Ackermann_steering_geometry + PxF32 steerAngles[4]={0.0f,0.0f,0.0f,0.0f}; + { + computeAckermannCorrectedSteerAngles(driveSimData,wheels4SimData,steer,steerAngles); + } + + END_TIMER(TIMER_COMPONENTS_UPDATE); + START_TIMER(TIMER_ADMIN); + + //Store the susp line raycast data. + for(PxU32 i=0;imWheelsSimData,carChassisLinVel,carChassisTransform,gForward); + const PxF32 timeFraction=1.0f/(1.0f*numSubSteps); + const PxF32 subTimestep=timestep*timeFraction; + const PxF32 recipSubTimeStep=1.0f/subTimestep; + const PxF32 recipTimestep=1.0f/timestep; + const PxF32 minLongSlipDenominator=vehDrive4W->mWheelsSimData.mMinLongSlipDenominator; + ProcessSuspWheelTireConstData constData={timeFraction, subTimestep, recipSubTimeStep, gravity, gravityMagnitude, recipGravityMagnitude, false, minLongSlipDenominator, vehActor, &drivableSurfaceToTireFrictionPairs}; + + END_TIMER(TIMER_ADMIN); + + for(PxU32 k=0;k(inputData.vehWheels4DynData->mTireLowForwardSpeedTimers)); + updateLowSpeedTimers(outputData.newLowSideSpeedTimers, const_cast(inputData.vehWheels4DynData->mTireLowSideSpeedTimers)); + updateJounces(outputData.jounces, const_cast(inputData.vehWheels4DynData->mJounces)); + if((numSubSteps-1) == k) + { + updateCachedHitData(outputData.cachedHitCounts, outputData.cachedHitPlanes, outputData.cachedHitDistances, outputData.cachedFrictionMultipliers, outputData.cachedHitQueryTypes, &wheels4DynData); + } + chassisForce+=outputData.chassisForce; + chassisTorque+=outputData.chassisTorque; + if(0 == k) + { + wheels4DynData.mVehicleConstraints->mData=outputData.vehConstraintData; + } + storeSuspWheelTireResults(outputData, inputData.steerAngles, &wheelQueryResults[4*0], numActiveWheelsPerBlock4[0]); + storeHitActorForces(outputData, &vehicleConcurrentUpdates.concurrentWheelUpdates[4*0], numActiveWheelsPerBlock4[0]); + + END_TIMER(TIMER_WHEELS); + START_TIMER(TIMER_INTERNAL_DYNAMICS_SOLVER); + + //Diff torque ratios needed (how we split the torque between the drive wheels). + //The sum of the torque ratios is always 1.0f. + //The drive torque delivered to each wheel is the total available drive torque multiplied by the + //diff torque ratio for each wheel. + PxF32 diffTorqueRatios[4]={0.0f,0.0f,0.0f,0.0f}; + computeDiffTorqueRatios(driveSimData.getDiffData(),handbrake,wheels4DynData.mWheelSpeeds,diffTorqueRatios); + + PX_CHECK_AND_RETURN( + (activeWheelStates[0] || 0.0f==diffTorqueRatios[0]) && + (activeWheelStates[1] || 0.0f==diffTorqueRatios[1]) && + (activeWheelStates[2] || 0.0f==diffTorqueRatios[2]) && + (activeWheelStates[3] || 0.0f==diffTorqueRatios[3]), + "PxVehicleDifferential4WData must be configured so that no torque is delivered to a disabled wheel"); + + PxF32 engineDriveTorque; + { + const PxVehicleEngineData& engineData=driveSimData.getEngineData(); + const PxF32 engineOmega=driveDynData.getEngineRotationSpeed(); + engineDriveTorque=computeEngineDriveTorque(engineData,engineOmega,accel); + #if PX_DEBUG_VEHICLE_ON + updateGraphDataEngineDriveTorque(engineDriveTorque); + #endif + } + + PxF32 engineDampingRate; + { + const PxVehicleEngineData& engineData=driveSimData.getEngineData(); + engineDampingRate=computeEngineDampingRate(engineData,currentGear,accel); + } + + //Update the wheel and engine speeds - 5x5 matrix coupling engine and wheels. + ImplicitSolverInput implicitSolverInput= + { + subTimestep, + brake, handbrake, + K, G, + clutchAccuracyMode, clutchMaxIterations, + engineDriveTorque, engineDampingRate, + diffTorqueRatios, aveWheelSpeedContributions, + brakeTorques, isBrakeApplied, outputData.tireTorques, + 1, 4, + &wheels4SimData, &driveSimData + }; + ImplicitSolverOutput implicitSolverOutput= + { + &wheels4DynData, &driveDynData + }; + solveDrive4WInternaDynamicsEnginePlusDrivenWheels(implicitSolverInput, &implicitSolverOutput); + + END_TIMER(TIMER_INTERNAL_DYNAMICS_SOLVER); + START_TIMER(TIMER_POSTUPDATE1); + + //Integrate wheel rotation angle (theta += omega*dt) + integrateWheelRotationAngles + (subTimestep, + K,G,engineDriveTorque, + outputData.jounces,diffTorqueRatios,outputData.forwardSpeeds,isBrakeApplied, + driveSimData,wheels4SimData, + driveDynData,wheels4DynData); + } + + END_TIMER(TIMER_POSTUPDATE1); + + ////////////////////////////////////////////////////////////////////////// + //susp and tire forces from extra wheels (non-driven wheels) + ////////////////////////////////////////////////////////////////////////// + for(PxU32 j=1;jmWheelsSimData.mActiveWheelsBitmapBuffer, extraWheelActiveStates); + + ProcessSuspWheelTireInputData extraInputData= + { + isIntentionToAccelerate, extraIsAccelApplied, extraIsBrakeApplied, extraWheelSteerAngles, extraWheelActiveStates, + carChassisTransform, carChassisLinVel, carChassisAngVel, + &wheelLocalPoseRotations[j], &wheelThetas[j], &wheels4SimDatas[j], &wheels4DynDatas[j], &tires4ForceCalculators[j], &tireLoadFilterData, numActiveWheelsPerBlock4[j], + }; + ProcessSuspWheelTireOutputData extraOutputData; + processSuspTireWheels(4*j, constData, extraInputData, extraOutputData); + updateLowSpeedTimers(extraOutputData.newLowForwardSpeedTimers, const_cast(extraInputData.vehWheels4DynData->mTireLowForwardSpeedTimers)); + updateLowSpeedTimers(extraOutputData.newLowSideSpeedTimers, const_cast(extraInputData.vehWheels4DynData->mTireLowSideSpeedTimers)); + updateJounces(extraOutputData.jounces, const_cast(extraInputData.vehWheels4DynData->mJounces)); + if((numSubSteps-1) == k) + { + updateCachedHitData(extraOutputData.cachedHitCounts, extraOutputData.cachedHitPlanes, extraOutputData.cachedHitDistances, extraOutputData.cachedFrictionMultipliers, extraOutputData.cachedHitQueryTypes, &wheels4DynDatas[j]); + } + chassisForce+=extraOutputData.chassisForce; + chassisTorque+=extraOutputData.chassisTorque; + if(0 == k) + { + wheels4DynDatas[j].mVehicleConstraints->mData=extraOutputData.vehConstraintData; + } + storeSuspWheelTireResults(extraOutputData, extraInputData.steerAngles, &wheelQueryResults[4*j], numActiveWheelsPerBlock4[j]); + storeHitActorForces(extraOutputData, &vehicleConcurrentUpdates.concurrentWheelUpdates[4*j], numActiveWheelsPerBlock4[j]); + + //Integrate the tire torques (omega += (tireTorque + brakeTorque)*dt) + integrateUndriveWheelRotationSpeeds(subTimestep, brake, handbrake, extraOutputData.tireTorques, extraWheelBrakeTorques, wheels4SimDatas[j], wheels4DynDatas[j]); + + //Integrate wheel rotation angle (theta += omega*dt) + integrateWheelRotationAngles + (subTimestep, + 0,0,0, + extraOutputData.jounces,extraWheelsDiffTorqueRatios,extraOutputData.forwardSpeeds,extraIsBrakeApplied, + driveSimData,wheels4SimDatas[j], + driveDynData,wheels4DynDatas[j]); + } + + START_TIMER(TIMER_POSTUPDATE2); + + //Apply the anti-roll suspension. + procesAntiRollSuspension(vehDrive4W->mWheelsSimData, carChassisTransform, wheelQueryResults, chassisTorque); + + //Integrate one sustep. + integrateBody(inverseChassisMass, inverseInertia ,chassisForce, chassisTorque, subTimestep, carChassisLinVel, carChassisAngVel, carChassisTransform); + + END_TIMER(TIMER_POSTUPDATE2); + } + + START_TIMER(TIMER_POSTUPDATE3); + + //Set the new chassis linear/angular velocity. + if(!gApplyForces) + { + vehicleConcurrentUpdates.linearMomentumChange = carChassisLinVel; + vehicleConcurrentUpdates.angularMomentumChange = carChassisAngVel; + } + else + { + //integration steps are: + //v = v0 + a*dt (1) + //x = x0 + v*dt (2) + //Sub (2) into (1. + //x = x0 + v0*dt + a*dt*dt; + //Rearrange for a + //a = (x -x0 - v0*dt)/(dt*dt) = [(x-x0)/dt - v0/dt] + //Rearrange again with v = (x-x0)/dt + //a = (v - v0)/dt + vehicleConcurrentUpdates.linearMomentumChange = (carChassisLinVel-carChassisLinVelOrig)*recipTimestep;; + vehicleConcurrentUpdates.angularMomentumChange = (carChassisAngVel-carChassisAngVelOrig)*recipTimestep;; + } + + //Compute and pose the wheels from jounces, rotations angles, and steer angles. + PxTransform localPoses0[4] = {PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity)}; + computeWheelLocalPoses(wheels4SimDatas[0],wheels4DynDatas[0],&wheelQueryResults[4*0],numActiveWheelsPerBlock4[0],carChassisCMLocalPose,localPoses0); + //Copy the poses to the wheelQueryResults + wheelQueryResults[4*0 + 0].localPose = localPoses0[0]; + wheelQueryResults[4*0 + 1].localPose = localPoses0[1]; + wheelQueryResults[4*0 + 2].localPose = localPoses0[2]; + wheelQueryResults[4*0 + 3].localPose = localPoses0[3]; + //Copy the poses to the concurrent update data. + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 0].localPose = localPoses0[0]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 1].localPose = localPoses0[1]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 2].localPose = localPoses0[2]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 3].localPose = localPoses0[3]; + for(PxU32 i=1;iwheelQueryResults) + { + PxMemCopy(vehWheelQueryResults->wheelQueryResults, wheelQueryResults, sizeof(PxWheelQueryResult)*numActiveWheels); + } + + if(vehConcurrentUpdates) + { + //Copy across to input data structure so that writes can be applied later. + PxMemCopy(vehConcurrentUpdates->concurrentWheelUpdates, vehicleConcurrentUpdates.concurrentWheelUpdates, sizeof(PxVehicleWheelConcurrentUpdateData)*numActiveWheels); + vehConcurrentUpdates->linearMomentumChange = vehicleConcurrentUpdates.linearMomentumChange; + vehConcurrentUpdates->angularMomentumChange = vehicleConcurrentUpdates.angularMomentumChange; + vehConcurrentUpdates->staySleeping = vehicleConcurrentUpdates.staySleeping; + vehConcurrentUpdates->wakeup = vehicleConcurrentUpdates.wakeup; + } + else + { + //Apply the writes immediately. + PxVehicleWheels* vehWheels[1]={vehDrive4W}; + PxVehiclePostUpdates(&vehicleConcurrentUpdates, 1, vehWheels); + } + + END_TIMER(TIMER_POSTUPDATE3); +} + +void PxVehicleUpdate::updateDriveNW +(const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleDriveNW* vehDriveNW, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates) +{ + PX_SIMD_GUARD; // denorm exception triggered in transformInertiaTensor() on osx + + START_TIMER(TIMER_ADMIN); + + PX_CHECK_AND_RETURN( + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_ACCEL]>-0.01f && + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_ACCEL]<1.01f, + "Illegal vehicle control value - accel must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_BRAKE]>-0.01f && + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_BRAKE]<1.01f, + "Illegal vehicle control value - brake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_HANDBRAKE]>-0.01f && + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_HANDBRAKE]<1.01f, + "Illegal vehicle control value - handbrake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_LEFT]>-1.01f && + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_LEFT]<1.01f, + "Illegal vehicle control value - left steer must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_RIGHT]>-1.01f && + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_RIGHT]<1.01f, + "Illegal vehicle control value - right steer must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + PxAbs(vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_RIGHT]- + vehDriveNW->mDriveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_STEER_LEFT])<1.01f, + "Illegal vehicle control value - right steer value minus left steer value must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + !(vehDriveNW->getRigidDynamicActor()->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC), + "Attempting to update a drive4W with a kinematic actor - this isn't allowed"); + PX_CHECK_AND_RETURN( + NULL==vehWheelQueryResults || vehWheelQueryResults->nbWheelQueryResults >= vehDriveNW->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + +#if PX_CHECKED + for(PxU32 i=0;imWheelsSimData.getNbWheels();i++) + { + PX_CHECK_AND_RETURN(!vehDriveNW->mWheelsSimData.getIsWheelDisabled(i) || !vehDriveNW->mDriveSimData.getDiffData().getIsDrivenWheel(i), + "PxVehicleDifferentialNWData must be configured so that no torque is delivered to a disabled wheel"); + } +#endif + + END_TIMER(TIMER_ADMIN); + START_TIMER(TIMER_GRAPHS); + +#if PX_DEBUG_VEHICLE_ON + for(PxU32 i=0;imWheelsSimData.mNbWheels4;i++) + { + updateGraphDataInternalWheelDynamics(4*i,vehDriveNW->mWheelsDynData.mWheels4DynData[i].mWheelSpeeds); + } + updateGraphDataInternalEngineDynamics(vehDriveNW->mDriveDynData.getEngineRotationSpeed()); +#endif + + END_TIMER(TIMER_GRAPHS); + START_TIMER(TIMER_ADMIN); + + //Unpack the vehicle. + //Unpack the NW simulation and instanced dynamics components. + const PxVehicleWheels4SimData* wheels4SimDatas=vehDriveNW->mWheelsSimData.mWheels4SimData; + const PxVehicleTireLoadFilterData& tireLoadFilterData=vehDriveNW->mWheelsSimData.mNormalisedLoadFilter; + PxVehicleWheels4DynData* wheels4DynDatas=vehDriveNW->mWheelsDynData.mWheels4DynData; + const PxU32 numWheels4=vehDriveNW->mWheelsSimData.mNbWheels4; + const PxU32 numActiveWheels=vehDriveNW->mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=4-(4*numWheels4 - numActiveWheels); + const PxVehicleDriveSimDataNW driveSimData=vehDriveNW->mDriveSimData; + PxVehicleDriveDynData& driveDynData=vehDriveNW->mDriveDynData; + PxRigidDynamic* vehActor=vehDriveNW->mActor; + + //We need to store that data we are going to write to actors so we can do this at the end in one go with fewer write locks. + PxVehicleWheelConcurrentUpdateData wheelConcurrentUpdates[PX_MAX_NB_WHEELS]; + PxVehicleConcurrentUpdateData vehicleConcurrentUpdates; + vehicleConcurrentUpdates.nbConcurrentWheelUpdates = numActiveWheels; + vehicleConcurrentUpdates.concurrentWheelUpdates = wheelConcurrentUpdates; + + //In each block of 4 wheels record how many wheels are active. + PxU32 numActiveWheelsPerBlock4[PX_MAX_NB_SUSPWHEELTIRE4]={0,0,0,0,0}; + numActiveWheelsPerBlock4[0]=PxMin(numActiveWheels,PxU32(4)); + for(PxU32 i=1;iisSleeping()) + { + if(finiteInputApplied) + { + //Driving inputs so we need the actor to start moving. + vehicleConcurrentUpdates.wakeup = true; + } + else if(isOnDynamicActor(vehDriveNW->mWheelsSimData, vehDriveNW->mWheelsDynData)) + { + //Driving on dynamic so we need to keep moving. + vehicleConcurrentUpdates.wakeup = true; + } + else + { + //No driving inputs and the actor is asleep. + //Set internal dynamics to sleep. + setInternalDynamicsToZero(vehDriveNW); + if(vehConcurrentUpdates) vehConcurrentUpdates->staySleeping = true; + return; + } + } + } + + //Organise the shader data in blocks of 4. + PxVehicleTireForceCalculator4 tires4ForceCalculators[PX_MAX_NB_SUSPWHEELTIRE4]; + for(PxU32 i=0;imWheelsDynData.mTireForceCalculators->mShaderData[4*i+0]; + tires4ForceCalculators[i].mShaderData[1]=vehDriveNW->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+1]; + tires4ForceCalculators[i].mShaderData[2]=vehDriveNW->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+2]; + tires4ForceCalculators[i].mShaderData[3]=vehDriveNW->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+3]; + tires4ForceCalculators[i].mShader=vehDriveNW->mWheelsDynData.mTireForceCalculators->mShader; + } + + //Mark the constraints as dirty to force them to be updated in the sdk. + for(PxU32 i=0;imarkDirty(); + } + + //Need to store report data to pose the wheels. + PxWheelQueryResult wheelQueryResults[PX_MAX_NB_WHEELS]; + + END_TIMER(TIMER_ADMIN); + START_TIMER(TIMER_COMPONENTS_UPDATE); + + //Center of mass local pose. + PxTransform carChassisCMLocalPose; + //Compute the transform of the center of mass. + PxTransform origCarChassisTransform; + PxTransform carChassisTransform; + //Inverse mass and inertia to apply the tire/suspension forces as impulses. + PxF32 inverseChassisMass; + PxVec3 inverseInertia; + //Linear and angular velocity. + PxVec3 carChassisLinVel; + PxVec3 carChassisAngVel; + { + carChassisCMLocalPose = vehActor->getCMassLocalPose(); + carChassisCMLocalPose.q = PxQuat(PxIdentity); + origCarChassisTransform = vehActor->getGlobalPose().transform(carChassisCMLocalPose); + carChassisTransform = origCarChassisTransform; + const PxF32 chassisMass = vehActor->getMass(); + inverseChassisMass = 1.0f/chassisMass; + inverseInertia = vehActor->getMassSpaceInvInertiaTensor(); + carChassisLinVel = vehActor->getLinearVelocity(); + carChassisAngVel = vehActor->getAngularVelocity(); + } + + //Get the local poses of the wheel shapes. + //These are the poses from the last frame and equal to the poses used for the raycast we will process. + PxQuat wheelLocalPoseRotations[PX_MAX_NB_WHEELS]; + PxF32 wheelThetas[PX_MAX_NB_WHEELS]; + { + for (PxU32 i = 0; i < numActiveWheels; i++) + { + const PxI32 shapeId = vehDriveNW->mWheelsSimData.getWheelShapeMapping(i); + if (-1 != shapeId) + { + PxShape* shape = NULL; + vehActor->getShapes(&shape, 1, PxU32(shapeId)); + wheelLocalPoseRotations[i] = shape->getLocalPose().q; + wheelThetas[i] = vehDriveNW->mWheelsDynData.getWheelRotationAngle(i); + } + } + } + + //Update the auto-box and decide whether to change gear up or down. + PxF32 autoboxCompensatedAnalogAccel = driveDynData.mControlAnalogVals[PxVehicleDriveNWControl::eANALOG_INPUT_ACCEL]; + if(driveDynData.getUseAutoGears()) + { + autoboxCompensatedAnalogAccel = processAutoBox(PxVehicleDriveNWControl::eANALOG_INPUT_ACCEL,timestep,driveSimData,driveDynData); + } + + //Process gear-up/gear-down commands. + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + processGears(timestep,gearsData,driveDynData); + } + + //Clutch strength. + PxF32 K; + { + const PxVehicleClutchData& clutchData=driveSimData.getClutchData(); + const PxU32 currentGear=driveDynData.getCurrentGear(); + K=computeClutchStrength(clutchData, currentGear); + } + + //Clutch accuracy. + PxVehicleClutchAccuracyMode::Enum clutchAccuracyMode; + PxU32 clutchMaxIterations; + { + const PxVehicleClutchData& clutchData=driveSimData.getClutchData(); + clutchAccuracyMode=clutchData.mAccuracyMode; + clutchMaxIterations=clutchData.mEstimateIterations; + } + + //Gear ratio. + PxF32 G; + PxU32 currentGear; + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + currentGear=driveDynData.getCurrentGear(); + G=computeGearRatio(gearsData,currentGear); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataGearRatio(G); +#endif + } + + //Retrieve control values from vehicle controls. + PxF32 accel,brake,handbrake,steerLeft,steerRight; + PxF32 steer; + bool isIntentionToAccelerate; + { + getVehicleNWControlValues(driveDynData,accel,brake,handbrake,steerLeft,steerRight); + steer=steerRight-steerLeft; + accel=autoboxCompensatedAnalogAccel; + isIntentionToAccelerate = (accel>0.0f && 0.0f==brake && 0.0f==handbrake && PxVehicleGearsData::eNEUTRAL != currentGear); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataControlInputs(accel,brake,handbrake,steerLeft,steerRight); +#endif + } + + //Compute the wheels that are disabled or enabled. + bool activeWheelStates[PX_MAX_NB_WHEELS]; + PxMemSet(activeWheelStates, 0, sizeof(bool)*PX_MAX_NB_WHEELS); + for(PxU32 i=0;imWheelsSimData.mActiveWheelsBitmapBuffer, &activeWheelStates[4*i]); + } + + //Contribution of each driven wheel to average wheel speed at clutch. + //For NW drive equal torque split is supported. + const PxVehicleDifferentialNWData diffData=driveSimData.getDiffData(); + const PxF32 invNumDrivenWheels=diffData.mInvNbDrivenWheels; + PxF32 aveWheelSpeedContributions[PX_MAX_NB_WHEELS]; + PxMemSet(aveWheelSpeedContributions, 0, sizeof(PxF32)*PX_MAX_NB_WHEELS); + for(PxU32 i=0;i0); + for(PxU32 i=0;imWheelsSimData.getWheelData(i); + const PxF32 steerGain=wheelData.mMaxSteer; + const PxF32 toe=wheelData.mToeAngle; + steerAngles[i]=steerGain*steer + toe; + } + + //Diff torque ratios needed (how we split the torque between the drive wheels). + //The sum of the torque ratios is always 1.0f. + //The drive torque delivered to each wheel is the total available drive torque multiplied by the + //diff torque ratio for each wheel. + PxF32 diffTorqueRatios[PX_MAX_NB_WHEELS]; + PxMemSet(diffTorqueRatios, 0, sizeof(PxF32)*PX_MAX_NB_WHEELS); + for(PxU32 i=0;imWheelsSimData,carChassisLinVel,carChassisTransform,gForward); + const PxF32 timeFraction=1.0f/(1.0f*numSubSteps); + const PxF32 subTimestep=timestep*timeFraction; + const PxF32 recipSubTimeStep=1.0f/subTimestep; + const PxF32 recipTimestep=1.0f/timestep; + const PxF32 minLongSlipDenominator=vehDriveNW->mWheelsSimData.mMinLongSlipDenominator; + ProcessSuspWheelTireConstData constData={timeFraction, subTimestep, recipSubTimeStep, gravity, gravityMagnitude, recipGravityMagnitude, false, minLongSlipDenominator, vehActor, &drivableSurfaceToTireFrictionPairs}; + + END_TIMER(TIMER_ADMIN); + + for(PxU32 k=0;k(inputData.vehWheels4DynData->mTireLowForwardSpeedTimers)); + updateLowSpeedTimers(outputData[i].newLowSideSpeedTimers, const_cast(inputData.vehWheels4DynData->mTireLowSideSpeedTimers)); + updateJounces(outputData[i].jounces, const_cast(inputData.vehWheels4DynData->mJounces)); + if((numSubSteps-1) == k) + { + updateCachedHitData(outputData[i].cachedHitCounts, outputData[i].cachedHitPlanes, outputData[i].cachedHitDistances, outputData[i].cachedFrictionMultipliers, outputData[i].cachedHitQueryTypes, &wheels4DynDatas[i]); + } + chassisForce+=outputData[i].chassisForce; + chassisTorque+=outputData[i].chassisTorque; + if(0 == k) + { + wheels4DynDatas[i].mVehicleConstraints->mData=outputData[i].vehConstraintData; + } + storeSuspWheelTireResults(outputData[i], inputData.steerAngles, &wheelQueryResults[4*i], numActiveWheelsPerBlock4[i]); + storeHitActorForces(outputData[i], &vehicleConcurrentUpdates.concurrentWheelUpdates[4*i], numActiveWheelsPerBlock4[i]); + } + + //Store the tire torques in a single array. + PxF32 tireTorques[PX_MAX_NB_WHEELS]; + for(PxU32 i=0;imWheelsSimData, carChassisTransform, wheelQueryResults, chassisTorque); + + //Integrate the chassis velocity by applying the accumulated force and torque. + integrateBody(inverseChassisMass, inverseInertia, chassisForce, chassisTorque, subTimestep, carChassisLinVel, carChassisAngVel, carChassisTransform); + + END_TIMER(TIMER_POSTUPDATE2); + } + + //Set the new chassis linear/angular velocity. + if(!gApplyForces) + { + vehicleConcurrentUpdates.linearMomentumChange = carChassisLinVel; + vehicleConcurrentUpdates.angularMomentumChange = carChassisAngVel; + } + else + { + //integration steps are: + //v = v0 + a*dt (1) + //x = x0 + v*dt (2) + //Sub (2) into (1. + //x = x0 + v0*dt + a*dt*dt; + //Rearrange for a + //a = (x -x0 - v0*dt)/(dt*dt) = [(x-x0)/dt - v0/dt] + //Rearrange again with v = (x-x0)/dt + //a = (v - v0)/dt + vehicleConcurrentUpdates.linearMomentumChange = (carChassisLinVel-carChassisLinVelOrig)*recipTimestep; + vehicleConcurrentUpdates.angularMomentumChange = (carChassisAngVel-carChassisAngVelOrig)*recipTimestep; + } + + START_TIMER(TIMER_POSTUPDATE3); + + //Pose the wheels from jounces, rotations angles, and steer angles. + PxTransform localPoses0[4] = {PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity)}; + computeWheelLocalPoses(wheels4SimDatas[0],wheels4DynDatas[0],&wheelQueryResults[4*0],numActiveWheelsPerBlock4[0],carChassisCMLocalPose,localPoses0); + wheelQueryResults[4*0 + 0].localPose = localPoses0[0]; + wheelQueryResults[4*0 + 1].localPose = localPoses0[1]; + wheelQueryResults[4*0 + 2].localPose = localPoses0[2]; + wheelQueryResults[4*0 + 3].localPose = localPoses0[3]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 0].localPose = localPoses0[0]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 1].localPose = localPoses0[1]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 2].localPose = localPoses0[2]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 3].localPose = localPoses0[3]; + for(PxU32 i=1;iwheelQueryResults) + { + PxMemCopy(vehWheelQueryResults->wheelQueryResults, wheelQueryResults, sizeof(PxWheelQueryResult)*numActiveWheels); + } + + if(vehConcurrentUpdates) + { + //Copy across to input data structure so that writes can be applied later. + PxMemCopy(vehConcurrentUpdates->concurrentWheelUpdates, vehicleConcurrentUpdates.concurrentWheelUpdates, sizeof(PxVehicleWheelConcurrentUpdateData)*numActiveWheels); + vehConcurrentUpdates->linearMomentumChange = vehicleConcurrentUpdates.linearMomentumChange; + vehConcurrentUpdates->angularMomentumChange = vehicleConcurrentUpdates.angularMomentumChange; + vehConcurrentUpdates->staySleeping = vehicleConcurrentUpdates.staySleeping; + vehConcurrentUpdates->wakeup = vehicleConcurrentUpdates.wakeup; + } + else + { + //Apply the writes immediately. + PxVehicleWheels* vehWheels[1]={vehDriveNW}; + PxVehiclePostUpdates(&vehicleConcurrentUpdates, 1, vehWheels); + } + + END_TIMER(TIMER_POSTUPDATE3); +} + +void PxVehicleUpdate::updateTank +(const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleDriveTank* vehDriveTank, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates) +{ + PX_SIMD_GUARD; // denorm exception in transformInertiaTensor() + + PX_CHECK_AND_RETURN( + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL]>-0.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL]<1.01f, + "Illegal tank control value - accel must be in range (0,1)" ); + PX_CHECK_AND_RETURN( + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT]>-0.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT]<1.01f, + "Illegal tank control value - left brake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT]>-0.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT]<1.01f, + "Illegal tank control right value - right brake must be in range (0,1)"); + PX_CHECK_AND_RETURN( + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]>-1.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]<1.01f, + "Illegal tank control value - left thrust must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]>-1.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]<1.01f, + "Illegal tank control value - right thrust must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + PxVehicleDriveTankControlModel::eSPECIAL==vehDriveTank->mDriveModel || + (vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]>-0.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]<1.01f), + "Illegal tank control value - left thrust must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + PxVehicleDriveTankControlModel::eSPECIAL==vehDriveTank->mDriveModel || + (vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]>-0.01f && + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]<1.01f), + "Illegal tank control value - right thrust must be in range (-1,1)"); + PX_CHECK_AND_RETURN( + PxVehicleDriveTankControlModel::eSPECIAL==vehDriveTank->mDriveModel || + 0.0f== + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]* + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT], + "Illegal tank control value - thrust left and brake left simultaneously non-zero in standard drive mode"); + PX_CHECK_AND_RETURN( + PxVehicleDriveTankControlModel::eSPECIAL==vehDriveTank->mDriveModel || + 0.0f== + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]* + vehDriveTank->mDriveDynData.mControlAnalogVals[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT], + "Illegal tank control value - thrust right and brake right simultaneously non-zero in standard drive mode"); + PX_CHECK_AND_RETURN( + !(vehDriveTank->getRigidDynamicActor()->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC), + "Attempting to update a tank with a kinematic actor - this isn't allowed"); + PX_CHECK_AND_RETURN( + NULL==vehWheelQueryResults || vehWheelQueryResults->nbWheelQueryResults >= vehDriveTank->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + +#if PX_CHECKED + { + PxVec3 fl=vehDriveTank->mWheelsSimData.getWheelCentreOffset(PxVehicleDriveTankWheelOrder::eFRONT_LEFT); + PxVec3 fr=vehDriveTank->mWheelsSimData.getWheelCentreOffset(PxVehicleDriveTankWheelOrder::eFRONT_RIGHT); + const PxVec3 right=gRight; + const PxF32 s0=computeSign((fr-fl).dot(right)); + for(PxU32 i=PxVehicleDriveTankWheelOrder::e1ST_FROM_FRONT_LEFT;imWheelsSimData.getNbWheels();i+=2) + { + PxVec3 rl=vehDriveTank->mWheelsSimData.getWheelCentreOffset(i); + PxVec3 rr=vehDriveTank->mWheelsSimData.getWheelCentreOffset(i+1); + const PxF32 t0=computeSign((rr-rl).dot(right)); + PX_CHECK_AND_RETURN(s0==t0 || 0==s0 || 0==t0, "Tank wheels must be ordered with odd wheels on one side and even wheels on the other side"); + } + } +#endif + + +#if PX_DEBUG_VEHICLE_ON + for(PxU32 i=0;imWheelsSimData.mNbWheels4;i++) + { + updateGraphDataInternalWheelDynamics(4*i,vehDriveTank->mWheelsDynData.mWheels4DynData[i].mWheelSpeeds); + } + updateGraphDataInternalEngineDynamics(vehDriveTank->mDriveDynData.getEngineRotationSpeed()); +#endif + + //Unpack the tank simulation and instanced dynamics components. + const PxVehicleWheels4SimData* wheels4SimDatas=vehDriveTank->mWheelsSimData.mWheels4SimData; + const PxVehicleTireLoadFilterData& tireLoadFilterData=vehDriveTank->mWheelsSimData.mNormalisedLoadFilter; + PxVehicleWheels4DynData* wheels4DynDatas=vehDriveTank->mWheelsDynData.mWheels4DynData; + const PxU32 numWheels4=vehDriveTank->mWheelsSimData.mNbWheels4; + const PxU32 numActiveWheels=vehDriveTank->mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=4-(4*numWheels4-numActiveWheels); + const PxVehicleDriveSimData driveSimData=vehDriveTank->mDriveSimData; + PxVehicleDriveDynData& driveDynData=vehDriveTank->mDriveDynData; + PxRigidDynamic* vehActor=vehDriveTank->mActor; + + //We need to store that data we are going to write to actors so we can do this at the end in one go with fewer write locks. + PxVehicleWheelConcurrentUpdateData wheelConcurrentUpdates[PX_MAX_NB_WHEELS]; + PxVehicleConcurrentUpdateData vehicleConcurrentUpdates; + vehicleConcurrentUpdates.nbConcurrentWheelUpdates = numActiveWheels; + vehicleConcurrentUpdates.concurrentWheelUpdates = wheelConcurrentUpdates; + + //Test if a non-zero drive torque was applied or if a non-zero steer angle was applied. + bool finiteInputApplied=false; + if(0!=driveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT) || + 0!=driveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT) || + 0!=driveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL) || + driveDynData.getGearDown() || driveDynData.getGearUp()) + { + finiteInputApplied=true; + } + + //Awake or sleep. + { + if(vehActor->isSleeping()) + { + if(finiteInputApplied) + { + //Driving inputs so we need the actor to start moving. + vehicleConcurrentUpdates.wakeup = true; + } + else if(isOnDynamicActor(vehDriveTank->mWheelsSimData, vehDriveTank->mWheelsDynData)) + { + //Driving on dynamic so we need to keep moving. + vehicleConcurrentUpdates.wakeup = true; + } + else + { + //No driving inputs and the actor is asleep. + //Set internal dynamics to sleep. + setInternalDynamicsToZero(vehDriveTank); + if(vehConcurrentUpdates) vehConcurrentUpdates->staySleeping = true; + return; + } + } + } + + //In each block of 4 wheels record how many wheels are active. + PxU32 numActiveWheelsPerBlock4[PX_MAX_NB_SUSPWHEELTIRE4]={0,0,0,0,0}; + numActiveWheelsPerBlock4[0]=PxMin(numActiveWheels,PxU32(4)); + for(PxU32 i=1;imWheelsDynData.mTireForceCalculators->mShaderData[4*i+0]; + tires4ForceCalculators[i].mShaderData[1]=vehDriveTank->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+1]; + tires4ForceCalculators[i].mShaderData[2]=vehDriveTank->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+2]; + tires4ForceCalculators[i].mShaderData[3]=vehDriveTank->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+3]; + tires4ForceCalculators[i].mShader=vehDriveTank->mWheelsDynData.mTireForceCalculators->mShader; + } + + //Mark the suspension/tire constraints as dirty to force them to be updated in the sdk. + for(PxU32 i=0;imarkDirty(); + } + + //Need to store report data to pose the wheels. + PxWheelQueryResult wheelQueryResults[PX_MAX_NB_WHEELS]; + + + //Center of mass local pose. + PxTransform carChassisCMLocalPose; + //Compute the transform of the center of mass. + PxTransform origCarChassisTransform; + PxTransform carChassisTransform; + //Inverse mass and inertia to apply the tire/suspension forces as impulses. + PxF32 inverseChassisMass; + PxVec3 inverseInertia; + //Linear and angular velocity. + PxVec3 carChassisLinVel; + PxVec3 carChassisAngVel; + { + carChassisCMLocalPose = vehActor->getCMassLocalPose(); + carChassisCMLocalPose.q = PxQuat(PxIdentity); + origCarChassisTransform = vehActor->getGlobalPose().transform(carChassisCMLocalPose); + carChassisTransform = origCarChassisTransform; + const PxF32 chassisMass = vehActor->getMass(); + inverseChassisMass = 1.0f/chassisMass; + inverseInertia = vehActor->getMassSpaceInvInertiaTensor(); + carChassisLinVel = vehActor->getLinearVelocity(); + carChassisAngVel = vehActor->getAngularVelocity(); + } + + //Get the local poses of the wheel shapes. + //These are the poses from the last frame and equal to the poses used for the raycast we will process. + PxQuat wheelLocalPoseRotations[PX_MAX_NB_WHEELS]; + PxF32 wheelThetas[PX_MAX_NB_WHEELS]; + { + for (PxU32 i = 0; i < numActiveWheels; i++) + { + const PxI32 shapeId = vehDriveTank->mWheelsSimData.getWheelShapeMapping(i); + if (-1 != shapeId) + { + PxShape* shape = NULL; + vehActor->getShapes(&shape, 1, PxU32(shapeId)); + wheelLocalPoseRotations[i] = shape->getLocalPose().q; + wheelThetas[i] = vehDriveTank->mWheelsDynData.getWheelRotationAngle(i); + } + } + } + + + //Retrieve control values from vehicle controls. + PxF32 accel,brakeLeft,brakeRight,thrustLeft,thrustRight; + { + getTankControlValues(driveDynData,accel,brakeLeft,brakeRight,thrustLeft,thrustRight); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataControlInputs(accel,brakeLeft,brakeRight,thrustLeft,thrustRight); +#endif + } + + //Update the auto-box and decide whether to change gear up or down. + //If the tank is supposed to turn sharply don't process the auto-box. + bool useAutoGears; + if(vehDriveTank->getDriveModel()==PxVehicleDriveTankControlModel::eSPECIAL) + { + useAutoGears = driveDynData.getUseAutoGears() ? ((((thrustRight*thrustLeft) >= 0.0f) || (0.0f==thrustLeft && 0.0f==thrustRight)) ? true : false) : false; + } + else + { + useAutoGears = driveDynData.getUseAutoGears() ? (thrustRight*brakeLeft>0 || thrustLeft*brakeRight>0 ? false : true) : false; + } + if(useAutoGears) + { + processAutoBox(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL,timestep,driveSimData,driveDynData); + } + + //Process gear-up/gear-down commands. + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + processGears(timestep,gearsData,driveDynData); + } + + //Clutch strength; + PxF32 K; + { + const PxVehicleClutchData& clutchData=driveSimData.getClutchData(); + const PxU32 currentGear=driveDynData.getCurrentGear(); + K=computeClutchStrength(clutchData, currentGear); + } + + //Gear ratio. + PxF32 G; + PxU32 currentGear; + { + const PxVehicleGearsData& gearsData=driveSimData.getGearsData(); + currentGear=driveDynData.getCurrentGear(); + G=computeGearRatio(gearsData,currentGear); +#if PX_DEBUG_VEHICLE_ON + updateGraphDataGearRatio(G); +#endif + } + + bool isIntentionToAccelerate; + { + const PxF32 thrustLeftAbs=PxAbs(thrustLeft); + const PxF32 thrustRightAbs=PxAbs(thrustRight); + isIntentionToAccelerate = (accel*(thrustLeftAbs+thrustRightAbs)>0 && PxVehicleGearsData::eNEUTRAL != currentGear); + } + + + //Compute the wheels that are enabled/disabled. + bool activeWheelStates[PX_MAX_NB_WHEELS]; + PxMemZero(activeWheelStates, sizeof(bool)*PX_MAX_NB_WHEELS); + for(PxU32 i=0;imWheelsSimData.mActiveWheelsBitmapBuffer, &activeWheelStates[4*i]); + } + + //Set up contribution of each wheel to the average wheel speed at the clutch + //Set up the torque ratio delivered by the diff to each wheel. + //Set the sign of the gearing applied to the left and right wheels. + PxF32 aveWheelSpeedContributions[PX_MAX_NB_WHEELS]; + PxF32 diffTorqueRatios[PX_MAX_NB_WHEELS]; + PxF32 wheelGearings[PX_MAX_NB_WHEELS]; + PxMemZero(aveWheelSpeedContributions, sizeof(PxF32)*PX_MAX_NB_WHEELS); + PxMemZero(diffTorqueRatios, sizeof(PxF32)*PX_MAX_NB_WHEELS); + PxMemZero(wheelGearings, sizeof(PxF32)*PX_MAX_NB_WHEELS); + computeTankDiff + (thrustLeft, thrustRight, + numActiveWheels, activeWheelStates, + aveWheelSpeedContributions, diffTorqueRatios, wheelGearings); + + //Compute an accelerator pedal value per wheel. + bool isAccelApplied[PX_MAX_NB_WHEELS]; + PxMemZero(isAccelApplied, sizeof(bool)*PX_MAX_NB_WHEELS); + if(isIntentionToAccelerate) + { + PX_ASSERT(accel>0); + for(PxU32 i=0;imWheelsSimData,carChassisLinVel,carChassisTransform,gForward); + const PxF32 timeFraction=1.0f/(1.0f*numSubSteps); + const PxF32 subTimestep=timestep*timeFraction; + const PxF32 recipSubTimeStep=1.0f/subTimestep; + const PxF32 recipTimestep=1.0f/timestep; + const PxF32 minLongSlipDenominator=vehDriveTank->mWheelsSimData.mMinLongSlipDenominator; + ProcessSuspWheelTireConstData constData={timeFraction, subTimestep, recipSubTimeStep, gravity, gravityMagnitude, recipGravityMagnitude, true, minLongSlipDenominator, vehActor, &drivableSurfaceToTireFrictionPairs}; + + for(PxU32 k=0;k(inputData.vehWheels4DynData->mTireLowForwardSpeedTimers)); + updateLowSpeedTimers(outputData[i].newLowSideSpeedTimers, const_cast(inputData.vehWheels4DynData->mTireLowSideSpeedTimers)); + updateJounces(outputData[i].jounces, const_cast(inputData.vehWheels4DynData->mJounces)); + if((numSubSteps-1) == k) + { + updateCachedHitData(outputData[i].cachedHitCounts, outputData[i].cachedHitPlanes, outputData[i].cachedHitDistances, outputData[i].cachedFrictionMultipliers, outputData[i].cachedHitQueryTypes, &wheels4DynDatas[i]); + } + chassisForce+=outputData[i].chassisForce; + chassisTorque+=outputData[i].chassisTorque; + if(0 == k) + { + wheels4DynDatas[i].mVehicleConstraints->mData=outputData[i].vehConstraintData; + } + storeSuspWheelTireResults(outputData[i], inputData.steerAngles, &wheelQueryResults[4*i], numActiveWheelsPerBlock4[i]); + storeHitActorForces(outputData[i], &vehicleConcurrentUpdates.concurrentWheelUpdates[4*i], numActiveWheelsPerBlock4[i]); + } + + //Copy the tire torques to a single array. + PxF32 tireTorques[PX_MAX_NB_WHEELS]; + for(PxU32 i=0;imWheelsSimData, carChassisTransform, wheelQueryResults, chassisTorque); + + //Integrate the chassis velocity by applying the accumulated force and torque. + integrateBody(inverseChassisMass, inverseInertia, chassisForce, chassisTorque, subTimestep, carChassisLinVel, carChassisAngVel, carChassisTransform); + } + + //Set the new chassis linear/angular velocity. + if(!gApplyForces) + { + vehicleConcurrentUpdates.linearMomentumChange = carChassisLinVel; + vehicleConcurrentUpdates.angularMomentumChange = carChassisAngVel; + } + else + { + //integration steps are: + //v = v0 + a*dt (1) + //x = x0 + v*dt (2) + //Sub (2) into (1. + //x = x0 + v0*dt + a*dt*dt; + //Rearrange for a + //a = (x -x0 - v0*dt)/(dt*dt) = [(x-x0)/dt - v0/dt] + //Rearrange again with v = (x-x0)/dt + //a = (v - v0)/dt + vehicleConcurrentUpdates.linearMomentumChange = (carChassisLinVel-carChassisLinVelOrig)*recipTimestep; + vehicleConcurrentUpdates.angularMomentumChange = (carChassisAngVel-carChassisAngVelOrig)*recipTimestep; + } + + //Pose the wheels from jounces, rotations angles, and steer angles. + PxTransform localPoses0[4] = {PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity)}; + computeWheelLocalPoses(wheels4SimDatas[0],wheels4DynDatas[0],&wheelQueryResults[4*0],numActiveWheelsPerBlock4[0],carChassisCMLocalPose,localPoses0); + wheelQueryResults[4*0 + 0].localPose = localPoses0[0]; + wheelQueryResults[4*0 + 1].localPose = localPoses0[1]; + wheelQueryResults[4*0 + 2].localPose = localPoses0[2]; + wheelQueryResults[4*0 + 3].localPose = localPoses0[3]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 0].localPose = localPoses0[0]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 1].localPose = localPoses0[1]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 2].localPose = localPoses0[2]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 3].localPose = localPoses0[3]; + for(PxU32 i=1;iwheelQueryResults) + { + PxMemCopy(vehWheelQueryResults->wheelQueryResults, wheelQueryResults, sizeof(PxWheelQueryResult)*numActiveWheels); + } + + if(vehConcurrentUpdates) + { + //Copy across to input data structure so that writes can be applied later. + PxMemCopy(vehConcurrentUpdates->concurrentWheelUpdates, vehicleConcurrentUpdates.concurrentWheelUpdates, sizeof(PxVehicleWheelConcurrentUpdateData)*numActiveWheels); + vehConcurrentUpdates->linearMomentumChange = vehicleConcurrentUpdates.linearMomentumChange; + vehConcurrentUpdates->angularMomentumChange = vehicleConcurrentUpdates.angularMomentumChange; + vehConcurrentUpdates->staySleeping = vehicleConcurrentUpdates.staySleeping; + vehConcurrentUpdates->wakeup = vehicleConcurrentUpdates.wakeup; + } + else + { + //Apply the writes immediately. + PxVehicleWheels* vehWheels[1]={vehDriveTank}; + PxVehiclePostUpdates(&vehicleConcurrentUpdates, 1, vehWheels); + } +} + +void PxVehicleUpdate::updateNoDrive +(const PxF32 timestep, + const PxVec3& gravity, const PxF32 gravityMagnitude, const PxF32 recipGravityMagnitude, + const PxVehicleDrivableSurfaceToTireFrictionPairs& drivableSurfaceToTireFrictionPairs, + PxVehicleNoDrive* vehNoDrive, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleConcurrentUpdateData* vehConcurrentUpdates) +{ + PX_SIMD_GUARD; // denorm exception in transformInertiaTensor() on osx + + PX_CHECK_AND_RETURN( + !(vehNoDrive->getRigidDynamicActor()->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC), + "Attempting to update a PxVehicleNoDrive with a kinematic actor - this isn't allowed"); + + PX_CHECK_AND_RETURN( + NULL==vehWheelQueryResults || vehWheelQueryResults->nbWheelQueryResults >= vehNoDrive->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + +#if PX_CHECKED + for(PxU32 i=0;imWheelsSimData.getNbWheels();i++) + { + PX_CHECK_AND_RETURN( + !vehNoDrive->mWheelsSimData.getIsWheelDisabled(i) || 0==vehNoDrive->getDriveTorque(i), + "Disabled wheels should have zero drive torque applied to them."); + } +#endif + +#if PX_DEBUG_VEHICLE_ON + for(PxU32 i=0;imWheelsSimData.mNbWheels4;i++) + { + updateGraphDataInternalWheelDynamics(4*i,vehNoDrive->mWheelsDynData.mWheels4DynData[i].mWheelSpeeds); + } +#endif + + //Unpack the tank simulation and instanced dynamics components. + const PxVehicleWheels4SimData* wheels4SimDatas=vehNoDrive->mWheelsSimData.mWheels4SimData; + const PxVehicleTireLoadFilterData& tireLoadFilterData=vehNoDrive->mWheelsSimData.mNormalisedLoadFilter; + PxVehicleWheels4DynData* wheels4DynDatas=vehNoDrive->mWheelsDynData.mWheels4DynData; + const PxU32 numWheels4=vehNoDrive->mWheelsSimData.mNbWheels4; + const PxU32 numActiveWheels=vehNoDrive->mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=4-(4*numWheels4-numActiveWheels); + PxRigidDynamic* vehActor=vehNoDrive->mActor; + + //We need to store that data we are going to write to actors so we can do this at the end in one go with fewer write locks. + PxVehicleWheelConcurrentUpdateData wheelConcurrentUpdates[PX_MAX_NB_WHEELS]; + PxVehicleConcurrentUpdateData vehicleConcurrentUpdates; + vehicleConcurrentUpdates.nbConcurrentWheelUpdates = numActiveWheels; + vehicleConcurrentUpdates.concurrentWheelUpdates = wheelConcurrentUpdates; + + //Test if a non-zero drive torque was applied or if a non-zero steer angle was applied. + bool finiteInputApplied=false; + for(PxU32 i=0;igetDriveTorque(i) != 0.0f) + { + finiteInputApplied=true; + break; + } + if(vehNoDrive->getSteerAngle(i)!=0.0f) + { + finiteInputApplied=true; + break; + } + } + + //Wake or sleep. + { + if(vehActor->isSleeping()) + { + if(finiteInputApplied) + { + //Driving inputs so we need the actor to start moving. + vehicleConcurrentUpdates.wakeup = true; + } + else if(isOnDynamicActor(vehNoDrive->mWheelsSimData, vehNoDrive->mWheelsDynData)) + { + //Driving on dynamic so we need to keep moving. + vehicleConcurrentUpdates.wakeup = true; + } + else + { + //No driving inputs and the actor is asleep. + //Set internal dynamics to sleep. + setInternalDynamicsToZero(vehNoDrive); + if(vehConcurrentUpdates) vehConcurrentUpdates->staySleeping = true; + return; + } + } + } + + //In each block of 4 wheels record how many wheels are active. + PxU32 numActiveWheelsPerBlock4[PX_MAX_NB_SUSPWHEELTIRE4]={0,0,0,0,0}; + numActiveWheelsPerBlock4[0]=PxMin(numActiveWheels,PxU32(4)); + for(PxU32 i=1;imWheelsDynData.mTireForceCalculators->mShaderData[4*i+0]; + tires4ForceCalculators[i].mShaderData[1]=vehNoDrive->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+1]; + tires4ForceCalculators[i].mShaderData[2]=vehNoDrive->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+2]; + tires4ForceCalculators[i].mShaderData[3]=vehNoDrive->mWheelsDynData.mTireForceCalculators->mShaderData[4*i+3]; + tires4ForceCalculators[i].mShader=vehNoDrive->mWheelsDynData.mTireForceCalculators->mShader; + } + + //Mark the suspension/tire constraints as dirty to force them to be updated in the sdk. + for(PxU32 i=0;imarkDirty(); + } + + //Need to store report data to pose the wheels. + PxWheelQueryResult wheelQueryResults[PX_MAX_NB_WHEELS]; + + //Center of mass local pose. + PxTransform carChassisCMLocalPose; + //Compute the transform of the center of mass. + PxTransform origCarChassisTransform; + PxTransform carChassisTransform; + //Inverse mass and inertia to apply the tire/suspension forces as impulses. + PxF32 inverseChassisMass; + PxVec3 inverseInertia; + //Linear and angular velocity. + PxVec3 carChassisLinVel; + PxVec3 carChassisAngVel; + { + carChassisCMLocalPose = vehActor->getCMassLocalPose(); + carChassisCMLocalPose.q = PxQuat(PxIdentity); + origCarChassisTransform = vehActor->getGlobalPose().transform(carChassisCMLocalPose); + carChassisTransform = origCarChassisTransform; + const PxF32 chassisMass = vehActor->getMass(); + inverseChassisMass = 1.0f/chassisMass; + inverseInertia = vehActor->getMassSpaceInvInertiaTensor(); + carChassisLinVel = vehActor->getLinearVelocity(); + carChassisAngVel = vehActor->getAngularVelocity(); + } + + //Get the local poses of the wheel shapes. + //These are the poses from the last frame and equal to the poses used for the raycast we will process. + PxQuat wheelLocalPoseRotations[PX_MAX_NB_WHEELS]; + PxF32 wheelThetas[PX_MAX_NB_WHEELS]; + { + for (PxU32 i = 0; i < numActiveWheels; i++) + { + const PxI32 shapeId = vehNoDrive->mWheelsSimData.getWheelShapeMapping(i); + if (-1 != shapeId) + { + PxShape* shape = NULL; + vehActor->getShapes(&shape, 1, PxU32(shapeId)); + wheelLocalPoseRotations[i] = shape->getLocalPose().q; + wheelThetas[i] = vehNoDrive->mWheelsDynData.getWheelRotationAngle(i); + } + } + } + + PxF32 maxAccel=0; + PxF32 maxBrake=0; + for(PxU32 i=0;imDriveTorques[i]), maxAccel); + maxBrake = PxMax(PxAbs(vehNoDrive->mBrakeTorques[i]), maxBrake); + } + const bool isIntentionToAccelerate = (maxAccel>0.0f && 0.0f==maxBrake); + + //Store the susp line raycast data. + for(PxU32 i=0;imWheelsSimData,carChassisLinVel,carChassisTransform,gForward); + const PxF32 timeFraction=1.0f/(1.0f*numSubSteps); + const PxF32 subTimestep=timestep*timeFraction; + const PxF32 recipSubTimeStep=1.0f/subTimestep; + const PxF32 recipTimestep=1.0f/timestep; + const PxF32 minLongSlipDenominator=vehNoDrive->mWheelsSimData.mMinLongSlipDenominator; + ProcessSuspWheelTireConstData constData={timeFraction, subTimestep, recipSubTimeStep, gravity, gravityMagnitude, recipGravityMagnitude, false, minLongSlipDenominator, vehActor, &drivableSurfaceToTireFrictionPairs}; + + for(PxU32 k=0;kmBrakeTorques[4*i]; + const PxF32* PX_RESTRICT rawSteerAngles=&vehNoDrive->mSteerAngles[4*i]; + const PxF32* PX_RESTRICT rawDriveTorques=&vehNoDrive->mDriveTorques[4*i]; + + //Work out which wheels are enabled. + bool activeWheelStates[4]={false,false,false,false}; + computeWheelActiveStates(4*i, vehNoDrive->mWheelsSimData.mActiveWheelsBitmapBuffer, activeWheelStates); + + const PxVehicleWheels4SimData& wheels4SimData=wheels4SimDatas[i]; + PxVehicleWheels4DynData& wheels4DynData=wheels4DynDatas[i]; + + //Compute the brake torques. + PxF32 brakeTorques[4]={0.0f,0.0f,0.0f,0.0f}; + bool isBrakeApplied[4]={false,false,false,false}; + computeNoDriveBrakeTorques + (wheels4SimData.mWheels,wheels4DynData.mWheelSpeeds,rawBrakeTorques, + brakeTorques,isBrakeApplied); + + //Compute the per wheel accel pedal values. + bool isAccelApplied[4]={false,false,false,false}; + if(isIntentionToAccelerate) + { + computeIsAccelApplied(rawDriveTorques, isAccelApplied); + } + + //Compute jounces, slips, tire forces, suspension forces etc. + ProcessSuspWheelTireInputData inputData= + { + isIntentionToAccelerate, isAccelApplied, isBrakeApplied, rawSteerAngles, activeWheelStates, + carChassisTransform, carChassisLinVel, carChassisAngVel, + &wheelLocalPoseRotations[i], &wheelThetas[i], &wheels4SimData, &wheels4DynData, &tires4ForceCalculators[i], &tireLoadFilterData, numActiveWheelsPerBlock4[i] + }; + ProcessSuspWheelTireOutputData outputData; + processSuspTireWheels(4*i, constData, inputData, outputData); + updateLowSpeedTimers(outputData.newLowForwardSpeedTimers, const_cast(inputData.vehWheels4DynData->mTireLowForwardSpeedTimers)); + updateLowSpeedTimers(outputData.newLowSideSpeedTimers, const_cast(inputData.vehWheels4DynData->mTireLowSideSpeedTimers)); + updateJounces(outputData.jounces, const_cast(inputData.vehWheels4DynData->mJounces)); + if((numSubSteps-1) == k) + { + updateCachedHitData(outputData.cachedHitCounts, outputData.cachedHitPlanes, outputData.cachedHitDistances, outputData.cachedFrictionMultipliers, outputData.cachedHitQueryTypes, &wheels4DynData); + } + chassisForce+=outputData.chassisForce; + chassisTorque+=outputData.chassisTorque; + if(0 == k) + { + wheels4DynDatas[i].mVehicleConstraints->mData=outputData.vehConstraintData; + } + storeSuspWheelTireResults(outputData, inputData.steerAngles, &wheelQueryResults[4*i], numActiveWheelsPerBlock4[i]); + storeHitActorForces(outputData, &vehicleConcurrentUpdates.concurrentWheelUpdates[4*i], numActiveWheelsPerBlock4[i]); + + //Integrate wheel speeds. + const PxF32 wheelDampingRates[4]= + { + wheels4SimData.getWheelData(0).mDampingRate, + wheels4SimData.getWheelData(1).mDampingRate, + wheels4SimData.getWheelData(2).mDampingRate, + wheels4SimData.getWheelData(3).mDampingRate + }; + integrateNoDriveWheelSpeeds( + subTimestep, + brakeTorques,isBrakeApplied,rawDriveTorques,outputData.tireTorques,wheelDampingRates, + wheels4SimData,wheels4DynData); + + integrateNoDriveWheelRotationAngles( + subTimestep, + rawDriveTorques, + outputData.jounces, outputData.forwardSpeeds, isBrakeApplied, + wheels4SimData, + wheels4DynData); + } + + //Apply the anti-roll suspension. + procesAntiRollSuspension(vehNoDrive->mWheelsSimData, carChassisTransform, wheelQueryResults, chassisTorque); + + //Integrate the chassis velocity by applying the accumulated force and torque. + integrateBody(inverseChassisMass, inverseInertia, chassisForce, chassisTorque, subTimestep, carChassisLinVel, carChassisAngVel, carChassisTransform); + } + + //Set the new chassis linear/angular velocity. + if(!gApplyForces) + { + vehicleConcurrentUpdates.linearMomentumChange = carChassisLinVel; + vehicleConcurrentUpdates.angularMomentumChange = carChassisAngVel; + } + else + { + //integration steps are: + //v = v0 + a*dt (1) + //x = x0 + v*dt (2) + //Sub (2) into (1. + //x = x0 + v0*dt + a*dt*dt; + //Rearrange for a + //a = (x -x0 - v0*dt)/(dt*dt) = [(x-x0)/dt - v0/dt] + //Rearrange again with v = (x-x0)/dt + //a = (v - v0)/dt + vehicleConcurrentUpdates.linearMomentumChange = (carChassisLinVel-carChassisLinVelOrig)*recipTimestep; + vehicleConcurrentUpdates.angularMomentumChange = (carChassisAngVel-carChassisAngVelOrig)*recipTimestep; + } + + //Pose the wheels from jounces, rotations angles, and steer angles. + PxTransform localPoses0[4] = {PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity), PxTransform(PxIdentity)}; + computeWheelLocalPoses(wheels4SimDatas[0],wheels4DynDatas[0],&wheelQueryResults[4*0],numActiveWheelsPerBlock4[0],carChassisCMLocalPose,localPoses0); + wheelQueryResults[4*0 + 0].localPose = localPoses0[0]; + wheelQueryResults[4*0 + 1].localPose = localPoses0[1]; + wheelQueryResults[4*0 + 2].localPose = localPoses0[2]; + wheelQueryResults[4*0 + 3].localPose = localPoses0[3]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 0].localPose = localPoses0[0]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 1].localPose = localPoses0[1]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 2].localPose = localPoses0[2]; + vehicleConcurrentUpdates.concurrentWheelUpdates[4*0 + 3].localPose = localPoses0[3]; + for(PxU32 i=1;iwheelQueryResults) + { + PxMemCopy(vehWheelQueryResults->wheelQueryResults, wheelQueryResults, sizeof(PxWheelQueryResult)*numActiveWheels); + } + + if(vehConcurrentUpdates) + { + //Copy across to input data structure so that writes can be applied later. + PxMemCopy(vehConcurrentUpdates->concurrentWheelUpdates, vehicleConcurrentUpdates.concurrentWheelUpdates, sizeof(PxVehicleWheelConcurrentUpdateData)*numActiveWheels); + vehConcurrentUpdates->linearMomentumChange = vehicleConcurrentUpdates.linearMomentumChange; + vehConcurrentUpdates->angularMomentumChange = vehicleConcurrentUpdates.angularMomentumChange; + vehConcurrentUpdates->staySleeping = vehicleConcurrentUpdates.staySleeping; + vehConcurrentUpdates->wakeup = vehicleConcurrentUpdates.wakeup; + } + else + { + //Apply the writes immediately. + PxVehicleWheels* vehWheels[1]={vehNoDrive}; + PxVehiclePostUpdates(&vehicleConcurrentUpdates, 1, vehWheels); + } +} + + +void PxVehicleUpdate::shiftOrigin(const PxVec3& shift, const PxU32 numVehicles, PxVehicleWheels** vehicles) +{ + for(PxU32 i=0; i < numVehicles; i++) + { + //Get the current car. + PxVehicleWheels& veh = *vehicles[i]; + PxVehicleWheels4DynData* PX_RESTRICT wheels4DynData=veh.mWheelsDynData.mWheels4DynData; + const PxU32 numWheels4=veh.mWheelsSimData.mNbWheels4; + + //Blocks of 4 wheels. + for(PxU32 j=0; j < numWheels4; j++) + { + bool activeWheelStates[4]={false,false,false,false}; + computeWheelActiveStates(4*j, veh.mWheelsSimData.mActiveWheelsBitmapBuffer, activeWheelStates); + + if (wheels4DynData[j].mRaycastResults) // this is set when a query has been scheduled + { + PxVehicleWheels4DynData::SuspLineRaycast& raycast = + reinterpret_cast(wheels4DynData[j].mQueryOrCachedHitResults); + + for(PxU32 k=0; k < 4; k++) + { + if (activeWheelStates[k]) + { + raycast.mStarts[k] -= shift; + + if (wheels4DynData[j].mRaycastResults[k].hasBlock) + const_cast(wheels4DynData[j].mRaycastResults[k].block.position) -= shift; + } + } + } + else if(wheels4DynData[i].mSweepResults) + { + PxVehicleWheels4DynData::SuspLineSweep& sweep = + reinterpret_cast(wheels4DynData[j].mQueryOrCachedHitResults); + + for(PxU32 k=0; k < 4; k++) + { + if (activeWheelStates[k]) + { + sweep.mStartPose[k].p -= shift; + + if (wheels4DynData[j].mSweepResults[k].hasBlock) + const_cast(wheels4DynData[j].mSweepResults[k].block.position) -= shift; + } + } + } + } + } +} + +}//namespace physx + +#if PX_DEBUG_VEHICLE_ON + +///////////////////////////////////////////////////////////////////////////////// +//Update a single vehicle of any type and record the associated telemetry data. +///////////////////////////////////////////////////////////////////////////////// + +void PxVehicleUpdate::updateSingleVehicleAndStoreTelemetryData +(const PxF32 timestep, const PxVec3& gravity, const PxVehicleDrivableSurfaceToTireFrictionPairs& vehicleDrivableSurfaceToTireFrictionPairs, + PxVehicleWheels* vehWheels, PxVehicleWheelQueryResult* vehWheelQueryResults, PxVehicleTelemetryData& telemetryData) +{ + START_TIMER(TIMER_ALL); + + PX_CHECK_MSG(gravity.magnitude()>0, "gravity vector must have non-zero length"); + PX_CHECK_MSG(timestep>0, "timestep must be greater than zero"); + PX_CHECK_AND_RETURN(gThresholdForwardSpeedForWheelAngleIntegration>0, "PxInitVehicleSDK needs to be called before ever calling PxVehicleUpdateSingleVehicleAndStoreTelemetryData"); + PX_CHECK_MSG(vehWheels->mWheelsSimData.getNbWheels()==telemetryData.getNbWheelGraphs(), "vehicle and telemetry data need to have the same number of wheels"); + PX_CHECK_AND_RETURN(NULL==vehWheelQueryResults || vehWheelQueryResults->nbWheelQueryResults >= vehWheels->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + +#if PX_CHECKED + for(PxU32 i=0;imWheelsSimData.mNbWheels4;i++) + { + PX_CHECK_MSG(vehWheels->mWheelsDynData.mWheels4DynData[i].mRaycastResults || vehWheels->mWheelsDynData.mWheels4DynData[i].mSweepResults, + "Need to call PxVehicleSuspensionRaycasts or PxVehicleSuspensionSweeps before trying to update"); + } + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + PX_CHECK_MSG(vehWheels->mWheelsDynData.mTireForceCalculators->mShaderData[i], "Need to set non-null tire force shader data ptr"); + } + PX_CHECK_MSG(vehWheels->mWheelsDynData.mTireForceCalculators->mShader, "Need to set non-null tire force shader function"); + + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(i) || -1==vehWheels->mWheelsSimData.getWheelShapeMapping(i), + "Disabled wheels must not be associated with a PxShape: use setWheelShapeMapping to remove the association"); + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(i) || 0==vehWheels->mWheelsDynData.getWheelRotationSpeed(i), + "Disabled wheels must have zero rotation speed: use setWheelRotationSpeed to set the wheel to zero rotation speed"); + } +#endif + + PxF32 engineGraphData[PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS]; + PxMemZero(&engineGraphData[0], PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS*sizeof(PxF32)); + gCarEngineGraphData=engineGraphData; + + PxF32 wheelGraphData[PX_MAX_NB_WHEELS][PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS]; + PxMemZero(&wheelGraphData[0][0], PX_MAX_NB_WHEELS*PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS*sizeof(PxF32)); + for(PxU32 i=0;i<4*vehWheels->mWheelsSimData.mNbWheels4;i++) + { + gCarWheelGraphData[i]=wheelGraphData[i]; + } + for(PxU32 i=4*vehWheels->mWheelsSimData.mNbWheels4; imType) + { + case PxVehicleTypes::eDRIVE4W: + { + PxVehicleDrive4W* vehDrive4W=static_cast(vehWheels); + + PxVehicleUpdate::updateDrive4W( + timestep, + gravity, gravityMagnitude, recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDrive4W, vehWheelQueryResults, NULL); + + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + telemetryData.mWheelGraphs[i].updateTimeSlice(wheelGraphData[i]); + telemetryData.mSuspforceAppPoints[i]=suspForceAppPoints[i]; + telemetryData.mTireforceAppPoints[i]=tireForceAppPoints[i]; + } + telemetryData.mEngineGraph->updateTimeSlice(engineGraphData); + } + break; + case PxVehicleTypes::eDRIVENW: + { + PxVehicleDriveNW* vehDriveNW=static_cast(vehWheels); + + PxVehicleUpdate::updateDriveNW( + timestep, + gravity, gravityMagnitude, recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveNW, vehWheelQueryResults, NULL); + + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + telemetryData.mWheelGraphs[i].updateTimeSlice(wheelGraphData[i]); + telemetryData.mSuspforceAppPoints[i]=suspForceAppPoints[i]; + telemetryData.mTireforceAppPoints[i]=tireForceAppPoints[i]; + } + telemetryData.mEngineGraph->updateTimeSlice(engineGraphData); + } + break; + + case PxVehicleTypes::eDRIVETANK: + { + PxVehicleDriveTank* vehDriveTank=static_cast(vehWheels); + + PxVehicleUpdate::updateTank( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveTank, vehWheelQueryResults, NULL); + + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + telemetryData.mWheelGraphs[i].updateTimeSlice(wheelGraphData[i]); + telemetryData.mSuspforceAppPoints[i]=suspForceAppPoints[i]; + telemetryData.mTireforceAppPoints[i]=tireForceAppPoints[i]; + } + telemetryData.mEngineGraph->updateTimeSlice(engineGraphData); + } + break; + case PxVehicleTypes::eNODRIVE: + { + PxVehicleNoDrive* vehDriveNoDrive=static_cast(vehWheels); + + PxVehicleUpdate::updateNoDrive( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveNoDrive, vehWheelQueryResults, NULL); + + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;i++) + { + telemetryData.mWheelGraphs[i].updateTimeSlice(wheelGraphData[i]); + telemetryData.mSuspforceAppPoints[i]=suspForceAppPoints[i]; + telemetryData.mTireforceAppPoints[i]=tireForceAppPoints[i]; + } + } + break; + + default: + PX_CHECK_MSG(false, "updateSingleVehicleAndStoreTelemetryData - unsupported vehicle type"); + break; + } + + END_TIMER(TIMER_ALL); + +#if PX_VEHICLE_PROFILE + + gTimerCount++; + if(10==gTimerCount) + { + + /* + printf("%f %f %f %f %f %f %f %f %f\n", + localTimers[TIMER_ADMIN]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_GRAPHS]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_COMPONENTS_UPDATE]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_WHEELS]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_INTERNAL_DYNAMICS_SOLVER]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_POSTUPDATE1]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_POSTUPDATE2]/(1.0f*localTimers[TIMER_ALL]), + localTimers[TIMER_POSTUPDATE3]/(1.0f*localTimers[TIMER_ALL]), + floatTimeIn10sOfNs); + */ + + printf("%f %f %f %f %f %f \n", + getTimerFraction(TIMER_WHEELS), + getTimerFraction(TIMER_INTERNAL_DYNAMICS_SOLVER), + getTimerFraction(TIMER_POSTUPDATE2), + getTimerFraction(TIMER_POSTUPDATE3), + getTimerInMilliseconds(TIMER_ALL), + getTimerInMilliseconds(TIMER_RAYCASTS)); + + gTimerCount=0; + for(PxU32 i=0;i0, "gravity vector must have non-zero length"); + PX_CHECK_AND_RETURN(timestep>0, "timestep must be greater than zero"); + PX_CHECK_AND_RETURN(gThresholdForwardSpeedForWheelAngleIntegration>0, "PxInitVehicleSDK needs to be called before ever calling PxVehicleUpdates"); + +#if PX_CHECKED + for(PxU32 i=0;imWheelsSimData.mNbWheels4;j++) + { + PX_CHECK_MSG( + vehWheels->mWheelsDynData.mWheels4DynData[j].mRaycastResults || + vehWheels->mWheelsDynData.mWheels4DynData[j].mSweepResults || + vehWheels->mWheelsDynData.mWheels4DynData[0].mHasCachedRaycastHitPlane || + (vehWheels->mWheelsSimData.getIsWheelDisabled(4*j+0) && + vehWheels->mWheelsSimData.getIsWheelDisabled(4*j+1) && + vehWheels->mWheelsSimData.getIsWheelDisabled(4*j+2) && + vehWheels->mWheelsSimData.getIsWheelDisabled(4*j+3)), + "Need to call PxVehicleSuspensionRaycasts or PxVehicleSuspensionSweeps at least once before trying to update"); + } + for(PxU32 j=0;jmWheelsSimData.mNbActiveWheels;j++) + { + PX_CHECK_MSG(vehWheels->mWheelsDynData.mTireForceCalculators->mShaderData[j], "Need to set non-null tire force shader data ptr"); + } + PX_CHECK_MSG(vehWheels->mWheelsDynData.mTireForceCalculators->mShader, "Need to set non-null tire force shader function"); + + PX_CHECK_AND_RETURN(NULL==vehicleWheelQueryResults || vehicleWheelQueryResults[i].nbWheelQueryResults >= vehicles[i]->mWheelsSimData.getNbWheels(), + "nbWheelQueryResults must always be greater than or equal to number of wheels in corresponding vehicle"); + + for(PxU32 j=0;jmWheelsSimData.mNbActiveWheels;j++) + { + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(j) || -1==vehWheels->mWheelsSimData.getWheelShapeMapping(j), + "Disabled wheels must not be associated with a PxShape: use setWheelShapeMapping to remove the association"); + + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(j) || 0==vehWheels->mWheelsDynData.getWheelRotationSpeed(j), + "Disabled wheels must have zero rotation speed: use setWheelRotationSpeed to set the wheel to zero rotation speed"); + } + + PX_CHECK_AND_RETURN(!vehicleConcurrentUpdates || (vehicleConcurrentUpdates[i].concurrentWheelUpdates && vehicleConcurrentUpdates[i].nbConcurrentWheelUpdates >= vehicles[i]->mWheelsSimData.getNbWheels()), + "vehicleConcurrentUpdates is illegally configured with either null pointers or with insufficient memory for successful concurrent updates."); + + for(PxU32 j=0; j < vehWheels->mWheelsSimData.mNbActiveAntiRollBars; j++) + { + const PxVehicleAntiRollBarData antiRoll = vehWheels->mWheelsSimData.getAntiRollBarData(j); + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(antiRoll.mWheel0), "Wheel0 of antiroll bar is disabled. This is not supported."); + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(antiRoll.mWheel1), "Wheel1 of antiroll bar is disabled. This is not supported."); + } + } +#endif + +#if PX_DEBUG_VEHICLE_ON + gCarEngineGraphData=NULL; + for(PxU32 j=0;jmType) + { + case PxVehicleTypes::eDRIVE4W: + { + PxVehicleDrive4W* vehDrive4W=static_cast(vehWheels); + + PxVehicleUpdate::updateDrive4W( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDrive4W, vehWheelQueryResults, vehConcurrentUpdateData); + } + break; + + case PxVehicleTypes::eDRIVENW: + { + PxVehicleDriveNW* vehDriveNW=static_cast(vehWheels); + + PxVehicleUpdate::updateDriveNW( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveNW, vehWheelQueryResults, vehConcurrentUpdateData); + } + break; + + case PxVehicleTypes::eDRIVETANK: + { + PxVehicleDriveTank* vehDriveTank=static_cast(vehWheels); + + PxVehicleUpdate::updateTank( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveTank, vehWheelQueryResults, vehConcurrentUpdateData); + } + break; + + case PxVehicleTypes::eNODRIVE: + { + PxVehicleNoDrive* vehDriveNoDrive=static_cast(vehWheels); + + PxVehicleUpdate::updateNoDrive( + timestep, + gravity,gravityMagnitude,recipGravityMagnitude, + vehicleDrivableSurfaceToTireFrictionPairs, + vehDriveNoDrive, vehWheelQueryResults, vehConcurrentUpdateData); + } + break; + + default: + PX_CHECK_MSG(false, "update - unsupported vehicle type"); + break; + } + } +} + + +void PxVehicleUpdate::updatePost +(const PxVehicleConcurrentUpdateData* vehicleConcurrentUpdates, const PxU32 numVehicles, PxVehicleWheels** vehicles) +{ + PX_CHECK_AND_RETURN(vehicleConcurrentUpdates, "vehicleConcurrentUpdates must be non-null."); + +#if PX_CHECKED + for(PxU32 i=0;imWheelsSimData.mNbActiveWheels;j++) + { + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(j) || -1==vehWheels->mWheelsSimData.getWheelShapeMapping(j), + "Disabled wheels must not be associated with a PxShape: use setWheelShapeMapping to remove the association"); + + PX_CHECK_AND_RETURN(!vehWheels->mWheelsSimData.getIsWheelDisabled(j) || 0==vehWheels->mWheelsDynData.getWheelRotationSpeed(j), + "Disabled wheels must have zero rotation speed: use setWheelRotationSpeed to set the wheel to zero rotation speed"); + + PX_CHECK_AND_RETURN(vehicleConcurrentUpdates[i].concurrentWheelUpdates && vehicleConcurrentUpdates[i].nbConcurrentWheelUpdates >= vehWheels->mWheelsSimData.getNbWheels(), + "vehicleConcurrentUpdates is illegally configured with either null pointers or insufficient memory for successful concurrent vehicle updates."); + } + } +#endif + + for(PxU32 i=0;igetRigidDynamicActor(); + + //Get the concurrent update data for the ith vehicle. + //This contains the data that couldn't get updated concurrently and now must be + //set sequentially. + const PxVehicleConcurrentUpdateData& vehicleConcurrentUpdate = vehicleConcurrentUpdates[i]; + + //Test if the actor is to remain sleeping. + //If the actor is to remain sleeping then do nothing. + if(!vehicleConcurrentUpdate.staySleeping) + { + //Wake the vehicle's actor up as required. + if(vehicleConcurrentUpdate.wakeup) + { + vehActor->wakeUp(); + } + + //Apply momentum changes to vehicle's actor + if(!gApplyForces) + { + vehActor->setLinearVelocity(vehicleConcurrentUpdate.linearMomentumChange, false); + vehActor->setAngularVelocity(vehicleConcurrentUpdate.angularMomentumChange, false); + } + else + { + vehActor->addForce(vehicleConcurrentUpdate.linearMomentumChange, PxForceMode::eACCELERATION, false); + vehActor->addTorque(vehicleConcurrentUpdate.angularMomentumChange, PxForceMode::eACCELERATION, false); + } + + //In each block of 4 wheels record how many wheels are active. + const PxU32 numActiveWheels=vehWheels->mWheelsSimData.mNbActiveWheels; + const PxU32 numWheels4 = vehWheels->mWheelsSimData.getNbWheels4(); + const PxU32 numActiveWheelsInLast4=4-(4*numWheels4 - numActiveWheels); + PxU32 numActiveWheelsPerBlock4[PX_MAX_NB_SUSPWHEELTIRE4]={0,0,0,0,0}; + numActiveWheelsPerBlock4[0]=PxMin(numActiveWheels,PxU32(4)); + for(PxU32 j=1;jmWheelsSimData.mWheels4SimData[j],localPoses,numActiveWheelsPerBlock4[j],vehActor); + } + + //Apply forces to dynamic actors hit by the wheels. + for(PxU32 j=0;jgetCMassLocalPose(); + massXform.q = PxQuat(PxIdentity); + PxTransform carChassisTrnsfm = vehActor->getGlobalPose().transform(massXform); + + //Add a raycast for each wheel. + for(PxU32 j=0;j=0); + PX_ASSERT(maxDroop>=0); + + if(!activeWheelStates[j]) + { + //For disabled wheels just issue a raycast of almost zero length. + //This should be very cheap and ought to hit nothing. + bodySpaceWheelCentreOffset=PxVec3(0,0,0); + maxDroop=1e-5f*gToleranceScaleLength; + maxBounce=1e-5f*gToleranceScaleLength; + radius=1e-5f*gToleranceScaleLength; + } + + PxVec3 suspLineStart; + PxVec3 suspLineDir; + computeSuspensionRaycast(carChassisTrnsfm,bodySpaceWheelCentreOffset,bodySpaceSuspTravelDir,radius,maxBounce,suspLineStart,suspLineDir); + + //Total length from top of wheel at max compression to bottom of wheel at max droop. + PxF32 suspLineLength=radius + maxBounce + maxDroop + radius; + //Add another radius on for good measure. + suspLineLength+=radius; + + //Store the susp line ray for later use. + PxVehicleWheels4DynData::SuspLineRaycast& raycast = + reinterpret_cast(wheels4DynData.mQueryOrCachedHitResults); + raycast.mStarts[j]=suspLineStart; + raycast.mDirs[j]=suspLineDir; + raycast.mLengths[j]=suspLineLength; + + //Add the raycast to the scene query. + batchQuery->raycast( + suspLineStart, suspLineDir, suspLineLength, 0, + PxHitFlag::ePOSITION|PxHitFlag::eNORMAL|PxHitFlag::eUV, carFilterData[j]); + } +} + +void PxVehicleUpdate::suspensionRaycasts(PxBatchQuery* batchQuery, const PxU32 numVehicles, PxVehicleWheels** vehicles, const PxU32 numSceneQueryResults, PxRaycastQueryResult* sceneQueryResults, const bool* vehiclesToRaycast) +{ + START_TIMER(TIMER_RAYCASTS); + + //Reset all hit counts to zero. + for(PxU32 i=0;i> 2); + const PxU32 numActiveWheels=veh.mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=numActiveWheels-4*numWheels4; + PxRigidDynamic* vehActor=veh.mActor; + + //Set the results pointer and start the raycasts. + PX_ASSERT(numActiveWheelsInLast4<4); + + //Blocks of 4 wheels. + for(PxU32 j=0;j= (sqres+4)) + { + carFilterData[0].data=wheels4SimData[j].getSceneQueryFilterData(0); + carFilterData[1].data=wheels4SimData[j].getSceneQueryFilterData(1); + carFilterData[2].data=wheels4SimData[j].getSceneQueryFilterData(2); + carFilterData[3].data=wheels4SimData[j].getSceneQueryFilterData(3); + wheels4DynData[j].mRaycastResults=sqres; + PxVehicleWheels4SuspensionRaycasts(batchQuery,wheels4SimData[j],wheels4DynData[j],carFilterData,activeWheelStates,4,vehActor); + } + else + { + PX_CHECK_MSG(false, "PxVehicleUpdate::suspensionRaycasts - numSceneQueryResults not big enough to support one raycast hit report per wheel. Increase size of sceneQueryResults"); + } + sqres+=4; + } + } + //Remainder that don't make up a block of 4. + if(numActiveWheelsInLast4>0) + { + const PxU32 j=numWheels4; + + bool activeWheelStates[4]={false,false,false,false}; + computeWheelActiveStates(4*j, veh.mWheelsSimData.mActiveWheelsBitmapBuffer, activeWheelStates); + + wheels4DynData[j].mRaycastResults=NULL; + wheels4DynData[j].mSweepResults=NULL; + + if(NULL==vehiclesToRaycast || vehiclesToRaycast[i]) + { + if((sceneQueryResults + numSceneQueryResults) >= (sqres+numActiveWheelsInLast4)) + { + if(0execute(); + + END_TIMER(TIMER_RAYCASTS); +} + +void physx::PxVehicleSuspensionRaycasts(PxBatchQuery* batchQuery, const PxU32 numVehicles, PxVehicleWheels** vehicles, const PxU32 numSceneQueryesults, PxRaycastQueryResult* sceneQueryResults, const bool* vehiclesToRaycast) +{ + PX_PROFILE_ZONE("PxVehicleSuspensionRaycasts::ePROFILE_RAYCASTS",0); + PxVehicleUpdate::suspensionRaycasts(batchQuery, numVehicles, vehicles, numSceneQueryesults, sceneQueryResults, vehiclesToRaycast); +} + + +void PxVehicleWheels4SuspensionSweeps +(PxBatchQuery* batchQuery, + const PxVehicleWheels4SimData& wheels4SimData, PxVehicleWheels4DynData& wheels4DynData, + const PxQueryFilterData* carFilterData, const bool* activeWheelStates, const PxU32 numActiveWheels, + const PxU16 nbHitsPerQuery, + const PxI32* wheelShapeIds, + PxRigidDynamic* vehActor, + const PxF32 sweepWidthScale, const PxF32 sweepRadiusScale) +{ + PX_UNUSED(sweepWidthScale); + PX_UNUSED(sweepRadiusScale); + + //Get the transform of the chassis. + PxTransform carChassisTrnsfm=vehActor->getGlobalPose().transform(vehActor->getCMassLocalPose()); + + //Add a raycast for each wheel. + for(PxU32 j=0;jgetShapes(&wheelShape, 1, PxU32(wheelShapeIds[j])); + + PxGeometryHolder suspGeometry; + if (PxGeometryType::eCONVEXMESH == wheelShape->getGeometryType()) + { + PxConvexMeshGeometry convMeshGeom; + wheelShape->getConvexMeshGeometry(convMeshGeom); + convMeshGeom.scale.scale = + PxVec3( + PxAbs(gRight.x*sweepWidthScale + (gUp.x + gForward.x)*sweepRadiusScale), + PxAbs(gRight.y*sweepWidthScale + (gUp.y + gForward.y)*sweepRadiusScale), + PxAbs(gRight.z*sweepWidthScale + (gUp.z + gForward.z)*sweepRadiusScale)); + suspGeometry.storeAny(convMeshGeom); + } + else if (PxGeometryType::eCAPSULE == wheelShape->getGeometryType()) + { + PxCapsuleGeometry capsuleGeom; + wheelShape->getCapsuleGeometry(capsuleGeom); + capsuleGeom.halfHeight *= sweepWidthScale; + capsuleGeom.radius *= sweepRadiusScale; + suspGeometry.storeAny(capsuleGeom); + } + else + { + PX_ASSERT(PxGeometryType::eSPHERE == wheelShape->getGeometryType()); + PxSphereGeometry sphereGeom; + wheelShape->getSphereGeometry(sphereGeom); + sphereGeom.radius *= sweepRadiusScale; + suspGeometry.storeAny(sphereGeom); + } + + const PxQuat wheelLocalPoseRotation = wheelShape->getLocalPose().q; + const PxF32 wheelTheta = wheels4DynData.mWheelRotationAngles[j]; + + const PxVec3& bodySpaceSuspTravelDir = wheels4SimData.getSuspTravelDirection(j); + PxVec3 bodySpaceWheelCentreOffset = wheels4SimData.getWheelCentreOffset(j); + PxF32 maxDroop = susp.mMaxDroop; + PxF32 maxBounce = susp.mMaxCompression; + PxF32 radius = wheel.mRadius; + PX_ASSERT(maxBounce >= 0); + PX_ASSERT(maxDroop >= 0); + + if(!activeWheelStates[j]) + { + //For disabled wheels just issue a raycast of almost zero length. + //This should be very cheap and ought to hit nothing. + bodySpaceWheelCentreOffset = PxVec3(0,0,0); + maxDroop = 1e-5f*gToleranceScaleLength; + maxBounce = 1e-5f*gToleranceScaleLength; + radius = 1e-5f*gToleranceScaleLength; + } + + PxTransform suspPoseStart; + PxVec3 suspLineDir; + computeSuspensionSweep( + carChassisTrnsfm, + wheelLocalPoseRotation, wheelTheta, + bodySpaceWheelCentreOffset, bodySpaceSuspTravelDir, radius, maxBounce, + suspPoseStart, suspLineDir); + const PxF32 suspLineLength = radius + maxBounce + maxDroop + radius; + + //Store the susp line ray for later use. + PxVehicleWheels4DynData::SuspLineSweep& sweep = + reinterpret_cast(wheels4DynData.mQueryOrCachedHitResults); + sweep.mStartPose[j] = suspPoseStart; + sweep.mDirs[j] = suspLineDir; + sweep.mLengths[j] = suspLineLength; + sweep.mGometries[j] = suspGeometry; + + //Add the raycast to the scene query. + batchQuery->sweep(sweep.mGometries[j].any(), + suspPoseStart, suspLineDir, suspLineLength, nbHitsPerQuery, + PxHitFlag::ePOSITION|PxHitFlag::eNORMAL|PxHitFlag::eUV, + carFilterData[j]); + } +} + + +void PxVehicleUpdate::suspensionSweeps +(PxBatchQuery* batchQuery, + const PxU32 numVehicles, PxVehicleWheels** vehicles, + const PxU32 numSceneQueryResults, PxSweepQueryResult* sceneQueryResults, const PxU16 nbHitsPerQuery, + const bool* vehiclesToSweep, + const PxF32 sweepWidthScale, const PxF32 sweepRadiusScale) +{ + PX_CHECK_MSG(sweepWidthScale > 0.0f, "PxVehicleUpdate::suspensionSweeps - sweepWidthScale must be greater than 0.0"); + PX_CHECK_MSG(sweepRadiusScale > 0.0f, "PxVehicleUpdate::suspensionSweeps - sweepRadiusScale must be greater than 0.0"); + + START_TIMER(TIMER_SWEEPS); + + //Reset all hit counts to zero. + for(PxU32 i=0;i> 2); + const PxU32 numActiveWheels=veh.mWheelsSimData.mNbActiveWheels; + const PxU32 numActiveWheelsInLast4=numActiveWheels-4*numWheels4; + PxRigidDynamic* vehActor=veh.mActor; + + //Set the results pointer and start the raycasts. + PX_ASSERT(numActiveWheelsInLast4<4); + + //Get the shape ids for the wheels. + PxI32 wheelShapeIds[PX_MAX_NB_WHEELS]; + PxMemSet(wheelShapeIds, 0xff, sizeof(PxI32)*PX_MAX_NB_WHEELS); + for(PxU32 j = 0; j < veh.mWheelsSimData.getNbWheels(); j++) + { + PX_CHECK_AND_RETURN(veh.mWheelsSimData.getWheelShapeMapping(j) != -1, "PxVehicleUpdate::suspensionSweeps - trying to sweep a shape that doesn't exist."); + wheelShapeIds[j] = veh.mWheelsSimData.getWheelShapeMapping(j); + } + + //Blocks of 4 wheels. + for(PxU32 j=0;j= (sqres+4)) + { + carFilterData[0].data=wheels4SimData[j].getSceneQueryFilterData(0); + carFilterData[1].data=wheels4SimData[j].getSceneQueryFilterData(1); + carFilterData[2].data=wheels4SimData[j].getSceneQueryFilterData(2); + carFilterData[3].data=wheels4SimData[j].getSceneQueryFilterData(3); + wheels4DynData[j].mSweepResults=sqres; + PxVehicleWheels4SuspensionSweeps( + batchQuery, + wheels4SimData[j], wheels4DynData[j], + carFilterData, activeWheelStates, 4, + nbHitsPerQuery, + wheelShapeIds4, + vehActor, + sweepWidthScale, sweepRadiusScale); + } + else + { + PX_CHECK_MSG(false, "PxVehicleUpdate::suspensionRaycasts - numSceneQueryResults not big enough to support one raycast hit report per wheel. Increase size of sceneQueryResults"); + } + sqres+=4; + } + } + //Remainder that don't make up a block of 4. + if(numActiveWheelsInLast4>0) + { + const PxU32 j=numWheels4; + + bool activeWheelStates[4]={false,false,false,false}; + computeWheelActiveStates(4*j, veh.mWheelsSimData.mActiveWheelsBitmapBuffer, activeWheelStates); + + const PxI32* wheelShapeIds4 = wheelShapeIds + 4*j; + + wheels4DynData[j].mRaycastResults=NULL; + wheels4DynData[j].mSweepResults=NULL; + + if(NULL==vehiclesToSweep || vehiclesToSweep[i]) + { + if((sceneQueryResults + numSceneQueryResults) >= (sqres+numActiveWheelsInLast4)) + { + if(0execute(); + + END_TIMER(TIMER_SWEEPS); +} + +namespace physx +{ + void PxVehicleSuspensionSweeps + (PxBatchQuery* batchQuery, + const PxU32 nbVehicles, PxVehicleWheels** vehicles, + const PxU32 nbSceneQueryResults, PxSweepQueryResult* sceneQueryResults, const PxU16 nbHitsPerQuery, + const bool* vehiclesToSweep, + const PxF32 sweepWidthScale, const PxF32 sweepRadiusScale) + { + PX_PROFILE_ZONE("PxVehicleSuspensionSweeps::ePROFILE_SWEEPS",0); + PxVehicleUpdate::suspensionSweeps( + batchQuery, nbVehicles, vehicles, nbSceneQueryResults, sceneQueryResults, nbHitsPerQuery, vehiclesToSweep, sweepWidthScale, sweepRadiusScale); + } +} + + diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp new file mode 100644 index 000000000..1b2a67be3 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp @@ -0,0 +1,857 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleWheels.h" +#include "PxVehicleSuspWheelTire4.h" +#include "PxVehicleSuspLimitConstraintShader.h" +#include "PxVehicleDefaults.h" +#include "PxRigidDynamic.h" +#include "PxShape.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" +#include "CmBitMap.h" +#include "PxPhysics.h" +#include "PsIntrinsics.h" +#include "PsFoundation.h" + +namespace physx +{ + +extern PxVec3 gRight; +extern PxVec3 gUp; +extern PxVec3 gForward; + +PxF32 gThresholdLongSpeed=5.0f; +PxU32 gLowLongSpeedSubstepCount=3; +PxU32 gHighLongSpeedSubstepCount=1; +PxF32 gMinLongSlipDenominator=4.0f; + +extern PxF32 gToleranceScaleLength; + + +PxU32 PxVehicleWheelsSimData::computeByteSize(const PxU32 numWheels) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + const PxU32 byteSize = sizeof(PxVehicleWheels4SimData)*numWheels4 + sizeof(PxVehicleAntiRollBarData)*2*numWheels4; + return byteSize; +} + +PxU8* PxVehicleWheelsSimData::patchUpPointers(const PxU32 numWheels, PxVehicleWheelsSimData* simData, PxU8* ptrIn) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + PxU8* ptr = ptrIn; + simData->mWheels4SimData = reinterpret_cast(ptr); + ptr += sizeof(PxVehicleWheels4SimData)*numWheels4; + simData->mAntiRollBars = reinterpret_cast(ptr); + ptr += sizeof(PxVehicleAntiRollBarData)*numWheels4*2; + PX_ASSERT((ptrIn + computeByteSize(numWheels)) == ptr); + return ptr; +} + +PxVehicleWheelsSimData::PxVehicleWheelsSimData(const PxU32 numWheels) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + + //Set numWheels + mNbWheels4 = numWheels4; + mNbActiveWheels = numWheels; + + //Set numAntiRollBars to zero. + mNbAntiRollBars4 = 2*numWheels4; + mNbActiveAntiRollBars = 0; + + //Placement new for wheels4 + for(PxU32 i=0;i> 5)); + for(PxU32 i=0;i(PX_ALLOC(byteSize, "PxVehicleWheelsSimData")); + + //Patchup pointers + PxU8* ptr = ptrStart; + PxVehicleWheelsSimData* simData = reinterpret_cast(ptr); + ptr += sizeof(PxVehicleWheelsSimData); + ptr = patchUpPointers(numWheels, simData, ptr); + PX_ASSERT((ptrStart+ byteSize) == ptr); + + //Constructor. + new(simData) PxVehicleWheelsSimData(numWheels); + + //Finished. + return simData; +} + +void PxVehicleWheelsSimData::setChassisMass(const PxF32 chassisMass) +{ + + //Target spring natural frequency = 9.66 + //Target spring damping ratio = 0.62 + const PxF32 mult=1.0f/(1.0f*mNbActiveWheels); + const PxF32 sprungMass=chassisMass*mult; + const PxF32 w0=9.66f; + const PxF32 r=0.62f; + for(PxU32 i=0;i> 5)); + + return *this; +} + +void PxVehicleWheelsSimData::copy(const PxVehicleWheelsSimData& src, const PxU32 srcWheel, const PxU32 wheel) +{ + PX_CHECK_AND_RETURN(srcWheel < src.mNbActiveWheels, "Illegal src wheel"); + PX_CHECK_AND_RETURN(wheel < mNbActiveWheels, "Illegal target wheel"); + + setSuspensionData(wheel,src.getSuspensionData(srcWheel)); + setWheelData(wheel,src.getWheelData(srcWheel)); + setTireData(wheel,src.getTireData(srcWheel)); + setSuspTravelDirection(wheel,src.getSuspTravelDirection(srcWheel)); + setSuspForceAppPointOffset(wheel,src.getSuspForceAppPointOffset(srcWheel)); + setTireForceAppPointOffset(wheel,src.getTireForceAppPointOffset(srcWheel)); + setWheelCentreOffset(wheel,src.getWheelCentreOffset(srcWheel)); + setWheelShapeMapping(wheel, src.getWheelShapeMapping(srcWheel)); + setSceneQueryFilterData(wheel, src.getSceneQueryFilterData(srcWheel)); + if(src.getIsWheelDisabled(srcWheel)) + disableWheel(wheel); + else + enableWheel(wheel); +} + +bool PxVehicleWheelsSimData::isValid() const +{ + for(PxU32 i=0;i> 5); + bm.reset(wheel); +} + +void PxVehicleWheelsSimData::enableWheel(const PxU32 wheel) +{ + PX_CHECK_AND_RETURN(wheel < 4*mNbWheels4, "PxVehicleWheelsSimData::disableWheel - Illegal wheel"); + + Cm::BitMap bm; + bm.setWords(mActiveWheelsBitmapBuffer, ((PX_MAX_NB_WHEELS + 31) & ~31) >> 5); + bm.set(wheel); +} + +bool PxVehicleWheelsSimData::getIsWheelDisabled(const PxU32 wheel) const +{ + PX_CHECK_AND_RETURN_VAL(wheel < 4*mNbWheels4, "PxVehicleWheelsSimData::getIsWheelDisabled - Illegal wheel", false); + Cm::BitMap bm; + bm.setWords(const_cast(mActiveWheelsBitmapBuffer), ((PX_MAX_NB_WHEELS + 31) & ~31) >> 5); + return (bm.test(wheel) ? false : true); +} + +const PxVehicleSuspensionData& PxVehicleWheelsSimData::getSuspensionData(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getSuspensionData - Illegal wheel"); + return mWheels4SimData[id>>2].getSuspensionData(id & 3); +} + +const PxVehicleWheelData& PxVehicleWheelsSimData::getWheelData(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getWheelData - Illegal wheel"); + return mWheels4SimData[id>>2].getWheelData(id & 3); +} + +const PxVehicleTireData& PxVehicleWheelsSimData::getTireData(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getTireData - Illegal wheel"); + return mWheels4SimData[id>>2].getTireData(id & 3); +} + +const PxVec3& PxVehicleWheelsSimData::getSuspTravelDirection(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getSuspTravelDirection - Illegal wheel"); + return mWheels4SimData[id>>2].getSuspTravelDirection(id & 3); +} + +const PxVec3& PxVehicleWheelsSimData::getSuspForceAppPointOffset(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getSuspForceAppPointOffset - Illegal wheel"); + return mWheels4SimData[id>>2].getSuspForceAppPointOffset(id & 3); +} + +const PxVec3& PxVehicleWheelsSimData::getTireForceAppPointOffset(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getTireForceAppPointOffset - Illegal wheel"); + return mWheels4SimData[id>>2].getTireForceAppPointOffset(id & 3); +} + +const PxVec3& PxVehicleWheelsSimData::getWheelCentreOffset(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getWheelCentreOffset - Illegal wheel"); + return mWheels4SimData[id>>2].getWheelCentreOffset(id & 3); +} + +PxI32 PxVehicleWheelsSimData::getWheelShapeMapping(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getWheelShapeMapping - Illegal wheel"); + return mWheels4SimData[id>>2].getWheelShapeMapping(id & 3); +} + +const PxFilterData& PxVehicleWheelsSimData::getSceneQueryFilterData(const PxU32 id) const +{ + PX_CHECK_MSG(id < 4*mNbWheels4, "PxVehicleWheelsSimData::getSceneQueryFilterData - Illegal wheel"); + return mWheels4SimData[id>>2].getSceneQueryFilterData(id & 3); +} + +const PxVehicleAntiRollBarData& PxVehicleWheelsSimData::getAntiRollBarData(const PxU32 antiRollId) const +{ + PX_CHECK_MSG(antiRollId < mNbActiveAntiRollBars, "PxVehicleWheelsSimData::getAntiRollBarData - Illegal anti-roll bar"); + return mAntiRollBars[antiRollId]; +} + +void PxVehicleWheelsSimData::setSuspensionData(const PxU32 id, const PxVehicleSuspensionData& susp) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setSuspensionData - Illegal wheel"); + mWheels4SimData[id>>2].setSuspensionData(id & 3, susp); +} + +void PxVehicleWheelsSimData::setWheelData(const PxU32 id, const PxVehicleWheelData& wheel) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setWheelData - Illegal wheel"); + mWheels4SimData[id>>2].setWheelData(id & 3, wheel); +} + +void PxVehicleWheelsSimData::setTireData(const PxU32 id, const PxVehicleTireData& tire) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setTireData - Illegal wheel"); + mWheels4SimData[id>>2].setTireData(id & 3, tire); +} + +void PxVehicleWheelsSimData::setSuspTravelDirection(const PxU32 id, const PxVec3& dir) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setSuspTravelDirection - Illegal wheel"); + mWheels4SimData[id>>2].setSuspTravelDirection(id & 3, dir); +} + +void PxVehicleWheelsSimData::setSuspForceAppPointOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setSuspForceAppPointOffset - Illegal wheel"); + mWheels4SimData[id>>2].setSuspForceAppPointOffset(id & 3, offset); +} + +void PxVehicleWheelsSimData::setTireForceAppPointOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setTireForceAppPointOffset - Illegal wheel"); + mWheels4SimData[id>>2].setTireForceAppPointOffset(id & 3, offset); +} + +void PxVehicleWheelsSimData::setWheelCentreOffset(const PxU32 id, const PxVec3& offset) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setWheelCentreOffset - Illegal wheel"); + mWheels4SimData[id>>2].setWheelCentreOffset(id & 3, offset); +} + +void PxVehicleWheelsSimData::setWheelShapeMapping(const PxU32 id, const PxI32 shapeId) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setWheelShapeMapping - Illegal wheel"); + mWheels4SimData[id>>2].setWheelShapeMapping(id & 3, shapeId); +} + +void PxVehicleWheelsSimData::setSceneQueryFilterData(const PxU32 id, const PxFilterData& sqFilterData) +{ + PX_CHECK_AND_RETURN(id < 4*mNbWheels4, "PxVehicleWheelsSimData::setSceneQueryFilterData - Illegal wheel"); + mWheels4SimData[id>>2].setSceneQueryFilterData(id & 3, sqFilterData); +} + +void PxVehicleWheelsSimData::setTireLoadFilterData(const PxVehicleTireLoadFilterData& tireLoadFilter) +{ + PX_CHECK_AND_RETURN(tireLoadFilter.mMaxNormalisedLoad>tireLoadFilter.mMinNormalisedLoad, "Illegal graph points"); + PX_CHECK_AND_RETURN(tireLoadFilter.mMaxFilteredNormalisedLoad>0, "Max filtered load must be greater than zero"); + mNormalisedLoadFilter=tireLoadFilter; + mNormalisedLoadFilter.mDenominator=1.0f/(mNormalisedLoadFilter.mMaxNormalisedLoad-mNormalisedLoadFilter.mMinNormalisedLoad); +} + +PxU32 PxVehicleWheelsSimData::addAntiRollBarData(const PxVehicleAntiRollBarData& antiRollBar) +{ + PX_CHECK_AND_RETURN_VAL(antiRollBar.isValid(), "Illegal antiRollBar", 0xffffffff) + PX_CHECK_AND_RETURN_VAL(antiRollBar.mWheel0 < mNbActiveWheels, "Illegal wheel0", 0xffffffff); + PX_CHECK_AND_RETURN_VAL(antiRollBar.mWheel1 < mNbActiveWheels, "Illegal wheel1", 0xffffffff); + + //If the anti-roll pair already exists then modify it. + for(PxU32 i = 0; i < mNbActiveAntiRollBars; i++) + { + if( ((mAntiRollBars[i].mWheel0 == antiRollBar.mWheel0) && (mAntiRollBars[i].mWheel1 == antiRollBar.mWheel1)) || + ((mAntiRollBars[i].mWheel1 == antiRollBar.mWheel0) && (mAntiRollBars[i].mWheel0 == antiRollBar.mWheel1)) ) + { + mAntiRollBars[i].mStiffness = antiRollBar.mStiffness; + return i; + } + } + + //Check we have space for an extra anti-roll bar. + PX_CHECK_AND_RETURN_VAL(mNbActiveAntiRollBars < 2*mNbWheels4, "The buffer of anti-roll bars is full", 0xffffffff); + + //Add a new anti-roll bar. + const PxU32 id = mNbActiveAntiRollBars; + mAntiRollBars[mNbActiveAntiRollBars] = antiRollBar; + mNbActiveAntiRollBars++; + + //Finished. + return id; +} + +//Only used for serialization. +void PxVehicleWheelsSimData::setAntiRollBarData(const PxU32 id, const PxVehicleAntiRollBarData& antiRoll) +{ + PX_UNUSED(id); + addAntiRollBarData(antiRoll); +} + +void PxVehicleWheelsSimData::setSubStepCount(const PxReal thresholdLongitudinalSpeed, const PxU32 lowForwardSpeedSubStepCount, const PxU32 highForwardSpeedSubStepCount) +{ + PX_CHECK_AND_RETURN(thresholdLongitudinalSpeed>=0, "thresholdLongitudinalSpeed must be greater than or equal to zero."); + PX_CHECK_AND_RETURN(lowForwardSpeedSubStepCount>0, "lowForwardSpeedSubStepCount must be greater than zero."); + PX_CHECK_AND_RETURN(highForwardSpeedSubStepCount>0, "highForwardSpeedSubStepCount must be greater than zero."); + + mThresholdLongitudinalSpeed=thresholdLongitudinalSpeed; + mLowForwardSpeedSubStepCount=lowForwardSpeedSubStepCount; + mHighForwardSpeedSubStepCount=highForwardSpeedSubStepCount; +} + +void PxVehicleWheelsSimData::setMinLongSlipDenominator(const PxReal minLongSlipDenominator) +{ + PX_CHECK_AND_RETURN(minLongSlipDenominator>0, "minLongSlipDenominator must be greater than or equal to zero."); + mMinLongSlipDenominator=minLongSlipDenominator; +} + + + +///////////////////////////// + +PxU32 PxVehicleWheelsDynData::computeByteSize(const PxU32 numWheels) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + const PxU32 byteSize = + sizeof(PxVehicleWheels4DynData)*numWheels4 + + sizeof(PxVehicleTireForceCalculator) + sizeof(void*)*4*numWheels4 + + sizeof(void*)*4*numWheels4 + + sizeof(PxVehicleConstraintShader)*numWheels4; + return byteSize; +} + +PxU8* PxVehicleWheelsDynData::patchUpPointers(const PxU32 numWheels, PxVehicleWheelsDynData* dynData, PxU8* ptrIn) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + PxU8* ptr = ptrIn; + dynData->mWheels4DynData = reinterpret_cast(ptr); + ptr += sizeof(PxVehicleWheels4DynData)*numWheels4; + dynData->mTireForceCalculators = reinterpret_cast(ptr); + ptr += sizeof(PxVehicleTireForceCalculator); + dynData->mTireForceCalculators->mShaderData = reinterpret_cast(ptr); + ptr += sizeof(void*)*4*numWheels4; + dynData->mUserDatas = reinterpret_cast(ptr); + ptr += sizeof(void*)*4*numWheels4; + for(PxU32 i=0;imWheels4DynData[i].setVehicleConstraintShader(shader); + ptr += sizeof(PxVehicleConstraintShader); + } + + PX_ASSERT((ptrIn + computeByteSize(numWheels)) == ptr); + return ptr; +} + +PxVehicleWheelsDynData::PxVehicleWheelsDynData(const PxU32 numWheels) +{ + const PxU32 numWheels4 =(((numWheels + 3) & ~3) >> 2); + + mNbWheels4=numWheels4; + mNbActiveWheels=numWheels; + + //Placement new for wheels4 + for(PxU32 i=0;imShaderData[i]=NULL; + } + new(mTireForceCalculators) PxVehicleTireForceCalculator; + + //Initialise user data + for(PxU32 i=0;i<4*numWheels4;i++) + { + mUserDatas[i]=NULL; + } +} + +bool PxVehicleWheelsDynData::isValid() const +{ + for(PxU32 i=0;imShader=tireForceShaderFn; +} + +void PxVehicleWheelsDynData::setTireForceShaderData(const PxU32 tireId, const void* tireForceShaderData) +{ + PX_CHECK_AND_RETURN(tireId < mNbActiveWheels, "PxVehicleWheelsDynData::setTireForceShaderData - Illegal tire"); + mTireForceCalculators->mShaderData[tireId]=tireForceShaderData; +} + +const void* PxVehicleWheelsDynData::getTireForceShaderData(const PxU32 tireId) const +{ + PX_CHECK_AND_RETURN_VAL(tireId < mNbActiveWheels, "PxVehicleWheelsDynData::getTireForceShaderData - Illegal tire", NULL); + return mTireForceCalculators->mShaderData[tireId]; +} + +void PxVehicleWheelsDynData::setWheelRotationSpeed(const PxU32 wheelIdx, const PxReal speed) +{ + PX_CHECK_AND_RETURN(wheelIdx < mNbActiveWheels, "PxVehicleWheelsDynData::setWheelRotationSpeed - Illegal wheel"); + PxVehicleWheels4DynData& suspWheelTire4=mWheels4DynData[(wheelIdx>>2)]; + suspWheelTire4.mWheelSpeeds[wheelIdx & 3] = speed; + suspWheelTire4.mCorrectedWheelSpeeds[wheelIdx & 3] = speed; +} + +PxReal PxVehicleWheelsDynData::getWheelRotationSpeed(const PxU32 wheelIdx) const +{ + PX_CHECK_AND_RETURN_VAL(wheelIdx < mNbActiveWheels, "PxVehicleWheelsDynData::getWheelRotationSpeed - Illegal wheel", 0.0f); + const PxVehicleWheels4DynData& suspWheelTire4=mWheels4DynData[(wheelIdx>>2)]; + return suspWheelTire4.mCorrectedWheelSpeeds[wheelIdx & 3]; +} + +void PxVehicleWheelsDynData::setWheelRotationAngle(const PxU32 wheelIdx, const PxReal angle) +{ + PX_CHECK_AND_RETURN(wheelIdx < mNbActiveWheels, "PxVehicleWheelsDynData::setWheelRotationAngle - Illegal wheel"); + PxVehicleWheels4DynData& suspWheelTire4=mWheels4DynData[(wheelIdx>>2)]; + suspWheelTire4.mWheelRotationAngles[wheelIdx & 3] = angle; +} + +PxReal PxVehicleWheelsDynData::getWheelRotationAngle(const PxU32 wheelIdx) const +{ + PX_CHECK_AND_RETURN_VAL(wheelIdx < mNbActiveWheels, "PxVehicleWheelsDynData::getWheelRotationAngle - Illegal wheel", 0.0f); + const PxVehicleWheels4DynData& suspWheelTire4=mWheels4DynData[(wheelIdx>>2)]; + return suspWheelTire4.mWheelRotationAngles[wheelIdx & 3]; +} + +void PxVehicleWheels::setToRestState() +{ + //Set the rigid body to rest and clear all the accumulated forces and impulses. + if(!(mActor->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC)) + { + mActor->setLinearVelocity(PxVec3(0,0,0)); + mActor->setAngularVelocity(PxVec3(0,0,0)); + mActor->clearForce(PxForceMode::eACCELERATION); + mActor->clearForce(PxForceMode::eVELOCITY_CHANGE); + mActor->clearTorque(PxForceMode::eACCELERATION); + mActor->clearTorque(PxForceMode::eVELOCITY_CHANGE); + } + + //Set the wheels to rest state. + mWheelsDynData.setToRestState(); +} + +bool PxVehicleWheels::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mWheelsSimData.isValid(), "invalid mWheelsSimData", false); + PX_CHECK_AND_RETURN_VAL(mWheelsDynData.isValid(), "invalid mWheelsDynData", false); + return true; +} + +PxU32 PxVehicleWheels::computeByteSize(const PxU32 numWheels) +{ + const PxU32 byteSize = + PxVehicleWheelsSimData::computeByteSize(numWheels) + + PxVehicleWheelsDynData::computeByteSize(numWheels); + return byteSize; +} + +PxU8* PxVehicleWheels::patchupPointers(const PxU32 numWheels, PxVehicleWheels* vehWheels, PxU8* ptrIn) +{ + PxU8* ptr = ptrIn; + ptr = PxVehicleWheelsSimData::patchUpPointers(numWheels, &vehWheels->mWheelsSimData, ptr); + ptr = PxVehicleWheelsDynData::patchUpPointers(numWheels, &vehWheels->mWheelsDynData, ptr); + PX_ASSERT((ptrIn + computeByteSize(numWheels)) == ptr); + return ptr; +} + +void PxVehicleWheels::init(const PxU32 numWheels) +{ + new(&mWheelsSimData) PxVehicleWheelsSimData(numWheels); + new(&mWheelsDynData) PxVehicleWheelsDynData(numWheels); + + for(PxU32 i = 0; i < mWheelsSimData.mNbWheels4; i++) + { + new(&mWheelsDynData.mWheels4DynData[i].getVehicletConstraintShader()) PxVehicleConstraintShader(this); + } + + mOnConstraintReleaseCounter = Ps::to8(mWheelsSimData.mNbWheels4); +} + +void PxVehicleWheels::free() +{ + PX_CHECK_AND_RETURN(mWheelsSimData.mNbWheels4>0, "Cars with zero wheels are illegal"); + + const PxU32 numSuspWheelTire4 = mWheelsSimData.mNbWheels4; + + for(PxU32 i=0;igetMass()-totalSprungMass)/vehActor->getMass()) < 0.01f, "Sum of suspension sprung masses doesn't match actor mass"); +#endif + + //Copy the simulation data. + mWheelsSimData=wheelsData; + + //Set the actor pointer. + mActor=vehActor; + + //Set all the sq result ptrs to null. + const PxU32 numSuspWheelTire4=wheelsData.mNbWheels4; + for(PxU32 i=0;icreateConstraint(vehActor, NULL, shader, t, sizeof(PxVehicleConstraintShader::VehicleConstraintData)); + shader.mConstraint->markDirty(); + } + + //Set up the shader data ptrs. + for(PxU32 i=0;igetNbShapes(), "Illegal wheel shape mapping, shape does not exist on actor"); + + //Compute the shape local pose + const PxTransform chassisCMOffset=mActor->getCMassLocalPose(); + PxTransform wheelOffset=chassisCMOffset; + wheelOffset.p+=mWheelsSimData.getWheelCentreOffset(i); + //Pose the shape. + PxShape* shapeBuffer[1]; + mActor->getShapes(shapeBuffer,1,PxU32(shapeId)); + shapeBuffer[0]->setLocalPose(wheelOffset); + } + } +} + +void PxVehicleWheels::requiresObjects(PxProcessPxBaseCallback& c) +{ + c.process(*mActor); + + for(PxU32 i=0;i(old); + new_nx->setConstraintFunctions(*connector, shaders); + return new_nx; +} + +void PxVehicleWheels::resolveReferences(PxDeserializationContext& context) +{ + context.translatePxBase(mActor); + + for(PxU32 i=0;igetGlobalPose().transform(mActor->getCMassLocalPose()); + return mActor->getLinearVelocity().dot(vehicleChassisTrnsfm.q.rotate(gForward)); +} + +PxReal PxVehicleWheels::computeSidewaysSpeed() const +{ + const PxTransform vehicleChassisTrnsfm=mActor->getGlobalPose().transform(mActor->getCMassLocalPose()); + return mActor->getLinearVelocity().dot(vehicleChassisTrnsfm.q.rotate(gRight)); +} + +//////////////////////////////////////////////////////////////////////////// + +void PxVehicleWheelsDynData::setUserData(const PxU32 tireIdx, void* userData) +{ + PX_CHECK_AND_RETURN(tireIdx < mNbActiveWheels, "PxVehicleWheelsDynData::setUserData - Illegal wheel"); + mUserDatas[tireIdx]=userData; +} + +void* PxVehicleWheelsDynData::getUserData(const PxU32 tireIdx) const +{ + PX_CHECK_AND_RETURN_VAL(tireIdx < mNbActiveWheels, "PxVehicleWheelsDynData::setUserData - Illegal wheel", NULL); + return mUserDatas[tireIdx]; +} + + +//////////////////////////////////////////////////////////////////////////// + +void PxVehicleWheelsDynData::copy(const PxVehicleWheelsDynData& src, const PxU32 srcWheel, const PxU32 trgWheel) +{ + PX_CHECK_AND_RETURN(srcWheel < src.mNbActiveWheels, "PxVehicleWheelsDynData::copy - Illegal src wheel"); + PX_CHECK_AND_RETURN(trgWheel < mNbActiveWheels, "PxVehicleWheelsDynData::copy - Illegal trg wheel"); + + const PxVehicleWheels4DynData& src4 = src.mWheels4DynData[(srcWheel>>2)]; + PxVehicleWheels4DynData& trg4 = mWheels4DynData[(trgWheel>>2)]; + + trg4.mWheelSpeeds[trgWheel & 3] = src4.mWheelSpeeds[srcWheel & 3]; + trg4.mCorrectedWheelSpeeds[trgWheel & 3] = src4.mCorrectedWheelSpeeds[srcWheel & 3]; + trg4.mTireLowForwardSpeedTimers[trgWheel & 3] = src4.mTireLowForwardSpeedTimers[srcWheel & 3]; + trg4.mTireLowSideSpeedTimers[trgWheel & 3] = src4.mTireLowSideSpeedTimers[srcWheel & 3]; + trg4.mWheelRotationAngles[trgWheel & 3] = src4.mWheelRotationAngles[srcWheel & 3]; + + if(src4.mRaycastResults) + { + const PxVehicleWheels4DynData::SuspLineRaycast& suspLineRaycastSrc = reinterpret_cast(src4.mQueryOrCachedHitResults); + PxVehicleWheels4DynData::SuspLineRaycast& suspLineRaycastTrg = reinterpret_cast(trg4.mQueryOrCachedHitResults); + + suspLineRaycastTrg.mStarts[trgWheel & 3] = suspLineRaycastSrc.mStarts[srcWheel & 3]; + suspLineRaycastTrg.mDirs[trgWheel & 3] = suspLineRaycastSrc.mDirs[srcWheel & 3]; + suspLineRaycastTrg.mLengths[trgWheel & 3] = suspLineRaycastSrc.mLengths[srcWheel & 3]; + } + else if(src4.mSweepResults) + { + const PxVehicleWheels4DynData::SuspLineSweep& suspLineSweepSrc = reinterpret_cast(src4.mQueryOrCachedHitResults); + PxVehicleWheels4DynData::SuspLineSweep& suspLineSweepTrg = reinterpret_cast(trg4.mQueryOrCachedHitResults); + + suspLineSweepTrg.mStartPose[trgWheel & 3] = suspLineSweepSrc.mStartPose[srcWheel & 3]; + suspLineSweepTrg.mDirs[trgWheel & 3] = suspLineSweepSrc.mDirs[srcWheel & 3]; + suspLineSweepTrg.mLengths[trgWheel & 3] = suspLineSweepSrc.mLengths[srcWheel & 3]; + } + else + { + const PxVehicleWheels4DynData::CachedSuspLineSceneQuerytHitResult& cachedHitResultSrc = reinterpret_cast(src4.mQueryOrCachedHitResults); + PxVehicleWheels4DynData::CachedSuspLineSceneQuerytHitResult& cachedHitResultTrg = reinterpret_cast(trg4.mQueryOrCachedHitResults); + + cachedHitResultTrg.mPlanes[trgWheel & 3] = cachedHitResultSrc.mPlanes[srcWheel & 3]; + cachedHitResultTrg.mFrictionMultipliers[trgWheel & 3] = cachedHitResultSrc.mFrictionMultipliers[srcWheel & 3]; + cachedHitResultTrg.mCounts[trgWheel & 3] = cachedHitResultSrc.mCounts[srcWheel & 3]; + cachedHitResultTrg.mDistances[trgWheel & 3] = cachedHitResultSrc.mDistances[srcWheel & 3]; + } +} + + +} //namespace physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp new file mode 100644 index 000000000..380ae49b8 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp @@ -0,0 +1,474 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleUtilControl.h" +#include "PxVehicleDrive4W.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +#if PX_CHECKED +void testValidAnalogValue(const PxF32 actualValue, const PxF32 minVal, const PxF32 maxVal, const char* errorString) +{ + const PxF32 tolerance = 1e-2f; + PX_CHECK_MSG((actualValue > (minVal - tolerance)) && (actualValue < (maxVal + tolerance)), errorString); +} +#endif + + +PxF32 processDigitalValue +(const PxU32 inputType, + const PxVehicleKeySmoothingData& keySmoothing, const bool digitalValue, + const PxF32 timestep, + const PxF32 analogVal) +{ + PxF32 newAnalogVal=analogVal; + if(digitalValue) + { + newAnalogVal+=keySmoothing.mRiseRates[inputType]*timestep; + } + else + { + newAnalogVal-=keySmoothing.mFallRates[inputType]*timestep; + } + + return PxClamp(newAnalogVal,0.0f,1.0f); +} + +void PxVehicleDriveSmoothDigitalRawInputsAndSetAnalogInputs +(const PxVehicleKeySmoothingData& keySmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxF32 timestep, + const bool isVehicleInAir, + const PxVehicleWheels& vehicle, PxVehicleDriveDynData& driveDynData) +{ + const bool gearup=rawInputData.getGearUp(); + const bool geardown=rawInputData.getGearDown(); + driveDynData.setGearDown(geardown); + driveDynData.setGearUp(gearup); + + const PxF32 accel=processDigitalValue(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL,keySmoothing,rawInputData.getDigitalAccel(),timestep,driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL)); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL,accel); + + const PxF32 brake=processDigitalValue(PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE,keySmoothing,rawInputData.getDigitalBrake(),timestep,driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE)); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE,brake); + + const PxF32 handbrake=processDigitalValue(PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE,keySmoothing,rawInputData.getDigitalHandbrake(),timestep,driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE)); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE,handbrake); + + PxF32 steerLeft=processDigitalValue(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT,keySmoothing,rawInputData.getDigitalSteerLeft(),timestep,driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT)); + PxF32 steerRight=processDigitalValue(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT,keySmoothing,rawInputData.getDigitalSteerRight(),timestep,driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT)); + const PxF32 vz=vehicle.computeForwardSpeed(); + const PxF32 vzAbs=PxAbs(vz); + const PxF32 maxSteer=(isVehicleInAir ? 1.0f :steerVsForwardSpeedTable.getYVal(vzAbs)); + const PxF32 steer=PxAbs(steerRight-steerLeft); + if(steer>maxSteer) + { + const PxF32 k=maxSteer/steer; + steerLeft*=k; + steerRight*=k; + } + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT, steerLeft); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT, steerRight); +} + +////////////////////////////////// + +//process value in range(0,1) +PX_FORCE_INLINE PxF32 processPositiveAnalogValue +(const PxF32 riseRate, const PxF32 fallRate, + const PxF32 currentVal, const PxF32 targetVal, + const PxF32 timestep) +{ + PX_ASSERT(targetVal>=-0.01f && targetVal<=1.01f); + PxF32 val; + if(currentVal0) + { + val=currentVal-fallRate*timestep; + val=PxMax(val,0.0f); + } + else if(currentVal<0) + { + val=currentVal+fallRate*timestep; + val=PxMin(val,0.0f); + } + } + else + { + if(currentVal < targetVal) + { + if(currentVal<0) + { + val=currentVal + fallRate*timestep; + val=PxMin(val,targetVal); + } + else + { + val=currentVal + riseRate*timestep; + val=PxMin(val,targetVal); + } + } + else + { + if(currentVal>0) + { + val=currentVal - fallRate*timestep; + val=PxMax(val,targetVal); + } + else + { + val=currentVal - riseRate*timestep; + val=PxMax(val,targetVal); + } + } + } + return val; +} + +void PxVehicleDriveSmoothAnalogRawInputsAndSetAnalogInputs +(const PxVehiclePadSmoothingData& padSmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxF32 timestep, + const bool isVehicleInAir, + const PxVehicleWheels& vehicle, PxVehicleDriveDynData& driveDynData) +{ + //gearup/geardown + const bool gearup=rawInputData.getGearUp(); + const bool geardown=rawInputData.getGearDown(); + driveDynData.setGearUp(gearup); + driveDynData.setGearDown(geardown); + + //Update analog inputs for focus vehicle. + + //Process the accel. + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL]; + const PxF32 currentVal=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL); + const PxF32 targetVal=rawInputData.getAnalogAccel(); + const PxF32 accel=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_ACCEL, accel); + } + + //Process the brake + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE]; + const PxF32 currentVal=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE); + const PxF32 targetVal=rawInputData.getAnalogBrake(); + const PxF32 brake=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_BRAKE, brake); + } + + //Process the handbrake. + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE]; + const PxF32 currentVal=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE); + const PxF32 targetVal=rawInputData.getAnalogHandbrake(); + const PxF32 handbrake=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_HANDBRAKE, handbrake); + } + + //Process the steer + { + const PxF32 vz=vehicle.computeForwardSpeed(); + const PxF32 vzAbs=PxAbs(vz); + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT]; + const PxF32 currentVal=driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT)-driveDynData.getAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT); + const PxF32 targetVal=rawInputData.getAnalogSteer()*(isVehicleInAir ? 1.0f :steerVsForwardSpeedTable.getYVal(vzAbs)); + const PxF32 steer=processAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_LEFT, 0.0f); + driveDynData.setAnalogInput(PxVehicleDrive4WControl::eANALOG_INPUT_STEER_RIGHT, steer); + } +} + + +//////////////// + +void PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs +(const PxVehicleKeySmoothingData& keySmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxF32 timestep, + const bool isVehicleInAir, + PxVehicleDrive4W& focusVehicle) +{ + PxVehicleDriveSmoothDigitalRawInputsAndSetAnalogInputs + (keySmoothing, steerVsForwardSpeedTable, rawInputData, timestep, isVehicleInAir, focusVehicle, focusVehicle.mDriveDynData); +} + +void PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs +(const PxVehiclePadSmoothingData& padSmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDrive4WRawInputData& rawInputData, + const PxF32 timestep, + const bool isVehicleInAir, + PxVehicleDrive4W& focusVehicle) +{ + PxVehicleDriveSmoothAnalogRawInputsAndSetAnalogInputs + (padSmoothing,steerVsForwardSpeedTable,rawInputData,timestep,isVehicleInAir,focusVehicle,focusVehicle.mDriveDynData); +} + +//////////////// + +void PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs +(const PxVehicleKeySmoothingData& keySmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDriveNWRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDriveNW& focusVehicle) +{ + PxVehicleDriveSmoothDigitalRawInputsAndSetAnalogInputs + (keySmoothing,steerVsForwardSpeedTable,rawInputData,timestep,isVehicleInAir,focusVehicle,focusVehicle.mDriveDynData); +} + +void PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs +(const PxVehiclePadSmoothingData& padSmoothing, const PxFixedSizeLookupTable<8>& steerVsForwardSpeedTable, + const PxVehicleDriveNWRawInputData& rawInputData, + const PxReal timestep, + const bool isVehicleInAir, + PxVehicleDriveNW& focusVehicle) +{ + PxVehicleDriveSmoothAnalogRawInputsAndSetAnalogInputs + (padSmoothing,steerVsForwardSpeedTable,rawInputData,timestep,isVehicleInAir,focusVehicle,focusVehicle.mDriveDynData); +} + +//////////////// + +void PxVehicleDriveTankSmoothAnalogRawInputsAndSetAnalogInputs +(const PxVehiclePadSmoothingData& padSmoothing, + const PxVehicleDriveTankRawInputData& rawInputData, + const PxReal timestep, + PxVehicleDriveTank& focusVehicle) +{ + //Process the gearup/geardown buttons. + const bool gearup=rawInputData.getGearUp(); + const bool geardown=rawInputData.getGearDown(); + focusVehicle.mDriveDynData.setGearUp(gearup); + focusVehicle.mDriveDynData.setGearDown(geardown); + + //Process the accel. + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL); + const PxF32 targetVal=rawInputData.getAnalogAccel(); + const PxF32 accel=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL, accel); + } + + PX_ASSERT(focusVehicle.getDriveModel()==rawInputData.getDriveModel()); + switch(rawInputData.getDriveModel()) + { + case PxVehicleDriveTankControlModel::eSPECIAL: + { + //Process the left brake. + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT); + const PxF32 targetVal=rawInputData.getAnalogLeftBrake(); + const PxF32 accel=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, accel); + } + + //Process the right brake. + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT); + const PxF32 targetVal=rawInputData.getAnalogRightBrake(); + const PxF32 accel=processPositiveAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, accel); + } + + //Left thrust + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT); + const PxF32 targetVal=rawInputData.getAnalogLeftThrust(); + const PxF32 val=processAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, val); + } + + //Right thrust + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT); + const PxF32 targetVal=rawInputData.getAnalogRightThrust(); + const PxF32 val=processAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, val); + } + } + break; + + case PxVehicleDriveTankControlModel::eSTANDARD: + { + //Right thrust + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT); + const PxF32 targetVal=rawInputData.getAnalogRightThrust()-rawInputData.getAnalogRightBrake(); + const PxF32 val=processAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + if(val>0) + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, val); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, 0.0f); + } + else + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, 0.0f); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, -val); + } + } + + //Left thrust + { + const PxF32 riseRate=padSmoothing.mRiseRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]; + const PxF32 fallRate=padSmoothing.mFallRates[PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT]; + const PxF32 currentVal=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT); + const PxF32 targetVal=rawInputData.getAnalogLeftThrust()-rawInputData.getAnalogLeftBrake(); + const PxF32 val=processAnalogValue(riseRate,fallRate,currentVal,targetVal,timestep); + if(val>0) + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, val); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, 0.0f); + } + else + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, 0.0f); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, -val); + } + } + } + break; + } +} + +void PxVehicleDriveTankSmoothDigitalRawInputsAndSetAnalogInputs +(const PxVehicleKeySmoothingData& keySmoothing, + const PxVehicleDriveTankRawInputData& rawInputData, + const PxF32 timestep, + PxVehicleDriveTank& focusVehicle) +{ + PxF32 val; + val=processDigitalValue(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL,keySmoothing,rawInputData.getDigitalAccel(),timestep,focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL)); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_ACCEL, val); + val=processDigitalValue(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT,keySmoothing,rawInputData.getDigitalLeftThrust(),timestep,focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT)); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, val); + val=processDigitalValue(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT,keySmoothing,rawInputData.getDigitalRightThrust(),timestep,focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT)); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, val); + val=processDigitalValue(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT,keySmoothing,rawInputData.getDigitalLeftBrake(),timestep,focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT)); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, val); + val=processDigitalValue(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT,keySmoothing,rawInputData.getDigitalRightBrake(),timestep,focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT)); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, val); + + //Update digital inputs for focus vehicle. + focusVehicle.mDriveDynData.setGearUp(rawInputData.getGearUp()); + focusVehicle.mDriveDynData.setGearDown(rawInputData.getGearDown()); + + switch(rawInputData.getDriveModel()) + { + case PxVehicleDriveTankControlModel::eSPECIAL: + { + const PxF32 thrustL=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, thrustL); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, 0.0f); + + const PxF32 thrustR=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, thrustR); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, 0.0f); + } + break; + case PxVehicleDriveTankControlModel::eSTANDARD: + { + const PxF32 thrustL=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT); + if(thrustL>0) + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, thrustL); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, 0.0f); + } + else + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_LEFT, 0.0f); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_LEFT, -thrustL); + } + + const PxF32 thrustR=focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT)-focusVehicle.mDriveDynData.getAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT); + if(thrustR>0) + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, thrustR); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, 0.0f); + } + else + { + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_THRUST_RIGHT, 0.0f); + focusVehicle.mDriveDynData.setAnalogInput(PxVehicleDriveTankControl::eANALOG_INPUT_BRAKE_RIGHT, -thrustR); + } + } + break; + } + +} + + + +} //physx + diff --git a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp new file mode 100644 index 000000000..32604c924 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp @@ -0,0 +1,246 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMath.h" +#include "PxVehicleUtilSetup.h" +#include "PxVehicleDrive4W.h" +#include "PxVehicleDriveNW.h" +#include "PxVehicleDriveTank.h" +#include "PxVehicleNoDrive.h" +#include "PxVehicleWheels.h" +#include "PxVehicleUtil.h" +#include "PxVehicleUpdate.h" +#include "PsFoundation.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +void enable3WMode(const PxU32 rightDirection, const PxU32 upDirection, const bool removeFrontWheel, PxVehicleWheelsSimData& wheelsSimData, PxVehicleWheelsDynData& wheelsDynData, PxVehicleDriveSimData4W& driveSimData); + +void computeDirection(PxU32& rightDirection, PxU32& upDirection); + +void PxVehicle4WEnable3WTadpoleMode(PxVehicleWheelsSimData& wheelsSimData, PxVehicleWheelsDynData& wheelsDynData, PxVehicleDriveSimData4W& driveSimData) +{ + PX_CHECK_AND_RETURN + (!wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eFRONT_LEFT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eREAR_LEFT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eREAR_RIGHT), "PxVehicle4WEnable3WTadpoleMode requires no wheels to be disabled"); + + PxU32 rightDirection=0xffffffff; + PxU32 upDirection=0xffffffff; + computeDirection(rightDirection, upDirection); + PX_CHECK_AND_RETURN(rightDirection<3 && upDirection<3, "PxVehicle4WEnable3WTadpoleMode requires the vectors set in PxVehicleSetBasisVectors to be axis-aligned"); + + enable3WMode(rightDirection, upDirection, false, wheelsSimData, wheelsDynData, driveSimData); +} + +void PxVehicle4WEnable3WDeltaMode(PxVehicleWheelsSimData& wheelsSimData, PxVehicleWheelsDynData& wheelsDynData, PxVehicleDriveSimData4W& driveSimData) +{ + PX_CHECK_AND_RETURN + (!wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eFRONT_LEFT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eREAR_LEFT) && + !wheelsSimData.getIsWheelDisabled(PxVehicleDrive4WWheelOrder::eREAR_RIGHT), "PxVehicle4WEnable3WDeltaMode requires no wheels to be disabled"); + + PxU32 rightDirection=0xffffffff; + PxU32 upDirection=0xffffffff; + computeDirection(rightDirection, upDirection); + PX_CHECK_AND_RETURN(rightDirection<3 && upDirection<3, "PxVehicle4WEnable3WTadpoleMode requires the vectors set in PxVehicleSetBasisVectors to be axis-aligned"); + + enable3WMode(rightDirection, upDirection, true, wheelsSimData, wheelsDynData, driveSimData); +} + +void computeSprungMasses(const PxU32 numSprungMasses, const PxVec3* sprungMassCoordinates, const PxVec3& centreOfMass, const PxReal totalMass, const PxU32 gravityDirection, PxReal* sprungMasses); + +void PxVehicleComputeSprungMasses(const PxU32 numSprungMasses, const PxVec3* sprungMassCoordinates, const PxVec3& centreOfMass, const PxReal totalMass, const PxU32 gravityDirection, PxReal* sprungMasses) +{ + computeSprungMasses(numSprungMasses, sprungMassCoordinates, centreOfMass, totalMass, gravityDirection, sprungMasses); +} + +void PxVehicleCopyDynamicsData(const PxVehicleCopyDynamicsMap& wheelMap, const PxVehicleWheels& src, PxVehicleWheels* trg) +{ + PX_CHECK_AND_RETURN(trg, "PxVehicleCopyDynamicsData requires that trg is a valid vehicle pointer"); + + PX_CHECK_AND_RETURN(src.getVehicleType() == trg->getVehicleType(), "PxVehicleCopyDynamicsData requires that both src and trg are the same type of vehicle"); + +#if PX_CHECKED + { + const PxU32 numWheelsSrc = src.mWheelsSimData.getNbWheels(); + const PxU32 numWheelsTrg = trg->mWheelsSimData.getNbWheels(); + PxU8 copiedWheelsSrc[PX_MAX_NB_WHEELS]; + PxMemZero(copiedWheelsSrc, sizeof(PxU8) * PX_MAX_NB_WHEELS); + PxU8 setWheelsTrg[PX_MAX_NB_WHEELS]; + PxMemZero(setWheelsTrg, sizeof(PxU8) * PX_MAX_NB_WHEELS); + for(PxU32 i = 0; i < PxMin(numWheelsSrc, numWheelsTrg); i++) + { + const PxU32 srcWheelId = wheelMap.sourceWheelIds[i]; + PX_CHECK_AND_RETURN(srcWheelId < numWheelsSrc, "PxVehicleCopyDynamicsData - wheelMap contains illegal source wheel id"); + PX_CHECK_AND_RETURN(0 == copiedWheelsSrc[srcWheelId], "PxVehicleCopyDynamicsData - wheelMap contains illegal source wheel id"); + copiedWheelsSrc[srcWheelId] = 1; + + const PxU32 trgWheelId = wheelMap.targetWheelIds[i]; + PX_CHECK_AND_RETURN(trgWheelId < numWheelsTrg, "PxVehicleCopyDynamicsData - wheelMap contains illegal target wheel id"); + PX_CHECK_AND_RETURN(0 == setWheelsTrg[trgWheelId], "PxVehicleCopyDynamicsData - wheelMap contains illegal target wheel id"); + setWheelsTrg[trgWheelId]=1; + } + } +#endif + + + const PxU32 numWheelsSrc = src.mWheelsSimData.getNbWheels(); + const PxU32 numWheelsTrg = trg->mWheelsSimData.getNbWheels(); + + //Set all dynamics data on the target to zero. + //Be aware that setToRestState sets the rigid body to + //rest so set the momentum back after calling setToRestState. + PxRigidDynamic* actorTrg = trg->getRigidDynamicActor(); + PxVec3 linVel = actorTrg->getLinearVelocity(); + PxVec3 angVel = actorTrg->getAngularVelocity(); + switch(src.getVehicleType()) + { + case PxVehicleTypes::eDRIVE4W: + static_cast(trg)->setToRestState(); + break; + case PxVehicleTypes::eDRIVENW: + static_cast(trg)->setToRestState(); + break; + case PxVehicleTypes::eDRIVETANK: + static_cast(trg)->setToRestState(); + break; + case PxVehicleTypes::eNODRIVE: + static_cast(trg)->setToRestState(); + break; + default: + break; + } + actorTrg->setLinearVelocity(linVel); + actorTrg->setAngularVelocity(angVel); + + + //Keep a track of the wheels on trg that have their dynamics data set as a copy from src. + PxU8 setWheelsTrg[PX_MAX_NB_WHEELS]; + PxMemZero(setWheelsTrg, sizeof(PxU8) * PX_MAX_NB_WHEELS); + + //Keep a track of the average wheel rotation speed of all enabled wheels on src. + PxU32 numEnabledWheelsSrc = 0; + PxF32 accumulatedWheelRotationSpeedSrc = 0.0f; + + //Copy wheel dynamics data from src wheels to trg wheels. + //Track the target wheels that have been given dynamics data from src wheels. + //Compute the accumulated wheel rotation speed of all enabled src wheels. + const PxU32 numMappedWheels = PxMin(numWheelsSrc, numWheelsTrg); + for(PxU32 i = 0; i < numMappedWheels; i++) + { + const PxU32 srcWheelId = wheelMap.sourceWheelIds[i]; + const PxU32 trgWheelId = wheelMap.targetWheelIds[i]; + + trg->mWheelsDynData.copy(src.mWheelsDynData, srcWheelId, trgWheelId); + + setWheelsTrg[trgWheelId] = 1; + + if(!src.mWheelsSimData.getIsWheelDisabled(srcWheelId)) + { + numEnabledWheelsSrc++; + accumulatedWheelRotationSpeedSrc += src.mWheelsDynData.getWheelRotationSpeed(srcWheelId); + } + } + + //Compute the average wheel rotation speed of src. + PxF32 averageWheelRotationSpeedSrc = 0; + if(numEnabledWheelsSrc > 0) + { + averageWheelRotationSpeedSrc = (accumulatedWheelRotationSpeedSrc/ (1.0f * numEnabledWheelsSrc)); + } + + //For wheels on trg that have not had their dynamics data copied from src just set + //their wheel rotation speed to the average wheel rotation speed. + for(PxU32 i = 0; i < numWheelsTrg; i++) + { + if(0 == setWheelsTrg[i] && !trg->mWheelsSimData.getIsWheelDisabled(i)) + { + trg->mWheelsDynData.setWheelRotationSpeed(i, averageWheelRotationSpeedSrc); + } + } + + //Copy the engine rotation speed/gear states/autobox states/etc. + switch(src.getVehicleType()) + { + case PxVehicleTypes::eDRIVE4W: + case PxVehicleTypes::eDRIVENW: + case PxVehicleTypes::eDRIVETANK: + { + const PxVehicleDriveDynData& driveDynDataSrc = static_cast(src).mDriveDynData; + PxVehicleDriveDynData* driveDynDataTrg = &static_cast(trg)->mDriveDynData; + *driveDynDataTrg = driveDynDataSrc; + } + break; + default: + break; + } +} + +bool areEqual(const PxQuat& q0, const PxQuat& q1) +{ + return ((q0.x == q1.x) && (q0.y == q1.y) && (q0.z == q1.z) && (q0.w == q1.w)); +} + +void PxVehicleUpdateCMassLocalPose(const PxTransform& oldCMassLocalPose, const PxTransform& newCMassLocalPose, const PxU32 gravityDirection, PxVehicleWheels* vehicle) +{ + PX_CHECK_AND_RETURN(areEqual(PxQuat(PxIdentity), oldCMassLocalPose.q), "Only center of mass poses with identity rotation are supported"); + PX_CHECK_AND_RETURN(areEqual(PxQuat(PxIdentity), newCMassLocalPose.q), "Only center of mass poses with identity rotation are supported"); + PX_CHECK_AND_RETURN(0==gravityDirection || 1==gravityDirection || 2==gravityDirection, "gravityDirection must be 0 or 1 or 2."); + + //Update the offsets from the rigid body center of mass. + PxVec3 wheelCenterCMOffsets[PX_MAX_NB_WHEELS]; + const PxU32 nbWheels = vehicle->mWheelsSimData.getNbWheels(); + for(PxU32 i = 0; i < nbWheels; i++) + { + wheelCenterCMOffsets[i] = vehicle->mWheelsSimData.getWheelCentreOffset(i) + oldCMassLocalPose.p - newCMassLocalPose.p; + vehicle->mWheelsSimData.setWheelCentreOffset(i, vehicle->mWheelsSimData.getWheelCentreOffset(i) + oldCMassLocalPose.p - newCMassLocalPose.p); + vehicle->mWheelsSimData.setSuspForceAppPointOffset(i, vehicle->mWheelsSimData.getSuspForceAppPointOffset(i) + oldCMassLocalPose.p - newCMassLocalPose.p); + vehicle->mWheelsSimData.setTireForceAppPointOffset(i, vehicle->mWheelsSimData.getTireForceAppPointOffset(i) + oldCMassLocalPose.p - newCMassLocalPose.p); + } + + //Now update the sprung masses. + PxF32 sprungMasses[PX_MAX_NB_WHEELS]; + PxVehicleComputeSprungMasses(nbWheels, wheelCenterCMOffsets, PxVec3(0,0,0), vehicle->getRigidDynamicActor()->getMass(), gravityDirection, sprungMasses); + for(PxU32 i = 0; i < nbWheels; i++) + { + PxVehicleSuspensionData suspData = vehicle->mWheelsSimData.getSuspensionData(i); + const PxF32 massRatio = sprungMasses[i]/suspData.mSprungMass; + suspData.mSprungMass = sprungMasses[i]; + suspData.mSpringStrength *= massRatio; + suspData.mSpringDamperRate *= massRatio; + vehicle->mWheelsSimData.setSuspensionData(i, suspData); + } +} + +}//physx diff --git a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp new file mode 100644 index 000000000..459d26a2f --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp @@ -0,0 +1,577 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxVehicleUtilTelemetry.h" +#include "PsFoundation.h" +#include "PsUtilities.h" +#include "stdio.h" +#include "CmPhysXCommon.h" + +namespace physx +{ + +#if PX_DEBUG_VEHICLE_ON + +PxVehicleGraphDesc::PxVehicleGraphDesc() +: mPosX(PX_MAX_F32), + mPosY(PX_MAX_F32), + mSizeX(PX_MAX_F32), + mSizeY(PX_MAX_F32), + mBackgroundColor(PxVec3(PX_MAX_F32,PX_MAX_F32,PX_MAX_F32)), + mAlpha(PX_MAX_F32) +{ +} + +bool PxVehicleGraphDesc::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mPosX != PX_MAX_F32, "PxVehicleGraphDesc.mPosX must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mPosY != PX_MAX_F32, "PxVehicleGraphDesc.mPosY must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mSizeX != PX_MAX_F32, "PxVehicleGraphDesc.mSizeX must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mSizeY != PX_MAX_F32, "PxVehicleGraphDesc.mSizeY must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mBackgroundColor.x != PX_MAX_F32 && mBackgroundColor.y != PX_MAX_F32 && mBackgroundColor.z != PX_MAX_F32, "PxVehicleGraphDesc.mBackgroundColor must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mAlpha != PX_MAX_F32, "PxVehicleGraphDesc.mAlpha must be initialised", false); + return true; +} + +PxVehicleGraphChannelDesc::PxVehicleGraphChannelDesc() +: mMinY(PX_MAX_F32), + mMaxY(PX_MAX_F32), + mMidY(PX_MAX_F32), + mColorLow(PxVec3(PX_MAX_F32,PX_MAX_F32,PX_MAX_F32)), + mColorHigh(PxVec3(PX_MAX_F32,PX_MAX_F32,PX_MAX_F32)), + mTitle(NULL) +{ +} + +bool PxVehicleGraphChannelDesc::isValid() const +{ + PX_CHECK_AND_RETURN_VAL(mMinY != PX_MAX_F32, "PxVehicleGraphChannelDesc.mMinY must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mMaxY != PX_MAX_F32, "PxVehicleGraphChannelDesc.mMaxY must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mMidY != PX_MAX_F32, "PxVehicleGraphChannelDesc.mMidY must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mColorLow.x != PX_MAX_F32 && mColorLow.y != PX_MAX_F32 && mColorLow.z != PX_MAX_F32, "PxVehicleGraphChannelDesc.mColorLow must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mColorHigh.x != PX_MAX_F32 && mColorHigh.y != PX_MAX_F32 && mColorHigh.z != PX_MAX_F32, "PxVehicleGraphChannelDesc.mColorHigh must be initialised", false); + PX_CHECK_AND_RETURN_VAL(mTitle, "PxVehicleGraphChannelDesc.mTitle must be initialised", false); + return true; +} + +PxVehicleGraph::PxVehicleGraph() +{ + mBackgroundMinX=0; + mBackgroundMaxX=0; + mBackgroundMinY=0; + mBackgroundMaxY=0; + mSampleTide=0; + mBackgroundColor=PxVec3(255.f,255.f,255.f); + mBackgroundAlpha=1.0f; + for(PxU32 i=0;i= size_t(PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS) && size_t(PxVehicleGraph::eMAX_NB_CHANNELS) >= size_t(PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS)); +} + +PxVehicleGraph::~PxVehicleGraph() +{ +} + +void PxVehicleGraph::setup(const PxVehicleGraphDesc& desc, const PxVehicleGraphType::Enum graphType) +{ + mBackgroundMinX = (desc.mPosX - 0.5f*desc.mSizeX); + mBackgroundMaxX = (desc.mPosX + 0.5f*desc.mSizeX); + mBackgroundMinY = (desc.mPosY - 0.5f*desc.mSizeY); + mBackgroundMaxY = (desc.mPosY + 0.5f*desc.mSizeY); + + mBackgroundColor=desc.mBackgroundColor; + mBackgroundAlpha=desc.mAlpha; + + mNbChannels = (PxVehicleGraphType::eWHEEL==graphType) ? PxU32(PxVehicleWheelGraphChannel::eMAX_NB_WHEEL_CHANNELS) : PxU32(PxVehicleDriveGraphChannel::eMAX_NB_DRIVE_CHANNELS); +} + +void PxVehicleGraph::setChannel(PxVehicleGraphChannelDesc& desc, const PxU32 channel) +{ + PX_ASSERT(channel(PX_ALLOC(size, "PxVehicleNWTelemetryData")); + + //Patch up the pointers. + PxU8* ptr = reinterpret_cast(vehTelData) + sizeof(PxVehicleTelemetryData); + vehTelData->mEngineGraph = reinterpret_cast(ptr); + new(vehTelData->mEngineGraph) PxVehicleGraph(); + ptr += sizeof(PxVehicleGraph); + vehTelData->mWheelGraphs = reinterpret_cast(ptr); + for(PxU32 i=0;imWheelGraphs[i]) PxVehicleGraph(); + } + ptr += sizeof(PxVehicleGraph)*numWheels; + vehTelData->mSuspforceAppPoints = reinterpret_cast(ptr); + ptr += sizeof(PxVec3)*numWheels; + vehTelData->mTireforceAppPoints = reinterpret_cast(ptr); + ptr += sizeof(PxVec3)*numWheels; + + //Set the number of wheels in each structure that needs it. + vehTelData->mNbActiveWheels=numWheels; + + //Finished. + return vehTelData; +} + +void PxVehicleTelemetryData::free() +{ + PX_FREE(this); +} + +void physx::PxVehicleTelemetryData::setup +(const PxF32 graphSizeX, const PxF32 graphSizeY, +const PxF32 engineGraphPosX, const PxF32 engineGraphPosY, +const PxF32* const wheelGraphPosX, const PxF32* const wheelGraphPosY, +const PxVec3& backgroundColor, const PxVec3& lineColorHigh, const PxVec3& lineColorLow) +{ + mEngineGraph->setupEngineGraph + (graphSizeX, graphSizeY, engineGraphPosX, engineGraphPosY, + backgroundColor, lineColorHigh, lineColorLow); + + const PxU32 numActiveWheels=mNbActiveWheels; + for(PxU32 k=0;kclearRecordedChannelData(); + + const PxU32 numActiveWheels=mNbActiveWheels; + for(PxU32 k=0;k or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +PxVehicleChassisData_PropertiesStart, +PxVehicleChassisData_MMOI, +PxVehicleChassisData_MMass, +PxVehicleChassisData_MCMOffset, +PxVehicleChassisData_PropertiesStop, +PxVehicleEngineData_PropertiesStart, +PxVehicleEngineData_RecipMOI, +PxVehicleEngineData_RecipMaxOmega, +PxVehicleEngineData_MTorqueCurve, +PxVehicleEngineData_MMOI, +PxVehicleEngineData_MPeakTorque, +PxVehicleEngineData_MMaxOmega, +PxVehicleEngineData_MDampingRateFullThrottle, +PxVehicleEngineData_MDampingRateZeroThrottleClutchEngaged, +PxVehicleEngineData_MDampingRateZeroThrottleClutchDisengaged, +PxVehicleEngineData_PropertiesStop, +PxVehicleGearsData_PropertiesStart, +PxVehicleGearsData_GearRatio, +PxVehicleGearsData_MFinalRatio, +PxVehicleGearsData_MNbRatios, +PxVehicleGearsData_MSwitchTime, +PxVehicleGearsData_PropertiesStop, +PxVehicleAutoBoxData_PropertiesStart, +PxVehicleAutoBoxData_Latency, +PxVehicleAutoBoxData_UpRatios, +PxVehicleAutoBoxData_DownRatios, +PxVehicleAutoBoxData_PropertiesStop, +PxVehicleDifferential4WData_PropertiesStart, +PxVehicleDifferential4WData_MFrontRearSplit, +PxVehicleDifferential4WData_MFrontLeftRightSplit, +PxVehicleDifferential4WData_MRearLeftRightSplit, +PxVehicleDifferential4WData_MCentreBias, +PxVehicleDifferential4WData_MFrontBias, +PxVehicleDifferential4WData_MRearBias, +PxVehicleDifferential4WData_MType, +PxVehicleDifferential4WData_PropertiesStop, +PxVehicleDifferentialNWData_PropertiesStart, +PxVehicleDifferentialNWData_DrivenWheelStatus, +PxVehicleDifferentialNWData_PropertiesStop, +PxVehicleAckermannGeometryData_PropertiesStart, +PxVehicleAckermannGeometryData_MAccuracy, +PxVehicleAckermannGeometryData_MFrontWidth, +PxVehicleAckermannGeometryData_MRearWidth, +PxVehicleAckermannGeometryData_MAxleSeparation, +PxVehicleAckermannGeometryData_PropertiesStop, +PxVehicleClutchData_PropertiesStart, +PxVehicleClutchData_MStrength, +PxVehicleClutchData_MAccuracyMode, +PxVehicleClutchData_MEstimateIterations, +PxVehicleClutchData_PropertiesStop, +PxVehicleTireLoadFilterData_PropertiesStart, +PxVehicleTireLoadFilterData_Denominator, +PxVehicleTireLoadFilterData_MMinNormalisedLoad, +PxVehicleTireLoadFilterData_MMinFilteredNormalisedLoad, +PxVehicleTireLoadFilterData_MMaxNormalisedLoad, +PxVehicleTireLoadFilterData_MMaxFilteredNormalisedLoad, +PxVehicleTireLoadFilterData_PropertiesStop, +PxVehicleWheelData_PropertiesStart, +PxVehicleWheelData_RecipRadius, +PxVehicleWheelData_RecipMOI, +PxVehicleWheelData_MRadius, +PxVehicleWheelData_MWidth, +PxVehicleWheelData_MMass, +PxVehicleWheelData_MMOI, +PxVehicleWheelData_MDampingRate, +PxVehicleWheelData_MMaxBrakeTorque, +PxVehicleWheelData_MMaxHandBrakeTorque, +PxVehicleWheelData_MMaxSteer, +PxVehicleWheelData_MToeAngle, +PxVehicleWheelData_PropertiesStop, +PxVehicleSuspensionData_PropertiesStart, +PxVehicleSuspensionData_RecipMaxCompression, +PxVehicleSuspensionData_RecipMaxDroop, +PxVehicleSuspensionData_MassAndPreserveNaturalFrequency, +PxVehicleSuspensionData_MSpringStrength, +PxVehicleSuspensionData_MSpringDamperRate, +PxVehicleSuspensionData_MMaxCompression, +PxVehicleSuspensionData_MMaxDroop, +PxVehicleSuspensionData_MSprungMass, +PxVehicleSuspensionData_MCamberAtRest, +PxVehicleSuspensionData_MCamberAtMaxCompression, +PxVehicleSuspensionData_MCamberAtMaxDroop, +PxVehicleSuspensionData_PropertiesStop, +PxVehicleAntiRollBarData_PropertiesStart, +PxVehicleAntiRollBarData_MWheel0, +PxVehicleAntiRollBarData_MWheel1, +PxVehicleAntiRollBarData_MStiffness, +PxVehicleAntiRollBarData_PropertiesStop, +PxVehicleTireData_PropertiesStart, +PxVehicleTireData_RecipLongitudinalStiffnessPerUnitGravity, +PxVehicleTireData_FrictionVsSlipGraphRecipx1Minusx0, +PxVehicleTireData_FrictionVsSlipGraphRecipx2Minusx1, +PxVehicleTireData_MLatStiffX, +PxVehicleTireData_MLatStiffY, +PxVehicleTireData_MLongitudinalStiffnessPerUnitGravity, +PxVehicleTireData_MCamberStiffnessPerUnitGravity, +PxVehicleTireData_MType, +PxVehicleTireData_MFrictionVsSlipGraph, +PxVehicleTireData_PropertiesStop, +PxVehicleWheels4SimData_PropertiesStart, +PxVehicleWheels4SimData_TireRestLoadsArray, +PxVehicleWheels4SimData_RecipTireRestLoadsArray, +PxVehicleWheels4SimData_PropertiesStop, +PxVehicleWheelsSimData_PropertiesStart, +PxVehicleWheelsSimData_ChassisMass, +PxVehicleWheelsSimData_SuspensionData, +PxVehicleWheelsSimData_WheelData, +PxVehicleWheelsSimData_TireData, +PxVehicleWheelsSimData_SuspTravelDirection, +PxVehicleWheelsSimData_SuspForceAppPointOffset, +PxVehicleWheelsSimData_TireForceAppPointOffset, +PxVehicleWheelsSimData_WheelCentreOffset, +PxVehicleWheelsSimData_WheelShapeMapping, +PxVehicleWheelsSimData_SceneQueryFilterData, +PxVehicleWheelsSimData_AntiRollBarData, +PxVehicleWheelsSimData_TireLoadFilterData, +PxVehicleWheelsSimData_MinLongSlipDenominator, +PxVehicleWheelsSimData_ThresholdLongSpeed, +PxVehicleWheelsSimData_LowForwardSpeedSubStepCount, +PxVehicleWheelsSimData_HighForwardSpeedSubStepCount, +PxVehicleWheelsSimData_WheelEnabledState, +PxVehicleWheelsSimData_PropertiesStop, +PxVehicleWheelsDynData_PropertiesStart, +PxVehicleWheelsDynData_TireForceShaderFunction, +PxVehicleWheelsDynData_WheelRotationSpeed, +PxVehicleWheelsDynData_WheelRotationAngle, +PxVehicleWheelsDynData_Wheel4DynData, +PxVehicleWheelsDynData_PropertiesStop, +PxVehicleWheels_PropertiesStart, +PxVehicleWheels_VehicleType, +PxVehicleWheels_RigidDynamicActor, +PxVehicleWheels_ConcreteTypeName, +PxVehicleWheels_MWheelsSimData, +PxVehicleWheels_MWheelsDynData, +PxVehicleWheels_PropertiesStop, +PxVehicleDriveDynData_PropertiesStart, +PxVehicleDriveDynData_AnalogInput, +PxVehicleDriveDynData_GearUp, +PxVehicleDriveDynData_GearDown, +PxVehicleDriveDynData_UseAutoGears, +PxVehicleDriveDynData_CurrentGear, +PxVehicleDriveDynData_TargetGear, +PxVehicleDriveDynData_EngineRotationSpeed, +PxVehicleDriveDynData_GearChange, +PxVehicleDriveDynData_GearSwitchTime, +PxVehicleDriveDynData_AutoBoxSwitchTime, +PxVehicleDriveDynData_MUseAutoGears, +PxVehicleDriveDynData_MGearUpPressed, +PxVehicleDriveDynData_MGearDownPressed, +PxVehicleDriveDynData_MCurrentGear, +PxVehicleDriveDynData_MTargetGear, +PxVehicleDriveDynData_MEnginespeed, +PxVehicleDriveDynData_MGearSwitchTime, +PxVehicleDriveDynData_MAutoBoxSwitchTime, +PxVehicleDriveDynData_PropertiesStop, +PxVehicleDriveSimData_PropertiesStart, +PxVehicleDriveSimData_EngineData, +PxVehicleDriveSimData_GearsData, +PxVehicleDriveSimData_ClutchData, +PxVehicleDriveSimData_AutoBoxData, +PxVehicleDriveSimData_PropertiesStop, +PxVehicleDriveSimData4W_PropertiesStart, +PxVehicleDriveSimData4W_DiffData, +PxVehicleDriveSimData4W_AckermannGeometryData, +PxVehicleDriveSimData4W_PropertiesStop, +PxVehicleDrive_PropertiesStart, +PxVehicleDrive_ConcreteTypeName, +PxVehicleDrive_MDriveDynData, +PxVehicleDrive_PropertiesStop, +PxVehicleDrive4W_PropertiesStart, +PxVehicleDrive4W_ConcreteTypeName, +PxVehicleDrive4W_MDriveSimData, +PxVehicleDrive4W_PropertiesStop, +PxVehicleDriveTank_PropertiesStart, +PxVehicleDriveTank_DriveModel, +PxVehicleDriveTank_ConcreteTypeName, +PxVehicleDriveTank_MDriveSimData, +PxVehicleDriveTank_PropertiesStop, +PxVehicleDriveSimDataNW_PropertiesStart, +PxVehicleDriveSimDataNW_DiffData, +PxVehicleDriveSimDataNW_PropertiesStop, +PxVehicleDriveNW_PropertiesStart, +PxVehicleDriveNW_ConcreteTypeName, +PxVehicleDriveNW_MDriveSimData, +PxVehicleDriveNW_PropertiesStop, +PxVehicleNoDrive_PropertiesStart, +PxVehicleNoDrive_BrakeTorque, +PxVehicleNoDrive_DriveTorque, +PxVehicleNoDrive_SteerAngle, +PxVehicleNoDrive_ConcreteTypeName, +PxVehicleNoDrive_PropertiesStop, + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h new file mode 100644 index 000000000..22275e592 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h @@ -0,0 +1,1797 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +#define PX_PROPERTY_INFO_NAME PxVehiclePropertyInfoName + class PxVehicleChassisData; + struct PxVehicleChassisDataGeneratedValues + { + PxVec3 MMOI; + PxReal MMass; + PxVec3 MCMOffset; + PxVehicleChassisDataGeneratedValues( const PxVehicleChassisData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleChassisData, MMOI, PxVehicleChassisDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleChassisData, MMass, PxVehicleChassisDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleChassisData, MCMOffset, PxVehicleChassisDataGeneratedValues) + struct PxVehicleChassisDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleChassisData"; } + PxPropertyInfo MMOI; + PxPropertyInfo MMass; + PxPropertyInfo MCMOffset; + + PxVehicleChassisDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MMOI, inStartIndex + 0 );; + inOperator( MMass, inStartIndex + 1 );; + inOperator( MCMOffset, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleChassisDataGeneratedInfo Info; + const PxVehicleChassisDataGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxEMPTYConversion[] = { + { "PxEmpty", static_cast( physx::PxEmpty ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< const physx::PxEMPTY > { PxEnumTraits() : NameConversion( g_physx__PxEMPTYConversion ) {} const PxU32ToName* NameConversion; }; + class PxVehicleEngineData; + struct PxVehicleEngineDataGeneratedValues + { + PxReal RecipMOI; + PxReal RecipMaxOmega; + PxReal MMOI; + PxReal MPeakTorque; + PxReal MMaxOmega; + PxReal MDampingRateFullThrottle; + PxReal MDampingRateZeroThrottleClutchEngaged; + PxReal MDampingRateZeroThrottleClutchDisengaged; + PxVehicleEngineDataGeneratedValues( const PxVehicleEngineData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, RecipMOI, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, RecipMaxOmega, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MMOI, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MPeakTorque, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MMaxOmega, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MDampingRateFullThrottle, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MDampingRateZeroThrottleClutchEngaged, PxVehicleEngineDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleEngineData, MDampingRateZeroThrottleClutchDisengaged, PxVehicleEngineDataGeneratedValues) + struct PxVehicleEngineDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleEngineData"; } + PxReadOnlyPropertyInfo RecipMOI; + PxReadOnlyPropertyInfo RecipMaxOmega; + MTorqueCurveProperty MTorqueCurve; + PxPropertyInfo MMOI; + PxPropertyInfo MPeakTorque; + PxPropertyInfo MMaxOmega; + PxPropertyInfo MDampingRateFullThrottle; + PxPropertyInfo MDampingRateZeroThrottleClutchEngaged; + PxPropertyInfo MDampingRateZeroThrottleClutchDisengaged; + + PxVehicleEngineDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 9; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( RecipMOI, inStartIndex + 0 );; + inOperator( RecipMaxOmega, inStartIndex + 1 );; + inOperator( MTorqueCurve, inStartIndex + 2 );; + inOperator( MMOI, inStartIndex + 3 );; + inOperator( MPeakTorque, inStartIndex + 4 );; + inOperator( MMaxOmega, inStartIndex + 5 );; + inOperator( MDampingRateFullThrottle, inStartIndex + 6 );; + inOperator( MDampingRateZeroThrottleClutchEngaged, inStartIndex + 7 );; + inOperator( MDampingRateZeroThrottleClutchDisengaged, inStartIndex + 8 );; + return 9 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleEngineDataGeneratedInfo Info; + const PxVehicleEngineDataGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxVehicleGearsData__EnumConversion[] = { + { "eREVERSE", static_cast( physx::PxVehicleGearsData::eREVERSE ) }, + { "eNEUTRAL", static_cast( physx::PxVehicleGearsData::eNEUTRAL ) }, + { "eFIRST", static_cast( physx::PxVehicleGearsData::eFIRST ) }, + { "eSECOND", static_cast( physx::PxVehicleGearsData::eSECOND ) }, + { "eTHIRD", static_cast( physx::PxVehicleGearsData::eTHIRD ) }, + { "eFOURTH", static_cast( physx::PxVehicleGearsData::eFOURTH ) }, + { "eFIFTH", static_cast( physx::PxVehicleGearsData::eFIFTH ) }, + { "eSIXTH", static_cast( physx::PxVehicleGearsData::eSIXTH ) }, + { "eSEVENTH", static_cast( physx::PxVehicleGearsData::eSEVENTH ) }, + { "eEIGHTH", static_cast( physx::PxVehicleGearsData::eEIGHTH ) }, + { "eNINTH", static_cast( physx::PxVehicleGearsData::eNINTH ) }, + { "eTENTH", static_cast( physx::PxVehicleGearsData::eTENTH ) }, + { "eELEVENTH", static_cast( physx::PxVehicleGearsData::eELEVENTH ) }, + { "eTWELFTH", static_cast( physx::PxVehicleGearsData::eTWELFTH ) }, + { "eTHIRTEENTH", static_cast( physx::PxVehicleGearsData::eTHIRTEENTH ) }, + { "eFOURTEENTH", static_cast( physx::PxVehicleGearsData::eFOURTEENTH ) }, + { "eFIFTEENTH", static_cast( physx::PxVehicleGearsData::eFIFTEENTH ) }, + { "eSIXTEENTH", static_cast( physx::PxVehicleGearsData::eSIXTEENTH ) }, + { "eSEVENTEENTH", static_cast( physx::PxVehicleGearsData::eSEVENTEENTH ) }, + { "eEIGHTEENTH", static_cast( physx::PxVehicleGearsData::eEIGHTEENTH ) }, + { "eNINETEENTH", static_cast( physx::PxVehicleGearsData::eNINETEENTH ) }, + { "eTWENTIETH", static_cast( physx::PxVehicleGearsData::eTWENTIETH ) }, + { "eTWENTYFIRST", static_cast( physx::PxVehicleGearsData::eTWENTYFIRST ) }, + { "eTWENTYSECOND", static_cast( physx::PxVehicleGearsData::eTWENTYSECOND ) }, + { "eTWENTYTHIRD", static_cast( physx::PxVehicleGearsData::eTWENTYTHIRD ) }, + { "eTWENTYFOURTH", static_cast( physx::PxVehicleGearsData::eTWENTYFOURTH ) }, + { "eTWENTYFIFTH", static_cast( physx::PxVehicleGearsData::eTWENTYFIFTH ) }, + { "eTWENTYSIXTH", static_cast( physx::PxVehicleGearsData::eTWENTYSIXTH ) }, + { "eTWENTYSEVENTH", static_cast( physx::PxVehicleGearsData::eTWENTYSEVENTH ) }, + { "eTWENTYEIGHTH", static_cast( physx::PxVehicleGearsData::eTWENTYEIGHTH ) }, + { "eTWENTYNINTH", static_cast( physx::PxVehicleGearsData::eTWENTYNINTH ) }, + { "eTHIRTIETH", static_cast( physx::PxVehicleGearsData::eTHIRTIETH ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxVehicleGearsData::Enum > { PxEnumTraits() : NameConversion( g_physx__PxVehicleGearsData__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxVehicleGearsData; + struct PxVehicleGearsDataGeneratedValues + { + PxReal GearRatio[physx::PxVehicleGearsData::eGEARSRATIO_COUNT]; + PxReal MFinalRatio; + PxU32 MNbRatios; + PxReal MSwitchTime; + PxVehicleGearsDataGeneratedValues( const PxVehicleGearsData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleGearsData, GearRatio, PxVehicleGearsDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleGearsData, MFinalRatio, PxVehicleGearsDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleGearsData, MNbRatios, PxVehicleGearsDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleGearsData, MSwitchTime, PxVehicleGearsDataGeneratedValues) + struct PxVehicleGearsDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleGearsData"; } + PxIndexedPropertyInfo GearRatio; + PxPropertyInfo MFinalRatio; + PxPropertyInfo MNbRatios; + PxPropertyInfo MSwitchTime; + + PxVehicleGearsDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( GearRatio, inStartIndex + 0 );; + inOperator( MFinalRatio, inStartIndex + 1 );; + inOperator( MNbRatios, inStartIndex + 2 );; + inOperator( MSwitchTime, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleGearsDataGeneratedInfo Info; + const PxVehicleGearsDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleAutoBoxData; + struct PxVehicleAutoBoxDataGeneratedValues + { + PxReal Latency; + PxReal UpRatios[physx::PxVehicleGearsData::eGEARSRATIO_COUNT]; + PxReal DownRatios[physx::PxVehicleGearsData::eGEARSRATIO_COUNT]; + PxVehicleAutoBoxDataGeneratedValues( const PxVehicleAutoBoxData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAutoBoxData, Latency, PxVehicleAutoBoxDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAutoBoxData, UpRatios, PxVehicleAutoBoxDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAutoBoxData, DownRatios, PxVehicleAutoBoxDataGeneratedValues) + struct PxVehicleAutoBoxDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleAutoBoxData"; } + PxPropertyInfo Latency; + PxIndexedPropertyInfo UpRatios; + PxIndexedPropertyInfo DownRatios; + + PxVehicleAutoBoxDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Latency, inStartIndex + 0 );; + inOperator( UpRatios, inStartIndex + 1 );; + inOperator( DownRatios, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleAutoBoxDataGeneratedInfo Info; + const PxVehicleAutoBoxDataGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxVehicleDifferential4WData__EnumConversion[] = { + { "eDIFF_TYPE_LS_4WD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD ) }, + { "eDIFF_TYPE_LS_FRONTWD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_LS_FRONTWD ) }, + { "eDIFF_TYPE_LS_REARWD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_LS_REARWD ) }, + { "eDIFF_TYPE_OPEN_4WD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_4WD ) }, + { "eDIFF_TYPE_OPEN_FRONTWD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_FRONTWD ) }, + { "eDIFF_TYPE_OPEN_REARWD", static_cast( physx::PxVehicleDifferential4WData::eDIFF_TYPE_OPEN_REARWD ) }, + { "eMAX_NB_DIFF_TYPES", static_cast( physx::PxVehicleDifferential4WData::eMAX_NB_DIFF_TYPES ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxVehicleDifferential4WData::Enum > { PxEnumTraits() : NameConversion( g_physx__PxVehicleDifferential4WData__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxVehicleDifferential4WData; + struct PxVehicleDifferential4WDataGeneratedValues + { + PxReal MFrontRearSplit; + PxReal MFrontLeftRightSplit; + PxReal MRearLeftRightSplit; + PxReal MCentreBias; + PxReal MFrontBias; + PxReal MRearBias; + PxVehicleDifferential4WData::Enum MType; + PxVehicleDifferential4WDataGeneratedValues( const PxVehicleDifferential4WData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MFrontRearSplit, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MFrontLeftRightSplit, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MRearLeftRightSplit, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MCentreBias, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MFrontBias, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MRearBias, PxVehicleDifferential4WDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferential4WData, MType, PxVehicleDifferential4WDataGeneratedValues) + struct PxVehicleDifferential4WDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleDifferential4WData"; } + PxPropertyInfo MFrontRearSplit; + PxPropertyInfo MFrontLeftRightSplit; + PxPropertyInfo MRearLeftRightSplit; + PxPropertyInfo MCentreBias; + PxPropertyInfo MFrontBias; + PxPropertyInfo MRearBias; + PxPropertyInfo MType; + + PxVehicleDifferential4WDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 7; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MFrontRearSplit, inStartIndex + 0 );; + inOperator( MFrontLeftRightSplit, inStartIndex + 1 );; + inOperator( MRearLeftRightSplit, inStartIndex + 2 );; + inOperator( MCentreBias, inStartIndex + 3 );; + inOperator( MFrontBias, inStartIndex + 4 );; + inOperator( MRearBias, inStartIndex + 5 );; + inOperator( MType, inStartIndex + 6 );; + return 7 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDifferential4WDataGeneratedInfo Info; + const PxVehicleDifferential4WDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDifferentialNWData; + struct PxVehicleDifferentialNWDataGeneratedValues + { + PxU32 DrivenWheelStatus; + PxVehicleDifferentialNWDataGeneratedValues( const PxVehicleDifferentialNWData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDifferentialNWData, DrivenWheelStatus, PxVehicleDifferentialNWDataGeneratedValues) + struct PxVehicleDifferentialNWDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleDifferentialNWData"; } + PxPropertyInfo DrivenWheelStatus; + + PxVehicleDifferentialNWDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( DrivenWheelStatus, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDifferentialNWDataGeneratedInfo Info; + const PxVehicleDifferentialNWDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleAckermannGeometryData; + struct PxVehicleAckermannGeometryDataGeneratedValues + { + PxReal MAccuracy; + PxReal MFrontWidth; + PxReal MRearWidth; + PxReal MAxleSeparation; + PxVehicleAckermannGeometryDataGeneratedValues( const PxVehicleAckermannGeometryData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAckermannGeometryData, MAccuracy, PxVehicleAckermannGeometryDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAckermannGeometryData, MFrontWidth, PxVehicleAckermannGeometryDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAckermannGeometryData, MRearWidth, PxVehicleAckermannGeometryDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAckermannGeometryData, MAxleSeparation, PxVehicleAckermannGeometryDataGeneratedValues) + struct PxVehicleAckermannGeometryDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleAckermannGeometryData"; } + PxPropertyInfo MAccuracy; + PxPropertyInfo MFrontWidth; + PxPropertyInfo MRearWidth; + PxPropertyInfo MAxleSeparation; + + PxVehicleAckermannGeometryDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MAccuracy, inStartIndex + 0 );; + inOperator( MFrontWidth, inStartIndex + 1 );; + inOperator( MRearWidth, inStartIndex + 2 );; + inOperator( MAxleSeparation, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleAckermannGeometryDataGeneratedInfo Info; + const PxVehicleAckermannGeometryDataGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxVehicleClutchAccuracyMode__EnumConversion[] = { + { "eESTIMATE", static_cast( physx::PxVehicleClutchAccuracyMode::eESTIMATE ) }, + { "eBEST_POSSIBLE", static_cast( physx::PxVehicleClutchAccuracyMode::eBEST_POSSIBLE ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxVehicleClutchAccuracyMode::Enum > { PxEnumTraits() : NameConversion( g_physx__PxVehicleClutchAccuracyMode__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxVehicleClutchData; + struct PxVehicleClutchDataGeneratedValues + { + PxReal MStrength; + PxVehicleClutchAccuracyMode::Enum MAccuracyMode; + PxU32 MEstimateIterations; + PxVehicleClutchDataGeneratedValues( const PxVehicleClutchData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleClutchData, MStrength, PxVehicleClutchDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleClutchData, MAccuracyMode, PxVehicleClutchDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleClutchData, MEstimateIterations, PxVehicleClutchDataGeneratedValues) + struct PxVehicleClutchDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleClutchData"; } + PxPropertyInfo MStrength; + PxPropertyInfo MAccuracyMode; + PxPropertyInfo MEstimateIterations; + + PxVehicleClutchDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MStrength, inStartIndex + 0 );; + inOperator( MAccuracyMode, inStartIndex + 1 );; + inOperator( MEstimateIterations, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleClutchDataGeneratedInfo Info; + const PxVehicleClutchDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleTireLoadFilterData; + struct PxVehicleTireLoadFilterDataGeneratedValues + { + PxReal Denominator; + PxReal MMinNormalisedLoad; + PxReal MMinFilteredNormalisedLoad; + PxReal MMaxNormalisedLoad; + PxReal MMaxFilteredNormalisedLoad; + PxVehicleTireLoadFilterDataGeneratedValues( const PxVehicleTireLoadFilterData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireLoadFilterData, Denominator, PxVehicleTireLoadFilterDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireLoadFilterData, MMinNormalisedLoad, PxVehicleTireLoadFilterDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireLoadFilterData, MMinFilteredNormalisedLoad, PxVehicleTireLoadFilterDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireLoadFilterData, MMaxNormalisedLoad, PxVehicleTireLoadFilterDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireLoadFilterData, MMaxFilteredNormalisedLoad, PxVehicleTireLoadFilterDataGeneratedValues) + struct PxVehicleTireLoadFilterDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleTireLoadFilterData"; } + PxReadOnlyPropertyInfo Denominator; + PxPropertyInfo MMinNormalisedLoad; + PxPropertyInfo MMinFilteredNormalisedLoad; + PxPropertyInfo MMaxNormalisedLoad; + PxPropertyInfo MMaxFilteredNormalisedLoad; + + PxVehicleTireLoadFilterDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( Denominator, inStartIndex + 0 );; + inOperator( MMinNormalisedLoad, inStartIndex + 1 );; + inOperator( MMinFilteredNormalisedLoad, inStartIndex + 2 );; + inOperator( MMaxNormalisedLoad, inStartIndex + 3 );; + inOperator( MMaxFilteredNormalisedLoad, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleTireLoadFilterDataGeneratedInfo Info; + const PxVehicleTireLoadFilterDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleWheelData; + struct PxVehicleWheelDataGeneratedValues + { + PxReal RecipRadius; + PxReal RecipMOI; + PxReal MRadius; + PxReal MWidth; + PxReal MMass; + PxReal MMOI; + PxReal MDampingRate; + PxReal MMaxBrakeTorque; + PxReal MMaxHandBrakeTorque; + PxReal MMaxSteer; + PxReal MToeAngle; + PxVehicleWheelDataGeneratedValues( const PxVehicleWheelData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, RecipRadius, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, RecipMOI, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MRadius, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MWidth, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MMass, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MMOI, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MDampingRate, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MMaxBrakeTorque, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MMaxHandBrakeTorque, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MMaxSteer, PxVehicleWheelDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelData, MToeAngle, PxVehicleWheelDataGeneratedValues) + struct PxVehicleWheelDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleWheelData"; } + PxReadOnlyPropertyInfo RecipRadius; + PxReadOnlyPropertyInfo RecipMOI; + PxPropertyInfo MRadius; + PxPropertyInfo MWidth; + PxPropertyInfo MMass; + PxPropertyInfo MMOI; + PxPropertyInfo MDampingRate; + PxPropertyInfo MMaxBrakeTorque; + PxPropertyInfo MMaxHandBrakeTorque; + PxPropertyInfo MMaxSteer; + PxPropertyInfo MToeAngle; + + PxVehicleWheelDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 11; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( RecipRadius, inStartIndex + 0 );; + inOperator( RecipMOI, inStartIndex + 1 );; + inOperator( MRadius, inStartIndex + 2 );; + inOperator( MWidth, inStartIndex + 3 );; + inOperator( MMass, inStartIndex + 4 );; + inOperator( MMOI, inStartIndex + 5 );; + inOperator( MDampingRate, inStartIndex + 6 );; + inOperator( MMaxBrakeTorque, inStartIndex + 7 );; + inOperator( MMaxHandBrakeTorque, inStartIndex + 8 );; + inOperator( MMaxSteer, inStartIndex + 9 );; + inOperator( MToeAngle, inStartIndex + 10 );; + return 11 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleWheelDataGeneratedInfo Info; + const PxVehicleWheelDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleSuspensionData; + struct PxVehicleSuspensionDataGeneratedValues + { + PxReal RecipMaxCompression; + PxReal RecipMaxDroop; + PxReal MSpringStrength; + PxReal MSpringDamperRate; + PxReal MMaxCompression; + PxReal MMaxDroop; + PxReal MSprungMass; + PxReal MCamberAtRest; + PxReal MCamberAtMaxCompression; + PxReal MCamberAtMaxDroop; + PxVehicleSuspensionDataGeneratedValues( const PxVehicleSuspensionData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, RecipMaxCompression, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, RecipMaxDroop, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MSpringStrength, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MSpringDamperRate, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MMaxCompression, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MMaxDroop, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MSprungMass, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MCamberAtRest, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MCamberAtMaxCompression, PxVehicleSuspensionDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleSuspensionData, MCamberAtMaxDroop, PxVehicleSuspensionDataGeneratedValues) + struct PxVehicleSuspensionDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleSuspensionData"; } + PxReadOnlyPropertyInfo RecipMaxCompression; + PxReadOnlyPropertyInfo RecipMaxDroop; + PxWriteOnlyPropertyInfo MassAndPreserveNaturalFrequency; + PxPropertyInfo MSpringStrength; + PxPropertyInfo MSpringDamperRate; + PxPropertyInfo MMaxCompression; + PxPropertyInfo MMaxDroop; + PxPropertyInfo MSprungMass; + PxPropertyInfo MCamberAtRest; + PxPropertyInfo MCamberAtMaxCompression; + PxPropertyInfo MCamberAtMaxDroop; + + PxVehicleSuspensionDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 11; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( RecipMaxCompression, inStartIndex + 0 );; + inOperator( RecipMaxDroop, inStartIndex + 1 );; + inOperator( MassAndPreserveNaturalFrequency, inStartIndex + 2 );; + inOperator( MSpringStrength, inStartIndex + 3 );; + inOperator( MSpringDamperRate, inStartIndex + 4 );; + inOperator( MMaxCompression, inStartIndex + 5 );; + inOperator( MMaxDroop, inStartIndex + 6 );; + inOperator( MSprungMass, inStartIndex + 7 );; + inOperator( MCamberAtRest, inStartIndex + 8 );; + inOperator( MCamberAtMaxCompression, inStartIndex + 9 );; + inOperator( MCamberAtMaxDroop, inStartIndex + 10 );; + return 11 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleSuspensionDataGeneratedInfo Info; + const PxVehicleSuspensionDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleAntiRollBarData; + struct PxVehicleAntiRollBarDataGeneratedValues + { + PxU32 MWheel0; + PxU32 MWheel1; + PxF32 MStiffness; + PxVehicleAntiRollBarDataGeneratedValues( const PxVehicleAntiRollBarData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAntiRollBarData, MWheel0, PxVehicleAntiRollBarDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAntiRollBarData, MWheel1, PxVehicleAntiRollBarDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleAntiRollBarData, MStiffness, PxVehicleAntiRollBarDataGeneratedValues) + struct PxVehicleAntiRollBarDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleAntiRollBarData"; } + PxPropertyInfo MWheel0; + PxPropertyInfo MWheel1; + PxPropertyInfo MStiffness; + + PxVehicleAntiRollBarDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( MWheel0, inStartIndex + 0 );; + inOperator( MWheel1, inStartIndex + 1 );; + inOperator( MStiffness, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleAntiRollBarDataGeneratedInfo Info; + const PxVehicleAntiRollBarDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleTireData; + struct PxVehicleTireDataGeneratedValues + { + PxReal RecipLongitudinalStiffnessPerUnitGravity; + PxReal FrictionVsSlipGraphRecipx1Minusx0; + PxReal FrictionVsSlipGraphRecipx2Minusx1; + PxReal MLatStiffX; + PxReal MLatStiffY; + PxReal MLongitudinalStiffnessPerUnitGravity; + PxReal MCamberStiffnessPerUnitGravity; + PxU32 MType; + PxReal MFrictionVsSlipGraph[3][2]; + PxVehicleTireDataGeneratedValues( const PxVehicleTireData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, RecipLongitudinalStiffnessPerUnitGravity, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, FrictionVsSlipGraphRecipx1Minusx0, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, FrictionVsSlipGraphRecipx2Minusx1, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MLatStiffX, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MLatStiffY, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MLongitudinalStiffnessPerUnitGravity, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MCamberStiffnessPerUnitGravity, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MType, PxVehicleTireDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleTireData, MFrictionVsSlipGraph, PxVehicleTireDataGeneratedValues) + struct PxVehicleTireDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleTireData"; } + PxReadOnlyPropertyInfo RecipLongitudinalStiffnessPerUnitGravity; + PxReadOnlyPropertyInfo FrictionVsSlipGraphRecipx1Minusx0; + PxReadOnlyPropertyInfo FrictionVsSlipGraphRecipx2Minusx1; + PxPropertyInfo MLatStiffX; + PxPropertyInfo MLatStiffY; + PxPropertyInfo MLongitudinalStiffnessPerUnitGravity; + PxPropertyInfo MCamberStiffnessPerUnitGravity; + PxPropertyInfo MType; + MFrictionVsSlipGraphProperty MFrictionVsSlipGraph; + + PxVehicleTireDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 9; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( RecipLongitudinalStiffnessPerUnitGravity, inStartIndex + 0 );; + inOperator( FrictionVsSlipGraphRecipx1Minusx0, inStartIndex + 1 );; + inOperator( FrictionVsSlipGraphRecipx2Minusx1, inStartIndex + 2 );; + inOperator( MLatStiffX, inStartIndex + 3 );; + inOperator( MLatStiffY, inStartIndex + 4 );; + inOperator( MLongitudinalStiffnessPerUnitGravity, inStartIndex + 5 );; + inOperator( MCamberStiffnessPerUnitGravity, inStartIndex + 6 );; + inOperator( MType, inStartIndex + 7 );; + inOperator( MFrictionVsSlipGraph, inStartIndex + 8 );; + return 9 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleTireDataGeneratedInfo Info; + const PxVehicleTireDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleWheels4SimData; + struct PxVehicleWheels4SimDataGeneratedValues + { + const PxReal * TireRestLoadsArray; + const PxReal * RecipTireRestLoadsArray; + PxVehicleWheels4SimDataGeneratedValues( const PxVehicleWheels4SimData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels4SimData, TireRestLoadsArray, PxVehicleWheels4SimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels4SimData, RecipTireRestLoadsArray, PxVehicleWheels4SimDataGeneratedValues) + struct PxVehicleWheels4SimDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleWheels4SimData"; } + PxReadOnlyPropertyInfo TireRestLoadsArray; + PxReadOnlyPropertyInfo RecipTireRestLoadsArray; + + PxVehicleWheels4SimDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( TireRestLoadsArray, inStartIndex + 0 );; + inOperator( RecipTireRestLoadsArray, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleWheels4SimDataGeneratedInfo Info; + const PxVehicleWheels4SimDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleWheelsSimData; + struct PxVehicleWheelsSimDataGeneratedValues + { + PxVehicleTireLoadFilterData TireLoadFilterData; + PxF32 MinLongSlipDenominator; + PxF32 ThresholdLongSpeed; + PxU32 LowForwardSpeedSubStepCount; + PxU32 HighForwardSpeedSubStepCount; + PxVehicleWheelsSimDataGeneratedValues( const PxVehicleWheelsSimData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsSimData, TireLoadFilterData, PxVehicleWheelsSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsSimData, MinLongSlipDenominator, PxVehicleWheelsSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsSimData, ThresholdLongSpeed, PxVehicleWheelsSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsSimData, LowForwardSpeedSubStepCount, PxVehicleWheelsSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsSimData, HighForwardSpeedSubStepCount, PxVehicleWheelsSimDataGeneratedValues) + struct PxVehicleWheelsSimDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleWheelsSimData"; } + PxWriteOnlyPropertyInfo ChassisMass; + PxExtendedIndexedPropertyInfo SuspensionData; + PxExtendedIndexedPropertyInfo WheelData; + PxExtendedIndexedPropertyInfo TireData; + PxExtendedIndexedPropertyInfo SuspTravelDirection; + PxExtendedIndexedPropertyInfo SuspForceAppPointOffset; + PxExtendedIndexedPropertyInfo TireForceAppPointOffset; + PxExtendedIndexedPropertyInfo WheelCentreOffset; + PxExtendedIndexedPropertyInfo WheelShapeMapping; + PxExtendedIndexedPropertyInfo SceneQueryFilterData; + PxExtendedIndexedPropertyInfo AntiRollBarData; + PxPropertyInfo TireLoadFilterData; + PxPropertyInfo MinLongSlipDenominator; + PxPropertyInfo ThresholdLongSpeed; + PxPropertyInfo LowForwardSpeedSubStepCount; + PxPropertyInfo HighForwardSpeedSubStepCount; + PxExtendedIndexedPropertyInfo WheelEnabledState; + + PxVehicleWheelsSimDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 17; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ChassisMass, inStartIndex + 0 );; + inOperator( SuspensionData, inStartIndex + 1 );; + inOperator( WheelData, inStartIndex + 2 );; + inOperator( TireData, inStartIndex + 3 );; + inOperator( SuspTravelDirection, inStartIndex + 4 );; + inOperator( SuspForceAppPointOffset, inStartIndex + 5 );; + inOperator( TireForceAppPointOffset, inStartIndex + 6 );; + inOperator( WheelCentreOffset, inStartIndex + 7 );; + inOperator( WheelShapeMapping, inStartIndex + 8 );; + inOperator( SceneQueryFilterData, inStartIndex + 9 );; + inOperator( AntiRollBarData, inStartIndex + 10 );; + inOperator( TireLoadFilterData, inStartIndex + 11 );; + inOperator( MinLongSlipDenominator, inStartIndex + 12 );; + inOperator( ThresholdLongSpeed, inStartIndex + 13 );; + inOperator( LowForwardSpeedSubStepCount, inStartIndex + 14 );; + inOperator( HighForwardSpeedSubStepCount, inStartIndex + 15 );; + inOperator( WheelEnabledState, inStartIndex + 16 );; + return 17 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleWheelsSimDataGeneratedInfo Info; + const PxVehicleWheelsSimDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleWheelsDynData; + struct PxVehicleWheelsDynDataGeneratedValues + { + PxVehicleWheels4DynData * Wheel4DynData; + PxVehicleWheelsDynDataGeneratedValues( const PxVehicleWheelsDynData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheelsDynData, Wheel4DynData, PxVehicleWheelsDynDataGeneratedValues) + struct PxVehicleWheelsDynDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleWheelsDynData"; } + PxWriteOnlyPropertyInfo TireForceShaderFunction; + PxExtendedIndexedPropertyInfo WheelRotationSpeed; + PxExtendedIndexedPropertyInfo WheelRotationAngle; + PxReadOnlyPropertyInfo Wheel4DynData; + + PxVehicleWheelsDynDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( TireForceShaderFunction, inStartIndex + 0 );; + inOperator( WheelRotationSpeed, inStartIndex + 1 );; + inOperator( WheelRotationAngle, inStartIndex + 2 );; + inOperator( Wheel4DynData, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleWheelsDynDataGeneratedInfo Info; + const PxVehicleWheelsDynDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleWheels; + struct PxVehicleWheelsGeneratedValues + { + PxU32 VehicleType; + const PxRigidDynamic * RigidDynamicActor; + const char * ConcreteTypeName; + PxVehicleWheelsSimData MWheelsSimData; + PxVehicleWheelsDynData MWheelsDynData; + PxVehicleWheelsGeneratedValues( const PxVehicleWheels* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels, VehicleType, PxVehicleWheelsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels, RigidDynamicActor, PxVehicleWheelsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels, ConcreteTypeName, PxVehicleWheelsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels, MWheelsSimData, PxVehicleWheelsGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleWheels, MWheelsDynData, PxVehicleWheelsGeneratedValues) + struct PxVehicleWheelsGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleWheels"; } + PxReadOnlyPropertyInfo VehicleType; + PxReadOnlyPropertyInfo RigidDynamicActor; + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MWheelsSimData; + PxPropertyInfo MWheelsDynData; + + PxVehicleWheelsGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 5; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( VehicleType, inStartIndex + 0 );; + inOperator( RigidDynamicActor, inStartIndex + 1 );; + inOperator( ConcreteTypeName, inStartIndex + 2 );; + inOperator( MWheelsSimData, inStartIndex + 3 );; + inOperator( MWheelsDynData, inStartIndex + 4 );; + return 5 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleWheelsGeneratedInfo Info; + const PxVehicleWheelsGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDriveDynData; + struct PxVehicleDriveDynDataGeneratedValues + { + _Bool GearUp; + _Bool GearDown; + _Bool UseAutoGears; + PxU32 CurrentGear; + PxU32 TargetGear; + PxReal EngineRotationSpeed; + PxU32 GearChange; + PxReal GearSwitchTime; + PxReal AutoBoxSwitchTime; + _Bool MUseAutoGears; + _Bool MGearUpPressed; + _Bool MGearDownPressed; + PxU32 MCurrentGear; + PxU32 MTargetGear; + PxReal MEnginespeed; + PxReal MGearSwitchTime; + PxReal MAutoBoxSwitchTime; + PxVehicleDriveDynDataGeneratedValues( const PxVehicleDriveDynData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, GearUp, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, GearDown, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, UseAutoGears, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, CurrentGear, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, TargetGear, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, EngineRotationSpeed, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, GearChange, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, GearSwitchTime, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, AutoBoxSwitchTime, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MUseAutoGears, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MGearUpPressed, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MGearDownPressed, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MCurrentGear, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MTargetGear, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MEnginespeed, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MGearSwitchTime, PxVehicleDriveDynDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveDynData, MAutoBoxSwitchTime, PxVehicleDriveDynDataGeneratedValues) + struct PxVehicleDriveDynDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleDriveDynData"; } + PxExtendedIndexedPropertyInfo AnalogInput; + PxPropertyInfo GearUp; + PxPropertyInfo GearDown; + PxPropertyInfo UseAutoGears; + PxPropertyInfo CurrentGear; + PxPropertyInfo TargetGear; + PxPropertyInfo EngineRotationSpeed; + PxPropertyInfo GearChange; + PxPropertyInfo GearSwitchTime; + PxPropertyInfo AutoBoxSwitchTime; + PxPropertyInfo MUseAutoGears; + PxPropertyInfo MGearUpPressed; + PxPropertyInfo MGearDownPressed; + PxPropertyInfo MCurrentGear; + PxPropertyInfo MTargetGear; + PxPropertyInfo MEnginespeed; + PxPropertyInfo MGearSwitchTime; + PxPropertyInfo MAutoBoxSwitchTime; + + PxVehicleDriveDynDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 18; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( AnalogInput, inStartIndex + 0 );; + inOperator( GearUp, inStartIndex + 1 );; + inOperator( GearDown, inStartIndex + 2 );; + inOperator( UseAutoGears, inStartIndex + 3 );; + inOperator( CurrentGear, inStartIndex + 4 );; + inOperator( TargetGear, inStartIndex + 5 );; + inOperator( EngineRotationSpeed, inStartIndex + 6 );; + inOperator( GearChange, inStartIndex + 7 );; + inOperator( GearSwitchTime, inStartIndex + 8 );; + inOperator( AutoBoxSwitchTime, inStartIndex + 9 );; + inOperator( MUseAutoGears, inStartIndex + 10 );; + inOperator( MGearUpPressed, inStartIndex + 11 );; + inOperator( MGearDownPressed, inStartIndex + 12 );; + inOperator( MCurrentGear, inStartIndex + 13 );; + inOperator( MTargetGear, inStartIndex + 14 );; + inOperator( MEnginespeed, inStartIndex + 15 );; + inOperator( MGearSwitchTime, inStartIndex + 16 );; + inOperator( MAutoBoxSwitchTime, inStartIndex + 17 );; + return 18 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveDynDataGeneratedInfo Info; + const PxVehicleDriveDynDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDriveSimData; + struct PxVehicleDriveSimDataGeneratedValues + { + PxVehicleEngineData EngineData; + PxVehicleGearsData GearsData; + PxVehicleClutchData ClutchData; + PxVehicleAutoBoxData AutoBoxData; + PxVehicleDriveSimDataGeneratedValues( const PxVehicleDriveSimData* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData, EngineData, PxVehicleDriveSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData, GearsData, PxVehicleDriveSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData, ClutchData, PxVehicleDriveSimDataGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData, AutoBoxData, PxVehicleDriveSimDataGeneratedValues) + struct PxVehicleDriveSimDataGeneratedInfo + + { + static const char* getClassName() { return "PxVehicleDriveSimData"; } + PxPropertyInfo EngineData; + PxPropertyInfo GearsData; + PxPropertyInfo ClutchData; + PxPropertyInfo AutoBoxData; + + PxVehicleDriveSimDataGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( EngineData, inStartIndex + 0 );; + inOperator( GearsData, inStartIndex + 1 );; + inOperator( ClutchData, inStartIndex + 2 );; + inOperator( AutoBoxData, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveSimDataGeneratedInfo Info; + const PxVehicleDriveSimDataGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDriveSimData4W; + struct PxVehicleDriveSimData4WGeneratedValues + : PxVehicleDriveSimDataGeneratedValues { + PxVehicleDifferential4WData DiffData; + PxVehicleAckermannGeometryData AckermannGeometryData; + PxVehicleDriveSimData4WGeneratedValues( const PxVehicleDriveSimData4W* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData4W, DiffData, PxVehicleDriveSimData4WGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimData4W, AckermannGeometryData, PxVehicleDriveSimData4WGeneratedValues) + struct PxVehicleDriveSimData4WGeneratedInfo + : PxVehicleDriveSimDataGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDriveSimData4W"; } + PxPropertyInfo DiffData; + PxPropertyInfo AckermannGeometryData; + + PxVehicleDriveSimData4WGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleDriveSimDataGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleDriveSimDataGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleDriveSimDataGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( DiffData, inStartIndex + 0 );; + inOperator( AckermannGeometryData, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveSimData4WGeneratedInfo Info; + const PxVehicleDriveSimData4WGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDrive; + struct PxVehicleDriveGeneratedValues + : PxVehicleWheelsGeneratedValues { + const char * ConcreteTypeName; + PxVehicleDriveDynData MDriveDynData; + PxVehicleDriveGeneratedValues( const PxVehicleDrive* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDrive, ConcreteTypeName, PxVehicleDriveGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDrive, MDriveDynData, PxVehicleDriveGeneratedValues) + struct PxVehicleDriveGeneratedInfo + : PxVehicleWheelsGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDrive"; } + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MDriveDynData; + + PxVehicleDriveGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleWheelsGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleWheelsGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleWheelsGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ConcreteTypeName, inStartIndex + 0 );; + inOperator( MDriveDynData, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveGeneratedInfo Info; + const PxVehicleDriveGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDrive4W; + struct PxVehicleDrive4WGeneratedValues + : PxVehicleDriveGeneratedValues { + const char * ConcreteTypeName; + PxVehicleDriveSimData4W MDriveSimData; + PxVehicleDrive4WGeneratedValues( const PxVehicleDrive4W* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDrive4W, ConcreteTypeName, PxVehicleDrive4WGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDrive4W, MDriveSimData, PxVehicleDrive4WGeneratedValues) + struct PxVehicleDrive4WGeneratedInfo + : PxVehicleDriveGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDrive4W"; } + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MDriveSimData; + + PxVehicleDrive4WGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleDriveGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleDriveGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleDriveGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ConcreteTypeName, inStartIndex + 0 );; + inOperator( MDriveSimData, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDrive4WGeneratedInfo Info; + const PxVehicleDrive4WGeneratedInfo* getInfo() { return &Info; } + }; + + static PxU32ToName g_physx__PxVehicleDriveTankControlModel__EnumConversion[] = { + { "eSTANDARD", static_cast( physx::PxVehicleDriveTankControlModel::eSTANDARD ) }, + { "eSPECIAL", static_cast( physx::PxVehicleDriveTankControlModel::eSPECIAL ) }, + { NULL, 0 } + }; + +template<> struct PxEnumTraits< physx::PxVehicleDriveTankControlModel::Enum > { PxEnumTraits() : NameConversion( g_physx__PxVehicleDriveTankControlModel__EnumConversion ) {} const PxU32ToName* NameConversion; }; + class PxVehicleDriveTank; + struct PxVehicleDriveTankGeneratedValues + : PxVehicleDriveGeneratedValues { + PxVehicleDriveTankControlModel::Enum DriveModel; + const char * ConcreteTypeName; + PxVehicleDriveSimData MDriveSimData; + PxVehicleDriveTankGeneratedValues( const PxVehicleDriveTank* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveTank, DriveModel, PxVehicleDriveTankGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveTank, ConcreteTypeName, PxVehicleDriveTankGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveTank, MDriveSimData, PxVehicleDriveTankGeneratedValues) + struct PxVehicleDriveTankGeneratedInfo + : PxVehicleDriveGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDriveTank"; } + PxPropertyInfo DriveModel; + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MDriveSimData; + + PxVehicleDriveTankGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleDriveGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleDriveGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 3; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleDriveGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( DriveModel, inStartIndex + 0 );; + inOperator( ConcreteTypeName, inStartIndex + 1 );; + inOperator( MDriveSimData, inStartIndex + 2 );; + return 3 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveTankGeneratedInfo Info; + const PxVehicleDriveTankGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDriveSimDataNW; + struct PxVehicleDriveSimDataNWGeneratedValues + : PxVehicleDriveSimDataGeneratedValues { + PxVehicleDifferentialNWData DiffData; + PxVehicleDriveSimDataNWGeneratedValues( const PxVehicleDriveSimDataNW* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveSimDataNW, DiffData, PxVehicleDriveSimDataNWGeneratedValues) + struct PxVehicleDriveSimDataNWGeneratedInfo + : PxVehicleDriveSimDataGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDriveSimDataNW"; } + PxPropertyInfo DiffData; + + PxVehicleDriveSimDataNWGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleDriveSimDataGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleDriveSimDataGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 1; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleDriveSimDataGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( DiffData, inStartIndex + 0 );; + return 1 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveSimDataNWGeneratedInfo Info; + const PxVehicleDriveSimDataNWGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleDriveNW; + struct PxVehicleDriveNWGeneratedValues + : PxVehicleDriveGeneratedValues { + const char * ConcreteTypeName; + PxVehicleDriveSimDataNW MDriveSimData; + PxVehicleDriveNWGeneratedValues( const PxVehicleDriveNW* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveNW, ConcreteTypeName, PxVehicleDriveNWGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleDriveNW, MDriveSimData, PxVehicleDriveNWGeneratedValues) + struct PxVehicleDriveNWGeneratedInfo + : PxVehicleDriveGeneratedInfo + { + static const char* getClassName() { return "PxVehicleDriveNW"; } + PxReadOnlyPropertyInfo ConcreteTypeName; + PxPropertyInfo MDriveSimData; + + PxVehicleDriveNWGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleDriveGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleDriveGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 2; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleDriveGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( ConcreteTypeName, inStartIndex + 0 );; + inOperator( MDriveSimData, inStartIndex + 1 );; + return 2 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleDriveNWGeneratedInfo Info; + const PxVehicleDriveNWGeneratedInfo* getInfo() { return &Info; } + }; + + class PxVehicleNoDrive; + struct PxVehicleNoDriveGeneratedValues + : PxVehicleWheelsGeneratedValues { + const char * ConcreteTypeName; + PxVehicleNoDriveGeneratedValues( const PxVehicleNoDrive* inSource ); + }; + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxVehicleNoDrive, ConcreteTypeName, PxVehicleNoDriveGeneratedValues) + struct PxVehicleNoDriveGeneratedInfo + : PxVehicleWheelsGeneratedInfo + { + static const char* getClassName() { return "PxVehicleNoDrive"; } + PxExtendedIndexedPropertyInfo BrakeTorque; + PxExtendedIndexedPropertyInfo DriveTorque; + PxExtendedIndexedPropertyInfo SteerAngle; + PxReadOnlyPropertyInfo ConcreteTypeName; + + PxVehicleNoDriveGeneratedInfo(); + template + TReturnType visitType( TOperator inOperator ) const + { + return inOperator( reinterpret_cast(NULL) ); + } + template + void visitBases( TOperator inOperator ) + { + PX_UNUSED(inOperator); + inOperator( *static_cast( this ) ); + } + template + PxU32 visitBaseProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inStartIndex = PxVehicleWheelsGeneratedInfo::visitBaseProperties( inOperator, inStartIndex ); + inStartIndex = PxVehicleWheelsGeneratedInfo::visitInstanceProperties( inOperator, inStartIndex ); + return inStartIndex; + } + static PxU32 instancePropertyCount() { return 4; } + static PxU32 totalPropertyCount() { return instancePropertyCount() + + PxVehicleWheelsGeneratedInfo::totalPropertyCount(); } + template + PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const + { + PX_UNUSED(inOperator); + PX_UNUSED(inStartIndex); + inOperator( BrakeTorque, inStartIndex + 0 );; + inOperator( DriveTorque, inStartIndex + 1 );; + inOperator( SteerAngle, inStartIndex + 2 );; + inOperator( ConcreteTypeName, inStartIndex + 3 );; + return 4 + inStartIndex; + } + }; + template<> struct PxClassInfoTraits + { + PxVehicleNoDriveGeneratedInfo Info; + const PxVehicleNoDriveGeneratedInfo* getInfo() { return &Info; } + }; + + + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON +#undef PX_PROPERTY_INFO_NAME diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h new file mode 100644 index 000000000..35d2b4760 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h @@ -0,0 +1,97 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_VEHICLE_METADATAOBJECTS_H +#define PX_VEHICLE_METADATAOBJECTS_H +#include "PxPhysicsAPI.h" +#include "extensions/PxExtensionsAPI.h" +#include "PxMetaDataObjects.h" +#include "PxExtensionMetaDataObjects.h" + +/** \addtogroup physics +@{ +*/ + +namespace physx +{ + +struct PxVehiclePropertyInfoName +{ + enum Enum + { + Unnamed = PxExtensionsPropertyInfoName::LastPxPropertyInfoName, +#include "PxVehicleAutoGeneratedMetaDataObjectNames.h" + LastPxPropertyInfoName + }; +}; + +#define DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( type, prop, valueStruct ) \ + template<> struct PxPropertyToValueStructMemberMap< PxVehiclePropertyInfoName::type##_##prop > \ + { \ + PxU32 Offset; \ + PxPropertyToValueStructMemberMap< PxVehiclePropertyInfoName::type##_##prop >() : Offset( PX_OFFSET_OF_RT( valueStruct, prop ) ) {} \ + template void visitProp( TOperator inOperator, valueStruct& inStruct ) { inOperator( inStruct.prop ); } \ + }; + +struct MFrictionVsSlipGraphProperty : public PxExtendedDualIndexedPropertyInfo +{ + PX_PHYSX_CORE_API MFrictionVsSlipGraphProperty(); +}; + +struct MTorqueCurveProperty : public PxFixedSizeLookupTablePropertyInfo +{ + PX_PHYSX_CORE_API MTorqueCurveProperty(); +}; + +#if PX_CLANG +#if __has_warning("-Wshadow-field") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wshadow-field" +#endif +#endif +#include "PxVehicleAutoGeneratedMetaDataObjects.h" +#if PX_CLANG +#if __has_warning("-Wshadow-field") +#pragma clang diagnostic pop +#endif +#endif + +#undef DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp new file mode 100644 index 000000000..7056b9989 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp @@ -0,0 +1,727 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +// This code is auto-generated by the PhysX Clang metadata generator. Do not edit or be +// prepared for your edits to be quietly ignored next time the clang metadata generator is +// run. You can find the most recent version of clang metadata generator by contacting +// Chris Nuernberger or Dilip or Adam. +// The source code for the generate was at one time checked into: +// physx/PhysXMetaDataGenerator/llvm/tools/clang/lib/Frontend/PhysXMetaDataAction.cpp +#include "PxVehicleMetaDataObjects.h" +#include "PxMetaDataCppPrefix.h" +#include "PxVehicleSuspWheelTire4.h" +using namespace physx; +inline PxVec3 getPxVehicleChassisDataMMOI( const PxVehicleChassisData* inOwner ) { return inOwner->mMOI; } +inline void setPxVehicleChassisDataMMOI( PxVehicleChassisData* inOwner, PxVec3 inData) { inOwner->mMOI = inData; } +inline PxReal getPxVehicleChassisDataMMass( const PxVehicleChassisData* inOwner ) { return inOwner->mMass; } +inline void setPxVehicleChassisDataMMass( PxVehicleChassisData* inOwner, PxReal inData) { inOwner->mMass = inData; } +inline PxVec3 getPxVehicleChassisDataMCMOffset( const PxVehicleChassisData* inOwner ) { return inOwner->mCMOffset; } +inline void setPxVehicleChassisDataMCMOffset( PxVehicleChassisData* inOwner, PxVec3 inData) { inOwner->mCMOffset = inData; } + PxVehicleChassisDataGeneratedInfo::PxVehicleChassisDataGeneratedInfo() + : MMOI( "MMOI", setPxVehicleChassisDataMMOI, getPxVehicleChassisDataMMOI ) + , MMass( "MMass", setPxVehicleChassisDataMMass, getPxVehicleChassisDataMMass ) + , MCMOffset( "MCMOffset", setPxVehicleChassisDataMCMOffset, getPxVehicleChassisDataMCMOffset ) +{} + PxVehicleChassisDataGeneratedValues::PxVehicleChassisDataGeneratedValues( const PxVehicleChassisData* inSource ) + :MMOI( inSource->mMOI ) + ,MMass( inSource->mMass ) + ,MCMOffset( inSource->mCMOffset ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleEngineData_RecipMOI( const PxVehicleEngineData* inObj ) { return inObj->getRecipMOI(); } +PxReal getPxVehicleEngineData_RecipMaxOmega( const PxVehicleEngineData* inObj ) { return inObj->getRecipMaxOmega(); } +inline PxReal getPxVehicleEngineDataMMOI( const PxVehicleEngineData* inOwner ) { return inOwner->mMOI; } +inline void setPxVehicleEngineDataMMOI( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mMOI = inData; } +inline PxReal getPxVehicleEngineDataMPeakTorque( const PxVehicleEngineData* inOwner ) { return inOwner->mPeakTorque; } +inline void setPxVehicleEngineDataMPeakTorque( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mPeakTorque = inData; } +inline PxReal getPxVehicleEngineDataMMaxOmega( const PxVehicleEngineData* inOwner ) { return inOwner->mMaxOmega; } +inline void setPxVehicleEngineDataMMaxOmega( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mMaxOmega = inData; } +inline PxReal getPxVehicleEngineDataMDampingRateFullThrottle( const PxVehicleEngineData* inOwner ) { return inOwner->mDampingRateFullThrottle; } +inline void setPxVehicleEngineDataMDampingRateFullThrottle( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mDampingRateFullThrottle = inData; } +inline PxReal getPxVehicleEngineDataMDampingRateZeroThrottleClutchEngaged( const PxVehicleEngineData* inOwner ) { return inOwner->mDampingRateZeroThrottleClutchEngaged; } +inline void setPxVehicleEngineDataMDampingRateZeroThrottleClutchEngaged( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mDampingRateZeroThrottleClutchEngaged = inData; } +inline PxReal getPxVehicleEngineDataMDampingRateZeroThrottleClutchDisengaged( const PxVehicleEngineData* inOwner ) { return inOwner->mDampingRateZeroThrottleClutchDisengaged; } +inline void setPxVehicleEngineDataMDampingRateZeroThrottleClutchDisengaged( PxVehicleEngineData* inOwner, PxReal inData) { inOwner->mDampingRateZeroThrottleClutchDisengaged = inData; } + PxVehicleEngineDataGeneratedInfo::PxVehicleEngineDataGeneratedInfo() + : RecipMOI( "RecipMOI", getPxVehicleEngineData_RecipMOI) + , RecipMaxOmega( "RecipMaxOmega", getPxVehicleEngineData_RecipMaxOmega) + , MMOI( "MMOI", setPxVehicleEngineDataMMOI, getPxVehicleEngineDataMMOI ) + , MPeakTorque( "MPeakTorque", setPxVehicleEngineDataMPeakTorque, getPxVehicleEngineDataMPeakTorque ) + , MMaxOmega( "MMaxOmega", setPxVehicleEngineDataMMaxOmega, getPxVehicleEngineDataMMaxOmega ) + , MDampingRateFullThrottle( "MDampingRateFullThrottle", setPxVehicleEngineDataMDampingRateFullThrottle, getPxVehicleEngineDataMDampingRateFullThrottle ) + , MDampingRateZeroThrottleClutchEngaged( "MDampingRateZeroThrottleClutchEngaged", setPxVehicleEngineDataMDampingRateZeroThrottleClutchEngaged, getPxVehicleEngineDataMDampingRateZeroThrottleClutchEngaged ) + , MDampingRateZeroThrottleClutchDisengaged( "MDampingRateZeroThrottleClutchDisengaged", setPxVehicleEngineDataMDampingRateZeroThrottleClutchDisengaged, getPxVehicleEngineDataMDampingRateZeroThrottleClutchDisengaged ) +{} + PxVehicleEngineDataGeneratedValues::PxVehicleEngineDataGeneratedValues( const PxVehicleEngineData* inSource ) + :RecipMOI( getPxVehicleEngineData_RecipMOI( inSource ) ) + ,RecipMaxOmega( getPxVehicleEngineData_RecipMaxOmega( inSource ) ) + ,MMOI( inSource->mMOI ) + ,MPeakTorque( inSource->mPeakTorque ) + ,MMaxOmega( inSource->mMaxOmega ) + ,MDampingRateFullThrottle( inSource->mDampingRateFullThrottle ) + ,MDampingRateZeroThrottleClutchEngaged( inSource->mDampingRateZeroThrottleClutchEngaged ) + ,MDampingRateZeroThrottleClutchDisengaged( inSource->mDampingRateZeroThrottleClutchDisengaged ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleGearsData_GearRatio( PxVehicleGearsData* inObj, PxVehicleGearsData::Enum inIndex, PxReal inArg ){ inObj->setGearRatio( inIndex, inArg ); } +PxReal getPxVehicleGearsData_GearRatio( const PxVehicleGearsData* inObj, PxVehicleGearsData::Enum inIndex ) { return inObj->getGearRatio( inIndex ); } +inline PxReal getPxVehicleGearsDataMFinalRatio( const PxVehicleGearsData* inOwner ) { return inOwner->mFinalRatio; } +inline void setPxVehicleGearsDataMFinalRatio( PxVehicleGearsData* inOwner, PxReal inData) { inOwner->mFinalRatio = inData; } +inline PxU32 getPxVehicleGearsDataMNbRatios( const PxVehicleGearsData* inOwner ) { return inOwner->mNbRatios; } +inline void setPxVehicleGearsDataMNbRatios( PxVehicleGearsData* inOwner, PxU32 inData) { inOwner->mNbRatios = inData; } +inline PxReal getPxVehicleGearsDataMSwitchTime( const PxVehicleGearsData* inOwner ) { return inOwner->mSwitchTime; } +inline void setPxVehicleGearsDataMSwitchTime( PxVehicleGearsData* inOwner, PxReal inData) { inOwner->mSwitchTime = inData; } + PxVehicleGearsDataGeneratedInfo::PxVehicleGearsDataGeneratedInfo() + : GearRatio( "GearRatio", setPxVehicleGearsData_GearRatio, getPxVehicleGearsData_GearRatio) + , MFinalRatio( "MFinalRatio", setPxVehicleGearsDataMFinalRatio, getPxVehicleGearsDataMFinalRatio ) + , MNbRatios( "MNbRatios", setPxVehicleGearsDataMNbRatios, getPxVehicleGearsDataMNbRatios ) + , MSwitchTime( "MSwitchTime", setPxVehicleGearsDataMSwitchTime, getPxVehicleGearsDataMSwitchTime ) +{} + PxVehicleGearsDataGeneratedValues::PxVehicleGearsDataGeneratedValues( const PxVehicleGearsData* inSource ) + :MFinalRatio( inSource->mFinalRatio ) + ,MNbRatios( inSource->mNbRatios ) + ,MSwitchTime( inSource->mSwitchTime ) +{ + PX_UNUSED(inSource); + for ( PxU32 idx = 0; idx < static_cast( physx::PxVehicleGearsData::eGEARSRATIO_COUNT ); ++idx ) + GearRatio[idx] = getPxVehicleGearsData_GearRatio( inSource, static_cast< PxVehicleGearsData::Enum >( idx ) ); +} +void setPxVehicleAutoBoxData_Latency( PxVehicleAutoBoxData* inObj, const PxReal inArg){ inObj->setLatency( inArg ); } +PxReal getPxVehicleAutoBoxData_Latency( const PxVehicleAutoBoxData* inObj ) { return inObj->getLatency(); } +void setPxVehicleAutoBoxData_UpRatios( PxVehicleAutoBoxData* inObj, PxVehicleGearsData::Enum inIndex, PxReal inArg ){ inObj->setUpRatios( inIndex, inArg ); } +PxReal getPxVehicleAutoBoxData_UpRatios( const PxVehicleAutoBoxData* inObj, PxVehicleGearsData::Enum inIndex ) { return inObj->getUpRatios( inIndex ); } +void setPxVehicleAutoBoxData_DownRatios( PxVehicleAutoBoxData* inObj, PxVehicleGearsData::Enum inIndex, PxReal inArg ){ inObj->setDownRatios( inIndex, inArg ); } +PxReal getPxVehicleAutoBoxData_DownRatios( const PxVehicleAutoBoxData* inObj, PxVehicleGearsData::Enum inIndex ) { return inObj->getDownRatios( inIndex ); } + PxVehicleAutoBoxDataGeneratedInfo::PxVehicleAutoBoxDataGeneratedInfo() + : Latency( "Latency", setPxVehicleAutoBoxData_Latency, getPxVehicleAutoBoxData_Latency) + , UpRatios( "UpRatios", setPxVehicleAutoBoxData_UpRatios, getPxVehicleAutoBoxData_UpRatios) + , DownRatios( "DownRatios", setPxVehicleAutoBoxData_DownRatios, getPxVehicleAutoBoxData_DownRatios) +{} + PxVehicleAutoBoxDataGeneratedValues::PxVehicleAutoBoxDataGeneratedValues( const PxVehicleAutoBoxData* inSource ) + :Latency( getPxVehicleAutoBoxData_Latency( inSource ) ) +{ + PX_UNUSED(inSource); + for ( PxU32 idx = 0; idx < static_cast( physx::PxVehicleGearsData::eGEARSRATIO_COUNT ); ++idx ) + UpRatios[idx] = getPxVehicleAutoBoxData_UpRatios( inSource, static_cast< PxVehicleGearsData::Enum >( idx ) ); + for ( PxU32 idx = 0; idx < static_cast( physx::PxVehicleGearsData::eGEARSRATIO_COUNT ); ++idx ) + DownRatios[idx] = getPxVehicleAutoBoxData_DownRatios( inSource, static_cast< PxVehicleGearsData::Enum >( idx ) ); +} +inline PxReal getPxVehicleDifferential4WDataMFrontRearSplit( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mFrontRearSplit; } +inline void setPxVehicleDifferential4WDataMFrontRearSplit( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mFrontRearSplit = inData; } +inline PxReal getPxVehicleDifferential4WDataMFrontLeftRightSplit( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mFrontLeftRightSplit; } +inline void setPxVehicleDifferential4WDataMFrontLeftRightSplit( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mFrontLeftRightSplit = inData; } +inline PxReal getPxVehicleDifferential4WDataMRearLeftRightSplit( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mRearLeftRightSplit; } +inline void setPxVehicleDifferential4WDataMRearLeftRightSplit( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mRearLeftRightSplit = inData; } +inline PxReal getPxVehicleDifferential4WDataMCentreBias( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mCentreBias; } +inline void setPxVehicleDifferential4WDataMCentreBias( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mCentreBias = inData; } +inline PxReal getPxVehicleDifferential4WDataMFrontBias( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mFrontBias; } +inline void setPxVehicleDifferential4WDataMFrontBias( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mFrontBias = inData; } +inline PxReal getPxVehicleDifferential4WDataMRearBias( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mRearBias; } +inline void setPxVehicleDifferential4WDataMRearBias( PxVehicleDifferential4WData* inOwner, PxReal inData) { inOwner->mRearBias = inData; } +inline PxVehicleDifferential4WData::Enum getPxVehicleDifferential4WDataMType( const PxVehicleDifferential4WData* inOwner ) { return inOwner->mType; } +inline void setPxVehicleDifferential4WDataMType( PxVehicleDifferential4WData* inOwner, PxVehicleDifferential4WData::Enum inData) { inOwner->mType = inData; } + PxVehicleDifferential4WDataGeneratedInfo::PxVehicleDifferential4WDataGeneratedInfo() + : MFrontRearSplit( "MFrontRearSplit", setPxVehicleDifferential4WDataMFrontRearSplit, getPxVehicleDifferential4WDataMFrontRearSplit ) + , MFrontLeftRightSplit( "MFrontLeftRightSplit", setPxVehicleDifferential4WDataMFrontLeftRightSplit, getPxVehicleDifferential4WDataMFrontLeftRightSplit ) + , MRearLeftRightSplit( "MRearLeftRightSplit", setPxVehicleDifferential4WDataMRearLeftRightSplit, getPxVehicleDifferential4WDataMRearLeftRightSplit ) + , MCentreBias( "MCentreBias", setPxVehicleDifferential4WDataMCentreBias, getPxVehicleDifferential4WDataMCentreBias ) + , MFrontBias( "MFrontBias", setPxVehicleDifferential4WDataMFrontBias, getPxVehicleDifferential4WDataMFrontBias ) + , MRearBias( "MRearBias", setPxVehicleDifferential4WDataMRearBias, getPxVehicleDifferential4WDataMRearBias ) + , MType( "MType", setPxVehicleDifferential4WDataMType, getPxVehicleDifferential4WDataMType ) +{} + PxVehicleDifferential4WDataGeneratedValues::PxVehicleDifferential4WDataGeneratedValues( const PxVehicleDifferential4WData* inSource ) + :MFrontRearSplit( inSource->mFrontRearSplit ) + ,MFrontLeftRightSplit( inSource->mFrontLeftRightSplit ) + ,MRearLeftRightSplit( inSource->mRearLeftRightSplit ) + ,MCentreBias( inSource->mCentreBias ) + ,MFrontBias( inSource->mFrontBias ) + ,MRearBias( inSource->mRearBias ) + ,MType( inSource->mType ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleDifferentialNWData_DrivenWheelStatus( PxVehicleDifferentialNWData* inObj, PxU32 inArg){ inObj->setDrivenWheelStatus( inArg ); } +PxU32 getPxVehicleDifferentialNWData_DrivenWheelStatus( const PxVehicleDifferentialNWData* inObj ) { return inObj->getDrivenWheelStatus(); } + PxVehicleDifferentialNWDataGeneratedInfo::PxVehicleDifferentialNWDataGeneratedInfo() + : DrivenWheelStatus( "DrivenWheelStatus", setPxVehicleDifferentialNWData_DrivenWheelStatus, getPxVehicleDifferentialNWData_DrivenWheelStatus) +{} + PxVehicleDifferentialNWDataGeneratedValues::PxVehicleDifferentialNWDataGeneratedValues( const PxVehicleDifferentialNWData* inSource ) + :DrivenWheelStatus( getPxVehicleDifferentialNWData_DrivenWheelStatus( inSource ) ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxVehicleAckermannGeometryDataMAccuracy( const PxVehicleAckermannGeometryData* inOwner ) { return inOwner->mAccuracy; } +inline void setPxVehicleAckermannGeometryDataMAccuracy( PxVehicleAckermannGeometryData* inOwner, PxReal inData) { inOwner->mAccuracy = inData; } +inline PxReal getPxVehicleAckermannGeometryDataMFrontWidth( const PxVehicleAckermannGeometryData* inOwner ) { return inOwner->mFrontWidth; } +inline void setPxVehicleAckermannGeometryDataMFrontWidth( PxVehicleAckermannGeometryData* inOwner, PxReal inData) { inOwner->mFrontWidth = inData; } +inline PxReal getPxVehicleAckermannGeometryDataMRearWidth( const PxVehicleAckermannGeometryData* inOwner ) { return inOwner->mRearWidth; } +inline void setPxVehicleAckermannGeometryDataMRearWidth( PxVehicleAckermannGeometryData* inOwner, PxReal inData) { inOwner->mRearWidth = inData; } +inline PxReal getPxVehicleAckermannGeometryDataMAxleSeparation( const PxVehicleAckermannGeometryData* inOwner ) { return inOwner->mAxleSeparation; } +inline void setPxVehicleAckermannGeometryDataMAxleSeparation( PxVehicleAckermannGeometryData* inOwner, PxReal inData) { inOwner->mAxleSeparation = inData; } + PxVehicleAckermannGeometryDataGeneratedInfo::PxVehicleAckermannGeometryDataGeneratedInfo() + : MAccuracy( "MAccuracy", setPxVehicleAckermannGeometryDataMAccuracy, getPxVehicleAckermannGeometryDataMAccuracy ) + , MFrontWidth( "MFrontWidth", setPxVehicleAckermannGeometryDataMFrontWidth, getPxVehicleAckermannGeometryDataMFrontWidth ) + , MRearWidth( "MRearWidth", setPxVehicleAckermannGeometryDataMRearWidth, getPxVehicleAckermannGeometryDataMRearWidth ) + , MAxleSeparation( "MAxleSeparation", setPxVehicleAckermannGeometryDataMAxleSeparation, getPxVehicleAckermannGeometryDataMAxleSeparation ) +{} + PxVehicleAckermannGeometryDataGeneratedValues::PxVehicleAckermannGeometryDataGeneratedValues( const PxVehicleAckermannGeometryData* inSource ) + :MAccuracy( inSource->mAccuracy ) + ,MFrontWidth( inSource->mFrontWidth ) + ,MRearWidth( inSource->mRearWidth ) + ,MAxleSeparation( inSource->mAxleSeparation ) +{ + PX_UNUSED(inSource); +} +inline PxReal getPxVehicleClutchDataMStrength( const PxVehicleClutchData* inOwner ) { return inOwner->mStrength; } +inline void setPxVehicleClutchDataMStrength( PxVehicleClutchData* inOwner, PxReal inData) { inOwner->mStrength = inData; } +inline PxVehicleClutchAccuracyMode::Enum getPxVehicleClutchDataMAccuracyMode( const PxVehicleClutchData* inOwner ) { return inOwner->mAccuracyMode; } +inline void setPxVehicleClutchDataMAccuracyMode( PxVehicleClutchData* inOwner, PxVehicleClutchAccuracyMode::Enum inData) { inOwner->mAccuracyMode = inData; } +inline PxU32 getPxVehicleClutchDataMEstimateIterations( const PxVehicleClutchData* inOwner ) { return inOwner->mEstimateIterations; } +inline void setPxVehicleClutchDataMEstimateIterations( PxVehicleClutchData* inOwner, PxU32 inData) { inOwner->mEstimateIterations = inData; } + PxVehicleClutchDataGeneratedInfo::PxVehicleClutchDataGeneratedInfo() + : MStrength( "MStrength", setPxVehicleClutchDataMStrength, getPxVehicleClutchDataMStrength ) + , MAccuracyMode( "MAccuracyMode", setPxVehicleClutchDataMAccuracyMode, getPxVehicleClutchDataMAccuracyMode ) + , MEstimateIterations( "MEstimateIterations", setPxVehicleClutchDataMEstimateIterations, getPxVehicleClutchDataMEstimateIterations ) +{} + PxVehicleClutchDataGeneratedValues::PxVehicleClutchDataGeneratedValues( const PxVehicleClutchData* inSource ) + :MStrength( inSource->mStrength ) + ,MAccuracyMode( inSource->mAccuracyMode ) + ,MEstimateIterations( inSource->mEstimateIterations ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleTireLoadFilterData_Denominator( const PxVehicleTireLoadFilterData* inObj ) { return inObj->getDenominator(); } +inline PxReal getPxVehicleTireLoadFilterDataMMinNormalisedLoad( const PxVehicleTireLoadFilterData* inOwner ) { return inOwner->mMinNormalisedLoad; } +inline void setPxVehicleTireLoadFilterDataMMinNormalisedLoad( PxVehicleTireLoadFilterData* inOwner, PxReal inData) { inOwner->mMinNormalisedLoad = inData; } +inline PxReal getPxVehicleTireLoadFilterDataMMinFilteredNormalisedLoad( const PxVehicleTireLoadFilterData* inOwner ) { return inOwner->mMinFilteredNormalisedLoad; } +inline void setPxVehicleTireLoadFilterDataMMinFilteredNormalisedLoad( PxVehicleTireLoadFilterData* inOwner, PxReal inData) { inOwner->mMinFilteredNormalisedLoad = inData; } +inline PxReal getPxVehicleTireLoadFilterDataMMaxNormalisedLoad( const PxVehicleTireLoadFilterData* inOwner ) { return inOwner->mMaxNormalisedLoad; } +inline void setPxVehicleTireLoadFilterDataMMaxNormalisedLoad( PxVehicleTireLoadFilterData* inOwner, PxReal inData) { inOwner->mMaxNormalisedLoad = inData; } +inline PxReal getPxVehicleTireLoadFilterDataMMaxFilteredNormalisedLoad( const PxVehicleTireLoadFilterData* inOwner ) { return inOwner->mMaxFilteredNormalisedLoad; } +inline void setPxVehicleTireLoadFilterDataMMaxFilteredNormalisedLoad( PxVehicleTireLoadFilterData* inOwner, PxReal inData) { inOwner->mMaxFilteredNormalisedLoad = inData; } + PxVehicleTireLoadFilterDataGeneratedInfo::PxVehicleTireLoadFilterDataGeneratedInfo() + : Denominator( "Denominator", getPxVehicleTireLoadFilterData_Denominator) + , MMinNormalisedLoad( "MMinNormalisedLoad", setPxVehicleTireLoadFilterDataMMinNormalisedLoad, getPxVehicleTireLoadFilterDataMMinNormalisedLoad ) + , MMinFilteredNormalisedLoad( "MMinFilteredNormalisedLoad", setPxVehicleTireLoadFilterDataMMinFilteredNormalisedLoad, getPxVehicleTireLoadFilterDataMMinFilteredNormalisedLoad ) + , MMaxNormalisedLoad( "MMaxNormalisedLoad", setPxVehicleTireLoadFilterDataMMaxNormalisedLoad, getPxVehicleTireLoadFilterDataMMaxNormalisedLoad ) + , MMaxFilteredNormalisedLoad( "MMaxFilteredNormalisedLoad", setPxVehicleTireLoadFilterDataMMaxFilteredNormalisedLoad, getPxVehicleTireLoadFilterDataMMaxFilteredNormalisedLoad ) +{} + PxVehicleTireLoadFilterDataGeneratedValues::PxVehicleTireLoadFilterDataGeneratedValues( const PxVehicleTireLoadFilterData* inSource ) + :Denominator( getPxVehicleTireLoadFilterData_Denominator( inSource ) ) + ,MMinNormalisedLoad( inSource->mMinNormalisedLoad ) + ,MMinFilteredNormalisedLoad( inSource->mMinFilteredNormalisedLoad ) + ,MMaxNormalisedLoad( inSource->mMaxNormalisedLoad ) + ,MMaxFilteredNormalisedLoad( inSource->mMaxFilteredNormalisedLoad ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleWheelData_RecipRadius( const PxVehicleWheelData* inObj ) { return inObj->getRecipRadius(); } +PxReal getPxVehicleWheelData_RecipMOI( const PxVehicleWheelData* inObj ) { return inObj->getRecipMOI(); } +inline PxReal getPxVehicleWheelDataMRadius( const PxVehicleWheelData* inOwner ) { return inOwner->mRadius; } +inline void setPxVehicleWheelDataMRadius( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mRadius = inData; } +inline PxReal getPxVehicleWheelDataMWidth( const PxVehicleWheelData* inOwner ) { return inOwner->mWidth; } +inline void setPxVehicleWheelDataMWidth( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mWidth = inData; } +inline PxReal getPxVehicleWheelDataMMass( const PxVehicleWheelData* inOwner ) { return inOwner->mMass; } +inline void setPxVehicleWheelDataMMass( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mMass = inData; } +inline PxReal getPxVehicleWheelDataMMOI( const PxVehicleWheelData* inOwner ) { return inOwner->mMOI; } +inline void setPxVehicleWheelDataMMOI( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mMOI = inData; } +inline PxReal getPxVehicleWheelDataMDampingRate( const PxVehicleWheelData* inOwner ) { return inOwner->mDampingRate; } +inline void setPxVehicleWheelDataMDampingRate( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mDampingRate = inData; } +inline PxReal getPxVehicleWheelDataMMaxBrakeTorque( const PxVehicleWheelData* inOwner ) { return inOwner->mMaxBrakeTorque; } +inline void setPxVehicleWheelDataMMaxBrakeTorque( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mMaxBrakeTorque = inData; } +inline PxReal getPxVehicleWheelDataMMaxHandBrakeTorque( const PxVehicleWheelData* inOwner ) { return inOwner->mMaxHandBrakeTorque; } +inline void setPxVehicleWheelDataMMaxHandBrakeTorque( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mMaxHandBrakeTorque = inData; } +inline PxReal getPxVehicleWheelDataMMaxSteer( const PxVehicleWheelData* inOwner ) { return inOwner->mMaxSteer; } +inline void setPxVehicleWheelDataMMaxSteer( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mMaxSteer = inData; } +inline PxReal getPxVehicleWheelDataMToeAngle( const PxVehicleWheelData* inOwner ) { return inOwner->mToeAngle; } +inline void setPxVehicleWheelDataMToeAngle( PxVehicleWheelData* inOwner, PxReal inData) { inOwner->mToeAngle = inData; } + PxVehicleWheelDataGeneratedInfo::PxVehicleWheelDataGeneratedInfo() + : RecipRadius( "RecipRadius", getPxVehicleWheelData_RecipRadius) + , RecipMOI( "RecipMOI", getPxVehicleWheelData_RecipMOI) + , MRadius( "MRadius", setPxVehicleWheelDataMRadius, getPxVehicleWheelDataMRadius ) + , MWidth( "MWidth", setPxVehicleWheelDataMWidth, getPxVehicleWheelDataMWidth ) + , MMass( "MMass", setPxVehicleWheelDataMMass, getPxVehicleWheelDataMMass ) + , MMOI( "MMOI", setPxVehicleWheelDataMMOI, getPxVehicleWheelDataMMOI ) + , MDampingRate( "MDampingRate", setPxVehicleWheelDataMDampingRate, getPxVehicleWheelDataMDampingRate ) + , MMaxBrakeTorque( "MMaxBrakeTorque", setPxVehicleWheelDataMMaxBrakeTorque, getPxVehicleWheelDataMMaxBrakeTorque ) + , MMaxHandBrakeTorque( "MMaxHandBrakeTorque", setPxVehicleWheelDataMMaxHandBrakeTorque, getPxVehicleWheelDataMMaxHandBrakeTorque ) + , MMaxSteer( "MMaxSteer", setPxVehicleWheelDataMMaxSteer, getPxVehicleWheelDataMMaxSteer ) + , MToeAngle( "MToeAngle", setPxVehicleWheelDataMToeAngle, getPxVehicleWheelDataMToeAngle ) +{} + PxVehicleWheelDataGeneratedValues::PxVehicleWheelDataGeneratedValues( const PxVehicleWheelData* inSource ) + :RecipRadius( getPxVehicleWheelData_RecipRadius( inSource ) ) + ,RecipMOI( getPxVehicleWheelData_RecipMOI( inSource ) ) + ,MRadius( inSource->mRadius ) + ,MWidth( inSource->mWidth ) + ,MMass( inSource->mMass ) + ,MMOI( inSource->mMOI ) + ,MDampingRate( inSource->mDampingRate ) + ,MMaxBrakeTorque( inSource->mMaxBrakeTorque ) + ,MMaxHandBrakeTorque( inSource->mMaxHandBrakeTorque ) + ,MMaxSteer( inSource->mMaxSteer ) + ,MToeAngle( inSource->mToeAngle ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleSuspensionData_RecipMaxCompression( const PxVehicleSuspensionData* inObj ) { return inObj->getRecipMaxCompression(); } +PxReal getPxVehicleSuspensionData_RecipMaxDroop( const PxVehicleSuspensionData* inObj ) { return inObj->getRecipMaxDroop(); } +void setPxVehicleSuspensionData_MassAndPreserveNaturalFrequency( PxVehicleSuspensionData* inObj, const PxReal inArg){ inObj->setMassAndPreserveNaturalFrequency( inArg ); } +inline PxReal getPxVehicleSuspensionDataMSpringStrength( const PxVehicleSuspensionData* inOwner ) { return inOwner->mSpringStrength; } +inline void setPxVehicleSuspensionDataMSpringStrength( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mSpringStrength = inData; } +inline PxReal getPxVehicleSuspensionDataMSpringDamperRate( const PxVehicleSuspensionData* inOwner ) { return inOwner->mSpringDamperRate; } +inline void setPxVehicleSuspensionDataMSpringDamperRate( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mSpringDamperRate = inData; } +inline PxReal getPxVehicleSuspensionDataMMaxCompression( const PxVehicleSuspensionData* inOwner ) { return inOwner->mMaxCompression; } +inline void setPxVehicleSuspensionDataMMaxCompression( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mMaxCompression = inData; } +inline PxReal getPxVehicleSuspensionDataMMaxDroop( const PxVehicleSuspensionData* inOwner ) { return inOwner->mMaxDroop; } +inline void setPxVehicleSuspensionDataMMaxDroop( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mMaxDroop = inData; } +inline PxReal getPxVehicleSuspensionDataMSprungMass( const PxVehicleSuspensionData* inOwner ) { return inOwner->mSprungMass; } +inline void setPxVehicleSuspensionDataMSprungMass( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mSprungMass = inData; } +inline PxReal getPxVehicleSuspensionDataMCamberAtRest( const PxVehicleSuspensionData* inOwner ) { return inOwner->mCamberAtRest; } +inline void setPxVehicleSuspensionDataMCamberAtRest( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mCamberAtRest = inData; } +inline PxReal getPxVehicleSuspensionDataMCamberAtMaxCompression( const PxVehicleSuspensionData* inOwner ) { return inOwner->mCamberAtMaxCompression; } +inline void setPxVehicleSuspensionDataMCamberAtMaxCompression( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mCamberAtMaxCompression = inData; } +inline PxReal getPxVehicleSuspensionDataMCamberAtMaxDroop( const PxVehicleSuspensionData* inOwner ) { return inOwner->mCamberAtMaxDroop; } +inline void setPxVehicleSuspensionDataMCamberAtMaxDroop( PxVehicleSuspensionData* inOwner, PxReal inData) { inOwner->mCamberAtMaxDroop = inData; } + PxVehicleSuspensionDataGeneratedInfo::PxVehicleSuspensionDataGeneratedInfo() + : RecipMaxCompression( "RecipMaxCompression", getPxVehicleSuspensionData_RecipMaxCompression) + , RecipMaxDroop( "RecipMaxDroop", getPxVehicleSuspensionData_RecipMaxDroop) + , MassAndPreserveNaturalFrequency( "MassAndPreserveNaturalFrequency", setPxVehicleSuspensionData_MassAndPreserveNaturalFrequency) + , MSpringStrength( "MSpringStrength", setPxVehicleSuspensionDataMSpringStrength, getPxVehicleSuspensionDataMSpringStrength ) + , MSpringDamperRate( "MSpringDamperRate", setPxVehicleSuspensionDataMSpringDamperRate, getPxVehicleSuspensionDataMSpringDamperRate ) + , MMaxCompression( "MMaxCompression", setPxVehicleSuspensionDataMMaxCompression, getPxVehicleSuspensionDataMMaxCompression ) + , MMaxDroop( "MMaxDroop", setPxVehicleSuspensionDataMMaxDroop, getPxVehicleSuspensionDataMMaxDroop ) + , MSprungMass( "MSprungMass", setPxVehicleSuspensionDataMSprungMass, getPxVehicleSuspensionDataMSprungMass ) + , MCamberAtRest( "MCamberAtRest", setPxVehicleSuspensionDataMCamberAtRest, getPxVehicleSuspensionDataMCamberAtRest ) + , MCamberAtMaxCompression( "MCamberAtMaxCompression", setPxVehicleSuspensionDataMCamberAtMaxCompression, getPxVehicleSuspensionDataMCamberAtMaxCompression ) + , MCamberAtMaxDroop( "MCamberAtMaxDroop", setPxVehicleSuspensionDataMCamberAtMaxDroop, getPxVehicleSuspensionDataMCamberAtMaxDroop ) +{} + PxVehicleSuspensionDataGeneratedValues::PxVehicleSuspensionDataGeneratedValues( const PxVehicleSuspensionData* inSource ) + :RecipMaxCompression( getPxVehicleSuspensionData_RecipMaxCompression( inSource ) ) + ,RecipMaxDroop( getPxVehicleSuspensionData_RecipMaxDroop( inSource ) ) + ,MSpringStrength( inSource->mSpringStrength ) + ,MSpringDamperRate( inSource->mSpringDamperRate ) + ,MMaxCompression( inSource->mMaxCompression ) + ,MMaxDroop( inSource->mMaxDroop ) + ,MSprungMass( inSource->mSprungMass ) + ,MCamberAtRest( inSource->mCamberAtRest ) + ,MCamberAtMaxCompression( inSource->mCamberAtMaxCompression ) + ,MCamberAtMaxDroop( inSource->mCamberAtMaxDroop ) +{ + PX_UNUSED(inSource); +} +inline PxU32 getPxVehicleAntiRollBarDataMWheel0( const PxVehicleAntiRollBarData* inOwner ) { return inOwner->mWheel0; } +inline void setPxVehicleAntiRollBarDataMWheel0( PxVehicleAntiRollBarData* inOwner, PxU32 inData) { inOwner->mWheel0 = inData; } +inline PxU32 getPxVehicleAntiRollBarDataMWheel1( const PxVehicleAntiRollBarData* inOwner ) { return inOwner->mWheel1; } +inline void setPxVehicleAntiRollBarDataMWheel1( PxVehicleAntiRollBarData* inOwner, PxU32 inData) { inOwner->mWheel1 = inData; } +inline PxF32 getPxVehicleAntiRollBarDataMStiffness( const PxVehicleAntiRollBarData* inOwner ) { return inOwner->mStiffness; } +inline void setPxVehicleAntiRollBarDataMStiffness( PxVehicleAntiRollBarData* inOwner, PxF32 inData) { inOwner->mStiffness = inData; } + PxVehicleAntiRollBarDataGeneratedInfo::PxVehicleAntiRollBarDataGeneratedInfo() + : MWheel0( "MWheel0", setPxVehicleAntiRollBarDataMWheel0, getPxVehicleAntiRollBarDataMWheel0 ) + , MWheel1( "MWheel1", setPxVehicleAntiRollBarDataMWheel1, getPxVehicleAntiRollBarDataMWheel1 ) + , MStiffness( "MStiffness", setPxVehicleAntiRollBarDataMStiffness, getPxVehicleAntiRollBarDataMStiffness ) +{} + PxVehicleAntiRollBarDataGeneratedValues::PxVehicleAntiRollBarDataGeneratedValues( const PxVehicleAntiRollBarData* inSource ) + :MWheel0( inSource->mWheel0 ) + ,MWheel1( inSource->mWheel1 ) + ,MStiffness( inSource->mStiffness ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleTireData_RecipLongitudinalStiffnessPerUnitGravity( const PxVehicleTireData* inObj ) { return inObj->getRecipLongitudinalStiffnessPerUnitGravity(); } +PxReal getPxVehicleTireData_FrictionVsSlipGraphRecipx1Minusx0( const PxVehicleTireData* inObj ) { return inObj->getFrictionVsSlipGraphRecipx1Minusx0(); } +PxReal getPxVehicleTireData_FrictionVsSlipGraphRecipx2Minusx1( const PxVehicleTireData* inObj ) { return inObj->getFrictionVsSlipGraphRecipx2Minusx1(); } +inline PxReal getPxVehicleTireDataMLatStiffX( const PxVehicleTireData* inOwner ) { return inOwner->mLatStiffX; } +inline void setPxVehicleTireDataMLatStiffX( PxVehicleTireData* inOwner, PxReal inData) { inOwner->mLatStiffX = inData; } +inline PxReal getPxVehicleTireDataMLatStiffY( const PxVehicleTireData* inOwner ) { return inOwner->mLatStiffY; } +inline void setPxVehicleTireDataMLatStiffY( PxVehicleTireData* inOwner, PxReal inData) { inOwner->mLatStiffY = inData; } +inline PxReal getPxVehicleTireDataMLongitudinalStiffnessPerUnitGravity( const PxVehicleTireData* inOwner ) { return inOwner->mLongitudinalStiffnessPerUnitGravity; } +inline void setPxVehicleTireDataMLongitudinalStiffnessPerUnitGravity( PxVehicleTireData* inOwner, PxReal inData) { inOwner->mLongitudinalStiffnessPerUnitGravity = inData; } +inline PxReal getPxVehicleTireDataMCamberStiffnessPerUnitGravity( const PxVehicleTireData* inOwner ) { return inOwner->mCamberStiffnessPerUnitGravity; } +inline void setPxVehicleTireDataMCamberStiffnessPerUnitGravity( PxVehicleTireData* inOwner, PxReal inData) { inOwner->mCamberStiffnessPerUnitGravity = inData; } +inline PxU32 getPxVehicleTireDataMType( const PxVehicleTireData* inOwner ) { return inOwner->mType; } +inline void setPxVehicleTireDataMType( PxVehicleTireData* inOwner, PxU32 inData) { inOwner->mType = inData; } + PxVehicleTireDataGeneratedInfo::PxVehicleTireDataGeneratedInfo() + : RecipLongitudinalStiffnessPerUnitGravity( "RecipLongitudinalStiffnessPerUnitGravity", getPxVehicleTireData_RecipLongitudinalStiffnessPerUnitGravity) + , FrictionVsSlipGraphRecipx1Minusx0( "FrictionVsSlipGraphRecipx1Minusx0", getPxVehicleTireData_FrictionVsSlipGraphRecipx1Minusx0) + , FrictionVsSlipGraphRecipx2Minusx1( "FrictionVsSlipGraphRecipx2Minusx1", getPxVehicleTireData_FrictionVsSlipGraphRecipx2Minusx1) + , MLatStiffX( "MLatStiffX", setPxVehicleTireDataMLatStiffX, getPxVehicleTireDataMLatStiffX ) + , MLatStiffY( "MLatStiffY", setPxVehicleTireDataMLatStiffY, getPxVehicleTireDataMLatStiffY ) + , MLongitudinalStiffnessPerUnitGravity( "MLongitudinalStiffnessPerUnitGravity", setPxVehicleTireDataMLongitudinalStiffnessPerUnitGravity, getPxVehicleTireDataMLongitudinalStiffnessPerUnitGravity ) + , MCamberStiffnessPerUnitGravity( "MCamberStiffnessPerUnitGravity", setPxVehicleTireDataMCamberStiffnessPerUnitGravity, getPxVehicleTireDataMCamberStiffnessPerUnitGravity ) + , MType( "MType", setPxVehicleTireDataMType, getPxVehicleTireDataMType ) +{} + PxVehicleTireDataGeneratedValues::PxVehicleTireDataGeneratedValues( const PxVehicleTireData* inSource ) + :RecipLongitudinalStiffnessPerUnitGravity( getPxVehicleTireData_RecipLongitudinalStiffnessPerUnitGravity( inSource ) ) + ,FrictionVsSlipGraphRecipx1Minusx0( getPxVehicleTireData_FrictionVsSlipGraphRecipx1Minusx0( inSource ) ) + ,FrictionVsSlipGraphRecipx2Minusx1( getPxVehicleTireData_FrictionVsSlipGraphRecipx2Minusx1( inSource ) ) + ,MLatStiffX( inSource->mLatStiffX ) + ,MLatStiffY( inSource->mLatStiffY ) + ,MLongitudinalStiffnessPerUnitGravity( inSource->mLongitudinalStiffnessPerUnitGravity ) + ,MCamberStiffnessPerUnitGravity( inSource->mCamberStiffnessPerUnitGravity ) + ,MType( inSource->mType ) +{ + PX_UNUSED(inSource); + PxMemCopy( MFrictionVsSlipGraph, inSource->mFrictionVsSlipGraph, sizeof( MFrictionVsSlipGraph ) ); +} +const PxReal * getPxVehicleWheels4SimData_TireRestLoadsArray( const PxVehicleWheels4SimData* inObj ) { return inObj->getTireRestLoadsArray(); } +const PxReal * getPxVehicleWheels4SimData_RecipTireRestLoadsArray( const PxVehicleWheels4SimData* inObj ) { return inObj->getRecipTireRestLoadsArray(); } + PxVehicleWheels4SimDataGeneratedInfo::PxVehicleWheels4SimDataGeneratedInfo() + : TireRestLoadsArray( "TireRestLoadsArray", getPxVehicleWheels4SimData_TireRestLoadsArray) + , RecipTireRestLoadsArray( "RecipTireRestLoadsArray", getPxVehicleWheels4SimData_RecipTireRestLoadsArray) +{} + PxVehicleWheels4SimDataGeneratedValues::PxVehicleWheels4SimDataGeneratedValues( const PxVehicleWheels4SimData* inSource ) + :TireRestLoadsArray( getPxVehicleWheels4SimData_TireRestLoadsArray( inSource ) ) + ,RecipTireRestLoadsArray( getPxVehicleWheels4SimData_RecipTireRestLoadsArray( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleWheelsSimData_ChassisMass( PxVehicleWheelsSimData* inObj, const PxF32 inArg){ inObj->setChassisMass( inArg ); } +PxVehicleSuspensionData getPxVehicleWheelsSimData_SuspensionData( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getSuspensionData( index ); } +PxU32 getNbPxVehicleWheelsSimData_SuspensionData( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbSuspensionData( ); } +void setPxVehicleWheelsSimData_SuspensionData( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVehicleSuspensionData inValue ){ inObj->setSuspensionData( inIndex, inValue ); } +PxVehicleWheelData getPxVehicleWheelsSimData_WheelData( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getWheelData( index ); } +PxU32 getNbPxVehicleWheelsSimData_WheelData( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbWheelData( ); } +void setPxVehicleWheelsSimData_WheelData( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVehicleWheelData inValue ){ inObj->setWheelData( inIndex, inValue ); } +PxVehicleTireData getPxVehicleWheelsSimData_TireData( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getTireData( index ); } +PxU32 getNbPxVehicleWheelsSimData_TireData( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbTireData( ); } +void setPxVehicleWheelsSimData_TireData( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVehicleTireData inValue ){ inObj->setTireData( inIndex, inValue ); } +PxVec3 getPxVehicleWheelsSimData_SuspTravelDirection( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getSuspTravelDirection( index ); } +PxU32 getNbPxVehicleWheelsSimData_SuspTravelDirection( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbSuspTravelDirection( ); } +void setPxVehicleWheelsSimData_SuspTravelDirection( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVec3 inValue ){ inObj->setSuspTravelDirection( inIndex, inValue ); } +PxVec3 getPxVehicleWheelsSimData_SuspForceAppPointOffset( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getSuspForceAppPointOffset( index ); } +PxU32 getNbPxVehicleWheelsSimData_SuspForceAppPointOffset( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbSuspForceAppPointOffset( ); } +void setPxVehicleWheelsSimData_SuspForceAppPointOffset( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVec3 inValue ){ inObj->setSuspForceAppPointOffset( inIndex, inValue ); } +PxVec3 getPxVehicleWheelsSimData_TireForceAppPointOffset( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getTireForceAppPointOffset( index ); } +PxU32 getNbPxVehicleWheelsSimData_TireForceAppPointOffset( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbTireForceAppPointOffset( ); } +void setPxVehicleWheelsSimData_TireForceAppPointOffset( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVec3 inValue ){ inObj->setTireForceAppPointOffset( inIndex, inValue ); } +PxVec3 getPxVehicleWheelsSimData_WheelCentreOffset( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getWheelCentreOffset( index ); } +PxU32 getNbPxVehicleWheelsSimData_WheelCentreOffset( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbWheelCentreOffset( ); } +void setPxVehicleWheelsSimData_WheelCentreOffset( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVec3 inValue ){ inObj->setWheelCentreOffset( inIndex, inValue ); } +PxI32 getPxVehicleWheelsSimData_WheelShapeMapping( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getWheelShapeMapping( index ); } +PxU32 getNbPxVehicleWheelsSimData_WheelShapeMapping( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbWheelShapeMapping( ); } +void setPxVehicleWheelsSimData_WheelShapeMapping( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxI32 inValue ){ inObj->setWheelShapeMapping( inIndex, inValue ); } +PxFilterData getPxVehicleWheelsSimData_SceneQueryFilterData( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getSceneQueryFilterData( index ); } +PxU32 getNbPxVehicleWheelsSimData_SceneQueryFilterData( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbSceneQueryFilterData( ); } +void setPxVehicleWheelsSimData_SceneQueryFilterData( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxFilterData inValue ){ inObj->setSceneQueryFilterData( inIndex, inValue ); } +PxVehicleAntiRollBarData getPxVehicleWheelsSimData_AntiRollBarData( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getAntiRollBarData( index ); } +PxU32 getNbPxVehicleWheelsSimData_AntiRollBarData( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbAntiRollBarData( ); } +void setPxVehicleWheelsSimData_AntiRollBarData( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, PxVehicleAntiRollBarData inValue ){ inObj->setAntiRollBarData( inIndex, inValue ); } +void setPxVehicleWheelsSimData_TireLoadFilterData( PxVehicleWheelsSimData* inObj, const PxVehicleTireLoadFilterData & inArg){ inObj->setTireLoadFilterData( inArg ); } +PxVehicleTireLoadFilterData getPxVehicleWheelsSimData_TireLoadFilterData( const PxVehicleWheelsSimData* inObj ) { return inObj->getTireLoadFilterData(); } +void setPxVehicleWheelsSimData_MinLongSlipDenominator( PxVehicleWheelsSimData* inObj, const PxReal inArg){ inObj->setMinLongSlipDenominator( inArg ); } +PxF32 getPxVehicleWheelsSimData_MinLongSlipDenominator( const PxVehicleWheelsSimData* inObj ) { return inObj->getMinLongSlipDenominator(); } +void setPxVehicleWheelsSimData_ThresholdLongSpeed( PxVehicleWheelsSimData* inObj, const PxF32 inArg){ inObj->setThresholdLongSpeed( inArg ); } +PxF32 getPxVehicleWheelsSimData_ThresholdLongSpeed( const PxVehicleWheelsSimData* inObj ) { return inObj->getThresholdLongSpeed(); } +void setPxVehicleWheelsSimData_LowForwardSpeedSubStepCount( PxVehicleWheelsSimData* inObj, const PxU32 inArg){ inObj->setLowForwardSpeedSubStepCount( inArg ); } +PxU32 getPxVehicleWheelsSimData_LowForwardSpeedSubStepCount( const PxVehicleWheelsSimData* inObj ) { return inObj->getLowForwardSpeedSubStepCount(); } +void setPxVehicleWheelsSimData_HighForwardSpeedSubStepCount( PxVehicleWheelsSimData* inObj, const PxU32 inArg){ inObj->setHighForwardSpeedSubStepCount( inArg ); } +PxU32 getPxVehicleWheelsSimData_HighForwardSpeedSubStepCount( const PxVehicleWheelsSimData* inObj ) { return inObj->getHighForwardSpeedSubStepCount(); } +_Bool getPxVehicleWheelsSimData_WheelEnabledState( const PxVehicleWheelsSimData* inObj, const PxU32 index ) { return inObj->getWheelEnabledState( index ); } +PxU32 getNbPxVehicleWheelsSimData_WheelEnabledState( const PxVehicleWheelsSimData* inObj ) { return inObj->getNbWheelEnabledState( ); } +void setPxVehicleWheelsSimData_WheelEnabledState( PxVehicleWheelsSimData* inObj, const PxU32 inIndex, _Bool inValue ){ inObj->setWheelEnabledState( inIndex, inValue ); } + PxVehicleWheelsSimDataGeneratedInfo::PxVehicleWheelsSimDataGeneratedInfo() + : ChassisMass( "ChassisMass", setPxVehicleWheelsSimData_ChassisMass) + , SuspensionData( "SuspensionData", getPxVehicleWheelsSimData_SuspensionData, getNbPxVehicleWheelsSimData_SuspensionData, setPxVehicleWheelsSimData_SuspensionData ) + , WheelData( "WheelData", getPxVehicleWheelsSimData_WheelData, getNbPxVehicleWheelsSimData_WheelData, setPxVehicleWheelsSimData_WheelData ) + , TireData( "TireData", getPxVehicleWheelsSimData_TireData, getNbPxVehicleWheelsSimData_TireData, setPxVehicleWheelsSimData_TireData ) + , SuspTravelDirection( "SuspTravelDirection", getPxVehicleWheelsSimData_SuspTravelDirection, getNbPxVehicleWheelsSimData_SuspTravelDirection, setPxVehicleWheelsSimData_SuspTravelDirection ) + , SuspForceAppPointOffset( "SuspForceAppPointOffset", getPxVehicleWheelsSimData_SuspForceAppPointOffset, getNbPxVehicleWheelsSimData_SuspForceAppPointOffset, setPxVehicleWheelsSimData_SuspForceAppPointOffset ) + , TireForceAppPointOffset( "TireForceAppPointOffset", getPxVehicleWheelsSimData_TireForceAppPointOffset, getNbPxVehicleWheelsSimData_TireForceAppPointOffset, setPxVehicleWheelsSimData_TireForceAppPointOffset ) + , WheelCentreOffset( "WheelCentreOffset", getPxVehicleWheelsSimData_WheelCentreOffset, getNbPxVehicleWheelsSimData_WheelCentreOffset, setPxVehicleWheelsSimData_WheelCentreOffset ) + , WheelShapeMapping( "WheelShapeMapping", getPxVehicleWheelsSimData_WheelShapeMapping, getNbPxVehicleWheelsSimData_WheelShapeMapping, setPxVehicleWheelsSimData_WheelShapeMapping ) + , SceneQueryFilterData( "SceneQueryFilterData", getPxVehicleWheelsSimData_SceneQueryFilterData, getNbPxVehicleWheelsSimData_SceneQueryFilterData, setPxVehicleWheelsSimData_SceneQueryFilterData ) + , AntiRollBarData( "AntiRollBarData", getPxVehicleWheelsSimData_AntiRollBarData, getNbPxVehicleWheelsSimData_AntiRollBarData, setPxVehicleWheelsSimData_AntiRollBarData ) + , TireLoadFilterData( "TireLoadFilterData", setPxVehicleWheelsSimData_TireLoadFilterData, getPxVehicleWheelsSimData_TireLoadFilterData) + , MinLongSlipDenominator( "MinLongSlipDenominator", setPxVehicleWheelsSimData_MinLongSlipDenominator, getPxVehicleWheelsSimData_MinLongSlipDenominator) + , ThresholdLongSpeed( "ThresholdLongSpeed", setPxVehicleWheelsSimData_ThresholdLongSpeed, getPxVehicleWheelsSimData_ThresholdLongSpeed) + , LowForwardSpeedSubStepCount( "LowForwardSpeedSubStepCount", setPxVehicleWheelsSimData_LowForwardSpeedSubStepCount, getPxVehicleWheelsSimData_LowForwardSpeedSubStepCount) + , HighForwardSpeedSubStepCount( "HighForwardSpeedSubStepCount", setPxVehicleWheelsSimData_HighForwardSpeedSubStepCount, getPxVehicleWheelsSimData_HighForwardSpeedSubStepCount) + , WheelEnabledState( "WheelEnabledState", getPxVehicleWheelsSimData_WheelEnabledState, getNbPxVehicleWheelsSimData_WheelEnabledState, setPxVehicleWheelsSimData_WheelEnabledState ) +{} + PxVehicleWheelsSimDataGeneratedValues::PxVehicleWheelsSimDataGeneratedValues( const PxVehicleWheelsSimData* inSource ) + :TireLoadFilterData( getPxVehicleWheelsSimData_TireLoadFilterData( inSource ) ) + ,MinLongSlipDenominator( getPxVehicleWheelsSimData_MinLongSlipDenominator( inSource ) ) + ,ThresholdLongSpeed( getPxVehicleWheelsSimData_ThresholdLongSpeed( inSource ) ) + ,LowForwardSpeedSubStepCount( getPxVehicleWheelsSimData_LowForwardSpeedSubStepCount( inSource ) ) + ,HighForwardSpeedSubStepCount( getPxVehicleWheelsSimData_HighForwardSpeedSubStepCount( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleWheelsDynData_TireForceShaderFunction( PxVehicleWheelsDynData* inObj, PxVehicleComputeTireForce inArg){ inObj->setTireForceShaderFunction( inArg ); } +PxReal getPxVehicleWheelsDynData_WheelRotationSpeed( const PxVehicleWheelsDynData* inObj, const PxU32 index ) { return inObj->getWheelRotationSpeed( index ); } +PxU32 getNbPxVehicleWheelsDynData_WheelRotationSpeed( const PxVehicleWheelsDynData* inObj ) { return inObj->getNbWheelRotationSpeed( ); } +void setPxVehicleWheelsDynData_WheelRotationSpeed( PxVehicleWheelsDynData* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setWheelRotationSpeed( inIndex, inValue ); } +PxReal getPxVehicleWheelsDynData_WheelRotationAngle( const PxVehicleWheelsDynData* inObj, const PxU32 index ) { return inObj->getWheelRotationAngle( index ); } +PxU32 getNbPxVehicleWheelsDynData_WheelRotationAngle( const PxVehicleWheelsDynData* inObj ) { return inObj->getNbWheelRotationAngle( ); } +void setPxVehicleWheelsDynData_WheelRotationAngle( PxVehicleWheelsDynData* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setWheelRotationAngle( inIndex, inValue ); } +PxVehicleWheels4DynData * getPxVehicleWheelsDynData_Wheel4DynData( const PxVehicleWheelsDynData* inObj ) { return inObj->getWheel4DynData(); } + PxVehicleWheelsDynDataGeneratedInfo::PxVehicleWheelsDynDataGeneratedInfo() + : TireForceShaderFunction( "TireForceShaderFunction", setPxVehicleWheelsDynData_TireForceShaderFunction) + , WheelRotationSpeed( "WheelRotationSpeed", getPxVehicleWheelsDynData_WheelRotationSpeed, getNbPxVehicleWheelsDynData_WheelRotationSpeed, setPxVehicleWheelsDynData_WheelRotationSpeed ) + , WheelRotationAngle( "WheelRotationAngle", getPxVehicleWheelsDynData_WheelRotationAngle, getNbPxVehicleWheelsDynData_WheelRotationAngle, setPxVehicleWheelsDynData_WheelRotationAngle ) + , Wheel4DynData( "Wheel4DynData", getPxVehicleWheelsDynData_Wheel4DynData) +{} + PxVehicleWheelsDynDataGeneratedValues::PxVehicleWheelsDynDataGeneratedValues( const PxVehicleWheelsDynData* inSource ) + :Wheel4DynData( getPxVehicleWheelsDynData_Wheel4DynData( inSource ) ) +{ + PX_UNUSED(inSource); +} +PxU32 getPxVehicleWheels_VehicleType( const PxVehicleWheels* inObj ) { return inObj->getVehicleType(); } +const PxRigidDynamic * getPxVehicleWheels_RigidDynamicActor( const PxVehicleWheels* inObj ) { return inObj->getRigidDynamicActor(); } +const char * getPxVehicleWheels_ConcreteTypeName( const PxVehicleWheels* inObj ) { return inObj->getConcreteTypeName(); } +inline PxVehicleWheelsSimData getPxVehicleWheelsMWheelsSimData( const PxVehicleWheels* inOwner ) { return inOwner->mWheelsSimData; } +inline void setPxVehicleWheelsMWheelsSimData( PxVehicleWheels* inOwner, PxVehicleWheelsSimData inData) { inOwner->mWheelsSimData = inData; } +inline PxVehicleWheelsDynData getPxVehicleWheelsMWheelsDynData( const PxVehicleWheels* inOwner ) { return inOwner->mWheelsDynData; } +inline void setPxVehicleWheelsMWheelsDynData( PxVehicleWheels* inOwner, PxVehicleWheelsDynData inData) { inOwner->mWheelsDynData = inData; } + PxVehicleWheelsGeneratedInfo::PxVehicleWheelsGeneratedInfo() + : VehicleType( "VehicleType", getPxVehicleWheels_VehicleType) + , RigidDynamicActor( "RigidDynamicActor", getPxVehicleWheels_RigidDynamicActor) + , ConcreteTypeName( "ConcreteTypeName", getPxVehicleWheels_ConcreteTypeName) + , MWheelsSimData( "MWheelsSimData", setPxVehicleWheelsMWheelsSimData, getPxVehicleWheelsMWheelsSimData ) + , MWheelsDynData( "MWheelsDynData", setPxVehicleWheelsMWheelsDynData, getPxVehicleWheelsMWheelsDynData ) +{} + PxVehicleWheelsGeneratedValues::PxVehicleWheelsGeneratedValues( const PxVehicleWheels* inSource ) + :VehicleType( getPxVehicleWheels_VehicleType( inSource ) ) + ,RigidDynamicActor( getPxVehicleWheels_RigidDynamicActor( inSource ) ) + ,ConcreteTypeName( getPxVehicleWheels_ConcreteTypeName( inSource ) ) + ,MWheelsSimData( inSource->mWheelsSimData ) + ,MWheelsDynData( inSource->mWheelsDynData ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleDriveDynData_AnalogInput( const PxVehicleDriveDynData* inObj, const PxU32 index ) { return inObj->getAnalogInput( index ); } +PxU32 getNbPxVehicleDriveDynData_AnalogInput( const PxVehicleDriveDynData* inObj ) { return inObj->getNbAnalogInput( ); } +void setPxVehicleDriveDynData_AnalogInput( PxVehicleDriveDynData* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setAnalogInput( inIndex, inValue ); } +void setPxVehicleDriveDynData_GearUp( PxVehicleDriveDynData* inObj, const _Bool inArg){ inObj->setGearUp( inArg ); } +_Bool getPxVehicleDriveDynData_GearUp( const PxVehicleDriveDynData* inObj ) { return inObj->getGearUp(); } +void setPxVehicleDriveDynData_GearDown( PxVehicleDriveDynData* inObj, const _Bool inArg){ inObj->setGearDown( inArg ); } +_Bool getPxVehicleDriveDynData_GearDown( const PxVehicleDriveDynData* inObj ) { return inObj->getGearDown(); } +void setPxVehicleDriveDynData_UseAutoGears( PxVehicleDriveDynData* inObj, const _Bool inArg){ inObj->setUseAutoGears( inArg ); } +_Bool getPxVehicleDriveDynData_UseAutoGears( const PxVehicleDriveDynData* inObj ) { return inObj->getUseAutoGears(); } +void setPxVehicleDriveDynData_CurrentGear( PxVehicleDriveDynData* inObj, PxU32 inArg){ inObj->setCurrentGear( inArg ); } +PxU32 getPxVehicleDriveDynData_CurrentGear( const PxVehicleDriveDynData* inObj ) { return inObj->getCurrentGear(); } +void setPxVehicleDriveDynData_TargetGear( PxVehicleDriveDynData* inObj, PxU32 inArg){ inObj->setTargetGear( inArg ); } +PxU32 getPxVehicleDriveDynData_TargetGear( const PxVehicleDriveDynData* inObj ) { return inObj->getTargetGear(); } +void setPxVehicleDriveDynData_EngineRotationSpeed( PxVehicleDriveDynData* inObj, const PxF32 inArg){ inObj->setEngineRotationSpeed( inArg ); } +PxReal getPxVehicleDriveDynData_EngineRotationSpeed( const PxVehicleDriveDynData* inObj ) { return inObj->getEngineRotationSpeed(); } +void setPxVehicleDriveDynData_GearChange( PxVehicleDriveDynData* inObj, const PxU32 inArg){ inObj->setGearChange( inArg ); } +PxU32 getPxVehicleDriveDynData_GearChange( const PxVehicleDriveDynData* inObj ) { return inObj->getGearChange(); } +void setPxVehicleDriveDynData_GearSwitchTime( PxVehicleDriveDynData* inObj, const PxReal inArg){ inObj->setGearSwitchTime( inArg ); } +PxReal getPxVehicleDriveDynData_GearSwitchTime( const PxVehicleDriveDynData* inObj ) { return inObj->getGearSwitchTime(); } +void setPxVehicleDriveDynData_AutoBoxSwitchTime( PxVehicleDriveDynData* inObj, const PxReal inArg){ inObj->setAutoBoxSwitchTime( inArg ); } +PxReal getPxVehicleDriveDynData_AutoBoxSwitchTime( const PxVehicleDriveDynData* inObj ) { return inObj->getAutoBoxSwitchTime(); } +inline _Bool getPxVehicleDriveDynDataMUseAutoGears( const PxVehicleDriveDynData* inOwner ) { return inOwner->mUseAutoGears; } +inline void setPxVehicleDriveDynDataMUseAutoGears( PxVehicleDriveDynData* inOwner, _Bool inData) { inOwner->mUseAutoGears = inData; } +inline _Bool getPxVehicleDriveDynDataMGearUpPressed( const PxVehicleDriveDynData* inOwner ) { return inOwner->mGearUpPressed; } +inline void setPxVehicleDriveDynDataMGearUpPressed( PxVehicleDriveDynData* inOwner, _Bool inData) { inOwner->mGearUpPressed = inData; } +inline _Bool getPxVehicleDriveDynDataMGearDownPressed( const PxVehicleDriveDynData* inOwner ) { return inOwner->mGearDownPressed; } +inline void setPxVehicleDriveDynDataMGearDownPressed( PxVehicleDriveDynData* inOwner, _Bool inData) { inOwner->mGearDownPressed = inData; } +inline PxU32 getPxVehicleDriveDynDataMCurrentGear( const PxVehicleDriveDynData* inOwner ) { return inOwner->mCurrentGear; } +inline void setPxVehicleDriveDynDataMCurrentGear( PxVehicleDriveDynData* inOwner, PxU32 inData) { inOwner->mCurrentGear = inData; } +inline PxU32 getPxVehicleDriveDynDataMTargetGear( const PxVehicleDriveDynData* inOwner ) { return inOwner->mTargetGear; } +inline void setPxVehicleDriveDynDataMTargetGear( PxVehicleDriveDynData* inOwner, PxU32 inData) { inOwner->mTargetGear = inData; } +inline PxReal getPxVehicleDriveDynDataMEnginespeed( const PxVehicleDriveDynData* inOwner ) { return inOwner->mEnginespeed; } +inline void setPxVehicleDriveDynDataMEnginespeed( PxVehicleDriveDynData* inOwner, PxReal inData) { inOwner->mEnginespeed = inData; } +inline PxReal getPxVehicleDriveDynDataMGearSwitchTime( const PxVehicleDriveDynData* inOwner ) { return inOwner->mGearSwitchTime; } +inline void setPxVehicleDriveDynDataMGearSwitchTime( PxVehicleDriveDynData* inOwner, PxReal inData) { inOwner->mGearSwitchTime = inData; } +inline PxReal getPxVehicleDriveDynDataMAutoBoxSwitchTime( const PxVehicleDriveDynData* inOwner ) { return inOwner->mAutoBoxSwitchTime; } +inline void setPxVehicleDriveDynDataMAutoBoxSwitchTime( PxVehicleDriveDynData* inOwner, PxReal inData) { inOwner->mAutoBoxSwitchTime = inData; } + PxVehicleDriveDynDataGeneratedInfo::PxVehicleDriveDynDataGeneratedInfo() + : AnalogInput( "AnalogInput", getPxVehicleDriveDynData_AnalogInput, getNbPxVehicleDriveDynData_AnalogInput, setPxVehicleDriveDynData_AnalogInput ) + , GearUp( "GearUp", setPxVehicleDriveDynData_GearUp, getPxVehicleDriveDynData_GearUp) + , GearDown( "GearDown", setPxVehicleDriveDynData_GearDown, getPxVehicleDriveDynData_GearDown) + , UseAutoGears( "UseAutoGears", setPxVehicleDriveDynData_UseAutoGears, getPxVehicleDriveDynData_UseAutoGears) + , CurrentGear( "CurrentGear", setPxVehicleDriveDynData_CurrentGear, getPxVehicleDriveDynData_CurrentGear) + , TargetGear( "TargetGear", setPxVehicleDriveDynData_TargetGear, getPxVehicleDriveDynData_TargetGear) + , EngineRotationSpeed( "EngineRotationSpeed", setPxVehicleDriveDynData_EngineRotationSpeed, getPxVehicleDriveDynData_EngineRotationSpeed) + , GearChange( "GearChange", setPxVehicleDriveDynData_GearChange, getPxVehicleDriveDynData_GearChange) + , GearSwitchTime( "GearSwitchTime", setPxVehicleDriveDynData_GearSwitchTime, getPxVehicleDriveDynData_GearSwitchTime) + , AutoBoxSwitchTime( "AutoBoxSwitchTime", setPxVehicleDriveDynData_AutoBoxSwitchTime, getPxVehicleDriveDynData_AutoBoxSwitchTime) + , MUseAutoGears( "MUseAutoGears", setPxVehicleDriveDynDataMUseAutoGears, getPxVehicleDriveDynDataMUseAutoGears ) + , MGearUpPressed( "MGearUpPressed", setPxVehicleDriveDynDataMGearUpPressed, getPxVehicleDriveDynDataMGearUpPressed ) + , MGearDownPressed( "MGearDownPressed", setPxVehicleDriveDynDataMGearDownPressed, getPxVehicleDriveDynDataMGearDownPressed ) + , MCurrentGear( "MCurrentGear", setPxVehicleDriveDynDataMCurrentGear, getPxVehicleDriveDynDataMCurrentGear ) + , MTargetGear( "MTargetGear", setPxVehicleDriveDynDataMTargetGear, getPxVehicleDriveDynDataMTargetGear ) + , MEnginespeed( "MEnginespeed", setPxVehicleDriveDynDataMEnginespeed, getPxVehicleDriveDynDataMEnginespeed ) + , MGearSwitchTime( "MGearSwitchTime", setPxVehicleDriveDynDataMGearSwitchTime, getPxVehicleDriveDynDataMGearSwitchTime ) + , MAutoBoxSwitchTime( "MAutoBoxSwitchTime", setPxVehicleDriveDynDataMAutoBoxSwitchTime, getPxVehicleDriveDynDataMAutoBoxSwitchTime ) +{} + PxVehicleDriveDynDataGeneratedValues::PxVehicleDriveDynDataGeneratedValues( const PxVehicleDriveDynData* inSource ) + :GearUp( getPxVehicleDriveDynData_GearUp( inSource ) ) + ,GearDown( getPxVehicleDriveDynData_GearDown( inSource ) ) + ,UseAutoGears( getPxVehicleDriveDynData_UseAutoGears( inSource ) ) + ,CurrentGear( getPxVehicleDriveDynData_CurrentGear( inSource ) ) + ,TargetGear( getPxVehicleDriveDynData_TargetGear( inSource ) ) + ,EngineRotationSpeed( getPxVehicleDriveDynData_EngineRotationSpeed( inSource ) ) + ,GearChange( getPxVehicleDriveDynData_GearChange( inSource ) ) + ,GearSwitchTime( getPxVehicleDriveDynData_GearSwitchTime( inSource ) ) + ,AutoBoxSwitchTime( getPxVehicleDriveDynData_AutoBoxSwitchTime( inSource ) ) + ,MUseAutoGears( inSource->mUseAutoGears ) + ,MGearUpPressed( inSource->mGearUpPressed ) + ,MGearDownPressed( inSource->mGearDownPressed ) + ,MCurrentGear( inSource->mCurrentGear ) + ,MTargetGear( inSource->mTargetGear ) + ,MEnginespeed( inSource->mEnginespeed ) + ,MGearSwitchTime( inSource->mGearSwitchTime ) + ,MAutoBoxSwitchTime( inSource->mAutoBoxSwitchTime ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleDriveSimData_EngineData( PxVehicleDriveSimData* inObj, const PxVehicleEngineData & inArg){ inObj->setEngineData( inArg ); } +PxVehicleEngineData getPxVehicleDriveSimData_EngineData( const PxVehicleDriveSimData* inObj ) { return inObj->getEngineData(); } +void setPxVehicleDriveSimData_GearsData( PxVehicleDriveSimData* inObj, const PxVehicleGearsData & inArg){ inObj->setGearsData( inArg ); } +PxVehicleGearsData getPxVehicleDriveSimData_GearsData( const PxVehicleDriveSimData* inObj ) { return inObj->getGearsData(); } +void setPxVehicleDriveSimData_ClutchData( PxVehicleDriveSimData* inObj, const PxVehicleClutchData & inArg){ inObj->setClutchData( inArg ); } +PxVehicleClutchData getPxVehicleDriveSimData_ClutchData( const PxVehicleDriveSimData* inObj ) { return inObj->getClutchData(); } +void setPxVehicleDriveSimData_AutoBoxData( PxVehicleDriveSimData* inObj, const PxVehicleAutoBoxData & inArg){ inObj->setAutoBoxData( inArg ); } +PxVehicleAutoBoxData getPxVehicleDriveSimData_AutoBoxData( const PxVehicleDriveSimData* inObj ) { return inObj->getAutoBoxData(); } + PxVehicleDriveSimDataGeneratedInfo::PxVehicleDriveSimDataGeneratedInfo() + : EngineData( "EngineData", setPxVehicleDriveSimData_EngineData, getPxVehicleDriveSimData_EngineData) + , GearsData( "GearsData", setPxVehicleDriveSimData_GearsData, getPxVehicleDriveSimData_GearsData) + , ClutchData( "ClutchData", setPxVehicleDriveSimData_ClutchData, getPxVehicleDriveSimData_ClutchData) + , AutoBoxData( "AutoBoxData", setPxVehicleDriveSimData_AutoBoxData, getPxVehicleDriveSimData_AutoBoxData) +{} + PxVehicleDriveSimDataGeneratedValues::PxVehicleDriveSimDataGeneratedValues( const PxVehicleDriveSimData* inSource ) + :EngineData( getPxVehicleDriveSimData_EngineData( inSource ) ) + ,GearsData( getPxVehicleDriveSimData_GearsData( inSource ) ) + ,ClutchData( getPxVehicleDriveSimData_ClutchData( inSource ) ) + ,AutoBoxData( getPxVehicleDriveSimData_AutoBoxData( inSource ) ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleDriveSimData4W_DiffData( PxVehicleDriveSimData4W* inObj, const PxVehicleDifferential4WData & inArg){ inObj->setDiffData( inArg ); } +PxVehicleDifferential4WData getPxVehicleDriveSimData4W_DiffData( const PxVehicleDriveSimData4W* inObj ) { return inObj->getDiffData(); } +void setPxVehicleDriveSimData4W_AckermannGeometryData( PxVehicleDriveSimData4W* inObj, const PxVehicleAckermannGeometryData & inArg){ inObj->setAckermannGeometryData( inArg ); } +PxVehicleAckermannGeometryData getPxVehicleDriveSimData4W_AckermannGeometryData( const PxVehicleDriveSimData4W* inObj ) { return inObj->getAckermannGeometryData(); } + PxVehicleDriveSimData4WGeneratedInfo::PxVehicleDriveSimData4WGeneratedInfo() + : DiffData( "DiffData", setPxVehicleDriveSimData4W_DiffData, getPxVehicleDriveSimData4W_DiffData) + , AckermannGeometryData( "AckermannGeometryData", setPxVehicleDriveSimData4W_AckermannGeometryData, getPxVehicleDriveSimData4W_AckermannGeometryData) +{} + PxVehicleDriveSimData4WGeneratedValues::PxVehicleDriveSimData4WGeneratedValues( const PxVehicleDriveSimData4W* inSource ) + :PxVehicleDriveSimDataGeneratedValues( inSource ) + ,DiffData( getPxVehicleDriveSimData4W_DiffData( inSource ) ) + ,AckermannGeometryData( getPxVehicleDriveSimData4W_AckermannGeometryData( inSource ) ) +{ + PX_UNUSED(inSource); +} +const char * getPxVehicleDrive_ConcreteTypeName( const PxVehicleDrive* inObj ) { return inObj->getConcreteTypeName(); } +inline PxVehicleDriveDynData getPxVehicleDriveMDriveDynData( const PxVehicleDrive* inOwner ) { return inOwner->mDriveDynData; } +inline void setPxVehicleDriveMDriveDynData( PxVehicleDrive* inOwner, PxVehicleDriveDynData inData) { inOwner->mDriveDynData = inData; } + PxVehicleDriveGeneratedInfo::PxVehicleDriveGeneratedInfo() + : ConcreteTypeName( "ConcreteTypeName", getPxVehicleDrive_ConcreteTypeName) + , MDriveDynData( "MDriveDynData", setPxVehicleDriveMDriveDynData, getPxVehicleDriveMDriveDynData ) +{} + PxVehicleDriveGeneratedValues::PxVehicleDriveGeneratedValues( const PxVehicleDrive* inSource ) + :PxVehicleWheelsGeneratedValues( inSource ) + ,ConcreteTypeName( getPxVehicleDrive_ConcreteTypeName( inSource ) ) + ,MDriveDynData( inSource->mDriveDynData ) +{ + PX_UNUSED(inSource); +} +const char * getPxVehicleDrive4W_ConcreteTypeName( const PxVehicleDrive4W* inObj ) { return inObj->getConcreteTypeName(); } +inline PxVehicleDriveSimData4W getPxVehicleDrive4WMDriveSimData( const PxVehicleDrive4W* inOwner ) { return inOwner->mDriveSimData; } +inline void setPxVehicleDrive4WMDriveSimData( PxVehicleDrive4W* inOwner, PxVehicleDriveSimData4W inData) { inOwner->mDriveSimData = inData; } + PxVehicleDrive4WGeneratedInfo::PxVehicleDrive4WGeneratedInfo() + : ConcreteTypeName( "ConcreteTypeName", getPxVehicleDrive4W_ConcreteTypeName) + , MDriveSimData( "MDriveSimData", setPxVehicleDrive4WMDriveSimData, getPxVehicleDrive4WMDriveSimData ) +{} + PxVehicleDrive4WGeneratedValues::PxVehicleDrive4WGeneratedValues( const PxVehicleDrive4W* inSource ) + :PxVehicleDriveGeneratedValues( inSource ) + ,ConcreteTypeName( getPxVehicleDrive4W_ConcreteTypeName( inSource ) ) + ,MDriveSimData( inSource->mDriveSimData ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleDriveTank_DriveModel( PxVehicleDriveTank* inObj, const PxVehicleDriveTankControlModel::Enum inArg){ inObj->setDriveModel( inArg ); } +PxVehicleDriveTankControlModel::Enum getPxVehicleDriveTank_DriveModel( const PxVehicleDriveTank* inObj ) { return inObj->getDriveModel(); } +const char * getPxVehicleDriveTank_ConcreteTypeName( const PxVehicleDriveTank* inObj ) { return inObj->getConcreteTypeName(); } +inline PxVehicleDriveSimData getPxVehicleDriveTankMDriveSimData( const PxVehicleDriveTank* inOwner ) { return inOwner->mDriveSimData; } +inline void setPxVehicleDriveTankMDriveSimData( PxVehicleDriveTank* inOwner, PxVehicleDriveSimData inData) { inOwner->mDriveSimData = inData; } + PxVehicleDriveTankGeneratedInfo::PxVehicleDriveTankGeneratedInfo() + : DriveModel( "DriveModel", setPxVehicleDriveTank_DriveModel, getPxVehicleDriveTank_DriveModel) + , ConcreteTypeName( "ConcreteTypeName", getPxVehicleDriveTank_ConcreteTypeName) + , MDriveSimData( "MDriveSimData", setPxVehicleDriveTankMDriveSimData, getPxVehicleDriveTankMDriveSimData ) +{} + PxVehicleDriveTankGeneratedValues::PxVehicleDriveTankGeneratedValues( const PxVehicleDriveTank* inSource ) + :PxVehicleDriveGeneratedValues( inSource ) + ,DriveModel( getPxVehicleDriveTank_DriveModel( inSource ) ) + ,ConcreteTypeName( getPxVehicleDriveTank_ConcreteTypeName( inSource ) ) + ,MDriveSimData( inSource->mDriveSimData ) +{ + PX_UNUSED(inSource); +} +void setPxVehicleDriveSimDataNW_DiffData( PxVehicleDriveSimDataNW* inObj, const PxVehicleDifferentialNWData & inArg){ inObj->setDiffData( inArg ); } +PxVehicleDifferentialNWData getPxVehicleDriveSimDataNW_DiffData( const PxVehicleDriveSimDataNW* inObj ) { return inObj->getDiffData(); } + PxVehicleDriveSimDataNWGeneratedInfo::PxVehicleDriveSimDataNWGeneratedInfo() + : DiffData( "DiffData", setPxVehicleDriveSimDataNW_DiffData, getPxVehicleDriveSimDataNW_DiffData) +{} + PxVehicleDriveSimDataNWGeneratedValues::PxVehicleDriveSimDataNWGeneratedValues( const PxVehicleDriveSimDataNW* inSource ) + :PxVehicleDriveSimDataGeneratedValues( inSource ) + ,DiffData( getPxVehicleDriveSimDataNW_DiffData( inSource ) ) +{ + PX_UNUSED(inSource); +} +const char * getPxVehicleDriveNW_ConcreteTypeName( const PxVehicleDriveNW* inObj ) { return inObj->getConcreteTypeName(); } +inline PxVehicleDriveSimDataNW getPxVehicleDriveNWMDriveSimData( const PxVehicleDriveNW* inOwner ) { return inOwner->mDriveSimData; } +inline void setPxVehicleDriveNWMDriveSimData( PxVehicleDriveNW* inOwner, PxVehicleDriveSimDataNW inData) { inOwner->mDriveSimData = inData; } + PxVehicleDriveNWGeneratedInfo::PxVehicleDriveNWGeneratedInfo() + : ConcreteTypeName( "ConcreteTypeName", getPxVehicleDriveNW_ConcreteTypeName) + , MDriveSimData( "MDriveSimData", setPxVehicleDriveNWMDriveSimData, getPxVehicleDriveNWMDriveSimData ) +{} + PxVehicleDriveNWGeneratedValues::PxVehicleDriveNWGeneratedValues( const PxVehicleDriveNW* inSource ) + :PxVehicleDriveGeneratedValues( inSource ) + ,ConcreteTypeName( getPxVehicleDriveNW_ConcreteTypeName( inSource ) ) + ,MDriveSimData( inSource->mDriveSimData ) +{ + PX_UNUSED(inSource); +} +PxReal getPxVehicleNoDrive_BrakeTorque( const PxVehicleNoDrive* inObj, const PxU32 index ) { return inObj->getBrakeTorque( index ); } +PxU32 getNbPxVehicleNoDrive_BrakeTorque( const PxVehicleNoDrive* inObj ) { return inObj->getNbBrakeTorque( ); } +void setPxVehicleNoDrive_BrakeTorque( PxVehicleNoDrive* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setBrakeTorque( inIndex, inValue ); } +PxReal getPxVehicleNoDrive_DriveTorque( const PxVehicleNoDrive* inObj, const PxU32 index ) { return inObj->getDriveTorque( index ); } +PxU32 getNbPxVehicleNoDrive_DriveTorque( const PxVehicleNoDrive* inObj ) { return inObj->getNbDriveTorque( ); } +void setPxVehicleNoDrive_DriveTorque( PxVehicleNoDrive* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setDriveTorque( inIndex, inValue ); } +PxReal getPxVehicleNoDrive_SteerAngle( const PxVehicleNoDrive* inObj, const PxU32 index ) { return inObj->getSteerAngle( index ); } +PxU32 getNbPxVehicleNoDrive_SteerAngle( const PxVehicleNoDrive* inObj ) { return inObj->getNbSteerAngle( ); } +void setPxVehicleNoDrive_SteerAngle( PxVehicleNoDrive* inObj, const PxU32 inIndex, PxReal inValue ){ inObj->setSteerAngle( inIndex, inValue ); } +const char * getPxVehicleNoDrive_ConcreteTypeName( const PxVehicleNoDrive* inObj ) { return inObj->getConcreteTypeName(); } + PxVehicleNoDriveGeneratedInfo::PxVehicleNoDriveGeneratedInfo() + : BrakeTorque( "BrakeTorque", getPxVehicleNoDrive_BrakeTorque, getNbPxVehicleNoDrive_BrakeTorque, setPxVehicleNoDrive_BrakeTorque ) + , DriveTorque( "DriveTorque", getPxVehicleNoDrive_DriveTorque, getNbPxVehicleNoDrive_DriveTorque, setPxVehicleNoDrive_DriveTorque ) + , SteerAngle( "SteerAngle", getPxVehicleNoDrive_SteerAngle, getNbPxVehicleNoDrive_SteerAngle, setPxVehicleNoDrive_SteerAngle ) + , ConcreteTypeName( "ConcreteTypeName", getPxVehicleNoDrive_ConcreteTypeName) +{} + PxVehicleNoDriveGeneratedValues::PxVehicleNoDriveGeneratedValues( const PxVehicleNoDrive* inSource ) + :PxVehicleWheelsGeneratedValues( inSource ) + ,ConcreteTypeName( getPxVehicleNoDrive_ConcreteTypeName( inSource ) ) +{ + PX_UNUSED(inSource); +} diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp new file mode 100644 index 000000000..b8b2bf691 --- /dev/null +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp @@ -0,0 +1,86 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPhysicsAPI.h" +#include "extensions/PxExtensionsAPI.h" +#include "PxVehicleMetaDataObjects.h" +#include "PxExtensionMetaDataObjects.h" + +namespace physx +{ + inline void SetMFrictionVsSlipGraph( PxVehicleTireData* inTireData, PxU32 idx1, PxU32 idx2, PxReal val ) { inTireData->mFrictionVsSlipGraph[idx1][idx2] = val; } + inline PxReal GetMFrictionVsSlipGraph( const PxVehicleTireData* inTireData, PxU32 idx1, PxU32 idx2 ) + { + return inTireData->mFrictionVsSlipGraph[idx1][idx2]; + } + PX_PHYSX_CORE_API MFrictionVsSlipGraphProperty::MFrictionVsSlipGraphProperty() + : PxExtendedDualIndexedPropertyInfo ( "MFrictionVsSlipGraph", SetMFrictionVsSlipGraph, GetMFrictionVsSlipGraph, 3, 2 ) + { + + } + + inline PxU32 GetNbWheels( const PxVehicleWheels* inStats ) { return inStats->mWheelsSimData.getNbWheels(); } + + inline PxU32 GetNbTorqueCurvePair( const PxVehicleEngineData* inStats ) { return inStats->mTorqueCurve.getNbDataPairs(); } + + + inline PxReal getXTorqueCurvePair( const PxVehicleEngineData* inStats, PxU32 index) + { + return inStats->mTorqueCurve.getX(index); + } + inline PxReal getYTorqueCurvePair( const PxVehicleEngineData* inStats, PxU32 index) + { + return inStats->mTorqueCurve.getY(index); + } + + void addTorqueCurvePair(PxVehicleEngineData* inStats, const PxReal x, const PxReal y) + { + inStats->mTorqueCurve.addPair(x, y); + } + + void clearTorqueCurvePair(PxVehicleEngineData* inStats) + { + inStats->mTorqueCurve.clear(); + } + + PX_PHYSX_CORE_API MTorqueCurveProperty::MTorqueCurveProperty() + : PxFixedSizeLookupTablePropertyInfo("MTorqueCurve", getXTorqueCurvePair, getYTorqueCurvePair, GetNbTorqueCurvePair, addTorqueCurvePair, clearTorqueCurvePair) + { + } + + +} + diff --git a/src/PhysX/physx/source/pvd/include/PsPvd.h b/src/PhysX/physx/source/pvd/include/PsPvd.h new file mode 100644 index 000000000..62b476bd2 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PsPvd.h @@ -0,0 +1,83 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PSPVD_H +#define PXPVDSDK_PSPVD_H + +/** \addtogroup pvd +@{ +*/ +#include "pvd/PxPvd.h" +#include "PsBroadcast.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPvdTransport; + +#if !PX_DOXYGEN +namespace pvdsdk +{ +#endif + +class PvdDataStream; +class PvdClient; +class PvdOMMetaDataProvider; + +// PsPvd is used for advanced user, it support custom pvd client API +class PsPvd : public physx::PxPvd, public shdfnd::AllocationListener +{ + public: + virtual void addClient(PvdClient* client) = 0; + virtual void removeClient(PvdClient* client) = 0; + + virtual bool registerObject(const void* inItem) = 0; + virtual bool unRegisterObject(const void* inItem) = 0; + + //AllocationListener + void onAllocation(size_t size, const char* typeName, const char* filename, int line, void* allocatedMemory) = 0; + void onDeallocation(void* addr) = 0; + + virtual PvdOMMetaDataProvider& getMetaDataProvider() = 0; + + virtual uint64_t getNextStreamId() = 0; + // Call to flush events to PVD + virtual void flush() = 0; + +}; + +#if !PX_DOXYGEN +} // namespace pvdsdk +} // namespace physx +#endif + +/** @} */ +#endif // PXPVDSDK_PSPVD_H diff --git a/src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h b/src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h new file mode 100644 index 000000000..160ddded4 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h @@ -0,0 +1,231 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEALLOCATORWRAPPER_H +#define PXPVDSDK_PXPROFILEALLOCATORWRAPPER_H + +#include "foundation/PxPreprocessor.h" +#include "foundation/PxAllocatorCallback.h" +#include "foundation/PxErrorCallback.h" +#include "foundation/PxAssert.h" + +#include "PsArray.h" +#include "PsHashMap.h" + +namespace physx { namespace profile { + + /** + \brief Helper struct to encapsulate the user allocator callback + Useful for array and hash templates + */ + struct PxProfileAllocatorWrapper + { + PxAllocatorCallback* mUserAllocator; + + PxProfileAllocatorWrapper( PxAllocatorCallback& inUserAllocator ) + : mUserAllocator( &inUserAllocator ) + { + } + + PxProfileAllocatorWrapper( PxAllocatorCallback* inUserAllocator ) + : mUserAllocator( inUserAllocator ) + { + } + + PxAllocatorCallback& getAllocator() const + { + PX_ASSERT( NULL != mUserAllocator ); + return *mUserAllocator; + } + }; + + /** + \brief Helper class to encapsulate the reflection allocator + */ + template + class PxProfileWrapperReflectionAllocator + { + static const char* getName() + { +#if PX_LINUX || PX_ANDROID || PX_PS4 || PX_IOS || PX_OSX || PX_EMSCRIPTEN || PX_SWITCH + return __PRETTY_FUNCTION__; +#else + return typeid(T).name(); +#endif + } + PxProfileAllocatorWrapper* mWrapper; + + public: + PxProfileWrapperReflectionAllocator(PxProfileAllocatorWrapper& inWrapper) : mWrapper( &inWrapper ) {} + PxProfileWrapperReflectionAllocator( const PxProfileWrapperReflectionAllocator& inOther ) + : mWrapper( inOther.mWrapper ) + { + } + PxProfileWrapperReflectionAllocator& operator=( const PxProfileWrapperReflectionAllocator& inOther ) + { + mWrapper = inOther.mWrapper; + return *this; + } + PxAllocatorCallback& getAllocator() { return mWrapper->getAllocator(); } + void* allocate(size_t size, const char* filename, int line) + { +#if PX_CHECKED // checked and debug builds + if(!size) + return 0; + return getAllocator().allocate(size, getName(), filename, line); +#else + return getAllocator().allocate(size, "", filename, line); +#endif + } + void deallocate(void* ptr) + { + if(ptr) + getAllocator().deallocate(ptr); + } + }; + + /** + \brief Helper class to encapsulate the named allocator + */ + struct PxProfileWrapperNamedAllocator + { + PxProfileAllocatorWrapper* mWrapper; + const char* mAllocationName; + PxProfileWrapperNamedAllocator(PxProfileAllocatorWrapper& inWrapper, const char* inAllocationName) + : mWrapper( &inWrapper ) + , mAllocationName( inAllocationName ) + {} + PxProfileWrapperNamedAllocator( const PxProfileWrapperNamedAllocator& inOther ) + : mWrapper( inOther.mWrapper ) + , mAllocationName( inOther.mAllocationName ) + { + } + PxProfileWrapperNamedAllocator& operator=( const PxProfileWrapperNamedAllocator& inOther ) + { + mWrapper = inOther.mWrapper; + mAllocationName = inOther.mAllocationName; + return *this; + } + PxAllocatorCallback& getAllocator() { return mWrapper->getAllocator(); } + void* allocate(size_t size, const char* filename, int line) + { + if(!size) + return 0; + return getAllocator().allocate(size, mAllocationName, filename, line); + } + void deallocate(void* ptr) + { + if(ptr) + getAllocator().deallocate(ptr); + } + }; + + /** + \brief Helper struct to encapsulate the array + */ + template + struct PxProfileArray : public shdfnd::Array > + { + typedef PxProfileWrapperReflectionAllocator TAllocatorType; + + PxProfileArray( PxProfileAllocatorWrapper& inWrapper ) + : shdfnd::Array( TAllocatorType( inWrapper ) ) + { + } + + PxProfileArray( const PxProfileArray< T >& inOther ) + : shdfnd::Array( inOther, inOther ) + { + } + }; + + /** + \brief Helper struct to encapsulate the array + */ + template > + struct PxProfileHashMap : public shdfnd::HashMap > + { + typedef shdfnd::HashMap > THashMapType; + typedef PxProfileWrapperReflectionAllocator TAllocatorType; + PxProfileHashMap( PxProfileAllocatorWrapper& inWrapper ) + : THashMapType( TAllocatorType( inWrapper ) ) + { + } + }; + + /** + \brief Helper function to encapsulate the profile allocation + */ + template + inline TDataType* PxProfileAllocate( PxAllocatorCallback* inAllocator, const char* file, int inLine ) + { + PxProfileAllocatorWrapper wrapper( inAllocator ); + typedef PxProfileWrapperReflectionAllocator< TDataType > TAllocator; + TAllocator theAllocator( wrapper ); + return reinterpret_cast( theAllocator.allocate( sizeof( TDataType ), file, inLine ) ); + } + + /** + \brief Helper function to encapsulate the profile allocation + */ + template + inline TDataType* PxProfileAllocate( PxAllocatorCallback& inAllocator, const char* file, int inLine ) + { + return PxProfileAllocate( &inAllocator, file, inLine ); + } + + /** + \brief Helper function to encapsulate the profile deallocation + */ + template + inline void PxProfileDeleteAndDeallocate( PxProfileAllocatorWrapper& inAllocator, TDataType* inDType ) + { + PX_ASSERT(inDType); + PxAllocatorCallback& allocator( inAllocator.getAllocator() ); + inDType->~TDataType(); + allocator.deallocate( inDType ); + } + + /** + \brief Helper function to encapsulate the profile deallocation + */ + template + inline void PxProfileDeleteAndDeallocate( PxAllocatorCallback& inAllocator, TDataType* inDType ) + { + PxProfileAllocatorWrapper wrapper( &inAllocator ); + PxProfileDeleteAndDeallocate( wrapper, inDType ); + } + +} } + +#define PX_PROFILE_NEW( allocator, dtype ) new (physx::profile::PxProfileAllocate( allocator, __FILE__, __LINE__ )) dtype +#define PX_PROFILE_DELETE( allocator, obj ) physx::profile::PxProfileDeleteAndDeallocate( allocator, obj ); + +#endif // PXPVDSDK_PXPROFILEALLOCATORWRAPPER_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdClient.h b/src/PhysX/physx/source/pvd/include/PxPvdClient.h new file mode 100644 index 000000000..6ba6a3011 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdClient.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDCLIENT_H +#define PXPVDSDK_PXPVDCLIENT_H + +/** \addtogroup pvd +@{ +*/ +#include "foundation/PxFlags.h" +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +class PvdDataStream; +class PvdUserRenderer; + +/** +\brief PvdClient is the per-client connection to PVD. +It provides callback when PVD is connected/disconnted. +It provides access to the internal object so that advanced users can create extension client. +*/ +class PvdClient +{ + public: + virtual PvdDataStream* getDataStream() = 0; + virtual PvdUserRenderer* getUserRender() = 0; + + virtual bool isConnected() const = 0; + virtual void onPvdConnected() = 0; + virtual void onPvdDisconnected() = 0; + virtual void flush() = 0; + + protected: + virtual ~PvdClient() + { + } +}; + +#if !PX_DOXYGEN +} // namespace pvdsdk +} // namespace physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDCLIENT_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdDataStream.h b/src/PhysX/physx/source/pvd/include/PxPvdDataStream.h new file mode 100644 index 000000000..c16d35b46 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdDataStream.h @@ -0,0 +1,272 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#ifndef PXPVDSDK_PXPVDDATASTREAM_H +#define PXPVDSDK_PXPVDDATASTREAM_H + +/** \addtogroup pvd +@{ +*/ +#include "pvd/PxPvd.h" +#include "PxPvdErrorCodes.h" +#include "PxPvdObjectModelBaseTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +class PvdPropertyDefinitionHelper; + +class PvdMetaDataStream +{ + protected: + virtual ~PvdMetaDataStream() + { + } + + public: + virtual PvdError createClass(const NamespacedName& nm) = 0; + template + PvdError createClass() + { + return createClass(getPvdNamespacedNameForType()); + } + + virtual PvdError deriveClass(const NamespacedName& parent, const NamespacedName& child) = 0; + template + PvdError deriveClass() + { + return deriveClass(getPvdNamespacedNameForType(), getPvdNamespacedNameForType()); + } + + virtual bool isClassExist(const NamespacedName& nm) = 0; + template + bool isClassExist() + { + return isClassExist(getPvdNamespacedNameForType()); + } + + virtual PvdError createProperty(const NamespacedName& clsName, const char* name, const char* semantic, + const NamespacedName& dtypeName, PropertyType::Enum propertyType, + DataRef values = DataRef()) = 0; + template + PvdError createProperty(String name, String semantic = "", PropertyType::Enum propertyType = PropertyType::Scalar, + DataRef values = DataRef()) + { + return createProperty(getPvdNamespacedNameForType(), name, semantic, + getPvdNamespacedNameForType(), propertyType, values); + } + + virtual PvdError createPropertyMessage(const NamespacedName& cls, const NamespacedName& msgName, + DataRef entries, uint32_t messageSizeInBytes) = 0; + + template + PvdError createPropertyMessage(DataRef entries) + { + return createPropertyMessage(getPvdNamespacedNameForType(), getPvdNamespacedNameForType(), + entries, sizeof(TMsgType)); + } +}; + +class PvdInstanceDataStream +{ + protected: + virtual ~PvdInstanceDataStream() + { + } + + public: + virtual PvdError createInstance(const NamespacedName& cls, const void* instance) = 0; + + template + PvdError createInstance(const TDataType* inst) + { + return createInstance(getPvdNamespacedNameForType(), inst); + } + virtual bool isInstanceValid(const void* instance) = 0; + + // If the property will fit or is already completely in memory + virtual PvdError setPropertyValue(const void* instance, String name, DataRef data, + const NamespacedName& incomingTypeName) = 0; + template + PvdError setPropertyValue(const void* instance, String name, const TDataType& value) + { + const uint8_t* dataStart = reinterpret_cast(&value); + return setPropertyValue(instance, name, DataRef(dataStart, dataStart + sizeof(TDataType)), + getPvdNamespacedNameForType()); + } + + template + PvdError setPropertyValue(const void* instance, String name, const TDataType* value, uint32_t numItems) + { + const uint8_t* dataStart = reinterpret_cast(value); + return setPropertyValue(instance, name, + DataRef(dataStart, dataStart + sizeof(TDataType) * numItems), + getPvdNamespacedNameForType()); + } + + // Else if the property is very large (contact reports) you can send it in chunks. + virtual PvdError beginSetPropertyValue(const void* instance, String name, const NamespacedName& incomingTypeName) = 0; + + template + PvdError beginSetPropertyValue(const void* instance, String name) + { + return beginSetPropertyValue(instance, name, getPvdNamespacedNameForType()); + } + virtual PvdError appendPropertyValueData(DataRef data) = 0; + + template + PvdError appendPropertyValueData(const TDataType* value, uint32_t numItems) + { + const uint8_t* dataStart = reinterpret_cast(value); + return appendPropertyValueData(DataRef(dataStart, dataStart + numItems * sizeof(TDataType))); + } + + virtual PvdError endSetPropertyValue() = 0; + + // Set a set of properties to various values on an object. + + virtual PvdError setPropertyMessage(const void* instance, const NamespacedName& msgName, + DataRef data) = 0; + + template + PvdError setPropertyMessage(const void* instance, const TDataType& value) + { + const uint8_t* dataStart = reinterpret_cast(&value); + return setPropertyMessage(instance, getPvdNamespacedNameForType(), + DataRef(dataStart, sizeof(TDataType))); + } + // If you need to send of lot of identical messages, this avoids a hashtable lookup per message. + virtual PvdError beginPropertyMessageGroup(const NamespacedName& msgName) = 0; + + template + PvdError beginPropertyMessageGroup() + { + return beginPropertyMessageGroup(getPvdNamespacedNameForType()); + } + virtual PvdError sendPropertyMessageFromGroup(const void* instance, DataRef data) = 0; + + template + PvdError sendPropertyMessageFromGroup(const void* instance, const TDataType& value) + { + const uint8_t* dataStart = reinterpret_cast(&value); + return sendPropertyMessageFromGroup(instance, DataRef(dataStart, sizeof(TDataType))); + } + + virtual PvdError endPropertyMessageGroup() = 0; + + // These functions ensure the target array doesn't contain duplicates + virtual PvdError pushBackObjectRef(const void* instId, String propName, const void* objRef) = 0; + virtual PvdError removeObjectRef(const void* instId, String propName, const void* objRef) = 0; + + // Instance elimination. + virtual PvdError destroyInstance(const void* key) = 0; + + // Profiling hooks + virtual PvdError beginSection(const void* instance, String name) = 0; + virtual PvdError endSection(const void* instance, String name) = 0; + + // Origin Shift + virtual PvdError originShift(const void* scene, PxVec3 shift) = 0; + + public: + /*For some cases, pvd command cannot be run immediately. For example, when create joints, while the actors may still + *pending for insert, the joints update commands can be run deffered. + */ + class PvdCommand + { + public: + // Assigned is needed for copying + PvdCommand(const PvdCommand&) + { + } + PvdCommand& operator=(const PvdCommand&) + { + return *this; + } + + public: + PvdCommand() + { + } + virtual ~PvdCommand() + { + } + + // Not pure virtual so can have default PvdCommand obj + virtual bool canRun(PvdInstanceDataStream&) + { + return false; + } + virtual void run(PvdInstanceDataStream&) + { + } + }; + + // PVD SDK provide this helper function to allocate cmd's memory and release them at after flush the command queue + virtual void* allocateMemForCmd(uint32_t length) = 0; + + // PVD will call the destructor of PvdCommand object at the end fo flushPvdCommand + virtual void pushPvdCommand(PvdCommand& cmd) = 0; + virtual void flushPvdCommand() = 0; +}; + +class PvdDataStream : public PvdInstanceDataStream, public PvdMetaDataStream +{ + protected: + virtual ~PvdDataStream() + { + } + + public: + virtual void release() = 0; + virtual bool isConnected() = 0; + + virtual void addProfileZone(void* zone, const char* name) = 0; + virtual void addProfileZoneEvent(void* zone, const char* name, uint16_t eventId, bool compileTimeEnabled) = 0; + + virtual PvdPropertyDefinitionHelper& getPropertyDefinitionHelper() = 0; + + virtual void setIsTopLevelUIElement(const void* instance, bool topLevel) = 0; + virtual void sendErrorMessage(uint32_t code, const char* message, const char* file, uint32_t line) = 0; + virtual void updateCamera(const char* name, const PxVec3& origin, const PxVec3& up, const PxVec3& target) = 0; + +/** + \brief Create a new PvdDataStream. + \param pvd A pointer to a valid PxPvd instance. This must be non-null. +*/ + static PvdDataStream* create(PxPvd* pvd); +}; +#if !PX_DOXYGEN +} // pvdsdk +} // physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDDATASTREAM_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h b/src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h new file mode 100644 index 000000000..503491b0c --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#ifndef PXPVDSDK_PXPVDDATASTREAMHELPERS_H +#define PXPVDSDK_PXPVDDATASTREAMHELPERS_H + +/** \addtogroup pvd +@{ +*/ +#include "PxPvdObjectModelBaseTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +class PvdPropertyDefinitionHelper +{ + protected: + virtual ~PvdPropertyDefinitionHelper() + { + } + + public: + /** + Push a name c such that it appends such as a.b.c. + */ + virtual void pushName(const char* inName, const char* inAppendStr = ".") = 0; + /** + Push a name c such that it appends like a.b[c] + */ + virtual void pushBracketedName(const char* inName, const char* leftBracket = "[", const char* rightBracket = "]") = 0; + /** + * Pop the current name + */ + virtual void popName() = 0; + + virtual void clearNameStack() = 0; + /** + * Get the current name at the top of the name stack. + * Would return "a.b.c" or "a.b[c]" in the above examples. + */ + virtual const char* getTopName() = 0; + + virtual void addNamedValue(const char* name, uint32_t value) = 0; + virtual void clearNamedValues() = 0; + virtual DataRef getNamedValues() = 0; + + /** + * Define a property using the top of the name stack and the passed-in semantic + */ + virtual void createProperty(const NamespacedName& clsName, const char* inSemantic, const NamespacedName& dtypeName, + PropertyType::Enum propType = PropertyType::Scalar) = 0; + + template + void createProperty(const char* inSemantic = "", PropertyType::Enum propType = PropertyType::Scalar) + { + createProperty(getPvdNamespacedNameForType(), inSemantic, getPvdNamespacedNameForType(), + propType); + } + + // The datatype used for instances needs to be pointer unless you actually have pvdsdk::InstanceId members on your + // value structs. + virtual void addPropertyMessageArg(const NamespacedName& inDatatype, uint32_t inOffset, uint32_t inSize) = 0; + + template + void addPropertyMessageArg(uint32_t offset) + { + addPropertyMessageArg(getPvdNamespacedNameForType(), offset, static_cast(sizeof(TDataType))); + } + virtual void addPropertyMessage(const NamespacedName& clsName, const NamespacedName& msgName, + uint32_t inStructSizeInBytes) = 0; + template + void addPropertyMessage() + { + addPropertyMessage(getPvdNamespacedNameForType(), getPvdNamespacedNameForType(), + static_cast(sizeof(TMsgType))); + } + virtual void clearPropertyMessageArgs() = 0; + + void clearBufferedData() + { + clearNameStack(); + clearPropertyMessageArgs(); + clearNamedValues(); + } +}; + +#if !PX_DOXYGEN +} // pvdsdk +} // physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDDATASTREAMHELPERS_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h b/src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h new file mode 100644 index 000000000..8865b6aa6 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h @@ -0,0 +1,62 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#ifndef PXPVDSDK_PXPVDERRORCODES_H +#define PXPVDSDK_PXPVDERRORCODES_H + +/** \addtogroup pvd +@{ +*/ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +struct PvdErrorType +{ + enum Enum + { + Success = 0, + NetworkError, + ArgumentError, + Disconnect, + InternalProblem + }; +}; + +typedef PvdErrorType::Enum PvdError; + +#if !PX_DOXYGEN +} +} +#endif +/** @} */ +#endif // PXPVDSDK_PXPVDERRORCODES_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h b/src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h new file mode 100644 index 000000000..66384a726 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h @@ -0,0 +1,428 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDOBJECTMODELBASETYPES_H +#define PXPVDSDK_PXPVDOBJECTMODELBASETYPES_H + +/** \addtogroup pvd +@{ +*/ +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +using namespace physx; + +inline const char* nonNull(const char* str) +{ + return str ? str : ""; +} +// strcmp will crash if passed a null string, however, +// so we need to make sure that doesn't happen. We do that +// by equating NULL and the empty string, "". +inline bool safeStrEq(const char* lhs, const char* rhs) +{ + return ::strcmp(nonNull(lhs), nonNull(rhs)) == 0; +} + +// Does this string have useful information in it. +inline bool isMeaningful(const char* str) +{ + return *(nonNull(str)) > 0; +} + +inline uint32_t safeStrLen(const char* str) +{ + str = nonNull(str); + return static_cast(strlen(str)); +} + +struct ObjectRef +{ + int32_t mInstanceId; + + ObjectRef(int32_t iid = -1) : mInstanceId(iid) + { + } + operator int32_t() const + { + return mInstanceId; + } + bool hasValue() const + { + return mInstanceId > 0; + } +}; + +struct U32Array4 +{ + uint32_t mD0; + uint32_t mD1; + uint32_t mD2; + uint32_t mD3; + U32Array4(uint32_t d0, uint32_t d1, uint32_t d2, uint32_t d3) : mD0(d0), mD1(d1), mD2(d2), mD3(d3) + { + } + U32Array4() : mD0(0), mD1(0), mD2(0), mD3(0) + { + } +}; + +typedef bool PvdBool; +typedef const char* String; +typedef void* VoidPtr; +typedef double PvdF64; +typedef float PvdF32; +typedef int64_t PvdI64; +typedef uint64_t PvdU64; +typedef int32_t PvdI32; +typedef uint32_t PvdU32; +typedef int16_t PvdI16; +typedef uint16_t PvdU16; +typedef int8_t PvdI8; +typedef uint8_t PvdU8; + +struct PvdColor +{ + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + PvdColor(uint8_t _r, uint8_t _g, uint8_t _b, uint8_t _a = 255) : r(_r), g(_g), b(_b), a(_a) + { + } + PvdColor() : r(0), g(0), b(0), a(255) + { + } + PvdColor(uint32_t abgr) + { + uint8_t* valPtr = reinterpret_cast(&abgr); + r = valPtr[0]; + g = valPtr[1]; + b = valPtr[2]; + a = valPtr[3]; + } +}; + +struct StringHandle +{ + uint32_t mHandle; + StringHandle(uint32_t val = 0) : mHandle(val) + { + } + operator uint32_t() const + { + return mHandle; + } +}; + +#define DECLARE_TYPES \ +DECLARE_BASE_PVD_TYPE(PvdI8) \ +DECLARE_BASE_PVD_TYPE(PvdU8) \ +DECLARE_BASE_PVD_TYPE(PvdI16) \ +DECLARE_BASE_PVD_TYPE(PvdU16) \ +DECLARE_BASE_PVD_TYPE(PvdI32) \ +DECLARE_BASE_PVD_TYPE(PvdU32) \ +DECLARE_BASE_PVD_TYPE(PvdI64) \ +DECLARE_BASE_PVD_TYPE(PvdU64) \ +DECLARE_BASE_PVD_TYPE(PvdF32) \ +DECLARE_BASE_PVD_TYPE(PvdF64) \ +DECLARE_BASE_PVD_TYPE(PvdBool) \ +DECLARE_BASE_PVD_TYPE(PvdColor) \ +DECLARE_BASE_PVD_TYPE(String) \ +DECLARE_BASE_PVD_TYPE(StringHandle) \ +DECLARE_BASE_PVD_TYPE(ObjectRef) \ +DECLARE_BASE_PVD_TYPE(VoidPtr) \ +DECLARE_BASE_PVD_TYPE(PxVec2) \ +DECLARE_BASE_PVD_TYPE(PxVec3) \ +DECLARE_BASE_PVD_TYPE(PxVec4) \ +DECLARE_BASE_PVD_TYPE(PxBounds3) \ +DECLARE_BASE_PVD_TYPE(PxQuat) \ +DECLARE_BASE_PVD_TYPE(PxTransform) \ +DECLARE_BASE_PVD_TYPE(PxMat33) \ +DECLARE_BASE_PVD_TYPE(PxMat44) \ +DECLARE_BASE_PVD_TYPE(U32Array4) + +struct PvdBaseType +{ + enum Enum + { + None = 0, + InternalStart = 1, + InternalStop = 64, +#define DECLARE_BASE_PVD_TYPE(type) type, + DECLARE_TYPES + Last +#undef DECLARE_BASE_PVD_TYPE + }; +}; +struct NamespacedName +{ + String mNamespace; + String mName; + NamespacedName(String ns, String nm) : mNamespace(ns), mName(nm) + { + } + NamespacedName(String nm = "") : mNamespace(""), mName(nm) + { + } + bool operator==(const NamespacedName& other) const + { + return safeStrEq(mNamespace, other.mNamespace) && safeStrEq(mName, other.mName); + } +}; + +struct NamedValue +{ + String mName; + uint32_t mValue; + NamedValue(String nm = "", uint32_t val = 0) : mName(nm), mValue(val) + { + } +}; + +template +struct BaseDataTypeToTypeMap +{ + bool compile_error; +}; +template +struct BaseTypeToDataTypeMap +{ + bool compile_error; +}; + +// Users can extend this mapping with new datatypes. +template +struct PvdDataTypeToNamespacedNameMap +{ + bool Name; +}; +// This mapping tells you the what class id to use for the base datatypes +// +#define DECLARE_BASE_PVD_TYPE(type) \ + template <> \ + struct BaseDataTypeToTypeMap \ + { \ + enum Enum \ + { \ + BaseTypeEnum = PvdBaseType::type \ + }; \ + }; \ + template <> \ + struct BaseDataTypeToTypeMap \ + { \ + enum Enum \ + { \ + BaseTypeEnum = PvdBaseType::type \ + }; \ + }; \ + template <> \ + struct BaseTypeToDataTypeMap \ + { \ + typedef type TDataType; \ + }; \ + template <> \ + struct PvdDataTypeToNamespacedNameMap \ + { \ + NamespacedName Name; \ + PvdDataTypeToNamespacedNameMap() : Name("physx3", #type) \ + { \ + } \ + }; \ + template <> \ + struct PvdDataTypeToNamespacedNameMap \ + { \ + NamespacedName Name; \ + PvdDataTypeToNamespacedNameMap() : Name("physx3", #type) \ + { \ + } \ + }; + +DECLARE_TYPES +#undef DECLARE_BASE_PVD_TYPE + +template +inline int32_t getPvdTypeForType() +{ + return static_cast(BaseDataTypeToTypeMap::BaseTypeEnum); +} +template +inline NamespacedName getPvdNamespacedNameForType() +{ + return PvdDataTypeToNamespacedNameMap().Name; +} + +#define DEFINE_PVD_TYPE_NAME_MAP(type, ns, name) \ + template <> \ + struct PvdDataTypeToNamespacedNameMap \ + { \ + NamespacedName Name; \ + PvdDataTypeToNamespacedNameMap() : Name(ns, name) \ + { \ + } \ + }; + +#define DEFINE_PVD_TYPE_ALIAS(newType, oldType) \ + template <> \ + struct PvdDataTypeToNamespacedNameMap \ + { \ + NamespacedName Name; \ + PvdDataTypeToNamespacedNameMap() : Name(PvdDataTypeToNamespacedNameMap().Name) \ + { \ + } \ + }; + +DEFINE_PVD_TYPE_ALIAS(const void*, void*) + +struct ArrayData +{ + uint8_t* mBegin; + uint8_t* mEnd; + uint8_t* mCapacity; //>= stop + ArrayData(uint8_t* beg = NULL, uint8_t* end = NULL, uint8_t* cap = NULL) : mBegin(beg), mEnd(end), mCapacity(cap) + { + } + uint8_t* begin() + { + return mBegin; + } + uint8_t* end() + { + return mEnd; + } + uint32_t byteCapacity() + { + return static_cast(mCapacity - mBegin); + } + uint32_t byteSize() const + { + return static_cast(mEnd - mBegin); + } // in bytes + uint32_t numberOfItems(uint32_t objectByteSize) + { + if(objectByteSize) + return byteSize() / objectByteSize; + return 0; + } + + void forgetData() + { + mBegin = mEnd = mCapacity = 0; + } +}; + +template +class DataRef +{ + const T* mBegin; + const T* mEnd; + + public: + DataRef(const T* b, uint32_t count) : mBegin(b), mEnd(b + count) + { + } + DataRef(const T* b = NULL, const T* e = NULL) : mBegin(b), mEnd(e) + { + } + DataRef(const DataRef& o) : mBegin(o.mBegin), mEnd(o.mEnd) + { + } + DataRef& operator=(const DataRef& o) + { + mBegin = o.mBegin; + mEnd = o.mEnd; + return *this; + } + uint32_t size() const + { + return static_cast(mEnd - mBegin); + } + const T* begin() const + { + return mBegin; + } + const T* end() const + { + return mEnd; + } + const T& operator[](uint32_t idx) const + { + PX_ASSERT(idx < size()); + return mBegin[idx]; + } + const T& back() const + { + PX_ASSERT(mEnd > mBegin); + return *(mEnd - 1); + } +}; + +struct PropertyType +{ + enum Enum + { + Unknown = 0, + Scalar, + Array + }; +}; + +// argument to the create property message function +struct PropertyMessageArg +{ + String mPropertyName; + NamespacedName mDatatypeName; + // where in the message this property starts. + uint32_t mMessageOffset; + // size of this entry object + uint32_t mByteSize; + + PropertyMessageArg(String propName, NamespacedName dtype, uint32_t msgOffset, uint32_t byteSize) + : mPropertyName(propName), mDatatypeName(dtype), mMessageOffset(msgOffset), mByteSize(byteSize) + { + } + PropertyMessageArg() : mPropertyName(""), mMessageOffset(0), mByteSize(0) + { + } +}; + +class PvdUserRenderer; +DEFINE_PVD_TYPE_NAME_MAP(PvdUserRenderer, "_debugger_", "PvdUserRenderer") + +#if !PX_DOXYGEN +} +} +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDOBJECTMODELBASETYPES_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h b/src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h new file mode 100644 index 000000000..ef851c596 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h @@ -0,0 +1,140 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDRENDERBUFFER_H +#define PXPVDSDK_PXPVDRENDERBUFFER_H + +/** \addtogroup pvd +@{ +*/ + +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif + +/** +\brief Default color values used for debug rendering. +*/ +struct PvdDebugColor +{ + enum Enum + { + eARGB_BLACK = 0xff000000, + eARGB_RED = 0xffff0000, + eARGB_GREEN = 0xff00ff00, + eARGB_BLUE = 0xff0000ff, + eARGB_YELLOW = 0xffffff00, + eARGB_MAGENTA = 0xffff00ff, + eARGB_CYAN = 0xff00ffff, + eARGB_WHITE = 0xffffffff, + eARGB_GREY = 0xff808080, + eARGB_DARKRED = 0x88880000, + eARGB_DARKGREEN = 0x88008800, + eARGB_DARKBLUE = 0x88000088 + }; +}; + +/** +\brief Used to store a single point and colour for debug rendering. +*/ +struct PvdDebugPoint +{ + PvdDebugPoint(const PxVec3& p, const uint32_t& c) : pos(p), color(c) + { + } + + PxVec3 pos; + uint32_t color; +}; + +/** +\brief Used to store a single line and colour for debug rendering. +*/ +struct PvdDebugLine +{ + PvdDebugLine(const PxVec3& p0, const PxVec3& p1, const uint32_t& c) : pos0(p0), color0(c), pos1(p1), color1(c) + { + } + + PxVec3 pos0; + uint32_t color0; + PxVec3 pos1; + uint32_t color1; +}; + +/** +\brief Used to store a single triangle and colour for debug rendering. +*/ +struct PvdDebugTriangle +{ + PvdDebugTriangle(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2, const uint32_t& c) + : pos0(p0), color0(c), pos1(p1), color1(c), pos2(p2), color2(c) + { + } + + PxVec3 pos0; + uint32_t color0; + PxVec3 pos1; + uint32_t color1; + PxVec3 pos2; + uint32_t color2; +}; + +/** +\brief Used to store a text for debug rendering. Doesn't own 'string' array. +*/ +struct PvdDebugText +{ + PvdDebugText() : string(0) + { + } + + PvdDebugText(const PxVec3& p, const float& s, const uint32_t& c, const char* str) + : position(p), size(s), color(c), string(str) + { + } + + PxVec3 position; + float size; + uint32_t color; + const char* string; +}; + +#if !PX_DOXYGEN +} +} // namespace physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDRENDERBUFFER_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h b/src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h new file mode 100644 index 000000000..9598fe410 --- /dev/null +++ b/src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h @@ -0,0 +1,107 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#ifndef PXPVDSDK_PXPVDUSERRENDERER_H +#define PXPVDSDK_PXPVDUSERRENDERER_H + +/** \addtogroup pvd +@{ +*/ +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "pvd/PxPvd.h" + +#include "PxPvdDataStream.h" +#include "PxPvdRenderBuffer.h" +#include "PsUserAllocated.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxPvd; + +#if !PX_DOXYGEN +namespace pvdsdk +{ +#endif + +class RendererEventClient; + +class PvdUserRenderer : public shdfnd::UserAllocated +{ + protected: + virtual ~PvdUserRenderer() + { + } + + public: + virtual void release() = 0; + virtual void setClient(RendererEventClient* client) = 0; + + // Instance to associate the further rendering with. + virtual void setInstanceId(const void* instanceId) = 0; + // Draw these points associated with this instance + virtual void drawPoints(const PvdDebugPoint* points, uint32_t count) = 0; + // Draw these lines associated with this instance + virtual void drawLines(const PvdDebugLine* lines, uint32_t count) = 0; + // Draw these triangles associated with this instance + virtual void drawTriangles(const PvdDebugTriangle* triangles, uint32_t count) = 0; + // Draw this text associated with this instance + virtual void drawText(const PvdDebugText& text) = 0; + + // Draw SDK debug render + virtual void drawRenderbuffer(const PvdDebugPoint* pointData, uint32_t pointCount, const PvdDebugLine* lineData, + uint32_t lineCount, const PvdDebugTriangle* triangleData, uint32_t triangleCount) = 0; + + // Constraint visualization routines + virtual void visualizeJointFrames(const PxTransform& parent, const PxTransform& child) = 0; + virtual void visualizeLinearLimit(const PxTransform& t0, const PxTransform& t1, float value, bool active) = 0; + virtual void visualizeAngularLimit(const PxTransform& t0, float lower, float upper, bool active) = 0; + virtual void visualizeLimitCone(const PxTransform& t, float tanQSwingY, float tanQSwingZ, bool active) = 0; + virtual void visualizeDoubleCone(const PxTransform& t, float angle, bool active) = 0; + + // Clear the immedate buffer. + virtual void flushRenderEvents() = 0; + + static PvdUserRenderer* create(uint32_t bufferSize = 0x2000); +}; + +class RendererEventClient +{ + public: + virtual ~RendererEventClient(){} + + virtual void handleBufferFlush(const uint8_t* inData, uint32_t inLength) = 0; +}; + +#if !PX_DOXYGEN +} +} +#endif +/** @} */ +#endif // PXPVDSDK_PXPVDUSERRENDERER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h b/src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h new file mode 100644 index 000000000..5ac872652 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h @@ -0,0 +1,58 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILECONTEXTPROVIDER_H +#define PXPVDSDK_PXPROFILECONTEXTPROVIDER_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + + struct PxProfileEventExecutionContext + { + uint32_t mThreadId; + uint8_t mCpuId; + uint8_t mThreadPriority; + + PxProfileEventExecutionContext( uint32_t inThreadId = 0, uint8_t inThreadPriority = 2 /*eThreadPriorityNormal*/, uint8_t inCpuId = 0 ) + : mThreadId( inThreadId ) + , mCpuId( inCpuId ) + , mThreadPriority( inThreadPriority ) + { + } + + bool operator==( const PxProfileEventExecutionContext& inOther ) const + { + return mThreadId == inOther.mThreadId + && mCpuId == inOther.mCpuId + && mThreadPriority == inOther.mThreadPriority; + } + }; + +} } + +#endif // PXPVDSDK_PXPROFILECONTEXTPROVIDER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h b/src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h new file mode 100644 index 000000000..fdf68ec72 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h @@ -0,0 +1,52 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILECONTEXTPROVIDERIMPL_H +#define PXPVDSDK_PXPROFILECONTEXTPROVIDERIMPL_H + +#include "PxProfileContextProvider.h" + +#include "PsThread.h" + +namespace physx { namespace profile { + + struct PxDefaultContextProvider + { + PxProfileEventExecutionContext getExecutionContext() + { + shdfnd::Thread::Id theId( shdfnd::Thread::getId() ); + return PxProfileEventExecutionContext( static_cast( theId ), static_cast( shdfnd::ThreadPriority::eNORMAL ), 0 ); + } + + uint32_t getThreadId() + { + return static_cast( shdfnd::Thread::getId() ); + } + }; +} } + +#endif // PXPVDSDK_PXPROFILECONTEXTPROVIDERIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h new file mode 100644 index 000000000..9b8d4c0cf --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h @@ -0,0 +1,167 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEDATABUFFER_H +#define PXPVDSDK_PXPROFILEDATABUFFER_H + +#include "PxProfileAllocatorWrapper.h" +#include "PxProfileMemoryBuffer.h" +#include "PxProfileEventBufferClient.h" + +namespace physx { namespace profile { + + template + class DataBuffer //base class for buffers that cache data and then dump the data to clients. + { + public: + typedef TMutex TMutexType; + typedef TScopedLock TScopedLockType; + typedef PxProfileWrapperNamedAllocator TU8AllocatorType; + + typedef MemoryBuffer TMemoryBufferType; + typedef PxProfileArray TBufferClientArray; + + protected: + + PxProfileAllocatorWrapper mWrapper; + TMemoryBufferType mDataArray; + TBufferClientArray mBufferClients; + uint32_t mBufferFullAmount; + EventContextInformation mEventContextInformation; + TMutexType* mBufferMutex; + volatile bool mHasClients; + EventSerializer mSerializer; + + public: + + DataBuffer( PxAllocatorCallback* inFoundation + , uint32_t inBufferFullAmount + , TMutexType* inBufferMutex + , const char* inAllocationName ) + : mWrapper( inFoundation ) + , mDataArray( TU8AllocatorType( mWrapper, inAllocationName ) ) + , mBufferClients( mWrapper ) + , mBufferFullAmount( inBufferFullAmount ) + , mBufferMutex( inBufferMutex ) + , mHasClients( false ) + , mSerializer( &mDataArray ) + { + //The data array is never resized really. We ensure + //it is bigger than it will ever need to be. + mDataArray.reserve( inBufferFullAmount + 68 ); + } + + virtual ~DataBuffer() + { + while(mBufferClients.size() ) + { + removeClient( *mBufferClients[0] ); + } + } + + PxProfileAllocatorWrapper& getWrapper() { return mWrapper; } + TMutexType* getBufferMutex() { return mBufferMutex; } + void setBufferMutex(TMutexType* mutex) { mBufferMutex = mutex; } + + void addClient( PxProfileEventBufferClient& inClient ) + { + TScopedLockType lock( mBufferMutex ); + mBufferClients.pushBack( &inClient ); + mHasClients = true; + } + + void removeClient( PxProfileEventBufferClient& inClient ) + { + TScopedLockType lock( mBufferMutex ); + for ( uint32_t idx =0; idx < mBufferClients.size(); ++idx ) + { + if (mBufferClients[idx] == &inClient ) + { + inClient.handleClientRemoved(); + mBufferClients.replaceWithLast( idx ); + break; + } + } + mHasClients = mBufferClients.size() != 0; + } + + + bool hasClients() const + { + return mHasClients; + } + + virtual void flushEvents() + { + TScopedLockType lock(mBufferMutex); + const uint8_t* theData = mDataArray.begin(); + uint32_t theDataSize = mDataArray.size(); + sendDataToClients(theData, theDataSize); + mDataArray.clear(); + clearCachedData(); + } + + //Used for chaining together event buffers. + virtual void handleBufferFlush( const uint8_t* inData, uint32_t inDataSize ) + { + TScopedLockType lock( mBufferMutex ); + if ( inData && inDataSize ) + { + clearCachedData(); + if ( mDataArray.size() + inDataSize >= mBufferFullAmount ) + flushEvents(); + if ( inDataSize >= mBufferFullAmount ) + sendDataToClients( inData, inDataSize ); + else + mDataArray.write( inData, inDataSize ); + } + } + + protected: + virtual void clearCachedData() + { + } + + private: + + void sendDataToClients( const uint8_t* inData, uint32_t inDataSize ) + { + uint32_t clientCount = mBufferClients.size(); + for( uint32_t idx =0; idx < clientCount; ++idx ) + mBufferClients[idx]->handleBufferFlush( inData, inDataSize ); + } + + }; + +}} + + +#endif // PXPVDSDK_PXPROFILEDATABUFFER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h b/src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h new file mode 100644 index 000000000..a3e87860b --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h @@ -0,0 +1,218 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEDATAPARSING_H +#define PXPVDSDK_PXPROFILEDATAPARSING_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + + //Converts datatypes without using type punning. + struct BlockParserDataConverter + { + union + { + uint8_t mU8[8]; + uint16_t mU16[4]; + uint32_t mU32[2]; + uint64_t mU64[1]; + + int8_t mI8[8]; + int16_t mI16[4]; + int32_t mI32[2]; + int64_t mI64[1]; + + + float mF32[2]; + double mF64[1]; + }; + + template inline TDataType convert() { PX_ASSERT( false ); return TDataType(); } + + template + inline void convert( const TDataType& ) {} + }; + + template<> inline uint8_t BlockParserDataConverter::convert() { return mU8[0]; } + template<> inline uint16_t BlockParserDataConverter::convert() { return mU16[0]; } + template<> inline uint32_t BlockParserDataConverter::convert() { return mU32[0]; } + template<> inline uint64_t BlockParserDataConverter::convert() { return mU64[0]; } + template<> inline int8_t BlockParserDataConverter::convert() { return mI8[0]; } + template<> inline int16_t BlockParserDataConverter::convert() { return mI16[0]; } + template<> inline int32_t BlockParserDataConverter::convert() { return mI32[0]; } + template<> inline int64_t BlockParserDataConverter::convert() { return mI64[0]; } + template<> inline float BlockParserDataConverter::convert() { return mF32[0]; } + template<> inline double BlockParserDataConverter::convert() { return mF64[0]; } + + template<> inline void BlockParserDataConverter::convert( const uint8_t& inData ) { mU8[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const uint16_t& inData ) { mU16[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const uint32_t& inData ) { mU32[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const uint64_t& inData ) { mU64[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const int8_t& inData ) { mI8[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const int16_t& inData ) { mI16[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const int32_t& inData ) { mI32[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const int64_t& inData ) { mI64[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const float& inData ) { mF32[0] = inData; } + template<> inline void BlockParserDataConverter::convert( const double& inData ) { mF64[0] = inData; } + + + //Handles various details around parsing blocks of uint8_t data. + struct BlockParseFunctions + { + template + static inline void swapBytes( uint8_t* inData ) + { + for ( uint32_t idx = 0; idx < ByteCount/2; ++idx ) + { + uint32_t endIdx = ByteCount-idx-1; + uint8_t theTemp = inData[idx]; + inData[idx] = inData[endIdx]; + inData[endIdx] = theTemp; + } + } + + static inline bool checkLength( const uint8_t* inStart, const uint8_t* inStop, uint32_t inLength ) + { + return static_cast(inStop - inStart) >= inLength; + } + //warning work-around + template + static inline T val(T v) {return v;} + + template + static inline bool parse( const uint8_t*& inStart, const uint8_t* inStop, TDataType& outData ) + { + if ( checkLength( inStart, inStop, sizeof( TDataType ) ) ) + { + BlockParserDataConverter theConverter; + for ( uint32_t idx =0; idx < sizeof( TDataType ); ++idx ) + theConverter.mU8[idx] = inStart[idx]; + if ( val(DoSwapBytes)) + swapBytes( theConverter.mU8 ); + outData = theConverter.convert(); + inStart += sizeof( TDataType ); + return true; + } + return false; + } + + template + static inline bool parseBlock( const uint8_t*& inStart, const uint8_t* inStop, TDataType* outData, uint32_t inNumItems ) + { + uint32_t desired = sizeof(TDataType)*inNumItems; + if ( checkLength( inStart, inStop, desired ) ) + { + if ( val(DoSwapBytes) ) + { + for ( uint32_t item = 0; item < inNumItems; ++item ) + { + BlockParserDataConverter theConverter; + for ( uint32_t idx =0; idx < sizeof( TDataType ); ++idx ) + theConverter.mU8[idx] = inStart[idx]; + swapBytes( theConverter.mU8 ); + outData[item] = theConverter.convert(); + inStart += sizeof(TDataType); + } + } + else + { + uint8_t* target = reinterpret_cast(outData); + memmove( target, inStart, desired ); + inStart += desired; + } + return true; + } + return false; + } + + //In-place byte swapping block + template + static inline bool parseBlock( uint8_t*& inStart, const uint8_t* inStop, uint32_t inNumItems ) + { + uint32_t desired = sizeof(TDataType)*inNumItems; + if ( checkLength( inStart, inStop, desired ) ) + { + if ( val(DoSwapBytes) ) + { + for ( uint32_t item = 0; item < inNumItems; ++item, inStart += sizeof( TDataType ) ) + swapBytes( inStart ); //In-place swap. + } + else + inStart += sizeof( TDataType ) * inNumItems; + return true; + } + return false; + } + }; + + //Wraps the begin/end keeping track of them. + template + struct BlockParser + { + const uint8_t* mBegin; + const uint8_t* mEnd; + BlockParser( const uint8_t* inBegin=NULL, const uint8_t* inEnd=NULL ) + : mBegin( inBegin ) + , mEnd( inEnd ) + { + } + inline bool hasMoreData() const { return mBegin != mEnd; } + inline bool checkLength( uint32_t inLength ) { return BlockParseFunctions::checkLength( mBegin, mEnd, inLength ); } + + template + inline bool read( TDataType& outDatatype ) { return BlockParseFunctions::parse( mBegin, mEnd, outDatatype ); } + + template + inline bool readBlock( TDataType* outDataPtr, uint32_t inNumItems ) { return BlockParseFunctions::parseBlock( mBegin, mEnd, outDataPtr, inNumItems ); } + + template + inline bool readBlock( uint32_t inNumItems ) + { + uint8_t* theTempPtr = const_cast(mBegin); + bool retval = BlockParseFunctions::parseBlock( theTempPtr, mEnd, inNumItems ); + mBegin = theTempPtr; + return retval; + } + + uint32_t amountLeft() const { return static_cast( mEnd - mBegin ); } + }; + + //Reads the data without checking for error conditions + template + inline TDataType blockParserRead( TBlockParserType& inType ) + { + TDataType retval; + inType.read( retval ); + return retval; + } +}} + +#endif // PXPVDSDK_PXPROFILEDATAPARSING_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h new file mode 100644 index 000000000..00c82db80 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h @@ -0,0 +1,267 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEEVENTBUFFER_H +#define PXPVDSDK_PXPROFILEEVENTBUFFER_H + +#include "PxProfileEvents.h" +#include "PxProfileEventSerialization.h" +#include "PxProfileDataBuffer.h" +#include "PxProfileContextProvider.h" + +#include "PsTime.h" + +namespace physx { namespace profile { + + /** + * An event buffer maintains an in-memory buffer of events. When this buffer is full + * it sends to buffer to all handlers registered and resets the buffer. + * + * It is parameterized in four ways. The first is a context provider that provides + * both thread id and context id. + * + * The second is the mutex (which may be null) and a scoped locking mechanism. Thus the buffer + * may be used in a multithreaded context but clients of the buffer don't pay for this if they + * don't intend to use it this way. + * + * Finally the buffer may use an event filtering mechanism. This mechanism needs one function, + * namely isEventEnabled( uint8_t subsystem, uint8_t eventId ). + * + * All of these systems can be parameterized at compile time leading to an event buffer + * that should be as fast as possible given the constraints. + * + * Buffers may be chained together as this buffer has a handleBufferFlush method that + * will grab the mutex and add the data to this event buffer. + * + * Overall, lets look at the PhysX SDK an how all the pieces fit together. + * The SDK should have a mutex-protected event buffer where actual devs or users of PhysX + * can register handlers. This buffer has slow but correct implementations of the + * context provider interface. + * + * The SDK object should also have a concrete event filter which was used in the + * construction of the event buffer and which it exposes through opaque interfaces. + * + * The SDK should protect its event buffer and its event filter from multithreaded + * access and thus this provides the safest and slowest way to log events and to + * enable/disable events. + * + * Each scene should also have a concrete event filter. This filter is updated from + * the SDK event filter (in a mutex protected way) every frame. Thus scenes can change + * their event filtering on a frame-by-frame basis. It means that tasks running + * under the scene don't need a mutex when accessing the filter. + * + * Furthermore the scene should have an event buffer that always sets the context id + * on each event to the scene. This allows PVD and other systems to correlate events + * to scenes. Scenes should provide access only to a relative event sending system + * that looks up thread id upon each event but uses the scene id. + * + * The SDK's event buffer should be setup as an EventBufferClient for each scene's + * event buffer. Thus the SDK should expose an EventBufferClient interface that + * any client can use. + * + * For extremely *extremely* performance sensitive areas we should create a specialized + * per-scene, per-thread event buffer that is set on the task for these occasions. This buffer + * uses a trivial event context setup with the scene's context id and the thread id. It should + * share the scene's concrete event filter and it should have absolutely no locking. It should + * empty into the scene's event buffer which in some cases should empty into the SDK's event buffer + * which when full will push events all the way out of the system. The task should *always* flush + * the event buffer (if it has one) when it is finished; nothing else will work reliably. + * + * If the per-scene,per-thread event buffer is correctly parameterized and fully defined adding + * a new event should be an inline operation requiring no mutex grabs in the common case. I don't + * believe you can get faster event production than this; the events are as small as possible (all + * relative events) and they are all produced inline resulting in one 4 byte header and one + * 8 byte timestamp per event. Reducing the memory pressure in this way reduces the communication + * overhead, the mutex grabs, basically everything that makes profiling expensive at the cost + * of a per-scene,per-thread event buffer (which could easily be reduced to a per-thread event + * buffer. + */ + template + class EventBuffer : public DataBuffer + { + public: + typedef DataBuffer TBaseType; + typedef TContextProvider TContextProviderType; + typedef TEventFilter TEventFilterType; + typedef typename TBaseType::TMutexType TMutexType; + typedef typename TBaseType::TScopedLockType TScopedLockType; + typedef typename TBaseType::TU8AllocatorType TU8AllocatorType; + typedef typename TBaseType::TMemoryBufferType TMemoryBufferType; + typedef typename TBaseType::TBufferClientArray TBufferClientArray; + + private: + EventContextInformation mEventContextInformation; + uint64_t mLastTimestamp; + TContextProvider mContextProvider; + TEventFilterType mEventFilter; + + public: + EventBuffer(PxAllocatorCallback* inFoundation + , uint32_t inBufferFullAmount + , const TContextProvider& inProvider + , TMutexType* inBufferMutex + , const TEventFilterType& inEventFilter ) + : TBaseType( inFoundation, inBufferFullAmount, inBufferMutex, "struct physx::profile::ProfileEvent" ) + , mLastTimestamp( 0 ) + , mContextProvider( inProvider ) + , mEventFilter( inEventFilter ) + { + memset(&mEventContextInformation,0,sizeof(EventContextInformation)); + } + + TContextProvider& getContextProvider() { return mContextProvider; } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint32_t threadId, uint64_t contextId, uint8_t cpuId, uint8_t threadPriority, uint64_t inTimestamp) + { + TScopedLockType lock(TBaseType::mBufferMutex); + if ( mEventFilter.isEventEnabled( inId ) ) + { + StartEvent theEvent; + theEvent.init( threadId, contextId, cpuId, threadPriority, inTimestamp ); + doAddProfileEvent( inId, theEvent ); + } + } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint64_t contextId) + { + PxProfileEventExecutionContext ctx( mContextProvider.getExecutionContext() ); + startEvent( inId, ctx.mThreadId, contextId, ctx.mCpuId, static_cast(ctx.mThreadPriority), shdfnd::Time::getCurrentCounterValue() ); + } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint64_t contextId, uint32_t threadId) + { + startEvent( inId, threadId, contextId, 0, 0, shdfnd::Time::getCurrentCounterValue() ); + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint32_t threadId, uint64_t contextId, uint8_t cpuId, uint8_t threadPriority, uint64_t inTimestamp) + { + TScopedLockType lock(TBaseType::mBufferMutex); + if ( mEventFilter.isEventEnabled( inId ) ) + { + StopEvent theEvent; + theEvent.init( threadId, contextId, cpuId, threadPriority, inTimestamp ); + doAddProfileEvent( inId, theEvent ); + } + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint64_t contextId) + { + PxProfileEventExecutionContext ctx( mContextProvider.getExecutionContext() ); + stopEvent( inId, ctx.mThreadId, contextId, ctx.mCpuId, static_cast(ctx.mThreadPriority), shdfnd::Time::getCurrentCounterValue() ); + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint64_t contextId, uint32_t threadId) + { + stopEvent( inId, threadId, contextId, 0, 0, shdfnd::Time::getCurrentCounterValue() ); + } + + inline void eventValue( uint16_t inId, uint64_t contextId, int64_t inValue ) + { + eventValue( inId, mContextProvider.getThreadId(), contextId, inValue ); + } + + inline void eventValue( uint16_t inId, uint32_t threadId, uint64_t contextId, int64_t inValue ) + { + TScopedLockType lock( TBaseType::mBufferMutex ); + EventValue theEvent; + theEvent.init( inValue, contextId, threadId ); + EventHeader theHeader( static_cast( getEventType() ), inId ); + //set the header relative timestamp; + EventValue& theType( theEvent ); + theType.setupHeader( theHeader ); + sendEvent( theHeader, theType ); + } + + void flushProfileEvents() + { + TBaseType::flushEvents(); + } + + void release() + { + PX_PROFILE_DELETE( TBaseType::mWrapper.mUserFoundation, this ); + } + protected: + //Clears the cache meaning event compression + //starts over again. + //only called when the buffer mutex is held + void clearCachedData() + { + mEventContextInformation.setToDefault(); + mLastTimestamp = 0; + } + + template + PX_FORCE_INLINE void doAddProfileEvent(uint16_t eventId, const TProfileEventType& inType) + { + TScopedLockType lock(TBaseType::mBufferMutex); + if (mEventContextInformation == inType.mContextInformation) + doAddEvent(static_cast(inType.getRelativeEventType()), eventId, inType.getRelativeEvent()); + else + { + mEventContextInformation = inType.mContextInformation; + doAddEvent( static_cast( getEventType() ), eventId, inType ); + } + } + + template + PX_FORCE_INLINE void doAddEvent(uint8_t inEventType, uint16_t eventId, const TDataType& inType) + { + EventHeader theHeader( inEventType, eventId ); + //set the header relative timestamp; + TDataType& theType( const_cast( inType ) ); + uint64_t currentTs = inType.getTimestamp(); + theType.setupHeader(theHeader, mLastTimestamp); + mLastTimestamp = currentTs; + sendEvent( theHeader, theType ); + } + + template + PX_FORCE_INLINE void sendEvent( EventHeader& inHeader, TDataType& inType ) + { + uint32_t sizeToWrite = sizeof(inHeader) + inType.getEventSize(inHeader); + PX_UNUSED(sizeToWrite); + + uint32_t writtenSize = inHeader.streamify( TBaseType::mSerializer ); + writtenSize += inType.streamify(TBaseType::mSerializer, inHeader); + + PX_ASSERT(writtenSize == sizeToWrite); + + if ( TBaseType::mDataArray.size() >= TBaseType::mBufferFullAmount ) + flushProfileEvents(); + + } + + }; +}} +#endif // PXPVDSDK_PXPROFILEEVENTBUFFER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h new file mode 100644 index 000000000..0d252f23e --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h @@ -0,0 +1,318 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEEVENTBUFFERATOMIC_H +#define PXPVDSDK_PXPROFILEEVENTBUFFERATOMIC_H + +#include "PxProfileEvents.h" +#include "PxProfileEventSerialization.h" +#include "PxProfileDataBuffer.h" + +#include "PsArray.h" +#include "PsAlloca.h" +#include "PsTime.h" +#include "PsCpu.h" +#include "PsAtomic.h" +#include "PsAllocator.h" + + +namespace physx { + namespace profile { + + static const uint32_t LOCAL_BUFFER_SIZE = 512; + + /** + * An event buffer maintains an in-memory buffer of events. When this buffer is full + * it sends to buffer to all handlers registered and resets the buffer. + * + * It is parameterized in four ways. The first is a context provider that provides + * both thread id and context id. + * + * The second is the mutex (which may be null) and a scoped locking mechanism. Thus the buffer + * may be used in a multithreaded context but clients of the buffer don't pay for this if they + * don't intend to use it this way. + * + * Finally the buffer may use an event filtering mechanism. This mechanism needs one function, + * namely isEventEnabled( uint8_t subsystem, uint8_t eventId ). + * + * All of these systems can be parameterized at compile time leading to an event buffer + * that should be as fast as possible given the constraints. + * + * Buffers may be chained together as this buffer has a handleBufferFlush method that + * will grab the mutex and add the data to this event buffer. + * + * Overall, lets look at the PhysX SDK an how all the pieces fit together. + * The SDK should have a mutex-protected event buffer where actual devs or users of PhysX + * can register handlers. This buffer has slow but correct implementations of the + * context provider interface. + * + * The SDK object should also have a concrete event filter which was used in the + * construction of the event buffer and which it exposes through opaque interfaces. + * + * The SDK should protect its event buffer and its event filter from multithreaded + * access and thus this provides the safest and slowest way to log events and to + * enable/disable events. + * + * Each scene should also have a concrete event filter. This filter is updated from + * the SDK event filter (in a mutex protected way) every frame. Thus scenes can change + * their event filtering on a frame-by-frame basis. It means that tasks running + * under the scene don't need a mutex when accessing the filter. + * + * Furthermore the scene should have an event buffer that always sets the context id + * on each event to the scene. This allows PVD and other systems to correlate events + * to scenes. Scenes should provide access only to a relative event sending system + * that looks up thread id upon each event but uses the scene id. + * + * The SDK's event buffer should be setup as an EventBufferClient for each scene's + * event buffer. Thus the SDK should expose an EventBufferClient interface that + * any client can use. + * + * For extremely *extremely* performance sensitive areas we should create a specialized + * per-scene, per-thread event buffer that is set on the task for these occasions. This buffer + * uses a trivial event context setup with the scene's context id and the thread id. It should + * share the scene's concrete event filter and it should have absolutely no locking. It should + * empty into the scene's event buffer which in some cases should empty into the SDK's event buffer + * which when full will push events all the way out of the system. The task should *always* flush + * the event buffer (if it has one) when it is finished; nothing else will work reliably. + * + * If the per-scene,per-thread event buffer is correctly parameterized and fully defined adding + * a new event should be an inline operation requiring no mutex grabs in the common case. I don't + * believe you can get faster event production than this; the events are as small as possible (all + * relative events) and they are all produced inline resulting in one 4 byte header and one + * 8 byte timestamp per event. Reducing the memory pressure in this way reduces the communication + * overhead, the mutex grabs, basically everything that makes profiling expensive at the cost + * of a per-scene,per-thread event buffer (which could easily be reduced to a per-thread event + * buffer. + */ + template + class EventBufferAtomic : public DataBuffer < TMutex, TScopedLock > + { + public: + typedef DataBuffer TBaseType; + typedef TContextProvider TContextProviderType; + typedef TEventFilter TEventFilterType; + typedef typename TBaseType::TMutexType TMutexType; + typedef typename TBaseType::TScopedLockType TScopedLockType; + typedef typename TBaseType::TU8AllocatorType TU8AllocatorType; + typedef typename TBaseType::TMemoryBufferType TMemoryBufferType; + typedef typename TBaseType::TBufferClientArray TBufferClientArray; + + private: + TContextProvider mContextProvider; + TEventFilterType mEventFilter; + volatile int32_t mReserved; + volatile int32_t mWritten; + + public: + EventBufferAtomic(PxAllocatorCallback* inFoundation + , uint32_t inBufferFullAmount + , const TContextProvider& inProvider + , TMutexType* inBufferMutex + , const TEventFilterType& inEventFilter) + : TBaseType(inFoundation, inBufferFullAmount, inBufferMutex, "struct physx::profile::ProfileEvent") + , mContextProvider(inProvider) + , mEventFilter(inEventFilter) + , mReserved(0) + , mWritten(0) + { + } + + TContextProvider& getContextProvider() { return mContextProvider; } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint32_t threadId, uint64_t contextId, uint8_t cpuId, uint8_t threadPriority, uint64_t inTimestamp) + { + if (mEventFilter.isEventEnabled(inId)) + { + StartEvent theEvent; + theEvent.init(threadId, contextId, cpuId, threadPriority, inTimestamp); + doAddProfileEvent(inId, theEvent); + } + } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint64_t contextId) + { + PxProfileEventExecutionContext ctx(mContextProvider.getExecutionContext()); + startEvent(inId, ctx.mThreadId, contextId, ctx.mCpuId, static_cast(ctx.mThreadPriority), shdfnd::Time::getCurrentCounterValue()); + } + + PX_FORCE_INLINE void startEvent(uint16_t inId, uint64_t contextId, uint32_t threadId) + { + startEvent(inId, threadId, contextId, 0, 0, shdfnd::Time::getCurrentCounterValue()); + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint32_t threadId, uint64_t contextId, uint8_t cpuId, uint8_t threadPriority, uint64_t inTimestamp) + { + if (mEventFilter.isEventEnabled(inId)) + { + StopEvent theEvent; + theEvent.init(threadId, contextId, cpuId, threadPriority, inTimestamp); + doAddProfileEvent(inId, theEvent); + } + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint64_t contextId) + { + PxProfileEventExecutionContext ctx(mContextProvider.getExecutionContext()); + stopEvent(inId, ctx.mThreadId, contextId, ctx.mCpuId, static_cast(ctx.mThreadPriority), shdfnd::Time::getCurrentCounterValue()); + } + + PX_FORCE_INLINE void stopEvent(uint16_t inId, uint64_t contextId, uint32_t threadId) + { + stopEvent(inId, threadId, contextId, 0, 0, shdfnd::Time::getCurrentCounterValue()); + } + + inline void eventValue(uint16_t inId, uint64_t contextId, int64_t inValue) + { + eventValue(inId, mContextProvider.getThreadId(), contextId, inValue); + } + + inline void eventValue(uint16_t inId, uint32_t threadId, uint64_t contextId, int64_t inValue) + { + EventValue theEvent; + theEvent.init(inValue, contextId, threadId); + EventHeader theHeader(static_cast(getEventType()), inId); + //set the header relative timestamp; + EventValue& theType(theEvent); + theType.setupHeader(theHeader); + + int32_t sizeToWrite = int32_t(sizeof(theHeader) + theType.getEventSize(theHeader)); + int32_t reserved = shdfnd::atomicAdd(&mReserved, sizeToWrite); + sendEvent(theHeader, theType, reserved, sizeToWrite); + } + + void flushProfileEvents(int32_t reserved = -1) + { + TScopedLockType lock(TBaseType::mBufferMutex); + + // set the buffer full to lock additional writes + int32_t reservedOld = shdfnd::atomicExchange(&mReserved, int32_t(TBaseType::mBufferFullAmount + 1)); + if (reserved == -1) + reserved = reservedOld; + + // spin till we have written all the data + while (reserved > mWritten) + { + } + + // check if we have written all data + PX_ASSERT(reserved == mWritten); + + // set the correct size of the serialization data buffer + TBaseType::mSerializer.mArray->setEnd(TBaseType::mSerializer.mArray->begin() + mWritten); + + // flush events + TBaseType::flushEvents(); + + // write master timestamp and set reserved/written to start writing to buffer again + mWritten = 0; + mReserved = 0; + } + + void release() + { + PX_PROFILE_DELETE(TBaseType::mWrapper.mUserFoundation, this); + } + protected: + //Clears the cache meaning event compression + //starts over again. + //only called when the buffer mutex is held + void clearCachedData() + { + } + + template + PX_FORCE_INLINE void doAddProfileEvent(uint16_t eventId, const TProfileEventType& inType) + { + doAddEvent(static_cast(getEventType()), eventId, inType); + } + + template + PX_FORCE_INLINE void doAddEvent(uint8_t inEventType, uint16_t eventId, const TDataType& inType) + { + EventHeader theHeader(inEventType, eventId); + TDataType& theType(const_cast(inType)); + theType.setupHeader(theHeader, 0); + + const int32_t sizeToWrite = int32_t(sizeof(theHeader) + theType.getEventSize(theHeader)); + + int32_t reserved = shdfnd::atomicAdd(&mReserved, sizeToWrite); + sendEvent(theHeader, theType, reserved, sizeToWrite); + } + + template + PX_FORCE_INLINE void sendEvent(EventHeader& inHeader, TDataType& inType, int32_t reserved, int32_t sizeToWrite) + { + // if we don't fit to the buffer, we wait till it is flushed + if (reserved - sizeToWrite >= int32_t(TBaseType::mBufferFullAmount)) + { + while (reserved - sizeToWrite >= int32_t(TBaseType::mBufferFullAmount)) + { + // I32 overflow + if (mReserved < int32_t(TBaseType::mBufferFullAmount)) + { + reserved = shdfnd::atomicAdd(&mReserved, sizeToWrite); + } + } + } + + int32_t writeIndex = reserved - sizeToWrite; + uint32_t writtenSize = 0; + + PX_ASSERT(writeIndex >= 0); + + PX_ALLOCA(tempBuffer, uint8_t, sizeToWrite); + TempMemoryBuffer memoryBuffer(tempBuffer, sizeToWrite); + EventSerializer eventSerializer(&memoryBuffer); + + writtenSize = inHeader.streamify(eventSerializer); + writtenSize += inType.streamify(eventSerializer, inHeader); + + TBaseType::mSerializer.mArray->reserve(writeIndex + writtenSize); + TBaseType::mSerializer.mArray->write(&tempBuffer[0], writtenSize, writeIndex); + + PX_ASSERT(writtenSize == uint32_t(sizeToWrite)); + shdfnd::atomicAdd(&mWritten, sizeToWrite); + + if (reserved >= int32_t(TBaseType::mBufferFullAmount)) + { + TScopedLockType lock(TBaseType::mBufferMutex); + // we flush the buffer if its full and we did not flushed him in the meantime + if(mReserved >= reserved) + flushProfileEvents(reserved); + } + } + + }; + } +} +#endif // PXPVDSDK_PXPROFILEEVENTBUFFERATOMIC_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h new file mode 100644 index 000000000..869fdbd36 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h @@ -0,0 +1,80 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTBUFFERCLIENT_H +#define PXPVDSDK_PXPROFILEEVENTBUFFERCLIENT_H + +#include "PxProfileEventNames.h" + +namespace physx { namespace profile { + + /** + \brief Client handles the data when an event buffer flushes. This data + can be parsed (PxProfileEventHandler.h) as a binary set of events. + */ + class PxProfileEventBufferClient + { + protected: + virtual ~PxProfileEventBufferClient(){} + public: + /** + \brief Callback when the event buffer is full. This data is serialized profile events + and can be read back using: PxProfileEventHandler::parseEventBuffer. + + \param inData Provided buffer data. + \param inLength Data length. + + @see PxProfileEventHandler::parseEventBuffer. + */ + virtual void handleBufferFlush( const uint8_t* inData, uint32_t inLength ) = 0; + + /** + \brief Happens if something removes all the clients from the manager. + */ + virtual void handleClientRemoved() = 0; + }; + + /** + \brief Client handles new profile event add. + */ + class PxProfileZoneClient : public PxProfileEventBufferClient + { + protected: + virtual ~PxProfileZoneClient(){} + public: + /** + \brief Callback when new profile event is added. + + \param inName Added profile event name. + */ + virtual void handleEventAdded( const PxProfileEventName& inName ) = 0; + }; + +} } + + +#endif // PXPVDSDK_PXPROFILEEVENTBUFFERCLIENT_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h new file mode 100644 index 000000000..86ed6fb24 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h @@ -0,0 +1,94 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTBUFFERCLIENTMANAGER_H +#define PXPVDSDK_PXPROFILEEVENTBUFFERCLIENTMANAGER_H + +#include "PxProfileEventBufferClient.h" + +namespace physx { namespace profile { + + /** + \brief Manager keep collections of PxProfileEventBufferClient clients. + + @see PxProfileEventBufferClient + */ + class PxProfileEventBufferClientManager + { + protected: + virtual ~PxProfileEventBufferClientManager(){} + public: + /** + \brief Adds new client. + \param inClient Client to add. + */ + virtual void addClient( PxProfileEventBufferClient& inClient ) = 0; + + /** + \brief Removes a client. + \param inClient Client to remove. + */ + virtual void removeClient( PxProfileEventBufferClient& inClient ) = 0; + + /** + \brief Check if manager has clients. + \return True if manager has added clients. + */ + virtual bool hasClients() const = 0; + }; + + /** + \brief Manager keep collections of PxProfileZoneClient clients. + + @see PxProfileZoneClient + */ + class PxProfileZoneClientManager + { + protected: + virtual ~PxProfileZoneClientManager(){} + public: + /** + \brief Adds new client. + \param inClient Client to add. + */ + virtual void addClient( PxProfileZoneClient& inClient ) = 0; + + /** + \brief Removes a client. + \param inClient Client to remove. + */ + virtual void removeClient( PxProfileZoneClient& inClient ) = 0; + + /** + \brief Check if manager has clients. + \return True if manager has added clients. + */ + virtual bool hasClients() const = 0; + }; +} } + +#endif // PXPVDSDK_PXPROFILEEVENTBUFFERCLIENTMANAGER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventId.h b/src/PhysX/physx/source/pvd/src/PxProfileEventId.h new file mode 100644 index 000000000..260abd55d --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventId.h @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTID_H +#define PXPVDSDK_PXPROFILEEVENTID_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + /** + \brief A event id structure. Optionally includes information about + if the event was enabled at compile time. + */ + struct PxProfileEventId + { + uint16_t eventId; + mutable bool compileTimeEnabled; + + /** + \brief Profile event id constructor. + \param inId Profile event id. + \param inCompileTimeEnabled Compile time enabled. + */ + PxProfileEventId( uint16_t inId = 0, bool inCompileTimeEnabled = true ) + : eventId( inId ) + , compileTimeEnabled( inCompileTimeEnabled ) + { + } + + operator uint16_t () const { return eventId; } + + bool operator==( const PxProfileEventId& inOther ) const + { + return eventId == inOther.eventId; + } + }; + +} } + +#endif // PXPVDSDK_PXPROFILEEVENTID_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp b/src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp new file mode 100644 index 000000000..8b037e8e2 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxProfileEventBuffer.h" +#include "PxProfileZoneImpl.h" +#include "PxProfileZoneManagerImpl.h" +#include "PxProfileMemoryEventBuffer.h" +#include "PsUserAllocated.h" + +namespace physx { namespace profile { + + struct PxProfileNameProviderForward + { + PxProfileNames mNames; + PxProfileNameProviderForward( PxProfileNames inNames ) + : mNames( inNames ) + { + } + PxProfileNames getProfileNames() const { return mNames; } + }; + + PxProfileZone& PxProfileZone::createProfileZone( PxAllocatorCallback* inAllocator, const char* inSDKName, PxProfileNames inNames, uint32_t inEventBufferByteSize ) + { + typedef ZoneImpl TSDKType; + return *PX_PROFILE_NEW( inAllocator, TSDKType ) ( inAllocator, inSDKName, inEventBufferByteSize, PxProfileNameProviderForward( inNames ) ); + } + + PxProfileZoneManager& PxProfileZoneManager::createProfileZoneManager(PxAllocatorCallback* inAllocator ) + { + return *PX_PROFILE_NEW( inAllocator, ZoneManagerImpl ) ( inAllocator ); + } + + PxProfileMemoryEventBuffer& PxProfileMemoryEventBuffer::createMemoryEventBuffer( PxAllocatorCallback& inAllocator, uint32_t inBufferSize ) + { + return *PX_PROFILE_NEW( &inAllocator, PxProfileMemoryEventBufferImpl )( inAllocator, inBufferSize ); + } + +} } + diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h b/src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h new file mode 100644 index 000000000..287cb57e2 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEEVENTMUTEX_H +#define PXPVDSDK_PXPROFILEEVENTMUTEX_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + + /** + * Mutex interface that hides implementation around lock and unlock. + * The event system locks the mutex for every interaction. + */ + class PxProfileEventMutex + { + protected: + virtual ~PxProfileEventMutex(){} + public: + virtual void lock() = 0; + virtual void unlock() = 0; + }; + + /** + * Take any mutex type that implements lock and unlock and make an EventMutex out of it. + */ + template + struct PxProfileEventMutexImpl : public PxProfileEventMutex + { + TMutexType* mMutex; + PxProfileEventMutexImpl( TMutexType* inMtx ) : mMutex( inMtx ) {} + virtual void lock() { mMutex->lock(); } + virtual void unlock() { mMutex->unlock(); } + }; + +} } + +#endif // PXPVDSDK_PXPROFILEEVENTMUTEX_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventNames.h b/src/PhysX/physx/source/pvd/src/PxProfileEventNames.h new file mode 100644 index 000000000..508252b9e --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventNames.h @@ -0,0 +1,89 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTNAMES_H +#define PXPVDSDK_PXPROFILEEVENTNAMES_H + +#include "PxProfileEventId.h" + +namespace physx { namespace profile { + + /** + \brief Mapping from event id to name. + */ + struct PxProfileEventName + { + const char* name; + PxProfileEventId eventId; + + /** + \brief Default constructor. + \param inName Profile event name. + \param inId Profile event id. + */ + PxProfileEventName( const char* inName, PxProfileEventId inId ) : name( inName ), eventId( inId ) {} + }; + + /** + \brief Aggregator of event id -> name mappings + */ + struct PxProfileNames + { + /** + \brief Default constructor that doesn't point to any names. + \param inEventCount Number of provided events. + \param inSubsystems Event names array. + */ + PxProfileNames( uint32_t inEventCount = 0, const PxProfileEventName* inSubsystems = NULL ) + : eventCount( inEventCount ) + , events( inSubsystems ) + { + } + + uint32_t eventCount; + const PxProfileEventName* events; + }; + + /** + \brief Provides a mapping from event ID -> name. + */ + class PxProfileNameProvider + { + public: + /** + \brief Returns profile event names. + \return Profile event names. + */ + virtual PxProfileNames getProfileNames() const = 0; + + protected: + virtual ~PxProfileNameProvider(){} + PxProfileNameProvider& operator=(const PxProfileNameProvider&) { return *this; } + }; +} } + +#endif // PXPVDSDK_PXPROFILEEVENTNAMES_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventSender.h b/src/PhysX/physx/source/pvd/src/PxProfileEventSender.h new file mode 100644 index 000000000..ee80096b6 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventSender.h @@ -0,0 +1,111 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTSENDER_H +#define PXPVDSDK_PXPROFILEEVENTSENDER_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + + /** + \brief Tagging interface to indicate an object that is capable of flushing a profile + event stream at a certain point. + */ + class PxProfileEventFlusher + { + protected: + virtual ~PxProfileEventFlusher(){} + public: + /** + \brief Flush profile events. Sends the profile event buffer to hooked clients. + */ + virtual void flushProfileEvents() = 0; + }; + + /** + \brief Sends the full events where the caller must provide the context and thread id. + */ + class PxProfileEventSender + { + protected: + virtual ~PxProfileEventSender(){} + public: + + /** + \brief Use this as a thread id for events that start on one thread and end on another + */ + static const uint32_t CrossThreadId = 99999789; + + /** + \brief Send a start profile event, optionally with a context. Events are sorted by thread + and context in the client side. + \param inId Profile event id. + \param contextId Context id. + */ + virtual void startEvent( uint16_t inId, uint64_t contextId) = 0; + /** + \brief Send a stop profile event, optionally with a context. Events are sorted by thread + and context in the client side. + \param inId Profile event id. + \param contextId Context id. + */ + virtual void stopEvent( uint16_t inId, uint64_t contextId) = 0; + + /** + \brief Send a start profile event, optionally with a context. Events are sorted by thread + and context in the client side. + \param inId Profile event id. + \param contextId Context id. + \param threadId Thread id. + */ + virtual void startEvent( uint16_t inId, uint64_t contextId, uint32_t threadId) = 0; + /** + \brief Send a stop profile event, optionally with a context. Events are sorted by thread + and context in the client side. + \param inId Profile event id. + \param contextId Context id. + \param threadId Thread id. + */ + virtual void stopEvent( uint16_t inId, uint64_t contextId, uint32_t threadId ) = 0; + + virtual void atEvent(uint16_t inId, uint64_t contextId, uint32_t threadId, uint64_t start, uint64_t stop) = 0; + + /** + \brief Set an specific events value. This is different than the profiling value + for the event; it is a value recorded and kept around without a timestamp associated + with it. This value is displayed when the event itself is processed. + \param inId Profile event id. + \param contextId Context id. + \param inValue Value to set for the event. + */ + virtual void eventValue( uint16_t inId, uint64_t contextId, int64_t inValue ) = 0; + }; + +} } + +#endif // PXPVDSDK_PXPROFILEEVENTSENDER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h b/src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h new file mode 100644 index 000000000..ca15a019e --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h @@ -0,0 +1,257 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEEVENTSERIALIZATION_H +#define PXPVDSDK_PXPROFILEEVENTSERIALIZATION_H + +#include "PxProfileDataParsing.h" +#include "PxProfileEvents.h" + +namespace physx { namespace profile { + + /** + * Array type must be a pxu8 container. Templated so that this object can write + * to different collections. + */ + + template + struct EventSerializer + { + TArrayType* mArray; + EventSerializer( TArrayType* inA ) : mArray( inA ) {} + + template + uint32_t streamify( const char*, const TDataType& inType ) + { + return mArray->write( inType ); + } + + uint32_t streamify( const char*, const char*& inType ) + { + PX_ASSERT( inType != NULL ); + uint32_t len( static_cast( strlen( inType ) ) ); + ++len; //include the null terminator + uint32_t writtenSize = 0; + writtenSize = mArray->write(len); + writtenSize += mArray->write(inType, len); + return writtenSize; + } + + uint32_t streamify( const char*, const uint8_t* inData, uint32_t len ) + { + uint32_t writtenSize = mArray->write(len); + if ( len ) + writtenSize += mArray->write(inData, len); + return writtenSize; + } + + uint32_t streamify( const char* nm, const uint64_t& inType, EventStreamCompressionFlags::Enum inFlags ) + { + uint32_t writtenSize = 0; + switch( inFlags ) + { + case EventStreamCompressionFlags::U8: + writtenSize = streamify(nm, static_cast(inType)); + break; + case EventStreamCompressionFlags::U16: + writtenSize = streamify(nm, static_cast(inType)); + break; + case EventStreamCompressionFlags::U32: + writtenSize = streamify(nm, static_cast(inType)); + break; + case EventStreamCompressionFlags::U64: + writtenSize = streamify(nm, inType); + break; + } + return writtenSize; + } + + uint32_t streamify( const char* nm, const uint32_t& inType, EventStreamCompressionFlags::Enum inFlags ) + { + uint32_t writtenSize = 0; + switch( inFlags ) + { + case EventStreamCompressionFlags::U8: + writtenSize = streamify(nm, static_cast(inType)); + break; + case EventStreamCompressionFlags::U16: + writtenSize = streamify(nm, static_cast(inType)); + break; + case EventStreamCompressionFlags::U32: + case EventStreamCompressionFlags::U64: + writtenSize = streamify(nm, inType); + break; + } + return writtenSize; + } + }; + + /** + * The event deserializes takes a buffer implements the streamify functions + * by setting the passed in data to the data in the buffer. + */ + template + struct EventDeserializer + { + const uint8_t* mData; + uint32_t mLength; + bool mFail; + + EventDeserializer( const uint8_t* inData, uint32_t inLength ) + : mData( inData ) + , mLength( inLength ) + , mFail( false ) + { + if ( mData == NULL ) + mLength = 0; + } + + bool val() { return TSwapBytes; } + + uint32_t streamify( const char* , uint8_t& inType ) + { + uint8_t* theData = reinterpret_cast( &inType ); //type punned pointer... + if ( mFail || sizeof( inType ) > mLength ) + { + PX_ASSERT( false ); + mFail = true; + } + else + { + for( uint32_t idx = 0; idx < sizeof( uint8_t ); ++idx, ++mData, --mLength ) + theData[idx] = *mData; + } + return 0; + } + + //default streamify reads things natively as bytes. + template + uint32_t streamify( const char* , TDataType& inType ) + { + uint8_t* theData = reinterpret_cast( &inType ); //type punned pointer... + if ( mFail || sizeof( inType ) > mLength ) + { + PX_ASSERT( false ); + mFail = true; + } + else + { + for( uint32_t idx = 0; idx < sizeof( TDataType ); ++idx, ++mData, --mLength ) + theData[idx] = *mData; + bool temp = val(); + if ( temp ) + BlockParseFunctions::swapBytes( theData ); + } + return 0; + } + + uint32_t streamify( const char*, const char*& inType ) + { + uint32_t theLen; + streamify( "", theLen ); + theLen = PxMin( theLen, mLength ); + inType = reinterpret_cast( mData ); + mData += theLen; + mLength -= theLen; + return 0; + } + + uint32_t streamify( const char*, const uint8_t*& inData, uint32_t& len ) + { + uint32_t theLen; + streamify( "", theLen ); + theLen = PxMin( theLen, mLength ); + len = theLen; + inData = reinterpret_cast( mData ); + mData += theLen; + mLength -= theLen; + return 0; + } + + uint32_t streamify( const char* nm, uint64_t& inType, EventStreamCompressionFlags::Enum inFlags ) + { + switch( inFlags ) + { + case EventStreamCompressionFlags::U8: + { + uint8_t val=0; + streamify( nm, val ); + inType = val; + } + break; + case EventStreamCompressionFlags::U16: + { + uint16_t val; + streamify( nm, val ); + inType = val; + } + break; + case EventStreamCompressionFlags::U32: + { + uint32_t val; + streamify( nm, val ); + inType = val; + } + break; + case EventStreamCompressionFlags::U64: + streamify( nm, inType ); + break; + } + return 0; + } + + uint32_t streamify( const char* nm, uint32_t& inType, EventStreamCompressionFlags::Enum inFlags ) + { + switch( inFlags ) + { + case EventStreamCompressionFlags::U8: + { + uint8_t val=0; + streamify( nm, val ); + inType = val; + } + break; + case EventStreamCompressionFlags::U16: + { + uint16_t val=0; + streamify( nm, val ); + inType = val; + } + break; + case EventStreamCompressionFlags::U32: + case EventStreamCompressionFlags::U64: + streamify( nm, inType ); + break; + } + return 0; + } + }; +}} +#endif // PXPVDSDK_PXPROFILEEVENTSERIALIZATION_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEvents.h b/src/PhysX/physx/source/pvd/src/PxProfileEvents.h new file mode 100644 index 000000000..9bf02ed8c --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileEvents.h @@ -0,0 +1,705 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEEVENTS_H +#define PXPVDSDK_PXPROFILEEVENTS_H + +#include "foundation/PxMath.h" +#include "foundation/PxAssert.h" + +#include "PxProfileEventId.h" + + +#define PX_PROFILE_UNION_1(a) physx::profile::TUnion +#define PX_PROFILE_UNION_2(a,b) physx::profile::TUnion +#define PX_PROFILE_UNION_3(a,b,c) physx::profile::TUnion +#define PX_PROFILE_UNION_4(a,b,c,d) physx::profile::TUnion +#define PX_PROFILE_UNION_5(a,b,c,d,e) physx::profile::TUnion +#define PX_PROFILE_UNION_6(a,b,c,d,e,f) physx::profile::TUnion +#define PX_PROFILE_UNION_7(a,b,c,d,e,f,g) physx::profile::TUnion +#define PX_PROFILE_UNION_8(a,b,c,d,e,f,g,h) physx::profile::TUnion +#define PX_PROFILE_UNION_9(a,b,c,d,e,f,g,h,i) physx::profile::TUnion + +namespace physx { namespace profile { + + struct Empty {}; + + template struct Type2Type {}; + + template + union TUnion + { + typedef U Head; + typedef V Tail; + + Head head; + Tail tail; + + template + void init(const TDataType& inData) + { + toType(Type2Type()).init(inData); + } + + template + PX_FORCE_INLINE TDataType& toType(const Type2Type& outData) { return tail.toType(outData); } + + PX_FORCE_INLINE Head& toType(const Type2Type&) { return head; } + + template + PX_FORCE_INLINE const TDataType& toType(const Type2Type& outData) const { return tail.toType(outData); } + + PX_FORCE_INLINE const Head& toType(const Type2Type&) const { return head; } + }; + + struct EventTypes + { + enum Enum + { + Unknown = 0, + StartEvent, + StopEvent, + RelativeStartEvent, //reuses context,id from the earlier event. + RelativeStopEvent, //reuses context,id from the earlier event. + EventValue, + CUDAProfileBuffer //obsolete, placeholder to skip data from PhysX SDKs < 3.4 + }; + }; + + struct EventStreamCompressionFlags + { + enum Enum + { + U8 = 0, + U16 = 1, + U32 = 2, + U64 = 3, + CompressionMask = 3 + }; + }; + +#if (PX_PS4) || (PX_APPLE_FAMILY) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#endif + + //Find the smallest value that will represent the incoming value without loss. + //We can enlarge the current compression value, but we can't make is smaller. + //In this way, we can use this function to find the smallest compression setting + //that will work for a set of values. + inline EventStreamCompressionFlags::Enum findCompressionValue( uint64_t inValue, EventStreamCompressionFlags::Enum inCurrentCompressionValue = EventStreamCompressionFlags::U8 ) + { + PX_ASSERT_WITH_MESSAGE( (inCurrentCompressionValue >= EventStreamCompressionFlags::U8) && + (inCurrentCompressionValue <= EventStreamCompressionFlags::U64), + "Invalid inCurrentCompressionValue in profile::findCompressionValue"); + + //Fallthrough is intentional + switch( inCurrentCompressionValue ) + { + case EventStreamCompressionFlags::U8: + if ( inValue <= UINT8_MAX ) + return EventStreamCompressionFlags::U8; + case EventStreamCompressionFlags::U16: + if ( inValue <= UINT16_MAX ) + return EventStreamCompressionFlags::U16; + case EventStreamCompressionFlags::U32: + if ( inValue <= UINT32_MAX ) + return EventStreamCompressionFlags::U32; + case EventStreamCompressionFlags::U64: + break; + } + return EventStreamCompressionFlags::U64; + } + + //Find the smallest value that will represent the incoming value without loss. + //We can enlarge the current compression value, but we can't make is smaller. + //In this way, we can use this function to find the smallest compression setting + //that will work for a set of values. + inline EventStreamCompressionFlags::Enum findCompressionValue( uint32_t inValue, EventStreamCompressionFlags::Enum inCurrentCompressionValue = EventStreamCompressionFlags::U8 ) + { + PX_ASSERT_WITH_MESSAGE( (inCurrentCompressionValue >= EventStreamCompressionFlags::U8) && + (inCurrentCompressionValue <= EventStreamCompressionFlags::U64), + "Invalid inCurrentCompressionValue in profile::findCompressionValue"); + + //Fallthrough is intentional + switch( inCurrentCompressionValue ) + { + case EventStreamCompressionFlags::U8: + if ( inValue <= UINT8_MAX ) + return EventStreamCompressionFlags::U8; + case EventStreamCompressionFlags::U16: + if ( inValue <= UINT16_MAX ) + return EventStreamCompressionFlags::U16; + case EventStreamCompressionFlags::U32: + case EventStreamCompressionFlags::U64: + break; + } + return EventStreamCompressionFlags::U32; + } + +#if (PX_PS4) || (PX_APPLE_FAMILY) +#pragma clang diagnostic pop +#endif + + //Event header is 32 bytes and precedes all events. + struct EventHeader + { + uint8_t mEventType; //Used to parse the correct event out of the stream + uint8_t mStreamOptions; //Timestamp compression, etc. + uint16_t mEventId; //16 bit per-event-system event id + EventHeader( uint8_t type = 0, uint16_t id = 0 ) + : mEventType( type ) + , mStreamOptions( uint8_t(-1) ) + , mEventId( id ) + { + } + + EventHeader( EventTypes::Enum type, uint16_t id ) + : mEventType( static_cast( type ) ) + , mStreamOptions( uint8_t(-1) ) + , mEventId( id ) + { + } + + EventStreamCompressionFlags::Enum getTimestampCompressionFlags() const + { + return static_cast ( mStreamOptions & EventStreamCompressionFlags::CompressionMask ); + } + + uint64_t compressTimestamp( uint64_t inLastTimestamp, uint64_t inCurrentTimestamp ) + { + mStreamOptions = EventStreamCompressionFlags::U64; + uint64_t retval = inCurrentTimestamp; + if ( inLastTimestamp ) + { + retval = inCurrentTimestamp - inLastTimestamp; + EventStreamCompressionFlags::Enum compressionValue = findCompressionValue( retval ); + mStreamOptions = static_cast( compressionValue ); + if ( compressionValue == EventStreamCompressionFlags::U64 ) + retval = inCurrentTimestamp; //just send the timestamp as is. + } + return retval; + } + + uint64_t uncompressTimestamp( uint64_t inLastTimestamp, uint64_t inCurrentTimestamp ) const + { + if ( getTimestampCompressionFlags() != EventStreamCompressionFlags::U64 ) + return inLastTimestamp + inCurrentTimestamp; + return inCurrentTimestamp; + } + + void setContextIdCompressionFlags( uint64_t inContextId ) + { + uint8_t options = static_cast( findCompressionValue( inContextId ) ); + mStreamOptions = uint8_t(mStreamOptions | options << 2); + } + + EventStreamCompressionFlags::Enum getContextIdCompressionFlags() const + { + return static_cast< EventStreamCompressionFlags::Enum >( ( mStreamOptions >> 2 ) & EventStreamCompressionFlags::CompressionMask ); + } + + bool operator==( const EventHeader& inOther ) const + { + return mEventType == inOther.mEventType + && mStreamOptions == inOther.mStreamOptions + && mEventId == inOther.mEventId; + } + + template + inline uint32_t streamify( TStreamType& inStream ) + { + uint32_t writtenSize = inStream.streamify( "EventType", mEventType ); + writtenSize += inStream.streamify("StreamOptions", mStreamOptions); //Timestamp compression, etc. + writtenSize += inStream.streamify("EventId", mEventId); //16 bit per-event-system event id + return writtenSize; + } + + + }; + + //Declaration of type level getEventType function that maps enumeration event types to datatypes + template + inline EventTypes::Enum getEventType() { PX_ASSERT( false ); return EventTypes::Unknown; } + + //Relative profile event means this event is sharing the context and thread id + //with the event before it. + struct RelativeProfileEvent + { + uint64_t mTensOfNanoSeconds; //timestamp is in tensOfNanonseconds + void init( uint64_t inTs ) { mTensOfNanoSeconds = inTs; } + void init( const RelativeProfileEvent& inData ) { mTensOfNanoSeconds = inData.mTensOfNanoSeconds; } + bool operator==( const RelativeProfileEvent& other ) const + { + return mTensOfNanoSeconds == other.mTensOfNanoSeconds; + } + template + uint32_t streamify( TStreamType& inStream, const EventHeader& inHeader ) + { + return inStream.streamify( "TensOfNanoSeconds", mTensOfNanoSeconds, inHeader.getTimestampCompressionFlags() ); + } + uint64_t getTimestamp() const { return mTensOfNanoSeconds; } + void setTimestamp( uint64_t inTs ) { mTensOfNanoSeconds = inTs; } + void setupHeader( EventHeader& inHeader, uint64_t inLastTimestamp ) + { + mTensOfNanoSeconds = inHeader.compressTimestamp( inLastTimestamp, mTensOfNanoSeconds ); + } + + uint32_t getEventSize(const EventHeader& inHeader) + { + uint32_t size = 0; + switch (inHeader.getTimestampCompressionFlags()) + { + case EventStreamCompressionFlags::U8: + size = 1; + break; + case EventStreamCompressionFlags::U16: + size = 2; + break; + case EventStreamCompressionFlags::U32: + size = 4; + break; + case EventStreamCompressionFlags::U64: + size = 8; + break; + } + return size; + } + }; + + //Start version of the relative event. + struct RelativeStartEvent : public RelativeProfileEvent + { + void init( uint64_t inTs = 0 ) { RelativeProfileEvent::init( inTs ); } + void init( const RelativeStartEvent& inData ) { RelativeProfileEvent::init( inData ); } + template + void handle( THandlerType* inHdlr, uint16_t eventId, uint32_t thread, uint64_t context, uint8_t inCpuId, uint8_t threadPriority ) const + { + inHdlr->onStartEvent( PxProfileEventId( eventId ), thread, context, inCpuId, threadPriority, mTensOfNanoSeconds ); + } + }; + + template<> inline EventTypes::Enum getEventType() { return EventTypes::RelativeStartEvent; } + + //Stop version of relative event. + struct RelativeStopEvent : public RelativeProfileEvent + { + void init( uint64_t inTs = 0 ) { RelativeProfileEvent::init( inTs ); } + void init( const RelativeStopEvent& inData ) { RelativeProfileEvent::init( inData ); } + template + void handle( THandlerType* inHdlr, uint16_t eventId, uint32_t thread, uint64_t context, uint8_t inCpuId, uint8_t threadPriority ) const + { + inHdlr->onStopEvent( PxProfileEventId( eventId ), thread, context, inCpuId, threadPriority, mTensOfNanoSeconds ); + } + }; + + template<> inline EventTypes::Enum getEventType() { return EventTypes::RelativeStopEvent; } + + struct EventContextInformation + { + uint64_t mContextId; + uint32_t mThreadId; //Thread this event was taken from + uint8_t mThreadPriority; + uint8_t mCpuId; + + void init( uint32_t inThreadId = UINT32_MAX + , uint64_t inContextId = (uint64_t(-1)) + , uint8_t inPriority = UINT8_MAX + , uint8_t inCpuId = UINT8_MAX ) + { + mContextId = inContextId; + mThreadId = inThreadId; + mThreadPriority = inPriority; + mCpuId = inCpuId; + } + + void init( const EventContextInformation& inData ) + { + mContextId = inData.mContextId; + mThreadId = inData.mThreadId; + mThreadPriority = inData.mThreadPriority; + mCpuId = inData.mCpuId; + } + + template + uint32_t streamify( TStreamType& inStream, EventStreamCompressionFlags::Enum inContextIdFlags ) + { + uint32_t writtenSize = inStream.streamify( "ThreadId", mThreadId ); + writtenSize += inStream.streamify("ContextId", mContextId, inContextIdFlags); + writtenSize += inStream.streamify("ThreadPriority", mThreadPriority); + writtenSize += inStream.streamify("CpuId", mCpuId); + return writtenSize; + } + + bool operator==( const EventContextInformation& other ) const + { + return mThreadId == other.mThreadId + && mContextId == other.mContextId + && mThreadPriority == other.mThreadPriority + && mCpuId == other.mCpuId; + } + + void setToDefault() + { + *this = EventContextInformation(); + } + }; + + //Profile event contains all the data required to tell the profile what is going + //on. + struct ProfileEvent + { + EventContextInformation mContextInformation; + RelativeProfileEvent mTimeData; //timestamp in seconds. + void init( uint32_t inThreadId, uint64_t inContextId, uint8_t inCpuId, uint8_t inPriority, uint64_t inTs ) + { + mContextInformation.init( inThreadId, inContextId, inPriority, inCpuId ); + mTimeData.init( inTs ); + } + + void init( const ProfileEvent& inData ) + { + mContextInformation.init( inData.mContextInformation ); + mTimeData.init( inData.mTimeData ); + } + + bool operator==( const ProfileEvent& other ) const + { + return mContextInformation == other.mContextInformation + && mTimeData == other.mTimeData; + } + + template + uint32_t streamify( TStreamType& inStream, const EventHeader& inHeader ) + { + uint32_t writtenSize = mContextInformation.streamify(inStream, inHeader.getContextIdCompressionFlags()); + writtenSize += mTimeData.streamify(inStream, inHeader); + return writtenSize; + } + + uint32_t getEventSize(const EventHeader& inHeader) + { + uint32_t eventSize = 0; + // time is stored depending on the conpress flag mTimeData.streamify(inStream, inHeader); + switch (inHeader.getTimestampCompressionFlags()) + { + case EventStreamCompressionFlags::U8: + eventSize++; + break; + case EventStreamCompressionFlags::U16: + eventSize += 2; + break; + case EventStreamCompressionFlags::U32: + eventSize += 4; + break; + case EventStreamCompressionFlags::U64: + eventSize += 8; + break; + } + + // context information + // mContextInformation.streamify( inStream, inHeader.getContextIdCompressionFlags() ); + eventSize += 6; // uint32_t mThreadId; uint8_t mThreadPriority; uint8_t mCpuId; + switch (inHeader.getContextIdCompressionFlags()) + { + case EventStreamCompressionFlags::U8: + eventSize++; + break; + case EventStreamCompressionFlags::U16: + eventSize += 2; + break; + case EventStreamCompressionFlags::U32: + eventSize += 4; + break; + case EventStreamCompressionFlags::U64: + eventSize += 8; + break; + } + + return eventSize; + } + + uint64_t getTimestamp() const { return mTimeData.getTimestamp(); } + void setTimestamp( uint64_t inTs ) { mTimeData.setTimestamp( inTs ); } + + void setupHeader( EventHeader& inHeader, uint64_t inLastTimestamp ) + { + mTimeData.setupHeader( inHeader, inLastTimestamp ); + inHeader.setContextIdCompressionFlags( mContextInformation.mContextId ); + } + }; + + //profile start event starts the profile session. + struct StartEvent : public ProfileEvent + { + void init( uint32_t inThreadId = 0, uint64_t inContextId = 0, uint8_t inCpuId = 0, uint8_t inPriority = 0, uint64_t inTensOfNanoSeconds = 0 ) + { + ProfileEvent::init( inThreadId, inContextId, inCpuId, inPriority, inTensOfNanoSeconds ); + } + void init( const StartEvent& inData ) + { + ProfileEvent::init( inData ); + } + + RelativeStartEvent getRelativeEvent() const { RelativeStartEvent theEvent; theEvent.init( mTimeData.mTensOfNanoSeconds ); return theEvent; } + EventTypes::Enum getRelativeEventType() const { return getEventType(); } + }; + + template<> inline EventTypes::Enum getEventType() { return EventTypes::StartEvent; } + + //Profile stop event stops the profile session. + struct StopEvent : public ProfileEvent + { + void init( uint32_t inThreadId = 0, uint64_t inContextId = 0, uint8_t inCpuId = 0, uint8_t inPriority = 0, uint64_t inTensOfNanoSeconds = 0 ) + { + ProfileEvent::init( inThreadId, inContextId, inCpuId, inPriority, inTensOfNanoSeconds ); + } + void init( const StopEvent& inData ) + { + ProfileEvent::init( inData ); + } + RelativeStopEvent getRelativeEvent() const { RelativeStopEvent theEvent; theEvent.init( mTimeData.mTensOfNanoSeconds ); return theEvent; } + EventTypes::Enum getRelativeEventType() const { return getEventType(); } + }; + + template<> inline EventTypes::Enum getEventType() { return EventTypes::StopEvent; } + + struct EventValue + { + uint64_t mValue; + uint64_t mContextId; + uint32_t mThreadId; + void init( int64_t inValue = 0, uint64_t inContextId = 0, uint32_t inThreadId = 0 ) + { + mValue = static_cast( inValue ); + mContextId = inContextId; + mThreadId = inThreadId; + } + + void init( const EventValue& inData ) + { + mValue = inData.mValue; + mContextId = inData.mContextId; + mThreadId = inData.mThreadId; + } + + int64_t getValue() const { return static_cast( mValue ); } + + void setupHeader( EventHeader& inHeader ) + { + mValue = inHeader.compressTimestamp( 0, mValue ); + inHeader.setContextIdCompressionFlags( mContextId ); + } + + template + uint32_t streamify( TStreamType& inStream, const EventHeader& inHeader ) + { + uint32_t writtenSize = inStream.streamify("Value", mValue, inHeader.getTimestampCompressionFlags()); + writtenSize += inStream.streamify("ContextId", mContextId, inHeader.getContextIdCompressionFlags()); + writtenSize += inStream.streamify("ThreadId", mThreadId); + return writtenSize; + } + + uint32_t getEventSize(const EventHeader& inHeader) + { + uint32_t eventSize = 0; + // value + switch (inHeader.getTimestampCompressionFlags()) + { + case EventStreamCompressionFlags::U8: + eventSize++; + break; + case EventStreamCompressionFlags::U16: + eventSize += 2; + break; + case EventStreamCompressionFlags::U32: + eventSize += 4; + break; + case EventStreamCompressionFlags::U64: + eventSize += 8; + break; + } + + // context information + switch (inHeader.getContextIdCompressionFlags()) + { + case EventStreamCompressionFlags::U8: + eventSize++; + break; + case EventStreamCompressionFlags::U16: + eventSize += 2; + break; + case EventStreamCompressionFlags::U32: + eventSize += 4; + break; + case EventStreamCompressionFlags::U64: + eventSize += 8; + break; + } + + eventSize += 4; // uint32_t mThreadId; + + return eventSize; + } + + bool operator==( const EventValue& other ) const + { + return mValue == other.mValue + && mContextId == other.mContextId + && mThreadId == other.mThreadId; + } + + template + void handle( THandlerType* inHdlr, uint16_t eventId ) const + { + inHdlr->onEventValue( PxProfileEventId( eventId ), mThreadId, mContextId, getValue() ); + } + + }; + template<> inline EventTypes::Enum getEventType() { return EventTypes::EventValue; } + + //obsolete, placeholder to skip data from PhysX SDKs < 3.4 + struct CUDAProfileBuffer + { + uint64_t mTimestamp; + float mTimespan; + const uint8_t* mCudaData; + uint32_t mBufLen; + uint32_t mVersion; + + template + uint32_t streamify( TStreamType& inStream, const EventHeader& ) + { + uint32_t writtenSize = inStream.streamify("Timestamp", mTimestamp); + writtenSize += inStream.streamify("Timespan", mTimespan); + writtenSize += inStream.streamify("CudaData", mCudaData, mBufLen); + writtenSize += inStream.streamify("BufLen", mBufLen); + writtenSize += inStream.streamify("Version", mVersion); + return writtenSize; + } + + bool operator==( const CUDAProfileBuffer& other ) const + { + return mTimestamp == other.mTimestamp + && mTimespan == other.mTimespan + && mBufLen == other.mBufLen + && memcmp( mCudaData, other.mCudaData, mBufLen ) == 0 + && mVersion == other.mVersion; + } + }; + + template<> inline EventTypes::Enum getEventType() { return EventTypes::CUDAProfileBuffer; } + + //Provides a generic equal operation for event data objects. + template + struct EventDataEqualOperator + { + TEventData mData; + EventDataEqualOperator( const TEventData& inD ) : mData( inD ) {} + template bool operator()( const TDataType& inRhs ) const { return mData.toType( Type2Type() ) == inRhs; } + bool operator()() const { return false; } + }; + + /** + * Generic event container that combines and even header with the generic event data type. + * Provides unsafe and typesafe access to the event data. + */ + class Event + { + public: + typedef PX_PROFILE_UNION_7(StartEvent, StopEvent, RelativeStartEvent, RelativeStopEvent, EventValue, CUDAProfileBuffer, uint8_t) EventData; + + private: + EventHeader mHeader; + EventData mData; + public: + Event() {} + + template + Event( EventHeader inHeader, const TDataType& inData ) + : mHeader( inHeader ) + { + mData.init(inData); + } + + template + Event( uint16_t eventId, const TDataType& inData ) + : mHeader( getEventType(), eventId ) + { + mData.init(inData); + } + const EventHeader& getHeader() const { return mHeader; } + const EventData& getData() const { return mData; } + + template + const TDataType& getValue() const { PX_ASSERT( mHeader.mEventType == getEventType() ); return mData.toType(); } + + template + TDataType& getValue() { PX_ASSERT( mHeader.mEventType == getEventType() ); return mData.toType(); } + + template + inline TRetVal visit( TOperator inOp ) const; + + bool operator==( const Event& inOther ) const + { + if ( !(mHeader == inOther.mHeader ) ) return false; + if ( mHeader.mEventType ) + return inOther.visit( EventDataEqualOperator( mData ) ); + return true; + } + }; + + //Combining the above union type with an event type means that an object can get the exact + //data out of the union. Using this function means that all callsites will be forced to + //deal with the newer datatypes and that the switch statement only exists in once place. + //Implements conversion from enum -> datatype + template + TRetVal visit( EventTypes::Enum inEventType, const Event::EventData& inData, TOperator inOperator ) + { + switch( inEventType ) + { + case EventTypes::StartEvent: return inOperator( inData.toType( Type2Type() ) ); + case EventTypes::StopEvent: return inOperator( inData.toType( Type2Type() ) ); + case EventTypes::RelativeStartEvent: return inOperator( inData.toType( Type2Type() ) ); + case EventTypes::RelativeStopEvent: return inOperator( inData.toType( Type2Type() ) ); + case EventTypes::EventValue: return inOperator( inData.toType( Type2Type() ) ); + //obsolete, placeholder to skip data from PhysX SDKs < 3.4 + case EventTypes::CUDAProfileBuffer: return inOperator( inData.toType( Type2Type() ) ); + case EventTypes::Unknown: break; + } + uint8_t type = static_cast( inEventType ); + return inOperator( type ); + } + + template + inline TRetVal Event::visit( TOperator inOp ) const + { + return physx::profile::visit( static_cast(mHeader.mEventType), mData, inOp ); + } +} } + +#endif // PXPVDSDK_PXPROFILEEVENTS_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemory.h b/src/PhysX/physx/source/pvd/src/PxProfileMemory.h new file mode 100644 index 000000000..604d5091e --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemory.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEMEMORY_H +#define PXPVDSDK_PXPROFILEMEMORY_H + +#include "PxProfileEventBufferClientManager.h" +#include "PxProfileEventSender.h" +#include "PsBroadcast.h" + +namespace physx { namespace profile { + + /** + \brief Record events so a late-connecting client knows about + all outstanding allocations + */ + class PxProfileMemoryEventRecorder : public shdfnd::AllocationListener + { + protected: + virtual ~PxProfileMemoryEventRecorder(){} + public: + /** + \brief Set the allocation listener + \param inListener Allocation listener. + */ + virtual void setListener(AllocationListener* inListener) = 0; + /** + \brief Release the instance. + */ + virtual void release() = 0; + }; + + /** + \brief Stores memory events into the memory buffer. + */ + class PxProfileMemoryEventBuffer + : public shdfnd::AllocationListener //add a new event to the buffer + , public PxProfileEventBufferClientManager //add clients to handle the serialized memory events + , public PxProfileEventFlusher //flush the buffer + { + protected: + virtual ~PxProfileMemoryEventBuffer(){} + public: + + /** + \brief Release the instance. + */ + virtual void release() = 0; + + /** + \brief Create a non-mutex-protected event buffer. + \param inAllocator Allocation callback. + \param inBufferSize Internal buffer size. + */ + static PxProfileMemoryEventBuffer& createMemoryEventBuffer(PxAllocatorCallback& inAllocator, uint32_t inBufferSize = 0x1000); + }; + + + +} } // namespace physx + + +#endif // PXPVDSDK_PXPROFILEMEMORY_H + + diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h new file mode 100644 index 000000000..ca986aad7 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h @@ -0,0 +1,192 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEMEMORYBUFFER_H +#define PXPVDSDK_PXPROFILEMEMORYBUFFER_H + +#include "PsAllocator.h" +#include "foundation/PxMemory.h" + +namespace physx { namespace profile { + + template::Type > + class MemoryBuffer : public TAllocator + { + uint8_t* mBegin; + uint8_t* mEnd; + uint8_t* mCapacityEnd; + + public: + MemoryBuffer( const TAllocator& inAlloc = TAllocator() ) : TAllocator( inAlloc ), mBegin( 0 ), mEnd( 0 ), mCapacityEnd( 0 ) {} + ~MemoryBuffer() + { + if ( mBegin ) TAllocator::deallocate( mBegin ); + } + uint32_t size() const { return static_cast( mEnd - mBegin ); } + uint32_t capacity() const { return static_cast( mCapacityEnd - mBegin ); } + uint8_t* begin() { return mBegin; } + uint8_t* end() { return mEnd; } + void setEnd(uint8_t* nEnd) { mEnd = nEnd; } + const uint8_t* begin() const { return mBegin; } + const uint8_t* end() const { return mEnd; } + void clear() { mEnd = mBegin; } + uint32_t write( uint8_t inValue ) + { + growBuf( 1 ); + *mEnd = inValue; + ++mEnd; + return 1; + } + + template + uint32_t write( const TDataType& inValue ) + { + uint32_t writtenSize = sizeof(TDataType); + growBuf(writtenSize); + const uint8_t* __restrict readPtr = reinterpret_cast< const uint8_t* >( &inValue ); + uint8_t* __restrict writePtr = mEnd; + for ( uint32_t idx = 0; idx < sizeof(TDataType); ++idx ) writePtr[idx] = readPtr[idx]; + mEnd += writtenSize; + return writtenSize; + } + + template + uint32_t write( const TDataType* inValue, uint32_t inLength ) + { + if ( inValue && inLength ) + { + uint32_t writeSize = inLength * sizeof( TDataType ); + growBuf( writeSize ); + PxMemCopy( mBegin + size(), inValue, writeSize ); + mEnd += writeSize; + return writeSize; + } + return 0; + } + + // used by atomic write. Store the data and write the end afterwards + // we dont check the buffer size, it should not resize on the fly + template + uint32_t write(const TDataType* inValue, uint32_t inLength, int32_t index) + { + if (inValue && inLength) + { + uint32_t writeSize = inLength * sizeof(TDataType); + PX_ASSERT(mBegin + index + writeSize < mCapacityEnd); + PxMemCopy(mBegin + index, inValue, writeSize); + return writeSize; + } + return 0; + } + + void growBuf( uint32_t inAmount ) + { + uint32_t newSize = size() + inAmount; + reserve( newSize ); + } + void resize( uint32_t inAmount ) + { + reserve( inAmount ); + mEnd = mBegin + inAmount; + } + void reserve( uint32_t newSize ) + { + uint32_t currentSize = size(); + if ( newSize >= capacity() ) + { + const uint32_t allocSize = mBegin ? newSize * 2 : newSize; + + uint8_t* newData = static_cast(TAllocator::allocate(allocSize, __FILE__, __LINE__)); + memset(newData, 0xf,allocSize); + if ( mBegin ) + { + PxMemCopy( newData, mBegin, currentSize ); + TAllocator::deallocate( mBegin ); + } + mBegin = newData; + mEnd = mBegin + currentSize; + mCapacityEnd = mBegin + allocSize; + } + } + }; + + + class TempMemoryBuffer + { + uint8_t* mBegin; + uint8_t* mEnd; + uint8_t* mCapacityEnd; + + public: + TempMemoryBuffer(uint8_t* data, int32_t size) : mBegin(data), mEnd(data), mCapacityEnd(data + size) {} + ~TempMemoryBuffer() + { + } + uint32_t size() const { return static_cast(mEnd - mBegin); } + uint32_t capacity() const { return static_cast(mCapacityEnd - mBegin); } + const uint8_t* begin() { return mBegin; } + uint8_t* end() { return mEnd; } + const uint8_t* begin() const { return mBegin; } + const uint8_t* end() const { return mEnd; } + uint32_t write(uint8_t inValue) + { + *mEnd = inValue; + ++mEnd; + return 1; + } + + template + uint32_t write(const TDataType& inValue) + { + uint32_t writtenSize = sizeof(TDataType); + const uint8_t* __restrict readPtr = reinterpret_cast(&inValue); + uint8_t* __restrict writePtr = mEnd; + for (uint32_t idx = 0; idx < sizeof(TDataType); ++idx) writePtr[idx] = readPtr[idx]; + mEnd += writtenSize; + return writtenSize; + } + + template + uint32_t write(const TDataType* inValue, uint32_t inLength) + { + if (inValue && inLength) + { + uint32_t writeSize = inLength * sizeof(TDataType); + PxMemCopy(mBegin + size(), inValue, writeSize); + mEnd += writeSize; + return writeSize; + } + return 0; + } + }; + +}} + +#endif // PXPVDSDK_PXPROFILEMEMORYBUFFER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h new file mode 100644 index 000000000..e3784f5ae --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h @@ -0,0 +1,157 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEMEMORYEVENTBUFFER_H +#define PXPVDSDK_PXPROFILEMEMORYEVENTBUFFER_H + +#include "PxProfileDataBuffer.h" +#include "PxProfileMemoryEvents.h" +#include "PxProfileMemory.h" +#include "PxProfileScopedMutexLock.h" +#include "PxProfileAllocatorWrapper.h" +#include "PxProfileEventMutex.h" + +#include "PsHash.h" +#include "PsHashMap.h" +#include "PsUserAllocated.h" + +namespace physx { namespace profile { + + template + class MemoryEventBuffer : public DataBuffer + { + public: + typedef DataBuffer TBaseType; + typedef typename TBaseType::TMutexType TMutexType; + typedef typename TBaseType::TScopedLockType TScopedLockType; + typedef typename TBaseType::TU8AllocatorType TU8AllocatorType; + typedef typename TBaseType::TMemoryBufferType TMemoryBufferType; + typedef typename TBaseType::TBufferClientArray TBufferClientArray; + typedef shdfnd::HashMap, TU8AllocatorType> TCharPtrToHandleMap; + + protected: + TCharPtrToHandleMap mStringTable; + + public: + + MemoryEventBuffer( PxAllocatorCallback& cback + , uint32_t inBufferFullAmount + , TMutexType* inBufferMutex ) + : TBaseType( &cback, inBufferFullAmount, inBufferMutex, "struct physx::profile::MemoryEvent" ) + , mStringTable( TU8AllocatorType( TBaseType::getWrapper(), "MemoryEventStringBuffer" ) ) + { + } + + uint32_t getHandle( const char* inData ) + { + if ( inData == NULL ) inData = ""; + const typename TCharPtrToHandleMap::Entry* result( mStringTable.find( inData ) ); + if ( result ) + return result->second; + uint32_t hdl = mStringTable.size() + 1; + mStringTable.insert( inData, hdl ); + StringTableEvent theEvent; + theEvent.init( inData, hdl ); + sendEvent( theEvent ); + return hdl; + } + + void onAllocation( size_t inSize, const char* inType, const char* inFile, uint32_t inLine, uint64_t addr ) + { + if ( addr == 0 ) + return; + uint32_t typeHdl( getHandle( inType ) ); + uint32_t fileHdl( getHandle( inFile ) ); + AllocationEvent theEvent; + theEvent.init( inSize, typeHdl, fileHdl, inLine, addr ); + sendEvent( theEvent ); + } + + void onDeallocation( uint64_t addr ) + { + if ( addr == 0 ) + return; + DeallocationEvent theEvent; + theEvent.init( addr ); + sendEvent( theEvent ); + } + + void flushProfileEvents() + { + TBaseType::flushEvents(); + } + + protected: + + template + void sendEvent( TDataType inType ) + { + MemoryEventHeader theHeader( getMemoryEventType() ); + inType.setup( theHeader ); + theHeader.streamify( TBaseType::mSerializer ); + inType.streamify( TBaseType::mSerializer, theHeader ); + if ( TBaseType::mDataArray.size() >= TBaseType::mBufferFullAmount ) + flushProfileEvents(); + } + }; + + class PxProfileMemoryEventBufferImpl : public shdfnd::UserAllocated + , public PxProfileMemoryEventBuffer + { + typedef MemoryEventBuffer TMemoryBufferType; + TMemoryBufferType mBuffer; + + public: + PxProfileMemoryEventBufferImpl( PxAllocatorCallback& alloc, uint32_t inBufferFullAmount ) + : mBuffer( alloc, inBufferFullAmount, NULL ) + { + } + + virtual void onAllocation( size_t size, const char* typeName, const char* filename, int line, void* allocatedMemory ) + { + mBuffer.onAllocation( size, typeName, filename, uint32_t(line), static_cast(reinterpret_cast(allocatedMemory)) ); + } + virtual void onDeallocation( void* allocatedMemory ) + { + mBuffer.onDeallocation(static_cast(reinterpret_cast(allocatedMemory)) ); + } + + virtual void addClient( PxProfileEventBufferClient& inClient ) { mBuffer.addClient( inClient ); } + virtual void removeClient( PxProfileEventBufferClient& inClient ) { mBuffer.removeClient( inClient ); } + virtual bool hasClients() const { return mBuffer.hasClients(); } + + virtual void flushProfileEvents() { mBuffer.flushProfileEvents(); } + + virtual void release(){ PX_PROFILE_DELETE( mBuffer.getWrapper().getAllocator(), this ); } + }; +}} + +#endif // PXPVDSDK_PXPROFILEMEMORYEVENTBUFFER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h new file mode 100644 index 000000000..4a206c70d --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h @@ -0,0 +1,411 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEMEMORYEVENTS_H +#define PXPVDSDK_PXPROFILEMEMORYEVENTS_H + +#include "PxProfileEvents.h" + +//Memory events define their own event stream + +namespace physx { namespace profile { + struct MemoryEventTypes + { + enum Enum + { + Unknown = 0, + StringTableEvent, //introduce a new mapping of const char* -> integer + AllocationEvent, + DeallocationEvent, + FullAllocationEvent + }; + }; + + template + inline unsigned char convertToNBits( TDataType inType ) + { + uint8_t conversion = static_cast( inType ); + PX_ASSERT( conversion < (1 << numBits) ); + return conversion; + } + + template + inline unsigned char convertToTwoBits( TDataType inType ) + { + return convertToNBits<2>( inType ); + } + + template + inline unsigned char convertToFourBits( TDataType inType ) + { + return convertToNBits<4>( inType ); + } + + inline EventStreamCompressionFlags::Enum fromNumber( uint8_t inNum ) { return static_cast( inNum ); } + + template + inline void compileCheckSize() + { + PX_COMPILE_TIME_ASSERT( lhs <= rhs ); + } + + //Used for predictable bit fields. + template + struct BitMaskSetter + { + //Create a mask that masks out the orginal value shift into place + static TDataType createOffsetMask() { return TDataType(createMask() << TOffset); } + //Create a mask of TNumBits number of tis + static TDataType createMask() { return static_cast((1 << TNumBits) - 1); } + void setValue( TDataType& inCurrent, TInputType inData ) + { + PX_ASSERT( inData < ( 1 << TNumBits ) ); + + //Create a mask to remove the current value. + TDataType theMask = TDataType(~(createOffsetMask())); + //Clear out current value. + inCurrent = TDataType(inCurrent & theMask); + //Create the new value. + TDataType theAddition = static_cast( inData << TOffset ); + //or it into the existing value. + inCurrent = TDataType(inCurrent | theAddition); + } + + TInputType getValue( TDataType inCurrent ) + { + return static_cast( ( inCurrent >> TOffset ) & createMask() ); + } + }; + + + struct MemoryEventHeader + { + uint16_t mValue; + + typedef BitMaskSetter TTypeBitmask; + typedef BitMaskSetter TAddrCompressBitmask; + typedef BitMaskSetter TTypeCompressBitmask; + typedef BitMaskSetter TFnameCompressBitmask; + typedef BitMaskSetter TSizeCompressBitmask; + typedef BitMaskSetter TLineCompressBitmask; + + //That leaves size as the only thing not compressed usually. + + MemoryEventHeader( MemoryEventTypes::Enum inType = MemoryEventTypes::Unknown ) + : mValue( 0 ) + { + uint8_t defaultCompression( convertToTwoBits( EventStreamCompressionFlags::U64 ) ); + TTypeBitmask().setValue( mValue, convertToFourBits( inType ) ); + TAddrCompressBitmask().setValue( mValue, defaultCompression ); + TTypeCompressBitmask().setValue( mValue, defaultCompression ); + TFnameCompressBitmask().setValue( mValue, defaultCompression ); + TSizeCompressBitmask().setValue( mValue, defaultCompression ); + TLineCompressBitmask().setValue( mValue, defaultCompression ); + } + + MemoryEventTypes::Enum getType() const { return static_cast( TTypeBitmask().getValue( mValue ) ); } + +#define DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( name ) \ + void set##name( EventStreamCompressionFlags::Enum inEnum ) { T##name##Bitmask().setValue( mValue, convertToTwoBits( inEnum ) ); } \ + EventStreamCompressionFlags::Enum get##name() const { return fromNumber( T##name##Bitmask().getValue( mValue ) ); } + + DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( AddrCompress ) + DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( TypeCompress ) + DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( FnameCompress ) + DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( SizeCompress ) + DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( LineCompress ) + +#undef DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR + + bool operator==( const MemoryEventHeader& inOther ) const + { + return mValue == inOther.mValue; + } + template + void streamify( TStreamType& inStream ) + { + inStream.streamify( "Header", mValue ); + } + }; + + //Declaration of type level getMemoryEventType function that maps enumeration event types to datatypes + template + inline MemoryEventTypes::Enum getMemoryEventType() { PX_ASSERT( false ); return MemoryEventTypes::Unknown; } + + inline bool safeStrEq( const char* lhs, const char* rhs ) + { + if ( lhs == rhs ) + return true; + //If they aren't equal, and one of them is null, + //then they can't be equal. + //This is assuming that the null char* is not equal to + //the empty "" char*. + if ( !lhs || !rhs ) + return false; + + return ::strcmp( lhs, rhs ) == 0; + } + + struct StringTableEvent + { + const char* mString; + uint32_t mHandle; + + void init( const char* inStr = "", uint32_t inHdl = 0 ) + { + mString = inStr; + mHandle = inHdl; + } + + void init( const StringTableEvent& inData ) + { + mString = inData.mString; + mHandle = inData.mHandle; + } + + bool operator==( const StringTableEvent& inOther ) const + { + return mHandle == inOther.mHandle + && safeStrEq( mString, inOther.mString ); + } + + void setup( MemoryEventHeader& ) const {} + + template + void streamify( TStreamType& inStream, const MemoryEventHeader& ) + { + inStream.streamify( "String", mString ); + inStream.streamify( "Handle", mHandle ); + } + }; + template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::StringTableEvent; } + + struct MemoryEventData + { + uint64_t mAddress; + void init( uint64_t addr ) + { + mAddress = addr; + } + + void init( const MemoryEventData& inData) + { + mAddress = inData.mAddress; + } + + bool operator==( const MemoryEventData& inOther ) const + { + return mAddress == inOther.mAddress; + } + + void setup( MemoryEventHeader& inHeader ) const + { + inHeader.setAddrCompress( findCompressionValue( mAddress ) ); + } + + template + void streamify( TStreamType& inStream, const MemoryEventHeader& inHeader ) + { + inStream.streamify( "Address", mAddress, inHeader.getAddrCompress() ); + } + }; + + struct AllocationEvent : public MemoryEventData + { + uint32_t mSize; + uint32_t mType; + uint32_t mFile; + uint32_t mLine; + void init( size_t size = 0, uint32_t type = 0, uint32_t file = 0, uint32_t line = 0, uint64_t addr = 0 ) + { + MemoryEventData::init( addr ); + mSize = static_cast( size ); + mType = type; + mFile = file; + mLine = line; + } + + void init( const AllocationEvent& inData ) + { + MemoryEventData::init( inData ); + mSize = inData.mSize; + mType = inData.mType; + mFile = inData.mFile; + mLine = inData.mLine; + } + + bool operator==( const AllocationEvent& inOther ) const + { + return MemoryEventData::operator==( inOther ) + && mSize == inOther.mSize + && mType == inOther.mType + && mFile == inOther.mFile + && mLine == inOther.mLine; + } + + void setup( MemoryEventHeader& inHeader ) const + { + inHeader.setTypeCompress( findCompressionValue( mType ) ); + inHeader.setFnameCompress( findCompressionValue( mFile ) ); + inHeader.setSizeCompress( findCompressionValue( mSize ) ); + inHeader.setLineCompress( findCompressionValue( mLine ) ); + MemoryEventData::setup( inHeader ); + } + + template + void streamify( TStreamType& inStream, const MemoryEventHeader& inHeader ) + { + inStream.streamify( "Size", mSize, inHeader.getSizeCompress() ); + inStream.streamify( "Type", mType, inHeader.getTypeCompress() ); + inStream.streamify( "File", mFile, inHeader.getFnameCompress() ); + inStream.streamify( "Line", mLine, inHeader.getLineCompress() ); + MemoryEventData::streamify( inStream, inHeader ); + } + }; + template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::AllocationEvent; } + + + struct FullAllocationEvent : public MemoryEventData + { + size_t mSize; + const char* mType; + const char* mFile; + uint32_t mLine; + void init( size_t size, const char* type, const char* file, uint32_t line, uint64_t addr ) + { + MemoryEventData::init( addr ); + mSize = size; + mType = type; + mFile = file; + mLine = line; + } + + void init( const FullAllocationEvent& inData ) + { + MemoryEventData::init( inData ); + mSize = inData.mSize; + mType = inData.mType; + mFile = inData.mFile; + mLine = inData.mLine; + } + + bool operator==( const FullAllocationEvent& inOther ) const + { + return MemoryEventData::operator==( inOther ) + && mSize == inOther.mSize + && safeStrEq( mType, inOther.mType ) + && safeStrEq( mFile, inOther.mFile ) + && mLine == inOther.mLine; + } + + void setup( MemoryEventHeader& ) const {} + }; + + template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::FullAllocationEvent; } + + struct DeallocationEvent : public MemoryEventData + { + void init( uint64_t addr = 0 ) { MemoryEventData::init( addr ); } + void init( const DeallocationEvent& inData ) { MemoryEventData::init( inData ); } + }; + + template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::DeallocationEvent; } + + class MemoryEvent + { + public: + typedef PX_PROFILE_UNION_5(StringTableEvent, AllocationEvent, DeallocationEvent, FullAllocationEvent, uint8_t) EventData; + + private: + MemoryEventHeader mHeader; + EventData mData; + public: + + MemoryEvent() {} + MemoryEvent( MemoryEventHeader inHeader, const EventData& inData = EventData() ) + : mHeader( inHeader ) + , mData( inData ) + { + } + + template + MemoryEvent( const TDataType& inType ) + : mHeader( getMemoryEventType() ) + , mData( inType ) + { + //set the appropriate compression bits. + inType.setup( mHeader ); + } + const MemoryEventHeader& getHeader() const { return mHeader; } + const EventData& getData() const { return mData; } + + template + const TDataType& getValue() const { PX_ASSERT( mHeader.getType() == getMemoryEventType() ); return mData.toType(); } + + template + TDataType& getValue() { PX_ASSERT( mHeader.getType() == getMemoryEventType() ); return mData.toType(); } + + template + inline TRetVal visit( TOperator inOp ) const; + + bool operator==( const MemoryEvent& inOther ) const + { + if ( !(mHeader == inOther.mHeader ) ) return false; + if ( mHeader.getType() ) + return inOther.visit( EventDataEqualOperator( mData ) ); + return true; + } + }; + + template + inline TRetVal visit( MemoryEventTypes::Enum inEventType, const MemoryEvent::EventData& inData, TOperator inOperator ) + { + switch( inEventType ) + { + case MemoryEventTypes::StringTableEvent: return inOperator( inData.toType( Type2Type() ) ); + case MemoryEventTypes::AllocationEvent: return inOperator( inData.toType( Type2Type() ) ); + case MemoryEventTypes::DeallocationEvent: return inOperator( inData.toType( Type2Type() ) ); + case MemoryEventTypes::FullAllocationEvent: return inOperator( inData.toType( Type2Type() ) ); + case MemoryEventTypes::Unknown: return inOperator( static_cast( inEventType ) ); + } + return TRetVal(); + } + + template + inline TRetVal MemoryEvent::visit( TOperator inOp ) const + { + return physx::profile::visit( mHeader.getType(), mData, inOp ); + } +}} + +#endif // PXPVDSDK_PXPROFILEMEMORYEVENTS_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h b/src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h new file mode 100644 index 000000000..fe53314dd --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h @@ -0,0 +1,107 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILESCOPEDEVENT_H +#define PXPVDSDK_PXPROFILESCOPEDEVENT_H + +#include "PxProfileEventId.h" +#include "PxProfileCompileTimeEventFilter.h" + +namespace physx { namespace profile { + + /** + \brief Template version of startEvent, called directly on provided profile buffer. + + \param inBuffer Profile event buffer. + \param inId Profile event id. + \param inContext Profile event context. + */ + template + inline void startEvent( TBufferType* inBuffer, const PxProfileEventId& inId, uint64_t inContext ) + { + if ( TEnabled && inBuffer ) inBuffer->startEvent( inId, inContext ); + } + + /** + \brief Template version of stopEvent, called directly on provided profile buffer. + + \param inBuffer Profile event buffer. + \param inId Profile event id. + \param inContext Profile event context. + */ + template + inline void stopEvent( TBufferType* inBuffer, const PxProfileEventId& inId, uint64_t inContext ) + { + if ( TEnabled && inBuffer ) inBuffer->stopEvent( inId, inContext ); + } + + /** + \brief Template version of startEvent, called directly on provided profile buffer. + + \param inEnabled If profile event is enabled. + \param inBuffer Profile event buffer. + \param inId Profile event id. + \param inContext Profile event context. + */ + template + inline void startEvent( bool inEnabled, TBufferType* inBuffer, const PxProfileEventId& inId, uint64_t inContext ) + { + if ( inEnabled && inBuffer ) inBuffer->startEvent( inId, inContext ); + } + + /** + \brief Template version of stopEvent, called directly on provided profile buffer. + + \param inEnabled If profile event is enabled. + \param inBuffer Profile event buffer. + \param inId Profile event id. + \param inContext Profile event context. + */ + template + inline void stopEvent( bool inEnabled, TBufferType* inBuffer, const PxProfileEventId& inId, uint64_t inContext ) + { + if ( inEnabled && inBuffer ) inBuffer->stopEvent( inId, inContext ); + } + + /** + \brief Template version of eventValue, called directly on provided profile buffer. + + \param inEnabled If profile event is enabled. + \param inBuffer Profile event buffer. + \param inId Profile event id. + \param inContext Profile event context. + \param inValue Event value. + */ + template + inline void eventValue( bool inEnabled, TBufferType* inBuffer, const PxProfileEventId& inId, uint64_t inContext, int64_t inValue ) + { + if ( inEnabled && inBuffer ) inBuffer->eventValue( inId, inContext, inValue ); + } + +}} + +#endif // PXPVDSDK_PXPROFILESCOPEDEVENT_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h b/src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h new file mode 100644 index 000000000..dac1e4cb0 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h @@ -0,0 +1,64 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILESCOPEDMUTEXLOCK_H +#define PXPVDSDK_PXPROFILESCOPEDMUTEXLOCK_H + +#include "foundation/Px.h" + +namespace physx { namespace profile { + + /** + * Generic class to wrap any mutex type that has lock and unlock methods + */ + template + struct ScopedLockImpl + { + TMutexType* mMutex; + ScopedLockImpl( TMutexType* inM ) : mMutex( inM ) + { + if ( mMutex ) mMutex->lock(); + } + ~ScopedLockImpl() + { + if ( mMutex ) mMutex->unlock(); + } + }; + + /** + * Null locking system that does nothing. + */ + struct NullLock + { + template NullLock( TDataType*) {} + }; +}} + +#endif // PXPVDSDK_PXPROFILESCOPEDMUTEXLOCK_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h b/src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h new file mode 100644 index 000000000..cb768367b --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h @@ -0,0 +1,315 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEZONEIMPL_H +#define PXPVDSDK_PXPROFILEZONEIMPL_H + +#include "PxPvdProfileZone.h" +#include "PxProfileZoneManager.h" +#include "PxProfileContextProviderImpl.h" +#include "PxProfileScopedMutexLock.h" +#include "PxProfileEventBufferAtomic.h" +#include "PsMutex.h" + +namespace physx { namespace profile { + + /** + \brief Simple event filter that enables all events. + */ + struct PxProfileNullEventFilter + { + void setEventEnabled( const PxProfileEventId&, bool) { PX_ASSERT(false); } + bool isEventEnabled( const PxProfileEventId&) const { return true; } + }; + + typedef shdfnd::MutexT > TZoneMutexType; + typedef ScopedLockImpl TZoneLockType; + typedef EventBuffer< PxDefaultContextProvider, TZoneMutexType, TZoneLockType, PxProfileNullEventFilter > TZoneEventBufferType; + //typedef EventBufferAtomic< PxDefaultContextProvider, TZoneMutexType, TZoneLockType, PxProfileNullEventFilter > TZoneEventBufferType; + + template + class ZoneImpl : TZoneEventBufferType //private inheritance intended + , public PxProfileZone + , public PxProfileEventBufferClient + { + typedef shdfnd::MutexT > TMutexType; + typedef PxProfileHashMap TNameToEvtIndexMap; + //ensure we don't reuse event ids. + typedef PxProfileHashMap TEvtIdToNameMap; + typedef TMutexType::ScopedLock TLockType; + + + const char* mName; + mutable TMutexType mMutex; + PxProfileArray mEventNames; + // to avoid locking, read-only and read-write map exist + TNameToEvtIndexMap mNameToEvtIndexMapR; + TNameToEvtIndexMap mNameToEvtIndexMapRW; + //ensure we don't reuse event ids. + TEvtIdToNameMap mEvtIdToNameMap; + + PxProfileZoneManager* mProfileZoneManager; + + PxProfileArray mZoneClients; + volatile bool mEventsActive; + + PX_NOCOPY(ZoneImpl) + public: + ZoneImpl( PxAllocatorCallback* inAllocator, const char* inName, uint32_t bufferSize = 0x10000 /*64k*/, const TNameProvider& inProvider = TNameProvider() ) + : TZoneEventBufferType( inAllocator, bufferSize, PxDefaultContextProvider(), NULL, PxProfileNullEventFilter() ) + , mName( inName ) + , mMutex( PxProfileWrapperReflectionAllocator( mWrapper ) ) + , mEventNames( mWrapper ) + , mNameToEvtIndexMapR( mWrapper ) + , mNameToEvtIndexMapRW( mWrapper ) + , mEvtIdToNameMap( mWrapper ) + , mProfileZoneManager( NULL ) + , mZoneClients( mWrapper ) + , mEventsActive( false ) + { + TZoneEventBufferType::setBufferMutex( &mMutex ); + //Initialize the event name structure with existing names from the name provider. + PxProfileNames theNames( inProvider.getProfileNames() ); + for ( uint32_t idx = 0; idx < theNames.eventCount; ++idx ) + { + const PxProfileEventName& theName (theNames.events[idx]); + doAddName( theName.name, theName.eventId.eventId, theName.eventId.compileTimeEnabled ); + } + TZoneEventBufferType::addClient( *this ); + } + + virtual ~ZoneImpl() { + if ( mProfileZoneManager != NULL ) + mProfileZoneManager->removeProfileZone( *this ); + mProfileZoneManager = NULL; + TZoneEventBufferType::removeClient( *this ); + } + + void doAddName( const char* inName, uint16_t inEventId, bool inCompileTimeEnabled ) + { + TLockType theLocker( mMutex ); + mEvtIdToNameMap.insert( inEventId, inName ); + uint32_t idx = static_cast( mEventNames.size() ); + mNameToEvtIndexMapRW.insert( inName, idx ); + mEventNames.pushBack( PxProfileEventName( inName, PxProfileEventId( inEventId, inCompileTimeEnabled ) ) ); + } + + virtual void flushEventIdNameMap() + { + // copy the RW map into R map + if (mNameToEvtIndexMapRW.size()) + { + for (TNameToEvtIndexMap::Iterator iter = mNameToEvtIndexMapRW.getIterator(); !iter.done(); ++iter) + { + mNameToEvtIndexMapR.insert(iter->first, iter->second); + } + mNameToEvtIndexMapRW.clear(); + } + } + + virtual uint16_t getEventIdForName( const char* inName ) + { + return getEventIdsForNames( &inName, 1 ); + } + + virtual uint16_t getEventIdsForNames( const char** inNames, uint32_t inLen ) + { + if ( inLen == 0 ) + return 0; + + // search the read-only map first + const TNameToEvtIndexMap::Entry* theEntry( mNameToEvtIndexMapR.find( inNames[0] ) ); + if ( theEntry ) + return mEventNames[theEntry->second].eventId; + + TLockType theLocker(mMutex); + + const TNameToEvtIndexMap::Entry* theReEntry(mNameToEvtIndexMapRW.find(inNames[0])); + if (theReEntry) + return mEventNames[theReEntry->second].eventId; + + //Else git R dun. + uint16_t nameSize = static_cast( mEventNames.size() ); + //We don't allow 0 as an event id. + uint16_t eventId = nameSize; + //Find a contiguous set of unique event ids + bool foundAnEventId = false; + do + { + foundAnEventId = false; + ++eventId; + for ( uint16_t idx = 0; idx < inLen && foundAnEventId == false; ++idx ) + foundAnEventId = mEvtIdToNameMap.find( uint16_t(eventId + idx) ) != NULL; + } + while( foundAnEventId ); + + uint32_t clientCount = mZoneClients.size(); + for ( uint16_t nameIdx = 0; nameIdx < inLen; ++nameIdx ) + { + uint16_t newId = uint16_t(eventId + nameIdx); + doAddName( inNames[nameIdx], newId, true ); + for( uint32_t clientIdx =0; clientIdx < clientCount; ++clientIdx ) + mZoneClients[clientIdx]->handleEventAdded( PxProfileEventName( inNames[nameIdx], PxProfileEventId( newId ) ) ); + } + + return eventId; + } + + virtual void setProfileZoneManager(PxProfileZoneManager* inMgr) + { + mProfileZoneManager = inMgr; + } + + virtual PxProfileZoneManager* getProfileZoneManager() + { + return mProfileZoneManager; + } + + + + const char* getName() { return mName; } + + PxProfileEventBufferClient* getEventBufferClient() { return this; } + + //SDK implementation + + void addClient( PxProfileZoneClient& inClient ) + { + TLockType lock( mMutex ); + mZoneClients.pushBack( &inClient ); + mEventsActive = true; + } + + void removeClient( PxProfileZoneClient& inClient ) + { + TLockType lock( mMutex ); + for ( uint32_t idx =0; idx < mZoneClients.size(); ++idx ) + { + if (mZoneClients[idx] == &inClient ) + { + inClient.handleClientRemoved(); + mZoneClients.replaceWithLast( idx ); + break; + } + } + mEventsActive = mZoneClients.size() != 0; + } + + virtual bool hasClients() const + { + return mEventsActive; + } + + virtual PxProfileNames getProfileNames() const + { + TLockType theLocker( mMutex ); + const PxProfileEventName* theNames = mEventNames.begin(); + uint32_t theEventCount = uint32_t(mEventNames.size()); + return PxProfileNames( theEventCount, theNames ); + } + + virtual void release() + { + PX_PROFILE_DELETE( mWrapper.getAllocator(), this ); + } + + //Implementation chaining the buffer flush to our clients + virtual void handleBufferFlush( const uint8_t* inData, uint32_t inLength ) + { + TLockType theLocker( mMutex ); + + uint32_t clientCount = mZoneClients.size(); + for( uint32_t idx =0; idx < clientCount; ++idx ) + mZoneClients[idx]->handleBufferFlush( inData, inLength ); + } + //Happens if something removes all the clients from the manager. + virtual void handleClientRemoved() {} + + //Send a profile event, optionally with a context. Events are sorted by thread + //and context in the client side. + virtual void startEvent( uint16_t inId, uint64_t contextId) + { + if( mEventsActive ) + { + TZoneEventBufferType::startEvent( inId, contextId ); + } + } + virtual void stopEvent( uint16_t inId, uint64_t contextId) + { + if( mEventsActive ) + { + TZoneEventBufferType::stopEvent( inId, contextId ); + } + } + + virtual void startEvent( uint16_t inId, uint64_t contextId, uint32_t threadId) + { + if( mEventsActive ) + { + TZoneEventBufferType::startEvent( inId, contextId, threadId ); + } + } + virtual void stopEvent( uint16_t inId, uint64_t contextId, uint32_t threadId ) + { + if( mEventsActive ) + { + TZoneEventBufferType::stopEvent( inId, contextId, threadId ); + } + } + + virtual void atEvent(uint16_t inId, uint64_t contextId, uint32_t threadId, uint64_t start, uint64_t stop) + { + if (mEventsActive) + { + TZoneEventBufferType::startEvent(inId, threadId, contextId, 0, 0, start); + TZoneEventBufferType::stopEvent(inId, threadId, contextId, 0, 0, stop); + } + } + + /** + * Set an specific events value. This is different than the profiling value + * for the event; it is a value recorded and kept around without a timestamp associated + * with it. This value is displayed when the event itself is processed. + */ + virtual void eventValue( uint16_t inId, uint64_t contextId, int64_t inValue ) + { + if( mEventsActive ) + { + TZoneEventBufferType::eventValue( inId, contextId, inValue ); + } + } + virtual void flushProfileEvents() + { + TZoneEventBufferType::flushProfileEvents(); + } + }; + +}} +#endif // PXPVDSDK_PXPROFILEZONEIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h b/src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h new file mode 100644 index 000000000..5a1f37076 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h @@ -0,0 +1,155 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPROFILEZONEMANAGER_H +#define PXPVDSDK_PXPROFILEZONEMANAGER_H + +#include "PxProfileEventSender.h" +#include "PxProfileEventNames.h" + +namespace physx { + + class PxAllocatorCallback; + + namespace profile { + + class PxProfileZone; + class PxProfileNameProvider; + + /** + \brief Profile zone handler for zone add/remove notification. + */ + class PxProfileZoneHandler + { + protected: + virtual ~PxProfileZoneHandler(){} + public: + /** + \brief On zone added notification + + \note Not a threadsafe call; handlers are expected to be able to handle + this from any thread. + + \param inSDK Added zone. + */ + virtual void onZoneAdded( PxProfileZone& inSDK ) = 0; + /** + \brief On zone removed notification + + \note Not a threadsafe call; handlers are expected to be able to handle + this from any thread. + + \param inSDK removed zone. + */ + virtual void onZoneRemoved( PxProfileZone& inSDK ) = 0; + }; + + /** + \brief The profiling system was setup in the expectation that there would be several + systems that each had its own island of profile information. PhysX, client code, + and APEX would be the first examples of these. Each one of these islands is represented + by a profile zone. + + The Manager is a singleton-like object where all these different systems can be registered + so that clients of the profiling system can have one point to capture *all* profiling events. + + Flushing the manager implies that you want to loop through all the profile zones and flush + each one. + + @see PxProfileEventFlusher + */ + class PxProfileZoneManager + : public PxProfileEventFlusher //Tell all SDK's to flush their queue of profile events. + { + protected: + virtual ~PxProfileZoneManager(){} + public: + /** + \brief Add new profile zone for the manager. + \note Threadsafe call, can be done from any thread. Handlers that are already connected + will get a new callback on the current thread. + + \param inSDK Profile zone to add. + */ + virtual void addProfileZone( PxProfileZone& inSDK ) = 0; + /** + \brief Removes profile zone from the manager. + \note Threadsafe call, can be done from any thread. Handlers that are already connected + will get a new callback on the current thread. + + \param inSDK Profile zone to remove. + */ + virtual void removeProfileZone( PxProfileZone& inSDK ) = 0; + + /** + \brief Add profile zone handler callback for the profile zone notifications. + + \note Threadsafe call. The new handler will immediately be notified about all + known SDKs. + + \param inHandler Profile zone handler to add. + */ + virtual void addProfileZoneHandler( PxProfileZoneHandler& inHandler ) = 0; + /** + \brief Removes profile zone handler callback for the profile zone notifications. + + \note Threadsafe call. The new handler will immediately be notified about all + known SDKs. + + \param inHandler Profile zone handler to remove. + */ + virtual void removeProfileZoneHandler( PxProfileZoneHandler& inHandler ) = 0; + + + /** + \brief Create a new profile zone. This means you don't need access to a PxFoundation to + create your profile zone object, and your object is automatically registered with + the profile zone manager. + + You still need to release your object when you are finished with it. + \param inSDKName Name of the SDK object. + \param inNames Option set of event id to name mappings. + \param inEventBufferByteSize rough maximum size of the event buffer. May exceed this size + by sizeof one event. When full an immediate call to all listeners is made. + */ + virtual PxProfileZone& createProfileZone( const char* inSDKName, PxProfileNames inNames = PxProfileNames(), uint32_t inEventBufferByteSize = 0x4000 /*16k*/ ) = 0; + + /** + \brief Releases the profile manager instance. + */ + virtual void release() = 0; + + /** + \brief Create the profile zone manager. + \param inAllocatorCallback Allocator callback. + */ + static PxProfileZoneManager& createProfileZoneManager(PxAllocatorCallback* inAllocatorCallback ); + }; + +} } + +#endif // PXPVDSDK_PXPROFILEZONEMANAGER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h b/src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h new file mode 100644 index 000000000..a336a763c --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h @@ -0,0 +1,173 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PXPVDSDK_PXPROFILEZONEMANAGERIMPL_H +#define PXPVDSDK_PXPROFILEZONEMANAGERIMPL_H + +#include "PxProfileZoneManager.h" +#include "PxProfileScopedMutexLock.h" +#include "PxPvdProfileZone.h" +#include "PxProfileAllocatorWrapper.h" + +#include "PsArray.h" +#include "PsMutex.h" + +namespace physx { namespace profile { + + struct NullEventNameProvider : public PxProfileNameProvider + { + virtual PxProfileNames getProfileNames() const { return PxProfileNames( 0, 0 ); } + }; + + class ZoneManagerImpl : public PxProfileZoneManager + { + typedef ScopedLockImpl TScopedLockType; + PxProfileAllocatorWrapper mWrapper; + PxProfileArray mZones; + PxProfileArray mHandlers; + shdfnd::Mutex mMutex; + + ZoneManagerImpl( const ZoneManagerImpl& inOther ); + ZoneManagerImpl& operator=( const ZoneManagerImpl& inOther ); + + public: + + ZoneManagerImpl(PxAllocatorCallback* inFoundation) + : mWrapper( inFoundation ) + , mZones( mWrapper ) + , mHandlers( mWrapper ) + {} + + virtual ~ZoneManagerImpl() + { + //This assert would mean that a profile zone is outliving us. + //This will cause a crash when the profile zone is released. + PX_ASSERT( mZones.size() == 0 ); + while( mZones.size() ) + removeProfileZone( *mZones.back() ); + } + + virtual void addProfileZone( PxProfileZone& inSDK ) + { + TScopedLockType lock( &mMutex ); + + if ( inSDK.getProfileZoneManager() != NULL ) + { + if ( inSDK.getProfileZoneManager() == this ) + return; + else //there must be two managers in the system somehow. + { + PX_ASSERT( false ); + inSDK.getProfileZoneManager()->removeProfileZone( inSDK ); + } + } + mZones.pushBack( &inSDK ); + inSDK.setProfileZoneManager( this ); + for ( uint32_t idx =0; idx < mHandlers.size(); ++idx ) + mHandlers[idx]->onZoneAdded( inSDK ); + } + + virtual void removeProfileZone( PxProfileZone& inSDK ) + { + TScopedLockType lock( &mMutex ); + if ( inSDK.getProfileZoneManager() == NULL ) + return; + + else if ( inSDK.getProfileZoneManager() != this ) + { + PX_ASSERT( false ); + inSDK.getProfileZoneManager()->removeProfileZone( inSDK ); + return; + } + + inSDK.setProfileZoneManager( NULL ); + for ( uint32_t idx = 0; idx < mZones.size(); ++idx ) + { + if ( mZones[idx] == &inSDK ) + { + for ( uint32_t handler =0; handler < mHandlers.size(); ++handler ) + mHandlers[handler]->onZoneRemoved( inSDK ); + mZones.replaceWithLast( idx ); + } + } + } + + virtual void flushProfileEvents() + { + uint32_t sdkCount = mZones.size(); + for ( uint32_t idx = 0; idx < sdkCount; ++idx ) + mZones[idx]->flushProfileEvents(); + } + + virtual void addProfileZoneHandler( PxProfileZoneHandler& inHandler ) + { + TScopedLockType lock( &mMutex ); + mHandlers.pushBack( &inHandler ); + for ( uint32_t idx = 0; idx < mZones.size(); ++idx ) + inHandler.onZoneAdded( *mZones[idx] ); + } + + virtual void removeProfileZoneHandler( PxProfileZoneHandler& inHandler ) + { + TScopedLockType lock( &mMutex ); + for( uint32_t idx = 0; idx < mZones.size(); ++idx ) + inHandler.onZoneRemoved( *mZones[idx] ); + for( uint32_t idx = 0; idx < mHandlers.size(); ++idx ) + { + if ( mHandlers[idx] == &inHandler ) + mHandlers.replaceWithLast( idx ); + } + } + + virtual PxProfileZone& createProfileZone( const char* inSDKName, PxProfileNameProvider* inProvider, uint32_t inEventBufferByteSize ) + { + NullEventNameProvider nullProvider; + if ( inProvider == NULL ) + inProvider = &nullProvider; + return createProfileZone( inSDKName, inProvider->getProfileNames(), inEventBufferByteSize ); + } + + + virtual PxProfileZone& createProfileZone( const char* inSDKName, PxProfileNames inNames, uint32_t inEventBufferByteSize ) + { + PxProfileZone& retval( PxProfileZone::createProfileZone( &mWrapper.getAllocator(), inSDKName, inNames, inEventBufferByteSize ) ); + addProfileZone( retval ); + return retval; + } + + virtual void release() + { + PX_PROFILE_DELETE( mWrapper.getAllocator(), this ); + } + }; +} } + + +#endif // PXPVDSDK_PXPROFILEZONEMANAGERIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvd.cpp b/src/PhysX/physx/source/pvd/src/PxPvd.cpp new file mode 100644 index 000000000..7873e373a --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvd.cpp @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdImpl.h" +namespace physx +{ +namespace pvdsdk +{ +ForwardingAllocator gForwardingAllocator; +PxAllocatorCallback* gPvdAllocatorCallback = &gForwardingAllocator; +} // namespace pvdsdk + +PxPvd* PxCreatePvd(PxFoundation& foundation) +{ + pvdsdk::gPvdAllocatorCallback = &foundation.getAllocatorCallback(); + pvdsdk::PvdImpl::initialize(); + return pvdsdk::PvdImpl::getInstance(); +} + +} // namespace physx diff --git a/src/PhysX/physx/source/pvd/src/PxPvdBits.h b/src/PhysX/physx/source/pvd/src/PxPvdBits.h new file mode 100644 index 000000000..2d2b93a7a --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdBits.h @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDBITS_H +#define PXPVDSDK_PXPVDBITS_H + +#include "PxPvdObjectModelBaseTypes.h" + +namespace physx +{ +namespace pvdsdk +{ + +// Marshallers cannot assume src is aligned, but they can assume dest is aligned. +typedef void (*TSingleMarshaller)(const uint8_t* src, uint8_t* dest); +typedef void (*TBlockMarshaller)(const uint8_t* src, uint8_t* dest, uint32_t numItems); + +template +static inline void doSwapBytes(uint8_t* __restrict inData) +{ + for(uint32_t idx = 0; idx < ByteCount / 2; ++idx) + { + uint32_t endIdx = ByteCount - idx - 1; + uint8_t theTemp = inData[idx]; + inData[idx] = inData[endIdx]; + inData[endIdx] = theTemp; + } +} + +template +static inline void doSwapBytes(uint8_t* __restrict inData, uint32_t itemCount) +{ + uint8_t* end = inData + itemCount * ByteCount; + for(; inData < end; inData += ByteCount) + doSwapBytes(inData); +} + +static inline void swapBytes(uint8_t* __restrict dataPtr, uint32_t numBytes, uint32_t itemWidth) +{ + uint32_t numItems = numBytes / itemWidth; + switch(itemWidth) + { + case 1: + break; + case 2: + doSwapBytes<2>(dataPtr, numItems); + break; + case 4: + doSwapBytes<4>(dataPtr, numItems); + break; + case 8: + doSwapBytes<8>(dataPtr, numItems); + break; + case 16: + doSwapBytes<16>(dataPtr, numItems); + break; + default: + PX_ASSERT(false); + break; + } +} + +static inline void swapBytes(uint8_t&) +{ +} +static inline void swapBytes(int8_t&) +{ +} +static inline void swapBytes(uint16_t& inData) +{ + doSwapBytes<2>(reinterpret_cast(&inData)); +} +static inline void swapBytes(int16_t& inData) +{ + doSwapBytes<2>(reinterpret_cast(&inData)); +} +static inline void swapBytes(uint32_t& inData) +{ + doSwapBytes<4>(reinterpret_cast(&inData)); +} +static inline void swapBytes(int32_t& inData) +{ + doSwapBytes<4>(reinterpret_cast(&inData)); +} +static inline void swapBytes(float& inData) +{ + doSwapBytes<4>(reinterpret_cast(&inData)); +} +static inline void swapBytes(uint64_t& inData) +{ + doSwapBytes<8>(reinterpret_cast(&inData)); +} +static inline void swapBytes(int64_t& inData) +{ + doSwapBytes<8>(reinterpret_cast(&inData)); +} +static inline void swapBytes(double& inData) +{ + doSwapBytes<8>(reinterpret_cast(&inData)); +} + +static inline bool checkLength(const uint8_t* inStart, const uint8_t* inStop, uint32_t inLength) +{ + return static_cast(inStop - inStart) >= inLength; +} +} +} +#endif // PXPVDSDK_PXPVDBITS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h b/src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h new file mode 100644 index 000000000..0a3d3fccc --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h @@ -0,0 +1,126 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDBYTESTREAMS_H +#define PXPVDSDK_PXPVDBYTESTREAMS_H +#include "PxPvdObjectModelBaseTypes.h" + +namespace physx +{ +namespace pvdsdk +{ + +static inline uint32_t strLen(const char* inStr) +{ + uint32_t len = 0; + if(inStr) + { + while(*inStr) + { + ++len; + ++inStr; + } + } + return len; +} + +class PvdInputStream +{ + protected: + virtual ~PvdInputStream() + { + } + + public: + // Return false if you can't write the number of bytes requested + // But make an absolute best effort to read the data... + virtual bool read(uint8_t* buffer, uint32_t& len) = 0; + + template + bool read(TDataType* buffer, uint32_t numItems) + { + uint32_t expected = numItems; + uint32_t amountToRead = numItems * sizeof(TDataType); + read(reinterpret_cast(buffer), amountToRead); + numItems = amountToRead / sizeof(TDataType); + PX_ASSERT(numItems == expected); + return expected == numItems; + } + + template + PvdInputStream& operator>>(TDataType& data) + { + uint32_t dataSize = static_cast(sizeof(TDataType)); + bool success = read(reinterpret_cast(&data), dataSize); + // PX_ASSERT( success ); + // PX_ASSERT( dataSize == sizeof( data ) ); + (void)success; + return *this; + } +}; + +class PvdOutputStream +{ + protected: + virtual ~PvdOutputStream() + { + } + + public: + // Return false if you can't write the number of bytes requested + // But make an absolute best effort to write the data... + virtual bool write(const uint8_t* buffer, uint32_t len) = 0; + virtual bool directCopy(PvdInputStream& inStream, uint32_t len) = 0; + + template + bool write(const TDataType* buffer, uint32_t numItems) + { + return write(reinterpret_cast(buffer), numItems * sizeof(TDataType)); + } + + template + PvdOutputStream& operator<<(const TDataType& data) + { + bool success = write(reinterpret_cast(&data), sizeof(data)); + PX_ASSERT(success); + (void)success; + return *this; + } + + PvdOutputStream& operator<<(const char* inString) + { + if(inString && *inString) + { + uint32_t len(strLen(inString)); + write(inString, len); + } + return *this; + } +}; +} +} +#endif // PXPVDSDK_PXPVDBYTESTREAMS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h new file mode 100644 index 000000000..d0b843680 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h @@ -0,0 +1,55 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDCOMMSTREAMEVENTSINK_H +#define PXPVDSDK_PXPVDCOMMSTREAMEVENTSINK_H + +#include "PxPvdObjectModelBaseTypes.h" +#include "PxPvdCommStreamEvents.h" +#include "PxPvdCommStreamTypes.h" + +namespace physx +{ +namespace pvdsdk +{ + +class PvdCommStreamEventSink +{ + public: + template + static void writeStreamEvent(const EventSerializeable& evt, PvdCommStreamEventTypes::Enum evtType, TStreamType& stream) + { + EventStreamifier streamifier_concrete(stream); + PvdEventSerializer& streamifier(streamifier_concrete); + streamifier.streamify(evtType); + const_cast(evt).serialize(streamifier); + } +}; + +} // pvd +} // physx +#endif // PXPVDSDK_PXPVDCOMMSTREAMEVENTSINK_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h new file mode 100644 index 000000000..89361be66 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h @@ -0,0 +1,987 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDCOMMSTREAMEVENTS_H +#define PXPVDSDK_PXPVDCOMMSTREAMEVENTS_H + +#include "foundation/PxVec3.h" +#include "foundation/PxFlags.h" + +#include "PxPvdObjectModelBaseTypes.h" +#include "PsTime.h" + +namespace physx +{ +namespace pvdsdk +{ + +struct CommStreamFlagTypes +{ + enum Enum + { + Is64BitPtr = 1 + }; +}; + +typedef PxFlags CommStreamFlags; + +template +struct PvdCommVariableSizedEventCheck +{ + bool variable_size_check; +}; + +// Pick out the events that are possibly very large. +// This helps us keep our buffers close to the size the user requested. +#define DECLARE_TYPE_VARIABLE_SIZED(type) \ + template <> \ + struct PvdCommVariableSizedEventCheck \ + { \ + uint32_t variable_size_check; \ + }; + +struct NameHandleValue; +struct StreamPropMessageArg; +struct StringHandleEvent; +struct CreateClass; +struct DeriveClass; +struct CreateProperty; +struct CreatePropertyMessage; +struct CreateInstance; +struct SetPropertyValue; +struct BeginSetPropertyValue; +struct AppendPropertyValueData; +struct EndSetPropertyValue; +struct SetPropertyMessage; +struct BeginPropertyMessageGroup; +struct SendPropertyMessageFromGroup; +struct EndPropertyMessageGroup; +struct CreateDestroyInstanceProperty; +struct PushBackObjectRef; +struct RemoveObjectRef; +struct BeginSection; +struct EndSection; +struct SetPickable; +struct SetColor; +struct SetIsTopLevel; +struct SetCamera; +struct AddProfileZone; +struct AddProfileZoneEvent; +struct StreamEndEvent; +struct ErrorMessage; +struct OriginShift; +struct DestroyInstance; + +#define DECLARE_COMM_STREAM_EVENTS \ + \ +DECLARE_PVD_COMM_STREAM_EVENT(StringHandleEvent) \ +DECLARE_PVD_COMM_STREAM_EVENT(CreateClass) \ +DECLARE_PVD_COMM_STREAM_EVENT(DeriveClass) \ +DECLARE_PVD_COMM_STREAM_EVENT(CreateProperty) \ +DECLARE_PVD_COMM_STREAM_EVENT(CreatePropertyMessage) \ +DECLARE_PVD_COMM_STREAM_EVENT(CreateInstance) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetPropertyValue) \ +DECLARE_PVD_COMM_STREAM_EVENT(BeginSetPropertyValue) \ +DECLARE_PVD_COMM_STREAM_EVENT(AppendPropertyValueData) \ +DECLARE_PVD_COMM_STREAM_EVENT(EndSetPropertyValue) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetPropertyMessage) \ +DECLARE_PVD_COMM_STREAM_EVENT(BeginPropertyMessageGroup) \ +DECLARE_PVD_COMM_STREAM_EVENT(SendPropertyMessageFromGroup) \ +DECLARE_PVD_COMM_STREAM_EVENT(EndPropertyMessageGroup) \ +DECLARE_PVD_COMM_STREAM_EVENT(DestroyInstance) \ +DECLARE_PVD_COMM_STREAM_EVENT(PushBackObjectRef) \ +DECLARE_PVD_COMM_STREAM_EVENT(RemoveObjectRef) \ +DECLARE_PVD_COMM_STREAM_EVENT(BeginSection) \ +DECLARE_PVD_COMM_STREAM_EVENT(EndSection) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetPickable) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetColor) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetIsTopLevel) \ +DECLARE_PVD_COMM_STREAM_EVENT(SetCamera) \ +DECLARE_PVD_COMM_STREAM_EVENT(AddProfileZone) \ +DECLARE_PVD_COMM_STREAM_EVENT(AddProfileZoneEvent) \ +DECLARE_PVD_COMM_STREAM_EVENT(StreamEndEvent) \ +DECLARE_PVD_COMM_STREAM_EVENT(ErrorMessage) \ +DECLARE_PVD_COMM_STREAM_EVENT_NO_COMMA(OriginShift) + +struct PvdCommStreamEventTypes +{ + enum Enum + { + Unknown = 0, +#define DECLARE_PVD_COMM_STREAM_EVENT(x) x, +#define DECLARE_PVD_COMM_STREAM_EVENT_NO_COMMA(x) x + DECLARE_COMM_STREAM_EVENTS +#undef DECLARE_PVD_COMM_STREAM_EVENT_NO_COMMA +#undef DECLARE_PVD_COMM_STREAM_EVENT + , Last + }; +}; + +template +struct DatatypeToCommEventType +{ + bool compile_error; +}; +template +struct CommEventTypeToDatatype +{ + bool compile_error; +}; + +#define DECLARE_PVD_COMM_STREAM_EVENT(x) \ + template <> \ + struct DatatypeToCommEventType \ + { \ + enum Enum \ + { \ + EEventTypeMap = PvdCommStreamEventTypes::x \ + }; \ + }; \ + template <> \ + struct CommEventTypeToDatatype \ + { \ + typedef x TEventType; \ + }; +#define DECLARE_PVD_COMM_STREAM_EVENT_NO_COMMA(x) \ + \ +template<> struct DatatypeToCommEventType \ + { \ + enum Enum \ + { \ + EEventTypeMap = PvdCommStreamEventTypes::x \ + }; \ + }; \ + \ +template<> struct CommEventTypeToDatatype \ + { \ + typedef x TEventType; \ + }; + +DECLARE_COMM_STREAM_EVENTS +#undef DECLARE_PVD_COMM_STREAM_EVENT_NO_COMMA +#undef DECLARE_PVD_COMM_STREAM_EVENT + +template +PvdCommStreamEventTypes::Enum getCommStreamEventType() +{ + return static_cast(DatatypeToCommEventType::EEventTypeMap); +} + +struct StreamNamespacedName +{ + StringHandle mNamespace; // StringHandle handles + StringHandle mName; + StreamNamespacedName(StringHandle ns = 0, StringHandle nm = 0) : mNamespace(ns), mName(nm) + { + } +}; + +class EventSerializeable; + +class PvdEventSerializer +{ + protected: + virtual ~PvdEventSerializer() + { + } + + public: + virtual void streamify(uint8_t& val) = 0; + virtual void streamify(uint16_t& val) = 0; + virtual void streamify(uint32_t& val) = 0; + virtual void streamify(float& val) = 0; + virtual void streamify(uint64_t& val) = 0; + virtual void streamify(String& val) = 0; + virtual void streamify(DataRef& data) = 0; + virtual void streamify(DataRef& data) = 0; + virtual void streamify(DataRef& data) = 0; + virtual void streamify(DataRef& data) = 0; + + void streamify(StringHandle& hdl) + { + streamify(hdl.mHandle); + } + void streamify(CommStreamFlags& flags) + { + uint32_t val(flags); + streamify(val); + flags = CommStreamFlags(val); + } + + void streamify(PvdCommStreamEventTypes::Enum& val) + { + uint8_t detyped = static_cast(val); + streamify(detyped); + val = static_cast(detyped); + } + void streamify(PropertyType::Enum& val) + { + uint8_t detyped = static_cast(val); + streamify(detyped); + val = static_cast(detyped); + } + + void streamify(bool& val) + { + uint8_t detyped = uint8_t(val ? 1 : 0); + streamify(detyped); + val = detyped ? true : false; + } + + void streamify(StreamNamespacedName& name) + { + streamify(name.mNamespace); + streamify(name.mName); + } + + void streamify(PvdColor& color) + { + streamify(color.r); + streamify(color.g); + streamify(color.b); + streamify(color.a); + } + + void streamify(PxVec3& vec) + { + streamify(vec.x); + streamify(vec.y); + streamify(vec.z); + } + + static uint32_t measure(const EventSerializeable& evt); +}; + +class EventSerializeable +{ + protected: + virtual ~EventSerializeable() + { + } + + public: + virtual void serialize(PvdEventSerializer& serializer) = 0; +}; + +/** Numbers generated from random.org +129919156 17973702 401496246 144984007 336950759 +907025328 837150850 679717896 601529147 269478202 +*/ +struct StreamInitialization : public EventSerializeable +{ + static uint32_t getStreamId() + { + return 837150850; + } + static uint32_t getStreamVersion() + { + return 1; + } + + uint32_t mStreamId; + uint32_t mStreamVersion; + uint64_t mTimestampNumerator; + uint64_t mTimestampDenominator; + CommStreamFlags mStreamFlags; + StreamInitialization() + : mStreamId(getStreamId()) + , mStreamVersion(getStreamVersion()) + , mTimestampNumerator(physx::shdfnd::Time::getCounterFrequency().mNumerator * 10) + , mTimestampDenominator(physx::shdfnd::Time::getCounterFrequency().mDenominator) + , mStreamFlags(sizeof(void*) == 4 ? 0 : 1) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mStreamId); + s.streamify(mStreamVersion); + s.streamify(mTimestampNumerator); + s.streamify(mTimestampDenominator); + s.streamify(mStreamFlags); + } +}; + +struct EventGroup : public EventSerializeable +{ + uint32_t mDataSize; // in bytes, data directly follows this header + uint32_t mNumEvents; + uint64_t mStreamId; + uint64_t mTimestamp; + + EventGroup(uint32_t dataSize = 0, uint32_t numEvents = 0, uint64_t streamId = 0, uint64_t ts = 0) + : mDataSize(dataSize), mNumEvents(numEvents), mStreamId(streamId), mTimestamp(ts) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mDataSize); + s.streamify(mNumEvents); + s.streamify(mStreamId); + s.streamify(mTimestamp); + } +}; + +struct StringHandleEvent : public EventSerializeable +{ + String mString; + uint32_t mHandle; + StringHandleEvent(String str, uint32_t hdl) : mString(str), mHandle(hdl) + { + } + StringHandleEvent() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mString); + s.streamify(mHandle); + } +}; + +DECLARE_TYPE_VARIABLE_SIZED(StringHandleEvent) + +typedef uint64_t Timestamp; + +struct CreateClass : public EventSerializeable +{ + StreamNamespacedName mName; + CreateClass(StreamNamespacedName nm) : mName(nm) + { + } + CreateClass() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mName); + } +}; + +struct DeriveClass : public EventSerializeable +{ + StreamNamespacedName mParent; + StreamNamespacedName mChild; + + DeriveClass(StreamNamespacedName p, StreamNamespacedName c) : mParent(p), mChild(c) + { + } + DeriveClass() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mParent); + s.streamify(mChild); + } +}; + +struct NameHandleValue : public EventSerializeable +{ + StringHandle mName; + uint32_t mValue; + NameHandleValue(StringHandle name, uint32_t val) : mName(name), mValue(val) + { + } + NameHandleValue() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mName); + s.streamify(mValue); + } +}; +/*virtual PvdError createProperty( StreamNamespacedName clsName, StringHandle name, StringHandle semantic + , StreamNamespacedName dtypeName, PropertyType::Enum propertyType + , DataRef values = DataRef() ) = 0; */ +struct CreateProperty : public EventSerializeable +{ + StreamNamespacedName mClass; + StringHandle mName; + StringHandle mSemantic; + StreamNamespacedName mDatatypeName; + PropertyType::Enum mPropertyType; + DataRef mValues; + + CreateProperty(StreamNamespacedName cls, StringHandle name, StringHandle semantic, StreamNamespacedName dtypeName, + PropertyType::Enum ptype, DataRef values) + : mClass(cls), mName(name), mSemantic(semantic), mDatatypeName(dtypeName), mPropertyType(ptype), mValues(values) + { + } + CreateProperty() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mClass); + s.streamify(mName); + s.streamify(mSemantic); + s.streamify(mDatatypeName); + s.streamify(mPropertyType); + s.streamify(mValues); + } +}; + +struct StreamPropMessageArg : public EventSerializeable +{ + StringHandle mPropertyName; + StreamNamespacedName mDatatypeName; + uint32_t mMessageOffset; + uint32_t mByteSize; + StreamPropMessageArg(StringHandle pname, StreamNamespacedName dtypeName, uint32_t offset, uint32_t byteSize) + : mPropertyName(pname), mDatatypeName(dtypeName), mMessageOffset(offset), mByteSize(byteSize) + { + } + + StreamPropMessageArg() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mPropertyName); + s.streamify(mDatatypeName); + s.streamify(mMessageOffset); + s.streamify(mByteSize); + } +}; + +/* + virtual PvdError createPropertyMessage( StreamNamespacedName cls, StreamNamespacedName msgName + , DataRef entries, uint32_t messageSizeInBytes ) = + 0;*/ +struct CreatePropertyMessage : public EventSerializeable +{ + StreamNamespacedName mClass; + StreamNamespacedName mMessageName; + DataRef mMessageEntries; + uint32_t mMessageByteSize; + + CreatePropertyMessage(StreamNamespacedName cls, StreamNamespacedName msgName, DataRef propArg, + uint32_t messageByteSize) + : mClass(cls), mMessageName(msgName), mMessageEntries(propArg), mMessageByteSize(messageByteSize) + { + } + CreatePropertyMessage() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mClass); + s.streamify(mMessageName); + s.streamify(mMessageEntries); + s.streamify(mMessageByteSize); + } +}; + +/**Changing immediate data on instances*/ + +// virtual PvdError createInstance( StreamNamespacedName cls, uint64_t instance ) = 0; +struct CreateInstance : public EventSerializeable +{ + StreamNamespacedName mClass; + uint64_t mInstanceId; + + CreateInstance(StreamNamespacedName cls, uint64_t streamId) : mClass(cls), mInstanceId(streamId) + { + } + CreateInstance() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mClass); + s.streamify(mInstanceId); + } +}; + +// virtual PvdError setPropertyValue( uint64_t instance, StringHandle name, DataRef data, +// StreamNamespacedName incomingTypeName ) = 0; +struct SetPropertyValue : public EventSerializeable +{ + uint64_t mInstanceId; + StringHandle mPropertyName; + DataRef mData; + StreamNamespacedName mIncomingTypeName; + uint32_t mNumItems; + + SetPropertyValue(uint64_t instance, StringHandle name, DataRef data, + StreamNamespacedName incomingTypeName, uint32_t numItems) + : mInstanceId(instance), mPropertyName(name), mData(data), mIncomingTypeName(incomingTypeName), mNumItems(numItems) + { + } + + SetPropertyValue() + { + } + + void serializeBeginning(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mPropertyName); + s.streamify(mIncomingTypeName); + s.streamify(mNumItems); + } + + void serialize(PvdEventSerializer& s) + { + serializeBeginning(s); + s.streamify(mData); + } +}; + +DECLARE_TYPE_VARIABLE_SIZED(SetPropertyValue) + +struct BeginSetPropertyValue : public EventSerializeable +{ + uint64_t mInstanceId; + StringHandle mPropertyName; + StreamNamespacedName mIncomingTypeName; + + BeginSetPropertyValue(uint64_t instance, StringHandle name, StreamNamespacedName incomingTypeName) + : mInstanceId(instance), mPropertyName(name), mIncomingTypeName(incomingTypeName) + { + } + BeginSetPropertyValue() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mPropertyName); + s.streamify(mIncomingTypeName); + } +}; + +// virtual PvdError appendPropertyValueData( DataRef data ) = 0; +struct AppendPropertyValueData : public EventSerializeable +{ + DataRef mData; + uint32_t mNumItems; + AppendPropertyValueData(DataRef data, uint32_t numItems) : mData(data), mNumItems(numItems) + { + } + AppendPropertyValueData() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mData); + s.streamify(mNumItems); + } +}; + +DECLARE_TYPE_VARIABLE_SIZED(AppendPropertyValueData) + +// virtual PvdError endSetPropertyValue() = 0; +struct EndSetPropertyValue : public EventSerializeable +{ + EndSetPropertyValue() + { + } + + void serialize(PvdEventSerializer&) + { + } +}; + +// virtual PvdError setPropertyMessage( uint64_t instance, StreamNamespacedName msgName, DataRef data ) = +// 0; +struct SetPropertyMessage : public EventSerializeable +{ + uint64_t mInstanceId; + StreamNamespacedName mMessageName; + DataRef mData; + + SetPropertyMessage(uint64_t instance, StreamNamespacedName msgName, DataRef data) + : mInstanceId(instance), mMessageName(msgName), mData(data) + { + } + + SetPropertyMessage() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mMessageName); + s.streamify(mData); + } +}; + +DECLARE_TYPE_VARIABLE_SIZED(SetPropertyMessage) + +// virtual PvdError beginPropertyMessageGroup( StreamNamespacedName msgName ) = 0; +struct BeginPropertyMessageGroup : public EventSerializeable +{ + StreamNamespacedName mMsgName; + BeginPropertyMessageGroup(StreamNamespacedName msgName) : mMsgName(msgName) + { + } + BeginPropertyMessageGroup() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mMsgName); + } +}; + +// virtual PvdError sendPropertyMessageFromGroup( uint64_t instance, DataRef data ) = 0; +struct SendPropertyMessageFromGroup : public EventSerializeable +{ + uint64_t mInstance; + DataRef mData; + + SendPropertyMessageFromGroup(uint64_t instance, DataRef data) : mInstance(instance), mData(data) + { + } + SendPropertyMessageFromGroup() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstance); + s.streamify(mData); + } +}; + +DECLARE_TYPE_VARIABLE_SIZED(SendPropertyMessageFromGroup) + +// virtual PvdError endPropertyMessageGroup() = 0; +struct EndPropertyMessageGroup : public EventSerializeable +{ + EndPropertyMessageGroup() + { + } + + void serialize(PvdEventSerializer&) + { + } +}; + +struct PushBackObjectRef : public EventSerializeable +{ + uint64_t mInstanceId; + StringHandle mProperty; + uint64_t mObjectRef; + + PushBackObjectRef(uint64_t instId, StringHandle prop, uint64_t objRef) + : mInstanceId(instId), mProperty(prop), mObjectRef(objRef) + { + } + + PushBackObjectRef() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mProperty); + s.streamify(mObjectRef); + } +}; + +struct RemoveObjectRef : public EventSerializeable +{ + uint64_t mInstanceId; + StringHandle mProperty; + uint64_t mObjectRef; + + RemoveObjectRef(uint64_t instId, StringHandle prop, uint64_t objRef) + : mInstanceId(instId), mProperty(prop), mObjectRef(objRef) + { + } + + RemoveObjectRef() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mProperty); + s.streamify(mObjectRef); + } +}; + +// virtual PvdError destroyInstance( uint64_t key ) = 0; +struct DestroyInstance : public EventSerializeable +{ + uint64_t mInstanceId; + DestroyInstance(uint64_t instance) : mInstanceId(instance) + { + } + DestroyInstance() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + } +}; + +// virtual PvdError beginSection( uint64_t sectionId, StringHandle name ) = 0; +struct BeginSection : public EventSerializeable +{ + uint64_t mSectionId; + StringHandle mName; + Timestamp mTimestamp; + BeginSection(uint64_t sectionId, StringHandle name, uint64_t timestamp) + : mSectionId(sectionId), mName(name), mTimestamp(timestamp) + { + } + BeginSection() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mSectionId); + s.streamify(mName); + s.streamify(mTimestamp); + } +}; +// virtual PvdError endSection( uint64_t sectionId, StringHandle name ) = 0; +struct EndSection : public EventSerializeable +{ + uint64_t mSectionId; + StringHandle mName; + Timestamp mTimestamp; + EndSection(uint64_t sectionId, StringHandle name, uint64_t timestamp) + : mSectionId(sectionId), mName(name), mTimestamp(timestamp) + { + } + EndSection() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mSectionId); + s.streamify(mName); + s.streamify(mTimestamp); + } +}; + +// virtual void setPickable( void* instance, bool pickable ) = 0; +struct SetPickable : public EventSerializeable +{ + uint64_t mInstanceId; + bool mPickable; + SetPickable(uint64_t instId, bool pick) : mInstanceId(instId), mPickable(pick) + { + } + SetPickable() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mPickable); + } +}; +// virtual void setColor( void* instance, const PvdColor& color ) = 0; +struct SetColor : public EventSerializeable +{ + uint64_t mInstanceId; + PvdColor mColor; + SetColor(uint64_t instId, PvdColor color) : mInstanceId(instId), mColor(color) + { + } + SetColor() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mColor); + } +}; + +// virtual void setColor( void* instance, const PvdColor& color ) = 0; +struct SetIsTopLevel : public EventSerializeable +{ + uint64_t mInstanceId; + bool mIsTopLevel; + + SetIsTopLevel(uint64_t instId, bool topLevel) : mInstanceId(instId), mIsTopLevel(topLevel) + { + } + SetIsTopLevel() : mIsTopLevel(false) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mIsTopLevel); + } +}; + +struct SetCamera : public EventSerializeable +{ + String mName; + PxVec3 mPosition; + PxVec3 mUp; + PxVec3 mTarget; + SetCamera(String name, const PxVec3& pos, const PxVec3& up, const PxVec3& target) + : mName(name), mPosition(pos), mUp(up), mTarget(target) + { + } + SetCamera() : mName(NULL) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mName); + s.streamify(mPosition); + s.streamify(mUp); + s.streamify(mTarget); + } +}; + +struct ErrorMessage : public EventSerializeable +{ + uint32_t mCode; + String mMessage; + String mFile; + uint32_t mLine; + + ErrorMessage(uint32_t code, String message, String file, uint32_t line) + : mCode(code), mMessage(message), mFile(file), mLine(line) + { + } + + ErrorMessage() : mMessage(NULL), mFile(NULL) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mCode); + s.streamify(mMessage); + s.streamify(mFile); + s.streamify(mLine); + } +}; + +struct AddProfileZone : public EventSerializeable +{ + uint64_t mInstanceId; + String mName; + AddProfileZone(uint64_t iid, String nm) : mInstanceId(iid), mName(nm) + { + } + AddProfileZone() : mName(NULL) + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mName); + } +}; + +struct AddProfileZoneEvent : public EventSerializeable +{ + uint64_t mInstanceId; + String mName; + uint16_t mEventId; + bool mCompileTimeEnabled; + AddProfileZoneEvent(uint64_t iid, String nm, uint16_t eid, bool cte) + : mInstanceId(iid), mName(nm), mEventId(eid), mCompileTimeEnabled(cte) + { + } + AddProfileZoneEvent() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mName); + s.streamify(mEventId); + s.streamify(mCompileTimeEnabled); + } +}; + +struct StreamEndEvent : public EventSerializeable +{ + String mName; + StreamEndEvent() : mName("StreamEnd") + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mName); + } +}; + +struct OriginShift : public EventSerializeable +{ + uint64_t mInstanceId; + PxVec3 mShift; + + OriginShift(uint64_t iid, const PxVec3& shift) : mInstanceId(iid), mShift(shift) + { + } + OriginShift() + { + } + + void serialize(PvdEventSerializer& s) + { + s.streamify(mInstanceId); + s.streamify(mShift); + } +}; +} // pvdsdk +} // physx + +#endif // PXPVDSDK_PXPVDCOMMSTREAMEVENTS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h new file mode 100644 index 000000000..b57075086 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h @@ -0,0 +1,229 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDCOMMSTREAMTYPES_H +#define PXPVDSDK_PXPVDCOMMSTREAMTYPES_H + +#include "foundation/PxErrorCallback.h" +#include "pvd/PxPvdTransport.h" + +#include "PxPvdRenderBuffer.h" +#include "PxPvdObjectModelBaseTypes.h" +#include "PxPvdCommStreamEvents.h" +#include "PxPvdDataStream.h" +#include "PsMutex.h" + +namespace physx +{ +namespace profile +{ +class PxProfileZone; +class PxProfileMemoryEventBuffer; +} +namespace pvdsdk +{ +struct PvdErrorMessage; +class PvdObjectModelMetaData; + +DEFINE_PVD_TYPE_NAME_MAP(profile::PxProfileZone, "_debugger_", "PxProfileZone") +DEFINE_PVD_TYPE_NAME_MAP(profile::PxProfileMemoryEventBuffer, "_debugger_", "PxProfileMemoryEventBuffer") +DEFINE_PVD_TYPE_NAME_MAP(PvdErrorMessage, "_debugger_", "PvdErrorMessage") +// All event streams are on the 'events' property of objects of these types +static inline NamespacedName getMemoryEventTotalsClassName() +{ + return NamespacedName("_debugger", "MemoryEventTotals"); +} + +class PvdOMMetaDataProvider +{ + protected: + virtual ~PvdOMMetaDataProvider() + { + } + + public: + virtual void addRef() = 0; + virtual void release() = 0; + virtual PvdObjectModelMetaData& lock() = 0; + virtual void unlock() = 0; + virtual bool createInstance(const NamespacedName& clsName, const void* instance) = 0; + virtual bool isInstanceValid(const void* instance) = 0; + virtual void destroyInstance(const void* instance) = 0; + virtual int32_t getInstanceClassType(const void* instance) = 0; +}; + +class PvdCommStreamEmbeddedTypes +{ + public: + static const char* getProfileEventStreamSemantic() + { + return "profile event stream"; + } + static const char* getMemoryEventStreamSemantic() + { + return "memory event stream"; + } + static const char* getRendererEventStreamSemantic() + { + return "render event stream"; + } +}; + +class PvdCommStreamEventBufferClient; + +template +struct EventStreamifier : public PvdEventSerializer +{ + TStreamType& mBuffer; + EventStreamifier(TStreamType& buf) : mBuffer(buf) + { + } + + template + void write(const TDataType& type) + { + mBuffer.write(reinterpret_cast(&type), sizeof(TDataType)); + } + template + void write(const TDataType* type, uint32_t count) + { + mBuffer.write(reinterpret_cast(type), count * sizeof(TDataType)); + } + + void writeRef(DataRef data) + { + uint32_t amount = static_cast(data.size()); + write(amount); + write(data.begin(), amount); + } + void writeRef(DataRef data) + { + uint32_t amount = static_cast(data.size()); + write(amount); + write(data.begin(), amount); + } + template + void writeRef(DataRef data) + { + uint32_t amount = static_cast(data.size()); + write(amount); + for(uint32_t idx = 0; idx < amount; ++idx) + { + TDataType& dtype(const_cast(data[idx])); + dtype.serialize(*this); + } + } + + virtual void streamify(uint16_t& val) + { + write(val); + } + virtual void streamify(uint8_t& val) + { + write(val); + } + virtual void streamify(uint32_t& val) + { + write(val); + } + virtual void streamify(float& val) + { + write(val); + } + virtual void streamify(uint64_t& val) + { + write(val); + } + virtual void streamify(PvdDebugText& val) + { + write(val.color); + write(val.position); + write(val.size); + streamify(val.string); + } + + virtual void streamify(String& val) + { + uint32_t len = 0; + String temp = nonNull(val); + if(*temp) + len = static_cast(strlen(temp) + 1); + write(len); + write(val, len); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + + private: + EventStreamifier& operator=(const EventStreamifier&); +}; + +struct MeasureStream +{ + uint32_t mSize; + MeasureStream() : mSize(0) + { + } + template + void write(const TDataType& val) + { + mSize += sizeof(val); + } + template + void write(const TDataType*, uint32_t count) + { + mSize += sizeof(TDataType) * count; + } +}; + +struct DataStreamState +{ + enum Enum + { + Open, + SetPropertyValue, + PropertyMessageGroup + }; +}; + +} // pvdsdk +} // physx +#endif // PXPVDSDK_PXPVDCOMMSTREAMTYPES_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp b/src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp new file mode 100644 index 000000000..5195c7d44 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp @@ -0,0 +1,864 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxPvdCommStreamEventSink.h" +#include "PxPvdDataStreamHelpers.h" +#include "PxPvdObjectModelInternalTypes.h" +#include "PxPvdImpl.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace physx::pvdsdk; +using namespace physx::shdfnd; + +namespace +{ + +struct ScopedMetaData +{ + PvdOMMetaDataProvider& mProvider; + PvdObjectModelMetaData& mMeta; + ScopedMetaData(PvdOMMetaDataProvider& provider) : mProvider(provider), mMeta(provider.lock()) + { + } + ~ScopedMetaData() + { + mProvider.unlock(); + } + PvdObjectModelMetaData* operator->() + { + return &mMeta; + } + + private: + ScopedMetaData& operator=(const ScopedMetaData&); +}; + +struct PropertyDefinitionHelper : public PvdPropertyDefinitionHelper +{ + PvdDataStream* mStream; + PvdOMMetaDataProvider& mProvider; + Array mNameBuffer; + Array mNameStack; + Array mNamedValues; + Array mPropertyMessageArgs; + + PropertyDefinitionHelper(PvdOMMetaDataProvider& provider) + : mStream(NULL) + , mProvider(provider) + , mNameBuffer("PropertyDefinitionHelper::mNameBuffer") + , mNameStack("PropertyDefinitionHelper::mNameStack") + , mNamedValues("PropertyDefinitionHelper::mNamedValues") + , mPropertyMessageArgs("PropertyDefinitionHelper::mPropertyMessageArgs") + { + } + void setStream(PvdDataStream* stream) + { + mStream = stream; + } + + inline void appendStrToBuffer(const char* str) + { + if(str == NULL) + return; + size_t strLen = strlen(str); + size_t endBufOffset = mNameBuffer.size(); + size_t resizeLen = endBufOffset; + // account for null + if(mNameBuffer.empty()) + resizeLen += 1; + else + endBufOffset -= 1; + + mNameBuffer.resize(static_cast(resizeLen + strLen)); + char* endPtr = mNameBuffer.begin() + endBufOffset; + PxMemCopy(endPtr, str, static_cast(strLen)); + } + + virtual void pushName(const char* nm, const char* appender = ".") + { + size_t nameBufLen = mNameBuffer.size(); + mNameStack.pushBack(static_cast(nameBufLen)); + if(mNameBuffer.empty() == false) + appendStrToBuffer(appender); + appendStrToBuffer(nm); + mNameBuffer.back() = 0; + } + + virtual void pushBracketedName(const char* inName, const char* leftBracket = "[", const char* rightBracket = "]") + { + size_t nameBufLen = mNameBuffer.size(); + mNameStack.pushBack(static_cast(nameBufLen)); + appendStrToBuffer(leftBracket); + appendStrToBuffer(inName); + appendStrToBuffer(rightBracket); + mNameBuffer.back() = 0; + } + + virtual void popName() + { + if(mNameStack.empty()) + return; + mNameBuffer.resize(static_cast(mNameStack.back())); + mNameStack.popBack(); + if(mNameBuffer.empty() == false) + mNameBuffer.back() = 0; + } + + virtual const char* getTopName() + { + if(mNameBuffer.size()) + return mNameBuffer.begin(); + return ""; + } + virtual void clearNameStack() + { + mNameBuffer.clear(); + mNameStack.clear(); + } + + virtual void addNamedValue(const char* name, uint32_t value) + { + mNamedValues.pushBack(NamedValue(name, value)); + } + virtual void clearNamedValues() + { + mNamedValues.clear(); + } + + virtual DataRef getNamedValues() + { + return DataRef(mNamedValues.begin(), mNamedValues.size()); + } + + virtual void createProperty(const NamespacedName& clsName, const char* inSemantic, const NamespacedName& dtypeName, + PropertyType::Enum propType) + { + mStream->createProperty(clsName, getTopName(), inSemantic, dtypeName, propType, getNamedValues()); + clearNamedValues(); + } + const char* registerStr(const char* str) + { + ScopedMetaData scopedProvider(mProvider); + return scopedProvider->getStringTable().registerStr(str); + } + virtual void addPropertyMessageArg(const NamespacedName& inDatatype, uint32_t inOffset, uint32_t inSize) + { + mPropertyMessageArgs.pushBack(PropertyMessageArg(registerStr(getTopName()), inDatatype, inOffset, inSize)); + } + virtual void addPropertyMessage(const NamespacedName& clsName, const NamespacedName& msgName, + uint32_t inStructSizeInBytes) + { + if(mPropertyMessageArgs.empty()) + { + PX_ASSERT(false); + return; + } + mStream->createPropertyMessage( + clsName, msgName, DataRef(mPropertyMessageArgs.begin(), mPropertyMessageArgs.size()), + inStructSizeInBytes); + } + virtual void clearPropertyMessageArgs() + { + mPropertyMessageArgs.clear(); + } + + private: + PropertyDefinitionHelper& operator=(const PropertyDefinitionHelper&); +}; + +class PvdMemPool +{ + // Link List + Array mMemBuffer; + uint32_t mLength; + uint32_t mBufIndex; + + // 4k for one page + static const int BUFFER_LENGTH = 4096; + PX_NOCOPY(PvdMemPool) + public: + PvdMemPool(const char* bufDataName) : mMemBuffer(bufDataName), mLength(0), mBufIndex(0) + { + grow(); + } + + ~PvdMemPool() + { + for(uint32_t i = 0; i < mMemBuffer.size(); i++) + { + PX_FREE(mMemBuffer[i]); + } + } + + void grow() + { + if(mBufIndex + 1 < mMemBuffer.size()) + { + mBufIndex++; + } + else + { + uint8_t* Buf = reinterpret_cast(PX_ALLOC(BUFFER_LENGTH, "PvdMemPool::mMemBuffer.buf")); + mMemBuffer.pushBack(Buf); + mBufIndex = mMemBuffer.size() - 1; + } + mLength = 0; + } + + void* allocate(uint32_t length) + { + if(length > uint32_t(BUFFER_LENGTH)) + return NULL; + + if(length + mLength > uint32_t(BUFFER_LENGTH)) + grow(); + + void* mem = reinterpret_cast(&mMemBuffer[mBufIndex][mLength]); + mLength += length; + return mem; + } + + void clear() + { + mLength = 0; + mBufIndex = 0; + } +}; +struct PvdOutStream : public PvdDataStream, public UserAllocated +{ + HashMap mStringHashMap; + PvdOMMetaDataProvider& mMetaDataProvider; + Array mTempBuffer; + PropertyDefinitionHelper mPropertyDefinitionHelper; + DataStreamState::Enum mStreamState; + + ClassDescription mSPVClass; + PropertyMessageDescription mMessageDesc; + // Set property value and SetPropertyMessage calls require + // us to write the data out to a separate buffer + // when strings are involved. + ForwardingMemoryBuffer mSPVBuffer; + uint32_t mEventCount; + uint32_t mPropertyMessageSize; + bool mConnected; + uint64_t mStreamId; + Array mPvdCommandArray; + PvdMemPool mPvdCommandPool; + PxPvdTransport& mTransport; + + PvdOutStream(PxPvdTransport& transport, PvdOMMetaDataProvider& provider, uint64_t streamId) + : mStringHashMap("PvdOutStream::mStringHashMap") + , mMetaDataProvider(provider) + , mTempBuffer("PvdOutStream::mTempBuffer") + , mPropertyDefinitionHelper(mMetaDataProvider) + , mStreamState(DataStreamState::Open) + , mSPVBuffer("PvdCommStreamBufferedEventSink::mSPVBuffer") + , mEventCount(0) + , mPropertyMessageSize(0) + , mConnected(true) + , mStreamId(streamId) + , mPvdCommandArray("PvdCommStreamBufferedEventSink::mPvdCommandArray") + , mPvdCommandPool("PvdCommStreamBufferedEventSink::mPvdCommandPool") + , mTransport(transport) + { + mPropertyDefinitionHelper.setStream(this); + } + virtual ~PvdOutStream() + { + } + + virtual void release() + { + PVD_DELETE(this); + } + + StringHandle toStream(String nm) + { + if(nm == NULL || *nm == 0) + return 0; + const HashMap::Entry* entry(mStringHashMap.find(nm)); + if(entry) + return entry->second; + ScopedMetaData meta(mMetaDataProvider); + StringHandle hdl = meta->getStringTable().strToHandle(nm); + nm = meta->getStringTable().handleToStr(hdl); + handlePvdEvent(StringHandleEvent(nm, hdl)); + mStringHashMap.insert(nm, hdl); + return hdl; + } + + StreamNamespacedName toStream(const NamespacedName& nm) + { + return StreamNamespacedName(toStream(nm.mNamespace), toStream(nm.mName)); + } + + bool isClassExist(const NamespacedName& nm) + { + ScopedMetaData meta(mMetaDataProvider); + return meta->findClass(nm).hasValue(); + } + + bool createMetaClass(const NamespacedName& nm) + { + ScopedMetaData meta(mMetaDataProvider); + meta->getOrCreateClass(nm); + return true; + } + + bool deriveMetaClass(const NamespacedName& parent, const NamespacedName& child) + { + ScopedMetaData meta(mMetaDataProvider); + return meta->deriveClass(parent, child); + } + +// You will notice that some functions are #pragma'd out throughout this file. +// This is because they are only called from asserts which means they aren't +// called in release. This causes warnings when building using snc which break +// the build. +#if PX_DEBUG + + bool propertyExists(const NamespacedName& nm, String pname) + { + ScopedMetaData meta(mMetaDataProvider); + return meta->findProperty(nm, pname).hasValue(); + } + +#endif + + PvdError boolToError(bool val) + { + if(val) + return PvdErrorType::Success; + return PvdErrorType::NetworkError; + } + + // PvdMetaDataStream + virtual PvdError createClass(const NamespacedName& nm) + { + PX_ASSERT(mStreamState == DataStreamState::Open); +#if PX_DEBUG + PX_ASSERT(isClassExist(nm) == false); +#endif + createMetaClass(nm); + return boolToError(handlePvdEvent(CreateClass(toStream(nm)))); + } + + virtual PvdError deriveClass(const NamespacedName& parent, const NamespacedName& child) + { + PX_ASSERT(mStreamState == DataStreamState::Open); +#if PX_DEBUG + PX_ASSERT(isClassExist(parent)); + PX_ASSERT(isClassExist(child)); +#endif + deriveMetaClass(parent, child); + return boolToError(handlePvdEvent(DeriveClass(toStream(parent), toStream(child)))); + } + + template + TDataType* allocTemp(uint32_t numItems) + { + uint32_t desiredBytes = numItems * sizeof(TDataType); + if(desiredBytes > mTempBuffer.size()) + mTempBuffer.resize(desiredBytes); + TDataType* retval = reinterpret_cast(mTempBuffer.begin()); + if(numItems) + { + PVD_FOREACH(idx, numItems) new (retval + idx) TDataType(); + } + return retval; + } + +#if PX_DEBUG + + // Property datatypes need to be uniform. + // At this point, the data stream cannot handle properties that + // A struct with a float member and a char member would work. + // A struct with a float member and a long member would work (more efficiently). + bool isValidPropertyDatatype(const NamespacedName& dtypeName) + { + ScopedMetaData meta(mMetaDataProvider); + ClassDescription clsDesc(meta->findClass(dtypeName)); + return clsDesc.mRequiresDestruction == false; + } + +#endif + + NamespacedName createMetaProperty(const NamespacedName& clsName, String name, String semantic, + const NamespacedName& dtypeName, PropertyType::Enum propertyType) + { + ScopedMetaData meta(mMetaDataProvider); + int32_t dtypeType = meta->findClass(dtypeName)->mClassId; + NamespacedName typeName = dtypeName; + if(dtypeType == getPvdTypeForType()) + { + dtypeType = getPvdTypeForType(); + typeName = getPvdNamespacedNameForType(); + } + Option propOpt = + meta->createProperty(meta->findClass(clsName)->mClassId, name, semantic, dtypeType, propertyType); + PX_ASSERT(propOpt.hasValue()); + PX_UNUSED(propOpt); + return typeName; + } + + virtual PvdError createProperty(const NamespacedName& clsName, String name, String semantic, + const NamespacedName& incomingDtypeName, PropertyType::Enum propertyType, + DataRef values) + { + PX_ASSERT(mStreamState == DataStreamState::Open); +#if PX_DEBUG + PX_ASSERT(isClassExist(clsName)); + PX_ASSERT(propertyExists(clsName, name) == false); +#endif + NamespacedName dtypeName(incomingDtypeName); + if(safeStrEq(dtypeName.mName, "VoidPtr")) + dtypeName.mName = "ObjectRef"; +#if PX_DEBUG + PX_ASSERT(isClassExist(dtypeName)); + PX_ASSERT(isValidPropertyDatatype(dtypeName)); +#endif + NamespacedName typeName = createMetaProperty(clsName, name, semantic, dtypeName, propertyType); + // Can't have arrays of strings or arrays of string handles due to the difficulty + // of quickly dealing with them on the network receiving side. + if(propertyType == PropertyType::Array && safeStrEq(typeName.mName, "StringHandle")) + { + PX_ASSERT(false); + return PvdErrorType::ArgumentError; + } + uint32_t numItems = values.size(); + NameHandleValue* streamValues = allocTemp(numItems); + PVD_FOREACH(idx, numItems) + streamValues[idx] = NameHandleValue(toStream(values[idx].mName), values[idx].mValue); + CreateProperty evt(toStream(clsName), toStream(name), toStream(semantic), toStream(typeName), propertyType, + DataRef(streamValues, numItems)); + return boolToError(handlePvdEvent(evt)); + } + + bool createMetaPropertyMessage(const NamespacedName& cls, const NamespacedName& msgName, + DataRef entries, uint32_t messageSizeInBytes) + { + ScopedMetaData meta(mMetaDataProvider); + return meta->createPropertyMessage(cls, msgName, entries, messageSizeInBytes).hasValue(); + } +#if PX_DEBUG + + bool messageExists(const NamespacedName& msgName) + { + ScopedMetaData meta(mMetaDataProvider); + return meta->findPropertyMessage(msgName).hasValue(); + } + +#endif + + virtual PvdError createPropertyMessage(const NamespacedName& cls, const NamespacedName& msgName, + DataRef entries, uint32_t messageSizeInBytes) + { + PX_ASSERT(mStreamState == DataStreamState::Open); +#if PX_DEBUG + PX_ASSERT(isClassExist(cls)); + PX_ASSERT(messageExists(msgName) == false); +#endif + createMetaPropertyMessage(cls, msgName, entries, messageSizeInBytes); + uint32_t numItems = entries.size(); + StreamPropMessageArg* streamValues = allocTemp(numItems); + PVD_FOREACH(idx, numItems) + streamValues[idx] = + StreamPropMessageArg(toStream(entries[idx].mPropertyName), toStream(entries[idx].mDatatypeName), + entries[idx].mMessageOffset, entries[idx].mByteSize); + CreatePropertyMessage evt(toStream(cls), toStream(msgName), + DataRef(streamValues, numItems), messageSizeInBytes); + return boolToError(handlePvdEvent(evt)); + } + + uint64_t toStream(const void* instance) + { + return PVD_POINTER_TO_U64(instance); + } + virtual PvdError createInstance(const NamespacedName& cls, const void* instance) + { + PX_ASSERT(isInstanceValid(instance) == false); + PX_ASSERT(mStreamState == DataStreamState::Open); + bool success = mMetaDataProvider.createInstance(cls, instance); + PX_ASSERT(success); + (void)success; + return boolToError(handlePvdEvent(CreateInstance(toStream(cls), toStream(instance)))); + } + + virtual bool isInstanceValid(const void* instance) + { + return mMetaDataProvider.isInstanceValid(instance); + } + +#if PX_DEBUG + + // If the property will fit or is already completely in memory + bool checkPropertyType(const void* instance, String name, const NamespacedName& incomingType) + { + int32_t instType = mMetaDataProvider.getInstanceClassType(instance); + ScopedMetaData meta(mMetaDataProvider); + Option prop = meta->findProperty(instType, name); + if(prop.hasValue() == false) + return false; + int32_t propType = prop->mDatatype; + int32_t incomingTypeId = meta->findClass(incomingType)->mClassId; + if(incomingTypeId != getPvdTypeForType()) + { + MarshalQueryResult result = meta->checkMarshalling(incomingTypeId, propType); + bool possible = result.needsMarshalling == false || result.canMarshal; + return possible; + } + else + { + if(propType != getPvdTypeForType()) + return false; + } + return true; + } + +#endif + + DataRef bufferPropertyValue(ClassDescriptionSizeInfo info, DataRef data) + { + uint32_t realSize = info.mByteSize; + uint32_t numItems = data.size() / realSize; + if(info.mPtrOffsets.size() != 0) + { + mSPVBuffer.clear(); + PVD_FOREACH(item, numItems) + { + const uint8_t* itemPtr = data.begin() + item * realSize; + mSPVBuffer.write(itemPtr, realSize); + PVD_FOREACH(stringIdx, info.mPtrOffsets.size()) + { + PtrOffset offset(info.mPtrOffsets[stringIdx]); + if(offset.mOffsetType == PtrOffsetType::VoidPtrOffset) + continue; + const char* strPtr; + physx::intrinsics::memCopy(&strPtr, itemPtr + offset.mOffset, sizeof(char*)); + strPtr = nonNull(strPtr); + uint32_t len = safeStrLen(strPtr) + 1; + mSPVBuffer.write(strPtr, len); + } + } + data = DataRef(mSPVBuffer.begin(), mSPVBuffer.size()); + } + return data; + } + + virtual PvdError setPropertyValue(const void* instance, String name, DataRef data, + const NamespacedName& incomingTypeName) + { + + PX_ASSERT(isInstanceValid(instance)); +#if PX_DEBUG + PX_ASSERT(isClassExist(incomingTypeName)); +#endif + PX_ASSERT(mStreamState == DataStreamState::Open); + ClassDescription clsDesc; + { + ScopedMetaData meta(mMetaDataProvider); + clsDesc = meta->findClass(incomingTypeName); + } + uint32_t realSize = clsDesc.getNativeSize(); + uint32_t numItems = data.size() / realSize; + data = bufferPropertyValue(clsDesc.getNativeSizeInfo(), data); + SetPropertyValue evt(toStream(instance), toStream(name), data, toStream(incomingTypeName), numItems); + return boolToError(handlePvdEvent(evt)); + } + + // Else if the property is very large (contact reports) you can send it in chunks. + virtual PvdError beginSetPropertyValue(const void* instance, String name, const NamespacedName& incomingTypeName) + { + PX_ASSERT(isInstanceValid(instance)); +#if PX_DEBUG + PX_ASSERT(isClassExist(incomingTypeName)); + PX_ASSERT(checkPropertyType(instance, name, incomingTypeName)); +#endif + PX_ASSERT(mStreamState == DataStreamState::Open); + mStreamState = DataStreamState::SetPropertyValue; + { + ScopedMetaData meta(mMetaDataProvider); + mSPVClass = meta->findClass(incomingTypeName); + } + BeginSetPropertyValue evt(toStream(instance), toStream(name), toStream(incomingTypeName)); + return boolToError(handlePvdEvent(evt)); + } + + virtual PvdError appendPropertyValueData(DataRef data) + { + uint32_t realSize = mSPVClass.getNativeSize(); + uint32_t numItems = data.size() / realSize; + data = bufferPropertyValue(mSPVClass.getNativeSizeInfo(), data); + PX_ASSERT(mStreamState == DataStreamState::SetPropertyValue); + return boolToError(handlePvdEvent(AppendPropertyValueData(data, numItems))); + } + virtual PvdError endSetPropertyValue() + { + PX_ASSERT(mStreamState == DataStreamState::SetPropertyValue); + mStreamState = DataStreamState::Open; + return boolToError(handlePvdEvent(EndSetPropertyValue())); + } + +#if PX_DEBUG + + bool checkPropertyMessage(const void* instance, const NamespacedName& msgName) + { + int32_t clsId = mMetaDataProvider.getInstanceClassType(instance); + ScopedMetaData meta(mMetaDataProvider); + PropertyMessageDescription desc(meta->findPropertyMessage(msgName)); + bool retval = meta->isDerivedFrom(clsId, desc.mClassId); + return retval; + } + +#endif + + DataRef bufferPropertyMessage(const PropertyMessageDescription& desc, DataRef data) + { + if(desc.mStringOffsets.size()) + { + mSPVBuffer.clear(); + mSPVBuffer.write(data.begin(), data.size()); + PVD_FOREACH(idx, desc.mStringOffsets.size()) + { + const char* strPtr; + physx::intrinsics::memCopy(&strPtr, data.begin() + desc.mStringOffsets[idx], sizeof(char*)); + strPtr = nonNull(strPtr); + uint32_t len = safeStrLen(strPtr) + 1; + mSPVBuffer.write(strPtr, len); + } + data = DataRef(mSPVBuffer.begin(), mSPVBuffer.end()); + } + return data; + } + + virtual PvdError setPropertyMessage(const void* instance, const NamespacedName& msgName, DataRef data) + { + ScopedMetaData meta(mMetaDataProvider); + PX_ASSERT(isInstanceValid(instance)); +#if PX_DEBUG + PX_ASSERT(messageExists(msgName)); + PX_ASSERT(checkPropertyMessage(instance, msgName)); +#endif + PropertyMessageDescription desc(meta->findPropertyMessage(msgName)); + if(data.size() < desc.mMessageByteSize) + { + PX_ASSERT(false); + return PvdErrorType::ArgumentError; + } + data = bufferPropertyMessage(desc, data); + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent(SetPropertyMessage(toStream(instance), toStream(msgName), data))); + } + +#if PX_DEBUG + + bool checkBeginPropertyMessageGroup(const NamespacedName& msgName) + { + ScopedMetaData meta(mMetaDataProvider); + PropertyMessageDescription desc(meta->findPropertyMessage(msgName)); + return desc.mStringOffsets.size() == 0; + } + +#endif + // If you need to send of lot of identical messages, this avoids a hashtable lookup per message. + virtual PvdError beginPropertyMessageGroup(const NamespacedName& msgName) + { +#if PX_DEBUG + PX_ASSERT(messageExists(msgName)); + PX_ASSERT(checkBeginPropertyMessageGroup(msgName)); +#endif + PX_ASSERT(mStreamState == DataStreamState::Open); + mStreamState = DataStreamState::PropertyMessageGroup; + ScopedMetaData meta(mMetaDataProvider); + mMessageDesc = meta->findPropertyMessage(msgName); + return boolToError(handlePvdEvent(BeginPropertyMessageGroup(toStream(msgName)))); + } + + virtual PvdError sendPropertyMessageFromGroup(const void* instance, DataRef data) + { + PX_ASSERT(mStreamState == DataStreamState::PropertyMessageGroup); + PX_ASSERT(isInstanceValid(instance)); +#if PX_DEBUG + PX_ASSERT(checkPropertyMessage(instance, mMessageDesc.mMessageName)); +#endif + if(mMessageDesc.mMessageByteSize != data.size()) + { + PX_ASSERT(false); + return PvdErrorType::ArgumentError; + } + if(data.size() < mMessageDesc.mMessageByteSize) + return PvdErrorType::ArgumentError; + data = bufferPropertyMessage(mMessageDesc, data); + return boolToError(handlePvdEvent(SendPropertyMessageFromGroup(toStream(instance), data))); + } + virtual PvdError endPropertyMessageGroup() + { + PX_ASSERT(mStreamState == DataStreamState::PropertyMessageGroup); + mStreamState = DataStreamState::Open; + return boolToError(handlePvdEvent(EndPropertyMessageGroup())); + } + virtual PvdError pushBackObjectRef(const void* instance, String propName, const void* data) + { + PX_ASSERT(isInstanceValid(instance)); + PX_ASSERT(isInstanceValid(data)); + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent(PushBackObjectRef(toStream(instance), toStream(propName), toStream(data)))); + } + virtual PvdError removeObjectRef(const void* instance, String propName, const void* data) + { + PX_ASSERT(isInstanceValid(instance)); + PX_ASSERT(isInstanceValid(data)); + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent(RemoveObjectRef(toStream(instance), toStream(propName), toStream(data)))); + } + // Instance elimination. + virtual PvdError destroyInstance(const void* instance) + { + PX_ASSERT(isInstanceValid(instance)); + PX_ASSERT(mStreamState == DataStreamState::Open); + mMetaDataProvider.destroyInstance(instance); + return boolToError(handlePvdEvent(DestroyInstance(toStream(instance)))); + } + + // Profiling hooks + virtual PvdError beginSection(const void* instance, String name) + { + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent( + BeginSection(toStream(instance), toStream(name), Time::getCurrentCounterValue()))); + } + + virtual PvdError endSection(const void* instance, String name) + { + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent( + EndSection(toStream(instance), toStream(name), Time::getCurrentCounterValue()))); + } + + virtual PvdError originShift(const void* scene, PxVec3 shift) + { + PX_ASSERT(mStreamState == DataStreamState::Open); + return boolToError(handlePvdEvent(OriginShift(toStream(scene), shift))); + } + + virtual void addProfileZone(void* zone, const char* name) + { + handlePvdEvent(AddProfileZone(toStream(zone), name)); + } + virtual void addProfileZoneEvent(void* zone, const char* name, uint16_t eventId, bool compileTimeEnabled) + { + handlePvdEvent(AddProfileZoneEvent(toStream(zone), name, eventId, compileTimeEnabled)); + } + + // add a variable sized event + void addEvent(const EventSerializeable& evt, PvdCommStreamEventTypes::Enum evtType) + { + MeasureStream measure; + PvdCommStreamEventSink::writeStreamEvent(evt, evtType, measure); + EventGroup evtGroup(measure.mSize, 1, mStreamId, Time::getCurrentCounterValue()); + EventStreamifier streamifier(mTransport.lock()); + evtGroup.serialize(streamifier); + PvdCommStreamEventSink::writeStreamEvent(evt, evtType, mTransport); + mTransport.unlock(); + } + + void setIsTopLevelUIElement(const void* instance, bool topLevel) + { + addEvent(SetIsTopLevel(static_cast(reinterpret_cast(instance)), topLevel), + getCommStreamEventType()); + } + + void sendErrorMessage(uint32_t code, const char* message, const char* file, uint32_t line) + { + addEvent(ErrorMessage(code, message, file, line), getCommStreamEventType()); + } + + void updateCamera(const char* name, const PxVec3& origin, const PxVec3& up, const PxVec3& target) + { + addEvent(SetCamera(name, origin, up, target), getCommStreamEventType()); + } + + template + bool handlePvdEvent(const TEventType& evt) + { + addEvent(evt, getCommStreamEventType()); + return mConnected; + } + + virtual PvdPropertyDefinitionHelper& getPropertyDefinitionHelper() + { + mPropertyDefinitionHelper.clearBufferedData(); + return mPropertyDefinitionHelper; + } + + virtual bool isConnected() + { + return mConnected; + } + + virtual void* allocateMemForCmd(uint32_t length) + { + return mPvdCommandPool.allocate(length); + } + + virtual void pushPvdCommand(PvdCommand& cmd) + { + mPvdCommandArray.pushBack(&cmd); + } + + virtual void flushPvdCommand() + { + uint32_t cmdQueueSize = mPvdCommandArray.size(); + for(uint32_t i = 0; i < cmdQueueSize; i++) + { + if(mPvdCommandArray[i]) + { + // if(mPvdCommandArray[i]->canRun(*this)) + mPvdCommandArray[i]->run(*this); + mPvdCommandArray[i]->~PvdCommand(); + } + } + mPvdCommandArray.clear(); + mPvdCommandPool.clear(); + } + + PX_NOCOPY(PvdOutStream) +}; +} + +PvdDataStream* PvdDataStream::create(PxPvd* pvd) +{ + if(pvd == NULL) + { + getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PvdDataStream::create - pvd must be non-NULL!"); + return NULL; + } + + PvdImpl* pvdImpl = static_cast(pvd); + return PVD_NEW(PvdOutStream)(*pvdImpl->getTransport(), pvdImpl->getMetaDataProvider(), pvdImpl->getNextStreamId()); +} diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp new file mode 100644 index 000000000..ba939b045 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp @@ -0,0 +1,218 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdDefaultFileTransport.h" + +namespace physx +{ +namespace pvdsdk +{ + +PvdDefaultFileTransport::PvdDefaultFileTransport(const char* name) : mConnected(false), mWrittenData(0), mLocked(false) +{ + mFileBuffer = PX_NEW(PsFileBuffer)(name, PxFileBuf::OPEN_WRITE_ONLY); +} + +PvdDefaultFileTransport::~PvdDefaultFileTransport() +{ +} + +bool PvdDefaultFileTransport::connect() +{ + PX_ASSERT(mFileBuffer); + mConnected = mFileBuffer->isOpen(); + return mConnected; +} + +void PvdDefaultFileTransport::disconnect() +{ + mConnected = false; +} + +bool PvdDefaultFileTransport::isConnected() +{ + return mConnected; +} + +bool PvdDefaultFileTransport::write(const uint8_t* inBytes, uint32_t inLength) +{ + PX_ASSERT(mLocked); + PX_ASSERT(mFileBuffer); + if (mConnected) + { + uint32_t len = mFileBuffer->write(inBytes, inLength); + mWrittenData += len; + return len == inLength; + } + else + return false; +} + +PxPvdTransport& PvdDefaultFileTransport::lock() +{ + mMutex.lock(); + PX_ASSERT(!mLocked); + mLocked = true; + return *this; +} + +void PvdDefaultFileTransport::unlock() +{ + PX_ASSERT(mLocked); + mLocked = false; + mMutex.unlock(); +} + +void PvdDefaultFileTransport::flush() +{ +} + +uint64_t PvdDefaultFileTransport::getWrittenDataSize() +{ + return mWrittenData; +} + +void PvdDefaultFileTransport::release() +{ + if (mFileBuffer) + { + mFileBuffer->close(); + delete mFileBuffer; + } + mFileBuffer = NULL; + PX_DELETE(this); +} + +class NullFileTransport : public physx::PxPvdTransport, public physx::shdfnd::UserAllocated +{ + PX_NOCOPY(NullFileTransport) + public: + NullFileTransport(); + virtual ~NullFileTransport(); + + virtual bool connect(); + virtual void disconnect(); + virtual bool isConnected(); + + virtual bool write(const uint8_t* inBytes, uint32_t inLength); + + virtual PxPvdTransport& lock(); + virtual void unlock(); + + virtual void flush(); + + virtual uint64_t getWrittenDataSize(); + + virtual void release(); + + private: + bool mConnected; + uint64_t mWrittenData; + physx::shdfnd::Mutex mMutex; + bool mLocked; // for debug, remove it when finished +}; + +NullFileTransport::NullFileTransport() : mConnected(false), mWrittenData(0), mLocked(false) +{ +} + +NullFileTransport::~NullFileTransport() +{ +} + +bool NullFileTransport::connect() +{ + mConnected = true; + return true; +} + +void NullFileTransport::disconnect() +{ + mConnected = false; +} + +bool NullFileTransport::isConnected() +{ + return mConnected; +} + +bool NullFileTransport::write(const uint8_t* /*inBytes*/, uint32_t inLength) +{ + PX_ASSERT(mLocked); + if(mConnected) + { + uint32_t len = inLength; + mWrittenData += len; + return len == inLength; + } + else + return false; +} + +PxPvdTransport& NullFileTransport::lock() +{ + mMutex.lock(); + PX_ASSERT(!mLocked); + mLocked = true; + return *this; +} + +void NullFileTransport::unlock() +{ + PX_ASSERT(mLocked); + mLocked = false; + mMutex.unlock(); +} + +void NullFileTransport::flush() +{ +} + +uint64_t NullFileTransport::getWrittenDataSize() +{ + return mWrittenData; +} + +void NullFileTransport::release() +{ + PX_DELETE(this); +} + +} // namespace pvdsdk + +PxPvdTransport* PxDefaultPvdFileTransportCreate(const char* name) +{ + if(name) + return PX_NEW(pvdsdk::PvdDefaultFileTransport)(name); + else + return PX_NEW(pvdsdk::NullFileTransport)(); +} + +} // namespace physx + diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h new file mode 100644 index 000000000..e64b4a0a7 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDDEFAULTFILETRANSPORT_H +#define PXPVDSDK_PXPVDDEFAULTFILETRANSPORT_H + +#include "pvd/PxPvdTransport.h" + +#include "PsUserAllocated.h" +#include "PsFileBuffer.h" +#include "PsMutex.h" + +namespace physx +{ +namespace pvdsdk +{ + +class PvdDefaultFileTransport : public physx::PxPvdTransport, public physx::shdfnd::UserAllocated +{ + PX_NOCOPY(PvdDefaultFileTransport) + public: + PvdDefaultFileTransport(const char* name); + virtual ~PvdDefaultFileTransport(); + + virtual bool connect(); + virtual void disconnect(); + virtual bool isConnected(); + + virtual bool write(const uint8_t* inBytes, uint32_t inLength); + + virtual PxPvdTransport& lock(); + virtual void unlock(); + + virtual void flush(); + + virtual uint64_t getWrittenDataSize(); + + virtual void release(); + + private: + physx::PsFileBuffer* mFileBuffer; + bool mConnected; + uint64_t mWrittenData; + physx::shdfnd::Mutex mMutex; + bool mLocked; // for debug, remove it when finished +}; + +} // pvdsdk +} // physx + +#endif // PXPVDSDK_PXPVDDEFAULTFILETRANSPORT_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp new file mode 100644 index 000000000..3ed5d03d7 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp @@ -0,0 +1,134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdDefaultSocketTransport.h" + +namespace physx +{ +namespace pvdsdk +{ +PvdDefaultSocketTransport::PvdDefaultSocketTransport(const char* host, int port, unsigned int timeoutInMilliseconds) +: mHost(host), mPort(uint16_t(port)), mTimeout(timeoutInMilliseconds), mConnected(false), mWrittenData(0) +{ +} + +PvdDefaultSocketTransport::~PvdDefaultSocketTransport() +{ +} + +bool PvdDefaultSocketTransport::connect() +{ + if(mConnected) + return true; + + if(mSocket.connect(mHost, mPort, mTimeout)) + { + mSocket.setBlocking(true); + mConnected = true; + } + return mConnected; +} + +void PvdDefaultSocketTransport::disconnect() +{ + mSocket.flush(); + mSocket.disconnect(); + mConnected = false; +} + +bool PvdDefaultSocketTransport::isConnected() +{ + return mSocket.isConnected(); +} + +bool PvdDefaultSocketTransport::write(const uint8_t* inBytes, uint32_t inLength) +{ + if(mConnected) + { + if(inLength == 0) + return true; + + uint32_t amountWritten = 0; + uint32_t totalWritten = 0; + do + { + // Sockets don't have to write as much as requested, so we need + // to wrap this call in a do/while loop. + // If they don't write any bytes then we consider them disconnected. + amountWritten = mSocket.write(inBytes, inLength); + inLength -= amountWritten; + inBytes += amountWritten; + totalWritten += amountWritten; + } while(inLength && amountWritten); + + if(amountWritten == 0) + return false; + + mWrittenData += totalWritten; + + return true; + } + else + return false; +} + +PxPvdTransport& PvdDefaultSocketTransport::lock() +{ + mMutex.lock(); + return *this; +} + +void PvdDefaultSocketTransport::unlock() +{ + mMutex.unlock(); +} + +void PvdDefaultSocketTransport::flush() +{ + mSocket.flush(); +} + +uint64_t PvdDefaultSocketTransport::getWrittenDataSize() +{ + return mWrittenData; +} + +void PvdDefaultSocketTransport::release() +{ + PX_DELETE(this); +} + +} // namespace pvdsdk + +PxPvdTransport* PxDefaultPvdSocketTransportCreate(const char* host, int port, unsigned int timeoutInMilliseconds) +{ + return PX_NEW(pvdsdk::PvdDefaultSocketTransport)(host, port, timeoutInMilliseconds); +} + +} // namespace physx diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h new file mode 100644 index 000000000..d0d7d4b2a --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h @@ -0,0 +1,79 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDDEFAULTSOCKETTRANSPORT_H +#define PXPVDSDK_PXPVDDEFAULTSOCKETTRANSPORT_H + +#include "pvd/PxPvdTransport.h" + +#include "PsUserAllocated.h" +#include "PsSocket.h" +#include "PsMutex.h" + +namespace physx +{ +namespace pvdsdk +{ +class PvdDefaultSocketTransport : public PxPvdTransport, public shdfnd::UserAllocated +{ + PX_NOCOPY(PvdDefaultSocketTransport) + public: + PvdDefaultSocketTransport(const char* host, int port, unsigned int timeoutInMilliseconds); + virtual ~PvdDefaultSocketTransport(); + + virtual bool connect(); + virtual void disconnect(); + virtual bool isConnected(); + + virtual bool write(const uint8_t* inBytes, uint32_t inLength); + + virtual void flush(); + + virtual PxPvdTransport& lock(); + virtual void unlock(); + + virtual uint64_t getWrittenDataSize(); + + virtual void release(); + + private: + shdfnd::Socket mSocket; + const char* mHost; + uint16_t mPort; + unsigned int mTimeout; + bool mConnected; + uint64_t mWrittenData; + shdfnd::Mutex mMutex; + bool mlocked; +}; + +} // pvdsdk +} // physx + +#endif // PXPVDSDK_PXPVDDEFAULTSOCKETTRANSPORT_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdFoundation.h b/src/PhysX/physx/source/pvd/src/PxPvdFoundation.h new file mode 100644 index 000000000..52feeee57 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdFoundation.h @@ -0,0 +1,318 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDFOUNDATION_H +#define PXPVDSDK_PXPVDFOUNDATION_H + +#include "foundation/PxVec3.h" +#include "foundation/PxTransform.h" +#include "foundation/PxBounds3.h" + +#include "PsArray.h" +#include "PsHashMap.h" +#include "PsHashSet.h" +#include "PsPool.h" +#include "PsString.h" + +#include "PxPvdObjectModelBaseTypes.h" + +namespace physx +{ +namespace pvdsdk +{ + +extern PxAllocatorCallback* gPvdAllocatorCallback; + +class ForwardingAllocator : public PxAllocatorCallback +{ + void* allocate(size_t size, const char* typeName, const char* filename, int line) + { + return shdfnd::getAllocator().allocate(size, typeName, filename, line); + } + void deallocate(void* ptr) + { + shdfnd::getAllocator().deallocate(ptr); + } +}; + +class RawMemoryBuffer +{ + uint8_t* mBegin; + uint8_t* mEnd; + uint8_t* mCapacityEnd; + const char* mBufDataName; + + public: + RawMemoryBuffer(const char* name) : mBegin(0), mEnd(0), mCapacityEnd(0),mBufDataName(name) + { + PX_UNUSED(mBufDataName); + } + ~RawMemoryBuffer() + { + if(mBegin) + PX_FREE(mBegin); + } + uint32_t size() const + { + return static_cast(mEnd - mBegin); + } + uint32_t capacity() const + { + return static_cast(mCapacityEnd - mBegin); + } + uint8_t* begin() + { + return mBegin; + } + uint8_t* end() + { + return mEnd; + } + const uint8_t* begin() const + { + return mBegin; + } + const uint8_t* end() const + { + return mEnd; + } + void clear() + { + mEnd = mBegin; + } + const char* cStr() + { + if(mEnd && (*mEnd != 0)) + write(0); + return reinterpret_cast(mBegin); + } + uint32_t write(uint8_t inValue) + { + *growBuf(1) = inValue; + return 1; + } + + template + uint32_t write(const TDataType& inValue) + { + const uint8_t* __restrict readPtr = reinterpret_cast(&inValue); + uint8_t* __restrict writePtr = growBuf(sizeof(TDataType)); + for(uint32_t idx = 0; idx < sizeof(TDataType); ++idx) + writePtr[idx] = readPtr[idx]; + return sizeof(TDataType); + } + + template + uint32_t write(const TDataType* inValue, uint32_t inLength) + { + uint32_t writeSize = inLength * sizeof(TDataType); + if(inValue && inLength) + { + physx::intrinsics::memCopy(growBuf(writeSize), inValue, writeSize); + } + if(inLength && !inValue) + { + PX_ASSERT(false); + // You can't not write something, because that will cause + // the receiving end to crash. + for(uint32_t idx = 0; idx < writeSize; ++idx) + write(0); + } + return writeSize; + } + + uint8_t* growBuf(uint32_t inAmount) + { + uint32_t offset = size(); + uint32_t newSize = offset + inAmount; + reserve(newSize); + mEnd += inAmount; + return mBegin + offset; + } + void writeZeros(uint32_t inAmount) + { + uint32_t offset = size(); + growBuf(inAmount); + physx::intrinsics::memZero(begin() + offset, inAmount); + } + void reserve(uint32_t newSize) + { + uint32_t currentSize = size(); + if(newSize && newSize >= capacity()) + { + uint32_t newDataSize = newSize > 4096 ? newSize + (newSize >> 2) : newSize*2; + uint8_t* newData = static_cast(PX_ALLOC(newDataSize, mBufDataName)); + if(mBegin) + { + physx::intrinsics::memCopy(newData, mBegin, currentSize); + PX_FREE(mBegin); + } + mBegin = newData; + mEnd = mBegin + currentSize; + mCapacityEnd = mBegin + newDataSize; + } + } +}; + +struct ForwardingMemoryBuffer : public RawMemoryBuffer +{ + ForwardingMemoryBuffer(const char* bufDataName) : RawMemoryBuffer(bufDataName) + { + } + + ForwardingMemoryBuffer& operator<<(const char* inString) + { + if(inString && *inString) + { + uint32_t len = static_cast(strlen(inString)); + write(inString, len); + } + return *this; + } + + template + inline ForwardingMemoryBuffer& toStream(const char* inFormat, const TDataType inData) + { + char buffer[128] = { 0 }; + shdfnd::snprintf(buffer, 128, inFormat, inData); + *this << buffer; + return *this; + } + + inline ForwardingMemoryBuffer& operator<<(bool inData) + { + *this << (inData ? "true" : "false"); + return *this; + } + inline ForwardingMemoryBuffer& operator<<(int32_t inData) + { + return toStream("%d", inData); + } + inline ForwardingMemoryBuffer& operator<<(uint16_t inData) + { + return toStream("%u", uint32_t(inData)); + } + inline ForwardingMemoryBuffer& operator<<(uint8_t inData) + { + return toStream("%u", uint32_t(inData)); + } + inline ForwardingMemoryBuffer& operator<<(char inData) + { + return toStream("%c", inData); + } + inline ForwardingMemoryBuffer& operator<<(uint32_t inData) + { + return toStream("%u", inData); + } + inline ForwardingMemoryBuffer& operator<<(uint64_t inData) + { + return toStream("%I64u", inData); + } + inline ForwardingMemoryBuffer& operator<<(int64_t inData) + { + return toStream("%I64d", inData); + } + inline ForwardingMemoryBuffer& operator<<(const void* inData) + { + return *this << static_cast(reinterpret_cast(inData)); + } + inline ForwardingMemoryBuffer& operator<<(float inData) + { + return toStream("%g", double(inData)); + } + inline ForwardingMemoryBuffer& operator<<(double inData) + { + return toStream("%g", inData); + } + inline ForwardingMemoryBuffer& operator<<(const PxVec3& inData) + { + *this << inData[0]; + *this << " "; + *this << inData[1]; + *this << " "; + *this << inData[2]; + return *this; + } + + inline ForwardingMemoryBuffer& operator<<(const PxQuat& inData) + { + *this << inData.x; + *this << " "; + *this << inData.y; + *this << " "; + *this << inData.z; + *this << " "; + *this << inData.w; + return *this; + } + + inline ForwardingMemoryBuffer& operator<<(const PxTransform& inData) + { + *this << inData.q; + *this << " "; + *this << inData.p; + return *this; + } + + inline ForwardingMemoryBuffer& operator<<(const PxBounds3& inData) + { + *this << inData.minimum; + *this << " "; + *this << inData.maximum; + return *this; + } + +}; + +template +inline void* PvdAllocate(const char* typeName, const char* file, int line) +{ + PX_ASSERT(gPvdAllocatorCallback); + return gPvdAllocatorCallback->allocate(sizeof(TDataType), typeName, file, line); +} + +template +inline void PvdDeleteAndDeallocate(TDataType* inDType) +{ + PX_ASSERT(gPvdAllocatorCallback); + if(inDType) + { + inDType->~TDataType(); + gPvdAllocatorCallback->deallocate(inDType); + } +} +} +} + +#define PVD_NEW(dtype) new (PvdAllocate(#dtype, __FILE__, __LINE__)) dtype +#define PVD_DELETE(obj) PvdDeleteAndDeallocate(obj); +//#define PVD_NEW(dtype) PX_NEW(dtype) +//#define PVD_DELETE(obj) PX_DELETE(obj) +#define PVD_FOREACH(varname, stop) for(uint32_t varname = 0; varname < stop; ++varname) +#define PVD_POINTER_TO_U64(ptr) static_cast(reinterpret_cast(ptr)) + +#endif // PXPVDSDK_PXPVDFOUNDATION_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp b/src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp new file mode 100644 index 000000000..d6374584e --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp @@ -0,0 +1,399 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdImpl.h" +#include "PxPvdMemClient.h" +#include "PxPvdProfileZoneClient.h" +#include "PxPvdProfileZone.h" + +#include "PsFoundation.h" + +#if PX_NVTX +#include "nvToolsExt.h" +#endif + +namespace +{ + const char* gSdkName = "PhysXSDK"; +} + +namespace physx +{ +namespace pvdsdk +{ + +class CmEventNameProvider : public physx::profile::PxProfileNameProvider +{ +public: + physx::profile::PxProfileNames getProfileNames() const + { + physx::profile::PxProfileNames ret; + ret.eventCount = 0; + return ret; + } +}; + +CmEventNameProvider gProfileNameProvider; + +void initializeModelTypes(PvdDataStream& stream) +{ + stream.createClass(); + stream.createProperty( + "events", PvdCommStreamEmbeddedTypes::getProfileEventStreamSemantic(), PropertyType::Array); + + stream.createClass(); + stream.createProperty( + "events", PvdCommStreamEmbeddedTypes::getMemoryEventStreamSemantic(), PropertyType::Array); + + stream.createClass(); + stream.createProperty( + "events", PvdCommStreamEmbeddedTypes::getRendererEventStreamSemantic(), PropertyType::Array); +} + +PvdImpl* PvdImpl::sInstance = NULL; +uint32_t PvdImpl::sRefCount = 0; + +PvdImpl::PvdImpl() +: mPvdTransport(NULL) +, mSharedMetaProvider(NULL) +, mMemClient(NULL) +, mIsConnected(false) +, mIsNVTXSupportEnabled(true) +, mNVTXContext(0) +, mNextStreamId(1) +, mProfileClient(NULL) +, mProfileZone(NULL) +{ + mProfileZoneManager = &physx::profile::PxProfileZoneManager::createProfileZoneManager(&physx::shdfnd::getAllocator()); + mProfileClient = PVD_NEW(PvdProfileZoneClient)(*this); +} + +PvdImpl::~PvdImpl() +{ + if((mFlags & PxPvdInstrumentationFlag::ePROFILE) ) + { + PxSetProfilerCallback(NULL); + } + + disconnect(); + + if ( mProfileZoneManager ) + { + mProfileZoneManager->release(); + mProfileZoneManager = NULL; + } + + PVD_DELETE(mProfileClient); + mProfileClient = NULL; +} + +bool PvdImpl::connect(PxPvdTransport& transport, PxPvdInstrumentationFlags flags) +{ + if(mIsConnected) + { + physx::shdfnd::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxPvd::connect - recall connect! Should call disconnect before re-connect."); + return false; + } + + mFlags = flags; + mPvdTransport = &transport; + + mIsConnected = mPvdTransport->connect(); + + if(mIsConnected) + { + mSharedMetaProvider = PVD_NEW(MetaDataProvider); + sendTransportInitialization(); + + PvdDataStream* stream = PvdDataStream::create(this); + initializeModelTypes(*stream); + stream->release(); + + if(mFlags & PxPvdInstrumentationFlag::eMEMORY) + { + mMemClient = PVD_NEW(PvdMemClient)(*this); + mPvdClients.pushBack(mMemClient); + } + + if((mFlags & PxPvdInstrumentationFlag::ePROFILE) && mProfileZoneManager) + { + mPvdClients.pushBack(mProfileClient); + mProfileZone = &physx::profile::PxProfileZone::createProfileZone(&physx::shdfnd::getAllocator(),gSdkName,gProfileNameProvider.getProfileNames()); + } + + for(uint32_t i = 0; i < mPvdClients.size(); i++) + mPvdClients[i]->onPvdConnected(); + + if (mProfileZone) + { + mProfileZoneManager->addProfileZoneHandler(*mProfileClient); + mProfileZoneManager->addProfileZone( *mProfileZone ); + } + + if ((mFlags & PxPvdInstrumentationFlag::ePROFILE)) + { + PxSetProfilerCallback(this); + } + } + return mIsConnected; +} + +void PvdImpl::disconnect() +{ + if(mProfileZone) + { + mProfileZoneManager->removeProfileZoneHandler(*mProfileClient); + mProfileZoneManager->removeProfileZone( *mProfileZone ); + mProfileZone->release(); + mProfileZone=NULL; + removeClient(mProfileClient); + } + + if(mIsConnected) + { + for(uint32_t i = 0; i < mPvdClients.size(); i++) + mPvdClients[i]->onPvdDisconnected(); + + if(mMemClient) + { + removeClient(mMemClient); + PvdMemClient* tmp = mMemClient; //avoid tracking deallocation itsself + mMemClient = NULL; + PVD_DELETE(tmp); + } + + mSharedMetaProvider->release(); + mPvdTransport->disconnect(); + mObjectRegistrar.clear(); + mIsConnected = false; + } +} + +void PvdImpl::flush() +{ + for(uint32_t i = 0; i < mPvdClients.size(); i++) + mPvdClients[i]->flush(); + if ( mProfileZone ) + { + mProfileZone->flushEventIdNameMap(); + mProfileZone->flushProfileEvents(); + } +} + +bool PvdImpl::isConnected(bool useCachedStatus) +{ + if(mPvdTransport) + return useCachedStatus ? mIsConnected : mPvdTransport->isConnected(); + else + return false; +} + +PxPvdTransport* PvdImpl::getTransport() +{ + return mPvdTransport; +} + +PxPvdInstrumentationFlags PvdImpl::getInstrumentationFlags() +{ + return mFlags; +} + +void PvdImpl::sendTransportInitialization() +{ + StreamInitialization init; + EventStreamifier stream(mPvdTransport->lock()); + init.serialize(stream); + mPvdTransport->unlock(); +} + +void PvdImpl::addClient(PvdClient* client) +{ + PX_ASSERT(client); + for(uint32_t i = 0; i < mPvdClients.size(); i++) + { + if(client == mPvdClients[i]) + return; + } + mPvdClients.pushBack(client); + if(mIsConnected) + { + client->onPvdConnected(); + } +} + +void PvdImpl::removeClient(PvdClient* client) +{ + for(uint32_t i = 0; i < mPvdClients.size(); i++) + { + if(client == mPvdClients[i]) + { + client->onPvdDisconnected(); + mPvdClients.remove(i); + } + } +} + +void PvdImpl::onAllocation(size_t inSize, const char* inType, const char* inFile, int inLine, void* inAddr) +{ + if(mMemClient) + mMemClient->onAllocation(inSize, inType, inFile, inLine, inAddr); +} + +void PvdImpl::onDeallocation(void* inAddr) +{ + if(mMemClient) + mMemClient->onDeallocation(inAddr); +} + +PvdOMMetaDataProvider& PvdImpl::getMetaDataProvider() +{ + return *mSharedMetaProvider; +} + +bool PvdImpl::registerObject(const void* inItem) +{ + return mObjectRegistrar.addItem(inItem); +} + + +bool PvdImpl::unRegisterObject(const void* inItem) +{ + return mObjectRegistrar.decItem(inItem); +} + +uint64_t PvdImpl::getNextStreamId() +{ + uint64_t retval = ++mNextStreamId; + return retval; +} + +bool PvdImpl::initialize() +{ + if(0 == sRefCount) + { + sInstance = PVD_NEW(PvdImpl)(); + } + ++sRefCount; + return !!sInstance; +} + +void PvdImpl::release() +{ + if(sRefCount > 0) + { + if(--sRefCount) + return; + + PVD_DELETE(sInstance); + sInstance = NULL; + } +} + +PvdImpl* PvdImpl::getInstance() +{ + return sInstance; +} + + +/************************************************************************************************************************** +Instrumented profiling events +***************************************************************************************************************************/ + +static const uint32_t CrossThreadId = 99999789; + +void* PvdImpl::zoneStart(const char* eventName, bool detached, uint64_t contextId) +{ + if(mProfileZone) + { + const uint16_t id = mProfileZone->getEventIdForName(eventName); + if(detached) + mProfileZone->startEvent(id, contextId, CrossThreadId); + else + mProfileZone->startEvent(id, contextId); + } +#if PX_NVTX + if(mIsNVTXSupportEnabled) + { + if(detached) + { + // TODO : Need to use the nvtxRangeStart API for cross thread events + nvtxEventAttributes_t eventAttrib; + memset(&eventAttrib, 0, sizeof(eventAttrib)); + eventAttrib.version = NVTX_VERSION; + eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; + eventAttrib.colorType = NVTX_COLOR_ARGB; + eventAttrib.color = 0xFF00FF00; + eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII; + eventAttrib.message.ascii = eventName; + nvtxMarkEx(&eventAttrib); + } + else + { + nvtxRangePush(eventName); + } + } +#endif + return NULL; +} + +void PvdImpl::zoneEnd(void* /*profilerData*/, const char* eventName, bool detached, uint64_t contextId) +{ + if(mProfileZone) + { + const uint16_t id = mProfileZone->getEventIdForName(eventName); + if(detached) + mProfileZone->stopEvent(id, contextId, CrossThreadId); + else + mProfileZone->stopEvent(id, contextId); + } +#if PX_NVTX + if(mIsNVTXSupportEnabled) + { + if(detached) + { + nvtxEventAttributes_t eventAttrib; + memset(&eventAttrib, 0, sizeof(eventAttrib)); + eventAttrib.version = NVTX_VERSION; + eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; + eventAttrib.colorType = NVTX_COLOR_ARGB; + eventAttrib.color = 0xFFFF0000; + eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII; + eventAttrib.message.ascii = eventName; + nvtxMarkEx(&eventAttrib); + } + else + { + nvtxRangePop(); + } + } +#endif +} +} // pvd + +} // physx diff --git a/src/PhysX/physx/source/pvd/src/PxPvdImpl.h b/src/PhysX/physx/source/pvd/src/PxPvdImpl.h new file mode 100644 index 000000000..ab19343d9 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdImpl.h @@ -0,0 +1,221 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDIMPL_H +#define PXPVDSDK_PXPVDIMPL_H + +#include "foundation/PxProfiler.h" + +#include "PsAllocator.h" +#include "PsPvd.h" +#include "PsArray.h" +#include "PsMutex.h" +#include "PxPvdCommStreamTypes.h" +#include "PxPvdFoundation.h" +#include "PxPvdObjectModelMetaData.h" +#include "PxPvdObjectRegistrar.h" + +namespace physx +{ + +namespace profile +{ + class PxProfileZoneManager; +} + +namespace pvdsdk +{ +class PvdMemClient; +class PvdProfileZoneClient; + +struct MetaDataProvider : public PvdOMMetaDataProvider, public shdfnd::UserAllocated +{ + typedef shdfnd::Mutex::ScopedLock TScopedLockType; + typedef shdfnd::HashMap TInstTypeMap; + PvdObjectModelMetaData& mMetaData; + shdfnd::Mutex mMutex; + uint32_t mRefCount; + TInstTypeMap mTypeMap; + + MetaDataProvider() + : mMetaData(PvdObjectModelMetaData::create()), mRefCount(0), mTypeMap("MetaDataProvider::mTypeMap") + { + mMetaData.addRef(); + } + virtual ~MetaDataProvider() + { + mMetaData.release(); + } + + virtual void addRef() + { + TScopedLockType locker(mMutex); + ++mRefCount; + } + virtual void release() + { + { + TScopedLockType locker(mMutex); + if(mRefCount) + --mRefCount; + } + if(!mRefCount) + PVD_DELETE(this); + } + virtual PvdObjectModelMetaData& lock() + { + mMutex.lock(); + return mMetaData; + } + virtual void unlock() + { + mMutex.unlock(); + } + + virtual bool createInstance(const NamespacedName& clsName, const void* instance) + { + TScopedLockType locker(mMutex); + Option cls(mMetaData.findClass(clsName)); + if(cls.hasValue() == false) + return false; + int32_t instType = cls->mClassId; + mTypeMap.insert(instance, instType); + return true; + } + virtual bool isInstanceValid(const void* instance) + { + TScopedLockType locker(mMutex); + ClassDescription classDesc; + bool retval = mTypeMap.find(instance) != NULL; +#if PX_DEBUG + if(retval) + classDesc = mMetaData.getClass(mTypeMap.find(instance)->second); +#endif + return retval; + } + virtual void destroyInstance(const void* instance) + { + { + TScopedLockType locker(mMutex); + mTypeMap.erase(instance); + } + } + virtual int32_t getInstanceClassType(const void* instance) + { + TScopedLockType locker(mMutex); + const TInstTypeMap::Entry* entry = mTypeMap.find(instance); + if(entry) + return entry->second; + return -1; + } + + private: + MetaDataProvider& operator=(const MetaDataProvider&); + MetaDataProvider(const MetaDataProvider&); +}; + +////////////////////////////////////////////////////////////////////////// +/*! +PvdImpl is the realization of PxPvd. +It implements the interface methods and provides richer functionality for advanced users or internal clients (such as +PhysX or APEX), including handler notification for clients. +*/ +////////////////////////////////////////////////////////////////////////// +class PvdImpl : public PsPvd, public shdfnd::UserAllocated +{ + PX_NOCOPY(PvdImpl) + + typedef shdfnd::Mutex::ScopedLock TScopedLockType; + typedef void (PvdImpl::*TAllocationHandler)(size_t size, const char* typeName, const char* filename, int line, + void* allocatedMemory); + typedef void (PvdImpl::*TDeallocationHandler)(void* allocatedMemory); + + public: + PvdImpl(); + virtual ~PvdImpl(); + void release(); + + bool connect(PxPvdTransport& transport, PxPvdInstrumentationFlags flags); + void disconnect(); + bool isConnected(bool useCachedStatus = true); + void flush(); + + PxPvdTransport* getTransport(); + PxPvdInstrumentationFlags getInstrumentationFlags(); + + void addClient(PvdClient* client); + void removeClient(PvdClient* client); + + PvdOMMetaDataProvider& getMetaDataProvider(); + + bool registerObject(const void* inItem); + bool unRegisterObject(const void* inItem); + + //AllocationListener + void onAllocation(size_t size, const char* typeName, const char* filename, int line, void* allocatedMemory); + void onDeallocation(void* addr); + + uint64_t getNextStreamId(); + + static bool initialize(); + static PvdImpl* getInstance(); + + // Profiling + + virtual void* zoneStart(const char* eventName, bool detached, uint64_t contextId); + + virtual void zoneEnd(void* profilerData, const char *eventName, bool detached, uint64_t contextId); + + private: + void sendTransportInitialization(); + + PxPvdTransport* mPvdTransport; + physx::shdfnd::Array mPvdClients; + + MetaDataProvider* mSharedMetaProvider; // shared between clients + ObjectRegistrar mObjectRegistrar; + + PvdMemClient* mMemClient; + + PxPvdInstrumentationFlags mFlags; + bool mIsConnected; + bool mIsNVTXSupportEnabled; + uint32_t mNVTXContext; + uint64_t mNextStreamId; + physx::profile::PxProfileZoneManager*mProfileZoneManager; + PvdProfileZoneClient* mProfileClient; + physx::profile::PxProfileZone* mProfileZone; + static PvdImpl* sInstance; + static uint32_t sRefCount; +}; + +} // namespace pvdsdk +} + +#endif // PXPVDSDK_PXPVDIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h b/src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h new file mode 100644 index 000000000..71bdcedd1 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDINTERNALBYTESTREAMS_H +#define PXPVDSDK_PXPVDINTERNALBYTESTREAMS_H + +#include "PxPvdByteStreams.h" +#include "PxPvdFoundation.h" + +namespace physx +{ +namespace pvdsdk +{ +struct MemPvdInputStream : public PvdInputStream +{ + const uint8_t* mBegin; + const uint8_t* mEnd; + bool mGood; + + MemPvdInputStream(const uint8_t* beg = NULL, const uint8_t* end = NULL) + { + mBegin = beg; + mEnd = end; + mGood = true; + } + + uint32_t size() const + { + return mGood ? static_cast(mEnd - mBegin) : 0; + } + bool isGood() const + { + return mGood; + } + + void setup(uint8_t* start, uint8_t* stop) + { + mBegin = start; + mEnd = stop; + } + + void nocopyRead(uint8_t*& buffer, uint32_t& len) + { + if(len == 0 || mGood == false) + { + len = 0; + buffer = NULL; + return; + } + uint32_t original = len; + len = PxMin(len, size()); + if(mGood && len != original) + mGood = false; + buffer = const_cast(mBegin); + mBegin += len; + } + + virtual bool read(uint8_t* buffer, uint32_t& len) + { + if(len == 0) + return true; + uint32_t original = len; + len = PxMin(len, size()); + + physx::intrinsics::memCopy(buffer, mBegin, len); + mBegin += len; + if(len < original) + physx::intrinsics::memZero(buffer + len, original - len); + mGood = mGood && len == original; + return mGood; + } +}; +} +} +#endif // PXPVDSDK_PXPVDINTERNALBYTESTREAMS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h b/src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h new file mode 100644 index 000000000..8c80ddc27 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h @@ -0,0 +1,220 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDMARSHALLING_H +#define PXPVDSDK_PXPVDMARSHALLING_H + +#include "foundation/PxIntrinsics.h" + +#include "PxPvdObjectModelBaseTypes.h" +#include "PxPvdBits.h" + +namespace physx +{ +namespace pvdsdk +{ + +// Define marshalling + +template +struct PvdMarshalling +{ + bool canMarshal; + PvdMarshalling() : canMarshal(false) + { + } +}; + +template +static inline void marshalSingleT(const uint8_t* srcData, uint8_t* destData) +{ + smtype incoming; + + physx::intrinsics::memCopy(&incoming, srcData, sizeof(smtype)); + lgtype outgoing = static_cast(incoming); + physx::intrinsics::memCopy(destData, &outgoing, sizeof(lgtype)); +} + +template +static inline void marshalBlockT(const uint8_t* srcData, uint8_t* destData, uint32_t numBytes) +{ + for(const uint8_t* item = srcData, *end = srcData + numBytes; item < end; + item += sizeof(smtype), destData += sizeof(lgtype)) + marshalSingleT(item, destData); +} + +#define PVD_TYPE_MARSHALLER(smtype, lgtype) \ + template <> \ + struct PvdMarshalling \ + { \ + uint32_t canMarshal; \ + static void marshalSingle(const uint8_t* srcData, uint8_t* destData) \ + { \ + marshalSingleT(srcData, destData); \ + } \ + static void marshalBlock(const uint8_t* srcData, uint8_t* destData, uint32_t numBytes) \ + { \ + marshalBlockT(srcData, destData, numBytes); \ + } \ + }; + +// define marshalling tables. +PVD_TYPE_MARSHALLER(int8_t, int16_t) +PVD_TYPE_MARSHALLER(int8_t, uint16_t) +PVD_TYPE_MARSHALLER(int8_t, int32_t) +PVD_TYPE_MARSHALLER(int8_t, uint32_t) +PVD_TYPE_MARSHALLER(int8_t, int64_t) +PVD_TYPE_MARSHALLER(int8_t, uint64_t) +PVD_TYPE_MARSHALLER(int8_t, PvdF32) +PVD_TYPE_MARSHALLER(int8_t, PvdF64) + +PVD_TYPE_MARSHALLER(uint8_t, int16_t) +PVD_TYPE_MARSHALLER(uint8_t, uint16_t) +PVD_TYPE_MARSHALLER(uint8_t, int32_t) +PVD_TYPE_MARSHALLER(uint8_t, uint32_t) +PVD_TYPE_MARSHALLER(uint8_t, int64_t) +PVD_TYPE_MARSHALLER(uint8_t, uint64_t) +PVD_TYPE_MARSHALLER(uint8_t, PvdF32) +PVD_TYPE_MARSHALLER(uint8_t, PvdF64) + +PVD_TYPE_MARSHALLER(int16_t, int32_t) +PVD_TYPE_MARSHALLER(int16_t, uint32_t) +PVD_TYPE_MARSHALLER(int16_t, int64_t) +PVD_TYPE_MARSHALLER(int16_t, uint64_t) +PVD_TYPE_MARSHALLER(int16_t, PvdF32) +PVD_TYPE_MARSHALLER(int16_t, PvdF64) + +PVD_TYPE_MARSHALLER(uint16_t, int32_t) +PVD_TYPE_MARSHALLER(uint16_t, uint32_t) +PVD_TYPE_MARSHALLER(uint16_t, int64_t) +PVD_TYPE_MARSHALLER(uint16_t, uint64_t) +PVD_TYPE_MARSHALLER(uint16_t, PvdF32) +PVD_TYPE_MARSHALLER(uint16_t, PvdF64) + +PVD_TYPE_MARSHALLER(int32_t, int64_t) +PVD_TYPE_MARSHALLER(int32_t, uint64_t) +PVD_TYPE_MARSHALLER(int32_t, PvdF64) +PVD_TYPE_MARSHALLER(int32_t, PvdF32) + +PVD_TYPE_MARSHALLER(uint32_t, int64_t) +PVD_TYPE_MARSHALLER(uint32_t, uint64_t) +PVD_TYPE_MARSHALLER(uint32_t, PvdF64) +PVD_TYPE_MARSHALLER(uint32_t, PvdF32) + +PVD_TYPE_MARSHALLER(PvdF32, PvdF64) +PVD_TYPE_MARSHALLER(PvdF32, uint32_t) +PVD_TYPE_MARSHALLER(PvdF32, int32_t) + +PVD_TYPE_MARSHALLER(uint64_t, PvdF64) +PVD_TYPE_MARSHALLER(int64_t, PvdF64) +PVD_TYPE_MARSHALLER(PvdF64, uint64_t) +PVD_TYPE_MARSHALLER(PvdF64, int64_t) + +template +static inline bool getMarshalOperators(TSingleMarshaller&, TBlockMarshaller&, TMarshaller&, bool) +{ + return false; +} + +template +static inline bool getMarshalOperators(TSingleMarshaller& single, TBlockMarshaller& block, TMarshaller&, uint32_t) +{ + single = TMarshaller::marshalSingle; + block = TMarshaller::marshalBlock; + return true; +} + +template +static inline bool getMarshalOperators(TSingleMarshaller& single, TBlockMarshaller& block) +{ + single = NULL; + block = NULL; + PvdMarshalling marshaller = PvdMarshalling(); + return getMarshalOperators(single, block, marshaller, marshaller.canMarshal); +} + +template +static inline bool getMarshalOperators(TSingleMarshaller& single, TBlockMarshaller& block, int32_t lgtypeId) +{ + switch(lgtypeId) + { + case PvdBaseType::PvdI8: // int8_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdU8: // uint8_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdI16: // int16_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdU16: // uint16_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdI32: // int32_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdU32: // uint32_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdI64: // int64_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdU64: // uint64_t: + return getMarshalOperators(single, block); + case PvdBaseType::PvdF32: + return getMarshalOperators(single, block); + case PvdBaseType::PvdF64: + return getMarshalOperators(single, block); + } + return false; +} + +static inline bool getMarshalOperators(TSingleMarshaller& single, TBlockMarshaller& block, int32_t smtypeId, + int32_t lgtypeId) +{ + switch(smtypeId) + { + case PvdBaseType::PvdI8: // int8_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdU8: // uint8_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdI16: // int16_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdU16: // uint16_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdI32: // int32_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdU32: // uint32_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdI64: // int64_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdU64: // uint64_t: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdF32: + return getMarshalOperators(single, block, lgtypeId); + case PvdBaseType::PvdF64: + return getMarshalOperators(single, block, lgtypeId); + } + return false; +} +} +} + +#endif // PXPVDSDK_PXPVDMARSHALLING_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp new file mode 100644 index 000000000..f6e201982 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdImpl.h" +#include "PxPvdMemClient.h" + +namespace physx +{ +namespace pvdsdk +{ + +PvdMemClient::PvdMemClient(PvdImpl& pvd) +: mSDKPvd(pvd) +, mPvdDataStream(NULL) +, mIsConnected(false) +, mMemEventBuffer(profile::PxProfileMemoryEventBuffer::createMemoryEventBuffer(*gPvdAllocatorCallback)) +{ +} + +PvdMemClient::~PvdMemClient() +{ + mSDKPvd.removeClient(this); + if(mMemEventBuffer.hasClients()) + mPvdDataStream->destroyInstance(&mMemEventBuffer); + mMemEventBuffer.release(); +} + +PvdDataStream* PvdMemClient::getDataStream() +{ + return mPvdDataStream; +} + +PvdUserRenderer* PvdMemClient::getUserRender() +{ + PX_ASSERT(0); + return NULL; +} + +void PvdMemClient::setObjectRegistrar(ObjectRegistrar*) +{ +} + +bool PvdMemClient::isConnected() const +{ + return mIsConnected; +} + +void PvdMemClient::onPvdConnected() +{ + if(mIsConnected) + return; + mIsConnected = true; + + mPvdDataStream = PvdDataStream::create(&mSDKPvd); + mPvdDataStream->createInstance(&mMemEventBuffer); + mMemEventBuffer.addClient(*this); +} + +void PvdMemClient::onPvdDisconnected() +{ + if(!mIsConnected) + return; + mIsConnected = false; + + flush(); + + mMemEventBuffer.removeClient(*this); + mPvdDataStream->release(); + mPvdDataStream = NULL; +} + +void PvdMemClient::onAllocation(size_t inSize, const char* inType, const char* inFile, int inLine, void* inAddr) +{ + mMutex.lock(); + mMemEventBuffer.onAllocation(inSize, inType, inFile, inLine, inAddr); + mMutex.unlock(); +} + +void PvdMemClient::onDeallocation(void* inAddr) +{ + mMutex.lock(); + mMemEventBuffer.onDeallocation(inAddr); + mMutex.unlock(); +} + +void PvdMemClient::flush() +{ + mMutex.lock(); + mMemEventBuffer.flushProfileEvents(); + mMutex.unlock(); +} + +void PvdMemClient::handleBufferFlush(const uint8_t* inData, uint32_t inLength) +{ + if(mPvdDataStream) + mPvdDataStream->setPropertyValue(&mMemEventBuffer, "events", inData, inLength); +} + +void PvdMemClient::handleClientRemoved() +{ +} + +} // pvd +} // physx diff --git a/src/PhysX/physx/source/pvd/src/PxPvdMemClient.h b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.h new file mode 100644 index 000000000..de4c202e8 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.h @@ -0,0 +1,85 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDMEMCLIENT_H +#define PXPVDSDK_PXPVDMEMCLIENT_H + +#include "PxPvdClient.h" +#include "PsHashMap.h" +#include "PsMutex.h" +#include "PsBroadcast.h" +#include "PxProfileEventBufferClient.h" +#include "PxProfileMemory.h" + +namespace physx +{ +class PvdDataStream; + +namespace pvdsdk +{ +class PvdImpl; +class PvdMemClient : public PvdClient, + public profile::PxProfileEventBufferClient, + public shdfnd::UserAllocated +{ + PX_NOCOPY(PvdMemClient) + public: + PvdMemClient(PvdImpl& pvd); + virtual ~PvdMemClient(); + + bool isConnected() const; + void onPvdConnected(); + void onPvdDisconnected(); + void flush(); + + PvdDataStream* getDataStream(); + PvdUserRenderer* getUserRender(); + void setObjectRegistrar(ObjectRegistrar*); + void sendMemEvents(); + + // memory event + void onAllocation(size_t size, const char* typeName, const char* filename, int line, void* allocatedMemory); + void onDeallocation(void* addr); + + private: + PvdImpl& mSDKPvd; + PvdDataStream* mPvdDataStream; + bool mIsConnected; + + // mem profile + shdfnd::Mutex mMutex; // mem onallocation can called from different threads + profile::PxProfileMemoryEventBuffer& mMemEventBuffer; + void handleBufferFlush(const uint8_t* inData, uint32_t inLength); + void handleClientRemoved(); +}; + +} // namespace pvdsdk +} // namespace physx + +#endif // PXPVDSDK_PXPVDMEMCLIENT_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h new file mode 100644 index 000000000..b273ca0f6 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h @@ -0,0 +1,32 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +DECLARE_INTERNAL_PVD_TYPE(ArrayData) + +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h new file mode 100644 index 000000000..bf9b6870b --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h @@ -0,0 +1,154 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDOBJECTMODELINTERNALTYPES_H +#define PXPVDSDK_PXPVDOBJECTMODELINTERNALTYPES_H + +#include "foundation/PxMemory.h" +#include "PxPvdObjectModelBaseTypes.h" +#include "PsArray.h" +#include "PxPvdFoundation.h" + +namespace physx +{ +namespace pvdsdk +{ + +struct PvdInternalType +{ + enum Enum + { + None = 0, +#define DECLARE_INTERNAL_PVD_TYPE(type) type, +#include "PxPvdObjectModelInternalTypeDefs.h" + Last +#undef DECLARE_INTERNAL_PVD_TYPE + }; +}; + +PX_COMPILE_TIME_ASSERT(uint32_t(PvdInternalType::Last) <= uint32_t(PvdBaseType::InternalStop)); + +template +struct DataTypeToPvdTypeMap +{ + bool compile_error; +}; +template +struct PvdTypeToDataTypeMap +{ + bool compile_error; +}; + +#define DECLARE_INTERNAL_PVD_TYPE(type) \ + template <> \ + struct DataTypeToPvdTypeMap \ + { \ + enum Enum \ + { \ + BaseTypeEnum = PvdInternalType::type \ + }; \ + }; \ + template <> \ + struct PvdTypeToDataTypeMap \ + { \ + typedef type TDataType; \ + }; \ + template <> \ + struct PvdDataTypeToNamespacedNameMap \ + { \ + NamespacedName Name; \ + PvdDataTypeToNamespacedNameMap() : Name("physx3_debugger_internal", #type) \ + { \ + } \ + }; +#include "PxPvdObjectModelInternalTypeDefs.h" +#undef DECLARE_INTERNAL_PVD_TYPE + +template +DataRef toDataRef(const shdfnd::Array& data) +{ + return DataRef(data.begin(), data.end()); +} + +static inline bool safeStrEq(const DataRef& lhs, const DataRef& rhs) +{ + uint32_t count = lhs.size(); + if(count != rhs.size()) + return false; + for(uint32_t idx = 0; idx < count; ++idx) + if(!safeStrEq(lhs[idx], rhs[idx])) + return false; + return true; +} + +static inline char* copyStr(const char* str) +{ + str = nonNull(str); + uint32_t len = static_cast(strlen(str)); + char* newData = reinterpret_cast(PX_ALLOC(len + 1, "string")); + PxMemCopy(newData, str, len); + newData[len] = 0; + return newData; +} + +// Used for predictable bit fields. +template +struct BitMaskSetter +{ + // Create a mask that masks out the orginal value shift into place + static TDataType createOffsetMask() + { + return createMask() << TOffset; + } + // Create a mask of TNumBits number of tis + static TDataType createMask() + { + return static_cast((1 << TNumBits) - 1); + } + void setValue(TDataType& inCurrent, TInputType inData) + { + PX_ASSERT(inData < (1 << TNumBits)); + + // Create a mask to remove the current value. + TDataType theMask = ~(createOffsetMask()); + // Clear out current value. + inCurrent = inCurrent & theMask; + // Create the new value. + TDataType theAddition = reinterpret_cast(inData << TOffset); + // or it into the existing value. + inCurrent = inCurrent | theAddition; + } + + TInputType getValue(TDataType inCurrent) + { + return static_cast((inCurrent >> TOffset) & createMask()); + } +}; + +} +} +#endif // PXPVDSDK_PXPVDOBJECTMODELINTERNALTYPES_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp new file mode 100644 index 000000000..6d5635240 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp @@ -0,0 +1,1503 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxPvdObjectModelInternalTypes.h" +#include "PxPvdObjectModelMetaData.h" +#include "PxPvdInternalByteStreams.h" +#include "PxPvdMarshalling.h" + +using namespace physx; +using namespace pvdsdk; +using namespace shdfnd; + +namespace +{ + +struct PropDescImpl : public PropertyDescription, public UserAllocated +{ + Array mValueNames; + PropDescImpl(const PropertyDescription& inBase, StringTable& table) + : PropertyDescription(inBase), mValueNames("NamedValue") + { + mName = table.registerStr(mName); + } + PropDescImpl() : mValueNames("NamedValue") + { + } + + template + void serialize(TSerializer& serializer) + { + serializer.streamify(mOwnerClassName); + serializer.streamify(mOwnerClassId); + serializer.streamify(mSemantic); + serializer.streamify(mDatatype); + serializer.streamify(mDatatypeName); + serializer.streamify(mPropertyType); + serializer.streamify(mPropertyId); + serializer.streamify(m32BitOffset); + serializer.streamify(m64BitOffset); + serializer.streamify(mValueNames); + serializer.streamify(mName); + } +}; + +struct ClassDescImpl : public ClassDescription, public UserAllocated +{ + Array mPropImps; + Array m32OffsetArray; + Array m64OffsetArray; + ClassDescImpl(const ClassDescription& inBase) + : ClassDescription(inBase) + , mPropImps("PropDescImpl*") + , m32OffsetArray("ClassDescImpl::m32OffsetArray") + , m64OffsetArray("ClassDescImpl::m64OffsetArray") + { + PVD_FOREACH(idx, get32BitSizeInfo().mPtrOffsets.size()) + m32OffsetArray.pushBack(get32BitSizeInfo().mPtrOffsets[idx]); + PVD_FOREACH(idx, get64BitSizeInfo().mPtrOffsets.size()) + m64OffsetArray.pushBack(get64BitSizeInfo().mPtrOffsets[idx]); + } + ClassDescImpl() + : mPropImps("PropDescImpl*") + , m32OffsetArray("ClassDescImpl::m32OffsetArray") + , m64OffsetArray("ClassDescImpl::m64OffsetArray") + { + } + PropDescImpl* findProperty(String name) + { + PVD_FOREACH(idx, mPropImps.size()) + { + if(safeStrEq(mPropImps[idx]->mName, name)) + return mPropImps[idx]; + } + return NULL; + } + void addProperty(PropDescImpl* prop) + { + mPropImps.pushBack(prop); + } + + void addPtrOffset(PtrOffsetType::Enum type, uint32_t offset32, uint32_t offset64) + { + m32OffsetArray.pushBack(PtrOffset(type, offset32)); + m64OffsetArray.pushBack(PtrOffset(type, offset64)); + get32BitSizeInfo().mPtrOffsets = DataRef(m32OffsetArray.begin(), m32OffsetArray.end()); + get64BitSizeInfo().mPtrOffsets = DataRef(m64OffsetArray.begin(), m64OffsetArray.end()); + } + + template + void serialize(TSerializer& serializer) + { + serializer.streamify(mName); + serializer.streamify(mClassId); + serializer.streamify(mBaseClass); + serializer.streamify(mPackedUniformWidth); + serializer.streamify(mPackedClassType); + serializer.streamify(mLocked); + serializer.streamify(mRequiresDestruction); + serializer.streamify(get32BitSize()); + serializer.streamify(get32BitSizeInfo().mDataByteSize); + serializer.streamify(get32BitSizeInfo().mAlignment); + serializer.streamify(get64BitSize()); + serializer.streamify(get64BitSizeInfo().mDataByteSize); + serializer.streamify(get64BitSizeInfo().mAlignment); + serializer.streamifyLinks(mPropImps); + serializer.streamify(m32OffsetArray); + serializer.streamify(m64OffsetArray); + get32BitSizeInfo().mPtrOffsets = DataRef(m32OffsetArray.begin(), m32OffsetArray.end()); + get64BitSizeInfo().mPtrOffsets = DataRef(m64OffsetArray.begin(), m64OffsetArray.end()); + } +}; + +class StringTableImpl : public StringTable, public UserAllocated +{ + HashMap mStrings; + uint32_t mNextStrHandle; + HashMap mHandleToStr; + HashMap mStrToHandle; + + public: + StringTableImpl() + : mStrings("StringTableImpl::mStrings") + , mNextStrHandle(1) + , mHandleToStr("StringTableImpl::mHandleToStr") + , mStrToHandle("StringTableImpl::mStrToHandle") + { + } + uint32_t nextHandleValue() + { + return mNextStrHandle++; + } + virtual ~StringTableImpl() + { + for(HashMap::Iterator iter = mStrings.getIterator(); !iter.done(); ++iter) + PX_FREE(iter->second); + mStrings.clear(); + } + virtual uint32_t getNbStrs() + { + return mStrings.size(); + } + virtual uint32_t getStrs(const char** outStrs, uint32_t bufLen, uint32_t startIdx = 0) + { + startIdx = PxMin(getNbStrs(), startIdx); + uint32_t numStrs(PxMin(getNbStrs() - startIdx, bufLen)); + HashMap::Iterator iter(mStrings.getIterator()); + for(uint32_t idx = 0; idx < startIdx; ++idx, ++iter) + ; + for(uint32_t idx = 0; idx < numStrs && !iter.done(); ++idx, ++iter) + outStrs[idx] = iter->second; + return numStrs; + } + void addStringHandle(char* str, uint32_t hdl) + { + mHandleToStr.insert(hdl, str); + mStrToHandle.insert(str, hdl); + } + + uint32_t addStringHandle(char* str) + { + uint32_t theNewHandle = nextHandleValue(); + addStringHandle(str, theNewHandle); + return theNewHandle; + } + const char* doRegisterStr(const char* str, bool& outAdded) + { + PX_ASSERT(isMeaningful(str)); + const HashMap::Entry* entry(mStrings.find(str)); + if(entry == NULL) + { + outAdded = true; + char* retval(copyStr(str)); + mStrings.insert(retval, retval); + return retval; + } + return entry->second; + } + virtual const char* registerStr(const char* str, bool& outAdded) + { + outAdded = false; + if(isMeaningful(str) == false) + return ""; + const char* retval = doRegisterStr(str, outAdded); + if(outAdded) + addStringHandle(const_cast(retval)); + return retval; + } + + NamespacedName registerName(const NamespacedName& nm) + { + return NamespacedName(registerStr(nm.mNamespace), registerStr(nm.mName)); + } + const char* registerStr(const char* str) + { + bool ignored; + return registerStr(str, ignored); + } + + virtual StringHandle strToHandle(const char* str) + { + if(isMeaningful(str) == false) + return 0; + const HashMap::Entry* entry(mStrToHandle.find(str)); + if(entry) + return entry->second; + bool added = false; + const char* registeredStr = doRegisterStr(str, added); + uint32_t theNewHandle = addStringHandle(const_cast(registeredStr)); + PX_ASSERT(mStrToHandle.find(str)); + PX_ASSERT(added); + return theNewHandle; + } + + virtual const char* handleToStr(uint32_t hdl) + { + if(hdl == 0) + return ""; + const HashMap::Entry* entry(mHandleToStr.find(hdl)); + if(entry) + return entry->second; + // unregistered handle... + return ""; + } + + void write(PvdOutputStream& stream) + { + uint32_t numStrs = static_cast(mHandleToStr.size()); + stream << numStrs; + stream << mNextStrHandle; + for(HashMap::Iterator iter = mHandleToStr.getIterator(); !iter.done(); ++iter) + { + stream << iter->first; + uint32_t len = static_cast(strlen(iter->second) + 1); + stream << len; + stream.write(reinterpret_cast(iter->second), len); + } + } + + template + void read(TReader& stream) + { + mHandleToStr.clear(); + mStrToHandle.clear(); + uint32_t numStrs; + stream >> numStrs; + stream >> mNextStrHandle; + Array readBuffer("StringTable::read::readBuffer"); + uint32_t bufSize = 0; + for(uint32_t idx = 0; idx < numStrs; ++idx) + { + uint32_t handleValue; + uint32_t bufLen; + stream >> handleValue; + stream >> bufLen; + if(bufSize < bufLen) + readBuffer.resize(bufLen); + bufSize = PxMax(bufSize, bufLen); + stream.read(readBuffer.begin(), bufLen); + bool ignored; + const char* newStr = doRegisterStr(reinterpret_cast(readBuffer.begin()), ignored); + addStringHandle(const_cast(newStr), handleValue); + } + } + + virtual void release() + { + PVD_DELETE(this); + } + + private: + StringTableImpl& operator=(const StringTableImpl&); +}; + +struct NamespacedNameHasher +{ + uint32_t operator()(const NamespacedName& nm) + { + return Hash()(nm.mNamespace) ^ Hash()(nm.mName); + } + bool equal(const NamespacedName& lhs, const NamespacedName& rhs) + { + return safeStrEq(lhs.mNamespace, rhs.mNamespace) && safeStrEq(lhs.mName, rhs.mName); + } +}; + +struct ClassPropertyName +{ + NamespacedName mName; + String mPropName; + ClassPropertyName(const NamespacedName& name = NamespacedName(), String propName = "") + : mName(name), mPropName(propName) + { + } +}; + +struct ClassPropertyNameHasher +{ + uint32_t operator()(const ClassPropertyName& nm) + { + return NamespacedNameHasher()(nm.mName) ^ Hash()(nm.mPropName); + } + bool equal(const ClassPropertyName& lhs, const ClassPropertyName& rhs) + { + return NamespacedNameHasher().equal(lhs.mName, rhs.mName) && safeStrEq(lhs.mPropName, rhs.mPropName); + } +}; + +struct PropertyMessageEntryImpl : public PropertyMessageEntry +{ + PropertyMessageEntryImpl(const PropertyMessageEntry& data) : PropertyMessageEntry(data) + { + } + PropertyMessageEntryImpl() + { + } + template + void serialize(TSerializerType& serializer) + { + serializer.streamify(mDatatypeName); + serializer.streamify(mDatatypeId); + serializer.streamify(mMessageOffset); + serializer.streamify(mByteSize); + serializer.streamify(mDestByteSize); + serializer.streamify(mProperty); + } +}; + +struct PropertyMessageDescriptionImpl : public PropertyMessageDescription, public UserAllocated +{ + Array mEntryImpls; + Array mEntries; + Array mStringOffsetArray; + PropertyMessageDescriptionImpl(const PropertyMessageDescription& data) + : PropertyMessageDescription(data) + , mEntryImpls("PropertyMessageDescriptionImpl::mEntryImpls") + , mEntries("PropertyMessageDescriptionImpl::mEntries") + , mStringOffsetArray("PropertyMessageDescriptionImpl::mStringOffsets") + { + } + PropertyMessageDescriptionImpl() + : mEntryImpls("PropertyMessageDescriptionImpl::mEntryImpls") + , mEntries("PropertyMessageDescriptionImpl::mEntries") + , mStringOffsetArray("PropertyMessageDescriptionImpl::mStringOffsets") + { + } + + ~PropertyMessageDescriptionImpl() + { + } + + void addEntry(const PropertyMessageEntryImpl& entry) + { + mEntryImpls.pushBack(entry); + mEntries.pushBack(entry); + mProperties = DataRef(mEntries.begin(), mEntries.end()); + } + + template + void serialize(TSerializerType& serializer) + { + serializer.streamify(mClassName); + serializer.streamify(mClassId); // No other class has this id, it is DB-unique + serializer.streamify(mMessageName); + serializer.streamify(mMessageId); + serializer.streamify(mMessageByteSize); + serializer.streamify(mEntryImpls); + serializer.streamify(mStringOffsetArray); + if(mEntries.size() != mEntryImpls.size()) + { + mEntries.clear(); + uint32_t numEntries = static_cast(mEntryImpls.size()); + for(uint32_t idx = 0; idx < numEntries; ++idx) + mEntries.pushBack(mEntryImpls[idx]); + } + mProperties = DataRef(mEntries.begin(), mEntries.end()); + mStringOffsets = DataRef(mStringOffsetArray.begin(), mStringOffsetArray.end()); + } + + private: + PropertyMessageDescriptionImpl& operator=(const PropertyMessageDescriptionImpl&); +}; + +struct PvdObjectModelMetaDataImpl : public PvdObjectModelMetaData, public UserAllocated +{ + typedef HashMap TNameToClassMap; + typedef HashMap TNameToPropMap; + typedef HashMap TNameToPropertyMessageMap; + + TNameToClassMap mNameToClasses; + TNameToPropMap mNameToProperties; + Array mClasses; + Array mProperties; + StringTableImpl* mStringTable; + TNameToPropertyMessageMap mPropertyMessageMap; + Array mPropertyMessages; + int32_t mNextClassId; + uint32_t mRefCount; + + PvdObjectModelMetaDataImpl() + : mNameToClasses("NamespacedName->ClassDescImpl*") + , mNameToProperties("ClassPropertyName->PropDescImpl*") + , mClasses("ClassDescImpl*") + , mProperties("PropDescImpl*") + , mStringTable(PVD_NEW(StringTableImpl)()) + , mPropertyMessageMap("PropertyMessageMap") + , mPropertyMessages("PvdObjectModelMetaDataImpl::mPropertyMessages") + , mNextClassId(1) + , mRefCount(0) + { + } + + private: + PvdObjectModelMetaDataImpl& operator=(const PvdObjectModelMetaDataImpl&); + + public: + int32_t nextClassId() + { + return mNextClassId++; + } + void initialize() + { + // Create the default classes. + { + ClassDescImpl& aryData = getOrCreateClassImpl(getPvdNamespacedNameForType(), + DataTypeToPvdTypeMap::BaseTypeEnum); + aryData.get32BitSize() = sizeof(ArrayData); + aryData.get32BitSizeInfo().mAlignment = sizeof(void*); + aryData.get64BitSize() = sizeof(ArrayData); + aryData.get64BitSizeInfo().mAlignment = sizeof(void*); + aryData.mLocked = true; + } +#define CREATE_BASIC_PVD_CLASS(type) \ + { \ + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); \ + cls.get32BitSize() = sizeof(type); \ + cls.get32BitSizeInfo().mAlignment = sizeof(type); \ + cls.get64BitSize() = sizeof(type); \ + cls.get64BitSizeInfo().mAlignment = sizeof(type); \ + cls.mLocked = true; \ + cls.mPackedUniformWidth = sizeof(type); \ + cls.mPackedClassType = getPvdTypeForType(); \ + } + CREATE_BASIC_PVD_CLASS(int8_t) + CREATE_BASIC_PVD_CLASS(uint8_t) + CREATE_BASIC_PVD_CLASS(bool) + CREATE_BASIC_PVD_CLASS(int16_t) + CREATE_BASIC_PVD_CLASS(uint16_t) + CREATE_BASIC_PVD_CLASS(int32_t) + CREATE_BASIC_PVD_CLASS(uint32_t) + // CREATE_BASIC_PVD_CLASS(uint32_t) + CREATE_BASIC_PVD_CLASS(int64_t) + CREATE_BASIC_PVD_CLASS(uint64_t) + CREATE_BASIC_PVD_CLASS(float) + CREATE_BASIC_PVD_CLASS(double) +#undef CREATE_BASIC_PVD_CLASS + +#define CREATE_PTR_TYPE_PVD_CLASS(type, ptrType) \ + { \ + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); \ + cls.get32BitSize() = 4; \ + cls.get32BitSizeInfo().mAlignment = 4; \ + cls.get64BitSize() = 8; \ + cls.get64BitSizeInfo().mAlignment = 8; \ + cls.mLocked = true; \ + cls.addPtrOffset(PtrOffsetType::ptrType, 0, 0); \ + } + + CREATE_PTR_TYPE_PVD_CLASS(String, StringOffset) + CREATE_PTR_TYPE_PVD_CLASS(VoidPtr, VoidPtrOffset) + CREATE_PTR_TYPE_PVD_CLASS(StringHandle, StringOffset) + CREATE_PTR_TYPE_PVD_CLASS(ObjectRef, VoidPtrOffset) + +#undef CREATE_64BIT_ADJUST_PVD_CLASS + + int32_t fltClassType = getPvdTypeForType(); + int32_t u32ClassType = getPvdTypeForType(); + int32_t v3ClassType = getPvdTypeForType(); + int32_t v4ClassType = getPvdTypeForType(); + int32_t qtClassType = getPvdTypeForType(); + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "r", "", getPvdTypeForType(), PropertyType::Scalar); + createProperty(cls.mClassId, "g", "", getPvdTypeForType(), PropertyType::Scalar); + createProperty(cls.mClassId, "b", "", getPvdTypeForType(), PropertyType::Scalar); + createProperty(cls.mClassId, "a", "", getPvdTypeForType(), PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 1); + PX_ASSERT(cls.get32BitSize() == 4); + PX_ASSERT(cls.get64BitSizeInfo().mAlignment == 1); + PX_ASSERT(cls.get64BitSize() == 4); + PX_ASSERT(cls.mPackedUniformWidth == 1); + PX_ASSERT(cls.mPackedClassType == getPvdTypeForType()); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "x", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "y", "", fltClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 8); + PX_ASSERT(cls.get64BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get64BitSize() == 8); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + { + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "x", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "y", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "z", "", fltClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 12); + PX_ASSERT(cls.get64BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get64BitSize() == 12); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + { + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "x", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "y", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "z", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "w", "", fltClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 16); + PX_ASSERT(cls.get64BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get64BitSize() == 16); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "x", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "y", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "z", "", fltClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "w", "", fltClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 16); + PX_ASSERT(cls.get64BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get64BitSize() == 16); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "minimum", "", v3ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "maximum", "", v3ClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 24); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "q", "", qtClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "p", "", v3ClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 28); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "column0", "", v3ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "column1", "", v3ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "column2", "", v3ClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 36); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "column0", "", v4ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "column1", "", v4ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "column2", "", v4ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "column3", "", v4ClassType, PropertyType::Scalar); + PX_ASSERT(cls.get32BitSizeInfo().mAlignment == 4); + PX_ASSERT(cls.get32BitSize() == 64); + PX_ASSERT(cls.mPackedUniformWidth == 4); + PX_ASSERT(cls.mPackedClassType == fltClassType); + cls.mLocked = true; + } + + { + ClassDescImpl& cls = + getOrCreateClassImpl(getPvdNamespacedNameForType(), getPvdTypeForType()); + createProperty(cls.mClassId, "d0", "", u32ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "d1", "", u32ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "d2", "", u32ClassType, PropertyType::Scalar); + createProperty(cls.mClassId, "d3", "", u32ClassType, PropertyType::Scalar); + cls.mLocked = true; + } + } + virtual ~PvdObjectModelMetaDataImpl() + { + mStringTable->release(); + PVD_FOREACH(idx, mClasses.size()) + { + if(mClasses[idx] != NULL) + PVD_DELETE(mClasses[idx]); + } + mClasses.clear(); + PVD_FOREACH(idx, mProperties.size()) PVD_DELETE(mProperties[idx]); + mProperties.clear(); + PVD_FOREACH(idx, mPropertyMessages.size()) PVD_DELETE(mPropertyMessages[idx]); + mPropertyMessages.clear(); + } + + ClassDescImpl& getOrCreateClassImpl(const NamespacedName& nm, int32_t idx) + { + ClassDescImpl* impl(getClassImpl(idx)); + if(impl) + return *impl; + NamespacedName safeName(mStringTable->registerStr(nm.mNamespace), mStringTable->registerStr(nm.mName)); + while(idx >= int32_t(mClasses.size())) + mClasses.pushBack(NULL); + mClasses[uint32_t(idx)] = PVD_NEW(ClassDescImpl)(ClassDescription(safeName, idx)); + mNameToClasses.insert(nm, mClasses[uint32_t(idx)]); + mNextClassId = PxMax(mNextClassId, idx + 1); + return *mClasses[uint32_t(idx)]; + } + + ClassDescImpl& getOrCreateClassImpl(const NamespacedName& nm) + { + ClassDescImpl* retval = findClassImpl(nm); + if(retval) + return *retval; + return getOrCreateClassImpl(nm, nextClassId()); + } + virtual ClassDescription getOrCreateClass(const NamespacedName& nm) + { + return getOrCreateClassImpl(nm); + } + // get or create parent, lock parent. deriveFrom getOrCreatechild. + virtual bool deriveClass(const NamespacedName& parent, const NamespacedName& child) + { + ClassDescImpl& p(getOrCreateClassImpl(parent)); + ClassDescImpl& c(getOrCreateClassImpl(child)); + + if(c.mBaseClass >= 0) + { + PX_ASSERT(c.mBaseClass == p.mClassId); + return false; + } + p.mLocked = true; + c.mBaseClass = p.mClassId; + c.get32BitSizeInfo() = p.get32BitSizeInfo(); + c.get64BitSizeInfo() = p.get64BitSizeInfo(); + c.mPackedClassType = p.mPackedClassType; + c.mPackedUniformWidth = p.mPackedUniformWidth; + c.mRequiresDestruction = p.mRequiresDestruction; + c.m32OffsetArray = p.m32OffsetArray; + c.m64OffsetArray = p.m64OffsetArray; + // Add all the parent propertes to this class in the global name map. + for(ClassDescImpl* parent0 = &p; parent0 != NULL; parent0 = getClassImpl(parent0->mBaseClass)) + { + PVD_FOREACH(idx, parent0->mPropImps.size()) + mNameToProperties.insert(ClassPropertyName(c.mName, parent0->mPropImps[idx]->mName), parent0->mPropImps[idx]); + + if(parent0->mBaseClass < 0) + break; + } + + return true; + } + ClassDescImpl* findClassImpl(const NamespacedName& nm) const + { + const TNameToClassMap::Entry* entry(mNameToClasses.find(nm)); + if(entry) + return entry->second; + return NULL; + } + virtual Option findClass(const NamespacedName& nm) const + { + ClassDescImpl* retval = findClassImpl(nm); + if(retval) + return *retval; + return Option(); + } + + ClassDescImpl* getClassImpl(int32_t classId) const + { + if(classId < 0) + return NULL; + uint32_t idx = uint32_t(classId); + if(idx < mClasses.size()) + return mClasses[idx]; + return NULL; + } + + virtual Option getClass(int32_t classId) const + { + ClassDescImpl* impl(getClassImpl(classId)); + if(impl) + return *impl; + return None(); + } + + virtual ClassDescription* getClassPtr(int32_t classId) const + { + return getClassImpl(classId); + } + + virtual Option getParentClass(int32_t classId) const + { + ClassDescImpl* impl(getClassImpl(classId)); + if(impl == NULL) + return None(); + return getClass(impl->mBaseClass); + } + + virtual void lockClass(int32_t classId) + { + ClassDescImpl* impl(getClassImpl(classId)); + PX_ASSERT(impl); + if(impl) + impl->mLocked = true; + } + virtual uint32_t getNbClasses() const + { + uint32_t total = 0; + PVD_FOREACH(idx, mClasses.size()) if(mClasses[idx])++ total; + return total; + } + + virtual uint32_t getClasses(ClassDescription* outClasses, uint32_t requestCount, uint32_t startIndex = 0) const + { + uint32_t classCount(getNbClasses()); + startIndex = PxMin(classCount, startIndex); + uint32_t retAmount = PxMin(requestCount, classCount - startIndex); + + uint32_t idx = 0; + while(startIndex) + { + if(mClasses[idx] != NULL) + --startIndex; + ++idx; + } + + uint32_t inserted = 0; + uint32_t classesSize = static_cast(mClasses.size()); + while(inserted < retAmount && idx < classesSize) + { + if(mClasses[idx] != NULL) + { + outClasses[inserted] = *mClasses[idx]; + ++inserted; + } + ++idx; + } + return inserted; + } + + uint32_t updateByteSizeAndGetPropertyAlignment(ClassDescriptionSizeInfo& dest, const ClassDescriptionSizeInfo& src) + { + uint32_t alignment = src.mAlignment; + dest.mAlignment = PxMax(dest.mAlignment, alignment); + uint32_t offset = align(dest.mDataByteSize, alignment); + dest.mDataByteSize = offset + src.mByteSize; + dest.mByteSize = align(dest.mDataByteSize, dest.mAlignment); + return offset; + } + + void transferPtrOffsets(ClassDescriptionSizeInfo& destInfo, Array& destArray, + const Array& src, uint32_t offset) + { + PVD_FOREACH(idx, src.size()) + destArray.pushBack(PtrOffset(src[idx].mOffsetType, src[idx].mOffset + offset)); + destInfo.mPtrOffsets = DataRef(destArray.begin(), destArray.end()); + } + + virtual Option createProperty(int32_t classId, String name, String semantic, int32_t datatype, + PropertyType::Enum propertyType) + { + ClassDescImpl* cls(getClassImpl(classId)); + PX_ASSERT(cls); + if(!cls) + return None(); + if(cls->mLocked) + { + PX_ASSERT(false); + return None(); + } + PropDescImpl* impl(cls->findProperty(name)); + // duplicate property definition + if(impl) + { + PX_ASSERT(false); + return None(); + } + if(datatype == getPvdTypeForType()) + { + PX_ASSERT(false); + return None(); + } + // The datatype for this property has not been declared. + ClassDescImpl* propDType(getClassImpl(datatype)); + PX_ASSERT(propDType); + if(!propDType) + return None(); + NamespacedName propClsName(propDType->mName); + int32_t propPackedWidth = propDType->mPackedUniformWidth; + int32_t propPackedType = propDType->mPackedClassType; + // The implications of properties being complex types aren't major + //*until* you start trying to undue a property event that set values + // of those complex types. Then things just get too complex. + if(propDType->mRequiresDestruction) + { + PX_ASSERT(false); + return None(); + } + bool requiresDestruction = propDType->mRequiresDestruction || cls->mRequiresDestruction; + + if(propertyType == PropertyType::Array) + { + int32_t tempId = DataTypeToPvdTypeMap::BaseTypeEnum; + propDType = getClassImpl(tempId); + PX_ASSERT(propDType); + if(!propDType) + return None(); + requiresDestruction = true; + } + uint32_t offset32 = updateByteSizeAndGetPropertyAlignment(cls->get32BitSizeInfo(), propDType->get32BitSizeInfo()); + uint32_t offset64 = updateByteSizeAndGetPropertyAlignment(cls->get64BitSizeInfo(), propDType->get64BitSizeInfo()); + transferPtrOffsets(cls->get32BitSizeInfo(), cls->m32OffsetArray, propDType->m32OffsetArray, offset32); + transferPtrOffsets(cls->get64BitSizeInfo(), cls->m64OffsetArray, propDType->m64OffsetArray, offset64); + propDType->mLocked = true; // Can't add members to the property type. + cls->mRequiresDestruction = requiresDestruction; + int32_t propId = int32_t(mProperties.size()); + PropertyDescription newDesc(cls->mName, cls->mClassId, name, semantic, datatype, propClsName, propertyType, + propId, offset32, offset64); + mProperties.pushBack(PVD_NEW(PropDescImpl)(newDesc, *mStringTable)); + mNameToProperties.insert(ClassPropertyName(cls->mName, mProperties.back()->mName), mProperties.back()); + cls->addProperty(mProperties.back()); + bool firstProp = cls->mPropImps.size() == 1; + + if(firstProp) + { + cls->mPackedUniformWidth = propPackedWidth; + cls->mPackedClassType = propPackedType; + } + else + { + bool packed = (propPackedWidth > 0) && (cls->get32BitSizeInfo().mDataByteSize % propPackedWidth) == 0; + if(cls->mPackedClassType >= 0) // maybe uncheck packed class type + { + if(propPackedType < 0 || cls->mPackedClassType != propPackedType + // Object refs require conversion from stream to db id + || + datatype == getPvdTypeForType() + // Strings also require conversion from stream to db id. + || + datatype == getPvdTypeForType() || packed == false) + cls->mPackedClassType = -1; + } + if(cls->mPackedUniformWidth >= 0) // maybe uncheck packed class width + { + if(propPackedWidth < 0 || cls->mPackedUniformWidth != propPackedWidth + // object refs, because they require special treatment during parsing, + // cannot be packed + || + datatype == getPvdTypeForType() + // Likewise, string handles are special because the data needs to be sent *after* + // the + || + datatype == getPvdTypeForType() || packed == false) + cls->mPackedUniformWidth = -1; // invalid packed width. + } + } + return *mProperties.back(); + } + + PropDescImpl* findPropImpl(const NamespacedName& clsName, String prop) const + { + const TNameToPropMap::Entry* entry = mNameToProperties.find(ClassPropertyName(clsName, prop)); + if(entry) + return entry->second; + return NULL; + } + virtual Option findProperty(const NamespacedName& cls, String propName) const + { + PropDescImpl* prop(findPropImpl(cls, propName)); + if(prop) + return *prop; + return None(); + } + + virtual Option findProperty(int32_t clsId, String propName) const + { + ClassDescImpl* cls(getClassImpl(clsId)); + PX_ASSERT(cls); + if(!cls) + return None(); + PropDescImpl* prop(findPropImpl(cls->mName, propName)); + if(prop) + return *prop; + return None(); + } + + PropDescImpl* getPropertyImpl(int32_t propId) const + { + PX_ASSERT(propId >= 0); + if(propId < 0) + return NULL; + uint32_t val = uint32_t(propId); + if(val >= mProperties.size()) + { + PX_ASSERT(false); + return NULL; + } + return mProperties[val]; + } + + virtual Option getProperty(int32_t propId) const + { + PropDescImpl* impl(getPropertyImpl(propId)); + if(impl) + return *impl; + return None(); + } + + virtual void setNamedPropertyValues(DataRef values, int32_t propId) + { + PropDescImpl* impl(getPropertyImpl(propId)); + if(impl) + { + impl->mValueNames.resize(values.size()); + PVD_FOREACH(idx, values.size()) impl->mValueNames[idx] = values[idx]; + } + } + + virtual DataRef getNamedPropertyValues(int32_t propId) const + { + PropDescImpl* impl(getPropertyImpl(propId)); + if(impl) + { + return toDataRef(impl->mValueNames); + } + return DataRef(); + } + + virtual uint32_t getNbProperties(int32_t classId) const + { + uint32_t retval = 0; + for(ClassDescImpl* impl(getClassImpl(classId)); impl; impl = getClassImpl(impl->mBaseClass)) + { + retval += impl->mPropImps.size(); + if(impl->mBaseClass < 0) + break; + } + return retval; + } + + // Properties need to be returned in base class order, so this requires a recursive function. + uint32_t getPropertiesImpl(int32_t classId, PropertyDescription*& outBuffer, uint32_t& numItems, + uint32_t& startIdx) const + { + ClassDescImpl* impl = getClassImpl(classId); + if(impl) + { + uint32_t retval = 0; + if(impl->mBaseClass >= 0) + retval = getPropertiesImpl(impl->mBaseClass, outBuffer, numItems, startIdx); + + uint32_t localStart = PxMin(impl->mPropImps.size(), startIdx); + uint32_t localNumItems = PxMin(numItems, impl->mPropImps.size() - localStart); + PVD_FOREACH(idx, localNumItems) + { + outBuffer[idx] = *impl->mPropImps[localStart + idx]; + } + + startIdx -= localStart; + numItems -= localNumItems; + outBuffer += localNumItems; + return retval + localNumItems; + } + return 0; + } + + virtual uint32_t getProperties(int32_t classId, PropertyDescription* outBuffer, uint32_t numItems, + uint32_t startIdx) const + { + return getPropertiesImpl(classId, outBuffer, numItems, startIdx); + } + + virtual MarshalQueryResult checkMarshalling(int32_t srcClsId, int32_t dstClsId) const + { + Option propTypeOpt(getClass(dstClsId)); + if(propTypeOpt.hasValue() == false) + { + PX_ASSERT(false); + return MarshalQueryResult(); + } + const ClassDescription& propType(propTypeOpt); + + Option incomingTypeOpt(getClass(srcClsId)); + if(incomingTypeOpt.hasValue() == false) + { + PX_ASSERT(false); + return MarshalQueryResult(); + } + const ClassDescription& incomingType(incomingTypeOpt); + // Can only marshal simple things at this point in time. + bool needsMarshalling = false; + bool canMarshal = false; + TSingleMarshaller single = NULL; + TBlockMarshaller block = NULL; + if(incomingType.mClassId != propType.mClassId) + { + // Check that marshalling is even possible. + if((incomingType.mPackedUniformWidth >= 0 && propType.mPackedUniformWidth >= 0) == false) + { + PX_ASSERT(false); + return MarshalQueryResult(); + } + + int32_t srcType = incomingType.mPackedClassType; + int32_t dstType = propType.mPackedClassType; + + int32_t srcWidth = incomingType.mPackedUniformWidth; + int32_t dstWidth = propType.mPackedUniformWidth; + canMarshal = getMarshalOperators(single, block, srcType, dstType); + if(srcWidth == dstWidth) + needsMarshalling = canMarshal; // If the types are the same width, we assume we can convert between some + // of them seamlessly (uint16_t, int16_t) + else + { + needsMarshalling = true; + // If we can't marshall and we have to then we can't set the property value. + // This indicates that the src and dest are different properties and we don't + // know how to convert between them. + if(!canMarshal) + { + PX_ASSERT(false); + return MarshalQueryResult(); + } + } + } + return MarshalQueryResult(srcClsId, dstClsId, canMarshal, needsMarshalling, block); + } + + PropertyMessageDescriptionImpl* findPropertyMessageImpl(const NamespacedName& messageName) const + { + const TNameToPropertyMessageMap::Entry* entry = mPropertyMessageMap.find(messageName); + if(entry) + return entry->second; + return NULL; + } + + PropertyMessageDescriptionImpl* getPropertyMessageImpl(int32_t msg) const + { + int32_t msgCount = int32_t(mPropertyMessages.size()); + if(msg >= 0 && msg < msgCount) + return mPropertyMessages[uint32_t(msg)]; + return NULL; + } + + virtual Option createPropertyMessage(const NamespacedName& clsName, + const NamespacedName& messageName, + DataRef entries, + uint32_t messageSize) + { + PropertyMessageDescriptionImpl* existing(findPropertyMessageImpl(messageName)); + if(existing) + { + PX_ASSERT(false); + return None(); + } + ClassDescImpl* cls = findClassImpl(clsName); + PX_ASSERT(cls); + if(!cls) + return None(); + int32_t msgId = int32_t(mPropertyMessages.size()); + PropertyMessageDescriptionImpl* newMessage = PVD_NEW(PropertyMessageDescriptionImpl)( + PropertyMessageDescription(mStringTable->registerName(clsName), cls->mClassId, + mStringTable->registerName(messageName), msgId, messageSize)); + uint32_t calculatedSize = 0; + PVD_FOREACH(idx, entries.size()) + { + PropertyMessageArg entry(entries[idx]); + ClassDescImpl* dtypeCls = findClassImpl(entry.mDatatypeName); + if(dtypeCls == NULL) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + ClassDescriptionSizeInfo dtypeInfo(dtypeCls->get32BitSizeInfo()); + uint32_t incomingSize = dtypeInfo.mByteSize; + if(entry.mByteSize < incomingSize) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + + calculatedSize = PxMax(calculatedSize, entry.mMessageOffset + entry.mByteSize); + if(calculatedSize > messageSize) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + + Option propName(findProperty(cls->mClassId, entry.mPropertyName)); + if(propName.hasValue() == false) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + + Option propCls(getClass(propName.getValue().mDatatype)); + if(propCls.hasValue() == false) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + + PropertyMessageEntryImpl newEntry(PropertyMessageEntry( + propName, dtypeCls->mName, dtypeCls->mClassId, entry.mMessageOffset, incomingSize, dtypeInfo.mByteSize)); + newMessage->addEntry(newEntry); + + if(newEntry.mDatatypeId == getPvdTypeForType()) + newMessage->mStringOffsetArray.pushBack(entry.mMessageOffset); + + // property messages cannot be marshalled at this time. + if(newEntry.mDatatypeId != getPvdTypeForType() && newEntry.mDatatypeId != getPvdTypeForType()) + { + MarshalQueryResult marshalInfo = checkMarshalling(newEntry.mDatatypeId, newEntry.mProperty.mDatatype); + if(marshalInfo.needsMarshalling) + { + PX_ASSERT(false); + goto DestroyNewMessage; + } + } + } + + if(newMessage) + { + newMessage->mStringOffsets = + DataRef(newMessage->mStringOffsetArray.begin(), newMessage->mStringOffsetArray.end()); + mPropertyMessages.pushBack(newMessage); + mPropertyMessageMap.insert(messageName, newMessage); + return *newMessage; + } + + DestroyNewMessage: + if(newMessage) + PVD_DELETE(newMessage); + + return None(); + } + virtual Option findPropertyMessage(const NamespacedName& msgName) const + { + PropertyMessageDescriptionImpl* desc(findPropertyMessageImpl(msgName)); + if(desc) + return *desc; + return None(); + } + + virtual Option getPropertyMessage(int32_t msgId) const + { + PropertyMessageDescriptionImpl* desc(getPropertyMessageImpl(msgId)); + if(desc) + return *desc; + return None(); + } + + virtual uint32_t getNbPropertyMessages() const + { + return mPropertyMessages.size(); + } + + virtual uint32_t getPropertyMessages(PropertyMessageDescription* msgBuf, uint32_t bufLen, uint32_t startIdx = 0) const + { + startIdx = PxMin(startIdx, getNbPropertyMessages()); + bufLen = PxMin(bufLen, getNbPropertyMessages() - startIdx); + PVD_FOREACH(idx, bufLen) msgBuf[idx] = *mPropertyMessages[idx + startIdx]; + return bufLen; + } + + struct MetaDataWriter + { + const PvdObjectModelMetaDataImpl& mMetaData; + PvdOutputStream& mStream; + MetaDataWriter(const PvdObjectModelMetaDataImpl& meta, PvdOutputStream& stream) + : mMetaData(meta), mStream(stream) + { + } + + void streamify(NamespacedName& type) + { + mStream << mMetaData.mStringTable->strToHandle(type.mNamespace); + mStream << mMetaData.mStringTable->strToHandle(type.mName); + } + void streamify(String& type) + { + mStream << mMetaData.mStringTable->strToHandle(type); + } + void streamify(int32_t& type) + { + mStream << type; + } + void streamify(uint32_t& type) + { + mStream << type; + } + void streamify(uint8_t type) + { + mStream << type; + } + void streamify(bool type) + { + streamify( uint8_t(type)); + } + void streamify(PropertyType::Enum type) + { + uint32_t val = static_cast(type); + mStream << val; + } + void streamify(NamedValue& type) + { + streamify(type.mValue); + streamify(type.mName); + } + void streamifyLinks(PropDescImpl* prop) + { + streamify(prop->mPropertyId); + } + void streamify(PropertyDescription& prop) + { + streamify(prop.mPropertyId); + } + void streamify(PropertyMessageEntryImpl& prop) + { + prop.serialize(*this); + } + void streamify(PtrOffset& off) + { + uint32_t type = off.mOffsetType; + mStream << type; + mStream << off.mOffset; + } + template + void streamify(TDataType* type) + { + int32_t existMarker = type ? 1 : 0; + mStream << existMarker; + if(type) + type->serialize(*this); + } + template + void streamify(const Array& type) + { + mStream << static_cast(type.size()); + PVD_FOREACH(idx, type.size()) streamify(const_cast(type[idx])); + } + template + void streamifyLinks(const Array& type) + { + mStream << static_cast(type.size()); + PVD_FOREACH(idx, type.size()) streamifyLinks(const_cast(type[idx])); + } + + private: + MetaDataWriter& operator=(const MetaDataWriter&); + }; + + template + struct MetaDataReader + { + PvdObjectModelMetaDataImpl& mMetaData; + TStreamType& mStream; + MetaDataReader(PvdObjectModelMetaDataImpl& meta, TStreamType& stream) : mMetaData(meta), mStream(stream) + { + } + + void streamify(NamespacedName& type) + { + streamify(type.mNamespace); + streamify(type.mName); + } + + void streamify(String& type) + { + uint32_t handle; + mStream >> handle; + type = mMetaData.mStringTable->handleToStr(handle); + } + void streamify(int32_t& type) + { + mStream >> type; + } + void streamify(uint32_t& type) + { + mStream >> type; + } + void streamify(bool& type) + { + uint8_t data; + mStream >> data; + type = data ? true : false; + } + + void streamify(PropertyType::Enum& type) + { + uint32_t val; + mStream >> val; + type = static_cast(val); + } + void streamify(NamedValue& type) + { + streamify(type.mValue); + streamify(type.mName); + } + void streamify(PropertyMessageEntryImpl& type) + { + type.serialize(*this); + } + void streamify(PtrOffset& off) + { + uint32_t type; + mStream >> type; + mStream >> off.mOffset; + off.mOffsetType = static_cast(type); + } + void streamifyLinks(PropDescImpl*& prop) + { + int32_t propId; + streamify(propId); + prop = mMetaData.getPropertyImpl(propId); + } + void streamify(PropertyDescription& prop) + { + streamify(prop.mPropertyId); + prop = mMetaData.getProperty(prop.mPropertyId); + } + template + void streamify(TDataType*& type) + { + uint32_t existMarker; + mStream >> existMarker; + if(existMarker) + { + TDataType* newType = PVD_NEW(TDataType)(); + newType->serialize(*this); + type = newType; + } + else + type = NULL; + } + template + void streamify(Array& type) + { + uint32_t typeSize; + mStream >> typeSize; + type.resize(typeSize); + PVD_FOREACH(idx, type.size()) streamify(type[idx]); + } + template + void streamifyLinks(Array& type) + { + uint32_t typeSize; + mStream >> typeSize; + type.resize(typeSize); + PVD_FOREACH(idx, type.size()) streamifyLinks(type[idx]); + } + + private: + MetaDataReader& operator=(const MetaDataReader&); + }; + + virtual void write(PvdOutputStream& stream) const + { + stream << getCurrentPvdObjectModelVersion(); + stream << mNextClassId; + mStringTable->write(stream); + MetaDataWriter writer(*this, stream); + writer.streamify(mProperties); + writer.streamify(mClasses); + writer.streamify(mPropertyMessages); + } + + template + void read(TReaderType& stream) + { + uint32_t version; + stream >> version; + stream >> mNextClassId; + mStringTable->read(stream); + MetaDataReader reader(*this, stream); + reader.streamify(mProperties); + reader.streamify(mClasses); + reader.streamify(mPropertyMessages); + + mNameToClasses.clear(); + mNameToProperties.clear(); + mPropertyMessageMap.clear(); + PVD_FOREACH(i, mClasses.size()) + { + ClassDescImpl* cls(mClasses[i]); + if(cls == NULL) + continue; + mNameToClasses.insert(cls->mName, mClasses[i]); + uint32_t propCount = getNbProperties(cls->mClassId); + PropertyDescription descs[16]; + uint32_t offset = 0; + for(uint32_t idx = 0; idx < propCount; idx = offset) + { + uint32_t numProps = getProperties(cls->mClassId, descs, 16, offset); + offset += numProps; + for(uint32_t propIdx = 0; propIdx < numProps; ++propIdx) + { + PropDescImpl* prop = getPropertyImpl(descs[propIdx].mPropertyId); + if(prop) + mNameToProperties.insert(ClassPropertyName(cls->mName, prop->mName), prop); + } + } + } + PVD_FOREACH(idx, mPropertyMessages.size()) + mPropertyMessageMap.insert(mPropertyMessages[idx]->mMessageName, mPropertyMessages[idx]); + } + + virtual StringTable& getStringTable() const + { + return *mStringTable; + } + virtual void addRef() + { + ++mRefCount; + } + virtual void release() + { + if(mRefCount) + --mRefCount; + if(!mRefCount) + PVD_DELETE(this); + } +}; +} + +uint32_t PvdObjectModelMetaData::getCurrentPvdObjectModelVersion() +{ + return 1; +} + +PvdObjectModelMetaData& PvdObjectModelMetaData::create() +{ + PvdObjectModelMetaDataImpl& retval(*PVD_NEW(PvdObjectModelMetaDataImpl)()); + retval.initialize(); + return retval; +} + +PvdObjectModelMetaData& PvdObjectModelMetaData::create(PvdInputStream& stream) +{ + PvdObjectModelMetaDataImpl& retval(*PVD_NEW(PvdObjectModelMetaDataImpl)()); + retval.read(stream); + return retval; +} + +StringTable& StringTable::create() +{ + return *PVD_NEW(StringTableImpl)(); +} diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h new file mode 100644 index 000000000..eb2baea83 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h @@ -0,0 +1,481 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#ifndef PXPVDSDK_PXPVDOBJECTMODELMETADATA_H +#define PXPVDSDK_PXPVDOBJECTMODELMETADATA_H + +#include "foundation/PxAssert.h" +#include "PxPvdObjectModelBaseTypes.h" +#include "PxPvdBits.h" + +namespace physx +{ +namespace pvdsdk +{ + +class PvdInputStream; +class PvdOutputStream; + +struct PropertyDescription +{ + NamespacedName mOwnerClassName; + int32_t mOwnerClassId; + String mName; + String mSemantic; + // The datatype this property corresponds to. + int32_t mDatatype; + // The name of the datatype + NamespacedName mDatatypeName; + // Scalar or array. + PropertyType::Enum mPropertyType; + // No other property under any class has this id, it is DB-unique. + int32_t mPropertyId; + // Offset in bytes into the object's data section where this property starts. + uint32_t m32BitOffset; + // Offset in bytes into the object's data section where this property starts. + uint32_t m64BitOffset; + + PropertyDescription(const NamespacedName& clsName, int32_t classId, String name, String semantic, int32_t datatype, + const NamespacedName& datatypeName, PropertyType::Enum propType, int32_t propId, + uint32_t offset32, uint32_t offset64) + : mOwnerClassName(clsName) + , mOwnerClassId(classId) + , mName(name) + , mSemantic(semantic) + , mDatatype(datatype) + , mDatatypeName(datatypeName) + , mPropertyType(propType) + , mPropertyId(propId) + , m32BitOffset(offset32) + , m64BitOffset(offset64) + { + } + PropertyDescription() + : mOwnerClassId(-1) + , mName("") + , mSemantic("") + , mDatatype(-1) + , mPropertyType(PropertyType::Unknown) + , mPropertyId(-1) + , m32BitOffset(0) + , m64BitOffset(0) + + { + } + + virtual ~PropertyDescription() + { + } +}; + +struct PtrOffsetType +{ + enum Enum + { + UnknownOffset, + VoidPtrOffset, + StringOffset + }; +}; + +struct PtrOffset +{ + PtrOffsetType::Enum mOffsetType; + uint32_t mOffset; + PtrOffset(PtrOffsetType::Enum type, uint32_t offset) : mOffsetType(type), mOffset(offset) + { + } + PtrOffset() : mOffsetType(PtrOffsetType::UnknownOffset), mOffset(0) + { + } +}; + +inline uint32_t align(uint32_t offset, uint32_t alignment) +{ + uint32_t startOffset = offset; + uint32_t alignmentMask = ~(alignment - 1); + offset = (offset + alignment - 1) & alignmentMask; + PX_ASSERT(offset >= startOffset && (offset % alignment) == 0); + (void)startOffset; + return offset; +} + +struct ClassDescriptionSizeInfo +{ + // The size of the data section of this object, padded to alignment. + uint32_t mByteSize; + // The last data member goes to here. + uint32_t mDataByteSize; + // Alignment in bytes of the data section of this object. + uint32_t mAlignment; + // the offsets of string handles in the binary value of this class + DataRef mPtrOffsets; + ClassDescriptionSizeInfo() : mByteSize(0), mDataByteSize(0), mAlignment(0) + { + } +}; + +struct ClassDescription +{ + NamespacedName mName; + // No other class has this id, it is DB-unique + int32_t mClassId; + // Only single derivation supported. + int32_t mBaseClass; + // If this class has properties that are of uniform type, then we note that. + // This means that when deserialization an array of these objects we can just use + // single function to endian convert the entire mess at once. + int32_t mPackedUniformWidth; + // If this class is composed uniformly of members of a given type + // Or all of its properties are composed uniformly of members of + // a give ntype, then this class's packed type is that type. + // PxTransform's packed type would be float. + int32_t mPackedClassType; + // 0: 32Bit 1: 64Bit + ClassDescriptionSizeInfo mSizeInfo[2]; + // No further property additions allowed. + bool mLocked; + // True when this datatype has an array on it that needs to be + // separately deleted. + bool mRequiresDestruction; + + ClassDescription(NamespacedName name, int32_t id) + : mName(name) + , mClassId(id) + , mBaseClass(-1) + , mPackedUniformWidth(-1) + , mPackedClassType(-1) + , mLocked(false) + , mRequiresDestruction(false) + { + } + ClassDescription() + : mClassId(-1), mBaseClass(-1), mPackedUniformWidth(-1), mPackedClassType(-1), mLocked(false), mRequiresDestruction(false) + { + } + virtual ~ClassDescription() + { + } + + ClassDescriptionSizeInfo& get32BitSizeInfo() + { + return mSizeInfo[0]; + } + ClassDescriptionSizeInfo& get64BitSizeInfo() + { + return mSizeInfo[1]; + } + uint32_t& get32BitSize() + { + return get32BitSizeInfo().mByteSize; + } + uint32_t& get64BitSize() + { + return get64BitSizeInfo().mByteSize; + } + + uint32_t get32BitSize() const + { + return mSizeInfo[0].mByteSize; + } + const ClassDescriptionSizeInfo& getNativeSizeInfo() const + { + return mSizeInfo[(sizeof(void*) >> 2) - 1]; + } + uint32_t getNativeSize() const + { + return getNativeSizeInfo().mByteSize; + } +}; + +struct MarshalQueryResult +{ + int32_t srcType; + int32_t dstType; + // If canMarshal != needsMarshalling we have a problem. + bool canMarshal; + bool needsMarshalling; + // Non null if marshalling is possible. + TBlockMarshaller marshaller; + MarshalQueryResult(int32_t _srcType = -1, int32_t _dstType = -1, bool _canMarshal = false, bool _needs = false, + TBlockMarshaller _m = NULL) + : srcType(_srcType), dstType(_dstType), canMarshal(_canMarshal), needsMarshalling(_needs), marshaller(_m) + { + } +}; + +struct PropertyMessageEntry +{ + PropertyDescription mProperty; + NamespacedName mDatatypeName; + // datatype of the data in the message. + int32_t mDatatypeId; + // where in the message this property starts. + uint32_t mMessageOffset; + // size of this entry object + uint32_t mByteSize; + + // If the chain of properties doesn't have any array properties this indicates the + uint32_t mDestByteSize; + + PropertyMessageEntry(PropertyDescription propName, NamespacedName dtypeName, int32_t dtype, uint32_t messageOff, + uint32_t byteSize, uint32_t destByteSize) + : mProperty(propName) + , mDatatypeName(dtypeName) + , mDatatypeId(dtype) + , mMessageOffset(messageOff) + , mByteSize(byteSize) + , mDestByteSize(destByteSize) + { + } + PropertyMessageEntry() : mDatatypeId(-1), mMessageOffset(0), mByteSize(0), mDestByteSize(0) + { + } +}; + +// Create a struct that defines a subset of the properties on an object. +struct PropertyMessageDescription +{ + NamespacedName mClassName; + // No other class has this id, it is DB-unique + int32_t mClassId; + NamespacedName mMessageName; + int32_t mMessageId; + DataRef mProperties; + uint32_t mMessageByteSize; + // Offsets into the property message where const char* items are. + DataRef mStringOffsets; + PropertyMessageDescription(const NamespacedName& nm, int32_t clsId, const NamespacedName& msgName, int32_t msgId, + uint32_t msgSize) + : mClassName(nm), mClassId(clsId), mMessageName(msgName), mMessageId(msgId), mMessageByteSize(msgSize) + { + } + PropertyMessageDescription() : mClassId(-1), mMessageId(-1), mMessageByteSize(0) + { + } + virtual ~PropertyMessageDescription() + { + } +}; + +class StringTable +{ + protected: + virtual ~StringTable() + { + } + + public: + virtual uint32_t getNbStrs() = 0; + virtual uint32_t getStrs(const char** outStrs, uint32_t bufLen, uint32_t startIdx = 0) = 0; + virtual const char* registerStr(const char* str, bool& outAdded) = 0; + const char* registerStr(const char* str) + { + bool ignored; + return registerStr(str, ignored); + } + virtual StringHandle strToHandle(const char* str) = 0; + virtual const char* handleToStr(uint32_t hdl) = 0; + virtual void release() = 0; + + static StringTable& create(); +}; + +struct None +{ +}; + +template +class Option +{ + T mValue; + bool mHasValue; + + public: + Option(const T& val) : mValue(val), mHasValue(true) + { + } + Option(None nothing = None()) : mHasValue(false) + { + (void)nothing; + } + Option(const Option& other) : mValue(other.mValue), mHasValue(other.mHasValue) + { + } + Option& operator=(const Option& other) + { + mValue = other.mValue; + mHasValue = other.mHasValue; + return *this; + } + bool hasValue() const + { + return mHasValue; + } + const T& getValue() const + { + PX_ASSERT(hasValue()); + return mValue; + } + T& getValue() + { + PX_ASSERT(hasValue()); + return mValue; + } + operator const T&() const + { + return getValue(); + } + operator T&() + { + return getValue(); + } + T* operator->() + { + return &getValue(); + } + const T* operator->() const + { + return &getValue(); + } +}; + +/** + * Create new classes and add properties to some existing ones. + * The default classes are created already, the simple types + * along with the basic math types. + * (uint8_t, int8_t, etc ) + * (PxVec3, PxQuat, PxTransform, PxMat33, PxMat34, PxMat44) + */ +class PvdObjectModelMetaData +{ + protected: + virtual ~PvdObjectModelMetaData() + { + } + + public: + virtual ClassDescription getOrCreateClass(const NamespacedName& nm) = 0; + // get or create parent, lock parent. deriveFrom getOrCreatechild. + virtual bool deriveClass(const NamespacedName& parent, const NamespacedName& child) = 0; + virtual Option findClass(const NamespacedName& nm) const = 0; + template + Option findClass() + { + return findClass(getPvdNamespacedNameForType()); + } + virtual Option getClass(int32_t classId) const = 0; + virtual ClassDescription* getClassPtr(int32_t classId) const = 0; + + virtual Option getParentClass(int32_t classId) const = 0; + bool isDerivedFrom(int32_t classId, int32_t parentClass) const + { + if(classId == parentClass) + return true; + ClassDescription* p = getClassPtr(getClassPtr(classId)->mBaseClass); + while(p != NULL) + { + if(p->mClassId == parentClass) + return true; + p = getClassPtr(p->mBaseClass); + } + return false; + } + + virtual void lockClass(int32_t classId) = 0; + + virtual uint32_t getNbClasses() const = 0; + virtual uint32_t getClasses(ClassDescription* outClasses, uint32_t requestCount, uint32_t startIndex = 0) const = 0; + + // Create a nested property. + // This way you can have obj.p.x without explicity defining the class p. + virtual Option createProperty(int32_t classId, String name, String semantic, int32_t datatype, + PropertyType::Enum propertyType = PropertyType::Scalar) = 0; + Option createProperty(NamespacedName clsId, String name, String semantic, NamespacedName dtype, + PropertyType::Enum propertyType = PropertyType::Scalar) + { + return createProperty(findClass(clsId)->mClassId, name, semantic, findClass(dtype)->mClassId, propertyType); + } + Option createProperty(NamespacedName clsId, String name, NamespacedName dtype, + PropertyType::Enum propertyType = PropertyType::Scalar) + { + return createProperty(findClass(clsId)->mClassId, name, "", findClass(dtype)->mClassId, propertyType); + } + Option createProperty(int32_t clsId, String name, int32_t dtype, + PropertyType::Enum propertyType = PropertyType::Scalar) + { + return createProperty(clsId, name, "", dtype, propertyType); + } + template + Option createProperty(int32_t clsId, String name, String semantic = "", + PropertyType::Enum propertyType = PropertyType::Scalar) + { + return createProperty(clsId, name, semantic, getPvdNamespacedNameForType(), propertyType); + } + virtual Option findProperty(const NamespacedName& cls, String prop) const = 0; + virtual Option findProperty(int32_t clsId, String prop) const = 0; + virtual Option getProperty(int32_t propId) const = 0; + virtual void setNamedPropertyValues(DataRef values, int32_t propId) = 0; + // for enumerations and flags. + virtual DataRef getNamedPropertyValues(int32_t propId) const = 0; + + virtual uint32_t getNbProperties(int32_t classId) const = 0; + virtual uint32_t getProperties(int32_t classId, PropertyDescription* outBuffer, uint32_t bufCount, + uint32_t startIdx = 0) const = 0; + + // Does one cls id differ marshalling to another and if so return the functions to do it. + virtual MarshalQueryResult checkMarshalling(int32_t srcClsId, int32_t dstClsId) const = 0; + + // messages and classes are stored in separate maps, so a property message can have the same name as a class. + virtual Option createPropertyMessage(const NamespacedName& cls, + const NamespacedName& msgName, + DataRef entries, + uint32_t messageSize) = 0; + virtual Option findPropertyMessage(const NamespacedName& msgName) const = 0; + virtual Option getPropertyMessage(int32_t msgId) const = 0; + + virtual uint32_t getNbPropertyMessages() const = 0; + virtual uint32_t getPropertyMessages(PropertyMessageDescription* msgBuf, uint32_t bufLen, + uint32_t startIdx = 0) const = 0; + + virtual StringTable& getStringTable() const = 0; + + virtual void write(PvdOutputStream& stream) const = 0; + void save(PvdOutputStream& stream) const + { + write(stream); + } + + virtual void addRef() = 0; + virtual void release() = 0; + + static uint32_t getCurrentPvdObjectModelVersion(); + static PvdObjectModelMetaData& create(); + static PvdObjectModelMetaData& create(PvdInputStream& stream); +}; +} +} +#endif // PXPVDSDK_PXPVDOBJECTMODELMETADATA_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp new file mode 100644 index 000000000..3bdfaaf7f --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp @@ -0,0 +1,80 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdObjectRegistrar.h" + +namespace physx +{ +namespace pvdsdk +{ + +bool ObjectRegistrar::addItem(const void* inItem) +{ + physx::shdfnd::Mutex::ScopedLock lock(mRefCountMapLock); + + if(mRefCountMap.find(inItem)) + { + uint32_t& counter = mRefCountMap[inItem]; + counter++; + return false; + } + else + { + mRefCountMap.insert(inItem, 1); + return true; + } +} + +bool ObjectRegistrar::decItem(const void* inItem) +{ + physx::shdfnd::Mutex::ScopedLock lock(mRefCountMapLock); + const physx::shdfnd::HashMap::Entry* entry = mRefCountMap.find(inItem); + if(entry) + { + uint32_t& retval(const_cast(entry->second)); + if(retval) + --retval; + uint32_t theValue = retval; + if(theValue == 0) + { + mRefCountMap.erase(inItem); + return true; + } + } + return false; +} + +void ObjectRegistrar::clear() +{ + physx::shdfnd::Mutex::ScopedLock lock(mRefCountMapLock); + mRefCountMap.clear(); +} + +} // pvdsdk +} // physx diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h new file mode 100644 index 000000000..5cf0e8549 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h @@ -0,0 +1,71 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDOBJECTREGISTRAR_H +#define PXPVDSDK_PXPVDOBJECTREGISTRAR_H + +/** \addtogroup pvd +@{ +*/ + +#include "PsHashMap.h" +#include "PsMutex.h" + +#if !PX_DOXYGEN +namespace physx +{ +namespace pvdsdk +{ +#endif +class ObjectRegistrar +{ + PX_NOCOPY(ObjectRegistrar) + public: + ObjectRegistrar() + { + } + virtual ~ObjectRegistrar() + { + } + + bool addItem(const void* inItem); + bool decItem(const void* inItem); + void clear(); + + private: + physx::shdfnd::HashMap mRefCountMap; + physx::shdfnd::Mutex mRefCountMapLock; +}; +#if !PX_DOXYGEN +} // pvdsdk +} // physx +#endif + +/** @} */ +#endif // PXPVDSDK_PXPVDOBJECTREGISTRAR_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h b/src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h new file mode 100644 index 000000000..555b64acd --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h @@ -0,0 +1,142 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDPROFILEZONE_H +#define PXPVDSDK_PXPVDPROFILEZONE_H + +#include "foundation/PxPreprocessor.h" + +#include "PxProfileEventBufferClientManager.h" +#include "PxProfileEventNames.h" +#include "PxProfileEventSender.h" + +namespace physx { + class PxAllocatorCallback; + + namespace profile { + + class PxProfileZoneManager; + + /** + \brief The profiling system was setup in the expectation that there would be several + systems that each had its own island of profile information. PhysX, client code, + and APEX would be the first examples of these. Each one of these islands is represented + by a profile zone. + + A profile zone combines a name, a place where all the events coming from its interface + can flushed, and a mapping from event number to full event name. + + It also provides a top level filtering service where profile events + can be filtered by event id. + + The profile zone implements a system where if there is no one + listening to events it doesn't provide a mechanism to send them. In this way + the event system is short circuited when there aren't any clients. + + All functions on this interface should be considered threadsafe. + + @see PxProfileZoneClientManager, PxProfileNameProvider, PxProfileEventSender, PxProfileEventFlusher + */ + class PxProfileZone : public PxProfileZoneClientManager + , public PxProfileNameProvider + , public PxProfileEventSender + , public PxProfileEventFlusher + { + protected: + virtual ~PxProfileZone(){} + public: + /** + \brief Get profile zone name. + \return Zone name. + */ + virtual const char* getName() = 0; + /** + \brief Release the profile zone. + */ + virtual void release() = 0; + + /** + \brief Set profile zone manager for the zone. + \param inMgr Profile zone manager. + */ + virtual void setProfileZoneManager(PxProfileZoneManager* inMgr) = 0; + /** + \brief Get profile zone manager for the zone. + \return Profile zone manager. + */ + virtual PxProfileZoneManager* getProfileZoneManager() = 0; + + /** + \brief Get or create a new event id for a given name. + If you pass in a previously defined event name (including one returned) + from the name provider) you will just get the same event id back. + \param inName Profile event name. + */ + virtual uint16_t getEventIdForName( const char* inName ) = 0; + + /** + \brief Specifies that it is a safe point to flush read-write name map into + read-only map. Make sure getEventIdForName is not called from a different thread. + */ + virtual void flushEventIdNameMap() = 0; + + /** + \brief Reserve a contiguous set of profile event ids for a set of names. + + This function does not do any meaningful error checking other than to ensure + that if it does generate new ids they are contiguous. If the first name is already + registered, that is the ID that will be returned regardless of what other + names are registered. Thus either use this function alone (without the above + function) or don't use it. + If you register "one","two","three" and the function returns an id of 4, then + "one" is mapped to 4, "two" is mapped to 5, and "three" is mapped to 6. + + \param inNames set of names to register. + \param inLen Length of the name list. + + \return The first id associated with the first name. The rest of the names + will be associated with monotonically incrementing uint16_t values from the first + id. + */ + virtual uint16_t getEventIdsForNames( const char** inNames, uint32_t inLen ) = 0; + + /** + \brief Create a new profile zone. + + \param inAllocator memory allocation is controlled through the foundation if one is passed in. + \param inSDKName Name of the profile zone; useful for clients to understand where events came from. + \param inNames Mapping from event id -> event name. + \param inEventBufferByteSize Size of the canonical event buffer. This does not need to be a large number + as profile events are fairly small individually. + \return a profile zone implementation. + */ + static PxProfileZone& createProfileZone(PxAllocatorCallback* inAllocator, const char* inSDKName, PxProfileNames inNames = PxProfileNames(), uint32_t inEventBufferByteSize = 0x10000 /*64k*/); + + }; +} } + +#endif // PXPVDSDK_PXPVDPROFILEZONE_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp new file mode 100644 index 000000000..ce22db789 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp @@ -0,0 +1,171 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxPvdImpl.h" +#include "PxPvdProfileZoneClient.h" +#include "PxPvdProfileZone.h" + +namespace physx +{ +namespace pvdsdk +{ +struct ProfileZoneClient : public profile::PxProfileZoneClient, public shdfnd::UserAllocated +{ + profile::PxProfileZone& mZone; + PvdDataStream& mStream; + + ProfileZoneClient(profile::PxProfileZone& zone, PvdDataStream& stream) : mZone(zone), mStream(stream) + { + } + + ~ProfileZoneClient() + { + mZone.removeClient(*this); + } + + virtual void createInstance() + { + mStream.addProfileZone(&mZone, mZone.getName()); + mStream.createInstance(&mZone); + mZone.addClient(*this); + profile::PxProfileNames names(mZone.getProfileNames()); + PVD_FOREACH(idx, names.eventCount) + { + handleEventAdded(names.events[idx]); + } + } + + virtual void handleEventAdded(const profile::PxProfileEventName& inName) + { + mStream.addProfileZoneEvent(&mZone, inName.name, inName.eventId.eventId, inName.eventId.compileTimeEnabled); + } + + virtual void handleBufferFlush(const uint8_t* inData, uint32_t inLength) + { + mStream.setPropertyValue(&mZone, "events", inData, inLength); + } + + virtual void handleClientRemoved() + { + mStream.destroyInstance(&mZone); + } + + private: + ProfileZoneClient& operator=(const ProfileZoneClient&); +}; +} +} + +using namespace physx; +using namespace pvdsdk; + +PvdProfileZoneClient::PvdProfileZoneClient(PvdImpl& pvd) : mSDKPvd(pvd), mPvdDataStream(NULL), mIsConnected(false) +{ +} + +PvdProfileZoneClient::~PvdProfileZoneClient() +{ + mSDKPvd.removeClient(this); + // all zones should removed + PX_ASSERT(mProfileZoneClients.size() == 0); +} + +PvdDataStream* PvdProfileZoneClient::getDataStream() +{ + return mPvdDataStream; +} + +PvdUserRenderer* PvdProfileZoneClient::getUserRender() +{ + PX_ASSERT(0); + return NULL; +} + +void PvdProfileZoneClient::setObjectRegistrar(ObjectRegistrar*) +{ +} + +bool PvdProfileZoneClient::isConnected() const +{ + return mIsConnected; +} + +void PvdProfileZoneClient::onPvdConnected() +{ + if(mIsConnected) + return; + mIsConnected = true; + + mPvdDataStream = PvdDataStream::create(&mSDKPvd); + +} + +void PvdProfileZoneClient::onPvdDisconnected() +{ + if(!mIsConnected) + return; + + mIsConnected = false; + flush(); + + mPvdDataStream->release(); + mPvdDataStream = NULL; +} + +void PvdProfileZoneClient::flush() +{ + PVD_FOREACH(idx, mProfileZoneClients.size()) + mProfileZoneClients[idx]->mZone.flushProfileEvents(); +} + +void PvdProfileZoneClient::onZoneAdded(profile::PxProfileZone& zone) +{ + PX_ASSERT(mIsConnected); + ProfileZoneClient* client = PVD_NEW(ProfileZoneClient)(zone, *mPvdDataStream); + mMutex.lock(); + client->createInstance(); + mProfileZoneClients.pushBack(client); + mMutex.unlock(); +} + +void PvdProfileZoneClient::onZoneRemoved(profile::PxProfileZone& zone) +{ + for(uint32_t i = 0; i < mProfileZoneClients.size(); i++) + { + if(&zone == &mProfileZoneClients[i]->mZone) + { + mMutex.lock(); + ProfileZoneClient* client = mProfileZoneClients[i]; + mProfileZoneClients.replaceWithLast(i); + PVD_DELETE(client); + mMutex.unlock(); + return; + } + } +} diff --git a/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h new file mode 100644 index 000000000..4438b8101 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXPVDSDK_PXPVDPROFILEZONECLIENT_H +#define PXPVDSDK_PXPVDPROFILEZONECLIENT_H +#include "PxPvdClient.h" +#include "PsHashMap.h" +#include "PsMutex.h" +#include "PxProfileZoneManager.h" + +namespace physx +{ +namespace pvdsdk +{ +class PvdImpl; +class PvdDataStream; + +struct ProfileZoneClient; + +class PvdProfileZoneClient : public PvdClient, public profile::PxProfileZoneHandler, public shdfnd::UserAllocated +{ + PX_NOCOPY(PvdProfileZoneClient) + public: + PvdProfileZoneClient(PvdImpl& pvd); + virtual ~PvdProfileZoneClient(); + + bool isConnected() const; + void onPvdConnected(); + void onPvdDisconnected(); + void flush(); + + PvdDataStream* getDataStream(); + PvdUserRenderer* getUserRender(); + void setObjectRegistrar(ObjectRegistrar*); + + // PxProfileZoneHandler + void onZoneAdded(profile::PxProfileZone& inSDK); + void onZoneRemoved(profile::PxProfileZone& inSDK); + + private: + shdfnd::Mutex mMutex; // zoneAdded can called from different threads + PvdImpl& mSDKPvd; + PvdDataStream* mPvdDataStream; + physx::shdfnd::Array mProfileZoneClients; + bool mIsConnected; +}; + +} // namespace pvdsdk +} // namespace physx + +#endif // PXPVDSDK_PXPVDPROFILEZONECLIENT_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h new file mode 100644 index 000000000..70b858400 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h @@ -0,0 +1,385 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXPVDSDK_PXPVDUSERRENDERIMPL_H +#define PXPVDSDK_PXPVDUSERRENDERIMPL_H + +#include "PxPvdUserRenderer.h" + +namespace physx +{ +namespace pvdsdk +{ + +struct PvdUserRenderTypes +{ + enum Enum + { + Unknown = 0, +#define DECLARE_PVD_IMMEDIATE_RENDER_TYPE(type) type, +#define DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA(type) type +#include "PxPvdUserRenderTypes.h" +#undef DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA +#undef DECLARE_PVD_IMMEDIATE_RENDER_TYPE + }; +}; + +class RenderSerializer +{ + protected: + virtual ~RenderSerializer() + { + } + + public: + virtual void streamify(uint64_t& val) = 0; + virtual void streamify(float& val) = 0; + virtual void streamify(uint32_t& val) = 0; + virtual void streamify(uint8_t& val) = 0; + virtual void streamify(DataRef& val) = 0; + virtual void streamify(DataRef& val) = 0; + virtual void streamify(DataRef& val) = 0; + virtual void streamify(DataRef& val) = 0; + virtual void streamify(PvdDebugText& val) = 0; + virtual bool isGood() = 0; + virtual uint32_t hasData() = 0; + + void streamify(PvdUserRenderTypes::Enum& val) + { + uint8_t data = static_cast(val); + streamify(data); + val = static_cast(data); + } + void streamify(PxVec3& val) + { + streamify(val[0]); + streamify(val[1]); + streamify(val[2]); + } + + void streamify(PvdColor& val) + { + streamify(val.r); + streamify(val.g); + streamify(val.b); + streamify(val.a); + } + void streamify(PxTransform& val) + { + streamify(val.q.x); + streamify(val.q.y); + streamify(val.q.z); + streamify(val.q.w); + streamify(val.p.x); + streamify(val.p.y); + streamify(val.p.z); + } + void streamify(bool& val) + { + uint8_t tempVal = uint8_t(val ? 1 : 0); + streamify(tempVal); + val = tempVal ? true : false; + } +}; + +template +struct BulkRenderEvent +{ + DataRef mData; + BulkRenderEvent(const TBulkRenderType* data, uint32_t count) : mData(data, count) + { + } + BulkRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(mData); + } +}; +struct SetInstanceIdRenderEvent +{ + uint64_t mInstanceId; + SetInstanceIdRenderEvent(uint64_t iid) : mInstanceId(iid) + { + } + SetInstanceIdRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(mInstanceId); + } +}; +struct PointsRenderEvent : BulkRenderEvent +{ + PointsRenderEvent(const PvdDebugPoint* data, uint32_t count) : BulkRenderEvent(data, count) + { + } + PointsRenderEvent() + { + } +}; +struct LinesRenderEvent : BulkRenderEvent +{ + LinesRenderEvent(const PvdDebugLine* data, uint32_t count) : BulkRenderEvent(data, count) + { + } + LinesRenderEvent() + { + } +}; +struct TrianglesRenderEvent : BulkRenderEvent +{ + TrianglesRenderEvent(const PvdDebugTriangle* data, uint32_t count) : BulkRenderEvent(data, count) + { + } + TrianglesRenderEvent() + { + } +}; +struct DebugRenderEvent +{ + DataRef mPointData; + DataRef mLineData; + DataRef mTriangleData; + DebugRenderEvent(const PvdDebugPoint* pointData, uint32_t pointCount, const PvdDebugLine* lineData, + uint32_t lineCount, const PvdDebugTriangle* triangleData, uint32_t triangleCount) + : mPointData(pointData, pointCount), mLineData(lineData, lineCount), mTriangleData(triangleData, triangleCount) + { + } + + DebugRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(mPointData); + serializer.streamify(mLineData); + serializer.streamify(mTriangleData); + } +}; + +struct TextRenderEvent +{ + PvdDebugText mText; + TextRenderEvent(const PvdDebugText& text) + { + mText.color = text.color; + mText.position = text.position; + mText.size = text.size; + mText.string = text.string; + } + TextRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(mText); + } +}; + +struct JointFramesRenderEvent +{ + PxTransform parent; + PxTransform child; + JointFramesRenderEvent(const PxTransform& p, const PxTransform& c) : parent(p), child(c) + { + } + JointFramesRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(parent); + serializer.streamify(child); + } +}; +struct LinearLimitRenderEvent +{ + PxTransform t0; + PxTransform t1; + float value; + bool active; + LinearLimitRenderEvent(const PxTransform& _t0, const PxTransform& _t1, float _value, bool _active) + : t0(_t0), t1(_t1), value(_value), active(_active) + { + } + LinearLimitRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(t0); + serializer.streamify(t1); + serializer.streamify(value); + serializer.streamify(active); + } +}; +struct AngularLimitRenderEvent +{ + PxTransform t0; + float lower; + float upper; + bool active; + AngularLimitRenderEvent(const PxTransform& _t0, float _lower, float _upper, bool _active) + : t0(_t0), lower(_lower), upper(_upper), active(_active) + { + } + AngularLimitRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(t0); + serializer.streamify(lower); + serializer.streamify(upper); + serializer.streamify(active); + } +}; +struct LimitConeRenderEvent +{ + PxTransform t; + float ySwing; + float zSwing; + bool active; + LimitConeRenderEvent(const PxTransform& _t, float _ySwing, float _zSwing, bool _active) + : t(_t), ySwing(_ySwing), zSwing(_zSwing), active(_active) + { + } + LimitConeRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(t); + serializer.streamify(ySwing); + serializer.streamify(zSwing); + serializer.streamify(active); + } +}; +struct DoubleConeRenderEvent +{ + PxTransform t; + float angle; + bool active; + DoubleConeRenderEvent(const PxTransform& _t, float _angle, bool _active) : t(_t), angle(_angle), active(_active) + { + } + DoubleConeRenderEvent() + { + } + void serialize(RenderSerializer& serializer) + { + serializer.streamify(t); + serializer.streamify(angle); + serializer.streamify(active); + } +}; + +template +struct RenderSerializerMap +{ + void serialize(RenderSerializer& s, TDataType& d) + { + d.serialize(s); + } +}; +template <> +struct RenderSerializerMap +{ + void serialize(RenderSerializer& s, uint8_t& d) + { + s.streamify(d); + } +}; + +template <> +struct RenderSerializerMap +{ + void serialize(RenderSerializer& s, PvdDebugPoint& d) + { + s.streamify(d.pos); + s.streamify(d.color); + } +}; + +template <> +struct RenderSerializerMap +{ + void serialize(RenderSerializer& s, PvdDebugLine& d) + { + s.streamify(d.pos0); + s.streamify(d.color0); + s.streamify(d.pos1); + s.streamify(d.color1); + } +}; + +template <> +struct RenderSerializerMap +{ + void serialize(RenderSerializer& s, PvdDebugTriangle& d) + { + s.streamify(d.pos0); + s.streamify(d.color0); + s.streamify(d.pos1); + s.streamify(d.color1); + s.streamify(d.pos2); + s.streamify(d.color2); + } +}; + +template +struct PvdTypeToRenderType +{ + bool compile_error; +}; + +#define DECLARE_PVD_IMMEDIATE_RENDER_TYPE(type) \ + template <> \ + struct PvdTypeToRenderType \ + { \ + enum Enum \ + { \ + EnumVal = PvdUserRenderTypes::type \ + }; \ + }; + +#include "PxPvdUserRenderTypes.h" +#undef DECLARE_PVD_IMMEDIATE_RENDER_TYPE + +template +PvdUserRenderTypes::Enum getPvdRenderTypeFromType() +{ + return static_cast(PvdTypeToRenderType::EnumVal); +} + +} +} + +#endif // PXPVDSDK_PXPVDUSERRENDERIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h new file mode 100644 index 000000000..f313e66f0 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +#define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON + +#ifndef DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA +#define DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA DECLARE_PVD_IMMEDIATE_RENDER_TYPE +#endif + +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(SetInstanceId) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(Points) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(Lines) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(Triangles) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(JointFrames) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(LinearLimit) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(AngularLimit) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(LimitCone) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(DoubleCone) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE(Text) +DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA(Debug) + +#undef DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA +#undef THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp new file mode 100644 index 000000000..98c12eba9 --- /dev/null +++ b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp @@ -0,0 +1,405 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "PxPvdUserRenderImpl.h" +#include "PxPvdInternalByteStreams.h" +#include "PxPvdBits.h" +#include + +using namespace physx; +using namespace physx::pvdsdk; + +namespace +{ + +template +struct RenderWriter : public RenderSerializer +{ + TStreamType& mStream; + RenderWriter(TStreamType& stream) : mStream(stream) + { + } + template + void write(const TDataType* val, uint32_t count) + { + uint32_t numBytes = count * sizeof(TDataType); + mStream.write(reinterpret_cast(val), numBytes); + } + template + void write(const TDataType& val) + { + write(&val, 1); + } + + template + void writeRef(DataRef& val) + { + uint32_t amount = val.size(); + write(amount); + if(amount) + write(val.begin(), amount); + } + + virtual void streamify(uint64_t& val) + { + write(val); + } + virtual void streamify(uint32_t& val) + { + write(val); + } + virtual void streamify(float& val) + { + write(val); + } + virtual void streamify(uint8_t& val) + { + write(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + + virtual void streamify(PvdDebugText& val) + { + write(val.color); + write(val.position); + write(val.size); + + uint32_t amount = static_cast(strlen(val.string)) + 1; + write(amount); + if(amount) + write(val.string, amount); + } + + virtual void streamify(DataRef& val) + { + writeRef(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + virtual void streamify(DataRef& val) + { + writeRef(val); + } + + virtual uint32_t hasData() + { + return false; + } + virtual bool isGood() + { + return true; + } + + private: + RenderWriter& operator=(const RenderWriter&); +}; + +struct UserRenderer : public PvdUserRenderer +{ + ForwardingMemoryBuffer mBuffer; + uint32_t mBufferCapacity; + RendererEventClient* mClient; + + UserRenderer(uint32_t bufferFullAmount) + : mBuffer("UserRenderBuffer"), mBufferCapacity(bufferFullAmount), mClient(NULL) + { + } + virtual ~UserRenderer() + { + } + virtual void release() + { + PVD_DELETE(this); + } + + template + void handleEvent(TEventType evt) + { + RenderWriter _writer(mBuffer); + RenderSerializer& writer(_writer); + + PvdUserRenderTypes::Enum evtType(getPvdRenderTypeFromType()); + writer.streamify(evtType); + evt.serialize(writer); + if(mBuffer.size() >= mBufferCapacity) + flushRenderEvents(); + } + virtual void setInstanceId(const void* iid) + { + handleEvent(SetInstanceIdRenderEvent(PVD_POINTER_TO_U64(iid))); + } + // Draw these points associated with this instance + virtual void drawPoints(const PvdDebugPoint* points, uint32_t count) + { + handleEvent(PointsRenderEvent(points, count)); + } + // Draw these lines associated with this instance + virtual void drawLines(const PvdDebugLine* lines, uint32_t count) + { + handleEvent(LinesRenderEvent(lines, count)); + } + // Draw these triangles associated with this instance + virtual void drawTriangles(const PvdDebugTriangle* triangles, uint32_t count) + { + handleEvent(TrianglesRenderEvent(triangles, count)); + } + + virtual void drawText(const PvdDebugText& text) + { + handleEvent(TextRenderEvent(text)); + } + + virtual void drawRenderbuffer(const PvdDebugPoint* pointData, uint32_t pointCount, const PvdDebugLine* lineData, + uint32_t lineCount, const PvdDebugTriangle* triangleData, uint32_t triangleCount) + { + handleEvent(DebugRenderEvent(pointData, pointCount, lineData, lineCount, triangleData, triangleCount)); + } + + // Constraint visualization routines + virtual void visualizeJointFrames(const PxTransform& parent, const PxTransform& child) + { + handleEvent(JointFramesRenderEvent(parent, child)); + } + virtual void visualizeLinearLimit(const PxTransform& t0, const PxTransform& t1, float value, bool active) + { + handleEvent(LinearLimitRenderEvent(t0, t1, value, active)); + } + virtual void visualizeAngularLimit(const PxTransform& t0, float lower, float upper, bool active) + { + handleEvent(AngularLimitRenderEvent(t0, lower, upper, active)); + } + virtual void visualizeLimitCone(const PxTransform& t, float tanQSwingY, float tanQSwingZ, bool active) + { + handleEvent(LimitConeRenderEvent(t, tanQSwingY, tanQSwingZ, active)); + } + virtual void visualizeDoubleCone(const PxTransform& t, float angle, bool active) + { + handleEvent(DoubleConeRenderEvent(t, angle, active)); + } + // Clear the immedate buffer. + virtual void flushRenderEvents() + { + if(mClient) + mClient->handleBufferFlush(mBuffer.begin(), mBuffer.size()); + mBuffer.clear(); + } + + virtual void setClient(RendererEventClient* client) + { + mClient = client; + } + + private: + UserRenderer& operator=(const UserRenderer&); +}; + +template +struct RenderReader : public RenderSerializer +{ + MemPvdInputStream mStream; + ForwardingMemoryBuffer& mBuffer; + + RenderReader(ForwardingMemoryBuffer& buf) : mBuffer(buf) + { + } + void setData(DataRef data) + { + mStream.setup(const_cast(data.begin()), const_cast(data.end())); + } + virtual void streamify(uint32_t& val) + { + mStream >> val; + } + virtual void streamify(uint64_t& val) + { + mStream >> val; + } + virtual void streamify(float& val) + { + mStream >> val; + } + virtual void streamify(uint8_t& val) + { + mStream >> val; + } + template + void readRef(DataRef& val) + { + uint32_t count; + mStream >> count; + uint32_t numBytes = sizeof(TDataType) * count; + + TDataType* dataPtr = reinterpret_cast(mBuffer.growBuf(numBytes)); + mStream.read(reinterpret_cast(dataPtr), numBytes); + val = DataRef(dataPtr, count); + } + + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(PvdDebugText& val) + { + mStream >> val.color; + mStream >> val.position; + mStream >> val.size; + + uint32_t len = 0; + mStream >> len; + + uint8_t* dataPtr = mBuffer.growBuf(len); + mStream.read(dataPtr, len); + val.string = reinterpret_cast(dataPtr); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual bool isGood() + { + return mStream.isGood(); + } + virtual uint32_t hasData() + { + return uint32_t(mStream.size() > 0); + } + + private: + RenderReader& operator=(const RenderReader&); +}; + +template <> +struct RenderReader : public RenderSerializer +{ + MemPvdInputStream mStream; + ForwardingMemoryBuffer& mBuffer; + RenderReader(ForwardingMemoryBuffer& buf) : mBuffer(buf) + { + } + void setData(DataRef data) + { + mStream.setup(const_cast(data.begin()), const_cast(data.end())); + } + + template + void read(TDataType& val) + { + mStream >> val; + swapBytes(val); + } + virtual void streamify(uint64_t& val) + { + read(val); + } + virtual void streamify(uint32_t& val) + { + read(val); + } + virtual void streamify(float& val) + { + read(val); + } + virtual void streamify(uint8_t& val) + { + read(val); + } + template + void readRef(DataRef& val) + { + uint32_t count; + mStream >> count; + swapBytes(count); + uint32_t numBytes = sizeof(TDataType) * count; + + TDataType* dataPtr = reinterpret_cast(mBuffer.growBuf(numBytes)); + PVD_FOREACH(idx, count) + RenderSerializerMap().serialize(*this, dataPtr[idx]); + val = DataRef(dataPtr, count); + } + + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual void streamify(PvdDebugText& val) + { + mStream >> val.color; + mStream >> val.position; + mStream >> val.size; + + uint32_t len = 0; + mStream >> len; + + uint8_t* dataPtr = mBuffer.growBuf(len); + mStream.read(dataPtr, len); + val.string = reinterpret_cast(dataPtr); + } + virtual void streamify(DataRef& val) + { + readRef(val); + } + virtual bool isGood() + { + return mStream.isGood(); + } + virtual uint32_t hasData() + { + return uint32_t(mStream.size() > 0); + } + + private: + RenderReader& operator=(const RenderReader&); +}; + +} + +PvdUserRenderer* PvdUserRenderer::create(uint32_t bufferSize) +{ + return PVD_NEW(UserRenderer)(bufferSize); +} + diff --git a/src/PhysX/physx/source/scenequery/include/SqPruner.h b/src/PhysX/physx/source/scenequery/include/SqPruner.h new file mode 100644 index 000000000..0a42b2e6b --- /dev/null +++ b/src/PhysX/physx/source/scenequery/include/SqPruner.h @@ -0,0 +1,397 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_PRUNER_H +#define SQ_PRUNER_H + +#include "foundation/PxBounds3.h" +#include "PxGeometry.h" +#include "PxQueryReport.h" +#include "PxQueryFiltering.h" +#include "PsUserAllocated.h" +#include "SqPruningStructure.h" +#include "GuSphere.h" +#include "GuBox.h" +#include "GuCapsule.h" + +namespace physx +{ + namespace Gu + { + class ShapeData; + class BVHStructure; + } +} + +namespace physx +{ + namespace Cm + { + class RenderOutput; + } + +namespace Sq +{ + +typedef PxU32 PrunerHandle; +typedef PxU32 PrunerCompoundId; + +static const PrunerHandle INVALID_PRUNERHANDLE = 0xFFffFFff; +static const PxReal SQ_PRUNER_INFLATION = 1.01f; // pruner test shape inflation (not narrow phase shape) + +struct PrunerPayload +{ + size_t data[2]; + + PX_FORCE_INLINE bool operator == (const PrunerPayload& other) const + { + return (data[0] == other.data[0]) && (data[1] == other.data[1]); + } +}; + +struct PrunerCallback +{ + virtual PxAgain invoke(PxReal& distance, const PrunerPayload& payload) = 0; + virtual ~PrunerCallback() {} +}; + +class Pruner : public Ps::UserAllocated +{ +public: + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * \brief Adds objects to the pruner. + * \param results [out] an array for resulting handles + * \param bounds [in] an array of bounds. These bounds are used as-is so they should be pre-inflated if inflation is needed. + * \param userData [in] an array of object data + * \param count [in] the number of objects in the arrays + * \param hasPruningStructure [in] if added objects have pruning structure. The structure will be merged later, adding the objects will not invalidate the pruner. + * + * \return true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE. + * + * Handles are usable as indices. Each handle is either be a recycled handle returned by the client via removeObjects(), + * or a fresh handle that is either zero, or one greater than the last fresh handle returned. + * + * Objects and bounds in the arrays have the same number of elements and ordering. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* userData, PxU32 count, bool hasPruningStructure) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Removes objects from the pruner. + * \param handles [in] the objects to remove + * \param count [in] the number of objects to remove + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void removeObjects(const PrunerHandle* handles, PxU32 count) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Updates objects after manually updating their bounds via "getPayload" calls. + * \param handles [in] the objects to update + * \param count [in] the number of objects to update + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Updates objects with new indexed bounds. + * \param handles [in] the objects to update + * \param indices [in] the indices of the bounds in the bounds array + * \param newBounds [in] updated bounds array + * \param count [in] the number of objects to update + * + * \warning THESE BOUNDS WILL BE INFLATED ON-THE-FLY. So this is inconsistent with the "addObjects" behavior. + * \warning The inflation value is hardcoded in Sq::inflateBounds(). + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Makes the queries consistent with previous changes. + * This function must be called before starting queries on an updated Pruner and assert otherwise. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void commit() = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Merges pruning structure to current pruner, parameters may differ for each pruner implementation + * \param mergeParams [in] Pruning structure to merge. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void merge(const void* mergeParams) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Query functions + * + * Note: return value may disappear if PrunerCallback contains the necessary information + * currently it is still used for the dynamic pruner internally (to decide if added objects must be queried) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const = 0; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const = 0; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Retrieve the object data associated with the handle + * + * \param handle The handle returned by addObjects() + * + * \return A reference to the object data + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const PrunerPayload& getPayload(PrunerHandle handle) const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Retrieve the object data associated with the handle, plus the destination address for its matrix. The user is then expected to write the new AABB there. + * + * \param handle [in] The handle returned by addObjects() + * \param bounds [out] destination address for this object's bounds + * + * \return A reference to the object data + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const PrunerPayload& getPayload(PrunerHandle handle, PxBounds3*& bounds) const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Preallocate space + * + * \param entries the number of entries to preallocate space for + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void preallocate(PxU32 entries) = 0; + + // shift the origin of the pruner objects + virtual void shiftOrigin(const PxVec3& shift) = 0; + + virtual ~Pruner() {} + + // additional 'internal' interface + virtual void visualize(Cm::RenderOutput&, PxU32) const {} +}; + +////////////////////////////////////////////////////////////////////////// +/** +* Pruner building accel structure over time base class +*/ +////////////////////////////////////////////////////////////////////////// +class IncrementalPruner: public Pruner +{ +public: + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * gets rid of internal accel struct. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void purge() = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * sets the rebuild hint rate used for step building the accel structure. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void setRebuildRateHint(PxU32 nbStepsForRebuild) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Steps the accel structure build. + * synchronousCall specifies if initialization can happen. It should not initialize build when called from a different thread + * returns true if finished + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool buildStep(bool synchronousCall = true) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Prepares new tree build + * returns true if new tree is needed + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool prepareBuild() = 0; +}; + + +////////////////////////////////////////////////////////////////////////// +// Compound flag to use for static/dynamic filtering atm +struct CompoundFlag +{ + enum Enum + { + STATIC_COMPOUND = (1<<0), + DYNAMIC_COMPOUND = (1<<1) + }; +}; + +PX_COMPILE_TIME_ASSERT(PxQueryFlag::eSTATIC & CompoundFlag::STATIC_COMPOUND); +PX_COMPILE_TIME_ASSERT(PxQueryFlag::eDYNAMIC & CompoundFlag::DYNAMIC_COMPOUND); + +////////////////////////////////////////////////////////////////////////// +/** +* Pruner holding compound objects +*/ +////////////////////////////////////////////////////////////////////////// +class CompoundPruner: public Ps::UserAllocated +{ +public: + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * \brief Adds compound to the pruner. + * \param results [out] an array for resulting handles + * \param bvhStructure [in] BVH structure holding bounds and BVH. + * \param compoundId [in] compound id + * \param transform [in] compound transform + * \param userData [in] an array of object data + * + * \return true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE. + * + * Handles are usable as indices. Each handle is either be a recycled handle returned by the client via removeObjects(), + * or a fresh handle that is either zero, or one greater than the last fresh handle returned. + * + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool addCompound(PrunerHandle* results, const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& transform, CompoundFlag::Enum flags, const PrunerPayload* userData) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Removes compound from the pruner. + * \param compoundId [in] compound to remove + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void removeCompound(PrunerCompoundId compoundId) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Updates compound object + * \param compoundId [in] compound to update + * \param transform [in] compound transformation + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void updateCompound(PrunerCompoundId compoundId, const PxTransform& transform) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Updates object after manually updating their bounds via "getPayload" calls. + * \param compoundId [in] compound that the object belongs to + * \param handle [in] the object to update + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void updateObjectAfterManualBoundsUpdates(PrunerCompoundId compoundId, const PrunerHandle handle) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Removes object from compound pruner. + * \param compoundId [in] compound that the object belongs to + * \param handle [in] the object to remove + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void removeObject(PrunerCompoundId compoundId, const PrunerHandle handle) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * \brief Adds object to the pruner. + * \param compoundId [in] compound that the object belongs to + * \param result [out] an array for resulting handles + * \param bounds [in] an array of bounds. These bounds are used as-is so they should be pre-inflated if inflation is needed. + * \param userData [in] an array of object data + * + * \return true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool addObject(PrunerCompoundId compoundId, PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload userData) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Query functions + * + * Note: return value may disappear if PrunerCallback contains the necessary information + * currently it is still used for the dynamic pruner internally (to decide if added objects must be queried) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&, PxQueryFlags flags) const = 0; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&, PxQueryFlags flags) const = 0; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&, PxQueryFlags flags) const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Retrieve the object data associated with the handle + * + * \param handle [in] The handle returned by addObjects() + * \param compoundId [in] The compound id + * + * \return A reference to the object data + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const PrunerPayload& getPayload(PrunerHandle handle, PrunerCompoundId compoundId) const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Retrieve the object data associated with the handle, plus the destination address for its matrix. The user is then expected to write the new AABB there. + * + * \param handle [in] The handle returned by addObjects() + * \param compoundId [in] The compound id + * \param bounds [out] destination address for this object's bounds + * + * \return A reference to the object data + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const PrunerPayload& getPayload(PrunerHandle handle, PrunerCompoundId compoundId, PxBounds3*& bounds) const = 0; + + + // shift the origin of the pruner objects + virtual void shiftOrigin(const PxVec3& shift) = 0; + + virtual ~CompoundPruner() {} + + // additional 'internal' interface + virtual void visualize(Cm::RenderOutput&, PxU32) const {} + +}; + + +////////////////////////////////////////////////////////////////////////// +/** +* Creates AABBPruner +*/ +////////////////////////////////////////////////////////////////////////// +IncrementalPruner* createAABBPruner(bool incrementalRebuild); + +} + +} + +#endif // SQ_PRUNER_H diff --git a/src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h b/src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h new file mode 100644 index 000000000..5e1a9e0a5 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h @@ -0,0 +1,62 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SQ_PRUNER_MERGE_DATA +#define SQ_PRUNER_MERGE_DATA +/** \addtogroup physics +@{ */ + +#include "CmPhysXCommon.h" + +namespace physx +{ + namespace Sq + { + class AABBTreeRuntimeNode; + + struct AABBPrunerMergeData + { + AABBPrunerMergeData(PxU32 nbNodes, const AABBTreeRuntimeNode* nodes, PxU32 nbObjects, const PxU32* indices) + : mNbNodes(nbNodes), mAABBTreeNodes(nodes), mNbObjects(nbObjects), mAABBTreeIndices(indices) + { + } + + PxU32 mNbNodes; // Nb nodes in AABB tree + const AABBTreeRuntimeNode* mAABBTreeNodes; // AABB tree runtime nodes + PxU32 mNbObjects; // Nb objects in AABB tree + const PxU32* mAABBTreeIndices; // AABB tree indices + }; + + } // namespace Sq + +} + +/** @} */ +#endif // SQ_PRUNING_STRUCTURE diff --git a/src/PhysX/physx/source/scenequery/include/SqPruningStructure.h b/src/PhysX/physx/source/scenequery/include/SqPruningStructure.h new file mode 100644 index 000000000..259a28daa --- /dev/null +++ b/src/PhysX/physx/source/scenequery/include/SqPruningStructure.h @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SQ_PRUNING_STRUCTURE +#define SQ_PRUNING_STRUCTURE +/** \addtogroup physics +@{ */ + +#include "CmPhysXCommon.h" + +#include "PxPruningStructure.h" + +#include "PsUserAllocated.h" + +namespace physx +{ + namespace Sq + { + class AABBTreeRuntimeNode; + + struct PruningIndex + { + enum Enum + { + eSTATIC = 0, + eDYNAMIC = 1, + + eCOUNT = 2 + }; + }; + + class PruningStructure : public PxPruningStructure, public Ps::UserAllocated + { + PX_NOCOPY(PruningStructure) + public: + // PX_SERIALIZATION + PruningStructure(PxBaseFlags baseFlags); + virtual void resolveReferences(PxDeserializationContext& ); + static PruningStructure* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void exportExtraData(PxSerializationContext&); + void importExtraData(PxDeserializationContext&); + virtual void requiresObjects(PxProcessPxBaseCallback&); + //~PX_SERIALIZATION + + // PX_PRUNING_STRUCTURE + virtual PxU32 getRigidActors(PxRigidActor** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const; + virtual PxU32 getNbRigidActors() const { return mNbActors; } + void release(); + // ~PX_PRUNING_STRUCTURE + + PruningStructure(); + ~PruningStructure(); + + bool build(PxRigidActor*const* actors, PxU32 nbActors); + + PX_FORCE_INLINE PxU32 getNbActors() const { return mNbActors; } + PX_FORCE_INLINE PxActor*const* getActors() const { return mActors; } + + PX_FORCE_INLINE AABBTreeRuntimeNode* getTreeNodes(PruningIndex::Enum currentTree) const { return mAABBTreeNodes[currentTree]; } + PX_FORCE_INLINE PxU32 getTreeNbNodes(PruningIndex::Enum currentTree) const { return mNbNodes[currentTree]; } + + PX_FORCE_INLINE PxU32* getTreeIndices(PruningIndex::Enum currentTree) const { return mAABBTreeIndices[currentTree]; } + PX_FORCE_INLINE PxU32 getNbObjects(PruningIndex::Enum currentTree) const { return mNbObjects[currentTree]; } + + PX_FORCE_INLINE bool isValid() const { return mValid; } + void invalidate(PxActor* actor); + + private: + PxU32 mNbNodes[2]; // Nb nodes in AABB tree + AABBTreeRuntimeNode* mAABBTreeNodes[2]; // AABB tree runtime nodes + PxU32 mNbObjects[2]; // Nb objects in AABB tree + PxU32* mAABBTreeIndices[2]; // AABB tree indices + PxU32 mNbActors; // Nb actors from which the pruner structure was build + PxActor** mActors; // actors used for pruner structure build, used later for serialization + bool mValid; // pruning structure validity + }; + } // namespace Sq + +} + +/** @} */ +#endif // SQ_PRUNING_STRUCTURE diff --git a/src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h b/src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h new file mode 100644 index 000000000..977eb4092 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h @@ -0,0 +1,221 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCENEQUERYMANAGER +#define PX_PHYSICS_SCENEQUERYMANAGER +/** \addtogroup physics +@{ */ + +#include "PxSceneDesc.h" +#include "CmBitMap.h" +#include "PsArray.h" +#include "SqPruner.h" +#include "PsMutex.h" +#include "PxActor.h" // needed for offset table +#include "ScScene.h" +// threading +#include "PsSync.h" + +namespace physx +{ +namespace Scb +{ + class Scene; + class Shape; + class Actor; +} + +namespace Gu +{ + class BVHStructure; +} + +namespace Sq +{ + typedef size_t PrunerData; + #define SQ_INVALID_PRUNER_DATA 0xffffffff + + struct PrunerPayload; + class Pruner; + class CompoundPruner; + + // PT: extended pruner structure. We might want to move the additional data to the pruner itself later. + struct PrunerExt + { + PrunerExt(); + ~PrunerExt(); + + void init(PxPruningStructureType::Enum type, PxU64 contextID, PxU32 sceneLimit); + void flushMemory(); + void preallocate(PxU32 nbShapes); + void flushShapes(PxU32 index); + + void addToDirtyList(PrunerHandle handle); + Ps::IntBool isDirty(PrunerHandle handle) const; + void removeFromDirtyList(PrunerHandle handle); + void growDirtyList(PrunerHandle handle); + + PX_FORCE_INLINE PxPruningStructureType::Enum type() const { return mPrunerType; } + PX_FORCE_INLINE const Pruner* pruner() const { return mPruner; } + PX_FORCE_INLINE Pruner* pruner() { return mPruner; } + PX_FORCE_INLINE PxU32 timestamp() const { return mTimestamp; } + PX_FORCE_INLINE void invalidateTimestamp() { mTimestamp++; } + + private: + Pruner* mPruner; + Cm::BitMap mDirtyMap; + Ps::Array mDirtyList; + PxPruningStructureType::Enum mPrunerType; + PxU32 mTimestamp; + + PX_NOCOPY(PrunerExt) + + friend class SceneQueryManager; + }; + + typedef Ps::Pair CompoundPair; + typedef Ps::CoalescedHashSet CompoundPrunerSet; + // AB: extended compoud pruner structure, buffers compound shape changes and flushes them. + struct CompoundPrunerExt + { + CompoundPrunerExt(); + ~CompoundPrunerExt(); + + void flushMemory(); + void preallocate(PxU32 nbShapes); + void flushShapes(); + + void addToDirtyList(PrunerCompoundId compoundId, PrunerHandle handle); + Ps::IntBool isDirty(PrunerCompoundId compoundId, PrunerHandle handle) const; + void removeFromDirtyList(PrunerCompoundId compoundId, PrunerHandle handle); + + PX_FORCE_INLINE const CompoundPruner* pruner() const { return mPruner; } + PX_FORCE_INLINE CompoundPruner* pruner() { return mPruner; } + + private: + CompoundPruner* mPruner; + CompoundPrunerSet mDirtyList; + + PX_NOCOPY(CompoundPrunerExt) + + friend class SceneQueryManager; + }; + + + struct DynamicBoundsSync : public Sc::SqBoundsSync + { + virtual void sync(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds, PxU32 count, const Cm::BitMap& dirtyShapeSimMap); + + Pruner* mPruner; + PxU32* mTimestamp; + }; + + class SceneQueryManager : public Ps::UserAllocated + { + PX_NOCOPY(SceneQueryManager) + public: + SceneQueryManager(Scb::Scene& scene, PxPruningStructureType::Enum staticStructure, + PxPruningStructureType::Enum dynamicStructure, PxU32 dynamicTreeRebuildRateHint, + const PxSceneLimits& limits); + ~SceneQueryManager(); + + PrunerData addPrunerShape(const Scb::Shape& scbShape, const Scb::Actor& scbActor, bool dynamic, PrunerCompoundId compoundId, const PxBounds3* bounds=NULL, bool hasPrunerStructure = false); + void removePrunerShape(PrunerCompoundId compoundId, PrunerData shapeData); + const PrunerPayload& getPayload(PrunerCompoundId compoundId, PrunerData shapeData) const; + + void addPruningStructure(const Sq::PruningStructure& ps); + void addCompoundShape(const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& compoundTransform, PrunerData* prunerData, const Scb::Shape** scbShapes, const Scb::Actor& scbActor); + + public: + PX_FORCE_INLINE Scb::Scene& getScene() const { return mScene; } + PX_FORCE_INLINE PxU32 getDynamicTreeRebuildRateHint() const { return mRebuildRateHint; } + + PX_FORCE_INLINE const PrunerExt& get(PruningIndex::Enum index) const { return mPrunerExt[index]; } + PX_FORCE_INLINE PrunerExt& get(PruningIndex::Enum index) { return mPrunerExt[index]; } + + PX_FORCE_INLINE const CompoundPrunerExt& getCompoundPruner() const { return mCompoundPrunerExt; } + + void preallocate(PxU32 staticShapes, PxU32 dynamicShapes); + void markForUpdate(PrunerCompoundId compoundId, PrunerData s); + void setDynamicTreeRebuildRateHint(PxU32 dynTreeRebuildRateHint); + + void flushUpdates(); + void forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure); + void sceneQueryBuildStep(PruningIndex::Enum index); + + void updateCompoundActors(Sc::BodyCore*const* bodies, PxU32 numBodies); + void updateCompoundActor(PrunerCompoundId compoundId, const PxTransform& compoundTransform, bool dynamic); + void removeCompoundActor(PrunerCompoundId compoundId, bool dynamic); + + DynamicBoundsSync& getDynamicBoundsSync() { return mDynamicBoundsSync; } + + bool prepareSceneQueriesUpdate(PruningIndex::Enum index); + + // Force a rebuild of the aabb/loose octree etc to allow raycasting on multiple threads. + void afterSync(PxSceneQueryUpdateMode::Enum updateMode); + void shiftOrigin(const PxVec3& shift); + + void flushMemory(); + private: + PrunerExt mPrunerExt[PruningIndex::eCOUNT]; + CompoundPrunerExt mCompoundPrunerExt; + + PxU32 mRebuildRateHint; + + Scb::Scene& mScene; + + // threading + shdfnd::Mutex mSceneQueryLock; // to make sure only one query updates the dirty pruner structure if multiple queries run in parallel + + DynamicBoundsSync mDynamicBoundsSync; + + volatile bool mPrunerNeedsUpdating; + + void flushShapes(); + }; + + /////////////////////////////////////////////////////////////////////////////// + + // PT: TODO: replace PrunerData with just PxU32 to save memory on Win64. Breaks binary compatibility though. + // PT: was previously called 'ActorShape' but does not contain an actor or shape pointer, contrary to the Np-level struct with the same name. + // PT: it only contains a pruner index (0 or 1) and a pruner handle. Hence the new name. + PX_FORCE_INLINE PrunerData createPrunerData(PxU32 index, PrunerHandle h) { return PrunerData((h << 1) | index); } + PX_FORCE_INLINE PxU32 getPrunerIndex(PrunerData data) { return PxU32(data & 1); } + PX_FORCE_INLINE PrunerHandle getPrunerHandle(PrunerData data) { return PrunerHandle(data >> 1); } + + /////////////////////////////////////////////////////////////////////////////// + + +} // namespace Sq + +} + +/** @} */ +#endif diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp new file mode 100644 index 000000000..f61611555 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp @@ -0,0 +1,848 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "common/PxProfileZone.h" +#include "PsIntrinsics.h" +#include "PsUserAllocated.h" +#include "PsBitUtils.h" +#include "PsFoundation.h" +#include "SqAABBPruner.h" +#include "SqAABBTree.h" +#include "SqPrunerMergeData.h" +#include "GuSphere.h" +#include "GuBox.h" +#include "GuCapsule.h" +#include "GuAABBTreeQuery.h" +#include "GuBounds.h" + +using namespace physx; +using namespace Gu; +using namespace Sq; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +IncrementalPruner* physx::Sq::createAABBPruner(bool incrementalRebuild) +{ + return PX_NEW(Sq::AABBPruner)(incrementalRebuild, 0); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: currently limited to 15 max +#define NB_OBJECTS_PER_NODE 4 + +AABBPruner::AABBPruner(bool incrementalRebuild, PxU64 contextID) : + mAABBTree (NULL), + mNewTree (NULL), + mCachedBoxes (NULL), + mNbCachedBoxes (0), + mNbCalls (0), + mTimeStamp (0), + mBucketPruner (&mPool), + mProgress (BUILD_NOT_STARTED), + mRebuildRateHint (100), + mAdaptiveRebuildTerm(0), + mIncrementalRebuild (incrementalRebuild), + mUncommittedChanges (false), + mNeedsNewTree (false), + mNewTreeFixups (PX_DEBUG_EXP("AABBPruner::mNewTreeFixups")), + mContextID (contextID) +{ +} + +AABBPruner::~AABBPruner() +{ + release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Add, Remove, Update methods + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool AABBPruner::addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* payload, PxU32 count, bool hasPruningStructure) +{ + PX_PROFILE_ZONE("SceneQuery.prunerAddObjects", mContextID); + + if(!count) + return true; + + // no need to do refitMarked for added objects since they are not in the tree + + // if we have provided pruning structure, we will merge it, the changes will be applied after the objects has been addded + if(!hasPruningStructure || !mAABBTree) + mUncommittedChanges = true; + + // PT: TODO: 'addObjects' for bucket pruner too. Not urgent since we always call the function with count=1 at the moment + const PxU32 valid = mPool.addObjects(results, bounds, payload, count); + + // Bucket pruner is only used while the dynamic pruner is rebuilding + // For the static pruner a full rebuild will happen in commit() every time we modify something, this is not true if + // pruning structure was provided. The objects tree will be merged directly into the static tree. No rebuild will be triggered. + if(mIncrementalRebuild && mAABBTree) + { + mNeedsNewTree = true; // each add forces a tree rebuild + + // if a pruner structure is provided, we dont move the new objects into bucket pruner + // the pruning structure will be merged into the bucket pruner + if(!hasPruningStructure) + { + for(PxU32 i=0;imarkNodeForRefit(treeNodeIndex); + else // otherwise it means it should be in the bucket pruner + { + bool found = mBucketPruner.updateObject(newBounds[poolIndex], payloads[poolIndex], poolIndex); + PX_UNUSED(found); PX_ASSERT(found); + } + + if(mProgress==BUILD_NEW_MAPPING || mProgress==BUILD_FULL_REFIT) + mToRefit.pushBack(poolIndex); + } + } +} + +void AABBPruner::updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count) +{ + PX_PROFILE_ZONE("SceneQuery.prunerUpdateObjects", mContextID); + + if(!count) + return; + + mUncommittedChanges = true; + + mPool.updateObjectsAndInflateBounds(handles, indices, newBounds, count); + + if(mIncrementalRebuild && mAABBTree) + { + mNeedsNewTree = true; // each update forces a tree rebuild + PrunerPayload* payloads = mPool.getObjects(); + for(PxU32 i=0; imarkNodeForRefit(treeNodeIndex); + else // otherwise it means it should be in the bucket pruner + { + // PT: TODO: is this line correct? +// bool found = mBucketPruner.updateObject(newBounds[indices[i]], mPool.getPayload(handles[i])); + PX_ASSERT(&payloads[poolIndex]==&mPool.getPayload(handles[i])); + // PT: TODO: don't we need to read the pool's array here, to pass the inflated bounds? + bool found = mBucketPruner.updateObject(newBounds[indices[i]], payloads[poolIndex], poolIndex); + PX_UNUSED(found); PX_ASSERT(found); + } + + if(mProgress == BUILD_NEW_MAPPING || mProgress == BUILD_FULL_REFIT) + mToRefit.pushBack(poolIndex); + } + } +} + +void AABBPruner::removeObjects(const PrunerHandle* handles, PxU32 count) +{ + PX_PROFILE_ZONE("SceneQuery.prunerRemoveObjects", mContextID); + + if(!count) + return; + + mUncommittedChanges = true; + + for(PxU32 i=0; imarkNodeForRefit(treeNodeIndex); // mark the spot as blank + mBucketPruner.swapIndex(poolIndex, swappedPayload, poolRelocatedLastIndex); // if swapped index is in bucket pruner + } + else + { + PX_ASSERT(treeNodeIndex==INVALID_PRUNERHANDLE); + PxU32 timeStamp; + bool status = mBucketPruner.removeObject(removedPayload, poolIndex, swappedPayload, poolRelocatedLastIndex, timeStamp); + PX_ASSERT(status); + PX_UNUSED(status); + } + + mTreeMap.invalidate(poolIndex, poolRelocatedLastIndex, *mAABBTree); + if(mNewTree) + mNewTreeFixups.pushBack(NewTreeFixup(poolIndex, poolRelocatedLastIndex)); + } + } + + if (mPool.getNbActiveObjects()==0) + { + // this is just to make sure we release all the internal data once all the objects are out of the pruner + // since this is the only place we know that and we don't want to keep memory reserved + release(); + + // Pruner API requires a commit before the next query, even if we ended up removing the entire tree here. This + // forces that to happen. + mUncommittedChanges = true; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Query Implementation + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxAgain AABBPruner::overlap(const ShapeData& queryVolume, PrunerCallback& pcb) const +{ + PX_ASSERT(!mUncommittedChanges); + + PxAgain again = true; + + if(mAABBTree) + { + switch(queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if(queryVolume.isOBB()) + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + else + { + const Gu::AABBAABBTest test(queryVolume.getPrunerInflatedWorldAABB()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + } + break; + case PxGeometryType::eCAPSULE: + { + const Gu::Capsule& capsule = queryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest test( capsule.p1, queryVolume.getPrunerWorldRot33().column0, + queryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::eSPHERE: + { + const Gu::Sphere& sphere = queryVolume.getGuSphere(); + Gu::SphereAABBTest test(sphere.center, sphere.radius); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::eCONVEXMESH: + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + } + + if(again && mIncrementalRebuild && mBucketPruner.getNbObjects()) + again = mBucketPruner.overlap(queryVolume, pcb); + + return again; +} + +PxAgain AABBPruner::sweep(const ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PX_ASSERT(!mUncommittedChanges); + + PxAgain again = true; + + if(mAABBTree) + { + const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB(); + const PxVec3 extents = aabb.getExtents(); + again = AABBTreeRaycast()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, aabb.getCenter(), unitDir, inOutDistance, extents, pcb); + } + + if(again && mIncrementalRebuild && mBucketPruner.getNbObjects()) + again = mBucketPruner.sweep(queryVolume, unitDir, inOutDistance, pcb); + + return again; +} + +PxAgain AABBPruner::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PX_ASSERT(!mUncommittedChanges); + + PxAgain again = true; + + if(mAABBTree) + again = AABBTreeRaycast()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, origin, unitDir, inOutDistance, PxVec3(0.0f), pcb); + + if(again && mIncrementalRebuild && mBucketPruner.getNbObjects()) + again = mBucketPruner.raycast(origin, unitDir, inOutDistance, pcb); + + return again; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Other methods of Pruner Interface + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// This isn't part of the pruner virtual interface, but it is part of the public interface +// of AABBPruner - it gets called by SqManager to force a rebuild, and requires a commit() before +// queries can take place + +void AABBPruner::purge() +{ + release(); + mUncommittedChanges = true; // this ensures a commit() must happen before any query +} + +void AABBPruner::setRebuildRateHint(PxU32 nbStepsForRebuild) +{ + PX_ASSERT(nbStepsForRebuild > 3); + mRebuildRateHint = (nbStepsForRebuild-3); // looks like a magic number to account for the rebuild pipeline latency + mAdaptiveRebuildTerm = 0; +} + +// Commit either performs a refit if background rebuild is not yet finished +// or swaps the current tree for the second tree rebuilt in the background +void AABBPruner::commit() +{ + PX_PROFILE_ZONE("SceneQuery.prunerCommit", mContextID); + + if(!mUncommittedChanges && (mProgress != BUILD_FINISHED)) + // Q: seems like this is both for refit and finalization so is this is correct? + // i.e. in a situation when we started rebuilding a tree and didn't add anything since + // who is going to set mUncommittedChanges to true? + // A: it's set in buildStep at final stage, so that finalization is forced. + // Seems a bit difficult to follow and verify correctness. + return; + + mUncommittedChanges = false; + + if(!mAABBTree || !mIncrementalRebuild) + { +#if PX_CHECKED + if(!mIncrementalRebuild && mAABBTree) + Ps::getFoundation().error(PxErrorCode::ePERF_WARNING, __FILE__, __LINE__, "SceneQuery static AABB Tree rebuilt, because a shape attached to a static actor was added, removed or moved, and PxSceneDesc::staticStructure is set to eSTATIC_AABB_TREE."); +#endif + fullRebuildAABBTree(); + return; + } + + // Note: it is not safe to call AABBPruner::build() here + // because the first thread will perform one step of the incremental update, + // continue raycasting, while the second thread performs the next step in + // the incremental update + + // Calling Refit() below is safe. It will call + // StaticPruner::build() when necessary. Both will early + // exit if the tree is already up to date, if it is not already, then we + // must be the first thread performing raycasts on a dirty tree and other + // scene query threads will be locked out by the write lock in + // SceneQueryManager::flushUpdates() + + + if (mProgress != BUILD_FINISHED) + { + // Calling refit because the second tree is not ready to be swapped in (mProgress != BUILD_FINISHED) + // Generally speaking as long as things keep moving the second build will never catch up with true state + refitUpdatedAndRemoved(); + } + else + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeFinalize", mContextID); + + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeSwitch", mContextID); + + PX_DELETE(mAABBTree); // delete the old tree + PX_FREE_AND_RESET(mCachedBoxes); + mProgress = BUILD_NOT_STARTED; // reset the build state to initial + + // Adjust adaptive term to get closer to specified rebuild rate. + // perform an even division correction to make sure the rebuild rate adds up + if (mNbCalls > mRebuildRateHint) + mAdaptiveRebuildTerm++; + else if (mNbCalls < mRebuildRateHint) + mAdaptiveRebuildTerm--; + + // Switch trees +#if PX_DEBUG + mNewTree->validate(); +#endif + mAABBTree = mNewTree; // set current tree to progressively rebuilt tree + mNewTree = NULL; // clear out the progressively rebuild tree pointer + } + + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeMapping", mContextID); + + // rebuild the tree map to match the current (newly built) tree + mTreeMap.initMap(PxMax(mPool.getNbActiveObjects(), mNbCachedBoxes), *mAABBTree); + + // The new mapping has been computed using only indices stored in the new tree. Those indices map the pruning pool + // we had when starting to build the tree. We need to re-apply recorded moves to fix the tree that finished rebuilding. + // AP: the problem here is while we are rebuilding the tree there are ongoing modifications to the current tree + // but the background build has a cached copy of all the AABBs at the time it was started + // (and will produce indices referencing those) + // Things that can happen in the meantime: update, remove, add, commit + for(NewTreeFixup* r = mNewTreeFixups.begin(); r < mNewTreeFixups.end(); r++) + { + // PT: we're not doing a full refit after this point anymore, so the remaining deleted objects must be manually marked for + // refit (otherwise their AABB in the tree would remain valid, leading to crashes when the corresponding index is 0xffffffff). + // We must do this before invalidating the corresponding tree nodes in the map, obviously (otherwise we'd be reading node + // indices that we already invalidated). + const PoolIndex poolIndex = r->removedIndex; + const TreeNodeIndex treeNodeIndex = mTreeMap[poolIndex]; + if(treeNodeIndex!=INVALID_NODE_ID) + mAABBTree->markNodeForRefit(treeNodeIndex); + + mTreeMap.invalidate(r->removedIndex, r->relocatedLastIndex, *mAABBTree); + } + mNewTreeFixups.clear(); // clear out the fixups since we just applied them all + } + + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeFinalRefit", mContextID); + + const PxU32 size = mToRefit.size(); + for(PxU32 i=0;imarkNodeForRefit(treeNodeIndex); + } + mToRefit.clear(); + refitUpdatedAndRemoved(); + } + + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeRemoveObjects", mContextID); + + PxU32 nbRemovedPairs = mBucketPruner.removeMarkedObjects(mTimeStamp-1); + PX_UNUSED(nbRemovedPairs); + + mNeedsNewTree = mBucketPruner.getNbObjects()>0; + } + } + + updateBucketPruner(); +} + + +void AABBPruner::shiftOrigin(const PxVec3& shift) +{ + mPool.shiftOrigin(shift); + + if(mAABBTree) + mAABBTree->shiftOrigin(shift); + + if(mIncrementalRebuild) + mBucketPruner.shiftOrigin(shift); + + if(mNewTree) + mNewTree->shiftOrigin(shift); +} + +#include "CmRenderOutput.h" +void AABBPruner::visualize(Cm::RenderOutput& out, PxU32 color) const +{ + // getAABBTree() asserts when pruner is dirty. NpScene::visualization() does not enforce flushUpdate. see DE7834 + const AABBTree* tree = mAABBTree; + + if(tree && tree->getNodes()) + { + struct Local + { + static void _Draw(const AABBTreeRuntimeNode* root, const AABBTreeRuntimeNode* node, Cm::RenderOutput& out_) + { + out_ << Cm::DebugBox(node->mBV, true); + if (node->isLeaf()) + return; + _Draw(root, node->getPos(root), out_); + _Draw(root, node->getNeg(root), out_); + } + }; + out << PxTransform(PxIdentity); + out << color; + Local::_Draw(tree->getNodes(), tree->getNodes(), out); + } + + // Render added objects not yet in the tree + out << PxTransform(PxIdentity); + out << PxU32(PxDebugColor::eARGB_WHITE); + + if(mIncrementalRebuild && mBucketPruner.getNbObjects()) + mBucketPruner.visualize(out, color); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Internal methods + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool AABBPruner::buildStep(bool synchronousCall) +{ + PX_PROFILE_ZONE("SceneQuery.prunerBuildStep", mContextID); + + PX_ASSERT(mIncrementalRebuild); + if(mNeedsNewTree) + { + if(mProgress==BUILD_NOT_STARTED) + { + if(!synchronousCall || !prepareBuild()) + return false; + } + else if(mProgress==BUILD_INIT) + { + mNewTree->progressiveBuild(mBuilder, mBuildStats, 0, 0); + mProgress = BUILD_IN_PROGRESS; + mNbCalls = 0; + + // Use a heuristic to estimate the number of work units needed for rebuilding the tree. + // The general idea is to use the number of work units of the previous tree to build the new tree. + // This works fine as long as the number of leaves remains more or less the same for the old and the + // new tree. If that is not the case, this estimate can be way off and the work units per step will + // be either much too small or too large. Hence, in that case we will try to estimate the number of work + // units based on the number of leaves of the new tree as follows: + // + // - Assume new tree with n leaves is perfectly-balanced + // - Compute the depth of perfectly-balanced tree with n leaves + // - Estimate number of working units for the new tree + + const PxU32 depth = Ps::ilog2(mBuilder.mNbPrimitives); // Note: This is the depth without counting the leaf layer + const PxU32 estimatedNbWorkUnits = depth * mBuilder.mNbPrimitives; // Estimated number of work units for new tree + const PxU32 estimatedNbWorkUnitsOld = mAABBTree ? mAABBTree->getTotalPrims() : 0; + if ((estimatedNbWorkUnits <= (estimatedNbWorkUnitsOld << 1)) && (estimatedNbWorkUnits >= (estimatedNbWorkUnitsOld >> 1))) + // The two estimates do not differ by more than a factor 2 + mTotalWorkUnits = estimatedNbWorkUnitsOld; + else + { + mAdaptiveRebuildTerm = 0; + mTotalWorkUnits = estimatedNbWorkUnits; + } + + const PxI32 totalWorkUnits = PxI32(mTotalWorkUnits + (mAdaptiveRebuildTerm * mBuilder.mNbPrimitives)); + mTotalWorkUnits = PxU32(PxMax(totalWorkUnits, 0)); + } + else if(mProgress==BUILD_IN_PROGRESS) + { + mNbCalls++; + const PxU32 Limit = 1 + (mTotalWorkUnits / mRebuildRateHint); + // looks like progressiveRebuild returns 0 when finished + if (!mNewTree->progressiveBuild(mBuilder, mBuildStats, 1, Limit)) + { + // Done + mProgress = BUILD_NEW_MAPPING; +#if PX_DEBUG + mNewTree->validate(); +#endif + } + } + else if(mProgress==BUILD_NEW_MAPPING) + { + mNbCalls++; + mProgress = BUILD_FULL_REFIT; + + // PT: we can't call fullRefit without creating the new mapping first: the refit function will fetch boxes from + // the pool using "primitive indices" captured in the tree. But some of these indices may have been invalidated + // if objects got removed while the tree was built. So we need to invalidate the corresponding nodes before refit, + // that way the #prims will be zero and the code won't fetch a wrong box (which may now below to a different object). + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeMapping", mContextID); + + if(mNewTreeFixups.size()) + { + mNewTreeMap.initMap(PxMax(mPool.getNbActiveObjects(), mNbCachedBoxes), *mNewTree); + + // The new mapping has been computed using only indices stored in the new tree. Those indices map the pruning pool + // we had when starting to build the tree. We need to re-apply recorded moves to fix the tree. + for(NewTreeFixup* r = mNewTreeFixups.begin(); r < mNewTreeFixups.end(); r++) + mNewTreeMap.invalidate(r->removedIndex, r->relocatedLastIndex, *mNewTree); + + mNewTreeFixups.clear(); +#if PX_DEBUG + mNewTree->validate(); +#endif + } + } + } + else if(mProgress==BUILD_FULL_REFIT) + { + mNbCalls++; + mProgress = BUILD_LAST_FRAME; + + { + PX_PROFILE_ZONE("SceneQuery.prunerNewTreeFullRefit", mContextID); + + // We need to refit the new tree because objects may have moved while we were building it. + mNewTree->fullRefit(mPool.getCurrentWorldBoxes()); + } + } + else if(mProgress==BUILD_LAST_FRAME) + { + mProgress = BUILD_FINISHED; + } + + // This is required to be set because commit handles both refit and a portion of build finalization (why?) + // This is overly conservative also only necessary in case there were no updates at all to the tree since the last tree swap + // It also overly conservative in a sense that it could be set only if mProgress was just set to BUILD_FINISHED + // If run asynchronously from a different thread, we touched just the new AABB build phase, we should not mark the main tree as dirty + if(synchronousCall) + mUncommittedChanges = true; + + return mProgress==BUILD_FINISHED; + } + + return false; +} + +bool AABBPruner::prepareBuild() +{ + PX_PROFILE_ZONE("SceneQuery.prepareBuild", mContextID); + + PX_ASSERT(mIncrementalRebuild); + if(mNeedsNewTree) + { + if(mProgress==BUILD_NOT_STARTED) + { + const PxU32 nbObjects = mPool.getNbActiveObjects(); + if(!nbObjects) + return false; + + PX_DELETE(mNewTree); + mNewTree = PX_NEW(AABBTree); + + mNbCachedBoxes = nbObjects; + // PT: we always allocate one extra box, to make sure we can safely use V4 loads on the array + mCachedBoxes = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(nbObjects+1), "PxBound3")); + + PxMemCopy(mCachedBoxes, mPool.getCurrentWorldBoxes(), nbObjects*sizeof(PxBounds3)); + + // PT: objects currently in the bucket pruner will be in the new tree. They are marked with the + // current timestamp (mTimeStamp). However more objects can get added while we compute the new tree, + // and those ones will not be part of it. These new objects will be marked with the new timestamp + // value (mTimeStamp+1), and we can use these different values to remove the proper objects from + // the bucket pruner (when switching to the new tree). + mTimeStamp++; +#if USE_INCREMENTAL_PRUNER + // notify the incremental pruner to swap trees + mBucketPruner.timeStampChange(); +#endif + mBuilder.reset(); + mBuilder.mNbPrimitives = mNbCachedBoxes; + mBuilder.mAABBArray = mCachedBoxes; + mBuilder.mLimit = NB_OBJECTS_PER_NODE; + + mBuildStats.reset(); + + // start recording modifications to the tree made during rebuild to reapply (fix the new tree) eventually + PX_ASSERT(mNewTreeFixups.size()==0); + + mProgress = BUILD_INIT; + } + } + else + return false; + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds an AABB-tree for objects in the pruning pool. + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBPruner::fullRebuildAABBTree() +{ + PX_PROFILE_ZONE("SceneQuery.prunerFullRebuildAABBTree", mContextID); + + // Release possibly already existing tree + PX_DELETE_AND_RESET(mAABBTree); + + // Don't bother building an AABB-tree if there isn't a single static object + const PxU32 nbObjects = mPool.getNbActiveObjects(); + if(!nbObjects) + return true; + + bool Status; + { + // Create a new tree + mAABBTree = PX_NEW(AABBTree); + + AABBTreeBuildParams TB; + TB.mNbPrimitives = nbObjects; + TB.mAABBArray = mPool.getCurrentWorldBoxes(); + TB.mLimit = NB_OBJECTS_PER_NODE; + Status = mAABBTree->build(TB); + } + + // No need for the tree map for static pruner + if(mIncrementalRebuild) + mTreeMap.initMap(PxMax(nbObjects,mNbCachedBoxes),*mAABBTree); + + return Status; +} + +// called in the end of commit(), but only if mIncrementalRebuild is true +void AABBPruner::updateBucketPruner() +{ + PX_PROFILE_ZONE("SceneQuery.prunerUpdateBucketPruner", mContextID); + + PX_ASSERT(mIncrementalRebuild); + mBucketPruner.build(); +} + +PxBounds3 AABBPruner::getAABB(PrunerHandle handle) +{ + return mPool.getWorldAABB(handle); +} + +void AABBPruner::release() // this can be called from purge() +{ + mBucketPruner.release(); + + mTimeStamp = 0; + + mTreeMap.release(); + mNewTreeMap.release(); + + PX_FREE_AND_RESET(mCachedBoxes); + mBuilder.reset(); + PX_DELETE_AND_RESET(mNewTree); + PX_DELETE_AND_RESET(mAABBTree); + + mNbCachedBoxes = 0; + mProgress = BUILD_NOT_STARTED; + mNewTreeFixups.clear(); + mUncommittedChanges = false; +} + +// Refit current tree +void AABBPruner::refitUpdatedAndRemoved() +{ + PX_PROFILE_ZONE("SceneQuery.prunerRefitUpdatedAndRemoved", mContextID); + + PX_ASSERT(mIncrementalRebuild); + AABBTree* tree = getAABBTree(); + if(!tree) + return; + +#if PX_DEBUG + tree->validate(); +#endif + + //### missing a way to skip work if not needed + + const PxU32 nbObjects = mPool.getNbActiveObjects(); + // At this point there still can be objects in the tree that are blanked out so it's an optimization shortcut (not required) + if(!nbObjects) + return; + + mBucketPruner.refitMarkedNodes(mPool.getCurrentWorldBoxes()); + tree->refitMarkedNodes(mPool.getCurrentWorldBoxes()); +} + +void AABBPruner::merge(const void* mergeParams) +{ + const AABBPrunerMergeData& pruningStructure = *reinterpret_cast (mergeParams); + + if(mAABBTree) + { + // index in pruning pool, where new objects were added + const PxU32 pruningPoolIndex = mPool.getNbActiveObjects() - pruningStructure.mNbObjects; + + // create tree from given nodes and indices + AABBTreeMergeData aabbTreeMergeParams(pruningStructure.mNbNodes, pruningStructure.mAABBTreeNodes, + pruningStructure.mNbObjects, pruningStructure.mAABBTreeIndices, pruningPoolIndex); + + if (!mIncrementalRebuild) + { + // merge tree directly + mAABBTree->mergeTree(aabbTreeMergeParams); + } + else + { + mBucketPruner.addTree(aabbTreeMergeParams, mTimeStamp); + } + } +} diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBPruner.h b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.h new file mode 100644 index 000000000..f575443db --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.h @@ -0,0 +1,269 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_AABB_PRUNER_H +#define SQ_AABB_PRUNER_H + +#include "SqPruningPool.h" +#include "SqExtendedBucketPruner.h" +#include "SqAABBTreeUpdateMap.h" +#include "SqAABBTree.h" + +namespace physx +{ + +namespace Sq +{ + // PT: we build the new tree over a number of frames/states, in order to limit perf spikes in 'updatePruningTrees'. + // The states are as follows: + // + // BUILD_NOT_STARTED (1 frame, AABBPruner): + // + // This is the initial state, before the new (AABBTree) build even starts. In this frame/state, we perform the AABBPruner-related + // memory allocations: + // - the new AABB tree is allocated + // - the array of cached bounding boxes is allocated and filled + // + // BUILD_INIT (1 frame, AABBTree): + // + // This is the first frame in which the new tree gets built. It deserves its own special state since various things happen in the + // first frame, that do no happen in subsequent frames. Basically most initial AABBTree-related allocations happen here (but no + // build step per se). + // + // BUILD_IN_PROGRESS (N frames, AABBTree): + // + // This is the core build function, actually building the tree. This should be mostly allocation-free, except here and there when + // building non-complete trees, and during the last call when the tree is finally built. + // + // BUILD_NEW_MAPPING (1 frame, AABBPruner): + // + // After the new AABBTree is built, we recreate an AABBTreeUpdateMap for the new tree, and use it to invalidate nodes whose objects + // have been removed during the build. + // + // We need to do that before doing a full refit in the next stage/frame. If we don't do that, the refit code will fetch a wrong box, + // that may very well belong to an entirely new object. + // + // Note that this mapping/update map (mNewTreeMap) is temporary, and only needed for the next stage. + // + // BUILD_FULL_REFIT (1 frame, AABBPruner): + // + // Once the new update map is available, we fully refit the new tree. AABBs of moved objects get updated. AABBs of removed objects + // become empty. + // + // BUILD_LAST_FRAME (1 frame, AABBPruner): + // + // This is an artificial frame used to delay the tree switching code. The switch happens as soon as we reach the BUILD_FINISHED + // state, but we don't want to execute BUILD_FULL_REFIT and the switch in the same frame. This extra BUILD_LAST_FRAME stage buys + // us one frame, i.e. we have one frame in which we do BUILD_FULL_REFIT, and in the next frame we'll do both BUILD_LAST_FRAME / + // BUILD_FINISHED / the switch. + // + // BUILD_FINISHED (1 frame, AABBPruner): + // + // Several things happen in this 'finalization' frame/stage: + // - We switch the trees (old one is deleted, cached boxes are deleted, new tree pointer is setup) + // - A new (final) update map is created (mTreeMap). The map is used to invalidate objects that may have been removed during + // the BUILD_NEW_MAPPING and BUILD_FULL_REFIT frames. The nodes containing these removed objects are marked for refit. + // - Nodes containing objects that have moved during the BUILD_NEW_MAPPING and BUILD_FULL_REFIT frames are marked for refit. + // - We do a partial refit on the new tree, to take these final changes into account. This small partial refit is usually much + // cheaper than the full refit we previously performed here. + // - We remove old objects from the bucket pruner + // + enum BuildStatus + { + BUILD_NOT_STARTED, + BUILD_INIT, + BUILD_IN_PROGRESS, + BUILD_NEW_MAPPING, + BUILD_FULL_REFIT, + BUILD_LAST_FRAME, + BUILD_FINISHED, + + BUILD_FORCE_DWORD = 0xffffffff + }; + + // This class implements the Pruner interface for internal SQ use with some additional specialized functions + // The underlying data structure is a binary AABB tree + // AABBPruner supports insertions, removals and updates for dynamic objects + // The tree is either entirely rebuilt in a single frame (static pruner) or progressively rebuilt over multiple frames (dynamic pruner) + // The rebuild happens on a copy of the tree + // the copy is then swapped with current tree at the time commit() is called (only if mBuildState is BUILD_FINISHED), + // otherwise commit() will perform a refit operation applying any pending changes to the current tree + // While the tree is being rebuilt a temporary data structure (BucketPruner) is also kept in sync and used to speed up + // queries on updated objects that are not yet in either old or new tree. + // The requirements on the order of calls: + // commit() is required to be called before any queries to apply modifications + // queries can be issued on multiple threads after commit is called + // commit, buildStep, add/remove/update have to be called from the same thread or otherwise strictly serialized by external code + // and cannot be issued while a query is running + class AABBPruner : public IncrementalPruner + { + public: + AABBPruner(bool incrementalRebuild, PxU64 contextID); // true is equivalent to former dynamic pruner + virtual ~AABBPruner(); + + // Pruner + virtual bool addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* userData, PxU32 count, bool hasPruningStructure); + virtual void removeObjects(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count); + virtual void commit(); + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual const PrunerPayload& getPayload(PrunerHandle handle) const { return mPool.getPayload(handle); } + virtual const PrunerPayload& getPayload(PrunerHandle handle, PxBounds3*& bounds) const { return mPool.getPayload(handle, bounds); } + virtual void preallocate(PxU32 entries) { mPool.preallocate(entries); } + virtual void shiftOrigin(const PxVec3& shift); + virtual void visualize(Cm::RenderOutput& out, PxU32 color) const; + virtual void merge(const void* mergeParams); + //~Pruner + + // IncrementalPruner + virtual void purge(); // gets rid of internal accel struct + virtual void setRebuildRateHint(PxU32 nbStepsForRebuild); // Besides the actual rebuild steps, 3 additional steps are needed. + virtual bool buildStep(bool synchronousCall = true); // returns true if finished + virtual bool prepareBuild(); // returns true if new tree is needed + //~IncrementalPruner + + // direct access for test code + + PX_FORCE_INLINE PxU32 getNbAddedObjects() const { return mBucketPruner.getNbObjects(); } + PX_FORCE_INLINE const Sq::AABBTree* getAABBTree() const { PX_ASSERT(!mUncommittedChanges); return mAABBTree; } + PX_FORCE_INLINE Sq::AABBTree* getAABBTree() { PX_ASSERT(!mUncommittedChanges); return mAABBTree; } + PX_FORCE_INLINE void setAABBTree(Sq::AABBTree* tree) { mAABBTree = tree; } + PX_FORCE_INLINE const Sq::AABBTree* hasAABBTree() const { return mAABBTree; } + PX_FORCE_INLINE BuildStatus getBuildStatus() const { return mProgress; } + + // local functions +// private: + Sq::AABBTree* mAABBTree; // current active tree + Gu::AABBTreeBuildParams mBuilder; // this class deals with the details of the actual tree building + Gu::BuildStats mBuildStats; + + // tree with build in progress, assigned to mAABBTree in commit, when mProgress is BUILD_FINISHED + // created in buildStep(), BUILD_NOT_STARTED + // This is non-null when there is a tree rebuild going on in progress + // and thus also indicates that we have to start saving the fixups + Sq::AABBTree* mNewTree; + + // during rebuild the pool might change so we need a copy of boxes for the tree build + PxBounds3* mCachedBoxes; + PxU32 mNbCachedBoxes; + + // incremented in commit(), serves as a progress counter for rebuild + PxU32 mNbCalls; + + // PT: incremented each time we start building a new tree (i.e. effectively identifies a given tree) + // Timestamp is passed to bucket pruner to mark objects added there, linking them to a specific tree. + // When switching to the new tree, timestamp is used to remove old objects (now in the new tree) from + // the bucket pruner. + PxU32 mTimeStamp; + + // this pruner is used for queries on objects that are not in the current tree yet + // includes both the objects in the tree being rebuilt and all the objects added later + ExtendedBucketPruner mBucketPruner; + + BuildStatus mProgress; // current state of second tree build progress + + // Fraction (as in 1/Nth) of the total number of primitives + // that should be processed per step by the AABB builder + // so if this value is 1, all primitives will be rebuilt, 2 => 1/2 of primitives per step etc. + // see also mNbCalls, mNbCalls varies from 0 to mRebuildRateHint-1 + PxU32 mRebuildRateHint; + + // Estimate for how much work has to be done to rebuild the tree. + PxU32 mTotalWorkUnits; + + // Term to correct the work unit estimate if the rebuild rate is not matched + PxI32 mAdaptiveRebuildTerm; + + PruningPool mPool; // Pool of AABBs + + // maps pruning pool indices to aabb tree indices + // maps to INVALID_NODE_ID if the pool entry was removed or "pool index is outside input domain" + // The map is the inverse of the tree mapping: (node[map[poolID]].primitive == poolID) + // So: + // treeNodeIndex = mTreeMap.operator[](poolIndex) + // aabbTree->treeNodes[treeNodeIndex].primitives[0] == poolIndex + AABBTreeUpdateMap mTreeMap; + // Temporary update map, see BuildStatus notes above for details + AABBTreeUpdateMap mNewTreeMap; + + // This is only set once in the constructor and is equivalent to isDynamicTree + // if it set to false then a 1-shot rebuild is performed in commit() + // bucket pruner is only used with incremental rebuild + bool mIncrementalRebuild; + + // A rebuild can be triggered even when the Pruner is not dirty + // mUncommittedChanges is set to true in add, remove, update and buildStep + // mUncommittedChanges is set to false in commit + // mUncommittedChanges has to be false (commit() has to be called) in order to run a query as defined by the + // mUncommittedChanges is not set to true in add, when pruning structure is provided. Scene query shapes + // are merged to current AABB tree directly + // Pruner higher level API + bool mUncommittedChanges; + + // A new AABB tree is built if an object was added, removed or updated + // Changing objects during a build will trigger another rebuild right afterwards + // this is set to true if a new tree has to be created again after the current rebuild is done + bool mNeedsNewTree; + + // This struct is used to record modifications made to the pruner state + // while a tree is building in the background + // this is so we can apply the modifications to the tree at the time of completion + // the recorded fixup information is: removedIndex (in ::remove()) and + // lastIndexMoved which is the last index in the pruner array + // (since the way we remove from PruningPool is by swapping last into removed slot, + // we need to apply a fixup so that it syncs up that operation in the new tree) + struct NewTreeFixup + { + PX_FORCE_INLINE NewTreeFixup(PxU32 removedIndex_, PxU32 relocatedLastIndex_) + : removedIndex(removedIndex_), relocatedLastIndex(relocatedLastIndex_) {} + PxU32 removedIndex; + PxU32 relocatedLastIndex; + }; + Ps::Array mNewTreeFixups; + + Ps::Array mToRefit; + + PxU64 mContextID; + + // Internal methods + bool fullRebuildAABBTree(); // full rebuild function, used with static pruner mode + void release(); + void refitUpdatedAndRemoved(); + void updateBucketPruner(); + PxBounds3 getAABB(PrunerHandle h); + }; + +} // namespace Sq + +} + +#endif // SQ_AABB_PRUNER_H diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp b/src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp new file mode 100644 index 000000000..8faec8104 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp @@ -0,0 +1,920 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "SqAABBTree.h" +#include "SqAABBTreeUpdateMap.h" +#include "SqBounds.h" + +#include "PsMathUtils.h" +#include "PsFoundation.h" +#include "GuInternal.h" + +using namespace physx; +using namespace Sq; +using namespace Gu; + +#define INVALID_ID 0xffffffff + +// Progressive building +class Sq::FIFOStack : public Ps::UserAllocated +{ +public: + FIFOStack() : mStack(PX_DEBUG_EXP("SQFIFOStack")), mCurIndex(0) {} + ~FIFOStack() {} + + PX_FORCE_INLINE PxU32 getNbEntries() const { return mStack.size(); } + PX_FORCE_INLINE void push(AABBTreeBuildNode* entry) { mStack.pushBack(entry); } + bool pop(AABBTreeBuildNode*& entry); +private: + Ps::Array mStack; + PxU32 mCurIndex; //!< Current index within the container +}; + +bool Sq::FIFOStack::pop(AABBTreeBuildNode*& entry) +{ + const PxU32 NbEntries = mStack.size(); // Get current number of entries + if (!NbEntries) + return false; // Can be NULL when no value has been pushed. This is an invalid pop call. + entry = mStack[mCurIndex++]; // Get oldest entry, move to next one + if (mCurIndex == NbEntries) + { + // All values have been poped + mStack.clear(); + mCurIndex = 0; + } + return true; +} +//~Progressive building + +void flatten(const NodeAllocator& nodeAllocator, AABBTreeRuntimeNode* dest) +{ + // PT: gathers all build nodes allocated so far and flatten them to a linear destination array of smaller runtime nodes + PxU32 offset = 0; + const PxU32 nbSlabs = nodeAllocator.mSlabs.size(); + for(PxU32 s=0;s= nodeAllocator.mSlabs[j].mPool && pool[i].mPos < nodeAllocator.mSlabs[j].mPool + nodeAllocator.mSlabs[j].mNbUsedNodes) + { + localNodeIndex = PxU32(pool[i].mPos - nodeAllocator.mSlabs[j].mPool); + break; + } + nodeBase += nodeAllocator.mSlabs[j].mNbUsedNodes; + } + const PxU32 nodeIndex = nodeBase + localNodeIndex; + dest[offset].mData = nodeIndex<<1; + } + offset++; + } + } +} + +AABBTree::AABBTree() : + mIndices (NULL), + mNbIndices (0), + mRuntimePool (NULL), + mParentIndices (NULL), + mTotalNbNodes (0), + mTotalPrims (0) +{ +// Progressive building + mStack = NULL; +//~Progressive building + +// REFIT + mRefitHighestSetWord = 0; +//~REFIT +} + +AABBTree::~AABBTree() +{ + release(false); +} + +void AABBTree::release(bool clearRefitMap) +{ +// Progressive building + PX_DELETE_AND_RESET(mStack); +//~Progressive building + PX_FREE_AND_RESET(mParentIndices); + PX_DELETE_ARRAY(mRuntimePool); + mNodeAllocator.release(); + PX_FREE_AND_RESET(mIndices); + mTotalNbNodes = 0; + mNbIndices = 0; + +// REFIT + if(clearRefitMap) + mRefitBitmask.clearAll(); + mRefitHighestSetWord = 0; +//~REFIT +} + +// Initialize nodes/indices from the input tree merge data +void AABBTree::initTree(const AABBTreeMergeData& tree) +{ + PX_ASSERT(mIndices == NULL); + PX_ASSERT(mRuntimePool == NULL); + PX_ASSERT(mParentIndices == NULL); + + // allocate,copy indices + mIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*tree.mNbIndices, "AABB tree indices")); + mNbIndices = tree.mNbIndices; + PxMemCopy(mIndices, tree.mIndices, sizeof(PxU32)*tree.mNbIndices); + + // allocate,copy nodes + mRuntimePool = PX_NEW(AABBTreeRuntimeNode)[tree.mNbNodes]; + mTotalNbNodes = tree.mNbNodes; + PxMemCopy(mRuntimePool, tree.mNodes, sizeof(AABBTreeRuntimeNode)*tree.mNbNodes); +} + +// Shift indices of the tree by offset. Used for merged trees, when initial indices needs to be shifted to match indices in current pruning pool +void AABBTree::shiftIndices(PxU32 offset) +{ + for (PxU32 i = 0; i < mNbIndices; i++) + { + mIndices[i] += offset; + } +} + +bool AABBTree::buildInit(AABBTreeBuildParams& params, BuildStats& stats) +{ + // Checkings + const PxU32 nbPrimitives = params.mNbPrimitives; + if(!nbPrimitives) + return false; + + // Release previous tree + release(); + + // Initialize indices. This list will be modified during build. + mNbIndices = nbPrimitives; + return initAABBTreeBuild(params, mNodeAllocator, stats, mIndices); +} + +void AABBTree::buildEnd(AABBTreeBuildParams& params, BuildStats& stats) +{ + PX_FREE_AND_RESET(params.mCache); + // Get back total number of nodes + mTotalNbNodes = stats.getCount(); + mTotalPrims = stats.mTotalPrims; + + mRuntimePool = PX_NEW(AABBTreeRuntimeNode)[mTotalNbNodes]; + PX_ASSERT(mTotalNbNodes==mNodeAllocator.mTotalNbNodes); + flatten(mNodeAllocator, mRuntimePool); + mNodeAllocator.release(); +} + +bool AABBTree::build(AABBTreeBuildParams& params) +{ + const PxU32 nbPrimitives = params.mNbPrimitives; + if(!nbPrimitives) + return false; + + // Release previous tree + release(); + + BuildStats stats; + mNbIndices = nbPrimitives; + + const bool buildStatus = buildAABBTree(params, mNodeAllocator, stats, mIndices); + PX_UNUSED(buildStatus); + PX_ASSERT(buildStatus); + + buildEnd(params, stats); + return true; +} + +void AABBTree::shiftOrigin(const PxVec3& shift) +{ + AABBTreeRuntimeNode* const nodeBase = mRuntimePool; + const PxU32 totalNbNodes = mTotalNbNodes; + for(PxU32 i=0; isubdivide(params, stats, nodeBase, indices); + + if(!node->isLeaf()) + { + AABBTreeBuildNode* pos = const_cast(node->getPos()); + PX_ASSERT(pos); + AABBTreeBuildNode* neg = pos + 1; + stack.push(neg); + stack.push(pos); + } + + stats.mTotalPrims += node->mNbPrimitives; + return node->mNbPrimitives; +} + +PxU32 AABBTree::progressiveBuild(AABBTreeBuildParams& params, BuildStats& stats, PxU32 progress, PxU32 limit) +{ + if(progress==0) + { + if(!buildInit(params, stats)) + return PX_INVALID_U32; + + mStack = PX_NEW(FIFOStack); + mStack->push(mNodeAllocator.mPool); + return progress++; + } + else if(progress==1) + { + PxU32 stackCount = mStack->getNbEntries(); + if(stackCount) + { + PxU32 Total = 0; + const PxU32 Limit = limit; + while(Totalpop(Entry)) + Total += incrementalBuildHierarchy(*mStack, Entry, params, stats, mNodeAllocator, mIndices); + else + break; + } + return progress; + } + + buildEnd(params, stats); + + PX_DELETE_AND_RESET(mStack); + + return 0; // Done! + } + return PX_INVALID_U32; +} +//~Progressive building + + + +static PX_FORCE_INLINE PxU32 BitsToDwords(PxU32 nb_bits) +{ + return (nb_bits>>5) + ((nb_bits&31) ? 1 : 0); +} + +bool Sq::BitArray::init(PxU32 nb_bits) +{ + mSize = BitsToDwords(nb_bits); + // Get ram for n bits + PX_FREE(mBits); + mBits = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mSize, "BitArray::mBits")); + // Set all bits to 0 + clearAll(); + return true; +} + +void Sq::BitArray::resize(PxU32 maxBitNumber) +{ + const PxU32 newSize = BitsToDwords(maxBitNumber); + if (newSize <= mSize) + return; + + PxU32* newBits = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*newSize, "BitArray::mBits")); + PxMemZero(newBits + mSize, (newSize - mSize) * sizeof(PxU32)); + PxMemCopy(newBits, mBits, mSize*sizeof(PxU32)); + PX_FREE(mBits); + mBits = newBits; + mSize = newSize; +} + +static PX_FORCE_INLINE PxU32 getNbPrimitives(PxU32 data) { return (data>>1)&15; } +static PX_FORCE_INLINE const PxU32* getPrimitives(const PxU32* base, PxU32 data) { return base + (data>>5); } +static PX_FORCE_INLINE const AABBTreeRuntimeNode* getPos(const AABBTreeRuntimeNode* base, PxU32 data) { return base + (data>>1); } +static PX_FORCE_INLINE PxU32 isLeaf(PxU32 data) { return data&1; } + +static PX_FORCE_INLINE void refitNode(AABBTreeRuntimeNode* PX_RESTRICT current, const PxBounds3* PX_RESTRICT boxes, const PxU32* PX_RESTRICT indices, AABBTreeRuntimeNode* PX_RESTRICT const nodeBase) +{ + // PT: we can safely use V4 loads on both boxes and nodes here: + // - it's safe on boxes because we allocated one extra box in the pruning pool + // - it's safe on nodes because there's always some data within the node, after the BV + + const PxU32 data = current->mData; + + Vec4V resultMinV, resultMaxV; + if(isLeaf(data)) + { + const PxU32 nbPrims = getNbPrimitives(data); + if(nbPrims) + { + const PxU32* primitives = getPrimitives(indices, data); + resultMinV = V4LoadU(&boxes[*primitives].minimum.x); + resultMaxV = V4LoadU(&boxes[*primitives].maximum.x); + + if(nbPrims>1) + { + const PxU32* last = primitives + nbPrims; + primitives++; + + while(primitives!=last) + { + resultMinV = V4Min(resultMinV, V4LoadU(&boxes[*primitives].minimum.x)); + resultMaxV = V4Max(resultMaxV, V4LoadU(&boxes[*primitives].maximum.x)); + primitives++; + } + } + } + else + { + // Might happen after a node has been invalidated + const float max = SQ_EMPTY_BOUNDS_EXTENTS; + resultMinV = V4Load(max); + resultMaxV = V4Load(-max); + } + } + else + { + const AABBTreeRuntimeNode* pos = getPos(nodeBase, data); + const AABBTreeRuntimeNode* neg = pos+1; + + const PxBounds3& posBox = pos->mBV; + const PxBounds3& negBox = neg->mBV; + + resultMinV = V4Min(V4LoadU(&posBox.minimum.x), V4LoadU(&negBox.minimum.x)); +// resultMaxV = V4Max(V4LoadU(&posBox.maximum.x), V4LoadU(&negBox.maximum.x)); + +#if PX_INTEL_FAMILY && !defined(PX_SIMD_DISABLED) + Vec4V posMinV = V4LoadU(&posBox.minimum.z); + Vec4V negMinV = V4LoadU(&negBox.minimum.z); + posMinV = _mm_shuffle_ps(posMinV, posMinV, _MM_SHUFFLE(0, 3, 2, 1)); + negMinV = _mm_shuffle_ps(negMinV, negMinV, _MM_SHUFFLE(0, 3, 2, 1)); + resultMaxV = V4Max(posMinV, negMinV); +#else + // PT: fixes the perf issue but not really convincing + resultMaxV = Vec4V_From_Vec3V(V3Max(V3LoadU(&posBox.maximum.x), V3LoadU(&negBox.maximum.x))); +#endif + } + + // PT: the V4 stores overwrite the data after the BV, but we just put it back afterwards + V4StoreU(resultMinV, ¤t->mBV.minimum.x); + V4StoreU(resultMaxV, ¤t->mBV.maximum.x); + current->mData = data; +} + +void AABBTree::fullRefit(const PxBounds3* boxes) +{ + PX_ASSERT(boxes); + + const PxU32* indices = mIndices; + AABBTreeRuntimeNode* const nodeBase = mRuntimePool; + PX_ASSERT(nodeBase); + + // Bottom-up update + PxU32 index = mTotalNbNodes; + while(index--) + { + AABBTreeRuntimeNode* current = nodeBase + index; + if(index) + Ps::prefetch(current - 1); + + refitNode(current, boxes, indices, nodeBase); + } +} + +static void _createParentArray(PxU32 totalNbNodes, PxU32* parentIndices, const AABBTreeRuntimeNode* parentNode, const AABBTreeRuntimeNode* currentNode, const AABBTreeRuntimeNode* root) +{ + const PxU32 parentIndex = PxU32(parentNode - root); + const PxU32 currentIndex = PxU32(currentNode - root); + PX_ASSERT(parentIndexisLeaf()) + { + _createParentArray(totalNbNodes, parentIndices, currentNode, currentNode->getPos(root), root); + _createParentArray(totalNbNodes, parentIndices, currentNode, currentNode->getNeg(root), root); + } +} + +void AABBTree::markNodeForRefit(TreeNodeIndex nodeIndex) +{ + if(!mRefitBitmask.getBits()) + mRefitBitmask.init(mTotalNbNodes); + + PX_ASSERT(nodeIndex(PX_ALLOC(sizeof(PxU32)*mTotalNbNodes, "AABB parent indices")); + _createParentArray(mTotalNbNodes, mParentIndices, mRuntimePool, mRuntimePool, mRuntimePool); + } + + PxU32 currentIndex = nodeIndex; + while(1) + { + PX_ASSERT(currentIndex>5; + mRefitHighestSetWord = PxMax(mRefitHighestSetWord, currentMarkedWord); + + const PxU32 parentIndex = mParentIndices[currentIndex]; + PX_ASSERT(parentIndex == 0 || parentIndex < currentIndex); + if(currentIndex == parentIndex) + break; + currentIndex = parentIndex; + } + } +} + +#define FIRST_VERSION +#ifdef FIRST_VERSION +void AABBTree::refitMarkedNodes(const PxBounds3* boxes) +{ + if(!mRefitBitmask.getBits()) + return; // No refit needed + + { + /*const*/ PxU32* bits = const_cast(mRefitBitmask.getBits()); + PxU32 size = mRefitHighestSetWord+1; +#ifdef _DEBUG + if(1) + { + const PxU32 totalSize = mRefitBitmask.getSize(); + for(PxU32 i=size;i>5); + PX_ASSERT(mask==PxU32(1<<(index&31))); + if(currentBits & mask) + { + refitNode(nodeBase + index, boxes, indices, nodeBase); +#ifdef _DEBUG + nbRefit++; +#endif + } + mask>>=1; + } + bits[size] = 0; + } + + mRefitHighestSetWord = 0; +// mRefitBitmask.clearAll(); + } +} +#endif + + +//#define SECOND_VERSION +#ifdef SECOND_VERSION +void AABBTree::refitMarkedNodes(const PxBounds3* boxes) +{ + /*const*/ PxU32* bits = const_cast(mRefitBitmask.getBits()); + if(!bits) + return; // No refit needed + + const PxU32 lastSetBit = mRefitBitmask.findLast(); + + const PxU32* indices = mIndices; + AABBTreeRuntimeNode* const nodeBase = mRuntimePool; + + for(PxU32 w = 0; w <= lastSetBit >> 5; ++w) + { + for(PxU32 b = bits[w]; b; b &= b-1) + { + const PxU32 index = (PxU32)(w<<5|Ps::lowestSetBit(b)); + + + + while(size--) + { + // Test 32 bits at a time + const PxU32 currentBits = bits[size]; + if(!currentBits) + continue; + + PxU32 index = (size+1)<<5; + PxU32 mask = PxU32(1<<((index-1)&31)); + PxU32 _Count=32; + while(_Count--) + { + index--; + Ps::prefetch(nodeBase + index); + + PX_ASSERT(size==index>>5); + PX_ASSERT(mask==PxU32(1<<(index&31))); + if(currentBits & mask) + { + refitNode(nodeBase + index, boxes, indices, nodeBase); +#ifdef _DEBUG + nbRefit++; +#endif + } + mask>>=1; + } + bits[size] = 0; + } + mRefitHighestSetWord = 0; +// mRefitBitmask.clearAll(); + } +} +#endif + +PX_FORCE_INLINE static void setLeafData(PxU32& leafData, const AABBTreeRuntimeNode& node, const PxU32 indicesOffset) +{ + const PxU32 index = indicesOffset + (node.mData >> 5); + const PxU32 nbPrims = node.getNbPrimitives(); + PX_ASSERT(nbPrims <= 16); + leafData = (index << 5) | ((nbPrims & 15) << 1) | 1; +} + +// Copy the tree into nodes. Update node indices, leaf indices. +void AABBTree::addRuntimeChilds(PxU32& nodeIndex, const AABBTreeMergeData& treeParams) +{ + PX_ASSERT(nodeIndex < mTotalNbNodes + treeParams.mNbNodes + 1); + const PxU32 baseNodeIndex = nodeIndex; + + // copy the src tree into dest tree nodes, update its data + for (PxU32 i = 0; i < treeParams.mNbNodes; i++) + { + PX_ASSERT(nodeIndex < mTotalNbNodes + treeParams.mNbNodes + 1); + mRuntimePool[nodeIndex].mBV = treeParams.mNodes[i].mBV; + if (treeParams.mNodes[i].isLeaf()) + { + setLeafData(mRuntimePool[nodeIndex].mData, treeParams.mNodes[i], mNbIndices); + } + else + { + const PxU32 srcNodeIndex = baseNodeIndex + (treeParams.mNodes[i].getPosIndex()); + mRuntimePool[nodeIndex].mData = srcNodeIndex << 1; + mParentIndices[srcNodeIndex] = nodeIndex; + mParentIndices[srcNodeIndex + 1] = nodeIndex; + } + nodeIndex++; + } +} + +// Merge tree into targetNode, where target node is a leaf +// 1. Allocate new nodes/parent, copy all the nodes/parents +// 2. Create new node at the end, copy the data from target node +// 3. Copy the merge tree after the new node, create the parent map for them, update the leaf indices +// Schematic view: +// Target Nodes: ...Tn... +// Input tree: R1->Rc0, Rc1... +// Merged tree: ...Tnc->...->Nc0,R1->Rc0,Rc1... +// where new node: Nc0==Tn and Tnc is not a leaf anymore and points to Nc0 + +void AABBTree::mergeRuntimeLeaf(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& treeParams, PxU32 targetMergeNodeIndex) +{ + PX_ASSERT(mParentIndices); + PX_ASSERT(targetNode.isLeaf()); + + // 1. Allocate new nodes/parent, copy all the nodes/parents + // allocate new runtime pool with max combine number of nodes + // we allocate only 1 additional node each merge + AABBTreeRuntimeNode* newRuntimePool = PX_NEW(AABBTreeRuntimeNode)[mTotalNbNodes + treeParams.mNbNodes + 1]; + PxU32* newParentIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*(mTotalNbNodes + treeParams.mNbNodes + 1), "AABB parent indices")); + + // copy the whole target nodes, we will add the new node at the end together with the merge tree + PxMemCopy(newRuntimePool, mRuntimePool, sizeof(AABBTreeRuntimeNode)*(mTotalNbNodes)); + PxMemCopy(newParentIndices, mParentIndices, sizeof(PxU32)*(mTotalNbNodes)); + + // 2. Create new node at the end, copy the data from target node + PxU32 nodeIndex = mTotalNbNodes; + // copy the targetNode at the end of the new nodes + newRuntimePool[nodeIndex].mBV = targetNode.mBV; + newRuntimePool[nodeIndex].mData = targetNode.mData; + // update the parent information + newParentIndices[nodeIndex] = targetMergeNodeIndex; + + // mark for refit + if (mRefitBitmask.getBits() && mRefitBitmask.isSet(targetMergeNodeIndex)) + { + mRefitBitmask.setBit(nodeIndex); + const PxU32 currentMarkedWord = nodeIndex >> 5; + mRefitHighestSetWord = PxMax(mRefitHighestSetWord, currentMarkedWord); + } + + // swap pointers + PX_DELETE_ARRAY(mRuntimePool); + mRuntimePool = newRuntimePool; + PX_FREE(mParentIndices); + mParentIndices = newParentIndices; + + // 3. Copy the merge tree after the new node, create the parent map for them, update the leaf indices + nodeIndex++; + addRuntimeChilds(nodeIndex, treeParams); + PX_ASSERT(nodeIndex == mTotalNbNodes + 1 + treeParams.mNbNodes); + + // update the parent information for the input tree root node + mParentIndices[mTotalNbNodes + 1] = targetMergeNodeIndex; + + // fix the child information for the target node, was a leaf before + mRuntimePool[targetMergeNodeIndex].mData = mTotalNbNodes << 1; + + // update the total number of nodes + mTotalNbNodes = mTotalNbNodes + 1 + treeParams.mNbNodes; +} + +// Merge tree into targetNode, where target node is not a leaf +// 1. Allocate new nodes/parent, copy the nodes/parents till targetNodePosIndex +// 2. Create new node , copy the data from target node +// 3. Copy the rest of the target tree nodes/parents at the end -> targetNodePosIndex + 1 + treeParams.mNbNodes +// 4. Copy the merge tree after the new node, create the parent map for them, update the leaf indices +// 5. Go through the nodes copied at the end and fix the parents/childs +// Schematic view: +// Target Nodes: ...Tn->...->Tc0,Tc1... +// Input tree: R1->Rc0, Rc1... +// Merged tree: ...Tn->...->Nc0,R1->Rc0,Rc1...,Tc0,Tc1... +// where new node: Nc0->...->Tc0,Tc1 +void AABBTree::mergeRuntimeNode(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& treeParams, PxU32 targetMergeNodeIndex) +{ + PX_ASSERT(mParentIndices); + PX_ASSERT(!targetNode.isLeaf()); + + // Get the target node child pos, this is where we insert the new node and the input tree + const PxU32 targetNodePosIndex = targetNode.getPosIndex(); + + // 1. Allocate new nodes/parent, copy the nodes/parents till targetNodePosIndex + // allocate new runtime pool with max combine number of nodes + // we allocate only 1 additional node each merge + AABBTreeRuntimeNode* newRuntimePool = PX_NEW(AABBTreeRuntimeNode)[mTotalNbNodes + treeParams.mNbNodes + 1]; + PxU32* newParentIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*(mTotalNbNodes + treeParams.mNbNodes + 1), "AABB parent indices")); + // copy the untouched part of the nodes and parents + PxMemCopy(newRuntimePool, mRuntimePool, sizeof(AABBTreeRuntimeNode)*(targetNodePosIndex)); + PxMemCopy(newParentIndices, mParentIndices, sizeof(PxU32)*(targetNodePosIndex)); + + PxU32 nodeIndex = targetNodePosIndex; + // 2. Create new node , copy the data from target node + newRuntimePool[nodeIndex].mBV = targetNode.mBV; + newRuntimePool[nodeIndex].mData = ((targetNode.mData >> 1) + 1 + treeParams.mNbNodes) << 1; + // update parent information + newParentIndices[nodeIndex] = targetMergeNodeIndex; + + // handle mark for refit + if(mRefitBitmask.getBits() && mRefitBitmask.isSet(targetMergeNodeIndex)) + { + mRefitBitmask.setBit(nodeIndex); + const PxU32 currentMarkedWord = nodeIndex >> 5; + mRefitHighestSetWord = PxMax(mRefitHighestSetWord, currentMarkedWord); + } + + // 3. Copy the rest of the target tree nodes/parents at the end -> targetNodePosIndex + 1 + treeParams.mNbNodes + if(mTotalNbNodes - targetNodePosIndex) + { + PX_ASSERT(mTotalNbNodes - targetNodePosIndex > 0); + PxMemCopy(newRuntimePool + targetNodePosIndex + 1 + treeParams.mNbNodes, mRuntimePool + targetNodePosIndex, sizeof(AABBTreeRuntimeNode)*(mTotalNbNodes - targetNodePosIndex)); + PxMemCopy(newParentIndices + targetNodePosIndex + 1 + treeParams.mNbNodes, mParentIndices + targetNodePosIndex, sizeof(PxU32)*(mTotalNbNodes - targetNodePosIndex)); + } + // swap the pointers, release the old memory + PX_DELETE_ARRAY(mRuntimePool); + mRuntimePool = newRuntimePool; + PX_FREE(mParentIndices); + mParentIndices = newParentIndices; + + // 4. Copy the merge tree after the new node, create the parent map for them, update the leaf indices + nodeIndex++; + addRuntimeChilds(nodeIndex, treeParams); + PX_ASSERT(nodeIndex == targetNodePosIndex + 1 + treeParams.mNbNodes); + // update the total number of nodes + mTotalNbNodes = mTotalNbNodes + 1 + treeParams.mNbNodes; + + // update the parent information for the input tree root node + mParentIndices[targetNodePosIndex + 1] = targetMergeNodeIndex; + + // 5. Go through the nodes copied at the end and fix the parents/childs + for (PxU32 i = targetNodePosIndex + 1 + treeParams.mNbNodes; i < mTotalNbNodes; i++) + { + // check if the parent is the targetNode, if yes update the parent to new node + if(mParentIndices[i] == targetMergeNodeIndex) + { + mParentIndices[i] = targetNodePosIndex; + } + else + { + // if parent node has been moved, update the parent node + if(mParentIndices[i] >= targetNodePosIndex) + { + mParentIndices[i] = mParentIndices[i] + 1 + treeParams.mNbNodes; + } + else + { + // if parent has not been moved, update its child information + const PxU32 parentIndex = mParentIndices[i]; + // update the child information to point to Pos child + if(i % 2 != 0) + { + const PxU32 srcNodeIndex = mRuntimePool[parentIndex].getPosIndex(); + // if child index points to a node that has been moved, update the child index + PX_ASSERT(!mRuntimePool[parentIndex].isLeaf()); + PX_ASSERT(srcNodeIndex > targetNodePosIndex); + mRuntimePool[parentIndex].mData = (1 + treeParams.mNbNodes + srcNodeIndex) << 1; + } + } + } + if(!mRuntimePool[i].isLeaf()) + { + // update the child node index + const PxU32 srcNodeIndex = 1 + treeParams.mNbNodes + mRuntimePool[i].getPosIndex(); + mRuntimePool[i].mData = srcNodeIndex << 1; + } + } +} + +// traverse the target node, the tree is inside the targetNode, and find the best place where merge the tree +void AABBTree::traverseRuntimeNode(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& treeParams, PxU32 nodeIndex) +{ + const AABBTreeRuntimeNode& srcNode = treeParams.getRootNode(); + PX_ASSERT(srcNode.mBV.isInside(targetNode.mBV)); + + // Check if the srcNode(tree) can fit inside any of the target childs. If yes, traverse the target tree child + AABBTreeRuntimeNode& targetPosChild = *targetNode.getPos(mRuntimePool); + if(srcNode.mBV.isInside(targetPosChild.mBV)) + { + return traverseRuntimeNode(targetPosChild, treeParams, targetNode.getPosIndex()); + } + + AABBTreeRuntimeNode& targetNegChild = *targetNode.getNeg(mRuntimePool); + if (srcNode.mBV.isInside(targetNegChild.mBV)) + { + return traverseRuntimeNode(targetNegChild, treeParams, targetNode.getNegIndex()); + } + + // we cannot traverse target anymore, lets add the srcTree to current target node + if(targetNode.isLeaf()) + mergeRuntimeLeaf(targetNode, treeParams, nodeIndex); + else + mergeRuntimeNode(targetNode, treeParams, nodeIndex); +} + +// Merge the input tree into current tree. +// Traverse the tree and find the smallest node, where the whole new tree fits. When we find the node +// we create one new node pointing to the original children and the to the input tree root. +void AABBTree::mergeTree(const AABBTreeMergeData& treeParams) +{ + // allocate new indices buffer + PxU32* newIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*(mNbIndices + treeParams.mNbIndices), "AABB tree indices")); + PxMemCopy(newIndices, mIndices, sizeof(PxU32)*mNbIndices); + PX_FREE(mIndices); + mIndices = newIndices; + mTotalPrims += treeParams.mNbIndices; + + // copy the new indices, re-index using the provided indicesOffset. Note that indicesOffset + // must be provided, as original mNbIndices can be different than indicesOffset dues to object releases. + for (PxU32 i = 0; i < treeParams.mNbIndices; i++) + { + mIndices[mNbIndices + i] = treeParams.mIndicesOffset + treeParams.mIndices[i]; + } + + // check the mRefitBitmask if we fit all the new nodes + mRefitBitmask.resize(mTotalNbNodes + treeParams.mNbNodes + 1); + + // create the parent information so we can update it + if(!mParentIndices) + { + mParentIndices = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mTotalNbNodes, "AABB parent indices")); + _createParentArray(mTotalNbNodes, mParentIndices, mRuntimePool, mRuntimePool, mRuntimePool); + } + + // if new tree is inside the root AABB we will traverse the tree to find better node where to attach the tree subnodes + // if the root is a leaf we merge with the root. + if(treeParams.getRootNode().mBV.isInside(mRuntimePool[0].mBV) && !mRuntimePool[0].isLeaf()) + { + traverseRuntimeNode(mRuntimePool[0], treeParams, 0); + } + else + { + if(mRuntimePool[0].isLeaf()) + { + mergeRuntimeLeaf(mRuntimePool[0], treeParams, 0); + } + else + { + mergeRuntimeNode(mRuntimePool[0], treeParams, 0); + } + + // increase the tree root AABB + mRuntimePool[0].mBV.include(treeParams.getRootNode().mBV); + } + +#ifdef _DEBUG + //verify parent indices + for (PxU32 i = 0; i < mTotalNbNodes; i++) + { + if (i) + { + PX_ASSERT(mRuntimePool[mParentIndices[i]].getPosIndex() == i || mRuntimePool[mParentIndices[i]].getNegIndex() == i); + } + if (!mRuntimePool[i].isLeaf()) + { + PX_ASSERT(mParentIndices[mRuntimePool[i].getPosIndex()] == i); + PX_ASSERT(mParentIndices[mRuntimePool[i].getNegIndex()] == i); + } + } + + // verify the tree nodes, leafs + for (PxU32 i = 0; i < mTotalNbNodes; i++) + { + if (mRuntimePool[i].isLeaf()) + { + const PxU32 index = mRuntimePool[i].mData >> 5; + const PxU32 nbPrim = mRuntimePool[i].getNbPrimitives(); + PX_ASSERT(index + nbPrim <= mNbIndices + treeParams.mNbIndices); + } + else + { + const PxU32 nodeIndex = (mRuntimePool[i].getPosIndex()); + PX_ASSERT(nodeIndex < mTotalNbNodes); + } + } +#endif // _DEBUG + + mNbIndices += treeParams.mNbIndices; +} + + + diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBTree.h b/src/PhysX/physx/source/scenequery/src/SqAABBTree.h new file mode 100644 index 000000000..bde284d65 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqAABBTree.h @@ -0,0 +1,259 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_AABBTREE_H +#define SQ_AABBTREE_H + +#include "foundation/PxMemory.h" +#include "foundation/PxBounds3.h" +#include "PsUserAllocated.h" +#include "PsVecMath.h" +#include "SqTypedef.h" +#include "GuAABBTreeBuild.h" +#include "PsArray.h" + +namespace physx +{ + +using namespace shdfnd::aos; + +namespace Sq +{ + class AABBTreeUpdateMap; + + typedef Ps::Pair TreeMergePair; + typedef Ps::Array TreeMergeMap; + + class BitArray + { + public: + BitArray() : mBits(NULL), mSize(0) {} + BitArray(PxU32 nb_bits) { init(nb_bits); } + ~BitArray() { PX_FREE_AND_RESET(mBits); mBits = NULL; } + + bool init(PxU32 nb_bits); + + // Data management + PX_FORCE_INLINE void setBit(PxU32 bit_number) + { + mBits[bit_number>>5] |= 1<<(bit_number&31); + } + PX_FORCE_INLINE void clearBit(PxU32 bit_number) + { + mBits[bit_number>>5] &= ~(1<<(bit_number&31)); + } + PX_FORCE_INLINE void toggleBit(PxU32 bit_number) + { + mBits[bit_number>>5] ^= 1<<(bit_number&31); + } + + PX_FORCE_INLINE void clearAll() { PxMemZero(mBits, mSize*4); } + PX_FORCE_INLINE void setAll() { PxMemSet(mBits, 0xff, mSize*4); } + + void resize(PxU32 maxBitNumber); + + // Data access + PX_FORCE_INLINE Ps::IntBool isSet(PxU32 bit_number) const + { + return Ps::IntBool(mBits[bit_number>>5] & (1<<(bit_number&31))); + } + + PX_FORCE_INLINE const PxU32* getBits() const { return mBits; } + PX_FORCE_INLINE PxU32 getSize() const { return mSize; } + + protected: + PxU32* mBits; //!< Array of bits + PxU32 mSize; //!< Size of the array in dwords + }; + + + //! AABB tree node used for runtime (smaller than for build) + class AABBTreeRuntimeNode : public Ps::UserAllocated + { + public: + PX_FORCE_INLINE AABBTreeRuntimeNode() {} + PX_FORCE_INLINE ~AABBTreeRuntimeNode() {} + + PX_FORCE_INLINE PxU32 isLeaf() const { return mData&1; } + + PX_FORCE_INLINE const PxU32* getPrimitives(const PxU32* base) const { return base + (mData>>5); } + PX_FORCE_INLINE PxU32* getPrimitives(PxU32* base) { return base + (mData>>5); } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return (mData>>1)&15; } + + PX_FORCE_INLINE PxU32 getPosIndex() const { return mData>>1; } + PX_FORCE_INLINE PxU32 getNegIndex() const { return (mData>>1) + 1; } + PX_FORCE_INLINE const AABBTreeRuntimeNode* getPos(const AABBTreeRuntimeNode* base) const { return base + (mData>>1); } + PX_FORCE_INLINE const AABBTreeRuntimeNode* getNeg(const AABBTreeRuntimeNode* base) const { const AABBTreeRuntimeNode* P = getPos(base); return P ? P+1 : NULL;} + + PX_FORCE_INLINE AABBTreeRuntimeNode* getPos(AABBTreeRuntimeNode* base) { return base + (mData >> 1); } + PX_FORCE_INLINE AABBTreeRuntimeNode* getNeg(AABBTreeRuntimeNode* base) { AABBTreeRuntimeNode* P = getPos(base); return P ? P + 1 : NULL; } + + PX_FORCE_INLINE PxU32 getNbRuntimePrimitives() const { return (mData>>1)&15; } + PX_FORCE_INLINE void setNbRunTimePrimitives(PxU32 val) + { + PX_ASSERT(val<16); + PxU32 data = mData & ~(15<<1); + data |= val<<1; + mData = data; + } + + PX_FORCE_INLINE void getAABBCenterExtentsV(Vec3V* center, Vec3V* extents) const + { + const Vec4V minV = V4LoadU(&mBV.minimum.x); + const Vec4V maxV = V4LoadU(&mBV.maximum.x); + + const float half = 0.5f; + const FloatV halfV = FLoad(half); + + *extents = Vec3V_From_Vec4V(V4Scale(V4Sub(maxV, minV), halfV)); + *center = Vec3V_From_Vec4V(V4Scale(V4Add(maxV, minV), halfV)); + } + + PX_FORCE_INLINE void getAABBCenterExtentsV2(Vec3V* center, Vec3V* extents) const + { + const Vec4V minV = V4LoadU(&mBV.minimum.x); + const Vec4V maxV = V4LoadU(&mBV.maximum.x); + + *extents = Vec3V_From_Vec4V(V4Sub(maxV, minV)); + *center = Vec3V_From_Vec4V(V4Add(maxV, minV)); + } + + PX_FORCE_INLINE void getAABBMinMaxV(Vec4V* minV, Vec4V* maxV) const + { + *minV = V4LoadU(&mBV.minimum.x); + *maxV = V4LoadU(&mBV.maximum.x); + } + + PxBounds3 mBV; // Global bounding-volume enclosing all the node-related primitives + PxU32 mData; // 27 bits node or prim index|4 bits #prims|1 bit leaf + }; + + //! Contains AABB-tree merge parameters + class AABBTreeMergeData + { + public: + AABBTreeMergeData(PxU32 nbNodes, const AABBTreeRuntimeNode* nodes, PxU32 nbIndices, const PxU32* indices, PxU32 indicesOffset) : + mNbNodes(nbNodes), mNodes(nodes), mNbIndices(nbIndices), mIndices(indices), mIndicesOffset(indicesOffset) + { + } + + ~AABBTreeMergeData() {} + + PX_FORCE_INLINE const AABBTreeRuntimeNode& getRootNode() const { return mNodes[0]; } + + public: + PxU32 mNbNodes; //!< Number of nodes of AABB tree merge + const AABBTreeRuntimeNode* mNodes; //!< Nodes of AABB tree merge + + PxU32 mNbIndices; //!< Number of indices of AABB tree merge + const PxU32* mIndices; //!< Indices of AABB tree merge + + PxU32 mIndicesOffset; //!< Indices offset from pruning pool + }; + + // Progressive building + class FIFOStack; + //~Progressive building + + //! AABB-tree, N primitives/leaf + class AABBTree : public Ps::UserAllocated + { + public: + AABBTree(); + ~AABBTree(); + // Build + bool build(Gu::AABBTreeBuildParams& params); + // Progressive building + PxU32 progressiveBuild(Gu::AABBTreeBuildParams& params, Gu::BuildStats& stats, PxU32 progress, PxU32 limit); + //~Progressive building + void release(bool clearRefitMap=true); + + // Merge tree with another one + void mergeTree(const AABBTreeMergeData& tree); + // Initialize tree from given merge data + void initTree(const AABBTreeMergeData& tree); + + // Data access + PX_FORCE_INLINE const PxU32* getIndices() const { return mIndices; } + PX_FORCE_INLINE PxU32* getIndices() { return mIndices; } + PX_FORCE_INLINE void setIndices(PxU32* indices) { mIndices = indices; } + PX_FORCE_INLINE PxU32 getNbNodes() const { return mTotalNbNodes; } + PX_FORCE_INLINE const AABBTreeRuntimeNode* getNodes() const { return mRuntimePool; } + PX_FORCE_INLINE AABBTreeRuntimeNode* getNodes() { return mRuntimePool; } + PX_FORCE_INLINE void setNodes(AABBTreeRuntimeNode* nodes) { mRuntimePool = nodes; } + PX_FORCE_INLINE PxU32 getTotalPrims() const { return mTotalPrims; } + +#if PX_DEBUG + void validate() const; +#endif + void shiftOrigin(const PxVec3& shift); + + // Shift indices of the tree by offset. Used for merged trees, when initial indices needs to be shifted to match indices in current pruning pool + void shiftIndices(PxU32 offset); + + private: + PxU32* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation). + PxU32 mNbIndices; //!< Nb indices + AABBTreeRuntimeNode* mRuntimePool; //!< Linear pool of nodes. + Gu::NodeAllocator mNodeAllocator; + PxU32* mParentIndices; //!< PT: hot/cold split, keep parent data in separate array + // Stats + PxU32 mTotalNbNodes; //!< Number of nodes in the tree. + PxU32 mTotalPrims; //!< Copy of final BuildStats::mTotalPrims + + // Progressive building + FIFOStack* mStack; + //~Progressive building + bool buildInit(Gu::AABBTreeBuildParams& params, Gu::BuildStats& stats); + void buildEnd(Gu::AABBTreeBuildParams& params, Gu::BuildStats& stats); + + // tree merge + void mergeRuntimeNode(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& tree, PxU32 targetNodeIndex); + void mergeRuntimeLeaf(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& tree, PxU32 targetNodeIndex); + void addRuntimeChilds(PxU32& nodeIndex, const AABBTreeMergeData& tree); + void traverseRuntimeNode(AABBTreeRuntimeNode& targetNode, const AABBTreeMergeData& tree, PxU32 nodeIndex); + // REFIT + public: + void fullRefit(const PxBounds3* boxes); + + // adds node[index] to a list of nodes to refit when refitMarkedNodes is called + // Note that this includes updating the hierarchy up the chain + void markNodeForRefit(TreeNodeIndex nodeIndex); + void refitMarkedNodes(const PxBounds3* boxes); + private: + BitArray mRefitBitmask; //!< bit is set for each node index in markForRefit + PxU32 mRefitHighestSetWord; + //~REFIT + }; + +} // namespace Sq + +} + +#endif // SQ_AABBTREE_H diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp b/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp new file mode 100644 index 000000000..e7512aaa6 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp @@ -0,0 +1,197 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "SqAABBTreeUpdateMap.h" +#include "SqAABBTree.h" + +using namespace physx; +using namespace Sq; + +static const PxU32 SHRINK_THRESHOLD = 1024; + +void AABBTreeUpdateMap::initMap(PxU32 nbObjects, const AABBTree& tree) +{ + if(!nbObjects) + { + release(); + return; + } + + // Memory management + { + const PxU32 mapSize = nbObjects; + const PxU32 targetCapacity = mapSize + (mapSize>>2); + + PxU32 currentCapacity = mMapping.capacity(); + if( ( targetCapacity < (currentCapacity>>1) ) && ( (currentCapacity-targetCapacity) > SHRINK_THRESHOLD ) ) + { + // trigger reallocation of a smaller array, there is enough memory to save + currentCapacity = 0; + } + + if(mapSize > currentCapacity) + { + // the mapping values are invalid and reset below in any case + // so there is no need to copy the values at all + mMapping.reset(); + mMapping.reserve(targetCapacity); // since size is 0, reserve will also just allocate + } + + mMapping.forceSize_Unsafe(mapSize); + + for(PxU32 i=0;igetNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= 16); + + // retrieve the primitives pointer + PxU32* primitives = node0->getPrimitives(tree.getIndices()); + PX_ASSERT(primitives); + + // PT: look for desired pool index in the leaf + bool foundIt = false; + for(PxU32 i=0;isetNbRunTimePrimitives(last); + primitives[i] = INVALID_POOL_ID; // Mark primitive index as invalid in the node + mMapping[prunerIndex0] = INVALID_NODE_ID; // invalidate the node index for pool 0 + + // PT: swap within the leaf node. No need to update the mapping since they should all point + // to the same tree node anyway. + if(last!=i) + Ps::swap(primitives[i], primitives[last]); + break; + } + } + PX_ASSERT(foundIt); + PX_UNUSED(foundIt); + } + + if (nodeIndex1!=INVALID_NODE_ID) + { + // PT: with multiple primitives per leaf, tree nodes may very well be the same for different pool indices. + // However the pool indices may be the same when a swap has been skipped in the pruning pool, in which + // case there is nothing to do. + if(prunerIndex0!=prunerIndex1) + { + PX_ASSERT(nodeIndex1 < tree.getNbNodes()); + PX_ASSERT(nodes[nodeIndex1].isLeaf()); + AABBTreeRuntimeNode* node1 = nodes + nodeIndex1; + const PxU32 nbPrims = node1->getNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= 16); + + // retrieve the primitives pointer + PxU32* primitives = node1->getPrimitives(tree.getIndices()); + PX_ASSERT(primitives); + + // PT: look for desired pool index in the leaf + bool foundIt = false; + for(PxU32 i=0;i mMapping; + }; + +} +} + +#endif diff --git a/src/PhysX/physx/source/scenequery/src/SqBounds.cpp b/src/PhysX/physx/source/scenequery/src/SqBounds.cpp new file mode 100644 index 000000000..a0f9cc0fa --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqBounds.cpp @@ -0,0 +1,75 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxTransform.h" +#include "SqBounds.h" +#include "CmTransformUtils.h" +#include "SqPruner.h" +#include "ScbShape.h" +#include "ScbActor.h" +#include "ScbRigidStatic.h" +#include "ScbBody.h" +#include "PsAllocator.h" +#include "GuBounds.h" + +using namespace physx; +using namespace Sq; + +void Sq::computeStaticWorldAABB(PxBounds3& bounds, const Scb::Shape& scbShape, const Scb::Actor& scbActor) +{ + const PxTransform& shape2Actor = scbShape.getShape2Actor(); + + PX_ALIGN(16, PxTransform) globalPose; + + Cm::getStaticGlobalPoseAligned(static_cast(scbActor).getActor2World(), shape2Actor, globalPose); + Gu::computeBounds(bounds, scbShape.getGeometry(), globalPose, 0.0f, NULL, SQ_PRUNER_INFLATION); +} + +void Sq::computeDynamicWorldAABB(PxBounds3& bounds, const Scb::Shape& scbShape, const Scb::Actor& scbActor) +{ + const PxTransform& shape2Actor = scbShape.getShape2Actor(); + + PX_ALIGN(16, PxTransform) globalPose; + { + const Scb::Body& body = static_cast(scbActor); + PX_ALIGN(16, PxTransform) kinematicTarget; + const PxU16 sqktFlags = PxRigidBodyFlag::eKINEMATIC | PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES; + const bool useTarget = (PxU16(body.getFlags()) & sqktFlags) == sqktFlags; + const PxTransform& body2World = (useTarget && body.getKinematicTarget(kinematicTarget)) ? kinematicTarget : body.getBody2World(); + Cm::getDynamicGlobalPoseAligned(body2World, shape2Actor, body.getBody2Actor(), globalPose); + } + + Gu::computeBounds(bounds, scbShape.getGeometry(), globalPose, 0.0f, NULL, SQ_PRUNER_INFLATION); +} + +const ComputeBoundsFunc Sq::gComputeBoundsTable[2] = +{ + computeStaticWorldAABB, + computeDynamicWorldAABB +}; diff --git a/src/PhysX/physx/source/scenequery/src/SqBounds.h b/src/PhysX/physx/source/scenequery/src/SqBounds.h new file mode 100644 index 000000000..592d13098 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqBounds.h @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_BOUNDS_H +#define SQ_BOUNDS_H + +#include "CmPhysXCommon.h" +#include "foundation/PxBounds3.h" +#include "PsVecMath.h" + +namespace physx +{ + namespace Scb + { + class Shape; + class Actor; + } + +namespace Sq +{ + void computeStaticWorldAABB(PxBounds3& bounds, const Scb::Shape& scbShape, const Scb::Actor& scbActor); + void computeDynamicWorldAABB(PxBounds3& bounds, const Scb::Shape& scbShape, const Scb::Actor& scbActor); + + typedef void(*ComputeBoundsFunc) (PxBounds3& bounds, const Scb::Shape& scbShape, const Scb::Actor& scbActor); + + extern const ComputeBoundsFunc gComputeBoundsTable[2]; + + // PT: TODO: - check that this is compatible with Gu::computeBounds(..., SQ_PRUNER_INFLATION, ...) + // PT: TODO: - refactor with "inflateBounds" in GuBounds.cpp if possible + // PT: TODO: - use SQ_PRUNER_INFLATION instead of hardcoding "0.01f" + PX_FORCE_INLINE void inflateBounds(PxBounds3& dst, const PxBounds3& src) + { + using namespace physx::shdfnd::aos; + + const Vec4V minV = V4LoadU(&src.minimum.x); + const Vec4V maxV = V4LoadU(&src.maximum.x); + const Vec4V eV = V4Scale(V4Sub(maxV, minV), FLoad(0.5f * 0.01f)); + + V4StoreU(V4Sub(minV, eV), &dst.minimum.x); + PX_ALIGN(16, PxVec4) max4; + V4StoreA(V4Add(maxV, eV), &max4.x); + dst.maximum = PxVec3(max4.x, max4.y, max4.z); + } + + // PT: the PX_MAX_BOUNDS_EXTENTS value is too large and produces INF floats when the box values are squared in + // some collision routines. Thus, for the SQ subsystem we use this alternative (smaller) value to mark empty bounds. + // See PX-954 for details. + #define SQ_EMPTY_BOUNDS_EXTENTS PxSqrt(0.25f * 1e33f) +} +} + +#endif // SQ_BOUNDS_H diff --git a/src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp new file mode 100644 index 000000000..b36d60432 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp @@ -0,0 +1,2599 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "SqBucketPruner.h" +#include "GuIntersectionBoxBox.h" +#include "GuInternal.h" +#include "PsVecMath.h" +#include "foundation/PxUnionCast.h" +#include "CmRadixSortBuffered.h" +#include "CmRenderOutput.h" +#include "PsFPU.h" +#include "PsBitUtils.h" +#include "PsIntrinsics.h" +#include "GuBounds.h" + +using namespace physx::shdfnd::aos; + +using namespace physx; +using namespace Sq; +using namespace Gu; +using namespace Ps; + +#define INVALID_HANDLE 0xffffffff + +/* +TODO: +- if Core is always available, mSortedObjects could be replaced with just indices to mCoreObjects => less memory. +- UTS: + - test that queries against empty boxes all return false +- invalidate after 16 removes +- check shiftOrigin stuff (esp what happens to emptied boxes) + - isn't there a very hard-to-find bug waiting to happen in there, + when the shift touches the empty box and overrides mdata0/mdata1 with "wrong" values that break the sort? +- revisit updateObject/removeObject +- optimize/cache computation of free global bounds before clipRay + +- remove temp memory buffers (sorted arrays) +- take care of code duplication +- better code to generate SIMD 0x7fffffff +- refactor SIMD tests +- optimize: + - better split values + - optimize update (bitmap, less data copy, etc) + - use ray limits in traversal code too? + - the SIMD XBOX code operates on Min/Max rather than C/E. Change format? + - or just try the alternative ray-box code (as on PC) ==> pretty much exactly the same speed +*/ + +//#define VERIFY_SORT +//#define BRUTE_FORCE_LIMIT 32 +#define LOCAL_SIZE 256 // Size of various local arrays. Dynamic allocations occur if exceeded. +#define USE_SIMD // Use SIMD code or not (sanity performance check) +#define NODE_SORT // Enable/disable node sorting +#define NODE_SORT_MIN_COUNT 16 // Limit above which node sorting is performed +#if PX_INTEL_FAMILY + #if COMPILE_VECTOR_INTRINSICS + #define CAN_USE_MOVEMASK + #endif +#endif + +#define ALIGN16(size) ((unsigned(size)+15) & unsigned(~15)) + +#ifdef _DEBUG + #define AlignedLoad V4LoadU + #define AlignedStore V4StoreU +#else + #define AlignedLoad V4LoadA + #define AlignedStore V4StoreA +#endif + +// SAT-based ray-box overlap test has accuracy issues for long rays, so we clip them against the global AABB to limit these issues. +static void clipRay(const PxVec3& rayOrig, const PxVec3& rayDir, float& maxDist, const PxVec3& boxMin, const PxVec3& boxMax) +{ + const PxVec3 boxCenter = (boxMax + boxMin)*0.5f; + const PxVec3 boxExtents = (boxMax - boxMin)*0.5f; + const float dpc = boxCenter.dot(rayDir); + const float extentsMagnitude = boxExtents.magnitude(); + const float dpMin = dpc - extentsMagnitude; + const float dpMax = dpc + extentsMagnitude; + const float dpO = rayOrig.dot(rayDir); + const float boxLength = extentsMagnitude * 2.0f; + const float distToBox = PxMin(PxAbs(dpMin - dpO), PxAbs(dpMax - dpO)); + maxDist = distToBox + boxLength * 2.0f; +} + +BucketPrunerNode::BucketPrunerNode() +{ + for(PxU32 i=0;i<5;i++) + mBucketBox[i].setEmpty(); +} + +static const PxU8 gCodes[] = { 4, 4, 4, 4, 4, 3, 2, 2, + 4, 1, 0, 0, 4, 1, 0, 0, + 4, 1, 0, 0, 2, 1, 0, 0, + 3, 1, 0, 0, 2, 1, 0, 0}; + +#ifdef CAN_USE_MOVEMASK +/*static PX_FORCE_INLINE PxU32 classifyBox_x86(const BucketBox& box, const PxVec4& limits, const bool useY, const bool isCrossBucket) +{ + const Vec4V extents = AlignedLoad(&box.mExtents.x); + const Vec4V center = AlignedLoad(&box.mCenter.x); + const Vec4V plus = V4Add(extents, center); + const Vec4V minus = V4Sub(extents, center); + + Vec4V tmp; + if(useY) // PT: this is a constant so branch prediction works here + tmp = _mm_shuffle_ps(plus, minus, _MM_SHUFFLE(0,1,0,1)); + else + tmp = _mm_shuffle_ps(plus, minus, _MM_SHUFFLE(0,2,0,2)); + + const Vec4V comp = _mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(0,2,1,3)); // oh well, nm + + const PxU32 Code = (PxU32)_mm_movemask_ps(V4IsGrtr(V4LoadA(&limits.x), comp)); + return gCodes[Code | PxU32(isCrossBucket)<<4]; +}*/ + +static PX_FORCE_INLINE PxU32 classifyBox_x86(const Vec4V boxMin, const Vec4V boxMax, const PxVec4& limits, const bool useY, const bool isCrossBucket) +{ + const Vec4V plus = boxMax; + const Vec4V minus = V4Neg(boxMin); + + Vec4V tmp; + if(useY) // PT: this is a constant so branch prediction works here + tmp = _mm_shuffle_ps(plus, minus, _MM_SHUFFLE(0,1,0,1)); + else + tmp = _mm_shuffle_ps(plus, minus, _MM_SHUFFLE(0,2,0,2)); + + const Vec4V comp = _mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(0,2,1,3)); // oh well, nm + + const PxU32 Code = PxU32(_mm_movemask_ps(V4IsGrtr(V4LoadA(&limits.x), comp))); + return gCodes[Code | PxU32(isCrossBucket)<<4]; +} +#endif + +#ifdef CAN_USE_MOVEMASK + #if PX_DEBUG + #define USE_CLASSIFY_BOX + #endif +#else + #define USE_CLASSIFY_BOX +#endif + +#ifdef USE_CLASSIFY_BOX +static PX_FORCE_INLINE PxU32 classifyBox(const BucketBox& box, const float limitX, const float limitYZ, const PxU32 yz, const bool isCrossBucket) +{ + const bool upperPart = (box.mCenter[yz] + box.mExtents[yz])limitYZ; + const bool leftPart = (box.mCenter.x + box.mExtents.x)limitX; + + // Table-based box classification avoids many branches + const PxU32 Code = PxU32(rightPart)|(PxU32(leftPart)<<1)|(PxU32(lowerPart)<<2)|(PxU32(upperPart)<<3); + return gCodes[Code + (isCrossBucket ? 16 : 0)]; +} +#endif + +void BucketPrunerNode::classifyBoxes( float limitX, float limitYZ, + PxU32 nb, BucketBox* PX_RESTRICT boxes, const PrunerPayload* PX_RESTRICT objects, + BucketBox* PX_RESTRICT sortedBoxes, PrunerPayload* PX_RESTRICT sortedObjects, + bool isCrossBucket, PxU32 sortAxis) +{ + const PxU32 yz = PxU32(sortAxis == 1 ? 2 : 1); + + #ifdef _DEBUG + { + float prev = boxes[0].mDebugMin; + for(PxU32 i=1;i=prev); + prev = current; + } + } + #endif + + // Local (stack-based) min/max bucket bounds + PX_ALIGN(16, PxVec4) bucketBoxMin[5]; + PX_ALIGN(16, PxVec4) bucketBoxMax[5]; + { + const PxBounds3 empty = PxBounds3::empty(); + for(PxU32 i=0;i<5;i++) + { + mCounters[i] = 0; + bucketBoxMin[i] = PxVec4(empty.minimum, 0.0f); + bucketBoxMax[i] = PxVec4(empty.maximum, 0.0f); + } + } + + { +#ifdef CAN_USE_MOVEMASK + // DS: order doesn't play nice with x86 shuffles :-| + PX_ALIGN(16, PxVec4) limits(-limitX, limitX, -limitYZ, limitYZ); + const bool useY = yz==1; +#endif + // Determine in which bucket each object falls, update bucket bounds + for(PxU32 i=0;i=prev); + prev = current; + } + } + } + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +static void processChildBuckets(PxU32 nbAllocated, + BucketBox* sortedBoxesInBucket, PrunerPayload* sortedObjectsInBucket, + const BucketPrunerNode& bucket, BucketPrunerNode* PX_RESTRICT childBucket, + BucketBox* PX_RESTRICT baseBucketsBoxes, PrunerPayload* PX_RESTRICT baseBucketsObjects, + PxU32 sortAxis) +{ + PX_UNUSED(nbAllocated); + + const PxU32 yz = PxU32(sortAxis == 1 ? 2 : 1); + for(PxU32 i=0;i<5;i++) + { + const PxU32 nbInBucket = bucket.mCounters[i]; + if(!nbInBucket) + { + childBucket[i].initCounters(); + continue; + } + BucketBox* bucketsBoxes = baseBucketsBoxes + bucket.mOffsets[i]; + PrunerPayload* bucketsObjects = baseBucketsObjects + bucket.mOffsets[i]; + PX_ASSERT(nbInBucket<=nbAllocated); + + const float limitX = bucket.mBucketBox[i].mCenter.x; + const float limitYZ = bucket.mBucketBox[i].mCenter[yz]; + const bool isCrossBucket = i==4; + childBucket[i].classifyBoxes(limitX, limitYZ, nbInBucket, bucketsBoxes, bucketsObjects, + sortedBoxesInBucket, sortedObjectsInBucket, + isCrossBucket, sortAxis); + + PxMemCopy(bucketsBoxes, sortedBoxesInBucket, sizeof(BucketBox)*nbInBucket); + PxMemCopy(bucketsObjects, sortedObjectsInBucket, sizeof(PrunerPayload)*nbInBucket); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxU32 encodeFloat(PxU32 newPos) +{ + //we may need to check on -0 and 0 + //But it should make no practical difference. + if(newPos & PX_SIGN_BITMASK) //negative? + return ~newPos;//reverse sequence of negative numbers + else + return newPos | PX_SIGN_BITMASK; // flip sign +} + +static PX_FORCE_INLINE void computeRayLimits(float& rayMin, float& rayMax, const PxVec3& rayOrig, const PxVec3& rayDir, float maxDist, PxU32 sortAxis) +{ + const float rayOrigValue = rayOrig[sortAxis]; + const float rayDirValue = rayDir[sortAxis] * maxDist; + rayMin = PxMin(rayOrigValue, rayOrigValue + rayDirValue); + rayMax = PxMax(rayOrigValue, rayOrigValue + rayDirValue); +} + +static PX_FORCE_INLINE void computeRayLimits(float& rayMin, float& rayMax, const PxVec3& rayOrig, const PxVec3& rayDir, float maxDist, const PxVec3& inflate, PxU32 sortAxis) +{ + const float inflateValue = inflate[sortAxis]; + const float rayOrigValue = rayOrig[sortAxis]; + const float rayDirValue = rayDir[sortAxis] * maxDist; + rayMin = PxMin(rayOrigValue, rayOrigValue + rayDirValue) - inflateValue; + rayMax = PxMax(rayOrigValue, rayOrigValue + rayDirValue) + inflateValue; +} + +static PX_FORCE_INLINE void encodeBoxMinMax(BucketBox& box, const PxU32 axis) +{ + const float min = box.mCenter[axis] - box.mExtents[axis]; + const float max = box.mCenter[axis] + box.mExtents[axis]; + + const PxU32* binaryMin = reinterpret_cast(&min); + const PxU32* binaryMax = reinterpret_cast(&max); + box.mData0 = encodeFloat(binaryMin[0]); + box.mData1 = encodeFloat(binaryMax[0]); +} + +/////////////////////////////////////////////////////////////////////////////// + +BucketPrunerCore::BucketPrunerCore(bool externalMemory) : + mCoreNbObjects (0), + mCoreCapacity (0), + mCoreBoxes (NULL), + mCoreObjects (NULL), + mCoreRemap (NULL), + mSortedWorldBoxes (NULL), + mSortedObjects (NULL), + mNbFree (0), + mSortedNb (0), + mSortedCapacity (0), + mSortAxis (0), + mDirty (true), + mOwnMemory (!externalMemory) +{ + mGlobalBox.setEmpty(); + + mLevel1.initCounters(); + + for(PxU32 i=0;i<5;i++) + mLevel2[i].initCounters(); + for(PxU32 j=0;j<5;j++) + for(PxU32 i=0;i<5;i++) + mLevel3[j][i].initCounters(); +} + +BucketPrunerCore::~BucketPrunerCore() +{ + release(); +} + +void BucketPrunerCore::release() +{ + mDirty = true; + mCoreNbObjects = 0; + + mCoreCapacity = 0; + if(mOwnMemory) + { + PX_FREE_AND_RESET(mCoreBoxes); + PX_FREE_AND_RESET(mCoreObjects); + PX_FREE_AND_RESET(mCoreRemap); + } + + PX_FREE_AND_RESET(mSortedWorldBoxes); + PX_FREE_AND_RESET(mSortedObjects); + mSortedNb = 0; + mSortedCapacity = 0; + + mNbFree = 0; +#ifdef USE_REGULAR_HASH_MAP + mMap.clear(); +#else + mMap.purge(); +#endif +} + +void BucketPrunerCore::setExternalMemory(PxU32 nbObjects, PxBounds3* boxes, PrunerPayload* objects) +{ + PX_ASSERT(!mOwnMemory); + mCoreNbObjects = nbObjects; + mCoreBoxes = boxes; + mCoreObjects = objects; + mCoreRemap = NULL; +} + +void BucketPrunerCore::allocateSortedMemory(PxU32 nb) +{ + mSortedNb = nb; + if(nb<=mSortedCapacity && (nb>=mSortedCapacity/2)) + return; + + const PxU32 capacity = Ps::nextPowerOfTwo(nb); + mSortedCapacity = capacity; + + PxU32 bytesNeededForBoxes = capacity*sizeof(BucketBox); + bytesNeededForBoxes = ALIGN16(bytesNeededForBoxes); + + PxU32 bytesNeededForObjects = capacity*sizeof(PrunerPayload); + bytesNeededForObjects = ALIGN16(bytesNeededForObjects); + + PX_FREE(mSortedObjects); + PX_FREE(mSortedWorldBoxes); + mSortedWorldBoxes = reinterpret_cast(PX_ALLOC(bytesNeededForBoxes, "BucketPruner")); + mSortedObjects = reinterpret_cast(PX_ALLOC(bytesNeededForObjects, "BucketPruner")); + PX_ASSERT(!(size_t(mSortedWorldBoxes)&15)); + PX_ASSERT(!(size_t(mSortedObjects)&15)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void BucketPrunerCore::resizeCore() +{ + const PxU32 capacity = mCoreCapacity ? mCoreCapacity*2 : 32; + mCoreCapacity = capacity; + + const PxU32 bytesNeededForBoxes = capacity*sizeof(PxBounds3); + const PxU32 bytesNeededForObjects = capacity*sizeof(PrunerPayload); + const PxU32 bytesNeededForRemap = capacity*sizeof(PxU32); + + PxBounds3* newCoreBoxes = reinterpret_cast(PX_ALLOC(bytesNeededForBoxes, "BucketPruner")); + PrunerPayload* newCoreObjects = reinterpret_cast(PX_ALLOC(bytesNeededForObjects, "BucketPruner")); + PxU32* newCoreRemap = reinterpret_cast(PX_ALLOC(bytesNeededForRemap, "BucketPruner")); + if(mCoreBoxes) + { + PxMemCopy(newCoreBoxes, mCoreBoxes, mCoreNbObjects*sizeof(PxBounds3)); + PX_FREE(mCoreBoxes); + } + if(mCoreObjects) + { + PxMemCopy(newCoreObjects, mCoreObjects, mCoreNbObjects*sizeof(PrunerPayload)); + PX_FREE(mCoreObjects); + } + if(mCoreRemap) + { + PxMemCopy(newCoreRemap, mCoreRemap, mCoreNbObjects*sizeof(PxU32)); + PX_FREE(mCoreRemap); + } + mCoreBoxes = newCoreBoxes; + mCoreObjects = newCoreObjects; + mCoreRemap = newCoreRemap; +} + +PX_FORCE_INLINE void BucketPrunerCore::addObjectInternal(const PrunerPayload& object, const PxBounds3& worldAABB, PxU32 timeStamp) +{ + if(mCoreNbObjects==mCoreCapacity) + resizeCore(); + + const PxU32 index = mCoreNbObjects++; + mCoreObjects[index] = object; + mCoreBoxes[index] = worldAABB; // PT: TODO: check assembly here + mCoreRemap[index] = 0xffffffff; + + // Objects are only inserted into the map once they're part of the main/core arrays. +#ifdef USE_REGULAR_HASH_MAP + bool ok = mMap.insert(object, BucketPrunerPair(index, timeStamp)); +#else + BucketPrunerPair* ok = mMap.addPair(object, index, timeStamp); +#endif + PX_UNUSED(ok); + PX_ASSERT(ok); +} + +bool BucketPrunerCore::addObject(const PrunerPayload& object, const PxBounds3& worldAABB, PxU32 timeStamp) +{ +/* + We should probably use a bigger Payload struct here, which would also contains the external handle. + (EDIT: we can't even do that, because of the setExternalMemory function) + When asked to update/remove an object it would be O(n) to find the proper object in the mSortedObjects array. + + - + + For removing it we can simply empty the corresponding box, and the object will never be returned from queries. + Maybe this isn't even true, since boxes are sorted along one axis. So marking a box as empty could break the code relying on a sorted order. + An alternative is to mark the external handle as invalid, and ignore the object when a hit is found. + + (EDIT: the sorting is now tested via data0/data1 anyway so we could mark the box as empty without breaking this) + + - + + For updating an object we would need to keep the (sub) array sorted (not the whole thing, only the array within a bucket). + We don't know the range (what part of the array maps to our bucket) but we may have the bucket ID somewhere? If we'd have this + we could parse the array left/right and resort just the right boxes. If we don't have this we may be able to "quickly" find the + range by traversing the tree, looking for the proper bucket. In any case I don't think there's a mapping to update within a bucket, + unlike in SAP or MBP. So we should be able to shuffle a bucket without having to update anything. For example there's no mapping + between the Core array and the Sorted array. It's a shame in a way because we'd need one, but it's not there - and in fact I think + we can free the Core array once Sorted is created, we don't need it at all. + + If we don't want to re-sort the full bucket we can just mark it as dirty and ignore the sort-based early exits in the queries. Then we + can incrementally resort it over N frames or something. + + This only works if the updated object remains in the same bucket though. If it moves to another bucket it becomes tempting to just remove + the object and re-insert it. + + - + + Now for adding an object, we can first have a "free pruner" and do the 16 next entries brute-force. Rebuilding every 16 objects might + give a good speedup already. Otherwise we need to do something more complicated. +*/ + + PX_ASSERT(mOwnMemory); + PX_ASSERT(!mDirty || !mNbFree); + if(!mDirty) + { + // In this path the structure is marked as valid. We do not want to invalidate it for each new object... + if(mNbFreesecond.mCoreIndex; + timeStamp = removedEntry->second.mTimeStamp; +#else + PxU32 coreIndex; // This is the object's index in the core arrays. + if(mMap.removePair(object, coreIndex, timeStamp)) + { +#endif + // In this codepath, the object we want to remove exists in the core arrays. + + // We will need to remove it from both the core arrays & the sorted arrays. + const PxU32 sortedIndex = mCoreRemap[coreIndex]; // This is the object's index in the sorted arrays. + +#ifdef USE_REGULAR_HASH_MAP + bool status = mMap.erase(object); + PX_ASSERT(status); + PX_UNUSED(status); +#endif + + // First let's deal with the core arrays + mCoreNbObjects--; + if(coreIndex!=mCoreNbObjects) + { + // If it wasn't the last object in the array, close the gaps as usual + const PrunerPayload& movedObject = mCoreObjects[mCoreNbObjects]; + mCoreBoxes[coreIndex] = mCoreBoxes[mCoreNbObjects]; + mCoreObjects[coreIndex] = movedObject; + mCoreRemap[coreIndex] = mCoreRemap[mCoreNbObjects]; + + // Since we just moved the last object, its index in the core arrays has changed. + // We must reflect this change in the map. +#ifdef USE_REGULAR_HASH_MAP + BucketPrunerMap::Entry* movedEntry = const_cast(mMap.find(movedObject)); + PX_ASSERT(movedEntry->second.mCoreIndex==mCoreNbObjects); + movedEntry->second.mCoreIndex = coreIndex; +#else + BucketPrunerPair* movedEntry = const_cast(mMap.findPair(movedObject)); + PX_ASSERT(movedEntry->mCoreIndex==mCoreNbObjects); + movedEntry->mCoreIndex = coreIndex; +#endif + } + + // Now, let's deal with the sorted arrays. + // If the structure is dirty, the sorted arrays will be rebuilt from scratch so there's no need to + // update them right now. + if(!mDirty) + { + // If the structure is valid, we want to keep it this way to avoid rebuilding sorted arrays after + // each removal. We can't "close the gaps" easily here because order of objects in the arrays matters. + + // Instead we just invalidate the object by setting its bounding box as empty. + // Queries against empty boxes will never return a hit, so this effectively "removes" the object + // from any subsequent query results. Sorted arrays now contain a "disabled" object, until next build. + + // Invalidating the box does not invalidate the sorting, since it's now captured in mData0/mData1. + // That is, mData0/mData1 keep their previous integer-encoded values, as if the box/object was still here. + mSortedWorldBoxes[sortedIndex].mCenter = PxVec3(0.0f); + mSortedWorldBoxes[sortedIndex].mExtents = PxVec3(-SQ_EMPTY_BOUNDS_EXTENTS); + // Note that we don't touch mSortedObjects here. We could, but this is not necessary. + } + return true; + } + + // Here, the object we want to remove exists in the free array. So we just parse it. + for(PxU32 i=0;i(mMap.find(movedObject)); + PX_ASSERT(movedEntry->second.mCoreIndex==coreNbObjects); + movedEntry->second.mCoreIndex = coreIndex; +#else + BucketPrunerPair* movedEntry = const_cast(mMap.findPair(movedObject)); + PX_ASSERT(movedEntry->mCoreIndex==coreNbObjects); + movedEntry->mCoreIndex = coreIndex; +#endif + } + + nbRemoved++; +#ifdef USE_REGULAR_HASH_MAP + bool status = mMap.erase(p.first); + PX_ASSERT(status); + PX_UNUSED(status); +#else + const PxU32 hashValue = hash(p.mPayload) & mMap.mMask; + mMap.removePairInternal(p.mPayload, hashValue, i); +#endif + nbActivePairs--; + } + else i++; + } + mCoreNbObjects = coreNbObjects; + +#ifdef USE_REGULAR_HASH_MAP +#else + mMap.shrinkMemory(); +#endif + } + + // PT: ...then we look in the 'free' array + PxU32 i=0; + while(i0); + Vec4V mergedMinV = V4LoadU(&boxes[nb-1].minimum.x); + Vec4V mergedMaxV = Vec4V_From_Vec3V(V3LoadU(&boxes[nb-1].maximum.x)); + for(PxU32 i=0;i(sortedObjects); + for(PxU32 i=0;i + PX_CUDA_CALLABLE PX_FORCE_INLINE void tswap(T& x, T& y) + { + T tmp = x; + x = y; + y = tmp; + } + +/* PX_FORCE_INLINE __m128 DotV(const __m128 a, const __m128 b) + { + const __m128 dot1 = _mm_mul_ps(a, b); + const __m128 shuf1 = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dot1), _MM_SHUFFLE(0,0,0,0))); + const __m128 shuf2 = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dot1), _MM_SHUFFLE(1,1,1,1))); + const __m128 shuf3 = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dot1), _MM_SHUFFLE(2,2,2,2))); + return _mm_add_ps(_mm_add_ps(shuf1, shuf2), shuf3); + }*/ + +// PT: hmmm, by construction, isn't the order always the same for all bucket pruners? +// => maybe not because the bucket boxes are still around the merged aabbs, not around the bucket +// Still we could do something here +static /*PX_FORCE_INLINE*/ PxU32 sort(const BucketPrunerNode& parent, const PxVec3& rayDir) +{ + const PxU32 totalCount = parent.mCounters[0]+parent.mCounters[1]+parent.mCounters[2]+parent.mCounters[3]+parent.mCounters[4]; + if(totalCount(dp); + const PxU32* values = PxUnionCast(dp); + + PxU32 value0 = values[0]; + PxU32 value1 = values[1]; + PxU32 value2 = values[2]; + PxU32 value3 = values[3]; + PxU32 value4 = values[4]; + + for(PxU32 j=0;j<5-1;j++) + { + if(value1(dp); + +// const PxU32 mask = ~7U; + const PxU32 mask = 0x7ffffff8; + PxU32 value0 = (values[0]&mask); + PxU32 value1 = (values[1]&mask)|1; + PxU32 value2 = (values[2]&mask)|2; + PxU32 value3 = (values[3]&mask)|3; + PxU32 value4 = (values[4]&mask)|4; + +#define SORT_BLOCK \ + if(value1(PX_ALLOC(nb*sizeof(size_t), "")); +for(PxU32 i=0;iLOCAL_SIZE) + { + tempObjects = reinterpret_cast(PX_ALLOC(sizeof(PrunerPayload)*nb, "BucketPruner")); + tempBoxes = reinterpret_cast(PX_ALLOC(nb*sizeof(BucketBox), "BucketPruner")); + } + else + { + tempObjects = localTempObjects; + tempBoxes = localTempBoxes; + } + + mSortAxis = sortBoxes(nb, mCoreBoxes, mCoreObjects, mGlobalBox, tempBoxes, tempObjects); + + PX_ASSERT(mSortAxis); + + allocateSortedMemory(nb); + BucketBox* sortedBoxes = mSortedWorldBoxes; + PrunerPayload* sortedObjects = mSortedObjects; + + const PxU32 yz = PxU32(mSortAxis == 1 ? 2 : 1); + const float limitX = mGlobalBox.mCenter.x; + const float limitYZ = mGlobalBox.mCenter[yz]; + mLevel1.classifyBoxes(limitX, limitYZ, nb, tempBoxes, tempObjects, + sortedBoxes, sortedObjects, + false, mSortAxis); + + processChildBuckets(nb, tempBoxes, tempObjects, + mLevel1, mLevel2, mSortedWorldBoxes, mSortedObjects, + mSortAxis); + + for(PxU32 j=0;j<5;j++) + processChildBuckets(nb, tempBoxes, tempObjects, + mLevel2[j], mLevel3[j], mSortedWorldBoxes + mLevel1.mOffsets[j], mSortedObjects + mLevel1.mOffsets[j], + mSortAxis); + + { + for(PxU32 i=0;iLOCAL_SIZE) + { + PX_FREE(tempBoxes); + PX_FREE(tempObjects); + } + +for(PxU32 i=0;i(&MaskI)), DataV); + _mm_store_ps(&rayParams->mData.x, DataV); + _mm_store_ps(&rayParams->mData2.x, Data2V); + _mm_store_ps(&rayParams->mFDir.x, FDirV); + #else + const PxVec3 data = 0.5f * rayDir * maxDist; + rayParams->mData = data; + rayParams->mData2 = rayOrig + data; + rayParams->mFDir.x = PxAbs(data.x); + rayParams->mFDir.y = PxAbs(data.y); + rayParams->mFDir.z = PxAbs(data.z); + #endif + } + + template + static PX_FORCE_INLINE IntBool _segmentAABB(const BucketBox& box, const RayParams* PX_RESTRICT params) + { + #ifdef USE_SIMD + const PxU32 maskI = 0x7fffffff; + const __m128 fdirV = _mm_load_ps(¶ms->mFDir.x); +// #ifdef _DEBUG + const __m128 extentsV = inflateT ? _mm_add_ps(_mm_loadu_ps(&box.mExtents.x), _mm_load_ps(¶ms->mInflate.x)) : _mm_loadu_ps(&box.mExtents.x); + const __m128 DV = _mm_sub_ps(_mm_load_ps(¶ms->mData2.x), _mm_loadu_ps(&box.mCenter.x)); +/* #else + const __m128 extentsV = inflateT ? _mm_add_ps(_mm_load_ps(&box.mExtents.x), _mm_load_ps(¶ms->mInflate.x)) : _mm_load_ps(&box.mExtents.x); + const __m128 DV = _mm_sub_ps(_mm_load_ps(¶ms->mData2.x), _mm_load_ps(&box.mCenter.x)); + #endif*/ + __m128 absDV = _mm_and_ps(DV, _mm_load1_ps(reinterpret_cast(&maskI))); + absDV = _mm_cmpgt_ps(absDV, _mm_add_ps(extentsV, fdirV)); + const PxU32 test = PxU32(_mm_movemask_ps(absDV)); + if(test&7) + return 0; + + const __m128 dataZYX_V = _mm_load_ps(¶ms->mData.x); + const __m128 dataXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(dataZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 DXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(DV), _MM_SHUFFLE(3,0,2,1))); + const __m128 fV = _mm_sub_ps(_mm_mul_ps(dataZYX_V, DXZY_V), _mm_mul_ps(dataXZY_V, DV)); + + const __m128 fdirZYX_V = _mm_load_ps(¶ms->mFDir.x); + const __m128 fdirXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(fdirZYX_V), _MM_SHUFFLE(3,0,2,1))); + const __m128 extentsXZY_V = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(extentsV), _MM_SHUFFLE(3,0,2,1))); + const __m128 fg = _mm_add_ps(_mm_mul_ps(extentsV, fdirXZY_V), _mm_mul_ps(extentsXZY_V, fdirZYX_V)); + + __m128 absfV = _mm_and_ps(fV, _mm_load1_ps(reinterpret_cast(&maskI))); + absfV = _mm_cmpgt_ps(absfV, fg); + const PxU32 test2 = PxU32(_mm_movemask_ps(absfV)); + if(test2&7) + return 0; + return 1; + #else + const float boxExtentsx = inflateT ? box.mExtents.x + params->mInflate.x : box.mExtents.x; + const float Dx = params->mData2.x - box.mCenter.x; if(fabsf(Dx) > boxExtentsx + params->mFDir.x) return IntFalse; + + const float boxExtentsz = inflateT ? box.mExtents.z + params->mInflate.z : box.mExtents.z; + const float Dz = params->mData2.z - box.mCenter.z; if(fabsf(Dz) > boxExtentsz + params->mFDir.z) return IntFalse; + + const float boxExtentsy = inflateT ? box.mExtents.y + params->mInflate.y : box.mExtents.y; + const float Dy = params->mData2.y - box.mCenter.y; if(fabsf(Dy) > boxExtentsy + params->mFDir.y) return IntFalse; + + float f; + f = params->mData.y * Dz - params->mData.z * Dy; if(fabsf(f) > boxExtentsy*params->mFDir.z + boxExtentsz*params->mFDir.y) return IntFalse; + f = params->mData.z * Dx - params->mData.x * Dz; if(fabsf(f) > boxExtentsx*params->mFDir.z + boxExtentsz*params->mFDir.x) return IntFalse; + f = params->mData.x * Dy - params->mData.y * Dx; if(fabsf(f) > boxExtentsx*params->mFDir.y + boxExtentsy*params->mFDir.x) return IntFalse; + return IntTrue; + #endif + } +#else + #include "GuBVHTestsSIMD.h" + + typedef RayAABBTest BPRayAABBTest; + +template +static PX_FORCE_INLINE IntBool _segmentAABB(const BucketBox& box, const BPRayAABBTest& test) +{ + return static_cast(test.check(V3LoadU(box.mCenter), V3LoadU(box.mExtents))); +} + +/*static PX_FORCE_INLINE IntBool _segmentAABB(const BucketBox& box, const BPRayAABBTest& test, PxU32 rayMinLimitX, PxU32 rayMaxLimitX) +{ + if(rayMinLimitX>box.mData1) + return 0; + if(rayMaxLimitX +static PxAgain processBucket( + PxU32 nb, const BucketBox* PX_RESTRICT baseBoxes, PrunerPayload* PX_RESTRICT baseObjects, + PxU32 offset, PxU32 totalAllocated, + const PxVec3& rayOrig, const PxVec3& rayDir, float& maxDist, +#ifdef CAN_USE_MOVEMASK + RayParams* PX_RESTRICT rayParams, +#else + BPRayAABBTest& test, const PxVec3& inflate, +#endif + PrunerCallback& pcb, PxU32& _rayMinLimitInt, PxU32& _rayMaxLimitInt, PxU32 sortAxis) +{ + PX_UNUSED(totalAllocated); + + const BucketBox* PX_RESTRICT _boxes = baseBoxes + offset; + PrunerPayload* PX_RESTRICT _objects = baseObjects + offset; + + PxU32 rayMinLimitInt = _rayMinLimitInt; + PxU32 rayMaxLimitInt = _rayMaxLimitInt; + + const BucketBox* last = _boxes + nb; + + while(_boxes!=last) + { + const BucketBox& currentBox = *_boxes++; + PrunerPayload* currentObject = _objects++; + + if(currentBox.mData1rayMaxLimitInt) + goto Exit; + +#ifdef CAN_USE_MOVEMASK + if(!_segmentAABB(currentBox, rayParams)) + continue; +#else + if(!_segmentAABB(currentBox, test)) + continue; +#endif + + const float MaxDist = maxDist; + const PxAgain again = pcb.invoke(maxDist, *currentObject); + if(!again) + return false; + if(maxDist < MaxDist) + { + float rayMinLimit, rayMaxLimit; +#ifdef CAN_USE_MOVEMASK + if(inflateT) + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, rayParams->mInflate, sortAxis); + else + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, sortAxis); + + precomputeRayData(rayParams, rayOrig, rayDir, maxDist); +#else + if(inflateT) + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, inflate, sortAxis); + else + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, sortAxis); + + test.setDistance(maxDist); +#endif + const PxU32* binaryMinLimit = reinterpret_cast(&rayMinLimit); + const PxU32* binaryMaxLimit = reinterpret_cast(&rayMaxLimit); + rayMinLimitInt = encodeFloat(binaryMinLimit[0]); + rayMaxLimitInt = encodeFloat(binaryMaxLimit[0]); + } + } +Exit: + + _rayMinLimitInt = rayMinLimitInt; + _rayMaxLimitInt = rayMaxLimitInt; + return true; +} + +#ifdef NODE_SORT +static PxU32 computeDirMask(const PxVec3& dir) +{ + const PxU32* binary = reinterpret_cast(&dir.x); + const PxU32 X = (binary[0])>>31; + const PxU32 Y = (binary[1])>>31; + const PxU32 Z = (binary[2])>>31; + return Z|(Y<<1)|(X<<2); +} +#endif + +template +static PxAgain stab(const BucketPrunerCore& core, PrunerCallback& pcb, const PxVec3& rayOrig, const PxVec3& rayDir, float& maxDist, const PxVec3 inflate) +{ + const PxU32 nb = core.mSortedNb; + if(!nb && !core.mNbFree) + return true; + + if(maxDist==PX_MAX_F32) + { + /*const*/ PxVec3 boxMin = core.mGlobalBox.getMin() - inflate; + /*const*/ PxVec3 boxMax = core.mGlobalBox.getMax() + inflate; + + if(core.mNbFree) + { + // TODO: optimize this + PxBounds3 freeGlobalBounds; + freeGlobalBounds.setEmpty(); + for(PxU32 i=0;i(tmp, &rayParams)) +#else + if(_segmentAABB(tmp, test)) +#endif + { + if(!pcb.invoke(maxDist, core.mFreeObjects[i])) + return false; + } + } + + if(!nb) + return true; + +#ifdef CAN_USE_MOVEMASK + if(!_segmentAABB(core.mGlobalBox, &rayParams)) + return true; +#else + if(!_segmentAABB(core.mGlobalBox, test)) + return true; +#endif + + const PxU32 sortAxis = core.mSortAxis; + float rayMinLimit, rayMaxLimit; + if(inflateT) + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, inflate, sortAxis); + else + computeRayLimits(rayMinLimit, rayMaxLimit, rayOrig, rayDir, maxDist, sortAxis); + + const PxU32* binaryMinLimit = reinterpret_cast(&rayMinLimit); + const PxU32* binaryMaxLimit = reinterpret_cast(&rayMaxLimit); + PxU32 rayMinLimitInt = encodeFloat(binaryMinLimit[0]); + PxU32 rayMaxLimitInt = encodeFloat(binaryMaxLimit[0]); +/* +float rayMinLimitX, rayMaxLimitX; +if(inflateT) + computeRayLimits(rayMinLimitX, rayMaxLimitX, rayOrig, rayDir, maxDist, inflate, 0); +else + computeRayLimits(rayMinLimitX, rayMaxLimitX, rayOrig, rayDir, maxDist, 0); + +PxU32 rayMinLimitIntX = encodeFloat(PX_IR(rayMinLimitX)); +PxU32 rayMaxLimitIntX = encodeFloat(PX_IR(rayMaxLimitX)); +*/ + + float currentDist = maxDist; + +#ifdef NODE_SORT + const PxU32 dirIndex = computeDirMask(rayDir); + PxU32 orderi = core.mLevel1.mOrder[dirIndex]; +// PxU32 orderi = sort(core.mLevel1, rayDir); + + for(PxU32 i_=0;i_<5;i_++) + { + const PxU32 i = orderi&7; orderi>>=3; +#else + for(PxU32 i=0;i<5;i++) + { +#endif + +#ifdef CAN_USE_MOVEMASK + if(core.mLevel1.mCounters[i] && _segmentAABB(core.mLevel1.mBucketBox[i], &rayParams)) +#else + if(core.mLevel1.mCounters[i] && _segmentAABB(core.mLevel1.mBucketBox[i], test)) +// if(core.mLevel1.mCounters[i] && _segmentAABB(core.mLevel1.mBucketBox[i], test, rayMinLimitIntX, rayMaxLimitIntX)) +#endif + { + +#ifdef NODE_SORT + PxU32 orderj = core.mLevel2[i].mOrder[dirIndex]; +// PxU32 orderj = sort(core.mLevel2[i], rayDir); + + for(PxU32 j_=0;j_<5;j_++) + { + const PxU32 j = orderj&7; orderj>>=3; +#else + for(PxU32 j=0;j<5;j++) + { +#endif + +#ifdef CAN_USE_MOVEMASK + if(core.mLevel2[i].mCounters[j] && _segmentAABB(core.mLevel2[i].mBucketBox[j], &rayParams)) +#else + if(core.mLevel2[i].mCounters[j] && _segmentAABB(core.mLevel2[i].mBucketBox[j], test)) +// if(core.mLevel2[i].mCounters[j] && _segmentAABB(core.mLevel2[i].mBucketBox[j], test, rayMinLimitIntX, rayMaxLimitIntX)) +#endif + { + const BucketPrunerNode& parent = core.mLevel3[i][j]; + const PxU32 parentOffset = core.mLevel1.mOffsets[i] + core.mLevel2[i].mOffsets[j]; + +#ifdef NODE_SORT + PxU32 orderk = parent.mOrder[dirIndex]; +// PxU32 orderk = sort(parent, rayDir); + + for(PxU32 k_=0;k_<5;k_++) + { + const PxU32 k = orderk&7; orderk>>=3; +#else + for(PxU32 k=0;k<5;k++) + { +#endif + const PxU32 nbInBucket = parent.mCounters[k]; +#ifdef CAN_USE_MOVEMASK + if(nbInBucket && _segmentAABB(parent.mBucketBox[k], &rayParams)) +#else + if(nbInBucket && _segmentAABB(parent.mBucketBox[k], test)) +// if(nbInBucket && _segmentAABB(parent.mBucketBox[k], test, rayMinLimitIntX, rayMaxLimitIntX)) +#endif + { + const PxU32 offset = parentOffset + parent.mOffsets[k]; + const PxAgain again = processBucket( nbInBucket, core.mSortedWorldBoxes, core.mSortedObjects, + offset, core.mSortedNb, + rayOrig, rayDir, currentDist, +#ifdef CAN_USE_MOVEMASK + &rayParams, +#else + test, inflate, +#endif + pcb, + rayMinLimitInt, rayMaxLimitInt, + sortAxis); + if(!again) + return false; + } + } + } + } + } + } + + maxDist = currentDist; + return true; +} + +PxAgain BucketPrunerCore::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + return ::stab<0>(*this, pcb, origin, unitDir, inOutDistance, PxVec3(0.0f)); +} + +PxAgain BucketPrunerCore::sweep(const ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + const PxVec3 extents = queryVolume.getPrunerInflatedWorldAABB().getExtents(); + return ::stab<1>(*this, pcb, queryVolume.getPrunerInflatedWorldAABB().getCenter(), unitDir, inOutDistance, extents); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +static PX_FORCE_INLINE bool processBucket( PxU32 nb, const BucketBox* PX_RESTRICT baseBoxes, PrunerPayload* PX_RESTRICT baseObjects, + PxU32 offset, PxU32 totalAllocated, + const Test& test, PrunerCallback& pcb, + PxU32 minLimitInt, PxU32 maxLimitInt) +{ + PX_UNUSED(totalAllocated); + + const BucketBox* PX_RESTRICT boxes = baseBoxes + offset; + PrunerPayload* PX_RESTRICT objects = baseObjects + offset; + + while(nb--) + { + const BucketBox& currentBox = *boxes++; + PrunerPayload* currentObject = objects++; + + if(currentBox.mData1maxLimitInt) + { + if(doAssert) + PX_ASSERT(!test(currentBox)); + return true; + } + + if(test(currentBox)) + { + PxReal dist = -1.0f; // no distance for overlaps + if(!pcb.invoke(dist, *currentObject)) + return false; + } + } + return true; +} + +template +class BucketPrunerOverlapTraversal +{ +public: + PX_FORCE_INLINE BucketPrunerOverlapTraversal() {} + + /*PX_FORCE_INLINE*/ bool operator()(const BucketPrunerCore& core, const Test& test, PrunerCallback& pcb, const PxBounds3& cullBox) const + { + for(PxU32 i=0;i(&boxMinLimit); + const PxU32* binaryMaxLimit = reinterpret_cast(&boxMaxLimit); + const PxU32 rayMinLimitInt = encodeFloat(binaryMinLimit[0]); + const PxU32 rayMaxLimitInt = encodeFloat(binaryMaxLimit[0]); + + for(PxU32 i=0;i<5;i++) + { + if(core.mLevel1.mCounters[i] && test(core.mLevel1.mBucketBox[i])) + { + for(PxU32 j=0;j<5;j++) + { + if(core.mLevel2[i].mCounters[j] && test(core.mLevel2[i].mBucketBox[j])) + { + for(PxU32 k=0;k<5;k++) + { + const PxU32 nbInBucket = core.mLevel3[i][j].mCounters[k]; + if(nbInBucket && test(core.mLevel3[i][j].mBucketBox[k])) + { + const PxU32 offset = core.mLevel1.mOffsets[i] + core.mLevel2[i].mOffsets[j] + core.mLevel3[i][j].mOffsets[k]; + if(!processBucket(nbInBucket, core.mSortedWorldBoxes, core.mSortedObjects, + offset, core.mSortedNb, test, pcb, rayMinLimitInt, rayMaxLimitInt)) + return false; + } + } + } + } + } + } + return true; + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef CAN_USE_MOVEMASK +PX_FORCE_INLINE PxU32 BAllTrue3_R(const BoolV a) +{ + const PxI32 moveMask = _mm_movemask_ps(a); + return PxU32((moveMask & 0x7) == (0x7)); +} +#endif + +#ifdef USE_SIMD +struct SphereAABBTest_SIMD +{ + PX_FORCE_INLINE SphereAABBTest_SIMD(const Gu::Sphere& sphere) : + #ifdef CAN_USE_MOVEMASK + mCenter (V4LoadU(&sphere.center.x)), + #else + mCenter (V3LoadU(sphere.center)), + #endif + mRadius2(FLoad(sphere.radius * sphere.radius)) + {} + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + #ifdef CAN_USE_MOVEMASK + const Vec4V boxCenter = AlignedLoad(&box.mCenter.x); + const Vec4V boxExtents = AlignedLoad(&box.mExtents.x); + // + const Vec4V offset = V4Sub(mCenter, boxCenter); + const Vec4V closest = V4Clamp(offset, V4Neg(boxExtents), boxExtents); + const Vec4V d = V4Sub(offset, closest); + + const FloatV dot = V4Dot3(d,d); + return Ps::IntBool(BAllTrue3_R(FIsGrtrOrEq(mRadius2, dot))); + #else + const Vec3V boxCenter = V3LoadU(box.mCenter); + const Vec3V boxExtents = V3LoadU(box.mExtents); + // + const Vec3V offset = V3Sub(mCenter, boxCenter); + const Vec3V closest = V3Clamp(offset, V3Neg(boxExtents), boxExtents); + const Vec3V d = V3Sub(offset, closest); + return Ps::IntBool(BAllEqTTTT(FIsGrtrOrEq(mRadius2, V3Dot(d, d)))); + #endif + } + + PX_FORCE_INLINE Ps::IntBool operator()(const PxBounds3& bounds) const + { + BucketBox tmp; + tmp.mCenter = bounds.getCenter(); + tmp.mExtents = bounds.getExtents(); + return (*this)(tmp); + } + +private: + SphereAABBTest_SIMD& operator=(const SphereAABBTest_SIMD&); + #ifdef CAN_USE_MOVEMASK + const Vec4V mCenter; + #else + const Vec3V mCenter; + #endif + const FloatV mRadius2; +}; +#else +struct SphereAABBTest_Scalar +{ + PX_FORCE_INLINE SphereAABBTest_Scalar(const Gu::Sphere& sphere) : + mCenter (sphere.center), + mRadius2(sphere.radius * sphere.radius) + {} + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + const PxVec3 minimum = box.getMin(); + const PxVec3 maximum = box.getMax(); + + float d = 0.0f; + + //find the square of the distance + //from the sphere to the box + for(PxU32 i=0;i<3;i++) + { + if(mCenter[i]maximum[i]) + { + const float s = mCenter[i] - maximum[i]; + d += s*s; + } + } + return d <= mRadius2; + } + +private: + SphereAABBTest_Scalar& operator=(const SphereAABBTest_Scalar&); + const PxVec3 mCenter; + float mRadius2; +}; +#endif + +#ifdef USE_SIMD +typedef SphereAABBTest_SIMD BucketPrunerSphereAABBTest; +#else +typedef SphereAABBTest_Scalar BucketPrunerSphereAABBTest; +#endif + +/////////////////////////////////////////////////////////////////////////////// + +struct BucketPrunerAABBAABBTest +{ + PX_FORCE_INLINE BucketPrunerAABBAABBTest(const PxBounds3& queryBox) : mBox(queryBox) {} + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + // PT: we don't use PxBounds3::intersects() because isValid() asserts on our empty boxes! + const PxVec3 bucketMin = box.getMin(); + const PxVec3 bucketMax = box.getMax(); + return !(mBox.minimum.x > bucketMax.x || bucketMin.x > mBox.maximum.x || + mBox.minimum.y > bucketMax.y || bucketMin.y > mBox.maximum.y || + mBox.minimum.z > bucketMax.z || bucketMin.z > mBox.maximum.z); + } + + PX_FORCE_INLINE Ps::IntBool operator()(const PxBounds3& bounds) const + { + // PT: we don't use PxBounds3::intersects() because isValid() asserts on our empty boxes! + const PxVec3& bucketMin = bounds.minimum; + const PxVec3& bucketMax = bounds.maximum; + return !(mBox.minimum.x > bucketMax.x || bucketMin.x > mBox.maximum.x || + mBox.minimum.y > bucketMax.y || bucketMin.y > mBox.maximum.y || + mBox.minimum.z > bucketMax.z || bucketMin.z > mBox.maximum.z); + } +private: + BucketPrunerAABBAABBTest& operator=(const BucketPrunerAABBAABBTest&); + const PxBounds3 mBox; +}; + +/*struct BucketPrunerAABBAABBTest_SIMD +{ + PX_FORCE_INLINE BucketPrunerAABBAABBTest_SIMD(const PxBounds3& b) + : mCenter(V3LoadU(b.getCenter())) + , mExtents(V3LoadU(b.getExtents())) + {} + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + return V3AllGrtrOrEq(V3Add(mExtents, AlignedLoad(&box.mExtents.x)), V3Abs(V3Sub(AlignedLoad(&box.mCenter.x), mCenter))); + } +private: + BucketPrunerAABBAABBTest_SIMD& operator=(const BucketPrunerAABBAABBTest_SIMD&); + const Vec3V mCenter, mExtents; +};*/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef USE_SIMD +struct OBBAABBTest_SIMD +{ + OBBAABBTest_SIMD(const PxMat33& rotation, const PxVec3& translation, const PxVec3& extents) + { + const Vec3V eps = V3Load(1e-6f); + + mT = V3LoadU(translation); + mExtents = V3LoadU(extents); + + // storing the transpose matrices yields a simpler SIMD test + mRT = Mat33V_From_PxMat33(rotation.getTranspose()); + mART = Mat33V(V3Add(V3Abs(mRT.col0), eps), V3Add(V3Abs(mRT.col1), eps), V3Add(V3Abs(mRT.col2), eps)); + mBB_xyz = M33TrnspsMulV3(mART, mExtents); + +/* if(fullTest) + { + const Vec3V eYZX = V3PermYZX(mExtents), eZXY = V3PermZXY(mExtents); + + mBB_123 = V3MulAdd(eYZX, V3PermZXY(mART.col0), V3Mul(eZXY, V3PermYZX(mART.col0))); + mBB_456 = V3MulAdd(eYZX, V3PermZXY(mART.col1), V3Mul(eZXY, V3PermYZX(mART.col1))); + mBB_789 = V3MulAdd(eYZX, V3PermZXY(mART.col2), V3Mul(eZXY, V3PermYZX(mART.col2))); + }*/ + } + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + const Vec3V extentsV = V3LoadU(box.mExtents); + + const Vec3V t = V3Sub(mT, V3LoadU(box.mCenter)); + + // class I - axes of AABB + if(V3OutOfBounds(t, V3Add(extentsV, mBB_xyz))) + return Ps::IntFalse; + + const Vec3V rX = mRT.col0, rY = mRT.col1, rZ = mRT.col2; + const Vec3V arX = mART.col0, arY = mART.col1, arZ = mART.col2; + + const FloatV eX = V3GetX(extentsV), eY = V3GetY(extentsV), eZ = V3GetZ(extentsV); + const FloatV tX = V3GetX(t), tY = V3GetY(t), tZ = V3GetZ(t); + + // class II - axes of OBB + { + const Vec3V v = V3ScaleAdd(rZ, tZ, V3ScaleAdd(rY, tY, V3Scale(rX, tX))); + const Vec3V v2 = V3ScaleAdd(arZ, eZ, V3ScaleAdd(arY, eY, V3ScaleAdd(arX, eX, mExtents))); + if(V3OutOfBounds(v, v2)) + return Ps::IntFalse; + } + +// if(!fullTest) + return Ps::IntTrue; + +/* // class III - edge cross products. Almost all OBB tests early-out with type I or type II, + // so early-outs here probably aren't useful (TODO: profile) + + const Vec3V va = V3NegScaleSub(rZ, tY, V3Scale(rY, tZ)); + const Vec3V va2 = V3ScaleAdd(arY, eZ, V3ScaleAdd(arZ, eY, mBB_123)); + const BoolV ba = BOr(V3IsGrtr(va, va2), V3IsGrtr(V3Neg(va2), va)); + + const Vec3V vb = V3NegScaleSub(rX, tZ, V3Scale(rZ, tX)); + const Vec3V vb2 = V3ScaleAdd(arX, eZ, V3ScaleAdd(arZ, eX, mBB_456)); + const BoolV bb = BOr(V3IsGrtr(vb, vb2), V3IsGrtr(V3Neg(vb2), vb)); + + const Vec3V vc = V3NegScaleSub(rY, tX, V3Scale(rX, tY)); + const Vec3V vc2 = V3ScaleAdd(arX, eY, V3ScaleAdd(arY, eX, mBB_789)); + const BoolV bc = BOr(V3IsGrtr(vc, vc2), V3IsGrtr(V3Neg(vc2), vc)); + + return BAllEq(BOr(ba, BOr(bb,bc)), BFFFF());*/ + } + + PX_FORCE_INLINE Ps::IntBool operator()(const PxBounds3& bounds) const + { + BucketBox tmp; + tmp.mCenter = bounds.getCenter(); + tmp.mExtents = bounds.getExtents(); + return (*this)(tmp); + } + + Vec3V mExtents; // extents of OBB + Vec3V mT; // translation of OBB + Mat33V mRT; // transpose of rotation matrix of OBB + Mat33V mART; // transpose of mRT, padded by epsilon + Vec3V mBB_xyz; // extents of OBB along coordinate axes + +/* Vec3V mBB_123; // projections of extents onto edge-cross axes + Vec3V mBB_456; + Vec3V mBB_789;*/ +}; +#else +struct OBBAABBTest_Scalar +{ + OBBAABBTest_Scalar(const PxMat33& rotation, const PxVec3& translation, const PxVec3& extents) + { + mR = rotation; + mT = translation; + mExtents = extents; + + const PxVec3 eps(1e-6f); + mAR = PxMat33(mR[0].abs() + eps, mR[1].abs() + eps, mR[2].abs() + eps); // Epsilon prevents floating-point inaccuracies (strategy borrowed from RAPID) + mBB_xyz = mAR.transform(mExtents); // Precompute box-box data - Courtesy of Erwin de Vries + +/* PxReal ex = mExtents.x, ey = mExtents.y, ez = mExtents.z; + mBB_1 = ey*mAR[2].x + ez*mAR[1].x; mBB_2 = ez*mAR[0].x + ex*mAR[2].x; mBB_3 = ex*mAR[1].x + ey*mAR[0].x; + mBB_4 = ey*mAR[2].y + ez*mAR[1].y; mBB_5 = ez*mAR[0].y + ex*mAR[2].y; mBB_6 = ex*mAR[1].y + ey*mAR[0].y; + mBB_7 = ey*mAR[2].z + ez*mAR[1].z; mBB_8 = ez*mAR[0].z + ex*mAR[2].z; mBB_9 = ex*mAR[1].z + ey*mAR[0].z;*/ + } + + PX_FORCE_INLINE Ps::IntBool operator()(const BucketBox& box) const + { + const PxVec3& c = box.mCenter; + const PxVec3& e = box.mExtents; + + const PxVec3 T = mT - c; + // Class I : A's basis vectors + if(PxAbs(T.x) > e.x + mBB_xyz.x) return Ps::IntFalse; + if(PxAbs(T.y) > e.y + mBB_xyz.y) return Ps::IntFalse; + if(PxAbs(T.z) > e.z + mBB_xyz.z) return Ps::IntFalse; + + // Class II : B's basis vectors + if(PxAbs(T.dot(mR[0])) > e.dot(mAR[0]) + mExtents.x) return Ps::IntFalse; + if(PxAbs(T.dot(mR[1])) > e.dot(mAR[1]) + mExtents.y) return Ps::IntFalse; + if(PxAbs(T.dot(mR[2])) > e.dot(mAR[2]) + mExtents.z) return Ps::IntFalse; + + // Class III : 9 cross products + if(0) + { + if(PxAbs(T.z*mR[0].y - T.y*mR[0].z) > e.y*mAR[0].z + e.z*mAR[0].y + mBB_1) return Ps::IntFalse; // L = A0 x B0 + if(PxAbs(T.z*mR[1].y - T.y*mR[1].z) > e.y*mAR[1].z + e.z*mAR[1].y + mBB_2) return Ps::IntFalse; // L = A0 x B1 + if(PxAbs(T.z*mR[2].y - T.y*mR[2].z) > e.y*mAR[2].z + e.z*mAR[2].y + mBB_3) return Ps::IntFalse; // L = A0 x B2 + + if(PxAbs(T.x*mR[0].z - T.z*mR[0].x) > e.x*mAR[0].z + e.z*mAR[0].x + mBB_4) return Ps::IntFalse; // L = A1 x B0 + if(PxAbs(T.x*mR[1].z - T.z*mR[1].x) > e.x*mAR[1].z + e.z*mAR[1].x + mBB_5) return Ps::IntFalse; // L = A1 x B1 + if(PxAbs(T.x*mR[2].z - T.z*mR[2].x) > e.x*mAR[2].z + e.z*mAR[2].x + mBB_6) return Ps::IntFalse; // L = A1 x B2 + + if(PxAbs(T.y*mR[0].x - T.x*mR[0].y) > e.x*mAR[0].y + e.y*mAR[0].x + mBB_7) return Ps::IntFalse; // L = A2 x B0 + if(PxAbs(T.y*mR[1].x - T.x*mR[1].y) > e.x*mAR[1].y + e.y*mAR[1].x + mBB_8) return Ps::IntFalse; // L = A2 x B1 + if(PxAbs(T.y*mR[2].x - T.x*mR[2].y) > e.x*mAR[2].y + e.y*mAR[2].x + mBB_9) return Ps::IntFalse; // L = A2 x B2 + } + return Ps::IntTrue; + } + +private: + PxMat33 mR; // rotation matrix + PxMat33 mAR; // absolute rotation matrix + PxVec3 mT; // translation from obb space to model space + PxVec3 mExtents; + + PxVec3 mBB_xyz; + + float mBB_1, mBB_2, mBB_3; + float mBB_4, mBB_5, mBB_6; + float mBB_7, mBB_8, mBB_9; +}; +#endif + +#ifdef USE_SIMD +typedef OBBAABBTest_SIMD BucketPrunerOBBAABBTest; +#else +typedef OBBAABBTest_Scalar BucketPrunerOBBAABBTest; +#endif + +/////////////////////////////////////////////////////////////////////////////// + +PxAgain BucketPrunerCore::overlap(const ShapeData& queryVolume, PrunerCallback& pcb) const +{ + PX_ASSERT(!mDirty); + PxAgain again = true; + + const PxBounds3& cullBox = queryVolume.getPrunerInflatedWorldAABB(); + + switch(queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if(queryVolume.isOBB()) + { + const BucketPrunerOverlapTraversal overlap; + again = overlap(*this, + BucketPrunerOBBAABBTest( + queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerWorldPos(), + queryVolume.getPrunerBoxGeomExtentsInflated()), + pcb, cullBox); + } + else + { + const BucketPrunerOverlapTraversal overlap; + again = overlap(*this, BucketPrunerAABBAABBTest(cullBox), pcb, cullBox); + } + } + break; + + case PxGeometryType::eCAPSULE: + { + const BucketPrunerOverlapTraversal overlap; + again = overlap(*this, + BucketPrunerOBBAABBTest( + queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerWorldPos(), + queryVolume.getPrunerBoxGeomExtentsInflated()), + pcb, cullBox); + } + break; + + case PxGeometryType::eSPHERE: + { + const Sphere& sphere = queryVolume.getGuSphere(); + const PxVec3 sphereExtents(sphere.radius); + const BucketPrunerOverlapTraversal overlap; + again = overlap(*this, BucketPrunerSphereAABBTest(sphere), pcb, cullBox); + } + break; + + case PxGeometryType::eCONVEXMESH: + { + const BucketPrunerOverlapTraversal overlap; + again = overlap(*this, + BucketPrunerOBBAABBTest( + queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerWorldPos(), + queryVolume.getPrunerBoxGeomExtentsInflated()), + pcb, cullBox); + } + break; + + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + return again; +} + +/////////////////////////////////////////////////////////////////////////////// + +void BucketPrunerCore::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i=0;i the pair is persistent + return &activePairs[offset]; +} + +// Internal version saving hash computation +PX_FORCE_INLINE BucketPrunerPair* BucketPrunerMap::findPair(const PrunerPayload& payload, PxU32 hashValue) const +{ + if(!mHashTable) + return NULL; // Nothing has been allocated yet + + BucketPrunerPair* PX_RESTRICT activePairs = mActivePairs; + const PxU32* PX_RESTRICT next = mNext; + + // Look for it in the table + PxU32 offset = mHashTable[hashValue]; + while(offset!=INVALID_ID && differentPair(activePairs[offset], payload)) + { + offset = next[offset]; // Better to have a separate array for this + } + if(offset==INVALID_ID) + return NULL; + PX_ASSERT(offset the pair is persistent + return &activePairs[offset]; +} + +/////////////////////////////////////////////////////////////////////////////// + +BucketPrunerPair* BucketPrunerMap::addPair(const PrunerPayload& payload, PxU32 coreIndex, PxU32 timeStamp) +{ + PxU32 hashValue = hash(payload) & mMask; + + { + BucketPrunerPair* PX_RESTRICT p = findPair(payload, hashValue); + if(p) + { + PX_ASSERT(p->mCoreIndex==coreIndex); + PX_ASSERT(p->mTimeStamp==timeStamp); + return p; // Persistent pair + } + } + + // This is a new pair + if(mNbActivePairs >= mHashSize) + { + // Get more entries + mHashSize = Ps::nextPowerOfTwo(mNbActivePairs+1); + mMask = mHashSize-1; + + reallocPairs(); + + // Recompute hash value with new hash size + hashValue = hash(payload) & mMask; // ### redundant hash computation here? + } + + BucketPrunerPair* PX_RESTRICT p = &mActivePairs[mNbActivePairs]; + p->mPayload = payload; + p->mCoreIndex = coreIndex; + p->mTimeStamp = timeStamp; + mNext[mNbActivePairs] = mHashTable[hashValue]; + mHashTable[hashValue] = mNbActivePairs++; + return p; +} + +/////////////////////////////////////////////////////////////////////////////// + +void BucketPrunerMap::removePairInternal(const PrunerPayload& /*payload*/, PxU32 hashValue, PxU32 pairIndex) +{ + // Walk the hash table to fix mNext + { + PxU32 offset = mHashTable[hashValue]; + PX_ASSERT(offset!=INVALID_ID); + + PxU32 previous=INVALID_ID; + while(offset!=pairIndex) + { + previous = offset; + offset = mNext[offset]; + } + + // Let us go/jump us + if(previous!=INVALID_ID) + { + PX_ASSERT(mNext[previous]==pairIndex); + mNext[previous] = mNext[pairIndex]; + } + // else we were the first + else mHashTable[hashValue] = mNext[pairIndex]; + // we're now free to reuse mNext[pairIndex] without breaking the list + } +#if PX_DEBUG + mNext[pairIndex]=INVALID_ID; +#endif + // Invalidate entry + + // Fill holes + if(1) + { + // 1) Remove last pair + const PxU32 lastPairIndex = mNbActivePairs-1; + if(lastPairIndex==pairIndex) + { + mNbActivePairs--; + } + else + { + const BucketPrunerPair* last = &mActivePairs[lastPairIndex]; + const PxU32 lastHashValue = hash(last->mPayload) & mMask; + + // Walk the hash table to fix mNext + PxU32 offset = mHashTable[lastHashValue]; + PX_ASSERT(offset!=INVALID_ID); + + PxU32 previous=INVALID_ID; + while(offset!=lastPairIndex) + { + previous = offset; + offset = mNext[offset]; + } + + // Let us go/jump us + if(previous!=INVALID_ID) + { + PX_ASSERT(mNext[previous]==lastPairIndex); + mNext[previous] = mNext[lastPairIndex]; + } + // else we were the first + else mHashTable[lastHashValue] = mNext[lastPairIndex]; + // we're now free to reuse mNext[lastPairIndex] without breaking the list + +#if PX_DEBUG + mNext[lastPairIndex]=INVALID_ID; +#endif + + // Don't invalidate entry since we're going to shrink the array + + // 2) Re-insert in free slot + mActivePairs[pairIndex] = mActivePairs[lastPairIndex]; +#if PX_DEBUG + PX_ASSERT(mNext[pairIndex]==INVALID_ID); +#endif + mNext[pairIndex] = mHashTable[lastHashValue]; + mHashTable[lastHashValue] = pairIndex; + + mNbActivePairs--; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool BucketPrunerMap::removePair(const PrunerPayload& payload, PxU32& coreIndex, PxU32& timeStamp) +{ + const PxU32 hashValue = hash(payload) & mMask; + const BucketPrunerPair* p = findPair(payload, hashValue); + if(!p) + return false; + PX_ASSERT(p->mPayload==payload); + + coreIndex = p->mCoreIndex; + timeStamp = p->mTimeStamp; + + removePairInternal(payload, hashValue, getPairIndex(p)); + + shrinkMemory(); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void BucketPrunerMap::shrinkMemory() +{ + // Check correct memory against actually used memory + const PxU32 correctHashSize = Ps::nextPowerOfTwo(mNbActivePairs); + if(mHashSize==correctHashSize) + return; + + if(mReservedMemory && correctHashSize < mReservedMemory) + return; + + // Reduce memory used + mHashSize = correctHashSize; + mMask = mHashSize-1; + + reallocPairs(); +} + +/////////////////////////////////////////////////////////////////////////////// + + static PX_FORCE_INLINE void storeDwords(PxU32* dest, PxU32 nb, PxU32 value) + { + while(nb--) + *dest++ = value; + } + +void BucketPrunerMap::reallocPairs() +{ + MBP_FREE(mHashTable); + mHashTable = reinterpret_cast(MBP_ALLOC(mHashSize*sizeof(PxU32))); + storeDwords(mHashTable, mHashSize, INVALID_ID); + + // Get some bytes for new entries + BucketPrunerPair* newPairs = reinterpret_cast(MBP_ALLOC(mHashSize * sizeof(BucketPrunerPair))); + PX_ASSERT(newPairs); + + PxU32* newNext = reinterpret_cast(MBP_ALLOC(mHashSize * sizeof(PxU32))); + PX_ASSERT(newNext); + + // Copy old data if needed + if(mNbActivePairs) + PxMemCopy(newPairs, mActivePairs, mNbActivePairs*sizeof(BucketPrunerPair)); + // ### check it's actually needed... probably only for pairs whose hash value was cut by the and + // yeah, since hash(id0, id1) is a constant + // However it might not be needed to recompute them => only less efficient but still ok + for(PxU32 i=0;i 3 bits/index => 3*5=15 bits total, for each of the 8 canonical directions + }PX_ALIGN_SUFFIX(16); + + PX_FORCE_INLINE PxU32 hash(const PrunerPayload& payload) + { +#if PX_P64_FAMILY +// const PxU32 h0 = Ps::hash((const void*)payload.data[0]); +// const PxU32 h1 = Ps::hash((const void*)payload.data[1]); + const PxU32 h0 = PxU32(PX_MAX_U32 & payload.data[0]); + const PxU32 h1 = PxU32(PX_MAX_U32 & payload.data[1]); + return Ps::hash(PxU64(h0)|(PxU64(h1)<<32)); +#else + return Ps::hash(PxU64(payload.data[0])|(PxU64(payload.data[1])<<32)); +#endif + } + +#ifdef USE_REGULAR_HASH_MAP + struct BucketPrunerPair : public Ps::UserAllocated + { + PX_FORCE_INLINE BucketPrunerPair() {} + PX_FORCE_INLINE BucketPrunerPair(PxU32 index, PxU32 stamp) : mCoreIndex(index), mTimeStamp(stamp) {} + PxU32 mCoreIndex; // index in mCoreObjects + PxU32 mTimeStamp; + }; + typedef Ps::HashMap BucketPrunerMap; +#else + struct BucketPrunerPair : public Ps::UserAllocated + { + PrunerPayload mPayload; + PxU32 mCoreIndex; // index in mCoreObjects + PxU32 mTimeStamp; + }; + + // Custom hash-map - currently faster than the regular hash-map (Ps::HashMap), in particular for 'find-and-erase' operations. + class BucketPrunerMap : public Ps::UserAllocated + { + public: + BucketPrunerMap(); + ~BucketPrunerMap(); + + void purge(); + void shrinkMemory(); + + BucketPrunerPair* addPair (const PrunerPayload& payload, PxU32 coreIndex, PxU32 timeStamp); + bool removePair (const PrunerPayload& payload, PxU32& coreIndex, PxU32& timeStamp); + const BucketPrunerPair* findPair (const PrunerPayload& payload) const; + PX_FORCE_INLINE PxU32 getPairIndex (const BucketPrunerPair* pair) const + { + return (PxU32((size_t(pair) - size_t(mActivePairs)))/sizeof(BucketPrunerPair)); + } + + PxU32 mHashSize; + PxU32 mMask; + PxU32 mNbActivePairs; + PxU32* mHashTable; + PxU32* mNext; + BucketPrunerPair* mActivePairs; + PxU32 mReservedMemory; + + PX_FORCE_INLINE BucketPrunerPair* findPair(const PrunerPayload& payload, PxU32 hashValue) const; + void removePairInternal(const PrunerPayload& payload, PxU32 hashValue, PxU32 pairIndex); + void reallocPairs(); + void reserveMemory(PxU32 memSize); + }; +#endif + + class BucketPrunerCore : public Ps::UserAllocated + { + public: + BucketPrunerCore(bool externalMemory=true); + ~BucketPrunerCore(); + + void release(); + + void setExternalMemory(PxU32 nbObjects, PxBounds3* boxes, PrunerPayload* objects); + + bool addObject(const PrunerPayload& object, const PxBounds3& worldAABB, PxU32 timeStamp=0); + bool removeObject(const PrunerPayload& object, PxU32& timeStamp); + bool updateObject(const PxBounds3& worldAABB, const PrunerPayload& object); + + // PT: look for objects marked with input timestamp everywhere in the structure, and remove them. This is the same + // as calling 'removeObject' individually for all these objects, but much more efficient. Returns number of removed objects. + PxU32 removeMarkedObjects(PxU32 timeStamp); + + PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + + void shiftOrigin(const PxVec3& shift); + + void visualize(Cm::RenderOutput& out, PxU32 color) const; + + PX_FORCE_INLINE void build() { classifyBoxes(); } + + PX_FORCE_INLINE PxU32 getNbObjects() const { return mNbFree + mCoreNbObjects; } + +// private: + PxU32 mCoreNbObjects; // Current number of objects in core arrays + PxU32 mCoreCapacity; // Capacity of core arrays + PxBounds3* mCoreBoxes; // Core array + PrunerPayload* mCoreObjects; // Core array + PxU32* mCoreRemap; // Remaps core index to sorted index, i.e. sortedIndex = mCoreRemap[coreIndex] + + BucketBox* mSortedWorldBoxes; // Sorted array + PrunerPayload* mSortedObjects; // Sorted array + + PxU32 mNbFree; // Current number of objects in the "free array" (mFreeObjects/mFreeBounds) + PrunerPayload mFreeObjects[FREE_PRUNER_SIZE]; // mNbFree objects are stored here + PxBounds3 mFreeBounds[FREE_PRUNER_SIZE]; // mNbFree object bounds are stored here + PxU32 mFreeStamps[FREE_PRUNER_SIZE]; + + BucketPrunerMap mMap; // Maps (PrunerPayload) object to corresponding index in core array. + // Objects in the free array do not appear in this map. + PxU32 mSortedNb; + PxU32 mSortedCapacity; + PxU32 mSortAxis; + + BucketBox mGlobalBox; // Global bounds around all objects in the structure (except the ones in the "free" array) + BucketPrunerNode mLevel1; + BucketPrunerNode mLevel2[5]; + BucketPrunerNode mLevel3[5][5]; + + bool mDirty; + bool mOwnMemory; + private: + void classifyBoxes(); + void allocateSortedMemory(PxU32 nb); + void resizeCore(); + PX_FORCE_INLINE void addObjectInternal(const PrunerPayload& object, const PxBounds3& worldAABB, PxU32 timeStamp); + }; + +#if PX_VC + #pragma warning(pop) +#endif + + class BucketPruner : public Pruner + { + public: + BucketPruner(); + virtual ~BucketPruner(); + + // Pruner + virtual bool addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* payload, PxU32 count, bool); + virtual void removeObjects(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count); + virtual void commit(); + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual const PrunerPayload& getPayload(PrunerHandle handle) const { return mPool.getPayload(handle); } + virtual const PrunerPayload& getPayload(PrunerHandle handle, PxBounds3*& bounds) const { return mPool.getPayload(handle, bounds); } + virtual void preallocate(PxU32 entries) { mPool.preallocate(entries); } + virtual void shiftOrigin(const PxVec3& shift); + virtual void visualize(Cm::RenderOutput& out, PxU32 color) const; + // merge not implemented for bucket pruner + virtual void merge(const void* ) {} + //~Pruner + + private: + BucketPrunerCore mCore; + PruningPool mPool; + }; + +} // namespace Sq + +} + +#endif // SQ_BUCKETPRUNER_H diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp new file mode 100644 index 000000000..214633c7b --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp @@ -0,0 +1,598 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsFoundation.h" +#include "SqCompoundPruner.h" +#include "SqIncrementalAABBTree.h" +#include "SqPruningPool.h" +#include "GuAABBTreeQuery.h" +#include "GuSphere.h" +#include "GuBox.h" +#include "GuCapsule.h" +#include "GuBounds.h" +#include "GuBVHStructure.h" + + +using namespace physx; +using namespace Gu; +using namespace Sq; +using namespace Cm; + +#define PARANOIA_CHECKS 0 + +/////////////////////////////////////////////////////////////////////////////////////////////// + +BVHCompoundPruner::BVHCompoundPruner() +{ + mCompoundTreePool.preallocate(32); + mMainTreeUpdateMap.resizeUninitialized(32); + mPoolActorMap.resizeUninitialized(32); + mChangedLeaves.reserve(32); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +BVHCompoundPruner::~BVHCompoundPruner() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool BVHCompoundPruner::addCompound(PrunerHandle* results, const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& transform, CompoundFlag::Enum flags, const PrunerPayload* userData) +{ + PX_ASSERT(bvhStructure.getNbBounds()); + + const PxBounds3 compoundBounds = PxBounds3::transformFast(transform, bvhStructure.getNodes()->mBV); + const PoolIndex poolIndex = mCompoundTreePool.addCompound(results, bvhStructure, compoundBounds, transform, flags, userData); + + mChangedLeaves.clear(); + IncrementalAABBTreeNode* node = mMainTree.insert(poolIndex, mCompoundTreePool.getCurrentCompoundBounds(), mChangedLeaves); + updateMapping(poolIndex, node); + + mActorPoolMap[compoundId] = poolIndex; + mPoolActorMap[poolIndex] = compoundId; + +#if PARANOIA_CHECKS + test(); +#endif + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node) +{ + // resize mapping if needed + if(mMainTreeUpdateMap.size() <= poolIndex) + { + const PxU32 resizeSize = mMainTreeUpdateMap.size() * 2; + mMainTreeUpdateMap.resize(resizeSize); + mPoolActorMap.resize(resizeSize); + } + + // if a node was split we need to update the node indices and also the sibling indices + if(!mChangedLeaves.empty()) + { + if(node && node->isLeaf()) + { + for(PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + mMainTreeUpdateMap[node->getPrimitives(NULL)[j]] = node; + } + } + + for(PxU32 i = 0; i < mChangedLeaves.size(); i++) + { + IncrementalAABBTreeNode* changedNode = mChangedLeaves[i]; + PX_ASSERT(changedNode->isLeaf()); + + for(PxU32 j = 0; j < changedNode->getNbPrimitives(); j++) + { + mMainTreeUpdateMap[changedNode->getPrimitives(NULL)[j]] = changedNode; + } + } + } + else + { + mMainTreeUpdateMap[poolIndex] = node; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::removeCompound(PrunerCompoundId compoundId) +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + + if(poolIndexEntry) + { + const PoolIndex poolIndex = poolIndexEntry->second; + const PoolIndex poolRelocatedLastIndex = mCompoundTreePool.removeCompound(poolIndex); + + IncrementalAABBTreeNode* node = mMainTree.remove(mMainTreeUpdateMap[poolIndex], poolIndex, mCompoundTreePool.getCurrentCompoundBounds()); + // if node moved to its parent + if(node && node->isLeaf()) + { + for (PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + mMainTreeUpdateMap[index] = node; + } + } + + // fix indices if we made a swap + if(poolRelocatedLastIndex != poolIndex) + { + mMainTreeUpdateMap[poolIndex] = mMainTreeUpdateMap[poolRelocatedLastIndex]; + mMainTree.fixupTreeIndices(mMainTreeUpdateMap[poolIndex], poolRelocatedLastIndex, poolIndex); + + mActorPoolMap[mPoolActorMap[poolRelocatedLastIndex]] = poolIndex; + mPoolActorMap[poolIndex] = mPoolActorMap[poolRelocatedLastIndex]; + } + + mActorPoolMap.erase(compoundId); + } + +#if PARANOIA_CHECKS + test(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::updateCompound(PrunerCompoundId compoundId, const PxTransform& transform) +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + + if(poolIndexEntry) + { + const PxU32 poolIndex = poolIndexEntry->second; + PxBounds3 localBounds; + const IncrementalAABBTreeNode* node = mCompoundTreePool.getCompoundTrees()[poolIndex].mTree->getNodes(); + mCompoundTreePool.getCompoundTrees()[poolIndex].mGlobalPose = transform; + V4StoreU(node->mBVMin, &localBounds.minimum.x); + PX_ALIGN(16, PxVec4) max4; + V4StoreA(node->mBVMax, &max4.x); + localBounds.maximum = PxVec3(max4.x, max4.y, max4.z); + const PxBounds3 compoundBounds = PxBounds3::transformFast(transform, localBounds); + mCompoundTreePool.getCurrentCompoundBounds()[poolIndex] = compoundBounds; + mChangedLeaves.clear(); + IncrementalAABBTreeNode* mainTreeNode = mMainTree.update(mMainTreeUpdateMap[poolIndex], poolIndex, mCompoundTreePool.getCurrentCompoundBounds(), mChangedLeaves); + // we removed node during update, need to update the mapping + updateMapping(poolIndex, mainTreeNode); + } + +#if PARANOIA_CHECKS + test(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::test() +{ + + if(mMainTree.getNodes()) + { + for(PxU32 i = 0; i < mCompoundTreePool.getNbObjects(); i++) + { + mMainTree.checkTreeLeaf(mMainTreeUpdateMap[i], i); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::release() +{ +} + +////////////////////////////////////////////////////////////////////////// +// Queries implementation +////////////////////////////////////////////////////////////////////////// +// Raycast/sweeps callback for main AABB tree +template +struct MainTreeRaycastCompoundPrunerCallback +{ + MainTreeRaycastCompoundPrunerCallback(const PxVec3& origin, const PxVec3& unitDir, const PxVec3& extent, PrunerCallback& prunerCallback, PxQueryFlags flags) + : mOrigin(origin), mUnitDir(unitDir), mExtent(extent), mPrunerCallback(prunerCallback), mQueryFlags(flags) + { + } + + virtual ~MainTreeRaycastCompoundPrunerCallback() {} + + virtual PxAgain invoke(PxReal& distance, const CompoundTree& compoundTree) + { + if(!(compoundTree.mFlags & PxU32(mQueryFlags)) || !compoundTree.mTree->getNodes()) + return true; + + // transfer to actor local space + const PxVec3 localOrigin = compoundTree.mGlobalPose.transformInv(mOrigin); + const PxVec3 localDir = compoundTree.mGlobalPose.q.rotateInv(mUnitDir); + PxVec3 localExtent = mExtent; + + if(tInflate) + { + PxBounds3 wBounds = PxBounds3::centerExtents(mOrigin, mExtent); + PxBounds3 localBounds = PxBounds3::transformSafe(compoundTree.mGlobalPose.getInverse(), wBounds); + localExtent = localBounds.getExtents(); + } + + // raycast the merged tree + return AABBTreeRaycast() + (compoundTree.mPruningPool->getObjects(), compoundTree.mPruningPool->getCurrentWorldBoxes(), *compoundTree.mTree, localOrigin, localDir, distance, localExtent, mPrunerCallback); + } + + PX_NOCOPY(MainTreeRaycastCompoundPrunerCallback) + +private: + const PxVec3& mOrigin; + const PxVec3& mUnitDir; + const PxVec3& mExtent; + PrunerCallback& mPrunerCallback; + PxQueryFlags mQueryFlags; +}; + +////////////////////////////////////////////////////////////////////////// +// raycast against the compound pruner +PxAgain BVHCompoundPruner::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& prunerCallback, PxQueryFlags flags) const +{ + PxAgain again = true; + + // search the main tree if there are nodes + if(mMainTree.getNodes()) + { + const PxVec3 extent(0.0f); + // main tree callback + MainTreeRaycastCompoundPrunerCallback pcb(origin, unitDir, extent, prunerCallback, flags); + // traverse the main tree + again = AABBTreeRaycast >() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, origin, unitDir, inOutDistance, extent, pcb); + } + + return again; +} + +////////////////////////////////////////////////////////////////////////// +// overlap main tree callback +// A.B. templated version is complicated due to test transformations, will do a callback per primitive +struct MainTreeOverlapCompoundPrunerCallback +{ + MainTreeOverlapCompoundPrunerCallback(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) + : mQueryVolume(queryVolume), mPrunerCallback(prunerCallback), mQueryFlags(flags) + { + } + + virtual ~MainTreeOverlapCompoundPrunerCallback() {} + + PX_NOCOPY(MainTreeOverlapCompoundPrunerCallback) + +protected: + const Gu::ShapeData& mQueryVolume; + PrunerCallback& mPrunerCallback; + PxQueryFlags mQueryFlags; +}; + +// OBB +struct MainTreeOBBOverlapCompoundPrunerCallback: public MainTreeOverlapCompoundPrunerCallback +{ + MainTreeOBBOverlapCompoundPrunerCallback(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) + : MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags) {} + virtual PxAgain invoke(PxReal& , const CompoundTree& compoundTree) + { + if(!(compoundTree.mFlags & PxU32(mQueryFlags)) || !compoundTree.mTree->getNodes()) + return true; + + const PxVec3 localPos = compoundTree.mGlobalPose.transformInv(mQueryVolume.getPrunerWorldPos()); + const PxMat33 transfMat(compoundTree.mGlobalPose.q); + const PxMat33 localRot = transfMat.getTranspose()*mQueryVolume.getPrunerWorldRot33(); + + const Gu::OBBAABBTest localTest(localPos, localRot, mQueryVolume.getPrunerBoxGeomExtentsInflated()); + // overlap the compound local tree + return AABBTreeOverlap() + (compoundTree.mPruningPool->getObjects(), compoundTree.mPruningPool->getCurrentWorldBoxes(), *compoundTree.mTree, localTest, mPrunerCallback); + } + + PX_NOCOPY(MainTreeOBBOverlapCompoundPrunerCallback) +}; + +// AABB +struct MainTreeAABBOverlapCompoundPrunerCallback: public MainTreeOverlapCompoundPrunerCallback +{ + MainTreeAABBOverlapCompoundPrunerCallback(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) + : MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags) {} + + virtual PxAgain invoke(PxReal& , const CompoundTree& compoundTree) + { + if(!(compoundTree.mFlags & PxU32(mQueryFlags)) || !compoundTree.mTree->getNodes()) + return true; + + const PxVec3 localPos = compoundTree.mGlobalPose.transformInv(mQueryVolume.getPrunerWorldPos()); + const PxMat33 transfMat(compoundTree.mGlobalPose.q); + const PxMat33 localRot = transfMat.getTranspose()*mQueryVolume.getPrunerWorldRot33(); + + // A.B. we dont have the AABB in local space, either we test OBB local space or + // we retest the AABB with the worldSpace AABB of the local tree??? + const Gu::OBBAABBTest localTest(localPos, localRot, mQueryVolume.getPrunerBoxGeomExtentsInflated()); + // overlap the compound local tree + return AABBTreeOverlap() + (compoundTree.mPruningPool->getObjects(), compoundTree.mPruningPool->getCurrentWorldBoxes(), *compoundTree.mTree, localTest, mPrunerCallback); + } + + PX_NOCOPY(MainTreeAABBOverlapCompoundPrunerCallback) +}; + +// Capsule +struct MainTreeCapsuleOverlapCompoundPrunerCallback: public MainTreeOverlapCompoundPrunerCallback +{ + MainTreeCapsuleOverlapCompoundPrunerCallback(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) + : MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags) {} + + virtual PxAgain invoke(PxReal& , const CompoundTree& compoundTree) + { + if(!(compoundTree.mFlags & PxU32(mQueryFlags)) || !compoundTree.mTree->getNodes()) + return true; + + const PxMat33 transfMat(compoundTree.mGlobalPose.q); + const Gu::Capsule& capsule = mQueryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest localTest( + compoundTree.mGlobalPose.transformInv(capsule.p1), + transfMat.getTranspose()*mQueryVolume.getPrunerWorldRot33().column0, + mQueryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + + // overlap the compound local tree + return AABBTreeOverlap() + (compoundTree.mPruningPool->getObjects(), compoundTree.mPruningPool->getCurrentWorldBoxes(), *compoundTree.mTree, localTest, mPrunerCallback); + } + + PX_NOCOPY(MainTreeCapsuleOverlapCompoundPrunerCallback) +}; + +// Sphere +struct MainTreeSphereOverlapCompoundPrunerCallback: public MainTreeOverlapCompoundPrunerCallback +{ + MainTreeSphereOverlapCompoundPrunerCallback(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) + : MainTreeOverlapCompoundPrunerCallback(queryVolume, prunerCallback, flags) {} + + virtual PxAgain invoke(PxReal& , const CompoundTree& compoundTree) + { + if(!(compoundTree.mFlags & PxU32(mQueryFlags)) || !compoundTree.mTree->getNodes()) + return true; + + const Gu::Sphere& sphere = mQueryVolume.getGuSphere(); + Gu::SphereAABBTest localTest(compoundTree.mGlobalPose.transformInv(sphere.center), sphere.radius); + + // overlap the compound local tree + return AABBTreeOverlap() + (compoundTree.mPruningPool->getObjects(), compoundTree.mPruningPool->getCurrentWorldBoxes(), *compoundTree.mTree, localTest, mPrunerCallback); + } + + PX_NOCOPY(MainTreeSphereOverlapCompoundPrunerCallback) +}; + + +////////////////////////////////////////////////////////////////////////// +// overlap implementation +PxAgain BVHCompoundPruner::overlap(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback, PxQueryFlags flags) const +{ + PxAgain again = true; + + if(mMainTree.getNodes()) + { + switch (queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if(queryVolume.isOBB()) + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + MainTreeOBBOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags); + again = AABBTreeOverlap() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, test, pcb); + } + else + { + const Gu::AABBAABBTest test(queryVolume.getPrunerInflatedWorldAABB()); + MainTreeAABBOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags); + again = AABBTreeOverlap() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, test, pcb); + } + } + break; + case PxGeometryType::eCAPSULE: + { + const Gu::Capsule& capsule = queryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest test(capsule.p1, queryVolume.getPrunerWorldRot33().column0, + queryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + MainTreeCapsuleOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags); + again = AABBTreeOverlap() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, test, pcb); + } + break; + case PxGeometryType::eSPHERE: + { + const Gu::Sphere& sphere = queryVolume.getGuSphere(); + Gu::SphereAABBTest test(sphere.center, sphere.radius); + MainTreeSphereOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags); + again = AABBTreeOverlap() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, test, pcb); + } + break; + case PxGeometryType::eCONVEXMESH: + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + MainTreeOBBOverlapCompoundPrunerCallback pcb(queryVolume, prunerCallback, flags); + again = AABBTreeOverlap() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, test, pcb); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + } + + return again; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +PxAgain BVHCompoundPruner::sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& prunerCallback, PxQueryFlags flags) const +{ + PxAgain again = true; + + if(mMainTree.getNodes()) + { + const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB(); + const PxVec3 extents = aabb.getExtents(); + const PxVec3 center = aabb.getCenter(); + MainTreeRaycastCompoundPrunerCallback pcb(center, unitDir, extents, prunerCallback, flags); + again = AABBTreeRaycast >() + (mCompoundTreePool.getCompoundTrees(), mCompoundTreePool.getCurrentCompoundBounds(), mMainTree, center, unitDir, inOutDistance, extents, pcb); + } + return again; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +const PrunerPayload& BVHCompoundPruner::getPayload(PrunerHandle handle, PrunerCompoundId compoundId) const +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + + return mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].mPruningPool->getPayload(handle); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +const PrunerPayload& BVHCompoundPruner::getPayload(PrunerHandle handle, PrunerCompoundId compoundId, PxBounds3*& bounds) const +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + + return mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].mPruningPool->getPayload(handle, bounds); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::updateObjectAfterManualBoundsUpdates(PrunerCompoundId compoundId, const PrunerHandle handle) +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + if(!poolIndexEntry) + return; + + mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].updateObjectAfterManualBoundsUpdates(handle); + + const PxU32 poolIndex = poolIndexEntry->second; + updateMainTreeNode(poolIndex); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::removeObject(PrunerCompoundId compoundId, const PrunerHandle handle) +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + if(!poolIndexEntry) + return; + + const PxU32 poolIndex = poolIndexEntry->second; + + mCompoundTreePool.getCompoundTrees()[poolIndex].removeObject(handle); + + // edge case, we removed all objects for the compound tree, we need to remove it now completely + if(!mCompoundTreePool.getCompoundTrees()[poolIndex].mTree->getNodes()) + { + removeCompound(compoundId); + } + else + { + updateMainTreeNode(poolIndex); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool BVHCompoundPruner::addObject(PrunerCompoundId compoundId, PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload userData) +{ + const ActorIdPoolIndexMap::Entry* poolIndexEntry = mActorPoolMap.find(compoundId); + PX_ASSERT(poolIndexEntry); + if(!poolIndexEntry) + return false; + + mCompoundTreePool.getCompoundTrees()[poolIndexEntry->second].addObject(result, bounds, userData); + + const PxU32 poolIndex = poolIndexEntry->second; + updateMainTreeNode(poolIndex); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::updateMainTreeNode(PoolIndex poolIndex) +{ + PxBounds3 localBounds; + const IncrementalAABBTreeNode* node = mCompoundTreePool.getCompoundTrees()[poolIndex].mTree->getNodes(); + V4StoreU(node->mBVMin, &localBounds.minimum.x); + PX_ALIGN(16, PxVec4) max4; + V4StoreA(node->mBVMax, &max4.x); + localBounds.maximum = PxVec3(max4.x, max4.y, max4.z); + const PxBounds3 compoundBounds = PxBounds3::transformFast(mCompoundTreePool.getCompoundTrees()[poolIndex].mGlobalPose, localBounds); + mCompoundTreePool.getCurrentCompoundBounds()[poolIndex] = compoundBounds; + + mChangedLeaves.clear(); + IncrementalAABBTreeNode* mainTreeNode = mMainTree.update(mMainTreeUpdateMap[poolIndex], poolIndex, mCompoundTreePool.getCurrentCompoundBounds(), mChangedLeaves); + // we removed node during update, need to update the mapping + updateMapping(poolIndex, mainTreeNode); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::shiftOrigin(const PxVec3& shift) +{ + mCompoundTreePool.shiftOrigin(shift); + + mMainTree.shiftOrigin(shift); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void BVHCompoundPruner::visualize(Cm::RenderOutput&, PxU32) const +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h new file mode 100644 index 000000000..2c2663f62 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SQ_COMPOUNDPRUNER_H +#define SQ_COMPOUNDPRUNER_H + +#include "SqPrunerMergeData.h" +#include "SqCompoundPruningPool.h" +#include "SqPruningPool.h" +#include "SqIncrementalAABBTree.h" +#include "PsHashMap.h" +#include "PsArray.h" + +namespace physx +{ +namespace Sq +{ + + /////////////////////////////////////////////////////////////////////////////////////////////// + + typedef Ps::HashMap ActorIdPoolIndexMap; + typedef Ps::Array PoolIndexActorIdMap; + + /////////////////////////////////////////////////////////////////////////////////////////////// + + class BVHCompoundPruner: public CompoundPruner + { + public: + BVHCompoundPruner(); + ~BVHCompoundPruner(); + + void release(); + + // CompoundPruner + // compound level + virtual bool addCompound(PrunerHandle* results, const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& transform, CompoundFlag::Enum flags, const PrunerPayload* userData); + virtual void removeCompound(PrunerCompoundId compoundId); + virtual void updateCompound(PrunerCompoundId compoundId, const PxTransform& transform); + // object level + virtual void updateObjectAfterManualBoundsUpdates(PrunerCompoundId compoundId, const PrunerHandle handle); + virtual void removeObject(PrunerCompoundId compoundId, const PrunerHandle handle); + virtual bool addObject(PrunerCompoundId compoundId, PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload userData); + //queries + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&, PxQueryFlags flags) const; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&, PxQueryFlags flags) const; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&, PxQueryFlags flags) const; + virtual const PrunerPayload& getPayload(PrunerHandle handle, PrunerCompoundId compoundId) const; + virtual const PrunerPayload& getPayload(PrunerHandle handle, PrunerCompoundId compoundId, PxBounds3*& bounds) const; + virtual void shiftOrigin(const PxVec3& shift); + virtual void visualize(Cm::RenderOutput&, PxU32) const; + // ~CompoundPruner + + private: + void updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node); + void updateMainTreeNode(PoolIndex index); + + void test(); + private: + IncrementalAABBTree mMainTree; + UpdateMap mMainTreeUpdateMap; + + CompoundTreePool mCompoundTreePool; + ActorIdPoolIndexMap mActorPoolMap; + PoolIndexActorIdMap mPoolActorMap; + NodeList mChangedLeaves; + }; +} +} + +#endif + diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp new file mode 100644 index 000000000..be67e1a9e --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp @@ -0,0 +1,279 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsFoundation.h" +#include "PsAllocator.h" +#include "SqCompoundPruningPool.h" +#include "SqAABBTree.h" +#include "SqPruningPool.h" +#include "GuBVHStructure.h" + +using namespace physx; +using namespace Gu; +using namespace Sq; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundTree::updateObjectAfterManualBoundsUpdates(PrunerHandle handle) +{ + const PxBounds3* newBounds = mPruningPool->getCurrentWorldBoxes(); + const PoolIndex poolIndex = mPruningPool->getIndex(handle); + NodeList changedLeaves; + changedLeaves.reserve(8); + IncrementalAABBTreeNode* node = mTree->update((*mUpdateMap)[poolIndex], poolIndex, newBounds, changedLeaves); + // we removed node during update, need to update the mapping + updateMapping(poolIndex, node, changedLeaves); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundTree::removeObject(PrunerHandle handle) +{ + const PoolIndex poolIndex = mPruningPool->getIndex(handle); // save the pool index for removed object + const PoolIndex poolRelocatedLastIndex = mPruningPool->removeObject(handle); // save the lastIndex returned by removeObject + + IncrementalAABBTreeNode* node = mTree->remove((*mUpdateMap)[poolIndex], poolIndex, mPruningPool->getCurrentWorldBoxes()); + // if node moved to its parent + if (node && node->isLeaf()) + { + for (PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + (*mUpdateMap)[index] = node; + } + } + + (*mUpdateMap)[poolIndex] = (*mUpdateMap)[poolRelocatedLastIndex]; + // fix indices if we made a swap + if(poolRelocatedLastIndex != poolIndex) + mTree->fixupTreeIndices((*mUpdateMap)[poolIndex], poolRelocatedLastIndex, poolIndex); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool CompoundTree::addObject(PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload payload) +{ + mPruningPool->addObjects(&result, &bounds, &payload, 1); + + const PoolIndex poolIndex = mPruningPool->getIndex(result); + NodeList changedLeaves; + changedLeaves.reserve(8); + IncrementalAABBTreeNode* node = mTree->insert(poolIndex, mPruningPool->getCurrentWorldBoxes(), changedLeaves); + updateMapping(poolIndex, node, changedLeaves); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundTree::updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node, const NodeList& changedLeaves) +{ + // if a node was split we need to update the node indices and also the sibling indices + if(!changedLeaves.empty()) + { + if(node && node->isLeaf()) + { + for(PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + (*mUpdateMap)[index] = node; + } + } + + for(PxU32 i = 0; i < changedLeaves.size(); i++) + { + IncrementalAABBTreeNode* changedNode = changedLeaves[i]; + PX_ASSERT(changedNode->isLeaf()); + + for(PxU32 j = 0; j < changedNode->getNbPrimitives(); j++) + { + const PoolIndex index = changedNode->getPrimitives(NULL)[j]; + (*mUpdateMap)[index] = changedNode; + } + } + } + else + { + (*mUpdateMap)[poolIndex] = node; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +CompoundTreePool::CompoundTreePool(): + mNbObjects(0), + mMaxNbObjects(0), + mCompoundBounds(NULL), + mCompoundTrees(NULL) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +CompoundTreePool::~CompoundTreePool() +{ + PX_FREE_AND_RESET(mCompoundBounds); + PX_FREE_AND_RESET(mCompoundTrees); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +bool CompoundTreePool::resize(PxU32 newCapacity) +{ + // PT: we always allocate one extra box, to make sure we can safely use V4 loads on the array + PxBounds3* newBoxes = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(newCapacity+1), "PxBounds3")); + CompoundTree* newTrees = reinterpret_cast(PX_ALLOC(sizeof(CompoundTree)*newCapacity, "IncrementalTrees*")); + + // memzero, we need to set the pointers in the compound tree to NULL + PxMemZero(newTrees, sizeof(CompoundTree)*newCapacity); + + if((NULL==newBoxes) || (NULL==newTrees)) + { + PX_FREE_AND_RESET(newBoxes); + PX_FREE_AND_RESET(newTrees); + return false; + } + + if(mCompoundBounds) PxMemCopy(newBoxes, mCompoundBounds, mNbObjects*sizeof(PxBounds3)); + if(mCompoundTrees) PxMemCopy(newTrees, mCompoundTrees, mNbObjects*sizeof(CompoundTree)); + mMaxNbObjects = newCapacity; + + PX_FREE_AND_RESET(mCompoundBounds); + PX_FREE_AND_RESET(mCompoundTrees); + mCompoundBounds = newBoxes; + mCompoundTrees = newTrees; + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundTreePool::preallocate(PxU32 newCapacity) +{ + if(newCapacity>mMaxNbObjects) + resize(newCapacity); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +void CompoundTreePool::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i=0; i < mNbObjects; i++) + { + mCompoundBounds[i].minimum -= shift; + mCompoundBounds[i].maximum -= shift; + + mCompoundTrees[i].mGlobalPose.p -= shift; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +PoolIndex CompoundTreePool::addCompound(PrunerHandle* results, const BVHStructure& bvhStructure, const PxBounds3& compoundBounds, const PxTransform& transform, + CompoundFlag::Enum flags, const PrunerPayload* userData) +{ + if(mNbObjects==mMaxNbObjects) // increase the capacity on overflow + { + if(!resize(PxMax(mMaxNbObjects*2, 32))) + { + // pool can return an invalid handle if memory alloc fails + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "CompoundTreePool::addCompound memory allocation in resize failed."); + return INVALID_PRUNERHANDLE; + } + } + PX_ASSERT(mNbObjects!=mMaxNbObjects); + + const PoolIndex index = mNbObjects++; + + mCompoundBounds[index] = compoundBounds; + + const PxU32 nbObjects = bvhStructure.getNbBounds(); + + CompoundTree& tree = mCompoundTrees[index]; + PX_ASSERT(tree.mPruningPool == NULL); + PX_ASSERT(tree.mTree == NULL); + PX_ASSERT(tree.mUpdateMap == NULL); + + tree.mGlobalPose = transform; + tree.mFlags = flags; + + // prepare the pruning pool + PruningPool* pool = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PruningPool), PX_DEBUG_EXP("Pruning pool")), PruningPool); + pool->preallocate(nbObjects); + pool->addObjects(results, bvhStructure.getBounds(), userData, nbObjects); + tree.mPruningPool = pool; + + // prepare update map + UpdateMap* map = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(UpdateMap), PX_DEBUG_EXP("Update map")), UpdateMap); + map->resizeUninitialized(nbObjects); + tree.mUpdateMap = map; + + IncrementalAABBTree* iTree = PX_NEW(IncrementalAABBTree)(); + iTree->copy(bvhStructure, *map); + tree.mTree = iTree; + + return index; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +PoolIndex CompoundTreePool::removeCompound(PoolIndex indexOfRemovedObject) +{ + PX_ASSERT(mNbObjects); + + // release the tree + mCompoundTrees[indexOfRemovedObject].mTree->release(); + mCompoundTrees[indexOfRemovedObject].mTree->~IncrementalAABBTree(); + PX_FREE_AND_RESET(mCompoundTrees[indexOfRemovedObject].mTree); + + mCompoundTrees[indexOfRemovedObject].mUpdateMap->clear(); + mCompoundTrees[indexOfRemovedObject].mUpdateMap->~Array(); + PX_FREE_AND_RESET(mCompoundTrees[indexOfRemovedObject].mUpdateMap); + + mCompoundTrees[indexOfRemovedObject].mPruningPool->~PruningPool(); + PX_FREE_AND_RESET(mCompoundTrees[indexOfRemovedObject].mPruningPool); + + const PoolIndex indexOfLastObject = --mNbObjects; // swap the object at last index with index + if(indexOfLastObject!=indexOfRemovedObject) + { + // PT: move last object's data to recycled spot (from removed object) + + // PT: the last object has moved so we need to handle the mappings for this object + mCompoundBounds [indexOfRemovedObject] = mCompoundBounds [indexOfLastObject]; + mCompoundTrees [indexOfRemovedObject] = mCompoundTrees [indexOfLastObject]; + + mCompoundTrees [indexOfLastObject].mPruningPool = NULL; + mCompoundTrees [indexOfLastObject].mUpdateMap = NULL; + mCompoundTrees [indexOfLastObject].mTree = NULL; + } + + return indexOfLastObject; +} + diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h new file mode 100644 index 000000000..059273e02 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h @@ -0,0 +1,108 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef SQ_COMPOUNDPRUNING_POOL_H +#define SQ_COMPOUNDPRUNING_POOL_H + +#include "SqPrunerMergeData.h" +#include "SqIncrementalAABBTree.h" +#include "PsArray.h" + +namespace physx +{ +namespace Sq +{ + class PruningPool; + + /////////////////////////////////////////////////////////////////////////////////////////////// + + typedef Ps::Array UpdateMap; + + /////////////////////////////////////////////////////////////////////////////////////////////// + + class CompoundTree + { + public: + void updateObjectAfterManualBoundsUpdates(PrunerHandle handle); + void removeObject(PrunerHandle handle); + bool addObject(PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload userData); + + private: + void updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node, const NodeList& changedLeaves); + + public: + IncrementalAABBTree* mTree; + PruningPool* mPruningPool; + UpdateMap* mUpdateMap; + PxTransform mGlobalPose; + CompoundFlag::Enum mFlags; + }; + + /////////////////////////////////////////////////////////////////////////////////////////////// + + class CompoundTreePool + { + public: + CompoundTreePool(); + ~CompoundTreePool(); + + void preallocate(PxU32 newCapacity); + + PoolIndex addCompound(PrunerHandle* results, const Gu::BVHStructure& bvhStructure, const PxBounds3& compoundBounds, const PxTransform& transform, CompoundFlag::Enum flags, const PrunerPayload* userData); + PoolIndex removeCompound(PoolIndex index); + + void shiftOrigin(const PxVec3& shift); + + PX_FORCE_INLINE const PxBounds3* getCurrentCompoundBounds() const { return mCompoundBounds; } + PX_FORCE_INLINE PxBounds3* getCurrentCompoundBounds() { return mCompoundBounds; } + + PX_FORCE_INLINE const CompoundTree* getCompoundTrees() const { return mCompoundTrees; } + PX_FORCE_INLINE CompoundTree* getCompoundTrees() { return mCompoundTrees; } + + PX_FORCE_INLINE PxU32 getNbObjects() const { return mNbObjects; } + + + private: + bool resize(PxU32 newCapacity); + + private: + PxU32 mNbObjects; //!< Current number of objects + PxU32 mMaxNbObjects; //!< Max. number of objects (capacity for mWorldBoxes, mObjects) + + //!< these arrays are parallel + PxBounds3* mCompoundBounds; //!< List of compound world boxes, stores mNbObjects, capacity=mMaxNbObjects + CompoundTree* mCompoundTrees; + }; + +} +} + +#endif + diff --git a/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp new file mode 100644 index 000000000..136652d58 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp @@ -0,0 +1,920 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "SqExtendedBucketPruner.h" +#include "SqAABBTree.h" +#include "SqPrunerMergeData.h" +#include "GuAABBTreeQuery.h" +#include "GuBounds.h" +#include "CmBitMap.h" + +using namespace physx; +using namespace Sq; +using namespace Gu; +using namespace Ps; + +#define NB_OBJECTS_PER_NODE 4 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Constructor, preallocate trees, bounds +ExtendedBucketPruner::ExtendedBucketPruner(const PruningPool* pool) + : +#if USE_INCREMENTAL_PRUNER + mPrunerCore(pool), +#else + mPrunerCore(false), +#endif + mPruningPool(pool), mMainTree(NULL), mBounds(NULL), mMergedTrees(NULL), + mCurrentTreeIndex(0), mTreesDirty(false) +{ + // preallocated size for bounds, trees + mCurrentTreeCapacity = 32; + + mBounds = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(mCurrentTreeCapacity + 1), "Bounds")); + mMergedTrees = reinterpret_cast(PX_ALLOC(sizeof(MergedTree)*mCurrentTreeCapacity, "AABB trees")); + mExtendedBucketPrunerMap.reserve(mCurrentTreeCapacity); + + // create empty main tree + mMainTree = PX_NEW(AABBTree); + + // create empty merge trees + for (PxU32 i = 0; i < mCurrentTreeCapacity; i++) + { + mMergedTrees[i].mTimeStamp = 0; + mMergedTrees[i].mTree = PX_NEW(AABBTree); + } +} + +////////////////////////////////////////////////////////////////////////// + +ExtendedBucketPruner::~ExtendedBucketPruner() +{ + // release main tree + if (mMainTree) + { + PX_DELETE_AND_RESET(mMainTree); + } + + // release merged trees + for (PxU32 i = 0; i < mCurrentTreeCapacity; i++) + { + AABBTree* aabbTree = mMergedTrees[i].mTree; + PX_DELETE(aabbTree); + } + + PX_FREE(mBounds); + PX_FREE(mMergedTrees); +} + +////////////////////////////////////////////////////////////////////////// +// release all objects in bucket pruner +void ExtendedBucketPruner::release() +{ + // release core bucket pruner + mPrunerCore.release(); + + mMainTreeUpdateMap.release(); + mMergeTreeUpdateMap.release(); + + // release all objecs from the map + mExtendedBucketPrunerMap.clear(); + + // release all merged trees + for (PxU32 i = 0; i < mCurrentTreeCapacity; i++) + { + mMergedTrees[i].mTimeStamp = 0; + mMergedTrees[i].mTree->release(); + } + + // reset current tree index + mCurrentTreeIndex = 0; +} + +////////////////////////////////////////////////////////////////////////// +// Add a tree from a pruning structure +// 1. get new tree index +// 2. initialize merged tree, bounds +// 3. create update map for the merged tree +// 4. build new tree of trees from given trees bounds +// 5. add new objects into extended bucket pruner map +// 6. shift indices in the merged tree +void ExtendedBucketPruner::addTree(const AABBTreeMergeData& mergeData, PxU32 timeStamp) +{ + // check if we have to resize + if(mCurrentTreeIndex == mCurrentTreeCapacity) + { + resize(mCurrentTreeCapacity*2); + } + + // get current merge tree index + const PxU32 mergeTreeIndex = mCurrentTreeIndex++; + + // get payloads pointers - the pointers start at mIndicesOffset, thats where all + // objects were added before merge was called + const PrunerPayload* payloads = &mPruningPool->getObjects()[mergeData.mIndicesOffset]; + + // setup merged tree with the merge data and timestamp + mMergedTrees[mergeTreeIndex].mTimeStamp = timeStamp; + AABBTree& mergedTree = *mMergedTrees[mergeTreeIndex].mTree; + mergedTree.initTree(mergeData); + // set bounds + mBounds[mergeTreeIndex] = mergeData.getRootNode().mBV; + + // update temporally update map for the current merge tree, map is used to setup the base extended bucket pruner map + mMergeTreeUpdateMap.initMap(mergeData.mNbIndices, mergedTree); + + // create new base tree of trees + buildMainAABBTree(); + + // Add each object into extended bucket pruner hash map + for (PxU32 i = 0; i < mergeData.mNbIndices; i++) + { + ExtendedBucketPrunerData mapData; + mapData.mMergeIndex = mergeTreeIndex; + mapData.mTimeStamp = timeStamp; + PX_ASSERT(mMergeTreeUpdateMap[i] < mergedTree.getNbNodes()); + // get node information from the merge tree update map + mapData.mSubTreeNode = mMergeTreeUpdateMap[i]; + mExtendedBucketPrunerMap.insert(payloads[i], mapData); + } + // merged tree indices needs to be shifted now, we cannot shift it in init - the update map + // could not be constructed otherwise, as the indices wont start from 0. The indices + // needs to be shifted by offset from the pruning pool, where the new objects were added into the pruning pool. + mergedTree.shiftIndices(mergeData.mIndicesOffset); + +#if PX_DEBUG + checkValidity(); +#endif // PX_DEBUG +} + +////////////////////////////////////////////////////////////////////////// +// Builds the new main AABB tree with given current active merged trees and its bounds +void ExtendedBucketPruner::buildMainAABBTree() +{ + // create the AABB tree from given merged trees bounds + AABBTreeBuildParams sTB; + sTB.mNbPrimitives = mCurrentTreeIndex; + sTB.mAABBArray = mBounds; + sTB.mLimit = NB_OBJECTS_PER_NODE; + bool status = mMainTree->build(sTB); + + PX_UNUSED(status); + PX_ASSERT(status); + + // Init main tree update map for the new main tree + mMainTreeUpdateMap.initMap(mCurrentTreeIndex, *mMainTree); +} + +////////////////////////////////////////////////////////////////////////// +// resize internal memory, buffers +void ExtendedBucketPruner::resize(PxU32 size) +{ + PX_ASSERT(size > mCurrentTreeCapacity); + // allocate new bounds + PxBounds3* newBounds = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(size + 1), "Bounds")); + // copy previous bounds + PxMemCopy(newBounds, mBounds, sizeof(PxBounds3)*mCurrentTreeCapacity); + PX_FREE(mBounds); + mBounds = newBounds; + + // allocate new merged trees + MergedTree* newMergeTrees = reinterpret_cast(PX_ALLOC(sizeof(MergedTree)*size, "AABB trees")); + // copy previous merged trees + PxMemCopy(newMergeTrees, mMergedTrees, sizeof(MergedTree)*mCurrentTreeCapacity); + PX_FREE(mMergedTrees); + mMergedTrees = newMergeTrees; + // allocate new trees for merged trees + for (PxU32 i = mCurrentTreeCapacity; i < size; i++) + { + mMergedTrees[i].mTimeStamp = 0; + mMergedTrees[i].mTree = PX_NEW(AABBTree); + } + + mCurrentTreeCapacity = size; +} + +////////////////////////////////////////////////////////////////////////// +// Update object +bool ExtendedBucketPruner::updateObject(const PxBounds3& worldAABB, const PrunerPayload& object, const PoolIndex poolIndex) +{ + const ExtendedBucketPrunerMap::Entry* extendedPrunerEntry = mExtendedBucketPrunerMap.find(object); + + // if object is not in tree of trees, it is in bucket pruner core + if(!extendedPrunerEntry) + { +#if USE_INCREMENTAL_PRUNER + PX_UNUSED(worldAABB); + return mPrunerCore.updateObject(poolIndex); +#else + PX_UNUSED(poolIndex); + return mPrunerCore.updateObject(worldAABB, object); +#endif + } + else + { + const ExtendedBucketPrunerData& data = extendedPrunerEntry->second; + + PX_ASSERT(data.mMergeIndex < mCurrentTreeIndex); + + // update tree where objects belongs to + AABBTree& tree = *mMergedTrees[data.mMergeIndex].mTree; + PX_ASSERT(data.mSubTreeNode < tree.getNbNodes()); + // mark for refit node in merged tree + tree.markNodeForRefit(data.mSubTreeNode); + PX_ASSERT(mMainTreeUpdateMap[data.mMergeIndex] < mMainTree->getNbNodes()); + // mark for refit node in main aabb tree + mMainTree->markNodeForRefit(mMainTreeUpdateMap[data.mMergeIndex]); + mTreesDirty = true; + } + return true; +} + +////////////////////////////////////////////////////////////////////////// +// refit merged nodes +// 1. refit nodes in merged trees +// 2. check if after refit root node is valid - might happen edge case +// where all objects were released - the root node is then invalid +// in this edge case we need to compact the merged trees array +// and create new main AABB tree +// 3. If all merged trees bounds are valid - refit main tree +// 4. If bounds are invalid create new main AABB tree +void ExtendedBucketPruner::refitMarkedNodes(const PxBounds3* boxes) +{ + // if no tree needs update early exit + if(!mTreesDirty) + return; + + // refit trees and update bounds for main tree + PxU32 nbValidTrees = 0; + for (PxU32 i = mCurrentTreeIndex; i--; ) + { + AABBTree& tree = *mMergedTrees[i].mTree; + tree.refitMarkedNodes(boxes); + const PxBounds3& bounds = tree.getNodes()[0].mBV; + // check if bounds are valid, if all objects of the tree were released, the bounds + // will be invalid, in that case we cannot use this tree anymore. + if(bounds.isValid()) + { + nbValidTrees++; + } + mBounds[i] = bounds; + } + + if(nbValidTrees == mCurrentTreeIndex) + { + // no tree has been removed refit main tree + mMainTree->refitMarkedNodes(mBounds); + } + else + { + // edge case path, tree does not have a valid root node bounds - all objects from the tree were released + // we might even fire perf warning + // compact the tree array - no holes in the array, remember the swap position + PxU32* swapMap = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mCurrentTreeIndex + 1, "Swap Map")); + PxU32 writeIndex = 0; + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + AABBTree& tree = *mMergedTrees[i].mTree; + if(tree.getNodes()[0].mBV.isValid()) + { + // we have to store the tree into an empty location + if(i != writeIndex) + { + PX_ASSERT(writeIndex < i); + AABBTree* ptr = mMergedTrees[writeIndex].mTree; + mMergedTrees[writeIndex] = mMergedTrees[i]; + mMergedTrees[i].mTree = ptr; + mBounds[writeIndex] = mBounds[i]; + } + // remember the swap location + swapMap[i] = writeIndex; + writeIndex++; + } + else + { + // tree is not valid, release it + tree.release(); + mMergedTrees[i].mTimeStamp = 0; + } + + // remember the swap + swapMap[mCurrentTreeIndex] = i; + } + + PX_ASSERT(writeIndex == nbValidTrees); + + // new merged trees size + mCurrentTreeIndex = nbValidTrees; + + if(mCurrentTreeIndex) + { + // trees have changed, we need to rebuild the main tree + buildMainAABBTree(); + + // fixup the object entries, the merge index has changed + for (ExtendedBucketPrunerMap::Iterator iter = mExtendedBucketPrunerMap.getIterator(); !iter.done(); ++iter) + { + ExtendedBucketPrunerData& data = iter->second; + PX_ASSERT(swapMap[data.mMergeIndex] < nbValidTrees); + data.mMergeIndex = swapMap[data.mMergeIndex]; + } + } + else + { + // if there is no tree release the main tree + mMainTree->release(); + } + PX_FREE(swapMap); + } +#if PX_DEBUG + checkValidity(); +#endif + mTreesDirty = false; +} + +////////////////////////////////////////////////////////////////////////// +// remove object +bool ExtendedBucketPruner::removeObject(const PrunerPayload& object, PxU32 objectIndex, const PrunerPayload& swapObject, + PxU32 swapObjectIndex, PxU32& timeStamp) +{ + ExtendedBucketPrunerMap::Entry dataEntry; + + // if object is not in tree of trees, it is in bucket pruner core + if (!mExtendedBucketPrunerMap.erase(object, dataEntry)) + { + // we need to call invalidateObjects, it might happen that the swapped object + // does belong to the extended bucket pruner, in that case the objects index + // needs to be swapped. + // do not call additional bucket pruner swap, that does happen during remove + swapIndex(objectIndex, swapObject, swapObjectIndex, false); +#if USE_INCREMENTAL_PRUNER + return mPrunerCore.removeObject(objectIndex, swapObjectIndex, timeStamp); +#else + return mPrunerCore.removeObject(object, timeStamp); +#endif + } + else + { + const ExtendedBucketPrunerData& data = dataEntry.second; + + // mark tree nodes where objects belongs to + AABBTree& tree = *mMergedTrees[data.mMergeIndex].mTree; + PX_ASSERT(data.mSubTreeNode < tree.getNbNodes()); + // mark the merged tree for refit + tree.markNodeForRefit(data.mSubTreeNode); + PX_ASSERT(mMainTreeUpdateMap[data.mMergeIndex] < mMainTree->getNbNodes()); + // mark the main tree for refit + mMainTree->markNodeForRefit(mMainTreeUpdateMap[data.mMergeIndex]); + + // call invalidate object to swap the object indices in the merged trees + invalidateObject(data, objectIndex, swapObject, swapObjectIndex); + + mTreesDirty = true; + } +#if PX_DEBUG + checkValidity(); +#endif // PX_DEBUG + return true; +} + +////////////////////////////////////////////////////////////////////////// +// invalidate object +// remove the objectIndex from the merged tree +void ExtendedBucketPruner::invalidateObject(const ExtendedBucketPrunerData& data, PxU32 objectIndex, const PrunerPayload& swapObject, + PxU32 swapObjectIndex) +{ + // get the merged tree + AABBTree& tree = *mMergedTrees[data.mMergeIndex].mTree; + PX_ASSERT(data.mSubTreeNode < tree.getNbNodes()); + PX_ASSERT(tree.getNodes()[data.mSubTreeNode].isLeaf()); + // get merged tree node + AABBTreeRuntimeNode& node0 = tree.getNodes()[data.mSubTreeNode]; + const PxU32 nbPrims = node0.getNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= NB_OBJECTS_PER_NODE); + + // retrieve the primitives pointer + PxU32* primitives = node0.getPrimitives(tree.getIndices()); + PX_ASSERT(primitives); + + // Look for desired pool index in the leaf + bool foundIt = false; + for (PxU32 i = 0; i < nbPrims; i++) + { + if (objectIndex == primitives[i]) + { + foundIt = true; + const PxU32 last = nbPrims - 1; + node0.setNbRunTimePrimitives(last); + primitives[i] = INVALID_POOL_ID; // Mark primitive index as invalid in the node + + // Swap within the leaf node. No need to update the mapping since they should all point + // to the same tree node anyway. + if (last != i) + Ps::swap(primitives[i], primitives[last]); + break; + } + } + PX_ASSERT(foundIt); + PX_UNUSED(foundIt); + + swapIndex(objectIndex, swapObject, swapObjectIndex); +} + +// Swap object index +// if swapObject is in a merged tree its index needs to be swapped with objectIndex +void ExtendedBucketPruner::swapIndex(PxU32 objectIndex, const PrunerPayload& swapObject, PxU32 swapObjectIndex, bool corePrunerIncluded) +{ + PX_UNUSED(corePrunerIncluded); + if (objectIndex == swapObjectIndex) + return; + + const ExtendedBucketPrunerMap::Entry* extendedPrunerSwapEntry = mExtendedBucketPrunerMap.find(swapObject); + + // if swapped object index is in extended pruner, we have to fix the primitives index + if (extendedPrunerSwapEntry) + { + const ExtendedBucketPrunerData& swapData = extendedPrunerSwapEntry->second; + AABBTree& swapTree = *mMergedTrees[swapData.mMergeIndex].mTree; + // With multiple primitives per leaf, tree nodes may very well be the same for different pool indices. + // However the pool indices may be the same when a swap has been skipped in the pruning pool, in which + // case there is nothing to do. + PX_ASSERT(swapData.mSubTreeNode < swapTree.getNbNodes()); + PX_ASSERT(swapTree.getNodes()[swapData.mSubTreeNode].isLeaf()); + AABBTreeRuntimeNode* node1 = swapTree.getNodes() + swapData.mSubTreeNode; + const PxU32 nbPrims = node1->getNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= NB_OBJECTS_PER_NODE); + + // retrieve the primitives pointer + PxU32* primitives = node1->getPrimitives(swapTree.getIndices()); + PX_ASSERT(primitives); + + // look for desired pool index in the leaf + bool foundIt = false; + for (PxU32 i = 0; i < nbPrims; i++) + { + if (swapObjectIndex == primitives[i]) + { + foundIt = true; + primitives[i] = objectIndex; // point node to the pool object moved to + break; + } + } + PX_ASSERT(foundIt); + PX_UNUSED(foundIt); + } +#if USE_INCREMENTAL_PRUNER + else + { + if(corePrunerIncluded) + mPrunerCore.swapIndex(objectIndex, swapObjectIndex); + } +#endif +} + +////////////////////////////////////////////////////////////////////////// +// Optimized removal of timestamped objects from the extended bucket pruner +PxU32 ExtendedBucketPruner::removeMarkedObjects(PxU32 timeStamp) +{ + // remove objects from the core bucket pruner + PxU32 retVal = mPrunerCore.removeMarkedObjects(timeStamp); + + // nothing to be removed + if(!mCurrentTreeIndex) + return retVal; + + // if last merged tree is the timeStamp to remove, we can clear all + // this is safe as the merged trees array is time ordered, never shifted + if(mMergedTrees[mCurrentTreeIndex - 1].mTimeStamp == timeStamp) + { + retVal += mExtendedBucketPrunerMap.size(); + cleanTrees(); + return retVal; + } + + // get the highest index in the merged trees array, where timeStamp match + // we release than all trees till the index + PxU32 highestTreeIndex = 0xFFFFFFFF; + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + if(mMergedTrees[i].mTimeStamp == timeStamp) + highestTreeIndex = i; + else + break; + } + + // if no timestamp found early exit + if(highestTreeIndex == 0xFFFFFFFF) + { + return retVal; + } + + PX_ASSERT(highestTreeIndex < mCurrentTreeIndex); + // get offset, where valid trees start + const PxU32 mergeTreeOffset = highestTreeIndex + 1; + + // shrink the array to merged trees with a valid timeStamp + mCurrentTreeIndex = mCurrentTreeIndex - mergeTreeOffset; + // go over trees and swap released trees with valid trees from the back (valid trees are at the back) + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + // store bounds, timestamp + mBounds[i] = mMergedTrees[mergeTreeOffset + i].mTree->getNodes()[0].mBV; + mMergedTrees[i].mTimeStamp = mMergedTrees[mergeTreeOffset + i].mTimeStamp; + + // release the tree with timestamp + AABBTree* ptr = mMergedTrees[i].mTree; + ptr->release(); + + // store the valid tree + mMergedTrees[i].mTree = mMergedTrees[mergeTreeOffset + i].mTree; + // store the release tree at the offset + mMergedTrees[mergeTreeOffset + i].mTree = ptr; + mMergedTrees[mergeTreeOffset + i].mTimeStamp = 0; + } + // release the rest of the trees with not valid timestamp + for (PxU32 i = mCurrentTreeIndex; i <= highestTreeIndex; i++) + { + mMergedTrees[i].mTree->release(); + mMergedTrees[i].mTimeStamp = 0; + } + + // build new main AABB tree with only trees with valid valid timeStamp + buildMainAABBTree(); + + // remove all unnecessary trees and map entries + bool removeEntry = false; + PxU32 numRemovedEntries = 0; + ExtendedBucketPrunerMap::EraseIterator eraseIterator = mExtendedBucketPrunerMap.getEraseIterator(); + ExtendedBucketPrunerMap::Entry* entry = eraseIterator.eraseCurrentGetNext(removeEntry); + while (entry) + { + ExtendedBucketPrunerData& data = entry->second; + // data to be removed + if (data.mTimeStamp == timeStamp) + { + removeEntry = true; + numRemovedEntries++; + } + else + { + // update the merge index and main tree node index + PX_ASSERT(highestTreeIndex < data.mMergeIndex); + data.mMergeIndex -= mergeTreeOffset; + removeEntry = false; + } + entry = eraseIterator.eraseCurrentGetNext(removeEntry); + } + +#if PX_DEBUG + checkValidity(); +#endif // PX_DEBUG + // return the number of removed objects + return retVal + numRemovedEntries; +} + +////////////////////////////////////////////////////////////////////////// +// clean all trees, all objects have been released +void ExtendedBucketPruner::cleanTrees() +{ + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + mMergedTrees[i].mTree->release(); + mMergedTrees[i].mTimeStamp = 0; + } + mExtendedBucketPrunerMap.clear(); + mCurrentTreeIndex = 0; + mMainTree->release(); +} + +////////////////////////////////////////////////////////////////////////// +// shift origin +void ExtendedBucketPruner::shiftOrigin(const PxVec3& shift) +{ + mMainTree->shiftOrigin(shift); + + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + mMergedTrees[i].mTree->shiftOrigin(shift); + } + + mPrunerCore.shiftOrigin(shift); +} + +////////////////////////////////////////////////////////////////////////// +// Queries implementation +////////////////////////////////////////////////////////////////////////// +// Raycast/sweeps callback for main AABB tree +template +struct MainTreeRaycastPrunerCallback: public PrunerCallback +{ + MainTreeRaycastPrunerCallback(const PxVec3& origin, const PxVec3& unitDir, const PxVec3& extent, PrunerCallback& prunerCallback, const PruningPool* pool) + : mOrigin(origin), mUnitDir(unitDir), mExtent(extent), mPrunerCallback(prunerCallback), mPruningPool(pool) + { + } + + virtual PxAgain invoke(PxReal& distance, const PrunerPayload& payload) + { + // payload data match merged tree data MergedTree, we can cast it + const AABBTree* aabbTree = reinterpret_cast (payload.data[0]); + // raycast the merged tree + return AABBTreeRaycast()(mPruningPool->getObjects(), mPruningPool->getCurrentWorldBoxes(), *aabbTree, mOrigin, mUnitDir, distance, mExtent, mPrunerCallback); + } + + PX_NOCOPY(MainTreeRaycastPrunerCallback) + +private: + const PxVec3& mOrigin; + const PxVec3& mUnitDir; + const PxVec3& mExtent; + PrunerCallback& mPrunerCallback; + const PruningPool* mPruningPool; +}; + +////////////////////////////////////////////////////////////////////////// +// raycast against the extended bucket pruner +PxAgain ExtendedBucketPruner::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& prunerCallback) const +{ + PxAgain again = true; + + // searc the bucket pruner first + if (mPrunerCore.getNbObjects()) + again = mPrunerCore.raycast(origin, unitDir, inOutDistance, prunerCallback); + + if (again && mExtendedBucketPrunerMap.size()) + { + const PxVec3 extent(0.0f); + // main tree callback + MainTreeRaycastPrunerCallback pcb(origin, unitDir, extent, prunerCallback, mPruningPool); + // traverse the main tree + again = AABBTreeRaycast()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, origin, unitDir, inOutDistance, extent, pcb); + } + + return again; +} + +////////////////////////////////////////////////////////////////////////// +// overlap main tree callback +template +struct MainTreeOverlapPrunerCallback : public PrunerCallback +{ + MainTreeOverlapPrunerCallback(const Test& test, PrunerCallback& prunerCallback, const PruningPool* pool) + : mTest(test), mPrunerCallback(prunerCallback), mPruningPool(pool) + { + } + + virtual PxAgain invoke(PxReal& , const PrunerPayload& payload) + { + // payload data match merged tree data MergedTree, we can cast it + const AABBTree* aabbTree = reinterpret_cast (payload.data[0]); + // overlap the merged tree + return AABBTreeOverlap()(mPruningPool->getObjects(), mPruningPool->getCurrentWorldBoxes(), *aabbTree, mTest, mPrunerCallback); + } + + PX_NOCOPY(MainTreeOverlapPrunerCallback) + +private: + const Test& mTest; + PrunerCallback& mPrunerCallback; + const PruningPool* mPruningPool; +}; + +////////////////////////////////////////////////////////////////////////// +// overlap implementation +PxAgain ExtendedBucketPruner::overlap(const Gu::ShapeData& queryVolume, PrunerCallback& prunerCallback) const +{ + PxAgain again = true; + + // core bucket pruner overlap + if (mPrunerCore.getNbObjects()) + again = mPrunerCore.overlap(queryVolume, prunerCallback); + + if(again && mExtendedBucketPrunerMap.size()) + { + switch (queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if (queryVolume.isOBB()) + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + MainTreeOverlapPrunerCallback pcb(test, prunerCallback, mPruningPool); + again = AABBTreeOverlap()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, test, pcb); + } + else + { + const Gu::AABBAABBTest test(queryVolume.getPrunerInflatedWorldAABB()); + MainTreeOverlapPrunerCallback pcb(test, prunerCallback, mPruningPool); + again = AABBTreeOverlap()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, test, pcb); + } + } + break; + case PxGeometryType::eCAPSULE: + { + const Gu::Capsule& capsule = queryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest test(capsule.p1, queryVolume.getPrunerWorldRot33().column0, + queryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + MainTreeOverlapPrunerCallback pcb(test, prunerCallback, mPruningPool); + again = AABBTreeOverlap()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, test, pcb); + } + break; + case PxGeometryType::eSPHERE: + { + const Gu::Sphere& sphere = queryVolume.getGuSphere(); + Gu::SphereAABBTest test(sphere.center, sphere.radius); + MainTreeOverlapPrunerCallback pcb(test, prunerCallback, mPruningPool); + again = AABBTreeOverlap()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, test, pcb); + } + break; + case PxGeometryType::eCONVEXMESH: + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + MainTreeOverlapPrunerCallback pcb(test, prunerCallback, mPruningPool); + again = AABBTreeOverlap()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, test, pcb); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + } + + return again; +} + +////////////////////////////////////////////////////////////////////////// +// sweep implementation +PxAgain ExtendedBucketPruner::sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& prunerCallback) const +{ + PxAgain again = true; + + // core bucket pruner sweep + if (mPrunerCore.getNbObjects()) + again = mPrunerCore.sweep(queryVolume, unitDir, inOutDistance, prunerCallback); + + if(again && mExtendedBucketPrunerMap.size()) + { + const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB(); + const PxVec3 extents = aabb.getExtents(); + const PxVec3 center = aabb.getCenter(); + MainTreeRaycastPrunerCallback pcb(center, unitDir, extents, prunerCallback, mPruningPool); + again = AABBTreeRaycast()(reinterpret_cast(mMergedTrees), mBounds, *mMainTree, center, unitDir, inOutDistance, extents, pcb); + } + return again; +} + + +////////////////////////////////////////////////////////////////////////// +#include "CmRenderOutput.h" + +// visualization +static void visualizeTree(Cm::RenderOutput& out, PxU32 color, AABBTree* tree) +{ + if(tree && tree->getNodes()) + { + struct Local + { + static void _Draw(const AABBTreeRuntimeNode* root, const AABBTreeRuntimeNode* node, Cm::RenderOutput& out_) + { + out_ << Cm::DebugBox(node->mBV, true); + if (node->isLeaf()) + return; + _Draw(root, node->getPos(root), out_); + _Draw(root, node->getNeg(root), out_); + } + }; + out << PxTransform(PxIdentity); + out << color; + Local::_Draw(tree->getNodes(), tree->getNodes(), out); + } +} + +void ExtendedBucketPruner::visualize(Cm::RenderOutput& out, PxU32 color) const +{ + visualizeTree(out, color, mMainTree); + + for(PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + visualizeTree(out, color, mMergedTrees[i].mTree); + } + + mPrunerCore.visualize(out, color); +} + +////////////////////////////////////////////////////////////////////////// + +#if PX_DEBUG +// extended bucket pruner validity check +bool ExtendedBucketPruner::checkValidity() +{ + Cm::BitMap testBitmap; + testBitmap.resizeAndClear(mCurrentTreeIndex); + for (PxU32 i = 0; i < mMainTree->getNbNodes(); i++) + { + const AABBTreeRuntimeNode& node = mMainTree->getNodes()[i]; + if(node.isLeaf()) + { + const PxU32 nbPrims = node.getNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= NB_OBJECTS_PER_NODE); + + const PxU32* primitives = node.getPrimitives(mMainTree->getIndices()); + for (PxU32 j = 0; j < nbPrims; j++) + { + const PxU32 index = primitives[j]; + // check if index is correct + PX_ASSERT(index < mCurrentTreeIndex); + // mark the index in the test bitmap, must be once set only, all merged trees must be in the main tree + PX_ASSERT(testBitmap.test(index) == IntFalse); + testBitmap.set(index); + } + } + } + + Cm::BitMap mergeTreeTestBitmap; + mergeTreeTestBitmap.resizeAndClear(mPruningPool->getNbActiveObjects()); + for (PxU32 i = 0; i < mCurrentTreeIndex; i++) + { + // check if bounds are the same as the merged tree root bounds + PX_ASSERT(mBounds[i].maximum.x == mMergedTrees[i].mTree->getNodes()[0].mBV.maximum.x); + PX_ASSERT(mBounds[i].maximum.y == mMergedTrees[i].mTree->getNodes()[0].mBV.maximum.y); + PX_ASSERT(mBounds[i].maximum.z == mMergedTrees[i].mTree->getNodes()[0].mBV.maximum.z); + PX_ASSERT(mBounds[i].minimum.x == mMergedTrees[i].mTree->getNodes()[0].mBV.minimum.x); + PX_ASSERT(mBounds[i].minimum.y == mMergedTrees[i].mTree->getNodes()[0].mBV.minimum.y); + PX_ASSERT(mBounds[i].minimum.z == mMergedTrees[i].mTree->getNodes()[0].mBV.minimum.z); + + // check each tree + const AABBTree& mergedTree = *mMergedTrees[i].mTree; + for (PxU32 j = 0; j < mergedTree.getNbNodes(); j++) + { + const AABBTreeRuntimeNode& node = mergedTree.getNodes()[j]; + if (node.isLeaf()) + { + const PxU32 nbPrims = node.getNbRuntimePrimitives(); + PX_ASSERT(nbPrims <= NB_OBJECTS_PER_NODE); + + const PxU32* primitives = node.getPrimitives(mergedTree.getIndices()); + for (PxU32 k = 0; k < nbPrims; k++) + { + const PxU32 index = primitives[k]; + // check if index is correct + PX_ASSERT(index < mPruningPool->getNbActiveObjects()); + // mark the index in the test bitmap, must be once set only, all merged trees must be in the main tree + PX_ASSERT(mergeTreeTestBitmap.test(index) == IntFalse); + mergeTreeTestBitmap.set(index); + + const PrunerPayload& payload = mPruningPool->getObjects()[index]; + const ExtendedBucketPrunerMap::Entry* extendedPrunerSwapEntry = mExtendedBucketPrunerMap.find(payload); + PX_ASSERT(extendedPrunerSwapEntry); + + const ExtendedBucketPrunerData& data = extendedPrunerSwapEntry->second; + PX_ASSERT(data.mMergeIndex == i); + PX_ASSERT(data.mSubTreeNode == j); + } + } + } + } + for (PxU32 i = mCurrentTreeIndex; i < mCurrentTreeCapacity; i++) + { + PX_ASSERT(mMergedTrees[i].mTree->getIndices() == NULL); + PX_ASSERT(mMergedTrees[i].mTree->getNodes() == NULL); + } + for (ExtendedBucketPrunerMap::Iterator iter = mExtendedBucketPrunerMap.getIterator(); !iter.done(); ++iter) + { + const ExtendedBucketPrunerData& data = iter->second; + PX_ASSERT(mMainTreeUpdateMap[data.mMergeIndex] < mMainTree->getNbNodes()); + PX_ASSERT(data.mMergeIndex < mCurrentTreeIndex); + PX_ASSERT(data.mSubTreeNode < mMergedTrees[data.mMergeIndex].mTree->getNbNodes()); + } + return true; +} +#endif + diff --git a/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h new file mode 100644 index 000000000..8ebea6d66 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h @@ -0,0 +1,198 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_EXTENDEDBUCKETPRUNER_H +#define SQ_EXTENDEDBUCKETPRUNER_H + +#include "SqTypedef.h" +#include "SqBucketPruner.h" +#include "SqIncrementalAABBPrunerCore.h" +#include "SqAABBTreeUpdateMap.h" +#include "PsHashMap.h" + +#define USE_INCREMENTAL_PRUNER 1 + +namespace physx +{ +namespace Sq +{ + struct AABBPrunerMergeData; + class AABBTreeMergeData; + +#if USE_INCREMENTAL_PRUNER + typedef IncrementalAABBPrunerCore PrunerCore; +#else + typedef BucketPrunerCore PrunerCore; +#endif + + // Extended bucket pruner data, if an object belongs to the tree of trees, we need to + // remember node for the sub tree, the tree it belongs to and the main tree node + struct ExtendedBucketPrunerData + { + PxU32 mTimeStamp; // timestamp + TreeNodeIndex mSubTreeNode; // sub tree node index + PxU32 mMergeIndex; // index in bounds and merged trees array + }; + + // Merged tree structure, holds tree and its timeStamp, released when no objects is in the tree + // or timeStamped objects are released + struct MergedTree + { + AABBTree* mTree; // AABB tree + size_t mTimeStamp; // needs to be size_t to match PrunerPayload size + }; + // needs to be size_t to match PrunerPayload size, pointer used for AABB tree query callbacks + PX_COMPILE_TIME_ASSERT(sizeof(MergedTree) == sizeof(PrunerPayload)); + + // hashing function for PrunerPaylod key + struct ExtendedBucketPrunerHash + { + PX_FORCE_INLINE uint32_t operator()(const PrunerPayload& payload) const + { +#if PX_P64_FAMILY + // const PxU32 h0 = Ps::hash((const void*)payload.data[0]); + // const PxU32 h1 = Ps::hash((const void*)payload.data[1]); + const PxU32 h0 = PxU32(PX_MAX_U32 & payload.data[0]); + const PxU32 h1 = PxU32(PX_MAX_U32 & payload.data[1]); + return Ps::hash(PxU64(h0) | (PxU64(h1) << 32)); +#else + return Ps::hash(PxU64(payload.data[0]) | (PxU64(payload.data[1]) << 32)); +#endif + } + PX_FORCE_INLINE bool equal(const PrunerPayload& k0, const PrunerPayload& k1) const + { + return (k0.data[0] == k1.data[0]) && (k0.data[1] == k1.data[1]); + } + }; + + // A.B. replace, this is useless, need to be able to traverse the map and release while traversing, also eraseAt failed + typedef Ps::HashMap ExtendedBucketPrunerMap; + + // Extended bucket pruner holds single objects in a bucket pruner and AABBtrees in a tree of trees. + // Base usage of ExtendedBucketPruner is for dynamic AABBPruner new objects, that did not make it + // into new tree. Single objects go directly into a bucket pruner, while merged AABBtrees + // go into a tree of trees. + class ExtendedBucketPruner + { + public: + ExtendedBucketPruner(const PruningPool* pool); + virtual ~ExtendedBucketPruner(); + + // release + void release(); + + // add single object into a bucket pruner directly + PX_FORCE_INLINE bool addObject(const PrunerPayload& object, const PxBounds3& worldAABB, PxU32 timeStamp, const PoolIndex poolIndex) + { +#if USE_INCREMENTAL_PRUNER + PX_UNUSED(worldAABB); + PX_UNUSED(object); + return mPrunerCore.addObject(poolIndex, timeStamp); +#else + PX_UNUSED(poolIndex); + return mPrunerCore.addObject(object, worldAABB, timeStamp); +#endif + } + + // add AABB tree from pruning structure - adds new primitive into main AABB tree + void addTree(const AABBTreeMergeData& mergeData, PxU32 timeStamp); + + // update object + bool updateObject(const PxBounds3& worldAABB, const PrunerPayload& object, const PoolIndex poolIndex); + + // remove object, removed object is replaced in pruning pool by swapped object, indices needs to be updated + bool removeObject(const PrunerPayload& object, PxU32 objectIndex, const PrunerPayload& swapObject, + PxU32 swapObjectIndex, PxU32& timeStamp); + + + // swap object index, the object index can be in core pruner or tree of trees + void swapIndex(PxU32 objectIndex, const PrunerPayload& swapObject, PxU32 swapObjectIndex, bool corePrunerIncluded = true); + + // refit marked nodes in tree of trees + void refitMarkedNodes(const PxBounds3* boxes); + +#if USE_INCREMENTAL_PRUNER + // notify timestampChange - swap trees in incremental pruner + void timeStampChange() { mPrunerCore.timeStampChange(); } +#endif + + + // look for objects marked with input timestamp everywhere in the structure, and remove them. This is the same + // as calling 'removeObject' individually for all these objects, but much more efficient. Returns number of removed objects. + PxU32 removeMarkedObjects(PxU32 timeStamp); + + // queries against the pruner + PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + + // origin shift + void shiftOrigin(const PxVec3& shift); + + // debug visualize + void visualize(Cm::RenderOutput& out, PxU32 color) const; + + PX_FORCE_INLINE void build() { mPrunerCore.build(); } + + PX_FORCE_INLINE PxU32 getNbObjects() const { return mPrunerCore.getNbObjects() + mExtendedBucketPrunerMap.size(); } + + private: + + // separate call for indices invalidation, object can be either in AABBPruner or Bucket pruner, but the swapped object can be + // in the tree of trees + void invalidateObject(const ExtendedBucketPrunerData& object, PxU32 objectIndex, const PrunerPayload& swapObject, + PxU32 swapObjectIndex); + + void resize(PxU32 size); + void buildMainAABBTree(); + void cleanTrees(); + +#if PX_DEBUG + // Extended bucket pruner validity check + bool checkValidity(); +#endif + private: + PrunerCore mPrunerCore; // pruner for single objects + const PruningPool* mPruningPool; // Pruning pool from AABB pruner + ExtendedBucketPrunerMap mExtendedBucketPrunerMap; // Map holding objects from tree merge - objects in tree of trees + AABBTree* mMainTree; // Main tree holding merged trees + AABBTreeUpdateMap mMainTreeUpdateMap; // Main tree updated map - merged trees index to nodes + AABBTreeUpdateMap mMergeTreeUpdateMap; // Merged tree update map used while tree is merged + PxBounds3* mBounds; // Merged trees bounds used for main tree building + MergedTree* mMergedTrees; // Merged trees + PxU32 mCurrentTreeIndex; // Current trees index + PxU32 mCurrentTreeCapacity; // Current tress capacity + bool mTreesDirty; // Dirty marker + }; + +} // namespace Sq + +} + +#endif // SQ_EXTENDEDBUCKETPRUNER_H diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp new file mode 100644 index 000000000..1a90b2b9a --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp @@ -0,0 +1,473 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "common/PxProfileZone.h" +#include "SqIncrementalAABBPruner.h" +#include "SqIncrementalAABBTree.h" +#include "SqAABBTree.h" +#include "GuAABBTreeQuery.h" +#include "GuSphere.h" +#include "GuBox.h" +#include "GuCapsule.h" +#include "GuBounds.h" +#include "PsBitUtils.h" + +using namespace physx; +using namespace Gu; +using namespace Sq; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// PT: currently limited to 15 max +#define NB_OBJECTS_PER_NODE 4 + +#define PARANOIA_CHECKS 0 + +IncrementalAABBPruner::IncrementalAABBPruner(PxU32 sceneLimit, PxU64 contextID) : + mAABBTree (NULL), + mContextID (contextID) +{ + PX_UNUSED(mContextID); + mMapping.resizeUninitialized(sceneLimit); + mPool.preallocate(sceneLimit); + + mChangedLeaves.reserve(32); +} + +IncrementalAABBPruner::~IncrementalAABBPruner() +{ + release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Add, Remove, Update methods + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool IncrementalAABBPruner::addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* payload, PxU32 count, bool ) +{ + PX_PROFILE_ZONE("SceneQuery.prunerAddObjects", mContextID); + + if(!count) + return true; + + const PxU32 valid = mPool.addObjects(results, bounds, payload, count); + + if(mAABBTree) + { + for(PxU32 i=0;iinsert(poolIndex, mPool.getCurrentWorldBoxes(), mChangedLeaves); + updateMapping(poolIndex, node); + } + + #if PARANOIA_CHECKS + test(); + #endif + } + + return valid==count; +} + +void IncrementalAABBPruner::updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node) +{ + // resize mapping if needed + if(mMapping.size() <= poolIndex) + { + mMapping.resize(mMapping.size() * 2); + } + + // if a node was split we need to update the node indices and also the sibling indices + if(!mChangedLeaves.empty()) + { + if(node && node->isLeaf()) + { + for(PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + mMapping[node->getPrimitives(NULL)[j]] = node; + } + } + + for(PxU32 i = 0; i < mChangedLeaves.size(); i++) + { + IncrementalAABBTreeNode* changedNode = mChangedLeaves[i]; + PX_ASSERT(changedNode->isLeaf()); + + for(PxU32 j = 0; j < changedNode->getNbPrimitives(); j++) + { + mMapping[changedNode->getPrimitives(NULL)[j]] = changedNode; + } + } + } + else + { + mMapping[poolIndex] = node; + } +} + +void IncrementalAABBPruner::updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count) +{ + PX_PROFILE_ZONE("SceneQuery.prunerUpdateObjects", mContextID); + + if(!count || !mAABBTree) + return; + + const PxBounds3* newBounds = mPool.getCurrentWorldBoxes(); + for(PxU32 i=0; iupdate(mMapping[poolIndex], poolIndex, newBounds, mChangedLeaves); + // we removed node during update, need to update the mapping + updateMapping(poolIndex, node); + } + +#if PARANOIA_CHECKS + test(); +#endif + +} + +void IncrementalAABBPruner::updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count) +{ + PX_PROFILE_ZONE("SceneQuery.prunerUpdateObjects", mContextID); + + if(!count) + return; + + mPool.updateObjectsAndInflateBounds(handles, indices, newBounds, count); + + if(!mAABBTree) + return; + + const PxBounds3* poolBounds = mPool.getCurrentWorldBoxes(); + for(PxU32 i=0; iupdate(mMapping[poolIndex], poolIndex, poolBounds, mChangedLeaves); + // we removed node during update, need to update the mapping + updateMapping(poolIndex, node); + } + +#if PARANOIA_CHECKS + test(); +#endif + +} + +void IncrementalAABBPruner::removeObjects(const PrunerHandle* handles, PxU32 count) +{ + PX_PROFILE_ZONE("SceneQuery.prunerRemoveObjects", mContextID); + + if(!count) + return; + + for(PxU32 i=0; iremove(mMapping[poolIndex], poolIndex, mPool.getCurrentWorldBoxes()); + // if node moved to its parent + if (node && node->isLeaf()) + { + for (PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + mMapping[index] = node; + } + } + mMapping[poolIndex] = mMapping[poolRelocatedLastIndex]; + // fix indices if we made a swap + if(poolRelocatedLastIndex != poolIndex) + mAABBTree->fixupTreeIndices(mMapping[poolIndex], poolRelocatedLastIndex, poolIndex); + + if(!mAABBTree->getNodes()) + { + release(); + } + } + } + +#if PARANOIA_CHECKS + test(); +#endif + +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Query Implementation + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxAgain IncrementalAABBPruner::overlap(const ShapeData& queryVolume, PrunerCallback& pcb) const +{ + PxAgain again = true; + + if(mAABBTree && mAABBTree->getNodes()) + { + switch(queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if(queryVolume.isOBB()) + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + else + { + const Gu::AABBAABBTest test(queryVolume.getPrunerInflatedWorldAABB()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + } + break; + case PxGeometryType::eCAPSULE: + { + const Gu::Capsule& capsule = queryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest test( capsule.p1, queryVolume.getPrunerWorldRot33().column0, + queryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::eSPHERE: + { + const Gu::Sphere& sphere = queryVolume.getGuSphere(); + Gu::SphereAABBTest test(sphere.center, sphere.radius); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::eCONVEXMESH: + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, test, pcb); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + } + + return again; +} + +PxAgain IncrementalAABBPruner::sweep(const ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PxAgain again = true; + + if(mAABBTree && mAABBTree->getNodes()) + { + const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB(); + const PxVec3 extents = aabb.getExtents(); + again = AABBTreeRaycast()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, aabb.getCenter(), unitDir, inOutDistance, extents, pcb); + } + + return again; +} + +PxAgain IncrementalAABBPruner::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PxAgain again = true; + + if(mAABBTree && mAABBTree->getNodes()) + again = AABBTreeRaycast()(mPool.getObjects(), mPool.getCurrentWorldBoxes(), *mAABBTree, origin, unitDir, inOutDistance, PxVec3(0.0f), pcb); + + return again; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Other methods of Pruner Interface + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// This isn't part of the pruner virtual interface, but it is part of the public interface +// of AABBPruner - it gets called by SqManager to force a rebuild, and requires a commit() before +// queries can take place + +void IncrementalAABBPruner::purge() +{ + release(); +} + +void IncrementalAABBPruner::setRebuildRateHint(PxU32 ) +{ +} + +bool IncrementalAABBPruner::buildStep(bool ) +{ + return true; +} + +bool IncrementalAABBPruner::prepareBuild() +{ + return false; +} + +// Commit either performs a refit if background rebuild is not yet finished +// or swaps the current tree for the second tree rebuilt in the background +void IncrementalAABBPruner::commit() +{ + PX_PROFILE_ZONE("SceneQuery.prunerCommit", mContextID); + + if (!mAABBTree) + { + fullRebuildAABBTree(); + return; + } +} + +void IncrementalAABBPruner::fullRebuildAABBTree() +{ + // Don't bother building an AABB-tree if there isn't a single static object + const PxU32 nbObjects = mPool.getNbActiveObjects(); + if (!nbObjects) + return; + + const PxU32 indicesSize = Ps::nextPowerOfTwo(nbObjects); + if(indicesSize > mMapping.size()) + { + mMapping.resizeUninitialized(indicesSize); + } + + // copy the temp optimized tree into the new incremental tree + mAABBTree = PX_NEW(IncrementalAABBTree)(); + + AABBTreeBuildParams TB; + TB.mNbPrimitives = nbObjects; + TB.mAABBArray = mPool.getCurrentWorldBoxes(); + TB.mLimit = NB_OBJECTS_PER_NODE; + mAABBTree->build(TB, mMapping); + +#if PARANOIA_CHECKS + test(); +#endif +} + +void IncrementalAABBPruner::shiftOrigin(const PxVec3& shift) +{ + mPool.shiftOrigin(shift); + + if(mAABBTree) + mAABBTree->shiftOrigin(shift); +} + +#include "CmRenderOutput.h" +void IncrementalAABBPruner::visualize(Cm::RenderOutput& out, PxU32 color) const +{ + // getAABBTree() asserts when pruner is dirty. NpScene::visualization() does not enforce flushUpdate. see DE7834 + const IncrementalAABBTree* tree = mAABBTree; + + if(tree && tree->getNodes()) + { + struct Local + { + static void _Draw(const IncrementalAABBTreeNode* root, const IncrementalAABBTreeNode* node, Cm::RenderOutput& out_) + { + PxBounds3 bounds; + V4StoreU(node->mBVMin, &bounds.minimum.x); + V4StoreU(node->mBVMax, &bounds.maximum.x); + out_ << Cm::DebugBox(bounds, true); + if (node->isLeaf()) + return; + _Draw(root, node->getPos(root), out_); + _Draw(root, node->getNeg(root), out_); + } + }; + out << PxTransform(PxIdentity); + out << color; + Local::_Draw(tree->getNodes(), tree->getNodes(), out); + } + + // Render added objects not yet in the tree + out << PxTransform(PxIdentity); + out << PxU32(PxDebugColor::eARGB_WHITE); +} + +void IncrementalAABBPruner::release() // this can be called from purge() +{ + if (mAABBTree) + { + PX_DELETE(mAABBTree); + mAABBTree = NULL; + } +} + +void IncrementalAABBPruner::test() +{ + if(mAABBTree) + { + mAABBTree->hierarchyCheck(mPool.getNbActiveObjects(), mPool.getCurrentWorldBoxes()); + for(PxU32 i = 0; i < mPool.getNbActiveObjects(); i++) + { + mAABBTree->checkTreeLeaf(mMapping[i], i); + } + } +} + +void IncrementalAABBPruner::merge(const void* ) +{ + //const AABBPrunerMergeData& pruningStructure = *reinterpret_cast (mergeParams); + + //if(mAABBTree) + //{ + // // index in pruning pool, where new objects were added + // const PxU32 pruningPoolIndex = mPool.getNbActiveObjects() - pruningStructure.mNbObjects; + + // // create tree from given nodes and indices + // AABBTreeMergeData aabbTreeMergeParams(pruningStructure.mNbNodes, pruningStructure.mAABBTreeNodes, + // pruningStructure.mNbObjects, pruningStructure.mAABBTreeIndices, pruningPoolIndex); + + // if (!mIncrementalRebuild) + // { + // // merge tree directly + // mAABBTree->mergeTree(aabbTreeMergeParams); + // } + // else + // { + // mBucketPruner.addTree(aabbTreeMergeParams, mTimeStamp); + // } + //} +} diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h new file mode 100644 index 000000000..bb29d63d0 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_INCREMENTAL_AABB_PRUNER_H +#define SQ_INCREMENTAL_AABB_PRUNER_H + +#include "SqPruner.h" +#include "SqPruningPool.h" +#include "SqIncrementalAABBTree.h" +#include "SqAABBTreeUpdateMap.h" + +namespace physx +{ + +namespace Sq +{ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + class IncrementalAABBPruner : public IncrementalPruner + { + public: + IncrementalAABBPruner(PxU32 sceneLimit, PxU64 contextID); + virtual ~IncrementalAABBPruner(); + + // Pruner + virtual bool addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* userData, PxU32 count, bool hasPruningStructure); + virtual void removeObjects(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count); + virtual void updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count); + virtual void commit(); + virtual PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + virtual PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + virtual const PrunerPayload& getPayload(PrunerHandle handle) const { return mPool.getPayload(handle); } + virtual const PrunerPayload& getPayload(PrunerHandle handle, PxBounds3*& bounds) const { return mPool.getPayload(handle, bounds); } + virtual void preallocate(PxU32 entries) { mPool.preallocate(entries); } + virtual void shiftOrigin(const PxVec3& shift); + virtual void visualize(Cm::RenderOutput& out, PxU32 color) const; + virtual void merge(const void* mergeParams); + //~Pruner + + // IncrementalPruner + virtual void purge(); // gets rid of internal accel struct + virtual void setRebuildRateHint(PxU32 nbStepsForRebuild); // Besides the actual rebuild steps, 3 additional steps are needed. + virtual bool buildStep(bool ); // returns true if finished + virtual bool prepareBuild(); + //~IncrementalPruner + + // direct access for test code + PX_FORCE_INLINE const IncrementalAABBTree* getAABBTree() const { return mAABBTree; } + + // local functions + private: + void release(); + void fullRebuildAABBTree(); + void test(); + void updateMapping(const PoolIndex poolIndex, IncrementalAABBTreeNode* node); + + private: + IncrementalAABBTree* mAABBTree; + + PruningPool mPool; // Pool of AABBs + + Ps::Array mMapping; + + PxU64 mContextID; + NodeList mChangedLeaves; + }; + +} // namespace Sq + +} // namespace physx +#endif diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp new file mode 100644 index 000000000..cf9e17420 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp @@ -0,0 +1,435 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "SqIncrementalAABBPrunerCore.h" +#include "SqIncrementalAABBTree.h" +#include "SqPruningPool.h" +#include "SqAABBTree.h" +#include "GuAABBTreeQuery.h" +#include "GuSphere.h" +#include "GuBox.h" +#include "GuCapsule.h" +#include "GuBounds.h" + +using namespace physx; +using namespace Gu; +using namespace Sq; +using namespace Cm; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define PARANOIA_CHECKS 0 + +IncrementalAABBPrunerCore::IncrementalAABBPrunerCore(const PruningPool* pool) : + mCurrentTree (1), + mLastTree (0), + mPool (pool) +{ + mAABBTree[0].mapping.reserve(256); + mAABBTree[1].mapping.reserve(256); + mChangedLeaves.reserve(32); +} + +IncrementalAABBPrunerCore::~IncrementalAABBPrunerCore() +{ + release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void IncrementalAABBPrunerCore::release() // this can be called from purge() +{ + for(PxU32 i = 0; i < NUM_TREES; i++) + { + if(mAABBTree[i].tree) + { + PX_DELETE(mAABBTree[i].tree); + mAABBTree[i].tree = NULL; + } + mAABBTree[i].mapping.clear(); + mAABBTree[i].timeStamp = 0; + } + mCurrentTree = 1; + mLastTree = 0; +} + +bool IncrementalAABBPrunerCore::addObject(const PoolIndex poolIndex, PxU32 timeStamp) +{ + CoreTree& tree = mAABBTree[mCurrentTree]; + if(!tree.tree || !tree.tree->getNodes()) + { + if(!tree.tree) + tree.tree = PX_NEW(IncrementalAABBTree)(); + tree.timeStamp = timeStamp; + } + PX_ASSERT(tree.timeStamp == timeStamp); + + mChangedLeaves.clear(); + IncrementalAABBTreeNode* node = tree.tree->insert(poolIndex, mPool->getCurrentWorldBoxes(), mChangedLeaves); + updateMapping(tree.mapping, poolIndex, node); + +#if PARANOIA_CHECKS + test(); +#endif + + return true; +} + +void IncrementalAABBPrunerCore::updateMapping(IncrementalPrunerMap& mapping, const PoolIndex poolIndex, IncrementalAABBTreeNode* node) +{ + // if some node leaves changed, we need to update mapping + if(!mChangedLeaves.empty()) + { + if(node && node->isLeaf()) + { + for(PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + mapping[index] = node; + } + } + + for(PxU32 i = 0; i < mChangedLeaves.size(); i++) + { + IncrementalAABBTreeNode* changedNode = mChangedLeaves[i]; + PX_ASSERT(changedNode->isLeaf()); + + for(PxU32 j = 0; j < changedNode->getNbPrimitives(); j++) + { + const PoolIndex index = changedNode->getPrimitives(NULL)[j]; + mapping[index] = changedNode; + } + } + } + else + { + PX_ASSERT(node->isLeaf()); + mapping[poolIndex] = node; + } +} + +bool IncrementalAABBPrunerCore::removeObject(const PoolIndex poolIndex, const PoolIndex poolRelocatedLastIndex, PxU32& timeStamp) +{ + // erase the entry and get the data + IncrementalPrunerMap::Entry entry; + bool foundEntry = true; + const PxU32 treeIndex = mAABBTree[mLastTree].mapping.erase(poolIndex, entry) ? mLastTree : mCurrentTree; + // if it was not found in the last tree look at the current tree + if(treeIndex == mCurrentTree) + foundEntry = mAABBTree[mCurrentTree].mapping.erase(poolIndex, entry); + + // exit somethings is wrong here, entry was not found here + PX_ASSERT(foundEntry); + if(!foundEntry) + return false; + + // tree must exist + PX_ASSERT(mAABBTree[treeIndex].tree); + CoreTree& tree = mAABBTree[treeIndex]; + timeStamp = tree.timeStamp; + + // remove the poolIndex from the tree, update the tree bounds immediatelly + IncrementalAABBTreeNode* node = tree.tree->remove(entry.second, poolIndex, mPool->getCurrentWorldBoxes()); + if(node && node->isLeaf()) + { + for(PxU32 j = 0; j < node->getNbPrimitives(); j++) + { + const PoolIndex index = node->getPrimitives(NULL)[j]; + tree.mapping[index] = node; + } + } + + // nothing to swap, last object, early exit + if(poolIndex == poolRelocatedLastIndex) + { +#if PARANOIA_CHECKS + test(); +#endif + return true; + } + + // fix the indices, we need to swap the index with last index + // erase the relocated index from the tree it is + IncrementalPrunerMap::Entry relocatedEntry; + const PxU32 treeRelocatedIndex = mAABBTree[mCurrentTree].mapping.erase(poolRelocatedLastIndex, relocatedEntry) ? mCurrentTree : mLastTree; + foundEntry = true; + if(treeRelocatedIndex == mLastTree) + foundEntry = mAABBTree[mLastTree].mapping.erase(poolRelocatedLastIndex, relocatedEntry); + + if(foundEntry) + { + CoreTree& relocatedTree = mAABBTree[treeRelocatedIndex]; + + // set the new mapping + relocatedTree.mapping[poolIndex] = relocatedEntry.second; + // update the tree indices - swap + relocatedTree.tree->fixupTreeIndices(relocatedEntry.second, poolRelocatedLastIndex, poolIndex); + } + +#if PARANOIA_CHECKS + test(); +#endif + return true; +} + +void IncrementalAABBPrunerCore::swapIndex(const PoolIndex poolIndex, const PoolIndex poolRelocatedLastIndex) +{ + // fix the indices, we need to swap the index with last index + // erase the relocated index from the tre it is + IncrementalPrunerMap::Entry relocatedEntry; + const PxU32 treeRelocatedIndex = mAABBTree[mCurrentTree].mapping.erase(poolRelocatedLastIndex, relocatedEntry) ? mCurrentTree : mLastTree; + bool foundEntry = true; + if(treeRelocatedIndex == mLastTree) + foundEntry = mAABBTree[mLastTree].mapping.erase(poolRelocatedLastIndex, relocatedEntry); + + // relocated index is not here + if(!foundEntry) + return; + + CoreTree& relocatedTree = mAABBTree[treeRelocatedIndex]; + + // set the new mapping + relocatedTree.mapping[poolIndex] = relocatedEntry.second; + // update the tree indices - swap + relocatedTree.tree->fixupTreeIndices(relocatedEntry.second, poolRelocatedLastIndex, poolIndex); +} + +bool IncrementalAABBPrunerCore::updateObject(const PoolIndex poolIndex) +{ + const IncrementalPrunerMap::Entry* entry = mAABBTree[mLastTree].mapping.find(poolIndex); + const PxU32 treeIndex = entry ? mLastTree : mCurrentTree; + if(!entry) + entry = mAABBTree[mCurrentTree].mapping.find(poolIndex); + + // we have not found it + PX_ASSERT(entry); + if(!entry) + return false; + + CoreTree& tree = mAABBTree[treeIndex]; + mChangedLeaves.clear(); + IncrementalAABBTreeNode* node = tree.tree->updateFast(entry->second, poolIndex, mPool->getCurrentWorldBoxes(), mChangedLeaves); + if(!mChangedLeaves.empty() || node != entry->second) + updateMapping(tree.mapping, poolIndex, node); + +#if PARANOIA_CHECKS + test(false); +#endif + + return true; +} + +PxU32 IncrementalAABBPrunerCore::removeMarkedObjects(PxU32 timeStamp) +{ + // early exit is no tree exists + if(!mAABBTree[mLastTree].tree || !mAABBTree[mLastTree].tree->getNodes()) + { + PX_ASSERT(mAABBTree[mLastTree].mapping.size() == 0); + PX_ASSERT(!mAABBTree[mCurrentTree].tree || mAABBTree[mCurrentTree].timeStamp != timeStamp); + return 0; + } + + PX_UNUSED(timeStamp); + PX_ASSERT(timeStamp == mAABBTree[mLastTree].timeStamp); + + // release the last tree + CoreTree& tree = mAABBTree[mLastTree]; + PxU32 nbObjects = tree.mapping.size(); + tree.mapping.clear(); + tree.timeStamp = 0; + + tree.tree->release(); + + return nbObjects; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Query Implementation + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxAgain IncrementalAABBPrunerCore::overlap(const ShapeData& queryVolume, PrunerCallback& pcb) const +{ + PxAgain again = true; + + for(PxU32 i = 0; i < NUM_TREES; i++) + { + const CoreTree& tree = mAABBTree[i]; + if(tree.tree && tree.tree->getNodes() && again) + { + switch(queryVolume.getType()) + { + case PxGeometryType::eBOX: + { + if(queryVolume.isOBB()) + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, test, pcb); + } + else + { + const Gu::AABBAABBTest test(queryVolume.getPrunerInflatedWorldAABB()); + again = AABBTreeOverlap()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, test, pcb); + } + } + break; + case PxGeometryType::eCAPSULE: + { + const Gu::Capsule& capsule = queryVolume.getGuCapsule(); + const Gu::CapsuleAABBTest test( capsule.p1, queryVolume.getPrunerWorldRot33().column0, + queryVolume.getCapsuleHalfHeight()*2.0f, PxVec3(capsule.radius*SQ_PRUNER_INFLATION)); + again = AABBTreeOverlap()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, test, pcb); + } + break; + case PxGeometryType::eSPHERE: + { + const Gu::Sphere& sphere = queryVolume.getGuSphere(); + Gu::SphereAABBTest test(sphere.center, sphere.radius); + again = AABBTreeOverlap()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, test, pcb); + } + break; + case PxGeometryType::eCONVEXMESH: + { + const Gu::OBBAABBTest test(queryVolume.getPrunerWorldPos(), queryVolume.getPrunerWorldRot33(), queryVolume.getPrunerBoxGeomExtentsInflated()); + again = AABBTreeOverlap()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, test, pcb); + } + break; + case PxGeometryType::ePLANE: + case PxGeometryType::eTRIANGLEMESH: + case PxGeometryType::eHEIGHTFIELD: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + PX_ALWAYS_ASSERT_MESSAGE("unsupported overlap query volume geometry type"); + } + } + } + + return again; +} + +PxAgain IncrementalAABBPrunerCore::sweep(const ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PxAgain again = true; + + for(PxU32 i = 0; i < NUM_TREES; i++) + { + const CoreTree& tree = mAABBTree[i]; + if(tree.tree && tree.tree->getNodes() && again) + { + const PxBounds3& aabb = queryVolume.getPrunerInflatedWorldAABB(); + const PxVec3 extents = aabb.getExtents(); + again = AABBTreeRaycast()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, aabb.getCenter(), unitDir, inOutDistance, extents, pcb); + } + } + + return again; +} + +PxAgain IncrementalAABBPrunerCore::raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback& pcb) const +{ + PxAgain again = true; + + for(PxU32 i = 0; i < NUM_TREES; i++) + { + const CoreTree& tree = mAABBTree[i]; + if(tree.tree && tree.tree->getNodes() && again) + { + again = AABBTreeRaycast()(mPool->getObjects(), mPool->getCurrentWorldBoxes(), *tree.tree, origin, unitDir, inOutDistance, PxVec3(0.0f), pcb); + } + } + return again; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void IncrementalAABBPrunerCore::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i = 0; i < NUM_TREES; i++) + { + if(mAABBTree[i].tree) + { + mAABBTree[i].tree->shiftOrigin(shift); + } + } +} + + +#include "CmRenderOutput.h" +void IncrementalAABBPrunerCore::visualize(Cm::RenderOutput& out, PxU32 color) const +{ + for(PxU32 i = 0; i < NUM_TREES; i++) + { + if(mAABBTree[i].tree && mAABBTree[i].tree->getNodes()) + { + struct Local + { + static void _Draw(const IncrementalAABBTreeNode* root, const IncrementalAABBTreeNode* node, Cm::RenderOutput& out_) + { + PxBounds3 bounds; + V4StoreU(node->mBVMin, &bounds.minimum.x); + V4StoreU(node->mBVMax, &bounds.maximum.x); + out_ << Cm::DebugBox(bounds, true); + if (node->isLeaf()) + return; + _Draw(root, node->getPos(root), out_); + _Draw(root, node->getNeg(root), out_); + } + }; + out << PxTransform(PxIdentity); + out << color; + Local::_Draw(mAABBTree[i].tree->getNodes(), mAABBTree[i].tree->getNodes(), out); + + + // Render added objects not yet in the tree + out << PxTransform(PxIdentity); + out << PxU32(PxDebugColor::eARGB_WHITE); + } + } +} + +void IncrementalAABBPrunerCore::test(bool chierarcyCheck) +{ + PxU32 maxDepth[NUM_TREES] = { 0, 0 }; + for(PxU32 i = 0; i < NUM_TREES; i++) + { + if(mAABBTree[i].tree) + { + if(chierarcyCheck) + mAABBTree[i].tree->hierarchyCheck(mPool->getCurrentWorldBoxes()); + for (IncrementalPrunerMap::Iterator iter = mAABBTree[i].mapping.getIterator(); !iter.done(); ++iter) + { + mAABBTree[i].tree->checkTreeLeaf(iter->second, iter->first); + PxU32 depth = mAABBTree[i].tree->getTreeLeafDepth(iter->second); + if(depth > maxDepth[i]) + maxDepth[i] = depth; + } + } + } +} diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h new file mode 100644 index 000000000..4308a5e02 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h @@ -0,0 +1,115 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_INCREMENTAL_AABB_PRUNER_CORE_H +#define SQ_INCREMENTAL_AABB_PRUNER_CORE_H + +#include "SqPruner.h" +#include "SqPruningPool.h" +#include "SqIncrementalAABBTree.h" +#include "SqAABBTreeUpdateMap.h" +#include "PsHashMap.h" + +namespace physx +{ + +namespace Sq +{ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef Ps::HashMap IncrementalPrunerMap; + + struct CoreTree + { + CoreTree(): + timeStamp(0), + tree(NULL) + { + } + + PxU32 timeStamp; + IncrementalAABBTree* tree; + IncrementalPrunerMap mapping; + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + class IncrementalAABBPrunerCore : public Ps::UserAllocated + { + public: + IncrementalAABBPrunerCore(const PruningPool* pool); + ~IncrementalAABBPrunerCore(); + + void release(); + + bool addObject(const PoolIndex poolIndex, PxU32 timeStamp); + bool removeObject(const PoolIndex poolIndex, const PoolIndex poolRelocatedLastIndex, PxU32& timeStamp); + + // if we swap object from bucket pruner index with an index in the regular AABB pruner + void swapIndex(const PoolIndex poolIndex, const PoolIndex poolRelocatedLastIndex); + + bool updateObject(const PoolIndex poolIndex); + + PxU32 removeMarkedObjects(PxU32 timeStamp); + + PxAgain raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + PxAgain overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const; + PxAgain sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const; + + void shiftOrigin(const PxVec3& shift); + + void visualize(Cm::RenderOutput& out, PxU32 color) const; + + PX_FORCE_INLINE void timeStampChange() + { + // swap current and last tree + mLastTree = (mLastTree + 1) % 2; + mCurrentTree = (mCurrentTree + 1) % 2; + } + + void build() {} + + PX_FORCE_INLINE PxU32 getNbObjects() const { return mAABBTree[0].mapping.size() + mAABBTree[1].mapping.size(); } + + private: + void updateMapping(IncrementalPrunerMap& mapping, const PoolIndex poolIndex, IncrementalAABBTreeNode* node); + void test(bool chierarcyCheck = true); + + private: + static const PxU32 NUM_TREES = 2; + + PxU32 mCurrentTree; + PxU32 mLastTree; + CoreTree mAABBTree[NUM_TREES]; + const PruningPool* mPool; // Pruning pool from AABB pruner + NodeList mChangedLeaves; + }; + +}} + +#endif + diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp new file mode 100644 index 000000000..a1d1fd43b --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp @@ -0,0 +1,1077 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "SqIncrementalAABBTree.h" +#include "SqAABBTree.h" +#include "SqAABBTreeUpdateMap.h" +#include "SqBounds.h" +#include "GuBVHStructure.h" +#include "PsVecMath.h" +#include "PsFPU.h" + +using namespace physx; +using namespace Sq; +using namespace Gu; +using namespace shdfnd::aos; + +#define SUPPORT_TREE_ROTATION 1 +#define DEALLOCATE_RESET 0 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +IncrementalAABBTree::IncrementalAABBTree(): + mIndicesPool("AABBTreeIndicesPool", 256), + mNodesPool("AABBTreeNodesPool", 256 ), + mRoot(NULL) +{ + +} + +IncrementalAABBTree::~IncrementalAABBTree() +{ + release(); +} + +void IncrementalAABBTree::release() +{ + if(mRoot) + { + releaseNode(mRoot); + mRoot = NULL; + } +} + +void IncrementalAABBTree::releaseNode(IncrementalAABBTreeNode* node) +{ + PX_ASSERT(node); + if(node->isLeaf()) + { + mIndicesPool.deallocate(node->mIndices); + } + else + { + releaseNode(node->mChilds[0]); + releaseNode(node->mChilds[1]); + } + if(!node->mParent) + { + mNodesPool.deallocate(reinterpret_cast(node)); + return; + } + if(node->mParent->mChilds[1] == node) + { + mNodesPool.deallocate(reinterpret_cast(node->mParent->mChilds[0])); + } +} + + +// check if node is inside the given bounds +PX_FORCE_INLINE static bool nodeInsideBounds(const Vec4V& nodeMin, const Vec4V& nodeMax, const Vec4V& parentMin, const Vec4V& parentMax) +{ + return !(Ps::IntBool(V4AnyGrtr3(parentMin, nodeMin)) || Ps::IntBool(V4AnyGrtr3(nodeMax, parentMax))); +} + +// update the node parent hierarchy, when insert happen, we can early exit when the node is inside its parent +// no further update is needed +PX_FORCE_INLINE static void updateHierarchyAfterInsert(IncrementalAABBTreeNode* node) +{ + IncrementalAABBTreeNode* parent = node->mParent; + IncrementalAABBTreeNode* testNode = node; + while(parent) + { + // check if we can early exit + if(!nodeInsideBounds(testNode->mBVMin, testNode->mBVMax, parent->mBVMin, parent->mBVMax)) + { + parent->mBVMin = V4Min(parent->mChilds[0]->mBVMin, parent->mChilds[1]->mBVMin); + parent->mBVMax = V4Max(parent->mChilds[0]->mBVMax, parent->mChilds[1]->mBVMax); + } + else + break; + testNode = parent; + parent = parent->mParent; + } +} + +// add an index into the leaf indices list and update the node bounds +PX_FORCE_INLINE static void addPrimitiveIntoNode(IncrementalAABBTreeNode* node, const PoolIndex index, const Vec4V& minV, const Vec4V& maxV) +{ + PX_ASSERT(node->isLeaf()); + AABBTreeIndices& nodeIndices = *node->mIndices; + PX_ASSERT(nodeIndices.nbIndices < NB_OBJECTS_PER_NODE); + + // store the new handle + nodeIndices.indices[nodeIndices.nbIndices++] = index; + + // increase the node bounds + node->mBVMin = V4Min(node->mBVMin, minV); + node->mBVMax = V4Max(node->mBVMax, maxV); + + updateHierarchyAfterInsert(node); +} + +// check if node does intersect with given bounds +PX_FORCE_INLINE static bool nodeIntersection(IncrementalAABBTreeNode& node, const Vec4V& minV, const Vec4V& maxV) +{ + return !(Ps::IntBool(V4AnyGrtr3(node.mBVMin, maxV)) || Ps::IntBool(V4AnyGrtr3(minV, node.mBVMax))); +} + +// traversal strategy +PX_FORCE_INLINE static PxU32 traversalDirection(const IncrementalAABBTreeNode& child0, const IncrementalAABBTreeNode& child1, const Vec4V& testCenterV, + bool testRotation, bool& rotateNode, PxU32& largesRotateNode) +{ + // traverse in the direction of a node which is closer + // we compare the node and object centers + const Vec4V centerCh0V = V4Add(child0.mBVMax, child0.mBVMin); + const Vec4V centerCh1V = V4Add(child1.mBVMax, child1.mBVMin); + + const Vec4V ch0D = V4Sub(testCenterV, centerCh0V); + const Vec4V ch1D = V4Sub(testCenterV, centerCh1V); + + if(testRotation) + { + // if some volume is 3x larger than we do a rotation + const float volumeCompare = 3.0f; + + PX_ALIGN(16, PxVec4) sizeCh0; + PX_ALIGN(16, PxVec4) sizeCh1; + const Vec4V sizeCh0V = V4Sub(child0.mBVMax, child0.mBVMin); + const Vec4V sizeCh1V = V4Sub(child1.mBVMax, child1.mBVMin); + V4StoreA(sizeCh0V, &sizeCh0.x); + V4StoreA(sizeCh1V, &sizeCh1.x); + + const float volumeCh0 = sizeCh0.x*sizeCh0.y*sizeCh0.z; + const float volumeCh1 = sizeCh1.x*sizeCh1.y*sizeCh1.z; + + if((volumeCh0*volumeCompare < volumeCh1) || (volumeCh1*volumeCompare < volumeCh0)) + { + largesRotateNode = (volumeCh0 > volumeCh1) ? 0u : 1u; + rotateNode = true; + } + } + + const BoolV con = FIsGrtr(V4Dot3(ch0D, ch0D), V4Dot3(ch1D, ch1D)); + return (BAllEqTTTT(con) == 1) ? PxU32(1) : PxU32(0); +} + +// remove an index from the leaf +PX_FORCE_INLINE static void removePrimitiveFromNode(IncrementalAABBTreeNode* node, const PoolIndex index) +{ + AABBTreeIndices& indices = *node->mIndices; + PX_ASSERT(indices.nbIndices > 1); + + for (PxU32 i = indices.nbIndices; i--; ) + { + if(node->mIndices->indices[i] == index) + { + node->mIndices->indices[i] = node->mIndices->indices[--indices.nbIndices]; + return; + } + } + // if handle was not found something is wrong here + PX_ASSERT(0); +} + +// check if bounds are equal with given node min/max +PX_FORCE_INLINE static bool boundsEqual(const Vec4V& testMin, const Vec4V& testMax, const Vec4V& nodeMin, const Vec4V& nodeMax) +{ + return (Ps::IntBool(V4AllEq(nodeMin, testMin)) && Ps::IntBool(V4AllEq(testMax, nodeMax))); +} + +// update the node hierarchy bounds when remove happen, we can early exit if the bounds are equal and no bounds update +// did happen +PX_FORCE_INLINE static void updateHierarchyAfterRemove(IncrementalAABBTreeNode* node, const PxBounds3* bounds) +{ + if(node->isLeaf()) + { + const AABBTreeIndices& indices = *node->mIndices; + PX_ASSERT(indices.nbIndices > 0); + + Vec4V bvMin = V4LoadU(&bounds[indices.indices[0]].minimum.x); + Vec4V bvMax = V4LoadU(&bounds[indices.indices[0]].maximum.x); + for(PxU32 i = 1; i < indices.nbIndices; i++) + { + const Vec4V minV = V4LoadU(&bounds[indices.indices[i]].minimum.x); + const Vec4V maxV = V4LoadU(&bounds[indices.indices[i]].maximum.x); + + bvMin = V4Min(bvMin, minV); + bvMax = V4Max(bvMax, maxV); + } + + node->mBVMin = V4ClearW(bvMin); + node->mBVMax = V4ClearW(bvMax); + } + else + { + node->mBVMin = V4Min(node->mChilds[0]->mBVMin, node->mChilds[1]->mBVMin); + node->mBVMax = V4Max(node->mChilds[0]->mBVMax, node->mChilds[1]->mBVMax); + } + + IncrementalAABBTreeNode* parent = node->mParent; + while(parent) + { + const Vec4V newMinV = V4Min(parent->mChilds[0]->mBVMin, parent->mChilds[1]->mBVMin); + const Vec4V newMaxV = V4Max(parent->mChilds[0]->mBVMax, parent->mChilds[1]->mBVMax); + + const bool earlyExit = boundsEqual(newMinV, newMaxV, parent->mBVMin, parent->mBVMax); + if(earlyExit) + break; + + parent->mBVMin = newMinV; + parent->mBVMax = newMaxV; + + parent = parent->mParent; + } +} + +// split the leaf node along the most significant axis +IncrementalAABBTreeNode* IncrementalAABBTree::splitLeafNode(IncrementalAABBTreeNode* node, const PoolIndex index, const Vec4V& minV, const Vec4V& maxV, const PxBounds3* bounds) +{ + PX_ASSERT(node->isLeaf()); + + IncrementalAABBTreeNode* returnNode = NULL; + + // create new pairs of nodes, parent will remain the node (the one we split) + IncrementalAABBTreeNode* child0 = reinterpret_cast(mNodesPool.allocate()); + IncrementalAABBTreeNode* child1 = child0 + 1; + AABBTreeIndices* newIndices = mIndicesPool.allocate(); + + // get the split axis + PX_ALIGN(16, PxVec4) vars; + PX_ALIGN(16, PxVec4) center; + const float half = 0.5f; + const FloatV halfV = FLoad(half); + const Vec4V newMinV = V4Min(node->mBVMin, minV); + const Vec4V newMaxV = V4Max(node->mBVMax, maxV); + const Vec4V centerV = V4Scale(V4Add(newMaxV, newMinV), halfV); + const Vec4V varsV = V4Sub(newMaxV, newMinV); + V4StoreA(varsV, &vars.x); + V4StoreA(centerV, ¢er.x); + const PxU32 axis = Ps::largestAxis(PxVec3(vars.x, vars.y, vars.z)); + + // setup parent + child0->mParent = node; + child1->mParent = node; + child0->mIndices = node->mIndices; + child0->mChilds[1] = NULL; + child1->mIndices = newIndices; + child1->mChilds[1] = NULL; + + AABBTreeIndices& child0Indices = *child0->mIndices; // the original node indices + AABBTreeIndices& child1Indices = *child1->mIndices; // new empty indices + child1Indices.nbIndices = 0; + + // split the node + for(PxU32 i = child0Indices.nbIndices; i--;) + { + const PxBounds3& primitiveBounds = bounds[child0Indices.indices[i]]; + const float pCenter = primitiveBounds.getCenter(axis); + if(center[axis] >= pCenter) + { + // move to new node + child1Indices.indices[child1Indices.nbIndices++] = child0Indices.indices[i]; + child0Indices.nbIndices--; + child0Indices.indices[i] = child0Indices.indices[child0Indices.nbIndices]; + } + } + + // check where to put the new node, if there is still a free space + if(child0Indices.nbIndices == 0 || child1Indices.nbIndices == NB_OBJECTS_PER_NODE) + { + child0Indices.nbIndices = 1; + child0Indices.indices[0] = index; + returnNode = child0; + } + else + { + if(child0Indices.nbIndices == NB_OBJECTS_PER_NODE) + { + child1Indices.nbIndices = 1; + child1Indices.indices[0] = index; + returnNode = child1; + } + else + { + const PxBounds3& primitiveBounds = bounds[index]; + const float pCenter = primitiveBounds.getCenter(axis); + if(center[axis] >= pCenter) + { + // move to new node + child1Indices.indices[child1Indices.nbIndices++] = index; + returnNode = child1; + } + else + { + // move to old node + child0Indices.indices[child0Indices.nbIndices++] = index; + returnNode = child0; + } + } + } + + // update bounds for the new nodes + Vec4V bvMin = V4LoadU(&bounds[child0Indices.indices[0]].minimum.x); + Vec4V bvMax = V4LoadU(&bounds[child0Indices.indices[0]].maximum.x); + for(PxU32 i = 1; i < child0Indices.nbIndices; i++) + { + const Vec4V nodeMinV = V4LoadU(&bounds[child0Indices.indices[i]].minimum.x); + const Vec4V nodeMaxV = V4LoadU(&bounds[child0Indices.indices[i]].maximum.x); + + bvMin = V4Min(bvMin, nodeMinV); + bvMax = V4Max(bvMax, nodeMaxV); + } + child0->mBVMin = V4ClearW(bvMin); + child0->mBVMax = V4ClearW(bvMax); + + bvMin = V4LoadU(&bounds[child1Indices.indices[0]].minimum.x); + bvMax = V4LoadU(&bounds[child1Indices.indices[0]].maximum.x); + for(PxU32 i = 1; i < child1Indices.nbIndices; i++) + { + const Vec4V nodeMinV = V4LoadU(&bounds[child1Indices.indices[i]].minimum.x); + const Vec4V nodeMaxV = V4LoadU(&bounds[child1Indices.indices[i]].maximum.x); + + bvMin = V4Min(bvMin, nodeMinV); + bvMax = V4Max(bvMax, nodeMaxV); + } + child1->mBVMin = V4ClearW(bvMin); + child1->mBVMax = V4ClearW(bvMax); + + // node parent is the same, setup the new childs + node->mChilds[0] = child0; + node->mChilds[1] = child1; + node->mBVMin = newMinV; + node->mBVMax = newMaxV; + + updateHierarchyAfterInsert(node); + + PX_ASSERT(returnNode); + return returnNode; +} + +void IncrementalAABBTree::rotateTree(IncrementalAABBTreeNode* node, NodeList& changedLeaf, PxU32 largesRotateNodeIn, const PxBounds3* bounds, bool rotateAgain) +{ + PX_ASSERT(!node->isLeaf()); + + IncrementalAABBTreeNode* smallerNode = node->mChilds[(largesRotateNodeIn == 0) ? 1 : 0]; + IncrementalAABBTreeNode* largerNode = node->mChilds[largesRotateNodeIn]; + PX_ASSERT(!largerNode->isLeaf()); + + // take a leaf from larger node and add it to the smaller node + const Vec4V testCenterV = V4Add(smallerNode->mBVMax, smallerNode->mBVMin); + IncrementalAABBTreeNode* rotationNode = NULL; // store a node that seems not balanced + PxU32 largesRotateNode = 0; + bool rotateNode = false; + PxU32 traversalIndex = traversalDirection(*largerNode->mChilds[0], *largerNode->mChilds[1], testCenterV, false, rotateNode, largesRotateNode); + IncrementalAABBTreeNode* closestNode = largerNode->mChilds[traversalIndex]; + while(!closestNode->isLeaf()) + { + Ps::prefetchLine(closestNode->mChilds[0]->mChilds[0]); + Ps::prefetchLine(closestNode->mChilds[1]->mChilds[0]); + + traversalIndex = traversalDirection(*closestNode->mChilds[0], *closestNode->mChilds[1], testCenterV, false, rotateNode, largesRotateNode); + closestNode = closestNode->mChilds[traversalIndex]; + } + + // we have the leaf that we want to rotate + // create new parent and remove the current leaf + changedLeaf.findAndReplaceWithLast(closestNode); + IncrementalAABBTreeNode* parent = closestNode->mParent; + IncrementalAABBTreeNodePair* removedPair = reinterpret_cast(parent->mChilds[0]); + PX_ASSERT(!parent->isLeaf()); + + // copy the remaining child into parent + IncrementalAABBTreeNode* remainingChild = (parent->mChilds[0] == closestNode) ? parent->mChilds[1] : parent->mChilds[0]; + parent->mBVMax = remainingChild->mBVMax; + parent->mBVMin = remainingChild->mBVMin; + if(remainingChild->isLeaf()) + { + parent->mIndices = remainingChild->mIndices; + parent->mChilds[1] = NULL; + changedLeaf.findAndReplaceWithLast(remainingChild); + changedLeaf.pushBack(parent); + } + else + { + parent->mChilds[0] = remainingChild->mChilds[0]; + parent->mChilds[0]->mParent = parent; + parent->mChilds[1] = remainingChild->mChilds[1]; + parent->mChilds[1]->mParent = parent; + } + + // update the hieararchy after the node removal + if(parent->mParent) + { + updateHierarchyAfterRemove(parent->mParent, bounds); + } + + // find new spot for the node + // take a leaf from larger node and add it to the smaller node + IncrementalAABBTreeNode* newSpotNode = NULL; + if(smallerNode->isLeaf()) + { + newSpotNode = smallerNode; + } + else + { + const Vec4V testClosestNodeCenterV = V4Add(closestNode->mBVMax, closestNode->mBVMin); + rotationNode = NULL; // store a node that seems not balanced + largesRotateNode = 0; + rotateNode = false; + bool testRotation = rotateAgain; + traversalIndex = traversalDirection(*smallerNode->mChilds[0], *smallerNode->mChilds[1], testClosestNodeCenterV, testRotation, rotateNode, largesRotateNode); + if(rotateNode && !smallerNode->mChilds[largesRotateNode]->isLeaf()) + { + rotationNode = smallerNode; + testRotation = false; + } + newSpotNode = smallerNode->mChilds[traversalIndex]; + while(!newSpotNode->isLeaf()) + { + Ps::prefetchLine(newSpotNode->mChilds[0]->mChilds[0]); + Ps::prefetchLine(newSpotNode->mChilds[1]->mChilds[0]); + + traversalIndex = traversalDirection(*newSpotNode->mChilds[0], *newSpotNode->mChilds[1], testClosestNodeCenterV, testRotation, rotateNode, largesRotateNode); + if(!rotationNode && rotateNode && !newSpotNode->mChilds[largesRotateNode]->isLeaf()) + { + rotationNode = newSpotNode; + testRotation = false; + } + newSpotNode = newSpotNode->mChilds[traversalIndex]; + } + } + + // we have the closest leaf in the smaller child, lets merge it with the closestNode + if(newSpotNode->getNbPrimitives() + closestNode->getNbPrimitives() <= NB_OBJECTS_PER_NODE) + { + // all primitives fit into new spot, we merge here simply + AABBTreeIndices* targetIndices = newSpotNode->mIndices; + const AABBTreeIndices* sourceIndices = closestNode->mIndices; + for(PxU32 i = 0; i < sourceIndices->nbIndices; i++) + { + targetIndices->indices[targetIndices->nbIndices++] = sourceIndices->indices[i]; + } + PX_ASSERT(targetIndices->nbIndices <= NB_OBJECTS_PER_NODE); + if(changedLeaf.find(newSpotNode) == changedLeaf.end()) + changedLeaf.pushBack(newSpotNode); + mIndicesPool.deallocate(closestNode->mIndices); + + newSpotNode->mBVMin = V4Min(newSpotNode->mBVMin, closestNode->mBVMin); + newSpotNode->mBVMax = V4Max(newSpotNode->mBVMax, closestNode->mBVMax); + updateHierarchyAfterInsert(newSpotNode); + } + else + { + // we need to make new parent with newSpotNode and closestNode as childs + // create new pairs of nodes, parent will remain the node (the one we split) + IncrementalAABBTreeNode* child0 = reinterpret_cast(mNodesPool.allocate()); + IncrementalAABBTreeNode* child1 = child0 + 1; + + // setup parent + child0->mParent = newSpotNode; + child1->mParent = newSpotNode; + child0->mIndices = newSpotNode->mIndices; + child0->mChilds[1] = NULL; + child0->mBVMin = newSpotNode->mBVMin; + child0->mBVMax = newSpotNode->mBVMax; + child1->mIndices = closestNode->mIndices; + child1->mChilds[1] = NULL; + child1->mBVMin = closestNode->mBVMin; + child1->mBVMax = closestNode->mBVMax; + + // node parent is the same, setup the new childs + newSpotNode->mChilds[0] = child0; + newSpotNode->mChilds[1] = child1; + + newSpotNode->mBVMin = V4Min(child0->mBVMin, child1->mBVMin); + newSpotNode->mBVMax = V4Max(child0->mBVMax, child1->mBVMax); + + updateHierarchyAfterInsert(newSpotNode); + + changedLeaf.findAndReplaceWithLast(newSpotNode); + changedLeaf.pushBack(child0); + changedLeaf.pushBack(child1); + } + + // deallocate the closestNode, it has been moved +#if DEALLOCATE_RESET + removedPair->mNode0.mChilds[0] = NULL; + removedPair->mNode0.mChilds[1] = NULL; + + removedPair->mNode1.mChilds[0] = NULL; + removedPair->mNode1.mChilds[1] = NULL; +#endif + mNodesPool.deallocate(removedPair); + + // try to do one more rotation for the newly added node part of tree + if(rotationNode) + { + rotateTree(rotationNode, changedLeaf, largesRotateNode, bounds, false); + } +} + + +// insert new bounds into tree +IncrementalAABBTreeNode* IncrementalAABBTree::insert(const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf) +{ + PX_SIMD_GUARD; + + // get the bounds, reset the W value + const Vec4V minV = V4ClearW(V4LoadU(&bounds[index].minimum.x)); + const Vec4V maxV = V4ClearW(V4LoadU(&bounds[index].maximum.x)); + + // check if tree is empty + if(!mRoot) + { + // make it a leaf + AABBTreeIndices* indices = mIndicesPool.construct(index); + mRoot = reinterpret_cast (mNodesPool.allocate()); + mRoot->mBVMin = minV; + mRoot->mBVMax = maxV; + mRoot->mIndices = indices; + mRoot->mChilds[1] = NULL; + mRoot->mParent = NULL; + + return mRoot; + } + else + { + // check if root is a leaf + if(mRoot->isLeaf()) + { + // if we still can insert the primitive into the leaf, or we need to split + if(mRoot->getNbPrimitives() < NB_OBJECTS_PER_NODE) + { + // simply add the primitive into the current leaf + addPrimitiveIntoNode(mRoot, index, minV, maxV); + return mRoot; + } + else + { + // need to split the node + // check if the leaf is not marked as changed, we need to remove it + if(!changedLeaf.empty()) + { + PX_ASSERT(changedLeaf.size() == 1); + if(changedLeaf[0] == mRoot) + changedLeaf.popBack(); + } + IncrementalAABBTreeNode* retNode = splitLeafNode(mRoot, index, minV, maxV, bounds); + mRoot = retNode->mParent; + IncrementalAABBTreeNode* sibling = (mRoot->mChilds[0] == retNode) ? mRoot->mChilds[1] : mRoot->mChilds[0]; + if(sibling->isLeaf()) + changedLeaf.pushBack(sibling); + changedLeaf.pushBack(retNode); + return retNode; + } + } + else + { + const Vec4V testCenterV = V4Add(maxV, minV); + IncrementalAABBTreeNode* returnNode = NULL; + IncrementalAABBTreeNode* rotationNode = NULL; // store a node that seems not balanced + PxU32 largesRotateNode = 0; + bool rotateNode = false; +#if SUPPORT_TREE_ROTATION + bool testRotation = true; +#else + bool testRotation = false; +#endif + // we dont need to modify root, lets traverse the tree to find the right spot + PxU32 traversalIndex = traversalDirection(*mRoot->mChilds[0], *mRoot->mChilds[1], testCenterV, testRotation, rotateNode, largesRotateNode); + if(rotateNode && !mRoot->mChilds[largesRotateNode]->isLeaf()) + { + rotationNode = mRoot; + testRotation = false; + } + IncrementalAABBTreeNode* baseNode = mRoot->mChilds[traversalIndex]; + while(!baseNode->isLeaf()) + { + Ps::prefetchLine(baseNode->mChilds[0]->mChilds[0]); + Ps::prefetchLine(baseNode->mChilds[1]->mChilds[0]); + + traversalIndex = traversalDirection(*baseNode->mChilds[0], *baseNode->mChilds[1], testCenterV, testRotation, rotateNode, largesRotateNode); + if(!rotationNode && rotateNode && !baseNode->mChilds[largesRotateNode]->isLeaf()) + { + rotationNode = baseNode; + testRotation = false; + } + baseNode = baseNode->mChilds[traversalIndex]; + } + + // if we still can insert the primitive into the leaf, or we need to split + if(baseNode->getNbPrimitives() < NB_OBJECTS_PER_NODE) + { + // simply add the primitive into the current leaf + addPrimitiveIntoNode(baseNode, index, minV, maxV); + returnNode = baseNode; + if(!changedLeaf.empty()) + { + PX_ASSERT(changedLeaf.size() == 1); + if(changedLeaf[0] != baseNode) + changedLeaf.pushBack(baseNode); + } + else + changedLeaf.pushBack(baseNode); + } + else + { + // split + // check if the leaf is not marked as changed, we need to remove it + if(!changedLeaf.empty()) + { + PX_ASSERT(changedLeaf.size() == 1); + if(changedLeaf[0] == baseNode) + changedLeaf.popBack(); + } + IncrementalAABBTreeNode* retNode = splitLeafNode(baseNode, index, minV, maxV, bounds); + const IncrementalAABBTreeNode* splitParent = retNode->mParent; + changedLeaf.pushBack(splitParent->mChilds[0]); + changedLeaf.pushBack(splitParent->mChilds[1]); + + returnNode = retNode; + } + + if(rotationNode) + { + rotateTree(rotationNode, changedLeaf, largesRotateNode, bounds, true); + returnNode = NULL; + } + + return returnNode; + } + } +} + +// update the index, do a full remove/insert update +IncrementalAABBTreeNode* IncrementalAABBTree::update(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf) +{ + PX_SIMD_GUARD; + + IncrementalAABBTreeNode* removedNode = remove(node, index, bounds); + if(removedNode && removedNode->isLeaf()) + { + changedLeaf.pushBack(removedNode); + } + return insert(index, bounds, changedLeaf); +} + +// update the index, faster version with a lazy update of objects that moved just a bit +IncrementalAABBTreeNode* IncrementalAABBTree::updateFast(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf) +{ + PX_SIMD_GUARD; + + const Vec4V minV = V4ClearW(V4LoadU(&bounds[index].minimum.x)); + const Vec4V maxV = V4ClearW(V4LoadU(&bounds[index].maximum.x)); + + // for update fast, we dont care if the tree gets slowly unbalanced, we are building a new tree already + if(nodeIntersection(*node, minV, maxV)) + { + updateHierarchyAfterRemove(node, bounds); + return node; + } + else + { + IncrementalAABBTreeNode* removedNode = remove(node, index, bounds); + if(removedNode && removedNode->isLeaf()) + { + changedLeaf.pushBack(removedNode); + } + return insert(index, bounds, changedLeaf); + } +} + +// remove primitive from the tree, return a node if it moved to its parent +IncrementalAABBTreeNode* IncrementalAABBTree::remove(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds) +{ + PX_SIMD_GUARD; + PX_ASSERT(node->isLeaf()); + // if we just remove the primitive from the list + if(node->getNbPrimitives() > 1) + { + removePrimitiveFromNode(node, index); + + // update the hierarchy + updateHierarchyAfterRemove(node, bounds); + return NULL; + } + else + { + // if root node and the last primitive remove root + if(node == mRoot) + { +#if DEALLOCATE_RESET + IncrementalAABBTreeNodePair* removedPair = reinterpret_cast(node); + removedPair->mNode0.mChilds[0] = NULL; + removedPair->mNode0.mChilds[1] = NULL; + + removedPair->mNode1.mChilds[0] = NULL; + removedPair->mNode1.mChilds[1] = NULL; +#endif + mNodesPool.deallocate(reinterpret_cast(node)); + mRoot = NULL; + return NULL; + } + else + { + // create new parent and remove the current leaf + IncrementalAABBTreeNode* parent = node->mParent; + IncrementalAABBTreeNodePair* removedPair = reinterpret_cast(parent->mChilds[0]); + PX_ASSERT(!parent->isLeaf()); + + // copy the remaining child into parent + IncrementalAABBTreeNode* remainingChild = (parent->mChilds[0] == node) ? parent->mChilds[1] : parent->mChilds[0]; + parent->mBVMax = remainingChild->mBVMax; + parent->mBVMin = remainingChild->mBVMin; + if(remainingChild->isLeaf()) + { + parent->mIndices = remainingChild->mIndices; + parent->mChilds[1] = NULL; + } + else + { + parent->mChilds[0] = remainingChild->mChilds[0]; + parent->mChilds[0]->mParent = parent; + parent->mChilds[1] = remainingChild->mChilds[1]; + parent->mChilds[1]->mParent = parent; + } + + if(parent->mParent) + { + updateHierarchyAfterRemove(parent->mParent, bounds); + } + + mIndicesPool.deallocate(node->mIndices); +#if DEALLOCATE_RESET + removedPair->mNode0.mChilds[0] = NULL; + removedPair->mNode0.mChilds[1] = NULL; + + removedPair->mNode1.mChilds[0] = NULL; + removedPair->mNode1.mChilds[1] = NULL; +#endif + mNodesPool.deallocate(removedPair); + return parent; + } + } +} + +// fixup the indices +void IncrementalAABBTree::fixupTreeIndices(IncrementalAABBTreeNode* node, const PoolIndex index, const PoolIndex newIndex) +{ + PX_ASSERT(node->isLeaf()); + + AABBTreeIndices& indices = *node->mIndices; + for(PxU32 i = 0; i < indices.nbIndices; i++) + { + if(indices.indices[i] == index) + { + indices.indices[i] = newIndex; + return; + } + } + PX_ASSERT(0); +} + +// shift node +static void shiftNode(IncrementalAABBTreeNode* node, const Vec4V& shiftV) +{ + node->mBVMax = V4Sub(node->mBVMax, shiftV); + node->mBVMin = V4Sub(node->mBVMin, shiftV); + + if(!node->isLeaf()) + { + shiftNode(node->mChilds[0], shiftV); + shiftNode(node->mChilds[1], shiftV); + } +} + +// shift origin +void IncrementalAABBTree::shiftOrigin(const PxVec3& shift) +{ + if(mRoot) + { + const Vec4V shiftV = V4ClearW(V4LoadU(&shift.x)); + + shiftNode(mRoot, shiftV); + } +} + +static void checkNode(IncrementalAABBTreeNode* node, IncrementalAABBTreeNode* parent, const PxBounds3* bounds, PoolIndex maxIndex, PxU32& numIndices, PxU32& numNodes) +{ + PX_ASSERT(node->mParent == parent); + PX_ASSERT(!parent->isLeaf()); + PX_ASSERT(parent->mChilds[0] == node || parent->mChilds[1] == node); + + numNodes++; + if(!node->isLeaf()) + { + PX_ASSERT(nodeInsideBounds(node->mChilds[0]->mBVMin, node->mChilds[0]->mBVMax, node->mBVMin, node->mBVMax)); + PX_ASSERT(nodeInsideBounds(node->mChilds[1]->mBVMin, node->mChilds[1]->mBVMax, node->mBVMin, node->mBVMax)); + + const Vec4V testMinV = V4Min(parent->mChilds[0]->mBVMin, parent->mChilds[1]->mBVMin); + const Vec4V testMaxV = V4Max(parent->mChilds[0]->mBVMax, parent->mChilds[1]->mBVMax); + + PX_UNUSED(testMinV); + PX_UNUSED(testMaxV); + PX_ASSERT(nodeInsideBounds(node->mBVMin, node->mBVMax, testMinV, testMaxV)); + + checkNode(node->mChilds[0], node, bounds, maxIndex, numIndices, numNodes); + checkNode(node->mChilds[1], node, bounds, maxIndex, numIndices, numNodes); + } + else + { + const AABBTreeIndices& indices = *node->mIndices; + PX_ASSERT(indices.nbIndices); + Vec4V testMinV = V4ClearW(V4LoadU(&bounds[indices.indices[0]].minimum.x)); + Vec4V testMaxV = V4ClearW(V4LoadU(&bounds[indices.indices[0]].maximum.x)); + for(PxU32 i = 0; i < indices.nbIndices; i++) + { + PX_ASSERT(indices.indices[i] < maxIndex); + numIndices++; + + const Vec4V minV = V4ClearW(V4LoadU(&bounds[indices.indices[i]].minimum.x)); + const Vec4V maxV = V4ClearW(V4LoadU(&bounds[indices.indices[i]].maximum.x)); + + testMinV = V4Min(testMinV, minV); + testMaxV = V4Max(testMaxV, maxV); + + PX_ASSERT(nodeInsideBounds(minV, maxV, node->mBVMin, node->mBVMax)); + } + + PX_ASSERT(boundsEqual(testMinV, testMaxV, node->mBVMin, node->mBVMax)); + } +} + +void IncrementalAABBTree::hierarchyCheck(PoolIndex maxIndex, const PxBounds3* bounds) +{ + PxU32 numHandles = 0; + PxU32 numPosNodes = 0; + PxU32 numNegNodes = 0; + if(mRoot && !mRoot->isLeaf()) + { + checkNode(mRoot->mChilds[0], mRoot, bounds, maxIndex, numHandles, numPosNodes); + checkNode(mRoot->mChilds[1], mRoot, bounds, maxIndex, numHandles, numNegNodes); + + PX_ASSERT(numHandles == maxIndex); + } +} + +void IncrementalAABBTree::hierarchyCheck(const PxBounds3* bounds) +{ + PxU32 numHandles = 0; + PxU32 numPosNodes = 0; + PxU32 numNegNodes = 0; + if(mRoot && !mRoot->isLeaf()) + { + checkNode(mRoot->mChilds[0], mRoot, bounds, 0xFFFFFFFF, numHandles, numPosNodes); + checkNode(mRoot->mChilds[1], mRoot, bounds, 0xFFFFFFFF, numHandles, numNegNodes); + } +} + +void IncrementalAABBTree::checkTreeLeaf(IncrementalAABBTreeNode* leaf, PoolIndex h) +{ + PX_ASSERT(leaf->isLeaf()); + + const AABBTreeIndices& indices = *leaf->mIndices; + bool found = false; + for(PxU32 i = 0; i < indices.nbIndices; i++) + { + if(indices.indices[i] == h) + { + found = true; + break; + } + } + PX_UNUSED(found); + PX_ASSERT(found); +} + +PxU32 IncrementalAABBTree::getTreeLeafDepth(IncrementalAABBTreeNode* leaf) +{ + PxU32 depth = 1; + IncrementalAABBTreeNode* parent = leaf->mParent; + while(parent) + { + depth++; + parent = parent->mParent; + } + return depth; +} + +// build the tree from given bounds +bool IncrementalAABBTree::build(AABBTreeBuildParams& params, Ps::Array& mapping) +{ + // Init stats + BuildStats stats; + const PxU32 nbPrimitives = params.mNbPrimitives; + if (!nbPrimitives) + return false; + + PxU32* indices = NULL; + const bool buildStatus = buildAABBTree(params, mNodeAllocator, stats, indices); + PX_UNUSED(buildStatus); + PX_ASSERT(buildStatus); + + PX_FREE_AND_RESET(params.mCache); + + IncrementalAABBTreeNode** treeNodes = reinterpret_cast(PX_ALLOC(sizeof(IncrementalAABBTreeNode*)*(stats.getCount()), "temp node helper array")); + PxMemSet(treeNodes, 0, sizeof(IncrementalAABBTreeNode*)*(stats.getCount())); + + clone(mapping, indices, treeNodes); + mRoot = treeNodes[0]; + mRoot->mParent = NULL; + + PX_FREE_AND_RESET(indices); + PX_FREE_AND_RESET(treeNodes); + + mNodeAllocator.release(); + return true; +} + +// clone the tree, the tree is computed in the NodeAllocator, similar to AABBTree flatten +void IncrementalAABBTree::clone(Ps::Array& mapping, const PxU32* _indices, IncrementalAABBTreeNode** treeNodes) +{ + PxU32 offset = 0; + const PxU32 nbSlabs = mNodeAllocator.mSlabs.size(); + for (PxU32 s = 0; s(mNodesPool.allocate()); + treeNodes[offset] = destNode; + } + + destNode->mBVMin = V4ClearW(V4LoadU(&pool[i].mBV.minimum.x)); + destNode->mBVMax = V4ClearW(V4LoadU(&pool[i].mBV.maximum.x)); + + if (pool[i].isLeaf()) + { + AABBTreeIndices* indices = mIndicesPool.allocate(); + destNode->mIndices = indices; + destNode->mChilds[1] = NULL; + indices->nbIndices = pool[i].getNbPrimitives(); + PX_ASSERT(indices->nbIndices <= 16); + const PxU32* sourceIndices = _indices + pool[i].mNodeIndex; + for (PxU32 iIndices = 0; iIndices < indices->nbIndices; iIndices++) + { + const PxU32 sourceIndex = sourceIndices[iIndices]; + indices->indices[iIndices] = sourceIndex; + PX_ASSERT(sourceIndex < mapping.size()); + mapping[sourceIndex] = destNode; + } + } + else + { + PX_ASSERT(pool[i].mPos); + PxU32 localNodeIndex = 0xffffffff; + PxU32 nodeBase = 0; + for (PxU32 j = 0; j= mNodeAllocator.mSlabs[j].mPool && pool[i].mPos < mNodeAllocator.mSlabs[j].mPool + mNodeAllocator.mSlabs[j].mNbUsedNodes) + { + localNodeIndex = PxU32(pool[i].mPos - mNodeAllocator.mSlabs[j].mPool); + break; + } + nodeBase += mNodeAllocator.mSlabs[j].mNbUsedNodes; + } + const PxU32 nodeIndex = nodeBase + localNodeIndex; + + IncrementalAABBTreeNode* child0 = treeNodes[nodeIndex]; + IncrementalAABBTreeNode* child1 = treeNodes[nodeIndex + 1]; + if(!child0) + { + PX_ASSERT(!child1); + child0 = reinterpret_cast(mNodesPool.allocate()); + child1 = child0 + 1; + treeNodes[nodeIndex] = child0; + treeNodes[nodeIndex + 1] = child1; + } + + destNode->mChilds[0] = child0; + destNode->mChilds[1] = child1; + child0->mParent = destNode; + child1->mParent = destNode; + } + offset++; + } + } +} + +void IncrementalAABBTree::copyNode(IncrementalAABBTreeNode& destNode, const BVHNode& sourceNode, + const BVHNode* nodeBase, IncrementalAABBTreeNode* parent, const PxU32* primitivesBase, + Ps::Array& mapping) +{ + destNode.mParent = parent; + destNode.mBVMin = V4ClearW(V4LoadU(&sourceNode.mBV.minimum.x)); + destNode.mBVMax = V4ClearW(V4LoadU(&sourceNode.mBV.maximum.x)); + if(sourceNode.isLeaf()) + { + AABBTreeIndices* indices = mIndicesPool.allocate(); + destNode.mIndices = indices; + indices->nbIndices = sourceNode.getNbPrimitives(); + const PxU32* sourceIndices = sourceNode.getPrimitives(primitivesBase); + for(PxU32 i = 0; i < indices->nbIndices; i++) + { + const PxU32 sourceIndex = sourceIndices[i]; + indices->indices[i] = sourceIndex; + mapping[sourceIndex] = &destNode; + } + } + else + { + IncrementalAABBTreeNodePair* nodePair = mNodesPool.construct(); + IncrementalAABBTreeNode* child0 = &nodePair->mNode0; + IncrementalAABBTreeNode* child1 = &nodePair->mNode1; + + destNode.mChilds[0] = child0; + destNode.mChilds[1] = child1; + + copyNode(*destNode.mChilds[0], *sourceNode.getPos(nodeBase), nodeBase, &destNode, primitivesBase, mapping); + copyNode(*destNode.mChilds[1], *sourceNode.getNeg(nodeBase), nodeBase, &destNode, primitivesBase, mapping); + } +} + +// build the tree from the prebuild AABB tree +void IncrementalAABBTree::copy(const BVHStructure& bvhStructure, Ps::Array& mapping) +{ + if(bvhStructure.getNbBounds() == 0) + return; + + IncrementalAABBTreeNodePair* nodePair = mNodesPool.construct(); + mRoot = &nodePair->mNode0; + + const BVHNode* nodes = bvhStructure.getNodes(); + copyNode(*mRoot, *nodes, nodes, NULL, bvhStructure.getIndices(), mapping); +} + diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h new file mode 100644 index 000000000..6f5602739 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_INCREMENTAL_AABB_TREE_H +#define SQ_INCREMENTAL_AABB_TREE_H + +#include "foundation/PxBounds3.h" +#include "PsUserAllocated.h" +#include "PsVecMath.h" +#include "PsPool.h" +#include "PsHashMap.h" +#include "GuAABBTreeBuild.h" +#include "SqPruner.h" +#include "SqTypedef.h" +#include "SqPrunerMergeData.h" + + +namespace physx +{ + namespace Gu + { + struct BVHNode; + } + + using namespace shdfnd::aos; + + namespace Sq + { + class AABBTree; + + #define NB_OBJECTS_PER_NODE 4 + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // tree indices, can change in runtime + struct AABBTreeIndices + { + PX_FORCE_INLINE AABBTreeIndices(PoolIndex index) : + nbIndices(1) + { + indices[0] = index; + for(PxU32 i = 1; i < NB_OBJECTS_PER_NODE; i++) + { + indices[i] = 0; + } + } + + PxU32 nbIndices; + PoolIndex indices[NB_OBJECTS_PER_NODE]; + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // tree node, has parent information + class IncrementalAABBTreeNode : public Ps::UserAllocated + { + public: + PX_FORCE_INLINE IncrementalAABBTreeNode(): + mParent(NULL) + { + mChilds[0] = NULL; + mChilds[1] = NULL; + } + PX_FORCE_INLINE IncrementalAABBTreeNode(AABBTreeIndices* indices): + mParent(NULL) + { + mIndices = indices; + mChilds[1] = NULL; + } + PX_FORCE_INLINE ~IncrementalAABBTreeNode() {} + + PX_FORCE_INLINE PxU32 isLeaf() const { return PxU32(mChilds[1]==0); } + + PX_FORCE_INLINE const PxU32* getPrimitives(const PxU32* ) const { return &mIndices->indices[0]; } + PX_FORCE_INLINE PxU32* getPrimitives(PxU32* ) { return &mIndices->indices[0]; } + PX_FORCE_INLINE PxU32 getNbPrimitives() const { return mIndices->nbIndices; } + + PX_FORCE_INLINE const IncrementalAABBTreeNode* getPos(const IncrementalAABBTreeNode* ) const { return mChilds[0]; } + PX_FORCE_INLINE const IncrementalAABBTreeNode* getNeg(const IncrementalAABBTreeNode* ) const { return mChilds[1]; } + + PX_FORCE_INLINE IncrementalAABBTreeNode* getPos(IncrementalAABBTreeNode* ) { return mChilds[0]; } + PX_FORCE_INLINE IncrementalAABBTreeNode* getNeg(IncrementalAABBTreeNode* ) { return mChilds[1]; } + + PX_FORCE_INLINE void getAABBCenterExtentsV(physx::shdfnd::aos::Vec3V* center, physx::shdfnd::aos::Vec3V* extents) const + { + const float half = 0.5f; + const FloatV halfV = FLoad(half); + + *extents = Vec3V_From_Vec4V((V4Scale(V4Sub(mBVMax, mBVMin), halfV))); + *center = Vec3V_From_Vec4V((V4Scale(V4Add(mBVMax, mBVMin), halfV))); + } + + PX_FORCE_INLINE void getAABBCenterExtentsV2(physx::shdfnd::aos::Vec3V* center, physx::shdfnd::aos::Vec3V* extents) const + { + *extents = Vec3V_From_Vec4V((V4Sub(mBVMax, mBVMin))); + *center = Vec3V_From_Vec4V((V4Add(mBVMax, mBVMin))); + } + + PX_FORCE_INLINE void getAABBMinMaxV(physx::shdfnd::aos::Vec4V* minV, physx::shdfnd::aos::Vec4V* maxV) const + { + *minV = mBVMin; + *maxV = mBVMax; + } + + Vec4V mBVMin; // Global bounding-volume min enclosing all the node-related primitives + Vec4V mBVMax; // Global bounding-volume max enclosing all the node-related primitives + IncrementalAABBTreeNode* mParent; // node parent + union + { + IncrementalAABBTreeNode* mChilds[2]; // childs of node if not a leaf + AABBTreeIndices* mIndices; // if leaf, indices information + }; + }; + + struct IncrementalAABBTreeNodePair + { + IncrementalAABBTreeNode mNode0; + IncrementalAABBTreeNode mNode1; + }; + + typedef Ps::Array NodeList; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // incremental AABB tree, all changes are immediatelly reflected to the tree + class IncrementalAABBTree : public Ps::UserAllocated + { + public: + IncrementalAABBTree(); + ~IncrementalAABBTree(); + + // Build the tree for the first time + bool build(Gu::AABBTreeBuildParams& params, Ps::Array& mapping); + + // insert a new index into the tree + IncrementalAABBTreeNode* insert(const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf); + + // update the object in the tree - full update insert/remove + IncrementalAABBTreeNode* update(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf); + // update the object in the tree, faster method, that may unballance the tree + IncrementalAABBTreeNode* updateFast(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds, NodeList& changedLeaf); + + // remove object from the tree + IncrementalAABBTreeNode* remove(IncrementalAABBTreeNode* node, const PoolIndex index, const PxBounds3* bounds); + + // fixup the tree indices, if we swapped the objects in the pruning pool + void fixupTreeIndices(IncrementalAABBTreeNode* node, const PoolIndex index, const PoolIndex newIndex); + + // origin shift + void shiftOrigin(const PxVec3& shift); + + // get the tree root node + const IncrementalAABBTreeNode* getNodes() const { return mRoot; } + + // define this function so we can share the scene query code with regular AABBTree + const PxU32* getIndices() const { return NULL; } + + // paranoia checks + void hierarchyCheck(PoolIndex maxIndex, const PxBounds3* bounds); + void hierarchyCheck(const PxBounds3* bounds); + void checkTreeLeaf(IncrementalAABBTreeNode* leaf, PoolIndex h); + PxU32 getTreeLeafDepth(IncrementalAABBTreeNode* leaf); + + void release(); + + void copy(const Gu::BVHStructure& bvhStructure, Ps::Array& mapping); + + private: + + // clone the tree from the generic AABB tree that was built + void clone(Ps::Array& mapping, const PxU32* indices, IncrementalAABBTreeNode** treeNodes); + + void copyNode(IncrementalAABBTreeNode& destNode, const Gu::BVHNode& sourceNode, const Gu::BVHNode* nodeBase, + IncrementalAABBTreeNode* parent, const PxU32* primitivesBase, Ps::Array& mapping); + + // split leaf node, the newly added object does not fit in + IncrementalAABBTreeNode* splitLeafNode(IncrementalAABBTreeNode* node, const PoolIndex index, const Vec4V& minV, const Vec4V& maxV, const PxBounds3* bounds); + + void rotateTree(IncrementalAABBTreeNode* node, NodeList& changedLeaf, PxU32 largesRotateNode, const PxBounds3* bounds, bool rotateAgain); + + void releaseNode(IncrementalAABBTreeNode* node); + private: + Ps::Pool mIndicesPool; + Ps::Pool mNodesPool; + IncrementalAABBTreeNode* mRoot; + + Gu::NodeAllocator mNodeAllocator; + }; + } +} + +#endif diff --git a/src/PhysX/physx/source/scenequery/src/SqMetaData.cpp b/src/PhysX/physx/source/scenequery/src/SqMetaData.cpp new file mode 100644 index 000000000..e5a57940f --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqMetaData.cpp @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxMetaData.h" + +#include "SqPruningStructure.h" + +using namespace physx; +using namespace Sq; + +/////////////////////////////////////////////////////////////////////////////// + +void PruningStructure::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_VCLASS(stream, PruningStructure) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PruningStructure, PxBase) + + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mNbNodes[0], 0) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mNbNodes[1], 0) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, AABBTreeRuntimeNode, mAABBTreeNodes[0], PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, AABBTreeRuntimeNode, mAABBTreeNodes[1], PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mNbObjects[0], 0) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mNbObjects[1], 0) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mAABBTreeIndices[0], PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mAABBTreeIndices[1], PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxU32, mNbActors, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, PxActor*, mActors, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, PruningStructure, bool, mValid, 0) +} + + diff --git a/src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp b/src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp new file mode 100644 index 000000000..327d5ebf0 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp @@ -0,0 +1,182 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "foundation/PxMemory.h" +#include "SqPruningPool.h" + +using namespace physx; +using namespace Sq; +using namespace Cm; + +PruningPool::PruningPool() : + mNbObjects (0), + mMaxNbObjects (0), + mWorldBoxes (NULL), + mObjects (NULL), + mHandleToIndex (NULL), + mIndexToHandle (NULL), + mFirstRecycledHandle(INVALID_PRUNERHANDLE) +{ +} + +PruningPool::~PruningPool() +{ + PX_FREE_AND_RESET(mWorldBoxes); + PX_FREE_AND_RESET(mObjects); + PX_FREE_AND_RESET(mHandleToIndex); + PX_FREE_AND_RESET(mIndexToHandle); +} + +bool PruningPool::resize(PxU32 newCapacity) +{ + // PT: we always allocate one extra box, to make sure we can safely use V4 loads on the array + PxBounds3* newBoxes = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(newCapacity+1), "PxBounds3")); + PrunerPayload* newData = reinterpret_cast(PX_ALLOC(sizeof(PrunerPayload)*newCapacity, "PrunerPayload*")); + PrunerHandle* newIndexToHandle = reinterpret_cast(PX_ALLOC(sizeof(PrunerHandle)*newCapacity, "Pruner Index Mapping")); + PoolIndex* newHandleToIndex = reinterpret_cast(PX_ALLOC(sizeof(PoolIndex)*newCapacity, "Pruner Index Mapping")); + if( (NULL==newBoxes) || (NULL==newData) || (NULL==newIndexToHandle) || (NULL==newHandleToIndex) + ) + { + PX_FREE_AND_RESET(newBoxes); + PX_FREE_AND_RESET(newData); + PX_FREE_AND_RESET(newIndexToHandle); + PX_FREE_AND_RESET(newHandleToIndex); + return false; + } + + if(mWorldBoxes) PxMemCopy(newBoxes, mWorldBoxes, mNbObjects*sizeof(PxBounds3)); + if(mObjects) PxMemCopy(newData, mObjects, mNbObjects*sizeof(PrunerPayload)); + if(mIndexToHandle) PxMemCopy(newIndexToHandle, mIndexToHandle, mNbObjects*sizeof(PrunerHandle)); + if(mHandleToIndex) PxMemCopy(newHandleToIndex, mHandleToIndex, mMaxNbObjects*sizeof(PoolIndex)); + mMaxNbObjects = newCapacity; + + PX_FREE_AND_RESET(mWorldBoxes); + PX_FREE_AND_RESET(mObjects); + PX_FREE_AND_RESET(mHandleToIndex); + PX_FREE_AND_RESET(mIndexToHandle); + mWorldBoxes = newBoxes; + mObjects = newData; + mHandleToIndex = newHandleToIndex; + mIndexToHandle = newIndexToHandle; + + return true; +} + +void PruningPool::preallocate(PxU32 newCapacity) +{ + if(newCapacity>mMaxNbObjects) + resize(newCapacity); +} + +PxU32 PruningPool::addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* payload, PxU32 count) +{ + for(PxU32 i=0;i(mMaxNbObjects*2, 64))) + { + // pool can return an invalid handle if memory alloc fails + // should probably have an error here or not handle this + results[i] = INVALID_PRUNERHANDLE; // PT: we need to write the potentially invalid handle to let users know which object failed first + return i; + } + } + PX_ASSERT(mNbObjects!=mMaxNbObjects); + + const PoolIndex index = mNbObjects++; + + // update mHandleToIndex and mIndexToHandle mappings + PrunerHandle handle; + if(mFirstRecycledHandle != INVALID_PRUNERHANDLE) + { + // mFirstRecycledHandle is an entry into a freelist for removed slots + // this path is only taken if we have any removed slots + handle = mFirstRecycledHandle; + mFirstRecycledHandle = mHandleToIndex[handle]; + } + else + { + handle = index; + } + + // PT: TODO: investigate why we added mIndexToHandle/mHandleToIndex. The initial design with 'Prunable' objects didn't need these arrays. + + // PT: these 3 arrays are "parallel" + mWorldBoxes [index] = bounds[i]; // store the payload and AABB in parallel arrays + mObjects [index] = payload[i]; + mIndexToHandle [index] = handle; + + mHandleToIndex[handle] = index; + results[i] = handle; + } + return count; +} + +PoolIndex PruningPool::removeObject(PrunerHandle h) +{ + PX_ASSERT(mNbObjects); + + // remove the object and its AABB by provided PrunerHandle and update mHandleToIndex and mIndexToHandle mappings + const PoolIndex indexOfRemovedObject = mHandleToIndex[h]; // retrieve object's index from handle + + const PoolIndex indexOfLastObject = --mNbObjects; // swap the object at last index with index + if(indexOfLastObject!=indexOfRemovedObject) + { + // PT: move last object's data to recycled spot (from removed object) + + // PT: the last object has moved so we need to handle the mappings for this object + // PT: TODO: investigate where this double-mapping comes from. Should not be needed... + + // PT: these 3 arrays are "parallel" + const PrunerHandle handleOfLastObject = mIndexToHandle[indexOfLastObject]; + mWorldBoxes [indexOfRemovedObject] = mWorldBoxes [indexOfLastObject]; + mObjects [indexOfRemovedObject] = mObjects [indexOfLastObject]; + mIndexToHandle [indexOfRemovedObject] = handleOfLastObject; + + mHandleToIndex[handleOfLastObject] = indexOfRemovedObject; + } + + // mHandleToIndex also stores the freelist for removed handles (in place of holes formed by removed handles) + mHandleToIndex[h] = mFirstRecycledHandle; // update linked list of available recycled handles + mFirstRecycledHandle = h; // update the list head + + return indexOfLastObject; +} + +void PruningPool::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i=0; i < mNbObjects; i++) + { + mWorldBoxes[i].minimum -= shift; + mWorldBoxes[i].maximum -= shift; + } +} diff --git a/src/PhysX/physx/source/scenequery/src/SqPruningPool.h b/src/PhysX/physx/source/scenequery/src/SqPruningPool.h new file mode 100644 index 000000000..9886f8a23 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqPruningPool.h @@ -0,0 +1,120 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_PRUNINGPOOL_H +#define SQ_PRUNINGPOOL_H + +#include "SqPruner.h" +#include "SqTypedef.h" +#include "SqBounds.h" + +namespace physx +{ +namespace Sq +{ + // This class is designed to maintain a two way mapping between pair(PrunerPayload,AABB) and PrunerHandle + // Internally there's also an index for handles (AP: can be simplified?) + // This class effectively stores bounded pruner payloads, returns a PrunerHandle and allows O(1) + // access to them using a PrunerHandle + // Supported operations are add, remove, update bounds + class PruningPool + { + public: + PruningPool(); + ~PruningPool(); + + PX_FORCE_INLINE const PrunerPayload& getPayload(PrunerHandle handle) const { return mObjects[getIndex(handle)]; } + + PX_FORCE_INLINE const PrunerPayload& getPayload(PrunerHandle handle, PxBounds3*& bounds) const + { + const PoolIndex index = getIndex(handle); + bounds = mWorldBoxes + index; + return mObjects[index]; + } + + void shiftOrigin(const PxVec3& shift); + + // PT: adds 'count' objects to the pool. Needs 'count' bounds and 'count' payloads passed as input. Writes out 'count' handles + // in 'results' array. Function returns number of successfully added objects, ideally 'count' but can be less in case we run + // out of memory. + PxU32 addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* payload, PxU32 count); + + // this function will swap the last object with the hole formed by removed PrunerHandle object + // and return the removed last object's index in the pool + PoolIndex removeObject(PrunerHandle h); + + // Data access + PX_FORCE_INLINE PoolIndex getIndex(PrunerHandle h)const { return mHandleToIndex[h]; } + PX_FORCE_INLINE PrunerPayload* getObjects() const { return mObjects; } + PX_FORCE_INLINE PxU32 getNbActiveObjects() const { return mNbObjects; } + PX_FORCE_INLINE const PxBounds3* getCurrentWorldBoxes() const { return mWorldBoxes; } + PX_FORCE_INLINE PxBounds3* getCurrentWorldBoxes() { return mWorldBoxes; } + + PX_FORCE_INLINE const PxBounds3& getWorldAABB(PrunerHandle h) const + { + return mWorldBoxes[getIndex(h)]; + } + + PX_FORCE_INLINE void updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count) + { + for(PxU32 i=0; igetConcreteType(); + if (type == PxConcreteType::eRIGID_STATIC) + { + static_cast(mActors[i])->getShapeManager().setPruningStructure(NULL); + } + else if (type == PxConcreteType::eRIGID_DYNAMIC) + { + static_cast(mActors[i])->getShapeManager().setPruningStructure(NULL); + } + } + + if(getBaseFlags() & PxBaseFlag::eOWNS_MEMORY) + { + delete this; + } + else + { + this->~PruningStructure(); + } +} + +template +static void getShapeBounds(PxRigidActor* actor, bool dynamic, PxBounds3* bounds, PxU32& numShapes) +{ + PruningIndex::Enum treeStructure = dynamic ? PruningIndex::eDYNAMIC : PruningIndex::eSTATIC; + ActorType& a = *static_cast(actor); + const PxU32 nbShapes = a.getNbShapes(); + for (PxU32 iShape = 0; iShape < nbShapes; iShape++) + { + NpShape* shape = a.getShapeManager().getShapes()[iShape]; + if (shape->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE) + { + const Scb::Shape& scbShape = shape->getScbShape(); + const Scb::Actor& scbActor = a.getScbActorFast(); + + (gComputeBoundsTable[treeStructure])(*bounds, scbShape, scbActor); + bounds++; + numShapes++; + } + } +} + +////////////////////////////////////////////////////////////////////////// +bool PruningStructure::build(PxRigidActor*const* actors, PxU32 nbActors) +{ + PX_ASSERT(actors); + PX_ASSERT(nbActors > 0); + + PxU32 numShapes[2] = { 0, 0 }; + // parse the actors first to get the shapes size + for (PxU32 actorsDone = 0; actorsDone < nbActors; actorsDone++) + { + if (actorsDone + 1 < nbActors) + Ps::prefetch(actors[actorsDone + 1], sizeof(NpRigidDynamic)); // worst case: PxRigidStatic is smaller + + PxType type = actors[actorsDone]->getConcreteType(); + const PxRigidActor& actor = *(actors[actorsDone]); + + Scb::ControlState::Enum cs = NpActor::getScbFromPxActor(actor).getControlState(); + if (!((cs == Scb::ControlState::eNOT_IN_SCENE) || ((cs == Scb::ControlState::eREMOVE_PENDING)))) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PrunerStructure::build: Actor already assigned to a scene!"); + return false; + } + + const PxU32 nbShapes = actor.getNbShapes(); + bool hasQueryShape = false; + for (PxU32 iShape = 0; iShape < nbShapes; iShape++) + { + PxShape* shape; + actor.getShapes(&shape, 1, iShape); + if(shape->getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE) + { + hasQueryShape = true; + if (type == PxConcreteType::eRIGID_STATIC) + numShapes[PruningIndex::eSTATIC]++; + else + numShapes[PruningIndex::eDYNAMIC]++; + } + } + + // each provided actor must have a query shape + if(!hasQueryShape) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PrunerStructure::build: Provided actor has no scene query shape!"); + return false; + } + + if (type == PxConcreteType::eRIGID_STATIC) + { + NpRigidStatic* rs = static_cast(actors[actorsDone]); + if(rs->getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PrunerStructure::build: Provided actor has already a pruning structure!"); + return false; + } + rs->getShapeManager().setPruningStructure(this); + } + else if (type == PxConcreteType::eRIGID_DYNAMIC) + { + NpRigidDynamic* rd = static_cast(actors[actorsDone]); + if (rd->getShapeManager().getPruningStructure()) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PrunerStructure::build: Provided actor has already a pruning structure!"); + return false; + } + rd->getShapeManager().setPruningStructure(this); + } + else + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PrunerStructure::build: Provided actor is not a rigid actor!"); + return false; + } + } + + PxBounds3* bounds[2] = { NULL, NULL }; + + for (PxU32 i = 0; i < 2; i++) + { + if(numShapes[i]) + { + bounds[i] = reinterpret_cast(PX_ALLOC(sizeof(PxBounds3)*(numShapes[i] + 1), "Pruner bounds")); + } + } + + // now I go again and gather bounds and payload + numShapes[PruningIndex::eSTATIC] = 0; + numShapes[PruningIndex::eDYNAMIC] = 0; + for (PxU32 actorsDone = 0; actorsDone < nbActors; actorsDone++) + { + PxType type = actors[actorsDone]->getConcreteType(); + if (type == PxConcreteType::eRIGID_STATIC) + { + getShapeBounds(actors[actorsDone], false, + &bounds[PruningIndex::eSTATIC][numShapes[PruningIndex::eSTATIC]], numShapes[PruningIndex::eSTATIC]); + } + else if (type == PxConcreteType::eRIGID_DYNAMIC) + { + getShapeBounds(actors[actorsDone], true, + &bounds[PruningIndex::eDYNAMIC][numShapes[PruningIndex::eDYNAMIC]], numShapes[PruningIndex::eDYNAMIC]); + } + } + + AABBTree aabbTrees[2]; + for (PxU32 i = 0; i < 2; i++) + { + mNbObjects[i] = numShapes[i]; + if (numShapes[i]) + { + // create the AABB tree + AABBTreeBuildParams sTB; + sTB.mNbPrimitives = numShapes[i]; + sTB.mAABBArray = bounds[i]; + sTB.mLimit = NB_OBJECTS_PER_NODE; + bool status = aabbTrees[i].build(sTB); + + PX_UNUSED(status); + PX_ASSERT(status); + + // store the tree nodes + mNbNodes[i] = aabbTrees[i].getNbNodes(); + mAABBTreeNodes[i] = reinterpret_cast(PX_ALLOC(sizeof(AABBTreeRuntimeNode)*mNbNodes[i], "AABBTreeRuntimeNode")); + PxMemCopy(mAABBTreeNodes[i], aabbTrees[i].getNodes(), sizeof(AABBTreeRuntimeNode)*mNbNodes[i]); + mAABBTreeIndices[i] = reinterpret_cast(PX_ALLOC(sizeof(PxU32)*mNbObjects[i], "PxU32")); + PxMemCopy(mAABBTreeIndices[i], aabbTrees[i].getIndices(), sizeof(PxU32)*mNbObjects[i]); + + // discard the data + PX_FREE(bounds[i]); + } + } + + // store the actors for verification and serialization + mNbActors = nbActors; + mActors = reinterpret_cast(PX_ALLOC(sizeof(PxActor*)*mNbActors, "PxActor*")); + PxMemCopy(mActors, actors, sizeof(PxActor*)*mNbActors); + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +PruningStructure* PruningStructure::createObject(PxU8*& address, PxDeserializationContext& context) +{ + PruningStructure* obj = new (address)PruningStructure(PxBaseFlag::eIS_RELEASABLE); + address += sizeof(PruningStructure); + obj->importExtraData(context); + obj->resolveReferences(context); + return obj; +} + +////////////////////////////////////////////////////////////////////////// + +void PruningStructure::resolveReferences(PxDeserializationContext& context) +{ + if (!isValid()) + return; + + for (PxU32 i = 0; i < mNbActors; i++) + { + context.translatePxBase(mActors[i]); + } +} + +////////////////////////////////////////////////////////////////////////// + +void PruningStructure::requiresObjects(PxProcessPxBaseCallback& c) +{ + if (!isValid()) + return; + + for (PxU32 i = 0; i < mNbActors; i++) + { + c.process(*mActors[i]); + } +} + +////////////////////////////////////////////////////////////////////////// + +void PruningStructure::exportExtraData(PxSerializationContext& stream) +{ + if (!isValid()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PrunerStructure::exportExtraData: Pruning structure is invalid!"); + return; + } + + for (PxU32 i = 0; i < 2; i++) + { + if (mAABBTreeNodes[i]) + { + // store nodes + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mAABBTreeNodes[i], mNbNodes[i] * sizeof(AABBTreeRuntimeNode)); + } + + if(mAABBTreeIndices[i]) + { + // store indices + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mAABBTreeIndices[i], mNbObjects[i] * sizeof(PxU32)); + } + } + + if(mActors) + { + // store actor pointers + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(mActors, mNbActors * sizeof(PxActor*)); + } +} + +////////////////////////////////////////////////////////////////////////// + +void PruningStructure::importExtraData(PxDeserializationContext& context) +{ + if (!isValid()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PrunerStructure::importExtraData: Pruning structure is invalid!"); + return; + } + + for (PxU32 i = 0; i < 2; i++) + { + if (mAABBTreeNodes[i]) + { + mAABBTreeNodes[i] = context.readExtraData(mNbNodes[i]); + } + if(mAABBTreeIndices[i]) + { + mAABBTreeIndices[i] = context.readExtraData(mNbObjects[i]); + } + } + + if (mActors) + { + // read actor pointers + mActors = context.readExtraData(mNbActors); + } +} + +////////////////////////////////////////////////////////////////////////// + +PxU32 PruningStructure::getRigidActors(PxRigidActor** userBuffer, PxU32 bufferSize, PxU32 startIndex/* =0 */) const +{ + if(!isValid()) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PrunerStructure::getRigidActors: Pruning structure is invalid!"); + return 0; + } + + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mActors, mNbActors); +} + +////////////////////////////////////////////////////////////////////////// + +void PruningStructure::invalidate(PxActor* actor) +{ + PX_ASSERT(actor); + + // remove actor from the actor list to avoid mem corruption + // this slow, but should be called only with error msg send to user about invalid behavior + for (PxU32 i = 0; i < mNbActors; i++) + { + if(mActors[i] == actor) + { + // set pruning structure to NULL and remove the actor from the list + PxType type = mActors[i]->getConcreteType(); + if (type == PxConcreteType::eRIGID_STATIC) + { + static_cast(mActors[i])->getShapeManager().setPruningStructure(NULL); + } + else if (type == PxConcreteType::eRIGID_DYNAMIC) + { + static_cast(mActors[i])->getShapeManager().setPruningStructure(NULL); + } + + mActors[i] = mActors[mNbActors--]; + break; + } + } + + mValid = false; +} + diff --git a/src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp b/src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp new file mode 100644 index 000000000..9449ac4c1 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp @@ -0,0 +1,604 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "SqSceneQueryManager.h" +#include "SqAABBPruner.h" +#include "SqIncrementalAABBPruner.h" +#include "SqBucketPruner.h" +#include "SqPrunerMergeData.h" +#include "SqBounds.h" +#include "NpBatchQuery.h" +#include "PxFiltering.h" +#include "NpRigidDynamic.h" +#include "NpRigidStatic.h" +#include "NpArticulationLink.h" +#include "CmTransformUtils.h" +#include "PsAllocator.h" +#include "PxSceneDesc.h" +#include "ScBodyCore.h" +#include "SqPruner.h" +#include "SqCompoundPruner.h" +#include "GuBounds.h" +#include "NpShape.h" +#include "common/PxProfileZone.h" + +using namespace physx; +using namespace Sq; +using namespace Sc; + +PrunerExt::PrunerExt() : + mPruner (NULL), + mDirtyList (PX_DEBUG_EXP("SQmDirtyList")), + mPrunerType (PxPruningStructureType::eLAST), + mTimestamp (0xffffffff) +{ +} + +PrunerExt::~PrunerExt() +{ + PX_DELETE_AND_RESET(mPruner); +} + +void PrunerExt::init(PxPruningStructureType::Enum type, PxU64 contextID, PxU32 ) +{ + if(0) // PT: to force testing the bucket pruner + { + mPrunerType = PxPruningStructureType::eNONE; + mTimestamp = 0; + mPruner = PX_NEW(BucketPruner); + return; + } + + mPrunerType = type; + mTimestamp = 0; + Pruner* pruner = NULL; + switch(type) + { + case PxPruningStructureType::eNONE: { pruner = PX_NEW(BucketPruner); break; } + case PxPruningStructureType::eDYNAMIC_AABB_TREE: { pruner = PX_NEW(AABBPruner)(true, contextID); break; } + case PxPruningStructureType::eSTATIC_AABB_TREE: { pruner = PX_NEW(AABBPruner)(false, contextID); break; } + case PxPruningStructureType::eLAST: break; + } + mPruner = pruner; +} + +void PrunerExt::preallocate(PxU32 nbShapes) +{ + if(nbShapes > mDirtyMap.size()) + mDirtyMap.resize(nbShapes); + + if(mPruner) + mPruner->preallocate(nbShapes); +} + +void PrunerExt::flushMemory() +{ + if(!mDirtyList.size()) + mDirtyList.reset(); + + // PT: TODO: flush bitmap here + + // PT: TODO: flush pruner here? +} + +void PrunerExt::flushShapes(PxU32 index) +{ + const PxU32 numDirtyList = mDirtyList.size(); + if(!numDirtyList) + return; + const PrunerHandle* const prunerHandles = mDirtyList.begin(); + + const ComputeBoundsFunc func = gComputeBoundsTable[index]; + + for(PxU32 i=0; igetPayload(handle, bounds); + (func)(*bounds, *(reinterpret_cast(pp.data[0])), *(reinterpret_cast(pp.data[1]))); //PAYLOAD + } + // PT: batch update happens after the loop instead of once per loop iteration + mPruner->updateObjectsAfterManualBoundsUpdates(prunerHandles, numDirtyList); + mTimestamp += numDirtyList; + mDirtyList.clear(); +} + +// PT: TODO: re-inline this +void PrunerExt::addToDirtyList(PrunerHandle handle) +{ + Cm::BitMap& dirtyMap = mDirtyMap; + if(!dirtyMap.test(handle)) + { + dirtyMap.set(handle); + mDirtyList.pushBack(handle); + mTimestamp++; + } +} + +// PT: TODO: re-inline this +Ps::IntBool PrunerExt::isDirty(PrunerHandle handle) const +{ + return mDirtyMap.test(handle); +} + +// PT: TODO: re-inline this +void PrunerExt::removeFromDirtyList(PrunerHandle handle) +{ + Cm::BitMap& dirtyMap = mDirtyMap; + if(dirtyMap.test(handle)) + { + dirtyMap.reset(handle); + mDirtyList.findAndReplaceWithLast(handle); + } +} + +// PT: TODO: re-inline this +void PrunerExt::growDirtyList(PrunerHandle handle) +{ + // pruners must either provide indices in order or reuse existing indices, so this 'if' is enough to ensure we have space for the new handle + // PT: TODO: fix this. There is just no need for any of it. The pruning pool itself could support the feature for free, similar to what we do + // in MBP. There would be no need for the bitmap or the dirty list array. However doing this through the virtual interface would be clumsy, + // adding the cost of virtual calls for very cheap & simple operations. It would be a lot easier to drop it and go back to what we had before. + + Cm::BitMap& dirtyMap = mDirtyMap; + if(dirtyMap.size() <= handle) + dirtyMap.resize(PxMax(dirtyMap.size() * 2, 1024)); + PX_ASSERT(handle mDirtyList.size()) + mDirtyList.reserve(nbShapes); +} + +void CompoundPrunerExt::flushMemory() +{ + if(!mDirtyList.size()) + mDirtyList.clear(); +} + +void CompoundPrunerExt::flushShapes() +{ + const PxU32 numDirtyList = mDirtyList.size(); + if(!numDirtyList) + return; + + const CompoundPair* const compoundPairs = mDirtyList.getEntries(); + + for(PxU32 i=0; igetPayload(handle, compoundId, bounds); + const Scb::Shape& scbShape = *reinterpret_cast(pp.data[0]); + + const PxTransform& shape2Actor = scbShape.getShape2Actor(); + Gu::computeBounds(*bounds, scbShape.getGeometry(), shape2Actor, 0.0f, NULL, SQ_PRUNER_INFLATION); + + // A.B. not very effective, we might do better here + mPruner->updateObjectAfterManualBoundsUpdates(compoundId, handle); + } + + mDirtyList.clear(); +} + +// PT: TODO: re-inline this +void CompoundPrunerExt::addToDirtyList(PrunerCompoundId compoundId, PrunerHandle handle) +{ + mDirtyList.insert(CompoundPair(compoundId, handle)); +} + +// PT: TODO: re-inline this +Ps::IntBool CompoundPrunerExt::isDirty(PrunerCompoundId compoundId, PrunerHandle handle) const +{ + return mDirtyList.contains(CompoundPair(compoundId, handle)); +} + +// PT: TODO: re-inline this +void CompoundPrunerExt::removeFromDirtyList(PrunerCompoundId compoundId, PrunerHandle handle) +{ + mDirtyList.erase(CompoundPair(compoundId, handle)); +} + +/////////////////////////////////////////////////////////////////////////////// + +SceneQueryManager::SceneQueryManager( Scb::Scene& scene, PxPruningStructureType::Enum staticStructure, + PxPruningStructureType::Enum dynamicStructure, PxU32 dynamicTreeRebuildRateHint, + const PxSceneLimits& limits) : + mScene (scene) +{ + mPrunerExt[PruningIndex::eSTATIC].init(staticStructure, scene.getContextId(), limits.maxNbStaticShapes ? limits.maxNbStaticShapes : 1024); + mPrunerExt[PruningIndex::eDYNAMIC].init(dynamicStructure, scene.getContextId(), limits.maxNbDynamicShapes ? limits.maxNbDynamicShapes : 1024); + + setDynamicTreeRebuildRateHint(dynamicTreeRebuildRateHint); + + preallocate(limits.maxNbStaticShapes, limits.maxNbDynamicShapes); + + mDynamicBoundsSync.mPruner = mPrunerExt[PruningIndex::eDYNAMIC].pruner(); + mDynamicBoundsSync.mTimestamp = &mPrunerExt[PruningIndex::eDYNAMIC].mTimestamp; + + mCompoundPrunerExt.mPruner = PX_NEW(BVHCompoundPruner); + mCompoundPrunerExt.preallocate(32); + + mPrunerNeedsUpdating = false; +} + +SceneQueryManager::~SceneQueryManager() +{ +} + +void SceneQueryManager::flushMemory() +{ + for(PxU32 i=0;iaddObjects(&handle, &b, &pp, 1, hasPrunerStructure); + + mPrunerExt[index].growDirtyList(handle); + } + else + { + PxBounds3 b; + const PxTransform& shape2Actor = scbShape.getShape2Actor(); + Gu::computeBounds(b, scbShape.getGeometry(), shape2Actor, 0.0f, NULL, SQ_PRUNER_INFLATION); + + PX_ASSERT(mCompoundPrunerExt.pruner()); + mCompoundPrunerExt.pruner()->addObject(compoundId, handle, b, pp); + } + + return createPrunerData(index, handle); +} + +const PrunerPayload& SceneQueryManager::getPayload(PrunerCompoundId compoundId, PrunerData data) const +{ + const PxU32 index = getPrunerIndex(data); + const PrunerHandle handle = getPrunerHandle(data); + + if(compoundId == INVALID_PRUNERHANDLE) + return mPrunerExt[index].pruner()->getPayload(handle); + else + return mCompoundPrunerExt.pruner()->getPayload(handle, compoundId); +} + +void SceneQueryManager::removePrunerShape(PrunerCompoundId compoundId, PrunerData data) +{ + mPrunerNeedsUpdating = true; + const PxU32 index = getPrunerIndex(data); + const PrunerHandle handle = getPrunerHandle(data); + + mPrunerExt[index].invalidateTimestamp(); + if(compoundId == INVALID_PRUNERHANDLE) + { + PX_ASSERT(mPrunerExt[index].pruner()); + + mPrunerExt[index].removeFromDirtyList(handle); + + mPrunerExt[index].pruner()->removeObjects(&handle, 1); + } + else + { + mCompoundPrunerExt.removeFromDirtyList(compoundId, handle); + mCompoundPrunerExt.pruner()->removeObject(compoundId, handle); + } +} + +void SceneQueryManager::setDynamicTreeRebuildRateHint(PxU32 rebuildRateHint) +{ + mRebuildRateHint = rebuildRateHint; + + for(PxU32 i=0;i(mPrunerExt[i].pruner())->setRebuildRateHint(rebuildRateHint); + } +} + +void SceneQueryManager::afterSync(PxSceneQueryUpdateMode::Enum updateMode) +{ + PX_PROFILE_ZONE("Sim.sceneQueryBuildStep", mScene.getContextId()); + + if(updateMode == PxSceneQueryUpdateMode::eBUILD_DISABLED_COMMIT_DISABLED) + { + mPrunerNeedsUpdating = true; + return; + } + + // flush user modified objects + flushShapes(); + + bool commit = updateMode == PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_ENABLED; + + for(PxU32 i = 0; i<2; i++) + { + if(mPrunerExt[i].pruner() && mPrunerExt[i].type() == PxPruningStructureType::eDYNAMIC_AABB_TREE) + static_cast(mPrunerExt[i].pruner())->buildStep(true); + + if(commit) + mPrunerExt[i].pruner()->commit(); + } + + mPrunerNeedsUpdating = !commit; +} + +void SceneQueryManager::flushShapes() +{ + PX_PROFILE_ZONE("SceneQuery.flushShapes", mScene.getContextId()); + + // must already have acquired writer lock here + + for(PxU32 i=0; icommit(); + + Ps::memoryBarrier(); + mPrunerNeedsUpdating = false; + } + mSceneQueryLock.unlock(); + } +} + +void SceneQueryManager::forceDynamicTreeRebuild(bool rebuildStaticStructure, bool rebuildDynamicStructure) +{ + PX_PROFILE_ZONE("SceneQuery.forceDynamicTreeRebuild", mScene.getContextId()); + + const bool rebuild[PruningIndex::eCOUNT] = { rebuildStaticStructure, rebuildDynamicStructure }; + + Ps::Mutex::ScopedLock lock(mSceneQueryLock); + for(PxU32 i=0; i(mPrunerExt[i].pruner())->purge(); + static_cast(mPrunerExt[i].pruner())->commit(); + } + } +} + +void SceneQueryManager::sceneQueryBuildStep(PruningIndex::Enum index) +{ + PX_PROFILE_ZONE("SceneQuery.sceneQueryBuildStep", mScene.getContextId()); + + if (mPrunerExt[index].pruner() && mPrunerExt[index].type() == PxPruningStructureType::eDYNAMIC_AABB_TREE) + { + const bool buildFinished = static_cast(mPrunerExt[index].pruner())->buildStep(false); + if(buildFinished) + { + mPrunerNeedsUpdating = true; + } + } +} + +bool SceneQueryManager::prepareSceneQueriesUpdate(PruningIndex::Enum index) +{ + bool retVal = false; + if (mPrunerExt[index].pruner() && mPrunerExt[index].type() == PxPruningStructureType::eDYNAMIC_AABB_TREE) + { + retVal = static_cast(mPrunerExt[index].pruner())->prepareBuild(); + } + return retVal; +} + +void SceneQueryManager::shiftOrigin(const PxVec3& shift) +{ + for(PxU32 i=0; ishiftOrigin(shift); + + mCompoundPrunerExt.pruner()->shiftOrigin(shift); +} + +void DynamicBoundsSync::sync(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds, PxU32 count, const Cm::BitMap& dirtyShapeSimMap) +{ + if(!count) + return; + + PxU32 startIndex = 0; + PxU32 numIndices = count; + + // if shape sim map is not empty, parse the indices and skip update for the dirty one + if(dirtyShapeSimMap.count()) + { + numIndices = 0; + + for(PxU32 i=0; iupdateObjectsAndInflateBounds(handles + startIndex, indices + startIndex, bounds, numIndices); + numIndices = 0; + startIndex = i + 1; + } + else + numIndices++; + } + // PT: we fallback to the next line on purpose - no "else" + } + + mPruner->updateObjectsAndInflateBounds(handles + startIndex, indices + startIndex, bounds, numIndices); + + (*mTimestamp)++; +} + +void SceneQueryManager::addPruningStructure(const Sq::PruningStructure& pS) +{ + if(pS.getTreeNodes(PruningIndex::eSTATIC)) + { + AABBPrunerMergeData params(pS.getTreeNbNodes(PruningIndex::eSTATIC), pS.getTreeNodes(PruningIndex::eSTATIC), + pS.getNbObjects(PruningIndex::eSTATIC), pS.getTreeIndices(PruningIndex::eSTATIC)); + mPrunerExt[PruningIndex::eSTATIC].pruner()->merge(¶ms); + } + if(pS.getTreeNodes(PruningIndex::eDYNAMIC)) + { + AABBPrunerMergeData params(pS.getTreeNbNodes(PruningIndex::eDYNAMIC), pS.getTreeNodes(PruningIndex::eDYNAMIC), + pS.getNbObjects(PruningIndex::eDYNAMIC), pS.getTreeIndices(PruningIndex::eDYNAMIC)); + mPrunerExt[PruningIndex::eDYNAMIC].pruner()->merge(¶ms); + } +} + +void SceneQueryManager::addCompoundShape(const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& compoundTransform, PrunerData* prunerData, const Scb::Shape** scbShapes, const Scb::Actor& scbActor) +{ + PX_ASSERT(mCompoundPrunerExt.mPruner); + + const PxU32 nbShapes = bvhStructure.getNbBounds(); + + PX_ALLOCA(res, PrunerHandle, nbShapes); + PX_ALLOCA(payloads, PrunerPayload, nbShapes); + + for(PxU32 i = 0; i < nbShapes; i++) + { + payloads[i].data[0] = size_t(scbShapes[i]); //PAYLOAD + payloads[i].data[1] = size_t(&scbActor); //PAYLOAD + } + + CompoundFlag::Enum flags = (scbActor.getActorType() == PxActorType::eRIGID_DYNAMIC) ? CompoundFlag::DYNAMIC_COMPOUND : CompoundFlag::STATIC_COMPOUND; + mCompoundPrunerExt.mPruner->addCompound(res, bvhStructure, compoundId, compoundTransform, flags, payloads); + const PxU32 index = (flags & CompoundFlag::STATIC_COMPOUND) ? PxU32(0) : PxU32(1); + mPrunerExt[index].invalidateTimestamp(); + + for(PxU32 i = 0; i < nbShapes; i++) + { + prunerData[i] = createPrunerData(index, res[i]); + } +} + +void SceneQueryManager::updateCompoundActors(Sc::BodyCore*const* bodies, PxU32 numBodies) +{ + PX_ASSERT(mCompoundPrunerExt.mPruner); + for(PxU32 i = 0; i < numBodies; i++) + { + mCompoundPrunerExt.mPruner->updateCompound(bodies[i]->getRigidID(), bodies[i]->getBody2World()); + } + mPrunerExt[1].invalidateTimestamp(); +} + +void SceneQueryManager::updateCompoundActor(PrunerCompoundId compoundId, const PxTransform& compoundTransform, bool dynamic) +{ + mCompoundPrunerExt.mPruner->updateCompound(compoundId, compoundTransform); + mPrunerExt[dynamic].invalidateTimestamp(); +} + +void SceneQueryManager::removeCompoundActor(PrunerCompoundId compoundId, bool dynamic) +{ + PX_ASSERT(mCompoundPrunerExt.mPruner); + mCompoundPrunerExt.mPruner->removeCompound(compoundId); + mPrunerExt[dynamic].invalidateTimestamp(); +} + + diff --git a/src/PhysX/physx/source/scenequery/src/SqTypedef.h b/src/PhysX/physx/source/scenequery/src/SqTypedef.h new file mode 100644 index 000000000..0bcb5e0f1 --- /dev/null +++ b/src/PhysX/physx/source/scenequery/src/SqTypedef.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef SQ_TYPEDEF_H +#define SQ_TYPEDEF_H + +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Sq +{ + typedef PxU32 PoolIndex; + typedef PxU32 TreeNodeIndex; + + class AABBTree; +} +} + +#endif // SQ_TYPEDEF_H diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h new file mode 100644 index 000000000..3dc4f4101 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h @@ -0,0 +1,131 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_ACTOR_CORE +#define PX_COLLISION_ACTOR_CORE + +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PxMetaData.h" +#include "PxActor.h" + +namespace physx +{ + +class PxActor; + +namespace Sc +{ + + class Scene; + class ActorSim; + + class ActorCore : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + ActorCore(const PxEMPTY) : mSim(NULL), mActorFlags(PxEmpty) + { + } + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + ActorCore(PxActorType::Enum actorType, PxU8 actorFlags, + PxClientID owner, PxDominanceGroup dominanceGroup); + /*virtual*/ ~ActorCore(); + + PX_FORCE_INLINE ActorSim* getSim() const { return mSim; } + PX_FORCE_INLINE void setSim(ActorSim* sim) + { + PX_ASSERT((sim==NULL) ^ (mSim==NULL)); + mSim = sim; + } + + PX_FORCE_INLINE PxActorFlags getActorFlags() const { return mActorFlags; } + void setActorFlags(PxActorFlags af); + + PX_FORCE_INLINE PxDominanceGroup getDominanceGroup() const + { + return PxDominanceGroup(mDominanceGroup); + } + void setDominanceGroup(PxDominanceGroup g); + + PX_FORCE_INLINE void setOwnerClient(PxClientID inId) + { + const PxU32 aggid = mAggregateIDOwnerClient & 0x00ffffff; + mAggregateIDOwnerClient = (PxU32(inId)<<24) | aggid; + } + PX_FORCE_INLINE PxClientID getOwnerClient() const + { + return mAggregateIDOwnerClient>>24; + } + + PX_FORCE_INLINE PxActorType::Enum getActorCoreType() const { return PxActorType::Enum(mActorType); } + + void reinsertShapes(); + + PX_FORCE_INLINE void setAggregateID(PxU32 id) + { + PX_ASSERT(id==0xffffffff || id<(1<<24)); + const PxU32 ownerClient = mAggregateIDOwnerClient & 0xff000000; + mAggregateIDOwnerClient = (id & 0x00ffffff) | ownerClient; + } + PX_FORCE_INLINE PxU32 getAggregateID() const + { + const PxU32 id = mAggregateIDOwnerClient & 0x00ffffff; + return id == 0x00ffffff ? PX_INVALID_U32 : id; + } + private: + ActorSim* mSim; // + PxU32 mAggregateIDOwnerClient; // PxClientID (8bit) | aggregate ID (24bit) + // PT: TODO: the remaining members could be packed into just a 16bit mask + PxActorFlags mActorFlags; // PxActor's flags (PxU8) => only 4 bits used + PxU8 mActorType; // Actor type (8 bits, but 3 would be enough) + PxU8 mDominanceGroup; // Dominance group (8 bits, but 5 would be enough because "must be < 32") + }; + +#if PX_P64_FAMILY + PX_COMPILE_TIME_ASSERT(sizeof(Sc::ActorCore)==16); +#else + PX_COMPILE_TIME_ASSERT(sizeof(Sc::ActorCore)==12); +#endif + +} // namespace Sc + +} + +////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h new file mode 100644 index 000000000..5f02cfeb7 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h @@ -0,0 +1,227 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_ARTICULATION_CORE +#define PX_PHYSICS_SCP_ARTICULATION_CORE + +#include "ScActorCore.h" +#include "DyArticulation.h" +#include "DyFeatherstoneArticulation.h" + +namespace physx +{ + +class PxvArticulation; + +namespace IG +{ + class NodeIndex; +} + + +namespace Sc +{ + typedef Dy::FsData ArticulationDriveCache; + + class ArticulationSim; + class BodyCore; + class BodySim; + + class ArticulationCore + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + //--------------------------------------------------------------------------------- + // Construction, destruction & initialization + //--------------------------------------------------------------------------------- + +// PX_SERIALIZATION + public: + ArticulationCore(const PxEMPTY) : mSim(NULL) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + ArticulationCore(); + ~ArticulationCore(); + + //--------------------------------------------------------------------------------- + // External API + //--------------------------------------------------------------------------------- + PxU32 getInternalDriveIterations() const; + void setInternalDriveIterations(const PxU32 v); + + PxU32 getExternalDriveIterations() const; + void setExternalDriveIterations(const PxU32 v); + + PxU32 getMaxProjectionIterations() const; + void setMaxProjectionIterations(const PxU32 v); + + PxReal getSeparationTolerance() const; + void setSeparationTolerance(const PxReal v); + + PxReal getSleepThreshold() const; + void setSleepThreshold(const PxReal v); + + PxReal getFreezeThreshold() const; + void setFreezeThreshold(const PxReal v); + + PxReal getWakeCounter() const; + void setWakeCounter(const PxReal v); + void setWakeCounterInternal(const PxReal v); + + bool isSleeping() const; + void wakeUp(PxReal wakeCounter); + void putToSleep(); + + PxU16 getSolverIterationCounts() const; + void setSolverIterationCounts(PxU16 c); + + PxArticulation* getPxArticulation(); + const PxArticulation* getPxArticulation() const; + + + //--------------------------------------------------------------------------------- + // Drive Cache API + //--------------------------------------------------------------------------------- + ArticulationDriveCache* createDriveCache(PxReal compliance, + PxU32 driveIterations) const; + + void updateDriveCache(ArticulationDriveCache& cache, + PxReal compliance, + PxU32 driveIterations) const; + + void releaseDriveCache(ArticulationDriveCache& cache) const; + + PxU32 getCacheLinkCount(const ArticulationDriveCache& cache) const; + + void applyImpulse(BodyCore& link, + const ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque); + + void computeImpulseResponse(BodyCore& link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const; + + //--------------------------------------------------------------------------------- + // external reduced coordinate API + //--------------------------------------------------------------------------------- + void setArticulationFlags(PxArticulationFlags flags) { mCore.flags = flags; } + PxArticulationFlags getArticulationFlags() const { return mCore.flags; } + + PxU32 getDofs() const; + + PxArticulationCache* createCache() const; + + PxU32 getCacheDataSize() const; + + void zeroCache(PxArticulationCache& cache) const; + + void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag)const; + + void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const; + + void releaseCache(PxArticulationCache& cache) const; + + void packJointData(const PxReal* maximum, PxReal* reduced) const; + + void unpackJointData(const PxReal* reduced, PxReal* maximum) const; + + void commonInit() const; + + void computeGeneralizedGravityForce(PxArticulationCache& cache) const; + + void computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const; + + void computeGeneralizedExternalForce(PxArticulationCache& cache) const; + + void computeJointAcceleration(PxArticulationCache& cache) const; + + void computeJointForce(PxArticulationCache& cache) const; + + void computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const; + + void computeCoefficentMatrix(PxArticulationCache& cache) const; + + bool computeLambda(PxArticulationCache& cache, PxArticulationCache& rollBackCache, const PxReal* const jointTorque, const PxVec3 gravity, const PxU32 maxIter) const; + + void computeGeneralizedMassMatrix(PxArticulationCache& cache) const; + + PxU32 getCoefficentMatrixSize() const; + //--------------------------------------------------------------------------------- + // Internal API + //--------------------------------------------------------------------------------- + public: + PX_FORCE_INLINE void setSim(ArticulationSim* sim) + { + PX_ASSERT((sim==0) ^ (mSim == 0)); + mSim = sim; + } + PX_FORCE_INLINE ArticulationSim* getSim() const { return mSim; } + + PX_FORCE_INLINE const Dy::ArticulationCore& getCore() { return mCore; } + + static PX_FORCE_INLINE ArticulationCore& getArticulationCore(ArticulationCore& core) + { + size_t offset = PX_OFFSET_OF(ArticulationCore, mCore); + return *reinterpret_cast(reinterpret_cast(&core) - offset); + } + + PX_INLINE PxArticulationBase::Enum getArticulationType() const { return PxArticulationBase::Enum(mType); } + PX_INLINE void setArticulationType(PxArticulationBase::Enum type) { mType = type; } + + + IG::NodeIndex getIslandNodeIndex() const; + + void setGlobalPose(); + + void setDirty(const bool dirty); + + + private: + ArticulationSim* mSim; + Dy::ArticulationCore mCore; + PxU32 mType; + }; + + + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h new file mode 100644 index 000000000..fbb96b8d0 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h @@ -0,0 +1,226 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_ARTICULATION_JOINT_CORE +#define PX_PHYSICS_SCP_ARTICULATION_JOINT_CORE + +#include "foundation/PxTransform.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" +#include "DyArticulation.h" +#include "PxMetaData.h" + +namespace physx +{ +namespace Sc +{ + + class BodyCore; + class ArticulationJointSim; + class ArticulationCore; + + class ArticulationJointDesc + { + public: + BodyCore* parent; + BodyCore* child; + PxTransform parentPose; + PxTransform childPose; + }; + + class ArticulationJointCore : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + //--------------------------------------------------------------------------------- + // Construction, destruction & initialization + //--------------------------------------------------------------------------------- + public: +// PX_SERIALIZATION + ArticulationJointCore(const PxEMPTY) : mSim(NULL), mCore(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + ArticulationJointCore( const PxTransform& parentFrame, + const PxTransform& childFrame, + PxArticulationBase::Enum type); + + ~ArticulationJointCore(); + + //--------------------------------------------------------------------------------- + // External API + //--------------------------------------------------------------------------------- + + const PxTransform& getParentPose() const { return mCore.parentPose; } + void setParentPose(const PxTransform&); + + const PxTransform& getChildPose() const { return mCore.childPose; } + void setChildPose(const PxTransform&); + + const PxQuat& getTargetOrientation() const { return mCore.targetPosition; } + void setTargetOrientation(const PxQuat&); + + + const PxVec3& getTargetVelocity() const { return mCore.targetVelocity; } + void setTargetVelocity(const PxVec3&); + + PxReal getStiffness() const { return mCore.spring; } + void setStiffness(PxReal); + + PxReal getDamping() const { return mCore.damping; } + void setDamping(PxReal); + + PxReal getInternalCompliance() const { return mCore.internalCompliance; } + void setInternalCompliance(PxReal); + + PxReal getExternalCompliance() const { return mCore.externalCompliance; } + void setExternalCompliance(PxReal); + + void getSwingLimit(PxReal& yLimit, PxReal& zLimit) const { yLimit = mCore.limits[PxArticulationAxis::eSWING1].low; zLimit = mCore.limits[PxArticulationAxis::eSWING2].low; } + void setSwingLimit(PxReal yLimit, PxReal zLimit); + + PxReal getTangentialStiffness() const { return mCore.tangentialStiffness; } + void setTangentialStiffness(PxReal); + + PxReal getTangentialDamping() const { return mCore.tangentialDamping; } + void setTangentialDamping(PxReal); + + bool getSwingLimitEnabled() const { return mCore.swingLimited; } + void setSwingLimitEnabled(bool); + + PxReal getSwingLimitContactDistance() const { return mCore.swingLimitContactDistance; } + void setSwingLimitContactDistance(PxReal); + + void getTwistLimit(PxReal& lower, PxReal& upper) const { lower = mCore.limits[PxArticulationAxis::eTWIST].low; upper = mCore.limits[PxArticulationAxis::eTWIST].high; } + void setTwistLimit(PxReal lower, PxReal upper); + + void getLimit(PxArticulationAxis::Enum axis, PxReal& lower, PxReal& upper) const + { + lower = mCore.limits[axis].low; + upper = mCore.limits[axis].high; + } + + void setLimit(PxArticulationAxis::Enum axis, PxReal lower, PxReal upper) + { + mCore.limits[axis].low = lower; + mCore.limits[axis].high = upper; + } + + void getDrive(PxArticulationAxis::Enum axis, PxReal& stiffness, PxReal& damping, PxReal& maxForce, bool& isAcceleration) const + { + stiffness = mCore.drives[axis].stiffness; + damping = mCore.drives[axis].damping; + maxForce = mCore.drives[axis].maxForce; + isAcceleration = mCore.drives[axis].isAcceleration; + } + + void setDrive(PxArticulationAxis::Enum axis, PxReal stiffness, PxReal damping, PxReal maxForce, bool isAcceleration) + { + mCore.drives[axis].stiffness = stiffness; + mCore.drives[axis].damping = damping; + mCore.drives[axis].maxForce = maxForce; + mCore.drives[axis].isAcceleration = isAcceleration; + } + + void setTargetP(PxArticulationAxis::Enum axis, PxReal targetP); + + PxReal getTargetP(PxArticulationAxis::Enum axis) const + { + return mCore.targetP[axis]; + } + + void setTargetV(PxArticulationAxis::Enum axis, PxReal targetV); + + PxReal getTargetV(PxArticulationAxis::Enum axis) const + { + return mCore.targetV[axis]; + } + + bool getTwistLimitEnabled() const { return mCore.twistLimited; } + void setTwistLimitEnabled(bool); + + PxReal getTwistLimitContactDistance() const { return mCore.twistLimitContactDistance; } + void setTwistLimitContactDistance(PxReal); + + void setDriveType(PxArticulationJointDriveType::Enum type); + PxArticulationJointDriveType::Enum + getDriveType() const { return PxArticulationJointDriveType::Enum(mCore.driveType); } + + void setJointType(PxArticulationJointType::Enum type); + PxArticulationJointType::Enum getJointType() const; + + + void setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion); + PxArticulationMotion::Enum + getMotion(PxArticulationAxis::Enum axis) const; + + void setFrictionCoefficient(const PxReal coefficient); + PxReal getFrictionCoefficient() const; + + void setMaxJointVelocity(const PxReal maxJointV); + PxReal getMaxJointVelocity() const; + //--------------------------------------------------------------------------------- + // Low Level data access - some wouldn't be needed if the interface wasn't virtual + //--------------------------------------------------------------------------------- + + PX_FORCE_INLINE ArticulationJointSim* getSim() const { return mSim; } + PX_FORCE_INLINE void setSim(ArticulationJointSim* sim) + { + PX_ASSERT((sim==0) ^ (mSim == 0)); + mSim = sim; + } + + PX_FORCE_INLINE Dy::ArticulationJointCore& getCore() { return mCore; } + + PX_FORCE_INLINE void setArticulation(ArticulationCore* articulation) + { + mArticulation = articulation; + } + + PX_FORCE_INLINE void setRoot(PxArticulationJointBase* base) { mRootType = base; } + PX_FORCE_INLINE PxArticulationJointBase* getRoot() const { return mRootType; } + + private: + ArticulationJointSim* mSim; + Dy::ArticulationJointCore mCore; + ArticulationCore* mArticulation; + PxArticulationJointBase* mRootType; + + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h new file mode 100644 index 000000000..f8592b67f --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h @@ -0,0 +1,213 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_BODYCORE +#define PX_PHYSICS_SCP_BODYCORE + +#include "foundation/PxTransform.h" +#include "ScRigidCore.h" +#include "PxRigidDynamic.h" +#include "PxvDynamics.h" +#include "PxvConfig.h" +#include "PsPool.h" + +namespace physx +{ +class PxRigidBodyDesc; + +namespace Sc +{ + class BodySim; + struct SimStateData; + + struct KinematicTransform + { + PxTransform targetPose; // The body will move to this pose over the superstep following this getting set. + PxU8 targetValid; // User set a kinematic target. + PxU8 pad[2]; + PxU8 type; + }; + + class BodyCore : public RigidCore + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + + //--------------------------------------------------------------------------------- + // Construction, destruction & initialization + //--------------------------------------------------------------------------------- + public: +// PX_SERIALIZATION + BodyCore(const PxEMPTY) : RigidCore(PxEmpty), mCore(PxEmpty), mSimStateData(NULL) {} + static void getBinaryMetaData(PxOutputStream& stream); + void disableInternalCaching(bool disable); + size_t getSerialCore(PxsBodyCore& serialCore); +//~PX_SERIALIZATION + BodyCore(PxActorType::Enum type, const PxTransform& bodyPose); + /*virtual*/ ~BodyCore(); + + //--------------------------------------------------------------------------------- + // External API + //--------------------------------------------------------------------------------- + PX_FORCE_INLINE const PxTransform& getBody2World() const { return mCore.body2World; } + void setBody2World(const PxTransform& p); + + PX_FORCE_INLINE const PxVec3& getLinearVelocity() const { return mCore.linearVelocity; } + void setLinearVelocity(const PxVec3& v); + + PX_FORCE_INLINE const PxVec3& getAngularVelocity() const { return mCore.angularVelocity; } + void setAngularVelocity(const PxVec3& v); + + PX_FORCE_INLINE void updateVelocities(const PxVec3& linearVelModPerStep, const PxVec3& angularVelModPerStep) + { + mCore.linearVelocity += linearVelModPerStep; + mCore.angularVelocity += angularVelModPerStep; + } + + PX_FORCE_INLINE const PxTransform& getBody2Actor() const { return mCore.getBody2Actor(); } + void setBody2Actor(const PxTransform& p); + + void addSpatialAcceleration(Ps::Pool* simStateDataPool, const PxVec3* linAcc, const PxVec3* angAcc); + void setSpatialAcceleration(Ps::Pool* simStateDataPool, const PxVec3* linAcc, const PxVec3* angAcc); + void clearSpatialAcceleration(bool force, bool torque); + void addSpatialVelocity(Ps::Pool* simStateDataPool, const PxVec3* linVelDelta, const PxVec3* angVelDelta); + void clearSpatialVelocity(bool force, bool torque); + + PX_FORCE_INLINE PxReal getMaxPenetrationBias() const { return mCore.maxPenBias; } + PX_FORCE_INLINE void setMaxPenetrationBias(PxReal p) { mCore.maxPenBias = p; } + + PxReal getInverseMass() const; + void setInverseMass(PxReal m); + const PxVec3& getInverseInertia() const; + void setInverseInertia(const PxVec3& i); + + PxReal getLinearDamping() const; + void setLinearDamping(PxReal d); + + PxReal getAngularDamping() const; + void setAngularDamping(PxReal d); + + PX_FORCE_INLINE PxRigidBodyFlags getFlags() const { return mCore.mFlags; } + void setFlags(Ps::Pool* simStateDataPool, PxRigidBodyFlags f); + + PX_FORCE_INLINE PxRigidDynamicLockFlags getRigidDynamicLockFlags() const { return mCore.lockFlags; } + + PX_FORCE_INLINE void setRigidDynamicLockFlags(PxRigidDynamicLockFlags flags) { mCore.lockFlags = flags; } + + PX_FORCE_INLINE PxReal getSleepThreshold() const { return mCore.sleepThreshold; } + void setSleepThreshold(PxReal t); + + PX_FORCE_INLINE PxReal getFreezeThreshold() const { return mCore.freezeThreshold; } + void setFreezeThreshold(PxReal t); + + PX_FORCE_INLINE PxReal getMaxContactImpulse() const { return mCore.maxContactImpulse; } + void setMaxContactImpulse(PxReal m); + + PxU32 getInternalIslandNodeIndex() const; + + PX_FORCE_INLINE PxReal getWakeCounter() const { return mCore.wakeCounter; } + void setWakeCounter(PxReal wakeCounter, bool forceWakeUp=false); + + bool isSleeping() const; + PX_FORCE_INLINE void wakeUp(PxReal wakeCounter) { setWakeCounter(wakeCounter, true); } + void putToSleep(); + + PxReal getMaxAngVelSq() const; + void setMaxAngVelSq(PxReal v); + + PxReal getMaxLinVelSq() const; + void setMaxLinVelSq(PxReal v); + + PX_FORCE_INLINE PxU16 getSolverIterationCounts() const { return mCore.solverIterationCounts; } + void setSolverIterationCounts(PxU16 c); + + bool getKinematicTarget(PxTransform& p) const; + bool getHasValidKinematicTarget() const; + void setKinematicTarget(Ps::Pool* simStateDataPool, const PxTransform& p, PxReal wakeCounter); + void invalidateKinematicTarget(); + + + PX_FORCE_INLINE PxReal getContactReportThreshold() const { return mCore.contactReportThreshold; } + void setContactReportThreshold(PxReal t) { mCore.contactReportThreshold = t; } + + void onOriginShift(const PxVec3& shift); + + //--------------------------------------------------------------------------------- + // Internal API + //--------------------------------------------------------------------------------- + + PX_FORCE_INLINE void setLinearVelocityInternal(const PxVec3& v) { mCore.linearVelocity = v; } + PX_FORCE_INLINE void setAngularVelocityInternal(const PxVec3& v) { mCore.angularVelocity = v; } + PX_FORCE_INLINE void setWakeCounterFromSim(PxReal c) { mCore.wakeCounter = c; } + + BodySim* getSim() const; + + PX_FORCE_INLINE PxsBodyCore& getCore() { return mCore; } + PX_FORCE_INLINE const PxsBodyCore& getCore() const { return mCore; } + + PX_FORCE_INLINE PxReal getCCDAdvanceCoefficient() const { return mCore.ccdAdvanceCoefficient; } + PX_FORCE_INLINE void setCCDAdvanceCoefficient(PxReal c) { mCore.ccdAdvanceCoefficient = c; } + + bool setupSimStateData(Ps::Pool* simStateDataPool, const bool isKinematic, const bool targetValid = false); + void tearDownSimStateData(Ps::Pool* simStateDataPool, const bool isKinematic); + + bool checkSimStateKinematicStatus(bool) const; + + Ps::IntBool isFrozen() const; + + PX_FORCE_INLINE const SimStateData* getSimStateData(bool isKinematic) const { return (mSimStateData && (checkSimStateKinematicStatus(isKinematic)) ? mSimStateData : NULL); } + PX_FORCE_INLINE SimStateData* getSimStateData(bool isKinematic) { return (mSimStateData && (checkSimStateKinematicStatus(isKinematic)) ? mSimStateData : NULL); } + + PX_FORCE_INLINE SimStateData* getSimStateData_Unchecked() const { return mSimStateData; } + + static PX_FORCE_INLINE BodyCore& getCore(PxsBodyCore& core) + { + size_t offset = PX_OFFSET_OF_RT(BodyCore, mCore); + return *reinterpret_cast(reinterpret_cast(&core) - offset); + } + + private: + void backup(SimStateData&); + void restore(); + + PX_ALIGN_PREFIX(16) PxsBodyCore mCore PX_ALIGN_SUFFIX(16); + SimStateData* mSimStateData; + }; + + PxActor* getPxActorFromBodyCore(Sc::BodyCore* bodyCore, PxActorType::Enum& type); + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h new file mode 100644 index 000000000..5520b6497 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h @@ -0,0 +1,134 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CONSTRAINTCORE +#define PX_PHYSICS_CONSTRAINTCORE + +#include "CmPhysXCommon.h" +#include "PxConstraintDesc.h" +#include "PsAllocator.h" +#include "PxConstraint.h" + +namespace physx +{ + +class PxConstraint; + +namespace Sc +{ + class ConstraintCore; + class ConstraintSim; + class RigidCore; + + + class ConstraintCore : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + ConstraintCore(const PxEMPTY) : mFlags(PxEmpty), mConnector(NULL), mSim(NULL) {} + PX_FORCE_INLINE void setConstraintFunctions(PxConstraintConnector& n, + const PxConstraintShaderTable& shaders) + { + mConnector = &n; + mSolverPrep = shaders.solverPrep; + mProject = shaders.project; + mVisualize = shaders.visualize; + } + static void getBinaryMetaData(PxOutputStream& stream); +//~PX_SERIALIZATION + ConstraintCore(PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize); + ~ConstraintCore(); + + // The two-step protocol here allows us to unlink the constraint prior to deleting + // the actors when synchronizing the scene, then set the bodies after new actors have been inserted + + void prepareForSetBodies(); + void setBodies(RigidCore* r0v, RigidCore* r1v); + + PxConstraint* getPxConstraint(); + const PxConstraint* getPxConstraint() const; + PX_FORCE_INLINE PxConstraintConnector* getPxConnector() const { return mConnector; } + + PX_FORCE_INLINE PxConstraintFlags getFlags() const { return mFlags; } + void setFlags(PxConstraintFlags flags); + + void getForce(PxVec3& force, PxVec3& torque) const; + + bool updateConstants(void* addr); + + void setBreakForce(PxReal linear, PxReal angular); + void getBreakForce(PxReal& linear, PxReal& angular) const; + + void setMinResponseThreshold(PxReal threshold); + PxReal getMinResponseThreshold() const { return mMinResponseThreshold; } + + void breakApart(); + + PX_FORCE_INLINE PxConstraintVisualize getVisualize() const { return mVisualize; } + PX_FORCE_INLINE PxConstraintProject getProject() const { return mProject; } + PX_FORCE_INLINE PxConstraintSolverPrep getSolverPrep() const { return mSolverPrep; } + PX_FORCE_INLINE PxU32 getConstantBlockSize() const { return mDataSize; } + + PX_FORCE_INLINE void setSim(ConstraintSim* sim) + { + PX_ASSERT((sim==0) ^ (mSim == 0)); + mSim = sim; + } + PX_FORCE_INLINE ConstraintSim* getSim() const { return mSim; } + private: + PxConstraintFlags mFlags; + PxU16 mPaddingFromFlags; // PT: because flags are PxU16 + + PxVec3 mAppliedForce; + PxVec3 mAppliedTorque; + + PxConstraintConnector* mConnector; + PxConstraintProject mProject; + PxConstraintSolverPrep mSolverPrep; + PxConstraintVisualize mVisualize; + PxU32 mDataSize; + PxReal mLinearBreakForce; + PxReal mAngularBreakForce; + PxReal mMinResponseThreshold; + + ConstraintSim* mSim; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScIterators.h b/src/PhysX/physx/source/simulationcontroller/include/ScIterators.h new file mode 100644 index 000000000..30905476f --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScIterators.h @@ -0,0 +1,104 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_ITERATOR +#define PX_PHYSICS_SCP_ITERATOR + +#include "foundation/PxVec3.h" +#include "PxContact.h" + +namespace physx +{ +class PxShape; +class PxsContactManagerOutputIterator; + +namespace Sc +{ + class ShapeSim; + class Interaction; + + struct Contact + { + Contact() + : normal(0.0f) + , point(0.0f) + , separation(0.0f) + , normalForce(0.0f) + {} + + PxVec3 normal; + PxVec3 point; + PxShape* shape0; + PxShape* shape1; + PxReal separation; + PxReal normalForce; + PxU32 faceIndex0; // these are the external indices + PxU32 faceIndex1; + bool normalForceAvailable; + }; + + class ContactIterator + { + public: + + class Pair + { + public: + Pair() : mIter(NULL, NULL, NULL, 0, 0) {} + Pair(const void*& contactPatches, const void*& contactPoints, const PxU32 /*contactDataSize*/, const PxReal*& forces, PxU32 numContacts, PxU32 numPatches, ShapeSim& shape0, ShapeSim& shape1); + Contact* getNextContact(); + + private: + PxU32 mIndex; + PxU32 mNumContacts; + PxContactStreamIterator mIter; + const PxReal* mForces; + Contact mCurrentContact; + }; + + ContactIterator() {} + explicit ContactIterator(Interaction** first, Interaction** last, PxsContactManagerOutputIterator& outputs): mCurrent(first), mLast(last), mOffset(0), mOutputs(&outputs) {} + Pair* getNextPair(); + + private: + Interaction** mCurrent; + Interaction** mLast; + Pair mCurrentPair; + PxU32 mOffset; + PxsContactManagerOutputIterator* mOutputs; + + private: + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h new file mode 100644 index 000000000..16d033448 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h @@ -0,0 +1,70 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_MATERIAL_CORE +#define PX_PHYSICS_SCP_MATERIAL_CORE + +#include "foundation/PxVec3.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PxMaterial.h" +#include "PxsMaterialCore.h" + +namespace physx +{ +class PxMaterial; + +namespace Sc +{ +typedef PxsMaterialData MaterialData; + +class MaterialCore : public PxsMaterialCore +{ +//= ATTENTION! ===================================================================================== +// Changing the data layout of this class breaks the binary serialization format. See comments for +// PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData +// function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION +// accordingly. +//================================================================================================== +public: + MaterialCore(const MaterialData& desc) : PxsMaterialCore(desc) {} + MaterialCore(const PxEMPTY) : PxsMaterialCore(PxEmpty) {} + MaterialCore() {} + ~MaterialCore() {} + static void getBinaryMetaData(PxOutputStream& stream); + + PX_FORCE_INLINE void save(MaterialData& data) const { data = *this; } + PX_FORCE_INLINE void load(const MaterialData& data) { static_cast(*this) = data; } // To make synchronization between master material and scene material table less painful +}; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h b/src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h new file mode 100644 index 000000000..465f107fd --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h @@ -0,0 +1,115 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SC_PHYSICS +#define PX_PHYSICS_SC_PHYSICS + +#include "PxPhysics.h" +#include "PxScene.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PsBasicTemplates.h" +#include "PxActor.h" + +namespace physx +{ + +class PxMaterial; +class PxTolerancesScale; +struct PxvOffsetTable; + +#if PX_SUPPORT_GPU_PHYSX +class PxPhysXGpu; +#endif + +namespace Sc +{ + class Scene; + class StaticCore; + class RigidCore; + class BodyCore; + class ArticulationCore; + class ConstraintCore; + class ShapeCore; + + struct OffsetTable + { + PX_FORCE_INLINE OffsetTable() {} + + PX_FORCE_INLINE PxActor* convertScRigidStatic2PxActor(StaticCore* sc) const { return Ps::pointerOffset(sc, scRigidStatic2PxActor); } + PX_FORCE_INLINE PxActor* convertScRigidDynamic2PxActor(BodyCore* sc) const { return Ps::pointerOffset(sc, scRigidDynamic2PxActor); } + PX_FORCE_INLINE PxActor* convertScArticulationLink2PxActor(BodyCore* sc) const { return Ps::pointerOffset(sc, scArticulationLink2PxActor); } + + PX_FORCE_INLINE PxShape* convertScShape2Px(ShapeCore* sc) const { return Ps::pointerOffset(sc, scShape2Px); } + PX_FORCE_INLINE const PxShape* convertScShape2Px(const ShapeCore* sc) const { return Ps::pointerOffset(sc, scShape2Px); } + + PX_FORCE_INLINE PxArticulation* convertScArticulation2Px(ArticulationCore* sc) const { return Ps::pointerOffset(sc, scArticulation2Px); } + PX_FORCE_INLINE const PxArticulation* convertScArticulation2Px(const ArticulationCore* sc) const { return Ps::pointerOffset(sc, scArticulation2Px); } + + PX_FORCE_INLINE PxConstraint* convertScConstraint2Px(ConstraintCore* sc) const { return Ps::pointerOffset(sc, scConstraint2Px); } + PX_FORCE_INLINE const PxConstraint* convertScConstraint2Px(const ConstraintCore* sc) const { return Ps::pointerOffset(sc, scConstraint2Px); } + + ptrdiff_t scRigidStatic2PxActor; + ptrdiff_t scRigidDynamic2PxActor; + ptrdiff_t scArticulationLink2PxActor; + ptrdiff_t scShape2Px; + ptrdiff_t scArticulation2Px; + ptrdiff_t scSoftBody2Px; + ptrdiff_t scConstraint2Px; + + ptrdiff_t scCore2PxActor[PxActorType::eACTOR_COUNT]; + }; + extern OffsetTable gOffsetTable; + + class Physics : public Ps::UserAllocated + { + public: + PX_FORCE_INLINE static Physics& getInstance() { return *mInstance; } + + Physics(const PxTolerancesScale&, const PxvOffsetTable& pxvOffsetTable); + ~Physics(); // use release() instead + public: + void release(); + + PX_FORCE_INLINE const PxTolerancesScale& getTolerancesScale() const { return mScale; } + + private: + PxTolerancesScale mScale; + static Physics* mInstance; + + public: + static const PxReal sWakeCounterOnCreation; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h new file mode 100644 index 000000000..732b9f97b --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h @@ -0,0 +1,94 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_RB_CORE +#define PX_PHYSICS_SCP_RB_CORE + +#include "ScActorCore.h" +#include "PxvDynamics.h" +#include "PxShape.h" + +namespace physx +{ + +namespace Sc +{ + + class RigidSim; + + struct ShapeChangeNotifyFlag + { + enum Enum + { + eGEOMETRY = 1<<0, + eMATERIAL = 1<<1, + eSHAPE2BODY = 1<<2, + eFILTERDATA = 1<<3, + eCONTACTOFFSET = 1<<4, + eRESTOFFSET = 1<<5, + eFLAGS = 1<<6, + eRESET_FILTERING = 1<<7 + + }; + }; + typedef PxFlags ShapeChangeNotifyFlags; + PX_FLAGS_OPERATORS(ShapeChangeNotifyFlag::Enum,PxU32) + + + class ShapeCore; + + class RigidCore : public ActorCore + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + PxActor* getPxActor() const; + void addShapeToScene(ShapeCore& shape); + void removeShapeFromScene(ShapeCore& shape, bool wakeOnLostTouch); + void onShapeChange(ShapeCore& shape, ShapeChangeNotifyFlags notifyFlags, PxShapeFlags newShapeFlags = PxShapeFlags(), bool forceBoundsUpdate = false); + + RigidSim* getSim() const; + PxU32 getRigidID() const; + static void getBinaryMetaData(PxOutputStream& stream); + protected: + RigidCore(const PxEMPTY) : ActorCore(PxEmpty) {} + RigidCore(PxActorType::Enum type); + ~RigidCore(); + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScScene.h b/src/PhysX/physx/source/simulationcontroller/include/ScScene.h new file mode 100644 index 000000000..912b2c897 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScScene.h @@ -0,0 +1,993 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_SCENE +#define PX_PHYSICS_SCP_SCENE + +#include "CmPhysXCommon.h" +#include "PxPhysXConfig.h" +#include "PxScene.h" +#include "PxSceneDesc.h" +#include "PxSimulationEventCallback.h" +#include "PsPool.h" +#include "PsHashSet.h" +#include "CmRenderOutput.h" +#include "CmTask.h" +#include "CmFlushPool.h" +#include "CmPreallocatingPool.h" +#include "CmBitMap.h" +#include "ScIterators.h" +#include "PxsMaterialManager.h" +#include "PxvManager.h" +#include "ScBodyCore.h" +#include "PxArticulationBase.h" + +#define PX_MAX_DOMINANCE_GROUP 32 + +class OverlapFilterTask; + +namespace physx +{ + +// PT: TODO: move INVALID_FILTER_PAIR_INDEX out of public API +struct PxFilterInfo +{ + PX_FORCE_INLINE PxFilterInfo() : filterFlags(0), pairFlags(0), filterPairIndex(INVALID_FILTER_PAIR_INDEX) {} + PX_FORCE_INLINE PxFilterInfo(PxFilterFlags filterFlags_) : filterFlags(filterFlags_), pairFlags(0), filterPairIndex(INVALID_FILTER_PAIR_INDEX) {} + + PxFilterFlags filterFlags; + PxPairFlags pairFlags; + PxU32 filterPairIndex; +}; + +struct PxTriggerPair; + +class PxsIslandManager; +class PxsSimulationController; +class PxsSimulationControllerCallback; +class PxsMemoryManager; + +#if PX_SUPPORT_GPU_PHYSX +class PxsKernelWranglerManager; +class PxsHeapMemoryAllocatorManager; +#endif + +namespace IG +{ + class SimpleIslandManager; +} + +class PxsCCDContext; + +namespace Cm +{ + class DeferredIDPool; + class IDPool; +} + +namespace Bp +{ + class AABBManager; + class BroadPhase; + class BoundsArray; +} + +namespace Sq +{ + typedef PxU32 PrunerHandle; // PT: we should get this from SqPruner.h but it cannot be included from here +} + +namespace Dy +{ + class ArticulationV; + class Context; +} + +namespace Pt +{ + class Context; +} + +namespace Sc +{ + class ActorSim; + class ElementSim; + class Interaction; + + class ShapeCore; + class RigidCore; + class StaticCore; + class ConstraintCore; + class MaterialCore; + class ArticulationCore; + class ArticulationJointCore; + class LLArticulationPool; + class LLArticulationRCPool; + class BodyCore; + + class NPhaseCore; + class LowLevelThreadingThunk; + class Client; + class ConstraintInteraction; + + class BodySim; + class ShapeSim; + class RigidSim; + class StaticSim; + class ConstraintSim; + struct ConstraintGroupNode; + class ConstraintProjectionManager; + struct TriggerPairExtraData; + class ObjectIDTracker; + class ActorPairReport; + class ContactStreamManager; + class SqBoundsManager; + class ShapeInteraction; + class ElementInteractionMarker; + class ArticulationSim; + + class SimStats; + + struct SimStateData; + + struct BatchInsertionState + { + BodySim* bodySim; + StaticSim*staticSim; + ShapeSim* shapeSim; + ptrdiff_t staticActorOffset; + ptrdiff_t staticShapeTableOffset; + ptrdiff_t dynamicActorOffset; + ptrdiff_t dynamicShapeTableOffset; + ptrdiff_t shapeOffset; + }; + + struct BatchRemoveState + { + Ps::InlineArray bufferedShapes; + Ps::InlineArray removedShapes; + }; + + struct InteractionType + { + enum Enum + { + eOVERLAP = 0, // corresponds to ShapeInteraction + eTRIGGER, // corresponds to TriggerInteraction + eMARKER, // corresponds to ElementInteractionMarker + eTRACKED_IN_SCENE_COUNT, // not a real type, interactions above this limit are tracked in the scene + eCONSTRAINTSHADER, // corresponds to ConstraintInteraction + eARTICULATION, // corresponds to ArticulationJointSim + + eINVALID + }; + }; + + struct SceneInternalFlag + { + enum Enum + { + eSCENE_SIP_STATES_DIRTY_DOMINANCE = (1<<1), + eSCENE_SIP_STATES_DIRTY_VISUALIZATION = (1<<2), + eSCENE_DEFAULT = 0 + }; + }; + + struct SimulationStage + { + enum Enum + { + eCOMPLETE, + eCOLLIDE, + eFETCHCOLLIDE, + eADVANCE, + eFETCHRESULT + }; + }; + + // PT: TODO: revisit the need for a virtual interface + struct SqBoundsSync + { + virtual void sync(const Sq::PrunerHandle* handles, const PxU32* indices, const PxBounds3* bounds, PxU32 count, const Cm::BitMap& dirtyShapeSimMap) = 0; + + virtual ~SqBoundsSync() {} + }; + + // PT: TODO: revisit the need for a virtual interface + struct SqRefFinder + { + virtual Sq::PrunerHandle find(const PxRigidBody* body, const PxShape* shape) = 0; + + virtual ~SqRefFinder() {} + }; + + class Scene : public Ps::UserAllocated + { + struct SimpleBodyPair + { + BodySim* body1; + BodySim* body2; + PxU32 body1ID; + PxU32 body2ID; + }; + + PX_NOCOPY(Scene) + + //--------------------------------------------------------------------------------- + // External interface + //--------------------------------------------------------------------------------- + public: + + void release(); + + PX_FORCE_INLINE void setGravity(const PxVec3& g) { mGravity = g; mBodyGravityDirty = true; } + PX_FORCE_INLINE PxVec3 getGravity() const { return mGravity; } + PX_FORCE_INLINE void setElapsedTime(const PxReal t) { mDt = t; mOneOverDt = t > 0.0f ? 1.0f/t : 0.0f; } + + void setBounceThresholdVelocity(const PxReal t); + PxReal getBounceThresholdVelocity() const; + + PX_FORCE_INLINE void setPublicFlags(PxSceneFlags flags) { mPublicFlags = flags; } + PX_FORCE_INLINE PxSceneFlags getPublicFlags() const { return mPublicFlags; } + + void setFrictionType(PxFrictionType::Enum model); + PxFrictionType::Enum getFrictionType() const; + void setPCM(bool enabled); + void setContactCache(bool enabled); + + void addStatic(StaticCore&, void*const *shapes, PxU32 nbShapes, size_t shapePtrOffset, PxBounds3* uninflatedBounds); + void removeStatic(StaticCore&, Ps::InlineArray& removedShapes, bool wakeOnLostTouch); + + void addBody(BodyCore&, void*const *shapes, PxU32 nbShapes, size_t shapePtrOffset, PxBounds3* uninflatedBounds, bool compound); + void removeBody(BodyCore&, Ps::InlineArray& removedShapes, bool wakeOnLostTouch); + + // Batch insertion API. + // the bounds generated here are the uninflated bounds for the shapes, *if* they are trigger or sim shapes. + // It's up to the caller to ensure the bounds array is big enough. + // Some care is required in handling these since sim and SQ tweak the bounds in slightly different ways. + + void startBatchInsertion(BatchInsertionState&); + void addStatic(PxActor* actor, BatchInsertionState&, PxBounds3* outBounds); + void addBody(PxActor* actor, BatchInsertionState&, PxBounds3* outBounds, bool compound); + void finishBatchInsertion(BatchInsertionState&); + + // Batch remove helpers + PX_FORCE_INLINE void setBatchRemove(BatchRemoveState* bs) { mBatchRemoveState = bs; } + PX_FORCE_INLINE BatchRemoveState* getBatchRemove() const { return mBatchRemoveState; } + void prefetchForRemove(const BodyCore& ) const; + void prefetchForRemove(const StaticCore& ) const; + + void addConstraint(ConstraintCore&, RigidCore*, RigidCore*); + void removeConstraint(ConstraintCore&); + + void addArticulation(ArticulationCore&, BodyCore& root); + void removeArticulation(ArticulationCore&); + + void addArticulationJoint(ArticulationJointCore&, BodyCore& parent, BodyCore& child); + void removeArticulationJoint(ArticulationJointCore&); + + void addArticulationSimControl(Sc::ArticulationCore& core); + + PxU32 getNbArticulations() const; + Sc::ArticulationCore* const* getArticulations(); + + PxU32 getNbConstraints() const; + Sc::ConstraintCore*const * getConstraints(); + + void initContactsIterator(ContactIterator&, PxsContactManagerOutputIterator&); + + // Simulation events + void setSimulationEventCallback(PxSimulationEventCallback* callback); + PxSimulationEventCallback* getSimulationEventCallback() const; + + // Contact modification + void setContactModifyCallback(PxContactModifyCallback* callback); + PxContactModifyCallback* getContactModifyCallback() const; + void setCCDContactModifyCallback(PxCCDContactModifyCallback* callback); + PxCCDContactModifyCallback* getCCDContactModifyCallback() const; + + void setCCDMaxPasses(PxU32 ccdMaxPasses); + PxU32 getCCDMaxPasses() const; + + // Broad-phase callback + void setBroadPhaseCallback(PxBroadPhaseCallback* callback); + PxBroadPhaseCallback* getBroadPhaseCallback() const; + + // Broad-phase management + void finishBroadPhase(PxBaseTask* continuation); + void finishBroadPhaseStage2(const PxU32 ccdPass); + void preallocateContactManagers(PxBaseTask* continuation); + + void islandInsertion(PxBaseTask* continuation); + void registerContactManagers(PxBaseTask* continuation); + void registerInteractions(PxBaseTask* continuation); + void registerSceneInteractions(PxBaseTask* continuation); + + void secondPassNarrowPhase(PxBaseTask* continuation); + + // Groups + void setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance); + PxDominanceGroupPair getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const; + + void setSolverBatchSize(PxU32 solverBatchSize); + PxU32 getSolverBatchSize() const; + + void setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value); + PxReal getVisualizationParameter(PxVisualizationParameter::Enum param) const; + + void setVisualizationCullingBox(const PxBounds3& box); + const PxBounds3& getVisualizationCullingBox() const; + + // Run + void simulate(PxReal timeStep, PxBaseTask* continuation); + void advance(PxReal timeStep, PxBaseTask* continuation); + void collide(PxReal timeStep, PxBaseTask* continuation); + void endSimulation(); + void flush(bool sendPendingReports); + void fireBrokenConstraintCallbacks(); + void fireTriggerCallbacks(); + void fireQueuedContactCallbacks(bool asPartOfFlush); + void fireOnAdvanceCallback(); + + const Ps::Array& + getQueuedContactPairHeaders(); + + bool fireOutOfBoundsCallbacks(); + void prepareOutOfBoundsCallbacks(); + void postCallbacksPreSync(); + void postReportsCleanup(); + void fireCallbacksPostSync(); + void syncSceneQueryBounds(SqBoundsSync& sync, SqRefFinder& finder); + + PxU32 getDefaultContactReportStreamBufferSize() const; + + PxReal getFrictionOffsetThreshold() const; + + PX_FORCE_INLINE void setLimits(const PxSceneLimits& limits) { mLimits = limits; } + PX_FORCE_INLINE const PxSceneLimits& getLimits() const { return mLimits; } + + void visualizeStartStep(); + void visualizeEndStep(); + Cm::RenderBuffer& getRenderBuffer(); + + void setNbContactDataBlocks(PxU32 blockCount); + PxU32 getNbContactDataBlocksUsed() const; + PxU32 getMaxNbContactDataBlocksUsed() const; + PxU32 getMaxNbConstraintDataBlocksUsed() const; + + void setScratchBlock(void* addr, PxU32 size); + +// PX_ENABLE_SIM_STATS + void getStats(PxSimulationStatistics& stats) const; + PX_FORCE_INLINE SimStats& getStatsInternal() { return *mStats; } +// PX_ENABLE_SIM_STATS + + void buildActiveActors(); + void buildActiveAndFrozenActors(); + PxActor** getActiveActors(PxU32& nbActorsOut); + void setActiveActors(PxActor** actors, PxU32 nbActors); + + PxActor** getFrozenActors(PxU32& nbActorsOut); + + void finalizeContactStreamAndCreateHeader(PxContactPairHeader& header, + const ActorPairReport& aPair, + ContactStreamManager& cs, PxU32 removedShapeTestMask); + + PxClientID createClient(); + PX_FORCE_INLINE PxU32 getNbClients() const { return mClients.size(); } + + PxTaskManager* getTaskManagerPtr() const { return mTaskManager; } + + void shiftOrigin(const PxVec3& shift); + + PX_FORCE_INLINE Ps::Pool* getSimStateDataPool() { return mSimStateDataPool; } + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + //internal public methods: + public: + Scene(const PxSceneDesc& desc, PxU64 contextID); + ~Scene() {} //use release() plz. + + void preAllocate(PxU32 nbStatics, PxU32 nbBodies, PxU32 nbStaticShapes, PxU32 nbDynamicShapes); + + PX_FORCE_INLINE PxsContext* getLowLevelContext() { return mLLContext; } + PX_FORCE_INLINE const PxsContext* getLowLevelContext() const { return mLLContext; } + + PX_FORCE_INLINE Dy::Context* getDynamicsContext() { return mDynamicsContext; } + PX_FORCE_INLINE const Dy::Context* getDynamicsContext() const { return mDynamicsContext; } + + PX_FORCE_INLINE PxsSimulationController* getSimulationController() { return mSimulationController; } + PX_FORCE_INLINE const PxsSimulationController* getSimulationController() const { return mSimulationController; } + + PX_FORCE_INLINE Bp::AABBManager* getAABBManager() { return mAABBManager; } + PX_FORCE_INLINE const Bp::AABBManager* getAABBManager() const { return mAABBManager; } + PX_FORCE_INLINE Ps::Array& getCcdBodies() { return mCcdBodies; } + + /*PX_FORCE_INLINE PxsIslandManager* getIslandManager() { return mIslandManager; } + PX_FORCE_INLINE const PxsIslandManager* getIslandManager() const { return mIslandManager; }*/ + + PX_FORCE_INLINE IG::SimpleIslandManager* getSimpleIslandManager() { return mSimpleIslandManager; } + PX_FORCE_INLINE const IG::SimpleIslandManager* getSimpleIslandManager() const { return mSimpleIslandManager; } + + PX_FORCE_INLINE Sc::SimulationStage::Enum getSimulationStage() const { return mSimulationStage; } + PX_FORCE_INLINE void setSimulationStage(Sc::SimulationStage::Enum stage) { mSimulationStage = stage; } + + void addShape(RigidSim&, ShapeCore&, PxBounds3* uninflatedBounds); + void removeShape(ShapeSim&, bool wakeOnLostTouch); + + void registerShapeInNphase(const ShapeCore& shapeCore); + void unregisterShapeFromNphase(const ShapeCore& shapeCore); + + void notifyNphaseOnUpdateShapeMaterial(const ShapeCore& shapeCore); + + // Get an array of the active actors. + PX_FORCE_INLINE BodyCore*const* getActiveBodiesArray() const { return mActiveBodies.begin(); } + PX_FORCE_INLINE PxU32 getNumActiveBodies() const { return mActiveBodies.size(); } + + PX_FORCE_INLINE BodyCore*const* getActiveCompoundBodiesArray() const { return mActiveCompoundBodies.begin(); } + PX_FORCE_INLINE PxU32 getNumActiveCompoundBodies() const { return mActiveCompoundBodies.size(); } + + PX_FORCE_INLINE PxU32 getNbInteractions(InteractionType::Enum type) const { return mInteractions[type].size(); } + PX_FORCE_INLINE PxU32 getNbActiveInteractions(InteractionType::Enum type) const { return mActiveInteractionCount[type]; } + // Get all interactions of a certain type + PX_FORCE_INLINE Interaction** getInteractions(InteractionType::Enum type) { return mInteractions[type].begin(); } + PX_FORCE_INLINE Interaction** getActiveInteractions(InteractionType::Enum type) { return mInteractions[type].begin(); } + + void registerInteraction(Interaction* interaction, bool active); + void unregisterInteraction(Interaction* interaction); + + void notifyInteractionActivated(Interaction* interaction); + void notifyInteractionDeactivated(Interaction* interaction); + + // for pool management of interaction arrays, a major cause of dynamic allocation + void** allocatePointerBlock(PxU32 size); + void deallocatePointerBlock(void**, PxU32 size); + private: + // Get the number of active one-way dominator actors + PX_FORCE_INLINE PxU32 getActiveKinematicBodiesCount() const { return mActiveKinematicBodyCount; } + + // Get an iterator to the active one-way dominator actors + PX_FORCE_INLINE BodyCore*const* getActiveKinematicBodies() const { return mActiveBodies.begin(); } + + // Get the number of active non-kinematic actors + PX_FORCE_INLINE PxU32 getActiveDynamicBodiesCount() const { return mActiveBodies.size() - mActiveKinematicBodyCount; } + + // Get the active non-kinematic actors + PX_FORCE_INLINE BodyCore*const* getActiveDynamicBodies() const { return mActiveBodies.begin() + mActiveKinematicBodyCount; } + + void swapInteractionArrayIndices(PxU32 id1, PxU32 id2, InteractionType::Enum type); + public: + + PX_FORCE_INLINE Cm::BitMap& getDirtyShapeSimMap() { return mDirtyShapeSimMap; } + + void addToActiveBodyList(BodySim& actor); + void addToActiveCompoundBodyList(BodySim& actor); + void removeFromActiveBodyList(BodySim& actor); + void removeFromActiveCompoundBodyList(BodySim& actor); + void swapInActiveBodyList(BodySim& body); // call when an active body gets switched from dynamic to kinematic or vice versa + + void addToFrozenBodyList(BodySim& actor); + void removeFromFrozenBodyList(BodySim& actor); + void addBrokenConstraint(Sc::ConstraintCore*); + void addActiveBreakableConstraint(Sc::ConstraintSim*, ConstraintInteraction*); + void removeActiveBreakableConstraint(Sc::ConstraintSim*); + //the Actor should register its top level shapes with these. + void removeBody(BodySim&); + + void raiseSceneFlag(SceneInternalFlag::Enum flag) { mInternalFlags |= flag; } + + //lists of actors woken up or put to sleep last simulate + void onBodyWakeUp(BodySim* body); + void onBodySleep(BodySim* body); + + PX_FORCE_INLINE bool isValid() const { return (mLLContext != NULL); } + + void addToLostTouchList(BodySim* body1, BodySim* body2); + + void initDominanceMatrix(); + + void setCreateContactReports(bool s); + + PxU32 createAggregate(void* userData, bool selfCollisions); + void deleteAggregate(PxU32 id); + + Dy::ArticulationV* createLLArticulation(Sc::ArticulationSim* sim); + void destroyLLArticulation(Dy::ArticulationV&); + + + Ps::Pool* + getConstraintInteractionPool() const { return mConstraintInteractionPool; } + public: + PxBroadPhaseType::Enum getBroadPhaseType() const; + bool getBroadPhaseCaps(PxBroadPhaseCaps& caps) const; + PxU32 getNbBroadPhaseRegions() const; + PxU32 getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex) const; + PxU32 addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion); + bool removeBroadPhaseRegion(PxU32 handle); + void** getOutOfBoundsAggregates(); + PxU32 getNbOutOfBoundsAggregates(); + void clearOutOfBoundsAggregates(); + + PX_FORCE_INLINE const PxsMaterialManager& getMaterialManager() const { return mMaterialManager; } + PX_FORCE_INLINE PxsMaterialManager& getMaterialManager() { return mMaterialManager; } + + //material management functions to be called from Scb::Scene + void registerMaterialInNP(const PxsMaterialCore& materialCore); + void updateMaterialInNP(const PxsMaterialCore& materialCore); + void unregisterMaterialInNP(const PxsMaterialCore& materialCore); + + // Collision filtering + void setFilterShaderData(const void* data, PxU32 dataSize); + PX_FORCE_INLINE const void* getFilterShaderDataFast() const { return mFilterShaderData; } + PX_FORCE_INLINE PxU32 getFilterShaderDataSizeFast() const { return mFilterShaderDataSize; } + PX_FORCE_INLINE PxSimulationFilterShader getFilterShaderFast() const { return mFilterShader; } + PX_FORCE_INLINE PxSimulationFilterCallback* getFilterCallbackFast() const { return mFilterCallback; } + PX_FORCE_INLINE PxPairFilteringMode::Enum getKineKineFilteringMode() const { return mKineKineFilteringMode; } + PX_FORCE_INLINE PxPairFilteringMode::Enum getStaticKineFilteringMode() const { return mStaticKineFilteringMode; } + + PX_FORCE_INLINE PxU32 getTimeStamp() const { return mTimeStamp; } + PX_FORCE_INLINE PxU32 getReportShapePairTimeStamp() const { return mReportShapePairTimeStamp; } + + PX_FORCE_INLINE PxReal getOneOverDt() const { return mOneOverDt; } + PX_FORCE_INLINE PxReal getDt() const { return mDt; } + + PX_FORCE_INLINE const PxVec3& getGravityFast() const { return mGravity; } + PX_FORCE_INLINE bool readFlag(SceneInternalFlag::Enum flag) const { return (mInternalFlags & flag) != 0; } + PX_FORCE_INLINE bool readPublicFlag(PxSceneFlag::Enum flag) const { return (mPublicFlags & flag); } + + PX_FORCE_INLINE NPhaseCore* getNPhaseCore() const { return mNPhaseCore; } + + void checkConstraintBreakage(); + + PX_FORCE_INLINE Ps::Array& + getTriggerBufferExtraData() { return *mTriggerBufferExtraData; } + PX_FORCE_INLINE Ps::Array& getTriggerBufferAPI() { return mTriggerBufferAPI; } + void reserveTriggerReportBufferSpace(const PxU32 pairCount, PxTriggerPair*& triggerPairBuffer, TriggerPairExtraData*& triggerPairExtraBuffer); + + PX_FORCE_INLINE ObjectIDTracker& getRigidIDTracker() { return *mRigidIDTracker; } + PX_FORCE_INLINE ObjectIDTracker& getShapeIDTracker() { return *mShapeIDTracker; } + + PX_FORCE_INLINE void markReleasedBodyIDForLostTouch(PxU32 id) { mLostTouchPairsDeletedBodyIDs.growAndSet(id); } + void resizeReleasedBodyIDMaps(PxU32 maxActors, PxU32 numActors); + + PX_FORCE_INLINE StaticSim& getStaticAnchor() { return *mStaticAnchor; } + + PX_FORCE_INLINE ConstraintProjectionManager& getProjectionManager() { return *mProjectionManager; } + + PX_FORCE_INLINE Bp::BoundsArray& getBoundsArray() const { return *mBoundsArray; } + PX_FORCE_INLINE void updateContactDistance(PxU32 idx, PxReal distance) { (*mContactDistance)[idx] = distance; mHasContactDistanceChanged = true; } + PX_FORCE_INLINE SqBoundsManager& getSqBoundsManager() const { return *mSqBoundsManager; } + + PX_FORCE_INLINE PxReal getVisualizationScale() const { return mVisualizationScale; } // This is actually redundant but makes checks whether debug visualization is enabled faster + + PX_FORCE_INLINE BodyCore* const* getSleepBodiesArray(PxU32& count) { count = mSleepBodies.size(); return mSleepBodies.getEntries(); } + + PX_FORCE_INLINE PxTaskManager& getTaskManager() const { PX_ASSERT(mTaskManager); return *mTaskManager; } + + Cm::FlushPool* getFlushPool(); + + PX_FORCE_INLINE bool getStabilizationEnabled() const { return mEnableStabilization; } + + PX_FORCE_INLINE void setPostSolverVelocityNeeded() { mContactReportsNeedPostSolverVelocity = true; } + + ObjectIDTracker& getConstraintIDTracker() { return *mConstraintIDTracker; } + + + void* allocateConstraintBlock(PxU32 size); + void deallocateConstraintBlock(void* addr, PxU32 size); + + PX_INLINE ObjectIDTracker& getElementIDPool() { return *mElementIDPool; } + + PX_FORCE_INLINE Cm::BitMap& getVelocityModifyMap() { return mVelocityModifyMap; } + + void stepSetupCollide(PxBaseTask* continuation);//This is very important to guarantee thread safty in the collide + PX_FORCE_INLINE void addToPosePreviewList(BodySim& b) { PX_ASSERT(!mPosePreviewBodies.contains(&b)); mPosePreviewBodies.insert(&b); } + PX_FORCE_INLINE void removeFromPosePreviewList(BodySim& b) { PX_ASSERT(mPosePreviewBodies.contains(&b)); mPosePreviewBodies.erase(&b); } +#if PX_DEBUG + PX_FORCE_INLINE bool isInPosePreviewList(BodySim& b) const { return mPosePreviewBodies.contains(&b); } +#endif + + PX_FORCE_INLINE void setSpeculativeCCDRigidBody(const PxU32 index) { mSpeculativeCCDRigidBodyBitMap.growAndSet(index); } + PX_FORCE_INLINE void resetSpeculativeCCDRigidBody(const PxU32 index) { mSpeculativeCCDRigidBodyBitMap.reset(index); } + + PX_FORCE_INLINE void setSpeculativeCCDArticulationLink(const PxU32 index) { mSpeculativeCDDArticulationBitMap.growAndSet(index); } + PX_FORCE_INLINE void resetSpeculativeCCDArticulationLink(const PxU32 index) { mSpeculativeCDDArticulationBitMap.reset(index); } + + PX_FORCE_INLINE PxU64 getContextId() const { return mContextId; } + PX_FORCE_INLINE bool isUsingGpuRigidBodies() const { return mUseGpuRigidBodies; } + + //internal private methods: + private: + void releaseConstraints(bool endOfScene); + PX_INLINE void clearBrokenConstraintBuffer(); + + ///////////////////////////////////////////////////////////// + + void prepareCollide(); + + void collideStep(PxBaseTask* continuation); + void advanceStep(PxBaseTask* continuation); + + // subroutines of collideStep/solveStep: + void kinematicsSetup(PxBaseTask* continuation); + void stepSetupSolve(PxBaseTask* continuation); + //void stepSetupSimulate(); + + void fetchPatchEvents(PxBaseTask*); + void processNarrowPhaseTouchEvents(); + void processNarrowPhaseTouchEventsStage2(PxBaseTask*); + void setEdgesConnected(PxBaseTask*); + void processNarrowPhaseLostTouchEvents(PxBaseTask*); + void processNarrowPhaseLostTouchEventsIslands(PxBaseTask*); + void processLostTouchPairs(); + void integrateKinematicPose(); + void updateKinematicCached(PxBaseTask* task); + + void beforeSolver(PxBaseTask* continuation); + void checkForceThresholdContactEvents(const PxU32 ccdPass); + void processCallbacks(bool pendingReportsOnly = false); + void endStep(); + private: + PX_FORCE_INLINE void putObjectsToSleep(PxU32 infoFlag); + PX_FORCE_INLINE void putInteractionsToSleep(); + PX_FORCE_INLINE void wakeObjectsUp(PxU32 infoFlag); + + void collectPostSolverVelocitiesBeforeCCD(); + void updateFromVisualizationParameters(); + + void clearSleepWakeBodies(void); + PX_INLINE void cleanUpSleepBodies(); + PX_INLINE void cleanUpWokenBodies(); + PX_INLINE void cleanUpSleepOrWokenBodies(Ps::CoalescedHashSet& bodyList, PxU32 removeFlag, bool& validMarker); + + //internal variables: + private: + // Material manager + PX_ALIGN(16, PxsMaterialManager mMaterialManager); + + PxU64 mContextId; + + Ps::Array mActiveBodies; // Sorted: kinematic before dynamic + PxU32 mActiveKinematicBodyCount; // Number of active kinematics. This is also the index in mActiveBodies where the active dynamic bodies start. + Ps::Array mActiveCompoundBodies; + + Ps::Array mInteractions[InteractionType::eTRACKED_IN_SCENE_COUNT]; + PxU32 mActiveInteractionCount[InteractionType::eTRACKED_IN_SCENE_COUNT]; // Interactions with id < activeInteractionCount are active + + template + struct Block + { + PxU8 mem[sizeof(T)*size]; + Block() {} // get around VS warning C4345, otherwise useless + }; + + typedef Block PointerBlock8; + typedef Block PointerBlock16; + typedef Block PointerBlock32; + + Ps::Pool mPointerBlock8Pool; + Ps::Pool mPointerBlock16Pool; + Ps::Pool mPointerBlock32Pool; + + PxsContext* mLLContext; + + Bp::AABBManager* mAABBManager; + Bp::BroadPhase* mBP; + PxsCCDContext* mCCDContext; + PxI32 mNumFastMovingShapes; + PxU32 mCCDPass; + + //PxsIslandManager* mIslandManager; + + IG::SimpleIslandManager* mSimpleIslandManager; + + Dy::Context* mDynamicsContext; + + + PxsMemoryManager* mMemoryManager; + +#if PX_SUPPORT_GPU_PHYSX + PxsKernelWranglerManager* mGpuWranglerManagers; + PxsHeapMemoryAllocatorManager* mHeapMemoryAllocationManager; +#endif + + PxsSimulationController* mSimulationController; + + PxsSimulationControllerCallback* mSimulationControllerCallback; + + PxSceneLimits mLimits; + + PxVec3 mGravity; //!< Gravity vector + PxU32 mBodyGravityDirty; // Set to true before body->updateForces() when the mGravity has changed + + Ps::Array + mQueuedContactPairHeaders; + //time: + //constants set with setTiming(): + PxReal mDt; //delta time for current step. + PxReal mOneOverDt; //inverse of dt. + //stepping / counters: + PxU32 mTimeStamp; //Counts number of steps. + PxU32 mReportShapePairTimeStamp; //Timestamp for refreshing the shape pair report structure. Updated before delayed shape/actor deletion and before CCD passes. + //containers: + // Those ones contain shape ptrs from Actor, i.e. compound level, not subparts + + Ps::CoalescedHashSet + mConstraints; + + Sc::ConstraintProjectionManager* mProjectionManager; + Bp::BoundsArray* mBoundsArray; + Ps::Array* mContactDistance; + bool mHasContactDistanceChanged; + SqBoundsManager* mSqBoundsManager; + + Ps::Array mCcdBodies; + Ps::Array mProjectedBodies; + Ps::Array mTriggerBufferAPI; + Ps::Array* + mTriggerBufferExtraData; + + PxU32 mRemovedShapeCountAtSimStart; // counter used to detect whether there have been buffered shape removals + + Ps::CoalescedHashSet mArticulations; + + Ps::Array mBrokenConstraints; + Ps::CoalescedHashSet mActiveBreakableConstraints; + + // pools for joint buffers + // Fixed joint is 92 bytes, D6 is 364 bytes right now. So these three pools cover all the internal cases + typedef Block MemBlock128; + typedef Block MemBlock256; + typedef Block MemBlock384; + + Ps::Pool2 mMemBlock128Pool; + Ps::Pool2 mMemBlock256Pool; + Ps::Pool2 mMemBlock384Pool; + + + // broad phase data: + NPhaseCore* mNPhaseCore; + + // Collision filtering + void* mFilterShaderData; + PxU32 mFilterShaderDataSize; + PxU32 mFilterShaderDataCapacity; + PxSimulationFilterShader mFilterShader; + PxSimulationFilterCallback* mFilterCallback; + + PxPairFilteringMode::Enum mKineKineFilteringMode; + PxPairFilteringMode::Enum mStaticKineFilteringMode; + + Ps::CoalescedHashSet mSleepBodies; + Ps::CoalescedHashSet mWokeBodies; + bool mWokeBodyListValid; + bool mSleepBodyListValid; + bool mEnableStabilization; + Ps::Array mClients; //an array of transform arrays, one for each client. + + Ps::Array mActiveActors; + Ps::Array mFrozenActors; + + Ps::Array mClientPosePreviewBodies; // buffer for bodies that requested early report of the integrated pose (eENABLE_POSE_INTEGRATION_PREVIEW). + // This buffer gets exposed to users. Is officially accessible from PxSimulationEventCallback::onAdvance() + // until the next simulate()/advance(). + Ps::Array mClientPosePreviewBuffer; // buffer of newly integrated poses for the bodies that requested a preview. This buffer gets exposed + // to users. + + PxSimulationEventCallback* mSimulationEventCallback; + PxBroadPhaseCallback* mBroadPhaseCallback; + + SimStats* mStats; + PxU32 mInternalFlags; //!< Combination of ::SceneFlag + PxSceneFlags mPublicFlags; //copy of PxSceneDesc::flags, of type PxSceneFlag + + ObjectIDTracker* mConstraintIDTracker; + ObjectIDTracker* mShapeIDTracker; + ObjectIDTracker* mRigidIDTracker; + ObjectIDTracker* mElementIDPool; + + StaticSim* mStaticAnchor; + + Cm::PreallocatingPool* mShapeSimPool; + Cm::PreallocatingPool* mStaticSimPool; + Cm::PreallocatingPool* mBodySimPool; + Ps::Pool* mConstraintSimPool; + LLArticulationPool* mLLArticulationPool; + LLArticulationRCPool* mLLArticulationRCPool; + + Ps::Pool* + mConstraintInteractionPool; + + Ps::Pool* mSimStateDataPool; + + BatchRemoveState* mBatchRemoveState; + + Ps::Array mLostTouchPairs; + Cm::BitMap mLostTouchPairsDeletedBodyIDs; // Need to know which bodies have been deleted when processing the lost touch pair list. + // Can't use the existing rigid object ID tracker class since this map needs to be cleared at + // another point in time. + + Cm::BitMap mVelocityModifyMap; + + Ps::Array mTouchFoundEvents; + Ps::Array mTouchLostEvents; + + Ps::Array mFoundPatchManagers; + Ps::Array mLostPatchManagers; + + Ps::Array mOutOfBoundsIDs; + + Cm::BitMap mDirtyShapeSimMap; + + PxU32 mDominanceBitMatrix[PX_MAX_DOMINANCE_GROUP]; + + PxReal mVisualizationScale; // Redundant but makes checks whether debug visualization is enabled faster + + bool mVisualizationParameterChanged; + + // statics: + PxU32 mNbRigidStatics; + PxU32 mNbRigidDynamics; + PxU32 mNbGeometries[PxGeometryType::eGEOMETRY_COUNT]; + + PxU32 mNumDeactivatingNodes[2]; + + // task decomposition + void preBroadPhase(PxBaseTask* continuation); + void broadPhase(PxBaseTask* continuation); + void postBroadPhase(PxBaseTask* continuation); + void postBroadPhaseContinuation(PxBaseTask* continuation); + void preRigidBodyNarrowPhase(PxBaseTask* continuation); + void postBroadPhaseStage2(PxBaseTask* continuation); + void postBroadPhaseStage3(PxBaseTask* continuation); + void rigidBodyNarrowPhase(PxBaseTask* continuation); + void unblockNarrowPhase(PxBaseTask* continuation); + void islandGen(PxBaseTask* continuation); + void processLostSolverPatches(PxBaseTask* continuation); + void postIslandGen(PxBaseTask* continuation); + void processTriggerInteractions(PxBaseTask* continuation); + void solver(PxBaseTask* continuation); + void updateBodiesAndShapes(PxBaseTask* continuation); + void updateSimulationController(PxBaseTask* continuation); + void updateDynamics(PxBaseTask* continuation); + void processLostContacts(PxBaseTask*); + void processLostContacts2(PxBaseTask*); + void processLostContacts3(PxBaseTask*); + void destroyManagers(PxBaseTask*); + void lostTouchReports(PxBaseTask*); + void unregisterInteractions(PxBaseTask*); + void postThirdPassIslandGen(PxBaseTask*); + void postSolver(PxBaseTask* continuation); + void constraintProjection(PxBaseTask* continuation); + void afterIntegration(PxBaseTask* continuation); // performs sleep check, for instance + void postCCDPass(PxBaseTask* continuation); + void ccdBroadPhaseAABB(PxBaseTask* continuation); + void ccdBroadPhase(PxBaseTask* continuation); + void updateCCDMultiPass(PxBaseTask* continuation); + void updateCCDSinglePass(PxBaseTask* continuation); + void updateCCDSinglePassStage2(PxBaseTask* continuation); + void updateCCDSinglePassStage3(PxBaseTask* continuation); + void finalizationPhase(PxBaseTask* continuation); + + void postNarrowPhase(PxBaseTask* continuation); + + void addShapes(void *const* shapes, PxU32 nbShapes, size_t ptrOffset, RigidSim& sim, PxBounds3* outBounds); + void removeShapes(RigidSim& , Ps::InlineArray& , Ps::InlineArray&, bool wakeOnLostTouch); + + + private: + + void addShapes(void *const* shapes, PxU32 nbShapes, size_t ptrOffset, RigidSim& sim, ShapeSim*& prefetchedShapeSim, PxBounds3* outBounds); + + Cm::DelegateTask mSecondPassNarrowPhase; + Cm::DelegateFanoutTask mPostNarrowPhase; + Cm::DelegateFanoutTask mFinalizationPhase; + Cm::DelegateTask mUpdateCCDMultiPass; + + //multi-pass ccd stuff + Ps::Array > mUpdateCCDSinglePass; + Ps::Array > mUpdateCCDSinglePass2; + Ps::Array > mUpdateCCDSinglePass3; + Ps::Array > mCCDBroadPhaseAABB; + Ps::Array > mCCDBroadPhase; + Ps::Array > mPostCCDPass; + PxU32 mCurrentCCDTask; + + Cm::DelegateTask mAfterIntegration; + Cm::DelegateTask mConstraintProjection; + Cm::DelegateTask mPostSolver; + Cm::DelegateTask mSolver; + Cm::DelegateTask mUpdateBodiesAndShapes; + Cm::DelegateTask mUpdateSimulationController; + Cm::DelegateTask mUpdateDynamics; + Cm::DelegateTask mProcessLostContactsTask; + Cm::DelegateTask mProcessLostContactsTask2; + Cm::DelegateTask mProcessLostContactsTask3; + Cm::DelegateTask mDestroyManagersTask; + Cm::DelegateTask mLostTouchReportsTask; + Cm::DelegateTask mUnregisterInteractionsTask; + Cm::DelegateTask mProcessNarrowPhaseLostTouchTasks; + Cm::DelegateTask mProcessNPLostTouchEvents; + Cm::DelegateTask mPostThirdPassIslandGenTask; + Cm::DelegateTask mPostIslandGen; + Cm::DelegateTask mIslandGen; + Cm::DelegateTask mPreRigidBodyNarrowPhase; + Cm::DelegateTask mSetEdgesConnectedTask; + Cm::DelegateTask mFetchPatchEventsTask; + Cm::DelegateTask mProcessLostPatchesTask; + Cm::DelegateTask mRigidBodyNarrowPhase; + Cm::DelegateTask mRigidBodyNPhaseUnlock; + Cm::DelegateTask mPostBroadPhase; + Cm::DelegateTask mPostBroadPhaseCont; + Cm::DelegateTask mPostBroadPhase2; + Cm::DelegateFanoutTask mPostBroadPhase3; + Cm::DelegateTask mPreallocateContactManagers; + Cm::DelegateTask mIslandInsertion; + Cm::DelegateTask mRegisterContactManagers; + Cm::DelegateTask mRegisterInteractions; + Cm::DelegateTask mRegisterSceneInteractions; + Cm::DelegateTask mBroadPhase; + Cm::DelegateTask mAdvanceStep; + Cm::DelegateTask mCollideStep; + + Cm::FlushPool mTaskPool; + PxTaskManager* mTaskManager; + + bool mContactReportsNeedPostSolverVelocity; + bool mUseGpuRigidBodies; + + SimulationStage::Enum mSimulationStage; + + ConstraintGroupNode** mTmpConstraintGroupRootBuffer; // temporary list of constraint group roots, used for constraint projection + + Ps::CoalescedHashSet mPosePreviewBodies; // list of bodies that requested early report of the integrated pose (eENABLE_POSE_INTEGRATION_PREVIEW). + + Ps::Array mPreallocatedContactManagers; + Ps::Array mPreallocatedShapeInteractions; + Ps::Array mPreallocatedInteractionMarkers; + + OverlapFilterTask* mOverlapFilterTaskHead; + Ps::Array mFilterInfo; + Cm::BitMap mSpeculativeCCDRigidBodyBitMap; + Cm::BitMap mSpeculativeCDDArticulationBitMap; + }; + + bool activateInteraction(Sc::Interaction* interaction, void* data); + bool deactivateInteraction(Sc::Interaction* interaction); + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h new file mode 100644 index 000000000..ce52fa86f --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h @@ -0,0 +1,137 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_SHAPECORE +#define PX_PHYSICS_SCP_SHAPECORE + +#include "PsUserAllocated.h" +#include "GuGeometryUnion.h" +#include "PxvGeometry.h" +#include "PsUtilities.h" +#include "PxFiltering.h" +#include "PxShape.h" + +namespace physx +{ +class PxShape; + +namespace Sc +{ + class Scene; + class RigidCore; + class BodyCore; + class ShapeSim; + class MaterialCore; + + class ShapeCore : public Ps::UserAllocated + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: +// PX_SERIALIZATION + ShapeCore(const PxEMPTY); + void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); + void resolveMaterialReference(PxU32 materialTableIndex, PxU16 materialIndex); +//~PX_SERIALIZATION + + ShapeCore(const PxGeometry& geometry, + PxShapeFlags shapeFlags, + const PxU16* materialIndices, + PxU16 materialCount); + + ~ShapeCore(); + + PX_FORCE_INLINE PxGeometryType::Enum getGeometryType() const { return mCore.geometry.getType(); } + PxShape* getPxShape(); + const PxShape* getPxShape() const; + + PX_FORCE_INLINE const Gu::GeometryUnion& getGeometryUnion() const { return mCore.geometry; } + PX_FORCE_INLINE const PxGeometry& getGeometry() const { return mCore.geometry.getGeometry(); } + void setGeometry(const PxGeometry& geom); + + PxU16 getNbMaterialIndices() const; + const PxU16* getMaterialIndices() const; + void setMaterialIndices(const PxU16* materialIndices, PxU16 materialIndexCount); + + PX_FORCE_INLINE const PxTransform& getShape2Actor() const { return mCore.transform; } + PX_FORCE_INLINE void setShape2Actor(const PxTransform& s2b) { mCore.transform = s2b; } + + PX_FORCE_INLINE const PxFilterData& getSimulationFilterData() const { return mSimulationFilterData; } + PX_FORCE_INLINE void setSimulationFilterData(const PxFilterData& data) { mSimulationFilterData = data; } + + // PT: this one doesn't need double buffering + PX_FORCE_INLINE const PxFilterData& getQueryFilterData() const { return mQueryFilterData; } + PX_FORCE_INLINE void setQueryFilterData(const PxFilterData& data) { mQueryFilterData = data; } + + PX_FORCE_INLINE PxReal getContactOffset() const { return mCore.contactOffset; } + PX_FORCE_INLINE void setContactOffset(PxReal offset) { mCore.contactOffset = offset; } + + PX_FORCE_INLINE PxReal getRestOffset() const { return mRestOffset; } + PX_FORCE_INLINE void setRestOffset(PxReal offset) { mRestOffset = offset; } + + PX_FORCE_INLINE PxReal getTorsionalPatchRadius() const { return mTorsionalRadius; } + PX_FORCE_INLINE void setTorsionalPatchRadius(PxReal tpr) { mTorsionalRadius = tpr; } + + PX_FORCE_INLINE PxReal getMinTorsionalPatchRadius() const {return mMinTorsionalPatchRadius; } + PX_FORCE_INLINE void setMinTorsionalPatchRadius(PxReal radius) { mMinTorsionalPatchRadius = radius; } + + PX_FORCE_INLINE PxShapeFlags getFlags() const { return PxShapeFlags(mCore.mShapeFlags); } + PX_FORCE_INLINE void setFlags(PxShapeFlags f) { mCore.mShapeFlags = f; } + + PX_FORCE_INLINE const PxsShapeCore& getCore() const { return mCore; } + + static PX_FORCE_INLINE ShapeCore& getCore(PxsShapeCore& core) + { + size_t offset = PX_OFFSET_OF(ShapeCore, mCore); + return *reinterpret_cast(reinterpret_cast(&core) - offset); + } + + protected: + PxFilterData mQueryFilterData; // Query filter data PT: TODO: consider moving this to SceneQueryShapeData + PxFilterData mSimulationFilterData; // Simulation filter data + PxsShapeCore PX_ALIGN(16, mCore); + PxReal mRestOffset; // same as the API property of the same name + PxReal mTorsionalRadius; + PxReal mMinTorsionalPatchRadius; + }; + +} // namespace Sc + + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h new file mode 100644 index 000000000..198488be4 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h @@ -0,0 +1,78 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_STATIC_CORE +#define PX_PHYSICS_SCP_STATIC_CORE + +#include "ScRigidCore.h" +#include "PxvDynamics.h" + +namespace physx +{ +namespace Sc +{ + + class StaticSim; + + class StaticCore: public RigidCore + { + //= ATTENTION! ===================================================================================== + // Changing the data layout of this class breaks the binary serialization format. See comments for + // PX_BINARY_SERIAL_VERSION. If a modification is required, please adjust the getBinaryMetaData + // function. If the modification is made on a custom branch, please change PX_BINARY_SERIAL_VERSION + // accordingly. + //================================================================================================== + public: + StaticCore(const PxTransform& actor2World): RigidCore(PxActorType::eRIGID_STATIC) + { + mCore.body2World = actor2World; + mCore.mFlags = PxRigidBodyFlags(); + } + + PX_FORCE_INLINE const PxTransform& getActor2World() const { return mCore.body2World; } + void setActor2World(const PxTransform& actor2World); + + PX_FORCE_INLINE PxsRigidCore& getCore() { return mCore; } + + StaticCore(const PxEMPTY) : RigidCore(PxEmpty), mCore(PxEmpty) {} + static void getBinaryMetaData(PxOutputStream& stream); + + StaticSim* getSim() const; + + PX_FORCE_INLINE void onOriginShift(const PxVec3& shift) { mCore.body2World.p -= shift; } + private: + PxsRigidCore mCore; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp new file mode 100644 index 000000000..b6ac47aeb --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScActorCore.h" +#include "ScActorSim.h" +#include "ScShapeCore.h" +#include "ScShapeSim.h" +#include "ScBodySim.h" + +using namespace physx; + +Sc::ActorCore::ActorCore(PxActorType::Enum actorType, PxU8 actorFlags, PxClientID owner, PxDominanceGroup dominanceGroup) : + mSim (NULL), + mAggregateIDOwnerClient ((PxU32(owner)<<24)|0x00ffffff), + mActorFlags (actorFlags), + mActorType (PxU8(actorType)), + mDominanceGroup (dominanceGroup) +{ + PX_ASSERT((actorType & 0xff) == actorType); +} + +Sc::ActorCore::~ActorCore() +{ +} + +void Sc::ActorCore::setActorFlags(PxActorFlags af) +{ + const PxActorFlags old = mActorFlags; + if(af!=old) + { + mActorFlags = af; + + if(mSim) + mSim->postActorFlagChange(old, af); + } +} + +void Sc::ActorCore::setDominanceGroup(PxDominanceGroup g) +{ + PX_ASSERT(g<128); + mDominanceGroup = g; + if(mSim) + { + //force all related interactions to refresh, so they fetch new dominance values. + mSim->setActorsInteractionsDirty(InteractionDirtyFlag::eDOMINANCE, NULL, InteractionFlag::eRB_ELEMENT); + } +} + +void Sc::ActorCore::reinsertShapes() +{ + PX_ASSERT(mSim); + if(!mSim) + return; + + ElementSim* current = mSim->getElements_(); + while(current) + { + static_cast(current)->reinsertBroadPhase(); + current = current->mNextInActor; + } +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h b/src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h new file mode 100644 index 000000000..1d8b26fef --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h @@ -0,0 +1,217 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_COLLISION_ACTORPAIR +#define PX_COLLISION_ACTORPAIR + +#include "ScRigidSim.h" +#include "ScContactStream.h" +#include "ScNPhaseCore.h" + +namespace physx +{ +namespace Sc +{ + class ActorPairContactReportData + { + public: + ActorPairContactReportData() : + mStrmResetStamp (0xffffffff), + mActorAID (0xffffffff), + mActorBID (0xffffffff), + mPxActorA (NULL), + mPxActorB (NULL) + {} + + ContactStreamManager mContactStreamManager; + PxU32 mStrmResetStamp; + PxU32 mActorAID; + PxU32 mActorBID; + PxActor* mPxActorA; + PxActor* mPxActorB; + }; + + /** + \brief Class shared by all shape interactions for a pair of actors. + + This base class is used if no shape pair of an actor pair has contact reports requested. + */ + class ActorPair + { + public: + + enum ActorPairFlags + { + eIS_REPORT_PAIR = (1<<0), + eNEXT_FREE = (1<<1) + }; + + PX_FORCE_INLINE ActorPair() : mInternalFlags(0), mTouchCount(0), mRefCount(0) {} + PX_FORCE_INLINE ~ActorPair() {} + + PX_FORCE_INLINE Ps::IntBool isReportPair() const { return (mInternalFlags & eIS_REPORT_PAIR); } + + PX_FORCE_INLINE void incTouchCount() { mTouchCount++; PX_ASSERT(mTouchCount); } + PX_FORCE_INLINE void decTouchCount() { PX_ASSERT(mTouchCount); mTouchCount--; } + PX_FORCE_INLINE PxU32 getTouchCount() const { return mTouchCount; } + + PX_FORCE_INLINE void incRefCount() { ++mRefCount; PX_ASSERT(mRefCount>0); } + PX_FORCE_INLINE PxU32 decRefCount() { PX_ASSERT(mRefCount>0); return --mRefCount; } + PX_FORCE_INLINE PxU32 getRefCount() const { return mRefCount; } + + private: + ActorPair& operator=(const ActorPair&); + + protected: + PxU16 mInternalFlags; + PxU16 mTouchCount; + PxU16 mRefCount; + PxU16 mPad; // instances of this class are stored in a pool which needs an item size of at least size_t + }; + + /** + \brief Class shared by all shape interactions for a pair of actors if contact reports are requested. + + This class is used if at least one shape pair of an actor pair has contact reports requested. + + \note If a pair of actors had contact reports requested for some of the shape interactions but all of them switch to not wanting contact reports + any longer, then the ActorPairReport instance is kept being used and won't get replaced by a simpler ActorPair instance. + */ + class ActorPairReport : public ActorPair + { + public: + + enum ActorPairReportFlags + { + eIS_IN_CONTACT_REPORT_ACTOR_PAIR_SET = ActorPair::eNEXT_FREE // PT: whether the pair is already stored in the 'ContactReportActorPairSet' or not + }; + + PX_FORCE_INLINE ActorPairReport(RigidSim&, RigidSim&); + PX_FORCE_INLINE ~ActorPairReport(); + + PX_INLINE ContactStreamManager& createContactStreamManager(NPhaseCore&); + PX_FORCE_INLINE ContactStreamManager& getContactStreamManager() const { PX_ASSERT(mReportData); return mReportData->mContactStreamManager; } + PX_FORCE_INLINE RigidSim& getActorA() const { return mActorA; } + PX_FORCE_INLINE RigidSim& getActorB() const { return mActorB; } + PX_INLINE PxU32 getActorAID() const { PX_ASSERT(mReportData); return mReportData->mActorAID; } + PX_INLINE PxU32 getActorBID() const { PX_ASSERT(mReportData); return mReportData->mActorBID; } + PX_INLINE PxActor* getPxActorA() const { PX_ASSERT(mReportData); return mReportData->mPxActorA; } + PX_INLINE PxActor* getPxActorB() const { PX_ASSERT(mReportData); return mReportData->mPxActorB; } + PX_FORCE_INLINE bool streamResetNeeded(PxU32 cmpStamp) const; + PX_INLINE bool streamResetStamp(PxU32 cmpStamp); + + PX_FORCE_INLINE PxU16 isInContactReportActorPairSet() const { return PxU16(mInternalFlags & eIS_IN_CONTACT_REPORT_ACTOR_PAIR_SET); } + PX_FORCE_INLINE void setInContactReportActorPairSet() { mInternalFlags |= eIS_IN_CONTACT_REPORT_ACTOR_PAIR_SET; } + PX_FORCE_INLINE void clearInContactReportActorPairSet() { mInternalFlags &= ~eIS_IN_CONTACT_REPORT_ACTOR_PAIR_SET; } + + PX_FORCE_INLINE void createContactReportData(NPhaseCore&); + PX_FORCE_INLINE void releaseContactReportData(NPhaseCore&); + PX_FORCE_INLINE const ActorPairContactReportData* hasReportData() const { return mReportData; } + + PX_FORCE_INLINE void convert(ActorPair& aPair) { PX_ASSERT(!aPair.isReportPair()); mTouchCount = PxU16(aPair.getTouchCount()); mRefCount = PxU16(aPair.getRefCount()); } + + PX_FORCE_INLINE static ActorPairReport& cast(ActorPair& aPair) { PX_ASSERT(aPair.isReportPair()); return static_cast(aPair); } + + private: + ActorPairReport& operator=(const ActorPairReport&); + + RigidSim& mActorA; + RigidSim& mActorB; + + ActorPairContactReportData* mReportData; + }; + +} // namespace Sc + +PX_FORCE_INLINE Sc::ActorPairReport::ActorPairReport(RigidSim& actor0, RigidSim& actor1) : ActorPair(), +mActorA (actor0), +mActorB (actor1), +mReportData (NULL) +{ + PX_ASSERT(mInternalFlags == 0); + mInternalFlags = ActorPair::eIS_REPORT_PAIR; +} + +PX_FORCE_INLINE Sc::ActorPairReport::~ActorPairReport() +{ + PX_ASSERT(mReportData == NULL); +} + +PX_FORCE_INLINE bool Sc::ActorPairReport::streamResetNeeded(PxU32 cmpStamp) const +{ + return (cmpStamp != mReportData->mStrmResetStamp); +} + +PX_INLINE bool Sc::ActorPairReport::streamResetStamp(PxU32 cmpStamp) +{ + PX_ASSERT(mReportData); + const bool ret = streamResetNeeded(cmpStamp); + mReportData->mStrmResetStamp = cmpStamp; + return ret; +} + +PX_INLINE Sc::ContactStreamManager& Sc::ActorPairReport::createContactStreamManager(NPhaseCore& npCore) +{ + // Lazy create report data + if(!mReportData) + createContactReportData(npCore); + + return mReportData->mContactStreamManager; +} + +PX_FORCE_INLINE void Sc::ActorPairReport::createContactReportData(NPhaseCore& npCore) +{ + PX_ASSERT(!mReportData); + Sc::ActorPairContactReportData* reportData = npCore.createActorPairContactReportData(); + mReportData = reportData; + + if(reportData) + { + reportData->mActorAID = mActorA.getRigidID(); + reportData->mActorBID = mActorB.getRigidID(); + + reportData->mPxActorA = mActorA.getPxActor(); + reportData->mPxActorB = mActorB.getPxActor(); + } +} + +PX_FORCE_INLINE void Sc::ActorPairReport::releaseContactReportData(NPhaseCore& npCore) +{ + // Can't take the NPhaseCore (scene) reference from the actors since they're already gone on scene release + + if(mReportData) + { + npCore.releaseActorPairContactReportData(mReportData); + mReportData = NULL; + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp new file mode 100644 index 000000000..a58394641 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp @@ -0,0 +1,144 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "CmPhysXCommon.h" +#include "ScActorSim.h" +#include "ScActorCore.h" +#include "ScElementSim.h" +#include "ScScene.h" +#include "ScInteraction.h" + +using namespace physx; + +Sc::ActorSim::ActorSim(Scene& scene, ActorCore& core) : + mFirstElement (NULL), + mElementCount (0), + mScene (scene), + mCore (core) +{ + core.setSim(this); +} + +Sc::ActorSim::~ActorSim() +{ + mInteractions.releaseMem(*this); +} + +void Sc::ActorSim::registerInteractionInActor(Interaction* interaction) +{ + const PxU32 id = mInteractions.size(); + mInteractions.pushBack(interaction, *this); + interaction->setActorId(this, id); +} + +void Sc::ActorSim::unregisterInteractionFromActor(Interaction* interaction) +{ + const PxU32 i = interaction->getActorId(this); + PX_ASSERT(i < mInteractions.size()); + mInteractions.replaceWithLast(i); + if (isetActorId(this, i); +} + +void Sc::ActorSim::onElementAttach(ElementSim& element) +{ + element.mNextInActor = mFirstElement; + mFirstElement = &element; + mElementCount++; +} + +void Sc::ActorSim::onElementDetach(ElementSim& element) +{ + PX_ASSERT(mFirstElement); // PT: else we shouldn't be called + ElementSim* currentElem = mFirstElement; + ElementSim* previousElem = NULL; + while(currentElem) + { + if(currentElem==&element) + { + if(previousElem) + previousElem->mNextInActor = currentElem->mNextInActor; + else + mFirstElement = currentElem->mNextInActor; + mElementCount--; + return; + } + previousElem = currentElem; + currentElem = currentElem->mNextInActor; + } + PX_ASSERT(0); +} + +// PT: TODO: refactor with Sc::ParticlePacketShape::reallocInteractions - sschirm: particles are gone +void Sc::ActorSim::reallocInteractions(Sc::Interaction**& mem, PxU32& capacity, PxU32 size, PxU32 requiredMinCapacity) +{ + Interaction** newMem; + PxU32 newCapacity; + + if(requiredMinCapacity==0) + { + newCapacity = 0; + newMem = 0; + } + else if(requiredMinCapacity<=INLINE_INTERACTION_CAPACITY) + { + newCapacity = INLINE_INTERACTION_CAPACITY; + newMem = mInlineInteractionMem; + } + else + { + newCapacity = Ps::nextPowerOfTwo(requiredMinCapacity-1); + newMem = reinterpret_cast(mScene.allocatePointerBlock(newCapacity)); + } + + PX_ASSERT(newCapacity >= requiredMinCapacity && requiredMinCapacity>=size); + + if(mem) + { + PxMemCopy(newMem, mem, size*sizeof(Interaction*)); + + if(mem!=mInlineInteractionMem) + mScene.deallocatePointerBlock(reinterpret_cast(mem), capacity); + } + + capacity = newCapacity; + mem = newMem; +} + +void Sc::ActorSim::setActorsInteractionsDirty(InteractionDirtyFlag::Enum flag, const ActorSim* other, PxU8 interactionFlag) +{ + PxU32 size = getActorInteractionCount(); + Interaction** interactions = getActorInteractions(); + while(size--) + { + Interaction* interaction = *interactions++; + if((!other || other == &interaction->getActorSim0() || other == &interaction->getActorSim1()) && (interaction->readInteractionFlag(interactionFlag))) + interaction->setDirty(flag); + } +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h new file mode 100644 index 000000000..578695629 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h @@ -0,0 +1,124 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_ACTOR_SIM +#define PX_PHYSICS_SCP_ACTOR_SIM + +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "CmUtils.h" +#include "PxActor.h" +#include "ScInteractionFlags.h" +#include "ScActorCore.h" + +namespace physx +{ + +class PxActor; + +namespace Sc +{ + class Interaction; + class ElementSim; + + class ActorSim : public Ps::UserAllocated + { + friend class Scene; // the scene is allowed to set the scene array index + friend class Interaction; + PX_NOCOPY(ActorSim) + + public: + enum ActivityChangeInfoFlag + { + AS_PART_OF_CREATION = (1 << 0), + AS_PART_OF_ISLAND_GEN = (1 << 1) + }; + + ActorSim(Scene&, ActorCore&); + virtual ~ActorSim(); + + // Get the scene the actor resides in + PX_FORCE_INLINE Scene& getScene() const { return mScene; } + + // Get the number of interactions connected to the actor + PX_FORCE_INLINE PxU32 getActorInteractionCount() const { return mInteractions.size(); } + + // Get an iterator to the interactions connected to the actor + PX_FORCE_INLINE Interaction** getActorInteractions() const { return mInteractions.begin(); } + + // Get first element in the actor (linked list) + PX_FORCE_INLINE ElementSim* getElements_() { return mFirstElement; } + PX_FORCE_INLINE const ElementSim* getElements_() const { return mFirstElement; } + + PX_FORCE_INLINE PxU32 getElementCount() const { return mElementCount; } + + // Get the type ID of the actor + PX_FORCE_INLINE PxActorType::Enum getActorType() const { return mCore.getActorCoreType(); } + + // Returns true if the actor is a dynamic rigid body (including articulation links) + PX_FORCE_INLINE bool isDynamicRigid() const { const PxActorType::Enum type = getActorType(); return type == PxActorType::eRIGID_DYNAMIC || type == PxActorType::eARTICULATION_LINK; } + + void onElementAttach(ElementSim& element); + void onElementDetach(ElementSim& element); + + virtual void postActorFlagChange(PxU32, PxU32) {} + + void setActorsInteractionsDirty(InteractionDirtyFlag::Enum flag, const ActorSim* other, PxU8 interactionFlag); + + PX_FORCE_INLINE ActorCore& getActorCore() const { return mCore; } + + private: + //These are called from interaction creation/destruction + void registerInteractionInActor(Interaction* interaction); + void unregisterInteractionFromActor(Interaction* interaction); + + void reallocInteractions(Sc::Interaction**& mem, PxU32& capacity, PxU32 size, PxU32 requiredMinCapacity); + protected: + // dsequeira: interaction arrays are a major cause of small allocations, so we don't want to delegate them to the heap allocator + // it's not clear this inline array is really needed, we should take it out and see whether the cache perf is worse + + static const PxU32 INLINE_INTERACTION_CAPACITY = 4; + Interaction* mInlineInteractionMem[INLINE_INTERACTION_CAPACITY]; + + Cm::OwnedArray + mInteractions; + + ElementSim* mFirstElement; + PxU32 mElementCount; + + Scene& mScene; + + ActorCore& mCore; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp new file mode 100644 index 000000000..0f73f27bb --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp @@ -0,0 +1,384 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScArticulationCore.h" + +#include "PsFoundation.h" +#include "ScPhysics.h" +#include "ScBodyCore.h" +#include "ScBodySim.h" +#include "ScArticulationSim.h" +#include "DyArticulation.h" + +using namespace physx; + +Sc::ArticulationCore::ArticulationCore() : + mSim(NULL) +{ + const PxTolerancesScale& scale = Physics::getInstance().getTolerancesScale(); + + mCore.internalDriveIterations = 4; + mCore.externalDriveIterations = 4; + mCore.maxProjectionIterations = 4; + mCore.separationTolerance = 0.1f * scale.length; + mCore.solverIterationCounts = 1<<8 | 4; + mCore.sleepThreshold = 5e-5f * scale.speed * scale.speed; + mCore.wakeCounter = Physics::sWakeCounterOnCreation; + mCore.freezeThreshold = 5e-6f * scale.speed * scale.speed; +} + + +Sc::ArticulationCore::~ArticulationCore() +{ +} + + +//-------------------------------------------------------------- +// +// ArticulationCore interface implementation +// +//-------------------------------------------------------------- + +PxU32 Sc::ArticulationCore::getInternalDriveIterations() const +{ + return mCore.internalDriveIterations; +} + +void Sc::ArticulationCore::setInternalDriveIterations(const PxU32 v) +{ + mCore.internalDriveIterations = v; +} + +PxU32 Sc::ArticulationCore::getExternalDriveIterations() const +{ + return mCore.externalDriveIterations; +} + +void Sc::ArticulationCore::setExternalDriveIterations(const PxU32 v) +{ + mCore.externalDriveIterations = v; +} + +PxU32 Sc::ArticulationCore::getMaxProjectionIterations() const +{ + return mCore.maxProjectionIterations; +} + +void Sc::ArticulationCore::setMaxProjectionIterations(const PxU32 v) +{ + mCore.maxProjectionIterations = v; +} + +PxReal Sc::ArticulationCore::getSeparationTolerance() const +{ + return mCore.separationTolerance; +} + +void Sc::ArticulationCore::setSeparationTolerance(const PxReal v) +{ + mCore.separationTolerance = v; +} + +PxReal Sc::ArticulationCore::getWakeCounter() const +{ + return mCore.wakeCounter; +} + +void Sc::ArticulationCore::setWakeCounterInternal(const PxReal v) +{ + mCore.wakeCounter = v; +} + +void Sc::ArticulationCore::setWakeCounter(const PxReal v) +{ + mCore.wakeCounter = v; + +#ifdef _DEBUG + if(mSim) + mSim->debugCheckWakeCounterOfLinks(v); +#endif +} + +bool Sc::ArticulationCore::isSleeping() const +{ + return mSim ? mSim->isSleeping() : (mCore.wakeCounter == 0.0f); +} + +void Sc::ArticulationCore::wakeUp(PxReal wakeCounter) +{ + mCore.wakeCounter = wakeCounter; + +#ifdef _DEBUG + if(mSim) + mSim->debugCheckSleepStateOfLinks(false); +#endif +} + +void Sc::ArticulationCore::putToSleep() +{ + mCore.wakeCounter = 0.0f; + +#ifdef _DEBUG + if(mSim) + mSim->debugCheckSleepStateOfLinks(true); +#endif +} + +PxReal Sc::ArticulationCore::getSleepThreshold() const +{ + return mCore.sleepThreshold; +} + +void Sc::ArticulationCore::setSleepThreshold(const PxReal v) +{ + mCore.sleepThreshold = v; +} + +PxReal Sc::ArticulationCore::getFreezeThreshold() const +{ + return mCore.freezeThreshold; +} + +void Sc::ArticulationCore::setFreezeThreshold(const PxReal v) +{ + mCore.freezeThreshold = v; +} + +PxU16 Sc::ArticulationCore::getSolverIterationCounts() const +{ + return mCore.solverIterationCounts; +} + +void Sc::ArticulationCore::setSolverIterationCounts(const PxU16 v) +{ + mCore.solverIterationCounts = v; +} + + +PxArticulation* Sc::ArticulationCore::getPxArticulation() +{ + return gOffsetTable.convertScArticulation2Px(this); +} + + +const PxArticulation* Sc::ArticulationCore::getPxArticulation() const +{ + return gOffsetTable.convertScArticulation2Px(this); +} + +Sc::ArticulationDriveCache* Sc::ArticulationCore::createDriveCache(PxReal compliance, + PxU32 driveIterations) const +{ + return mSim? mSim->createDriveCache(compliance, driveIterations) : NULL; +} + + +void Sc::ArticulationCore::updateDriveCache(ArticulationDriveCache& cache, + PxReal compliance, + PxU32 driveIterations) const +{ + mSim->updateDriveCache(cache, compliance, driveIterations); +} + + +void Sc::ArticulationCore::releaseDriveCache(Sc::ArticulationDriveCache& driveCache) const +{ + if(mSim) + mSim->releaseDriveCache(driveCache); +} + + +PxU32 Sc::ArticulationCore::getCacheLinkCount(const ArticulationDriveCache& cache) const +{ + return Dy::PxvArticulationDriveCache::getLinkCount(cache); +} + +void Sc::ArticulationCore::applyImpulse(Sc::BodyCore& link, + const Sc::ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) +{ + if(mSim) + mSim->applyImpulse(link, driveCache, force, torque); +} + +void Sc::ArticulationCore::computeImpulseResponse(Sc::BodyCore& link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const Sc::ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const +{ + if(mSim) + mSim->computeImpulseResponse(link, linearResponse, angularResponse, driveCache, force, torque); +} + +PxU32 Sc::ArticulationCore::getDofs() const +{ + return mSim ? mSim->getDofs() : 0; +} + +PxArticulationCache* Sc::ArticulationCore::createCache() const +{ + return mSim ? mSim->createCache() : NULL; +} + +PxU32 Sc::ArticulationCore::getCacheDataSize() const +{ + return mSim ? mSim->getCacheDataSize() : 0; +} + +void Sc::ArticulationCore::zeroCache(PxArticulationCache& cache) const +{ + if (mSim) + mSim->zeroCache(cache); +} + +void Sc::ArticulationCore::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const +{ + if (mSim) + mSim->applyCache(cache, flag); +} + +void Sc::ArticulationCore::copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const +{ + if (mSim) + mSim->copyInternalStateToCache(cache, flag); +} + +void Sc::ArticulationCore::releaseCache(PxArticulationCache& cache) const +{ + if (mSim) + mSim->releaseCache(cache); +} + +void Sc::ArticulationCore::packJointData(const PxReal* maximum, PxReal* reduced) const +{ + if (mSim) + mSim->packJointData(maximum, reduced); +} + +void Sc::ArticulationCore::unpackJointData(const PxReal* reduced, PxReal* maximum) const +{ + if (mSim) + mSim->unpackJointData(reduced, maximum); +} + +void Sc::ArticulationCore::commonInit() const +{ + if (mSim) + mSim->commonInit(); +} + +void Sc::ArticulationCore::computeGeneralizedGravityForce(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeGeneralizedGravityForce(cache); +} + +void Sc::ArticulationCore::computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeCoriolisAndCentrifugalForce(cache); +} + +void Sc::ArticulationCore::computeGeneralizedExternalForce(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeGeneralizedExternalForce(cache); +} + +void Sc::ArticulationCore::computeJointAcceleration(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeJointAcceleration(cache); +} + +void Sc::ArticulationCore::computeJointForce(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeJointForce(cache); +} + +void Sc::ArticulationCore::computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeKinematicJacobian(linkID, cache); +} + + +void Sc::ArticulationCore::computeCoefficentMatrix(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeCoefficentMatrix(cache); +} + +bool Sc::ArticulationCore::computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxVec3 gravity, const PxU32 maxIter) const +{ + if (mSim) + { + return mSim->computeLambda(cache, initialState, jointTorque, gravity, maxIter); + } + + return false; +} + +void Sc::ArticulationCore::computeGeneralizedMassMatrix(PxArticulationCache& cache) const +{ + if (mSim) + mSim->computeGeneralizedMassMatrix(cache); +} + +PxU32 Sc::ArticulationCore::getCoefficentMatrixSize() const +{ + if (mSim) + return mSim->getCoefficentMatrixSize(); + + return 0; +} + +IG::NodeIndex Sc::ArticulationCore::getIslandNodeIndex() const +{ + if (mSim) + return mSim->getIslandNodeIndex(); + return IG::NodeIndex(IG_INVALID_NODE); +} + +void Sc::ArticulationCore::setGlobalPose() +{ + if (mSim) + mSim->setGlobalPose(); +} + +void Sc::ArticulationCore::setDirty(const bool dirty) +{ + if (mSim) + mSim->setDirty(dirty); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp new file mode 100644 index 000000000..f9a01b3b9 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp @@ -0,0 +1,300 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScArticulationJointCore.h" +#include "ScArticulationCore.h" +#include "ScArticulationJointSim.h" +#include "ScBodyCore.h" + + +using namespace physx; + +Sc::ArticulationJointCore::ArticulationJointCore(const PxTransform& parentFrame, + const PxTransform& childFrame, + PxArticulationBase::Enum type) : + mSim (NULL) +{ + PX_ASSERT(parentFrame.isValid()); + PX_ASSERT(childFrame.isValid()); + + mCore.parentPose = parentFrame; + mCore.childPose = childFrame; + + mCore.targetPosition = PxQuat(PxIdentity); + mCore.targetVelocity = PxVec3(0.f); + + mCore.driveType = PxArticulationJointDriveType::eTARGET; + + mCore.spring = 0.0f; + mCore.damping = 0.0f; + + mCore.internalCompliance = 1.0f; + mCore.externalCompliance = 1.0f; + + if (type == PxArticulationBase::eMaximumCoordinate) + { + PxReal swingYLimit = PxPi / 4; + PxReal swingZLimit = PxPi / 4; + mCore.limits[PxArticulationAxis::eSWING1].low = swingYLimit; + mCore.limits[PxArticulationAxis::eSWING1].high = swingYLimit; + mCore.limits[PxArticulationAxis::eSWING2].low = swingZLimit; + mCore.limits[PxArticulationAxis::eSWING2].high = swingZLimit; + mCore.swingLimitContactDistance = 0.05f; + + PxReal twistLimitLow = -PxPi / 4; + PxReal twistLimitHigh = PxPi / 4; + + mCore.limits[PxArticulationAxis::eTWIST].low = twistLimitLow; + mCore.limits[PxArticulationAxis::eTWIST].high = twistLimitHigh; + mCore.twistLimitContactDistance = 0.05f; + mCore.tanQSwingY = PxTan(swingYLimit / 4); + mCore.tanQSwingZ = PxTan(swingZLimit / 4); + mCore.tanQSwingPad = PxTan(mCore.swingLimitContactDistance / 4); + + mCore.tanQTwistHigh = PxTan(twistLimitHigh / 4); + mCore.tanQTwistLow = PxTan(twistLimitLow / 4); + mCore.tanQTwistPad = PxTan(mCore.twistLimitContactDistance / 4); + } + else + { + + for (PxU32 i = 0; i < 6; ++i) + { + mCore.limits[i].low = 0.f; + mCore.limits[i].high = 0.f; + mCore.drives[i].stiffness = 0.f; + mCore.drives[i].damping = 0.f; + mCore.drives[i].maxForce = 0.f; + mCore.targetP[i] = 0.f; + mCore.targetV[i] = 0.f; + } + + mCore.twistLimitContactDistance = 0.f; + mCore.tanQSwingY = 0.f; + mCore.tanQSwingZ = 0.f; + mCore.tanQSwingPad = 0.f; + + mCore.tanQTwistHigh = 0.f; + mCore.tanQTwistLow = 0.f; + mCore.tanQTwistPad = 0.f; + } + + mCore.swingLimited = false; + mCore.twistLimited = false; + mCore.prismaticLimited = false; + mCore.tangentialStiffness = 0.0f; + mCore.tangentialDamping = 0.0f; + + mCore.frictionCoefficient = 0.05f; + + mCore.jointType = PxArticulationJointType::eUNDEFINED; + + for(PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) + mCore.motion[i] = PxArticulationMotion::eLOCKED; + +} + + +Sc::ArticulationJointCore::~ArticulationJointCore() +{ + PX_ASSERT(getSim() == 0); +} + + +//-------------------------------------------------------------- +// +// ArticulationJointCore interface implementation +// +//-------------------------------------------------------------- + + +void Sc::ArticulationJointCore::setParentPose(const PxTransform& t) +{ + mCore.parentPose = t; + mCore.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::ePOSE; + mArticulation->setDirty(true); +} + + +void Sc::ArticulationJointCore::setChildPose(const PxTransform& t) +{ + mCore.childPose = t; + mCore.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::ePOSE; + mArticulation->setDirty(true); +} + + +void Sc::ArticulationJointCore::setTargetOrientation(const PxQuat& p) +{ + mCore.targetPosition = p; +} + +void Sc::ArticulationJointCore::setTargetVelocity(const PxVec3& v) +{ + mCore.targetVelocity = v; +} + +void Sc::ArticulationJointCore::setDriveType(PxArticulationJointDriveType::Enum type) +{ + mCore.driveType = PxU8(type); +} + +void Sc::ArticulationJointCore::setJointType(PxArticulationJointType::Enum type) +{ + mCore.jointType = PxU8(type); +} + +PxArticulationJointType::Enum Sc::ArticulationJointCore::getJointType() const +{ + return PxArticulationJointType::Enum(mCore.jointType); +} + +void Sc::ArticulationJointCore::setMotion(PxArticulationAxis::Enum axis, PxArticulationMotion::Enum motion) +{ + mCore.motion[axis] = motion; + mCore.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::eMOTION; + mArticulation->setDirty(true); +} + +PxArticulationMotion::Enum Sc::ArticulationJointCore::getMotion(PxArticulationAxis::Enum axis) const +{ + return PxArticulationMotion::Enum(PxU8(mCore.motion[axis])); +} + +void Sc::ArticulationJointCore::setFrictionCoefficient(const PxReal coefficient) +{ + mCore.frictionCoefficient = coefficient; +} + +PxReal Sc::ArticulationJointCore::getFrictionCoefficient() const +{ + return mCore.frictionCoefficient; +} + +void Sc::ArticulationJointCore::setMaxJointVelocity(const PxReal maxJointV) +{ + mCore.maxJointVelocity = maxJointV; +} + +PxReal Sc::ArticulationJointCore::getMaxJointVelocity() const +{ + return mCore.maxJointVelocity; +} + +void Sc::ArticulationJointCore::setStiffness(PxReal s) +{ + mCore.spring = s; +} + + +void Sc::ArticulationJointCore::setDamping(PxReal d) +{ + mCore.damping = d; +} + + +void Sc::ArticulationJointCore::setInternalCompliance(PxReal r) +{ + mCore.internalCompliance = r; +} + +void Sc::ArticulationJointCore::setExternalCompliance(PxReal r) +{ + mCore.externalCompliance = r; +} + + +void Sc::ArticulationJointCore::setSwingLimit(PxReal yLimit, PxReal zLimit) +{ + mCore.limits[PxArticulationAxis::eSWING1].low = yLimit; + mCore.limits[PxArticulationAxis::eSWING2].low = zLimit; + + mCore.tanQSwingY = PxTan(yLimit/4); + mCore.tanQSwingZ = PxTan(zLimit/4); +} + + +void Sc::ArticulationJointCore::setTangentialStiffness(PxReal s) +{ + mCore.tangentialStiffness = s; +} + + +void Sc::ArticulationJointCore::setTangentialDamping(PxReal d) +{ + mCore.tangentialDamping = d; +} + + +void Sc::ArticulationJointCore::setSwingLimitEnabled(bool e) +{ + mCore.swingLimited = e; +} + +void Sc::ArticulationJointCore::setSwingLimitContactDistance(PxReal e) +{ + mCore.swingLimitContactDistance = e; + mCore.tanQSwingPad = PxTan(e/4); +} + + +void Sc::ArticulationJointCore::setTwistLimit(PxReal lower, PxReal upper) +{ + mCore.limits[PxArticulationAxis::eTWIST].low = lower; + mCore.limits[PxArticulationAxis::eTWIST].high = upper; + + mCore.tanQTwistHigh = PxTan(upper/4); + mCore.tanQTwistLow = PxTan(lower/4); +} + +void Sc::ArticulationJointCore::setTwistLimitEnabled(bool e) +{ + mCore.twistLimited = e; +} + +void Sc::ArticulationJointCore::setTwistLimitContactDistance(PxReal e) +{ + mCore.twistLimitContactDistance = e; + mCore.tanQTwistPad = PxTan(e/4); +} + +void Sc::ArticulationJointCore::setTargetP(PxArticulationAxis::Enum axis, PxReal targetP) +{ + mCore.targetP[axis] = targetP; + mCore.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::eTARGETPOSE; + mArticulation->setDirty(true); +} + +void Sc::ArticulationJointCore::setTargetV(PxArticulationAxis::Enum axis, PxReal targetV) +{ + mCore.targetV[axis] = targetV; + mCore.dirtyFlag |= Dy::ArticulationJointCoreDirtyFlag::eTARGETVELOCITY; + mArticulation->setDirty(true); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp new file mode 100644 index 000000000..a0127f707 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScArticulationJointSim.h" +#include "ScArticulationJointCore.h" +#include "ScBodySim.h" +#include "ScScene.h" +#include "PxsRigidBody.h" +#include "DyArticulation.h" +#include "ScArticulationSim.h" +#include "PxsSimpleIslandManager.h" + +using namespace physx; + +Sc::ArticulationJointSim::ArticulationJointSim(ArticulationJointCore& joint, ActorSim& parent, ActorSim& child) : + Interaction (parent, child, InteractionType::eARTICULATION, 0), + mCore (joint) +{ + registerInActors(); + + BodySim& childBody = static_cast(child), + & parentBody = static_cast(parent); + + parentBody.getArticulation()->addBody(childBody, &parentBody, this); + + mCore.setSim(this); +} + +Sc::ArticulationJointSim::~ArticulationJointSim() +{ + // articulation interactions do not make use of the dirty flags yet. If they did, a setClean(true) has to be introduced here. + PX_ASSERT(!readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)); + PX_ASSERT(!getDirtyFlags()); + + unregisterFromActors(); + + BodySim& child = getChild(); + child.getArticulation()->removeBody(child); + + mCore.setSim(NULL); +} + +Sc::BodySim& Sc::ArticulationJointSim::getParent() const +{ + return static_cast(getActorSim0()); +} + +Sc::BodySim& Sc::ArticulationJointSim::getChild() const +{ + return static_cast(getActorSim1()); +} + +bool Sc::ArticulationJointSim::onActivate_(void*) +{ + if(!(getParent().isActive() && getChild().isActive())) + return false; + + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; +} + +bool Sc::ArticulationJointSim::onDeactivate_() +{ + clearInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h new file mode 100644 index 000000000..7924b3c18 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h @@ -0,0 +1,71 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_ARTICULATION_JOINT_SIM +#define PX_PHYSICS_SCP_ARTICULATION_JOINT_SIM + +#include "ScInteraction.h" +#include "DyArticulation.h" + +namespace physx +{ +namespace Sc +{ + class ArticulationJointCore; + class BodySim; + + class ArticulationJointSim : public Interaction + { + ArticulationJointSim& operator=(const ArticulationJointSim &); + + public: + ArticulationJointSim(ArticulationJointCore& joint, ActorSim& parent, ActorSim& child); + ~ArticulationJointSim(); + + bool onActivate_(void*); + bool onDeactivate_(); + + PX_FORCE_INLINE ArticulationJointCore& getCore() const { return mCore; } + + BodySim& getParent() const; + BodySim& getChild() const; + + //--------------------------------------------------------------------------------- + // Low Level data access + //--------------------------------------------------------------------------------- + private: + ArticulationJointCore& mCore; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp new file mode 100644 index 000000000..d89b6ce2e --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp @@ -0,0 +1,813 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScArticulationSim.h" +#include "ScArticulationCore.h" +#include "ScArticulationJointSim.h" +#include "ScArticulationJointCore.h" +#include "ScBodySim.h" +#include "ScConstraintSim.h" +#include "ScScene.h" + +#include "DyArticulation.h" +#include "DyConstraint.h" +#include "DyFeatherstoneArticulation.h" +#include "PxsContext.h" +#include "CmSpatialVector.h" +#include "PsVecMath.h" +#include "PxsSimpleIslandManager.h" +#include "ScShapeSim.h" + +using namespace physx; +using namespace physx::Dy; + + +Sc::ArticulationSim::ArticulationSim(ArticulationCore& core, Scene& scene, BodyCore& root) : + mLLArticulation(NULL), + mScene(scene), + mCore(core), + mLinks (PX_DEBUG_EXP("ScArticulationSim::links")), + mBodies (PX_DEBUG_EXP("ScArticulationSim::bodies")), + mJoints (PX_DEBUG_EXP("ScArticulationSim::joints")), + mMaxDepth(0) +{ + mLinks.reserve(16); + mJoints.reserve(16); + mBodies.reserve(16); + + mLLArticulation = mScene.createLLArticulation(this); + + + mIslandNodeIndex = scene.getSimpleIslandManager()->addArticulation(this, mLLArticulation, false); + + if(!mLLArticulation) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Articulation: could not allocate low-level resources."); + return; + } + + mLLArticulation->setDirty(true); + + PX_ASSERT(root.getSim()); + + addBody(*root.getSim(), NULL, NULL); + + + + mCore.setSim(this); + + mLLArticulation->setDyContext(mScene.getDynamicsContext()); + mLLArticulation->getSolverDesc().core = &core.getCore(); + mLLArticulation->getSolverDesc().internalLoads = NULL; + mLLArticulation->getSolverDesc().externalLoads = NULL; + mLLArticulation->getSolverDesc().articulation = NULL; + mLLArticulation->getSolverDesc().poses = NULL; + mLLArticulation->getSolverDesc().motionVelocity = NULL; + mLLArticulation->getSolverDesc().acceleration = NULL; + mLLArticulation->getSolverDesc().totalDataSize = 0; + mLLArticulation->getSolverDesc().solverDataSize = 0; + mLLArticulation->getSolverDesc().linkCount = 0; + mLLArticulation->getSolverDesc().scratchMemory = NULL; + mLLArticulation->getSolverDesc().scratchMemorySize = 0; + + //mLLArticulation->onUpdateSolverDesc(); +} + + +Sc::ArticulationSim::~ArticulationSim() +{ + if (!mLLArticulation) + return; + + mScene.destroyLLArticulation(*mLLArticulation); + + mScene.getSimpleIslandManager()->removeNode(mIslandNodeIndex); + + mCore.setSim(NULL); +} + +PxU32 Sc::ArticulationSim::findBodyIndex(BodySim& body) const +{ + for(PxU32 i=0; igetBody(0); + BodySim* bodySim1 = constraintSim->getBody(1); + + ArticulationLoopConstraint lConstraint; + if (bodySim0) + lConstraint.linkIndex0 = findBodyIndex(*bodySim0); + else + lConstraint.linkIndex0 = 0x80000000; + + if(bodySim1) + lConstraint.linkIndex1 = findBodyIndex(*bodySim1); + else + lConstraint.linkIndex1 = 0x80000000; + + lConstraint.constraint = &constraintSim->getLowLevelConstraint(); + + mLoopConstraints.pushBack(lConstraint); +} + +void Sc::ArticulationSim::removeLoopConstraint(ConstraintSim* constraintSim) +{ + Dy::Constraint* constraint = &constraintSim->getLowLevelConstraint(); + + const PxU32 size = mLoopConstraints.size(); + PxU32 index = 0; + while (index < size && mLoopConstraints[index].constraint != constraint) + ++index; + + if (index != size) + mLoopConstraints.replaceWithLast(index); +} + +void Sc::ArticulationSim::updateCached(Cm::BitMapPinned* shapeChangedMap) +{ + for(PxU32 i=0; iupdateCached(shapeChangedMap); +} + +void Sc::ArticulationSim::markShapesUpdated(Cm::BitMapPinned* shapeChangedMap) +{ + for (PxU32 a = 0; a < mBodies.size(); ++a) + { + Sc::ElementSim* current = mBodies[a]->getElements_(); + while (current) + { + Sc::ShapeSim* sim = static_cast(current); + if (sim->isInBroadPhase()) + shapeChangedMap->growAndSet(sim->getElementID()); + current = current->mNextInActor; + } + } +} + +void Sc::ArticulationSim::updateContactDistance(PxReal* contactDistance, const PxReal dt, Bp::BoundsArray& boundsArray) +{ + for (PxU32 i = 0; iupdateContactDistance(contactDistance, dt, boundsArray); +} + +ArticulationLinkHandle Sc::ArticulationSim::getLinkHandle(BodySim &body) const +{ + return reinterpret_cast(mLLArticulation) | findBodyIndex(body); +} + +void Sc::ArticulationSim::addBody(BodySim& body, + BodySim* parent, + ArticulationJointSim* joint) +{ + mBodies.pushBack(&body); + mJoints.pushBack(joint); + mLLArticulation->addBody(); + + PxU32 index = mLinks.size(); + + PX_ASSERT((((index==0) && (joint == 0)) && (parent == 0)) || + (((index!=0) && joint) && (parent && (parent->getArticulation() == this)))); + + ArticulationLink &link = mLinks.insert(); + link.body = &body.getLowLevelBody(); + link.bodyCore = &body.getBodyCore().getCore(); + link.children = 0; + bool shouldSleep; + bool currentlyAsleep; + bool bodyReadyForSleep = body.checkSleepReadinessBesidesWakeCounter(); + PxReal wakeCounter = getCore().getWakeCounter(); + + if(parent) + { + currentlyAsleep = !mBodies[0]->isActive(); + shouldSleep = currentlyAsleep && bodyReadyForSleep; + + PxU32 parentIndex = findBodyIndex(*parent); + link.parent = parentIndex; + link.pathToRoot = mLinks[parentIndex].pathToRoot | ArticulationBitField(1)<getCore().getCore(); + mLinks[parentIndex].children |= ArticulationBitField(1)<> 32); + const PxU32 depth = Ps::bitCount(low) + Ps::bitCount(high); + mMaxDepth = PxMax(depth, mMaxDepth); + + mLLArticulation->setMaxDepth(mMaxDepth); + + if (currentlyAsleep && (!shouldSleep)) + { + for(PxU32 i=0; i < (mBodies.size() - 1); i++) + mBodies[i]->internalWakeUpArticulationLink(wakeCounter); + } + + body.setArticulation(this, wakeCounter, shouldSleep, index); +} + + +void Sc::ArticulationSim::removeBody(BodySim &body) +{ + PX_ASSERT(body.getArticulation() == this); + PxU32 index = findBodyIndex(body); + body.setArticulation(NULL, 0.0f, true, 0); + + ArticulationLink &link0 = mLinks[index]; + + PX_ASSERT(link0.children == 0); + PX_UNUSED(link0); + + // copy all the later links down by one + for(PxU32 i=index+1;iindex) + link.pathToRoot = (link.pathToRoot&fixedIndices) | (link.pathToRoot&shiftIndices)>>1; + link.children = (link.children&fixedIndices) | (link.children&shiftIndices)>>1; + + const PxU32 low = PxU32(link.pathToRoot & 0xffffffff); + const PxU32 high = PxU32(link.pathToRoot >> 32); + const PxU32 depth = Ps::bitCount(low) + Ps::bitCount(high); + mMaxDepth = PxMax(depth, mMaxDepth); + } + + mLinks.popBack(); + + mLLArticulation->setMaxDepth(mMaxDepth); + mLLArticulation->removeBody(); +} + + +void Sc::ArticulationSim::checkResize() const +{ + if(!mBodies.size()) + return; + + //if this is needed, we need to re-allocated the link data + mLLArticulation->resize(mLinks.size()); + + mLLArticulation->getSolverDesc().links = const_cast(mLinks.begin()); + mLLArticulation->getSolverDesc().linkCount = Ps::to8(mLinks.size()); + + //if this is needed, we need to re-allocated the joint data + mLLArticulation->onUpdateSolverDesc(); +} + +PxU32 Sc::ArticulationSim::getCCDLinks(BodySim** sims) +{ + PxU32 nbCCDBodies = 0; + for (PxU32 a = 0; a < mBodies.size(); ++a) + { + if (mBodies[a]->getLowLevelBody().getCore().mFlags & PxRigidBodyFlag::eENABLE_CCD) + { + sims[nbCCDBodies++] = mBodies[a]; + } + } + return nbCCDBodies; +} + +void Sc::ArticulationSim::sleepCheck(PxReal dt) +{ + if(!mBodies.size()) + return; + +#if PX_CHECKED + { + PxReal maxTimer = 0.0f, minTimer = PX_MAX_F32; + bool allActive = true, noneActive = true; + for(PxU32 i=0;igetBodyCore().getWakeCounter(); + maxTimer = PxMax(maxTimer, timer); + minTimer = PxMin(minTimer, timer); + bool active = mBodies[i]->isActive(); + allActive &= active; + noneActive &= !active; + } + // either all links are asleep, or no links are asleep + PX_ASSERT(maxTimer==0 || minTimer!=0); + PX_ASSERT(allActive || noneActive); + } + +#endif + + if(!mBodies[0]->isActive()) + return; + + PxReal sleepThreshold = getCore().getCore().sleepThreshold; + + PxReal maxTimer = 0.0f, minTimer = PX_MAX_F32; + + for(PxU32 i=0;igetMotionVelocity(i); + PxReal timer = mBodies[i]->updateWakeCounter(dt, sleepThreshold, motionVelocity); + maxTimer = PxMax(maxTimer, timer); + minTimer = PxMin(minTimer, timer); + } + + mCore.setWakeCounterInternal(maxTimer); + + if(maxTimer != 0.0f) + { + if(minTimer == 0.0f) + { + // make sure nothing goes to sleep unless everything does + for(PxU32 i=0;igetBodyCore().setWakeCounterFromSim(PxMax(1e-6f, mBodies[i]->getBodyCore().getWakeCounter())); + } + return; + } + + for(PxU32 i=0;inotifyReadyForSleeping(); + mBodies[i]->resetSleepFilter(); + } + + mScene.getSimpleIslandManager()->deactivateNode(mIslandNodeIndex); +} + +bool Sc::ArticulationSim::isSleeping() const +{ + bool isActive = mBodies[0]->isActive(); + PX_UNUSED(isActive); + return (mBodies.size() > 0) ? (!mBodies[0]->isActive()) : true; +} + +void Sc::ArticulationSim::internalWakeUp(PxReal wakeCounter) +{ + if(mCore.getWakeCounter() < wakeCounter) + { + mCore.setWakeCounterInternal(wakeCounter); + for(PxU32 i=0;iinternalWakeUpArticulationLink(wakeCounter); + } +} + +void Sc::ArticulationSim::setActive(const bool b, const PxU32 infoFlag) +{ + for(PxU32 i=0;isetActive(b, infoFlag); + } +} + +void Sc::ArticulationSim::updateForces(PxReal dt, bool simUsesAdaptiveForce) +{ + PxU32 count = 0; + + + for(PxU32 i=0;igetType(); + const bool useAccelerations = (type == PxArticulationBase::Enum::eReducedCoordinate); + + mBodies[i]->updateForces(dt, NULL, NULL, count, &mLLArticulation->getSolverDesc().acceleration[i], + useAccelerations, simUsesAdaptiveForce); + } +} + +void Sc::ArticulationSim::saveLastCCDTransform() +{ + for(PxU32 i=0;igetLowLevelBody().saveLastCCDTransform(); + } +} + + +Sc::ArticulationDriveCache* Sc::ArticulationSim::createDriveCache(PxReal compliance, + PxU32 driveIterations) const +{ + + checkResize(); + PxU32 solverDataSize, totalSize, scratchSize; + getLowLevelArticulation()->getDataSizes(mLinks.size(), solverDataSize, totalSize, scratchSize); + + // In principle we should only need solverDataSize here. But right now prepareFsData generates the auxiliary data + // for use in potential debugging, which takes up extra space. + FsData* data = reinterpret_cast(PX_ALLOC(totalSize,"Articulation Drive Cache")); + PxvArticulationDriveCache::initialize(*data, Ps::to16(mLinks.size()), mLinks.begin(), compliance, driveIterations, mLLArticulation->getSolverDesc().scratchMemory, mLLArticulation->getSolverDesc().scratchMemorySize); + + return data; +} + + +void Sc::ArticulationSim::updateDriveCache(ArticulationDriveCache& cache, + PxReal compliance, + PxU32 driveIterations) const +{ + checkResize(); + PxvArticulationDriveCache::initialize(cache, Ps::to16(mLinks.size()), mLinks.begin(), compliance, driveIterations, + mLLArticulation->getSolverDesc().scratchMemory, mLLArticulation->getSolverDesc().scratchMemorySize); +} + + +void Sc::ArticulationSim::releaseDriveCache(Sc::ArticulationDriveCache& driveCache) const +{ + PX_FREE(&driveCache); +} + + +void Sc::ArticulationSim::applyImpulse(Sc::BodyCore& link, + const Sc::ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) +{ + Cm::SpatialVectorV v[DY_ARTICULATION_MAX_SIZE], z[DY_ARTICULATION_MAX_SIZE]; + PxMemZero(z, mLinks.size()*sizeof(Cm::SpatialVector)); + PxMemZero(v, mLinks.size()*sizeof(Cm::SpatialVector)); + + PxU32 bodyIndex = findBodyIndex(*link.getSim()); + z[bodyIndex].linear = Ps::aos::V3LoadU(-force); + z[bodyIndex].angular = Ps::aos::V3LoadU(-torque); + + PxvArticulationDriveCache::applyImpulses(driveCache, z, v); + for(PxU32 i=0;igetBodyCore(); + PxVec3 lv, av; + Ps::aos::V3StoreU(v[i].linear, lv); + Ps::aos::V3StoreU(v[i].angular, av); + + body.setLinearVelocity(body.getLinearVelocity()+lv); + body.setAngularVelocity(body.getAngularVelocity()+av); + } +} + +void Sc::ArticulationSim::computeImpulseResponse(Sc::BodyCore& link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const Sc::ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const +{ + Cm::SpatialVectorV v; + PxvArticulationDriveCache::getImpulseResponse(driveCache, findBodyIndex(*link.getSim()), Cm::SpatialVectorV(Ps::aos::V3LoadU(force), Ps::aos::V3LoadU(torque)), v); + Ps::aos::V3StoreU(v.linear, linearResponse); + Ps::aos::V3StoreU(v.angular, angularResponse); +} + +PxU32 Sc::ArticulationSim::getDofs() const +{ + return mLLArticulation->getDofs(); +} + +PxU32 Sc::ArticulationSim::getDof(const PxU32 linkID) const +{ + return mLLArticulation->getDof(linkID); +} + +PxArticulationCache* Sc::ArticulationSim::createCache() const +{ + + checkResize(); + + PxU32 totalSize = getCacheDataSize() + sizeof(PxArticulationCache); + + PxU8* tCache = reinterpret_cast(PX_ALLOC(totalSize, "Articulation cache")); + + PxMemZero(tCache, totalSize); + + const PxU32 totalDofs = mLLArticulation->getDofs(); + + PxArticulationCache* cache = reinterpret_cast(tCache); + + PxU32 offset = sizeof(PxArticulationCache); + cache->externalForces = reinterpret_cast(tCache + offset); + + offset += sizeof(Cm::SpatialVector) * mLinks.size(); + + cache->jacobian = reinterpret_cast(tCache + offset); + + offset += sizeof(PxReal) * totalDofs * 6;//maximum 6 columns for coefficent matrix + cache->massMatrix = reinterpret_cast(tCache + offset); + + offset += sizeof(PxReal) *totalDofs * totalDofs; + cache->jointVelocity = reinterpret_cast(tCache + offset); + + offset += sizeof(PxReal) * totalDofs; + cache->jointAcceleration = reinterpret_cast(tCache + offset); + + offset += sizeof(PxReal) * totalDofs; + cache->jointPosition = reinterpret_cast(tCache + offset); + + offset += sizeof(PxReal) * totalDofs; + cache->jointForce = reinterpret_cast(tCache + offset); + + offset += sizeof(void*); + cache->coefficentMatrix = reinterpret_cast(tCache + offset); + + offset += sizeof(void*); + cache->lambda = reinterpret_cast(tCache + offset); + + const PxU32 scratchMemorySize = getScratchMemorySize(); + void* scratchMemory = PX_ALLOC(scratchMemorySize, "Cache scratch memory"); + cache->scratchMemory = scratchMemory; + + cache->scratchAllocator = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxcScratchAllocator), "PxScrachAllocator"), PxcScratchAllocator)(); + + reinterpret_cast(cache->scratchAllocator)->setBlock(scratchMemory, scratchMemorySize); + + return cache; +} + +PxU32 Sc::ArticulationSim::getCacheDataSize() const +{ + const PxU32 totalDofs = mLLArticulation->getDofs(); + + const PxU32 jointCount = mLinks.size() - 1; + PxU32 totalSize = + sizeof(Cm::SpatialVector) * mLinks.size() //external force + + sizeof(PxKinematicJacobian) * jointCount //jacobian matrix + + sizeof(PxReal) * totalDofs * totalDofs //mass matrix + + sizeof(PxReal) * totalDofs * 4 //jointVelocity, jointAcceleration, jointPosition, joint force + + sizeof(void*) * 2 //pointer to coefficent matrix and pointer to lambda vector + + sizeof(PxArticulationRootLinkData) //root link data + + sizeof(void*) * 2 //pointer to scratch memory/scratch memory allocator + + sizeof(PxU32); //version + + return totalSize; +} + +PxU32 Sc::ArticulationSim::getCacheConstantDataSize() const +{ + PxU32 totalSize = sizeof(void*) * 2 + sizeof(PxU32); //pointer to scratch memory & scrach memory allocator, version + + return totalSize; +} + +PxU32 Sc::ArticulationSim::getScratchMemorySize() const +{ + const PxU32 totalDofs = mLLArticulation->getDofs(); + const PxU32 linkCount = mLinks.size(); + + PxU32 totalSize = + sizeof(Cm::SpatialVectorF) * linkCount * 5 //motionVelocity, motionAccelerations, coriolisVectors, spatialZAVectors, externalAccels; + + sizeof(Dy::SpatialMatrix) * linkCount //compositeSpatialInertias; + + sizeof(PxReal) * totalDofs * 5; //jointVelocity, jointAcceleration, jointForces, jointPositions, jointFrictionForces + + return totalSize; +} + + +void Sc::ArticulationSim::zeroCache(PxArticulationCache& cache) const +{ + const PxU32 cacheDataSize = getCacheDataSize(); + const PxU32 constantSize = getCacheConstantDataSize(); + + //don't zero gravity, dt and version + PxMemZero(cache.externalForces, (cacheDataSize - constantSize)); +} + +//copy external data to internal data +void Sc::ArticulationSim::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const +{ + //checkResize(); + mLLArticulation->applyCache(cache, flag); +} + +//copy internal data to external data +void Sc::ArticulationSim::copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const +{ + mLLArticulation->copyInternalStateToCache(cache, flag); +} + +//release cache +void Sc::ArticulationSim::releaseCache(PxArticulationCache& cache) const +{ + if (cache.scratchAllocator) + { + PxcScratchAllocator* scratchAlloc = reinterpret_cast(cache.scratchAllocator); + scratchAlloc->~PxcScratchAllocator(); + PX_FREE_AND_RESET(cache.scratchAllocator); + } + + if (cache.scratchMemory) + PX_FREE_AND_RESET(cache.scratchMemory); + + PX_FREE(&cache); +} + +void Sc::ArticulationSim::packJointData(const PxReal* maximum, PxReal* reduced) const +{ + mLLArticulation->packJointData(maximum, reduced); +} + +void Sc::ArticulationSim::unpackJointData(const PxReal* reduced, PxReal* maximum) const +{ + mLLArticulation->unpackJointData(reduced, maximum); +} + +void Sc::ArticulationSim::commonInit() +{ + mLLArticulation->initializeCommonData(); +} + +void Sc::ArticulationSim::computeGeneralizedGravityForce(PxArticulationCache& cache) +{ + mLLArticulation->getGeneralizedGravityForce(mScene.getGravityFast(), cache); +} + +void Sc::ArticulationSim::computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) +{ + mLLArticulation->getCoriolisAndCentrifugalForce(cache); +} + +void Sc::ArticulationSim::computeGeneralizedExternalForce(PxArticulationCache& cache) +{ + mLLArticulation->getGeneralizedExternalForce(cache); +} + +void Sc::ArticulationSim::computeJointAcceleration(PxArticulationCache& cache) +{ + mLLArticulation->getJointAcceleration(mScene.getGravityFast(), cache); +} + +void Sc::ArticulationSim::computeJointForce(PxArticulationCache& cache) +{ + mLLArticulation->getJointForce(cache); +} + +void Sc::ArticulationSim::computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) +{ + mLLArticulation->getKinematicJacobian(linkID, cache); +} + +void Sc::ArticulationSim::computeCoefficentMatrix(PxArticulationCache& cache) +{ + mLLArticulation->getCoefficentMatrixWithLoopJoints(mLoopConstraints.begin(), mLoopConstraints.size(), cache); +} + +bool Sc::ArticulationSim::computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, + const PxReal* const jointTorque, const PxVec3 gravity, const PxU32 maxIter) +{ + return mLLArticulation->getLambda(mLoopConstraints.begin(), mLoopConstraints.size(), cache, initialState, jointTorque, gravity, maxIter); + +} + +void Sc::ArticulationSim::computeGeneralizedMassMatrix(PxArticulationCache& cache) +{ + + mLLArticulation->getGeneralizedMassMatrixCRB(cache); + + + /*const PxU32 totalDofs = mLLArticulation->getDofs(); + + PxReal* massMatrix = reinterpret_cast(PX_ALLOC(sizeof(PxReal) * totalDofs * totalDofs, "MassMatrix")); + PxMemCopy(massMatrix, cache.massMatrix, sizeof(PxReal)*totalDofs * totalDofs); + + mLLArticulation->getGeneralizedMassMatrix(cache); + + PxReal* massMatrix1 = cache.massMatrix; + for (PxU32 i = 0; i < totalDofs; ++i) + { + PxReal* row = &massMatrix1[i * totalDofs]; + + for (PxU32 j = 0; j < totalDofs; ++j) + { + const PxReal dif = row[j] - massMatrix[j*totalDofs + i]; + if (PxAbs(dif) > 2e-4f) + { + int bob = 0; + PX_UNUSED(bob); + } + } + + } + + PX_FREE(massMatrix);*/ + +} + +PxU32 Sc::ArticulationSim::getCoefficentMatrixSize() const +{ + const PxU32 size = mLoopConstraints.size(); + const PxU32 totalDofs = mLLArticulation->getDofs(); + return sizeof(PxReal) * size * totalDofs; +} + +// This method allows user teleport the root links and the articulation +//system update all other links pose +void Sc::ArticulationSim::setGlobalPose() +{ + checkResize(); + mLLArticulation->teleportRootLink(); +} + +void Sc::ArticulationSim::setDirty(const bool dirty) +{ + mLLArticulation->setDirty(dirty); +} + +void Sc::ArticulationSim::debugCheckWakeCounterOfLinks(PxReal wakeCounter) const +{ + PX_UNUSED(wakeCounter); + +#ifdef _DEBUG + // make sure the links are in sync with the articulation + for(PxU32 i=0; i < mBodies.size(); i++) + { + PX_ASSERT(mBodies[i]->getBodyCore().getWakeCounter() == wakeCounter); + } +#endif +} + +void Sc::ArticulationSim::debugCheckSleepStateOfLinks(bool isSleeping) const +{ + PX_UNUSED(isSleeping); + +#ifdef _DEBUG + // make sure the links are in sync with the articulation + for(PxU32 i=0; i < mBodies.size(); i++) + { + if (isSleeping) + { + PX_ASSERT(!mBodies[i]->isActive()); + PX_ASSERT(mBodies[i]->getBodyCore().getWakeCounter() == 0.0f); + PX_ASSERT(mBodies[i]->checkSleepReadinessBesidesWakeCounter()); + } + else + PX_ASSERT(mBodies[i]->isActive()); + } +#endif +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h new file mode 100644 index 000000000..e52000b5d --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h @@ -0,0 +1,213 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_ARTICULATION_SIM +#define PX_PHYSICS_ARTICULATION_SIM + + +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "DyArticulation.h" +#include "ScArticulationCore.h" +#include "PxsSimpleIslandManager.h" + +namespace physx +{ + +namespace Dy +{ + class Articulation; +} + +class PxsTransformCache; +class PxsSimulationController; +class PxJoint; + +namespace Cm +{ + class SpatialVector; + template class BitMapBase; + typedef BitMapBase BitMap; +} + +namespace Bp +{ + class BoundsArray; +} + +namespace Sc +{ + + class BodySim; + class ArticulationJointSim; + class ArticulationCore; + class Scene; + class ConstraintSim; + + + class ArticulationSim : public Ps::UserAllocated + { + public: + ArticulationSim(ArticulationCore& core, + Scene& scene, + BodyCore& root); + + ~ArticulationSim(); + + PX_INLINE Dy::ArticulationV* getLowLevelArticulation() const { return mLLArticulation; } + PX_INLINE ArticulationCore& getCore() const { return mCore; } + + void addBody(BodySim& body, + BodySim* parent, + ArticulationJointSim* joint); + void removeBody(BodySim &sim); + + Dy::ArticulationLinkHandle getLinkHandle(BodySim& body) const; + + void checkResize() const; // resize LL memory if necessary + + void debugCheckWakeCounterOfLinks(PxReal wakeCounter) const; + void debugCheckSleepStateOfLinks(bool isSleeping) const; + + bool isSleeping() const; + void internalWakeUp(PxReal wakeCounter); // called when sim sets sleep timer + void sleepCheck(PxReal dt); + PxU32 getCCDLinks(BodySim** sims); + void updateCached(Cm::BitMapPinned* shapehapeChangedMap); + void markShapesUpdated(Cm::BitMapPinned* shapeChangedMap); + void updateContactDistance(PxReal* contactDistance, const PxReal dt, Bp::BoundsArray& boundsArray); + + void setActive(const bool b, const PxU32 infoFlag=0); + + void updateForces(PxReal dt, bool simUsesAdaptiveForce); + void saveLastCCDTransform(); + + // drive cache implementation + // + ArticulationDriveCache* + createDriveCache(PxReal compliance, + PxU32 driveIterations) const; + + void updateDriveCache(ArticulationDriveCache& cache, + PxReal compliance, + PxU32 driveIterations) const; + + void releaseDriveCache(ArticulationDriveCache& cache) const; + + void applyImpulse(BodyCore& link, + const ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque); + + void computeImpulseResponse(BodyCore& link, + PxVec3& linearResponse, + PxVec3& angularResponse, + const ArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const; + //external reduced coordinate implementation + PxU32 getDofs() const; + + //This function return the dof of the inbound joint, which belong to a link with corresponding linkID + PxU32 getDof(const PxU32 linkID) const; + + PxArticulationCache* createCache() const; + + PxU32 getCacheDataSize() const; + + PxU32 getCacheConstantDataSize() const; + + PxU32 getScratchMemorySize() const; + + void zeroCache(PxArticulationCache&) const; + + void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const; + + void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const; + + void releaseCache(PxArticulationCache&) const; + + void packJointData(const PxReal* maximum, PxReal* reduced) const; + + void unpackJointData(const PxReal* reduced, PxReal* maximum) const; + + void commonInit(); + + void computeGeneralizedGravityForce(PxArticulationCache& cache); + + void computeCoriolisAndCentrifugalForce(PxArticulationCache& cache); + + void computeGeneralizedExternalForce(PxArticulationCache& cache); + + void computeJointAcceleration(PxArticulationCache& cache); + + void computeJointForce(PxArticulationCache& cache); + + void computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache); + + void computeCoefficentMatrix(PxArticulationCache& cache); + + bool computeLambda(PxArticulationCache& cache, PxArticulationCache& rollBackCache, const PxReal* jointTorque, const PxVec3 gravity, const PxU32 maxIter); + + void computeGeneralizedMassMatrix(PxArticulationCache& cache); + + PxU32 getCoefficentMatrixSize() const; + //internal method implementation + PX_FORCE_INLINE IG::NodeIndex getIslandNodeIndex() const { return mIslandNodeIndex; } + + void setGlobalPose(); + + void setDirty(const bool dirty); + PxU32 findBodyIndex(BodySim &body) const; + + void addLoopConstraint(ConstraintSim* constraint); + void removeLoopConstraint(ConstraintSim* constraint); + + PxU32 getMaxDepth() { return mMaxDepth; } + + private: + ArticulationSim& operator=(const ArticulationSim&); + + Dy::ArticulationV* mLLArticulation; + Scene& mScene; + ArticulationCore& mCore; + Ps::Array mLinks; + Ps::Array mBodies; + Ps::Array mJoints; + IG::NodeIndex mIslandNodeIndex; + Ps::Array mLoopConstraints; + PxU32 mMaxDepth; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp new file mode 100644 index 000000000..289716af7 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp @@ -0,0 +1,717 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScBodyCore.h" +#include "ScBodySim.h" +#include "ScPhysics.h" +#include "ScScene.h" +#include "PxsSimulationController.h" +#include "PsFoundation.h" + +using namespace physx; + +static void updateBodySim(Sc::BodySim* bodySim) +{ + const bool isArticulationLink = bodySim->isArticulationLink(); + bodySim->getScene().getSimulationController()->updateDynamic(isArticulationLink, bodySim->getNodeIndex()); +} + +static void updateBodySim(Sc::BodyCore& bodyCore) +{ + Sc::BodySim* bodySim = bodyCore.getSim(); + if(bodySim) + updateBodySim(bodySim); +} + +Sc::BodyCore::BodyCore(PxActorType::Enum type, const PxTransform& bodyPose) +: RigidCore(type) +{ + const PxTolerancesScale& scale = Physics::getInstance().getTolerancesScale(); + // sizeof(BodyCore) = 176 => 160 => 144 => 160 bytes + + mCore.wakeCounter = Sc::Physics::sWakeCounterOnCreation; + mCore.inverseInertia = PxVec3(1.0f); + mCore.inverseMass = 1.0f; + mCore.body2World = bodyPose; + + PX_ASSERT(mCore.body2World.p.isFinite()); + PX_ASSERT(mCore.body2World.q.isFinite()); + + mCore.sleepThreshold = 5e-5f * scale.speed * scale.speed; + mCore.freezeThreshold = 2.5e-5f * scale.speed * scale.speed; + mSimStateData = NULL; + mCore.maxPenBias = -1e32f;//-PX_MAX_F32; + mCore.mFlags = PxRigidBodyFlags(); + mCore.linearVelocity = PxVec3(0.0f); + mCore.angularVelocity = PxVec3(0.0f); + mCore.linearDamping = 0.0f; + mCore.solverIterationCounts = (1 << 8) | 4; + mCore.contactReportThreshold = PX_MAX_F32; + mCore.setBody2Actor(PxTransform(PxIdentity)); + mCore.ccdAdvanceCoefficient = 0.15f; + mCore.maxContactImpulse = 1e32f;// PX_MAX_F32; + mCore.isFastMoving = false; + mCore.lockFlags = PxRigidDynamicLockFlags(0); + + if(type == PxActorType::eRIGID_DYNAMIC) + { + mCore.angularDamping = 0.05f; + mCore.maxAngularVelocitySq = 100.0f * 100.0f; +// mCore.maxAngularVelocitySq = 7.0f * 7.0f; + mCore.maxLinearVelocitySq = 1e32f; //PX_MAX_F32; + } + else + { + mCore.linearDamping = 0.05f; + mCore.angularDamping = 0.05f; + mCore.maxAngularVelocitySq = 50.0f * 50.0f; + mCore.maxLinearVelocitySq = 100.f * 100.f * scale.length * scale.length; + } +} + +Sc::BodyCore::~BodyCore() +{ + PX_ASSERT(getSim() == 0); + PX_ASSERT(!mSimStateData); +} + +Sc::BodySim* Sc::BodyCore::getSim() const +{ + return static_cast(Sc::ActorCore::getSim()); +} + +size_t Sc::BodyCore::getSerialCore(PxsBodyCore& serialCore) +{ + serialCore = mCore; + if(mSimStateData && mSimStateData->isKine()) + { + const Kinematic* kine = mSimStateData->getKinematicData(); + serialCore.inverseMass = kine->backupInvMass; + serialCore.inverseInertia = kine->backupInverseInertia; + serialCore.linearDamping = kine->backupLinearDamping; + serialCore.angularDamping = kine->backupAngularDamping; + serialCore.maxAngularVelocitySq = kine->backupMaxAngVelSq; + serialCore.maxLinearVelocitySq = kine->backupMaxLinVelSq; + } + return reinterpret_cast(&mCore); +} + +//-------------------------------------------------------------- +// +// BodyCore interface implementation +// +//-------------------------------------------------------------- + +void Sc::BodyCore::setBody2World(const PxTransform& p) +{ + mCore.body2World = p; + PX_ASSERT(p.p.isFinite()); + PX_ASSERT(p.q.isFinite()); + + BodySim* sim = getSim(); + if(sim) + { + sim->postBody2WorldChange(); + updateBodySim(sim); + } +} + +void Sc::BodyCore::setLinearVelocity(const PxVec3& v) +{ + mCore.linearVelocity = v; + + updateBodySim(*this); +} + +void Sc::BodyCore::setAngularVelocity(const PxVec3& v) +{ + mCore.angularVelocity = v; + + updateBodySim(*this); +} + +void Sc::BodyCore::setBody2Actor(const PxTransform& p) +{ + PX_ASSERT(p.p.isFinite()); + PX_ASSERT(p.q.isFinite()); + + mCore.setBody2Actor(p); + + BodySim* sim = getSim(); + if(sim) + { + sim->notifyShapesOfTransformChange(); + updateBodySim(sim); + } +} + +void Sc::BodyCore::addSpatialAcceleration(Ps::Pool* simStateDataPool, const PxVec3* linAcc, const PxVec3* angAcc) +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if(sim) + sim->notifyAddSpatialAcceleration(); + + if(!mSimStateData || !mSimStateData->isVelMod()) + setupSimStateData(simStateDataPool, false); + + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->notifyAddAcceleration(); + if(linAcc) velmod->accumulateLinearVelModPerSec(*linAcc); + if(angAcc) velmod->accumulateAngularVelModPerSec(*angAcc); +} + +void Sc::BodyCore::setSpatialAcceleration(Ps::Pool* simStateDataPool, const PxVec3* linAcc, const PxVec3* angAcc) +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if (sim) + sim->notifyAddSpatialAcceleration(); + + if (!mSimStateData || !mSimStateData->isVelMod()) + setupSimStateData(simStateDataPool, false); + + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->notifyAddAcceleration(); + if (linAcc) velmod->setLinearVelModPerSec(*linAcc); + if (angAcc) velmod->setAngularVelModPerSec(*angAcc); +} + +void Sc::BodyCore::clearSpatialAcceleration(bool force, bool torque) +{ + PX_ASSERT(force || torque); + + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if(sim) + sim->notifyClearSpatialAcceleration(); + + if(mSimStateData) + { + PX_ASSERT(mSimStateData->isVelMod()); + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->notifyClearAcceleration(); + if(force) + velmod->clearLinearVelModPerSec(); + if(torque) + velmod->clearAngularVelModPerSec(); + } +} + +void Sc::BodyCore::addSpatialVelocity(Ps::Pool* simStateDataPool, const PxVec3* linVelDelta, const PxVec3* angVelDelta) +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if(sim) + sim->notifyAddSpatialVelocity(); + + if(!mSimStateData || !mSimStateData->isVelMod()) + setupSimStateData(simStateDataPool, false); + + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->notifyAddVelocity(); + if(linVelDelta) + velmod->accumulateLinearVelModPerStep(*linVelDelta); + if(angVelDelta) + velmod->accumulateAngularVelModPerStep(*angVelDelta); +} + +void Sc::BodyCore::clearSpatialVelocity(bool force, bool torque) +{ + PX_ASSERT(force || torque); + + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if(sim) + sim->notifyClearSpatialVelocity(); + + if(mSimStateData) + { + PX_ASSERT(mSimStateData->isVelMod()); + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->notifyClearVelocity(); + if(force) + velmod->clearLinearVelModPerStep(); + if(torque) + velmod->clearAngularVelModPerStep(); + } +} + +PxReal Sc::BodyCore::getInverseMass() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupInvMass : mCore.inverseMass; +} + +void Sc::BodyCore::setInverseMass(PxReal m) +{ + if(mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupInvMass = m; + else + { + mCore.inverseMass = m; + updateBodySim(*this); + } +} + +const PxVec3& Sc::BodyCore::getInverseInertia() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupInverseInertia : mCore.inverseInertia; +} + +void Sc::BodyCore::setInverseInertia(const PxVec3& i) +{ + if(mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupInverseInertia = i; + else + { + mCore.inverseInertia = i; + updateBodySim(*this); + } +} + +PxReal Sc::BodyCore::getLinearDamping() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupLinearDamping : mCore.linearDamping; +} + +void Sc::BodyCore::setLinearDamping(PxReal d) +{ + if(mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupLinearDamping = d; + else + { + mCore.linearDamping = d; + updateBodySim(*this); + } +} + +PxReal Sc::BodyCore::getAngularDamping() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupAngularDamping : mCore.angularDamping; +} + +void Sc::BodyCore::setAngularDamping(PxReal v) +{ + if(mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupAngularDamping = v; + else + { + mCore.angularDamping = v; + updateBodySim(*this); + } +} + +PxReal Sc::BodyCore::getMaxAngVelSq() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupMaxAngVelSq : mCore.maxAngularVelocitySq; +} + +void Sc::BodyCore::setMaxAngVelSq(PxReal v) +{ + if(mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupMaxAngVelSq = v; + else + { + mCore.maxAngularVelocitySq = v; + updateBodySim(*this); + } +} + +PxReal Sc::BodyCore::getMaxLinVelSq() const +{ + return mSimStateData && mSimStateData->isKine() ? mSimStateData->getKinematicData()->backupMaxLinVelSq : mCore.maxLinearVelocitySq; +} + +void Sc::BodyCore::setMaxLinVelSq(PxReal v) +{ + if (mSimStateData && mSimStateData->isKine()) + mSimStateData->getKinematicData()->backupMaxLinVelSq = v; + else + { + mCore.maxLinearVelocitySq = v; + updateBodySim(*this); + } +} + +void Sc::BodyCore::setFlags(Ps::Pool* simStateDataPool, PxRigidBodyFlags f) +{ + const PxRigidBodyFlags old = mCore.mFlags; + if(f != old) + { + const PxU32 wasKinematic = old & PxRigidBodyFlag::eKINEMATIC; + const PxU32 isKinematic = f & PxRigidBodyFlag::eKINEMATIC; + const bool switchToKinematic = ((!wasKinematic) && isKinematic); + const bool switchToDynamic = (wasKinematic && (!isKinematic)); + + mCore.mFlags = f; + BodySim* sim = getSim(); + if (sim) + { + PX_ASSERT(simStateDataPool); + + const PxU32 posePreviewFlag = f & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW; + if(PxU32(old & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW) != posePreviewFlag) + sim->postPosePreviewChange(posePreviewFlag); + + // for those who might wonder about the complexity here: + // our current behavior is that you are not allowed to set a kinematic target unless the object is in a scene. + // Thus, the kinematic data should only be created/destroyed when we know for sure that we are in a scene. + + if(switchToKinematic) + { + setupSimStateData(simStateDataPool, true, false); + sim->postSwitchToKinematic(); + } + else if(switchToDynamic) + { + tearDownSimStateData(simStateDataPool, true); + sim->postSwitchToDynamic(); + } + + const PxU32 wasSpeculativeCCD = old & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD; + const PxU32 isSpeculativeCCD = f & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD; + + if(wasSpeculativeCCD ^ isSpeculativeCCD) + { + if(wasSpeculativeCCD) + { + if(sim->isArticulationLink()) + sim->getScene().resetSpeculativeCCDArticulationLink(sim->getNodeIndex().index()); + else + sim->getScene().resetSpeculativeCCDRigidBody(sim->getNodeIndex().index()); + + sim->getLowLevelBody().mInternalFlags &= (~PxcRigidBody::eSPECULATIVE_CCD); + } + else + { + if(sim->isArticulationLink()) + sim->getScene().setSpeculativeCCDArticulationLink(sim->getNodeIndex().index()); + else + sim->getScene().setSpeculativeCCDRigidBody(sim->getNodeIndex().index()); + + sim->getLowLevelBody().mInternalFlags |= (PxcRigidBody::eSPECULATIVE_CCD); + } + } + } + + if(switchToKinematic) + putToSleep(); + + if(sim) + { + const PxRigidBodyFlags ktFlags(PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES | PxRigidBodyFlag::eKINEMATIC); + const bool hadKt = (old & ktFlags) == ktFlags; + const bool hasKt = (f & ktFlags) == ktFlags; + if(hasKt && !hadKt) + sim->destroySqBounds(); + else if(hadKt && !hasKt) + sim->createSqBounds(); + } + } +} + +void Sc::BodyCore::setMaxContactImpulse(PxReal m) +{ + mCore.maxContactImpulse = m; + updateBodySim(*this); +} + +PxU32 Sc::BodyCore::getInternalIslandNodeIndex() const +{ + BodySim* sim = getSim(); + if (sim) + { + return sim->getNodeIndex().index(); + } + + return IG_INVALID_NODE; +} + +void Sc::BodyCore::setWakeCounter(PxReal wakeCounter, bool forceWakeUp) +{ + mCore.wakeCounter = wakeCounter; + BodySim* sim = getSim(); + if(sim) + { + //wake counter change, we need to trigger dma pxgbodysim data again + updateBodySim(sim); + if((wakeCounter > 0.0f) || forceWakeUp) + sim->wakeUp(); + sim->postSetWakeCounter(wakeCounter, forceWakeUp); + } +} + +void Sc::BodyCore::setSleepThreshold(PxReal t) +{ + mCore.sleepThreshold = t; + updateBodySim(*this); +} + +void Sc::BodyCore::setFreezeThreshold(PxReal t) +{ + mCore.freezeThreshold = t; + updateBodySim(*this); +} + +bool Sc::BodyCore::isSleeping() const +{ + BodySim* sim = getSim(); + return sim ? !sim->isActive() : true; +} + +void Sc::BodyCore::putToSleep() +{ + mCore.linearVelocity = PxVec3(0.0f); + mCore.angularVelocity = PxVec3(0.0f); + + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + BodySim* sim = getSim(); + if(sim) + { + sim->notifyClearSpatialAcceleration(); + sim->notifyClearSpatialVelocity(); + } + + //The velmod data is stored in a separate structure so we can record forces before scene insertion. + if(mSimStateData && mSimStateData->isVelMod()) + { + VelocityMod* velmod = mSimStateData->getVelocityModData(); + velmod->clear(); + } + + // important to clear all values before setting the wake counter because the values decide + // whether an object is ready to go to sleep or not. + setWakeCounter(0.0f); + + if(sim) + sim->putToSleep(); +} + +void Sc::BodyCore::disableInternalCaching(bool disable) +{ + PX_ASSERT(!mSimStateData || mSimStateData->isKine()); + + if(mSimStateData) + { + PX_ASSERT(getFlags() & PxRigidBodyFlag::eKINEMATIC); + + if(disable) + restore(); + else + backup(*mSimStateData); + } +} + +bool Sc::BodyCore::setupSimStateData(Ps::Pool* simStateDataPool, const bool isKinematic, const bool targetValid) +{ + SimStateData* data = mSimStateData; + if(!data) + { + data = simStateDataPool->construct(); + if(!data) + return false; + } + + if(isKinematic) + { + PX_ASSERT(!mSimStateData || !mSimStateData->isKine()); + + new(data) SimStateData(SimStateData::eKine); + Kinematic* kine = data->getKinematicData(); + kine->targetValid = PxU8(targetValid ? 1 : 0); + backup(*data); + } + else + { + PX_ASSERT(!mSimStateData || !mSimStateData->isVelMod()); + PX_ASSERT(!targetValid); + + new(data) SimStateData(SimStateData::eVelMod); + VelocityMod* velmod = data->getVelocityModData(); + velmod->clear(); + velmod->flags = 0; + } + mSimStateData = data; + return true; +} + +bool Sc::BodyCore::checkSimStateKinematicStatus(const bool isKinematic) const +{ + PX_ASSERT(mSimStateData); + return mSimStateData->isKine() == isKinematic; +} + +void Sc::BodyCore::tearDownSimStateData(Ps::Pool* simStateDataPool, const bool isKinematic) +{ + PX_ASSERT(!mSimStateData || mSimStateData->isKine() == isKinematic); + + if(mSimStateData) + { + if(isKinematic) + restore(); + + simStateDataPool->destroy(mSimStateData); + mSimStateData=NULL; + } +} + +void Sc::BodyCore::backup(SimStateData& b) +{ + PX_ASSERT(b.isKine()); + + Kinematic* kine = b.getKinematicData(); + kine->backupLinearDamping = mCore.linearDamping; + kine->backupAngularDamping = mCore.angularDamping; + kine->backupInverseInertia = mCore.inverseInertia; + kine->backupInvMass = mCore.inverseMass; + kine->backupMaxAngVelSq = mCore.maxAngularVelocitySq; + kine->backupMaxLinVelSq = mCore.maxLinearVelocitySq; + + mCore.inverseMass = 0.0f; + mCore.inverseInertia = PxVec3(0.0f); + mCore.linearDamping = 0.0f; + mCore.angularDamping = 0.0f; + mCore.maxAngularVelocitySq = PX_MAX_REAL; + mCore.maxLinearVelocitySq = PX_MAX_REAL; +} + +void Sc::BodyCore::restore() +{ + PX_ASSERT(mSimStateData && mSimStateData->isKine()); + + const Kinematic* kine = mSimStateData->getKinematicData(); + mCore.inverseMass = kine->backupInvMass; + mCore.inverseInertia = kine->backupInverseInertia; + mCore.linearDamping = kine->backupLinearDamping; + mCore.angularDamping = kine->backupAngularDamping; + mCore.maxAngularVelocitySq = kine->backupMaxAngVelSq; + mCore.maxLinearVelocitySq = kine->backupMaxLinVelSq; +} + +void Sc::BodyCore::onOriginShift(const PxVec3& shift) +{ + mCore.body2World.p -= shift; + if(mSimStateData && (getFlags() & PxRigidBodyFlag::eKINEMATIC) && mSimStateData->getKinematicData()->targetValid) + mSimStateData->getKinematicData()->targetPose.p -= shift; + + BodySim* b = getSim(); + if(b) + b->onOriginShift(shift); // BodySim might not exist if actor has simulation disabled (PxActorFlag::eDISABLE_SIMULATION) +} + +// PT: TODO: isn't that the same as BodyCore->getPxActor() now? +PxActor* Sc::getPxActorFromBodyCore(Sc::BodyCore* r, PxActorType::Enum& type) +{ + const PxActorType::Enum actorCoretype = r->getActorCoreType(); + type = actorCoretype; + return Ps::pointerOffset(r, Sc::gOffsetTable.scCore2PxActor[actorCoretype]); +} + +// PT: TODO: why do we test againt NULL everywhere but not in 'isFrozen' ? +Ps::IntBool Sc::BodyCore::isFrozen() const +{ + return getSim()->isFrozen(); +} + +void Sc::BodyCore::setSolverIterationCounts(PxU16 c) +{ + mCore.solverIterationCounts = c; + Sc::BodySim* sim = getSim(); + if(sim) + sim->getLowLevelBody().solverIterationCounts = c; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Sc::BodyCore::getKinematicTarget(PxTransform& p) const +{ + PX_ASSERT(mCore.mFlags & PxRigidBodyFlag::eKINEMATIC); + + if(mSimStateData && mSimStateData->isKine() && mSimStateData->getKinematicData()->targetValid) + { + p = mSimStateData->getKinematicData()->targetPose; + return true; + } + else + return false; +} + +bool Sc::BodyCore::getHasValidKinematicTarget() const +{ + //The use pattern for this is that we should only look for kinematic data if we know it is kinematic. + //We might look for velmod data even if it is kinematic. + PX_ASSERT(!mSimStateData || mSimStateData->isKine()); + return mSimStateData && mSimStateData->isKine() && mSimStateData->getKinematicData()->targetValid; +} + +void Sc::BodyCore::setKinematicTarget(Ps::Pool* simStateDataPool, const PxTransform& p, PxReal wakeCounter) +{ + PX_ASSERT(mCore.mFlags & PxRigidBodyFlag::eKINEMATIC); + PX_ASSERT(!mSimStateData || mSimStateData->isKine()); + + if(mSimStateData) + { + Kinematic* kine = mSimStateData->getKinematicData(); + kine->targetPose = p; + kine->targetValid = 1; + + Sc::BodySim* bSim = getSim(); + if(bSim) + bSim->postSetKinematicTarget(); + } + else + { + if(setupSimStateData(simStateDataPool, true, true)) + { + PX_ASSERT(!getSim()); // covers the following scenario: kinematic gets added to scene while sim is running and target gets set (at that point the sim object does not yet exist) + + Kinematic* kine = mSimStateData->getKinematicData(); + kine->targetPose = p; + kine->targetValid = 1; + } + else + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "PxRigidDynamic: setting kinematic target failed, not enough memory."); + } + + wakeUp(wakeCounter); +} + +void Sc::BodyCore::invalidateKinematicTarget() +{ + PX_ASSERT(mSimStateData && mSimStateData->isKine()); + mSimStateData->getKinematicData()->targetValid = 0; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp new file mode 100644 index 000000000..612f4c99d --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp @@ -0,0 +1,973 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScBodySim.h" +#include "ScShapeSim.h" +#include "ScScene.h" +#include "ScArticulationSim.h" +#include "PxsContext.h" +#include "PxsSimpleIslandManager.h" +#include "PxsSimulationController.h" + +using namespace physx; +using namespace physx::Dy; +using namespace Sc; + +#define PX_FREEZE_INTERVAL 1.5f +#define PX_FREE_EXIT_THRESHOLD 4.f +#define PX_FREEZE_TOLERANCE 0.25f + +#define PX_SLEEP_DAMPING 0.5f +#define PX_FREEZE_SCALE 0.9f + +BodySim::BodySim(Scene& scene, BodyCore& core, bool compound) : + RigidSim (scene, core), + mLLBody (&core.getCore()), + mNodeIndex (IG_INVALID_NODE), + mInternalFlags (0), + mVelModState (VMF_GRAVITY_DIRTY), + mActiveListIndex (SC_NOT_IN_SCENE_INDEX), + mActiveCompoundListIndex (SC_NOT_IN_SCENE_INDEX), + mArticulation (NULL), + mConstraintGroup (NULL) +{ + // For 32-bit, sizeof(BodyCore) = 160 bytes, sizeof(BodySim) = 192 bytes + mLLBody.sleepLinVelAcc = PxVec3(0); + mLLBody.sleepAngVelAcc = PxVec3(0); + mLLBody.freezeCount = PX_FREEZE_INTERVAL; + mLLBody.accelScale = 1.f; + mLLBody.solverIterationCounts = core.getCore().solverIterationCounts; + core.getCore().numCountedInteractions = 0; + core.getCore().numBodyInteractions = 0; + mLLBody.mInternalFlags = 0; + if (core.getActorFlags()&PxActorFlag::eDISABLE_GRAVITY) + mLLBody.mInternalFlags |= PxsRigidBody::eDISABLE_GRAVITY; + if (core.getFlags() & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + mLLBody.mInternalFlags |= PxsRigidBody::eSPECULATIVE_CCD; + + //If a body pending insertion was given a force/torque then it will have + //the dirty flags stored in a separate structure. Copy them across + //so we can use them now that the BodySim is constructed. + SimStateData* simStateData = core.getSimStateData(false); + bool hasPendingForce = false; + if(simStateData) + { + VelocityMod* velmod = simStateData->getVelocityModData(); + hasPendingForce = (velmod->flags != 0) && + (!velmod->getLinearVelModPerSec().isZero() || !velmod->getAngularVelModPerSec().isZero() || + !velmod->getLinearVelModPerStep().isZero() || !velmod->getAngularVelModPerStep().isZero()); + mVelModState = velmod->flags; + velmod->flags = 0; + } + + // PT: don't read the core ptr we just wrote, use input param + // PT: at time of writing we get a big L2 here because even though bodycore has been prefetched, the wake counter is 160 bytes away + const bool isAwake = (core.getWakeCounter() > 0) || + (!core.getLinearVelocity().isZero()) || + (!core.getAngularVelocity().isZero()) || + hasPendingForce; + + const bool isKine = isKinematic(); + + IG::SimpleIslandManager* simpleIslandManager = scene.getSimpleIslandManager(); + if (!isArticulationLink()) + { + mNodeIndex = simpleIslandManager->addRigidBody(&mLLBody, isKine, isAwake); + } + else + { + if(mArticulation) + { + const ArticulationLinkHandle articLinkhandle = mArticulation->getLinkHandle(*this); + IG::NodeIndex index = mArticulation->getIslandNodeIndex(); + mNodeIndex.setIndices(index.index(), articLinkhandle & (DY_ARTICULATION_MAX_SIZE-1)); + } + } + + //If a user add force or torque before the body is inserted into the scene, + //this logic will make sure pre solver stage apply external force/torque to the body + if(hasPendingForce && !isArticulationLink()) + scene.getVelocityModifyMap().growAndSet(mNodeIndex.index()); + + PX_ASSERT(mActiveListIndex == SC_NOT_IN_SCENE_INDEX); + + // A.B. need to set the compound rigid flag early enough, so that we add the rigid into + // active list and do not create the shape bounds + if(compound) + raiseInternalFlag(BF_IS_COMPOUND_RIGID); + + setActive(isAwake, ActorSim::AS_PART_OF_CREATION); + + if (isAwake) + { + scene.addToActiveBodyList(*this); + PX_ASSERT(isActive()); + } + else + { + mActiveListIndex = SC_NOT_IN_ACTIVE_LIST_INDEX; + mActiveCompoundListIndex = SC_NOT_IN_ACTIVE_LIST_INDEX; + PX_ASSERT(!isActive()); + + simpleIslandManager->deactivateNode(mNodeIndex); + } + + if (isKine) + { + initKinematicStateBase(core, true); + + const SimStateData* kd = core.getSimStateData(true); + if (!kd) + { + core.setupSimStateData(scene.getSimStateDataPool(), true, false); + notifyPutToSleep(); // sleep state of kinematics is fully controlled by the simulation controller not the island manager + } + else + { + PX_ASSERT(kd->isKine()); + PX_ASSERT(kd->getKinematicData()->targetValid); // the only reason for the kinematic data to exist at that point already is if the target has been set + PX_ASSERT(isAwake); // the expectation is that setting a target also sets the wake counter to a positive value + postSetKinematicTarget(); + } + } +} + +BodySim::~BodySim() +{ + Scene& scene = getScene(); + const bool active = isActive(); + + getBodyCore().tearDownSimStateData(scene.getSimStateDataPool(), isKinematic() ? true : false); + + PX_ASSERT(!readInternalFlag(BF_ON_DEATHROW)); // Before 3.0 it could happen that destroy could get called twice. Assert to make sure this is fixed. + raiseInternalFlag(BF_ON_DEATHROW); + + scene.removeBody(*this); + PX_ASSERT(!getConstraintGroup()); // Removing from scene should erase constraint group node if it existed + + if(mArticulation) + mArticulation->removeBody(*this); + + //Articulations are represented by a single node, so they must only be removed by the articulation and not the links! + if(mArticulation == NULL && mNodeIndex.articulationLinkId() == 0) //If it wasn't an articulation link, then we can remove it + scene.getSimpleIslandManager()->removeNode(mNodeIndex); + + PX_ASSERT(mActiveListIndex != SC_NOT_IN_SCENE_INDEX); + + if (active) + scene.removeFromActiveBodyList(*this); + + mActiveListIndex = SC_NOT_IN_SCENE_INDEX; + mActiveCompoundListIndex = SC_NOT_IN_SCENE_INDEX; + + mCore.setSim(NULL); +} + +void BodySim::updateCached(Cm::BitMapPinned* shapeChangedMap) +{ + if(!(mLLBody.mInternalFlags & PxsRigidBody::eFROZEN)) + { + ElementSim* current = getElements_(); + while(current) + { + static_cast(current)->updateCached(0, shapeChangedMap); + current = current->mNextInActor; + } + } +} + +void BodySim::updateCached(PxsTransformCache& transformCache, Bp::BoundsArray& boundsArray) +{ + PX_ASSERT(!(mLLBody.mInternalFlags & PxsRigidBody::eFROZEN)); // PT: should not be called otherwise + + ElementSim* current = getElements_(); + while(current) + { + static_cast(current)->updateCached(transformCache, boundsArray); + current = current->mNextInActor; + } +} + +void BodySim::updateContactDistance(PxReal* contactDistance, const PxReal dt, Bp::BoundsArray& boundsArray) +{ + if (getLowLevelBody().getCore().mFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD + && !(getLowLevelBody().mInternalFlags & PxcRigidBody::eFROZEN)) + { + const PxVec3 linVel = getLowLevelBody().getLinearVelocity(); + const PxVec3 aVel = getLowLevelBody().getAngularVelocity(); + const PxReal inflation = linVel.magnitude() * dt; + + ElementSim* current = getElements_(); + while(current) + { + static_cast(current)->updateContactDistance(contactDistance, inflation, aVel, dt, boundsArray); + current = current->mNextInActor; + } + } +} + +//-------------------------------------------------------------- +// +// BodyCore interface implementation +// +//-------------------------------------------------------------- + +void BodySim::notifyAddSpatialAcceleration() +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + raiseVelocityModFlag(VMF_ACC_DIRTY); + + if(!isArticulationLink()) + getScene().getVelocityModifyMap().growAndSet(getNodeIndex().index()); +} + +void BodySim::notifyClearSpatialAcceleration() +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + raiseVelocityModFlag(VMF_ACC_DIRTY); + if (!isArticulationLink()) + getScene().getVelocityModifyMap().growAndSet(getNodeIndex().index()); +} + +void BodySim::notifyAddSpatialVelocity() +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + raiseVelocityModFlag(VMF_VEL_DIRTY); + if (!isArticulationLink()) + getScene().getVelocityModifyMap().growAndSet(getNodeIndex().index()); +} + +void BodySim::notifyClearSpatialVelocity() +{ + //The dirty flag is stored separately in the BodySim so that we query the dirty flag before going to + //the expense of querying the simStateData for the velmod values. + raiseVelocityModFlag(VMF_VEL_DIRTY); + if (!isArticulationLink()) + getScene().getVelocityModifyMap().growAndSet(getNodeIndex().index()); +} + +void BodySim::postActorFlagChange(PxU32 oldFlags, PxU32 newFlags) +{ + // PT: don't convert to bool if not needed + const PxU32 wasWeightless = oldFlags & PxActorFlag::eDISABLE_GRAVITY; + const PxU32 isWeightless = newFlags & PxActorFlag::eDISABLE_GRAVITY; + + if (isWeightless != wasWeightless) + { + if (mVelModState == 0) raiseVelocityModFlag(VMF_GRAVITY_DIRTY); + + if (isWeightless) + mLLBody.mInternalFlags |= PxsRigidBody::eDISABLE_GRAVITY; + else + mLLBody.mInternalFlags &= (~PxsRigidBody::eDISABLE_GRAVITY); + } +} + +void BodySim::postBody2WorldChange() +{ + mLLBody.saveLastCCDTransform(); + notifyShapesOfTransformChange(); +} + +void BodySim::postSetWakeCounter(PxReal t, bool forceWakeUp) +{ + if ((t > 0.0f) || forceWakeUp) + notifyNotReadyForSleeping(); + else + { + const bool readyForSleep = checkSleepReadinessBesidesWakeCounter(); + if (readyForSleep) + notifyReadyForSleeping(); + } +} + +void BodySim::postSetKinematicTarget() +{ + PX_ASSERT(getBodyCore().getSimStateData(true)); + PX_ASSERT(getBodyCore().getSimStateData(true)->isKine()); + PX_ASSERT(getBodyCore().getSimStateData(true)->getKinematicData()->targetValid); + + raiseInternalFlag(BF_KINEMATIC_MOVED); // Important to set this here already because trigger interactions need to have this information when being activated. + clearInternalFlag(BF_KINEMATIC_SURFACE_VELOCITY); +} + +static void updateBPGroup(ElementSim* current) +{ + while(current) + { + static_cast(current)->updateBPGroup(); + current = current->mNextInActor; + } +} + +void BodySim::postSwitchToKinematic() +{ + initKinematicStateBase(getBodyCore(), false); + + // - interactions need to get refiltered to make sure that kinematic-kinematic and kinematic-static pairs get suppressed + // - unlike postSwitchToDynamic(), constraint interactions are not marked dirty here because a transition to kinematic will put the object asleep which in turn + // triggers onDeactivate_() on the constraint pairs that are active. If such pairs get deactivated, they will get removed from the list of active breakable + // constraints automatically. + setActorsInteractionsDirty(InteractionDirtyFlag::eBODY_KINEMATIC, NULL, InteractionFlag::eFILTERABLE); + + getScene().getSimpleIslandManager()->setKinematic(mNodeIndex); + + // + updateBPGroup(getElements_()); +} + +void BodySim::postSwitchToDynamic() +{ + mScene.getSimpleIslandManager()->setDynamic(mNodeIndex); + + setForcesToDefaults(true); + + if(getConstraintGroup()) + getConstraintGroup()->markForProjectionTreeRebuild(mScene.getProjectionManager()); + + // - interactions need to get refiltered to make sure that former kinematic-kinematic and kinematic-static pairs get enabled + // - switching from kinematic to dynamic does not change the sleep state of the body. The constraint interactions are marked dirty + // to check later whether they need to be activated plus potentially tracked for constraint break testing. This special treatment + // is necessary because constraints between two kinematic bodies are considered inactive, no matter whether one of the kinematics + // is active (has a target) or not. + setActorsInteractionsDirty(InteractionDirtyFlag::eBODY_KINEMATIC, NULL, InteractionFlag::eFILTERABLE | InteractionFlag::eCONSTRAINT); + + clearInternalFlag(BF_KINEMATIC_MOVE_FLAGS); + + if(isActive()) + mScene.swapInActiveBodyList(*this); + + // + updateBPGroup(getElements_()); +} + +void BodySim::postPosePreviewChange(const PxU32 posePreviewFlag) +{ + if (isActive()) + { + if (posePreviewFlag & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW) + getScene().addToPosePreviewList(*this); + else + getScene().removeFromPosePreviewList(*this); + } + else + PX_ASSERT(!getScene().isInPosePreviewList(*this)); +} + +//-------------------------------------------------------------- +// +// Sleeping +// +//-------------------------------------------------------------- + +void BodySim::activate() +{ + // Activate body + { + PX_ASSERT((!isKinematic()) || notInScene() || readInternalFlag(InternalFlags(BF_KINEMATIC_MOVED | BF_KINEMATIC_SURFACE_VELOCITY))); // kinematics should only get activated when a target is set. + // exception: object gets newly added, then the state change will happen later + if(!isArticulationLink()) + { + mLLBody.mInternalFlags &= (~PxsRigidBody::eFROZEN); + // Put in list of activated bodies. The list gets cleared at the end of a sim step after the sleep callbacks have been fired. + getScene().onBodyWakeUp(this); + } + + BodyCore& core = getBodyCore(); + if(core.getFlags() & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW) + { + PX_ASSERT(!getScene().isInPosePreviewList(*this)); + getScene().addToPosePreviewList(*this); + } + createSqBounds(); + } + + // Activate interactions + { + const PxU32 nbInteractions = getActorInteractionCount(); + + for(PxU32 i=0; igetType() != InteractionType::eOVERLAP && + interaction->getType() != InteractionType::eMARKER; + + if(!interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE) && (isNotIGControlled)) + { + const bool proceed = activateInteraction(interaction, NULL); + + if (proceed && (interaction->getType() < InteractionType::eTRACKED_IN_SCENE_COUNT)) + getScene().notifyInteractionActivated(interaction); + } + } + } +} + +void BodySim::deactivate() +{ + // Deactivate interactions + { + const PxU32 nbInteractions = getActorInteractionCount(); + + for(PxU32 i=0; igetType() != InteractionType::eOVERLAP && + interaction->getType() != InteractionType::eMARKER; + + if (interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE) && isNotIGControlled) + { + const bool proceed = deactivateInteraction(interaction); + if (proceed && (interaction->getType() < InteractionType::eTRACKED_IN_SCENE_COUNT)) + getScene().notifyInteractionDeactivated(interaction); + } + } + } + + // Deactivate body + { + PX_ASSERT((!isKinematic()) || notInScene() || !readInternalFlag(BF_KINEMATIC_MOVED)); // kinematics should only get deactivated when no target is set. + // exception: object gets newly added, then the state change will happen later + BodyCore& core = getBodyCore(); + if(!readInternalFlag(BF_ON_DEATHROW)) + { + // Set velocity to 0. + // Note: this is also fine if the method gets called because the user puts something to sleep (this behavior is documented in the API) + PX_ASSERT(core.getWakeCounter() == 0.0f); + const PxVec3 zero(0.0f); + core.setLinearVelocityInternal(zero); + core.setAngularVelocityInternal(zero); + + setForcesToDefaults(!(mLLBody.mInternalFlags & PxsRigidBody::eDISABLE_GRAVITY)); + } + + if(!isArticulationLink()) // Articulations have their own sleep logic. + getScene().onBodySleep(this); + + if(core.getFlags() & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW) + { + PX_ASSERT(getScene().isInPosePreviewList(*this)); + getScene().removeFromPosePreviewList(*this); + } + destroySqBounds(); + } +} + +void BodySim::setActive(bool active, PxU32 infoFlag) +{ + PX_ASSERT(!active || isDynamicRigid()); // Currently there should be no need to activate an actor that does not take part in island generation + + const PxU32 asPartOfCreation = infoFlag & ActorSim::AS_PART_OF_CREATION; + if (asPartOfCreation || isActive() != active) + { + PX_ASSERT(!asPartOfCreation || (getActorInteractionCount() == 0)); // On creation or destruction there should be no interactions + + if (active) + { + if (!asPartOfCreation) + { + // Inactive => Active + getScene().addToActiveBodyList(*this); + } + + activate(); + + PX_ASSERT(asPartOfCreation || isActive()); + } + else + { + if (!asPartOfCreation) + { + // Active => Inactive + getScene().removeFromActiveBodyList(*this); + } + + deactivate(); + + PX_ASSERT(asPartOfCreation || (!isActive())); + } + } +} + +void BodySim::wakeUp() +{ + setActive(true); + notifyWakeUp(true); +} + +void BodySim::putToSleep() +{ + PX_ASSERT(getBodyCore().getWakeCounter() == 0.0f); + PX_ASSERT(getBodyCore().getLinearVelocity().isZero()); + PX_ASSERT(getBodyCore().getAngularVelocity().isZero()); +#ifdef _DEBUG + // pending forces should have been cleared at this point + const SimStateData* sd = getBodyCore().getSimStateData(false); + if (sd) + { + const VelocityMod* vm = sd->getVelocityModData(); + PX_ASSERT(vm->linearPerSec.isZero() && vm->linearPerStep.isZero() && vm->angularPerSec.isZero() && vm->angularPerStep.isZero()); + } +#endif + + setActive(false); + notifyPutToSleep(); + + clearInternalFlag(InternalFlags(BF_KINEMATIC_SETTLING | BF_KINEMATIC_SETTLING_2)); // putToSleep is used when a kinematic gets removed from the scene while the sim is running and then gets re-inserted immediately. + // We can move this code when we look into the open task of making buffered re-insertion more consistent with the non-buffered case. +} + +void BodySim::internalWakeUp(PxReal wakeCounterValue) +{ + if(mArticulation) + mArticulation->internalWakeUp(wakeCounterValue); + else + internalWakeUpBase(wakeCounterValue); +} + +void BodySim::internalWakeUpArticulationLink(PxReal wakeCounterValue) +{ + PX_ASSERT(mArticulation); + internalWakeUpBase(wakeCounterValue); +} + +void BodySim::internalWakeUpBase(PxReal wakeCounterValue) //this one can only increase the wake counter, not decrease it, so it can't be used to put things to sleep! +{ + if ((!isKinematic()) && (getBodyCore().getWakeCounter() < wakeCounterValue)) + { + PX_ASSERT(wakeCounterValue > 0.0f); + getBodyCore().setWakeCounterFromSim(wakeCounterValue); + + //we need to update the gpu body sim because we reset the wake counter for the body core + mScene.getSimulationController()->updateDynamic(isArticulationLink(), mNodeIndex); + setActive(true); + notifyWakeUp(false); + mLLBody.mInternalFlags &= (~PxsRigidBody::eFROZEN); + } +} + +void BodySim::notifyReadyForSleeping() +{ + if(mArticulation == NULL) + getScene().getSimpleIslandManager()->deactivateNode(mNodeIndex); +} + +void BodySim::notifyNotReadyForSleeping() +{ + getScene().getSimpleIslandManager()->activateNode(mNodeIndex); +} + +void BodySim::notifyWakeUp(bool /*wakeUpInIslandGen*/) +{ + getScene().getSimpleIslandManager()->activateNode(mNodeIndex); +} + +void BodySim::notifyPutToSleep() +{ + getScene().getSimpleIslandManager()->putNodeToSleep(mNodeIndex); +} + +void BodySim::resetSleepFilter() +{ + mLLBody.sleepAngVelAcc = PxVec3(0.0f); + mLLBody.sleepLinVelAcc = PxVec3(0.0f); +} + +//This function will be called by CPU sleepCheck code +PxReal BodySim::updateWakeCounter(PxReal dt, PxReal energyThreshold, const Cm::SpatialVector& motionVelocity) +{ + // update the body's sleep state and + BodyCore& core = getBodyCore(); + + const PxReal wakeCounterResetTime = ScInternalWakeCounterResetValue; + + PxReal wc = core.getWakeCounter(); + + { + PxVec3 bcSleepLinVelAcc = mLLBody.sleepLinVelAcc; + PxVec3 bcSleepAngVelAcc = mLLBody.sleepAngVelAcc; + + if(wc < wakeCounterResetTime * 0.5f || wc < dt) + { + const PxTransform& body2World = getBody2World(); + + // calculate normalized energy: kinetic energy divided by mass + const PxVec3 t = core.getInverseInertia(); + const PxVec3 inertia(t.x > 0.0f ? 1.0f/t.x : 1.0f, t.y > 0.0f ? 1.0f/t.y : 1.0f, t.z > 0.0f ? 1.0f/t.z : 1.0f); + + PxVec3 sleepLinVelAcc =motionVelocity.linear; + PxVec3 sleepAngVelAcc = body2World.q.rotateInv(motionVelocity.angular); + + bcSleepLinVelAcc += sleepLinVelAcc; + bcSleepAngVelAcc += sleepAngVelAcc; + + PxReal invMass = core.getInverseMass(); + if(invMass == 0.0f) + invMass = 1.0f; + + const PxReal angular = bcSleepAngVelAcc.multiply(bcSleepAngVelAcc).dot(inertia) * invMass; + const PxReal linear = bcSleepLinVelAcc.magnitudeSquared(); + PxReal normalizedEnergy = 0.5f * (angular + linear); + + // scale threshold by cluster factor (more contacts => higher sleep threshold) + const PxReal clusterFactor = PxReal(1 + getNumCountedInteractions()); + const PxReal threshold = clusterFactor*energyThreshold; + + if (normalizedEnergy >= threshold) + { + PX_ASSERT(isActive()); + resetSleepFilter(); + const float factor = threshold == 0.0f ? 2.0f : PxMin(normalizedEnergy/threshold, 2.0f); + PxReal oldWc = wc; + wc = factor * 0.5f * wakeCounterResetTime + dt * (clusterFactor - 1.0f); + core.setWakeCounterFromSim(wc); + if (oldWc == 0.0f) // for the case where a sleeping body got activated by the system (not the user) AND got processed by the solver as well + notifyNotReadyForSleeping(); + + return wc; + } + } + + mLLBody.sleepLinVelAcc = bcSleepLinVelAcc; + mLLBody.sleepAngVelAcc = bcSleepAngVelAcc; + } + + wc = PxMax(wc-dt, 0.0f); + core.setWakeCounterFromSim(wc); + return wc; +} + +//-------------------------------------------------------------- +// +// Kinematics +// +//-------------------------------------------------------------- + +PX_FORCE_INLINE void BodySim::initKinematicStateBase(BodyCore&, bool asPartOfCreation) +{ + PX_ASSERT(!readInternalFlag(BF_KINEMATIC_MOVED)); + + if (!asPartOfCreation && isActive()) + getScene().swapInActiveBodyList(*this); + + //mLLBody.setAccelerationV(Cm::SpatialVector::zero()); + + // Need to be before setting setRigidBodyFlag::KINEMATIC + + if (getConstraintGroup()) + getConstraintGroup()->markForProjectionTreeRebuild(getScene().getProjectionManager()); +} + +void BodySim::calculateKinematicVelocity(PxReal oneOverDt) +{ + PX_ASSERT(isKinematic()); + + /*------------------------------------------------\ + | kinematic bodies are moved directly by the user and are not influenced by external forces + | we simply determine the distance moved since the last simulation frame and + | assign the appropriate delta to the velocity. This vel will be used to shove dynamic + | objects in the solver. + | We have to do this like so in a delayed way, because when the user sets the target pos the dt is not + | yet known. + \------------------------------------------------*/ + PX_ASSERT(isActive()); + + BodyCore& core = getBodyCore(); + + if (readInternalFlag(BF_KINEMATIC_MOVED)) + { + clearInternalFlag(InternalFlags(BF_KINEMATIC_SETTLING | BF_KINEMATIC_SETTLING_2)); + const SimStateData* kData = core.getSimStateData(true); + PX_ASSERT(kData); + PX_ASSERT(kData->isKine()); + PX_ASSERT(kData->getKinematicData()->targetValid); + PxVec3 linVelLL, angVelLL; + const PxTransform targetPose = kData->getKinematicData()->targetPose; + const PxTransform& currBody2World = getBody2World(); + + //the kinematic target pose is now the target of the body (CoM) and not the actor. + + PxVec3 deltaPos = targetPose.p; + deltaPos -= currBody2World.p; + linVelLL = deltaPos * oneOverDt; + + PxQuat q = targetPose.q * currBody2World.q.getConjugate(); + + if (q.w < 0) //shortest angle. + q = -q; + + PxReal angle; + PxVec3 axis; + q.toRadiansAndUnitAxis(angle, axis); + angVelLL = axis * angle * oneOverDt; + + core.getCore().linearVelocity = linVelLL; + core.getCore().angularVelocity = angVelLL; + + // Moving a kinematic should trigger a wakeUp call on a higher level. + PX_ASSERT(core.getWakeCounter()>0); + PX_ASSERT(isActive()); + + } + else if (!readInternalFlag(BF_KINEMATIC_SURFACE_VELOCITY)) + { + core.setLinearVelocity(PxVec3(0)); + core.setAngularVelocity(PxVec3(0)); + } +} + +void BodySim::updateKinematicPose() +{ + /*------------------------------------------------\ + | kinematic bodies are moved directly by the user and are not influenced by external forces + | we simply determine the distance moved since the last simulation frame and + | assign the appropriate delta to the velocity. This vel will be used to shove dynamic + | objects in the solver. + | We have to do this like so in a delayed way, because when the user sets the target pos the dt is not + | yet known. + \------------------------------------------------*/ + + PX_ASSERT(isKinematic()); + PX_ASSERT(isActive()); + + BodyCore& core = getBodyCore(); + + if (readInternalFlag(BF_KINEMATIC_MOVED)) + { + clearInternalFlag(InternalFlags(BF_KINEMATIC_SETTLING | BF_KINEMATIC_SETTLING_2)); + const SimStateData* kData = core.getSimStateData(true); + PX_ASSERT(kData); + PX_ASSERT(kData->isKine()); + PX_ASSERT(kData->getKinematicData()->targetValid); + + const PxTransform targetPose = kData->getKinematicData()->targetPose; + getBodyCore().getCore().body2World = targetPose; + } +} + +bool BodySim::deactivateKinematic() +{ + BodyCore& core = getBodyCore(); + if(readInternalFlag(BF_KINEMATIC_SETTLING_2)) + { + clearInternalFlag(BF_KINEMATIC_SETTLING_2); + core.setWakeCounterFromSim(0); // For sleeping objects the wake counter must be 0. This needs to hold for kinematics too. + notifyReadyForSleeping(); + notifyPutToSleep(); + setActive(false); + return true; + } + else if (readInternalFlag(BF_KINEMATIC_SETTLING)) + { + clearInternalFlag(BF_KINEMATIC_SETTLING); + raiseInternalFlag(BF_KINEMATIC_SETTLING_2); + } + else if (!readInternalFlag(BF_KINEMATIC_SURFACE_VELOCITY)) + { + clearInternalFlag(BF_KINEMATIC_MOVED); + raiseInternalFlag(BF_KINEMATIC_SETTLING); + } + return false; +} + +//-------------------------------------------------------------- +// +// Miscellaneous +// +//-------------------------------------------------------------- + +void BodySim::updateForces(PxReal dt, PxsRigidBody** updatedBodySims, PxU32* updatedBodyNodeIndices, PxU32& index, Cm::SpatialVector* acceleration, const bool useAccelerations, bool simUsesAdaptiveForce) +{ + PxVec3 linAcc(0.0f), angAcc(0.0f); + + const bool accDirty = readVelocityModFlag(VMF_ACC_DIRTY); + const bool velDirty = readVelocityModFlag(VMF_VEL_DIRTY); + + BodyCore& bodyCore = getBodyCore(); + SimStateData* simStateData = NULL; + + //if we change the logic like this, which means we don't need to have two seperate variables in the pxgbodysim to represent linAcc and angAcc. However, this + //means angAcc will be always 0 + if( (accDirty || velDirty) && ((simStateData = bodyCore.getSimStateData(false)) != NULL) ) + { + VelocityMod* velmod = simStateData->getVelocityModData(); + + //we don't have support for articulation yet + if (updatedBodySims) + { + updatedBodySims[index] = &getLowLevelBody(); + updatedBodyNodeIndices[index++] = getNodeIndex().index(); + } + + if(velDirty) + { + linAcc = velmod->getLinearVelModPerStep(); + angAcc = velmod->getAngularVelModPerStep(); + + if (useAccelerations) + { + acceleration->linear = linAcc / dt; + acceleration->angular = angAcc / dt; + + } + else + { + getBodyCore().updateVelocities(linAcc, angAcc); + } + } + + if (accDirty) + { + linAcc = velmod->getLinearVelModPerSec(); + angAcc = velmod->getAngularVelModPerSec(); + + if (acceleration) + { + acceleration->linear = linAcc; + acceleration->angular = angAcc; + } + else + { + PxReal scale = dt; + if (simUsesAdaptiveForce) + { + if (getScene().getSimpleIslandManager()->getAccurateIslandSim().getIslandStaticTouchCount(mNodeIndex) != 0) + { + scale *= mLLBody.accelScale; + } + } + getBodyCore().updateVelocities(linAcc*scale, angAcc*scale); + } + } + } + + setForcesToDefaults(readVelocityModFlag(VMF_ACC_DIRTY)); +} + +void BodySim::onConstraintDetach() +{ + PX_ASSERT(readInternalFlag(BF_HAS_CONSTRAINTS)); + + PxU32 size = getActorInteractionCount(); + Interaction** interactions = getActorInteractions(); + unregisterCountedInteraction(); + + while(size--) + { + const Interaction* interaction = *interactions++; + if(interaction->getType() == InteractionType::eCONSTRAINTSHADER) + return; + } + + clearInternalFlag(BF_HAS_CONSTRAINTS); // There are no other constraint interactions left +} + +void BodySim::setArticulation(ArticulationSim* a, PxReal wakeCounter, bool asleep, PxU32 bodyIndex) +{ + mArticulation = a; + if(a) + { + IG::NodeIndex index = mArticulation->getIslandNodeIndex(); + mNodeIndex.setIndices(index.index(), bodyIndex); + getBodyCore().setWakeCounterFromSim(wakeCounter); + + if (getFlagsFast() & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + getScene().setSpeculativeCCDArticulationLink(mNodeIndex.index()); + + if (!asleep) + { + setActive(true); + notifyWakeUp(false); + } + else + { + notifyReadyForSleeping(); + notifyPutToSleep(); + setActive(false); + } + } + else + { + //Setting a 1 in the articulation ID to avoid returning the node Index to the node index + //manager + mNodeIndex.setIndices(IG_INVALID_NODE, 1); + } +} + +void BodySim::createSqBounds() +{ + if(!isActive() || usingSqKinematicTarget() || readInternalFlag(BF_IS_COMPOUND_RIGID)) + return; + + PX_ASSERT(!isFrozen()); + + ElementSim* current = getElements_(); + while(current) + { + static_cast(current)->createSqBounds(); + current = current->mNextInActor; + } +} + +void BodySim::destroySqBounds() +{ + ElementSim* current = getElements_(); + while(current) + { + static_cast(current)->destroySqBounds(); + current = current->mNextInActor; + } +} + +void BodySim::freezeTransforms(Cm::BitMapPinned* shapeChangedMap) +{ + ElementSim* current = getElements_(); + while(current) + { + ShapeSim* sim = static_cast(current); + sim->updateCached(PxsTransformFlag::eFROZEN, shapeChangedMap); + sim->destroySqBounds(); + current = current->mNextInActor; + } +} + +void BodySim::disableCompound() +{ + if(isActive()) + getScene().removeFromActiveCompoundBodyList(*this); + clearInternalFlag(BF_IS_COMPOUND_RIGID); +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h new file mode 100644 index 000000000..f72f73af3 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h @@ -0,0 +1,333 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_BODYSIM +#define PX_PHYSICS_SCP_BODYSIM + +#include "PsUtilities.h" +#include "PsIntrinsics.h" +#include "ScRigidSim.h" +#include "PxvDynamics.h" +#include "ScBodyCore.h" +#include "ScSimStateData.h" +#include "ScConstraintGroupNode.h" +#include "PxRigidDynamic.h" +#include "DyArticulation.h" +#include "PxsRigidBody.h" +#include "PxsSimpleIslandManager.h" + +namespace physx +{ +namespace Bp +{ + class BoundsArray; +} + class PxsTransformCache; + class PxsSimulationController; +namespace Sc +{ + #define SC_NOT_IN_SCENE_INDEX 0xffffffff // the body is not in the scene yet + #define SC_NOT_IN_ACTIVE_LIST_INDEX 0xfffffffe // the body is in the scene but not in the active list + + class Scene; + class ArticulationSim; + + static const PxReal ScInternalWakeCounterResetValue = 20.0f*0.02f; + + class BodySim : public RigidSim + { + public: + enum InternalFlags + { + //BF_DISABLE_GRAVITY = 1 << 0, // Don't apply the scene's gravity + + BF_HAS_STATIC_TOUCH = 1 << 1, // Set when a body is part of an island with static contacts. Needed to be able to recalculate adaptive force if this changes + BF_KINEMATIC_MOVED = 1 << 2, // Set when the kinematic was moved + + BF_ON_DEATHROW = 1 << 3, // Set when the body is destroyed + + BF_IS_IN_SLEEP_LIST = 1 << 4, // Set when the body is added to the list of bodies which were put to sleep + BF_IS_IN_WAKEUP_LIST = 1 << 5, // Set when the body is added to the list of bodies which were woken up + BF_SLEEP_NOTIFY = 1 << 6, // A sleep notification should be sent for this body (and not a wakeup event, even if the body is part of the woken list as well) + BF_WAKEUP_NOTIFY = 1 << 7, // A wake up notification should be sent for this body (and not a sleep event, even if the body is part of the sleep list as well) + + BF_HAS_CONSTRAINTS = 1 << 8, // Set if the body has one or more constraints + BF_KINEMATIC_SETTLING = 1 << 9, // Set when the body was moved kinematically last frame + BF_KINEMATIC_SETTLING_2 = 1 << 10, + BF_KINEMATIC_MOVE_FLAGS = BF_KINEMATIC_MOVED | BF_KINEMATIC_SETTLING | BF_KINEMATIC_SETTLING_2, //Used to clear kinematic masks in 1 call + BF_KINEMATIC_SURFACE_VELOCITY = 1 << 11, //Set when the application calls setKinematicVelocity. Actor remains awake until application calls clearKinematicVelocity. + BF_IS_COMPOUND_RIGID = 1 << 12 // Set when the body is a compound actor, we dont want to set the sq bounds + + // PT: WARNING: flags stored on 16-bits now. + }; + + public: + BodySim(Scene&, BodyCore&, bool); + virtual ~BodySim(); + + void notifyAddSpatialAcceleration(); + void notifyClearSpatialAcceleration(); + void notifyAddSpatialVelocity(); + void notifyClearSpatialVelocity(); + void updateCached(Cm::BitMapPinned* shapeChangedMap); + void updateCached(PxsTransformCache& transformCache, Bp::BoundsArray& boundsArray); + void updateContactDistance(PxReal* contactDistance, const PxReal dt, Bp::BoundsArray& boundsArray); + + // hooks for actions in body core when it's attached to a sim object. Generally + // we get called after the attribute changed. + + virtual void postActorFlagChange(PxU32 oldFlags, PxU32 newFlags); + void postBody2WorldChange(); + void postSetWakeCounter(PxReal t, bool forceWakeUp); + void postSetKinematicTarget(); + void postSwitchToKinematic(); + void postSwitchToDynamic(); + void postPosePreviewChange(const PxU32 posePreviewFlag); // called when PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW changes + + PX_FORCE_INLINE const PxTransform& getBody2World() const { return getBodyCore().getCore().body2World; } + PX_FORCE_INLINE const PxTransform& getBody2Actor() const { return getBodyCore().getCore().getBody2Actor(); } + PX_FORCE_INLINE const PxsRigidBody& getLowLevelBody() const { return mLLBody; } + PX_FORCE_INLINE PxsRigidBody& getLowLevelBody() { return mLLBody; } + void wakeUp(); // note: for user API call purposes only, i.e., use from BodyCore. For simulation internal purposes there is internalWakeUp(). + void putToSleep(); + + void disableCompound(); + + static PxU32 getRigidBodyOffset() { return PxU32(PX_OFFSET_OF_RT(BodySim, mLLBody));} + + private: + void activate(); + void deactivate(); + + //--------------------------------------------------------------------------------- + // Constraint projection + //--------------------------------------------------------------------------------- + public: + PX_FORCE_INLINE ConstraintGroupNode* getConstraintGroup() { return mConstraintGroup; } + PX_FORCE_INLINE void setConstraintGroup(ConstraintGroupNode* node) { mConstraintGroup = node; } + + //// A list of active projection trees in the scene might be better + //PX_FORCE_INLINE void projectPose() { PX_ASSERT(mConstraintGroup); ConstraintGroupNode::projectPose(*mConstraintGroup); } + + //--------------------------------------------------------------------------------- + // Kinematics + //--------------------------------------------------------------------------------- + public: + PX_FORCE_INLINE bool isKinematic() const { return getBodyCore().getFlags() & PxRigidBodyFlag::eKINEMATIC; } + PX_FORCE_INLINE bool isArticulationLink() const { return getActorType() == PxActorType::eARTICULATION_LINK; } + void calculateKinematicVelocity(PxReal oneOverDt); + void updateKinematicPose(); + bool deactivateKinematic(); + private: + PX_FORCE_INLINE void initKinematicStateBase(BodyCore&, bool asPartOfCreation); + + //--------------------------------------------------------------------------------- + // Sleeping + //--------------------------------------------------------------------------------- + public: + PX_FORCE_INLINE bool isActive() const { return (mActiveListIndex < SC_NOT_IN_ACTIVE_LIST_INDEX); } + void setActive(bool active, PxU32 infoFlag=0); // see ActivityChangeInfoFlag + + PX_FORCE_INLINE PxU32 getActiveListIndex() const { return mActiveListIndex; } // if the body is active, the index is smaller than SC_NOT_IN_ACTIVE_LIST_INDEX + PX_FORCE_INLINE void setActiveListIndex(PxU32 index) { mActiveListIndex = index; } + + PX_FORCE_INLINE PxU32 getActiveCompoundListIndex() const { return mActiveCompoundListIndex; } // if the body is active and is compound, the index is smaller than SC_NOT_IN_ACTIVE_LIST_INDEX + PX_FORCE_INLINE void setActiveCompoundListIndex(PxU32 index) { mActiveCompoundListIndex = index; } + + void internalWakeUp(PxReal wakeCounterValue=ScInternalWakeCounterResetValue); + void internalWakeUpArticulationLink(PxReal wakeCounterValue); // called by ArticulationSim to wake up this link + + PxReal updateWakeCounter(PxReal dt, PxReal energyThreshold, const Cm::SpatialVector& motionVelocity); + + void resetSleepFilter(); + void notifyReadyForSleeping(); // inform the sleep island generation system that the body is ready for sleeping + void notifyNotReadyForSleeping(); // inform the sleep island generation system that the body is not ready for sleeping + PX_FORCE_INLINE bool checkSleepReadinessBesidesWakeCounter(); // for API triggered changes to test sleep readiness + + PX_FORCE_INLINE void registerCountedInteraction() { mLLBody.getCore().numCountedInteractions++; PX_ASSERT(mLLBody.getCore().numCountedInteractions); } + PX_FORCE_INLINE void unregisterCountedInteraction() { PX_ASSERT(mLLBody.getCore().numCountedInteractions); mLLBody.getCore().numCountedInteractions--;} + PX_FORCE_INLINE PxU32 getNumCountedInteractions() const { return mLLBody.getCore().numCountedInteractions; } + + PX_FORCE_INLINE Ps::IntBool isFrozen() const { return Ps::IntBool(mLLBody.mInternalFlags & PxsRigidBody::eFROZEN); } + private: + PX_FORCE_INLINE void notifyWakeUp(bool wakeUpInIslandGen = false); // inform the sleep island generation system that the object got woken up + PX_FORCE_INLINE void notifyPutToSleep(); // inform the sleep island generation system that the object was put to sleep + PX_FORCE_INLINE void internalWakeUpBase(PxReal wakeCounterValue); + + //--------------------------------------------------------------------------------- + // External velocity changes + //--------------------------------------------------------------------------------- + public: + void updateForces(PxReal dt, PxsRigidBody** updatedBodySims, PxU32* updatedBodyNodeIndices, + PxU32& index, Cm::SpatialVector* acceleration, const bool useAcceleration, bool simUsesAdaptiveForce); + private: + PX_FORCE_INLINE void raiseVelocityModFlag(VelocityModFlags f) { mVelModState |= f; } + PX_FORCE_INLINE void clearVelocityModFlag(VelocityModFlags f) { mVelModState &= ~f; } + PX_FORCE_INLINE bool readVelocityModFlag(VelocityModFlags f) { return (mVelModState & f) != 0; } + PX_FORCE_INLINE void setForcesToDefaults(bool enableGravity); + + //--------------------------------------------------------------------------------- + // Miscellaneous + //--------------------------------------------------------------------------------- + public: + PX_FORCE_INLINE PxU16 getInternalFlag() const { return mInternalFlags; } + PX_FORCE_INLINE PxU16 readInternalFlag(InternalFlags flag) const { return PxU16(mInternalFlags & flag); } + PX_FORCE_INLINE void raiseInternalFlag(InternalFlags flag) { mInternalFlags |= flag; } + PX_FORCE_INLINE void clearInternalFlag(InternalFlags flag) { mInternalFlags &= ~flag; } + PX_FORCE_INLINE PxU32 getFlagsFast() const { return getBodyCore().getFlags(); } + + PX_FORCE_INLINE void incrementBodyConstraintCounter() { mLLBody.mCore->numBodyInteractions++; } + PX_FORCE_INLINE void decrementBodyConstraintCounter() { PX_ASSERT(mLLBody.mCore->numBodyInteractions>0); mLLBody.mCore->numBodyInteractions--; } + + PX_FORCE_INLINE BodyCore& getBodyCore() const { return static_cast(getRigidCore()); } + + PX_INLINE ArticulationSim* getArticulation() const { return mArticulation; } + void setArticulation(ArticulationSim* a, PxReal wakeCounter, bool asleep, PxU32 bodyIndex); + + PX_FORCE_INLINE IG::NodeIndex getNodeIndex() const { return mNodeIndex; } + + PX_FORCE_INLINE void onConstraintAttach() { raiseInternalFlag(BF_HAS_CONSTRAINTS); registerCountedInteraction(); } + void onConstraintDetach(); + + PX_FORCE_INLINE void onOriginShift(const PxVec3& shift) { mLLBody.mLastTransform.p -= shift; } + + PX_FORCE_INLINE bool notInScene() const { return mActiveListIndex == SC_NOT_IN_SCENE_INDEX; } + + PX_FORCE_INLINE bool usingSqKinematicTarget() const + { + PxU32 ktFlags(PxRigidBodyFlag::eUSE_KINEMATIC_TARGET_FOR_SCENE_QUERIES | PxRigidBodyFlag::eKINEMATIC); + return (getFlagsFast()&ktFlags) == ktFlags; + } + + PX_FORCE_INLINE PxU32 getNbShapes() const { return mElementCount; } + + void createSqBounds(); + void destroySqBounds(); + void freezeTransforms(Cm::BitMapPinned* shapeChangedMap); + void invalidateSqBounds(); + private: + + //--------------------------------------------------------------------------------- + // Base body + //--------------------------------------------------------------------------------- + PxsRigidBody mLLBody; + + //--------------------------------------------------------------------------------- + // Island manager + //--------------------------------------------------------------------------------- + IG::NodeIndex mNodeIndex; + + //--------------------------------------------------------------------------------- + // External velocity changes + //--------------------------------------------------------------------------------- + // VelocityMod data allocated on the fly when the user applies velocity changes + // which need to be accumulated. + // VelMod dirty flags stored in BodySim so we can save ourselves the expense of looking at + // the separate velmod data if no forces have been set. + PxU16 mInternalFlags; + PxU8 mVelModState; + + //--------------------------------------------------------------------------------- + // Sleeping + //--------------------------------------------------------------------------------- + PxU32 mActiveListIndex; // Used by Scene to track active bodies + PxU32 mActiveCompoundListIndex; // Used by Scene to track active compound bodies + + //--------------------------------------------------------------------------------- + // Articulation + //--------------------------------------------------------------------------------- + ArticulationSim* mArticulation; // NULL if not in an articulation + + //--------------------------------------------------------------------------------- + // Joints & joint groups + //--------------------------------------------------------------------------------- + + // This is a tree data structure that gives us the projection order of joints in which this body is the tree root. + // note: the link of the root body is not necces. the root link due to the re-rooting of the articulation! + ConstraintGroupNode* mConstraintGroup; + }; + +} // namespace Sc + +PX_FORCE_INLINE void Sc::BodySim::setForcesToDefaults(bool enableGravity) +{ + if (!(mLLBody.mCore->mFlags & PxRigidBodyFlag::eRETAIN_ACCELERATIONS)) + { + SimStateData* simStateData = getBodyCore().getSimStateData(false); + if(simStateData) + { + VelocityMod* velmod = simStateData->getVelocityModData(); + velmod->clear(); + } + + if (enableGravity) + mVelModState = VMF_GRAVITY_DIRTY; // We want to keep the gravity flag to make sure the acceleration gets changed to gravity-only + // in the next step (unless the application adds new forces of course) + else + mVelModState = 0; + } + else + { + SimStateData* simStateData = getBodyCore().getSimStateData(false); + if (simStateData) + { + VelocityMod* velmod = simStateData->getVelocityModData(); + velmod->clearPerStep(); + } + + mVelModState &= (~(VMF_VEL_DIRTY)); + } +} + +PX_FORCE_INLINE bool Sc::BodySim::checkSleepReadinessBesidesWakeCounter() +{ + const BodyCore& bodyCore = getBodyCore(); + const SimStateData* simStateData = bodyCore.getSimStateData(false); + const VelocityMod* velmod = simStateData ? simStateData->getVelocityModData() : NULL; + + bool readyForSleep = bodyCore.getLinearVelocity().isZero() && bodyCore.getAngularVelocity().isZero(); + if (readVelocityModFlag(VMF_ACC_DIRTY)) + { + readyForSleep = readyForSleep && (!velmod || velmod->getLinearVelModPerSec().isZero()); + readyForSleep = readyForSleep && (!velmod || velmod->getAngularVelModPerSec().isZero()); + } + if (readVelocityModFlag(VMF_VEL_DIRTY)) + { + readyForSleep = readyForSleep && (!velmod || velmod->getLinearVelModPerStep().isZero()); + readyForSleep = readyForSleep && (!velmod || velmod->getAngularVelModPerStep().isZero()); + } + + return readyForSleep; +} + + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScClient.h b/src/PhysX/physx/source/simulationcontroller/src/ScClient.h new file mode 100644 index 000000000..f19bc3154 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScClient.h @@ -0,0 +1,53 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_CLIENT +#define PX_PHYSICS_CLIENT + +#include "PxScene.h" +#include "CmPhysXCommon.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Sc +{ + class Client : public Ps::UserAllocated + { + public: + Client() + {} + + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp new file mode 100644 index 000000000..55009e5e7 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp @@ -0,0 +1,149 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsFoundation.h" + +#include "ScPhysics.h" +#include "ScBodyCore.h" +#include "ScConstraintCore.h" +#include "ScConstraintSim.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Sc::ConstraintCore::ConstraintCore(PxConstraintConnector& connector, const PxConstraintShaderTable& shaders, PxU32 dataSize) +: mFlags(PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES) +, mAppliedForce(PxVec3(0)) +, mAppliedTorque(PxVec3(0)) +, mConnector(&connector) +, mProject(shaders.project) +, mSolverPrep(shaders.solverPrep) +, mVisualize(shaders.visualize) +, mDataSize(dataSize) +, mLinearBreakForce(PX_MAX_F32) +, mAngularBreakForce(PX_MAX_F32) +, mMinResponseThreshold(0) +, mSim(NULL) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Sc::ConstraintCore::~ConstraintCore() +{ +} + +void Sc::ConstraintCore::setFlags(PxConstraintFlags flags) +{ + PxConstraintFlags old = mFlags; + flags = flags | (old & PxConstraintFlag::eGPU_COMPATIBLE); + if(flags != old) + { + mFlags = flags; + if(getSim()) + getSim()->postFlagChange(old, flags); + } +} + +void Sc::ConstraintCore::getForce(PxVec3& force, PxVec3& torque) const +{ + if(!mSim) + { + force = PxVec3(0,0,0); + torque = PxVec3(0,0,0); + } + else + mSim->getForce(force, torque); + +} + +void Sc::ConstraintCore::setBodies(RigidCore* r0v, RigidCore* r1v) +{ + if(mSim) + mSim->postBodiesChange(r0v, r1v); +} + +bool Sc::ConstraintCore::updateConstants(void* addr) +{ + if (getSim()) + { + getSim()->setConstantsLL(addr); + return true; + } + return false; +} + +void Sc::ConstraintCore::setBreakForce(PxReal linear, PxReal angular) +{ + mLinearBreakForce = linear; + mAngularBreakForce = angular; + + if (getSim()) + getSim()->setBreakForceLL(linear, angular); +} + +void Sc::ConstraintCore::getBreakForce(PxReal& linear, PxReal& angular) const +{ + linear = mLinearBreakForce; + angular = mAngularBreakForce; +} + +void Sc::ConstraintCore::setMinResponseThreshold(PxReal threshold) +{ + mMinResponseThreshold = threshold; + + if (getSim()) + getSim()->setMinResponseThresholdLL(threshold); +} + +PxConstraint* Sc::ConstraintCore::getPxConstraint() +{ + return gOffsetTable.convertScConstraint2Px(this); +} + +const PxConstraint* Sc::ConstraintCore::getPxConstraint() const +{ + return gOffsetTable.convertScConstraint2Px(this); +} + +void Sc::ConstraintCore::breakApart() +{ + // TODO: probably want to do something with the interaction here + // as well as remove the constraint from LL. + + mFlags |= PxConstraintFlag::eBROKEN; +} + +void Sc::ConstraintCore::prepareForSetBodies() +{ + if(mSim) + mSim->preBodiesChange(); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp new file mode 100644 index 000000000..9177013ff --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp @@ -0,0 +1,138 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScConstraintGroupNode.h" +#include "ScConstraintProjectionManager.h" +#include "PsFoundation.h" +#include "ScBodySim.h" +#include "ScConstraintSim.h" +#include "ScConstraintInteraction.h" + +using namespace physx; + +Sc::ConstraintGroupNode::ConstraintGroupNode(BodySim& b) : + body(&b), + parent(this), + tail(this), + rank(0), + next(NULL), + + projectionFirstRoot(NULL), + projectionNextRoot(NULL), + projectionParent(NULL), + projectionFirstChild(NULL), + projectionNextSibling(NULL), + projectionConstraint(NULL), + + flags(0) +{ +} + + +// +// Implementation of FIND of +// UNION-FIND algo. +// +Sc::ConstraintGroupNode& Sc::ConstraintGroupNode::getRoot() +{ + PX_ASSERT(parent); + + ConstraintGroupNode* root = parent; + + if (root->parent == root) + return *root; + else + { + PxU32 nbHops = 1; + root = root->parent; + + while(root != root->parent) + { + root = root->parent; + nbHops++; + } + + // Write root to all nodes on the path + ConstraintGroupNode* curr = this; + while(nbHops) + { + ConstraintGroupNode* n = curr->parent; + curr->parent = root; + curr = n; + nbHops--; + } + + return *root; + } +} + + +void Sc::ConstraintGroupNode::markForProjectionTreeRebuild(ConstraintProjectionManager& cpManager) +{ + ConstraintGroupNode& root = getRoot(); + if (!root.readFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE)) + { + cpManager.addToPendingTreeUpdates(root); + } +} + + +void Sc::ConstraintGroupNode::initProjectionData(ConstraintGroupNode* parent_, ConstraintSim* c) +{ + projectionConstraint = c; + + //add us to parent's child list: + if (parent_) + { + projectionNextSibling = parent_->projectionFirstChild; + parent_->projectionFirstChild = this; + + projectionParent = parent_; + } +} + + +void Sc::ConstraintGroupNode::clearProjectionData() +{ + projectionFirstRoot = NULL; + projectionNextRoot = NULL; + projectionParent = NULL; + projectionFirstChild = NULL; + projectionNextSibling = NULL; + projectionConstraint = NULL; +} + + +void Sc::ConstraintGroupNode::projectPose(ConstraintGroupNode& node, Ps::Array& projectedBodies) +{ + PX_ASSERT(node.hasProjectionTreeRoot()); + + Sc::ConstraintProjectionTree::projectPose(node, projectedBodies); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h new file mode 100644 index 000000000..144ffaecc --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h @@ -0,0 +1,178 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_CONSTRAINT_GROUP_NODE +#define PX_PHYSICS_SCP_CONSTRAINT_GROUP_NODE + +#include "ScConstraintProjectionTree.h" +#include "PsUtilities.h" // for Ps::to8() + +namespace physx +{ +namespace Sc +{ + class ConstraintSim; + class BodySim; + class ConstraintProjectionManager; + + // A 'simulation island' of constraints. Created by a union-find algorithm every time a new constraint is added to any of the involved bodies. + struct ConstraintGroupNode : public Ps::UserAllocated + { + enum StateFlags + { + eDISCOVERED = 1 << 0, // Used during projection tree generation to mark processed nodes. + eIN_PROJECTION_PASS_LIST = 1 << 1, // Temporarily used to avoid duplicate entries in the list of nodes that should project the pose after the solver + ePENDING_TREE_UPDATE = 1 << 2, // Marks the constraint groups that need their projection trees updated. Must only be set on the root group node. + eNEXT_FREE_SHIFT = 3, + eNEXT_FREE = 1 << eNEXT_FREE_SHIFT + }; + + // these flags should give a rough hint how many projecting constraints to expect in the constraint group. This will be used for + // load balancing when running projection in parallel. The intervals were chosen somewhat arbitrarily but the general motivation was + // to cover very simple constraint setups, simple ragdolls, complex ragdolls and very complex projection setups. Note that the load + // balancing is not waterproof since at the end it is the projection shader from the external constraint implementer (for example, a joint) + // which decides based on some thresholds whether projection runs or not. + enum ProjectionCountHintFlags + { + e1_TO_4 = eNEXT_FREE, + e5_TO_16 = eNEXT_FREE << 1, + e17_TO_64 = eNEXT_FREE << 2, + e65_TO_INF = eNEXT_FREE << 3, + eCLEAR_MASK = ~(0xffffffff << eNEXT_FREE_SHIFT) + }; + + ConstraintGroupNode(BodySim& b); + ~ConstraintGroupNode() + { + PX_ASSERT(!readFlag(ePENDING_TREE_UPDATE)); + PX_ASSERT(projectionFirstRoot == NULL); + } + + PX_FORCE_INLINE void raiseFlag(StateFlags f) { flags |= f; } + PX_FORCE_INLINE void clearFlag(StateFlags f) { flags &= ~f; } + PX_FORCE_INLINE bool readFlag(StateFlags f) const { return (flags & f) != 0; } + PX_FORCE_INLINE PxU32 getProjectionCountHint() const; + PX_FORCE_INLINE void setProjectionCountHint(PxU32 constraintsToProjectCount); + + ConstraintGroupNode& getRoot(); + + PX_FORCE_INLINE void buildProjectionTrees(); //build the projection trees for a constraint group. + void markForProjectionTreeRebuild(ConstraintProjectionManager&); + PX_FORCE_INLINE void purgeProjectionTrees(); + PX_FORCE_INLINE bool hasProjectionTreeRoot() { return projectionFirstRoot != NULL; } + PX_FORCE_INLINE void setProjectionTreeRoot(ConstraintGroupNode* root) { projectionFirstRoot = root; } + + void initProjectionData(ConstraintGroupNode* parent, ConstraintSim* c); + void clearProjectionData(); + + static void projectPose(ConstraintGroupNode& root, Ps::Array& projectedBodies); + + + BodySim* body; //the owner body of this node + + //tree for union/find: + ConstraintGroupNode* parent; + ConstraintGroupNode* tail; //only valid if this is root of group, points to LList tail node. + PxU32 rank; //rank counter for union/find. Initially zero. Is number of hops from root to furthest leaf in tree. This is just a hint to create more balanced trees. + + //linked list for traversal: + ConstraintGroupNode* next; //next in list, NULL at tail. + + //projection tree information + ConstraintGroupNode* projectionFirstRoot; //pointer to first projection tree root node. Only set for constraint group roots + ConstraintGroupNode* projectionNextRoot; //pointer to next projection root node. Only set for constraint group roots + //a constraint group can consist of multiple projection trees if kinematics are involved! Because a kinematic doesn't split + //the constraint group as a static anchor does. + ConstraintGroupNode* projectionParent; //node to project to + ConstraintGroupNode* projectionFirstChild; //first node which gets projected to this one + ConstraintGroupNode* projectionNextSibling; //the next sibling which gets projected to the same node as this one. NULL if projectionParent is NULL. + ConstraintSim* projectionConstraint; //the constraint to project (constraint to projection parent) + + private: + PxU8 flags; + }; + +} // namespace Sc + + +PX_FORCE_INLINE PxU32 Sc::ConstraintGroupNode::getProjectionCountHint() const +{ + // return the mean of the upper and lower bound + + if (flags & ConstraintGroupNode::e65_TO_INF) + return 128; + else if (flags & ConstraintGroupNode::e17_TO_64) + return 40; + else if (flags & ConstraintGroupNode::e5_TO_16) + return 10; + else if (flags & ConstraintGroupNode::e1_TO_4) + return 2; + + return 0; +} + + +PX_FORCE_INLINE void Sc::ConstraintGroupNode::setProjectionCountHint(PxU32 constraintsToProjectCount) +{ + PxU8 tmpFlags = flags; + tmpFlags &= PxU8(ConstraintGroupNode::eCLEAR_MASK); + + if (constraintsToProjectCount >= 65) + tmpFlags |= ConstraintGroupNode::e65_TO_INF; + else if (constraintsToProjectCount >= 17) + tmpFlags |= ConstraintGroupNode::e17_TO_64; + else if (constraintsToProjectCount >= 5) + tmpFlags |= ConstraintGroupNode::e5_TO_16; + else if (constraintsToProjectCount >= 1) + tmpFlags |= ConstraintGroupNode::e1_TO_4; + + flags = tmpFlags; +} + + +PX_FORCE_INLINE void Sc::ConstraintGroupNode::buildProjectionTrees() +{ + PX_ASSERT(this == parent); // Only call for group roots + PX_ASSERT(!hasProjectionTreeRoot()); + + ConstraintProjectionTree::buildProjectionTrees(*this); +} + + +PX_FORCE_INLINE void Sc::ConstraintGroupNode::purgeProjectionTrees() +{ + PX_ASSERT(this == parent); // Only call for group roots + PX_ASSERT(hasProjectionTreeRoot()); + ConstraintProjectionTree::purgeProjectionTrees(*this); +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp new file mode 100644 index 000000000..c2562d8d3 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp @@ -0,0 +1,169 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScConstraintInteraction.h" +#include "ScConstraintSim.h" +#include "ScBodySim.h" +#include "ScScene.h" +#include "PxsRigidBody.h" +#include "PxsSimpleIslandManager.h" + +using namespace physx; +using namespace Sc; + +ConstraintInteraction::ConstraintInteraction(ConstraintSim* constraint, RigidSim& r0, RigidSim& r1) : + Interaction (r0, r1, InteractionType::eCONSTRAINTSHADER, InteractionFlag::eCONSTRAINT), + mConstraint (constraint) +{ + registerInActors(); + + BodySim* b0 = mConstraint->getBody(0); + BodySim* b1 = mConstraint->getBody(1); + + if(b0) + b0->onConstraintAttach(); + if(b1) + b1->onConstraintAttach(); + + IG::SimpleIslandManager* simpleIslandManager = getScene().getSimpleIslandManager(); + mEdgeIndex = simpleIslandManager->addConstraint(&mConstraint->getLowLevelConstraint(), b0 ? b0->getNodeIndex() : IG::NodeIndex(), b1 ? b1->getNodeIndex() : IG::NodeIndex(), this); +} + +ConstraintInteraction::~ConstraintInteraction() +{ + PX_ASSERT(!readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)); + PX_ASSERT(!getDirtyFlags()); + PX_ASSERT(!mConstraint->readFlag(ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED)); +} + +static PX_FORCE_INLINE void removeFromActiveBreakableList(ConstraintSim* constraint, Scene& s) +{ + if(constraint->readFlag(ConstraintSim::eBREAKABLE | ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED) == (ConstraintSim::eBREAKABLE | ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED)) + s.removeActiveBreakableConstraint(constraint); +} + +void ConstraintInteraction::destroy() +{ + setClean(true); // removes the pair from the dirty interaction list etc. + + Scene& scene = getScene(); + + removeFromActiveBreakableList(mConstraint, scene); + + if(mEdgeIndex != IG_INVALID_EDGE) + scene.getSimpleIslandManager()->removeConnection(mEdgeIndex); + mEdgeIndex = IG_INVALID_EDGE; + + unregisterFromActors(); + + BodySim* b0 = mConstraint->getBody(0); + BodySim* b1 = mConstraint->getBody(1); + + if(b0) + b0->onConstraintDetach(); // Note: Has to be done AFTER the interaction has unregistered from the actors + if(b1) + b1->onConstraintDetach(); // Note: Has to be done AFTER the interaction has unregistered from the actors + + clearInteractionFlag(InteractionFlag::eIS_ACTIVE); // ensures that broken constraints do not go into the list of active breakable constraints anymore +} + +void ConstraintInteraction::updateState() +{ + PX_ASSERT(!mConstraint->isBroken()); + PX_ASSERT(getDirtyFlags() & InteractionDirtyFlag::eBODY_KINEMATIC); // at the moment this should be the only reason for this method being called + + // at least one of the bodies got switched from kinematic to dynamic. This will not have changed the sleep state of the interactions, so the + // constraint interactions are just marked dirty and processed as part of the dirty interaction update system. + // + // -> need to check whether to activate the constraint and whether constraint break testing + // is now necessary + // + // the transition from dynamic to kinematic will always trigger an onDeactivate_() (because the body gets deactivated) + // and thus there is no need to consider that case here. + // + + onActivate_(NULL); // note: this will not activate if the necessary conditions are not met, so it can be called even if the pair has been deactivated again before the + // simulation step started +} + +bool ConstraintInteraction::onActivate_(void*) +{ + PX_ASSERT(!mConstraint->isBroken()); + + BodySim* b0 = mConstraint->getBody(0); + BodySim* b1 = mConstraint->getBody(1); + + const bool b0Vote = !b0 || b0->isActive(); + const bool b1Vote = !b1 || b1->isActive(); + + const bool b0Dynamic = b0 && (!b0->isKinematic()); + const bool b1Dynamic = b1 && (!b1->isKinematic()); + + // + // note: constraints between kinematics and kinematics/statics are always inactive and must not be activated + // + if((b0Vote || b1Vote) && (b0Dynamic || b1Dynamic)) + { + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + + if(mConstraint->readFlag(ConstraintSim::eBREAKABLE | ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED) == ConstraintSim::eBREAKABLE) + getScene().addActiveBreakableConstraint(mConstraint, this); + + return true; + } + else + return false; +} + +bool ConstraintInteraction::onDeactivate_() +{ + const BodySim* b0 = mConstraint->getBody(0); + const BodySim* b1 = mConstraint->getBody(1); + + const bool b0Dynamic = b0 && (!b0->isKinematic()); + const bool b1Dynamic = b1 && (!b1->isKinematic()); + + PX_ASSERT( (!b0 && b1 && !b1->isActive()) || + (!b1 && b0 && !b0->isActive()) || + ((b0 && b1 && (!b0->isActive() || !b1->isActive()))) ); + + // + // note: constraints between kinematics and kinematics/statics should always get deactivated + // + if(((!b0 || !b0->isActive()) && (!b1 || !b1->isActive())) || (!b0Dynamic && !b1Dynamic)) + { + removeFromActiveBreakableList(mConstraint, getScene()); + + clearInteractionFlag(InteractionFlag::eIS_ACTIVE); + + return true; + } + else + return false; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h new file mode 100644 index 000000000..b6d38d795 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h @@ -0,0 +1,66 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_CONSTRAINTSHADERINTERACTION +#define PX_PHYSICS_SCP_CONSTRAINTSHADERINTERACTION + +#include "ScInteraction.h" + +namespace physx +{ +namespace Sc +{ + class ConstraintSim; + class RigidSim; + + class ConstraintInteraction : public Interaction + { + public: + ConstraintInteraction(ConstraintSim* shader, RigidSim& r0, RigidSim& r1); + ~ConstraintInteraction(); + + bool onActivate_(void* data); + bool onDeactivate_(); + + void updateState(); + void destroy(); // disables the interaction and unregisters from the system. Does NOT delete the object. This is used on destruction but also when a constraint breaks. + + PX_FORCE_INLINE ConstraintSim* getConstraint() { return mConstraint; } + PX_FORCE_INLINE PxU32 getEdgeIndex() const { return mEdgeIndex; } + + private: + ConstraintSim* mConstraint; + PxU32 mEdgeIndex; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp new file mode 100644 index 000000000..4b69fd29c --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp @@ -0,0 +1,505 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PxcScratchAllocator.h" +#include "ScConstraintProjectionManager.h" +#include "ScBodySim.h" +#include "ScConstraintSim.h" +#include "ScConstraintInteraction.h" + +using namespace physx; + + +namespace physx +{ +namespace Sc +{ + +template +class ScratchAllocatorList +{ +private: + struct ElementBlock + { + PX_FORCE_INLINE ElementBlock() {} + PX_FORCE_INLINE void init(PxU32 countAtStart) { next = NULL; count = countAtStart; } + + ElementBlock* next; + PxU32 count; + T elements[elementsPerBlock]; + }; + + PX_FORCE_INLINE const ScratchAllocatorList& operator=(const ScratchAllocatorList&) {} + + +public: + class Iterator + { + friend class ScratchAllocatorList; + + public: + T const* getNext() + { + if (mCurrentBlock) + { + if (mIndex < mCurrentBlock->count) + { + return &mCurrentBlock->elements[mIndex++]; + } + else + { + if (mCurrentBlock->next) + { + PX_ASSERT(mCurrentBlock->count == elementsPerBlock); + mCurrentBlock = mCurrentBlock->next; + PX_ASSERT(mCurrentBlock->count > 0); + + mIndex = 1; + return &mCurrentBlock->elements[0]; + } + else + return NULL; + } + } + else + return NULL; + } + + private: + Iterator(const ElementBlock* startBlock) : mCurrentBlock(startBlock), mIndex(0) {} + + private: + const ElementBlock* mCurrentBlock; + PxU32 mIndex; + }; + + PX_FORCE_INLINE ScratchAllocatorList(PxcScratchAllocator& scratchAllocator) : mScratchAllocator(scratchAllocator) + { + mFirstBlock = reinterpret_cast(scratchAllocator.alloc(sizeof(ElementBlock), true)); + if (mFirstBlock) + mFirstBlock->init(0); + + mCurrentBlock = mFirstBlock; + } + + PX_FORCE_INLINE ~ScratchAllocatorList() + { + freeMemory(); + } + + PX_FORCE_INLINE bool add(const T& element) + { + if (mCurrentBlock) + { + if (mCurrentBlock->count < elementsPerBlock) + { + mCurrentBlock->elements[mCurrentBlock->count] = element; + mCurrentBlock->count++; + return true; + } + else + { + PX_ASSERT(mCurrentBlock->next == NULL); + PX_ASSERT(mCurrentBlock->count == elementsPerBlock); + + ElementBlock* newBlock = reinterpret_cast(mScratchAllocator.alloc(sizeof(ElementBlock), true)); + if (newBlock) + { + newBlock->init(1); + newBlock->elements[0] = element; + mCurrentBlock->next = newBlock; + mCurrentBlock = newBlock; + return true; + } + else + return false; + } + } + else + return false; + } + + PX_FORCE_INLINE Iterator getIterator() const + { + return Iterator(mFirstBlock); + } + + PX_FORCE_INLINE void freeMemory() + { + ElementBlock* block = mFirstBlock; + + while(block) + { + ElementBlock* blockToFree = block; + block = block->next; + + mScratchAllocator.free(blockToFree); + } + } + + +private: + PxcScratchAllocator& mScratchAllocator; + ElementBlock* mFirstBlock; + ElementBlock* mCurrentBlock; +}; + +} +} + + +Sc::ConstraintProjectionManager::ConstraintProjectionManager() : + mNodePool(PX_DEBUG_EXP("projectionNodePool")) +{ +} + + +void Sc::ConstraintProjectionManager::addToPendingGroupUpdates(Sc::ConstraintSim& s) +{ + PX_ASSERT(!s.readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)); + bool isNew = mPendingGroupUpdates.insert(&s); + PX_UNUSED(isNew); + PX_ASSERT(isNew); + + s.setFlag(ConstraintSim::ePENDING_GROUP_UPDATE); +} + + +void Sc::ConstraintProjectionManager::removeFromPendingGroupUpdates(Sc::ConstraintSim& s) +{ + PX_ASSERT(s.readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)); + bool didExist = mPendingGroupUpdates.erase(&s); + PX_UNUSED(didExist); + PX_ASSERT(didExist); + + s.clearFlag(ConstraintSim::ePENDING_GROUP_UPDATE); +} + + +void Sc::ConstraintProjectionManager::addToPendingTreeUpdates(ConstraintGroupNode& n) +{ + PX_ASSERT(&n == &n.getRoot()); + PX_ASSERT(!n.readFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE)); + bool isNew = mPendingTreeUpdates.insert(&n); + PX_UNUSED(isNew); + PX_ASSERT(isNew); + + n.raiseFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE); +} + + +void Sc::ConstraintProjectionManager::removeFromPendingTreeUpdates(ConstraintGroupNode& n) +{ + PX_ASSERT(&n == &n.getRoot()); + PX_ASSERT(n.readFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE)); + bool didExist = mPendingTreeUpdates.erase(&n); + PX_UNUSED(didExist); + PX_ASSERT(didExist); + + n.clearFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE); +} + + +PX_INLINE Sc::ConstraintGroupNode* Sc::ConstraintProjectionManager::createGroupNode(BodySim& b) +{ + ConstraintGroupNode* n = mNodePool.construct(b); + b.setConstraintGroup(n); + return n; +} + + +// +// Implementation of UNION of +// UNION-FIND algo. +// It also updates the group traversal +// linked list. +// +void Sc::ConstraintProjectionManager::groupUnion(ConstraintGroupNode& root0, ConstraintGroupNode& root1) +{ + // Should only get called for the roots + PX_ASSERT(&root0 == root0.parent); + PX_ASSERT(&root1 == root1.parent); + + if (&root0 != &root1) //different groups? If not, its already merged. + { + //UNION(this, other); //union-find algo unites groups. + ConstraintGroupNode* newRoot; + ConstraintGroupNode* otherRoot; + if (root0.rank > root1.rank) + { + //hisGroup appended to mygroup. + newRoot = &root0; + otherRoot = &root1; + } + else + { + //myGroup appended to hisGroup. + newRoot = &root1; + otherRoot = &root0; + //there is a chance that the two ranks were equal, in which case the tree depth just increased. + root1.rank++; + } + + PX_ASSERT(newRoot->parent == newRoot); + otherRoot->parent = newRoot; + + //update traversal linked list: + newRoot->tail->next = otherRoot; + newRoot->tail = otherRoot->tail; + } +} + + +// +// Add a body to a constraint projection group. +// +void Sc::ConstraintProjectionManager::addToGroup(BodySim& b, BodySim* other, ConstraintSim& c) +{ + // If both bodies of the constraint are defined, we want to fetch the reference to the group root + // from body 0 by default (allows to avoid checking both) + PX_ASSERT(&b == c.getBody(0) || (c.getBody(0) == NULL && &b == c.getBody(1))); + PX_UNUSED(c); + + ConstraintGroupNode* myRoot; + if (!b.getConstraintGroup()) + myRoot = createGroupNode(b); + else + { + myRoot = &b.getConstraintGroup()->getRoot(); + if (myRoot->hasProjectionTreeRoot()) + myRoot->purgeProjectionTrees(); // If a new constraint gets added to a constraint group, projection trees need to be recreated + } + + if (other) + { + ConstraintGroupNode* otherRoot; + if (!other->getConstraintGroup()) + otherRoot = createGroupNode(*other); + else + { + otherRoot = &other->getConstraintGroup()->getRoot(); + if (otherRoot->hasProjectionTreeRoot()) + otherRoot->purgeProjectionTrees(); // If a new constraint gets added to a constraint group, projection trees need to be recreated + } + + //merge the two groups, if disjoint. + groupUnion(*myRoot, *otherRoot); + } +} + + +// +// Add all projection constraints connected to the specified body to the pending update list but +// ignore the specified constraint. +// +void Sc::ConstraintProjectionManager::markConnectedConstraintsForUpdate(BodySim& b, ConstraintSim* c) +{ + PxU32 size = b.getActorInteractionCount(); + Interaction** interactions = b.getActorInteractions(); + while(size--) + { + Interaction* interaction = *interactions++; + if (interaction->getType() == InteractionType::eCONSTRAINTSHADER) + { + ConstraintSim* ct = static_cast(interaction)->getConstraint(); + + if ((ct != c) && ct->needsProjection() && (!ct->readFlag(ConstraintSim::ePENDING_GROUP_UPDATE))) + { + //mark constraint for pending update: + addToPendingGroupUpdates(*ct); + } + } + } +} + + +// +// Add all constraints connected to the specified body to an array but +// ignore the specified constraint. +// +PX_FORCE_INLINE static void dumpConnectedConstraints(Sc::BodySim& b, Sc::ConstraintSim* c, Sc::ScratchAllocatorList& constraintList) +{ + PxU32 size = b.getActorInteractionCount(); + Sc::Interaction** interactions = b.getActorInteractions(); + while(size--) + { + Sc::Interaction* interaction = *interactions++; + if (interaction->getType() == Sc::InteractionType::eCONSTRAINTSHADER) + { + Sc::ConstraintSim* ct = static_cast(interaction)->getConstraint(); + + if ((ct != c) && (!ct->readFlag(Sc::ConstraintSim::ePENDING_GROUP_UPDATE))) + { + bool success = constraintList.add(ct); + PX_UNUSED(success); + PX_ASSERT(success); + } + } + } +} + + +PX_FORCE_INLINE void Sc::ConstraintProjectionManager::processConstraintForGroupBuilding(ConstraintSim* c, ScratchAllocatorList& constraintList) +{ + c->clearFlag(ConstraintSim::ePENDING_GROUP_UPDATE); + + // Find all constraints connected to the two bodies of the dirty constraint. + // - Constraints to static anchors are ignored (note: kinematics can't be ignored because they might get switched to dynamics any time which + // does trigger a projection tree rebuild but not a constraint tree rebuild + // - Already processed bodies are ignored as well + BodySim* b0 = c->getBody(0); + if (b0 && !b0->getConstraintGroup()) + { + dumpConnectedConstraints(*b0, c, constraintList); + } + BodySim* b1 = c->getBody(1); + if (b1 && !b1->getConstraintGroup()) + { + dumpConnectedConstraints(*b1, c, constraintList); + } + + BodySim* b = c->getAnyBody(); + PX_ASSERT(b); + + addToGroup(*b, c->getOtherBody(b), *c); //this will eventually merge some body's constraint groups. +} + + +void Sc::ConstraintProjectionManager::processPendingUpdates(PxcScratchAllocator& scratchAllocator) +{ + // + // if there are dirty projection trees, then rebuild them + // + const PxU32 nbProjectionTreesToUpdate = mPendingTreeUpdates.size(); + if (nbProjectionTreesToUpdate) + { + ConstraintGroupNode* const* projectionTreesToUpdate = mPendingTreeUpdates.getEntries(); + for(PxU32 i=0; i < nbProjectionTreesToUpdate; i++) + { + ConstraintGroupNode* n = projectionTreesToUpdate[i]; + + PX_ASSERT(n == &n->getRoot()); // only root nodes should be in that list + PX_ASSERT(n->readFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE)); + + n->clearFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE); + + // note: it is valid to get here and not have a projection root. This is the case if all nodes of a constraint graph are kinematic + // at some point (hence no projection root) and later some of those get switched to dynamic. + if (n->hasProjectionTreeRoot()) + n->purgeProjectionTrees(); + n->buildProjectionTrees(); + } + + mPendingTreeUpdates.clear(); + } + + // + // if there are new/dirty constraints, update groups + // + const PxU32 nbProjectionConstraintsToUpdate = mPendingGroupUpdates.size(); + + if (nbProjectionConstraintsToUpdate) + { + ScratchAllocatorList nonProjectionConstraintList(scratchAllocator); + + ConstraintSim* const* projectionConstraintsToUpdate = mPendingGroupUpdates.getEntries(); + +#if PX_DEBUG + // At the beginning the list should only contain constraints with projection. + // Further below other constraints, connected to the constraints with projection, will be added too. + for(PxU32 i=0; i < nbProjectionConstraintsToUpdate; i++) + { + PX_ASSERT(projectionConstraintsToUpdate[i]->needsProjection()); + } +#endif + for(PxU32 i=0; i < nbProjectionConstraintsToUpdate; i++) + { + processConstraintForGroupBuilding(projectionConstraintsToUpdate[i], nonProjectionConstraintList); + } + + ScratchAllocatorList::Iterator iter = nonProjectionConstraintList.getIterator(); + ConstraintSim* const* nextConstraint = iter.getNext(); + while(nextConstraint) + { + processConstraintForGroupBuilding(*nextConstraint, nonProjectionConstraintList); + + nextConstraint = iter.getNext(); + } + + // Now find all the newly made groups and build projection trees. + // Don't need to iterate over the additionally constraints since the roots are supposed to be + // fetchable from any node. + for (PxU32 i=0; i < nbProjectionConstraintsToUpdate; i++) + { + ConstraintSim* c = projectionConstraintsToUpdate[i]; + BodySim* b = c->getAnyBody(); + PX_ASSERT(b); + PX_ASSERT(b->getConstraintGroup()); + + ConstraintGroupNode& root = b->getConstraintGroup()->getRoot(); + if (!root.hasProjectionTreeRoot()) // Build projection tree only once + root.buildProjectionTrees(); + } + + mPendingGroupUpdates.clear(); + } +} + + +// +// Called if a body or a constraint gets deleted. All projecting constraints of the +// group (except the deleted one) are moved to the dirty list and all group nodes are destroyed. +// +void Sc::ConstraintProjectionManager::invalidateGroup(ConstraintGroupNode& node, ConstraintSim* deletedConstraint) +{ + ConstraintGroupNode* n = &node.getRoot(); + + if (n->readFlag(ConstraintGroupNode::ePENDING_TREE_UPDATE)) + { + removeFromPendingTreeUpdates(*n); + } + + while (n) //go through nodes in constraint group + { + markConnectedConstraintsForUpdate(*n->body, deletedConstraint); + + //destroy the body's constraint group information + + ConstraintGroupNode* next = n->next; //save next node ptr before we destroy it! + + BodySim* b = n->body; + b->setConstraintGroup(NULL); + if (n->hasProjectionTreeRoot()) + n->purgeProjectionTrees(); + mNodePool.destroy(n); + + n = next; + } +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h new file mode 100644 index 000000000..34a0c8185 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h @@ -0,0 +1,84 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_CONSTRAINT_PROJECTION_MANAGER +#define PX_PHYSICS_SCP_CONSTRAINT_PROJECTION_MANAGER + +#include "PsPool.h" +#include "PsHashSet.h" +#include "ScConstraintGroupNode.h" + +namespace physx +{ + class PxcScratchAllocator; + +namespace Sc +{ + class ConstraintSim; + class BodySim; + template class ScratchAllocatorList; + + class ConstraintProjectionManager : public Ps::UserAllocated + { + public: + ConstraintProjectionManager(); + ~ConstraintProjectionManager() {} + + void addToPendingGroupUpdates(ConstraintSim& s); + void removeFromPendingGroupUpdates(ConstraintSim& s); + + void addToPendingTreeUpdates(ConstraintGroupNode& n); + void removeFromPendingTreeUpdates(ConstraintGroupNode& n); + + void processPendingUpdates(PxcScratchAllocator&); + void invalidateGroup(ConstraintGroupNode& node, ConstraintSim* constraintDeleted); + + private: + PX_INLINE Sc::ConstraintGroupNode* createGroupNode(BodySim& b); + + void addToGroup(BodySim& b, BodySim* other, ConstraintSim& c); + void groupUnion(ConstraintGroupNode& root0, ConstraintGroupNode& root1); + void markConnectedConstraintsForUpdate(BodySim& b, ConstraintSim* c); + PX_FORCE_INLINE void processConstraintForGroupBuilding(ConstraintSim* c, ScratchAllocatorList&); + + + private: + Ps::Pool mNodePool; + Ps::CoalescedHashSet mPendingGroupUpdates; //list of constraints for which constraint projection groups need to be generated/updated + Ps::CoalescedHashSet mPendingTreeUpdates; //list of constraint groups that need their projection trees rebuilt. Note: non of the + //constraints in those groups are allowed to be in mPendingGroupUpdates at the same time + //because a group update will automatically trigger tree rebuilds. + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp new file mode 100644 index 000000000..9dc2d245c --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp @@ -0,0 +1,567 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScConstraintProjectionTree.h" +#include "ScScene.h" +#include "ScBodySim.h" +#include "ScConstraintCore.h" +#include "ScConstraintSim.h" +#include "ScConstraintInteraction.h" + +#include "PsFoundation.h" +#include "PsBasicTemplates.h" +#include "PsSort.h" +#include "PsArray.h" + +using namespace physx; + +//------------------------------------------------------------------------------------------ +// +// The projection tree related code +// +// Projection trees are built out of a constraint group/graph. The constraint group just tracks +// the constraint connectivity while the projection trees define the projection root and +// the projection order. +// A constraint group can contain multiple projection trees. +// +//------------------------------------------------------------------------------------------ + +class Sc::BodyRank +{ +public: + PX_INLINE bool operator>(const BodyRank & b) const + { + return rank > b.rank; + } + + Sc::ConstraintGroupNode* startingNode; + Sc::ConstraintSim* constraintToFixedAnchor; + PxU32 rank; + + // + // The following weights are defined to fulfill the projection priorities described further below + // + static const PxU32 sOneWayProjection = PxU32(1 << 31); + static const PxU32 sAttachedToStatic = (1 << 30); + static const PxU32 sAttachedToKinematic = (1 << 29); + static const PxU32 sAllDominantDynamic = (1 << 28); // if for a dynamic body all connections with projection, are one-way towards the body + static const PxU32 sDominantDynamic = (1 << 27); // almost the same as above but there is at least one two-way projection + static const PxU32 sAttachedToDynamic = 1; + static const PxU32 sPrimaryTreeRootMinRank = sOneWayProjection | sAllDominantDynamic; +}; + + +PX_INLINE bool isFixedBody(const Sc::BodySim* b) +{ + return (!b || (b->isKinematic())); +} + + +void Sc::ConstraintProjectionTree::getConstraintStatus(const ConstraintSim& c, const BodySim* b, BodySim*& otherBody, PxU32& projectToBody, PxU32& projectToOtherBody) +{ + const PxU32 isBroken = c.isBroken() ? 0 : 0xffffffff; + const PxU32 projFlags = c.getCore().getFlags() & PxConstraintFlag::ePROJECTION; + + if (b == c.getBody(0)) + { + projectToBody = isBroken & (projFlags & PxConstraintFlag::ePROJECT_TO_ACTOR0); + projectToOtherBody = isBroken & (projFlags & PxConstraintFlag::ePROJECT_TO_ACTOR1); + + otherBody = c.getBody(1); + } + else + { + projectToBody = isBroken & (projFlags & PxConstraintFlag::ePROJECT_TO_ACTOR1); + projectToOtherBody = isBroken & (projFlags & PxConstraintFlag::ePROJECT_TO_ACTOR0); + + otherBody = c.getBody(0); + } +} + + +void Sc::ConstraintProjectionTree::rankConstraint(ConstraintSim& c, BodyRank& br, PxU32& dominanceTracking, PxU32& constraintsToProjectCount) +{ + PxU32 projectToBody, projectToOtherBody; + BodySim* otherB; + getConstraintStatus(c, br.startingNode->body, otherB, projectToBody, projectToOtherBody); + + if (isFixedBody(otherB)) // joint to fixed anchor + { + PxU32 rank; + if (projectToOtherBody) + { + dominanceTracking = 0; // makes sure that the flags below will never get raised again for the body + br.rank &= ~(BodyRank::sAllDominantDynamic | BodyRank::sDominantDynamic); + rank = BodyRank::sOneWayProjection; //we should prefer picking projected constraints as the root over non-projected ones. + constraintsToProjectCount++; + } + else + rank = 0; + + if (!otherB) + rank |= BodyRank::sAttachedToStatic; + else + { + PX_ASSERT(otherB->isKinematic()); + rank |= BodyRank::sAttachedToKinematic; + } + + // the highest ranked fixed anchor constraint should get tracked + if ((!br.constraintToFixedAnchor) || (rank > br.rank)) + br.constraintToFixedAnchor = &c; + + br.rank |= rank; + } + else + { + if (projectToBody && projectToOtherBody) + { + dominanceTracking &= ~BodyRank::sAllDominantDynamic; // makes sure that from now on this will never get raised again for the body + br.rank &= ~BodyRank::sAllDominantDynamic; + constraintsToProjectCount++; + } + else if (projectToOtherBody) + { + dominanceTracking &= ~(BodyRank::sAllDominantDynamic | BodyRank::sDominantDynamic); // makes sure that from now on these will never get raised again for the body + br.rank &= ~(BodyRank::sAllDominantDynamic | BodyRank::sDominantDynamic); + constraintsToProjectCount++; + } + else if (projectToBody) + { + br.rank |= BodyRank::sOneWayProjection | (dominanceTracking & (BodyRank::sAllDominantDynamic | BodyRank::sDominantDynamic)); + constraintsToProjectCount++; + } + + br.rank += BodyRank::sAttachedToDynamic; + } +} + + +/* +the goal here is to take the constraint group whose root is passed, and create one or more projection trees. + +At the moment, the group has to be acyclic and have at most 1 constraint with the ground to be accepted +without being broken up into multiple trees. + +We 'flood fill' the constraint graph several times, starting at bodies where projection trees can be rooted. +Projection tree roots are always dynamic bodies which either need to get projected to a fixed anchor directly +or have projecting constraints between dynamics some way along the tree branches. Static and kinematic actors +are never roots and will not be explicitly part of any tree (but a tree root can project to at most one such fixed node). + +The algo looks like this: + +for all bodies +mark body as undiscovered +rank this body + +The rank of a body depends on the constraints it's connected to. It defines the projection priority which +should be (highest first): +- dynamic attached to static/world with projection +- dynamic attached to kinematic with projection +- all dominant dynamic (has projecting constraints and all of them are one-way towards this dynamic) +---- all the ones above are guaranteed tree roots +- dominant dynamic (same as above but there is at least one projecting two-way constraint as well) +- partially dominant dynamic (has at least one projecting one-way constraints towards this dynamic and at least one projecting one-way constraints towards an other body) +- dynamic attached to static/world without projection +- dynamic attached to kinematic without projection +- dynamic with or without two-way projecting constraints to other dynamics (among these, the one with the highest connectivity count wins) + +for the first three priority types sorted according to rank: +create a projection tree root and grow the tree one connectivity layer at a time + +do the same for dominant dynamic bodies that have not been visited/discovered yet + +for all remaining bodies sorted according to rank: +if the body still hasn't been visited/discovered start a projection tree there and build the whole tree in one go +before moving to the next potential root. +*/ +void Sc::ConstraintProjectionTree::buildProjectionTrees(ConstraintGroupNode& root) +{ + PX_ASSERT(&root == root.parent); + PX_ASSERT(!root.hasProjectionTreeRoot()); + + Ps::InlineArray bodyRankArray PX_DEBUG_EXP("bodyRankArray"); + BodyRank br; + PxU32 constraintsToProjectCount = 0; + ConstraintGroupNode* node0 = &root; + while (node0) //for all nodes in group + { + PX_ASSERT(node0->body); + if (!node0->body->isKinematic()) + { + node0->clearFlag(ConstraintGroupNode::eDISCOVERED); + + //rank + br.startingNode = node0; + br.rank = 0; + br.constraintToFixedAnchor = 0; + PxU32 dominanceTracking = BodyRank::sAllDominantDynamic | BodyRank::sDominantDynamic; + + //go through all constraints connected to body + PxU32 size = node0->body->getActorInteractionCount(); + Sc::Interaction** interactions = node0->body->getActorInteractions(); + while(size--) + { + Interaction* interaction = *interactions++; + if (interaction->getType() == InteractionType::eCONSTRAINTSHADER) + { + ConstraintSim* c = static_cast(interaction)->getConstraint(); + rankConstraint(*c, br, dominanceTracking, constraintsToProjectCount); + } + } + + PX_ASSERT(br.rank); //if it has no constraints then why is it in the constraint group? + + if (br.rank >= BodyRank::sPrimaryTreeRootMinRank) + node0->raiseFlag(ConstraintGroupNode::eDISCOVERED); // we create a tree for each node attached to a fixed anchor, or a node which is an all dominating dynamic + // -> make sure they do not include each other + + bodyRankArray.pushBack(br); + } + else + node0->raiseFlag(ConstraintGroupNode::eDISCOVERED); // a kinematic does not get projected, it might only get projected to and it is never part of a tree. + + node0 = node0->next; + } + + root.setProjectionCountHint(constraintsToProjectCount); + + if (bodyRankArray.size()) // all of the bodies might have been switched to kinematic in which case there will be no ranked body + { + //sort bodyRankArray + + Ps::sort(&bodyRankArray.front(), bodyRankArray.size(), Ps::Greater()); + + ConstraintGroupNode** nodeQueue = reinterpret_cast(PX_ALLOC_TEMP(sizeof(ConstraintGroupNode*)*bodyRankArray.size(), "ProjectionNodeQueue")); + if (nodeQueue) + { + //build the projectionTree + + ConstraintGroupNode* firstProjectionTreeRoot = NULL; + + //go through it in sorted order + + // + // bodies attached to fixed anchors with projecting constraints or all dominant rigid dynamics should get processed first. + // For each of those we create a projection tree for sure, by extending one connectivity level from the root at a time. + // This way we make sure that scenarios like a bridge that is attached to fixed anchors at both ends breaks in the middle + // and not at one of the fixed anchors. + // + // this gets repeated for dominant dynamics. The reason for this is to cover cases where a dominant dynamic is connected to + // a higher ranked node by a chain of two-way constraints. In such a case the two-way constraint should project the dominant + // dynamic towards the higher ranked node and not start a tree on its own. + // + PxU32 brIdx = 0; + PxU32 stopIdx = bodyRankArray.size(); + PxU32 skipCount = 0; + PxU32 ranksToProcess = BodyRank::sPrimaryTreeRootMinRank; + ConstraintGroupNode** nodeQueueEnd; + ConstraintGroupNode** nodeQueueCurrent; + for(PxU32 i=0; i < 2; i++) + { + nodeQueueEnd = nodeQueue; + while((brIdx < stopIdx) && (bodyRankArray[brIdx].rank >= ranksToProcess)) + { + BodyRank& bRank = bodyRankArray[brIdx]; + PX_ASSERT((brIdx == 0) || (bRank.rank <= bodyRankArray[brIdx-1].rank)); + + ConstraintGroupNode& node = *bRank.startingNode; + PX_ASSERT(node.readFlag(ConstraintGroupNode::eDISCOVERED)); + + node.initProjectionData(NULL, bRank.constraintToFixedAnchor); + + if (bRank.rank & (BodyRank::sAttachedToStatic | BodyRank::sAttachedToKinematic)) + { + // for static/kinematic attached, the current node is already a child, so we must not traverse the neighborhood yet + // but rather add the current node to the queue. + PX_ASSERT(bRank.constraintToFixedAnchor); + *nodeQueueEnd = &node; + nodeQueueEnd++; + } + else + { + PX_ASSERT(!bRank.constraintToFixedAnchor); + PxU32 addedNodeCount = projectionTreeBuildStep(node, bRank.constraintToFixedAnchor, nodeQueueEnd); + nodeQueueEnd += addedNodeCount; + } + + node.projectionNextRoot = firstProjectionTreeRoot; + firstProjectionTreeRoot = &node; + + brIdx++; + } + + // first neighbor connectivity level has been pushed to a queue for all chosen tree roots. Now extend the trees one level at a time. + nodeQueueCurrent = nodeQueue; + while(nodeQueueCurrent != nodeQueueEnd) + { + ConstraintGroupNode* node = *nodeQueueCurrent; + PX_ASSERT(node->readFlag(ConstraintGroupNode::eDISCOVERED)); + nodeQueueCurrent++; + + PxU32 addedNodeCount = projectionTreeBuildStep(*node, node->projectionConstraint, nodeQueueEnd); + nodeQueueEnd += addedNodeCount; + } + + brIdx += skipCount; + skipCount = 0; + + // find dominant dynamics that have not been discovered yet and arrange them in a consecutive block + ranksToProcess = BodyRank::sOneWayProjection | BodyRank::sDominantDynamic; + stopIdx = brIdx; + PxU32 k = brIdx; + while((k < bodyRankArray.size()) && (bodyRankArray[k].rank >= ranksToProcess)) + { + ConstraintGroupNode* node = bodyRankArray[k].startingNode; + if (!node->readFlag(ConstraintGroupNode::eDISCOVERED)) + { + node->raiseFlag(ConstraintGroupNode::eDISCOVERED); + bodyRankArray[stopIdx] = bodyRankArray[k]; + stopIdx++; + } + else + skipCount++; + + k++; + } + } + + // + // for every body that has not been discovered yet, we build a tree. Here we do not advance one connectivity level + // at a time because there should be no fight over the nodes among equal roots anymore (or rather no fight that could + // break one-way projection in an unfair way). + // + PX_ASSERT((brIdx == 0) || (brIdx == bodyRankArray.size()) || (bodyRankArray[brIdx].rank < bodyRankArray[brIdx-1].rank)); + for(PxU32 i=brIdx; i < bodyRankArray.size(); i++) + { + nodeQueueEnd = nodeQueue; + + BodyRank& bRank = bodyRankArray[i]; + PX_ASSERT((i == brIdx) || (bRank.rank <= bodyRankArray[i-1].rank)); +#ifdef _DEBUG + if (bRank.rank & (BodyRank::sAttachedToStatic | BodyRank::sAttachedToKinematic)) + { PX_ASSERT(bRank.constraintToFixedAnchor); } + else + { PX_ASSERT(!bRank.constraintToFixedAnchor); } +#endif + + ConstraintGroupNode& node = *bRank.startingNode; + if (!node.readFlag(ConstraintGroupNode::eDISCOVERED)) + { + node.raiseFlag(ConstraintGroupNode::eDISCOVERED); + + PxU32 addedNodeCount = projectionTreeBuildStep(node, bRank.constraintToFixedAnchor, nodeQueueEnd); + nodeQueueEnd += addedNodeCount; + + nodeQueueCurrent = nodeQueue; + while(nodeQueueCurrent != nodeQueueEnd) + { + ConstraintGroupNode* n = *nodeQueueCurrent; + PX_ASSERT(n->readFlag(ConstraintGroupNode::eDISCOVERED)); + PX_ASSERT(n->projectionConstraint); + nodeQueueCurrent++; + + PxU32 nodeCount = projectionTreeBuildStep(*n, n->projectionConstraint, nodeQueueEnd); + nodeQueueEnd += nodeCount; + } + + node.projectionNextRoot = firstProjectionTreeRoot; + firstProjectionTreeRoot = &node; + } + } + + root.setProjectionTreeRoot(firstProjectionTreeRoot); + + PX_FREE(nodeQueue); + } + else + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Allocating projection node queue failed!"); + } +} + + +PxU32 Sc::ConstraintProjectionTree::projectionTreeBuildStep(ConstraintGroupNode& node, ConstraintSim* cToParent, ConstraintGroupNode** nodeQueue) +{ + PX_ASSERT(node.readFlag(ConstraintGroupNode::eDISCOVERED)); + + PxU32 nodeQueueFillCount = 0; + + //go through all constraints attached to the body. + BodySim* body = node.body; + PxU32 size = body->getActorInteractionCount(); + Sc::Interaction** interactions = body->getActorInteractions(); + while(size--) + { + Interaction* interaction = *interactions++; + if (interaction->getType() == InteractionType::eCONSTRAINTSHADER) + { + ConstraintSim* c = static_cast(interaction)->getConstraint(); + + if (c != cToParent) //don't go back along the edge we came from (not really necessary I guess since the ConstraintGroupNode::eDISCOVERED marker should solve this) + { + PxU32 projectToBody, projectToOtherBody; + BodySim* neighbor; + getConstraintStatus(*c, body, neighbor, projectToBody, projectToOtherBody); + + if(!isFixedBody(neighbor) && (!projectToOtherBody || projectToBody)) //just ignore the eventual constraint with environment over here. Body might be attached to multiple fixed anchors. + //Also make sure to ignore one-way projection that goes the opposite way. + { + ConstraintGroupNode* neighborNode = neighbor->getConstraintGroup(); + PX_ASSERT(neighborNode); + + if (!neighborNode->readFlag(ConstraintGroupNode::eDISCOVERED)) + { + *nodeQueue = neighborNode; + + neighborNode->initProjectionData(&node, c); + neighborNode->raiseFlag(ConstraintGroupNode::eDISCOVERED); //flag body nodes that we process so we can detect loops + + nodeQueueFillCount++; + nodeQueue++; + } + } + } + } + } + + return nodeQueueFillCount; +} + + +void Sc::ConstraintProjectionTree::purgeProjectionTrees(ConstraintGroupNode& root) +{ + PX_ASSERT(&root == root.parent); + PX_ASSERT(root.hasProjectionTreeRoot()); + + // CA: New code (non recursive: recursive calls can cause stack overflow with huge trees) + ConstraintGroupNode* projRoot = root.projectionFirstRoot; + do + { + ConstraintGroupNode* currentNode = projRoot; + projRoot = projRoot->projectionNextRoot; // need to do it here because the info might get cleared below + + do + { + // Go down the tree until we find a leaf + if (currentNode->projectionFirstChild) + { + currentNode = currentNode->projectionFirstChild; + continue; + } + + // Delete current node and go to next sibling or parent + ConstraintGroupNode* nodeToDelete = currentNode; + ConstraintGroupNode* parent = currentNode->projectionParent; + currentNode = currentNode->projectionNextSibling; + + // Mark parent as leaf + if (nodeToDelete->projectionParent) + nodeToDelete->projectionParent->projectionFirstChild = NULL; + + // Clear projection info + nodeToDelete->clearProjectionData(); + + if (currentNode != NULL) + continue; + + // No more siblings jump back to parent + currentNode = parent; + + } while (currentNode != NULL); + + } while (projRoot != NULL); + + root.projectionFirstRoot = NULL; // it can happen that the constraint graph root is not part of a projection tree (if it is a kinematic, for example) but it still points to the + // first projection tree root and that needs to get cleaned up as well. + PX_ASSERT(!root.projectionNextRoot); + PX_ASSERT(!root.projectionParent); + PX_ASSERT(!root.projectionFirstChild); + PX_ASSERT(!root.projectionNextSibling); + PX_ASSERT(!root.projectionConstraint); +} + + +void Sc::ConstraintProjectionTree::projectPoseForTree(ConstraintGroupNode& node, Ps::Array& projectedBodies) +{ + // create a dummy node to keep the loops compact while covering the special case of the first node + PX_ASSERT(node.body); + ConstraintGroupNode dummyNode(*node.body); + dummyNode.projectionNextSibling = &node; + ConstraintGroupNode* currentNode = &dummyNode; + + // non recursive: recursive calls can cause stack overflow with huge trees + do + { + ConstraintGroupNode* nextSiblingNode = currentNode->projectionNextSibling; + + while (nextSiblingNode) + { + currentNode = nextSiblingNode; + ConstraintGroupNode* nextChildNode = currentNode; + + do + { + currentNode = nextChildNode; + + //----------------------------------------------------------------------------- + ConstraintSim* c = currentNode->projectionConstraint; + + if (c && c->hasDynamicBody() && c->needsProjection()) + { + c->projectPose(currentNode->body, projectedBodies); + } + //----------------------------------------------------------------------------- + + nextChildNode = currentNode->projectionFirstChild; + + } while (nextChildNode); + + nextSiblingNode = currentNode->projectionNextSibling; + } + + currentNode = currentNode->projectionParent; + + } while (currentNode != NULL); +} + + +void Sc::ConstraintProjectionTree::projectPose(ConstraintGroupNode& root, Ps::Array& projectedBodies) +{ + PX_ASSERT(&root == root.parent); + PX_ASSERT(root.hasProjectionTreeRoot()); + + ConstraintGroupNode* projRoot = root.projectionFirstRoot; + do + { + projectPoseForTree(*projRoot, projectedBodies); + projRoot = projRoot->projectionNextRoot; + + } while (projRoot != NULL); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h new file mode 100644 index 000000000..a13aaf5f8 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h @@ -0,0 +1,75 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_CONSTRAINT_PROJECTION_TREE +#define PX_PHYSICS_SCP_CONSTRAINT_PROJECTION_TREE + +#include "PsArray.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" + +namespace physx +{ +namespace Sc +{ + struct ConstraintGroupNode; + class ConstraintSim; + class BodySim; + class BodyRank; + + class ConstraintProjectionTree + { + /** + This class serves both the static administration of an articulation and the actual articulation itself. + An Articulation object holds several articulation root nodes which make up a simulation island that + is further connected with lagrange joints. + */ + public: + ConstraintProjectionTree() {} + ~ConstraintProjectionTree() {} + + static void buildProjectionTrees(ConstraintGroupNode& root); + static void purgeProjectionTrees(ConstraintGroupNode& root); + + static void projectPose(ConstraintGroupNode& root, Ps::Array& projectedBodies); + + private: + static PxU32 projectionTreeBuildStep(ConstraintGroupNode& node, ConstraintSim* cToParent, ConstraintGroupNode** nodeStack); + + static void getConstraintStatus(const ConstraintSim& c, const BodySim* b, BodySim*& otherBody, PxU32& projectToBody, PxU32& projectToOtherBody); + static void rankConstraint(ConstraintSim&, BodyRank&, PxU32& dominanceTracking, PxU32& constraintsToProjectCount); + static void projectPoseForTree(ConstraintGroupNode& node, Ps::Array& projectedBodies); + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp new file mode 100644 index 000000000..79c255ca0 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp @@ -0,0 +1,490 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScScene.h" +#include "ScConstraintProjectionManager.h" +#include "ScBodySim.h" +#include "ScStaticSim.h" +#include "PxsContext.h" +#include "ScConstraintCore.h" +#include "ScConstraintSim.h" +#include "ScConstraintInteraction.h" +#include "ScElementSimInteraction.h" +#include "CmVisualization.h" +#include "ScObjectIDTracker.h" +#include "DyContext.h" + +using namespace physx; + +PX_FORCE_INLINE void invalidateConstraintGroupsOnAdd(Sc::ConstraintProjectionManager& cpm, Sc::BodySim* b0, Sc::BodySim* b1, Sc::ConstraintSim& constraint) +{ + // constraint groups get built by starting from dirty constraints that need projection. If a non-projecting constraint gets added + // we need to restart the whole process (we do not want to track dirty non-projecting constraints because of a scenario where + // all constraints of a group get switched to non-projecting which should kill the group and not rebuild a new one). + if (b0 && b0->getConstraintGroup()) + cpm.invalidateGroup(*b0->getConstraintGroup(), &constraint); + if (b1 && b1->getConstraintGroup()) + cpm.invalidateGroup(*b1->getConstraintGroup(), &constraint); +} + +Sc::ConstraintSim::ConstraintSim(ConstraintCore& core, RigidCore* r0, RigidCore* r1, Scene& scene) : + mScene (scene), + mCore (core), + mInteraction(NULL), + mFlags (0) +{ + mBodies[0] = (r0 && (r0->getActorCoreType() != PxActorType::eRIGID_STATIC)) ? static_cast(r0->getSim()) : 0; + mBodies[1] = (r1 && (r1->getActorCoreType() != PxActorType::eRIGID_STATIC)) ? static_cast(r1->getSim()) : 0; + + mLowLevelConstraint.index = scene.getConstraintIDTracker().createID(); + Ps::Array& writeBackPool = scene.getDynamicsContext()->getConstraintWriteBackPool(); + if (mLowLevelConstraint.index >= writeBackPool.capacity()) + { + writeBackPool.reserve(writeBackPool.capacity() * 2); + } + + writeBackPool.resize(PxMax(writeBackPool.size(), mLowLevelConstraint.index + 1)); + writeBackPool[mLowLevelConstraint.index].initialize(); + + if (!createLLConstraint()) + return; + + PxReal linBreakForce, angBreakForce; + core.getBreakForce(linBreakForce, angBreakForce); + if ((linBreakForce < PX_MAX_F32) || (angBreakForce < PX_MAX_F32)) + setFlag(eBREAKABLE); + + core.setSim(this); + + ConstraintProjectionManager& cpm = scene.getProjectionManager(); + if (!needsProjection()) + invalidateConstraintGroupsOnAdd(cpm, mBodies[0], mBodies[1], *this); + else + cpm.addToPendingGroupUpdates(*this); + + ConstraintSim* cs = this; // to make the Wii U compiler happy + mInteraction = mScene.getConstraintInteractionPool()->construct(cs, + r0 ? *r0->getSim() : scene.getStaticAnchor(), + r1 ? *r1->getSim() : scene.getStaticAnchor()); + + PX_ASSERT(!mInteraction->isRegistered()); // constraint interactions must not register in the scene, there is a list of Sc::ConstraintSim instead +} + +Sc::ConstraintSim::~ConstraintSim() +{ + PX_ASSERT(mInteraction); // This is fine now, a body which gets removed from the scene removes all constraints automatically + PX_ASSERT(!mInteraction->isRegistered()); // constraint interactions must not register in the scene, there is a list of Sc::ConstraintSim instead + + if (readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)) + mScene.getProjectionManager().removeFromPendingGroupUpdates(*this); + + if (!isBroken()) + mInteraction->destroy(); + + mScene.getConstraintIDTracker().releaseID(mLowLevelConstraint.index); + mScene.getConstraintInteractionPool()->destroy(mInteraction); + + destroyLLConstraint(); + + mCore.setSim(NULL); +} + +bool Sc::ConstraintSim::createLLConstraint() +{ + Dy::Constraint& llc = mLowLevelConstraint; + ConstraintCore& core = getCore(); + PxU32 constantBlockSize = core.getConstantBlockSize(); + + void* constantBlock = mScene.allocateConstraintBlock(constantBlockSize); + if(!constantBlock) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Constraint: could not allocate low-level resources."); + return false; + } + + //Ensure the constant block isn't just random data because some functions may attempt to use it before it is + //setup. Specifically pvd visualization of joints + //-CN + + PxMemZero( constantBlock, constantBlockSize); + + core.getBreakForce(llc.linBreakForce, llc.angBreakForce); + llc.flags = core.getFlags(); + llc.constantBlockSize = PxU16(constantBlockSize); + + llc.solverPrep = core.getSolverPrep(); + llc.project = core.getProject(); + llc.constantBlock = constantBlock; + + //llc.index = mLowLevelConstraint.index; + llc.body0 = mBodies[0] ? &mBodies[0]->getLowLevelBody() : 0; + llc.body1 = mBodies[1] ? &mBodies[1]->getLowLevelBody() : 0; + llc.bodyCore0 = mBodies[0] ? &llc.body0->getCore() : NULL; + llc.bodyCore1 = mBodies[1] ? &llc.body1->getCore() : NULL; + + llc.minResponseThreshold = core.getMinResponseThreshold(); + + return true; +} + +void Sc::ConstraintSim::destroyLLConstraint() +{ + if(mLowLevelConstraint.constantBlock) + { + mScene.deallocateConstraintBlock(mLowLevelConstraint.constantBlock, + mLowLevelConstraint.constantBlockSize); + } +} + +void Sc::ConstraintSim::preBodiesChange() +{ + PX_ASSERT(mInteraction); + + BodySim* b = getConstraintGroupBody(); + if (b) + mScene.getProjectionManager().invalidateGroup(*b->getConstraintGroup(), this); + + if (!isBroken()) + mInteraction->destroy(); + + mScene.getConstraintInteractionPool()->destroy(mInteraction); + mInteraction = NULL; +} + +void Sc::ConstraintSim::postBodiesChange(RigidCore* r0, RigidCore* r1) +{ + PX_ASSERT(mInteraction == NULL); + + BodySim* b0 = (r0 && (r0->getActorCoreType() != PxActorType::eRIGID_STATIC)) ? static_cast(r0->getSim()) : 0; + BodySim* b1 = (r1 && (r1->getActorCoreType() != PxActorType::eRIGID_STATIC)) ? static_cast(r1->getSim()) : 0; + + ConstraintProjectionManager& cpm = mScene.getProjectionManager(); + PxConstraintFlags::InternalType projectionNeeded = getCore().getFlags() & PxConstraintFlag::ePROJECTION; // can not use "needsProjection()" because that takes into account whether the constraint is broken + if (!projectionNeeded) + invalidateConstraintGroupsOnAdd(cpm, b0, b1, *this); + else if (!readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)) + cpm.addToPendingGroupUpdates(*this); + + Dy::Constraint& c = mLowLevelConstraint; + + c.body0 = b0 ? &b0->getLowLevelBody() : NULL; + c.body1 = b1 ? &b1->getLowLevelBody() : NULL; + + c.bodyCore0 = c.body0 ? &c.body0->getCore() : NULL; + c.bodyCore1 = c.body1 ? &c.body1->getCore() : NULL; + + mBodies[0] = b0; + mBodies[1] = b1; + + ConstraintSim* cs = this; // to make the Wii U compiler happy + mInteraction = mScene.getConstraintInteractionPool()->construct(cs, + r0 ? *r0->getSim() : mScene.getStaticAnchor(), + r1 ? *r1->getSim() : mScene.getStaticAnchor()); +} + +void Sc::ConstraintSim::checkMaxForceExceeded() +{ + PX_ASSERT(readFlag(eCHECK_MAX_FORCE_EXCEEDED)); + + Dy::ConstraintWriteback& solverOutput = mScene.getDynamicsContext()->getConstraintWriteBackPool()[mLowLevelConstraint.index]; + if(solverOutput.broken) + { + setFlag(ConstraintSim::eBROKEN); + mScene.addBrokenConstraint(&mCore); + mCore.breakApart(); + mInteraction->destroy(); + + // update related SIPs + { + ActorSim& a0 = mInteraction->getActorSim0(); + ActorSim& a1 = mInteraction->getActorSim1(); + ActorSim& actor = (a0.getActorInteractionCount()< a1.getActorInteractionCount()) ? a0 : a1; + + actor.setActorsInteractionsDirty(InteractionDirtyFlag::eFILTER_STATE, NULL, InteractionFlag::eRB_ELEMENT); + // because broken constraints can re-enable contact response between the two bodies + } + + PX_ASSERT(!readFlag(eCHECK_MAX_FORCE_EXCEEDED)); + } +} + +void Sc::ConstraintSim::getForce(PxVec3& lin, PxVec3& ang) +{ + const PxReal recipDt = mScene.getOneOverDt(); + Dy::ConstraintWriteback& solverOutput= mScene.getDynamicsContext()->getConstraintWriteBackPool()[mLowLevelConstraint.index]; + lin = solverOutput.linearImpulse * recipDt; + ang = solverOutput.angularImpulse * recipDt; +} + +void Sc::ConstraintSim::setBreakForceLL(PxReal linear, PxReal angular) +{ + PxU8 wasBreakable = readFlag(eBREAKABLE); + PxU8 isBreakable; + if ((linear < PX_MAX_F32) || (angular < PX_MAX_F32)) + isBreakable = eBREAKABLE; + else + isBreakable = 0; + + if (isBreakable != wasBreakable) + { + if (isBreakable) + { + PX_ASSERT(!readFlag(eCHECK_MAX_FORCE_EXCEEDED)); + setFlag(eBREAKABLE); + if (mInteraction->readInteractionFlag(InteractionFlag::eIS_ACTIVE)) + mScene.addActiveBreakableConstraint(this, mInteraction); + } + else + { + if (readFlag(eCHECK_MAX_FORCE_EXCEEDED)) + mScene.removeActiveBreakableConstraint(this); + clearFlag(eBREAKABLE); + } + } + + mLowLevelConstraint.linBreakForce = linear; + mLowLevelConstraint.angBreakForce = angular; +} + +void Sc::ConstraintSim::postFlagChange(PxConstraintFlags oldFlags, PxConstraintFlags newFlags) +{ + mLowLevelConstraint.flags = newFlags; + + // PT: don't convert to bool if not needed + const PxU32 hadProjection = (oldFlags & PxConstraintFlag::ePROJECTION); + const PxU32 needsProjection = (newFlags & PxConstraintFlag::ePROJECTION); + + if(needsProjection && !hadProjection) + { + PX_ASSERT(!readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)); // Non-projecting constrainst should not be part of the update list + + Sc::BodySim* b0 = getBody(0); + Sc::BodySim* b1 = getBody(1); + if ((!b0 || b0->getConstraintGroup()) && (!b1 || b1->getConstraintGroup())) + { + // Already part of a constraint group but not as a projection constraint -> re-generate projection tree + PX_ASSERT(b0 != NULL || b1 != NULL); + if (b0) + b0->getConstraintGroup()->markForProjectionTreeRebuild(mScene.getProjectionManager()); + else + b1->getConstraintGroup()->markForProjectionTreeRebuild(mScene.getProjectionManager()); + } + else + { + // Not part of a constraint group yet + mScene.getProjectionManager().addToPendingGroupUpdates(*this); + } + } + else if(!needsProjection && hadProjection) + { + if (!readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)) + { + Sc::BodySim* b = getConstraintGroupBody(); + if (b) + { + PX_ASSERT(b->getConstraintGroup()); + mScene.getProjectionManager().invalidateGroup(*b->getConstraintGroup(), NULL); + } + // This is conservative but it could be the case that this constraint with projection was the only + // one in the group and thus the whole group must be killed. If we had a counter for the number of + // projecting constraints per group, we could just update the projection tree if the counter was + // larger than 1. But switching the projection flag does not seem likely anyway. + } + else + mScene.getProjectionManager().removeFromPendingGroupUpdates(*this); // Was part of a group which got invalidated + + PX_ASSERT(!readFlag(ConstraintSim::ePENDING_GROUP_UPDATE)); // make sure the expected post-condition is met for all paths + } +} + +Sc::RigidSim& Sc::ConstraintSim::getRigid(PxU32 i) +{ + PX_ASSERT(mInteraction); + + if (i == 0) + return static_cast(mInteraction->getActorSim0()); + else + return static_cast(mInteraction->getActorSim1()); +} + +bool Sc::ConstraintSim::hasDynamicBody() +{ + return (mBodies[0] && (!mBodies[0]->isKinematic())) || (mBodies[1] && (!mBodies[1]->isKinematic())); +} + +static void constrainMotion(PxsRigidBody* body, PxTransform& targetPose) +{ + //Now constraint deltaPos and deltaRot + const PxU32 lockFlags = body->mCore->lockFlags; + + if (lockFlags) + { + const PxTransform& currBody2World = body->mCore->body2World; + + PxVec3 deltaPos = targetPose.p - currBody2World.p; + + PxQuat deltaQ = targetPose.q * currBody2World.q.getConjugate(); + + if (deltaQ.w < 0) //shortest angle. + deltaQ = -deltaQ; + + PxReal angle; + PxVec3 axis; + deltaQ.toRadiansAndUnitAxis(angle, axis); + PxVec3 deltaRot = axis * angle; + + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_X) + deltaPos.x = 0.0f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Y) + deltaPos.y = 0.0f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_LINEAR_Z) + deltaPos.z = 0.0f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_X) + deltaRot.x = 0.0f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y) + deltaRot.y = 0.0f; + if (lockFlags & PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z) + deltaRot.z = 0.0f; + + targetPose.p = currBody2World.p + deltaPos; + + PxReal w2 = deltaRot.magnitudeSquared(); + if (w2 != 0.0f) + { + PxReal w = PxSqrt(w2); + + const PxReal v = w * 0.5f; + PxReal s, q; + Ps::sincos(v, s, q); + s /= w; + + const PxVec3 pqr = deltaRot * s; + const PxQuat quatVel(pqr.x, pqr.y, pqr.z, 0); + PxQuat result = quatVel * currBody2World.q; + + result += currBody2World.q * q; + + targetPose.q = result.getNormalized(); + } + else + { + targetPose.q = currBody2World.q; + } + } +} + +void Sc::ConstraintSim::projectPose(BodySim* childBody, Ps::Array& projectedBodies) +{ +#if PX_DEBUG + // We expect bodies in low level constraints to have same order as high level counterpart + PxsRigidBody* b0 = mLowLevelConstraint.body0; + PxsRigidBody* b1 = mLowLevelConstraint.body1; + PX_ASSERT( (childBody == getBody(0) && &childBody->getLowLevelBody() == b0) || + (childBody == getBody(1) && &childBody->getLowLevelBody() == b1) ); +#endif + + Dy::Constraint& constraint = getLowLevelConstraint(); + bool projectToBody0 = childBody == getBody(1); + + PxsRigidBody* body0 = constraint.body0, + * body1 = constraint.body1; + + PxTransform body0ToWorld = body0 ? body0->getPose() : PxTransform(PxIdentity); + PxTransform body1ToWorld = body1 ? body1->getPose() : PxTransform(PxIdentity); + + (*constraint.project)(constraint.constantBlock, body0ToWorld, body1ToWorld, projectToBody0); + + if(projectToBody0) + { + PX_ASSERT(body1); + //Constrain new pose to valid world motion + constrainMotion(body1, body1ToWorld); + body1->setPose(body1ToWorld); + projectedBodies.pushBack(getBody(1)); + } + else + { + PX_ASSERT(body0); + //Constrain new pose to valid world motion + constrainMotion(body0, body0ToWorld); + body0->setPose(body0ToWorld); + projectedBodies.pushBack(getBody(0)); + } +} + +bool Sc::ConstraintSim::needsProjection() +{ + const Dy::ConstraintWriteback& solverOutput = mScene.getDynamicsContext()->getConstraintWriteBackPool()[mLowLevelConstraint.index]; + return (getCore().getFlags() & PxConstraintFlag::ePROJECTION ) && !solverOutput.broken; +} + +PX_INLINE Sc::BodySim* Sc::ConstraintSim::getConstraintGroupBody() +{ + BodySim* b = NULL; + if (mBodies[0] && mBodies[0]->getConstraintGroup()) + b = mBodies[0]; + else if (mBodies[1] && mBodies[1]->getConstraintGroup()) + b = mBodies[1]; + + return b; +} + +void Sc::ConstraintSim::visualize(PxRenderBuffer& output) +{ + if(!(getCore().getFlags() & PxConstraintFlag::eVISUALIZATION)) + return; + + PxsRigidBody* b0 = mLowLevelConstraint.body0; + PxsRigidBody* b1 = mLowLevelConstraint.body1; + + const PxTransform idt(PxIdentity); + const PxTransform& t0 = b0 ? b0->getPose() : idt; + const PxTransform& t1 = b1 ? b1->getPose() : idt; + + const PxReal frameScale = mScene.getVisualizationScale() * mScene.getVisualizationParameter(PxVisualizationParameter::eJOINT_LOCAL_FRAMES); + const PxReal limitScale = mScene.getVisualizationScale() * mScene.getVisualizationParameter(PxVisualizationParameter::eJOINT_LIMITS); + + Cm::RenderOutput renderOut(static_cast(output)); + Cm::ConstraintImmediateVisualizer viz(frameScale, limitScale, renderOut); + + PxU32 flags = 0; + if(frameScale!=0.0f) + flags |= PxConstraintVisualizationFlag::eLOCAL_FRAMES; + if(limitScale!=0.0f) + flags |= PxConstraintVisualizationFlag::eLIMITS; + + mCore.getVisualize()(viz, mLowLevelConstraint.constantBlock, t0, t1, flags); +} + +void Sc::ConstraintSim::setConstantsLL(void* addr) +{ + PxMemCopy(mLowLevelConstraint.constantBlock, addr, mLowLevelConstraint.constantBlockSize); + + getAnyBody()->getScene().getSimulationController()->updateJoint(mInteraction->getEdgeIndex(), &mLowLevelConstraint); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h new file mode 100644 index 000000000..30e3462cd --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h @@ -0,0 +1,156 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_CONSTRAINT_SIM +#define PX_PHYSICS_CONSTRAINT_SIM + +#include "PxSimulationEventCallback.h" +#include "DyConstraint.h" + +namespace physx +{ +namespace Sc +{ + + class Scene; + class ConstraintInteraction; + class ConstraintCore; + class RigidCore; + class BodySim; + class RigidSim; + + class ConstraintSim : public Ps::UserAllocated + { + public: + enum Enum + { + ePENDING_GROUP_UPDATE = (1<<0), // For constraint projection an island of the bodies connected by constraints is generated. + // Schedule generation/update of the island this constraint is a part of. + eBREAKABLE = (1<<1), // The constraint can break + eCHECK_MAX_FORCE_EXCEEDED = (1<<2), // This constraint will get tested for breakage at the end of the sim step + eBROKEN = (1<<3) + }; + + ConstraintSim(ConstraintCore& core, + RigidCore* r0, + RigidCore* r1, + Scene& scene); + + ~ConstraintSim(); + + void preBodiesChange(); + void postBodiesChange(RigidCore* r0, RigidCore* r1); + + void checkMaxForceExceeded(); + + void setBreakForceLL(PxReal linear, PxReal angular); + PX_INLINE void setMinResponseThresholdLL(PxReal threshold); + PX_INLINE void setAngularConstraintLinearCoefficientLL(PxReal coefficient); + void setConstantsLL(void* addr); + PX_INLINE const void* getConstantsLL() const; + + void postFlagChange(PxConstraintFlags oldFlags, PxConstraintFlags newFlags); + + PX_FORCE_INLINE const Dy::Constraint& getLowLevelConstraint() const { return mLowLevelConstraint; } + PX_FORCE_INLINE Dy::Constraint& getLowLevelConstraint() { return mLowLevelConstraint; } + PX_FORCE_INLINE ConstraintCore& getCore() const { return mCore; } + PX_FORCE_INLINE BodySim* getBody(PxU32 i) const // for static actors or world attached constraints NULL is returned + { + return mBodies[i]; + } + + RigidSim& getRigid(PxU32 i); + + void getForce(PxVec3& force, PxVec3& torque); + + PX_FORCE_INLINE PxU8 readFlag(PxU8 flag) const { return PxU8(mFlags & flag); } + PX_FORCE_INLINE void setFlag(PxU8 flag) { mFlags |= flag; } + PX_FORCE_INLINE void clearFlag(PxU8 flag) { mFlags &= ~flag; } + PX_FORCE_INLINE PxU32 isBroken() const { return PxU32(mFlags) & ConstraintSim::eBROKEN; } + + + //------------------------------------ Projection trees ----------------------------------------- + private: + PX_INLINE BodySim* getConstraintGroupBody(); + + public: + bool hasDynamicBody(); + + void projectPose(BodySim* childBody, Ps::Array& projectedBodies); + PX_INLINE BodySim* getOtherBody(BodySim*); + PX_INLINE BodySim* getAnyBody(); + + bool needsProjection(); + //----------------------------------------------------------------------------------------------- + + void visualize(PxRenderBuffer &out); + private: + ConstraintSim& operator=(const ConstraintSim&); + bool createLLConstraint(); + void destroyLLConstraint(); + private: + Dy::Constraint mLowLevelConstraint; + Scene& mScene; + ConstraintCore& mCore; + ConstraintInteraction* mInteraction; + BodySim* mBodies[2]; + PxU8 mFlags; + }; +} // namespace Sc + + +PX_INLINE void Sc::ConstraintSim::setMinResponseThresholdLL(PxReal threshold) +{ + mLowLevelConstraint.minResponseThreshold = threshold; +} + +PX_INLINE const void* Sc::ConstraintSim::getConstantsLL() const +{ + return mLowLevelConstraint.constantBlock; +} + + +PX_INLINE Sc::BodySim* Sc::ConstraintSim::getOtherBody(BodySim* b) +{ + return (b == mBodies[0]) ? mBodies[1] : mBodies[0]; +} + + +PX_INLINE Sc::BodySim* Sc::ConstraintSim::getAnyBody() +{ + if (mBodies[0]) + return mBodies[0]; + else + return mBodies[1]; +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h b/src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h new file mode 100644 index 000000000..84e95bc82 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h @@ -0,0 +1,174 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_CONTACTREPORTBUFFER +#define PX_PHYSICS_SCP_CONTACTREPORTBUFFER + +#include "foundation/Px.h" +#include "common/PxProfileZone.h" + +namespace physx +{ + namespace Sc + { + class ContactReportBuffer + { + public: + PX_FORCE_INLINE ContactReportBuffer(PxU32 initialSize, bool noResizeAllowed) + : mBuffer(NULL) + ,mCurrentBufferIndex(0) + ,mCurrentBufferSize(initialSize) + ,mDefaultBufferSize(initialSize) + ,mLastBufferIndex(0) + ,mAllocationLocked(noResizeAllowed) + { + mBuffer = allocateBuffer(initialSize); + PX_ASSERT(mBuffer); + } + + ~ContactReportBuffer() + { + PX_FREE(mBuffer); + } + + PX_FORCE_INLINE void reset(); + PX_FORCE_INLINE void flush(); + + PX_FORCE_INLINE PxU8* allocateNotThreadSafe(PxU32 size, PxU32& index, PxU32 alignment= 16); + PX_FORCE_INLINE PxU8* reallocateNotThreadSafe(PxU32 size, PxU32& index, PxU32 alignment= 16, PxU32 lastIndex = 0xFFFFFFFF); + PX_FORCE_INLINE PxU8* getData(const PxU32& index) const { return mBuffer+index; } + + PX_FORCE_INLINE PxU32 getDefaultBufferSize() const {return mDefaultBufferSize;} + + private: + PX_FORCE_INLINE PxU8* allocateBuffer(PxU32 size); + + private: + PxU8* mBuffer; + PxU32 mCurrentBufferIndex; + PxU32 mCurrentBufferSize; + PxU32 mDefaultBufferSize; + PxU32 mLastBufferIndex; + bool mAllocationLocked; + }; + + } // namespace Sc + + ////////////////////////////////////////////////////////////////////////// + + PX_FORCE_INLINE void Sc::ContactReportBuffer::reset() + { + mCurrentBufferIndex = 0; + mLastBufferIndex = 0xFFFFFFFF; + } + + ////////////////////////////////////////////////////////////////////////// + + void Sc::ContactReportBuffer::flush() + { + mCurrentBufferIndex = 0; + mLastBufferIndex = 0xFFFFFFFF; + + if(mCurrentBufferSize != mDefaultBufferSize) + { + PX_FREE(mBuffer); + + mBuffer = allocateBuffer(mDefaultBufferSize); + PX_ASSERT(mBuffer); + + mCurrentBufferSize = mDefaultBufferSize; + } + } + + ////////////////////////////////////////////////////////////////////////// + + PxU8* Sc::ContactReportBuffer::allocateNotThreadSafe(PxU32 size, PxU32& index ,PxU32 alignment/* =16 */) + { + PX_ASSERT(shdfnd::isPowerOfTwo(alignment)); + + // padding for alignment + PxU32 pad = ((mCurrentBufferIndex+alignment-1)&~(alignment-1)) - mCurrentBufferIndex; + + index = mCurrentBufferIndex + pad; + + if (index + size > mCurrentBufferSize) + { + PX_PROFILE_ZONE("ContactReportBuffer::Resize", 0); + if(mAllocationLocked) + return NULL; + + PxU32 oldBufferSize = mCurrentBufferSize; + while(index + size > mCurrentBufferSize) + { + mCurrentBufferSize *= 2; + } + + PxU8* tempBuffer = allocateBuffer(mCurrentBufferSize); + + PxMemCopy(tempBuffer,mBuffer,oldBufferSize); + + PX_FREE(mBuffer); + + mBuffer = tempBuffer; + } + + + PxU8* ptr = mBuffer + index; + mLastBufferIndex = index; + PX_ASSERT((reinterpret_cast(ptr)&(alignment-1)) == 0); + mCurrentBufferIndex += size + pad; + return ptr; + } + + ////////////////////////////////////////////////////////////////////////// + + PxU8* Sc::ContactReportBuffer::reallocateNotThreadSafe(PxU32 size, PxU32& index ,PxU32 alignment/* =16 */, PxU32 lastIndex) + { + if(lastIndex != mLastBufferIndex) + { + return allocateNotThreadSafe(size,index,alignment); + } + else + { + mCurrentBufferIndex = mLastBufferIndex; + return allocateNotThreadSafe(size,index,alignment); + } + } + + ////////////////////////////////////////////////////////////////////////// + + PX_FORCE_INLINE PxU8* Sc::ContactReportBuffer::allocateBuffer(PxU32 size) + { + return (static_cast(PX_ALLOC(size, "ContactReportBuffer"))); + } + +} // namespace physx + +#endif // PX_PHYSICS_SCP_CONTACTREPORTBUFFER diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h b/src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h new file mode 100644 index 000000000..0aa386fa8 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h @@ -0,0 +1,413 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_CONTACTSTREAM +#define PX_PHYSICS_SCP_CONTACTSTREAM + +#include "foundation/Px.h" +#include "PxSimulationEventCallback.h" +#include "ScObjectIDTracker.h" +#include "ScRigidSim.h" +#include "ScStaticSim.h" +#include "ScBodySim.h" + +namespace physx +{ + class PxShape; + +namespace Sc +{ + class ActorPair; + + + // Internal counterpart of PxContactPair + struct ContactShapePair + { + public: + PxShape* shapes[2]; + const PxU8* contactPatches; + const PxU8* contactPoints; + const PxReal* contactForces; + PxU32 requiredBufferSize; + PxU8 contactCount; + PxU8 patchCount; + PxU16 constraintStreamSize; + PxU16 flags; + PxU16 events; + PxU32 shapeID[2]; + //26 (or 38 on 64bit) + }; + PX_COMPILE_TIME_ASSERT(sizeof(ContactShapePair) == sizeof(PxContactPair)); + + struct ContactStreamManagerFlag + { + enum Enum + { + /** + \brief Need to test stream for shapes that were removed from the actor/scene + + Usually this is the case when a shape gets removed from the scene, however, other operations that remove the + broadphase volume of a pair object have to be considered as well since the shape might get removed later after such an + operation. The scenarios to consider are: + + \li shape gets removed (this includes raising PxActorFlag::eDISABLE_SIMULATION) + \li shape switches to eSCENE_QUERY_SHAPE only + \li shape switches to eTRIGGER_SHAPE + \li resetFiltering() + \li actor gets removed from an aggregate + + */ + eTEST_FOR_REMOVED_SHAPES = (1<<0), + + /** + \brief Invalid stream memory not allocated + */ + eINVALID_STREAM = (1<<1), + + /** + \brief Incomplete stream will be reported + */ + eINCOMPLETE_STREAM = (1<<2), + + /** + \brief The stream contains extra data with PxContactPairVelocity items where the post solver velocity needs to get written to. + Only valid for discrete collision (in CCD the post response velocity is available immediately). + */ + eNEEDS_POST_SOLVER_VELOCITY = (1<<3), + + /** + \brief Contains pairs that lost touch + + This info is used as an optimization to only parse the stream and check for removed shapes if there is a potential for + having removed shapes in the stream that won't get detected in any other way. For example, there is the scenario where + during the simulation a pair loses AABB touch and gets deleted. At that point a lost touch event might get written to the + stream. If at fetchResults a buffered shape removal takes place, and that shape was part of the mentioned pair, there is + no way any longer to make the connection to the corresponding event stream (since the pair has been deleted during the sim). + */ + eHAS_PAIRS_THAT_LOST_TOUCH = (1<<4), + + /** + \brief Marker for the next available free flag + */ + eNEXT_FREE_FLAG = (1<<5) + }; + }; + + struct ContactStreamHeader + { + PxU16 contactPass; // marker for extra data to know when a new collison pass started (discrete collision -> CCD pass 1 -> CCD pass 2 -> ...) + PxU16 pad; // to keep the stream 4byte aligned + }; + + /** + \brief Contact report logic and data management. + + The internal contact report stream has the following format: + + ContactStreamHeader | PxContactPairIndex0 | (PxContactPairPose0, PxContactPairVelocity0) | ... | PxContactPairIndexN | (PxContactPairPoseN, PxContactPairVelocityN) | (unused memory up to maxExtraDataSize ) | + PxContactPair0 | ... | PxContactPairM | (unsued pairs up to maxPairCount) + */ + class ContactStreamManager + { + public: + PX_FORCE_INLINE ContactStreamManager() : maxPairCount(0), flags_and_maxExtraDataBlocks(0) {} + PX_FORCE_INLINE ~ContactStreamManager() {} + + PX_FORCE_INLINE void reset(); + + PX_FORCE_INLINE PxU16 getFlags() const; + PX_FORCE_INLINE void raiseFlags(PxU16 flags); + PX_FORCE_INLINE void clearFlags(PxU16 flags); + + PX_FORCE_INLINE PxU32 getMaxExtraDataSize() const; + PX_FORCE_INLINE void setMaxExtraDataSize(PxU32 size); // size in bytes (will translate into blocks internally) + + PX_FORCE_INLINE Sc::ContactShapePair* getShapePairs(PxU8* contactReportPairData) const; + + PX_FORCE_INLINE static void convertDeletedShapesInContactStream(ContactShapePair*, PxU32 pairCount, const ObjectIDTracker&); + + PX_FORCE_INLINE static PxU32 computeExtraDataBlockCount(PxU32 extraDataSize); + PX_FORCE_INLINE static PxU32 computeExtraDataBlockSize(PxU32 extraDataSize); + PX_FORCE_INLINE static PxU16 computeContactReportExtraDataSize(PxU32 extraDataFlags, bool addHeader); + PX_FORCE_INLINE static void fillInContactReportExtraData(PxContactPairVelocity*, PxU32 index, const RigidSim&, bool isCCDPass); + PX_FORCE_INLINE static void fillInContactReportExtraData(PxContactPairPose*, PxU32 index, const RigidSim&, bool isCCDPass, const bool useCurrentTransform); + PX_FORCE_INLINE void fillInContactReportExtraData(PxU8* stream, PxU32 extraDataFlags, const RigidSim&, const RigidSim&, PxU32 ccdPass, const bool useCurrentTransform, PxU32 pairIndex, PxU32 sizeOffset); + PX_FORCE_INLINE void setContactReportPostSolverVelocity(PxU8* stream, const RigidSim&, const RigidSim&); + + PxU32 bufferIndex; // marks the start of the shape pair stream of the actor pair (byte offset with respect to global contact buffer stream) + PxU16 maxPairCount; // used to reserve the same amount of memory as in the last frame (as an initial guess) + PxU16 currentPairCount; // number of shape pairs stored in the buffer + PxU16 extraDataSize; // size of the extra data section in the stream + private: + PxU16 flags_and_maxExtraDataBlocks; // used to reserve the same amount of memory as in the last frame (as an initial guess) + + public: + static const PxU32 sExtraDataBlockSizePow2 = 4; // extra data gets allocated as a multiple of 2^sExtraDataBlockSizePow2 to keep memory low of this struct. + static const PxU32 sFlagMask = (ContactStreamManagerFlag::eNEXT_FREE_FLAG - 1); + static const PxU32 sMaxExtraDataShift = 5; // shift necessary to extract the maximum number of blocks allocated for extra data + + PX_COMPILE_TIME_ASSERT(ContactStreamManagerFlag::eNEXT_FREE_FLAG == (1 << sMaxExtraDataShift)); + }; + +} // namespace Sc + + +PX_FORCE_INLINE void Sc::ContactStreamManager::reset() +{ + currentPairCount = 0; + extraDataSize = 0; + flags_and_maxExtraDataBlocks &= ~sFlagMask; +} + + +PX_FORCE_INLINE PxU16 Sc::ContactStreamManager::getFlags() const +{ + return (flags_and_maxExtraDataBlocks & sFlagMask); +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::raiseFlags(PxU16 flags) +{ + PX_ASSERT(flags < ContactStreamManagerFlag::eNEXT_FREE_FLAG); + + flags_and_maxExtraDataBlocks |= flags; +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::clearFlags(PxU16 flags) +{ + PX_ASSERT(flags < ContactStreamManagerFlag::eNEXT_FREE_FLAG); + + PxU16 tmpFlags = getFlags(); + tmpFlags &= ~flags; + flags_and_maxExtraDataBlocks &= ~sFlagMask; + raiseFlags(tmpFlags); +} + + +PX_FORCE_INLINE PxU32 Sc::ContactStreamManager::getMaxExtraDataSize() const +{ + return PxU32((flags_and_maxExtraDataBlocks >> sMaxExtraDataShift) << sExtraDataBlockSizePow2); +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::setMaxExtraDataSize(PxU32 size) +{ + PxU32 nbBlocks = computeExtraDataBlockCount(size); + flags_and_maxExtraDataBlocks = Ps::to16((flags_and_maxExtraDataBlocks & sFlagMask) | (nbBlocks << sMaxExtraDataShift)); +} + + +PX_FORCE_INLINE Sc::ContactShapePair* Sc::ContactStreamManager::getShapePairs(PxU8* contactReportPairData) const +{ + return reinterpret_cast(contactReportPairData + getMaxExtraDataSize()); +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::convertDeletedShapesInContactStream(ContactShapePair* shapePairs, PxU32 pairCount, const ObjectIDTracker& tracker) +{ + for(PxU32 i=0; i < pairCount; i++) + { + ContactShapePair& csp = shapePairs[i]; + + PxU32 shape0ID = csp.shapeID[0]; + PxU32 shape1ID = csp.shapeID[1]; + + PxU16 flags = csp.flags; + PX_COMPILE_TIME_ASSERT(sizeof(flags) == sizeof((reinterpret_cast(0))->flags)); + + if (tracker.isDeletedID(shape0ID)) + flags |= PxContactPairFlag::eREMOVED_SHAPE_0; + if (tracker.isDeletedID(shape1ID)) + flags |= PxContactPairFlag::eREMOVED_SHAPE_1; + + csp.flags = flags; + } +} + + +PX_FORCE_INLINE PxU32 Sc::ContactStreamManager::computeExtraDataBlockCount(PxU32 extraDataSize_) +{ + PxU32 nbBlocks; + if (extraDataSize_ & ((1 << sExtraDataBlockSizePow2) - 1)) // not a multiple of block size -> need one block more + nbBlocks = (extraDataSize_ >> sExtraDataBlockSizePow2) + 1; + else + nbBlocks = (extraDataSize_ >> sExtraDataBlockSizePow2); + + return nbBlocks; +} + + +PX_FORCE_INLINE PxU32 Sc::ContactStreamManager::computeExtraDataBlockSize(PxU32 extraDataSize_) +{ + return (computeExtraDataBlockCount(extraDataSize_) << sExtraDataBlockSizePow2); +} + + +PX_FORCE_INLINE PxU16 Sc::ContactStreamManager::computeContactReportExtraDataSize(PxU32 extraDataFlags, bool addHeader) +{ + PX_ASSERT(extraDataFlags); + + PxU16 extraDataSize_ = sizeof(PxContactPairIndex); + if (extraDataFlags & PxPairFlag::ePRE_SOLVER_VELOCITY) + extraDataSize_ += sizeof(PxContactPairVelocity); + if (extraDataFlags & PxPairFlag::ePOST_SOLVER_VELOCITY) + extraDataSize_ += sizeof(PxContactPairVelocity); + if (extraDataFlags & PxPairFlag::eCONTACT_EVENT_POSE) + extraDataSize_ += sizeof(PxContactPairPose); + if (addHeader) + extraDataSize_ += sizeof(ContactStreamHeader); + return extraDataSize_; +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::fillInContactReportExtraData(PxContactPairVelocity* cpVel, PxU32 index, const RigidSim& rs, bool isCCDPass) +{ + if (rs.getActorType() != PxActorType::eRIGID_STATIC) + { + const BodySim& bs = static_cast(rs); + if ((!isCCDPass) || (cpVel->type == PxContactPairExtraDataType::ePOST_SOLVER_VELOCITY)) + { + const BodyCore& bc = bs.getBodyCore(); + cpVel->linearVelocity[index] = bc.getLinearVelocity(); + cpVel->angularVelocity[index] = bc.getAngularVelocity(); + } + else + { + PX_ASSERT(cpVel->type == PxContactPairExtraDataType::ePRE_SOLVER_VELOCITY); + const Cm::SpatialVector& vel = bs.getLowLevelBody().getPreSolverVelocities(); + cpVel->linearVelocity[index] = vel.linear; + cpVel->angularVelocity[index] = vel.angular; + } + } + else + { + cpVel->linearVelocity[index] = PxVec3(0.0f); + cpVel->angularVelocity[index] = PxVec3(0.0f); + } +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::fillInContactReportExtraData(PxContactPairPose* cpPose, PxU32 index, const RigidSim& rs, bool isCCDPass, const bool useCurrentTransform) +{ + if(rs.getActorType() != PxActorType::eRIGID_STATIC) + { + const BodySim& bs = static_cast(rs); + const BodyCore& bc = bs.getBodyCore(); + const PxTransform& src = (!isCCDPass && useCurrentTransform) ? bc.getBody2World() : bs.getLowLevelBody().getLastCCDTransform(); + cpPose->globalPose[index] = src * bc.getBody2Actor().getInverse(); + } + else + { + const StaticSim& ss = static_cast(rs); + const StaticCore& sc = ss.getStaticCore(); + cpPose->globalPose[index] = sc.getActor2World(); + } +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::fillInContactReportExtraData(PxU8* stream, PxU32 extraDataFlags, const RigidSim& rs0, const RigidSim& rs1, PxU32 ccdPass, const bool useCurrentTransform, + PxU32 pairIndex, PxU32 sizeOffset) +{ + ContactStreamHeader* strHeader = reinterpret_cast(stream); + strHeader->contactPass = Ps::to16(ccdPass); + + stream += sizeOffset; + PxU8* edStream = stream; + bool isCCDPass = (ccdPass != 0); + + { + PxContactPairIndex* cpIndex = reinterpret_cast(edStream); + cpIndex->type = PxContactPairExtraDataType::eCONTACT_PAIR_INDEX; + cpIndex->index = Ps::to16(pairIndex); + edStream += sizeof(PxContactPairIndex); + + PX_ASSERT(edStream <= reinterpret_cast(getShapePairs(stream))); + } + + // Important: make sure this one is the first after the PxContactPairIndex item for discrete contacts as it needs to get filled in before the reports get sent + // (post solver velocity is not available when it gets created) + if (extraDataFlags & PxPairFlag::ePOST_SOLVER_VELOCITY) + { + PxContactPairVelocity* cpVel = reinterpret_cast(edStream); + cpVel->type = PxContactPairExtraDataType::ePOST_SOLVER_VELOCITY; + edStream += sizeof(PxContactPairVelocity); + + if (!isCCDPass) + raiseFlags(ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY); // don't know the post solver velocity yet + else + { + ContactStreamManager::fillInContactReportExtraData(cpVel, 0, rs0, true); + ContactStreamManager::fillInContactReportExtraData(cpVel, 1, rs1, true); + } + + PX_ASSERT(edStream <= reinterpret_cast(getShapePairs(stream))); + } + if (extraDataFlags & PxPairFlag::ePRE_SOLVER_VELOCITY) + { + PxContactPairVelocity* cpVel = reinterpret_cast(edStream); + cpVel->type = PxContactPairExtraDataType::ePRE_SOLVER_VELOCITY; + ContactStreamManager::fillInContactReportExtraData(cpVel, 0, rs0, isCCDPass); + ContactStreamManager::fillInContactReportExtraData(cpVel, 1, rs1, isCCDPass); + edStream += sizeof(PxContactPairVelocity); + + PX_ASSERT(edStream <= reinterpret_cast(getShapePairs(stream))); + } + if (extraDataFlags & PxPairFlag::eCONTACT_EVENT_POSE) + { + PxContactPairPose* cpPose = reinterpret_cast(edStream); + cpPose->type = PxContactPairExtraDataType::eCONTACT_EVENT_POSE; + ContactStreamManager::fillInContactReportExtraData(cpPose, 0, rs0, isCCDPass, useCurrentTransform); + ContactStreamManager::fillInContactReportExtraData(cpPose, 1, rs1, isCCDPass, useCurrentTransform); + edStream += sizeof(PxContactPairPose); + + PX_ASSERT(edStream <= reinterpret_cast(getShapePairs(stream))); + } + + extraDataSize = Ps::to16(sizeOffset + PxU32(edStream - stream)); +} + + +PX_FORCE_INLINE void Sc::ContactStreamManager::setContactReportPostSolverVelocity(PxU8* stream, const RigidSim& rs0, const RigidSim& rs1) +{ + PX_ASSERT(extraDataSize > (sizeof(ContactStreamHeader) + sizeof(PxContactPairIndex))); + PxContactPairVelocity* cpVel = reinterpret_cast(stream + sizeof(ContactStreamHeader) + sizeof(PxContactPairIndex)); + PX_ASSERT(cpVel->type == PxContactPairExtraDataType::ePOST_SOLVER_VELOCITY); + + fillInContactReportExtraData(cpVel, 0, rs0, false); + fillInContactReportExtraData(cpVel, 1, rs1, false); + + clearFlags(ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY); +} + + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp new file mode 100644 index 000000000..7bf63fd1e --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp @@ -0,0 +1,45 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScElementInteractionMarker.h" +#include "ScNPhaseCore.h" + +using namespace physx; + +Sc::ElementInteractionMarker::~ElementInteractionMarker() +{ + if(isRegistered()) + { + Scene& scene = getScene(); + scene.unregisterInteraction(this); + scene.getNPhaseCore()->unregisterInteraction(this); + } + unregisterFromActors(); +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h new file mode 100644 index 000000000..5af3eba15 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h @@ -0,0 +1,68 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_COLLISION_ELEMENT_INTERACTION_MARKER +#define PX_COLLISION_ELEMENT_INTERACTION_MARKER + +#include "ScElementSimInteraction.h" +#include "ScNPhaseCore.h" + +namespace physx +{ +namespace Sc +{ + class ElementInteractionMarker : public ElementSimInteraction + { + public: + PX_INLINE ElementInteractionMarker(ElementSim& element0, ElementSim& element1, bool createParallel/* = false*/); + ~ElementInteractionMarker(); + + bool onActivate_(void*) { return false; } + bool onDeactivate_() { return true; } + }; + +} // namespace Sc + + +PX_INLINE Sc::ElementInteractionMarker::ElementInteractionMarker(ElementSim& element0, ElementSim& element1, bool createParallel) : + ElementSimInteraction(element0, element1, InteractionType::eMARKER, InteractionFlag::eRB_ELEMENT|InteractionFlag::eFILTERABLE) +{ + if(!createParallel) + { + bool active = registerInActors(); + PX_UNUSED(active); + PX_ASSERT(!active); + getScene().registerInteraction(this, false); + getScene().getNPhaseCore()->registerInteraction(this); + } +} + +} + +#endif //PX_COLLISION_SHAPEINTERACTIONMARKER diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp new file mode 100644 index 000000000..717295989 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp @@ -0,0 +1,133 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "PsFoundation.h" +#include "PxsContext.h" +#include "ScElementSim.h" +#include "ScElementSimInteraction.h" +#include "ScSqBoundsManager.h" +#include "ScSimStats.h" + +using namespace physx; +using namespace Sc; + +static PX_FORCE_INLINE bool interactionHasElement(const Interaction* it, const ElementSim* elem) +{ + if(it->readInteractionFlag(InteractionFlag::eRB_ELEMENT)) + { + PX_ASSERT( (it->getType() == InteractionType::eMARKER) || + (it->getType() == InteractionType::eOVERLAP) || + (it->getType() == InteractionType::eTRIGGER) ); + + const ElementSimInteraction* ei = static_cast(it); + if((&ei->getElement0() == elem) || (&ei->getElement1() == elem)) + return true; + } + return false; +} + +Sc::ElementSimInteraction* Sc::ElementSim::ElementInteractionIterator::getNext() +{ + while(mInteractions!=mInteractionsLast) + { + Interaction* it = *mInteractions++; + if(interactionHasElement(it, mElement)) + return static_cast(it); + } + return NULL; +} + +Sc::ElementSimInteraction* Sc::ElementSim::ElementInteractionReverseIterator::getNext() +{ + while(mInteractions!=mInteractionsLast) + { + Interaction* it = *--mInteractionsLast; + if(interactionHasElement(it, mElement)) + return static_cast(it); + } + return NULL; +} + +Sc::ElementSim::ElementSim(ActorSim& actor) : + mNextInActor (NULL), + mActor (actor), + mInBroadPhase (false) +{ + initID(); + + actor.onElementAttach(*this); +} + +Sc::ElementSim::~ElementSim() +{ + PX_ASSERT(!mInBroadPhase); + releaseID(); + mActor.onElementDetach(*this); +} + +void Sc::ElementSim::setElementInteractionsDirty(InteractionDirtyFlag::Enum flag, PxU8 interactionFlag) +{ + ElementSim::ElementInteractionIterator iter = getElemInteractions(); + ElementSimInteraction* interaction = iter.getNext(); + while(interaction) + { + if(interaction->readInteractionFlag(interactionFlag)) + interaction->setDirty(flag); + + interaction = iter.getNext(); + } +} + +void Sc::ElementSim::addToAABBMgr(PxReal contactDistance, Bp::FilterGroup::Enum group, Ps::IntBool isTrigger) +{ + Sc::Scene& scene = getScene(); + if(!scene.getAABBManager()->addBounds(mElementID, contactDistance, group, this, mActor.getActorCore().getAggregateID(), isTrigger ? Bp::ElementType::eTRIGGER : Bp::ElementType::eSHAPE)) + { + Ps::getFoundation().error(PxErrorCode::eINTERNAL_ERROR, __FILE__, __LINE__, "Unable to create broadphase entity because only 32768 shapes are supported"); + return; + } + mInBroadPhase = true; +#if PX_ENABLE_SIM_STATS + scene.getStatsInternal().incBroadphaseAdds(); +#endif +} + +void Sc::ElementSim::removeFromAABBMgr() +{ + PX_ASSERT(mInBroadPhase); + Sc::Scene& scene = getScene(); + scene.getAABBManager()->removeBounds(mElementID); + scene.getAABBManager()->getChangedAABBMgActorHandleMap().growAndReset(mElementID); + + mInBroadPhase = false; +#if PX_ENABLE_SIM_STATS + scene.getStatsInternal().incBroadphaseRemoves(); +#endif +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h new file mode 100644 index 000000000..61cf51868 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h @@ -0,0 +1,130 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_ELEMENT_SIM +#define PX_PHYSICS_SCP_ELEMENT_SIM + +#include "PsUserAllocated.h" +#include "PxFiltering.h" +#include "PxvConfig.h" +#include "ScActorSim.h" +#include "ScInteraction.h" +#include "BpAABBManager.h" +#include "ScObjectIDTracker.h" + +namespace physx +{ +namespace Sc +{ + class ElementSimInteraction; + + /* + A ElementSim is a part of a ActorSim. It contributes to the activation framework by adding its + interactions to the actor. */ + class ElementSim : public Ps::UserAllocated + { + PX_NOCOPY(ElementSim) + + public: + class ElementInteractionIterator + { + public: + PX_FORCE_INLINE ElementInteractionIterator(const ElementSim& e, PxU32 nbInteractions, Interaction** interactions) : + mInteractions(interactions), mInteractionsLast(interactions + nbInteractions), mElement(&e) {} + ElementSimInteraction* getNext(); + + private: + Interaction** mInteractions; + Interaction** mInteractionsLast; + const ElementSim* mElement; + }; + + class ElementInteractionReverseIterator + { + public: + PX_FORCE_INLINE ElementInteractionReverseIterator(const ElementSim& e, PxU32 nbInteractions, Interaction** interactions) : + mInteractions(interactions), mInteractionsLast(interactions + nbInteractions), mElement(&e) {} + ElementSimInteraction* getNext(); + + private: + Interaction** mInteractions; + Interaction** mInteractionsLast; + const ElementSim* mElement; + }; + + ElementSim(ActorSim& actor); + protected: + ~ElementSim(); + public: + + // Get an iterator to the interactions connected to the element + PX_FORCE_INLINE ElementInteractionIterator getElemInteractions() const { return ElementInteractionIterator(*this, mActor.getActorInteractionCount(), mActor.getActorInteractions()); } + PX_FORCE_INLINE ElementInteractionReverseIterator getElemInteractionsReverse() const { return ElementInteractionReverseIterator(*this, mActor.getActorInteractionCount(), mActor.getActorInteractions()); } + + PX_FORCE_INLINE ActorSim& getActor() const { return mActor; } + + PX_FORCE_INLINE Scene& getScene() const { return mActor.getScene(); } + + PX_FORCE_INLINE PxU32 getElementID() const { return mElementID; } + PX_FORCE_INLINE bool isInBroadPhase() const { return mInBroadPhase; } + + void addToAABBMgr(PxReal contactDistance, Bp::FilterGroup::Enum group, Ps::IntBool isTrigger); + void removeFromAABBMgr(); + + void setElementInteractionsDirty(InteractionDirtyFlag::Enum flag, PxU8 interactionFlag); + + PX_FORCE_INLINE void initID() + { + Scene& scene = getScene(); + mElementID = scene.getElementIDPool().createID(); + scene.getBoundsArray().initEntry(mElementID); + } + + PX_FORCE_INLINE void releaseID() + { + getScene().getElementIDPool().releaseID(mElementID); + } + public: + ElementSim* mNextInActor; + private: + ActorSim& mActor; + + PxU32 mElementID : 31; + PxU32 mInBroadPhase : 1; + }; + + PX_FORCE_INLINE void setFilterObjectAttributeType(PxFilterObjectAttributes& attr, PxFilterObjectType::Enum type) + { + PX_ASSERT((attr & (PxFilterObjectType::eMAX_TYPE_COUNT-1)) == 0); + attr |= type; + } +} // namespace Sc +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h new file mode 100644 index 000000000..ccb52bbe0 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h @@ -0,0 +1,77 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_ELEMENT_SIM_INTERACTION +#define PX_PHYSICS_SCP_ELEMENT_SIM_INTERACTION + +#include "ScInteraction.h" +#include "ScElementSim.h" + +namespace physx +{ +namespace Sc +{ + class ElementSimInteraction : public Interaction + { + public: + PX_FORCE_INLINE ElementSim& getElement0() const { return mElement0; } + PX_FORCE_INLINE ElementSim& getElement1() const { return mElement1; } + + PX_FORCE_INLINE void setFilterPairIndex(PxU32 filterPairIndex) { mFilterPairIndex = filterPairIndex; } + PX_FORCE_INLINE PxU32 getFilterPairIndex() const { return mFilterPairIndex; } + + protected: + PX_INLINE ElementSimInteraction(ElementSim& element0, ElementSim& element1, InteractionType::Enum type, PxU8 flags); + virtual ~ElementSimInteraction() {} + + ElementSimInteraction& operator=(const ElementSimInteraction&); + + private: + ElementSim& mElement0; + ElementSim& mElement1; + PxU32 mFilterPairIndex; + }; + +} // namespace Sc + +////////////////////////////////////////////////////////////////////////// + +PX_INLINE Sc::ElementSimInteraction::ElementSimInteraction(ElementSim& element0, ElementSim& element1, InteractionType::Enum type, PxU8 flags) : + Interaction (element0.getActor(), element1.getActor(), type, flags), + mElement0 (element0), + mElement1 (element1), + mFilterPairIndex(INVALID_FILTER_PAIR_INDEX) +{ +} + + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp new file mode 100644 index 000000000..360db5796 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp @@ -0,0 +1,71 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/Px.h" + +#include "ScInteraction.h" +#include "ScNPhaseCore.h" + +using namespace physx; + +Sc::Interaction::Interaction(ActorSim& actor0, ActorSim& actor1, InteractionType::Enum type, PxU8 flags) : + mActor0 (actor0), + mActor1 (actor1), + mSceneId (PX_INVALID_INTERACTION_SCENE_ID), + mActorId0 (PX_INVALID_INTERACTION_ACTOR_ID), + mActorId1 (PX_INVALID_INTERACTION_ACTOR_ID), + mInteractionType (Ps::to8(type)), + mInteractionFlags (flags), + mDirtyFlags (0) +{ + PX_ASSERT_WITH_MESSAGE(&actor0.getScene() == &actor1.getScene(),"Cannot create an interaction between actors belonging to different scenes."); + PX_ASSERT(PxU32(type)<256); // PT: type is now stored on a byte +} + +void Sc::Interaction::addToDirtyList() +{ + getActorSim0().getScene().getNPhaseCore()->addToDirtyInteractionList(this); +} + +void Sc::Interaction::removeFromDirtyList() +{ + getActorSim0().getScene().getNPhaseCore()->removeFromDirtyInteractionList(this); +} + +void Sc::Interaction::setClean(bool removeFromList) +{ + if (readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)) + { + if (removeFromList) // if we process all dirty interactions anyway, then we can just clear the list at the end and save the work here. + removeFromDirtyList(); + clearInteractionFlag(InteractionFlag::eIN_DIRTY_LIST); + } + + mDirtyFlags = 0; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h new file mode 100644 index 000000000..effdddd5b --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h @@ -0,0 +1,210 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_INTERACTION +#define PX_PHYSICS_SCP_INTERACTION + +#include "foundation/Px.h" +#include "ScInteractionFlags.h" +#include "ScScene.h" +#include "ScActorSim.h" +#include "PsUserAllocated.h" +#include "PsUtilities.h" +#include "PsFoundation.h" + +namespace physx +{ +#define PX_INVALID_INTERACTION_ACTOR_ID 0xffffffff +#define PX_INVALID_INTERACTION_SCENE_ID 0xffffffff + +namespace Sc +{ + // Interactions are used for connecting actors into activation groups. An interaction always connects exactly two actors. + // An interaction is implicitly active if at least one of the two actors it connects is active. + + class Interaction : public Ps::UserAllocated + { + PX_NOCOPY(Interaction) + + protected: + Interaction(ActorSim& actor0, ActorSim& actor1, InteractionType::Enum interactionType, PxU8 flags); + ~Interaction() { PX_ASSERT(!readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)); } + + public: + // Interactions automatically register themselves in the actors here + PX_FORCE_INLINE bool registerInActors(void* data = NULL); + + // Interactions automatically unregister themselves from the actors here + PX_FORCE_INLINE void unregisterFromActors(); + + PX_FORCE_INLINE ActorSim& getActorSim0() const { return mActor0; } + PX_FORCE_INLINE ActorSim& getActorSim1() const { return mActor1; } + + PX_FORCE_INLINE Scene& getScene() const { return mActor0.getScene(); } + + PX_FORCE_INLINE InteractionType::Enum getType() const { return InteractionType::Enum(mInteractionType); } + + PX_FORCE_INLINE PxU8 readInteractionFlag(PxU8 flag) const { return PxU8(mInteractionFlags & flag); } + PX_FORCE_INLINE void raiseInteractionFlag(InteractionFlag::Enum flag) { mInteractionFlags |= flag; } + PX_FORCE_INLINE void clearInteractionFlag(InteractionFlag::Enum flag) { mInteractionFlags &= ~flag; } + + /** + \brief Mark the interaction as dirty. This will put the interaction into a list that is processed once per simulation step. + + @see InteractionDirtyFlag + */ + PX_FORCE_INLINE void setDirty(PxU32 dirtyFlags); + + /** + \brief Clear all flags that mark the interaction as dirty and optionally remove the interaction from the list of dirty interactions. + + @see InteractionDirtyFlag + */ + /*PX_FORCE_INLINE*/ void setClean(bool removeFromList); + + PX_FORCE_INLINE Ps::IntBool needsRefiltering() const { return (getDirtyFlags() & InteractionDirtyFlag::eFILTER_STATE); } + + PX_FORCE_INLINE Ps::IntBool isElementInteraction() const; + + // Called when an interaction is activated or created. + // Return true if activation should proceed else return false (for example: joint interaction between two kinematics should not get activated) +// virtual bool onActivate_(void* data) = 0; + + // Called when an interaction is deactivated. + // Return true if deactivation should proceed else return false (for example: joint interaction between two kinematics can ignore deactivation because it always is deactivated) +// virtual bool onDeactivate_() = 0; + + PX_FORCE_INLINE void setInteractionId(PxU32 id) { mSceneId = id; } + PX_FORCE_INLINE PxU32 getInteractionId() const { return mSceneId; } + PX_FORCE_INLINE bool isRegistered() const { return mSceneId != PX_INVALID_INTERACTION_SCENE_ID; } + + PX_FORCE_INLINE void setActorId(ActorSim* actor, PxU32 id); + PX_FORCE_INLINE PxU32 getActorId(const ActorSim* actor) const; + + PX_FORCE_INLINE PxU8 getDirtyFlags() const { return mDirtyFlags; } + + private: + void addToDirtyList(); + void removeFromDirtyList(); + + ActorSim& mActor0; + ActorSim& mActor1; + + // PT: TODO: merge the 6bits of the 3 PxU8s in the top bits of the 3 PxU32s + PxU32 mSceneId; // PT: TODO: merge this with mInteractionType + + // PT: TODO: are those IDs even worth caching? Since the number of interactions per actor is (or should be) small, + // we could just do a linear search and save memory here... + PxU32 mActorId0; // PT: id of this interaction within mActor0's mInteractions array + PxU32 mActorId1; // PT: id of this interaction within mActor1's mInteractions array + protected: + PxU8 mInteractionType; // PT: stored on a byte to save space, should be InteractionType enum, 5/6 bits needed here + PxU8 mInteractionFlags; // PT: 6 bits needed here, see InteractionFlag enum + PxU8 mDirtyFlags; // PT: 6 bits needed here, see InteractionDirtyFlag enum + PxU8 mPadding; + }; + +} // namespace Sc + +////////////////////////////////////////////////////////////////////////// + +PX_FORCE_INLINE bool Sc::Interaction::registerInActors(void* data) +{ + bool active = activateInteraction(this, data); + + mActor0.registerInteractionInActor(this); + mActor1.registerInteractionInActor(this); + + return active; +} + +PX_FORCE_INLINE void Sc::Interaction::unregisterFromActors() +{ + mActor0.unregisterInteractionFromActor(this); + mActor1.unregisterInteractionFromActor(this); +} + +PX_FORCE_INLINE void Sc::Interaction::setActorId(ActorSim* actor, PxU32 id) +{ + PX_ASSERT(id != PX_INVALID_INTERACTION_ACTOR_ID); + PX_ASSERT(&mActor0 == actor || &mActor1 == actor); + if(&mActor0 == actor) + mActorId0 = id; + else + mActorId1 = id; +} + +PX_FORCE_INLINE PxU32 Sc::Interaction::getActorId(const ActorSim* actor) const +{ + PX_ASSERT(&mActor0 == actor || &mActor1 == actor); + return &mActor0 == actor ? mActorId0 : mActorId1; +} + +PX_FORCE_INLINE Ps::IntBool Sc::Interaction::isElementInteraction() const +{ + const Ps::IntBool res = readInteractionFlag(InteractionFlag::eRB_ELEMENT); + PX_ASSERT( (res && + ((getType() == InteractionType::eOVERLAP) || + (getType() == InteractionType::eTRIGGER) || + (getType() == InteractionType::eMARKER))) || + (!res && + ((getType() == InteractionType::eCONSTRAINTSHADER) || + (getType() == InteractionType::eARTICULATION)))); + + return res; +} + +PX_FORCE_INLINE void Sc::Interaction::setDirty(PxU32 dirtyFlags) +{ + PX_ASSERT(getType() != InteractionType::eARTICULATION); + + mDirtyFlags |= Ps::to8(dirtyFlags); + if(!readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)) + { + addToDirtyList(); + raiseInteractionFlag(InteractionFlag::eIN_DIRTY_LIST); + } +} + +//PX_FORCE_INLINE void Sc::Interaction::setClean(bool removeFromList) +//{ +// if (readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)) +// { +// if (removeFromList) // if we process all dirty interactions anyway, then we can just clear the list at the end and save the work here. +// removeFromDirtyList(); +// clearInteractionFlag(InteractionFlag::eIN_DIRTY_LIST); +// } +// +// mDirtyFlags = 0; +//} + + +} + +#endif // PX_PHYSICS_SCP_INTERACTION diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h b/src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h new file mode 100644 index 000000000..cd2b8eaa9 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_INTERACTION_FLAGS +#define PX_PHYSICS_SCP_INTERACTION_FLAGS + +#include "foundation/Px.h" + +namespace physx +{ + +namespace Sc +{ + struct InteractionFlag // PT: TODO: use PxFlags + { + enum Enum + { + eRB_ELEMENT = (1 << 0), // Interactions between rigid body shapes + eCONSTRAINT = (1 << 1), + eFILTERABLE = (1 << 2), // Interactions that go through the filter code + eIN_DIRTY_LIST = (1 << 3), // The interaction is in the dirty list + eIS_FILTER_PAIR = (1 << 4), // The interaction is tracked by the filter callback mechanism + eIS_ACTIVE = (1 << 5) + }; + }; + + struct InteractionDirtyFlag + { + enum Enum + { + eFILTER_STATE = (1 << 0), // All changes filtering related + eMATERIAL = (1 << 1), + eBODY_KINEMATIC = (1 << 2) | eFILTER_STATE, // A transition between dynamic and kinematic (and vice versa) require a refiltering + eDOMINANCE = (1 << 3), + eREST_OFFSET = (1 << 4), + eVISUALIZATION = (1 << 5) + }; + }; + + +} // namespace Sc + + +} // namespace physx + + +#endif // PX_PHYSICS_SCP_INTERACTION_FLAGS diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp new file mode 100644 index 000000000..2b4ad6153 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp @@ -0,0 +1,105 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScIterators.h" +#include "ScBodySim.h" +#include "ScShapeSim.h" +#include "ScShapeInteraction.h" + +using namespace physx; + +/////////////////////////////////////////////////////////////////////////////// + +Sc::ContactIterator::Pair::Pair(const void*& contactPatches, const void*& contactPoints, PxU32 /*contactDataSize*/, const PxReal*& forces, PxU32 numContacts, PxU32 numPatches, + ShapeSim& shape0, ShapeSim& shape1) +: mIndex(0) +, mNumContacts(numContacts) +, mIter(reinterpret_cast(contactPatches), reinterpret_cast(contactPoints), reinterpret_cast(forces + numContacts), numPatches, numContacts) +, mForces(forces) +{ + mCurrentContact.shape0 = shape0.getPxShape(); + mCurrentContact.shape1 = shape1.getPxShape(); + mCurrentContact.normalForceAvailable = (forces != NULL); +} + +Sc::ContactIterator::Pair* Sc::ContactIterator::getNextPair() +{ + if(mCurrent < mLast) + { + ShapeInteraction* si = static_cast(*mCurrent); + + const void* contactPatches = NULL; + const void* contactPoints = NULL; + PxU32 contactDataSize = 0; + const PxReal* forces = NULL; + PxU32 numContacts = 0; + PxU32 numPatches = 0; + + PxU32 nextOffset = si->getContactPointData(contactPatches, contactPoints, contactDataSize, numContacts, numPatches, forces, mOffset, *mOutputs); + + if (nextOffset == mOffset) + ++mCurrent; + else + mOffset = nextOffset; + + mCurrentPair = Pair(contactPatches, contactPoints, contactDataSize, forces, numContacts, numPatches, si->getShape0(), si->getShape1()); + return &mCurrentPair; + } + else + return NULL; +} + +Sc::Contact* Sc::ContactIterator::Pair::getNextContact() +{ + if(mIndex < mNumContacts) + { + if(!mIter.hasNextContact()) + { + if(!mIter.hasNextPatch()) + return NULL; + mIter.nextPatch(); + } + PX_ASSERT(mIter.hasNextContact()); + mIter.nextContact(); + + mCurrentContact.normal = mIter.getContactNormal(); + mCurrentContact.point = mIter.getContactPoint(); + mCurrentContact.separation = mIter.getSeparation(); + mCurrentContact.normalForce = mForces ? mForces[mIndex] : 0; + mCurrentContact.faceIndex0 = mIter.getFaceIndex0(); + mCurrentContact.faceIndex1 = mIter.getFaceIndex1(); + + mIndex++; + return &mCurrentContact; + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp new file mode 100644 index 000000000..1a7105e97 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp @@ -0,0 +1,424 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxIO.h" +#include "ScActorCore.h" +#include "ScActorSim.h" +#include "ScBodyCore.h" +#include "ScStaticCore.h" +#include "ScConstraintCore.h" +#include "ScMaterialCore.h" +#include "ScShapeCore.h" +#include "ScArticulationCore.h" +#include "ScArticulationJointCore.h" + +using namespace physx; +using namespace Ps; +using namespace Cm; +using namespace Sc; + +/////////////////////////////////////////////////////////////////////////////// + +template class PxMetaDataArray : public physx::shdfnd::Array +{ +public: + static PX_FORCE_INLINE physx::PxU32 getDataOffset() { return PX_OFFSET_OF(PxMetaDataArray, mData); } + static PX_FORCE_INLINE physx::PxU32 getDataSize() { return PX_SIZE_OF(PxMetaDataArray, mData); } + static PX_FORCE_INLINE physx::PxU32 getSizeOffset() { return PX_OFFSET_OF(PxMetaDataArray, mSize); } + static PX_FORCE_INLINE physx::PxU32 getSizeSize() { return PX_SIZE_OF(PxMetaDataArray, mSize); } + static PX_FORCE_INLINE physx::PxU32 getCapacityOffset() { return PX_OFFSET_OF(PxMetaDataArray, mCapacity); } + static PX_FORCE_INLINE physx::PxU32 getCapacitySize() { return PX_SIZE_OF(PxMetaDataArray, mCapacity); } +}; + +void Sc::ActorCore::getBinaryMetaData(PxOutputStream& stream) +{ + // 16 bytes + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxActorFlags, PxU8) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxDominanceGroup, PxU8) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxClientID, PxU8) + + PX_DEF_BIN_METADATA_CLASS(stream, Sc::ActorCore) + + PX_DEF_BIN_METADATA_ITEM(stream, Sc::ActorCore, ActorSim, mSim, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, Sc::ActorCore, PxU32, mAggregateIDOwnerClient, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sc::ActorCore, PxActorFlags, mActorFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sc::ActorCore, PxU8, mActorType, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sc::ActorCore, PxU8, mDominanceGroup, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PxsRigidCore(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxsRigidCore) + + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxTransform, body2World, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxRigidBodyFlags, mFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxU8, mIdtBody2Actor, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxU16, solverIterationCounts, 0) +} + +namespace +{ + class ShadowPxsBodyCore : public PxsBodyCore + { + public: + static void getBinaryMetaData(PxOutputStream& stream) + { + PX_DEF_BIN_METADATA_CLASS(stream, ShadowPxsBodyCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, ShadowPxsBodyCore, PxsRigidCore) + + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxTransform, body2Actor, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, ccdAdvanceCoefficient, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxVec3, linearVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, maxPenBias, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxVec3, angularVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, contactReportThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, maxAngularVelocitySq, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, maxLinearVelocitySq, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, linearDamping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, angularDamping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxVec3, inverseInertia, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, inverseMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, maxContactImpulse, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, sleepThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, freezeThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, wakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxReal, solverWakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxU32, numCountedInteractions, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxU32, numBodyInteractions, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxU16, isFastMoving, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShadowPxsBodyCore, PxU16, lockFlags, 0) + } + }; +} + +static void getBinaryMetaData_PxsBodyCore(PxOutputStream& stream) +{ + getBinaryMetaData_PxsRigidCore(stream); + +/* PX_DEF_BIN_METADATA_CLASS(stream, PxsBodyCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, PxsBodyCore, PxsRigidCore) + + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxTransform, body2Actor, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, ccdAdvanceCoefficient, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxVec3, linearVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, maxPenBias, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxVec3, angularVelocity, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, contactReportThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, maxAngularVelocitySq, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, maxLinearVelocitySq, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, linearDamping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, angularDamping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxVec3, inverseInertia, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, inverseMass, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, maxContactImpulse, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, sleepThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, freezeThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, wakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxReal, solverWakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsBodyCore, PxU32, numCountedInteractions, 0)*/ + + ShadowPxsBodyCore::getBinaryMetaData(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxsBodyCore, ShadowPxsBodyCore) +} + +/* +We need to fix the header deps by moving the API out of PhysXCore and into its own dir where other code can get to it. +[25.08.2010 18:34:57] Dilip Sequeira: In the meantime, I think it's Ok to include PxSDK.h, but you're right, we need to be very careful about include deps in that direction. +[25.08.2010 18:38:15] Dilip Sequeira: On the memory thing... PxsBodyCore has 28 bytes of padding at the end, for no reason. In addition, it has two words of padding after the velocity fields, to facilitate SIMD loads. But in fact, Vec3FromVec4 is fast enough such that unless you were using it in an inner loop (which we never are with PxsBodyCore) that padding isn't worth it. +[25.08.2010 18:38:58] Dilip Sequeira: So, we should drop the end-padding, and move the damping values to replace the velocity padding. This probably requires a bit of fixup in the places where we do SIMD writes to the velocity. +[25.08.2010 18:39:18] Dilip Sequeira: Then we're down to 92 bytes of data, and 4 bytes of padding I think. +[25.08.2010 18:50:41] Dilip Sequeira: The reason we don't want to put the sleep data there explicitly is that it isn't LL data so I'd rather not have it in an LL interface struct. +[25.08.2010 19:04:53] Gordon Yeoman nvidia: simd loads are faster when they are 16-byte aligned. I think the padding might be to ensure the second vector is also 16-byte aligned. We could drop the second 4-byte pad but dropping the 1st 4-byte pad will likely have performance implications. +[25.08.2010 19:06:22] Dilip Sequeira: We should still align the vec3s, as now - but we shouldn't use padding to do it, since there are a boatload of scalar data fields floating around in that struct too. +*/ +void Sc::BodyCore::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PxsBodyCore(stream); + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxRigidBodyFlags, PxU8) + +// 176 => 144 bytes + PX_DEF_BIN_METADATA_CLASS(stream, Sc::BodyCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Sc::BodyCore, Sc::RigidCore) + + PX_DEF_BIN_METADATA_ITEM(stream, Sc::BodyCore, PxsBodyCore, mCore, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Sc::BodyCore, SimStateData, mSimStateData, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Sc::ConstraintCore::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxConstraintFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, ConstraintCore) + + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxConstraintFlags, mFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxU16, mPaddingFromFlags, PxMetaDataFlag::ePADDING) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxVec3, mAppliedForce, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxVec3, mAppliedTorque, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxConstraintConnector, mConnector, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxConstraintProject, mProject, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxConstraintSolverPrep, mSolverPrep, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxConstraintVisualize, mVisualize, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxU32, mDataSize, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxReal, mLinearBreakForce, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxReal, mAngularBreakForce, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, PxReal, mMinResponseThreshold, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, ConstraintCore, ConstraintSim, mSim, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Sc::MaterialCore::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxCombineMode::Enum, PxU32) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxMaterialFlags, PxU16) + + PX_DEF_BIN_METADATA_CLASS(stream, MaterialCore) + + // MaterialData + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxReal, dynamicFriction, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxReal, staticFriction, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxReal, restitution, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxMaterialFlags, flags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxU8, fricRestCombineMode, 0) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxU8, padding, PxMetaDataFlag::ePADDING) + + // MaterialCore + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxMaterial, mNxMaterial, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, MaterialCore, PxU32, mMaterialIndex, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +void Sc::RigidCore::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Sc::RigidCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Sc::RigidCore, Sc::ActorCore) +} + + +/////////////////////////////////////////////////////////////////////////////// + +void Sc::StaticCore::getBinaryMetaData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Sc::StaticCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Sc::StaticCore, Sc::RigidCore) + + PX_DEF_BIN_METADATA_ITEM(stream, Sc::StaticCore, PxsRigidCore, mCore, 0) + +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_PxFilterData(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxFilterData) + + PX_DEF_BIN_METADATA_ITEM(stream, PxFilterData, PxU32, word0, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxFilterData, PxU32, word1, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxFilterData, PxU32, word2, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxFilterData, PxU32, word3, 0) +} + +static void getBinaryMetaData_PxsShapeCore(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, PxsShapeCore) + + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, PxTransform, transform, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, Gu::GeometryUnion, geometry, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, PxReal, contactOffset, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, PxShapeFlags, mShapeFlags, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, PxU8, mOwnsMaterialIdxMemory, 0) + PX_DEF_BIN_METADATA_ITEM(stream, PxsShapeCore, PxU16, materialIndex, 0) +} + +void Sc::ShapeCore::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_PxFilterData(stream); + getBinaryMetaData_PxsShapeCore(stream); + + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxShapeFlags, PxU8) + +// 144 => 128 bytes + PX_DEF_BIN_METADATA_CLASS(stream, ShapeCore) + + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxFilterData, mQueryFilterData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxFilterData, mSimulationFilterData, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxsShapeCore, mCore, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxReal, mRestOffset, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxReal, mTorsionalRadius, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ShapeCore, PxReal, mMinTorsionalPatchRadius, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_ArticulationCore(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Dy::ArticulationCore) + + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxArticulationFlags, PxU8) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxU32, internalDriveIterations, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxU32, externalDriveIterations, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxU32, maxProjectionIterations, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxU16, solverIterationCounts, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxReal, separationTolerance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxReal, sleepThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxReal, freezeThreshold, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxReal, wakeCounter, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationCore, PxArticulationFlags, flags, 0) +} + +void Sc::ArticulationCore::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_ArticulationCore(stream); + + PX_DEF_BIN_METADATA_CLASS(stream, ArticulationCore) + + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationCore, ArticulationSim, mSim, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationCore, Dy::ArticulationCore, mCore, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationCore, PxU32, mType, 0) +} + +/////////////////////////////////////////////////////////////////////////////// + +static void getBinaryMetaData_ArticulationLimit(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Dy::ArticulationLimit) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationLimit, PxReal, low, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationLimit, PxReal, high, 0) +} + +static void getBinaryMetaData_ArticulationDrive(PxOutputStream& stream) +{ + PX_DEF_BIN_METADATA_CLASS(stream, Dy::ArticulationDrive) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationDrive, PxReal, stiffness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationDrive, PxReal, damping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationDrive, PxReal, maxForce, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationDrive, bool, isAcceleration, 0) +} + +static void getBinaryMetaData_ArticulationJointCoreBase(PxOutputStream& stream) +{ + getBinaryMetaData_ArticulationLimit(stream); + getBinaryMetaData_ArticulationDrive(stream); + + PX_DEF_BIN_METADATA_CLASS(stream, Dy::ArticulationJointCoreBase) + + PX_DEF_BIN_METADATA_TYPEDEF(stream, ArticulationJointCoreDirtyFlags, PxU8) + PX_DEF_BIN_METADATA_TYPEDEF(stream, PxArticulationMotions, PxU8) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxTransform, parentPose, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxTransform, childPose, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, Dy::ArticulationLimit, limits, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, Dy::ArticulationDrive, drives, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, PxReal, targetP, 0) + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, PxReal, targetV, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxQuat, relativeQuat, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxReal, frictionCoefficient, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxU32, jointOffset, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, PxU8, dofIds, 0) + + PX_DEF_BIN_METADATA_ITEMS_AUTO(stream, Dy::ArticulationJointCoreBase, PxArticulationMotions, motion, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, PxReal, maxJointVelocity, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, ArticulationJointCoreDirtyFlags, dirtyFlag, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCoreBase, bool, prismaticLimited, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxU8, jointType, 0) +} + + +static void getBinaryMetaData_ArticulationJointCore(PxOutputStream& stream) +{ + getBinaryMetaData_ArticulationJointCoreBase(stream); + PX_DEF_BIN_METADATA_CLASS(stream, Dy::ArticulationJointCore) + PX_DEF_BIN_METADATA_BASE_CLASS(stream, Dy::ArticulationJointCore, Dy::ArticulationJointCoreBase) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxQuat, targetPosition, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxVec3, targetVelocity, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, spring, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, damping, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, internalCompliance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, externalCompliance, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, swingLimitContactDistance, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tangentialStiffness, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tangentialDamping, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, bool, swingLimited, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, bool, twistLimited, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxU8, driveType, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, twistLimitContactDistance, 0) + + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQSwingY, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQSwingZ, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQSwingPad, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQTwistHigh, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQTwistLow, 0) + PX_DEF_BIN_METADATA_ITEM(stream, Dy::ArticulationJointCore, PxReal, tanQTwistPad, 0) +} + +void Sc::ArticulationJointCore::getBinaryMetaData(PxOutputStream& stream) +{ + getBinaryMetaData_ArticulationJointCore(stream); + PX_DEF_BIN_METADATA_CLASS(stream, ArticulationJointCore) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationJointCore, ArticulationJointSim, mSim, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationJointCore, Dy::ArticulationJointCore, mCore, 0) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationJointCore, Dy::ArticulationCore, mArticulation, PxMetaDataFlag::ePTR) + PX_DEF_BIN_METADATA_ITEM(stream, ArticulationJointCore, Dy::PxArticulationJointBase, mRootType, PxMetaDataFlag::ePTR) +} + +/////////////////////////////////////////////////////////////////////////////// + +#define PX_DEF_BIN_METADATA_ARRAY(stream, Class, type, array) \ +{ PxMetaDataEntry tmp = {"void", #array".mData", PxU32(PX_OFFSET_OF(Class, array)) + PxMetaDataArray::getDataOffset(), PxMetaDataArray::getDataSize(), 1, 0, PxMetaDataFlag::ePTR, 0}; PX_STORE_METADATA(stream, tmp); } \ +{ PxMetaDataEntry tmp = {"PxU32", #array".mSize", PxU32(PX_OFFSET_OF(Class, array)) + PxMetaDataArray::getSizeOffset(), PxMetaDataArray::getSizeSize(), 1, 0, 0, 0}; PX_STORE_METADATA(stream, tmp); } \ +{ PxMetaDataEntry tmp = {"PxU32", #array".mCapacity", PxU32(PX_OFFSET_OF(Class, array)) + PxMetaDataArray::getCapacityOffset(), PxMetaDataArray::getCapacitySize(), 1, 0, PxMetaDataFlag::eCOUNT_MASK_MSB, 0}; PX_STORE_METADATA(stream, tmp); } \ +{ PxMetaDataEntry tmp = {#type, 0, PxU32(PX_OFFSET_OF(Class, array)) + PxMetaDataArray::getSizeOffset(), PxMetaDataArray::getSizeSize(), 0, 0, PxMetaDataFlag::eEXTRA_DATA, 0}; PX_STORE_METADATA(stream, tmp); } + + + + + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp new file mode 100644 index 000000000..a6f8b3165 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp @@ -0,0 +1,2054 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScNPhaseCore.h" +#include "ScShapeInteraction.h" +#include "ScTriggerInteraction.h" +#include "ScElementInteractionMarker.h" +#include "ScConstraintInteraction.h" +#include "ScConstraintSim.h" +#include "ScConstraintCore.h" +#include "ScSimStats.h" +#include "ScObjectIDTracker.h" +#include "ScSimStats.h" + +#include "PsThread.h" +#include "BpBroadPhase.h" +#include "common/PxProfileZone.h" + +using namespace physx; +using namespace Sc; +using namespace Gu; + + +class Sc::FilterPairManager : public Ps::UserAllocated +{ + PX_NOCOPY(FilterPairManager) +public: + FilterPairManager() + : mPairs(PX_DEBUG_EXP("FilterPairManager Array")) + , mFree(INVALID_FILTER_PAIR_INDEX) + {} + + PxU32 acquireIndex() + { + PxU32 index; + if(mFree == INVALID_FILTER_PAIR_INDEX) + { + index = mPairs.size(); + mPairs.pushBack(NULL); + } + else + { + index = PxU32(mFree); + mFree = reinterpret_cast(mPairs[index]); + mPairs[index] = NULL; + } + return index; + } + + void releaseIndex(PxU32 index) + { + mPairs[index] = reinterpret_cast(mFree); + mFree = index; + } + + void setPair(PxU32 index, Sc::ElementSimInteraction* ptr) + { + mPairs[index] = ptr; + } + + Sc::ElementSimInteraction* operator[](PxU32 index) + { + return mPairs[index]; + } + + PxU32 findIndex(Sc::ElementSimInteraction* ptr) + { + return ptr->getFilterPairIndex(); + } + +private: + Ps::Array mPairs; + uintptr_t mFree; +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static PX_FORCE_INLINE PxU32 hasTriggerFlags(PxShapeFlags flags) { return PxU32(flags) & PxU32(PxShapeFlag::eTRIGGER_SHAPE); } +static void getFilterInfo_ShapeSim(PxFilterObjectAttributes& filterAttr, PxFilterData& filterData, const Sc::ShapeSim& shape) +{ + filterAttr = hasTriggerFlags(shape.getCore().getFlags()) ? PxFilterObjectFlag::eTRIGGER : PxFilterObjectFlag::Enum(0); + + BodySim* b = shape.getBodySim(); + if(b) + { + if(!b->isArticulationLink()) + { + if(b->isKinematic()) + filterAttr |= PxFilterObjectFlag::eKINEMATIC; + + setFilterObjectAttributeType(filterAttr, PxFilterObjectType::eRIGID_DYNAMIC); + } + else + setFilterObjectAttributeType(filterAttr, PxFilterObjectType::eARTICULATION); + } + else + { + setFilterObjectAttributeType(filterAttr, PxFilterObjectType::eRIGID_STATIC); + } + + filterData = shape.getCore().getSimulationFilterData(); +} + +static PX_FORCE_INLINE void getFilterInfo(PxFilterData& fd, PxFilterObjectAttributes& fa, const ElementSim& e) +{ + getFilterInfo_ShapeSim(fa, fd, static_cast(e)); +} + +static void getFilterInfo(PxFilterData& fd0, PxFilterData& fd1, PxFilterObjectAttributes& fa0, PxFilterObjectAttributes& fa1, const ElementSim& e0, const ElementSim& e1) +{ + getFilterInfo(fd0, fa0, e0); + getFilterInfo(fd1, fa1, e1); +} + +static PX_INLINE void callPairLost(Sc::Scene& scene, const ElementSim& e0, const ElementSim& e1, PxU32 pairID, bool objVolumeRemoved) +{ + PxFilterData fd0(PxEmpty), fd1(PxEmpty); + PxFilterObjectAttributes fa0, fa1; + getFilterInfo(fd0, fd1, fa0, fa1, e0, e1); + + scene.getFilterCallbackFast()->pairLost(pairID, fa0, fd0, fa1, fd1, objVolumeRemoved); +} + +// Filtering + +static PX_INLINE void checkFilterFlags(PxFilterFlags& filterFlags) +{ + if((filterFlags & (PxFilterFlag::eKILL | PxFilterFlag::eSUPPRESS)) == (PxFilterFlag::eKILL | PxFilterFlag::eSUPPRESS)) + { +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: eKILL and eSUPPRESS must not be set simultaneously. eSUPPRESS will be used."); +#endif + filterFlags.clear(PxFilterFlag::eKILL); + } +} + +static PX_FORCE_INLINE PxPairFlags checkRbPairFlags(const ShapeSim& s0, const ShapeSim& s1, PxPairFlags pairFlags) +{ +#if PX_CHECKED + // we want to avoid to run contact generation for pairs that should not get resolved or have no contact/trigger reports + if (!(PxU32(pairFlags) & (PxPairFlag::eSOLVE_CONTACT | ShapeInteraction::CONTACT_REPORT_EVENTS))) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: Pair with no contact/trigger reports detected, nor is PxPairFlag::eSOLVE_CONTACT set. It is recommended to suppress/kill such pairs for performance reasons."); + } + else if(!(pairFlags & (PxPairFlag::eDETECT_DISCRETE_CONTACT | PxPairFlag::eDETECT_CCD_CONTACT))) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: Pair did not request either eDETECT_DISCRETE_CONTACT or eDETECT_CCD_CONTACT. It is recommended to suppress/kill such pairs for performance reasons."); + } + + if(((s0.getFlags() & PxShapeFlag::eTRIGGER_SHAPE)!=0 || (s1.getFlags() & PxShapeFlag::eTRIGGER_SHAPE)!=0) && + (pairFlags & PxPairFlag::eTRIGGER_DEFAULT) && (pairFlags & PxPairFlag::eDETECT_CCD_CONTACT)) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: CCD isn't supported on Triggers yet"); + } +#else + PX_UNUSED(s0); + PX_UNUSED(s1); +#endif + return pairFlags; +} + +static PX_INLINE PxPairFlags checkRbPairFlags( const ShapeSim& s0, const ShapeSim& s1, + const Sc::BodySim* bs0, const Sc::BodySim* bs1, + PxPairFlags pairFlags, PxFilterFlags filterFlags) +{ + if(filterFlags & (PxFilterFlag::eSUPPRESS | PxFilterFlag::eKILL)) + return pairFlags; + + if (bs0 && bs0->isKinematic() && + bs1 && bs1->isKinematic() && + (pairFlags & PxPairFlag::eSOLVE_CONTACT)) + { +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: Resolving contacts between two kinematic objects is invalid. Contacts will not get resolved."); +#endif + pairFlags.clear(PxPairFlag::eSOLVE_CONTACT); + } + + return checkRbPairFlags(s0, s1, pairFlags); +} + +// PT: version specialized for ShapeSim/ShapeSim +static PX_INLINE PxPairFlags checkRbPairFlags( const ShapeSim& s0, const ShapeSim& s1, + bool kine0, bool kine1, + PxPairFlags pairFlags, PxFilterFlags filterFlags) +{ + if(filterFlags & (PxFilterFlag::eSUPPRESS | PxFilterFlag::eKILL)) + return pairFlags; + + if(kine0 && kine1 && (pairFlags & PxPairFlag::eSOLVE_CONTACT)) + { +#if PX_CHECKED + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: Resolving contacts between two kinematic objects is invalid. Contacts will not get resolved."); +#endif + pairFlags.clear(PxPairFlag::eSOLVE_CONTACT); + } + + return checkRbPairFlags(s0, s1, pairFlags); +} + +static PX_FORCE_INLINE void fetchActorAndShape(const ElementSim& e, PxActor*& a, PxShape*& s) +{ + const ShapeSim& sim = static_cast(e); + a = sim.getRbSim().getPxActor(); + s = sim.getPxShape(); +} + +static void runFilter(PxFilterInfo& filterInfo, const FilteringContext& context, const ElementSim& e0, const ElementSim& e1, PxU32 filterPairIndex, bool doCallbacks) +{ + PxFilterData fd0(PxEmpty), fd1(PxEmpty); + PxFilterObjectAttributes fa0, fa1; + getFilterInfo(fd0, fd1, fa0, fa1, e0, e1); + + // Run filter shader + filterInfo.filterFlags = context.mFilterShader(fa0, fd0, fa1, fd1, filterInfo.pairFlags, context.mFilterShaderData, context.mFilterShaderDataSize); + + if(filterInfo.filterFlags & PxFilterFlag::eCALLBACK) + { + if(context.mFilterCallback) + { + if(!doCallbacks) + { + return; + } + else + { + if(filterPairIndex == INVALID_FILTER_PAIR_INDEX) + filterPairIndex = context.mFilterPairManager->acquireIndex(); + // If a FilterPair is provided, then we use it, else we create a new one + // (A FilterPair is provided in the case for a pairLost()-pairFound() sequence after refiltering) + + PxActor* a0, *a1; + PxShape* s0, *s1; + fetchActorAndShape(e0, a0, s0); + fetchActorAndShape(e1, a1, s1); + + filterInfo.filterFlags = context.mFilterCallback->pairFound(filterPairIndex, fa0, fd0, a0, s0, fa1, fd1, a1, s1, filterInfo.pairFlags); + filterInfo.filterPairIndex = filterPairIndex; + } + } + else + { + filterInfo.filterFlags.clear(PxFilterFlag::eNOTIFY); + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: eCALLBACK set but no filter callback defined."); + } + } + + checkFilterFlags(filterInfo.filterFlags); + + if(filterPairIndex!=INVALID_FILTER_PAIR_INDEX && ((filterInfo.filterFlags & PxFilterFlag::eKILL) || ((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) != PxFilterFlag::eNOTIFY))) + { + if((filterInfo.filterFlags & PxFilterFlag::eKILL) && ((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) == PxFilterFlag::eNOTIFY)) + context.mFilterCallback->pairLost(filterPairIndex, fa0, fd0, fa1, fd1, false); + + if((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) != PxFilterFlag::eNOTIFY) + { + // No notification, hence we don't need to treat it as a filter callback pair anymore. + // Make sure that eCALLBACK gets removed as well + filterInfo.filterFlags.clear(PxFilterFlag::eNOTIFY); + } + + context.mFilterPairManager->releaseIndex(filterPairIndex); + filterInfo.filterPairIndex = INVALID_FILTER_PAIR_INDEX; + } + + // Sanity checks + PX_ASSERT( (filterInfo.filterFlags != PxFilterFlag::eKILL) || + ((filterInfo.filterFlags == PxFilterFlag::eKILL) && (filterInfo.filterPairIndex == INVALID_FILTER_PAIR_INDEX)) ); + PX_ASSERT( ((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) != PxFilterFlag::eNOTIFY) || + (((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) == PxFilterFlag::eNOTIFY) && filterInfo.filterPairIndex!=INVALID_FILTER_PAIR_INDEX) ); +} + +// PT: version specialized for ShapeSim/ShapeSim +static PX_FORCE_INLINE void runFilterShapeSim(PxFilterInfo& filterInfo, const FilteringContext& context, const ShapeSim& e0, const ShapeSim& e1, const PxFilterObjectAttributes fa0, const PxFilterObjectAttributes fa1) +{ + // Run filter shader + { + const PxFilterData fd0 = e0.getCore().getSimulationFilterData(); + const PxFilterData fd1 = e1.getCore().getSimulationFilterData(); + filterInfo.filterFlags = context.mFilterShader(fa0, fd0, fa1, fd1, filterInfo.pairFlags, context.mFilterShaderData, context.mFilterShaderDataSize); + } + + if(filterInfo.filterFlags & PxFilterFlag::eCALLBACK) + { + if(context.mFilterCallback) + { + return; + } + else + { + filterInfo.filterFlags.clear(PxFilterFlag::eNOTIFY); + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Filtering: eCALLBACK set but no filter callback defined."); + } + } + + checkFilterFlags(filterInfo.filterFlags); + + // Sanity checks + PX_ASSERT( (filterInfo.filterFlags != PxFilterFlag::eKILL) || + ((filterInfo.filterFlags == PxFilterFlag::eKILL) && (filterInfo.filterPairIndex == INVALID_FILTER_PAIR_INDEX)) ); + PX_ASSERT( ((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) != PxFilterFlag::eNOTIFY) || + (((filterInfo.filterFlags & PxFilterFlag::eNOTIFY) == PxFilterFlag::eNOTIFY) && filterInfo.filterPairIndex!=INVALID_FILTER_PAIR_INDEX) ); +} + +// helper method for some cleanup code that is used multiple times for early outs in case a rigid body collision pair gets filtered out due to some hardwired filter criteria +static PX_FORCE_INLINE PxFilterInfo filterOutRbCollisionPair(FilterPairManager* filterPairManager, PxU32 filterPairIndex, const PxFilterFlags filterFlags) +{ + if(filterPairIndex!=INVALID_FILTER_PAIR_INDEX) + filterPairManager->releaseIndex(filterPairIndex); + + return PxFilterInfo(filterFlags); +} + +PxFilterInfo Sc::filterRbCollisionPairSecondStage(const FilteringContext& context, const ShapeSim& s0, const ShapeSim& s1, const Sc::BodySim* b0, const Sc::BodySim* b1, PxU32 filterPairIndex, bool runCallbacks) +{ + PxFilterInfo filterInfo; + runFilter(filterInfo, context, s0, s1, filterPairIndex, runCallbacks); + + if(runCallbacks || (!(filterInfo.filterFlags & PxFilterFlag::eCALLBACK))) + filterInfo.pairFlags = checkRbPairFlags(s0, s1, b0, b1, filterInfo.pairFlags, filterInfo.filterFlags); + + return filterInfo; +} + +// PT: version specialized for ShapeSim/ShapeSim +static PX_FORCE_INLINE PxFilterInfo filterRbCollisionPairSecondStage(const FilteringContext& context, const ShapeSim& s0, const ShapeSim& s1, bool kine0, bool kine1, const PxFilterObjectAttributes fa0, const PxFilterObjectAttributes fa1) +{ + PxFilterInfo filterInfo; + runFilterShapeSim(filterInfo, context, s0, s1, fa0, fa1); + + if(!(filterInfo.filterFlags & PxFilterFlag::eCALLBACK)) + filterInfo.pairFlags = checkRbPairFlags(s0, s1, kine0, kine1, filterInfo.pairFlags, filterInfo.filterFlags); + + return filterInfo; +} + +static bool filterArticulationLinks(const ActorSim& rbActor0, const ActorSim& rbActor1) +{ + // If the bodies are articulation links of the same articulation and one link is the parent of the other link, then disable collision + PxU32 size = rbActor0.getActorInteractionCount(); + Interaction** interactions = rbActor0.getActorInteractions(); + while(size--) + { + const Interaction* interaction = *interactions++; + if(interaction->getType() == InteractionType::eARTICULATION) + { + if((&interaction->getActorSim0() == &rbActor1) || (&interaction->getActorSim1() == &rbActor1)) + return true; + } + } + return false; +} + +static bool filterJointedBodies(const Sc::BodySim* body, const ActorSim& other) +{ + if(!body->readInternalFlag(Sc::BodySim::BF_HAS_CONSTRAINTS)) + return false; + + const Sc::ActorSim* actorToMatch; + PxU32 size; + Interaction** interactions; + + if(body->getActorInteractionCount() <= other.getActorInteractionCount()) + { + size = body->getActorInteractionCount(); + interactions = body->getActorInteractions(); + actorToMatch = &other; + } + else + { + size = other.getActorInteractionCount(); + interactions = other.getActorInteractions(); + actorToMatch = body; + } + + while(size--) + { + Interaction* interaction = *interactions++; + if(interaction->getType() == InteractionType::eCONSTRAINTSHADER) + { + ConstraintInteraction* csi = static_cast(interaction); + if((&csi->getActorSim0() == actorToMatch) || (&csi->getActorSim1() == actorToMatch)) + return !(csi->getConstraint()->getCore().getFlags() & PxConstraintFlag::eCOLLISION_ENABLED); + } + } + return false; +} + +static PX_FORCE_INLINE bool filterJointedBodies(const Sc::BodySim* b0, const Sc::BodySim* b1, const ActorSim& rbActor0, const ActorSim& rbActor1) +{ + // If the bodies of the shape pair are connected by a joint, we need to check whether this connection disables the collision. + // Note: As an optimization, the dynamic bodies have a flag which specifies whether they have any constraints at all. That works + // because a constraint has at least one dynamic body and an interaction is tracked by both objects. + + if(b0) + return filterJointedBodies(b0, rbActor1); + + if(b1) + return filterJointedBodies(b1, rbActor0); + + return false; +} + +static PX_FORCE_INLINE bool filterKinematics(const Sc::BodySim* b0, const Sc::BodySim* b1, bool kine0, bool kine1, const PxSceneFlags& sceneFlags) +{ + // if at least one object is kinematic + const bool kinematicPair = kine0 | kine1; + if(kinematicPair) + { + // ...then ignore kinematic vs. static pairs + if(!(sceneFlags & PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS)) + { + if(!b0 || !b1) + return true; + } + + // ...and ignore kinematic vs. kinematic pairs + if(!(sceneFlags & PxSceneFlag::eENABLE_KINEMATIC_PAIRS)) + { + if(kine0 && kine1) + return true; + } + } + return false; +} + +static PxFilterInfo filterRbCollisionPair(const FilteringContext& context, const ShapeSim& s0, const ShapeSim& s1, PxU32 filterPairIndex, bool& isTriggerPair, bool runCallbacks) +{ + const Sc::BodySim* b0 = s0.getBodySim(); + const Sc::BodySim* b1 = s1.getBodySim(); + + const PxU32 trigger0 = s0.getFlags() & PxShapeFlag::eTRIGGER_SHAPE; + const PxU32 trigger1 = s1.getFlags() & PxShapeFlag::eTRIGGER_SHAPE; + isTriggerPair = (trigger0 | trigger1)!=0; + if(isTriggerPair) + { + if(trigger0 && trigger1) // // trigger-trigger pairs are not supported + return filterOutRbCollisionPair(context.mFilterPairManager, filterPairIndex, PxFilterFlag::eKILL); + } + else + { + const bool kine0 = b0 ? b0->isKinematic() : false; + const bool kine1 = b1 ? b1->isKinematic() : false; + + if(filterKinematics(b0, b1, kine0, kine1, context.mSceneFlags)) + return filterOutRbCollisionPair(context.mFilterPairManager, filterPairIndex, PxFilterFlag::eSUPPRESS); + + const ActorSim& rbActor0 = s0.getActor(); + const ActorSim& rbActor1 = s1.getActor(); + + if(filterJointedBodies(b0, b1, rbActor0, rbActor1)) + return filterOutRbCollisionPair(context.mFilterPairManager, filterPairIndex, PxFilterFlag::eSUPPRESS); + + if((rbActor0.getActorType() == PxActorType::eARTICULATION_LINK) && (rbActor1.getActorType() == PxActorType::eARTICULATION_LINK)) + { + if(filterArticulationLinks(rbActor0, rbActor1)) + return filterOutRbCollisionPair(context.mFilterPairManager, filterPairIndex, PxFilterFlag::eKILL); + } + } + return filterRbCollisionPairSecondStage(context, s0, s1, b0, b1, filterPairIndex, runCallbacks); +} + +// PT: indexed by PxActorType +static const PxU32 gTypeData[] = { + PxFilterObjectType::eRIGID_STATIC<<1, + (PxFilterObjectType::eRIGID_DYNAMIC<<1)|1, + (PxFilterObjectType::eARTICULATION<<1)|1 +}; + +// PT: version specialized for ShapeSim/ShapeSim (no triggers) +static PX_FORCE_INLINE PxFilterInfo filterRbCollisionPair(const FilteringContext& context, const ShapeSim& s0, const ShapeSim& s1) +{ + const ActorSim& rbActor0 = s0.getActor(); + const PxActorType::Enum actorType0 = rbActor0.getActorType(); + const PxU32 typeData0 = gTypeData[actorType0]; + PxFilterObjectAttributes filterAttr0 = typeData0>>1; + const Sc::BodySim* b0; + bool kine0; + if(typeData0 & 1) + { + b0 = static_cast(&rbActor0); + kine0 = b0->isKinematic(); + filterAttr0 |= kine0 ? PxFilterObjectFlag::eKINEMATIC : 0; + } + else + { + b0 = NULL; + kine0 = false; + } + + const ActorSim& rbActor1 = s1.getActor(); + const PxActorType::Enum actorType1 = rbActor1.getActorType(); + const PxU32 typeData1 = gTypeData[actorType1]; + PxFilterObjectAttributes filterAttr1 = typeData1>>1; + const Sc::BodySim* b1; + bool kine1; + if(typeData1 & 1) + { + b1 = static_cast(&rbActor1); + kine1 = b1->isKinematic(); + filterAttr1 |= kine1 ? PxFilterObjectFlag::eKINEMATIC : 0; + } + else + { + b1 = NULL; + kine1 = false; + } + + PX_ASSERT(!(s0.getFlags() & PxShapeFlag::eTRIGGER_SHAPE)); + PX_ASSERT(!(s1.getFlags() & PxShapeFlag::eTRIGGER_SHAPE)); + + if(filterKinematics(b0, b1, kine0, kine1, context.mSceneFlags)) + return PxFilterInfo(PxFilterFlag::eSUPPRESS); + + if(filterJointedBodies(b0, b1, rbActor0, rbActor1)) + return PxFilterInfo(PxFilterFlag::eSUPPRESS); + + if((actorType0 == PxActorType::eARTICULATION_LINK) && (actorType1 == PxActorType::eARTICULATION_LINK)) + { + if(filterArticulationLinks(rbActor0, rbActor1)) + return PxFilterInfo(PxFilterFlag::eKILL); + } + + return filterRbCollisionPairSecondStage(context, s0, s1, kine0, kine1, filterAttr0, filterAttr1); +} + +void Sc::NPhaseCore::runOverlapFilters( PxU32 nbToProcess, const Bp::AABBOverlap* PX_RESTRICT pairs, PxFilterInfo* PX_RESTRICT filterInfo, + PxU32& nbToKeep_, PxU32& nbToSuppress_, PxU32& nbToCallback_, PxU32* PX_RESTRICT keepMap, PxU32* PX_RESTRICT callbackMap) +{ + PxU32 nbToKeep = 0; + PxU32 nbToSuppress = 0; + PxU32 nbToCallback = 0; + + const FilteringContext context(mOwnerScene, mFilterPairManager); + + for(PxU32 i=0; i(pair.mUserData0); + Sc::ElementSim* e1 = reinterpret_cast(pair.mUserData1); + PX_ASSERT(!findInteraction(e0, e1)); + + ShapeSim* s0 = static_cast(e0); + ShapeSim* s1 = static_cast(e1); + PX_ASSERT(&s0->getActor() != &s1->getActor()); // No actor internal interactions + + filterInfo[i] = filterRbCollisionPair(context, *s0, *s1); + + const PxFilterFlags filterFlags = filterInfo[i].filterFlags; + + if(!(filterFlags & PxFilterFlag::eKILL)) + { + if(filterFlags & PxFilterFlag::eCALLBACK) + { + nbToCallback++; + callbackMap[i / 32] |= (1 << (i & 31)); + } + else + { + if(!(filterFlags & PxFilterFlag::eSUPPRESS)) + nbToKeep++; + else + nbToSuppress++; + keepMap[i / 32] |= (1 << (i & 31)); + } + } + } + + nbToKeep_ = nbToKeep; + nbToSuppress_ = nbToSuppress; + nbToCallback_ = nbToCallback; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Sc::NPhaseCore::NPhaseCore(Scene& scene, const PxSceneDesc& sceneDesc) : + mOwnerScene (scene), + mContactReportActorPairSet (PX_DEBUG_EXP("contactReportPairSet")), + mPersistentContactEventPairList (PX_DEBUG_EXP("persistentContactEventPairs")), + mNextFramePersistentContactEventPairIndex(0), + mForceThresholdContactEventPairList (PX_DEBUG_EXP("forceThresholdContactEventPairs")), + mContactReportBuffer (sceneDesc.contactReportStreamBufferSize, (sceneDesc.flags & PxSceneFlag::eDISABLE_CONTACT_REPORT_BUFFER_RESIZE)), + mActorPairPool (PX_DEBUG_EXP("actorPairPool")), + mActorPairReportPool (PX_DEBUG_EXP("actorPairReportPool")), + mShapeInteractionPool (Ps::AllocatorTraits::Type(PX_DEBUG_EXP("shapeInteractionPool")), 256), + mTriggerInteractionPool (PX_DEBUG_EXP("triggerInteractionPool")), + mActorPairContactReportDataPool (PX_DEBUG_EXP("actorPairContactReportPool")), + mInteractionMarkerPool (PX_DEBUG_EXP("interactionMarkerPool")) + ,mMergeProcessedTriggerInteractions (scene.getContextId(), this, "ScNPhaseCore.mergeProcessedTriggerInteractions") + ,mTmpTriggerProcessingBlock (NULL) + ,mTriggerPairsToDeactivateCount (0) +{ + mFilterPairManager = PX_NEW(FilterPairManager); +} + +Sc::NPhaseCore::~NPhaseCore() +{ + // Clear pending actor pairs (waiting on contact report callback) + clearContactReportActorPairs(false); + PX_DELETE(mFilterPairManager); +} + +PxU32 Sc::NPhaseCore::getDefaultContactReportStreamBufferSize() const +{ + return mContactReportBuffer.getDefaultBufferSize(); +} + +Sc::ElementSimInteraction* Sc::NPhaseCore::findInteraction(ElementSim* _element0, ElementSim* _element1) +{ + const Ps::HashMap::Entry* pair = mElementSimMap.find(ElementSimKey(_element0, _element1)); + return pair ? pair->second : NULL; +} + +void Sc::NPhaseCore::onOverlapCreated(const Bp::AABBOverlap* PX_RESTRICT pairs, PxU32 pairCount) +{ + for(PxU32 i=0; i(pairs[i].mUserData0); + ElementSim* volume1 = reinterpret_cast(pairs[i].mUserData1); + PX_ASSERT(!findInteraction(volume0, volume1)); + + ShapeSim* shapeHi = static_cast(volume1); + ShapeSim* shapeLo = static_cast(volume0); + + // No actor internal interactions + PX_ASSERT(&shapeHi->getActor() != &shapeLo->getActor()); + + // PT: this case is only for triggers these days + PX_ASSERT((shapeLo->getFlags() & PxShapeFlag::eTRIGGER_SHAPE) || (shapeHi->getFlags() & PxShapeFlag::eTRIGGER_SHAPE)); + + /*Sc::ElementSimInteraction* interaction =*/ createRbElementInteraction(*shapeHi, *shapeLo, NULL, NULL, NULL); + } +} + +void Sc::NPhaseCore::registerInteraction(ElementSimInteraction* interaction) +{ + mElementSimMap.insert(ElementSimKey(&interaction->getElement0(), &interaction->getElement1()), interaction); +} + +void Sc::NPhaseCore::unregisterInteraction(ElementSimInteraction* interaction) +{ + mElementSimMap.erase(ElementSimKey(&interaction->getElement0(), &interaction->getElement1())); +} + +ElementSimInteraction* Sc::NPhaseCore::onOverlapRemovedStage1(ElementSim* volume0, ElementSim* volume1) +{ + return findInteraction(volume0, volume1); +} + +void Sc::NPhaseCore::onOverlapRemoved(ElementSim* volume0, ElementSim* volume1, const PxU32 ccdPass, void* elemSim, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + ElementSim* elementHi = volume1; + ElementSim* elementLo = volume0; + // No actor internal interactions + PX_ASSERT(&elementHi->getActor() != &elementLo->getActor()); + + // PT: TODO: get rid of 'findInteraction', cf US10491 + ElementSimInteraction* interaction = elemSim ? reinterpret_cast(elemSim) : findInteraction(elementHi, elementLo); + // MS: The check below is necessary since at the moment LowLevel broadphase still tracks + // killed pairs and hence reports lost overlaps + if(interaction) + { + PxU32 flags = PxU32(PairReleaseFlag::eWAKE_ON_LOST_TOUCH); + PX_ASSERT(interaction->isElementInteraction()); + releaseElementPair(static_cast(interaction), flags, ccdPass, true, outputs, useAdaptiveForce); + } +} + +// MS: TODO: optimize this for the actor release case? +void Sc::NPhaseCore::onVolumeRemoved(ElementSim* volume, PxU32 flags, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + const PxU32 ccdPass = 0; + + flags |= PairReleaseFlag::eSHAPE_BP_VOLUME_REMOVED; + + // Release interactions + // IMPORTANT: Iterate from the back of the list to the front as we release interactions which + // triggers a replace with last + ElementSim::ElementInteractionReverseIterator iter = volume->getElemInteractionsReverse(); + ElementSimInteraction* interaction = iter.getNext(); + while(interaction) + { + PX_ASSERT( (interaction->getType() == InteractionType::eMARKER) || + (interaction->getType() == InteractionType::eOVERLAP) || + (interaction->getType() == InteractionType::eTRIGGER) ); + + releaseElementPair(interaction, flags, ccdPass, true, outputs, useAdaptiveForce); + + interaction = iter.getNext(); + } +} + +Sc::ElementSimInteraction* Sc::NPhaseCore::createRbElementInteraction(const PxFilterInfo& finfo, ShapeSim& s0, ShapeSim& s1, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction, + Sc::ElementInteractionMarker* interactionMarker, bool isTriggerPair) +{ + ElementSimInteraction* pair = NULL; + + if((finfo.filterFlags & PxFilterFlag::eSUPPRESS) == false) + { + if(!isTriggerPair) + pair = createShapeInteraction(s0, s1, finfo.pairFlags, contactManager, shapeInteraction); + else + pair = createTriggerInteraction(s0, s1, finfo.pairFlags); + } + else + pair = createElementInteractionMarker(s0, s1, interactionMarker); + + if(finfo.filterPairIndex != INVALID_FILTER_PAIR_INDEX) + { + // Mark the pair as a filter callback pair + pair->raiseInteractionFlag(InteractionFlag::eIS_FILTER_PAIR); + + // Filter callback pair: Set the link to the interaction + mFilterPairManager->setPair(finfo.filterPairIndex, pair); + pair->setFilterPairIndex(finfo.filterPairIndex); + } + + return pair; +} + +Sc::ElementSimInteraction* Sc::NPhaseCore::createRbElementInteraction(ShapeSim& s0, ShapeSim& s1, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction, + Sc::ElementInteractionMarker* interactionMarker) +{ + const FilteringContext context(mOwnerScene, mFilterPairManager); + + bool isTriggerPair = false; + const PxFilterInfo finfo = filterRbCollisionPair(context, s0, s1, INVALID_FILTER_PAIR_INDEX, isTriggerPair, false); + + if(finfo.filterFlags & PxFilterFlag::eKILL) + { + PX_ASSERT(finfo.filterPairIndex == INVALID_FILTER_PAIR_INDEX); // No filter callback pair info for killed pairs + return NULL; + } + + return createRbElementInteraction(finfo, s0, s1, contactManager, shapeInteraction, interactionMarker, isTriggerPair); +} + +void Sc::NPhaseCore::managerNewTouch(Sc::ShapeInteraction& interaction) +{ + //(1) if the pair hasn't already been assigned, look it up! + + ActorPair* actorPair = interaction.getActorPair(); + + if(!actorPair) + { + ShapeSim& s0 = static_cast(interaction.getElement0()); + ShapeSim& s1 = static_cast(interaction.getElement1()); + actorPair = findActorPair(&s0, &s1, interaction.isReportPair()); + actorPair->incRefCount(); //It's being referenced by a new pair... + interaction.setActorPair(*actorPair); + } +} + +Sc::ShapeInteraction* Sc::NPhaseCore::createShapeInteraction(ShapeSim& s0, ShapeSim& s1, PxPairFlags pairFlags, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction) +{ + ShapeSim* _s0 = &s0; + ShapeSim* _s1 = &s1; + + /* + This tries to ensure that if one of the bodies is static or kinematic, it will be body B + There is a further optimization to force all pairs that share the same bodies to have + the same body ordering. This reduces the number of required partitions in the parallel solver. + Sorting rules are: + If bodyA is static, swap + If bodyA is rigidDynamic and bodyB is articulation, swap + If bodyA is in an earlier BP group than bodyB, swap + */ + { + Sc::RigidSim& rs0 = s0.getRbSim(); + Sc::RigidSim& rs1 = s1.getRbSim(); + + const PxActorType::Enum actorType0 = rs0.getActorType(); + const PxActorType::Enum actorType1 = rs1.getActorType(); + if( actorType0 == PxActorType::eRIGID_STATIC + || (actorType0 == PxActorType::eRIGID_DYNAMIC && actorType1 == PxActorType::eARTICULATION_LINK) + || ((actorType0 == PxActorType::eRIGID_DYNAMIC && actorType1 == PxActorType::eRIGID_DYNAMIC) && s0.getBodySim()->isKinematic()) + || (actorType0 == actorType1 && rs0.getRigidID() < rs1.getRigidID())) + Ps::swap(_s0, _s1); + } + + ShapeInteraction* si = shapeInteraction ? shapeInteraction : mShapeInteractionPool.allocate(); + PX_PLACEMENT_NEW(si, ShapeInteraction)(*_s0, *_s1, pairFlags, contactManager); + + PX_ASSERT(si->mReportPairIndex == INVALID_REPORT_PAIR_ID); + + return si; +} + +Sc::TriggerInteraction* Sc::NPhaseCore::createTriggerInteraction(ShapeSim& s0, ShapeSim& s1, PxPairFlags triggerFlags) +{ + ShapeSim* triggerShape; + ShapeSim* otherShape; + + if(s1.getFlags() & PxShapeFlag::eTRIGGER_SHAPE) + { + triggerShape = &s1; + otherShape = &s0; + } + else + { + triggerShape = &s0; + otherShape = &s1; + } + TriggerInteraction* pair = mTriggerInteractionPool.construct(*triggerShape, *otherShape); + pair->setTriggerFlags(triggerFlags); + return pair; +} + +Sc::ElementInteractionMarker* Sc::NPhaseCore::createElementInteractionMarker(ElementSim& e0, ElementSim& e1, Sc::ElementInteractionMarker* interactionMarker) +{ + ElementInteractionMarker* pair = interactionMarker ? interactionMarker : mInteractionMarkerPool.allocate(); + PX_PLACEMENT_NEW(pair, ElementInteractionMarker)(e0, e1, interactionMarker != NULL); + return pair; +} + +Sc::ElementSimInteraction* Sc::NPhaseCore::refilterInteraction(ElementSimInteraction* pair, const PxFilterInfo* filterInfo, bool removeFromDirtyList, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + const InteractionType::Enum oldType = pair->getType(); + + switch (oldType) + { + case InteractionType::eTRIGGER: + case InteractionType::eMARKER: + case InteractionType::eOVERLAP: + { + ShapeSim& s0 = static_cast(pair->getElement0()); + ShapeSim& s1 = static_cast(pair->getElement1()); + + PxFilterInfo finfo; + if(filterInfo) + { + // The filter changes are provided by an outside source (the user filter callback) + + finfo = *filterInfo; + PX_ASSERT(finfo.filterPairIndex!=INVALID_FILTER_PAIR_INDEX); + + if((finfo.filterFlags & PxFilterFlag::eKILL) && + ((finfo.filterFlags & PxFilterFlag::eNOTIFY) == PxFilterFlag::eNOTIFY) ) + { + callPairLost(mOwnerScene, pair->getElement0(), pair->getElement1(), finfo.filterPairIndex, false); + mFilterPairManager->releaseIndex(finfo.filterPairIndex); + finfo.filterPairIndex = INVALID_FILTER_PAIR_INDEX; + } + + Sc::BodySim* bs0 = s0.getBodySim(); + Sc::BodySim* bs1 = s1.getBodySim(); + finfo.pairFlags = checkRbPairFlags(s0, s1, bs0, bs1, finfo.pairFlags, finfo.filterFlags); + + //!!!Filter Issue: Need to check whether the requested changes don't violate hard constraints. + // - Assert that the shapes are not connected through a collision disabling joint + } + else + { + PxU32 filterPairIndex = INVALID_FILTER_PAIR_INDEX; + if(pair->readInteractionFlag(InteractionFlag::eIS_FILTER_PAIR)) + { + filterPairIndex = mFilterPairManager->findIndex(pair); + PX_ASSERT(filterPairIndex!=INVALID_FILTER_PAIR_INDEX); + + callPairLost(mOwnerScene, pair->getElement0(), pair->getElement1(), filterPairIndex, false); + } + + const FilteringContext context(mOwnerScene, mFilterPairManager); + + bool isTriggerPair; + finfo = filterRbCollisionPair(context, s0, s1, filterPairIndex, isTriggerPair, true); + PX_UNUSED(isTriggerPair); + } + + if(pair->readInteractionFlag(InteractionFlag::eIS_FILTER_PAIR) && + ((finfo.filterFlags & PxFilterFlag::eNOTIFY) != PxFilterFlag::eNOTIFY) ) + { + // The pair was a filter callback pair but not any longer + pair->clearInteractionFlag(InteractionFlag::eIS_FILTER_PAIR); + + if(finfo.filterPairIndex!=INVALID_FILTER_PAIR_INDEX) + { + mFilterPairManager->releaseIndex(finfo.filterPairIndex); + finfo.filterPairIndex = INVALID_FILTER_PAIR_INDEX; + } + } + + struct Local + { + static Sc::InteractionType::Enum getRbElementInteractionType(const ShapeSim* primitive0, const ShapeSim* primitive1, PxFilterFlags filterFlag) + { + if(filterFlag & PxFilterFlag::eKILL) + return InteractionType::eINVALID; + + if(filterFlag & PxFilterFlag::eSUPPRESS) + return InteractionType::eMARKER; + + if(primitive0->getFlags() & PxShapeFlag::eTRIGGER_SHAPE + || primitive1->getFlags() & PxShapeFlag::eTRIGGER_SHAPE) + return InteractionType::eTRIGGER; + + PX_ASSERT( (primitive0->getGeometryType() != PxGeometryType::eTRIANGLEMESH) || + (primitive1->getGeometryType() != PxGeometryType::eTRIANGLEMESH)); + + return InteractionType::eOVERLAP; + } + }; + + const InteractionType::Enum newType = Local::getRbElementInteractionType(&s0, &s1, finfo.filterFlags); + if(pair->getType() != newType) //Only convert interaction type if the type has changed + { + return convert(pair, newType, finfo, removeFromDirtyList, outputs, useAdaptiveForce); + } + else + { + //The pair flags might have changed, we need to forward the new ones + if(oldType == InteractionType::eOVERLAP) + { + ShapeInteraction* si = static_cast(pair); + + const PxU32 newPairFlags = finfo.pairFlags; + const PxU32 oldPairFlags = si->getPairFlags(); + PX_ASSERT((newPairFlags & ShapeInteraction::PAIR_FLAGS_MASK) == newPairFlags); + PX_ASSERT((oldPairFlags & ShapeInteraction::PAIR_FLAGS_MASK) == oldPairFlags); + + if(newPairFlags != oldPairFlags) + { + if(!(oldPairFlags & ShapeInteraction::CONTACT_REPORT_EVENTS) && (newPairFlags & ShapeInteraction::CONTACT_REPORT_EVENTS) && (si->getActorPair() == NULL || !si->getActorPair()->isReportPair())) + { + // for this actor pair there was no shape pair that requested contact reports but now there is one + // -> all the existing shape pairs need to get re-adjusted to point to an ActorPairReport instance instead. + findActorPair(&s0, &s1, Ps::IntTrue); + } + + if(si->readFlag(ShapeInteraction::IN_PERSISTENT_EVENT_LIST) && (!(newPairFlags & PxPairFlag::eNOTIFY_TOUCH_PERSISTS))) + { + // the new report pair flags don't require persistent checks anymore -> remove from persistent list + // Note: The pair might get added to the force threshold list later + if(si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)) + removeFromPersistentContactEventPairs(si); + else + si->clearFlag(ShapeInteraction::WAS_IN_PERSISTENT_EVENT_LIST); + } + + if(newPairFlags & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS) + { + PX_ASSERT((si->mReportPairIndex == INVALID_REPORT_PAIR_ID) || (!si->readFlag(ShapeInteraction::WAS_IN_PERSISTENT_EVENT_LIST))); + + if(si->mReportPairIndex == INVALID_REPORT_PAIR_ID && si->readInteractionFlag(InteractionFlag::eIS_ACTIVE)) + { + PX_ASSERT(!si->readFlag(ShapeInteraction::WAS_IN_PERSISTENT_EVENT_LIST)); // sanity check: an active pair should never have this flag set + + if(si->hasTouch()) + addToForceThresholdContactEventPairs(si); + } + } + else if((oldPairFlags & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS)) + { + // no force threshold events needed any longer -> clear flags + si->clearFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_FLAGS); + + if(si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)) + removeFromForceThresholdContactEventPairs(si); + } + } + si->setPairFlags(finfo.pairFlags); + } + else if(oldType == InteractionType::eTRIGGER) + static_cast(pair)->setTriggerFlags(finfo.pairFlags); + + return pair; + } + } + case InteractionType::eCONSTRAINTSHADER: + case InteractionType::eARTICULATION: + case InteractionType::eTRACKED_IN_SCENE_COUNT: + case InteractionType::eINVALID: + PX_ASSERT(0); + break; + } + return NULL; +} + +Sc::ActorPair* Sc::NPhaseCore::findActorPair(ShapeSim* s0, ShapeSim* s1, Ps::IntBool isReportPair) +{ + PX_ASSERT(!(s0->getFlags() & PxShapeFlag::eTRIGGER_SHAPE) + && !(s1->getFlags() & PxShapeFlag::eTRIGGER_SHAPE)); + // This method is only for the case where a ShapeInteraction is going to be created. + // Else we might create an ActorPair that does not get referenced and causes a mem leak. + + BodyPairKey key; + + RigidSim* aLess = &s0->getRbSim(); + RigidSim* aMore = &s1->getRbSim(); + + if(aLess->getRigidID() > aMore->getRigidID()) + Ps::swap(aLess, aMore); + + key.mSim0 = aLess->getRigidID(); + key.mSim1 = aMore->getRigidID(); + + Sc::ActorPair*& actorPair = mActorPairMap[key]; + + //PX_ASSERT(actorPair == NULL || actorPair->getRefCount() > 0); + if(actorPair == NULL) + { + if(!isReportPair) + actorPair = mActorPairPool.construct(); + else + actorPair = mActorPairReportPool.construct(s0->getRbSim(), s1->getRbSim()); + } + + Ps::IntBool actorPairHasReports = actorPair->isReportPair(); + + if(!isReportPair || actorPairHasReports) + return actorPair; + else + { + PxU32 size = aLess->getActorInteractionCount(); + Interaction** interactions = aLess->getActorInteractions(); + + ActorPairReport* actorPairReport = mActorPairReportPool.construct(s0->getRbSim(), s1->getRbSim()); + actorPairReport->convert(*actorPair); + + while(size--) + { + Interaction* interaction = *interactions++; + if((&interaction->getActorSim0() == aMore) || (&interaction->getActorSim1() == aMore)) + { + PX_ASSERT(((&interaction->getActorSim0() == aLess) || (&interaction->getActorSim1() == aLess))); + + if(interaction->getType() == InteractionType::eOVERLAP) + { + ShapeInteraction* si = static_cast(interaction); + if(si->getActorPair() != NULL) + si->setActorPair(*actorPairReport); + } + } + } + actorPair = actorPairReport; + } + return actorPair; +} + +PX_FORCE_INLINE void Sc::NPhaseCore::destroyActorPairReport(ActorPairReport& aPair) +{ + PX_ASSERT(aPair.isReportPair()); + + aPair.releaseContactReportData(*this); + mActorPairReportPool.destroy(&aPair); +} + +Sc::ElementSimInteraction* Sc::NPhaseCore::convert(ElementSimInteraction* pair, InteractionType::Enum newType, PxFilterInfo& filterInfo, bool removeFromDirtyList, + PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + PX_ASSERT(newType != pair->getType()); + + ElementSim& elementA = pair->getElement0(); + ElementSim& elementB = pair->getElement1(); + + // Wake up the actors of the pair + if((pair->getActorSim0().getActorType() == PxActorType::eRIGID_DYNAMIC) && !(static_cast(pair->getActorSim0()).isActive())) + static_cast(pair->getActorSim0()).internalWakeUp(); + if((pair->getActorSim1().getActorType() == PxActorType::eRIGID_DYNAMIC) && !(static_cast(pair->getActorSim1()).isActive())) + static_cast(pair->getActorSim1()).internalWakeUp(); + + // Since the FilterPair struct might have been re-used in the newly created interaction, we need to clear + // the filter pair marker of the old interaction to avoid that the FilterPair gets deleted by the releaseElementPair() + // call that follows. + pair->clearInteractionFlag(InteractionFlag::eIS_FILTER_PAIR); + + // PT: we need to unregister the old interaction *before* creating the new one, because Sc::NPhaseCore::registerInteraction will use + // ElementSim pointers which are the same for both. Since "releaseElementPair" will call the unregister function from + // the element's dtor, we don't need to do it explicitly here. Just release the object. + releaseElementPair(pair, PairReleaseFlag::eWAKE_ON_LOST_TOUCH | PairReleaseFlag::eBP_VOLUME_REMOVED, 0, removeFromDirtyList, outputs, useAdaptiveForce); + + ElementSimInteraction* result = NULL; + switch(newType) + { + case InteractionType::eINVALID: + // This means the pair should get killed + break; + case InteractionType::eMARKER: + { + result = createElementInteractionMarker(elementA, elementB, NULL); + break; + } + case InteractionType::eOVERLAP: + { + result = createShapeInteraction(static_cast(elementA), static_cast(elementB), filterInfo.pairFlags, NULL, NULL); + break; + } + case InteractionType::eTRIGGER: + { + result = createTriggerInteraction(static_cast(elementA), static_cast(elementB), filterInfo.pairFlags); + break; + } + case InteractionType::eCONSTRAINTSHADER: + case InteractionType::eARTICULATION: + case InteractionType::eTRACKED_IN_SCENE_COUNT: + PX_ASSERT(0); + break; + }; + + if(filterInfo.filterPairIndex != INVALID_FILTER_PAIR_INDEX) + { + PX_ASSERT(result); + // If a filter callback pair is going to get killed, then the FilterPair struct should already have + // been deleted. + + // Mark the new interaction as a filter callback pair + result->raiseInteractionFlag(InteractionFlag::eIS_FILTER_PAIR); + + mFilterPairManager->setPair(filterInfo.filterPairIndex, result); + result->setFilterPairIndex(filterInfo.filterPairIndex); + } + return result; +} + +namespace physx +{ +namespace Sc +{ +static bool findTriggerContacts(TriggerInteraction* tri, bool toBeDeleted, bool volumeRemoved, + PxTriggerPair& triggerPair, TriggerPairExtraData& triggerPairExtra, + SimStats::TriggerPairCountsNonVolatile& triggerPairStats) +{ + ShapeSim& s0 = tri->getTriggerShape(); + ShapeSim& s1 = tri->getOtherShape(); + + const PxPairFlags pairFlags = tri->getTriggerFlags(); + PxPairFlags pairEvent; + + bool overlap; + PxU8 testForRemovedShapes = 0; + if(toBeDeleted) + { + // The trigger interaction is to lie down in its tomb, hence we know that the overlap is gone. + // What remains is to check whether the interaction was deleted because of a shape removal in + // which case we need to later check for removed shapes. + + overlap = false; + + if(volumeRemoved) + { + // Note: only the first removed volume can be detected when the trigger interaction is deleted but at a later point the second volume might get removed too. + testForRemovedShapes = TriggerPairFlag::eTEST_FOR_REMOVED_SHAPES; + } + } + else + { +#if PX_ENABLE_SIM_STATS + PX_ASSERT(s0.getGeometryType() < PxGeometryType::eCONVEXMESH+1); // The first has to be the trigger shape + triggerPairStats[s0.getGeometryType()][s1.getGeometryType()]++; +#else + PX_UNUSED(triggerPairStats); +#endif + + ShapeSim* primitive0 = &s0; + ShapeSim* primitive1 = &s1; + + PX_ASSERT(primitive0->getFlags() & PxShapeFlag::eTRIGGER_SHAPE + || primitive1->getFlags() & PxShapeFlag::eTRIGGER_SHAPE); + + // Reorder them if needed + if(primitive0->getGeometryType() > primitive1->getGeometryType()) + Ps::swap(primitive0, primitive1); + + const Gu::GeomOverlapFunc overlapFunc = + Gu::getOverlapFuncTable()[primitive0->getGeometryType()][primitive1->getGeometryType()]; + + PX_ALIGN(16, PxTransform globalPose0); + primitive0->getAbsPoseAligned(&globalPose0); + + PX_ALIGN(16, PxTransform globalPose1); + primitive1->getAbsPoseAligned(&globalPose1); + + PX_ASSERT(overlapFunc); + overlap = overlapFunc( primitive0->getCore().getGeometry(), globalPose0, + primitive1->getCore().getGeometry(), globalPose1, + &tri->getTriggerCache()); + } + + const bool hadOverlap = tri->lastFrameHadContacts(); + if(hadOverlap) + { + if(!overlap) + pairEvent = PxPairFlag::eNOTIFY_TOUCH_LOST; + } + else + { + if(overlap) + pairEvent = PxPairFlag::eNOTIFY_TOUCH_FOUND; + } + tri->updateLastFrameHadContacts(overlap); + + const PxPairFlags triggeredFlags = pairEvent & pairFlags; + if(triggeredFlags) + { + triggerPair.triggerShape = s0.getPxShape(); + triggerPair.otherShape = s1.getPxShape(); + triggerPair.status = PxPairFlag::Enum(PxU32(pairEvent)); + triggerPair.flags = PxTriggerPairFlags(testForRemovedShapes); + + const RigidCore& rigidCore0 = s0.getRbSim().getRigidCore(); + const RigidCore& rigidCore1 = s1.getRbSim().getRigidCore(); + + triggerPair.triggerActor = static_cast(rigidCore0.getPxActor()); + triggerPair.otherActor = static_cast(rigidCore1.getPxActor()); + + triggerPairExtra = TriggerPairExtraData(s0.getID(), s1.getID(), + rigidCore0.getOwnerClient(), rigidCore1.getOwnerClient()); + return true; + } + return false; +} + +class TriggerContactTask : public Cm::Task +{ +private: + TriggerContactTask& operator = (const TriggerContactTask&); + +public: + TriggerContactTask( Interaction* const* triggerPairs, PxU32 triggerPairCount, Ps::Mutex& lock, + TriggerInteraction** pairsToDeactivate, volatile PxI32& pairsToDeactivateCount, + Scene& scene) + : + Cm::Task(scene.getContextId()), + mTriggerPairs(triggerPairs), + mTriggerPairCount(triggerPairCount), + mLock(lock), + mPairsToDeactivate(pairsToDeactivate), + mPairsToDeactivateCount(pairsToDeactivateCount), + mScene(scene) + { + } + + virtual void runInternal() + { + SimStats::TriggerPairCountsNonVolatile triggerPairStats; +#if PX_ENABLE_SIM_STATS + PxMemZero(&triggerPairStats, sizeof(SimStats::TriggerPairCountsNonVolatile)); +#endif + PxTriggerPair triggerPair[sTriggerPairsPerTask]; + TriggerPairExtraData triggerPairExtra[sTriggerPairsPerTask]; + PxU32 triggerReportItemCount = 0; + + TriggerInteraction* deactivatePairs[sTriggerPairsPerTask]; + PxI32 deactivatePairCount = 0; + + for(PxU32 i=0; i < mTriggerPairCount; i++) + { + TriggerInteraction* tri = static_cast(mTriggerPairs[i]); + + PX_ASSERT(tri->readInteractionFlag(InteractionFlag::eIS_ACTIVE)); + + if(findTriggerContacts(tri, false, false, triggerPair[triggerReportItemCount], triggerPairExtra[triggerReportItemCount], triggerPairStats)) + triggerReportItemCount++; + + if(!(tri->readFlag(TriggerInteraction::PROCESS_THIS_FRAME))) + { + // active trigger pairs for which overlap tests were not forced should remain in the active list + // to catch transitions between overlap and no overlap + continue; + } + else + { + tri->clearFlag(TriggerInteraction::PROCESS_THIS_FRAME); + + // explicitly scheduled overlap test is done (after object creation, teleport, ...). Check if trigger pair should remain active or not. + + if(!tri->onActivate_(0)) + { + PX_ASSERT(tri->readInteractionFlag(InteractionFlag::eIS_ACTIVE)); + // Why is the assert enough? + // Once an explicit overlap test is scheduled, the interaction can not get deactivated anymore until it got processed. + + tri->clearInteractionFlag(InteractionFlag::eIS_ACTIVE); + deactivatePairs[deactivatePairCount] = tri; + deactivatePairCount++; + } + } + } + + if(triggerReportItemCount) + { + PxTriggerPair* triggerPairBuffer; + TriggerPairExtraData* triggerPairExtraBuffer; + + { + Ps::Mutex::ScopedLock lock(mLock); + + mScene.reserveTriggerReportBufferSpace(triggerReportItemCount, triggerPairBuffer, triggerPairExtraBuffer); + + PxMemCopy(triggerPairBuffer, triggerPair, sizeof(PxTriggerPair) * triggerReportItemCount); + PxMemCopy(triggerPairExtraBuffer, triggerPairExtra, sizeof(TriggerPairExtraData) * triggerReportItemCount); + } + } + + if(deactivatePairCount) + { + PxI32 newSize = Ps::atomicAdd(&mPairsToDeactivateCount, deactivatePairCount); + PxMemCopy(mPairsToDeactivate + newSize - deactivatePairCount, deactivatePairs, sizeof(TriggerInteraction*) * deactivatePairCount); + } + +#if PX_ENABLE_SIM_STATS + SimStats& simStats = mScene.getStatsInternal(); + for(PxU32 i=0; i < PxGeometryType::eCONVEXMESH+1; i++) + { + for(PxU32 j=0; j < PxGeometryType::eGEOMETRY_COUNT; j++) + { + if(triggerPairStats[i][j] != 0) + Ps::atomicAdd(&simStats.numTriggerPairs[i][j], triggerPairStats[i][j]); + } + } +#endif + } + + virtual const char* getName() const + { + return "ScNPhaseCore.triggerInteractionWork"; + } + +public: + static const PxU32 sTriggerPairsPerTask = 64; + +private: + Interaction* const* mTriggerPairs; + const PxU32 mTriggerPairCount; + Ps::Mutex& mLock; + TriggerInteraction** mPairsToDeactivate; + volatile PxI32& mPairsToDeactivateCount; + Scene& mScene; +}; + +} // namespace Sc +} // namespace physx + +void Sc::NPhaseCore::processTriggerInteractions(PxBaseTask* continuation) +{ + PX_ASSERT(!mTmpTriggerProcessingBlock); + PX_ASSERT(mTriggerPairsToDeactivateCount == 0); + + Scene& scene = mOwnerScene; + + // Triggers + Interaction** triggerInteractions = mOwnerScene.getActiveInteractions(InteractionType::eTRIGGER); + const PxU32 pairCount = mOwnerScene.getNbActiveInteractions(InteractionType::eTRIGGER); + + if(pairCount > 0) + { + const PxU32 taskCountWithoutRemainder = pairCount / TriggerContactTask::sTriggerPairsPerTask; + const PxU32 maxTaskCount = taskCountWithoutRemainder + 1; + const PxU32 pairPtrSize = pairCount * sizeof(TriggerInteraction*); + const PxU32 memBlockSize = pairPtrSize + (maxTaskCount * sizeof(TriggerContactTask)); + void* triggerProcessingBlock = scene.getLowLevelContext()->getScratchAllocator().alloc(memBlockSize, true); + if(triggerProcessingBlock) + { + const bool hasMultipleThreads = scene.getTaskManager().getCpuDispatcher()->getWorkerCount() > 1; + const bool moreThanOneBatch = pairCount > TriggerContactTask::sTriggerPairsPerTask; + const bool scheduleTasks = hasMultipleThreads && moreThanOneBatch; + // when running on a single thread, the task system seems to cause the main overhead (locking and atomic operations + // seemed less of an issue). Hence, the tasks get run directly in that case. Same if there is only one batch. + + mTmpTriggerProcessingBlock = triggerProcessingBlock; // note: gets released in the continuation task + if(scheduleTasks) + mMergeProcessedTriggerInteractions.setContinuation(continuation); + + TriggerInteraction** triggerPairsToDeactivateWriteBack = reinterpret_cast(triggerProcessingBlock); + TriggerContactTask* triggerContactTaskBuffer = reinterpret_cast(reinterpret_cast(triggerProcessingBlock) + pairPtrSize); + + PxU32 remainder = pairCount; + while(remainder) + { + const PxU32 nb = remainder > TriggerContactTask::sTriggerPairsPerTask ? TriggerContactTask::sTriggerPairsPerTask : remainder; + remainder -= nb; + + TriggerContactTask* task = triggerContactTaskBuffer; + task = PX_PLACEMENT_NEW(task, TriggerContactTask( triggerInteractions, nb, mTriggerWriteBackLock, + triggerPairsToDeactivateWriteBack, mTriggerPairsToDeactivateCount, scene)); + if(scheduleTasks) + { + task->setContinuation(&mMergeProcessedTriggerInteractions); + task->removeReference(); + } + else + task->runInternal(); + + triggerContactTaskBuffer++; + triggerInteractions += nb; + } + + if(scheduleTasks) + mMergeProcessedTriggerInteractions.removeReference(); + else + mMergeProcessedTriggerInteractions.runInternal(); + } + else + { + Ps::getFoundation().getErrorCallback().reportError(PxErrorCode::eOUT_OF_MEMORY, "Temporary memory for trigger pair processing could not be allocated. Trigger overlap tests will not take place.", __FILE__, __LINE__); + } + } +} + +void Sc::NPhaseCore::mergeProcessedTriggerInteractions(PxBaseTask*) +{ + if(mTmpTriggerProcessingBlock) + { + // deactivate pairs that do not need trigger checks any longer (until woken up again) + TriggerInteraction** triggerPairsToDeactivate = reinterpret_cast(mTmpTriggerProcessingBlock); + for(PxI32 i=0; i < mTriggerPairsToDeactivateCount; i++) + { + mOwnerScene.notifyInteractionDeactivated(triggerPairsToDeactivate[i]); + } + mTriggerPairsToDeactivateCount = 0; + + mOwnerScene.getLowLevelContext()->getScratchAllocator().free(mTmpTriggerProcessingBlock); + mTmpTriggerProcessingBlock = NULL; + } +} + +void Sc::NPhaseCore::visualize(Cm::RenderOutput& renderOut, PxsContactManagerOutputIterator& outputs) +{ + if(mOwnerScene.getVisualizationScale() == 0.0f) + return; + + Interaction** interactions = mOwnerScene.getActiveInteractions(InteractionType::eOVERLAP); + PxU32 nbActiveInteractions = mOwnerScene.getNbActiveInteractions(InteractionType::eOVERLAP); + while(nbActiveInteractions--) + static_cast(*interactions++)->visualize(renderOut, outputs); +} + +class ProcessPersistentContactTask : public Cm::Task +{ + Sc::NPhaseCore& mCore; + ContactReportBuffer& mBuffer; + Ps::Mutex& mMutex; + ShapeInteraction*const* mPersistentEventPairs; + PxU32 mNbPersistentEventPairs; + PxsContactManagerOutputIterator mOutputs; + PX_NOCOPY(ProcessPersistentContactTask) +public: + + ProcessPersistentContactTask(Sc::NPhaseCore& core, ContactReportBuffer& buffer, Ps::Mutex& mutex, ShapeInteraction*const* persistentEventPairs, + PxU32 nbPersistentEventPairs, PxsContactManagerOutputIterator& outputs) : Cm::Task(0), mCore(core), mBuffer(buffer), mMutex(mutex), + mPersistentEventPairs(persistentEventPairs), mNbPersistentEventPairs(nbPersistentEventPairs), mOutputs(outputs) + { + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("ProcessPersistentContactTask", 0); + //ContactReportAllocationManager alloc(mBuffer, mMutex, 16 * 1024); + PxU32 size = mNbPersistentEventPairs; + ShapeInteraction*const* persistentEventPairs = mPersistentEventPairs; + while (size--) + { + ShapeInteraction* pair = *persistentEventPairs++; + if (size) + { + if (size > 1) + { + if (size > 2) + { + ShapeInteraction* nextPair = *(persistentEventPairs + 2); + Ps::prefetchLine(nextPair); + } + + ShapeInteraction* nextPair = *(persistentEventPairs + 1); + ActorPair* aPair = nextPair->getActorPair(); + Ps::prefetchLine(aPair); + + Ps::prefetchLine(&nextPair->getShape0()); + Ps::prefetchLine(&nextPair->getShape1()); + } + ShapeInteraction* nextPair = *(persistentEventPairs); + Ps::prefetchLine(&nextPair->getShape0().getActor()); + Ps::prefetchLine(&nextPair->getShape1().getActor()); + } + + PX_ASSERT(pair->hasTouch()); + PX_ASSERT(pair->isReportPair()); + + const PxU32 pairFlags = pair->getPairFlags(); + if ((pairFlags & PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) == PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) + { + // do not process the pair if only eDETECT_CCD_CONTACT is enabled because at this point CCD did not run yet. Plus the current CCD implementation can not reliably provide eNOTIFY_TOUCH_PERSISTS events + // for performance reasons. + //KS - filter based on edge activity! + + const BodySim* bodySim0 = pair->getShape0().getBodySim(); + const BodySim* bodySim1 = pair->getShape1().getBodySim(); + PX_ASSERT(bodySim0); // the first shape always belongs to a dynamic body + + if (bodySim0->isActive() || (bodySim1 && bodySim1->isActive())) + pair->processUserNotificationAsync(PxPairFlag::eNOTIFY_TOUCH_PERSISTS, 0, false, 0, false, mOutputs/*, &alloc*/); + } + } + } + + virtual const char* getName() const + { + return "ScNPhaseCore.ProcessPersistentContactTask"; + } + +}; + + +void Sc::NPhaseCore::processPersistentContactEvents(PxsContactManagerOutputIterator& outputs, PxBaseTask* continuation) +{ + PX_UNUSED(continuation); + PX_UNUSED(outputs); + PX_PROFILE_ZONE("Sc::NPhaseCore::processPersistentContactEvents", mOwnerScene.getContextId()); + //TODO: put back this optimization -- now we have to do this stuff if at least one client has a callback registered. + // if (ownerScene.getSimulatonEventCallbackFast() != NULL) + { + // Go through ShapeInteractions which requested persistent contact event reports. This is necessary since there are no low level events for persistent contact. + ShapeInteraction*const* persistentEventPairs = getCurrentPersistentContactEventPairs(); + PxU32 size = getCurrentPersistentContactEventPairCount(); + while (size--) + { + ShapeInteraction* pair = *persistentEventPairs++; + if (size) + { + ShapeInteraction* nextPair = *persistentEventPairs; + Ps::prefetchLine(nextPair); + } + + ActorPair* aPair = pair->getActorPair(); + Ps::prefetchLine(aPair); + + PX_ASSERT(pair->hasTouch()); + PX_ASSERT(pair->isReportPair()); + + const PxU32 pairFlags = pair->getPairFlags(); + if ((pairFlags & PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) == PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) + { + // do not process the pair if only eDETECT_CCD_CONTACT is enabled because at this point CCD did not run yet. Plus the current CCD implementation can not reliably provide eNOTIFY_TOUCH_PERSISTS events + // for performance reasons. + //KS - filter based on edge activity! + + const BodySim* bodySim0 = pair->getShape0().getBodySim(); + const BodySim* bodySim1 = pair->getShape1().getBodySim(); + PX_ASSERT(bodySim0); // the first shape always belongs to a dynamic body + + if (bodySim0->isActive() || (bodySim1 && bodySim1->isActive())) + pair->processUserNotification(PxPairFlag::eNOTIFY_TOUCH_PERSISTS, 0, false, 0, false, outputs); + } + } + } + + //{ + // PX_PROFILE_ZONE("Sc::NPhaseCore::processPersistentContactEvents2", mOwnerScene.getContextId()); + // // Go through ShapeInteractions which requested persistent contact event reports. This is necessary since there are no low level events for persistent contact. + // ShapeInteraction*const* persistentEventPairs = getCurrentPersistentContactEventPairs(); + // PxU32 size = getCurrentPersistentContactEventPairCount(); + + // //const PxU32 nbPerTask = 512u; + + // //for (PxU32 a = 0; a < size; a += nbPerTask) + // { + // ProcessPersistentContactTask* task = PX_PLACEMENT_NEW(mOwnerScene.getFlushPool()->allocate(sizeof(ProcessPersistentContactTask)), + // ProcessPersistentContactTask)(*this, mContactReportBuffer, mBufferAllocLock, persistentEventPairs, size, outputs); + // task->setContinuation(continuation); + // task->removeReference(); + // } + + // while (size--) + // { + // ShapeInteraction* pair = *persistentEventPairs++; + // if (size) + // { + // if (size > 1) + // { + // ShapeInteraction* nextPair = *(persistentEventPairs + 1); + // Ps::prefetchLine(nextPair); + // } + // ShapeInteraction* nextPair = *(persistentEventPairs); + // ActorPair* aPair = nextPair->getActorPair(); + // Ps::prefetchLine(aPair); + // } + + // PX_ASSERT(pair->hasTouch()); + // PX_ASSERT(pair->isReportPair()); + + // const PxU32 pairFlags = pair->getPairFlags(); + // if ((pairFlags & PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) == PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS | PxPairFlag::eDETECT_DISCRETE_CONTACT)) + // { + // if (pair->getActorPair() != NULL) + // { + // ActorPairReport& aPairReport = pair->getActorPairReport(); + + // if (!aPairReport.isInContactReportActorPairSet()) + // { + // aPairReport.setInContactReportActorPairSet(); + // addToContactReportActorPairSet(&aPairReport); + // aPairReport.incRefCount(); + // } + + // //aPairReport.createContactStreamManager(*this); + // } + // } + // } + //} +} + +void Sc::NPhaseCore::fireCustomFilteringCallbacks(PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + PX_PROFILE_ZONE("Sim.fireCustomFilteringCallbacks", mOwnerScene.getContextId()); + + PxSimulationFilterCallback* callback = mOwnerScene.getFilterCallbackFast(); + + if(callback) + { + // Ask user for pair filter status changes + PxU32 pairID; + PxFilterFlags filterFlags; + PxPairFlags pairFlags; + while(callback->statusChange(pairID, pairFlags, filterFlags)) + { + ElementSimInteraction* ei = (*mFilterPairManager)[pairID]; + + PX_ASSERT(ei); + // Check if the user tries to update a pair even though he deleted it earlier in the same frame + + checkFilterFlags(filterFlags); + + PX_ASSERT(ei->readInteractionFlag(InteractionFlag::eIS_FILTER_PAIR)); + + PxFilterInfo finfo; + finfo.filterFlags = filterFlags; + finfo.pairFlags = pairFlags; + finfo.filterPairIndex = pairID; + + ElementSimInteraction* refInt = refilterInteraction(ei, &finfo, true, outputs, useAdaptiveForce); + + // this gets called at the end of the simulation -> there should be no dirty interactions around + PX_ASSERT(!refInt->readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)); + PX_ASSERT(!refInt->getDirtyFlags()); + + if((refInt == ei) && (refInt->getType() == InteractionType::eOVERLAP)) // No interaction conversion happened, the pairFlags were just updated + static_cast(refInt)->updateState(InteractionDirtyFlag::eFILTER_STATE); + } + } +} + +void Sc::NPhaseCore::addToDirtyInteractionList(Interaction* pair) +{ + mDirtyInteractions.insert(pair); +} + +void Sc::NPhaseCore::removeFromDirtyInteractionList(Interaction* pair) +{ + PX_ASSERT(mDirtyInteractions.contains(pair)); + + mDirtyInteractions.erase(pair); +} + +void Sc::NPhaseCore::updateDirtyInteractions(PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + // The sleeping SIs will be updated on activation + // clow: Sleeping SIs are not awaken for visualization updates + const bool dirtyDominance = mOwnerScene.readFlag(SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_DOMINANCE); + const bool dirtyVisualization = mOwnerScene.readFlag(SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_VISUALIZATION); + if(dirtyDominance || dirtyVisualization) + { + // Update all interactions. + + const PxU8 mask = Ps::to8((dirtyDominance ? InteractionDirtyFlag::eDOMINANCE : 0) | (dirtyVisualization ? InteractionDirtyFlag::eVISUALIZATION : 0)); + + Interaction** it = mOwnerScene.getInteractions(Sc::InteractionType::eOVERLAP); + PxU32 size = mOwnerScene.getNbInteractions(Sc::InteractionType::eOVERLAP); + while(size--) + { + Interaction* pair = *it++; + + PX_ASSERT(pair->getType() == InteractionType::eOVERLAP); + + if(!pair->readInteractionFlag(InteractionFlag::eIN_DIRTY_LIST)) + { + PX_ASSERT(!pair->getDirtyFlags()); + static_cast(pair)->updateState(mask); + } + else + pair->setDirty(mask); // the pair will get processed further below anyway, so just mark the flags dirty + } + } + + // Update all interactions in the dirty list + const PxU32 dirtyItcCount = mDirtyInteractions.size(); + Sc::Interaction* const* dirtyInteractions = mDirtyInteractions.getEntries(); + for(PxU32 i = 0; i < dirtyItcCount; i++) + { + Interaction* refInt = dirtyInteractions[i]; + Interaction* interaction = refInt; + + if(interaction->isElementInteraction() && interaction->needsRefiltering()) + { + ElementSimInteraction* pair = static_cast(interaction); + + refInt = refilterInteraction(pair, NULL, false, outputs, useAdaptiveForce); + } + + if(interaction == refInt) // Refiltering might convert the pair to another type and kill the old one. In that case we don't want to update the new pair since it has been updated on creation. + { + const InteractionType::Enum iType = interaction->getType(); + if (iType == InteractionType::eOVERLAP) + static_cast(interaction)->updateState(0); + else if (iType == InteractionType::eCONSTRAINTSHADER) + static_cast(interaction)->updateState(); + + interaction->setClean(false); // false because the dirty interactions list gets cleard further below + } + } + + mDirtyInteractions.clear(); +} + +void Sc::NPhaseCore::releaseElementPair(ElementSimInteraction* pair, PxU32 flags, const PxU32 ccdPass, bool removeFromDirtyList, PxsContactManagerOutputIterator& outputs, + bool useAdaptiveForce) +{ + pair->setClean(removeFromDirtyList); // Removes the pair from the dirty interaction list etc. + + if(pair->readInteractionFlag(InteractionFlag::eIS_FILTER_PAIR)) + { + // Check if this is a filter callback pair + const PxU32 filterPairIndex = mFilterPairManager->findIndex(pair); + PX_ASSERT(filterPairIndex!=INVALID_FILTER_PAIR_INDEX); + + // Is this interaction removed because one of the pair object broadphase volumes got deleted by the user? + const bool userRemovedVolume = ((flags & PairReleaseFlag::eBP_VOLUME_REMOVED) != 0); + + callPairLost(mOwnerScene, pair->getElement0(), pair->getElement1(), filterPairIndex, userRemovedVolume); + + mFilterPairManager->releaseIndex(filterPairIndex); + } + + switch(pair->getType()) + { + case InteractionType::eTRIGGER: + { + TriggerInteraction* tri = static_cast(pair); + PxTriggerPair triggerPair; + TriggerPairExtraData triggerPairExtra; + if (findTriggerContacts(tri, true, ((flags & PairReleaseFlag::eBP_VOLUME_REMOVED) != 0), + triggerPair, triggerPairExtra, + const_cast(mOwnerScene.getStatsInternal().numTriggerPairs))) + // cast away volatile-ness (this is fine since the method does not run in parallel) + { + mOwnerScene.getTriggerBufferAPI().pushBack(triggerPair); + mOwnerScene.getTriggerBufferExtraData().pushBack(triggerPairExtra); + } + mTriggerInteractionPool.destroy(tri); + } + break; + case InteractionType::eMARKER: + { + ElementInteractionMarker* interactionMarker = static_cast(pair); + mInteractionMarkerPool.destroy(interactionMarker); + } + break; + case InteractionType::eOVERLAP: + { + ShapeInteraction* si = static_cast(pair); + if(flags & PairReleaseFlag::eSHAPE_BP_VOLUME_REMOVED) + lostTouchReports(si, flags, ccdPass, outputs, useAdaptiveForce); + + mShapeInteractionPool.destroy(si); + } + break; + case InteractionType::eCONSTRAINTSHADER: + case InteractionType::eARTICULATION: + case InteractionType::eTRACKED_IN_SCENE_COUNT: + case InteractionType::eINVALID: + PX_ASSERT(0); + return; + } +} + +void Sc::NPhaseCore::lostTouchReports(ShapeInteraction* si, PxU32 flags, PxU32 ccdPass, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + if(si->hasTouch()) + { + if(si->isReportPair()) + si->sendLostTouchReport((flags & PairReleaseFlag::eBP_VOLUME_REMOVED) != 0, ccdPass, outputs); + + si->adjustCountersOnLostTouch(si->getShape0().getBodySim(), si->getShape1().getBodySim(), useAdaptiveForce); + } + + ActorPair* aPair = si->getActorPair(); + if(aPair && aPair->decRefCount() == 0) + { + RigidSim* sim0 = static_cast(&si->getActorSim0()); + RigidSim* sim1 = static_cast(&si->getActorSim1()); + + BodyPairKey pair; + + if(sim0->getRigidID() > sim1->getRigidID()) + Ps::swap(sim0, sim1); + + pair.mSim0 = sim0->getRigidID(); + pair.mSim1 = sim1->getRigidID(); + + mActorPairMap.erase(pair); + + if(!aPair->isReportPair()) + { + mActorPairPool.destroy(aPair); + } + else + { + ActorPairReport& apr = ActorPairReport::cast(*aPair); + destroyActorPairReport(apr); + } + } + si->clearActorPair(); + + if(si->hasTouch() || (!si->hasKnownTouchState())) + { + BodySim* b0 = si->getShape0().getBodySim(); + BodySim* b1 = si->getShape1().getBodySim(); + + if(flags & PairReleaseFlag::eWAKE_ON_LOST_TOUCH) + { + if(!b0 || !b1) + { + if(b0) + b0->internalWakeUp(); + if(b1) + b1->internalWakeUp(); + } + else if(!si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) + { + mOwnerScene.addToLostTouchList(b0, b1); + } + } + } +} + +void Sc::NPhaseCore::clearContactReportActorPairs(bool shrinkToZero) +{ + for(PxU32 i=0; i < mContactReportActorPairSet.size(); i++) + { + //TODO: prefetch? + ActorPairReport* aPair = mContactReportActorPairSet[i]; + const PxU32 refCount = aPair->getRefCount(); + PX_ASSERT(aPair->isInContactReportActorPairSet()); + PX_ASSERT(refCount > 0); + aPair->decRefCount(); // Reference held by contact callback + if(refCount > 1) + { + aPair->clearInContactReportActorPairSet(); + } + else + { + BodyPairKey pair; + PxU32 actorAID = aPair->getActorAID(); + PxU32 actorBID = aPair->getActorBID(); + pair.mSim0 = PxMin(actorAID, actorBID); + pair.mSim1 = PxMax(actorAID, actorBID); + + mActorPairMap.erase(pair); + destroyActorPairReport(*aPair); + } + } + + if(!shrinkToZero) + mContactReportActorPairSet.clear(); + else + mContactReportActorPairSet.reset(); +} + +void Sc::NPhaseCore::addToPersistentContactEventPairs(ShapeInteraction* si) +{ + // Pairs which request events which do not get triggered by the sdk and thus need to be tested actively every frame. + PX_ASSERT(si->getPairFlags() & (PxPairFlag::eNOTIFY_TOUCH_PERSISTS | ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS)); + PX_ASSERT(si->mReportPairIndex == INVALID_REPORT_PAIR_ID); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + PX_ASSERT(si->hasTouch()); // only pairs which can from now on lose or keep contact should be in this list + + si->raiseFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST); + if(mPersistentContactEventPairList.size() == mNextFramePersistentContactEventPairIndex) + { + si->mReportPairIndex = mPersistentContactEventPairList.size(); + mPersistentContactEventPairList.pushBack(si); + } + else + { + //swap with first entry that will be active next frame + ShapeInteraction* firstDelayedSi = mPersistentContactEventPairList[mNextFramePersistentContactEventPairIndex]; + firstDelayedSi->mReportPairIndex = mPersistentContactEventPairList.size(); + mPersistentContactEventPairList.pushBack(firstDelayedSi); + si->mReportPairIndex = mNextFramePersistentContactEventPairIndex; + mPersistentContactEventPairList[mNextFramePersistentContactEventPairIndex] = si; + } + + mNextFramePersistentContactEventPairIndex++; +} + +void Sc::NPhaseCore::addToPersistentContactEventPairsDelayed(ShapeInteraction* si) +{ + // Pairs which request events which do not get triggered by the sdk and thus need to be tested actively every frame. + PX_ASSERT(si->getPairFlags() & (PxPairFlag::eNOTIFY_TOUCH_PERSISTS | ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS)); + PX_ASSERT(si->mReportPairIndex == INVALID_REPORT_PAIR_ID); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + PX_ASSERT(si->hasTouch()); // only pairs which can from now on lose or keep contact should be in this list + + si->raiseFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST); + si->mReportPairIndex = mPersistentContactEventPairList.size(); + mPersistentContactEventPairList.pushBack(si); +} + +void Sc::NPhaseCore::removeFromPersistentContactEventPairs(ShapeInteraction* si) +{ + PX_ASSERT(si->getPairFlags() & (PxPairFlag::eNOTIFY_TOUCH_PERSISTS | ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS)); + PX_ASSERT(si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + PX_ASSERT(si->hasTouch()); // only pairs which could lose or keep contact should be in this list + + PxU32 index = si->mReportPairIndex; + PX_ASSERT(index != INVALID_REPORT_PAIR_ID); + + if(index < mNextFramePersistentContactEventPairIndex) + { + const PxU32 replaceIdx = mNextFramePersistentContactEventPairIndex - 1; + + if((mNextFramePersistentContactEventPairIndex < mPersistentContactEventPairList.size()) && (index != replaceIdx)) + { + // keep next frame persistent pairs at the back of the list + ShapeInteraction* tmp = mPersistentContactEventPairList[replaceIdx]; + mPersistentContactEventPairList[index] = tmp; + tmp->mReportPairIndex = index; + index = replaceIdx; + } + + mNextFramePersistentContactEventPairIndex--; + } + + si->clearFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST); + si->mReportPairIndex = INVALID_REPORT_PAIR_ID; + mPersistentContactEventPairList.replaceWithLast(index); + if(index < mPersistentContactEventPairList.size()) // Only adjust the index if the removed SIP was not at the end of the list + mPersistentContactEventPairList[index]->mReportPairIndex = index; +} + +void Sc::NPhaseCore::addToForceThresholdContactEventPairs(ShapeInteraction* si) +{ + PX_ASSERT(si->getPairFlags() & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS); + PX_ASSERT(si->mReportPairIndex == INVALID_REPORT_PAIR_ID); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + PX_ASSERT(si->hasTouch()); + + si->raiseFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST); + si->mReportPairIndex = mForceThresholdContactEventPairList.size(); + mForceThresholdContactEventPairList.pushBack(si); +} + +void Sc::NPhaseCore::removeFromForceThresholdContactEventPairs(ShapeInteraction* si) +{ + PX_ASSERT(si->getPairFlags() & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS); + PX_ASSERT(si->readFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + PX_ASSERT(!si->readFlag(ShapeInteraction::IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(si->hasTouch()); + + const PxU32 index = si->mReportPairIndex; + PX_ASSERT(index != INVALID_REPORT_PAIR_ID); + + si->clearFlag(ShapeInteraction::IS_IN_FORCE_THRESHOLD_EVENT_LIST); + si->mReportPairIndex = INVALID_REPORT_PAIR_ID; + mForceThresholdContactEventPairList.replaceWithLast(index); + if(index < mForceThresholdContactEventPairList.size()) // Only adjust the index if the removed SIP was not at the end of the list + mForceThresholdContactEventPairList[index]->mReportPairIndex = index; +} + +PxU8* Sc::NPhaseCore::reserveContactReportPairData(PxU32 pairCount, PxU32 extraDataSize, PxU32& bufferIndex, + ContactReportAllocationManager* alloc) +{ + extraDataSize = Sc::ContactStreamManager::computeExtraDataBlockSize(extraDataSize); + return alloc ? alloc->allocate(extraDataSize + (pairCount * sizeof(Sc::ContactShapePair)), bufferIndex) : mContactReportBuffer.allocateNotThreadSafe(extraDataSize + (pairCount * sizeof(Sc::ContactShapePair)), bufferIndex); +} + +PxU8* Sc::NPhaseCore::resizeContactReportPairData(PxU32 pairCount, PxU32 extraDataSize, Sc::ContactStreamManager& csm) +{ + PX_ASSERT((pairCount > csm.maxPairCount) || (extraDataSize > csm.getMaxExtraDataSize())); + PX_ASSERT((csm.currentPairCount == csm.maxPairCount) || (extraDataSize > csm.getMaxExtraDataSize())); + PX_ASSERT(extraDataSize >= csm.getMaxExtraDataSize()); // we do not support stealing memory from the extra data part when the memory for pair info runs out + + PxU32 bufferIndex; + Ps::prefetch(mContactReportBuffer.getData(csm.bufferIndex)); + + extraDataSize = Sc::ContactStreamManager::computeExtraDataBlockSize(extraDataSize); + PxU8* stream = mContactReportBuffer.reallocateNotThreadSafe(extraDataSize + (pairCount * sizeof(Sc::ContactShapePair)), bufferIndex, 16, csm.bufferIndex); + PxU8* oldStream = mContactReportBuffer.getData(csm.bufferIndex); + if(stream) + { + const PxU32 maxExtraDataSize = csm.getMaxExtraDataSize(); + if(csm.bufferIndex != bufferIndex) + { + if(extraDataSize <= maxExtraDataSize) + PxMemCopy(stream, oldStream, maxExtraDataSize + (csm.currentPairCount * sizeof(Sc::ContactShapePair))); + else + { + PxMemCopy(stream, oldStream, csm.extraDataSize); + PxMemCopy(stream + extraDataSize, oldStream + maxExtraDataSize, csm.currentPairCount * sizeof(Sc::ContactShapePair)); + } + csm.bufferIndex = bufferIndex; + } + else if(extraDataSize > maxExtraDataSize) + PxMemMove(stream + extraDataSize, oldStream + maxExtraDataSize, csm.currentPairCount * sizeof(Sc::ContactShapePair)); + + if(pairCount > csm.maxPairCount) + csm.maxPairCount = Ps::to16(pairCount); + if(extraDataSize > maxExtraDataSize) + csm.setMaxExtraDataSize(extraDataSize); + } + return stream; +} + +Sc::ActorPairContactReportData* Sc::NPhaseCore::createActorPairContactReportData() +{ + Ps::Mutex::ScopedLock lock(mReportAllocLock); + return mActorPairContactReportDataPool.construct(); +} + +void Sc::NPhaseCore::releaseActorPairContactReportData(ActorPairContactReportData* data) +{ + mActorPairContactReportDataPool.destroy(data); +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h new file mode 100644 index 000000000..852975781 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h @@ -0,0 +1,390 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_NPHASE_CORE +#define PX_PHYSICS_SCP_NPHASE_CORE + +#include "CmPhysXCommon.h" +#include "CmRenderOutput.h" +#include "PxPhysXConfig.h" +#include "PsUserAllocated.h" +#include "PsMutex.h" +#include "PsAtomic.h" +#include "PsPool.h" +#include "PsHashSet.h" +#include "PsHashMap.h" +#include "PxSimulationEventCallback.h" +#include "ScTriggerPairs.h" +#include "ScScene.h" +#include "ScContactReportBuffer.h" +#include "PsHash.h" + +namespace physx +{ +namespace Bp +{ + struct AABBOverlap; + struct BroadPhasePair; +} + +namespace Sc +{ + class ActorSim; + class ElementSim; + class ShapeSim; + + class Interaction; + class ElementSimInteraction; + class ElementInteractionMarker; + class TriggerInteraction; + + class ShapeInteraction; + class ActorPair; + class ActorPairReport; + + class ActorPairContactReportData; + struct ContactShapePair; + + class NPhaseContext; + class ContactStreamManager; + + struct FilterPair; + class FilterPairManager; + + class RigidSim; + + struct PairReleaseFlag + { + enum Enum + { + eBP_VOLUME_REMOVED = (1 << 0), // the broadphase volume of one pair object has been removed. + eSHAPE_BP_VOLUME_REMOVED = (1 << 1) | eBP_VOLUME_REMOVED, // the removed broadphase volume was from a rigid body shape. + eWAKE_ON_LOST_TOUCH = (1 << 2) + }; + }; + + /* + Description: NPhaseCore encapsulates the near phase processing to allow multiple implementations(eg threading and non + threaded). + + The broadphase inserts shape pairs into the NPhaseCore, which are then processed into contact point streams. + Pairs can then be processed into AxisConstraints by the GroupSolveCore. + */ + + struct BodyPairKey + { + PxU32 mSim0; + PxU32 mSim1; + + PX_FORCE_INLINE bool operator == (const BodyPairKey& pair) const { return mSim0 == pair.mSim0 && mSim1 == pair.mSim1; } + }; + + PX_INLINE PxU32 hash(const BodyPairKey& key) + { + const PxU32 add0 = key.mSim0; + const PxU32 add1 = key.mSim1; + + const PxU32 base = PxU32((add0 & 0xFFFF) | (add1 << 16)); + + return physx::shdfnd::hash(base); + } + + struct ElementSimKey + { + ElementSim* mSim0, *mSim1; + + ElementSimKey() : mSim0(NULL), mSim1(NULL) + {} + + ElementSimKey(ElementSim* sim0, ElementSim* sim1) + { + if(sim0 > sim1) + Ps::swap(sim0, sim1); + mSim0 = sim0; + mSim1 = sim1; + } + + PX_FORCE_INLINE bool operator == (const ElementSimKey& pair) const { return mSim0 == pair.mSim0 && mSim1 == pair.mSim1; } + }; + + PX_INLINE PxU32 hash(const ElementSimKey& key) + { + PxU32 add0 = (size_t(key.mSim0)) & 0xFFFFFFFF; + PxU32 add1 = (size_t(key.mSim1)) & 0xFFFFFFFF; + + //Clear the lower 2 bits, they will be 0s anyway + add0 = add0 >> 2; + add1 = add1 >> 2; + + const PxU32 base = PxU32((add0 & 0xFFFF) | (add1 << 16)); + + return physx::shdfnd::hash(base); + } + + class ContactReportAllocationManager + { + PxU8* mBuffer; + PxU32 mBufferSize; + PxU32 mCurrentBufferIndex; + PxU32 mCurrentOffset; + ContactReportBuffer& mReportBuffer; + Ps::Mutex& mMutex; + const PxU32 mBuferBlockSize; + PX_NOCOPY(ContactReportAllocationManager) + public: + + ContactReportAllocationManager(ContactReportBuffer& buffer, Ps::Mutex& mutex, const PxU32 bufferBlockSize = 16384) : mBuffer(NULL), mBufferSize(0), mCurrentBufferIndex(0), + mCurrentOffset(0), mReportBuffer(buffer), mMutex(mutex), mBuferBlockSize(bufferBlockSize) + { + } + + PxU8* allocate(const PxU32 size, PxU32& index, PxU32 alignment = 16u) + { + //(1) fix up offsets... + PxU32 pad = ((mCurrentBufferIndex + alignment - 1)&~(alignment - 1)) - mCurrentBufferIndex; + PxU32 currOffset = mCurrentOffset + pad; + + if ((currOffset + size) > mBufferSize) + { + const PxU32 allocSize = PxMax(size, mBuferBlockSize); + + mMutex.lock(); + mBuffer = mReportBuffer.allocateNotThreadSafe(allocSize, mCurrentBufferIndex, alignment); + mCurrentOffset = currOffset = 0; + mBufferSize = allocSize; + mMutex.unlock(); + } + + PxU8* ret = mBuffer + currOffset; + index = mCurrentBufferIndex + currOffset; + mCurrentOffset = currOffset + size; + return ret; + } + }; + + class NPhaseCore : public Ps::UserAllocated + { + PX_NOCOPY(NPhaseCore) + + public: + NPhaseCore(Scene& scene, const PxSceneDesc& desc); + ~NPhaseCore(); + + void onOverlapCreated(const Bp::AABBOverlap* PX_RESTRICT pairs, PxU32 pairCount); + + void runOverlapFilters( PxU32 nbToProcess, const Bp::AABBOverlap* PX_RESTRICT pairs, PxFilterInfo* PX_RESTRICT filterInfo, + PxU32& nbToKeep, PxU32& nbToSuppress, PxU32& nbToCallback, PxU32* PX_RESTRICT keepMap, PxU32* PX_RESTRICT callbackMap); + + ElementSimInteraction* onOverlapRemovedStage1(ElementSim* volume0, ElementSim* volume1); + void onOverlapRemoved(ElementSim* volume0, ElementSim* volume1, const PxU32 ccdPass, void* elemSim, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + void onVolumeRemoved(ElementSim* volume, PxU32 flags, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + + void managerNewTouch(Sc::ShapeInteraction& interaction); + + PxU32 getDefaultContactReportStreamBufferSize() const; + + void fireCustomFilteringCallbacks(PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + + void addToDirtyInteractionList(Interaction* interaction); + void removeFromDirtyInteractionList(Interaction* interaction); + void updateDirtyInteractions(PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + + /* + Description: Perform trigger overlap tests. + */ + void processTriggerInteractions(PxBaseTask* continuation); + + /* + Description: Gather results from trigger overlap tests and clean up. + */ + void mergeProcessedTriggerInteractions(PxBaseTask* continuation); + + /* + Description: Check candidates for persistent touch contact events and create those events if necessary. + */ + void processPersistentContactEvents(PxsContactManagerOutputIterator& outputs, PxBaseTask* continuation); + + /* + Description: Displays visualizations associated with the near phase. + */ + void visualize(Cm::RenderOutput& out, PxsContactManagerOutputIterator& outputs); + + PX_FORCE_INLINE Scene& getScene() const { return mOwnerScene; } + + PX_FORCE_INLINE void addToContactReportActorPairSet(ActorPairReport* pair) { mContactReportActorPairSet.pushBack(pair); } + void clearContactReportActorPairs(bool shrinkToZero); + PX_FORCE_INLINE PxU32 getNbContactReportActorPairs() const { return mContactReportActorPairSet.size(); } + PX_FORCE_INLINE ActorPairReport* const* getContactReportActorPairs() const { return mContactReportActorPairSet.begin(); } + + void addToPersistentContactEventPairs(ShapeInteraction*); + void addToPersistentContactEventPairsDelayed(ShapeInteraction*); + void removeFromPersistentContactEventPairs(ShapeInteraction*); + PX_FORCE_INLINE PxU32 getCurrentPersistentContactEventPairCount() const { return mNextFramePersistentContactEventPairIndex; } + PX_FORCE_INLINE ShapeInteraction* const* getCurrentPersistentContactEventPairs() const { return mPersistentContactEventPairList.begin(); } + PX_FORCE_INLINE PxU32 getAllPersistentContactEventPairCount() const { return mPersistentContactEventPairList.size(); } + PX_FORCE_INLINE ShapeInteraction* const* getAllPersistentContactEventPairs() const { return mPersistentContactEventPairList.begin(); } + PX_FORCE_INLINE void preparePersistentContactEventListForNextFrame(); + + void addToForceThresholdContactEventPairs(ShapeInteraction*); + void removeFromForceThresholdContactEventPairs(ShapeInteraction*); + PX_FORCE_INLINE PxU32 getForceThresholdContactEventPairCount() const { return mForceThresholdContactEventPairList.size(); } + PX_FORCE_INLINE ShapeInteraction* const* getForceThresholdContactEventPairs() const { return mForceThresholdContactEventPairList.begin(); } + + PX_FORCE_INLINE PxU8* getContactReportPairData(const PxU32& bufferIndex) const { return mContactReportBuffer.getData(bufferIndex); } + PxU8* reserveContactReportPairData(PxU32 pairCount, PxU32 extraDataSize, PxU32& bufferIndex, ContactReportAllocationManager* alloc = NULL); + PxU8* resizeContactReportPairData(PxU32 pairCount, PxU32 extraDataSize, Sc::ContactStreamManager& csm); + PX_FORCE_INLINE void clearContactReportStream() { mContactReportBuffer.reset(); } // Do not free memory at all + PX_FORCE_INLINE void freeContactReportStreamMemory() { mContactReportBuffer.flush(); } + + ActorPairContactReportData* createActorPairContactReportData(); + void releaseActorPairContactReportData(ActorPairContactReportData* data); + + void registerInteraction(ElementSimInteraction* interaction); + void unregisterInteraction(ElementSimInteraction* interaction); + + ElementSimInteraction* createRbElementInteraction(const PxFilterInfo& fInfo, ShapeSim& s0, ShapeSim& s1, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction, + Sc::ElementInteractionMarker* interactionMarker, bool isTriggerPair); + + private: + ElementSimInteraction* createRbElementInteraction(ShapeSim& s0, ShapeSim& s1, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction, + Sc::ElementInteractionMarker* interactionMarker); + + void releaseElementPair(ElementSimInteraction* pair, PxU32 flags, const PxU32 ccdPass, bool removeFromDirtyList, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + void lostTouchReports(ShapeInteraction* pair, PxU32 flags, const PxU32 ccdPass, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + + ShapeInteraction* createShapeInteraction(ShapeSim& s0, ShapeSim& s1, PxPairFlags pairFlags, PxsContactManager* contactManager, Sc::ShapeInteraction* shapeInteraction); + TriggerInteraction* createTriggerInteraction(ShapeSim& s0, ShapeSim& s1, PxPairFlags triggerFlags); + ElementInteractionMarker* createElementInteractionMarker(ElementSim& e0, ElementSim& e1, ElementInteractionMarker* marker); + + //------------- Filtering ------------- + + ElementSimInteraction* refilterInteraction(ElementSimInteraction* pair, const PxFilterInfo* filterInfo, bool removeFromDirtyList, PxsContactManagerOutputIterator& outputs, + bool useAdaptiveForce); + //------------------------------------- + + ElementSimInteraction* convert(ElementSimInteraction* pair, InteractionType::Enum type, PxFilterInfo& filterInfo, bool removeFromDirtyList, PxsContactManagerOutputIterator& outputs, + bool useAdaptiveForce); + + ActorPair* findActorPair(ShapeSim* s0, ShapeSim* s1, Ps::IntBool isReportPair); + PX_FORCE_INLINE void destroyActorPairReport(ActorPairReport&); + + Sc::ElementSimInteraction* findInteraction(ElementSim* _element0, ElementSim* _element1); + + // Pooling + Scene& mOwnerScene; + + Ps::Array mContactReportActorPairSet; + Ps::Array mPersistentContactEventPairList; // Pairs which request events which do not get triggered by the sdk and thus need to be tested actively every frame. + // May also contain force threshold event pairs (see mForceThresholdContactEventPairList) + // This list is split in two, the elements in front are for the current frame, the elements at the + // back will get added next frame. + PxU32 mNextFramePersistentContactEventPairIndex; // start index of the pairs which need to get added to the persistent list for next frame + + Ps::Array mForceThresholdContactEventPairList; // Pairs which request force threshold contact events. A pair is only in this list if it does have contact. + // Note: If a pair additionally requests PxPairFlag::eNOTIFY_TOUCH_PERSISTS events, then it + // goes into mPersistentContactEventPairList instead. This allows to share the list index. + // + // data layout: + // ContactActorPair0_ExtraData, ContactShapePair0_0, ContactShapePair0_1, ... ContactShapePair0_N, + // ContactActorPair1_ExtraData, ContactShapePair1_0, ... + // + ContactReportBuffer mContactReportBuffer; // Shape pair information for contact reports + + Ps::CoalescedHashSet mDirtyInteractions; + FilterPairManager* mFilterPairManager; + + // Pools + Ps::Pool mActorPairPool; + Ps::Pool mActorPairReportPool; + Ps::Pool mShapeInteractionPool; + Ps::Pool mTriggerInteractionPool; + Ps::Pool mActorPairContactReportDataPool; + Ps::Pool mInteractionMarkerPool; + + Cm::DelegateTask mMergeProcessedTriggerInteractions; + void* mTmpTriggerProcessingBlock; // temporary memory block to process trigger pairs in parallel + Ps::Mutex mTriggerWriteBackLock; + volatile PxI32 mTriggerPairsToDeactivateCount; + Ps::HashMap mActorPairMap; + + Ps::HashMap mElementSimMap; + + Ps::Mutex mBufferAllocLock; + Ps::Mutex mReportAllocLock; + + friend class Sc::Scene; + friend class Sc::ShapeInteraction; + }; + + struct FilteringContext + { + FilteringContext(const Sc::Scene& scene, FilterPairManager* filterPairManager) : + mFilterShader (scene.getFilterShaderFast()), + mFilterShaderData (scene.getFilterShaderDataFast()), + mFilterShaderDataSize (scene.getFilterShaderDataSizeFast()), + mFilterCallback (scene.getFilterCallbackFast()), + mFilterPairManager (filterPairManager), + mSceneFlags (scene.getPublicFlags()) + { + const PxPairFilteringMode::Enum kineKineFilteringMode = scene.getKineKineFilteringMode(); + if(kineKineFilteringMode==PxPairFilteringMode::eKEEP) + mSceneFlags |= PxSceneFlag::eENABLE_KINEMATIC_PAIRS; + else if(kineKineFilteringMode==PxPairFilteringMode::eSUPPRESS) + mSceneFlags &= ~PxSceneFlag::eENABLE_KINEMATIC_PAIRS; + + const PxPairFilteringMode::Enum staticKineFilteringMode = scene.getStaticKineFilteringMode(); + if(staticKineFilteringMode==PxPairFilteringMode::eKEEP) + mSceneFlags |= PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS; + else if(staticKineFilteringMode==PxPairFilteringMode::eSUPPRESS) + mSceneFlags &= ~PxSceneFlag::eENABLE_KINEMATIC_STATIC_PAIRS; + } + + PxSimulationFilterShader mFilterShader; + const void* mFilterShaderData; + PxU32 mFilterShaderDataSize; + PxSimulationFilterCallback* mFilterCallback; + FilterPairManager* mFilterPairManager; + PxSceneFlags mSceneFlags; + }; + + // helper function to run the filter logic after some hardwired filter criteria have been passed successfully + PxFilterInfo filterRbCollisionPairSecondStage(const FilteringContext& context, const ShapeSim& s0, const ShapeSim& s1, const Sc::BodySim* b0, const Sc::BodySim* b1, PxU32 filterPairIndex, bool runCallbacks); + +} // namespace Sc + + +PX_FORCE_INLINE void Sc::NPhaseCore::preparePersistentContactEventListForNextFrame() +{ + // reports have been processed -> "activate" next frame candidates for persistent contact events + mNextFramePersistentContactEventPairIndex = mPersistentContactEventPairList.size(); +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h b/src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h new file mode 100644 index 000000000..9f3b43add --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SC_OBJECT_ID_TRACKER +#define PX_PHYSICS_SC_OBJECT_ID_TRACKER + +#include "CmPhysXCommon.h" +#include "CmIDPool.h" +#include "CmBitMap.h" +#include "PsUserAllocated.h" + +namespace physx +{ +namespace Sc +{ + // PT: TODO: this has no direct dependency on "Sc". It should really be a "Cm" class. + class ObjectIDTracker : public Ps::UserAllocated + { + PX_NOCOPY(ObjectIDTracker) + public: + ObjectIDTracker() : mPendingReleasedIDs(PX_DEBUG_EXP("objectIDTrackerIDs")) {} + + PX_INLINE PxU32 createID() { return mIDPool.getNewID(); } + PX_INLINE void releaseID(PxU32 id) + { + markIDAsDeleted(id); + mPendingReleasedIDs.pushBack(id); + } + PX_INLINE Ps::IntBool isDeletedID(PxU32 id) const { return mDeletedIDsMap.boundedTest(id); } + PX_FORCE_INLINE PxU32 getDeletedIDCount() const { return mPendingReleasedIDs.size(); } + PX_INLINE void clearDeletedIDMap() { mDeletedIDsMap.clear(); } + PX_INLINE void resizeDeletedIDMap(PxU32 id, PxU32 numIds) + { + mDeletedIDsMap.resize(id); + mPendingReleasedIDs.reserve(numIds); + } + PX_INLINE void processPendingReleases() + { + for(PxU32 i=0; i < mPendingReleasedIDs.size(); i++) + { + mIDPool.freeID(mPendingReleasedIDs[i]); + } + mPendingReleasedIDs.clear(); + } + PX_INLINE void reset() + { + processPendingReleases(); + mPendingReleasedIDs.reset(); + + // Don't free stuff in IDPool, we still need the list of free IDs + + // And it does not seem worth freeing the memory of the bitmap + } + + PX_INLINE PxU32 getMaxID() + { + return mIDPool.getMaxID(); + } + private: + PX_INLINE void markIDAsDeleted(PxU32 id) { PX_ASSERT(!isDeletedID(id)); mDeletedIDsMap.growAndSet(id); } + + + private: + Cm::IDPool mIDPool; + Cm::BitMap mDeletedIDsMap; + Ps::Array mPendingReleasedIDs; // Buffer for released IDs to make sure newly created objects do not re-use these IDs immediately + }; + +} +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp new file mode 100644 index 000000000..7fd2884de --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp @@ -0,0 +1,61 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScPhysics.h" +#include "ScScene.h" +#include "PxvGlobals.h" +#include "PxTolerancesScale.h" + +using namespace physx; + +Sc::Physics* Sc::Physics::mInstance = NULL; +const PxReal Sc::Physics::sWakeCounterOnCreation = 20.0f*0.02f; + +namespace physx +{ + namespace Sc + { + OffsetTable gOffsetTable; + } +} + +Sc::Physics::Physics(const PxTolerancesScale& scale, const PxvOffsetTable& pxvOffsetTable) +: mScale(scale) +{ + mInstance = this; + PxvInit(pxvOffsetTable); +} + + +Sc::Physics::~Physics() +{ + PxvTerm(); + mInstance = 0; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp new file mode 100644 index 000000000..03f6672e7 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp @@ -0,0 +1,125 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScBodyCore.h" +#include "ScStaticCore.h" +#include "ScRigidSim.h" +#include "ScShapeSim.h" +#include "ScScene.h" +#include "ScPhysics.h" + +using namespace physx; +using namespace Sc; + +static ShapeSim& getSimForShape(ShapeCore& core, const ActorSim& actorSim) +{ + const ElementSim* current = actorSim.getElements_(); + while(current) + { + const ShapeSim* sim = static_cast(current); + if(&sim->getCore() == &core) + return *const_cast(sim); + current = current->mNextInActor; + } + + PX_ASSERT(0); // should never fail + return *reinterpret_cast(1); +} + +RigidCore::RigidCore(const PxActorType::Enum type) +: ActorCore(type, PxActorFlag::eVISUALIZATION, PX_DEFAULT_CLIENT, 0) +{ +} + +RigidCore::~RigidCore() +{ +} + +void RigidCore::addShapeToScene(ShapeCore& shapeCore) +{ + RigidSim* sim = getSim(); + PX_ASSERT(sim); + if(!sim) + return; + sim->getScene().addShape(*sim, shapeCore, NULL); +} + +void RigidCore::removeShapeFromScene(ShapeCore& shapeCore, bool wakeOnLostTouch) +{ + RigidSim* sim = getSim(); + if(!sim) + return; + ShapeSim& s = getSimForShape(shapeCore, *sim); + sim->getScene().removeShape(s, wakeOnLostTouch); +} + +void RigidCore::onShapeChange(ShapeCore& shape, ShapeChangeNotifyFlags notifyFlags, PxShapeFlags oldShapeFlags, bool forceBoundsUpdate) +{ + // DS: We pass flags to avoid searching multiple times or exposing RigidSim outside SC, and this form is + // more convenient for the Scb::Shape::syncState method. If we start hitting this a lot we should do it + // a different way, but shape modification after insertion is rare. + + RigidSim* sim = getSim(); + if(!sim) + return; + ShapeSim& s = getSimForShape(shape, *sim); + + if(notifyFlags & ShapeChangeNotifyFlag::eGEOMETRY) + s.onVolumeOrTransformChange(forceBoundsUpdate); + if(notifyFlags & ShapeChangeNotifyFlag::eMATERIAL) + s.onMaterialChange(); + if(notifyFlags & ShapeChangeNotifyFlag::eRESET_FILTERING) + s.onResetFiltering(); + if(notifyFlags & ShapeChangeNotifyFlag::eSHAPE2BODY) + s.onVolumeOrTransformChange(forceBoundsUpdate); + if(notifyFlags & ShapeChangeNotifyFlag::eFILTERDATA) + s.onFilterDataChange(); + if(notifyFlags & ShapeChangeNotifyFlag::eFLAGS) + s.onFlagChange(oldShapeFlags); + if(notifyFlags & ShapeChangeNotifyFlag::eCONTACTOFFSET) + s.onContactOffsetChange(); + if(notifyFlags & ShapeChangeNotifyFlag::eRESTOFFSET) + s.onRestOffsetChange(); +} + +RigidSim* RigidCore::getSim() const +{ + return static_cast(ActorCore::getSim()); +} + +PxU32 RigidCore::getRigidID() const +{ + return static_cast(ActorCore::getSim())->getRigidID(); +} + +PxActor* RigidCore::getPxActor() const +{ + return Ps::pointerOffset(const_cast(this), gOffsetTable.scCore2PxActor[getActorCoreType()]); +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp new file mode 100644 index 000000000..2ffeda1aa --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp @@ -0,0 +1,88 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScScene.h" +#include "ScRigidSim.h" +#include "ScShapeSim.h" +#include "PsFoundation.h" + +using namespace physx; +using namespace Sc; + +/* + PT: + + The BP group ID comes from a Cm::IDPool, and RigidSim is the only class releasing the ID. + + The rigid tracker ID comes from a Cm::IDPool internal to an ObjectIDTracker, and RigidSim + is the only class using it. + + Thus we should: + - promote the BP group ID stuff to a "tracker" object + - use the BP group ID as a rigid ID +*/ + +RigidSim::RigidSim(Scene& scene, RigidCore& core) : ActorSim(scene, core) +{ + mRigidId = scene.getRigidIDTracker().createID(); +} + +RigidSim::~RigidSim() +{ + Scene& scScene = getScene(); + scScene.getRigidIDTracker().releaseID(mRigidId); +} + +void notifyActorInteractionsOfTransformChange(ActorSim& actor); +void RigidSim::notifyShapesOfTransformChange() +{ + if(0) + { + ElementSim* current = getElements_(); + while(current) + { + ShapeSim* sim = static_cast(current); + sim->onVolumeOrTransformChange(false); + current = current->mNextInActor; + } + } + else + { + ElementSim* current = getElements_(); + while(current) + { + ShapeSim* sim = static_cast(current); + sim->markBoundsForUpdate(false); + current = current->mNextInActor; + } + + notifyActorInteractionsOfTransformChange(*this); + } +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h new file mode 100644 index 000000000..830b80973 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h @@ -0,0 +1,63 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_RB_SIM +#define PX_PHYSICS_SCP_RB_SIM + +#include "ScActorSim.h" +#include "ScRigidCore.h" + +namespace physx +{ +namespace Sc +{ + class Scene; + + class RigidSim : public ActorSim + { + public: + RigidSim(Scene&, RigidCore&); + virtual ~RigidSim(); + + PX_FORCE_INLINE RigidCore& getRigidCore() const { return static_cast(mCore); } + + PX_FORCE_INLINE PxU32 getRigidID() const { return mRigidId; } + + void notifyShapesOfTransformChange(); + + PxActor* getPxActor() const { return getRigidCore().getPxActor(); } + private: + PxU32 mRigidId; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp new file mode 100644 index 000000000..fc554f6e9 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp @@ -0,0 +1,6223 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#define NOMINMAX + +#include "common/PxProfileZone.h" +#include "ScPhysics.h" +#include "ScScene.h" +#include "ScClient.h" +#include "BpAABBManager.h" +#include "BpBroadPhase.h" +#include "ScStaticSim.h" +#include "ScConstraintSim.h" +#include "ScConstraintProjectionManager.h" +#include "ScConstraintCore.h" +#include "ScArticulationCore.h" +#include "ScArticulationJointCore.h" +#include "ScMaterialCore.h" +#include "ScArticulationSim.h" +#include "ScArticulationJointSim.h" +#include "PsTime.h" +#include "ScConstraintInteraction.h" +#include "ScTriggerInteraction.h" +#include "ScSimStats.h" +#include "ScTriggerPairs.h" +#include "ScObjectIDTracker.h" +#include "DyArticulation.h" +#include "PxvManager.h" +#include "PxvGlobals.h" +#include "DyContext.h" +#include "PxsCCD.h" +#include "PxsSimpleIslandManager.h" +#include "PxsSimulationController.h" +#include "PxsContext.h" +#include "ScSqBoundsManager.h" +#include "ScElementSim.h" + + +#if defined(__APPLE__) && defined(__POWERPC__) +#include +#endif + +#if PX_SUPPORT_GPU_PHYSX +#include "PxPhysXGpu.h" +#include "PxsKernelWrangler.h" +#include "PxsHeapMemoryAllocator.h" +#include "cudamanager/PxCudaContextManager.h" +#endif + +#include "PxsMemoryManager.h" + +//////////// + +#include "PxvNphaseImplementationContext.h" +#include "ScShapeInteraction.h" +#include "ScElementInteractionMarker.h" +#include "PxsContext.h" + +#include "PxRigidDynamic.h" + +#include "PxvDynamics.h" +#include "DyArticulation.h" +#include "DyFeatherstoneArticulation.h" + +using namespace physx; +using namespace physx::shdfnd; +using namespace physx::Cm; +using namespace physx::Dy; + +// slightly ugly, but we don't want a compile-time dependency on DY_ARTICULATION_MAX_SIZE in the ScScene.h header +namespace physx { +#if PX_SUPPORT_GPU_PHYSX + +PX_PHYSX_GPU_API Bp::BPMemoryAllocator* createGpuMemoryAllocator(); + +#endif + +namespace Sc { + +class LLArticulationPool: public Ps::Pool > +{ +public: + LLArticulationPool() {} +}; + +class LLArticulationRCPool : public Ps::Pool > +{ +public: + LLArticulationRCPool() {} +}; + + +static const char* sFilterShaderDataMemAllocId = "SceneDesc filterShaderData"; + +}} + +void PxcClearContactCacheStats(); +void PxcDisplayContactCacheStats(); + +class ScAfterIntegrationTask : public Cm::Task +{ +public: + static const PxU32 MaxTasks = 256; +private: + const IG::NodeIndex* const mIndices; + const PxU32 mNumBodies; + PxsContext* mContext; + Context* mDynamicsContext; + PxsTransformCache& mCache; + Sc::Scene& mScene; + +public: + + ScAfterIntegrationTask(const IG::NodeIndex* const indices, PxU32 numBodies, PxsContext* context, Context* dynamicsContext, PxsTransformCache& cache, Sc::Scene& scene) : + Cm::Task (scene.getContextId()), + mIndices (indices), + mNumBodies (numBodies), + mContext (context), + mDynamicsContext(dynamicsContext), + mCache (cache), + mScene (scene) + { + } + + virtual void runInternal() + { + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + Sc::BodySim* bpUpdates[MaxTasks]; + Sc::BodySim* ccdBodies[MaxTasks]; + Sc::BodySim* activateBodies[MaxTasks]; + Sc::BodySim* deactivateBodies[MaxTasks]; + PxU32 nbBpUpdates = 0, nbCcdBodies = 0; + + IG::SimpleIslandManager& manager = *mScene.getSimpleIslandManager(); + const IG::IslandSim& islandSim = manager.getAccurateIslandSim(); + Bp::BoundsArray& boundsArray = mScene.getBoundsArray(); + + Sc::BodySim* frozen[MaxTasks], * unfrozen[MaxTasks]; + PxU32 nbFrozen = 0, nbUnfrozen = 0; + PxU32 nbActivated = 0, nbDeactivated = 0; + + for(PxU32 i = 0; i < mNumBodies; i++) + { + PxsRigidBody* rigid = islandSim.getRigidBody(mIndices[i]); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(rigid) - rigidBodyOffset); + //This move to PxgPostSolveWorkerTask for the gpu dynamic + //bodySim->sleepCheck(mDt, mOneOverDt, mEnableStabilization); + + PxsBodyCore& bodyCore = bodySim->getBodyCore().getCore(); + //If we got in this code, then this is an active object this frame. The solver computed the new wakeCounter and we + //commit it at this stage. We need to do it this way to avoid a race condition between the solver and the island gen, where + //the island gen may have deactivated a body while the solver decided to change its wake counter. + bodyCore.wakeCounter = bodyCore.solverWakeCounter; + PxsRigidBody& llBody = bodySim->getLowLevelBody(); + + const Ps::IntBool isFrozen = bodySim->isFrozen(); + if(!isFrozen) + { + bpUpdates[nbBpUpdates++] = bodySim; + + // PT: TODO: remove duplicate "isFrozen" test inside updateCached +// bodySim->updateCached(NULL); + bodySim->updateCached(mCache, boundsArray); + } + + if(llBody.isFreezeThisFrame() && isFrozen) + { + frozen[nbFrozen++] = bodySim; + } + else if(llBody.isUnfreezeThisFrame()) + { + unfrozen[nbUnfrozen++] = bodySim; + } + + if(bodyCore.mFlags & PxRigidBodyFlag::eENABLE_CCD) + ccdBodies[nbCcdBodies++] = bodySim; + + if(llBody.isActivateThisFrame()) + { + PX_ASSERT(!llBody.isDeactivateThisFrame()); + activateBodies[nbActivated++] = bodySim; + } + else if(llBody.isDeactivateThisFrame()) + { + deactivateBodies[nbDeactivated++] = bodySim; + } + llBody.clearAllFrameFlags(); + } + if(nbBpUpdates) + { + mCache.setChangedState(); + boundsArray.setChangedState(); + } + + if(nbBpUpdates>0 || nbFrozen > 0 || nbCcdBodies>0 || nbActivated>0 || nbDeactivated>0) + { + //Write active bodies to changed actor map + mContext->getLock().lock(); + Cm::BitMapPinned& changedAABBMgrHandles = mScene.getAABBManager()->getChangedAABBMgActorHandleMap(); + + for(PxU32 i = 0; i < nbBpUpdates; i++) + { + Sc::ElementSim* current = bpUpdates[i]->getElements_(); + while(current) + { + Sc::ShapeSim* sim = static_cast(current); + // PT: TODO: what's the difference between this test and "isInBroadphase" as used in bodySim->updateCached ? + // PT: Also, shouldn't it be "isInAABBManager" rather than BP ? + if(sim->getFlags()&PxU32(PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) // TODO: need trigger shape here? + changedAABBMgrHandles.growAndSet(sim->getElementID()); + current = current->mNextInActor; + } + } + + Ps::Array& sceneCcdBodies = mScene.getCcdBodies(); + for (PxU32 i = 0; i < nbCcdBodies; i++) + sceneCcdBodies.pushBack(ccdBodies[i]); + + for(PxU32 i=0;iisFrozen()); + frozen[i]->freezeTransforms(&changedAABBMgrHandles); + } + + for(PxU32 i=0;iisFrozen()); + unfrozen[i]->createSqBounds(); + } + + for(PxU32 i = 0; i < nbActivated; ++i) + { + activateBodies[i]->notifyNotReadyForSleeping(); + } + + for(PxU32 i = 0; i < nbDeactivated; ++i) + { + deactivateBodies[i]->notifyReadyForSleeping(); + } + + mContext->getLock().unlock(); + } + } + + virtual const char* getName() const + { + return "ScScene.afterIntegrationTask"; + } + +private: + ScAfterIntegrationTask& operator = (const ScAfterIntegrationTask&); +}; + +class ScSimulationControllerCallback : public PxsSimulationControllerCallback +{ + Sc::Scene* mScene; +public: + + ScSimulationControllerCallback(Sc::Scene* scene) : mScene(scene) + { + } + + virtual void updateScBodyAndShapeSim(PxBaseTask* continuation) + { + PxsContext* contextLL = mScene->getLowLevelContext(); + IG::SimpleIslandManager* islandManager = mScene->getSimpleIslandManager(); + Dy::Context* dynamicContext = mScene->getDynamicsContext(); + + Cm::FlushPool& flushPool = contextLL->getTaskPool(); + + const PxU32 MaxBodiesPerTask = ScAfterIntegrationTask::MaxTasks; + + PxsTransformCache& cache = contextLL->getTransformCache(); + + const IG::IslandSim& islandSim = islandManager->getAccurateIslandSim(); + + /*const*/ PxU32 numBodies = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + const IG::NodeIndex*const nodeIndices = islandSim.getActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + if(1) + { + PxU32 nbShapes = 0; + PxU32 startIdx = 0; + for (PxU32 i = 0; i < numBodies; i++) + { + if (nbShapes >= MaxBodiesPerTask) + { + ScAfterIntegrationTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScAfterIntegrationTask)), ScAfterIntegrationTask(nodeIndices + startIdx, i - startIdx, + contextLL, dynamicContext, cache, *mScene)); + task->setContinuation(continuation); + task->removeReference(); + startIdx = i; + nbShapes = 0; + } + + PxsRigidBody* rigid = islandSim.getRigidBody(nodeIndices[i]); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(rigid) - rigidBodyOffset); + nbShapes += PxMax(1u, bodySim->getNbShapes()); //Always add at least 1 shape in, even if the body has zero shapes because there is still some per-body overhead + } + + if (nbShapes) + { + ScAfterIntegrationTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScAfterIntegrationTask)), ScAfterIntegrationTask(nodeIndices + startIdx, numBodies - startIdx, + contextLL, dynamicContext, cache, *mScene)); + task->setContinuation(continuation); + task->removeReference(); + } + } + else + { + // PT: + const PxU32 numCpuTasks = continuation->getTaskManager()->getCpuDispatcher()->getWorkerCount(); + + PxU32 nbPerTask; + if(numCpuTasks) + { + nbPerTask = numBodies > numCpuTasks ? numBodies / numCpuTasks : numBodies; + } + else + { + nbPerTask = numBodies; + } + + // PT: we need to respect that limit even with a single thread, because of hardcoded buffer limits in ScAfterIntegrationTask. + if(nbPerTask>MaxBodiesPerTask) + nbPerTask = MaxBodiesPerTask; + + PxU32 start = 0; + while(numBodies) + { + const PxU32 nb = numBodies < nbPerTask ? numBodies : nbPerTask; + + ScAfterIntegrationTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScAfterIntegrationTask)), ScAfterIntegrationTask(nodeIndices+start, nb, + contextLL, dynamicContext, cache, *mScene)); + + start += nb; + numBodies -= nb; + + task->setContinuation(continuation); + task->removeReference(); + } + } + } + + virtual PxU32 getNbCcdBodies() + { + return mScene->getCcdBodies().size(); + } +}; + +class PxgUpdateBodyAndShapeStatusTask : public Cm::Task +{ +public: + static const PxU32 MaxTasks = 256; +private: + const IG::NodeIndex* const mNodeIndices; + const PxU32 mNumBodies; + Sc::Scene& mScene; + void** mRigidBodyLL; + PxU32* mActivatedBodies; + PxU32* mDeactivatedBodies; + PxI32& mCCDBodyWriteIndex; + +public: + + PxgUpdateBodyAndShapeStatusTask(const IG::NodeIndex* const indices, PxU32 numBodies, void** rigidBodyLL, PxU32* activatedBodies, PxU32* deactivatedBodies, Sc::Scene& scene, PxI32& ccdBodyWriteIndex) : + Cm::Task (scene.getContextId()), + mNodeIndices (indices), + mNumBodies (numBodies), + mScene (scene), + mRigidBodyLL (rigidBodyLL), + mActivatedBodies (activatedBodies), + mDeactivatedBodies (deactivatedBodies), + mCCDBodyWriteIndex (ccdBodyWriteIndex) + { + } + + virtual void runInternal() + { + IG::SimpleIslandManager& islandManager = *mScene.getSimpleIslandManager(); + const IG::IslandSim& islandSim = islandManager.getAccurateIslandSim(); + + PxU32 nbCcdBodies = 0; + + Ps::Array& sceneCcdBodies = mScene.getCcdBodies(); + Sc::BodySim* ccdBodies[MaxTasks]; + + const size_t bodyOffset = PX_OFFSET_OF_RT(Sc::BodySim, getLowLevelBody()); + + for(PxU32 i=0; i(mRigidBodyLL[nodeIndex]); + + PxsBodyCore* bodyCore = &rigidLL->getCore(); + bodyCore->wakeCounter = bodyCore->solverWakeCounter; + //we can set the frozen/unfrozen flag in GPU, but we have copied the internalflags + //from the solverbodysleepdata to pxsbodycore, so we just need to clear the frozen flag in here + rigidLL->clearAllFrameFlags(); + + PX_ASSERT(mActivatedBodies[nodeIndex] <= 1); + PX_ASSERT(mDeactivatedBodies[nodeIndex] <= 1); + if(mActivatedBodies[nodeIndex]) + { + PX_ASSERT(bodyCore->wakeCounter > 0.0f); + islandManager.activateNode(mNodeIndices[i]); + } + else if(mDeactivatedBodies[nodeIndex]) + { + //KS - the CPU code can reset the wake counter due to lost touches in parallel with the solver, so we need to verify + //that the wakeCounter is still 0 before deactivating the node + if(bodyCore->wakeCounter == 0.0f) + islandManager.deactivateNode(mNodeIndices[i]); + } + + if (bodyCore->mFlags & PxRigidBodyFlag::eENABLE_CCD) + { + PxsRigidBody* rigidBody = islandSim.getRigidBody(mNodeIndices[i]); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(rigidBody) - bodyOffset); + ccdBodies[nbCcdBodies++] = bodySim; + } + } + if(nbCcdBodies > 0) + { + PxI32 startIndex = Ps::atomicAdd(&mCCDBodyWriteIndex, PxI32(nbCcdBodies)) - PxI32(nbCcdBodies); + for(PxU32 a = 0; a < nbCcdBodies; ++a) + { + sceneCcdBodies[startIndex + a] = ccdBodies[a]; + } + } + } + + virtual const char* getName() const + { + return "ScScene.PxgUpdateBodyAndShapeStatusTask"; + } + +private: + PxgUpdateBodyAndShapeStatusTask& operator = (const PxgUpdateBodyAndShapeStatusTask&); +}; + +class PxgSimulationControllerCallback : public PxsSimulationControllerCallback +{ + Sc::Scene* mScene; + PxI32 mCcdBodyWriteIndex; + +public: + PxgSimulationControllerCallback(Sc::Scene* scene) : mScene(scene), mCcdBodyWriteIndex(0) + { + } + + virtual void updateScBodyAndShapeSim(PxBaseTask* continuation) + { + IG::SimpleIslandManager* islandManager = mScene->getSimpleIslandManager(); + PxsSimulationController* simulationController = mScene->getSimulationController(); + PxsContext* contextLL = mScene->getLowLevelContext(); + IG::IslandSim& islandSim = islandManager->getAccurateIslandSim(); + const PxU32 numBodies = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + const IG::NodeIndex*const nodeIndices = islandSim.getActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + PxU32* activatedBodies = simulationController->getActiveBodies(); + PxU32* deactivatedBodies = simulationController->getDeactiveBodies(); + + //PxsRigidBody** rigidBodyLL = simulationController->getRigidBodies(); + void** rigidBodyLL = simulationController->getRigidBodies(); + + Cm::FlushPool& flushPool = contextLL->getTaskPool(); + + Ps::Array& ccdBodies = mScene->getCcdBodies(); + ccdBodies.forceSize_Unsafe(0); + ccdBodies.reserve(numBodies); + ccdBodies.forceSize_Unsafe(numBodies); + + mCcdBodyWriteIndex = 0; + + for(PxU32 i = 0; i < numBodies; i+=PxgUpdateBodyAndShapeStatusTask::MaxTasks) + { + PxgUpdateBodyAndShapeStatusTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(PxgUpdateBodyAndShapeStatusTask)), PxgUpdateBodyAndShapeStatusTask(nodeIndices + i, + PxMin(PxgUpdateBodyAndShapeStatusTask::MaxTasks, numBodies - i), rigidBodyLL, activatedBodies, deactivatedBodies, *mScene, mCcdBodyWriteIndex)); + task->setContinuation(continuation); + task->removeReference(); + } + + PxU32* unfrozenShapeIndices = simulationController->getUnfrozenShapes(); + PxU32* frozenShapeIndices = simulationController->getFrozenShapes(); + const PxU32 nbFrozenShapes = simulationController->getNbFrozenShapes(); + const PxU32 nbUnfrozenShapes = simulationController->getNbUnfrozenShapes(); + + PxsShapeSim** shapeSimsLL = simulationController->getShapeSims(); + + const size_t shapeOffset = PX_OFFSET_OF_RT(Sc::ShapeSim, getLLShapeSim()); + + for(PxU32 i=0; i(reinterpret_cast(shapeLL) - shapeOffset); + shape->destroySqBounds(); + } + + for(PxU32 i=0; i(reinterpret_cast(shapeLL) - shapeOffset); + shape->createSqBounds(); + } + } + + virtual PxU32 getNbCcdBodies() + { + return PxU32(mCcdBodyWriteIndex); + } +}; + +Sc::Scene::Scene(const PxSceneDesc& desc, PxU64 contextID) : + mContextId (contextID), + mActiveBodies (PX_DEBUG_EXP("sceneActiveBodies")), + mActiveKinematicBodyCount (0), + mPointerBlock8Pool (PX_DEBUG_EXP("scenePointerBlock8Pool")), + mPointerBlock16Pool (PX_DEBUG_EXP("scenePointerBlock16Pool")), + mPointerBlock32Pool (PX_DEBUG_EXP("scenePointerBlock32Pool")), + mLLContext (0), + mBodyGravityDirty (true), + mDt (0), + mOneOverDt (0), + mTimeStamp (1), // PT: has to start to 1 to fix determinism bug. I don't know why yet but it works. + mReportShapePairTimeStamp (0), + mTriggerBufferAPI (PX_DEBUG_EXP("sceneTriggerBufferAPI")), + mRemovedShapeCountAtSimStart (0), + mArticulations (PX_DEBUG_EXP("sceneArticulations")), + mBrokenConstraints (PX_DEBUG_EXP("sceneBrokenConstraints")), + mActiveBreakableConstraints (PX_DEBUG_EXP("sceneActiveBreakableConstraints")), + mMemBlock128Pool (PX_DEBUG_EXP("PxsContext ConstraintBlock128Pool")), + mMemBlock256Pool (PX_DEBUG_EXP("PxsContext ConstraintBlock256Pool")), + mMemBlock384Pool (PX_DEBUG_EXP("PxsContext ConstraintBlock384Pool")), + mNPhaseCore (NULL), + mKineKineFilteringMode (desc.kineKineFilteringMode), + mStaticKineFilteringMode (desc.staticKineFilteringMode), + mSleepBodies (PX_DEBUG_EXP("sceneSleepBodies")), + mWokeBodies (PX_DEBUG_EXP("sceneWokeBodies")), + mEnableStabilization (desc.flags & PxSceneFlag::eENABLE_STABILIZATION), + mClients (PX_DEBUG_EXP("sceneClients")), + mActiveActors (PX_DEBUG_EXP("clientActiveActors")), + mFrozenActors (PX_DEBUG_EXP("clientFrozenActors")), + mClientPosePreviewBodies (PX_DEBUG_EXP("clientPosePreviewBodies")), + mClientPosePreviewBuffer (PX_DEBUG_EXP("clientPosePreviewBuffer")), + mSimulationEventCallback (NULL), + mBroadPhaseCallback (NULL), + mInternalFlags (SceneInternalFlag::eSCENE_DEFAULT), + mPublicFlags (desc.flags), + mStaticAnchor (NULL), + mBatchRemoveState (NULL), + mLostTouchPairs (PX_DEBUG_EXP("sceneLostTouchPairs")), + mOutOfBoundsIDs (PX_DEBUG_EXP("sceneOutOfBoundsIds")), + mVisualizationScale (0.0f), + mVisualizationParameterChanged (false), + mNbRigidStatics (0), + mNbRigidDynamics (0), + mSecondPassNarrowPhase (contextID, this, "ScScene.secondPassNarrowPhase"), + mPostNarrowPhase (contextID, this, "ScScene.postNarrowPhase"), + mFinalizationPhase (contextID, this, "ScScene.finalizationPhase"), + mUpdateCCDMultiPass (contextID, this, "ScScene.updateCCDMultiPass"), + mAfterIntegration (contextID, this, "ScScene.afterIntegration"), + mConstraintProjection (contextID, this, "ScScene.constraintProjection"), + mPostSolver (contextID, this, "ScScene.postSolver"), + mSolver (contextID, this, "ScScene.rigidBodySolver"), + mUpdateBodiesAndShapes (contextID, this, "ScScene.updateBodiesAndShapes"), + mUpdateSimulationController (contextID, this, "ScScene.updateSimulationController"), + mUpdateDynamics (contextID, this, "ScScene.updateDynamics"), + mProcessLostContactsTask (contextID, this, "ScScene.processLostContact"), + mProcessLostContactsTask2 (contextID, this, "ScScene.processLostContact2"), + mProcessLostContactsTask3 (contextID, this, "ScScene.processLostContact3"), + mDestroyManagersTask (contextID, this, "ScScene.destroyManagers"), + mLostTouchReportsTask (contextID, this, "ScScene.lostTouchReports"), + mUnregisterInteractionsTask (contextID, this, "ScScene.unregisterInteractions"), + mProcessNarrowPhaseLostTouchTasks(contextID, this, "ScScene.processNpLostTouchTask"), + mProcessNPLostTouchEvents (contextID, this, "ScScene.processNPLostTouchEvents"), + mPostThirdPassIslandGenTask (contextID, this, "ScScene.postThirdPassIslandGenTask"), + mPostIslandGen (contextID, this, "ScScene.postIslandGen"), + mIslandGen (contextID, this, "ScScene.islandGen"), + mPreRigidBodyNarrowPhase (contextID, this, "ScScene.preRigidBodyNarrowPhase"), + mSetEdgesConnectedTask (contextID, this, "ScScene.setEdgesConnectedTask"), + mFetchPatchEventsTask (contextID, this, "ScScene.fetchPatchEventsTask"), + mProcessLostPatchesTask (contextID, this, "ScScene.processLostSolverPatchesTask"), + mRigidBodyNarrowPhase (contextID, this, "ScScene.rigidBodyNarrowPhase"), + mRigidBodyNPhaseUnlock (contextID, this, "ScScene.unblockNarrowPhase"), + mPostBroadPhase (contextID, this, "ScScene.postBroadPhase"), + mPostBroadPhaseCont (contextID, this, "ScScene.postBroadPhaseCont"), + mPostBroadPhase2 (contextID, this, "ScScene.postBroadPhase2"), + mPostBroadPhase3 (contextID, this, "ScScene.postBroadPhase3"), + mPreallocateContactManagers (contextID, this, "ScScene.preallocateContactManagers"), + mIslandInsertion (contextID, this, "ScScene.islandInsertion"), + mRegisterContactManagers (contextID, this, "ScScene.registerContactManagers"), + mRegisterInteractions (contextID, this, "ScScene.registerInteractions"), + mRegisterSceneInteractions (contextID, this, "ScScene.registerSceneInteractions"), + mBroadPhase (contextID, this, "ScScene.broadPhase"), + mAdvanceStep (contextID, this, "ScScene.advanceStep"), + mCollideStep (contextID, this, "ScScene.collideStep"), + mTaskPool (16384), + mContactReportsNeedPostSolverVelocity(false), + mUseGpuRigidBodies (false), + mSimulationStage (SimulationStage::eCOMPLETE), + mTmpConstraintGroupRootBuffer (NULL), + mPosePreviewBodies (PX_DEBUG_EXP("scenePosePreviewBodies")), + mOverlapFilterTaskHead (NULL) +{ + + mCCDPass = 0; + for (int i=0; i < InteractionType::eTRACKED_IN_SCENE_COUNT; ++i) + mActiveInteractionCount[i] = 0; + + mStats = PX_NEW(SimStats); + mConstraintIDTracker = PX_NEW(ObjectIDTracker); + mShapeIDTracker = PX_NEW(ObjectIDTracker); + mRigidIDTracker = PX_NEW(ObjectIDTracker); + mElementIDPool = PX_NEW(ObjectIDTracker); + + mTriggerBufferExtraData = reinterpret_cast(PX_ALLOC(sizeof(TriggerBufferExtraData), "ScScene::TriggerBufferExtraData")); + new(mTriggerBufferExtraData) TriggerBufferExtraData(PX_DEBUG_EXP("ScScene::TriggerPairExtraData")); + + mStaticSimPool = PX_NEW(PreallocatingPool)(64, "StaticSim"); + mBodySimPool = PX_NEW(PreallocatingPool)(64, "BodySim"); + mShapeSimPool = PX_NEW(PreallocatingPool)(64, "ShapeSim"); + mConstraintSimPool = PX_NEW(Ps::Pool)(PX_DEBUG_EXP("ScScene::ConstraintSim")); + mConstraintInteractionPool = PX_NEW(Ps::Pool)(PX_DEBUG_EXP("ScScene::ConstraintInteraction")); + mLLArticulationPool = PX_NEW(LLArticulationPool); + mLLArticulationRCPool = PX_NEW(LLArticulationRCPool); + + mSimStateDataPool = PX_NEW(Ps::Pool)(PX_DEBUG_EXP("ScScene::SimStateData")); + + mClients.pushBack(PX_NEW(Client)()); + mProjectionManager = PX_NEW(ConstraintProjectionManager)(); + + mSqBoundsManager = PX_NEW(SqBoundsManager); + + mTaskManager = physx::PxTaskManager::createTaskManager(Ps::getFoundation().getErrorCallback(), desc.cpuDispatcher, desc.gpuDispatcher); + + for(PxU32 i=0; igetGpuDispatcher() == NULL) + { + shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, + __FILE__, __LINE__, "GPU solver pipeline failed, switching to software"); + + useGpuDynamics = false; + } + else if (!mTaskManager->getGpuDispatcher()->getCudaContextManager()->supportsArchSM30()) + useGpuDynamics = false; + } + + if (desc.broadPhaseType == PxBroadPhaseType::eGPU) + { + useGpuBroadphase = true; + if (mTaskManager->getGpuDispatcher() == NULL) + { + shdfnd::getFoundation().error(PxErrorCode::eDEBUG_WARNING, + __FILE__, __LINE__, "GPU Bp pipeline failed, switching to software"); + + useGpuBroadphase = false; + } + else if (!mTaskManager->getGpuDispatcher()->getCudaContextManager()->supportsArchSM30()) + useGpuBroadphase = false; + } +#endif + + mUseGpuRigidBodies = useGpuBroadphase || useGpuDynamics; + + mLLContext = PX_NEW(PxsContext)(desc, mTaskManager, mTaskPool, contextID); + + if (mLLContext == 0) + { + Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "Failed to create context!"); + return; + } + mLLContext->setMaterialManager(&getMaterialManager()); + + mMemoryManager = NULL; + +#if PX_SUPPORT_GPU_PHYSX + mHeapMemoryAllocationManager = NULL; + mGpuWranglerManagers = NULL; + + if (useGpuBroadphase || useGpuDynamics) + { + mMemoryManager = PxvGetPhysXGpu(true)->createGpuMemoryManager(mLLContext->getTaskManager().getGpuDispatcher(), NULL); + mGpuWranglerManagers = PxvGetPhysXGpu(true)->createGpuKernelWranglerManager(mLLContext->getTaskManager().getGpuDispatcher(), getFoundation().getErrorCallback(),desc.gpuComputeVersion); + mHeapMemoryAllocationManager = PxvGetPhysXGpu(true)->createGpuHeapMemoryAllocatorManager(desc.gpuDynamicsConfig.heapCapacity, mMemoryManager, desc.gpuComputeVersion); + } + else +#endif + { + mMemoryManager = createMemoryManager(); + } + + //Note: broadphase should be independent of AABBManager. MBP uses it to call getBPBounds but it has + //already been passed all bounds in BroadPhase::update() so should use that instead. + if(!useGpuBroadphase) + { + PxBroadPhaseType::Enum broadPhaseType = desc.broadPhaseType; + + if (broadPhaseType == PxBroadPhaseType::eGPU) + broadPhaseType = PxBroadPhaseType::eABP; + + mBP = Bp::BroadPhase::create( + broadPhaseType, + desc.limits.maxNbRegions, + desc.limits.maxNbBroadPhaseOverlaps, + desc.limits.maxNbStaticShapes, + desc.limits.maxNbDynamicShapes, + contextID); + } + else + { +#if PX_SUPPORT_GPU_PHYSX + mBP = PxvGetPhysXGpu(true)->createGpuBroadPhase + ( + mGpuWranglerManagers, + mLLContext->getTaskManager().getGpuDispatcher(), + NULL, + desc.gpuComputeVersion, + desc.gpuDynamicsConfig, + mHeapMemoryAllocationManager); +#endif + } + + //create allocator + Ps::VirtualAllocatorCallback* allocatorCallback = mMemoryManager->createHostMemoryAllocator(desc.gpuComputeVersion); + Ps::VirtualAllocator allocator(allocatorCallback); + + typedef Ps::Array ContactDistArray; + + mBoundsArray = PX_NEW(Bp::BoundsArray)(allocator); + //mBoundsArray = PX_NEW(Bp::BoundsArray); + mContactDistance = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ContactDistArray), PX_DEBUG_EXP("ContactDistance")), ContactDistArray)(allocator); + mHasContactDistanceChanged = false; + + const bool useEnhancedDeterminism = getPublicFlags() & PxSceneFlag::eENABLE_ENHANCED_DETERMINISM; + const bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + mSimpleIslandManager = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(IG::SimpleIslandManager), PX_DEBUG_EXP("SimpleIslandManager")), IG::SimpleIslandManager)(useEnhancedDeterminism, contextID); + + if (!useGpuDynamics) + { + if (desc.solverType == PxSolverType::ePGS) + { + mDynamicsContext = createDynamicsContext + (&mLLContext->getNpMemBlockPool(), mLLContext->getScratchAllocator(), + mLLContext->getTaskPool(), mLLContext->getSimStats(), &mLLContext->getTaskManager(), allocatorCallback, &getMaterialManager(), + &mSimpleIslandManager->getAccurateIslandSim(), contextID, mEnableStabilization, useEnhancedDeterminism, useAdaptiveForce, desc.maxBiasCoefficient, + !!(desc.flags & PxSceneFlag::eENABLE_FRICTION_EVERY_ITERATION)); + } + else + { + mDynamicsContext = createTGSDynamicsContext + (&mLLContext->getNpMemBlockPool(), mLLContext->getScratchAllocator(), + mLLContext->getTaskPool(), mLLContext->getSimStats(), &mLLContext->getTaskManager(), allocatorCallback, &getMaterialManager(), + &mSimpleIslandManager->getAccurateIslandSim(), contextID, mEnableStabilization, useEnhancedDeterminism, useAdaptiveForce, + desc.getTolerancesScale().length); + } + + mLLContext->setNphaseImplementationContext(createNphaseImplementationContext(*mLLContext, &mSimpleIslandManager->getAccurateIslandSim())); + + mSimulationControllerCallback = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ScSimulationControllerCallback), PX_DEBUG_EXP("ScSimulationControllerCallback")), ScSimulationControllerCallback(this)); + mSimulationController = createSimulationController(mSimulationControllerCallback); + + mAABBManager = PX_NEW(Bp::AABBManager)(*mBP, *mBoundsArray, *mContactDistance, + desc.limits.maxNbAggregates, desc.limits.maxNbStaticShapes + desc.limits.maxNbDynamicShapes, allocator, contextID, + desc.kineKineFilteringMode, desc.staticKineFilteringMode); + } + else + { +#if PX_SUPPORT_GPU_PHYSX + mDynamicsContext = PxvGetPhysXGpu(true)->createGpuDynamicsContext(mLLContext->getTaskPool(), mGpuWranglerManagers, mLLContext->getTaskManager().getGpuDispatcher(), NULL, + desc.gpuDynamicsConfig, &mSimpleIslandManager->getAccurateIslandSim(), desc.gpuMaxNumPartitions, mEnableStabilization, useEnhancedDeterminism, useAdaptiveForce, desc.maxBiasCoefficient, desc.gpuComputeVersion, mLLContext->getSimStats(), + mHeapMemoryAllocationManager, !!(desc.flags & PxSceneFlag::eENABLE_FRICTION_EVERY_ITERATION), desc.solverType); + + void* contactStreamBase = NULL; + void* patchStreamBase = NULL; + void* forceAndIndiceStreamBase = NULL; + + mDynamicsContext->getDataStreamBase(contactStreamBase, patchStreamBase, forceAndIndiceStreamBase); + + PxvNphaseImplementationContextUsableAsFallback* cpuNphaseImplementation = createNphaseImplementationContext(*mLLContext, &mSimpleIslandManager->getAccurateIslandSim()); + mLLContext->setNphaseFallbackImplementationContext(cpuNphaseImplementation); + + PxvNphaseImplementationContext* gpuNphaseImplementation = PxvGetPhysXGpu(true)->createGpuNphaseImplementationContext(*mLLContext, mGpuWranglerManagers, cpuNphaseImplementation, desc.gpuDynamicsConfig, contactStreamBase, patchStreamBase, + forceAndIndiceStreamBase, getBoundsArray().getBounds(), &mSimpleIslandManager->getAccurateIslandSim(), mDynamicsContext, desc.gpuComputeVersion, mHeapMemoryAllocationManager); + + mSimulationControllerCallback = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(PxgSimulationControllerCallback), PX_DEBUG_EXP("PxgSimulationControllerCallback")), PxgSimulationControllerCallback(this)); + + mSimulationController = PxvGetPhysXGpu(true)->createGpuSimulationController(mGpuWranglerManagers, mLLContext->getTaskManager().getGpuDispatcher(), NULL, + mDynamicsContext, gpuNphaseImplementation, mBP, useGpuBroadphase, mSimpleIslandManager, mSimulationControllerCallback, desc.gpuComputeVersion, mHeapMemoryAllocationManager); + + mSimulationController->setBounds(mBoundsArray); + mDynamicsContext->setSimulationController(mSimulationController); + + mLLContext->setNphaseImplementationContext(gpuNphaseImplementation); + + mLLContext->mContactStreamPool = &mDynamicsContext->getContactStreamPool(); + mLLContext->mPatchStreamPool = &mDynamicsContext->getPatchStreamPool(); + mLLContext->mForceAndIndiceStreamPool = &mDynamicsContext->getForceStreamPool(); + + Ps::VirtualAllocator tAllocator(mHeapMemoryAllocationManager->mMappedMemoryAllocators); + + mAABBManager = PX_NEW(Bp::AABBManager)(*mBP, *mBoundsArray, *mContactDistance, + desc.limits.maxNbAggregates, desc.limits.maxNbStaticShapes + desc.limits.maxNbDynamicShapes, tAllocator, contextID, + desc.kineKineFilteringMode, desc.staticKineFilteringMode); +#endif + } + + //Construct the bitmap of updated actors required as input to the broadphase update + if(desc.limits.maxNbBodies) + { + // PT: TODO: revisit this. Why do we handle the added/removed and updated bitmaps entirely differently, in different places? And what is this weird formula here? + mAABBManager->getChangedAABBMgActorHandleMap().resize((2*desc.limits.maxNbBodies + 256) & ~255); + } + + //mLLContext->createTransformCache(mDynamicsContext->getAllocatorCallback()); + + mLLContext->createTransformCache(*allocatorCallback); + mLLContext->setContactDistance(mContactDistance); + + mCCDContext = physx::PxsCCDContext::create(mLLContext, mDynamicsContext->getThresholdStream(), *mLLContext->getNphaseImplementationContext()); + + setSolverBatchSize(desc.solverBatchSize); + mDynamicsContext->setFrictionOffsetThreshold(desc.frictionOffsetThreshold); + mDynamicsContext->setCCDSeparationThreshold(desc.ccdMaxSeparation); + mDynamicsContext->setSolverOffsetSlop(desc.solverOffsetSlop); + + const PxTolerancesScale& scale = Physics::getInstance().getTolerancesScale(); + mDynamicsContext->setCorrelationDistance(0.025f * scale.length); + mLLContext->setMeshContactMargin(0.01f * scale.length); + mLLContext->setToleranceLength(scale.length); + + // the original descriptor uses + // bounce iff impact velocity > threshold + // but LL use + // bounce iff separation velocity < -threshold + // hence we negate here. + + mDynamicsContext->setBounceThreshold(-desc.bounceThresholdVelocity); + + StaticCore* anchorCore = PX_NEW(StaticCore)(PxTransform(PxIdentity)); + + mStaticAnchor = mStaticSimPool->construct(*this, *anchorCore); + + mNPhaseCore = PX_NEW(NPhaseCore)(*this, desc); + + initDominanceMatrix(); + +// DeterminismDebugger::begin(); + + mWokeBodyListValid = true; + mSleepBodyListValid = true; + + //load from desc: + setLimits(desc.limits); + + // Create broad phase + setBroadPhaseCallback(desc.broadPhaseCallback); + + setGravity(desc.gravity); + + setFrictionType(desc.frictionType); + + setPCM(desc.flags & PxSceneFlag::eENABLE_PCM); + + setContactCache(!(desc.flags & PxSceneFlag::eDISABLE_CONTACT_CACHE)); + setSimulationEventCallback(desc.simulationEventCallback); + setContactModifyCallback(desc.contactModifyCallback); + setCCDContactModifyCallback(desc.ccdContactModifyCallback); + setCCDMaxPasses(desc.ccdMaxPasses); + PX_ASSERT(mNPhaseCore); // refactor paranoia + + PX_ASSERT( ((desc.filterShaderData) && (desc.filterShaderDataSize > 0)) || + (!(desc.filterShaderData) && (desc.filterShaderDataSize == 0)) ); + if (desc.filterShaderData) + { + mFilterShaderData = PX_ALLOC(desc.filterShaderDataSize, sFilterShaderDataMemAllocId); + PxMemCopy(mFilterShaderData, desc.filterShaderData, desc.filterShaderDataSize); + mFilterShaderDataSize = desc.filterShaderDataSize; + mFilterShaderDataCapacity = desc.filterShaderDataSize; + } + else + { + mFilterShaderData = NULL; + mFilterShaderDataSize = 0; + mFilterShaderDataCapacity = 0; + } + mFilterShader = desc.filterShader; + mFilterCallback = desc.filterCallback; +} + +void Sc::Scene::release() +{ + // TODO: PT: check virtual stuff + + mTimeStamp++; + + //collisionSpace.purgeAllPairs(); + + //purgePairs(); + //releaseTagData(); + + // We know release all the shapes before the collision space + //collisionSpace.deleteAllShapes(); + + //collisionSpace.release(); + + //DeterminismDebugger::end(); + + ///clear broken constraint list: + clearBrokenConstraintBuffer(); + + PX_DELETE_AND_RESET(mNPhaseCore); + + PX_FREE_AND_RESET(mFilterShaderData); + + if (mStaticAnchor) + { + StaticCore& core = mStaticAnchor->getStaticCore(); + mStaticSimPool->destroy(mStaticAnchor); + delete &core; + } + + // Free object IDs and the deleted object id map + postReportsCleanup(); + + //before the task manager + if (mLLContext) + { + if(mLLContext->getNphaseFallbackImplementationContext()) + { + mLLContext->getNphaseFallbackImplementationContext()->destroy(); + mLLContext->setNphaseFallbackImplementationContext(NULL); + } + + if(mLLContext->getNphaseImplementationContext()) + { + mLLContext->getNphaseImplementationContext()->destroy(); + mLLContext->setNphaseImplementationContext(NULL); + } + } + + PX_DELETE_AND_RESET(mProjectionManager); + PX_DELETE_AND_RESET(mSqBoundsManager); + PX_DELETE_AND_RESET(mBoundsArray); + + for(PxU32 i=0;i~TriggerBufferExtraData(); + PX_FREE(mTriggerBufferExtraData); + + PX_DELETE(mElementIDPool); + PX_DELETE(mRigidIDTracker); + PX_DELETE(mShapeIDTracker); + PX_DELETE(mConstraintIDTracker); + PX_DELETE(mStats); + + mAABBManager->destroy(); + + mBP->destroy(); + + mSimulationControllerCallback->~PxsSimulationControllerCallback(); + PX_FREE(mSimulationControllerCallback); + mSimulationController->~PxsSimulationController(); + PX_FREE(mSimulationController); + + mDynamicsContext->destroy(); + + mCCDContext->destroy(); + + mSimpleIslandManager->~SimpleIslandManager(); + PX_FREE(mSimpleIslandManager); + +#if PX_SUPPORT_GPU_PHYSX + if (mGpuWranglerManagers) + { + mGpuWranglerManagers->~PxsKernelWranglerManager(); + PX_FREE(mGpuWranglerManagers); + mGpuWranglerManagers = NULL; + } + + if (mHeapMemoryAllocationManager) + { + mHeapMemoryAllocationManager->~PxsHeapMemoryAllocatorManager(); + PX_FREE(mHeapMemoryAllocationManager); + mHeapMemoryAllocationManager = NULL; + } +#endif + + if (mTaskManager) + mTaskManager->release(); + + if (mLLContext) + { + PX_DELETE(mLLContext); + mLLContext = NULL; + } + + mContactDistance->~Array(); + PX_FREE(mContactDistance); + + + if (mMemoryManager) + { + mMemoryManager->~PxsMemoryManager(); + PX_FREE(mMemoryManager); + mMemoryManager = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::preAllocate(PxU32 nbStatics, PxU32 nbBodies, PxU32 nbStaticShapes, PxU32 nbDynamicShapes) +{ + // PT: TODO: this is only used for my addActors benchmark for now. Pre-allocate more arrays here. + + mActiveBodies.reserve(PxMax(64,nbBodies)); + + mStaticSimPool->preAllocate(nbStatics); + + mBodySimPool->preAllocate(nbBodies); + + mShapeSimPool->preAllocate(nbStaticShapes + nbDynamicShapes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::addToActiveBodyList(BodySim& body) +{ + PX_ASSERT(body.getActiveListIndex() >= SC_NOT_IN_ACTIVE_LIST_INDEX); + + // Sort: kinematic before dynamic + const PxU32 size = mActiveBodies.size(); + BodyCore* appendedBodyCore = &body.getBodyCore(); // PT: by default we append the incoming body... + PxU32 incomingBodyActiveListIndex = size; // PT: ...at the end of the current array. + + if(body.isKinematic()) // PT: Except if incoming body is kinematic, in which case: + { + const PxU32 nbKinematics = mActiveKinematicBodyCount++; // PT: - we increase their number + if(nbKinematics != size) // PT: - if there's at least one dynamic in the array... + { + PX_ASSERT(appendedBodyCore != mActiveBodies[nbKinematics]); + appendedBodyCore = mActiveBodies[nbKinematics]; // PT: ...then we grab the first dynamic after the kinematics... + appendedBodyCore->getSim()->setActiveListIndex(size); // PT: ...and we move that one back to the end of the array... + + mActiveBodies[nbKinematics] = &body.getBodyCore(); // PT: ...while the incoming kine replaces the dynamic we moved out. + incomingBodyActiveListIndex = nbKinematics; // PT: ...thus the incoming kine's index is the prev #kines. + } + } + // for active compound rigids add to separate array, so we dont have to traverse all active actors + if(body.readInternalFlag(BodySim::BF_IS_COMPOUND_RIGID)) + { + PX_ASSERT(body.getActiveCompoundListIndex() >= SC_NOT_IN_ACTIVE_LIST_INDEX); + const PxU32 compoundIndex = mActiveCompoundBodies.size(); + mActiveCompoundBodies.pushBack(appendedBodyCore); + body.setActiveCompoundListIndex(compoundIndex); + } + body.setActiveListIndex(incomingBodyActiveListIndex); // PT: will be 'size' or 'nbKinematics' + mActiveBodies.pushBack(appendedBodyCore); // PT: will be the incoming object or the first dynamic we moved out. +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::addToActiveCompoundBodyList(BodySim& body) +{ + PX_ASSERT(body.readInternalFlag(BodySim::BF_IS_COMPOUND_RIGID)); + + BodyCore* appendedBodyCore = &body.getBodyCore(); + + PX_ASSERT(body.getActiveCompoundListIndex() >= SC_NOT_IN_ACTIVE_LIST_INDEX); + const PxU32 compoundIndex = mActiveCompoundBodies.size(); + mActiveCompoundBodies.pushBack(appendedBodyCore); + body.setActiveCompoundListIndex(compoundIndex); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::removeFromActiveCompoundBodyList(BodySim& body) +{ + PxU32 removedCompoundIndex = body.getActiveCompoundListIndex(); + PX_ASSERT(removedCompoundIndex < SC_NOT_IN_ACTIVE_LIST_INDEX); + body.setActiveCompoundListIndex(SC_NOT_IN_ACTIVE_LIST_INDEX); + + const PxU32 newCompoundSize = mActiveCompoundBodies.size() - 1; + + if(removedCompoundIndex!=newCompoundSize) + { + Sc::BodyCore* lastBody = mActiveCompoundBodies[newCompoundSize]; + mActiveCompoundBodies[removedCompoundIndex] = lastBody; + lastBody->getSim()->setActiveListIndex(removedCompoundIndex); + } + mActiveCompoundBodies.forceSize_Unsafe(newCompoundSize); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::removeFromActiveBodyList(BodySim& body) +{ + PxU32 removedIndex = body.getActiveListIndex(); + PX_ASSERT(removedIndex < SC_NOT_IN_ACTIVE_LIST_INDEX); + PX_ASSERT(mActiveBodies[removedIndex]==&body.getBodyCore()); + body.setActiveListIndex(SC_NOT_IN_ACTIVE_LIST_INDEX); + + const PxU32 newSize = mActiveBodies.size() - 1; + + // Sort: kinematic before dynamic + if(removedIndex < mActiveKinematicBodyCount) // PT: same as 'body.isKinematic()' but without accessing the Core data + { + PX_ASSERT(mActiveKinematicBodyCount); + PX_ASSERT(body.isKinematic()); + const PxU32 swapIndex = --mActiveKinematicBodyCount; + if(newSize != swapIndex // PT: equal if the array only contains kinematics + && removedIndex < swapIndex) // PT: i.e. "if we don't remove the last kinematic" + { + BodyCore* swapBody = mActiveBodies[swapIndex]; + swapBody->getSim()->setActiveListIndex(removedIndex); + mActiveBodies[removedIndex] = swapBody; + removedIndex = swapIndex; + } + } + + // for active compound rigids add to separate array, so we dont have to traverse all active actors + // A.B. TODO we should handle kinematic switch, no need to hold kinematic rigids in compound list + if(body.readInternalFlag(BodySim::BF_IS_COMPOUND_RIGID)) + { + PxU32 removedCompoundIndex = body.getActiveCompoundListIndex(); + PX_ASSERT(removedCompoundIndex < SC_NOT_IN_ACTIVE_LIST_INDEX); + body.setActiveCompoundListIndex(SC_NOT_IN_ACTIVE_LIST_INDEX); + + const PxU32 newCompoundSize = mActiveCompoundBodies.size() - 1; + + if(removedCompoundIndex!=newCompoundSize) + { + Sc::BodyCore* lastBody = mActiveCompoundBodies[newCompoundSize]; + mActiveCompoundBodies[removedCompoundIndex] = lastBody; + lastBody->getSim()->setActiveCompoundListIndex(removedCompoundIndex); + } + mActiveCompoundBodies.forceSize_Unsafe(newCompoundSize); + } + + if(removedIndex!=newSize) + { + Sc::BodyCore* lastBody = mActiveBodies[newSize]; + mActiveBodies[removedIndex] = lastBody; + lastBody->getSim()->setActiveListIndex(removedIndex); + } + mActiveBodies.forceSize_Unsafe(newSize); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::swapInActiveBodyList(BodySim& body) +{ + const PxU32 activeListIndex = body.getActiveListIndex(); + PX_ASSERT(activeListIndex < SC_NOT_IN_ACTIVE_LIST_INDEX); + + PxU32 swapIndex; + PxU32 newActiveKinematicBodyCount; + if(activeListIndex < mActiveKinematicBodyCount) + { + // kinematic -> dynamic + PX_ASSERT(!body.isKinematic()); // the corresponding flag gets switched before this call + PX_ASSERT(mActiveKinematicBodyCount > 0); // there has to be at least one kinematic body + + swapIndex = mActiveKinematicBodyCount - 1; + newActiveKinematicBodyCount = swapIndex; + } + else + { + // dynamic -> kinematic + PX_ASSERT(body.isKinematic()); // the corresponding flag gets switched before this call + PX_ASSERT(mActiveKinematicBodyCount < mActiveBodies.size()); // there has to be at least one dynamic body + + swapIndex = mActiveKinematicBodyCount; + newActiveKinematicBodyCount = swapIndex + 1; + } + + BodyCore*& swapBodyRef = mActiveBodies[swapIndex]; + body.setActiveListIndex(swapIndex); + BodyCore* swapBody = swapBodyRef; + swapBodyRef = &body.getBodyCore(); + + swapBody->getSim()->setActiveListIndex(activeListIndex); + mActiveBodies[activeListIndex] = swapBody; + + mActiveKinematicBodyCount = newActiveKinematicBodyCount; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::registerInteraction(Interaction* interaction, bool active) +{ + const InteractionType::Enum type = interaction->getType(); + const PxU32 sceneArrayIndex = mInteractions[type].size(); + interaction->setInteractionId(sceneArrayIndex); + if(mInteractions[type].capacity()==0) + mInteractions[type].reserve(64); + + mInteractions[type].pushBack(interaction); + if (active) + { + if (sceneArrayIndex > mActiveInteractionCount[type]) + swapInteractionArrayIndices(sceneArrayIndex, mActiveInteractionCount[type], type); + mActiveInteractionCount[type]++; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::unregisterInteraction(Interaction* interaction) +{ + const InteractionType::Enum type = interaction->getType(); + const PxU32 sceneArrayIndex = interaction->getInteractionId(); + mInteractions[type].replaceWithLast(sceneArrayIndex); + interaction->setInteractionId(PX_INVALID_INTERACTION_SCENE_ID); + if (sceneArrayIndexsetInteractionId(sceneArrayIndex); + if (sceneArrayIndex& interArray = mInteractions[type]; + Interaction* interaction1 = interArray[id1]; + Interaction* interaction2 = interArray[id2]; + interArray[id1] = interaction2; + interArray[id2] = interaction1; + interaction1->setInteractionId(id2); + interaction2->setInteractionId(id1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::notifyInteractionActivated(Interaction* interaction) +{ + PX_ASSERT((interaction->getType() == InteractionType::eOVERLAP) || (interaction->getType() == InteractionType::eTRIGGER)); + PX_ASSERT(interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE)); + PX_ASSERT(interaction->getInteractionId() != PX_INVALID_INTERACTION_SCENE_ID); + + const InteractionType::Enum type = interaction->getType(); + + PX_ASSERT(interaction->getInteractionId() >= mActiveInteractionCount[type]); + + if (mActiveInteractionCount[type] < mInteractions[type].size()) + swapInteractionArrayIndices(mActiveInteractionCount[type], interaction->getInteractionId(), type); + mActiveInteractionCount[type]++; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::notifyInteractionDeactivated(Interaction* interaction) +{ + PX_ASSERT((interaction->getType() == InteractionType::eOVERLAP) || (interaction->getType() == InteractionType::eTRIGGER)); + PX_ASSERT(!interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE)); + PX_ASSERT(interaction->getInteractionId() != PX_INVALID_INTERACTION_SCENE_ID); + + const InteractionType::Enum type = interaction->getType(); + PX_ASSERT(interaction->getInteractionId() < mActiveInteractionCount[type]); + + if (mActiveInteractionCount[type] > 1) + swapInteractionArrayIndices(mActiveInteractionCount[type]-1, interaction->getInteractionId(), type); + mActiveInteractionCount[type]--; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void** Sc::Scene::allocatePointerBlock(PxU32 size) +{ + PX_ASSERT(size>32 || size == 32 || size == 16 || size == 8); + void* ptr; + if(size==8) + ptr = mPointerBlock8Pool.construct(); + else if(size == 16) + ptr = mPointerBlock16Pool.construct(); + else if(size == 32) + ptr = mPointerBlock32Pool.construct(); + else + ptr = PX_ALLOC(size * sizeof(void*), "void*"); + + return reinterpret_cast(ptr); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::deallocatePointerBlock(void** block, PxU32 size) +{ + PX_ASSERT(size>32 || size == 32 || size == 16 || size == 8); + if(size==8) + mPointerBlock8Pool.destroy(reinterpret_cast(block)); + else if(size == 16) + mPointerBlock16Pool.destroy(reinterpret_cast(block)); + else if(size == 32) + mPointerBlock32Pool.destroy(reinterpret_cast(block)); + else + return PX_FREE(block); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +PxBroadPhaseType::Enum Sc::Scene::getBroadPhaseType() const +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->getType(); +} + +bool Sc::Scene::getBroadPhaseCaps(PxBroadPhaseCaps& caps) const +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->getCaps(caps); +} + +PxU32 Sc::Scene::getNbBroadPhaseRegions() const +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->getNbRegions(); +} + +PxU32 Sc::Scene::getBroadPhaseRegions(PxBroadPhaseRegionInfo* userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->getRegions(userBuffer, bufferSize, startIndex); +} + +PxU32 Sc::Scene::addBroadPhaseRegion(const PxBroadPhaseRegion& region, bool populateRegion) +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->addRegion(region, populateRegion); +} + +bool Sc::Scene::removeBroadPhaseRegion(PxU32 handle) +{ + Bp::BroadPhase* bp = mAABBManager->getBroadPhase(); + return bp->removeRegion(handle); +} + +void** Sc::Scene::getOutOfBoundsAggregates() +{ + PxU32 dummy; + return mAABBManager->getOutOfBoundsAggregates(dummy); +} + +PxU32 Sc::Scene::getNbOutOfBoundsAggregates() +{ + PxU32 val; + mAABBManager->getOutOfBoundsAggregates(val); + return val; +} + +void Sc::Scene::clearOutOfBoundsAggregates() +{ + mAABBManager->clearOutOfBoundsAggregates(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::setFilterShaderData(const void* data, PxU32 dataSize) +{ + PX_UNUSED(sFilterShaderDataMemAllocId); + + if (data) + { + PX_ASSERT(dataSize > 0); + + void* buffer; + + if (dataSize <= mFilterShaderDataCapacity) + buffer = mFilterShaderData; + else + { + buffer = PX_ALLOC(dataSize, sFilterShaderDataMemAllocId); + if (buffer) + { + mFilterShaderDataCapacity = dataSize; + if (mFilterShaderData) + PX_FREE(mFilterShaderData); + } + else + { + Ps::getFoundation().error(PxErrorCode::eOUT_OF_MEMORY, __FILE__, __LINE__, "Failed to allocate memory for filter shader data!"); + return; + } + } + + PxMemCopy(buffer, data, dataSize); + mFilterShaderData = buffer; + mFilterShaderDataSize = dataSize; + } + else + { + PX_ASSERT(dataSize == 0); + + if (mFilterShaderData) + PX_FREE_AND_RESET(mFilterShaderData); + mFilterShaderDataSize = 0; + mFilterShaderDataCapacity = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Cm::RenderBuffer& Sc::Scene::getRenderBuffer() +{ + return mLLContext->getRenderBuffer(); +} + +void Sc::Scene::prepareCollide() +{ + mReportShapePairTimeStamp++; // deleted actors/shapes should get separate pair entries in contact reports + mContactReportsNeedPostSolverVelocity = false; + + mRemovedShapeCountAtSimStart = mShapeIDTracker->getDeletedIDCount(); + + getRenderBuffer().clear(); + + ///clear broken constraint list: + clearBrokenConstraintBuffer(); + + updateFromVisualizationParameters(); + + visualizeStartStep(); + +#ifdef DUMP_PROFILER + dumpProfiler(this); +#endif + + PxcClearContactCacheStats(); +} + +void Sc::Scene::simulate(PxReal timeStep, PxBaseTask* continuation) +{ + if(timeStep != 0.0f) + { + mDt = timeStep; + mOneOverDt = 0.0f < mDt ? 1.0f/mDt : 0.0f; + + mAdvanceStep.setContinuation(continuation); + + prepareCollide(); + stepSetupCollide(&mAdvanceStep); + + mCollideStep.setContinuation(&mAdvanceStep); + + mAdvanceStep.removeReference(); + mCollideStep.removeReference(); + } +} + +void Sc::Scene::advance(PxReal timeStep, PxBaseTask* continuation) +{ + if(timeStep != 0.0f) + { + mDt = timeStep; + mOneOverDt = 0.0f < mDt ? 1.0f/mDt : 0.0f; + + mAdvanceStep.setContinuation(continuation); + + stepSetupSolve(&mAdvanceStep); + + + mAdvanceStep.removeReference(); + } +} + +void Sc::Scene::setBounceThresholdVelocity(const PxReal t) +{ + mDynamicsContext->setBounceThreshold(-t); +} + +PxReal Sc::Scene::getBounceThresholdVelocity() const +{ + return -mDynamicsContext->getBounceThreshold(); +} + +void Sc::Scene::collide(PxReal timeStep, PxBaseTask* continuation) +{ + mDt = timeStep; + + prepareCollide(); + stepSetupCollide(continuation); + + mLLContext->beginUpdate(); + + mCollideStep.setContinuation(continuation); + mCollideStep.removeReference(); +} + +void Sc::Scene::setFrictionType(PxFrictionType::Enum model) +{ + mDynamicsContext->setFrictionType(model); +} + +PxFrictionType::Enum Sc::Scene::getFrictionType() const +{ + return mDynamicsContext->getFrictionType(); +} + +void Sc::Scene::setPCM(bool enabled) +{ + mLLContext->setPCM(enabled); +} + +void Sc::Scene::setContactCache(bool enabled) +{ + mLLContext->setContactCache(enabled); +} + +void Sc::Scene::endSimulation() +{ + // Handle user contact filtering + // Note: Do this before the contact callbacks get fired since the filter callback might + // trigger contact reports (touch lost due to re-filtering) + + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + mNPhaseCore->fireCustomFilteringCallbacks(outputs, mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE); + + mNPhaseCore->preparePersistentContactEventListForNextFrame(); + + mSimulationController->releaseDeferredArticulationIds(); + + endStep(); // - Update time stamps + + PxcDisplayContactCacheStats(); +} + +void Sc::Scene::flush(bool sendPendingReports) +{ + if (sendPendingReports) + { + fireQueuedContactCallbacks(true); + mNPhaseCore->clearContactReportStream(); + mNPhaseCore->clearContactReportActorPairs(true); + + fireTriggerCallbacks(); + } + else + { + mNPhaseCore->clearContactReportActorPairs(true); // To clear the actor pair set + } + postReportsCleanup(); + mNPhaseCore->freeContactReportStreamMemory(); + + mTriggerBufferAPI.reset(); + mTriggerBufferExtraData->reset(); + + clearBrokenConstraintBuffer(); + mBrokenConstraints.reset(); + + clearSleepWakeBodies(); //!!! If we send out these reports on flush then this would not be necessary + + mClients.shrink(); + + mShapeIDTracker->reset(); + mRigidIDTracker->reset(); + + processLostTouchPairs(); // Processes the lost touch bodies + PX_ASSERT(mLostTouchPairs.size() == 0); + mLostTouchPairs.reset(); + // Does not seem worth deleting the bitmap for the lost touch pair list + + mActiveBodies.shrink(); + + for(PxU32 i=0; i < InteractionType::eTRACKED_IN_SCENE_COUNT; i++) + { + mInteractions[i].shrink(); + } + + //!!! TODO: look into retrieving memory from the NPhaseCore & Broadphase class (all the pools in there etc.) + + mLLContext->getNpMemBlockPool().releaseUnusedBlocks(); +} + +// User callbacks + +void Sc::Scene::setSimulationEventCallback(PxSimulationEventCallback* callback) +{ + if(!mSimulationEventCallback && callback) + { + // if there was no callback before, the sleeping bodies have to be prepared for potential notification events (no shortcut possible anymore) + BodyCore* const* sleepingBodies = mSleepBodies.getEntries(); + for(PxU32 i=0; i < mSleepBodies.size(); i++) + sleepingBodies[i]->getSim()->raiseInternalFlag(BodySim::BF_SLEEP_NOTIFY); + } + + mSimulationEventCallback = callback; +} + +PxSimulationEventCallback* Sc::Scene::getSimulationEventCallback() const +{ + return mSimulationEventCallback; +} + +void Sc::Scene::setContactModifyCallback(PxContactModifyCallback* callback) +{ + mLLContext->setContactModifyCallback(callback); +} + +PxContactModifyCallback* Sc::Scene::getContactModifyCallback() const +{ + return mLLContext->getContactModifyCallback(); +} + +void Sc::Scene::setCCDContactModifyCallback(PxCCDContactModifyCallback* callback) +{ + mCCDContext->setCCDContactModifyCallback(callback); +} + +PxCCDContactModifyCallback* Sc::Scene::getCCDContactModifyCallback() const +{ + return mCCDContext->getCCDContactModifyCallback(); +} + +void Sc::Scene::setCCDMaxPasses(PxU32 ccdMaxPasses) +{ + mCCDContext->setCCDMaxPasses(ccdMaxPasses); +} + +PxU32 Sc::Scene::getCCDMaxPasses() const +{ + return mCCDContext->getCCDMaxPasses(); +} + +void Sc::Scene::setBroadPhaseCallback(PxBroadPhaseCallback* callback) +{ + mBroadPhaseCallback = callback; +} + +PxBroadPhaseCallback* Sc::Scene::getBroadPhaseCallback() const +{ + return mBroadPhaseCallback; +} + +void Sc::Scene::removeBody(BodySim& body) //this also notifies any connected joints! +{ + ConstraintGroupNode* node = body.getConstraintGroup(); + if (node) + { + //invalidate the constraint group: + //this adds all constraints of the group to the dirty list such that groups get re-generated next frame + getProjectionManager().invalidateGroup(*node, NULL); + } + + BodyCore& core = body.getBodyCore(); + + // Remove from sleepBodies array + mSleepBodies.erase(&core); + PX_ASSERT(!mSleepBodies.contains(&core)); + + // Remove from wokeBodies array + mWokeBodies.erase(&core); + PX_ASSERT(!mWokeBodies.contains(&core)); + + if (body.isActive() && (core.getFlags() & PxRigidBodyFlag::eENABLE_POSE_INTEGRATION_PREVIEW)) + removeFromPosePreviewList(body); + else + PX_ASSERT(!isInPosePreviewList(body)); + + markReleasedBodyIDForLostTouch(body.getRigidID()); +} + +void Sc::Scene::addConstraint(ConstraintCore& constraint, RigidCore* body0, RigidCore* body1) +{ + ConstraintSim* sim = mConstraintSimPool->construct(constraint, body0, body1, *this); + PX_UNUSED(sim); + + mConstraints.insert(&constraint); +} + +void Sc::Scene::removeConstraint(ConstraintCore& constraint) +{ + ConstraintSim* cSim = constraint.getSim(); + + if (cSim) + { + BodySim* b = cSim->getAnyBody(); + ConstraintGroupNode* n = b->getConstraintGroup(); + + if (n) + getProjectionManager().invalidateGroup(*n, cSim); + mConstraintSimPool->destroy(cSim); + } + + mConstraints.erase(&constraint); +} + +void Sc::Scene::addArticulation(ArticulationCore& articulation, BodyCore& root) +{ + ArticulationSim* sim = PX_NEW(ArticulationSim)(articulation, *this, root); + + if (sim && (sim->getLowLevelArticulation() == NULL)) + { + PX_DELETE(sim); + return; + } + mArticulations.insert(&articulation); +} + +void Sc::Scene::removeArticulation(ArticulationCore& articulation) +{ + ArticulationSim* a = articulation.getSim(); + if (a) + PX_DELETE(a); + mArticulations.erase(&articulation); +} + +void Sc::Scene::addArticulationJoint(ArticulationJointCore& joint, BodyCore& parent, BodyCore& child) +{ + ArticulationJointSim* sim = PX_NEW(ArticulationJointSim)(joint, *parent.getSim(), *child.getSim()); + PX_UNUSED(sim); +} + +void Sc::Scene::removeArticulationJoint(ArticulationJointCore& joint) +{ + if (joint.getSim()) + PX_DELETE(joint.getSim()); +} + +void Sc::Scene::addArticulationSimControl(Sc::ArticulationCore& core) +{ + Sc::ArticulationSim* sim = core.getSim(); + if (sim) + mSimulationController->addArticulation(sim->getLowLevelArticulation(), sim->getIslandNodeIndex()); +} + +void Sc::Scene::addBrokenConstraint(Sc::ConstraintCore* c) +{ + PX_ASSERT(mBrokenConstraints.find(c) == mBrokenConstraints.end()); + mBrokenConstraints.pushBack(c); +} + +void Sc::Scene::addActiveBreakableConstraint(Sc::ConstraintSim* c, Sc::ConstraintInteraction* ci) +{ + PX_ASSERT(ci && ci->readInteractionFlag(InteractionFlag::eIS_ACTIVE)); + PX_UNUSED(ci); + PX_ASSERT(!mActiveBreakableConstraints.contains(c)); + PX_ASSERT(!c->isBroken()); + mActiveBreakableConstraints.insert(c); + c->setFlag(ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED); +} + +void Sc::Scene::removeActiveBreakableConstraint(Sc::ConstraintSim* c) +{ + const bool exists = mActiveBreakableConstraints.erase(c); + PX_ASSERT(exists); + PX_UNUSED(exists); + c->clearFlag(ConstraintSim::eCHECK_MAX_FORCE_EXCEEDED); +} + +void* Sc::Scene::allocateConstraintBlock(PxU32 size) +{ + if(size<=128) + return mMemBlock128Pool.construct(); + else if(size<=256) + return mMemBlock256Pool.construct(); + else if(size<=384) + return mMemBlock384Pool.construct(); + else + return PX_ALLOC(size, "ConstraintBlock"); +} + +void Sc::Scene::deallocateConstraintBlock(void* ptr, PxU32 size) +{ + if(size<=128) + mMemBlock128Pool.destroy(reinterpret_cast(ptr)); + else if(size<=256) + mMemBlock256Pool.destroy(reinterpret_cast(ptr)); + else if(size<=384) + mMemBlock384Pool.destroy(reinterpret_cast(ptr)); + else + PX_FREE(ptr); +} + +//int testAxisConstraint(Sc::Scene& scene); +//int testCasts(Shape* shape); +//int testCasts(Shape& shape); + +/*-------------------------------*\ +| Adam's explanation of the RB solver: +| This is a novel idea of mine, +| a combination of ideas on +| Milenkovic's Optimization +| Based Animation, and Trinkle's +| time stepping schemes. +| +| A time step goes like this: +| +| Taking no substeps: +| 0) Compute contact points. +| 1) Update external forces. This may include friction. +| 2) Integrate external forces to current velocities. +| 3) Solve for impulses at contacts which will prevent +| interpenetration at next timestep given some +| velocity integration scheme. +| 4) Use the integration scheme on velocity to +| reach the next state. Here we should not have any +| interpenetration at the old contacts, but perhaps +| at new contacts. If interpenetrating at new contacts, +| just add these to the contact list; no need to repeat +| the time step, because the scheme will get rid of the +| penetration by the next step. +| +| +| Advantages: +| + Large steps, LOD realism. +| + very simple. +| +\*-------------------------------*/ + +void Sc::Scene::advanceStep(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.solveQueueTasks", getContextId()); + + if (mDt != 0.0f) + { + mFinalizationPhase.addDependent(*continuation); + mFinalizationPhase.removeReference(); + + if (mPublicFlags & PxSceneFlag::eENABLE_CCD) + { + mUpdateCCDMultiPass.setContinuation(&mFinalizationPhase); + mAfterIntegration.setContinuation(&mUpdateCCDMultiPass); + mUpdateCCDMultiPass.removeReference(); + } + else + { + mAfterIntegration.setContinuation(&mFinalizationPhase); + } + + mPostSolver.setContinuation(&mAfterIntegration); + mUpdateSimulationController.setContinuation(&mPostSolver); + mUpdateDynamics.setContinuation(&mUpdateSimulationController); + mUpdateBodiesAndShapes.setContinuation(&mUpdateDynamics); + mSolver.setContinuation(&mUpdateBodiesAndShapes); + mPostIslandGen.setContinuation(&mSolver); + mIslandGen.setContinuation(&mPostIslandGen); + mPostNarrowPhase.addDependent(mIslandGen); + mPostNarrowPhase.removeReference(); + + mSecondPassNarrowPhase.setContinuation(&mPostNarrowPhase); + + mFinalizationPhase.removeReference(); + mAfterIntegration.removeReference(); + mPostSolver.removeReference(); + mUpdateSimulationController.removeReference(); + mUpdateDynamics.removeReference(); + mUpdateBodiesAndShapes.removeReference(); + mSolver.removeReference(); + mPostIslandGen.removeReference(); + mIslandGen.removeReference(); + mPostNarrowPhase.removeReference(); + mSecondPassNarrowPhase.removeReference(); + } +} + +//void Sc::Scene::advanceStep(PxBaseTask* continuation) +//{ +// PX_PROFILE_ZONE("Sim.solveQueueTasks", getContextId()); +// +// if(mDt!=0.0f) +// { +// mFinalizationPhase.addDependent(*continuation); +// mFinalizationPhase.removeReference(); +// +// if(mPublicFlags & PxSceneFlag::eENABLE_CCD) +// { +// mUpdateCCDMultiPass.setContinuation(&mFinalizationPhase); +// mAfterIntegration.setContinuation(&mUpdateCCDMultiPass); +// mUpdateCCDMultiPass.removeReference(); +// } +// else +// { +// mAfterIntegration.setContinuation(&mFinalizationPhase); +// } +// +// mPostSolver.setContinuation(&mAfterIntegration); +// mUpdateSimulationController.setContinuation(&mPostSolver); +// mUpdateDynamics.setContinuation(&mUpdateSimulationController); +// mSolver.setContinuation(&mUpdateDynamics); +// mProcessTriggerInteractions.setContinuation(&mSolver); +// mPostIslandGen.setContinuation(&mProcessTriggerInteractions); +// mIslandGen.setContinuation(&mPostIslandGen); +// +// mFinalizationPhase.removeReference(); +// mAfterIntegration.removeReference(); +// mPostSolver.removeReference(); +// mUpdateSimulationController.removeReference(); +// mUpdateDynamics.removeReference(); +// mSolver.removeReference(); +// mProcessTriggerInteractions.removeReference(); +// mPostIslandGen.removeReference(); +// mIslandGen.removeReference(); +// } +//} + +void Sc::Scene::collideStep(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.collideQueueTasks", getContextId()); + PX_PROFILE_START_CROSSTHREAD("Basic.collision", getContextId()); + + mStats->simStart(); + mLLContext->beginUpdate(); + + mPostNarrowPhase.setTaskManager(*continuation->getTaskManager()); + mPostNarrowPhase.addReference(); + + mFinalizationPhase.setTaskManager(*continuation->getTaskManager()); + mFinalizationPhase.addReference(); + + mRigidBodyNarrowPhase.setContinuation(continuation); + mPreRigidBodyNarrowPhase.setContinuation(&mRigidBodyNarrowPhase); + + mRigidBodyNarrowPhase.removeReference(); + mPreRigidBodyNarrowPhase.removeReference(); +} + +void Sc::Scene::broadPhase(PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Basic.broadPhase", getContextId()); + + const PxU32 numCpuTasks = continuation->getTaskManager()->getCpuDispatcher()->getWorkerCount(); + mAABBManager->updateAABBsAndBP(numCpuTasks, mLLContext->getTaskPool(), &mLLContext->getScratchAllocator(), mHasContactDistanceChanged, continuation, &mRigidBodyNPhaseUnlock); +} + +void Sc::Scene::postBroadPhase(PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Basic.postBroadPhase", getContextId()); + + //Notify narrow phase that broad phase has completed + mLLContext->getNphaseImplementationContext()->postBroadPhaseUpdateContactManager(); + mAABBManager->postBroadPhase(continuation, &mRigidBodyNPhaseUnlock, *getFlushPool()); + +} + +void Sc::Scene::postBroadPhaseContinuation(PxBaseTask* continuation) +{ + mAABBManager->getChangedAABBMgActorHandleMap().clear(); + + // - Finishes broadphase update + // - Adds new interactions (and thereby contact managers if needed) + finishBroadPhase(continuation); +} + +void Sc::Scene::postBroadPhaseStage2(PxBaseTask* continuation) +{ + // - Wakes actors that lost touch if appropriate + processLostTouchPairs(); + //Release unused Cms back to the pool (later, this needs to be done in a thread-safe way from multiple worker threads + mIslandInsertion.setContinuation(continuation); + mRegisterContactManagers.setContinuation(continuation); + mRegisterInteractions.setContinuation(continuation); + mRegisterSceneInteractions.setContinuation(continuation); + mIslandInsertion.removeReference(); + mRegisterContactManagers.removeReference(); + mRegisterInteractions.removeReference(); + mRegisterSceneInteractions.removeReference(); + + { + PX_PROFILE_ZONE("Sim.processNewOverlaps.release", getContextId()); + for (PxU32 a = 0; a < mPreallocatedContactManagers.size(); ++a) + { + if ((reinterpret_cast(mPreallocatedContactManagers[a]) & 1) == 0) + mLLContext->getContactManagerPool().put(mPreallocatedContactManagers[a]); + } + + for (PxU32 a = 0; a < mPreallocatedShapeInteractions.size(); ++a) + { + if ((reinterpret_cast(mPreallocatedShapeInteractions[a]) & 1) == 0) + mNPhaseCore->mShapeInteractionPool.deallocate(mPreallocatedShapeInteractions[a]); + } + + for (PxU32 a = 0; a < mPreallocatedInteractionMarkers.size(); ++a) + { + if ((reinterpret_cast(mPreallocatedInteractionMarkers[a]) & 1) == 0) + mNPhaseCore->mInteractionMarkerPool.deallocate(mPreallocatedInteractionMarkers[a]); + } + } +} + +void Sc::Scene::postBroadPhaseStage3(PxBaseTask* /*continuation*/) +{ + finishBroadPhaseStage2(0); + + PX_PROFILE_STOP_CROSSTHREAD("Basic.postBroadPhase", getContextId()); + PX_PROFILE_STOP_CROSSTHREAD("Basic.broadPhase", getContextId()); +} + +class DirtyShapeUpdatesTask : public Cm::Task +{ +public: + static const PxU32 MaxShapes = 256; + + PxsTransformCache& mCache; + Bp::BoundsArray& mBoundsArray; + Sc::ShapeSim* mShapes[MaxShapes]; + PxU32 mNbShapes; + + DirtyShapeUpdatesTask(PxU64 contextID, PxsTransformCache& cache, Bp::BoundsArray& boundsArray) : + Cm::Task (contextID), + mCache (cache), + mBoundsArray(boundsArray), + mNbShapes (0) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbShapes; ++a) + { + mShapes[a]->updateCached(mCache, mBoundsArray); + } + } + + virtual const char* getName() const { return "DirtyShapeUpdatesTask"; } + +private: + PX_NOCOPY(DirtyShapeUpdatesTask) +}; + +class SpeculativeCCDContactDistanceUpdateTask : public Cm::Task +{ +public: + static const PxU32 MaxBodies = 128; + PxReal* mContactDistances; + PxReal mDt; + Sc::BodySim* mBodySims[MaxBodies]; + PxU32 mNbBodies; + + Bp::BoundsArray& mBoundsArray; + + SpeculativeCCDContactDistanceUpdateTask(PxU64 contextID, PxReal* contactDistances, const PxReal dt, Bp::BoundsArray& boundsArray) : + Cm::Task (contextID), + mContactDistances (contactDistances), + mDt (dt), + mNbBodies (0), + mBoundsArray (boundsArray) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbBodies; ++a) + { + mBodySims[a]->updateContactDistance(mContactDistances, mDt, mBoundsArray); + } + } + + virtual const char* getName() const { return "SpeculativeCCDContactDistanceUpdateTask"; } + +private: + PX_NOCOPY(SpeculativeCCDContactDistanceUpdateTask) +}; + +class SpeculativeCCDContactDistanceArticulationUpdateTask : public Cm::Task +{ +public: + PxReal* mContactDistances; + PxReal mDt; + Sc::ArticulationSim* mArticulation; + Bp::BoundsArray& mBoundsArray; + + SpeculativeCCDContactDistanceArticulationUpdateTask(PxU64 contextID, PxReal* contactDistances, const PxReal dt, Bp::BoundsArray& boundsArray) : + Cm::Task (contextID), + mContactDistances (contactDistances), + mDt (dt), + mBoundsArray (boundsArray) + { + } + + virtual void runInternal() + { + mArticulation->updateContactDistance(mContactDistances, mDt, mBoundsArray); + } + + virtual const char* getName() const { return "SpeculativeCCDContactDistanceArticulationUpdateTask"; } + +private: + PX_NOCOPY(SpeculativeCCDContactDistanceArticulationUpdateTask) +}; + + +void Sc::Scene::preRigidBodyNarrowPhase(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Scene.preNarrowPhase", getContextId()); + + PxU32 index; + + Cm::FlushPool& pool = mLLContext->getTaskPool(); + + //calculate contact distance for speculative CCD shapes + Cm::BitMap::Iterator speculativeCCDIter(mSpeculativeCCDRigidBodyBitMap); + + SpeculativeCCDContactDistanceUpdateTask* ccdTask = PX_PLACEMENT_NEW(pool.allocate(sizeof(SpeculativeCCDContactDistanceUpdateTask)), SpeculativeCCDContactDistanceUpdateTask)(getContextId(), mContactDistance->begin(), mDt, *mBoundsArray); + + IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + Cm::BitMapPinned& changedMap = mAABBManager->getChangedAABBMgActorHandleMap(); + + const size_t bodyOffset = PX_OFFSET_OF_RT(Sc::BodySim, getLowLevelBody()); + + bool hasContactDistanceChanged = mHasContactDistanceChanged; + while ((index = speculativeCCDIter.getNext()) != Cm::BitMap::Iterator::DONE) + { + PxsRigidBody* rigidBody = islandSim.getRigidBody(IG::NodeIndex(index)); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(rigidBody)-bodyOffset); + if (bodySim) + { + hasContactDistanceChanged = true; + ccdTask->mBodySims[ccdTask->mNbBodies++] = bodySim; + + ElementSim* current = bodySim->getElements_(); + while (current) + { + changedMap.growAndSet(current->getElementID()); + current = current->mNextInActor; + } + + if (ccdTask->mNbBodies == SpeculativeCCDContactDistanceUpdateTask::MaxBodies) + { + ccdTask->setContinuation(continuation); + ccdTask->removeReference(); + ccdTask = PX_PLACEMENT_NEW(pool.allocate(sizeof(SpeculativeCCDContactDistanceUpdateTask)), SpeculativeCCDContactDistanceUpdateTask)(getContextId(), mContactDistance->begin(), mDt, *mBoundsArray); + } + } + } + + if (ccdTask->mNbBodies != 0) + { + ccdTask->setContinuation(continuation); + ccdTask->removeReference(); + } + + //calculate contact distance for articulation links + SpeculativeCCDContactDistanceArticulationUpdateTask* articulationUpdateTask = NULL; + + Cm::BitMap::Iterator articulateCCDIter(mSpeculativeCDDArticulationBitMap); + while ((index = articulateCCDIter.getNext()) != Cm::BitMap::Iterator::DONE) + { + Sc::ArticulationSim* articulationSim = islandSim.getLLArticulation(IG::NodeIndex(index))->getArticulationSim(); + if (articulationSim) + { + hasContactDistanceChanged = true; + articulationUpdateTask = PX_PLACEMENT_NEW(pool.allocate(sizeof(SpeculativeCCDContactDistanceArticulationUpdateTask)), SpeculativeCCDContactDistanceArticulationUpdateTask)(getContextId(), mContactDistance->begin(), mDt, *mBoundsArray); + articulationUpdateTask->mArticulation = articulationSim; + articulationUpdateTask->setContinuation(continuation); + articulationUpdateTask->removeReference(); + } + } + + mHasContactDistanceChanged = hasContactDistanceChanged; + + //Process dirty shapeSims... + Cm::BitMap::Iterator dirtyShapeIter(mDirtyShapeSimMap); + + PxsTransformCache& cache = mLLContext->getTransformCache(); + Bp::BoundsArray& boundsArray = mAABBManager->getBoundsArray(); + + DirtyShapeUpdatesTask* task = PX_PLACEMENT_NEW(pool.allocate(sizeof(DirtyShapeUpdatesTask)), DirtyShapeUpdatesTask)(getContextId(), cache, boundsArray); + + bool hasDirtyShapes = false; + while ((index = dirtyShapeIter.getNext()) != Cm::BitMap::Iterator::DONE) + { + Sc::ShapeSim* shapeSim = reinterpret_cast(mAABBManager->getUserData(index)); + if (shapeSim) + { + hasDirtyShapes = true; + changedMap.growAndSet(index); + task->mShapes[task->mNbShapes++] = shapeSim; + if (task->mNbShapes == DirtyShapeUpdatesTask::MaxShapes) + { + task->setContinuation(continuation); + task->removeReference(); + task = PX_PLACEMENT_NEW(pool.allocate(sizeof(DirtyShapeUpdatesTask)), DirtyShapeUpdatesTask)(getContextId(), cache, boundsArray); + } + } + } + + if (hasDirtyShapes) + { + //Setting the boundsArray and transform cache as dirty so that they get DMAd to GPU if GPU dynamics and BP are being used respectively. + //These bits are no longer set when we update the cached state for actors due to an optimization avoiding setting these dirty bits multiple times. + getBoundsArray().setChangedState(); + getLowLevelContext()->getTransformCache().setChangedState(); + } + + if (task->mNbShapes != 0) + { + task->setContinuation(continuation); + task->removeReference(); + } + + mDirtyShapeSimMap.clear(); +} + +void Sc::Scene::rigidBodyNarrowPhase(PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Basic.narrowPhase", getContextId()); + + mCCDPass = 0; + + mPostBroadPhase3.addDependent(*continuation); + mPostBroadPhase2.setContinuation(&mPostBroadPhase3); + mPostBroadPhaseCont.setContinuation(&mPostBroadPhase2); + mPostBroadPhase.setContinuation(&mPostBroadPhaseCont); + mBroadPhase.setContinuation(&mPostBroadPhase); + mRigidBodyNPhaseUnlock.setContinuation(continuation); + mRigidBodyNPhaseUnlock.addReference(); + + mLLContext->resetThreadContexts(); + + mLLContext->updateContactManager(mDt, mBoundsArray->hasChanged(), mHasContactDistanceChanged, continuation, &mRigidBodyNPhaseUnlock); // Starts update of contact managers + + mPostBroadPhase3.removeReference(); + mPostBroadPhase2.removeReference(); + mPostBroadPhaseCont.removeReference(); + mPostBroadPhase.removeReference(); + mBroadPhase.removeReference(); +} + +void Sc::Scene::unblockNarrowPhase(PxBaseTask*) +{ + this->mLLContext->getNphaseImplementationContext()->startNarrowPhaseTasks(); +} + +void Sc::Scene::postNarrowPhase(PxBaseTask* /*continuation*/) +{ + mHasContactDistanceChanged = false; + mLLContext->fetchUpdateContactManager(); //Sync on contact gen results! + + releaseConstraints(false); + + PX_PROFILE_STOP_CROSSTHREAD("Basic.narrowPhase", getContextId()); + PX_PROFILE_STOP_CROSSTHREAD("Basic.collision", getContextId()); +} + +void Sc::Scene::fetchPatchEvents(PxBaseTask*) +{ + PxU32 foundPatchCount, lostPatchCount; + + { + PX_PROFILE_ZONE("Sim.preIslandGen.managerPatchEvents", getContextId()); + mLLContext->getManagerPatchEventCount(foundPatchCount, lostPatchCount); + + mFoundPatchManagers.forceSize_Unsafe(0); + mFoundPatchManagers.resizeUninitialized(foundPatchCount); + + mLostPatchManagers.forceSize_Unsafe(0); + mLostPatchManagers.resizeUninitialized(lostPatchCount); + + mLLContext->fillManagerPatchChangedEvents(mFoundPatchManagers.begin(), foundPatchCount, mLostPatchManagers.begin(), lostPatchCount); + + mFoundPatchManagers.forceSize_Unsafe(foundPatchCount); + mLostPatchManagers.forceSize_Unsafe(lostPatchCount); + } +} + +void Sc::Scene::processNarrowPhaseTouchEvents() +{ + PxsContext* context = mLLContext; + + { + PX_PROFILE_ZONE("Sim.preIslandGen", getContextId()); + + // Update touch states from LL + PxU32 newTouchCount, lostTouchCount; + PxU32 ccdTouchCount = 0; + { + PX_PROFILE_ZONE("Sim.preIslandGen.managerTouchEvents", getContextId()); + context->getManagerTouchEventCount(reinterpret_cast(&newTouchCount), reinterpret_cast(&lostTouchCount), NULL); + //PX_ALLOCA(newTouches, PxvContactManagerTouchEvent, newTouchCount); + //PX_ALLOCA(lostTouches, PxvContactManagerTouchEvent, lostTouchCount); + + mTouchFoundEvents.forceSize_Unsafe(0); + mTouchFoundEvents.reserve(newTouchCount); + mTouchFoundEvents.forceSize_Unsafe(newTouchCount); + + mTouchLostEvents.forceSize_Unsafe(0); + mTouchLostEvents.reserve(lostTouchCount); + mTouchLostEvents.forceSize_Unsafe(lostTouchCount); + + { + context->fillManagerTouchEvents(mTouchFoundEvents.begin(), reinterpret_cast(newTouchCount), mTouchLostEvents.begin(), + reinterpret_cast(lostTouchCount), NULL, reinterpret_cast(ccdTouchCount)); + + mTouchFoundEvents.forceSize_Unsafe(newTouchCount); + mTouchLostEvents.forceSize_Unsafe(lostTouchCount); + } + } + + context->getSimStats().mNbNewTouches = newTouchCount; + context->getSimStats().mNbLostTouches = lostTouchCount; + } +} + +class InteractionNewTouchTask : public Cm::Task +{ + PxvContactManagerTouchEvent* mEvents; + const PxU32 mNbEvents; + PxsContactManagerOutputIterator mOutputs; + const bool mUseAdaptiveForce; + +public: + InteractionNewTouchTask(PxU64 contextID, PxvContactManagerTouchEvent* events, PxU32 nbEvents, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) : + Cm::Task (contextID), + mEvents (events), + mNbEvents (nbEvents), + mOutputs (outputs), + mUseAdaptiveForce (useAdaptiveForce) + { + } + + virtual const char* getName() const + { + return "InteractionNewTouchTask"; + } + + void hackInContinuation(PxBaseTask* cont) + { + PX_ASSERT(mCont == NULL); + mCont = cont; + if (mCont) + mCont->addReference(); + } + + virtual void runInternal() + { + for (PxU32 i = 0; i < mNbEvents; ++i) + { + Sc::ShapeInteraction* si = reinterpret_cast(mEvents[i].userData); + PX_ASSERT(si); + si->managerNewTouch(0, true, mOutputs, mUseAdaptiveForce); + } + } +private: + PX_NOCOPY(InteractionNewTouchTask) +}; + +void Sc::Scene::processNarrowPhaseTouchEventsStage2(PxBaseTask* continuation) +{ + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + //Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + PxU32 newTouchCount = mTouchFoundEvents.size(); + + { + const PxU32 nbPerTask = 256; + PX_PROFILE_ZONE("Sim.preIslandGen.newTouches", getContextId()); + + InteractionNewTouchTask* prevTask = NULL; + + //for (PxU32 i = 0; i < newTouchCount; ++i) + for (PxU32 i = 0; i < newTouchCount; i+= nbPerTask) + { + const PxU32 nbToProcess = PxMin(newTouchCount - i, nbPerTask); + + for (PxU32 a = 0; a < nbToProcess; ++a) + { + ShapeInteraction* si = reinterpret_cast(mTouchFoundEvents[i + a].userData); + PX_ASSERT(si); + mNPhaseCore->managerNewTouch(*si); + si->managerNewTouch(0, true, outputs, useAdaptiveForce); + } + + //InteractionNewTouchTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(InteractionNewTouchTask)), InteractionNewTouchTask)(mTouchFoundEvents.begin() + i, nbToProcess, outputs); + ////task->setContinuation(continuation); + //task->setContinuation(*continuation->getTaskManager(), NULL); + //if (prevTask) + //{ + // prevTask->hackInContinuation(task); + // prevTask->removeReference(); + //} + + //prevTask = task; + + //task->removeReference(); + } + + if (prevTask) + { + prevTask->hackInContinuation(continuation); + prevTask->removeReference(); + } + } + + /*{ + PX_PROFILE_ZONE("Sim.preIslandGen.newTouchesInteraction", getContextId()); + for (PxU32 i = 0; i < newTouchCount; ++i) + { + ShapeInteraction* si = reinterpret_cast(mTouchFoundEvents[i].userData); + PX_ASSERT(si); + si->managerNewTouch(0, true, outputs); + } + }*/ + +} + +void Sc::Scene::setEdgesConnected(PxBaseTask*) +{ + { + PxU32 newTouchCount = mTouchFoundEvents.size(); + PX_PROFILE_ZONE("Sim.preIslandGen.islandTouches", getContextId()); + { + PX_PROFILE_ZONE("Sim.preIslandGen.setEdgesConnected", getContextId()); + for (PxU32 i = 0; i < newTouchCount; ++i) + { + ShapeInteraction* si = reinterpret_cast(mTouchFoundEvents[i].userData); + if (!si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) + { + mSimpleIslandManager->setEdgeConnected(si->getEdgeIndex()); + } + } + } + + mSimpleIslandManager->secondPassIslandGen(); + + wakeObjectsUp(ActorSim::AS_PART_OF_ISLAND_GEN); + } +} + +void Sc::Scene::processNarrowPhaseLostTouchEventsIslands(PxBaseTask*) +{ + { + PX_PROFILE_ZONE("Sc::Scene.islandLostTouches", getContextId()); + for (PxU32 i = 0; i < mTouchLostEvents.size(); ++i) + { + ShapeInteraction* si = reinterpret_cast(mTouchLostEvents[i].userData); + mSimpleIslandManager->setEdgeDisconnected(si->getEdgeIndex()); + } + } +} + +void Sc::Scene::processNarrowPhaseLostTouchEvents(PxBaseTask*) +{ + { + PX_PROFILE_ZONE("Sc::Scene.processNarrowPhaseLostTouchEvents", getContextId()); + PxsContactManagerOutputIterator outputs = this->mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + for (PxU32 i = 0; i < mTouchLostEvents.size(); ++i) + { + ShapeInteraction* si = reinterpret_cast(mTouchLostEvents[i].userData); + PX_ASSERT(si); + if (si->managerLostTouch(0, true, outputs, useAdaptiveForce) && !si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) + addToLostTouchList(si->getShape0().getBodySim(), si->getShape1().getBodySim()); + } + } +} + +void Sc::Scene::processLostSolverPatches(PxBaseTask* /*continuation*/) +{ + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + mDynamicsContext->processLostPatches(*mSimpleIslandManager, mLostPatchManagers.begin(), mLostPatchManagers.size(), outputs); +} + +void Sc::Scene::islandGen(PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Basic.rigidBodySolver", getContextId()); + + //mLLContext->runModifiableContactManagers(); //KS - moved here so that we can get up-to-date touch found/lost events in IG + + mProcessLostPatchesTask.setContinuation(&mUpdateDynamics); + mFetchPatchEventsTask.setContinuation(&mProcessLostPatchesTask); + mProcessLostPatchesTask.removeReference(); + mFetchPatchEventsTask.removeReference(); + processNarrowPhaseTouchEvents(); + + mSetEdgesConnectedTask.setContinuation(continuation); + mSetEdgesConnectedTask.removeReference(); + + processNarrowPhaseTouchEventsStage2(continuation); +} + +PX_FORCE_INLINE void Sc::Scene::putObjectsToSleep(PxU32 infoFlag) +{ + PX_PROFILE_ZONE("Sc::Scene::putObjectsToSleep", getContextId()); + const IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + //Set to sleep all bodies that were in awake islands that have just been put to sleep. + const PxU32 nbBodiesToSleep = islandSim.getNbNodesToDeactivate(IG::Node::eRIGID_BODY_TYPE); + const IG::NodeIndex*const bodyIndices = islandSim.getNodesToDeactivate(IG::Node::eRIGID_BODY_TYPE); + + for(PxU32 i=0;i(reinterpret_cast(rigidBody) - Sc::BodySim::getRigidBodyOffset()); + bodySim->setActive(false, infoFlag); + } + } + + const PxU32 nbArticulationsToSleep = islandSim.getNbNodesToDeactivate(IG::Node::eARTICULATION_TYPE); + const IG::NodeIndex*const articIndices = islandSim.getNodesToDeactivate(IG::Node::eARTICULATION_TYPE); + + for(PxU32 i=0;igetArticulationSim(); + if (articSim && !islandSim.getNode(articIndices[i]).isActive()) + articSim->setActive(false, infoFlag); + } +} + +PX_FORCE_INLINE void Sc::Scene::putInteractionsToSleep() +{ + PX_PROFILE_ZONE("Sc::Scene::putInteractionsToSleep", getContextId()); + const IG::IslandSim& islandSim = mSimpleIslandManager->getSpeculativeIslandSim(); + + //KS - only deactivate contact managers based on speculative state to trigger contact gen. When the actors were deactivated based on accurate state + //joints should have been deactivated. + + { + PxU32 nbDeactivatingEdges = islandSim.getNbDeactivatingEdges(IG::Edge::eCONTACT_MANAGER); + const IG::EdgeIndex* deactivatingEdgeIds = islandSim.getDeactivatingEdges(IG::Edge::eCONTACT_MANAGER); + + for (PxU32 i = 0; i < nbDeactivatingEdges; ++i) + { + Sc::Interaction* interaction = mSimpleIslandManager->getInteraction(deactivatingEdgeIds[i]); + + if (interaction && interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE)) + { + if (!islandSim.getEdge(deactivatingEdgeIds[i]).isActive()) + { + const bool proceed = deactivateInteraction(interaction); + if (proceed && (interaction->getType() < InteractionType::eTRACKED_IN_SCENE_COUNT)) + notifyInteractionDeactivated(interaction); + } + } + } + } +} + +PX_FORCE_INLINE void Sc::Scene::wakeObjectsUp(PxU32 infoFlag) +{ + //Wake up all bodies that were in sleeping islands that have just been hit by a moving object. + + const IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + const PxU32 nbBodiesToWake = islandSim.getNbNodesToActivate(IG::Node::eRIGID_BODY_TYPE); + const IG::NodeIndex*const bodyIndices = islandSim.getNodesToActivate(IG::Node::eRIGID_BODY_TYPE); + + for(PxU32 i=0;i(reinterpret_cast(rigidBody) - Sc::BodySim::getRigidBodyOffset()); + bodySim->setActive(true, infoFlag); + } + } + + const PxU32 nbArticulationsToWake = islandSim.getNbNodesToActivate(IG::Node::eARTICULATION_TYPE); + const IG::NodeIndex*const articIndices = islandSim.getNodesToActivate(IG::Node::eARTICULATION_TYPE); + + for(PxU32 i=0;igetArticulationSim(); + if (articSim && islandSim.getNode(articIndices[i]).isActive()) + articSim->setActive(true, infoFlag); + } +} + +void Sc::Scene::postIslandGen(PxBaseTask* continuationTask) +{ + { + PX_PROFILE_ZONE("Sim.postIslandGen", getContextId()); + + // - Performs collision detection for trigger interactions + { + mNPhaseCore->processTriggerInteractions(continuationTask); + } + } +} + +void Sc::Scene::solver(PxBaseTask* continuation) +{ + PX_PROFILE_STOP_CROSSTHREAD("Basic.narrowPhase", getContextId()); + PX_PROFILE_START_CROSSTHREAD("Basic.rigidBodySolver", getContextId()); + //Update forces per body in parallel. This can overlap with the other work in this phase. + beforeSolver(continuation); + + PX_PROFILE_ZONE("Sim.postNarrowPhaseSecondPass", getContextId()); + //Narrowphase is completely finished so the streams can be swapped. + mLLContext->swapStreams(); + + //PxsContactManagerOutputIterator outputs = this->mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + //mNPhaseCore->processPersistentContactEvents(outputs, continuation); +} + +void Sc::Scene::updateBodiesAndShapes(PxBaseTask* continuation) +{ + PX_UNUSED(continuation); + //dma bodies and shapes data to gpu + mSimulationController->updateBodiesAndShapes(continuation); +} + +Cm::FlushPool* Sc::Scene::getFlushPool() +{ + return &mLLContext->getTaskPool(); +} + +void Sc::Scene::postThirdPassIslandGen(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sc::Scene::postThirdPassIslandGen", getContextId()); + putObjectsToSleep(ActorSim::AS_PART_OF_ISLAND_GEN); + putInteractionsToSleep(); + + PxsContactManagerOutputIterator outputs = this->mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + mNPhaseCore->processPersistentContactEvents(outputs, continuation); +} + +void Sc::Scene::processLostContacts(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sc::Scene::processLostContacts", getContextId()); + mProcessNarrowPhaseLostTouchTasks.setContinuation(continuation); + mProcessNarrowPhaseLostTouchTasks.removeReference(); + + //mLostTouchReportsTask.setContinuation(&mProcessLostContactsTask3); + mProcessNPLostTouchEvents.setContinuation(continuation); + mProcessNPLostTouchEvents.removeReference(); + + { + PX_PROFILE_ZONE("Sim.findInteractionsPtrs", getContextId()); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + ElementSim* volume0 = reinterpret_cast(p->mUserData0); + ElementSim* volume1 = reinterpret_cast(p->mUserData1); + p->mPairUserData = mNPhaseCore->onOverlapRemovedStage1(volume0, volume1); + p++; + } + } +} + +void Sc::Scene::lostTouchReports(PxBaseTask*) +{ + { + PX_PROFILE_ZONE("Sim.lostTouchReports", getContextId()); + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + + { + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + if(p->mPairUserData) + { + Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); + if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP) + mNPhaseCore->lostTouchReports(static_cast(elemInteraction), PxU32(PairReleaseFlag::eWAKE_ON_LOST_TOUCH), 0, outputs, useAdaptiveForce); + } + p++; + } + } + } +} + +void Sc::Scene::unregisterInteractions(PxBaseTask*) +{ + { + PX_PROFILE_ZONE("Sim.unregisterInteractions", getContextId()); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + + { + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + if(p->mPairUserData) + { + Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); + if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP || elemInteraction->getType() == Sc::InteractionType::eMARKER) + { + unregisterInteraction(elemInteraction); + mNPhaseCore->unregisterInteraction(elemInteraction); + } + } + p++; + } + } + } +} + +void Sc::Scene::destroyManagers(PxBaseTask*) +{ + { + PX_PROFILE_ZONE("Sim.destroyManagers", getContextId()); + + mPostThirdPassIslandGenTask.setContinuation(mProcessLostContactsTask3.getContinuation()); + + mSimpleIslandManager->thirdPassIslandGen(&mPostThirdPassIslandGenTask); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + if(p->mPairUserData) + { + Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); + if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP) + { + Sc::ShapeInteraction* si = static_cast(elemInteraction); + if(si->getContactManager()) + si->destroyManager(); + } + } + p++; + } + } +} + +void Sc::Scene::processLostContacts2(PxBaseTask* continuation) +{ + mDestroyManagersTask.setContinuation(continuation); + mLostTouchReportsTask.setContinuation(&mDestroyManagersTask); + mLostTouchReportsTask.removeReference(); + + + mUnregisterInteractionsTask.setContinuation(continuation); + mUnregisterInteractionsTask.removeReference(); + + { + PX_PROFILE_ZONE("Sim.clearIslandData", getContextId()); +// PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + { + Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + Sc::ElementSimInteraction* pair = reinterpret_cast(p->mPairUserData); + if(pair) + { + if(pair->getType() == InteractionType::eOVERLAP) + { + ShapeInteraction* si = static_cast(pair); + si->clearIslandGenData(); + } + } + p++; + } + } + } + + mDestroyManagersTask.removeReference(); +} + +void Sc::Scene::processLostContacts3(PxBaseTask* /*continuation*/) +{ + { + PX_PROFILE_ZONE("Sim.processLostOverlapsStage2", getContextId()); + + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + + { + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + ElementSim* volume0 = reinterpret_cast(p->mUserData0); + ElementSim* volume1 = reinterpret_cast(p->mUserData1); + + mNPhaseCore->onOverlapRemoved(volume0, volume1, false, p->mPairUserData, outputs, useAdaptiveForce); + p++; + } + } + + { + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eTRIGGER, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + ElementSim* volume0 = reinterpret_cast(p->mUserData0); + ElementSim* volume1 = reinterpret_cast(p->mUserData1); + + mNPhaseCore->onOverlapRemoved(volume0, volume1, false, NULL, outputs, useAdaptiveForce); + p++; + } + } + + aabbMgr->getBroadPhase()->deletePairs(); + aabbMgr->freeBuffers(); + } + + mPostThirdPassIslandGenTask.removeReference(); +} + +//This is called after solver finish +void Sc::Scene::updateSimulationController(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateSimulationController", getContextId()); + //for pxgdynamicscontext: copy solver body data to body core + mDynamicsContext->updateBodyCore(continuation); + + PxsTransformCache& cache = getLowLevelContext()->getTransformCache(); + Bp::BoundsArray& boundArray = getBoundsArray(); + + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + //changedAABBMgrActorHandles.resizeAndClear(getElementIDPool().getMaxID()); + + mSimulationController->gpuDmabackData(cache, boundArray, changedAABBMgrActorHandles); + //mSimulationController->update(cache, boundArray, changedAABBMgrActorHandles); +} + +void Sc::Scene::updateDynamics(PxBaseTask* continuation) +{ + //Allow processLostContactsTask to run until after 2nd pass of solver completes (update bodies, run sleeping logic etc.) + mProcessLostContactsTask3.setContinuation(static_cast(continuation)->getContinuation()); + mProcessLostContactsTask2.setContinuation(&mProcessLostContactsTask3); + mProcessLostContactsTask.setContinuation(&mProcessLostContactsTask2); + + ////dma bodies and shapes data to gpu + //mSimulationController->updateBodiesAndShapes(); + + mLLContext->getNpMemBlockPool().acquireConstraintMemory(); + + PX_PROFILE_START_CROSSTHREAD("Basic.dynamics", getContextId()); + PxU32 maxPatchCount = mLLContext->getMaxPatchCount(); + + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + PxsContactManagerOutput* cmOutputBase = mLLContext->getNphaseImplementationContext()->getGPUContactManagerOutputBase(); + + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + changedAABBMgrActorHandles.resizeAndClear(getElementIDPool().getMaxID()); + + //mNPhaseCore->processPersistentContactEvents(outputs, continuation); + + mDynamicsContext->update(*mSimpleIslandManager, continuation, &mProcessLostContactsTask, + mFoundPatchManagers.begin(), mFoundPatchManagers.size(), mLostPatchManagers.begin(), mLostPatchManagers.size(), + maxPatchCount, outputs, cmOutputBase, mDt, mGravity, changedAABBMgrActorHandles.getWordCount()); + + mSimpleIslandManager->clearDestroyedEdges(); + + mProcessLostContactsTask3.removeReference(); + mProcessLostContactsTask2.removeReference(); + mProcessLostContactsTask.removeReference(); +} + +void Sc::Scene::updateCCDMultiPass(PxBaseTask* parentContinuation) +{ + getCcdBodies().forceSize_Unsafe(mSimulationControllerCallback->getNbCcdBodies()); + + // second run of the broadphase for making sure objects we have integrated did not tunnel. + if(mPublicFlags & PxSceneFlag::eENABLE_CCD) + { + if (mContactReportsNeedPostSolverVelocity) + { + // the CCD code will overwrite the post solver body velocities, hence, we need to extract the info + // first if any CCD enabled pair requested it. + collectPostSolverVelocitiesBeforeCCD(); + } + + //We use 2 CCD task chains to be able to chain together an arbitrary number of ccd passes + if(mPostCCDPass.size() != 2) + { + mPostCCDPass.clear(); + mUpdateCCDSinglePass.clear(); + mCCDBroadPhase.clear(); + mCCDBroadPhaseAABB.clear(); + mPostCCDPass.reserve(2); + mUpdateCCDSinglePass.reserve(2); + mUpdateCCDSinglePass2.reserve(2); + mUpdateCCDSinglePass3.reserve(2); + mCCDBroadPhase.reserve(2); + mCCDBroadPhaseAABB.reserve(2); + for (int j = 0; j < 2; j++) + { + mPostCCDPass.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.postCCDPass")); + mUpdateCCDSinglePass.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.updateCCDSinglePass")); + mUpdateCCDSinglePass2.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.updateCCDSinglePassStage2")); + mUpdateCCDSinglePass3.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.updateCCDSinglePassStage3")); + mCCDBroadPhase.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.ccdBroadPhase")); + mCCDBroadPhaseAABB.pushBack(Cm::DelegateTask(getContextId(), this, "ScScene.ccdBroadPhaseAABB")); + } + } + + //reset thread context in a place we know all tasks possibly accessing it, are in sync with. (see US6664) + mLLContext->resetThreadContexts(); + + mCCDContext->updateCCDBegin(); + + mCCDBroadPhase[0].setContinuation(parentContinuation); + mCCDBroadPhaseAABB[0].setContinuation(&mCCDBroadPhase[0]); + mCCDBroadPhase[0].removeReference(); + mCCDBroadPhaseAABB[0].removeReference(); + } +} + +class UpdateCCDBoundsTask : public Cm::Task +{ + + Sc::BodySim** mBodySims; + PxU32 mNbToProcess; + PxI32* mNumFastMovingShapes; + +public: + + static const PxU32 MaxPerTask = 256; + + UpdateCCDBoundsTask(PxU64 contextID, Sc::BodySim** bodySims, PxU32 nbToProcess, PxI32* numFastMovingShapes) : + Cm::Task (contextID), + mBodySims (bodySims), + mNbToProcess (nbToProcess), + mNumFastMovingShapes(numFastMovingShapes) + { + } + + virtual const char* getName() const { return "UpdateCCDBoundsTask";} + + virtual void runInternal() + { + PxU32 activeShapes = 0; + for (PxU32 i = 0; i < mNbToProcess; i++) + { + PxU32 isFastMoving = 0; + Sc::BodySim& bodySim = *mBodySims[i]; + + Sc::ElementSim* current = bodySim.getElements_(); + while(current) + { + Sc::ShapeSim* sim = static_cast(current); + if(sim->getFlags()&PxU32(PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) + { + Ps::IntBool fastMovingShape = sim->updateSweptBounds(); + activeShapes += fastMovingShape; + + isFastMoving = isFastMoving | fastMovingShape; + } + current = current->mNextInActor; + } + + bodySim.getLowLevelBody().getCore().isFastMoving = PxU16(isFastMoving); + } + + Ps::atomicAdd(mNumFastMovingShapes, PxI32(activeShapes)); + } +}; + +void Sc::Scene::ccdBroadPhaseAABB(PxBaseTask* continuation) +{ + PX_PROFILE_START_CROSSTHREAD("Sim.ccdBroadPhaseComplete", getContextId()); + PX_PROFILE_ZONE("Sim.ccdBroadPhaseAABB", getContextId()); + PX_UNUSED(continuation); + + PxU32 currentPass = mCCDContext->getCurrentCCDPass(); + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + mNumFastMovingShapes = 0; + + //If we are on the 1st pass or we had some sweep hits previous CCD pass, we need to run CCD again + if( currentPass == 0 || mCCDContext->getNumSweepHits()) + { + for (PxU32 i = 0; i < mCcdBodies.size(); i+= UpdateCCDBoundsTask::MaxPerTask) + { + const PxU32 nbToProcess = PxMin(UpdateCCDBoundsTask::MaxPerTask, mCcdBodies.size() - i); + UpdateCCDBoundsTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(UpdateCCDBoundsTask)), UpdateCCDBoundsTask)(getContextId(), &mCcdBodies[i], nbToProcess, &mNumFastMovingShapes); + task->setContinuation(continuation); + task->removeReference(); + } + } +} + +void Sc::Scene::ccdBroadPhase(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.ccdBroadPhase", getContextId()); + + PxU32 currentPass = mCCDContext->getCurrentCCDPass(); + const PxU32 ccdMaxPasses = mCCDContext->getCCDMaxPasses(); + mCCDPass = currentPass+1; + + //If we are on the 1st pass or we had some sweep hits previous CCD pass, we need to run CCD again + if( (currentPass == 0 || mCCDContext->getNumSweepHits()) && mNumFastMovingShapes != 0) + { + const PxU32 currIndex = currentPass & 1; + const PxU32 nextIndex = 1 - currIndex; + //Initialize the CCD task chain unless this is the final pass + if(currentPass != (ccdMaxPasses - 1)) + { + mCCDBroadPhase[nextIndex].setContinuation(continuation); + mCCDBroadPhaseAABB[nextIndex].setContinuation(&mCCDBroadPhase[nextIndex]); + } + mPostCCDPass[currIndex].setContinuation(currentPass == ccdMaxPasses-1 ? continuation : &mCCDBroadPhaseAABB[nextIndex]); + mUpdateCCDSinglePass3[currIndex].setContinuation(&mPostCCDPass[currIndex]); + mUpdateCCDSinglePass2[currIndex].setContinuation(&mUpdateCCDSinglePass3[currIndex]); + mUpdateCCDSinglePass[currIndex].setContinuation(&mUpdateCCDSinglePass2[currIndex]); + + //Do the actual broad phase + PxBaseTask* continuationTask = &mUpdateCCDSinglePass[currIndex]; + const PxU32 numCpuTasks = continuationTask->getTaskManager()->getCpuDispatcher()->getWorkerCount(); + + mAABBManager->updateAABBsAndBP(numCpuTasks, mLLContext->getTaskPool(), &mLLContext->getScratchAllocator(), false, continuationTask, NULL); + + //Allow the CCD task chain to continue + mPostCCDPass[currIndex].removeReference(); + mUpdateCCDSinglePass3[currIndex].removeReference(); + mUpdateCCDSinglePass2[currIndex].removeReference(); + mUpdateCCDSinglePass[currIndex].removeReference(); + if(currentPass != (ccdMaxPasses - 1)) + { + mCCDBroadPhase[nextIndex].removeReference(); + mCCDBroadPhaseAABB[nextIndex].removeReference(); + } + } + else if (currentPass == 0) + { + PX_PROFILE_STOP_CROSSTHREAD("Sim.ccdBroadPhaseComplete", getContextId()); + mCCDContext->resetContactManagers(); + } +} + +void Sc::Scene::updateCCDSinglePass(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateCCDSinglePass", getContextId()); + mReportShapePairTimeStamp++; // This will makes sure that new report pairs will get created instead of re-using the existing ones. + + mAABBManager->postBroadPhase(NULL, NULL, *getFlushPool()); + finishBroadPhase(continuation); + + const PxU32 currentPass = mCCDContext->getCurrentCCDPass() + 1; // 0 is reserved for discrete collision phase + if(currentPass == 1) // reset the handle map so we only update CCD objects from here on + { + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + //changedAABBMgrActorHandles.clear(); + for(PxU32 i = 0; i < mCcdBodies.size();i++) + { + Sc::ElementSim* current = mCcdBodies[i]->getElements_(); + while(current) + { + Sc::ShapeSim* sim = static_cast(current); + if(sim->getFlags()&PxU32(PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) // TODO: need trigger shape here? + changedAABBMgrActorHandles.growAndSet(sim->getElementID()); + current = current->mNextInActor; + } + } + } +} + +void Sc::Scene::updateCCDSinglePassStage2(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateCCDSinglePassStage2", getContextId()); + postBroadPhaseStage2(continuation); +} + +void Sc::Scene::updateCCDSinglePassStage3(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateCCDSinglePassStage3", getContextId()); + mReportShapePairTimeStamp++; // This will makes sure that new report pairs will get created instead of re-using the existing ones. + + const PxU32 currentPass = mCCDContext->getCurrentCCDPass() + 1; // 0 is reserved for discrete collision phase + finishBroadPhaseStage2(currentPass); + PX_PROFILE_STOP_CROSSTHREAD("Sim.ccdBroadPhaseComplete", getContextId()); + + //reset thread context in a place we know all tasks possibly accessing it, are in sync with. (see US6664) + mLLContext->resetThreadContexts(); + + mCCDContext->updateCCD(mDt, continuation, mSimpleIslandManager->getAccurateIslandSim(), (mPublicFlags & PxSceneFlag::eDISABLE_CCD_RESWEEP), mNumFastMovingShapes); +} + +class ScKinematicPoseUpdateTask : public Cm::Task +{ + Sc::BodyCore*const* mKinematics; + PxU32 mNbKinematics; + +public: + static const PxU32 NbKinematicsPerTask = 1024; + + ScKinematicPoseUpdateTask(Sc::BodyCore*const* kinematics, PxU32 nbKinematics, PxU64 contextID) : + Cm::Task(contextID), mKinematics(kinematics), mNbKinematics(nbKinematics) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbKinematics; ++a) + { + if ((a + 16) < mNbKinematics) + { + Ps::prefetchLine(mKinematics[a + 16]); + + if ((a + 4) < mNbKinematics) + { + Ps::prefetchLine(mKinematics[a + 4]->getSim()); + Ps::prefetchLine(mKinematics[a + 4]->getSimStateData_Unchecked()); + } + } + Sc::BodyCore* b = mKinematics[a]; + PX_ASSERT(b->getSim()->isKinematic()); + PX_ASSERT(b->getSim()->isActive()); + b->getSim()->updateKinematicPose(); + } + } + + virtual const char* getName() const + { + return "ScScene.ScKinematicPoseUpdateTask"; + } +}; + +void Sc::Scene::integrateKinematicPose() +{ + PX_PROFILE_ZONE("Sim.integrateKinematicPose", getContextId()); + + PxU32 nbKinematics = getActiveKinematicBodiesCount(); + BodyCore*const* kinematics = getActiveKinematicBodies(); + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + for(PxU32 i = 0; i < nbKinematics; i+= ScKinematicPoseUpdateTask::NbKinematicsPerTask) + { + ScKinematicPoseUpdateTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicPoseUpdateTask)), ScKinematicPoseUpdateTask) + (kinematics + i, PxMin(nbKinematics - i, ScKinematicPoseUpdateTask::NbKinematicsPerTask), mContextId); + task->setContinuation(&mConstraintProjection); + task->removeReference(); + } +} + +class ScKinematicShapeUpdateTask : public Cm::Task +{ + Sc::BodyCore*const* mKinematics; + PxU32 mNbKinematics; + PxsTransformCache& mCache; + Bp::BoundsArray& mBoundsArray; + + PX_NOCOPY(ScKinematicShapeUpdateTask) + +public: + static const PxU32 NbKinematicsShapesPerTask = 1024; + + ScKinematicShapeUpdateTask(Sc::BodyCore*const* kinematics, PxU32 nbKinematics, PxsTransformCache& cache, Bp::BoundsArray& boundsArray, PxU64 contextID) : + Cm::Task(contextID), mKinematics(kinematics), mNbKinematics(nbKinematics), mCache(cache), mBoundsArray(boundsArray) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbKinematics; ++a) + { + Sc::BodyCore* b = mKinematics[a]; + PX_ASSERT(b->getSim()->isKinematic()); + PX_ASSERT(b->getSim()->isActive()); + + b->getSim()->updateCached(mCache, mBoundsArray); + } + } + + virtual const char* getName() const + { + return "ScScene.KinematicShapeUpdateTask"; + } +}; + +void Sc::Scene::updateKinematicCached(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateKinematicCached", getContextId()); + + PxU32 nbKinematics = getActiveKinematicBodiesCount(); + BodyCore*const* kinematics = getActiveKinematicBodies(); + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + + PxU32 startIndex = 0; + PxU32 nbShapes = 0; + + { + PX_PROFILE_ZONE("ShapeUpdate", getContextId()); + for (PxU32 i = 0; i < nbKinematics; i++) + { + BodyCore* b = kinematics[i]; + BodySim* sim = b->getSim(); + PX_ASSERT(sim->isKinematic()); + PX_ASSERT(sim->isActive()); + + nbShapes += sim->getNbShapes(); + + if (nbShapes >= ScKinematicShapeUpdateTask::NbKinematicsShapesPerTask) + { + ScKinematicShapeUpdateTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicShapeUpdateTask)), ScKinematicShapeUpdateTask) + (kinematics + startIndex, (i + 1) - startIndex, mLLContext->getTransformCache(), *mBoundsArray, mContextId); + + task->setContinuation(continuation); + task->removeReference(); + startIndex = i + 1; + nbShapes = 0; + } + } + + if (nbShapes) + { + ScKinematicShapeUpdateTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicShapeUpdateTask)), ScKinematicShapeUpdateTask) + (kinematics + startIndex, nbKinematics - startIndex, mLLContext->getTransformCache(), *mBoundsArray, mContextId); + + task->setContinuation(continuation); + task->removeReference(); + } + } + + if (nbKinematics) + { + Cm::BitMapPinned& changedAABBMap = mAABBManager->getChangedAABBMgActorHandleMap(); + mLLContext->getTransformCache().setChangedState(); + mBoundsArray->setChangedState(); + for (PxU32 i = 0; i < nbKinematics; ++i) + { + Sc::BodySim* bodySim = kinematics[i]->getSim(); + + if ((i+16) < nbKinematics) + { + Ps::prefetchLine(kinematics[i + 16]); + if ((i + 8) < nbKinematics) + { + Ps::prefetchLine(kinematics[i + 8]->getSim()); + } + if ((i + 4) < nbKinematics) + { + Ps::prefetchLine(kinematics[i + 4]->getSim()->getElements_()); + } + } + + Sc::ElementSim* current = bodySim->getElements_(); + while(current) + { + Sc::ShapeSim* sim = static_cast(current); + //KS - TODO - can we parallelize this? The problem with parallelizing is that it's a bit operation, + //so we would either need to use atomic operations or have some high-level concept that guarantees + //that threads don't write to the same word in the map simultaneously + if (sim->getFlags()&PxU32(PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) + { + changedAABBMap.set(sim->getElementID()); + } + current = current->mNextInActor; + } + + mSimulationController->updateDynamic(false, bodySim->getNodeIndex()); + } + } +} + +class ConstraintProjectionTask : public Cm::Task +{ +private: + ConstraintProjectionTask& operator = (const ConstraintProjectionTask&); + +public: + ConstraintProjectionTask(Sc::ConstraintGroupNode* const* projectionRoots, PxU32 projectionRootCount, Ps::Array& projectedBodies, PxsContext* llContext) : + Cm::Task (llContext->getContextId()), + mProjectionRoots (projectionRoots), + mProjectionRootCount(projectionRootCount), + mProjectedBodies (projectedBodies), + mLLContext (llContext) + { + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("ConstraintProjection", mContextID); + PxcNpThreadContext* context = mLLContext->getNpThreadContext(); + Ps::Array& tempArray = context->mBodySimPool; + tempArray.forceSize_Unsafe(0); + for(PxU32 i=0; i < mProjectionRootCount; i++) + { + PX_ASSERT(mProjectionRoots[i]->hasProjectionTreeRoot()); // else, it must not be in the projection root list + Sc::ConstraintGroupNode::projectPose(*mProjectionRoots[i], tempArray); + mProjectionRoots[i]->clearFlag(Sc::ConstraintGroupNode::eIN_PROJECTION_PASS_LIST); + } + + if (tempArray.size() > 0) + { + mLLContext->getLock().lock(); + for (PxU32 a = 0; a < tempArray.size(); ++a) + mProjectedBodies.pushBack(tempArray[a]); + mLLContext->getLock().unlock(); + } + + mLLContext->putNpThreadContext(context); + } + + virtual const char* getName() const + { + return "ScScene.constraintProjectionWork"; + } + +public: + static const PxU32 sProjectingConstraintsPerTask = 256; // just a guideline, will not match exactly most of the time + +private: + Sc::ConstraintGroupNode* const* mProjectionRoots; + const PxU32 mProjectionRootCount; + Ps::Array& mProjectedBodies; + PxsContext* mLLContext; +}; + +void Sc::Scene::constraintProjection(PxBaseTask* continuation) +{ + if (mConstraints.size() == 0) + return; + PxU32 constraintGroupRootCount = 0; + //BodyCore*const* activeBodies = getActiveBodiesArray(); + //PxU32 activeBodyCount = getNumActiveBodies(); + IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + PxU32 activeBodyCount = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + const IG::NodeIndex* activeNodeIds = islandSim.getActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + PX_ASSERT(!mTmpConstraintGroupRootBuffer); + PxU32 index = 0; + + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + if(activeBodyCount) + { + mTmpConstraintGroupRootBuffer = reinterpret_cast(mLLContext->getScratchAllocator().alloc(sizeof(ConstraintGroupNode*) * activeBodyCount, true)); + if(mTmpConstraintGroupRootBuffer) + { + while(activeBodyCount--) + { + PxsRigidBody* rBody = islandSim.getRigidBody(activeNodeIds[index++]); + + Sc::BodySim* sim = reinterpret_cast(reinterpret_cast(rBody) - rigidBodyOffset); + //This move to PxgPostSolveWorkerTask for the gpu dynamic + //bodySim->sleepCheck(mDt, mOneOverDt, mEnableStabilization); + + if(sim->getConstraintGroup()) + { + ConstraintGroupNode& root = sim->getConstraintGroup()->getRoot(); + if(!root.readFlag(ConstraintGroupNode::eIN_PROJECTION_PASS_LIST) && root.hasProjectionTreeRoot()) + { + mTmpConstraintGroupRootBuffer[constraintGroupRootCount++] = &root; + root.raiseFlag(ConstraintGroupNode::eIN_PROJECTION_PASS_LIST); + } + } + } + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + PxU32 constraintsToProjectCount = 0; + PxU32 startIndex = 0; + for(PxU32 i=0; i < constraintGroupRootCount; i++) + { + ConstraintGroupNode* root = mTmpConstraintGroupRootBuffer[i]; + + constraintsToProjectCount += root->getProjectionCountHint(); // for load balancing + if (constraintsToProjectCount >= ConstraintProjectionTask::sProjectingConstraintsPerTask) + { + ConstraintProjectionTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ConstraintProjectionTask)), + ConstraintProjectionTask(mTmpConstraintGroupRootBuffer + startIndex, i - startIndex + 1, mProjectedBodies, mLLContext)); + task->setContinuation(continuation); + task->removeReference(); + + constraintsToProjectCount = 0; + startIndex = i + 1; + } + } + + if (constraintsToProjectCount) + { + PX_ASSERT(startIndex < constraintGroupRootCount); + + ConstraintProjectionTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ConstraintProjectionTask)), + ConstraintProjectionTask(mTmpConstraintGroupRootBuffer + startIndex, constraintGroupRootCount - startIndex, mProjectedBodies, mLLContext)); + task->setContinuation(continuation); + task->removeReference(); + } + } + else + { + getFoundation().getErrorCallback().reportError(PxErrorCode::eOUT_OF_MEMORY, "List for collecting constraint projection roots could not be allocated. No projection will take place.", __FILE__, __LINE__); + } + } +} + +void Sc::Scene::postSolver(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sc::Scene::postSolver", getContextId()); + PxcNpMemBlockPool& blockPool = mLLContext->getNpMemBlockPool(); + + //Merge... + mDynamicsContext->mergeResults(); + blockPool.releaseConstraintMemory(); + //Swap friction! + blockPool.swapFrictionStreams(); + + mCcdBodies.clear(); + mProjectedBodies.clear(); + +#if PX_ENABLE_SIM_STATS + mLLContext->getSimStats().mPeakConstraintBlockAllocations = blockPool.getPeakConstraintBlockCount(); +#endif + + mConstraintProjection.setContinuation(continuation); + + integrateKinematicPose(); + + mConstraintProjection.removeReference(); + + //afterIntegration(continuation); +} + +void Sc::Scene::postCCDPass(PxBaseTask* /*continuation*/) +{ + // - Performs sleep check + // - Updates touch flags + + PxU32 currentPass = mCCDContext->getCurrentCCDPass(); + PX_ASSERT(currentPass > 0); // to make sure changes to the CCD pass counting get noticed. For contact reports, 0 means discrete collision phase. + + int newTouchCount, lostTouchCount, ccdTouchCount; + mLLContext->getManagerTouchEventCount(&newTouchCount, &lostTouchCount, &ccdTouchCount); + PX_ALLOCA(newTouches, PxvContactManagerTouchEvent, newTouchCount); + PX_ALLOCA(lostTouches, PxvContactManagerTouchEvent, lostTouchCount); + PX_ALLOCA(ccdTouches, PxvContactManagerTouchEvent, ccdTouchCount); + + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + // Note: For contact notifications it is important that the new touch pairs get processed before the lost touch pairs. + // This allows to know for sure if a pair of actors lost all touch (see eACTOR_PAIR_LOST_TOUCH). + mLLContext->fillManagerTouchEvents(newTouches, newTouchCount, lostTouches, lostTouchCount, ccdTouches, ccdTouchCount); + for(PxI32 i=0; i(newTouches[i].userData); + PX_ASSERT(si); + mNPhaseCore->managerNewTouch(*si); + si->managerNewTouch(currentPass, true, outputs, useAdaptiveForce); + if (!si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) + { + mSimpleIslandManager->setEdgeConnected(si->getEdgeIndex()); + } + } + for(PxI32 i=0; i(lostTouches[i].userData); + PX_ASSERT(si); + if (si->managerLostTouch(currentPass, true, outputs, useAdaptiveForce) && !si->readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) + addToLostTouchList(si->getShape0().getBodySim(), si->getShape1().getBodySim()); + + mSimpleIslandManager->setEdgeDisconnected(si->getEdgeIndex()); + } + for(PxI32 i=0; i(ccdTouches[i].userData); + PX_ASSERT(si); + si->sendCCDRetouch(currentPass, outputs); + } + checkForceThresholdContactEvents(currentPass); + { + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + + for (PxU32 i = 0, s = mCcdBodies.size(); i < s; i++) + { + BodySim*const body = mCcdBodies[i]; + if(i+8 < s) + Ps::prefetch(mCcdBodies[i+8], 512); + + PX_ASSERT(body->getBody2World().p.isFinite()); + PX_ASSERT(body->getBody2World().q.isFinite()); + + body->updateCached(&changedAABBMgrActorHandles); + } + + ArticulationCore* const* articList = mArticulations.getEntries(); + for(PxU32 i=0;igetSim()->updateCached(&changedAABBMgrActorHandles); + } + } +} + +void Sc::Scene::finalizationPhase(PxBaseTask* /*continuation*/) +{ + PX_PROFILE_ZONE("Sim.sceneFinalization", getContextId()); + + if (mCCDContext) + { + //KS - force simulation controller to update any bodies updated by the CCD. When running GPU simulation, this would be required + //to ensure that cached body states are updated + const PxU32 nbUpdatedBodies = mCCDContext->getNumUpdatedBodies(); + PxsRigidBody*const* updatedBodies = mCCDContext->getUpdatedBodies(); + + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + for (PxU32 a = 0; a < nbUpdatedBodies; ++a) + { + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(updatedBodies[a]) - rigidBodyOffset); + mSimulationController->updateDynamic(bodySim->isArticulationLink(), bodySim->getNodeIndex()); + } + + mCCDContext->clearUpdatedBodies(); + } + + if (mTmpConstraintGroupRootBuffer) + { + mLLContext->getScratchAllocator().free(mTmpConstraintGroupRootBuffer); + mTmpConstraintGroupRootBuffer = NULL; + } + + fireOnAdvanceCallback(); // placed here because it needs to be done after sleep check and afrer potential CCD passes + + checkConstraintBreakage(); // Performs breakage tests on breakable constraints + + PX_PROFILE_STOP_CROSSTHREAD("Basic.rigidBodySolver", getContextId()); + + //KS - process deleted elementIDs now - before GPU particles releases elements, causing issues - sschirm: particles are gone... + mElementIDPool->processPendingReleases(); + mElementIDPool->clearDeletedIDMap(); + + visualizeEndStep(); + + mTaskPool.clear(); + + mReportShapePairTimeStamp++; // important to do this before fetchResults() is called to make sure that delayed deleted actors/shapes get + // separate pair entries in contact reports +} + +void Sc::Scene::postReportsCleanup() +{ + mShapeIDTracker->processPendingReleases(); + mShapeIDTracker->clearDeletedIDMap(); + + mRigidIDTracker->processPendingReleases(); + mRigidIDTracker->clearDeletedIDMap(); + + mConstraintIDTracker->processPendingReleases(); + mConstraintIDTracker->clearDeletedIDMap(); +} + +void Sc::Scene::syncSceneQueryBounds(SqBoundsSync& sync, SqRefFinder& finder) +{ + mSqBoundsManager->syncBounds(sync, finder, mBoundsArray->begin(), getContextId(), mDirtyShapeSimMap); +} + +class ScKinematicUpdateTask : public Cm::Task +{ + Sc::BodyCore*const* mKinematics; + PxU32 mNbKinematics; + PxReal mOneOverDt; + +public: + static const PxU32 NbKinematicsPerTask = 1024; + + ScKinematicUpdateTask(Sc::BodyCore*const* kinematics, PxU32 nbKinematics, PxReal oneOverDt, PxU64 contextID) : + Cm::Task(contextID), mKinematics(kinematics), mNbKinematics(nbKinematics), mOneOverDt(oneOverDt) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbKinematics; ++a) + { + Sc::BodyCore* b = mKinematics[a]; + PX_ASSERT(b->getSim()->isKinematic()); + PX_ASSERT(b->getSim()->isActive()); + + b->getSim()->calculateKinematicVelocity(mOneOverDt); + } + } + + virtual const char* getName() const + { + return "ScScene.KinematicUpdateTask"; + } +}; + +class ScKinematicAddDynamicTask : public Cm::Task +{ + Sc::BodyCore*const* mKinematics; + PxU32 mNbKinematics; + PxsSimulationController& mSimulationController; + + PX_NOCOPY(ScKinematicAddDynamicTask) + +public: + + ScKinematicAddDynamicTask(Sc::BodyCore*const* kinematics, PxU32 nbKinematics, PxsSimulationController& simulationController, PxU64 contextID) : + Cm::Task(contextID), mKinematics(kinematics), mNbKinematics(nbKinematics), mSimulationController(simulationController) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbKinematics; ++a) + { + Sc::BodySim* bodySim = mKinematics[a]->getSim(); + mSimulationController.updateDynamic(false, bodySim->getNodeIndex()); + } + } + + virtual const char* getName() const + { + return "ScScene.KinematicAddDynamicTask"; + } +}; + +void Sc::Scene::kinematicsSetup(PxBaseTask* continuation) +{ + PX_UNUSED(continuation); + const PxU32 nbKinematics = getActiveKinematicBodiesCount(); + BodyCore*const* kinematics = getActiveKinematicBodies(); + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + for(PxU32 i = 0; i < nbKinematics; i += ScKinematicUpdateTask::NbKinematicsPerTask) + { + ScKinematicUpdateTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicUpdateTask)), ScKinematicUpdateTask) + (kinematics + i, PxMin(ScKinematicUpdateTask::NbKinematicsPerTask, nbKinematics - i), mOneOverDt, mContextId); + + task->setContinuation(continuation); + task->removeReference(); + } + + if((mPublicFlags & PxSceneFlag::eENABLE_GPU_DYNAMICS)) + { + ScKinematicAddDynamicTask* addTask = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicAddDynamicTask)), ScKinematicAddDynamicTask) + (kinematics, nbKinematics, *mSimulationController, mContextId); + + addTask->setContinuation(continuation); + addTask->removeReference(); + } +} + +//stepSetup is called in solve, but not collide +void Sc::Scene::stepSetupSolve(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.stepSetupSolve", getContextId()); + + kinematicsSetup(continuation); +} + +void Sc::Scene::stepSetupCollide(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.stepSetupCollide", getContextId()); + + { + PX_PROFILE_ZONE("Sim.projectionTreeUpdates", getContextId()); + mProjectionManager->processPendingUpdates(mLLContext->getScratchAllocator()); + } + + kinematicsSetup(continuation); + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + // Update all dirty interactions + mNPhaseCore->updateDirtyInteractions(outputs, mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE); + mInternalFlags &= ~(SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_DOMINANCE | SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_VISUALIZATION); +} + +void Sc::Scene::processLostTouchPairs() +{ + PX_PROFILE_ZONE("Sc::Scene::processLostTouchPairs", getContextId()); + for (PxU32 i=0; iinternalWakeUp(); + if (!deletedBody2) + mLostTouchPairs[i].body2->internalWakeUp(); + continue; + } + + // If both are sleeping, we let them sleep + // (for example, two sleeping objects touch and the user teleports one (without waking it up)) + if (!mLostTouchPairs[i].body1->isActive() && + !mLostTouchPairs[i].body2->isActive()) + { + continue; + } + + // If only one has fallen asleep, we wake them both + if (!mLostTouchPairs[i].body1->isActive() || + !mLostTouchPairs[i].body2->isActive()) + { + mLostTouchPairs[i].body1->internalWakeUp(); + mLostTouchPairs[i].body2->internalWakeUp(); + } + } + + mLostTouchPairs.clear(); + mLostTouchPairsDeletedBodyIDs.clear(); +} + +class ScBeforeSolverTask : public Cm::Task +{ +public: + static const PxU32 MaxBodiesPerTask = 256; + IG::NodeIndex mBodies[MaxBodiesPerTask]; + PxU32 mNumBodies; + const PxReal mDt; + IG::SimpleIslandManager* mIslandManager; + PxsSimulationController* mSimulationController; + bool mSimUsesAdaptiveForce; + +public: + + ScBeforeSolverTask(PxReal dt, IG::SimpleIslandManager* islandManager, PxsSimulationController* simulationController, PxU64 contextID, bool simUsesAdaptiveForce) : + Cm::Task (contextID), + mDt (dt), + mIslandManager (islandManager), + mSimulationController (simulationController), + mSimUsesAdaptiveForce (simUsesAdaptiveForce) + { + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("Sim.ScBeforeSolverTask", mContextID); + const IG::IslandSim& islandSim = mIslandManager->getAccurateIslandSim(); + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + PxsRigidBody* updatedBodySims[MaxBodiesPerTask]; + PxU32 updatedBodyNodeIndices[MaxBodiesPerTask]; + PxU32 nbUpdatedBodySims = 0; + + for(PxU32 i = 0; i < mNumBodies; i++) + { + IG::NodeIndex index = mBodies[i]; + if(islandSim.getActiveNodeIndex(index) != IG_INVALID_NODE) + { + if (islandSim.getNode(index).mType == IG::Node::eRIGID_BODY_TYPE) + { + PxsRigidBody* body = islandSim.getRigidBody(index); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(body) - rigidBodyOffset); + bodySim->updateForces(mDt, updatedBodySims, updatedBodyNodeIndices, nbUpdatedBodySims, NULL, false, mSimUsesAdaptiveForce); + } + } + } + + if(nbUpdatedBodySims) + mSimulationController->updateBodies(updatedBodySims, updatedBodyNodeIndices, nbUpdatedBodySims); + } + + virtual const char* getName() const + { + return "ScScene.beforeSolver"; + } + +private: + ScBeforeSolverTask& operator = (const ScBeforeSolverTask&); +}; + + +class ScArticBeforeSolverTask : public Cm::Task +{ +public: + const IG::NodeIndex* const mArticIndices; + PxU32 mNumArticulations; + const PxReal mDt; + IG::SimpleIslandManager* mIslandManager; + bool mSimUsesAdaptiveForce; + +public: + + ScArticBeforeSolverTask(const IG::NodeIndex* const articIndices, PxU32 nbArtics, PxReal dt, IG::SimpleIslandManager* islandManager, PxU64 contextID, bool simUsesAdaptiveForce) : + Cm::Task(contextID), + mArticIndices(articIndices), + mNumArticulations(nbArtics), + mDt(dt), + mIslandManager(islandManager), + mSimUsesAdaptiveForce(simUsesAdaptiveForce) + { + } + + virtual void runInternal() + { + PX_PROFILE_ZONE("Sim.ScArticBeforeSolverTask", mContextID); + const IG::IslandSim& islandSim = mIslandManager->getAccurateIslandSim(); + + for (PxU32 a = 0; a < mNumArticulations; ++a) + { + Sc::ArticulationSim* PX_RESTRICT articSim = islandSim.getLLArticulation(mArticIndices[a])->getArticulationSim(); + articSim->checkResize(); + articSim->updateForces(mDt, mSimUsesAdaptiveForce); + articSim->saveLastCCDTransform(); + } + } + + virtual const char* getName() const + { + return "ScScene.ScArticBeforeSolverTask"; + } + +private: + ScArticBeforeSolverTask& operator = (const ScArticBeforeSolverTask&); +}; + + +void Sc::Scene::beforeSolver(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sim.updateForces", getContextId()); + + // Note: For contact notifications it is important that force threshold checks are done after new/lost touches have been processed + // because pairs might get added to the list processed below + + // Atoms that passed contact force threshold + ThresholdStream& thresholdStream = mDynamicsContext->getThresholdStream(); + thresholdStream.clear(); + + const IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + const PxU32 nbActiveBodies = islandSim.getNbActiveNodes(IG::Node::eRIGID_BODY_TYPE); + + mNumDeactivatingNodes[IG::Node::eRIGID_BODY_TYPE] = 0;//islandSim.getNbNodesToDeactivate(IG::Node::eRIGID_BODY_TYPE); + mNumDeactivatingNodes[IG::Node::eARTICULATION_TYPE] = 0;//islandSim.getNbNodesToDeactivate(IG::Node::eARTICULATION_TYPE); + + const PxU32 MaxBodiesPerTask = ScBeforeSolverTask::MaxBodiesPerTask; + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + mSimulationController->reserve(nbActiveBodies); + + bool simUsesAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + { + Cm::BitMap::Iterator iter(mVelocityModifyMap); + + for (PxU32 i = iter.getNext(); i != Cm::BitMap::Iterator::DONE; /*i = iter.getNext()*/) + { + ScBeforeSolverTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScBeforeSolverTask)), ScBeforeSolverTask(mDt, mSimpleIslandManager, mSimulationController, getContextId(), simUsesAdaptiveForce)); + PxU32 count = 0; + for (; count < MaxBodiesPerTask && i != Cm::BitMap::Iterator::DONE; i = iter.getNext()) + { + PxsRigidBody* body = islandSim.getRigidBody(IG::NodeIndex(i)); + bool retainsAccelerations = false; + if (body) + { + task->mBodies[count++] = IG::NodeIndex(i); + + retainsAccelerations = (body->mCore->mFlags & PxRigidBodyFlag::eRETAIN_ACCELERATIONS); + + } + + if(!retainsAccelerations) + { + mVelocityModifyMap.reset(i); + } + } + task->mNumBodies = count; + task->setContinuation(continuation); + task->removeReference(); + } + } + + const PxU32 nbActiveArticulations = islandSim.getNbActiveNodes(IG::Node::eARTICULATION_TYPE); + const IG::NodeIndex* const articIndices = islandSim.getActiveNodes(IG::Node::eARTICULATION_TYPE); + + const PxU32 nbArticsPerTask = 8; + + for(PxU32 a = 0; a < nbActiveArticulations; a+= nbArticsPerTask) + { + const PxU32 nbToProcess = PxMin(PxU32(nbActiveArticulations - a), nbArticsPerTask); + ScArticBeforeSolverTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScArticBeforeSolverTask)), ScArticBeforeSolverTask(articIndices+a, nbToProcess, + mDt, mSimpleIslandManager, getContextId(), simUsesAdaptiveForce)); + + task->setContinuation(continuation); + task->removeReference(); + } + + mBodyGravityDirty = false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class UpdatProjectedPoseTask : public Cm::Task +{ + Sc::BodySim** mProjectedBodies; + PxU32 mNbBodiesToProcess; + +public: + UpdatProjectedPoseTask(PxU64 contextID, Sc::BodySim** projectedBodies, PxU32 nbBodiesToProcess) : + Cm::Task (contextID), + mProjectedBodies (projectedBodies), + mNbBodiesToProcess (nbBodiesToProcess) + { + } + + virtual void runInternal() + { + for (PxU32 a = 0; a < mNbBodiesToProcess; ++a) + { + mProjectedBodies[a]->updateCached(NULL); + } + } + + virtual const char* getName() const + { + return "ScScene.UpdatProjectedPoseTask"; + } +}; + +class UpdateArticulationTask : public Cm::Task +{ + Sc::ArticulationCore* const* mArticList; + PxU32 mNbArticulations; + PxReal mDt; + +public: + + UpdateArticulationTask(PxU64 contextId, Sc::ArticulationCore* const* articList, PxU32 nbArticulations, PxReal dt) : Cm::Task(contextId), mArticList(articList), mNbArticulations(nbArticulations), + mDt(dt) + { + } + + virtual const char* getName() const { return "UpdateArticulationTask"; } + + virtual void runInternal() + { + for (PxU32 i = 0; i < mNbArticulations; ++i) + { + Sc::ArticulationSim* articSim = mArticList[i]->getSim(); + articSim->sleepCheck(mDt); + articSim->updateCached(NULL); + } + } +}; + +void Sc::Scene::afterIntegration(PxBaseTask* continuation) +{ + PX_PROFILE_ZONE("Sc::Scene::afterIntegration", getContextId()); + mLLContext->getTransformCache().resetChangedState(); //Reset the changed state. If anything outside of the GPU kernels updates any shape's transforms, this will be raised again + getBoundsArray().resetChangedState(); + + PxsTransformCache& cache = getLowLevelContext()->getTransformCache(); + Bp::BoundsArray& boundArray = getBoundsArray(); + + { + PX_PROFILE_ZONE("AfterIntegration::lockStage", getContextId()); + mLLContext->getLock().lock(); + + + mSimulationController->udpateScBodyAndShapeSim(cache, boundArray, continuation); + + const IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + const PxU32 rigidBodyOffset = Sc::BodySim::getRigidBodyOffset(); + + const PxU32 numBodiesToDeactivate = islandSim.getNbNodesToDeactivate(IG::Node::eRIGID_BODY_TYPE); + + const IG::NodeIndex*const deactivatingIndices = islandSim.getNodesToDeactivate(IG::Node::eRIGID_BODY_TYPE); + + PxU32 previousNumBodiesToDeactivate = mNumDeactivatingNodes[IG::Node::eRIGID_BODY_TYPE]; + + { + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + PX_PROFILE_ZONE("AfterIntegration::deactivateStage", getContextId()); + for (PxU32 i = previousNumBodiesToDeactivate; i < numBodiesToDeactivate; i++) + { + PxsRigidBody* rigid = islandSim.getRigidBody(deactivatingIndices[i]); + Sc::BodySim* bodySim = reinterpret_cast(reinterpret_cast(rigid) - rigidBodyOffset); + //we need to set the rigid body back to the previous pose for the deactivated objects. This emulates the previous behavior where island gen ran before the solver, ensuring + //that bodies that should be deactivated this frame never reach the solver. We now run the solver in parallel with island gen, so objects that should be deactivated this frame + //still reach the solver and are integrated. However, on the frame when they should be deactivated, we roll back to their state at the beginning of the frame to ensure that the + //user perceives the same behavior as before. + + PxsBodyCore& bodyCore = bodySim->getBodyCore().getCore(); + + //if(!islandSim.getNode(bodySim->getNodeIndex()).isActive()) + rigid->setPose(rigid->getLastCCDTransform()); + + + bodySim->updateCached(&changedAABBMgrActorHandles); + mSimulationController->updateDynamic(bodySim->isArticulationLink(), bodySim->getNodeIndex()); + + //solver is running in parallel with IG(so solver might solving the body which IG identify as deactivatedNodes). After we moved sleepCheck into the solver after integration, sleepChecks + //might have processed bodies that are now considered deactivated. This could have resulted in either freezing or unfreezing one of these bodies this frame, so we need to process those + //events to ensure that the SqManager's bounds arrays are consistently maintained. Also, we need to clear the frame flags for these bodies. + + if (rigid->isFreezeThisFrame()) + bodySim->freezeTransforms(&mAABBManager->getChangedAABBMgActorHandleMap()); + /*else if(bodyCore.isUnfreezeThisFrame()) + bodySim->createSqBounds();*/ + + //PX_ASSERT(bodyCore.wakeCounter == 0.0f); + //KS - the IG deactivates bodies in parallel with the solver. It appears that under certain circumstances, the solver's integration (which performs + //sleep checks) could decide that the body is no longer a candidate for sleeping on the same frame that the island gen decides to deactivate the island + //that the body is contained in. This is a rare occurrence but the behavior we want to emulate is that of IG running before solver so we should therefore + //permit the IG to make the authoritative decision over whether the body should be active or inactive. + bodyCore.wakeCounter = 0.0f; + bodyCore.linearVelocity = PxVec3(0.0f); + bodyCore.angularVelocity = PxVec3(0.0f); + + rigid->clearAllFrameFlags(); + + /*Sc::ShapeSim* sim; + for (Sc::ShapeIterator iterator(*bodySim); (sim = iterator.getNext()) != NULL;) + { + if (sim->isInBroadPhase()) + changedAABBMgrActorHandles.growAndSet(sim->getElementID()); + }*/ + } + } + + const PxU32 maxBodiesPerTask = 256; + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + { + PX_PROFILE_ZONE("AfterIntegration::dispatchTasks", getContextId()); + for (PxU32 a = 0; a < mProjectedBodies.size(); a += maxBodiesPerTask) + { + UpdatProjectedPoseTask* task = + PX_PLACEMENT_NEW(flushPool.allocate(sizeof(UpdatProjectedPoseTask)), UpdatProjectedPoseTask)(getContextId(), &mProjectedBodies[a], PxMin(maxBodiesPerTask, mProjectedBodies.size() - a)); + task->setContinuation(continuation); + task->removeReference(); + } + } + + { + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + PX_PROFILE_ZONE("AfterIntegration::growAndSet", getContextId()); + for (PxU32 a = 0; a < mProjectedBodies.size(); ++a) + { + if (!mProjectedBodies[a]->isFrozen()) + { + Sc::ElementSim* current = mProjectedBodies[a]->getElements_(); + while(current) + { + Sc::ShapeSim* sim = static_cast(current); + if(sim->isInBroadPhase()) + changedAABBMgrActorHandles.growAndSet(sim->getElementID()); + current = current->mNextInActor; + } + } + } + } + { + PX_PROFILE_ZONE("AfterIntegration::managerAndDynamic", getContextId()); + const PxU32 unrollSize = 256; + for (PxU32 a = 0; a < mProjectedBodies.size(); a += unrollSize) + { + PxsRigidBody* tempBodies[unrollSize]; + PxU32 nodeIds[unrollSize]; + const PxU32 nbToProcess = PxMin(unrollSize, mProjectedBodies.size() - a); + for (PxU32 i = 0; i < nbToProcess; ++i) + { + tempBodies[i] = &mProjectedBodies[a + i]->getLowLevelBody(); + nodeIds[i] = mProjectedBodies[a + i]->getNodeIndex().index(); + } + //KS - it seems that grabbing the CUDA context/releasing it is expensive so we should minimize how + //frequently we do that. Batch processing like this helps + mSimulationController->addDynamics(tempBodies, nodeIds, nbToProcess); + } + } + + updateKinematicCached(continuation); + + mLLContext->getLock().unlock(); + } + + //KS - TODO - parallelize this bit!!!!! + if(mArticulations.size()) + { + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + ArticulationCore* const* articList = mArticulations.getEntries(); + + const PxU32 nbArticulationsPerTask = 8; + const PxU32 articCount = mArticulations.size(); + for (PxU32 i = 0; i < articCount; i += nbArticulationsPerTask) + { + UpdateArticulationTask* task = + PX_PLACEMENT_NEW(flushPool.allocate(sizeof(UpdateArticulationTask)), UpdateArticulationTask)(getContextId(), articList + i, PxMin(nbArticulationsPerTask, PxU32(articCount - i)), mDt); + task->setContinuation(continuation); + task->removeReference(); + } + + mLLContext->getLock().lock(); + Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); + + + Sc::BodySim* ccdBodySims[DY_ARTICULATION_MAX_SIZE]; + + for(PxU32 i=0;igetSim(); + + //KS - check links for CCD flags and add to mCcdBodies list if required.... + PxU32 nbIdx = articSim->getCCDLinks(ccdBodySims); + + for (PxU32 a = 0; a < nbIdx; ++a) + { + mCcdBodies.pushBack(ccdBodySims[a]); + } + + articSim->markShapesUpdated(&changedAABBMgrActorHandles); + + } + mLLContext->getLock().unlock(); + } + + PX_PROFILE_STOP_CROSSTHREAD("Basic.dynamics", getContextId()); + + checkForceThresholdContactEvents(0); +} + +void Sc::Scene::checkForceThresholdContactEvents(const PxU32 ccdPass) +{ + PX_PROFILE_ZONE("Sim.checkForceThresholdContactEvents", getContextId()); + + // Note: For contact notifications it is important that force threshold checks are done after new/lost touches have been processed + // because pairs might get added to the list processed below + + // Bodies that passed contact force threshold + + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + ThresholdStream& thresholdStream = mDynamicsContext->getForceChangedThresholdStream(); + + const PxU32 nbThresholdElements = thresholdStream.size(); + + for (PxU32 i = 0; i< nbThresholdElements; ++i) + { + ThresholdStreamElement& elem = thresholdStream[i]; + ShapeInteraction* si = elem.shapeInteraction; + + //If there is a shapeInteraction and the shapeInteraction points to a contactManager (i.e. the CM was not destroyed in parallel with the solver) + if (si != NULL) + { + PxU32 pairFlags = si->getPairFlags(); + if (pairFlags & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS) + { + si->swapAndClearForceThresholdExceeded(); + + if (elem.accumulatedForce > elem.threshold * mDt) + { + si->raiseFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_NOW); + + PX_ASSERT(si->hasTouch()); + + //If the accumulatedForce is large than the threshold in the current frame and the accumulatedForce is less than the threshold in the previous frame, + //and the user request notify for found event, we will raise eNOTIFY_THRESHOLD_FORCE_FOUND + if ((!si->readFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_BEFORE)) && (pairFlags & PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND)) + { + si->processUserNotification(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND, 0, false, ccdPass, false, outputs); + } + else if (si->readFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_BEFORE) && (pairFlags & PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS)) + { + si->processUserNotification(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS, 0, false, ccdPass, false, outputs); + } + } + else + { + //If the accumulatedForce is less than the threshold in the current frame and the accumulatedForce is large than the threshold in the previous frame, + //and the user request notify for found event, we will raise eNOTIFY_THRESHOLD_FORCE_LOST + if (si->readFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_BEFORE) && (pairFlags & PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST)) + { + si->processUserNotification(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST, 0, false, ccdPass, false, outputs); + } + } + } + } + } +} + +void Sc::Scene::endStep() +{ + mTimeStamp++; +// INVALID_SLEEP_COUNTER is 0xffffffff. Therefore the last bit is masked. Look at Body::isForcedToSleep() for example. +// if(timeStamp==PX_INVALID_U32) timeStamp = 0; // Reserve INVALID_ID for something else + mTimeStamp &= 0x7fffffff; + + mReportShapePairTimeStamp++; // to make sure that deleted shapes/actors after fetchResults() create new report pairs +} + +void Sc::Scene::resizeReleasedBodyIDMaps(PxU32 maxActors, PxU32 numActors) +{ + mLostTouchPairsDeletedBodyIDs.resize(maxActors); + mRigidIDTracker->resizeDeletedIDMap(maxActors,numActors); + mShapeIDTracker->resizeDeletedIDMap(maxActors,numActors); +} + +/** +Render objects before simulation starts +*/ +void Sc::Scene::visualizeStartStep() +{ + PX_PROFILE_ZONE("Sim.visualizeStartStep", getContextId()); + +#if PX_ENABLE_DEBUG_VISUALIZATION + if(!getVisualizationScale()) + { + // make sure visualization inside simulate was skipped + PX_ASSERT(getRenderBuffer().empty()); + return; // early out if visualization scale is 0 + } + + Cm::RenderOutput out(getRenderBuffer()); + + if(getVisualizationParameter(PxVisualizationParameter::eCOLLISION_COMPOUNDS)) + mAABBManager->visualize(out); + + // Visualize joints + Sc::ConstraintCore*const * constraints = mConstraints.getEntries(); + for(PxU32 i=0, size = mConstraints.size();igetSim()->visualize(getRenderBuffer()); + + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + mNPhaseCore->visualize(out, outputs); +#endif +} + +/** +Render objects after simulation finished. Use this for data that is only available after simulation. +*/ +void Sc::Scene::visualizeEndStep() +{ + PX_PROFILE_ZONE("Sim.visualizeEndStep", getContextId()); + +#if PX_ENABLE_DEBUG_VISUALIZATION + if(!getVisualizationScale()) + { + // make sure any visualization before was skipped + PX_ASSERT(getRenderBuffer().empty()); + return; // early out if visualization scale is 0 + } + + Cm::RenderOutput out(getRenderBuffer()); +#endif +} + +void Sc::Scene::collectPostSolverVelocitiesBeforeCCD() +{ + if (mContactReportsNeedPostSolverVelocity) + { + ActorPairReport*const* actorPairs = mNPhaseCore->getContactReportActorPairs(); + PxU32 nbActorPairs = mNPhaseCore->getNbContactReportActorPairs(); + for(PxU32 i=0; i < nbActorPairs; i++) + { + if (i < (nbActorPairs - 1)) + Ps::prefetchLine(actorPairs[i+1]); + + ActorPairReport* aPair = actorPairs[i]; + + ContactStreamManager& cs = aPair->getContactStreamManager(); + + PxU32 streamManagerFlag = cs.getFlags(); + if(streamManagerFlag & ContactStreamManagerFlag::eINVALID_STREAM) + continue; + + PxU8* stream = mNPhaseCore->getContactReportPairData(cs.bufferIndex); + + if(i + 1 < nbActorPairs) + Ps::prefetch(&(actorPairs[i+1]->getContactStreamManager())); + + if (!cs.extraDataSize) + continue; + else if (streamManagerFlag & ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY) + cs.setContactReportPostSolverVelocity(stream, aPair->getActorA(), aPair->getActorB()); + } + } +} + +void Sc::Scene::finalizeContactStreamAndCreateHeader(PxContactPairHeader& header, const ActorPairReport& aPair, ContactStreamManager& cs, PxU32 removedShapeTestMask) +{ + PxU8* stream = mNPhaseCore->getContactReportPairData(cs.bufferIndex); + PxU32 streamManagerFlag = cs.getFlags(); + ContactShapePair* contactPairs = cs.getShapePairs(stream); + const PxU16 nbShapePairs = cs.currentPairCount; + PX_ASSERT(nbShapePairs > 0); + + if (streamManagerFlag & removedShapeTestMask) + { + // At least one shape of this actor pair has been deleted. Need to traverse the contact buffer, + // find the pairs which contain deleted shapes and set the flags accordingly. + + ContactStreamManager::convertDeletedShapesInContactStream(contactPairs, nbShapePairs, getShapeIDTracker()); + } + PX_ASSERT(contactPairs); + + ObjectIDTracker& RigidIDTracker = getRigidIDTracker(); + header.actors[0] = static_cast(aPair.getPxActorA()); + header.actors[1] = static_cast(aPair.getPxActorB()); + PxU16 headerFlags = 0; + if (RigidIDTracker.isDeletedID(aPair.getActorAID())) + headerFlags |= PxContactPairHeaderFlag::eREMOVED_ACTOR_0; + if (RigidIDTracker.isDeletedID(aPair.getActorBID())) + headerFlags |= PxContactPairHeaderFlag::eREMOVED_ACTOR_1; + header.flags = PxContactPairHeaderFlags(headerFlags); + header.pairs = reinterpret_cast(contactPairs); + header.nbPairs = nbShapePairs; + + PxU16 extraDataSize = cs.extraDataSize; + if (!extraDataSize) + header.extraDataStream = NULL; + else + { + PX_ASSERT(extraDataSize >= sizeof(ContactStreamHeader)); + extraDataSize -= sizeof(ContactStreamHeader); + header.extraDataStream = stream + sizeof(ContactStreamHeader); + + if (streamManagerFlag & ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY) + { + PX_ASSERT(!(headerFlags & Ps::to16(PxContactPairHeaderFlag::eREMOVED_ACTOR_0 | PxContactPairHeaderFlag::eREMOVED_ACTOR_1))); + cs.setContactReportPostSolverVelocity(stream, aPair.getActorA(), aPair.getActorB()); + } + } + header.extraDataStreamSize = extraDataSize; +} + +const Ps::Array& Sc::Scene::getQueuedContactPairHeaders() +{ + // if buffered shape removals occured, then the criteria for testing the contact stream for events with removed shape pointers needs to be more strict. + PX_ASSERT(mRemovedShapeCountAtSimStart <= mShapeIDTracker->getDeletedIDCount()); + bool reducedTestForRemovedShapes = mRemovedShapeCountAtSimStart == mShapeIDTracker->getDeletedIDCount(); + const PxU32 removedShapeTestMask = PxU32((reducedTestForRemovedShapes ? ContactStreamManagerFlag::eTEST_FOR_REMOVED_SHAPES : (ContactStreamManagerFlag::eTEST_FOR_REMOVED_SHAPES | ContactStreamManagerFlag::eHAS_PAIRS_THAT_LOST_TOUCH))); + + ActorPairReport*const* actorPairs = mNPhaseCore->getContactReportActorPairs(); + PxU32 nbActorPairs = mNPhaseCore->getNbContactReportActorPairs(); + mQueuedContactPairHeaders.reserve(nbActorPairs); + mQueuedContactPairHeaders.clear(); + + for (PxU32 i = 0; i < nbActorPairs; i++) + { + if (i < (nbActorPairs - 1)) + Ps::prefetchLine(actorPairs[i + 1]); + + ActorPairReport* aPair = actorPairs[i]; + ContactStreamManager& cs = aPair->getContactStreamManager(); + if (cs.getFlags() & ContactStreamManagerFlag::eINVALID_STREAM) + continue; + + if (i + 1 < nbActorPairs) + Ps::prefetch(&(actorPairs[i + 1]->getContactStreamManager())); + + PxContactPairHeader &pairHeader = mQueuedContactPairHeaders.insert(); + finalizeContactStreamAndCreateHeader(pairHeader, *aPair, cs, removedShapeTestMask); + + cs.maxPairCount = cs.currentPairCount; + cs.setMaxExtraDataSize(cs.extraDataSize); + } + + return mQueuedContactPairHeaders; +} + +/* +Threading: called in the context of the user thread, but only after the physics thread has finished its run +*/ +void Sc::Scene::fireQueuedContactCallbacks(bool asPartOfFlush) +{ + if(mSimulationEventCallback) + { + // if buffered shape removals occured, then the criteria for testing the contact stream for events with removed shape pointers needs to be more strict. + PX_ASSERT(asPartOfFlush || (mRemovedShapeCountAtSimStart <= mShapeIDTracker->getDeletedIDCount())); + bool reducedTestForRemovedShapes = asPartOfFlush || (mRemovedShapeCountAtSimStart == mShapeIDTracker->getDeletedIDCount()); + const PxU32 removedShapeTestMask = PxU32(reducedTestForRemovedShapes ? ContactStreamManagerFlag::eTEST_FOR_REMOVED_SHAPES : (ContactStreamManagerFlag::eTEST_FOR_REMOVED_SHAPES | ContactStreamManagerFlag::eHAS_PAIRS_THAT_LOST_TOUCH)); + + ActorPairReport*const* actorPairs = mNPhaseCore->getContactReportActorPairs(); + PxU32 nbActorPairs = mNPhaseCore->getNbContactReportActorPairs(); + for(PxU32 i=0; i < nbActorPairs; i++) + { + if (i < (nbActorPairs - 1)) + Ps::prefetchLine(actorPairs[i+1]); + + ActorPairReport* aPair = actorPairs[i]; + ContactStreamManager* cs = &aPair->getContactStreamManager(); + if (cs == NULL || cs->getFlags() & ContactStreamManagerFlag::eINVALID_STREAM) + continue; + + if (i + 1 < nbActorPairs) + Ps::prefetch(&(actorPairs[i+1]->getContactStreamManager())); + + PxContactPairHeader pairHeader; + finalizeContactStreamAndCreateHeader(pairHeader, *aPair, *cs, removedShapeTestMask); + + mSimulationEventCallback->onContact(pairHeader, pairHeader.pairs, pairHeader.nbPairs); + + // estimates for next frame + cs->maxPairCount = cs->currentPairCount; + cs->setMaxExtraDataSize(cs->extraDataSize); + } + } +} + +PX_FORCE_INLINE void markDeletedShapes(Sc::ObjectIDTracker& idTracker, Sc::TriggerPairExtraData& tped, PxTriggerPair& pair) +{ + PxTriggerPairFlags::InternalType flags = 0; + if (idTracker.isDeletedID(tped.shape0ID)) + flags |= PxTriggerPairFlag::eREMOVED_SHAPE_TRIGGER; + if (idTracker.isDeletedID(tped.shape1ID)) + flags |= PxTriggerPairFlag::eREMOVED_SHAPE_OTHER; + + pair.flags = PxTriggerPairFlags(flags); +} + +void Sc::Scene::fireTriggerCallbacks() +{ + // triggers + const PxU32 nbTriggerPairs = mTriggerBufferAPI.size(); + PX_ASSERT(nbTriggerPairs == mTriggerBufferExtraData->size()); + if(nbTriggerPairs) + { + // cases to take into account: + // - no simulation/trigger shape has been removed -> no need to test shape references for removed shapes + // - simulation/trigger shapes have been removed but only while the simulation was not running -> only test the events that have + // a marker for removed shapes set + // - simulation/trigger shapes have been removed while the simulation was running -> need to test all events (see explanation + // below) + // + // If buffered shape removals occured, then all trigger events need to be tested for removed shape pointers. + // An optimization like in the contact report case is not applicable here because trigger interactions do not + // have a reference to their reported events. It can happen that a trigger overlap found event is created but + // a shape of that pair gets removed while the simulation is running. When processing the lost touch from the + // shape removal, no link to the overlap found event is possible and thus it can not be marked as dirty. + const bool hasRemovedShapes = mShapeIDTracker->getDeletedIDCount() > 0; + const bool forceTestsForRemovedShapes = (mRemovedShapeCountAtSimStart < mShapeIDTracker->getDeletedIDCount()); + + if(mSimulationEventCallback) + { + if (!hasRemovedShapes) + mSimulationEventCallback->onTrigger(mTriggerBufferAPI.begin(), nbTriggerPairs); + else + { + for(PxU32 i = 0; i < nbTriggerPairs; i++) + { + PxTriggerPair& triggerPair = mTriggerBufferAPI[i]; + + if (forceTestsForRemovedShapes || (PxTriggerPairFlags::InternalType(triggerPair.flags) & TriggerPairFlag::eTEST_FOR_REMOVED_SHAPES)) + markDeletedShapes(*mShapeIDTracker, (*mTriggerBufferExtraData)[i], triggerPair); + } + + mSimulationEventCallback->onTrigger(mTriggerBufferAPI.begin(), nbTriggerPairs); + } + } + } + + // PT: clear the buffer **even when there's no simulationEventCallback**. + mTriggerBufferAPI.clear(); + mTriggerBufferExtraData->clear(); +} + +void Sc::Scene::fireBrokenConstraintCallbacks() +{ + if(!mSimulationEventCallback) + return; + + const PxU32 count = mBrokenConstraints.size(); + for(PxU32 i=0;igetSim()) // the constraint might have been removed while the simulation was running + { + PxU32 typeID = 0xffffffff; + void* externalRef = c->getPxConnector()->getExternalReference(typeID); + PX_CHECK_MSG(typeID != 0xffffffff, "onConstraintBreak: Invalid constraint type ID."); + + PxConstraintInfo constraintInfo(c->getPxConstraint(), externalRef, typeID); + mSimulationEventCallback->onConstraintBreak(&constraintInfo, 1); + } + } +} + +/* +Threading: called in the context of the user thread, but only after the physics thread has finished its run +*/ +void Sc::Scene::fireCallbacksPostSync() +{ + // + // Fire sleep & woken callbacks + // + + // A body should be either in the sleep or the woken list. If it is in both, remove it from the list it was + // least recently added to. + + if(!mSleepBodyListValid) + cleanUpSleepBodies(); + + if(!mWokeBodyListValid) + cleanUpWokenBodies(); + + if(mSimulationEventCallback) + { + // allocate temporary data + const PxU32 nbSleep = mSleepBodies.size(); + const PxU32 nbWoken = mWokeBodies.size(); + const PxU32 arrSize = PxMax(nbSleep, nbWoken); + PxActor** actors = arrSize ? reinterpret_cast(PX_ALLOC_TEMP(arrSize*sizeof(PxActor*), "PxActor*")) : NULL; + if(actors) + { + if(nbSleep) + { + PxU32 destSlot = 0; + BodyCore* const* sleepingBodies = mSleepBodies.getEntries(); + for(PxU32 i=0; igetActorFlags() & PxActorFlag::eSEND_SLEEP_NOTIFIES) + actors[destSlot++] = body->getPxActor(); + } + + if(destSlot) + mSimulationEventCallback->onSleep(actors, destSlot); + + //if (PX_DBG_IS_CONNECTED()) + //{ + // for (PxU32 i = 0; i < nbSleep; ++i) + // { + // BodyCore* body = mSleepBodies[i]; + // PX_ASSERT(body->getActorType() == PxActorType::eRIGID_DYNAMIC); + // } + //} + } + + // do the same thing for bodies that have just woken up + + if(nbWoken) + { + PxU32 destSlot = 0; + BodyCore* const* wokenBodies = mWokeBodies.getEntries(); + for(PxU32 i=0; igetActorFlags() & PxActorFlag::eSEND_SLEEP_NOTIFIES) + actors[destSlot++] = body->getPxActor(); + } + + if(destSlot) + mSimulationEventCallback->onWake(actors, destSlot); + + //if (PX_DBG_IS_CONNECTED()) + //{ + // for (PxU32 i = 0; i < nbWoken; ++i) + // { + // BodyCore* body = mWokeBodies[i]; + // PX_ASSERT(actors[i]->getType() == PxActorType::eRIGID_DYNAMIC); + // } + //} + } + + PX_FREE_AND_RESET(actors); + } + } + + clearSleepWakeBodies(); +} + +void Sc::Scene::prepareOutOfBoundsCallbacks() +{ + PxU32 nbOut0; + void** outObjects = mAABBManager->getOutOfBoundsObjects(nbOut0); + + mOutOfBoundsIDs.clear(); + for(PxU32 i=0;i(outObjects[i]); + + Sc::ShapeSim* sim = static_cast(volume); + PxU32 id = sim->getID(); + mOutOfBoundsIDs.pushBack(id); + } +} + +bool Sc::Scene::fireOutOfBoundsCallbacks() +{ + bool outputWarning = false; + + // Actors + { + PxU32 nbOut0; + void** outObjects = mAABBManager->getOutOfBoundsObjects(nbOut0); + + const ObjectIDTracker& tracker = getShapeIDTracker(); + + PxBroadPhaseCallback* cb = mBroadPhaseCallback; + for(PxU32 i=0;i(outObjects[i]); + + Sc::ShapeSim* sim = static_cast(volume); + if(tracker.isDeletedID(mOutOfBoundsIDs[i])) + continue; + + if(cb) + { + ActorSim& actor = volume->getActor(); + RigidSim& rigidSim = static_cast(actor); + PxActor* pxActor = rigidSim.getPxActor(); + PxShape* px = sim->getPxShape(); + cb->onObjectOutOfBounds(*px, *pxActor); + } + else + { + outputWarning = true; + } + } + mAABBManager->clearOutOfBoundsObjects(); + } + + return outputWarning; +} + +void Sc::Scene::fireOnAdvanceCallback() +{ + if(!mSimulationEventCallback) + return; + + const PxU32 nbPosePreviews = mPosePreviewBodies.size(); + if(!nbPosePreviews) + return; + + mClientPosePreviewBodies.clear(); + mClientPosePreviewBodies.reserve(nbPosePreviews); + + mClientPosePreviewBuffer.clear(); + mClientPosePreviewBuffer.reserve(nbPosePreviews); + + const BodySim*const* PX_RESTRICT posePreviewBodies = mPosePreviewBodies.getEntries(); + for(PxU32 i=0; i(b.getPxActor())); + mClientPosePreviewBuffer.pushBack(c.body2World * c.getBody2Actor().getInverse()); + } + } + + const PxU32 bodyCount = mClientPosePreviewBodies.size(); + if(bodyCount) + mSimulationEventCallback->onAdvance(mClientPosePreviewBodies.begin(), mClientPosePreviewBuffer.begin(), bodyCount); +} + +void Sc::Scene::postCallbacksPreSync() +{ + PX_PROFILE_ZONE("Sim.postCallbackPreSync", mContextId); + // clear contact stream data + mNPhaseCore->clearContactReportStream(); + mNPhaseCore->clearContactReportActorPairs(false); + + // Put/prepare kinematics to/for sleep and invalidate target pose + // note: this needs to get done after the contact callbacks because + // the target might get read there. + // + PxU32 nbKinematics = getActiveKinematicBodiesCount(); + BodyCore*const* kinematics = getActiveKinematicBodies(); + + //KS - this method must run over the kinematic actors in reverse. + while(nbKinematics--) + { + if(nbKinematics > 16) + { + Ps::prefetchLine((kinematics[nbKinematics-16])); + } + if (nbKinematics > 4) + { + Ps::prefetchLine(((kinematics[nbKinematics - 4]))->getSimStateData_Unchecked()); + } + + BodyCore* b = kinematics[nbKinematics]; + //kinematics++; + PX_ASSERT(b->getSim()->isKinematic()); + PX_ASSERT(b->getSim()->isActive()); + + b->invalidateKinematicTarget(); + b->getSim()->deactivateKinematic(); + } + + releaseConstraints(true); //release constraint blocks at the end of the frame, so user can retrieve the blocks +} + +void Sc::Scene::setNbContactDataBlocks(PxU32 numBlocks) +{ + mLLContext->getNpMemBlockPool().setBlockCount(numBlocks); +} + +PxU32 Sc::Scene::getNbContactDataBlocksUsed() const +{ + return mLLContext->getNpMemBlockPool().getUsedBlockCount(); +} + +PxU32 Sc::Scene::getMaxNbContactDataBlocksUsed() const +{ + return mLLContext->getNpMemBlockPool().getMaxUsedBlockCount(); +} + +PxU32 Sc::Scene::getMaxNbConstraintDataBlocksUsed() const +{ + return mLLContext->getNpMemBlockPool().getPeakConstraintBlockCount(); +} + +void Sc::Scene::setScratchBlock(void* addr, PxU32 size) +{ + return mLLContext->setScratchBlock(addr, size); +} + +void Sc::Scene::checkConstraintBreakage() +{ + PX_PROFILE_ZONE("Sim.checkConstraintBreakage", getContextId()); + + PxU32 count = mActiveBreakableConstraints.size(); + ConstraintSim* const* constraints = mActiveBreakableConstraints.getEntries(); + while(count) + { + count--; + constraints[count]->checkMaxForceExceeded(); // start from the back because broken constraints get removed from the list + } +} + +void Sc::Scene::getStats(PxSimulationStatistics& s) const +{ + mStats->readOut(s, mLLContext->getSimStats()); + s.nbStaticBodies = mNbRigidStatics; + s.nbDynamicBodies = mNbRigidDynamics; + s.nbArticulations = mArticulations.size(); + + s.nbAggregates = mAABBManager->getNbActiveAggregates(); + for(PxU32 i=0; i(reinterpret_cast(shapes[i])+ptrOffset); + + //PxBounds3* target = uninflatedBounds ? uninflatedBounds + i : uninflatedBounds; + //mShapeSimPool->construct(sim, sc, llBody, target); + + ShapeSim* shapeSim = mShapeSimPool->construct(bodySim, sc); + mNbGeometries[sc.getGeometryType()]++; + + mSimulationController->addShape(&shapeSim->getLLShapeSim(), shapeSim->getID()); + + if (outBounds) + outBounds[i] = mBoundsArray->getBounds(shapeSim->getElementID()); + + mLLContext->getNphaseImplementationContext()->registerShape(sc.getCore()); + } +} + +void Sc::Scene::removeShapes(Sc::RigidSim& sim, Ps::InlineArray& shapesBuffer , Ps::InlineArray& removedShapes, bool wakeOnLostTouch) +{ + Sc::ElementSim* current = sim.getElements_(); + while(current) + { + Sc::ShapeSim* s = static_cast(current); + // can do two 2x the allocs in the worst case, but actors with >64 shapes are not common + shapesBuffer.pushBack(s); + removedShapes.pushBack(&s->getCore()); + current = current->mNextInActor; + } + + for(PxU32 i=0;iconstruct(*this, ro); + + mNbRigidStatics++; + addShapes(shapes, nbShapes, shapePtrOffset, *sim, uninflatedBounds); +} + +void Sc::Scene::prefetchForRemove(const StaticCore& core) const +{ + StaticSim* sim = core.getSim(); + if(sim) + { + Ps::prefetch(sim,sizeof(Sc::StaticSim)); + Ps::prefetch(sim->getElements_(),sizeof(Sc::ElementSim)); + } +} + +void Sc::Scene::prefetchForRemove(const BodyCore& core) const +{ + BodySim *sim = core.getSim(); + if(sim) + { + Ps::prefetch(sim,sizeof(Sc::BodySim)); + Ps::prefetch(sim->getElements_(),sizeof(Sc::ElementSim)); + } +} + +void Sc::Scene::removeStatic(StaticCore& ro, Ps::InlineArray& removedShapes, bool wakeOnLostTouch) +{ + PX_ASSERT(ro.getActorCoreType() == PxActorType::eRIGID_STATIC); + + StaticSim* sim = ro.getSim(); + if(sim) + { + if(mBatchRemoveState) + { + removeShapes(*sim, mBatchRemoveState->bufferedShapes ,removedShapes, wakeOnLostTouch); + } + else + { + Ps::InlineArray shapesBuffer; + removeShapes(*sim, shapesBuffer ,removedShapes, wakeOnLostTouch); + } + mStaticSimPool->destroy(static_cast(ro.getSim())); + mNbRigidStatics--; + } +} + +void Sc::Scene::addBody(BodyCore& body, void*const *shapes, PxU32 nbShapes, size_t shapePtrOffset, PxBounds3* outBounds, bool compound) +{ + // sim objects do all the necessary work of adding themselves to broad phase, + // activation, registering with the interaction system, etc + + BodySim* sim = mBodySimPool->construct(*this, body, compound); + + const bool isArticulationLink = sim->isArticulationLink(); + + if (sim->getLowLevelBody().mCore->mFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + { + if (isArticulationLink) + { + if (sim->getNodeIndex().isValid()) + mSpeculativeCDDArticulationBitMap.growAndSet(sim->getNodeIndex().index()); + } + else + mSpeculativeCCDRigidBodyBitMap.growAndSet(sim->getNodeIndex().index()); + } + //if rigid body is articulation link, the node index will be invalid. We should add the link to the scene after we add the + //articulation for gpu + if(sim->getNodeIndex().isValid()) + mSimulationController->addDynamic(&sim->getLowLevelBody(), sim->getNodeIndex()); + mNbRigidDynamics++; + addShapes(shapes, nbShapes, shapePtrOffset, *sim, outBounds); +} + +void Sc::Scene::removeBody(BodyCore& body, Ps::InlineArray& removedShapes, bool wakeOnLostTouch) +{ + BodySim *sim = body.getSim(); + if(sim) + { + if(mBatchRemoveState) + { + removeShapes(*sim, mBatchRemoveState->bufferedShapes ,removedShapes, wakeOnLostTouch); + } + else + { + Ps::InlineArray shapesBuffer; + removeShapes(*sim,shapesBuffer, removedShapes, wakeOnLostTouch); + } + + if (!sim->isArticulationLink()) + { + //clear bit map + if (sim->getLowLevelBody().mCore->mFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + mSpeculativeCCDRigidBodyBitMap.reset(sim->getNodeIndex().index()); + } + mBodySimPool->destroy(sim); + mNbRigidDynamics--; + } +} + +void Sc::Scene::addShape(RigidSim& owner, ShapeCore& shapeCore, PxBounds3* uninflatedBounds) +{ + ShapeSim* sim = mShapeSimPool->construct(owner, shapeCore); + mNbGeometries[shapeCore.getGeometryType()]++; + + //register shape + mSimulationController->addShape(&sim->getLLShapeSim(), sim->getID()); + if (uninflatedBounds) + *uninflatedBounds = mBoundsArray->getBounds(sim->getElementID()); + registerShapeInNphase(shapeCore); +} + +void Sc::Scene::removeShape(ShapeSim& shape, bool wakeOnLostTouch) +{ + //BodySim* body = shape.getBodySim(); + //if(body) + // body->postShapeDetach(); + + unregisterShapeFromNphase(shape.getCore()); + + mSimulationController->removeShape(shape.getID()); + + mNbGeometries[shape.getCore().getGeometryType()]--; + shape.removeFromBroadPhase(wakeOnLostTouch); + mShapeSimPool->destroy(&shape); +} + +void Sc::Scene::registerShapeInNphase(const ShapeCore& shape) +{ + mLLContext->getNphaseImplementationContext()->registerShape(shape.getCore()); +} + +void Sc::Scene::unregisterShapeFromNphase(const ShapeCore& shape) +{ + mLLContext->getNphaseImplementationContext()->unregisterShape(shape.getCore()); +} + +void Sc::Scene::notifyNphaseOnUpdateShapeMaterial(const ShapeCore& shapeCore) +{ + mLLContext->getNphaseImplementationContext()->updateShapeMaterial(shapeCore.getCore()); +} + +void Sc::Scene::startBatchInsertion(BatchInsertionState&state) +{ + state.shapeSim = mShapeSimPool->allocateAndPrefetch(); + state.staticSim = mStaticSimPool->allocateAndPrefetch(); + state.bodySim = mBodySimPool->allocateAndPrefetch(); +} + +void Sc::Scene::addShapes(void *const* shapes, PxU32 nbShapes, size_t ptrOffset, RigidSim& rigidSim, ShapeSim*& prefetchedShapeSim, PxBounds3* outBounds) +{ + for(PxU32 i=0;iallocateAndPrefetch(); + const ShapeCore& sc = *Ps::pointerOffset(shapes[i], ptrdiff_t(ptrOffset)); + new(prefetchedShapeSim) ShapeSim(rigidSim, sc); + outBounds[i] = mBoundsArray->getBounds(prefetchedShapeSim->getElementID()); + + mSimulationController->addShape(&prefetchedShapeSim->getLLShapeSim(), prefetchedShapeSim->getID()); + prefetchedShapeSim = nextShapeSim; + mNbGeometries[sc.getGeometryType()]++; + + + mLLContext->getNphaseImplementationContext()->registerShape(sc.getCore()); + } +} + +void Sc::Scene::addStatic(PxActor* actor, BatchInsertionState& s, PxBounds3* outBounds) +{ + // static core has been prefetched by caller + Sc::StaticSim* sim = s.staticSim; // static core has been prefetched by the caller + + const Cm::PtrTable* shapeTable = Ps::pointerOffset(actor, s.staticShapeTableOffset); + void*const* shapes = shapeTable->getPtrs(); + if(shapeTable->getCount()) + Ps::prefetch(shapes[0],PxU32(s.shapeOffset+sizeof(Sc::ShapeCore))); + + mStaticSimPool->construct(sim, *this, *Ps::pointerOffset(actor, s.staticActorOffset)); + s.staticSim = mStaticSimPool->allocateAndPrefetch(); + + addShapes(shapes, shapeTable->getCount(), size_t(s.shapeOffset), *sim, s.shapeSim, outBounds); + mNbRigidStatics++; +} + +void Sc::Scene::addBody(PxActor* actor, BatchInsertionState& s, PxBounds3* outBounds, bool compound) +{ + Sc::BodySim* sim = s.bodySim; // body core has been prefetched by the caller + + const Cm::PtrTable* shapeTable = Ps::pointerOffset(actor, s.dynamicShapeTableOffset); + void*const* shapes = shapeTable->getPtrs(); + if(shapeTable->getCount()) + Ps::prefetch(shapes[0], PxU32(s.shapeOffset+sizeof(Sc::ShapeCore))); + + mBodySimPool->construct(sim, *this, *Ps::pointerOffset(actor, s.dynamicActorOffset), compound); + s.bodySim = mBodySimPool->allocateAndPrefetch(); + + const bool isArticulationLink = sim->isArticulationLink(); + + if (isArticulationLink) + { + if (sim->getLowLevelBody().mCore->mFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + mSpeculativeCDDArticulationBitMap.growAndSet(sim->getNodeIndex().index()); + } + else + { + if (sim->getLowLevelBody().mCore->mFlags & PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD) + mSpeculativeCCDRigidBodyBitMap.growAndSet(sim->getNodeIndex().index()); + } + + if(sim->getNodeIndex().isValid()) + mSimulationController->addDynamic(&sim->getLowLevelBody(), sim->getNodeIndex()); + + addShapes(shapes, shapeTable->getCount(), size_t(s.shapeOffset), *sim, s.shapeSim, outBounds); + mNbRigidDynamics++; +} + +void Sc::Scene::finishBatchInsertion(BatchInsertionState& state) +{ + // a little bit lazy - we could deal with the last one in the batch specially to avoid overallocating by one. + + mStaticSimPool->releasePreallocated(static_cast(state.staticSim)); + mBodySimPool->releasePreallocated(static_cast(state.bodySim)); + mShapeSimPool->releasePreallocated(state.shapeSim); +} + +void Sc::Scene::initContactsIterator(ContactIterator& contactIterator, PxsContactManagerOutputIterator& outputs) +{ + outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + Interaction** first = mInteractions[Sc::InteractionType::eOVERLAP].begin(); + contactIterator = ContactIterator(first, first + mActiveInteractionCount[Sc::InteractionType::eOVERLAP], outputs); +} + +void Sc::Scene::setDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2, const PxDominanceGroupPair& dominance) +{ + struct { + void operator()(PxU32& bits, PxDominanceGroup shift, PxReal weight) + { + if(weight != PxReal(0)) + bits |= (PxU32(1) << shift); + else + bits &= ~(PxU32(1) << shift); + } + } bitsetter; + + bitsetter(mDominanceBitMatrix[group1], group2, dominance.dominance0); + bitsetter(mDominanceBitMatrix[group2], group1, dominance.dominance1); + + mInternalFlags |= SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_DOMINANCE; //force an update on all interactions on matrix change -- very expensive but we have no choice!! +} + +PxDominanceGroupPair Sc::Scene::getDominanceGroupPair(PxDominanceGroup group1, PxDominanceGroup group2) const +{ + PxU8 dom0 = PxU8((mDominanceBitMatrix[group1]>>group2) & 0x1 ? 1u : 0u); + PxU8 dom1 = PxU8((mDominanceBitMatrix[group2]>>group1) & 0x1 ? 1u : 0u); + return PxDominanceGroupPair(dom0, dom1); +} + +void Sc::Scene::setCreateContactReports(bool s) +{ + if(mLLContext) + mLLContext->setCreateContactStream(s); +} + +void Sc::Scene::setSolverBatchSize(PxU32 solverBatchSize) +{ + mDynamicsContext->setSolverBatchSize(solverBatchSize); +} + +PxU32 Sc::Scene::getSolverBatchSize() const +{ + return mDynamicsContext->getSolverBatchSize(); +} + +void Sc::Scene::setVisualizationParameter(PxVisualizationParameter::Enum param, PxReal value) +{ + mVisualizationParameterChanged = true; + + PX_ASSERT(mLLContext->getVisualizationParameter(PxVisualizationParameter::eSCALE) == mVisualizationScale); // Safety check because the scale is duplicated for performance reasons + + mLLContext->setVisualizationParameter(param, value); + + if (param == PxVisualizationParameter::eSCALE) + mVisualizationScale = value; +} + +PxReal Sc::Scene::getVisualizationParameter(PxVisualizationParameter::Enum param) const +{ + PX_ASSERT(mLLContext->getVisualizationParameter(PxVisualizationParameter::eSCALE) == mVisualizationScale); // Safety check because the scale is duplicated for performance reasons + + return mLLContext->getVisualizationParameter(param); +} + +void Sc::Scene::setVisualizationCullingBox(const PxBounds3& box) +{ + mLLContext->setVisualizationCullingBox(box); +} + +const PxBounds3& Sc::Scene::getVisualizationCullingBox() const +{ + return mLLContext->getVisualizationCullingBox(); +} + +PxReal Sc::Scene::getFrictionOffsetThreshold() const +{ + return mDynamicsContext->getFrictionOffsetThreshold(); +} + +PxU32 Sc::Scene::getDefaultContactReportStreamBufferSize() const +{ + return mNPhaseCore->getDefaultContactReportStreamBufferSize(); +} + +void Sc::Scene::buildActiveActors() +{ + PxU32 numActiveBodies = 0; + BodyCore*const* PX_RESTRICT activeBodies; + if(!(getPublicFlags() & PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS)) + { + numActiveBodies = getNumActiveBodies(); + activeBodies = getActiveBodiesArray(); + } + else + { + numActiveBodies = getActiveDynamicBodiesCount(); + activeBodies = getActiveDynamicBodies(); + } + + mActiveActors.clear(); + + for(PxU32 i=0; iisFrozen()) + { + PxRigidActor* ra = static_cast(activeBodies[i]->getPxActor()); + PX_ASSERT(ra); + mActiveActors.pushBack(ra); + } + } +} + +// PT: TODO: unify buildActiveActors & buildActiveAndFrozenActors +void Sc::Scene::buildActiveAndFrozenActors() +{ + PxU32 numActiveBodies = 0; + BodyCore*const* PX_RESTRICT activeBodies; + if(!(getPublicFlags() & PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS)) + { + numActiveBodies = getNumActiveBodies(); + activeBodies = getActiveBodiesArray(); + } + else + { + numActiveBodies = getActiveDynamicBodiesCount(); + activeBodies = getActiveDynamicBodies(); + } + + mActiveActors.clear(); + mFrozenActors.clear(); + + for(PxU32 i=0; i(activeBodies[i]->getPxActor()); + PX_ASSERT(ra); + + if(!activeBodies[i]->isFrozen()) + mActiveActors.pushBack(ra); + else + mFrozenActors.pushBack(ra); + } +} + +PxActor** Sc::Scene::getActiveActors(PxU32& nbActorsOut) +{ + nbActorsOut = mActiveActors.size(); + + if(!nbActorsOut) + return NULL; + + return mActiveActors.begin(); +} + +void Sc::Scene::setActiveActors(PxActor** actors, PxU32 nbActors) +{ + mActiveActors.forceSize_Unsafe(0); + mActiveActors.resize(nbActors); + PxMemCopy(mActiveActors.begin(), actors, sizeof(PxActor*) * nbActors); +} + +PxActor** Sc::Scene::getFrozenActors(PxU32& nbActorsOut) +{ + nbActorsOut = mFrozenActors.size(); + + if(!nbActorsOut) + return NULL; + + return mFrozenActors.begin(); +} + +void Sc::Scene::reserveTriggerReportBufferSpace(const PxU32 pairCount, PxTriggerPair*& triggerPairBuffer, TriggerPairExtraData*& triggerPairExtraBuffer) +{ + const PxU32 oldSize = mTriggerBufferAPI.size(); + const PxU32 newSize = oldSize + pairCount; + const PxU32 newCapacity = PxU32(newSize * 1.5f); + mTriggerBufferAPI.reserve(newCapacity); + mTriggerBufferAPI.forceSize_Unsafe(newSize); + triggerPairBuffer = mTriggerBufferAPI.begin() + oldSize; + + PX_ASSERT(oldSize == mTriggerBufferExtraData->size()); + mTriggerBufferExtraData->reserve(newCapacity); + mTriggerBufferExtraData->forceSize_Unsafe(newSize); + triggerPairExtraBuffer = mTriggerBufferExtraData->begin() + oldSize; +} + +PxClientID Sc::Scene::createClient() +{ + mClients.pushBack(PX_NEW(Client)()); + return PxClientID(mClients.size()-1); +} + +void Sc::Scene::clearSleepWakeBodies(void) +{ + // Clear sleep/woken marker flags + BodyCore* const* sleepingBodies = mSleepBodies.getEntries(); + for(PxU32 i=0; i < mSleepBodies.size(); i++) + { + BodySim* body = sleepingBodies[i]->getSim(); + + PX_ASSERT(!body->readInternalFlag(BodySim::BF_WAKEUP_NOTIFY)); + body->clearInternalFlag(BodySim::BF_SLEEP_NOTIFY); + + // A body can be in both lists depending on the sequence of events + body->clearInternalFlag(BodySim::BF_IS_IN_SLEEP_LIST); + body->clearInternalFlag(BodySim::BF_IS_IN_WAKEUP_LIST); + } + + BodyCore* const* wokenBodies = mWokeBodies.getEntries(); + for(PxU32 i=0; i < mWokeBodies.size(); i++) + { + BodySim* body = wokenBodies[i]->getSim(); + + PX_ASSERT(!body->readInternalFlag(BodySim::BF_SLEEP_NOTIFY)); + body->clearInternalFlag(BodySim::BF_WAKEUP_NOTIFY); + + // A body can be in both lists depending on the sequence of events + body->clearInternalFlag(BodySim::BF_IS_IN_SLEEP_LIST); + body->clearInternalFlag(BodySim::BF_IS_IN_WAKEUP_LIST); + } + + mSleepBodies.clear(); + mWokeBodies.clear(); + mWokeBodyListValid = true; + mSleepBodyListValid = true; +} + +void Sc::Scene::onBodySleep(BodySim* body) +{ + if(mSimulationEventCallback) + { + if (body->readInternalFlag(BodySim::BF_WAKEUP_NOTIFY)) + { + PX_ASSERT(!body->readInternalFlag(BodySim::BF_SLEEP_NOTIFY)); + + // Body is in the list of woken bodies, hence, mark this list as dirty such that it gets cleaned up before + // being sent to the user + body->clearInternalFlag(BodySim::BF_WAKEUP_NOTIFY); + mWokeBodyListValid = false; + } + + body->raiseInternalFlag(BodySim::BF_SLEEP_NOTIFY); + } + + // Avoid multiple insertion (the user can do multiple transitions between asleep and awake) + // + // even if no sleep events are requested, we still need to track the objects which were put to sleep because + // for those we need to sync the buffered state (strictly speaking this only applies to sleep changes that are + // triggered by the simulation and not the user but we do not distinguish here) + if (!body->readInternalFlag(BodySim::BF_IS_IN_SLEEP_LIST)) + { + if(mSimulationEventCallback) + { + PX_ASSERT(!mSleepBodies.contains(&body->getBodyCore())); + } + mSleepBodies.insert(&body->getBodyCore()); + body->raiseInternalFlag(BodySim::BF_IS_IN_SLEEP_LIST); + } +} + +void Sc::Scene::onBodyWakeUp(BodySim* body) +{ + if(!mSimulationEventCallback) + return; + + if (body->readInternalFlag(BodySim::BF_SLEEP_NOTIFY)) + { + PX_ASSERT(!body->readInternalFlag(BodySim::BF_WAKEUP_NOTIFY)); + + // Body is in the list of sleeping bodies, hence, mark this list as dirty such it gets cleaned up before + // being sent to the user + body->clearInternalFlag(BodySim::BF_SLEEP_NOTIFY); + mSleepBodyListValid = false; + } + + body->raiseInternalFlag(BodySim::BF_WAKEUP_NOTIFY); + + // Avoid multiple insertion (the user can do multiple transitions between asleep and awake) + if (!body->readInternalFlag(BodySim::BF_IS_IN_WAKEUP_LIST)) + { + PX_ASSERT(!mWokeBodies.contains(&body->getBodyCore())); + mWokeBodies.insert(&body->getBodyCore()); + body->raiseInternalFlag(BodySim::BF_IS_IN_WAKEUP_LIST); + } +} + +PX_INLINE void Sc::Scene::cleanUpSleepBodies() +{ + BodyCore* const* bodyArray = mSleepBodies.getEntries(); + PxU32 bodyCount = mSleepBodies.size(); + + IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); + + while (bodyCount--) + { + BodySim* body = bodyArray[bodyCount]->getSim(); + + if (body->readInternalFlag(static_cast(BodySim::BF_WAKEUP_NOTIFY))) + { + body->clearInternalFlag(static_cast(BodySim::BF_IS_IN_WAKEUP_LIST)); + mSleepBodies.erase(bodyArray[bodyCount]); + } + else if (islandSim.getNode(body->getNodeIndex()).isActive()) + { + //This body is still active in the island simulation, so the request to deactivate the actor by the application must have failed. Recover by undoing this + mSleepBodies.erase(bodyArray[bodyCount]); + body->internalWakeUp(); + } + } + + mSleepBodyListValid = true; +} + +PX_INLINE void Sc::Scene::cleanUpWokenBodies() +{ + cleanUpSleepOrWokenBodies(mWokeBodies, BodySim::BF_SLEEP_NOTIFY, mWokeBodyListValid); +} + +PX_INLINE void Sc::Scene::cleanUpSleepOrWokenBodies(Ps::CoalescedHashSet& bodyList, PxU32 removeFlag, bool& validMarker) +{ + // With our current logic it can happen that a body is added to the sleep as well as the woken body list in the + // same frame. + // + // Examples: + // - Kinematic is created (added to woken list) but has not target (-> deactivation -> added to sleep list) + // - Dynamic is created (added to woken list) but is forced to sleep by user (-> deactivation -> added to sleep list) + // + // This code traverses the sleep/woken body list and removes bodies which have been initially added to the given + // list but do not belong to it anymore. + + BodyCore* const* bodyArray = bodyList.getEntries(); + PxU32 bodyCount = bodyList.size(); + while (bodyCount--) + { + BodySim* body = bodyArray[bodyCount]->getSim(); + + if (body->readInternalFlag(static_cast(removeFlag))) + bodyList.erase(bodyArray[bodyCount]); + } + + validMarker = true; +} + +void Sc::Scene::releaseConstraints(bool endOfScene) +{ + PX_ASSERT(mLLContext); + + if(getStabilizationEnabled()) + { + //If stabilization is enabled, we're caching contacts for next frame + if(!endOfScene) + { + //So we only clear memory (flip buffers) when not at the end-of-scene. + //This means we clear after narrow phase completed so we can + //release the previous frame's contact buffers before we enter the solve phase. + mLLContext->getNpMemBlockPool().releaseContacts(); + } + } + else if(endOfScene) + { + //We now have a double-buffered pool of mem blocks so we must + //release both pools (which actually triggers the memory used this + //frame to be released + mLLContext->getNpMemBlockPool().releaseContacts(); + mLLContext->getNpMemBlockPool().releaseContacts(); + } +} + +PX_INLINE void Sc::Scene::clearBrokenConstraintBuffer() +{ + mBrokenConstraints.clear(); +} + +void Sc::Scene::updateFromVisualizationParameters() +{ + if (!mVisualizationParameterChanged) // All up to date + return; + + // Update SIPs if visualization is enabled + if (getVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL) || + getVisualizationParameter(PxVisualizationParameter::eCONTACT_ERROR) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_FORCE)) + mInternalFlags |= SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_VISUALIZATION; + + mVisualizationParameterChanged = false; +} + +void Sc::Scene::addToLostTouchList(BodySim* body1, BodySim* body2) +{ + PX_ASSERT(body1 != 0); + PX_ASSERT(body2 != 0); + SimpleBodyPair p = { body1, body2, body1->getRigidID(), body2->getRigidID() }; + mLostTouchPairs.pushBack(p); +} + +void Sc::Scene::initDominanceMatrix() +{ + //init all dominance pairs such that: + //if g1 == g2, then (1.0f, 1.0f) is returned + //if g1 < g2, then (0.0f, 1.0f) is returned + //if g1 > g2, then (1.0f, 0.0f) is returned + + PxU32 mask = ~PxU32(1); + for (unsigned i = 0; i < PX_MAX_DOMINANCE_GROUP; ++i, mask <<= 1) + mDominanceBitMatrix[i] = ~mask; +} + +ArticulationV* Sc::Scene::createLLArticulation(Sc::ArticulationSim* sim) +{ + ArticulationV* articulation = NULL; + + if (sim->getCore().getArticulationType() == PxArticulationBase::eMaximumCoordinate) + { + articulation = mLLArticulationPool->construct(sim); + } + else + { + PX_ASSERT(sim->getCore().getArticulationType() == PxArticulationBase::eReducedCoordinate); + articulation = mLLArticulationRCPool->construct(sim); + } + + return articulation; +} + +void Sc::Scene::destroyLLArticulation(ArticulationV& articulation) +{ + if (articulation.getType() == PxArticulationBase::eMaximumCoordinate) + mLLArticulationPool->destroy(static_cast(&articulation)); + else + { + PX_ASSERT(articulation.getType() == PxArticulationBase::eReducedCoordinate); + mLLArticulationRCPool->destroy(static_cast(&articulation)); + } +} + +PxU32 Sc::Scene::getNbArticulations() const +{ + return mArticulations.size(); +} + +Sc::ArticulationCore* const* Sc::Scene::getArticulations() +{ + return mArticulations.getEntries(); +} + +PxU32 Sc::Scene::getNbConstraints() const +{ + return mConstraints.size(); +} + +Sc::ConstraintCore*const * Sc::Scene::getConstraints() +{ + return mConstraints.getEntries(); +} + +PxU32 Sc::Scene::createAggregate(void* userData, bool selfCollisions) +{ + const physx::Bp::BoundsIndex index = getElementIDPool().createID(); + mBoundsArray->initEntry(index); +#ifdef BP_USE_AGGREGATE_GROUP_TAIL + return mAABBManager->createAggregate(index, Bp::FilterGroup::eINVALID, userData, selfCollisions); +#else + // PT: TODO: ideally a static compound would have a static group + const PxU32 rigidId = getRigidIDTracker().createID(); + const Bp::FilterGroup::Enum bpGroup = Bp::FilterGroup::Enum(rigidId + Bp::FilterGroup::eDYNAMICS_BASE); + return mAABBManager->createAggregate(index, bpGroup, userData, selfCollisions); +#endif +} + +void Sc::Scene::deleteAggregate(PxU32 id) +{ + Bp::BoundsIndex index; + Bp::FilterGroup::Enum bpGroup; +#ifdef BP_USE_AGGREGATE_GROUP_TAIL + if(mAABBManager->destroyAggregate(index, bpGroup, id)) + { + getElementIDPool().releaseID(index); + } +#else + if(mAABBManager->destroyAggregate(index, bpGroup, id)) + { + getElementIDPool().releaseID(index); + + // PT: this is clumsy.... + const PxU32 rigidId = PxU32(bpGroup) - Bp::FilterGroup::eDYNAMICS_BASE; + getRigidIDTracker().releaseID(rigidId); + } +#endif +} + +void Sc::Scene::shiftOrigin(const PxVec3& shift) +{ + // + // adjust low level context + // + mLLContext->shiftOrigin(shift); + + // adjust bounds array + // + mBoundsArray->shiftOrigin(shift); + + // + // adjust broadphase + // + mAABBManager->shiftOrigin(shift); + + // + // adjust constraints + // + ConstraintCore*const * constraints = mConstraints.getEntries(); + for(PxU32 i=0, size = mConstraints.size(); i < size; i++) + { + constraints[i]->getPxConnector()->onOriginShift(shift); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void Sc::Scene::islandInsertion(PxBaseTask* /*continuation*/) +{ + { + PX_PROFILE_ZONE("Sim.processNewOverlaps.islandInsertion", getContextId()); + + const PxU32 nbShapeIdxCreated = mPreallocatedShapeInteractions.size(); + for (PxU32 a = 0; a < nbShapeIdxCreated; ++a) + { + size_t address = reinterpret_cast(mPreallocatedShapeInteractions[a]); + if (address & 1) + { + ShapeInteraction* interaction = reinterpret_cast(address&size_t(~1)); + + PxsContactManager* contactManager = const_cast(interaction->getContactManager()); + + Sc::BodySim* bs0 = interaction->getShape0().getBodySim(); + Sc::BodySim* bs1 = interaction->getShape1().getBodySim(); + + IG::NodeIndex nodeIndexB; + if (bs1) + nodeIndexB = bs1->getNodeIndex(); + + IG::EdgeIndex edgeIdx = mSimpleIslandManager->addContactManager(contactManager, bs0->getNodeIndex(), nodeIndexB, interaction); + interaction->mEdgeIndex = edgeIdx; + + if (contactManager) + contactManager->getWorkUnit().mEdgeIndex = edgeIdx; + } + } + + // - Wakes actors that lost touch if appropriate + //processLostTouchPairs(); + + if(mCCDPass == 0) + { + mSimpleIslandManager->firstPassIslandGen(); + } + } +} + +void Sc::Scene::registerContactManagers(PxBaseTask* /*continuation*/) +{ + { + PxvNphaseImplementationContext* nphaseContext = mLLContext->getNphaseImplementationContext(); + PX_PROFILE_ZONE("Sim.processNewOverlaps.registerCms", getContextId()); + //nphaseContext->registerContactManagers(mPreallocatedContactManagers.begin(), mPreallocatedContactManagers.size(), mLLContext->getContactManagerPool().getMaxUsedIndex()); + const PxU32 nbCmsCreated = mPreallocatedContactManagers.size(); + for (PxU32 a = 0; a < nbCmsCreated; ++a) + { + size_t address = reinterpret_cast(mPreallocatedContactManagers[a]); + if (address & 1) + { + PxsContactManager* cm = reinterpret_cast(address&size_t(~1)); + nphaseContext->registerContactManager(cm, 0, 0); + } + } + } +} + +void Sc::Scene::registerInteractions(PxBaseTask* /*continuation*/) +{ + { + PX_PROFILE_ZONE("Sim.processNewOverlaps.registerInteractions", getContextId()); + const PxU32 nbShapeIdxCreated = mPreallocatedShapeInteractions.size(); + for (PxU32 a = 0; a < nbShapeIdxCreated; ++a) + { + size_t address = reinterpret_cast(mPreallocatedShapeInteractions[a]); + if (address & 1) + { + ShapeInteraction* interaction = reinterpret_cast(address&size_t(~1)); + + ActorSim& actorSim0 = interaction->getActorSim0(); + ActorSim& actorSim1 = interaction->getActorSim1(); + actorSim0.registerInteractionInActor(interaction); + actorSim1.registerInteractionInActor(interaction); + + Sc::BodySim* bs0 = actorSim0.isDynamicRigid() ? static_cast(&actorSim0) : NULL; + Sc::BodySim* bs1 = actorSim1.isDynamicRigid() ? static_cast(&actorSim1) : NULL; + + bs0->registerCountedInteraction(); + if(bs1) + bs1->registerCountedInteraction(); + } + } + + const PxU32 nbMarkersCreated = mPreallocatedInteractionMarkers.size(); + for (PxU32 a = 0; a < nbMarkersCreated; ++a) + { + size_t address = reinterpret_cast(mPreallocatedInteractionMarkers[a]); + if (address & 1) + { + ElementInteractionMarker* interaction = reinterpret_cast(address&size_t(~1)); + interaction->registerInActors(NULL); + } + } + } +} + +void Sc::Scene::registerSceneInteractions(PxBaseTask* /*continuation*/) +{ + PX_PROFILE_ZONE("Sim.processNewOverlaps.registerInteractionsScene", getContextId()); + const PxU32 nbShapeIdxCreated = mPreallocatedShapeInteractions.size(); + for (PxU32 a = 0; a < nbShapeIdxCreated; ++a) + { + size_t address = reinterpret_cast(mPreallocatedShapeInteractions[a]); + if (address & 1) + { + ShapeInteraction* interaction = reinterpret_cast(address&size_t(~1)); + registerInteraction(interaction, interaction->getContactManager() != NULL); + mNPhaseCore->registerInteraction(interaction); + + const PxsContactManager* cm = interaction->getContactManager(); + if (cm != NULL) + { + mLLContext->setActiveContactManager(cm); + } + } + } + + const PxU32 nbInteractionMarkers = mPreallocatedInteractionMarkers.size(); + for (PxU32 a = 0; a < nbInteractionMarkers; ++a) + { + size_t address = reinterpret_cast(mPreallocatedInteractionMarkers[a]); + if (address & 1) + { + ElementInteractionMarker* interaction = reinterpret_cast(address&size_t(~1)); + registerInteraction(interaction, false); + mNPhaseCore->registerInteraction(interaction); + } + } +} + +class OverlapFilterTask : public Cm::Task +{ +public: + static const PxU32 MaxPairs = 512; + Sc::NPhaseCore* mNPhaseCore; + const Bp::AABBOverlap* mPairs; + + PxU32 mNbToProcess; + + PxU32 mKeepMap[MaxPairs/32]; + PxU32 mCallbackMap[MaxPairs/32]; + + PxFilterInfo* mFinfo; + + PxU32 mNbToKeep; + PxU32 mNbToSuppress; + PxU32 mNbToCallback; + + OverlapFilterTask* mNext; + + OverlapFilterTask(PxU64 contextID, Sc::NPhaseCore* nPhaseCore, PxFilterInfo* fInfo, const Bp::AABBOverlap* pairs, const PxU32 nbToProcess) : + Cm::Task (contextID), + mNPhaseCore (nPhaseCore), + mPairs (pairs), + mNbToProcess (nbToProcess), + mFinfo (fInfo), + mNbToKeep (0), + mNbToSuppress (0), + mNbToCallback (0), + mNext (NULL) + { + PxMemZero(mKeepMap, sizeof(mKeepMap)); + PxMemZero(mCallbackMap, sizeof(mCallbackMap)); + } + + virtual void runInternal() + { + mNPhaseCore->runOverlapFilters( mNbToProcess, mPairs, mFinfo, mNbToKeep, mNbToSuppress, mNbToCallback, mKeepMap, mCallbackMap); + } + + virtual const char* getName() const { return "OverlapFilterTask"; } +}; + +class OnOverlapCreatedTask : public Cm::Task +{ +public: + Sc::NPhaseCore* mNPhaseCore; + const Bp::AABBOverlap* mPairs; + const PxFilterInfo* mFinfo; + PxsContactManager** mContactManagers; + Sc::ShapeInteraction** mShapeInteractions; + Sc::ElementInteractionMarker** mInteractionMarkers; + PxU32 mNbToProcess; + + OnOverlapCreatedTask(PxU64 contextID, Sc::NPhaseCore* nPhaseCore, const Bp::AABBOverlap* pairs, const PxFilterInfo* fInfo, PxsContactManager** contactManagers, Sc::ShapeInteraction** shapeInteractions, Sc::ElementInteractionMarker** interactionMarkers, + PxU32 nbToProcess) : + Cm::Task (contextID), + mNPhaseCore (nPhaseCore), + mPairs (pairs), + mFinfo (fInfo), + mContactManagers (contactManagers), + mShapeInteractions (shapeInteractions), + mInteractionMarkers (interactionMarkers), + mNbToProcess (nbToProcess) + { + } + + virtual void runInternal() + { + PxsContactManager** currentCm = mContactManagers; + Sc::ShapeInteraction** currentSI = mShapeInteractions; + Sc::ElementInteractionMarker** currentEI = mInteractionMarkers; + + for(PxU32 i=0; i(pair.mUserData1); + Sc::ShapeSim* s1 = reinterpret_cast(pair.mUserData0); + + Sc::ElementSimInteraction* interaction = mNPhaseCore->createRbElementInteraction(mFinfo[i], *s0, *s1, *currentCm, *currentSI, *currentEI, 0); + if(interaction) + { + if(interaction->getType() == Sc::InteractionType::eOVERLAP) + { + *currentSI = reinterpret_cast(reinterpret_cast(*currentSI) | 1); + currentSI++; + + if(static_cast(interaction)->getContactManager()) + { + *currentCm = reinterpret_cast(reinterpret_cast(*currentCm) | 1); + currentCm++; + } + } + else if(interaction->getType() == Sc::InteractionType::eMARKER) + { + *currentEI = reinterpret_cast(reinterpret_cast(*currentEI) | 1); + currentEI++; + } + } + } + } + + virtual const char* getName() const { return "OnOverlapCreatedTask"; } +}; + +void Sc::Scene::finishBroadPhase(PxBaseTask* continuation) +{ + PX_UNUSED(continuation); + PX_PROFILE_ZONE("Sc::Scene::finishBroadPhase", getContextId()); + + Bp::AABBManager* aabbMgr = mAABBManager; + + { + PX_PROFILE_ZONE("Sim.processNewOverlaps", getContextId()); + + { + //KS - these functions call "registerInActors", while OverlapFilterTask reads the list of interactions + //in an actor. This could lead to a race condition and a crash if they occur at the same time, so we + //serialize these operations + PX_PROFILE_ZONE("Sim.processNewOverlaps.createOverlapsNoShapeInteractions", getContextId()); + { + PxU32 createdOverlapCount; + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getCreatedOverlaps(Bp::ElementType::eTRIGGER, createdOverlapCount); + + mLLContext->getSimStats().mNbNewPairs += createdOverlapCount; + mNPhaseCore->onOverlapCreated(p, createdOverlapCount); + } + } + + { + PxU32 createdOverlapCount; + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getCreatedOverlaps(Bp::ElementType::eSHAPE, createdOverlapCount); + { + //We allocate at least 1 element in this array to ensure that the onOverlapCreated functions don't go bang! + mPreallocatedContactManagers.reserve(1); + mPreallocatedShapeInteractions.reserve(1); + mPreallocatedInteractionMarkers.reserve(1); + + mPreallocatedContactManagers.forceSize_Unsafe(1); + mPreallocatedShapeInteractions.forceSize_Unsafe(1); + mPreallocatedInteractionMarkers.forceSize_Unsafe(1); + } + + mLLContext->getSimStats().mNbNewPairs += createdOverlapCount; + + mPreallocateContactManagers.setContinuation(continuation); + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + mFilterInfo.forceSize_Unsafe(0); + mFilterInfo.reserve(createdOverlapCount); + mFilterInfo.forceSize_Unsafe(createdOverlapCount); + + const PxU32 nbPairsPerTask = OverlapFilterTask::MaxPairs; + mOverlapFilterTaskHead = NULL; + OverlapFilterTask* previousTask = NULL; + for(PxU32 a=0; asetContinuation(&mPreallocateContactManagers); + task->removeReference(); + + if(previousTask) + previousTask->mNext = task; + else + mOverlapFilterTaskHead = task; + + previousTask = task; + } + } + + mPreallocateContactManagers.removeReference(); + } +} + +void Sc::Scene::preallocateContactManagers(PxBaseTask* continuation) +{ + //Iterate over all filter tasks and work out how many pairs we need... + + PxU32 createdOverlapCount = 0; + + PxU32 totalCreatedPairs = 0; + PxU32 totalSuppressPairs = 0; + + PxU32 overlapCount; + Bp::AABBOverlap* PX_RESTRICT p = mAABBManager->getCreatedOverlaps(Bp::ElementType::eSHAPE, overlapCount); + PxFilterInfo* fInfo = mFilterInfo.begin(); + + OverlapFilterTask* task = mOverlapFilterTaskHead; + while(task) + { + if(task->mNbToCallback) + { + //Iterate and process callbacks. Refilter then increment the results, setting the appropriate settings + const FilteringContext context(*this, mNPhaseCore->mFilterPairManager); + + for(PxU32 w = 0; w < (OverlapFilterTask::MaxPairs / 32); ++w) + { + for(PxU32 b = task->mCallbackMap[w]; b; b &= b - 1) + { + const PxU32 index = (w << 5) + Ps::lowestSetBit(b); + const Bp::AABBOverlap& pair = task->mPairs[index]; + Sc::ShapeSim* s0 = reinterpret_cast(pair.mUserData0); + Sc::ShapeSim* s1 = reinterpret_cast(pair.mUserData1); + + const PxFilterInfo finfo = filterRbCollisionPairSecondStage(context, *s0, *s1, s0->getBodySim(), s1->getBodySim(), INVALID_FILTER_PAIR_INDEX, true); + + task->mFinfo[index] = finfo; + + if(!(finfo.filterFlags & PxFilterFlag::eKILL)) + { + if((finfo.filterFlags & PxFilterFlag::eSUPPRESS) == false) + task->mNbToKeep++; + else + task->mNbToSuppress++; + task->mKeepMap[index / 32] |= (1 << (index & 31)); + } + } + } + } + + totalCreatedPairs += task->mNbToKeep; + totalSuppressPairs += task->mNbToSuppress; + task = task->mNext; + } + + { + //We allocate at least 1 element in this array to ensure that the onOverlapCreated functions don't go bang! + mPreallocatedContactManagers.reserve(totalCreatedPairs+1); + mPreallocatedShapeInteractions.reserve(totalCreatedPairs+1); + mPreallocatedInteractionMarkers.reserve(totalSuppressPairs+1); + + mPreallocatedContactManagers.forceSize_Unsafe(totalCreatedPairs); + mPreallocatedShapeInteractions.forceSize_Unsafe(totalCreatedPairs); + mPreallocatedInteractionMarkers.forceSize_Unsafe(totalSuppressPairs); + } + + const PxU32 nbPairsPerTask = 256; + PxsContactManager** cms = mPreallocatedContactManagers.begin(); + Sc::ShapeInteraction** shapeInter = mPreallocatedShapeInteractions.begin(); + Sc::ElementInteractionMarker** markerIter = mPreallocatedInteractionMarkers.begin(); + + Cm::FlushPool& flushPool = mLLContext->getTaskPool(); + + struct Local + { + static void processBatch(const PxU32 createdCurrIdx, PxU32& createdStartIdx, const PxU32 suppressedCurrIdx, PxU32& suppressedStartIdx, const PxU32 batchSize, + PxsContext* const context, NPhaseCore* const core, OnOverlapCreatedTask* const createTask, PxBaseTask* const continuation_, + PxsContactManager** const cms_, Sc::ShapeInteraction** const shapeInter_, Sc::ElementInteractionMarker** const markerIter_) + { + const PxU32 nbToCreate = createdCurrIdx - createdStartIdx; + const PxU32 nbToSuppress = suppressedCurrIdx - suppressedStartIdx; + + context->getContactManagerPool().preallocate(nbToCreate, cms_ + createdStartIdx); + for(PxU32 i=0; imShapeInteractionPool.allocate(); + for(PxU32 i=0; imInteractionMarkerPool.allocate(); + + createdStartIdx = createdCurrIdx; + suppressedStartIdx = suppressedCurrIdx; + + createTask->mNbToProcess = batchSize; + createTask->setContinuation(continuation_); + createTask->removeReference(); + } + }; + + // PT: TODO: why do we create the task immediately? Why not create it only when a batch is full? + OnOverlapCreatedTask* createTask = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(OnOverlapCreatedTask)), OnOverlapCreatedTask)(getContextId(), mNPhaseCore, p, fInfo, cms, shapeInter, markerIter, 0); + + PxU32 batchSize = 0; + PxU32 suppressedStartIdx = 0; + PxU32 createdStartIdx = 0; + PxU32 suppressedCurrIdx = 0; + PxU32 createdCurrIdx = 0; + PxU32 currentReadIdx = 0; + + task = mOverlapFilterTaskHead; + while(task) + { + if(task->mNbToKeep || task->mNbToSuppress) + { + for(PxU32 w = 0; w < (OverlapFilterTask::MaxPairs/32); ++w) + { + for(PxU32 b = task->mKeepMap[w]; b; b &= b-1) + { + const PxU32 index = (w<<5) + Ps::lowestSetBit(b); + + if(createdOverlapCount < (index + currentReadIdx)) + { + p[createdOverlapCount] = task->mPairs[index]; + fInfo[createdOverlapCount] = task->mFinfo[index]; + } + createdOverlapCount++; + batchSize++; + } + } + + suppressedCurrIdx += task->mNbToSuppress; + createdCurrIdx += task->mNbToKeep; + + if(batchSize >= nbPairsPerTask) + { + Local::processBatch(createdCurrIdx, createdStartIdx, suppressedCurrIdx, suppressedStartIdx, batchSize, mLLContext, mNPhaseCore, createTask, continuation, cms, shapeInter, markerIter); + + createTask = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(OnOverlapCreatedTask)), OnOverlapCreatedTask)(getContextId(), mNPhaseCore, p + createdOverlapCount, + fInfo + createdOverlapCount, cms + createdStartIdx, shapeInter + createdStartIdx, markerIter + suppressedStartIdx, 0); + + batchSize = 0; + } + } + currentReadIdx += OverlapFilterTask::MaxPairs; + task = task->mNext; + } + + if(batchSize) + Local::processBatch(createdCurrIdx, createdStartIdx, suppressedCurrIdx, suppressedStartIdx, batchSize, mLLContext, mNPhaseCore, createTask, continuation, cms, shapeInter, markerIter); +} + +void Sc::Scene::finishBroadPhaseStage2(const PxU32 ccdPass) +{ + PX_PROFILE_ZONE("Sc::Scene::finishBroadPhase2", getContextId()); + + Bp::AABBManager* aabbMgr = mAABBManager; + + for(PxU32 i=0; igetDestroyedOverlaps(Bp::ElementType::Enum(i), destroyedOverlapCount); + mLLContext->getSimStats().mNbLostPairs += destroyedOverlapCount; + } + + //KS - we need to defer processing lost overlaps until later! + if (ccdPass) + { + PX_PROFILE_ZONE("Sim.processLostOverlaps", getContextId()); + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + const bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + PxU32 destroyedOverlapCount; + + { + Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + + while(destroyedOverlapCount--) + { + ElementSim* volume0 = reinterpret_cast(p->mUserData0); + ElementSim* volume1 = reinterpret_cast(p->mUserData1); + + //KS - this is a bit ugly. We split the "onOverlapRemoved" for shape interactions to parallelize it and that means + //that we have to call each of the individual stages of the remove here. + + //First, we have to get the interaction pointer... + Sc::ElementSimInteraction* interaction = mNPhaseCore->onOverlapRemovedStage1(volume0, volume1); + p->mPairUserData = interaction; + if(interaction) + { + if(interaction->getType() == Sc::InteractionType::eOVERLAP || interaction->getType() == Sc::InteractionType::eMARKER) + { + //If it's a standard "overlap" interaction, we have to send a lost touch report, unregister it, and destroy its manager and island gen data. + if(interaction->getType() == Sc::InteractionType::eOVERLAP) + { + Sc::ShapeInteraction* si = static_cast(interaction); + mNPhaseCore->lostTouchReports(si, PxU32(PairReleaseFlag::eWAKE_ON_LOST_TOUCH), 0, outputs, useAdaptiveForce); + si->destroyManager(); + si->clearIslandGenData(); + } + + unregisterInteraction(interaction); + mNPhaseCore->unregisterInteraction(interaction); + } + + //Then call "onOverlapRemoved" to actually free the interaction + mNPhaseCore->onOverlapRemoved(volume0, volume1, ccdPass, interaction, outputs, useAdaptiveForce); + } + p++; + } + } + + { + Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eTRIGGER, destroyedOverlapCount); + + while(destroyedOverlapCount--) + { + ElementSim* volume0 = reinterpret_cast(p->mUserData0); + ElementSim* volume1 = reinterpret_cast(p->mUserData1); + + p->mPairUserData = NULL; + + //KS - this is a bit ugly. + mNPhaseCore->onOverlapRemoved(volume0, volume1, ccdPass, NULL, outputs, useAdaptiveForce); + p++; + } + } + } + + // - Wakes actors that lost touch if appropriate + processLostTouchPairs(); + + if (ccdPass) + { + aabbMgr->getBroadPhase()->deletePairs(); + + aabbMgr->freeBuffers(); + } +} + +void Sc::Scene::secondPassNarrowPhase(PxBaseTask* /*continuation*/) +{ + { + PX_PROFILE_ZONE("Sim.postIslandGen", getContextId()); + mSimpleIslandManager->additionalSpeculativeActivation(); + // wake interactions + { + PX_PROFILE_ZONE("ScScene.wakeInteractions", getContextId()); + const IG::IslandSim& speculativeSim = mSimpleIslandManager->getSpeculativeIslandSim(); + + //KS - only wake contact managers based on speculative state to trigger contact gen. Waking actors based on accurate state + //should activate and joints. + { + PxU32 nbActivatingEdges = speculativeSim.getNbActivatedEdges(IG::Edge::eCONTACT_MANAGER); + const IG::EdgeIndex* activatingEdges = speculativeSim.getActivatedEdges(IG::Edge::eCONTACT_MANAGER); + + for(PxU32 i = 0; i < nbActivatingEdges; ++i) + { + Sc::Interaction* interaction = mSimpleIslandManager->getInteraction(activatingEdges[i]); + + if(interaction && !interaction->readInteractionFlag(InteractionFlag::eIS_ACTIVE)) + { + if(speculativeSim.getEdge(activatingEdges[i]).isActive()) + { + const bool proceed = activateInteraction(interaction, NULL); + + if(proceed && (interaction->getType() < InteractionType::eTRACKED_IN_SCENE_COUNT)) + notifyInteractionActivated(interaction); + } + } + } + } + } + } + mLLContext->secondPassUpdateContactManager(mDt, &mPostNarrowPhase); // Starts update of contact managers +} + +//~BROADPHASE + +void Sc::Scene::registerMaterialInNP(const PxsMaterialCore& materialCore) +{ + mLLContext->getNphaseImplementationContext()->registerMaterial(materialCore); +} + +void Sc::Scene::updateMaterialInNP(const PxsMaterialCore& materialCore) +{ + mLLContext->getNphaseImplementationContext()->updateMaterial(materialCore); +} + +void Sc::Scene::unregisterMaterialInNP(const PxsMaterialCore& materialCore) +{ + mLLContext->getNphaseImplementationContext()->unregisterMaterial(materialCore); +} + +bool Sc::activateInteraction(Sc::Interaction* interaction, void* data) +{ + switch(interaction->getType()) + { + case InteractionType::eOVERLAP: + return static_cast(interaction)->onActivate_(data); + + case InteractionType::eTRIGGER: + return static_cast(interaction)->onActivate_(data); + + case InteractionType::eMARKER: + return static_cast(interaction)->onActivate_(data); + + case InteractionType::eCONSTRAINTSHADER: + return static_cast(interaction)->onActivate_(data); + + case InteractionType::eARTICULATION: + return static_cast(interaction)->onActivate_(data); + + case InteractionType::eTRACKED_IN_SCENE_COUNT: + case InteractionType::eINVALID: + PX_ASSERT(0); + break; + } + return false; +} + +bool Sc::deactivateInteraction(Sc::Interaction* interaction) +{ + switch(interaction->getType()) + { + case InteractionType::eOVERLAP: + return static_cast(interaction)->onDeactivate_(); + + case InteractionType::eTRIGGER: + return static_cast(interaction)->onDeactivate_(); + + case InteractionType::eMARKER: + return static_cast(interaction)->onDeactivate_(); + + case InteractionType::eCONSTRAINTSHADER: + return static_cast(interaction)->onDeactivate_(); + + case InteractionType::eARTICULATION: + return static_cast(interaction)->onDeactivate_(); + + case InteractionType::eTRACKED_IN_SCENE_COUNT: + case InteractionType::eINVALID: + PX_ASSERT(0); + break; + } + return false; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp new file mode 100644 index 000000000..5d53be878 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp @@ -0,0 +1,372 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxErrorCallback.h" +#include "ScShapeSim.h" +#include "ScPhysics.h" +#include "GuConvexMesh.h" +#include "GuTriangleMesh.h" +#include "GuHeightField.h" +#include "ScMaterialCore.h" + +using namespace physx; +using namespace Sc; + +// djs: temporary cruft + +static PxConvexMeshGeometryLL extendForLL(const PxConvexMeshGeometry& hlGeom) +{ + PxConvexMeshGeometryLL llGeom; + static_cast(llGeom) = hlGeom; + + Gu::ConvexMesh* cm = static_cast(hlGeom.convexMesh); + + llGeom.hullData = &(cm->getHull()); + llGeom.gpuCompatible = hlGeom.convexMesh->isGpuCompatible(); + + return llGeom; +} + +static PxTriangleMeshGeometryLL extendForLL(const PxTriangleMeshGeometry& hlGeom) +{ + PxTriangleMeshGeometryLL llGeom; + static_cast(llGeom) = hlGeom; + + Gu::TriangleMesh* tm = static_cast(hlGeom.triangleMesh); + llGeom.meshData = tm; + llGeom.materialIndices = tm->getMaterials(); + llGeom.materials = static_cast(hlGeom).materials; + + return llGeom; +} + +static PxHeightFieldGeometryLL extendForLL(const PxHeightFieldGeometry& hlGeom) +{ + PxHeightFieldGeometryLL llGeom; + static_cast(llGeom) = hlGeom; + + Gu::HeightField* hf = static_cast(hlGeom.heightField); + + llGeom.heightFieldData = &hf->getData(); + + llGeom.materials = static_cast(hlGeom).materials; + + return llGeom; +} + +ShapeCore::ShapeCore(const PxGeometry& geometry, PxShapeFlags shapeFlags, const PxU16* materialIndices, PxU16 materialCount) : + mRestOffset(0.0f), + mTorsionalRadius(0.f), + mMinTorsionalPatchRadius(0.f) +{ + mCore.mOwnsMaterialIdxMemory = true; + + PX_ASSERT(materialCount > 0); + + const PxTolerancesScale& scale = Physics::getInstance().getTolerancesScale(); + mCore.geometry.set(geometry); + mCore.transform = PxTransform(PxIdentity); + mCore.contactOffset = 0.02f * scale.length; + + mCore.mShapeFlags = shapeFlags; + + setMaterialIndices(materialIndices, materialCount); +} + +// PX_SERIALIZATION +ShapeCore::ShapeCore(const PxEMPTY) : + mQueryFilterData (PxEmpty), + mSimulationFilterData (PxEmpty), + mCore (PxEmpty) +{ + mCore.mOwnsMaterialIdxMemory = false; +} +//~PX_SERIALIZATION + +ShapeCore::~ShapeCore() +{ + if(mCore.geometry.getType() == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + if(mCore.mOwnsMaterialIdxMemory) + meshGeom.materials.deallocate(); + } + else if(mCore.geometry.getType() == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + if(mCore.mOwnsMaterialIdxMemory) + hfGeom.materials.deallocate(); + } +} + +PxU16 Sc::ShapeCore::getNbMaterialIndices() const +{ + const PxGeometryType::Enum geomType = mCore.geometry.getType(); + + if((geomType != PxGeometryType::eTRIANGLEMESH) && (geomType != PxGeometryType::eHEIGHTFIELD)) + { + return 1; + } + else if(geomType == PxGeometryType::eTRIANGLEMESH) + { + const PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + return meshGeom.materials.numIndices; + } + else + { + PX_ASSERT(geomType == PxGeometryType::eHEIGHTFIELD); + const PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + return hfGeom.materials.numIndices; + } +} + +const PxU16* Sc::ShapeCore::getMaterialIndices() const +{ + const PxGeometryType::Enum geomType = mCore.geometry.getType(); + + if((geomType != PxGeometryType::eTRIANGLEMESH) && (geomType != PxGeometryType::eHEIGHTFIELD)) + { + return &mCore.materialIndex; + } + else if(geomType == PxGeometryType::eTRIANGLEMESH) + { + const PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + return meshGeom.materials.indices; + } + else + { + PX_ASSERT(geomType == PxGeometryType::eHEIGHTFIELD); + const PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + return hfGeom.materials.indices; + } +} + +PX_FORCE_INLINE void setMaterialsHelper(MaterialIndicesStruct& materials, const PxU16* materialIndices, PxU16 materialIndexCount, PxU8& ownsMemory) +{ + if(materials.numIndices < materialIndexCount) + { + if(materials.indices && ownsMemory) + materials.deallocate(); + materials.allocate(materialIndexCount); + ownsMemory = true; + } + PxMemCopy(materials.indices, materialIndices, sizeof(PxU16)*materialIndexCount); + materials.numIndices = materialIndexCount; +} + +void ShapeCore::setMaterialIndices(const PxU16* materialIndices, PxU16 materialIndexCount) +{ + const PxGeometryType::Enum geomType = mCore.geometry.getType(); + mCore.materialIndex = materialIndices[0]; + + if(geomType == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + setMaterialsHelper(meshGeom.materials, materialIndices, materialIndexCount, mCore.mOwnsMaterialIdxMemory); + } + else if(geomType == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + setMaterialsHelper(hfGeom.materials, materialIndices, materialIndexCount, mCore.mOwnsMaterialIdxMemory); + } +} + +void ShapeCore::setGeometry(const PxGeometry& geom) +{ + const PxGeometryType::Enum oldGeomType = mCore.geometry.getType(); + const PxGeometryType::Enum newGeomType = geom.getType(); + + // copy material related data to restore it after the new geometry has been set + MaterialIndicesStruct materials; + PX_ASSERT(materials.numIndices == 0); + + if(oldGeomType == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + materials = meshGeom.materials; + } + else if(oldGeomType == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + materials = hfGeom.materials; + } + + mCore.geometry.set(geom); + + if((newGeomType == PxGeometryType::eTRIANGLEMESH) || (newGeomType == PxGeometryType::eHEIGHTFIELD)) + { + MaterialIndicesStruct* newMaterials; + + if(newGeomType == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + newMaterials = &meshGeom.materials; + } + else + { + PX_ASSERT(newGeomType == PxGeometryType::eHEIGHTFIELD); + PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + newMaterials = &hfGeom.materials; + } + + if(materials.numIndices != 0) // old type was mesh type + *newMaterials = materials; + else + { // old type was non-mesh type + newMaterials->allocate(1); + *newMaterials->indices = mCore.materialIndex; + mCore.mOwnsMaterialIdxMemory = true; + } + } + else if((materials.numIndices != 0) && mCore.mOwnsMaterialIdxMemory) + { + // geometry changed to non-mesh type + materials.deallocate(); + } +} + +PxShape* ShapeCore::getPxShape() +{ + return Sc::gOffsetTable.convertScShape2Px(this); +} + +const PxShape* ShapeCore::getPxShape() const +{ + return Sc::gOffsetTable.convertScShape2Px(this); +} + +// PX_SERIALIZATION + +PX_FORCE_INLINE void exportExtraDataMaterials(PxSerializationContext& stream, const MaterialIndicesStruct& materials) +{ + stream.alignData(PX_SERIAL_ALIGN); + stream.writeData(materials.indices, sizeof(PxU16)*materials.numIndices); +} + +void ShapeCore::exportExtraData(PxSerializationContext& stream) +{ + const PxGeometryType::Enum geomType = mCore.geometry.getType(); + + if(geomType == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = mCore.geometry.get(); + exportExtraDataMaterials(stream, meshGeom.materials); + } + else if(geomType == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometryLL& hfGeom = mCore.geometry.get(); + exportExtraDataMaterials(stream, hfGeom.materials); + } +} + +void ShapeCore::importExtraData(PxDeserializationContext& context) +{ + const PxGeometryType::Enum geomType = mCore.geometry.getType(); + + if(geomType == PxGeometryType::eTRIANGLEMESH) + { + MaterialIndicesStruct& materials = mCore.geometry.get().materials; + materials.indices = context.readExtraData(materials.numIndices); + } + else if(geomType == PxGeometryType::eHEIGHTFIELD) + { + MaterialIndicesStruct& materials = mCore.geometry.get().materials; + materials.indices = context.readExtraData(materials.numIndices); + } +} + +void ShapeCore::resolveMaterialReference(PxU32 materialTableIndex, PxU16 materialIndex) +{ + if(materialTableIndex == 0) + { + mCore.materialIndex = materialIndex; + } + + PxGeometry& geom = const_cast(mCore.geometry.getGeometry()); + + if(geom.getType() == PxGeometryType::eHEIGHTFIELD) + { + PxHeightFieldGeometryLL& hfGeom = static_cast(geom); + hfGeom.materials.indices[materialTableIndex] = materialIndex; + } + else if(geom.getType() == PxGeometryType::eTRIANGLEMESH) + { + PxTriangleMeshGeometryLL& meshGeom = static_cast(geom); + meshGeom.materials.indices[materialTableIndex] = materialIndex; + } +} + +void ShapeCore::resolveReferences(PxDeserializationContext& context) +{ + // Resolve geometry pointers if needed + PxGeometry& geom = const_cast(mCore.geometry.getGeometry()); + + switch(geom.getType()) + { + case PxGeometryType::eCONVEXMESH: + { + PxConvexMeshGeometryLL& convexGeom = static_cast(geom); + context.translatePxBase(convexGeom.convexMesh); + + // update the hullData pointer + static_cast(geom) = extendForLL(convexGeom); + } + break; + + case PxGeometryType::eHEIGHTFIELD: + { + PxHeightFieldGeometryLL& hfGeom = static_cast(geom); + context.translatePxBase(hfGeom.heightField); + + // update hf pointers + static_cast(geom) = extendForLL(hfGeom); + } + break; + + case PxGeometryType::eTRIANGLEMESH: + { + PxTriangleMeshGeometryLL& meshGeom = static_cast(geom); + context.translatePxBase(meshGeom.triangleMesh); + + // update mesh pointers + static_cast(geom) = extendForLL(meshGeom); + } + break; + case PxGeometryType::eSPHERE: + case PxGeometryType::ePLANE: + case PxGeometryType::eCAPSULE: + case PxGeometryType::eBOX: + case PxGeometryType::eGEOMETRY_COUNT: + case PxGeometryType::eINVALID: + break; + + } +} + +//~PX_SERIALIZATION diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp new file mode 100644 index 000000000..d9c9af0b9 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp @@ -0,0 +1,1208 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScShapeInteraction.h" +#include "ScPhysics.h" +#include "PxsContext.h" +#include "PxsMaterialCombiner.h" +#include "GuTriangleMesh.h" +#include "ScStaticSim.h" +#include "PxvManager.h" +#include "PxsSimpleIslandManager.h" +#include "PxvNphaseImplementationContext.h" + +using namespace physx; + +Sc::ShapeInteraction::ShapeInteraction(ShapeSim& s1, ShapeSim& s2, PxPairFlags pairFlags, PxsContactManager* contactManager) : + ElementSimInteraction (s1, s2, InteractionType::eOVERLAP, InteractionFlag::eRB_ELEMENT|InteractionFlag::eFILTERABLE), + mContactReportStamp (PX_INVALID_U32), + mFlags (0), + mActorPair (NULL), + mReportPairIndex (INVALID_REPORT_PAIR_ID), + mManager (NULL), + mEdgeIndex (IG_INVALID_EDGE), + mReportStreamIndex (0) +{ + // The PxPairFlags get stored in the SipFlag, make sure any changes get noticed + PX_COMPILE_TIME_ASSERT(PxPairFlag::eSOLVE_CONTACT == (1<<0)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eMODIFY_CONTACTS == (1<<1)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_FOUND == (1<<2)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_PERSISTS == (1<<3)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_LOST == (1<<4)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_CCD == (1<<5)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND == (1<<6)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS == (1<<7)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST == (1<<8)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_CONTACT_POINTS == (1<<9)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eDETECT_DISCRETE_CONTACT == (1<<10)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eDETECT_CCD_CONTACT == (1<<11)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::ePRE_SOLVER_VELOCITY == (1<<12)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::ePOST_SOLVER_VELOCITY == (1<<13)); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eCONTACT_EVENT_POSE == (1<<14)); + PX_COMPILE_TIME_ASSERT((PAIR_FLAGS_MASK & PxPairFlag::eSOLVE_CONTACT) == PxPairFlag::eSOLVE_CONTACT); + PX_COMPILE_TIME_ASSERT((PxPairFlag::eSOLVE_CONTACT | PAIR_FLAGS_MASK) == PAIR_FLAGS_MASK); + PX_COMPILE_TIME_ASSERT((PAIR_FLAGS_MASK & PxPairFlag::eCONTACT_EVENT_POSE) == PxPairFlag::eCONTACT_EVENT_POSE); + PX_COMPILE_TIME_ASSERT((PxPairFlag::eCONTACT_EVENT_POSE | PAIR_FLAGS_MASK) == PAIR_FLAGS_MASK); + + setPairFlags(pairFlags); + + //Add a fresh edge to the island manager. + Scene& scene = getScene(); + Sc::BodySim* bs0 = getShape0().getBodySim(); + Sc::BodySim* bs1 = getShape1().getBodySim(); + + PX_ASSERT(bs0); // The shapes have to be sorted such that the first shape belongs to a dynamic + + updateFlags(scene, bs0, bs1, pairFlags); + + if(contactManager == NULL) + { + IG::NodeIndex indexA, indexB; + //if(bs0) // the first shape always belongs to a dynamic body (we assert for this above) + { + indexA = bs0->getNodeIndex(); + bs0->registerCountedInteraction(); + } + if(bs1) + { + indexB = bs1->getNodeIndex(); + bs1->registerCountedInteraction(); + } + + IG::SimpleIslandManager* simpleIslandManager = scene.getSimpleIslandManager(); + + mEdgeIndex = simpleIslandManager->addContactManager(NULL, indexA, indexB, this); + + const bool active = registerInActors(contactManager); // this will call onActivate_() on the interaction + scene.getNPhaseCore()->registerInteraction(this); + scene.registerInteraction(this, active); + } + else + { + onActivate_(contactManager); + } +} + + +Sc::ShapeInteraction::~ShapeInteraction() +{ + Sc::BodySim* body0 = getShape0().getBodySim(); + Sc::BodySim* body1 = getShape1().getBodySim(); + PX_ASSERT(body0); // the first shape always belongs to a dynamic body + + body0->unregisterCountedInteraction(); + if (body1) + body1->unregisterCountedInteraction(); + + if(mManager) + { + destroyManager(); + } + + if(mEdgeIndex != IG_INVALID_EDGE) + { + Scene& scene = getScene(); + + scene.getSimpleIslandManager()->removeConnection(mEdgeIndex); + mEdgeIndex = IG_INVALID_EDGE; + + scene.unregisterInteraction(this); + scene.getNPhaseCore()->unregisterInteraction(this); + } + + // This will remove the interaction from the actors list, which will prevent + // update calls to this actor because of Body::wakeUp below. + unregisterFromActors(); + + if(mReportPairIndex != INVALID_REPORT_PAIR_ID) + { + removeFromReportPairList(); + } +} + +void Sc::ShapeInteraction::clearIslandGenData() +{ + if(mEdgeIndex != IG_INVALID_EDGE) + { + Scene& scene = getScene(); + scene.getSimpleIslandManager()->removeConnection(mEdgeIndex); + mEdgeIndex = IG_INVALID_EDGE; + } +} + +void Sc::ShapeInteraction::visualize(Cm::RenderOutput& out, PxsContactManagerOutputIterator& outputs) +{ + if(mManager) // sleeping pairs have no contact points -> do not visualize + { + Scene& scene = getScene(); + const PxReal scale = scene.getVisualizationScale(); + + size_t ptrActor0 = reinterpret_cast(&getShape0().getRbSim()); + size_t ptrActor1 = reinterpret_cast(&getShape1().getRbSim()); + const PxReal flipNormal = (ptrActor0 < ptrActor1) ? 1.0f : -1.0f; + + PxU32 offset; + PxU32 nextOffset = 0; + do + { + const void* contactPatches; + const void* contactPoints; + PxU32 contactDataSize; + PxU32 contactPointCount; + PxU32 contactPatchCount; + const PxReal* impulses; + + offset = nextOffset; + nextOffset = getContactPointData(contactPatches, contactPoints, contactDataSize, contactPointCount, contactPatchCount, impulses, offset, outputs); + + const PxReal param_contactForce = scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_FORCE); + const PxReal param_contactNormal = scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL); + const PxReal param_contactError = scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_ERROR); + const PxReal param_contactPoint = scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT); + + const PxU32* faceIndices = reinterpret_cast(impulses + contactPointCount); + PxContactStreamIterator iter(reinterpret_cast(contactPatches), reinterpret_cast(contactPoints), faceIndices, contactPatchCount, contactPointCount); + + PxU32 i = 0; + while(iter.hasNextPatch()) + { + iter.nextPatch(); + while(iter.hasNextContact()) + { + iter.nextContact(); + + PxReal length = 0; + PxU32 color = 0; + + if((param_contactForce != 0.0f) && impulses) + { + length = scale * param_contactForce * impulses[i]; + color = 0xff0000; + } + else if(param_contactNormal != 0.0f) + { + length = scale * param_contactNormal; + color = 0x0000ff; + } + else if(param_contactError != 0.0f) + { + length = PxAbs(scale * param_contactError * iter.getSeparation()); + color = 0xffff00; + } + + if(length != 0.0f) + out << Cm::RenderOutput::LINES << color << iter.getContactPoint() << iter.getContactPoint() + iter.getContactNormal() * length * flipNormal; + + if(param_contactPoint != 0) + { + PxReal s = scale * 0.1f; + PxVec3 point = iter.getContactPoint(); + + if(0) //temp debug to see identical contacts + point.x += scale * 0.01f * (contactPointCount - i + 1); + + out << Cm::RenderOutput::LINES << PxU32(PxDebugColor::eARGB_RED); + out << point + PxVec3(-s,0,0) << point + PxVec3(s,0,0); + out << point + PxVec3(0,-s,0) << point + PxVec3(0,s,0); + out << point + PxVec3(0,0,-s) << point + PxVec3(0,0,s); + + } + } + } + } + while(nextOffset != offset); + } +} + +PX_FORCE_INLINE void Sc::ShapeInteraction::processReportPairOnActivate() +{ + PX_ASSERT(isReportPair()); + PX_ASSERT(mReportPairIndex == INVALID_REPORT_PAIR_ID); + + if(readFlag(WAS_IN_PERSISTENT_EVENT_LIST)) + { + getScene().getNPhaseCore()->addToPersistentContactEventPairs(this); + mFlags &= ~WAS_IN_PERSISTENT_EVENT_LIST; + } +} + +PX_FORCE_INLINE void Sc::ShapeInteraction::processReportPairOnDeactivate() +{ + PX_ASSERT(isReportPair()); + PX_ASSERT(mReportPairIndex != INVALID_REPORT_PAIR_ID); + PX_COMPILE_TIME_ASSERT(IS_IN_PERSISTENT_EVENT_LIST == (WAS_IN_PERSISTENT_EVENT_LIST >> 1)); + PX_ASSERT(!(readFlag(WAS_IN_PERSISTENT_EVENT_LIST))); + + const PxU32 wasInPersList = (mFlags & IS_IN_PERSISTENT_EVENT_LIST) << 1; + mFlags |= wasInPersList; + + removeFromReportPairList(); +} + +void Sc::ShapeInteraction::setContactReportPostSolverVelocity(ContactStreamManager& cs) +{ + Scene& scene = getScene(); + NPhaseCore* npcore = scene.getNPhaseCore(); + PxU8* stream = npcore->getContactReportPairData(cs.bufferIndex); + + ActorPairReport& apr = getActorPairReport(); + cs.setContactReportPostSolverVelocity(stream, apr.getActorA(), apr.getActorB()); +} + +void Sc::ShapeInteraction::resetManagerCachedState() const +{ + if(mManager) + { + Sc::Scene& scene = getScene(); + PxvNphaseImplementationContext* nphaseImplementationContext = scene.getLowLevelContext()->getNphaseImplementationContext(); + PX_ASSERT(nphaseImplementationContext); + + mManager->resetFrictionCachedState(); + nphaseImplementationContext->refreshContactManager(mManager); + } +} + +/* + This method can be called from various stages in the pipeline, some of which operate before the actor has advanced its pose and some after it has advanced its pose. + Discrete touch found events operate before the pose has been updated. This is because we are using the found event to active the bodies before solve so that we can just + solve the activated bodies. + Lost touch events occur after the pose has been updated. +*/ +void Sc::ShapeInteraction::processUserNotificationSync() +{ + PX_ASSERT(hasTouch()); + + if (mManager) + Ps::prefetchLine(mManager); + + Scene& scene = getScene(); + NPhaseCore* npcore = scene.getNPhaseCore(); + PX_UNUSED(npcore); + + // make sure shape A and shape B are the same way round as the actors (in compounds they may be swapped) + // TODO: make "unswapped" a SIP flag and set it in updateState() + if (mActorPair == NULL) + { + return; + } + ActorPairReport& aPairReport = getActorPairReport(); + + if (!aPairReport.isInContactReportActorPairSet()) + { + aPairReport.setInContactReportActorPairSet(); + npcore->addToContactReportActorPairSet(&aPairReport); + aPairReport.incRefCount(); + } + + aPairReport.createContactStreamManager(*npcore); +} + +void Sc::ShapeInteraction::processUserNotificationAsync(PxU32 contactEvent, PxU16 infoFlags, bool touchLost, + const PxU32 ccdPass, const bool useCurrentTransform, PxsContactManagerOutputIterator& outputs, ContactReportAllocationManager* alloc) +{ + + contactEvent = (!ccdPass) ? contactEvent : (contactEvent | PxPairFlag::eNOTIFY_TOUCH_CCD); + + if (mActorPair == NULL) + { + return; + } + + ActorPairReport& aPairReport = getActorPairReport(); + Scene& scene = getScene(); + NPhaseCore* npcore = scene.getNPhaseCore(); + ContactStreamManager& cs = aPairReport.createContactStreamManager(*npcore); + // Prepare user notification + PxU32 timeStamp = scene.getTimeStamp(); + PxU32 shapePairTimeStamp = scene.getReportShapePairTimeStamp(); + + const PxU32 pairFlags = getPairFlags(); + PX_ASSERT(pairFlags & contactEvent); + + const PxU32 extraDataFlags = pairFlags & CONTACT_REPORT_EXTRA_DATA; + + PxU8* stream = NULL; + ContactShapePair* pairStream = NULL; + + const bool unswapped = &aPairReport.getActorA() == &getShape0().getRbSim(); + const Sc::ShapeSim& shapeA = unswapped ? getShape0() : getShape1(); + const Sc::ShapeSim& shapeB = unswapped ? getShape1() : getShape0(); + + if(aPairReport.streamResetStamp(timeStamp)) + { + PX_ASSERT(mContactReportStamp != shapePairTimeStamp); // actor pair and shape pair timestamps must both be out of sync in this case + + PxU16 maxCount; + if(cs.maxPairCount != 0) + maxCount = cs.maxPairCount; // use value from previous report + else + { + // TODO: Use some kind of heuristic + maxCount = 2; + cs.maxPairCount = maxCount; + } + + PxU32 maxExtraDataSize; + if(!extraDataFlags || touchLost) + { + maxExtraDataSize = 0; + cs.setMaxExtraDataSize(maxExtraDataSize); + } + else + { + PxU32 currentMaxExtraDataSize = cs.getMaxExtraDataSize(); + maxExtraDataSize = ContactStreamManager::computeContactReportExtraDataSize(extraDataFlags, true); + PX_ASSERT(maxExtraDataSize > 0); + if(maxExtraDataSize <= currentMaxExtraDataSize) + maxExtraDataSize = currentMaxExtraDataSize; // use value from previous report + else + cs.setMaxExtraDataSize(maxExtraDataSize); + } + + stream = npcore->reserveContactReportPairData(maxCount, maxExtraDataSize, cs.bufferIndex, alloc); + + if(!maxExtraDataSize) // this is the usual case, so set it first for branch prediction + cs.reset(); + else if(stream) + { + cs.reset(); + PX_ASSERT(extraDataFlags); + PX_ASSERT(!touchLost); + + cs.fillInContactReportExtraData(stream, extraDataFlags, aPairReport.getActorA(), aPairReport.getActorB(), ccdPass, useCurrentTransform, 0, sizeof(ContactStreamHeader)); + if((extraDataFlags & PxPairFlag::ePOST_SOLVER_VELOCITY) && (pairFlags & PxPairFlag::eDETECT_CCD_CONTACT)) + scene.setPostSolverVelocityNeeded(); + } + } + else + { + const PxU32 currentPairCount = cs.currentPairCount; + if(currentPairCount != 0) + { + PxU8* tmpStreamPtr = npcore->getContactReportPairData(cs.bufferIndex); + if(!extraDataFlags) + stream = tmpStreamPtr; // this is the usual case, so set it first for branch prediction + else + { + if(!touchLost) + { + // - the first few shape pair events might not request extra data + // - the events so far were due to touch lost + // - multiple reports due to CCD multiple passes + // Hence, the extra data has to be created/extended now. + // + const PxU16 oldExtraDataSize = cs.extraDataSize; + PxI32 lastContactPass; + if(oldExtraDataSize) + { + ContactStreamHeader* strHeader = reinterpret_cast(tmpStreamPtr); + lastContactPass = strHeader->contactPass; + } + else + lastContactPass = -1; + + if(PxI32(ccdPass) > lastContactPass) // do not send extra data mulitple times for the same contact pass + { + const PxU16 extraDataSize = PxU16(oldExtraDataSize + ContactStreamManager::computeContactReportExtraDataSize(extraDataFlags, (oldExtraDataSize == 0))); + PxU8* strPtr; + if (extraDataSize <= cs.getMaxExtraDataSize()) + strPtr = tmpStreamPtr; + else + strPtr = npcore->resizeContactReportPairData(currentPairCount < cs.maxPairCount ? cs.maxPairCount : PxU32(cs.maxPairCount+1), extraDataSize, cs); + // the check for max pair count is there to avoid another potential allocation further below + + if(strPtr) + { + stream = strPtr; + PxU32 sizeOffset; + if(oldExtraDataSize) + sizeOffset = oldExtraDataSize; + else + sizeOffset = sizeof(ContactStreamHeader); + cs.fillInContactReportExtraData(strPtr, extraDataFlags, aPairReport.getActorA(), aPairReport.getActorB(), ccdPass, useCurrentTransform, currentPairCount, sizeOffset); + if((extraDataFlags & PxPairFlag::ePOST_SOLVER_VELOCITY) && (pairFlags & PxPairFlag::eDETECT_CCD_CONTACT)) + scene.setPostSolverVelocityNeeded(); + } + else + { + stream = tmpStreamPtr; + cs.raiseFlags(ContactStreamManagerFlag::eINCOMPLETE_STREAM); + } + } + else + stream = tmpStreamPtr; + } + else + stream = tmpStreamPtr; + } + } + } + + if(stream) + pairStream = cs.getShapePairs(stream); + else + { + cs.raiseFlags(ContactStreamManagerFlag::eINVALID_STREAM); + return; + } + + ContactShapePair* cp; + if(mContactReportStamp != shapePairTimeStamp) + { + // this shape pair is not in the contact notification stream yet + + if(cs.currentPairCount < cs.maxPairCount) + cp = pairStream + cs.currentPairCount; + else + { + const PxU32 newSize = PxU32(cs.currentPairCount + (cs.currentPairCount >> 1) + 1); + stream = npcore->resizeContactReportPairData(newSize, cs.getMaxExtraDataSize(), cs); + if(stream) + { + pairStream = cs.getShapePairs(stream); + cp = pairStream + cs.currentPairCount; + } + else + { + cs.raiseFlags(ContactStreamManagerFlag::eINCOMPLETE_STREAM); + return; + } + } + + //!!! why is alignment important here? Looks almost like some refactor nonsense + PX_ASSERT(0==(reinterpret_cast(stream) & 0x0f)); // check 16Byte alignment + + mReportStreamIndex = cs.currentPairCount; + cp->shapes[0] = shapeA.getPxShape(); + cp->shapes[1] = shapeB.getPxShape(); + cp->contactPatches = NULL; + cp->contactPoints = NULL; + cp->contactForces = NULL; + cp->contactCount = 0; + cp->patchCount = 0; + cp->constraintStreamSize = 0; + cp->requiredBufferSize = 0; + cp->flags = infoFlags; + PX_ASSERT(contactEvent <= 0xffff); + cp->events = PxU16(contactEvent); + cp->shapeID[0] = shapeA.getID(); + cp->shapeID[1] = shapeB.getID(); + + cs.currentPairCount++; + + mContactReportStamp = shapePairTimeStamp; + } + else + { + // this shape pair is in the contact notification stream already but there is a second event (can happen with force threshold reports, for example). + + PX_ASSERT(mReportStreamIndex < cs.currentPairCount); + cp = &pairStream[mReportStreamIndex]; + cp->events |= contactEvent; + if(touchLost && cp->events & PxPairFlag::eNOTIFY_TOUCH_PERSISTS) + cp->events &= PxU16(~PxPairFlag::eNOTIFY_TOUCH_PERSISTS); + cp->flags |= infoFlags; + } + + if((getPairFlags() & PxPairFlag::eNOTIFY_CONTACT_POINTS) && mManager && (!cp->contactPatches) && !(contactEvent & PxU32(PxPairFlag::eNOTIFY_TOUCH_LOST | PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST))) + { + const PxcNpWorkUnit& workUnit = mManager->getWorkUnit(); + PxsContactManagerOutput* output = NULL; + if(workUnit.mNpIndex & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK) + output = &getScene().getLowLevelContext()->getNphaseImplementationContext()->getNewContactManagerOutput(workUnit.mNpIndex); + else + output = &outputs.getContactManager(workUnit.mNpIndex); + + const PxsCCDContactHeader* ccdContactData = reinterpret_cast(workUnit.ccdContacts); + + const bool isCCDPass = (ccdPass != 0); + if((output->nbPatches && !isCCDPass) || (ccdContactData && (!ccdContactData->isFromPreviousPass) && isCCDPass)) + { + const PxU8* contactPatchData; + const PxU8* contactPointData; + PxU32 cDataSize; + PxU32 alignedContactDataSize; + const PxReal* impulses; + + PxU32 nbPoints = output->nbContacts; + PxU32 contactPatchCount = output->nbPatches; + + if(!isCCDPass) + { + PX_ASSERT(0==(reinterpret_cast(output->contactPatches) & 0x0f)); // check 16Byte alignment + contactPatchData = output->contactPatches; + contactPointData = output->contactPoints; + cDataSize = sizeof(PxContactPatch)*output->nbPatches + sizeof(PxContact)*output->nbContacts; + alignedContactDataSize = (cDataSize + 0xf) & 0xfffffff0; + impulses = output->contactForces; + } + else + { + PX_ASSERT(0==(reinterpret_cast(ccdContactData) & 0x0f)); // check 16Byte alignment + contactPatchData = reinterpret_cast(ccdContactData) + sizeof(PxsCCDContactHeader); + contactPointData = contactPatchData + sizeof(PxContactPatch); + cDataSize = ccdContactData->contactStreamSize - sizeof(PxsCCDContactHeader); + PxU32 tmpAlignedSize = (ccdContactData->contactStreamSize + 0xf) & 0xfffffff0; + alignedContactDataSize = tmpAlignedSize - sizeof(PxsCCDContactHeader); + impulses = reinterpret_cast(contactPatchData + alignedContactDataSize); + nbPoints = 1; + contactPatchCount = 1; + } + + infoFlags = cp->flags; + infoFlags |= unswapped ? 0 : PxContactPairFlag::eINTERNAL_CONTACTS_ARE_FLIPPED; + + //PX_ASSERT(0==(reinterpret_cast(impulses) & 0x0f)); + + const PxU32 impulseSize = impulses ? (nbPoints * sizeof(PxReal)) : 0; + if(impulseSize) + infoFlags |= PxContactPairFlag::eINTERNAL_HAS_IMPULSES; + cp->contactPatches = contactPatchData; + cp->contactPoints = contactPointData; + cp->contactCount = Ps::to8(nbPoints); + cp->patchCount = Ps::to8(contactPatchCount); + cp->constraintStreamSize = Ps::to16(cDataSize); + cp->requiredBufferSize = alignedContactDataSize + impulseSize; + cp->contactForces = impulses; + + cp->flags = infoFlags; + } + } +} + +void Sc::ShapeInteraction::processUserNotification(PxU32 contactEvent, PxU16 infoFlags, bool touchLost, const PxU32 ccdPass, const bool useCurrentTransform, PxsContactManagerOutputIterator& outputs) +{ + processUserNotificationSync(); + processUserNotificationAsync(contactEvent, infoFlags, touchLost, ccdPass, useCurrentTransform, outputs); +} + +PxU32 Sc::ShapeInteraction::getContactPointData(const void*& contactPatches, const void*& contactPoints, PxU32& contactDataSize, PxU32& contactPointCount, PxU32& numPatches, const PxReal*& impulses, PxU32 startOffset, + PxsContactManagerOutputIterator& outputs) +{ + // Process LL generated contacts + if(mManager != NULL) + { + PxcNpWorkUnit& workUnit = mManager->getWorkUnit(); + + PxsContactManagerOutput* output = NULL; + + if(workUnit.mNpIndex & PxsContactManagerBase::NEW_CONTACT_MANAGER_MASK) + { + output = &getScene().getLowLevelContext()->getNphaseImplementationContext()->getNewContactManagerOutput(workUnit.mNpIndex); + } + else + { + output = &outputs.getContactManager(workUnit.mNpIndex); + } + + /*const void* dcdContactPatches; + const void* dcdContactPoints; + PxU32 dcdContactPatchCount; + const PxReal* dcdImpulses; + const PxsCCDContactHeader* ccdContactStream; + PxU32 dcdContactCount = mManager->getContactPointData(dcdContactPatches, dcdContactPoints, dcdContactPatchCount, dcdImpulses, ccdContactStream); + + PX_ASSERT(((dcdContactCount == 0) && (!ccdContactStream)) || ((dcdContactCount > 0) && hasTouch()) || (ccdContactStream && hasCCDTouch()));*/ + + const PxsCCDContactHeader* ccdContactStream = reinterpret_cast(workUnit.ccdContacts); + + PxU32 idx = 0; + if(output->nbContacts) + { + if(startOffset == 0) + { + contactPatches = output->contactPatches; + contactPoints = output->contactPoints; + contactDataSize = sizeof(PxContactPatch) * output->nbPatches + sizeof(PxContact) * output->nbContacts; + contactPointCount = output->nbContacts; + numPatches = output->nbPatches; + impulses = output->contactForces; + + if(!ccdContactStream) + return startOffset; + else + return (startOffset + 1); + } + + idx++; + } + + while(ccdContactStream) + { + if(startOffset == idx) + { + const PxU8* stream = reinterpret_cast(ccdContactStream); + PxU16 streamSize = ccdContactStream->contactStreamSize; + contactPatches = stream + sizeof(PxsCCDContactHeader); + contactPoints = stream + sizeof(PxsCCDContactHeader) + sizeof(PxContactPatch); + contactDataSize = streamSize - sizeof(PxsCCDContactHeader); + contactPointCount = 1; + numPatches = 1; + impulses = reinterpret_cast(stream + ((streamSize + 0xf) & 0xfffffff0)); + + if(!ccdContactStream->nextStream) + return startOffset; + else + return (startOffset + 1); + } + + idx++; + ccdContactStream = ccdContactStream->nextStream; + } + } + + contactPatches = NULL; + contactPoints = NULL; + contactDataSize = 0; + contactPointCount = 0; + numPatches = 0; + impulses = NULL; + return startOffset; +} + +// Note that LL will not send end touch events for managers that are destroyed while having contact +void Sc::ShapeInteraction::managerNewTouch(const PxU32 ccdPass, bool adjustCounters, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + if(readFlag(HAS_TOUCH)) + return; // Do not count the touch twice (for instance when recreating a manager with touch) + // We have contact this frame + setHasTouch(); + + if(adjustCounters) + adjustCountersOnNewTouch(useAdaptiveForce); + + if(!isReportPair()) + return; + else + { + PX_ASSERT(hasTouch()); + PX_ASSERT(!readFlag(IS_IN_PERSISTENT_EVENT_LIST)); + PX_ASSERT(!readFlag(IS_IN_FORCE_THRESHOLD_EVENT_LIST)); + + const PxU32 pairFlags = getPairFlags(); + if(pairFlags & PxPairFlag::eNOTIFY_TOUCH_FOUND) + { + PxU16 infoFlag = 0; + if(mActorPair->getTouchCount() == 1) // this code assumes that the actor pair touch count does get incremented beforehand + { + infoFlag = PxContactPairFlag::eACTOR_PAIR_HAS_FIRST_TOUCH; + } + + processUserNotification(PxPairFlag::eNOTIFY_TOUCH_FOUND, infoFlag, false, ccdPass, true, outputs); + } + + if(pairFlags & PxPairFlag::eNOTIFY_TOUCH_PERSISTS) + { + getScene().getNPhaseCore()->addToPersistentContactEventPairsDelayed(this); // to make sure that from now on, the pairs are tested for persistent contact events + } + else if(pairFlags & ShapeInteraction::CONTACT_FORCE_THRESHOLD_PAIRS) + { + // new touch -> need to start checking for force threshold events + // Note: this code assumes that it runs before the pairs get tested for force threshold exceeded + getScene().getNPhaseCore()->addToForceThresholdContactEventPairs(this); + } + } +} + + +bool Sc::ShapeInteraction::managerLostTouch(const PxU32 ccdPass, bool adjustCounters, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce) +{ + if(!readFlag(HAS_TOUCH)) + return false; + + // We do not have LL contacts this frame and also we lost LL contact this frame + + if(!isReportPair()) + { + setHasNoTouch(); + } + else + { + PX_ASSERT(hasTouch()); + + sendLostTouchReport(false, ccdPass, outputs); + + if(readFlag(IS_IN_CONTACT_EVENT_LIST)) + { + // don't need to worry about persistent/force-threshold contact events until next new touch + + if(readFlag(IS_IN_FORCE_THRESHOLD_EVENT_LIST)) + { + getScene().getNPhaseCore()->removeFromForceThresholdContactEventPairs(this); + } + else + { + PX_ASSERT(readFlag(IS_IN_PERSISTENT_EVENT_LIST)); + getScene().getNPhaseCore()->removeFromPersistentContactEventPairs(this); + } + + clearFlag(FORCE_THRESHOLD_EXCEEDED_FLAGS); + } + + setHasNoTouch(); + } + + BodySim* body0 = getShape0().getBodySim(); + BodySim* body1 = getShape1().getBodySim(); + PX_ASSERT(body0); // the first shape always belongs to a dynamic body + + if(adjustCounters) + adjustCountersOnLostTouch(body0, body1, useAdaptiveForce); + + if(!body1) + { + body0->internalWakeUp(); + return false; + } + return true; +} + + +PX_FORCE_INLINE void Sc::ShapeInteraction::updateFlags(const Sc::Scene& scene, const Sc::BodySim* bs0, const Sc::BodySim* bs1, const PxU32 pairFlags) +{ + PX_ASSERT(bs0); // the first shape always belongs to a dynamic body + + // Check if collision response is disabled + bool enabled = (!bs0->isKinematic()) || (bs1 && !bs1->isKinematic()); // If the pair has no dynamic body then disable response + enabled = enabled && (pairFlags & PxPairFlag::eSOLVE_CONTACT); + setFlag(CONTACTS_RESPONSE_DISABLED, !enabled); + + // Check if contact points needed + setFlag(CONTACTS_COLLECT_POINTS, ( (pairFlags & PxPairFlag::eNOTIFY_CONTACT_POINTS) || + (pairFlags & PxPairFlag::eMODIFY_CONTACTS) || + scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT) || + scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL) || + scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_ERROR) || + scene.getVisualizationParameter(PxVisualizationParameter::eCONTACT_FORCE)) ); +} + +void Sc::ShapeInteraction::updateState(const PxU8 externalDirtyFlags) +{ + const PxU32 oldContactState = getManagerContactState(); + + const PxU8 dirtyFlags = PxU8(getDirtyFlags() | externalDirtyFlags); + const PxU32 pairFlags = getPairFlags(); + Scene& scene = getScene(); + + if(dirtyFlags & (InteractionDirtyFlag::eFILTER_STATE | InteractionDirtyFlag::eVISUALIZATION)) + { + Sc::BodySim* bs0 = getShape0().getBodySim(); + Sc::BodySim* bs1 = getShape1().getBodySim(); + + Ps::IntBool wasDisabled = readFlag(CONTACTS_RESPONSE_DISABLED); + + updateFlags(scene, bs0, bs1, pairFlags); + + Ps::IntBool isDisabled = readFlag(CONTACTS_RESPONSE_DISABLED); + + if(!wasDisabled && isDisabled) + { + scene.getSimpleIslandManager()->setEdgeDisconnected(mEdgeIndex); + } + else if(wasDisabled && !isDisabled) + { + if(readFlag(ShapeInteraction::HAS_TOUCH)) + { + scene.getSimpleIslandManager()->setEdgeConnected(mEdgeIndex); + } + } + } + + const PxU32 newContactState = getManagerContactState(); + const bool recreateManager = (oldContactState != newContactState); + + // No use in updating manager properties if the manager is going to be re-created or does not exist yet + if((!recreateManager) && (mManager != 0)) + { + ShapeSim& shapeSim0 = getShape0(); + ShapeSim& shapeSim1 = getShape1(); + + // Update dominance + if(dirtyFlags & InteractionDirtyFlag::eDOMINANCE) + { + Sc::BodySim* bs0 = shapeSim0.getBodySim(); + Sc::BodySim* bs1 = shapeSim1.getBodySim(); + PX_ASSERT(bs0); // the first shape always belongs to a dynamic body + + // Static actors are in dominance group zero and must remain there + const PxDominanceGroup dom0 = bs0->getActorCore().getDominanceGroup(); + const PxDominanceGroup dom1 = bs1 ? bs1->getActorCore().getDominanceGroup() : PxDominanceGroup(0); + + const PxDominanceGroupPair cdom = getScene().getDominanceGroupPair(dom0, dom1); + mManager->setDominance0(cdom.dominance0); + mManager->setDominance1(cdom.dominance1); + } + + if (dirtyFlags & InteractionDirtyFlag::eBODY_KINEMATIC) + { + //Kinematic flags changed - clear flag for kinematic on the pair + Sc::BodySim* bs1 = shapeSim1.getBodySim(); + if (bs1 != NULL) + { + if (bs1->isKinematic()) + { + mManager->getWorkUnit().flags |= PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR; + } + else + { + mManager->getWorkUnit().flags &= (~PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR); + } + } + } + + // Update skin width + if(dirtyFlags & InteractionDirtyFlag::eREST_OFFSET) + { + mManager->setRestDistance(shapeSim0.getRestOffset() + shapeSim1.getRestOffset()); + } + + //we may want to only write these if they have changed, the set code is a bit painful for the integration flags because of bit unpacking + packing. + mManager->setCCD((getPairFlags() & PxPairFlag::eDETECT_CCD_CONTACT) != 0); + } + else if (readInteractionFlag(InteractionFlag::eIS_ACTIVE)) // only re-create the manager if the pair is active + { + PX_ASSERT(mManager); // if the pair is active, there has to be a manager + + if (dirtyFlags & InteractionDirtyFlag::eBODY_KINEMATIC) + { + //Kinematic->dynamic transition + const IG::IslandSim& islandSim = getScene().getSimpleIslandManager()->getSpeculativeIslandSim(); + + // + //check whether active in the speculative sim! + const BodySim* bodySim0 = getShape0().getBodySim(); + const BodySim* bodySim1 = getShape1().getBodySim(); + + if (!islandSim.getNode(bodySim0->getNodeIndex()).isActiveOrActivating() && + (bodySim1 == NULL || !islandSim.getNode(bodySim1->getNodeIndex()).isActiveOrActivating())) + { + onDeactivate_(); + scene.notifyInteractionDeactivated(this); + } + else + { + //Else we are allowed to be active, so recreate + if (mEdgeIndex != IG_INVALID_EDGE) + scene.getSimpleIslandManager()->clearEdgeRigidCM(mEdgeIndex); + destroyManager(); + createManager(NULL); + } + } + else + { + + PX_ASSERT(activeManagerAllowed()); + + // A) This is a newly created pair + // + // B) The contact notification or processing state has changed. + // All existing managers need to be deleted and recreated with the correct flag set + // These flags can only be set at creation in LL + //KS - added this code here because it is no longer done in destroyManager() - a side-effect of the parallelization of the interaction management code + if (mEdgeIndex != IG_INVALID_EDGE) + scene.getSimpleIslandManager()->clearEdgeRigidCM(mEdgeIndex); + destroyManager(); + createManager(NULL); + } + } +} + +bool Sc::ShapeInteraction::onActivate_(void* contactManager) +{ + if(isReportPair())// && !(infoFlag & ActorSim::AS_PART_OF_ISLAND_GEN_PASS_1)) + { + // for pairs that go through a second island pass, there is the possibility that they get put to sleep again after the second pass. + // So we do not want to check for re-insertion into the persistent report pair list yet. + processReportPairOnActivate(); + } + + if(updateManager(contactManager)) + { + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; + } + else + return false; +} + + +bool Sc::ShapeInteraction::onDeactivate_() +{ + PX_ASSERT(getShape0().getActor().isDynamicRigid() || getShape1().getActor().isDynamicRigid()); + + const BodySim* bodySim0 = getShape0().getBodySim(); + const BodySim* bodySim1 = getShape1().getBodySim(); + PX_ASSERT(bodySim0); // the first shape always belongs to a dynamic body + + PX_ASSERT( (!bodySim0 && bodySim1 && !bodySim1->isActive()) || + (!bodySim1 && bodySim0 && !bodySim0->isActive()) || + ((bodySim0 && bodySim1 && (!bodySim0->isActive() || !bodySim1->isActive()))) ); + + if((!bodySim0->isActive()) && (!bodySim1 || !bodySim1->isActive())) + { + if(mReportPairIndex != INVALID_REPORT_PAIR_ID) + { + processReportPairOnDeactivate(); + } + + PX_ASSERT((mManager->getTouchStatus() > 0) == (hasTouch() > 0)); + + if(mManager != 0) + { + if((!readFlag(TOUCH_KNOWN)) && mManager->touchStatusKnown() && (!mManager->getTouchStatus())) + { + // for pairs that are inserted asleep, we do not know the touch state. If they run through narrowphase and a touch is found, + // then a managerNewTouch() call will inform this object about the found touch. However, if narrowphase detects that there + // is no touch, this object will not be informed about it. The low level manager will always know though. Now, before destroying + // the pair manager, we need to record "does not have touch" state if available. + raiseFlag(HAS_NO_TOUCH); + } + + destroyManager(); + if(mEdgeIndex != IG_INVALID_EDGE) + getScene().getSimpleIslandManager()->clearEdgeRigidCM(mEdgeIndex); + } + Scene& scene = getScene(); + scene.getSimpleIslandManager()->deactivateEdge(mEdgeIndex); + + // + // We distinguish two scenarios here: + // + // A) island generation deactivates objects: + // -> the deactivated body was active + // -> narrowphase ran on this pair + // -> the touch status is known + // -> touch: the objects of the pair are in the same island + // -> no touch: the objects of the pair are in different islands + // + // As a consequence, the edge state is not changed. The assumption is that anything that could break the touch status + // from here on will have to mark the edges connected (for example if the object gets moved). + // + // B) user deactivates objects: + // -> the touch status might not be known (for example, the pose gets integrated after the solver which might cause a change + // in touch status. If the object gets put to sleep after that, we have to be conservative and mark the edge connected. + // other example: an active object gets moved by the user and then deactivated). + // + + clearInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; + } + else + { + return false; + } +} + +void Sc::ShapeInteraction::createManager(void* contactManager) +{ + //PX_PROFILE_ZONE("ShapeInteraction.createManager", 0); + + Sc::Scene& scene = getScene(); + + const PxU32 pairFlags = getPairFlags(); + + const int disableCCDContact = !(pairFlags & PxPairFlag::eDETECT_CCD_CONTACT); + + PxsContactManager* manager = scene.getLowLevelContext()->createContactManager(reinterpret_cast(contactManager), !disableCCDContact); + PxcNpWorkUnit& mNpUnit = manager->getWorkUnit(); + + // Check if contact generation callback has been ordered on the pair + int contactChangeable = 0; + if(pairFlags & PxPairFlag::eMODIFY_CONTACTS) + contactChangeable = 1; + + ShapeSim& shapeSim0 = getShape0(); + ShapeSim& shapeSim1 = getShape1(); + + const PxActorType::Enum type0 = shapeSim0.getActor().getActorType(); + const PxActorType::Enum type1 = shapeSim1.getActor().getActorType(); + + const int disableResponse = readFlag(CONTACTS_RESPONSE_DISABLED) ? 1 : 0; + const int disableDiscreteContact = !(pairFlags & PxPairFlag::eDETECT_DISCRETE_CONTACT); + const int reportContactInfo = readFlag(CONTACTS_COLLECT_POINTS); + const int hasForceThreshold = !disableResponse && (pairFlags & CONTACT_FORCE_THRESHOLD_PAIRS); + int touching; + if(readFlag(TOUCH_KNOWN)) + touching = readFlag(HAS_TOUCH) ? 1 : -1; + else + touching = 0; + + // Static actors are in dominance group zero and must remain there + + Sc::BodySim* bs0 = shapeSim0.getBodySim(); + Sc::BodySim* bs1 = shapeSim1.getBodySim(); + + PX_ASSERT(bs0); // The shapes have to be sorted such that the first shape belongs to a dynamic + + const PxDominanceGroup dom0 = bs0->getActorCore().getDominanceGroup(); + const PxDominanceGroup dom1 = bs1 ? bs1->getActorCore().getDominanceGroup() : PxDominanceGroup(0); + + const bool kinematicActor = bs1 ? bs1->isKinematic() : false; + + const PxDominanceGroupPair cdom = scene.getDominanceGroupPair(dom0, dom1); + + const PxI32 hasArticulations= (type0 == PxActorType::eARTICULATION_LINK) | (type1 == PxActorType::eARTICULATION_LINK)<<1; + const PxI32 hasDynamics = (type0 != PxActorType::eRIGID_STATIC) | (type1 != PxActorType::eRIGID_STATIC)<<1; + + const PxsShapeCore* shapeCore0 = &shapeSim0.getCore().getCore(); + const PxsShapeCore* shapeCore1 = &shapeSim1.getCore().getCore(); + + //Initialize the manager.... + + manager->mRigidBody0 = &bs0->getLowLevelBody(); + manager->mRigidBody1 = bs1 ? &bs1->getLowLevelBody() : NULL; + manager->mShapeInteraction = this; + mNpUnit.shapeCore0 = shapeCore0; + mNpUnit.shapeCore1 = shapeCore1; + + PX_ASSERT(shapeCore0->transform.isValid() && shapeCore1->transform.isValid()); + + mNpUnit.rigidCore0 = &shapeSim0.getPxsRigidCore(); + mNpUnit.rigidCore1 = &shapeSim1.getPxsRigidCore(); + + mNpUnit.restDistance = shapeSim0.getRestOffset() + shapeSim1.getRestOffset(); + mNpUnit.dominance0 = cdom.dominance0; + mNpUnit.dominance1 = cdom.dominance1; + mNpUnit.geomType0 = PxU8(shapeCore0->geometry.getType()); + mNpUnit.geomType1 = PxU8(shapeCore1->geometry.getType()); + mNpUnit.mTransformCache0 = shapeSim0.getTransformCacheID(); + mNpUnit.mTransformCache1 = shapeSim1.getTransformCacheID(); + + mNpUnit.mTorsionalPatchRadius = PxMax(shapeSim0.getTorsionalPatchRadius(),shapeSim1.getTorsionalPatchRadius()); + mNpUnit.mMinTorsionalPatchRadius = PxMax(shapeSim0.getMinTorsionalPatchRadius(), shapeSim1.getMinTorsionalPatchRadius()); + + PxU16 wuflags = 0; + + if(hasArticulations & 1) + wuflags |= PxcNpWorkUnitFlag::eARTICULATION_BODY0; + + if(hasArticulations & 2) + wuflags |= PxcNpWorkUnitFlag::eARTICULATION_BODY1; + + if(hasDynamics & 1) + wuflags |= PxcNpWorkUnitFlag::eDYNAMIC_BODY0; + + if(hasDynamics & 2) + wuflags |= PxcNpWorkUnitFlag::eDYNAMIC_BODY1; + + if(!disableResponse && !contactChangeable) + wuflags |= PxcNpWorkUnitFlag::eOUTPUT_CONSTRAINTS; + + if(!disableDiscreteContact) + wuflags |= PxcNpWorkUnitFlag::eDETECT_DISCRETE_CONTACT; + if(kinematicActor) + wuflags |= PxcNpWorkUnitFlag::eHAS_KINEMATIC_ACTOR; + + if(disableResponse) + wuflags |= PxcNpWorkUnitFlag::eDISABLE_RESPONSE; + if(!disableCCDContact) + wuflags |= PxcNpWorkUnitFlag::eDETECT_CCD_CONTACTS; + + // this is just the user req: contact reports can also be generated by body thresholding + if(reportContactInfo || contactChangeable) + wuflags |= PxcNpWorkUnitFlag::eOUTPUT_CONTACTS; + + if(hasForceThreshold) + wuflags |= PxcNpWorkUnitFlag::eFORCE_THRESHOLD; + + if(contactChangeable) + wuflags |= PxcNpWorkUnitFlag::eMODIFIABLE_CONTACT; + + mNpUnit.flags = wuflags; + + manager->mFlags = PxU32(contactChangeable ? PxsContactManager::PXS_CM_CHANGEABLE : 0) | PxU32(disableCCDContact ? 0 : PxsContactManager::PXS_CM_CCD_LINEAR); + + //manager->mUserData = this; + + mNpUnit.mNpIndex = 0xFFffFFff; + + mManager = manager; + + PxU8 statusFlags = 0; + + if(touching > 0) + { + statusFlags |= PxcNpWorkUnitStatusFlag::eHAS_TOUCH; + } + else if (touching < 0) + { + statusFlags |= PxcNpWorkUnitStatusFlag::eHAS_NO_TOUCH; + } + + mNpUnit.statusFlags = statusFlags; + + //KS - do not register the CMs here if contactManager isn't null. This implies this is a newly-found pair so we'll do that addition outside in parallel + + if(contactManager == NULL) + { + scene.getSimpleIslandManager()->setEdgeRigidCM(mEdgeIndex, mManager); + PxvNphaseImplementationContext* nphaseImplementationContext = scene.getLowLevelContext()->getNphaseImplementationContext(); + PX_ASSERT(nphaseImplementationContext); + nphaseImplementationContext->registerContactManager(mManager, touching, 0); + } +} + +void Sc::ShapeInteraction::onShapeChangeWhileSleeping(bool shapeOfDynamicChanged) +{ + // + // if an operation that can break touch status occurs, all deactivated pairs need to set the sleep island edge + // to connected to make sure that potentially joined islands get detected once parts of the island wake up. + // Active interactions can be ignored because the edges of those will be marked connected on deactivation. + // + if(!mManager) + { + Scene& scene = getScene(); + + BodySim* body0 = getShape0().getBodySim(); + PX_ASSERT(body0); // the first shape always belongs to a dynamic body + + if(shapeOfDynamicChanged && !readFlag(TOUCH_KNOWN)) + { + // conservative approach: if a pair was added asleep, and a body/shape gets moved, we want to check next frame + // whether the other body should get woken up. The motivation behind this is to get a similar behavior as in + // the case where the objects fell asleep rather than have been added asleep (in that case the object will be + // woken up with one frame delay). + + BodySim* body1 = getShape1().getBodySim(); + + if(body1 && !readFlag(ShapeInteraction::CONTACTS_RESPONSE_DISABLED)) // the first shape always belongs to a dynamic body, hence no need to test body0 + scene.addToLostTouchList(body0, body1); // note: this will cause duplicate entries if the pair loses AABB overlap the next frame + } + else if((!shapeOfDynamicChanged) && !body0->isActive()) + { + // if the shape of a static rigid body moves or is scaled, then this can break touch status and a second island gen + // pass will be needed. However, statics do not have island nodes that can be marked accordinlgy and instead we mark + // the dynamics of all pairs if they are asleep (if they are awake, they will get marked when the user puts them to sleep). + + /*PX_ASSERT(!getShape1().getBodySim()); + body0->notifySecondIslandGenPassNeeded();*/ + } + } +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h new file mode 100644 index 000000000..9e18b98a9 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h @@ -0,0 +1,412 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_SHAPE_INTERACTION +#define PX_COLLISION_SHAPE_INTERACTION + +#include "ScElementSimInteraction.h" +#include "ScShapeSim.h" +#include "ScActorPair.h" +#include "ScScene.h" +#include "ScBodySim.h" +#include "PxsContactManager.h" +#include "PxsContext.h" +#include "PxsSimpleIslandManager.h" + +#define INVALID_REPORT_PAIR_ID 0xffffffff + +namespace physx +{ +class PxsContactManagerOutputIterator; +namespace Sc +{ + class ContactReportAllocationManager; + /* + Description: A ShapeInteraction represents a pair of objects which _may_ have contacts. Created by the broadphase + and processed by the NPhaseCore. + */ + class ShapeInteraction : public ElementSimInteraction + { + friend class NPhaseCore; + ShapeInteraction& operator=(const ShapeInteraction&); + public: + enum SiFlag + { + PAIR_FLAGS_MASK = (PxPairFlag::eNEXT_FREE - 1), // Bits where the PxPairFlags get stored + NEXT_FREE = ((PAIR_FLAGS_MASK << 1) & ~PAIR_FLAGS_MASK), + + HAS_TOUCH = (NEXT_FREE << 0), // Tracks the last know touch state + HAS_NO_TOUCH = (NEXT_FREE << 1), // Tracks the last know touch state + TOUCH_KNOWN = (HAS_TOUCH | HAS_NO_TOUCH), // If none of these flags is set, the touch state is not known (for example, this is true for pairs that never ran narrowphase + + CONTACTS_COLLECT_POINTS = (NEXT_FREE << 2), // The user wants to get the contact points (includes debug rendering) + CONTACTS_RESPONSE_DISABLED = (NEXT_FREE << 3), // Collision response disabled (either by the user through PxPairFlag::eSOLVE_CONTACT or because the pair has two kinematics) + + CONTACT_FORCE_THRESHOLD_PAIRS = PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND) | PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS) | PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST), + CONTACT_REPORT_EVENTS = PxU32(PxPairFlag::eNOTIFY_TOUCH_FOUND) | PxU32(PxPairFlag::eNOTIFY_TOUCH_PERSISTS) | PxU32(PxPairFlag::eNOTIFY_TOUCH_LOST) | + PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND) | PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS) | PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST), + CONTACT_REPORT_EXTRA_DATA = PxU32(PxPairFlag::ePRE_SOLVER_VELOCITY) | PxU32(PxPairFlag::ePOST_SOLVER_VELOCITY) | PxU32(PxPairFlag::eCONTACT_EVENT_POSE), + + FORCE_THRESHOLD_EXCEEDED_NOW = (NEXT_FREE << 4), + FORCE_THRESHOLD_EXCEEDED_BEFORE = (NEXT_FREE << 5), + FORCE_THRESHOLD_EXCEEDED_FLAGS = FORCE_THRESHOLD_EXCEEDED_NOW | FORCE_THRESHOLD_EXCEEDED_BEFORE, + + IS_IN_PERSISTENT_EVENT_LIST = (NEXT_FREE << 6), // The pair is in the list of persistent contact events + WAS_IN_PERSISTENT_EVENT_LIST = (NEXT_FREE << 7), // The pair is inactive but used to be in the list of persistent contact events + IN_PERSISTENT_EVENT_LIST = IS_IN_PERSISTENT_EVENT_LIST | WAS_IN_PERSISTENT_EVENT_LIST, + IS_IN_FORCE_THRESHOLD_EVENT_LIST= (NEXT_FREE << 8), // The pair is in the list of force threshold contact events + IS_IN_CONTACT_EVENT_LIST = IS_IN_PERSISTENT_EVENT_LIST | IS_IN_FORCE_THRESHOLD_EVENT_LIST, + + LL_MANAGER_RECREATE_EVENT = CONTACT_REPORT_EVENTS | CONTACTS_COLLECT_POINTS | + CONTACTS_RESPONSE_DISABLED | PxU32(PxPairFlag::eMODIFY_CONTACTS) + }; + ShapeInteraction(ShapeSim& s1, ShapeSim& s2, PxPairFlags pairFlags, PxsContactManager* contactManager); + ~ShapeInteraction(); + + // Submits to contact stream + void processUserNotification(PxU32 contactEvent, PxU16 infoFlags, bool touchLost, const PxU32 ccdPass, const bool useCurrentTransform, + PxsContactManagerOutputIterator& outputs); // ccdPass is 0 for discrete collision and then 1,2,... for the CCD passes + + void processUserNotificationSync(); + + void processUserNotificationAsync(PxU32 contactEvent, PxU16 infoFlags, bool touchLost, const PxU32 ccdPass, const bool useCurrentTransform, + PxsContactManagerOutputIterator& outputs, ContactReportAllocationManager* alloc = NULL); // ccdPass is 0 for discrete collision and then 1,2,... for the CCD passes + + void visualize(Cm::RenderOutput&, PxsContactManagerOutputIterator&); + + PxU32 getContactPointData(const void*& contactPatches, const void*& contactPoints, PxU32& contactDataSize, PxU32& contactPointCount, PxU32& patchCount, const PxReal*& impulses, PxU32 startOffset, PxsContactManagerOutputIterator& outputs); + + bool managerLostTouch(const PxU32 ccdPass, bool adjustCounters, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + void managerNewTouch(const PxU32 ccdPass, bool adjustCounters, PxsContactManagerOutputIterator& outputs, bool useAdaptiveForce); + + PX_FORCE_INLINE void adjustCountersOnLostTouch(BodySim*, BodySim*, bool useAdaptiveForce); + PX_FORCE_INLINE void adjustCountersOnNewTouch(bool useAdaptiveForce); + + PX_FORCE_INLINE void sendCCDRetouch(const PxU32 ccdPass, PxsContactManagerOutputIterator& outputs); + void setContactReportPostSolverVelocity(ContactStreamManager& cs); + PX_FORCE_INLINE void sendLostTouchReport(bool shapeVolumeRemoved, const PxU32 ccdPass, PxsContactManagerOutputIterator& ouptuts); + void resetManagerCachedState() const; + + PX_FORCE_INLINE ActorPair* getActorPair() const { return mActorPair; } + PX_FORCE_INLINE void setActorPair(ActorPair& aPair) { mActorPair = &aPair; } + PX_FORCE_INLINE void clearActorPair() { mActorPair = NULL; } + PX_FORCE_INLINE ActorPairReport& getActorPairReport() const { return ActorPairReport::cast(*mActorPair); } + PX_INLINE Ps::IntBool isReportPair() const { /*PX_ASSERT(!(Ps::IntBool(getPairFlags() & CONTACT_REPORT_EVENTS)) || mActorPair->isReportPair());*/ return Ps::IntBool(getPairFlags() & CONTACT_REPORT_EVENTS); } + PX_INLINE Ps::IntBool hasTouch() const { return readFlag(HAS_TOUCH); } + PX_INLINE Ps::IntBool hasCCDTouch() const { PX_ASSERT(mManager); return mManager->getHadCCDContact(); } + PX_INLINE void swapAndClearForceThresholdExceeded(); + + PX_FORCE_INLINE void raiseFlag(SiFlag flag) { mFlags |= flag; } + PX_FORCE_INLINE Ps::IntBool readFlag(SiFlag flag) const { return Ps::IntBool(mFlags & flag); } + PX_FORCE_INLINE PxU32 getPairFlags() const; + + PX_FORCE_INLINE void removeFromReportPairList(); + + void onShapeChangeWhileSleeping(bool shapeOfDynamicChanged); + + PX_FORCE_INLINE Ps::IntBool hasKnownTouchState() const; + + bool onActivate_(void* data); + bool onDeactivate_(); + + void updateState(const PxU8 externalDirtyFlags); + + const PxsContactManager* getContactManager() const { return mManager; } + + void clearIslandGenData(); + + PX_FORCE_INLINE PxU32 getEdgeIndex() const { return mEdgeIndex; } + + PX_FORCE_INLINE Sc::ShapeSim& getShape0() const { return static_cast(getElement0()); } + PX_FORCE_INLINE Sc::ShapeSim& getShape1() const { return static_cast(getElement1()); } + + private: + PxU32 mContactReportStamp; + PxU32 mFlags; + ActorPair* mActorPair; + PxU32 mReportPairIndex; // Owned by NPhaseCore for its report pair list + + PxsContactManager* mManager; + + PxU32 mEdgeIndex; + + PxU16 mReportStreamIndex; // position of this pair in the contact report stream + + // Internal functions: + + void createManager(void* contactManager); + PX_INLINE void resetManager(); + PX_INLINE bool updateManager(void* contactManager); + PX_INLINE void destroyManager(); + PX_FORCE_INLINE bool activeManagerAllowed() const; + PX_FORCE_INLINE PxU32 getManagerContactState() const { return mFlags & LL_MANAGER_RECREATE_EVENT; } + + PX_FORCE_INLINE void clearFlag(SiFlag flag) { mFlags &= ~flag; } + PX_INLINE void setFlag(SiFlag flag, bool value) + { + if (value) + raiseFlag(flag); + else + clearFlag(flag); + } + PX_FORCE_INLINE void setHasTouch() { clearFlag(HAS_NO_TOUCH); raiseFlag(HAS_TOUCH); } + PX_FORCE_INLINE void setHasNoTouch() { clearFlag(HAS_TOUCH); raiseFlag(HAS_NO_TOUCH); } + + PX_FORCE_INLINE void setPairFlags(PxPairFlags flags); + + PX_FORCE_INLINE void processReportPairOnActivate(); + PX_FORCE_INLINE void processReportPairOnDeactivate(); + + // Certain SiFlag cache properties of the pair. If these properties change then the flags have to be updated. + // For example: is collision enabled for this pair? are contact points requested for this pair? + PX_FORCE_INLINE void updateFlags(const Sc::Scene&, const Sc::BodySim*, const Sc::BodySim*, const PxU32 pairFlags); + + friend class Sc::Scene; + }; + +} // namespace Sc + + +PX_FORCE_INLINE void Sc::ShapeInteraction::sendLostTouchReport(bool shapeVolumeRemoved, const PxU32 ccdPass, PxsContactManagerOutputIterator& outputs) +{ + PX_ASSERT(hasTouch()); + PX_ASSERT(isReportPair()); + + PxU32 thresholdForceLost = readFlag(ShapeInteraction::FORCE_THRESHOLD_EXCEEDED_NOW) ? PxU32(PxPairFlag::eNOTIFY_THRESHOLD_FORCE_LOST) : 0; // make sure to only send report if force is still above threshold + PxU32 triggeredFlags = getPairFlags() & (PxU32(PxPairFlag::eNOTIFY_TOUCH_LOST) | thresholdForceLost); + if (triggeredFlags) + { + PxU16 infoFlag = 0; + if (mActorPair->getTouchCount() == 1) // this code assumes that the actor pair touch count does get decremented afterwards + { + infoFlag |= PxContactPairFlag::eACTOR_PAIR_LOST_TOUCH; + } + + //Lost touch is processed after solver, so we should use the previous transform to update the pose for objects if user request eCONTACT_EVENT_POSE + processUserNotification(triggeredFlags, infoFlag, true, ccdPass, false, outputs); + } + + ActorPairReport& apr = getActorPairReport(); + if (apr.hasReportData() && !apr.streamResetNeeded(getScene().getTimeStamp())) + { + // If there has been no event recorded yet, there is no need to worry about events with shape pointers which might later reference + // removed shapes due to buffered removal, i.e., removal while the simulation was running. + // This is also correct for CCD scenarios where a touch could get lost and then found again etc. If in such a case there ever is a contact event + // recorded, there will always be another sendLostTouchReport() call at some later point (caused by the simulation or when the shape gets + // removed at fetchResults). + PxU16 flagsToRaise = ContactStreamManagerFlag::eHAS_PAIRS_THAT_LOST_TOUCH; + + ContactStreamManager& cs = apr.getContactStreamManager(); + + if (shapeVolumeRemoved) + { + flagsToRaise |= ContactStreamManagerFlag::eTEST_FOR_REMOVED_SHAPES; + + // if an actor gets deleted while the simulation is running and the actor has a pending contact report with post solver + // velocity extra data, then the post solver velocity needs to get written now because it is too late when the reports + // get fired (the object will have been deleted already) + + if (cs.getFlags() & ContactStreamManagerFlag::eNEEDS_POST_SOLVER_VELOCITY) + setContactReportPostSolverVelocity(cs); + } + + cs.raiseFlags(flagsToRaise); + } +} + + +PX_FORCE_INLINE void Sc::ShapeInteraction::setPairFlags(PxPairFlags flags) +{ + PX_ASSERT(PxU32(flags) < PxPairFlag::eNEXT_FREE); // to find out if a new PxPairFlag has been added after eLAST instead of in front + + PxU32 newFlags = mFlags; + PxU32 fl = PxU32(flags) & PAIR_FLAGS_MASK; + newFlags &= (~PAIR_FLAGS_MASK); // clear old flags + newFlags |= fl; + + mFlags = newFlags; +} + + +// PT: returning PxU32 instead of PxPairFlags to remove LHS. Please do not undo this. +PX_FORCE_INLINE PxU32 Sc::ShapeInteraction::getPairFlags() const +{ + return (mFlags & PAIR_FLAGS_MASK); +} + + +PX_INLINE void Sc::ShapeInteraction::swapAndClearForceThresholdExceeded() +{ + PxU32 flags = mFlags; + + PX_COMPILE_TIME_ASSERT(FORCE_THRESHOLD_EXCEEDED_NOW == (FORCE_THRESHOLD_EXCEEDED_BEFORE >> 1)); + + PxU32 nowToBefore = (flags & FORCE_THRESHOLD_EXCEEDED_NOW) << 1; + flags &= ~(FORCE_THRESHOLD_EXCEEDED_NOW | FORCE_THRESHOLD_EXCEEDED_BEFORE); + flags |= nowToBefore; + + mFlags = flags; +} + +PX_FORCE_INLINE void Sc::ShapeInteraction::removeFromReportPairList() +{ + // this method should only get called if the pair is in the list for + // persistent or force based contact reports + PX_ASSERT(mReportPairIndex != INVALID_REPORT_PAIR_ID); + PX_ASSERT(readFlag(IS_IN_CONTACT_EVENT_LIST)); + + Scene& scene = getScene(); + + if (readFlag(IS_IN_FORCE_THRESHOLD_EVENT_LIST)) + scene.getNPhaseCore()->removeFromForceThresholdContactEventPairs(this); + else + { + PX_ASSERT(readFlag(IS_IN_PERSISTENT_EVENT_LIST)); + scene.getNPhaseCore()->removeFromPersistentContactEventPairs(this); + } +} + +PX_INLINE bool Sc::ShapeInteraction::updateManager(void* contactManager) +{ + if (activeManagerAllowed()) + { + if (mManager == 0) + createManager(contactManager); + + return (mManager != NULL); // creation might fail (pool reached limit, mem allocation failed etc.) + } + else + return false; +} + +PX_INLINE void Sc::ShapeInteraction::destroyManager() +{ + PX_ASSERT(mManager); + + Scene& scene = getScene(); + + PxvNphaseImplementationContext* nphaseImplementationContext = scene.getLowLevelContext()->getNphaseImplementationContext(); + PX_ASSERT(nphaseImplementationContext); + nphaseImplementationContext->unregisterContactManager(mManager); + + /*if (mEdgeIndex != IG_INVALID_EDGE) + scene.getSimpleIslandManager()->clearEdgeRigidCM(mEdgeIndex);*/ + scene.getLowLevelContext()->destroyContactManager(mManager); + mManager = 0; +} + + +PX_FORCE_INLINE bool Sc::ShapeInteraction::activeManagerAllowed() const +{ + PX_ASSERT(getShape0().getActor().isDynamicRigid() || getShape1().getActor().isDynamicRigid()); + + const BodySim* bodySim0 = getShape0().getBodySim(); + const BodySim* bodySim1 = getShape1().getBodySim(); + PX_ASSERT(bodySim0); // the first shape always belongs to a dynamic body + + const IG::IslandSim& islandSim = getScene().getSimpleIslandManager()->getSpeculativeIslandSim(); + + //if((bodySim0->isActive()) || (bodySim1 && bodySim1->isActive())) + //check whether active in the speculative sim! + if (islandSim.getNode(bodySim0->getNodeIndex()).isActive() || + (bodySim1 && islandSim.getNode(bodySim1->getNodeIndex()).isActive())) + { + return true; + } + else + { + //Sleeping kinematic 0 vs sleeping kinematic 1 + return false; + } +} + + +PX_FORCE_INLINE void Sc::ShapeInteraction::sendCCDRetouch(const PxU32 ccdPass, PxsContactManagerOutputIterator& outputs) +{ + PxU32 pairFlags = getPairFlags(); + if (pairFlags & PxPairFlag::eNOTIFY_TOUCH_CCD) + { + processUserNotification(PxPairFlag::eNOTIFY_TOUCH_CCD, 0, false, ccdPass, false, outputs); + } +} + + +PX_FORCE_INLINE void Sc::ShapeInteraction::adjustCountersOnLostTouch(BodySim* body0, BodySim* body1, bool useAdaptiveForce) +{ + PX_ASSERT(body0); // the first shape always belongs to a dynamic body + + PX_ASSERT(mActorPair->getTouchCount()); + + mActorPair->decTouchCount(); + + if (useAdaptiveForce || mActorPair->getTouchCount() == 0) + { + body0->decrementBodyConstraintCounter(); + if (body1) + body1->decrementBodyConstraintCounter(); + } +} + + +PX_FORCE_INLINE void Sc::ShapeInteraction::adjustCountersOnNewTouch(bool useAdaptiveForce) +{ + BodySim* body0 = getShape0().getBodySim(); + BodySim* body1 = getShape1().getBodySim(); + PX_ASSERT(body0); // the first shape always belongs to a dynamic body + + mActorPair->incTouchCount(); + //If using adaptive force, always record a body constraint, otherwise only record if this is the first constraint + //with this pair of bodies (doubling up usage of this counter for both adaptive force and stabilization) + if (useAdaptiveForce || mActorPair->getTouchCount() == 1) + { + body0->incrementBodyConstraintCounter(); + if (body1) + body1->incrementBodyConstraintCounter(); + } +} + + +PX_FORCE_INLINE Ps::IntBool Sc::ShapeInteraction::hasKnownTouchState() const +{ + // For a pair where the bodies were added asleep, the touch state is not known until narrowphase runs on the pair for the first time. + // If such a pair looses AABB overlap before, the conservative approach is to wake the bodies up. This method provides an indicator that + // this is such a pair. Note: this might also wake up objects that do not touch but that's the price to pay (unless we want to run + // overlap tests on such pairs). + + if (mManager) + return mManager->touchStatusKnown(); + else + return readFlag(TOUCH_KNOWN); +} + + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp new file mode 100644 index 000000000..7752c395d --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp @@ -0,0 +1,494 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScBodySim.h" +#include "ScStaticSim.h" +#include "ScScene.h" +#include "ScElementSimInteraction.h" +#include "ScShapeInteraction.h" +#include "ScTriggerInteraction.h" +#include "ScSimStats.h" +#include "ScObjectIDTracker.h" +#include "GuHeightFieldUtil.h" +#include "GuTriangleMesh.h" +#include "GuConvexMeshData.h" +#include "GuHeightField.h" +#include "PxsContext.h" +#include "PxsTransformCache.h" +#include "CmTransformUtils.h" +#include "GuBounds.h" +#include "PxsRigidBody.h" +#include "ScSqBoundsManager.h" +#include "PxsSimulationController.h" +#include "common/PxProfileZone.h" + +using namespace physx; +using namespace Gu; +using namespace Sc; + +// PT: keep local functions in cpp, no need to pollute the header. Don't force conversions to bool if not necessary. +static PX_FORCE_INLINE PxU32 hasTriggerFlags(PxShapeFlags flags) { return PxU32(flags) & PxU32(PxShapeFlag::eTRIGGER_SHAPE); } +static PX_FORCE_INLINE PxU32 isBroadPhase(PxShapeFlags flags) { return PxU32(flags) & PxU32(PxShapeFlag::eTRIGGER_SHAPE|PxShapeFlag::eSIMULATION_SHAPE); } + +static PX_FORCE_INLINE void resetElementID(Scene& scene, ShapeSim& shapeSim) +{ + PX_ASSERT(!shapeSim.isInBroadPhase()); + + scene.getDirtyShapeSimMap().reset(shapeSim.getElementID()); + + if(shapeSim.getSqBoundsId() != PX_INVALID_U32) + shapeSim.destroySqBounds(); +} + +void ShapeSim::initSubsystemsDependingOnElementID() +{ + Scene& scScene = getScene(); + + Bp::BoundsArray& boundsArray = scScene.getBoundsArray(); + const PxU32 index = getElementID(); + + PX_ALIGN(16, PxTransform absPos); + getAbsPoseAligned(&absPos); + + PxsTransformCache& cache = scScene.getLowLevelContext()->getTransformCache(); + cache.initEntry(index); + cache.setTransformCache(absPos, 0, index); + + boundsArray.updateBounds(absPos, mCore.getGeometryUnion(), index); + + { + PX_PROFILE_ZONE("API.simAddShapeToBroadPhase", scScene.getContextId()); + if(isBroadPhase(mCore.getFlags())) + internalAddToBroadPhase(); + else + scScene.getAABBManager()->reserveSpaceForBounds(index); + scScene.updateContactDistance(index, getContactOffset()); + } + + if(scScene.getDirtyShapeSimMap().size() <= index) + scScene.getDirtyShapeSimMap().resize(PxMax(index+1, (scScene.getDirtyShapeSimMap().size()+1) * 2u)); + + RigidSim& owner = getRbSim(); + if(owner.isDynamicRigid() && static_cast(owner).isActive()) + createSqBounds(); + + // Init LL shape + { + mLLShape.mElementIndex = index; + mLLShape.mShapeCore = const_cast(&mCore.getCore()); + + if(owner.getActorType()==PxActorType::eRIGID_STATIC) + { + mLLShape.mBodySimIndex = IG::NodeIndex(IG_INVALID_NODE); + } + else + { + BodySim& bodySim = static_cast(getActor()); + mLLShape.mBodySimIndex = bodySim.getNodeIndex(); + //mLLShape.mLocalBound = computeBounds(mCore.getGeometry(), PxTransform(PxIdentity)); + } + } +} + +ShapeSim::ShapeSim(RigidSim& owner, const ShapeCore& core) : + ElementSim (owner), + mCore (core), + mSqBoundsId (PX_INVALID_U32) +{ + // sizeof(ShapeSim) = 32 bytes + Scene& scScene = getScene(); + + mId = scScene.getShapeIDTracker().createID(); + + initSubsystemsDependingOnElementID(); +} + +ShapeSim::~ShapeSim() +{ + Scene& scScene = getScene(); + resetElementID(scScene, *this); + scScene.getShapeIDTracker().releaseID(mId); +} + +Bp::FilterGroup::Enum ShapeSim::getBPGroup() const +{ + bool isKinematic = false; + BodySim* bs = getBodySim(); + if(bs) + isKinematic = bs->isKinematic(); + + RigidSim& rbSim = getRbSim(); + return Bp::getFilterGroup(rbSim.getActorType()==PxActorType::eRIGID_STATIC, rbSim.getRigidID(), isKinematic); +} + +PX_FORCE_INLINE void ShapeSim::internalAddToBroadPhase() +{ + PX_ASSERT(!isInBroadPhase()); + + addToAABBMgr(mCore.getContactOffset(), getBPGroup(), Ps::IntBool(mCore.getCore().mShapeFlags & PxShapeFlag::eTRIGGER_SHAPE)); +} + +PX_FORCE_INLINE void ShapeSim::internalRemoveFromBroadPhase(bool wakeOnLostTouch) +{ + PX_ASSERT(isInBroadPhase()); + removeFromAABBMgr(); + + Scene& scene = getScene(); + PxsContactManagerOutputIterator outputs = scene.getLowLevelContext()->getNphaseImplementationContext()->getContactManagerOutputs(); + scene.getNPhaseCore()->onVolumeRemoved(this, wakeOnLostTouch ? PxU32(PairReleaseFlag::eWAKE_ON_LOST_TOUCH) : 0, outputs, scene.getPublicFlags() & PxSceneFlag::eADAPTIVE_FORCE); +} + +void ShapeSim::removeFromBroadPhase(bool wakeOnLostTouch) +{ + if(isInBroadPhase()) + internalRemoveFromBroadPhase(wakeOnLostTouch); +} + +void ShapeSim::reinsertBroadPhase() +{ + if(isInBroadPhase()) + internalRemoveFromBroadPhase(); +// internalAddToBroadPhase(); + + Scene& scene = getScene(); + + // Scene::removeShape + { + //unregisterShapeFromNphase(shape.getCore()); + + // PT: "getID" is const but the addShape call used LLShape, which uses elementID, so.... + scene.getSimulationController()->removeShape(getID()); + } + + // Call ShapeSim dtor + { + resetElementID(scene, *this); + } + + // Call ElementSim dtor + { + releaseID(); + } + + // Call ElementSim ctor + { + initID(); + } + + // Call ShapeSim ctor + { + initSubsystemsDependingOnElementID(); + } + + // Scene::addShape + { + scene.getSimulationController()->addShape(&getLLShapeSim(), getID()); + // PT: TODO: anything else needed here? + //registerShapeInNphase(shapeCore); + } +} + +void ShapeSim::onFilterDataChange() +{ + setElementInteractionsDirty(InteractionDirtyFlag::eFILTER_STATE, InteractionFlag::eFILTERABLE); +} + +void ShapeSim::onResetFiltering() +{ + if(isInBroadPhase()) + reinsertBroadPhase(); +} + +void ShapeSim::onMaterialChange() +{ + setElementInteractionsDirty(InteractionDirtyFlag::eMATERIAL, InteractionFlag::eRB_ELEMENT); +} + +void ShapeSim::onRestOffsetChange() +{ + setElementInteractionsDirty(InteractionDirtyFlag::eREST_OFFSET, InteractionFlag::eRB_ELEMENT); +} + +void ShapeSim::onContactOffsetChange() +{ + if(isInBroadPhase()) + getScene().getAABBManager()->setContactOffset(getElementID(), mCore.getContactOffset()); +} + +void ShapeSim::onFlagChange(PxShapeFlags oldFlags) +{ + const PxShapeFlags newFlags = mCore.getFlags(); + + const bool oldBp = isBroadPhase(oldFlags)!=0; + const bool newBp = isBroadPhase(newFlags)!=0; + + // Change of collision shape flags requires removal/add to broadphase + if(oldBp != newBp) + { + if(!oldBp && newBp) + { + // A.B. if a trigger was removed and inserted within the same frame we need to reinsert + if(hasTriggerFlags(newFlags) && getScene().getAABBManager()->isMarkedForRemove(getElementID())) + { + reinsertBroadPhase(); + } + else + { + internalAddToBroadPhase(); + } + } + else + internalRemoveFromBroadPhase(); + } + else + { + const bool wasTrigger = hasTriggerFlags(oldFlags)!=0; + const bool isTrigger = hasTriggerFlags(newFlags)!=0; + if(wasTrigger != isTrigger) + reinsertBroadPhase(); // re-insertion is necessary because trigger pairs get killed + } + + const PxShapeFlags hadSq = oldFlags & PxShapeFlag::eSCENE_QUERY_SHAPE; + const PxShapeFlags hasSq = newFlags & PxShapeFlag::eSCENE_QUERY_SHAPE; + if(hasSq && !hadSq) + { + BodySim* body = getBodySim(); + if(body && body->isActive()) + createSqBounds(); + } + else if(hadSq && !hasSq) + destroySqBounds(); +} + +void ShapeSim::getAbsPoseAligned(PxTransform* PX_RESTRICT globalPose) const +{ + const PxTransform& shape2Actor = mCore.getCore().transform; + const PxTransform* actor2World = NULL; + if(getActor().getActorType()==PxActorType::eRIGID_STATIC) + { + PxsRigidCore& core = static_cast(getActor()).getStaticCore().getCore(); + actor2World = &core.body2World; + } + else + { + PxsBodyCore& core = static_cast(getActor()).getBodyCore().getCore(); + if(!core.mIdtBody2Actor) + { + Cm::getDynamicGlobalPoseAligned(core.body2World, shape2Actor, core.getBody2Actor(), *globalPose); + return; + } + actor2World = &core.body2World; + } + Cm::getStaticGlobalPoseAligned(*actor2World, shape2Actor, *globalPose); +} + +BodySim* ShapeSim::getBodySim() const +{ + ActorSim& a = getActor(); + return a.isDynamicRigid() ? static_cast(&a) : NULL; +} + +PxsRigidCore& ShapeSim::getPxsRigidCore() const +{ + ActorSim& a = getActor(); + return a.isDynamicRigid() ? static_cast(a).getBodyCore().getCore() + : static_cast(a).getStaticCore().getCore(); +} + +void ShapeSim::updateCached(PxU32 transformCacheFlags, Cm::BitMapPinned* shapeChangedMap) +{ + PX_ALIGN(16, PxTransform absPose); + getAbsPoseAligned(&absPose); + + Scene& scene = getScene(); + const PxU32 index = getElementID(); + + scene.getLowLevelContext()->getTransformCache().setTransformCache(absPose, transformCacheFlags, index); + scene.getBoundsArray().updateBounds(absPose, mCore.getGeometryUnion(), index); + if (shapeChangedMap && isInBroadPhase()) + shapeChangedMap->growAndSet(index); +} + +void ShapeSim::updateCached(PxsTransformCache& transformCache, Bp::BoundsArray& boundsArray) +{ + const PxU32 index = getElementID(); + + PxsCachedTransform& ct = transformCache.getTransformCache(index); + Ps::prefetchLine(&ct); + + getAbsPoseAligned(&ct.transform); + + ct.flags = 0; + + PxBounds3& b = boundsArray.begin()[index]; + Gu::computeBounds(b, mCore.getGeometryUnion().getGeometry(), ct.transform, 0.0f, NULL, 1.0f); +} + +void ShapeSim::updateContactDistance(PxReal* contactDistance, const PxReal inflation, const PxVec3 angVel, const PxReal dt, Bp::BoundsArray& boundsArray) +{ + const PxU32 index = getElementID(); + + const PxBounds3& bounds = boundsArray.getBounds(index); + + PxReal radius = bounds.getExtents().magnitude(); + + //Heuristic for angular velocity... + PxReal angularInflation = angVel.magnitude() * dt * radius; + + contactDistance[index] = getContactOffset() + inflation + angularInflation; +} + +Ps::IntBool ShapeSim::updateSweptBounds() +{ + Vec3p endOrigin, endExtent; + const ShapeCore& shapeCore = mCore; + const PxTransform& endPose = getScene().getLowLevelContext()->getTransformCache().getTransformCache(getElementID()).transform; + PxReal ccdThreshold = computeBoundsWithCCDThreshold(endOrigin, endExtent, shapeCore.getGeometry(), endPose, NULL); + + PxBounds3 bounds = PxBounds3::centerExtents(endOrigin, endExtent); + + BodySim* body = getBodySim(); + PxcRigidBody& rigidBody = body->getLowLevelBody(); + PxsBodyCore& bodyCore = body->getBodyCore().getCore(); + PX_ALIGN(16, PxTransform shape2World); + Cm::getDynamicGlobalPoseAligned(rigidBody.mLastTransform, shapeCore.getShape2Actor(), bodyCore.getBody2Actor(), shape2World); + PxBounds3 startBounds = computeBounds(shapeCore.getGeometry(), shape2World); + + const Ps::IntBool isFastMoving = (startBounds.getCenter() - endOrigin).magnitudeSquared() >= ccdThreshold * ccdThreshold ? 1 : 0; + + if (isFastMoving) + bounds.include(startBounds); + + PX_ASSERT(bounds.minimum.x <= bounds.maximum.x + && bounds.minimum.y <= bounds.maximum.y + && bounds.minimum.z <= bounds.maximum.z); + + getScene().getBoundsArray().setBounds(bounds, getElementID()); + + return isFastMoving; +} + +void ShapeSim::updateBPGroup() +{ + if(isInBroadPhase()) + { + Sc::Scene& scene = getScene(); + scene.getAABBManager()->setBPGroup(getElementID(), getBPGroup()); + + reinsertBroadPhase(); +// internalRemoveFromBroadPhase(); +// internalAddToBroadPhase(); + } +} + +void ShapeSim::markBoundsForUpdate(bool forceBoundsUpdate) +{ + Scene& scene = getScene(); + if(forceBoundsUpdate) + updateCached(0, &scene.getAABBManager()->getChangedAABBMgActorHandleMap()); + else if(isInBroadPhase()) + scene.getDirtyShapeSimMap().growAndSet(getElementID()); +} + +static PX_FORCE_INLINE void updateInteraction(Scene& scene, Interaction* i, const bool isDynamic, const bool isAsleep) +{ + if(i->getType() == InteractionType::eOVERLAP) + { + ShapeInteraction* si = static_cast(i); + si->resetManagerCachedState(); + + if (isAsleep) + si->onShapeChangeWhileSleeping(isDynamic); + } + else if(i->getType() == InteractionType::eTRIGGER) + (static_cast(i))->forceProcessingThisFrame(scene); // trigger pairs need to be checked next frame +} + +void ShapeSim::onVolumeOrTransformChange(bool forceBoundsUpdate) +{ + Scene& scene = getScene(); + BodySim* body = getBodySim(); + const bool isDynamic = (body != NULL); + const bool isAsleep = body ? !body->isActive() : true; + + ElementSim::ElementInteractionIterator iter = getElemInteractions(); + ElementSimInteraction* i = iter.getNext(); + while(i) + { + updateInteraction(scene, i, isDynamic, isAsleep); + i = iter.getNext(); + } + + markBoundsForUpdate(forceBoundsUpdate); +} + +void notifyActorInteractionsOfTransformChange(ActorSim& actor) +{ + bool isDynamic; + bool isAsleep; + if(actor.isDynamicRigid()) + { + isDynamic = true; + isAsleep = !static_cast(actor).isActive(); + } + else + { + isDynamic = false; + isAsleep = true; + } + + Scene& scene = actor.getScene(); + + PxU32 nbInteractions = actor.getActorInteractionCount(); + Interaction** interactions = actor.getActorInteractions(); + while(nbInteractions--) + updateInteraction(scene, *interactions++, isDynamic, isAsleep); +} + +void ShapeSim::createSqBounds() +{ + if(mSqBoundsId!=PX_INVALID_U32) + return; + + BodySim* bodySim = getBodySim(); + PX_ASSERT(bodySim); + + if(bodySim->usingSqKinematicTarget() || bodySim->isFrozen() || !bodySim->isActive() || bodySim->readInternalFlag(BodySim::BF_IS_COMPOUND_RIGID)) + return; + + if(mCore.getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE) + getScene().getSqBoundsManager().addShape(*this); +} + +void ShapeSim::destroySqBounds() +{ + if(mSqBoundsId!=PX_INVALID_U32) + getScene().getSqBoundsManager().removeShape(*this); +} + diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h new file mode 100644 index 000000000..eadc1a2da --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h @@ -0,0 +1,151 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_SHAPESIM +#define PX_PHYSICS_SCP_SHAPESIM + +#include "ScElementSim.h" +#include "ScShapeCore.h" +#include "CmPtrTable.h" +#include "ScRigidSim.h" +#include "PxsShapeSim.h" + +namespace physx +{ + + class PxsTransformCache; +namespace Gu +{ + class TriangleMesh; + class HeightField; +} + +/** Simulation object corresponding to a shape core object. This object is created when + a ShapeCore object is added to the simulation, and destroyed when it is removed +*/ + +struct PxsRigidCore; + +namespace Sc +{ + + class RigidSim; + class ShapeCore; + class Scene; + class BodySim; + class StaticSim; + + class ShapeSim : public ElementSim + { + ShapeSim &operator=(const ShapeSim &); + public: + + // passing in a pointer for the shape to output its bounds allows us not to have to compute them twice. + // A neater way to do this would be to ask the AABB Manager for the bounds after the shape has been + // constructed, but there is currently no spec for what the AABBMgr is allowed to do with the bounds, + // hence better not to assume anything. + + ShapeSim(RigidSim&, const ShapeCore& core); + ~ShapeSim(); + + void reinsertBroadPhase(); + + PX_FORCE_INLINE const ShapeCore& getCore() const { return mCore; } + + // TODO: compile time coupling + + PX_INLINE PxGeometryType::Enum getGeometryType() const { return mCore.getGeometryType(); } + + // This is just for getting a reference for the user, so we cast away const-ness + + PX_INLINE PxShape* getPxShape() const { return const_cast(mCore.getPxShape()); } + + PX_FORCE_INLINE PxReal getRestOffset() const { return mCore.getRestOffset(); } + PX_FORCE_INLINE PxReal getTorsionalPatchRadius() const { return mCore.getTorsionalPatchRadius(); } + PX_FORCE_INLINE PxReal getMinTorsionalPatchRadius() const { return mCore.getMinTorsionalPatchRadius(); } + PX_FORCE_INLINE PxU32 getFlags() const { return mCore.getFlags(); } + PX_FORCE_INLINE PxReal getContactOffset() const { return mCore.getContactOffset(); } + + PX_FORCE_INLINE RigidSim& getRbSim() const { return static_cast(getActor()); } + BodySim* getBodySim() const; + + PxsRigidCore& getPxsRigidCore() const; + + void onFilterDataChange(); + void onRestOffsetChange(); + void onFlagChange(PxShapeFlags oldFlags); + void onResetFiltering(); + void onVolumeOrTransformChange(bool forceBoundsUpdate); + void onMaterialChange(); // remove when material properties are gone from PxcNpWorkUnit + void onContactOffsetChange(); + void markBoundsForUpdate(bool forceBoundsUpdate); + + void getAbsPoseAligned(PxTransform* PX_RESTRICT globalPose) const; + + PX_FORCE_INLINE PxU32 getID() const { return mId; } + PX_FORCE_INLINE PxU32 getTransformCacheID() const { return getElementID(); } + + void removeFromBroadPhase(bool wakeOnLostTouch); + + void createSqBounds(); + void destroySqBounds(); + + PX_FORCE_INLINE PxU32 getSqBoundsId() const { return mSqBoundsId; } + PX_FORCE_INLINE void setSqBoundsId(PxU32 id) { mSqBoundsId = id; } + + void updateCached(PxU32 transformCacheFlags, Cm::BitMapPinned* shapeChangedMap); + void updateCached(PxsTransformCache& transformCache, Bp::BoundsArray& boundsArray); + void updateContactDistance(PxReal* contactDistance, const PxReal inflation, const PxVec3 angVel, const PxReal dt, Bp::BoundsArray& boundsArray); + Ps::IntBool updateSweptBounds(); + void updateBPGroup(); + + PX_FORCE_INLINE PxsShapeSim& getLLShapeSim() { return mLLShape; } + private: + PxsShapeSim mLLShape; + const ShapeCore& mCore; + PxU32 mId; + PxU32 mSqBoundsId; + + PX_FORCE_INLINE void internalAddToBroadPhase(); + PX_FORCE_INLINE void internalRemoveFromBroadPhase(bool wakeOnLostTouch=true); + void initSubsystemsDependingOnElementID(); + Bp::FilterGroup::Enum getBPGroup() const; + }; + +#if !PX_P64_FAMILY +// PX_COMPILE_TIME_ASSERT(32==sizeof(Sc::ShapeSim)); // after removing bounds from shapes +// PX_COMPILE_TIME_ASSERT((sizeof(Sc::ShapeSim) % 16) == 0); // aligned mem bounds are better for prefetching +#endif + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h b/src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h new file mode 100644 index 000000000..97be8b629 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h @@ -0,0 +1,139 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_SIMSTATEDATA +#define PX_SIMSTATEDATA + +#include "foundation/PxMemory.h" +#include "ScBodyCore.h" + +namespace physx +{ +namespace Sc +{ + struct Kinematic : public KinematicTransform + { + // The following members buffer the original body data to restore them when switching back to dynamic body + // (for kinematics the corresponding LowLevel properties are set to predefined values) + PxVec3 backupInverseInertia; // The inverse of the body space inertia tensor + PxReal backupInvMass; // The inverse of the body mass + PxReal backupLinearDamping; // The velocity is scaled by (1.0f - this * dt) inside integrateVelocity() every substep. + PxReal backupAngularDamping; + PxReal backupMaxAngVelSq; // The angular velocity's magnitude is clamped to this maximum value. + PxReal backupMaxLinVelSq; // The angular velocity's magnitude is clamped to this maximum value + }; + PX_COMPILE_TIME_ASSERT(0 == (sizeof(Kinematic) & 0x0f)); + + // Important: Struct is reset in setForcesToDefaults. + + enum VelocityModFlags + { + VMF_GRAVITY_DIRTY = (1 << 0), + VMF_ACC_DIRTY = (1 << 1), + VMF_VEL_DIRTY = (1 << 2) + }; + + struct VelocityMod + { + PxVec3 linearPerSec; // A request to change the linear velocity by this much each second. The velocity is changed by this * dt inside integrateVelocity(). + PxU8 flags; + PxU8 pad0[3]; + PxVec3 angularPerSec; + PxU8 pad1[3]; + PxU8 type; + PxVec3 linearPerStep; // A request to change the linear velocity by this much the next step. The velocity is changed inside updateForces(). + PxU32 pad2; + PxVec3 angularPerStep; + PxU32 pad3; + + PX_FORCE_INLINE void clear() { linearPerSec = angularPerSec = linearPerStep = angularPerStep = PxVec3(0.0f); } + + PX_FORCE_INLINE void clearPerStep() { linearPerStep = angularPerStep = PxVec3(0.0f); } + + PX_FORCE_INLINE const PxVec3& getLinearVelModPerSec() const { return linearPerSec; } + PX_FORCE_INLINE void accumulateLinearVelModPerSec(const PxVec3& v) { linearPerSec += v; } + PX_FORCE_INLINE void setLinearVelModPerSec(const PxVec3& v) { linearPerSec = v; } + PX_FORCE_INLINE void clearLinearVelModPerSec() { linearPerSec = PxVec3(0.0f); } + + PX_FORCE_INLINE const PxVec3& getLinearVelModPerStep() const { return linearPerStep; } + PX_FORCE_INLINE void accumulateLinearVelModPerStep(const PxVec3& v) { linearPerStep += v; } + PX_FORCE_INLINE void clearLinearVelModPerStep() { linearPerStep = PxVec3(0.0f); } + + PX_FORCE_INLINE const PxVec3& getAngularVelModPerSec() const { return angularPerSec; } + PX_FORCE_INLINE void accumulateAngularVelModPerSec(const PxVec3& v) { angularPerSec += v; } + PX_FORCE_INLINE void setAngularVelModPerSec(const PxVec3& v) { angularPerSec = v; } + PX_FORCE_INLINE void clearAngularVelModPerSec() { angularPerSec = PxVec3(0.0f); } + + PX_FORCE_INLINE const PxVec3& getAngularVelModPerStep() const { return angularPerStep; } + PX_FORCE_INLINE void accumulateAngularVelModPerStep(const PxVec3& v) { angularPerStep += v; } + PX_FORCE_INLINE void clearAngularVelModPerStep() { angularPerStep = PxVec3(0.0f); } + + PX_FORCE_INLINE void notifyAddAcceleration() { flags |= VMF_ACC_DIRTY; } + PX_FORCE_INLINE void notifyClearAcceleration() { flags |= VMF_ACC_DIRTY; } + PX_FORCE_INLINE void notifyAddVelocity() { flags |= VMF_VEL_DIRTY; } + PX_FORCE_INLINE void notifyClearVelocity() { flags |= VMF_VEL_DIRTY; } + }; + PX_COMPILE_TIME_ASSERT(sizeof(VelocityMod) == sizeof(Kinematic)); + + + // Structure to store data for kinematics (target pose etc.) + // note: we do not delete this object for kinematics even if no target is set. + struct SimStateData : public Ps::UserAllocated // TODO: may want to optimize the allocation of this further. + { + PxU8 data[sizeof(Kinematic)]; + + enum Enum + { + eVelMod=0, + eKine + }; + + SimStateData(){} + SimStateData(const PxU8 type) + { + PxMemZero(data, sizeof(Kinematic)); + Kinematic* kine = reinterpret_cast(data); + kine->type = type; + } + + PX_FORCE_INLINE PxU32 getType() const { const Kinematic* kine = reinterpret_cast(data); return kine->type;} + PX_FORCE_INLINE bool isKine() const {return eKine == getType();} + PX_FORCE_INLINE bool isVelMod() const {return eVelMod == getType();} + + Kinematic* getKinematicData() { Kinematic* kine = reinterpret_cast(data); PX_ASSERT(eKine == kine->type); return kine;} + VelocityMod* getVelocityModData() { VelocityMod* velmod = reinterpret_cast(data); PX_ASSERT(eVelMod == velmod->type); return velmod;} + const Kinematic* getKinematicData() const { const Kinematic* kine = reinterpret_cast(data); PX_ASSERT(eKine == kine->type); return kine;} + const VelocityMod* getVelocityModData() const { const VelocityMod* velmod = reinterpret_cast(data); PX_ASSERT(eVelMod == velmod->type); return velmod;} + }; + +} // namespace Sc + +} // namespace physx + +#endif //PX_SIMSTATEDATA diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp new file mode 100644 index 000000000..b246dfbbd --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp @@ -0,0 +1,132 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "foundation/PxMemory.h" +#include "ScSimStats.h" +#include "PxvSimStats.h" + +using namespace physx; + +Sc::SimStats::SimStats() +{ + numBroadPhaseAdds = numBroadPhaseRemoves = 0; + + clear(); +} + +void Sc::SimStats::clear() +{ +#if PX_ENABLE_SIM_STATS + PxMemZero(const_cast(reinterpret_cast(&numTriggerPairs)), sizeof(TriggerPairCounts)); + numBroadPhaseAddsPending = numBroadPhaseRemovesPending = 0; +#endif +} + +void Sc::SimStats::simStart() +{ +#if PX_ENABLE_SIM_STATS + // pending broadphase adds/removes are now the current ones + numBroadPhaseAdds = numBroadPhaseAddsPending; + numBroadPhaseRemoves = numBroadPhaseRemovesPending; + clear(); +#endif +} + +void Sc::SimStats::readOut(PxSimulationStatistics& s, const PxvSimStats& simStats) const +{ +#if PX_ENABLE_SIM_STATS + s = PxSimulationStatistics(); // clear stats + + for(PxU32 i=0; i < PxGeometryType::eCONVEXMESH+1; i++) + { + for(PxU32 j=0; j < PxGeometryType::eGEOMETRY_COUNT; j++) + { + s.nbTriggerPairs[i][j] += PxU32(numTriggerPairs[i][j]); + if (i != j) + s.nbTriggerPairs[j][i] += PxU32(numTriggerPairs[i][j]); + } + } + + s.nbBroadPhaseAdds = numBroadPhaseAdds; + s.nbBroadPhaseRemoves = numBroadPhaseRemoves; + + for(PxU32 i=0; i < PxGeometryType::eGEOMETRY_COUNT; i++) + { + s.nbDiscreteContactPairs[i][i] = simStats.mNbDiscreteContactPairs[i][i]; + s.nbModifiedContactPairs[i][i] = simStats.mNbModifiedContactPairs[i][i]; + s.nbCCDPairs[i][i] = simStats.mNbCCDPairs[i][i]; + + for(PxU32 j=i+1; j < PxGeometryType::eGEOMETRY_COUNT; j++) + { + PxU32 c = simStats.mNbDiscreteContactPairs[i][j]; + s.nbDiscreteContactPairs[i][j] = c; + s.nbDiscreteContactPairs[j][i] = c; + + c = simStats.mNbModifiedContactPairs[i][j]; + s.nbModifiedContactPairs[i][j] = c; + s.nbModifiedContactPairs[j][i] = c; + + c = simStats.mNbCCDPairs[i][j]; + s.nbCCDPairs[i][j] = c; + s.nbCCDPairs[j][i] = c; + } +#if PX_DEBUG + for(PxU32 j=0; j < i; j++) + { + // PxvSimStats should only use one half of the matrix + PX_ASSERT(simStats.mNbDiscreteContactPairs[i][j] == 0); + PX_ASSERT(simStats.mNbModifiedContactPairs[i][j] == 0); + PX_ASSERT(simStats.mNbCCDPairs[i][j] == 0); + } +#endif + } + + s.nbDiscreteContactPairsTotal = simStats.mNbDiscreteContactPairsTotal; + s.nbDiscreteContactPairsWithCacheHits = simStats.mNbDiscreteContactPairsWithCacheHits; + s.nbDiscreteContactPairsWithContacts = simStats.mNbDiscreteContactPairsWithContacts; + s.nbActiveConstraints = simStats.mNbActiveConstraints; + s.nbActiveDynamicBodies = simStats.mNbActiveDynamicBodies; + s.nbActiveKinematicBodies = simStats.mNbActiveKinematicBodies; + + s.nbAxisSolverConstraints = simStats.mNbAxisSolverConstraints; + + s.peakConstraintMemory = simStats.mPeakConstraintBlockAllocations * 16 * 1024; + s.compressedContactSize = simStats.mTotalCompressedContactSize; + s.requiredContactConstraintMemory = simStats.mTotalConstraintSize; + s.nbNewPairs = simStats.mNbNewPairs; + s.nbLostPairs = simStats.mNbLostPairs; + s.nbNewTouches = simStats.mNbNewTouches; + s.nbLostTouches = simStats.mNbLostTouches; + s.nbPartitions = simStats.mNbPartitions; + +#else + PX_UNUSED(s); + PX_UNUSED(simStats); +#endif +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h new file mode 100644 index 000000000..06356e815 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h @@ -0,0 +1,89 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_SIM_STATS +#define PX_PHYSICS_SCP_SIM_STATS + +#include "PsAtomic.h" +#include "PsUserAllocated.h" +#include "CmPhysXCommon.h" +#include "PxGeometry.h" +#include "PxSimulationStatistics.h" + +namespace physx +{ + +struct PxvSimStats; + +namespace Sc +{ + + /* + Description: contains statistics for the scene. + */ + class SimStats : public Ps::UserAllocated + { + public: + SimStats(); + + void clear(); //set counters to zero + void simStart(); + void readOut(PxSimulationStatistics& dest, const PxvSimStats& simStats) const; + + PX_INLINE void incBroadphaseAdds() + { + numBroadPhaseAddsPending++; + } + + PX_INLINE void incBroadphaseRemoves() + { + numBroadPhaseRemovesPending++; + } + + private: + // Broadphase adds/removes for the current simulation step + PxU32 numBroadPhaseAdds; + PxU32 numBroadPhaseRemoves; + + // Broadphase adds/removes for the next simulation step + PxU32 numBroadPhaseAddsPending; + PxU32 numBroadPhaseRemovesPending; + + public: + typedef PxI32 TriggerPairCountsNonVolatile[PxGeometryType::eCONVEXMESH+1][PxGeometryType::eGEOMETRY_COUNT]; + typedef volatile TriggerPairCountsNonVolatile TriggerPairCounts; + TriggerPairCounts numTriggerPairs; + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp new file mode 100644 index 000000000..952436230 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp @@ -0,0 +1,43 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScSimulationController.h" +#include "PsAllocator.h" + +using namespace physx; + +PxsSimulationController* physx::createSimulationController(PxsSimulationControllerCallback* callback) +{ + return PX_PLACEMENT_NEW(PX_ALLOC(sizeof(Sc::SimulationController), PX_DEBUG_EXP("ScSimulationController")), Sc::SimulationController(callback)); +} + +void Sc::SimulationController::udpateScBodyAndShapeSim(PxsTransformCache& /*cache*/, Bp::BoundsArray& /*boundArray*/, PxBaseTask* continuation) +{ + mCallback->updateScBodyAndShapeSim(continuation); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h new file mode 100644 index 000000000..6360c4dbf --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "PxsSimulationController.h" + +#ifndef SC_SIMULATION_CONTROLLER_H +#define SC_SIMULATION_CONTROLLER_H + + +namespace physx +{ + +class PxsHeapMemoryAllocator; + +namespace Sc +{ + + class SimulationController : public PxsSimulationController + { + PX_NOCOPY(SimulationController) + public: + SimulationController(PxsSimulationControllerCallback* callback): PxsSimulationController(callback) + { + } + + virtual ~SimulationController(){} + virtual void addJoint(const PxU32 /*edgeIndex*/, Dy::Constraint* /*constraint*/, IG::IslandSim& /*islandSim*/, Ps::Array& /*jointIndices*/, + Ps::Array& /*managerIter*/, PxU32 /*uniqueId*/){} + virtual void removeJoint(const PxU32 /*edgeIndex*/, Dy::Constraint* /*constraint*/, Ps::Array& /*jointIndices*/, IG::IslandSim& /*islandSim*/){} + virtual void addShape(PxsShapeSim* /*shapeSim*/, const PxU32 /*index*/){} + virtual void removeShape(const PxU32 /*index*/){} + virtual void addDynamic(PxsRigidBody* /*rigidBody*/, const IG::NodeIndex& /*nodeIndex*/){} + virtual void addDynamics(PxsRigidBody** /*rigidBody*/, const PxU32* /*nodeIndex*/, PxU32 /*nbBodies*/) {} + virtual void addArticulation(Dy::ArticulationV* /*articulation*/, const IG::NodeIndex& /*nodeIndex*/){} + virtual void releaseArticulation(Dy::ArticulationV* /*articulation*/) {} + virtual void releaseDeferredArticulationIds(){} + virtual void updateDynamic(const bool /*isArticulationLink*/, const IG::NodeIndex& /*nodeIndex*/) {} + virtual void updateJoint(const PxU32 /*edgeIndex*/, Dy::Constraint* /*constraint*/){} + virtual void updateBodies(PxsRigidBody** /*rigidBodies*/, PxU32* /*nodeIndices*/, const PxU32 /*nbBodies*/) {} + virtual void updateBody(PxsRigidBody* /*rigidBody*/, const PxU32 /*nodeIndex*/) {} + virtual void updateBodiesAndShapes(PxBaseTask* /*continuation*/){} + virtual void update(const PxU32 /*bitMapWordCounts*/){} + virtual void gpuDmabackData(PxsTransformCache& /*cache*/, Bp::BoundsArray& /*boundArray*/, Cm::BitMapPinned& /*changedAABBMgrHandles*/){} + virtual void udpateScBodyAndShapeSim(PxsTransformCache& cache, Bp::BoundsArray& boundArray, PxBaseTask* continuation); + virtual PxU32* getActiveBodies() { return NULL; } + virtual PxU32* getDeactiveBodies() { return NULL; } + virtual void** getRigidBodies() { return NULL; } + virtual PxU32 getNbBodies() { return 0; } + virtual PxU32 getNbFrozenShapes() { return 0; } + virtual PxU32 getNbUnfrozenShapes() { return 0; } + + virtual PxU32* getUnfrozenShapes() { return NULL; } + virtual PxU32* getFrozenShapes() { return NULL; } + virtual PxsShapeSim** getShapeSims() { return NULL; } + virtual PxU32 getNbShapes() { return 0; } + + virtual void clear() { } + virtual void setBounds(Bp::BoundsArray* /*boundArray*/){} + virtual void reserve(const PxU32 /*nbBodies*/) {} + + }; +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp new file mode 100644 index 000000000..7676fcd57 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp @@ -0,0 +1,122 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScSqBoundsManager.h" +#include "ScBodySim.h" +#include "ScShapeSim.h" +#include "common/PxProfileZone.h" + +using namespace physx; +using namespace Sc; + +SqBoundsManager::SqBoundsManager() : + mShapes (PX_DEBUG_EXP("SqBoundsManager::mShapes")), + mRefs (PX_DEBUG_EXP("SqBoundsManager::mRefs")), + mBoundsIndices (PX_DEBUG_EXP("SqBoundsManager::mBoundsIndices")), + mRefless (PX_DEBUG_EXP("SqBoundsManager::mRefless")) +{ +} + +void SqBoundsManager::addShape(ShapeSim& shape) +{ + PX_ASSERT(shape.getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE); + PX_ASSERT(!shape.getBodySim()->usingSqKinematicTarget()); + PX_ASSERT(!shape.getBodySim()->isFrozen()); + + const PxU32 id = mShapes.size(); + PX_ASSERT(id == mRefs.size()); + PX_ASSERT(id == mBoundsIndices.size()); + + shape.setSqBoundsId(id); + + mShapes.pushBack(&shape); + mRefs.pushBack(PX_INVALID_U32); // PT: TODO: should be INVALID_PRUNERHANDLE but cannot include SqPruner.h + mBoundsIndices.pushBack(shape.getElementID()); + mRefless.pushBack(&shape); +} + +void SqBoundsManager::removeShape(ShapeSim& shape) +{ + const PxU32 id = shape.getSqBoundsId(); + PX_ASSERT(id!=PX_INVALID_U32); + + shape.setSqBoundsId(PX_INVALID_U32); + mShapes[id] = mShapes.back(); + mBoundsIndices[id] = mBoundsIndices.back(); + mRefs[id] = mRefs.back(); + + if(id+1 != mShapes.size()) + mShapes[id]->setSqBoundsId(id); + + mShapes.popBack(); + mRefs.popBack(); + mBoundsIndices.popBack(); +} + +void SqBoundsManager::syncBounds(SqBoundsSync& sync, SqRefFinder& finder, const PxBounds3* bounds, PxU64 contextID, const Cm::BitMap& dirtyShapeSimMap) +{ + PX_PROFILE_ZONE("Sim.sceneQuerySyncBounds", contextID); + PX_UNUSED(contextID); + +#if PX_DEBUG + for(PxU32 i=0;iusingSqKinematicTarget()); + PX_ASSERT(!shape.getBodySim()->isFrozen()); + } +#endif + + ShapeSim*const * shapes = mRefless.begin(); + for(PxU32 i=0, size = mRefless.size();igetSqBoundsId(); + // PT: + // + // If id == PX_INVALID_U32, the shape has been removed and not re-added. Nothing to do in this case, we just ignore it. + // This case didn't previously exist since mRefless only contained valid (added) shapes. But now we left removed shapes in the + // structure, and these have an id == PX_INVALID_U32. + // + // Now if the id is valid but mRefs[id] == PX_INVALID_U32, this is a regular shape that has been added and not processed yet. + // So we process it. + // + // Finally, if both id and mRefs[id] are not PX_INVALID_U32, this is a shape that has been added, removed, and re-added. The + // array contains the same shape twice and we only need to process it once. + if(id!=PX_INVALID_U32) + { + if(mRefs[id] == PX_INVALID_U32) // PT: TODO: should be INVALID_PRUNERHANDLE but cannot include SqPruner.h + mRefs[id] = finder.find(static_cast(shapes[i]->getBodySim()->getPxActor()), shapes[i]->getPxShape()); + } + } + mRefless.clear(); + + sync.sync(mRefs.begin(), mBoundsIndices.begin(), bounds, mShapes.size(), dirtyShapeSimMap); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h new file mode 100644 index 000000000..74d16425b --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_SQ_BOUNDS_MANAGER +#define PX_PHYSICS_SCP_SQ_BOUNDS_MANAGER + +#include "CmPhysXCommon.h" +#include "foundation/PxBounds3.h" +#include "PsArray.h" +#include "PsUserAllocated.h" +#include "PsHashSet.h" +#include "CmBitMap.h" + +namespace physx +{ +namespace Sq +{ +typedef PxU32 PrunerHandle; // PT: we should get this from SqPruner.h but it cannot be included from here +} + +namespace Sc +{ +struct SqBoundsSync; +struct SqRefFinder; +class ShapeSim; + +class SqBoundsManager : public Ps::UserAllocated +{ + PX_NOCOPY(SqBoundsManager) +public: + SqBoundsManager(); + + void addShape(ShapeSim& shape); + void removeShape(ShapeSim& shape); + void syncBounds(SqBoundsSync& sync, SqRefFinder& finder, const PxBounds3* bounds, PxU64 contextID, const Cm::BitMap& dirtyShapeSimMap); + +private: + + Ps::Array mShapes; // + Ps::Array mRefs; // SQ pruner references + Ps::Array mBoundsIndices; // indices into the Sc bounds array + Ps::Array mRefless; // shapesims without references +}; +} +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp new file mode 100644 index 000000000..12ff0e08f --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp @@ -0,0 +1,49 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#include "ScStaticCore.h" +#include "ScStaticSim.h" +#include "PxRigidStatic.h" + +using namespace physx; + +Sc::StaticSim* Sc::StaticCore::getSim() const +{ + return static_cast(Sc::ActorCore::getSim()); +} + +void Sc::StaticCore::setActor2World(const PxTransform& actor2World) +{ + mCore.body2World = actor2World; + + StaticSim* sim = getSim(); + if(sim) + sim->notifyShapesOfTransformChange(); +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h new file mode 100644 index 000000000..f57cf6369 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_PHYSICS_SCP_STATIC_SIM +#define PX_PHYSICS_SCP_STATIC_SIM + +#include "ScRigidSim.h" +#include "ScStaticCore.h" + +namespace physx +{ +namespace Sc +{ + class StaticSim : public RigidSim + { + //--------------------------------------------------------------------------------- + // Construction, destruction & initialization + //--------------------------------------------------------------------------------- + public: + StaticSim(Scene& scene, StaticCore& core) : RigidSim(scene, core) {} + ~StaticSim() { getStaticCore().setSim(NULL); } + + PX_FORCE_INLINE StaticCore& getStaticCore() const { return static_cast(getRigidCore()); } + }; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp new file mode 100644 index 000000000..a4f5ce858 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp @@ -0,0 +1,135 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#include "ScTriggerInteraction.h" +#include "ScBodySim.h" +#include "ScNPhaseCore.h" + +using namespace physx; +using namespace Sc; + +TriggerInteraction::TriggerInteraction( ShapeSim& tShape, ShapeSim& oShape) : + ElementSimInteraction(tShape, oShape, InteractionType::eTRIGGER, InteractionFlag::eRB_ELEMENT | InteractionFlag::eFILTERABLE), + mFlags(PROCESS_THIS_FRAME), + mLastFrameHadContacts(false) +{ + // The PxPairFlags eNOTIFY_TOUCH_FOUND and eNOTIFY_TOUCH_LOST get stored and mixed up with internal flags. Make sure any breaking change gets noticed. + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_FOUND < PxPairFlag::eNOTIFY_TOUCH_LOST); + PX_COMPILE_TIME_ASSERT((PAIR_FLAGS_MASK & PxPairFlag::eNOTIFY_TOUCH_FOUND) == PxPairFlag::eNOTIFY_TOUCH_FOUND); + PX_COMPILE_TIME_ASSERT((PAIR_FLAGS_MASK & PxPairFlag::eNOTIFY_TOUCH_LOST) == PxPairFlag::eNOTIFY_TOUCH_LOST); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_FOUND < 0xffff); + PX_COMPILE_TIME_ASSERT(PxPairFlag::eNOTIFY_TOUCH_LOST < 0xffff); + PX_COMPILE_TIME_ASSERT(LAST < 0xffff); + + bool active = registerInActors(); + Scene& scene = getScene(); + scene.registerInteraction(this, active); + scene.getNPhaseCore()->registerInteraction(this); + + PX_ASSERT(getTriggerShape().getFlags() & PxShapeFlag::eTRIGGER_SHAPE); + mTriggerCache.state = Gu::TRIGGER_DISJOINT; +} + +TriggerInteraction::~TriggerInteraction() +{ + Scene& scene = getScene(); + scene.unregisterInteraction(this); + scene.getNPhaseCore()->unregisterInteraction(this); + unregisterFromActors(); +} + +static bool isOneActorActive(TriggerInteraction* trigger) +{ + const BodySim* bodySim0 = trigger->getTriggerShape().getBodySim(); + if(bodySim0 && bodySim0->isActive()) + { + PX_ASSERT(!bodySim0->isKinematic() || bodySim0->readInternalFlag(BodySim::BF_KINEMATIC_MOVED) || + bodySim0->readInternalFlag(BodySim::InternalFlags(BodySim::BF_KINEMATIC_SETTLING | BodySim::BF_KINEMATIC_SETTLING_2))); + return true; + } + + const BodySim* bodySim1 = trigger->getOtherShape().getBodySim(); + if(bodySim1 && bodySim1->isActive()) + { + PX_ASSERT(!bodySim1->isKinematic() || bodySim1->readInternalFlag(BodySim::BF_KINEMATIC_MOVED) || + bodySim1->readInternalFlag(BodySim::InternalFlags(BodySim::BF_KINEMATIC_SETTLING | BodySim::BF_KINEMATIC_SETTLING_2))); + return true; + } + + return false; +} + +// +// Some general information about triggers and sleeping +// +// The goal is to avoid running overlap tests if both objects are sleeping. +// This is an optimization for eNOTIFY_TOUCH_LOST events since the overlap state +// can not change if both objects are sleeping. eNOTIFY_TOUCH_FOUND should be sent nonetheless. +// For this to work the following assumptions are made: +// - On creation or if the pose of an actor is set, the pair will always be checked. +// - If the scenario above does not apply, then a trigger pair can only be deactivated, if both actors are sleeping. +// - If an overlapping actor is activated/deactivated, the trigger interaction gets notified +// +bool TriggerInteraction::onActivate_(void*) +{ + // IMPORTANT: this method can get called concurrently from multiple threads -> make sure shared resources + // are protected (note: there are none at the moment but it might change) + + if(!(readFlag(PROCESS_THIS_FRAME))) + { + if(isOneActorActive(this)) + { + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; + } + else + return false; + } + else + { + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; // newly created trigger pairs should always test for overlap, no matter the sleep state + } +} + +bool TriggerInteraction::onDeactivate_() +{ + if(!readFlag(PROCESS_THIS_FRAME)) + { + if(!isOneActorActive(this)) + { + clearInteractionFlag(InteractionFlag::eIS_ACTIVE); + return true; + } + else + return false; + } + else + return false; +} diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h new file mode 100644 index 000000000..5de0fee14 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h @@ -0,0 +1,122 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + + +#ifndef PX_COLLISION_TRIGGERINTERACTION +#define PX_COLLISION_TRIGGERINTERACTION + +#include "ScElementSimInteraction.h" +#include "ScShapeSim.h" +#include "GuOverlapTests.h" + +namespace physx +{ +namespace Sc +{ + class TriggerInteraction : public ElementSimInteraction + { + public: + enum TriggerFlag + { + PAIR_FLAGS_MASK = ((PxPairFlag::eNOTIFY_TOUCH_LOST << 1) - 1), // Bits where the PxPairFlags eNOTIFY_TOUCH_FOUND and eNOTIFY_TOUCH_LOST get stored + NEXT_FREE = ((PAIR_FLAGS_MASK << 1) & ~PAIR_FLAGS_MASK), + + PROCESS_THIS_FRAME = (NEXT_FREE << 0), // the trigger pair is new or the pose of an actor was set -> initial processing required. + // This is important to cover cases where a static or kinematic + // (non-moving) trigger is created and overlaps with a sleeping + // object. Or for the case where a static/kinematic is teleported to a new + // location. TOUCH_FOUND should still get sent in that case. + LAST = (NEXT_FREE << 1) + }; + + TriggerInteraction(ShapeSim& triggerShape, ShapeSim& otherShape); + ~TriggerInteraction(); + + PX_FORCE_INLINE Gu::TriggerCache& getTriggerCache() { return mTriggerCache; } + PX_FORCE_INLINE ShapeSim& getTriggerShape() const { return static_cast(getElement0()); } + PX_FORCE_INLINE ShapeSim& getOtherShape() const { return static_cast(getElement1()); } + + PX_FORCE_INLINE bool lastFrameHadContacts() const { return mLastFrameHadContacts; } + PX_FORCE_INLINE void updateLastFrameHadContacts(bool hasContact) { mLastFrameHadContacts = hasContact; } + + PX_FORCE_INLINE PxPairFlags getTriggerFlags() const { return PxPairFlags(PxU32(mFlags) & PAIR_FLAGS_MASK); } + PX_FORCE_INLINE void setTriggerFlags(PxPairFlags triggerFlags); + + PX_FORCE_INLINE void raiseFlag(TriggerFlag flag) { mFlags |= flag; } + PX_FORCE_INLINE void clearFlag(TriggerFlag flag) { mFlags &= ~flag; } + PX_FORCE_INLINE Ps::IntBool readFlag(TriggerFlag flag) const { return Ps::IntBool(mFlags & flag); } + + PX_FORCE_INLINE void forceProcessingThisFrame(Sc::Scene& scene); + + bool onActivate_(void*); + bool onDeactivate_(); + + protected: + Gu::TriggerCache mTriggerCache; + PxU16 mFlags; + bool mLastFrameHadContacts; + }; + +} // namespace Sc + + +PX_FORCE_INLINE void Sc::TriggerInteraction::setTriggerFlags(PxPairFlags triggerFlags) +{ + PX_ASSERT(PxU32(triggerFlags) < (PxPairFlag::eDETECT_CCD_CONTACT << 1)); // to find out if a new PxPairFlag has been added in which case PAIR_FLAGS_MASK needs to get adjusted + +#if PX_CHECKED + if (triggerFlags & PxPairFlag::eNOTIFY_TOUCH_PERSISTS) + { + PX_WARN_ONCE("Trigger pairs do not support PxPairFlag::eNOTIFY_TOUCH_PERSISTS events any longer."); + } +#endif + + PxU32 newFlags = mFlags; + PxU32 fl = PxU32(triggerFlags) & PxU32(PxPairFlag::eNOTIFY_TOUCH_FOUND|PxPairFlag::eNOTIFY_TOUCH_LOST); + newFlags &= (~PAIR_FLAGS_MASK); // clear old flags + newFlags |= fl; + + mFlags = PxU16(newFlags); +} + + +PX_FORCE_INLINE void Sc::TriggerInteraction::forceProcessingThisFrame(Sc::Scene& scene) +{ + raiseFlag(PROCESS_THIS_FRAME); + + if (!readInteractionFlag(InteractionFlag::eIS_ACTIVE)) + { + raiseInteractionFlag(InteractionFlag::eIS_ACTIVE); + scene.notifyInteractionActivated(this); + } +} + +} + +#endif diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h new file mode 100644 index 000000000..dbbbbc819 --- /dev/null +++ b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_PHYSICS_SCP_TRIGGER_PAIRS +#define PX_PHYSICS_SCP_TRIGGER_PAIRS + +#include "PsArray.h" +#include "CmPhysXCommon.h" +#include "PxFiltering.h" +#include "PxClient.h" +#include "PxSimulationEventCallback.h" + +namespace physx +{ +class PxShape; + +namespace Sc +{ + struct TriggerPairFlag + { + enum Enum + { + eTEST_FOR_REMOVED_SHAPES = PxTriggerPairFlag::eNEXT_FREE // for cases where the pair got deleted because one of the shape volumes got removed from broadphase. + // This covers scenarios like volume re-insertion into broadphase as well since the shape might get removed + // after such an operation. The scenarios to consider are: + // + // - shape gets removed (this includes raising PxActorFlag::eDISABLE_SIMULATION) + // - shape switches to eSCENE_QUERY_SHAPE only + // - shape switches to eSIMULATION_SHAPE + // - resetFiltering() + // - actor gets removed from an aggregate + }; + }; + + PX_COMPILE_TIME_ASSERT((1 << (8*sizeof(PxTriggerPairFlags::InternalType))) > TriggerPairFlag::eTEST_FOR_REMOVED_SHAPES); + + struct TriggerPairExtraData + { + PX_INLINE TriggerPairExtraData() : + shape0ID(0xffffffff), + shape1ID(0xffffffff), + client0ID(0xff), + client1ID(0xff) + { + } + + PX_INLINE TriggerPairExtraData(PxU32 s0ID, PxU32 s1ID, + PxClientID cl0ID, PxClientID cl1ID) : + shape0ID(s0ID), + shape1ID(s1ID), + client0ID(cl0ID), + client1ID(cl1ID) + { + } + + PxU32 shape0ID; + PxU32 shape1ID; + PxClientID client0ID; + PxClientID client1ID; + }; + + typedef Ps::Array TriggerBufferExtraData; + typedef Ps::Array TriggerBufferAPI; + +} // namespace Sc + +} + +#endif diff --git a/src/PhysX/physx/source/task/src/TaskManager.cpp b/src/PhysX/physx/source/task/src/TaskManager.cpp new file mode 100644 index 000000000..e5cd3fdf2 --- /dev/null +++ b/src/PhysX/physx/source/task/src/TaskManager.cpp @@ -0,0 +1,538 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#include "task/PxTask.h" +#include "task/PxTaskDefine.h" +#include "foundation/PxErrors.h" + +#include "PsThread.h" +#include "PsAtomic.h" +#include "PsMutex.h" +#include "PsHashMap.h" +#include "PsArray.h" +#include "PsAllocator.h" + +#define DOT_LOG 0 + +#define LOCK() shdfnd::Mutex::ScopedLock __lock__(mMutex) + +namespace physx +{ + const int EOL = -1; + typedef shdfnd::HashMap PxTaskNameToIDMap; + + struct PxTaskDepTableRow + { + PxTaskID mTaskID; + int mNextDep; + }; + typedef shdfnd::Array PxTaskDepTable; + + class PxTaskTableRow + { + public: + PxTaskTableRow() : mRefCount( 1 ), mStartDep(EOL), mLastDep(EOL) {} + void addDependency( PxTaskDepTable& depTable, PxTaskID taskID ) + { + int newDep = int(depTable.size()); + PxTaskDepTableRow row; + row.mTaskID = taskID; + row.mNextDep = EOL; + depTable.pushBack( row ); + + if( mLastDep == EOL ) + { + mStartDep = mLastDep = newDep; + } + else + { + depTable[ uint32_t(mLastDep) ].mNextDep = newDep; + mLastDep = newDep; + } + } + + PxTask * mTask; + volatile int mRefCount; + PxTaskType::Enum mType; + int mStartDep; + int mLastDep; + }; + typedef shdfnd::Array PxTaskTable; + + +/* Implementation of PxTaskManager abstract API */ +class PxTaskMgr : public PxTaskManager, public shdfnd::UserAllocated +{ + PX_NOCOPY(PxTaskMgr) +public: + PxTaskMgr(PxErrorCallback& , PxCpuDispatcher*, PxGpuDispatcher*); + ~PxTaskMgr(); + + void setCpuDispatcher( PxCpuDispatcher& ref ) + { + mCpuDispatcher = &ref; + } + + void setGpuDispatcher( PxGpuDispatcher& ref ) + { + mGpuDispatcher = &ref; + } + + PxCpuDispatcher* getCpuDispatcher() const + { + return mCpuDispatcher; + } + + PxGpuDispatcher* getGpuDispatcher() const + { + return mGpuDispatcher; + } + + void resetDependencies(); + void startSimulation(); + void stopSimulation(); + void taskCompleted( PxTask& task ); + + PxTaskID getNamedTask( const char *name ); + PxTaskID submitNamedTask( PxTask *task, const char *name, PxTaskType::Enum type = PxTaskType::TT_CPU ); + PxTaskID submitUnnamedTask( PxTask& task, PxTaskType::Enum type = PxTaskType::TT_CPU ); + PxTask* getTaskFromID( PxTaskID ); + + bool dispatchTask( PxTaskID taskID, bool gpuGroupStart ); + bool resolveRow( PxTaskID taskID, bool gpuGroupStart ); + + void release(); + + void finishBefore( PxTask& task, PxTaskID taskID ); + void startAfter( PxTask& task, PxTaskID taskID ); + + void addReference( PxTaskID taskID ); + void decrReference( PxTaskID taskID ); + int32_t getReference( PxTaskID taskID ) const; + + void decrReference( PxLightCpuTask& lighttask ); + void addReference( PxLightCpuTask& lighttask ); + + PxErrorCallback& mErrorCallback; + PxCpuDispatcher *mCpuDispatcher; + PxGpuDispatcher *mGpuDispatcher; + PxTaskNameToIDMap mName2IDmap; + volatile int mPendingTasks; + shdfnd::Mutex mMutex; + + PxTaskDepTable mDepTable; + PxTaskTable mTaskTable; + + shdfnd::Array mStartDispatch; + }; + +PxTaskManager* PxTaskManager::createTaskManager(PxErrorCallback& errorCallback, PxCpuDispatcher* cpuDispatcher, PxGpuDispatcher* gpuDispatcher) +{ + return PX_NEW(PxTaskMgr)(errorCallback, cpuDispatcher, gpuDispatcher); +} + +PxTaskMgr::PxTaskMgr(PxErrorCallback& errorCallback, PxCpuDispatcher* cpuDispatcher, PxGpuDispatcher* gpuDispatcher) + : mErrorCallback (errorCallback) + , mCpuDispatcher( cpuDispatcher ) + , mGpuDispatcher( gpuDispatcher ) + , mPendingTasks( 0 ) + , mDepTable(PX_DEBUG_EXP("PxTaskDepTable")) + , mTaskTable(PX_DEBUG_EXP("PxTaskTable")) + , mStartDispatch(PX_DEBUG_EXP("StartDispatch")) +{ +} + +PxTaskMgr::~PxTaskMgr() +{ +} + +void PxTaskMgr::release() +{ + PX_DELETE(this); +} + +void PxTaskMgr::decrReference(PxLightCpuTask& lighttask) +{ + /* This does not need a lock! */ + if (!shdfnd::atomicDecrement(&lighttask.mRefCount)) + { + PX_ASSERT(mCpuDispatcher); + if (mCpuDispatcher) + { + mCpuDispatcher->submitTask(lighttask); + } + else + { + lighttask.release(); + } + } +} + +void PxTaskMgr::addReference(PxLightCpuTask& lighttask) +{ + /* This does not need a lock! */ + shdfnd::atomicIncrement(&lighttask.mRefCount); +} + +/* + * Called by the owner (Scene) at the start of every frame, before + * asking for tasks to be submitted. + */ +void PxTaskMgr::resetDependencies() +{ + PX_ASSERT( !mPendingTasks ); // only valid if you don't resubmit named tasks, this is true for the SDK + PX_ASSERT( mCpuDispatcher ); + mTaskTable.clear(); + mDepTable.clear(); + mName2IDmap.clear(); + mPendingTasks = 0; +} + +/* + * Called by the owner (Scene) to start simulating the task graph. + * Dispatch all tasks with refCount == 1 + */ +void PxTaskMgr::startSimulation() +{ + PX_ASSERT( mCpuDispatcher ); + + if( mGpuDispatcher ) + { + mGpuDispatcher->startSimulation(); + } + + /* Handle empty task graph */ + if( mPendingTasks == 0 ) + { + + return; + } + + bool gpuDispatch = false; + for( PxTaskID i = 0 ; i < mTaskTable.size() ; i++ ) + { + if( mTaskTable[ i ].mType == PxTaskType::TT_COMPLETED ) + { + continue; + } + if( !shdfnd::atomicDecrement( &mTaskTable[ i ].mRefCount ) ) + { + mStartDispatch.pushBack(i); + } + } + for( uint32_t i=0; ifinishGroup(); + } +} + +void PxTaskMgr::stopSimulation() +{ + if( mGpuDispatcher ) + { + mGpuDispatcher->stopSimulation(); + } +} + +PxTaskID PxTaskMgr::getNamedTask( const char *name ) +{ + const PxTaskNameToIDMap::Entry *ret; + { + LOCK(); + ret = mName2IDmap.find( name ); + } + if( ret ) + { + return ret->second; + } + else + { + // create named entry in task table, without a task + return submitNamedTask( NULL, name, PxTaskType::TT_NOT_PRESENT ); +} +} + +PxTask* PxTaskMgr::getTaskFromID( PxTaskID id ) +{ + LOCK(); // todo: reader lock necessary? + return mTaskTable[ id ].mTask; +} + + +/* If called at runtime, must be thread-safe */ +PxTaskID PxTaskMgr::submitNamedTask( PxTask *task, const char *name, PxTaskType::Enum type ) +{ + if( task ) + { + task->mTm = this; + task->submitted(); + } + + LOCK(); + + const PxTaskNameToIDMap::Entry *ret = mName2IDmap.find( name ); + if( ret ) + { + PxTaskID prereg = ret->second; + if( task ) + { + /* name was registered for us by a dependent task */ + PX_ASSERT( !mTaskTable[ prereg ].mTask ); + PX_ASSERT( mTaskTable[ prereg ].mType == PxTaskType::TT_NOT_PRESENT ); + mTaskTable[ prereg ].mTask = task; + mTaskTable[ prereg ].mType = type; + task->mTaskID = prereg; + } + return prereg; + } + else + { + shdfnd::atomicIncrement(&mPendingTasks); + PxTaskID id = static_cast(mTaskTable.size()); + mName2IDmap[ name ] = id; + if( task ) + { + task->mTaskID = id; + } + PxTaskTableRow r; + r.mTask = task; + r.mType = type; + mTaskTable.pushBack(r); + return id; + } +} + +/* + * Add an unnamed task to the task table + */ +PxTaskID PxTaskMgr::submitUnnamedTask( PxTask& task, PxTaskType::Enum type ) +{ + shdfnd::atomicIncrement(&mPendingTasks); + + task.mTm = this; + task.submitted(); + + LOCK(); + task.mTaskID = static_cast(mTaskTable.size()); + PxTaskTableRow r; + r.mTask = &task; + r.mType = type; + mTaskTable.pushBack(r); + return task.mTaskID; +} + + +/* Called by worker threads (or cooperating application threads) when a + * PxTask has completed. Propogate depdenencies, decrementing all + * referenced tasks' refCounts. If any of those reach zero, activate + * those tasks. + */ +void PxTaskMgr::taskCompleted( PxTask& task ) +{ + LOCK(); + if( resolveRow( task.mTaskID, false ) ) + { + mGpuDispatcher->finishGroup(); + } +} + +/* ================== Private Functions ======================= */ + +/* + * Add a dependency to force 'task' to complete before the + * referenced 'taskID' is allowed to be dispatched. + */ +void PxTaskMgr::finishBefore( PxTask& task, PxTaskID taskID ) +{ + LOCK(); + PX_ASSERT( mTaskTable[ taskID ].mType != PxTaskType::TT_COMPLETED ); + + mTaskTable[ task.mTaskID ].addDependency( mDepTable, taskID ); + shdfnd::atomicIncrement( &mTaskTable[ taskID ].mRefCount ); +} + + +/* + * Add a dependency to force 'task' to wait for the referenced 'taskID' + * to complete before it is allowed to be dispatched. + */ +void PxTaskMgr::startAfter( PxTask& task, PxTaskID taskID ) +{ + LOCK(); + PX_ASSERT( mTaskTable[ taskID ].mType != PxTaskType::TT_COMPLETED ); + + mTaskTable[ taskID ].addDependency( mDepTable, task.mTaskID ); + shdfnd::atomicIncrement( &mTaskTable[ task.mTaskID ].mRefCount ); +} + + +void PxTaskMgr::addReference( PxTaskID taskID ) +{ + LOCK(); + shdfnd::atomicIncrement( &mTaskTable[ taskID ].mRefCount ); +} + +/* + * Remove one reference count from a task. Intended for use by the + * GPU dispatcher, to remove reference counts when CUDA events are + * resolved. Must be done here to make it thread safe. + */ +void PxTaskMgr::decrReference( PxTaskID taskID ) +{ + LOCK(); + + if( !shdfnd::atomicDecrement( &mTaskTable[ taskID ].mRefCount ) ) + { + if( dispatchTask( taskID, false ) ) + { + mGpuDispatcher->finishGroup(); + } + } +} + +int32_t PxTaskMgr::getReference(PxTaskID taskID) const +{ + return mTaskTable[ taskID ].mRefCount; +} + +/* + * A task has completed, decrement all dependencies and submit tasks + * that are ready to run. Signal simulation end if ther are no more + * pending tasks. + */ +bool PxTaskMgr::resolveRow( PxTaskID taskID, bool gpuGroupStart ) +{ + int depRow = mTaskTable[ taskID ].mStartDep; + + uint32_t streamIndex = 0; + bool syncRequired = false; + if( mTaskTable[ taskID ].mTask ) + { + streamIndex = mTaskTable[ taskID ].mTask->mStreamIndex; + } + + while( depRow != EOL ) + { + PxTaskDepTableRow& row = mDepTable[ uint32_t(depRow) ]; + PxTaskTableRow& dtt = mTaskTable[ row.mTaskID ]; + + // pass stream index to (up to one) dependent GPU task + if( dtt.mTask && dtt.mType == PxTaskType::TT_GPU && streamIndex ) + { + if( dtt.mTask->mStreamIndex ) + { + PX_ASSERT( dtt.mTask->mStreamIndex != streamIndex ); + dtt.mTask->mPreSyncRequired = true; + } + else if( syncRequired ) + { + dtt.mTask->mPreSyncRequired = true; + } + else + { + dtt.mTask->mStreamIndex = streamIndex; + /* only one forward task gets to use this stream */ + syncRequired = true; + } + } + + if( !shdfnd::atomicDecrement( &dtt.mRefCount ) ) + { + gpuGroupStart |= dispatchTask( row.mTaskID, gpuGroupStart ); + } + + depRow = row.mNextDep; + } + + shdfnd::atomicDecrement( &mPendingTasks ); + return gpuGroupStart; +} + +/* + * Submit a ready task to its appropriate dispatcher. + */ +bool PxTaskMgr::dispatchTask( PxTaskID taskID, bool gpuGroupStart ) +{ + LOCK(); // todo: reader lock necessary? + PxTaskTableRow& tt = mTaskTable[ taskID ]; + + // prevent re-submission + if( tt.mType == PxTaskType::TT_COMPLETED ) + { + mErrorCallback.reportError(PxErrorCode::eDEBUG_WARNING, "PxTask dispatched twice", __FILE__, __LINE__); + return false; + } + + switch ( tt.mType ) + { + case PxTaskType::TT_CPU: + mCpuDispatcher->submitTask( *tt.mTask ); + break; + + case PxTaskType::TT_GPU: +#if PX_WINDOWS_FAMILY + if( mGpuDispatcher ) + { + if( !gpuGroupStart ) + { + mGpuDispatcher->startGroup(); + } + mGpuDispatcher->submitTask( *tt.mTask ); + gpuGroupStart = true; + } + else +#endif + { + mErrorCallback.reportError(PxErrorCode::eDEBUG_WARNING, "No GPU dispatcher", __FILE__, __LINE__); + } + break; + + case PxTaskType::TT_NOT_PRESENT: + /* No task registered with this taskID, resolve its dependencies */ + PX_ASSERT(!tt.mTask); + //shdfnd::getFoundation().error(PX_INFO, "unregistered task resolved"); + gpuGroupStart |= resolveRow( taskID, gpuGroupStart ); + break; + case PxTaskType::TT_COMPLETED: + default: + mErrorCallback.reportError(PxErrorCode::eDEBUG_WARNING, "Unknown task type", __FILE__, __LINE__); + gpuGroupStart |= resolveRow( taskID, gpuGroupStart ); + break; + } + + tt.mType = PxTaskType::TT_COMPLETED; + return gpuGroupStart; +} + +}// end physx namespace diff --git a/src/PhysX/physx/tools/binarymetadata/android/android.metaData b/src/PhysX/physx/tools/binarymetadata/android/android.metaData new file mode 100644 index 0000000000000000000000000000000000000000..32e15b966c5b4ca5758c6cc338bd1e66da83675d GIT binary patch literal 44148 zcmd6w51eI3Rp;;I4@L+uAWDRQ-8h7SjBHO5!#EDuGt)Ck!_4Gmx@R&maWilFy_tT= z`eQ)Ib?e^uIyty+ad(zw7m%FjWcQ)D!&4kLhujMtnf$N{ zk0-l|KNa0|iu#vtz_-J9RMdY7>=8soE{@7=3g@c7FPzI}%MV$Z z_%$T4GCmKxsD!_ad~1I*=O)(9W8J>=A10~qDf<`6|4O#+rpUfaI5#o>X?&X@Q+>32 zD<|F|iKX}o`PTnSIX5vq^3N+mrpC|mt(^3K4@oSg|9iC0N>o`GTi!SHY?xkxP+5ug^SpJE35VQxo|O=Wc4_8vW<-Gq{ne3 zmf|la^Q%g@+LwLCt({|2zJK$9Q{DAfM~Stg__#h?l*QKqyez(sY@lE9)eZV%|HuZ3 z_-VLU8Gkuk{w(AF5ng8BIhe+vDAFalX9NC|@YMJ#)+DO`vn1(Djj!5&7fIur;=ci_ z(y#mLM}q!T|34PhPpyv{-%n9Sk{VyFpPz+`X+DIaoO$`DNWaCjh?SFXpAhC+Y@d}C z{~CCif5e{+U+15oQ{4O`9j2$ox$J!o<)%3nMdc(f3VQt2_|y3HaMM3C#(Dpw|JI;G zb*$f3RQ{KL#pM4Y?^E}?NK>%5zWGP?-9_3G>K5yhoxKMBT#l*r?I&SxCvAnN^lN>2 zH+&mhx{LZh0Fw`jB3+V)5={BVKL;zGMUdN z$-gQ8Tn^hoD)XP>Z#P{2OV!u>xE`+9(U{7{SZ;#JM@2FJjo{`#*=S;VHuB#OQvV*3 z^rh_6`ua0)*%otaNBkXd&Hpm~t8mS=6xaBC0RA+%Y&N?&%|+v*XAp|HJyK{{3g_%7;afF3FQPG5=bg^3BeT{QDd#Nk>sE>k3XxzuBy^U#foj z{~B~ipOV$%)X5<-)kaY)>lRL|eT|`s>Dj1%8?`UKLK|iAvj&q5MX{{caANi;hD^Rx z`*i=h4;|8{{#iZK^EQCmD2iqM8Yk9%YJDp5Tz@zb*BGpmh$}|&dR8#v^3l2_AmZx7 zx)6@I*64L6L|kjkh1RYeBd$53`|E@R{t*;7r>%~Hn1^yvBa zvS6RqD9g8UiqCCuu~dAj|2yGZ;qsByx3yvpc7(L8gscCj;9EFO^xqVa--lnuQMye( zr@5@b^$sV+HU4+QXW;U2vHp+1%s;Z<#LCG&J>N(s?DJV}{C*rAVkxfvn|}GwB-Z~^ zl#!(BtNov^(64&G443Unrq|pzO`hUM*D>ePiTHnmYiv_o{&|pOv0KzHC4T}}?8o)3 z{ZGTJ|Ec}E{PWKwNh<#I{PzX8SW3U@Nx%7Dc1m=#^O!prF&lsLkJX9mU$|~@K{v)& zS?M?b74<)vy5h=zl|hcSj)?0SOcHbRv$&q2WM7JFem{dUvSpTJ^*D7h&F2<)jUy(n z;~Jl>a6Ko*+{RDq*NfoC3Y<$P-^=HV;Wu;KLMpbuU%E+au#|pVKVKB;noo69()uI+ zT^%?FCss}u;#YB044CNshsl@1H`4!Tc@1OKf0Nk0viMSYn?D*Kn?K5*A;|~Y->ZL| zW(|D=vG@|V_=>rWleooKiYtEVjGx+4yH+nWc8S^mLsb2 zajwmtw;`u^WqomHe@g#xufK|KR9{>o`z<~!PK*7s@hx&|-^RC$+xUJtT(+71RDE$< zKV@%fd@p5e)jyT(>m$gfa8&$XrkAIP##1_C5x*QRR>o)GQx*K1;WHI{Cw#Voe;d43 z!sVYi`0)}x53~NOjEVJM-+x_48HxI5`7VPG@o$HVmGLHgs)7&TGbLR1zY;#nF&;lI zorvEJxA-gCC;nq_i@y|q6Wrn_#eWfALr+=zTK^VHxZ>+qNoDJg#h>Y4LytthTqC`Y zx;8!^;o5}TjuUa+gCsGxerS9@O6tRplPsT8Cw~^{*BWMG<)+Ed{#TOfzt+D`qeCp_ z@%sAr@QVwaOD9@i{{^o8rs|7dfHGSb%&u7fQ@GG|vOd84CmT#mkJjhQf->!YEZ@rM z`|llau@qPRdAQn6an-*GPKYGA>bK$iOLEo!ZlJ!$PwD?5nDtLSG_ihM0=MT=_22TX z+%zY8ekdECH*TO`i$KN$3<##i=zj3lQ1TRqdG z{{0oa7(X$87+%~z8CU=Q6+Xo=)j#oz`Eqev3D^7eZ-C1WvP~%#@fX6sh0HRpo6}B` z#h|ON3&h6X`e!kqqtdsa+v4M0su0B14{=*RVs3V7eUSef)VqXa`KCwXH;;}x$cM86P^Hlxk!K%hb-w&Ao8N6n>Fd~1AmVdH@k`^EMCsaC?}zn4;fv4S6gA1mR~e;j^u3Ag?4 z@dCH;v-xTEsh<)Zl{)B7iN;ED#|He>8}J{2+xTgHHQ5wS?t{NI=!oOP>{tKa4i}5L z>5=`vLsEI^G`+@k{3x7ZF6Ezq^DoK&0Z?VXxcVqb_3x3W{){i?KFv;X>5!!Olb(=e z&m?KArbt$gQzznP-&Dxc!OH3PQ`3}@XpOOam%+z2nD)2Eid^MhOqpkK6f1Jg-2?Dv zb4<2tLKpZ1g4kr{>qo;k!7d;zRfE*TH8wYJ5yD zr%qJ=K6u&uR{dXq*Ep8de=q!Ic&dHr|0w);1^*j(qk=yIU#{R!#ISY=m;IN*`y9*c zzZ`I9NqyP>BKTSbpNGGyf**t5Rl%3xcUSNc{9Fm2CjURcUt7W72!BHfSN#4W{2q>} z_!a*(_>Y(H_rny!MNuqBG`~_}{12#a^W%Z2pv9@Utq&=#_HBK*m-<%ERDA;eccizG zp*D@%`uYW!d{`8-Z}StY@6S_kvwtd5V(ln?zKJrDxP71FfB*GjxL6rK0GGWfF8j3q zpc~2YQ#;2f^CXV4$?Q_TNgJ+lp5O`wWCVLTX*1_>`{uuC@?Q9<96vU1g^QK(UxJtUNBVymF8`$JzZYiq9jAFd+;KxbD`tr}s)L-M6@{jm3{8bhFEc~t# z{u-EkR21v~eQ@jlY*fhXRDJCaBq{$&_b*XKY=Y|o(f#K);c8DYXnHwyBL4eu`7Xu( z0`_@QF@9|RosR)eCrwd5_Mh=7*o#P8O88#bjijw5e46|Qd|QEY=|tnxh0kz|+xOJ3 zZ)VRhL=4by&0zc@vcIB>)-uw8$b2Y#QgjrcuHi8ME!dxlu6kq{&~1q8Q;v{ z$(|IKYV8j-wiEHewM72gN_~xgs=m1P7iH_Kxb_#9RMgl0q8L9EcOw7j{xQXse5Mr3 zE(|mOZi|4GI|#3Oe$n`7{;O|WNLJ5OO>?5(mlosC_;*C@+n9@)9`%1H=OJbOI|CO> z@jD~^L5wnD#`tPaGFZ=F;m&J$PkG&Pn5GDQp9+>%m#*7MOW$4G?DN$b~|GEL! z{cZD8T+!tHP5hZ~#g9^Cf~%zQc`jV@cZL*}O2_me|89dXbAFsu`yu!goPX==SA6^^e7b~FCHqfywe`*!{AF01s!5_zu-;P!AuZQ1U!LNiLui)3f8zo%h zb0d72Bma`)cM{$z;qucEerpMrf4&RehL`#8_3(ZL|1a=S1%E4iwSxZ&{LTvge)w7i z|3mnzO88&EEIwaL852AGJiO}uqWHD@i{jVnnI7GLp2&|M?*_#FvwO)iVJ{+`E8)9g z)21vsN|)q1xY@7xH!(YIgf~c%RQu9>8(b{rDla(;dpW6i|1kT%A6C`AxNMZ9?0ek? z^~I&5to~0$^=ExC_i6nXmk!CS`}7~i_5SaExYm{#lI2^`_eT0RN6=2iO@E5({lN!< z{s$8Jb$|PP_(wRV?r&P({tEtCj`E+i%c&FbFTi#GNpa2Jr?7dufQqT{orYfqzo>%m zgkN03Uk0=BzZYF56n7$S<9~0+)4{l&|Bg~d{y!JxtBmA3BK^xg%Y9mXanqmTdcNvV zM!uOPSv{wO5Bcvb`~b(4|HSWtA1mQ{K6?Ya0Z;9(?}M3tW+`K0c8Ht(vj#aD--Tne ze#o|MQN9(m@!dg2YJ62r~%0KA1S@yS10nhHQ`Rx3&&P?a!!Q5Nlud zEs-QsB8nzL`3W6D11*Y|f@;3@l5?>5S8EpRTKh~ExZY{&II zWq!is-zhR=pOvdgA@ZGp>)d>zbCs3I{_mlTB-U?b<-hMEiKY0DM*3%^B#3+=5x4I@ zXI+l}u=d6E{iotT#r6A(`zRxOQtj*g?pxtga3zXQ$o(*jzu5?yed4)&5w}xu`Ow*C znNd3%+4o!2{X>%GkIj+Tz-cc25^m!bHQ|cs{`ncWVpsRiBDeU~{I&R&pImv48sCSh zUzBqp<6mIrpMF%x{3CAuiMiP!ZvIK}$J#&Izg+lrzQ42BZ+2N%9<6_#itd!i$CBs4 z&3_;9S?<&PCvN_Wx!Eah@t@)w`S0IRe;28^KHL1&{zChQDXhAeWbJb5#OD74T&3pM z<>c>0|N9D@OD7w}*G<$l|J-SnIGTUN%|9_WJJr8sQrY|$?~^n?Q}%2Bqy5ch_%g}# za_U6u`zrNUxe`n9SHRE0WV6}rHZxqm23FNS@v8p)1biAjMX~tWX#7=Q_s?SgO`rBp zzd{-Hf0k72--EDEk!rB11b+nfgs1!OXH)*w_+JViaV+Ci>yP@rmHP63T%SuPyI|(u zwa9oI2XXUn%&n-ntxqYwk$)fS^;_|A9X8HlaNK{Jzeiv#(p(8w|IU!EE#Vs9m%|Uh zr|>9;37O{V)$leTZr=)tzZTvv;g5EGRsU>!-$5HDHooFEzA?9=+F!qkGV<5GWLm!I zk$>J!`WZmnKl88n1EilT;hJ9`hQF=AO~3qW_D@+RN3%ovZGWjbC>tc!zSbY@FU3^H z>Zy!G^Go|*UZW(}AL-Zr5;r9KH%-3wm)d*8{pZq&xb~NNc1UsQSO3gDD|c+Gudn%G z{Z|_%)(`#nY5oKq67|{gT?QZGpMZ;{_%!+d05{(i>puc}8V#5AU+b6Ir#+U5#h19n zSIn&)Jzrghe%YqJTfXU$ecRz%IHvsft+2hMtpzT95^>pQ-1HoSsn7a;(d?Ig6SMzi zp}z8K205CYE1VypjCAcF=@~p0{r7QhgiBP$>bVR)PQuj3qL?jrz|B6@H!(eW{(iLI z&wL+k$X_Y{%0ByjMT);RvQIH$65FTWZ%9)0W&c~yAy&rKzcTw&U+>S$?$6@yM!$TQ z(*ME8e#M`O*)Og>N>W_<{}f)FAFcu);(rO3id6fb0oeF%u}U1ReQ|3)#ijp?l&Na} zqOUJ+U-h2~w{^$bHB-bdg->xz*?$?#>{lO6OpmzPpW@R0EtDy<|Jx${DgTR`{uGz~ zqZRtaZ-&dZRR2zF(7yO%+5R0-`-*WBTc5;jeT}*Kf1~}^E2+;bxOF05NM0Ycf6iyQ zPtz~1HByq|^6#4|Q{$NOulW7&#R~r0@M9%h{{JZHX1K=R?Bdjk^#3V*m19}^{|vvg zgv&oqc}AA4!L_y*^nUU#1kvmWp-1U(x>L3zS(7-1KtkMEi@!^G9rJT&30*@u$P@so-1S z?<(QCe{F~VK?&FTwhR7n3D^Gc8u-N+zJ)SH`}O>ABYXyM9A5nWj_x0~z|X-`{nPi4 z9k`y~b&o3Q7k>r(T_t=P{v+`B!8NAE`r>bdzrTXN3I3rHuKE8~_(v+Z?*D&K!FB)t z;}Wj-2fvE`Kjp~3b@q$@F8t3b_+P^Ryn_D&{NpA3dCyH;yXP;wg{>Xmae@5`s{byfff9n1tZt)Xy zvtQieC&mA*e);DilJuwg_t%kr-QP{j zPQ5=kPk;Q0#P!XW)8tT&8sybj;ZRT=NY_muEy@?Qa;bF+k=$<#J9l3%J|jrviYO>`{9b+viddn<_dl@{Am^ZR`?|q z{FQK~acTRng-=!RH^aA-@M-cjzFRq_#`j&+-N^r+lw*)G|9lV~Vr5+aeZ?yO|99$F z`TvjLRsR1ZyvqNdgID?gqHn17|5M>p75%#uzNLacA1?ow`A6fkt;lsVL9V2(jnAyh z@gMR0*hd*jnf@AFtc>3TuNt2@310>lHi{{V#lxGhBid^G&J$y|H zNk#q=vX6-~-quI5auGj0#U7@DpMyVC!G9LMnZBj;%fG({pM`Jcyl9{JhvBsnuD_rC z1pGLBHd|kxtnBZnvg}^CbQkq2J~Te}!4+d1ywD%X7pKPK!)Y!yKCq{ZKwFP@T&N~30@Wdr{U(iqJEI;pVp^T|GtL;7Jt%VVtQ2n9(^FG{t4a3M^*oR z4qnzj)tCR*^)LAUehQd^xpxWt>CYR&sFeu z!mq918ovWYejXW!t#4DD%l<9lXk%&dw=LwGpNxN)wr%_$*hHZqcB=mWPs&I(lhj5m z;{OXSR>m*-Mjsz#{8{jsiuya?vn8DDtOh@>BStI5BKunK8pl-o1K8c9w^2`hv+5RK z_rl&u(r>R){5N5LOw#++6xZ{`r{M45$iG}ar(U*<{Xeoijw&nB{oxYINaQ265sUaY z!o^Zt{eM3EILB1~W#4vqA1>WWvAhIk@$p#McLQx4r_ui#`BSCk|eS} z&v!Za7xw)mu`>P>@K2ZUMe^SXzZu1{zo=jFW&T@>;Q5r5i0l23B<5CB{nPiq7r@m& z%Xby{(D%pseQzJ2GR7@__3ZXObc{;)G6iVN!>iig4OiQ#`m$g5uXXoN zHHe7o{$;Vd$>oke#MM8BD7im~*I%`7?bLz_^S_w+U*|J{>-~?$M|pAmWB;GI`OoYx zw*Of1cz(tGH~%UAEq>1hpXgxoPuzZ=6LT9!?T-$G_>fM^H&u$yn@H;KRD$0M)88ko z!epP-mp>$^&(2cjPL44bGr1eS#)axyePcT6`Rf&drTEWK|CMkpGhwO8C;HFB%s=Kc ziskL-{_y~1B(tQ=I*DYO^AEyjIi~CvSNzX$RBV{vZK3&N>dJOZP35k)*~?{nPsiv6TJdZR%^CFXO9ljei+`CH&?J{-5B-EBLR&8x>sd zAC@clXQYU-N(n|+r1jO~9ZWh5#46km&Qu@sm6C*ZbznO^BLk$txANr#hQ-|5J{ zJSZt(der|{2m97IpCZK~{yO*?N2MuJEI$$H-yGT$H~pF)_Xqv`DBpBw{ND=~)A(CG z26ZEzbO zYnN;%;yd8#TUq;i;ktjP_%!)ie=jNF(y#bf_x?AG)j@Qde`JG+*{S+%%1Bb}i=T#z zmGM`?FRI`_059{;8)0VOY$CqI6@!u#*Z9AaGK$S9%2>ZtUZVcLn=}JY#n*!{Ykwxc zd5rHP;`)w45_4-u_I;f6FkJRozN^5;-$wfN4$Z_)#Z7ujo%tv@s#2gAByc1|KZ5KsSxuN&F7K0tuHCA{@MDX{#n1I zL-LkLe>{I}{)ku2Z?$jpTkRM1Yu!ID)Bok{eE0tfxxoDw|2Jj&@0wqj>96M>m+5cB zzsmik^hNH!2k_OI{_6Mfnfu#P_ZPGmXZky{CuaI6arHY|`&bFWhuK z=jUzGZ+$jx(l64pP5RyPrt`G=oqwLzsO&tg1m~Tn{=A1&TRpNPTUxFUIGf+0%k5WZ z!{y#!qzul+S4hyH{7%(dN>XJhYt6?(i2^WoJ0Ke#OB(y_Ifb zcxBdUbz7a4PS&Zfx?^p1aZv9LPxc0#>~^*kwbg_5(Un(bOTG3=r#lml+r#mSY;R}h z&V_z`)T*~>Z`2#?&qlp|)~`2avJ<`0sMpc?_H1{%-CL@|vxVh)qjzRcYjm*Q&pPho zhTfpD7lTGwXI!pRt2Y|0?x`#kt&IkY^%L!8cDmPUWSs>N7ad>kM6cJ*I@g`)4i7dv zy}{Y6vuEYx$>!k5u-TvwwRXMR%(`sBYpZ*E-Qj3Z$8Gc7(+BIr;lffKMeSC%Ss%<~ z_3o*awoZJ;cFWjK)jjoLbAP*jYRLKG*?u!*$%yXKpgC%~%+9#j;pRzapR>DTS5!OL z94`BIhpzQOZ-ftf-3)Ix2RL)S+h{E{hYO>@%F>82c2`+ik@qly-ss9IID(t z?%W3_6KhU4T@!}A(cbgxY1Rpy86(EmbYvZD^$KWHBeH+c>-Y>`;o6mUd#*mJXUleS zs=uxM6?ZcSj5 zchR-Y)~V&u{#LWyFh=uHZniuw5zhT+4ti(&iJve2JSyznR-(xnJ<*P}o?GSu*>bUI z^4eR!&|hv2nt7o?z0q1xmkaqJ8tVPwN_$*-x!ykM&7;QN0`yh(^zyLFRD9p+sM#H< zg_vBkvU8`|aHOlyp6}xRZoPfoiCbAIhJJ_+d+R!;M6p!swa=dFb$tOpbvs9yJUg<^ zp%X`&1KR2wY;~Cl%~e*6-Fx;h12LgFxbCDTbk+$PhO8Z@e2gBhpYi(E=Nvk*uW_nL ztwSf&x*E{b({-+gY3Gg4j0WW`!oInijHR_TR$r{7k^{N&Jy-Hls= z#lB)ooXq+X278iQPJ1hW<|4T ze_d-cR!>}bZ(3(<5@F!j2T>+NG^lfZ$~U9I-Pa$s+6--24p`0o46{Y5L%Yqx&5l<3 z(CH=Tfol%$xn(qH<>kWlx-uun^;Klg(JQZ595kERVSU!>r?>&Mx&%S1OW#`EJ9Ho8 zvcAe?ef5s4KN<{uVcg_yc?3>=TBj&Cfw@+laO2L|u`@Ss2gDIAEVS;Rl|w6?!@b@} z8H$XR&VlCXW}DMRR_^wpUPIaLJQVQBVVI&)693gFrPx5JjYK8x}_r9*A-FDM3F1j6;VzovgfGO z)apyOH5=Dw2e_NJy+>{GWoE&_`l)Vfw9;s1dyZb^hRcPWn?f4UtTR#j(23x_d1|Q* zQ+VBg!P?tvo;;bWCacc<&1G?KAB61!H~CKP7T@al4#IE;GbYwnKS{&tt;MS~+TCsJ zIXl9>aTqsOXC^PT4~W)pzo1WMv3IJE!reG3>bk}aVi-10!;FgqtxjvC41dx+vDa<1 zhS@MICjLgO6>4#LKmhgH4R(0igJ_TD_Hs7s-1aD}ioSOX%rS;Rt7TZ!nWDjba(j)| z$+La~Ra@Qdc5h*KGuLA_)w@g0YX-d)?E?3R;2oH>r{)qAgXn>pk2|DyR5Aa$BYdZB~J7j zXRl!Ih)3A^s^T7U{D^es8zWs^;hN5dz4qzmV4l^nV( zTB{DY{b!WtTbd&bvxTfbj{gw8xnGM^`W`AtKNtVt# zqG&o(zl|=k`)&?`pKge9*iw4faPyZQaW5ZgCn~2+%-B(-f0<(gKo3QG4`(p zHFq{4qXBOEIX`iwEo0{dE+Iz88(Rx>uh*{Jz%|Zx>z&rp{z1zbm!^6u=rqAu}^9uKQB_TOv zztqRIgerH=@KALs?AW!#R(ynQ1luze4#69ryN4QEkQ!|3{qv3GxJ}TwA(p}B;A~`` zCMFLw>Uo?i2iOi~u{Rik>uuYNv0UVNwfbmjIRLkjANq$UZz+)$u3b0nnQi=p(AnMZ zvvf4}_qg4$5cn&!-@Xza4D$Nju6uxVPgCJEZm>4!Y2V0AIh@3B!Oid5pvgufI**GS zZ4FoIZFe(|PBn!74(})6o;@LZo>M-yo51TBJdXIUV%g$!PIj$wI? zvb^tFVM{qTXq|56OVcoGvY*yIxtPx)5xUM`MdgRhe%&u?i@i1r94l59x8@9pGg4Oi z+fTc9CR^s&XI&?pEljG`$&=kCJM2bSmaxe?t2NxcwA57dZjKe5@X{W=l_7dIp0F+_ zbFINBEZ&peI@t3|4!vXruJur`wA^Yp+*w=qPwbHohuNq;IMp20!trP}Xe#ngH{DAQ z&3kEzm-Aa?SC|Kl-pTm}`!Cm=mSks|t;ku!?92LW(>mx~a!zCK;k3A0T4}${wBE4g z9ONy{l@HO$qG=(ahvzms7)H{fA<7z_p{#2=EcA0NmOB;Z^AtYf{lP*$RO39md5r60 z`{)dnts~Bjt|Q~@L>t#H$buWyoIm~c@hNX@+{L2wUaMGADR*^%=*lltE)L2(4%X)? zUORDF7oL+r;;Osga{F}GhlQmRI)7{W&i=KU~m#USZ@K^be4JsqsD54F>bv!R3q>F1P50m6+=8-C4e9+htg_J9`Gb z+nQZz4xWjYwsGZnz#=Q+Y&U3G#~++U`390f7@v10F7kYKquS2$kge?{Gk2lc=6zec ztv&CdKE1O$N*JpsZ}(tO*T}e@Qq$cRc;8WX+qy%2KiZu`^$sq}HThbH`nDa&R(J;S z+m}4+P~SzvoZpaOhjaMBdY4B>9w{fU!fssw!s_bQfKcC}DGsee{rPaua9X@qLj$%2 z4u*%rzSX7n%8(V1M;n*rcl*u}`EWbHV~#s+=aG2m)?z*W=I{IV=mjfoiHCY}25M@B zsn%iV*?!^Vl%1?gtlt)fV0~G^p&GNN2~6?|iy3}+JltDfT5j^4Z#>%;M$;W`=Rcnyj zu!Sw~?ONq!%6ubRxP9l&+F+&2gk~k;E$|BaNH#ykoXAW0OkiX*CBl#nGiRP_W9f<$ z+-0Ko-nG@at3B2&)Q8k$o*p!pn(h`skh!w%f^IT9*sBbA5?kT+Gk(Na@_XC-z7$tu zGBvZatZTd(zQZ44TswTUmo>h)^1>N;d0oA}_eP$sx83G4n3{uHy*0>I z`hBJ$8y!AFU5wBx|IjJQgnW*No2PjmA2ppS@0^J&=Tmpr%vWi5AtW9rso^ z&vX5m%)$#cj#0IVEFZnCu4QWTl%J3CO{rx$HBx)E-`a&$!{ShHfEr;(<6YQxK62;B za9}>5H^vw2LGrCkHs0c7eIHP6%My)<-!kGezaMj>2e+vJ9kqw49F zYX%dX>SosA?UeP`x2l{&OWWgrl}$8cWFHlfj= zOy;>!nas-ew^o~eopW@2iN*!@tm|);tUv66uD&Xp$n?*e-JLtRo3&2z1i6o0)7h-& z--M6!jo1Q|b&ppbys+nM2<=Ll&C1@lI+@)&ajRccU7c`a%WG-hHON)m*6MmCb_2}I z%{R0L9BnSQmfFoZtzq1GeL&(_ZR&ROEY&;3>v!+(-7BM>rXyE69O?7aIMmmK?sBeo zrkiK%T^Z<+L&bO#>z*zA`M&NchH=<$S~a4J$K=J9b_;5NZ$jcMZFVP(9ADnB+;lIJ zCv49PFRai?SNAmErv>+*zzw0ub{6P)K9y2d%pMilT;Dg%cY}iyjy*0_4RgN{`r!$C#t*3 ztJ_&aR)%`-Tw3G`UqtVsL*)A?x{61o5GQ%u5a$+R@(P!VDaHAj`k=8$XPa&Id%Rj9 zTmp5sFW%ME#*fDkRZkPwfW9XfH2uSLD6Qx^(5vgI+VxEuqP&IM<5yBnN!T9im2>Es z>PSail1R^|MR7@pgK>GUD9lnQm^YXLu@~DB^Og zKLk}+|yf8Su>o_~G3_#GkvxpdoExuRIz-^hZ? z*Y#_>e7Ls5Wv(m?d@)uvXnlxe14^yr0)^L0?|{hp7A z51YxT8D5oNC$!=hc2{n?a_Z~nlW|fVBdKqX(5CMYG`9|Mztvu%>kIO2Kd%jKCw-Xr zoILE>cP4sFo(bP29q9FLTj~4XDY=zc&m1&==!ClEFZAsJyD+_Sh>GvZyXrR=D!+4Q z^Wg4dw;bAY%f3U`%pcl!%i?v1uRpTymc2&~AKgdZ;^Fyy3ojY_b}x@AOUt~7T^ZDu z&Q5%F62^I~TQ^g^glCU)TF;fd_Kxns*LCZhktu>&@rJm>xGST ztD?9)#)o7>q9q; J&2#x={2xrOFdP5? literal 0 HcmV?d00001 diff --git a/src/PhysX/physx/tools/binarymetadata/ios/ios64.metaData b/src/PhysX/physx/tools/binarymetadata/ios/ios64.metaData new file mode 100644 index 0000000000000000000000000000000000000000..d2214a55b586efe4c8d4a01e13b78c7220200e69 GIT binary patch literal 52514 zcmdU&3!G$EUFYu%2?mHbK!C^sR)>fqM(jzlFpdysdLEgEc~qt+lL4Zcn(mtEO6t+6 zs-Ee|Mrl_9ao5$r%6_5-odsOkh!PebQDhAQE>Ra z^|@UwWU-|_fA9Z){{Qno=bn43G927{bXQRn7vU4iCHT8**9)$#;P|9LYf)Sr zq-*~edAvlExGxj;)U~QYi}n4h4RTZBzG{NLuh}5CP2Bbg`mU4wA5n~6>I#mVRJXpp ziHts4?q+k>tAWgV+^jf`SB`tBwESIlFgBp4z?A zN^rDZ>-!IhET_-h>&2D%Ywiu=7~`D2ACf%nUqx*^jQ8)sM3&R%eEwN+WODiJ{=HjV zS^TW;{o*)gIeot>dD(&2u>58`6sfrhEH^-w zi;M4XUn!0ZyPjNzv!b|44cm0&{gc%)?!^*3p@eh)?1wqqKVFG`%}S3ssLT56JiQ_5 zkjIYKxHwPe#Fgd!jT^LUeeJ|&gkM+3Mh%58s9?3n_`7XPj3b5zmh2 z^2+z4PlzMKd6`_sON&os?B?dL$Lk4NM|dq`Xnm}+n0t!2vN)T2wzxHMR{K;`TU?>@ zHF>3W>r3;1wr#(PI{n%@rdR3*2j}05#SH{w6>~R-VwcZu7IHZtbOq~%_5Uya7x{o}t%{-BQ2;Bt1oKEGQWp9gYw zKOni|nD&Kr$N3}T#^pXY_Ze~NcyR1p?DwNR9`2vxT!F!yzwU?QJcWi_oS&+N>q;Ft zZn-#bll(dz%f`d=<9cx%kDNZQ2RDh^36Ar|aYHqfn&ghR+kNzS+#xO<588II-i?mO z_ekFxb)*kDKYV}tF>&;PeX>2eP|8%+W!M^j!;>d7JtY-kK z^5x>5ET)XRLflhIIPZs2KlUfatk>_au1fsiJ;ZWf;(2tnI5N3;AmoEJrnzDIK3DZV~5<%Q)ve zc)qwLFqb48>nvXHUo36~Z8?2+1-dA9i@Ou&SK>2n_kiRt*YP}tuJa~WK6IUtWoG+{5 zwt}Od*2g-F*R_8qj&aCw| zxLx1h9u#*1OwR5U+F5>4+#>4hagT^wD&ZWz|0Qm90{0i~E>v6VdKQxQ*%~=VH5z z1LIu5C-{qFE-oj~p5w;*x41!W>7T*j(lznR=_5COKUzT>{n$hy3hOLaD4!e`wo#{x zdC%Xg#8b|AF?MM&w@nBe;C%l$TEaQ+-y?mzA8llQ*zPKL7LLs-KEEVAX&$75>wSbd?-Ow8ND??7 zoL3)~-t)jtjG{Ozia*NOaMl(@@F^!dK^_u?ug z`pi9^#!$B6v!30ji(~wA+_NQ5$Aj-TTpSnQ2d@%OdG2}IzjM1zoS*Zo$9k>rusFte zJ$*HC+$Q8W>+6c+A;-CYuM)TBdro!m^^&J?VGLdDkK>oVN6Nh)avu13noKUQ-n2ox z&cC;bXTNfGx2wZHC9cfwFJ|oS*R?#@U+3Qk6T7Th?q>UOK@B`Ge&$dO#c}yFab@$v z+^5BD#nrj^IWEQHqW-IJ$i>Cnlf^MkInLjkexM??N@IJy^x}K(-{qZ{K@jVY6)A(49YADW^!?OP{TKD5K z&K=M6kK^WI-t)34p7PxM?Mc3>qZ#Y5UUT0qj`tNZWt^`YKf13!AU$4Z%DA_Sn;Hjn zZhgNjE*&?<&&B;5f4-!gxnQ|@+xuPVb-t8w7sUB_XPoo?^kd>GFvPx>aepE1$_dDEqma^WHJvFa6Fh(q^ z8{}TQLGC_r?jLQqZ>-y&`^CK@(VO$v@q17lncVo=--pHd9%y^Cot6)at7{(lyU8~oaZc8OYY|~Gvi#O@xEO=f3HGjobx($MBKB%Ew-cOmHK*@>aR(;o`w4N?3HshA z?&cDGUjIHU?!*M{FT^cO;4Y?y;$pi`5!Wo?9KS2XErDazf=lwO}iBN~-zQo*a zab(uxj)*I}|Jo1lPZ^$EzS#aZOJCW2)m&E`<3k??eo#5e9g^Rr;}*2##$zhbMe$m3 zWzQAoB~Rl_oi4U-eLpIm@?8APy+d4CoX!2TxUx8#`#EupbI$JjBv1WVWhYfO>FE30 zuZw5%Gx4!p*x+|_9~QS2f@+3yUj2zU>dN)+uWXMAfKtJSHplIvDU?gaP2nuZdHy^{ z+!f$*`8Ti|)vo!=zOrnSo-}@(>n`r6^W_`FQ_g&`+`PF1;>hGUkDJ$_3b<8##`QVQ zC#CN^m|UFAwZ*-80{2RBZz85VQDEskT6<31+&Kj>Hims?*Rrw%?#N6wEN_gu+etmAYEH!Jxq zI&LrFrqIrEtGJ!0ua^gXaeF3kuNJp|0{6Y*4wP_jlibgFRdBhuyh~h~FIR$f@%a6^ zIJb`<={t+#@{xqk#l_q|h$FKe_cSI73Vq0NHs|LQ=F~`@@?sRv%jZiU4>^72yzlhy zxg3w6EYatEXW9MD`n>NfTb~_g-_I+sL7U@tXVm|6+@`^~n4c5pJfM8NJn;HQ=1P35 zhu5)8#Y5r$?&&I==i)b?=^u5vSg+%9PCVr~edfMJ9GM*VZ!>li)dPR5@7_e1(`W9D z6YQG1UtC$9`aSI|vlOBsvx9TUg@o;sH=QxPw26?bU~C#*OlZVNd2Fz!bo`M2o!v=VNc6uwH_GfV6` zUtTY+0uKLMpat#~Z{gP2k=lu0Mf$hq#pq z+(Y8-oWT8>xYY^V?}~d>3HLF{(|L3rtc$DvAa2rq-}5cK?{mIckM(+=_!Rz!Gbr}} z;62V9>nu}}zgWk6OStPLpNf~Q`Rn`9jp9;&x!$|j{w?Be)sgaS|8(K);>hIk>Xnk; zr6bptoW6S`pVU8d_J`fb`SJZ5)Mw6m%k;f9qpwO>#ql#|y%gs-fB*3=>8*g9#>aY4 z4ds0qyOjiT)m&;f$N7EY2NJsv!IJevt3}~`$#09h06}hF;`_p%iTfvbt{)S>bZl}1i`}qx$r}_7=Z14c}p_ohaFUR@)%gxeT z!PO7oV?99`{>*3W-eY0Hn@jEHIKQ{(NiXNd@kEbRdpuq#ZV7_ic$oVxad(z*eh=~k z;_e2Qdv3g6@-)s>cM3?V4}=bbE&^M&iAc9mtOjN9!jjo{rgnLF5}{2-dt)o$6fT)c;yxy zX*aiyPszZu#8q&fi?g}siMz6d^ShIq<%;=>rG#c}b!-?S6LoF5nK zBKxrm=Q%%Y*S}ZU4=(4&l=R)M{#9|l-uRvsceI4FU60#NaP-4`bt_I#zAY2K-1yqx z??~d8^TXru-QvjPxF5{erOz(T2XoE`%2SR$v6%Dkhd7UN9Pei=_sc$g&GpaUtGz?q zG`I>rwufpc@5=bGnn3o$T;q%(y!y2Cp=qStqqD!4-1^x3Ktejs=F&K) zocn1mjdPCsLdDtp-6uRPI(Y(zDn9OiY(-4R;!=U&pA+ZHWzYJ@k6tgoR$MwBTawOs zJj|uzk#fGoTsnVq+(yUaU#ovJIx_Eb^W6JR?ntu<0b1`o&jmtxZaJ2m&mcFS(mR6nj8_hrK z^L~i&%jJRh;}6Mx1zZ&$_Y2if9+CVLIxeAgJ3iyw#ZQm?lXQFwPIKey{JTON$74Ni zQl2_)&zC;tUe4bc$1aX;O^^tq=3 z$m<#Vz~c4U&pj2iQHS-Q8j9BqKleskEVGmZLsdFAJxYfCuW zbzCao=!5y=>5`8V|~u3znNV_fc_Va-9AB8*vqIIe#y{JZgT1 zj%D%ldY}4HjhBYMUQf-Xd7pCj$L}q!l3n^RjgRG4Z9iTpZYKmeKXyqztK)ufxjZmu zKg?P0aa_l~_;)wv&=mCX$YOtQkiC@4`MVr1D_$Xx9oE)a^q${{5uc6!_0Btm+|8=JAvc4AO5{4 zk(@sJ`*!IivmWQTl=)$OZ&Yy0{4n=!*(IK{`@xLAe1Gp^f6X~Ql;=3x{X^+x>~eO^ zeOw$5Iqs90{#{AqD(;`Tw0}9yb}#z!s9;k6o+z%Yf7W-oIL0LBhq)`nRZ6(4Bv0p& zLJ&G^f6b--<~ZB^M(Hi{cW=gSZhXz9c5|HV-aNssxs&3`;=H)Q{+auH_ir`RzjQpj zj+;yGTPf%9-RS=QZrSzk@j{X$f0&CbZ^-oTo+t``ZP%RdHx%YLkK5a%w+e2(ynmOt zqZ7Da6?eRZ^Z0&P+|A&af9^l3p}2p4C~gI<>-F!m;_fWr9OtJ$BkEtpA@{x26_Tg( zcPU;H{yGoLIUguYIs0SI`vm4nj`Kd&?{g~Pa{ChR52n@6t>CufGrm9g2Fds9c%np~ z^Thk&#S+eW<+zZ$2Ok#pa~hv5_}Gt0&*#U~uSt2|6lc3IQLOThisNj~{!qLsh6i)L z4?6#&QlnNT;W)=d^$587o41syJ{}Y=NxCd z{v9pznfI>o-`jd0yIKFB2o1MTSaSb-8J_taraH&o+s|X63+XL7m9nN zg!6r2M%*V$I6tQx6n7~cy%OxWzkZ&+Mcg!i6X3?<=lgkG9N&xP;^N=u^u)at9G@M> z?V5XyIKCsyaZ|XK~mS4 zuTZ}x?PIrR#&;FAUF?s!bbVemgrhmHlaJOuc6=T&&bfJRf4x8GgUjs?UYhZjI$b<2 z=F)slIs0oa&F38VFFl_0df-&I5Ck^1$2|dmeZ^(me2Zd@gy=SDf$C5yLi8IEF0$Uh=7Uy5_IBH)O_* z{c>?$So|Wz zdTBQom;bXtyMCW|3IE|CmDFF>Sv($37FT9>ynkiiLp)u2oTs!w*2VF*-L!u>f6cjn zWOCei|H}NB&h+nuJBOqD=k;n|62G00r@6(;ypMR9I5N~>J*b9a?zFgFXkCvxBW|XI zn~G|S*NEGL^ITlaeUG^PC7k^|FYdqu?nlKzK9-{z%I0UsV`_I=G9Ju3e$Nn3S#CVcJx?5& z^|%*_E6Zo=J0PxXUYa{5j(NPE-IL-jo4_rJyLU0e>)HK2ag_<&+r(X2!cC!_ z#d)K^9p6Q)=kMj>Dqz>+t`K+S1n%p^aeUX)=RCM-oYVU9_0p5(!A`I)*5hj8Cgs7a#ZAhC9}>qr;Mk47NAmpe?>i^O?`NcMP5hvn#p}x-i{l!(p1)sG z;Z?)mH95Yqb*RtdwjBb-X8gYJyh^+x9k&O3Gl0l?y^ha_=T#5k!#<|P>%rklY<&m^ ze#SZH%S{ysQC4XPpKglW_M>9OXroQb$*`EaMx6Ur#vKwjY5vy4O`5-Vit|{E+m(0_7p~*E zxO|7eG%nQXV!hV)Mpppj;$rdrs!WQ@e-l?07whvpUlSLN@4E!1ew?rqIBrtidGLr8 zqrH;Ig%w^m{!|&4~bE5c>PRVAFq}ULtO13Fo|D5Vxm|(ipSUaQh|`% zR(u#o>SytInDclz-sV1}ewpL7m4VS=-Z*~$TRgcf_|Rrr%>Au6GV5_qd``T&GJ$)J zxakSpb>eoGaA*$Yn79)-Tf*evCoT4)Bd!Wz&W{zzU#BC#$KiNb9qTOrMe=v*$nR%z z+%HT1`#N4I;r!hF32`3-N1OblWlAT`t4{!647nbKqOpGLmGLyqgT4?Rtk?JRXN#wd z`o{CX+*gYulj9ucuM>9y+?I?!`};z1OJc_D?veZp<;OQYN`Bmy@uTTMfbXt)+`dgb zLu~cZ$0i203oyc0E7R@wg{}9uu_SJ3i;J{@q44 zr<^ZwT>LvN&LPIda;Yo)yD$IuZz}+D^Ud#5eo20;mT*&2?0IxIxQoz+LoAv{ACjKb zU&h77{3peIT1U!r{j=T8Ul}!!$#IXDJheL=FA0Bb*X#Y1cnWj+?8mkVcHO`0#nIQC zK6Bo0@Y!u7e(VT}Iqx^h-m94NcvNsL*EHvoZ=CiwjSKr{`&HCg?5{a89yitd7ZeoqE21bXSt7l zzwx6JxF3_g9|Omwb+Ei@z4~#<(>Qw!Xv_9}pZ*2$lh5Leyl=X!6z+_ z^Y@Eeg^-^LK56;kjNL8j6FJ-UJbzbWcPWuur}OW9;>a-ntjCDoi?hpQ*XzyFf>^e`^>=Ts}bF!DlnSFGzUi;e>Px*R&+$nBSobMKQ$pn4> zQCwM^-z0hJ$IeXNoAW%OA34tX_fF~M9IAkIJa`?8|462Pd`Ir$syTn3 zO?k@MzWw+U>3t>y^uu!N{i}@Koi+x@RdcD`l(SyPEw#(oSZ=#hsyz0qW92Fs%H@kW z=jk*!jir6(QV3qcb9m^bHrFQpeE68^fM&gbVy??aGYf{*2_vzVI}_kOgk z$6Y7x;Sz4#-`^4^HwP~?ZP)%jByKy7jAIoa=GjI#VE^Aa$0hAw&h8hA^LR|^-{*@v zkjZC`r;GE@`LZaU^7ZD?8F7==-B*iaF0ZH0^RjH+{oafpl|eEO=aV_FAM_)~IWFnC z%eXimypHAV8N2Cv<@Lbaq;<{xOV>5_Z`|(Xu)Vo>>SMV=@&60`qVWIpyQ#PUgBKV4 zSMVNN@ZV_rl7jy-*d^iL_j_Eyf92}&1^+FJClve_b1p6T?|pn}!GG1|i3R`7jK^%^ zFIF$w#Q(nO;!Tf3eeD)tkA}ZQe}(ejcCd%8lEL z`Ndinwb|`F-S&c_x7g|S*?>A)uDQBsw8J@S)s-XlTC+IS?9|wFtUiAY{zHOu#U1hF z+TtuKH!KI?OlP^h(7UE+HQJ5Va;s?7R>HBmaSk4=c?6}gSGxO*A(-e z=5nh&t>ZQwuP%1CcI=p2s`VSSChYY)-F-#Bvs5hA7N(1noqoU5Vtre&tJ&<#*B~$E z7HbQgvonqU!P-*M3V&|ubQgA`L;a#P+6C*oJMCV-TSM4p+h-2e zdcC>%8Wc4f?Ru>{UDVpAmz%7_hHY-xhD&E^z52dp?Q{?Iqvw|DYGNna^WA#C9vWLi z>`47o=wIk}%V)UkV7<2(?e>E8Zl{kJcG?A^UGE~8v+ae(e7!fFUc9z`5D#*CHaz|-cUpD@LU zg#8WOX^ln{nI0n?1~IwZCeWuCqW$&8>BauOM!mUU8O$fG#o~}qJ&tL&b2e7utcmpm zj$td&6goX($JPfu3+e1u^l9`w_I_?@vEHo@pl)rUu}qi8+LaBprQULLNH5l!r(*YT z#qKd-ykw>`NV|gg-j#m6-KT|Aa{cm-9qz-8Z6^3^8{u!)nukx`iuIxwhv-P`UCTDhofZBqu9at_gGxTuzEI5w_9L~WOB9! zGY(d<;MvJ|X$-+^n|{=a>z6r;_SLvTqt_!RF`9PPlL#Fg#vp0L5Or%fKON0*aQ80t z8chtXmIJKmafW%RYQb*(NWI1NUQW-42wZ<;=9Yf9G2nE*hB-C7p2*CxYpy=pt=Eeq z{9CP^#%-X{MiMmI@U79lgLgBW)>d#@Te+iH>UVn)Mwr5l4~a9V)_~$xu%}T&y5auW zaxKi;F7gN#<{Edv%Aw`fkxr-224==`>p=ZXy@~2ktl!N;odq_Bx{!DX{d5c&`=S+^ zin-2mcRnm|G1^pm5+Jo`Y3kv|K0Lu)AGZ-k68G_TADvo=na|O9#q>8le=vnwgO)fc z;1-c>YY?`KAXl$J*dk=+7}Zp3^S9L(ZYU1mp5BZR_2etugM+ox?M8ojpG z47a*5vbM%as@0o|SEIkHy)bjGkNyqQxV|zyddl5EvVO+}eY6QXs9Ill;{*+^;Reyu z7OGd^qV%|HL!~ee|DWd-L0QS^k(;9M^^8~t1$P6x???Vm&UA6^^wNu26lu{ z&9gb~jpoB?b)`3yVcP}bWM|>r)!0EIBG>@)!Wp#05oygX^m%r5FkSRI%`^4xELNwm z*$O`Qbhn3Ql0%j8lKJ_CT?=OzgOG0xEL>QHv^|05Mki@ zwfX*$`V#i-!?DA%v@32jP0roUwdz3Fg(hv$(jKG%x5C<-`m}-FS+`gBBCpf9@^gX& z)3dFm|1A7#)+ z+8VumwO;>V=L|cH0Nv)vF^n$?pmB()s=mX zCPbm*XYmL#UGVR=@b~KQ_Zp;R5BsGhgchlaJEtD4PHV@`9X9hr+X!sW2rGHhdU&+) zg0z5beSFGsOV|Vr4PhCqch4o=c4Lwg=836BKj&`~|h-*!6#;{xr+N!nw{GtS5 zBj1aUP_d`TG@S=G{p2=$LTK$;TEfz?u&;yL9TtLk2JN>m>%m}feY+hVBf~S5s;R+h zx5IrSZpx~p!nrWNtKB*_8c97xjx~DAwPv`PCsht%yu-%{IMc1yZgXejJWZPr8ANUg zUMCZ17z8eYd(L5j55s&VR6yN(_9f|e!pDF&<2 z+ciI5r};3)#)I&Z4!mVY_=G&tyMbg+qubZwJsPcpow(${ORT`v4iwBUHku2e*5v&Y z{b3AyMZeZPUGGNeE?7_=uE>>SYtXA=M zg6&7CcRzD!aCQLMH8>%cCgmUx*0wTVTPYjTb2Lf9xGT*NBg?Q5G6_abup>g=4gb zdn6w>w7t-~se0I`XzMa)!vZwOhe0Fu3TqqF`}2UD?alE%&ol}K;{#+|YNE%y!JvEL z5L$){S6Fnl665ml25qot`&6rTYo^<|t=^_)MJ8F=h8L#;mh>WR4ue*-;vw{Cuz|!N z4C}3tlR-OnqukEoA)DJv%-p$p6K~y`P40OQEx|kAQ8ZRb+pcc6#*qo0;>vJe!26C` z*w!6dilf~+RBIt*15NR&LrdNc6w7!9iQAV!)1jr14SV8-1pNpBKUizy(Gic7qi5Q! zGeNMrhBZLfJDbwfI|L2}F88nk;?X8F#oc}g#9+9C0FOE0 zxDAiQy|5PZ@ptgPZ-%c~5tekQM`s|XmZn+@JJ06xN2l!QwaE3`Gz4o|3VSr>rwMc^ zDjaW!!xM$OYx9eBytv?2W~`@Fh^J>fzmEx&fV1tBd^e!8YPio})OnMMeQ6yyppi<^ zJsmulM&ni#x@iUK7uA>x>r%`C<}25ruwl~{_;#-H?8@vyF?aip9o6n~8xtBU5#9wa zV;_ml&v;7`rNKqN43TmkK1#xSqRl|A6H$V&8(ZmooLySWGI085r(3hnr*nuY6wo z?bCg{O^)Z>+1I(iz&v(Dyo=B&-U)fWr`y3dvt*asr{hba-L=+9ygtP!F7$LVUMn4_^~1B!ajcT6zKjbdMi8&4@se}+s8q$b#&`vN zeYbWdyxWW351ypP#eTVT3>)vp60Ta}+kM7oL)tT!hl^LXVGUXC@x5~?a&<)bE?Su% zv*=npDk)C}d4oK6iZLoUWlk~A&(^vNN8xO}iTxg4tsq?_4Yx16tEmnjk0IgZc}Xz9 z?+Lo~_%JP+*>#{(<5P9;jT(}+x!coMQbCEf$9&~1&v*@Wq$u1!#JAtg1|}|rNUet6 zZ`Bvpi13UsPUF2rc!jr?-cxUfm*3n!4LY$457kHTWHD3Ic0Y6$LllnKGY_(4cuf%X z1|u@sg=pPFGaR==C*x(;^7|lc;;^2@2COT?J|LcuCg%yBHir%1Kn1?x!1qUfz;ox@{bQ0(@w(>~k{4+;zW8(jOAoBT5QC1Lx@Z?Khf z!(9{dae*t(8Ug7ewR7`$;~n%yWytf`X5F~0p2bu72ohQhwD#9}u}8WR9gE+2bDD;I zz_2&EuN@prGE7|be{Z)No_}M$#2q3Ma{jh+48{26@kXYQukmZRd<5G%9VpYlk2f>c z?i{{>_h1d;HViF$6c&-)wPtulifP8*_3(vDh_zJ5xDQ4orp;*9^sXJ8XS5C+$4hjCEfva(IL4L=M`oUetZ#gt`%icrR&mP)) z%hAI}Zn$ypExT_#a%?Z!jvkraJ9phMw!87DGQWryvCG}s{JD{@PBhL#-MpFdB|P>x zXZT!+*WSrJ_;3qf|r7p zZVtHhN3(-Sgl$^Phw$)(KM{9gx6T-?YZVttVI-!*}TfqM(jzlFpdysdLEgEc~qt+lL4Zcn(mtEO6t+6 zs-Ee|Mrl_9ao5$r%6_5-odsOkh!PebQDhAQE>Ra z^|@UwWU-|_fA9Z){{Qno=bn43G927{bXQRn7vU4iCHQ;bmKR)G!SP9h)}pvL zNZ0-`@_30RabG6xscTh*7VG<08|0?MeboegU$a4Oo4D;0^j##xLI)=uN?PM$nO1_M^*YYqmdKbdmb=+@zg-Pva(3^MJhgkJ zmEdT-*7qM0Sx%q1*NZFj*W4S#F~&K4KO}kDzlz#;81LVMi7cnj`TVou$mH_b{d>2# zviMow`^9n0a{7K%^0EW3VfoE?C{llO>%o+`Kh}{4t}%`W>n!Fz9!~?aUjP1DoMUHw zY91WCn0_f*9-~hXZjOlU+7GXP_GJqiSieLD?039+_}6jyGI7>N!n#;@KQCA1S#E$V z7Z=~(zET_+c0IWaXGL+98n)@k`zNbq+>0f6LJ8;o*$;EJf4ma?nw1`NP?z=Bd3rHXfVBAy-3 z<(2P8pAbie^D?=NmlmJO*v-vfkJl5lj__K>(E3wzxv) zYw}9%)|ci1ZQFhob^5h+Os~`r4$i+9iyH{WD&}qym)hN#u#S)IE+u-`%L9)u8RmiY zSoIm{`F0&E5YSh1th3mU?-I8aZDh=mNz1+Bz8a0W`^SHk{6QV3!R73FeSWt%J`d#V zen4`^G3^WMj`K&vjmv#*?la=j@!;6I*zZSsJlsFWxdMYZf87tqc?u1=I6qYj*OfYQ z+;VZ=Ci!(bmW_w!$Mxbk9yxtp4{j2-6CCG_PR1Qe)#_OW8&xo`(%5zZ|-NrT{eMxkGRWAIOo9!#Ch(H+qHe?!L!i7OLSSh z>EQf=8a|<81#LNh$H#3exZJpXR(d=xz($J4?J`x;akGpU*q@D#+g4p{y(30(V%^&_ zcImInKh>_s_n`Fhz>oD~ z-2HUBj}jO6s*?J_d~vbfjr!qz$^FvHLvCO4lakxM?fEEi@m^KZ{#6a(=>C;)mK!wZ zaZXglsxC0+d>!cV1!J7!z2^2b#@X+|{4UYw`=R4tY}^m8zZVYxRmSXkzCX5v`=6OO z=kmb$_oqq6SRId#xxW<01M^_q@Bb@#I=)w*HxfT9q*9bK;+~5-ZQ3qnX?e9czF*F9&cD}*T2jVIv+@DLH#*aDZ;(j{LpA}Cz<7~Nk zzo*%(1&z#l{j*(k|>3b_6FSdMBaQ#w9R+#=2ymvPQ{ z@O*JgU@l2G)>*vXzgXM~+H(5t3UpEI7I!Dkuf%8E?g7bPuH$(yIlEp@9cO-zL|q;$ z9{KV5Y;K=896~Ygb8~ypz#Mepd)Bo4Q@M@WeYCknbeiMgGVaHEdC%*d2ljUfZPQZ5 zkg(1e`nlE|k@Nhc;=DJP=KXm?I65E9rFoy@rgZIT!q)X)|@`iw>OJB4ld`fmA+lv&1j&_ zal5|1Jt*!3n4H}yw6px8xJA_0;~o*WRKhua|4ZEJ1nw`y-Ce@De;0oV0F-<1$@R~6 zFB5lf3Fka;K5rH0{Nng*!N>74=Qvj&Z6xLa;P(C>>Y@<#W z^Pay~iKm?LV(ijlZksqV>v1m-SDCm7S^X5*7BeNcNo4CpZZbjU531@%r7Pk{zZhbcQI&pg@aOcJCpTPZy zxC0ZopAuJ{!1?}hw1jiszeoCbKibIru-#SgEF7Cve11uK(mY59*ZT-_-Y4MFktA?F zIIlh|z2||Q7)5bb6n~Vl%Q1GbK98H9-zd+GukRm!F1?INE-u#hDRGyV=<|K+@5NP0 z^qG4+jiGGCXFa=57svSLxMxeAjtAdwxHvAp4_+mn^4#;Zf9H0cI6voEkM&yLVR4M{ zdirYOxJ}4$*4GusLymL*UL|hL_nhkB>m^U)!Wg>PAIC3!kCb~qW{LG;misSNU;>zZS zxlfDRimP++b6kqYMg3Rdkc*4CCyQg8a-6?6{Yr6rz|n_sKg?Y#Ztn!nzYE$2&T|CS zc!I-t{y8qrK_2<}M%hdA)MEtk;C+O-bUjTu`{Q-e<9i-Drtz^H)li%-hh_g^wC=}e zoI9TBAIHtbyys<8JmtCh+mn1%M>E!Az2?4K9PcY+$~a#)eso`dKzh8+lyPqtH#H9E z-1>f5Tsm%ypNsoB{(MO}bHQ@+w)eZz>wGEWE{OB<&N%1&>Bq!XV2FJ$5I{`|HI?h<_LFY7GME02f$*b>EIEoHqYdum>DV2oH+ zH^{wqgWP@M+&|iI-&nUl_ltW+qBrNSx#r%ONN~&)vZy%ospOM(y9@w>!vea_{n_iNuTZYMbBYEIw%;tovE_Y>l(6ZE}L z+|4EWy#9Sy+=&U?Ux-_nz+FryQ5NyZ#l?1?BCc7&Ieu4&TLQqu>N$VSy-D1?C7kEmPl>w^ zTyDOZd#|`3DB*rha?d+pBZYls@wySu*8Vm3JJOfV^M?}L`Cu+xCv%+pm#&lNq0f4( z`lGV@*E&7`LGHe_nKu>`$2*=4|JskIiE|u>M68*6uDI0SN&>r|&iiY`6QK^veTli< z;>fJW9T8V{|Fs|9pE5kTe6js+mcFw4s=2N>#)m!({Gf7_J0!nL$1P~fjmK1=i{iE7 z%APCEOP^ zxGTWr@^4@_s$KJ!eP!7uJ!$+n*InFC=gT*Ur=0m>xp{L3#F5Ey9yhN;6>zKgjO%lp zPfFi;Fu6FJYm0mH1n!mM-crKdE4kwhY@|4T_lry8cRV5OkM((@+ zac&Z zb2%PCS)$MT&a(TP^?Bb}wmv(~zMofMgEq(Q&Zz(CxJ`p|F+V5Hc|iGkdEoVr%$4|9 z53ggHiig7g-P2V#&&6*((?9BTv0lgJoOsG}`pkWcI5Iiz-)8J4st5j9-@S=2r_bCQ zC)hQ2zqqnI^?Uwzi1XU#csM>kFZsy$CXlNa#QD1MBYg_xNv`CN=DaU?TEge{CFZ8Y zk;!q+gRc?C^>i8^{ak0z8IRl5;uaw}k>R%Kimw;P4 z)EQF8mogUXJ0_0*J#{W$rXpV4D(=z}PFQh9+!k>3Vcd^G@^8`cX(ik?DSVZ%pYNt&6<5tU|0&LK9=AuNcNz+ETyA{Lc|E93;Qmhfj!xh%<$t(?0n!vq9Tz>-h4sk0J zxQE2uIf45%ajO%!-xc?&67FM?r}OAMSQl6SLENPKzUNzd-{*X@9_#f!@hSWdXHf0| zz)<`f3c4DmT=cgJ{2!p^Vj#I8^xvma=mx4{aeJ{sw3su{^`Qo#gWP7)hi{x zOGmCPIeqs?KB<4^><_z<^W*zBsL!1Bmg#$IMqib%isNU_dMVCv{{G`#(pv#HjgR%9 z8p``Jb}I?ws=3r|j`RD(4lHV400fOAV#P@|i6Zctgx%+~-&3phr zxdaAtoY$+Ti@OY5F0ZDdn&K*P{0+r=+)i<2_wyShPxJ3#+28@{Lot`;Uyk$pmz$-x zf~z0E$9jS?{F%?#y~o0YH<#MYaei;nlU~k^%l)$4 z&zC=2CxiobOwIF1_^kJd{|E`}e7gUB<=5yt&kFj=Sio@yaba z(r#`YpOS%RiL2l|7iV+N6L)0^=Y7))#cc)0zPtaZhO$TUG_RBzK|ap=xtqeAzHkx# z`1fIC_*)VGUL+(p&i#9ls_Zi+xwst9^pEq>#r?A%4e^xk#D_6Vi{s*dziB6gIX^De zMfPJC&U1d)u79txA6(9lDe1di{j1`9z41LO?q~^TyB@cl;OK|>>Qb*MY!B5?-j(rVHG%Ahxzvx8^CjjyF7zYEZPbrnQU88d$F0yuUu-Wpfj?8Kd|Vvo zP%i(xANr)Y6W}UPHm=Wk;QXtA^Sord*$2kndG%@OL(@pPM`wR8x%IL6frNCN&82Zp zIrr0C8s{AMg^IKHyH9vpbn*laReaq4*ov5r#iatlKPS$Y%bxX*AH80Ft+;eNwj`bN zc$iDaBjtRFxpe;KxQ&j-zgGWdbY$M==DGKs-cL@0dl(=0%lL3*uVv!=pgV=5<7_UCbIREt$E7X3Tc9K7hq*K2E-&G{fA+q(0`4AsY!B5? zd|&-m>3aaJWXibLhmf3 zq&#)po-cjOy_~-@lBeT#A#uXvW-cAKl=CI#(sew?ZFJneQ1?sc>5YodDjdv>$15c7 z=*V|rInHtU7I9_o!<<)liMtp2a(Ois)2eu_xQD^z#@F2UiF>4kd$jLcj?1LHGB+u& z{M`5>@?#79e-OUle2ps7 zkk>QzfyL{ypL;53qYmpqH59KKe(sq$p_F(FgO#*%1`ab8~E?Z5R9Pe{c8?G#->Q7c4h#?xW(!%F|a(Q6R zewee~@W`-jrY*yZe+ z`?xqBa@;2~{kxLJRop*wY5#JZ?OycdQNg7CJyBd)|E%wFag0gM4|7+DtCVn8NuJIl zg&=g;{+dhu&2hH-jnZ4@@7|2v-1wSH?dCY!y?KINb0@`>#d&dq{WJIZ?%!&rf9ZI5 z9XFTWw^GjIyV3pq-LmW7XjKk$htVM%uUKGb6@Ov<#F@)GOtz@l+PuvzH5Vd z^`;5q;e0Wd=2bdw=Dc40g!D46a`|HJ-Qp&$r{c=iQ|t4-lk4evcD?UBQNlTX&gTVi zxqSZ33Gs72o12u+=DyhT+4+*@v-9P1$>%>&oTo4jxx%q#`E+JHE;u1cC-FL5zb$7wufpc-gj;hw~E%>x@+!P;_jQkJx|<&C7kygFBJDk z3FrI5jJQvhaDGlXDDF}?dL`I#fBigti@0e3C%}!z&-e4XIKCIn#l^qR>4|$QI6ga$ z+cozZaePOZ zviFmIpZFfx{V@86|E-Cix!(}?dlR^ii~Icv+~0`%gA(p>9CQ?~8?3MJkBZk*bLo1T za`cJCT)Lj-IPcf~rS3aD9#2wyxR%mK`$=C}%=ta=?Pz1n95-)}z5Y4QT(2%j+2^u8 zU!i_Y+Q)9sjPELJyVxId>H55C2uE{XCm*eS?D#xloOAQs{(67V2bbF)yfou4b-H+5 z%%%C9a`x9;n$J1zUwS@!eAE0}RZu>c{CjyO&Ll`2hj_a5I8SMVtc&AqyJ`P&{+e_D z$mF>3{+0PLo$22RcMeDQ&+FB`Bz`*~Pjicxc^~mIab&2&dQc6;+-Y&U(7GOXM%+vZ zHx<eTzwBEl{yi-2IP7xlIOjr9d!9Hl z>v1mbbo7@6xl;@!jbEpVtSam%gs&um68rCXMfJN#CUL{XKD$#`jOfO&Z_Nh?_LN zPy7l-JHCrp&)>_%Rlu&tT_Ntu3EbC-!l~ngPmYq#^?D<#Z$hX z-DBd&tjE>FP0E8;i<^`OKO~NMz_A;DkL3B`-*--m-_JrkJ^Z94>v&G>!cd6jrYI&Kg6W&n}(dL5q;&#NB7hkZHQ zUU56YF=xj8F!v#G)e_GC9@Iz0odCBpqYq7?e5w+U=fTnDxZSBJ9<1YU2FKjTAr|=0 za*@C^4=O_^3l(MFq=lVLG;jX3wMj5{Q5()_K7n>2s#6z8!Rw=3}=E?mcR zarq8`XC{HZuH?4$Lt&SEv6QDrOI){D!gw|H_}@S)ALnEP9CWY*)J_?&okWdipc zanlpH>%{FW;m{n)F>xnwwuH&SPg?9pM_d)coF6NazfMPfkHhh>I@VeKi{$Urk>Ahc zxL=n1_jSBb!uh%T6XHGwjyCy8%al%>SDygD7;-%dMPvQgE8}UJ2Yn$tSg-Hr&lXP^ z^^NC&xvv&SCdWC>UnlMaxGfod_VE3Y<44nj0N-8pxP6;= zhzgFI@bw2qYL`e(bFzcOkdlj9yQd1`k$UK0M=uGjl1@f7Ct*^g}#?7Dx~i=(eO zedfI1;IrFE{MZo`bKY;1y;m{k@u=Whu4&FE-#G1W8W;A@_N%C~*k5yGJZ`KzmVW=7 za;d)KREF{|h4XmW-?ILFzBtcw$B(f}<5y(^isxm@JrpGIkH^_u`a9&5+oWr}&pwjm z=?Taz7gl&4)x<49FrDF+B=>*Ed>#z_us+sVoTsl8{}#06@;<=$^Ez>FMV-2=&vGCA ze&a_ca6cw}KL(CV>tK1+diCRyr*ZZe(3b7{KK%>gDcgz97FLpCe?KU0CuBK)&3XRr z0hgP7S+DSgcn?jI#j$KxKbE{?b3 z_vEj#q$pNVtqe~?&D=A@t)ev-zbRe!Tyd`|;jC|3+}#s6=ks@tbDD22lAhGxE$Ryo z=m%p=J$a3Rm?r~eA5|7!qU31SCSC$9nt`_G!DARYHIL2hX z{>_TJtc072-5>lr(cZU|*>#?-*(WL|=43C8GyCXbz4o^&p7QnlxKrGuINvSqk_r0$ zqqwp-ze)1ckDZykH|KdmKXRP&@14@iIaC4bcSDgRdfD6 zoAQ*ief#kz()&yZ=!fOj`&SveJ8cY*tL9R>DQCToTWXiFvD|j2RC(-I$I4YOl*<=$ z&eLgd9Ao#ZfNqv2OHVq!7lI`IF>lWMUP>=SB>Z(hozKsa-iIK)1Ru*;XE8S|?)_+6 zkGoFX!zJ9fzrQ6;ZVp~(+OGY5NZfWD8OJI<%(IPf!2Z8;j!W9VoZT-J=kb`-zt0zU zAd}A=PZ#H*^JP&yp#;{O-=MdAPHcT;fz1}`r7 zui!nl;J?xKB?bRwuuH%NJZ5uPt2c9{+q{|n{X9O^l^eGg z^NY1EYO~vUy6pu;Z?V(uvjKIqTyu5NXoqvusw+q8wPtau*{QMVSbhE){D%bRiaX-T zwZ&OfZdeY&na*;1p?6KuYP1`zftP1x&qy8DWLXQ^1KEld|DJNhx$cp$hE4qg@s1@bRo3b?;fq4Y}Si2oyJ1Znu7=@ zt$5wZPN!M44xerJ4%Se9oT#pVMP%Ls?Jc>AQj2SD-fv3$yKVgay z3Huwm(;AH^GCf8(3}SM*O`uOPMEmQF(~JFmje2vzGMGkah*}y(|5CyH5+L73)PW4$+a=yOxzO zm#UrSxzn9?1mXm49m5pbUbGIKJXY_*R_kD+jrmYt!Md?)W(M;RU8r{tpW?JGTGG(N z3UWH;=#kplsBdk{p_6+TPSQt|eb!%?< zN>A6~KsmTiml|v_YE!?Ue#|s;jqp>!R4oAtLN3nzP@3FXwVfAdBZnwY|$>eMe zW*n?y!LyU`(ino-HvOm-*DrGx?W=KxMz2RsVl?fnClNY0j6u?fA?ns}ema`r;O<@O zHJTV&EeBZB;|%js)q>snk$Q{ky_}v85xD-y%q{(HW5DTr4RdOEJ&~DX*Ia$HTdx;K z__ta+joUz@jU;Ha;aj792k&M$t*zj+wsJ?Y)bI8pj4*{89};I!tpUZYU{9llbi@6# z($vF^eRzVqK5iq7B<|zwK037!GoPdJis^59{$L8V1}$+? zz%3%%)*x&dL9SkdutmtsF{-K7=5MPn+)y0AJ-rzt>d9BQ2M24X+l~J6LcN$dc5N80 zkal4TaX^dK$km5VDtxoJiZ-n9a2JEMyHh`PYM>fTHQe7q6YlU`Z5nW^Zw_z(Pu-fv`_91+pS*F(_#{D#9X0{E_RVXo#q1edEAL`rxtc}o^@f9q*XC` zH-|ZfVc=@1MIBRA-A8L@p>gV5+(uPbc7;8h_BeYwm`%0zeEs@vXPJA!-OUCTq1ipf z-29Fm0o~o{){9V^YhV`{{_Hw^x?4Yu=*{lIj;!8|S7GiCb;o+#E{$2C>LZQQ4eSV^ znrCy|8_kE)>Pl}Y!?p{=$q>243rB!?>FCG+zOyB5wc1|i=XSh%nX$GzdPI`*`UepqCx_4Z7o*7I(0Ai}`) zYxDgh^(E}vhhv9jX;<86nw-0xYt@0U3r*Uhr9DUkZiTfu^=SjUvu>~MMP8?I<>v$m zre|AA%gtWB=r!y0rC|?FVd=ah$)>Zl+u$Ph;PtNJbW_rTEoBrgg!v1Pa4+w1Kgytu zv^9GBYQ6r!&KY(X0lLkTV;El!>LA!i84hqg&iRouZW&uA5fbF+aAWI4k9vI;25#Y8 zyVh#V@9Q=~2a8_FoWUt(P9IBB*l3>ua|Gv!1@5QA6GwRH3EOb2$9w=%-L*Kosw?{% zO^8Cr&*Bkgy5Qe!;qTSq?=?ut9`;L12rW_-cTPQ8oz{+>J8b5Mwh`E#5mxf1_3&up z1!)1>`uLRNmaqvL8p1MI@19G#$BBu@8hW0#G63Ge9PM;_5Z9XCjA6MLv{h^U`9%rB zM!pvxp<+*wX*v&X`pIqhgwWcxw1lN&VP6NgJ1hk84BBsB)`P*|`gS`!Muul9Ra1l2 zZio9u+>}*Gg>zwkSG#p=G?IFX9BcHJYt3*oPpTZkc!!S@aHd+~8SK(VI-uN|A!+KJ1siN@ zq2JTMaz_|{Pa{Tni!e7Bs$rXN9>eR=_x@R2Y)3*EgCoP{jEx)S*n^kRT&%u)Sgqpi z1lx~N?|$ae;OqdhYj8p?P0B$YtZik!wo*2v=V+3I%R_R7eR}X=ZvG^X+KJ%|_a3HS zsLkO43=5JDbh6t))Ud2#<-T$I^C0SDEokB8Q@kMV3c|HvuMr<(qbxM=;>J9$3&&^= z_eef&XnUb|Q}wV>(bi?qh6QMl4}(VR71lPU_vZmQ+neKko@o>e#s|o_)I^VYgF*Mg zA+!t^uCVB8CC2694ccJQ_Ni9w)=al^TfI%qicGS!4KGdyEa^qs90sju#Y5=PU;~Lk z7}i@OCxdqEM!B8ELpHaUn7MQHCf>R=o80prT7q}JqiC#>wq4zBjUy90#g*Z{fcG7> zu&q0^6i2&tsMbQr2AbkkhnBn@D3X$aP`6!vJ$PZQ`; zR5;!chbIbm*X9@NcyYn4%vevU5Kqr|ejgJk0cYDM`EEdG)o`D|sPiTh`_ejaKqHl+ zdpdY9jmE7gbkhpdFRC#Y)}@#O%vY{KVZ){^@aQlkkoJnuyXW$3ByYc~D|=oL#k_^alp1a0gXVla+#--< zfej~c6SD(*l^&kNmT~(TK4Q$rz3t$>lwO9(R4>k9UBjEOfGfHM+&}(h{a2HahsLYtae5_?J^))Q;nk`WZZr z_v=Ab49-YXh-tWM4p!-K7B)q(H{twxbnXal3!S1F9{88y9u}#J1cPk2a~n$3t)b{v zZdNj#(EF6IA4+$;$QC{ddY4zWs2bzr&x*!6}BwN zh{P=;VixyfVU*%njE#lFT$J+5;>Iy{BOx$vwT zZispmh8=60>({w? zI@-ZTCVcrDO!|FAv=7|_(?=U!y;eF<>xXBd<5(qCeHj-_j38c7<0a?tQK^b=jqwWl z`flw^c()h5A3RBoi~Vxx7&hLGC0w<{xBHCGhO}oc4;Qa&!y2;O<9p{)Vk zX3@2HR8pP{@&6e`6Z<{9T0y!<8g5^BS5qB69z(**^O9hI z-xGA}@nKptv+F>o#;5Aw8#N?tbGN6jq=FJ{kNL`3p79#$NKv?dh;P4}4NP1Lky;JC z->NUH5#bqOoW^^L@Ct7&y{Fy|FTc5e8gyb89;%Pv$zrCa?SAMihA14dXC7q9@R}g% z4Mt?N3(>lVW;kw#PR7fw<@Z6@#9=*)4Omx(eLy@RP0kZMZ4Mj2feL)Xf$xv}$TQfj zjbv+F((t5?fo6Wpu>oIkVgllUD*RA`5B23zi~|*NN@OD`_Xdk}I1g6f6y6Xq5$g*h zt>NT|ekT#;ztU^0n_PamtjCNwJBP=>pxEtUr+v5^9uyY#H@NmKH~D4qOTzY*-(V}} zhPx)@;{sQnH3HH{YUk$h#yjYZ%8=)=&AM@0J&ULE5hS!2Xzj1{VvlqsIu^h4<}?la zfMIWRUpqLMWSF?<|K4slJpaagi919jt7>e=B){KR5NoN9aUYCGOq0LWG&uB$lu)7M= z71enCU^0%X!$|VmBWUAy2%KAoaKGhVq8&ke+mF|V-bo+9drmy;`a2UoCQs|TqywGK zZOcpX*Ggd}<}(M(A3905;t9V!z%Goh91{FR16Skbg8Ysh^@F>P-*RZ?mc56rpFOnq zmZOJ{+;HRGTXx@gweUHt7;au0ubz1y)Mb^u23t^;fGx)gTD*uz9H z+#GQ0k7fsv2-~!n58>enf!xPCc6^dTP~DpBwFVc*z9^dp`=vBEm^i(D18;KEx9@}N zwT+7kZd|e{-Z)hspdnQTxfNp^x0C2`?8jhRI2^4#c$v-}GGEtTdsDQ7FHYK6mulTP zy#K|?wswmf1DEyJ1#P*{TeS< zb=OL>J~#NLA@PFKM802p6UH1~@wPK&W$c1LCRdJ$kJk9YCWMt)+#HGzbwVp|xY!Z; vW)Rw9`Xb`V<}q2*({LYBxJU1Zn4}|f9zP8kYZ^o`fb#m#O~dYmb~OAy_hnZ( literal 0 HcmV?d00001 diff --git a/src/PhysX/physx/tools/binarymetadata/mac/mac64.metaData b/src/PhysX/physx/tools/binarymetadata/mac/mac64.metaData new file mode 100644 index 0000000000000000000000000000000000000000..77a6123f1d6044f285218552b565ec8302de8422 GIT binary patch literal 52514 zcmdU&3!G$EUFYu%2?mHbK!C^sR)>fqM(jzlFpdysdLEgEc~qt+lL4Zcn(mtEO6t+6 zs-Ee|Mrl_9ao5$r%6_5-odsOkh!PebQDhAQE>Ra z^|@UwWU-|_fA9Z){{Qno=bn43G927{bXQRn7vU4iCHQ;rmKR)G!SP9h)}pvL zNZ0-`@_30RabG6xscTh*7VG<08|0?MeboegU$a4Oo4D;0^j##xLI)=uN?PM$nO1_M^*YYqmdKbdmb=+@zg-Pva(3^MJhgkJ zmEdT-*7qM0Sx%q1*NZFj*W4S#F~&K4KO}kDzlz#;81LVMi7cnj`TVou$mH_b{d>2# zviMow`^9n0a{7K%^0EW3VfoE?C{llO>%o+`Kh}{4t}%`W>n!Fz9!~?aUjP1DoMUHw zY91WCn0_f*9-~hXZjOlU+7GXP_GJqiSieLD?039+_}6jyGI7>N!n#;@KQCA1S#E$V z7Z=~(zET_+c0IWaXGL+98n)@k`zNbq+>0f6LJ8;o*$;EJf4ma?nw1`NP?z=Bd3rHXfVBAy-3 z<(2P8pAbie^D?=NmlmJO*v-vfkJl5lj__K>(E3wzxv) zYw}9%)|ci1ZQFhob^5h+Os~`r4$i+9iyH{WD&}qym)hN#u#S)IE+u-`%L9)u8RmiY zSoIm{`F0&E5YSh1th3mU?-I8aZDh=mNz1+Bz8a0W`^SHk{6QV3!R73FeSWt%J`d#V zen4`^G3^WMj`K&vjmv#*?la=j@!;6I*zZSsJlsFWxdMYZf87tqc?u1=I6qYj*OfYQ z+;VZ=Ci!(bmW_w!$Mxbk9yxtp4{j2-6CCG_PR1Qe)#_OW8&xo`(%5zZ|-NrT{eMxkGRWAIOo9!#Ch(H+qHe?!L!i7OLSSh z>EQf=8a|<81#LNh$H#3exZJpXR(d=xz($J4?J`x;akGpU*q@D#+g4p{y(30(V%^&_ zcImInKh>_s_n`Fhz>oD~ z-2HUBj}jO6s*?J_d~vbfjr!qz$^FvHLvCO4lakxM?fEEi@m^KZ{#6a(=>C;)mK!wZ zaZXglsxC0+d>!cV1!J7!z2^2b#@X+|{4UYw`=R4tY}^m8zZVYxRmSXkzCX5v`=6OO z=kmb$_oqq6SRId#xxW<01M^_q@Bb@#I=)w*HxfT9q*9bK;+~5-ZQ3qnX?e9czF*F9&cD}*T2jVIv+@DLH#*aDZ;(j{LpA}Cz<7~Nk zzo*%(1&z#l{j*(k|>3b_6FSdMBaQ#w9R+#=2ymvPQ{ z@O*JgU@l2G)>*vXzgXM~+H(5t3UpEI7I!Dkuf%8E?g7bPuH$(yIlEp@9cO-zL|q;$ z9{KV5Y;K=896~Ygb8~ypz#Mepd)Bo4Q@M@WeYCknbeiMgGVaHEdC%*d2ljUfZPQZ5 zkg(1e`nlE|k@Nhc;=DJP=KXm?I65E9rFoy@rgZIT!q)X)|@`iw>OJB4ld`fmA+lv&1j&_ zal5|1Jt*!3n4H}yw6px8xJA_0;~o*WRKhua|4ZEJ1nw`y-Ce@De;0oV0F-<1$@R~6 zFB5lf3Fka;K5rH0{Nng*!N>74=Qvj&Z6xLa;P(C>>Y@<#W z^Pay~iKm?LV(ijlZksqV>v1m-SDCm7S^X5*7BeNcNo4CpZZbjU531@%r7Pk{zZhbcQI&pg@aOcJCpTPZy zxC0ZopAuJ{!1?}hw1jiszeoCbKibIru-#SgEF7Cve11uK(mY59*ZT-_-Y4MFktA?F zIIlh|z2||Q7)5bb6n~Vl%Q1GbK98H9-zd+GukRm!F1?INE-u#hDRGyV=<|K+@5NP0 z^qG4+jiGGCXFa=57svSLxMxeAjtAdwxHvAp4_+mn^4#;Zf9H0cI6voEkM&yLVR4M{ zdirYOxJ}4$*4GusLymL*UL|hL_nhkB>m^U)!Wg>PAIC3!kCb~qW{LG;misSNU;>zZS zxlfDRimP++b6kqYMg3Rdkc*4CCyQg8a-6?6{Yr6rz|n_sKg?Y#Ztn!nzYE$2&T|CS zc!I-t{y8qrK_2<}M%hdA)MEtk;C+O-bUjTu`{Q-e<9i-Drtz^H)li%-hh_g^wC=}e zoI9TBAIHtbyys<8JmtCh+mn1%M>E!Az2?4K9PcY+$~a#)eso`dKzh8+lyPqtH#H9E z-1>f5Tsm%ypNsoB{(MO}bHQ@+w)eZz>wGEWE{OB<&N%1&>Bq!XV2FJ$5I{`|HI?h<_LFY7GME02f$*b>EIEoHqYdum>DV2oH+ zH^{wqgWP@M+&|iI-&nUl_ltW+qBrNSx#r%ONN~&)vZy%ospOM(y9@w>!vea_{n_iNuTZYMbBYEIw%;tovE_Y>l(6ZE}L z+|4EWy#9Sy+=&U?Ux-_nz+FryQ5NyZ#l?1?BCc7&Ieu4&TLQqu>N$VSy-D1?C7kEmPl>w^ zTyDOZd#|`3DB*rha?d+pBZYls@wySu*8Vm3JJOfV^M?}L`Cu+xCv%+pm#&lNq0f4( z`lGV@*E&7`LGHe_nKu>`$2*=4|JskIiE|u>M68*6uDI0SN&>r|&iiY`6QK^veTli< z;>fJW9T8V{|Fs|9pE5kTe6js+mcFw4s=2N>#)m!({Gf7_J0!nL$1P~fjmK1=i{iE7 z%APCEOP^ zxGTWr@^4@_s$KJ!eP!7uJ!$+n*InFC=gT*Ur=0m>xp{L3#F5Ey9yhN;6>zKgjO%lp zPfFi;Fu6FJYm0mH1n!mM-crKdE4kwhY@|4T_lry8cRV5OkM((@+ zac&Z zb2%PCS)$MT&a(TP^?Bb}wmv(~zMofMgEq(Q&Zz(CxJ`p|F+V5Hc|iGkdEoVr%$4|9 z53ggHiig7g-P2V#&&6*((?9BTv0lgJoOsG}`pkWcI5Iiz-)8J4st5j9-@S=2r_bCQ zC)hQ2zqqnI^?Uwzi1XU#csM>kFZsy$CXlNa#QD1MBYg_xNv`CN=DaU?TEge{CFZ8Y zk;!q+gRc?C^>i8^{ak0z8IRl5;uaw}k>R%Kimw;P4 z)EQF8mogUXJ0_0*J#{W$rXpV4D(=z}PFQh9+!k>3Vcd^G@^8`cX(ik?DSVZ%pYNt&6<5tU|0&LK9=AuNcNz+ETyA{Lc|E93;Qmhfj!xh%<$t(?0n!vq9Tz>-h4sk0J zxQE2uIf45%ajO%!-xc?&67FM?r}OAMSQl6SLENPKzUNzd-{*X@9_#f!@hSWdXHf0| zz)<`f3c4DmT=cgJ{2!p^Vj#I8^xvma=mx4{aeJ{sw3su{^`Qo#gWP7)hi{x zOGmCPIeqs?KB<4^><_z<^W*zBsL!1Bmg#$IMqib%isNU_dMVCv{{G`#(pv#HjgR%9 z8p``Jb}I?ws=3r|j`RD(4lHV400fOAV#P@|i6Zctgx%+~-&3phr zxdaAtoY$+Ti@OY5F0ZDdn&K*P{0+r=+)i<2_wyShPxJ3#+28@{Lot`;Uyk$pmz$-x zf~z0E$9jS?{F%?#y~o0YH<#MYaei;nlU~k^%l)$4 z&zC=2CxiobOwIF1_^kJd{|E`}e7gUB<=5yt&kFj=Sio@yaba z(r#`YpOS%RiL2l|7iV+N6L)0^=Y7))#cc)0zPtaZhO$TUG_RBzK|ap=xtqeAzHkx# z`1fIC_*)VGUL+(p&i#9ls_Zi+xwst9^pEq>#r?A%4e^xk#D_6Vi{s*dziB6gIX^De zMfPJC&U1d)u79txA6(9lDe1di{j1`9z41LO?q~^TyB@cl;OK|>>Qb*MY!B5?-j(rVHG%Ahxzvx8^CjjyF7zYEZPbrnQU88d$F0yuUu-Wpfj?8Kd|Vvo zP%i(xANr)Y6W}UPHm=Wk;QXtA^Sord*$2kndG%@OL(@pPM`wR8x%IL6frNCN&82Zp zIrr0C8s{AMg^IKHyH9vpbn*laReaq4*ov5r#iatlKPS$Y%bxX*AH80Ft+;eNwj`bN zc$iDaBjtRFxpe;KxQ&j-zgGWdbY$M==DGKs-cL@0dl(=0%lL3*uVv!=pgV=5<7_UCbIREt$E7X3Tc9K7hq*K2E-&G{fA+q(0`4AsY!B5? zd|&-m>3aaJWXibLhmf3 zq&#)po-cjOy_~-@lBeT#A#uXvW-cAKl=CI#(sew?ZFJneQ1?sc>5YodDjdv>$15c7 z=*V|rInHtU7I9_o!<<)liMtp2a(Ois)2eu_xQD^z#@F2UiF>4kd$jLcj?1LHGB+u& z{M`5>@?#79e-OUle2ps7 zkk>QzfyL{ypL;53qYmpqH59KKe(sq$p_F(FgO#*%1`ab8~E?Z5R9Pe{c8?G#->Q7c4h#?xW(!%F|a(Q6R zewee~@W`-jrY*yZe+ z`?xqBa@;2~{kxLJRop*wY5#JZ?OycdQNg7CJyBd)|E%wFag0gM4|7+DtCVn8NuJIl zg&=g;{+dhu&2hH-jnZ4@@7|2v-1wSH?dCY!y?KINb0@`>#d&dq{WJIZ?%!&rf9ZI5 z9XFTWw^GjIyV3pq-LmW7XjKk$htVM%uUKGb6@Ov<#F@)GOtz@l+PuvzH5Vd z^`;5q;e0Wd=2bdw=Dc40g!D46a`|HJ-Qp&$r{c=iQ|t4-lk4evcD?UBQNlTX&gTVi zxqSZ33Gs72o12u+=DyhT+4+*@v-9P1$>%>&oTo4jxx%q#`E+JHE;u1cC-FL5zb$7wufpc-gj;hw~E%>x@+!P;_jQkJx|<&C7kygFBJDk z3FrI5jJQvhaDGlXDDF}?dL`I#fBigti@0e3C%}!z&-e4XIKCIn#l^qR>4|$QI6ga$ z+cozZaePOZ zviFmIpZFfx{V@86|E-Cix!(}?dlR^ii~Icv+~0`%gA(p>9CQ?~8?3MJkBZk*bLo1T za`cJCT)Lj-IPcf~rS3aD9#2wyxR%mK`$=C}%=ta=?Pz1n95-)}z5Y4QT(2%j+2^u8 zU!i_Y+Q)9sjPELJyVxId>H55C2uE{XCm*eS?D#xloOAQs{(67V2bbF)yfou4b-H+5 z%%%C9a`x9;n$J1zUwS@!eAE0}RZu>c{CjyO&Ll`2hj_a5I8SMVtc&AqyJ`P&{+e_D z$mF>3{+0PLo$22RcMeDQ&+FB`Bz`*~Pjicxc^~mIab&2&dQc6;+-Y&U(7GOXM%+vZ zHx<eTzwBEl{yi-2IP7xlIOjr9d!9Hl z>v1mbbo7@6xl;@!jbEpVtSam%gs&um68rCXMfJN#CUL{XKD$#`jOfO&Z_Nh?_LN zPy7l-JHCrp&)>_%Rlu&tT_Ntu3EbC-!l~ngPmYq#^?D<#Z$hX z-DBd&tjE>FP0E8;i<^`OKO~NMz_A;DkL3B`-*--m-_JrkJ^Z94>v&G>!cd6jrYI&Kg6W&n}(dL5q;&#NB7hkZHQ zUU56YF=xj8F!v#G)e_GC9@Iz0odCBpqYq7?e5w+U=fTnDxZSBJ9<1YU2FKjTAr|=0 za*@C^4=O_^3l(MFq=lVLG;jX3wMj5{Q5()_K7n>2s#6z8!Rw=3}=E?mcR zarq8`XC{HZuH?4$Lt&SEv6QDrOI){D!gw|H_}@S)ALnEP9CWY*)J_?&okWdipc zanlpH>%{FW;m{n)F>xnwwuH&SPg?9pM_d)coF6NazfMPfkHhh>I@VeKi{$Urk>Ahc zxL=n1_jSBb!uh%T6XHGwjyCy8%al%>SDygD7;-%dMPvQgE8}UJ2Yn$tSg-Hr&lXP^ z^^NC&xvv&SCdWC>UnlMaxGfod_VE3Y<44nj0N-8pxP6;= zhzgFI@bw2qYL`e(bFzcOkdlj9yQd1`k$UK0M=uGjl1@f7Ct*^g}#?7Dx~i=(eO zedfI1;IrFE{MZo`bKY;1y;m{k@u=Whu4&FE-#G1W8W;A@_N%C~*k5yGJZ`KzmVW=7 za;d)KREF{|h4XmW-?ILFzBtcw$B(f}<5y(^isxm@JrpGIkH^_u`a9&5+oWr}&pwjm z=?Taz7gl&4)x<49FrDF+B=>*Ed>#z_us+sVoTsl8{}#06@;<=$^Ez>FMV-2=&vGCA ze&a_ca6cw}KL(CV>tK1+diCRyr*ZZe(3b7{KK%>gDcgz97FLpCe?KU0CuBK)&3XRr z0hgP7S+DSgcn?jI#j$KxKbE{?b3 z_vEj#q$pNVtqe~?&D=A@t)ev-zbRe!Tyd`|;jC|3+}#s6=ks@tbDD22lAhGxE$Ryo z=m%p=J$a3Rm?r~eA5|7!qU31SCSC$9nt`_G!DARYHIL2hX z{>_TJtc072-5>lr(cZU|*>#?-*(WL|=43C8GyCXbz4o^&p7QnlxKrGuINvSqk_r0$ zqqwp-ze)1ckDZykH|KdmKXRP&@14@iIaC4bcSDgRdfD6 zoAQ*ief#kz()&yZ=!fOj`&SveJ8cY*tL9R>DQCToTWXiFvD|j2RC(-I$I4YOl*<=$ z&eLgd9Ao#ZfNqv2OHVq!7lI`IF>lWMUP>=SB>Z(hozKsa-iIK)1Ru*;XE8S|?)_+6 zkGoFX!zJ9fzrQ6;ZVp~(+OGY5NZfWD8OJI<%(IPf!2Z8;j!W9VoZT-J=kb`-zt0zU zAd}A=PZ#H*^JP&yp#;{O-=MdAPHcT;fz1}`r7 zui!nl;J?xKB?bRwuuH%NJZ5uPt2c9{+q{|n{X9O^l^eGg z^NY1EYO~vUy6pu;Z?V(uvjKIqTyu5NXoqvusw+q8wPtau*{QMVSbhE){D%bRiaX-T zwZ&OfZdeY&na*;1p?6KuYP1`zftP1x&qy8DWLXQ^1KEld|DJNhx$cp$hE4qg@s1@bRo3b?;fq4Y}Si2oyJ1Znu7=@ zt$5wZPN!M44xerJ4%Se9oT#pVMP%Ls?Jc>AQj2SD-fv3$yKVgay z3Huwm(;AH^GCf8(3}SM*O`uOPMEmQF(~JFmje2vzGMGkah*}y(|5CyH5+L73)PW4$+a=yOxzO zm#UrSxzn9?1mXm49m5pbUbGIKJXY_*R_kD+jrmYt!Md?)W(M;RU8r{tpW?JGTGG(N z3UWH;=#kplsBdk{p_6+TPSQt|eb!%?< zN>A6~KsmTiml|v_YE!?Ue#|s;jqp>!R4oAtLN3nzP@3FXwVfAdBZnwY|$>eMe zW*n?y!LyU`(ino-HvOm-*DrGx?W=KxMz2RsVl?fnClNY0j6u?fA?ns}ema`r;O<@O zHJTV&EeBZB;|%js)q>snk$Q{ky_}v85xD-y%q{(HW5DTr4RdOEJ&~DX*Ia$HTdx;K z__ta+joUz@jU;Ha;aj792k&M$t*zj+wsJ?Y)bI8pj4*{89};I!tpUZYU{9llbi@6# z($vF^eRzVqK5iq7B<|zwK037!GoPdJis^59{$L8V1}$+? zz%3%%)*x&dL9SkdutmtsF{-K7=5MPn+)y0AJ-rzt>d9BQ2M24X+l~J6LcN$dc5N80 zkal4TaX^dK$km5VDtxoJiZ-n9a2JEMyHh`PYM>fTHQe7q6YlU`Z5nW^Zw_z(Pu-fv`_91+pS*F(_#{D#9X0{E_RVXo#q1edEAL`rxtc}o^@f9q*XC` zH-|ZfVc=@1MIBRA-A8L@p>gV5+(uPbc7;8h_BeYwm`%0zeEs@vXPJA!-OUCTq1ipf z-29Fm0o~o{){9V^YhV`{{_Hw^x?4Yu=*{lIj;!8|S7GiCb;o+#E{$2C>LZQQ4eSV^ znrCy|8_kE)>Pl}Y!?p{=$q>243rB!?>FCG+zOyB5wc1|i=XSh%nX$GzdPI`*`UepqCx_4Z7o*7I(0Ai}`) zYxDgh^(E}vhhv9jX;<86nw-0xYt@0U3r*Uhr9DUkZiTfu^=SjUvu>~MMP8?I<>v$m zre|AA%gtWB=r!y0rC|?FVd=ah$)>Zl+u$Ph;PtNJbW_rTEoBrgg!v1Pa4+w1Kgytu zv^9GBYQ6r!&KY(X0lLkTV;El!>LA!i84hqg&iRouZW&uA5fbF+aAWI4k9vI;25#Y8 zyVh#V@9Q=~2a8_FoWUt(P9IBB*l3>ua|Gv!1@5QA6GwRH3EOb2$9w=%-L*Kosw?{% zO^8Cr&*Bkgy5Qe!;qTSq?=?ut9`;L12rW_-cTPQ8oz{+>J8b5Mwh`E#5mxf1_3&up z1!)1>`uLRNmaqvL8p1MI@19G#$BBu@8hW0#G63Ge9PM;_5Z9XCjA6MLv{h^U`9%rB zM!pvxp<+*wX*v&X`pIqhgwWcxw1lN&VP6NgJ1hk84BBsB)`P*|`gS`!Muul9Ra1l2 zZio9u+>}*Gg>zwkSG#p=G?IFX9BcHJYt3*oPpTZkc!!S@aHd+~8SK(VI-uN|A!+KJ1siN@ zq2JTMaz_|{Pa{Tni!e7Bs$rXN9>eR=_x@R2Y)3*EgCoP{jEx)S*n^kRT&%u)Sgqpi z1lx~N?|$ae;OqdhYj8p?P0B$YtZik!wo*2v=V+3I%R_R7eR}X=ZvG^X+KJ%|_a3HS zsLkO43=5JDbh6t))Ud2#<-T$I^C0SDEokB8Q@kMV3c|HvuMr<(qbxM=;>J9$3&&^= z_eef&XnUb|Q}wV>(bi?qh6QMl4}(VR71lPU_vZmQ+neKko@o>e#s|o_)I^VYgF*Mg zA+!t^uCVB8CC2694ccJQ_Ni9w)=al^TfI%qicGS!4KGdyEa^qs90sju#Y5=PU;~Lk z7}i@OCxdqEM!B8ELpHaUn7MQHCf>R=o80prT7q}JqiC#>wq4zBjUy90#g*Z{fcG7> zu&q0^6i2&tsMbQr2AbkkhnBn@D3X$aP`6!vJ$PZQ`; zR5;!chbIbm*X9@NcyYn4%vevU5Kqr|ejgJk0cYDM`EEdG)o`D|sPiTh`_ejaKqHl+ zdpdY9jmE7gbkhpdFRC#Y)}@#O%vY{KVZ){^@aQlkkoJnuyXW$3ByYc~D|=oL#k_^alp1a0gXVla+#--< zfej~c6SD(*l^&kNmT~(TK4Q$rz3t$>lwO9(R4>k9UBjEOfGfHM+&}(h{a2HahsLYtae5_?J^))Q;nk`WZZr z_v=Ab49-YXh-tWM4p!-K7B)q(H{twxbnXal3!S1F9{88y9u}#J1cPk2a~n$3t)b{v zZdNj#(EF6IA4+$;$QC{ddY4zWs2bzr&x*!6}BwN zh{P=;VixyfVU*%njE#lFT$J+5;>Iy{BOx$vwT zZispmh8=60>({w? zI@-ZTCVcrDO!|FAv=7|_(?=U!y;eF<>xXBd<5(qCeHj-_j38c7<0a?tQK^b=jqwWl z`flw^c()h5A3RBoi~Vxx7&hLGC0w<{xBHCGhO}oc4;Qa&!y2;O<9p{)Vk zX3@2HR8pP{@&6e`6Z<{9T0y!<8g5^BS5qB69z(**^O9hI z-xGA}@nKptv+F>o#;5Aw8#N?tbGN6jq=FJ{kNL`3p79#$NKv?dh;P4}4NP1Lky;JC z->NUH5#bqOoW^^L@Ct7&y{Fy|FTc5e8gyb89;%Pv$zrCa?SAMihA14dXC7q9@R}g% z4Mt?N3(>lVW;kw#PR7fw<@Z6@#9=*)4Omx(eLy@RP0kZMZ4Mj2feL)Xf$xv}$TQfj zjbv+F((t5?fo6Wpu>oIkVgllUD*RA`5B23zi~|*NN@OD`_Xdk}I1g6f6y6Xq5$g*h zt>NT|ekT#;ztU^0n_PamtjCNwJBP=>pxEtUr+v5^9uyY#H@NmKH~D4qOTzY*-(V}} zhPx)@;{sQnH3HH{YUk$h#yjYZ%8=)=&AM@0J&ULE5hS!2Xzj1{VvlqsIu^h4<}?la zfMIWRUpqLMWSF?<|K4slJpaagi919jt7>e=B){KR5NoN9aUYCGOq0LWG&uB$lu)7M= z71enCU^0%X!$|VmBWUAy2%KAoaKGhVq8&ke+mF|V-bo+9drmy;`a2UoCQs|TqywGK zZOcpX*Ggd}<}(M(A3905;t9V!z%Goh91{FR16Skbg8Ysh^@F>P-*RZ?mc56rpFOnq zmZOJ{+;HRGTXx@gweUHt7;au0ubz1y)Mb^u23t^;fGx)gTD*uz9H z+#GQ0k7fsv2-~!n58>enf!xPCc6^dTP~DpBwFVc*z9^dp`=vBEm^i(D18;KEx9@}N zwT+7kZd|e{-Z)hspdnQTxfNp^x0C2`?8jhRI2^4#c$v-}GGEtTdsDQ7FHYK6mulTP zy#K|?wswmf1DEyJ1#P*{TeS< zb=OL>J~#NLA@PFKM802p6UH1~@wPK&W$c1LCRdJ$kJk9YCWMt)+#HGzbwVp|xY!Z; vW)Rw9`Xb`V<}q2*({LYBxJU1Zn4}|f9zP8kYZ^o`fb#m#O~dYmb~OAy#DZ5k literal 0 HcmV?d00001 diff --git a/src/PhysX/physx/tools/binarymetadata/windows/win32.metaData b/src/PhysX/physx/tools/binarymetadata/windows/win32.metaData new file mode 100644 index 0000000000000000000000000000000000000000..a1426697d6a003b9bee86652351e1cc53525f32f GIT binary patch literal 44148 zcmd6w51eI3Rp;;I4@L+uAWDRQ-8h7SjBHO5!#EDuGt)Ck!_4Gmx@R&maWilFy_tT= z`eQ)Ib?e^uIyty+ad(zw7m%FjtRH7nSgrk#Fs9=G?^Ed92%){=+2oJ!StQ`CrNQ-4xk(3Fju}KaFoQWU7yr zZ{@^0B(W4aCcDd#4pNB((5$kg~*zLk^y?;(k$^nWk;rhg0PCZWjaQ zRA#^U8%gp_s{WhExAvzvH;LPSSIA7&SNyz(B$kRFwg2m+vi__7`$_U`s{V(`Pl?J( z{)lAu%LWs(f0~R>hRj&M=A%UXuSsGt=hBJV{~Sr>rPK6snu`{XuO^)zk^|?tPp4n@ znSPt&n^CCqi%_UO6h-AFPlj9n%x2}A9hY$OtZ)(AZ)LUqJ{K+~ldK-6PPUP;o%A@a z#8UjlWPVi%SNpQhxV3X^%J*+RaH_lh>L{^x6d%`zi?aAyfS1MBkqz`KzPdqw>>t@6 z5kCzVE8{PR%b#WZKf=rGI|tJk6h*ou_iVs_5}q1=#hOI*f0iVDsqt0&?;>e@Q~Wnz zRr+;*{YcQC>i@^0`l*Ej#jzPm_ULfvA0va{E~pUW||zWpTZ?WC>nlzy!* z?}l%KOLtNK2Vn9+QKU=qP=YDn_~+o}zj&W#?L1olseSopiZ)aB%NF@(nq$g8PbTx( zB>6YxpUYu8NM-(0{OyLzf2sPKAJ@YbI~r5j7|TsC`KT!7zY*O0CmT&n&qn_HLF(T_ zlD?FET3>$#F56;m?TEhvuK8cae-*B|mf{+p55S)Wm(6B3r@3f+^bA5VmmCN_`52j> zB58jV>zBO}^RMPs%D?|iUHPym(j|EkC+1(vQ@+``k$;~5^=>yUe5|fTs~U21VmhY zSQo+(*BZUfgota6xzO6RW5hK_bbp8b{)NGKTVt$@j#_^&hKtFER*%zMz6SnOuEbLOQux=DaNR%6zFEqcm>xaf zUKZ@r8fE!bPVu=7E|!W<^?xUPD_lOZ`nFcg!H$r&m2ma{6nqQEiT;}c^84`1I7+wa z=QNi!xZdHUxW@l(_zYY=F4q4MnE6Ncn^-y7r{^2Vgnd5Cjo*)>LoCJBf734?n#B5l ziZYT^eYOAd75Y{0m*KKq$@H50rpZ(M=sM%Rdj2EOv|frQ}cGiv760 zwf||D^*^ zD=YoxzoPypQ&(L1uQJHd))8?%gGpj;eiqj=l*tF?UGu4qN?L#9 zzpDf1;Ka)5Li{R@iUAY7|1kMd_(u94Ew5pW`fn23R~BC?Z}UgvWAjJ(GbH&y`+N0| z)2yM7AQoTZ7GE*9aT2%qN^!+co$*s!YS-$8X54d02VR{2#x;I-z{}>Z=J%`M>RYVe zhR^O#Q}ERKKz8;+)Zf7|RbTP@V#k&2mIl zKF+n-^ETuZudFZb>`&=G?)6vkjp~a_WWU9S#c8pBHoiq}?c4a4aU0(+hs!q8pQ!<8Zjqjz5t@@|3eSHMk6po7j%k=UT(RfNnEaI2L#me{$e5!(fGkm6k?}X1*@Na|H zO1S(p2R~lI=V8`=l`*mY>-(?kC?iq-EZ=4DA^z=fu`=F-PgU>%e5QoU{#U|hImYA1 zr4#YH;TC^I`^0|?Zt<7mZ-QI=r1&qwYv?I!U+dpu30HjmDyeM!vG_CnYv_^4musZ= zQP;-jBV3zs+i@bUdypjN)(?&EM@fD7agyb8>g3NN{aV9JtlTsi+W$&Y{nz^UX>^Fi zJYHY_9)59wbLm9u>%YL&-&B3^3s7e3g4q@8e+n16PSyvQ|73%S>CyUpSx~0^kL6oA zegC}!E|%h|KMz;iDX#iA!3mKhSN%4ee@U+T-wo9F_$mEA1hf9hhbGpKOW^i=s{UKP zm7C^7&ktqe^TrMIYy96#8A(dN?i;@dFVp`Uk$&wlOw3M={|AHq)cDH2kCDXGf2(JD z)W5%i7vm@955tT5C*$hhzrv?Drurv-F<&ljE8%*-{ta;XLAEKyBK|`7w~$%Jb#vND zvKVyrb%EIUTmLKubX58lbX$D9OBI6H`XO%XN6gJmtq<~lgL;>cEZ_8K{N~YdCm9;Q zB0n6puQ4*Qa*FShl#!(3y92v}WX$UM8vHf@_I)JvM=ZsyYp{ zH$AfdcStHPou=2gjvs|H%%%JjaQ-FvKLD!i7grx8ss23@)t~Xj+^5+oE*+8-f6^1O z?3pBu)fCCuhNJAHkUx$@EXUm`tOC`3{SN${U3!Nui$?JZ&dI{;L8>Ki5S){;j;fyc%NgL z{g(soEU7R1Uj$#P;PdcTRq$i*yDIoH{O$@qf}bnl)8zjL_-iZp8{uy#;fmj1gx|w4 z6~E%Y2LJIA{(hKZxG0JRiRM>IjQ;`kZGJou6|^`NxAh^#)xNC{_fp^LnW|5~|Bm!F zGSsGVTVKBblMjnx_HBM*_5FDYZuU<_N~|5l&o@y<61VSj{O`YB3>Pco2jH?d#buxN zA9N!*ero3!WuC-QHkn<@H)+E)&J$e0fQ(=-CvE0DZr}VjP5!Ilwm&cG|1p^PM>qT9Im>l_Wu_4ucWC^w^*O->~Rdc!zFEP8X#6bSW$+=sA1;>S@~`IS6#O`;SYQ6RnfhxSQ~nWOhQF$UpM~F5 z!e0ZEkBVabzYlKxpN$HcovN?>fh6T$>Ha0kh)r-^AiDqjCS2_)22C%gPQ-s7F5jj2 zU%)<3D#nk^zw
                      7*&@$Nn=u1$z-`O9|f#yOFfDgin*-fNv{sE}dw6y6_o}ar>V7 z^=%EV=c6+I!|<}_qc_8}Ki*ZSZ~eO;ZsVsunwXzI1W$=s}z`eomJ@Ur;O`?0se8KR{B-vcxM&zMo6tPK4aJSFO@T1zG>94`{cMhrXpN2Q!7ng8w)`xF~%eJC@^6we=*Ol-ZzSHo_;1^Z! zo$!lG_{(56{`aEGgyK%bZT#;Ic{&)^^WRa*$p7b}e3g-WN2Gt*XSq+SFK+r%T+de> z%E&jfB&+9?@FD-5g&*LU@}Kx!@M9%h&u4FdH{hxL^?fk&&n#t3%not0f7T#JZxtX-$(it|0Y&W-1MjT7vYcpTCYFuzpYQxD116x@fCAxM|=w$HA$}f>vs4h zaM`33%S&K3zSgFAT+aS}EmdD-_5HILUzNGU!3PrueYf^f+K}zB{npmusQnq$3u5id zz9o`miXfJ_}tp(1d6Y<;OitV_* zr_4{7{5wU4?6Yz;DMY?AaGje^bgr@z+5bJ1k;M9~to-+VB(W6#(MbQSlmwA4B;xk{ z=d8=|AJ)FOzW-GGr?`H9aUW%5PpW;r-+e253a&))3ArC;@i!Ymvrjy?FXDD8E+0Dk zEHi3nBl~`fx_?O0{INL_8#v9yU&3wNq9$B1-9JAASM2KkS>zVqn!gs`@{=piQRDkC z^^0;YWc&-v{L_yLnSaF1KQT8u#LYh`{#g4*`hB^I*Jqo*+Fxk@Fojk3lB``$o!I<;fUDH} zx}5yI=zm{;75nzvkWEaf* zyA~O5;~;MSjky&SxAiH-H}daey?!e`uEWMz437J6^Y;j>MVc$&>fagCwIy8R`*QdJ z_!J)HFd@@iy&B#I#O+%l@z=upCH&E@uj-$T?>lJ2#Ku?L#y93xRQu~UQAYl{mrTny zJ@U`nNk0RK`)B?Ye}MFJC0z6C!|=BixapUF&HgFNP@V*N*8Pov?o{%id*`?SY0vG@|V z_=>r;qvxy3&@bE6cgr_DvTr+l3&)iIz7@8Yw6(ycPa-b+jGLZgF!fpAFPi<*Z({bp zEYw$i%^*j!bA|IGl##9-Bt3)2qW?b5jc|#|SUs1)$4Qv_SQNA64!GH;`X;7F&)<*s z`gGj`d4P3>g)Y^+5K7k-RPI^ zQu;p_*{}FBG5f{UM@foH|DVE(^TSo(L;Nq{Qju!^GXNXkEmn!6wJ&b%r?~Wgkup{7 zU-b3m?W_J%;kNEryJm{`rSK_^Df=&jnf>aciRlqH`%_%{zlAbo_J3QXKjnXM)1Ttf zf3!ls_|0(Hmg?V$4cZrfEZe^$YF{yKV(XK*t*gDCWojH#{uRF;zF5J38-A>W%l{uG-3-_Gn_Zkbk^Vo0uW~GF|DWM^ zmT>v!DbL8VHMrLHqW()^7XRvliH(oA#z+!#(<82Xkj5^>wLj7OqvHBy_cz^tUVx6L zaoj;F-haLswuf{87Wd!kYwWbYDUOeE8-MY0B#HFQkT#QKU)A$Rjkc@e>m=O#w}vhg z^N+a2*P20&#+sX7QbhAMvXA5&ue#9~)ndPoE@_A7@CoZNv59aiOjjPo9BK~yvJr#Tl z{9Pqn_pj~nKPcf^-*&+tF5%iAUIV`v!?#eTXuqBxZiLSOj>C(;-_iZ!7Wg@Ms(;rhyY5j%{o=2HzpI2#!+!++KDfrTSYP~&@b_2nH^Dzt!ZrWj3jat2*ZuzwD!A_d ze_X=#{@_>9|EC=Jx6Xd?--Z8K1^-L`&c)#4Ube zZuW~?{G|B5b^O@)X#Cemk}qfc7bE{1ceVY8^U@Gm4A+tBwx<>bm`yANkx$k$@jvh{prM~@z=v`{@OE*3flOp?wf=D z)cD*(!MDN1QuFJbk^aq69K`g?{tpEGYTxp$oaq-!>HpB9)Gz-$M3Vkg|Nc7Culu`+ z*{Syj=jo3>k+{D3a+>^$;bJjYdC5lYKZUwBKB@MlKh?fd&c~x|{{>O|$4x0my6r^s zcQ*8Ymh*UwRY3Qr1-O{%SUpaih}Yq}xhms*_?{9zP5vw3a~xCt75_o_wIy8k{}}v0 z1^;Py)%tZmylnka`@aL%-oj#-Qy+H4!`3(PKPO3IE?-GBK7U6#$N6!R z<#X!f3oz3^W0@SSocjN?XM2$HpZFHISQ)<>UN(PJe?MHYTUNgY-(10OhCi)>-wMB^ zg1-{ZG%jubweYD5{$}`=5Zjs=3w6!EZ?i>>vgTi{PrE21d4MDvV$uAWpYrShK#^AbRF>Tfm+qo|#fQe{KDc6xgBSWE`Qp@gd^pX;#z*!&LrM_owtSbt zhxj+at?d-Q7G4$qH^Hmo|1{iuSJV%3{nPrC>fiTJz~WCjOiYjJ-=hyC)jy&8_^9gN z&%w+3r~2~$y8Z?K-%kOvuO=gcYzjw>&x2BeOpz>K*AmUIPr^A&*e`n|pzL#eY~fhe zzenI(OSt^^7KvGr|=bJ@Qo9BnKu{7 zMf`uk#me|a-{|9`j6VxLQ&E2he71y>oz>vSb;M|;SY%%dUgMZ*e*n9i^fv0LZ&uyn z>t5J9N&4+oivK3;k4bvJn&NuC_!Rs-9Ql{)=hVxVvHwSw$5CY^x<6b(8Hs$PHewO~ zMz~mttN+i3ALp3rzwFx%@57~ADVCSOEIuA9`);6(<23qzV_!3}uWe34SAGirPLf3S z=lL!N|H8hXBv!_M0{-a|zDWK%;WwjL_80XlzRZ7X5j>x=5^=pBlEmDKs(3 zXZfxIANu}Szwhk>RK~c)ub$oBhmKJRpQeu1msPmxoJVpJPCiUsvtRu)v35QKf0!h> zfMogBzVv?qE>_0XzRIWCzu>v!_NORgV(n=CdM0HgDgCnla(GqyyWwg(RbTe&{Vg460|Lc4vaJ~Q0_$V)~f9(G=H~*Ra z#r7X79?!41|K>l%zs2vl;1eBe{)yY~b7F4esQuA_5FgTM`KC(oc@s(fol5XqVfy=o zRhaCv`tpYa_1Rg<+{rQKVkURP*SJt!t8Yw4J%7C-uoV9p>c0}MWhN{&`9%MDnEA(i zMzOpd-9H|njAWLyStpT9bN)g2EXS1n;)?${j*1QQyDc<-OkLSf6zP&Y|1$3*e>MIz z{xZ1P-yh@Le(Ao2GLqEzsegJuA(paVyiI+r^JRP$uJJG9uY})R!T%Hdcm@A;c%y>r z{ljtv{|xopCH#NG%zx)7V`6cn{$Kn&pS#9k%Ae++;!lIGaZL4J<39y|RS8%97r^hT z;EJESi=3F3TuoiGf3weWpRxTfrHmwHpW^mLV zmj@*UOpp5i>R{g*=ToFu#9s$r+dBcT>2Fs>)!u{u{wxu^N(yWF*{YiO&Lk5eeu(9 zu`>Qj_(c``2jFG?c_Ym1n@z-*xMEO};u`;VQbw^kMH%at%1hM$cavt|srY&jX6?`9 zH;?gsL|ordNMdg7$i9!09)`<4%Xby{_}fUo-l3V;skrHnx#>~gO~3kR`KD)DC(kFL zFf~5nijNE68Ut%LLzm=Y>RSKrbvgdS%8A?eGch+i6d%u`%sGy6`xkjN?)}*d;IFOV zJK%39;b#9m1#b4Mec5mAZwuvW95<7yIQah=xBkWL$NC>@ZvHE4|FPox89R#~`P;5u2H%^&fq`K|VCeyjbWey#iGW%|FIo$vl%As4v+;{T>h|6TJ7GyV1a<1+n? z_*c2Vl)lLQ_W-^+(_j5QK68It>i&ZE;!J;M_QXto?f0Zif75mTCjD~cf=&8;>4lrl z=lr})`mN8VP5MQ8wn@KR-gKT;zw^)28kL==mEgSd)Svf|YO6Ot!}Hc(#bmYRd=kdE)MG5;mO{hlikjiqPBXlKDzSCY^m2?>2znpaeFvkk?rm5 z+_})Nk6QIM?Tvba{n@D3&-(SoOm?C-8udCl-=6Jmw|h%x|2FYV}5=)jgGkqP5Xrv3{c6%ue@OjjXc(;-cg0o#^%2S?9Vl-QmGz zr#Cp8b@r^BJlPx^88#dAq1LWbPybd-`B~I9yn&qp02LHtU0# ztlmAf($K@PYpR=Jlk)EEE&;V8Z<{um)RK?JKQ|!>~nT^?22j! zo5N+_?$EV9=#B7UubbiR<^X5TcN?vx=5S#&SXmk|#_lTXu5<#BSJ87#v)5^k24~gq z&Yk<T)4JL_@tlTxpL>FW1{Ay?NBwTY$dGo?aeynTqdQ9W}cn zwGfkQR(9?*8;*1p+Vfr9->tW=J8>&3#n2DYVQ*c>lqi;Jz4qBty{<3dr*7vclV?ZP zIdtM^b3j|2gRL$zp}ES6v3t)RW*{as2iKj{gw8ra!;rP(l#kKF^)p`I`kX^2_BBp5 zsdeatT2}*_db-Z_FzvkY*-;jY?h*^kbtg{_o2*z1D<>MQ(<;4C@ATWMn4cUvvAc0= zu-I2@iIZ7h!eCEwi@bsU^Qd3HST|dzx*f`9(d6vpGmchS>a3H#s&~PBSN*7G*Q{t3 z?XPQX#_EX+?@jBhO(F~&`yk3>hz518Px)pvxcmCUR-2&>%K@vopJBF0b!fMFxY^N4 zA3D9{JaEn7J-3Vot-M^gURUPixW0<)IeO(4i-Tq}JFL%I{S-HVR+k`Xb?IBHdx!30 zT-H~)tgqgY^+$uDFN~YqEswy-PwN!rCNS5k6K>o&J9g&g?SMF;QN3w)dz_zRWB*SU=TmjaC}XY|qiF+;F+Db5lqInsp{>A371-H%~3K zVG6GsFj#wg&66i{)nwJVzqu^#?Srsg;3nV6-Qrsv-$5AeV8+DS>L+Pfy|s9?M!UO> zJ!ePQHxA?G>dfS&_5soQ?HBaPEcQ++NORo!cITRnhlufjP!7XtfNBI#V>5Pj0W# zI(gP_plYkT-R>>yZsvN-rh0d&dCj1=qFvzLc8f)5elA;B+PTve-P;>9Gk3PoV*ltq zcAq*mXr97*^KgjvtZEd}E}mD_qmru-87_9L%#ixowu~^Dx})(X5hVmA=Z-Qe$`H zw0z*=t;NE{D(rWKN1NbKEFcHhlG@Y4-Z4qHkO8*cv6Bktux?L_6YiM-Zue|*n@e-{JiP+%ss_Z!=^4JdLyP44&)L>u3UvouEZn zQI_{zD{Lv}2CdW0d}$hHP4?5;Cl~WsBtq92tf>63*{}O$ZL!y8fn&wW;?|r2aYo8Y zfBR|o&Sc9x`>gAPvxQ03I(f3&WQW}d%MvzuXSIgAmzJ7p-p#S16JFY*w=zV}#uL`% zWUe(Bg~fZ)TL*i7$)T65z_lIK32dA2&S~wof22Dl&>85+> zp?NP&@p68v>@e4fHbygyjThiaT>H;-|B zY#*JWvUS9{(RE~;ooM6w1zB*Tn)9dMK0f8Gjk{Qs-fI<0D&?*Y5MB9&%EdvM$HDqs z#cL-n>%wy~NL+OnTyCH4`mnHcLdUQZ-dASDBnOb2;=k4#6_OZZdBV@9KYl>Q);^V0`EKOZd-S#??=0HsNTV4xh7xhP~Wx#*$U4f ze*2PV9qPMinDZMF>~Ic0Snu-a$Rp+CRoJa7Kv-Sf8W8GRG{vEHs6QX>8BUA$YG}Z= zz`^iv*tfdWUKz3i@@V6-{BGYlA|GxCc+7Ff?K~0>-CC^2-~4^w9=%}2E%8uK&OlAA zFx5KjJlijvoU)U3iS^sU5UejNI8Qt2YZzvPhuYjB97n$_sv9g8i1A@v_DjS6(;!MMZR z7J6CRJ@EJa9+prgz+mH@+n5Bi#^_dURuV>p_bF~a6z_P67Cj2uJP&VxTfDf5y5rso z=XtI_lUaDd#xbfkk>#Ve)wN7*p7QfizA3dVr$%b8_FKEKYFHfV4NxQOXuJ#C&PVS2 z7!J$_^v3vtJxIQl$;MlptnUNLZCRob@mofG=J#W6l>Assj${~xJX6oLY@6ILa#TIt za?N0ZQ{Buuyq&WC`c{>5sL2bT+o*?(JYdV|t z{G0HRz7bo1vhMN9gBSLE4WV5rvsu~uRwuK2CvNqNs;d)jY6AUr!|Z_uMbE(t4-Z*o~3%Hc>V7Ey?bTU({$uYha-KS8i)Ft&|S{; z&UEvPy(kr7mYeQH z@`UYq;e{1i>FS>5`?TO56u2QYyQfCHP4?H?**CSo&^$XL-9^+X-3fI)H|X)5tjex* zPx+Tbd+VJOygp@=6s&ettQ+MA9Ry;F?ayQrmg&pnw!5Brvu|iMWbMK0=Jht(z0%69 z-80~IlkF{0eQllBu!X#ZQB#|z#U2|O_qDHU(%x72_R*a;z1SLr*GdQKBlj$Hj8!t6 z-lzqW5#$v$FFD7LN;SSO<`wicgZgRrZqN5Vf08Oc;fv^9bclQ(MOX2t6yhY08{*tTOkUwqF{L;^Qy(-I>1?yjevelx zgiE0A_QkuJ+W7GpqUvel8qoIygQkC&4y6@c2YPiqRlB}PLzK60d;ChuDGA$Sy>bpc zQyuAui_-l=di&jOF>wV6tyXxy)oiRoJR=lK^WMU}!dqW@uGw`jzqNnLjo8gY^7Q_Q;d()+VCWFKIlfGti2U1!eFRClindD)$o&J=B*|#z6`-C6b;D z-OCqgckNoCQ@kNkAT}Elx$fd{_=SX<{~^2zW@`D>vhE}1%mR;rPO;^?o%VGD_n^?Y zwxzXirL8ZMU+T86`UX41+<4bSJT|oQtOF{2xPEqtH{MRKS4N!sJk!l>^$btt6GdE( z73f@BA9{;IO$_tjd25=weZbh7a9=x^PckMh_U{`E-1D!G7r#R!AeU}CD_0b&`x{ws z`MQ3Mmk-x=xXhJ>fiGsu*Jd2wz}sNuejB@17P&=aZ@ukakuuHniypmjalQ_!z2EZ@ z@nJI=HN&g&>x5SP!tTmVS5AHXd@@d|VzRY*51mlA{DrBxW4pH%4c~|}BLgjbv zY#!Ww?3P1&ZrOL}n)yTfZdttU@byRb-Lm({;iLP=TRc3!Z{a0l-|pp6Woelgu`7f6 z(%FfxPQo~kb?auTm+ z0W`w94%Xz#QtXb|!}x;k=D@ArH=BFHZPR=_xQ8bkxnFl||0IP|EjQY0<(0iHA)502 zQtTWi&T!P?O>X@5J+B|OabCfZzBQ#AXE@J`x=_hu%lp`GC$ZSuk#7sfqcz9NbnTG! zy7sCYd^>z`(q&z$4;Fa;%f`%IA84K&5unSXkSSiw^EY4jMWnCfPgT^N<+kqT@r65Q zw`)Jn@eP|_LX%;?rt0Bz_iDSjkblz<^+MAm^!=(E7;}21+fJdCLIqbcs#(B`b-l20 zZdDYw$M{gsH^QcTuCIdt>HQhDSmDHnC zRXx*_jnb?H;;t)!mHk8wIt#e65hW}>qL?)ZxcDSUTwkcOK6tq3;saC`b$`Eee!r?y z)#rA#m<2f>s_(hy|GoeJ`Tx)VoO|xA%5ZSc(Vaz6T!c?3m*DS>H^1PT3XV@6v=+t1 zLAv^nktawriTiSKPg|`jv{>K2S|>Lp?rSFK`?_^UiZx>@dB zC-)X{W#hp)z+!#(iTAkbG(!06{yiW8^D38r?_H-|@0%V@?B?S4I~jes`;7DIqvF}| zTweKp^ht4KI4_gScxmzJjNRP)^>{r|>j1_nY7#^?rYJQyMO#w$sf>h8eGn<*XQ?$E5<~}Pf9S@GZi~WAI$HV<|oGUPx^Vj`woTt!`i}Ta8aBbF+ z`l`v1~j%KduwU@yO}(dT^t-9pE@`95+-$sY&j5yWK~R$L-?M@t|!N>s{}7 ze2?_KSx5Si^TYSI9}`C(*eBbweRDq}?y?Eod&ON|!Z{Bm4zQ6YJiV zu}gnl{;767z6Yh32Y#%_dXGwO`)>D9;-aof8W-M^T&#D!aanKw%>5hLXG~bNJ==ec zAV}9+(H?e*a&|)A8ln>temu_r%0e_D`1^AM87yB908l#Cisx zDqkV)DPqdFE5tpug!6tV^dM3q-a{<+C7wrDi6fJnM~>ePaa`}YCRiWq zEH9D#6*^9%EysC(&=R*9+(`VakV;X`h7z8K&vt)&f?b>VIdKes zZrr8>I-fl^+%M}f_W|iE+vhmWkBDRbR8xNHT3i&D<|JRXr6 zGEb8pbCkc$5RP6~&G~)EIOjN;^ZN_-CDmun>sm$jE8zCwV>zm!OzHSMaSJ$ST*f)) z!Slr}g1IE&SZDEi|6*~=Xv^ulGtfn`OWYke-;B?=-2;-pT*vcZa(2C*I?ntaiMl*i zJo4l9+1y@nID}%}=jL{!fjQ{H_pE98r*iAH`)G3u=rqT}W!#Uo@}Ac@5A5$E+NPz9 zAz_^{^mDB_BIo%>#d&Wo&HM9)aCAPHOY=U*P3hXx%9jex*>CHSc<}zdCvI~I=X_Za zw*?&iv_95ZysrH_ag0Nb8{bFpe#tf3`cNGyZm13LEP>M+<9^PCU8F@?!W}@ zr^Hn!aK3*WE#aK^@0C8@kJd9kY19lEak0Kni@Ur;pYL0LFRoIe&)gGe z3}p*GYuSBj3tE$g)F{CkIZ z_A6(1n>zec;>zs)V#e-1UCV?0b^d)QvCFFEZnhs6)W8GdXAad+9G5>6S2jP)eMa0C zT%C)b<5E06>c0|)TwKgOMI7Ul@|o+GHn6CB3# z&v9`M^2pCO%U+tN9wUec?<35m>uJi_AFq=h-}BHhjgRH1hT?oVEc*|mbss+C-0@8R zIBqWHJujQ$DbLN{p5!Y!nz0`1HTODkyswZc<9yxt(S7{^>G3*K#=TSA)HtAX>-%MK z>9{d|F7D^}^CjiX15g}BWVxX+3!o4?MN zi?}eOF!wIO$NgiS#r?Zf9OrM&506_#+>5|DXQ;=;P*PP#vH%mB=M^*Y3 zCvdM2*Dv90_msHh5^ntYZ3WyV_}E|8S)5lM5Bsqxio;sUdQbM$yz0Ohv8=327nh6q15uPz-*jvWf89@W)=OcIyG;6~bfnD+KGuV3DCSbT70Gxo@87L| zjr38*wbyc(mi(&jyoQ{}v=q{WuTS#d^)zZ_3Xb z!qJ@lyyzF*u9aL(17zWc--n4s?`#8oHgd%w7wO7waC z`-r#`6S%(+H$Q>9m`~2E$+J}aJP%QyM&uUJIi;8`ySMD{+fG>xO+-C&$pivcQ3fyd^7hx zaX(PP{hH*Scfdvp`^w^VBc84PYwmZXFP-NPCAjm!T)Ix?IQK7IC(lEl^;q@CWcROi zydQ$xeQP6cEGUk5JRAPCA5RzOI1Y(eGxuC^slSy3c0Zl>SBob?9hUnNbGyWmS&KU& zuI&D6KfFI>cyjq-``;>kW%pHcU2%*LeHi#b&2BlSDcqT zjWcz+*uM4ssCdeA@iX@>ab81R&v8B}edodC z;%u%h?yVEJSBiUE33rd=jyJH8;`rSsE{)&ugtR}_=Y1lP+_>4^PfIVE5soJ)-XHw3 zIQEBg#P-OrnEP#U9E%+HamoKd#|pUI`to>n@L4)?e&o34O8#OUr%Sl~lHaW3wi0d% z?JT#5+kyI8dC(WPdjj_war-84-z)Aw3HJ`k{hU_?my64T;?jKC4A#Zt_v_-^K7OR{ zERM@Z6FwIgbN?WY%v#*jnJ6gqA;;OApHrAqBYDb;Q9Li7FMT}Z^qKR%)4%6(Jc6=B zpZA?*_c!bFzO!t7cAR}bufPUvj@zA4|I=}s2IpdaR-E&I^0o57>mQlT_*f6GW0{JF z!vEdVl{nAEZ!Xh6>U6PQ$K{-O%5(b6eVaHkIqu(P>?W!Q{#f5Vi7=x=k_J$ro@rSan6IU z6UX&*8Xx^!XV4js+g0KgAUToYw(5#+6!!s~bIy(HGq+RRg%Xb6TeIvJ_qV7sq>e9T zEY^2S9RGXjT)s?2ytqZ&r6ruO;*7XW;ON7+ABE)KrsLB~xUEw7YH?p#V%PcdMsXEz z_}?mjou@x2ZmNW{ANPy9qJ;Yy$+yQX8@ipi5pgMv3JLx+*fxDFd;R4F>3EWqSyJ-UVjp9yB;0}tLFX5aA zH;Y>UhyRu4OH*80T>S@elkWSTZ|QxX^UZp!*Zah$@;{tGxf=lQapqWO znUeg)I^I*lT_^ceylmB9-;Zt(m-@@~-o^HB7I%w|lxO>=3vUxgCYM*Ql>AN|xwho= z-7Wc~{+Y8s>_*Oy?_Z}rbJkm?@9i0VRl+KcpE>KLILG<>j|Zi<0&W@~>p?Y?_h;-@ z63A6^sofms_lX}&>^=lb))TE3h4&@DE$#vYxqXT63x6i=bKr9K1#=tu0Dy7{4CXkm zSI-c48Ms_tO+_`umE!mtinX{M;>zyl*Grz}-@~%O1Js9NF3rCj=l3r+NpA&L-;a;= z1ZDU$m$7@dg$ZviwVUJo-l8YHoEygzJyz}Uc%`^S2y)|L?z_d^QNsB>$Pb9S3taBG z@jl7ZI9J^%933BXslPdHW>dV_>nZ)6PV_|Sa9`rQ+6O@{U(7qN4oH{#Wx1a(e^&k+ zEa84LF;?cu^#vD(;2&ri;H=4soflR(bM9Un{=ez+&Vrb z1J4py!Fevu=AI{Ra|!2t(+kCI0mr_(|EPwtTk~JuB{L31_<=w;kZ&r7GnV^gpT6e$=kL|tC2ksA1s~f( zHIxT4eyk*r{Vy%Hj5qth*gLO2BYkKZDR=Aa?o2?a~=3F1^ zFEN+S-yFBz@%Y#3-;9pT``kSDzSH~3X>bqY<9^wH=jmYm1CZ1Ab-G}`>~cSzn@83+ zDer5UI6vS{;pjMxLrt`@VJ>v$1UZ2iMezg&vEM=w=dTH(s_D=;xVgBW?qB-cQvu}ljD2A7 z`t0YP3fic{dQc6;>xQ3urqG&OpKaIAJzK!#{N?p5e(sq@eXYFmbI&y;ob5U;6>#*y z{Bd>!#q-=8+i2Uxe*51W{sWB%<;(@k&71p}I5IiT{{D@)3b>rV7hfJVKU2rD_<6lg z{iw!E!(Xqb=F+@RIs4=H7FWtHeVE3_a;vr;C_mFQEM*-RKxHKhqd&u)a4dxMhBrdyniA&)NM@#$Ud_cd@_b93RSaobCRh^fGoiyXHP2j)xrg zsZ9Si)3}QJXD;nuj$@yXK3UQSZ?n=qid88184%=UI zslPeScE4GA%lzGwv6~xTbE(}NXS+8|uxsw5xUx7eth0aSzR>+!$@DKB53l3q()(7* zd3@KqzrRa%{d>HSB*`DQ1p?+?W-qjjzReNNmRC7k2@jAusuD>&r7x4J^|bp9^JOTu60 zfjQ>`g(+u$%z2-{T*-0X$NGIv1zc`l;{Cz2`nd(%HhjkS2j3+5J{?b#=yRTUU%XJl zIjv|>k1z9TMM3#|^6Io7ku4UKz&J!h^J)fN~X+Aq&KA(L46UBK76^t(12_S0Jbu2P*TwO@Xf7`PeNIo@+rjbKaon!C*NWpi z!W=h+Ygz6U_damBb;I0S#PNONTHKF_`;`*T>(#r&mA$t$=lx&Vds}nf|CPO;^!vp3 z%I-(dKm2c1{LKA^xZj(=eL~#tPvHJW+#i&1kLRGHc->%qnSWHgo|;S7)0Cr6EauYn zG{G627;={F+KH5+E%3{v%fp0?_W9GPdgY5Osaprn;LCQX#_4x|*YtlY; zTV{M$VB5w1m`m5^6+<|h^E&xx?PJI15#yYj=l0k8gFd+2{@|q|il?jontM}b+}JM{=au!m zBe9#ymwR==Pl_XxTQ}aFvAfAeakO3g`@zI6`)9c?8MjL&XZP11WxI~^N2Ql`b8-1U z>$L0liI?yn9#TpDWu3+2@f2}ocE|fy_C3Tiq{n$m8)RJ^Z`)1#m-E-0`$s0njrXt2 zkLgVRPPlV8x_@4;_9pS$0ePBRyv+NEmx&`o9oBJk6^L1E}Kr zg1J8yPjSlm66e8Ri+c#%34AO^HI$9dj>pvQv}8P(cl@3yp0eC{n0uZ$GHY=!5?7Yb z)^|W$*}OD&OdRugExRYhT{eMR6nFUq?k;g$zSgq)ec~z;xOa%#T*6JEoyB>z1@&BB z{dehE@A$6w|Ih0~(o0|0^4I^rEtAIgx1?{<`2L=_N#pyc;wFvnXT?n#-zR+)qaEKx ztmW_J;woU*;;s<4c>?zh;yAu*>2n@jInHT)`9|qU^I!*9m+^W2Qt_0pW%rmkGHY=) zag*}kHR2}a!4HXJ9&qf&-y?Z``1hTY;`cMsw<>BpZIK%?~SuPTo=0PP$ z;vWtVi~XorG1_R;axyIDt`_IMm2ro}O`5+oag*ln9pXF|<8~z;#D(j4E-v3GFpUd! zx>&FEz1bB2xwu$-pDL5$^54Xj#l`wO&sW7o=%iI}aYQVzgHhxv;|P z#-EBK!#-LM>nv9DSyi^6ZLPRmd_}mdxUz)vcsxxU=j2*;pCgXz-&%IB5%;1A+)Kpm zDB+y<^Wt`waE{B0xP242H;FqifqS>O>IBaDauginw-KLERPp#aUn&ri+ky|{Nc}7x z4|5(5$J^Y8)h~0rwlXj}%p1q=e~Tx#2_M=_i@CoQM`kVVNzaK_S0-@J5jQ=7yH?ze z5)RFw920i}XN#B|{G`Qxbi`F5%=xh_`5ScP_c$C6t7DzzzexUW9r^uCj{9ZFe_zK7 zC7hqTKPm3x;AoSdv`p#5dG$#Ej3L*fP&C$$-5gKTJm?GI!Fqi^f3|qasBb(E%zdpm zGC9t1{swU;z-`Ltv%fDCwp{z^q8Op-|;z*_3t*aIputb!0mz{OYKIOpbekt%@wl<>So-~Q%BA{_QyI#? z6wc#ef6My!h2lKV9Y4k@jbD`wD4v%o_fU|;KOSdu>F!d!E}aOl-&Ov^La4z!}?ffah|?X{M*o$%liQ1&l|+O9d+umKFfXd`;8x+!2Ou? z{TMhdt%K!N>(!4-p2pc@KwGx&`}8k}r)&p4n^;MT{r!-*9gyYxHRt)e8(ePw{=W3M zpNXkZrEZpQcy2seZ#*0RHFv$Z)L)LVi~Y5|x_HVN7t75%E`E5a zwK6;nHFM7tw}RGO{HAo>bH%;7gtNYBad%DNoX_7i&S}2ANP1F#H>oc?jQd*^Pg%|n z=l#pYk;!rPcR?K2ryNH+EcSzQ(mI1ce%z7qW6*hk<9@t8@nZ$@3O;FZoWEb(3WWSr z@JY)LXY6iLpUBy+=lO$)-Ni(1ozB1aizCDQvmSFEw_mY7035e*&T)A}+$I9}jB~#y zd71|m8%y*$E`MjckmbhB+~c=IB_6YJyXKxPt}GACT_w(WP^Rx%ag51Y{o60@vJ!48 zc7O2iM0?*-X4iSTYM-c_n3cUW&g`R$_1fRAc*@uE;|_6?;(V95OD5?1kK)SW{1(Ym zKXzpD-kj$N{m607zjsS7=THT#(vyzwg&>K4%$xJRm(mLn34h&B=ks%<_aR6x!N+peSbFzhh6#ty!FEc4}-oR-d~X{~^J-;`VrQO>q{L>z9IX zrnA(Z?_FKA8tq1Fsa3RU%i&mEKH9Cdd#5_xR&g8tbJgnd!CL?7tBbi#bE(yy)^V$j zR~5Tj+qcgy*7}WF6ZZO@?%tx`Su7T7^V7x2PQTx2vA(s~*=%;^YLFMR3$^*q*_lTF zU~REzg+DiTy7Rlxp?=XCa;<7@e!kH@T?noAyGLs$oAu&Mr!il&W+B2!D_(c9(`gp1 z!)M#QgY{OYd#-5BES)-4@7~a>&%=jmv(~N`?Sl1Pop!I^ts!jt+h-2edcE1X8Wc4f z?Ru>{UDVpAmzu1^hOKVcic4o|z53o}?Q{?IqvsauYGNnabKQEs9vWLi>`47o=wIk} z%V)UkV7<2x?e>E8Zl{kJcG?A^UGE~8``h!4xq5H5-(8yPV~oRD(Ozmvz^L-`f@`4d zx_YNo?|0A9{Pyj8K%o(RI#UmZERJ1|@n0=1T5@S9!YEru(L#@-0$Am^?Co}1u_0ct zZ>ibbUF+A11y@clF74{Hm>e9oT#pVMP%Ls?Jc>AQj2SD-fv3$yKVgay3Huwm(;AH^ zGCf8(3}SM*O`uOPMEmNE(+mB*je2w5GMG zkah*}J%0tbEiA) z2*e58I)*8tEKG1{77du}Z!;;aS!!mRD)Tv$_>(=bj$@#_^(zCVJ zVv`pRDu+()oWDgqju$p4M`LXc9gdPgk75Vo-(ztV!|K^M-EM&`lF8W`%s5!Zf@de= zr7;Bi+w`MWT(`tow713;8oeGliP5yPoP=LSV*PF&>ddn_)P=-D=%-`I*cYwPRLpjkx^rQH zi_xailK`njOH&Uw_TdTc`nZiSlDLnz`{>ks%zTc3U4^h^ z1i5Mz!WJPj$Ec=So4d6>e|>QP_w;6rs3%|H9vrNlZa4Z%^Yvop*fn9eLfVBX!~rc@ zBUc|fsqpQ`RkUG+hr1Z8U7h-=Qv=m#s^R_?nsA5jYSVyQeQOYwXmxl8!Emb^BWr7% zq*}eXcs2Sv+w(K$`sm*>rBNMxE&*M&nJGHQ*^Q;S-B&~|kyIIUJ3ONXK^NmyI;x?+fyff_Kw8z=q!ECCv=jzvWJ4@UP?rJu$2<_ip%+77!9?)H# zZoLS#*#>rz;m^*~r@Qsjh~EC)*pbz{@hZ&yq3&3Z+odrpRDGmzx`7=bRP$_>d!xB< zT3zl9W!QFsIN6y$cNKP!hzK^oyl@6BaYS1C=leXnDwr;Mo#vT(cRyCAu-OVe_jI?1 zW|Bjd@shc@`JMA;7=w^+4J=$(h2!4vSRH%XMn5bv)p~oTQR{iPI1pjr`n9?Kk@_O` z?ZdIdva~a9G)>Ol#Bp$Is#s zX1d_tt>N!g;qTQ*$sYDgiwG@J6?aZOTAkL8ojYvihqe*eo)K2^ruFb>;{|CR+xqyF z_=M2fxwwd>V}5T3w>vBZ@eJB;ThfEU;QDquJVu6RDpga1)ozFTM%EBD_u}&@c#G z1b3gq0?93;q8i(1Ele*UNbOX?D&Q3wZWfUgZ^)|vTd>fzM~=RGy0%lR$rm*&BI=hXgr?7xCJF3F*5UXhD=_peyQHmwD{ zb0KKhdju`vmR9PwyVmJ7LV#jV2g-ZUiAB>X!H4H2b}$%8XG7A|I}0}0)pR@D^cqFjT`f-#mubqwoE*xY&+_Fa}44%^4fl&$0(Eqq$gp`LJ5W+X=QGrQUta zrNP+&WY^$?T$+@FJXqbzd~Ky{NYBwE373cD3j6fn!|dEi9<>w08SXtyzfhaS0~i)0 z9q44YgQ#Iy#mardw&y|A$6Co<5Ckn<_!kj3y07$T)4ub ztCbj+hc{@0Mcb!ZwOcdY&aL$}H7hd7(l)#}9k8SqX>%B~q7@IJM}rL{24PrljhqbH zu^Z)f77y9nUSj6X)|+_i)@*XmduS2f`HrHoO4@dIyETqX@Dx{u`vTr~)WWvz&|)0z z)}dMpAsc9lR~=gPcA!|oGf3RN44MuthHTg!Hzeps2>8KT8;_27q#QldZk-8&)itaE zy58B8rq-dw{kmraEitMx;4QE^uEUO5I|%TY6OLQ)NZbo+ zF&}>i@B3!>niXM5hkA4da%yR+wXpMSK7Vw|j$Vsgzg0u9nx(KuV}6=Im!iV)hB!P? zxT`j|P{)f4Ze_-LN`-iO#`F7_Knb|NeUk46bXE=b8H_q_GO;hM0|zuxDZ0CZ2h(WW zib6N7K>eZ`b75VIIlz478Wc8c+5+FkRi0hhKVQt=wtaiGyVS;n#!7^D!AsajV)HZJ zl0<1R6EHHI5*pH85xN&_%w2U7cbVkvcXfI93!<2}(3n!AZG6z2tA|?zk}R;{1a4w> zV6W1{lh_h&Kf_0ixwy9-+?UeJFq!JbIjn1VGkkkIq~hv1XvJ&(NMOI^8eY~!oNcP1 zZE!7L;>S*&sWrpqQe7Pgs3XIxuJW@Y{=kb0$sEWZE zX$mn7cg?{n9nQk0DE20tUx&^e!EK>aG{Xb`V%)xsiMllu-NMaErW1Of z681ysju+X&M?ueXy#a3E#ZBT)cq=?;3;v8YAqpPHeiB^i;pWkk&4 zek_bq9E;JB)F=!Z`CN-_Qy4}Zc^z-L?l6**ZWb-PowC2tDq9ZK@xo`&eXIaz<9rwr zO8?N%D-;sG`J2PafJBTDL-4BJgeHSB+7?D-w5izJSgyx)E=Y%`&^Q~Ob>poP>koE8 z_=;(yF+OXyw{OSYtZ@oYkbAIeI#+b!oA5rr7n_9?>mFWt;DtTDhTyIgvzg6%mPeaA zCvS<1YPd!>w!u~0cXbCUZf&%q65jv^-2Qp40mteKjk#uhH`g%Sd1FE%vfR||#IscA zG+w{Qc<)^5cQ_q!#={MZcxvqN%ffKFyK}ZZXxO#X5U8Z@FIDn?*n*t30B&?r$wJ;!-E2D2=(^qKHetB zbMEZxTwq`xJ0jji=oIgSJm1~z;G0>pOYPI~CDE>0>m**EVw9MyJ{!L_X_pQpVguWs zQ4?;A%%JbVv)Jt$8}sb#!NdFcHafi08uWIii`PxwTSE1|8eYSWwaxbH+&mraU?UU0 z{0%1kz9QO(?t$r}jjmoR9jNugv(Ry@lB&Lp3noSouc+~obNHxK#kar zi{1~Oq{hX5xpWL0@5UmoTIAb(#%EpHGna>pm$zaKS?ck8|Ja>vQDmZ0MG0)G|y7NcjY`uy79$u{=T_g>+FTAU%4j+#p;pKTrFu?B#y7l-l zEt=VNpi|>hb?}WElD65~(pOSJiMGdl;~hTd=0=U0jF zj4)2)y+wG1x0>EvZ-LEu@eu~NAP4ZQ`2@obQVJtj@UB~vSfHo5cLKlGTMb` z-9s}Rw?il6W!LcgAZ+5Wp2Y^NE5kk@o{%Qz37$5G4d6fpzTv?4M}FiP?AAuIH7;p* zQpZ3uKW5p0uQ)LQ@jw-RsKJN&aw*1v3OOaRk(7IbMLL`ZD{u;Lh?t1=`H|LeazwwA z2=ia*HP%fozg*U1#+;qSV_;D1_OR1F+zk&3^ZOcH`<9ygGWjK8`^s;ym2<;g6Y_DM zE6*wc=_9psb9mz&^hRaK^VnwHxUHVWQ~3xIS`4)I)q1f4A`)`$)^iNS_~r3NrjW1lYq)#_+d3U6)4-26GuG}LzJd2( z4dONoEqfFekzKWBctwh7#^3eug-eLFRL8gvMkJ=qXx8+u8JuUdA}-inh3SfFynZkl zN7Z2@`Rx(3@jC?0twXrqaxc-2AinL#YeVm(kKjEg9(Mhm2_KWE^mPEqskPa;x5ppGIKGzz@6l+pFXr{_=XaV?pcyjNn}d*5oxQ?2fUAiD0-n;MO0_ z4k8h@X)zzd!xI9zmv`*=B!!^5HQ8$oE{=UsHVyVmX>c%cdi@68U!M1QXTD$QwojYW{uD#~QXa`@Mw6QMLy0duyi;Y=0 zKTto_M}jW&)kyhxTfF&(FCybbu}U=527TR$#~0i=+im|iCmMEKWK#PzUasoS#c6d&q@R^D*2BlOK6w8ivA p#FNcqvZkluKBRDu-VrfLN9H_!8Zy>2h++Wc^`RSw-3#q#_ +#include "foundation/PxSimpleTypes.h" +#include "PxPhysXConfig.h" + +struct PropertyOverride +{ + const char* TypeName; + const char* PropName; + const char* OverridePropName; + PropertyOverride( const char* tn, const char* pn, const char* opn ) + : TypeName( tn ) + , PropName( pn ) + , OverridePropName( opn ) + { + } +}; + +//Property overrides will output this exact property name instead of the general +//property name that would be used. The properties need to have no template arguments +//and exactly the same initialization as the classes they are overriding. +struct DisabledPropertyEntry +{ + const char* mTypeName; + const char* mPropertyName; + DisabledPropertyEntry( const char* inTypeName, const char* inValueName ) + : mTypeName( inTypeName ) + , mPropertyName( inValueName ) + { + } +}; + + +struct CustomProperty +{ + const char* mPropertyType; + const char* mTypeName; + const char* mValueStructDefinition; + const char* mValueStructInit; + + CustomProperty( const char* inTypeName, const char* inName, const char* inPropertyType, const char* valueStructDefinition, const char* valueStructInit ) + : mPropertyType( inPropertyType ) + , mTypeName( inTypeName ) + , mValueStructDefinition( valueStructDefinition ) + , mValueStructInit( valueStructInit ) + { + } +}; + +#endif // PX_EXTENSIONS_COMMON_H diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h b/src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h new file mode 100644 index 000000000..88dc86ac6 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h @@ -0,0 +1,186 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_PHYSICS_NXPHYSICSWITHEXTENSIONS_API +#define PX_PHYSICS_NXPHYSICSWITHEXTENSIONS_API + +#include "PxExtensionsCommon.h" + +//Property overrides will output this exact property name instead of the general +//property name that would be used. The properties need to have no template arguments +//and exactly the same initialization as the classes they are overriding. +static PropertyOverride gPropertyOverrides[] = { + PropertyOverride( "PxShape", "Geometry", "PxShapeGeometryProperty" ), + PropertyOverride( "PxShape", "Materials", "PxShapeMaterialsProperty" ), + PropertyOverride( "PxRigidActor", "Shapes", "PxRigidActorShapeCollection" ), + PropertyOverride( "PxArticulationBase", "Links", "PxArticulationLinkCollectionProp" ), +}; + +static DisabledPropertyEntry gDisabledProperties[] = { + DisabledPropertyEntry( "PxSceneLimits", "IsValid" ), + DisabledPropertyEntry( "PxSceneDesc", "TolerancesScale" ), + DisabledPropertyEntry( "PxSceneDesc", "IsValid" ), + DisabledPropertyEntry( "PxShape", "Actor" ), + DisabledPropertyEntry( "PxArticulationLink", "Articulation" ), + DisabledPropertyEntry( "PxArticulationJointBase", "ParentArticulationLink" ), + DisabledPropertyEntry( "PxArticulationJointBase", "ChildArticulationLink" ), + DisabledPropertyEntry( "PxArticulationBase", "Impl" ), + DisabledPropertyEntry( "PxArticulationJointBase", "Impl" ), + DisabledPropertyEntry( "PxRigidActor", "IsRigidActor" ), + DisabledPropertyEntry( "PxRigidActor", "ClassName" ), + DisabledPropertyEntry( "PxRigidStatic", "ClassName" ), + DisabledPropertyEntry( "PxRigidDynamic", "ClassName" ), + DisabledPropertyEntry( "PxRigidBody", "IsRigidBody" ), + DisabledPropertyEntry("PxRigidBody", "InternalIslandNodeIndex"), + DisabledPropertyEntry( "PxActor", "IsRigidStatic" ), + DisabledPropertyEntry( "PxActor", "Type" ), + DisabledPropertyEntry( "PxActor", "ClassName" ), + DisabledPropertyEntry( "PxActor", "IsRigidDynamic" ), + DisabledPropertyEntry( "PxActor", "IsArticulationLink" ), + DisabledPropertyEntry( "PxActor", "IsRigidActor" ), + DisabledPropertyEntry( "PxActor", "IsRigidBody" ), + DisabledPropertyEntry( "PxMeshScale", "Inverse" ), + DisabledPropertyEntry( "PxMeshScale", "IsIdentity" ), + DisabledPropertyEntry( "PxMeshScale", "IsValidForTriangleMesh" ), + DisabledPropertyEntry( "PxMeshScale", "IsValidForConvexMesh" ), + DisabledPropertyEntry( "PxGeometry", "Type" ), + DisabledPropertyEntry( "PxBoxGeometry", "IsValid" ), + DisabledPropertyEntry( "PxSphereGeometry", "IsValid" ), + DisabledPropertyEntry( "PxPlaneGeometry", "IsValid" ), + DisabledPropertyEntry( "PxCapsuleGeometry", "IsValid" ), + DisabledPropertyEntry( "PxConvexMeshGeometry", "IsValid" ), + DisabledPropertyEntry( "PxTriangleMeshGeometry", "IsValid" ), + DisabledPropertyEntry( "PxHeightFieldGeometry", "IsValid" ), + DisabledPropertyEntry( "PxJoint", "ClassName" ), + DisabledPropertyEntry( "PxDistanceJoint", "ClassName" ), + DisabledPropertyEntry( "PxContactJoint", "ClassName"), + DisabledPropertyEntry( "PxFixedJoint", "ClassName" ), + DisabledPropertyEntry( "PxRevoluteJoint", "ClassName" ), + DisabledPropertyEntry( "PxPrismaticJoint", "ClassName" ), + DisabledPropertyEntry( "PxSphericalJoint", "ClassName" ), + DisabledPropertyEntry( "PxD6Joint", "ClassName" ), + DisabledPropertyEntry( "PxJointLimitParameters", "IsValid" ), + DisabledPropertyEntry( "PxJointLimitParameters", "IsSoft" ), + DisabledPropertyEntry( "PxJointLinearLimit", "IsValid" ), + DisabledPropertyEntry( "PxJointLinearLimitPair", "IsValid" ), + DisabledPropertyEntry( "PxJointAngularLimitPair", "IsValid" ), + DisabledPropertyEntry( "PxJointLimitCone", "IsValid" ), + DisabledPropertyEntry( "PxJointLimitPyramid", "IsValid" ), + DisabledPropertyEntry( "PxD6JointDrive", "IsValid" ), + // PT: added this for PVD-315. It's a mystery to me why we don't need to do that here for PxConvexMeshDesc. Maybe because the convex desc is in the cooking lib. + DisabledPropertyEntry( "PxHeightFieldDesc", "IsValid" ), +// DisabledPropertyEntry( "PxConstraint", "IsValid" ), +// DisabledPropertyEntry( "PxTolerancesScale", "IsValid" ), +}; + +//Append these properties to this type. +static CustomProperty gCustomProperties[] = { +#define DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY( propName, propType, fieldName ) CustomProperty("PxSimulationStatistics", #propName, #propType, "PxU32 " #propName "[PxGeometryType::eGEOMETRY_COUNT][PxGeometryType::eGEOMETRY_COUNT];", "PxMemCopy( "#propName ", inSource->"#fieldName", sizeof( "#propName" ) );" ) + DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY( NbDiscreteContactPairs, NbDiscreteContactPairsProperty, nbDiscreteContactPairs ), + DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY( NbModifiedContactPairs, NbModifiedContactPairsProperty, nbModifiedContactPairs), + DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY( NbCCDPairs, NbCCDPairsProperty, nbCCDPairs), + DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY( NbTriggerPairs, NbTriggerPairsProperty, nbTriggerPairs), +#undef DEFINE_SIM_STATS_DUAL_INDEXED_PROPERTY + CustomProperty( "PxSimulationStatistics", "NbShapes", "NbShapesProperty", "PxU32 NbShapes[PxGeometryType::eGEOMETRY_COUNT];", "PxMemCopy( NbShapes, inSource->nbShapes, sizeof( NbShapes ) );" ), + CustomProperty( "PxScene", "SimulationStatistics", "SimulationStatisticsProperty", "PxSimulationStatistics SimulationStatistics;", "inSource->getSimulationStatistics(SimulationStatistics);" ), +}; + +static const char* gImportantPhysXTypes[] = +{ + "PxRigidStatic", + "PxRigidDynamic", + "PxShape", + "PxArticulation", + "PxArticulationLink", + "PxMaterial", + "PxArticulationJointBase", + "PxArticulationJoint", + "PxArticulationJointReducedCoordinate", + "PxScene", + "PxPhysics", + "PxLowLevelArticulationFactory", + "PxHeightFieldDesc", + "PxMeshScale", + "PxConstraint", + "PxTolerancesScale", + "PxSimulationStatistics", + "PxSceneDesc", + "PxSceneLimits", + "PxgDynamicsMemoryConfig", + "PxBroadPhaseDesc", + "PxGeometry", + "PxBoxGeometry", + "PxCapsuleGeometry", + "PxConvexMeshGeometry", + "PxSphereGeometry", + "PxPlaneGeometry", + "PxTriangleMeshGeometry", + "PxHeightFieldGeometry", + "PxAggregate", + "PxPruningStructure", + //The mesh and heightfield buffers will need to be + //handled by hand; they are very unorthodox compared + //to the rest of the objects. +#if PX_SUPPORT_GPU_PHYSX + "PxgDynamicsMemoryConfig", +#endif +}; + +static const char* gExtensionPhysXTypes[] = +{ + "PxD6JointDrive", + "PxJointLinearLimit", + "PxJointLinearLimitPair", + "PxJointAngularLimitPair", + "PxJointLimitCone", + "PxJointLimitPyramid", + "PxD6Joint", + "PxDistanceJoint", + "PxContactJoint", + "PxFixedJoint", + "PxPrismaticJoint", + "PxRevoluteJoint", + "PxSphericalJoint", +}; + +//We absolutely never generate information about these types, even if types +//we do care about are derived from these types. +static const char* gAvoidedPhysXTypes[] = +{ + "PxSerializable", + "PxObservable", + "PxBase", + "PxArticulationReducedCoordinate", + "PxLowLevelArticulationFactory", + "PxBaseFlag::Enum", +}; + +#include "PxPhysicsAPI.h" +#include "extensions/PxExtensionsAPI.h" + +#endif diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h b/src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h new file mode 100644 index 000000000..0e0cfb1b7 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h @@ -0,0 +1,91 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. +#ifndef PX_PHYSICS_NXPHYSICSWITHVEHICLEEXTENSIONS_API +#define PX_PHYSICS_NXPHYSICSWITHVEHICLEEXTENSIONS_API + +#include "PxExtensionsCommon.h" + +static DisabledPropertyEntry gDisabledProperties[] = { + DisabledPropertyEntry( "PxVehicleWheelsDynData", "MTireForceCalculators" ), + DisabledPropertyEntry( "PxVehicleWheelsDynData", "TireForceShaderData" ), + DisabledPropertyEntry( "PxVehicleWheelsDynData", "UserData" ), + DisabledPropertyEntry( "PxVehicleWheels", "MActor" ), +}; + +//Append these properties to this type. +static CustomProperty gCustomProperties[] = { +#define DEFINE_VEHICLETIREDATA_INDEXED_PROPERTY( propName, propType, fieldName ) CustomProperty("PxVehicleTireData", #propName, #propType, "PxReal " #propName "[3][2];", "PxMemCopy( "#propName ", inSource->"#fieldName", sizeof( "#propName" ) );" ) + DEFINE_VEHICLETIREDATA_INDEXED_PROPERTY( MFrictionVsSlipGraph, MFrictionVsSlipGraphProperty, mFrictionVsSlipGraph), +#undef DEFINE_VEHICLETIREDATA_INDEXED_PROPERTY + + CustomProperty( "PxVehicleEngineData", "MTorqueCurve", "MTorqueCurveProperty", "", "" ), +}; + +static const char* gUserPhysXTypes[] = +{ + "PxVehicleWheels", + "PxVehicleWheelsSimData", + "PxVehicleWheelsDynData", + "PxVehicleDrive4W", + "PxVehicleWheels4SimData", + "PxVehicleDriveSimData4W", + "PxVehicleWheelData", + "PxVehicleSuspensionData", + "PxVehicleDriveDynData", + "PxVehicleDifferential4WData", + "PxVehicleDifferentialNWData", + "PxVehicleAckermannGeometryData", + "PxVehicleTireLoadFilterData", + "PxVehicleEngineData", + "PxVehicleGearsData", + "PxVehicleClutchData", + "PxVehicleAutoBoxData", + "PxVehicleTireData", + "PxVehicleChassisData", + "PxTorqueCurvePair", + "PxVehicleDriveTank", + "PxVehicleNoDrive", + "PxVehicleDriveSimDataNW", + "PxVehicleDriveNW", + "PxVehicleAntiRollBarData", +}; + +//We absolutely never generate information about these types, even if types +//we do care about are derived from these types. +static const char* gAvoidedPhysXTypes[] = +{ + "PxSerializable", + "PxObservable", + "PxBase", + "PxBaseFlag::Enum", +}; + +#include "PxPhysicsAPI.h" +#include "PxVehicleSuspWheelTire4.h" +#endif diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat new file mode 100644 index 000000000..651051768 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat @@ -0,0 +1,132 @@ +:: +:: Redistribution and use in source and binary forms, with or without +:: modification, are permitted provided that the following conditions +:: are met: +:: * Redistributions of source code must retain the above copyright +:: notice, this list of conditions and the following disclaimer. +:: * Redistributions in binary form must reproduce the above copyright +:: notice, this list of conditions and the following disclaimer in the +:: documentation and/or other materials provided with the distribution. +:: * Neither the name of NVIDIA CORPORATION nor the names of its +:: contributors may be used to endorse or promote products derived +:: from this software without specific prior written permission. +:: +:: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +:: EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +:: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +:: PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +:: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +:: EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +:: PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +:: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +:: OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +:: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +:: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +:: +:: Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + +@echo off +setlocal EnableDelayedExpansion + +:: batch script that runs generateMetaData.py with either +:: +:: -environment variable PYTHON +:: -p4sw location of python +:: -python.exe in PATH +:: +:: see readme.txt + +cd %~dp0 + +:: Get PxShared path from packman, if packman available: +set PACKMAN_CMD=..\..\buildtools\packman\packman +if not exist !PACKMAN_CMD! goto no_packman + +call "..\..\buildtools\update_packman.cmd" +@if errorlevel 1 @exit /b %errorlevel% + +call !PACKMAN_CMD! pull "%~dp0..\..\dependencies.xml" --include-tag=RequiredForMetaGen +@if errorlevel 1 @exit /b %errorlevel% +:no_packman + +:: look for python in p4 location unless PYTHON is set +if not defined PYTHON ( + call :find_root_path %~p0 "tools\python\3.3" _RESULT + if not !_RESULT!==0 ( + set PYTHON=!_RESULT!\tools\python\3.3\python.exe + ) +) + +:: look for python in PATH unless PYTHON is set +if not defined PYTHON ( + where python.exe > nul 2>&1 + IF !ERRORLEVEL! == 0 ( + set PYTHON=python.exe + ) +) + +if not defined PYTHON ( + goto no_python +) + +echo using: %PYTHON% + +:: visual studio 2015 is required for the meta data generation +:: run vcvarsall.bat to get visual studio developer console environment variables +if not defined VS140COMNTOOLS ( + goto no_vs140 +) + +echo calling: %VS140COMNTOOLS%..\..\VC\vcvarsall.bat +call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" +%PYTHON% generateMetaData.py %1 + +endlocal +exit /b %ERRORLEVEL% + +:no_python +echo no python found, please set PYTHON environment variable to python.exe path, or make sure python.exe is in PATH. +endlocal +exit /b 1 + +:no_vs140 +echo echo make sure vs 2015 is installed. +endlocal +exit /b 1 + + +:: ************************************************************************** +:: functions +:: ************************************************************************** + +:: find a root directory containing a known directory (as a hint) +:find_root_path + setlocal + set START_DIR=%~1 + set CONTAINED_DIR=%~2 + + :: search directory tree + set TMP_DIR=!START_DIR! + set OUT_DIR=0 + :find_root_path_loop + if exist !TMP_DIR!\!CONTAINED_DIR! goto :find_root_path_loop_end + set TMP_DIR=!TMP_DIR!..\ + + :: normalize path + pushd !TMP_DIR! + set OUT_DIR=!CD! + popd + + :: break if we reach the root, by checking the last two charactors + if "!OUT_DIR:~-2!"==":\" ( + set OUT_DIR=0 + goto :find_root_path_loop_end + ) + + goto :find_root_path_loop + :find_root_path_loop_end + + ::echo no idea why we need to use % here + endlocal & set _RESULT=%OUT_DIR% + exit /b 0 + diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py new file mode 100644 index 000000000..7af2f264c --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py @@ -0,0 +1,251 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + +from __future__ import print_function + +import argparse +import os +import stat +import sys +import re +import platform +import shutil +from lib import utils +from lib import compare + +# test mode: create copy of reference files +# update mode: try to open file in p4 if necessary +def setup_targetdir(metaDataDir, isTestMode): + if isTestMode: + targetDir = metaDataDir + "_test" + if os.path.isdir(targetDir): + print("deleting", targetDir) + shutil.rmtree(targetDir) + + def ignore_non_autogen(dir, files): + return filter(lambda f : not (os.path.isdir(os.path.join(dir, f)) + or re.search(r"AutoGenerated", f)), files) + + shutil.copytree(metaDataDir, targetDir, ignore=ignore_non_autogen) + #set write to new files: + for root, dirs, files in os.walk(targetDir): + for file in files: + os.chmod(os.path.join(root, file) , stat.S_IWRITE|stat.S_IREAD) + else: + targetDir = metaDataDir + + if not utils.check_files_writable(utils.list_autogen_files(targetDir)): + utils.try_checkout_files(utils.list_autogen_files(targetDir)) + + if not utils.check_files_writable(utils.list_autogen_files(targetDir)): + print("auto generated meta data files not writable:", targetDir) + print("aborting") + sys.exit(1) + + utils.clear_files(utils.list_autogen_files(targetDir)) + return targetDir + +# test mode: run perl script to compare reference and generated files +def test_targetdir(targetDir, metaDataDir, isTestMode): + + if isTestMode: + print("compare generated meta data files with reference meta data files:") + result = compare.compareMetaDataDirectories(targetDir, metaDataDir) + if not result: + print("failed!") + sys.exit(1) + else: + print("passed.") + +def get_osx_platform_path(): + cmd = "xcodebuild -showsdks" + (stdout, stderr) = utils.run_cmd(cmd) + if stderr != "": + print(stderr) + sys.exit(1) + + match = re.search(r"(-sdk macosx\d+.\d+)", stdout, flags=re.MULTILINE) + if not match: + print("coundn't parse output of:\n", cmd, "\naborting!") + sys.exit(1) + + sdkparam = match.group(0) + cmd = "xcodebuild -version " + sdkparam + " Path" + (sdkPath, stderr) = utils.run_cmd(cmd) + if stderr != "": + print(stderr) + sys.exit(1) + print("using sdk path:", sdkPath.rstrip()) + return sdkPath.rstrip() + +def includeString(path): + return ' -I"' + path + '"' + +########################################################################################################### +# main +########################################################################################################### + +parser = argparse.ArgumentParser(description='Generates meta data source files.') +parser.add_argument('-test', help='enables testing mode, internal only', action='store_true') + +args = parser.parse_args() + +scriptDir = os.path.dirname(os.path.realpath(__file__)) + +try: + os.makedirs("temp") +except: + None + +# find SDK_ROOT, EXTERNALS and PX_SHARED +sdkRoot = utils.find_root_path(scriptDir, "source") + +if os.path.isdir(os.path.join(sdkRoot, "../physx")): + externalsRoot = os.path.join(sdkRoot, "../externals") + pxSharedRoot = os.path.join(sdkRoot, "../pxshared") +else: + externalsRoot = os.path.join(utils.find_root_path(scriptDir, os.path.normpath("externals/clang")), "externals") + pxSharedRoot = os.path.normpath(os.environ['PM_PxShared_PATH']) + +print("testmode:", args.test) +print("root sdk:", sdkRoot) +print("root externals:", externalsRoot) +print("root shared:", pxSharedRoot) + +boilerPlateFile = os.path.join(sdkRoot, os.path.normpath("tools/physxmetadatagenerator/PxBoilerPlate.h")) + +includes = '' +includes += includeString(pxSharedRoot + '/include') +includes += includeString(sdkRoot + '/include') +includes += includeString(sdkRoot + '/source/foundation/include') +includes += includeString(sdkRoot + '/source/common/src') +includes += includeString(sdkRoot + '/source/geomutils/headers') +includes += includeString(sdkRoot + '/source/geomutils/src') +includes += includeString(sdkRoot + '/source/geomutils/Opcode') +includes += includeString(sdkRoot + '/source/physx/src') +includes += includeString(sdkRoot + '/source/physx/src/buffering') +includes += includeString(sdkRoot + '/source/simulationcontroller/src') +includes += includeString(sdkRoot + '/source/simulationcontroller/src/framework') +includes += includeString(sdkRoot + '/source/simulationcontroller/include') +includes += includeString(sdkRoot + '/source/physxcooking/include') +includes += includeString(sdkRoot + '/source/scenequery') +includes += includeString(sdkRoot + '/source/physxmetadata/core/include') +includes += includeString(sdkRoot + '/source/physxgpu/include') +includes += includeString(sdkRoot + '/source/pvd/include') +includes += includeString(sdkRoot + '/source/fastxml/include') +includes += includeString(sdkRoot + '/tools/physxmetadatagenerator') + +print("platform:", platform.system()) + +commonFlags = '-DNDEBUG -DPX_GENERATE_META_DATA -x c++-header -std=c++0x -w -Wno-c++11-narrowing -fms-extensions ' + +if platform.system() == "Windows": + debugFile = open("temp/clangCommandLine_windows.txt", "a") + + # read INCLUDE variable, set by calling batch script + sysIncludes = os.environ['INCLUDE'] + sysIncludes = sysIncludes.rstrip(';') + sysIncludeList = sysIncludes.split(';') + sysIncludeFlags = ' -isystem"' + '" -isystem"'.join(sysIncludeList) + '"' + + # for some reason -cc1 needs to go first in commonFlags + commonFlags = '-cc1 ' + commonFlags + platformFlags = '-DPX_VC=14 -D_WIN32 ' + sysIncludeFlags + clangExe = os.path.join(externalsRoot, os.path.normpath('clang/4.0.0/win32/bin/clang.exe')) + + +elif platform.system() == "Linux": + debugFile = open("temp/clangCommandLine_linux.txt", "a") + + platformFlags = '' + clangExe = os.path.join(externalsRoot, os.path.normpath('clang/4.0.0/linux32/bin/clang')) + +elif platform.system() == "Darwin": + debugFile = open("temp/clangCommandLine_osx.txt", "a") + + platformFlags = ' -isysroot' + get_osx_platform_path() + clangExe = os.path.join(externalsRoot, os.path.normpath('clang/4.0.0/osx/bin/clang')) +else: + print("unsupported platform, aborting!") + sys.exit(1) + +commonFlags += ' -boilerplate-file ' + boilerPlateFile + +#some checks +if not os.path.isfile(clangExe): + print("didn't find,", clangExe, ", aborting!") + sys.exit(1) + +clangExe = '"' + clangExe + '"' + +# required for execution of clang.exe +os.environ["PWD"] = os.path.join(sdkRoot, os.path.normpath("tools/physxmetadatagenerator")) + +############################### +# PxPhysicsWithExtensions # +############################### + +print("PxPhysicsWithExtensions:") + +srcPath = "PxPhysicsWithExtensionsAPI.h" +metaDataDir = os.path.join(sdkRoot, os.path.normpath("source/physxmetadata")) + +targetDir = setup_targetdir(metaDataDir, args.test) + +cmd = " ".join(["", clangExe, commonFlags, "", platformFlags, includes, srcPath, "-o", '"'+targetDir+'"']) +print(cmd, file = debugFile) +(stdout, stderr) = utils.run_cmd(cmd) +if (stderr != "" or stdout != ""): + print(stderr, "\n", stdout) +print("wrote meta data files in", targetDir) + +test_targetdir(targetDir, metaDataDir, args.test) + +############################### +# PxVehicleExtension # +############################### + +print("PxVehicleExtension:") + +srcPath = "PxVehicleExtensionAPI.h" +metaDataDir = os.path.join(sdkRoot, os.path.normpath("source/physxvehicle/src/physxmetadata")) + +includes += includeString(sdkRoot + '/include/vehicle') +includes += includeString(sdkRoot + '/source/physxvehicle/src') +includes += includeString(pxSharedRoot + '/include/foundation') + +targetDir = setup_targetdir(metaDataDir, args.test) + +cmd = " ".join(["", clangExe, commonFlags, "", platformFlags, includes, srcPath, "-o", '"'+targetDir+'"']) +print(cmd, file = debugFile) +(stdout, stderr) = utils.run_cmd(cmd) +if (stderr != "" or stdout != ""): + print(stderr, "\n", stdout) +print("wrote meta data files in", targetDir) + +test_targetdir(targetDir, metaDataDir, args.test) + diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh new file mode 100644 index 000000000..149b523a4 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh @@ -0,0 +1,47 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PHYSX_ROOT_DIR=$SCRIPT_DIR/../.. + +export GEN_META_DATA_PARAMETER=$1 + +if [ -f $PHYSX_ROOT_DIR/buildtools/update_packman.sh ] ; then + source $PHYSX_ROOT_DIR/buildtools/update_packman.sh + $PHYSX_ROOT_DIR/buildtools/packman/packman pull "$PHYSX_ROOT_DIR/dependencies.xml" --platform linux --include-tag=requiredForMetaGen --postscript $SCRIPT_DIR/helper.sh +else + export PM_PxShared_PATH="$PHYSX_ROOT_DIR/../pxshared" + $SCRIPT_DIR/helper.sh +fi + +status=$? +if [ "$status" -ne "0" ]; then + echo "Error $status" + exit 1 +fi diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/helper.sh b/src/PhysX/physx/tools/physxmetadatagenerator/helper.sh new file mode 100644 index 000000000..95b193442 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/helper.sh @@ -0,0 +1,37 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +#!/bin/bash + +set -e + +python generateMetaData.py $GEN_META_DATA_PARAMETER +status=$? +if [ "$status" -ne "0" ]; then + echo "Error $status" + exit 1 +fi diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/lib/__init__.py b/src/PhysX/physx/tools/physxmetadatagenerator/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py b/src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py new file mode 100644 index 000000000..a7657a8c8 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py @@ -0,0 +1,120 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + +# general utility module +from __future__ import print_function +from __future__ import with_statement + +import os +import sys +import re +from . import utils + +def compareMetaDataDirectories(candidateDir, referenceDir): + + print("reference dir:", referenceDir) + print("candidate dir:", candidateDir) + + if not _checkFileExistence(candidateDir, referenceDir): + return False + + referenceFiles = utils.list_autogen_files(referenceDir) + + #get corresponding candidate files without relying on os.walk order + def mapRefToCand(refFile): + return os.path.join(candidateDir, os.path.relpath(refFile, referenceDir)) + candidateFiles = [mapRefToCand(f) for f in referenceFiles] + + for (fileCand, fileRef) in zip(candidateFiles, referenceFiles): + + timeCand = os.path.getmtime(fileCand) + timeRef = os.path.getmtime(fileRef) + + if timeCand <= timeRef: + print("last modified time of candidate is not later than last modified time of reference:") + print("candidate:", fileCand, "\n", "reference:", fileRef) + print("ref:", timeRef) + print("cand:", timeCand) + return False + + #_read_file_content will remove line endings(windows/unix), but not ignore empty lines + candLines = _read_file_content(fileCand) + refLines = _read_file_content(fileRef) + if not (candLines and refLines): + return False + + if len(candLines) != len(refLines): + print("files got different number of lines:") + print("candidate:", fileCand, "\n", "reference:", fileRef) + print("ref:", len(refLines)) + print("cand:", len(candLines)) + return False + + for (i, (lineCand, lineRef)) in enumerate(zip(candLines, refLines)): + if (lineCand != lineRef): + print("candidate line is not equal to refence line:") + print("candidate:", fileCand, "\n", "reference:", fileRef) + print("@line number:", i) + print("ref:", lineRef) + print("cand:", lineCand) + return False + + return True + +############################################################################## +# internal functions +############################################################################## + +#will remove line endings(windows/unix), but not ignore empty lines +def _read_file_content(filePath): + lines = [] + try: + with open(filePath, "r") as file: + for line in file: + lines.append(line.rstrip()) + except: + print("issue with reading file:", filePath) + + return lines + +def _checkFileExistence(candidateDir, referenceDir): + candidateSet = set([os.path.relpath(f, candidateDir) for f in utils.list_autogen_files(candidateDir)]) + referenceSet = set([os.path.relpath(f, referenceDir) for f in utils.list_autogen_files(referenceDir)]) + + missingSet = referenceSet - candidateSet + if missingSet: + print("the following files are missing from the candidates:\n", "\n".join(missingSet)) + return False + + excessSet = candidateSet - referenceSet + if excessSet: + print("too many candidate files:\n", "\n".join(excessSet)) + return False + + return True + + diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py b/src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py new file mode 100644 index 000000000..1d3a786b5 --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py @@ -0,0 +1,104 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. + +# general utility module +from __future__ import print_function +from __future__ import with_statement + +import os +import sys +import re +import subprocess + +def list_autogen_files(dirPath): + autogenFiles = [] + for (root, subdirs, files) in os.walk(dirPath): + files = filter(lambda f : re.search(r"AutoGenerated", f), files) + autogenFiles.extend([os.path.join(root, f) for f in files]) + return autogenFiles + +#checkout files with p4 if available +def try_checkout_files(files): + + print("checking p4 connection parameter...") + + # checking p4 + cmd = "p4" + (stdout, stderr) = run_cmd(cmd) + + if stderr == "": + print("p4 available.") + else: + print("p4 unavailable.") + return + + cmd = "p4 edit " + " " + " ".join(files) + (stdout, stderr) = run_cmd(cmd) + print(stderr) + print(stdout) + +# check files writability +def check_files_writable(files): + for file in files: + if not os.access(file, os.W_OK): + return False + return True + +# find a root directory containing a known directory (as a hint) +def find_root_path(startDir, containedDir): + + currentDir = startDir + + # search directory tree + mergedDir = os.path.join(currentDir, containedDir) + while not os.path.isdir(mergedDir): + (currentDir, dir) = os.path.split(currentDir) + if not dir: + return None + + mergedDir = os.path.join(currentDir, containedDir) + + return currentDir + +def run_cmd(cmd, stdin = ""): + process = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + (stdoutRaw, stderrRaw) = process.communicate(stdin.encode('utf-8')) + stdout = stdoutRaw.decode(encoding='utf-8') + stderr = stderrRaw.decode(encoding='utf-8') + return (stdout, stderr) + +# clears content of files +def clear_files(files): + for file in files: + open(file, 'w').close() + +############################################################################## +# internal functions +############################################################################## + + diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/readme.txt b/src/PhysX/physx/tools/physxmetadatagenerator/readme.txt new file mode 100644 index 000000000..fa569f54c --- /dev/null +++ b/src/PhysX/physx/tools/physxmetadatagenerator/readme.txt @@ -0,0 +1,54 @@ +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## * Neither the name of NVIDIA CORPORATION nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. + +If the Physx API is changed, the API level serialization meta data files need to be updated. +This can be done by running: + +Windows: generateMetaData.bat +It looks for python in + 1. environment variable PYTHON + 2. p4sw location (NVidia only) + 3. PATH + +Linux/MacOS: generateMetaData.sh + +Linux and osx distributions mostly come with Python pre-installed. On windows please download +and install the latest python version from here: + https://www.python.org/downloads +and make sure to add python to your windows PATH (option in python installer) + +The generateMetaData.py tests for meta data files being writable. If not, p4 commands are +used to open the files for edit before the update. If p4 is not available (or not configured for cmd line usage, see https://www.perforce.com/perforce/r15.1/manuals/p4guide/chapter.configuration.html: "Using config files"), +the files need to be made writable manually. + +Requirements: +Windows: Visual Studio 2015 + +Testing: +generateMetaData.py -test runs the meta data generation in test mode. This mode checks that the current +set of meta data files is still in sync with the PhysX API (i.e. already generated meta data files). + diff --git a/src/PhysX/premake4.lua b/src/PhysX/premake4.lua new file mode 100644 index 000000000..a1a693ac0 --- /dev/null +++ b/src/PhysX/premake4.lua @@ -0,0 +1,128 @@ + project "PhysX" + + kind "StaticLib" + if os.is("Linux") then + buildoptions{"-fPIC"} + end + + defines { + "PX_PHYSX_STATIC_LIB", + "PX_COOKING", + "PX_FOUNDATION_DLL=0" + } + + configuration {"x64", "debug"} + defines {"_DEBUG"} + configuration {"x86", "debug"} + defines {"_DEBUG"} + configuration {"x64", "release"} + defines {"NDEBUG"} + configuration {"x86", "release"} + defines {"NDEBUG"} + configuration{} + + includedirs { + "physx/source/common/include", + "physx/source/common/src", + "physx/source/fastxml/include", + "physx/source/filebuf/include", + "physx/source/foundation/include", + "physx/source/geomutils/include", + "physx/source/geomutils/src", + "physx/source/geomutils/src/ccd", + "physx/source/geomutils/src/common", + "physx/source/geomutils/src/contact", + "physx/source/geomutils/src/convex", + "physx/source/geomutils/src/distance", + "physx/source/geomutils/src/gjk", + "physx/source/geomutils/src/hf", + "physx/source/geomutils/src/intersection", + "physx/source/geomutils/src/mesh", + "physx/source/geomutils/src/pcm", + "physx/source/geomutils/src/sweep", + "physx/source/lowlevel/api/include", + "physx/source/lowlevel/common/include", + "physx/source/lowlevel/common/include/collision", + "physx/source/lowlevel/common/include/pipeline", + "physx/source/lowlevel/common/include/utils", + "physx/source/lowlevel/software/include", + "physx/source/lowlevelaabb/include", + "physx/source/lowleveldynamics/include", + "physx/source/physx/src", + "physx/source/physx/src/buffering", + "physx/source/physx/src/device", + "physx/source/physxcooking/src", + "physx/source/physxcooking/src/convex", + "physx/source/physxcooking/src/mesh", + "physx/source/physxextensions/src", + "physx/source/physxextensions/src/serialization/Binary", + "physx/source/physxextensions/src/serialization/File", + "physx/source/physxextensions/src/serialization/Xml", + "physx/source/physxmetadata/core/include", + "physx/source/physxmetadata/extensions/include", + "physx/source/physxvehicle/src", + "physx/source/physxvehicle/src/physxmetadata/include", + "physx/source/pvd/include", + "physx/source/scenequery/include", + "physx/source/simulationcontroller/include", + "physx/source/simulationcontroller/src", +--public + "physx/include", + "physx/include/characterkinematic", + "physx/include/common", + "physx/include/cooking", + "physx/include/extensions", + "physx/include/geometry", + "physx/include/geomutils", + "physx/include/vehicle", + "pxshared/include", + } + + if os.is("Windows") then + files { + "physx/source/common/src/windows/*.cpp", + "physx/source/foundation/src/windows/*.cpp", + "physx/source/physx/src/device/windows/*.cpp", + "physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp", + } + includedirs { + "physx/include/gpu", + "physx/source/physxgpu/include", + } + else +-- files { +-- "physx/source/foundation/src/unix/*.cpp", +-- "physx/source/physx/src/device/linux/*.cpp", +-- } + end + + files { + "physx/source/common/src/*.cpp", + "physx/source/fastxml/src/*.cpp", + "physx/source/foundation/src/*.cpp", + "physx/source/geomutils/src/**.cpp", + "physx/source/immediatemode/src/*.cpp", + "physx/source/lowlevel/api/src/*.cpp", + "physx/source/lowlevel/common/src/**.cpp", + "physx/source/lowlevel/software/src/*.cpp", + "physx/source/lowlevelaabb/src/*.cpp", + "physx/source/lowleveldynamics/src/*.cpp", + "physx/source/physx/src/*.cpp", + "physx/source/physx/src/buffering/*.cpp", + "physx/source/physx/src/gpu/*.cpp", + "physx/source/physxcharacterkinematic/src/*.cpp", + "physx/source/physxcooking/src/*.cpp", + "physx/source/physxcooking/src/convex/*.cpp", + "physx/source/physxcooking/src/mesh/*.cpp", + "physx/source/physxextensions/src/*.cpp", + "physx/source/physxextensions/src/**.cpp", + "physx/source/physxmetadata/core/src/*.cpp", + "physx/source/physxmetadata/extensions/src/*.cpp", + "physx/source/physxvehicle/src/*.cpp", + "physx/source/physxvehicle/src/physxmetadata/src/*.cpp", + "physx/source/pvd/src/*.cpp", + "physx/source/scenequery/src/*.cpp", + "physx/source/simulationcontroller/src/*.cpp", + "physx/source/task/src/*.cpp", + } + \ No newline at end of file diff --git a/src/PhysX/pxshared/include/foundation/Px.h b/src/PhysX/pxshared/include/foundation/Px.h new file mode 100644 index 000000000..28c744853 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/Px.h @@ -0,0 +1,92 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PX_H +#define PXFOUNDATION_PX_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxSimpleTypes.h" + +/** files to always include */ +#include +#include + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +typedef uint32_t PxU32; + +class PxAllocatorCallback; +class PxErrorCallback; +struct PxErrorCode; +class PxAssertHandler; + +class PxInputStream; +class PxInputData; +class PxOutputStream; + +class PxVec2; +class PxVec3; +class PxVec4; +class PxMat33; +class PxMat44; +class PxPlane; +class PxQuat; +class PxTransform; +class PxBounds3; + +/** enum for empty constructor tag*/ +enum PxEMPTY +{ + PxEmpty +}; + +/** enum for zero constructor tag for vectors and matrices */ +enum PxZERO +{ + PxZero +}; + +/** enum for identity constructor flag for quaternions, transforms, and matrices */ +enum PxIDENTITY +{ + PxIdentity +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PX_H diff --git a/src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h b/src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h new file mode 100644 index 000000000..d9350ce2e --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXALLOCATORCALLBACK_H +#define PXFOUNDATION_PXALLOCATORCALLBACK_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Px.h" +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Abstract base class for an application defined memory allocator that can be used by the Nv library. + +\note The SDK state should not be modified from within any allocation/free function. + +Threading: All methods of this class should be thread safe as it can be called from the user thread +or the physics processing thread(s). +*/ + +class PxAllocatorCallback +{ + public: + /** + \brief destructor + */ + virtual ~PxAllocatorCallback() + { + } + + /** + \brief Allocates size bytes of memory, which must be 16-byte aligned. + + This method should never return NULL. If you run out of memory, then + you should terminate the app or take some other appropriate action. + + Threading: This function should be thread safe as it can be called in the context of the user thread + and physics processing thread(s). + + \param size Number of bytes to allocate. + \param typeName Name of the datatype that is being allocated + \param filename The source file which allocated the memory + \param line The source line which allocated the memory + \return The allocated block of memory. + */ + virtual void* allocate(size_t size, const char* typeName, const char* filename, int line) = 0; + + /** + \brief Frees memory previously allocated by allocate(). + + Threading: This function should be thread safe as it can be called in the context of the user thread + and physics processing thread(s). + + \param ptr Memory to free. + */ + virtual void deallocate(void* ptr) = 0; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXALLOCATORCALLBACK_H diff --git a/src/PhysX/pxshared/include/foundation/PxAssert.h b/src/PhysX/pxshared/include/foundation/PxAssert.h new file mode 100644 index 000000000..227035474 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxAssert.h @@ -0,0 +1,95 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXASSERT_H +#define PXFOUNDATION_PXASSERT_H + +/** \addtogroup foundation +@{ */ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/* Base class to handle assert failures */ +class PxAssertHandler +{ + public: + virtual ~PxAssertHandler() + { + } + virtual void operator()(const char* exp, const char* file, int line, bool& ignore) = 0; +}; + +PX_FOUNDATION_API PxAssertHandler& PxGetAssertHandler(); +PX_FOUNDATION_API void PxSetAssertHandler(PxAssertHandler& handler); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +#if !PX_ENABLE_ASSERTS +#define PX_ASSERT(exp) ((void)0) +#define PX_ALWAYS_ASSERT_MESSAGE(exp) ((void)0) +#define PX_ASSERT_WITH_MESSAGE(condition, message) ((void)0) +#else +#if PX_VC +#define PX_CODE_ANALYSIS_ASSUME(exp) \ + __analysis_assume(!!(exp)) // This macro will be used to get rid of analysis warning messages if a PX_ASSERT is used +// to "guard" illegal mem access, for example. +#else +#define PX_CODE_ANALYSIS_ASSUME(exp) +#endif +#define PX_ASSERT(exp) \ + { \ + static bool _ignore = false; \ + ((void)((!!(exp)) || (!_ignore && (physx::PxGetAssertHandler()(#exp, __FILE__, __LINE__, _ignore), false)))); \ + PX_CODE_ANALYSIS_ASSUME(exp); \ + } +#define PX_ALWAYS_ASSERT_MESSAGE(exp) \ + { \ + static bool _ignore = false; \ + if(!_ignore) \ + physx::PxGetAssertHandler()(exp, __FILE__, __LINE__, _ignore); \ + } +#define PX_ASSERT_WITH_MESSAGE(exp, message) \ + { \ + static bool _ignore = false; \ + ((void)((!!(exp)) || (!_ignore && (physx::PxGetAssertHandler()(message, __FILE__, __LINE__, _ignore), false)))); \ + PX_CODE_ANALYSIS_ASSUME(exp); \ + } +#endif + +#define PX_ALWAYS_ASSERT() PX_ASSERT(0) + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXASSERT_H diff --git a/src/PhysX/pxshared/include/foundation/PxBitAndData.h b/src/PhysX/pxshared/include/foundation/PxBitAndData.h new file mode 100644 index 000000000..be7699e49 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxBitAndData.h @@ -0,0 +1,87 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXBITANDDATA_H +#define PXFOUNDATION_PXBITANDDATA_H + +#include "foundation/Px.h" + +/** \addtogroup foundation + @{ +*/ +#if !PX_DOXYGEN +namespace physx +{ +#endif + +template +class PxBitAndDataT +{ + public: + PX_FORCE_INLINE PxBitAndDataT(const PxEMPTY) + { + } + PX_FORCE_INLINE PxBitAndDataT() : mData(0) + { + } + PX_FORCE_INLINE PxBitAndDataT(storageType data, bool bit = false) + { + mData = bit ? storageType(data | bitMask) : data; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE operator storageType() const + { + return storageType(mData & ~bitMask); + } + PX_CUDA_CALLABLE PX_FORCE_INLINE void setBit() + { + mData |= bitMask; + } + PX_CUDA_CALLABLE PX_FORCE_INLINE void clearBit() + { + mData &= ~bitMask; + } + PX_CUDA_CALLABLE PX_FORCE_INLINE storageType isBitSet() const + { + return storageType(mData & bitMask); + } + + protected: + storageType mData; +}; +typedef PxBitAndDataT PxBitAndByte; +typedef PxBitAndDataT PxBitAndWord; +typedef PxBitAndDataT PxBitAndDword; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PXFOUNDATION_PXBITANDDATA_H diff --git a/src/PhysX/pxshared/include/foundation/PxBounds3.h b/src/PhysX/pxshared/include/foundation/PxBounds3.h new file mode 100644 index 000000000..7a071c3ab --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxBounds3.h @@ -0,0 +1,480 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXBOUNDS3_H +#define PXFOUNDATION_PXBOUNDS3_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxTransform.h" +#include "foundation/PxMat33.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +// maximum extents defined such that floating point exceptions are avoided for standard use cases +#define PX_MAX_BOUNDS_EXTENTS (PX_MAX_REAL * 0.25f) + +/** +\brief Class representing 3D range or axis aligned bounding box. + +Stored as minimum and maximum extent corners. Alternate representation +would be center and dimensions. +May be empty or nonempty. For nonempty bounds, minimum <= maximum has to hold for all axes. +Empty bounds have to be represented as minimum = PX_MAX_BOUNDS_EXTENTS and maximum = -PX_MAX_BOUNDS_EXTENTS for all +axes. +All other representations are invalid and the behavior is undefined. +*/ +class PxBounds3 +{ + public: + /** + \brief Default constructor, not performing any initialization for performance reason. + \remark Use empty() function below to construct empty bounds. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3() + { + } + + /** + \brief Construct from two bounding points + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3(const PxVec3& minimum, const PxVec3& maximum); + + /** + \brief Return empty bounds. + */ + static PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 empty(); + + /** + \brief returns the AABB containing v0 and v1. + \param v0 first point included in the AABB. + \param v1 second point included in the AABB. + */ + static PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 boundsOfPoints(const PxVec3& v0, const PxVec3& v1); + + /** + \brief returns the AABB from center and extents vectors. + \param center Center vector + \param extent Extents vector + */ + static PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 centerExtents(const PxVec3& center, const PxVec3& extent); + + /** + \brief Construct from center, extent, and (not necessarily orthogonal) basis + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 + basisExtent(const PxVec3& center, const PxMat33& basis, const PxVec3& extent); + + /** + \brief Construct from pose and extent + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 poseExtent(const PxTransform& pose, const PxVec3& extent); + + /** + \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). + + This version is safe to call for empty bounds. + + \param[in] matrix Transform to apply, can contain scaling as well + \param[in] bounds The bounds to transform. + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 transformSafe(const PxMat33& matrix, const PxBounds3& bounds); + + /** + \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). + + Calling this method for empty bounds leads to undefined behavior. Use #transformSafe() instead. + + \param[in] matrix Transform to apply, can contain scaling as well + \param[in] bounds The bounds to transform. + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 transformFast(const PxMat33& matrix, const PxBounds3& bounds); + + /** + \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). + + This version is safe to call for empty bounds. + + \param[in] transform Transform to apply, can contain scaling as well + \param[in] bounds The bounds to transform. + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 transformSafe(const PxTransform& transform, const PxBounds3& bounds); + + /** + \brief gets the transformed bounds of the passed AABB (resulting in a bigger AABB). + + Calling this method for empty bounds leads to undefined behavior. Use #transformSafe() instead. + + \param[in] transform Transform to apply, can contain scaling as well + \param[in] bounds The bounds to transform. + */ + static PX_CUDA_CALLABLE PX_INLINE PxBounds3 transformFast(const PxTransform& transform, const PxBounds3& bounds); + + /** + \brief Sets empty to true + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void setEmpty(); + + /** + \brief Sets the bounds to maximum size [-PX_MAX_BOUNDS_EXTENTS, PX_MAX_BOUNDS_EXTENTS]. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void setMaximal(); + + /** + \brief expands the volume to include v + \param v Point to expand to. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void include(const PxVec3& v); + + /** + \brief expands the volume to include b. + \param b Bounds to perform union with. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void include(const PxBounds3& b); + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isEmpty() const; + + /** + \brief indicates whether the intersection of this and b is empty or not. + \param b Bounds to test for intersection. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool intersects(const PxBounds3& b) const; + + /** + \brief computes the 1D-intersection between two AABBs, on a given axis. + \param a the other AABB + \param axis the axis (0, 1, 2) + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool intersects1D(const PxBounds3& a, uint32_t axis) const; + + /** + \brief indicates if these bounds contain v. + \param v Point to test against bounds. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool contains(const PxVec3& v) const; + + /** + \brief checks a box is inside another box. + \param box the other AABB + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isInside(const PxBounds3& box) const; + + /** + \brief returns the center of this axis aligned box. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getCenter() const; + + /** + \brief get component of the box's center along a given axis + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float getCenter(uint32_t axis) const; + + /** + \brief get component of the box's extents along a given axis + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float getExtents(uint32_t axis) const; + + /** + \brief returns the dimensions (width/height/depth) of this axis aligned box. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getDimensions() const; + + /** + \brief returns the extents, which are half of the width/height/depth. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getExtents() const; + + /** + \brief scales the AABB. + + This version is safe to call for empty bounds. + + \param scale Factor to scale AABB by. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void scaleSafe(float scale); + + /** + \brief scales the AABB. + + Calling this method for empty bounds leads to undefined behavior. Use #scaleSafe() instead. + + \param scale Factor to scale AABB by. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void scaleFast(float scale); + + /** + fattens the AABB in all 3 dimensions by the given distance. + + This version is safe to call for empty bounds. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void fattenSafe(float distance); + + /** + fattens the AABB in all 3 dimensions by the given distance. + + Calling this method for empty bounds leads to undefined behavior. Use #fattenSafe() instead. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE void fattenFast(float distance); + + /** + checks that the AABB values are not NaN + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite() const; + + /** + checks that the AABB values describe a valid configuration. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isValid() const; + + PxVec3 minimum, maximum; +}; + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3::PxBounds3(const PxVec3& minimum_, const PxVec3& maximum_) +: minimum(minimum_), maximum(maximum_) +{ +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 PxBounds3::empty() +{ + return PxBounds3(PxVec3(PX_MAX_BOUNDS_EXTENTS), PxVec3(-PX_MAX_BOUNDS_EXTENTS)); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isFinite() const +{ + return minimum.isFinite() && maximum.isFinite(); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 PxBounds3::boundsOfPoints(const PxVec3& v0, const PxVec3& v1) +{ + return PxBounds3(v0.minimum(v1), v0.maximum(v1)); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxBounds3 PxBounds3::centerExtents(const PxVec3& center, const PxVec3& extent) +{ + return PxBounds3(center - extent, center + extent); +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 +PxBounds3::basisExtent(const PxVec3& center, const PxMat33& basis, const PxVec3& extent) +{ + // extended basis vectors + PxVec3 c0 = basis.column0 * extent.x; + PxVec3 c1 = basis.column1 * extent.y; + PxVec3 c2 = basis.column2 * extent.z; + + PxVec3 w; + // find combination of base vectors that produces max. distance for each component = sum of abs() + w.x = PxAbs(c0.x) + PxAbs(c1.x) + PxAbs(c2.x); + w.y = PxAbs(c0.y) + PxAbs(c1.y) + PxAbs(c2.y); + w.z = PxAbs(c0.z) + PxAbs(c1.z) + PxAbs(c2.z); + + return PxBounds3(center - w, center + w); +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::poseExtent(const PxTransform& pose, const PxVec3& extent) +{ + return basisExtent(pose.p, PxMat33(pose.q), extent); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::setEmpty() +{ + minimum = PxVec3(PX_MAX_BOUNDS_EXTENTS); + maximum = PxVec3(-PX_MAX_BOUNDS_EXTENTS); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::setMaximal() +{ + minimum = PxVec3(-PX_MAX_BOUNDS_EXTENTS); + maximum = PxVec3(PX_MAX_BOUNDS_EXTENTS); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::include(const PxVec3& v) +{ + PX_ASSERT(isValid()); + minimum = minimum.minimum(v); + maximum = maximum.maximum(v); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::include(const PxBounds3& b) +{ + PX_ASSERT(isValid()); + minimum = minimum.minimum(b.minimum); + maximum = maximum.maximum(b.maximum); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isEmpty() const +{ + PX_ASSERT(isValid()); + return minimum.x > maximum.x; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::intersects(const PxBounds3& b) const +{ + PX_ASSERT(isValid() && b.isValid()); + return !(b.minimum.x > maximum.x || minimum.x > b.maximum.x || b.minimum.y > maximum.y || minimum.y > b.maximum.y || + b.minimum.z > maximum.z || minimum.z > b.maximum.z); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::intersects1D(const PxBounds3& a, uint32_t axis) const +{ + PX_ASSERT(isValid() && a.isValid()); + return maximum[axis] >= a.minimum[axis] && a.maximum[axis] >= minimum[axis]; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::contains(const PxVec3& v) const +{ + PX_ASSERT(isValid()); + + return !(v.x < minimum.x || v.x > maximum.x || v.y < minimum.y || v.y > maximum.y || v.z < minimum.z || + v.z > maximum.z); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isInside(const PxBounds3& box) const +{ + PX_ASSERT(isValid() && box.isValid()); + if(box.minimum.x > minimum.x) + return false; + if(box.minimum.y > minimum.y) + return false; + if(box.minimum.z > minimum.z) + return false; + if(box.maximum.x < maximum.x) + return false; + if(box.maximum.y < maximum.y) + return false; + if(box.maximum.z < maximum.z) + return false; + return true; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 PxBounds3::getCenter() const +{ + PX_ASSERT(isValid()); + return (minimum + maximum) * 0.5f; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxBounds3::getCenter(uint32_t axis) const +{ + PX_ASSERT(isValid()); + return (minimum[axis] + maximum[axis]) * 0.5f; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxBounds3::getExtents(uint32_t axis) const +{ + PX_ASSERT(isValid()); + return (maximum[axis] - minimum[axis]) * 0.5f; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 PxBounds3::getDimensions() const +{ + PX_ASSERT(isValid()); + return maximum - minimum; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 PxBounds3::getExtents() const +{ + PX_ASSERT(isValid()); + return getDimensions() * 0.5f; +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::scaleSafe(float scale) +{ + PX_ASSERT(isValid()); + if(!isEmpty()) + scaleFast(scale); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::scaleFast(float scale) +{ + PX_ASSERT(isValid()); + *this = centerExtents(getCenter(), getExtents() * scale); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::fattenSafe(float distance) +{ + PX_ASSERT(isValid()); + if(!isEmpty()) + fattenFast(distance); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::fattenFast(float distance) +{ + PX_ASSERT(isValid()); + minimum.x -= distance; + minimum.y -= distance; + minimum.z -= distance; + + maximum.x += distance; + maximum.y += distance; + maximum.z += distance; +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformSafe(const PxMat33& matrix, const PxBounds3& bounds) +{ + PX_ASSERT(bounds.isValid()); + return !bounds.isEmpty() ? transformFast(matrix, bounds) : bounds; +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformFast(const PxMat33& matrix, const PxBounds3& bounds) +{ + PX_ASSERT(bounds.isValid()); + return PxBounds3::basisExtent(matrix * bounds.getCenter(), matrix, bounds.getExtents()); +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformSafe(const PxTransform& transform, const PxBounds3& bounds) +{ + PX_ASSERT(bounds.isValid()); + return !bounds.isEmpty() ? transformFast(transform, bounds) : bounds; +} + +PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformFast(const PxTransform& transform, const PxBounds3& bounds) +{ + PX_ASSERT(bounds.isValid()); + return PxBounds3::basisExtent(transform.transform(bounds.getCenter()), PxMat33(transform.q), bounds.getExtents()); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isValid() const +{ + return (isFinite() && (((minimum.x <= maximum.x) && (minimum.y <= maximum.y) && (minimum.z <= maximum.z)) || + ((minimum.x == PX_MAX_BOUNDS_EXTENTS) && (minimum.y == PX_MAX_BOUNDS_EXTENTS) && + (minimum.z == PX_MAX_BOUNDS_EXTENTS) && (maximum.x == -PX_MAX_BOUNDS_EXTENTS) && + (maximum.y == -PX_MAX_BOUNDS_EXTENTS) && (maximum.z == -PX_MAX_BOUNDS_EXTENTS)))); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXBOUNDS3_H diff --git a/src/PhysX/pxshared/include/foundation/PxErrorCallback.h b/src/PhysX/pxshared/include/foundation/PxErrorCallback.h new file mode 100644 index 000000000..554c76c8a --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxErrorCallback.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXERRORCALLBACK_H +#define PXFOUNDATION_PXERRORCALLBACK_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxErrors.h" +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief User defined interface class. Used by the library to emit debug information. + +\note The SDK state should not be modified from within any error reporting functions. + +Threading: The SDK sequences its calls to the output stream using a mutex, so the class need not +be implemented in a thread-safe manner if the SDK is the only client. +*/ +class PxErrorCallback +{ + public: + virtual ~PxErrorCallback() + { + } + + /** + \brief Reports an error code. + \param code Error code, see #PxErrorCode + \param message Message to display. + \param file File error occured in. + \param line Line number error occured on. + */ + virtual void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) = 0; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXERRORCALLBACK_H diff --git a/src/PhysX/pxshared/include/foundation/PxErrors.h b/src/PhysX/pxshared/include/foundation/PxErrors.h new file mode 100644 index 000000000..eaedc50fd --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxErrors.h @@ -0,0 +1,93 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXERRORS_H +#define PXFOUNDATION_PXERRORS_H +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Error codes + +These error codes are passed to #PxErrorCallback + +@see PxErrorCallback +*/ + +struct PxErrorCode +{ + enum Enum + { + eNO_ERROR = 0, + + //! \brief An informational message. + eDEBUG_INFO = 1, + + //! \brief a warning message for the user to help with debugging + eDEBUG_WARNING = 2, + + //! \brief method called with invalid parameter(s) + eINVALID_PARAMETER = 4, + + //! \brief method was called at a time when an operation is not possible + eINVALID_OPERATION = 8, + + //! \brief method failed to allocate some memory + eOUT_OF_MEMORY = 16, + + /** \brief The library failed for some reason. + Possibly you have passed invalid values like NaNs, which are not checked for. + */ + eINTERNAL_ERROR = 32, + + //! \brief An unrecoverable error, execution should be halted and log output flushed + eABORT = 64, + + //! \brief The SDK has determined that an operation may result in poor performance. + ePERF_WARNING = 128, + + //! \brief A bit mask for including all errors + eMASK_ALL = -1 + }; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXERRORS_H diff --git a/src/PhysX/pxshared/include/foundation/PxFlags.h b/src/PhysX/pxshared/include/foundation/PxFlags.h new file mode 100644 index 000000000..58d456cf5 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxFlags.h @@ -0,0 +1,376 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXFLAGS_H +#define PXFOUNDATION_PXFLAGS_H + +/** \addtogroup foundation + @{ +*/ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif +/** +\brief Container for bitfield flag variables associated with a specific enum type. + +This allows for type safe manipulation for bitfields. + +

                      Example

                      + // enum that defines each bit... + struct MyEnum + { + enum Enum + { + eMAN = 1, + eBEAR = 2, + ePIG = 4, + }; + }; + + // implements some convenient global operators. + PX_FLAGS_OPERATORS(MyEnum::Enum, uint8_t); + + PxFlags myFlags; + myFlags |= MyEnum::eMAN; + myFlags |= MyEnum::eBEAR | MyEnum::ePIG; + if(myFlags & MyEnum::eBEAR) + { + doSomething(); + } +*/ + +template +class PxFlags +{ + public: + typedef storagetype InternalType; + + PX_CUDA_CALLABLE PX_INLINE explicit PxFlags(const PxEMPTY) + { + } + PX_CUDA_CALLABLE PX_INLINE PxFlags(void); + PX_CUDA_CALLABLE PX_INLINE PxFlags(enumtype e); + PX_CUDA_CALLABLE PX_INLINE PxFlags(const PxFlags& f); + PX_CUDA_CALLABLE PX_INLINE explicit PxFlags(storagetype b); + + PX_CUDA_CALLABLE PX_INLINE bool isSet(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE PxFlags& set(enumtype e); + PX_CUDA_CALLABLE PX_INLINE bool operator==(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxFlags& f) const; + PX_CUDA_CALLABLE PX_INLINE bool operator==(bool b) const; + PX_CUDA_CALLABLE PX_INLINE bool operator!=(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE bool operator!=(const PxFlags& f) const; + + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator=(const PxFlags& f); + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator=(enumtype e); + + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator|=(enumtype e); + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator|=(const PxFlags& f); + PX_CUDA_CALLABLE PX_INLINE PxFlags operator|(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE PxFlags operator|(const PxFlags& f) const; + + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator&=(enumtype e); + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator&=(const PxFlags& f); + PX_CUDA_CALLABLE PX_INLINE PxFlags operator&(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE PxFlags operator&(const PxFlags& f) const; + + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator^=(enumtype e); + PX_CUDA_CALLABLE PX_INLINE PxFlags& operator^=(const PxFlags& f); + PX_CUDA_CALLABLE PX_INLINE PxFlags operator^(enumtype e) const; + PX_CUDA_CALLABLE PX_INLINE PxFlags operator^(const PxFlags& f) const; + + PX_CUDA_CALLABLE PX_INLINE PxFlags operator~(void) const; + + PX_CUDA_CALLABLE PX_INLINE operator bool(void) const; + PX_CUDA_CALLABLE PX_INLINE operator uint8_t(void) const; + PX_CUDA_CALLABLE PX_INLINE operator uint16_t(void) const; + PX_CUDA_CALLABLE PX_INLINE operator uint32_t(void) const; + + PX_CUDA_CALLABLE PX_INLINE void clear(enumtype e); + + public: + friend PX_INLINE PxFlags operator&(enumtype a, PxFlags& b) + { + PxFlags out; + out.mBits = a & b.mBits; + return out; + } + + private: + storagetype mBits; +}; + +#if !PX_DOXYGEN + +#define PX_FLAGS_OPERATORS(enumtype, storagetype) \ + PX_CUDA_CALLABLE PX_INLINE PxFlags operator|(enumtype a, enumtype b) \ + { \ + PxFlags r(a); \ + r |= b; \ + return r; \ + } \ + PX_CUDA_CALLABLE PX_INLINE PxFlags operator&(enumtype a, enumtype b) \ + { \ + PxFlags r(a); \ + r &= b; \ + return r; \ + } \ + PX_CUDA_CALLABLE PX_INLINE PxFlags operator~(enumtype a) \ + { \ + return ~PxFlags(a); \ + } + +#define PX_FLAGS_TYPEDEF(x, y) \ + typedef PxFlags x##s; \ + PX_FLAGS_OPERATORS(x::Enum, y) + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::PxFlags(void) +{ + mBits = 0; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::PxFlags(enumtype e) +{ + mBits = static_cast(e); +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::PxFlags(const PxFlags& f) +{ + mBits = f.mBits; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::PxFlags(storagetype b) +{ + mBits = b; +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::isSet(enumtype e) const +{ + return (mBits & static_cast(e)) == static_cast(e); +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::set(enumtype e) +{ + mBits = static_cast(e); + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::operator==(enumtype e) const +{ + return mBits == static_cast(e); +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::operator==(const PxFlags& f) const +{ + return mBits == f.mBits; +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::operator==(bool b) const +{ + return bool(*this) == b; +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::operator!=(enumtype e) const +{ + return mBits != static_cast(e); +} + +template +PX_CUDA_CALLABLE PX_INLINE bool PxFlags::operator!=(const PxFlags& f) const +{ + return mBits != f.mBits; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::operator=(enumtype e) +{ + mBits = static_cast(e); + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::operator=(const PxFlags& f) +{ + mBits = f.mBits; + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::operator|=(enumtype e) +{ + mBits |= static_cast(e); + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags:: +operator|=(const PxFlags& f) +{ + mBits |= f.mBits; + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags::operator|(enumtype e) const +{ + PxFlags out(*this); + out |= e; + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags:: +operator|(const PxFlags& f) const +{ + PxFlags out(*this); + out |= f; + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::operator&=(enumtype e) +{ + mBits &= static_cast(e); + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags:: +operator&=(const PxFlags& f) +{ + mBits &= f.mBits; + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags::operator&(enumtype e) const +{ + PxFlags out = *this; + out.mBits &= static_cast(e); + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags:: +operator&(const PxFlags& f) const +{ + PxFlags out = *this; + out.mBits &= f.mBits; + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags::operator^=(enumtype e) +{ + mBits ^= static_cast(e); + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags& PxFlags:: +operator^=(const PxFlags& f) +{ + mBits ^= f.mBits; + return *this; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags::operator^(enumtype e) const +{ + PxFlags out = *this; + out.mBits ^= static_cast(e); + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags:: +operator^(const PxFlags& f) const +{ + PxFlags out = *this; + out.mBits ^= f.mBits; + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags PxFlags::operator~(void) const +{ + PxFlags out; + out.mBits = storagetype(~mBits); + return out; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::operator bool(void) const +{ + return mBits ? true : false; +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::operator uint8_t(void) const +{ + return static_cast(mBits); +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::operator uint16_t(void) const +{ + return static_cast(mBits); +} + +template +PX_CUDA_CALLABLE PX_INLINE PxFlags::operator uint32_t(void) const +{ + return static_cast(mBits); +} + +template +PX_CUDA_CALLABLE PX_INLINE void PxFlags::clear(enumtype e) +{ + mBits &= ~static_cast(e); +} + +} // namespace physx +#endif //!PX_DOXYGEN + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXFLAGS_H diff --git a/src/PhysX/pxshared/include/foundation/PxIO.h b/src/PhysX/pxshared/include/foundation/PxIO.h new file mode 100644 index 000000000..4000c9bcd --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxIO.h @@ -0,0 +1,138 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXIO_H +#define PXFOUNDATION_PXIO_H + +/** \addtogroup common + @{ +*/ + +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Input stream class for I/O. + +The user needs to supply a PxInputStream implementation to a number of methods to allow the SDK to read data. +*/ + +class PxInputStream +{ + public: + /** + \brief read from the stream. The number of bytes read may be less than the number requested. + + \param[in] dest the destination address to which the data will be read + \param[in] count the number of bytes requested + + \return the number of bytes read from the stream. + */ + + virtual uint32_t read(void* dest, uint32_t count) = 0; + + virtual ~PxInputStream() + { + } +}; + +/** +\brief Input data class for I/O which provides random read access. + +The user needs to supply a PxInputData implementation to a number of methods to allow the SDK to read data. +*/ + +class PxInputData : public PxInputStream +{ + public: + /** + \brief return the length of the input data + + \return size in bytes of the input data + */ + + virtual uint32_t getLength() const = 0; + + /** + \brief seek to the given offset from the start of the data. + + \param[in] offset the offset to seek to. If greater than the length of the data, this call is equivalent to + seek(length); + */ + + virtual void seek(uint32_t offset) = 0; + + /** + \brief return the current offset from the start of the data + + \return the offset to seek to. + */ + + virtual uint32_t tell() const = 0; + + virtual ~PxInputData() + { + } +}; + +/** +\brief Output stream class for I/O. + +The user needs to supply a PxOutputStream implementation to a number of methods to allow the SDK to write data. +*/ + +class PxOutputStream +{ + public: + /** + \brief write to the stream. The number of bytes written may be less than the number sent. + + \param[in] src the destination address from which the data will be written + \param[in] count the number of bytes to be written + + \return the number of bytes written to the stream by this call. + */ + + virtual uint32_t write(const void* src, uint32_t count) = 0; + + virtual ~PxOutputStream() + { + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXIO_H diff --git a/src/PhysX/pxshared/include/foundation/PxIntrinsics.h b/src/PhysX/pxshared/include/foundation/PxIntrinsics.h new file mode 100644 index 000000000..d4257168d --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxIntrinsics.h @@ -0,0 +1,47 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXINTRINSICS_H +#define PXFOUNDATION_PXINTRINSICS_H + +#include "foundation/PxPreprocessor.h" + +#if PX_WINDOWS_FAMILY +#include "foundation/windows/PxWindowsIntrinsics.h" +#elif(PX_LINUX || PX_ANDROID || PX_APPLE_FAMILY || PX_PS4) +#include "foundation/unix/PxUnixIntrinsics.h" +#elif PX_XBOXONE +#include "foundation/XboxOne/PxXboxOneIntrinsics.h" +#elif PX_SWITCH +#include "foundation/switch/PxSwitchIntrinsics.h" +#else +#error "Platform not supported!" +#endif + +#endif // #ifndef PXFOUNDATION_PXINTRINSICS_H diff --git a/src/PhysX/pxshared/include/foundation/PxMat33.h b/src/PhysX/pxshared/include/foundation/PxMat33.h new file mode 100644 index 000000000..6cf4257f1 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxMat33.h @@ -0,0 +1,396 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMAT33_H +#define PXFOUNDATION_PXMAT33_H +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxVec3.h" +#include "foundation/PxQuat.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif +/*! +\brief 3x3 matrix class + +Some clarifications, as there have been much confusion about matrix formats etc in the past. + +Short: +- Matrix have base vectors in columns (vectors are column matrices, 3x1 matrices). +- Matrix is physically stored in column major format +- Matrices are concaternated from left + +Long: +Given three base vectors a, b and c the matrix is stored as + +|a.x b.x c.x| +|a.y b.y c.y| +|a.z b.z c.z| + +Vectors are treated as columns, so the vector v is + +|x| +|y| +|z| + +And matrices are applied _before_ the vector (pre-multiplication) +v' = M*v + +|x'| |a.x b.x c.x| |x| |a.x*x + b.x*y + c.x*z| +|y'| = |a.y b.y c.y| * |y| = |a.y*x + b.y*y + c.y*z| +|z'| |a.z b.z c.z| |z| |a.z*x + b.z*y + c.z*z| + + +Physical storage and indexing: +To be compatible with popular 3d rendering APIs (read D3d and OpenGL) +the physical indexing is + +|0 3 6| +|1 4 7| +|2 5 8| + +index = column*3 + row + +which in C++ translates to M[column][row] + +The mathematical indexing is M_row,column and this is what is used for _-notation +so _12 is 1st row, second column and operator(row, column)! + +*/ +class PxMat33 +{ + public: + //! Default constructor + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33() + { + } + + //! identity constructor + PX_CUDA_CALLABLE PX_INLINE PxMat33(PxIDENTITY r) + : column0(1.0f, 0.0f, 0.0f), column1(0.0f, 1.0f, 0.0f), column2(0.0f, 0.0f, 1.0f) + { + PX_UNUSED(r); + } + + //! zero constructor + PX_CUDA_CALLABLE PX_INLINE PxMat33(PxZERO r) : column0(0.0f), column1(0.0f), column2(0.0f) + { + PX_UNUSED(r); + } + + //! Construct from three base vectors + PX_CUDA_CALLABLE PxMat33(const PxVec3& col0, const PxVec3& col1, const PxVec3& col2) + : column0(col0), column1(col1), column2(col2) + { + } + + //! constructor from a scalar, which generates a multiple of the identity matrix + explicit PX_CUDA_CALLABLE PX_INLINE PxMat33(float r) + : column0(r, 0.0f, 0.0f), column1(0.0f, r, 0.0f), column2(0.0f, 0.0f, r) + { + } + + //! Construct from float[9] + explicit PX_CUDA_CALLABLE PX_INLINE PxMat33(float values[]) + : column0(values[0], values[1], values[2]) + , column1(values[3], values[4], values[5]) + , column2(values[6], values[7], values[8]) + { + } + + //! Construct from a quaternion + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33(const PxQuat& q) + { + const float x = q.x; + const float y = q.y; + const float z = q.z; + const float w = q.w; + + const float x2 = x + x; + const float y2 = y + y; + const float z2 = z + z; + + const float xx = x2 * x; + const float yy = y2 * y; + const float zz = z2 * z; + + const float xy = x2 * y; + const float xz = x2 * z; + const float xw = x2 * w; + + const float yz = y2 * z; + const float yw = y2 * w; + const float zw = z2 * w; + + column0 = PxVec3(1.0f - yy - zz, xy + zw, xz - yw); + column1 = PxVec3(xy - zw, 1.0f - xx - zz, yz + xw); + column2 = PxVec3(xz + yw, yz - xw, 1.0f - xx - yy); + } + + //! Copy constructor + PX_CUDA_CALLABLE PX_INLINE PxMat33(const PxMat33& other) + : column0(other.column0), column1(other.column1), column2(other.column2) + { + } + + //! Assignment operator + PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33& operator=(const PxMat33& other) + { + column0 = other.column0; + column1 = other.column1; + column2 = other.column2; + return *this; + } + + //! Construct from diagonal, off-diagonals are zero. + PX_CUDA_CALLABLE PX_INLINE static const PxMat33 createDiagonal(const PxVec3& d) + { + return PxMat33(PxVec3(d.x, 0.0f, 0.0f), PxVec3(0.0f, d.y, 0.0f), PxVec3(0.0f, 0.0f, d.z)); + } + + /** + \brief returns true if the two matrices are exactly equal + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxMat33& m) const + { + return column0 == m.column0 && column1 == m.column1 && column2 == m.column2; + } + + //! Get transposed matrix + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxMat33 getTranspose() const + { + const PxVec3 v0(column0.x, column1.x, column2.x); + const PxVec3 v1(column0.y, column1.y, column2.y); + const PxVec3 v2(column0.z, column1.z, column2.z); + + return PxMat33(v0, v1, v2); + } + + //! Get the real inverse + PX_CUDA_CALLABLE PX_INLINE const PxMat33 getInverse() const + { + const float det = getDeterminant(); + PxMat33 inverse; + + if(det != 0) + { + const float invDet = 1.0f / det; + + inverse.column0.x = invDet * (column1.y * column2.z - column2.y * column1.z); + inverse.column0.y = invDet * -(column0.y * column2.z - column2.y * column0.z); + inverse.column0.z = invDet * (column0.y * column1.z - column0.z * column1.y); + + inverse.column1.x = invDet * -(column1.x * column2.z - column1.z * column2.x); + inverse.column1.y = invDet * (column0.x * column2.z - column0.z * column2.x); + inverse.column1.z = invDet * -(column0.x * column1.z - column0.z * column1.x); + + inverse.column2.x = invDet * (column1.x * column2.y - column1.y * column2.x); + inverse.column2.y = invDet * -(column0.x * column2.y - column0.y * column2.x); + inverse.column2.z = invDet * (column0.x * column1.y - column1.x * column0.y); + + return inverse; + } + else + { + return PxMat33(PxIdentity); + } + } + + //! Get determinant + PX_CUDA_CALLABLE PX_INLINE float getDeterminant() const + { + return column0.dot(column1.cross(column2)); + } + + //! Unary minus + PX_CUDA_CALLABLE PX_INLINE const PxMat33 operator-() const + { + return PxMat33(-column0, -column1, -column2); + } + + //! Add + PX_CUDA_CALLABLE PX_INLINE const PxMat33 operator+(const PxMat33& other) const + { + return PxMat33(column0 + other.column0, column1 + other.column1, column2 + other.column2); + } + + //! Subtract + PX_CUDA_CALLABLE PX_INLINE const PxMat33 operator-(const PxMat33& other) const + { + return PxMat33(column0 - other.column0, column1 - other.column1, column2 - other.column2); + } + + //! Scalar multiplication + PX_CUDA_CALLABLE PX_INLINE const PxMat33 operator*(float scalar) const + { + return PxMat33(column0 * scalar, column1 * scalar, column2 * scalar); + } + + friend PxMat33 operator*(float, const PxMat33&); + + //! Matrix vector multiplication (returns 'this->transform(vec)') + PX_CUDA_CALLABLE PX_INLINE const PxVec3 operator*(const PxVec3& vec) const + { + return transform(vec); + } + + // a = b operators + + //! Matrix multiplication + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxMat33 operator*(const PxMat33& other) const + { + // Rows from this columns from other + // column0 = transform(other.column0) etc + return PxMat33(transform(other.column0), transform(other.column1), transform(other.column2)); + } + + //! Equals-add + PX_CUDA_CALLABLE PX_INLINE PxMat33& operator+=(const PxMat33& other) + { + column0 += other.column0; + column1 += other.column1; + column2 += other.column2; + return *this; + } + + //! Equals-sub + PX_CUDA_CALLABLE PX_INLINE PxMat33& operator-=(const PxMat33& other) + { + column0 -= other.column0; + column1 -= other.column1; + column2 -= other.column2; + return *this; + } + + //! Equals scalar multiplication + PX_CUDA_CALLABLE PX_INLINE PxMat33& operator*=(float scalar) + { + column0 *= scalar; + column1 *= scalar; + column2 *= scalar; + return *this; + } + + //! Equals matrix multiplication + PX_CUDA_CALLABLE PX_INLINE PxMat33& operator*=(const PxMat33& other) + { + *this = *this * other; + return *this; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE float operator()(unsigned int row, unsigned int col) const + { + return (*this)[col][row]; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE float& operator()(unsigned int row, unsigned int col) + { + return (*this)[col][row]; + } + + // Transform etc + + //! Transform vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3 transform(const PxVec3& other) const + { + return column0 * other.x + column1 * other.y + column2 * other.z; + } + + //! Transform vector by matrix transpose, v' = M^t*v + PX_CUDA_CALLABLE PX_INLINE const PxVec3 transformTranspose(const PxVec3& other) const + { + return PxVec3(column0.dot(other), column1.dot(other), column2.dot(other)); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const float* front() const + { + return &column0.x; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator[](unsigned int num) + { + return (&column0)[num]; + } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3& operator[](unsigned int num) const + { + return (&column0)[num]; + } + + // Data, see above for format! + + PxVec3 column0, column1, column2; // the three base vectors +}; + +// implementation from PxQuat.h +PX_CUDA_CALLABLE PX_INLINE PxQuat::PxQuat(const PxMat33& m) +{ + if(m.column2.z < 0) + { + if(m.column0.x > m.column1.y) + { + float t = 1 + m.column0.x - m.column1.y - m.column2.z; + *this = PxQuat(t, m.column0.y + m.column1.x, m.column2.x + m.column0.z, m.column1.z - m.column2.y) * + (0.5f / PxSqrt(t)); + } + else + { + float t = 1 - m.column0.x + m.column1.y - m.column2.z; + *this = PxQuat(m.column0.y + m.column1.x, t, m.column1.z + m.column2.y, m.column2.x - m.column0.z) * + (0.5f / PxSqrt(t)); + } + } + else + { + if(m.column0.x < -m.column1.y) + { + float t = 1 - m.column0.x - m.column1.y + m.column2.z; + *this = PxQuat(m.column2.x + m.column0.z, m.column1.z + m.column2.y, t, m.column0.y - m.column1.x) * + (0.5f / PxSqrt(t)); + } + else + { + float t = 1 + m.column0.x + m.column1.y + m.column2.z; + *this = PxQuat(m.column1.z - m.column2.y, m.column2.x - m.column0.z, m.column0.y - m.column1.x, t) * + (0.5f / PxSqrt(t)); + } + } +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXMAT33_H diff --git a/src/PhysX/pxshared/include/foundation/PxMat44.h b/src/PhysX/pxshared/include/foundation/PxMat44.h new file mode 100644 index 000000000..91500ae67 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxMat44.h @@ -0,0 +1,376 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMAT44_H +#define PXFOUNDATION_PXMAT44_H +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxQuat.h" +#include "foundation/PxVec4.h" +#include "foundation/PxMat33.h" +#include "foundation/PxTransform.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/*! +\brief 4x4 matrix class + +This class is layout-compatible with D3D and OpenGL matrices. More notes on layout are given in the PxMat33 + +@see PxMat33 PxTransform +*/ + +class PxMat44 +{ + public: + //! Default constructor + PX_CUDA_CALLABLE PX_INLINE PxMat44() + { + } + + //! identity constructor + PX_CUDA_CALLABLE PX_INLINE PxMat44(PxIDENTITY r) + : column0(1.0f, 0.0f, 0.0f, 0.0f) + , column1(0.0f, 1.0f, 0.0f, 0.0f) + , column2(0.0f, 0.0f, 1.0f, 0.0f) + , column3(0.0f, 0.0f, 0.0f, 1.0f) + { + PX_UNUSED(r); + } + + //! zero constructor + PX_CUDA_CALLABLE PX_INLINE PxMat44(PxZERO r) : column0(PxZero), column1(PxZero), column2(PxZero), column3(PxZero) + { + PX_UNUSED(r); + } + + //! Construct from four 4-vectors + PX_CUDA_CALLABLE PxMat44(const PxVec4& col0, const PxVec4& col1, const PxVec4& col2, const PxVec4& col3) + : column0(col0), column1(col1), column2(col2), column3(col3) + { + } + + //! constructor that generates a multiple of the identity matrix + explicit PX_CUDA_CALLABLE PX_INLINE PxMat44(float r) + : column0(r, 0.0f, 0.0f, 0.0f) + , column1(0.0f, r, 0.0f, 0.0f) + , column2(0.0f, 0.0f, r, 0.0f) + , column3(0.0f, 0.0f, 0.0f, r) + { + } + + //! Construct from three base vectors and a translation + PX_CUDA_CALLABLE PxMat44(const PxVec3& col0, const PxVec3& col1, const PxVec3& col2, const PxVec3& col3) + : column0(col0, 0), column1(col1, 0), column2(col2, 0), column3(col3, 1.0f) + { + } + + //! Construct from float[16] + explicit PX_CUDA_CALLABLE PX_INLINE PxMat44(float values[]) + : column0(values[0], values[1], values[2], values[3]) + , column1(values[4], values[5], values[6], values[7]) + , column2(values[8], values[9], values[10], values[11]) + , column3(values[12], values[13], values[14], values[15]) + { + } + + //! Construct from a quaternion + explicit PX_CUDA_CALLABLE PX_INLINE PxMat44(const PxQuat& q) + { + const float x = q.x; + const float y = q.y; + const float z = q.z; + const float w = q.w; + + const float x2 = x + x; + const float y2 = y + y; + const float z2 = z + z; + + const float xx = x2 * x; + const float yy = y2 * y; + const float zz = z2 * z; + + const float xy = x2 * y; + const float xz = x2 * z; + const float xw = x2 * w; + + const float yz = y2 * z; + const float yw = y2 * w; + const float zw = z2 * w; + + column0 = PxVec4(1.0f - yy - zz, xy + zw, xz - yw, 0.0f); + column1 = PxVec4(xy - zw, 1.0f - xx - zz, yz + xw, 0.0f); + column2 = PxVec4(xz + yw, yz - xw, 1.0f - xx - yy, 0.0f); + column3 = PxVec4(0.0f, 0.0f, 0.0f, 1.0f); + } + + //! Construct from a diagonal vector + explicit PX_CUDA_CALLABLE PX_INLINE PxMat44(const PxVec4& diagonal) + : column0(diagonal.x, 0.0f, 0.0f, 0.0f) + , column1(0.0f, diagonal.y, 0.0f, 0.0f) + , column2(0.0f, 0.0f, diagonal.z, 0.0f) + , column3(0.0f, 0.0f, 0.0f, diagonal.w) + { + } + + //! Construct from Mat33 and a translation + PX_CUDA_CALLABLE PxMat44(const PxMat33& axes, const PxVec3& position) + : column0(axes.column0, 0.0f), column1(axes.column1, 0.0f), column2(axes.column2, 0.0f), column3(position, 1.0f) + { + } + + PX_CUDA_CALLABLE PxMat44(const PxTransform& t) + { + *this = PxMat44(PxMat33(t.q), t.p); + } + + /** + \brief returns true if the two matrices are exactly equal + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxMat44& m) const + { + return column0 == m.column0 && column1 == m.column1 && column2 == m.column2 && column3 == m.column3; + } + + //! Copy constructor + PX_CUDA_CALLABLE PX_INLINE PxMat44(const PxMat44& other) + : column0(other.column0), column1(other.column1), column2(other.column2), column3(other.column3) + { + } + + //! Assignment operator + PX_CUDA_CALLABLE PX_INLINE PxMat44& operator=(const PxMat44& other) + { + column0 = other.column0; + column1 = other.column1; + column2 = other.column2; + column3 = other.column3; + return *this; + } + + //! Get transposed matrix + PX_CUDA_CALLABLE PX_INLINE const PxMat44 getTranspose() const + { + return PxMat44( + PxVec4(column0.x, column1.x, column2.x, column3.x), PxVec4(column0.y, column1.y, column2.y, column3.y), + PxVec4(column0.z, column1.z, column2.z, column3.z), PxVec4(column0.w, column1.w, column2.w, column3.w)); + } + + //! Unary minus + PX_CUDA_CALLABLE PX_INLINE const PxMat44 operator-() const + { + return PxMat44(-column0, -column1, -column2, -column3); + } + + //! Add + PX_CUDA_CALLABLE PX_INLINE const PxMat44 operator+(const PxMat44& other) const + { + return PxMat44(column0 + other.column0, column1 + other.column1, column2 + other.column2, + column3 + other.column3); + } + + //! Subtract + PX_CUDA_CALLABLE PX_INLINE const PxMat44 operator-(const PxMat44& other) const + { + return PxMat44(column0 - other.column0, column1 - other.column1, column2 - other.column2, + column3 - other.column3); + } + + //! Scalar multiplication + PX_CUDA_CALLABLE PX_INLINE const PxMat44 operator*(float scalar) const + { + return PxMat44(column0 * scalar, column1 * scalar, column2 * scalar, column3 * scalar); + } + + friend PxMat44 operator*(float, const PxMat44&); + + //! Matrix multiplication + PX_CUDA_CALLABLE PX_INLINE const PxMat44 operator*(const PxMat44& other) const + { + // Rows from this columns from other + // column0 = transform(other.column0) etc + return PxMat44(transform(other.column0), transform(other.column1), transform(other.column2), + transform(other.column3)); + } + + // a = b operators + + //! Equals-add + PX_CUDA_CALLABLE PX_INLINE PxMat44& operator+=(const PxMat44& other) + { + column0 += other.column0; + column1 += other.column1; + column2 += other.column2; + column3 += other.column3; + return *this; + } + + //! Equals-sub + PX_CUDA_CALLABLE PX_INLINE PxMat44& operator-=(const PxMat44& other) + { + column0 -= other.column0; + column1 -= other.column1; + column2 -= other.column2; + column3 -= other.column3; + return *this; + } + + //! Equals scalar multiplication + PX_CUDA_CALLABLE PX_INLINE PxMat44& operator*=(float scalar) + { + column0 *= scalar; + column1 *= scalar; + column2 *= scalar; + column3 *= scalar; + return *this; + } + + //! Equals matrix multiplication + PX_CUDA_CALLABLE PX_INLINE PxMat44& operator*=(const PxMat44& other) + { + *this = *this * other; + return *this; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE float operator()(unsigned int row, unsigned int col) const + { + return (*this)[col][row]; + } + + //! Element access, mathematical way! + PX_CUDA_CALLABLE PX_FORCE_INLINE float& operator()(unsigned int row, unsigned int col) + { + return (*this)[col][row]; + } + + //! Transform vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_INLINE const PxVec4 transform(const PxVec4& other) const + { + return column0 * other.x + column1 * other.y + column2 * other.z + column3 * other.w; + } + + //! Transform vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_INLINE const PxVec3 transform(const PxVec3& other) const + { + return transform(PxVec4(other, 1.0f)).getXYZ(); + } + + //! Rotate vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_INLINE const PxVec4 rotate(const PxVec4& other) const + { + return column0 * other.x + column1 * other.y + column2 * other.z; // + column3*0; + } + + //! Rotate vector by matrix, equal to v' = M*v + PX_CUDA_CALLABLE PX_INLINE const PxVec3 rotate(const PxVec3& other) const + { + return rotate(PxVec4(other, 1.0f)).getXYZ(); + } + + PX_CUDA_CALLABLE PX_INLINE const PxVec3 getBasis(int num) const + { + PX_ASSERT(num >= 0 && num < 3); + return (&column0)[num].getXYZ(); + } + + PX_CUDA_CALLABLE PX_INLINE const PxVec3 getPosition() const + { + return column3.getXYZ(); + } + + PX_CUDA_CALLABLE PX_INLINE void setPosition(const PxVec3& position) + { + column3.x = position.x; + column3.y = position.y; + column3.z = position.z; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE const float* front() const + { + return &column0.x; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec4& operator[](unsigned int num) + { + return (&column0)[num]; + } + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec4& operator[](unsigned int num) const + { + return (&column0)[num]; + } + + PX_CUDA_CALLABLE PX_INLINE void scale(const PxVec4& p) + { + column0 *= p.x; + column1 *= p.y; + column2 *= p.z; + column3 *= p.w; + } + + PX_CUDA_CALLABLE PX_INLINE const PxMat44 inverseRT(void) const + { + PxVec3 r0(column0.x, column1.x, column2.x), r1(column0.y, column1.y, column2.y), + r2(column0.z, column1.z, column2.z); + + return PxMat44(r0, r1, r2, -(r0 * column3.x + r1 * column3.y + r2 * column3.z)); + } + + PX_CUDA_CALLABLE PX_INLINE bool isFinite() const + { + return column0.isFinite() && column1.isFinite() && column2.isFinite() && column3.isFinite(); + } + + // Data, see above for format! + + PxVec4 column0, column1, column2, column3; // the four base vectors +}; + +// implementation from PxTransform.h +PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform::PxTransform(const PxMat44& m) +{ + PxVec3 column0 = PxVec3(m.column0.x, m.column0.y, m.column0.z); + PxVec3 column1 = PxVec3(m.column1.x, m.column1.y, m.column1.z); + PxVec3 column2 = PxVec3(m.column2.x, m.column2.y, m.column2.z); + + q = PxQuat(PxMat33(column0, column1, column2)); + p = PxVec3(m.column3.x, m.column3.y, m.column3.z); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXMAT44_H diff --git a/src/PhysX/pxshared/include/foundation/PxMath.h b/src/PhysX/pxshared/include/foundation/PxMath.h new file mode 100644 index 000000000..6f4817fcb --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxMath.h @@ -0,0 +1,338 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMATH_H +#define PXFOUNDATION_PXMATH_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxPreprocessor.h" + +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4985) // 'symbol name': attributes not present on previous declaration +#endif +#include +#if PX_VC +#pragma warning(pop) +#endif + +#include +#include "foundation/PxIntrinsics.h" +#include "foundation/PxAssert.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +// constants +static const float PxPi = float(3.141592653589793); +static const float PxHalfPi = float(1.57079632679489661923); +static const float PxTwoPi = float(6.28318530717958647692); +static const float PxInvPi = float(0.31830988618379067154); +static const float PxInvTwoPi = float(0.15915494309189533577); +static const float PxPiDivTwo = float(1.57079632679489661923); +static const float PxPiDivFour = float(0.78539816339744830962); + +/** +\brief The return value is the greater of the two specified values. +*/ +template +PX_CUDA_CALLABLE PX_FORCE_INLINE T PxMax(T a, T b) +{ + return a < b ? b : a; +} + +//! overload for float to use fsel on xbox +template <> +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxMax(float a, float b) +{ + return intrinsics::selectMax(a, b); +} + +/** +\brief The return value is the lesser of the two specified values. +*/ +template +PX_CUDA_CALLABLE PX_FORCE_INLINE T PxMin(T a, T b) +{ + return a < b ? a : b; +} + +template <> +//! overload for float to use fsel on xbox +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxMin(float a, float b) +{ + return intrinsics::selectMin(a, b); +} + +/* +Many of these are just implemented as PX_CUDA_CALLABLE PX_FORCE_INLINE calls to the C lib right now, +but later we could replace some of them with some approximations or more +clever stuff. +*/ + +/** +\brief abs returns the absolute value of its argument. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAbs(float a) +{ + return intrinsics::abs(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxEquals(float a, float b, float eps) +{ + return (PxAbs(a - b) < eps); +} + +/** +\brief abs returns the absolute value of its argument. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxAbs(double a) +{ + return ::fabs(a); +} + +/** +\brief abs returns the absolute value of its argument. +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE int32_t PxAbs(int32_t a) +{ + return ::abs(a); +} + +/** +\brief Clamps v to the range [hi,lo] +*/ +template +PX_CUDA_CALLABLE PX_FORCE_INLINE T PxClamp(T v, T lo, T hi) +{ + PX_ASSERT(lo <= hi); + return PxMin(hi, PxMax(lo, v)); +} + +//! \brief Square root. +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxSqrt(float a) +{ + return intrinsics::sqrt(a); +} + +//! \brief Square root. +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxSqrt(double a) +{ + return ::sqrt(a); +} + +//! \brief reciprocal square root. +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxRecipSqrt(float a) +{ + return intrinsics::recipSqrt(a); +} + +//! \brief reciprocal square root. +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxRecipSqrt(double a) +{ + return 1 / ::sqrt(a); +} + +//! trigonometry -- all angles are in radians. + +//! \brief Sine of an angle ( Unit: Radians ) +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxSin(float a) +{ + return intrinsics::sin(a); +} + +//! \brief Sine of an angle ( Unit: Radians ) +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxSin(double a) +{ + return ::sin(a); +} + +//! \brief Cosine of an angle (Unit: Radians) +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxCos(float a) +{ + return intrinsics::cos(a); +} + +//! \brief Cosine of an angle (Unit: Radians) +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxCos(double a) +{ + return ::cos(a); +} + +/** +\brief Tangent of an angle. +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxTan(float a) +{ + return ::tanf(a); +} + +/** +\brief Tangent of an angle. +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxTan(double a) +{ + return ::tan(a); +} + +/** +\brief Arcsine. +Returns angle between -PI/2 and PI/2 in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAsin(float f) +{ + return ::asinf(PxClamp(f, -1.0f, 1.0f)); +} + +/** +\brief Arcsine. +Returns angle between -PI/2 and PI/2 in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxAsin(double f) +{ + return ::asin(PxClamp(f, -1.0, 1.0)); +} + +/** +\brief Arccosine. +Returns angle between 0 and PI in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAcos(float f) +{ + return ::acosf(PxClamp(f, -1.0f, 1.0f)); +} + +/** +\brief Arccosine. +Returns angle between 0 and PI in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxAcos(double f) +{ + return ::acos(PxClamp(f, -1.0, 1.0)); +} + +/** +\brief ArcTangent. +Returns angle between -PI/2 and PI/2 in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAtan(float a) +{ + return ::atanf(a); +} + +/** +\brief ArcTangent. +Returns angle between -PI/2 and PI/2 in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxAtan(double a) +{ + return ::atan(a); +} + +/** +\brief Arctangent of (x/y) with correct sign. +Returns angle between -PI and PI in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxAtan2(float x, float y) +{ + return ::atan2f(x, y); +} + +/** +\brief Arctangent of (x/y) with correct sign. +Returns angle between -PI and PI in radians +Unit: Radians +*/ +PX_CUDA_CALLABLE PX_FORCE_INLINE double PxAtan2(double x, double y) +{ + return ::atan2(x, y); +} + +//! \brief returns true if the passed number is a finite floating point number as opposed to INF, NAN, etc. +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxIsFinite(float f) +{ + return intrinsics::isFinite(f); +} + +//! \brief returns true if the passed number is a finite floating point number as opposed to INF, NAN, etc. +PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxIsFinite(double f) +{ + return intrinsics::isFinite(f); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxFloor(float a) +{ + return ::floorf(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxExp(float a) +{ + return ::expf(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxCeil(float a) +{ + return ::ceilf(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxSign(float a) +{ + return physx::intrinsics::sign(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxPow(float x, float y) +{ + return ::powf(x, y); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float PxLog(float x) +{ + return ::logf(x); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXMATH_H diff --git a/src/PhysX/pxshared/include/foundation/PxMathUtils.h b/src/PhysX/pxshared/include/foundation/PxMathUtils.h new file mode 100644 index 000000000..737ff73d7 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxMathUtils.h @@ -0,0 +1,73 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMATHUTILS_H +#define PXFOUNDATION_PXMATHUTILS_H + +/** \addtogroup common + @{ +*/ + +#include "foundation/Px.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief finds the shortest rotation between two vectors. + +\param[in] from the vector to start from +\param[in] target the vector to rotate to +\return a rotation about an axis normal to the two vectors which takes one to the other via the shortest path +*/ + +PX_FOUNDATION_API PxQuat PxShortestRotation(const PxVec3& from, const PxVec3& target); + +/* \brief diagonalizes a 3x3 symmetric matrix y + +The returned matrix satisfies M = R * D * R', where R is the rotation matrix for the output quaternion, R' its +transpose, and D the diagonal matrix + +If the matrix is not symmetric, the result is undefined. + +\param[in] m the matrix to diagonalize +\param[out] axes a quaternion rotation which diagonalizes the matrix +\return the vector diagonal of the diagonalized matrix. +*/ + +PX_FOUNDATION_API PxVec3 PxDiagonalize(const PxMat33& m, PxQuat& axes); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/pxshared/include/foundation/PxMemory.h b/src/PhysX/pxshared/include/foundation/PxMemory.h new file mode 100644 index 000000000..68d721def --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxMemory.h @@ -0,0 +1,110 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMEMORY_H +#define PXFOUNDATION_PXMEMORY_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/Px.h" +#include "foundation/PxIntrinsics.h" +#include "foundation/PxSimpleTypes.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Sets the bytes of the provided buffer to zero. + +\param dest Pointer to block of memory to set zero. +\param count Number of bytes to set to zero. + +\return Pointer to memory block (same as input) +*/ +PX_FORCE_INLINE void* PxMemZero(void* dest, PxU32 count) +{ + return physx::intrinsics::memZero(dest, count); +} + +/** +\brief Sets the bytes of the provided buffer to the specified value. + +\param dest Pointer to block of memory to set to the specified value. +\param c Value to set the bytes of the block of memory to. +\param count Number of bytes to set to the specified value. + +\return Pointer to memory block (same as input) +*/ +PX_FORCE_INLINE void* PxMemSet(void* dest, PxI32 c, PxU32 count) +{ + return physx::intrinsics::memSet(dest, c, count); +} + +/** +\brief Copies the bytes of one memory block to another. The memory blocks must not overlap. + +\note Use #PxMemMove if memory blocks overlap. + +\param dest Pointer to block of memory to copy to. +\param src Pointer to block of memory to copy from. +\param count Number of bytes to copy. + +\return Pointer to destination memory block +*/ +PX_FORCE_INLINE void* PxMemCopy(void* dest, const void* src, PxU32 count) +{ + return physx::intrinsics::memCopy(dest, src, count); +} + +/** +\brief Copies the bytes of one memory block to another. The memory blocks can overlap. + +\note Use #PxMemCopy if memory blocks do not overlap. + +\param dest Pointer to block of memory to copy to. +\param src Pointer to block of memory to copy from. +\param count Number of bytes to copy. + +\return Pointer to destination memory block +*/ +PX_FORCE_INLINE void* PxMemMove(void* dest, const void* src, PxU32 count) +{ + return physx::intrinsics::memMove(dest, src, count); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PXFOUNDATION_PXMEMORY_H diff --git a/src/PhysX/pxshared/include/foundation/PxPlane.h b/src/PhysX/pxshared/include/foundation/PxPlane.h new file mode 100644 index 000000000..618d3f077 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxPlane.h @@ -0,0 +1,145 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXPLANE_H +#define PXFOUNDATION_PXPLANE_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxMath.h" +#include "foundation/PxVec3.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Representation of a plane. + + Plane equation used: n.dot(v) + d = 0 +*/ +class PxPlane +{ + public: + /** + \brief Constructor + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane() + { + } + + /** + \brief Constructor from a normal and a distance + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane(float nx, float ny, float nz, float distance) : n(nx, ny, nz), d(distance) + { + } + + /** + \brief Constructor from a normal and a distance + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane(const PxVec3& normal, float distance) : n(normal), d(distance) + { + } + + /** + \brief Constructor from a point on the plane and a normal + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane(const PxVec3& point, const PxVec3& normal) + : n(normal), d(-point.dot(n)) // p satisfies normal.dot(p) + d = 0 + { + } + + /** + \brief Constructor from three points + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane(const PxVec3& p0, const PxVec3& p1, const PxVec3& p2) + { + n = (p1 - p0).cross(p2 - p0).getNormalized(); + d = -p0.dot(n); + } + + /** + \brief returns true if the two planes are exactly equal + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxPlane& p) const + { + return n == p.n && d == p.d; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE float distance(const PxVec3& p) const + { + return p.dot(n) + d; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE bool contains(const PxVec3& p) const + { + return PxAbs(distance(p)) < (1.0e-7f); + } + + /** + \brief projects p into the plane + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 project(const PxVec3& p) const + { + return p - n * distance(p); + } + + /** + \brief find an arbitrary point in the plane + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 pointInPlane() const + { + return -n * d; + } + + /** + \brief equivalent plane with unit normal + */ + + PX_CUDA_CALLABLE PX_FORCE_INLINE void normalize() + { + float denom = 1.0f / n.magnitude(); + n *= denom; + d *= denom; + } + + PxVec3 n; //!< The normal to the plane + float d; //!< The distance from the origin +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXPLANE_H diff --git a/src/PhysX/pxshared/include/foundation/PxPreprocessor.h b/src/PhysX/pxshared/include/foundation/PxPreprocessor.h new file mode 100644 index 000000000..13d84d93a --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxPreprocessor.h @@ -0,0 +1,553 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXPREPROCESSOR_H +#define PXFOUNDATION_PXPREPROCESSOR_H + +#include +#if !defined(PX_GENERATE_META_DATA) +#include +#endif +/** \addtogroup foundation + @{ +*/ + +#define PX_STRINGIZE_HELPER(X) #X +#define PX_STRINGIZE(X) PX_STRINGIZE_HELPER(X) + +#define PX_CONCAT_HELPER(X, Y) X##Y +#define PX_CONCAT(X, Y) PX_CONCAT_HELPER(X, Y) + +/* +The following preprocessor identifiers specify compiler, OS, and architecture. +All definitions have a value of 1 or 0, use '#if' instead of '#ifdef'. +*/ + +/** +Compiler defines, see http://sourceforge.net/p/predef/wiki/Compilers/ +*/ +#if defined(_MSC_VER) +#if _MSC_VER >= 1910 +#define PX_VC 15 +#elif _MSC_VER >= 1900 +#define PX_VC 14 +#elif _MSC_VER >= 1800 +#define PX_VC 12 +#elif _MSC_VER >= 1700 +#define PX_VC 11 +#elif _MSC_VER >= 1600 +#define PX_VC 10 +#elif _MSC_VER >= 1500 +#define PX_VC 9 +#else +#error "Unknown VC version" +#endif +#elif defined(__clang__) +#define PX_CLANG 1 +#elif defined(__GNUC__) // note: __clang__ implies __GNUC__ +#define PX_GCC 1 +#else +#error "Unknown compiler" +#endif + +/** +Operating system defines, see http://sourceforge.net/p/predef/wiki/OperatingSystems/ +*/ +#if defined(_XBOX_ONE) +#define PX_XBOXONE 1 +#elif defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_APP +#define PX_UWP 1 +#elif defined(_WIN64) // note: _XBOX_ONE implies _WIN64 +#define PX_WIN64 1 +#elif defined(_WIN32) // note: _M_PPC implies _WIN32 +#define PX_WIN32 1 +#elif defined(__ANDROID__) +#define PX_ANDROID 1 +#elif defined(__linux__) || defined (__EMSCRIPTEN__) // note: __ANDROID__ implies __linux__ +#define PX_LINUX 1 +#elif defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) +#define PX_IOS 1 +#elif defined(__APPLE__) +#define PX_OSX 1 +#elif defined(__ORBIS__) +#define PX_PS4 1 +#elif defined(__NX__) +#define PX_SWITCH 1 +#else +#error "Unknown operating system" +#endif + +/** +Architecture defines, see http://sourceforge.net/p/predef/wiki/Architectures/ +*/ +#if defined(__x86_64__) || defined(_M_X64) // ps4 compiler defines _M_X64 without value +#define PX_X64 1 +#elif defined(__i386__) || defined(_M_IX86) || defined (__EMSCRIPTEN__) +#define PX_X86 1 +#elif defined(__arm64__) || defined(__aarch64__) +#define PX_A64 1 +#elif defined(__arm__) || defined(_M_ARM) +#define PX_ARM 1 +#elif defined(__ppc__) || defined(_M_PPC) || defined(__CELLOS_LV2__) +#define PX_PPC 1 +#else +#error "Unknown architecture" +#endif + +/** +SIMD defines +*/ +#if !defined(PX_SIMD_DISABLED) +#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) || (defined (__EMSCRIPTEN__) && defined(__SSE2__)) +#define PX_SSE2 1 +#endif +#if defined(_M_ARM) || defined(__ARM_NEON__) || defined(__ARM_NEON) +#define PX_NEON 1 +#endif +#if defined(_M_PPC) || defined(__CELLOS_LV2__) +#define PX_VMX 1 +#endif +#endif + +/** +define anything not defined on this platform to 0 +*/ +#ifndef PX_VC +#define PX_VC 0 +#endif +#ifndef PX_CLANG +#define PX_CLANG 0 +#endif +#ifndef PX_GCC +#define PX_GCC 0 +#endif +#ifndef PX_XBOXONE +#define PX_XBOXONE 0 +#endif +#ifndef PX_WIN64 +#define PX_WIN64 0 +#endif +#ifndef PX_WIN32 +#define PX_WIN32 0 +#endif +#ifndef PX_ANDROID +#define PX_ANDROID 0 +#endif +#ifndef PX_LINUX +#define PX_LINUX 0 +#endif +#ifndef PX_IOS +#define PX_IOS 0 +#endif +#ifndef PX_OSX +#define PX_OSX 0 +#endif +#ifndef PX_PS4 +#define PX_PS4 0 +#endif +#ifndef PX_SWITCH +#define PX_SWITCH 0 +#endif +#ifndef PX_UWP +#define PX_UWP 0 +#endif +#ifndef PX_X64 +#define PX_X64 0 +#endif +#ifndef PX_X86 +#define PX_X86 0 +#endif +#ifndef PX_A64 +#define PX_A64 0 +#endif +#ifndef PX_ARM +#define PX_ARM 0 +#endif +#ifndef PX_PPC +#define PX_PPC 0 +#endif +#ifndef PX_SSE2 +#define PX_SSE2 0 +#endif +#ifndef PX_NEON +#define PX_NEON 0 +#endif +#ifndef PX_VMX +#define PX_VMX 0 +#endif + +/* +define anything not defined through the command line to 0 +*/ +#ifndef PX_DEBUG +#define PX_DEBUG 0 +#endif +#ifndef PX_CHECKED +#define PX_CHECKED 0 +#endif +#ifndef PX_PROFILE +#define PX_PROFILE 0 +#endif +#ifndef PX_DEBUG_CRT +#define PX_DEBUG_CRT 0 +#endif +#ifndef PX_NVTX +#define PX_NVTX 0 +#endif +#ifndef PX_DOXYGEN +#define PX_DOXYGEN 0 +#endif + +/** +family shortcuts +*/ +// compiler +#define PX_GCC_FAMILY (PX_CLANG || PX_GCC) +// os +#define PX_WINDOWS_FAMILY (PX_WIN32 || PX_WIN64 || PX_UWP) +#define PX_MICROSOFT_FAMILY (PX_XBOXONE || PX_WINDOWS_FAMILY) +#define PX_LINUX_FAMILY (PX_LINUX || PX_ANDROID) +#define PX_APPLE_FAMILY (PX_IOS || PX_OSX) // equivalent to #if __APPLE__ +#define PX_UNIX_FAMILY (PX_LINUX_FAMILY || PX_APPLE_FAMILY) // shortcut for unix/posix platforms +#if defined(__EMSCRIPTEN__) +#define PX_EMSCRIPTEN 1 +#else +#define PX_EMSCRIPTEN 0 +#endif +// architecture +#define PX_INTEL_FAMILY (PX_X64 || PX_X86) +#define PX_ARM_FAMILY (PX_ARM || PX_A64) +#define PX_P64_FAMILY (PX_X64 || PX_A64) // shortcut for 64-bit architectures + +/** +C++ standard library defines +*/ +#if defined(_LIBCPP_VERSION) || PX_WIN64 || PX_WIN32 || PX_PS4 || PX_XBOXONE || PX_UWP || PX_EMSCRIPTEN +#define PX_LIBCPP 1 +#else +#define PX_LIBCPP 0 +#endif + +// legacy define for PhysX +#define PX_WINDOWS (PX_WINDOWS_FAMILY && !PX_ARM_FAMILY) + +/** +Assert macro +*/ +#ifndef PX_ENABLE_ASSERTS +#if PX_DEBUG && !defined(__CUDACC__) +#define PX_ENABLE_ASSERTS 1 +#else +#define PX_ENABLE_ASSERTS 0 +#endif +#endif + +/** +DLL export macros +*/ +#ifndef PX_C_EXPORT +#if PX_WINDOWS_FAMILY || PX_LINUX +#define PX_C_EXPORT extern "C" +#else +#define PX_C_EXPORT +#endif +#endif + +#if PX_UNIX_FAMILY&& __GNUC__ >= 4 +#define PX_UNIX_EXPORT __attribute__((visibility("default"))) +#else +#define PX_UNIX_EXPORT +#endif + +#if PX_WINDOWS_FAMILY +#define PX_DLL_EXPORT __declspec(dllexport) +#define PX_DLL_IMPORT __declspec(dllimport) +#else +#define PX_DLL_EXPORT PX_UNIX_EXPORT +#define PX_DLL_IMPORT +#endif + +/** +Define API function declaration + +PX_FOUNDATION_DLL=1 - used by the DLL library (PhysXCommon) to export the API +PX_FOUNDATION_DLL=0 - for windows configurations where the PX_FOUNDATION_API is linked through standard static linking +no definition - this will allow DLLs and libraries to use the exported API from PhysXCommon + +*/ + +#if PX_WINDOWS_FAMILY && !PX_ARM_FAMILY +#ifndef PX_FOUNDATION_DLL +#define PX_FOUNDATION_API PX_DLL_IMPORT +#elif PX_FOUNDATION_DLL +#define PX_FOUNDATION_API PX_DLL_EXPORT +#endif +#elif PX_UNIX_FAMILY +#ifdef PX_FOUNDATION_DLL +#define PX_FOUNDATION_API PX_UNIX_EXPORT +#endif +#endif + +#ifndef PX_FOUNDATION_API +#define PX_FOUNDATION_API +#endif + +/** +Calling convention +*/ +#ifndef PX_CALL_CONV +#if PX_MICROSOFT_FAMILY +#define PX_CALL_CONV __cdecl +#else +#define PX_CALL_CONV +#endif +#endif + +/** +Pack macros - disabled on SPU because they are not supported +*/ +#if PX_VC +#define PX_PUSH_PACK_DEFAULT __pragma(pack(push, 8)) +#define PX_POP_PACK __pragma(pack(pop)) +#elif PX_GCC_FAMILY +#define PX_PUSH_PACK_DEFAULT _Pragma("pack(push, 8)") +#define PX_POP_PACK _Pragma("pack(pop)") +#else +#define PX_PUSH_PACK_DEFAULT +#define PX_POP_PACK +#endif + +/** +Inline macro +*/ +#define PX_INLINE inline +#if PX_MICROSOFT_FAMILY +#pragma inline_depth(255) +#endif + +/** +Force inline macro +*/ +#if PX_VC +#define PX_FORCE_INLINE __forceinline +#elif PX_LINUX // Workaround; Fedora Core 3 do not agree with force inline and PxcPool +#define PX_FORCE_INLINE inline +#elif PX_GCC_FAMILY +#define PX_FORCE_INLINE inline __attribute__((always_inline)) +#else +#define PX_FORCE_INLINE inline +#endif + +/** +Noinline macro +*/ +#if PX_MICROSOFT_FAMILY +#define PX_NOINLINE __declspec(noinline) +#elif PX_GCC_FAMILY +#define PX_NOINLINE __attribute__((noinline)) +#else +#define PX_NOINLINE +#endif + +/** +Restrict macro +*/ +#if defined(__CUDACC__) +#define PX_RESTRICT __restrict__ +#else +#define PX_RESTRICT __restrict +#endif + +/** +Noalias macro +*/ +#if PX_MICROSOFT_FAMILY +#define PX_NOALIAS __declspec(noalias) +#else +#define PX_NOALIAS +#endif + +/** +Alignment macros + +PX_ALIGN_PREFIX and PX_ALIGN_SUFFIX can be used for type alignment instead of aligning individual variables as follows: +PX_ALIGN_PREFIX(16) +struct A { +... +} PX_ALIGN_SUFFIX(16); +This declaration style is parsed correctly by Visual Assist. + +*/ +#ifndef PX_ALIGN +#if PX_MICROSOFT_FAMILY +#define PX_ALIGN(alignment, decl) __declspec(align(alignment)) decl +#define PX_ALIGN_PREFIX(alignment) __declspec(align(alignment)) +#define PX_ALIGN_SUFFIX(alignment) +#elif PX_GCC_FAMILY +#define PX_ALIGN(alignment, decl) decl __attribute__((aligned(alignment))) +#define PX_ALIGN_PREFIX(alignment) +#define PX_ALIGN_SUFFIX(alignment) __attribute__((aligned(alignment))) +#elif defined __CUDACC__ +#define PX_ALIGN(alignment, decl) __align__(alignment) decl +#define PX_ALIGN_PREFIX(alignment) +#define PX_ALIGN_SUFFIX(alignment) __align__(alignment)) +#else +#define PX_ALIGN(alignment, decl) +#define PX_ALIGN_PREFIX(alignment) +#define PX_ALIGN_SUFFIX(alignment) +#endif +#endif + +/** +Deprecated macro +- To deprecate a function: Place PX_DEPRECATED at the start of the function header (leftmost word). +- To deprecate a 'typedef', a 'struct' or a 'class': Place PX_DEPRECATED directly after the keywords ('typdef', +'struct', 'class'). + +Use these macro definitions to create warnings for deprecated functions +\#define PX_DEPRECATED __declspec(deprecated) // Microsoft +\#define PX_DEPRECATED __attribute__((deprecated())) // GCC +*/ +#define PX_DEPRECATED + +/** +General defines +*/ + +// static assert +#if(defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || (PX_PS4) || (PX_APPLE_FAMILY) || (PX_SWITCH) || (PX_CLANG && PX_ARM) +#define PX_COMPILE_TIME_ASSERT(exp) typedef char PX_CONCAT(PxCompileTimeAssert_Dummy, __COUNTER__)[(exp) ? 1 : -1] __attribute__((unused)) +#else +#define PX_COMPILE_TIME_ASSERT(exp) typedef char PxCompileTimeAssert_Dummy[(exp) ? 1 : -1] +#endif + +#if PX_GCC_FAMILY +#define PX_OFFSET_OF(X, Y) __builtin_offsetof(X, Y) +#else +#define PX_OFFSET_OF(X, Y) offsetof(X, Y) +#endif + +#define PX_OFFSETOF_BASE 0x100 // casting the null ptr takes a special-case code path, which we don't want +#define PX_OFFSET_OF_RT(Class, Member) \ + (reinterpret_cast(&reinterpret_cast(PX_OFFSETOF_BASE)->Member) - size_t(PX_OFFSETOF_BASE)) + +// check that exactly one of NDEBUG and _DEBUG is defined +#if !defined(NDEBUG) ^ defined(_DEBUG) +#error Exactly one of NDEBUG and _DEBUG needs to be defined! +#endif + +// make sure PX_CHECKED is defined in all _DEBUG configurations as well +#if !PX_CHECKED && PX_DEBUG +#error PX_CHECKED must be defined when PX_DEBUG is defined +#endif + +#ifdef __CUDACC__ +#define PX_CUDA_CALLABLE __host__ __device__ +#else +#define PX_CUDA_CALLABLE +#endif + +// avoid unreferenced parameter warning +// preferred solution: omit the parameter's name from the declaration +template +PX_CUDA_CALLABLE PX_INLINE void PX_UNUSED(T const&) +{ +} + +// Ensure that the application hasn't tweaked the pack value to less than 8, which would break +// matching between the API headers and the binaries +// This assert works on win32/win64, but may need further specialization on other platforms. +// Some GCC compilers need the compiler flag -malign-double to be set. +// Apparently the apple-clang-llvm compiler doesn't support malign-double. +#if PX_PS4 || PX_APPLE_FAMILY || (PX_CLANG && !PX_ARM) +struct PxPackValidation +{ + char _; + long a; +}; +#elif PX_ANDROID || (PX_CLANG && PX_ARM) +struct PxPackValidation +{ + char _; + double a; +}; +#else +struct PxPackValidation +{ + char _; + long long a; +}; +#endif +// clang (as of version 3.9) cannot align doubles on 8 byte boundary when compiling for Intel 32 bit target +#if !PX_APPLE_FAMILY && !PX_EMSCRIPTEN && !(PX_CLANG && PX_X86) +PX_COMPILE_TIME_ASSERT(PX_OFFSET_OF(PxPackValidation, a) == 8); +#endif + +// use in a cpp file to suppress LNK4221 +#if PX_VC +#define PX_DUMMY_SYMBOL \ + namespace \ + { \ + char PxDummySymbol; \ + } +#else +#define PX_DUMMY_SYMBOL +#endif + +#if PX_GCC_FAMILY +#define PX_WEAK_SYMBOL __attribute__((weak)) // this is to support SIMD constant merging in template specialization +#else +#define PX_WEAK_SYMBOL +#endif + +// Macro for avoiding default assignment and copy, because doing this by inheritance can increase class size on some +// platforms. +#define PX_NOCOPY(Class) \ + \ +protected: \ + Class(const Class&); \ + Class& operator=(const Class&); + +#ifndef DISABLE_CUDA_PHYSX +//CUDA is currently supported only on windows +#define PX_SUPPORT_GPU_PHYSX ((PX_WINDOWS_FAMILY) || (PX_LINUX && PX_X64)) +#else +#define PX_SUPPORT_GPU_PHYSX 0 +#endif + +#define PX_SUPPORT_COMPUTE_PHYSX 0 + +#ifndef PX_SUPPORT_EXTERN_TEMPLATE +#define PX_SUPPORT_EXTERN_TEMPLATE ((!PX_ANDROID) && (PX_VC != 11)) +#else +#define PX_SUPPORT_EXTERN_TEMPLATE 0 +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXPREPROCESSOR_H diff --git a/src/PhysX/pxshared/include/foundation/PxProfiler.h b/src/PhysX/pxshared/include/foundation/PxProfiler.h new file mode 100644 index 000000000..82d978807 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxProfiler.h @@ -0,0 +1,99 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. + +#ifndef PXFOUNDATION_PXPROFILER_H +#define PXFOUNDATION_PXPROFILER_H + +#include "foundation/Px.h" + +namespace physx +{ + +/** +\brief The pure virtual callback interface for general purpose instrumentation and profiling of GameWorks modules as +well as applications +*/ +class PxProfilerCallback +{ +protected: + virtual ~PxProfilerCallback() {} + +public: + /************************************************************************************************************************** + Instrumented profiling events + ***************************************************************************************************************************/ + + /** + \brief Mark the beginning of a nested profile block + \param[in] eventName Event name. Must be a persistent const char * + \param[in] detached True for cross thread events + \param[in] contextId the context id of this zone. Zones with the same id belong to the same group. 0 is used for no specific group. + \return Returns implementation-specific profiler data for this event + */ + virtual void* zoneStart(const char* eventName, bool detached, uint64_t contextId) = 0; + + /** + \brief Mark the end of a nested profile block + \param[in] profilerData The data returned by the corresponding zoneStart call (or NULL if not available) + \param[in] eventName The name of the zone ending, must match the corresponding name passed with 'zoneStart'. Must be a persistent const char *. + \param[in] detached True for cross thread events. Should match the value passed to zoneStart. + \param[in] contextId The context of this zone. Should match the value passed to zoneStart. + + \note eventName plus contextId can be used to uniquely match up start and end of a zone. + */ + virtual void zoneEnd(void* profilerData, const char* eventName, bool detached, uint64_t contextId) = 0; +}; + +class PxProfileScoped +{ + public: + PX_FORCE_INLINE PxProfileScoped(PxProfilerCallback* callback, const char* eventName, bool detached, uint64_t contextId) : mCallback(callback), mProfilerData(NULL) + { + if(mCallback) + { + mEventName = eventName; + mContextId = contextId; + mDetached = detached; + mProfilerData = mCallback->zoneStart(eventName, detached, contextId); + } + } + + PX_FORCE_INLINE ~PxProfileScoped() + { + if(mCallback) + mCallback->zoneEnd(mProfilerData, mEventName, mDetached, mContextId); + } + PxProfilerCallback* mCallback; + const char* mEventName; + void* mProfilerData; + uint64_t mContextId; + bool mDetached; +}; + +} // end of physx namespace + +#endif // PXFOUNDATION_PXPROFILER_H diff --git a/src/PhysX/pxshared/include/foundation/PxQuat.h b/src/PhysX/pxshared/include/foundation/PxQuat.h new file mode 100644 index 000000000..7ae30e9cb --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxQuat.h @@ -0,0 +1,403 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXQUAT_H +#define PXFOUNDATION_PXQUAT_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxVec3.h" +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief This is a quaternion class. For more information on quaternion mathematics +consult a mathematics source on complex numbers. + +*/ + +class PxQuat +{ + public: + /** + \brief Default constructor, does not do any initialization. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat() + { + } + + //! identity constructor + PX_CUDA_CALLABLE PX_INLINE PxQuat(PxIDENTITY r) : x(0.0f), y(0.0f), z(0.0f), w(1.0f) + { + PX_UNUSED(r); + } + + /** + \brief Constructor from a scalar: sets the real part w to the scalar value, and the imaginary parts (x,y,z) to zero + */ + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat(float r) : x(0.0f), y(0.0f), z(0.0f), w(r) + { + } + + /** + \brief Constructor. Take note of the order of the elements! + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat(float nx, float ny, float nz, float nw) : x(nx), y(ny), z(nz), w(nw) + { + } + + /** + \brief Creates from angle-axis representation. + + Axis must be normalized! + + Angle is in radians! + + Unit: Radians + */ + PX_CUDA_CALLABLE PX_INLINE PxQuat(float angleRadians, const PxVec3& unitAxis) + { + PX_ASSERT(PxAbs(1.0f - unitAxis.magnitude()) < 1e-3f); + const float a = angleRadians * 0.5f; + const float s = PxSin(a); + w = PxCos(a); + x = unitAxis.x * s; + y = unitAxis.y * s; + z = unitAxis.z * s; + } + + /** + \brief Copy ctor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat(const PxQuat& v) : x(v.x), y(v.y), z(v.z), w(v.w) + { + } + + /** + \brief Creates from orientation matrix. + + \param[in] m Rotation matrix to extract quaternion from. + */ + PX_CUDA_CALLABLE PX_INLINE explicit PxQuat(const PxMat33& m); /* defined in PxMat33.h */ + + /** + \brief returns true if quat is identity + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isIdentity() const + { + return x==0.0f && y==0.0f && z==0.0f && w==1.0f; + } + + /** + \brief returns true if all elements are finite (not NAN or INF, etc.) + */ + PX_CUDA_CALLABLE bool isFinite() const + { + return PxIsFinite(x) && PxIsFinite(y) && PxIsFinite(z) && PxIsFinite(w); + } + + /** + \brief returns true if finite and magnitude is close to unit + */ + PX_CUDA_CALLABLE bool isUnit() const + { + const float unitTolerance = 1e-4f; + return isFinite() && PxAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns true if finite and magnitude is reasonably close to unit to allow for some accumulation of error vs + isValid + */ + PX_CUDA_CALLABLE bool isSane() const + { + const float unitTolerance = 1e-2f; + return isFinite() && PxAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns true if the two quaternions are exactly equal + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxQuat& q) const + { + return x == q.x && y == q.y && z == q.z && w == q.w; + } + + /** + \brief converts this quaternion to angle-axis representation + */ + PX_CUDA_CALLABLE PX_INLINE void toRadiansAndUnitAxis(float& angle, PxVec3& axis) const + { + const float quatEpsilon = 1.0e-8f; + const float s2 = x * x + y * y + z * z; + if(s2 < quatEpsilon * quatEpsilon) // can't extract a sensible axis + { + angle = 0.0f; + axis = PxVec3(1.0f, 0.0f, 0.0f); + } + else + { + const float s = PxRecipSqrt(s2); + axis = PxVec3(x, y, z) * s; + angle = PxAbs(w) < quatEpsilon ? PxPi : PxAtan2(s2 * s, w) * 2.0f; + } + } + + /** + \brief Gets the angle between this quat and the identity quaternion. + + Unit: Radians + */ + PX_CUDA_CALLABLE PX_INLINE float getAngle() const + { + return PxAcos(w) * 2.0f; + } + + /** + \brief Gets the angle between this quat and the argument + + Unit: Radians + */ + PX_CUDA_CALLABLE PX_INLINE float getAngle(const PxQuat& q) const + { + return PxAcos(dot(q)) * 2.0f; + } + + /** + \brief This is the squared 4D vector length, should be 1 for unit quaternions. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float magnitudeSquared() const + { + return x * x + y * y + z * z + w * w; + } + + /** + \brief returns the scalar product of this and other. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float dot(const PxQuat& v) const + { + return x * v.x + y * v.y + z * v.z + w * v.w; + } + + PX_CUDA_CALLABLE PX_INLINE PxQuat getNormalized() const + { + const float s = 1.0f / magnitude(); + return PxQuat(x * s, y * s, z * s, w * s); + } + + PX_CUDA_CALLABLE PX_INLINE float magnitude() const + { + return PxSqrt(magnitudeSquared()); + } + + // modifiers: + /** + \brief maps to the closest unit quaternion. + */ + PX_CUDA_CALLABLE PX_INLINE float normalize() // convert this PxQuat to a unit quaternion + { + const float mag = magnitude(); + if(mag != 0.0f) + { + const float imag = 1.0f / mag; + + x *= imag; + y *= imag; + z *= imag; + w *= imag; + } + return mag; + } + + /* + \brief returns the conjugate. + + \note for unit quaternions, this is the inverse. + */ + PX_CUDA_CALLABLE PX_INLINE PxQuat getConjugate() const + { + return PxQuat(-x, -y, -z, w); + } + + /* + \brief returns imaginary part. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec3 getImaginaryPart() const + { + return PxVec3(x, y, z); + } + + /** brief computes rotation of x-axis */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getBasisVector0() const + { + const float x2 = x * 2.0f; + const float w2 = w * 2.0f; + return PxVec3((w * w2) - 1.0f + x * x2, (z * w2) + y * x2, (-y * w2) + z * x2); + } + + /** brief computes rotation of y-axis */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getBasisVector1() const + { + const float y2 = y * 2.0f; + const float w2 = w * 2.0f; + return PxVec3((-z * w2) + x * y2, (w * w2) - 1.0f + y * y2, (x * w2) + z * y2); + } + + /** brief computes rotation of z-axis */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getBasisVector2() const + { + const float z2 = z * 2.0f; + const float w2 = w * 2.0f; + return PxVec3((y * w2) + x * z2, (-x * w2) + y * z2, (w * w2) - 1.0f + z * z2); + } + + /** + rotates passed vec by this (assumed unitary) + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3 rotate(const PxVec3& v) const + { + const float vx = 2.0f * v.x; + const float vy = 2.0f * v.y; + const float vz = 2.0f * v.z; + const float w2 = w * w - 0.5f; + const float dot2 = (x * vx + y * vy + z * vz); + return PxVec3((vx * w2 + (y * vz - z * vy) * w + x * dot2), (vy * w2 + (z * vx - x * vz) * w + y * dot2), + (vz * w2 + (x * vy - y * vx) * w + z * dot2)); + } + + /** + inverse rotates passed vec by this (assumed unitary) + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const PxVec3 rotateInv(const PxVec3& v) const + { + const float vx = 2.0f * v.x; + const float vy = 2.0f * v.y; + const float vz = 2.0f * v.z; + const float w2 = w * w - 0.5f; + const float dot2 = (x * vx + y * vy + z * vz); + return PxVec3((vx * w2 - (y * vz - z * vy) * w + x * dot2), (vy * w2 - (z * vx - x * vz) * w + y * dot2), + (vz * w2 - (x * vy - y * vx) * w + z * dot2)); + } + + /** + \brief Assignment operator + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat& operator=(const PxQuat& p) + { + x = p.x; + y = p.y; + z = p.z; + w = p.w; + return *this; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat& operator*=(const PxQuat& q) + { + const float tx = w * q.x + q.w * x + y * q.z - q.y * z; + const float ty = w * q.y + q.w * y + z * q.x - q.z * x; + const float tz = w * q.z + q.w * z + x * q.y - q.x * y; + + w = w * q.w - q.x * x - y * q.y - q.z * z; + x = tx; + y = ty; + z = tz; + + return *this; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat& operator+=(const PxQuat& q) + { + x += q.x; + y += q.y; + z += q.z; + w += q.w; + return *this; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat& operator-=(const PxQuat& q) + { + x -= q.x; + y -= q.y; + z -= q.z; + w -= q.w; + return *this; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat& operator*=(const float s) + { + x *= s; + y *= s; + z *= s; + w *= s; + return *this; + } + + /** quaternion multiplication */ + PX_CUDA_CALLABLE PX_INLINE PxQuat operator*(const PxQuat& q) const + { + return PxQuat(w * q.x + q.w * x + y * q.z - q.y * z, w * q.y + q.w * y + z * q.x - q.z * x, + w * q.z + q.w * z + x * q.y - q.x * y, w * q.w - x * q.x - y * q.y - z * q.z); + } + + /** quaternion addition */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat operator+(const PxQuat& q) const + { + return PxQuat(x + q.x, y + q.y, z + q.z, w + q.w); + } + + /** quaternion subtraction */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat operator-() const + { + return PxQuat(-x, -y, -z, -w); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat operator-(const PxQuat& q) const + { + return PxQuat(x - q.x, y - q.y, z - q.z, w - q.w); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxQuat operator*(float r) const + { + return PxQuat(x * r, y * r, z * r, w * r); + } + + /** the quaternion elements */ + float x, y, z, w; +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXQUAT_H diff --git a/src/PhysX/pxshared/include/foundation/PxSimpleTypes.h b/src/PhysX/pxshared/include/foundation/PxSimpleTypes.h new file mode 100644 index 000000000..162b788f5 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxSimpleTypes.h @@ -0,0 +1,112 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXSIMPLETYPES_H +#define PXFOUNDATION_PXSIMPLETYPES_H + +/** \addtogroup foundation + @{ +*/ + +// Platform specific types: +// Design note: Its OK to use int for general loop variables and temps. + +#include "foundation/PxPreprocessor.h" +#if PX_VC +#pragma warning(push) +#pragma warning(disable : 4668) // suppressing warning generated by Microsoft Visual Studio when including this standard +// header +#endif + +#if PX_LINUX +#define __STDC_LIMIT_MACROS +#endif + +#include +#if PX_VC +#pragma warning(pop) +#endif + +#if PX_VC // we could use inttypes.h starting with VC12 +#define PX_PRIu64 "I64u" +#else +#if !PX_PS4 && !PX_APPLE_FAMILY +#define __STDC_FORMAT_MACROS +#endif +#include +#define PX_PRIu64 PRIu64 +#endif + +namespace physx +{ +typedef int64_t PxI64; +typedef uint64_t PxU64; +typedef int32_t PxI32; +typedef uint32_t PxU32; +typedef int16_t PxI16; +typedef uint16_t PxU16; +typedef int8_t PxI8; +typedef uint8_t PxU8; +typedef float PxF32; +typedef double PxF64; +typedef float PxReal; +} + +// Type ranges + +// These are here because we sometimes have non-IEEE compliant platforms to deal with. +// Removal is under consideration (issue GWSD-34) + +#define PX_MAX_F32 3.4028234663852885981170418348452e+38F +// maximum possible float value +#define PX_MAX_F64 DBL_MAX // maximum possible double value + +#define PX_EPS_F32 FLT_EPSILON // maximum relative error of float rounding +#define PX_EPS_F64 DBL_EPSILON // maximum relative error of double rounding + +#define PX_MAX_REAL PX_MAX_F32 +#define PX_EPS_REAL PX_EPS_F32 +#define PX_NORMALIZATION_EPSILON float(1e-20f) + +// Legacy type ranges used by PhysX +#define PX_MAX_I8 INT8_MAX +#define PX_MIN_I8 INT8_MIN +#define PX_MAX_U8 UINT8_MAX +#define PX_MIN_U8 UINT8_MIN +#define PX_MAX_I16 INT16_MAX +#define PX_MIN_I16 INT16_MIN +#define PX_MAX_U16 UINT16_MAX +#define PX_MIN_U16 UINT16_MIN +#define PX_MAX_I32 INT32_MAX +#define PX_MIN_I32 INT32_MIN +#define PX_MAX_U32 UINT32_MAX +#define PX_MIN_U32 UINT32_MIN + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXSIMPLETYPES_H diff --git a/src/PhysX/pxshared/include/foundation/PxStrideIterator.h b/src/PhysX/pxshared/include/foundation/PxStrideIterator.h new file mode 100644 index 000000000..786938e3d --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxStrideIterator.h @@ -0,0 +1,353 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXSTRIDEITERATOR_H +#define PXFOUNDATION_PXSTRIDEITERATOR_H + +#include "foundation/Px.h" +#include "foundation/PxAssert.h" + +/** \addtogroup foundation + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief Iterator class for iterating over arrays of data that may be interleaved with other data. + +This class is used for iterating over arrays of elements that may have a larger element to element +offset, called the stride, than the size of the element itself (non-contiguous). + +The template parameter T denotes the type of the element accessed. The stride itself +is stored as a member field so multiple instances of a PxStrideIterator class can have +different strides. This is useful for cases were the stride depends on runtime configuration. + +The stride iterator can be used for index based access, e.g.: +\code + PxStrideIterator strideArray(...); + for (unsigned i = 0; i < 10; ++i) + { + PxVec3& vec = strideArray[i]; + ... + } +\endcode +or iteration by increment, e.g.: +\code + PxStrideIterator strideBegin(...); + PxStrideIterator strideEnd(strideBegin + 10); + for (PxStrideIterator it = strideBegin; it < strideEnd; ++it) + { + PxVec3& vec = *it; + ... + } +\endcode + +Two special cases: +- A stride of sizeof(T) represents a regular c array of type T. +- A stride of 0 can be used to describe re-occurrence of the same element multiple times. + +*/ +template +class PxStrideIterator +{ + +#if !PX_DOXYGEN + template + struct StripConst + { + typedef X Type; + }; + + template + struct StripConst + { + typedef X Type; + }; +#endif + + public: + /** + \brief Constructor. + + Optionally takes a pointer to an element and a stride. + + \param[in] ptr pointer to element, defaults to NULL. + \param[in] stride stride for accessing consecutive elements, defaults to the size of one element. + */ + explicit PX_INLINE PxStrideIterator(T* ptr = NULL, PxU32 stride = sizeof(T)) : mPtr(ptr), mStride(stride) + { + PX_ASSERT(mStride == 0 || sizeof(T) <= mStride); + } + + /** + \brief Copy constructor. + + \param[in] strideIterator PxStrideIterator to be copied. + */ + PX_INLINE PxStrideIterator(const PxStrideIterator::Type>& strideIterator) + : mPtr(strideIterator.ptr()), mStride(strideIterator.stride()) + { + PX_ASSERT(mStride == 0 || sizeof(T) <= mStride); + } + + /** + \brief Get pointer to element. + */ + PX_INLINE T* ptr() const + { + return mPtr; + } + + /** + \brief Get stride. + */ + PX_INLINE PxU32 stride() const + { + return mStride; + } + + /** + \brief Indirection operator. + */ + PX_INLINE T& operator*() const + { + return *mPtr; + } + + /** + \brief Dereferencing operator. + */ + PX_INLINE T* operator->() const + { + return mPtr; + } + + /** + \brief Indexing operator. + */ + PX_INLINE T& operator[](unsigned int i) const + { + return *byteAdd(mPtr, i * stride()); + } + + /** + \brief Pre-increment operator. + */ + PX_INLINE PxStrideIterator& operator++() + { + mPtr = byteAdd(mPtr, stride()); + return *this; + } + + /** + \brief Post-increment operator. + */ + PX_INLINE PxStrideIterator operator++(int) + { + PxStrideIterator tmp = *this; + mPtr = byteAdd(mPtr, stride()); + return tmp; + } + + /** + \brief Pre-decrement operator. + */ + PX_INLINE PxStrideIterator& operator--() + { + mPtr = byteSub(mPtr, stride()); + return *this; + } + + /** + \brief Post-decrement operator. + */ + PX_INLINE PxStrideIterator operator--(int) + { + PxStrideIterator tmp = *this; + mPtr = byteSub(mPtr, stride()); + return tmp; + } + + /** + \brief Addition operator. + */ + PX_INLINE PxStrideIterator operator+(unsigned int i) const + { + return PxStrideIterator(byteAdd(mPtr, i * stride()), stride()); + } + + /** + \brief Subtraction operator. + */ + PX_INLINE PxStrideIterator operator-(unsigned int i) const + { + return PxStrideIterator(byteSub(mPtr, i * stride()), stride()); + } + + /** + \brief Addition compound assignment operator. + */ + PX_INLINE PxStrideIterator& operator+=(unsigned int i) + { + mPtr = byteAdd(mPtr, i * stride()); + return *this; + } + + /** + \brief Subtraction compound assignment operator. + */ + PX_INLINE PxStrideIterator& operator-=(unsigned int i) + { + mPtr = byteSub(mPtr, i * stride()); + return *this; + } + + /** + \brief Iterator difference. + */ + PX_INLINE int operator-(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + int byteDiff = static_cast(reinterpret_cast(mPtr) - reinterpret_cast(other.mPtr)); + return byteDiff / static_cast(stride()); + } + + /** + \brief Equality operator. + */ + PX_INLINE bool operator==(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr == other.mPtr; + } + + /** + \brief Inequality operator. + */ + PX_INLINE bool operator!=(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr != other.mPtr; + } + + /** + \brief Less than operator. + */ + PX_INLINE bool operator<(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr < other.mPtr; + } + + /** + \brief Greater than operator. + */ + PX_INLINE bool operator>(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr > other.mPtr; + } + + /** + \brief Less or equal than operator. + */ + PX_INLINE bool operator<=(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr <= other.mPtr; + } + + /** + \brief Greater or equal than operator. + */ + PX_INLINE bool operator>=(const PxStrideIterator& other) const + { + PX_ASSERT(isCompatible(other)); + return mPtr >= other.mPtr; + } + + private: + PX_INLINE static T* byteAdd(T* ptr, PxU32 bytes) + { + return const_cast(reinterpret_cast(reinterpret_cast(ptr) + bytes)); + } + + PX_INLINE static T* byteSub(T* ptr, PxU32 bytes) + { + return const_cast(reinterpret_cast(reinterpret_cast(ptr) - bytes)); + } + + PX_INLINE bool isCompatible(const PxStrideIterator& other) const + { + int byteDiff = static_cast(reinterpret_cast(mPtr) - reinterpret_cast(other.mPtr)); + return (stride() == other.stride()) && (abs(byteDiff) % stride() == 0); + } + + T* mPtr; + PxU32 mStride; +}; + +/** +\brief Addition operator. +*/ +template +PX_INLINE PxStrideIterator operator+(int i, PxStrideIterator it) +{ + it += i; + return it; +} + +/** +\brief Stride iterator factory function which infers the iterator type. +*/ +template +PX_INLINE PxStrideIterator PxMakeIterator(T* ptr, PxU32 stride = sizeof(T)) +{ + return PxStrideIterator(ptr, stride); +} + +/** +\brief Stride iterator factory function which infers the iterator type. +*/ +template +PX_INLINE PxStrideIterator PxMakeIterator(const T* ptr, PxU32 stride = sizeof(T)) +{ + return PxStrideIterator(ptr, stride); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // PXFOUNDATION_PXSTRIDEITERATOR_H diff --git a/src/PhysX/pxshared/include/foundation/PxTransform.h b/src/PhysX/pxshared/include/foundation/PxTransform.h new file mode 100644 index 000000000..8b29584f3 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxTransform.h @@ -0,0 +1,215 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXTRANSFORM_H +#define PXFOUNDATION_PXTRANSFORM_H +/** \addtogroup foundation + @{ +*/ + +#include "foundation/PxQuat.h" +#include "foundation/PxPlane.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/*! +\brief class representing a rigid euclidean transform as a quaternion and a vector +*/ + +class PxTransform +{ + public: + PxQuat q; + PxVec3 p; + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform() + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE explicit PxTransform(const PxVec3& position) : q(PxIdentity), p(position) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE explicit PxTransform(PxIDENTITY r) : q(PxIdentity), p(PxZero) + { + PX_UNUSED(r); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE explicit PxTransform(const PxQuat& orientation) : q(orientation), p(0) + { + PX_ASSERT(orientation.isSane()); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform(float x, float y, float z, PxQuat aQ = PxQuat(PxIdentity)) + : q(aQ), p(x, y, z) + { + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform(const PxVec3& p0, const PxQuat& q0) : q(q0), p(p0) + { + PX_ASSERT(q0.isSane()); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE explicit PxTransform(const PxMat44& m); // defined in PxMat44.h + + /** + \brief returns true if the two transforms are exactly equal + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxTransform& t) const + { + return p == t.p && q == t.q; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform operator*(const PxTransform& x) const + { + PX_ASSERT(x.isSane()); + return transform(x); + } + + //! Equals matrix multiplication + PX_CUDA_CALLABLE PX_INLINE PxTransform& operator*=(PxTransform& other) + { + *this = *this * other; + return *this; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform getInverse() const + { + PX_ASSERT(isFinite()); + return PxTransform(q.rotateInv(-p), q.getConjugate()); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 transform(const PxVec3& input) const + { + PX_ASSERT(isFinite()); + return q.rotate(input) + p; + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 transformInv(const PxVec3& input) const + { + PX_ASSERT(isFinite()); + return q.rotateInv(input - p); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 rotate(const PxVec3& input) const + { + PX_ASSERT(isFinite()); + return q.rotate(input); + } + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 rotateInv(const PxVec3& input) const + { + PX_ASSERT(isFinite()); + return q.rotateInv(input); + } + + //! Transform transform to parent (returns compound transform: first src, then *this) + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform transform(const PxTransform& src) const + { + PX_ASSERT(src.isSane()); + PX_ASSERT(isSane()); + // src = [srct, srcr] -> [r*srct + t, r*srcr] + return PxTransform(q.rotate(src.p) + p, q * src.q); + } + + /** + \brief returns true if finite and q is a unit quaternion + */ + + PX_CUDA_CALLABLE bool isValid() const + { + return p.isFinite() && q.isFinite() && q.isUnit(); + } + + /** + \brief returns true if finite and quat magnitude is reasonably close to unit to allow for some accumulation of error + vs isValid + */ + + PX_CUDA_CALLABLE bool isSane() const + { + return isFinite() && q.isSane(); + } + + /** + \brief returns true if all elems are finite (not NAN or INF, etc.) + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite() const + { + return p.isFinite() && q.isFinite(); + } + + //! Transform transform from parent (returns compound transform: first src, then this->inverse) + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform transformInv(const PxTransform& src) const + { + PX_ASSERT(src.isSane()); + PX_ASSERT(isFinite()); + // src = [srct, srcr] -> [r^-1*(srct-t), r^-1*srcr] + PxQuat qinv = q.getConjugate(); + return PxTransform(qinv.rotate(src.p - p), qinv * src.q); + } + + /** + \brief transform plane + */ + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane transform(const PxPlane& plane) const + { + PxVec3 transformedNormal = rotate(plane.n); + return PxPlane(transformedNormal, plane.d - p.dot(transformedNormal)); + } + + /** + \brief inverse-transform plane + */ + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxPlane inverseTransform(const PxPlane& plane) const + { + PxVec3 transformedNormal = rotateInv(plane.n); + return PxPlane(transformedNormal, plane.d + p.dot(plane.n)); + } + + /** + \brief return a normalized transform (i.e. one in which the quaternion has unit magnitude) + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform getNormalized() const + { + return PxTransform(p, q.getNormalized()); + } +}; + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXTRANSFORM_H diff --git a/src/PhysX/pxshared/include/foundation/PxUnionCast.h b/src/PhysX/pxshared/include/foundation/PxUnionCast.h new file mode 100644 index 000000000..ec35911e0 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxUnionCast.h @@ -0,0 +1,69 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXUNIONCAST_H +#define PXFOUNDATION_PXUNIONCAST_H + +#include "foundation/Px.h" + +/** \addtogroup foundation +@{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +template +PX_FORCE_INLINE A PxUnionCast(B b) +{ + union AB + { + AB(B bb) : _b(bb) + { + } + B _b; + A _a; +// needed for clang 7 +#if PX_LINUX && PX_CLANG + } volatile u(b); +#else + } u(b); +#endif + return u._a; +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ + +#endif // PXFOUNDATION_PXUNIONCAST_H diff --git a/src/PhysX/pxshared/include/foundation/PxVec2.h b/src/PhysX/pxshared/include/foundation/PxVec2.h new file mode 100644 index 000000000..0736eddb8 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxVec2.h @@ -0,0 +1,347 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXVEC2_H +#define PXFOUNDATION_PXVEC2_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxMath.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief 2 Element vector class. + +This is a 2-dimensional vector class with public data members. +*/ +class PxVec2 +{ + public: + /** + \brief default constructor leaves data uninitialized. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2() + { + } + + /** + \brief zero constructor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2(PxZERO r) : x(0.0f), y(0.0f) + { + PX_UNUSED(r); + } + + /** + \brief Assigns scalar parameter to all elements. + + Useful to initialize to zero or one. + + \param[in] a Value to assign to elements. + */ + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2(float a) : x(a), y(a) + { + } + + /** + \brief Initializes from 2 scalar parameters. + + \param[in] nx Value to initialize X component. + \param[in] ny Value to initialize Y component. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2(float nx, float ny) : x(nx), y(ny) + { + } + + /** + \brief Copy ctor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2(const PxVec2& v) : x(v.x), y(v.y) + { + } + + // Operators + + /** + \brief Assignment operator + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2& operator=(const PxVec2& p) + { + x = p.x; + y = p.y; + return *this; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float& operator[](int index) + { + PX_ASSERT(index >= 0 && index <= 1); + + return reinterpret_cast(this)[index]; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const float& operator[](int index) const + { + PX_ASSERT(index >= 0 && index <= 1); + + return reinterpret_cast(this)[index]; + } + + /** + \brief returns true if the two vectors are exactly equal. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator==(const PxVec2& v) const + { + return x == v.x && y == v.y; + } + + /** + \brief returns true if the two vectors are not exactly equal. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator!=(const PxVec2& v) const + { + return x != v.x || y != v.y; + } + + /** + \brief tests for exact zero vector + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isZero() const + { + return x == 0.0f && y == 0.0f; + } + + /** + \brief returns true if all 2 elems of the vector are finite (not NAN or INF, etc.) + */ + PX_CUDA_CALLABLE PX_INLINE bool isFinite() const + { + return PxIsFinite(x) && PxIsFinite(y); + } + + /** + \brief is normalized - used by API parameter validation + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isNormalized() const + { + const float unitTolerance = 1e-4f; + return isFinite() && PxAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns the squared magnitude + + Avoids calling PxSqrt()! + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float magnitudeSquared() const + { + return x * x + y * y; + } + + /** + \brief returns the magnitude + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float magnitude() const + { + return PxSqrt(magnitudeSquared()); + } + + /** + \brief negation + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 operator-() const + { + return PxVec2(-x, -y); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 operator+(const PxVec2& v) const + { + return PxVec2(x + v.x, y + v.y); + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 operator-(const PxVec2& v) const + { + return PxVec2(x - v.x, y - v.y); + } + + /** + \brief scalar post-multiplication + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 operator*(float f) const + { + return PxVec2(x * f, y * f); + } + + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 operator/(float f) const + { + f = 1.0f / f; // PT: inconsistent notation with operator /= + return PxVec2(x * f, y * f); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2& operator+=(const PxVec2& v) + { + x += v.x; + y += v.y; + return *this; + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2& operator-=(const PxVec2& v) + { + x -= v.x; + y -= v.y; + return *this; + } + + /** + \brief scalar multiplication + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2& operator*=(float f) + { + x *= f; + y *= f; + return *this; + } + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2& operator/=(float f) + { + f = 1.0f / f; // PT: inconsistent notation with operator / + x *= f; + y *= f; + return *this; + } + + /** + \brief returns the scalar product of this and other. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float dot(const PxVec2& v) const + { + return x * v.x + y * v.y; + } + + /** return a unit vector */ + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 getNormalized() const + { + const float m = magnitudeSquared(); + return m > 0.0f ? *this * PxRecipSqrt(m) : PxVec2(0, 0); + } + + /** + \brief normalizes the vector in place + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float normalize() + { + const float m = magnitude(); + if(m > 0.0f) + *this /= m; + return m; + } + + /** + \brief a[i] * b[i], for all i. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 multiply(const PxVec2& a) const + { + return PxVec2(x * a.x, y * a.y); + } + + /** + \brief element-wise minimum + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 minimum(const PxVec2& v) const + { + return PxVec2(PxMin(x, v.x), PxMin(y, v.y)); + } + + /** + \brief returns MIN(x, y); + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float minElement() const + { + return PxMin(x, y); + } + + /** + \brief element-wise maximum + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec2 maximum(const PxVec2& v) const + { + return PxVec2(PxMax(x, v.x), PxMax(y, v.y)); + } + + /** + \brief returns MAX(x, y); + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float maxElement() const + { + return PxMax(x, y); + } + + float x, y; +}; + +PX_CUDA_CALLABLE static PX_FORCE_INLINE PxVec2 operator*(float f, const PxVec2& v) +{ + return PxVec2(f * v.x, f * v.y); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXVEC2_H diff --git a/src/PhysX/pxshared/include/foundation/PxVec3.h b/src/PhysX/pxshared/include/foundation/PxVec3.h new file mode 100644 index 000000000..9ba01a091 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxVec3.h @@ -0,0 +1,394 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXVEC3_H +#define PXFOUNDATION_PXVEC3_H + +/** \addtogroup foundation +@{ +*/ + +#include "foundation/PxMath.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief 3 Element vector class. + +This is a 3-dimensional vector class with public data members. +*/ +class PxVec3 +{ + public: + /** + \brief default constructor leaves data uninitialized. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3() + { + } + + /** + \brief zero constructor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3(PxZERO r) : x(0.0f), y(0.0f), z(0.0f) + { + PX_UNUSED(r); + } + + /** + \brief Assigns scalar parameter to all elements. + + Useful to initialize to zero or one. + + \param[in] a Value to assign to elements. + */ + explicit PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3(float a) : x(a), y(a), z(a) + { + } + + /** + \brief Initializes from 3 scalar parameters. + + \param[in] nx Value to initialize X component. + \param[in] ny Value to initialize Y component. + \param[in] nz Value to initialize Z component. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3(float nx, float ny, float nz) : x(nx), y(ny), z(nz) + { + } + + /** + \brief Copy ctor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3(const PxVec3& v) : x(v.x), y(v.y), z(v.z) + { + } + + // Operators + + /** + \brief Assignment operator + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator=(const PxVec3& p) + { + x = p.x; + y = p.y; + z = p.z; + return *this; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float& operator[](unsigned int index) + { + PX_ASSERT(index <= 2); + + return reinterpret_cast(this)[index]; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE const float& operator[](unsigned int index) const + { + PX_ASSERT(index <= 2); + + return reinterpret_cast(this)[index]; + } + + /** + \brief returns true if the two vectors are exactly equal. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator==(const PxVec3& v) const + { + return x == v.x && y == v.y && z == v.z; + } + + /** + \brief returns true if the two vectors are not exactly equal. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool operator!=(const PxVec3& v) const + { + return x != v.x || y != v.y || z != v.z; + } + + /** + \brief tests for exact zero vector + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isZero() const + { + return x == 0.0f && y == 0.0f && z == 0.0f; + } + + /** + \brief returns true if all 3 elems of the vector are finite (not NAN or INF, etc.) + */ + PX_CUDA_CALLABLE PX_INLINE bool isFinite() const + { + return PxIsFinite(x) && PxIsFinite(y) && PxIsFinite(z); + } + + /** + \brief is normalized - used by API parameter validation + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE bool isNormalized() const + { + const float unitTolerance = 1e-4f; + return isFinite() && PxAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns the squared magnitude + + Avoids calling PxSqrt()! + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float magnitudeSquared() const + { + return x * x + y * y + z * z; + } + + /** + \brief returns the magnitude + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float magnitude() const + { + return PxSqrt(magnitudeSquared()); + } + + /** + \brief negation + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 operator-() const + { + return PxVec3(-x, -y, -z); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 operator+(const PxVec3& v) const + { + return PxVec3(x + v.x, y + v.y, z + v.z); + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 operator-(const PxVec3& v) const + { + return PxVec3(x - v.x, y - v.y, z - v.z); + } + + /** + \brief scalar post-multiplication + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 operator*(float f) const + { + return PxVec3(x * f, y * f, z * f); + } + + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 operator/(float f) const + { + f = 1.0f / f; + return PxVec3(x * f, y * f, z * f); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator+=(const PxVec3& v) + { + x += v.x; + y += v.y; + z += v.z; + return *this; + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator-=(const PxVec3& v) + { + x -= v.x; + y -= v.y; + z -= v.z; + return *this; + } + + /** + \brief scalar multiplication + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator*=(float f) + { + x *= f; + y *= f; + z *= f; + return *this; + } + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3& operator/=(float f) + { + f = 1.0f / f; + x *= f; + y *= f; + z *= f; + return *this; + } + + /** + \brief returns the scalar product of this and other. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float dot(const PxVec3& v) const + { + return x * v.x + y * v.y + z * v.z; + } + + /** + \brief cross product + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 cross(const PxVec3& v) const + { + return PxVec3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x); + } + + /** return a unit vector */ + + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 getNormalized() const + { + const float m = magnitudeSquared(); + return m > 0.0f ? *this * PxRecipSqrt(m) : PxVec3(0, 0, 0); + } + + /** + \brief normalizes the vector in place + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float normalize() + { + const float m = magnitude(); + if(m > 0.0f) + *this /= m; + return m; + } + + /** + \brief normalizes the vector in place. Does nothing if vector magnitude is under PX_NORMALIZATION_EPSILON. + Returns vector magnitude if >= PX_NORMALIZATION_EPSILON and 0.0f otherwise. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float normalizeSafe() + { + const float mag = magnitude(); + if(mag < PX_NORMALIZATION_EPSILON) + return 0.0f; + *this *= 1.0f / mag; + return mag; + } + + /** + \brief normalizes the vector in place. Asserts if vector magnitude is under PX_NORMALIZATION_EPSILON. + returns vector magnitude. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float normalizeFast() + { + const float mag = magnitude(); + PX_ASSERT(mag >= PX_NORMALIZATION_EPSILON); + *this *= 1.0f / mag; + return mag; + } + + /** + \brief a[i] * b[i], for all i. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 multiply(const PxVec3& a) const + { + return PxVec3(x * a.x, y * a.y, z * a.z); + } + + /** + \brief element-wise minimum + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 minimum(const PxVec3& v) const + { + return PxVec3(PxMin(x, v.x), PxMin(y, v.y), PxMin(z, v.z)); + } + + /** + \brief returns MIN(x, y, z); + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float minElement() const + { + return PxMin(x, PxMin(y, z)); + } + + /** + \brief element-wise maximum + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 maximum(const PxVec3& v) const + { + return PxVec3(PxMax(x, v.x), PxMax(y, v.y), PxMax(z, v.z)); + } + + /** + \brief returns MAX(x, y, z); + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE float maxElement() const + { + return PxMax(x, PxMax(y, z)); + } + + /** + \brief returns absolute values of components; + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 abs() const + { + return PxVec3(PxAbs(x), PxAbs(y), PxAbs(z)); + } + + float x, y, z; +}; + +PX_CUDA_CALLABLE static PX_FORCE_INLINE PxVec3 operator*(float f, const PxVec3& v) +{ + return PxVec3(f * v.x, f * v.y, f * v.z); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXVEC3_H diff --git a/src/PhysX/pxshared/include/foundation/PxVec4.h b/src/PhysX/pxshared/include/foundation/PxVec4.h new file mode 100644 index 000000000..dd7ebdfa6 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxVec4.h @@ -0,0 +1,376 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXVEC4_H +#define PXFOUNDATION_PXVEC4_H +/** \addtogroup foundation +@{ +*/ +#include "foundation/PxMath.h" +#include "foundation/PxVec3.h" +#include "foundation/PxAssert.h" + +/** +\brief 4 Element vector class. + +This is a 4-dimensional vector class with public data members. +*/ +#if !PX_DOXYGEN +namespace physx +{ +#endif + +class PxVec4 +{ + public: + /** + \brief default constructor leaves data uninitialized. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4() + { + } + + /** + \brief zero constructor. + */ + PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec4(PxZERO r) : x(0.0f), y(0.0f), z(0.0f), w(0.0f) + { + PX_UNUSED(r); + } + + /** + \brief Assigns scalar parameter to all elements. + + Useful to initialize to zero or one. + + \param[in] a Value to assign to elements. + */ + explicit PX_CUDA_CALLABLE PX_INLINE PxVec4(float a) : x(a), y(a), z(a), w(a) + { + } + + /** + \brief Initializes from 3 scalar parameters. + + \param[in] nx Value to initialize X component. + \param[in] ny Value to initialize Y component. + \param[in] nz Value to initialize Z component. + \param[in] nw Value to initialize W component. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4(float nx, float ny, float nz, float nw) : x(nx), y(ny), z(nz), w(nw) + { + } + + /** + \brief Initializes from 3 scalar parameters. + + \param[in] v Value to initialize the X, Y, and Z components. + \param[in] nw Value to initialize W component. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4(const PxVec3& v, float nw) : x(v.x), y(v.y), z(v.z), w(nw) + { + } + + /** + \brief Initializes from an array of scalar parameters. + + \param[in] v Value to initialize with. + */ + explicit PX_CUDA_CALLABLE PX_INLINE PxVec4(const float v[]) : x(v[0]), y(v[1]), z(v[2]), w(v[3]) + { + } + + /** + \brief Copy ctor. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4(const PxVec4& v) : x(v.x), y(v.y), z(v.z), w(v.w) + { + } + + // Operators + + /** + \brief Assignment operator + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4& operator=(const PxVec4& p) + { + x = p.x; + y = p.y; + z = p.z; + w = p.w; + return *this; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_INLINE float& operator[](unsigned int index) + { + PX_ASSERT(index <= 3); + + return reinterpret_cast(this)[index]; + } + + /** + \brief element access + */ + PX_CUDA_CALLABLE PX_INLINE const float& operator[](unsigned int index) const + { + PX_ASSERT(index <= 3); + + return reinterpret_cast(this)[index]; + } + + /** + \brief returns true if the two vectors are exactly equal. + */ + PX_CUDA_CALLABLE PX_INLINE bool operator==(const PxVec4& v) const + { + return x == v.x && y == v.y && z == v.z && w == v.w; + } + + /** + \brief returns true if the two vectors are not exactly equal. + */ + PX_CUDA_CALLABLE PX_INLINE bool operator!=(const PxVec4& v) const + { + return x != v.x || y != v.y || z != v.z || w != v.w; + } + + /** + \brief tests for exact zero vector + */ + PX_CUDA_CALLABLE PX_INLINE bool isZero() const + { + return x == 0 && y == 0 && z == 0 && w == 0; + } + + /** + \brief returns true if all 3 elems of the vector are finite (not NAN or INF, etc.) + */ + PX_CUDA_CALLABLE PX_INLINE bool isFinite() const + { + return PxIsFinite(x) && PxIsFinite(y) && PxIsFinite(z) && PxIsFinite(w); + } + + /** + \brief is normalized - used by API parameter validation + */ + PX_CUDA_CALLABLE PX_INLINE bool isNormalized() const + { + const float unitTolerance = 1e-4f; + return isFinite() && PxAbs(magnitude() - 1) < unitTolerance; + } + + /** + \brief returns the squared magnitude + + Avoids calling PxSqrt()! + */ + PX_CUDA_CALLABLE PX_INLINE float magnitudeSquared() const + { + return x * x + y * y + z * z + w * w; + } + + /** + \brief returns the magnitude + */ + PX_CUDA_CALLABLE PX_INLINE float magnitude() const + { + return PxSqrt(magnitudeSquared()); + } + + /** + \brief negation + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 operator-() const + { + return PxVec4(-x, -y, -z, -w); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 operator+(const PxVec4& v) const + { + return PxVec4(x + v.x, y + v.y, z + v.z, w + v.w); + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 operator-(const PxVec4& v) const + { + return PxVec4(x - v.x, y - v.y, z - v.z, w - v.w); + } + + /** + \brief scalar post-multiplication + */ + + PX_CUDA_CALLABLE PX_INLINE PxVec4 operator*(float f) const + { + return PxVec4(x * f, y * f, z * f, w * f); + } + + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 operator/(float f) const + { + f = 1.0f / f; + return PxVec4(x * f, y * f, z * f, w * f); + } + + /** + \brief vector addition + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4& operator+=(const PxVec4& v) + { + x += v.x; + y += v.y; + z += v.z; + w += v.w; + return *this; + } + + /** + \brief vector difference + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4& operator-=(const PxVec4& v) + { + x -= v.x; + y -= v.y; + z -= v.z; + w -= v.w; + return *this; + } + + /** + \brief scalar multiplication + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4& operator*=(float f) + { + x *= f; + y *= f; + z *= f; + w *= f; + return *this; + } + /** + \brief scalar division + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4& operator/=(float f) + { + f = 1.0f / f; + x *= f; + y *= f; + z *= f; + w *= f; + return *this; + } + + /** + \brief returns the scalar product of this and other. + */ + PX_CUDA_CALLABLE PX_INLINE float dot(const PxVec4& v) const + { + return x * v.x + y * v.y + z * v.z + w * v.w; + } + + /** return a unit vector */ + + PX_CUDA_CALLABLE PX_INLINE PxVec4 getNormalized() const + { + float m = magnitudeSquared(); + return m > 0.0f ? *this * PxRecipSqrt(m) : PxVec4(0, 0, 0, 0); + } + + /** + \brief normalizes the vector in place + */ + PX_CUDA_CALLABLE PX_INLINE float normalize() + { + float m = magnitude(); + if(m > 0.0f) + *this /= m; + return m; + } + + /** + \brief a[i] * b[i], for all i. + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 multiply(const PxVec4& a) const + { + return PxVec4(x * a.x, y * a.y, z * a.z, w * a.w); + } + + /** + \brief element-wise minimum + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 minimum(const PxVec4& v) const + { + return PxVec4(PxMin(x, v.x), PxMin(y, v.y), PxMin(z, v.z), PxMin(w, v.w)); + } + + /** + \brief element-wise maximum + */ + PX_CUDA_CALLABLE PX_INLINE PxVec4 maximum(const PxVec4& v) const + { + return PxVec4(PxMax(x, v.x), PxMax(y, v.y), PxMax(z, v.z), PxMax(w, v.w)); + } + + PX_CUDA_CALLABLE PX_INLINE PxVec3 getXYZ() const + { + return PxVec3(x, y, z); + } + + /** + \brief set vector elements to zero + */ + PX_CUDA_CALLABLE PX_INLINE void setZero() + { + x = y = z = w = 0.0f; + } + + float x, y, z, w; +}; + +PX_CUDA_CALLABLE static PX_INLINE PxVec4 operator*(float f, const PxVec4& v) +{ + return PxVec4(f * v.x, f * v.y, f * v.z, f * v.w); +} + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXVEC4_H diff --git a/src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h b/src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h new file mode 100644 index 000000000..64c1367b7 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h @@ -0,0 +1,185 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXUNIXINTRINSICS_H +#define PXFOUNDATION_PXUNIXINTRINSICS_H + +#include "foundation/Px.h" +#include "foundation/PxAssert.h" + +#if !(PX_LINUX || PX_ANDROID || PX_PS4 || PX_APPLE_FAMILY) +#error "This file should only be included by Unix builds!!" +#endif + +#if (PX_LINUX || PX_ANDROID) && !defined(__CUDACC__) && !PX_EMSCRIPTEN + // Linux/android and CUDA compilation does not work with std::isfnite, as it is not marked as CUDA callable + #include + #ifndef isfinite + using std::isfinite; + #endif +#endif + +#include +#include + +namespace physx +{ +namespace intrinsics +{ +//! \brief platform-specific absolute value +PX_CUDA_CALLABLE PX_FORCE_INLINE float abs(float a) +{ + return ::fabsf(a); +} + +//! \brief platform-specific select float +PX_CUDA_CALLABLE PX_FORCE_INLINE float fsel(float a, float b, float c) +{ + return (a >= 0.0f) ? b : c; +} + +//! \brief platform-specific sign +PX_CUDA_CALLABLE PX_FORCE_INLINE float sign(float a) +{ + return (a >= 0.0f) ? 1.0f : -1.0f; +} + +//! \brief platform-specific reciprocal +PX_CUDA_CALLABLE PX_FORCE_INLINE float recip(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific reciprocal estimate +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipFast(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float sqrt(float a) +{ + return ::sqrtf(a); +} + +//! \brief platform-specific reciprocal square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrt(float a) +{ + return 1.0f / ::sqrtf(a); +} + +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrtFast(float a) +{ + return 1.0f / ::sqrtf(a); +} + +//! \brief platform-specific sine +PX_CUDA_CALLABLE PX_FORCE_INLINE float sin(float a) +{ + return ::sinf(a); +} + +//! \brief platform-specific cosine +PX_CUDA_CALLABLE PX_FORCE_INLINE float cos(float a) +{ + return ::cosf(a); +} + +//! \brief platform-specific minimum +PX_CUDA_CALLABLE PX_FORCE_INLINE float selectMin(float a, float b) +{ + return a < b ? a : b; +} + +//! \brief platform-specific maximum +PX_CUDA_CALLABLE PX_FORCE_INLINE float selectMax(float a, float b) +{ + return a > b ? a : b; +} + +//! \brief platform-specific finiteness check (not INF or NAN) +PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite(float a) +{ + //std::isfinite not recommended as of Feb 2017, since it doesn't work with g++/clang's floating point optimization. + union localU { PxU32 i; float f; } floatUnion; + floatUnion.f = a; + return !((floatUnion.i & 0x7fffffff) >= 0x7f800000); +} + +//! \brief platform-specific finiteness check (not INF or NAN) +PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite(double a) +{ + return !!isfinite(a); +} + +/*! +Sets \c count bytes starting at \c dst to zero. +*/ +PX_FORCE_INLINE void* memZero(void* dest, uint32_t count) +{ + return memset(dest, 0, count); +} + +/*! +Sets \c count bytes starting at \c dst to \c c. +*/ +PX_FORCE_INLINE void* memSet(void* dest, int32_t c, uint32_t count) +{ + return memset(dest, c, count); +} + +/*! +Copies \c count bytes from \c src to \c dst. User memMove if regions overlap. +*/ +PX_FORCE_INLINE void* memCopy(void* dest, const void* src, uint32_t count) +{ + return memcpy(dest, src, count); +} + +/*! +Copies \c count bytes from \c src to \c dst. Supports overlapping regions. +*/ +PX_FORCE_INLINE void* memMove(void* dest, const void* src, uint32_t count) +{ + return memmove(dest, src, count); +} + +/*! +Set 128B to zero starting at \c dst+offset. Must be aligned. +*/ +PX_FORCE_INLINE void memZero128(void* dest, uint32_t offset = 0) +{ + PX_ASSERT(((size_t(dest) + offset) & 0x7f) == 0); + memSet(reinterpret_cast(dest) + offset, 0, 128); +} + +} // namespace intrinsics +} // namespace physx + +#endif // #ifndef PXFOUNDATION_PXUNIXINTRINSICS_H diff --git a/src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h b/src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h new file mode 100644 index 000000000..3705b26e9 --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h @@ -0,0 +1,188 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXWINDOWSINTRINSICS_H +#define PXFOUNDATION_PXWINDOWSINTRINSICS_H + +#include "foundation/Px.h" +#include "foundation/PxAssert.h" + +#if !PX_WINDOWS_FAMILY +#error "This file should only be included by Windows builds!!" +#endif + +#include +#include + +#if !PX_DOXYGEN +namespace physx +{ +namespace intrinsics +{ +#endif + +//! \brief platform-specific absolute value +PX_CUDA_CALLABLE PX_FORCE_INLINE float abs(float a) +{ + return ::fabsf(a); +} + +//! \brief platform-specific select float +PX_CUDA_CALLABLE PX_FORCE_INLINE float fsel(float a, float b, float c) +{ + return (a >= 0.0f) ? b : c; +} + +//! \brief platform-specific sign +PX_CUDA_CALLABLE PX_FORCE_INLINE float sign(float a) +{ + return (a >= 0.0f) ? 1.0f : -1.0f; +} + +//! \brief platform-specific reciprocal +PX_CUDA_CALLABLE PX_FORCE_INLINE float recip(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific reciprocal estimate +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipFast(float a) +{ + return 1.0f / a; +} + +//! \brief platform-specific square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float sqrt(float a) +{ + return ::sqrtf(a); +} + +//! \brief platform-specific reciprocal square root +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrt(float a) +{ + return 1.0f / ::sqrtf(a); +} + +//! \brief platform-specific reciprocal square root estimate +PX_CUDA_CALLABLE PX_FORCE_INLINE float recipSqrtFast(float a) +{ + return 1.0f / ::sqrtf(a); +} + +//! \brief platform-specific sine +PX_CUDA_CALLABLE PX_FORCE_INLINE float sin(float a) +{ + return ::sinf(a); +} + +//! \brief platform-specific cosine +PX_CUDA_CALLABLE PX_FORCE_INLINE float cos(float a) +{ + return ::cosf(a); +} + +//! \brief platform-specific minimum +PX_CUDA_CALLABLE PX_FORCE_INLINE float selectMin(float a, float b) +{ + return a < b ? a : b; +} + +//! \brief platform-specific maximum +PX_CUDA_CALLABLE PX_FORCE_INLINE float selectMax(float a, float b) +{ + return a > b ? a : b; +} + +//! \brief platform-specific finiteness check (not INF or NAN) +PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite(float a) +{ +#ifdef __CUDACC__ + return !!isfinite(a); +#else + return (0 == ((_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF) & _fpclass(a))); +#endif +} + +//! \brief platform-specific finiteness check (not INF or NAN) +PX_CUDA_CALLABLE PX_FORCE_INLINE bool isFinite(double a) +{ +#ifdef __CUDACC__ + return !!isfinite(a); +#else + return (0 == ((_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF) & _fpclass(a))); +#endif +} + +/*! +Sets \c count bytes starting at \c dst to zero. +*/ +PX_FORCE_INLINE void* memZero(void* dest, uint32_t count) +{ + return memset(dest, 0, count); +} + +/*! +Sets \c count bytes starting at \c dst to \c c. +*/ +PX_FORCE_INLINE void* memSet(void* dest, int32_t c, uint32_t count) +{ + return memset(dest, c, count); +} + +/*! +Copies \c count bytes from \c src to \c dst. User memMove if regions overlap. +*/ +PX_FORCE_INLINE void* memCopy(void* dest, const void* src, uint32_t count) +{ + return memcpy(dest, src, count); +} + +/*! +Copies \c count bytes from \c src to \c dst. Supports overlapping regions. +*/ +PX_FORCE_INLINE void* memMove(void* dest, const void* src, uint32_t count) +{ + return memmove(dest, src, count); +} + +/*! +Set 128B to zero starting at \c dst+offset. Must be aligned. +*/ +PX_FORCE_INLINE void memZero128(void* dest, uint32_t offset = 0) +{ + PX_ASSERT(((size_t(dest) + offset) & 0x7f) == 0); + memSet(reinterpret_cast(dest) + offset, 0, 128); +} + +#if !PX_DOXYGEN +} // namespace intrinsics +} // namespace physx +#endif + +#endif // #ifndef PXFOUNDATION_PXWINDOWSINTRINSICS_H diff --git a/src/PhysXCharacterAll.cpp b/src/PhysXCharacterAll.cpp new file mode 100644 index 000000000..b4bd479f5 --- /dev/null +++ b/src/PhysXCharacterAll.cpp @@ -0,0 +1,10 @@ +#include "PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp" +#include "PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp" diff --git a/src/PhysXCommonAll.cpp b/src/PhysXCommonAll.cpp new file mode 100644 index 000000000..90aad96c8 --- /dev/null +++ b/src/PhysXCommonAll.cpp @@ -0,0 +1,8 @@ +#include "PhysX/physx/source/common/src/CmBoxPruning.cpp" +#include "PhysX/physx/source/common/src/CmCollection.cpp" +#include "PhysX/physx/source/common/src/CmMathUtils.cpp" +#include "PhysX/physx/source/common/src/CmPtrTable.cpp" +#include "PhysX/physx/source/common/src/CmRadixSort.cpp" +#include "PhysX/physx/source/common/src/CmRadixSortBuffered.cpp" +#include "PhysX/physx/source/common/src/CmRenderOutput.cpp" +#include "PhysX/physx/source/common/src/CmVisualization.cpp" diff --git a/src/PhysXCookingAll.cpp b/src/PhysXCookingAll.cpp new file mode 100644 index 000000000..67004a9b9 --- /dev/null +++ b/src/PhysXCookingAll.cpp @@ -0,0 +1,18 @@ +#include "PhysX/physx/source/physxcooking/src/Adjacencies.cpp" +#include "PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp" +#include "PhysX/physx/source/physxcooking/src/Cooking.cpp" +#include "PhysX/physx/source/physxcooking/src/CookingUtils.cpp" +#include "PhysX/physx/source/physxcooking/src/EdgeList.cpp" +#include "PhysX/physx/source/physxcooking/src/MeshCleaner.cpp" +#include "PhysX/physx/source/physxcooking/src/Quantizer.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.cpp" +#include "PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp" +#include "PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp" +#include "PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp" +#include "PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp" +#include "PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp" + diff --git a/src/PhysXExtensionAll.cpp b/src/PhysXExtensionAll.cpp new file mode 100644 index 000000000..5a9d566e1 --- /dev/null +++ b/src/PhysXExtensionAll.cpp @@ -0,0 +1,34 @@ +#include "PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtCollection.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp" + +#include "PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtExtensions.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtMetaData.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtPvd.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtSphericalJoint.cpp" +#include "PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp" +#include "PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp" diff --git a/src/PhysXFoundationAll.cpp b/src/PhysXFoundationAll.cpp new file mode 100644 index 000000000..3dd9cc720 --- /dev/null +++ b/src/PhysXFoundationAll.cpp @@ -0,0 +1,7 @@ +#include "PhysX/physx/source/foundation/src/PsAllocator.cpp" +#include "PhysX/physx/source/foundation/src/PsAssert.cpp" +#include "PhysX/physx/source/foundation/src/PsFoundation.cpp" +#include "PhysX/physx/source/foundation/src/PsMathUtils.cpp" +#include "PhysX/physx/source/foundation/src/PsString.cpp" +#include "PhysX/physx/source/foundation/src/PsTempAllocator.cpp" +#include "PhysX/physx/source/foundation/src/PsUtilities.cpp" diff --git a/src/PhysXFoundationUnix.cpp b/src/PhysXFoundationUnix.cpp new file mode 100644 index 000000000..bb70a242a --- /dev/null +++ b/src/PhysXFoundationUnix.cpp @@ -0,0 +1,10 @@ +#include "PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp" +#include "PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp" diff --git a/src/PhysXFoundationWindows.cpp b/src/PhysXFoundationWindows.cpp new file mode 100644 index 000000000..7e600f8f4 --- /dev/null +++ b/src/PhysXFoundationWindows.cpp @@ -0,0 +1,11 @@ +#include "PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp" +#include "PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp" + diff --git a/src/PhysXGeomUtilsAll.cpp b/src/PhysXGeomUtilsAll.cpp new file mode 100644 index 000000000..264065052 --- /dev/null +++ b/src/PhysXGeomUtilsAll.cpp @@ -0,0 +1,68 @@ +#include "PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp" +#include "PhysX/physx/source/geomutils/src/GuBounds.cpp" +#include "PhysX/physx/source/geomutils/src/GuBox.cpp" +#include "PhysX/physx/source/geomutils/src/GuBVHStructure.cpp" +#include "PhysX/physx/source/geomutils/src/GuCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp" +#include "PhysX/physx/source/geomutils/src/GuGeometryQuery.cpp" +#include "PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp" +#include "PhysX/physx/source/geomutils/src/GuInternal.cpp" +#include "PhysX/physx/source/geomutils/src/GuMeshFactory.cpp" +#include "PhysX/physx/source/geomutils/src/GuMetaData.cpp" +#include "PhysX/physx/source/geomutils/src/GuMTD.cpp" +#include "PhysX/physx/source/geomutils/src/GuOverlapTests.cpp" +#include "PhysX/physx/source/geomutils/src/GuRaycastTests.cpp" +#include "PhysX/physx/source/geomutils/src/GuSerialize.cpp" +#include "PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.cpp" +#include "PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp" +#include "PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp" +#include "PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp" +#include "PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp" +#include "PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp" +#include "PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp" +#include "PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp" +#include "PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp" +#include "PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp" +#include "PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp" +#include "PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp" +#include "PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp" +#include "PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp" +#include "PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp" +#include "PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp" + +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp" +#include "PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp" + diff --git a/src/PhysXLowLevelAll.cpp b/src/PhysXLowLevelAll.cpp new file mode 100644 index 000000000..8890717de --- /dev/null +++ b/src/PhysXLowLevelAll.cpp @@ -0,0 +1,48 @@ +#include "PhysX/physx/source/lowlevel/api/src/px_globals.cpp" +#include "PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactMethodImpl.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialHeightField.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMesh.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp" +#include "PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsCCD.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsContext.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp" +#include "PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyConstraintSetupBlock.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverControl.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp" +#include "PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp" \ No newline at end of file diff --git a/src/PhysXNpSrcAll.cpp b/src/PhysXNpSrcAll.cpp new file mode 100644 index 000000000..c73817ce4 --- /dev/null +++ b/src/PhysXNpSrcAll.cpp @@ -0,0 +1,34 @@ +#include "PhysX/physx/source/physx/src/NpActor.cpp" +#include "PhysX/physx/source/physx/src/NpAggregate.cpp" +#include "PhysX/physx/source/physx/src/NpArticulation.cpp" +#include "PhysX/physx/source/physx/src/NpArticulationJoint.cpp" +#include "PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp" +#include "PhysX/physx/source/physx/src/NpArticulationLink.cpp" +#include "PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp" +#include "PhysX/physx/source/physx/src/NpBatchQuery.cpp" +#include "PhysX/physx/source/physx/src/NpConstraint.cpp" +#include "PhysX/physx/source/physx/src/NpFactory.cpp" +#include "PhysX/physx/source/physx/src/NpMaterial.cpp" +#include "PhysX/physx/source/physx/src/NpMetaData.cpp" +#include "PhysX/physx/source/physx/src/NpPhysics.cpp" +#include "PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp" +#include "PhysX/physx/source/physx/src/NpReadCheck.cpp" +#include "PhysX/physx/source/physx/src/NpRigidDynamic.cpp" +#include "PhysX/physx/source/physx/src/NpRigidStatic.cpp" +#include "PhysX/physx/source/physx/src/NpScene.cpp" +#include "PhysX/physx/source/physx/src/NpSceneQueries.cpp" +#include "PhysX/physx/source/physx/src/NpSerializerAdapter.cpp" +#include "PhysX/physx/source/physx/src/NpShape.cpp" +#include "PhysX/physx/source/physx/src/NpShapeManager.cpp" +#include "PhysX/physx/source/physx/src/NpWriteCheck.cpp" +#include "PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp" +#include "PhysX/physx/source/physx/src/PvdPhysicsClient.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbActor.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbBase.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbScene.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.cpp" +#include "PhysX/physx/source/physx/src/buffering/ScbShape.cpp" +#include "PhysX/physx/source/physx/src/gpu/PxGpu.cpp" +#include "PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp" \ No newline at end of file diff --git a/src/PhysXPvdAll.cpp b/src/PhysXPvdAll.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/PhysXSceneQueryAll.cpp b/src/PhysXSceneQueryAll.cpp new file mode 100644 index 000000000..49e4e580e --- /dev/null +++ b/src/PhysXSceneQueryAll.cpp @@ -0,0 +1,15 @@ +#include "PhysX/physx/source/scenequery/src/SqAABBPruner.cpp" +#include "PhysX/physx/source/scenequery/src/SqAABBTree.cpp" +#include "PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp" +#include "PhysX/physx/source/scenequery/src/SqBounds.cpp" +#include "PhysX/physx/source/scenequery/src/SqBucketPruner.cpp" +#include "PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp" +#include "PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp" +#include "PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp" +#include "PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp" +#include "PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp" +#include "PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp" +#include "PhysX/physx/source/scenequery/src/SqMetaData.cpp" +#include "PhysX/physx/source/scenequery/src/SqPruningPool.cpp" +#include "PhysX/physx/source/scenequery/src/SqPruningStructure.cpp" +#include "PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp" diff --git a/src/PhysXSimulationControllerAll.cpp b/src/PhysXSimulationControllerAll.cpp new file mode 100644 index 000000000..12c5f67e0 --- /dev/null +++ b/src/PhysXSimulationControllerAll.cpp @@ -0,0 +1,31 @@ +#include "PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScIterators.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScScene.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp" +#include "PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp" \ No newline at end of file diff --git a/src/PhysXVehicleAll.cpp b/src/PhysXVehicleAll.cpp new file mode 100644 index 000000000..53b89d89b --- /dev/null +++ b/src/PhysXVehicleAll.cpp @@ -0,0 +1,18 @@ +#include "PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleDrive4W.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleUpdate.cpp" +#include "PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp" +#include "PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp" +#include "PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp" +#include "PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp" +#include "PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp" +#include "PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp" From 80ef56cfcaa78f9bd0ed1d9c8a249df8cecf72ed Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Wed, 13 Feb 2019 15:11:34 -0800 Subject: [PATCH 04/10] add missing header --- examples/Importers/ImportSTLDemo/LoadMeshFromSTL.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Importers/ImportSTLDemo/LoadMeshFromSTL.h b/examples/Importers/ImportSTLDemo/LoadMeshFromSTL.h index 53331e147..6e8dbed9e 100644 --- a/examples/Importers/ImportSTLDemo/LoadMeshFromSTL.h +++ b/examples/Importers/ImportSTLDemo/LoadMeshFromSTL.h @@ -5,7 +5,7 @@ #include //fopen #include "Bullet3Common/b3AlignedObjectArray.h" #include "../../CommonInterfaces/CommonFileIOInterface.h" - +#include //memcpy struct MySTLTriangle { float normal[3]; From 6d224996ef5d4791cde48b96cc785b521e420528 Mon Sep 17 00:00:00 2001 From: erwincoumans Date: Wed, 20 Feb 2019 19:43:23 -0800 Subject: [PATCH 05/10] update to latest PhysX version. fix handling commands that are not implemented yet --- build_visual_studio_vr_pybullet_double.bat | 2 +- .../physx/PhysXServerCommandProcessor.cpp | 20 +- src/PhysX/physx/include/PxActor.h | 2 +- src/PhysX/physx/include/PxAggregate.h | 2 +- src/PhysX/physx/include/PxArticulation.h | 2 +- src/PhysX/physx/include/PxArticulationBase.h | 35 +- src/PhysX/physx/include/PxArticulationJoint.h | 42 +- .../PxArticulationJointReducedCoordinate.h | 2 +- src/PhysX/physx/include/PxArticulationLink.h | 27 +- .../include/PxArticulationReducedCoordinate.h | 86 +- src/PhysX/physx/include/PxBatchQuery.h | 2 +- src/PhysX/physx/include/PxBatchQueryDesc.h | 2 +- src/PhysX/physx/include/PxBroadPhase.h | 2 +- src/PhysX/physx/include/PxClient.h | 2 +- src/PhysX/physx/include/PxConstraint.h | 2 +- src/PhysX/physx/include/PxConstraintDesc.h | 2 +- src/PhysX/physx/include/PxContact.h | 2 +- .../physx/include/PxContactModifyCallback.h | 2 +- src/PhysX/physx/include/PxDeletionListener.h | 2 +- src/PhysX/physx/include/PxFiltering.h | 2 +- src/PhysX/physx/include/PxForceMode.h | 2 +- src/PhysX/physx/include/PxFoundation.h | 3 +- src/PhysX/physx/include/PxImmediateMode.h | 4 +- src/PhysX/physx/include/PxLockedData.h | 2 +- src/PhysX/physx/include/PxMaterial.h | 17 +- src/PhysX/physx/include/PxPhysXConfig.h | 2 +- src/PhysX/physx/include/PxPhysics.h | 2 +- src/PhysX/physx/include/PxPhysicsAPI.h | 3 +- .../physx/include/PxPhysicsSerialization.h | 2 +- src/PhysX/physx/include/PxPhysicsVersion.h | 2 +- src/PhysX/physx/include/PxPruningStructure.h | 2 +- src/PhysX/physx/include/PxQueryFiltering.h | 2 +- src/PhysX/physx/include/PxQueryReport.h | 2 +- src/PhysX/physx/include/PxRigidActor.h | 2 +- src/PhysX/physx/include/PxRigidBody.h | 2 +- src/PhysX/physx/include/PxRigidDynamic.h | 2 +- src/PhysX/physx/include/PxRigidStatic.h | 2 +- src/PhysX/physx/include/PxScene.h | 2 +- src/PhysX/physx/include/PxSceneDesc.h | 24 +- src/PhysX/physx/include/PxSceneLock.h | 2 +- src/PhysX/physx/include/PxShape.h | 2 +- .../physx/include/PxSimulationEventCallback.h | 2 +- .../physx/include/PxSimulationStatistics.h | 2 +- .../physx/include/PxVisualizationParameter.h | 2 +- .../characterkinematic/PxBoxController.h | 3 +- .../characterkinematic/PxCapsuleController.h | 3 +- .../include/characterkinematic/PxController.h | 3 +- .../characterkinematic/PxControllerBehavior.h | 3 +- .../characterkinematic/PxControllerManager.h | 8 +- .../PxControllerObstacles.h | 3 +- .../include/characterkinematic/PxExtended.h | 2 +- .../physx/include/collision/PxCollisionDefs.h | 2 +- src/PhysX/physx/include/common/PxBase.h | 2 +- src/PhysX/physx/include/common/PxCollection.h | 2 +- .../physx/include/common/PxCoreUtilityTypes.h | 2 +- src/PhysX/physx/include/common/PxMetaData.h | 2 +- .../physx/include/common/PxMetaDataFlags.h | 2 +- .../include/common/PxPhysXCommonConfig.h | 6 +- .../common/PxPhysicsInsertionCallback.h | 2 +- .../physx/include/common/PxProfileZone.h | 2 +- .../physx/include/common/PxRenderBuffer.h | 2 +- .../physx/include/common/PxSerialFramework.h | 2 +- src/PhysX/physx/include/common/PxSerializer.h | 2 +- .../physx/include/common/PxStringTable.h | 2 +- .../physx/include/common/PxTolerancesScale.h | 2 +- src/PhysX/physx/include/common/PxTypeInfo.h | 2 +- .../common/windows/PxWindowsDelayLoadHook.h | 2 +- .../include/cooking/PxBVH33MidphaseDesc.h | 2 +- .../include/cooking/PxBVH34MidphaseDesc.h | 2 +- .../include/cooking/PxBVHStructureDesc.h | 2 +- .../physx/include/cooking/PxConvexMeshDesc.h | 2 +- src/PhysX/physx/include/cooking/PxCooking.h | 2 +- .../physx/include/cooking/PxMidphaseDesc.h | 2 +- .../include/cooking/PxTriangleMeshDesc.h | 2 +- src/PhysX/physx/include/cooking/Pxc.h | 2 +- .../cudamanager/PxCudaContextManager.h | 2 +- .../include/cudamanager/PxCudaMemoryManager.h | 2 +- .../physx/include/cudamanager/PxGpuCopyDesc.h | 2 +- .../include/cudamanager/PxGpuCopyDescQueue.h | 2 +- .../include/extensions/PxBinaryConverter.h | 2 +- .../include/extensions/PxBroadPhaseExt.h | 2 +- .../include/extensions/PxCollectionExt.h | 2 +- .../include/extensions/PxConstraintExt.h | 2 +- .../physx/include/extensions/PxContactJoint.h | 2 +- .../include/extensions/PxConvexMeshExt.h | 2 +- .../physx/include/extensions/PxD6Joint.h | 2 +- .../include/extensions/PxD6JointCreate.h | 2 +- .../include/extensions/PxDefaultAllocator.h | 2 +- .../extensions/PxDefaultCpuDispatcher.h | 2 +- .../extensions/PxDefaultErrorCallback.h | 2 +- .../PxDefaultSimulationFilterShader.h | 2 +- .../include/extensions/PxDefaultStreams.h | 2 +- .../include/extensions/PxDistanceJoint.h | 2 +- .../include/extensions/PxExtensionsAPI.h | 2 +- .../physx/include/extensions/PxFixedJoint.h | 2 +- src/PhysX/physx/include/extensions/PxJoint.h | 2 +- .../physx/include/extensions/PxJointLimit.h | 2 +- .../include/extensions/PxMassProperties.h | 2 +- .../include/extensions/PxPrismaticJoint.h | 2 +- .../physx/include/extensions/PxRaycastCCD.h | 2 +- .../include/extensions/PxRepXSerializer.h | 2 +- .../include/extensions/PxRepXSimpleType.h | 2 +- .../include/extensions/PxRevoluteJoint.h | 2 +- .../include/extensions/PxRigidActorExt.h | 2 +- .../physx/include/extensions/PxRigidBodyExt.h | 2 +- .../include/extensions/PxSceneQueryExt.h | 2 +- .../include/extensions/PxSerialization.h | 2 +- .../physx/include/extensions/PxShapeExt.h | 2 +- .../include/extensions/PxSimpleFactory.h | 2 +- .../include/extensions/PxSmoothNormals.h | 2 +- .../include/extensions/PxSphericalJoint.h | 2 +- .../include/extensions/PxStringTableExt.h | 2 +- .../include/extensions/PxTriangleMeshExt.h | 2 +- src/PhysX/physx/include/filebuf/PxFileBuf.h | 2 +- src/PhysX/physx/include/foundation/PxAssert.h | 98 ++ .../include/foundation/PxFoundationConfig.h | 57 + .../physx/include/foundation/PxMathUtils.h | 74 ++ .../physx/include/geometry/PxBVHStructure.h | 2 +- .../physx/include/geometry/PxBoxGeometry.h | 2 +- .../include/geometry/PxCapsuleGeometry.h | 3 +- .../physx/include/geometry/PxConvexMesh.h | 2 +- .../include/geometry/PxConvexMeshGeometry.h | 2 +- src/PhysX/physx/include/geometry/PxGeometry.h | 2 +- .../include/geometry/PxGeometryHelpers.h | 2 +- .../physx/include/geometry/PxGeometryQuery.h | 2 +- .../physx/include/geometry/PxHeightField.h | 2 +- .../include/geometry/PxHeightFieldDesc.h | 2 +- .../include/geometry/PxHeightFieldFlag.h | 2 +- .../include/geometry/PxHeightFieldGeometry.h | 2 +- .../include/geometry/PxHeightFieldSample.h | 2 +- .../physx/include/geometry/PxMeshQuery.h | 2 +- .../physx/include/geometry/PxMeshScale.h | 2 +- .../physx/include/geometry/PxPlaneGeometry.h | 3 +- .../include/geometry/PxSimpleTriangleMesh.h | 2 +- .../physx/include/geometry/PxSphereGeometry.h | 2 +- src/PhysX/physx/include/geometry/PxTriangle.h | 2 +- .../physx/include/geometry/PxTriangleMesh.h | 2 +- .../include/geometry/PxTriangleMeshGeometry.h | 2 +- .../physx/include/geomutils/GuContactBuffer.h | 2 +- .../physx/include/geomutils/GuContactPoint.h | 2 +- src/PhysX/physx/include/gpu/PxGpu.h | 2 +- src/PhysX/physx/include/pvd/PxPvd.h | 2 +- .../physx/include/pvd/PxPvdSceneClient.h | 2 +- src/PhysX/physx/include/pvd/PxPvdTransport.h | 2 +- src/PhysX/physx/include/solver/PxSolverDefs.h | 2 +- .../physx/include/task/PxCpuDispatcher.h | 2 +- .../physx/include/task/PxGpuDispatcher.h | 2 +- src/PhysX/physx/include/task/PxGpuTask.h | 2 +- src/PhysX/physx/include/task/PxTask.h | 2 +- src/PhysX/physx/include/task/PxTaskDefine.h | 2 +- src/PhysX/physx/include/task/PxTaskManager.h | 2 +- .../include/vehicle/PxVehicleComponents.h | 2 +- .../physx/include/vehicle/PxVehicleDrive.h | 3 +- .../physx/include/vehicle/PxVehicleDrive4W.h | 3 +- .../physx/include/vehicle/PxVehicleDriveNW.h | 3 +- .../include/vehicle/PxVehicleDriveTank.h | 3 +- .../physx/include/vehicle/PxVehicleNoDrive.h | 3 +- .../physx/include/vehicle/PxVehicleSDK.h | 2 +- .../physx/include/vehicle/PxVehicleShaders.h | 2 +- .../include/vehicle/PxVehicleTireFriction.h | 2 +- .../physx/include/vehicle/PxVehicleUpdate.h | 2 +- .../physx/include/vehicle/PxVehicleUtil.h | 2 +- .../include/vehicle/PxVehicleUtilControl.h | 2 +- .../include/vehicle/PxVehicleUtilSetup.h | 2 +- .../include/vehicle/PxVehicleUtilTelemetry.h | 2 +- .../physx/include/vehicle/PxVehicleWheels.h | 2 +- src/PhysX/physx/platform_readme.html | 4 +- src/PhysX/physx/release_notes.html | 42 +- .../include/windows/CmWindowsLoadLibrary.h | 2 +- .../windows/CmWindowsModuleUpdateLoader.h | 2 +- src/PhysX/physx/source/common/src/CmBitMap.h | 2 +- .../physx/source/common/src/CmBlockArray.h | 2 +- .../physx/source/common/src/CmCollection.cpp | 2 +- .../physx/source/common/src/CmCollection.h | 2 +- .../source/common/src/CmConeLimitHelper.h | 2 +- .../physx/source/common/src/CmFlushPool.h | 2 +- src/PhysX/physx/source/common/src/CmIDPool.h | 2 +- src/PhysX/physx/source/common/src/CmIO.h | 2 +- .../physx/source/common/src/CmMathUtils.cpp | 2 +- .../physx/source/common/src/CmMatrix34.h | 2 +- .../physx/source/common/src/CmPhysXCommon.h | 2 +- src/PhysX/physx/source/common/src/CmPool.h | 76 +- .../source/common/src/CmPreallocatingPool.h | 2 +- .../physx/source/common/src/CmPriorityQueue.h | 2 +- .../physx/source/common/src/CmPtrTable.cpp | 2 +- .../physx/source/common/src/CmPtrTable.h | 2 +- src/PhysX/physx/source/common/src/CmQueue.h | 2 +- .../physx/source/common/src/CmRadixSort.cpp | 2 +- .../physx/source/common/src/CmRadixSort.h | 2 +- .../source/common/src/CmRadixSortBuffered.cpp | 2 +- .../source/common/src/CmRadixSortBuffered.h | 2 +- .../physx/source/common/src/CmRefCountable.h | 2 +- .../physx/source/common/src/CmRenderBuffer.h | 2 +- .../source/common/src/CmRenderOutput.cpp | 2 +- .../physx/source/common/src/CmRenderOutput.h | 2 +- src/PhysX/physx/source/common/src/CmScaling.h | 2 +- .../physx/source/common/src/CmSpatialVector.h | 2 +- src/PhysX/physx/source/common/src/CmTask.h | 2 +- .../physx/source/common/src/CmTaskPool.h | 2 +- src/PhysX/physx/source/common/src/CmTmpMem.h | 2 +- .../source/common/src/CmTransformUtils.h | 2 +- src/PhysX/physx/source/common/src/CmUtils.h | 2 +- .../source/common/src/CmVisualization.cpp | 2 +- .../physx/source/common/src/CmVisualization.h | 2 +- .../src/windows/CmWindowsDelayLoadHook.cpp | 2 +- .../windows/CmWindowsModuleUpdateLoader.cpp | 2 +- .../source/compiler/cmake/CMakeLists.txt | 8 +- .../physx/source/compiler/cmake/FastXml.cmake | 2 +- .../source/compiler/cmake/IfTemplate.txt | 2 +- .../source/compiler/cmake/LowLevel.cmake | 3 +- .../source/compiler/cmake/LowLevelAABB.cmake | 2 +- .../compiler/cmake/LowLevelDynamics.cmake | 2 +- .../physx/source/compiler/cmake/PhysX.cmake | 2 +- .../cmake/PhysXCharacterKinematic.cmake | 3 +- .../source/compiler/cmake/PhysXCommon.cmake | 5 +- .../source/compiler/cmake/PhysXCooking.cmake | 2 +- .../compiler/cmake/PhysXExtensions.cmake | 2 +- .../compiler/cmake/PhysXFoundation.cmake | 18 +- .../source/compiler/cmake/PhysXPvdSDK.cmake | 2 +- .../source/compiler/cmake/PhysXTask.cmake | 2 +- .../source/compiler/cmake/PhysXVehicle.cmake | 2 +- .../source/compiler/cmake/SceneQuery.cmake | 2 +- .../compiler/cmake/SimulationController.cmake | 2 +- .../compiler/cmake/android/CMakeLists.txt | 18 +- .../compiler/cmake/android/FastXml.cmake | 4 +- .../compiler/cmake/android/LowLevel.cmake | 2 +- .../compiler/cmake/android/LowLevelAABB.cmake | 2 +- .../cmake/android/LowLevelDynamics.cmake | 2 +- .../source/compiler/cmake/android/PhysX.cmake | 2 +- .../android/PhysXCharacterKinematic.cmake | 2 +- .../compiler/cmake/android/PhysXCommon.cmake | 2 +- .../compiler/cmake/android/PhysXCooking.cmake | 2 +- .../cmake/android/PhysXExtensions.cmake | 2 +- .../cmake/android/PhysXFoundation.cmake | 2 +- .../compiler/cmake/android/PhysXPvdSDK.cmake | 2 +- .../compiler/cmake/android/PhysXTask.cmake | 2 +- .../compiler/cmake/android/PhysXVehicle.cmake | 2 +- .../compiler/cmake/android/SceneQuery.cmake | 2 +- .../cmake/android/SimulationController.cmake | 2 +- .../source/compiler/cmake/ios/CMakeLists.txt | 18 +- .../source/compiler/cmake/ios/FastXml.cmake | 4 +- .../source/compiler/cmake/ios/LowLevel.cmake | 2 +- .../compiler/cmake/ios/LowLevelAABB.cmake | 2 +- .../compiler/cmake/ios/LowLevelDynamics.cmake | 2 +- .../source/compiler/cmake/ios/PhysX.cmake | 2 +- .../cmake/ios/PhysXCharacterKinematic.cmake | 4 +- .../compiler/cmake/ios/PhysXCommon.cmake | 4 +- .../compiler/cmake/ios/PhysXCooking.cmake | 2 +- .../compiler/cmake/ios/PhysXExtensions.cmake | 2 +- .../compiler/cmake/ios/PhysXFoundation.cmake | 2 +- .../compiler/cmake/ios/PhysXPvdSDK.cmake | 2 +- .../source/compiler/cmake/ios/PhysXTask.cmake | 4 +- .../compiler/cmake/ios/PhysXVehicle.cmake | 2 +- .../compiler/cmake/ios/SceneQuery.cmake | 2 +- .../cmake/ios/SimulationController.cmake | 2 +- .../compiler/cmake/linux/CMakeLists.txt | 39 +- .../source/compiler/cmake/linux/FastXml.cmake | 2 +- .../compiler/cmake/linux/LowLevel.cmake | 2 +- .../compiler/cmake/linux/LowLevelAABB.cmake | 2 +- .../cmake/linux/LowLevelDynamics.cmake | 2 +- .../source/compiler/cmake/linux/PhysX.cmake | 10 +- .../cmake/linux/PhysXCharacterKinematic.cmake | 4 +- .../compiler/cmake/linux/PhysXCommon.cmake | 4 +- .../compiler/cmake/linux/PhysXCooking.cmake | 4 +- .../cmake/linux/PhysXExtensions.cmake | 2 +- .../cmake/linux/PhysXFoundation.cmake | 4 +- .../compiler/cmake/linux/PhysXPvdSDK.cmake | 2 +- .../compiler/cmake/linux/PhysXTask.cmake | 2 +- .../compiler/cmake/linux/PhysXVehicle.cmake | 2 +- .../compiler/cmake/linux/SceneQuery.cmake | 2 +- .../cmake/linux/SimulationController.cmake | 2 +- .../source/compiler/cmake/mac/CMakeLists.txt | 31 +- .../source/compiler/cmake/mac/FastXml.cmake | 4 +- .../source/compiler/cmake/mac/LowLevel.cmake | 2 +- .../compiler/cmake/mac/LowLevelAABB.cmake | 2 +- .../compiler/cmake/mac/LowLevelDynamics.cmake | 2 +- .../source/compiler/cmake/mac/PhysX.cmake | 2 +- .../cmake/mac/PhysXCharacterKinematic.cmake | 2 +- .../compiler/cmake/mac/PhysXCommon.cmake | 2 +- .../compiler/cmake/mac/PhysXCooking.cmake | 2 +- .../compiler/cmake/mac/PhysXExtensions.cmake | 2 +- .../compiler/cmake/mac/PhysXFoundation.cmake | 6 +- .../compiler/cmake/mac/PhysXPvdSDK.cmake | 2 +- .../source/compiler/cmake/mac/PhysXTask.cmake | 4 +- .../compiler/cmake/mac/PhysXVehicle.cmake | 2 +- .../compiler/cmake/mac/SceneQuery.cmake | 2 +- .../cmake/mac/SimulationController.cmake | 2 +- .../compiler/cmake/windows/CMakeLists.txt | 51 +- .../compiler/cmake/windows/FastXml.cmake | 4 +- .../compiler/cmake/windows/LowLevel.cmake | 2 +- .../compiler/cmake/windows/LowLevelAABB.cmake | 2 +- .../cmake/windows/LowLevelDynamics.cmake | 2 +- .../source/compiler/cmake/windows/PhysX.cmake | 16 +- .../windows/PhysXCharacterKinematic.cmake | 4 +- .../compiler/cmake/windows/PhysXCommon.cmake | 4 +- .../compiler/cmake/windows/PhysXCooking.cmake | 4 +- .../cmake/windows/PhysXExtensions.cmake | 2 +- .../cmake/windows/PhysXFoundation.cmake | 10 +- .../compiler/cmake/windows/PhysXPvdSDK.cmake | 2 +- .../compiler/cmake/windows/PhysXTask.cmake | 2 +- .../compiler/cmake/windows/PhysXVehicle.cmake | 2 +- .../compiler/cmake/windows/SceneQuery.cmake | 2 +- .../cmake/windows/SimulationController.cmake | 2 +- .../source/compiler/resource_x64/PhysX.rc | 2 +- .../compiler/resource_x64/PhysXCommon.rc | 2 +- .../compiler/resource_x64/PhysXCooking.rc | 2 +- .../compiler/resource_x64/PhysXFoundation.rc | Bin 4644 -> 4642 bytes .../source/compiler/resource_x64/resource.h | 2 +- .../source/compiler/resource_x86/PhysX.rc | 2 +- .../compiler/resource_x86/PhysXCommon.rc | 2 +- .../compiler/resource_x86/PhysXCooking.rc | 2 +- .../compiler/resource_x86/PhysXFoundation.rc | Bin 4642 -> 4642 bytes .../source/compiler/resource_x86/resource.h | 2 +- .../physx/source/fastxml/include/PsFastXml.h | 2 +- .../physx/source/fastxml/src/PsFastXml.cpp | 2 +- .../filebuf/include/PsAsciiConversion.h | 2 +- .../filebuf/include/PsAsciiConversion.inl | 2 +- .../source/filebuf/include/PsFileBuffer.h | 2 +- .../physx/source/filebuf/include/PsIOStream.h | 2 +- .../source/filebuf/include/PsIOStream.inl | 38 +- .../source/filebuf/include/PsMemoryBuffer.h | 2 +- .../physx/source/foundation/include/Ps.h | 2 +- .../foundation/include/PsAlignedMalloc.h | 2 +- .../source/foundation/include/PsAlloca.h | 2 +- .../source/foundation/include/PsAllocator.h | 2 +- .../physx/source/foundation/include/PsAoS.h | 2 +- .../physx/source/foundation/include/PsArray.h | 2 +- .../source/foundation/include/PsAtomic.h | 3 +- .../foundation/include/PsBasicTemplates.h | 2 +- .../source/foundation/include/PsBitUtils.h | 2 +- .../source/foundation/include/PsBroadcast.h | 2 +- .../physx/source/foundation/include/PsCpu.h | 2 +- .../physx/source/foundation/include/PsFPU.h | 18 +- .../source/foundation/include/PsFoundation.h | 2 +- .../physx/source/foundation/include/PsHash.h | 2 +- .../foundation/include/PsHashInternals.h | 2 +- .../source/foundation/include/PsHashMap.h | 2 +- .../source/foundation/include/PsHashSet.h | 2 +- .../foundation/include/PsInlineAllocator.h | 2 +- .../source/foundation/include/PsInlineAoS.h | 2 +- .../source/foundation/include/PsInlineArray.h | 2 +- .../source/foundation/include/PsIntrinsics.h | 2 +- .../source/foundation/include/PsMathUtils.h | 2 +- .../physx/source/foundation/include/PsMutex.h | 2 +- .../physx/source/foundation/include/PsPool.h | 2 +- .../physx/source/foundation/include/PsSList.h | 2 +- .../source/foundation/include/PsSocket.h | 2 +- .../physx/source/foundation/include/PsSort.h | 2 +- .../foundation/include/PsSortInternals.h | 2 +- .../source/foundation/include/PsString.h | 3 +- .../physx/source/foundation/include/PsSync.h | 2 +- .../foundation/include/PsTempAllocator.h | 2 +- .../source/foundation/include/PsThread.h | 2 +- .../physx/source/foundation/include/PsTime.h | 3 +- .../foundation/include/PsUserAllocated.h | 2 +- .../source/foundation/include/PsUtilities.h | 2 +- .../source/foundation/include/PsVecMath.h | 2 +- .../foundation/include/PsVecMathAoSScalar.h | 2 +- .../include/PsVecMathAoSScalarInline.h | 2 +- .../source/foundation/include/PsVecMathSSE.h | 2 +- .../foundation/include/PsVecMathUtilities.h | 2 +- .../source/foundation/include/PsVecQuat.h | 2 +- .../foundation/include/PsVecTransform.h | 2 +- .../foundation/include/unix/PsUnixAoS.h | 2 +- .../foundation/include/unix/PsUnixFPU.h | 5 +- .../foundation/include/unix/PsUnixInlineAoS.h | 2 +- .../include/unix/PsUnixIntrinsics.h | 2 +- .../include/unix/PsUnixTrigConstants.h | 2 +- .../include/unix/neon/PsUnixNeonAoS.h | 2 +- .../include/unix/neon/PsUnixNeonInlineAoS.h | 2 +- .../include/unix/sse2/PsUnixSse2AoS.h | 2 +- .../include/unix/sse2/PsUnixSse2InlineAoS.h | 2 +- .../foundation/include/windows/PsWindowsAoS.h | 2 +- .../foundation/include/windows/PsWindowsFPU.h | 2 +- .../include/windows/PsWindowsInclude.h | 2 +- .../include/windows/PsWindowsInlineAoS.h | 2 +- .../include/windows/PsWindowsIntrinsics.h | 2 +- .../include/windows/PsWindowsTrigConstants.h | 2 +- .../source/foundation/src/PsAllocator.cpp | 2 +- .../physx/source/foundation/src/PsAssert.cpp | 2 +- .../source/foundation/src/PsFoundation.cpp | 3 +- .../source/foundation/src/PsMathUtils.cpp | 2 +- .../physx/source/foundation/src/PsString.cpp | 2 +- .../source/foundation/src/PsTempAllocator.cpp | 2 +- .../source/foundation/src/PsUtilities.cpp | 2 +- .../foundation/src/unix/PsUnixAtomic.cpp | 2 +- .../source/foundation/src/unix/PsUnixCpu.cpp | 2 +- .../source/foundation/src/unix/PsUnixFPU.cpp | 2 +- .../foundation/src/unix/PsUnixMutex.cpp | 2 +- .../foundation/src/unix/PsUnixPrintString.cpp | 2 +- .../foundation/src/unix/PsUnixSList.cpp | 2 +- .../foundation/src/unix/PsUnixSocket.cpp | 2 +- .../source/foundation/src/unix/PsUnixSync.cpp | 2 +- .../foundation/src/unix/PsUnixThread.cpp | 2 +- .../source/foundation/src/unix/PsUnixTime.cpp | 2 +- .../src/windows/PsWindowsAtomic.cpp | 2 +- .../foundation/src/windows/PsWindowsCpu.cpp | 2 +- .../foundation/src/windows/PsWindowsFPU.cpp | 2 +- .../foundation/src/windows/PsWindowsMutex.cpp | 2 +- .../src/windows/PsWindowsPrintString.cpp | 2 +- .../foundation/src/windows/PsWindowsSList.cpp | 2 +- .../src/windows/PsWindowsSocket.cpp | 2 +- .../foundation/src/windows/PsWindowsSync.cpp | 2 +- .../src/windows/PsWindowsThread.cpp | 2 +- .../foundation/src/windows/PsWindowsTime.cpp | 2 +- .../physx/source/geomutils/include/GuBox.h | 2 +- .../geomutils/include/GuDistanceSegmentBox.h | 2 +- .../include/GuDistanceSegmentSegment.h | 2 +- .../geomutils/include/GuIntersectionBoxBox.h | 2 +- .../include/GuIntersectionTriangleBox.h | 2 +- .../include/GuIntersectionTriangleBoxRef.h | 2 +- .../source/geomutils/include/GuRaycastTests.h | 2 +- .../source/geomutils/include/GuSIMDHelpers.h | 2 +- .../source/geomutils/include/GuSegment.h | 2 +- .../source/geomutils/src/GuAABBTreeBuild.cpp | 2 +- .../source/geomutils/src/GuAABBTreeBuild.h | 2 +- .../source/geomutils/src/GuAABBTreeQuery.h | 2 +- .../source/geomutils/src/GuBVHStructure.cpp | 2 +- .../source/geomutils/src/GuBVHStructure.h | 2 +- .../source/geomutils/src/GuBVHTestsSIMD.h | 2 +- .../physx/source/geomutils/src/GuBounds.cpp | 2 +- .../physx/source/geomutils/src/GuBounds.h | 2 +- .../physx/source/geomutils/src/GuBox.cpp | 2 +- .../source/geomutils/src/GuCCTSweepTests.cpp | 2 +- .../physx/source/geomutils/src/GuCapsule.cpp | 2 +- .../physx/source/geomutils/src/GuCapsule.h | 2 +- .../source/geomutils/src/GuCenterExtents.h | 2 +- .../source/geomutils/src/GuGeometryQuery.cpp | 2 +- .../source/geomutils/src/GuGeometryUnion.cpp | 2 +- .../source/geomutils/src/GuGeometryUnion.h | 2 +- .../physx/source/geomutils/src/GuInternal.cpp | 2 +- .../physx/source/geomutils/src/GuInternal.h | 2 +- .../physx/source/geomutils/src/GuMTD.cpp | 2 +- src/PhysX/physx/source/geomutils/src/GuMTD.h | 2 +- .../source/geomutils/src/GuMeshFactory.cpp | 2 +- .../source/geomutils/src/GuMeshFactory.h | 2 +- .../physx/source/geomutils/src/GuMetaData.cpp | 2 +- .../source/geomutils/src/GuOverlapTests.cpp | 2 +- .../source/geomutils/src/GuOverlapTests.h | 2 +- .../source/geomutils/src/GuRaycastTests.cpp | 2 +- .../source/geomutils/src/GuSerialize.cpp | 2 +- .../physx/source/geomutils/src/GuSerialize.h | 2 +- .../physx/source/geomutils/src/GuSphere.h | 2 +- .../physx/source/geomutils/src/GuSweepMTD.cpp | 2 +- .../physx/source/geomutils/src/GuSweepMTD.h | 2 +- .../geomutils/src/GuSweepSharedTests.cpp | 2 +- .../source/geomutils/src/GuSweepSharedTests.h | 2 +- .../source/geomutils/src/GuSweepTests.cpp | 2 +- .../physx/source/geomutils/src/GuSweepTests.h | 2 +- .../src/ccd/GuCCDSweepConvexMesh.cpp | 2 +- .../geomutils/src/ccd/GuCCDSweepConvexMesh.h | 2 +- .../src/ccd/GuCCDSweepPrimitives.cpp | 2 +- .../src/common/GuBarycentricCoordinates.cpp | 2 +- .../src/common/GuBarycentricCoordinates.h | 2 +- .../geomutils/src/common/GuBoxConversion.h | 2 +- .../source/geomutils/src/common/GuEdgeCache.h | 2 +- .../geomutils/src/common/GuEdgeListData.h | 2 +- .../geomutils/src/common/GuSeparatingAxes.cpp | 2 +- .../geomutils/src/common/GuSeparatingAxes.h | 2 +- .../geomutils/src/contact/GuContactBoxBox.cpp | 2 +- .../src/contact/GuContactCapsuleBox.cpp | 2 +- .../src/contact/GuContactCapsuleCapsule.cpp | 2 +- .../src/contact/GuContactCapsuleConvex.cpp | 2 +- .../src/contact/GuContactCapsuleMesh.cpp | 2 +- .../src/contact/GuContactConvexConvex.cpp | 2 +- .../src/contact/GuContactConvexMesh.cpp | 2 +- .../src/contact/GuContactMethodImpl.h | 2 +- .../src/contact/GuContactPlaneBox.cpp | 2 +- .../src/contact/GuContactPlaneCapsule.cpp | 2 +- .../src/contact/GuContactPlaneConvex.cpp | 2 +- .../src/contact/GuContactPolygonPolygon.cpp | 2 +- .../src/contact/GuContactPolygonPolygon.h | 2 +- .../src/contact/GuContactSphereBox.cpp | 2 +- .../src/contact/GuContactSphereCapsule.cpp | 2 +- .../src/contact/GuContactSphereMesh.cpp | 2 +- .../src/contact/GuContactSpherePlane.cpp | 2 +- .../src/contact/GuContactSphereSphere.cpp | 2 +- .../geomutils/src/contact/GuFeatureCode.cpp | 2 +- .../geomutils/src/contact/GuFeatureCode.h | 2 +- .../geomutils/src/convex/GuBigConvexData.cpp | 2 +- .../geomutils/src/convex/GuBigConvexData.h | 2 +- .../geomutils/src/convex/GuBigConvexData2.h | 2 +- .../geomutils/src/convex/GuConvexEdgeFlags.h | 2 +- .../geomutils/src/convex/GuConvexHelper.cpp | 2 +- .../geomutils/src/convex/GuConvexHelper.h | 2 +- .../geomutils/src/convex/GuConvexMesh.cpp | 2 +- .../geomutils/src/convex/GuConvexMesh.h | 2 +- .../geomutils/src/convex/GuConvexMeshData.h | 2 +- .../src/convex/GuConvexSupportTable.cpp | 2 +- .../src/convex/GuConvexSupportTable.h | 2 +- .../src/convex/GuConvexUtilsInternal.cpp | 2 +- .../src/convex/GuConvexUtilsInternal.h | 2 +- .../source/geomutils/src/convex/GuCubeIndex.h | 2 +- .../geomutils/src/convex/GuHillClimbing.cpp | 2 +- .../geomutils/src/convex/GuHillClimbing.h | 2 +- .../geomutils/src/convex/GuShapeConvex.cpp | 2 +- .../geomutils/src/convex/GuShapeConvex.h | 2 +- .../src/distance/GuDistancePointBox.cpp | 2 +- .../src/distance/GuDistancePointBox.h | 2 +- .../src/distance/GuDistancePointSegment.h | 2 +- .../src/distance/GuDistancePointTriangle.cpp | 2 +- .../src/distance/GuDistancePointTriangle.h | 2 +- .../distance/GuDistancePointTriangleSIMD.h | 2 +- .../src/distance/GuDistanceSegmentBox.cpp | 2 +- .../src/distance/GuDistanceSegmentSegment.cpp | 2 +- .../distance/GuDistanceSegmentSegmentSIMD.h | 2 +- .../distance/GuDistanceSegmentTriangle.cpp | 2 +- .../src/distance/GuDistanceSegmentTriangle.h | 2 +- .../distance/GuDistanceSegmentTriangleSIMD.h | 2 +- .../physx/source/geomutils/src/gjk/GuEPA.cpp | 2 +- .../physx/source/geomutils/src/gjk/GuEPA.h | 2 +- .../source/geomutils/src/gjk/GuEPAFacet.h | 2 +- .../physx/source/geomutils/src/gjk/GuGJK.h | 2 +- .../geomutils/src/gjk/GuGJKPenetration.h | 2 +- .../source/geomutils/src/gjk/GuGJKRaycast.h | 2 +- .../source/geomutils/src/gjk/GuGJKSimplex.cpp | 2 +- .../source/geomutils/src/gjk/GuGJKSimplex.h | 2 +- .../source/geomutils/src/gjk/GuGJKTest.cpp | 2 +- .../source/geomutils/src/gjk/GuGJKTest.h | 2 +- .../source/geomutils/src/gjk/GuGJKType.h | 2 +- .../source/geomutils/src/gjk/GuGJKUtil.h | 2 +- .../physx/source/geomutils/src/gjk/GuVecBox.h | 2 +- .../source/geomutils/src/gjk/GuVecCapsule.h | 2 +- .../source/geomutils/src/gjk/GuVecConvex.h | 2 +- .../geomutils/src/gjk/GuVecConvexHull.h | 2 +- .../src/gjk/GuVecConvexHullNoScale.h | 2 +- .../source/geomutils/src/gjk/GuVecPlane.h | 2 +- .../source/geomutils/src/gjk/GuVecSphere.h | 2 +- .../source/geomutils/src/gjk/GuVecTriangle.h | 2 +- .../source/geomutils/src/hf/GuEntityReport.h | 2 +- .../source/geomutils/src/hf/GuHeightField.cpp | 2 +- .../source/geomutils/src/hf/GuHeightField.h | 2 +- .../geomutils/src/hf/GuHeightFieldData.h | 2 +- .../geomutils/src/hf/GuHeightFieldUtil.cpp | 2 +- .../geomutils/src/hf/GuHeightFieldUtil.h | 2 +- .../geomutils/src/hf/GuOverlapTestsHF.cpp | 2 +- .../source/geomutils/src/hf/GuSweepsHF.cpp | 2 +- .../src/intersection/GuIntersectionBoxBox.cpp | 2 +- .../GuIntersectionCapsuleTriangle.cpp | 2 +- .../GuIntersectionCapsuleTriangle.h | 2 +- .../intersection/GuIntersectionEdgeEdge.cpp | 2 +- .../src/intersection/GuIntersectionEdgeEdge.h | 2 +- .../src/intersection/GuIntersectionRay.h | 2 +- .../src/intersection/GuIntersectionRayBox.cpp | 2 +- .../src/intersection/GuIntersectionRayBox.h | 2 +- .../intersection/GuIntersectionRayBoxSIMD.h | 2 +- .../intersection/GuIntersectionRayCapsule.cpp | 2 +- .../intersection/GuIntersectionRayCapsule.h | 2 +- .../src/intersection/GuIntersectionRayPlane.h | 2 +- .../intersection/GuIntersectionRaySphere.cpp | 2 +- .../intersection/GuIntersectionRaySphere.h | 2 +- .../intersection/GuIntersectionRayTriangle.h | 2 +- .../intersection/GuIntersectionSphereBox.cpp | 2 +- .../intersection/GuIntersectionSphereBox.h | 2 +- .../GuIntersectionTriangleBox.cpp | 2 +- .../source/geomutils/src/mesh/GuBV32.cpp | 2 +- .../physx/source/geomutils/src/mesh/GuBV32.h | 2 +- .../source/geomutils/src/mesh/GuBV32Build.cpp | 2 +- .../source/geomutils/src/mesh/GuBV32Build.h | 2 +- .../physx/source/geomutils/src/mesh/GuBV4.cpp | 2 +- .../physx/source/geomutils/src/mesh/GuBV4.h | 2 +- .../source/geomutils/src/mesh/GuBV4Build.cpp | 2 +- .../source/geomutils/src/mesh/GuBV4Build.h | 2 +- .../source/geomutils/src/mesh/GuBV4Settings.h | 2 +- .../src/mesh/GuBV4_AABBAABBSweepTest.h | 2 +- .../geomutils/src/mesh/GuBV4_AABBSweep.cpp | 2 +- .../src/mesh/GuBV4_BoxBoxOverlapTest.h | 2 +- .../geomutils/src/mesh/GuBV4_BoxOverlap.cpp | 2 +- .../src/mesh/GuBV4_BoxOverlap_Internal.h | 2 +- .../src/mesh/GuBV4_BoxSweep_Internal.h | 2 +- .../src/mesh/GuBV4_BoxSweep_Params.h | 2 +- .../geomutils/src/mesh/GuBV4_CapsuleSweep.cpp | 2 +- .../src/mesh/GuBV4_CapsuleSweepAA.cpp | 2 +- .../src/mesh/GuBV4_CapsuleSweep_Internal.h | 2 +- .../source/geomutils/src/mesh/GuBV4_Common.h | 2 +- .../geomutils/src/mesh/GuBV4_Internal.h | 2 +- .../geomutils/src/mesh/GuBV4_OBBSweep.cpp | 2 +- .../mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h | 2 +- .../GuBV4_ProcessStreamNoOrder_SegmentAABB.h | 2 +- ...rocessStreamNoOrder_SegmentAABB_Inflated.h | 2 +- .../GuBV4_ProcessStreamNoOrder_SphereAABB.h | 2 +- .../mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h | 2 +- .../GuBV4_ProcessStreamOrdered_SegmentAABB.h | 2 +- ...rocessStreamOrdered_SegmentAABB_Inflated.h | 2 +- .../geomutils/src/mesh/GuBV4_Raycast.cpp | 2 +- .../source/geomutils/src/mesh/GuBV4_Slabs.h | 2 +- .../src/mesh/GuBV4_Slabs_KajiyaNoOrder.h | 2 +- .../src/mesh/GuBV4_Slabs_KajiyaOrdered.h | 2 +- .../src/mesh/GuBV4_Slabs_SwizzledNoOrder.h | 2 +- .../src/mesh/GuBV4_Slabs_SwizzledOrdered.h | 2 +- .../src/mesh/GuBV4_SphereOverlap.cpp | 2 +- .../geomutils/src/mesh/GuBV4_SphereSweep.cpp | 2 +- .../source/geomutils/src/mesh/GuBVConstants.h | 2 +- .../source/geomutils/src/mesh/GuMeshData.h | 2 +- .../source/geomutils/src/mesh/GuMeshQuery.cpp | 2 +- .../geomutils/src/mesh/GuMidphaseBV4.cpp | 2 +- .../geomutils/src/mesh/GuMidphaseInterface.h | 2 +- .../geomutils/src/mesh/GuMidphaseRTree.cpp | 2 +- .../geomutils/src/mesh/GuOverlapTestsMesh.cpp | 2 +- .../source/geomutils/src/mesh/GuRTree.cpp | 2 +- .../physx/source/geomutils/src/mesh/GuRTree.h | 2 +- .../geomutils/src/mesh/GuRTreeQueries.cpp | 2 +- .../geomutils/src/mesh/GuSweepConvexTri.h | 2 +- .../source/geomutils/src/mesh/GuSweepMesh.h | 2 +- .../geomutils/src/mesh/GuSweepsMesh.cpp | 2 +- .../source/geomutils/src/mesh/GuTriangle32.h | 2 +- .../geomutils/src/mesh/GuTriangleCache.h | 2 +- .../geomutils/src/mesh/GuTriangleMesh.cpp | 2 +- .../geomutils/src/mesh/GuTriangleMesh.h | 2 +- .../geomutils/src/mesh/GuTriangleMeshBV4.cpp | 2 +- .../geomutils/src/mesh/GuTriangleMeshBV4.h | 2 +- .../src/mesh/GuTriangleMeshRTree.cpp | 2 +- .../geomutils/src/mesh/GuTriangleMeshRTree.h | 2 +- .../src/mesh/GuTriangleVertexPointers.h | 2 +- .../geomutils/src/pcm/GuPCMContactBoxBox.cpp | 2 +- .../src/pcm/GuPCMContactBoxConvex.cpp | 2 +- .../src/pcm/GuPCMContactCapsuleBox.cpp | 2 +- .../src/pcm/GuPCMContactCapsuleCapsule.cpp | 2 +- .../src/pcm/GuPCMContactCapsuleConvex.cpp | 2 +- .../pcm/GuPCMContactCapsuleHeightField.cpp | 2 +- .../src/pcm/GuPCMContactCapsuleMesh.cpp | 2 +- .../src/pcm/GuPCMContactConvexCommon.cpp | 2 +- .../src/pcm/GuPCMContactConvexCommon.h | 2 +- .../src/pcm/GuPCMContactConvexConvex.cpp | 2 +- .../src/pcm/GuPCMContactConvexHeightField.cpp | 2 +- .../src/pcm/GuPCMContactConvexMesh.cpp | 2 +- .../geomutils/src/pcm/GuPCMContactGen.h | 2 +- .../src/pcm/GuPCMContactGenBoxConvex.cpp | 2 +- .../src/pcm/GuPCMContactGenSphereCapsule.cpp | 2 +- .../geomutils/src/pcm/GuPCMContactGenUtil.h | 2 +- .../src/pcm/GuPCMContactMeshCallback.h | 2 +- .../src/pcm/GuPCMContactPlaneBox.cpp | 2 +- .../src/pcm/GuPCMContactPlaneCapsule.cpp | 2 +- .../src/pcm/GuPCMContactPlaneConvex.cpp | 2 +- .../src/pcm/GuPCMContactSphereBox.cpp | 2 +- .../src/pcm/GuPCMContactSphereCapsule.cpp | 2 +- .../src/pcm/GuPCMContactSphereConvex.cpp | 2 +- .../src/pcm/GuPCMContactSphereHeightField.cpp | 2 +- .../src/pcm/GuPCMContactSphereMesh.cpp | 2 +- .../src/pcm/GuPCMContactSpherePlane.cpp | 2 +- .../src/pcm/GuPCMContactSphereSphere.cpp | 2 +- .../geomutils/src/pcm/GuPCMShapeConvex.cpp | 2 +- .../geomutils/src/pcm/GuPCMShapeConvex.h | 2 +- .../src/pcm/GuPCMTriangleContactGen.cpp | 2 +- .../src/pcm/GuPCMTriangleContactGen.h | 2 +- .../src/pcm/GuPersistentContactManifold.cpp | 2 +- .../src/pcm/GuPersistentContactManifold.h | 2 +- .../geomutils/src/sweep/GuSweepBoxBox.cpp | 2 +- .../geomutils/src/sweep/GuSweepBoxBox.h | 2 +- .../geomutils/src/sweep/GuSweepBoxSphere.cpp | 2 +- .../geomutils/src/sweep/GuSweepBoxSphere.h | 2 +- .../sweep/GuSweepBoxTriangle_FeatureBased.cpp | 2 +- .../sweep/GuSweepBoxTriangle_FeatureBased.h | 2 +- .../src/sweep/GuSweepBoxTriangle_SAT.cpp | 2 +- .../src/sweep/GuSweepBoxTriangle_SAT.h | 2 +- .../geomutils/src/sweep/GuSweepCapsuleBox.cpp | 2 +- .../geomutils/src/sweep/GuSweepCapsuleBox.h | 2 +- .../src/sweep/GuSweepCapsuleCapsule.cpp | 2 +- .../src/sweep/GuSweepCapsuleCapsule.h | 2 +- .../src/sweep/GuSweepCapsuleTriangle.cpp | 2 +- .../src/sweep/GuSweepCapsuleTriangle.h | 2 +- .../src/sweep/GuSweepSphereCapsule.cpp | 2 +- .../src/sweep/GuSweepSphereCapsule.h | 2 +- .../src/sweep/GuSweepSphereSphere.cpp | 2 +- .../geomutils/src/sweep/GuSweepSphereSphere.h | 2 +- .../src/sweep/GuSweepSphereTriangle.cpp | 2 +- .../src/sweep/GuSweepSphereTriangle.h | 2 +- .../src/sweep/GuSweepTriangleUtils.cpp | 2 +- .../src/sweep/GuSweepTriangleUtils.h | 2 +- .../immediatemode/src/NpImmediateMode.cpp | 1163 ++++++++--------- .../lowlevel/api/include/PxsMaterialCore.h | 2 +- .../lowlevel/api/include/PxsMaterialManager.h | 2 +- .../source/lowlevel/api/include/PxvConfig.h | 2 +- .../source/lowlevel/api/include/PxvDynamics.h | 2 +- .../source/lowlevel/api/include/PxvGeometry.h | 2 +- .../source/lowlevel/api/include/PxvGlobals.h | 2 +- .../source/lowlevel/api/include/PxvManager.h | 2 +- .../source/lowlevel/api/include/PxvSimStats.h | 2 +- .../source/lowlevel/api/src/px_globals.cpp | 2 +- .../include/collision/PxcContactMethodImpl.h | 2 +- .../pipeline/PxcConstraintBlockStream.h | 32 +- .../common/include/pipeline/PxcContactCache.h | 2 +- .../include/pipeline/PxcMaterialMethodImpl.h | 2 +- .../common/include/pipeline/PxcNpBatch.h | 31 +- .../common/include/pipeline/PxcNpCache.h | 2 +- .../include/pipeline/PxcNpCacheStreamPair.h | 2 +- .../include/pipeline/PxcNpContactPrepShared.h | 2 +- .../include/pipeline/PxcNpMemBlockPool.h | 2 +- .../include/pipeline/PxcNpThreadContext.h | 2 +- .../common/include/pipeline/PxcNpWorkUnit.h | 88 +- .../common/include/pipeline/PxcRigidBody.h | 76 +- .../include/utils/PxcScratchAllocator.h | 2 +- .../include/utils/PxcThreadCoherentCache.h | 2 +- .../common/src/collision/PxcContact.cpp | 2 +- .../common/src/pipeline/PxcContactCache.cpp | 2 +- .../src/pipeline/PxcContactMethodImpl.cpp | 2 +- .../src/pipeline/PxcMaterialHeightField.cpp | 2 +- .../common/src/pipeline/PxcMaterialMesh.cpp | 2 +- .../src/pipeline/PxcMaterialMethodImpl.cpp | 2 +- .../common/src/pipeline/PxcMaterialShape.cpp | 2 +- .../common/src/pipeline/PxcNpBatch.cpp | 2 +- .../src/pipeline/PxcNpCacheStreamPair.cpp | 2 +- .../src/pipeline/PxcNpContactPrepShared.cpp | 2 +- .../common/src/pipeline/PxcNpMemBlockPool.cpp | 2 +- .../src/pipeline/PxcNpThreadContext.cpp | 4 +- .../lowlevel/software/include/PxsBodySim.h | 2 +- .../source/lowlevel/software/include/PxsCCD.h | 16 +- .../software/include/PxsContactManager.h | 2 +- .../software/include/PxsContactManagerState.h | 2 +- .../lowlevel/software/include/PxsContext.h | 2 +- .../include/PxsDefaultMemoryManager.h | 2 +- .../software/include/PxsHeapMemoryAllocator.h | 2 +- .../PxsIncrementalConstraintPartitioning.h | 2 +- .../software/include/PxsIslandManagerTypes.h | 2 +- .../software/include/PxsIslandNodeIndex.h | 2 +- .../lowlevel/software/include/PxsIslandSim.h | 2 +- .../software/include/PxsKernelWrangler.h | 2 +- .../software/include/PxsMaterialCombiner.h | 2 +- .../software/include/PxsMemoryManager.h | 2 +- .../include/PxsNphaseImplementationContext.h | 2 +- .../lowlevel/software/include/PxsRigidBody.h | 2 +- .../lowlevel/software/include/PxsShapeSim.h | 2 +- .../software/include/PxsSimpleIslandManager.h | 2 +- .../include/PxsSimulationController.h | 2 +- .../software/include/PxsTransformCache.h | 2 +- .../include/PxvNphaseImplementationContext.h | 2 +- .../source/lowlevel/software/src/PxsCCD.cpp | 41 +- .../software/src/PxsContactManager.cpp | 2 +- .../lowlevel/software/src/PxsContext.cpp | 4 +- .../software/src/PxsDefaultMemoryManager.cpp | 2 +- .../lowlevel/software/src/PxsIslandSim.cpp | 2 +- .../software/src/PxsMaterialCombiner.cpp | 2 +- .../src/PxsNphaseImplementationContext.cpp | 2 +- .../software/src/PxsSimpleIslandManager.cpp | 2 +- .../lowlevelaabb/include/BpAABBManager.h | 2 +- .../lowlevelaabb/include/BpAABBManagerTasks.h | 2 +- .../lowlevelaabb/include/BpBroadPhase.h | 2 +- .../lowlevelaabb/include/BpBroadPhaseUpdate.h | 2 +- .../source/lowlevelaabb/src/BpAABBManager.cpp | 2 +- .../source/lowlevelaabb/src/BpBroadPhase.cpp | 2 +- .../lowlevelaabb/src/BpBroadPhaseABP.cpp | 2 +- .../source/lowlevelaabb/src/BpBroadPhaseABP.h | 2 +- .../lowlevelaabb/src/BpBroadPhaseMBP.cpp | 2 +- .../source/lowlevelaabb/src/BpBroadPhaseMBP.h | 2 +- .../lowlevelaabb/src/BpBroadPhaseMBPCommon.h | 2 +- .../lowlevelaabb/src/BpBroadPhaseSap.cpp | 2 +- .../source/lowlevelaabb/src/BpBroadPhaseSap.h | 2 +- .../lowlevelaabb/src/BpBroadPhaseSapAux.cpp | 2 +- .../lowlevelaabb/src/BpBroadPhaseSapAux.h | 3 +- .../lowlevelaabb/src/BpBroadPhaseShared.cpp | 2 +- .../lowlevelaabb/src/BpBroadPhaseShared.h | 2 +- .../source/lowlevelaabb/src/BpMBPTasks.cpp | 2 +- .../source/lowlevelaabb/src/BpMBPTasks.h | 2 +- .../source/lowlevelaabb/src/BpSAPTasks.cpp | 2 +- .../source/lowlevelaabb/src/BpSAPTasks.h | 2 +- .../lowleveldynamics/include/DyArticulation.h | 13 +- .../include/DyArticulationCore.h | 2 +- .../include/DyArticulationJointCore.h | 7 +- .../lowleveldynamics/include/DyConstraint.h | 29 +- .../include/DyConstraintWriteBack.h | 2 +- .../lowleveldynamics/include/DyContext.h | 2 +- .../include/DyFeatherstoneArticulation.h | 317 ++--- .../DyFeatherstoneArticulationJointData.h | 2 +- .../include/DyFeatherstoneArticulationUtils.h | 2 +- .../include/DySleepingConfigulation.h | 2 +- .../include/DyThresholdTable.h | 2 +- .../include/DyVArticulation.h | 142 +- .../lowleveldynamics/src/DyArticulation.cpp | 58 +- .../src/DyArticulationContactPrep.cpp | 8 +- .../src/DyArticulationContactPrep.h | 2 +- .../src/DyArticulationContactPrepPF.cpp | 2 +- .../src/DyArticulationFnsDebug.h | 2 +- .../src/DyArticulationFnsScalar.h | 2 +- .../src/DyArticulationFnsSimd.h | 2 +- .../src/DyArticulationHelper.cpp | 2 +- .../src/DyArticulationHelper.h | 2 +- .../src/DyArticulationPImpl.h | 2 +- .../src/DyArticulationReference.h | 2 +- .../src/DyArticulationSIMD.cpp | 2 +- .../src/DyArticulationScalar.cpp | 2 +- .../src/DyArticulationScalar.h | 2 +- .../src/DyArticulationUtils.h | 2 +- .../src/DyBodyCoreIntegrator.h | 2 +- .../src/DyConstraintPartition.cpp | 2 +- .../src/DyConstraintPartition.h | 2 +- .../lowleveldynamics/src/DyConstraintPrep.h | 2 +- .../src/DyConstraintSetup.cpp | 2 +- .../src/DyConstraintSetupBlock.cpp | 2 +- .../lowleveldynamics/src/DyContactPrep.cpp | 11 +- .../lowleveldynamics/src/DyContactPrep.h | 2 +- .../lowleveldynamics/src/DyContactPrep4.cpp | 32 +- .../lowleveldynamics/src/DyContactPrep4PF.cpp | 2 +- .../lowleveldynamics/src/DyContactPrepPF.cpp | 2 +- .../src/DyContactPrepShared.h | 2 +- .../lowleveldynamics/src/DyContactReduction.h | 2 +- .../src/DyCorrelationBuffer.h | 2 +- .../lowleveldynamics/src/DyDynamics.cpp | 2 +- .../source/lowleveldynamics/src/DyDynamics.h | 2 +- .../src/DyFeatherstoneArticulation.cpp | 247 ++-- .../src/DyFeatherstoneArticulationLink.h | 2 +- .../src/DyFeatherstoneForwardDynamic.cpp | 20 +- .../src/DyFeatherstoneInverseDynamic.cpp | 4 +- .../src/DyFrictionCorrelation.cpp | 2 +- .../lowleveldynamics/src/DyFrictionPatch.h | 2 +- .../src/DyFrictionPatchStreamPair.h | 2 +- .../src/DyRigidBodyToSolverBody.cpp | 5 +- .../lowleveldynamics/src/DySolverBody.h | 2 +- .../src/DySolverConstraint1D.h | 2 +- .../src/DySolverConstraint1D4.h | 2 +- .../src/DySolverConstraint1DStep.h | 2 +- .../src/DySolverConstraintDesc.h | 2 +- .../src/DySolverConstraintExtShared.h | 2 +- .../src/DySolverConstraintTypes.h | 2 +- .../src/DySolverConstraints.cpp | 2 +- .../src/DySolverConstraintsBlock.cpp | 2 +- .../src/DySolverConstraintsShared.h | 2 +- .../lowleveldynamics/src/DySolverContact.h | 2 +- .../lowleveldynamics/src/DySolverContact4.h | 2 +- .../lowleveldynamics/src/DySolverContactPF.h | 2 +- .../lowleveldynamics/src/DySolverContactPF4.h | 2 +- .../lowleveldynamics/src/DySolverContext.h | 2 +- .../lowleveldynamics/src/DySolverControl.cpp | 2 +- .../lowleveldynamics/src/DySolverControl.h | 2 +- .../src/DySolverControlPF.cpp | 2 +- .../lowleveldynamics/src/DySolverControlPF.h | 2 +- .../lowleveldynamics/src/DySolverCore.h | 2 +- .../source/lowleveldynamics/src/DySolverExt.h | 2 +- .../src/DySolverPFConstraints.cpp | 2 +- .../src/DySolverPFConstraintsBlock.cpp | 2 +- .../source/lowleveldynamics/src/DySpatial.h | 2 +- .../lowleveldynamics/src/DyTGSContactPrep.cpp | 23 +- .../src/DyTGSContactPrepBlock.cpp | 41 +- .../lowleveldynamics/src/DyTGSDynamics.cpp | 2 +- .../lowleveldynamics/src/DyTGSDynamics.h | 2 +- .../lowleveldynamics/src/DyTGSPartition.cpp | 2 +- .../lowleveldynamics/src/DyTGSPartition.h | 2 +- .../lowleveldynamics/src/DyThreadContext.cpp | 2 +- .../lowleveldynamics/src/DyThreadContext.h | 2 +- .../lowleveldynamics/src/DyThresholdTable.cpp | 2 +- src/PhysX/physx/source/physx/src/NpActor.cpp | 2 +- src/PhysX/physx/source/physx/src/NpActor.h | 2 +- .../physx/source/physx/src/NpActorTemplate.h | 10 +- .../physx/source/physx/src/NpAggregate.cpp | 2 +- .../physx/source/physx/src/NpAggregate.h | 2 +- .../physx/source/physx/src/NpArticulation.cpp | 3 +- .../physx/source/physx/src/NpArticulation.h | 77 +- .../source/physx/src/NpArticulationJoint.cpp | 2 +- .../source/physx/src/NpArticulationJoint.h | 2 +- .../NpArticulationJointReducedCoordinate.cpp | 2 +- .../NpArticulationJointReducedCoordinate.h | 2 +- .../source/physx/src/NpArticulationLink.cpp | 16 +- .../source/physx/src/NpArticulationLink.h | 3 +- .../src/NpArticulationReducedCoordinate.cpp | 637 +++++---- .../src/NpArticulationReducedCoordinate.h | 2 +- .../source/physx/src/NpArticulationTemplate.h | 38 +- .../physx/source/physx/src/NpBatchQuery.cpp | 2 +- .../physx/source/physx/src/NpBatchQuery.h | 2 +- src/PhysX/physx/source/physx/src/NpCast.h | 2 +- .../physx/source/physx/src/NpConnector.h | 2 +- .../physx/source/physx/src/NpConstraint.cpp | 2 +- .../physx/source/physx/src/NpConstraint.h | 2 +- .../physx/source/physx/src/NpFactory.cpp | 2 +- src/PhysX/physx/source/physx/src/NpFactory.h | 2 +- .../physx/source/physx/src/NpMaterial.cpp | 2 +- src/PhysX/physx/source/physx/src/NpMaterial.h | 2 +- .../source/physx/src/NpMaterialManager.h | 2 +- .../physx/source/physx/src/NpMetaData.cpp | 2 +- .../physx/source/physx/src/NpPhysics.cpp | 2 +- src/PhysX/physx/source/physx/src/NpPhysics.h | 2 +- .../physx/src/NpPhysicsInsertionCallback.h | 2 +- .../physx/src/NpPtrTableStorageManager.h | 2 +- .../physx/src/NpPvdSceneQueryCollector.cpp | 2 +- .../physx/src/NpPvdSceneQueryCollector.h | 2 +- .../physx/source/physx/src/NpQueryShared.h | 2 +- .../physx/source/physx/src/NpReadCheck.cpp | 2 +- .../physx/source/physx/src/NpReadCheck.h | 2 +- .../source/physx/src/NpRigidActorTemplate.h | 2 +- .../physx/src/NpRigidActorTemplateInternal.h | 2 +- .../source/physx/src/NpRigidBodyTemplate.h | 4 +- .../physx/source/physx/src/NpRigidDynamic.cpp | 29 +- .../physx/source/physx/src/NpRigidDynamic.h | 3 +- .../physx/source/physx/src/NpRigidStatic.cpp | 16 +- .../physx/source/physx/src/NpRigidStatic.h | 3 +- src/PhysX/physx/source/physx/src/NpScene.cpp | 104 +- src/PhysX/physx/source/physx/src/NpScene.h | 26 +- .../physx/source/physx/src/NpSceneAccessor.h | 2 +- .../physx/source/physx/src/NpSceneQueries.cpp | 2 +- .../physx/source/physx/src/NpSceneQueries.h | 2 +- .../source/physx/src/NpSerializerAdapter.cpp | 35 +- src/PhysX/physx/source/physx/src/NpShape.cpp | 2 +- src/PhysX/physx/source/physx/src/NpShape.h | 2 +- .../physx/source/physx/src/NpShapeManager.cpp | 2 +- .../physx/source/physx/src/NpShapeManager.h | 2 +- .../physx/source/physx/src/NpWriteCheck.cpp | 2 +- .../physx/source/physx/src/NpWriteCheck.h | 2 +- .../source/physx/src/PvdMetaDataBindingData.h | 2 +- .../physx/src/PvdMetaDataPvdBinding.cpp | 2 +- .../source/physx/src/PvdMetaDataPvdBinding.h | 2 +- .../source/physx/src/PvdPhysicsClient.cpp | 2 +- .../physx/source/physx/src/PvdPhysicsClient.h | 2 +- .../physx/source/physx/src/PvdTypeNames.h | 2 +- .../source/physx/src/buffering/ScbActor.cpp | 2 +- .../source/physx/src/buffering/ScbActor.h | 2 +- .../physx/src/buffering/ScbAggregate.cpp | 2 +- .../source/physx/src/buffering/ScbAggregate.h | 2 +- .../physx/src/buffering/ScbArticulation.h | 2 +- .../src/buffering/ScbArticulationJoint.h | 2 +- .../source/physx/src/buffering/ScbBase.cpp | 2 +- .../source/physx/src/buffering/ScbBase.h | 2 +- .../source/physx/src/buffering/ScbBody.h | 2 +- .../physx/src/buffering/ScbConstraint.h | 2 +- .../source/physx/src/buffering/ScbDefs.h | 2 +- .../physx/src/buffering/ScbMetaData.cpp | 2 +- .../source/physx/src/buffering/ScbNpDeps.h | 2 +- .../physx/src/buffering/ScbRigidObject.h | 2 +- .../physx/src/buffering/ScbRigidStatic.h | 2 +- .../source/physx/src/buffering/ScbScene.cpp | 2 +- .../source/physx/src/buffering/ScbScene.h | 2 +- .../physx/src/buffering/ScbSceneBuffer.h | 2 +- .../physx/src/buffering/ScbScenePvdClient.cpp | 2 +- .../physx/src/buffering/ScbScenePvdClient.h | 2 +- .../source/physx/src/buffering/ScbShape.cpp | 2 +- .../source/physx/src/buffering/ScbShape.h | 2 +- .../source/physx/src/buffering/ScbType.h | 2 +- .../source/physx/src/device/PhysXIndicator.h | 2 +- .../src/device/linux/PhysXIndicatorLinux.cpp | 2 +- .../source/physx/src/device/nvPhysXtoDrv.h | 2 +- .../device/windows/PhysXIndicatorWindows.cpp | 2 +- .../physx/source/physx/src/gpu/PxGpu.cpp | 2 +- .../physx/src/gpu/PxPhysXGpuModuleLoader.cpp | 2 +- .../src/windows/NpWindowsDelayLoadHook.cpp | 2 +- .../src/CctBoxController.cpp | 2 +- .../src/CctBoxController.h | 2 +- .../src/CctCapsuleController.cpp | 2 +- .../src/CctCapsuleController.h | 2 +- .../src/CctCharacterController.cpp | 2 +- .../src/CctCharacterController.h | 2 +- .../src/CctCharacterControllerCallbacks.cpp | 2 +- .../src/CctCharacterControllerManager.cpp | 51 +- .../src/CctCharacterControllerManager.h | 2 +- .../src/CctController.cpp | 2 +- .../src/CctController.h | 2 +- .../src/CctInternalStructs.h | 2 +- .../src/CctObstacleContext.cpp | 2 +- .../src/CctObstacleContext.h | 2 +- .../src/CctSweptBox.cpp | 2 +- .../physxcharacterkinematic/src/CctSweptBox.h | 2 +- .../src/CctSweptCapsule.cpp | 2 +- .../src/CctSweptCapsule.h | 2 +- .../src/CctSweptVolume.cpp | 2 +- .../src/CctSweptVolume.h | 2 +- .../physxcharacterkinematic/src/CctUtils.h | 2 +- .../source/physxcooking/src/Adjacencies.cpp | 2 +- .../source/physxcooking/src/Adjacencies.h | 2 +- .../physxcooking/src/BVHStructureBuilder.cpp | 2 +- .../physxcooking/src/BVHStructureBuilder.h | 2 +- .../physx/source/physxcooking/src/Cooking.cpp | 2 +- .../physx/source/physxcooking/src/Cooking.h | 2 +- .../source/physxcooking/src/CookingUtils.cpp | 2 +- .../source/physxcooking/src/CookingUtils.h | 2 +- .../source/physxcooking/src/EdgeList.cpp | 2 +- .../physx/source/physxcooking/src/EdgeList.h | 2 +- .../source/physxcooking/src/MeshCleaner.cpp | 2 +- .../source/physxcooking/src/MeshCleaner.h | 2 +- .../source/physxcooking/src/Quantizer.cpp | 2 +- .../physx/source/physxcooking/src/Quantizer.h | 2 +- .../src/convex/BigConvexDataBuilder.cpp | 2 +- .../src/convex/BigConvexDataBuilder.h | 2 +- .../src/convex/ConvexHullBuilder.cpp | 2 +- .../src/convex/ConvexHullBuilder.h | 2 +- .../physxcooking/src/convex/ConvexHullLib.cpp | 2 +- .../physxcooking/src/convex/ConvexHullLib.h | 2 +- .../src/convex/ConvexHullUtils.cpp | 2 +- .../physxcooking/src/convex/ConvexHullUtils.h | 2 +- .../src/convex/ConvexMeshBuilder.cpp | 2 +- .../src/convex/ConvexMeshBuilder.h | 2 +- .../src/convex/ConvexPolygonsBuilder.cpp | 2 +- .../src/convex/ConvexPolygonsBuilder.h | 2 +- .../src/convex/QuickHullConvexHullLib.cpp | 2 +- .../src/convex/QuickHullConvexHullLib.h | 2 +- .../src/convex/VolumeIntegration.cpp | 2 +- .../src/convex/VolumeIntegration.h | 2 +- .../src/mesh/GrbTriangleMeshCooking.cpp | 2 +- .../src/mesh/GrbTriangleMeshCooking.h | 2 +- .../src/mesh/HeightFieldCooking.cpp | 2 +- .../src/mesh/HeightFieldCooking.h | 2 +- .../physxcooking/src/mesh/MeshBuilder.cpp | 2 +- .../physxcooking/src/mesh/MeshBuilder.h | 2 +- .../physxcooking/src/mesh/QuickSelect.h | 2 +- .../physxcooking/src/mesh/RTreeCooking.cpp | 2 +- .../physxcooking/src/mesh/RTreeCooking.h | 2 +- .../src/mesh/TriangleMeshBuilder.cpp | 2 +- .../src/mesh/TriangleMeshBuilder.h | 2 +- .../windows/WindowsCookingDelayLoadHook.cpp | 2 +- .../physxextensions/src/ExtBroadPhase.cpp | 2 +- .../physxextensions/src/ExtCollection.cpp | 2 +- .../physxextensions/src/ExtConstraintHelper.h | 2 +- .../physxextensions/src/ExtContactJoint.cpp | 2 +- .../physxextensions/src/ExtContactJoint.h | 2 +- .../physxextensions/src/ExtConvexMeshExt.cpp | 2 +- .../src/ExtCpuWorkerThread.cpp | 2 +- .../physxextensions/src/ExtCpuWorkerThread.h | 2 +- .../source/physxextensions/src/ExtD6Joint.cpp | 2 +- .../source/physxextensions/src/ExtD6Joint.h | 2 +- .../physxextensions/src/ExtD6JointCreate.cpp | 2 +- .../src/ExtDefaultCpuDispatcher.cpp | 2 +- .../src/ExtDefaultCpuDispatcher.h | 2 +- .../src/ExtDefaultErrorCallback.cpp | 2 +- .../src/ExtDefaultSimulationFilterShader.cpp | 2 +- .../physxextensions/src/ExtDefaultStreams.cpp | 2 +- .../physxextensions/src/ExtDistanceJoint.cpp | 2 +- .../physxextensions/src/ExtDistanceJoint.h | 2 +- .../physxextensions/src/ExtExtensions.cpp | 2 +- .../physxextensions/src/ExtFixedJoint.cpp | 2 +- .../physxextensions/src/ExtFixedJoint.h | 2 +- .../physxextensions/src/ExtInertiaTensor.h | 2 +- .../source/physxextensions/src/ExtJoint.cpp | 2 +- .../source/physxextensions/src/ExtJoint.h | 2 +- .../src/ExtJointMetaDataExtensions.h | 2 +- .../physxextensions/src/ExtMetaData.cpp | 2 +- .../source/physxextensions/src/ExtPlatform.h | 2 +- .../physxextensions/src/ExtPrismaticJoint.cpp | 2 +- .../physxextensions/src/ExtPrismaticJoint.h | 2 +- .../source/physxextensions/src/ExtPvd.cpp | 2 +- .../physx/source/physxextensions/src/ExtPvd.h | 2 +- .../physxextensions/src/ExtPxStringTable.cpp | 2 +- .../physxextensions/src/ExtRaycastCCD.cpp | 2 +- .../physxextensions/src/ExtRevoluteJoint.cpp | 2 +- .../physxextensions/src/ExtRevoluteJoint.h | 2 +- .../physxextensions/src/ExtRigidActorExt.cpp | 2 +- .../physxextensions/src/ExtRigidBodyExt.cpp | 2 +- .../physxextensions/src/ExtSceneQueryExt.cpp | 2 +- .../physxextensions/src/ExtSerialization.h | 2 +- .../src/ExtSharedQueueEntryPool.h | 2 +- .../physxextensions/src/ExtSimpleFactory.cpp | 2 +- .../physxextensions/src/ExtSmoothNormals.cpp | 2 +- .../physxextensions/src/ExtSphericalJoint.cpp | 2 +- .../physxextensions/src/ExtSphericalJoint.h | 2 +- .../physxextensions/src/ExtTaskQueueHelper.h | 2 +- .../src/ExtTriangleMeshExt.cpp | 2 +- .../Binary/SnBinaryDeserialization.cpp | 2 +- .../Binary/SnBinarySerialization.cpp | 2 +- .../src/serialization/Binary/SnConvX.cpp | 2 +- .../src/serialization/Binary/SnConvX.h | 2 +- .../serialization/Binary/SnConvX_Align.cpp | 2 +- .../src/serialization/Binary/SnConvX_Align.h | 2 +- .../src/serialization/Binary/SnConvX_Common.h | 2 +- .../serialization/Binary/SnConvX_Convert.cpp | 6 +- .../serialization/Binary/SnConvX_Error.cpp | 2 +- .../serialization/Binary/SnConvX_MetaData.cpp | 2 +- .../serialization/Binary/SnConvX_MetaData.h | 2 +- .../serialization/Binary/SnConvX_Output.cpp | 2 +- .../src/serialization/Binary/SnConvX_Output.h | 2 +- .../serialization/Binary/SnConvX_Union.cpp | 2 +- .../src/serialization/Binary/SnConvX_Union.h | 2 +- .../Binary/SnSerializationContext.cpp | 2 +- .../Binary/SnSerializationContext.h | 2 +- .../src/serialization/File/SnFile.h | 2 +- .../src/serialization/SnSerialUtils.cpp | 20 +- .../src/serialization/SnSerialUtils.h | 2 +- .../src/serialization/SnSerialization.cpp | 2 +- .../serialization/SnSerializationRegistry.cpp | 2 +- .../serialization/SnSerializationRegistry.h | 2 +- .../Xml/SnJointRepXSerializer.cpp | 2 +- .../serialization/Xml/SnJointRepXSerializer.h | 2 +- .../serialization/Xml/SnPxStreamOperators.h | 2 +- .../src/serialization/Xml/SnRepX1_0Defaults.h | 2 +- .../src/serialization/Xml/SnRepX3_1Defaults.h | 2 +- .../src/serialization/Xml/SnRepX3_2Defaults.h | 2 +- .../src/serialization/Xml/SnRepXCollection.h | 2 +- .../Xml/SnRepXCoreSerializer.cpp | 2 +- .../serialization/Xml/SnRepXCoreSerializer.h | 2 +- .../serialization/Xml/SnRepXSerializerImpl.h | 2 +- .../src/serialization/Xml/SnRepXUpgrader.cpp | 2 +- .../src/serialization/Xml/SnRepXUpgrader.h | 2 +- .../src/serialization/Xml/SnSimpleXmlWriter.h | 2 +- .../src/serialization/Xml/SnXmlDeserializer.h | 2 +- .../src/serialization/Xml/SnXmlImpl.h | 2 +- .../serialization/Xml/SnXmlMemoryAllocator.h | 2 +- .../src/serialization/Xml/SnXmlMemoryPool.h | 2 +- .../Xml/SnXmlMemoryPoolStreams.h | 2 +- .../src/serialization/Xml/SnXmlReader.h | 2 +- .../serialization/Xml/SnXmlSerialization.cpp | 4 +- .../src/serialization/Xml/SnXmlSerializer.h | 2 +- .../serialization/Xml/SnXmlSimpleXmlWriter.h | 2 +- .../src/serialization/Xml/SnXmlStringToType.h | 2 +- .../serialization/Xml/SnXmlVisitorReader.h | 2 +- .../serialization/Xml/SnXmlVisitorWriter.h | 2 +- .../src/serialization/Xml/SnXmlWriter.h | 2 +- .../source/physxgpu/include/PxPhysXGpu.h | 2 +- .../include/PvdMetaDataDefineProperties.h | 2 +- .../core/include/PvdMetaDataExtensions.h | 2 +- .../core/include/PvdMetaDataPropertyVisitor.h | 2 +- .../PxAutoGeneratedMetaDataObjectNames.h | 3 +- .../include/PxAutoGeneratedMetaDataObjects.h | 21 +- .../core/include/PxMetaDataCompare.h | 2 +- .../core/include/PxMetaDataCppPrefix.h | 2 +- .../core/include/PxMetaDataObjects.h | 2 +- .../include/RepXMetaDataPropertyVisitor.h | 2 +- .../src/PxAutoGeneratedMetaDataObjects.cpp | 6 +- .../core/src/PxMetaDataObjects.cpp | 2 +- ...xtensionAutoGeneratedMetaDataObjectNames.h | 2 +- .../PxExtensionAutoGeneratedMetaDataObjects.h | 2 +- .../include/PxExtensionMetaDataObjects.h | 2 +- ...xExtensionAutoGeneratedMetaDataObjects.cpp | 2 +- .../physxvehicle/src/PxVehicleComponents.cpp | 2 +- .../physxvehicle/src/PxVehicleDefaults.h | 2 +- .../physxvehicle/src/PxVehicleDrive.cpp | 2 +- .../physxvehicle/src/PxVehicleDrive4W.cpp | 2 +- .../physxvehicle/src/PxVehicleDriveNW.cpp | 2 +- .../physxvehicle/src/PxVehicleDriveTank.cpp | 2 +- .../physxvehicle/src/PxVehicleLinearMath.h | 2 +- .../physxvehicle/src/PxVehicleMetaData.cpp | 2 +- .../physxvehicle/src/PxVehicleNoDrive.cpp | 2 +- .../source/physxvehicle/src/PxVehicleSDK.cpp | 2 +- .../src/PxVehicleSerialization.cpp | 2 +- .../physxvehicle/src/PxVehicleSerialization.h | 2 +- .../src/PxVehicleSuspLimitConstraintShader.h | 2 +- .../src/PxVehicleSuspWheelTire4.cpp | 2 +- .../src/PxVehicleSuspWheelTire4.h | 2 +- .../src/PxVehicleTireFriction.cpp | 2 +- .../physxvehicle/src/PxVehicleUpdate.cpp | 2 +- .../physxvehicle/src/PxVehicleWheels.cpp | 2 +- .../physxvehicle/src/VehicleUtilControl.cpp | 2 +- .../physxvehicle/src/VehicleUtilSetup.cpp | 2 +- .../physxvehicle/src/VehicleUtilTelemetry.cpp | 2 +- ...xVehicleAutoGeneratedMetaDataObjectNames.h | 2 +- .../PxVehicleAutoGeneratedMetaDataObjects.h | 2 +- .../include/PxVehicleMetaDataObjects.h | 2 +- .../PxVehicleAutoGeneratedMetaDataObjects.cpp | 2 +- .../src/PxVehicleMetaDataObjects.cpp | 2 +- src/PhysX/physx/source/pvd/include/PsPvd.h | 2 +- .../pvd/include/PxProfileAllocatorWrapper.h | 2 +- .../physx/source/pvd/include/PxPvdClient.h | 2 +- .../source/pvd/include/PxPvdDataStream.h | 2 +- .../pvd/include/PxPvdDataStreamHelpers.h | 2 +- .../source/pvd/include/PxPvdErrorCodes.h | 2 +- .../pvd/include/PxPvdObjectModelBaseTypes.h | 2 +- .../source/pvd/include/PxPvdRenderBuffer.h | 2 +- .../source/pvd/include/PxPvdUserRenderer.h | 2 +- .../source/pvd/src/PxProfileContextProvider.h | 2 +- .../pvd/src/PxProfileContextProviderImpl.h | 2 +- .../source/pvd/src/PxProfileDataBuffer.h | 2 +- .../source/pvd/src/PxProfileDataParsing.h | 2 +- .../source/pvd/src/PxProfileEventBuffer.h | 2 +- .../pvd/src/PxProfileEventBufferAtomic.h | 2 +- .../pvd/src/PxProfileEventBufferClient.h | 2 +- .../src/PxProfileEventBufferClientManager.h | 2 +- .../physx/source/pvd/src/PxProfileEventId.h | 2 +- .../source/pvd/src/PxProfileEventImpl.cpp | 2 +- .../source/pvd/src/PxProfileEventMutex.h | 2 +- .../source/pvd/src/PxProfileEventNames.h | 2 +- .../source/pvd/src/PxProfileEventSender.h | 2 +- .../pvd/src/PxProfileEventSerialization.h | 2 +- .../physx/source/pvd/src/PxProfileEvents.h | 2 +- .../physx/source/pvd/src/PxProfileMemory.h | 2 +- .../source/pvd/src/PxProfileMemoryBuffer.h | 2 +- .../pvd/src/PxProfileMemoryEventBuffer.h | 2 +- .../source/pvd/src/PxProfileMemoryEvents.h | 2 +- .../source/pvd/src/PxProfileScopedEvent.h | 2 +- .../source/pvd/src/PxProfileScopedMutexLock.h | 2 +- .../physx/source/pvd/src/PxProfileZoneImpl.h | 2 +- .../source/pvd/src/PxProfileZoneManager.h | 2 +- .../source/pvd/src/PxProfileZoneManagerImpl.h | 2 +- src/PhysX/physx/source/pvd/src/PxPvd.cpp | 2 +- src/PhysX/physx/source/pvd/src/PxPvdBits.h | 2 +- .../physx/source/pvd/src/PxPvdByteStreams.h | 2 +- .../source/pvd/src/PxPvdCommStreamEventSink.h | 2 +- .../source/pvd/src/PxPvdCommStreamEvents.h | 2 +- .../source/pvd/src/PxPvdCommStreamTypes.h | 2 +- .../physx/source/pvd/src/PxPvdDataStream.cpp | 3 +- .../pvd/src/PxPvdDefaultFileTransport.cpp | 2 +- .../pvd/src/PxPvdDefaultFileTransport.h | 2 +- .../pvd/src/PxPvdDefaultSocketTransport.cpp | 2 +- .../pvd/src/PxPvdDefaultSocketTransport.h | 2 +- .../physx/source/pvd/src/PxPvdFoundation.h | 2 +- src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp | 2 +- src/PhysX/physx/source/pvd/src/PxPvdImpl.h | 2 +- .../source/pvd/src/PxPvdInternalByteStreams.h | 2 +- .../physx/source/pvd/src/PxPvdMarshalling.h | 2 +- .../physx/source/pvd/src/PxPvdMemClient.cpp | 2 +- .../physx/source/pvd/src/PxPvdMemClient.h | 2 +- .../src/PxPvdObjectModelInternalTypeDefs.h | 2 +- .../pvd/src/PxPvdObjectModelInternalTypes.h | 2 +- .../pvd/src/PxPvdObjectModelMetaData.cpp | 2 +- .../source/pvd/src/PxPvdObjectModelMetaData.h | 2 +- .../source/pvd/src/PxPvdObjectRegistrar.cpp | 2 +- .../source/pvd/src/PxPvdObjectRegistrar.h | 2 +- .../physx/source/pvd/src/PxPvdProfileZone.h | 2 +- .../source/pvd/src/PxPvdProfileZoneClient.cpp | 2 +- .../source/pvd/src/PxPvdProfileZoneClient.h | 2 +- .../source/pvd/src/PxPvdUserRenderImpl.h | 2 +- .../source/pvd/src/PxPvdUserRenderTypes.h | 2 +- .../source/pvd/src/PxPvdUserRenderer.cpp | 2 +- .../source/scenequery/include/SqPruner.h | 2 +- .../scenequery/include/SqPrunerMergeData.h | 2 +- .../scenequery/include/SqPruningStructure.h | 2 +- .../scenequery/include/SqSceneQueryManager.h | 2 +- .../source/scenequery/src/SqAABBPruner.cpp | 2 +- .../source/scenequery/src/SqAABBPruner.h | 2 +- .../source/scenequery/src/SqAABBTree.cpp | 2 +- .../physx/source/scenequery/src/SqAABBTree.h | 2 +- .../scenequery/src/SqAABBTreeUpdateMap.cpp | 2 +- .../scenequery/src/SqAABBTreeUpdateMap.h | 2 +- .../physx/source/scenequery/src/SqBounds.cpp | 2 +- .../physx/source/scenequery/src/SqBounds.h | 2 +- .../source/scenequery/src/SqBucketPruner.cpp | 2 +- .../source/scenequery/src/SqBucketPruner.h | 2 +- .../scenequery/src/SqCompoundPruner.cpp | 2 +- .../source/scenequery/src/SqCompoundPruner.h | 2 +- .../scenequery/src/SqCompoundPruningPool.cpp | 2 +- .../scenequery/src/SqCompoundPruningPool.h | 2 +- .../scenequery/src/SqExtendedBucketPruner.cpp | 2 +- .../scenequery/src/SqExtendedBucketPruner.h | 2 +- .../src/SqIncrementalAABBPruner.cpp | 2 +- .../scenequery/src/SqIncrementalAABBPruner.h | 2 +- .../src/SqIncrementalAABBPrunerCore.cpp | 2 +- .../src/SqIncrementalAABBPrunerCore.h | 2 +- .../scenequery/src/SqIncrementalAABBTree.cpp | 2 +- .../scenequery/src/SqIncrementalAABBTree.h | 2 +- .../source/scenequery/src/SqMetaData.cpp | 2 +- .../source/scenequery/src/SqPruningPool.cpp | 2 +- .../source/scenequery/src/SqPruningPool.h | 2 +- .../scenequery/src/SqPruningStructure.cpp | 2 +- .../scenequery/src/SqSceneQueryManager.cpp | 2 +- .../physx/source/scenequery/src/SqTypedef.h | 2 +- .../include/ScActorCore.h | 2 +- .../include/ScArticulationCore.h | 67 +- .../include/ScArticulationJointCore.h | 2 +- .../simulationcontroller/include/ScBodyCore.h | 6 +- .../include/ScConstraintCore.h | 2 +- .../include/ScIterators.h | 2 +- .../include/ScMaterialCore.h | 2 +- .../simulationcontroller/include/ScPhysics.h | 2 +- .../include/ScRigidCore.h | 2 +- .../simulationcontroller/include/ScScene.h | 7 +- .../include/ScShapeCore.h | 2 +- .../include/ScStaticCore.h | 2 +- .../simulationcontroller/src/ScActorCore.cpp | 2 +- .../simulationcontroller/src/ScActorPair.h | 2 +- .../simulationcontroller/src/ScActorSim.cpp | 2 +- .../simulationcontroller/src/ScActorSim.h | 2 +- .../src/ScArticulationCore.cpp | 96 +- .../src/ScArticulationJointCore.cpp | 2 +- .../src/ScArticulationJointSim.cpp | 2 +- .../src/ScArticulationJointSim.h | 2 +- .../src/ScArticulationSim.cpp | 61 +- .../src/ScArticulationSim.h | 2 +- .../simulationcontroller/src/ScBodyCore.cpp | 26 +- .../simulationcontroller/src/ScBodySim.cpp | 2 +- .../simulationcontroller/src/ScBodySim.h | 2 +- .../simulationcontroller/src/ScClient.h | 2 +- .../src/ScConstraintCore.cpp | 2 +- .../src/ScConstraintGroupNode.cpp | 2 +- .../src/ScConstraintGroupNode.h | 2 +- .../src/ScConstraintInteraction.cpp | 2 +- .../src/ScConstraintInteraction.h | 2 +- .../src/ScConstraintProjectionManager.cpp | 2 +- .../src/ScConstraintProjectionManager.h | 2 +- .../src/ScConstraintProjectionTree.cpp | 2 +- .../src/ScConstraintProjectionTree.h | 2 +- .../src/ScConstraintSim.cpp | 2 +- .../src/ScConstraintSim.h | 2 +- .../src/ScContactReportBuffer.h | 2 +- .../src/ScContactStream.h | 2 +- .../src/ScElementInteractionMarker.cpp | 2 +- .../src/ScElementInteractionMarker.h | 2 +- .../simulationcontroller/src/ScElementSim.cpp | 2 +- .../simulationcontroller/src/ScElementSim.h | 2 +- .../src/ScElementSimInteraction.h | 2 +- .../src/ScInteraction.cpp | 2 +- .../simulationcontroller/src/ScInteraction.h | 2 +- .../src/ScInteractionFlags.h | 2 +- .../simulationcontroller/src/ScIterators.cpp | 2 +- .../simulationcontroller/src/ScMetaData.cpp | 2 +- .../simulationcontroller/src/ScNPhaseCore.cpp | 2 +- .../simulationcontroller/src/ScNPhaseCore.h | 2 +- .../src/ScObjectIDTracker.h | 2 +- .../simulationcontroller/src/ScPhysics.cpp | 2 +- .../simulationcontroller/src/ScRigidCore.cpp | 2 +- .../simulationcontroller/src/ScRigidSim.cpp | 2 +- .../simulationcontroller/src/ScRigidSim.h | 2 +- .../simulationcontroller/src/ScScene.cpp | 279 ++-- .../simulationcontroller/src/ScShapeCore.cpp | 2 +- .../src/ScShapeInteraction.cpp | 11 +- .../src/ScShapeInteraction.h | 2 +- .../simulationcontroller/src/ScShapeSim.cpp | 2 +- .../simulationcontroller/src/ScShapeSim.h | 2 +- .../simulationcontroller/src/ScSimStateData.h | 2 +- .../simulationcontroller/src/ScSimStats.cpp | 2 +- .../simulationcontroller/src/ScSimStats.h | 2 +- .../src/ScSimulationController.cpp | 2 +- .../src/ScSimulationController.h | 2 +- .../src/ScSqBoundsManager.cpp | 2 +- .../src/ScSqBoundsManager.h | 2 +- .../simulationcontroller/src/ScStaticCore.cpp | 2 +- .../simulationcontroller/src/ScStaticSim.h | 2 +- .../src/ScTriggerInteraction.cpp | 2 +- .../src/ScTriggerInteraction.h | 2 +- .../simulationcontroller/src/ScTriggerPairs.h | 2 +- .../physx/source/task/src/TaskManager.cpp | 2 +- .../physxmetadatagenerator/PxBoilerPlate.h | 2 +- .../PxExtensionsCommon.h | 2 +- .../PxPhysicsWithExtensionsAPI.h | 2 +- .../PxVehicleExtensionAPI.h | 2 +- .../generateMetaData.bat | 2 +- .../generateMetaData.py | 2 +- .../generateMetaData.sh | 2 +- .../tools/physxmetadatagenerator/helper.sh | 2 +- .../physxmetadatagenerator/lib/compare.py | 2 +- .../tools/physxmetadatagenerator/lib/utils.py | 2 +- .../tools/physxmetadatagenerator/readme.txt | 2 +- src/PhysX/pxshared/include/foundation/Px.h | 2 +- .../include/foundation/PxAllocatorCallback.h | 2 +- .../include/foundation/PxBitAndData.h | 2 +- .../pxshared/include/foundation/PxBounds3.h | 42 +- .../include/foundation/PxErrorCallback.h | 2 +- .../pxshared/include/foundation/PxErrors.h | 2 +- .../pxshared/include/foundation/PxFlags.h | 2 +- src/PhysX/pxshared/include/foundation/PxIO.h | 2 +- .../include/foundation/PxIntrinsics.h | 2 +- .../pxshared/include/foundation/PxMat33.h | 2 +- .../pxshared/include/foundation/PxMat44.h | 4 +- .../pxshared/include/foundation/PxMath.h | 6 +- .../pxshared/include/foundation/PxMemory.h | 2 +- .../pxshared/include/foundation/PxPlane.h | 2 +- .../include/foundation/PxPreprocessor.h | 27 +- .../pxshared/include/foundation/PxProfiler.h | 2 +- .../pxshared/include/foundation/PxQuat.h | 4 +- .../include/foundation/PxSharedAssert.h | 46 + .../include/foundation/PxSimpleTypes.h | 2 +- .../include/foundation/PxStrideIterator.h | 22 +- .../pxshared/include/foundation/PxTransform.h | 26 +- .../pxshared/include/foundation/PxUnionCast.h | 2 +- .../pxshared/include/foundation/PxVec2.h | 6 +- .../pxshared/include/foundation/PxVec3.h | 8 +- .../pxshared/include/foundation/PxVec4.h | 8 +- .../foundation/unix/PxUnixIntrinsics.h | 6 +- .../foundation/windows/PxWindowsIntrinsics.h | 6 +- 1339 files changed, 3885 insertions(+), 3847 deletions(-) create mode 100644 src/PhysX/physx/include/foundation/PxAssert.h create mode 100644 src/PhysX/physx/include/foundation/PxFoundationConfig.h create mode 100644 src/PhysX/physx/include/foundation/PxMathUtils.h create mode 100644 src/PhysX/pxshared/include/foundation/PxSharedAssert.h diff --git a/build_visual_studio_vr_pybullet_double.bat b/build_visual_studio_vr_pybullet_double.bat index e3e8a4dc5..f8b2184b9 100644 --- a/build_visual_studio_vr_pybullet_double.bat +++ b/build_visual_studio_vr_pybullet_double.bat @@ -18,7 +18,7 @@ rem SET myvar=c:\python-3.5.2 cd build3 -premake4 --double --enable_multithreading --midi --enable_static_vr_plugin --enable_openvr --enable_pybullet --python_include_dir="%myvar%/include" --python_lib_dir="%myvar%/libs" --targetdir="../bin" vs2010 +premake4 --enable_physx --double --enable_multithreading --midi --enable_static_vr_plugin --enable_openvr --enable_pybullet --python_include_dir="%myvar%/include" --python_lib_dir="%myvar%/libs" --targetdir="../bin" vs2010 rem premake4 --double --enable_multithreading --midi --enable_static_vr_plugin --enable_openvr --enable_pybullet --python_include_dir="%myvar%/include" --python_lib_dir="%myvar%/libs" --targetdir="../binserver" vs2010 rem premake4 --double --enable_grpc --enable_multithreading --midi --enable_static_vr_plugin --enable_openvr --enable_pybullet --python_include_dir="%myvar%/include" --python_lib_dir="%myvar%/libs" --targetdir="../binserver" vs2010 diff --git a/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp b/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp index c3a287f0d..ce57e23ee 100644 --- a/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp +++ b/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp @@ -1057,7 +1057,7 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman int sz = sizeof(SharedMemoryStatus); int sz2 = sizeof(SharedMemoryCommand); - bool hasStatus = false; + bool hasStatus = true; serverStatusOut.m_type = CMD_INVALID_STATUS; serverStatusOut.m_numDataStreamBytes = 0; @@ -1111,15 +1111,6 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman break; } - default: - { - BT_PROFILE("CMD_UNKNOWN"); - printf("Unknown command encountered: %d", clientCmd.m_type); - SharedMemoryStatus& serverCmd = serverStatusOut; - serverCmd.m_type = CMD_UNKNOWN_COMMAND_FLUSHED; - hasStatus = true; - } - case CMD_LOAD_URDF: { hasStatus = processLoadURDFCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); @@ -1433,6 +1424,15 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman break; } #endif + + default: + { + BT_PROFILE("CMD_UNKNOWN"); + printf("Unknown command encountered: %d", clientCmd.m_type); + SharedMemoryStatus& serverCmd = serverStatusOut; + serverCmd.m_type = CMD_UNKNOWN_COMMAND_FLUSHED; + hasStatus = true; + } }; return hasStatus; diff --git a/src/PhysX/physx/include/PxActor.h b/src/PhysX/physx/include/PxActor.h index 6e3306ca9..b5d48b10e 100644 --- a/src/PhysX/physx/include/PxActor.h +++ b/src/PhysX/physx/include/PxActor.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxAggregate.h b/src/PhysX/physx/include/PxAggregate.h index 3c0b97700..ad9307ea0 100644 --- a/src/PhysX/physx/include/PxAggregate.h +++ b/src/PhysX/physx/include/PxAggregate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxArticulation.h b/src/PhysX/physx/include/PxArticulation.h index fd4bc3e82..a3727ec08 100644 --- a/src/PhysX/physx/include/PxArticulation.h +++ b/src/PhysX/physx/include/PxArticulation.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxArticulationBase.h b/src/PhysX/physx/include/PxArticulationBase.h index 596ee4c5a..ac9fa8712 100644 --- a/src/PhysX/physx/include/PxArticulationBase.h +++ b/src/PhysX/physx/include/PxArticulationBase.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -41,7 +41,6 @@ namespace physx { #endif - class PxArticulationImpl; /** @@ -241,15 +240,12 @@ namespace physx @see PxArticulationLink */ - - virtual PxArticulationLink* createLink(PxArticulationLink* parent, const PxTransform& pose) = 0; - + virtual PxArticulationLink* createLink(PxArticulationLink* parent, const PxTransform& pose) = 0; /** \brief returns the number of links in the articulation */ - - virtual PxU32 getNbLinks() const = 0; + virtual PxU32 getNbLinks() const = 0; /** \brief returns the set of links in the articulation @@ -263,9 +259,7 @@ namespace physx @see ArticulationLink */ - - virtual PxU32 getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex = 0) const = 0; - + virtual PxU32 getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex = 0) const = 0; /** \brief Sets a name string for the object that can be retrieved with getName(). @@ -277,7 +271,7 @@ namespace physx @see getName() */ - virtual void setName(const char* name) = 0; + virtual void setName(const char* name) = 0; /** \brief Retrieves the name string set with setName(). @@ -286,7 +280,7 @@ namespace physx @see setName() */ - virtual const char* getName() const = 0; + virtual const char* getName() const = 0; /** \brief Retrieves the axis aligned bounding box enclosing the articulation. @@ -297,7 +291,7 @@ namespace physx @see PxBounds3 */ - virtual PxBounds3 getWorldBounds(float inflation = 1.01f) const = 0; + virtual PxBounds3 getWorldBounds(float inflation = 1.01f) const = 0; /** \brief Retrieves the aggregate the articulation might be a part of. @@ -306,12 +300,10 @@ namespace physx @see PxAggregate */ - virtual PxAggregate* getAggregate() const = 0; - - virtual PxArticulationImpl* getImpl() = 0; - - virtual const PxArticulationImpl* getImpl() const = 0; + virtual PxAggregate* getAggregate() const = 0; + virtual PxArticulationImpl* getImpl() = 0; + virtual const PxArticulationImpl* getImpl() const = 0; virtual PxArticulationBase::Enum getType() const = 0; void* userData; //!< user can assign this to whatever, usually to create a 1:1 relationship with a user object. @@ -319,13 +311,10 @@ namespace physx virtual ~PxArticulationBase() {} protected: - PX_INLINE PxArticulationBase(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationBase(PxType concreteType, PxBaseFlags baseFlags) : PxBase(concreteType, baseFlags), userData(NULL) {} PX_INLINE PxArticulationBase(PxBaseFlags baseFlags) : PxBase(baseFlags) {} public: - virtual PxArticulationJointBase* createArticulationJoint(PxArticulationLink& parent, - const PxTransform& parentFrame, - PxArticulationLink& child, - const PxTransform& childFrame) = 0; + virtual PxArticulationJointBase* createArticulationJoint(PxArticulationLink& parent, const PxTransform& parentFrame, PxArticulationLink& child, const PxTransform& childFrame) = 0; virtual void releaseArticulationJoint(PxArticulationJointBase* joint) = 0; }; diff --git a/src/PhysX/physx/include/PxArticulationJoint.h b/src/PhysX/physx/include/PxArticulationJoint.h index 295c19fbe..c51a3c1fa 100644 --- a/src/PhysX/physx/include/PxArticulationJoint.h +++ b/src/PhysX/physx/include/PxArticulationJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -106,7 +106,6 @@ struct PxArticulationJointType }; }; - class PxArticulationJointBase : public PxBase { public: @@ -125,7 +124,6 @@ public: @see getParentPose() */ - virtual void setParentPose(const PxTransform& pose) = 0; /** @@ -135,7 +133,6 @@ public: @see setParentPose() */ - virtual PxTransform getParentPose() const = 0; /** @@ -145,7 +142,6 @@ public: */ virtual PxArticulationLink& getChildArticulationLink() const = 0; - /** \brief set the joint pose in the child frame @@ -154,7 +150,6 @@ public: @see getChildPose() */ - virtual void setChildPose(const PxTransform& pose) = 0; /** @@ -205,7 +200,6 @@ public: @see getTargetOrientation() */ - virtual void setTargetOrientation(const PxQuat& orientation) = 0; /** @@ -238,7 +232,6 @@ public: */ virtual PxVec3 getTargetVelocity() const = 0; - /** \brief set the drive type @@ -259,8 +252,6 @@ public: virtual PxArticulationJointDriveType::Enum getDriveType() const = 0; - - /** \brief set the drive strength of the joint acceleration spring. @@ -285,7 +276,6 @@ public: */ virtual PxReal getStiffness() const = 0; - /** \brief set the damping of the joint acceleration spring @@ -306,7 +296,6 @@ public: @see setDamping() */ - virtual PxReal getDamping() const = 0; /** @@ -327,10 +316,8 @@ public: @see getInternalCompliance() */ - virtual void setInternalCompliance(PxReal compliance) = 0; - /** \brief get the internal compliance @@ -358,7 +345,6 @@ public: @see getExternalCompliance() */ - virtual void setExternalCompliance(PxReal compliance) = 0; /** @@ -370,8 +356,6 @@ public: */ virtual PxReal getExternalCompliance() const = 0; - - /** \brief set the extents of the cone limit. The extents are measured in the frame of the parent. @@ -387,7 +371,6 @@ public: */ virtual void setSwingLimit(PxReal zLimit, PxReal yLimit) = 0; - /** \brief get the extents for the swing limit cone @@ -400,17 +383,13 @@ public: */ virtual void getSwingLimit(PxReal& zLimit, PxReal& yLimit) const = 0; - - /** \brief set the tangential spring for the limit cone Range: ([0, PX_MAX_F32), [0, PX_MAX_F32)) Default: (0.0, 0.0) */ - virtual void setTangentialStiffness(PxReal spring) = 0; - /** \brief get the tangential spring for the swing limit cone @@ -420,16 +399,13 @@ public: */ virtual PxReal getTangentialStiffness() const = 0; - /** \brief set the tangential damping for the limit cone Range: ([0, PX_MAX_F32), [0, PX_MAX_F32)) Default: (0.0, 0.0) */ - virtual void setTangentialDamping(PxReal damping) = 0; - /** \brief get the tangential damping for the swing limit cone @@ -439,7 +415,6 @@ public: */ virtual PxReal getTangentialDamping() const = 0; - /** \brief set the contact distance for the swing limit @@ -450,10 +425,8 @@ public: @see getSwingLimitContactDistance() */ - virtual void setSwingLimitContactDistance(PxReal contactDistance) = 0; - /** \brief get the contact distance for the swing limit @@ -463,8 +436,6 @@ public: */ virtual PxReal getSwingLimitContactDistance() const = 0; - - /** \brief set the flag which enables the swing limit @@ -482,10 +453,8 @@ public: @see setSwingLimitEnabled() */ - virtual bool getSwingLimitEnabled() const = 0; - /** \brief set the bounds of the twistLimit @@ -508,7 +477,6 @@ public: @see setTwistLimit() */ - virtual void getTwistLimit(PxReal &lower, PxReal &upper) const = 0; /** @@ -528,10 +496,8 @@ public: @see setTwistLimitEnabled() */ - virtual bool getTwistLimitEnabled() const = 0; - /** \brief set the contact distance for the swing limit @@ -542,10 +508,8 @@ public: @see getTwistLimitContactDistance() */ - virtual void setTwistLimitContactDistance(PxReal contactDistance) = 0; - /** \brief get the contact distance for the swing limit @@ -555,13 +519,13 @@ public: */ virtual PxReal getTwistLimitContactDistance() const = 0; - virtual const char* getConcreteTypeName() const { return "PxArticulationJoint"; } + virtual const char* getConcreteTypeName() const { return "PxArticulationJoint"; } protected: PX_INLINE PxArticulationJoint(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationJointBase(concreteType, baseFlags) {} PX_INLINE PxArticulationJoint(PxBaseFlags baseFlags) : PxArticulationJointBase(baseFlags) {} virtual ~PxArticulationJoint() {} - virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationJoint", name) || PxArticulationJointBase::isKindOf(name); } + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationJoint", name) || PxArticulationJointBase::isKindOf(name); } }; #if !PX_DOXYGEN diff --git a/src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h b/src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h index 2397da8d5..f2fd59683 100644 --- a/src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h +++ b/src/PhysX/physx/include/PxArticulationJointReducedCoordinate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxArticulationLink.h b/src/PhysX/physx/include/PxArticulationLink.h index 9b3faae68..a6f1ab8d4 100644 --- a/src/PhysX/physx/include/PxArticulationLink.h +++ b/src/PhysX/physx/include/PxArticulationLink.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -66,8 +66,7 @@ public: @see PxArticulation::createLink() */ - virtual void release() = 0; - + virtual void release() = 0; /** \brief get the articulation to which this articulation link belongs. This returns the base class. The application should @@ -75,8 +74,7 @@ public: \return the articulation to which this link belongs */ - virtual PxArticulationBase& getArticulation() const = 0; - + virtual PxArticulationBase& getArticulation() const = 0; /** \brief Get the joint which connects this link to its parent. @@ -94,7 +92,7 @@ public: @see PxArticulationJoint */ - virtual PxU32 getInboundJointDof() const = 0; + virtual PxU32 getInboundJointDof() const = 0; /** \brief Get number of child links. @@ -103,15 +101,14 @@ public: @see getChildren() */ - virtual PxU32 getNbChildren() const = 0; - + virtual PxU32 getNbChildren() const = 0; /** \brief Get low-level link index \return low-level index */ - virtual PxU32 getLinkIndex() const = 0; + virtual PxU32 getLinkIndex() const = 0; /** \brief Retrieve all the child links. @@ -123,15 +120,15 @@ public: @see getNbChildren() */ - virtual PxU32 getChildren(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; + virtual PxU32 getChildren(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex=0) const = 0; - virtual const char* getConcreteTypeName() const { return "PxArticulationLink"; } + virtual const char* getConcreteTypeName() const { return "PxArticulationLink"; } protected: - PX_INLINE PxArticulationLink(PxType concreteType, PxBaseFlags baseFlags) : PxRigidBody(concreteType, baseFlags) {} - PX_INLINE PxArticulationLink(PxBaseFlags baseFlags) : PxRigidBody(baseFlags) {} - virtual ~PxArticulationLink() {} - virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationLink", name) || PxRigidBody::isKindOf(name); } + PX_INLINE PxArticulationLink(PxType concreteType, PxBaseFlags baseFlags) : PxRigidBody(concreteType, baseFlags) {} + PX_INLINE PxArticulationLink(PxBaseFlags baseFlags) : PxRigidBody(baseFlags) {} + virtual ~PxArticulationLink() {} + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulationLink", name) || PxRigidBody::isKindOf(name); } }; #if !PX_DOXYGEN diff --git a/src/PhysX/physx/include/PxArticulationReducedCoordinate.h b/src/PhysX/physx/include/PxArticulationReducedCoordinate.h index 6314c0953..15f9f23bf 100644 --- a/src/PhysX/physx/include/PxArticulationReducedCoordinate.h +++ b/src/PhysX/physx/include/PxArticulationReducedCoordinate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -53,11 +53,10 @@ namespace physx { enum Enum { - eFIX_BASE = (1 << 1) + eFIX_BASE = (1 << 0) }; }; - class PxJoint; typedef PxFlags PxArticulationFlags; @@ -75,7 +74,6 @@ namespace physx struct PxArticulationRootLinkData { - PxTransform transform; PxVec3 linVel; PxVec3 angVel; @@ -117,14 +115,11 @@ namespace physx void* scratchMemory; //this is used for internal calculation void* scratchAllocator; PxU32 version; //cache version. If the articulation configulation change, the cache is invalid - - }; typedef PxFlags PxArticulationCacheFlags; PX_FLAGS_OPERATORS(PxArticulationCache::Enum, PxU8) - /** \brief a tree structure of bodies connected by joints that is treated as a unit by the dynamics solver @@ -145,8 +140,7 @@ namespace physx { public: - virtual void release() = 0; - + virtual void release() = 0; /** \brief Sets flags on the articulation @@ -154,7 +148,7 @@ namespace physx \param[in] flags Articulation flags */ - virtual void setArticulationFlags(PxArticulationFlags flags) = 0; + virtual void setArticulationFlags(PxArticulationFlags flags) = 0; /** \brief Raises or clears a flag on the articulation @@ -163,40 +157,39 @@ namespace physx \param[in] value true/false indicating whether to raise or clear the flag */ - virtual void setArticulationFlag(PxArticulationFlag::Enum flag, bool value) = 0; + virtual void setArticulationFlag(PxArticulationFlag::Enum flag, bool value) = 0; /** \brief return PxArticulationFlags */ - virtual PxArticulationFlags getArticulationFlags() const = 0; + virtual PxArticulationFlags getArticulationFlags() const = 0; /** \brief returns the total Dofs of the articulation */ - virtual PxU32 getDofs() const = 0; + virtual PxU32 getDofs() const = 0; /** \brief create an articulation cache \note this call may only be made on articulations that are in a scene, and may not be made during simulation */ - virtual PxArticulationCache* createCache() const = 0; + virtual PxArticulationCache* createCache() const = 0; /** \brief Get the size of the articulation cache \note this call may only be made on articulations that are in a scene, and may not be made during simulation */ - virtual PxU32 getCacheDataSize() const = 0; + virtual PxU32 getCacheDataSize() const = 0; /** \brief zero all data in the articulation cache beside the cache version \note this call may only be made on articulations that are in a scene, and may not be made during simulation */ - virtual void zeroCache(PxArticulationCache& cache) = 0; + virtual void zeroCache(PxArticulationCache& cache) = 0; - /** \brief apply the user defined data in the cache to the articulation system @@ -206,7 +199,7 @@ namespace physx @see createCache copyInternalStateToCache */ - virtual void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake = true) = 0; + virtual void applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake = true) = 0; /** \brief copy the internal data of the articulation to the cache @@ -216,8 +209,7 @@ namespace physx @see createCache applyCache */ - virtual void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const = 0; - + virtual void copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const = 0; /** \brief release an articulation cache @@ -226,28 +218,26 @@ namespace physx @see createCache applyCache copyInternalStateToCache */ - virtual void releaseCache(PxArticulationCache& cache) const = 0; - + virtual void releaseCache(PxArticulationCache& cache) const = 0; /** \brief reduce the maximum data format to the reduced internal data \param[in] maximum joint data format \param[out] reduced joint data format */ - virtual void packJointData(const PxReal* maximum, PxReal* reduced) const = 0; + virtual void packJointData(const PxReal* maximum, PxReal* reduced) const = 0; /** \brief turn the reduced internal data to maximum joint data format \param[in] reduced joint data format \param[out] maximum joint data format */ - virtual void unpackJointData(const PxReal* reduced, PxReal* maximum) const = 0; + virtual void unpackJointData(const PxReal* reduced, PxReal* maximum) const = 0; /** \brief initialize all the common data for inverse dynamic */ - virtual void commonInit() const = 0; - + virtual void commonInit() const = 0; /** \brief determine the statically balance of the joint force of gravity for entire articulation. External force, joint velocity and joint acceleration @@ -257,7 +247,7 @@ namespace physx @see commonInit */ - virtual void computeGeneralizedGravityForce(PxArticulationCache& cache) const = 0; + virtual void computeGeneralizedGravityForce(PxArticulationCache& cache) const = 0; /** \brief determine coriolise and centrifugal force. External force, gravity and joint acceleration @@ -267,7 +257,7 @@ namespace physx @see commonInit */ - virtual void computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const = 0; + virtual void computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const = 0; /** \brief determine joint force change caused by external force. Gravity, joint acceleration and joint velocity @@ -277,7 +267,8 @@ namespace physx @see commonInit */ - virtual void computeGeneralizedExternalForce(PxArticulationCache& cache) const = 0; + virtual void computeGeneralizedExternalForce(PxArticulationCache& cache) const = 0; + /** \brief determine the joint acceleration for each joint This is purely calculates the change in joint acceleration due to change in the joint force @@ -286,8 +277,7 @@ namespace physx @see commonInit */ - virtual void computeJointAcceleration(PxArticulationCache& cache) const = 0; - + virtual void computeJointAcceleration(PxArticulationCache& cache) const = 0; /** \brief determine the joint force @@ -298,7 +288,7 @@ namespace physx @see commonInit */ - virtual void computeJointForce(PxArticulationCache& cache) const = 0; + virtual void computeJointForce(PxArticulationCache& cache) const = 0; /** \brief compute the kinematic jacobian for each joint from end effector to the root in world space @@ -307,16 +297,14 @@ namespace physx @see commonInit */ - virtual void computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const = 0; - + virtual void computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const = 0; /** \brief compute the coefficent matrix for contact force. PxContactJoint is the contact point \param[out] cache returs the coefficent matrix. Each column is the joint force effected by a contact based on impulse strength 1 @see commonInit */ - virtual void computeCoefficentMatrix(PxArticulationCache& cache) const = 0; - + virtual void computeCoefficentMatrix(PxArticulationCache& cache) const = 0; /** \brief compute the lambda value when the test impulse is 1 @@ -326,7 +314,7 @@ namespace physx \param[out] cache returns the coefficent matrix. Each column is the joint force effected by a contact based on impulse strength 1 @see commonInit */ - virtual bool computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxU32 maxIter) const = 0; + virtual bool computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxU32 maxIter) const = 0; /** \brief compute the joint-space inertia matrix @@ -334,7 +322,7 @@ namespace physx @see commonInit */ - virtual void computeGeneralizedMassMatrix(PxArticulationCache& cache) const = 0; + virtual void computeGeneralizedMassMatrix(PxArticulationCache& cache) const = 0; /** \brief add loop joint to the articulation system for inverse dynamic @@ -342,7 +330,7 @@ namespace physx @see commonInit */ - virtual void addLoopJoint(PxJoint* joint) = 0; + virtual void addLoopJoint(PxJoint* joint) = 0; /** \brief remove loop joint from the articulation system @@ -350,14 +338,13 @@ namespace physx @see commonInit */ - virtual void removeLoopJoint(PxJoint* joint) = 0; + virtual void removeLoopJoint(PxJoint* joint) = 0; /** \brief returns the number of loop joints in the articulation \return number of loop joints */ - - virtual PxU32 getNbLoopJoints() const = 0; + virtual PxU32 getNbLoopJoints() const = 0; /** \brief returns the set of loop constraints in the articulation @@ -371,15 +358,13 @@ namespace physx @see ArticulationLink */ - - virtual PxU32 getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex = 0) const = 0; + virtual PxU32 getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex = 0) const = 0; /** \brief returns the required size of coeffient matrix in the articulation. The coefficient matrix is number of constraint(loop joints) by total dofs. Constraint Torque = transpose(K) * lambda(). Lambda is a vector of number of constraints \return bite size of the coefficient matrix(nc * n) */ - - virtual PxU32 getCoefficentMatrixSize() const = 0; + virtual PxU32 getCoefficentMatrixSize() const = 0; /** \brief teleport root link to a new location @@ -388,12 +373,12 @@ namespace physx @see commonInit */ - virtual void teleportRootLink(const PxTransform& pose, bool autowake) = 0; + virtual void teleportRootLink(const PxTransform& pose, bool autowake) = 0; protected: - PX_INLINE PxArticulationReducedCoordinate(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationBase(concreteType, baseFlags) {} - PX_INLINE PxArticulationReducedCoordinate(PxBaseFlags baseFlags) : PxArticulationBase(baseFlags) {} - virtual ~PxArticulationReducedCoordinate() {} + PX_INLINE PxArticulationReducedCoordinate(PxType concreteType, PxBaseFlags baseFlags) : PxArticulationBase(concreteType, baseFlags) {} + PX_INLINE PxArticulationReducedCoordinate(PxBaseFlags baseFlags) : PxArticulationBase(baseFlags) {} + virtual ~PxArticulationReducedCoordinate() {} }; #if PX_VC @@ -404,6 +389,5 @@ namespace physx } // namespace physx #endif - /** @} */ #endif diff --git a/src/PhysX/physx/include/PxBatchQuery.h b/src/PhysX/physx/include/PxBatchQuery.h index 63f1e7209..0feb6f678 100644 --- a/src/PhysX/physx/include/PxBatchQuery.h +++ b/src/PhysX/physx/include/PxBatchQuery.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxBatchQueryDesc.h b/src/PhysX/physx/include/PxBatchQueryDesc.h index 185ca89a7..faefa08ef 100644 --- a/src/PhysX/physx/include/PxBatchQueryDesc.h +++ b/src/PhysX/physx/include/PxBatchQueryDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxBroadPhase.h b/src/PhysX/physx/include/PxBroadPhase.h index 39d3c4305..d3b231221 100644 --- a/src/PhysX/physx/include/PxBroadPhase.h +++ b/src/PhysX/physx/include/PxBroadPhase.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxClient.h b/src/PhysX/physx/include/PxClient.h index 4124add51..85a8ae21b 100644 --- a/src/PhysX/physx/include/PxClient.h +++ b/src/PhysX/physx/include/PxClient.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxConstraint.h b/src/PhysX/physx/include/PxConstraint.h index f0fb733c0..58ddcbc8d 100644 --- a/src/PhysX/physx/include/PxConstraint.h +++ b/src/PhysX/physx/include/PxConstraint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxConstraintDesc.h b/src/PhysX/physx/include/PxConstraintDesc.h index b469b55b5..138829313 100644 --- a/src/PhysX/physx/include/PxConstraintDesc.h +++ b/src/PhysX/physx/include/PxConstraintDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxContact.h b/src/PhysX/physx/include/PxContact.h index 0fab1333d..586baa42d 100644 --- a/src/PhysX/physx/include/PxContact.h +++ b/src/PhysX/physx/include/PxContact.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxContactModifyCallback.h b/src/PhysX/physx/include/PxContactModifyCallback.h index adca7a60a..d248ad86c 100644 --- a/src/PhysX/physx/include/PxContactModifyCallback.h +++ b/src/PhysX/physx/include/PxContactModifyCallback.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxDeletionListener.h b/src/PhysX/physx/include/PxDeletionListener.h index 0ca180360..b982ab3b3 100644 --- a/src/PhysX/physx/include/PxDeletionListener.h +++ b/src/PhysX/physx/include/PxDeletionListener.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxFiltering.h b/src/PhysX/physx/include/PxFiltering.h index b3a7f5289..6902c09a9 100644 --- a/src/PhysX/physx/include/PxFiltering.h +++ b/src/PhysX/physx/include/PxFiltering.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxForceMode.h b/src/PhysX/physx/include/PxForceMode.h index bac6253ec..f5915156d 100644 --- a/src/PhysX/physx/include/PxForceMode.h +++ b/src/PhysX/physx/include/PxForceMode.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxFoundation.h b/src/PhysX/physx/include/PxFoundation.h index 8a1104c5b..cc80a83c3 100644 --- a/src/PhysX/physx/include/PxFoundation.h +++ b/src/PhysX/physx/include/PxFoundation.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -36,6 +36,7 @@ #include "foundation/Px.h" #include "foundation/PxErrors.h" +#include "foundation/PxFoundationConfig.h" #if !PX_DOXYGEN namespace physx diff --git a/src/PhysX/physx/include/PxImmediateMode.h b/src/PhysX/physx/include/PxImmediateMode.h index 827d2a3ec..7e19fb51a 100644 --- a/src/PhysX/physx/include/PxImmediateMode.h +++ b/src/PhysX/physx/include/PxImmediateMode.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -192,7 +192,7 @@ namespace immediate PX_C_EXPORT PX_PHYSX_CORE_API void PxIntegrateSolverBodies(PxSolverBodyData* solverBodyData, PxSolverBody* solverBody, const PxVec3* linearMotionVelocity, const PxVec3* angularMotionState, const PxU32 nbBodiesToIntegrate, PxReal dt); /** - abrief Performs contact generation for a given pair of geometries at the specified poses. Produced contacts are stored in the provided Gu::ContactBuffer. Information is cached in PxCache structure + \brief Performs contact generation for a given pair of geometries at the specified poses. Produced contacts are stored in the provided Gu::ContactBuffer. Information is cached in PxCache structure to accelerate future contact generation between pairs. This cache data is valid only as long as the memory provided by PxCacheAllocator has not been released/re-used. Recommendation is to retain that data for a single simulation frame, discarding cached data after 2 frames. If the cached memory has been released/re-used prior to the corresponding pair having contact generation performed again, it is the application's responsibility to reset the PxCache. diff --git a/src/PhysX/physx/include/PxLockedData.h b/src/PhysX/physx/include/PxLockedData.h index 11b0b829e..bde841487 100644 --- a/src/PhysX/physx/include/PxLockedData.h +++ b/src/PhysX/physx/include/PxLockedData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxMaterial.h b/src/PhysX/physx/include/PxMaterial.h index 24dc74a3d..0d7b18b71 100644 --- a/src/PhysX/physx/include/PxMaterial.h +++ b/src/PhysX/physx/include/PxMaterial.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -77,7 +77,20 @@ struct PxMaterialFlag Note: This flag only has an effect if the PxMaterialFlag::eDISABLE_FRICTION bit is 0. */ - eDISABLE_STRONG_FRICTION = 1 << 1 + eDISABLE_STRONG_FRICTION = 1 << 1, + + /** + This flag only has an effect if PxFrictionType::ePATCH friction model is used. + + When using the patch friction model, up to 2 friction anchors are generated per patch. As the number of friction anchors + can be smaller than the number of contacts, the normal force is accumulated over all contacts and used to compute friction + for all anchors. Where there are more than 2 anchors, this can produce frictional behavior that is too strong (approximately 2x as strong + as analytical models suggest). + + This flag causes the normal force to be distributed between the friction anchors such that the total amount of friction applied does not + exceed the analyical results. + */ + eIMPROVED_PATCH_FRICTION = 1 << 2 }; }; diff --git a/src/PhysX/physx/include/PxPhysXConfig.h b/src/PhysX/physx/include/PxPhysXConfig.h index 9f532199b..93ec00318 100644 --- a/src/PhysX/physx/include/PxPhysXConfig.h +++ b/src/PhysX/physx/include/PxPhysXConfig.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxPhysics.h b/src/PhysX/physx/include/PxPhysics.h index e05dbc0c6..2ffc2cbe2 100644 --- a/src/PhysX/physx/include/PxPhysics.h +++ b/src/PhysX/physx/include/PxPhysics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxPhysicsAPI.h b/src/PhysX/physx/include/PxPhysicsAPI.h index 727774540..ffe979ed3 100644 --- a/src/PhysX/physx/include/PxPhysicsAPI.h +++ b/src/PhysX/physx/include/PxPhysicsAPI.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -155,7 +155,6 @@ Alternatively, one can instead directly #include a subset of the below files. //Character Controller #include "characterkinematic/PxBoxController.h" #include "characterkinematic/PxCapsuleController.h" -#include "characterkinematic/PxCharacter.h" #include "characterkinematic/PxController.h" #include "characterkinematic/PxControllerBehavior.h" #include "characterkinematic/PxControllerManager.h" diff --git a/src/PhysX/physx/include/PxPhysicsSerialization.h b/src/PhysX/physx/include/PxPhysicsSerialization.h index c4cad9d29..4f0446683 100644 --- a/src/PhysX/physx/include/PxPhysicsSerialization.h +++ b/src/PhysX/physx/include/PxPhysicsSerialization.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxPhysicsVersion.h b/src/PhysX/physx/include/PxPhysicsVersion.h index 9dd9de053..6a9e11606 100644 --- a/src/PhysX/physx/include/PxPhysicsVersion.h +++ b/src/PhysX/physx/include/PxPhysicsVersion.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxPruningStructure.h b/src/PhysX/physx/include/PxPruningStructure.h index beeb1d7ac..964c0b8bf 100644 --- a/src/PhysX/physx/include/PxPruningStructure.h +++ b/src/PhysX/physx/include/PxPruningStructure.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxQueryFiltering.h b/src/PhysX/physx/include/PxQueryFiltering.h index 7991246f6..98680bf95 100644 --- a/src/PhysX/physx/include/PxQueryFiltering.h +++ b/src/PhysX/physx/include/PxQueryFiltering.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxQueryReport.h b/src/PhysX/physx/include/PxQueryReport.h index f50322906..17b3ebbb6 100644 --- a/src/PhysX/physx/include/PxQueryReport.h +++ b/src/PhysX/physx/include/PxQueryReport.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxRigidActor.h b/src/PhysX/physx/include/PxRigidActor.h index 950f35e59..beec2a752 100644 --- a/src/PhysX/physx/include/PxRigidActor.h +++ b/src/PhysX/physx/include/PxRigidActor.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxRigidBody.h b/src/PhysX/physx/include/PxRigidBody.h index f25628f3c..6cbd33559 100644 --- a/src/PhysX/physx/include/PxRigidBody.h +++ b/src/PhysX/physx/include/PxRigidBody.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxRigidDynamic.h b/src/PhysX/physx/include/PxRigidDynamic.h index 071e9a9d3..a0ae0bcfc 100644 --- a/src/PhysX/physx/include/PxRigidDynamic.h +++ b/src/PhysX/physx/include/PxRigidDynamic.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxRigidStatic.h b/src/PhysX/physx/include/PxRigidStatic.h index 9d9281455..f98fa58ad 100644 --- a/src/PhysX/physx/include/PxRigidStatic.h +++ b/src/PhysX/physx/include/PxRigidStatic.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxScene.h b/src/PhysX/physx/include/PxScene.h index 49ea87f34..737832b8f 100644 --- a/src/PhysX/physx/include/PxScene.h +++ b/src/PhysX/physx/include/PxScene.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxSceneDesc.h b/src/PhysX/physx/include/PxSceneDesc.h index 67c107690..87f06629a 100644 --- a/src/PhysX/physx/include/PxSceneDesc.h +++ b/src/PhysX/physx/include/PxSceneDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -632,7 +632,7 @@ public: /** \brief Selects the solver algorithm to use. - Default: PxSolverType::eDEFAULT + Default: PxSolverType::ePGS @see PxSolverType */ @@ -848,6 +848,22 @@ public: */ PxU32 ccdMaxPasses; + /** + \brief CCD threshold + + CCD performs sweeps against shapes if and only if the relative motion of the shapes is fast-enough that a collision would be missed + by the discrete contact generation. However, in some circumstances, e.g. when the environment is constructed from large convex shapes, this + approach may produce undesired simulation artefacts. This parameter defines the minimum relative motion that would be required to force CCD between shapes. + The smaller of this value and the sum of the thresholds calculated for the shapes involved will be used. + + \note It is not advisable to set this to a very small value as this may lead to CCD "jamming" and detrimentally effect performance. This value should be at least larger than the translation caused by a single frame's gravitational effect + + Default: PX_MAX_F32 + Range: [Eps, PX_MAX_F32]
                      + */ + + PxReal ccdThreshold; + /** \brief The wake counter reset value @@ -980,6 +996,7 @@ PX_INLINE PxSceneDesc::PxSceneDesc(const PxTolerancesScale& scale): maxBiasCoefficient (PX_MAX_F32), contactReportStreamBufferSize (8192), ccdMaxPasses (1), + ccdThreshold (PX_MAX_F32), wakeCounterResetValue (20.0f*0.02f), sanityBounds (PxBounds3(PxVec3(-PX_MAX_BOUNDS_EXTENTS), PxVec3(PX_MAX_BOUNDS_EXTENTS))), gpuMaxNumPartitions (8), @@ -1020,6 +1037,9 @@ PX_INLINE bool PxSceneDesc::isValid() const if (solverOffsetSlop < 0.f) return false; + if(ccdThreshold <= 0.f) + return false; + if(!cpuDispatcher) return false; diff --git a/src/PhysX/physx/include/PxSceneLock.h b/src/PhysX/physx/include/PxSceneLock.h index 97ffad0c8..136d49568 100644 --- a/src/PhysX/physx/include/PxSceneLock.h +++ b/src/PhysX/physx/include/PxSceneLock.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxShape.h b/src/PhysX/physx/include/PxShape.h index 392006428..4ca85e639 100644 --- a/src/PhysX/physx/include/PxShape.h +++ b/src/PhysX/physx/include/PxShape.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxSimulationEventCallback.h b/src/PhysX/physx/include/PxSimulationEventCallback.h index 52186d374..8d89d965a 100644 --- a/src/PhysX/physx/include/PxSimulationEventCallback.h +++ b/src/PhysX/physx/include/PxSimulationEventCallback.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxSimulationStatistics.h b/src/PhysX/physx/include/PxSimulationStatistics.h index 6aa3e74a7..c08a86b49 100644 --- a/src/PhysX/physx/include/PxSimulationStatistics.h +++ b/src/PhysX/physx/include/PxSimulationStatistics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/PxVisualizationParameter.h b/src/PhysX/physx/include/PxVisualizationParameter.h index eac8a3f00..372f0073e 100644 --- a/src/PhysX/physx/include/PxVisualizationParameter.h +++ b/src/PhysX/physx/include/PxVisualizationParameter.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/characterkinematic/PxBoxController.h b/src/PhysX/physx/include/characterkinematic/PxBoxController.h index ecb7d3baa..6523a85a5 100644 --- a/src/PhysX/physx/include/characterkinematic/PxBoxController.h +++ b/src/PhysX/physx/include/characterkinematic/PxBoxController.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -34,7 +34,6 @@ @{ */ -#include "characterkinematic/PxCharacter.h" #include "characterkinematic/PxController.h" #if !PX_DOXYGEN diff --git a/src/PhysX/physx/include/characterkinematic/PxCapsuleController.h b/src/PhysX/physx/include/characterkinematic/PxCapsuleController.h index c8cf8a67f..869d8a19a 100644 --- a/src/PhysX/physx/include/characterkinematic/PxCapsuleController.h +++ b/src/PhysX/physx/include/characterkinematic/PxCapsuleController.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -34,7 +34,6 @@ @{ */ -#include "characterkinematic/PxCharacter.h" #include "characterkinematic/PxController.h" #if !PX_DOXYGEN diff --git a/src/PhysX/physx/include/characterkinematic/PxController.h b/src/PhysX/physx/include/characterkinematic/PxController.h index a0be05740..f5cac0d87 100644 --- a/src/PhysX/physx/include/characterkinematic/PxController.h +++ b/src/PhysX/physx/include/characterkinematic/PxController.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -35,7 +35,6 @@ @{ */ -#include "characterkinematic/PxCharacter.h" #include "characterkinematic/PxExtended.h" #include "characterkinematic/PxControllerObstacles.h" #include "PxQueryFiltering.h" diff --git a/src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h b/src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h index e3b3f74c9..7c4e295ea 100644 --- a/src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h +++ b/src/PhysX/physx/include/characterkinematic/PxControllerBehavior.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -34,7 +34,6 @@ */ #include "PxFiltering.h" -#include "characterkinematic/PxCharacter.h" #if !PX_DOXYGEN namespace physx diff --git a/src/PhysX/physx/include/characterkinematic/PxControllerManager.h b/src/PhysX/physx/include/characterkinematic/PxControllerManager.h index 924133ca3..1bc3aa979 100644 --- a/src/PhysX/physx/include/characterkinematic/PxControllerManager.h +++ b/src/PhysX/physx/include/characterkinematic/PxControllerManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -34,8 +34,6 @@ @{ */ -#include "characterkinematic/PxCharacter.h" - #include "PxPhysXConfig.h" #include "foundation/PxFlags.h" #include "foundation/PxErrorCallback.h" @@ -83,7 +81,7 @@ PX_FLAGS_OPERATORS(PxControllerDebugRenderFlag::Enum, PxU32) @see PxController PxBoxController PxCapsuleController */ -class PX_PHYSX_CHARACTER_API PxControllerManager +class PxControllerManager { public: /** @@ -295,7 +293,7 @@ protected: By default, locking is disabled. */ -PX_C_EXPORT PX_PHYSX_CHARACTER_API physx::PxControllerManager* PX_CALL_CONV PxCreateControllerManager(physx::PxScene& scene, bool lockingEnabled = false); +PX_C_EXPORT physx::PxControllerManager* PX_CALL_CONV PxCreateControllerManager(physx::PxScene& scene, bool lockingEnabled = false); /** @} */ #endif //PX_PHYSICS_CCT_MANAGER diff --git a/src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h b/src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h index ae1ef5231..243dc0060 100644 --- a/src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h +++ b/src/PhysX/physx/include/characterkinematic/PxControllerObstacles.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -33,7 +33,6 @@ @{ */ -#include "characterkinematic/PxCharacter.h" #include "characterkinematic/PxExtended.h" #include "geometry/PxGeometry.h" diff --git a/src/PhysX/physx/include/characterkinematic/PxExtended.h b/src/PhysX/physx/include/characterkinematic/PxExtended.h index 1adb4b9f4..7dc962269 100644 --- a/src/PhysX/physx/include/characterkinematic/PxExtended.h +++ b/src/PhysX/physx/include/characterkinematic/PxExtended.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/collision/PxCollisionDefs.h b/src/PhysX/physx/include/collision/PxCollisionDefs.h index 276954912..de7245f80 100644 --- a/src/PhysX/physx/include/collision/PxCollisionDefs.h +++ b/src/PhysX/physx/include/collision/PxCollisionDefs.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxBase.h b/src/PhysX/physx/include/common/PxBase.h index 848fe91cb..5d997d63a 100644 --- a/src/PhysX/physx/include/common/PxBase.h +++ b/src/PhysX/physx/include/common/PxBase.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxCollection.h b/src/PhysX/physx/include/common/PxCollection.h index 904055445..c5fbf5717 100644 --- a/src/PhysX/physx/include/common/PxCollection.h +++ b/src/PhysX/physx/include/common/PxCollection.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxCoreUtilityTypes.h b/src/PhysX/physx/include/common/PxCoreUtilityTypes.h index 57a475aa9..5302a5b13 100644 --- a/src/PhysX/physx/include/common/PxCoreUtilityTypes.h +++ b/src/PhysX/physx/include/common/PxCoreUtilityTypes.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxMetaData.h b/src/PhysX/physx/include/common/PxMetaData.h index c1a050fc4..a8a7cd51f 100644 --- a/src/PhysX/physx/include/common/PxMetaData.h +++ b/src/PhysX/physx/include/common/PxMetaData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxMetaDataFlags.h b/src/PhysX/physx/include/common/PxMetaDataFlags.h index 7c890f9e0..6f3a60448 100644 --- a/src/PhysX/physx/include/common/PxMetaDataFlags.h +++ b/src/PhysX/physx/include/common/PxMetaDataFlags.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxPhysXCommonConfig.h b/src/PhysX/physx/include/common/PxPhysXCommonConfig.h index 61e2d8754..e81e5ca42 100644 --- a/src/PhysX/physx/include/common/PxPhysXCommonConfig.h +++ b/src/PhysX/physx/include/common/PxPhysXCommonConfig.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -44,7 +44,7 @@ https://developercommunity.visualstudio.com/content/problem/66047/possible-compi #endif // define API function declaration (public API only needed because of extensions) -#if defined PX_PHYSX_STATIC_LIB || defined PX_PHYSX_CORE_STATIC_LIB +#if defined PX_PHYSX_STATIC_LIB #define PX_PHYSX_CORE_API #else #if PX_WINDOWS @@ -82,7 +82,7 @@ https://developercommunity.visualstudio.com/content/problem/66047/possible-compi #define PX_PHYSX_GPU_API #endif // PX_SUPPORT_GPU_PHYSX -#if defined PX_PHYSX_STATIC_LIB || defined PX_PHYSX_CORE_STATIC_LIB +#if defined PX_PHYSX_STATIC_LIB #define PX_PHYSX_COMMON_API #else #if PX_WINDOWS && !defined(__CUDACC__) diff --git a/src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h b/src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h index 414695259..f28fc444f 100644 --- a/src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h +++ b/src/PhysX/physx/include/common/PxPhysicsInsertionCallback.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxProfileZone.h b/src/PhysX/physx/include/common/PxProfileZone.h index 0fda159f1..5a7af2676 100644 --- a/src/PhysX/physx/include/common/PxProfileZone.h +++ b/src/PhysX/physx/include/common/PxProfileZone.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXFOUNDATION_PXPROFILEZONE_H #define PXFOUNDATION_PXPROFILEZONE_H diff --git a/src/PhysX/physx/include/common/PxRenderBuffer.h b/src/PhysX/physx/include/common/PxRenderBuffer.h index 8fe0b0fa1..ab3cc5684 100644 --- a/src/PhysX/physx/include/common/PxRenderBuffer.h +++ b/src/PhysX/physx/include/common/PxRenderBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxSerialFramework.h b/src/PhysX/physx/include/common/PxSerialFramework.h index ac2565a7b..9ff8e473e 100644 --- a/src/PhysX/physx/include/common/PxSerialFramework.h +++ b/src/PhysX/physx/include/common/PxSerialFramework.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxSerializer.h b/src/PhysX/physx/include/common/PxSerializer.h index d3c1327fc..80c6456cc 100644 --- a/src/PhysX/physx/include/common/PxSerializer.h +++ b/src/PhysX/physx/include/common/PxSerializer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxStringTable.h b/src/PhysX/physx/include/common/PxStringTable.h index 5564045eb..2e3508554 100644 --- a/src/PhysX/physx/include/common/PxStringTable.h +++ b/src/PhysX/physx/include/common/PxStringTable.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxTolerancesScale.h b/src/PhysX/physx/include/common/PxTolerancesScale.h index f966de3a4..e4165cbd7 100644 --- a/src/PhysX/physx/include/common/PxTolerancesScale.h +++ b/src/PhysX/physx/include/common/PxTolerancesScale.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/PxTypeInfo.h b/src/PhysX/physx/include/common/PxTypeInfo.h index 5e74ea331..1c84bed9e 100644 --- a/src/PhysX/physx/include/common/PxTypeInfo.h +++ b/src/PhysX/physx/include/common/PxTypeInfo.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h b/src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h index 7f3e45bae..82fc2e528 100644 --- a/src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h +++ b/src/PhysX/physx/include/common/windows/PxWindowsDelayLoadHook.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h b/src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h index dd0168922..1d5505177 100644 --- a/src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h +++ b/src/PhysX/physx/include/cooking/PxBVH33MidphaseDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h b/src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h index aec2fe218..6c708a414 100644 --- a/src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h +++ b/src/PhysX/physx/include/cooking/PxBVH34MidphaseDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/cooking/PxBVHStructureDesc.h b/src/PhysX/physx/include/cooking/PxBVHStructureDesc.h index 6c9a8544a..3474a8678 100644 --- a/src/PhysX/physx/include/cooking/PxBVHStructureDesc.h +++ b/src/PhysX/physx/include/cooking/PxBVHStructureDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/cooking/PxConvexMeshDesc.h b/src/PhysX/physx/include/cooking/PxConvexMeshDesc.h index 20d0ee1e6..5a8085101 100644 --- a/src/PhysX/physx/include/cooking/PxConvexMeshDesc.h +++ b/src/PhysX/physx/include/cooking/PxConvexMeshDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/cooking/PxCooking.h b/src/PhysX/physx/include/cooking/PxCooking.h index f1a8a257e..ae5e5dab8 100644 --- a/src/PhysX/physx/include/cooking/PxCooking.h +++ b/src/PhysX/physx/include/cooking/PxCooking.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/cooking/PxMidphaseDesc.h b/src/PhysX/physx/include/cooking/PxMidphaseDesc.h index 731926451..6daf4077d 100644 --- a/src/PhysX/physx/include/cooking/PxMidphaseDesc.h +++ b/src/PhysX/physx/include/cooking/PxMidphaseDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h b/src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h index 9aa422fc3..17a612f22 100644 --- a/src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h +++ b/src/PhysX/physx/include/cooking/PxTriangleMeshDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/cooking/Pxc.h b/src/PhysX/physx/include/cooking/Pxc.h index a279db6cd..55f43f93e 100644 --- a/src/PhysX/physx/include/cooking/Pxc.h +++ b/src/PhysX/physx/include/cooking/Pxc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/cudamanager/PxCudaContextManager.h b/src/PhysX/physx/include/cudamanager/PxCudaContextManager.h index 1bcaf2409..6ed643f0c 100644 --- a/src/PhysX/physx/include/cudamanager/PxCudaContextManager.h +++ b/src/PhysX/physx/include/cudamanager/PxCudaContextManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXCUDACONTEXTMANAGER_PXCUDACONTEXTMANAGER_H diff --git a/src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h b/src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h index 9d20feb61..ce44d9b06 100644 --- a/src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h +++ b/src/PhysX/physx/include/cudamanager/PxCudaMemoryManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXCUDACONTEXTMANAGER_PXCUDAMEMORYMANAGER_H #define PXCUDACONTEXTMANAGER_PXCUDAMEMORYMANAGER_H diff --git a/src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h b/src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h index 6429a7b1b..d37404270 100644 --- a/src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h +++ b/src/PhysX/physx/include/cudamanager/PxGpuCopyDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXCUDACONTEXTMANAGER_PXGPUCOPYDESC_H #define PXCUDACONTEXTMANAGER_PXGPUCOPYDESC_H diff --git a/src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h b/src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h index 64d190b03..e2bf0067b 100644 --- a/src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h +++ b/src/PhysX/physx/include/cudamanager/PxGpuCopyDescQueue.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXCUDACONTEXTMANAGER_PXGPUCOPYDESCQUEUE_H #define PXCUDACONTEXTMANAGER_PXGPUCOPYDESCQUEUE_H diff --git a/src/PhysX/physx/include/extensions/PxBinaryConverter.h b/src/PhysX/physx/include/extensions/PxBinaryConverter.h index 1a985fda7..542adce25 100644 --- a/src/PhysX/physx/include/extensions/PxBinaryConverter.h +++ b/src/PhysX/physx/include/extensions/PxBinaryConverter.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxBroadPhaseExt.h b/src/PhysX/physx/include/extensions/PxBroadPhaseExt.h index 8347bcc10..50f86763a 100644 --- a/src/PhysX/physx/include/extensions/PxBroadPhaseExt.h +++ b/src/PhysX/physx/include/extensions/PxBroadPhaseExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxCollectionExt.h b/src/PhysX/physx/include/extensions/PxCollectionExt.h index 157c28108..beba4b19a 100644 --- a/src/PhysX/physx/include/extensions/PxCollectionExt.h +++ b/src/PhysX/physx/include/extensions/PxCollectionExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxConstraintExt.h b/src/PhysX/physx/include/extensions/PxConstraintExt.h index 62ca07814..87eb4b3e7 100644 --- a/src/PhysX/physx/include/extensions/PxConstraintExt.h +++ b/src/PhysX/physx/include/extensions/PxConstraintExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxContactJoint.h b/src/PhysX/physx/include/extensions/PxContactJoint.h index 00f84a5e3..a834b4552 100644 --- a/src/PhysX/physx/include/extensions/PxContactJoint.h +++ b/src/PhysX/physx/include/extensions/PxContactJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxConvexMeshExt.h b/src/PhysX/physx/include/extensions/PxConvexMeshExt.h index 39b150f42..60bc2f790 100644 --- a/src/PhysX/physx/include/extensions/PxConvexMeshExt.h +++ b/src/PhysX/physx/include/extensions/PxConvexMeshExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxD6Joint.h b/src/PhysX/physx/include/extensions/PxD6Joint.h index 7fe722d8d..6ce359f36 100644 --- a/src/PhysX/physx/include/extensions/PxD6Joint.h +++ b/src/PhysX/physx/include/extensions/PxD6Joint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxD6JointCreate.h b/src/PhysX/physx/include/extensions/PxD6JointCreate.h index 0e2d1964a..513bde4a7 100644 --- a/src/PhysX/physx/include/extensions/PxD6JointCreate.h +++ b/src/PhysX/physx/include/extensions/PxD6JointCreate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxDefaultAllocator.h b/src/PhysX/physx/include/extensions/PxDefaultAllocator.h index 86405a44b..3814f40f3 100644 --- a/src/PhysX/physx/include/extensions/PxDefaultAllocator.h +++ b/src/PhysX/physx/include/extensions/PxDefaultAllocator.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h b/src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h index 458676b32..595f89838 100644 --- a/src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h +++ b/src/PhysX/physx/include/extensions/PxDefaultCpuDispatcher.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h b/src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h index 7880c12ed..d0a5c6914 100644 --- a/src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h +++ b/src/PhysX/physx/include/extensions/PxDefaultErrorCallback.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h b/src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h index c4edb5e11..40fd3903c 100644 --- a/src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h +++ b/src/PhysX/physx/include/extensions/PxDefaultSimulationFilterShader.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxDefaultStreams.h b/src/PhysX/physx/include/extensions/PxDefaultStreams.h index 9eddeda5e..3f5e7ad46 100644 --- a/src/PhysX/physx/include/extensions/PxDefaultStreams.h +++ b/src/PhysX/physx/include/extensions/PxDefaultStreams.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxDistanceJoint.h b/src/PhysX/physx/include/extensions/PxDistanceJoint.h index bd752d25d..29ad6926c 100644 --- a/src/PhysX/physx/include/extensions/PxDistanceJoint.h +++ b/src/PhysX/physx/include/extensions/PxDistanceJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxExtensionsAPI.h b/src/PhysX/physx/include/extensions/PxExtensionsAPI.h index 6647b3cc2..3fde4b99c 100644 --- a/src/PhysX/physx/include/extensions/PxExtensionsAPI.h +++ b/src/PhysX/physx/include/extensions/PxExtensionsAPI.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxFixedJoint.h b/src/PhysX/physx/include/extensions/PxFixedJoint.h index fff4b84e9..789cf2e48 100644 --- a/src/PhysX/physx/include/extensions/PxFixedJoint.h +++ b/src/PhysX/physx/include/extensions/PxFixedJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxJoint.h b/src/PhysX/physx/include/extensions/PxJoint.h index 14898d6be..f6f31f26d 100644 --- a/src/PhysX/physx/include/extensions/PxJoint.h +++ b/src/PhysX/physx/include/extensions/PxJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxJointLimit.h b/src/PhysX/physx/include/extensions/PxJointLimit.h index 85a4ec82f..5859f92da 100644 --- a/src/PhysX/physx/include/extensions/PxJointLimit.h +++ b/src/PhysX/physx/include/extensions/PxJointLimit.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxMassProperties.h b/src/PhysX/physx/include/extensions/PxMassProperties.h index a5bc057a5..17b431966 100644 --- a/src/PhysX/physx/include/extensions/PxMassProperties.h +++ b/src/PhysX/physx/include/extensions/PxMassProperties.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxPrismaticJoint.h b/src/PhysX/physx/include/extensions/PxPrismaticJoint.h index b82ac2c8a..aba1341aa 100644 --- a/src/PhysX/physx/include/extensions/PxPrismaticJoint.h +++ b/src/PhysX/physx/include/extensions/PxPrismaticJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxRaycastCCD.h b/src/PhysX/physx/include/extensions/PxRaycastCCD.h index ed8f84ba0..190251b4d 100644 --- a/src/PhysX/physx/include/extensions/PxRaycastCCD.h +++ b/src/PhysX/physx/include/extensions/PxRaycastCCD.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxRepXSerializer.h b/src/PhysX/physx/include/extensions/PxRepXSerializer.h index dae0ef604..ae60b89e3 100644 --- a/src/PhysX/physx/include/extensions/PxRepXSerializer.h +++ b/src/PhysX/physx/include/extensions/PxRepXSerializer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_REPX_SERIALIZER_H diff --git a/src/PhysX/physx/include/extensions/PxRepXSimpleType.h b/src/PhysX/physx/include/extensions/PxRepXSimpleType.h index bd6023e92..43c5a11e1 100644 --- a/src/PhysX/physx/include/extensions/PxRepXSimpleType.h +++ b/src/PhysX/physx/include/extensions/PxRepXSimpleType.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxRevoluteJoint.h b/src/PhysX/physx/include/extensions/PxRevoluteJoint.h index d7d1c685c..3aabbebf8 100644 --- a/src/PhysX/physx/include/extensions/PxRevoluteJoint.h +++ b/src/PhysX/physx/include/extensions/PxRevoluteJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxRigidActorExt.h b/src/PhysX/physx/include/extensions/PxRigidActorExt.h index 2a437be7f..d14793e0b 100644 --- a/src/PhysX/physx/include/extensions/PxRigidActorExt.h +++ b/src/PhysX/physx/include/extensions/PxRigidActorExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxRigidBodyExt.h b/src/PhysX/physx/include/extensions/PxRigidBodyExt.h index f7c8f53ed..2d1ef552e 100644 --- a/src/PhysX/physx/include/extensions/PxRigidBodyExt.h +++ b/src/PhysX/physx/include/extensions/PxRigidBodyExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxSceneQueryExt.h b/src/PhysX/physx/include/extensions/PxSceneQueryExt.h index 1d4b86c01..3e755e0c4 100644 --- a/src/PhysX/physx/include/extensions/PxSceneQueryExt.h +++ b/src/PhysX/physx/include/extensions/PxSceneQueryExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxSerialization.h b/src/PhysX/physx/include/extensions/PxSerialization.h index 2d111ca78..44980aef8 100644 --- a/src/PhysX/physx/include/extensions/PxSerialization.h +++ b/src/PhysX/physx/include/extensions/PxSerialization.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxShapeExt.h b/src/PhysX/physx/include/extensions/PxShapeExt.h index 02228d5e4..d11998722 100644 --- a/src/PhysX/physx/include/extensions/PxShapeExt.h +++ b/src/PhysX/physx/include/extensions/PxShapeExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxSimpleFactory.h b/src/PhysX/physx/include/extensions/PxSimpleFactory.h index 354fe2080..e4f1e4ccf 100644 --- a/src/PhysX/physx/include/extensions/PxSimpleFactory.h +++ b/src/PhysX/physx/include/extensions/PxSimpleFactory.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxSmoothNormals.h b/src/PhysX/physx/include/extensions/PxSmoothNormals.h index 0aa373b44..a14817d0c 100644 --- a/src/PhysX/physx/include/extensions/PxSmoothNormals.h +++ b/src/PhysX/physx/include/extensions/PxSmoothNormals.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxSphericalJoint.h b/src/PhysX/physx/include/extensions/PxSphericalJoint.h index 80e513193..f3cb509a1 100644 --- a/src/PhysX/physx/include/extensions/PxSphericalJoint.h +++ b/src/PhysX/physx/include/extensions/PxSphericalJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxStringTableExt.h b/src/PhysX/physx/include/extensions/PxStringTableExt.h index 3585983ea..c1ef6882a 100644 --- a/src/PhysX/physx/include/extensions/PxStringTableExt.h +++ b/src/PhysX/physx/include/extensions/PxStringTableExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/extensions/PxTriangleMeshExt.h b/src/PhysX/physx/include/extensions/PxTriangleMeshExt.h index 43dc29e2d..9be39a471 100644 --- a/src/PhysX/physx/include/extensions/PxTriangleMeshExt.h +++ b/src/PhysX/physx/include/extensions/PxTriangleMeshExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/filebuf/PxFileBuf.h b/src/PhysX/physx/include/filebuf/PxFileBuf.h index b54f09b2a..3ef07eeac 100644 --- a/src/PhysX/physx/include/filebuf/PxFileBuf.h +++ b/src/PhysX/physx/include/filebuf/PxFileBuf.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/foundation/PxAssert.h b/src/PhysX/physx/include/foundation/PxAssert.h new file mode 100644 index 000000000..88309d53f --- /dev/null +++ b/src/PhysX/physx/include/foundation/PxAssert.h @@ -0,0 +1,98 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_FOUNDATION_PX_ASSERT_H +#define PX_FOUNDATION_PX_ASSERT_H + +#include "foundation/PxFoundationConfig.h" +#include "foundation/Px.h" + +/** \addtogroup foundation + @{ +*/ + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/* Base class to handle assert failures */ +class PX_DEPRECATED PxAssertHandler +{ + public: + virtual ~PxAssertHandler() + { + } + virtual void operator()(const char* exp, const char* file, int line, bool& ignore) = 0; +}; + +PX_FOUNDATION_API PX_DEPRECATED PxAssertHandler& PxGetAssertHandler(); +PX_FOUNDATION_API PX_DEPRECATED void PxSetAssertHandler(PxAssertHandler& handler); + +#if !PX_ENABLE_ASSERTS + #define PX_ASSERT(exp) ((void)0) + #define PX_ALWAYS_ASSERT_MESSAGE(exp) ((void)0) + #define PX_ASSERT_WITH_MESSAGE(condition, message) ((void)0) +#else +#if PX_VC + #define PX_CODE_ANALYSIS_ASSUME(exp) \ + __analysis_assume(!!(exp)) // This macro will be used to get rid of analysis warning messages if a PX_ASSERT is used + // to "guard" illegal mem access, for example. +#else + #define PX_CODE_ANALYSIS_ASSUME(exp) +#endif + #define PX_ASSERT(exp) \ + { \ + static bool _ignore = false; \ + ((void)((!!(exp)) || (!_ignore && (physx::PxGetAssertHandler()(#exp, __FILE__, __LINE__, _ignore), false)))); \ + PX_CODE_ANALYSIS_ASSUME(exp); \ + } + #define PX_ALWAYS_ASSERT_MESSAGE(exp) \ + { \ + static bool _ignore = false; \ + if(!_ignore) \ + physx::PxGetAssertHandler()(exp, __FILE__, __LINE__, _ignore); \ + } + #define PX_ASSERT_WITH_MESSAGE(exp, message) \ + { \ + static bool _ignore = false; \ + ((void)((!!(exp)) || (!_ignore && (physx::PxGetAssertHandler()(message, __FILE__, __LINE__, _ignore), false)))); \ + PX_CODE_ANALYSIS_ASSUME(exp); \ + } +#endif // !PX_ENABLE_ASSERTS + +#define PX_ALWAYS_ASSERT() PX_ASSERT(0) + +#if !PX_DOXYGEN +} // namespace physx +#endif + + +/** @} */ +#endif // PX_FOUNDATION_PX_ASSERT_H diff --git a/src/PhysX/physx/include/foundation/PxFoundationConfig.h b/src/PhysX/physx/include/foundation/PxFoundationConfig.h new file mode 100644 index 000000000..0ff58be35 --- /dev/null +++ b/src/PhysX/physx/include/foundation/PxFoundationConfig.h @@ -0,0 +1,57 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PX_FOUNDATION_PX_FOUNDATION_CONFIG_H +#define PX_FOUNDATION_PX_FOUNDATION_CONFIG_H + +#include "foundation/PxPreprocessor.h" + +/** \addtogroup foundation + @{ +*/ + +#if defined PX_PHYSX_STATIC_LIB + #define PX_FOUNDATION_API +#else + #if PX_WINDOWS && !defined(__CUDACC__) + #if defined PX_PHYSX_FOUNDATION_EXPORTS + #define PX_FOUNDATION_API __declspec(dllexport) + #else + #define PX_FOUNDATION_API __declspec(dllimport) + #endif + #elif PX_UNIX_FAMILY + #define PX_FOUNDATION_API PX_UNIX_EXPORT + #else + #define PX_FOUNDATION_API + #endif +#endif + + +/** @} */ +#endif // PX_FOUNDATION_PX_ASSERT_H diff --git a/src/PhysX/physx/include/foundation/PxMathUtils.h b/src/PhysX/physx/include/foundation/PxMathUtils.h new file mode 100644 index 000000000..445439c0c --- /dev/null +++ b/src/PhysX/physx/include/foundation/PxMathUtils.h @@ -0,0 +1,74 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXMATHUTILS_H +#define PXFOUNDATION_PXMATHUTILS_H + +/** \addtogroup common + @{ +*/ + +#include "foundation/Px.h" +#include "foundation/PxFoundationConfig.h" + +#if !PX_DOXYGEN +namespace physx +{ +#endif + +/** +\brief finds the shortest rotation between two vectors. + +\param[in] from the vector to start from +\param[in] target the vector to rotate to +\return a rotation about an axis normal to the two vectors which takes one to the other via the shortest path +*/ + +PX_FOUNDATION_API PxQuat PxShortestRotation(const PxVec3& from, const PxVec3& target); + +/* \brief diagonalizes a 3x3 symmetric matrix y + +The returned matrix satisfies M = R * D * R', where R is the rotation matrix for the output quaternion, R' its +transpose, and D the diagonal matrix + +If the matrix is not symmetric, the result is undefined. + +\param[in] m the matrix to diagonalize +\param[out] axes a quaternion rotation which diagonalizes the matrix +\return the vector diagonal of the diagonalized matrix. +*/ + +PX_FOUNDATION_API PxVec3 PxDiagonalize(const PxMat33& m, PxQuat& axes); + +#if !PX_DOXYGEN +} // namespace physx +#endif + +/** @} */ +#endif diff --git a/src/PhysX/physx/include/geometry/PxBVHStructure.h b/src/PhysX/physx/include/geometry/PxBVHStructure.h index 86050b13e..515a542b1 100644 --- a/src/PhysX/physx/include/geometry/PxBVHStructure.h +++ b/src/PhysX/physx/include/geometry/PxBVHStructure.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxBoxGeometry.h b/src/PhysX/physx/include/geometry/PxBoxGeometry.h index a0fa7d0c6..d1bfde7de 100644 --- a/src/PhysX/physx/include/geometry/PxBoxGeometry.h +++ b/src/PhysX/physx/include/geometry/PxBoxGeometry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxCapsuleGeometry.h b/src/PhysX/physx/include/geometry/PxCapsuleGeometry.h index 4600f4da4..8acb931b2 100644 --- a/src/PhysX/physx/include/geometry/PxCapsuleGeometry.h +++ b/src/PhysX/physx/include/geometry/PxCapsuleGeometry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -34,6 +34,7 @@ @{ */ #include "geometry/PxGeometry.h" +#include "foundation/PxFoundationConfig.h" #if !PX_DOXYGEN namespace physx diff --git a/src/PhysX/physx/include/geometry/PxConvexMesh.h b/src/PhysX/physx/include/geometry/PxConvexMesh.h index b331a4223..921d1f837 100644 --- a/src/PhysX/physx/include/geometry/PxConvexMesh.h +++ b/src/PhysX/physx/include/geometry/PxConvexMesh.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h b/src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h index dad042c8c..d8bc9d1d8 100644 --- a/src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h +++ b/src/PhysX/physx/include/geometry/PxConvexMeshGeometry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxGeometry.h b/src/PhysX/physx/include/geometry/PxGeometry.h index 5c8133918..b0b5e9d6f 100644 --- a/src/PhysX/physx/include/geometry/PxGeometry.h +++ b/src/PhysX/physx/include/geometry/PxGeometry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxGeometryHelpers.h b/src/PhysX/physx/include/geometry/PxGeometryHelpers.h index e4637df70..6573907b0 100644 --- a/src/PhysX/physx/include/geometry/PxGeometryHelpers.h +++ b/src/PhysX/physx/include/geometry/PxGeometryHelpers.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxGeometryQuery.h b/src/PhysX/physx/include/geometry/PxGeometryQuery.h index 95274b34f..2be498133 100644 --- a/src/PhysX/physx/include/geometry/PxGeometryQuery.h +++ b/src/PhysX/physx/include/geometry/PxGeometryQuery.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxHeightField.h b/src/PhysX/physx/include/geometry/PxHeightField.h index 5cc07ae71..5f45226fb 100644 --- a/src/PhysX/physx/include/geometry/PxHeightField.h +++ b/src/PhysX/physx/include/geometry/PxHeightField.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldDesc.h b/src/PhysX/physx/include/geometry/PxHeightFieldDesc.h index eac748ccc..23fc6f1a9 100644 --- a/src/PhysX/physx/include/geometry/PxHeightFieldDesc.h +++ b/src/PhysX/physx/include/geometry/PxHeightFieldDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldFlag.h b/src/PhysX/physx/include/geometry/PxHeightFieldFlag.h index 0f0e407df..03cfe4ac9 100644 --- a/src/PhysX/physx/include/geometry/PxHeightFieldFlag.h +++ b/src/PhysX/physx/include/geometry/PxHeightFieldFlag.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h b/src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h index b0dc61102..a68c5dd9f 100644 --- a/src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h +++ b/src/PhysX/physx/include/geometry/PxHeightFieldGeometry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxHeightFieldSample.h b/src/PhysX/physx/include/geometry/PxHeightFieldSample.h index 4f2170556..634c4ea97 100644 --- a/src/PhysX/physx/include/geometry/PxHeightFieldSample.h +++ b/src/PhysX/physx/include/geometry/PxHeightFieldSample.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxMeshQuery.h b/src/PhysX/physx/include/geometry/PxMeshQuery.h index 0c381ea7e..284439d6e 100644 --- a/src/PhysX/physx/include/geometry/PxMeshQuery.h +++ b/src/PhysX/physx/include/geometry/PxMeshQuery.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxMeshScale.h b/src/PhysX/physx/include/geometry/PxMeshScale.h index 7771a7e0e..85206ba98 100644 --- a/src/PhysX/physx/include/geometry/PxMeshScale.h +++ b/src/PhysX/physx/include/geometry/PxMeshScale.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxPlaneGeometry.h b/src/PhysX/physx/include/geometry/PxPlaneGeometry.h index 002c72715..f25a3df9f 100644 --- a/src/PhysX/physx/include/geometry/PxPlaneGeometry.h +++ b/src/PhysX/physx/include/geometry/PxPlaneGeometry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -36,6 +36,7 @@ #include "foundation/PxPlane.h" #include "foundation/PxTransform.h" #include "geometry/PxGeometry.h" +#include "foundation/PxFoundationConfig.h" #if !PX_DOXYGEN namespace physx diff --git a/src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h b/src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h index eeaea9858..921699eda 100644 --- a/src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h +++ b/src/PhysX/physx/include/geometry/PxSimpleTriangleMesh.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxSphereGeometry.h b/src/PhysX/physx/include/geometry/PxSphereGeometry.h index 05ea69c76..d822314ed 100644 --- a/src/PhysX/physx/include/geometry/PxSphereGeometry.h +++ b/src/PhysX/physx/include/geometry/PxSphereGeometry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxTriangle.h b/src/PhysX/physx/include/geometry/PxTriangle.h index 68d227464..3cfffcce1 100644 --- a/src/PhysX/physx/include/geometry/PxTriangle.h +++ b/src/PhysX/physx/include/geometry/PxTriangle.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxTriangleMesh.h b/src/PhysX/physx/include/geometry/PxTriangleMesh.h index e6fc1582e..94d6a314f 100644 --- a/src/PhysX/physx/include/geometry/PxTriangleMesh.h +++ b/src/PhysX/physx/include/geometry/PxTriangleMesh.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h b/src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h index f2a8bdbd8..d93278517 100644 --- a/src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h +++ b/src/PhysX/physx/include/geometry/PxTriangleMeshGeometry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geomutils/GuContactBuffer.h b/src/PhysX/physx/include/geomutils/GuContactBuffer.h index 06568fe62..9b6bb6d72 100644 --- a/src/PhysX/physx/include/geomutils/GuContactBuffer.h +++ b/src/PhysX/physx/include/geomutils/GuContactBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/geomutils/GuContactPoint.h b/src/PhysX/physx/include/geomutils/GuContactPoint.h index 5f2bcb623..71c244c02 100644 --- a/src/PhysX/physx/include/geomutils/GuContactPoint.h +++ b/src/PhysX/physx/include/geomutils/GuContactPoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/gpu/PxGpu.h b/src/PhysX/physx/include/gpu/PxGpu.h index 5f495bcaa..75fcc7405 100644 --- a/src/PhysX/physx/include/gpu/PxGpu.h +++ b/src/PhysX/physx/include/gpu/PxGpu.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PX_GPU_H #define PX_GPU_H diff --git a/src/PhysX/physx/include/pvd/PxPvd.h b/src/PhysX/physx/include/pvd/PxPvd.h index df4cbc63d..dab681b9e 100644 --- a/src/PhysX/physx/include/pvd/PxPvd.h +++ b/src/PhysX/physx/include/pvd/PxPvd.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/pvd/PxPvdSceneClient.h b/src/PhysX/physx/include/pvd/PxPvdSceneClient.h index 86c5eb040..43592c3b7 100644 --- a/src/PhysX/physx/include/pvd/PxPvdSceneClient.h +++ b/src/PhysX/physx/include/pvd/PxPvdSceneClient.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/pvd/PxPvdTransport.h b/src/PhysX/physx/include/pvd/PxPvdTransport.h index 098fbb5c1..fc4da49a2 100644 --- a/src/PhysX/physx/include/pvd/PxPvdTransport.h +++ b/src/PhysX/physx/include/pvd/PxPvdTransport.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/solver/PxSolverDefs.h b/src/PhysX/physx/include/solver/PxSolverDefs.h index 6fad51007..5a29686c0 100644 --- a/src/PhysX/physx/include/solver/PxSolverDefs.h +++ b/src/PhysX/physx/include/solver/PxSolverDefs.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/task/PxCpuDispatcher.h b/src/PhysX/physx/include/task/PxCpuDispatcher.h index 523c69af4..ff466e669 100644 --- a/src/PhysX/physx/include/task/PxCpuDispatcher.h +++ b/src/PhysX/physx/include/task/PxCpuDispatcher.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXTASK_PXCPUDISPATCHER_H #define PXTASK_PXCPUDISPATCHER_H diff --git a/src/PhysX/physx/include/task/PxGpuDispatcher.h b/src/PhysX/physx/include/task/PxGpuDispatcher.h index ae53ab1a1..7e04bccd9 100644 --- a/src/PhysX/physx/include/task/PxGpuDispatcher.h +++ b/src/PhysX/physx/include/task/PxGpuDispatcher.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXTASK_PXGPUDISPATCHER_H #define PXTASK_PXGPUDISPATCHER_H diff --git a/src/PhysX/physx/include/task/PxGpuTask.h b/src/PhysX/physx/include/task/PxGpuTask.h index e9d88ac52..60e359bc1 100644 --- a/src/PhysX/physx/include/task/PxGpuTask.h +++ b/src/PhysX/physx/include/task/PxGpuTask.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXTASK_PXGPUTASK_H diff --git a/src/PhysX/physx/include/task/PxTask.h b/src/PhysX/physx/include/task/PxTask.h index b51e6bc58..c75ae9006 100644 --- a/src/PhysX/physx/include/task/PxTask.h +++ b/src/PhysX/physx/include/task/PxTask.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXTASK_PXTASK_H #define PXTASK_PXTASK_H diff --git a/src/PhysX/physx/include/task/PxTaskDefine.h b/src/PhysX/physx/include/task/PxTaskDefine.h index 935089046..4c439c08d 100644 --- a/src/PhysX/physx/include/task/PxTaskDefine.h +++ b/src/PhysX/physx/include/task/PxTaskDefine.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXTASK_PXTASKDEFINE_H #define PXTASK_PXTASKDEFINE_H diff --git a/src/PhysX/physx/include/task/PxTaskManager.h b/src/PhysX/physx/include/task/PxTaskManager.h index d1738ef0b..278c24fb0 100644 --- a/src/PhysX/physx/include/task/PxTaskManager.h +++ b/src/PhysX/physx/include/task/PxTaskManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXTASK_PXTASKMANAGER_H #define PXTASK_PXTASKMANAGER_H diff --git a/src/PhysX/physx/include/vehicle/PxVehicleComponents.h b/src/PhysX/physx/include/vehicle/PxVehicleComponents.h index 3a85fcbd5..4bf599f48 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleComponents.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleComponents.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDrive.h b/src/PhysX/physx/include/vehicle/PxVehicleDrive.h index 6358c7f84..bacd8a3ea 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleDrive.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleDrive.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -44,7 +44,6 @@ namespace physx struct PxFilterData; class PxGeometry; class PxPhysics; -class PxBatchQuery; class PxVehicleDrivableSurfaceToTireFrictionPairs; class PxShape; class PxMaterial; diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h b/src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h index ec91e7b49..65880a3d3 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleDrive4W.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -46,7 +46,6 @@ namespace physx struct PxFilterData; class PxGeometry; class PxPhysics; -class PxBatchQuery; class PxVehicleDrivableSurfaceToTireFrictionPairs; class PxShape; class PxMaterial; diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h b/src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h index f4a0f84ea..5a358f28d 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleDriveNW.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -45,7 +45,6 @@ namespace physx struct PxFilterData; class PxGeometry; class PxPhysics; -class PxBatchQuery; class PxVehicleDrivableSurfaceToTireFrictionPairs; class PxShape; class PxMaterial; diff --git a/src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h b/src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h index 3e176f124..dae2f2009 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleDriveTank.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -46,7 +46,6 @@ namespace physx struct PxFilterData; class PxGeometry; class PxPhysics; -class PxBatchQuery; class PxVehicleDrivableSurfaceToTireFrictionPairs; class PxShape; class PxMaterial; diff --git a/src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h b/src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h index a92a082e4..6c092c05d 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleNoDrive.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -45,7 +45,6 @@ namespace physx struct PxFilterData; class PxGeometry; class PxPhysics; -class PxBatchQuery; class PxVehicleDrivableSurfaceToTireFrictionPairs; class PxShape; class PxMaterial; diff --git a/src/PhysX/physx/include/vehicle/PxVehicleSDK.h b/src/PhysX/physx/include/vehicle/PxVehicleSDK.h index 01924b203..9568519d9 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleSDK.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleSDK.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/vehicle/PxVehicleShaders.h b/src/PhysX/physx/include/vehicle/PxVehicleShaders.h index 915ab0af5..740a0d692 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleShaders.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleShaders.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h b/src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h index dd4f05c30..855e4de5e 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleTireFriction.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUpdate.h b/src/PhysX/physx/include/vehicle/PxVehicleUpdate.h index 381a01ead..cd8fd5864 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleUpdate.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleUpdate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUtil.h b/src/PhysX/physx/include/vehicle/PxVehicleUtil.h index 8c67751bc..fc6d374c0 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleUtil.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleUtil.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h b/src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h index 3daf06453..d69edacf9 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleUtilControl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUtilSetup.h b/src/PhysX/physx/include/vehicle/PxVehicleUtilSetup.h index e4a1a4c9c..155ddbff5 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleUtilSetup.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleUtilSetup.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/vehicle/PxVehicleUtilTelemetry.h b/src/PhysX/physx/include/vehicle/PxVehicleUtilTelemetry.h index e6b4109d8..08fa26ca3 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleUtilTelemetry.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleUtilTelemetry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/include/vehicle/PxVehicleWheels.h b/src/PhysX/physx/include/vehicle/PxVehicleWheels.h index cd36131b4..3af39112c 100644 --- a/src/PhysX/physx/include/vehicle/PxVehicleWheels.h +++ b/src/PhysX/physx/include/vehicle/PxVehicleWheels.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/platform_readme.html b/src/PhysX/physx/platform_readme.html index 8d9c05553..550b29991 100644 --- a/src/PhysX/physx/platform_readme.html +++ b/src/PhysX/physx/platform_readme.html @@ -7,10 +7,10 @@ - +


                      -Copyright (C) 2008-2018 NVIDIA Corporation, 2701 San Thomas Expressway, Santa Clara, CA 95050 U.S.A. All rights reserved.
                      www.nvidia.com +Copyright (C) 2008-2019 NVIDIA Corporation, 2701 San Thomas Expressway, Santa Clara, CA 95050 U.S.A. All rights reserved. www.nvidia.com

                      diff --git a/src/PhysX/physx/release_notes.html b/src/PhysX/physx/release_notes.html index ea4e7b40f..238aab6a9 100644 --- a/src/PhysX/physx/release_notes.html +++ b/src/PhysX/physx/release_notes.html @@ -28,6 +28,39 @@ +

                      Release Notes - NVIDIA® PhysX® SDK 4.0.0.25635910

                      +

                      January 2019

                      + +

                      General

                      +
                        +
                      • Fixed:
                      • +
                          +
                        • Fixed issue in PxBinaryConverter::convert that could corrupt platform re-targeting of convex meshes with more than 127 vertices.
                        • +
                        • GenerateProject scripts should now also work when not called from PhysX directory.
                        • +
                        • GenerateProject script will now create correct compiler/ directory on Linux based systems.
                        • +
                        • Removed /Wall from MSVC compilers.
                        • +
                        • Fixed CMake install, added missing cudacontextmanager files.
                        • +
                        • Fixed binary serialization of actors in aggregates without serialization of the containing aggregate.
                        • +
                        +
                      • Removed:
                      • +
                          +
                        • CharacterKinematic API export/import macros have been removed.
                        • +
                        +
                      • Added:
                      • +
                          +
                        • Support for Linux samples has been added.
                        • +
                        • PxConfig.h include file will be generated during generate projects script. Including this file in your project will ensure that required defines (like PX_PHYSX_STATIC_LIB) are set.
                        • +
                        +
                      • Changed:
                      • +
                          +
                        • PX_FOUNDATION_API was moved to PhysX and uses PX_PHYSX_STATIC_LIB define as the rest of the SDK.
                        • +
                        • PxAssertHandler moved from PxShared to PhysX and marked as deprecated.
                        • +
                        • PxShared does use PX_SHARED_ASSERT instead of PX_ASSERT which is used just in the PhysX SDK and uses PxAssertHandler.
                        • +
                        +
                      + + +

                      Release Notes - NVIDIA® PhysX® SDK 4.0

                      December 2018

                      @@ -135,6 +168,7 @@
                    • A flag to enable friction constraints to be processed every frame has been added
                    • PxContactJoint added to represent contacts in inverse dynamics. Not intended for use in simulation.
                    • A missing PxShape::getReferenceCount() function has been added.
                    • +
                    • A new flag has been added to PxMaterial to improve friction accuracy. The flag is disabled by default to maintain legacy behavior.
                    • Removed:
                      • @@ -304,7 +338,7 @@ * Console code subject to platform NDA not available on GitHub. Developers licensed by respective platform owners please contact NVIDIA for access. - +

                        Release Notes - NVIDIA® PhysX® SDK 3.4.2.25256367

                        @@ -338,7 +372,7 @@
                      • Fixed a bug in BVH34. Raycasts could fail on binary deserialized BVH34 triangle meshes.
                      - +

                      Release Notes - NVIDIA® PhysX® SDK 3.4.2.24990349

                      @@ -362,7 +396,7 @@
                    • Fixed an incorrect mesh index reported in contact buffer when stabilization flag was used.
                    • - +

                      Release Notes - NVIDIA® PhysX® SDK 3.4.2.24698370

                      @@ -3674,7 +3708,7 @@


                      - Copyright (C) 2008-2018 NVIDIA Corporation, 2701 San Thomas Expressway, Santa Clara, CA 95050 U.S.A. All rights reserved. www.nvidia.com + Copyright (C) 2008-2019 NVIDIA Corporation, 2701 San Thomas Expressway, Santa Clara, CA 95050 U.S.A. All rights reserved. www.nvidia.com

                      diff --git a/src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h b/src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h index 6a99075be..2ced199eb 100644 --- a/src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h +++ b/src/PhysX/physx/source/common/include/windows/CmWindowsLoadLibrary.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h b/src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h index 3afd86db2..a97cf34bb 100644 --- a/src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h +++ b/src/PhysX/physx/source/common/include/windows/CmWindowsModuleUpdateLoader.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmBitMap.h b/src/PhysX/physx/source/common/src/CmBitMap.h index 31c62853b..bbe3f124f 100644 --- a/src/PhysX/physx/source/common/src/CmBitMap.h +++ b/src/PhysX/physx/source/common/src/CmBitMap.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmBlockArray.h b/src/PhysX/physx/source/common/src/CmBlockArray.h index 2e80a9b47..585475bc6 100644 --- a/src/PhysX/physx/source/common/src/CmBlockArray.h +++ b/src/PhysX/physx/source/common/src/CmBlockArray.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmCollection.cpp b/src/PhysX/physx/source/common/src/CmCollection.cpp index 87e9e1ccb..16a06d1bd 100644 --- a/src/PhysX/physx/source/common/src/CmCollection.cpp +++ b/src/PhysX/physx/source/common/src/CmCollection.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmCollection.h b/src/PhysX/physx/source/common/src/CmCollection.h index b80ec256b..6b73366b9 100644 --- a/src/PhysX/physx/source/common/src/CmCollection.h +++ b/src/PhysX/physx/source/common/src/CmCollection.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmConeLimitHelper.h b/src/PhysX/physx/source/common/src/CmConeLimitHelper.h index edd921b79..0423b10e9 100644 --- a/src/PhysX/physx/source/common/src/CmConeLimitHelper.h +++ b/src/PhysX/physx/source/common/src/CmConeLimitHelper.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmFlushPool.h b/src/PhysX/physx/source/common/src/CmFlushPool.h index e9e9ebe47..bc0932c6f 100644 --- a/src/PhysX/physx/source/common/src/CmFlushPool.h +++ b/src/PhysX/physx/source/common/src/CmFlushPool.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmIDPool.h b/src/PhysX/physx/source/common/src/CmIDPool.h index ea848face..20868058f 100644 --- a/src/PhysX/physx/source/common/src/CmIDPool.h +++ b/src/PhysX/physx/source/common/src/CmIDPool.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmIO.h b/src/PhysX/physx/source/common/src/CmIO.h index 5d290a0fb..c81618557 100644 --- a/src/PhysX/physx/source/common/src/CmIO.h +++ b/src/PhysX/physx/source/common/src/CmIO.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmMathUtils.cpp b/src/PhysX/physx/source/common/src/CmMathUtils.cpp index 70c71326e..03806a589 100644 --- a/src/PhysX/physx/source/common/src/CmMathUtils.cpp +++ b/src/PhysX/physx/source/common/src/CmMathUtils.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmMatrix34.h b/src/PhysX/physx/source/common/src/CmMatrix34.h index 01d8f34ea..bb85a1263 100644 --- a/src/PhysX/physx/source/common/src/CmMatrix34.h +++ b/src/PhysX/physx/source/common/src/CmMatrix34.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmPhysXCommon.h b/src/PhysX/physx/source/common/src/CmPhysXCommon.h index 6ff0387c6..946a79e39 100644 --- a/src/PhysX/physx/source/common/src/CmPhysXCommon.h +++ b/src/PhysX/physx/source/common/src/CmPhysXCommon.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmPool.h b/src/PhysX/physx/source/common/src/CmPool.h index a28ea0307..56276d52f 100644 --- a/src/PhysX/physx/source/common/src/CmPool.h +++ b/src/PhysX/physx/source/common/src/CmPool.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -54,26 +54,21 @@ class PoolList : public Ps::AllocatorTraits::Type typedef typename Ps::AllocatorTraits::Type Alloc; PX_NOCOPY(PoolList) public: - PX_INLINE PoolList(const Alloc& alloc, ArgumentType* argument, PxU32 eltsPerSlab, PxU32 maxSlabs) + PX_INLINE PoolList(const Alloc& alloc, ArgumentType* argument, PxU32 eltsPerSlab) : Alloc(alloc), mEltsPerSlab(eltsPerSlab), - mMaxSlabs(maxSlabs), mSlabCount(0), mFreeList(0), mFreeCount(0), - mSlabs(reinterpret_cast(Alloc::allocate(maxSlabs * sizeof(T*), __FILE__, __LINE__))), + mSlabs(NULL), mArgument(argument) { PX_ASSERT(mEltsPerSlab>0); - // either maxSlabs = 1 (non-resizable pool), or elts per slab must be a power of two - PX_ASSERT((maxSlabs==1) || ((maxSlabs < 8192) && (mEltsPerSlab & (mEltsPerSlab-1))) == 0); + PX_ASSERT((mEltsPerSlab & (mEltsPerSlab-1)) == 0); mLog2EltsPerSlab = 0; - if(mMaxSlabs>1) - { - for(mLog2EltsPerSlab=0; mEltsPerSlab!=PxU32(1<= mMaxSlabs) - return nbElements; //Return only nbFromFree because we're not going to allocate any slabs. Seriously, we need to nuke this "maxSlabs" stuff ASAP! - //allocate our slabs... PxU32 freeCount = mFreeCount; @@ -145,17 +136,29 @@ public: if (!mAddr) return nbElements; //Allocation failed so only return the set of elements we could allocate from the free list - mSlabs[mSlabCount++] = mAddr; + PxU32 newSlabCount = mSlabCount+1; // Make sure the usage bitmap is up-to-size - if (mUseBitmap.size() < mSlabCount*mEltsPerSlab) + if (mUseBitmap.size() < newSlabCount*mEltsPerSlab) { - mUseBitmap.resize(2 * mSlabCount*mEltsPerSlab); //set last element as not used + mUseBitmap.resize(2 * newSlabCount*mEltsPerSlab); //set last element as not used if (mFreeList) - PX_FREE(mFreeList); - mFreeList = reinterpret_cast(Alloc::allocate(2 * mSlabCount * mEltsPerSlab * sizeof(T*), __FILE__, __LINE__)); + Alloc::deallocate(mFreeList); + mFreeList = reinterpret_cast(Alloc::allocate(2 * newSlabCount * mEltsPerSlab * sizeof(T*), __FILE__, __LINE__)); + + T** slabs = reinterpret_cast(Alloc::allocate(2* newSlabCount *sizeof(T*), __FILE__, __LINE__)); + if (mSlabs) + { + PxMemCopy(slabs, mSlabs, sizeof(T*)*newSlabCount); + + Alloc::deallocate(mSlabs); + } + + mSlabs = slabs; } + mSlabs[mSlabCount++] = mAddr; + PxU32 baseIndex = (mSlabCount-1) * mEltsPerSlab; //Now add all these to the mFreeList and elements... @@ -217,7 +220,7 @@ public: { if(index>=mSlabCount*mEltsPerSlab || !(mUseBitmap.boundedTest(index))) return 0; - return mMaxSlabs==1 ? mSlabs[0]+index : mSlabs[index>>mLog2EltsPerSlab] + (index&(mEltsPerSlab-1)); + return mSlabs[index>>mLog2EltsPerSlab] + (index&(mEltsPerSlab-1)); } /* @@ -225,29 +228,37 @@ public: */ PX_FORCE_INLINE T* findByIndexFast(PxU32 index) const { - PX_ASSERT(mMaxSlabs != 1); return mSlabs[index>>mLog2EltsPerSlab] + (index&(mEltsPerSlab-1)); } bool extend() { - if(mSlabCount == mMaxSlabs) - return false; T * mAddr = reinterpret_cast(Alloc::allocate(mEltsPerSlab * sizeof(T), __FILE__, __LINE__)); if(!mAddr) return false; - mSlabs[mSlabCount++] = mAddr; - + PxU32 newSlabCount = mSlabCount+1; // Make sure the usage bitmap is up-to-size - if(mUseBitmap.size() < mSlabCount*mEltsPerSlab) + if(mUseBitmap.size() < newSlabCount*mEltsPerSlab) { - mUseBitmap.resize(2*mSlabCount*mEltsPerSlab); //set last element as not used + mUseBitmap.resize(2* newSlabCount*mEltsPerSlab); //set last element as not used if(mFreeList) - PX_FREE(mFreeList); - mFreeList = reinterpret_cast(Alloc::allocate(2*mSlabCount * mEltsPerSlab * sizeof(T*), __FILE__, __LINE__)); + Alloc::deallocate(mFreeList); + mFreeList = reinterpret_cast(Alloc::allocate(2* newSlabCount * mEltsPerSlab * sizeof(T*), __FILE__, __LINE__)); + + T** slabs = reinterpret_cast(Alloc::allocate(2 * newSlabCount * sizeof(T*), __FILE__, __LINE__)); + if (mSlabs) + { + PxMemCopy(slabs, mSlabs, sizeof(T*)*newSlabCount); + + Alloc::deallocate(mSlabs); + } + + mSlabs = slabs; } + + mSlabs[mSlabCount++] = mAddr; // Add to free list in descending order so that lowest indices get allocated first - // the FW context code currently *relies* on this behavior to grab the zero-index volume @@ -275,7 +286,6 @@ public: private: const PxU32 mEltsPerSlab; - const PxU32 mMaxSlabs; PxU32 mSlabCount; PxU32 mLog2EltsPerSlab; T** mFreeList; diff --git a/src/PhysX/physx/source/common/src/CmPreallocatingPool.h b/src/PhysX/physx/source/common/src/CmPreallocatingPool.h index e3b9ece83..704944b55 100644 --- a/src/PhysX/physx/source/common/src/CmPreallocatingPool.h +++ b/src/PhysX/physx/source/common/src/CmPreallocatingPool.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmPriorityQueue.h b/src/PhysX/physx/source/common/src/CmPriorityQueue.h index a1eeef523..ccec56a13 100644 --- a/src/PhysX/physx/source/common/src/CmPriorityQueue.h +++ b/src/PhysX/physx/source/common/src/CmPriorityQueue.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmPtrTable.cpp b/src/PhysX/physx/source/common/src/CmPtrTable.cpp index 2341f0a3b..888fb0bc3 100644 --- a/src/PhysX/physx/source/common/src/CmPtrTable.cpp +++ b/src/PhysX/physx/source/common/src/CmPtrTable.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmPtrTable.h b/src/PhysX/physx/source/common/src/CmPtrTable.h index cc5041d4d..933615656 100644 --- a/src/PhysX/physx/source/common/src/CmPtrTable.h +++ b/src/PhysX/physx/source/common/src/CmPtrTable.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmQueue.h b/src/PhysX/physx/source/common/src/CmQueue.h index 334a530b5..9bba870c9 100644 --- a/src/PhysX/physx/source/common/src/CmQueue.h +++ b/src/PhysX/physx/source/common/src/CmQueue.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmRadixSort.cpp b/src/PhysX/physx/source/common/src/CmRadixSort.cpp index bb0adf40e..baea6e22f 100644 --- a/src/PhysX/physx/source/common/src/CmRadixSort.cpp +++ b/src/PhysX/physx/source/common/src/CmRadixSort.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmRadixSort.h b/src/PhysX/physx/source/common/src/CmRadixSort.h index d8faf4e62..8c7419136 100644 --- a/src/PhysX/physx/source/common/src/CmRadixSort.h +++ b/src/PhysX/physx/source/common/src/CmRadixSort.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmRadixSortBuffered.cpp b/src/PhysX/physx/source/common/src/CmRadixSortBuffered.cpp index 81096309a..d1d701ea3 100644 --- a/src/PhysX/physx/source/common/src/CmRadixSortBuffered.cpp +++ b/src/PhysX/physx/source/common/src/CmRadixSortBuffered.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmRadixSortBuffered.h b/src/PhysX/physx/source/common/src/CmRadixSortBuffered.h index 9e6bcc034..96893592d 100644 --- a/src/PhysX/physx/source/common/src/CmRadixSortBuffered.h +++ b/src/PhysX/physx/source/common/src/CmRadixSortBuffered.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmRefCountable.h b/src/PhysX/physx/source/common/src/CmRefCountable.h index 361437628..06adcaf3d 100644 --- a/src/PhysX/physx/source/common/src/CmRefCountable.h +++ b/src/PhysX/physx/source/common/src/CmRefCountable.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmRenderBuffer.h b/src/PhysX/physx/source/common/src/CmRenderBuffer.h index 062bd0e34..aaffc2a57 100644 --- a/src/PhysX/physx/source/common/src/CmRenderBuffer.h +++ b/src/PhysX/physx/source/common/src/CmRenderBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmRenderOutput.cpp b/src/PhysX/physx/source/common/src/CmRenderOutput.cpp index bc5b78241..9d89b96f9 100644 --- a/src/PhysX/physx/source/common/src/CmRenderOutput.cpp +++ b/src/PhysX/physx/source/common/src/CmRenderOutput.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmRenderOutput.h b/src/PhysX/physx/source/common/src/CmRenderOutput.h index 4662ed1ab..782b5628f 100644 --- a/src/PhysX/physx/source/common/src/CmRenderOutput.h +++ b/src/PhysX/physx/source/common/src/CmRenderOutput.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmScaling.h b/src/PhysX/physx/source/common/src/CmScaling.h index 53ba22345..f76f8053d 100644 --- a/src/PhysX/physx/source/common/src/CmScaling.h +++ b/src/PhysX/physx/source/common/src/CmScaling.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmSpatialVector.h b/src/PhysX/physx/source/common/src/CmSpatialVector.h index 05e7ef57c..9d51d3bd4 100644 --- a/src/PhysX/physx/source/common/src/CmSpatialVector.h +++ b/src/PhysX/physx/source/common/src/CmSpatialVector.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmTask.h b/src/PhysX/physx/source/common/src/CmTask.h index cf28c7da5..681c6eff9 100644 --- a/src/PhysX/physx/source/common/src/CmTask.h +++ b/src/PhysX/physx/source/common/src/CmTask.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmTaskPool.h b/src/PhysX/physx/source/common/src/CmTaskPool.h index 8bb36a71b..3c3e00ea0 100644 --- a/src/PhysX/physx/source/common/src/CmTaskPool.h +++ b/src/PhysX/physx/source/common/src/CmTaskPool.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmTmpMem.h b/src/PhysX/physx/source/common/src/CmTmpMem.h index 9f30304d8..a68c87dbe 100644 --- a/src/PhysX/physx/source/common/src/CmTmpMem.h +++ b/src/PhysX/physx/source/common/src/CmTmpMem.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmTransformUtils.h b/src/PhysX/physx/source/common/src/CmTransformUtils.h index fe70469b6..d3a84887a 100644 --- a/src/PhysX/physx/source/common/src/CmTransformUtils.h +++ b/src/PhysX/physx/source/common/src/CmTransformUtils.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmUtils.h b/src/PhysX/physx/source/common/src/CmUtils.h index c9480bef7..bb7c1d995 100644 --- a/src/PhysX/physx/source/common/src/CmUtils.h +++ b/src/PhysX/physx/source/common/src/CmUtils.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmVisualization.cpp b/src/PhysX/physx/source/common/src/CmVisualization.cpp index 70a54e905..3f263f3b0 100644 --- a/src/PhysX/physx/source/common/src/CmVisualization.cpp +++ b/src/PhysX/physx/source/common/src/CmVisualization.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/CmVisualization.h b/src/PhysX/physx/source/common/src/CmVisualization.h index 982cd8018..67aeb895c 100644 --- a/src/PhysX/physx/source/common/src/CmVisualization.h +++ b/src/PhysX/physx/source/common/src/CmVisualization.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/windows/CmWindowsDelayLoadHook.cpp b/src/PhysX/physx/source/common/src/windows/CmWindowsDelayLoadHook.cpp index a0424983f..5ac2b0e68 100644 --- a/src/PhysX/physx/source/common/src/windows/CmWindowsDelayLoadHook.cpp +++ b/src/PhysX/physx/source/common/src/windows/CmWindowsDelayLoadHook.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp b/src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp index b10cba8d8..c94717c00 100644 --- a/src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp +++ b/src/PhysX/physx/source/common/src/windows/CmWindowsModuleUpdateLoader.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/compiler/cmake/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/CMakeLists.txt index f8747b862..35872d525 100644 --- a/src/PhysX/physx/source/compiler/cmake/CMakeLists.txt +++ b/src/PhysX/physx/source/compiler/cmake/CMakeLists.txt @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. cmake_minimum_required(VERSION 3.7) @@ -132,9 +132,15 @@ SET(CMAKE_POSITION_INDEPENDENT_CODE ON) SET(SOURCE_DISTRO_FILE_LIST "") +SET(HEADER_GUARD_NAME "CONFIG") +SET(HEADER_CONTENT "") + # Include the platform specific CMakeLists INCLUDE(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/CMakeLists.txt) +# generate PxPhysXConfig.h header that will contain PhysX configuration defines like PX_PHYSX_STATIC_LIB +CONFIGURE_FILE(${CMAKE_MODULE_PATH}/template/PxIncludeTemplate.h ${PHYSX_ROOT_DIR}/include/PxConfig.h) + IF(PX_GENERATE_SOURCE_DISTRO) FOREACH(FILE_NAME ${SOURCE_DISTRO_FILE_LIST}) FILE(APPEND "${CMAKE_CURRENT_BINARY_DIR}/source_distro_list.txt" "${FILE_NAME}\n") diff --git a/src/PhysX/physx/source/compiler/cmake/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/FastXml.cmake index 4ac700275..257ddccf5 100644 --- a/src/PhysX/physx/source/compiler/cmake/FastXml.cmake +++ b/src/PhysX/physx/source/compiler/cmake/FastXml.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build FastXml common diff --git a/src/PhysX/physx/source/compiler/cmake/IfTemplate.txt b/src/PhysX/physx/source/compiler/cmake/IfTemplate.txt index 5561e9ae7..4c48e99c1 100644 --- a/src/PhysX/physx/source/compiler/cmake/IfTemplate.txt +++ b/src/PhysX/physx/source/compiler/cmake/IfTemplate.txt @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. IF(TARGET_BUILD_PLATFORM STREQUAL "Windows") ELSEIF(TARGET_BUILD_PLATFORM STREQUAL "PS4") diff --git a/src/PhysX/physx/source/compiler/cmake/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/LowLevel.cmake index 8b97e6b98..aa51b9faf 100644 --- a/src/PhysX/physx/source/compiler/cmake/LowLevel.cmake +++ b/src/PhysX/physx/source/compiler/cmake/LowLevel.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevel common @@ -60,7 +60,6 @@ SET(LL_COMMON_COLLISION_HEADERS ) SOURCE_GROUP("Common Includes\\collision" FILES ${LL_COMMON_COLLISION_HEADERS}) SET(LL_COMMON_PIPELINE_HEADERS - ${LL_COMMON_DIR}/include/pipeline/PxcCCDStateStreamPair.h ${LL_COMMON_DIR}/include/pipeline/PxcConstraintBlockStream.h ${LL_COMMON_DIR}/include/pipeline/PxcContactCache.h ${LL_COMMON_DIR}/include/pipeline/PxcMaterialMethodImpl.h diff --git a/src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake index 1a655d664..b81e3e31e 100644 --- a/src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake +++ b/src/PhysX/physx/source/compiler/cmake/LowLevelAABB.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelAABB common diff --git a/src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake index 0069c413f..b55688cec 100644 --- a/src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake +++ b/src/PhysX/physx/source/compiler/cmake/LowLevelDynamics.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelDynamics common diff --git a/src/PhysX/physx/source/compiler/cmake/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/PhysX.cmake index a4628160e..ddba5ac63 100644 --- a/src/PhysX/physx/source/compiler/cmake/PhysX.cmake +++ b/src/PhysX/physx/source/compiler/cmake/PhysX.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysX (PROJECT not SOLUTION) common diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake index cd9ee9dd6..fc976e31d 100644 --- a/src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake +++ b/src/PhysX/physx/source/compiler/cmake/PhysXCharacterKinematic.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCharacterKinematic common @@ -39,7 +39,6 @@ include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/Ph SET(PHYSXCCT_HEADERS ${PHYSX_ROOT_DIR}/include/characterkinematic/PxBoxController.h ${PHYSX_ROOT_DIR}/include/characterkinematic/PxCapsuleController.h - ${PHYSX_ROOT_DIR}/include/characterkinematic/PxCharacter.h ${PHYSX_ROOT_DIR}/include/characterkinematic/PxController.h ${PHYSX_ROOT_DIR}/include/characterkinematic/PxControllerBehavior.h ${PHYSX_ROOT_DIR}/include/characterkinematic/PxControllerManager.h diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake index 5bcbf8222..aac4599f9 100644 --- a/src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake +++ b/src/PhysX/physx/source/compiler/cmake/PhysXCommon.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCommon common @@ -43,7 +43,6 @@ include(${PHYSX_ROOT_DIR}/${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/Ph SET(PHYSX_COMMON_SOURCE - ${COMMON_SRC_DIR}/CmBoxPruning.cpp ${COMMON_SRC_DIR}/CmCollection.cpp ${COMMON_SRC_DIR}/CmMathUtils.cpp ${COMMON_SRC_DIR}/CmPtrTable.cpp @@ -53,7 +52,6 @@ SET(PHYSX_COMMON_SOURCE ${COMMON_SRC_DIR}/CmVisualization.cpp ${COMMON_SRC_DIR}/CmBitMap.h ${COMMON_SRC_DIR}/CmBlockArray.h - ${COMMON_SRC_DIR}/CmBoxPruning.h ${COMMON_SRC_DIR}/CmCollection.h ${COMMON_SRC_DIR}/CmConeLimitHelper.h ${COMMON_SRC_DIR}/CmFlushPool.h @@ -137,7 +135,6 @@ SET(PHYSXCOMMON_COLLISION_HEADERS SOURCE_GROUP(include\\collision FILES ${PHYSXCOMMON_COLLISION_HEADERS}) SET(PHYSXCOMMON_GU_HEADERS - ${GU_SOURCE_DIR}/include/GuAxes.h ${GU_SOURCE_DIR}/include/GuBox.h ${GU_SOURCE_DIR}/include/GuDistanceSegmentBox.h ${GU_SOURCE_DIR}/include/GuDistanceSegmentSegment.h diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake index 4a7719cc9..b452a4904 100644 --- a/src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake +++ b/src/PhysX/physx/source/compiler/cmake/PhysXCooking.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCooking common diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake index aeb868943..50454d3d8 100644 --- a/src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake +++ b/src/PhysX/physx/source/compiler/cmake/PhysXExtensions.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXExtensions common diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake index fb93058f3..8b164baca 100644 --- a/src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake +++ b/src/PhysX/physx/source/compiler/cmake/PhysXFoundation.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXFoundation common @@ -40,11 +40,19 @@ SET(PHYSXFOUNDATION_HEADERS ) SOURCE_GROUP(include FILES ${PHYSXFOUNDATION_HEADERS}) +SET(PHYSXFOUNDATION_HEADERS_2 + ${PHYSX_ROOT_DIR}/include/foundation/PxAssert.h + ${PHYSX_ROOT_DIR}/include/foundation/PxFoundationConfig.h + ${PHYSX_ROOT_DIR}/include/foundation/PxMathUtils.h +) +SOURCE_GROUP(include\\foundation FILES ${PHYSXFOUNDATION_HEADERS_2}) + + SET(PXSHARED_HEADERS ${PXSHARED_PATH}/include/foundation/Px.h ${PXSHARED_PATH}/include/foundation/PxAllocatorCallback.h ${PXSHARED_PATH}/include/foundation/PxProfiler.h - ${PXSHARED_PATH}/include/foundation/PxAssert.h + ${PXSHARED_PATH}/include/foundation/PxSharedAssert.h ${PXSHARED_PATH}/include/foundation/PxBitAndData.h ${PXSHARED_PATH}/include/foundation/PxBounds3.h ${PXSHARED_PATH}/include/foundation/PxErrorCallback.h @@ -54,8 +62,7 @@ SET(PXSHARED_HEADERS ${PXSHARED_PATH}/include/foundation/PxIO.h ${PXSHARED_PATH}/include/foundation/PxMat33.h ${PXSHARED_PATH}/include/foundation/PxMat44.h - ${PXSHARED_PATH}/include/foundation/PxMath.h - ${PXSHARED_PATH}/include/foundation/PxMathUtils.h + ${PXSHARED_PATH}/include/foundation/PxMath.h ${PXSHARED_PATH}/include/foundation/PxMemory.h ${PXSHARED_PATH}/include/foundation/PxPlane.h ${PXSHARED_PATH}/include/foundation/PxPreprocessor.h @@ -131,6 +138,7 @@ ADD_LIBRARY(PhysXFoundation ${PHYSXFOUNDATION_LIBTYPE} ${PHYSXFOUNDATION_SOURCE} ${PHYSXFOUNDATION_SOURCE_HEADERS} ${PHYSXFOUNDATION_HEADERS} + ${PHYSXFOUNDATION_HEADERS_2} ${PHYSXFOUNDATION_PLATFORM_FILES} ${PXSHARED_HEADERS} ${PXSHARED_PLATFORM_HEADERS} @@ -138,6 +146,7 @@ ADD_LIBRARY(PhysXFoundation ${PHYSXFOUNDATION_LIBTYPE} # Add the headers to the install INSTALL(FILES ${PHYSXFOUNDATION_HEADERS} DESTINATION include) +INSTALL(FILES ${PHYSXFOUNDATION_HEADERS_2} DESTINATION include/foundation) INSTALL(FILES ${PHYSXFOUNDATION_SOURCE_HEADERS} DESTINATION source/foundation/include) INSTALL(FILES ${PXSHARED_HEADERS} DESTINATION ${PXSHARED_INSTALL_PREFIX}/include/foundation) @@ -189,6 +198,7 @@ IF(PX_GENERATE_SOURCE_DISTRO) LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_SOURCE}) LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_SOURCE_HEADERS}) LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_HEADERS}) + LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_HEADERS_2}) LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PHYSXFOUNDATION_PLATFORM_FILES}) LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PXSHARED_HEADERS}) LIST(APPEND SOURCE_DISTRO_FILE_LIST ${PXSHARED_PLATFORM_HEADERS}) diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake index aad0e7143..470393df6 100644 --- a/src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake +++ b/src/PhysX/physx/source/compiler/cmake/PhysXPvdSDK.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXPvdSDK common diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake index 61596f3d9..b9c032e04 100644 --- a/src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake +++ b/src/PhysX/physx/source/compiler/cmake/PhysXTask.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXTask common diff --git a/src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake index 64ac6ff85..0e2fb4b3a 100644 --- a/src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake +++ b/src/PhysX/physx/source/compiler/cmake/PhysXVehicle.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXVehicle common diff --git a/src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake index c73b20b24..feca1ba78 100644 --- a/src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake +++ b/src/PhysX/physx/source/compiler/cmake/SceneQuery.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SceneQuery common diff --git a/src/PhysX/physx/source/compiler/cmake/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/SimulationController.cmake index 577a75d38..219f6ab2e 100644 --- a/src/PhysX/physx/source/compiler/cmake/SimulationController.cmake +++ b/src/PhysX/physx/source/compiler/cmake/SimulationController.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SimulationController common diff --git a/src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt index b53ca716b..b4b2e55b4 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt +++ b/src/PhysX/physx/source/compiler/cmake/android/CMakeLists.txt @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # We define the CXX flags for this CMakeLists and all others that are included afterwards. This is a GLOBAL setting. # If/when the solutions go standalone (say, samples and visual tests) - those CMakeLists will need to be fixed. @@ -92,3 +92,19 @@ INCLUDE(SceneQuery.cmake) INCLUDE(SimulationController.cmake) INCLUDE(FastXml.cmake) +# Set folder PhysX SDK to all common SDK source projects +SET_PROPERTY(TARGET PhysX PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXCharacterKinematic PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXCommon PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXCooking PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXExtensions PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXVehicle PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET LowLevel PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET LowLevelAABB PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET LowLevelDynamics PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET SceneQuery PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET SimulationController PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET FastXml PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXPvdSDK PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXTask PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXFoundation PROPERTY FOLDER "PhysX SDK") \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake index 7ba5b42da..b2788efb9 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/FastXml.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build FastXml @@ -31,7 +31,7 @@ SET(FASTXML_COMPILE_DEFS # Common to all configurations - ${PHYSX_ANDROID_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + ${PHYSX_ANDROID_COMPILE_DEFS}; $<$:${PHYSX_ANDROID_DEBUG_COMPILE_DEFS};> $<$:${PHYSX_ANDROID_CHECKED_COMPILE_DEFS};> diff --git a/src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake index 8190dcae3..beb3c6ed0 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/LowLevel.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevel diff --git a/src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake index 8438d7a2a..304444b80 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/LowLevelAABB.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelAABB diff --git a/src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake index 3c0478908..3bd069f9a 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/LowLevelDynamics.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelDynamics diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake index be1c03fa1..fc6e1142e 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysX.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysX (PROJECT not SOLUTION) diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake index 083c4a81c..6c91a11b1 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXCharacterKinematic.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCharacterKinematic diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake index 33846420f..cb30e4edd 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXCommon.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCommon diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake index ea13629c4..9dd497f85 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXCooking.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCooking diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake index a75161f1b..4b263095c 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXExtensions.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXExtensions diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake index 3a403b252..74015cb5a 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXFoundation.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXFoundation diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake index a38d1580c..9c89d43bd 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXPvdSDK.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXPvdSDK diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake index 350cf7961..e9974430f 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXTask.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXTask diff --git a/src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake index c3e9bee77..f268b5d3a 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/PhysXVehicle.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXVehicle diff --git a/src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake index 03e10a690..23cb7648a 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/SceneQuery.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SceneQuery diff --git a/src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake index 36d435d58..bdd42a3a6 100644 --- a/src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake +++ b/src/PhysX/physx/source/compiler/cmake/android/SimulationController.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SimulationController diff --git a/src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt index bcbaf2284..4a9e55d0c 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt +++ b/src/PhysX/physx/source/compiler/cmake/ios/CMakeLists.txt @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. SET(PHYSX_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -ferror-limit=0 -Wall -Wextra -Werror -fstrict-aliasing -Wstrict-aliasing=2 -Weverything -Wno-unknown-warning-option -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-undefined-reinterpret-cast -Wno-invalid-offsetof -Wno-zero-as-null-pointer-constant -gdwarf-2" CACHE INTERAL "PhysX CXX") @@ -74,3 +74,19 @@ INCLUDE(SceneQuery.cmake) INCLUDE(SimulationController.cmake) INCLUDE(FastXml.cmake) +# Set folder PhysX SDK to all common SDK source projects +SET_PROPERTY(TARGET PhysX PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXCharacterKinematic PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXCommon PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXCooking PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXExtensions PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXVehicle PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET LowLevel PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET LowLevelAABB PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET LowLevelDynamics PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET SceneQuery PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET SimulationController PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET FastXml PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXPvdSDK PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXTask PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXFoundation PROPERTY FOLDER "PhysX SDK") \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake index 299dec64b..47fec399b 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/FastXml.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build FastXml @@ -33,7 +33,7 @@ SET(FASTXML_COMPILE_DEFS # Common to all configurations - ${PHYSX_IOS_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + ${PHYSX_IOS_COMPILE_DEFS}; $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> diff --git a/src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake index 9c0d07f0a..4a569f1f4 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/LowLevel.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevel diff --git a/src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake index 5ba12ba7a..75977873a 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelAABB.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelAABB diff --git a/src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake index f2747d0e0..96158a006 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/LowLevelDynamics.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelDynamics diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake index fe635d5ec..057cbd871 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysX.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysX (PROJECT not SOLUTION) diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake index af2d60b58..3dd134e03 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCharacterKinematic.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCharacterKinematic @@ -34,7 +34,7 @@ SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS # Common to all configurations - ${PHYSX_IOS_COMPILE_DEFS};PX_PHYSX_CHARACTER_EXPORTS;PX_PHYSX_CORE_EXPORTS;PX_FOUNDATION_DLL=1; + ${PHYSX_IOS_COMPILE_DEFS}; $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake index b19f22570..a88a3bcbb 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCommon.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCommon @@ -40,7 +40,7 @@ SET(PXCOMMON_PLATFORM_INCLUDES SET(PXCOMMON_COMPILE_DEFS # Common to all configurations - ${PHYSX_IOS_COMPILE_DEFS};PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS; + ${PHYSX_IOS_COMPILE_DEFS}; # Switch platforms here? $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS}> diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake index 73e6d26b2..1a76dc1e3 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXCooking.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCooking diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake index 773941eea..84f729f8e 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXExtensions.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXExtensions diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake index d047086fa..d44cc573c 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXFoundation.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXFoundation diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake index ba98d234e..a19143ff6 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXPvdSDK.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXPvdSDK diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake index 687e798ac..343de5606 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXTask.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXTask @@ -31,7 +31,7 @@ SET(PHYSXTASK_COMPILE_DEFS - ${PHYSX_IOS_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + ${PHYSX_IOS_COMPILE_DEFS}; $<$:${PHYSX_IOS_DEBUG_COMPILE_DEFS};> $<$:${PHYSX_IOS_CHECKED_COMPILE_DEFS};> $<$:${PHYSX_IOS_PROFILE_COMPILE_DEFS};> diff --git a/src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake index e5e5a477e..26de80fb6 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/PhysXVehicle.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXVehicle diff --git a/src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake index f00560aa4..7df44d20e 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/SceneQuery.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SceneQuery diff --git a/src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake index bf3309e65..68045f96f 100644 --- a/src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake +++ b/src/PhysX/physx/source/compiler/cmake/ios/SimulationController.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SimulationController diff --git a/src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt index 65ba87305..85465dc32 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt +++ b/src/PhysX/physx/source/compiler/cmake/linux/CMakeLists.txt @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. STRING(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWERCASE) @@ -81,6 +81,16 @@ IF(PX_GENERATE_GPU_PROJECTS_ONLY) INCLUDE(PhysXCudaContextManager.cmake) INCLUDE(PhysXArticulationGpu.cmake) INCLUDE(PhysXGpu.cmake) # must be last + + SET_PROPERTY(TARGET PhysXBroadphaseGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXCommonGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXNarrowphaseGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXSimulationControllerGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXArticulationGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXSolverGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET CloudGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXCudaContextManager PROPERTY FOLDER "PhysX SDK/GPU") ELSE() INCLUDE(PhysXFoundation.cmake) INCLUDE(LowLevel.cmake) @@ -97,6 +107,23 @@ ELSE() INCLUDE(FastXml.cmake) INCLUDE(PhysXPvdSDK.cmake) INCLUDE(PhysXTask.cmake) + + # Set folder PhysX SDK to all common SDK source projects + SET_PROPERTY(TARGET PhysX PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXCharacterKinematic PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXCommon PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXCooking PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXExtensions PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXVehicle PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET LowLevel PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET LowLevelAABB PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET LowLevelDynamics PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET SceneQuery PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET SimulationController PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET FastXml PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXPvdSDK PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXTask PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXFoundation PROPERTY FOLDER "PhysX SDK") SET(PHYSXDISTRO_LIBS PhysXFoundation PhysX PhysXCharacterKinematic PhysXPvdSDK PhysXCommon PhysXCooking PhysXExtensions PhysXVehicle) @@ -110,6 +137,16 @@ ELSE() INCLUDE(PhysXCudaContextManager.cmake) INCLUDE(PhysXArticulationGpu.cmake) INCLUDE(PhysXGpu.cmake) # must be last + + SET_PROPERTY(TARGET PhysXBroadphaseGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXCommonGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXNarrowphaseGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXSimulationControllerGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXArticulationGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXSolverGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET CloudGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXCudaContextManager PROPERTY FOLDER "PhysX SDK/GPU") LIST(APPEND PHYSXDISTRO_LIBS PhysXGpu) ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake index e2377741e..f78d11aaf 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/FastXml.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build FastXml diff --git a/src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake index 20b7eddf9..83f977b06 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/LowLevel.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevel diff --git a/src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake index 5ff3a9592..3ae9488a2 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelAABB.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelAABB diff --git a/src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake index e7cdc1d63..4472e1085 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/LowLevelDynamics.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelDynamics diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake index 5ab23e0d2..0e324d99c 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysX.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysX (PROJECT not SOLUTION) @@ -34,6 +34,13 @@ SET(PHYSX_GPU_HEADERS ) SOURCE_GROUP(include\\gpu FILES ${PHYSX_GPU_HEADERS}) +SET(PHYSX_CUDACONTEXT_MANAGER_GPU_HEADERS + ${PHYSX_ROOT_DIR}/include/cudamanager/PxCudaContextManager.h + ${PHYSX_ROOT_DIR}/include/cudamanager/PxCudaMemoryManager.h + ${PHYSX_ROOT_DIR}/include/cudamanager/PxGpuCopyDesc.h + ${PHYSX_ROOT_DIR}/include/cudamanager/PxGpuCopyDescQueue.h +) +SOURCE_GROUP(include\\cudamanager FILES ${PHYSX_CUDACONTEXT_MANAGER_GPU_HEADERS}) SET(PHYSX_PLATFORM_INCLUDES ${NVTOOLSEXT_INCLUDE_DIRS} @@ -57,6 +64,7 @@ SET(PHYSX_PLATFORM_SRC_FILES ) INSTALL(FILES ${PHYSX_GPU_HEADERS} DESTINATION include/gpu) +INSTALL(FILES ${PHYSX_CUDACONTEXT_MANAGER_GPU_HEADERS} DESTINATION include/cudamanager) IF(PX_GENERATE_STATIC_LIBRARIES) SET(PHYSX_LIBTYPE STATIC) diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake index daa63e5cd..28d849663 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCharacterKinematic.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCharacterKinematic @@ -33,7 +33,7 @@ SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS # Common to all configurations - ${PHYSX_LINUX_COMPILE_DEFS};PX_PHYSX_CHARACTER_STATIC_LIB;PX_PHYSX_STATIC_LIB; + ${PHYSX_LINUX_COMPILE_DEFS}; $<$:${PHYSX_LINUX_DEBUG_COMPILE_DEFS};> $<$:${PHYSX_LINUX_CHECKED_COMPILE_DEFS};> diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake index 6471c8b81..b00625579 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCommon.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCommon @@ -45,7 +45,7 @@ IF(PX_GENERATE_STATIC_LIBRARIES) SET(PHYSXCOMMON_LIBTYPE STATIC) ELSE() SET(PXCOMMON_LIBTYPE_DEFS - PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS; + PX_PHYSX_FOUNDATION_EXPORTS;PX_PHYSX_COMMON_EXPORTS; ) SET(PHYSXCOMMON_LIBTYPE SHARED) ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake index 5f11a3f5d..e65bc3a39 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXCooking.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCooking @@ -41,7 +41,7 @@ IF(PX_GENERATE_STATIC_LIBRARIES) ELSE() SET(PHYSXCOOKING_LIBTYPE SHARED) SET(PXCOOKING_LIBTYPE_DEFS - PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_CORE_EXPORTS + PX_PHYSX_FOUNDATION_EXPORTS;PX_PHYSX_COMMON_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_CORE_EXPORTS ) ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake index 8e5831592..3a3456112 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXExtensions.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXExtensions diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake index b4520e7fd..e3e6b08a8 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXFoundation.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXFoundation @@ -35,7 +35,7 @@ ELSE() SET(PHYSXFOUNDATION_LIBTYPE SHARED) SET(PHYSXFOUNDATION_PLATFORM_LINKED_LIBS rt) SET(PXFOUNDATION_LIBTYPE_DEFS - PX_FOUNDATION_DLL=1; + PX_PHYSX_FOUNDATION_EXPORTS; ) ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake index 164ca1f83..74060fd66 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXPvdSDK.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXPvdSDK diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake index a896acfb4..1e8ddfb1c 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXTask.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXTask diff --git a/src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake index 048d2ea02..c1686f3d9 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/PhysXVehicle.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXVehicle diff --git a/src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake index 6a9540b6a..a3b57aef1 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/SceneQuery.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SceneQuery diff --git a/src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake index a15ea443a..f543e50fe 100644 --- a/src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake +++ b/src/PhysX/physx/source/compiler/cmake/linux/SimulationController.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SimulationController diff --git a/src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt index 9e9618c62..af43d8471 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt +++ b/src/PhysX/physx/source/compiler/cmake/mac/CMakeLists.txt @@ -23,18 +23,10 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. - -OPTION(PX_32BIT "Specifies 32bit compilation flags." OFF) -OPTION(PX_64BIT "Specifies 64bit compilation flags." OFF) - -IF (PX_32BIT) - SET(OSX_BITNESS "-arch i386") -ELSE() - SET(OSX_BITNESS "-arch x86_64") - SET(CMAKE_OSX_ARCHITECTURES "x86_64") -ENDIF() +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. +SET(OSX_BITNESS "-arch x86_64") +SET(CMAKE_OSX_ARCHITECTURES "x86_64") SET(PHYSX_CXX_FLAGS "${OSX_BITNESS} -msse2 -std=c++11 -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections -Werror -ferror-limit=0 -Wall -Wextra -fstrict-aliasing -Wstrict-aliasing=2 -Weverything -Wno-unknown-warning-option -Wno-documentation-deprecated-sync -Wno-documentation-unknown-command -Wno-float-equal -Wno-padded -Wno-weak-vtables -Wno-cast-align -Wno-conversion -Wno-missing-noreturn -Wno-missing-variable-declarations -Wno-shift-sign-overflow -Wno-covered-switch-default -Wno-exit-time-destructors -Wno-global-constructors -Wno-missing-prototypes -Wno-unreachable-code -Wno-unused-macros -Wno-unused-member-function -Wno-used-but-marked-unused -Wno-weak-template-vtables -Wno-deprecated -Wno-non-virtual-dtor -Wno-invalid-noreturn -Wno-return-type-c-linkage -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -Wno-unused-local-typedef -Wno-old-style-cast -Wno-newline-eof -Wno-unused-private-field -Wno-undefined-reinterpret-cast -Wno-invalid-offsetof -Wno-zero-as-null-pointer-constant -gdwarf-2" CACHE INTERAL "PhysX CXX") @@ -89,4 +81,19 @@ INCLUDE(SceneQuery.cmake) INCLUDE(SimulationController.cmake) INCLUDE(FastXml.cmake) - +# Set folder PhysX SDK to all common SDK source projects +SET_PROPERTY(TARGET PhysX PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXCharacterKinematic PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXCommon PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXCooking PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXExtensions PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXVehicle PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET LowLevel PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET LowLevelAABB PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET LowLevelDynamics PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET SceneQuery PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET SimulationController PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET FastXml PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXPvdSDK PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXTask PROPERTY FOLDER "PhysX SDK") +SET_PROPERTY(TARGET PhysXFoundation PROPERTY FOLDER "PhysX SDK") diff --git a/src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake index ac4c4d53e..15d7fa312 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/FastXml.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build FastXml @@ -34,7 +34,7 @@ SET(FASTXML_COMPILE_DEFS # Common to all configurations - ${PHYSX_MAC_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + ${PHYSX_MAC_COMPILE_DEFS}; $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> diff --git a/src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake index b314a2f0c..1a6e92595 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/LowLevel.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevel diff --git a/src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake index fbec014b1..af413cf4a 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelAABB.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelAABB diff --git a/src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake index 9069816b2..060d6573b 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/LowLevelDynamics.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelDynamics diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake index 731c18224..954580850 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysX.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysX (PROJECT not SOLUTION) diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake index 055eb7911..bca24f5d0 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCharacterKinematic.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCharacterKinematic diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake index 739565130..2f5c96653 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCommon.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCommon diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake index 9e882ef83..142a12119 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXCooking.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCooking diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake index b4c15ac27..46b465b92 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXExtensions.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXExtensions diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake index 256293a02..1a3e836b7 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXFoundation.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXFoundation @@ -33,7 +33,7 @@ IF(PX_GENERATE_STATIC_LIBRARIES) SET(PHYSXFOUNDATION_LIBTYPE STATIC) ELSE() SET(PHYSXFOUNDATION_LIBTYPE SHARED) - SET(PHYSXFOUNDATION_SHARED_LIBRARY_DEFS PX_FOUNDATION_DLL=1;) + SET(PXFOUNDATION_LIBTYPE_DEFS PX_PHYSX_FOUNDATION_EXPORTS) ENDIF() SET(PXSHARED_PLATFORM_HEADERS @@ -93,7 +93,7 @@ SET(PHYSXFOUNDATION_PLATFORM_FILES SET(PHYSXFOUNDATION_COMPILE_DEFS # Common to all configurations - ${PHYSX_MAC_COMPILE_DEFS} + ${PHYSX_MAC_COMPILE_DEFS};${PXFOUNDATION_LIBTYPE_DEFS}; $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS}> $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS}> diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake index 1f0506c85..d1e9e0d62 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXPvdSDK.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXPvdSDK diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake index ec71f0c54..66cfc9f00 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXTask.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXTask @@ -31,7 +31,7 @@ SET(PHYSXTASK_COMPILE_DEFS - ${PHYSX_MAC_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + ${PHYSX_MAC_COMPILE_DEFS}; $<$:${PHYSX_MAC_DEBUG_COMPILE_DEFS};> $<$:${PHYSX_MAC_CHECKED_COMPILE_DEFS};> $<$:${PHYSX_MAC_PROFILE_COMPILE_DEFS};> diff --git a/src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake index c81d171e3..554c7ad54 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/PhysXVehicle.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXVehicle diff --git a/src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake index 6222fe3a0..10fb1a6fd 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/SceneQuery.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SceneQuery diff --git a/src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake index 6c811d569..164ae475d 100644 --- a/src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake +++ b/src/PhysX/physx/source/compiler/cmake/mac/SimulationController.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SimulationController diff --git a/src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt b/src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt index db4d0457d..0416c42e7 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt +++ b/src/PhysX/physx/source/compiler/cmake/windows/CMakeLists.txt @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. OPTION(PX_COPY_EXTERNAL_DLL "Copy external dlls into SDK bin directory" OFF) OPTION(PX_FLOAT_POINT_PRECISE_MATH "Float point precise math" OFF) @@ -41,9 +41,9 @@ ELSE() SET(PHYSX_FP_MODE "/fp:fast") ENDIF() IF(CMAKE_CL_64) - SET(PHYSX_CXX_FLAGS "/Wall /d2Zi+ /MP /WX /W4 /GF /GS- /GR- /Gd ${PHYSX_FP_MODE} /Oy ${PHYSX_WARNING_DISABLES}" CACHE INTERNAL "PhysX CXX") + SET(PHYSX_CXX_FLAGS "/d2Zi+ /MP /WX /W4 /GF /GS- /GR- /Gd ${PHYSX_FP_MODE} /Oy ${PHYSX_WARNING_DISABLES}" CACHE INTERNAL "PhysX CXX") ELSE() - SET(PHYSX_CXX_FLAGS "/Wall /arch:SSE2 /d2Zi+ /MP /WX /W4 /GF /GS- /GR- /Gd ${PHYSX_FP_MODE} /Oy ${PHYSX_WARNING_DISABLES}" CACHE INTERNAL "PhysX CXX") + SET(PHYSX_CXX_FLAGS "/arch:SSE2 /d2Zi+ /MP /WX /W4 /GF /GS- /GR- /Gd ${PHYSX_FP_MODE} /Oy ${PHYSX_WARNING_DISABLES}" CACHE INTERNAL "PhysX CXX") ENDIF() SET(PHYSX_CXX_FLAGS_DEBUG "/Od ${WINCRT_DEBUG} /RTCu /Zi" CACHE INTERNAL "PhysX Debug CXX Flags") @@ -53,7 +53,7 @@ SET(PHYSX_CXX_FLAGS_RELEASE "/Ox ${WINCRT_NDEBUG} /Zi" CACHE INTERNAL "PhysX Rel # cache lib type defs IF(PX_GENERATE_STATIC_LIBRARIES) - SET(PHYSX_LIBTYPE_DEFS "PX_PHYSX_STATIC_LIB;PX_FOUNDATION_DLL=0;" CACHE INTERNAL "PhysX lib type defs") + SET(PHYSX_LIBTYPE_DEFS "PX_PHYSX_STATIC_LIB;" CACHE INTERNAL "PhysX lib type defs") ENDIF() IF(PX_GENERATE_GPU_STATIC_LIBRARIES) SET(PHYSXGPU_LIBTYPE_DEFS "PX_PHYSX_GPU_STATIC;" CACHE INTERNAL "PhysX GPU lib type defs") @@ -163,6 +163,18 @@ IF(PX_GENERATE_GPU_PROJECTS_ONLY) INCLUDE(PhysXCudaContextManager.cmake) INCLUDE(PhysXArticulationGpu.cmake) INCLUDE(PhysXGpu.cmake) # must be the last PhysXGPU + + SET_PROPERTY(TARGET PhysXFoundation PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXCommon PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXBroadphaseGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXCommonGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXNarrowphaseGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXSimulationControllerGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXArticulationGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXSolverGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET CloudGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXCudaContextManager PROPERTY FOLDER "PhysX SDK/GPU") SET(PHYSXDISTRO_LIBS PhysXGpu) INSTALL( @@ -186,6 +198,23 @@ ELSE() INCLUDE(FastXml.cmake) INCLUDE(PhysXPvdSDK.cmake) INCLUDE(PhysXTask.cmake) + + # Set folder PhysX SDK to all common SDK source projects + SET_PROPERTY(TARGET PhysX PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXCharacterKinematic PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXCommon PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXCooking PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXExtensions PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXVehicle PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET LowLevel PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET LowLevelAABB PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET LowLevelDynamics PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET SceneQuery PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET SimulationController PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET FastXml PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXPvdSDK PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXTask PROPERTY FOLDER "PhysX SDK") + SET_PROPERTY(TARGET PhysXFoundation PROPERTY FOLDER "PhysX SDK") IF(PX_GENERATE_STATIC_LIBRARIES) SET(PHYSXDISTRO_LIBS PhysXFoundation PhysX PhysXCharacterKinematic PhysXPvdSDK PhysXCommon PhysXCooking PhysXExtensions PhysXVehicle) @@ -204,6 +233,16 @@ ELSE() INCLUDE(PhysXArticulationGpu.cmake) INCLUDE(PhysXGpu.cmake) # must be the last PhysXGPU + SET_PROPERTY(TARGET PhysXBroadphaseGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXCommonGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXNarrowphaseGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXSimulationControllerGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXArticulationGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXSolverGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET CloudGpu PROPERTY FOLDER "PhysX SDK/GPU") + SET_PROPERTY(TARGET PhysXCudaContextManager PROPERTY FOLDER "PhysX SDK/GPU") + LIST(APPEND PHYSXDISTRO_LIBS PhysXGpu) ENDIF() @@ -214,3 +253,7 @@ ELSE() ) ENDIF() + +IF(PX_GENERATE_STATIC_LIBRARIES) + STRING(APPEND HEADER_CONTENT "#define PX_PHYSX_STATIC_LIB\n") +ENDIF() \ No newline at end of file diff --git a/src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake b/src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake index da1153c3c..1b86595ef 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/FastXml.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build FastXml @@ -32,7 +32,7 @@ # Use generator expressions to set config specific preprocessor definitions SET(FASTXML_COMPILE_DEFS # Common to all configurations - ${PHYSX_WINDOWS_COMPILE_DEFS};PX_FOUNDATION_DLL=0; + ${PHYSX_WINDOWS_COMPILE_DEFS}; $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> diff --git a/src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake b/src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake index 7b037d3a6..3c0b1b3cb 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/LowLevel.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevel diff --git a/src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake index 2efd43b8b..cea99a4eb 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelAABB.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelAABB diff --git a/src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake index facc9890d..daa3afcbb 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/LowLevelDynamics.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build LowLevelDynamics diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake index aada387ab..3f8572df9 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysX.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysX (PROJECT not SOLUTION) @@ -44,10 +44,18 @@ SET(PHYSX_GPU_HEADERS ) SOURCE_GROUP(include\\gpu FILES ${PHYSX_GPU_HEADERS}) +SET(PHYSX_CUDACONTEXT_MANAGER_GPU_HEADERS + ${PHYSX_ROOT_DIR}/include/cudamanager/PxCudaContextManager.h + ${PHYSX_ROOT_DIR}/include/cudamanager/PxCudaMemoryManager.h + ${PHYSX_ROOT_DIR}/include/cudamanager/PxGpuCopyDesc.h + ${PHYSX_ROOT_DIR}/include/cudamanager/PxGpuCopyDescQueue.h +) +SOURCE_GROUP(include\\cudamanager FILES ${PHYSX_CUDACONTEXT_MANAGER_GPU_HEADERS}) + SET(PHYSX_COMMON_WINDOWS_HEADERS ${PHYSX_ROOT_DIR}/include/common/windows/PxWindowsDelayLoadHook.h ) -SOURCE_GROUP(include\\gpu FILES ${PHYSX_GPU_HEADERS}) +SOURCE_GROUP(include\\common\\windows FILES ${PHYSX_COMMON_WINDOWS_HEADERS}) SET(PHYSX_RESOURCE ${PHYSX_SOURCE_DIR}/compiler/resource_${RESOURCE_LIBPATH_SUFFIX}/PhysX.rc @@ -86,6 +94,7 @@ ENDIF() SET(PHYSX_PLATFORM_SRC_FILES ${PHYSX_GPU_HEADERS} + ${PHYSX_CUDACONTEXT_MANAGER_GPU_HEADERS} ${PHYSX_RESOURCE} ${PHYSX_COMMON_WINDOWS_HEADERS} @@ -98,6 +107,7 @@ SET(PHYSX_PLATFORM_SRC_FILES ) INSTALL(FILES ${PHYSX_GPU_HEADERS} DESTINATION include/gpu) +INSTALL(FILES ${PHYSX_CUDACONTEXT_MANAGER_GPU_HEADERS} DESTINATION include/cudamanager) INSTALL(FILES ${PHYSX_COMMON_WINDOWS_HEADERS} DESTINATION include/common/windows) IF(NV_USE_GAMEWORKS_OUTPUT_DIRS) @@ -111,7 +121,7 @@ ENDIF() IF(NOT PX_GENERATE_STATIC_LIBRARIES) SET(PXPHYSX_LIBTYPE_DEFS - PX_FOUNDATION_DLL=1;PX_PHYSX_CORE_EXPORTS; + PX_PHYSX_FOUNDATION_EXPORTS;PX_PHYSX_CORE_EXPORTS; ) ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake index 0519de2d7..5eaf4b40c 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCharacterKinematic.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCharacterKinematic @@ -35,7 +35,7 @@ SET(PHYSXCHARACTERKINEMATIC_LIBTYPE STATIC) SET(PHYSXCHARACTERKINEMATICS_COMPILE_DEFS # Common to all configurations - ${PHYSX_WINDOWS_COMPILE_DEFS};PX_PHYSX_CHARACTER_STATIC_LIB;${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} + ${PHYSX_WINDOWS_COMPILE_DEFS};${PHYSX_LIBTYPE_DEFS};${PHYSXGPU_LIBTYPE_DEFS} $<$:${PHYSX_WINDOWS_DEBUG_COMPILE_DEFS};> $<$:${PHYSX_WINDOWS_CHECKED_COMPILE_DEFS};> diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake index 572f0db76..76575853b 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCommon.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCommon @@ -58,7 +58,7 @@ SET(PXCOMMON_PLATFORM_INCLUDES IF(NOT PX_GENERATE_STATIC_LIBRARIES) SET(PXCOMMON_LIBTYPE_DEFS - PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS; + PX_PHYSX_COMMON_EXPORTS; ) ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake index 13c254ac4..85254ec0e 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXCooking.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXCooking @@ -31,7 +31,7 @@ IF(NOT PX_GENERATE_STATIC_LIBRARIES) SET(PXCOOKING_LIBTYPE_DEFS - PX_FOUNDATION_DLL=1;PX_PHYSX_COMMON_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_CORE_EXPORTS + PX_PHYSX_FOUNDATION_EXPORTS;PX_PHYSX_COMMON_EXPORTS;PX_PHYSX_COOKING_EXPORTS;PX_PHYSX_LOADER_EXPORTS;PX_PHYSX_CORE_EXPORTS ) ENDIF() diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake index 6fec964d7..213193206 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXExtensions.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXExtensions diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake index a4ca7b522..4f4990ca3 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXFoundation.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXFoundation @@ -33,7 +33,7 @@ IF(PX_GENERATE_STATIC_LIBRARIES) SET(PHYSXFOUNDATION_LIBTYPE STATIC) ELSE() SET(PHYSXFOUNDATION_LIBTYPE SHARED) - SET(PHYSXFOUNDATION_SHARED_LIBRARY_DEFS PX_FOUNDATION_DLL=1;) + SET(PXFOUNDATION_LIBTYPE_DEFS PX_PHYSX_FOUNDATION_EXPORTS) ENDIF() SET(PXSHARED_PLATFORM_HEADERS @@ -89,12 +89,6 @@ SET(PHYSXFOUNDATION_PLATFORM_INCLUDES ${LL_SOURCE_DIR}/include/windows ) -IF(NOT PX_GENERATE_STATIC_LIBRARIES) - SET(PXFOUNDATION_LIBTYPE_DEFS - PX_FOUNDATION_DLL=1; - ) -ENDIF() - SET(PHYSXFOUNDATION_COMPILE_DEFS # Common to all configurations ${PHYSX_WINDOWS_COMPILE_DEFS};_WINSOCK_DEPRECATED_NO_WARNINGS;${PXFOUNDATION_LIBTYPE_DEFS};${PHYSX_LIBTYPE_DEFS}; diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake index 06cb95478..90c6efc6d 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXPvdSDK.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXPvdSDK diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake index dcd1013e5..cfffc737e 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXTask.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXTask diff --git a/src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake b/src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake index 3c8eec283..cee25b335 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/PhysXVehicle.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build PhysXVehicle diff --git a/src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake b/src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake index 76604a4dc..351495ed3 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/SceneQuery.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SceneQuery diff --git a/src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake b/src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake index 856d9a23a..d6179ac5f 100644 --- a/src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake +++ b/src/PhysX/physx/source/compiler/cmake/windows/SimulationController.cmake @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. # # Build SimulationController diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysX.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysX.rc index e93c4a969..fb4252f22 100644 --- a/src/PhysX/physx/source/compiler/resource_x64/PhysX.rc +++ b/src/PhysX/physx/source/compiler/resource_x64/PhysX.rc @@ -70,7 +70,7 @@ BEGIN VALUE "FileDescription", "PhysX 64bit Dynamic Link Library" VALUE "FileVersion", "4.0.0.0" VALUE "InternalName", "PhysX_64" - VALUE "LegalCopyright", "Copyright (C) 2018 NVIDIA Corporation" + VALUE "LegalCopyright", "Copyright (C) 2019 NVIDIA Corporation" VALUE "OriginalFilename", "PhysX_64.dll" VALUE "ProductName", "PhysX" VALUE "ProductVersion", "4.0.0.0" diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc index 5d8a51c48..7782d962e 100644 --- a/src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc +++ b/src/PhysX/physx/source/compiler/resource_x64/PhysXCommon.rc @@ -70,7 +70,7 @@ BEGIN VALUE "FileDescription", "PhysXCommon 64bit Dynamic Link Library" VALUE "FileVersion", "4.0.0.0" VALUE "InternalName", "PhysXCommon_64" - VALUE "LegalCopyright", "Copyright (C) 2018 NVIDIA Corporation" + VALUE "LegalCopyright", "Copyright (C) 2019 NVIDIA Corporation" VALUE "OriginalFilename", "PhysXCommon_64.dll" VALUE "ProductName", "PhysX" VALUE "ProductVersion", "4.0.0.0" diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc index 577b8060d..235effbb2 100644 --- a/src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc +++ b/src/PhysX/physx/source/compiler/resource_x64/PhysXCooking.rc @@ -73,7 +73,7 @@ BEGIN VALUE "FileDescription", "PhysXCooking 64bit Dynamic Link Library" VALUE "FileVersion", "4.0.0.0" VALUE "InternalName", "PhysXCooking_64" - VALUE "LegalCopyright", "Copyright (C) 2018 NVIDIA Corporation" + VALUE "LegalCopyright", "Copyright (C) 2019 NVIDIA Corporation" VALUE "OriginalFilename", "PhysXCooking_64.dll" VALUE "ProductName", "PhysX" VALUE "ProductVersion", "4.0.0.0" diff --git a/src/PhysX/physx/source/compiler/resource_x64/PhysXFoundation.rc b/src/PhysX/physx/source/compiler/resource_x64/PhysXFoundation.rc index f2e1701b63d2ddabaf25bd782049b201c35e4eae..16a0ff05f4a482a59a36cd598c22bd23b832854c 100644 GIT binary patch delta 22 ecmZ3YvPfma1Rh4q$rE{6C-?D+Y}VoZ!2$qZ1PAm0 delta 20 ccmZ3avP5OW1Rh3<$rE{68I3oK^766*07?1< namespace physx diff --git a/src/PhysX/physx/source/foundation/include/PsSync.h b/src/PhysX/physx/source/foundation/include/PsSync.h index f8d73b36f..8f03850f6 100644 --- a/src/PhysX/physx/source/foundation/include/PsSync.h +++ b/src/PhysX/physx/source/foundation/include/PsSync.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsTempAllocator.h b/src/PhysX/physx/source/foundation/include/PsTempAllocator.h index 6594bafad..02540bb8f 100644 --- a/src/PhysX/physx/source/foundation/include/PsTempAllocator.h +++ b/src/PhysX/physx/source/foundation/include/PsTempAllocator.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsThread.h b/src/PhysX/physx/source/foundation/include/PsThread.h index c9304ca7a..615a2a416 100644 --- a/src/PhysX/physx/source/foundation/include/PsThread.h +++ b/src/PhysX/physx/source/foundation/include/PsThread.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsTime.h b/src/PhysX/physx/source/foundation/include/PsTime.h index 078e45365..8602362d5 100644 --- a/src/PhysX/physx/source/foundation/include/PsTime.h +++ b/src/PhysX/physx/source/foundation/include/PsTime.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -31,6 +31,7 @@ #define PSFOUNDATION_PSTIME_H #include "Ps.h" +#include "foundation/PxFoundationConfig.h" #if PX_LINUX || PX_ANDROID #include diff --git a/src/PhysX/physx/source/foundation/include/PsUserAllocated.h b/src/PhysX/physx/source/foundation/include/PsUserAllocated.h index 1ca80f193..d4aaf54e8 100644 --- a/src/PhysX/physx/source/foundation/include/PsUserAllocated.h +++ b/src/PhysX/physx/source/foundation/include/PsUserAllocated.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsUtilities.h b/src/PhysX/physx/source/foundation/include/PsUtilities.h index d991be1fd..11c5aef58 100644 --- a/src/PhysX/physx/source/foundation/include/PsUtilities.h +++ b/src/PhysX/physx/source/foundation/include/PsUtilities.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsVecMath.h b/src/PhysX/physx/source/foundation/include/PsVecMath.h index 680c350c6..322baa862 100644 --- a/src/PhysX/physx/source/foundation/include/PsVecMath.h +++ b/src/PhysX/physx/source/foundation/include/PsVecMath.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h index 8607565c8..4af1798fc 100644 --- a/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h +++ b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalar.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h index e88f79c9b..28e86ffb7 100644 --- a/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h +++ b/src/PhysX/physx/source/foundation/include/PsVecMathAoSScalarInline.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathSSE.h b/src/PhysX/physx/source/foundation/include/PsVecMathSSE.h index cc292caf8..a062b07da 100644 --- a/src/PhysX/physx/source/foundation/include/PsVecMathSSE.h +++ b/src/PhysX/physx/source/foundation/include/PsVecMathSSE.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h b/src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h index a00a944d6..b19d65479 100644 --- a/src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h +++ b/src/PhysX/physx/source/foundation/include/PsVecMathUtilities.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsVecQuat.h b/src/PhysX/physx/source/foundation/include/PsVecQuat.h index 8eb3ea487..ac665efd3 100644 --- a/src/PhysX/physx/source/foundation/include/PsVecQuat.h +++ b/src/PhysX/physx/source/foundation/include/PsVecQuat.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/PsVecTransform.h b/src/PhysX/physx/source/foundation/include/PsVecTransform.h index d9da8d0e4..1bb5ed6f0 100644 --- a/src/PhysX/physx/source/foundation/include/PsVecTransform.h +++ b/src/PhysX/physx/source/foundation/include/PsVecTransform.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h index 02fa34a25..06ac5f2bc 100644 --- a/src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixAoS.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h index 3a2138fea..86ae3c8e8 100644 --- a/src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixFPU.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -43,7 +43,6 @@ #include #endif - PX_INLINE physx::shdfnd::SIMDGuard::SIMDGuard() { #if !PX_EMSCRIPTEN && (PX_X86 || PX_X64) @@ -63,7 +62,7 @@ PX_INLINE physx::shdfnd::SIMDGuard::~SIMDGuard() } #else -#error No SIMD implementation for this unix platform. + #error No SIMD implementation for this unix platform. #endif // PX_LINUX || PX_PS4 || PX_OSX #endif // #ifndef PSFOUNDATION_PSUNIXFPU_H diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h index 9c14ea9f8..29ddac91d 100644 --- a/src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixInlineAoS.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h index b69b57c54..c5e819b10 100644 --- a/src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixIntrinsics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h b/src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h index 8753a24b8..058143eb1 100644 --- a/src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h +++ b/src/PhysX/physx/source/foundation/include/unix/PsUnixTrigConstants.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h index fc90c5235..5ca956003 100644 --- a/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h +++ b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonAoS.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h index 2db302e93..d73195075 100644 --- a/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h +++ b/src/PhysX/physx/source/foundation/include/unix/neon/PsUnixNeonInlineAoS.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h index 102746281..9259432d5 100644 --- a/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h +++ b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2AoS.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h index 9899af581..c22946bbb 100644 --- a/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h +++ b/src/PhysX/physx/source/foundation/include/unix/sse2/PsUnixSse2InlineAoS.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h index 2a2d71063..253720093 100644 --- a/src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsAoS.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h index 019bfc9a0..1235b400a 100644 --- a/src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsFPU.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h index 4dab60145..c002ab5f9 100644 --- a/src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInclude.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h index 887535d4a..b53673e57 100644 --- a/src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsInlineAoS.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h index b8c837d9c..980601c9a 100644 --- a/src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsIntrinsics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h b/src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h index f622cfe41..83de7ade6 100644 --- a/src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h +++ b/src/PhysX/physx/source/foundation/include/windows/PsWindowsTrigConstants.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/PsAllocator.cpp b/src/PhysX/physx/source/foundation/src/PsAllocator.cpp index 8ffff92ba..a084c98ea 100644 --- a/src/PhysX/physx/source/foundation/src/PsAllocator.cpp +++ b/src/PhysX/physx/source/foundation/src/PsAllocator.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/PsAssert.cpp b/src/PhysX/physx/source/foundation/src/PsAssert.cpp index a6e5a6143..fdcc50a2f 100644 --- a/src/PhysX/physx/source/foundation/src/PsAssert.cpp +++ b/src/PhysX/physx/source/foundation/src/PsAssert.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/PsFoundation.cpp b/src/PhysX/physx/source/foundation/src/PsFoundation.cpp index f2edf3f87..ad11facee 100644 --- a/src/PhysX/physx/source/foundation/src/PsFoundation.cpp +++ b/src/PhysX/physx/source/foundation/src/PsFoundation.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -83,7 +83,6 @@ Foundation& Foundation::getInstance() void Foundation::setInstance(Foundation& foundation) { - PX_ASSERT(mInstance == NULL || &foundation == mInstance); mInstance = &foundation; } diff --git a/src/PhysX/physx/source/foundation/src/PsMathUtils.cpp b/src/PhysX/physx/source/foundation/src/PsMathUtils.cpp index 9aa30690b..58584a211 100644 --- a/src/PhysX/physx/source/foundation/src/PsMathUtils.cpp +++ b/src/PhysX/physx/source/foundation/src/PsMathUtils.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/PsString.cpp b/src/PhysX/physx/source/foundation/src/PsString.cpp index 1916fb0df..ad244687b 100644 --- a/src/PhysX/physx/source/foundation/src/PsString.cpp +++ b/src/PhysX/physx/source/foundation/src/PsString.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp b/src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp index f174168ed..a8f3bb3ff 100644 --- a/src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp +++ b/src/PhysX/physx/source/foundation/src/PsTempAllocator.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/PsUtilities.cpp b/src/PhysX/physx/source/foundation/src/PsUtilities.cpp index 1d1e174ba..06ea3a5f0 100644 --- a/src/PhysX/physx/source/foundation/src/PsUtilities.cpp +++ b/src/PhysX/physx/source/foundation/src/PsUtilities.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp index 9ca9249cf..8880d279e 100644 --- a/src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixAtomic.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "Ps.h" diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp index dda248c39..062f86551 100644 --- a/src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixCpu.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp index b6fc57c40..0a3a4b2b5 100644 --- a/src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixFPU.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "PsFPU.h" diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp index 194e23ff3..b523d619d 100644 --- a/src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixMutex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp index a19eff11e..31db6bdee 100644 --- a/src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixPrintString.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp index a3b1e2e68..612dd5dc2 100644 --- a/src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixSList.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp index 0d273cc9d..41c07cacc 100644 --- a/src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixSocket.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp index 9336fd3e7..d6a5c47c1 100644 --- a/src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixSync.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp index a868512ad..1c292c10d 100644 --- a/src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixThread.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp b/src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp index e1fc32c1d..22479f304 100644 --- a/src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp +++ b/src/PhysX/physx/source/foundation/src/unix/PsUnixTime.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp index d8ebca2da..f7b672bc2 100644 --- a/src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsAtomic.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp index 75c308333..234352b2e 100644 --- a/src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsCpu.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp index a8575a062..3d1ed8751 100644 --- a/src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsFPU.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "PsFPU.h" diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp index 45cbeafea..f3c6a38ee 100644 --- a/src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsMutex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp index 50a83eb46..adf5b8314 100644 --- a/src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsPrintString.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp index 76647cc9c..7f7fdd7d6 100644 --- a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSList.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp index b50afa8e0..4a2b64bf0 100644 --- a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSocket.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp index b3675dc7a..9e96e174e 100644 --- a/src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsSync.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp index 243c501dc..e4b5060a3 100644 --- a/src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsThread.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp b/src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp index 71d5e41c6..772aa39c2 100644 --- a/src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp +++ b/src/PhysX/physx/source/foundation/src/windows/PsWindowsTime.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/include/GuBox.h b/src/PhysX/physx/source/geomutils/include/GuBox.h index bdd96e3ea..539ae738d 100644 --- a/src/PhysX/physx/source/geomutils/include/GuBox.h +++ b/src/PhysX/physx/source/geomutils/include/GuBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h index ab28b9268..039914378 100644 --- a/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h +++ b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h index 65202aaee..8c0622be8 100644 --- a/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h +++ b/src/PhysX/physx/source/geomutils/include/GuDistanceSegmentSegment.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h b/src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h index 1b1b8eee2..a53ed2bb4 100644 --- a/src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h +++ b/src/PhysX/physx/source/geomutils/include/GuIntersectionBoxBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h index 160ccd66f..d643751b5 100644 --- a/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h +++ b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h index e136c8ed7..714700228 100644 --- a/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h +++ b/src/PhysX/physx/source/geomutils/include/GuIntersectionTriangleBoxRef.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/include/GuRaycastTests.h b/src/PhysX/physx/source/geomutils/include/GuRaycastTests.h index e54e65b71..bb862657c 100644 --- a/src/PhysX/physx/source/geomutils/include/GuRaycastTests.h +++ b/src/PhysX/physx/source/geomutils/include/GuRaycastTests.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h b/src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h index a28f39f82..c15dd1802 100644 --- a/src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h +++ b/src/PhysX/physx/source/geomutils/include/GuSIMDHelpers.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/include/GuSegment.h b/src/PhysX/physx/source/geomutils/include/GuSegment.h index bcee652c5..433eab6aa 100644 --- a/src/PhysX/physx/source/geomutils/include/GuSegment.h +++ b/src/PhysX/physx/source/geomutils/include/GuSegment.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp index 1d9ae9dcc..39ad61345 100644 --- a/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h index 93dfb7f1a..05034140a 100644 --- a/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h +++ b/src/PhysX/physx/source/geomutils/src/GuAABBTreeBuild.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h b/src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h index 2fbc91c95..4938f9690 100644 --- a/src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h +++ b/src/PhysX/physx/source/geomutils/src/GuAABBTreeQuery.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp index b565da2b6..449e89898 100644 --- a/src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuBVHStructure.h b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.h index cf8b94706..d5f816b22 100644 --- a/src/PhysX/physx/source/geomutils/src/GuBVHStructure.h +++ b/src/PhysX/physx/source/geomutils/src/GuBVHStructure.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h b/src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h index 903b4416a..2ad12f9af 100644 --- a/src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h +++ b/src/PhysX/physx/source/geomutils/src/GuBVHTestsSIMD.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuBounds.cpp b/src/PhysX/physx/source/geomutils/src/GuBounds.cpp index 38ee4c779..aec9eb4d2 100644 --- a/src/PhysX/physx/source/geomutils/src/GuBounds.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuBounds.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuBounds.h b/src/PhysX/physx/source/geomutils/src/GuBounds.h index 6e8293f05..643d9eb58 100644 --- a/src/PhysX/physx/source/geomutils/src/GuBounds.h +++ b/src/PhysX/physx/source/geomutils/src/GuBounds.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuBox.cpp b/src/PhysX/physx/source/geomutils/src/GuBox.cpp index 53c173708..51d8882c6 100644 --- a/src/PhysX/physx/source/geomutils/src/GuBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp b/src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp index 08751a946..c5040ac69 100644 --- a/src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuCCTSweepTests.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuCapsule.cpp b/src/PhysX/physx/source/geomutils/src/GuCapsule.cpp index f0572a9ea..b61f4d99d 100644 --- a/src/PhysX/physx/source/geomutils/src/GuCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuCapsule.h b/src/PhysX/physx/source/geomutils/src/GuCapsule.h index 157bb9984..23890ec7d 100644 --- a/src/PhysX/physx/source/geomutils/src/GuCapsule.h +++ b/src/PhysX/physx/source/geomutils/src/GuCapsule.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuCenterExtents.h b/src/PhysX/physx/source/geomutils/src/GuCenterExtents.h index f81e0a278..56fde2f6b 100644 --- a/src/PhysX/physx/source/geomutils/src/GuCenterExtents.h +++ b/src/PhysX/physx/source/geomutils/src/GuCenterExtents.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuGeometryQuery.cpp b/src/PhysX/physx/source/geomutils/src/GuGeometryQuery.cpp index f0b10899c..23287953e 100644 --- a/src/PhysX/physx/source/geomutils/src/GuGeometryQuery.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuGeometryQuery.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp index 69b74f74f..117440652 100644 --- a/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h index 621a2f8d1..d43fe688f 100644 --- a/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h +++ b/src/PhysX/physx/source/geomutils/src/GuGeometryUnion.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuInternal.cpp b/src/PhysX/physx/source/geomutils/src/GuInternal.cpp index 8cfca77c1..21dd5bcac 100644 --- a/src/PhysX/physx/source/geomutils/src/GuInternal.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuInternal.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuInternal.h b/src/PhysX/physx/source/geomutils/src/GuInternal.h index cdab8a730..97f2cda6e 100644 --- a/src/PhysX/physx/source/geomutils/src/GuInternal.h +++ b/src/PhysX/physx/source/geomutils/src/GuInternal.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuMTD.cpp b/src/PhysX/physx/source/geomutils/src/GuMTD.cpp index 599254609..c5f0bbc7a 100644 --- a/src/PhysX/physx/source/geomutils/src/GuMTD.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuMTD.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuMTD.h b/src/PhysX/physx/source/geomutils/src/GuMTD.h index 572ca9437..aed1cd557 100644 --- a/src/PhysX/physx/source/geomutils/src/GuMTD.h +++ b/src/PhysX/physx/source/geomutils/src/GuMTD.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp index bd422fc6c..657c131d9 100644 --- a/src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuMeshFactory.h b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.h index e17ca335f..75e449de7 100644 --- a/src/PhysX/physx/source/geomutils/src/GuMeshFactory.h +++ b/src/PhysX/physx/source/geomutils/src/GuMeshFactory.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuMetaData.cpp b/src/PhysX/physx/source/geomutils/src/GuMetaData.cpp index 2b312630e..54bb6f39e 100644 --- a/src/PhysX/physx/source/geomutils/src/GuMetaData.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuMetaData.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp index 80fab6271..e0ed10b3f 100644 --- a/src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuOverlapTests.h b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.h index dd59a44c2..02db81314 100644 --- a/src/PhysX/physx/source/geomutils/src/GuOverlapTests.h +++ b/src/PhysX/physx/source/geomutils/src/GuOverlapTests.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp b/src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp index aba0a6b91..7587490bf 100644 --- a/src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuRaycastTests.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuSerialize.cpp b/src/PhysX/physx/source/geomutils/src/GuSerialize.cpp index 83205d8a2..4508b0c2f 100644 --- a/src/PhysX/physx/source/geomutils/src/GuSerialize.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuSerialize.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuSerialize.h b/src/PhysX/physx/source/geomutils/src/GuSerialize.h index 7bc92bdab..88ae4f42e 100644 --- a/src/PhysX/physx/source/geomutils/src/GuSerialize.h +++ b/src/PhysX/physx/source/geomutils/src/GuSerialize.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuSphere.h b/src/PhysX/physx/source/geomutils/src/GuSphere.h index c249f4e21..cb1f06660 100644 --- a/src/PhysX/physx/source/geomutils/src/GuSphere.h +++ b/src/PhysX/physx/source/geomutils/src/GuSphere.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp index d2e8cb917..bcc684e58 100644 --- a/src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepMTD.h b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.h index 9c63c9c2f..40f05dd77 100644 --- a/src/PhysX/physx/source/geomutils/src/GuSweepMTD.h +++ b/src/PhysX/physx/source/geomutils/src/GuSweepMTD.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp index f5b3ab00c..66b93c56b 100644 --- a/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h index 81c1ad2bd..563c018ca 100644 --- a/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h +++ b/src/PhysX/physx/source/geomutils/src/GuSweepSharedTests.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp b/src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp index 8f213b38b..fe4a46ee1 100644 --- a/src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp +++ b/src/PhysX/physx/source/geomutils/src/GuSweepTests.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/GuSweepTests.h b/src/PhysX/physx/source/geomutils/src/GuSweepTests.h index a77fcd3d1..3abf54869 100644 --- a/src/PhysX/physx/source/geomutils/src/GuSweepTests.h +++ b/src/PhysX/physx/source/geomutils/src/GuSweepTests.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.cpp b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.cpp index d4ee6d785..5c053fbac 100644 --- a/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h index 70eecc954..ba2d6cc22 100644 --- a/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h +++ b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepConvexMesh.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp index 8f6b1401b..ded00a31b 100644 --- a/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp +++ b/src/PhysX/physx/source/geomutils/src/ccd/GuCCDSweepPrimitives.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp index a072a3918..2dfb05d6c 100644 --- a/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp +++ b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h index 4dea0543a..171d26d59 100644 --- a/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h +++ b/src/PhysX/physx/source/geomutils/src/common/GuBarycentricCoordinates.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h b/src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h index 5d2c01c9e..bacf001c7 100644 --- a/src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h +++ b/src/PhysX/physx/source/geomutils/src/common/GuBoxConversion.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h b/src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h index 3729fc600..feb981b9c 100644 --- a/src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h +++ b/src/PhysX/physx/source/geomutils/src/common/GuEdgeCache.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h b/src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h index c32b58c94..b791ebdc5 100644 --- a/src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h +++ b/src/PhysX/physx/source/geomutils/src/common/GuEdgeListData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp b/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp index 8f20642b2..fe168fddf 100644 --- a/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp +++ b/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.h b/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.h index df2048cc3..4063ad78f 100644 --- a/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.h +++ b/src/PhysX/physx/source/geomutils/src/common/GuSeparatingAxes.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactBoxBox.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactBoxBox.cpp index 1af188211..f02badde0 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactBoxBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactBoxBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp index c2502147f..654c248ee 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp index 13ce6e029..16d63878a 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp index 772f04f7b..67f996a57 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp index cc56351c1..8aa2b896a 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactCapsuleMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp index e5ed03c3e..4c2f58d1f 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp index 2f70453f8..f5ff88ab3 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactConvexMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h b/src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h index 1a73901c6..a0b5d9875 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactMethodImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp index 533ddb65b..d9380589f 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp index bfde6cf2f..aa13c7567 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp index 28a84855d..712ee2545 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPlaneConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp index 740c77cf4..2d0567d35 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h index 7789b5af1..f25f64e47 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactPolygonPolygon.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp index 722364c44..20e394219 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp index 54d7c4d93..d098dbb86 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp index 2c1eeea81..23837e2d3 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp index 470b2d586..f6a5c3685 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSpherePlane.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp index d8ffdb4b9..bcf11a0cc 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuContactSphereSphere.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp index d0bc0ecf1..be56074fd 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp +++ b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h index ab1e0cb8e..5ead64493 100644 --- a/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h +++ b/src/PhysX/physx/source/geomutils/src/contact/GuFeatureCode.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp index 52e94931d..5f676cff3 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp +++ b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h index 90af247ec..673bebbfb 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h index 9fc5d717f..5de9651b4 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuBigConvexData2.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h index 9756e3de6..8d021a419 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexEdgeFlags.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp index 53fee1171..33f4d94a2 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.h index d264bac91..d90354294 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexHelper.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp index feb66f8da..c4630db8e 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h index 9a7d08ed6..d271c5b1e 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMesh.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h index 12e311914..f212558e1 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexMeshData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp index 564f68cbb..82fbf3fa3 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h index 1147ec982..8f802debd 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexSupportTable.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp index 851110773..5fa63ba94 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h index d7b2226a8..7791051c2 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuConvexUtilsInternal.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h b/src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h index 2001ad736..8e52ef920 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuCubeIndex.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp index cb03c9953..c14e503e8 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp +++ b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h index 0db865e86..bfe041de6 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuHillClimbing.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp index 4b089626d..4f838cecc 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h index 8d6f81d0b..5374b0597 100644 --- a/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h +++ b/src/PhysX/physx/source/geomutils/src/convex/GuShapeConvex.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp index e564e083e..bdaf6d39b 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h index 8ebcc3e51..fe7504d5b 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h index a441cde18..902212b02 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointSegment.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp index b86a49bf8..3b8b1085f 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h index 279991f54..51e5cb4c8 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangle.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h index 587952c87..d2babe517 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistancePointTriangleSIMD.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp index f97b039cf..94e78592d 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp index 3e97cc884..ff3385d5a 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegment.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h index f5d6394ac..d3be35e34 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentSegmentSIMD.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp index c2ab4e964..bfbd4cef8 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h index f91778702..268cbefb9 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangle.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h index 1c9938943..40cb1c76b 100644 --- a/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h +++ b/src/PhysX/physx/source/geomutils/src/distance/GuDistanceSegmentTriangleSIMD.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp index f45df41c9..480535bdf 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h index 4ae929579..851ea6f7d 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuEPA.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h b/src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h index 44de1afb6..583f0d7e7 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuEPAFacet.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h index 015005e76..03c1ae149 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJK.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h index 23fb003d0..63dfd4b04 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKPenetration.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h index 79c04931c..5394ba1a8 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKRaycast.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp index 9dc648cf5..e5005f86f 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h index 415d94070..b5d6c652b 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKSimplex.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp index 3e252d511..215a69077 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h index 412b9a438..3d75b6658 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKTest.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h index 6856a2611..646b1cdba 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKType.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h index b83364d0c..de3d81e47 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuGJKUtil.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h index d8e3d167e..ff97dee89 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecCapsule.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecCapsule.h index fdc17131f..cf16c5a3a 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuVecCapsule.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecCapsule.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h index 4133bc1e9..fe38a6b8b 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvex.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h index ff675fdb7..1c7d9d7c0 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHull.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHullNoScale.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHullNoScale.h index 7273e52dc..4b6ede5a0 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHullNoScale.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecConvexHullNoScale.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h index ca95a2d87..04e3667e6 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecPlane.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h index 95845b192..304209dd3 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecSphere.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h b/src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h index 6dd676ab5..30225d26c 100644 --- a/src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h +++ b/src/PhysX/physx/source/geomutils/src/gjk/GuVecTriangle.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuEntityReport.h b/src/PhysX/physx/source/geomutils/src/hf/GuEntityReport.h index 94c072a7a..d34a4a4f4 100644 --- a/src/PhysX/physx/source/geomutils/src/hf/GuEntityReport.h +++ b/src/PhysX/physx/source/geomutils/src/hf/GuEntityReport.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp index d7e795d61..779b39a3e 100644 --- a/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h index 7a1d36330..93b5465af 100644 --- a/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightField.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h index 2e165936d..4b589759e 100644 --- a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp index d4e9b9bb8..88f9491d2 100644 --- a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h index 18e52420a..391a18b12 100644 --- a/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h +++ b/src/PhysX/physx/source/geomutils/src/hf/GuHeightFieldUtil.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp index 4b14b885e..1a2ea3092 100644 --- a/src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp +++ b/src/PhysX/physx/source/geomutils/src/hf/GuOverlapTestsHF.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp b/src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp index 32e400b0a..4b58317c6 100644 --- a/src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp +++ b/src/PhysX/physx/source/geomutils/src/hf/GuSweepsHF.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp index 120a81c42..6e091676a 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionBoxBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp index 2cbb0eb5c..929971d0a 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h index 72c67d6ba..919bdf1fd 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionCapsuleTriangle.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp index 4a5be9d0e..9c3a05d55 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h index d9a4cd7c6..b0492256e 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionEdgeEdge.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h index 2e120457e..ef9e005e0 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRay.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp index 3b6c3fe95..5922a05c3 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.h index 965648436..29bbb41f3 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.h +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBoxSIMD.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBoxSIMD.h index 3d95493c1..6c82f4552 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBoxSIMD.h +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayBoxSIMD.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.cpp index 3eaee1790..b5aaf884a 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h index bac7c0b52..a25d4b045 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayCapsule.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h index 0418f1f49..7696d63b1 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayPlane.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp index d6244b714..57a0c7b4f 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h index f97d892a8..388cffc14 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRaySphere.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h index 25c081f5a..3455c4750 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionRayTriangle.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp index 26638001b..1abaecbe6 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h index e787c39d0..605623d5d 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionSphereBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp index 854fe53e0..bd0fe8a48 100644 --- a/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/intersection/GuIntersectionTriangleBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp index e049f883e..4b1fcb85c 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h index 1ac5d53c2..d70011e05 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp index 2f2e633f4..74f37975a 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h index 7c03e8193..9488f460b 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV32Build.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp index 8d7896148..5e553e704 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h index 098205a5e..30972e192 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp index 6cf6efabc..b7cf2c8f6 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.h index 2581388c9..586976f36 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Build.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h index f0dca6e4a..41202caf3 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4Settings.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h index a6cdd6978..c7be39f68 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBAABBSweepTest.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp index 17188acfa..d83644832 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_AABBSweep.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h index 29bd6a888..9e274d214 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxBoxOverlapTest.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp index 64603e3ba..616cedd75 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h index 5d89f5012..194ac8385 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxOverlap_Internal.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h index a5ba47544..1540ce38d 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Internal.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h index a80e18e8c..aa8c5f8c9 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_BoxSweep_Params.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp index f0ee08a08..2d7d6de21 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp index ea1d77bf9..1bb8c74fb 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweepAA.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h index 276fe4e8c..cb08a2725 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_CapsuleSweep_Internal.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h index 3ba60c972..b48a8e629 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Common.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h index 5f24bdcbb..1222989b8 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Internal.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp index fb87e71ce..ffaf0fa2a 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_OBBSweep.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h index 47093e431..9e37a0d43 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_OBBOBB.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h index 327c4105e..42f15bc0a 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h index a613ffdbd..0945e68f6 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SegmentAABB_Inflated.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h index bce32cf01..87c8f0b22 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamNoOrder_SphereAABB.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h index ce1ec85ff..eebdb5e32 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_OBBOBB.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h index 44787078b..0abaa8d42 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h index bd926d72f..13bbe8153 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_ProcessStreamOrdered_SegmentAABB_Inflated.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Raycast.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Raycast.cpp index fd8ce82a5..332b8b3ea 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Raycast.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Raycast.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h index 23ed4288e..cb91bd943 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h index a36f76d97..1c17f50f2 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaNoOrder.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h index e9d3ec241..b04731b65 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_KajiyaOrdered.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h index decaebad4..2f408688c 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledNoOrder.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h index c00d72037..1a7cd67b9 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_Slabs_SwizzledOrdered.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp index 362ca6135..113a5fdc4 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereOverlap.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp index 8250997ed..829f9cb9d 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBV4_SphereSweep.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h b/src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h index 7363fa62d..a72f4441d 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuBVConstants.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h index bd4fc0dfc..dbdbddf4b 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp index c4e2be499..58d9c2a33 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMeshQuery.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp index 0e141cbc1..9b279e6f4 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseBV4.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h index d9574476b..eeecee872 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseInterface.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp index f88f55381..e7838cbef 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuMidphaseRTree.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp index bc5ced894..d420f1529 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuOverlapTestsMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp index 7034f90c3..b602cc01e 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h index 4cd3ad9c5..11f7604f4 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuRTree.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp index 4030ba0d7..eeaacc9c0 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuRTreeQueries.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h index d6d803174..b5e58c388 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepConvexTri.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h index 7d568acb9..6d7b113de 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepMesh.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp index b4028ef8c..6b056c203 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuSweepsMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h index 421198706..908dbcc25 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangle32.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h index 5c46b8857..d176409de 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleCache.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp index dfe813423..1dc0d8ec5 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h index 20bdb5c4b..9e1a01446 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMesh.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp index 71945ef5e..4fe609312 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h index 558e2debd..a93a5c110 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshBV4.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp index 3c2710c4c..9eb44a87e 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h index 168281546..566643a8e 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleMeshRTree.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h index e32534bff..096fb1921 100644 --- a/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h +++ b/src/PhysX/physx/source/geomutils/src/mesh/GuTriangleVertexPointers.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp index 59a8faa08..671442bc1 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp index 8e118556b..a913f5bee 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactBoxConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp index cb77a96d8..40e4bbe0f 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp index 708c2cfb8..0d5027aa1 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp index d7a637c1f..6dc7bda87 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp index b8b4d6a6b..148c551a7 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleHeightField.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp index 57bcf18df..93aec0493 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactCapsuleMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp index be2692f68..862804123 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h index 2e46431b6..0185b4f53 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexCommon.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp index 27deee46c..c2596a69d 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp index afcceee2b..43dcceffa 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexHeightField.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp index 07ca96521..617d30ce6 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactConvexMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h index c072acd4c..8ddc95706 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGen.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp index 6bb333116..fcda4833a 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenBoxConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp index 25d92d74c..f8264b6b6 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenSphereCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h index 9e1084a53..06ee918af 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactGenUtil.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h index c96349973..fc4f39ed6 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactMeshCallback.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp index fd184ab8f..862505ccb 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp index 9e0023c9e..ed73e4038 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp index 43b6dc00d..5e1474a3a 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactPlaneConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp index a9e057ea0..78f5b457e 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp index a7851fe59..9cac7d9ed 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp index 2bc568c42..f601db8a9 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp index fb8288271..f1e9b250b 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereHeightField.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp index 549b78c79..e7642014e 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp index 203ed52a1..61a41e3c7 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSpherePlane.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp index 5ba3fee61..7f0225510 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMContactSphereSphere.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp index 9f805a02c..c3ef1d5d4 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h index a86b06b1d..1f292cef9 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMShapeConvex.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp index efd27ee93..9cae16eb9 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h index ece7b1050..20bd78bf7 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPCMTriangleContactGen.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp index 1360be1c7..6865dbdf1 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h index 5dc8dd965..a20d74be4 100644 --- a/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h +++ b/src/PhysX/physx/source/geomutils/src/pcm/GuPersistentContactManifold.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp index a7210a921..e187b2aba 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h index efb643ca3..002fb7cca 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp index 21a99edfd..45ceeb898 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h index 0e561f2c4..10baf676c 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxSphere.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp index 659e40023..bd32edc23 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h index 81ebc2878..fef06fabc 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_FeatureBased.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp index 13219ea87..110fb4f3e 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h index 3116f86e2..6a97a9d4a 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepBoxTriangle_SAT.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp index 36dafac7c..ed0eae751 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h index 55b094c02..d117576d3 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp index 5b05d0d75..da2af39bf 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.h index 6341ca275..7748db675 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleCapsule.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.cpp index c3e4c5415..c9e268d0a 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h index fa5dfdec0..725dda0a0 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepCapsuleTriangle.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp index 740c8d7b2..d39b82a08 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h index 98e7e5691..e48121c49 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereCapsule.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp index a9378d64a..66141566f 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h index 1c80b0273..51c61dd50 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereSphere.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp index 30728ac5a..507f3349e 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h index ecfe6ab00..f4863ba99 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepSphereTriangle.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp index 3960e0025..0c3f53821 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h index 323529351..e31c82dbb 100644 --- a/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h +++ b/src/PhysX/physx/source/geomutils/src/sweep/GuSweepTriangleUtils.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp b/src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp index e0215b60b..6e9c640ba 100644 --- a/src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp +++ b/src/PhysX/physx/source/immediatemode/src/NpImmediateMode.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -44,726 +44,701 @@ #include "GuPersistentContactManifold.h" #include "NpConstraint.h" -namespace physx +using namespace physx; + +namespace { - - namespace - { - #define MAX_NUM_PARTITIONS 32u - static PxU32 bitTable[32] = - { - 1u << 0, 1u << 1, 1u << 2, 1u << 3, 1u << 4, 1u << 5, 1u << 6, 1u << 7, 1u << 8, 1u << 9, 1u << 10, 1u << 11, 1u << 12, 1u << 13, 1u << 14, 1u << 15, 1u << 16, 1u << 17, - 1u << 18, 1u << 19, 1u << 20, 1u << 21, 1u << 22, 1u << 23, 1u << 24, 1u << 25, 1u << 26, 1u << 27, 1u << 28, 1u << 29, 1u << 30, 1u << 31 - }; + static PxU32 bitTable[32] = + { + 1u << 0, 1u << 1, 1u << 2, 1u << 3, 1u << 4, 1u << 5, 1u << 6, 1u << 7, 1u << 8, 1u << 9, 1u << 10, 1u << 11, 1u << 12, 1u << 13, 1u << 14, 1u << 15, 1u << 16, 1u << 17, + 1u << 18, 1u << 19, 1u << 20, 1u << 21, 1u << 22, 1u << 23, 1u << 24, 1u << 25, 1u << 26, 1u << 27, 1u << 28, 1u << 29, 1u << 30, 1u << 31 + }; - PxU32 getBit(const PxU32 index) + PxU32 getBit(const PxU32 index) + { + PX_ASSERT(index < 32); + return bitTable[index]; + } + + class RigidBodyClassification + { + PxSolverBody* PX_RESTRICT mBodies; + PxU32 mNumBodies; + + public: + RigidBodyClassification(PxSolverBody* PX_RESTRICT bodies, PxU32 numBodies) : mBodies(bodies), mNumBodies(numBodies) { - PX_ASSERT(index < 32); - return bitTable[index]; } - - class RigidBodyClassification + //Returns true if it is a dynamic-dynamic constriant; false if it is a dynamic-static or dynamic-kinematic constraint + PX_FORCE_INLINE bool classifyConstraint(const PxSolverConstraintDesc& desc, uintptr_t& indexA, uintptr_t& indexB, bool& activeA, bool& activeB) const { - PxSolverBody* PX_RESTRICT mBodies; - PxU32 mNumBodies; + indexA = uintptr_t(desc.bodyA - mBodies); + indexB = uintptr_t(desc.bodyB - mBodies); + activeA = indexA < mNumBodies; + activeB = indexB < mNumBodies; + return activeA && activeB; + } - public: - RigidBodyClassification(PxSolverBody* PX_RESTRICT bodies, PxU32 numBodies) : mBodies(bodies), mNumBodies(numBodies) + PX_FORCE_INLINE void reserveSpaceForStaticConstraints(PxU32* numConstraintsPerPartition) + { + for (PxU32 a = 0; a < mNumBodies; ++a) { - } + mBodies[a].solverProgress = 0; - //Returns true if it is a dynamic-dynamic constriant; false if it is a dynamic-static or dynamic-kinematic constraint - PX_FORCE_INLINE bool classifyConstraint(const PxSolverConstraintDesc& desc, uintptr_t& indexA, uintptr_t& indexB, bool& activeA, bool& activeB) const - { - indexA = uintptr_t(desc.bodyA - mBodies); - indexB = uintptr_t(desc.bodyB - mBodies); - activeA = indexA < mNumBodies; - activeB = indexB < mNumBodies; - return activeA && activeB; - } - - PX_FORCE_INLINE void reserveSpaceForStaticConstraints(PxU32* numConstraintsPerPartition) - { - for (PxU32 a = 0; a < mNumBodies; ++a) + for (PxU32 b = 0; b < mBodies[a].maxSolverFrictionProgress; ++b) { - mBodies[a].solverProgress = 0; - - for (PxU32 b = 0; b < mBodies[a].maxSolverFrictionProgress; ++b) - { - PxU32 partId = PxMin(PxU32(mBodies[a].maxSolverNormalProgress + b), MAX_NUM_PARTITIONS); - numConstraintsPerPartition[partId]++; - } + PxU32 partId = PxMin(PxU32(mBodies[a].maxSolverNormalProgress + b), MAX_NUM_PARTITIONS); + numConstraintsPerPartition[partId]++; } } - }; + } + }; - template - void classifyConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, - PxU32* numConstraintsPerPartition) + template + void classifyConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + PxU32* numConstraintsPerPartition) + { + const PxSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + + PxMemZero(numConstraintsPerPartition, sizeof(PxU32) * 33); + + for (PxU32 i = 0; i < numConstraints; ++i, _desc++) { - const PxSolverConstraintDesc* _desc = descs; - const PxU32 numConstraintsMin1 = numConstraints - 1; + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); - PxMemZero(numConstraintsPerPartition, sizeof(PxU32) * 33); + uintptr_t indexA, indexB; + bool activeA, activeB; - for (PxU32 i = 0; i < numConstraints; ++i, _desc++) + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB); + + if (notContainsStatic) { - const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); - Ps::prefetchLine(_desc[prefetchOffset].constraint); - Ps::prefetchLine(_desc[prefetchOffset].bodyA); - Ps::prefetchLine(_desc[prefetchOffset].bodyB); - Ps::prefetchLine(_desc + 8); + PxU32 partitionsA = _desc->bodyA->solverProgress; + PxU32 partitionsB = _desc->bodyB->solverProgress; - uintptr_t indexA, indexB; - bool activeA, activeB; - - const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB); - - if (notContainsStatic) + PxU32 availablePartition; { - PxU32 partitionsA = _desc->bodyA->solverProgress; - PxU32 partitionsB = _desc->bodyB->solverProgress; - - PxU32 availablePartition; + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) { - const PxU32 combinedMask = (~partitionsA & ~partitionsB); - availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); - if (availablePartition == MAX_NUM_PARTITIONS) - { - //Write to overflow partition... - numConstraintsPerPartition[availablePartition]++; - _desc->bodyA->maxSolverNormalProgress = MAX_NUM_PARTITIONS; - _desc->bodyB->maxSolverNormalProgress = MAX_NUM_PARTITIONS; - continue; - } - - const PxU32 partitionBit = getBit(availablePartition); - partitionsA |= partitionBit; - partitionsB |= partitionBit; + //Write to overflow partition... + numConstraintsPerPartition[availablePartition]++; + _desc->bodyA->maxSolverNormalProgress = MAX_NUM_PARTITIONS; + _desc->bodyB->maxSolverNormalProgress = MAX_NUM_PARTITIONS; + continue; } - _desc->bodyA->solverProgress = partitionsA; - _desc->bodyB->solverProgress = partitionsB; - numConstraintsPerPartition[availablePartition]++; - availablePartition++; - _desc->bodyA->maxSolverNormalProgress = PxMax(_desc->bodyA->maxSolverNormalProgress, PxU16(availablePartition)); - _desc->bodyB->maxSolverNormalProgress = PxMax(_desc->bodyB->maxSolverNormalProgress, PxU16(availablePartition)); - } - else - { - //Just count the number of static constraints and store in maxSolverFrictionProgress... - if (activeA) - _desc->bodyA->maxSolverFrictionProgress++; - else if (activeB) - _desc->bodyB->maxSolverFrictionProgress++; + const PxU32 partitionBit = getBit(availablePartition); + partitionsA |= partitionBit; + partitionsB |= partitionBit; } + + _desc->bodyA->solverProgress = partitionsA; + _desc->bodyB->solverProgress = partitionsB; + numConstraintsPerPartition[availablePartition]++; + availablePartition++; + _desc->bodyA->maxSolverNormalProgress = PxMax(_desc->bodyA->maxSolverNormalProgress, PxU16(availablePartition)); + _desc->bodyB->maxSolverNormalProgress = PxMax(_desc->bodyB->maxSolverNormalProgress, PxU16(availablePartition)); + } + else + { + //Just count the number of static constraints and store in maxSolverFrictionProgress... + if (activeA) + _desc->bodyA->maxSolverFrictionProgress++; + else if (activeB) + _desc->bodyB->maxSolverFrictionProgress++; } - - classification.reserveSpaceForStaticConstraints(numConstraintsPerPartition); - } - template - void writeConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, - PxU32* accumulatedConstraintsPerPartition, PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDesc) + classification.reserveSpaceForStaticConstraints(numConstraintsPerPartition); + } + + template + void writeConstraintDesc(const PxSolverConstraintDesc* PX_RESTRICT descs, const PxU32 numConstraints, Classification& classification, + PxU32* accumulatedConstraintsPerPartition, PxSolverConstraintDesc* PX_RESTRICT eaOrderedConstraintDesc) + { + const PxSolverConstraintDesc* _desc = descs; + const PxU32 numConstraintsMin1 = numConstraints - 1; + for (PxU32 i = 0; i < numConstraints; ++i, _desc++) { - const PxSolverConstraintDesc* _desc = descs; - const PxU32 numConstraintsMin1 = numConstraints - 1; - for (PxU32 i = 0; i < numConstraints; ++i, _desc++) + const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); + Ps::prefetchLine(_desc[prefetchOffset].constraint); + Ps::prefetchLine(_desc[prefetchOffset].bodyA); + Ps::prefetchLine(_desc[prefetchOffset].bodyB); + Ps::prefetchLine(_desc + 8); + + uintptr_t indexA, indexB; + bool activeA, activeB; + const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB); + + if (notContainsStatic) { - const PxU32 prefetchOffset = PxMin(numConstraintsMin1 - i, 4u); - Ps::prefetchLine(_desc[prefetchOffset].constraint); - Ps::prefetchLine(_desc[prefetchOffset].bodyA); - Ps::prefetchLine(_desc[prefetchOffset].bodyB); - Ps::prefetchLine(_desc + 8); + PxU32 partitionsA = _desc->bodyA->solverProgress; + PxU32 partitionsB = _desc->bodyB->solverProgress; - uintptr_t indexA, indexB; - bool activeA, activeB; - const bool notContainsStatic = classification.classifyConstraint(*_desc, indexA, indexB, activeA, activeB); - - if (notContainsStatic) + PxU32 availablePartition; { - PxU32 partitionsA = _desc->bodyA->solverProgress; - PxU32 partitionsB = _desc->bodyB->solverProgress; - - PxU32 availablePartition; + const PxU32 combinedMask = (~partitionsA & ~partitionsB); + availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); + if (availablePartition == MAX_NUM_PARTITIONS) { - const PxU32 combinedMask = (~partitionsA & ~partitionsB); - availablePartition = combinedMask == 0 ? MAX_NUM_PARTITIONS : Ps::lowestSetBit(combinedMask); - if (availablePartition == MAX_NUM_PARTITIONS) - { - eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; - continue; - } - - const PxU32 partitionBit = getBit(availablePartition); - - partitionsA |= partitionBit; - partitionsB |= partitionBit; + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + continue; } - _desc->bodyA->solverProgress = partitionsA; - _desc->bodyB->solverProgress = partitionsB; + const PxU32 partitionBit = getBit(availablePartition); - eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + partitionsA |= partitionBit; + partitionsB |= partitionBit; } - else - { - //Just count the number of static constraints and store in maxSolverFrictionProgress... - PxU32 index = 0; - if (activeA) - index = PxMin(PxU32(_desc->bodyA->maxSolverNormalProgress + _desc->bodyA->maxSolverFrictionProgress++), MAX_NUM_PARTITIONS); - else if (activeB) - index = PxMin(PxU32(_desc->bodyB->maxSolverNormalProgress + _desc->bodyB->maxSolverFrictionProgress++), MAX_NUM_PARTITIONS); - eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[index]++] = *_desc; - } + _desc->bodyA->solverProgress = partitionsA; + _desc->bodyB->solverProgress = partitionsB; + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[availablePartition]++] = *_desc; + } + else + { + //Just count the number of static constraints and store in maxSolverFrictionProgress... + PxU32 index = 0; + if (activeA) + index = PxMin(PxU32(_desc->bodyA->maxSolverNormalProgress + _desc->bodyA->maxSolverFrictionProgress++), MAX_NUM_PARTITIONS); + else if (activeB) + index = PxMin(PxU32(_desc->bodyB->maxSolverNormalProgress + _desc->bodyB->maxSolverFrictionProgress++), MAX_NUM_PARTITIONS); + + eaOrderedConstraintDesc[accumulatedConstraintsPerPartition[index]++] = *_desc; } } + } +} +void immediate::PxConstructSolverBodies(const PxRigidBodyData* inRigidData, PxSolverBodyData* outSolverBodyData, const PxU32 nbBodies, const PxVec3& gravity, const PxReal dt) +{ + for (PxU32 a = 0; a < nbBodies; ++a) + { + const PxRigidBodyData& rigidData = inRigidData[a]; + PxVec3 lv = rigidData.linearVelocity, av = rigidData.angularVelocity; + Dy::bodyCoreComputeUnconstrainedVelocity(gravity, dt, rigidData.linearDamping, rigidData.angularDamping, 1.f, rigidData.maxLinearVelocitySq, rigidData.maxAngularVelocitySq, lv, av, false); + Dy::copyToSolverBodyData(lv, av, rigidData.invMass, rigidData.invInertia, rigidData.body2World, -rigidData.maxDepenetrationVelocity, rigidData.maxContactImpulse, IG_INVALID_NODE, PX_MAX_F32, outSolverBodyData[a], 0); + } +} + +void immediate::PxConstructStaticSolverBody(const PxTransform& globalPose, PxSolverBodyData& solverBodyData) +{ + PxVec3 zero(0.f); + Dy::copyToSolverBodyData(zero, zero, 0.f, zero, globalPose, -PX_MAX_F32, PX_MAX_F32, IG_INVALID_NODE, PX_MAX_F32, solverBodyData, 0); +} + +void immediate::PxIntegrateSolverBodies(PxSolverBodyData* solverBodyData, PxSolverBody* solverBody, const PxVec3* linearMotionVelocity, const PxVec3* angularMotionState, const PxU32 nbBodiesToIntegrate, PxReal dt) +{ + for (PxU32 i = 0; i < nbBodiesToIntegrate; ++i) + { + PxVec3 lmv = linearMotionVelocity[i]; + PxVec3 amv = angularMotionState[i]; + Dy::integrateCore(lmv, amv, solverBody[i], solverBodyData[i], dt); + } +} + +PxU32 immediate::PxBatchConstraints(PxSolverConstraintDesc* solverConstraintDescs, const PxU32 nbConstraints, PxSolverBody* solverBodies, PxU32 numBodies, PxConstraintBatchHeader* outBatchHeaders, + PxSolverConstraintDesc* outOrderedConstraintDescs) +{ + PxU32 constraintsPerPartition[MAX_NUM_PARTITIONS + 1]; + + for (PxU32 a = 0; a < numBodies; ++a) + { + PxSolverBody& body = solverBodies[a]; + body.solverProgress = 0; + //We re-use maxSolverFrictionProgress and maxSolverNormalProgress to record the + //maximum partition used by dynamic constraints and the number of static constraints affecting + //a body. We use this to make partitioning much cheaper and be able to support + body.maxSolverFrictionProgress = 0; + body.maxSolverNormalProgress = 0; } - - void immediate::PxConstructSolverBodies(const PxRigidBodyData* inRigidData, PxSolverBodyData* outSolverBodyData, const PxU32 nbBodies, const PxVec3& gravity, const PxReal dt) { - for (PxU32 a = 0; a < nbBodies; ++a) + RigidBodyClassification classification(solverBodies, numBodies); + classifyConstraintDesc(solverConstraintDescs, nbConstraints, classification, constraintsPerPartition); + + PxU32 accumulation = 0; + for (PxU32 a = 0; a < MAX_NUM_PARTITIONS+1; ++a) { - const PxRigidBodyData& rigidData = inRigidData[a]; - PxVec3 lv = rigidData.linearVelocity, av = rigidData.angularVelocity; - Dy::bodyCoreComputeUnconstrainedVelocity(gravity, dt, rigidData.linearDamping, rigidData.angularDamping, 1.f, rigidData.maxLinearVelocitySq, rigidData.maxAngularVelocitySq, lv, av, false); - Dy::copyToSolverBodyData(lv, av, rigidData.invMass, rigidData.invInertia, rigidData.body2World, -rigidData.maxDepenetrationVelocity, rigidData.maxContactImpulse, IG_INVALID_NODE, PX_MAX_F32, outSolverBodyData[a], 0); + PxU32 count = constraintsPerPartition[a]; + constraintsPerPartition[a] = accumulation; + accumulation += count; } - } - - void immediate::PxConstructStaticSolverBody(const PxTransform& globalPose, PxSolverBodyData& solverBodyData) - { - PxVec3 zero(0.f); - Dy::copyToSolverBodyData(zero, zero, 0.f, zero, globalPose, -PX_MAX_F32, PX_MAX_F32, IG_INVALID_NODE, PX_MAX_F32, solverBodyData, 0); - } - - - void immediate::PxIntegrateSolverBodies(PxSolverBodyData* solverBodyData, PxSolverBody* solverBody, const PxVec3* linearMotionVelocity, const PxVec3* angularMotionState, const PxU32 nbBodiesToIntegrate, PxReal dt) - { - for (PxU32 i = 0; i < nbBodiesToIntegrate; ++i) - { - PxVec3 lmv = linearMotionVelocity[i]; - PxVec3 amv = angularMotionState[i]; - Dy::integrateCore(lmv, amv, solverBody[i], solverBodyData[i], dt); - } - } - - - PxU32 immediate::PxBatchConstraints(PxSolverConstraintDesc* solverConstraintDescs, const PxU32 nbConstraints, PxSolverBody* solverBodies, PxU32 numBodies, PxConstraintBatchHeader* outBatchHeaders, - PxSolverConstraintDesc* outOrderedConstraintDescs) - { - PxU32 constraintsPerPartition[MAX_NUM_PARTITIONS + 1]; for (PxU32 a = 0; a < numBodies; ++a) { PxSolverBody& body = solverBodies[a]; body.solverProgress = 0; - //We re-use maxSolverFrictionProgress and maxSolverNormalProgress to record the - //maximum partition used by dynamic constraints and the number of static constraints affecting - //a body. We use this to make partitioning much cheaper and be able to support + //Keep the dynamic constraint count but bump the static constraint count back to 0. + //This allows us to place the static constraints in the appropriate place when we see them + //because we know the maximum index for the dynamic constraints... body.maxSolverFrictionProgress = 0; - body.maxSolverNormalProgress = 0; } + writeConstraintDesc(solverConstraintDescs, nbConstraints, classification, constraintsPerPartition, outOrderedConstraintDescs); + + PxU32 numHeaders = 0; + PxU32 currentPartition = 0; + PxU32 maxJ = nbConstraints == 0 ? 0 : constraintsPerPartition[0]; + + const PxU32 maxBatchPartition = MAX_NUM_PARTITIONS; + for (PxU32 a = 0; a < nbConstraints;) { - RigidBodyClassification classification(solverBodies, numBodies); - classifyConstraintDesc(solverConstraintDescs, nbConstraints, classification, constraintsPerPartition); + PxConstraintBatchHeader& header = outBatchHeaders[numHeaders++]; + header.mStartIndex = a; - PxU32 accumulation = 0; - for (PxU32 a = 0; a < MAX_NUM_PARTITIONS+1; ++a) + PxU32 loopMax = PxMin(maxJ - a, 4u); + PxU16 j = 0; + if (loopMax > 0) { - PxU32 count = constraintsPerPartition[a]; - constraintsPerPartition[a] = accumulation; - accumulation += count; - } - - for (PxU32 a = 0; a < numBodies; ++a) - { - PxSolverBody& body = solverBodies[a]; - body.solverProgress = 0; - //Keep the dynamic constraint count but bump the static constraint count back to 0. - //This allows us to place the static constraints in the appropriate place when we see them - //because we know the maximum index for the dynamic constraints... - body.maxSolverFrictionProgress = 0; - } - - writeConstraintDesc(solverConstraintDescs, nbConstraints, classification, constraintsPerPartition, outOrderedConstraintDescs); - - PxU32 numHeaders = 0; - PxU32 currentPartition = 0; - PxU32 maxJ = nbConstraints == 0 ? 0 : constraintsPerPartition[0]; - - const PxU32 maxBatchPartition = MAX_NUM_PARTITIONS; - for (PxU32 a = 0; a < nbConstraints;) - { - PxConstraintBatchHeader& header = outBatchHeaders[numHeaders++]; - header.mStartIndex = a; - - PxU32 loopMax = PxMin(maxJ - a, 4u); - PxU16 j = 0; - if (loopMax > 0) + j = 1; + PxSolverConstraintDesc& desc = outOrderedConstraintDescs[a]; + if (currentPartition < maxBatchPartition) { - j = 1; - PxSolverConstraintDesc& desc = outOrderedConstraintDescs[a]; - if (currentPartition < maxBatchPartition) - { - for (; j < loopMax && desc.constraintLengthOver16 == outOrderedConstraintDescs[a + j].constraintLengthOver16; ++j); - } - header.mStride = j; - header.mConstraintType = desc.constraintLengthOver16; + for (; j < loopMax && desc.constraintLengthOver16 == outOrderedConstraintDescs[a + j].constraintLengthOver16; ++j); } - if (maxJ == (a + j) && maxJ != nbConstraints) - { - currentPartition++; - maxJ = constraintsPerPartition[currentPartition]; - } - a += j; + header.mStride = j; + header.mConstraintType = desc.constraintLengthOver16; } - return numHeaders; + if (maxJ == (a + j) && maxJ != nbConstraints) + { + currentPartition++; + maxJ = constraintsPerPartition[currentPartition]; + } + a += j; } + return numHeaders; } +} +bool immediate::PxCreateContactConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverContactDesc* contactDescs, + PxConstraintAllocator& allocator, PxReal invDt, PxReal bounceThreshold, PxReal frictionOffsetThreshold, PxReal correlationDistance) +{ + PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); + PX_ASSERT(bounceThreshold < 0.f); + PX_ASSERT(frictionOffsetThreshold > 0.f); + PX_ASSERT(correlationDistance > 0.f); - bool immediate::PxCreateContactConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverContactDesc* contactDescs, - PxConstraintAllocator& allocator, PxReal invDt, PxReal bounceThreshold, PxReal frictionOffsetThreshold, PxReal correlationDistance) + Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; + + Dy::CorrelationBuffer cb; + + PxU32 currentContactDescIdx = 0; + + for (PxU32 i = 0; i < nbHeaders; ++i) { - PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); - PX_ASSERT(bounceThreshold < 0.f); - PX_ASSERT(frictionOffsetThreshold > 0.f); - PX_ASSERT(correlationDistance > 0.f); - - Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; - - Dy::CorrelationBuffer cb; - - PxU32 currentContactDescIdx = 0; - - for (PxU32 i = 0; i < nbHeaders; ++i) + PxConstraintBatchHeader& batchHeader = batchHeaders[i]; + if (batchHeader.mStride == 4) { - PxConstraintBatchHeader& batchHeader = batchHeaders[i]; - if (batchHeader.mStride == 4) + PxU32 totalContacts = contactDescs[currentContactDescIdx].numContacts + contactDescs[currentContactDescIdx + 1].numContacts + + contactDescs[currentContactDescIdx + 2].numContacts + contactDescs[currentContactDescIdx + 3].numContacts; + + if (totalContacts <= 64) { - PxU32 totalContacts = contactDescs[currentContactDescIdx].numContacts + contactDescs[currentContactDescIdx + 1].numContacts + - contactDescs[currentContactDescIdx + 2].numContacts + contactDescs[currentContactDescIdx + 3].numContacts; - - if (totalContacts <= 64) - { - state = Dy::createFinalizeSolverContacts4(cb, - contactDescs + currentContactDescIdx, - invDt, - bounceThreshold, - frictionOffsetThreshold, - correlationDistance, - 0.f, - allocator); - } + state = Dy::createFinalizeSolverContacts4(cb, + contactDescs + currentContactDescIdx, + invDt, + bounceThreshold, + frictionOffsetThreshold, + correlationDistance, + 0.f, + allocator); } - - if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) - { - for (PxU32 a = 0; a < batchHeader.mStride; ++a) - { - Dy::createFinalizeSolverContacts(contactDescs[currentContactDescIdx + a], cb, invDt, bounceThreshold, - frictionOffsetThreshold, correlationDistance, 0.f, allocator, NULL); - } - } - PxU8 type = *contactDescs[currentContactDescIdx].desc->constraint; - - if (type == DY_SC_TYPE_STATIC_CONTACT) - { - //Check if any block of constraints is classified as type static (single) contact constraint. - //If they are, iterate over all constraints grouped with it and switch to "dynamic" contact constraint - //type if there's a dynamic contact constraint in the group. - for (PxU32 c = 1; c < batchHeader.mStride; ++c) - { - if (*contactDescs[currentContactDescIdx + c].desc->constraint == DY_SC_TYPE_RB_CONTACT) - { - type = DY_SC_TYPE_RB_CONTACT; - break; - } - } - } - - batchHeader.mConstraintType = type; - - currentContactDescIdx += batchHeader.mStride; } - - - - - return true; - } - - - bool immediate::PxCreateJointConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, PxReal dt, PxReal invDt) - { - PX_ASSERT(dt > 0.f); - PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); - - Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; - - PxU32 currentDescIdx = 0; - for (PxU32 i = 0; i < nbHeaders; ++i) + if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) { - PxConstraintBatchHeader& batchHeader = batchHeaders[i]; - - PxU8 type = DY_SC_TYPE_BLOCK_1D; - if (batchHeader.mStride == 4) - { - PxU32 totalRows = 0; - PxU32 maxRows = 0; - bool batchable = true; - for (PxU32 a = 0; a < batchHeader.mStride; ++a) - { - if (jointDescs[currentDescIdx + a].numRows == 0) - { - batchable = false; - break; - } - totalRows += jointDescs[currentDescIdx + a].numRows; - maxRows = PxMax(maxRows, jointDescs[currentDescIdx + a].numRows); - } - - if (batchable) - { - state = Dy::setupSolverConstraint4 - (jointDescs + currentDescIdx, - dt, invDt, totalRows, - allocator, maxRows); - } - } - - if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) - { - type = DY_SC_TYPE_RB_1D; - for (PxU32 a = 0; a < batchHeader.mStride; ++a) - { - Dy::ConstraintHelper::setupSolverConstraint(jointDescs[currentDescIdx + a], - allocator, dt, invDt, NULL); - } - } - - batchHeader.mConstraintType = type; - currentDescIdx += batchHeader.mStride; - } - - return true; - } - - bool immediate::PxCreateJointConstraintsWithShaders(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxConstraint** constraints, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, - PxReal dt, PxReal invDt) - { - Px1DConstraint allRows[Dy::MAX_CONSTRAINT_ROWS * 4]; - - //Runs shaders to fill in rows... - - PxU32 currentDescIdx = 0; - - for (PxU32 i = 0; i < nbHeaders; ++i) - { - - PxU32 numRows = 0; - PxU32 preppedIndex = 0; - PxU32 maxRows = 0; - - PxConstraintBatchHeader& batchHeader = batchHeaders[i]; - for (PxU32 a = 0; a < batchHeader.mStride; ++a) { - Px1DConstraint* rows = allRows + numRows; - PxSolverConstraintPrepDesc& desc = jointDescs[currentDescIdx + a]; - - NpConstraint* npConstraint = static_cast(constraints[currentDescIdx + a]); - - npConstraint->updateConstants(); - - Sc::ConstraintCore& core = npConstraint->getScbConstraint().getScConstraint(); - - PxConstraintSolverPrep prep = core.getPxConnector()->getPrep(); - const void* constantBlock = core.getPxConnector()->getConstantBlock(); - PX_ASSERT(prep); - - PxMemZero(rows + preppedIndex, sizeof(Px1DConstraint)*(Dy::MAX_CONSTRAINT_ROWS)); - for (PxU32 b = preppedIndex; b < Dy::MAX_CONSTRAINT_ROWS; ++b) - { - Px1DConstraint& c = rows[b]; - //Px1DConstraintInit(c); - c.minImpulse = -PX_MAX_REAL; - c.maxImpulse = PX_MAX_REAL; - } - - desc.mInvMassScales.linear0 = desc.mInvMassScales.linear1 = desc.mInvMassScales.angular0 = desc.mInvMassScales.angular1 = 1.f; - - desc.body0WorldOffset = PxVec3(0.f); - - PxVec3 cA2w, cB2w; - PxU32 constraintCount = prep(rows, - desc.body0WorldOffset, - Dy::MAX_CONSTRAINT_ROWS, - desc.mInvMassScales, - constantBlock, - desc.bodyFrame0, desc.bodyFrame1, - !!(core.getFlags() & PxConstraintFlag::eENABLE_EXTENDED_LIMITS), - cA2w, cB2w); - - preppedIndex = Dy::MAX_CONSTRAINT_ROWS - constraintCount; - - maxRows = PxMax(constraintCount, maxRows); - - desc.rows = rows; - desc.numRows = constraintCount; - numRows += constraintCount; + Dy::createFinalizeSolverContacts(contactDescs[currentContactDescIdx + a], cb, invDt, bounceThreshold, + frictionOffsetThreshold, correlationDistance, 0.f, allocator, NULL); } - - PxCreateJointConstraints(&batchHeader, 1, jointDescs + currentDescIdx, allocator, dt, invDt); - - currentDescIdx += batchHeader.mStride; } + PxU8 type = *contactDescs[currentContactDescIdx].desc->constraint; - return true; //KS - TODO - do some error reporting/management... - } - - PX_FORCE_INLINE bool PxIsZero(PxSolverBody* bodies, PxU32 nbBodies) - { - for (PxU32 i = 0; i < nbBodies; ++i) + if (type == DY_SC_TYPE_STATIC_CONTACT) { - if (!bodies[i].linearVelocity.isZero() || - !bodies[i].angularState.isZero()) - return false; - } - return true; - } - - - void immediate::PxSolveConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbBatchHeaders, PxSolverConstraintDesc* solverConstraintDescs, PxSolverBody* solverBodies, - PxVec3* linearMotionVelocity, PxVec3* angularMotionVelocity, const PxU32 nbSolverBodies, const PxU32 nbPositionIterations, const PxU32 nbVelocityIterations) - { - PX_ASSERT(nbPositionIterations > 0); - PX_ASSERT(nbVelocityIterations > 0); - PX_ASSERT(PxIsZero(solverBodies, nbSolverBodies)); //Ensure that solver body velocities have been zeroed before solving - - //Stage 1: solve the position iterations... - Dy::SolveBlockMethod* solveTable = Dy::getSolveBlockTable(); - - Dy::SolveBlockMethod* solveConcludeTable = Dy::getSolverConcludeBlockTable(); - - Dy::SolveWriteBackBlockMethod* solveWritebackTable = Dy::getSolveWritebackBlockTable(); - - Dy::SolverContext cache; - cache.mThresholdStreamIndex = 0; - cache.mThresholdStreamLength = 0xFFFFFFF; - - PX_ASSERT(nbPositionIterations > 0); - PX_ASSERT(nbVelocityIterations > 0); - - for (PxU32 i = nbPositionIterations; i > 1; --i) - { - cache.doFriction = i <= 3; - for (PxU32 a = 0; a < nbBatchHeaders; ++a) + //Check if any block of constraints is classified as type static (single) contact constraint. + //If they are, iterate over all constraints grouped with it and switch to "dynamic" contact constraint + //type if there's a dynamic contact constraint in the group. + for (PxU32 c = 1; c < batchHeader.mStride; ++c) { - PxConstraintBatchHeader& batch = batchHeaders[a]; - solveTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + if (*contactDescs[currentContactDescIdx + c].desc->constraint == DY_SC_TYPE_RB_CONTACT) + { + type = DY_SC_TYPE_RB_CONTACT; + break; + } } } - cache.doFriction = true; + batchHeader.mConstraintType = type; + + currentContactDescIdx += batchHeader.mStride; + } + return true; +} + +bool immediate::PxCreateJointConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, PxReal dt, PxReal invDt) +{ + PX_ASSERT(dt > 0.f); + PX_ASSERT(invDt > 0.f && PxIsFinite(invDt)); + + Dy::SolverConstraintPrepState::Enum state = Dy::SolverConstraintPrepState::eUNBATCHABLE; + + PxU32 currentDescIdx = 0; + for (PxU32 i = 0; i < nbHeaders; ++i) + { + PxConstraintBatchHeader& batchHeader = batchHeaders[i]; + + PxU8 type = DY_SC_TYPE_BLOCK_1D; + if (batchHeader.mStride == 4) + { + PxU32 totalRows = 0; + PxU32 maxRows = 0; + bool batchable = true; + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + if (jointDescs[currentDescIdx + a].numRows == 0) + { + batchable = false; + break; + } + totalRows += jointDescs[currentDescIdx + a].numRows; + maxRows = PxMax(maxRows, jointDescs[currentDescIdx + a].numRows); + } + + if (batchable) + { + state = Dy::setupSolverConstraint4 + (jointDescs + currentDescIdx, + dt, invDt, totalRows, + allocator, maxRows); + } + } + + if (state == Dy::SolverConstraintPrepState::eUNBATCHABLE) + { + type = DY_SC_TYPE_RB_1D; + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + Dy::ConstraintHelper::setupSolverConstraint(jointDescs[currentDescIdx + a], + allocator, dt, invDt, NULL); + } + } + + batchHeader.mConstraintType = type; + currentDescIdx += batchHeader.mStride; + } + + return true; +} + +bool immediate::PxCreateJointConstraintsWithShaders(PxConstraintBatchHeader* batchHeaders, const PxU32 nbHeaders, PxConstraint** constraints, PxSolverConstraintPrepDesc* jointDescs, PxConstraintAllocator& allocator, + PxReal dt, PxReal invDt) +{ + Px1DConstraint allRows[Dy::MAX_CONSTRAINT_ROWS * 4]; + + //Runs shaders to fill in rows... + + PxU32 currentDescIdx = 0; + + for (PxU32 i = 0; i < nbHeaders; ++i) + { + + PxU32 numRows = 0; + PxU32 preppedIndex = 0; + PxU32 maxRows = 0; + + PxConstraintBatchHeader& batchHeader = batchHeaders[i]; + + for (PxU32 a = 0; a < batchHeader.mStride; ++a) + { + Px1DConstraint* rows = allRows + numRows; + PxSolverConstraintPrepDesc& desc = jointDescs[currentDescIdx + a]; + + NpConstraint* npConstraint = static_cast(constraints[currentDescIdx + a]); + + npConstraint->updateConstants(); + + Sc::ConstraintCore& core = npConstraint->getScbConstraint().getScConstraint(); + + PxConstraintSolverPrep prep = core.getPxConnector()->getPrep(); + const void* constantBlock = core.getPxConnector()->getConstantBlock(); + PX_ASSERT(prep); + + PxMemZero(rows + preppedIndex, sizeof(Px1DConstraint)*(Dy::MAX_CONSTRAINT_ROWS)); + for (PxU32 b = preppedIndex; b < Dy::MAX_CONSTRAINT_ROWS; ++b) + { + Px1DConstraint& c = rows[b]; + //Px1DConstraintInit(c); + c.minImpulse = -PX_MAX_REAL; + c.maxImpulse = PX_MAX_REAL; + } + + desc.mInvMassScales.linear0 = desc.mInvMassScales.linear1 = desc.mInvMassScales.angular0 = desc.mInvMassScales.angular1 = 1.f; + + desc.body0WorldOffset = PxVec3(0.f); + + PxVec3 cA2w, cB2w; + PxU32 constraintCount = prep(rows, + desc.body0WorldOffset, + Dy::MAX_CONSTRAINT_ROWS, + desc.mInvMassScales, + constantBlock, + desc.bodyFrame0, desc.bodyFrame1, + !!(core.getFlags() & PxConstraintFlag::eENABLE_EXTENDED_LIMITS), + cA2w, cB2w); + + preppedIndex = Dy::MAX_CONSTRAINT_ROWS - constraintCount; + + maxRows = PxMax(constraintCount, maxRows); + + desc.rows = rows; + desc.numRows = constraintCount; + numRows += constraintCount; + } + + PxCreateJointConstraints(&batchHeader, 1, jointDescs + currentDescIdx, allocator, dt, invDt); + + currentDescIdx += batchHeader.mStride; + } + + return true; //KS - TODO - do some error reporting/management... +} + +PX_FORCE_INLINE bool PxIsZero(PxSolverBody* bodies, PxU32 nbBodies) +{ + for (PxU32 i = 0; i < nbBodies; ++i) + { + if (!bodies[i].linearVelocity.isZero() || + !bodies[i].angularState.isZero()) + return false; + } + return true; +} + +void immediate::PxSolveConstraints(PxConstraintBatchHeader* batchHeaders, const PxU32 nbBatchHeaders, PxSolverConstraintDesc* solverConstraintDescs, PxSolverBody* solverBodies, + PxVec3* linearMotionVelocity, PxVec3* angularMotionVelocity, const PxU32 nbSolverBodies, const PxU32 nbPositionIterations, const PxU32 nbVelocityIterations) +{ + PX_ASSERT(nbPositionIterations > 0); + PX_ASSERT(nbVelocityIterations > 0); + PX_ASSERT(PxIsZero(solverBodies, nbSolverBodies)); //Ensure that solver body velocities have been zeroed before solving + + //Stage 1: solve the position iterations... + Dy::SolveBlockMethod* solveTable = Dy::getSolveBlockTable(); + + Dy::SolveBlockMethod* solveConcludeTable = Dy::getSolverConcludeBlockTable(); + + Dy::SolveWriteBackBlockMethod* solveWritebackTable = Dy::getSolveWritebackBlockTable(); + + Dy::SolverContext cache; + cache.mThresholdStreamIndex = 0; + cache.mThresholdStreamLength = 0xFFFFFFF; + + PX_ASSERT(nbPositionIterations > 0); + PX_ASSERT(nbVelocityIterations > 0); + + for (PxU32 i = nbPositionIterations; i > 1; --i) + { + cache.doFriction = i <= 3; for (PxU32 a = 0; a < nbBatchHeaders; ++a) { PxConstraintBatchHeader& batch = batchHeaders[a]; - solveConcludeTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + solveTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); } + } - //Save motion velocities... + cache.doFriction = true; + for (PxU32 a = 0; a < nbBatchHeaders; ++a) + { + PxConstraintBatchHeader& batch = batchHeaders[a]; + solveConcludeTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + } - for (PxU32 a = 0; a < nbSolverBodies; ++a) - { - linearMotionVelocity[a] = solverBodies[a].linearVelocity; - angularMotionVelocity[a] = solverBodies[a].angularState; - } + //Save motion velocities... - for (PxU32 i = nbVelocityIterations; i > 1; --i) - { - for (PxU32 a = 0; a < nbBatchHeaders; ++a) - { - PxConstraintBatchHeader& batch = batchHeaders[a]; - solveTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); - } - } + for (PxU32 a = 0; a < nbSolverBodies; ++a) + { + linearMotionVelocity[a] = solverBodies[a].linearVelocity; + angularMotionVelocity[a] = solverBodies[a].angularState; + } + for (PxU32 i = nbVelocityIterations; i > 1; --i) + { for (PxU32 a = 0; a < nbBatchHeaders; ++a) { PxConstraintBatchHeader& batch = batchHeaders[a]; - solveWritebackTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + solveTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); } - } - - void createCache(Gu::Cache& cache, PxGeometryType::Enum geomType0, PxGeometryType::Enum geomType1, PxCacheAllocator& allocator) + for (PxU32 a = 0; a < nbBatchHeaders; ++a) { - - if (gEnablePCMCaching[geomType0][geomType1]) - { - if (geomType0 <= PxGeometryType::eCONVEXMESH && - geomType1 <= PxGeometryType::eCONVEXMESH) - { - if (geomType0 == PxGeometryType::eSPHERE || geomType1 == PxGeometryType::eSPHERE) - { - Gu::PersistentContactManifold* manifold = PX_PLACEMENT_NEW(allocator.allocateCacheData(sizeof(Gu::SpherePersistentContactManifold)), Gu::SpherePersistentContactManifold)(); - cache.setManifold(manifold); - } - else - { - Gu::PersistentContactManifold* manifold = PX_PLACEMENT_NEW(allocator.allocateCacheData(sizeof(Gu::LargePersistentContactManifold)), Gu::LargePersistentContactManifold)(); - cache.setManifold(manifold); + PxConstraintBatchHeader& batch = batchHeaders[a]; + solveWritebackTable[batch.mConstraintType](solverConstraintDescs + batch.mStartIndex, batch.mStride, cache); + } +} - } - cache.getManifold().clearManifold(); +void createCache(Gu::Cache& cache, PxGeometryType::Enum geomType0, PxGeometryType::Enum geomType1, PxCacheAllocator& allocator) +{ + if (gEnablePCMCaching[geomType0][geomType1]) + { + if (geomType0 <= PxGeometryType::eCONVEXMESH && + geomType1 <= PxGeometryType::eCONVEXMESH) + { + if (geomType0 == PxGeometryType::eSPHERE || geomType1 == PxGeometryType::eSPHERE) + { + Gu::PersistentContactManifold* manifold = PX_PLACEMENT_NEW(allocator.allocateCacheData(sizeof(Gu::SpherePersistentContactManifold)), Gu::SpherePersistentContactManifold)(); + cache.setManifold(manifold); } else { - //ML: raised 1 to indicate the manifold is multiManifold which is for contact gen in mesh/height field - //cache.manifold = 1; - cache.setMultiManifold(NULL); + Gu::PersistentContactManifold* manifold = PX_PLACEMENT_NEW(allocator.allocateCacheData(sizeof(Gu::LargePersistentContactManifold)), Gu::LargePersistentContactManifold)(); + cache.setManifold(manifold); + } + cache.getManifold().clearManifold(); } else { - //cache.manifold = 0; - cache.mCachedData = NULL; - cache.mManifoldFlags = 0; + //ML: raised 1 to indicate the manifold is multiManifold which is for contact gen in mesh/height field + //cache.manifold = 1; + cache.setMultiManifold(NULL); } } - - - - bool immediate::PxGenerateContacts(const PxGeometry* const * geom0, const PxGeometry* const * geom1, const PxTransform* pose0, const PxTransform* pose1, PxCache* contactCache, const PxU32 nbPairs, PxContactRecorder& contactRecorder, - const PxReal contactDistance, const PxReal meshContactMargin, const PxReal toleranceLength, PxCacheAllocator& allocator) + else { - PX_ASSERT(meshContactMargin > 0.f); - PX_ASSERT(toleranceLength > 0.f); - PX_ASSERT(contactDistance > 0.f); - Gu::ContactBuffer contactBuffer; + //cache.manifold = 0; + cache.mCachedData = NULL; + cache.mManifoldFlags = 0; + } +} - for (PxU32 i = 0; i < nbPairs; ++i) +bool immediate::PxGenerateContacts(const PxGeometry* const * geom0, const PxGeometry* const * geom1, const PxTransform* pose0, const PxTransform* pose1, PxCache* contactCache, const PxU32 nbPairs, PxContactRecorder& contactRecorder, + const PxReal contactDistance, const PxReal meshContactMargin, const PxReal toleranceLength, PxCacheAllocator& allocator) +{ + PX_ASSERT(meshContactMargin > 0.f); + PX_ASSERT(toleranceLength > 0.f); + PX_ASSERT(contactDistance > 0.f); + Gu::ContactBuffer contactBuffer; + + for (PxU32 i = 0; i < nbPairs; ++i) + { + contactBuffer.count = 0; + PxGeometryType::Enum type0 = geom0[i]->getType(); + PxGeometryType::Enum type1 = geom1[i]->getType(); + + const PxGeometry* tempGeom0 = geom0[i]; + const PxGeometry* tempGeom1 = geom1[i]; + + PX_ALIGN(16, PxTransform) transform0 = pose0[i]; + PX_ALIGN(16, PxTransform) transform1 = pose1[i]; + + const bool bSwap = type0 > type1; + if (bSwap) { + const PxGeometry* temp = tempGeom0; + tempGeom0 = geom1[i]; + tempGeom1 = temp; + PxGeometryType::Enum tempType = type0; + type0 = type1; + type1 = tempType; + transform1 = pose0[i]; + transform0 = pose1[i]; + } - contactBuffer.count = 0; - PxGeometryType::Enum type0 = geom0[i]->getType(); - PxGeometryType::Enum type1 = geom1[i]->getType(); + const Gu::GeometryUnion geomUnion0(*tempGeom0); + const Gu::GeometryUnion geomUnion1(*tempGeom1); - const PxGeometry* tempGeom0 = geom0[i]; - const PxGeometry* tempGeom1 = geom1[i]; + //Now work out which type of PCM we need... - PX_ALIGN(16, PxTransform) transform0 = pose0[i]; - PX_ALIGN(16, PxTransform) transform1 = pose1[i]; + Gu::Cache& cache = static_cast(contactCache[i]); - const bool bSwap = type0 > type1; - if (bSwap) + bool needsMultiManifold = type1 > PxGeometryType::eCONVEXMESH; + + Gu::NarrowPhaseParams params(contactDistance, meshContactMargin, toleranceLength); + + if (needsMultiManifold) + { + Gu::MultiplePersistentContactManifold multiManifold; + + if (cache.isMultiManifold()) { - const PxGeometry* temp = tempGeom0; - tempGeom0 = geom1[i]; - tempGeom1 = temp; - PxGeometryType::Enum tempType = type0; - type0 = type1; - type1 = tempType; - transform1 = pose0[i]; - transform0 = pose1[i]; - } - - const Gu::GeometryUnion geomUnion0(*tempGeom0); - const Gu::GeometryUnion geomUnion1(*tempGeom1); - - //Now work out which type of PCM we need... - - Gu::Cache& cache = static_cast(contactCache[i]); - - bool needsMultiManifold = type1 > PxGeometryType::eCONVEXMESH; - - Gu::NarrowPhaseParams params(contactDistance, meshContactMargin, toleranceLength); - - if (needsMultiManifold) - { - Gu::MultiplePersistentContactManifold multiManifold; - - if (cache.isMultiManifold()) - { - multiManifold.fromBuffer(reinterpret_cast(&cache.getMultipleManifold())); - } - else - { - multiManifold.initialize(); - } - cache.setMultiManifold(&multiManifold); - - //Do collision detection, then write manifold out... - g_PCMContactMethodTable[type0][type1](geomUnion0, geomUnion1, transform0, transform1, params, cache, contactBuffer, NULL); - - const PxU32 size = (sizeof(Gu::MultiPersistentManifoldHeader) + - multiManifold.mNumManifolds * sizeof(Gu::SingleManifoldHeader) + - multiManifold.mNumTotalContacts * sizeof(Gu::CachedMeshPersistentContact)); - - PxU8* buffer = allocator.allocateCacheData(size); - - multiManifold.toBuffer(buffer); - - cache.setMultiManifold(buffer); - + multiManifold.fromBuffer(reinterpret_cast(&cache.getMultipleManifold())); } else { - //Allocate the type of manifold we need again... - Gu::PersistentContactManifold* oldManifold = NULL; + multiManifold.initialize(); + } + cache.setMultiManifold(&multiManifold); - if (cache.isManifold()) - oldManifold = &cache.getManifold(); + //Do collision detection, then write manifold out... + g_PCMContactMethodTable[type0][type1](geomUnion0, geomUnion1, transform0, transform1, params, cache, contactBuffer, NULL); - //Allocates and creates the PCM... - createCache(cache, type0, type1, allocator); + const PxU32 size = (sizeof(Gu::MultiPersistentManifoldHeader) + + multiManifold.mNumManifolds * sizeof(Gu::SingleManifoldHeader) + + multiManifold.mNumTotalContacts * sizeof(Gu::CachedMeshPersistentContact)); - //Copy PCM from old to new manifold... - if (oldManifold) - { - Gu::PersistentContactManifold& manifold = cache.getManifold(); - manifold.mRelativeTransform = oldManifold->mRelativeTransform; - manifold.mNumContacts = oldManifold->mNumContacts; - manifold.mNumWarmStartPoints = oldManifold->mNumWarmStartPoints; - manifold.mAIndice[0] = oldManifold->mAIndice[0]; manifold.mAIndice[1] = oldManifold->mAIndice[1]; - manifold.mAIndice[2] = oldManifold->mAIndice[2]; manifold.mAIndice[3] = oldManifold->mAIndice[3]; - manifold.mBIndice[0] = oldManifold->mBIndice[0]; manifold.mBIndice[1] = oldManifold->mBIndice[1]; - manifold.mBIndice[2] = oldManifold->mBIndice[2]; manifold.mBIndice[3] = oldManifold->mBIndice[3]; - PxMemCopy(manifold.mContactPoints, oldManifold->mContactPoints, sizeof(Gu::PersistentContact)*manifold.mNumContacts); - } + PxU8* buffer = allocator.allocateCacheData(size); - g_PCMContactMethodTable[type0][type1](geomUnion0, geomUnion1, transform0, transform1, params, cache, contactBuffer, NULL); + multiManifold.toBuffer(buffer); + + cache.setMultiManifold(buffer); + } + else + { + //Allocate the type of manifold we need again... + Gu::PersistentContactManifold* oldManifold = NULL; + + if (cache.isManifold()) + oldManifold = &cache.getManifold(); + + //Allocates and creates the PCM... + createCache(cache, type0, type1, allocator); + + //Copy PCM from old to new manifold... + if (oldManifold) + { + Gu::PersistentContactManifold& manifold = cache.getManifold(); + manifold.mRelativeTransform = oldManifold->mRelativeTransform; + manifold.mNumContacts = oldManifold->mNumContacts; + manifold.mNumWarmStartPoints = oldManifold->mNumWarmStartPoints; + manifold.mAIndice[0] = oldManifold->mAIndice[0]; manifold.mAIndice[1] = oldManifold->mAIndice[1]; + manifold.mAIndice[2] = oldManifold->mAIndice[2]; manifold.mAIndice[3] = oldManifold->mAIndice[3]; + manifold.mBIndice[0] = oldManifold->mBIndice[0]; manifold.mBIndice[1] = oldManifold->mBIndice[1]; + manifold.mBIndice[2] = oldManifold->mBIndice[2]; manifold.mBIndice[3] = oldManifold->mBIndice[3]; + PxMemCopy(manifold.mContactPoints, oldManifold->mContactPoints, sizeof(Gu::PersistentContact)*manifold.mNumContacts); } - if (bSwap) - { - for (PxU32 a = 0; a < contactBuffer.count; ++a) - { - contactBuffer.contacts[a].normal = -contactBuffer.contacts[a].normal; - } - } + g_PCMContactMethodTable[type0][type1](geomUnion0, geomUnion1, transform0, transform1, params, cache, contactBuffer, NULL); + } - - if (contactBuffer.count != 0) + if (bSwap) + { + for (PxU32 a = 0; a < contactBuffer.count; ++a) { - //Record this contact pair... - contactRecorder.recordContacts(contactBuffer.contacts, contactBuffer.count, i); + contactBuffer.contacts[a].normal = -contactBuffer.contacts[a].normal; } } - return true; - } + if (contactBuffer.count != 0) + { + //Record this contact pair... + contactRecorder.recordContacts(contactBuffer.contacts, contactBuffer.count, i); + } + } + return true; } + diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h index 9178d0578..94c76fdd3 100644 --- a/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h +++ b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h index d81ca4eb6..d602eb75c 100644 --- a/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h +++ b/src/PhysX/physx/source/lowlevel/api/include/PxsMaterialManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h b/src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h index 524e26a8f..424686ab3 100644 --- a/src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvConfig.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h b/src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h index edaff42e3..3509b2a26 100644 --- a/src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvDynamics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h b/src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h index aef2d999b..9de406b63 100644 --- a/src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvGeometry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h b/src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h index de321befc..489ad9457 100644 --- a/src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvGlobals.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvManager.h b/src/PhysX/physx/source/lowlevel/api/include/PxvManager.h index 685340ff9..919ba17d8 100644 --- a/src/PhysX/physx/source/lowlevel/api/include/PxvManager.h +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h b/src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h index 530574b3a..9cce3f2eb 100644 --- a/src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h +++ b/src/PhysX/physx/source/lowlevel/api/include/PxvSimStats.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp b/src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp index 69603db69..5394dd2d5 100644 --- a/src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp +++ b/src/PhysX/physx/source/lowlevel/api/src/px_globals.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h b/src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h index 1ca5dccbd..4783ea0a5 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h +++ b/src/PhysX/physx/source/lowlevel/common/include/collision/PxcContactMethodImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h index 53f610bce..f1de8c56f 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcConstraintBlockStream.h @@ -23,12 +23,11 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. - #ifndef PXC_CONSTRAINTBLOCKPOOL_H #define PXC_CONSTRAINTBLOCKPOOL_H @@ -39,7 +38,6 @@ namespace physx { - class PxsConstraintBlockManager { public: @@ -48,15 +46,13 @@ public: { } - PX_FORCE_INLINE void reset() { mBlockPool.releaseConstraintBlocks(mTrackingArray); } - - PxcNpMemBlockArray mTrackingArray; - PxcNpMemBlockPool& mBlockPool; + PxcNpMemBlockArray mTrackingArray; + PxcNpMemBlockPool& mBlockPool; private: PxsConstraintBlockManager& operator=(const PxsConstraintBlockManager&); @@ -66,14 +62,14 @@ class PxcConstraintBlockStream { PX_NOCOPY(PxcConstraintBlockStream) public: - PxcConstraintBlockStream(PxcNpMemBlockPool & blockPool): - mBlockPool(blockPool), - mBlock(NULL), - mUsed(0) + PxcConstraintBlockStream(PxcNpMemBlockPool & blockPool) : + mBlockPool (blockPool), + mBlock (NULL), + mUsed (0) { } - PX_FORCE_INLINE PxU8* reserve(PxU32 size, PxsConstraintBlockManager& manager) + PX_FORCE_INLINE PxU8* reserve(PxU32 size, PxsConstraintBlockManager& manager) { size = (size+15)&~15; if(size>PxcNpMemBlock::SIZE) @@ -98,10 +94,7 @@ public: mUsed = 0; } - PX_FORCE_INLINE PxcNpMemBlockPool& getMemBlockPool() - { - return mBlockPool; - } + PX_FORCE_INLINE PxcNpMemBlockPool& getMemBlockPool() { return mBlockPool; } private: PxcNpMemBlockPool& mBlockPool; @@ -122,7 +115,7 @@ public: { } - PX_FORCE_INLINE PxU8* reserve(PxU32 size) + PX_FORCE_INLINE PxU8* reserve(PxU32 size) { size = (size+15)&~15; @@ -150,10 +143,7 @@ public: mUsed = 0; } - PX_FORCE_INLINE PxcNpMemBlockPool& getMemBlockPool() - { - return mBlockPool; - } + PX_FORCE_INLINE PxcNpMemBlockPool& getMemBlockPool() { return mBlockPool; } private: PxcNpMemBlockPool& mBlockPool; diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h index d8deb405d..b124586fd 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcContactCache.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h index 041ef0711..d4468a99c 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcMaterialMethodImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h index 1ccb09358..ff5aa421d 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpBatch.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -36,30 +36,15 @@ namespace physx { struct PxcNpWorkUnit; class PxcNpThreadContext; + struct PxsContactManagerOutput; -struct PxcNpWorkUnit; -class PxsContactManager; -struct PxsContactManagerOutput; + namespace Gu + { + struct Cache; + } -namespace Gu -{ - struct Cache; -} - -namespace Cm -{ - class FlushPool; -} - -class PxLightCpuTask; - -namespace Gu -{ - class PxgGpuNarrowphaseCoreInterface; -} - -void PxcDiscreteNarrowPhase(PxcNpThreadContext& context, const PxcNpWorkUnit& cmInput, Gu::Cache& cache, PxsContactManagerOutput& output); -void PxcDiscreteNarrowPhasePCM(PxcNpThreadContext& context, const PxcNpWorkUnit& cmInput, Gu::Cache& cache, PxsContactManagerOutput& output); + void PxcDiscreteNarrowPhase(PxcNpThreadContext& context, const PxcNpWorkUnit& cmInput, Gu::Cache& cache, PxsContactManagerOutput& output); + void PxcDiscreteNarrowPhasePCM(PxcNpThreadContext& context, const PxcNpWorkUnit& cmInput, Gu::Cache& cache, PxsContactManagerOutput& output); } #endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h index ed8048b68..32cf102e6 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCache.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h index ba7da39d2..f6eb1f882 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpCacheStreamPair.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h index 5683b0d1b..2cb3b25e8 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpContactPrepShared.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h index bfe4debf4..b4050e7b2 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpMemBlockPool.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h index 3a3bca94c..f051acef2 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpThreadContext.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h index 0cb68d584..95dc69613 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcNpWorkUnit.h @@ -23,12 +23,11 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. - #ifndef PXC_NPWORKUNIT_H #define PXC_NPWORKUNIT_H @@ -38,14 +37,9 @@ namespace physx { - -class PxvContact; - struct PxsRigidCore; struct PxsShapeCore; -class PxsMaterialManager; - struct PxcNpWorkUnitFlag { enum Enum @@ -83,53 +77,51 @@ struct PxcNpWorkUnitStatusFlag }; }; +// PT: TODO: fix the inconsistent namings (mXXX) in this class +struct PxcNpWorkUnit +{ + const PxsRigidCore* rigidCore0; // INPUT //4 //8 + const PxsRigidCore* rigidCore1; // INPUT //8 //16 + + const PxsShapeCore* shapeCore0; // INPUT //12 //24 + const PxsShapeCore* shapeCore1; // INPUT //16 //32 + + PxU8* ccdContacts; // OUTPUT //20 //40 + + PxU8* frictionDataPtr; // INOUT //24 //48 + + PxU16 flags; // INPUT //26 //50 + PxU8 frictionPatchCount; // INOUT //27 //51 + PxU8 statusFlags; // OUTPUT (see PxcNpWorkUnitStatusFlag) //28 //52 + + PxU8 dominance0; // INPUT //29 //53 + PxU8 dominance1; // INPUT //30 //54 + PxU8 geomType0; // INPUT //31 //55 + PxU8 geomType1; // INPUT //32 //56 + + PxU32 index; // INPUT //36 //60 + + PxReal restDistance; // INPUT //40 //64 + + PxU32 mTransformCache0; // //44 //68 + PxU32 mTransformCache1; // //48 //72 + + PxU32 mEdgeIndex; //inout the island gen edge index //52 //76 + PxU32 mNpIndex; //INPUT //56 //80 + + PxReal mTorsionalPatchRadius; //60 //84 + PxReal mMinTorsionalPatchRadius; //64 //88 +}; + /* * A struct to record the number of work units a particular constraint pointer references. * This is created at the beginning of the constriant data and is used to bypass constraint preparation when the * bodies are not moving a lot. In this case, we can recycle the constraints and save ourselves some cycles. */ -struct PxcNpWorkUnit; struct PxcNpWorkUnitBatch { - PxcNpWorkUnit* mUnits[4]; - PxU32 mSize; -}; - -struct PxcNpWorkUnit -{ - - const PxsRigidCore* rigidCore0; // INPUT //4 //8 - const PxsRigidCore* rigidCore1; // INPUT //8 //16 - - const PxsShapeCore* shapeCore0; // INPUT //12 //24 - const PxsShapeCore* shapeCore1; // INPUT //16 //32 - - PxU8* ccdContacts; // OUTPUT //20 //40 - - PxU8* frictionDataPtr; // INOUT //24 //48 - - PxU16 flags; // INPUT //26 //50 - PxU8 frictionPatchCount; // INOUT //27 //51 - PxU8 statusFlags; // OUTPUT (see PxcNpWorkUnitStatusFlag) //28 //52 - - PxU8 dominance0; // INPUT //29 //53 - PxU8 dominance1; // INPUT //30 //54 - PxU8 geomType0; // INPUT //31 //55 - PxU8 geomType1; // INPUT //32 //56 - - PxU32 index; // INPUT //36 //60 - - PxReal restDistance; // INPUT //40 //64 - - PxU32 mTransformCache0; // //44 //68 - PxU32 mTransformCache1; // //48 //72 - - PxU32 mEdgeIndex; //inout the island gen edge index //52 //76 - PxU32 mNpIndex; //INPUT //56 //80 - - PxReal mTorsionalPatchRadius; //60 //84 - PxReal mMinTorsionalPatchRadius; //64 //88 - + PxcNpWorkUnit* mUnits[4]; + PxU32 mSize; }; //#if !defined(PX_P64) @@ -141,7 +133,6 @@ PX_FORCE_INLINE void PxcNpWorkUnitClearContactState(PxcNpWorkUnit& n) n.ccdContacts = NULL; } - PX_FORCE_INLINE void PxcNpWorkUnitClearCachedState(PxcNpWorkUnit& n) { n.frictionDataPtr = 0; @@ -159,7 +150,6 @@ PX_FORCE_INLINE void PxcNpWorkUnitClearFrictionCachedState(PxcNpWorkUnit& n) #if !defined(PX_P64) //PX_COMPILE_TIME_ASSERT(sizeof(PxcNpWorkUnit)==128); #endif - } #endif diff --git a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h index 1f698d59e..645608d61 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h +++ b/src/PhysX/physx/source/lowlevel/common/include/pipeline/PxcRigidBody.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -38,9 +38,6 @@ namespace physx { - -class PxsContactManager; -struct PxsCCDPair; struct PxsCCDBody; #define PX_INTERNAL_LOCK_FLAG_START 8 @@ -52,63 +49,56 @@ public: enum PxcRigidBodyFlag { - eFROZEN = 1 << 0, //This flag indicates that the stabilization is enabled and the body is - //"frozen". By "frozen", we mean that the body's transform is unchanged - //from the previous frame. This permits various optimizations. - eFREEZE_THIS_FRAME = 1 << 1, - eUNFREEZE_THIS_FRAME = 1 << 2, - eACTIVATE_THIS_FRAME = 1 << 3, - eDEACTIVATE_THIS_FRAME = 1 << 4, - eDISABLE_GRAVITY = 1 << 5, - eSPECULATIVE_CCD = 1 << 6, + eFROZEN = 1 << 0, //This flag indicates that the stabilization is enabled and the body is + //"frozen". By "frozen", we mean that the body's transform is unchanged + //from the previous frame. This permits various optimizations. + eFREEZE_THIS_FRAME = 1 << 1, + eUNFREEZE_THIS_FRAME = 1 << 2, + eACTIVATE_THIS_FRAME = 1 << 3, + eDEACTIVATE_THIS_FRAME = 1 << 4, + eDISABLE_GRAVITY = 1 << 5, + eSPECULATIVE_CCD = 1 << 6, //KS - copied here for GPU simulation to avoid needing to pass another set of flags around. - eLOCK_LINEAR_X = 1 << (PX_INTERNAL_LOCK_FLAG_START), - eLOCK_LINEAR_Y = 1 << (PX_INTERNAL_LOCK_FLAG_START + 1), - eLOCK_LINEAR_Z = 1 << (PX_INTERNAL_LOCK_FLAG_START + 2), - eLOCK_ANGULAR_X = 1 << (PX_INTERNAL_LOCK_FLAG_START + 3), - eLOCK_ANGULAR_Y = 1 << (PX_INTERNAL_LOCK_FLAG_START + 4), - eLOCK_ANGULAR_Z = 1 << (PX_INTERNAL_LOCK_FLAG_START + 5) - - + eLOCK_LINEAR_X = 1 << (PX_INTERNAL_LOCK_FLAG_START), + eLOCK_LINEAR_Y = 1 << (PX_INTERNAL_LOCK_FLAG_START + 1), + eLOCK_LINEAR_Z = 1 << (PX_INTERNAL_LOCK_FLAG_START + 2), + eLOCK_ANGULAR_X = 1 << (PX_INTERNAL_LOCK_FLAG_START + 3), + eLOCK_ANGULAR_Y = 1 << (PX_INTERNAL_LOCK_FLAG_START + 4), + eLOCK_ANGULAR_Z = 1 << (PX_INTERNAL_LOCK_FLAG_START + 5) }; - PX_FORCE_INLINE PxcRigidBody(PxsBodyCore* core) - : mLastTransform(core->body2World), - mCCD(NULL), - mCore(core) - { - } + PX_FORCE_INLINE PxcRigidBody(PxsBodyCore* core) : + mLastTransform (core->body2World), + mCCD (NULL), + mCore (core) + {} - void adjustCCDLastTransform(); + void adjustCCDLastTransform(); protected: - ~PxcRigidBody() - { - } + ~PxcRigidBody() {} public: - PxTransform mLastTransform; //28 (28) + PxTransform mLastTransform; //28 (28) - PxU16 mInternalFlags; //30 (30) - PxU16 solverIterationCounts; //32 (32) + PxU16 mInternalFlags; //30 (30) + PxU16 solverIterationCounts; //32 (32) - PxsCCDBody* mCCD; //36 (40) // only valid during CCD + PxsCCDBody* mCCD; //36 (40) // only valid during CCD - PxsBodyCore* mCore; //40 (48) + PxsBodyCore* mCore; //40 (48) #if !PX_P64_FAMILY - PxU32 alignmentPad[2]; //48 (48) + PxU32 alignmentPad[2]; //48 (48) #endif - PxVec3 sleepLinVelAcc; //60 (60) - PxReal freezeCount; //64 (64) + PxVec3 sleepLinVelAcc; //60 (60) + PxReal freezeCount; //64 (64) - PxVec3 sleepAngVelAcc; //76 (76) - PxReal accelScale; //80 (80) - - + PxVec3 sleepAngVelAcc; //76 (76) + PxReal accelScale; //80 (80) } PX_ALIGN_SUFFIX(16); PX_COMPILE_TIME_ASSERT(0 == (sizeof(PxcRigidBody) & 0x0f)); diff --git a/src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h index 70d918e88..4c143b8e8 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h +++ b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcScratchAllocator.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h index d375eff46..8284abf21 100644 --- a/src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h +++ b/src/PhysX/physx/source/lowlevel/common/include/utils/PxcThreadCoherentCache.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp b/src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp index 7840341ed..b2dd5102a 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/collision/PxcContact.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp index 419163225..16349a096 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactCache.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactMethodImpl.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactMethodImpl.cpp index fee241289..98ca30019 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactMethodImpl.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcContactMethodImpl.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialHeightField.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialHeightField.cpp index 26b27272c..aef589669 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialHeightField.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialHeightField.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMesh.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMesh.cpp index 281dc1da0..676f1ff63 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMesh.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMesh.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp index 3a8dfcb6c..16bc439c2 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialMethodImpl.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp index 8b948eb5e..877b43e81 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcMaterialShape.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp index 0b3441ad9..6c116c274 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpBatch.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp index a31890e22..b02764516 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpCacheStreamPair.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp index a824d6a93..c393359b8 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpContactPrepShared.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp index 2c8b0160c..f7be99b82 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpMemBlockPool.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp index 9a20f7ea7..8c5c6f4be 100644 --- a/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp +++ b/src/PhysX/physx/source/lowlevel/common/src/pipeline/PxcNpThreadContext.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -51,7 +51,7 @@ PxcNpThreadContext::PxcNpThreadContext(PxcNpContext* params) : mContactStreamPool (params->mContactStreamPool), mPatchStreamPool (params->mPatchStreamPool), mForceAndIndiceStreamPool (params->mForceAndIndiceStreamPool), - mMaterialManager(params->mMaterialManager), + mMaterialManager (params->mMaterialManager), mLocalNewTouchCount (0), mLocalLostTouchCount (0), mLocalFoundPatchCount (0), diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h b/src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h index 12bc731da..aec3b5860 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsBodySim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h b/src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h index 883c49732..545ea6203 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsCCD.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -379,12 +379,12 @@ struct PxsCCDPair \param[in] pass The current CCD pass \return The normalized TOI. <=1.0 indicates a hit. Otherwise PX_MAX_REAL. */ - PxReal sweepFindToi(PxcNpThreadContext& threadContext, PxReal dt, PxU32 pass); + PxReal sweepFindToi(PxcNpThreadContext& threadContext, PxReal dt, PxU32 pass, PxReal ccdThreshold); /** \brief Performs a sweep estimation for this pair \return The normalized TOI. <= 1.0 indicates a potential hit, otherwise PX_MAX_REAL. */ - PxReal sweepEstimateToi(); + PxReal sweepEstimateToi(PxReal ccdThreshold); /** \brief Advances this pair to the TOI \param[in] dt The time-step @@ -432,7 +432,8 @@ public: /** \brief Creates this PxsCCDContext */ - static PxsCCDContext* create(PxsContext* context, Dy::ThresholdStream& dynamicsContext, PxvNphaseImplementationContext& nPhaseContext); + static PxsCCDContext* create(PxsContext* context, Dy::ThresholdStream& dynamicsContext, PxvNphaseImplementationContext& nPhaseContext, + PxReal ccdThreshold); /** \brief Destroys this PxsCCDContext @@ -485,6 +486,8 @@ public: */ PX_FORCE_INLINE void clearUpdatedBodies() { mUpdatedCCDBodies.forceSize_Unsafe(0); } + PX_FORCE_INLINE PxReal getCCDThreshold() const { return mCCDThreshold;} + /** \brief Runs the CCD contact modification. \param[in] contacts The list of modifiable contacts @@ -530,7 +533,8 @@ protected: \brief Constructor for PxsCCDContext \param[in] context The PxsContext that is associated with this PxsCCDContext. */ - PxsCCDContext(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext); + PxsCCDContext(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext, + PxReal ccdThreshold); /** \brief Destructor for PxsCCDContext */ @@ -608,6 +612,8 @@ private: Ps::Mutex mMutex; + PxReal mCCDThreshold; + private: PX_NOCOPY(PxsCCDContext) diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h index 1293e2aeb..b6b91464b 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h index 767eb08f3..d0419c9e7 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsContactManagerState.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsContext.h b/src/PhysX/physx/source/lowlevel/software/include/PxsContext.h index 7f5fcc5d8..2257d8784 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsContext.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsContext.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h index 950698b01..124fbe808 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsDefaultMemoryManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h b/src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h index 0646fe2d7..baefa11a2 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsHeapMemoryAllocator.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h index 0c75adab5..fd28fd434 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIncrementalConstraintPartitioning.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h index 021cd95f0..cbf1cff23 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandManagerTypes.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h index 713ae169b..f208049dc 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandNodeIndex.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h index 2390636bf..0f5fc39fd 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsIslandSim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h b/src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h index 9990dcb77..d0f82356a 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsKernelWrangler.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h b/src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h index 83a5e1337..19535c4e2 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsMaterialCombiner.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h index 9cf8d7b78..a8ec1c0a8 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsMemoryManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h b/src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h index d06467212..524b02038 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsNphaseImplementationContext.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h b/src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h index 571cba466..d3a04dc05 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsRigidBody.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h b/src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h index 7cadd5c01..1cb99b901 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsShapeSim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h b/src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h index 6c78eaa01..69c5f2f6a 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsSimpleIslandManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h b/src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h index 625352642..953a1efe5 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsSimulationController.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h b/src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h index 1970356f1..a14ec9897 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxsTransformCache.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h b/src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h index 6e8d84c9f..bc2435506 100644 --- a/src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h +++ b/src/PhysX/physx/source/lowlevel/software/include/PxvNphaseImplementationContext.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsCCD.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsCCD.cpp index 6a962c6e3..ef68a4abe 100644 --- a/src/PhysX/physx/source/lowlevel/software/src/PxsCCD.cpp +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsCCD.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -193,7 +193,8 @@ namespace namespace physx { - PxsCCDContext::PxsCCDContext(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext) : + PxsCCDContext::PxsCCDContext(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext, + PxReal ccdThreshold) : mPostCCDSweepTask (context->getContextId(), this, "PxsContext.postCCDSweep"), mPostCCDAdvanceTask (context->getContextId(), this, "PxsContext.postCCDAdvance"), mPostCCDDepenetrateTask (context->getContextId(), this, "PxsContext.postCCDDepenetrate"), @@ -205,7 +206,8 @@ namespace physx mCCDMaxPasses (1), mContext (context), mThresholdStream (thresholdStream), - mNphaseContext(nPhaseContext) + mNphaseContext (nPhaseContext), + mCCDThreshold (ccdThreshold) { } @@ -213,14 +215,15 @@ PxsCCDContext::~PxsCCDContext() { } -PxsCCDContext* PxsCCDContext::create(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext) +PxsCCDContext* PxsCCDContext::create(PxsContext* context, Dy::ThresholdStream& thresholdStream, PxvNphaseImplementationContext& nPhaseContext, + PxReal ccdThreshold) { PxsCCDContext* dc = reinterpret_cast( PX_ALLOC(sizeof(PxsCCDContext), "PxsCCDContext")); if(dc) { - new(dc) PxsCCDContext(context, thresholdStream, nPhaseContext); + new(dc) PxsCCDContext(context, thresholdStream, nPhaseContext, ccdThreshold); } return dc; } @@ -246,7 +249,8 @@ PxTransform PxsCCDShape::getLastCCDAbsPose(const PxsRigidBody* atom) const return atom->getLastCCDTransform() * atom->getCore().getBody2Actor().getInverse() * mShapeCore->transform; } -PxReal PxsCCDPair::sweepFindToi(PxcNpThreadContext& context, PxReal dt, PxU32 pass) +PxReal PxsCCDPair::sweepFindToi(PxcNpThreadContext& context, PxReal dt, PxU32 pass, + PxReal ccdThreshold) { printSeparator("findToi", pass, mBa0, mG0, NULL, PxGeometryType::eGEOMETRY_COUNT); //Update shape transforms if necessary @@ -298,7 +302,7 @@ PxReal PxsCCDPair::sweepFindToi(PxcNpThreadContext& context, PxReal dt, PxU32 pa const PxReal fastMovingThresh0 = ccdShape0->mFastMovingThreshold; const PxReal fastMovingThresh1 = ccdShape1->mFastMovingThreshold; - const PxReal sumFastMovingThresh = (fastMovingThresh0 + fastMovingThresh1); + const PxReal sumFastMovingThresh = PxMin(fastMovingThresh0 + fastMovingThresh1, ccdThreshold); PxReal toi = Gu::SweepShapeShape(*ccdShape0, *ccdShape1, tm0, tm1, lastTm0, lastTm1, restDistance, @@ -447,7 +451,7 @@ void PxsCCDPair::updateShapes() } } -PxReal PxsCCDPair::sweepEstimateToi() +PxReal PxsCCDPair::sweepEstimateToi(PxReal ccdThreshold) { //Update shape transforms if necessary updateShapes(); @@ -491,7 +495,7 @@ PxReal PxsCCDPair::sweepEstimateToi() //Work out the sum of the fast moving thresholds scaled by the step ratio const PxReal fastMovingThresh0 = ccdShape0->mFastMovingThreshold; const PxReal fastMovingThresh1 = ccdShape1->mFastMovingThreshold; - const PxReal sumFastMovingThresh = (fastMovingThresh0 + fastMovingThresh1); + const PxReal sumFastMovingThresh = PxMin(fastMovingThresh0 + fastMovingThresh1, ccdThreshold); mToiType = eEstimate; @@ -845,9 +849,11 @@ class PxsCCDSweepTask : public Cm::Task { PxsCCDPair** mPairs; PxU32 mNumPairs; + PxReal mCCDThreshold; public: - PxsCCDSweepTask(PxU64 contextID, PxsCCDPair** pairs, PxU32 nPairs) - : Cm::Task(contextID), mPairs(pairs), mNumPairs(nPairs) + PxsCCDSweepTask(PxU64 contextID, PxsCCDPair** pairs, PxU32 nPairs, + PxReal ccdThreshold) + : Cm::Task(contextID), mPairs(pairs), mNumPairs(nPairs), mCCDThreshold(ccdThreshold) { } @@ -856,7 +862,7 @@ public: for (PxU32 j = 0; j < mNumPairs; j++) { PxsCCDPair& pair = *mPairs[j]; - pair.sweepEstimateToi(); + pair.sweepEstimateToi(mCCDThreshold); pair.mEstimatePass = 0; } } @@ -920,6 +926,8 @@ public: PxcNpThreadContext* threadContext = mContext->getNpThreadContext(); + PxReal ccdThreshold = mCCDContext->getCCDThreshold(); + // -------------------------------------------------------------------------------------- // loop over island labels assigned to this thread PxU32 islandStart = mFirstIslandPair; @@ -973,7 +981,7 @@ public: //If the pair was an estimate, we must perform an accurate sweep now if(pair.mToiType == PxsCCDPair::eEstimate) { - pair.sweepFindToi(*threadContext, dt, mCCDPass); + pair.sweepFindToi(*threadContext, dt, mCCDPass, ccdThreshold); //Test to see if the pair is still the earliest pair. if((iFront + 1) < islandEnd && mCCDPairs[iFront+1]->mMinToi < pair.mMinToi) @@ -1136,7 +1144,7 @@ public: // resweep pair1 since either b0 or b1 trajectory has changed PxReal oldToi = pair1.mMinToi; verifyCCDPair(pair1); - PxReal toi1 = pair1.sweepEstimateToi(); + PxReal toi1 = pair1.sweepEstimateToi(ccdThreshold); PX_ASSERT(pair1.mBa0); // this is because mMinToiNormal is the impact point here if (toi1 < oldToi) { @@ -1544,7 +1552,7 @@ void PxsCCDContext::updateCCD(PxReal dt, PxBaseTask* continuation, IG::IslandSim //Calculate the sum of the thresholds and work out if we need to perform a sweep. - const PxReal thresh = threshold0 + threshold1; + const PxReal thresh = PxMin(threshold0 + threshold1, mCCDThreshold); //If no shape pairs in the entire scene are fast-moving, we can bypass the entire of the CCD. needsSweep = needsSweep || (trA - trB).magnitudeSquared() >= (thresh * thresh); } @@ -1723,7 +1731,8 @@ void PxsCCDContext::updateCCD(PxReal dt, PxBaseTask* continuation, IG::IslandSim PX_ASSERT_WITH_MESSAGE(ptr, "Failed to allocate PxsCCDSweepTask"); const PxU32 batchEnd = PxMin(nPairs, batchBegin + mCCDPairsPerBatch); PX_ASSERT(batchEnd >= batchBegin); - PxsCCDSweepTask* task = PX_PLACEMENT_NEW(ptr, PxsCCDSweepTask)(mContext->getContextId(), mCCDPtrPairs.begin() + batchBegin, batchEnd - batchBegin); + PxsCCDSweepTask* task = PX_PLACEMENT_NEW(ptr, PxsCCDSweepTask)(mContext->getContextId(), mCCDPtrPairs.begin() + batchBegin, batchEnd - batchBegin, + mCCDThreshold); task->setContinuation(*mContext->mTaskManager, &mPostCCDSweepTask); task->removeReference(); } diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp index a9c84dd27..9b89e8794 100644 --- a/src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsContactManager.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp index 56412e13c..ba6d543a5 100644 --- a/src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsContext.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -65,7 +65,7 @@ using namespace physx::shdfnd; PxsContext::PxsContext(const PxSceneDesc& desc, PxTaskManager* taskManager, Cm::FlushPool& taskPool, PxU64 contextID) : mNpThreadContextPool (this), - mContactManagerPool ("mContactManagerPool", this, 256, 8192), + mContactManagerPool ("mContactManagerPool", this, 256), mManifoldPool ("mManifoldPool", 256), mSphereManifoldPool ("mSphereManifoldPool", 256), mContactModifyCallback (NULL), diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp index 381830ed7..7772d47b5 100644 --- a/src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsDefaultMemoryManager.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp index 2dd427ee4..69644397a 100644 --- a/src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsIslandSim.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp index 476d3bc71..ae6452f9a 100644 --- a/src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsMaterialCombiner.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp index 4dbd52df2..ce5995bdb 100644 --- a/src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsNphaseImplementationContext.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp b/src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp index 728e6910b..cb5dbe1b2 100644 --- a/src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp +++ b/src/PhysX/physx/source/lowlevel/software/src/PxsSimpleIslandManager.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h b/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h index 3c54c0f3d..59967a751 100644 --- a/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h +++ b/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManagerTasks.h b/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManagerTasks.h index 7532d0da6..7836dacbe 100644 --- a/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManagerTasks.h +++ b/src/PhysX/physx/source/lowlevelaabb/include/BpAABBManagerTasks.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhase.h b/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhase.h index 11190cf82..476cdd725 100644 --- a/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhase.h +++ b/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhase.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h b/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h index 6fe718204..ae483ef0b 100644 --- a/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h +++ b/src/PhysX/physx/source/lowlevelaabb/include/BpBroadPhaseUpdate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp index 85109e5f6..4e7fb66a6 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpAABBManager.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp index 0df71d8b9..71dd51f6d 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhase.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp index 53362dfaa..a3b336001 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h index 0ab400bbb..65a998bdd 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseABP.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp index 92fe49d1d..40bf923fc 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h index 3ef82b480..e1152a6fb 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBP.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h index 1cc3f9dc8..ac609000f 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseMBPCommon.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.cpp index c98eb371a..defeb354d 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.cpp +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.h index 3a08495dd..f46d02d30 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.h +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSap.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.cpp index 531f8065c..fe4f1eafc 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.cpp +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h index 30c2366fd..b1a4f7001 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseSapAux.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -37,7 +37,6 @@ #include "BpBroadPhase.h" #include "BpBroadPhaseUpdate.h" #include "CmBitMap.h" -#include "GuAxes.h" #include "PxcScratchAllocator.h" namespace physx diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp index 0fef90132..32410b6b3 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h index 013f7c544..dc3e7935c 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpBroadPhaseShared.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp index 5504ce468..62f61b1a2 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h index 21f518516..128c189d1 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpMBPTasks.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp index 2735614b6..ce75f969e 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h index f9b0523b4..6f325eb16 100644 --- a/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h +++ b/src/PhysX/physx/source/lowlevelaabb/src/BpSAPTasks.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h index e653067f2..35282080e 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulation.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -43,12 +43,16 @@ namespace physx { class PxConstraintAllocator; - + class PxcConstraintBlockStream; + struct PxSolverConstraintDesc; + class PxsConstraintBlockManager; #define DY_DEBUG_ARTICULATION 0 namespace Dy { + struct ArticulationJointTransforms; + struct PxTGSSolverConstraintDesc; struct FsInertia; struct FsData; @@ -79,10 +83,7 @@ public: //void setFsDataPtr(FsData* data) { mFsData = data; } // get data sizes for allocation at higher levels - virtual void getDataSizes(PxU32 linkCount, - PxU32 &solverDataSize, - PxU32& totalSize, - PxU32& scratchSize); + virtual void getDataSizes(PxU32 linkCount, PxU32& solverDataSize, PxU32& totalSize, PxU32& scratchSize); virtual void getImpulseResponse( PxU32 linkID, diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h index 42e9dfdec..1c6cd38fe 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h index 724b5ff7d..79b617cc7 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyArticulationJointCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -38,7 +38,6 @@ namespace physx { namespace Dy { - struct ArticulationLimit { PxReal low, high; @@ -101,7 +100,6 @@ namespace physx relativeQuat = other.relativeQuat; jointType = other.jointType; jointOffset = other.jointOffset; //this is the dof offset for the joint in the cache - } // attachment points, don't change the order, otherwise it will break GPU code @@ -130,13 +128,10 @@ namespace physx PxU8 jointType; //1 291 PxU8 pad[13]; //13 304 - - ArticulationJointCoreBase() { maxJointVelocity = 100.f; } // PX_SERIALIZATION ArticulationJointCoreBase(const PxEMPTY&) {} //~PX_SERIALIZATION - }; } } diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h index 67f05583b..1151a5673 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -46,7 +46,6 @@ class PxsRigidBody; namespace Dy { - #if PX_VC #pragma warning(push) #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. @@ -56,22 +55,22 @@ struct Constraint { public: - PxReal linBreakForce; //0 - PxReal angBreakForce; //4 - PxU16 constantBlockSize; //6 - PxU16 flags; //8 + PxReal linBreakForce; //0 + PxReal angBreakForce; //4 + PxU16 constantBlockSize; //6 + PxU16 flags; //8 - PxConstraintSolverPrep solverPrep; //12 - PxConstraintProject project; //16 - void* constantBlock; //20 + PxConstraintSolverPrep solverPrep; //12 + PxConstraintProject project; //16 + void* constantBlock; //20 - PxsRigidBody* body0; //24 - PxsRigidBody* body1; //28 + PxsRigidBody* body0; //24 + PxsRigidBody* body1; //28 - PxsBodyCore* bodyCore0; //32 - PxsBodyCore* bodyCore1; //36 - PxU32 index; //40 //this is also a constraint write back index - PxReal minResponseThreshold; //44 + PxsBodyCore* bodyCore0; //32 + PxsBodyCore* bodyCore1; //36 + PxU32 index; //40 //this is also a constraint write back index + PxReal minResponseThreshold; //44 } PX_ALIGN_SUFFIX(16); #if PX_VC diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h index ca5d889a3..a98916211 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyConstraintWriteBack.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyContext.h b/src/PhysX/physx/source/lowleveldynamics/include/DyContext.h index b16d0feda..c13055f5b 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyContext.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyContext.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h index 44026b435..baeabde1f 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulation.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -47,7 +47,6 @@ namespace physx { - class PxContactJoint; struct PxSolverConstraintDesc; class PxcConstraintBlockStream; @@ -61,12 +60,12 @@ namespace physx namespace Dy { - //#if PX_VC //#pragma warning(push) //#pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. //#endif + struct PxTGSSolverConstraintDesc; //class ArticulationJointCoreData; class ArticulationLinkData; struct SpatialSubspaceMatrix; @@ -78,7 +77,7 @@ namespace Dy struct SpatialTransform; struct Constraint; - struct DyScratchAllocator +/* struct DyScratchAllocator { char* base; size_t size; @@ -96,11 +95,10 @@ namespace Dy taken += sizeReq; return result; } - }; - + };*/ //This stuct is used in TGS - class ArticulationTempData +/* class ArticulationTempData { public: Cm::SpatialVectorF mBaseLinkMotionVelocite; @@ -108,56 +106,56 @@ namespace Dy Ps::Array mJointPosition; // joint position Ps::Array mJointVelocity; // joint velocity Ps::Array mLinkTransform; // this is the transform list for links - }; + };*/ struct ArticulationInternalConstraint { //Common/shared directional info between, frictions and drives - Cm::UnAlignedSpatialVector row0; //24 24 - Cm::UnAlignedSpatialVector row1; //24 48 - Cm::UnAlignedSpatialVector deltaVA; //24 72 - Cm::UnAlignedSpatialVector deltaVB; //24 96 + Cm::UnAlignedSpatialVector row0; //24 24 + Cm::UnAlignedSpatialVector row1; //24 48 + Cm::UnAlignedSpatialVector deltaVA; //24 72 + Cm::UnAlignedSpatialVector deltaVB; //24 96 //Response information - PxReal recipResponse; //4 100 - PxReal response; //4 104 + PxReal recipResponse; //4 100 + PxReal response; //4 104 //Joint limit values - PxReal lowLimit; //4 108 - PxReal highLimit; //4 112 - PxReal lowImpulse; //4 116 changed - PxReal highImpulse; //4 120 changed - PxReal erp; //4 124 + PxReal lowLimit; //4 108 + PxReal highLimit; //4 112 + PxReal lowImpulse; //4 116 changed + PxReal highImpulse; //4 120 changed + PxReal erp; //4 124 //Joint spring drive info - PxReal driveTargetVel; //4 128 - PxReal driveBiasCoefficient; //4 132 - PxReal driveTarget; //4 136 - PxReal driveVelMultiplier; //4 140 - PxReal driveImpulseMultiplier; //4 144 - PxReal maxDriveForce; //4 148 - PxReal driveForce; //4 152 + PxReal driveTargetVel; //4 128 + PxReal driveBiasCoefficient; //4 132 + PxReal driveTarget; //4 136 + PxReal driveVelMultiplier; //4 140 + PxReal driveImpulseMultiplier; //4 144 + PxReal maxDriveForce; //4 148 + PxReal driveForce; //4 152 - PxReal maxFrictionForce; //4 156 - PxReal frictionForce; //4 160 - PxReal frictionForceCoefficient; //4 164 + PxReal maxFrictionForce; //4 156 + PxReal frictionForce; //4 160 + PxReal frictionForceCoefficient; //4 164 - bool isLinearConstraint; //1 165 - PxU8 padding[11]; //15 176 + bool isLinearConstraint; //1 165 + PxU8 padding[11]; //11 176 }; struct ArticulationInternalLockedAxis { //How much an impulse will effect angular velocity - Cm::UnAlignedSpatialVector deltaVA; //24 24 - Cm::UnAlignedSpatialVector deltaVB; //24 48 + Cm::UnAlignedSpatialVector deltaVA; //24 24 + Cm::UnAlignedSpatialVector deltaVB; //24 48 //Jacobian axis that is locked - PxVec3 axis; //12 60 + PxVec3 axis; //12 60 //Response information - PxReal recipResponse; //4 64 + PxReal recipResponse; //4 64 //Initial error - PxReal error; //4 68 + PxReal error; //4 68 //Bias scale - PxReal biasScale; //4 72 + PxReal biasScale; //4 72 - PxU32 pad[2]; //4 80 + PxU32 pad[2]; //4 80 }; class ArticulationData @@ -168,151 +166,142 @@ namespace Dy mLinksData(NULL), mJointData(NULL), mDofs(0xffffffff), mLocks(0), mDataDirty(true) { - } ~ArticulationData(); - PX_FORCE_INLINE void init(); - PX_FORCE_INLINE void resizeLinkData(const PxU32 linkCount); - PX_FORCE_INLINE void resizeJointData(const PxU32 dofs); + PX_FORCE_INLINE void init(); + void resizeLinkData(const PxU32 linkCount); + void resizeJointData(const PxU32 dofs); - PX_FORCE_INLINE PxReal* getJointAccelerations() { return mJointAcceleration.begin(); } - PX_FORCE_INLINE PxReal* getJointVelocities() { return mJointVelocity.begin(); } - PX_FORCE_INLINE PxReal* getJointDeltaVelocities() { return mJointDeltaVelocity.begin(); } - PX_FORCE_INLINE PxReal* getJointPositions() { return mJointPosition.begin(); } - PX_FORCE_INLINE PxReal* getJointForces() { return mJointForce.begin(); } + PX_FORCE_INLINE PxReal* getJointAccelerations() { return mJointAcceleration.begin(); } + PX_FORCE_INLINE PxReal* getJointVelocities() { return mJointVelocity.begin(); } + PX_FORCE_INLINE PxReal* getJointDeltaVelocities() { return mJointDeltaVelocity.begin(); } + PX_FORCE_INLINE PxReal* getJointPositions() { return mJointPosition.begin(); } + PX_FORCE_INLINE PxReal* getJointForces() { return mJointForce.begin(); } //PX_FORCE_INLINE PxReal* getJointFrictionForces() { return mJointFrictionForce.begin(); } - PX_FORCE_INLINE ArticulationInternalConstraint& getInternalConstraint(const PxU32 dofId) { return mInternalConstraints[dofId]; } - PX_FORCE_INLINE const ArticulationInternalConstraint& getInternalConstraint(const PxU32 dofId) const { return mInternalConstraints[dofId]; } + PX_FORCE_INLINE ArticulationInternalConstraint& getInternalConstraint(const PxU32 dofId) { return mInternalConstraints[dofId]; } + PX_FORCE_INLINE const ArticulationInternalConstraint& getInternalConstraint(const PxU32 dofId) const { return mInternalConstraints[dofId]; } - PX_FORCE_INLINE ArticulationInternalLockedAxis& getInternalLocks(const PxU32 dofId) { return mInternalLockedAxes[dofId]; } - PX_FORCE_INLINE const ArticulationInternalLockedAxis& getInternalLocks(const PxU32 dofId) const { return mInternalLockedAxes[dofId]; } + PX_FORCE_INLINE ArticulationInternalLockedAxis& getInternalLocks(const PxU32 dofId) { return mInternalLockedAxes[dofId]; } + PX_FORCE_INLINE const ArticulationInternalLockedAxis& getInternalLocks(const PxU32 dofId) const { return mInternalLockedAxes[dofId]; } + PX_FORCE_INLINE Cm::SpatialVectorF* getMotionVelocities() { return mMotionVelocities.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getMotionAccelerations() { return mMotionAccelerations.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getCorioliseVectors() { return mCorioliseVectors.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getSpatialZAVectors() { return mZAForces.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getTransmittedForces() { return mJointTransmittedForce.begin(); } - PX_FORCE_INLINE Cm::SpatialVectorF* getMotionVelocities() { return mMotionVelocities.begin(); } - PX_FORCE_INLINE Cm::SpatialVectorF* getMotionAccelerations() { return mMotionAccelerations.begin(); } - PX_FORCE_INLINE Cm::SpatialVectorF* getCorioliseVectors() { return mCorioliseVectors.begin(); } - PX_FORCE_INLINE Cm::SpatialVectorF* getSpatialZAVectors() { return mZAForces.begin(); } - PX_FORCE_INLINE Cm::SpatialVectorF* getTransmittedForces() { return mJointTransmittedForce.begin(); } - - PX_FORCE_INLINE Cm::SpatialVectorF* getPosIterMotionVelocities() { return mPosIterMotionVelocities.begin(); } - PX_FORCE_INLINE PxReal* getPosIterJointDeltaVelocities() { return mPosIterJointDeltaVelocities.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getPosIterMotionVelocities() { return mPosIterMotionVelocities.begin(); } + PX_FORCE_INLINE PxReal* getPosIterJointDeltaVelocities() { return mPosIterJointDeltaVelocities.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF& getPosIterMotionVelocity(const PxU32 index) { return mPosIterMotionVelocities[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getMotionVelocity(const PxU32 index) const { return mMotionVelocities[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getMotionAcceleration(const PxU32 index) const { return mMotionAccelerations[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getCorioliseVector(const PxU32 index) const { return mCorioliseVectors[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getSpatialZAVector(const PxU32 index) const { return mZAForces[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getTransmittedForce(const PxU32 index) const { return mJointTransmittedForce[index]; } - PX_FORCE_INLINE Cm::SpatialVectorF& getPosIterMotionVelocity(const PxU32 index) { return mPosIterMotionVelocities[index]; } - PX_FORCE_INLINE const Cm::SpatialVectorF& getMotionVelocity(const PxU32 index) const { return mMotionVelocities[index]; } - PX_FORCE_INLINE const Cm::SpatialVectorF& getMotionAcceleration(const PxU32 index) const { return mMotionAccelerations[index]; } - PX_FORCE_INLINE const Cm::SpatialVectorF& getCorioliseVector(const PxU32 index) const { return mCorioliseVectors[index]; } - PX_FORCE_INLINE const Cm::SpatialVectorF& getSpatialZAVector(const PxU32 index) const { return mZAForces[index]; } - PX_FORCE_INLINE const Cm::SpatialVectorF& getTransmittedForce(const PxU32 index) const { return mJointTransmittedForce[index]; } - - - PX_FORCE_INLINE Cm::SpatialVectorF& getMotionVelocity(const PxU32 index) { return mMotionVelocities[index]; } - PX_FORCE_INLINE Cm::SpatialVectorF& getMotionAcceleration(const PxU32 index) { return mMotionAccelerations[index]; } - PX_FORCE_INLINE Cm::SpatialVectorF& getCorioliseVector(const PxU32 index) { return mCorioliseVectors[index]; } - PX_FORCE_INLINE Cm::SpatialVectorF& getSpatialZAVector(const PxU32 index) { return mZAForces[index]; } - PX_FORCE_INLINE Cm::SpatialVectorF& getTransmittedForce(const PxU32 index) { return mJointTransmittedForce[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getMotionVelocity(const PxU32 index) { return mMotionVelocities[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getMotionAcceleration(const PxU32 index) { return mMotionAccelerations[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getCorioliseVector(const PxU32 index) { return mCorioliseVectors[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getSpatialZAVector(const PxU32 index) { return mZAForces[index]; } + PX_FORCE_INLINE Cm::SpatialVectorF& getTransmittedForce(const PxU32 index) { return mJointTransmittedForce[index]; } //PX_FORCE_INLINE Dy::SpatialMatrix* getTempSpatialMatrix() { mTempSpatialMatrix.begin(); } + PX_FORCE_INLINE PxTransform& getPreTransform(const PxU32 index) { return mPreTransform[index]; } + PX_FORCE_INLINE const PxTransform& getPreTransform(const PxU32 index) const { return mPreTransform[index]; } + PX_FORCE_INLINE void setPreTransform(const PxU32 index, const PxTransform& t){ mPreTransform[index] = t; } + PX_FORCE_INLINE PxTransform* getPreTransform() { return mPreTransform.begin(); } - PX_FORCE_INLINE PxTransform& getPreTransform(const PxU32 index) { return mPreTransform[index]; } - PX_FORCE_INLINE const PxTransform& getPreTransform(const PxU32 index) const { return mPreTransform[index]; } - PX_FORCE_INLINE void setPreTransform(const PxU32 index, const PxTransform& tra) { mPreTransform[index] = tra; } - PX_FORCE_INLINE PxTransform* getPreTransform() { return mPreTransform.begin(); } - - PX_FORCE_INLINE const Cm::SpatialVectorF& getDeltaMotionVector(const PxU32 index) const { return mDeltaMotionVector[index]; } + PX_FORCE_INLINE const Cm::SpatialVectorF& getDeltaMotionVector(const PxU32 index) const { return mDeltaMotionVector[index]; } PX_FORCE_INLINE void setDeltaMotionVector(const PxU32 index, const Cm::SpatialVectorF& vec) { mDeltaMotionVector[index] = vec; } - PX_FORCE_INLINE Cm::SpatialVectorF* getDeltaMotionVector() { return mDeltaMotionVector.begin(); } + PX_FORCE_INLINE Cm::SpatialVectorF* getDeltaMotionVector() { return mDeltaMotionVector.begin(); } - PX_FORCE_INLINE ArticulationLink* getLinks() const { return mLinks; } - PX_FORCE_INLINE PxU32 getLinkCount() const { return mLinkCount; } + PX_FORCE_INLINE ArticulationLink* getLinks() const { return mLinks; } + PX_FORCE_INLINE PxU32 getLinkCount() const { return mLinkCount; } - PX_FORCE_INLINE ArticulationLink& getLink(PxU32 index) const { return mLinks[index]; } + PX_FORCE_INLINE ArticulationLink& getLink(PxU32 index) const { return mLinks[index]; } - PX_FORCE_INLINE ArticulationLinkData* getLinkData() const { return mLinksData; } - ArticulationLinkData& getLinkData(PxU32 index) const; + PX_FORCE_INLINE ArticulationLinkData* getLinkData() const { return mLinksData; } + ArticulationLinkData& getLinkData(PxU32 index) const; - PX_FORCE_INLINE ArticulationJointCoreData* getJointData() const { return mJointData; } - ArticulationJointCoreData& getJointData(PxU32 index) const { return mJointData[index]; } + PX_FORCE_INLINE ArticulationJointCoreData* getJointData() const { return mJointData; } + ArticulationJointCoreData& getJointData(PxU32 index) const { return mJointData[index]; } - PX_FORCE_INLINE const ArticulationCore* getCore() const { return mCore; } - PX_FORCE_INLINE Cm::SpatialVector* getExternalAccelerations() { return mExternalAcceleration; } + PX_FORCE_INLINE const ArticulationCore* getCore() const { return mCore; } + PX_FORCE_INLINE Cm::SpatialVector* getExternalAccelerations() { return mExternalAcceleration; } - PX_FORCE_INLINE PxU32 getSolverDataSize() { return mSolverDataSize; } - PX_FORCE_INLINE Cm::SpatialVector& getExternalAcceleration(const PxU32 linkID) { return mExternalAcceleration[linkID]; } + PX_FORCE_INLINE PxU32 getSolverDataSize() const { return mSolverDataSize; } + PX_FORCE_INLINE Cm::SpatialVector& getExternalAcceleration(const PxU32 linkID) { return mExternalAcceleration[linkID]; } - PX_FORCE_INLINE PxReal getDt() const { return mDt; } - PX_FORCE_INLINE void setDt(const PxReal dt) { mDt = dt; } + PX_FORCE_INLINE PxReal getDt() const { return mDt; } + PX_FORCE_INLINE void setDt(const PxReal dt) { mDt = dt; } - PX_FORCE_INLINE bool getDataDirty() { return mDataDirty; } - PX_FORCE_INLINE void setDataDirty(const bool dirty) { mDataDirty = dirty; } + PX_FORCE_INLINE bool getDataDirty() const { return mDataDirty; } + PX_FORCE_INLINE void setDataDirty(const bool dirty) { mDataDirty = dirty; } - PX_FORCE_INLINE PxU32 getDofs() const { return mDofs; } - PX_FORCE_INLINE void setDofs(const PxU32 dof) { mDofs = dof; } + PX_FORCE_INLINE PxU32 getDofs() const { return mDofs; } + PX_FORCE_INLINE void setDofs(const PxU32 dof) { mDofs = dof; } - PX_FORCE_INLINE PxU32 getLocks() const { return mLocks; } - PX_FORCE_INLINE void setLocks(const PxU32 locks) { mLocks = locks; } + PX_FORCE_INLINE PxU32 getLocks() const { return mLocks; } + PX_FORCE_INLINE void setLocks(const PxU32 locks) { mLocks = locks; } - PX_FORCE_INLINE FeatherstoneArticulation* getArticulation() { return mArticulation;} - PX_FORCE_INLINE void setArticulation(FeatherstoneArticulation* articulation) { mArticulation = articulation; } + PX_FORCE_INLINE FeatherstoneArticulation* getArticulation() { return mArticulation; } + PX_FORCE_INLINE void setArticulation(FeatherstoneArticulation* articulation) { mArticulation = articulation; } - PX_FORCE_INLINE const SpatialMatrix& getBaseInvSpatialArticulatedInertia() const { return mBaseInvSpatialArticulatedInertia; } + PX_FORCE_INLINE const SpatialMatrix& getBaseInvSpatialArticulatedInertia() const { return mBaseInvSpatialArticulatedInertia; } - PX_FORCE_INLINE PxTransform* getAccumulatedPoses() { return mAccumulatedPoses.begin(); } - - PX_FORCE_INLINE const PxTransform* getAccumulatedPoses() const { return mAccumulatedPoses.begin(); } + PX_FORCE_INLINE PxTransform* getAccumulatedPoses() { return mAccumulatedPoses.begin(); } + PX_FORCE_INLINE const PxTransform* getAccumulatedPoses() const { return mAccumulatedPoses.begin(); } private: - - Ps::Array mJointAcceleration; // joint acceleration - Ps::Array mJointVelocity; // joint velocity - Ps::Array mJointDeltaVelocity; // joint velocity change due to contacts - Ps::Array mJointPosition; // joint position - Ps::Array mJointForce; // joint force - //Ps::Array mJointFrictionForce; // joint friction force + Ps::Array mJointAcceleration; // joint acceleration + Ps::Array mJointVelocity; // joint velocity + Ps::Array mJointDeltaVelocity; // joint velocity change due to contacts + Ps::Array mJointPosition; // joint position + Ps::Array mJointForce; // joint force + //Ps::Array mJointFrictionForce; // joint friction force - Ps::Array mPosIterJointDeltaVelocities; //joint delta velocity after postion iternation before velocity iteration - Ps::Array mPosIterMotionVelocities; //link motion velocites after position iteration before velocity iteration - Ps::Array mMotionVelocities; //link motion velocites - Ps::Array mMotionAccelerations; //link motion accelerations - Ps::Array mCorioliseVectors; //link coriolise vector - Ps::Array mZAForces; //link spatial zero acceleration force/ spatial articulated force - Ps::Array mJointTransmittedForce; - Ps::Array mInternalConstraints; - Ps::Array mInternalLockedAxes; + Ps::Array mPosIterJointDeltaVelocities; //joint delta velocity after postion iternation before velocity iteration + Ps::Array mPosIterMotionVelocities; //link motion velocites after position iteration before velocity iteration + Ps::Array mMotionVelocities; //link motion velocites + Ps::Array mMotionAccelerations; //link motion accelerations + Ps::Array mCorioliseVectors; //link coriolise vector + Ps::Array mZAForces; //link spatial zero acceleration force/ spatial articulated force + Ps::Array mJointTransmittedForce; + Ps::Array mInternalConstraints; + Ps::Array mInternalLockedAxes; + Ps::Array mDeltaMotionVector; //this is for TGS solver + Ps::Array mPreTransform; //this is the previous transform list for links - Ps::Array mDeltaMotionVector; //this is for TGS solver - Ps::Array mPreTransform; //this is the previous transform list for links + //Ps::Array mTempSpatialMatrix; - //Ps::Array mTempSpatialMatrix; + ArticulationLink* mLinks; + PxU32 mLinkCount; + ArticulationLinkData* mLinksData; + ArticulationJointCoreData* mJointData; + PxReal mDt; + PxU32 mDofs; + PxU32 mLocks; + const ArticulationCore* mCore; + Cm::SpatialVector* mExternalAcceleration; + PxU32 mSolverDataSize; + bool mDataDirty; //this means we need to call commonInit() + bool mJointDirty; //this means joint delta velocity has been changed by contacts so we need to update joint velocity/joint acceleration + FeatherstoneArticulation* mArticulation; - ArticulationLink* mLinks; - PxU32 mLinkCount; - ArticulationLinkData* mLinksData; - ArticulationJointCoreData* mJointData; - PxReal mDt; - PxU32 mDofs; - PxU32 mLocks; - const ArticulationCore* mCore; - Cm::SpatialVector* mExternalAcceleration; - PxU32 mSolverDataSize; - bool mDataDirty; //this means we need to call commonInit() - bool mJointDirty; //this means joint delta velocity has been changed by contacts so we need to update joint velocity/joint acceleration - FeatherstoneArticulation* mArticulation; + Ps::Array mAccumulatedPoses; + Ps::Array mDeltaQ; + PxReal mAccumulatedDt; - Ps::Array mAccumulatedPoses; - Ps::Array mDeltaQ; - PxReal mAccumulatedDt; - - //ArticulationTempData mTempData; - SpatialMatrix mBaseInvSpatialArticulatedInertia; + //ArticulationTempData mTempData; + SpatialMatrix mBaseInvSpatialArticulatedInertia; friend class FeatherstoneArticulation; - }; void ArticulationData::init() @@ -323,10 +312,8 @@ namespace Dy //zero joint velocity delta, which will be changed in pxcFsApplyImpulse if there are any contact with links PxMemZero(getJointDeltaVelocities(), sizeof(PxReal) * mDofs); mJointDirty = false; - } - struct ScratchData { public: @@ -360,7 +347,6 @@ namespace Dy PxReal* jointFrictionForces; }; - #if PX_VC #pragma warning(push) #pragma warning( disable : 4324 ) // Padding was added at the end of a structure because of a __declspec(align) value. @@ -375,16 +361,12 @@ namespace Dy FeatherstoneArticulation(Sc::ArticulationSim*); ~FeatherstoneArticulation(); - // get data sizes for allocation at higher levels - virtual void getDataSizes(PxU32 linkCount, - PxU32 &solverDataSize, - PxU32& totalSize, - PxU32& scratchSize); + virtual void getDataSizes(PxU32 linkCount, PxU32& solverDataSize, PxU32& totalSize, PxU32& scratchSize); - virtual bool resize(const PxU32 linkCount); + virtual bool resize(const PxU32 linkCount); - virtual void onUpdateSolverDesc(); + virtual void onUpdateSolverDesc(); virtual PxU32 getDofs(); @@ -457,9 +439,6 @@ namespace Dy virtual PxReal getLinkMaxPenBias(const PxU32 linkID) const; - - - static PxU32 computeUnconstrainedVelocities( const ArticulationSolverDesc& desc, PxReal dt, @@ -536,14 +515,12 @@ namespace Dy PxReal* jVelocities, PxReal* jAcceleration, PxReal* jPosition, PxReal* jointForce, const PxArticulationCacheFlags flag); - ArticulationData& getArticulationData() { return mArticulationData; } + PX_FORCE_INLINE ArticulationData& getArticulationData() { return mArticulationData; } - void setGpuRemapId(const PxU32 id) { mGpuRemapId = id; } - PxU32 getGpuRemapId() { return mGpuRemapId; } + PX_FORCE_INLINE void setGpuRemapId(const PxU32 id) { mGpuRemapId = id; } + PX_FORCE_INLINE PxU32 getGpuRemapId() const { return mGpuRemapId; } private: - - void constraintPrep(ArticulationLoopConstraint* lConstraints, const PxU32 nbJoints, Cm::SpatialVectorF* Z, PxSolverConstraintPrepDesc& prepDesc, PxSolverBody& sBody, PxSolverBodyData& sBodyData, PxSolverConstraintDesc* desc, PxConstraintAllocator& allocator); @@ -553,11 +530,7 @@ namespace Dy Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); - PxU32 computeUnconstrainedVelocitiesInternal( - const ArticulationSolverDesc& desc, - PxConstraintAllocator& allocator, - PxSolverConstraintDesc* constraintDesc, PxU32& acCount, const PxVec3& gravity, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); @@ -565,9 +538,8 @@ namespace Dy void computeUnconstrainedVelocitiesTGSInternal(const PxVec3& gravity, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV); - //copy joint data from fromJointData to toJointData - void copyJointData(ArticulationData& data, PxReal* toJointData, PxReal* fromJointData); + void copyJointData(ArticulationData& data, PxReal* toJointData, const PxReal* fromJointData); void computeDofs(); //this function calculates motion subspace matrix(s) for all tree joint @@ -626,7 +598,6 @@ namespace Dy //void copyToBodyCore(); void updateBodies(); - void applyExternalImpulse(ArticulationLink* links, const PxU32 linkCount, const bool fixBase, ArticulationData& data, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* deltaV, const PxReal dt, const PxVec3& gravity, Cm::SpatialVector* acceleration); @@ -687,7 +658,6 @@ namespace Dy Cm::SpatialVector& deltaV1, PxReal* jointVelocities); - Cm::SpatialVectorF getImpulseResponseInv(const bool fixBase, const PxU32 linkID, Cm::SpatialVectorF* Z, const Cm::SpatialVector& impulse, @@ -709,7 +679,6 @@ namespace Dy //provided joint velocity and joint acceleartion, compute link acceleration void computeLinkAccelerationInv(ArticulationData& data, ScratchData& scratchData); - void computeGeneralizedForceInv(ArticulationData& data, ScratchData& scratchData); void calculateMassMatrixColInv(ScratchData& scratchData); @@ -720,7 +689,6 @@ namespace Dy //joint limits void enforcePrismaticLimits(PxReal* jPosition, ArticulationJointCore* joint); - public: static void getImpulseSelfResponse(ArticulationLink* links, @@ -769,8 +737,6 @@ namespace Dy PxReal dt); PxU32 setupSolverConstraints( - PxConstraintAllocator& allocator, - PxSolverConstraintDesc* constraintDesc, ArticulationLink* links, const PxU32 linkCount, const bool fixBase, @@ -810,7 +776,6 @@ namespace Dy PxReal damping, PxReal dt); - //integration void propagateLinksDown(ArticulationData& data, PxReal* jointVelocities, PxReal* jointPositions, Cm::SpatialVectorF* motionVelocities); @@ -823,8 +788,8 @@ namespace Dy PxU8* allocateScratchSpatialData(PxcScratchAllocator* allocator, const PxU32 linkCount, ScratchData& scratchData, bool fallBackToHeap = false); - void allocateScratchSpatialData(DyScratchAllocator& allocator, - const PxU32 linkCount, ScratchData& scratchData); +// void allocateScratchSpatialData(DyScratchAllocator& allocator, +// const PxU32 linkCount, ScratchData& scratchData); //This method calculate the velocity change from parent to child using parent current motion velocity PxTransform propagateTransform(const PxU32 linkID, ArticulationLink* links, ArticulationJointCoreData& jointDatum, diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h index 023757e6f..f6b78a4de 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationJointData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h index 33db87784..12ee2e8df 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyFeatherstoneArticulationUtils.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h index 70e0511bd..b2f0dc689 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DySleepingConfigulation.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h b/src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h index b44a7eb9e..9f0387e5a 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyThresholdTable.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h b/src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h index 2a9bbe755..419e67166 100644 --- a/src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h +++ b/src/PhysX/physx/source/lowleveldynamics/include/DyVArticulation.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -43,33 +43,19 @@ #include "DyArticulationCore.h" #include "DyArticulationJointCore.h" - namespace physx { - - class PxcRigidBody; struct PxsBodyCore; - - class PxcConstraintBlockStream; class PxcRigidBody; - class PxsConstraintBlockManager; - struct PxSolverConstraintDesc; - namespace Sc { class ArticulationSim; } - namespace Dy { - - struct PxcFsScratchAllocator; - struct PxTGSSolverConstraintDesc; - static const size_t DY_ARTICULATION_MAX_SIZE = 64; - struct ArticulationJointTransforms; class ArticulationJointCoreData; struct Constraint; class Context; @@ -119,8 +105,8 @@ namespace physx //Cm::markSerializedMem(this, sizeof(ArticulationJointCore)); parentPose = PxTransform(PxIdentity); childPose = PxTransform(PxIdentity); - internalCompliance = 0; - externalCompliance = 0; + internalCompliance = 0.0f; + externalCompliance = 0.0f; swingLimitContactDistance = 0.05f; twistLimitContactDistance = 0.05f; @@ -139,11 +125,9 @@ namespace physx relativeQuat = PxQuat(PxIdentity); jointOffset = 0; - } - ArticulationJointCore(const PxTransform& parentFrame, - const PxTransform& childFrame) + ArticulationJointCore(const PxTransform& parentFrame, const PxTransform& childFrame) { parentPose = parentFrame; childPose = childFrame; @@ -168,9 +152,8 @@ namespace physx targetV[i] = 0.f; } - - PxReal swingYLimit = PxPi / 4; - PxReal swingZLimit = PxPi / 4; + const PxReal swingYLimit = PxPi / 4.0f; + const PxReal swingZLimit = PxPi / 4.0f; limits[PxArticulationAxis::eSWING1].low = swingYLimit; limits[PxArticulationAxis::eSWING2].low = swingZLimit; @@ -180,23 +163,20 @@ namespace physx tangentialStiffness = 0.0f; tangentialDamping = 0.0f; - PxReal twistLimitLow = -PxPi / 4; - PxReal twistLimitHigh = PxPi / 4; + const PxReal twistLimitLow = -PxPi / 4.0f; + const PxReal twistLimitHigh = PxPi / 4.0f; limits[PxArticulationAxis::eTWIST].low = twistLimitLow; limits[PxArticulationAxis::eTWIST].high = twistLimitHigh; twistLimitContactDistance = 0.05f; twistLimited = false; + tanQSwingY = PxTan(swingYLimit / 4.0f); + tanQSwingZ = PxTan(swingZLimit / 4.0f); + tanQSwingPad = PxTan(swingLimitContactDistance / 4.0f); - tanQSwingY = PxTan(swingYLimit / 4); - tanQSwingZ = PxTan(swingZLimit / 4); - tanQSwingPad = PxTan(swingLimitContactDistance / 4); - - tanQTwistHigh = PxTan(twistLimitHigh / 4); - tanQTwistLow = PxTan(twistLimitLow / 4); - tanQTwistPad = PxTan(twistLimitContactDistance / 4); - - + tanQTwistHigh = PxTan(twistLimitHigh / 4.0f); + tanQTwistLow = PxTan(twistLimitLow / 4.0f); + tanQTwistPad = PxTan(twistLimitContactDistance / 4.0f); prismaticLimited = false; @@ -213,7 +193,6 @@ namespace physx relativeQuat = (childFrame.q * (parentFrame.q.getConjugate())).getNormalized(); jointOffset = 0; - } void setJointPose(ArticulationJointCoreData& jointDatum); @@ -221,7 +200,6 @@ namespace physx // PX_SERIALIZATION ArticulationJointCore(const PxEMPTY&) {} //~PX_SERIALIZATION - }; struct ArticulationLoopConstraint @@ -230,7 +208,6 @@ namespace physx PxU32 linkIndex0; PxU32 linkIndex1; Dy::Constraint* constraint; - }; #define DY_ARTICULATION_LINK_NONE 0xffffffff @@ -239,12 +216,12 @@ namespace physx struct ArticulationLink { - ArticulationBitField children; // child bitmap - ArticulationBitField pathToRoot; // path to root, including link and root - PxcRigidBody* body; - PxsBodyCore* bodyCore; - ArticulationJointCore* inboundJoint; - PxU32 parent; + ArticulationBitField children; // child bitmap + ArticulationBitField pathToRoot; // path to root, including link and root + PxcRigidBody* body; + PxsBodyCore* bodyCore; + ArticulationJointCore* inboundJoint; + PxU32 parent; }; typedef size_t ArticulationLinkHandle; @@ -293,7 +270,6 @@ namespace physx } }; - static const size_t DY_ARTICULATION_IDMASK = DY_ARTICULATION_MAX_SIZE - 1; #if PX_VC @@ -307,12 +283,13 @@ namespace physx // public interface - ArticulationV(Sc::ArticulationSim* sim, PxArticulationBase::Enum type) : mArticulationSim(sim), - mContext(NULL), - mType(type), - mUpdateSolverData(true), - mDirty(false), - mMaxDepth(0) + ArticulationV(Sc::ArticulationSim* sim, PxArticulationBase::Enum type) : + mArticulationSim (sim), + mContext (NULL), + mType (type), + mUpdateSolverData (true), + mDirty (false), + mMaxDepth (0) {} virtual ~ArticulationV() {} @@ -334,34 +311,33 @@ namespace physx mUpdateSolverData = true; } - bool updateSolverData() {return mUpdateSolverData;} + PX_FORCE_INLINE bool updateSolverData() { return mUpdateSolverData; } - void setDirty(const bool dirty) { mDirty = dirty; } - bool getDirty() { return mDirty; } + PX_FORCE_INLINE void setDirty(const bool dirty) { mDirty = dirty; } + PX_FORCE_INLINE bool getDirty() const { return mDirty; } - PX_FORCE_INLINE PxU32 getMaxDepth() { return mMaxDepth; } - PX_FORCE_INLINE void setMaxDepth(const PxU32 depth) { mMaxDepth = depth; } + PX_FORCE_INLINE PxU32 getMaxDepth() const { return mMaxDepth; } + PX_FORCE_INLINE void setMaxDepth(const PxU32 depth) { mMaxDepth = depth; } // solver methods - PX_FORCE_INLINE PxU32 getLinkIndex(ArticulationLinkHandle handle) const { return PxU32(handle&DY_ARTICULATION_IDMASK); } - PX_FORCE_INLINE PxU32 getBodyCount() const { return mSolverDesc.linkCount; } - PX_FORCE_INLINE PxU32 getSolverDataSize() const { return mSolverDesc.solverDataSize; } - PX_FORCE_INLINE PxU32 getTotalDataSize() const { return mSolverDesc.totalDataSize; } - PX_FORCE_INLINE void getSolverDesc(ArticulationSolverDesc& d) const { d = mSolverDesc; } - PX_FORCE_INLINE ArticulationSolverDesc& getSolverDesc() { return mSolverDesc; } + PX_FORCE_INLINE PxU32 getLinkIndex(ArticulationLinkHandle handle) const { return PxU32(handle&DY_ARTICULATION_IDMASK); } + PX_FORCE_INLINE PxU32 getBodyCount() const { return mSolverDesc.linkCount; } + PX_FORCE_INLINE PxU32 getSolverDataSize() const { return mSolverDesc.solverDataSize; } + PX_FORCE_INLINE PxU32 getTotalDataSize() const { return mSolverDesc.totalDataSize; } + PX_FORCE_INLINE void getSolverDesc(ArticulationSolverDesc& d) const { d = mSolverDesc; } + PX_FORCE_INLINE ArticulationSolverDesc& getSolverDesc() { return mSolverDesc; } - PX_FORCE_INLINE const ArticulationCore* getCore() const { return mSolverDesc.core; } - PX_FORCE_INLINE PxU16 getIterationCounts() const { return mSolverDesc.core->solverIterationCounts; } + PX_FORCE_INLINE const ArticulationCore* getCore() const { return mSolverDesc.core; } + PX_FORCE_INLINE PxU16 getIterationCounts() const { return mSolverDesc.core->solverIterationCounts; } - PX_FORCE_INLINE Sc::ArticulationSim* getArticulationSim() const { return mArticulationSim; } + PX_FORCE_INLINE Sc::ArticulationSim* getArticulationSim() const { return mArticulationSim; } - PX_FORCE_INLINE PxU32 getType() const { return mType; } + PX_FORCE_INLINE PxU32 getType() const { return mType; } + + PX_FORCE_INLINE void setDyContext(Dy::Context* context) { mContext = context; } // get data sizes for allocation at higher levels - virtual void getDataSizes(PxU32 linkCount, - PxU32 &solverDataSize, - PxU32& totalSize, - PxU32& scratchSize) = 0; + virtual void getDataSizes(PxU32 linkCount, PxU32& solverDataSize, PxU32& totalSize, PxU32& scratchSize) = 0; virtual PxU32 getDofs() { return 0; } @@ -417,7 +393,6 @@ namespace physx Cm::SpatialVector& deltaV0, Cm::SpatialVector& deltaV1) const = 0; - virtual Cm::SpatialVectorV getLinkVelocity(const PxU32 linkID) const = 0; virtual Cm::SpatialVectorV getLinkMotionVector(const PxU32 linkID) const = 0; @@ -447,24 +422,23 @@ namespace physx //this is called by island gen to determine whether the articulation should be awake or sleep virtual Cm::SpatialVector getMotionVelocity(const PxU32 linkID) const = 0; - void setDyContext(Dy::Context* context) { mContext = context; } //These variables are used in the constraint partition - PxU16 maxSolverFrictionProgress; - PxU16 maxSolverNormalProgress; - PxU32 solverProgress; - PxU8 numTotalConstraints; + PxU16 maxSolverFrictionProgress; + PxU16 maxSolverNormalProgress; + PxU32 solverProgress; + PxU8 numTotalConstraints; protected: - Sc::ArticulationSim* mArticulationSim; - Dy::Context* mContext; - PxU32 mType; - ArticulationSolverDesc mSolverDesc; + Sc::ArticulationSim* mArticulationSim; + Dy::Context* mContext; + PxU32 mType; + ArticulationSolverDesc mSolverDesc; - Ps::Array mAcceleration; // supplied by Sc-layer to feed into articulations + Ps::Array mAcceleration; // supplied by Sc-layer to feed into articulations - bool mUpdateSolverData; - bool mDirty; //any of links update configulations, the boolean will be set to true - PxU32 mMaxDepth; + bool mUpdateSolverData; + bool mDirty; //any of links update configulations, the boolean will be set to true + PxU32 mMaxDepth; private: PX_NOCOPY(ArticulationV) @@ -483,8 +457,6 @@ namespace physx { return !(handle & DY_ARTICULATION_IDMASK); } - - } } diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp index d3bee470d..b9d73095c 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulation.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -46,7 +46,6 @@ #include "DyTGSDynamics.h" #include "common/PxProfileZone.h" - using namespace physx; // we encode articulation link handles in the lower bits of the pointer, so the @@ -86,7 +85,6 @@ namespace physx rows[0].inertia = Fns::invertInertia(rows[0].inertia); } - PX_COMPILE_TIME_ASSERT((sizeof(Articulation)&(DY_ARTICULATION_MAX_SIZE-1))==0); Articulation::Articulation(Sc::ArticulationSim* sim) @@ -106,7 +104,6 @@ Articulation::~Articulation() { } - #if DY_DEBUG_ARTICULATION void Articulation::computeResiduals(const Cm::SpatialVector *v, @@ -134,7 +131,6 @@ void Articulation::computeResiduals(const Cm::SpatialVector *v, printf("Energy %f, Error %f\n", energy, error); } - Cm::SpatialVector Articulation::computeMomentum(const FsInertia *inertia) const { typedef ArticulationFnsScalar Fns; @@ -146,8 +142,6 @@ Cm::SpatialVector Articulation::computeMomentum(const FsInertia *inertia) const return m; } - - void Articulation::checkLimits() const { for(PxU32 i=1;ilinkCount;i++) @@ -238,8 +232,6 @@ bool Dy::Articulation::resize(const PxU32 linkCount) mMotionVelocity.resize(linkCount, Cm::SpatialVector(PxVec3(0.0f), PxVec3(0.0f))); mSolverDesc.motionVelocity = mMotionVelocity.begin(); - - } Dy::ArticulationV::resize(linkCount); @@ -264,7 +256,6 @@ bool Dy::ArticulationV::resize(const PxU32 linkCount) return true; } - void PxvRegisterArticulations() { const PxU32 type = PxU32(PxArticulationBase::eMaximumCoordinate); @@ -283,7 +274,7 @@ void PxvRegisterArticulations() SolverCoreRegisterArticulationFnsCoulomb(); } -void Articulation::getDataSizes(PxU32 linkCount, PxU32 &solverDataSize, PxU32& totalSize, PxU32& scratchSize) +void Articulation::getDataSizes(PxU32 linkCount, PxU32& solverDataSize, PxU32& totalSize, PxU32& scratchSize) { solverDataSize = sizeof(FsData) // header + sizeof(Cm::SpatialVectorV) * linkCount // velocity @@ -304,7 +295,6 @@ void Articulation::getDataSizes(PxU32 linkCount, PxU32 &solverDataSize, PxU32& t + ((sizeof(ArticulationJointTransforms)+15)&~15) * linkCount + sizeof(Mat33V) * linkCount + ((sizeof(ArticulationJointTransforms)+15)&~15) * linkCount); - } void Articulation::getImpulseResponse( @@ -317,7 +307,7 @@ void Articulation::getImpulseResponse( ArticulationHelper::getImpulseResponse(matrix, linkID, impulse, deltaV); } -void Articulation::getImpulseSelfResponse( +void Articulation::getImpulseSelfResponse( PxU32 linkID0, PxU32 linkID1, Cm::SpatialVectorF* /*Z*/, @@ -333,7 +323,6 @@ void Articulation::getImpulseSelfResponse( reinterpret_cast(deltaV1)); } - Cm::SpatialVectorV Articulation::getLinkVelocity(const PxU32 linkID) const { FsData& matrix = *getFsDataPtr(); @@ -365,7 +354,6 @@ PxReal Articulation::getLinkMaxPenBias(const PxU32 linkID) const return maxPenBias[linkID]; } - PxU32 Articulation::getFsDataSize(PxU32 linkCount) { return sizeof(FsInertia) + sizeof(FsRow) * linkCount; @@ -376,9 +364,7 @@ PxU32 Articulation::getLtbDataSize(PxU32 linkCount) return sizeof(LtbRow) * linkCount; } -void Articulation::setInertia(FsInertia& inertia, - const PxsBodyCore& body, - const PxTransform& pose) +void Articulation::setInertia(FsInertia& inertia, const PxsBodyCore& body, const PxTransform& pose) { // assumes that elements that are supposed to be zero (i.e. la matrix and off diagonal elements of ll) are zero @@ -490,8 +476,7 @@ void Articulation::prepareDataBlock(FsData& fsData, } } -void Articulation::prepareFsData(FsData& fsData, - const ArticulationLink* links) +void Articulation::prepareFsData(FsData& fsData, const ArticulationLink* links) { typedef ArticulationFnsSimd Fns; @@ -641,7 +626,6 @@ void PxcLtbProject(const FsData& m, velocity[i] -= y[i]; } - void PxcFsComputeJointLoadsSimd(const FsData& matrix, const FsInertia*PX_RESTRICT baseInertia, Mat33V*PX_RESTRICT load, @@ -760,8 +744,7 @@ PX_FORCE_INLINE Cm::SpatialVectorV propagateDrivenImpulse(const FsRow& row, return result; } -void PxcFsApplyJointDrives(FsData& matrix, - const Vec3V* Q) +void PxcFsApplyJointDrives(FsData& matrix, const Vec3V* Q) { typedef ArticulationFnsSimd Fns; @@ -780,7 +763,6 @@ void PxcFsApplyJointDrives(FsData& matrix, for (PxU32 i = matrix.linkCount; i-->1;) Z[matrix.parent[i]] += propagateDrivenImpulse(rows[i], jointVectors[i], SZminusQ[i], Z[i], Q[i]); - dV[0] = Fns::multiply(getRootInverseInertia(matrix), -Z[0]); for (PxU32 i = 1; iinternalDriveIterations & 0xffff, allocator); - } { @@ -964,7 +941,6 @@ void Articulation::computeUnconstrainedVelocitiesInternal(const ArticulationSolv maxSolverFrictionProgress = 0; solverProgress = 0; - #if DY_ARTICULATION_DEBUG_VERIFY for (PxU32 i = 0; i(desc.articulation); @@ -1128,7 +1099,7 @@ void Articulation::recordDeltaMotion(const ArticulationSolverDesc &desc, const P PxQuat* deltaQ = desc.deltaQ; - Cm::SpatialVectorV* velocity = getVelocity(m); + const Cm::SpatialVectorV* velocity = getVelocity(m); Cm::SpatialVectorV* deltaMotion = getMotionVector(m); PxcFsFlushVelocity(m); @@ -1143,7 +1114,6 @@ void Articulation::recordDeltaMotion(const ArticulationSolverDesc &desc, const P PX_ASSERT(isFiniteVec3V(velocity[i].linear)); PX_ASSERT(isFiniteVec3V(velocity[i].angular)); } - } void Articulation::deltaMotionToMotionVelocity(const ArticulationSolverDesc& desc, PxReal invDt) @@ -1151,7 +1121,7 @@ void Articulation::deltaMotionToMotionVelocity(const ArticulationSolverDesc& des Articulation* articulation = static_cast(desc.articulation); FsData& m = *articulation->getFsDataPtr(); - Cm::SpatialVectorV* deltaMotion = getMotionVector(m); + const Cm::SpatialVectorV* deltaMotion = getMotionVector(m); Cm::SpatialVectorV* velocity = getVelocity(m); @@ -1224,7 +1194,6 @@ void Articulation::pxcFsApplyImpulse(PxU32 linkID, Ps::aos::Vec3V linear, matrix.dirty |= rows[linkID].pathToRoot; } - void Articulation::pxcFsGetVelocities(PxU32 linkID, PxU32 linkID1, Cm::SpatialVectorV& v0, Cm::SpatialVectorV& v1) { //Common case - linkID = parent of linkID1... @@ -1242,13 +1211,11 @@ void Articulation::pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear pxcFsApplyImpulse(linkID2, linear2, angular2, Z, deltaV); } - void Articulation::solveInternalConstraints(const PxReal /*dt*/, const PxReal /*invDt*/, Cm::SpatialVectorF* /*impulses*/, Cm::SpatialVectorF* /*DeltaV*/, bool) { } - Cm::SpatialVectorV Articulation::pxcFsGetVelocity(PxU32 linkID) { FsData& matrix = *getFsDataPtr(); @@ -1263,7 +1230,6 @@ Cm::SpatialVectorV Articulation::pxcFsGetVelocity(PxU32 linkID) // find the dirty node on the path (including the root) with the lowest index ArticulationBitField toUpdate = rows[linkID].pathToRoot & matrix.dirty; - if (toUpdate) { // store the dV elements densely and use an array map to decode - hopefully cache friendlier @@ -1304,7 +1270,6 @@ Cm::SpatialVectorV Articulation::pxcFsGetVelocity(PxU32 linkID) p--; } - while (p) // using "for(;p;p &= (p-1))" here generates LHSs from the ArticulationLowestSetBit { PxU32 i = ArticulationLowestSetBit(p); @@ -1605,7 +1570,6 @@ void Articulation::updateBodies(const ArticulationSolverDesc& desc, PxReal dt) } } - void PxvArticulationDriveCache::initialize( FsData &fsData, PxU16 linkCount, @@ -1639,7 +1603,7 @@ void PxvArticulationDriveCache::initialize( //ArticulationHelper::initializeDriveCache(articulation, cache, linkCount, links, compliance, iterations, scratchMemory, scratchMemorySize); } -PxU32 PxvArticulationDriveCache::getLinkCount(const FsData& cache) +PxU32 PxvArticulationDriveCache::getLinkCount(const FsData& cache) { return cache.linkCount; } @@ -1652,7 +1616,7 @@ void PxvArticulationDriveCache::applyImpulses(const FsData& cache, Articulation::applyImpulses(cache, Z, V); } -void PxvArticulationDriveCache::getImpulseResponse(const FsData& cache, +void PxvArticulationDriveCache::getImpulseResponse(const FsData& cache, PxU32 linkID, const Cm::SpatialVectorV& impulse, Cm::SpatialVectorV& deltaV) diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.cpp index a95815885..759a3a766 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -269,8 +269,10 @@ void setupFinalizeExtSolverContacts( const Gu::ContactPoint* contactBase0 = buffer + c.contactPatches[c.correlationListHeads[i]].start; const PxReal combinedRestitution = contactBase0->restitution; - const PxReal staticFriction = contactBase0->staticFriction; - const PxReal dynamicFriction = contactBase0->dynamicFriction; + PxReal coefficient = (contactBase0->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && frictionPatch.anchorCount == 2) ? 0.5f : 1.f; + + const PxReal staticFriction = contactBase0->staticFriction * coefficient; + const PxReal dynamicFriction = contactBase0->dynamicFriction * coefficient; const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h index 3d9fc7abe..6b129b7e7 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrep.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp index af2c30d54..5805d3ea3 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationContactPrepPF.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h index 676eb8a98..ff9437716 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsDebug.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h index 24a8d3f02..34be07d5c 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsScalar.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h index cf3bfef99..c1c2f41fb 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationFnsSimd.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp index 4653413c3..868055df6 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h index 0775d4066..dfc22445c 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationHelper.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h index 917c49682..f1501ae06 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationPImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h index cece78469..0e2b4b2de 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationReference.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp index d5b5466c8..a45c83905 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationSIMD.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp index 5889bb330..03ebb9021 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.h index c548161b9..0285a3e4b 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationScalar.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h index 7ac2ee46c..d23b38567 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyArticulationUtils.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h b/src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h index e8cf685e4..d872e0839 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyBodyCoreIntegrator.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp index 5d95b8e69..ca6805e6f 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "DyConstraintPartition.h" #include "DyArticulationUtils.h" diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h index a66cd6993..93c767502 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPartition.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h index 27d6190bd..943c908fc 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintPrep.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp index 911f76e58..004fa94f2 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetup.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetupBlock.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetupBlock.cpp index 68010cce9..c1dafbd22 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetupBlock.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyConstraintSetupBlock.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp index 0b02e9eed..803b2dd0e 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -239,9 +239,12 @@ static void setupFinalizeSolverConstraints(Sc::ShapeInteraction* shapeInteractio PxF32* forceBuffers = reinterpret_cast(ptr); PxMemZero(forceBuffers, sizeof(PxF32) * contactCount); ptr += ((contactCount + 3) & (~3)) * sizeof(PxF32); // jump to next 16-byte boundary + + const PxReal frictionCoefficient = (contactBase0->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && frictionPatch.anchorCount == 2) ? 0.5f : 1.f; + + const PxReal staticFriction = contactBase0->staticFriction * frictionCoefficient; + const PxReal dynamicFriction = contactBase0->dynamicFriction* frictionCoefficient; - const PxReal staticFriction = contactBase0->staticFriction; - const PxReal dynamicFriction = contactBase0->dynamicFriction; const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W=V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); @@ -264,7 +267,7 @@ static void setupFinalizeSolverConstraints(Sc::ShapeInteraction* shapeInteractio //const Vec3V normal = Vec3V_From_PxVec3_Aligned(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); const FloatV orthoThreshold = FLoad(0.70710678f); - const FloatV p1 = FLoad(0.1f); + const FloatV p1 = FLoad(0.0001f); // fallback: normal.cross((1,0,0)) or normal.cross((0,0,1)) const FloatV normalX = V3GetX(normal); const FloatV normalY = V3GetY(normal); diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h index 9edef9ee7..11b6b58bd 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp index d8fea61a1..1b87984df 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -287,7 +287,7 @@ static void setupFinalizeSolverConstraints4(PxSolverContactDesc* PX_RESTRICT des PxU32 maxPatches = PxMax(descs[0].numFrictionPatches, PxMax(descs[1].numFrictionPatches, PxMax(descs[2].numFrictionPatches, descs[3].numFrictionPatches))); - const Vec4V p1 = V4Splat(FLoad(0.1f)); + const Vec4V p1 = V4Splat(FLoad(0.0001f)); const Vec4V orthoThreshold = V4Splat(FLoad(0.70710678f)); @@ -647,15 +647,6 @@ static void setupFinalizeSolverConstraints4(PxSolverContactDesc* PX_RESTRICT des Vec4V maxImpulseScale = V4One(); { - const Vec4V staticFriction = V4LoadXYZW(contactBase0->staticFriction, contactBase1->staticFriction, - contactBase2->staticFriction, contactBase3->staticFriction); - - const Vec4V dynamicFriction = V4LoadXYZW(contactBase0->dynamicFriction, contactBase1->dynamicFriction, - contactBase2->dynamicFriction, contactBase3->dynamicFriction); - - PX_ASSERT(totalContacts == contactCount); - header->dynamicFriction = dynamicFriction; - header->staticFriction = staticFriction; const FrictionPatch& frictionPatch0 = c.frictionPatches[frictionIndex0]; const FrictionPatch& frictionPatch1 = c.frictionPatches[frictionIndex1]; @@ -671,9 +662,26 @@ static void setupFinalizeSolverConstraints4(PxSolverContactDesc* PX_RESTRICT des PxU32 clampedAnchorCount1 = hasFinished1 || (contactBase1->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount1; PxU32 clampedAnchorCount2 = hasFinished2 || (contactBase2->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount2; PxU32 clampedAnchorCount3 = hasFinished3 || (contactBase3->materialFlags & PxMaterialFlag::eDISABLE_FRICTION) ? 0 : anchorCount3; - + const PxU32 maxAnchorCount = PxMax(clampedAnchorCount0, PxMax(clampedAnchorCount1, PxMax(clampedAnchorCount2, clampedAnchorCount3))); + PxReal coefficient0 = (contactBase0->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && anchorCount0 == 2) ? 0.5f : 1.f; + PxReal coefficient1 = (contactBase1->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && anchorCount1 == 2) ? 0.5f : 1.f; + PxReal coefficient2 = (contactBase2->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && anchorCount2 == 2) ? 0.5f : 1.f; + PxReal coefficient3 = (contactBase3->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && anchorCount3 == 2) ? 0.5f : 1.f; + + const Vec4V staticFriction = V4LoadXYZW(contactBase0->staticFriction*coefficient0, contactBase1->staticFriction*coefficient1, + contactBase2->staticFriction*coefficient2, contactBase3->staticFriction*coefficient3); + + const Vec4V dynamicFriction = V4LoadXYZW(contactBase0->dynamicFriction*coefficient0, contactBase1->dynamicFriction*coefficient1, + contactBase2->dynamicFriction*coefficient2, contactBase3->dynamicFriction*coefficient3); + + PX_ASSERT(totalContacts == contactCount); + header->dynamicFriction = dynamicFriction; + header->staticFriction = staticFriction; + + + //if(clampedAnchorCount0 != clampedAnchorCount1 || clampedAnchorCount0 != clampedAnchorCount2 || clampedAnchorCount0 != clampedAnchorCount3) // Ps::debugBreak(); diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp index 0965f438c..ab03e6401 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrep4PF.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp index 1b0803c9d..8fcca5da2 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepPF.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h index e9684ba38..44430659a 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactPrepShared.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h b/src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h index 8ac9d62d4..312c6a0df 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyContactReduction.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h b/src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h index 4a091de52..7f161f98b 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyCorrelationBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp index 46df6a084..3e8df09ae 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h index 22bddcbab..04d2ba6b2 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyDynamics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp index c32da63c6..f82fbfcda 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulation.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -52,7 +52,6 @@ #define FEATURESTONE_DEBUG 0 #endif - // we encode articulation link handles in the lower bits of the pointer, so the // articulation has to be aligned, which in an aligned pool means we need to size it // appropriately @@ -68,7 +67,7 @@ namespace Dy void PxvRegisterArticulationsReducedCoordinate() { - PxArticulationBase::Enum type = PxArticulationBase::eReducedCoordinate; + const PxArticulationBase::Enum type = PxArticulationBase::eReducedCoordinate; ArticulationPImpl::sComputeUnconstrainedVelocities[type] = &FeatherstoneArticulation::computeUnconstrainedVelocities; ArticulationPImpl::sUpdateBodies[type] = &FeatherstoneArticulation::updateBodies; ArticulationPImpl::sUpdateBodiesTGS[type] = &FeatherstoneArticulation::updateBodiesTGS; @@ -84,10 +83,8 @@ namespace Dy SolverCoreRegisterArticulationFnsCoulomb(); } - ArticulationData::~ArticulationData() { - if (mLinksData) PX_FREE_AND_RESET(mLinksData); @@ -130,7 +127,6 @@ namespace Dy mJointTransmittedForce.reserve(linkCount); mJointTransmittedForce.forceSize_Unsafe(linkCount); - if (mLinksData) PX_FREE_AND_RESET(mLinksData); @@ -140,7 +136,6 @@ namespace Dy mLinksData = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ArticulationLinkData) * linkCount, PX_DEBUG_EXP("ArticulationLinkData")), ArticulationLinkData)(); mJointData = PX_PLACEMENT_NEW(PX_ALLOC(sizeof(ArticulationJointCoreData) * linkCount, PX_DEBUG_EXP("ArticulationJointCoreData")), ArticulationJointCoreData)(); - const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount; PxMemZero(mMotionVelocities.begin(), size); @@ -153,8 +148,6 @@ namespace Dy PxMemZero(mLinksData, sizeof(ArticulationLinkData) * linkCount); PxMemZero(mJointData, sizeof(ArticulationJointCoreData) * linkCount); - - } void ArticulationData::resizeJointData(const PxU32 dofs) @@ -180,7 +173,6 @@ namespace Dy mPosIterJointDeltaVelocities.reserve(dofs); mPosIterJointDeltaVelocities.forceSize_Unsafe(dofs); - /*mTempData.mJointForce.reserve(dofs); mTempData.mJointForce.forceSize_Unsafe(dofs);*/ @@ -230,7 +222,7 @@ namespace Dy { } - void FeatherstoneArticulation::copyJointData(ArticulationData& data, PxReal* toJointData, PxReal* fromJointData) + void FeatherstoneArticulation::copyJointData(ArticulationData& data, PxReal* toJointData, const PxReal* fromJointData) { const PxU32 linkCount = data.getLinkCount(); @@ -240,7 +232,7 @@ namespace Dy PxReal* dest = &toJointData[jointDatum.jointOffset]; - PxReal* source = &fromJointData[jointDatum.jointOffset]; + const PxReal* source = &fromJointData[jointDatum.jointOffset]; for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) { @@ -300,10 +292,8 @@ namespace Dy return false; } - - void FeatherstoneArticulation::getDataSizes(PxU32 linkCount, PxU32 &solverDataSize, PxU32& totalSize, PxU32& scratchSize) + void FeatherstoneArticulation::getDataSizes(PxU32 /*linkCount*/, PxU32& solverDataSize, PxU32& totalSize, PxU32& scratchSize) { - PX_UNUSED(linkCount); solverDataSize = 0; totalSize = 0; scratchSize = 0; @@ -542,9 +532,6 @@ namespace Dy PxMemZero(deferredZ, sizeof(Cm::SpatialVectorF)*linkCount); } - - - void FeatherstoneArticulation::recordDeltaMotion(const ArticulationSolverDesc& desc, const PxReal dt, Cm::SpatialVectorF* deltaV) { @@ -563,7 +550,6 @@ namespace Dy PxReal* jointVelocities = data.getJointVelocities(); PxReal* jointDeltaVelocities = data.getJointDeltaVelocities(); - data.mAccumulatedDt += dt; data.setDt(dt); @@ -600,8 +586,6 @@ namespace Dy deltaMotion[0] += motionVelocity * dt; /*deltaMotion[0].top = ang; deltaMotion[0].bottom = lin;*/ - - } for (PxU32 linkID = 1; linkID < linkCount; linkID++) @@ -625,12 +609,10 @@ namespace Dy jointDeltaVelocities[idx] = 0.f; } - PxVec3 lin, ang; calculateNewVelocity(newPose, data.mPreTransform[linkID], 1.f, lin, ang); - deltaMotion[linkID].top = ang;// motionVeloties[linkID].top * dt; deltaMotion[linkID].bottom = lin;// motionVeloties[linkID].top * dt; @@ -638,8 +620,6 @@ namespace Dy data.mAccumulatedPoses[linkID] = newPose; #if 0 - - ArticulationLink& link = data.getLink(linkID); if (link.inboundJoint->jointType != PxArticulationJointType::eSPHERICAL) @@ -678,7 +658,6 @@ namespace Dy void FeatherstoneArticulation::deltaMotionToMotionVelocity(const ArticulationSolverDesc& desc, PxReal invDt) { - FeatherstoneArticulation* articulation = static_cast(desc.articulation); ArticulationData& data = articulation->mArticulationData; const PxU32 linkCount = data.getLinkCount(); @@ -694,15 +673,12 @@ namespace Dy v = delta; desc.motionVelocity[linkID] = reinterpret_cast(delta); - } } - //This is used in the solveExt1D, solveExtContact Cm::SpatialVectorV FeatherstoneArticulation::pxcFsGetVelocity(PxU32 linkID) { - Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors(); const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; @@ -839,7 +815,6 @@ namespace Dy deltaV1 = propagateVelocityTestImpulse(tLinkDatum, tJointDatum, deferredZ[index], deltaV1); } - //ArticulationLink& link = mArticulationData.getLink(linkID); //PxTransform& body2World = link.bodyCore->body2World; //PxTransform& body2World = mArticulationData.getAccumulatedPoses()[linkID]; @@ -919,7 +894,6 @@ namespace Dy Z0 = propagateImpulse(tLinkDatum, jointDatum, Z0); deferredZ[tLink.parent] += Z0; } - } void FeatherstoneArticulation::pxcFsApplyImpulses(Cm::SpatialVectorF* Z) @@ -950,7 +924,6 @@ namespace Dy } deferredZ[0] += Z[0]; - } void FeatherstoneArticulation::pxcFsApplyImpulses(PxU32 linkID, const Ps::aos::Vec3V& linear, @@ -993,8 +966,6 @@ namespace Dy PxTransform& body2World = data.getPreTransform(linkID); PxTransform& body2World2 = data.getPreTransform(linkID2); - - PxU64 commonId = links[linkID].pathToRoot & links[linkID2].pathToRoot; PxU32 commonLink = Dy::ArticulationHighestSetBit(commonId); //Now, work from one to that common, then the other to that common, then go from there upwards... @@ -1152,9 +1123,6 @@ namespace Dy jVelocity[0] += jDeltaVelocity[0]; jDeltaVelocity[0] = 0.f; - - - newParentToChild = joint->relativeQuat; const PxVec3 e = newParentToChild.rotate(parentOffset); const PxVec3 d = childOffset; @@ -1233,9 +1201,7 @@ namespace Dy if (jRotation.w < 0) //shortest angle. jRotation = -jRotation; - jointRotation = jointRotation * jRotation; - - + jointRotation = jointRotation * jRotation; } newParentToChild = (jointRotation * joint->relativeQuat).getNormalized(); const PxVec3 e = newParentToChild.rotate(parentOffset); @@ -1243,12 +1209,9 @@ namespace Dy r = e + d; PX_ASSERT(r.isFinite()); - } else { - - PxVec3 worldAngVel = motionVelocities[linkID].top; newWorldQ = Ps::exp(worldAngVel*dt) * currentTransform.q; @@ -1314,7 +1277,6 @@ namespace Dy return cBody2World; } - const PxTransform& FeatherstoneArticulation::getCurrentTransform(PxU32 linkID) const { return mArticulationData.mAccumulatedPoses[linkID]; @@ -1329,7 +1291,6 @@ namespace Dy Cm::SpatialVectorF FeatherstoneArticulation::propagateVelocity(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z, PxReal* jointVelocity, const Cm::SpatialVectorF& hDeltaV) { - Cm::SpatialVectorF pDeltaV = linkDatum.childToParent.transposeTransform(hDeltaV); //parent velocity change Cm::SpatialVectorF temp = linkDatum.spatialArticulatedInertia * pDeltaV + Z; @@ -1362,12 +1323,10 @@ namespace Dy return pDeltaV + jointSpatialDeltaV; } - //This method calculate the velocity change due to collision/constraint impulse Cm::SpatialVectorF FeatherstoneArticulation::propagateVelocityTestImpulse(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z, const Cm::SpatialVectorF& hDeltaV) { - const SpatialTransform& c2p = linkDatum.childToParent; Cm::SpatialVectorF pDeltaV = c2p.transposeTransform(hDeltaV); //parent velocity change @@ -1430,7 +1389,6 @@ namespace Dy Cm::SpatialVectorF FeatherstoneArticulation::propagateImpulse(const ArticulationLinkData& linkDatum, const ArticulationJointCoreData& jointDatum, const Cm::SpatialVectorF& Z) { - Cm::SpatialVectorF temp(PxVec3(0.f), PxVec3(0.f)); for (PxU32 ind = 0; ind < jointDatum.dof; ++ind) @@ -1448,7 +1406,6 @@ namespace Dy const ArticulationData& data, Cm::SpatialVectorF* Z, PxReal* jointVelocities) { - Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero(); if (!fixBase) { @@ -1473,7 +1430,6 @@ namespace Dy Cm::SpatialVectorF FeatherstoneArticulation::getDeltaV(const bool fixBase, const PxU32 linkID, const ArticulationData& data, Cm::SpatialVectorF* Z) { - Cm::SpatialVectorF deltaV = Cm::SpatialVectorF::Zero(); if (!fixBase) { @@ -1525,7 +1481,6 @@ namespace Dy return getDeltaV(fixBase, linkID, data, Z); } - //This method use in impulse self response. The input impulse is in the link space Cm::SpatialVectorF FeatherstoneArticulation::getImpulseResponseWithJ( const PxU32 linkID, @@ -1542,7 +1497,6 @@ namespace Dy void FeatherstoneArticulation::saveVelocity(const ArticulationSolverDesc& d, Cm::SpatialVectorF* deltaV) { - FeatherstoneArticulation* arti = static_cast(d.articulation); ArticulationData& data = arti->mArticulationData; @@ -1561,7 +1515,7 @@ namespace Dy PxReal* jPosDeltaVels = data.getPosIterJointDeltaVelocities(); - PxReal* jDeltaVels = data.getJointDeltaVelocities(); + const PxReal* jDeltaVels = data.getJointDeltaVelocities(); PxMemCopy(jPosDeltaVels, jDeltaVels, sizeof(PxReal) * dofs); @@ -1569,8 +1523,6 @@ namespace Dy { PX_ASSERT(PxAbs(jPosDeltaVels[i]) < 30.f); }*/ - - } void FeatherstoneArticulation::saveVelocityTGS(const ArticulationSolverDesc& d, PxReal invDtF32) @@ -1579,7 +1531,6 @@ namespace Dy PX_UNUSED(invDtF32); } - void FeatherstoneArticulation::getImpulseSelfResponse( PxU32 linkID0, PxU32 linkID1, @@ -1595,7 +1546,6 @@ namespace Dy reinterpret_cast(deltaV1)); } - void getImpulseResponseSlow(Dy::ArticulationLink* links, const ArticulationData& data, PxU32 linkID0_, @@ -1605,7 +1555,6 @@ namespace Dy const Cm::SpatialVector& impulse1, Cm::SpatialVector& deltaV1) { - PxU32 stack[DY_ARTICULATION_MAX_SIZE]; Cm::SpatialVectorF Z[DY_ARTICULATION_MAX_SIZE]; //Cm::SpatialVectorF ZZV[DY_ARTICULATION_MAX_SIZE]; @@ -1615,7 +1564,6 @@ namespace Dy PxU32 linkID0 = linkID0_; PxU32 linkID1 = linkID1_; - const PxTransform& transform0 = data.getLink(linkID0_).bodyCore->body2World; const PxTransform& transform1 = data.getLink(linkID1_).bodyCore->body2World; @@ -1669,7 +1617,6 @@ namespace Dy for (PxU32 index = ic; (index--) > i1;) v = FeatherstoneArticulation::propagateVelocityTestImpulse(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], v); - Cm::SpatialVectorF dv1 = v; for (PxU32 index = i1; (index--) > i0;) dv1 = FeatherstoneArticulation::propagateVelocityTestImpulse(data.getLinkData(stack[index]), data.getJointData(stack[index]), Z[stack[index]], dv1); @@ -1683,10 +1630,8 @@ namespace Dy deltaV1.linear = transform1.rotate(dv1.bottom); deltaV1.angular = transform1.rotate(dv1.top); - } - void FeatherstoneArticulation::getImpulseSelfResponse(ArticulationLink* links, const bool fixBase, Cm::SpatialVectorF* Z, @@ -1791,12 +1736,10 @@ namespace Dy { init(s, PxVec3(0), PxVec3(0), axis, axis, 0, PX_MAX_F32); - FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, links[linkIndex].parent, Cm::SpatialVector(PxVec3(0), axis), s.deltaVA, linkIndex, Cm::SpatialVector(PxVec3(0), -axis), s.deltaVB); - const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); if (unitResponse<0.0f) Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Warning: articulation ill-conditioned or under severe stress, joint limit ignored"); @@ -1809,7 +1752,6 @@ namespace Dy s.impulseMultiplier = 1.0f; } - void FeatherstoneArticulation::createHardLimits( SolverConstraint1DExt& s0, SolverConstraint1DExt& s1, @@ -1840,7 +1782,6 @@ namespace Dy s1.impulseMultiplier = 1.0f; } - void FeatherstoneArticulation::createTangentialSpring( ArticulationLink* links, const bool fixBase, @@ -1900,7 +1841,6 @@ namespace Dy c.ang0Writeback = c.ang0; } - void createSpringDrive(Dy::SolverConstraint1DExt& c, PxReal error, PxReal targetVelocity, PxReal maxImpulse, PxReal stiffness, PxReal damping, PxReal dt, const Cm::SpatialVectorV& deltaVA, const Cm::SpatialVectorV& deltaVB, const PxReal unitResponse) @@ -1954,7 +1894,6 @@ namespace Dy c.flags = 0; } - void createDriveOrLimit(Dy::SolverConstraint1DExtStep& c, PxReal error, const PxReal minImpulse, const PxReal maxImpulse, bool keepBias, PxReal maxBias, const PxReal velTarget, const PxReal biasCoefficient, bool isLimit, const Cm::SpatialVectorV& deltaVA, const Cm::SpatialVectorV& deltaVB, PxReal recipResponse) @@ -2034,13 +1973,12 @@ namespace Dy ArticulationInternalConstraint* constraints = data.mInternalConstraints.begin(); ArticulationInternalLockedAxis* locks = data.mInternalLockedAxes.begin(); - for (PxU16 linkID = 1; linkID < linkCount; linkID++) { const ArticulationLink& link = links[linkID]; ArticulationJointCoreData& jointDatum = data.getJointData(linkID); - PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + const PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; PX_UNUSED(jPosition); const ArticulationLink& pLink = links[link.parent]; @@ -2049,7 +1987,7 @@ namespace Dy //const bool jointDrive = (j.driveType != PxArticulationJointDriveType::eNONE); - bool hasFriction = j.frictionCoefficient > 0.f; + const bool hasFriction = j.frictionCoefficient > 0.f; const PxReal fCoefficient = j.frictionCoefficient * dt; const PxReal transmissionForce = jointTransmittedForce[linkID].magnitude() * fCoefficient; @@ -2058,7 +1996,6 @@ namespace Dy const PxU32 limitedRows = 2u * jointDatum.limitedAxes; const PxU32 lockedRows = jointDatum.lockedAxes; - PxU8 driveRows = 0; for (PxU32 i = 0; i < PxArticulationAxis::eCOUNT; ++i) @@ -2088,7 +2025,7 @@ namespace Dy { if (j.motion[i] != PxArticulationMotion::eLOCKED) { - bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + const bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) { @@ -2110,13 +2047,13 @@ namespace Dy if (ratio < 1e-4f) unitResponse = 0.f;*/ - Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); - Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); + const Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); + const Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); - PxReal r0 = deltaV0.angular.dot(axis); - PxReal r1 = deltaV1.angular.dot(axis); + const PxReal r0 = deltaV0.angular.dot(axis); + const PxReal r1 = deltaV1.angular.dot(axis); - PxReal unitResponse = r0 - r1; + const PxReal unitResponse = r0 - r1; //Cm::SpatialVector rem0(deltaV0.linear, deltaV0.angular - (axis * r0)); //Cm::SpatialVector rem1(deltaV1.linear, deltaV1.angular - (axis*r1)); @@ -2159,19 +2096,18 @@ namespace Dy constraints->frictionForce = 0.f; constraints->maxFrictionForce = hasFriction ? transmissionForce/dt : 0.f; constraints->frictionForceCoefficient = isTGSSolver ? 0.f : 1.f; - if (hasDrive) { - PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; + const PxReal targetVelocity = jointDatum.targetJointVelocity[dofId]; PxReal targetPos = jointDatum.targetJointPosition[dofId]; //KS - clamp drive target within limits - no point in having 2 parts fight against each-other if (j.motion[i] == PxArticulationMotion::eLIMITED) targetPos = PxClamp(targetPos, j.limits[i].low, j.limits[i].high); - PxReal a = dt * dt * j.drives[i].stiffness + dt * j.drives[i].damping; - PxReal b = dt * (j.drives[i].damping * targetVelocity /*+ j.drives[i].stiffness * (targetPos - jointPos)*/); + const PxReal a = dt * dt * j.drives[i].stiffness + dt * j.drives[i].damping; + const PxReal b = dt * (j.drives[i].damping * targetVelocity /*+ j.drives[i].stiffness * (targetPos - jointPos)*/); PxReal x = 0.f; if (!j.drives[i].isAcceleration) @@ -2214,7 +2150,7 @@ namespace Dy { if (j.motion[i] != PxArticulationMotion::eLOCKED) { - bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); + const bool hasDrive = (j.motion[i] != PxArticulationMotion::eLOCKED && j.drives[i].maxForce > 0.f && (j.drives[i].stiffness > 0.f || j.drives[i].damping > 0.f)); if (j.motion[i] == PxArticulationMotion::eLIMITED || hasDrive || frictionRows) { @@ -2232,14 +2168,14 @@ namespace Dy //Now add in friction rows, then limit rows, then drives... - Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); - Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); + const Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); + const Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); - PxReal r0 = deltaV0.linear.dot(axis) + deltaV0.angular.dot(ang0); - PxReal r1 = deltaV1.linear.dot(axis) + + const PxReal r0 = deltaV0.linear.dot(axis) + deltaV0.angular.dot(ang0); + const PxReal r1 = deltaV1.linear.dot(axis) + deltaV1.angular.dot(ang1); - PxReal unitResponse = r0 - r1; + const PxReal unitResponse = r0 - r1; //Cm::SpatialVector rem0(deltaV0.linear - axis * r0, deltaV0.angular - (ang0 * r0)); //Cm::SpatialVector rem1(deltaV1.linear - axis * r1, deltaV1.angular - (ang1*r1)); @@ -2296,15 +2232,15 @@ namespace Dy if (hasDrive) { - PxReal targetVelocity = -jointDatum.targetJointVelocity[dofId]; + const PxReal targetVelocity = -jointDatum.targetJointVelocity[dofId]; PxReal targetPos = jointDatum.targetJointPosition[dofId]; //KS - clamp drive target within limits - no point in having 2 parts fight against each-other if (j.motion[i] == PxArticulationMotion::eLIMITED) targetPos = PxClamp(targetPos, j.limits[i].low, j.limits[i].high); - PxReal a = dt * dt * j.drives[i].stiffness + dt * j.drives[i].damping; - PxReal b = dt * (j.drives[i].damping * targetVelocity /*+ j.drives[i].stiffness * (targetPos - jointPos)*/); + const PxReal a = dt * dt * j.drives[i].stiffness + dt * j.drives[i].damping; + const PxReal b = dt * (j.drives[i].damping * targetVelocity /*+ j.drives[i].stiffness * (targetPos - jointPos)*/); PxReal x = 0.f; if (!j.drives[i].isAcceleration) @@ -2374,13 +2310,13 @@ namespace Dy if (ratio < 1e-4f) unitResponse = 0.f;*/ - Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); - Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); + const Cm::SpatialVector& deltaV0 = unsimdRef(deltaVA); + const Cm::SpatialVector& deltaV1 = unsimdRef(deltaVB); - PxReal r0 = deltaV0.angular.dot(axis); - PxReal r1 = deltaV1.angular.dot(axis); + const PxReal r0 = deltaV0.angular.dot(axis); + const PxReal r1 = deltaV1.angular.dot(axis); - PxReal unitResponse = r0 - r1; + const PxReal unitResponse = r0 - r1; //Cm::SpatialVector rem0(deltaV0.linear, deltaV0.angular - (axis * r0)); //Cm::SpatialVector rem1(deltaV1.linear, deltaV1.angular - (axis * r1)); @@ -2416,8 +2352,6 @@ namespace Dy #if 1 PxU32 FeatherstoneArticulation::setupSolverConstraints( - PxConstraintAllocator& /*allocator*/, - PxSolverConstraintDesc* /*constraintDesc*/, ArticulationLink* links, const PxU32 linkCount, const bool fixBase, @@ -2434,7 +2368,6 @@ namespace Dy #else - PxU32 FeatherstoneArticulation::setupSolverConstraints( PxConstraintAllocator& allocator, PxSolverConstraintDesc* constraintDesc, @@ -3101,7 +3034,6 @@ namespace Dy #endif - void FeatherstoneArticulation::createHardLimitTGS( SolverConstraint1DExtStep& s, const PxVec3& axis, @@ -3116,7 +3048,6 @@ namespace Dy s.deltaVA = deltaVA; s.deltaVB = deltaVB; - s.error = error; s.biasScale = -recipDt*0.7f; @@ -3146,7 +3077,7 @@ namespace Dy init(s, PxVec3(0), PxVec3(0), axis, axis, -PX_MAX_F32, PX_MAX_F32); Cm::SpatialVector axis6(PxVec3(0), axis); - PxU32 parent = links[linkIndex].parent; + const PxU32 parent = links[linkIndex].parent; FeatherstoneArticulation::getImpulseSelfResponse(links, fixBase, Z, data, parent, axis6, s.deltaVA, linkIndex, -axis6, s.deltaVB); const PxReal unitResponse = axis.dot(reinterpret_cast(s.deltaVA.angular)) - axis.dot(reinterpret_cast(s.deltaVB.angular)); @@ -3193,13 +3124,12 @@ namespace Dy ArticulationJointCore* joint = link.inboundJoint; - PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; + const PxReal* jPosition = &jointPositions[jointDatum.jointOffset]; PxQuat newParentToChild; PxQuat newWorldQ; PxVec3 r; - const PxVec3 childOffset = -joint->childPose.p; const PxVec3 parentOffset = joint->parentPose.p; @@ -3274,8 +3204,7 @@ namespace Dy } void FeatherstoneArticulation::jcalc(ArticulationData& data) - { - + { if (getDirty()) { ArticulationLink* links = data.getLinks(); @@ -3349,7 +3278,6 @@ namespace Dy void FeatherstoneArticulation::computeZ(ArticulationData& data, const PxVec3& gravity, ScratchData& scratchData) { - Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; Cm::SpatialVectorF* spatialZAForces = scratchData.spatialZAVectors; Cm::SpatialVector* externalAccels = scratchData.externalAccels; @@ -3402,7 +3330,7 @@ namespace Dy const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass; - PxMat33& I = linkDatum.spatialInertia.bottomLeft; + const PxMat33& I = linkDatum.spatialInertia.bottomLeft; const PxVec3 inertiaTensor(I.column0.x, I.column1.y, I.column2.z); @@ -3431,9 +3359,11 @@ namespace Dy { Cm::SpatialVectorF* motionVelocities = scratchData.motionVelocities; - Cm::SpatialVectorF* dragForces = Z; + Cm::SpatialVectorF* dragImpulse = Z; - PxMemZero(dragForces, sizeof(Cm::SpatialVectorF)*data.getLinkCount()); + Cm::SpatialVectorF* ZAForces = scratchData.spatialZAVectors; + + PxMemZero(dragImpulse, sizeof(Cm::SpatialVectorF)*data.getLinkCount()); const PxReal dt = data.getDt(); @@ -3449,18 +3379,21 @@ namespace Dy const PxReal m = core.inverseMass == 0.f ? 0.f : 1.0f / core.inverseMass; //PxMat33& I = linkDatum.spatialArticulatedInertia.bottomLeft; - PxVec3 I = PxVec3(1.f / core.inverseInertia.x, 1.f / core.inverseInertia.y, 1.f / core.inverseInertia.z); + const PxVec3 I = PxVec3(1.f / core.inverseInertia.x, 1.f / core.inverseInertia.y, 1.f / core.inverseInertia.z); - Cm::SpatialVectorF& d = dragForces[linkID]; + Cm::SpatialVectorF& d = dragImpulse[linkID]; Cm::SpatialVectorF v; v.top = body2World.rotateInv(motionVelocities[linkID].top); - v.bottom = body2World.rotateInv(motionVelocities[linkID].bottom); + v.bottom = body2World.rotateInv(motionVelocities[linkID].bottom); if (core.linearDamping > 0.f || core.angularDamping > 0.f) { - d.top += (v.bottom * core.linearDamping*m*dt); - d.bottom += I.multiply(v.top* core.angularDamping*dt); + const PxReal linDamp = PxMin(core.linearDamping*dt, 1.f); + const PxReal angDamp = PxMin(core.angularDamping*dt, 1.f); + + d.top += (v.bottom * linDamp*m) -ZAForces[linkID].top * dt * linDamp; + d.bottom += I.multiply(v.top* angDamp) -ZAForces[linkID].bottom * dt * angDamp; bAppliedImpusle = true; } @@ -3472,11 +3405,10 @@ namespace Dy if (angMag > maxAng || linMag > maxLin) { - if (angMag > maxAng) { - PxReal scale = 1.f - PxSqrt(maxAng) / PxSqrt(angMag); - PxVec3 tmpaccelerationAng = (I.multiply(v.top)*scale); + const PxReal scale = 1.f - PxSqrt(maxAng) / PxSqrt(angMag); + const PxVec3 tmpaccelerationAng = (I.multiply(v.top)*scale); PX_UNUSED(tmpaccelerationAng); d.bottom = tmpaccelerationAng; @@ -3485,20 +3417,19 @@ namespace Dy if (linMag > maxLin) { - PxReal scale = 1.f - (PxSqrt(maxLin) / PxSqrt(linMag))*0.8f; - PxVec3 tmpaccelerationLin = (v.bottom*m*scale); + const PxReal scale = 1.f - (PxSqrt(maxLin) / PxSqrt(linMag))*0.8f; + const PxVec3 tmpaccelerationLin = (v.bottom*m*scale); PX_UNUSED(tmpaccelerationLin); d.top = tmpaccelerationLin; bAppliedImpusle = true; - } } } if (bAppliedImpusle) { - applyImpulses(dragForces, DeltaV); + applyImpulses(dragImpulse, DeltaV); PxReal* deltaV = data.getJointDeltaVelocities(); PxReal* jointV = data.getJointVelocities(); @@ -3551,7 +3482,6 @@ namespace Dy relVel += jointDatum.motionMatrix[ind] * jV; } - const PxVec3 aVec = relVel.top; const PxVec3 force = pAngular.cross(aVec); @@ -3562,7 +3492,6 @@ namespace Dy const PxVec3 temp2 = aVec.cross(lVel); const PxVec3 torque = temp0 + temp1 + temp2; coriolis = Cm::SpatialVectorF(force, torque); - #else coriolis = Cm::SpatialVectorF(PxVec3(0.f), temp0); @@ -3600,16 +3529,13 @@ namespace Dy { PxMemZero(coriolisVectors, sizeof(Cm::SpatialVectorF)*linkCount); } - } static PxMat33 constructSkewSymmetricMatrix(const PxVec3 r) { - PxMat33 temp; - temp.column0 = PxVec3(0.f, r.z, -r.y); - temp.column1 = PxVec3(-r.z, 0.f, r.x); - temp.column2 = PxVec3(r.y, -r.x, 0.f); - return temp; + return PxMat33( PxVec3(0.0f, r.z, -r.y), + PxVec3(-r.z, 0.0f, r.x), + PxVec3(r.y, -r.x, 0.0f)); } void FeatherstoneArticulation::computeRelativeTransformC2P(ArticulationData& data) @@ -3630,7 +3556,7 @@ namespace Dy PxsBodyCore& pBodyCore = *pLink.bodyCore; const PxTransform& pBody2World = pBodyCore.body2World; - PxTransform tC2P = pBody2World.transformInv(body2World).getNormalized(); + const PxTransform tC2P = pBody2World.transformInv(body2World).getNormalized(); linkDatum.childToParent.R = PxMat33(tC2P.q); linkDatum.childToParent.q = tC2P.q; @@ -3640,7 +3566,7 @@ namespace Dy //child to parent rotation matrix const PxMat33& c2p = linkDatum.childToParent.R; //r is in link body space - PxMat33 skewMatrixPR = -constructSkewSymmetricMatrix(linkDatum.r); + const PxMat33 skewMatrixPR = -constructSkewSymmetricMatrix(linkDatum.r); //rotation matrix cToP's inverse is rotation matrix pToC linkDatum.childToParent.T = c2p * (-skewMatrixPR); @@ -3674,7 +3600,7 @@ namespace Dy const PxTransform& body2World = bodyCore.body2World; - PxTransform tC2B = bBody2World.transformInv(body2World).getNormalized(); + const PxTransform tC2B = bBody2World.transformInv(body2World).getNormalized(); linkDatum.childToBase.R= PxMat33(tC2B.q); linkDatum.childToBase.q = tC2B.q; const PxVec3 r = body2World.rotateInv(body2World.p - bBody2World.p);//body space of link i @@ -3682,18 +3608,16 @@ namespace Dy //child to parent rotation matrix const PxMat33& c2b = linkDatum.childToParent.R; //r is in link body space - PxMat33 skewMatrixPR = -constructSkewSymmetricMatrix(r); + const PxMat33 skewMatrixPR = -constructSkewSymmetricMatrix(r); //rotation matrix cToP's inverse is rotation matrix pToC linkDatum.childToBase.T = c2b * (-skewMatrixPR); } } - //compute all links velocities void FeatherstoneArticulation::computeLinkVelocities(ArticulationData& data, ScratchData& scratchData) { - ArticulationLink* links = data.getLinks(); ArticulationLinkData* linkData = data.getLinkData(); const PxU32 linkCount = data.getLinkCount(); @@ -3774,7 +3698,6 @@ namespace Dy if (count <= 1) return; - const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; ////(1) Flush velocities first...TODO - incorporate into code below properly...done! @@ -3791,19 +3714,15 @@ namespace Dy PX_UNUSED(baseVelocities); PX_UNUSED(transforms); - //PxMemZero(DeltaV, sizeof(Cm::SpatialVector)*count); Cm::SpatialVectorF* deferredZ = mArticulationData.getSpatialZAVectors(); bool impulse = false; { - impulses[0].top = PxVec3(0.f); impulses[0].bottom = PxVec3(0.f); - - if (!fixBase) { //ArticulationLink& link = links[0]; @@ -3819,8 +3738,6 @@ namespace Dy DeltaV[0].bottom = PxVec3(0.f); } - - PxU32 dofId = 0; PxU32 lockId = 0; for (PxU32 linkID = 1; linkID < count; ++linkID) @@ -3839,13 +3756,11 @@ namespace Dy Cm::UnAlignedSpatialVector i1(PxVec3(0.f), PxVec3(0.f)); - Cm::SpatialVectorF parentV = DeltaV[link.parent] + baseVelocities[link.parent]; + const Cm::SpatialVectorF localParentAccel = DeltaV[link.parent].rotateInv(transforms[link.parent]); - Cm::SpatialVectorF localParentAccel = DeltaV[link.parent].rotateInv(transforms[link.parent]); - - Cm::SpatialVectorF parentVelContrib = propagateVelocityTestImpulse(linkDatum, jointDatum, deferredZ[linkID], localParentAccel).rotate(transforms[linkID]); + const Cm::SpatialVectorF parentVelContrib = propagateVelocityTestImpulse(linkDatum, jointDatum, deferredZ[linkID], localParentAccel).rotate(transforms[linkID]); //Cm::SpatialVectorF parentVelContrib = ComputeDeltaVelocity(linkDatum, jointDatum, localParentAccel).rotate(transforms[linkID]); //DeltaV[linkID] will always be zero so doesn't need to be used in here... @@ -3855,7 +3770,6 @@ namespace Dy //constraint could have done, and this could lead to something not being accumulated. Cm::UnAlignedSpatialVector dv1(parentVelContrib.top, parentVelContrib.bottom); - if (jointDatum.dofInternalConstraintMask || jointDatum.lockedAxes) { Cm::UnAlignedSpatialVector i0(PxVec3(0.f), PxVec3(0.f)); @@ -3904,21 +3818,19 @@ namespace Dy const PxReal appliedFriction = constraint.frictionForce*constraint.frictionForceCoefficient; - PxReal frictionForce = PxClamp(-jointV *constraint.recipResponse + appliedFriction, + const PxReal frictionForce = PxClamp(-jointV *constraint.recipResponse + appliedFriction, -constraint.maxFrictionForce*dt, constraint.maxFrictionForce*dt); - PxReal frictionDeltaF = frictionForce - appliedFriction; + const PxReal frictionDeltaF = frictionForce - appliedFriction; constraint.frictionForce += frictionDeltaF; jointV += frictionDeltaF * constraint.response; - - - PxReal unclampedForce = constraint.driveImpulseMultiplier * constraint.driveForce + + const PxReal unclampedForce = constraint.driveImpulseMultiplier * constraint.driveForce + jointV * constraint.driveVelMultiplier + constraint.driveTargetVel + error * constraint.driveBiasCoefficient; - PxReal clampedForce = PxClamp(unclampedForce, -constraint.maxDriveForce, constraint.maxDriveForce); + const PxReal clampedForce = PxClamp(unclampedForce, -constraint.maxDriveForce, constraint.maxDriveForce); PxReal driveDeltaF = (clampedForce - constraint.driveForce); //Where we will be next frame - we use this to compute error bias terms to correct limits and drives... @@ -3927,7 +3839,6 @@ namespace Dy driveDeltaF += frictionDeltaF; - PxReal deltaF = 0.f; if (velocityIteration) @@ -3936,22 +3847,21 @@ namespace Dy } else { - PxReal futureP = jointP + jointV * dt; + const PxReal futureP = jointP + jointV * dt; if (futureP > constraint.highLimit) { - PxReal erp = jointP > constraint.highLimit ? constraint.erp : 1.f; + const PxReal erp = jointP > constraint.highLimit ? constraint.erp : 1.f; //PxReal deltaV = (constraint.highLimit - jointP)*invDt*erp - jointV; - PxReal deltaV = (constraint.highLimit - futureP)*invDt*erp; + const PxReal deltaV = (constraint.highLimit - futureP)*invDt*erp; deltaF = PxMin(constraint.highImpulse + deltaV * constraint.recipResponse, 0.f) - constraint.highImpulse; constraint.highImpulse += deltaF; - } else if (futureP < constraint.lowLimit) { - PxReal erp = jointP < constraint.lowLimit ? constraint.erp : 1.f; + const PxReal erp = jointP < constraint.lowLimit ? constraint.erp : 1.f; //PxReal deltaV = (constraint.lowLimit - jointP)*invDt*erp - jointV; - PxReal deltaV = (constraint.lowLimit - futureP)*invDt*erp; + const PxReal deltaV = (constraint.lowLimit - futureP)*invDt*erp; deltaF = PxMax(constraint.lowImpulse + deltaV * constraint.recipResponse, 0.f) - constraint.lowImpulse; constraint.lowImpulse += deltaF; } @@ -3975,8 +3885,7 @@ namespace Dy parentV += Cm::SpatialVectorF(deltaVP.top, deltaVP.bottom); childV += Cm::SpatialVectorF(deltaVC.top, deltaVC.bottom); - } - + } } } @@ -3986,9 +3895,9 @@ namespace Dy { ArticulationInternalLockedAxis& lockAxis = mArticulationData.mInternalLockedAxes[lockId++]; - PxReal jointV = lockAxis.axis.dot(childV.top) - lockAxis.axis.dot(parentV.top); + const PxReal jointV = lockAxis.axis.dot(childV.top) - lockAxis.axis.dot(parentV.top); - PxReal deltaJointP = lockAxis.axis.dot(deltaP[linkID].top) - lockAxis.axis.dot(deltaP[link.parent].top); + const PxReal deltaJointP = lockAxis.axis.dot(deltaP[linkID].top) - lockAxis.axis.dot(deltaP[link.parent].top); PX_UNUSED(deltaJointP); PxReal deltaV = -jointV; @@ -3999,7 +3908,7 @@ namespace Dy //deltaV = PxClamp(deltaV, -5.f, 5.f); - PxReal deltaF = deltaV * lockAxis.recipResponse; + const PxReal deltaF = deltaV * lockAxis.recipResponse; if (deltaF != 0.f) { @@ -4020,7 +3929,6 @@ namespace Dy parentV += deltaVP; childV += deltaVC; } - } DeltaV[link.parent] += Cm::SpatialVectorF(dv0.top, dv0.bottom); @@ -4172,7 +4080,6 @@ namespace Dy PxU8* FeatherstoneArticulation::allocateScratchSpatialData(PxcScratchAllocator* allocator, const PxU32 linkCount, ScratchData& scratchData, bool fallBackToHeap) { - const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount; const PxU32 totalSize = size * 4 + sizeof(Dy::SpatialMatrix) * linkCount; @@ -4191,7 +4098,7 @@ namespace Dy return tempMemory; } - void FeatherstoneArticulation::allocateScratchSpatialData(DyScratchAllocator& allocator, +/* void FeatherstoneArticulation::allocateScratchSpatialData(DyScratchAllocator& allocator, const PxU32 linkCount, ScratchData& scratchData) { const PxU32 size = sizeof(Cm::SpatialVectorF) * linkCount; @@ -4211,7 +4118,7 @@ namespace Dy offset += size; scratchData.compositeSpatialInertias = reinterpret_cast(tempMemory + offset); - } + }*/ }//namespace Dy diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h index 7eecc4d42..ef0b01d24 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneArticulationLink.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp index b6d38acd8..6237e3129 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -86,7 +86,6 @@ namespace Dy computeArticulatedSpatialZ(mArticulationData, scratchData); } - #if (FEATHERSTONE_DEBUG && (PX_DEBUG || PX_CHECKED)) static bool isSpatialVectorEqual(Cm::SpatialVectorF& t0, Cm::SpatialVectorF& t1) { @@ -672,15 +671,15 @@ namespace Dy { PX_UNUSED(contextID); PX_UNUSED(desc); + PX_UNUSED(allocator); + PX_UNUSED(constraintDesc); FeatherstoneArticulation* articulation = static_cast(desc.articulation); ArticulationData& data = articulation->mArticulationData; data.setDt(dt); - return articulation->computeUnconstrainedVelocitiesInternal(desc, allocator, constraintDesc, - acCount, gravity, Z, deltaV); + return articulation->computeUnconstrainedVelocitiesInternal(acCount, gravity, Z, deltaV); } - void FeatherstoneArticulation::computeUnconstrainedVelocitiesTGS( const ArticulationSolverDesc& desc, PxReal dt, const PxVec3& gravity, @@ -870,18 +869,13 @@ namespace Dy //} } - PxU32 FeatherstoneArticulation::computeUnconstrainedVelocitiesInternal( - const ArticulationSolverDesc& desc, - PxConstraintAllocator& allocator, - PxSolverConstraintDesc* constraintDesc, PxU32& acCount, const PxVec3& gravity, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) { PX_PROFILE_ZONE("Articulations:computeUnconstrainedVelocities", 0); - PX_UNUSED(desc); ArticulationLink* links = mArticulationData.getLinks(); const PxU32 linkCount = mArticulationData.getLinkCount(); const bool fixBase = mArticulationData.getCore()->flags & PxArticulationFlag::eFIX_BASE; @@ -928,11 +922,9 @@ namespace Dy mArticulationData.mDeltaQ[a] = PxQuat(PxIdentity); } - return setupSolverConstraints(allocator, constraintDesc, links, linkCount, - fixBase, mArticulationData, Z, acCount); + return setupSolverConstraints(links, linkCount, fixBase, mArticulationData, Z, acCount); } - void FeatherstoneArticulation::computeUnconstrainedVelocitiesTGSInternal(const PxVec3& gravity, Cm::SpatialVectorF* Z, Cm::SpatialVectorF* DeltaV) { @@ -1329,7 +1321,7 @@ namespace Dy const PxTransform& preTrans = link.bodyCore->body2World; - Cm::SpatialVectorF& posVel = data.getPosIterMotionVelocity(0); + const Cm::SpatialVectorF& posVel = data.getPosIterMotionVelocity(0); updateRootBody(posVel, preTrans, data, dt); } diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp index 5d45c0b82..24eb0c083 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFeatherstoneInverseDynamic.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -598,6 +598,8 @@ namespace Dy static void computeJacobian(PxKinematicJacobian& jacobian, ArticulationJointCoreData& jointDatum, const PxTransform& body2World) { + jacobian.nbColumns = jointDatum.motionMatrix.getNumColumns(); + for (PxU32 ind = 0; ind < jointDatum.motionMatrix.getNumColumns(); ++ind) { PxReal* jacoColumn = jacobian.j[ind]; diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp index 35bbfeac5..d3bc8cb81 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionCorrelation.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h index 5bf28a293..5745c4495 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatch.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h index 461e9b9bb..ca039dd49 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyFrictionPatchStreamPair.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp index 8efc7e170..7fa94e78f 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyRigidBodyToSolverBody.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -34,7 +34,7 @@ using namespace physx; -// PT: TODO: SIMDify all this... So far I only took care of the quat-to-matrix transform +// PT: TODO: SIMDify all this... void Dy::copyToSolverBodyData(const PxVec3& linearVelocity, const PxVec3& angularVelocity, const PxReal invMass, const PxVec3& invInertia, const PxTransform& globalPose, const PxReal maxDepenetrationVelocity, const PxReal maxContactImpulse, const PxU32 nodeIndex, const PxReal reportThreshold, PxSolverBodyData& data, PxU32 lockFlags) { @@ -42,6 +42,7 @@ void Dy::copyToSolverBodyData(const PxVec3& linearVelocity, const PxVec3& angula const PxVec3 safeSqrtInvInertia = computeSafeSqrtInertia(invInertia); + // PT: TODO: re-SIMDify this one const PxMat33 rotation(globalPose.q); Cm::transformInertiaTensor(safeSqrtInvInertia, rotation, data.sqrtInvInertia); diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h index 148f23fc5..f80cb2f56 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverBody.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h index 303b4d0d0..ff5b74216 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h index 0da65a22a..d24742e34 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1D4.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h index b2f7e25e4..fdd478d54 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraint1DStep.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h index 6f6408260..f14c388c9 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintDesc.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h index 007bd5972..d2206256d 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintExtShared.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h index 6b857c781..a1c8c3cdb 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintTypes.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp index ca0027eba..f8e61b106 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraints.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp index fb196cb62..801f409e5 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsBlock.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h index ed3d9e838..a6300d23a 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverConstraintsShared.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverContact.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverContact.h index d70360a99..6950d99e5 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverContact.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverContact.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverContact4.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverContact4.h index 354ebb57e..b870d2a5d 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverContact4.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverContact4.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF.h index ebf9f6427..c0f165ad7 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF4.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF4.h index 97620dc27..7f9d09429 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF4.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverContactPF4.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverContext.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverContext.h index 736810e35..00f161663 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverContext.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverContext.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.cpp index 683163704..4d9e63f4c 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h index f9315a33c..a47f69ca8 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp index 853ce9e15..a4b924c7c 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h index 7ab760bef..3ac4311cc 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverControlPF.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h index dbe1da918..6a07cca69 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h b/src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h index ae5703433..3733247bb 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverExt.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp index a7e6c018d..91100b509 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraints.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp index 65b28464e..3e61e7ba7 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySolverPFConstraintsBlock.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h b/src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h index 0196bdb97..040c763ff 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DySpatial.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp index ee854ae3b..084f42b31 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrep.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -627,7 +627,7 @@ namespace Dy //const Vec3V normal = Vec3V_From_PxVec3_Aligned(buffer.contacts[c.contactPatches[c.correlationListHeads[i]].start].normal); const FloatV orthoThreshold = FLoad(0.70710678f); - const FloatV p1 = FLoad(0.1f); + const FloatV p1 = FLoad(0.0001f); // fallback: normal.cross((1,0,0)) or normal.cross((0,0,1)) const FloatV normalX = V3GetX(normal); const FloatV normalY = V3GetY(normal); @@ -643,7 +643,7 @@ namespace Dy const VecCrossV t0Cross = V3PrepareCross(t0); - const Vec3V t1 = V3Cross(norCross, t0Cross); + const Vec3V t1 = V3Normalize(V3Cross(norCross, t0Cross)); //const VecCrossV t1Cross = V3PrepareCross(t1); @@ -672,6 +672,8 @@ namespace Dy header->frictionBrokenWritebackByte = writeback; + PxReal frictionScale = (contactBase0->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && frictionPatch.anchorCount == 2) ? 0.5f : 1.f; + for (PxU32 j = 0; j < frictionPatch.anchorCount; j++) { Ps::prefetchLine(ptr, 256); @@ -725,7 +727,7 @@ namespace Dy f0->raXnI_targetVelW = V4SetW(raXnInertia, targetVel); f0->rbXnI_velMultiplierW = V4SetW(rbXnInertia, velMultiplier); f0->appliedForce = 0.f; - f0->frictionScale = 1.f; + f0->frictionScale = frictionScale; f0->biasScale = frictionBiasScale; } @@ -759,7 +761,7 @@ namespace Dy f1->raXnI_targetVelW = V4SetW(raXnInertia, targetVel); f1->rbXnI_velMultiplierW = V4SetW(rbXnInertia, velMultiplier); f1->appliedForce = 0.f; - f1->frictionScale = 1.f; + f1->frictionScale = frictionScale; f1->biasScale = frictionBiasScale; } } @@ -1035,8 +1037,11 @@ namespace Dy const Gu::ContactPoint* contactBase0 = buffer + c.contactPatches[c.correlationListHeads[i]].start; const PxReal combinedRestitution = contactBase0->restitution; - const PxReal staticFriction = contactBase0->staticFriction; - const PxReal dynamicFriction = contactBase0->dynamicFriction; + const bool useImprovedFrictionPatch = !!(contactBase0->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION); + PxReal coefficient = useImprovedFrictionPatch ? 1.f/frictionPatch.anchorCount : 1.f; + + const PxReal staticFriction = contactBase0->staticFriction*coefficient; + const PxReal dynamicFriction = contactBase0->dynamicFriction*coefficient; const bool disableStrongFriction = !!(contactBase0->materialFlags & PxMaterialFlag::eDISABLE_FRICTION); staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetX(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(staticFriction)); staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W = V4SetY(staticFrictionX_dynamicFrictionY_dominance0Z_dominance1W, FLoad(dynamicFriction)); @@ -1253,8 +1258,6 @@ namespace Dy if (hasTorsionalFriction && frictionPatch.anchorCount == 1) { - header->numFrictionConstr++; - const FloatV torsionalPatchRadius = FLoad(torsionalPatchRadiusF32); const FloatV minTorsionalPatchRadius = FLoad(minTorsionalPatchRadiusF32); const FloatV torsionalFriction = FMax(minTorsionalPatchRadius, FSqrt(FMul(FMax(zero, FNeg(maxPenetration)), torsionalPatchRadius))); @@ -1309,7 +1312,7 @@ namespace Dy f->normalXYZ_ErrorW = V4SetW(V3Zero(), FLoad(-angle)); f->raXnI_targetVelW = V4SetW(V3LoadA(resp0.angular), zero); - f->rbXnI_velMultiplierW = V4SetW(V3LoadA(resp1.angular), velMultiplier); + f->rbXnI_velMultiplierW = V4SetW(V4Neg(Vec4V_From_Vec3V(V3LoadA(resp1.angular))), velMultiplier); f->biasScale = frictionBiasScale; f->appliedForce = 0.f; FStore(torsionalFriction, &f->frictionScale); diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp index 9263d1244..e8b8a7399 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSContactPrepBlock.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -451,7 +451,7 @@ static void setupFinalizeSolverConstraints4Step(PxTGSSolverContactDesc* PX_RESTR PxU32 maxPatches = PxMax(descs[0].numFrictionPatches, PxMax(descs[1].numFrictionPatches, PxMax(descs[2].numFrictionPatches, descs[3].numFrictionPatches))); - const Vec4V p1 = V4Splat(FLoad(0.1f)); + const Vec4V p1 = V4Splat(FLoad(0.0001f)); const Vec4V orthoThreshold = V4Splat(FLoad(0.70710678f)); @@ -525,6 +525,8 @@ static void setupFinalizeSolverConstraints4Step(PxTGSSolverContactDesc* PX_RESTR header->shapeInteraction[0] = descs[0].shapeInteraction; header->shapeInteraction[1] = descs[1].shapeInteraction; header->shapeInteraction[2] = descs[2].shapeInteraction; header->shapeInteraction[3] = descs[3].shapeInteraction; + + Vec4V* maxImpulse = reinterpret_cast(ptr + constraintSize * totalContacts); header->restitution = restitution; @@ -816,15 +818,6 @@ static void setupFinalizeSolverConstraints4Step(PxTGSSolverContactDesc* PX_RESTR Vec4V maxImpulseScale = V4One(); { - const Vec4V staticFriction = V4LoadXYZW(contactBase0->staticFriction, contactBase1->staticFriction, - contactBase2->staticFriction, contactBase3->staticFriction); - - const Vec4V dynamicFriction = V4LoadXYZW(contactBase0->dynamicFriction, contactBase1->dynamicFriction, - contactBase2->dynamicFriction, contactBase3->dynamicFriction); - - PX_ASSERT(totalContacts == contactCount); - header->dynamicFriction = dynamicFriction; - header->staticFriction = staticFriction; const FrictionPatch& frictionPatch0 = c.frictionPatches[frictionIndex0]; const FrictionPatch& frictionPatch1 = c.frictionPatches[frictionIndex1]; @@ -843,11 +836,28 @@ static void setupFinalizeSolverConstraints4Step(PxTGSSolverContactDesc* PX_RESTR const PxU32 maxAnchorCount = PxMax(clampedAnchorCount0, PxMax(clampedAnchorCount1, PxMax(clampedAnchorCount2, clampedAnchorCount3))); - //if(clampedAnchorCount0 != clampedAnchorCount1 || clampedAnchorCount0 != clampedAnchorCount2 || clampedAnchorCount0 != clampedAnchorCount3) - // Ps::debugBreak(); + PX_ALIGN(16, PxReal staticFriction[4]); + PX_ALIGN(16, PxReal dynamicFriction[4]); + //for (PxU32 f = 0; f < 4; ++f) + { + PxReal coeff0 = (contactBase0->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && clampedAnchorCount0 == 2) ? 0.5f : 1.f; + PxReal coeff1 = (contactBase1->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && clampedAnchorCount1 == 2) ? 0.5f : 1.f; + PxReal coeff2 = (contactBase2->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && clampedAnchorCount2 == 2) ? 0.5f : 1.f; + PxReal coeff3 = (contactBase3->materialFlags & PxMaterialFlag::eIMPROVED_PATCH_FRICTION && clampedAnchorCount3 == 2) ? 0.5f : 1.f; - //const bool haveFriction = maxAnchorCount != 0; + staticFriction[0] = contactBase0->staticFriction * coeff0; + dynamicFriction[0] = contactBase0->dynamicFriction * coeff0; + staticFriction[1] = contactBase1->staticFriction * coeff1; + dynamicFriction[1] = contactBase1->dynamicFriction * coeff1; + staticFriction[2] = contactBase2->staticFriction * coeff2; + dynamicFriction[2] = contactBase2->dynamicFriction * coeff2; + staticFriction[3] = contactBase3->staticFriction * coeff3; + dynamicFriction[3] = contactBase3->dynamicFriction * coeff3; + } + + PX_ASSERT(totalContacts == contactCount); + header->numFrictionConstr = Ps::to8(maxAnchorCount * 2); header->numFrictionConstrs[0] = Ps::to8(clampedAnchorCount0 * 2); header->numFrictionConstrs[1] = Ps::to8(clampedAnchorCount1 * 2); @@ -1226,6 +1236,9 @@ static void setupFinalizeSolverConstraints4Step(PxTGSSolverContactDesc* PX_RESTR } } + header->dynamicFriction = V4LoadA(dynamicFriction); + header->staticFriction = V4LoadA(staticFriction); + frictionPatchWritebackAddrIndex0++; frictionPatchWritebackAddrIndex1++; frictionPatchWritebackAddrIndex2++; diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp index 2c779c754..8d0c49407 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h index 6d2754074..443e26e7e 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSDynamics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp index 2b65cafdf..fa71bf8e0 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "DyTGSPartition.h" #include "DyArticulationUtils.h" diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h index 538309d2a..531710ec7 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyTGSPartition.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp index 8c9e172c3..2e0edcb59 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h index 7a77339fe..4b896d4d9 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyThreadContext.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp b/src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp index bb4cd3018..7ef5fa0d5 100644 --- a/src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp +++ b/src/PhysX/physx/source/lowleveldynamics/src/DyThresholdTable.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpActor.cpp b/src/PhysX/physx/source/physx/src/NpActor.cpp index 910b7958d..27f958431 100644 --- a/src/PhysX/physx/source/physx/src/NpActor.cpp +++ b/src/PhysX/physx/source/physx/src/NpActor.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpActor.h b/src/PhysX/physx/source/physx/src/NpActor.h index a4151a4c5..9b1e76efe 100644 --- a/src/PhysX/physx/source/physx/src/NpActor.h +++ b/src/PhysX/physx/source/physx/src/NpActor.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpActorTemplate.h b/src/PhysX/physx/source/physx/src/NpActorTemplate.h index 9f95ee971..649ea50c5 100644 --- a/src/PhysX/physx/source/physx/src/NpActorTemplate.h +++ b/src/PhysX/physx/source/physx/src/NpActorTemplate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -65,9 +65,11 @@ class NpActorTemplate : public APIClass, public NpActor, public Ps::UserAllocate public: // PX_SERIALIZATION NpActorTemplate(PxBaseFlags baseFlags) : APIClass(baseFlags), NpActor(PxEmpty) {} - virtual void exportExtraData(PxSerializationContext& stream) { NpActor::exportExtraData(stream); } - void importExtraData(PxDeserializationContext& context) { NpActor::importExtraData(context); } - virtual void resolveReferences(PxDeserializationContext& context) { NpActor::resolveReferences(context); } + + virtual void exportData(PxSerializationContext& context) const = 0; + virtual void exportExtraData(PxSerializationContext& context) { NpActor::exportExtraData(context); } + virtual void importExtraData(PxDeserializationContext& context) { NpActor::importExtraData(context); } + virtual void resolveReferences(PxDeserializationContext& context) { NpActor::resolveReferences(context); } //~PX_SERIALIZATION NpActorTemplate(PxType concreteType, PxBaseFlags baseFlags, const char* name, void* userData); diff --git a/src/PhysX/physx/source/physx/src/NpAggregate.cpp b/src/PhysX/physx/source/physx/src/NpAggregate.cpp index 2154fadd4..ee776beee 100644 --- a/src/PhysX/physx/source/physx/src/NpAggregate.cpp +++ b/src/PhysX/physx/source/physx/src/NpAggregate.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpAggregate.h b/src/PhysX/physx/source/physx/src/NpAggregate.h index 11200dc35..0054916d9 100644 --- a/src/PhysX/physx/source/physx/src/NpAggregate.h +++ b/src/PhysX/physx/source/physx/src/NpAggregate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpArticulation.cpp b/src/PhysX/physx/source/physx/src/NpArticulation.cpp index 4ce735c67..044b9d0c0 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulation.cpp +++ b/src/PhysX/physx/source/physx/src/NpArticulation.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -86,7 +86,6 @@ NpArticulation* NpArticulation::createObject(PxU8*& address, PxDeserializationCo NpArticulation::NpArticulation() : NpArticulationTemplate(PxConcreteType::eARTICULATION, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) { - PxArticulationBase::userData = NULL; mType = PxArticulationBase::eMaximumCoordinate; mImpl.mArticulation.setArticulationType(PxArticulationBase::eMaximumCoordinate); } diff --git a/src/PhysX/physx/source/physx/src/NpArticulation.h b/src/PhysX/physx/source/physx/src/NpArticulation.h index 8192e4373..9915cc20e 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulation.h +++ b/src/PhysX/physx/source/physx/src/NpArticulation.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -67,71 +67,66 @@ class NpArticulation : public NpArticulationTemplate //================================================================================================== public: - virtual ~NpArticulation(); + virtual ~NpArticulation(); // PX_SERIALIZATION - NpArticulation(PxBaseFlags baseFlags) : NpArticulationTemplate(baseFlags) - {} + NpArticulation(PxBaseFlags baseFlags) : NpArticulationTemplate(baseFlags){} - virtual void exportExtraData(PxSerializationContext& stream); - void importExtraData(PxDeserializationContext& context); - void resolveReferences(PxDeserializationContext& context); - virtual void requiresObjects(PxProcessPxBaseCallback& c); - static NpArticulation* createObject(PxU8*& address, PxDeserializationContext& context); - static void getBinaryMetaData(PxOutputStream& stream); + virtual void exportExtraData(PxSerializationContext& stream); + void importExtraData(PxDeserializationContext& context); + void resolveReferences(PxDeserializationContext& context); + virtual void requiresObjects(PxProcessPxBaseCallback& c); + static NpArticulation* createObject(PxU8*& address, PxDeserializationContext& context); + static void getBinaryMetaData(PxOutputStream& stream); //~PX_SERIALIZATION //--------------------------------------------------------------------------------- // PxArticulation implementation //--------------------------------------------------------------------------------- - virtual PxU32 getInternalDriveIterations() const; - virtual void setInternalDriveIterations(PxU32 iterations); + virtual PxU32 getInternalDriveIterations() const; + virtual void setInternalDriveIterations(PxU32 iterations); - virtual PxU32 getExternalDriveIterations() const; - virtual void setExternalDriveIterations(PxU32 iterations); + virtual PxU32 getExternalDriveIterations() const; + virtual void setExternalDriveIterations(PxU32 iterations); - virtual PxU32 getMaxProjectionIterations() const; - virtual void setMaxProjectionIterations(PxU32 iterations); + virtual PxU32 getMaxProjectionIterations() const; + virtual void setMaxProjectionIterations(PxU32 iterations); - virtual PxReal getSeparationTolerance() const; - virtual void setSeparationTolerance(PxReal tolerance); + virtual PxReal getSeparationTolerance() const; + virtual void setSeparationTolerance(PxReal tolerance); - virtual PxArticulationDriveCache* - createDriveCache(PxReal compliance, PxU32 driveIterations) const; + virtual PxArticulationDriveCache* createDriveCache(PxReal compliance, PxU32 driveIterations) const; - virtual void updateDriveCache(PxArticulationDriveCache& cache, PxReal compliance, PxU32 driveIterations) const; + virtual void updateDriveCache(PxArticulationDriveCache& cache, PxReal compliance, PxU32 driveIterations) const; - virtual void releaseDriveCache(PxArticulationDriveCache&) const; + virtual void releaseDriveCache(PxArticulationDriveCache&) const; - virtual void applyImpulse(PxArticulationLink*, - const PxArticulationDriveCache& driveCache, - const PxVec3& force, - const PxVec3& torque); + virtual void applyImpulse( PxArticulationLink*, + const PxArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque); - virtual void computeImpulseResponse(PxArticulationLink*, - PxVec3& linearResponse, - PxVec3& angularResponse, - const PxArticulationDriveCache& driveCache, - const PxVec3& force, - const PxVec3& torque) const; + virtual void computeImpulseResponse( PxArticulationLink*, + PxVec3& linearResponse, + PxVec3& angularResponse, + const PxArticulationDriveCache& driveCache, + const PxVec3& force, + const PxVec3& torque) const; - virtual const char* getConcreteTypeName() const { return "PxArticulation"; } + virtual const char* getConcreteTypeName() const { return "PxArticulation"; } //--------------------------------------------------------------------------------- // Miscellaneous //--------------------------------------------------------------------------------- - NpArticulation(); + NpArticulation(); + virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulation", name) || PxBase::isKindOf(name); } - virtual bool isKindOf(const char* name) const { return !::strcmp("PxArticulation", name) || PxBase::isKindOf(name); } - - virtual PxArticulationJointBase* createArticulationJoint(PxArticulationLink& parent, - const PxTransform& parentFrame, - PxArticulationLink& child, - const PxTransform& childFrame); - virtual void releaseArticulationJoint(PxArticulationJointBase* joint); +private: + virtual PxArticulationJointBase* createArticulationJoint(PxArticulationLink& parent, const PxTransform& parentFrame, PxArticulationLink& child, const PxTransform& childFrame); + virtual void releaseArticulationJoint(PxArticulationJointBase* joint); }; #if PX_VC diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp b/src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp index eea8fe4cb..6c72b6982 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp +++ b/src/PhysX/physx/source/physx/src/NpArticulationJoint.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJoint.h b/src/PhysX/physx/source/physx/src/NpArticulationJoint.h index 1c4189771..b252ec51f 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulationJoint.h +++ b/src/PhysX/physx/source/physx/src/NpArticulationJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp index 41345a0b3..a7c2a824f 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp +++ b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h index f2b1d19cc..2e8396a99 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h +++ b/src/PhysX/physx/source/physx/src/NpArticulationJointReducedCoordinate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpArticulationLink.cpp b/src/PhysX/physx/source/physx/src/NpArticulationLink.cpp index 3d215a7a0..90f8d7739 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulationLink.cpp +++ b/src/PhysX/physx/source/physx/src/NpArticulationLink.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -49,6 +49,20 @@ void NpArticulationLink::requiresObjects(PxProcessPxBaseCallback& c) c.process(*mInboundJoint); } +void NpArticulationLink::exportData(PxSerializationContext& context) const +{ + //Clearing the aggregate ID for serialization so we avoid having a stale + //reference after deserialization. The aggregate ID get's reset on readding to the + //scene anyway. + Sc::ActorCore& actorCore = const_cast(getScbActorFast().getActorCore()); + PxU32 backupAggregateID = actorCore.getAggregateID(); + actorCore.setAggregateID(PX_INVALID_U32); + + context.writeData(this, sizeof(NpArticulationLink)); + + actorCore.setAggregateID(backupAggregateID); +} + void NpArticulationLink::exportExtraData(PxSerializationContext& stream) { NpArticulationLinkT::exportExtraData(stream); diff --git a/src/PhysX/physx/source/physx/src/NpArticulationLink.h b/src/PhysX/physx/source/physx/src/NpArticulationLink.h index d525cb0c6..68cc5cf41 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulationLink.h +++ b/src/PhysX/physx/source/physx/src/NpArticulationLink.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -77,6 +77,7 @@ class NpArticulationLink : public NpArticulationLinkT public: // PX_SERIALIZATION NpArticulationLink(PxBaseFlags baseFlags) : NpArticulationLinkT(baseFlags), mChildLinks(PxEmpty) {} + virtual void exportData(PxSerializationContext& context) const; virtual void exportExtraData(PxSerializationContext& stream); void importExtraData(PxDeserializationContext& context); void registerReferences(PxSerializationContext& stream); diff --git a/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp index 765f89c0d..3ed04f39d 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp +++ b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -41,384 +41,369 @@ #include "PxPvdDataStream.h" #include "extensions/PxJoint.h" -namespace physx +using namespace physx; + +class LLReducedArticulationPool : public Ps::Pool > { +public: + LLReducedArticulationPool() {} +}; - class LLReducedArticulationPool : public Ps::Pool > - { - public: - LLReducedArticulationPool() {} - }; +NpArticulationReducedCoordinate::NpArticulationReducedCoordinate() + : NpArticulationTemplate(PxConcreteType::eARTICULATION, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) +{ + mType = PxArticulationBase::eReducedCoordinate; + mImpl.mArticulation.setArticulationType(PxArticulationBase::eReducedCoordinate); +} +void NpArticulationReducedCoordinate::setArticulationFlags(PxArticulationFlags flags) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.getArticulation().setArticulationFlags(flags); +} - NpArticulationReducedCoordinate::NpArticulationReducedCoordinate() - : NpArticulationTemplate(PxConcreteType::eARTICULATION, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) - { - PxArticulationBase::userData = NULL; - mType = PxArticulationBase::eReducedCoordinate; - mImpl.mArticulation.setArticulationType(PxArticulationBase::eReducedCoordinate); - } +void NpArticulationReducedCoordinate::setArticulationFlag(PxArticulationFlag::Enum flag, bool value) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + PxArticulationFlags flags = mImpl.getArticulation().getArticulationFlags(); + if(value) + flags |= flag; + else + flags &= (~flag); - void NpArticulationReducedCoordinate::setArticulationFlags(PxArticulationFlags flags) - { - NP_WRITE_CHECK(mImpl.getOwnerScene()); - mImpl.getArticulation().setArticulationFlags(flags); - } + mImpl.getArticulation().setArticulationFlags(flags); +} - void NpArticulationReducedCoordinate::setArticulationFlag(PxArticulationFlag::Enum flag, bool value) - { - NP_WRITE_CHECK(mImpl.getOwnerScene()); - PxArticulationFlags flags = mImpl.getArticulation().getArticulationFlags(); +PxArticulationFlags NpArticulationReducedCoordinate::getArticulationFlags() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.getArticulation().getArticulationFlags(); +} - if(value) - flags |= flag; - else - flags &= (~flag); +PxU32 NpArticulationReducedCoordinate::getDofs() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.mArticulation.getScArticulation().getDofs(); +} - mImpl.getArticulation().setArticulationFlags(flags); - } +PxArticulationCache* NpArticulationReducedCoordinate::createCache() const +{ + PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::createCache: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads - PxArticulationFlags NpArticulationReducedCoordinate::getArticulationFlags() const - { - NP_READ_CHECK(mImpl.getOwnerScene()); - return mImpl.getArticulation().getArticulationFlags(); - } + PxArticulationCache* cache = mImpl.mArticulation.getScArticulation().createCache(); + cache->version = mImpl.mCacheVersion; - PxU32 NpArticulationReducedCoordinate::getDofs() const - { - NP_READ_CHECK(mImpl.getOwnerScene()); - return mImpl.mArticulation.getScArticulation().getDofs(); - } + return cache; +} - PxArticulationCache* NpArticulationReducedCoordinate::createCache() const - { - PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::createCache: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads +PxU32 NpArticulationReducedCoordinate::getCacheDataSize() const +{ + PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::getCacheDataSize: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads - PxArticulationCache* cache = mImpl.mArticulation.getScArticulation().createCache(); - cache->version = mImpl.mCacheVersion; + return mImpl.mArticulation.getScArticulation().getCacheDataSize(); +} - return cache; - } +void NpArticulationReducedCoordinate::zeroCache(PxArticulationCache& cache) +{ + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + return mImpl.mArticulation.getScArticulation().zeroCache(cache); +} - PxU32 NpArticulationReducedCoordinate::getCacheDataSize() const - { - PX_CHECK_AND_RETURN_NULL(mImpl.getAPIScene(), "PxArticulation::getCacheDataSize: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads +void NpArticulationReducedCoordinate::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake) +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::applyCache: object must be in a scene"); - return mImpl.mArticulation.getScArticulation().getCacheDataSize(); - } + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::applyCache : cache is invalid, articulation configuration has changed! ") - void NpArticulationReducedCoordinate::zeroCache(PxArticulationCache& cache) - { - NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads - return mImpl.mArticulation.getScArticulation().zeroCache(cache); - } - - void NpArticulationReducedCoordinate::applyCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag, bool autowake) - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::applyCache: object must be in a scene"); - - PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::applyCache : cache is invalid, articulation configuration has changed! ") - - //if we try to do a bulk op when sim is running, return with error - if (static_cast(getScene())->getSimulationStage() != Sc::SimulationStage::eCOMPLETE) - { - Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, - "NpArticulation::applyCache() not allowed while simulation is running."); - return; - } - - mImpl.mArticulation.getScArticulation().applyCache(cache, flag); - - if (flag & PxArticulationCache::ePOSITION) + //if we try to do a bulk op when sim is running, return with error + if (static_cast(getScene())->getSimulationStage() != Sc::SimulationStage::eCOMPLETE) { - const PxU32 linkCount = mImpl.mArticulationLinks.size(); - - for (PxU32 i = 0; i < linkCount; ++i) - { - NpArticulationLink* link = mImpl.mArticulationLinks[i]; - //in the lowlevel articulation, we have already updated bodyCore's body2World - const PxTransform internalPose = link->getScbBodyFast().getScBody().getBody2World(); - link->getScbBodyFast().setBody2World(internalPose, false); - } + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, + "NpArticulation::applyCache() not allowed while simulation is running."); + return; } - mImpl.wakeUpInternal(false, autowake); + mImpl.mArticulation.getScArticulation().applyCache(cache, flag); - } - - void NpArticulationReducedCoordinate::copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const + if (flag & PxArticulationCache::ePOSITION) { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::copyInternalStateToCache: object must be in a scene"); + const PxU32 linkCount = mImpl.mArticulationLinks.size(); - PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::applyCache : cache is invalid, articulation configuration has changed! ") - - mImpl.mArticulation.getScArticulation().copyInternalStateToCache(cache, flag); - } - - void NpArticulationReducedCoordinate::releaseCache(PxArticulationCache& cache) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::releaseCache: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads - - mImpl.mArticulation.getScArticulation().releaseCache(cache); - } - - void NpArticulationReducedCoordinate::packJointData(const PxReal* maximum, PxReal* reduced) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::packJointData: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); - - mImpl.mArticulation.getScArticulation().packJointData(maximum, reduced); - } - - void NpArticulationReducedCoordinate::unpackJointData(const PxReal* reduced, PxReal* maximum) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::unpackJointData: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); - - mImpl.mArticulation.getScArticulation().unpackJointData(reduced, maximum); - } - - void NpArticulationReducedCoordinate::commonInit() const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::commonInit: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); - - mImpl.mArticulation.getScArticulation().commonInit(); - } - - void NpArticulationReducedCoordinate::computeGeneralizedGravityForce(PxArticulationCache& cache) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralisedGravityForce: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); - - PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralisedGravityForce : cache is invalid, articulation configuration has changed! "); - - - mImpl.mArticulation.getScArticulation().computeGeneralizedGravityForce(cache); - } - - void NpArticulationReducedCoordinate::computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeCoriolisAndCentrifugalForce: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); - - PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeCoriolisAndCentrifugalForce : cache is invalid, articulation configuration has changed! "); - - mImpl.mArticulation.getScArticulation().computeCoriolisAndCentrifugalForce(cache); - } - - void NpArticulationReducedCoordinate::computeGeneralizedExternalForce(PxArticulationCache& cache) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralizedExternalForce: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); - - PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralizedExternalForce : cache is invalid, articulation configuration has changed! "); - - mImpl.mArticulation.getScArticulation().computeGeneralizedExternalForce(cache); - } - - void NpArticulationReducedCoordinate::computeJointAcceleration(PxArticulationCache& cache) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeJointAcceleration: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); - - PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeJointAcceleration : cache is invalid, articulation configuration has changed! "); - - mImpl.mArticulation.getScArticulation().computeJointAcceleration(cache); - } - - void NpArticulationReducedCoordinate::computeJointForce(PxArticulationCache& cache) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeJointForce: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); - - PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeJointForce : cache is invalid, articulation configuration has changed! "); - - mImpl.mArticulation.getScArticulation().computeJointForce(cache); - } - - void NpArticulationReducedCoordinate::computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeKenematicJacobian: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); - - PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeKenematicJacobian : cache is invalid, articulation configuration has changed! "); - - mImpl.mArticulation.getScArticulation().computeKinematicJacobian(linkID, cache); - } - - void NpArticulationReducedCoordinate::computeCoefficentMatrix(PxArticulationCache& cache) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeCoefficentMatrix: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); - - PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeCoefficentMatrix : cache is invalid, articulation configuration has changed! "); - - for (PxU32 i = 0; i < mLoopJoints.size(); ++i) + for (PxU32 i = 0; i < linkCount; ++i) { - static_cast(mLoopJoints[i]->getConstraint())->updateConstants(); + NpArticulationLink* link = mImpl.mArticulationLinks[i]; + //in the lowlevel articulation, we have already updated bodyCore's body2World + const PxTransform internalPose = link->getScbBodyFast().getScBody().getBody2World(); + link->getScbBodyFast().setBody2World(internalPose, false); } - - mImpl.mArticulation.getScArticulation().computeCoefficentMatrix(cache); } - bool NpArticulationReducedCoordinate::computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxU32 maxIter) const - { + mImpl.wakeUpInternal(false, autowake); +} - if (!mImpl.getAPIScene()) - { - physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxArticulation::computeLambda : object must be in a scened!"); - return false; - } +void NpArticulationReducedCoordinate::copyInternalStateToCache(PxArticulationCache& cache, const PxArticulationCacheFlags flag) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::copyInternalStateToCache: object must be in a scene"); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::applyCache : cache is invalid, articulation configuration has changed! ") + + mImpl.mArticulation.getScArticulation().copyInternalStateToCache(cache, flag); +} + +void NpArticulationReducedCoordinate::releaseCache(PxArticulationCache& cache) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::releaseCache: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); // doesn't modify the scene, only reads + + mImpl.mArticulation.getScArticulation().releaseCache(cache); +} + +void NpArticulationReducedCoordinate::packJointData(const PxReal* maximum, PxReal* reduced) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::packJointData: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + mImpl.mArticulation.getScArticulation().packJointData(maximum, reduced); +} + +void NpArticulationReducedCoordinate::unpackJointData(const PxReal* reduced, PxReal* maximum) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::unpackJointData: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + mImpl.mArticulation.getScArticulation().unpackJointData(reduced, maximum); +} + +void NpArticulationReducedCoordinate::commonInit() const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::commonInit: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + mImpl.mArticulation.getScArticulation().commonInit(); +} + +void NpArticulationReducedCoordinate::computeGeneralizedGravityForce(PxArticulationCache& cache) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralisedGravityForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralisedGravityForce : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeGeneralizedGravityForce(cache); +} + +void NpArticulationReducedCoordinate::computeCoriolisAndCentrifugalForce(PxArticulationCache& cache) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeCoriolisAndCentrifugalForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeCoriolisAndCentrifugalForce : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeCoriolisAndCentrifugalForce(cache); +} + +void NpArticulationReducedCoordinate::computeGeneralizedExternalForce(PxArticulationCache& cache) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralizedExternalForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralizedExternalForce : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeGeneralizedExternalForce(cache); +} + +void NpArticulationReducedCoordinate::computeJointAcceleration(PxArticulationCache& cache) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeJointAcceleration: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeJointAcceleration : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeJointAcceleration(cache); +} + +void NpArticulationReducedCoordinate::computeJointForce(PxArticulationCache& cache) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeJointForce: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeJointForce : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeJointForce(cache); +} + +void NpArticulationReducedCoordinate::computeKinematicJacobian(const PxU32 linkID, PxArticulationCache& cache) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeKenematicJacobian: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeKenematicJacobian : cache is invalid, articulation configuration has changed! "); + + mImpl.mArticulation.getScArticulation().computeKinematicJacobian(linkID, cache); +} + +void NpArticulationReducedCoordinate::computeCoefficentMatrix(PxArticulationCache& cache) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeCoefficentMatrix: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); + + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeCoefficentMatrix : cache is invalid, articulation configuration has changed! "); + + for (PxU32 i = 0; i < mLoopJoints.size(); ++i) + { + static_cast(mLoopJoints[i]->getConstraint())->updateConstants(); + } + + mImpl.mArticulation.getScArticulation().computeCoefficentMatrix(cache); +} + +bool NpArticulationReducedCoordinate::computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxU32 maxIter) const +{ + if (!mImpl.getAPIScene()) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxArticulation::computeLambda : object must be in a scened!"); + return false; + } - NP_READ_CHECK(mImpl.getOwnerScene()); + NP_READ_CHECK(mImpl.getOwnerScene()); - if (cache.version != mImpl.mCacheVersion) - { - physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxArticulation::computeLambda : cache is invalid, articulation configuration has changed!"); - return false; - } - - return mImpl.mArticulation.getScArticulation().computeLambda(cache, initialState, jointTorque, getScene()->getGravity(), maxIter); + if (cache.version != mImpl.mCacheVersion) + { + physx::shdfnd::getFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxArticulation::computeLambda : cache is invalid, articulation configuration has changed!"); + return false; } - void NpArticulationReducedCoordinate::computeGeneralizedMassMatrix(PxArticulationCache& cache) const - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralizedMassMatrix: object must be in a scene"); - NP_READ_CHECK(mImpl.getOwnerScene()); + return mImpl.mArticulation.getScArticulation().computeLambda(cache, initialState, jointTorque, getScene()->getGravity(), maxIter); +} - PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralizedMassMatrix : cache is invalid, articulation configuration has changed! "); +void NpArticulationReducedCoordinate::computeGeneralizedMassMatrix(PxArticulationCache& cache) const +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulation::computeGeneralizedMassMatrix: object must be in a scene"); + NP_READ_CHECK(mImpl.getOwnerScene()); - mImpl.mArticulation.getScArticulation().computeGeneralizedMassMatrix(cache); - } + PX_CHECK_AND_RETURN(cache.version == mImpl.mCacheVersion, "PxArticulation::computeGeneralizedMassMatrix : cache is invalid, articulation configuration has changed! "); - void NpArticulationReducedCoordinate::addLoopJoint(PxJoint* joint) - { - - NP_WRITE_CHECK(mImpl.getOwnerScene()); + mImpl.mArticulation.getScArticulation().computeGeneralizedMassMatrix(cache); +} + +void NpArticulationReducedCoordinate::addLoopJoint(PxJoint* joint) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); #if PX_CHECKED - PxRigidActor* actor0; - PxRigidActor* actor1; + PxRigidActor* actor0; + PxRigidActor* actor1; - joint->getActors(actor0, actor1); + joint->getActors(actor0, actor1); - PxArticulationLink* link0 = NULL; - PxArticulationLink* link1 = NULL; + PxArticulationLink* link0 = NULL; + PxArticulationLink* link1 = NULL; - if(actor0) - link0 = actor0->is(); + if(actor0) + link0 = actor0->is(); - if(actor1) - link1 = actor1->is(); + if(actor1) + link1 = actor1->is(); - PX_CHECK_AND_RETURN((link0 || link1), "PxArticulation::addLoopJoint : at least one of the PxRigidActors need to be PxArticulationLink! "); + PX_CHECK_AND_RETURN((link0 || link1), "PxArticulation::addLoopJoint : at least one of the PxRigidActors need to be PxArticulationLink! "); - PxArticulationBase* base0 = NULL; - PxArticulationBase* base1 = NULL; - if (link0) - base0 = &link0->getArticulation(); + PxArticulationBase* base0 = NULL; + PxArticulationBase* base1 = NULL; + if (link0) + base0 = &link0->getArticulation(); - if (link1) - base1 = &link1->getArticulation(); - - PX_CHECK_AND_RETURN((base0 == this || base1 == this), "PxArticulation::addLoopJoint : at least one of the PxArticulationLink belongs to this articulation! "); - + if (link1) + base1 = &link1->getArticulation(); + PX_CHECK_AND_RETURN((base0 == this || base1 == this), "PxArticulation::addLoopJoint : at least one of the PxArticulationLink belongs to this articulation! "); #endif - - const PxU32 size = mLoopJoints.size(); - if (size >= mLoopJoints.capacity()) - { - mLoopJoints.reserve(size * 2 + 1); - } - - mLoopJoints.pushBack(joint); - - Scb::Articulation& scbArt = mImpl.getArticulation(); - Sc::ArticulationSim* scArtSim = scbArt.getScArticulation().getSim(); - - NpConstraint* constraint = static_cast(joint->getConstraint()); - Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); - if(scArtSim) - scArtSim->addLoopConstraint(cSim); - } - - void NpArticulationReducedCoordinate::removeLoopJoint(PxJoint* joint) + const PxU32 size = mLoopJoints.size(); + if (size >= mLoopJoints.capacity()) { - NP_WRITE_CHECK(mImpl.getOwnerScene()); - - mLoopJoints.findAndReplaceWithLast(joint); - - Scb::Articulation& scbArt = mImpl.getArticulation(); - Sc::ArticulationSim* scArtSim = scbArt.getScArticulation().getSim(); - - NpConstraint* constraint = static_cast(joint->getConstraint()); - Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); - scArtSim->removeLoopConstraint(cSim); + mLoopJoints.reserve(size * 2 + 1); } + mLoopJoints.pushBack(joint); - PxU32 NpArticulationReducedCoordinate::getNbLoopJoints() const - { - NP_READ_CHECK(mImpl.getOwnerScene()); - - return mLoopJoints.size(); - } - - PxU32 NpArticulationReducedCoordinate::getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex) const - { - NP_READ_CHECK(mImpl.getOwnerScene()); - - return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mLoopJoints.begin(), mLoopJoints.size()); - } - - PxU32 NpArticulationReducedCoordinate::getCoefficentMatrixSize() const - { - NP_READ_CHECK(mImpl.getOwnerScene()); - - return mImpl.mArticulation.getScArticulation().getCoefficentMatrixSize(); - } - - void NpArticulationReducedCoordinate::teleportRootLink(const PxTransform& pose, bool autowake) - { - PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulationReducedCoordinate::teleportRootLink: object must be in a scene"); - - PX_CHECK_AND_RETURN(pose.isValid(), "PxArticulationReducedCoordinate::teleportRootLink pose is not valid."); - - NP_WRITE_CHECK(mImpl.getOwnerScene()); - - NpArticulationLink* root = mImpl.mArticulationLinks[0]; - - root->setGlobalPoseInternal(pose, autowake); - } - - - NpArticulationReducedCoordinate::~NpArticulationReducedCoordinate() - { - NpFactory::getInstance().onArticulationRelease(this); - } - - PxArticulationJointBase* NpArticulationReducedCoordinate::createArticulationJoint(PxArticulationLink& parent, - const PxTransform& parentFrame, - PxArticulationLink& child, - const PxTransform& childFrame) - { - - return NpFactory::getInstance().createNpArticulationJointRC(static_cast(parent), parentFrame, static_cast(child), childFrame); - } - - void NpArticulationReducedCoordinate::releaseArticulationJoint(PxArticulationJointBase* joint) - { - NpFactory::getInstance().releaseArticulationJointRCToPool(*static_cast(joint)); - } + Scb::Articulation& scbArt = mImpl.getArticulation(); + Sc::ArticulationSim* scArtSim = scbArt.getScArticulation().getSim(); + NpConstraint* constraint = static_cast(joint->getConstraint()); + Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); + if(scArtSim) + scArtSim->addLoopConstraint(cSim); } +void NpArticulationReducedCoordinate::removeLoopJoint(PxJoint* joint) +{ + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + mLoopJoints.findAndReplaceWithLast(joint); + + Scb::Articulation& scbArt = mImpl.getArticulation(); + Sc::ArticulationSim* scArtSim = scbArt.getScArticulation().getSim(); + + NpConstraint* constraint = static_cast(joint->getConstraint()); + Sc::ConstraintSim* cSim = constraint->getScbConstraint().getScConstraint().getSim(); + scArtSim->removeLoopConstraint(cSim); +} + +PxU32 NpArticulationReducedCoordinate::getNbLoopJoints() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + + return mLoopJoints.size(); +} + +PxU32 NpArticulationReducedCoordinate::getLoopJoints(PxJoint** userBuffer, PxU32 bufferSize, PxU32 startIndex) const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + + return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mLoopJoints.begin(), mLoopJoints.size()); +} + +PxU32 NpArticulationReducedCoordinate::getCoefficentMatrixSize() const +{ + NP_READ_CHECK(mImpl.getOwnerScene()); + + return mImpl.mArticulation.getScArticulation().getCoefficentMatrixSize(); +} + +void NpArticulationReducedCoordinate::teleportRootLink(const PxTransform& pose, bool autowake) +{ + PX_CHECK_AND_RETURN(mImpl.getAPIScene(), "PxArticulationReducedCoordinate::teleportRootLink: object must be in a scene"); + + PX_CHECK_AND_RETURN(pose.isValid(), "PxArticulationReducedCoordinate::teleportRootLink pose is not valid."); + + NP_WRITE_CHECK(mImpl.getOwnerScene()); + + NpArticulationLink* root = mImpl.mArticulationLinks[0]; + + root->setGlobalPoseInternal(pose, autowake); +} + +NpArticulationReducedCoordinate::~NpArticulationReducedCoordinate() +{ + NpFactory::getInstance().onArticulationRelease(this); +} + +PxArticulationJointBase* NpArticulationReducedCoordinate::createArticulationJoint(PxArticulationLink& parent, + const PxTransform& parentFrame, + PxArticulationLink& child, + const PxTransform& childFrame) +{ + return NpFactory::getInstance().createNpArticulationJointRC(static_cast(parent), parentFrame, static_cast(child), childFrame); +} + +void NpArticulationReducedCoordinate::releaseArticulationJoint(PxArticulationJointBase* joint) +{ + NpFactory::getInstance().releaseArticulationJointRCToPool(*static_cast(joint)); +} + + diff --git a/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h index 718e31fa1..5280c71ba 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h +++ b/src/PhysX/physx/source/physx/src/NpArticulationReducedCoordinate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpArticulationTemplate.h b/src/PhysX/physx/source/physx/src/NpArticulationTemplate.h index fbe4dc68b..df148c61f 100644 --- a/src/PhysX/physx/source/physx/src/NpArticulationTemplate.h +++ b/src/PhysX/physx/source/physx/src/NpArticulationTemplate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -42,7 +42,6 @@ namespace physx { - class NpArticulationLink; class NpScene; class NpAggregate; @@ -125,15 +124,13 @@ public: PX_FORCE_INLINE Scb::Articulation& getScbArticulation() { return mArticulation; } PX_FORCE_INLINE const Scb::Articulation& getScbArticulation() const { return mArticulation; } - void increaseCacheVersion() { mCacheVersion++; } - + PX_FORCE_INLINE void increaseCacheVersion() { mCacheVersion++; } #if PX_ENABLE_DEBUG_VISUALIZATION public: PX_INLINE void visualize(Cm::RenderOutput& out, NpScene* scene); #endif - Scb::Articulation mArticulation; NpArticulationLinkArray mArticulationLinks; NpAggregate* mAggregate; @@ -159,7 +156,6 @@ public: NpArticulationTemplate(PxBaseFlags baseFlags) : APIClass(baseFlags), mImpl(PxEmpty) {} //~PX_SERIALIZATION - virtual void release(); virtual PxScene* getScene() const { return mImpl.getScene(); } @@ -196,7 +192,6 @@ public: virtual PxArticulationLink* createLink(PxArticulationLink* parent, const PxTransform& pose); - //--------------------------------------------------------------------------------- // Miscellaneous //--------------------------------------------------------------------------------- @@ -208,26 +203,20 @@ public: virtual PxArticulationBase::Enum getType() const { return PxArticulationBase::Enum(mType); } - #if PX_ENABLE_DEBUG_VISUALIZATION public: void visualize(Cm::RenderOutput& out, NpScene* scene) { mImpl.visualize(out, scene); } #endif public: - PxU32 mType; PxArticulationImpl mImpl; - - }; - template NpArticulationTemplate::NpArticulationTemplate() : APIClass(PxConcreteType::eARTICULATION, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE) { - PxArticulationBase::userData = NULL; mType = PxArticulationBase::eMaximumCoordinate; } @@ -328,7 +317,6 @@ void PxArticulationImpl::setSolverIterationCounts(PxU32 positionIters, PxU32 vel getArticulation().setSolverIterationCounts((velocityIters & 0xff) << 8 | (positionIters & 0xff)); } - void PxArticulationImpl::getSolverIterationCounts(PxU32 & positionIters, PxU32 & velocityIters) const { NP_READ_CHECK(getOwnerScene()); @@ -337,7 +325,6 @@ void PxArticulationImpl::getSolverIterationCounts(PxU32 & positionIters, PxU32 & positionIters = PxU32(x & 0xff); } - void PxArticulationImpl::setGlobalPose() { PX_CHECK_AND_RETURN(getAPIScene(), "PxArticulation::setGlobalPose: object must be in a scene"); @@ -361,7 +348,6 @@ void PxArticulationImpl::setGlobalPose() } } - bool PxArticulationImpl::isSleeping() const { NP_READ_CHECK(getOwnerScene()); @@ -370,36 +356,30 @@ bool PxArticulationImpl::isSleeping() const return getArticulation().isSleeping(); } - void PxArticulationImpl::setSleepThreshold(PxReal threshold) { NP_WRITE_CHECK(getOwnerScene()); getArticulation().setSleepThreshold(threshold); } - PxReal PxArticulationImpl::getSleepThreshold() const { NP_READ_CHECK(getOwnerScene()); return getArticulation().getSleepThreshold(); } - void PxArticulationImpl::setStabilizationThreshold(PxReal threshold) { NP_WRITE_CHECK(getOwnerScene()); getArticulation().setFreezeThreshold(threshold); } - PxReal PxArticulationImpl::getStabilizationThreshold() const { NP_READ_CHECK(getOwnerScene()); return getArticulation().getFreezeThreshold(); } - - void PxArticulationImpl::setWakeCounter(PxReal wakeCounterValue) { NP_WRITE_CHECK(getOwnerScene()); @@ -412,14 +392,12 @@ void PxArticulationImpl::setWakeCounter(PxReal wakeCounterValue) getArticulation().setWakeCounter(wakeCounterValue); } - PxReal PxArticulationImpl::getWakeCounter() const { NP_READ_CHECK(getOwnerScene()); return getArticulation().getWakeCounter(); } - void PxArticulationImpl::wakeUpInternal(bool forceWakeUp, bool autowake) { NpScene* scene = getAPIScene(); @@ -447,7 +425,6 @@ void PxArticulationImpl::wakeUpInternal(bool forceWakeUp, bool autowake) } } - void PxArticulationImpl::wakeUp() { NpScene* scene = getAPIScene(); @@ -463,7 +440,6 @@ void PxArticulationImpl::wakeUp() getArticulation().wakeUp(); } - void PxArticulationImpl::putToSleep() { NP_WRITE_CHECK(getOwnerScene()); @@ -477,21 +453,18 @@ void PxArticulationImpl::putToSleep() getArticulation().putToSleep(); } - PxU32 PxArticulationImpl::getNbLinks() const { NP_READ_CHECK(getOwnerScene()); return mArticulationLinks.size(); } - PxU32 PxArticulationImpl::getLinks(PxArticulationLink** userBuffer, PxU32 bufferSize, PxU32 startIndex) const { NP_READ_CHECK(getOwnerScene()); return Cm::getArrayOfPointers(userBuffer, bufferSize, startIndex, mArticulationLinks.begin(), mArticulationLinks.size()); } - PxBounds3 PxArticulationImpl::getWorldBounds(float inflation) const { NP_READ_CHECK(getOwnerScene()); @@ -509,40 +482,34 @@ PxBounds3 PxArticulationImpl::getWorldBounds(float inflation) const return PxBounds3::centerExtents(center, inflatedExtents); } - PxAggregate* PxArticulationImpl::getAggregate() const { NP_READ_CHECK(getOwnerScene()); return mAggregate; } - void PxArticulationImpl::setName(const char* debugName) { NP_WRITE_CHECK(getOwnerScene()); mName = debugName; } - const char* PxArticulationImpl::getName() const { NP_READ_CHECK(getOwnerScene()); return mName; } - NpScene* PxArticulationImpl::getAPIScene() const { return static_cast(mArticulation.getScbSceneForAPI() ? mArticulation.getScbSceneForAPI()->getPxScene() : NULL); } - NpScene* PxArticulationImpl::getOwnerScene() const { return static_cast(mArticulation.getScbScene() ? mArticulation.getScbScene()->getPxScene() : NULL); } - #if PX_ENABLE_DEBUG_VISUALIZATION void PxArticulationImpl::visualize(Cm::RenderOutput& out, NpScene* scene) @@ -552,7 +519,6 @@ void PxArticulationImpl::visualize(Cm::RenderOutput& out, NpScene* scene) } #endif // PX_ENABLE_DEBUG_VISUALIZATION - NpArticulationLink* PxArticulationImpl::getRoot() { if (!mArticulationLinks.size()) diff --git a/src/PhysX/physx/source/physx/src/NpBatchQuery.cpp b/src/PhysX/physx/source/physx/src/NpBatchQuery.cpp index b31ea85a9..76edaa6b7 100644 --- a/src/PhysX/physx/source/physx/src/NpBatchQuery.cpp +++ b/src/PhysX/physx/source/physx/src/NpBatchQuery.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpBatchQuery.h b/src/PhysX/physx/source/physx/src/NpBatchQuery.h index 4bc3c6f9c..6d015f77f 100644 --- a/src/PhysX/physx/source/physx/src/NpBatchQuery.h +++ b/src/PhysX/physx/source/physx/src/NpBatchQuery.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpCast.h b/src/PhysX/physx/source/physx/src/NpCast.h index 4ae9476ec..5663614d2 100644 --- a/src/PhysX/physx/source/physx/src/NpCast.h +++ b/src/PhysX/physx/source/physx/src/NpCast.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpConnector.h b/src/PhysX/physx/source/physx/src/NpConnector.h index d404153d8..da4553445 100644 --- a/src/PhysX/physx/source/physx/src/NpConnector.h +++ b/src/PhysX/physx/source/physx/src/NpConnector.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpConstraint.cpp b/src/PhysX/physx/source/physx/src/NpConstraint.cpp index 7e7d3a1e8..344a46b3a 100644 --- a/src/PhysX/physx/source/physx/src/NpConstraint.cpp +++ b/src/PhysX/physx/source/physx/src/NpConstraint.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpConstraint.h b/src/PhysX/physx/source/physx/src/NpConstraint.h index 4738b4cb4..1bbec1250 100644 --- a/src/PhysX/physx/source/physx/src/NpConstraint.h +++ b/src/PhysX/physx/source/physx/src/NpConstraint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpFactory.cpp b/src/PhysX/physx/source/physx/src/NpFactory.cpp index 416bcd125..a351501d4 100644 --- a/src/PhysX/physx/source/physx/src/NpFactory.cpp +++ b/src/PhysX/physx/source/physx/src/NpFactory.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpFactory.h b/src/PhysX/physx/source/physx/src/NpFactory.h index d36249995..76920eb4f 100644 --- a/src/PhysX/physx/source/physx/src/NpFactory.h +++ b/src/PhysX/physx/source/physx/src/NpFactory.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpMaterial.cpp b/src/PhysX/physx/source/physx/src/NpMaterial.cpp index b743c15cb..ec55138f9 100644 --- a/src/PhysX/physx/source/physx/src/NpMaterial.cpp +++ b/src/PhysX/physx/source/physx/src/NpMaterial.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpMaterial.h b/src/PhysX/physx/source/physx/src/NpMaterial.h index 722b9b379..a7d4f2b93 100644 --- a/src/PhysX/physx/source/physx/src/NpMaterial.h +++ b/src/PhysX/physx/source/physx/src/NpMaterial.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpMaterialManager.h b/src/PhysX/physx/source/physx/src/NpMaterialManager.h index d3c38a842..2179a0f57 100644 --- a/src/PhysX/physx/source/physx/src/NpMaterialManager.h +++ b/src/PhysX/physx/source/physx/src/NpMaterialManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpMetaData.cpp b/src/PhysX/physx/source/physx/src/NpMetaData.cpp index 4eb7c0e99..20ec8b712 100644 --- a/src/PhysX/physx/source/physx/src/NpMetaData.cpp +++ b/src/PhysX/physx/source/physx/src/NpMetaData.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpPhysics.cpp b/src/PhysX/physx/source/physx/src/NpPhysics.cpp index 8db81f7f5..8b5e56ea0 100644 --- a/src/PhysX/physx/source/physx/src/NpPhysics.cpp +++ b/src/PhysX/physx/source/physx/src/NpPhysics.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpPhysics.h b/src/PhysX/physx/source/physx/src/NpPhysics.h index 084f3ce0d..b314d7476 100644 --- a/src/PhysX/physx/source/physx/src/NpPhysics.h +++ b/src/PhysX/physx/source/physx/src/NpPhysics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h b/src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h index aba7297f1..78141e9b6 100644 --- a/src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h +++ b/src/PhysX/physx/source/physx/src/NpPhysicsInsertionCallback.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h b/src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h index 91e66adc0..f3b7c8bbc 100644 --- a/src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h +++ b/src/PhysX/physx/source/physx/src/NpPtrTableStorageManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp index 64de49aae..8d74ad341 100644 --- a/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp +++ b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h index 3290c884d..794623d67 100644 --- a/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h +++ b/src/PhysX/physx/source/physx/src/NpPvdSceneQueryCollector.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpQueryShared.h b/src/PhysX/physx/source/physx/src/NpQueryShared.h index 531cf17a7..3dff64ac0 100644 --- a/src/PhysX/physx/source/physx/src/NpQueryShared.h +++ b/src/PhysX/physx/source/physx/src/NpQueryShared.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpReadCheck.cpp b/src/PhysX/physx/source/physx/src/NpReadCheck.cpp index 3610823cf..a9c4220dc 100644 --- a/src/PhysX/physx/source/physx/src/NpReadCheck.cpp +++ b/src/PhysX/physx/source/physx/src/NpReadCheck.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpReadCheck.h b/src/PhysX/physx/source/physx/src/NpReadCheck.h index a3303b905..61a8d6b7d 100644 --- a/src/PhysX/physx/source/physx/src/NpReadCheck.h +++ b/src/PhysX/physx/source/physx/src/NpReadCheck.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h b/src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h index 6cacd67cf..c61e46c58 100644 --- a/src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h +++ b/src/PhysX/physx/source/physx/src/NpRigidActorTemplate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h b/src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h index f53f334b5..f2488def8 100644 --- a/src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h +++ b/src/PhysX/physx/source/physx/src/NpRigidActorTemplateInternal.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h b/src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h index dea694f13..d84cc948b 100644 --- a/src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h +++ b/src/PhysX/physx/source/physx/src/NpRigidBodyTemplate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -156,7 +156,7 @@ public: void visualize(Cm::RenderOutput& out, NpScene* scene); #endif - PX_FORCE_INLINE bool isKinematic() + PX_FORCE_INLINE bool isKinematic() const { return (APIClass::getConcreteType() == PxConcreteType::eRIGID_DYNAMIC) && (getScbBodyFast().getFlags() & PxRigidBodyFlag::eKINEMATIC); } diff --git a/src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp b/src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp index f0c4573b9..0e50bdc84 100644 --- a/src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp +++ b/src/PhysX/physx/source/physx/src/NpRigidDynamic.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -47,6 +47,33 @@ void NpRigidDynamic::requiresObjects(PxProcessPxBaseCallback& c) NpRigidDynamicT::requiresObjects(c); } +void NpRigidDynamic::exportData(PxSerializationContext& context) const +{ + //Clearing the aggregate ID for serialization so we avoid having a stale + //reference after deserialization. The aggregate ID get's reset on readding to the + //scene anyway. + //Also restore dynamic data in case the actor is configured as a kinematic. + //otherwise we would loose the data for switching the kinematic actor back to dynamic + //after deserialization. + Sc::BodyCore& bodyCore = const_cast(getScbBodyFast().getScBody()); + Sc::ActorCore& actorCore = const_cast(getScbActorFast().getActorCore()); + if (isKinematic()) + { + bodyCore.restoreDynamicData(); + } + PxU32 backupAggregateID = actorCore.getAggregateID(); + actorCore.setAggregateID(PX_INVALID_U32); + + context.writeData(this, sizeof(NpRigidDynamic)); + + actorCore.setAggregateID(backupAggregateID); + + if (isKinematic()) + { + bodyCore.backupDynamicData(); + } +} + NpRigidDynamic* NpRigidDynamic::createObject(PxU8*& address, PxDeserializationContext& context) { NpRigidDynamic* obj = new (address) NpRigidDynamic(PxBaseFlag::eIS_RELEASABLE); diff --git a/src/PhysX/physx/source/physx/src/NpRigidDynamic.h b/src/PhysX/physx/source/physx/src/NpRigidDynamic.h index ea9aaa914..749fd333d 100644 --- a/src/PhysX/physx/source/physx/src/NpRigidDynamic.h +++ b/src/PhysX/physx/source/physx/src/NpRigidDynamic.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -54,6 +54,7 @@ public: // PX_SERIALIZATION NpRigidDynamic(PxBaseFlags baseFlags) : NpRigidDynamicT(baseFlags) {} + virtual void exportData(PxSerializationContext& context) const; virtual void requiresObjects(PxProcessPxBaseCallback& c); static NpRigidDynamic* createObject(PxU8*& address, PxDeserializationContext& context); diff --git a/src/PhysX/physx/source/physx/src/NpRigidStatic.cpp b/src/PhysX/physx/source/physx/src/NpRigidStatic.cpp index 34af3d241..40148b4a6 100644 --- a/src/PhysX/physx/source/physx/src/NpRigidStatic.cpp +++ b/src/PhysX/physx/source/physx/src/NpRigidStatic.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -47,6 +47,20 @@ NpRigidStatic::~NpRigidStatic() } // PX_SERIALIZATION +void NpRigidStatic::exportData(PxSerializationContext& context) const +{ + //Clearing the aggregate ID for serialization so we avoid having a stale + //reference after deserialization. The aggregate ID get's reset on readding to the + //scene anyway. + Sc::ActorCore& actorCore = const_cast(getScbActorFast().getActorCore()); + PxU32 backupAggregateID = actorCore.getAggregateID(); + actorCore.setAggregateID(PX_INVALID_U32); + + context.writeData(this, sizeof(NpRigidStatic)); + + actorCore.setAggregateID(backupAggregateID); +} + void NpRigidStatic::requiresObjects(PxProcessPxBaseCallback& c) { NpRigidStaticT::requiresObjects(c); diff --git a/src/PhysX/physx/source/physx/src/NpRigidStatic.h b/src/PhysX/physx/source/physx/src/NpRigidStatic.h index 7da078970..16ef2bcc3 100644 --- a/src/PhysX/physx/source/physx/src/NpRigidStatic.h +++ b/src/PhysX/physx/source/physx/src/NpRigidStatic.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -59,6 +59,7 @@ class NpRigidStatic : public NpRigidStaticT public: // PX_SERIALIZATION NpRigidStatic(PxBaseFlags baseFlags) : NpRigidStaticT(baseFlags), mRigidStatic(PxEmpty) {} + virtual void exportData(PxSerializationContext& context) const; virtual void requiresObjects(PxProcessPxBaseCallback& c); static NpRigidStatic* createObject(PxU8*& address, PxDeserializationContext& context); static void getBinaryMetaData(PxOutputStream& stream); diff --git a/src/PhysX/physx/source/physx/src/NpScene.cpp b/src/PhysX/physx/source/physx/src/NpScene.cpp index d6f6d9f35..0b26e83f0 100644 --- a/src/PhysX/physx/source/physx/src/NpScene.cpp +++ b/src/PhysX/physx/source/physx/src/NpScene.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -835,30 +835,36 @@ void NpScene::addArticulation(PxArticulationBase& articulation) Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation already assigned to a scene. Call will be ignored!"); } +static void checkArticulationLink(NpScene* scene, NpArticulationLink* link) +{ +#if PX_CHECKED + scene->checkPositionSanity(*link, link->getGlobalPose(), "PxScene::addArticulation or PxScene::addAggregate"); +#else + PX_UNUSED(scene); +#endif + if(link->getMass()==0.0f) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero mass added to scene; defaulting mass to 1"); + link->setMass(1.0f); + } + + const PxVec3 inertia0 = link->getMassSpaceInertiaTensor(); + if(inertia0.x == 0.0f || inertia0.y == 0.0f || inertia0.z == 0.0f) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero moment of inertia added to scene; defaulting inertia to (1,1,1)"); + link->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); + } +} + void NpScene::addArticulationInternal(PxArticulationBase& npa) { - // Add root link first PxU32 nbLinks = npa.getNbLinks(); PX_ASSERT(nbLinks > 0); PxArticulationImpl* impl = reinterpret_cast(npa.getImpl()); NpArticulationLink* rootLink = static_cast(impl->getRoot()); -#if PX_CHECKED - checkPositionSanity(*rootLink, rootLink->getGlobalPose(), "PxScene::addArticulation or PxScene::addAggregate"); -#endif - if(rootLink->getMass()==0) - { - Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero mass added to scene; defaulting mass to 1"); - rootLink->setMass(1.0f); - } - - PxVec3 inertia0 = rootLink->getMassSpaceInertiaTensor(); - if(inertia0.x == 0.0f || inertia0.y == 0.0f || inertia0.z == 0.0f) - { - Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero moment of inertia added to scene; defaulting inertia to (1,1,1)"); - rootLink->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); - } + checkArticulationLink(this, rootLink); bool linkTriggersWakeUp = !rootLink->getScbBodyFast().checkSleepReadinessBesidesWakeCounter(); @@ -897,21 +903,7 @@ void NpScene::addArticulationInternal(PxArticulationBase& npa) { NpArticulationLink* child = children[i]; -#if PX_CHECKED - checkPositionSanity(*child, child->getGlobalPose(), "PxScene::addArticulation"); -#endif - if(child->getMass()==0) - { - Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero mass added to scene; defaulting mass to 1"); - child->setMass(1.0f); - } - - PxVec3 inertia = child->getMassSpaceInertiaTensor(); - if(inertia.x == 0.0f || inertia.y == 0.0f || inertia.z == 0.0f) - { - Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): Articulation link with zero moment of inertia added to scene; defaulting inertia to (1,1,1)"); - child->setMassSpaceInertiaTensor(PxVec3(1.0f, 1.0f, 1.0f)); - } + checkArticulationLink(this, child); linkTriggersWakeUp = linkTriggersWakeUp || (!child->getScbBodyFast().checkSleepReadinessBesidesWakeCounter()); @@ -955,8 +947,44 @@ void NpScene::addArticulationInternal(PxArticulationBase& npa) for (PxU32 i = 0; i < l->getNbChildren(); i++) { NpArticulationLink* child = children[i]; + child->setInboundJointDof(scArtSim->getDof(child->getLinkIndex())); + if (npa.getType() == PxArticulationBase::eReducedCoordinate) + { + PxArticulationJointReducedCoordinate* joint = static_cast(child->getInboundJoint()); + PxArticulationJointType::Enum jointType = joint->getJointType(); + + if (jointType == PxArticulationJointType::eUNDEFINED) + { + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): The application need to set joint type. defaulting joint type to eFix"); + joint->setJointType(PxArticulationJointType::eFIX); + child->setInboundJointDof(0); + } + + if (jointType != PxArticulationJointType::eFIX) + { + + PxArticulationMotion::Enum motionX = joint->getMotion(PxArticulationAxis::eX); + PxArticulationMotion::Enum motionY = joint->getMotion(PxArticulationAxis::eY); + PxArticulationMotion::Enum motionZ = joint->getMotion(PxArticulationAxis::eZ); + + PxArticulationMotion::Enum motionSwing1 = joint->getMotion(PxArticulationAxis::eSWING1); + PxArticulationMotion::Enum motionSwing2 = joint->getMotion(PxArticulationAxis::eSWING2); + PxArticulationMotion::Enum motionTwist = joint->getMotion(PxArticulationAxis::eTWIST); + + //PxArticulationMotion::eLOCKED is 0 + if (!(motionX | motionY | motionZ | motionSwing1 | motionSwing2 | motionTwist)) + { + //if all axis are locked, which means the user doesn't set the motion. In this case, we should change the joint type to be + //fix to avoid crash in the solver + Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "PxScene::addArticulation(): The application need to set joint motion. defaulting joint type to eFix"); + joint->setJointType(PxArticulationJointType::eFIX); + child->setInboundJointDof(0); + } + } + } + linkStack[stackSize] = child; stackSize++; } @@ -1871,20 +1899,15 @@ void NpScene::simulateOrCollide(PxReal elapsedTime, physx::PxBaseTask* completio mSceneCollide.setContinuation(&mCollisionCompletion); //Initialize scene completion task mSceneCompletion.setContinuation(*mTaskManager, NULL); - } - else - { - mSceneCompletion.setContinuation(*mTaskManager, completionTask); - mSceneExecution.setContinuation(*mTaskManager, &mSceneCompletion); - } - if (simStage == Sc::SimulationStage::eCOLLIDE) - { mCollisionCompletion.removeReference(); mSceneCollide.removeReference(); } else { + mSceneCompletion.setContinuation(*mTaskManager, completionTask); + mSceneExecution.setContinuation(*mTaskManager, &mSceneCompletion); + mSceneCompletion.removeReference(); mSceneExecution.removeReference(); } @@ -3020,3 +3043,4 @@ void NpScene::releaseBatchQuery(PxBatchQuery* sq) PX_UNUSED(found); PX_ASSERT(found); PX_DELETE_AND_RESET(npsq); } + diff --git a/src/PhysX/physx/source/physx/src/NpScene.h b/src/PhysX/physx/source/physx/src/NpScene.h index eca152c6e..a6c1aa002 100644 --- a/src/PhysX/physx/source/physx/src/NpScene.h +++ b/src/PhysX/physx/source/physx/src/NpScene.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -47,7 +47,6 @@ namespace physx { - class PhysicsThread; class PxBatchQueryDesc; class NpMaterial; @@ -75,18 +74,6 @@ class NpBatchQuery; class PxBatchQuery; -enum NpProfileZones -{ - NpScene_checkResults, - NpScene_reportContacts, - NpScene_reportProfiling, - NpScene_reportTriggers, - NpScene_stats, - - NpPrNumZones -}; - - class NpContactCallbackTask : public physx::PxLightCpuTask { NpScene* mScene; @@ -207,8 +194,6 @@ class NpScene : public NpSceneQueries, public Ps::UserAllocated virtual void processCallbacks(physx::PxBaseTask* continuation); virtual void fetchResultsFinish(PxU32* errorState = 0); - - virtual void flush(bool sendPendingReports) { flushSimulation(sendPendingReports); } virtual void flushSimulation(bool sendPendingReports); virtual void flushQueryUpdates(); @@ -369,8 +354,6 @@ private: void fetchResultsPreContactCallbacks(); void fetchResultsPostContactCallbacks(); - - void updateScbStateAndSetupSq(const PxRigidActor& rigidActor, Scb::Actor& actor, NpShapeManager& shapeManager, bool actorDynamic, const PxBounds3* bounds, bool hasPrunerStructure); PX_FORCE_INLINE void updateScbStateAndSetupSq(const PxRigidActor& rigidActor, Scb::Body& body, NpShapeManager& shapeManager, bool actorDynamic, const PxBounds3* bounds, bool hasPrunerStructure); @@ -391,14 +374,12 @@ private: Ps::Sync mCollisionDone; // physics thread signals this when all collisions ready Ps::Sync mSceneQueriesDone; // physics thread signals this when all scene queries update ready - //legacy timing settings: PxReal mElapsedTime; //needed to transfer the elapsed time param from the user to the sim thread. PxU32 mNbClients; // Tracks reserved clients for multiclient support. Ps::Array mClientBehaviorFlags;// Tracks behavior bits for clients. - struct SceneCompletion : public Cm::Task { SceneCompletion(PxU64 contextId, Ps::Sync& sync) : Cm::Task(contextId), mSync(sync){} @@ -433,7 +414,6 @@ private: typedef Cm::DelegateTask SceneCollide; typedef Cm::DelegateTask SceneAdvance; - PxTaskManager* mTaskManager; SceneCompletion mSceneCompletion; SceneCompletion mCollisionCompletion; @@ -461,13 +441,11 @@ private: bool mBuildFrozenActors; }; - PX_FORCE_INLINE void NpScene::addToConstraintList(PxConstraint& constraint) { mConstraints.insert(&constraint); } - PX_FORCE_INLINE void NpScene::removeFromConstraintList(PxConstraint& constraint) { const bool exists = mConstraints.erase(&constraint); @@ -475,7 +453,6 @@ PX_FORCE_INLINE void NpScene::removeFromConstraintList(PxConstraint& constraint) PX_UNUSED(exists); } - PX_FORCE_INLINE void NpScene::removeFromArticulationList(PxArticulationBase& articulation) { const bool exists = mArticulations.erase(&articulation); @@ -483,7 +460,6 @@ PX_FORCE_INLINE void NpScene::removeFromArticulationList(PxArticulationBase& art PX_UNUSED(exists); } - PX_FORCE_INLINE void NpScene::removeFromAggregateList(PxAggregate& aggregate) { const bool exists = mAggregates.erase(&aggregate); diff --git a/src/PhysX/physx/source/physx/src/NpSceneAccessor.h b/src/PhysX/physx/source/physx/src/NpSceneAccessor.h index b729b8c6d..0fd04a8d7 100644 --- a/src/PhysX/physx/source/physx/src/NpSceneAccessor.h +++ b/src/PhysX/physx/source/physx/src/NpSceneAccessor.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpSceneQueries.cpp b/src/PhysX/physx/source/physx/src/NpSceneQueries.cpp index 9cb098993..6c5877714 100644 --- a/src/PhysX/physx/source/physx/src/NpSceneQueries.cpp +++ b/src/PhysX/physx/source/physx/src/NpSceneQueries.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpSceneQueries.h b/src/PhysX/physx/source/physx/src/NpSceneQueries.h index e07fcfd73..87034250e 100644 --- a/src/PhysX/physx/source/physx/src/NpSceneQueries.h +++ b/src/PhysX/physx/source/physx/src/NpSceneQueries.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp b/src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp index eb48292b2..f4a0c3c1c 100644 --- a/src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp +++ b/src/PhysX/physx/source/physx/src/NpSerializerAdapter.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -54,23 +54,14 @@ namespace physx using namespace physx::Gu; template<> - void PxSerializerDefaultAdapter::exportData(PxBase& obj, PxSerializationContext& s) const + void PxSerializerDefaultAdapter::exportData(PxBase& obj, PxSerializationContext& s) const { - PxU32 classSize = sizeof(NpRigidDynamic); - NpRigidDynamic& dynamic = static_cast(obj); - - PxsBodyCore serialCore; - size_t address = dynamic.getScbBodyFast().getScBody().getSerialCore(serialCore); - PxU32 offset = PxU32(address - reinterpret_cast(&dynamic)); - PX_ASSERT(offset + sizeof(serialCore) <= classSize); - s.writeData(&dynamic, offset); - s.writeData(&serialCore, sizeof(serialCore)); - void* tail = reinterpret_cast(&dynamic) + offset + sizeof(serialCore); - s.writeData(tail, classSize - offset - sizeof(serialCore)); + NpRigidDynamic& rigidDynamic = static_cast(obj); + rigidDynamic.exportData(s); } template<> - void PxSerializerDefaultAdapter::registerReferences(PxBase& obj, PxSerializationContext& s) const + void PxSerializerDefaultAdapter::registerReferences(PxBase& obj, PxSerializationContext& s) const { NpRigidDynamic& dynamic = static_cast(obj); @@ -92,7 +83,21 @@ namespace physx } template<> - void PxSerializerDefaultAdapter::registerReferences(PxBase& obj, PxSerializationContext& s) const + void PxSerializerDefaultAdapter::exportData(PxBase& obj, PxSerializationContext& s) const + { + NpRigidStatic& rigidStatic = static_cast(obj); + rigidStatic.exportData(s); + } + + template<> + void PxSerializerDefaultAdapter::exportData(PxBase& obj, PxSerializationContext& s) const + { + NpArticulationLink& articulationLink = static_cast(obj); + articulationLink.exportData(s); + } + + template<> + void PxSerializerDefaultAdapter::registerReferences(PxBase& obj, PxSerializationContext& s) const { NpShape& shape = static_cast(obj); diff --git a/src/PhysX/physx/source/physx/src/NpShape.cpp b/src/PhysX/physx/source/physx/src/NpShape.cpp index fbbe27edc..5f56b1173 100644 --- a/src/PhysX/physx/source/physx/src/NpShape.cpp +++ b/src/PhysX/physx/source/physx/src/NpShape.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpShape.h b/src/PhysX/physx/source/physx/src/NpShape.h index ccc193ddf..d67be2902 100644 --- a/src/PhysX/physx/source/physx/src/NpShape.h +++ b/src/PhysX/physx/source/physx/src/NpShape.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpShapeManager.cpp b/src/PhysX/physx/source/physx/src/NpShapeManager.cpp index 0a37f728e..f68725194 100644 --- a/src/PhysX/physx/source/physx/src/NpShapeManager.cpp +++ b/src/PhysX/physx/source/physx/src/NpShapeManager.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpShapeManager.h b/src/PhysX/physx/source/physx/src/NpShapeManager.h index 52a840369..02855e59e 100644 --- a/src/PhysX/physx/source/physx/src/NpShapeManager.h +++ b/src/PhysX/physx/source/physx/src/NpShapeManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpWriteCheck.cpp b/src/PhysX/physx/source/physx/src/NpWriteCheck.cpp index f770f59da..397814ee7 100644 --- a/src/PhysX/physx/source/physx/src/NpWriteCheck.cpp +++ b/src/PhysX/physx/source/physx/src/NpWriteCheck.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/NpWriteCheck.h b/src/PhysX/physx/source/physx/src/NpWriteCheck.h index 5095a4c49..086126dd3 100644 --- a/src/PhysX/physx/source/physx/src/NpWriteCheck.h +++ b/src/PhysX/physx/source/physx/src/NpWriteCheck.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h b/src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h index 552b95a0b..3644ce56c 100644 --- a/src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h +++ b/src/PhysX/physx/source/physx/src/PvdMetaDataBindingData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_META_DATA_PVD_BINDING_DATA_H diff --git a/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp index ee812cd5f..ffb3e6c5c 100644 --- a/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp +++ b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h index 55df602b2..1ede5d99f 100644 --- a/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h +++ b/src/PhysX/physx/source/physx/src/PvdMetaDataPvdBinding.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp index 537cee7c2..044969654 100644 --- a/src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp +++ b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/PvdPhysicsClient.h b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.h index f4704b715..7e02bbc92 100644 --- a/src/PhysX/physx/source/physx/src/PvdPhysicsClient.h +++ b/src/PhysX/physx/source/physx/src/PvdPhysicsClient.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/PvdTypeNames.h b/src/PhysX/physx/source/physx/src/PvdTypeNames.h index d7a3b82f6..8ef01f1d1 100644 --- a/src/PhysX/physx/source/physx/src/PvdTypeNames.h +++ b/src/PhysX/physx/source/physx/src/PvdTypeNames.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PVD_TYPE_NAMES_H diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp index 67a921c11..fb4ee229b 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp +++ b/src/PhysX/physx/source/physx/src/buffering/ScbActor.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbActor.h b/src/PhysX/physx/source/physx/src/buffering/ScbActor.h index bba2c17f5..c3d40319c 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbActor.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbActor.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp index 85dd5edf2..e46e701cb 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp +++ b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h index 28cf591cd..c478da3a8 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbAggregate.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h b/src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h index 3d1a13089..a206940cc 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbArticulation.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h b/src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h index 67e743665..141abde03 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbArticulationJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp index 6fc476f57..8b5561a14 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp +++ b/src/PhysX/physx/source/physx/src/buffering/ScbBase.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbBase.h b/src/PhysX/physx/source/physx/src/buffering/ScbBase.h index c8b160edd..53f81b45f 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbBase.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbBase.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbBody.h b/src/PhysX/physx/source/physx/src/buffering/ScbBody.h index 33bee2a54..c674ff961 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbBody.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbBody.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h b/src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h index 129f68f8b..f5257dc3b 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbConstraint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbDefs.h b/src/PhysX/physx/source/physx/src/buffering/ScbDefs.h index d8c8c1111..be99a84a7 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbDefs.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbDefs.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp index fe498ee04..31b871258 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp +++ b/src/PhysX/physx/source/physx/src/buffering/ScbMetaData.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h b/src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h index e0868a82d..b21d7ee1e 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbNpDeps.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h b/src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h index f4cc38003..5411684a5 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbRigidObject.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h b/src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h index 7589faab3..6979e026f 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbRigidStatic.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp index 751af5837..80081f5b4 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp +++ b/src/PhysX/physx/source/physx/src/buffering/ScbScene.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbScene.h b/src/PhysX/physx/source/physx/src/buffering/ScbScene.h index af3934ca7..8d4d32601 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbScene.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbScene.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h b/src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h index 1fa8ad9d0..673013013 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbSceneBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.cpp index b9b1b4cc3..158247445 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.cpp +++ b/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h b/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h index d84414b2b..d9acaa50c 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbScenePvdClient.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp b/src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp index ccae62a06..74aa0d8cf 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp +++ b/src/PhysX/physx/source/physx/src/buffering/ScbShape.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbShape.h b/src/PhysX/physx/source/physx/src/buffering/ScbShape.h index 1652c477e..d5aaa5239 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbShape.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbShape.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/buffering/ScbType.h b/src/PhysX/physx/source/physx/src/buffering/ScbType.h index f1108ae61..2b8a5c611 100644 --- a/src/PhysX/physx/source/physx/src/buffering/ScbType.h +++ b/src/PhysX/physx/source/physx/src/buffering/ScbType.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/device/PhysXIndicator.h b/src/PhysX/physx/source/physx/src/device/PhysXIndicator.h index 6bb76f239..fa8758129 100644 --- a/src/PhysX/physx/source/physx/src/device/PhysXIndicator.h +++ b/src/PhysX/physx/source/physx/src/device/PhysXIndicator.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp b/src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp index c21905982..40e99a130 100644 --- a/src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp +++ b/src/PhysX/physx/source/physx/src/device/linux/PhysXIndicatorLinux.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h b/src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h index cbffc8292..ee0432c29 100644 --- a/src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h +++ b/src/PhysX/physx/source/physx/src/device/nvPhysXtoDrv.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp b/src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp index 6357e936f..71fd0f9b6 100644 --- a/src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp +++ b/src/PhysX/physx/source/physx/src/device/windows/PhysXIndicatorWindows.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp b/src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp index 6f8e5f3a5..35432d3f5 100644 --- a/src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp +++ b/src/PhysX/physx/source/physx/src/gpu/PxGpu.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "PxPhysXConfig.h" diff --git a/src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp b/src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp index 288d812da..994576c65 100644 --- a/src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp +++ b/src/PhysX/physx/source/physx/src/gpu/PxPhysXGpuModuleLoader.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "PxPhysXConfig.h" diff --git a/src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp b/src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp index 203a12cca..942cffe42 100644 --- a/src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp +++ b/src/PhysX/physx/source/physx/src/windows/NpWindowsDelayLoadHook.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp index f025526bb..39f3c2043 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h index 46f6d238d..874ab20af 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctBoxController.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp index ea96dfb56..8f0cf345d 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h index cefab7c38..bc35f1832 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCapsuleController.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp index 510ad5c0c..defaab48b 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h index b075a0ca3..c6aaa2cd8 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterController.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp index 893849a9c..105e8f79f 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerCallbacks.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp index 007b50603..6d0bc1ced 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -31,7 +31,6 @@ #include "CctBoxController.h" #include "CctCapsuleController.h" #include "CctObstacleContext.h" -#include "CmBoxPruning.h" #include "GuDistanceSegmentSegment.h" #include "GuDistanceSegmentBox.h" #include "PsUtilities.h" @@ -40,6 +39,7 @@ #include "PxScene.h" #include "PxPhysics.h" #include "PsFoundation.h" +#include "CmRadixSortBuffered.h" using namespace physx; using namespace Cct; @@ -606,6 +606,49 @@ static void InteractionCharacterCharacter(Controller* entity0, Controller* entit } } +// PT: TODO: this is the very old version, revisit with newer one +static void completeBoxPruning(const PxBounds3* bounds, PxU32 nb, Ps::Array& pairs) +{ + if(!nb) + return; + + pairs.clear(); + + float* PosList = reinterpret_cast(PX_ALLOC_TEMP(sizeof(float)*nb, "completeBoxPruning")); + + for(PxU32 i=0;i pairs; // PT: TODO: get rid of alloc - Cm::CompleteBoxPruning(boxes, nbEntities, pairs, Gu::Axes(physx::Gu::AXES_XZY)); // PT: TODO: revisit for variable up axis + completeBoxPruning(boxes, nbEntities, pairs); PxU32 nbPairs = pairs.size()>>1; const PxU32* indices = pairs.begin(); @@ -654,7 +697,7 @@ void CharacterControllerManager::computeInteractions(PxF32 elapsedTime, PxContro /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Public factory methods -PX_C_EXPORT PX_PHYSX_CHARACTER_API PxControllerManager* PX_CALL_CONV PxCreateControllerManager(PxScene& scene, bool lockingEnabled) +PX_C_EXPORT PxControllerManager* PX_CALL_CONV PxCreateControllerManager(PxScene& scene, bool lockingEnabled) { Ps::Foundation::incRefCount(); return PX_NEW(CharacterControllerManager)(scene, lockingEnabled); diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h index 94a64dfeb..10fbcf86b 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctCharacterControllerManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp index 96cced109..5a27772bf 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h index ab6a4c539..915e2dffc 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctController.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h index 71452aa23..a76cafedb 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctInternalStructs.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp index 36575cd79..0e10d5506 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.h index d5626deae..90718a1d7 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctObstacleContext.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp index d6731e02a..25aae1e1e 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h index 48ab0d954..2eb132cda 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptBox.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp index f4d735d84..d2a31f31e 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h index 8c9a2d8a0..fb7328938 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptCapsule.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp index 1d846b460..e4d8413b1 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h index a29435b04..2a721cf92 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctSweptVolume.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h b/src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h index 8ac1e7227..61cc0fe2e 100644 --- a/src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h +++ b/src/PhysX/physx/source/physxcharacterkinematic/src/CctUtils.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp b/src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp index 4db1c96da..18c472423 100644 --- a/src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp +++ b/src/PhysX/physx/source/physxcooking/src/Adjacencies.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/Adjacencies.h b/src/PhysX/physx/source/physxcooking/src/Adjacencies.h index 3df99bf83..8c66fbe20 100644 --- a/src/PhysX/physx/source/physxcooking/src/Adjacencies.h +++ b/src/PhysX/physx/source/physxcooking/src/Adjacencies.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp index 77e9e5f3a..84bdc5ff3 100644 --- a/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp +++ b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h index 93bf575b4..0d791e8b9 100644 --- a/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h +++ b/src/PhysX/physx/source/physxcooking/src/BVHStructureBuilder.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/Cooking.cpp b/src/PhysX/physx/source/physxcooking/src/Cooking.cpp index a56376309..051a04608 100644 --- a/src/PhysX/physx/source/physxcooking/src/Cooking.cpp +++ b/src/PhysX/physx/source/physxcooking/src/Cooking.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/Cooking.h b/src/PhysX/physx/source/physxcooking/src/Cooking.h index 7ee48ba51..553141cf1 100644 --- a/src/PhysX/physx/source/physxcooking/src/Cooking.h +++ b/src/PhysX/physx/source/physxcooking/src/Cooking.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp b/src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp index ed50a5c9d..f6074439b 100644 --- a/src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp +++ b/src/PhysX/physx/source/physxcooking/src/CookingUtils.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/CookingUtils.h b/src/PhysX/physx/source/physxcooking/src/CookingUtils.h index 7f5544b71..c5542dd21 100644 --- a/src/PhysX/physx/source/physxcooking/src/CookingUtils.h +++ b/src/PhysX/physx/source/physxcooking/src/CookingUtils.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/EdgeList.cpp b/src/PhysX/physx/source/physxcooking/src/EdgeList.cpp index aca9db0d8..d95752cfc 100644 --- a/src/PhysX/physx/source/physxcooking/src/EdgeList.cpp +++ b/src/PhysX/physx/source/physxcooking/src/EdgeList.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/EdgeList.h b/src/PhysX/physx/source/physxcooking/src/EdgeList.h index 5133f66e9..c8b9a03bc 100644 --- a/src/PhysX/physx/source/physxcooking/src/EdgeList.h +++ b/src/PhysX/physx/source/physxcooking/src/EdgeList.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/MeshCleaner.cpp b/src/PhysX/physx/source/physxcooking/src/MeshCleaner.cpp index 0983bfe99..a59c7926e 100644 --- a/src/PhysX/physx/source/physxcooking/src/MeshCleaner.cpp +++ b/src/PhysX/physx/source/physxcooking/src/MeshCleaner.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/MeshCleaner.h b/src/PhysX/physx/source/physxcooking/src/MeshCleaner.h index 6e58f71ec..69a3fd4bb 100644 --- a/src/PhysX/physx/source/physxcooking/src/MeshCleaner.h +++ b/src/PhysX/physx/source/physxcooking/src/MeshCleaner.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/Quantizer.cpp b/src/PhysX/physx/source/physxcooking/src/Quantizer.cpp index 4939a211d..20f68e3ba 100644 --- a/src/PhysX/physx/source/physxcooking/src/Quantizer.cpp +++ b/src/PhysX/physx/source/physxcooking/src/Quantizer.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/Quantizer.h b/src/PhysX/physx/source/physxcooking/src/Quantizer.h index cbab0e12e..cf5da0628 100644 --- a/src/PhysX/physx/source/physxcooking/src/Quantizer.h +++ b/src/PhysX/physx/source/physxcooking/src/Quantizer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp index 5db4c6abe..8daffb84c 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp +++ b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h index 3317fc2b6..21555ffbc 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h +++ b/src/PhysX/physx/source/physxcooking/src/convex/BigConvexDataBuilder.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp index c7c43414e..f20f7ed40 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h index e68f5ed0f..d49efaa93 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullBuilder.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp index f5b1033f3..169c34229 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h index 4d250f7c2..2c19adb87 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullLib.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp index 89309da59..d56053e92 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h index c97ff2b61..09f78a7ac 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexHullUtils.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp index c484be77d..ca8de9a70 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.h index 6c9a2a236..66cade0b5 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.h +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexMeshBuilder.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.cpp index 072903ac6..7f585c2fb 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.cpp +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h b/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h index ee2f67452..378fe1a50 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h +++ b/src/PhysX/physx/source/physxcooking/src/convex/ConvexPolygonsBuilder.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp index 9b280d9e4..ded4c77dc 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp +++ b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h index 7d576ed1f..5d85e21c1 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h +++ b/src/PhysX/physx/source/physxcooking/src/convex/QuickHullConvexHullLib.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp index 0c739bc90..ed5adb95f 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp +++ b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h index b8ae55beb..d448d2586 100644 --- a/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h +++ b/src/PhysX/physx/source/physxcooking/src/convex/VolumeIntegration.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp index c0f4beeea..0b5f01a6f 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp +++ b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h index 5cc742b7a..d5f49299b 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h +++ b/src/PhysX/physx/source/physxcooking/src/mesh/GrbTriangleMeshCooking.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp index 2b6056b83..1333c0d7e 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp +++ b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h index e3d5ef89f..4eb067ba7 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h +++ b/src/PhysX/physx/source/physxcooking/src/mesh/HeightFieldCooking.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp index 4da4ad3af..963a7a4c8 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp +++ b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h index b7cb901c4..aad5e605e 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h +++ b/src/PhysX/physx/source/physxcooking/src/mesh/MeshBuilder.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h b/src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h index e108f701a..35494bb2c 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h +++ b/src/PhysX/physx/source/physxcooking/src/mesh/QuickSelect.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp index f42a1c3c3..ef00de68d 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp +++ b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h index 6faf2f674..32254bdd0 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h +++ b/src/PhysX/physx/source/physxcooking/src/mesh/RTreeCooking.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp index 67cacd35f..79ad9ccee 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp +++ b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h index 4b187ec9e..fb745723b 100644 --- a/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h +++ b/src/PhysX/physx/source/physxcooking/src/mesh/TriangleMeshBuilder.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp b/src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp index 177dbe40b..cf8e9e700 100644 --- a/src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp +++ b/src/PhysX/physx/source/physxcooking/src/windows/WindowsCookingDelayLoadHook.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp b/src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp index ae638fb47..052d817b3 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtBroadPhase.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtCollection.cpp b/src/PhysX/physx/source/physxextensions/src/ExtCollection.cpp index ee9de2ae2..761cfe757 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtCollection.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtCollection.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h b/src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h index f9cdb45a2..7bebe5fd5 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtConstraintHelper.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp index 449eb6724..dadfdf0f5 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h index 08ad31d10..f378ea8a1 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtContactJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp index 97174e1e6..806a7cab4 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtConvexMeshExt.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp index 30e17c35a..4bdf74d6c 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h index e3f37b3f4..96269df25 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtCpuWorkerThread.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp index ac2e5fcea..065d26bad 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h index 93d3da2c7..16a9199ba 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtD6Joint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp b/src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp index dc4e7205d..f9ecc6a85 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtD6JointCreate.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp index e8a1470f4..f45245b2c 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h index fce239635..970a64344 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultCpuDispatcher.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp index d938a28b0..4e81ce3c6 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultErrorCallback.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp index 16e28fba5..3711112bb 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultSimulationFilterShader.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp index 44e5fb9c9..b16928cf6 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtDefaultStreams.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp index 33e08fe1e..0349e9073 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h index 09c136434..4cdcb2abf 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtDistanceJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp b/src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp index 05cd12411..6d6abc71c 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtExtensions.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp index ab9538df9..6228b5a10 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h index 34b8e3397..78d4606df 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtFixedJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h b/src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h index dcef2415c..7d6d94592 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtInertiaTensor.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp index 32ee392b4..8b2c4c0d3 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtJoint.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtJoint.h index 0bce87e3b..aae7e8213 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtJoint.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h b/src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h index 8c35c211d..fd0ed8068 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtJointMetaDataExtensions.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp b/src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp index 411ede6b3..2b7eeeedd 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtMetaData.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPlatform.h b/src/PhysX/physx/source/physxextensions/src/ExtPlatform.h index 8a7701970..3812614e1 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtPlatform.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtPlatform.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp index 86b9eaff5..18242e8c5 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h index f03fca2e6..d32708aab 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtPrismaticJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp b/src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp index ca1f40670..95a38384b 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtPvd.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPvd.h b/src/PhysX/physx/source/physxextensions/src/ExtPvd.h index 1f9d5796b..f9bbf484e 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtPvd.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtPvd.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp b/src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp index 8d640a4ae..c44ead204 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtPxStringTable.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp index 64a97800d..f59276497 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtRaycastCCD.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp index 39819b6a3..a2c96829e 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h index 36fa1eefe..ad69a1b5f 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtRevoluteJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp index acfe736f6..b619949f3 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtRigidActorExt.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp index 3f8e79259..6b1dea83a 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtRigidBodyExt.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp index 577f2ef34..316349061 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtSceneQueryExt.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSerialization.h b/src/PhysX/physx/source/physxextensions/src/ExtSerialization.h index 387566bf6..5a560a4e1 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtSerialization.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtSerialization.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h b/src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h index 9728df4d2..733e97958 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtSharedQueueEntryPool.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp b/src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp index 35f899e21..65460e4ce 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtSimpleFactory.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp b/src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp index fe2034ede..651b952e1 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtSmoothNormals.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.cpp b/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.cpp index fc3c61ec9..f611feccf 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h b/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h index 81694b529..e450a137d 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtSphericalJoint.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h b/src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h index 450382c73..1f51f0935 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h +++ b/src/PhysX/physx/source/physxextensions/src/ExtTaskQueueHelper.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp b/src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp index 6559d6346..832dd2aa0 100644 --- a/src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp +++ b/src/PhysX/physx/source/physxextensions/src/ExtTriangleMeshExt.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp index ecb87b4ca..223430a0e 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinaryDeserialization.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp index 4c4d279d2..f08829c20 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnBinarySerialization.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp index 10ca76946..9f64282cd 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. /* - get rid of STL diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h index 9b07a101c..b93325adc 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PX_CONVX_H #define PX_CONVX_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp index 45579ba90..8f2c423c3 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "SnConvX.h" #include "SnConvX_Align.h" diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.h index d43e5896d..e4c7be79b 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Align.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PX_CONVX_ALIGN_H #define PX_CONVX_ALIGN_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Common.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Common.h index b803fd30b..f17daebf5 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Common.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Common.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PX_CONVX_COMMON_H #define PX_CONVX_COMMON_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Convert.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Convert.cpp index 8f7fcb888..0d3729202 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Convert.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Convert.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "foundation/PxErrorCallback.h" #include "SnConvX.h" @@ -581,11 +581,11 @@ bool Sn::ConvX::convertClass(const char* buffer, const MetaClass* mc, int offset if(strcmp(srcEntry.entry.mName, "mNbHullVertices")==0) { assert(srcEntry.entry.mSize==1); - const int nbVerts = int(*(buffer+modSrcOffsetCheck)); + const PxU8 nbVerts = static_cast(*(buffer+modSrcOffsetCheck)); assert(!foundNbVerts); foundNbVerts = true; - const int gaussMapLimit = getBinaryMetaData(META_DATA_DST)->getGaussMapLimit(); + const PxU8 gaussMapLimit = static_cast(getBinaryMetaData(META_DATA_DST)->getGaussMapLimit()); if(nbVerts > gaussMapLimit) { // We need a gauss map and we have one => keep it diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp index f67666f88..92301e0e6 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Error.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "foundation/PxErrorCallback.h" #include "SnConvX.h" diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp index 64fbb942a..2d1446802 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "foundation/PxIO.h" #include "foundation/PxMemory.h" diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h index 8b1e5a1b6..84fe523ac 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_MetaData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PX_CONVX_METADATA_H #define PX_CONVX_METADATA_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp index d9fb8dada..d174a0183 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "foundation/PxIO.h" #include "foundation/PxErrorCallback.h" diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h index 18d03fc9f..7a6245322 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Output.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PX_CONVX_OUTPUT_H #define PX_CONVX_OUTPUT_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp index d8dc6bdb1..0fc1827cc 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "SnConvX.h" #include diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.h index e25eda6c3..701e8cf87 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnConvX_Union.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PX_UNION_H #define PX_UNION_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp index e7ed9d827..287db3453 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h index 624b50f79..20642b89d 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Binary/SnSerializationContext.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h b/src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h index a4a08373c..5297d6bac 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/File/SnFile.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp index a7c73ce78..c560902e0 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -38,7 +38,7 @@ using namespace physx; namespace { -#define SN_NUM_BINARY_PLATFORMS 13 +#define SN_NUM_BINARY_PLATFORMS 14 const PxU32 sBinaryPlatformTags[SN_NUM_BINARY_PLATFORMS] = { PX_MAKE_FOURCC('W','_','3','2'), @@ -53,7 +53,8 @@ const PxU32 sBinaryPlatformTags[SN_NUM_BINARY_PLATFORMS] = PX_MAKE_FOURCC('A','A','6','4'), PX_MAKE_FOURCC('X','O','N','E'), PX_MAKE_FOURCC('N','X','3','2'), - PX_MAKE_FOURCC('N','X','6','4') + PX_MAKE_FOURCC('N','X','6','4'), + PX_MAKE_FOURCC('L','A','6','4') }; const char* sBinaryPlatformNames[SN_NUM_BINARY_PLATFORMS] = @@ -62,15 +63,16 @@ const char* sBinaryPlatformNames[SN_NUM_BINARY_PLATFORMS] = "win64", "linux32", "linux64", - "macOSX32", - "macOSX64", + "mac32", + "mac64", "ps4", "android", "ios", "ios64", "xboxone", "switch32", - "switch64" + "switch64", + "linuxaarch64" }; #define SN_NUM_BINARY_COMPATIBLE_VERSIONS 1 @@ -93,9 +95,9 @@ PxU32 getBinaryPlatformTag() return sBinaryPlatformTags[0]; #elif PX_WINDOWS && PX_X64 return sBinaryPlatformTags[1]; -#elif PX_LINUX && (PX_X86 || PX_ARM) +#elif PX_LINUX && PX_X86 return sBinaryPlatformTags[2]; -#elif PX_LINUX && (PX_X64 || PX_A64) +#elif PX_LINUX && PX_X64 return sBinaryPlatformTags[3]; #elif PX_OSX && PX_X86 return sBinaryPlatformTags[4]; @@ -115,6 +117,8 @@ PxU32 getBinaryPlatformTag() return sBinaryPlatformTags[11]; #elif PX_SWITCH && PX_A64 return sBinaryPlatformTags[12]; +#elif PX_LINUX && PX_A64 + return sBinaryPlatformTags[13]; #else #error Unknown binary platform #endif diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.h b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.h index 63b6608df..4e8dcfbbc 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialUtils.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialization.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialization.cpp index 52a1f2816..1155859ed 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialization.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerialization.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.cpp index 1e7debbd3..bcec9b1a2 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h index 8c660d294..7fa10e106 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/SnSerializationRegistry.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp index 924740be6..feddfc439 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "PxMetaDataObjects.h" diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h index 3dd215690..6723fb3f3 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnJointRepXSerializer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef SN_JOINT_REPX_SERIALIZER_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h index fbc666854..36f12a966 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnPxStreamOperators.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_PXSTREAMOPERATORS_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h index 4c8cd665e..af0305b3a 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX1_0Defaults.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h index ed175fc4a..0d745a39a 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_1Defaults.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h index ac656bfad..415519b98 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepX3_2Defaults.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h index 56e2a4b0a..25d1ce99b 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCollection.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_REPXCOLLECTION_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp index eb0ab6e8c..ef5cc5c7a 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h index d04c46cd3..b06f24efa 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXCoreSerializer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef SN_REPX_CORE_SERIALIZER_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h index 95f7a3448..e5c092ae9 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXSerializerImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_REPX_SERIALIZER_IMPL_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp index 978e98b2e..6fd948385 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.h index b105b6a5d..205f8b7cc 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnRepXUpgrader.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_REPX_UPGRADER_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnSimpleXmlWriter.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnSimpleXmlWriter.h index 8d465483b..75657d04a 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnSimpleXmlWriter.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnSimpleXmlWriter.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_SIMPLEXMLWRITER_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h index cfad99207..7fdff2fd5 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlDeserializer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_DESERIALIZER_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h index 261ab313a..12eb246ad 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_IMPL_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h index a81e6141c..344bb1e74 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryAllocator.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_MEMORY_ALLOCATOR_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h index 537ffb5ad..c4446cdb7 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPool.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_MEMORYPOOL_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h index a1e65dabd..06865c64e 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlMemoryPoolStreams.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_MEMORY_POOL_STREAMS_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h index d5caa465b..907989725 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlReader.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_READER_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp index dc9f7a87c..4b3b5be74 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerialization.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "SnXmlImpl.h" @@ -595,7 +595,7 @@ namespace physx { namespace Sn { virtual void save( PxOutputStream& inStream ) { SimpleXmlWriterImpl theWriter( inStream, mAllocator.getAllocator() ); - theWriter.beginTag( "PhysX30Collection" ); + theWriter.beginTag( "PhysXCollection" ); theWriter.addAttribute( "version", mVersionStr ); { XmlWriterImpl theRepXWriter( &theWriter, &mPropertyBuffer ); diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h index caf71e286..a3f4a0f46 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSerializer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_SERIALIZER_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h index 3de9049eb..235e73b83 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlSimpleXmlWriter.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_SIMPLEXMLWRITER_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h index aa594d868..c4de3a85d 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlStringToType.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_STRINGTOTYPE_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h index 461be83b3..00bac567f 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorReader.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_VISITOR_READER_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h index 5caacaf2d..a26f2cd23 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlVisitorWriter.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_VISITOR_WRITER_H diff --git a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h index 666374573..56014a06f 100644 --- a/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h +++ b/src/PhysX/physx/source/physxextensions/src/serialization/Xml/SnXmlWriter.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_XML_WRITER_H diff --git a/src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h b/src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h index 57fb564d2..2c73f9d06 100644 --- a/src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h +++ b/src/PhysX/physx/source/physxgpu/include/PxPhysXGpu.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h index 27933da36..555c66711 100644 --- a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h +++ b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataDefineProperties.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h index 2e067ab49..9fb2728d9 100644 --- a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h +++ b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataExtensions.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h index 6509ef818..07eacc5a8 100644 --- a/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h +++ b/src/PhysX/physx/source/physxmetadata/core/include/PvdMetaDataPropertyVisitor.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h index f8c538f75..a6500291f 100644 --- a/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjectNames.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -334,6 +334,7 @@ PxSceneDesc_MaxNbContactDataBlocks, PxSceneDesc_MaxBiasCoefficient, PxSceneDesc_ContactReportStreamBufferSize, PxSceneDesc_CcdMaxPasses, +PxSceneDesc_CcdThreshold, PxSceneDesc_WakeCounterResetValue, PxSceneDesc_SanityBounds, PxSceneDesc_GpuDynamicsConfig, diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h index 2f8fd805a..5bfa3df56 100644 --- a/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxAutoGeneratedMetaDataObjects.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -110,6 +110,7 @@ template<> struct PxEnumTraits< physx::PxShapeFlag::Enum > { PxEnumTraits() : Na static PxU32ToName g_physx__PxMaterialFlag__EnumConversion[] = { { "eDISABLE_FRICTION", static_cast( physx::PxMaterialFlag::eDISABLE_FRICTION ) }, { "eDISABLE_STRONG_FRICTION", static_cast( physx::PxMaterialFlag::eDISABLE_STRONG_FRICTION ) }, + { "eIMPROVED_PATCH_FRICTION", static_cast( physx::PxMaterialFlag::eIMPROVED_PATCH_FRICTION ) }, { NULL, 0 } }; @@ -2563,6 +2564,7 @@ template<> struct PxEnumTraits< physx::PxSolverType::Enum > { PxEnumTraits() : N PxReal MaxBiasCoefficient; PxU32 ContactReportStreamBufferSize; PxU32 CcdMaxPasses; + PxReal CcdThreshold; PxReal WakeCounterResetValue; PxBounds3 SanityBounds; PxgDynamicsMemoryConfig GpuDynamicsConfig; @@ -2603,6 +2605,7 @@ template<> struct PxEnumTraits< physx::PxSolverType::Enum > { PxEnumTraits() : N DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, MaxBiasCoefficient, PxSceneDescGeneratedValues) DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, ContactReportStreamBufferSize, PxSceneDescGeneratedValues) DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, CcdMaxPasses, PxSceneDescGeneratedValues) + DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, CcdThreshold, PxSceneDescGeneratedValues) DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, WakeCounterResetValue, PxSceneDescGeneratedValues) DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, SanityBounds, PxSceneDescGeneratedValues) DEFINE_PROPERTY_TO_VALUE_STRUCT_MAP( PxSceneDesc, GpuDynamicsConfig, PxSceneDescGeneratedValues) @@ -2646,6 +2649,7 @@ template<> struct PxEnumTraits< physx::PxSolverType::Enum > { PxEnumTraits() : N PxPropertyInfo MaxBiasCoefficient; PxPropertyInfo ContactReportStreamBufferSize; PxPropertyInfo CcdMaxPasses; + PxPropertyInfo CcdThreshold; PxPropertyInfo WakeCounterResetValue; PxPropertyInfo SanityBounds; PxPropertyInfo GpuDynamicsConfig; @@ -2670,7 +2674,7 @@ template<> struct PxEnumTraits< physx::PxSolverType::Enum > { PxEnumTraits() : N PX_UNUSED(inStartIndex); return inStartIndex; } - static PxU32 instancePropertyCount() { return 39; } + static PxU32 instancePropertyCount() { return 40; } static PxU32 totalPropertyCount() { return instancePropertyCount(); } template PxU32 visitInstanceProperties( TOperator inOperator, PxU32 inStartIndex = 0 ) const @@ -2711,12 +2715,13 @@ template<> struct PxEnumTraits< physx::PxSolverType::Enum > { PxEnumTraits() : N inOperator( MaxBiasCoefficient, inStartIndex + 31 );; inOperator( ContactReportStreamBufferSize, inStartIndex + 32 );; inOperator( CcdMaxPasses, inStartIndex + 33 );; - inOperator( WakeCounterResetValue, inStartIndex + 34 );; - inOperator( SanityBounds, inStartIndex + 35 );; - inOperator( GpuDynamicsConfig, inStartIndex + 36 );; - inOperator( GpuMaxNumPartitions, inStartIndex + 37 );; - inOperator( GpuComputeVersion, inStartIndex + 38 );; - return 39 + inStartIndex; + inOperator( CcdThreshold, inStartIndex + 34 );; + inOperator( WakeCounterResetValue, inStartIndex + 35 );; + inOperator( SanityBounds, inStartIndex + 36 );; + inOperator( GpuDynamicsConfig, inStartIndex + 37 );; + inOperator( GpuMaxNumPartitions, inStartIndex + 38 );; + inOperator( GpuComputeVersion, inStartIndex + 39 );; + return 40 + inStartIndex; } }; template<> struct PxClassInfoTraits diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h index bdadf8d63..96533d5ce 100644 --- a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCompare.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h index c6190e35f..327eb339b 100644 --- a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataCppPrefix.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h index b4cc65bdf..8884a7693 100644 --- a/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h +++ b/src/PhysX/physx/source/physxmetadata/core/include/PxMetaDataObjects.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h b/src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h index 062300b3b..0f13cb617 100644 --- a/src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h +++ b/src/PhysX/physx/source/physxmetadata/core/include/RepXMetaDataPropertyVisitor.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp b/src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp index b23f1cf6f..b957290a4 100644 --- a/src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp +++ b/src/PhysX/physx/source/physxmetadata/core/src/PxAutoGeneratedMetaDataObjects.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -1046,6 +1046,8 @@ inline PxU32 getPxSceneDescContactReportStreamBufferSize( const PxSceneDesc* inO inline void setPxSceneDescContactReportStreamBufferSize( PxSceneDesc* inOwner, PxU32 inData) { inOwner->contactReportStreamBufferSize = inData; } inline PxU32 getPxSceneDescCcdMaxPasses( const PxSceneDesc* inOwner ) { return inOwner->ccdMaxPasses; } inline void setPxSceneDescCcdMaxPasses( PxSceneDesc* inOwner, PxU32 inData) { inOwner->ccdMaxPasses = inData; } +inline PxReal getPxSceneDescCcdThreshold( const PxSceneDesc* inOwner ) { return inOwner->ccdThreshold; } +inline void setPxSceneDescCcdThreshold( PxSceneDesc* inOwner, PxReal inData) { inOwner->ccdThreshold = inData; } inline PxReal getPxSceneDescWakeCounterResetValue( const PxSceneDesc* inOwner ) { return inOwner->wakeCounterResetValue; } inline void setPxSceneDescWakeCounterResetValue( PxSceneDesc* inOwner, PxReal inData) { inOwner->wakeCounterResetValue = inData; } inline PxBounds3 getPxSceneDescSanityBounds( const PxSceneDesc* inOwner ) { return inOwner->sanityBounds; } @@ -1091,6 +1093,7 @@ PX_PHYSX_CORE_API PxSceneDescGeneratedInfo::PxSceneDescGeneratedInfo() , MaxBiasCoefficient( "MaxBiasCoefficient", setPxSceneDescMaxBiasCoefficient, getPxSceneDescMaxBiasCoefficient ) , ContactReportStreamBufferSize( "ContactReportStreamBufferSize", setPxSceneDescContactReportStreamBufferSize, getPxSceneDescContactReportStreamBufferSize ) , CcdMaxPasses( "CcdMaxPasses", setPxSceneDescCcdMaxPasses, getPxSceneDescCcdMaxPasses ) + , CcdThreshold( "CcdThreshold", setPxSceneDescCcdThreshold, getPxSceneDescCcdThreshold ) , WakeCounterResetValue( "WakeCounterResetValue", setPxSceneDescWakeCounterResetValue, getPxSceneDescWakeCounterResetValue ) , SanityBounds( "SanityBounds", setPxSceneDescSanityBounds, getPxSceneDescSanityBounds ) , GpuDynamicsConfig( "GpuDynamicsConfig", setPxSceneDescGpuDynamicsConfig, getPxSceneDescGpuDynamicsConfig ) @@ -1131,6 +1134,7 @@ PX_PHYSX_CORE_API PxSceneDescGeneratedValues::PxSceneDescGeneratedValues( const ,MaxBiasCoefficient( inSource->maxBiasCoefficient ) ,ContactReportStreamBufferSize( inSource->contactReportStreamBufferSize ) ,CcdMaxPasses( inSource->ccdMaxPasses ) + ,CcdThreshold( inSource->ccdThreshold ) ,WakeCounterResetValue( inSource->wakeCounterResetValue ) ,SanityBounds( inSource->sanityBounds ) ,GpuDynamicsConfig( inSource->gpuDynamicsConfig ) diff --git a/src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp b/src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp index 2717e54ae..673de8d66 100644 --- a/src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp +++ b/src/PhysX/physx/source/physxmetadata/core/src/PxMetaDataObjects.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h index 1de957676..2cf4c6596 100644 --- a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h +++ b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjectNames.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h index ed59c9e9f..ee7517986 100644 --- a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h +++ b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionAutoGeneratedMetaDataObjects.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h index 12be4126c..4450e7dff 100644 --- a/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h +++ b/src/PhysX/physx/source/physxmetadata/extensions/include/PxExtensionMetaDataObjects.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp b/src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp index 3a9f74b1d..66b555161 100644 --- a/src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp +++ b/src/PhysX/physx/source/physxmetadata/extensions/src/PxExtensionAutoGeneratedMetaDataObjects.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp index 12f67eab3..57c2fe917 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleComponents.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h index d823f380a..8d1f9ff85 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDefaults.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp index e92047d90..9f0357e42 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive4W.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive4W.cpp index 7bd0ad2b2..081a48c27 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive4W.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDrive4W.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp index 8ca91c6f9..4fe5162e2 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveNW.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp index 30cb281d8..fcfa6a89e 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleDriveTank.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h index 84c08f726..00f6eb3fe 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleLinearMath.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp index 172b5f123..e67b6df21 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleMetaData.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp index a8a87f5fb..a01c814ab 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleNoDrive.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp index 18310f07e..e158c1aa1 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSDK.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp index dbb616c9b..276947e80 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h index 8e5914887..4008869e5 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSerialization.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h index d8864054d..484dc3bf1 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspLimitConstraintShader.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp index 61b61f176..2fe5f22a9 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h index 776bb345d..794927be8 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleSuspWheelTire4.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp index 57a5df790..78b69adba 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleTireFriction.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleUpdate.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleUpdate.cpp index ebf51924a..571038e56 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleUpdate.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleUpdate.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp b/src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp index 1b2a67be3..fe08e5ebf 100644 --- a/src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/PxVehicleWheels.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp index 380ae49b8..a43ca62a0 100644 --- a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilControl.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp index 32604c924..a34b73b81 100644 --- a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilSetup.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp index 459d26a2f..47c1e3e33 100644 --- a/src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/VehicleUtilTelemetry.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjectNames.h b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjectNames.h index 92096b073..f644c0eb9 100644 --- a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjectNames.h +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjectNames.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h index 22275e592..101b12fb4 100644 --- a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleAutoGeneratedMetaDataObjects.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h index 35d2b4760..e73866807 100644 --- a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/include/PxVehicleMetaDataObjects.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp index 7056b9989..0d967be4f 100644 --- a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleAutoGeneratedMetaDataObjects.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp index b8b2bf691..a15d027e2 100644 --- a/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp +++ b/src/PhysX/physx/source/physxvehicle/src/physxmetadata/src/PxVehicleMetaDataObjects.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/include/PsPvd.h b/src/PhysX/physx/source/pvd/include/PsPvd.h index 62b476bd2..02bfa7c91 100644 --- a/src/PhysX/physx/source/pvd/include/PsPvd.h +++ b/src/PhysX/physx/source/pvd/include/PsPvd.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h b/src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h index 160ddded4..b06e9d195 100644 --- a/src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h +++ b/src/PhysX/physx/source/pvd/include/PxProfileAllocatorWrapper.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/include/PxPvdClient.h b/src/PhysX/physx/source/pvd/include/PxPvdClient.h index 6ba6a3011..4eec5a56d 100644 --- a/src/PhysX/physx/source/pvd/include/PxPvdClient.h +++ b/src/PhysX/physx/source/pvd/include/PxPvdClient.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/include/PxPvdDataStream.h b/src/PhysX/physx/source/pvd/include/PxPvdDataStream.h index c16d35b46..4128324de 100644 --- a/src/PhysX/physx/source/pvd/include/PxPvdDataStream.h +++ b/src/PhysX/physx/source/pvd/include/PxPvdDataStream.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDDATASTREAM_H #define PXPVDSDK_PXPVDDATASTREAM_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h b/src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h index 503491b0c..e46ef70e5 100644 --- a/src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h +++ b/src/PhysX/physx/source/pvd/include/PxPvdDataStreamHelpers.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDDATASTREAMHELPERS_H #define PXPVDSDK_PXPVDDATASTREAMHELPERS_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h b/src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h index 8865b6aa6..59ea52cbe 100644 --- a/src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h +++ b/src/PhysX/physx/source/pvd/include/PxPvdErrorCodes.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDERRORCODES_H #define PXPVDSDK_PXPVDERRORCODES_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h b/src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h index 66384a726..1d8bf022a 100644 --- a/src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h +++ b/src/PhysX/physx/source/pvd/include/PxPvdObjectModelBaseTypes.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDOBJECTMODELBASETYPES_H #define PXPVDSDK_PXPVDOBJECTMODELBASETYPES_H diff --git a/src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h b/src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h index ef851c596..d86cf3410 100644 --- a/src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h +++ b/src/PhysX/physx/source/pvd/include/PxPvdRenderBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h b/src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h index 9598fe410..11269756b 100644 --- a/src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h +++ b/src/PhysX/physx/source/pvd/include/PxPvdUserRenderer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDUSERRENDERER_H #define PXPVDSDK_PXPVDUSERRENDERER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h b/src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h index 5ac872652..7b04bb944 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileContextProvider.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILECONTEXTPROVIDER_H #define PXPVDSDK_PXPROFILECONTEXTPROVIDER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h b/src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h index fdf68ec72..7ac44f58e 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileContextProviderImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILECONTEXTPROVIDERIMPL_H #define PXPVDSDK_PXPROFILECONTEXTPROVIDERIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h index 9b8d4c0cf..998ae05cf 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileDataBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h b/src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h index a3e87860b..8d5a8c245 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileDataParsing.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h index 00c82db80..10f6414ad 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h index 0d252f23e..f965ec28b 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferAtomic.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h index 869fdbd36..970209926 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClient.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILEEVENTBUFFERCLIENT_H #define PXPVDSDK_PXPROFILEEVENTBUFFERCLIENT_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h index 86ed6fb24..3f822aca8 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventBufferClientManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILEEVENTBUFFERCLIENTMANAGER_H #define PXPVDSDK_PXPROFILEEVENTBUFFERCLIENTMANAGER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventId.h b/src/PhysX/physx/source/pvd/src/PxProfileEventId.h index 260abd55d..21711dbba 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEventId.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventId.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILEEVENTID_H #define PXPVDSDK_PXPROFILEEVENTID_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp b/src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp index 8b037e8e2..9f18b4631 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventImpl.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "PxProfileEventBuffer.h" #include "PxProfileZoneImpl.h" diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h b/src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h index 287cb57e2..fa58765a0 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventMutex.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILEEVENTMUTEX_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventNames.h b/src/PhysX/physx/source/pvd/src/PxProfileEventNames.h index 508252b9e..c7b6e6a44 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEventNames.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventNames.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILEEVENTNAMES_H #define PXPVDSDK_PXPROFILEEVENTNAMES_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventSender.h b/src/PhysX/physx/source/pvd/src/PxProfileEventSender.h index ee80096b6..effb0463b 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEventSender.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventSender.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILEEVENTSENDER_H #define PXPVDSDK_PXPROFILEEVENTSENDER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h b/src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h index ca15a019e..17f59496f 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileEventSerialization.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileEvents.h b/src/PhysX/physx/source/pvd/src/PxProfileEvents.h index 9bf02ed8c..69f340d8c 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileEvents.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileEvents.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILEEVENTS_H #define PXPVDSDK_PXPROFILEEVENTS_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemory.h b/src/PhysX/physx/source/pvd/src/PxProfileMemory.h index 604d5091e..0f69777d3 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileMemory.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemory.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h index ca986aad7..39b01b489 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemoryBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h index e3784f5ae..1a5804147 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEventBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h index 4a206c70d..35bedb05b 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileMemoryEvents.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h b/src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h index fe53314dd..68c89fa85 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileScopedEvent.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILESCOPEDEVENT_H #define PXPVDSDK_PXPROFILESCOPEDEVENT_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h b/src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h index dac1e4cb0..2f65b50e0 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileScopedMutexLock.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h b/src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h index cb768367b..b0e8ec799 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileZoneImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h b/src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h index 5a1f37076..632545588 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileZoneManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPROFILEZONEMANAGER_H #define PXPVDSDK_PXPROFILEZONEMANAGER_H diff --git a/src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h b/src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h index a336a763c..163ba36b9 100644 --- a/src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h +++ b/src/PhysX/physx/source/pvd/src/PxProfileZoneManagerImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvd.cpp b/src/PhysX/physx/source/pvd/src/PxPvd.cpp index 7873e373a..eea1c05c7 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvd.cpp +++ b/src/PhysX/physx/source/pvd/src/PxPvd.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdBits.h b/src/PhysX/physx/source/pvd/src/PxPvdBits.h index 2d2b93a7a..f0e6035d3 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdBits.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdBits.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDBITS_H #define PXPVDSDK_PXPVDBITS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h b/src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h index 0a3d3fccc..e5dd84ee6 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdByteStreams.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDBYTESTREAMS_H #define PXPVDSDK_PXPVDBYTESTREAMS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h index d0b843680..7f66f98ac 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEventSink.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDCOMMSTREAMEVENTSINK_H #define PXPVDSDK_PXPVDCOMMSTREAMEVENTSINK_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h index 89361be66..b592361bb 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamEvents.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDCOMMSTREAMEVENTS_H #define PXPVDSDK_PXPVDCOMMSTREAMEVENTS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h index b57075086..3799dc1ad 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdCommStreamTypes.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDCOMMSTREAMTYPES_H #define PXPVDSDK_PXPVDCOMMSTREAMTYPES_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp b/src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp index 5195c7d44..fdf61527f 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp +++ b/src/PhysX/physx/source/pvd/src/PxPvdDataStream.cpp @@ -23,8 +23,9 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. +#include "foundation/PxAssert.h" #include "PxPvdCommStreamEventSink.h" #include "PxPvdDataStreamHelpers.h" #include "PxPvdObjectModelInternalTypes.h" diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp index ba939b045..4a1a71e48 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h index e64b4a0a7..a437120d8 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultFileTransport.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp index 3ed5d03d7..0b446e06d 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h index d0d7d4b2a..2b18a25f7 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdDefaultSocketTransport.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdFoundation.h b/src/PhysX/physx/source/pvd/src/PxPvdFoundation.h index 52feeee57..db880eec8 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdFoundation.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdFoundation.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDFOUNDATION_H #define PXPVDSDK_PXPVDFOUNDATION_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp b/src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp index d6374584e..26929e8d0 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp +++ b/src/PhysX/physx/source/pvd/src/PxPvdImpl.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdImpl.h b/src/PhysX/physx/source/pvd/src/PxPvdImpl.h index ab19343d9..cf1fcf2f5 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdImpl.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h b/src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h index 71bdcedd1..a83dc1504 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdInternalByteStreams.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDINTERNALBYTESTREAMS_H #define PXPVDSDK_PXPVDINTERNALBYTESTREAMS_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h b/src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h index 8c80ddc27..b27aa1cb6 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdMarshalling.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDMARSHALLING_H #define PXPVDSDK_PXPVDMARSHALLING_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp index f6e201982..de1bb9c7a 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp +++ b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdMemClient.h b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.h index de4c202e8..1fd647156 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdMemClient.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdMemClient.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h index b273ca0f6..c1fb5b76a 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypeDefs.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h index bf9b6870b..316a492b9 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelInternalTypes.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDOBJECTMODELINTERNALTYPES_H #define PXPVDSDK_PXPVDOBJECTMODELINTERNALTYPES_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp index 6d5635240..bc4d41015 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "PxPvdObjectModelInternalTypes.h" #include "PxPvdObjectModelMetaData.h" diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h index eb2baea83..56ea53501 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectModelMetaData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDOBJECTMODELMETADATA_H #define PXPVDSDK_PXPVDOBJECTMODELMETADATA_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp index 3bdfaaf7f..a0b8c34f1 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h index 5cf0e8549..59c41446d 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdObjectRegistrar.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h b/src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h index 555b64acd..238f52151 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdProfileZone.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDPROFILEZONE_H #define PXPVDSDK_PXPVDPROFILEZONE_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp index ce22db789..1e3c10004 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp +++ b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h index 4438b8101..0d9c00727 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdProfileZoneClient.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h index 70b858400..636fa511c 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderImpl.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXPVDSDK_PXPVDUSERRENDERIMPL_H #define PXPVDSDK_PXPVDUSERRENDERIMPL_H diff --git a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h index f313e66f0..de92be231 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h +++ b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderTypes.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #define THERE_IS_NO_INCLUDE_GUARD_HERE_FOR_A_REASON #ifndef DECLARE_PVD_IMMEDIATE_RENDER_TYPE_NO_COMMA diff --git a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp index 98c12eba9..7f1ad66d6 100644 --- a/src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp +++ b/src/PhysX/physx/source/pvd/src/PxPvdUserRenderer.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "PxPvdUserRenderImpl.h" #include "PxPvdInternalByteStreams.h" diff --git a/src/PhysX/physx/source/scenequery/include/SqPruner.h b/src/PhysX/physx/source/scenequery/include/SqPruner.h index 0a42b2e6b..0cb09cde5 100644 --- a/src/PhysX/physx/source/scenequery/include/SqPruner.h +++ b/src/PhysX/physx/source/scenequery/include/SqPruner.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h b/src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h index 5e1a9e0a5..9050d460c 100644 --- a/src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h +++ b/src/PhysX/physx/source/scenequery/include/SqPrunerMergeData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/include/SqPruningStructure.h b/src/PhysX/physx/source/scenequery/include/SqPruningStructure.h index 259a28daa..b3bc07f7c 100644 --- a/src/PhysX/physx/source/scenequery/include/SqPruningStructure.h +++ b/src/PhysX/physx/source/scenequery/include/SqPruningStructure.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h b/src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h index 977eb4092..df62add25 100644 --- a/src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h +++ b/src/PhysX/physx/source/scenequery/include/SqSceneQueryManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp index f61611555..116bd1017 100644 --- a/src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBPruner.h b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.h index f575443db..f60c5d596 100644 --- a/src/PhysX/physx/source/scenequery/src/SqAABBPruner.h +++ b/src/PhysX/physx/source/scenequery/src/SqAABBPruner.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp b/src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp index 8faec8104..a0c4bba6d 100644 --- a/src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqAABBTree.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBTree.h b/src/PhysX/physx/source/scenequery/src/SqAABBTree.h index bde284d65..7480e16ec 100644 --- a/src/PhysX/physx/source/scenequery/src/SqAABBTree.h +++ b/src/PhysX/physx/source/scenequery/src/SqAABBTree.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp b/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp index e7512aaa6..019536464 100644 --- a/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.h b/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.h index 7d1879c92..1ea5401b8 100644 --- a/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.h +++ b/src/PhysX/physx/source/scenequery/src/SqAABBTreeUpdateMap.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqBounds.cpp b/src/PhysX/physx/source/scenequery/src/SqBounds.cpp index a0f9cc0fa..a8489d6ec 100644 --- a/src/PhysX/physx/source/scenequery/src/SqBounds.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqBounds.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqBounds.h b/src/PhysX/physx/source/scenequery/src/SqBounds.h index 592d13098..94c1297f5 100644 --- a/src/PhysX/physx/source/scenequery/src/SqBounds.h +++ b/src/PhysX/physx/source/scenequery/src/SqBounds.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp index b36d60432..ac5328209 100644 --- a/src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqBucketPruner.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqBucketPruner.h b/src/PhysX/physx/source/scenequery/src/SqBucketPruner.h index e117500be..3fa517303 100644 --- a/src/PhysX/physx/source/scenequery/src/SqBucketPruner.h +++ b/src/PhysX/physx/source/scenequery/src/SqBucketPruner.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp index 214633c7b..425ad3774 100644 --- a/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h index 2c2663f62..4038e2acc 100644 --- a/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruner.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp index be67e1a9e..93bb761df 100644 --- a/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h index 059273e02..a55077c8e 100644 --- a/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h +++ b/src/PhysX/physx/source/scenequery/src/SqCompoundPruningPool.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp index 136652d58..723c28cbd 100644 --- a/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h index 8ebea6d66..e96ebc451 100644 --- a/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h +++ b/src/PhysX/physx/source/scenequery/src/SqExtendedBucketPruner.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp index 1a90b2b9a..ad10d2d05 100644 --- a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h index bb29d63d0..42f705667 100644 --- a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPruner.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp index cf9e17420..9c41c8245 100644 --- a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h index 4308a5e02..247df19d3 100644 --- a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBPrunerCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp index a1d1fd43b..82be0ab19 100644 --- a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h index 6f5602739..149e3983d 100644 --- a/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h +++ b/src/PhysX/physx/source/scenequery/src/SqIncrementalAABBTree.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqMetaData.cpp b/src/PhysX/physx/source/scenequery/src/SqMetaData.cpp index e5a57940f..56a882c05 100644 --- a/src/PhysX/physx/source/scenequery/src/SqMetaData.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqMetaData.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp b/src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp index 327d5ebf0..ecf612b8c 100644 --- a/src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqPruningPool.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqPruningPool.h b/src/PhysX/physx/source/scenequery/src/SqPruningPool.h index 9886f8a23..dbca5480b 100644 --- a/src/PhysX/physx/source/scenequery/src/SqPruningPool.h +++ b/src/PhysX/physx/source/scenequery/src/SqPruningPool.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqPruningStructure.cpp b/src/PhysX/physx/source/scenequery/src/SqPruningStructure.cpp index 2d4efc342..a300d932b 100644 --- a/src/PhysX/physx/source/scenequery/src/SqPruningStructure.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqPruningStructure.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp b/src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp index 9449ac4c1..28909db85 100644 --- a/src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp +++ b/src/PhysX/physx/source/scenequery/src/SqSceneQueryManager.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/scenequery/src/SqTypedef.h b/src/PhysX/physx/source/scenequery/src/SqTypedef.h index 0bcb5e0f1..a0da0c1b2 100644 --- a/src/PhysX/physx/source/scenequery/src/SqTypedef.h +++ b/src/PhysX/physx/source/scenequery/src/SqTypedef.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h index 3dc4f4101..560a6ff0d 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScActorCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h index 5f02cfeb7..888f649d0 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -45,7 +45,6 @@ namespace IG class NodeIndex; } - namespace Sc { typedef Dy::FsData ArticulationDriveCache; @@ -78,39 +77,38 @@ namespace Sc //--------------------------------------------------------------------------------- // External API //--------------------------------------------------------------------------------- - PxU32 getInternalDriveIterations() const; - void setInternalDriveIterations(const PxU32 v); + PX_FORCE_INLINE PxU32 getInternalDriveIterations() const { return mCore.internalDriveIterations; } + PX_FORCE_INLINE void setInternalDriveIterations(const PxU32 v) { mCore.internalDriveIterations = v; } - PxU32 getExternalDriveIterations() const; - void setExternalDriveIterations(const PxU32 v); + PX_FORCE_INLINE PxU32 getExternalDriveIterations() const { return mCore.externalDriveIterations; } + PX_FORCE_INLINE void setExternalDriveIterations(const PxU32 v) { mCore.externalDriveIterations = v; } - PxU32 getMaxProjectionIterations() const; - void setMaxProjectionIterations(const PxU32 v); + PX_FORCE_INLINE PxU32 getMaxProjectionIterations() const { return mCore.maxProjectionIterations; } + PX_FORCE_INLINE void setMaxProjectionIterations(const PxU32 v) { mCore.maxProjectionIterations = v; } - PxReal getSeparationTolerance() const; - void setSeparationTolerance(const PxReal v); + PX_FORCE_INLINE PxReal getSeparationTolerance() const { return mCore.separationTolerance; } + PX_FORCE_INLINE void setSeparationTolerance(const PxReal v) { mCore.separationTolerance = v; } - PxReal getSleepThreshold() const; - void setSleepThreshold(const PxReal v); + PX_FORCE_INLINE PxReal getSleepThreshold() const { return mCore.sleepThreshold; } + PX_FORCE_INLINE void setSleepThreshold(const PxReal v) { mCore.sleepThreshold = v; } - PxReal getFreezeThreshold() const; - void setFreezeThreshold(const PxReal v); + PX_FORCE_INLINE PxReal getFreezeThreshold() const { return mCore.freezeThreshold; } + PX_FORCE_INLINE void setFreezeThreshold(const PxReal v) { mCore.freezeThreshold = v; } - PxReal getWakeCounter() const; + PX_FORCE_INLINE PxU16 getSolverIterationCounts() const { return mCore.solverIterationCounts; } + PX_FORCE_INLINE void setSolverIterationCounts(PxU16 c) { mCore.solverIterationCounts = c; } + + PX_FORCE_INLINE PxReal getWakeCounter() const { return mCore.wakeCounter; } + PX_FORCE_INLINE void setWakeCounterInternal(const PxReal v) { mCore.wakeCounter = v; } void setWakeCounter(const PxReal v); - void setWakeCounterInternal(const PxReal v); bool isSleeping() const; void wakeUp(PxReal wakeCounter); void putToSleep(); - PxU16 getSolverIterationCounts() const; - void setSolverIterationCounts(PxU16 c); - PxArticulation* getPxArticulation(); const PxArticulation* getPxArticulation() const; - //--------------------------------------------------------------------------------- // Drive Cache API //--------------------------------------------------------------------------------- @@ -196,30 +194,25 @@ namespace Sc PX_FORCE_INLINE const Dy::ArticulationCore& getCore() { return mCore; } static PX_FORCE_INLINE ArticulationCore& getArticulationCore(ArticulationCore& core) - { - size_t offset = PX_OFFSET_OF(ArticulationCore, mCore); - return *reinterpret_cast(reinterpret_cast(&core) - offset); - } + { + const size_t offset = PX_OFFSET_OF(ArticulationCore, mCore); + return *reinterpret_cast(reinterpret_cast(&core) - offset); + } - PX_INLINE PxArticulationBase::Enum getArticulationType() const { return PxArticulationBase::Enum(mType); } - PX_INLINE void setArticulationType(PxArticulationBase::Enum type) { mType = type; } + PX_INLINE PxArticulationBase::Enum getArticulationType() const { return PxArticulationBase::Enum(mType); } + PX_INLINE void setArticulationType(PxArticulationBase::Enum type) { mType = type; } + IG::NodeIndex getIslandNodeIndex() const; - IG::NodeIndex getIslandNodeIndex() const; - - void setGlobalPose(); - - void setDirty(const bool dirty); - + void setGlobalPose(); + void setDirty(const bool dirty); private: - ArticulationSim* mSim; - Dy::ArticulationCore mCore; - PxU32 mType; + ArticulationSim* mSim; + Dy::ArticulationCore mCore; + PxU32 mType; }; - - } // namespace Sc } diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h index fbb96b8d0..6f4115a6f 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScArticulationJointCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h index f8592b67f..c643ca8dd 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScBodyCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -71,7 +71,9 @@ namespace Sc BodyCore(const PxEMPTY) : RigidCore(PxEmpty), mCore(PxEmpty), mSimStateData(NULL) {} static void getBinaryMetaData(PxOutputStream& stream); void disableInternalCaching(bool disable); - size_t getSerialCore(PxsBodyCore& serialCore); + void restoreDynamicData(); + void backupDynamicData(); + //~PX_SERIALIZATION BodyCore(PxActorType::Enum type, const PxTransform& bodyPose); /*virtual*/ ~BodyCore(); diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h index 5520b6497..484703ed3 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScConstraintCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScIterators.h b/src/PhysX/physx/source/simulationcontroller/include/ScIterators.h index 30905476f..d8b77350c 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScIterators.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScIterators.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h index 16d033448..2279279c9 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScMaterialCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h b/src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h index 465f107fd..c6a61606e 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScPhysics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h index 732b9f97b..5f7a68c0b 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScRigidCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScScene.h b/src/PhysX/physx/source/simulationcontroller/include/ScScene.h index 912b2c897..95bc4d58f 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScScene.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScScene.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -317,8 +317,8 @@ namespace Sc PxU32 getCCDMaxPasses() const; // Broad-phase callback - void setBroadPhaseCallback(PxBroadPhaseCallback* callback); - PxBroadPhaseCallback* getBroadPhaseCallback() const; + PX_FORCE_INLINE void setBroadPhaseCallback(PxBroadPhaseCallback* callback) { mBroadPhaseCallback = callback; } + PX_FORCE_INLINE PxBroadPhaseCallback* getBroadPhaseCallback() const { return mBroadPhaseCallback; } // Broad-phase management void finishBroadPhase(PxBaseTask* continuation); @@ -662,7 +662,6 @@ namespace Sc PX_FORCE_INLINE void wakeObjectsUp(PxU32 infoFlag); void collectPostSolverVelocitiesBeforeCCD(); - void updateFromVisualizationParameters(); void clearSleepWakeBodies(void); PX_INLINE void cleanUpSleepBodies(); diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h index ce52fa86f..700cc3a9a 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScShapeCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h b/src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h index 198488be4..0287f6428 100644 --- a/src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h +++ b/src/PhysX/physx/source/simulationcontroller/include/ScStaticCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp index b6ac47aeb..119070ced 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorCore.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h b/src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h index 1d8b26fef..daf3044aa 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorPair.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp index a58394641..f2254f8f5 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h index 578695629..b793701c3 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScActorSim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp index 0f73f27bb..195bf8bcc 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationCore.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -47,75 +47,23 @@ Sc::ArticulationCore::ArticulationCore() : mCore.internalDriveIterations = 4; mCore.externalDriveIterations = 4; mCore.maxProjectionIterations = 4; - mCore.separationTolerance = 0.1f * scale.length; mCore.solverIterationCounts = 1<<8 | 4; + mCore.separationTolerance = 0.1f * scale.length; mCore.sleepThreshold = 5e-5f * scale.speed * scale.speed; - mCore.wakeCounter = Physics::sWakeCounterOnCreation; mCore.freezeThreshold = 5e-6f * scale.speed * scale.speed; + mCore.wakeCounter = Physics::sWakeCounterOnCreation; } - Sc::ArticulationCore::~ArticulationCore() { } - //-------------------------------------------------------------- // // ArticulationCore interface implementation // //-------------------------------------------------------------- -PxU32 Sc::ArticulationCore::getInternalDriveIterations() const -{ - return mCore.internalDriveIterations; -} - -void Sc::ArticulationCore::setInternalDriveIterations(const PxU32 v) -{ - mCore.internalDriveIterations = v; -} - -PxU32 Sc::ArticulationCore::getExternalDriveIterations() const -{ - return mCore.externalDriveIterations; -} - -void Sc::ArticulationCore::setExternalDriveIterations(const PxU32 v) -{ - mCore.externalDriveIterations = v; -} - -PxU32 Sc::ArticulationCore::getMaxProjectionIterations() const -{ - return mCore.maxProjectionIterations; -} - -void Sc::ArticulationCore::setMaxProjectionIterations(const PxU32 v) -{ - mCore.maxProjectionIterations = v; -} - -PxReal Sc::ArticulationCore::getSeparationTolerance() const -{ - return mCore.separationTolerance; -} - -void Sc::ArticulationCore::setSeparationTolerance(const PxReal v) -{ - mCore.separationTolerance = v; -} - -PxReal Sc::ArticulationCore::getWakeCounter() const -{ - return mCore.wakeCounter; -} - -void Sc::ArticulationCore::setWakeCounterInternal(const PxReal v) -{ - mCore.wakeCounter = v; -} - void Sc::ArticulationCore::setWakeCounter(const PxReal v) { mCore.wakeCounter = v; @@ -151,43 +99,11 @@ void Sc::ArticulationCore::putToSleep() #endif } -PxReal Sc::ArticulationCore::getSleepThreshold() const -{ - return mCore.sleepThreshold; -} - -void Sc::ArticulationCore::setSleepThreshold(const PxReal v) -{ - mCore.sleepThreshold = v; -} - -PxReal Sc::ArticulationCore::getFreezeThreshold() const -{ - return mCore.freezeThreshold; -} - -void Sc::ArticulationCore::setFreezeThreshold(const PxReal v) -{ - mCore.freezeThreshold = v; -} - -PxU16 Sc::ArticulationCore::getSolverIterationCounts() const -{ - return mCore.solverIterationCounts; -} - -void Sc::ArticulationCore::setSolverIterationCounts(const PxU16 v) -{ - mCore.solverIterationCounts = v; -} - - PxArticulation* Sc::ArticulationCore::getPxArticulation() { return gOffsetTable.convertScArticulation2Px(this); } - const PxArticulation* Sc::ArticulationCore::getPxArticulation() const { return gOffsetTable.convertScArticulation2Px(this); @@ -199,7 +115,6 @@ Sc::ArticulationDriveCache* Sc::ArticulationCore::createDriveCache(PxReal compli return mSim? mSim->createDriveCache(compliance, driveIterations) : NULL; } - void Sc::ArticulationCore::updateDriveCache(ArticulationDriveCache& cache, PxReal compliance, PxU32 driveIterations) const @@ -207,14 +122,12 @@ void Sc::ArticulationCore::updateDriveCache(ArticulationDriveCache& cache, mSim->updateDriveCache(cache, compliance, driveIterations); } - void Sc::ArticulationCore::releaseDriveCache(Sc::ArticulationDriveCache& driveCache) const { if(mSim) mSim->releaseDriveCache(driveCache); } - PxU32 Sc::ArticulationCore::getCacheLinkCount(const ArticulationDriveCache& cache) const { return Dy::PxvArticulationDriveCache::getLinkCount(cache); @@ -333,7 +246,6 @@ void Sc::ArticulationCore::computeKinematicJacobian(const PxU32 linkID, PxArticu mSim->computeKinematicJacobian(linkID, cache); } - void Sc::ArticulationCore::computeCoefficentMatrix(PxArticulationCache& cache) const { if (mSim) @@ -343,9 +255,7 @@ void Sc::ArticulationCore::computeCoefficentMatrix(PxArticulationCache& cache) c bool Sc::ArticulationCore::computeLambda(PxArticulationCache& cache, PxArticulationCache& initialState, const PxReal* const jointTorque, const PxVec3 gravity, const PxU32 maxIter) const { if (mSim) - { return mSim->computeLambda(cache, initialState, jointTorque, gravity, maxIter); - } return false; } diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp index f9a01b3b9..60400babb 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointCore.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp index a0127f707..bba62b3a1 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h index 7924b3c18..014dd7923 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationJointSim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp index d89b6ce2e..20404afdf 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -48,15 +48,14 @@ using namespace physx; using namespace physx::Dy; - Sc::ArticulationSim::ArticulationSim(ArticulationCore& core, Scene& scene, BodyCore& root) : - mLLArticulation(NULL), - mScene(scene), - mCore(core), - mLinks (PX_DEBUG_EXP("ScArticulationSim::links")), - mBodies (PX_DEBUG_EXP("ScArticulationSim::bodies")), - mJoints (PX_DEBUG_EXP("ScArticulationSim::joints")), - mMaxDepth(0) + mLLArticulation (NULL), + mScene (scene), + mCore (core), + mLinks (PX_DEBUG_EXP("ScArticulationSim::links")), + mBodies (PX_DEBUG_EXP("ScArticulationSim::bodies")), + mJoints (PX_DEBUG_EXP("ScArticulationSim::joints")), + mMaxDepth (0) { mLinks.reserve(16); mJoints.reserve(16); @@ -64,7 +63,6 @@ Sc::ArticulationSim::ArticulationSim(ArticulationCore& core, Scene& scene, BodyC mLLArticulation = mScene.createLLArticulation(this); - mIslandNodeIndex = scene.getSimpleIslandManager()->addArticulation(this, mLLArticulation, false); if(!mLLArticulation) @@ -79,8 +77,6 @@ Sc::ArticulationSim::ArticulationSim(ArticulationCore& core, Scene& scene, BodyC addBody(*root.getSim(), NULL, NULL); - - mCore.setSim(this); mLLArticulation->setDyContext(mScene.getDynamicsContext()); @@ -100,7 +96,6 @@ Sc::ArticulationSim::ArticulationSim(ArticulationCore& core, Scene& scene, BodyC //mLLArticulation->onUpdateSolverDesc(); } - Sc::ArticulationSim::~ArticulationSim() { if (!mLLArticulation) @@ -194,20 +189,18 @@ ArticulationLinkHandle Sc::ArticulationSim::getLinkHandle(BodySim &body) const return reinterpret_cast(mLLArticulation) | findBodyIndex(body); } -void Sc::ArticulationSim::addBody(BodySim& body, - BodySim* parent, - ArticulationJointSim* joint) +void Sc::ArticulationSim::addBody(BodySim& body, BodySim* parent, ArticulationJointSim* joint) { mBodies.pushBack(&body); mJoints.pushBack(joint); mLLArticulation->addBody(); - PxU32 index = mLinks.size(); + const PxU32 index = mLinks.size(); PX_ASSERT((((index==0) && (joint == 0)) && (parent == 0)) || (((index!=0) && joint) && (parent && (parent->getArticulation() == this)))); - ArticulationLink &link = mLinks.insert(); + ArticulationLink& link = mLinks.insert(); link.body = &body.getLowLevelBody(); link.bodyCore = &body.getBodyCore().getCore(); link.children = 0; @@ -253,7 +246,6 @@ void Sc::ArticulationSim::addBody(BodySim& body, body.setArticulation(this, wakeCounter, shouldSleep, index); } - void Sc::ArticulationSim::removeBody(BodySim &body) { PX_ASSERT(body.getArticulation() == this); @@ -299,7 +291,6 @@ void Sc::ArticulationSim::removeBody(BodySim &body) mLLArticulation->removeBody(); } - void Sc::ArticulationSim::checkResize() const { if(!mBodies.size()) @@ -308,8 +299,8 @@ void Sc::ArticulationSim::checkResize() const //if this is needed, we need to re-allocated the link data mLLArticulation->resize(mLinks.size()); - mLLArticulation->getSolverDesc().links = const_cast(mLinks.begin()); - mLLArticulation->getSolverDesc().linkCount = Ps::to8(mLinks.size()); + mLLArticulation->getSolverDesc().links = const_cast(mLinks.begin()); + mLLArticulation->getSolverDesc().linkCount = Ps::to8(mLinks.size()); //if this is needed, we need to re-allocated the joint data mLLArticulation->onUpdateSolverDesc(); @@ -356,7 +347,7 @@ void Sc::ArticulationSim::sleepCheck(PxReal dt) if(!mBodies[0]->isActive()) return; - PxReal sleepThreshold = getCore().getCore().sleepThreshold; + const PxReal sleepThreshold = getCore().getCore().sleepThreshold; PxReal maxTimer = 0.0f, minTimer = PX_MAX_F32; @@ -424,7 +415,6 @@ void Sc::ArticulationSim::updateForces(PxReal dt, bool simUsesAdaptiveForce) { PxU32 count = 0; - for(PxU32 i=0;igetDataSizes(mLinks.size(), solverDataSize, totalSize, scratchSize); @@ -471,27 +458,19 @@ Sc::ArticulationDriveCache* Sc::ArticulationSim::createDriveCache(PxReal complia return data; } - -void Sc::ArticulationSim::updateDriveCache(ArticulationDriveCache& cache, - PxReal compliance, - PxU32 driveIterations) const +void Sc::ArticulationSim::updateDriveCache(ArticulationDriveCache& cache, PxReal compliance, PxU32 driveIterations) const { checkResize(); PxvArticulationDriveCache::initialize(cache, Ps::to16(mLinks.size()), mLinks.begin(), compliance, driveIterations, mLLArticulation->getSolverDesc().scratchMemory, mLLArticulation->getSolverDesc().scratchMemorySize); } - void Sc::ArticulationSim::releaseDriveCache(Sc::ArticulationDriveCache& driveCache) const { PX_FREE(&driveCache); } - -void Sc::ArticulationSim::applyImpulse(Sc::BodyCore& link, - const Sc::ArticulationDriveCache& driveCache, - const PxVec3& force, - const PxVec3& torque) +void Sc::ArticulationSim::applyImpulse(Sc::BodyCore& link, const Sc::ArticulationDriveCache& driveCache, const PxVec3& force, const PxVec3& torque) { Cm::SpatialVectorV v[DY_ARTICULATION_MAX_SIZE], z[DY_ARTICULATION_MAX_SIZE]; PxMemZero(z, mLinks.size()*sizeof(Cm::SpatialVector)); @@ -539,7 +518,6 @@ PxU32 Sc::ArticulationSim::getDof(const PxU32 linkID) const PxArticulationCache* Sc::ArticulationSim::createCache() const { - checkResize(); PxU32 totalSize = getCacheDataSize() + sizeof(PxArticulationCache); @@ -629,7 +607,6 @@ PxU32 Sc::ArticulationSim::getScratchMemorySize() const return totalSize; } - void Sc::ArticulationSim::zeroCache(PxArticulationCache& cache) const { const PxU32 cacheDataSize = getCacheDataSize(); @@ -722,15 +699,12 @@ bool Sc::ArticulationSim::computeLambda(PxArticulationCache& cache, PxArticulati const PxReal* const jointTorque, const PxVec3 gravity, const PxU32 maxIter) { return mLLArticulation->getLambda(mLoopConstraints.begin(), mLoopConstraints.size(), cache, initialState, jointTorque, gravity, maxIter); - } void Sc::ArticulationSim::computeGeneralizedMassMatrix(PxArticulationCache& cache) { - mLLArticulation->getGeneralizedMassMatrixCRB(cache); - /*const PxU32 totalDofs = mLLArticulation->getDofs(); PxReal* massMatrix = reinterpret_cast(PX_ALLOC(sizeof(PxReal) * totalDofs * totalDofs, "MassMatrix")); @@ -756,7 +730,6 @@ void Sc::ArticulationSim::computeGeneralizedMassMatrix(PxArticulationCache& cach } PX_FREE(massMatrix);*/ - } PxU32 Sc::ArticulationSim::getCoefficentMatrixSize() const diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h index e52000b5d..f37d1b5f9 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScArticulationSim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp index 289716af7..01a95a541 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScBodyCore.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -70,7 +70,6 @@ Sc::BodyCore::BodyCore(PxActorType::Enum type, const PxTransform& bodyPose) mCore.mFlags = PxRigidBodyFlags(); mCore.linearVelocity = PxVec3(0.0f); mCore.angularVelocity = PxVec3(0.0f); - mCore.linearDamping = 0.0f; mCore.solverIterationCounts = (1 << 8) | 4; mCore.contactReportThreshold = PX_MAX_F32; mCore.setBody2Actor(PxTransform(PxIdentity)); @@ -81,6 +80,7 @@ Sc::BodyCore::BodyCore(PxActorType::Enum type, const PxTransform& bodyPose) if(type == PxActorType::eRIGID_DYNAMIC) { + mCore.linearDamping = 0.0f; mCore.angularDamping = 0.05f; mCore.maxAngularVelocitySq = 100.0f * 100.0f; // mCore.maxAngularVelocitySq = 7.0f * 7.0f; @@ -106,20 +106,16 @@ Sc::BodySim* Sc::BodyCore::getSim() const return static_cast(Sc::ActorCore::getSim()); } -size_t Sc::BodyCore::getSerialCore(PxsBodyCore& serialCore) +void Sc::BodyCore::restoreDynamicData() { - serialCore = mCore; - if(mSimStateData && mSimStateData->isKine()) - { - const Kinematic* kine = mSimStateData->getKinematicData(); - serialCore.inverseMass = kine->backupInvMass; - serialCore.inverseInertia = kine->backupInverseInertia; - serialCore.linearDamping = kine->backupLinearDamping; - serialCore.angularDamping = kine->backupAngularDamping; - serialCore.maxAngularVelocitySq = kine->backupMaxAngVelSq; - serialCore.maxLinearVelocitySq = kine->backupMaxLinVelSq; - } - return reinterpret_cast(&mCore); + PX_ASSERT(mSimStateData && mSimStateData->isKine()); + restore(); +} + +void Sc::BodyCore::backupDynamicData() +{ + PX_ASSERT(mSimStateData && mSimStateData->isKine()); + backup(*mSimStateData); } //-------------------------------------------------------------- diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp index 612f4c99d..434b0576c 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h index f72f73af3..7f3c4cc15 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScBodySim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScClient.h b/src/PhysX/physx/source/simulationcontroller/src/ScClient.h index f19bc3154..6eff41315 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScClient.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScClient.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp index 55009e5e7..0570a9722 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintCore.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp index 9177013ff..ee2ee1b58 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h index 144ffaecc..a3287c6de 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintGroupNode.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp index c2562d8d3..765363f18 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h index b6d38d795..2fdca72d4 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintInteraction.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp index 4b69fd29c..715f0bc49 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h index 34a0c8185..b67e538ef 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp index 9dc2d245c..781deac7f 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h index a13aaf5f8..b01bda050 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintProjectionTree.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp index 79c255ca0..f95a5c047 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h index 30e3462cd..c7377db79 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScConstraintSim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h b/src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h index 84e95bc82..8cf2a5aac 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScContactReportBuffer.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h b/src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h index 0aa386fa8..6ca4cfa8b 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScContactStream.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp index 7bf63fd1e..e2abdb289 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h index 5af3eba15..ea88a4f17 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementInteractionMarker.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp index 717295989..ee44d4db5 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h index 61cf51868..4bc09d1ed 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementSim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h index ccb52bbe0..388b839a2 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScElementSimInteraction.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp index 360db5796..e07336923 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h index effdddd5b..8ae12699b 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScInteraction.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h b/src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h index cd2b8eaa9..a4605f060 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScInteractionFlags.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp index 2b4ad6153..2d29d5d6d 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScIterators.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp index 1a7105e97..61ff6bae0 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScMetaData.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp index a6f8b3165..3f25f02f1 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h index 852975781..aca2f2e65 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScNPhaseCore.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h b/src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h index 9f3b43add..c57ce8190 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScObjectIDTracker.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp index 7fd2884de..924bb6f7d 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScPhysics.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp index 03f6672e7..5e3322c06 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScRigidCore.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp index 2ffeda1aa..d5a786901 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h index 830b80973..f9fda6d40 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScRigidSim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp index fc554f6e9..f790cb87e 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScScene.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -276,7 +276,7 @@ public: } private: - ScAfterIntegrationTask& operator = (const ScAfterIntegrationTask&); + PX_NOCOPY(ScAfterIntegrationTask) }; class ScSimulationControllerCallback : public PxsSimulationControllerCallback @@ -467,7 +467,7 @@ public: } private: - PxgUpdateBodyAndShapeStatusTask& operator = (const PxgUpdateBodyAndShapeStatusTask&); + PX_NOCOPY(PxgUpdateBodyAndShapeStatusTask) }; class PxgSimulationControllerCallback : public PxsSimulationControllerCallback @@ -860,7 +860,8 @@ Sc::Scene::Scene(const PxSceneDesc& desc, PxU64 contextID) : mLLContext->createTransformCache(*allocatorCallback); mLLContext->setContactDistance(mContactDistance); - mCCDContext = physx::PxsCCDContext::create(mLLContext, mDynamicsContext->getThresholdStream(), *mLLContext->getNphaseImplementationContext()); + mCCDContext = physx::PxsCCDContext::create(mLLContext, mDynamicsContext->getThresholdStream(), *mLLContext->getNphaseImplementationContext(), + desc.ccdThreshold); setSolverBatchSize(desc.solverBatchSize); mDynamicsContext->setFrictionOffsetThreshold(desc.frictionOffsetThreshold); @@ -1472,17 +1473,22 @@ void Sc::Scene::prepareCollide() getRenderBuffer().clear(); - ///clear broken constraint list: + // Clear broken constraint list: clearBrokenConstraintBuffer(); - updateFromVisualizationParameters(); + // Update from visualization parameters + if(mVisualizationParameterChanged) + { + mVisualizationParameterChanged = false; + + // Update SIPs if visualization is enabled + if( getVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL) || + getVisualizationParameter(PxVisualizationParameter::eCONTACT_ERROR) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_FORCE)) + mInternalFlags |= SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_VISUALIZATION; + } visualizeStartStep(); -#ifdef DUMP_PROFILER - dumpProfiler(this); -#endif - PxcClearContactCacheStats(); } @@ -1514,8 +1520,7 @@ void Sc::Scene::advance(PxReal timeStep, PxBaseTask* continuation) mAdvanceStep.setContinuation(continuation); - stepSetupSolve(&mAdvanceStep); - + stepSetupSolve(&mAdvanceStep); mAdvanceStep.removeReference(); } @@ -1680,16 +1685,6 @@ PxU32 Sc::Scene::getCCDMaxPasses() const return mCCDContext->getCCDMaxPasses(); } -void Sc::Scene::setBroadPhaseCallback(PxBroadPhaseCallback* callback) -{ - mBroadPhaseCallback = callback; -} - -PxBroadPhaseCallback* Sc::Scene::getBroadPhaseCallback() const -{ - return mBroadPhaseCallback; -} - void Sc::Scene::removeBody(BodySim& body) //this also notifies any connected joints! { ConstraintGroupNode* node = body.getConstraintGroup(); @@ -1830,10 +1825,6 @@ void Sc::Scene::deallocateConstraintBlock(void* ptr, PxU32 size) PX_FREE(ptr); } -//int testAxisConstraint(Sc::Scene& scene); -//int testCasts(Shape* shape); -//int testCasts(Shape& shape); - /*-------------------------------*\ | Adam's explanation of the RB solver: | This is a novel idea of mine, @@ -1988,7 +1979,6 @@ void Sc::Scene::postBroadPhase(PxBaseTask* continuation) //Notify narrow phase that broad phase has completed mLLContext->getNphaseImplementationContext()->postBroadPhaseUpdateContactManager(); mAABBManager->postBroadPhase(continuation, &mRigidBodyNPhaseUnlock, *getFlushPool()); - } void Sc::Scene::postBroadPhaseContinuation(PxBaseTask* continuation) @@ -2081,7 +2071,7 @@ class SpeculativeCCDContactDistanceUpdateTask : public Cm::Task public: static const PxU32 MaxBodies = 128; PxReal* mContactDistances; - PxReal mDt; + const PxReal mDt; Sc::BodySim* mBodySims[MaxBodies]; PxU32 mNbBodies; @@ -2114,7 +2104,7 @@ class SpeculativeCCDContactDistanceArticulationUpdateTask : public Cm::Task { public: PxReal* mContactDistances; - PxReal mDt; + const PxReal mDt; Sc::ArticulationSim* mArticulation; Bp::BoundsArray& mBoundsArray; @@ -2170,7 +2160,8 @@ void Sc::Scene::preRigidBodyNarrowPhase(PxBaseTask* continuation) ElementSim* current = bodySim->getElements_(); while (current) { - changedMap.growAndSet(current->getElementID()); + if (static_cast(current)->getFlags() & PxShapeFlag::eSIMULATION_SHAPE) + changedMap.growAndSet(current->getElementID()); current = current->mNextInActor; } @@ -2570,7 +2561,7 @@ PX_FORCE_INLINE void Sc::Scene::putInteractionsToSleep() //joints should have been deactivated. { - PxU32 nbDeactivatingEdges = islandSim.getNbDeactivatingEdges(IG::Edge::eCONTACT_MANAGER); + const PxU32 nbDeactivatingEdges = islandSim.getNbDeactivatingEdges(IG::Edge::eCONTACT_MANAGER); const IG::EdgeIndex* deactivatingEdgeIds = islandSim.getDeactivatingEdges(IG::Edge::eCONTACT_MANAGER); for (PxU32 i = 0; i < nbDeactivatingEdges; ++i) @@ -2622,14 +2613,10 @@ PX_FORCE_INLINE void Sc::Scene::wakeObjectsUp(PxU32 infoFlag) void Sc::Scene::postIslandGen(PxBaseTask* continuationTask) { - { - PX_PROFILE_ZONE("Sim.postIslandGen", getContextId()); + PX_PROFILE_ZONE("Sim.postIslandGen", getContextId()); - // - Performs collision detection for trigger interactions - { - mNPhaseCore->processTriggerInteractions(continuationTask); - } - } + // - Performs collision detection for trigger interactions + mNPhaseCore->processTriggerInteractions(continuationTask); } void Sc::Scene::solver(PxBaseTask* continuation) @@ -2697,69 +2684,15 @@ void Sc::Scene::processLostContacts(PxBaseTask* continuation) void Sc::Scene::lostTouchReports(PxBaseTask*) { + PX_PROFILE_ZONE("Sim.lostTouchReports", getContextId()); + PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); + + bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + { - PX_PROFILE_ZONE("Sim.lostTouchReports", getContextId()); - PxsContactManagerOutputIterator outputs = mLLContext->getNphaseImplementationContext()->getContactManagerOutputs(); - - bool useAdaptiveForce = mPublicFlags & PxSceneFlag::eADAPTIVE_FORCE; - - Bp::AABBManager* aabbMgr = mAABBManager; - PxU32 destroyedOverlapCount; - - { - const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); - while(destroyedOverlapCount--) - { - if(p->mPairUserData) - { - Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); - if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP) - mNPhaseCore->lostTouchReports(static_cast(elemInteraction), PxU32(PairReleaseFlag::eWAKE_ON_LOST_TOUCH), 0, outputs, useAdaptiveForce); - } - p++; - } - } - } -} - -void Sc::Scene::unregisterInteractions(PxBaseTask*) -{ - { - PX_PROFILE_ZONE("Sim.unregisterInteractions", getContextId()); - - Bp::AABBManager* aabbMgr = mAABBManager; - PxU32 destroyedOverlapCount; - - { - const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); - while(destroyedOverlapCount--) - { - if(p->mPairUserData) - { - Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); - if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP || elemInteraction->getType() == Sc::InteractionType::eMARKER) - { - unregisterInteraction(elemInteraction); - mNPhaseCore->unregisterInteraction(elemInteraction); - } - } - p++; - } - } - } -} - -void Sc::Scene::destroyManagers(PxBaseTask*) -{ - { - PX_PROFILE_ZONE("Sim.destroyManagers", getContextId()); - - mPostThirdPassIslandGenTask.setContinuation(mProcessLostContactsTask3.getContinuation()); - - mSimpleIslandManager->thirdPassIslandGen(&mPostThirdPassIslandGenTask); - - Bp::AABBManager* aabbMgr = mAABBManager; - PxU32 destroyedOverlapCount; const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); while(destroyedOverlapCount--) { @@ -2767,10 +2700,31 @@ void Sc::Scene::destroyManagers(PxBaseTask*) { Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP) + mNPhaseCore->lostTouchReports(static_cast(elemInteraction), PxU32(PairReleaseFlag::eWAKE_ON_LOST_TOUCH), 0, outputs, useAdaptiveForce); + } + p++; + } + } +} + +void Sc::Scene::unregisterInteractions(PxBaseTask*) +{ + PX_PROFILE_ZONE("Sim.unregisterInteractions", getContextId()); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + + { + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + if(p->mPairUserData) + { + Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); + if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP || elemInteraction->getType() == Sc::InteractionType::eMARKER) { - Sc::ShapeInteraction* si = static_cast(elemInteraction); - if(si->getContactManager()) - si->destroyManager(); + unregisterInteraction(elemInteraction); + mNPhaseCore->unregisterInteraction(elemInteraction); } } p++; @@ -2778,6 +2732,33 @@ void Sc::Scene::destroyManagers(PxBaseTask*) } } +void Sc::Scene::destroyManagers(PxBaseTask*) +{ + PX_PROFILE_ZONE("Sim.destroyManagers", getContextId()); + + mPostThirdPassIslandGenTask.setContinuation(mProcessLostContactsTask3.getContinuation()); + + mSimpleIslandManager->thirdPassIslandGen(&mPostThirdPassIslandGenTask); + + Bp::AABBManager* aabbMgr = mAABBManager; + PxU32 destroyedOverlapCount; + const Bp::AABBOverlap* PX_RESTRICT p = aabbMgr->getDestroyedOverlaps(Bp::ElementType::eSHAPE, destroyedOverlapCount); + while(destroyedOverlapCount--) + { + if(p->mPairUserData) + { + Sc::ElementSimInteraction* elemInteraction = reinterpret_cast(p->mPairUserData); + if(elemInteraction->getType() == Sc::InteractionType::eOVERLAP) + { + Sc::ShapeInteraction* si = static_cast(elemInteraction); + if(si->getContactManager()) + si->destroyManager(); + } + } + p++; + } +} + void Sc::Scene::processLostContacts2(PxBaseTask* continuation) { mDestroyManagersTask.setContinuation(continuation); @@ -3174,12 +3155,12 @@ void Sc::Scene::integrateKinematicPose() { PX_PROFILE_ZONE("Sim.integrateKinematicPose", getContextId()); - PxU32 nbKinematics = getActiveKinematicBodiesCount(); + const PxU32 nbKinematics = getActiveKinematicBodiesCount(); BodyCore*const* kinematics = getActiveKinematicBodies(); Cm::FlushPool& flushPool = mLLContext->getTaskPool(); - for(PxU32 i = 0; i < nbKinematics; i+= ScKinematicPoseUpdateTask::NbKinematicsPerTask) + for(PxU32 i=0; igetTaskPool(); - PxU32 startIndex = 0; PxU32 nbShapes = 0; { PX_PROFILE_ZONE("ShapeUpdate", getContextId()); - for (PxU32 i = 0; i < nbKinematics; i++) + for(PxU32 i=0; igetSim(); @@ -3259,7 +3239,7 @@ void Sc::Scene::updateKinematicCached(PxBaseTask* continuation) } } - if (nbShapes) + if(nbShapes) { ScKinematicShapeUpdateTask* task = PX_PLACEMENT_NEW(flushPool.allocate(sizeof(ScKinematicShapeUpdateTask)), ScKinematicShapeUpdateTask) (kinematics + startIndex, nbKinematics - startIndex, mLLContext->getTransformCache(), *mBoundsArray, mContextId); @@ -3269,7 +3249,7 @@ void Sc::Scene::updateKinematicCached(PxBaseTask* continuation) } } - if (nbKinematics) + if(nbKinematics) { Cm::BitMapPinned& changedAABBMap = mAABBManager->getChangedAABBMgActorHandleMap(); mLLContext->getTransformCache().setChangedState(); @@ -3313,7 +3293,7 @@ void Sc::Scene::updateKinematicCached(PxBaseTask* continuation) class ConstraintProjectionTask : public Cm::Task { private: - ConstraintProjectionTask& operator = (const ConstraintProjectionTask&); + PX_NOCOPY(ConstraintProjectionTask) public: ConstraintProjectionTask(Sc::ConstraintGroupNode* const* projectionRoots, PxU32 projectionRootCount, Ps::Array& projectedBodies, PxsContext* llContext) : @@ -3605,11 +3585,13 @@ void Sc::Scene::syncSceneQueryBounds(SqBoundsSync& sync, SqRefFinder& finder) class ScKinematicUpdateTask : public Cm::Task { - Sc::BodyCore*const* mKinematics; - PxU32 mNbKinematics; - PxReal mOneOverDt; + Sc::BodyCore*const* mKinematics; + const PxU32 mNbKinematics; + const PxReal mOneOverDt; + PX_NOCOPY(ScKinematicUpdateTask) public: + static const PxU32 NbKinematicsPerTask = 1024; ScKinematicUpdateTask(Sc::BodyCore*const* kinematics, PxU32 nbKinematics, PxReal oneOverDt, PxU64 contextID) : @@ -3619,13 +3601,17 @@ public: virtual void runInternal() { - for (PxU32 a = 0; a < mNbKinematics; ++a) + Sc::BodyCore*const* kinematics = mKinematics; + PxU32 nb = mNbKinematics; + const float oneOverDt = mOneOverDt; + + while(nb--) { - Sc::BodyCore* b = mKinematics[a]; + Sc::BodyCore* b = *kinematics++; PX_ASSERT(b->getSim()->isKinematic()); PX_ASSERT(b->getSim()->isActive()); - b->getSim()->calculateKinematicVelocity(mOneOverDt); + b->getSim()->calculateKinematicVelocity(oneOverDt); } } @@ -3638,7 +3624,7 @@ public: class ScKinematicAddDynamicTask : public Cm::Task { Sc::BodyCore*const* mKinematics; - PxU32 mNbKinematics; + const PxU32 mNbKinematics; PxsSimulationController& mSimulationController; PX_NOCOPY(ScKinematicAddDynamicTask) @@ -3652,9 +3638,13 @@ public: virtual void runInternal() { - for (PxU32 a = 0; a < mNbKinematics; ++a) + Sc::BodyCore*const* kinematics = mKinematics; + PxU32 nb = mNbKinematics; + + while(nb--) { - Sc::BodySim* bodySim = mKinematics[a]->getSim(); + Sc::BodyCore* b = *kinematics++; + Sc::BodySim* bodySim = b->getSim(); mSimulationController.updateDynamic(false, bodySim->getNodeIndex()); } } @@ -3763,7 +3753,7 @@ public: const PxReal mDt; IG::SimpleIslandManager* mIslandManager; PxsSimulationController* mSimulationController; - bool mSimUsesAdaptiveForce; + const bool mSimUsesAdaptiveForce; public: @@ -3786,9 +3776,11 @@ public: PxU32 updatedBodyNodeIndices[MaxBodiesPerTask]; PxU32 nbUpdatedBodySims = 0; - for(PxU32 i = 0; i < mNumBodies; i++) + PxU32 nb = mNumBodies; + const IG::NodeIndex* bodies = mBodies;; + while(nb--) { - IG::NodeIndex index = mBodies[i]; + const IG::NodeIndex index = *bodies++; if(islandSim.getActiveNodeIndex(index) != IG_INVALID_NODE) { if (islandSim.getNode(index).mType == IG::Node::eRIGID_BODY_TYPE) @@ -3810,7 +3802,7 @@ public: } private: - ScBeforeSolverTask& operator = (const ScBeforeSolverTask&); + PX_NOCOPY(ScBeforeSolverTask) }; @@ -3818,10 +3810,10 @@ class ScArticBeforeSolverTask : public Cm::Task { public: const IG::NodeIndex* const mArticIndices; - PxU32 mNumArticulations; + const PxU32 mNumArticulations; const PxReal mDt; IG::SimpleIslandManager* mIslandManager; - bool mSimUsesAdaptiveForce; + const bool mSimUsesAdaptiveForce; public: @@ -3855,7 +3847,7 @@ public: } private: - ScArticBeforeSolverTask& operator = (const ScArticBeforeSolverTask&); + PX_NOCOPY(ScArticBeforeSolverTask) }; @@ -3938,9 +3930,11 @@ void Sc::Scene::beforeSolver(PxBaseTask* continuation) class UpdatProjectedPoseTask : public Cm::Task { Sc::BodySim** mProjectedBodies; - PxU32 mNbBodiesToProcess; + const PxU32 mNbBodiesToProcess; + PX_NOCOPY(UpdatProjectedPoseTask) public: + UpdatProjectedPoseTask(PxU64 contextID, Sc::BodySim** projectedBodies, PxU32 nbBodiesToProcess) : Cm::Task (contextID), mProjectedBodies (projectedBodies), @@ -3965,13 +3959,17 @@ public: class UpdateArticulationTask : public Cm::Task { Sc::ArticulationCore* const* mArticList; - PxU32 mNbArticulations; - PxReal mDt; + const PxU32 mNbArticulations; + const PxReal mDt; + PX_NOCOPY(UpdateArticulationTask) public: - UpdateArticulationTask(PxU64 contextId, Sc::ArticulationCore* const* articList, PxU32 nbArticulations, PxReal dt) : Cm::Task(contextId), mArticList(articList), mNbArticulations(nbArticulations), - mDt(dt) + UpdateArticulationTask(PxU64 contextId, Sc::ArticulationCore* const* articList, PxU32 nbArticulations, PxReal dt) : + Cm::Task (contextId), + mArticList (articList), + mNbArticulations (nbArticulations), + mDt (dt) { } @@ -4001,7 +3999,6 @@ void Sc::Scene::afterIntegration(PxBaseTask* continuation) PX_PROFILE_ZONE("AfterIntegration::lockStage", getContextId()); mLLContext->getLock().lock(); - mSimulationController->udpateScBodyAndShapeSim(cache, boundArray, continuation); const IG::IslandSim& islandSim = mSimpleIslandManager->getAccurateIslandSim(); @@ -4031,7 +4028,6 @@ void Sc::Scene::afterIntegration(PxBaseTask* continuation) //if(!islandSim.getNode(bodySim->getNodeIndex()).isActive()) rigid->setPose(rigid->getLastCCDTransform()); - bodySim->updateCached(&changedAABBMgrActorHandles); mSimulationController->updateDynamic(bodySim->isArticulationLink(), bodySim->getNodeIndex()); @@ -4140,7 +4136,6 @@ void Sc::Scene::afterIntegration(PxBaseTask* continuation) mLLContext->getLock().lock(); Cm::BitMapPinned& changedAABBMgrActorHandles = mAABBManager->getChangedAABBMgActorHandleMap(); - Sc::BodySim* ccdBodySims[DY_ARTICULATION_MAX_SIZE]; for(PxU32 i=0;imarkShapesUpdated(&changedAABBMgrActorHandles); - } mLLContext->getLock().unlock(); } @@ -5428,19 +5422,6 @@ PX_INLINE void Sc::Scene::clearBrokenConstraintBuffer() mBrokenConstraints.clear(); } -void Sc::Scene::updateFromVisualizationParameters() -{ - if (!mVisualizationParameterChanged) // All up to date - return; - - // Update SIPs if visualization is enabled - if (getVisualizationParameter(PxVisualizationParameter::eCONTACT_POINT) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_NORMAL) || - getVisualizationParameter(PxVisualizationParameter::eCONTACT_ERROR) || getVisualizationParameter(PxVisualizationParameter::eCONTACT_FORCE)) - mInternalFlags |= SceneInternalFlag::eSCENE_SIP_STATES_DIRTY_VISUALIZATION; - - mVisualizationParameterChanged = false; -} - void Sc::Scene::addToLostTouchList(BodySim* body1, BodySim* body2) { PX_ASSERT(body1 != 0); diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp index 5d53be878..b0ac51218 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeCore.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp index d9c9af0b9..3be8f3e6c 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -326,21 +326,18 @@ void Sc::ShapeInteraction::processUserNotificationSync() void Sc::ShapeInteraction::processUserNotificationAsync(PxU32 contactEvent, PxU16 infoFlags, bool touchLost, const PxU32 ccdPass, const bool useCurrentTransform, PxsContactManagerOutputIterator& outputs, ContactReportAllocationManager* alloc) { - contactEvent = (!ccdPass) ? contactEvent : (contactEvent | PxPairFlag::eNOTIFY_TOUCH_CCD); - if (mActorPair == NULL) - { + if(!mActorPair) return; - } ActorPairReport& aPairReport = getActorPairReport(); Scene& scene = getScene(); NPhaseCore* npcore = scene.getNPhaseCore(); ContactStreamManager& cs = aPairReport.createContactStreamManager(*npcore); // Prepare user notification - PxU32 timeStamp = scene.getTimeStamp(); - PxU32 shapePairTimeStamp = scene.getReportShapePairTimeStamp(); + const PxU32 timeStamp = scene.getTimeStamp(); + const PxU32 shapePairTimeStamp = scene.getReportShapePairTimeStamp(); const PxU32 pairFlags = getPairFlags(); PX_ASSERT(pairFlags & contactEvent); diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h index 9e18b98a9..5c45cdc7d 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeInteraction.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp index 7752c395d..34249e58f 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h index eadc1a2da..b8cc5fd89 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScShapeSim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h b/src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h index 97be8b629..e022219f3 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimStateData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp index b246dfbbd..9ee8f37d6 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h index 06356e815..5efbfac47 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimStats.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp index 952436230..72d360f72 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h index 6360c4dbf..fbf69b7c6 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSimulationController.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp index 7676fcd57..3f0ddd948 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h index 74d16425b..411c0f681 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScSqBoundsManager.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp index 12ff0e08f..2c51452cc 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScStaticCore.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h b/src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h index f57cf6369..4a13a4182 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScStaticSim.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp index a4f5ce858..b23869453 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp +++ b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h index 5de0fee14..718a72060 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerInteraction.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h index dbbbbc819..590869830 100644 --- a/src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h +++ b/src/PhysX/physx/source/simulationcontroller/src/ScTriggerPairs.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/physx/source/task/src/TaskManager.cpp b/src/PhysX/physx/source/task/src/TaskManager.cpp index e5cd3fdf2..2279e5142 100644 --- a/src/PhysX/physx/source/task/src/TaskManager.cpp +++ b/src/PhysX/physx/source/task/src/TaskManager.cpp @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #include "task/PxTask.h" #include "task/PxTaskDefine.h" diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/PxBoilerPlate.h b/src/PhysX/physx/tools/physxmetadatagenerator/PxBoilerPlate.h index 6be095b2f..ede1c3d4c 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/PxBoilerPlate.h +++ b/src/PhysX/physx/tools/physxmetadatagenerator/PxBoilerPlate.h @@ -23,4 +23,4 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/PxExtensionsCommon.h b/src/PhysX/physx/tools/physxmetadatagenerator/PxExtensionsCommon.h index 4f5178e6a..ce3c31d45 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/PxExtensionsCommon.h +++ b/src/PhysX/physx/tools/physxmetadatagenerator/PxExtensionsCommon.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_EXTENSIONS_COMMON_H diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h b/src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h index 88dc86ac6..ee06aa80e 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h +++ b/src/PhysX/physx/tools/physxmetadatagenerator/PxPhysicsWithExtensionsAPI.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_PHYSICS_NXPHYSICSWITHEXTENSIONS_API diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h b/src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h index 0e0cfb1b7..a000a2229 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h +++ b/src/PhysX/physx/tools/physxmetadatagenerator/PxVehicleExtensionAPI.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_PHYSICS_NXPHYSICSWITHVEHICLEEXTENSIONS_API diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat index 651051768..1413ebf2e 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat +++ b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.bat @@ -23,7 +23,7 @@ :: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE :: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. :: -:: Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. +:: Copyright (c) 2016-2019 NVIDIA Corporation. All rights reserved. @echo off setlocal EnableDelayedExpansion diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py index 7af2f264c..81e837364 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py +++ b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.py @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2016-2019 NVIDIA Corporation. All rights reserved. from __future__ import print_function diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh index 149b523a4..7e2b124a1 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh +++ b/src/PhysX/physx/tools/physxmetadatagenerator/generateMetaData.sh @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. #!/bin/bash diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/helper.sh b/src/PhysX/physx/tools/physxmetadatagenerator/helper.sh index 95b193442..6087bbccc 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/helper.sh +++ b/src/PhysX/physx/tools/physxmetadatagenerator/helper.sh @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. #!/bin/bash diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py b/src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py index a7657a8c8..78f052657 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py +++ b/src/PhysX/physx/tools/physxmetadatagenerator/lib/compare.py @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2016-2019 NVIDIA Corporation. All rights reserved. # general utility module from __future__ import print_function diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py b/src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py index 1d3a786b5..5ab148482 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py +++ b/src/PhysX/physx/tools/physxmetadatagenerator/lib/utils.py @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2016-2019 NVIDIA Corporation. All rights reserved. # general utility module from __future__ import print_function diff --git a/src/PhysX/physx/tools/physxmetadatagenerator/readme.txt b/src/PhysX/physx/tools/physxmetadatagenerator/readme.txt index fa569f54c..e9009512a 100644 --- a/src/PhysX/physx/tools/physxmetadatagenerator/readme.txt +++ b/src/PhysX/physx/tools/physxmetadatagenerator/readme.txt @@ -23,7 +23,7 @@ ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -## Copyright (c) 2018 NVIDIA Corporation. All rights reserved. +## Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved. If the Physx API is changed, the API level serialization meta data files need to be updated. This can be done by running: diff --git a/src/PhysX/pxshared/include/foundation/Px.h b/src/PhysX/pxshared/include/foundation/Px.h index 28c744853..cac0bd960 100644 --- a/src/PhysX/pxshared/include/foundation/Px.h +++ b/src/PhysX/pxshared/include/foundation/Px.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h b/src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h index d9350ce2e..db5987f55 100644 --- a/src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h +++ b/src/PhysX/pxshared/include/foundation/PxAllocatorCallback.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxBitAndData.h b/src/PhysX/pxshared/include/foundation/PxBitAndData.h index be7699e49..a1f355935 100644 --- a/src/PhysX/pxshared/include/foundation/PxBitAndData.h +++ b/src/PhysX/pxshared/include/foundation/PxBitAndData.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxBounds3.h b/src/PhysX/pxshared/include/foundation/PxBounds3.h index 7a071c3ab..cf4d44d38 100644 --- a/src/PhysX/pxshared/include/foundation/PxBounds3.h +++ b/src/PhysX/pxshared/include/foundation/PxBounds3.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -321,40 +321,40 @@ PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::setMaximal() PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::include(const PxVec3& v) { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); minimum = minimum.minimum(v); maximum = maximum.maximum(v); } PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::include(const PxBounds3& b) { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); minimum = minimum.minimum(b.minimum); maximum = maximum.maximum(b.maximum); } PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isEmpty() const { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); return minimum.x > maximum.x; } PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::intersects(const PxBounds3& b) const { - PX_ASSERT(isValid() && b.isValid()); + PX_SHARED_ASSERT(isValid() && b.isValid()); return !(b.minimum.x > maximum.x || minimum.x > b.maximum.x || b.minimum.y > maximum.y || minimum.y > b.maximum.y || b.minimum.z > maximum.z || minimum.z > b.maximum.z); } PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::intersects1D(const PxBounds3& a, uint32_t axis) const { - PX_ASSERT(isValid() && a.isValid()); + PX_SHARED_ASSERT(isValid() && a.isValid()); return maximum[axis] >= a.minimum[axis] && a.maximum[axis] >= minimum[axis]; } PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::contains(const PxVec3& v) const { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); return !(v.x < minimum.x || v.x > maximum.x || v.y < minimum.y || v.y > maximum.y || v.z < minimum.z || v.z > maximum.z); @@ -362,7 +362,7 @@ PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::contains(const PxVec3& v) const PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isInside(const PxBounds3& box) const { - PX_ASSERT(isValid() && box.isValid()); + PX_SHARED_ASSERT(isValid() && box.isValid()); if(box.minimum.x > minimum.x) return false; if(box.minimum.y > minimum.y) @@ -380,57 +380,57 @@ PX_CUDA_CALLABLE PX_FORCE_INLINE bool PxBounds3::isInside(const PxBounds3& box) PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 PxBounds3::getCenter() const { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); return (minimum + maximum) * 0.5f; } PX_CUDA_CALLABLE PX_FORCE_INLINE float PxBounds3::getCenter(uint32_t axis) const { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); return (minimum[axis] + maximum[axis]) * 0.5f; } PX_CUDA_CALLABLE PX_FORCE_INLINE float PxBounds3::getExtents(uint32_t axis) const { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); return (maximum[axis] - minimum[axis]) * 0.5f; } PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 PxBounds3::getDimensions() const { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); return maximum - minimum; } PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 PxBounds3::getExtents() const { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); return getDimensions() * 0.5f; } PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::scaleSafe(float scale) { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); if(!isEmpty()) scaleFast(scale); } PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::scaleFast(float scale) { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); *this = centerExtents(getCenter(), getExtents() * scale); } PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::fattenSafe(float distance) { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); if(!isEmpty()) fattenFast(distance); } PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::fattenFast(float distance) { - PX_ASSERT(isValid()); + PX_SHARED_ASSERT(isValid()); minimum.x -= distance; minimum.y -= distance; minimum.z -= distance; @@ -442,25 +442,25 @@ PX_CUDA_CALLABLE PX_FORCE_INLINE void PxBounds3::fattenFast(float distance) PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformSafe(const PxMat33& matrix, const PxBounds3& bounds) { - PX_ASSERT(bounds.isValid()); + PX_SHARED_ASSERT(bounds.isValid()); return !bounds.isEmpty() ? transformFast(matrix, bounds) : bounds; } PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformFast(const PxMat33& matrix, const PxBounds3& bounds) { - PX_ASSERT(bounds.isValid()); + PX_SHARED_ASSERT(bounds.isValid()); return PxBounds3::basisExtent(matrix * bounds.getCenter(), matrix, bounds.getExtents()); } PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformSafe(const PxTransform& transform, const PxBounds3& bounds) { - PX_ASSERT(bounds.isValid()); + PX_SHARED_ASSERT(bounds.isValid()); return !bounds.isEmpty() ? transformFast(transform, bounds) : bounds; } PX_CUDA_CALLABLE PX_INLINE PxBounds3 PxBounds3::transformFast(const PxTransform& transform, const PxBounds3& bounds) { - PX_ASSERT(bounds.isValid()); + PX_SHARED_ASSERT(bounds.isValid()); return PxBounds3::basisExtent(transform.transform(bounds.getCenter()), PxMat33(transform.q), bounds.getExtents()); } diff --git a/src/PhysX/pxshared/include/foundation/PxErrorCallback.h b/src/PhysX/pxshared/include/foundation/PxErrorCallback.h index 554c76c8a..b573d86e0 100644 --- a/src/PhysX/pxshared/include/foundation/PxErrorCallback.h +++ b/src/PhysX/pxshared/include/foundation/PxErrorCallback.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxErrors.h b/src/PhysX/pxshared/include/foundation/PxErrors.h index eaedc50fd..c0cb9c9c1 100644 --- a/src/PhysX/pxshared/include/foundation/PxErrors.h +++ b/src/PhysX/pxshared/include/foundation/PxErrors.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxFlags.h b/src/PhysX/pxshared/include/foundation/PxFlags.h index 58d456cf5..780fb2705 100644 --- a/src/PhysX/pxshared/include/foundation/PxFlags.h +++ b/src/PhysX/pxshared/include/foundation/PxFlags.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxIO.h b/src/PhysX/pxshared/include/foundation/PxIO.h index 4000c9bcd..e5e2fef9d 100644 --- a/src/PhysX/pxshared/include/foundation/PxIO.h +++ b/src/PhysX/pxshared/include/foundation/PxIO.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxIntrinsics.h b/src/PhysX/pxshared/include/foundation/PxIntrinsics.h index d4257168d..270b34bd6 100644 --- a/src/PhysX/pxshared/include/foundation/PxIntrinsics.h +++ b/src/PhysX/pxshared/include/foundation/PxIntrinsics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxMat33.h b/src/PhysX/pxshared/include/foundation/PxMat33.h index 6cf4257f1..d97d925cc 100644 --- a/src/PhysX/pxshared/include/foundation/PxMat33.h +++ b/src/PhysX/pxshared/include/foundation/PxMat33.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxMat44.h b/src/PhysX/pxshared/include/foundation/PxMat44.h index 91500ae67..0b6403e40 100644 --- a/src/PhysX/pxshared/include/foundation/PxMat44.h +++ b/src/PhysX/pxshared/include/foundation/PxMat44.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -301,7 +301,7 @@ class PxMat44 PX_CUDA_CALLABLE PX_INLINE const PxVec3 getBasis(int num) const { - PX_ASSERT(num >= 0 && num < 3); + PX_SHARED_ASSERT(num >= 0 && num < 3); return (&column0)[num].getXYZ(); } diff --git a/src/PhysX/pxshared/include/foundation/PxMath.h b/src/PhysX/pxshared/include/foundation/PxMath.h index 6f4817fcb..fc51373b0 100644 --- a/src/PhysX/pxshared/include/foundation/PxMath.h +++ b/src/PhysX/pxshared/include/foundation/PxMath.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -47,7 +47,7 @@ #include #include "foundation/PxIntrinsics.h" -#include "foundation/PxAssert.h" +#include "foundation/PxSharedAssert.h" #if !PX_DOXYGEN namespace physx @@ -136,7 +136,7 @@ PX_CUDA_CALLABLE PX_FORCE_INLINE int32_t PxAbs(int32_t a) template PX_CUDA_CALLABLE PX_FORCE_INLINE T PxClamp(T v, T lo, T hi) { - PX_ASSERT(lo <= hi); + PX_SHARED_ASSERT(lo <= hi); return PxMin(hi, PxMax(lo, v)); } diff --git a/src/PhysX/pxshared/include/foundation/PxMemory.h b/src/PhysX/pxshared/include/foundation/PxMemory.h index 68d721def..2aacd8803 100644 --- a/src/PhysX/pxshared/include/foundation/PxMemory.h +++ b/src/PhysX/pxshared/include/foundation/PxMemory.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxPlane.h b/src/PhysX/pxshared/include/foundation/PxPlane.h index 618d3f077..7d39323d3 100644 --- a/src/PhysX/pxshared/include/foundation/PxPlane.h +++ b/src/PhysX/pxshared/include/foundation/PxPlane.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxPreprocessor.h b/src/PhysX/pxshared/include/foundation/PxPreprocessor.h index 13d84d93a..314627fc6 100644 --- a/src/PhysX/pxshared/include/foundation/PxPreprocessor.h +++ b/src/PhysX/pxshared/include/foundation/PxPreprocessor.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -293,31 +293,6 @@ DLL export macros #define PX_DLL_IMPORT #endif -/** -Define API function declaration - -PX_FOUNDATION_DLL=1 - used by the DLL library (PhysXCommon) to export the API -PX_FOUNDATION_DLL=0 - for windows configurations where the PX_FOUNDATION_API is linked through standard static linking -no definition - this will allow DLLs and libraries to use the exported API from PhysXCommon - -*/ - -#if PX_WINDOWS_FAMILY && !PX_ARM_FAMILY -#ifndef PX_FOUNDATION_DLL -#define PX_FOUNDATION_API PX_DLL_IMPORT -#elif PX_FOUNDATION_DLL -#define PX_FOUNDATION_API PX_DLL_EXPORT -#endif -#elif PX_UNIX_FAMILY -#ifdef PX_FOUNDATION_DLL -#define PX_FOUNDATION_API PX_UNIX_EXPORT -#endif -#endif - -#ifndef PX_FOUNDATION_API -#define PX_FOUNDATION_API -#endif - /** Calling convention */ diff --git a/src/PhysX/pxshared/include/foundation/PxProfiler.h b/src/PhysX/pxshared/include/foundation/PxProfiler.h index 82d978807..5c063d1c4 100644 --- a/src/PhysX/pxshared/include/foundation/PxProfiler.h +++ b/src/PhysX/pxshared/include/foundation/PxProfiler.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. #ifndef PXFOUNDATION_PXPROFILER_H #define PXFOUNDATION_PXPROFILER_H diff --git a/src/PhysX/pxshared/include/foundation/PxQuat.h b/src/PhysX/pxshared/include/foundation/PxQuat.h index 7ae30e9cb..fa2c6c89d 100644 --- a/src/PhysX/pxshared/include/foundation/PxQuat.h +++ b/src/PhysX/pxshared/include/foundation/PxQuat.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -87,7 +87,7 @@ class PxQuat */ PX_CUDA_CALLABLE PX_INLINE PxQuat(float angleRadians, const PxVec3& unitAxis) { - PX_ASSERT(PxAbs(1.0f - unitAxis.magnitude()) < 1e-3f); + PX_SHARED_ASSERT(PxAbs(1.0f - unitAxis.magnitude()) < 1e-3f); const float a = angleRadians * 0.5f; const float s = PxSin(a); w = PxCos(a); diff --git a/src/PhysX/pxshared/include/foundation/PxSharedAssert.h b/src/PhysX/pxshared/include/foundation/PxSharedAssert.h new file mode 100644 index 000000000..1cb05cb2e --- /dev/null +++ b/src/PhysX/pxshared/include/foundation/PxSharedAssert.h @@ -0,0 +1,46 @@ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright (c) 2001-2004 NovodeX AG. All rights reserved. + +#ifndef PXFOUNDATION_PXASSERT_H +#define PXFOUNDATION_PXASSERT_H + +/** \addtogroup foundation +@{ */ + +#include "foundation/Px.h" + +#if !PX_ENABLE_ASSERTS + #define PX_SHARED_ASSERT(exp) ((void)0) +#else + #include + #define PX_SHARED_ASSERT(exp) assert(exp); +#endif // !PX_ENABLE_ASSERTS + +/** @} */ +#endif // #ifndef PXFOUNDATION_PXASSERT_H diff --git a/src/PhysX/pxshared/include/foundation/PxSimpleTypes.h b/src/PhysX/pxshared/include/foundation/PxSimpleTypes.h index 162b788f5..5649c91f1 100644 --- a/src/PhysX/pxshared/include/foundation/PxSimpleTypes.h +++ b/src/PhysX/pxshared/include/foundation/PxSimpleTypes.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxStrideIterator.h b/src/PhysX/pxshared/include/foundation/PxStrideIterator.h index 786938e3d..ed66f8c1c 100644 --- a/src/PhysX/pxshared/include/foundation/PxStrideIterator.h +++ b/src/PhysX/pxshared/include/foundation/PxStrideIterator.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -31,7 +31,7 @@ #define PXFOUNDATION_PXSTRIDEITERATOR_H #include "foundation/Px.h" -#include "foundation/PxAssert.h" +#include "foundation/PxSharedAssert.h" /** \addtogroup foundation @{ @@ -106,7 +106,7 @@ class PxStrideIterator */ explicit PX_INLINE PxStrideIterator(T* ptr = NULL, PxU32 stride = sizeof(T)) : mPtr(ptr), mStride(stride) { - PX_ASSERT(mStride == 0 || sizeof(T) <= mStride); + PX_SHARED_ASSERT(mStride == 0 || sizeof(T) <= mStride); } /** @@ -117,7 +117,7 @@ class PxStrideIterator PX_INLINE PxStrideIterator(const PxStrideIterator::Type>& strideIterator) : mPtr(strideIterator.ptr()), mStride(strideIterator.stride()) { - PX_ASSERT(mStride == 0 || sizeof(T) <= mStride); + PX_SHARED_ASSERT(mStride == 0 || sizeof(T) <= mStride); } /** @@ -237,7 +237,7 @@ class PxStrideIterator */ PX_INLINE int operator-(const PxStrideIterator& other) const { - PX_ASSERT(isCompatible(other)); + PX_SHARED_ASSERT(isCompatible(other)); int byteDiff = static_cast(reinterpret_cast(mPtr) - reinterpret_cast(other.mPtr)); return byteDiff / static_cast(stride()); } @@ -247,7 +247,7 @@ class PxStrideIterator */ PX_INLINE bool operator==(const PxStrideIterator& other) const { - PX_ASSERT(isCompatible(other)); + PX_SHARED_ASSERT(isCompatible(other)); return mPtr == other.mPtr; } @@ -256,7 +256,7 @@ class PxStrideIterator */ PX_INLINE bool operator!=(const PxStrideIterator& other) const { - PX_ASSERT(isCompatible(other)); + PX_SHARED_ASSERT(isCompatible(other)); return mPtr != other.mPtr; } @@ -265,7 +265,7 @@ class PxStrideIterator */ PX_INLINE bool operator<(const PxStrideIterator& other) const { - PX_ASSERT(isCompatible(other)); + PX_SHARED_ASSERT(isCompatible(other)); return mPtr < other.mPtr; } @@ -274,7 +274,7 @@ class PxStrideIterator */ PX_INLINE bool operator>(const PxStrideIterator& other) const { - PX_ASSERT(isCompatible(other)); + PX_SHARED_ASSERT(isCompatible(other)); return mPtr > other.mPtr; } @@ -283,7 +283,7 @@ class PxStrideIterator */ PX_INLINE bool operator<=(const PxStrideIterator& other) const { - PX_ASSERT(isCompatible(other)); + PX_SHARED_ASSERT(isCompatible(other)); return mPtr <= other.mPtr; } @@ -292,7 +292,7 @@ class PxStrideIterator */ PX_INLINE bool operator>=(const PxStrideIterator& other) const { - PX_ASSERT(isCompatible(other)); + PX_SHARED_ASSERT(isCompatible(other)); return mPtr >= other.mPtr; } diff --git a/src/PhysX/pxshared/include/foundation/PxTransform.h b/src/PhysX/pxshared/include/foundation/PxTransform.h index 8b29584f3..2164ca3c1 100644 --- a/src/PhysX/pxshared/include/foundation/PxTransform.h +++ b/src/PhysX/pxshared/include/foundation/PxTransform.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -66,7 +66,7 @@ class PxTransform PX_CUDA_CALLABLE PX_FORCE_INLINE explicit PxTransform(const PxQuat& orientation) : q(orientation), p(0) { - PX_ASSERT(orientation.isSane()); + PX_SHARED_ASSERT(orientation.isSane()); } PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform(float x, float y, float z, PxQuat aQ = PxQuat(PxIdentity)) @@ -76,7 +76,7 @@ class PxTransform PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform(const PxVec3& p0, const PxQuat& q0) : q(q0), p(p0) { - PX_ASSERT(q0.isSane()); + PX_SHARED_ASSERT(q0.isSane()); } PX_CUDA_CALLABLE PX_FORCE_INLINE explicit PxTransform(const PxMat44& m); // defined in PxMat44.h @@ -91,7 +91,7 @@ class PxTransform PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform operator*(const PxTransform& x) const { - PX_ASSERT(x.isSane()); + PX_SHARED_ASSERT(x.isSane()); return transform(x); } @@ -104,39 +104,39 @@ class PxTransform PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform getInverse() const { - PX_ASSERT(isFinite()); + PX_SHARED_ASSERT(isFinite()); return PxTransform(q.rotateInv(-p), q.getConjugate()); } PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 transform(const PxVec3& input) const { - PX_ASSERT(isFinite()); + PX_SHARED_ASSERT(isFinite()); return q.rotate(input) + p; } PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 transformInv(const PxVec3& input) const { - PX_ASSERT(isFinite()); + PX_SHARED_ASSERT(isFinite()); return q.rotateInv(input - p); } PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 rotate(const PxVec3& input) const { - PX_ASSERT(isFinite()); + PX_SHARED_ASSERT(isFinite()); return q.rotate(input); } PX_CUDA_CALLABLE PX_FORCE_INLINE PxVec3 rotateInv(const PxVec3& input) const { - PX_ASSERT(isFinite()); + PX_SHARED_ASSERT(isFinite()); return q.rotateInv(input); } //! Transform transform to parent (returns compound transform: first src, then *this) PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform transform(const PxTransform& src) const { - PX_ASSERT(src.isSane()); - PX_ASSERT(isSane()); + PX_SHARED_ASSERT(src.isSane()); + PX_SHARED_ASSERT(isSane()); // src = [srct, srcr] -> [r*srct + t, r*srcr] return PxTransform(q.rotate(src.p) + p, q * src.q); } @@ -171,8 +171,8 @@ class PxTransform //! Transform transform from parent (returns compound transform: first src, then this->inverse) PX_CUDA_CALLABLE PX_FORCE_INLINE PxTransform transformInv(const PxTransform& src) const { - PX_ASSERT(src.isSane()); - PX_ASSERT(isFinite()); + PX_SHARED_ASSERT(src.isSane()); + PX_SHARED_ASSERT(isFinite()); // src = [srct, srcr] -> [r^-1*(srct-t), r^-1*srcr] PxQuat qinv = q.getConjugate(); return PxTransform(qinv.rotate(src.p - p), qinv * src.q); diff --git a/src/PhysX/pxshared/include/foundation/PxUnionCast.h b/src/PhysX/pxshared/include/foundation/PxUnionCast.h index ec35911e0..6ae6c72d2 100644 --- a/src/PhysX/pxshared/include/foundation/PxUnionCast.h +++ b/src/PhysX/pxshared/include/foundation/PxUnionCast.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. diff --git a/src/PhysX/pxshared/include/foundation/PxVec2.h b/src/PhysX/pxshared/include/foundation/PxVec2.h index 0736eddb8..82d6be4eb 100644 --- a/src/PhysX/pxshared/include/foundation/PxVec2.h +++ b/src/PhysX/pxshared/include/foundation/PxVec2.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -109,7 +109,7 @@ class PxVec2 */ PX_CUDA_CALLABLE PX_FORCE_INLINE float& operator[](int index) { - PX_ASSERT(index >= 0 && index <= 1); + PX_SHARED_ASSERT(index >= 0 && index <= 1); return reinterpret_cast(this)[index]; } @@ -119,7 +119,7 @@ class PxVec2 */ PX_CUDA_CALLABLE PX_FORCE_INLINE const float& operator[](int index) const { - PX_ASSERT(index >= 0 && index <= 1); + PX_SHARED_ASSERT(index >= 0 && index <= 1); return reinterpret_cast(this)[index]; } diff --git a/src/PhysX/pxshared/include/foundation/PxVec3.h b/src/PhysX/pxshared/include/foundation/PxVec3.h index 9ba01a091..3e19f0066 100644 --- a/src/PhysX/pxshared/include/foundation/PxVec3.h +++ b/src/PhysX/pxshared/include/foundation/PxVec3.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -111,7 +111,7 @@ class PxVec3 */ PX_CUDA_CALLABLE PX_FORCE_INLINE float& operator[](unsigned int index) { - PX_ASSERT(index <= 2); + PX_SHARED_ASSERT(index <= 2); return reinterpret_cast(this)[index]; } @@ -121,7 +121,7 @@ class PxVec3 */ PX_CUDA_CALLABLE PX_FORCE_INLINE const float& operator[](unsigned int index) const { - PX_ASSERT(index <= 2); + PX_SHARED_ASSERT(index <= 2); return reinterpret_cast(this)[index]; } @@ -325,7 +325,7 @@ class PxVec3 PX_CUDA_CALLABLE PX_FORCE_INLINE float normalizeFast() { const float mag = magnitude(); - PX_ASSERT(mag >= PX_NORMALIZATION_EPSILON); + PX_SHARED_ASSERT(mag >= PX_NORMALIZATION_EPSILON); *this *= 1.0f / mag; return mag; } diff --git a/src/PhysX/pxshared/include/foundation/PxVec4.h b/src/PhysX/pxshared/include/foundation/PxVec4.h index dd7ebdfa6..264a88eff 100644 --- a/src/PhysX/pxshared/include/foundation/PxVec4.h +++ b/src/PhysX/pxshared/include/foundation/PxVec4.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -34,7 +34,7 @@ */ #include "foundation/PxMath.h" #include "foundation/PxVec3.h" -#include "foundation/PxAssert.h" +#include "foundation/PxSharedAssert.h" /** \brief 4 Element vector class. @@ -132,7 +132,7 @@ class PxVec4 */ PX_CUDA_CALLABLE PX_INLINE float& operator[](unsigned int index) { - PX_ASSERT(index <= 3); + PX_SHARED_ASSERT(index <= 3); return reinterpret_cast(this)[index]; } @@ -142,7 +142,7 @@ class PxVec4 */ PX_CUDA_CALLABLE PX_INLINE const float& operator[](unsigned int index) const { - PX_ASSERT(index <= 3); + PX_SHARED_ASSERT(index <= 3); return reinterpret_cast(this)[index]; } diff --git a/src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h b/src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h index 64c1367b7..c0a01ba75 100644 --- a/src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h +++ b/src/PhysX/pxshared/include/foundation/unix/PxUnixIntrinsics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -31,7 +31,7 @@ #define PXFOUNDATION_PXUNIXINTRINSICS_H #include "foundation/Px.h" -#include "foundation/PxAssert.h" +#include "foundation/PxSharedAssert.h" #if !(PX_LINUX || PX_ANDROID || PX_PS4 || PX_APPLE_FAMILY) #error "This file should only be included by Unix builds!!" @@ -175,7 +175,7 @@ Set 128B to zero starting at \c dst+offset. Must be aligned. */ PX_FORCE_INLINE void memZero128(void* dest, uint32_t offset = 0) { - PX_ASSERT(((size_t(dest) + offset) & 0x7f) == 0); + PX_SHARED_ASSERT(((size_t(dest) + offset) & 0x7f) == 0); memSet(reinterpret_cast(dest) + offset, 0, 128); } diff --git a/src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h b/src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h index 3705b26e9..b206bb7aa 100644 --- a/src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h +++ b/src/PhysX/pxshared/include/foundation/windows/PxWindowsIntrinsics.h @@ -23,7 +23,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -// Copyright (c) 2008-2018 NVIDIA Corporation. All rights reserved. +// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. @@ -31,7 +31,7 @@ #define PXFOUNDATION_PXWINDOWSINTRINSICS_H #include "foundation/Px.h" -#include "foundation/PxAssert.h" +#include "foundation/PxSharedAssert.h" #if !PX_WINDOWS_FAMILY #error "This file should only be included by Windows builds!!" @@ -176,7 +176,7 @@ Set 128B to zero starting at \c dst+offset. Must be aligned. */ PX_FORCE_INLINE void memZero128(void* dest, uint32_t offset = 0) { - PX_ASSERT(((size_t(dest) + offset) & 0x7f) == 0); + PX_SHARED_ASSERT(((size_t(dest) + offset) & 0x7f) == 0); memSet(reinterpret_cast(dest) + offset, 0, 128); } From adf31c8f644838c083248eb226dfa9f6b499e866 Mon Sep 17 00:00:00 2001 From: erwincoumans Date: Wed, 20 Feb 2019 19:57:10 -0800 Subject: [PATCH 06/10] remove PhysXClient.*, it was not needed (use PhysicsDirect instead) --- .../SharedMemory/PhysicsClientSharedMemory2.h | 6 +- examples/SharedMemory/physx/PhysXC_API.cpp | 5 +- examples/SharedMemory/physx/PhysXClient.cpp | 1409 ----------------- examples/SharedMemory/physx/PhysXClient.h | 135 -- examples/pybullet/premake4.lua | 2 - setup.py | 1 - 6 files changed, 6 insertions(+), 1552 deletions(-) delete mode 100644 examples/SharedMemory/physx/PhysXClient.cpp delete mode 100644 examples/SharedMemory/physx/PhysXClient.h diff --git a/examples/SharedMemory/PhysicsClientSharedMemory2.h b/examples/SharedMemory/PhysicsClientSharedMemory2.h index 70a9fd0a7..473ee0ec1 100644 --- a/examples/SharedMemory/PhysicsClientSharedMemory2.h +++ b/examples/SharedMemory/PhysicsClientSharedMemory2.h @@ -1,5 +1,5 @@ -#ifndef PHYSICS_CLIENT_SHARED_MEMORY2_H -#define PHYSICS_CLIENT_SHARED_MEMORY2_H +#ifndef PHYSICS_CLIENT_SHARED_MEMORY3_H +#define PHYSICS_CLIENT_SHARED_MEMORY3_H #include "PhysicsDirect.h" @@ -14,4 +14,4 @@ public: void setSharedMemoryInterface(class SharedMemoryInterface* sharedMem); }; -#endif //PHYSICS_CLIENT_SHARED_MEMORY2_H \ No newline at end of file +#endif //PHYSICS_CLIENT_SHARED_MEMORY3_H \ No newline at end of file diff --git a/examples/SharedMemory/physx/PhysXC_API.cpp b/examples/SharedMemory/physx/PhysXC_API.cpp index bd32027cf..d8706eb47 100644 --- a/examples/SharedMemory/physx/PhysXC_API.cpp +++ b/examples/SharedMemory/physx/PhysXC_API.cpp @@ -1,13 +1,14 @@ #ifdef BT_ENABLE_PHYSX #include "PhysXC_API.h" +#include "../PhysicsDirect.h" #include "PhysXServerCommandProcessor.h" -#include "PhysXClient.h" + B3_SHARED_API b3PhysicsClientHandle b3ConnectPhysX() { PhysXServerCommandProcessor* sdk = new PhysXServerCommandProcessor; - PhysXClient* direct = new PhysXClient(sdk, true); + PhysicsDirect* direct = new PhysicsDirect(sdk, true); bool connected; connected = direct->connect(); return (b3PhysicsClientHandle)direct; diff --git a/examples/SharedMemory/physx/PhysXClient.cpp b/examples/SharedMemory/physx/PhysXClient.cpp deleted file mode 100644 index 611c53125..000000000 --- a/examples/SharedMemory/physx/PhysXClient.cpp +++ /dev/null @@ -1,1409 +0,0 @@ -#ifdef BT_ENABLE_PHYSX -#include "PhysXClient.h" - -#include "../PhysicsClientSharedMemory.h" -#include "../../CommonInterfaces/CommonGUIHelperInterface.h" -#include "../SharedMemoryCommands.h" -#include "../PhysicsCommandProcessorInterface.h" -#include "../../Utils/b3Clock.h" - -#include "LinearMath/btHashMap.h" -#include "LinearMath/btAlignedObjectArray.h" -#include "../../../Extras/Serialize/BulletFileLoader/btBulletFile.h" -#include "../../../Extras/Serialize/BulletFileLoader/autogenerated/bullet.h" -#include "../BodyJointInfoUtility.h" -#include - -#include "../SharedMemoryUserData.h" -#include "LinearMath/btQuickprof.h" - -struct PhysXUserDataCache -{ - btHashMap m_userDataMap; - btHashMap m_keyToUserDataIdMap; - - ~PhysXUserDataCache() - { - } -}; - -struct BodyJointInfoCache2 -{ - std::string m_baseName; - btAlignedObjectArray m_jointInfo; - std::string m_bodyName; - - // Joint index -> user data. - btHashMap m_jointToUserDataMap; - - ~BodyJointInfoCache2() - { - } -}; - -struct PhysXDirectInternalData -{ - DummyGUIHelper m_noGfx; - - btAlignedObjectArray m_serverDNA; - SharedMemoryCommand m_command; - SharedMemoryStatus m_serverStatus; - - SharedMemoryCommand m_tmpInfoRequestCommand; - SharedMemoryStatus m_tmpInfoStatus; - bool m_hasStatus; - bool m_verboseOutput; - - btAlignedObjectArray m_debugLinesFrom; - btAlignedObjectArray m_debugLinesTo; - btAlignedObjectArray m_debugLinesColor; - - btHashMap m_bodyJointMap; - btHashMap m_userConstraintInfoMap; - - btAlignedObjectArray m_profileTimings; - btHashMap m_profileTimingStringArray; - - char m_bulletStreamDataServerToClient[SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE]; - btAlignedObjectArray m_cachedMassMatrix; - int m_cachedCameraPixelsWidth; - int m_cachedCameraPixelsHeight; - btAlignedObjectArray m_cachedCameraPixelsRGBA; - btAlignedObjectArray m_cachedCameraDepthBuffer; - btAlignedObjectArray m_cachedSegmentationMask; - - btAlignedObjectArray m_cachedContactPoints; - btAlignedObjectArray m_cachedOverlappingObjects; - - btAlignedObjectArray m_cachedVisualShapes; - btAlignedObjectArray m_cachedCollisionShapes; - - btAlignedObjectArray m_cachedVREvents; - - btAlignedObjectArray m_cachedKeyboardEvents; - btAlignedObjectArray m_cachedMouseEvents; - - btAlignedObjectArray m_raycastHits; - - PhysicsCommandProcessorInterface* m_commandProcessor; - bool m_ownsCommandProcessor; - double m_timeOutInSeconds; - - PhysXDirectInternalData() - : m_hasStatus(false), - m_verboseOutput(false), - m_cachedCameraPixelsWidth(0), - m_cachedCameraPixelsHeight(0), - m_commandProcessor(NULL), - m_ownsCommandProcessor(false), - m_timeOutInSeconds(1e30) - { - memset(&m_command, 0, sizeof(m_command)); - memset(&m_serverStatus, 0, sizeof(m_serverStatus)); - memset(m_bulletStreamDataServerToClient, 0, sizeof(m_bulletStreamDataServerToClient)); - } -}; - -PhysXClient::PhysXClient(PhysicsCommandProcessorInterface* physSdk, bool passSdkOwnership) -{ - int sz = sizeof(SharedMemoryCommand); - int sz2 = sizeof(SharedMemoryStatus); - - m_data = new PhysXDirectInternalData; - m_data->m_commandProcessor = physSdk; - m_data->m_ownsCommandProcessor = passSdkOwnership; -} - -PhysXClient::~PhysXClient() -{ - for (int i = 0; i < m_data->m_profileTimingStringArray.size(); i++) - { - std::string** str = m_data->m_profileTimingStringArray.getAtIndex(i); - if (str) - { - delete *str; - } - } - m_data->m_profileTimingStringArray.clear(); - - if (m_data->m_commandProcessor->isConnected()) - { - m_data->m_commandProcessor->disconnect(); - } - if (m_data->m_ownsCommandProcessor) - { - delete m_data->m_commandProcessor; - } - - resetData(); - - delete m_data; -} - -void PhysXClient::resetData() -{ - m_data->m_debugLinesFrom.clear(); - m_data->m_debugLinesTo.clear(); - m_data->m_debugLinesColor.clear(); - for (int i = 0; i < m_data->m_bodyJointMap.size(); i++) - { - BodyJointInfoCache2** bodyJointsPtr = m_data->m_bodyJointMap.getAtIndex(i); - if (bodyJointsPtr && *bodyJointsPtr) - { - delete (*bodyJointsPtr); - } - } - m_data->m_bodyJointMap.clear(); - m_data->m_userConstraintInfoMap.clear(); -} - -// return true if connection succesfull, can also check 'isConnected' -bool PhysXClient::connect() -{ - bool connected = m_data->m_commandProcessor->connect(); - m_data->m_commandProcessor->setGuiHelper(&m_data->m_noGfx); - - if (connected) - //also request serialization data - { - SharedMemoryCommand command; - command.m_type = CMD_REQUEST_INTERNAL_DATA; - bool hasStatus = m_data->m_commandProcessor->processCommand(command, m_data->m_serverStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - if (hasStatus) - { - postProcessStatus(m_data->m_serverStatus); - } - else - { - b3Clock clock; - double timeSec = clock.getTimeInSeconds(); - - while ((!hasStatus) && (clock.getTimeInSeconds() - timeSec < 10)) - { - const SharedMemoryStatus* stat = processServerStatus(); - if (stat) - { - hasStatus = true; - } - } - } - } - - return connected; -} - -// return true if connection succesfull, can also check 'isConnected' -bool PhysXClient::connect(struct GUIHelperInterface* guiHelper) -{ - bool connected = m_data->m_commandProcessor->connect(); - - m_data->m_commandProcessor->setGuiHelper(guiHelper); - - return connected; -} - -void PhysXClient::renderScene() -{ - int renderFlags = 0; - m_data->m_commandProcessor->renderScene(renderFlags); -} - -void PhysXClient::debugDraw(int debugDrawMode) -{ - m_data->m_commandProcessor->physicsDebugDraw(debugDrawMode); -} - -////todo: rename to 'disconnect' -void PhysXClient::disconnectSharedMemory() -{ - m_data->m_commandProcessor->disconnect(); - m_data->m_commandProcessor->setGuiHelper(0); -} - -bool PhysXClient::isConnected() const -{ - return m_data->m_commandProcessor->isConnected(); -} - -// return non-null if there is a status, nullptr otherwise -const SharedMemoryStatus* PhysXClient::processServerStatus() -{ - if (!m_data->m_hasStatus) - { - m_data->m_hasStatus = m_data->m_commandProcessor->receiveStatus(m_data->m_serverStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - } - - SharedMemoryStatus* stat = 0; - if (m_data->m_hasStatus) - { - stat = &m_data->m_serverStatus; - - postProcessStatus(m_data->m_serverStatus); - - m_data->m_hasStatus = false; - } - return stat; -} - -SharedMemoryCommand* PhysXClient::getAvailableSharedMemoryCommand() -{ - return &m_data->m_command; -} - -bool PhysXClient::canSubmitCommand() const -{ - return m_data->m_commandProcessor->isConnected(); -} - -bool PhysXClient::processDebugLines(const struct SharedMemoryCommand& orgCommand) -{ - SharedMemoryCommand command = orgCommand; - - const SharedMemoryStatus& serverCmd = m_data->m_serverStatus; - - do - { - bool hasStatus = m_data->m_commandProcessor->processCommand(command, m_data->m_serverStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - - b3Clock clock; - double startTime = clock.getTimeInSeconds(); - double timeOutInSeconds = m_data->m_timeOutInSeconds; - - while ((!hasStatus) && (clock.getTimeInSeconds() - startTime < timeOutInSeconds)) - { - const SharedMemoryStatus* stat = processServerStatus(); - if (stat) - { - hasStatus = true; - } - } - - m_data->m_hasStatus = hasStatus; - - if (hasStatus) - { - btAssert(m_data->m_serverStatus.m_type == CMD_DEBUG_LINES_COMPLETED); - - if (m_data->m_verboseOutput) - { - b3Printf("Success receiving %d debug lines", - serverCmd.m_sendDebugLinesArgs.m_numDebugLines); - } - - int numLines = serverCmd.m_sendDebugLinesArgs.m_numDebugLines; - float* linesFrom = - (float*)&m_data->m_bulletStreamDataServerToClient[0]; - float* linesTo = - (float*)(&m_data->m_bulletStreamDataServerToClient[0] + - numLines * 3 * sizeof(float)); - float* linesColor = - (float*)(&m_data->m_bulletStreamDataServerToClient[0] + - 2 * numLines * 3 * sizeof(float)); - - m_data->m_debugLinesFrom.resize(serverCmd.m_sendDebugLinesArgs.m_startingLineIndex + - numLines); - m_data->m_debugLinesTo.resize(serverCmd.m_sendDebugLinesArgs.m_startingLineIndex + - numLines); - m_data->m_debugLinesColor.resize( - serverCmd.m_sendDebugLinesArgs.m_startingLineIndex + numLines); - - for (int i = 0; i < numLines; i++) - { - TmpFloat3 from = CreateTmpFloat3(linesFrom[i * 3], linesFrom[i * 3 + 1], - linesFrom[i * 3 + 2]); - TmpFloat3 to = - CreateTmpFloat3(linesTo[i * 3], linesTo[i * 3 + 1], linesTo[i * 3 + 2]); - TmpFloat3 color = CreateTmpFloat3(linesColor[i * 3], linesColor[i * 3 + 1], - linesColor[i * 3 + 2]); - - m_data - ->m_debugLinesFrom[serverCmd.m_sendDebugLinesArgs.m_startingLineIndex + i] = - from; - m_data->m_debugLinesTo[serverCmd.m_sendDebugLinesArgs.m_startingLineIndex + i] = - to; - m_data->m_debugLinesColor[serverCmd.m_sendDebugLinesArgs.m_startingLineIndex + - i] = color; - } - - if (serverCmd.m_sendDebugLinesArgs.m_numRemainingDebugLines > 0) - { - m_data->m_hasStatus = false; - - command.m_type = CMD_REQUEST_DEBUG_LINES; - command.m_requestDebugLinesArguments.m_startingLineIndex = - serverCmd.m_sendDebugLinesArgs.m_numDebugLines + - serverCmd.m_sendDebugLinesArgs.m_startingLineIndex; - } - } - - } while (serverCmd.m_sendDebugLinesArgs.m_numRemainingDebugLines > 0); - - return m_data->m_hasStatus; -} - -bool PhysXClient::processVisualShapeData(const struct SharedMemoryCommand& orgCommand) -{ - SharedMemoryCommand command = orgCommand; - const SharedMemoryStatus& serverCmd = m_data->m_serverStatus; - - do - { - bool hasStatus = m_data->m_commandProcessor->processCommand(command, m_data->m_serverStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - - b3Clock clock; - double startTime = clock.getTimeInSeconds(); - double timeOutInSeconds = m_data->m_timeOutInSeconds; - - while ((!hasStatus) && (clock.getTimeInSeconds() - startTime < timeOutInSeconds)) - { - const SharedMemoryStatus* stat = processServerStatus(); - if (stat) - { - hasStatus = true; - } - } - - m_data->m_hasStatus = hasStatus; - if (hasStatus) - { - if (m_data->m_verboseOutput) - { - b3Printf("Visual Shape Information Request OK\n"); - } - int startVisualShapeIndex = serverCmd.m_sendVisualShapeArgs.m_startingVisualShapeIndex; - int numVisualShapesCopied = serverCmd.m_sendVisualShapeArgs.m_numVisualShapesCopied; - m_data->m_cachedVisualShapes.resize(startVisualShapeIndex + numVisualShapesCopied); - b3VisualShapeData* shapeData = (b3VisualShapeData*)&m_data->m_bulletStreamDataServerToClient[0]; - for (int i = 0; i < numVisualShapesCopied; i++) - { - m_data->m_cachedVisualShapes[startVisualShapeIndex + i] = shapeData[i]; - } - - if (serverCmd.m_sendVisualShapeArgs.m_numRemainingVisualShapes > 0 && serverCmd.m_sendVisualShapeArgs.m_numVisualShapesCopied) - { - m_data->m_hasStatus = false; - - command.m_type = CMD_REQUEST_VISUAL_SHAPE_INFO; - command.m_requestVisualShapeDataArguments.m_startingVisualShapeIndex = serverCmd.m_sendVisualShapeArgs.m_startingVisualShapeIndex + serverCmd.m_sendVisualShapeArgs.m_numVisualShapesCopied; - command.m_requestVisualShapeDataArguments.m_bodyUniqueId = serverCmd.m_sendVisualShapeArgs.m_bodyUniqueId; - } - } - } while (serverCmd.m_sendVisualShapeArgs.m_numRemainingVisualShapes > 0 && serverCmd.m_sendVisualShapeArgs.m_numVisualShapesCopied); - - return m_data->m_hasStatus; -} - -bool PhysXClient::processOverlappingObjects(const struct SharedMemoryCommand& orgCommand) -{ - SharedMemoryCommand command = orgCommand; - - const SharedMemoryStatus& serverCmd = m_data->m_serverStatus; - - do - { - bool hasStatus = m_data->m_commandProcessor->processCommand(command, m_data->m_serverStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - - b3Clock clock; - double startTime = clock.getTimeInSeconds(); - double timeOutInSeconds = m_data->m_timeOutInSeconds; - - while ((!hasStatus) && (clock.getTimeInSeconds() - startTime < timeOutInSeconds)) - { - const SharedMemoryStatus* stat = processServerStatus(); - if (stat) - { - hasStatus = true; - } - } - - m_data->m_hasStatus = hasStatus; - if (hasStatus) - { - if (m_data->m_verboseOutput) - { - b3Printf("Overlapping Objects Request OK\n"); - } - - int startOverlapIndex = serverCmd.m_sendOverlappingObjectsArgs.m_startingOverlappingObjectIndex; - int numOverlapCopied = serverCmd.m_sendOverlappingObjectsArgs.m_numOverlappingObjectsCopied; - m_data->m_cachedOverlappingObjects.resize(startOverlapIndex + numOverlapCopied); - b3OverlappingObject* objects = (b3OverlappingObject*)&m_data->m_bulletStreamDataServerToClient[0]; - - for (int i = 0; i < numOverlapCopied; i++) - { - m_data->m_cachedOverlappingObjects[startOverlapIndex + i] = objects[i]; - } - - if (serverCmd.m_sendOverlappingObjectsArgs.m_numRemainingOverlappingObjects > 0 && serverCmd.m_sendOverlappingObjectsArgs.m_numOverlappingObjectsCopied) - { - m_data->m_hasStatus = false; - command.m_type = CMD_REQUEST_AABB_OVERLAP; - command.m_requestOverlappingObjectsArgs.m_startingOverlappingObjectIndex = serverCmd.m_sendOverlappingObjectsArgs.m_startingOverlappingObjectIndex + serverCmd.m_sendOverlappingObjectsArgs.m_numOverlappingObjectsCopied; - } - } - } while (serverCmd.m_sendOverlappingObjectsArgs.m_numRemainingOverlappingObjects > 0 && serverCmd.m_sendOverlappingObjectsArgs.m_numOverlappingObjectsCopied); - - return m_data->m_hasStatus; -} - -bool PhysXClient::processContactPointData(const struct SharedMemoryCommand& orgCommand) -{ - SharedMemoryCommand command = orgCommand; - - const SharedMemoryStatus& serverCmd = m_data->m_serverStatus; - - do - { - bool hasStatus = m_data->m_commandProcessor->processCommand(command, m_data->m_serverStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - - b3Clock clock; - double startTime = clock.getTimeInSeconds(); - double timeOutInSeconds = m_data->m_timeOutInSeconds; - - while ((!hasStatus) && (clock.getTimeInSeconds() - startTime < timeOutInSeconds)) - { - const SharedMemoryStatus* stat = processServerStatus(); - if (stat) - { - hasStatus = true; - } - } - - m_data->m_hasStatus = hasStatus; - if (hasStatus) - { - if (m_data->m_verboseOutput) - { - b3Printf("Contact Point Information Request OK\n"); - } - int startContactIndex = serverCmd.m_sendContactPointArgs.m_startingContactPointIndex; - int numContactsCopied = serverCmd.m_sendContactPointArgs.m_numContactPointsCopied; - - m_data->m_cachedContactPoints.resize(startContactIndex + numContactsCopied); - - b3ContactPointData* contactData = (b3ContactPointData*)&m_data->m_bulletStreamDataServerToClient[0]; - - for (int i = 0; i < numContactsCopied; i++) - { - m_data->m_cachedContactPoints[startContactIndex + i] = contactData[i]; - } - - if (serverCmd.m_sendContactPointArgs.m_numRemainingContactPoints > 0 && serverCmd.m_sendContactPointArgs.m_numContactPointsCopied) - { - m_data->m_hasStatus = false; - - command.m_type = CMD_REQUEST_CONTACT_POINT_INFORMATION; - command.m_requestContactPointArguments.m_startingContactPointIndex = serverCmd.m_sendContactPointArgs.m_startingContactPointIndex + serverCmd.m_sendContactPointArgs.m_numContactPointsCopied; - command.m_requestContactPointArguments.m_objectAIndexFilter = -1; - command.m_requestContactPointArguments.m_objectBIndexFilter = -1; - } - } - } while (serverCmd.m_sendContactPointArgs.m_numRemainingContactPoints > 0 && serverCmd.m_sendContactPointArgs.m_numContactPointsCopied); - - return m_data->m_hasStatus; -} - -bool PhysXClient::processCamera(const struct SharedMemoryCommand& orgCommand) -{ - SharedMemoryCommand command = orgCommand; - - const SharedMemoryStatus& serverCmd = m_data->m_serverStatus; - - do - { - bool hasStatus = m_data->m_commandProcessor->processCommand(command, m_data->m_serverStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - - b3Clock clock; - double startTime = clock.getTimeInSeconds(); - double timeOutInSeconds = m_data->m_timeOutInSeconds; - - while ((!hasStatus) && (clock.getTimeInSeconds() - startTime < timeOutInSeconds)) - { - const SharedMemoryStatus* stat = processServerStatus(); - if (stat) - { - hasStatus = true; - } - } - - m_data->m_hasStatus = hasStatus; - if (hasStatus) - { - btAssert(m_data->m_serverStatus.m_type == CMD_CAMERA_IMAGE_COMPLETED); - - if (m_data->m_verboseOutput) - { - b3Printf("Camera image OK\n"); - } - - int numBytesPerPixel = 4; //RGBA - int numTotalPixels = serverCmd.m_sendPixelDataArguments.m_startingPixelIndex + - serverCmd.m_sendPixelDataArguments.m_numPixelsCopied + - serverCmd.m_sendPixelDataArguments.m_numRemainingPixels; - - m_data->m_cachedCameraPixelsWidth = 0; - m_data->m_cachedCameraPixelsHeight = 0; - - int numPixels = serverCmd.m_sendPixelDataArguments.m_imageWidth * serverCmd.m_sendPixelDataArguments.m_imageHeight; - - m_data->m_cachedCameraPixelsRGBA.reserve(numPixels * numBytesPerPixel); - m_data->m_cachedCameraDepthBuffer.resize(numTotalPixels); - m_data->m_cachedSegmentationMask.resize(numTotalPixels); - m_data->m_cachedCameraPixelsRGBA.resize(numTotalPixels * numBytesPerPixel); - - unsigned char* rgbaPixelsReceived = - (unsigned char*)&m_data->m_bulletStreamDataServerToClient[0]; - - float* depthBuffer = (float*)&(m_data->m_bulletStreamDataServerToClient[serverCmd.m_sendPixelDataArguments.m_numPixelsCopied * 4]); - int* segmentationMaskBuffer = (int*)&(m_data->m_bulletStreamDataServerToClient[serverCmd.m_sendPixelDataArguments.m_numPixelsCopied * 8]); - - // printf("pixel = %d\n", rgbaPixelsReceived[0]); - - for (int i = 0; i < serverCmd.m_sendPixelDataArguments.m_numPixelsCopied; i++) - { - m_data->m_cachedCameraDepthBuffer[i + serverCmd.m_sendPixelDataArguments.m_startingPixelIndex] = depthBuffer[i]; - } - for (int i = 0; i < serverCmd.m_sendPixelDataArguments.m_numPixelsCopied; i++) - { - m_data->m_cachedSegmentationMask[i + serverCmd.m_sendPixelDataArguments.m_startingPixelIndex] = segmentationMaskBuffer[i]; - } - for (int i = 0; i < serverCmd.m_sendPixelDataArguments.m_numPixelsCopied * numBytesPerPixel; i++) - { - m_data->m_cachedCameraPixelsRGBA[i + serverCmd.m_sendPixelDataArguments.m_startingPixelIndex * numBytesPerPixel] = rgbaPixelsReceived[i]; - } - - if (serverCmd.m_sendPixelDataArguments.m_numRemainingPixels > 0 && serverCmd.m_sendPixelDataArguments.m_numPixelsCopied) - { - m_data->m_hasStatus = false; - - // continue requesting remaining pixels - command.m_type = CMD_REQUEST_CAMERA_IMAGE_DATA; - command.m_requestPixelDataArguments.m_startPixelIndex = - serverCmd.m_sendPixelDataArguments.m_startingPixelIndex + - serverCmd.m_sendPixelDataArguments.m_numPixelsCopied; - } - else - { - m_data->m_cachedCameraPixelsWidth = serverCmd.m_sendPixelDataArguments.m_imageWidth; - m_data->m_cachedCameraPixelsHeight = serverCmd.m_sendPixelDataArguments.m_imageHeight; - } - } - } while (serverCmd.m_sendPixelDataArguments.m_numRemainingPixels > 0 && serverCmd.m_sendPixelDataArguments.m_numPixelsCopied); - - return m_data->m_hasStatus; -} - -void PhysXClient::processBodyJointInfo(int bodyUniqueId, const SharedMemoryStatus& serverCmd) -{ - BodyJointInfoCache2** cachePtr = m_data->m_bodyJointMap[bodyUniqueId]; - //don't process same bodyUniqueId multiple times - if (cachePtr) - { - return; - } - - bParse::btBulletFile bf( - &m_data->m_bulletStreamDataServerToClient[0], - serverCmd.m_numDataStreamBytes); - if (m_data->m_serverDNA.size()) - { - bf.setFileDNA(false, &m_data->m_serverDNA[0], m_data->m_serverDNA.size()); - } - else - { - bf.setFileDNAisMemoryDNA(); - } - bf.parse(false); - - if (bf.ok()) - { - BodyJointInfoCache2* bodyJoints = new BodyJointInfoCache2; - m_data->m_bodyJointMap.insert(bodyUniqueId, bodyJoints); - bodyJoints->m_bodyName = serverCmd.m_dataStreamArguments.m_bodyName; - - for (int i = 0; i < bf.m_multiBodies.size(); i++) - { - int flag = bf.getFlags(); - if ((flag & bParse::FD_DOUBLE_PRECISION) != 0) - { - Bullet::btMultiBodyDoubleData* mb = - (Bullet::btMultiBodyDoubleData*)bf.m_multiBodies[i]; - - if (mb->m_baseName) - { - bodyJoints->m_baseName = mb->m_baseName; - } - addJointInfoFromMultiBodyData(mb, bodyJoints, m_data->m_verboseOutput); - } - else - { - Bullet::btMultiBodyFloatData* mb = - (Bullet::btMultiBodyFloatData*)bf.m_multiBodies[i]; - - if (mb->m_baseName) - { - bodyJoints->m_baseName = mb->m_baseName; - } - addJointInfoFromMultiBodyData(mb, bodyJoints, m_data->m_verboseOutput); - } - } - - if (m_data->m_verboseOutput) - { - b3Printf("Received robot description ok!\n"); - } - } - else - { - b3Warning("Robot description not received"); - } -} - -void PhysXClient::processAddUserData(const struct SharedMemoryStatus& serverCmd) -{ - -} - -void PhysXClient::postProcessStatus(const struct SharedMemoryStatus& serverCmd) -{ - switch (serverCmd.m_type) - { - case CMD_REQUEST_RAY_CAST_INTERSECTIONS_COMPLETED: - { - if (m_data->m_verboseOutput) - { - b3Printf("Raycast completed"); - } - m_data->m_raycastHits.clear(); - b3RayHitInfo* rayHits = (b3RayHitInfo*)m_data->m_bulletStreamDataServerToClient; - for (int i = 0; i < serverCmd.m_raycastHits.m_numRaycastHits; i++) - { - m_data->m_raycastHits.push_back(rayHits[i]); - } - break; - } - case CMD_REQUEST_VR_EVENTS_DATA_COMPLETED: - { - if (m_data->m_verboseOutput) - { - b3Printf("Request VR Events completed"); - } - m_data->m_cachedVREvents.resize(serverCmd.m_sendVREvents.m_numVRControllerEvents); - for (int i = 0; i < serverCmd.m_sendVREvents.m_numVRControllerEvents; i++) - { - m_data->m_cachedVREvents[i] = serverCmd.m_sendVREvents.m_controllerEvents[i]; - } - break; - } - case CMD_REQUEST_KEYBOARD_EVENTS_DATA_COMPLETED: - { - if (m_data->m_verboseOutput) - { - b3Printf("Request keyboard events completed"); - } - m_data->m_cachedKeyboardEvents.resize(serverCmd.m_sendKeyboardEvents.m_numKeyboardEvents); - for (int i = 0; i < serverCmd.m_sendKeyboardEvents.m_numKeyboardEvents; i++) - { - m_data->m_cachedKeyboardEvents[i] = serverCmd.m_sendKeyboardEvents.m_keyboardEvents[i]; - } - break; - } - - case CMD_REQUEST_MOUSE_EVENTS_DATA_COMPLETED: - { - B3_PROFILE("CMD_REQUEST_MOUSE_EVENTS_DATA_COMPLETED"); - if (m_data->m_verboseOutput) - { - b3Printf("Request mouse events completed"); - } - m_data->m_cachedMouseEvents.resize(serverCmd.m_sendMouseEvents.m_numMouseEvents); - for (int i = 0; i < serverCmd.m_sendMouseEvents.m_numMouseEvents; i++) - { - m_data->m_cachedMouseEvents[i] = serverCmd.m_sendMouseEvents.m_mouseEvents[i]; - } - break; - } - - case CMD_REQUEST_INTERNAL_DATA_COMPLETED: - { - if (serverCmd.m_numDataStreamBytes) - { - int numStreamBytes = serverCmd.m_numDataStreamBytes; - m_data->m_serverDNA.resize(numStreamBytes); - for (int i = 0; i < numStreamBytes; i++) - { - m_data->m_serverDNA[i] = m_data->m_bulletStreamDataServerToClient[i]; - } - } - break; - } - case CMD_RESET_SIMULATION_COMPLETED: - { - resetData(); - break; - } - - case CMD_USER_CONSTRAINT_INFO_COMPLETED: - case CMD_USER_CONSTRAINT_COMPLETED: - { - int cid = serverCmd.m_userConstraintResultArgs.m_userConstraintUniqueId; - m_data->m_userConstraintInfoMap.insert(cid, serverCmd.m_userConstraintResultArgs); - break; - } - case CMD_REMOVE_USER_CONSTRAINT_COMPLETED: - { - int cid = serverCmd.m_userConstraintResultArgs.m_userConstraintUniqueId; - m_data->m_userConstraintInfoMap.remove(cid); - break; - } - case CMD_REMOVE_BODY_FAILED: - { - b3Warning("Remove body failed\n"); - break; - } - case CMD_REMOVE_BODY_COMPLETED: - { - for (int i = 0; i < serverCmd.m_removeObjectArgs.m_numBodies; i++) - { - int bodyUniqueId = serverCmd.m_removeObjectArgs.m_bodyUniqueIds[i]; - removeCachedBody(bodyUniqueId); - } - for (int i = 0; i < serverCmd.m_removeObjectArgs.m_numUserConstraints; i++) - { - int key = serverCmd.m_removeObjectArgs.m_userConstraintUniqueIds[i]; - m_data->m_userConstraintInfoMap.remove(key); - } - - break; - } - case CMD_CHANGE_USER_CONSTRAINT_COMPLETED: - { - int cid = serverCmd.m_userConstraintResultArgs.m_userConstraintUniqueId; - b3UserConstraint* userConstraintPtr = m_data->m_userConstraintInfoMap[cid]; - if (userConstraintPtr) - { - const b3UserConstraint* serverConstraint = &serverCmd.m_userConstraintResultArgs; - if (serverCmd.m_updateFlags & USER_CONSTRAINT_CHANGE_PIVOT_IN_B) - { - userConstraintPtr->m_childFrame[0] = serverConstraint->m_childFrame[0]; - userConstraintPtr->m_childFrame[1] = serverConstraint->m_childFrame[1]; - userConstraintPtr->m_childFrame[2] = serverConstraint->m_childFrame[2]; - } - if (serverCmd.m_updateFlags & USER_CONSTRAINT_CHANGE_FRAME_ORN_IN_B) - { - userConstraintPtr->m_childFrame[3] = serverConstraint->m_childFrame[3]; - userConstraintPtr->m_childFrame[4] = serverConstraint->m_childFrame[4]; - userConstraintPtr->m_childFrame[5] = serverConstraint->m_childFrame[5]; - userConstraintPtr->m_childFrame[6] = serverConstraint->m_childFrame[6]; - } - if (serverCmd.m_updateFlags & USER_CONSTRAINT_CHANGE_MAX_FORCE) - { - userConstraintPtr->m_maxAppliedForce = serverConstraint->m_maxAppliedForce; - } - if (serverCmd.m_updateFlags & USER_CONSTRAINT_CHANGE_GEAR_RATIO) - { - userConstraintPtr->m_gearRatio = serverConstraint->m_gearRatio; - } - if (serverCmd.m_updateFlags & USER_CONSTRAINT_CHANGE_RELATIVE_POSITION_TARGET) - { - userConstraintPtr->m_relativePositionTarget = serverConstraint->m_relativePositionTarget; - } - if (serverCmd.m_updateFlags & USER_CONSTRAINT_CHANGE_ERP) - { - userConstraintPtr->m_erp = serverConstraint->m_erp; - } - if (serverCmd.m_updateFlags & USER_CONSTRAINT_CHANGE_GEAR_AUX_LINK) - { - userConstraintPtr->m_gearAuxLink = serverConstraint->m_gearAuxLink; - } - } - break; - } - case CMD_USER_CONSTRAINT_REQUEST_STATE_COMPLETED: - { - break; - } - case CMD_SYNC_BODY_INFO_COMPLETED: - case CMD_MJCF_LOADING_COMPLETED: - case CMD_SDF_LOADING_COMPLETED: - { - //we'll stream further info from the physics server - //so serverCmd will be invalid, make a copy - - int numConstraints = serverCmd.m_sdfLoadedArgs.m_numUserConstraints; - for (int i = 0; i < numConstraints; i++) - { - int constraintUid = serverCmd.m_sdfLoadedArgs.m_userConstraintUniqueIds[i]; - - m_data->m_tmpInfoRequestCommand.m_type = CMD_USER_CONSTRAINT; - m_data->m_tmpInfoRequestCommand.m_updateFlags = USER_CONSTRAINT_REQUEST_INFO; - m_data->m_tmpInfoRequestCommand.m_userConstraintArguments.m_userConstraintUniqueId = constraintUid; - - bool hasStatus = m_data->m_commandProcessor->processCommand(m_data->m_tmpInfoRequestCommand, m_data->m_tmpInfoStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - - b3Clock clock; - double startTime = clock.getTimeInSeconds(); - double timeOutInSeconds = m_data->m_timeOutInSeconds; - - while ((!hasStatus) && (clock.getTimeInSeconds() - startTime < timeOutInSeconds)) - { - hasStatus = m_data->m_commandProcessor->receiveStatus(m_data->m_tmpInfoStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - } - - if (hasStatus) - { - int cid = m_data->m_tmpInfoStatus.m_userConstraintResultArgs.m_userConstraintUniqueId; - m_data->m_userConstraintInfoMap.insert(cid, m_data->m_tmpInfoStatus.m_userConstraintResultArgs); - } - } - - int numBodies = serverCmd.m_sdfLoadedArgs.m_numBodies; - for (int i = 0; i < numBodies; i++) - { - int bodyUniqueId = serverCmd.m_sdfLoadedArgs.m_bodyUniqueIds[i]; - - m_data->m_tmpInfoRequestCommand.m_type = CMD_REQUEST_BODY_INFO; - m_data->m_tmpInfoRequestCommand.m_sdfRequestInfoArgs.m_bodyUniqueId = bodyUniqueId; - - bool hasStatus = m_data->m_commandProcessor->processCommand(m_data->m_tmpInfoRequestCommand, m_data->m_tmpInfoStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - - b3Clock clock; - double startTime = clock.getTimeInSeconds(); - double timeOutInSeconds = m_data->m_timeOutInSeconds; - - while ((!hasStatus) && (clock.getTimeInSeconds() - startTime < timeOutInSeconds)) - { - hasStatus = m_data->m_commandProcessor->receiveStatus(m_data->m_tmpInfoStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - } - - if (hasStatus) - { - processBodyJointInfo(bodyUniqueId, m_data->m_tmpInfoStatus); - } - } - break; - } - case CMD_CREATE_MULTI_BODY_COMPLETED: - case CMD_URDF_LOADING_COMPLETED: - { - if (serverCmd.m_numDataStreamBytes > 0) - { - int bodyIndex = serverCmd.m_dataStreamArguments.m_bodyUniqueId; - processBodyJointInfo(bodyIndex, serverCmd); - } - break; - } - case CMD_BULLET_LOADING_FAILED: - { - b3Warning("Couldn't load .bullet file"); - break; - } - case CMD_BULLET_LOADING_COMPLETED: - { - break; - } - - case CMD_REQUEST_OPENGL_VISUALIZER_CAMERA_COMPLETED: - { - break; - } - - case CMD_REQUEST_OPENGL_VISUALIZER_CAMERA_FAILED: - { - b3Warning("requestOpenGLVisualizeCamera failed"); - break; - } - case CMD_REMOVE_USER_CONSTRAINT_FAILED: - { - b3Warning("removeConstraint failed"); - break; - } - case CMD_CHANGE_USER_CONSTRAINT_FAILED: - { - //b3Warning("changeConstraint failed"); - break; - } - - case CMD_USER_CONSTRAINT_FAILED: - { - b3Warning("createConstraint failed"); - break; - } - - case CMD_CREATE_COLLISION_SHAPE_FAILED: - { - b3Warning("createCollisionShape failed"); - break; - } - case CMD_CREATE_COLLISION_SHAPE_COMPLETED: - { - break; - } - - case CMD_CREATE_VISUAL_SHAPE_FAILED: - { - b3Warning("createVisualShape failed"); - break; - } - case CMD_CREATE_VISUAL_SHAPE_COMPLETED: - { - break; - } - - case CMD_CREATE_MULTI_BODY_FAILED: - { - b3Warning("createMultiBody failed"); - break; - } - case CMD_REQUEST_COLLISION_INFO_COMPLETED: - { - break; - } - case CMD_REQUEST_COLLISION_INFO_FAILED: - { - b3Warning("Request getCollisionInfo failed"); - break; - } - - case CMD_CUSTOM_COMMAND_COMPLETED: - { - break; - } - case CMD_CUSTOM_COMMAND_FAILED: - { - b3Warning("custom plugin command failed"); - break; - } - case CMD_CLIENT_COMMAND_COMPLETED: - { - break; - } - case CMD_CALCULATED_JACOBIAN_COMPLETED: - { - break; - } - case CMD_CALCULATED_JACOBIAN_FAILED: - { - b3Warning("jacobian calculation failed"); - break; - } - case CMD_CALCULATED_MASS_MATRIX_FAILED: - { - b3Warning("calculate mass matrix failed"); - break; - } - case CMD_CALCULATED_MASS_MATRIX_COMPLETED: - { - double* matrixData = (double*)&m_data->m_bulletStreamDataServerToClient[0]; - m_data->m_cachedMassMatrix.resize(serverCmd.m_massMatrixResultArgs.m_dofCount * serverCmd.m_massMatrixResultArgs.m_dofCount); - for (int i = 0; i < serverCmd.m_massMatrixResultArgs.m_dofCount * serverCmd.m_massMatrixResultArgs.m_dofCount; i++) - { - m_data->m_cachedMassMatrix[i] = matrixData[i]; - } - break; - } - case CMD_ACTUAL_STATE_UPDATE_COMPLETED: - { - break; - } - case CMD_DESIRED_STATE_RECEIVED_COMPLETED: - { - break; - } - case CMD_STEP_FORWARD_SIMULATION_COMPLETED: - { - break; - } - case CMD_REQUEST_PHYSICS_SIMULATION_PARAMETERS_COMPLETED: - { - break; - } - case CMD_SAVE_STATE_COMPLETED: - { - break; - } - case CMD_COLLISION_SHAPE_INFO_FAILED: - { - b3Warning("getCollisionShapeData failed"); - break; - } - case CMD_COLLISION_SHAPE_INFO_COMPLETED: - { - B3_PROFILE("CMD_COLLISION_SHAPE_INFO_COMPLETED"); - if (m_data->m_verboseOutput) - { - b3Printf("Collision Shape Information Request OK\n"); - } - int numCollisionShapesCopied = serverCmd.m_sendCollisionShapeArgs.m_numCollisionShapes; - m_data->m_cachedCollisionShapes.resize(numCollisionShapesCopied); - b3CollisionShapeData* shapeData = (b3CollisionShapeData*)&m_data->m_bulletStreamDataServerToClient[0]; - for (int i = 0; i < numCollisionShapesCopied; i++) - { - m_data->m_cachedCollisionShapes[i] = shapeData[i]; - } - break; - } - case CMD_RESTORE_STATE_FAILED: - { - b3Warning("restoreState failed"); - break; - } - case CMD_RESTORE_STATE_COMPLETED: - { - break; - } - case CMD_BULLET_SAVING_COMPLETED: - { - break; - } - case CMD_LOAD_SOFT_BODY_FAILED: - { - b3Warning("loadSoftBody failed"); - break; - } - case CMD_LOAD_SOFT_BODY_COMPLETED: - { - break; - } - case CMD_SYNC_USER_DATA_FAILED: - { - b3Warning("Synchronizing user data failed."); - break; - } - case CMD_ADD_USER_DATA_FAILED: - { - b3Warning("Adding user data failed (do the specified body and link exist?)"); - break; - } - case CMD_REMOVE_USER_DATA_FAILED: - { - b3Warning("Removing user data failed"); - break; - } - case CMD_ADD_USER_DATA_COMPLETED: - { - processAddUserData(serverCmd); - break; - } - case CMD_SYNC_USER_DATA_COMPLETED: - { - B3_PROFILE("CMD_SYNC_USER_DATA_COMPLETED"); - break; - } - case CMD_REMOVE_USER_DATA_COMPLETED: - { - break; - } - default: - { - //b3Warning("Unknown server status type"); - } - }; -} -bool PhysXClient::submitClientCommand(const struct SharedMemoryCommand& command) -{ - if (command.m_type == CMD_REQUEST_DEBUG_LINES) - { - return processDebugLines(command); - } - - if (command.m_type == CMD_REQUEST_CAMERA_IMAGE_DATA) - { - return processCamera(command); - } - if (command.m_type == CMD_REQUEST_CONTACT_POINT_INFORMATION) - { - return processContactPointData(command); - } - - if (command.m_type == CMD_REQUEST_VISUAL_SHAPE_INFO) - { - return processVisualShapeData(command); - } - if (command.m_type == CMD_REQUEST_AABB_OVERLAP) - { - return processOverlappingObjects(command); - } - - bool hasStatus = m_data->m_commandProcessor->processCommand(command, m_data->m_serverStatus, &m_data->m_bulletStreamDataServerToClient[0], SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE); - m_data->m_hasStatus = hasStatus; - /*if (hasStatus) - { - postProcessStatus(m_data->m_serverStatus); - m_data->m_hasStatus = false; - } - */ - return hasStatus; -} - -int PhysXClient::getNumBodies() const -{ - return m_data->m_bodyJointMap.size(); -} - -void PhysXClient::removeCachedBody(int bodyUniqueId) -{ - BodyJointInfoCache2** bodyJointsPtr = m_data->m_bodyJointMap[bodyUniqueId]; - if (bodyJointsPtr && *bodyJointsPtr) - { - delete (*bodyJointsPtr); - m_data->m_bodyJointMap.remove(bodyUniqueId); - } -} - -int PhysXClient::getNumUserConstraints() const -{ - return m_data->m_userConstraintInfoMap.size(); -} - -int PhysXClient::getUserConstraintInfo(int constraintUniqueId, struct b3UserConstraint& info) const -{ - b3UserConstraint* constraintPtr = m_data->m_userConstraintInfoMap[constraintUniqueId]; - if (constraintPtr) - { - info = *constraintPtr; - return 1; - } - return 0; -} - -int PhysXClient::getUserConstraintId(int serialIndex) const -{ - if ((serialIndex >= 0) && (serialIndex < getNumUserConstraints())) - { - return m_data->m_userConstraintInfoMap.getKeyAtIndex(serialIndex).getUid1(); - } - return -1; -} - -int PhysXClient::getBodyUniqueId(int serialIndex) const -{ - if ((serialIndex >= 0) && (serialIndex < getNumBodies())) - { - return m_data->m_bodyJointMap.getKeyAtIndex(serialIndex).getUid1(); - } - return -1; -} - -bool PhysXClient::getBodyInfo(int bodyUniqueId, struct b3BodyInfo& info) const -{ - BodyJointInfoCache2** bodyJointsPtr = m_data->m_bodyJointMap[bodyUniqueId]; - if (bodyJointsPtr && *bodyJointsPtr) - { - BodyJointInfoCache2* bodyJoints = *bodyJointsPtr; - strcpy(info.m_baseName, bodyJoints->m_baseName.c_str()); - strcpy(info.m_bodyName, bodyJoints->m_bodyName.c_str()); - return true; - } - - return false; -} - -int PhysXClient::getNumJoints(int bodyIndex) const -{ - BodyJointInfoCache2** bodyJointsPtr = m_data->m_bodyJointMap[bodyIndex]; - if (bodyJointsPtr && *bodyJointsPtr) - { - BodyJointInfoCache2* bodyJoints = *bodyJointsPtr; - return bodyJoints->m_jointInfo.size(); - } - btAssert(0); - return 0; -} - -bool PhysXClient::getJointInfo(int bodyIndex, int jointIndex, struct b3JointInfo& info) const -{ - BodyJointInfoCache2** bodyJointsPtr = m_data->m_bodyJointMap[bodyIndex]; - if (bodyJointsPtr && *bodyJointsPtr) - { - BodyJointInfoCache2* bodyJoints = *bodyJointsPtr; - if ((jointIndex >= 0) && (jointIndex < bodyJoints->m_jointInfo.size())) - { - info = bodyJoints->m_jointInfo[jointIndex]; - return true; - } - } - return false; -} - -void PhysXClient::setSharedMemoryKey(int key) -{ -} - -void PhysXClient::uploadBulletFileToSharedMemory(const char* data, int len) -{ - if (len > SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE) - { - len = SHARED_MEMORY_MAX_STREAM_CHUNK_SIZE; - } - for (int i = 0; i < len; i++) - { - m_data->m_bulletStreamDataServerToClient[i] = data[i]; - } - //m_data->m_physicsClient->uploadBulletFileToSharedMemory(data,len); -} - -void PhysXClient::uploadRaysToSharedMemory(struct SharedMemoryCommand& command, const double* rayFromWorldArray, const double* rayToWorldArray, int numRays) -{ - int curNumStreamingRays = command.m_requestRaycastIntersections.m_numStreamingRays; - int newNumRays = curNumStreamingRays + numRays; - btAssert(newNumRays < MAX_RAY_INTERSECTION_BATCH_SIZE_STREAMING); - - if (newNumRays < MAX_RAY_INTERSECTION_BATCH_SIZE_STREAMING) - { - for (int i = 0; i < numRays; i++) - { - b3RayData* rayDataStream = (b3RayData*)m_data->m_bulletStreamDataServerToClient; - rayDataStream[curNumStreamingRays + i].m_rayFromPosition[0] = rayFromWorldArray[i * 3 + 0]; - rayDataStream[curNumStreamingRays + i].m_rayFromPosition[1] = rayFromWorldArray[i * 3 + 1]; - rayDataStream[curNumStreamingRays + i].m_rayFromPosition[2] = rayFromWorldArray[i * 3 + 2]; - rayDataStream[curNumStreamingRays + i].m_rayToPosition[0] = rayToWorldArray[i * 3 + 0]; - rayDataStream[curNumStreamingRays + i].m_rayToPosition[1] = rayToWorldArray[i * 3 + 1]; - rayDataStream[curNumStreamingRays + i].m_rayToPosition[2] = rayToWorldArray[i * 3 + 2]; - command.m_requestRaycastIntersections.m_numStreamingRays++; - } - } -} - -int PhysXClient::getNumDebugLines() const -{ - return m_data->m_debugLinesFrom.size(); -} - -const float* PhysXClient::getDebugLinesFrom() const -{ - if (getNumDebugLines()) - { - return &m_data->m_debugLinesFrom[0].m_x; - } - return 0; -} -const float* PhysXClient::getDebugLinesTo() const -{ - if (getNumDebugLines()) - { - return &m_data->m_debugLinesTo[0].m_x; - } - return 0; -} -const float* PhysXClient::getDebugLinesColor() const -{ - if (getNumDebugLines()) - { - return &m_data->m_debugLinesColor[0].m_x; - } - return 0; -} - -void PhysXClient::getCachedCameraImage(b3CameraImageData* cameraData) -{ - if (cameraData) - { - cameraData->m_pixelWidth = m_data->m_cachedCameraPixelsWidth; - cameraData->m_pixelHeight = m_data->m_cachedCameraPixelsHeight; - cameraData->m_depthValues = m_data->m_cachedCameraDepthBuffer.size() ? &m_data->m_cachedCameraDepthBuffer[0] : 0; - cameraData->m_rgbColorData = m_data->m_cachedCameraPixelsRGBA.size() ? &m_data->m_cachedCameraPixelsRGBA[0] : 0; - cameraData->m_segmentationMaskValues = m_data->m_cachedSegmentationMask.size() ? &m_data->m_cachedSegmentationMask[0] : 0; - } -} - -void PhysXClient::getCachedContactPointInformation(struct b3ContactInformation* contactPointData) -{ - contactPointData->m_numContactPoints = m_data->m_cachedContactPoints.size(); - contactPointData->m_contactPointData = contactPointData->m_numContactPoints ? &m_data->m_cachedContactPoints[0] : 0; -} - -void PhysXClient::getCachedOverlappingObjects(struct b3AABBOverlapData* overlappingObjects) -{ - overlappingObjects->m_numOverlappingObjects = m_data->m_cachedOverlappingObjects.size(); - overlappingObjects->m_overlappingObjects = m_data->m_cachedOverlappingObjects.size() ? &m_data->m_cachedOverlappingObjects[0] : 0; -} - -void PhysXClient::getCachedVisualShapeInformation(struct b3VisualShapeInformation* visualShapesInfo) -{ - visualShapesInfo->m_numVisualShapes = m_data->m_cachedVisualShapes.size(); - visualShapesInfo->m_visualShapeData = visualShapesInfo->m_numVisualShapes ? &m_data->m_cachedVisualShapes[0] : 0; -} - -void PhysXClient::getCachedCollisionShapeInformation(struct b3CollisionShapeInformation* collisionShapesInfo) -{ - collisionShapesInfo->m_numCollisionShapes = m_data->m_cachedCollisionShapes.size(); - collisionShapesInfo->m_collisionShapeData = collisionShapesInfo->m_numCollisionShapes ? &m_data->m_cachedCollisionShapes[0] : 0; -} - -void PhysXClient::getCachedVREvents(struct b3VREventsData* vrEventsData) -{ - vrEventsData->m_numControllerEvents = m_data->m_cachedVREvents.size(); - vrEventsData->m_controllerEvents = vrEventsData->m_numControllerEvents ? &m_data->m_cachedVREvents[0] : 0; -} - -void PhysXClient::getCachedKeyboardEvents(struct b3KeyboardEventsData* keyboardEventsData) -{ - keyboardEventsData->m_numKeyboardEvents = m_data->m_cachedKeyboardEvents.size(); - keyboardEventsData->m_keyboardEvents = keyboardEventsData->m_numKeyboardEvents ? &m_data->m_cachedKeyboardEvents[0] : 0; -} - -void PhysXClient::getCachedMouseEvents(struct b3MouseEventsData* mouseEventsData) -{ - mouseEventsData->m_numMouseEvents = m_data->m_cachedMouseEvents.size(); - mouseEventsData->m_mouseEvents = mouseEventsData->m_numMouseEvents ? &m_data->m_cachedMouseEvents[0] : 0; -} - -void PhysXClient::getCachedRaycastHits(struct b3RaycastInformation* raycastHits) -{ - raycastHits->m_numRayHits = m_data->m_raycastHits.size(); - raycastHits->m_rayHits = raycastHits->m_numRayHits ? &m_data->m_raycastHits[0] : 0; -} - -void PhysXClient::getCachedMassMatrix(int dofCountCheck, double* massMatrix) -{ - int sz = dofCountCheck * dofCountCheck; - if (sz == m_data->m_cachedMassMatrix.size()) - { - for (int i = 0; i < sz; i++) - { - massMatrix[i] = m_data->m_cachedMassMatrix[i]; - } - } -} - -void PhysXClient::setTimeOut(double timeOutInSeconds) -{ - m_data->m_timeOutInSeconds = timeOutInSeconds; -} - -double PhysXClient::getTimeOut() const -{ - return m_data->m_timeOutInSeconds; -} - - -void PhysXClient::pushProfileTiming(const char* timingName) -{ - std::string** strPtr = m_data->m_profileTimingStringArray[timingName]; - std::string* str = 0; - if (strPtr) - { - str = *strPtr; - } - else - { - str = new std::string(timingName); - m_data->m_profileTimingStringArray.insert(timingName, str); - } - m_data->m_profileTimings.push_back(new CProfileSample(str->c_str())); -} - -void PhysXClient::popProfileTiming() -{ - if (m_data->m_profileTimings.size()) - { - CProfileSample* sample = m_data->m_profileTimings[m_data->m_profileTimings.size() - 1]; - m_data->m_profileTimings.pop_back(); - delete sample; - } -} - - - -#endif //BT_ENABLE_PHYSX diff --git a/examples/SharedMemory/physx/PhysXClient.h b/examples/SharedMemory/physx/PhysXClient.h deleted file mode 100644 index c5bd9f6ba..000000000 --- a/examples/SharedMemory/physx/PhysXClient.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef PHYSX_CLIENT_H -#define PHYSX_CLIENT_H - -#include "../PhysicsClient.h" - -///PhysicsDirect executes the commands directly, without transporting them or having a separate server executing commands -class PhysXClient : public PhysicsClient -{ -protected: - struct PhysXDirectInternalData* m_data; - - bool processDebugLines(const struct SharedMemoryCommand& orgCommand); - - bool processCamera(const struct SharedMemoryCommand& orgCommand); - - bool processContactPointData(const struct SharedMemoryCommand& orgCommand); - - bool processOverlappingObjects(const struct SharedMemoryCommand& orgCommand); - - bool processVisualShapeData(const struct SharedMemoryCommand& orgCommand); - - void processBodyJointInfo(int bodyUniqueId, const struct SharedMemoryStatus& serverCmd); - - void processAddUserData(const struct SharedMemoryStatus& serverCmd); - - void postProcessStatus(const struct SharedMemoryStatus& serverCmd); - - void resetData(); - - void removeCachedBody(int bodyUniqueId); - -public: - PhysXClient(class PhysicsCommandProcessorInterface* physSdk, bool passSdkOwnership); - - virtual ~PhysXClient(); - - // return true if connection succesfull, can also check 'isConnected' - //it is OK to pass a null pointer for the gui helper - virtual bool connect(); - - ////todo: rename to 'disconnect' - virtual void disconnectSharedMemory(); - - virtual bool isConnected() const; - - // return non-null if there is a status, nullptr otherwise - virtual const SharedMemoryStatus* processServerStatus(); - - virtual SharedMemoryCommand* getAvailableSharedMemoryCommand(); - - virtual bool canSubmitCommand() const; - - virtual bool submitClientCommand(const struct SharedMemoryCommand& command); - - virtual int getNumBodies() const; - - virtual int getBodyUniqueId(int serialIndex) const; - - virtual bool getBodyInfo(int bodyUniqueId, struct b3BodyInfo& info) const; - - virtual int getNumJoints(int bodyIndex) const; - - virtual bool getJointInfo(int bodyIndex, int jointIndex, struct b3JointInfo& info) const; - - virtual int getNumUserConstraints() const; - - virtual int getUserConstraintInfo(int constraintUniqueId, struct b3UserConstraint& info) const; - - virtual int getUserConstraintId(int serialIndex) const; - - ///todo: move this out of the - virtual void setSharedMemoryKey(int key); - - void uploadBulletFileToSharedMemory(const char* data, int len); - - virtual void uploadRaysToSharedMemory(struct SharedMemoryCommand& command, const double* rayFromWorldArray, const double* rayToWorldArray, int numRays); - - virtual int getNumDebugLines() const; - - virtual const float* getDebugLinesFrom() const; - virtual const float* getDebugLinesTo() const; - virtual const float* getDebugLinesColor() const; - - virtual void getCachedCameraImage(b3CameraImageData* cameraData); - - virtual void getCachedContactPointInformation(struct b3ContactInformation* contactPointData); - - virtual void getCachedOverlappingObjects(struct b3AABBOverlapData* overlappingObjects); - - virtual void getCachedVisualShapeInformation(struct b3VisualShapeInformation* visualShapesInfo); - - virtual void getCachedCollisionShapeInformation(struct b3CollisionShapeInformation* collisionShapesInfo); - - virtual void getCachedVREvents(struct b3VREventsData* vrEventsData); - - virtual void getCachedKeyboardEvents(struct b3KeyboardEventsData* keyboardEventsData); - - virtual void getCachedMouseEvents(struct b3MouseEventsData* mouseEventsData); - - virtual void getCachedRaycastHits(struct b3RaycastInformation* raycastHits); - - virtual void getCachedMassMatrix(int dofCountCheck, double* massMatrix); - - //the following APIs are for internal use for visualization: - virtual bool connect(struct GUIHelperInterface* guiHelper); - virtual void renderScene(); - virtual void debugDraw(int debugDrawMode); - - virtual void setTimeOut(double timeOutInSeconds); - virtual double getTimeOut() const; - - virtual bool getCachedUserData(int userDataId, struct b3UserDataValue& valueOut) const - { - return false; - } - virtual int getCachedUserDataId(int bodyUniqueId, int linkIndex, int visualShapeIndex, const char* key) const - { - return -1; - } - virtual int getNumUserData(int bodyUniqueId) const - { - return 0; - } - virtual void getUserDataInfo(int bodyUniqueId, int userDataIndex, const char** keyOut, int* userDataIdOut, int* linkIndexOut, int* visualShapeIndexOut) const - { - *keyOut = 0; - *userDataIdOut = -1; - return; - } - - virtual void pushProfileTiming(const char* timingName); - virtual void popProfileTiming(); -}; - -#endif //PHYSX_CLIENT_H diff --git a/examples/pybullet/premake4.lua b/examples/pybullet/premake4.lua index f0c26eac5..1832dd205 100644 --- a/examples/pybullet/premake4.lua +++ b/examples/pybullet/premake4.lua @@ -223,12 +223,10 @@ if not _OPTIONS["no-enet"] then "../../examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp", "../../examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.h", "../../examples/SharedMemory/physx/PhysXC_API.cpp", - "../../examples/SharedMemory/physx/PhysXClient.cpp", "../../examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp", "../../examples/SharedMemory/physx/PhysXUrdfImporter.cpp", "../../examples/SharedMemory/physx/URDF2PhysX.cpp", "../../examples/SharedMemory/physx/PhysXC_API.h", - "../../examples/SharedMemory/physx/PhysXClient.h", "../../examples/SharedMemory/physx/PhysXServerCommandProcessor.h", "../../examples/SharedMemory/physx/PhysXUrdfImporter.h", "../../examples/SharedMemory/physx/URDF2PhysX.h", diff --git a/setup.py b/setup.py index fe39a57a3..bb20a20e7 100644 --- a/setup.py +++ b/setup.py @@ -142,7 +142,6 @@ else: sources = ["examples/pybullet/pybullet.c"]\ +["examples/SharedMemory/physx/PhysXC_API.cpp"]\ -+["examples/SharedMemory/physx/PhysXClient.cpp"]\ +["examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp"]\ +["examples/SharedMemory/physx/PhysXUrdfImporter.cpp"]\ +["examples/SharedMemory/physx/URDF2PhysX.cpp"]\ From 3bf27cf8f25236ea115eddea440d143d6c375b34 Mon Sep 17 00:00:00 2001 From: erwincoumans Date: Wed, 20 Feb 2019 21:38:37 -0800 Subject: [PATCH 07/10] implement rudimentary contact callback. Does PhysX have a way to report ALL contact points, every frame, so we can update contact forces etc, and report all contacts? --- .../physx/PhysXServerCommandProcessor.cpp | 183 ++++++++++++++++-- .../physx/PhysXServerCommandProcessor.h | 2 +- examples/SharedMemory/physx/PhysXUserData.h | 2 + examples/SharedMemory/physx/URDF2PhysX.cpp | 2 + .../pybullet/examples/otherPhysicsEngine.py | 2 + 5 files changed, 178 insertions(+), 13 deletions(-) diff --git a/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp b/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp index ce57e23ee..ac25e9736 100644 --- a/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp +++ b/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp @@ -20,6 +20,7 @@ #include "URDF2PhysX.h" #include "../b3PluginManager.h" #include "PxRigidActorExt.h" +#include "LinearMath/btThreads.h" #define STATIC_EGLRENDERER_PLUGIN #ifdef STATIC_EGLRENDERER_PLUGIN @@ -48,6 +49,9 @@ public: } }; + + + struct InternalPhysXBodyData { physx::PxArticulationReducedCoordinate* mArticulation; @@ -76,13 +80,117 @@ struct InternalPhysXBodyData typedef b3PoolBodyHandle InternalPhysXBodyHandle; -struct PhysXServerCommandProcessorInternalData + +struct PhysXServerCommandProcessorInternalData : public physx::PxSimulationEventCallback, public physx::PxContactModifyCallback { bool m_isConnected; bool m_verboseOutput; double m_physicsDeltaTime; int m_numSimulationSubSteps; + btSpinMutex m_taskLock; + btAlignedObjectArray m_contactPoints; + + + void onContactModify(physx::PxContactModifyPair* const pairs, physx::PxU32 count) + { + for (physx::PxU32 i = 0; i contacts; + for (physx::PxU32 i = 0; i < nbPairs; i++) + { + physx::PxU32 contactCount = pairs[i].contactCount; + if (contactCount) + { + contacts.resize(contactCount); + pairs[i].extractContacts(&contacts[0], contactCount); + for (physx::PxU32 j = 0; j < contactCount; j++) + { + const physx::PxContactPairPoint& contact = contacts[i]; + b3ContactPointData srcPt; + MyPhysXUserData* udA = (MyPhysXUserData*)pairHeader.actors[0]->userData; + MyPhysXUserData* udB = (MyPhysXUserData*)pairHeader.actors[1]->userData; + srcPt.m_bodyUniqueIdA = udA->m_bodyUniqueId; + srcPt.m_linkIndexA = udA->m_linkIndex; + srcPt.m_bodyUniqueIdB = udB->m_bodyUniqueId; + srcPt.m_linkIndexB = udB->m_linkIndex; + srcPt.m_positionOnAInWS[0] = contact.position.x + contact.separation*contact.normal.x; + srcPt.m_positionOnAInWS[1] = contact.position.y + contact.separation*contact.normal.y; + srcPt.m_positionOnAInWS[2] = contact.position.z + contact.separation*contact.normal.z; + srcPt.m_positionOnBInWS[0] = contact.position.x - contact.separation*contact.normal.x; + srcPt.m_positionOnBInWS[1] = contact.position.y - contact.separation*contact.normal.y; + srcPt.m_positionOnBInWS[2] = contact.position.z - contact.separation*contact.normal.z; + srcPt.m_contactNormalOnBInWS[0] = contact.normal.x; + srcPt.m_contactNormalOnBInWS[1] = contact.normal.y; + srcPt.m_contactNormalOnBInWS[2] = contact.normal.z; + srcPt.m_contactDistance = contact.separation; + srcPt.m_contactFlags = 0; + srcPt.m_linearFrictionDirection1[0] = 0; + srcPt.m_linearFrictionDirection1[1] = 0; + srcPt.m_linearFrictionDirection1[2] = 0; + srcPt.m_linearFrictionDirection2[0] = 0; + srcPt.m_linearFrictionDirection2[1] = 0; + srcPt.m_linearFrictionDirection2[2] = 0; + + srcPt.m_linearFrictionForce2 = 0; + + srcPt.m_normalForce = contact.impulse.dot(contact.normal); + //compute friction direction from impulse projected in contact plane using contact normal. + physx::PxVec3 fric = contact.impulse - contact.normal*srcPt.m_normalForce; + double fricForce = fric.normalizeSafe(); + if (fricForce) + { + srcPt.m_linearFrictionDirection1[0] = fric.x; + srcPt.m_linearFrictionDirection1[1] = fric.y; + srcPt.m_linearFrictionDirection1[2] = fric.z; + srcPt.m_linearFrictionForce1 = fricForce; + } + m_contactPoints.push_back(srcPt); + // std::cout << "Contact: bw " << pairHeader.actors[0]->getName() << " and " << pairHeader.actors[1]->getName() << " | " << contacts[j].position.x << "," << contacts[j].position.y << "," + // << contacts[j].position.z << std::endl; + } + } + } + m_taskLock.unlock(); + } + + void onConstraintBreak(physx::PxConstraintInfo* constraints, physx::PxU32 count) + { + PX_UNUSED(constraints); + PX_UNUSED(count); + } + + void onWake(physx::PxActor** actors, physx::PxU32 count) + { + PX_UNUSED(actors); + PX_UNUSED(count); + } + + void onSleep(physx::PxActor** actors, physx::PxU32 count) + { + PX_UNUSED(actors); + PX_UNUSED(count); + } + + void onTrigger(physx::PxTriggerPair* pairs, physx::PxU32 count) + { + PX_UNUSED(pairs); + PX_UNUSED(count); + } + + void onAdvance(const physx::PxRigidBody* const*, const physx::PxTransform*, const physx::PxU32) + { + } b3PluginManager m_pluginManager; @@ -158,9 +266,10 @@ physx::PxFilterFlags MyPhysXFilter(physx::PxFilterObjectAttributes attributes0, PX_UNUSED(attributes1); PX_UNUSED(constantBlock); PX_UNUSED(constantBlockSize); - if (filterData0.word2 != 0 && filterData0.word2 == filterData1.word2) - return physx::PxFilterFlag::eKILL; - pairFlags |= physx::PxPairFlag::eCONTACT_DEFAULT; + // if (filterData0.word2 != 0 && filterData0.word2 == filterData1.word2) + // return physx::PxFilterFlag::eKILL; + pairFlags |= physx::PxPairFlag::eCONTACT_DEFAULT | physx::PxPairFlag::eNOTIFY_TOUCH_FOUND + | physx::PxPairFlag::eDETECT_DISCRETE_CONTACT | physx::PxPairFlag::eNOTIFY_CONTACT_POINTS | physx::PxPairFlag::eMODIFY_CONTACTS; return physx::PxFilterFlag::eDEFAULT; } @@ -186,13 +295,29 @@ bool PhysXServerCommandProcessor::connect() physx::PxSceneDesc sceneDesc(m_data->m_physics->getTolerancesScale()); sceneDesc.gravity = physx::PxVec3(0.0f, -9.81f, 0.0f); + + //todo: add some boolean, to pick solver sceneDesc.solverType = physx::PxSolverType::eTGS; //sceneDesc.solverType = physx::PxSolverType::ePGS; sceneDesc.cpuDispatcher = m_data->m_dispatcher; - //sceneDesc.filterShader = MyPhysXFilter; - sceneDesc.filterShader = physx::PxDefaultSimulationFilterShader; - + + //todo: add some boolean, to allow enable/disable of this contact filtering + bool enableContactCallback = false; + if (enableContactCallback) + { + sceneDesc.filterShader = MyPhysXFilter; + sceneDesc.simulationEventCallback = this->m_data; + sceneDesc.contactModifyCallback = this->m_data; + } + else + { + sceneDesc.filterShader = physx::PxDefaultSimulationFilterShader; + } + + + + m_data->m_scene = m_data->m_physics->createScene(sceneDesc); m_data->m_material = m_data->m_physics->createMaterial(0.5f, 0.5f, 0.f); @@ -1049,6 +1174,34 @@ bool PhysXServerCommandProcessor::processRequestPhysicsSimulationParametersComma return hasStatus; } +bool PhysXServerCommandProcessor::processRequestContactpointInformationCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes) +{ + SharedMemoryStatus& serverCmd = serverStatusOut; + int totalBytesPerContact = sizeof(b3ContactPointData); + int contactPointStorage = bufferSizeInBytes / totalBytesPerContact - 1; + + b3ContactPointData* contactData = (b3ContactPointData*)bufferServerToClient; + + int startContactPointIndex = clientCmd.m_requestContactPointArguments.m_startingContactPointIndex; + int numContactPointBatch = btMin(int(m_data->m_contactPoints.size()), contactPointStorage); + + int endContactPointIndex = startContactPointIndex + numContactPointBatch; + serverCmd.m_sendContactPointArgs.m_numContactPointsCopied = 0; + for (int i = startContactPointIndex; i < endContactPointIndex; i++) + { + const b3ContactPointData& srcPt = m_data->m_contactPoints[i]; + b3ContactPointData& destPt = contactData[serverCmd.m_sendContactPointArgs.m_numContactPointsCopied]; + destPt = srcPt; + serverCmd.m_sendContactPointArgs.m_numContactPointsCopied++; + } + serverCmd.m_sendContactPointArgs.m_startingContactPointIndex = startContactPointIndex; + serverCmd.m_sendContactPointArgs.m_numRemainingContactPoints = m_data->m_contactPoints.size() - startContactPointIndex - serverCmd.m_sendContactPointArgs.m_numContactPointsCopied; + serverCmd.m_numDataStreamBytes = totalBytesPerContact * serverCmd.m_sendContactPointArgs.m_numContactPointsCopied; + serverCmd.m_type = CMD_CONTACT_POINT_INFORMATION_COMPLETED; + + + return true; +} bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes) { @@ -1151,6 +1304,11 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman break; } + case CMD_REQUEST_CONTACT_POINT_INFORMATION: + { + hasStatus = processRequestContactpointInformationCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } #if 0 case CMD_SET_VR_CAMERA_STATE: @@ -1310,11 +1468,7 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman hasStatus = processConfigureOpenGLVisualizerCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); break; } - case CMD_REQUEST_CONTACT_POINT_INFORMATION: - { - hasStatus = processRequestContactpointInformationCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + case CMD_CALCULATE_INVERSE_DYNAMICS: { hasStatus = processInverseDynamicsCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); @@ -1767,8 +1921,13 @@ bool PhysXServerCommandProcessor::processForwardDynamicsCommand(const struct Sha int numArt = m_data->m_scene->getNbArticulations(); + { + B3_PROFILE("clear Contacts"); + m_data->m_contactPoints.clear(); + } { B3_PROFILE("PhysX_simulate_fetchResults"); + m_data->m_scene->simulate(m_data->m_physicsDeltaTime); m_data->m_scene->fetchResults(true); } diff --git a/examples/SharedMemory/physx/PhysXServerCommandProcessor.h b/examples/SharedMemory/physx/PhysXServerCommandProcessor.h index 5cf8fa058..ed6dca0c3 100644 --- a/examples/SharedMemory/physx/PhysXServerCommandProcessor.h +++ b/examples/SharedMemory/physx/PhysXServerCommandProcessor.h @@ -20,7 +20,7 @@ class PhysXServerCommandProcessor : public PhysicsCommandProcessorInterface bool processSendDesiredStateCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); bool processChangeDynamicsInfoCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); bool processRequestPhysicsSimulationParametersCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); - + bool processRequestContactpointInformationCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); bool processCustomCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); diff --git a/examples/SharedMemory/physx/PhysXUserData.h b/examples/SharedMemory/physx/PhysXUserData.h index 8f4642a81..b4149ee0c 100644 --- a/examples/SharedMemory/physx/PhysXUserData.h +++ b/examples/SharedMemory/physx/PhysXUserData.h @@ -4,5 +4,7 @@ struct MyPhysXUserData { int m_graphicsUniqueId; + int m_bodyUniqueId; + int m_linkIndex; }; #endif //PHYSX_USER_DATA_H \ No newline at end of file diff --git a/examples/SharedMemory/physx/URDF2PhysX.cpp b/examples/SharedMemory/physx/URDF2PhysX.cpp index b810fb7d5..9821ac1bc 100644 --- a/examples/SharedMemory/physx/URDF2PhysX.cpp +++ b/examples/SharedMemory/physx/URDF2PhysX.cpp @@ -796,6 +796,8 @@ btTransform ConvertURDF2PhysXInternal( //todo: mem leaks MyPhysXUserData* userData = new MyPhysXUserData(); userData->m_graphicsUniqueId = graphicsIndex; + userData->m_bodyUniqueId = u2b.getBodyUniqueId(); + userData->m_linkIndex = mbLinkIndex; linkPtr->userData = userData; } diff --git a/examples/pybullet/examples/otherPhysicsEngine.py b/examples/pybullet/examples/otherPhysicsEngine.py index 3399cff4f..4d61fca17 100644 --- a/examples/pybullet/examples/otherPhysicsEngine.py +++ b/examples/pybullet/examples/otherPhysicsEngine.py @@ -45,5 +45,7 @@ while (1): p.setJointMotorControl2(door,1,p.POSITION_CONTROL, targetPosition = angle, positionGain=10.1, velocityGain=1, force=11.001) else: p.setJointMotorControl2(door,1,p.VELOCITY_CONTROL, targetVelocity=1, force=1011) + contacts = p.getContactPoints() + print("contacts=",contacts) p.stepSimulation() time.sleep(1./240.) From 71b1191947dbf93703c07d9f69d2bdcd4a9d0357 Mon Sep 17 00:00:00 2001 From: erwincoumans Date: Thu, 21 Feb 2019 19:24:18 -0800 Subject: [PATCH 08/10] texture caching and geometry caching (PhysX) for much faster loading of many same objects, helps benchmarking/comparison. add command-line args for PhysX (numCores=..., solver=tgs, ) --- .../SharedMemory/PhysicsServerExample.cpp | 9 +- examples/SharedMemory/physx/PhysXC_API.cpp | 4 +- examples/SharedMemory/physx/PhysXC_API.h | 2 +- .../physx/PhysXServerCommandProcessor.cpp | 78 +++++-- .../physx/PhysXServerCommandProcessor.h | 4 +- examples/SharedMemory/physx/URDF2PhysX.cpp | 17 +- .../eglRendererVisualShapeConverter.cpp | 192 ++++++++++-------- .../pybullet/examples/otherPhysicsEngine.py | 27 ++- examples/pybullet/pybullet.c | 2 +- src/Bullet3Common/b3CommandLineArgs.h | 2 +- 10 files changed, 220 insertions(+), 117 deletions(-) diff --git a/examples/SharedMemory/PhysicsServerExample.cpp b/examples/SharedMemory/PhysicsServerExample.cpp index e864508b4..ce4c49267 100644 --- a/examples/SharedMemory/PhysicsServerExample.cpp +++ b/examples/SharedMemory/PhysicsServerExample.cpp @@ -658,6 +658,7 @@ public: } GUIHelperInterface* m_childGuiHelper; + btHashMap m_cachedTextureIds; int m_uidGenerator; const unsigned char* m_texels; int m_textureWidth; @@ -856,6 +857,11 @@ public: virtual int registerTexture(const unsigned char* texels, int width, int height) { + int* cachedTexture = m_cachedTextureIds[texels]; + if (cachedTexture) + { + return *cachedTexture; + } m_texels = texels; m_textureWidth = width; m_textureHeight = height; @@ -864,7 +870,7 @@ public: m_cs->setSharedParam(1, eGUIHelperRegisterTexture); workerThreadWait(); - + m_cachedTextureIds.insert(texels, m_textureId); return m_textureId; } virtual int registerGraphicsShape(const float* vertices, int numvertices, const int* indices, int numIndices, int primitiveType, int textureId) @@ -918,6 +924,7 @@ public: virtual void removeAllGraphicsInstances() { + m_cachedTextureIds.clear(); m_cs->lock(); m_cs->setSharedParam(1, eGUIHelperRemoveAllGraphicsInstances); workerThreadWait(); diff --git a/examples/SharedMemory/physx/PhysXC_API.cpp b/examples/SharedMemory/physx/PhysXC_API.cpp index d8706eb47..0fb64afaf 100644 --- a/examples/SharedMemory/physx/PhysXC_API.cpp +++ b/examples/SharedMemory/physx/PhysXC_API.cpp @@ -4,9 +4,9 @@ #include "PhysXServerCommandProcessor.h" -B3_SHARED_API b3PhysicsClientHandle b3ConnectPhysX() +B3_SHARED_API b3PhysicsClientHandle b3ConnectPhysX(int argc, char* argv[]) { - PhysXServerCommandProcessor* sdk = new PhysXServerCommandProcessor; + PhysXServerCommandProcessor* sdk = new PhysXServerCommandProcessor(argc,argv); PhysicsDirect* direct = new PhysicsDirect(sdk, true); bool connected; diff --git a/examples/SharedMemory/physx/PhysXC_API.h b/examples/SharedMemory/physx/PhysXC_API.h index 54ad3e1a6..42d117fb9 100644 --- a/examples/SharedMemory/physx/PhysXC_API.h +++ b/examples/SharedMemory/physx/PhysXC_API.h @@ -10,7 +10,7 @@ extern "C" { #endif - B3_SHARED_API b3PhysicsClientHandle b3ConnectPhysX(); + B3_SHARED_API b3PhysicsClientHandle b3ConnectPhysX(int argc, char* argv[]); #ifdef __cplusplus } diff --git a/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp b/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp index ac25e9736..aa2687a23 100644 --- a/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp +++ b/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp @@ -9,6 +9,7 @@ #include "Bullet3Common/b3AlignedObjectArray.h" #include "LinearMath/btMinMax.h" #include "Bullet3Common/b3FileUtils.h" +#include "Bullet3Common/b3CommandLineArgs.h" #include "../../Utils/b3ResourcePath.h" #include "Bullet3Common/b3ResizablePool.h" #include "PxPhysicsAPI.h" @@ -216,16 +217,19 @@ struct PhysXServerCommandProcessorInternalData : public physx::PxSimulationEvent int m_profileTimingLoggingUid; int m_stateLoggersUniqueId; std::string m_profileTimingFileName; + b3CommandLineArgs m_commandLineArgs; - PhysXServerCommandProcessorInternalData(PhysXServerCommandProcessor* sdk) + PhysXServerCommandProcessorInternalData(PhysXServerCommandProcessor* sdk, int argc, char* argv[]) : m_isConnected(false), m_verboseOutput(false), m_physicsDeltaTime(1. / 240.), m_numSimulationSubSteps(0), m_pluginManager(sdk), m_profileTimingLoggingUid(-1), - m_stateLoggersUniqueId(1) + m_stateLoggersUniqueId(1), + m_commandLineArgs(argc,argv) { + m_foundation = NULL; m_physics = NULL; m_cooking = NULL; @@ -246,9 +250,10 @@ struct PhysXServerCommandProcessorInternalData : public physx::PxSimulationEvent } }; -PhysXServerCommandProcessor::PhysXServerCommandProcessor() +PhysXServerCommandProcessor::PhysXServerCommandProcessor(int argc, char* argv[]) { - m_data = new PhysXServerCommandProcessorInternalData(this); + + m_data = new PhysXServerCommandProcessorInternalData(this, argc, argv); } PhysXServerCommandProcessor::~PhysXServerCommandProcessor() @@ -290,25 +295,42 @@ bool PhysXServerCommandProcessor::connect() m_data->m_physics = PxCreatePhysics(PX_PHYSICS_VERSION, *m_data->m_foundation, physx::PxTolerancesScale(), true, 0); m_data->m_cooking = PxCreateCooking(PX_PHYSICS_VERSION, *m_data->m_foundation, physx::PxCookingParams(physx::PxTolerancesScale())); - physx::PxU32 numCores = 1;// + + physx::PxU32 numCores = 1; + m_data->m_commandLineArgs.GetCmdLineArgument("numCores", numCores); + printf("PhysX numCores=%d\n", numCores); m_data->m_dispatcher = physx::PxDefaultCpuDispatcherCreate(numCores == 0 ? 0 : numCores - 1); physx::PxSceneDesc sceneDesc(m_data->m_physics->getTolerancesScale()); sceneDesc.gravity = physx::PxVec3(0.0f, -9.81f, 0.0f); - //todo: add some boolean, to pick solver - sceneDesc.solverType = physx::PxSolverType::eTGS; - //sceneDesc.solverType = physx::PxSolverType::ePGS; + + sceneDesc.solverType = physx::PxSolverType::ePGS; + std::string solver; + m_data->m_commandLineArgs.GetCmdLineArgument("solver", solver); + + if (solver=="tgs") + { + sceneDesc.solverType = physx::PxSolverType::eTGS; + printf("PhysX using TGS\n"); + } + else + { + printf("PhysX using PGS\n"); + } + sceneDesc.cpuDispatcher = m_data->m_dispatcher; //todo: add some boolean, to allow enable/disable of this contact filtering bool enableContactCallback = false; + m_data->m_commandLineArgs.GetCmdLineArgument("enableContactCallback", enableContactCallback); if (enableContactCallback) { sceneDesc.filterShader = MyPhysXFilter; sceneDesc.simulationEventCallback = this->m_data; sceneDesc.contactModifyCallback = this->m_data; + printf("PhysX enableContactCallback\n"); } else { @@ -1203,6 +1225,23 @@ bool PhysXServerCommandProcessor::processRequestContactpointInformationCommand(c return true; } +bool PhysXServerCommandProcessor::processCreateCollisionShapeCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes) +{ + return false; +} + + +bool PhysXServerCommandProcessor::processSetAdditionalSearchPathCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes) +{ + bool hasStatus = true; + + BT_PROFILE("CMD_SET_ADDITIONAL_SEARCH_PATH"); + b3ResourcePath::setAdditionalSearchPath(clientCmd.m_searchPathArgs.m_path); + serverStatusOut.m_type = CMD_CLIENT_COMMAND_COMPLETED; + return hasStatus; +} + + bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes) { // BT_PROFILE("processCommand"); @@ -1310,6 +1349,17 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman break; } + case CMD_CREATE_COLLISION_SHAPE: + { + hasStatus = processCreateCollisionShapeCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } + + case CMD_SET_ADDITIONAL_SEARCH_PATH: + { + hasStatus = processSetAdditionalSearchPathCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } #if 0 case CMD_SET_VR_CAMERA_STATE: { @@ -1365,11 +1415,7 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman hasStatus = processLoadSDFCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); break; } - case CMD_CREATE_COLLISION_SHAPE: - { - hasStatus = processCreateCollisionShapeCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + case CMD_CREATE_VISUAL_SHAPE: { hasStatus = processCreateVisualShapeCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); @@ -1380,11 +1426,7 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman hasStatus = processCreateMultiBodyCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); break; } - case CMD_SET_ADDITIONAL_SEARCH_PATH: - { - hasStatus = processSetAdditionalSearchPathCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + case CMD_LOAD_MJCF: { diff --git a/examples/SharedMemory/physx/PhysXServerCommandProcessor.h b/examples/SharedMemory/physx/PhysXServerCommandProcessor.h index ed6dca0c3..f50ad44b1 100644 --- a/examples/SharedMemory/physx/PhysXServerCommandProcessor.h +++ b/examples/SharedMemory/physx/PhysXServerCommandProcessor.h @@ -21,13 +21,15 @@ class PhysXServerCommandProcessor : public PhysicsCommandProcessorInterface bool processChangeDynamicsInfoCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); bool processRequestPhysicsSimulationParametersCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); bool processRequestContactpointInformationCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); + bool processCreateCollisionShapeCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); + bool processSetAdditionalSearchPathCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); bool processCustomCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); void resetSimulation(); bool processStateLoggingCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); public: - PhysXServerCommandProcessor(); + PhysXServerCommandProcessor(int argc, char* argv[]); virtual ~PhysXServerCommandProcessor(); diff --git a/examples/SharedMemory/physx/URDF2PhysX.cpp b/examples/SharedMemory/physx/URDF2PhysX.cpp index 9821ac1bc..31f2db41d 100644 --- a/examples/SharedMemory/physx/URDF2PhysX.cpp +++ b/examples/SharedMemory/physx/URDF2PhysX.cpp @@ -294,6 +294,13 @@ int convertLinkPhysXShapes(const URDFImporterInterface& u2b, URDF2PhysXCachedDat btScalar radius = collision->m_geometry.m_capsuleRadius; btScalar height = collision->m_geometry.m_capsuleHeight; + //static PxShape* createExclusiveShape(PxRigidActor& actor, const PxGeometry& geometry, PxMaterial*const* materials, PxU16 materialCount, + // PxShapeFlags shapeFlags = PxShapeFlag::eVISUALIZATION | PxShapeFlag::eSCENE_QUERY_SHAPE | PxShapeFlag::eSIMULATION_SHAPE) + //{ + physx::PxShapeFlags shapeFlags = physx::PxShapeFlag::eVISUALIZATION | physx::PxShapeFlag::eSCENE_QUERY_SHAPE | physx::PxShapeFlag::eSIMULATION_SHAPE; + + //shape = PxGetPhysics().createShape(physx::PxCapsuleGeometry(radius, 0.5*height), &material, 1, false, shapeFlags); + shape = physx::PxRigidActorExt::createExclusiveShape(*linkPtr, physx::PxCapsuleGeometry(radius, 0.5*height), *material); btTransform childTrans = col.m_linkLocalFrame; @@ -691,7 +698,8 @@ btTransform ConvertURDF2PhysXInternal( //Now create the slider and fixed joints... - cache.m_articulation->setSolverIterationCounts(32);//todo: API? + cache.m_articulation->setSolverIterationCounts(4);//todo: API? + //cache.m_articulation->setSolverIterationCounts(32);//todo: API? cache.m_jointTypes.push_back(physx::PxArticulationJointType::eUNDEFINED); cache.m_parentLocalPoses.push_back(physx::PxTransform()); @@ -807,8 +815,10 @@ btTransform ConvertURDF2PhysXInternal( convertLinkPhysXShapes(u2b, cache, creation, urdfLinkIndex, pathPrefix, localInertialFrame, cache.m_articulation, mbLinkIndex, linkPtr); - physx::PxRigidBodyExt::updateMassAndInertia(*rbLinkPtr, mass); - + if (rbLinkPtr) + { + physx::PxRigidBodyExt::updateMassAndInertia(*rbLinkPtr, mass); + } //base->setMass(massOut); //base->setMassSpaceInertiaTensor(diagTensor); @@ -1005,6 +1015,7 @@ physx::PxBase* URDF2PhysX(physx::PxFoundation* foundation, physx::PxPhysics* phy if (cache.m_rigidStatic) { + scene->addActor(*cache.m_rigidStatic); return cache.m_rigidStatic; } diff --git a/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp b/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp index 9f4943e4d..ee30db3a4 100644 --- a/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp +++ b/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp @@ -72,7 +72,7 @@ struct MyTexture3 struct EGLRendererObjectArray { - btAlignedObjectArray m_renderObjects; + btAlignedObjectArray m_graphicsInstanceIds; int m_objectUniqueId; int m_linkIndex; @@ -92,6 +92,33 @@ struct EGLRendererObjectArray #define START_WIDTH 1024 #define START_HEIGHT 768 +struct btHashVisual +{ + UrdfShape m_vis; + btTransform m_tr; + + int getHash() const + { + if (m_vis.m_geometry.m_meshFileName.length()) + { + btHashString s = m_vis.m_geometry.m_meshFileName.c_str(); + return s.getHash(); + } + return 0; + } + bool equals(const btHashVisual& other) const + { + if ((m_vis.m_geometry.m_type == URDF_GEOM_MESH) && + (other.m_vis.m_geometry.m_type == URDF_GEOM_MESH)) + { + bool sameTr = m_tr == other.m_tr; + bool sameVis = m_vis.m_geometry.m_meshFileName == other.m_vis.m_geometry.m_meshFileName; + bool sameLocalFrame = m_vis.m_linkLocalFrame == other.m_vis.m_linkLocalFrame; + return sameTr&&sameVis&&sameLocalFrame; + } + return false; + } +}; struct EGLRendererVisualShapeConverterInternalData { @@ -105,6 +132,8 @@ struct EGLRendererVisualShapeConverterInternalData btAlignedObjectArray m_graphicsIndexToSegmentationMask; btHashMap m_swRenderInstances; + btHashMap m_cachedTextureIds; + btHashMap m_cachedVisualShapes; btAlignedObjectArray m_visualShapes; @@ -942,7 +971,7 @@ int EGLRendererVisualShapeConverter::convertVisualShapes( colorIndex &= 3; btVector4 color; color = sColors[colorIndex]; - float rgbaColor[4] = {(float)color[0], (float)color[1], (float)color[2], (float)color[3]}; + float rgbaColor[4] = { (float)color[0], (float)color[1], (float)color[2], (float)color[3] }; //if (colObj->getCollisionShape()->getShapeType()==STATIC_PLANE_PROXYTYPE) //{ // color.setValue(1,1,1,1); @@ -1012,64 +1041,91 @@ int EGLRendererVisualShapeConverter::convertVisualShapes( visualShape.m_rgbaColor[1] = rgbaColor[1]; visualShape.m_rgbaColor[2] = rgbaColor[2]; visualShape.m_rgbaColor[3] = rgbaColor[3]; + int shapeIndex = -1; + btHashVisual tmp; { B3_PROFILE("convertURDFToVisualShape2"); - convertURDFToVisualShape2(vis, pathPrefix, localInertiaFrame.inverse() * childTrans, vertices, indices, textures, visualShape, fileIO, m_data->m_flags); + + + + btTransform tr = localInertiaFrame.inverse() * childTrans; + tmp.m_vis = *vis; + tmp.m_tr = tr; + + int* bla = m_data->m_cachedVisualShapes[tmp]; + if (bla) + { + shapeIndex = *bla; + } + else + { + convertURDFToVisualShape2(vis, pathPrefix, tr, vertices, indices, textures, visualShape, fileIO, m_data->m_flags); + } } m_data->m_visualShapes.push_back(visualShape); - if (vertices.size() && indices.size()) + int textureIndex = -1; + if (shapeIndex < 0) { - TinyRenderObjectData* tinyObj = new TinyRenderObjectData(m_data->m_rgbColorBuffer, m_data->m_depthBuffer, &m_data->m_shadowBuffer, &m_data->m_segmentationMaskBuffer, bodyUniqueId, linkIndex); - unsigned char* textureImage1 = 0; - int textureWidth = 0; - int textureHeight = 0; - bool isCached = false; - int textureIndex = -1; - - if (textures.size()) + if (vertices.size() && indices.size()) { - textureImage1 = textures[0].textureData1; - textureWidth = textures[0].m_width; - textureHeight = textures[0].m_height; - isCached = textures[0].m_isCached; - textureIndex = m_data->m_instancingRenderer->registerTexture(textureImage1, textureWidth, textureHeight); - } + unsigned char* textureImage1 = 0; + int textureWidth = 0; + int textureHeight = 0; + bool isCached = false; - { - B3_PROFILE("registerMeshShape"); - tinyObj->registerMeshShape(&vertices[0].xyzw[0], vertices.size(), &indices[0], indices.size(), rgbaColor, - textureImage1, textureWidth, textureHeight); - } - visuals->m_renderObjects.push_back(tinyObj); - - { - B3_PROFILE("m_instancingRenderer register"); - - // register mesh to m_instancingRenderer too. - - int shapeIndex = m_data->m_instancingRenderer->registerShape(&vertices[0].xyzw[0], vertices.size(), &indices[0], indices.size(), B3_GL_TRIANGLES, textureIndex); - double scaling[3] = {1, 1, 1}; - int graphicsIndex = m_data->m_instancingRenderer->registerGraphicsInstance(shapeIndex, &visualShape.m_localVisualFrame[0], &visualShape.m_localVisualFrame[3], &visualShape.m_rgbaColor[0], scaling); - - int segmentationMask = bodyUniqueId + ((linkIndex + 1) << 24); + if (textures.size()) { - if (graphicsIndex >= 0) + textureImage1 = textures[0].textureData1; + textureWidth = textures[0].m_width; + textureHeight = textures[0].m_height; + isCached = textures[0].m_isCached; + int* bla = m_data->m_cachedTextureIds[textureImage1]; + if (bla) { - visuals->m_graphicsInstanceIds.push_back(graphicsIndex); - - if (m_data->m_graphicsIndexToSegmentationMask.size() < (graphicsIndex + 1)) - { - m_data->m_graphicsIndexToSegmentationMask.resize(graphicsIndex + 1); - } - m_data->m_graphicsIndexToSegmentationMask[graphicsIndex] = segmentationMask; + textureIndex = *bla; + } + else + { + textureIndex = m_data->m_instancingRenderer->registerTexture(textureImage1, textureWidth, textureHeight); + m_data->m_cachedTextureIds.insert(textureImage1, textureIndex); } } - - m_data->m_instancingRenderer->writeTransforms(); } } + + + { + B3_PROFILE("m_instancingRenderer register"); + + // register mesh to m_instancingRenderer too. + + if (shapeIndex < 0) + { + shapeIndex = m_data->m_instancingRenderer->registerShape(&vertices[0].xyzw[0], vertices.size(), &indices[0], indices.size(), B3_GL_TRIANGLES, textureIndex); + m_data->m_cachedVisualShapes.insert(tmp, shapeIndex); + } + double scaling[3] = { 1, 1, 1 }; + int graphicsIndex = m_data->m_instancingRenderer->registerGraphicsInstance(shapeIndex, &visualShape.m_localVisualFrame[0], &visualShape.m_localVisualFrame[3], &visualShape.m_rgbaColor[0], scaling); + + int segmentationMask = bodyUniqueId + ((linkIndex + 1) << 24); + { + if (graphicsIndex >= 0) + { + visuals->m_graphicsInstanceIds.push_back(graphicsIndex); + + if (m_data->m_graphicsIndexToSegmentationMask.size() < (graphicsIndex + 1)) + { + m_data->m_graphicsIndexToSegmentationMask.resize(graphicsIndex + 1); + } + m_data->m_graphicsIndexToSegmentationMask[graphicsIndex] = segmentationMask; + } + } + + m_data->m_instancingRenderer->writeTransforms(); + } + for (int i = 0; i < textures.size(); i++) { if (!textures[i].m_isCached) @@ -1078,6 +1134,7 @@ int EGLRendererVisualShapeConverter::convertVisualShapes( } } } + } return orgGraphicsUniqueId; } @@ -1163,13 +1220,7 @@ void EGLRendererVisualShapeConverter::changeRGBAColor(int bodyUniqueId, int link EGLRendererObjectArray* visuals = *ptrptr; if ((bodyUniqueId == visuals->m_objectUniqueId) && (linkIndex == visuals->m_linkIndex)) { - for (int q = 0; q < visuals->m_renderObjects.size(); q++) - { - if (shapeIndex < 0 || q == shapeIndex) - { - visuals->m_renderObjects[q]->m_model->setColorRGBA(rgba); - } - } + } } } @@ -1503,15 +1554,11 @@ void EGLRendererVisualShapeConverter::removeVisualShape(int collisionObjectUniqu EGLRendererObjectArray* ptr = *ptrptr; if (ptr) { - for (int o = 0; o < ptr->m_renderObjects.size(); o++) + for (int i = 0; i < ptr->m_graphicsInstanceIds.size(); i++) { - for (int i = 0; i < ptr->m_graphicsInstanceIds.size(); i++) - { - m_data->m_instancingRenderer->removeGraphicsInstance(ptr->m_graphicsInstanceIds[i]); - } - - delete ptr->m_renderObjects[o]; + m_data->m_instancingRenderer->removeGraphicsInstance(ptr->m_graphicsInstanceIds[i]); } + } delete ptr; m_data->m_swRenderInstances.remove(collisionObjectUniqueId); @@ -1520,6 +1567,8 @@ void EGLRendererVisualShapeConverter::removeVisualShape(int collisionObjectUniqu void EGLRendererVisualShapeConverter::resetAll() { + m_data->m_cachedTextureIds.clear(); + for (int i = 0; i < m_data->m_swRenderInstances.size(); i++) { EGLRendererObjectArray** ptrptr = m_data->m_swRenderInstances.getAtIndex(i); @@ -1528,10 +1577,7 @@ void EGLRendererVisualShapeConverter::resetAll() EGLRendererObjectArray* ptr = *ptrptr; if (ptr) { - for (int o = 0; o < ptr->m_renderObjects.size(); o++) - { - delete ptr->m_renderObjects[o]; - } + } delete ptr; } @@ -1556,25 +1602,7 @@ void EGLRendererVisualShapeConverter::changeShapeTexture(int objectUniqueId, int btAssert(textureUniqueId < m_data->m_textures.size()); if (textureUniqueId >= 0 && textureUniqueId < m_data->m_textures.size()) { - for (int n = 0; n < m_data->m_swRenderInstances.size(); n++) - { - EGLRendererObjectArray** visualArrayPtr = m_data->m_swRenderInstances.getAtIndex(n); - if (0 == visualArrayPtr) - continue; //can this ever happen? - EGLRendererObjectArray* visualArray = *visualArrayPtr; - - if (visualArray->m_objectUniqueId == objectUniqueId && visualArray->m_linkIndex == jointIndex) - { - for (int v = 0; v < visualArray->m_renderObjects.size(); v++) - { - TinyRenderObjectData* renderObj = visualArray->m_renderObjects[v]; - if ((shapeIndex < 0) || (shapeIndex == v)) - { - renderObj->m_model->setDiffuseTextureFromData(m_data->m_textures[textureUniqueId].textureData1, m_data->m_textures[textureUniqueId].m_width, m_data->m_textures[textureUniqueId].m_height); - } - } - } - } + } } diff --git a/examples/pybullet/examples/otherPhysicsEngine.py b/examples/pybullet/examples/otherPhysicsEngine.py index 4d61fca17..4bff90a05 100644 --- a/examples/pybullet/examples/otherPhysicsEngine.py +++ b/examples/pybullet/examples/otherPhysicsEngine.py @@ -1,18 +1,27 @@ import pybullet as p +import pybullet_data as pd import time import math usePhysX = True if usePhysX: - p.connect(p.PhysX) + p.connect(p.PhysX,options="--numCores=1 --solver=pgs") p.loadPlugin("eglRendererPlugin") else: p.connect(p.GUI) -p.loadURDF("plane.urdf") +useMaximalCoordinates = False +p.setAdditionalSearchPath(pd.getDataPath()) +p.loadURDF("plane.urdf", useMaximalCoordinates=useMaximalCoordinates) +p.configureDebugVisualizer(p.COV_ENABLE_TINY_RENDERER,0) +p.configureDebugVisualizer(p.COV_ENABLE_RENDERING,0) +logId = p.startStateLogging(p.STATE_LOGGING_PROFILE_TIMINGS,"physx_create_dominoes.json") +for j in range (50): + for i in range (100): + sphere = p.loadURDF("domino/domino.urdf",[i*0.04,1+j*.25,0.03], useMaximalCoordinates=useMaximalCoordinates) +print("loaded!") +p.configureDebugVisualizer(p.COV_ENABLE_RENDERING,1) -for i in range (10): - sphere = p.loadURDF("marble_cube.urdf",[0,-1,1+i*1], useMaximalCoordinates=False) p.changeDynamics(sphere ,-1, mass=1000) door = p.loadURDF("door.urdf",[0,0,1]) @@ -31,8 +40,12 @@ prevTime = time.time() angle = math.pi*0.5 +count=0 while (1): - + count+=1 + if (count==10): + p.stopStateLogging(logId) + curTime = time.time() diff = curTime - prevTime @@ -45,7 +58,7 @@ while (1): p.setJointMotorControl2(door,1,p.POSITION_CONTROL, targetPosition = angle, positionGain=10.1, velocityGain=1, force=11.001) else: p.setJointMotorControl2(door,1,p.VELOCITY_CONTROL, targetVelocity=1, force=1011) - contacts = p.getContactPoints() - print("contacts=",contacts) + #contacts = p.getContactPoints() + #print("contacts=",contacts) p.stepSimulation() time.sleep(1./240.) diff --git a/examples/pybullet/pybullet.c b/examples/pybullet/pybullet.c index f3e01e5dc..ca3e2e784 100644 --- a/examples/pybullet/pybullet.c +++ b/examples/pybullet/pybullet.c @@ -456,7 +456,7 @@ static PyObject* pybullet_connectPhysicsServer(PyObject* self, PyObject* args, P #ifdef BT_ENABLE_PHYSX case eCONNECT_PHYSX: { - sm = b3ConnectPhysX(); + sm = b3ConnectPhysX(argc, argv); break; } #endif diff --git a/src/Bullet3Common/b3CommandLineArgs.h b/src/Bullet3Common/b3CommandLineArgs.h index 5fe4f25f8..3deb35bb7 100644 --- a/src/Bullet3Common/b3CommandLineArgs.h +++ b/src/Bullet3Common/b3CommandLineArgs.h @@ -23,7 +23,7 @@ public: void addArgs(int argc, char **argv) { - for (int i = 1; i < argc; i++) + for (int i = 0; i < argc; i++) { std::string arg = argv[i]; From 9ecc1cc4853bc70e868847a66dd57abb381d3f46 Mon Sep 17 00:00:00 2001 From: erwincoumans Date: Fri, 22 Feb 2019 09:17:55 -0800 Subject: [PATCH 09/10] Implement CustomProfilerCallback, hooking up to Bullet profiling test dominoes --- .../physx/PhysXServerCommandProcessor.cpp | 21 +++++++ .../pybullet/examples/otherPhysicsEngine.py | 57 +++++++++++++++---- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp b/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp index aa2687a23..89c24e8a4 100644 --- a/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp +++ b/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp @@ -50,7 +50,25 @@ public: } }; +class CustomProfilerCallback : public physx::PxProfilerCallback +{ +public: + virtual ~CustomProfilerCallback() {} + virtual void* zoneStart(const char* eventName, bool detached, uint64_t contextId) + { + b3EnterProfileZone(eventName); + return 0; + } + + virtual void zoneEnd(void* profilerData, const char* eventName, bool detached, uint64_t contextId) + { + b3LeaveProfileZone(); + } + +}; + +static CustomProfilerCallback gCustomProfilerCallback; struct InternalPhysXBodyData @@ -292,6 +310,9 @@ bool PhysXServerCommandProcessor::connect() { m_data->m_foundation = PxCreateFoundation(PX_PHYSICS_VERSION, m_data->m_allocator, m_data->m_errorCallback); + // This call should be performed after PVD is initialized, otherwise it will have no effect. + PxSetProfilerCallback(&gCustomProfilerCallback); + m_data->m_physics = PxCreatePhysics(PX_PHYSICS_VERSION, *m_data->m_foundation, physx::PxTolerancesScale(), true, 0); m_data->m_cooking = PxCreateCooking(PX_PHYSICS_VERSION, *m_data->m_foundation, physx::PxCookingParams(physx::PxTolerancesScale())); diff --git a/examples/pybullet/examples/otherPhysicsEngine.py b/examples/pybullet/examples/otherPhysicsEngine.py index 4bff90a05..93f9fbe44 100644 --- a/examples/pybullet/examples/otherPhysicsEngine.py +++ b/examples/pybullet/examples/otherPhysicsEngine.py @@ -4,27 +4,61 @@ import time import math usePhysX = True +useMaximalCoordinates = True if usePhysX: - p.connect(p.PhysX,options="--numCores=1 --solver=pgs") + p.connect(p.PhysX,options="--numCores=8 --solver=pgs") p.loadPlugin("eglRendererPlugin") else: p.connect(p.GUI) -useMaximalCoordinates = False +p.setPhysicsEngineParameter(fixedTimeStep=1./240.,numSolverIterations=4, minimumSolverIslandSize=1024) +p.setPhysicsEngineParameter(contactBreakingThreshold=0.01) + p.setAdditionalSearchPath(pd.getDataPath()) -p.loadURDF("plane.urdf", useMaximalCoordinates=useMaximalCoordinates) +#Always make ground plane maximal coordinates, to avoid performance drop in PhysX +#See https://github.com/NVIDIAGameWorks/PhysX/issues/71 + +p.loadURDF("plane.urdf", useMaximalCoordinates=True)#useMaximalCoordinates) p.configureDebugVisualizer(p.COV_ENABLE_TINY_RENDERER,0) p.configureDebugVisualizer(p.COV_ENABLE_RENDERING,0) logId = p.startStateLogging(p.STATE_LOGGING_PROFILE_TIMINGS,"physx_create_dominoes.json") -for j in range (50): - for i in range (100): - sphere = p.loadURDF("domino/domino.urdf",[i*0.04,1+j*.25,0.03], useMaximalCoordinates=useMaximalCoordinates) +jran = 50 +iran = 100 + +num=64 +radius=0.1 +numDominoes=0 + +for i in range (int(num*50)): + num=(radius*2*math.pi)/0.08 + radius += 0.05/float(num) + orn = p.getQuaternionFromEuler([0,0,0.5*math.pi+math.pi*2*i/float(num)]) + pos = [radius*math.cos(2*math.pi*(i/float(num))),radius*math.sin(2*math.pi*(i/float(num))), 0.03] + sphere = p.loadURDF("domino/domino.urdf",pos, orn, useMaximalCoordinates=useMaximalCoordinates) + numDominoes+=1 + +pos=[pos[0],pos[1],pos[2]+0.3] +orn = p.getQuaternionFromEuler([0,0,-math.pi/4.]) +sphere = p.loadURDF("domino/domino.urdf",pos, orn, useMaximalCoordinates=useMaximalCoordinates) + +print("numDominoes=",numDominoes) + + +#for j in range (20): +# for i in range (100): +# if (i<99): +# sphere = p.loadURDF("domino/domino.urdf",[i*0.04,1+j*.25,0.03], useMaximalCoordinates=useMaximalCoordinates) +# else: +# orn = p.getQuaternionFromEuler([0,-3.14*0.24,0]) +# sphere = p.loadURDF("domino/domino.urdf",[(i-1)*0.04,1+j*.25,0.03], orn, useMaximalCoordinates=useMaximalCoordinates) + + print("loaded!") -p.configureDebugVisualizer(p.COV_ENABLE_RENDERING,1) -p.changeDynamics(sphere ,-1, mass=1000) -door = p.loadURDF("door.urdf",[0,0,1]) +#p.changeDynamics(sphere ,-1, mass=1000) + +door = p.loadURDF("door.urdf",[0,0,-11]) p.changeDynamics(door ,1, linearDamping=0, angularDamping=0, jointDamping=0, mass=1) print("numJoints = ", p.getNumJoints(door)) @@ -43,8 +77,9 @@ angle = math.pi*0.5 count=0 while (1): count+=1 - if (count==10): + if (count==12): p.stopStateLogging(logId) + p.configureDebugVisualizer(p.COV_ENABLE_RENDERING,1) curTime = time.time() @@ -61,4 +96,4 @@ while (1): #contacts = p.getContactPoints() #print("contacts=",contacts) p.stepSimulation() - time.sleep(1./240.) + #time.sleep(1./240.) From a9996088c81b8a632750db522470a5e6a3da408c Mon Sep 17 00:00:00 2001 From: erwincoumans Date: Sun, 24 Feb 2019 14:09:42 -0800 Subject: [PATCH 10/10] Implement PyBullet.getCameraImage for PhysX backend. PhysX backend, allow arbitrary plane normal, a few other fixes. --- .../ImportURDFDemo/UrdfRenderingInterface.h | 7 + .../physx/PhysXServerCommandProcessor.cpp | 482 ++++++++++++++---- .../physx/PhysXServerCommandProcessor.h | 3 + examples/SharedMemory/physx/URDF2PhysX.cpp | 24 +- .../eglRendererVisualShapeConverter.cpp | 58 +++ .../eglRendererVisualShapeConverter.h | 3 + examples/pybullet/examples/testrender.py | 8 +- 7 files changed, 481 insertions(+), 104 deletions(-) diff --git a/examples/Importers/ImportURDFDemo/UrdfRenderingInterface.h b/examples/Importers/ImportURDFDemo/UrdfRenderingInterface.h index 7e01cffc7..02aa42534 100644 --- a/examples/Importers/ImportURDFDemo/UrdfRenderingInterface.h +++ b/examples/Importers/ImportURDFDemo/UrdfRenderingInterface.h @@ -95,6 +95,13 @@ struct UrdfRenderingInterface virtual void setProjectiveTextureMatrices(const float viewMatrix[16], const float projectionMatrix[16]) {} virtual void setProjectiveTexture(bool useProjectiveTexture) {} + + + virtual bool getCameraInfo(int* width, int* height, float viewMatrix[16], float projectionMatrix[16], float camUp[3], float camForward[3], float hor[3], float vert[3], float* yaw, float* pitch, float* camDist, float cameraTarget[3]) const + { + return false; + } + }; #endif //LINK_VISUAL_SHAPES_CONVERTER_H diff --git a/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp b/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp index 89c24e8a4..d5aa86666 100644 --- a/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp +++ b/examples/SharedMemory/physx/PhysXServerCommandProcessor.cpp @@ -109,8 +109,9 @@ struct PhysXServerCommandProcessorInternalData : public physx::PxSimulationEvent btSpinMutex m_taskLock; btAlignedObjectArray m_contactPoints; - + + void onContactModify(physx::PxContactModifyPair* const pairs, physx::PxU32 count) { for (physx::PxU32 i = 0; i m_userDebugParameters; + btAlignedObjectArray m_graphicsIndexToSegmentationMask; PhysXServerCommandProcessorInternalData(PhysXServerCommandProcessor* sdk, int argc, char* argv[]) : m_isConnected(false), @@ -245,7 +249,8 @@ struct PhysXServerCommandProcessorInternalData : public physx::PxSimulationEvent m_pluginManager(sdk), m_profileTimingLoggingUid(-1), m_stateLoggersUniqueId(1), - m_commandLineArgs(argc,argv) + m_commandLineArgs(argc,argv), + m_userDebugParametersUid(0) { m_foundation = NULL; @@ -756,13 +761,14 @@ bool PhysXServerCommandProcessor::processSendDesiredStateCommand(const struct Sh //find the joint motors and apply the desired velocity and maximum force/torque { int dofIndex = 6; //skip the 3 linear + 3 angular degree of freedom entries of the base + int posIndex = 7; //skip 3 positional and 4 orientation (quaternion) positional degrees of freedom of the base for (int link = 1; link < numLinks2; link++) { int dofs = physxLinks[link]->getInboundJointDof(); physx::PxReal stiffness = 10.f; physx::PxReal damping = 0.1f; physx::PxReal forceLimit = PX_MAX_F32; - int posIndex = 7; //skip 3 positional and 4 orientation (quaternion) positional degrees of freedom of the base + if (dofs == 1) { @@ -782,33 +788,34 @@ bool PhysXServerCommandProcessor::processSendDesiredStateCommand(const struct Sh if ((clientCmd.m_sendDesiredStateCommandArgument.m_hasDesiredStateFlags[posIndex] & SIM_DESIRED_STATE_HAS_Q) != 0) { desiredPosition = clientCmd.m_sendDesiredStateCommandArgument.m_desiredStateQ[posIndex]; - } - if ((clientCmd.m_sendDesiredStateCommandArgument.m_hasDesiredStateFlags[dofIndex] & SIM_DESIRED_STATE_HAS_KD) != 0) - { - kd = clientCmd.m_sendDesiredStateCommandArgument.m_Kd[dofIndex]; - } - physx::PxReal damping = kd; - if ((clientCmd.m_sendDesiredStateCommandArgument.m_hasDesiredStateFlags[dofIndex] & SIM_DESIRED_STATE_HAS_KP) != 0) - { - kp = clientCmd.m_sendDesiredStateCommandArgument.m_Kp[dofIndex]; - stiffness = kp; - } + if ((clientCmd.m_sendDesiredStateCommandArgument.m_hasDesiredStateFlags[dofIndex] & SIM_DESIRED_STATE_HAS_KD) != 0) + { + kd = clientCmd.m_sendDesiredStateCommandArgument.m_Kd[dofIndex]; + } + physx::PxReal damping = kd; - joint->setDriveVelocity(physx::PxArticulationAxis::eTWIST, desiredVelocity); - - + if ((clientCmd.m_sendDesiredStateCommandArgument.m_hasDesiredStateFlags[dofIndex] & SIM_DESIRED_STATE_HAS_KP) != 0) + { + kp = clientCmd.m_sendDesiredStateCommandArgument.m_Kp[dofIndex]; + stiffness = kp; + } - physx::PxReal forceLimit = 1000000.f; - if ((clientCmd.m_sendDesiredStateCommandArgument.m_hasDesiredStateFlags[dofIndex] & SIM_DESIRED_STATE_HAS_MAX_FORCE) != 0) - { - forceLimit = clientCmd.m_sendDesiredStateCommandArgument.m_desiredStateForceTorque[dofIndex]; + joint->setDriveVelocity(physx::PxArticulationAxis::eTWIST, desiredVelocity); + + + + physx::PxReal forceLimit = 1000000.f; + if ((clientCmd.m_sendDesiredStateCommandArgument.m_hasDesiredStateFlags[dofIndex] & SIM_DESIRED_STATE_HAS_MAX_FORCE) != 0) + { + forceLimit = clientCmd.m_sendDesiredStateCommandArgument.m_desiredStateForceTorque[dofIndex]; + } + bool isAcceleration = false; + + joint->setDriveTarget(physx::PxArticulationAxis::eTWIST, desiredPosition); + joint->setDrive(physx::PxArticulationAxis::eTWIST, stiffness, damping, forceLimit, isAcceleration); } - bool isAcceleration = false; - joint->setDrive(physx::PxArticulationAxis::eTWIST, stiffness, damping, forceLimit, isAcceleration); - - joint->setDriveTarget(physx::PxArticulationAxis::eTWIST, desiredPosition); } dofIndex += dofs; @@ -1262,6 +1269,46 @@ bool PhysXServerCommandProcessor::processSetAdditionalSearchPathCommand(const st return hasStatus; } +bool PhysXServerCommandProcessor::processUserDebugDrawCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes) +{ + ///dummy, so commands don't fail + bool hasStatus = true; + BT_PROFILE("CMD_USER_DEBUG_DRAW"); + SharedMemoryStatus& serverCmd = serverStatusOut; + int uid = 0; + serverCmd.m_userDebugDrawArgs.m_debugItemUniqueId = uid; + serverCmd.m_type = CMD_USER_DEBUG_DRAW_FAILED; + + if (clientCmd.m_updateFlags & USER_DEBUG_ADD_PARAMETER) + { + int uid = m_data->m_userDebugParametersUid++; + double value = clientCmd.m_userDebugDrawArgs.m_startValue; + m_data->m_userDebugParameters.insert(uid, value); + serverCmd.m_userDebugDrawArgs.m_debugItemUniqueId = uid; + serverCmd.m_type = CMD_USER_DEBUG_DRAW_COMPLETED; + } + if (clientCmd.m_updateFlags & USER_DEBUG_READ_PARAMETER) + { + double* valPtr = m_data->m_userDebugParameters[clientCmd.m_userDebugDrawArgs.m_itemUniqueId]; + if (valPtr) + { + serverCmd.m_userDebugDrawArgs.m_parameterValue = *valPtr; + serverCmd.m_type = CMD_USER_DEBUG_DRAW_PARAMETER_COMPLETED; + } + } + if (clientCmd.m_updateFlags & USER_DEBUG_REMOVE_ALL) + { + m_data->m_userDebugParameters.clear(); + serverCmd.m_type = CMD_USER_DEBUG_DRAW_COMPLETED; + } + if (clientCmd.m_updateFlags & USER_DEBUG_REMOVE_ONE_ITEM) + { + m_data->m_userDebugParameters.remove(clientCmd.m_userDebugDrawArgs.m_itemUniqueId); + serverCmd.m_type = CMD_USER_DEBUG_DRAW_COMPLETED; + } + return hasStatus; + +} bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes) { @@ -1381,6 +1428,19 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman hasStatus = processSetAdditionalSearchPathCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); break; } + + case CMD_USER_DEBUG_DRAW: + { + hasStatus = processUserDebugDrawCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } + + case CMD_REQUEST_CAMERA_IMAGE_DATA: + { + hasStatus = processRequestCameraImageCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } + #if 0 case CMD_SET_VR_CAMERA_STATE: { @@ -1415,11 +1475,6 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman break; } - case CMD_REQUEST_CAMERA_IMAGE_DATA: - { - hasStatus = processRequestCameraImageCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } case CMD_REQUEST_BODY_INFO: { @@ -1553,45 +1608,45 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman break; } case CMD_REMOVE_BODY: - { - hasStatus = processRemoveBodyCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processRemoveBodyCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_USER_CONSTRAINT: - { - hasStatus = processCreateUserConstraintCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processCreateUserConstraintCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_CALCULATE_INVERSE_KINEMATICS: - { - hasStatus = processCalculateInverseKinematicsCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processCalculateInverseKinematicsCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_REQUEST_VISUAL_SHAPE_INFO: - { - hasStatus = processRequestVisualShapeInfoCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processRequestVisualShapeInfoCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_REQUEST_COLLISION_SHAPE_INFO: { hasStatus = processRequestCollisionShapeInfoCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); break; } case CMD_UPDATE_VISUAL_SHAPE: - { - hasStatus = processUpdateVisualShapeCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processUpdateVisualShapeCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_CHANGE_TEXTURE: - { - hasStatus = processChangeTextureCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processChangeTextureCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_LOAD_TEXTURE: - { - hasStatus = processLoadTextureCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processLoadTextureCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_RESTORE_STATE: { hasStatus = processRestoreStateCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); @@ -1605,56 +1660,291 @@ bool PhysXServerCommandProcessor::processCommand(const struct SharedMemoryComman } case CMD_LOAD_BULLET: - { - hasStatus = processLoadBulletCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processLoadBulletCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_SAVE_BULLET: - { - hasStatus = processSaveBulletCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processSaveBulletCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_LOAD_MJCF: - { - hasStatus = processLoadMJCFCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } - case CMD_USER_DEBUG_DRAW: - { - hasStatus = processUserDebugDrawCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } - + { + hasStatus = processLoadMJCFCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } + case CMD_REQUEST_USER_DATA: - { - hasStatus = processRequestUserDataCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processRequestUserDataCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_ADD_USER_DATA: - { - hasStatus = processAddUserDataCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processAddUserDataCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } case CMD_REMOVE_USER_DATA: - { - hasStatus = processRemoveUserDataCommand(clientCmd,serverStatusOut,bufferServerToClient, bufferSizeInBytes); - break; - } + { + hasStatus = processRemoveUserDataCommand(clientCmd, serverStatusOut, bufferServerToClient, bufferSizeInBytes); + break; + } #endif - default: - { - BT_PROFILE("CMD_UNKNOWN"); - printf("Unknown command encountered: %d", clientCmd.m_type); - SharedMemoryStatus& serverCmd = serverStatusOut; - serverCmd.m_type = CMD_UNKNOWN_COMMAND_FLUSHED; - hasStatus = true; - } + default: + { + BT_PROFILE("CMD_UNKNOWN"); + printf("Unknown command encountered: %d", clientCmd.m_type); + SharedMemoryStatus& serverCmd = serverStatusOut; + serverCmd.m_type = CMD_UNKNOWN_COMMAND_FLUSHED; + hasStatus = true; + } }; return hasStatus; } + + +bool PhysXServerCommandProcessor::processRequestCameraImageCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes) +{ + bool hasStatus = true; + BT_PROFILE("CMD_REQUEST_CAMERA_IMAGE_DATA"); + int startPixelIndex = clientCmd.m_requestPixelDataArguments.m_startPixelIndex; + int width = clientCmd.m_requestPixelDataArguments.m_pixelWidth; + int height = clientCmd.m_requestPixelDataArguments.m_pixelHeight; + int numPixelsCopied = 0; + + int oldWidth; + int oldHeight; + m_data->m_pluginManager.getRenderInterface()->getWidthAndHeight(oldWidth, oldHeight); + + serverStatusOut.m_type = CMD_CAMERA_IMAGE_FAILED; + + if ((clientCmd.m_requestPixelDataArguments.m_startPixelIndex == 0) && + (clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_SET_PIXEL_WIDTH_HEIGHT) != 0) + { + if (m_data->m_pluginManager.getRenderInterface()) + { + + m_data->m_pluginManager.getRenderInterface()->setWidthAndHeight(clientCmd.m_requestPixelDataArguments.m_pixelWidth, + clientCmd.m_requestPixelDataArguments.m_pixelHeight); + } + } + int flags = 0; + if (clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_HAS_FLAGS) + { + flags = clientCmd.m_requestPixelDataArguments.m_flags; + } + if (m_data->m_pluginManager.getRenderInterface()) + { + m_data->m_pluginManager.getRenderInterface()->setFlags(flags); + } + + int numTotalPixels = width * height; + int numRemainingPixels = numTotalPixels - startPixelIndex; + + if (numRemainingPixels > 0) + { + int totalBytesPerPixel = 4 + 4 + 4; //4 for rgb, 4 for depth, 4 for segmentation mask + int maxNumPixels = bufferSizeInBytes / totalBytesPerPixel - 1; + unsigned char* pixelRGBA = (unsigned char*)bufferServerToClient; + int numRequestedPixels = btMin(maxNumPixels, numRemainingPixels); + + float* depthBuffer = (float*)(bufferServerToClient + numRequestedPixels * 4); + int* segmentationMaskBuffer = (int*)(bufferServerToClient + numRequestedPixels * 8); + + serverStatusOut.m_numDataStreamBytes = numRequestedPixels * totalBytesPerPixel; + float viewMat[16]; + float projMat[16]; + float projTextureViewMat[16]; + float projTextureProjMat[16]; + + if ((clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_HAS_CAMERA_MATRICES) == 0) + { + b3OpenGLVisualizerCameraInfo tmpCamResult; + bool result = m_data->m_pluginManager.getRenderInterface()->getCameraInfo( + &tmpCamResult.m_width, + &tmpCamResult.m_height, + tmpCamResult.m_viewMatrix, + tmpCamResult.m_projectionMatrix, + tmpCamResult.m_camUp, + tmpCamResult.m_camForward, + tmpCamResult.m_horizontal, + tmpCamResult.m_vertical, + &tmpCamResult.m_yaw, + &tmpCamResult.m_pitch, + &tmpCamResult.m_dist, + tmpCamResult.m_target); + if (result) + { + for (int i = 0; i < 16; i++) + { + viewMat[i] = tmpCamResult.m_viewMatrix[i]; + projMat[i] = tmpCamResult.m_projectionMatrix[i]; + } + } + else + { + //failed + m_data->m_pluginManager.getRenderInterface()->setWidthAndHeight(oldWidth, oldHeight); + return hasStatus; + } + } + else + { + for (int i = 0; i < 16; i++) + { + viewMat[i] = clientCmd.m_requestPixelDataArguments.m_viewMatrix[i]; + projMat[i] = clientCmd.m_requestPixelDataArguments.m_projectionMatrix[i]; + } + } + + { + if (m_data->m_pluginManager.getRenderInterface()) + { + if (clientCmd.m_requestPixelDataArguments.m_startPixelIndex == 0) + { + // printf("-------------------------------\nRendering\n"); + + if ((clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_SET_LIGHT_DIRECTION) != 0) + { + m_data->m_pluginManager.getRenderInterface()->setLightDirection(clientCmd.m_requestPixelDataArguments.m_lightDirection[0], clientCmd.m_requestPixelDataArguments.m_lightDirection[1], clientCmd.m_requestPixelDataArguments.m_lightDirection[2]); + } + + if ((clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_SET_LIGHT_COLOR) != 0) + { + m_data->m_pluginManager.getRenderInterface()->setLightColor(clientCmd.m_requestPixelDataArguments.m_lightColor[0], clientCmd.m_requestPixelDataArguments.m_lightColor[1], clientCmd.m_requestPixelDataArguments.m_lightColor[2]); + } + + if ((clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_SET_LIGHT_DISTANCE) != 0) + { + m_data->m_pluginManager.getRenderInterface()->setLightDistance(clientCmd.m_requestPixelDataArguments.m_lightDistance); + } + + if ((clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_SET_SHADOW) != 0) + { + m_data->m_pluginManager.getRenderInterface()->setShadow((clientCmd.m_requestPixelDataArguments.m_hasShadow != 0)); + } + + if ((clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_SET_AMBIENT_COEFF) != 0) + { + m_data->m_pluginManager.getRenderInterface()->setLightAmbientCoeff(clientCmd.m_requestPixelDataArguments.m_lightAmbientCoeff); + } + + if ((clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_SET_DIFFUSE_COEFF) != 0) + { + m_data->m_pluginManager.getRenderInterface()->setLightDiffuseCoeff(clientCmd.m_requestPixelDataArguments.m_lightDiffuseCoeff); + } + + if ((clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_SET_SPECULAR_COEFF) != 0) + { + m_data->m_pluginManager.getRenderInterface()->setLightSpecularCoeff(clientCmd.m_requestPixelDataArguments.m_lightSpecularCoeff); + } + + + if ((clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_HAS_CAMERA_MATRICES) != 0) + { + m_data->m_pluginManager.getRenderInterface()->render( + clientCmd.m_requestPixelDataArguments.m_viewMatrix, + clientCmd.m_requestPixelDataArguments.m_projectionMatrix); + } + else + { + b3OpenGLVisualizerCameraInfo tmpCamResult; + bool result = m_data->m_pluginManager.getRenderInterface()->getCameraInfo( + &tmpCamResult.m_width, + &tmpCamResult.m_height, + tmpCamResult.m_viewMatrix, + tmpCamResult.m_projectionMatrix, + tmpCamResult.m_camUp, + tmpCamResult.m_camForward, + tmpCamResult.m_horizontal, + tmpCamResult.m_vertical, + &tmpCamResult.m_yaw, + &tmpCamResult.m_pitch, + &tmpCamResult.m_dist, + tmpCamResult.m_target); + if (result) + { + m_data->m_pluginManager.getRenderInterface()->render(tmpCamResult.m_viewMatrix, tmpCamResult.m_projectionMatrix); + } + else + { + m_data->m_pluginManager.getRenderInterface()->render(); + } + } + } + } + + if (m_data->m_pluginManager.getRenderInterface()) + { + if ((flags & ER_USE_PROJECTIVE_TEXTURE) != 0) + { + m_data->m_pluginManager.getRenderInterface()->setProjectiveTexture(true); + if ((clientCmd.m_updateFlags & REQUEST_PIXEL_ARGS_HAS_PROJECTIVE_TEXTURE_MATRICES) != 0) + { + for (int i = 0; i < 16; i++) + { + projTextureViewMat[i] = clientCmd.m_requestPixelDataArguments.m_projectiveTextureViewMatrix[i]; + projTextureProjMat[i] = clientCmd.m_requestPixelDataArguments.m_projectiveTextureProjectionMatrix[i]; + } + } + else // If no specified matrices for projective texture, then use the camera matrices. + { + for (int i = 0; i < 16; i++) + { + projTextureViewMat[i] = viewMat[i]; + projTextureProjMat[i] = projMat[i]; + } + } + m_data->m_pluginManager.getRenderInterface()->setProjectiveTextureMatrices(projTextureViewMat, projTextureProjMat); + } + else + { + m_data->m_pluginManager.getRenderInterface()->setProjectiveTexture(false); + } + + if ((flags & ER_NO_SEGMENTATION_MASK) != 0) + { + segmentationMaskBuffer = 0; + } + + m_data->m_pluginManager.getRenderInterface()->copyCameraImageData(pixelRGBA, numRequestedPixels, + depthBuffer, numRequestedPixels, + segmentationMaskBuffer, numRequestedPixels, + startPixelIndex, &width, &height, &numPixelsCopied); + m_data->m_pluginManager.getRenderInterface()->setProjectiveTexture(false); + } + + #if 0 + m_data->m_guiHelper->debugDisplayCameraImageData(clientCmd.m_requestPixelDataArguments.m_viewMatrix, + clientCmd.m_requestPixelDataArguments.m_projectionMatrix, pixelRGBA, numRequestedPixels, + depthBuffer, numRequestedPixels, + segmentationMaskBuffer, numRequestedPixels, + startPixelIndex, width, height, &numPixelsCopied); + #endif + } + + //each pixel takes 4 RGBA values and 1 float = 8 bytes + } + else + { + } + + m_data->m_pluginManager.getRenderInterface()->setWidthAndHeight(oldWidth, oldHeight); + serverStatusOut.m_type = CMD_CAMERA_IMAGE_COMPLETED; + + serverStatusOut.m_sendPixelDataArguments.m_numPixelsCopied = numPixelsCopied; + serverStatusOut.m_sendPixelDataArguments.m_numRemainingPixels = numRemainingPixels - numPixelsCopied; + serverStatusOut.m_sendPixelDataArguments.m_startingPixelIndex = startPixelIndex; + serverStatusOut.m_sendPixelDataArguments.m_imageWidth = width; + serverStatusOut.m_sendPixelDataArguments.m_imageHeight = height; + return hasStatus; + +} + bool PhysXServerCommandProcessor::processRequestInternalDataCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes) { bool hasStatus = true; diff --git a/examples/SharedMemory/physx/PhysXServerCommandProcessor.h b/examples/SharedMemory/physx/PhysXServerCommandProcessor.h index f50ad44b1..7566268e2 100644 --- a/examples/SharedMemory/physx/PhysXServerCommandProcessor.h +++ b/examples/SharedMemory/physx/PhysXServerCommandProcessor.h @@ -23,11 +23,14 @@ class PhysXServerCommandProcessor : public PhysicsCommandProcessorInterface bool processRequestContactpointInformationCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); bool processCreateCollisionShapeCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); bool processSetAdditionalSearchPathCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); + bool processUserDebugDrawCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); + bool processRequestCameraImageCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); bool processCustomCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); void resetSimulation(); bool processStateLoggingCommand(const struct SharedMemoryCommand& clientCmd, struct SharedMemoryStatus& serverStatusOut, char* bufferServerToClient, int bufferSizeInBytes); + public: PhysXServerCommandProcessor(int argc, char* argv[]); diff --git a/examples/SharedMemory/physx/URDF2PhysX.cpp b/examples/SharedMemory/physx/URDF2PhysX.cpp index 31f2db41d..2a5a19a87 100644 --- a/examples/SharedMemory/physx/URDF2PhysX.cpp +++ b/examples/SharedMemory/physx/URDF2PhysX.cpp @@ -281,11 +281,21 @@ int convertLinkPhysXShapes(const URDFImporterInterface& u2b, URDF2PhysXCachedDat { btVector3 planeNormal = col.m_geometry.m_planeNormal; btScalar planeConstant = 0; //not available? - //PxPlane(1, 0, 0, 0). + + btVector3 planeRefAxis(1, 0, 0); + btQuaternion diffQuat = shortestArcQuat(planeRefAxis, planeNormal); shape = physx::PxRigidActorExt::createExclusiveShape(*linkPtr, physx::PxPlaneGeometry(), *material); - //todo: compensate for plane axis - //physx::PxTransform localPose = tr*physx::PxTransform - //shape->setLocalPose(localPose); + + btTransform diffTr; + diffTr.setIdentity(); + diffTr.setRotation(diffQuat); + btTransform localTrans = localInertiaFrame.inverse()*childTrans*diffTr; + physx::PxTransform tr; + tr.p = physx::PxVec3(localTrans.getOrigin()[0], localTrans.getOrigin()[1], localTrans.getOrigin()[2]); + tr.q = physx::PxQuat(localTrans.getRotation()[0], localTrans.getRotation()[1], localTrans.getRotation()[2], localTrans.getRotation()[3]); + + physx::PxTransform localPose = tr; + shape->setLocalPose(localPose); numShapes++; break; } @@ -698,8 +708,8 @@ btTransform ConvertURDF2PhysXInternal( //Now create the slider and fixed joints... - cache.m_articulation->setSolverIterationCounts(4);//todo: API? - //cache.m_articulation->setSolverIterationCounts(32);//todo: API? + //cache.m_articulation->setSolverIterationCounts(4);//todo: API? + cache.m_articulation->setSolverIterationCounts(32);//todo: API? cache.m_jointTypes.push_back(physx::PxArticulationJointType::eUNDEFINED); cache.m_parentLocalPoses.push_back(physx::PxTransform()); @@ -815,7 +825,7 @@ btTransform ConvertURDF2PhysXInternal( convertLinkPhysXShapes(u2b, cache, creation, urdfLinkIndex, pathPrefix, localInertialFrame, cache.m_articulation, mbLinkIndex, linkPtr); - if (rbLinkPtr) + if (rbLinkPtr && mass) { physx::PxRigidBodyExt::updateMassAndInertia(*rbLinkPtr, mass); } diff --git a/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp b/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp index ee30db3a4..f6eb2ae05 100644 --- a/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp +++ b/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.cpp @@ -1276,6 +1276,8 @@ void EGLRendererVisualShapeConverter::render(const float viewMat[16], const floa render(); + m_data->m_camera.disableVRCamera(); + //cout<m_instancingRenderer && m_data->m_instancingRenderer->getActiveCamera()) + { + *width = m_data->m_window->getWidth() * m_data->m_window->getRetinaScale(); + *height = m_data->m_window->getHeight() * m_data->m_window->getRetinaScale(); + m_data->m_instancingRenderer->getActiveCamera()->getCameraViewMatrix(viewMatrix); + m_data->m_instancingRenderer->getActiveCamera()->getCameraProjectionMatrix(projectionMatrix); + m_data->m_instancingRenderer->getActiveCamera()->getCameraUpVector(camUp); + m_data->m_instancingRenderer->getActiveCamera()->getCameraForwardVector(camForward); + + float top = 1.f; + float bottom = -1.f; + float tanFov = (top - bottom) * 0.5f / 1; + float fov = btScalar(2.0) * btAtan(tanFov); + btVector3 camPos, camTarget; + m_data->m_instancingRenderer->getActiveCamera()->getCameraPosition(camPos); + m_data->m_instancingRenderer->getActiveCamera()->getCameraTargetPosition(camTarget); + btVector3 rayFrom = camPos; + btVector3 rayForward = (camTarget - camPos); + rayForward.normalize(); + float farPlane = 10000.f; + rayForward *= farPlane; + + btVector3 rightOffset; + btVector3 cameraUp = btVector3(camUp[0], camUp[1], camUp[2]); + btVector3 vertical = cameraUp; + btVector3 hori; + hori = rayForward.cross(vertical); + hori.normalize(); + vertical = hori.cross(rayForward); + vertical.normalize(); + float tanfov = tanf(0.5f * fov); + hori *= 2.f * farPlane * tanfov; + vertical *= 2.f * farPlane * tanfov; + btScalar aspect = float(*width) / float(*height); + hori *= aspect; + //compute 'hor' and 'vert' vectors, useful to generate raytracer rays + hor[0] = hori[0] * m_data->m_window->getRetinaScale(); + hor[1] = hori[1] * m_data->m_window->getRetinaScale(); + hor[2] = hori[2] * m_data->m_window->getRetinaScale(); + vert[0] = vertical[0] * m_data->m_window->getRetinaScale(); + vert[1] = vertical[1] * m_data->m_window->getRetinaScale(); + vert[2] = vertical[2] * m_data->m_window->getRetinaScale(); + + *yaw = m_data->m_instancingRenderer->getActiveCamera()->getCameraYaw(); + *pitch = m_data->m_instancingRenderer->getActiveCamera()->getCameraPitch(); + *camDist = m_data->m_instancingRenderer->getActiveCamera()->getCameraDistance(); + cameraTarget[0] = camTarget[0]; + cameraTarget[1] = camTarget[1]; + cameraTarget[2] = camTarget[2]; + return true; + } + return false; +} diff --git a/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.h b/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.h index 5fe8a5f08..b0cfc36a7 100644 --- a/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.h +++ b/examples/SharedMemory/plugins/eglPlugin/eglRendererVisualShapeConverter.h @@ -59,6 +59,9 @@ struct EGLRendererVisualShapeConverter : public UrdfRenderingInterface virtual void mouseMoveCallback(float x, float y); virtual void mouseButtonCallback(int button, int state, float x, float y); + virtual bool getCameraInfo(int* width, int* height, float viewMatrix[16], float projectionMatrix[16], float camUp[3], float camForward[3], float hor[3], float vert[3], float* yaw, float* pitch, float* camDist, float cameraTarget[3]) const; + + }; #endif //EGL_RENDERER_VISUAL_SHAPE_CONVERTER_H diff --git a/examples/pybullet/examples/testrender.py b/examples/pybullet/examples/testrender.py index 8be4be7ff..fbdde2aaf 100644 --- a/examples/pybullet/examples/testrender.py +++ b/examples/pybullet/examples/testrender.py @@ -15,7 +15,13 @@ image = plt.imshow(img,interpolation='none',animated=True,label="blah") ax = plt.gca() -pybullet.connect(pybullet.DIRECT) +usePhysX = True +useMaximalCoordinates = True +if usePhysX: + pybullet.connect(pybullet.PhysX,options="--numCores=8 --solver=pgs") + pybullet.loadPlugin("eglRendererPlugin") +else: + pybullet.connect(pybullet.GUI) #pybullet.loadPlugin("eglRendererPlugin") pybullet.loadURDF("plane.urdf",[0,0,-1])